arasan_sdhc_fdt.c revision 1.5 1 /* $NetBSD: arasan_sdhc_fdt.c,v 1.5 2021/01/18 02:35:49 thorpej Exp $ */
2
3 /*-
4 * Copyright (c) 2019 Jared McNeill <jmcneill (at) invisible.ca>
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
23 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29 #include <sys/cdefs.h>
30 __KERNEL_RCSID(0, "$NetBSD: arasan_sdhc_fdt.c,v 1.5 2021/01/18 02:35:49 thorpej Exp $");
31
32 #include <sys/param.h>
33 #include <sys/bus.h>
34 #include <sys/device.h>
35 #include <sys/systm.h>
36 #include <sys/sysctl.h>
37 #include <sys/kmem.h>
38
39 #include <dev/sdmmc/sdhcreg.h>
40 #include <dev/sdmmc/sdhcvar.h>
41 #include <dev/sdmmc/sdmmcvar.h>
42
43 #include <dev/clk/clk_backend.h>
44
45 #include <dev/fdt/fdtvar.h>
46 #include <dev/fdt/syscon.h>
47
48 #define RK3399_GRF_EMMCCORE_CON0 0xf000
49 #define RK3399_CORECFG_BASECLKFREQ __BITS(15,8)
50 #define RK3399_CORECFG_TIMEOUTCLKUNIT __BIT(7)
51 #define RK3399_CORECFG_TUNINGCOUNT __BITS(5,0)
52 #define RK3399_GRF_EMMCCORE_CON11 0xf02c
53 #define RK3399_CORECFG_CLOCKMULTIPLIER __BITS(7,0)
54
55 enum arasan_sdhc_type {
56 AS_TYPE_RK3399 = 1,
57 };
58
59 struct arasan_sdhc_softc {
60 struct sdhc_softc sc_base;
61 struct sdhc_host *sc_host[1];
62 bus_space_tag_t sc_bst;
63 bus_space_handle_t sc_bsh;
64 bus_size_t sc_bsz;
65 int sc_phandle;
66 struct fdtbus_phy *sc_phy;
67 struct syscon *sc_syscon;
68 struct clk *sc_clk_xin;
69 struct clk *sc_clk_ahb;
70 enum arasan_sdhc_type sc_type;
71 struct clk_domain sc_clkdom;
72 struct clk sc_clk_card;
73 };
74
75 static const struct device_compatible_entry compat_data[] = {
76 { .compat = "rockchip,rk3399-sdhci-5.1",
77 .value = AS_TYPE_RK3399 },
78
79 { 0 }
80 };
81
82 static struct clk *
83 arasan_sdhc_clk_decode(device_t dev, int cc_phandle, const void *data, size_t len)
84 {
85 struct arasan_sdhc_softc * const sc = device_private(dev);
86
87 if (len != 0)
88 return NULL;
89
90 return &sc->sc_clk_card;
91 }
92
93 static const struct fdtbus_clock_controller_func arasan_sdhc_fdt_clk_funcs = {
94 .decode = arasan_sdhc_clk_decode,
95 };
96
97 static struct clk *
98 arasan_sdhc_clk_get(void *priv, const char *name)
99 {
100 struct arasan_sdhc_softc * const sc = priv;
101
102 if (strcmp(name, sc->sc_clk_card.name) != 0)
103 return NULL;
104
105 return &sc->sc_clk_card;
106 }
107
108 static u_int
109 arasan_sdhc_clk_get_rate(void *priv, struct clk *clk)
110 {
111 struct arasan_sdhc_softc * const sc = priv;
112
113 return clk_get_rate(sc->sc_clk_xin);
114 }
115
116 static const struct clk_funcs arasan_sdhc_clk_funcs = {
117 .get = arasan_sdhc_clk_get,
118 .get_rate = arasan_sdhc_clk_get_rate,
119 };
120
121 static int
122 arasan_sdhc_signal_voltage(struct sdhc_softc *sdhc, int signal_voltage)
123 {
124 if (signal_voltage == SDMMC_SIGNAL_VOLTAGE_180)
125 return 0;
126
127 return EINVAL;
128 }
129
130 static int
131 arasan_sdhc_bus_clock_pre(struct sdhc_softc *sdhc, int freq)
132 {
133 struct arasan_sdhc_softc * const sc = device_private(sdhc->sc_dev);
134 int error;
135
136 if (sc->sc_phy != NULL) {
137 error = fdtbus_phy_enable(sc->sc_phy, false);
138 if (error != 0)
139 return error;
140 }
141
142 return 0;
143 }
144
145 static int
146 arasan_sdhc_bus_clock_post(struct sdhc_softc *sdhc, int freq)
147 {
148 struct arasan_sdhc_softc * const sc = device_private(sdhc->sc_dev);
149 int error;
150
151 if (sc->sc_phy != NULL) {
152 error = fdtbus_phy_enable(sc->sc_phy, true);
153 if (error != 0)
154 return error;
155 }
156
157 return 0;
158 }
159
160 static void
161 arasan_sdhc_init_rk3399(struct arasan_sdhc_softc *sc)
162 {
163 uint32_t mask, val;
164
165 if (sc->sc_syscon == NULL)
166 return;
167
168 syscon_lock(sc->sc_syscon);
169
170 /* Disable clock multiplier */
171 mask = RK3399_CORECFG_CLOCKMULTIPLIER;
172 val = 0;
173 syscon_write_4(sc->sc_syscon, RK3399_GRF_EMMCCORE_CON11, (mask << 16) | val);
174
175 /* Set base clock frequency */
176 const u_int xin_rate = clk_get_rate(sc->sc_clk_xin);
177 mask = RK3399_CORECFG_BASECLKFREQ;
178 val = __SHIFTIN((xin_rate + (1000000 / 2)) / 1000000, RK3399_CORECFG_BASECLKFREQ);
179 syscon_write_4(sc->sc_syscon, RK3399_GRF_EMMCCORE_CON0, (mask << 16) | val);
180
181 syscon_unlock(sc->sc_syscon);
182 }
183
184 static void
185 arasan_sdhc_init(device_t dev)
186 {
187 struct arasan_sdhc_softc * const sc = device_private(dev);
188 const char * sdhci_5_1_compat[] = { "arasan,sdhci-5.1", NULL };
189 int error;
190
191 if (sc->sc_type == AS_TYPE_RK3399)
192 arasan_sdhc_init_rk3399(sc);
193
194 if (of_match_compatible(sc->sc_phandle, sdhci_5_1_compat)) {
195 sc->sc_phy = fdtbus_phy_get(sc->sc_phandle, "phy_arasan");
196 if (sc->sc_phy == NULL) {
197 aprint_error_dev(dev, "couldn't get PHY\n");
198 return;
199 }
200 sc->sc_base.sc_vendor_signal_voltage = arasan_sdhc_signal_voltage;
201 }
202
203 error = sdhc_host_found(&sc->sc_base, sc->sc_bst, sc->sc_bsh, sc->sc_bsz);
204 if (error != 0) {
205 aprint_error_dev(dev, "couldn't initialize host, error = %d\n", error);
206 return;
207 }
208 }
209
210 static int
211 arasan_sdhc_match(device_t parent, cfdata_t cf, void *aux)
212 {
213 struct fdt_attach_args * const faa = aux;
214
215 return of_match_compat_data(faa->faa_phandle, compat_data);
216 }
217
218 static void
219 arasan_sdhc_attach(device_t parent, device_t self, void *aux)
220 {
221 struct arasan_sdhc_softc * const sc = device_private(self);
222 struct fdt_attach_args * const faa = aux;
223 const int phandle = faa->faa_phandle;
224 char intrstr[128];
225 const char *clkname;
226 bus_addr_t addr;
227 bus_size_t size;
228 u_int bus_width;
229 int error;
230 void *ih;
231
232 fdtbus_clock_assign(phandle);
233
234 if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) {
235 aprint_error(": couldn't get registers\n");
236 return;
237 }
238
239 if (!fdtbus_intr_str(phandle, 0, intrstr, sizeof(intrstr))) {
240 aprint_error(": couldn't decode interrupt\n");
241 return;
242 }
243
244 sc->sc_clk_xin = fdtbus_clock_get(phandle, "clk_xin");
245 sc->sc_clk_ahb = fdtbus_clock_get(phandle, "clk_ahb");
246 if (sc->sc_clk_xin == NULL || sc->sc_clk_ahb == NULL) {
247 aprint_error(": couldn't get clocks\n");
248 return;
249 }
250 if (clk_enable(sc->sc_clk_xin) != 0 || clk_enable(sc->sc_clk_ahb) != 0) {
251 aprint_error(": couldn't enable clocks\n");
252 return;
253 }
254
255 sc->sc_syscon = fdtbus_syscon_acquire(phandle, "arasan,soc-ctl-syscon");
256
257 if (of_getprop_uint32(phandle, "bus-width", &bus_width) != 0)
258 bus_width = 4;
259
260 sc->sc_phandle = phandle;
261 sc->sc_bst = faa->faa_bst;
262 if (bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh) != 0) {
263 aprint_error(": couldn't map registers\n");
264 return;
265 }
266 sc->sc_bsz = size;
267 sc->sc_type = of_search_compatible(phandle, compat_data)->value;
268
269 const uint32_t caps = bus_space_read_4(sc->sc_bst, sc->sc_bsh, SDHC_CAPABILITIES);
270 if ((caps & (SDHC_ADMA2_SUPP|SDHC_64BIT_SYS_BUS)) == SDHC_ADMA2_SUPP) {
271 error = bus_dmatag_subregion(faa->faa_dmat, 0, 0xffffffff,
272 &sc->sc_base.sc_dmat, BUS_DMA_WAITOK);
273 if (error != 0) {
274 aprint_error(": couldn't create DMA tag: %d\n", error);
275 return;
276 }
277 } else {
278 sc->sc_base.sc_dmat = faa->faa_dmat;
279 }
280
281 sc->sc_base.sc_dev = self;
282 sc->sc_base.sc_host = sc->sc_host;
283 sc->sc_base.sc_flags = SDHC_FLAG_NO_CLKBASE |
284 SDHC_FLAG_SINGLE_POWER_WRITE |
285 SDHC_FLAG_32BIT_ACCESS |
286 SDHC_FLAG_USE_DMA |
287 SDHC_FLAG_USE_ADMA2 |
288 SDHC_FLAG_STOP_WITH_TC;
289 if (bus_width == 8)
290 sc->sc_base.sc_flags |= SDHC_FLAG_8BIT_MODE;
291 sc->sc_base.sc_clkbase = clk_get_rate(sc->sc_clk_xin) / 1000;
292 sc->sc_base.sc_vendor_bus_clock = arasan_sdhc_bus_clock_pre;
293 sc->sc_base.sc_vendor_bus_clock_post = arasan_sdhc_bus_clock_post;
294
295 aprint_naive("\n");
296 aprint_normal(": Arasan SDHCI controller\n");
297
298 clkname = fdtbus_get_string(phandle, "clock-output-names");
299 if (clkname == NULL)
300 clkname = faa->faa_name;
301
302 sc->sc_clkdom.name = device_xname(self);
303 sc->sc_clkdom.funcs = &arasan_sdhc_clk_funcs;
304 sc->sc_clkdom.priv = sc;
305 sc->sc_clk_card.domain = &sc->sc_clkdom;
306 sc->sc_clk_card.name = kmem_asprintf("%s", clkname);
307 clk_attach(&sc->sc_clk_card);
308
309 fdtbus_register_clock_controller(self, phandle, &arasan_sdhc_fdt_clk_funcs);
310
311 ih = fdtbus_intr_establish_xname(phandle, 0, IPL_SDMMC, 0,
312 sdhc_intr, &sc->sc_base, device_xname(self));
313 if (ih == NULL) {
314 aprint_error_dev(self, "couldn't establish interrupt on %s\n", intrstr);
315 return;
316 }
317 aprint_normal_dev(self, "interrupting on %s\n", intrstr);
318
319 arasan_sdhc_init(self);
320 }
321
322 CFATTACH_DECL_NEW(arasan_sdhc_fdt, sizeof(struct arasan_sdhc_softc),
323 arasan_sdhc_match, arasan_sdhc_attach, NULL, NULL);
324