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