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