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