xref: /openbsd-src/sys/arch/octeon/dev/octuctl.c (revision fe5dbe476cf4c4896041a8fff255ac2e7a02e845)
1 /*	$OpenBSD: octuctl.c,v 1.2 2017/07/25 11:01:28 jmatthew Exp $ */
2 
3 /*
4  * Copyright (c) 2015 Jonathan Matthew  <jmatthew@openbsd.org>
5  *
6  * Permission to use, copy, modify, and/or 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 #include <sys/param.h>
20 #include <sys/systm.h>
21 #include <sys/device.h>
22 
23 #include <machine/intr.h>
24 #include <machine/bus.h>
25 #include <machine/octeonreg.h>
26 #include <machine/octeonvar.h>
27 #include <machine/fdt.h>
28 
29 #include <octeon/dev/iobusvar.h>
30 #include <octeon/dev/octuctlreg.h>
31 #include <octeon/dev/octuctlvar.h>
32 
33 struct octuctl_softc {
34 	struct device		sc_dev;
35 	bus_space_tag_t		sc_iot;
36 	bus_space_handle_t	sc_ioh;
37 };
38 
39 int	octuctl_match(struct device *, void *, void *);
40 void	octuctl_attach(struct device *, struct device *, void *);
41 
42 const struct cfattach octuctl_ca = {
43 	sizeof(struct octuctl_softc), octuctl_match, octuctl_attach,
44 };
45 
46 struct cfdriver octuctl_cd = {
47 	NULL, "octuctl", DV_DULL
48 };
49 
50 uint8_t octuctl_read_1(bus_space_tag_t, bus_space_handle_t, bus_size_t);
51 uint16_t octuctl_read_2(bus_space_tag_t, bus_space_handle_t, bus_size_t);
52 uint32_t octuctl_read_4(bus_space_tag_t, bus_space_handle_t, bus_size_t);
53 void octuctl_write_1(bus_space_tag_t, bus_space_handle_t, bus_size_t, uint8_t);
54 void octuctl_write_2(bus_space_tag_t, bus_space_handle_t, bus_size_t, uint16_t);
55 void octuctl_write_4(bus_space_tag_t, bus_space_handle_t, bus_size_t, uint32_t);
56 
57 bus_space_t octuctl_tag = {
58 	.bus_base = PHYS_TO_XKPHYS(0, CCA_NC),
59 	.bus_private = NULL,
60 	._space_read_1 = octuctl_read_1,
61 	._space_write_1 = octuctl_write_1,
62 	._space_read_2 = octuctl_read_2,
63 	._space_write_2 = octuctl_write_2,
64 	._space_read_4 = octuctl_read_4,
65 	._space_write_4 = octuctl_write_4,
66 	._space_map = iobus_space_map,
67 	._space_unmap = iobus_space_unmap,
68 	._space_subregion = generic_space_region,
69 	._space_vaddr = generic_space_vaddr
70 };
71 
72 uint8_t
octuctl_read_1(bus_space_tag_t t,bus_space_handle_t h,bus_size_t o)73 octuctl_read_1(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o)
74 {
75 	return *(volatile uint8_t *)(h + (o^3));
76 }
77 
78 uint16_t
octuctl_read_2(bus_space_tag_t t,bus_space_handle_t h,bus_size_t o)79 octuctl_read_2(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o)
80 {
81 	return *(volatile uint16_t *)(h + (o^2));
82 }
83 
84 uint32_t
octuctl_read_4(bus_space_tag_t t,bus_space_handle_t h,bus_size_t o)85 octuctl_read_4(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o)
86 {
87 	return *(volatile uint32_t *)(h + o);
88 }
89 
90 void
octuctl_write_1(bus_space_tag_t t,bus_space_handle_t h,bus_size_t o,uint8_t v)91 octuctl_write_1(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o, uint8_t v)
92 {
93 	*(volatile uint8_t *)(h + (o^3)) = v;
94 }
95 
96 void
octuctl_write_2(bus_space_tag_t t,bus_space_handle_t h,bus_size_t o,uint16_t v)97 octuctl_write_2(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o, uint16_t v)
98 {
99 	*(volatile uint16_t *)(h + (o^2)) = v;
100 }
101 
102 void
octuctl_write_4(bus_space_tag_t t,bus_space_handle_t h,bus_size_t o,uint32_t v)103 octuctl_write_4(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o, uint32_t v)
104 {
105 	*(volatile uint32_t *)(h + o) = v;
106 }
107 
108 int
octuctl_match(struct device * parent,void * match,void * aux)109 octuctl_match(struct device *parent, void *match, void *aux)
110 {
111 	struct fdt_attach_args *faa = aux;
112 
113 	return OF_is_compatible(faa->fa_node, "cavium,octeon-6335-uctl");
114 }
115 
116 int
octuctlprint(void * aux,const char * parentname)117 octuctlprint(void *aux, const char *parentname)
118 {
119 	return (QUIET);
120 }
121 
122 void
octuctl_clock_setup(struct octuctl_softc * sc,uint64_t ctl)123 octuctl_clock_setup(struct octuctl_softc *sc, uint64_t ctl)
124 {
125 	int div;
126 	int lastdiv;
127 	int validdiv[] = { 1, 2, 3, 4, 6, 8, 12, INT_MAX };
128 	int i;
129 
130 	div = octeon_ioclock_speed() / UCTL_CLK_TARGET_FREQ;
131 
132 	/* start usb controller reset */
133 	ctl |= UCTL_CLK_RST_CTL_P_POR;
134 	ctl &= ~(UCTL_CLK_RST_CTL_HRST |
135 	    UCTL_CLK_RST_CTL_P_PRST |
136 	    UCTL_CLK_RST_CTL_O_CLKDIV_EN |
137 	    UCTL_CLK_RST_CTL_H_CLKDIV_EN |
138 	    UCTL_CLK_RST_CTL_H_CLKDIV_RST |
139 	    UCTL_CLK_RST_CTL_O_CLKDIV_RST);
140 	bus_space_write_8(sc->sc_iot, sc->sc_ioh, UCTL_CLK_RST_CTL, ctl);
141 
142 	/* set up for 12mhz crystal */
143 	ctl &= ~((3 << UCTL_CLK_RST_CTL_P_REFCLK_DIV_SHIFT) |
144 	    (3 << UCTL_CLK_RST_CTL_P_REFCLK_SEL_SHIFT));
145 	bus_space_write_8(sc->sc_iot, sc->sc_ioh, UCTL_CLK_RST_CTL, ctl);
146 
147 	/* set clock divider */
148 	lastdiv = 1;
149 	for (i = 0; i < nitems(validdiv); i++) {
150 		if (div < validdiv[i]) {
151 			div = lastdiv;
152 			break;
153 		}
154 		lastdiv = validdiv[i];
155 	}
156 
157 	ctl &= ~(0xf << UCTL_CLK_RST_CTL_H_DIV_SHIFT);
158 	ctl |= (div << UCTL_CLK_RST_CTL_H_DIV_SHIFT);
159 	bus_space_write_8(sc->sc_iot, sc->sc_ioh, UCTL_CLK_RST_CTL, ctl);
160 
161 	/* turn hclk on */
162 	ctl = bus_space_read_8(sc->sc_iot, sc->sc_ioh,
163 	    UCTL_CLK_RST_CTL);
164 	ctl |= UCTL_CLK_RST_CTL_H_CLKDIV_EN;
165 	bus_space_write_8(sc->sc_iot, sc->sc_ioh, UCTL_CLK_RST_CTL, ctl);
166 	ctl |= UCTL_CLK_RST_CTL_H_CLKDIV_RST;
167 	bus_space_write_8(sc->sc_iot, sc->sc_ioh, UCTL_CLK_RST_CTL, ctl);
168 
169 	delay(1);
170 
171 	/* power-on-reset finished */
172 	ctl &= ~UCTL_CLK_RST_CTL_P_POR;
173 	bus_space_write_8(sc->sc_iot, sc->sc_ioh, UCTL_CLK_RST_CTL, ctl);
174 
175 	delay(1000);
176 
177 	/* set up ohci clocks */
178 	ctl |= UCTL_CLK_RST_CTL_O_CLKDIV_RST;
179 	bus_space_write_8(sc->sc_iot, sc->sc_ioh, UCTL_CLK_RST_CTL, ctl);
180 	ctl |= UCTL_CLK_RST_CTL_O_CLKDIV_EN;
181 	bus_space_write_8(sc->sc_iot, sc->sc_ioh, UCTL_CLK_RST_CTL, ctl);
182 
183 	delay(1);
184 
185 	/* phy reset */
186 	ctl |= UCTL_CLK_RST_CTL_P_PRST;
187 	bus_space_write_8(sc->sc_iot, sc->sc_ioh, UCTL_CLK_RST_CTL, ctl);
188 
189 	delay(1);
190 
191 	/* clear host reset */
192 	ctl |= UCTL_CLK_RST_CTL_HRST;
193 	bus_space_write_8(sc->sc_iot, sc->sc_ioh, UCTL_CLK_RST_CTL, ctl);
194 }
195 
196 void
octuctl_attach(struct device * parent,struct device * self,void * aux)197 octuctl_attach(struct device *parent, struct device *self, void *aux)
198 {
199 	struct fdt_attach_args *faa = aux;
200 	struct octuctl_softc *sc = (struct octuctl_softc *)self;
201 	struct octuctl_attach_args uaa;
202 	uint64_t port_ctl;
203 	uint64_t ctl;
204 	uint64_t preg;
205 	uint64_t txvref;
206 	uint32_t reg[4];
207 	int port;
208 	int node;
209 	int rc;
210 
211 	if (faa->fa_nreg != 1) {
212 		printf(": expected one IO space, got %d\n", faa->fa_nreg);
213 		return;
214 	}
215 
216 	sc->sc_iot = faa->fa_iot;
217 	if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr, faa->fa_reg[0].size,
218 	    0, &sc->sc_ioh)) {
219 		printf(": could not map IO space\n");
220 		return;
221 	}
222 
223 	rc = OF_getpropint(faa->fa_node, "#address-cells", 0);
224 	if (rc != 2) {
225 		printf(": expected #address-cells 2, got %d\n", rc);
226 		return;
227 	}
228 	rc = OF_getpropint(faa->fa_node, "#size-cells", 0);
229 	if (rc != 2) {
230 		printf(": expected #size-cells 2, got %d\n", rc);
231 		return;
232 	}
233 
234 	/* do clock setup if not already done */
235 	bus_space_write_8(sc->sc_iot, sc->sc_ioh, UCTL_IF_ENA,
236 	    UCTL_IF_ENA_EN);
237 	ctl = bus_space_read_8(sc->sc_iot, sc->sc_ioh, UCTL_CLK_RST_CTL);
238 	if ((ctl & UCTL_CLK_RST_CTL_HRST) == 0)
239 		octuctl_clock_setup(sc, ctl);
240 
241 	/* port phy settings */
242 	for (port = 0; port < 2; port++) {
243 		preg = UCTL_UPHY_PORTX_STATUS + (port * 8);
244 		port_ctl = bus_space_read_8(sc->sc_iot, sc->sc_ioh, preg);
245 		txvref = 0xf;
246 		port_ctl |= (UCTL_UPHY_PORTX_STATUS_TXPREEMPHTUNE |
247 		    UCTL_UPHY_PORTX_STATUS_TXRISETUNE |
248 		    (txvref << UCTL_UPHY_PORTX_STATUS_TXVREF_SHIFT));
249 		bus_space_write_8(sc->sc_iot, sc->sc_ioh, preg, port_ctl);
250 	}
251 
252 	printf("\n");
253 
254 	uaa.aa_octuctl_bust = sc->sc_iot;
255 	uaa.aa_bust = &octuctl_tag;
256 	uaa.aa_dmat = faa->fa_dmat;
257 	uaa.aa_ioh = sc->sc_ioh;
258 
259 	for (node = OF_child(faa->fa_node); node != 0; node = OF_peer(node)) {
260 		if (OF_getproplen(node, "reg") != sizeof(reg))
261 			continue;
262 
263 		OF_getpropintarray(node, "reg", reg, sizeof(reg));
264 		uaa.aa_reg.addr = (((uint64_t)reg[0]) << 32) | reg[1];
265 		uaa.aa_reg.size = (((uint64_t)reg[2]) << 32) | reg[3];
266 		uaa.aa_node = node;
267 		config_found(self, &uaa, octuctlprint);
268 	}
269 }
270