xref: /openbsd-src/sys/arch/octeon/dev/octuctl.c (revision 0b7734b3d77bb9b21afec6f4621cae6c805dbd45)
1 /*	$OpenBSD: octuctl.c,v 1.1 2016/03/18 05:38:10 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/octeon_model.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
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
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
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
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
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
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
109 octuctl_match(struct device *parent, void *match, void *aux)
110 {
111 	int id;
112 
113 	id = octeon_get_chipid();
114 	switch (octeon_model_family(id)) {
115 	case OCTEON_MODEL_FAMILY_CN61XX:
116 		return (1);
117 	default:
118 		return (0);
119 	}
120 }
121 
122 int
123 octuctlprint(void *aux, const char *parentname)
124 {
125 	return (QUIET);
126 }
127 
128 void
129 octuctl_clock_setup(struct octuctl_softc *sc, uint64_t ctl)
130 {
131 	int div;
132 	int lastdiv;
133 	int validdiv[] = { 1, 2, 3, 4, 6, 8, 12, INT_MAX };
134 	int i;
135 
136 	div = octeon_ioclock_speed() / UCTL_CLK_TARGET_FREQ;
137 
138 	/* start usb controller reset */
139 	ctl |= UCTL_CLK_RST_CTL_P_POR;
140 	ctl &= ~(UCTL_CLK_RST_CTL_HRST |
141 	    UCTL_CLK_RST_CTL_P_PRST |
142 	    UCTL_CLK_RST_CTL_O_CLKDIV_EN |
143 	    UCTL_CLK_RST_CTL_H_CLKDIV_EN |
144 	    UCTL_CLK_RST_CTL_H_CLKDIV_RST |
145 	    UCTL_CLK_RST_CTL_O_CLKDIV_RST);
146 	bus_space_write_8(sc->sc_iot, sc->sc_ioh, UCTL_CLK_RST_CTL, ctl);
147 
148 	/* set up for 12mhz crystal */
149 	ctl &= ~((3 << UCTL_CLK_RST_CTL_P_REFCLK_DIV_SHIFT) |
150 	    (3 << UCTL_CLK_RST_CTL_P_REFCLK_SEL_SHIFT));
151 	bus_space_write_8(sc->sc_iot, sc->sc_ioh, UCTL_CLK_RST_CTL, ctl);
152 
153 	/* set clock divider */
154 	lastdiv = 1;
155 	for (i = 0; i < nitems(validdiv); i++) {
156 		if (div < validdiv[i]) {
157 			div = lastdiv;
158 			break;
159 		}
160 		lastdiv = validdiv[i];
161 	}
162 
163 	ctl &= ~(0xf << UCTL_CLK_RST_CTL_H_DIV_SHIFT);
164 	ctl |= (div << UCTL_CLK_RST_CTL_H_DIV_SHIFT);
165 	bus_space_write_8(sc->sc_iot, sc->sc_ioh, UCTL_CLK_RST_CTL, ctl);
166 
167 	/* turn hclk on */
168 	ctl = bus_space_read_8(sc->sc_iot, sc->sc_ioh,
169 	    UCTL_CLK_RST_CTL);
170 	ctl |= UCTL_CLK_RST_CTL_H_CLKDIV_EN;
171 	bus_space_write_8(sc->sc_iot, sc->sc_ioh, UCTL_CLK_RST_CTL, ctl);
172 	ctl |= UCTL_CLK_RST_CTL_H_CLKDIV_RST;
173 	bus_space_write_8(sc->sc_iot, sc->sc_ioh, UCTL_CLK_RST_CTL, ctl);
174 
175 	delay(1);
176 
177 	/* power-on-reset finished */
178 	ctl &= ~UCTL_CLK_RST_CTL_P_POR;
179 	bus_space_write_8(sc->sc_iot, sc->sc_ioh, UCTL_CLK_RST_CTL, ctl);
180 
181 	delay(1000);
182 
183 	/* set up ohci clocks */
184 	ctl |= UCTL_CLK_RST_CTL_O_CLKDIV_RST;
185 	bus_space_write_8(sc->sc_iot, sc->sc_ioh, UCTL_CLK_RST_CTL, ctl);
186 	ctl |= UCTL_CLK_RST_CTL_O_CLKDIV_EN;
187 	bus_space_write_8(sc->sc_iot, sc->sc_ioh, UCTL_CLK_RST_CTL, ctl);
188 
189 	delay(1);
190 
191 	/* phy reset */
192 	ctl |= UCTL_CLK_RST_CTL_P_PRST;
193 	bus_space_write_8(sc->sc_iot, sc->sc_ioh, UCTL_CLK_RST_CTL, ctl);
194 
195 	delay(1);
196 
197 	/* clear host reset */
198 	ctl |= UCTL_CLK_RST_CTL_HRST;
199 	bus_space_write_8(sc->sc_iot, sc->sc_ioh, UCTL_CLK_RST_CTL, ctl);
200 }
201 
202 void
203 octuctl_attach(struct device *parent, struct device *self, void *aux)
204 {
205 	struct octuctl_softc *sc = (struct octuctl_softc *)self;
206 	struct iobus_attach_args *aa = aux;
207 	struct octuctl_attach_args uaa;
208 	uint64_t port_ctl;
209 	uint64_t ctl;
210 	uint64_t preg;
211 	uint64_t txvref;
212 	int rc;
213 	int port;
214 
215 	sc->sc_iot = aa->aa_bust;
216 	rc = bus_space_map(sc->sc_iot, UCTL_BASE, UCTL_SIZE,
217 	    0, &sc->sc_ioh);
218 	KASSERT(rc == 0);
219 
220 	/* do clock setup if not already done */
221 	bus_space_write_8(sc->sc_iot, sc->sc_ioh, UCTL_IF_ENA,
222 	    UCTL_IF_ENA_EN);
223 	ctl = bus_space_read_8(sc->sc_iot, sc->sc_ioh, UCTL_CLK_RST_CTL);
224 	if ((ctl & UCTL_CLK_RST_CTL_HRST) == 0)
225 		octuctl_clock_setup(sc, ctl);
226 
227 	/* port phy settings */
228 	for (port = 0; port < 2; port++) {
229 		preg = UCTL_UPHY_PORTX_STATUS + (port * 8);
230 		port_ctl = bus_space_read_8(sc->sc_iot, sc->sc_ioh, preg);
231 		txvref = 0xf;
232 		port_ctl |= (UCTL_UPHY_PORTX_STATUS_TXPREEMPHTUNE |
233 		    UCTL_UPHY_PORTX_STATUS_TXRISETUNE |
234 		    (txvref << UCTL_UPHY_PORTX_STATUS_TXVREF_SHIFT));
235 		bus_space_write_8(sc->sc_iot, sc->sc_ioh, preg, port_ctl);
236 	}
237 
238 	printf("\n");
239 
240 	uaa.aa_octuctl_bust = aa->aa_bust;
241 	uaa.aa_bust = &octuctl_tag;
242 	uaa.aa_dmat = aa->aa_dmat;
243 	uaa.aa_ioh = sc->sc_ioh;
244 
245 	uaa.aa_name = "ehci";
246 	config_found(self, &uaa, octuctlprint);
247 
248 	uaa.aa_name = "ohci";
249 	config_found(self, &uaa, octuctlprint);
250 }
251