xref: /freebsd/sys/dev/sdhci/sdhci_fdt.c (revision 62e8ccc3)
1a9caca6aSWojciech A. Koszek /*-
24d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
3718cf2ccSPedro F. Giffuni  *
4a9caca6aSWojciech A. Koszek  * Copyright (c) 2012 Thomas Skibo
5a9caca6aSWojciech A. Koszek  * Copyright (c) 2008 Alexander Motin <mav@FreeBSD.org>
6a9caca6aSWojciech A. Koszek  * All rights reserved.
7a9caca6aSWojciech A. Koszek  *
8a9caca6aSWojciech A. Koszek  * Redistribution and use in source and binary forms, with or without
9a9caca6aSWojciech A. Koszek  * modification, are permitted provided that the following conditions
10a9caca6aSWojciech A. Koszek  * are met:
11a9caca6aSWojciech A. Koszek  * 1. Redistributions of source code must retain the above copyright
12a9caca6aSWojciech A. Koszek  *    notice, this list of conditions and the following disclaimer.
13a9caca6aSWojciech A. Koszek  * 2. Redistributions in binary form must reproduce the above copyright
14a9caca6aSWojciech A. Koszek  *    notice, this list of conditions and the following disclaimer in the
15a9caca6aSWojciech A. Koszek  *    documentation and/or other materials provided with the distribution.
16a9caca6aSWojciech A. Koszek  *
17a9caca6aSWojciech A. Koszek  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18a9caca6aSWojciech A. Koszek  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19a9caca6aSWojciech A. Koszek  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20a9caca6aSWojciech A. Koszek  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21a9caca6aSWojciech A. Koszek  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22a9caca6aSWojciech A. Koszek  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23a9caca6aSWojciech A. Koszek  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24a9caca6aSWojciech A. Koszek  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25a9caca6aSWojciech A. Koszek  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26a9caca6aSWojciech A. Koszek  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27a9caca6aSWojciech A. Koszek  */
28a9caca6aSWojciech A. Koszek 
29a9caca6aSWojciech A. Koszek /* Generic driver to attach sdhci controllers on simplebus.
30a9caca6aSWojciech A. Koszek  * Derived mainly from sdhci_pci.c
31a9caca6aSWojciech A. Koszek  */
32a9caca6aSWojciech A. Koszek 
33a9caca6aSWojciech A. Koszek #include <sys/param.h>
34a9caca6aSWojciech A. Koszek #include <sys/systm.h>
35a9caca6aSWojciech A. Koszek #include <sys/bus.h>
36a9caca6aSWojciech A. Koszek #include <sys/kernel.h>
37a9caca6aSWojciech A. Koszek #include <sys/lock.h>
38a9caca6aSWojciech A. Koszek #include <sys/module.h>
39a9caca6aSWojciech A. Koszek #include <sys/mutex.h>
40a9caca6aSWojciech A. Koszek #include <sys/resource.h>
41a9caca6aSWojciech A. Koszek #include <sys/rman.h>
42a9caca6aSWojciech A. Koszek #include <sys/sysctl.h>
43a9caca6aSWojciech A. Koszek #include <sys/taskqueue.h>
44a9caca6aSWojciech A. Koszek 
45a9caca6aSWojciech A. Koszek #include <machine/bus.h>
46a9caca6aSWojciech A. Koszek #include <machine/resource.h>
47a9caca6aSWojciech A. Koszek 
48a9caca6aSWojciech A. Koszek #include <dev/fdt/fdt_common.h>
49a9caca6aSWojciech A. Koszek #include <dev/ofw/ofw_bus.h>
50a9caca6aSWojciech A. Koszek #include <dev/ofw/ofw_bus_subr.h>
51a9caca6aSWojciech A. Koszek 
52b24594e5SGanbold Tsagaankhuu #include <dev/ofw/ofw_subr.h>
53be82b3a0SEmmanuel Vadot #include <dev/clk/clk.h>
54be82b3a0SEmmanuel Vadot #include <dev/clk/clk_fixed.h>
5562e8ccc3SEmmanuel Vadot #include <dev/syscon/syscon.h>
56950a6087SEmmanuel Vadot #include <dev/phy/phy.h>
57b24594e5SGanbold Tsagaankhuu 
58a9caca6aSWojciech A. Koszek #include <dev/mmc/bridge.h>
59b440e965SMarius Strobl 
60a9caca6aSWojciech A. Koszek #include <dev/sdhci/sdhci.h>
61a9caca6aSWojciech A. Koszek 
62a9caca6aSWojciech A. Koszek #include "mmcbr_if.h"
63a9caca6aSWojciech A. Koszek #include "sdhci_if.h"
64a9caca6aSWojciech A. Koszek 
65d597cfd5SEmmanuel Vadot #include "opt_mmccam.h"
66d597cfd5SEmmanuel Vadot 
67b24594e5SGanbold Tsagaankhuu #include "clkdev_if.h"
68b24594e5SGanbold Tsagaankhuu #include "syscon_if.h"
69b24594e5SGanbold Tsagaankhuu 
70a9caca6aSWojciech A. Koszek #define	MAX_SLOTS		6
71018101a8SLuiz Otavio O Souza #define	SDHCI_FDT_ARMADA38X	1
728c7e7474SEmmanuel Vadot #define	SDHCI_FDT_XLNX_ZY7	2
738c7e7474SEmmanuel Vadot #define	SDHCI_FDT_QUALCOMM	3
748c7e7474SEmmanuel Vadot #define	SDHCI_FDT_RK3399	4
758c7e7474SEmmanuel Vadot #define	SDHCI_FDT_RK3568	5
768c7e7474SEmmanuel Vadot #define	SDHCI_FDT_XLNX_ZMP	6
77b24594e5SGanbold Tsagaankhuu 
78b24594e5SGanbold Tsagaankhuu #define	RK3399_GRF_EMMCCORE_CON0		0xf000
79b24594e5SGanbold Tsagaankhuu #define	 RK3399_CORECFG_BASECLKFREQ		0xff00
80b24594e5SGanbold Tsagaankhuu #define	 RK3399_CORECFG_TIMEOUTCLKUNIT		(1 << 7)
81b24594e5SGanbold Tsagaankhuu #define	 RK3399_CORECFG_TUNINGCOUNT		0x3f
82b24594e5SGanbold Tsagaankhuu #define	RK3399_GRF_EMMCCORE_CON11		0xf02c
83b24594e5SGanbold Tsagaankhuu #define	 RK3399_CORECFG_CLOCKMULTIPLIER		0xff
84b24594e5SGanbold Tsagaankhuu 
85e00774a9SSøren Schmidt #define	RK3568_EMMC_HOST_CTRL			0x0508
86e00774a9SSøren Schmidt #define	RK3568_EMMC_EMMC_CTRL			0x052c
87e00774a9SSøren Schmidt #define	RK3568_EMMC_ATCTRL			0x0540
88e00774a9SSøren Schmidt #define	RK3568_EMMC_DLL_CTRL			0x0800
89e00774a9SSøren Schmidt #define	 DLL_CTRL_SRST				0x00000001
90e00774a9SSøren Schmidt #define	 DLL_CTRL_START				0x00000002
91e00774a9SSøren Schmidt #define	 DLL_CTRL_START_POINT_DEFAULT		0x00050000
92e00774a9SSøren Schmidt #define	 DLL_CTRL_INCREMENT_DEFAULT		0x00000200
93e00774a9SSøren Schmidt 
94e00774a9SSøren Schmidt #define	RK3568_EMMC_DLL_RXCLK			0x0804
95e00774a9SSøren Schmidt #define	 DLL_RXCLK_DELAY_ENABLE			0x08000000
96e00774a9SSøren Schmidt #define	 DLL_RXCLK_NO_INV			0x20000000
97e00774a9SSøren Schmidt 
98e00774a9SSøren Schmidt #define	RK3568_EMMC_DLL_TXCLK			0x0808
99e00774a9SSøren Schmidt #define	 DLL_TXCLK_DELAY_ENABLE			0x08000000
100e00774a9SSøren Schmidt #define	 DLL_TXCLK_TAPNUM_DEFAULT		0x00000008
101e00774a9SSøren Schmidt #define	 DLL_TXCLK_TAPNUM_FROM_SW		0x01000000
102e00774a9SSøren Schmidt 
103e00774a9SSøren Schmidt #define	RK3568_EMMC_DLL_STRBIN			0x080c
104e00774a9SSøren Schmidt #define	 DLL_STRBIN_DELAY_ENABLE		0x08000000
105e00774a9SSøren Schmidt #define	 DLL_STRBIN_TAPNUM_DEFAULT		0x00000008
106e00774a9SSøren Schmidt #define	DLL_STRBIN_TAPNUM_FROM_SW		0x01000000
107e00774a9SSøren Schmidt 
108e00774a9SSøren Schmidt #define	RK3568_EMMC_DLL_STATUS0			0x0840
109e00774a9SSøren Schmidt #define	 DLL_STATUS0_DLL_LOCK			0x00000100
110e00774a9SSøren Schmidt #define	 DLL_STATUS0_DLL_TIMEOUT		0x00000200
111e00774a9SSøren Schmidt 
112b24594e5SGanbold Tsagaankhuu #define	LOWEST_SET_BIT(mask)	((((mask) - 1) & (mask)) ^ (mask))
113b24594e5SGanbold Tsagaankhuu #define	SHIFTIN(x, mask)	((x) * LOWEST_SET_BIT(mask))
114b24594e5SGanbold Tsagaankhuu 
115018101a8SLuiz Otavio O Souza static struct ofw_compat_data compat_data[] = {
116018101a8SLuiz Otavio O Souza 	{ "marvell,armada-380-sdhci",	SDHCI_FDT_ARMADA38X },
1173d22784fSRuslan Bukin 	{ "qcom,sdhci-msm-v4",		SDHCI_FDT_QUALCOMM },
118b24594e5SGanbold Tsagaankhuu 	{ "rockchip,rk3399-sdhci-5.1",	SDHCI_FDT_RK3399 },
119018101a8SLuiz Otavio O Souza 	{ "xlnx,zy7_sdhci",		SDHCI_FDT_XLNX_ZY7 },
120e00774a9SSøren Schmidt 	{ "rockchip,rk3568-dwcmshc",	SDHCI_FDT_RK3568 },
121b426119aSEmmanuel Vadot 	{ "xlnx,zynqmp-8.9a",		SDHCI_FDT_XLNX_ZMP },
122018101a8SLuiz Otavio O Souza 	{ NULL, 0 }
123018101a8SLuiz Otavio O Souza };
124a9caca6aSWojciech A. Koszek 
125a9caca6aSWojciech A. Koszek struct sdhci_fdt_softc {
126a9caca6aSWojciech A. Koszek 	device_t	dev;		/* Controller device */
127a9caca6aSWojciech A. Koszek 	u_int		quirks;		/* Chip specific quirks */
128a9caca6aSWojciech A. Koszek 	u_int		caps;		/* If we override SDHCI_CAPABILITIES */
129e625c10bSIan Lepore 	uint32_t	max_clk;	/* Max possible freq */
130ab00a509SMarius Strobl 	uint8_t		sdma_boundary;	/* If we override the SDMA boundary */
131a9caca6aSWojciech A. Koszek 	struct resource *irq_res;	/* IRQ resource */
132a9caca6aSWojciech A. Koszek 	void		*intrhand;	/* Interrupt handle */
133a9caca6aSWojciech A. Koszek 
134a9caca6aSWojciech A. Koszek 	int		num_slots;	/* Number of slots on this controller*/
135a9caca6aSWojciech A. Koszek 	struct sdhci_slot slots[MAX_SLOTS];
136a9caca6aSWojciech A. Koszek 	struct resource	*mem_res[MAX_SLOTS];	/* Memory resource */
137cd394501SLuiz Otavio O Souza 
138cd394501SLuiz Otavio O Souza 	bool		wp_inverted;	/* WP pin is inverted */
139e758ed2dSEmmanuel Vadot 	bool		wp_disabled;	/* WP pin is not supported */
140cd394501SLuiz Otavio O Souza 	bool		no_18v;		/* No 1.8V support */
141b24594e5SGanbold Tsagaankhuu 
142b24594e5SGanbold Tsagaankhuu 	clk_t		clk_xin;	/* xin24m fixed clock */
143b24594e5SGanbold Tsagaankhuu 	clk_t		clk_ahb;	/* ahb clock */
144e00774a9SSøren Schmidt 	clk_t		clk_core;	/* core clock */
145b24594e5SGanbold Tsagaankhuu 	phy_t		phy;		/* phy to be used */
1460ee5d6fcSEmmanuel Vadot 
1470ee5d6fcSEmmanuel Vadot 	struct syscon	*syscon;	/* Handle to the syscon */
148a9caca6aSWojciech A. Koszek };
149a9caca6aSWojciech A. Koszek 
15081a4fe38SEmmanuel Vadot struct sdhci_exported_clocks_sc {
151b24594e5SGanbold Tsagaankhuu 	device_t	clkdev;
152b24594e5SGanbold Tsagaankhuu };
153b24594e5SGanbold Tsagaankhuu 
154b24594e5SGanbold Tsagaankhuu static int
sdhci_exported_clocks_init(struct clknode * clk,device_t dev)15581a4fe38SEmmanuel Vadot sdhci_exported_clocks_init(struct clknode *clk, device_t dev)
156b24594e5SGanbold Tsagaankhuu {
157b24594e5SGanbold Tsagaankhuu 
158b24594e5SGanbold Tsagaankhuu 	clknode_init_parent_idx(clk, 0);
159b24594e5SGanbold Tsagaankhuu 	return (0);
160b24594e5SGanbold Tsagaankhuu }
161b24594e5SGanbold Tsagaankhuu 
16281a4fe38SEmmanuel Vadot static clknode_method_t sdhci_exported_clocks_clknode_methods[] = {
163b24594e5SGanbold Tsagaankhuu 	/* Device interface */
16481a4fe38SEmmanuel Vadot 	CLKNODEMETHOD(clknode_init,	sdhci_exported_clocks_init),
165b24594e5SGanbold Tsagaankhuu 	CLKNODEMETHOD_END
166b24594e5SGanbold Tsagaankhuu };
16781a4fe38SEmmanuel Vadot DEFINE_CLASS_1(sdhci_exported_clocks_clknode, sdhci_exported_clocks_clknode_class,
16881a4fe38SEmmanuel Vadot     sdhci_exported_clocks_clknode_methods, sizeof(struct sdhci_exported_clocks_sc),
169b24594e5SGanbold Tsagaankhuu     clknode_class);
170b24594e5SGanbold Tsagaankhuu 
171b24594e5SGanbold Tsagaankhuu static int
sdhci_clock_ofw_map(struct clkdom * clkdom,uint32_t ncells,phandle_t * cells,struct clknode ** clk)17281a4fe38SEmmanuel Vadot sdhci_clock_ofw_map(struct clkdom *clkdom, uint32_t ncells,
173b24594e5SGanbold Tsagaankhuu     phandle_t *cells, struct clknode **clk)
174b24594e5SGanbold Tsagaankhuu {
17581a4fe38SEmmanuel Vadot 	int id = 1; /* Our clock id starts at 1 */
176b24594e5SGanbold Tsagaankhuu 
17781a4fe38SEmmanuel Vadot 	if (ncells != 0)
17881a4fe38SEmmanuel Vadot 		id = cells[1];
17981a4fe38SEmmanuel Vadot 	*clk = clknode_find_by_id(clkdom, id);
180b24594e5SGanbold Tsagaankhuu 
181b24594e5SGanbold Tsagaankhuu 	if (*clk == NULL)
182b24594e5SGanbold Tsagaankhuu 		return (ENXIO);
183b24594e5SGanbold Tsagaankhuu 	return (0);
184b24594e5SGanbold Tsagaankhuu }
185b24594e5SGanbold Tsagaankhuu 
186b24594e5SGanbold Tsagaankhuu static void
sdhci_export_clocks(struct sdhci_fdt_softc * sc)18781a4fe38SEmmanuel Vadot sdhci_export_clocks(struct sdhci_fdt_softc *sc)
188b24594e5SGanbold Tsagaankhuu {
189b24594e5SGanbold Tsagaankhuu 	struct clknode_init_def def;
19081a4fe38SEmmanuel Vadot 	struct sdhci_exported_clocks_sc *clksc;
191b24594e5SGanbold Tsagaankhuu 	struct clkdom *clkdom;
192b24594e5SGanbold Tsagaankhuu 	struct clknode *clk;
193b24594e5SGanbold Tsagaankhuu 	bus_addr_t paddr;
194b24594e5SGanbold Tsagaankhuu 	bus_size_t psize;
195b24594e5SGanbold Tsagaankhuu 	const char **clknames;
196b24594e5SGanbold Tsagaankhuu 	phandle_t node;
197b24594e5SGanbold Tsagaankhuu 	int i, nclocks, ncells, error;
198b24594e5SGanbold Tsagaankhuu 
19981a4fe38SEmmanuel Vadot 	node = ofw_bus_get_node(sc->dev);
200b24594e5SGanbold Tsagaankhuu 
201b24594e5SGanbold Tsagaankhuu 	if (ofw_reg_to_paddr(node, 0, &paddr, &psize, NULL) != 0) {
20281a4fe38SEmmanuel Vadot 		device_printf(sc->dev, "cannot parse 'reg' property\n");
203b24594e5SGanbold Tsagaankhuu 		return;
204b24594e5SGanbold Tsagaankhuu 	}
205b24594e5SGanbold Tsagaankhuu 
206b24594e5SGanbold Tsagaankhuu 	error = ofw_bus_parse_xref_list_get_length(node, "clocks",
207b24594e5SGanbold Tsagaankhuu 	    "#clock-cells", &ncells);
208b24594e5SGanbold Tsagaankhuu 	if (error != 0 || ncells != 2) {
20981a4fe38SEmmanuel Vadot 		device_printf(sc->dev, "couldn't find parent clocks\n");
210b24594e5SGanbold Tsagaankhuu 		return;
211b24594e5SGanbold Tsagaankhuu 	}
212b24594e5SGanbold Tsagaankhuu 
213b24594e5SGanbold Tsagaankhuu 	nclocks = ofw_bus_string_list_to_array(node, "clock-output-names",
214b24594e5SGanbold Tsagaankhuu 	    &clknames);
215b24594e5SGanbold Tsagaankhuu 	/* No clocks to export */
216b24594e5SGanbold Tsagaankhuu 	if (nclocks <= 0)
217b24594e5SGanbold Tsagaankhuu 		return;
218b24594e5SGanbold Tsagaankhuu 
21981a4fe38SEmmanuel Vadot 	clkdom = clkdom_create(sc->dev);
22081a4fe38SEmmanuel Vadot 	clkdom_set_ofw_mapper(clkdom, sdhci_clock_ofw_map);
221b24594e5SGanbold Tsagaankhuu 
22281a4fe38SEmmanuel Vadot 	for (i = 0; i < nclocks; i++) {
223b24594e5SGanbold Tsagaankhuu 		memset(&def, 0, sizeof(def));
22481a4fe38SEmmanuel Vadot 		def.id = i + 1; /* Exported clock IDs starts at 1 */
22581a4fe38SEmmanuel Vadot 		def.name = clknames[i];
22681a4fe38SEmmanuel Vadot 		def.parent_names = malloc(sizeof(char *) * 1, M_OFWPROP, M_WAITOK);
22781a4fe38SEmmanuel Vadot 		def.parent_names[0] = clk_get_name(sc->clk_xin);
22881a4fe38SEmmanuel Vadot 		def.parent_cnt = 1;
229b24594e5SGanbold Tsagaankhuu 
23081a4fe38SEmmanuel Vadot 		clk = clknode_create(clkdom, &sdhci_exported_clocks_clknode_class, &def);
231b24594e5SGanbold Tsagaankhuu 		if (clk == NULL) {
23281a4fe38SEmmanuel Vadot 			device_printf(sc->dev, "cannot create clknode\n");
233b24594e5SGanbold Tsagaankhuu 			return;
234b24594e5SGanbold Tsagaankhuu 		}
235b24594e5SGanbold Tsagaankhuu 
23681a4fe38SEmmanuel Vadot 		clksc = clknode_get_softc(clk);
23781a4fe38SEmmanuel Vadot 		clksc->clkdev = device_get_parent(sc->dev);
238b24594e5SGanbold Tsagaankhuu 
239b24594e5SGanbold Tsagaankhuu 		clknode_register(clkdom, clk);
24081a4fe38SEmmanuel Vadot 	}
241b24594e5SGanbold Tsagaankhuu 
242b24594e5SGanbold Tsagaankhuu 	if (clkdom_finit(clkdom) != 0) {
24381a4fe38SEmmanuel Vadot 		device_printf(sc->dev, "cannot finalize clkdom initialization\n");
244b24594e5SGanbold Tsagaankhuu 		return;
245b24594e5SGanbold Tsagaankhuu 	}
246b24594e5SGanbold Tsagaankhuu 
247b24594e5SGanbold Tsagaankhuu 	if (bootverbose)
248b24594e5SGanbold Tsagaankhuu 		clkdom_dump(clkdom);
249b24594e5SGanbold Tsagaankhuu }
250b24594e5SGanbold Tsagaankhuu 
251b24594e5SGanbold Tsagaankhuu static int
sdhci_init_clocks(device_t dev)2529377d704SEmmanuel Vadot sdhci_init_clocks(device_t dev)
253b24594e5SGanbold Tsagaankhuu {
254b24594e5SGanbold Tsagaankhuu 	struct sdhci_fdt_softc *sc = device_get_softc(dev);
255b24594e5SGanbold Tsagaankhuu 	int error;
256b24594e5SGanbold Tsagaankhuu 
257b24594e5SGanbold Tsagaankhuu 	/* Get and activate clocks */
258b24594e5SGanbold Tsagaankhuu 	error = clk_get_by_ofw_name(dev, 0, "clk_xin", &sc->clk_xin);
259b24594e5SGanbold Tsagaankhuu 	if (error != 0) {
260b24594e5SGanbold Tsagaankhuu 		device_printf(dev, "cannot get xin clock\n");
261b24594e5SGanbold Tsagaankhuu 		return (ENXIO);
262b24594e5SGanbold Tsagaankhuu 	}
263b24594e5SGanbold Tsagaankhuu 	error = clk_enable(sc->clk_xin);
264b24594e5SGanbold Tsagaankhuu 	if (error != 0) {
265b24594e5SGanbold Tsagaankhuu 		device_printf(dev, "cannot enable xin clock\n");
266b24594e5SGanbold Tsagaankhuu 		return (ENXIO);
267b24594e5SGanbold Tsagaankhuu 	}
268b24594e5SGanbold Tsagaankhuu 	error = clk_get_by_ofw_name(dev, 0, "clk_ahb", &sc->clk_ahb);
269b24594e5SGanbold Tsagaankhuu 	if (error != 0) {
270b24594e5SGanbold Tsagaankhuu 		device_printf(dev, "cannot get ahb clock\n");
271b24594e5SGanbold Tsagaankhuu 		return (ENXIO);
272b24594e5SGanbold Tsagaankhuu 	}
273b24594e5SGanbold Tsagaankhuu 	error = clk_enable(sc->clk_ahb);
274b24594e5SGanbold Tsagaankhuu 	if (error != 0) {
275b24594e5SGanbold Tsagaankhuu 		device_printf(dev, "cannot enable ahb clock\n");
276b24594e5SGanbold Tsagaankhuu 		return (ENXIO);
277b24594e5SGanbold Tsagaankhuu 	}
278b24594e5SGanbold Tsagaankhuu 
2799377d704SEmmanuel Vadot 	return (0);
2809377d704SEmmanuel Vadot }
2819377d704SEmmanuel Vadot 
2829377d704SEmmanuel Vadot static int
sdhci_init_phy(struct sdhci_fdt_softc * sc)2830ee5d6fcSEmmanuel Vadot sdhci_init_phy(struct sdhci_fdt_softc *sc)
2840ee5d6fcSEmmanuel Vadot {
2850ee5d6fcSEmmanuel Vadot 	int error;
2860ee5d6fcSEmmanuel Vadot 
2870ee5d6fcSEmmanuel Vadot 	/* Enable PHY */
2880ee5d6fcSEmmanuel Vadot 	error = phy_get_by_ofw_name(sc->dev, 0, "phy_arasan", &sc->phy);
2890ee5d6fcSEmmanuel Vadot 	if (error == ENOENT)
2900ee5d6fcSEmmanuel Vadot 		return (0);
2910ee5d6fcSEmmanuel Vadot 	if (error != 0) {
2920ee5d6fcSEmmanuel Vadot 		device_printf(sc->dev, "Could not get phy\n");
2930ee5d6fcSEmmanuel Vadot 		return (ENXIO);
2940ee5d6fcSEmmanuel Vadot 	}
2950ee5d6fcSEmmanuel Vadot 	error = phy_enable(sc->phy);
2960ee5d6fcSEmmanuel Vadot 	if (error != 0) {
2970ee5d6fcSEmmanuel Vadot 		device_printf(sc->dev, "Could not enable phy\n");
2980ee5d6fcSEmmanuel Vadot 		return (ENXIO);
2990ee5d6fcSEmmanuel Vadot 	}
3000ee5d6fcSEmmanuel Vadot 
3010ee5d6fcSEmmanuel Vadot 	return (0);
3020ee5d6fcSEmmanuel Vadot }
3030ee5d6fcSEmmanuel Vadot 
3040ee5d6fcSEmmanuel Vadot static int
sdhci_get_syscon(struct sdhci_fdt_softc * sc)3050ee5d6fcSEmmanuel Vadot sdhci_get_syscon(struct sdhci_fdt_softc *sc)
3060ee5d6fcSEmmanuel Vadot {
3070ee5d6fcSEmmanuel Vadot 	phandle_t node;
3080ee5d6fcSEmmanuel Vadot 
3090ee5d6fcSEmmanuel Vadot 	/* Get syscon */
3100ee5d6fcSEmmanuel Vadot 	node = ofw_bus_get_node(sc->dev);
3110ee5d6fcSEmmanuel Vadot 	if (OF_hasprop(node, "arasan,soc-ctl-syscon") &&
3120ee5d6fcSEmmanuel Vadot 	    syscon_get_by_ofw_property(sc->dev, node,
3130ee5d6fcSEmmanuel Vadot 	    "arasan,soc-ctl-syscon", &sc->syscon) != 0) {
3140ee5d6fcSEmmanuel Vadot 		device_printf(sc->dev, "cannot get syscon handle\n");
3150ee5d6fcSEmmanuel Vadot 		return (ENXIO);
3160ee5d6fcSEmmanuel Vadot 	}
3170ee5d6fcSEmmanuel Vadot 
3180ee5d6fcSEmmanuel Vadot 	return (0);
3190ee5d6fcSEmmanuel Vadot }
3200ee5d6fcSEmmanuel Vadot 
3210ee5d6fcSEmmanuel Vadot static int
sdhci_init_rk3399(device_t dev)3229377d704SEmmanuel Vadot sdhci_init_rk3399(device_t dev)
3239377d704SEmmanuel Vadot {
3249377d704SEmmanuel Vadot 	struct sdhci_fdt_softc *sc = device_get_softc(dev);
3259377d704SEmmanuel Vadot 	uint64_t freq;
3269377d704SEmmanuel Vadot 	uint32_t mask, val;
3279377d704SEmmanuel Vadot 	int error;
3289377d704SEmmanuel Vadot 
3299377d704SEmmanuel Vadot 	error = clk_get_freq(sc->clk_xin, &freq);
3309377d704SEmmanuel Vadot 	if (error != 0) {
3319377d704SEmmanuel Vadot 		device_printf(dev, "cannot get xin clock frequency\n");
3329377d704SEmmanuel Vadot 		return (ENXIO);
3339377d704SEmmanuel Vadot 	}
3349377d704SEmmanuel Vadot 
335b24594e5SGanbold Tsagaankhuu 	/* Disable clock multiplier */
336b24594e5SGanbold Tsagaankhuu 	mask = RK3399_CORECFG_CLOCKMULTIPLIER;
337b24594e5SGanbold Tsagaankhuu 	val = 0;
3380ee5d6fcSEmmanuel Vadot 	SYSCON_WRITE_4(sc->syscon, RK3399_GRF_EMMCCORE_CON11, (mask << 16) | val);
339b24594e5SGanbold Tsagaankhuu 
340b24594e5SGanbold Tsagaankhuu 	/* Set base clock frequency */
341b24594e5SGanbold Tsagaankhuu 	mask = RK3399_CORECFG_BASECLKFREQ;
342b24594e5SGanbold Tsagaankhuu 	val = SHIFTIN((freq + (1000000 / 2)) / 1000000,
343b24594e5SGanbold Tsagaankhuu 	    RK3399_CORECFG_BASECLKFREQ);
3440ee5d6fcSEmmanuel Vadot 	SYSCON_WRITE_4(sc->syscon, RK3399_GRF_EMMCCORE_CON0, (mask << 16) | val);
345b24594e5SGanbold Tsagaankhuu 
346b24594e5SGanbold Tsagaankhuu 	return (0);
347b24594e5SGanbold Tsagaankhuu }
348b24594e5SGanbold Tsagaankhuu 
349a9caca6aSWojciech A. Koszek static uint8_t
sdhci_fdt_read_1(device_t dev,struct sdhci_slot * slot,bus_size_t off)350a9caca6aSWojciech A. Koszek sdhci_fdt_read_1(device_t dev, struct sdhci_slot *slot, bus_size_t off)
351a9caca6aSWojciech A. Koszek {
352a9caca6aSWojciech A. Koszek 	struct sdhci_fdt_softc *sc = device_get_softc(dev);
3537e6ccea3SMarius Strobl 
354a9caca6aSWojciech A. Koszek 	return (bus_read_1(sc->mem_res[slot->num], off));
355a9caca6aSWojciech A. Koszek }
356a9caca6aSWojciech A. Koszek 
357a9caca6aSWojciech A. Koszek static void
sdhci_fdt_write_1(device_t dev,struct sdhci_slot * slot,bus_size_t off,uint8_t val)358a9caca6aSWojciech A. Koszek sdhci_fdt_write_1(device_t dev, struct sdhci_slot *slot, bus_size_t off,
359a9caca6aSWojciech A. Koszek     uint8_t val)
360a9caca6aSWojciech A. Koszek {
361a9caca6aSWojciech A. Koszek 	struct sdhci_fdt_softc *sc = device_get_softc(dev);
3627e6ccea3SMarius Strobl 
363a9caca6aSWojciech A. Koszek 	bus_write_1(sc->mem_res[slot->num], off, val);
364a9caca6aSWojciech A. Koszek }
365a9caca6aSWojciech A. Koszek 
366a9caca6aSWojciech A. Koszek static uint16_t
sdhci_fdt_read_2(device_t dev,struct sdhci_slot * slot,bus_size_t off)367a9caca6aSWojciech A. Koszek sdhci_fdt_read_2(device_t dev, struct sdhci_slot *slot, bus_size_t off)
368a9caca6aSWojciech A. Koszek {
369a9caca6aSWojciech A. Koszek 	struct sdhci_fdt_softc *sc = device_get_softc(dev);
3707e6ccea3SMarius Strobl 
371a9caca6aSWojciech A. Koszek 	return (bus_read_2(sc->mem_res[slot->num], off));
372a9caca6aSWojciech A. Koszek }
373a9caca6aSWojciech A. Koszek 
374a9caca6aSWojciech A. Koszek static void
sdhci_fdt_write_2(device_t dev,struct sdhci_slot * slot,bus_size_t off,uint16_t val)375a9caca6aSWojciech A. Koszek sdhci_fdt_write_2(device_t dev, struct sdhci_slot *slot, bus_size_t off,
376a9caca6aSWojciech A. Koszek     uint16_t val)
377a9caca6aSWojciech A. Koszek {
378a9caca6aSWojciech A. Koszek 	struct sdhci_fdt_softc *sc = device_get_softc(dev);
3797e6ccea3SMarius Strobl 
380a9caca6aSWojciech A. Koszek 	bus_write_2(sc->mem_res[slot->num], off, val);
381a9caca6aSWojciech A. Koszek }
382a9caca6aSWojciech A. Koszek 
383a9caca6aSWojciech A. Koszek static uint32_t
sdhci_fdt_read_4(device_t dev,struct sdhci_slot * slot,bus_size_t off)384a9caca6aSWojciech A. Koszek sdhci_fdt_read_4(device_t dev, struct sdhci_slot *slot, bus_size_t off)
385a9caca6aSWojciech A. Koszek {
386a9caca6aSWojciech A. Koszek 	struct sdhci_fdt_softc *sc = device_get_softc(dev);
387cd394501SLuiz Otavio O Souza 	uint32_t val32;
3887e6ccea3SMarius Strobl 
389cd394501SLuiz Otavio O Souza 	val32 = bus_read_4(sc->mem_res[slot->num], off);
390cd394501SLuiz Otavio O Souza 	if (off == SDHCI_CAPABILITIES && sc->no_18v)
391cd394501SLuiz Otavio O Souza 		val32 &= ~SDHCI_CAN_VDD_180;
392cd394501SLuiz Otavio O Souza 
393cd394501SLuiz Otavio O Souza 	return (val32);
394a9caca6aSWojciech A. Koszek }
395a9caca6aSWojciech A. Koszek 
396a9caca6aSWojciech A. Koszek static void
sdhci_fdt_write_4(device_t dev,struct sdhci_slot * slot,bus_size_t off,uint32_t val)397a9caca6aSWojciech A. Koszek sdhci_fdt_write_4(device_t dev, struct sdhci_slot *slot, bus_size_t off,
398a9caca6aSWojciech A. Koszek     uint32_t val)
399a9caca6aSWojciech A. Koszek {
400a9caca6aSWojciech A. Koszek 	struct sdhci_fdt_softc *sc = device_get_softc(dev);
4017e6ccea3SMarius Strobl 
402a9caca6aSWojciech A. Koszek 	bus_write_4(sc->mem_res[slot->num], off, val);
403a9caca6aSWojciech A. Koszek }
404a9caca6aSWojciech A. Koszek 
405a9caca6aSWojciech A. Koszek static void
sdhci_fdt_read_multi_4(device_t dev,struct sdhci_slot * slot,bus_size_t off,uint32_t * data,bus_size_t count)406a9caca6aSWojciech A. Koszek sdhci_fdt_read_multi_4(device_t dev, struct sdhci_slot *slot,
407a9caca6aSWojciech A. Koszek     bus_size_t off, uint32_t *data, bus_size_t count)
408a9caca6aSWojciech A. Koszek {
409a9caca6aSWojciech A. Koszek 	struct sdhci_fdt_softc *sc = device_get_softc(dev);
4107e6ccea3SMarius Strobl 
411a9caca6aSWojciech A. Koszek 	bus_read_multi_4(sc->mem_res[slot->num], off, data, count);
412a9caca6aSWojciech A. Koszek }
413a9caca6aSWojciech A. Koszek 
414a9caca6aSWojciech A. Koszek static void
sdhci_fdt_write_multi_4(device_t dev,struct sdhci_slot * slot,bus_size_t off,uint32_t * data,bus_size_t count)415a9caca6aSWojciech A. Koszek sdhci_fdt_write_multi_4(device_t dev, struct sdhci_slot *slot,
416a9caca6aSWojciech A. Koszek     bus_size_t off, uint32_t *data, bus_size_t count)
417a9caca6aSWojciech A. Koszek {
418a9caca6aSWojciech A. Koszek 	struct sdhci_fdt_softc *sc = device_get_softc(dev);
4197e6ccea3SMarius Strobl 
420a9caca6aSWojciech A. Koszek 	bus_write_multi_4(sc->mem_res[slot->num], off, data, count);
421a9caca6aSWojciech A. Koszek }
422a9caca6aSWojciech A. Koszek 
423a9caca6aSWojciech A. Koszek static void
sdhci_fdt_intr(void * arg)424a9caca6aSWojciech A. Koszek sdhci_fdt_intr(void *arg)
425a9caca6aSWojciech A. Koszek {
426a9caca6aSWojciech A. Koszek 	struct sdhci_fdt_softc *sc = (struct sdhci_fdt_softc *)arg;
427a9caca6aSWojciech A. Koszek 	int i;
428a9caca6aSWojciech A. Koszek 
4297e6ccea3SMarius Strobl 	for (i = 0; i < sc->num_slots; i++)
4307e6ccea3SMarius Strobl 		sdhci_generic_intr(&sc->slots[i]);
431a9caca6aSWojciech A. Koszek }
432a9caca6aSWojciech A. Koszek 
433a9caca6aSWojciech A. Koszek static int
sdhci_fdt_get_ro(device_t bus,device_t dev)434cd394501SLuiz Otavio O Souza sdhci_fdt_get_ro(device_t bus, device_t dev)
435cd394501SLuiz Otavio O Souza {
436cd394501SLuiz Otavio O Souza 	struct sdhci_fdt_softc *sc = device_get_softc(bus);
437cd394501SLuiz Otavio O Souza 
438e758ed2dSEmmanuel Vadot 	if (sc->wp_disabled)
439e758ed2dSEmmanuel Vadot 		return (false);
440cd394501SLuiz Otavio O Souza 	return (sdhci_generic_get_ro(bus, dev) ^ sc->wp_inverted);
441cd394501SLuiz Otavio O Souza }
442cd394501SLuiz Otavio O Souza 
443cd394501SLuiz Otavio O Souza static int
sdhci_fdt_set_clock(device_t dev,struct sdhci_slot * slot,int clock)444e00774a9SSøren Schmidt sdhci_fdt_set_clock(device_t dev, struct sdhci_slot *slot, int clock)
445e00774a9SSøren Schmidt {
446e00774a9SSøren Schmidt 	struct sdhci_fdt_softc *sc = device_get_softc(dev);
447e00774a9SSøren Schmidt 	int32_t val;
448e00774a9SSøren Schmidt 	int i;
449e00774a9SSøren Schmidt 
450e00774a9SSøren Schmidt 	if (ofw_bus_search_compatible(dev, compat_data)->ocd_data ==
451e00774a9SSøren Schmidt 	    SDHCI_FDT_RK3568) {
452e00774a9SSøren Schmidt 		if (clock == 400000)
453e00774a9SSøren Schmidt 			clock = 375000;
454e00774a9SSøren Schmidt 
455e00774a9SSøren Schmidt 		if (clock) {
456e00774a9SSøren Schmidt 			clk_set_freq(sc->clk_core, clock, 0);
457e00774a9SSøren Schmidt 
458e00774a9SSøren Schmidt 			if (clock <= 52000000) {
459e00774a9SSøren Schmidt 				bus_write_4(sc->mem_res[slot->num],
460e00774a9SSøren Schmidt 				    RK3568_EMMC_DLL_CTRL, 0x0);
461e00774a9SSøren Schmidt 				bus_write_4(sc->mem_res[slot->num],
462e00774a9SSøren Schmidt 				    RK3568_EMMC_DLL_RXCLK, DLL_RXCLK_NO_INV);
463e00774a9SSøren Schmidt 				bus_write_4(sc->mem_res[slot->num],
464e00774a9SSøren Schmidt 				    RK3568_EMMC_DLL_TXCLK, 0x0);
465e00774a9SSøren Schmidt 				bus_write_4(sc->mem_res[slot->num],
466e00774a9SSøren Schmidt 				    RK3568_EMMC_DLL_STRBIN, 0x0);
467e00774a9SSøren Schmidt 				return (clock);
468e00774a9SSøren Schmidt 			}
469e00774a9SSøren Schmidt 
470e00774a9SSøren Schmidt 			bus_write_4(sc->mem_res[slot->num],
471e00774a9SSøren Schmidt 			    RK3568_EMMC_DLL_CTRL, DLL_CTRL_START);
472e00774a9SSøren Schmidt 			DELAY(1000);
473e00774a9SSøren Schmidt 			bus_write_4(sc->mem_res[slot->num],
474e00774a9SSøren Schmidt 			    RK3568_EMMC_DLL_CTRL, 0);
475e00774a9SSøren Schmidt 			bus_write_4(sc->mem_res[slot->num],
476e00774a9SSøren Schmidt 			    RK3568_EMMC_DLL_CTRL, DLL_CTRL_START_POINT_DEFAULT |
477e00774a9SSøren Schmidt 			    DLL_CTRL_INCREMENT_DEFAULT | DLL_CTRL_START);
478e00774a9SSøren Schmidt 			for (i = 0; i < 500; i++) {
479e00774a9SSøren Schmidt 				val = bus_read_4(sc->mem_res[slot->num],
480e00774a9SSøren Schmidt 				    RK3568_EMMC_DLL_STATUS0);
481e00774a9SSøren Schmidt 				if (val & DLL_STATUS0_DLL_LOCK &&
482e00774a9SSøren Schmidt 				    !(val & DLL_STATUS0_DLL_TIMEOUT))
483e00774a9SSøren Schmidt 					break;
484e00774a9SSøren Schmidt 				DELAY(1000);
485e00774a9SSøren Schmidt 			}
486e00774a9SSøren Schmidt 			bus_write_4(sc->mem_res[slot->num], RK3568_EMMC_ATCTRL,
487e00774a9SSøren Schmidt 			    (0x1 << 16 | 0x2 << 17 | 0x3 << 19));
488e00774a9SSøren Schmidt 			bus_write_4(sc->mem_res[slot->num],
489e00774a9SSøren Schmidt 			    RK3568_EMMC_DLL_RXCLK,
490e00774a9SSøren Schmidt 			    DLL_RXCLK_DELAY_ENABLE | DLL_RXCLK_NO_INV);
491e00774a9SSøren Schmidt 			bus_write_4(sc->mem_res[slot->num],
492e00774a9SSøren Schmidt 			    RK3568_EMMC_DLL_TXCLK, DLL_TXCLK_DELAY_ENABLE |
493e00774a9SSøren Schmidt 			    DLL_TXCLK_TAPNUM_DEFAULT|DLL_TXCLK_TAPNUM_FROM_SW);
494e00774a9SSøren Schmidt 			bus_write_4(sc->mem_res[slot->num],
495e00774a9SSøren Schmidt 			    RK3568_EMMC_DLL_STRBIN, DLL_STRBIN_DELAY_ENABLE |
496e00774a9SSøren Schmidt 			    DLL_STRBIN_TAPNUM_DEFAULT |
497e00774a9SSøren Schmidt 			    DLL_STRBIN_TAPNUM_FROM_SW);
498e00774a9SSøren Schmidt 		}
499e00774a9SSøren Schmidt 	}
500e00774a9SSøren Schmidt 	return (clock);
501e00774a9SSøren Schmidt }
502e00774a9SSøren Schmidt 
503e00774a9SSøren Schmidt static int
sdhci_fdt_probe(device_t dev)504a9caca6aSWojciech A. Koszek sdhci_fdt_probe(device_t dev)
505a9caca6aSWojciech A. Koszek {
506a9caca6aSWojciech A. Koszek 	struct sdhci_fdt_softc *sc = device_get_softc(dev);
507a9caca6aSWojciech A. Koszek 	phandle_t node;
508a9caca6aSWojciech A. Koszek 	pcell_t cid;
509a9caca6aSWojciech A. Koszek 
510a9caca6aSWojciech A. Koszek 	sc->quirks = 0;
511a9caca6aSWojciech A. Koszek 	sc->num_slots = 1;
512e625c10bSIan Lepore 	sc->max_clk = 0;
513a9caca6aSWojciech A. Koszek 
514add35ed5SIan Lepore 	if (!ofw_bus_status_okay(dev))
515add35ed5SIan Lepore 		return (ENXIO);
516add35ed5SIan Lepore 
517018101a8SLuiz Otavio O Souza 	switch (ofw_bus_search_compatible(dev, compat_data)->ocd_data) {
518018101a8SLuiz Otavio O Souza 	case SDHCI_FDT_ARMADA38X:
519018101a8SLuiz Otavio O Souza 		sc->quirks = SDHCI_QUIRK_BROKEN_AUTO_STOP;
520018101a8SLuiz Otavio O Souza 		device_set_desc(dev, "ARMADA38X SDHCI controller");
521018101a8SLuiz Otavio O Souza 		break;
5223d22784fSRuslan Bukin 	case SDHCI_FDT_QUALCOMM:
523ab00a509SMarius Strobl 		sc->quirks = SDHCI_QUIRK_ALL_SLOTS_NON_REMOVABLE |
524ab00a509SMarius Strobl 		    SDHCI_QUIRK_BROKEN_SDMA_BOUNDARY;
525ab00a509SMarius Strobl 		sc->sdma_boundary = SDHCI_BLKSZ_SDMA_BNDRY_4K;
5263d22784fSRuslan Bukin 		device_set_desc(dev, "Qualcomm FDT SDHCI controller");
5273d22784fSRuslan Bukin 		break;
528b24594e5SGanbold Tsagaankhuu 	case SDHCI_FDT_RK3399:
529b24594e5SGanbold Tsagaankhuu 		device_set_desc(dev, "Rockchip RK3399 fdt SDHCI controller");
530b24594e5SGanbold Tsagaankhuu 		break;
531018101a8SLuiz Otavio O Souza 	case SDHCI_FDT_XLNX_ZY7:
532a9caca6aSWojciech A. Koszek 		sc->quirks = SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK;
533a9caca6aSWojciech A. Koszek 		device_set_desc(dev, "Zynq-7000 generic fdt SDHCI controller");
534018101a8SLuiz Otavio O Souza 		break;
535e00774a9SSøren Schmidt 	case SDHCI_FDT_RK3568:
536e00774a9SSøren Schmidt 		device_set_desc(dev, "Rockchip RK3568 fdt SDHCI controller");
537e00774a9SSøren Schmidt 		break;
538b426119aSEmmanuel Vadot 	case SDHCI_FDT_XLNX_ZMP:
539b426119aSEmmanuel Vadot 		device_set_desc(dev, "ZynqMP generic fdt SDHCI controller");
540b426119aSEmmanuel Vadot 		break;
541018101a8SLuiz Otavio O Souza 	default:
542a9caca6aSWojciech A. Koszek 		return (ENXIO);
543018101a8SLuiz Otavio O Souza 	}
544a9caca6aSWojciech A. Koszek 
545a9caca6aSWojciech A. Koszek 	node = ofw_bus_get_node(dev);
546a9caca6aSWojciech A. Koszek 
547e625c10bSIan Lepore 	/* Allow dts to patch quirks, slots, and max-frequency. */
548e625c10bSIan Lepore 	if ((OF_getencprop(node, "quirks", &cid, sizeof(cid))) > 0)
549e625c10bSIan Lepore 		sc->quirks = cid;
550e625c10bSIan Lepore 	if ((OF_getencprop(node, "num-slots", &cid, sizeof(cid))) > 0)
551e625c10bSIan Lepore 		sc->num_slots = cid;
552e625c10bSIan Lepore 	if ((OF_getencprop(node, "max-frequency", &cid, sizeof(cid))) > 0)
553e625c10bSIan Lepore 		sc->max_clk = cid;
554cd394501SLuiz Otavio O Souza 	if (OF_hasprop(node, "no-1-8-v"))
555cd394501SLuiz Otavio O Souza 		sc->no_18v = true;
556cd394501SLuiz Otavio O Souza 	if (OF_hasprop(node, "wp-inverted"))
557cd394501SLuiz Otavio O Souza 		sc->wp_inverted = true;
558e758ed2dSEmmanuel Vadot 	if (OF_hasprop(node, "disable-wp"))
559e758ed2dSEmmanuel Vadot 		sc->wp_disabled = true;
560e625c10bSIan Lepore 
561a9caca6aSWojciech A. Koszek 	return (0);
562a9caca6aSWojciech A. Koszek }
563a9caca6aSWojciech A. Koszek 
564a9caca6aSWojciech A. Koszek static int
sdhci_fdt_attach(device_t dev)565a9caca6aSWojciech A. Koszek sdhci_fdt_attach(device_t dev)
566a9caca6aSWojciech A. Koszek {
567a9caca6aSWojciech A. Koszek 	struct sdhci_fdt_softc *sc = device_get_softc(dev);
5687e6ccea3SMarius Strobl 	struct sdhci_slot *slot;
5699377d704SEmmanuel Vadot 	int err, slots, rid, i, compat;
570a9caca6aSWojciech A. Koszek 
571a9caca6aSWojciech A. Koszek 	sc->dev = dev;
572a9caca6aSWojciech A. Koszek 
573a9caca6aSWojciech A. Koszek 	/* Allocate IRQ. */
574a9caca6aSWojciech A. Koszek 	rid = 0;
575a9caca6aSWojciech A. Koszek 	sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
576a9caca6aSWojciech A. Koszek 	    RF_ACTIVE);
577a9caca6aSWojciech A. Koszek 	if (sc->irq_res == NULL) {
578a9caca6aSWojciech A. Koszek 		device_printf(dev, "Can't allocate IRQ\n");
579a9caca6aSWojciech A. Koszek 		return (ENOMEM);
580a9caca6aSWojciech A. Koszek 	}
581a9caca6aSWojciech A. Koszek 
5829377d704SEmmanuel Vadot 	compat = ofw_bus_search_compatible(dev, compat_data)->ocd_data;
5839377d704SEmmanuel Vadot 	switch (compat) {
5849377d704SEmmanuel Vadot 	case SDHCI_FDT_RK3399:
5859377d704SEmmanuel Vadot 	case SDHCI_FDT_XLNX_ZMP:
5869377d704SEmmanuel Vadot 		err = sdhci_init_clocks(dev);
5879377d704SEmmanuel Vadot 		if (err != 0) {
5889377d704SEmmanuel Vadot 			device_printf(dev, "Cannot init clocks\n");
5899377d704SEmmanuel Vadot 			return (err);
5909377d704SEmmanuel Vadot 		}
59181a4fe38SEmmanuel Vadot 		sdhci_export_clocks(sc);
5920ee5d6fcSEmmanuel Vadot 		if ((err = sdhci_init_phy(sc)) != 0) {
5930ee5d6fcSEmmanuel Vadot 			device_printf(dev, "Cannot init phy\n");
5940ee5d6fcSEmmanuel Vadot 			return (err);
5950ee5d6fcSEmmanuel Vadot 		}
5960ee5d6fcSEmmanuel Vadot 		if ((err = sdhci_get_syscon(sc)) != 0) {
5970ee5d6fcSEmmanuel Vadot 			device_printf(dev, "Cannot get syscon handle\n");
5980ee5d6fcSEmmanuel Vadot 			return (err);
5990ee5d6fcSEmmanuel Vadot 		}
6009377d704SEmmanuel Vadot 		if (compat == SDHCI_FDT_RK3399) {
601b24594e5SGanbold Tsagaankhuu 			err = sdhci_init_rk3399(dev);
602b24594e5SGanbold Tsagaankhuu 			if (err != 0) {
603b24594e5SGanbold Tsagaankhuu 				device_printf(dev, "Cannot init RK3399 SDHCI\n");
604b24594e5SGanbold Tsagaankhuu 				return (err);
605b24594e5SGanbold Tsagaankhuu 			}
606b24594e5SGanbold Tsagaankhuu 		}
6079377d704SEmmanuel Vadot 		break;
6089377d704SEmmanuel Vadot 	case SDHCI_FDT_RK3568:
609e00774a9SSøren Schmidt 		/* setup & enable clocks */
610e00774a9SSøren Schmidt 		if (clk_get_by_ofw_name(dev, 0, "core", &sc->clk_core)) {
611e00774a9SSøren Schmidt 			device_printf(dev, "cannot get core clock\n");
612e00774a9SSøren Schmidt 			return (ENXIO);
613e00774a9SSøren Schmidt 		}
614e00774a9SSøren Schmidt 		clk_enable(sc->clk_core);
6159377d704SEmmanuel Vadot 		break;
6169377d704SEmmanuel Vadot 	default:
6179377d704SEmmanuel Vadot 		break;
618e00774a9SSøren Schmidt 	}
619e00774a9SSøren Schmidt 
620a9caca6aSWojciech A. Koszek 	/* Scan all slots. */
621a9caca6aSWojciech A. Koszek 	slots = sc->num_slots;	/* number of slots determined in probe(). */
622a9caca6aSWojciech A. Koszek 	sc->num_slots = 0;
623a9caca6aSWojciech A. Koszek 	for (i = 0; i < slots; i++) {
6247e6ccea3SMarius Strobl 		slot = &sc->slots[sc->num_slots];
625a9caca6aSWojciech A. Koszek 
626a9caca6aSWojciech A. Koszek 		/* Allocate memory. */
627a9caca6aSWojciech A. Koszek 		rid = 0;
628a9caca6aSWojciech A. Koszek 		sc->mem_res[i] = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
629a9caca6aSWojciech A. Koszek 							&rid, RF_ACTIVE);
630a9caca6aSWojciech A. Koszek 		if (sc->mem_res[i] == NULL) {
6317e6ccea3SMarius Strobl 			device_printf(dev,
6327e6ccea3SMarius Strobl 			    "Can't allocate memory for slot %d\n", i);
633a9caca6aSWojciech A. Koszek 			continue;
634a9caca6aSWojciech A. Koszek 		}
635a9caca6aSWojciech A. Koszek 
636a9caca6aSWojciech A. Koszek 		slot->quirks = sc->quirks;
637a9caca6aSWojciech A. Koszek 		slot->caps = sc->caps;
638e625c10bSIan Lepore 		slot->max_clk = sc->max_clk;
639ab00a509SMarius Strobl 		slot->sdma_boundary = sc->sdma_boundary;
640a9caca6aSWojciech A. Koszek 
641a9caca6aSWojciech A. Koszek 		if (sdhci_init_slot(dev, slot, i) != 0)
642a9caca6aSWojciech A. Koszek 			continue;
643a9caca6aSWojciech A. Koszek 
644a9caca6aSWojciech A. Koszek 		sc->num_slots++;
645a9caca6aSWojciech A. Koszek 	}
646a9caca6aSWojciech A. Koszek 	device_printf(dev, "%d slot(s) allocated\n", sc->num_slots);
647a9caca6aSWojciech A. Koszek 
648a9caca6aSWojciech A. Koszek 	/* Activate the interrupt */
649a9caca6aSWojciech A. Koszek 	err = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_MISC | INTR_MPSAFE,
650a9caca6aSWojciech A. Koszek 	    NULL, sdhci_fdt_intr, sc, &sc->intrhand);
651a9caca6aSWojciech A. Koszek 	if (err) {
652a9caca6aSWojciech A. Koszek 		device_printf(dev, "Cannot setup IRQ\n");
653a9caca6aSWojciech A. Koszek 		return (err);
654a9caca6aSWojciech A. Koszek 	}
655a9caca6aSWojciech A. Koszek 
656a9caca6aSWojciech A. Koszek 	/* Process cards detection. */
6577e6ccea3SMarius Strobl 	for (i = 0; i < sc->num_slots; i++)
6587e6ccea3SMarius Strobl 		sdhci_start_slot(&sc->slots[i]);
659a9caca6aSWojciech A. Koszek 
660a9caca6aSWojciech A. Koszek 	return (0);
661a9caca6aSWojciech A. Koszek }
662a9caca6aSWojciech A. Koszek 
663a9caca6aSWojciech A. Koszek static int
sdhci_fdt_detach(device_t dev)664a9caca6aSWojciech A. Koszek sdhci_fdt_detach(device_t dev)
665a9caca6aSWojciech A. Koszek {
666a9caca6aSWojciech A. Koszek 	struct sdhci_fdt_softc *sc = device_get_softc(dev);
667a9caca6aSWojciech A. Koszek 	int i;
668a9caca6aSWojciech A. Koszek 
669a9caca6aSWojciech A. Koszek 	bus_generic_detach(dev);
670a9caca6aSWojciech A. Koszek 	bus_teardown_intr(dev, sc->irq_res, sc->intrhand);
671a9caca6aSWojciech A. Koszek 	bus_release_resource(dev, SYS_RES_IRQ, rman_get_rid(sc->irq_res),
672a9caca6aSWojciech A. Koszek 	    sc->irq_res);
673a9caca6aSWojciech A. Koszek 
674a9caca6aSWojciech A. Koszek 	for (i = 0; i < sc->num_slots; i++) {
6757e6ccea3SMarius Strobl 		sdhci_cleanup_slot(&sc->slots[i]);
676a9caca6aSWojciech A. Koszek 		bus_release_resource(dev, SYS_RES_MEMORY,
6777e6ccea3SMarius Strobl 		    rman_get_rid(sc->mem_res[i]), sc->mem_res[i]);
678a9caca6aSWojciech A. Koszek 	}
679a9caca6aSWojciech A. Koszek 
680a9caca6aSWojciech A. Koszek 	return (0);
681a9caca6aSWojciech A. Koszek }
682a9caca6aSWojciech A. Koszek 
683a9caca6aSWojciech A. Koszek static device_method_t sdhci_fdt_methods[] = {
684a9caca6aSWojciech A. Koszek 	/* device_if */
685a9caca6aSWojciech A. Koszek 	DEVMETHOD(device_probe,		sdhci_fdt_probe),
686a9caca6aSWojciech A. Koszek 	DEVMETHOD(device_attach,	sdhci_fdt_attach),
687a9caca6aSWojciech A. Koszek 	DEVMETHOD(device_detach,	sdhci_fdt_detach),
688a9caca6aSWojciech A. Koszek 
689a9caca6aSWojciech A. Koszek 	/* Bus interface */
690a9caca6aSWojciech A. Koszek 	DEVMETHOD(bus_read_ivar,	sdhci_generic_read_ivar),
691a9caca6aSWojciech A. Koszek 	DEVMETHOD(bus_write_ivar,	sdhci_generic_write_ivar),
692a9caca6aSWojciech A. Koszek 
693a9caca6aSWojciech A. Koszek 	/* mmcbr_if */
694a9caca6aSWojciech A. Koszek 	DEVMETHOD(mmcbr_update_ios,	sdhci_generic_update_ios),
695a9caca6aSWojciech A. Koszek 	DEVMETHOD(mmcbr_request,	sdhci_generic_request),
696cd394501SLuiz Otavio O Souza 	DEVMETHOD(mmcbr_get_ro,		sdhci_fdt_get_ro),
697a9caca6aSWojciech A. Koszek 	DEVMETHOD(mmcbr_acquire_host,	sdhci_generic_acquire_host),
698a9caca6aSWojciech A. Koszek 	DEVMETHOD(mmcbr_release_host,	sdhci_generic_release_host),
699a9caca6aSWojciech A. Koszek 
700a9caca6aSWojciech A. Koszek 	/* SDHCI registers accessors */
701a9caca6aSWojciech A. Koszek 	DEVMETHOD(sdhci_read_1,		sdhci_fdt_read_1),
702a9caca6aSWojciech A. Koszek 	DEVMETHOD(sdhci_read_2,		sdhci_fdt_read_2),
703a9caca6aSWojciech A. Koszek 	DEVMETHOD(sdhci_read_4,		sdhci_fdt_read_4),
704a9caca6aSWojciech A. Koszek 	DEVMETHOD(sdhci_read_multi_4,	sdhci_fdt_read_multi_4),
705a9caca6aSWojciech A. Koszek 	DEVMETHOD(sdhci_write_1,	sdhci_fdt_write_1),
706a9caca6aSWojciech A. Koszek 	DEVMETHOD(sdhci_write_2,	sdhci_fdt_write_2),
707a9caca6aSWojciech A. Koszek 	DEVMETHOD(sdhci_write_4,	sdhci_fdt_write_4),
708a9caca6aSWojciech A. Koszek 	DEVMETHOD(sdhci_write_multi_4,	sdhci_fdt_write_multi_4),
709e00774a9SSøren Schmidt 	DEVMETHOD(sdhci_set_clock,	sdhci_fdt_set_clock),
710a9caca6aSWojciech A. Koszek 
711a9caca6aSWojciech A. Koszek 	DEVMETHOD_END
712a9caca6aSWojciech A. Koszek };
713a9caca6aSWojciech A. Koszek 
714a9caca6aSWojciech A. Koszek static driver_t sdhci_fdt_driver = {
715a9caca6aSWojciech A. Koszek 	"sdhci_fdt",
716a9caca6aSWojciech A. Koszek 	sdhci_fdt_methods,
717a9caca6aSWojciech A. Koszek 	sizeof(struct sdhci_fdt_softc),
718a9caca6aSWojciech A. Koszek };
719a9caca6aSWojciech A. Koszek 
7208f35a52dSJohn Baldwin DRIVER_MODULE(sdhci_fdt, simplebus, sdhci_fdt_driver, NULL, NULL);
721ab00a509SMarius Strobl SDHCI_DEPEND(sdhci_fdt);
722b83d1009SEmmanuel Vadot #ifndef MMCCAM
72355dae242SMarius Strobl MMC_DECLARE_BRIDGE(sdhci_fdt);
724d597cfd5SEmmanuel Vadot #endif
725