xref: /openbsd-src/sys/arch/octeon/dev/octxctl.c (revision bb7d75aeee586ab4fe453341a47ddd076c35cf2f)
1 /*	$OpenBSD: octxctl.c,v 1.2 2018/01/16 15:22:14 visa Exp $	*/
2 
3 /*
4  * Copyright (c) 2017 Visa Hankala
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 /*
20  * Driver for OCTEON USB3 controller bridge.
21  */
22 
23 #include <sys/param.h>
24 #include <sys/systm.h>
25 #include <sys/device.h>
26 #include <sys/malloc.h>
27 
28 #include <machine/fdt.h>
29 #include <machine/octeonvar.h>
30 
31 #include <dev/ofw/fdt.h>
32 #include <dev/ofw/openfirm.h>
33 
34 #include <octeon/dev/iobusvar.h>
35 #include <octeon/dev/octxctlreg.h>
36 
37 #define XCTL_RD_8(sc, reg) \
38 	bus_space_read_8((sc)->sc_iot, (sc)->sc_ioh, (reg))
39 #define XCTL_WR_8(sc, reg, val) \
40 	bus_space_write_8((sc)->sc_iot, (sc)->sc_ioh, (reg), (val))
41 
42 struct octxctl_softc {
43 	struct device		sc_dev;
44 	bus_space_tag_t		sc_iot;
45 	bus_space_handle_t	sc_ioh;
46 };
47 
48 int	 octxctl_match(struct device *, void *, void *);
49 void	 octxctl_attach(struct device *, struct device *, void *);
50 
51 int	 octxctl_dwc3_init(struct octxctl_softc *, struct fdt_reg *);
52 void	 octxctl_uctl_init(struct octxctl_softc *, uint64_t, uint64_t);
53 uint8_t	 octxctl_read_1(bus_space_tag_t, bus_space_handle_t, bus_size_t);
54 uint16_t octxctl_read_2(bus_space_tag_t, bus_space_handle_t, bus_size_t);
55 uint32_t octxctl_read_4(bus_space_tag_t, bus_space_handle_t, bus_size_t);
56 void	 octxctl_write_1(bus_space_tag_t, bus_space_handle_t, bus_size_t,
57 	    uint8_t);
58 void	 octxctl_write_2(bus_space_tag_t, bus_space_handle_t, bus_size_t,
59 	    uint16_t);
60 void	 octxctl_write_4(bus_space_tag_t, bus_space_handle_t, bus_size_t,
61 	    uint32_t);
62 
63 const struct cfattach octxctl_ca = {
64 	sizeof(struct octxctl_softc), octxctl_match, octxctl_attach
65 };
66 
67 struct cfdriver octxctl_cd = {
68 	NULL, "octxctl", DV_DULL
69 };
70 
71 bus_space_t octxctl_tag = {
72 	.bus_base = PHYS_TO_XKPHYS(0, CCA_NC),
73 	._space_read_1 = octxctl_read_1,
74 	._space_read_2 = octxctl_read_2,
75 	._space_read_4 = octxctl_read_4,
76 	._space_write_1 = octxctl_write_1,
77 	._space_write_2 = octxctl_write_2,
78 	._space_write_4 = octxctl_write_4,
79 	._space_map = iobus_space_map,
80 	._space_unmap = iobus_space_unmap,
81 	._space_subregion = generic_space_region,
82 	._space_vaddr = generic_space_vaddr
83 };
84 
85 int
86 octxctl_match(struct device *parent, void *match, void *aux)
87 {
88 	struct fdt_attach_args *faa = aux;
89 	int child;
90 
91 	if (OF_is_compatible(faa->fa_node, "cavium,octeon-7130-usb-uctl") == 0)
92 		return 0;
93 	if ((child = OF_child(faa->fa_node)) == 0)
94 		return 0;
95 	return OF_is_compatible(child, "cavium,octeon-7130-xhci");
96 }
97 
98 void
99 octxctl_attach(struct device *parent, struct device *self, void *aux)
100 {
101 	char clock_type_hs[32];
102 	char clock_type_ss[32];
103 	struct fdt_reg child_reg;
104 	struct fdt_attach_args child_faa;
105 	struct fdt_attach_args *faa = aux;
106 	struct octxctl_softc *sc = (struct octxctl_softc *)self;
107 	uint64_t clock_freq, clock_sel;
108 	uint32_t reg[4];
109 	int child;
110 
111 	if (faa->fa_nreg != 1) {
112 		printf(": expected one IO space, got %d\n", faa->fa_nreg);
113 		return;
114 	}
115 
116 	child = OF_child(faa->fa_node);
117 	if (OF_getpropint(faa->fa_node, "#address-cells", 0) != 2 ||
118 	    OF_getpropint(faa->fa_node, "#size-cells", 0) != 2) {
119 		printf(": invalid fdt reg cells\n");
120 		return;
121 	}
122 	if (OF_getproplen(child, "reg") != sizeof(reg)) {
123 		printf(": invalid child fdt reg\n");
124 		return;
125 	}
126 	OF_getpropintarray(child, "reg", reg, sizeof(reg));
127 	child_reg.addr = ((uint64_t)reg[0] << 32) | reg[1];
128 	child_reg.size = ((uint64_t)reg[2] << 32) | reg[3];
129 
130 	clock_freq = OF_getpropint(faa->fa_node, "refclk-frequency", 0);
131 
132 	if (OF_getprop(faa->fa_node, "refclk-type-hs", clock_type_hs,
133 	    sizeof(clock_type_hs)) < 0)
134 		goto error;
135 	if (OF_getprop(faa->fa_node, "refclk-type-ss", clock_type_ss,
136 	    sizeof(clock_type_ss)) < 0)
137 		goto error;
138 	clock_sel = 0;
139 	if (strcmp(clock_type_ss, "dlmc_ref_clk1") == 0)
140 		clock_sel |= 1;
141 	if (strcmp(clock_type_hs, "pll_ref_clk") == 0)
142 		clock_sel |= 2;
143 
144 	sc->sc_iot = faa->fa_iot;
145 	if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr, faa->fa_reg[0].size,
146 	    0, &sc->sc_ioh)) {
147 		printf(": could not map registers\n");
148 		goto error;
149 	}
150 
151 	octxctl_uctl_init(sc, clock_freq, clock_sel);
152 
153 	if (octxctl_dwc3_init(sc, &child_reg) != 0) {
154 		/* Error message has been printed already. */
155 		goto error;
156 	}
157 
158 	printf("\n");
159 
160 	memset(&child_faa, 0, sizeof(child_faa));
161 	child_faa.fa_name = "";
162 	child_faa.fa_node = child;
163 	child_faa.fa_iot = &octxctl_tag;
164 	child_faa.fa_dmat = faa->fa_dmat;
165 	child_faa.fa_reg = &child_reg;
166 	child_faa.fa_nreg = 1;
167 	/* child_faa.fa_intr is not utilized. */
168 
169 	config_found(self, &child_faa, NULL);
170 
171 	return;
172 
173 error:
174 	if (sc->sc_ioh != 0)
175 		bus_space_unmap(sc->sc_iot, sc->sc_ioh, faa->fa_reg[0].size);
176 }
177 
178 void
179 octxctl_uctl_init(struct octxctl_softc *sc, uint64_t clock_freq,
180     uint64_t clock_sel)
181 {
182 	static const uint32_t clock_divs[] = { 1, 2, 4, 6, 8, 16, 24, 32 };
183 	uint64_t i, val;
184 	uint64_t ioclock = octeon_ioclock_speed();
185 	uint64_t mpll_mult;
186 	uint64_t refclk_fsel;
187 
188 	/*
189 	 * Put the bridge controller, USB core, PHY, and clock divider
190 	 * into reset.
191 	 */
192 	val = XCTL_RD_8(sc, XCTL_CTL);
193 	val |= XCTL_CTL_UCTL_RST;
194 	val |= XCTL_CTL_UAHC_RST;
195 	val |= XCTL_CTL_UPHY_RST;
196 	XCTL_WR_8(sc, XCTL_CTL, val);
197 	val = XCTL_RD_8(sc, XCTL_CTL);
198 	val |= XCTL_CTL_CLKDIV_RST;
199 	XCTL_WR_8(sc, XCTL_CTL, val);
200 
201 	/* Select IO clock divisor. */
202 	for (i = 0; i < nitems(clock_divs); i++) {
203 		if (ioclock / clock_divs[i] < 300000000)
204 			break;
205 	}
206 
207 	/* Update the divisor and enable the clock. */
208 	val = XCTL_RD_8(sc, XCTL_CTL);
209 	val &= ~XCTL_CTL_CLKDIV_SEL;
210 	val |= (i << XCTL_CTL_CLKDIV_SEL_SHIFT) & XCTL_CTL_CLKDIV_SEL;
211 	val |= XCTL_CTL_CLK_EN;
212 	XCTL_WR_8(sc, XCTL_CTL, val);
213 
214 	/* Take the clock divider out of reset. */
215 	val = XCTL_RD_8(sc, XCTL_CTL);
216 	val &= ~XCTL_CTL_CLKDIV_RST;
217 	XCTL_WR_8(sc, XCTL_CTL, val);
218 
219 	/* Select the reference clock. */
220 	switch (clock_freq) {
221 	case 50000000:
222 		refclk_fsel = 0x07;
223 		mpll_mult = 0x32;
224 		break;
225 	case 125000000:
226 		refclk_fsel = 0x07;
227 		mpll_mult = 0x28;
228 		break;
229 	case 100000000:
230 	default:
231 		if (clock_sel < 2)
232 			refclk_fsel = 0x07;
233 		else
234 			refclk_fsel = 0x27;
235 		mpll_mult = 0x19;
236 		break;
237 	}
238 
239 	/* Set the clock and power up PHYs. */
240 	val = XCTL_RD_8(sc, XCTL_CTL);
241 	val &= ~XCTL_CTL_REFCLK_SEL;
242 	val |= clock_sel << XCTL_CTL_REFCLK_SEL_SHIFT;
243 	val &= ~XCTL_CTL_REFCLK_DIV2;
244 	val &= ~XCTL_CTL_REFCLK_FSEL;
245 	val |= refclk_fsel << XCTL_CTL_REFCLK_FSEL_SHIFT;
246 	val &= ~XCTL_CTL_MPLL_MULT;
247 	val |= mpll_mult << XCTL_CTL_MPLL_MULT_SHIFT;
248 	val |= XCTL_CTL_SSC_EN;
249 	val |= XCTL_CTL_REFCLK_SSP_EN;
250 	val |= XCTL_CTL_SSPOWER_EN;
251 	val |= XCTL_CTL_HSPOWER_EN;
252 	XCTL_WR_8(sc, XCTL_CTL, val);
253 
254 	delay(100);
255 
256 	/* Take the bridge out of reset. */
257 	val = XCTL_RD_8(sc, XCTL_CTL);
258 	val &= ~XCTL_CTL_UCTL_RST;
259 	XCTL_WR_8(sc, XCTL_CTL, val);
260 
261 	delay(100);
262 
263 	/* Disable port power control. */
264 	val = XCTL_RD_8(sc, XCTL_HOST_CFG);
265 	val &= ~XCTL_HOST_CFG_PPC_EN;
266 	XCTL_WR_8(sc, XCTL_HOST_CFG, val);
267 
268 	/* Enable host-only mode. */
269 	val = XCTL_RD_8(sc, XCTL_CTL);
270 	val &= ~XCTL_CTL_DRD_MODE;
271 	XCTL_WR_8(sc, XCTL_CTL, val);
272 
273 	delay(100);
274 
275 	/* Take the USB core out of reset. */
276 	val = XCTL_RD_8(sc, XCTL_CTL);
277 	val &= ~XCTL_CTL_UAHC_RST;
278 	XCTL_WR_8(sc, XCTL_CTL, val);
279 
280 	delay(100);
281 
282 	val = XCTL_RD_8(sc, XCTL_CTL);
283 	val |= XCTL_CTL_CSCLK_EN;
284 	XCTL_WR_8(sc, XCTL_CTL, val);
285 
286 	/* Take the PHY out of reset. */
287 	val = XCTL_RD_8(sc, XCTL_CTL);
288 	val &= ~XCTL_CTL_UPHY_RST;
289 	XCTL_WR_8(sc, XCTL_CTL, val);
290 	(void)XCTL_RD_8(sc, XCTL_CTL);
291 
292 	/* Fix endianess. */
293 	val = XCTL_RD_8(sc, XCTL_SHIM_CFG);
294 	val &= ~XCTL_SHIM_CFG_CSR_BYTE_SWAP;
295 	val &= ~XCTL_SHIM_CFG_DMA_BYTE_SWAP;
296 	val |= 3ull << XCTL_SHIM_CFG_CSR_BYTE_SWAP_SHIFT;
297 	val |= 1ull << XCTL_SHIM_CFG_DMA_BYTE_SWAP_SHIFT;
298 	XCTL_WR_8(sc, XCTL_SHIM_CFG, val);
299 	(void)XCTL_RD_8(sc, XCTL_SHIM_CFG);
300 }
301 
302 int
303 octxctl_dwc3_init(struct octxctl_softc *sc, struct fdt_reg *reg)
304 {
305 	bus_space_handle_t ioh;
306 	uint32_t rev;
307 	uint32_t val;
308 	int error = 0;
309 
310 	if (bus_space_map(sc->sc_iot, reg->addr, reg->size, 0, &ioh) != 0) {
311 		printf(": could not map USB3 core registers\n");
312 		return EIO;
313 	}
314 
315 	val = bus_space_read_4(sc->sc_iot, ioh, DWC3_GSNPSID);
316 	if ((val & 0xffff0000u) != 0x55330000u) {
317 		printf(": no DWC3 core\n");
318 		error = EIO;
319 		goto out;
320 	}
321 	rev = val & 0xffffu;
322 	printf(": DWC3 rev 0x%04x", rev);
323 
324 	val = bus_space_read_4(sc->sc_iot, ioh, DWC3_GUSB3PIPECTL(0));
325 	val &= ~DWC3_GUSB3PIPECTL_UX_EXIT_PX;
326 	val |= DWC3_GUSB3PIPECTL_SUSPHY;
327 	bus_space_write_4(sc->sc_iot, ioh, DWC3_GUSB3PIPECTL(0), val);
328 
329 	val = bus_space_read_4(sc->sc_iot, ioh, DWC3_GUSB2PHYCFG(0));
330 	val |= DWC3_GUSB2PHYCFG_SUSPHY;
331 	bus_space_write_4(sc->sc_iot, ioh, DWC3_GUSB2PHYCFG(0), val);
332 
333 	/* Set the controller into host mode. */
334 	val = bus_space_read_4(sc->sc_iot, ioh, DWC3_GCTL);
335 	val &= ~DWC3_GCTL_PRTCAP_MASK;
336 	val |= DWC3_GCTL_PRTCAP_HOST;
337 	bus_space_write_4(sc->sc_iot, ioh, DWC3_GCTL, val);
338 
339 	val = bus_space_read_4(sc->sc_iot, ioh, DWC3_GCTL);
340 	val &= ~DWC3_GCTL_SCALEDOWN_MASK;
341 	val &= ~DWC3_GCTL_DISSCRAMBLE;
342 	if (rev >= DWC3_REV_210A && rev <= DWC3_REV_250A)
343 		val |= DWC3_GCTL_DSBLCLKGTNG | DWC3_GCTL_SOFITPSYNC;
344 	else
345 		val &= ~DWC3_GCTL_DSBLCLKGTNG;
346 	bus_space_write_4(sc->sc_iot, ioh, DWC3_GCTL, val);
347 
348 out:
349 	bus_space_unmap(sc->sc_iot, ioh, reg->size);
350 
351 	return error;
352 }
353 
354 /*
355  * Bus access routines for xhci(4).
356  */
357 
358 uint8_t
359 octxctl_read_1(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o)
360 {
361 	return *(volatile uint8_t *)(h + (o ^ 3));
362 }
363 
364 uint16_t
365 octxctl_read_2(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o)
366 {
367 	return *(volatile uint16_t *)(h + (o ^ 2));
368 }
369 
370 uint32_t
371 octxctl_read_4(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o)
372 {
373 	return *(volatile uint32_t *)(h + o);
374 }
375 
376 void
377 octxctl_write_1(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o,
378     uint8_t v)
379 {
380 	*(volatile uint8_t *)(h + (o ^ 3)) = v;
381 }
382 
383 void
384 octxctl_write_2(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o,
385     uint16_t v)
386 {
387 	*(volatile uint16_t *)(h + (o ^ 2)) = v;
388 }
389 
390 void
391 octxctl_write_4(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o,
392     uint32_t v)
393 {
394 	*(volatile uint32_t *)(h + o) = v;
395 }
396