arasan_sdhc_fdt.c revision 1.6 1 /* $NetBSD: arasan_sdhc_fdt.c,v 1.6 2021/01/27 03:10:21 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.6 2021/01/27 03:10:21 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 DEVICE_COMPAT_EOL
80 };
81
82 static const struct device_compatible_entry sdhci_5_1_compat[] = {
83 { .compat = "arasan,sdhci-5.1" },
84 DEVICE_COMPAT_EOL
85 };
86
87 static struct clk *
88 arasan_sdhc_clk_decode(device_t dev, int cc_phandle, const void *data, size_t len)
89 {
90 struct arasan_sdhc_softc * const sc = device_private(dev);
91
92 if (len != 0)
93 return NULL;
94
95 return &sc->sc_clk_card;
96 }
97
98 static const struct fdtbus_clock_controller_func arasan_sdhc_fdt_clk_funcs = {
99 .decode = arasan_sdhc_clk_decode,
100 };
101
102 static struct clk *
103 arasan_sdhc_clk_get(void *priv, const char *name)
104 {
105 struct arasan_sdhc_softc * const sc = priv;
106
107 if (strcmp(name, sc->sc_clk_card.name) != 0)
108 return NULL;
109
110 return &sc->sc_clk_card;
111 }
112
113 static u_int
114 arasan_sdhc_clk_get_rate(void *priv, struct clk *clk)
115 {
116 struct arasan_sdhc_softc * const sc = priv;
117
118 return clk_get_rate(sc->sc_clk_xin);
119 }
120
121 static const struct clk_funcs arasan_sdhc_clk_funcs = {
122 .get = arasan_sdhc_clk_get,
123 .get_rate = arasan_sdhc_clk_get_rate,
124 };
125
126 static int
127 arasan_sdhc_signal_voltage(struct sdhc_softc *sdhc, int signal_voltage)
128 {
129 if (signal_voltage == SDMMC_SIGNAL_VOLTAGE_180)
130 return 0;
131
132 return EINVAL;
133 }
134
135 static int
136 arasan_sdhc_bus_clock_pre(struct sdhc_softc *sdhc, int freq)
137 {
138 struct arasan_sdhc_softc * const sc = device_private(sdhc->sc_dev);
139 int error;
140
141 if (sc->sc_phy != NULL) {
142 error = fdtbus_phy_enable(sc->sc_phy, false);
143 if (error != 0)
144 return error;
145 }
146
147 return 0;
148 }
149
150 static int
151 arasan_sdhc_bus_clock_post(struct sdhc_softc *sdhc, int freq)
152 {
153 struct arasan_sdhc_softc * const sc = device_private(sdhc->sc_dev);
154 int error;
155
156 if (sc->sc_phy != NULL) {
157 error = fdtbus_phy_enable(sc->sc_phy, true);
158 if (error != 0)
159 return error;
160 }
161
162 return 0;
163 }
164
165 static void
166 arasan_sdhc_init_rk3399(struct arasan_sdhc_softc *sc)
167 {
168 uint32_t mask, val;
169
170 if (sc->sc_syscon == NULL)
171 return;
172
173 syscon_lock(sc->sc_syscon);
174
175 /* Disable clock multiplier */
176 mask = RK3399_CORECFG_CLOCKMULTIPLIER;
177 val = 0;
178 syscon_write_4(sc->sc_syscon, RK3399_GRF_EMMCCORE_CON11, (mask << 16) | val);
179
180 /* Set base clock frequency */
181 const u_int xin_rate = clk_get_rate(sc->sc_clk_xin);
182 mask = RK3399_CORECFG_BASECLKFREQ;
183 val = __SHIFTIN((xin_rate + (1000000 / 2)) / 1000000, RK3399_CORECFG_BASECLKFREQ);
184 syscon_write_4(sc->sc_syscon, RK3399_GRF_EMMCCORE_CON0, (mask << 16) | val);
185
186 syscon_unlock(sc->sc_syscon);
187 }
188
189 static void
190 arasan_sdhc_init(device_t dev)
191 {
192 struct arasan_sdhc_softc * const sc = device_private(dev);
193 int error;
194
195 if (sc->sc_type == AS_TYPE_RK3399)
196 arasan_sdhc_init_rk3399(sc);
197
198 if (of_compatible_match(sc->sc_phandle, sdhci_5_1_compat)) {
199 sc->sc_phy = fdtbus_phy_get(sc->sc_phandle, "phy_arasan");
200 if (sc->sc_phy == NULL) {
201 aprint_error_dev(dev, "couldn't get PHY\n");
202 return;
203 }
204 sc->sc_base.sc_vendor_signal_voltage = arasan_sdhc_signal_voltage;
205 }
206
207 error = sdhc_host_found(&sc->sc_base, sc->sc_bst, sc->sc_bsh, sc->sc_bsz);
208 if (error != 0) {
209 aprint_error_dev(dev, "couldn't initialize host, error = %d\n", error);
210 return;
211 }
212 }
213
214 static int
215 arasan_sdhc_match(device_t parent, cfdata_t cf, void *aux)
216 {
217 struct fdt_attach_args * const faa = aux;
218
219 return of_compatible_match(faa->faa_phandle, compat_data);
220 }
221
222 static void
223 arasan_sdhc_attach(device_t parent, device_t self, void *aux)
224 {
225 struct arasan_sdhc_softc * const sc = device_private(self);
226 struct fdt_attach_args * const faa = aux;
227 const int phandle = faa->faa_phandle;
228 char intrstr[128];
229 const char *clkname;
230 bus_addr_t addr;
231 bus_size_t size;
232 u_int bus_width;
233 int error;
234 void *ih;
235
236 fdtbus_clock_assign(phandle);
237
238 if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) {
239 aprint_error(": couldn't get registers\n");
240 return;
241 }
242
243 if (!fdtbus_intr_str(phandle, 0, intrstr, sizeof(intrstr))) {
244 aprint_error(": couldn't decode interrupt\n");
245 return;
246 }
247
248 sc->sc_clk_xin = fdtbus_clock_get(phandle, "clk_xin");
249 sc->sc_clk_ahb = fdtbus_clock_get(phandle, "clk_ahb");
250 if (sc->sc_clk_xin == NULL || sc->sc_clk_ahb == NULL) {
251 aprint_error(": couldn't get clocks\n");
252 return;
253 }
254 if (clk_enable(sc->sc_clk_xin) != 0 || clk_enable(sc->sc_clk_ahb) != 0) {
255 aprint_error(": couldn't enable clocks\n");
256 return;
257 }
258
259 sc->sc_syscon = fdtbus_syscon_acquire(phandle, "arasan,soc-ctl-syscon");
260
261 if (of_getprop_uint32(phandle, "bus-width", &bus_width) != 0)
262 bus_width = 4;
263
264 sc->sc_phandle = phandle;
265 sc->sc_bst = faa->faa_bst;
266 if (bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh) != 0) {
267 aprint_error(": couldn't map registers\n");
268 return;
269 }
270 sc->sc_bsz = size;
271 sc->sc_type = of_compatible_lookup(phandle, compat_data)->value;
272
273 const uint32_t caps = bus_space_read_4(sc->sc_bst, sc->sc_bsh, SDHC_CAPABILITIES);
274 if ((caps & (SDHC_ADMA2_SUPP|SDHC_64BIT_SYS_BUS)) == SDHC_ADMA2_SUPP) {
275 error = bus_dmatag_subregion(faa->faa_dmat, 0, 0xffffffff,
276 &sc->sc_base.sc_dmat, BUS_DMA_WAITOK);
277 if (error != 0) {
278 aprint_error(": couldn't create DMA tag: %d\n", error);
279 return;
280 }
281 } else {
282 sc->sc_base.sc_dmat = faa->faa_dmat;
283 }
284
285 sc->sc_base.sc_dev = self;
286 sc->sc_base.sc_host = sc->sc_host;
287 sc->sc_base.sc_flags = SDHC_FLAG_NO_CLKBASE |
288 SDHC_FLAG_SINGLE_POWER_WRITE |
289 SDHC_FLAG_32BIT_ACCESS |
290 SDHC_FLAG_USE_DMA |
291 SDHC_FLAG_USE_ADMA2 |
292 SDHC_FLAG_STOP_WITH_TC;
293 if (bus_width == 8)
294 sc->sc_base.sc_flags |= SDHC_FLAG_8BIT_MODE;
295 sc->sc_base.sc_clkbase = clk_get_rate(sc->sc_clk_xin) / 1000;
296 sc->sc_base.sc_vendor_bus_clock = arasan_sdhc_bus_clock_pre;
297 sc->sc_base.sc_vendor_bus_clock_post = arasan_sdhc_bus_clock_post;
298
299 aprint_naive("\n");
300 aprint_normal(": Arasan SDHCI controller\n");
301
302 clkname = fdtbus_get_string(phandle, "clock-output-names");
303 if (clkname == NULL)
304 clkname = faa->faa_name;
305
306 sc->sc_clkdom.name = device_xname(self);
307 sc->sc_clkdom.funcs = &arasan_sdhc_clk_funcs;
308 sc->sc_clkdom.priv = sc;
309 sc->sc_clk_card.domain = &sc->sc_clkdom;
310 sc->sc_clk_card.name = kmem_asprintf("%s", clkname);
311 clk_attach(&sc->sc_clk_card);
312
313 fdtbus_register_clock_controller(self, phandle, &arasan_sdhc_fdt_clk_funcs);
314
315 ih = fdtbus_intr_establish_xname(phandle, 0, IPL_SDMMC, 0,
316 sdhc_intr, &sc->sc_base, device_xname(self));
317 if (ih == NULL) {
318 aprint_error_dev(self, "couldn't establish interrupt on %s\n", intrstr);
319 return;
320 }
321 aprint_normal_dev(self, "interrupting on %s\n", intrstr);
322
323 arasan_sdhc_init(self);
324 }
325
326 CFATTACH_DECL_NEW(arasan_sdhc_fdt, sizeof(struct arasan_sdhc_softc),
327 arasan_sdhc_match, arasan_sdhc_attach, NULL, NULL);
328