octxctl.c revision 1.1
1/*	$OpenBSD: octxctl.c,v 1.1 2017/08/01 16:18:12 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
42struct octxctl_softc {
43	struct device		sc_dev;
44	bus_space_tag_t		sc_iot;
45	bus_space_handle_t	sc_ioh;
46};
47
48int	 octxctl_match(struct device *, void *, void *);
49void	 octxctl_attach(struct device *, struct device *, void *);
50
51int	 octxctl_dwc3_init(struct octxctl_softc *, struct fdt_reg *);
52void	 octxctl_uctl_init(struct octxctl_softc *, uint64_t, uint64_t);
53uint8_t	 octxctl_read_1(bus_space_tag_t, bus_space_handle_t, bus_size_t);
54uint16_t octxctl_read_2(bus_space_tag_t, bus_space_handle_t, bus_size_t);
55uint32_t octxctl_read_4(bus_space_tag_t, bus_space_handle_t, bus_size_t);
56void	 octxctl_write_1(bus_space_tag_t, bus_space_handle_t, bus_size_t,
57	    uint8_t);
58void	 octxctl_write_2(bus_space_tag_t, bus_space_handle_t, bus_size_t,
59	    uint16_t);
60void	 octxctl_write_4(bus_space_tag_t, bus_space_handle_t, bus_size_t,
61	    uint32_t);
62
63const struct cfattach octxctl_ca = {
64	sizeof(struct octxctl_softc), octxctl_match, octxctl_attach
65};
66
67struct cfdriver octxctl_cd = {
68	NULL, "octxctl", DV_DULL
69};
70
71bus_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
85int
86octxctl_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
98void
99octxctl_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
173error:
174	if (sc->sc_ioh != 0)
175		bus_space_unmap(sc->sc_iot, sc->sc_ioh, faa->fa_reg[0].size);
176}
177
178void
179octxctl_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
302int
303octxctl_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
348out:
349	bus_space_unmap(sc->sc_iot, ioh, reg->size);
350
351	return 0;
352}
353
354/*
355 * Bus access routines for xhci(4).
356 */
357
358uint8_t
359octxctl_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
364uint16_t
365octxctl_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
370uint32_t
371octxctl_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
376void
377octxctl_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
383void
384octxctl_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
390void
391octxctl_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