1*fe5dbe47Sjmatthew /* $OpenBSD: octuctl.c,v 1.2 2017/07/25 11:01:28 jmatthew Exp $ */
2494889e9Sjmatthew
3494889e9Sjmatthew /*
4494889e9Sjmatthew * Copyright (c) 2015 Jonathan Matthew <jmatthew@openbsd.org>
5494889e9Sjmatthew *
6494889e9Sjmatthew * Permission to use, copy, modify, and/or distribute this software for any
7494889e9Sjmatthew * purpose with or without fee is hereby granted, provided that the above
8494889e9Sjmatthew * copyright notice and this permission notice appear in all copies.
9494889e9Sjmatthew *
10494889e9Sjmatthew * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11494889e9Sjmatthew * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12494889e9Sjmatthew * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13494889e9Sjmatthew * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14494889e9Sjmatthew * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15494889e9Sjmatthew * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16494889e9Sjmatthew * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17494889e9Sjmatthew */
18494889e9Sjmatthew
19494889e9Sjmatthew #include <sys/param.h>
20494889e9Sjmatthew #include <sys/systm.h>
21494889e9Sjmatthew #include <sys/device.h>
22494889e9Sjmatthew
23494889e9Sjmatthew #include <machine/intr.h>
24494889e9Sjmatthew #include <machine/bus.h>
25494889e9Sjmatthew #include <machine/octeonreg.h>
26494889e9Sjmatthew #include <machine/octeonvar.h>
27*fe5dbe47Sjmatthew #include <machine/fdt.h>
28494889e9Sjmatthew
29494889e9Sjmatthew #include <octeon/dev/iobusvar.h>
30494889e9Sjmatthew #include <octeon/dev/octuctlreg.h>
31494889e9Sjmatthew #include <octeon/dev/octuctlvar.h>
32494889e9Sjmatthew
33494889e9Sjmatthew struct octuctl_softc {
34494889e9Sjmatthew struct device sc_dev;
35494889e9Sjmatthew bus_space_tag_t sc_iot;
36494889e9Sjmatthew bus_space_handle_t sc_ioh;
37494889e9Sjmatthew };
38494889e9Sjmatthew
39494889e9Sjmatthew int octuctl_match(struct device *, void *, void *);
40494889e9Sjmatthew void octuctl_attach(struct device *, struct device *, void *);
41494889e9Sjmatthew
42494889e9Sjmatthew const struct cfattach octuctl_ca = {
43494889e9Sjmatthew sizeof(struct octuctl_softc), octuctl_match, octuctl_attach,
44494889e9Sjmatthew };
45494889e9Sjmatthew
46494889e9Sjmatthew struct cfdriver octuctl_cd = {
47494889e9Sjmatthew NULL, "octuctl", DV_DULL
48494889e9Sjmatthew };
49494889e9Sjmatthew
50494889e9Sjmatthew uint8_t octuctl_read_1(bus_space_tag_t, bus_space_handle_t, bus_size_t);
51494889e9Sjmatthew uint16_t octuctl_read_2(bus_space_tag_t, bus_space_handle_t, bus_size_t);
52494889e9Sjmatthew uint32_t octuctl_read_4(bus_space_tag_t, bus_space_handle_t, bus_size_t);
53494889e9Sjmatthew void octuctl_write_1(bus_space_tag_t, bus_space_handle_t, bus_size_t, uint8_t);
54494889e9Sjmatthew void octuctl_write_2(bus_space_tag_t, bus_space_handle_t, bus_size_t, uint16_t);
55494889e9Sjmatthew void octuctl_write_4(bus_space_tag_t, bus_space_handle_t, bus_size_t, uint32_t);
56494889e9Sjmatthew
57494889e9Sjmatthew bus_space_t octuctl_tag = {
58494889e9Sjmatthew .bus_base = PHYS_TO_XKPHYS(0, CCA_NC),
59494889e9Sjmatthew .bus_private = NULL,
60494889e9Sjmatthew ._space_read_1 = octuctl_read_1,
61494889e9Sjmatthew ._space_write_1 = octuctl_write_1,
62494889e9Sjmatthew ._space_read_2 = octuctl_read_2,
63494889e9Sjmatthew ._space_write_2 = octuctl_write_2,
64494889e9Sjmatthew ._space_read_4 = octuctl_read_4,
65494889e9Sjmatthew ._space_write_4 = octuctl_write_4,
66494889e9Sjmatthew ._space_map = iobus_space_map,
67494889e9Sjmatthew ._space_unmap = iobus_space_unmap,
68494889e9Sjmatthew ._space_subregion = generic_space_region,
69494889e9Sjmatthew ._space_vaddr = generic_space_vaddr
70494889e9Sjmatthew };
71494889e9Sjmatthew
72494889e9Sjmatthew uint8_t
octuctl_read_1(bus_space_tag_t t,bus_space_handle_t h,bus_size_t o)73494889e9Sjmatthew octuctl_read_1(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o)
74494889e9Sjmatthew {
75494889e9Sjmatthew return *(volatile uint8_t *)(h + (o^3));
76494889e9Sjmatthew }
77494889e9Sjmatthew
78494889e9Sjmatthew uint16_t
octuctl_read_2(bus_space_tag_t t,bus_space_handle_t h,bus_size_t o)79494889e9Sjmatthew octuctl_read_2(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o)
80494889e9Sjmatthew {
81494889e9Sjmatthew return *(volatile uint16_t *)(h + (o^2));
82494889e9Sjmatthew }
83494889e9Sjmatthew
84494889e9Sjmatthew uint32_t
octuctl_read_4(bus_space_tag_t t,bus_space_handle_t h,bus_size_t o)85494889e9Sjmatthew octuctl_read_4(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o)
86494889e9Sjmatthew {
87494889e9Sjmatthew return *(volatile uint32_t *)(h + o);
88494889e9Sjmatthew }
89494889e9Sjmatthew
90494889e9Sjmatthew void
octuctl_write_1(bus_space_tag_t t,bus_space_handle_t h,bus_size_t o,uint8_t v)91494889e9Sjmatthew octuctl_write_1(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o, uint8_t v)
92494889e9Sjmatthew {
93494889e9Sjmatthew *(volatile uint8_t *)(h + (o^3)) = v;
94494889e9Sjmatthew }
95494889e9Sjmatthew
96494889e9Sjmatthew void
octuctl_write_2(bus_space_tag_t t,bus_space_handle_t h,bus_size_t o,uint16_t v)97494889e9Sjmatthew octuctl_write_2(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o, uint16_t v)
98494889e9Sjmatthew {
99494889e9Sjmatthew *(volatile uint16_t *)(h + (o^2)) = v;
100494889e9Sjmatthew }
101494889e9Sjmatthew
102494889e9Sjmatthew void
octuctl_write_4(bus_space_tag_t t,bus_space_handle_t h,bus_size_t o,uint32_t v)103494889e9Sjmatthew octuctl_write_4(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o, uint32_t v)
104494889e9Sjmatthew {
105494889e9Sjmatthew *(volatile uint32_t *)(h + o) = v;
106494889e9Sjmatthew }
107494889e9Sjmatthew
108494889e9Sjmatthew int
octuctl_match(struct device * parent,void * match,void * aux)109494889e9Sjmatthew octuctl_match(struct device *parent, void *match, void *aux)
110494889e9Sjmatthew {
111*fe5dbe47Sjmatthew struct fdt_attach_args *faa = aux;
112494889e9Sjmatthew
113*fe5dbe47Sjmatthew return OF_is_compatible(faa->fa_node, "cavium,octeon-6335-uctl");
114494889e9Sjmatthew }
115494889e9Sjmatthew
116494889e9Sjmatthew int
octuctlprint(void * aux,const char * parentname)117494889e9Sjmatthew octuctlprint(void *aux, const char *parentname)
118494889e9Sjmatthew {
119494889e9Sjmatthew return (QUIET);
120494889e9Sjmatthew }
121494889e9Sjmatthew
122494889e9Sjmatthew void
octuctl_clock_setup(struct octuctl_softc * sc,uint64_t ctl)123494889e9Sjmatthew octuctl_clock_setup(struct octuctl_softc *sc, uint64_t ctl)
124494889e9Sjmatthew {
125494889e9Sjmatthew int div;
126494889e9Sjmatthew int lastdiv;
127494889e9Sjmatthew int validdiv[] = { 1, 2, 3, 4, 6, 8, 12, INT_MAX };
128494889e9Sjmatthew int i;
129494889e9Sjmatthew
130494889e9Sjmatthew div = octeon_ioclock_speed() / UCTL_CLK_TARGET_FREQ;
131494889e9Sjmatthew
132494889e9Sjmatthew /* start usb controller reset */
133494889e9Sjmatthew ctl |= UCTL_CLK_RST_CTL_P_POR;
134494889e9Sjmatthew ctl &= ~(UCTL_CLK_RST_CTL_HRST |
135494889e9Sjmatthew UCTL_CLK_RST_CTL_P_PRST |
136494889e9Sjmatthew UCTL_CLK_RST_CTL_O_CLKDIV_EN |
137494889e9Sjmatthew UCTL_CLK_RST_CTL_H_CLKDIV_EN |
138494889e9Sjmatthew UCTL_CLK_RST_CTL_H_CLKDIV_RST |
139494889e9Sjmatthew UCTL_CLK_RST_CTL_O_CLKDIV_RST);
140494889e9Sjmatthew bus_space_write_8(sc->sc_iot, sc->sc_ioh, UCTL_CLK_RST_CTL, ctl);
141494889e9Sjmatthew
142494889e9Sjmatthew /* set up for 12mhz crystal */
143494889e9Sjmatthew ctl &= ~((3 << UCTL_CLK_RST_CTL_P_REFCLK_DIV_SHIFT) |
144494889e9Sjmatthew (3 << UCTL_CLK_RST_CTL_P_REFCLK_SEL_SHIFT));
145494889e9Sjmatthew bus_space_write_8(sc->sc_iot, sc->sc_ioh, UCTL_CLK_RST_CTL, ctl);
146494889e9Sjmatthew
147494889e9Sjmatthew /* set clock divider */
148494889e9Sjmatthew lastdiv = 1;
149494889e9Sjmatthew for (i = 0; i < nitems(validdiv); i++) {
150494889e9Sjmatthew if (div < validdiv[i]) {
151494889e9Sjmatthew div = lastdiv;
152494889e9Sjmatthew break;
153494889e9Sjmatthew }
154494889e9Sjmatthew lastdiv = validdiv[i];
155494889e9Sjmatthew }
156494889e9Sjmatthew
157494889e9Sjmatthew ctl &= ~(0xf << UCTL_CLK_RST_CTL_H_DIV_SHIFT);
158494889e9Sjmatthew ctl |= (div << UCTL_CLK_RST_CTL_H_DIV_SHIFT);
159494889e9Sjmatthew bus_space_write_8(sc->sc_iot, sc->sc_ioh, UCTL_CLK_RST_CTL, ctl);
160494889e9Sjmatthew
161494889e9Sjmatthew /* turn hclk on */
162494889e9Sjmatthew ctl = bus_space_read_8(sc->sc_iot, sc->sc_ioh,
163494889e9Sjmatthew UCTL_CLK_RST_CTL);
164494889e9Sjmatthew ctl |= UCTL_CLK_RST_CTL_H_CLKDIV_EN;
165494889e9Sjmatthew bus_space_write_8(sc->sc_iot, sc->sc_ioh, UCTL_CLK_RST_CTL, ctl);
166494889e9Sjmatthew ctl |= UCTL_CLK_RST_CTL_H_CLKDIV_RST;
167494889e9Sjmatthew bus_space_write_8(sc->sc_iot, sc->sc_ioh, UCTL_CLK_RST_CTL, ctl);
168494889e9Sjmatthew
169494889e9Sjmatthew delay(1);
170494889e9Sjmatthew
171494889e9Sjmatthew /* power-on-reset finished */
172494889e9Sjmatthew ctl &= ~UCTL_CLK_RST_CTL_P_POR;
173494889e9Sjmatthew bus_space_write_8(sc->sc_iot, sc->sc_ioh, UCTL_CLK_RST_CTL, ctl);
174494889e9Sjmatthew
175494889e9Sjmatthew delay(1000);
176494889e9Sjmatthew
177494889e9Sjmatthew /* set up ohci clocks */
178494889e9Sjmatthew ctl |= UCTL_CLK_RST_CTL_O_CLKDIV_RST;
179494889e9Sjmatthew bus_space_write_8(sc->sc_iot, sc->sc_ioh, UCTL_CLK_RST_CTL, ctl);
180494889e9Sjmatthew ctl |= UCTL_CLK_RST_CTL_O_CLKDIV_EN;
181494889e9Sjmatthew bus_space_write_8(sc->sc_iot, sc->sc_ioh, UCTL_CLK_RST_CTL, ctl);
182494889e9Sjmatthew
183494889e9Sjmatthew delay(1);
184494889e9Sjmatthew
185494889e9Sjmatthew /* phy reset */
186494889e9Sjmatthew ctl |= UCTL_CLK_RST_CTL_P_PRST;
187494889e9Sjmatthew bus_space_write_8(sc->sc_iot, sc->sc_ioh, UCTL_CLK_RST_CTL, ctl);
188494889e9Sjmatthew
189494889e9Sjmatthew delay(1);
190494889e9Sjmatthew
191494889e9Sjmatthew /* clear host reset */
192494889e9Sjmatthew ctl |= UCTL_CLK_RST_CTL_HRST;
193494889e9Sjmatthew bus_space_write_8(sc->sc_iot, sc->sc_ioh, UCTL_CLK_RST_CTL, ctl);
194494889e9Sjmatthew }
195494889e9Sjmatthew
196494889e9Sjmatthew void
octuctl_attach(struct device * parent,struct device * self,void * aux)197494889e9Sjmatthew octuctl_attach(struct device *parent, struct device *self, void *aux)
198494889e9Sjmatthew {
199*fe5dbe47Sjmatthew struct fdt_attach_args *faa = aux;
200494889e9Sjmatthew struct octuctl_softc *sc = (struct octuctl_softc *)self;
201494889e9Sjmatthew struct octuctl_attach_args uaa;
202494889e9Sjmatthew uint64_t port_ctl;
203494889e9Sjmatthew uint64_t ctl;
204494889e9Sjmatthew uint64_t preg;
205494889e9Sjmatthew uint64_t txvref;
206*fe5dbe47Sjmatthew uint32_t reg[4];
207494889e9Sjmatthew int port;
208*fe5dbe47Sjmatthew int node;
209*fe5dbe47Sjmatthew int rc;
210494889e9Sjmatthew
211*fe5dbe47Sjmatthew if (faa->fa_nreg != 1) {
212*fe5dbe47Sjmatthew printf(": expected one IO space, got %d\n", faa->fa_nreg);
213*fe5dbe47Sjmatthew return;
214*fe5dbe47Sjmatthew }
215*fe5dbe47Sjmatthew
216*fe5dbe47Sjmatthew sc->sc_iot = faa->fa_iot;
217*fe5dbe47Sjmatthew if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr, faa->fa_reg[0].size,
218*fe5dbe47Sjmatthew 0, &sc->sc_ioh)) {
219*fe5dbe47Sjmatthew printf(": could not map IO space\n");
220*fe5dbe47Sjmatthew return;
221*fe5dbe47Sjmatthew }
222*fe5dbe47Sjmatthew
223*fe5dbe47Sjmatthew rc = OF_getpropint(faa->fa_node, "#address-cells", 0);
224*fe5dbe47Sjmatthew if (rc != 2) {
225*fe5dbe47Sjmatthew printf(": expected #address-cells 2, got %d\n", rc);
226*fe5dbe47Sjmatthew return;
227*fe5dbe47Sjmatthew }
228*fe5dbe47Sjmatthew rc = OF_getpropint(faa->fa_node, "#size-cells", 0);
229*fe5dbe47Sjmatthew if (rc != 2) {
230*fe5dbe47Sjmatthew printf(": expected #size-cells 2, got %d\n", rc);
231*fe5dbe47Sjmatthew return;
232*fe5dbe47Sjmatthew }
233494889e9Sjmatthew
234494889e9Sjmatthew /* do clock setup if not already done */
235494889e9Sjmatthew bus_space_write_8(sc->sc_iot, sc->sc_ioh, UCTL_IF_ENA,
236494889e9Sjmatthew UCTL_IF_ENA_EN);
237494889e9Sjmatthew ctl = bus_space_read_8(sc->sc_iot, sc->sc_ioh, UCTL_CLK_RST_CTL);
238494889e9Sjmatthew if ((ctl & UCTL_CLK_RST_CTL_HRST) == 0)
239494889e9Sjmatthew octuctl_clock_setup(sc, ctl);
240494889e9Sjmatthew
241494889e9Sjmatthew /* port phy settings */
242494889e9Sjmatthew for (port = 0; port < 2; port++) {
243494889e9Sjmatthew preg = UCTL_UPHY_PORTX_STATUS + (port * 8);
244494889e9Sjmatthew port_ctl = bus_space_read_8(sc->sc_iot, sc->sc_ioh, preg);
245494889e9Sjmatthew txvref = 0xf;
246494889e9Sjmatthew port_ctl |= (UCTL_UPHY_PORTX_STATUS_TXPREEMPHTUNE |
247494889e9Sjmatthew UCTL_UPHY_PORTX_STATUS_TXRISETUNE |
248494889e9Sjmatthew (txvref << UCTL_UPHY_PORTX_STATUS_TXVREF_SHIFT));
249494889e9Sjmatthew bus_space_write_8(sc->sc_iot, sc->sc_ioh, preg, port_ctl);
250494889e9Sjmatthew }
251494889e9Sjmatthew
252494889e9Sjmatthew printf("\n");
253494889e9Sjmatthew
254*fe5dbe47Sjmatthew uaa.aa_octuctl_bust = sc->sc_iot;
255494889e9Sjmatthew uaa.aa_bust = &octuctl_tag;
256*fe5dbe47Sjmatthew uaa.aa_dmat = faa->fa_dmat;
257494889e9Sjmatthew uaa.aa_ioh = sc->sc_ioh;
258494889e9Sjmatthew
259*fe5dbe47Sjmatthew for (node = OF_child(faa->fa_node); node != 0; node = OF_peer(node)) {
260*fe5dbe47Sjmatthew if (OF_getproplen(node, "reg") != sizeof(reg))
261*fe5dbe47Sjmatthew continue;
262494889e9Sjmatthew
263*fe5dbe47Sjmatthew OF_getpropintarray(node, "reg", reg, sizeof(reg));
264*fe5dbe47Sjmatthew uaa.aa_reg.addr = (((uint64_t)reg[0]) << 32) | reg[1];
265*fe5dbe47Sjmatthew uaa.aa_reg.size = (((uint64_t)reg[2]) << 32) | reg[3];
266*fe5dbe47Sjmatthew uaa.aa_node = node;
267494889e9Sjmatthew config_found(self, &uaa, octuctlprint);
268494889e9Sjmatthew }
269*fe5dbe47Sjmatthew }
270