1 /* $OpenBSD: exehci.c,v 1.11 2021/10/24 17:52:27 mpi Exp $ */
2 /*
3 * Copyright (c) 2012-2013 Patrick Wildt <patrick@blueri.se>
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17
18 #include <sys/param.h>
19 #include <sys/systm.h>
20 #include <sys/device.h>
21
22 #include <machine/intr.h>
23 #include <machine/bus.h>
24 #include <machine/fdt.h>
25
26 #include <dev/usb/usb.h>
27 #include <dev/usb/usbdi.h>
28 #include <dev/usb/usbdivar.h>
29
30 #include <dev/ofw/openfirm.h>
31 #include <dev/ofw/ofw_clock.h>
32 #include <dev/ofw/ofw_gpio.h>
33 #include <dev/ofw/ofw_misc.h>
34 #include <dev/ofw/fdt.h>
35
36 #include <dev/usb/ehcireg.h>
37 #include <dev/usb/ehcivar.h>
38
39 /* registers */
40 #define USBPHY_CTRL0 0x00
41 #define USBPHY_TUNE0 0x04
42 #define HSICPHY_CTRL1 0x10
43 #define HSICPHY_TUNE1 0x14
44 #define HSICPHY_CTRL2 0x20
45 #define HSICPHY_TUNE2 0x24
46 #define EHCI_CTRL 0x30
47 #define OHCI_CTRL 0x34
48 #define USBOTG_SYS 0x38
49 #define USBOTG_TUNE 0x40
50
51 /* bits and bytes */
52 #define CLK_24MHZ 5
53
54 #define HOST_CTRL0_PHYSWRSTALL (1U << 31)
55 #define HOST_CTRL0_COMMONON_N (1 << 9)
56 #define HOST_CTRL0_SIDDQ (1 << 6)
57 #define HOST_CTRL0_FORCESLEEP (1 << 5)
58 #define HOST_CTRL0_FORCESUSPEND (1 << 4)
59 #define HOST_CTRL0_WORDINTERFACE (1 << 3)
60 #define HOST_CTRL0_UTMISWRST (1 << 2)
61 #define HOST_CTRL0_LINKSWRST (1 << 1)
62 #define HOST_CTRL0_PHYSWRST (1 << 0)
63
64 #define HOST_CTRL0_FSEL_MASK (7 << 16)
65
66 #define EHCI_CTRL_ENAINCRXALIGN (1 << 29)
67 #define EHCI_CTRL_ENAINCR4 (1 << 28)
68 #define EHCI_CTRL_ENAINCR8 (1 << 27)
69 #define EHCI_CTRL_ENAINCR16 (1 << 26)
70
71 /* SYSREG registers */
72 #define USB20PHY_CFG 0x230
73 #define USB20PHY_CFG_HOST_LINK_EN (1 << 0)
74
75 /* PMU registers */
76 #define USB_HOST_POWER_5250 0x708
77 #define USB_HOST_POWER_54XX 0x70c
78 #define USB_HOST_POWER_EN (1 << 0)
79
80 int exehci_match(struct device *, void *, void *);
81 void exehci_attach(struct device *, struct device *, void *);
82 int exehci_detach(struct device *, int);
83
84 struct exehci_softc {
85 struct ehci_softc sc;
86 void *sc_ih;
87 int sc_phy;
88 bus_space_handle_t ph_ioh;
89 };
90
91 const struct cfattach exehci_ca = {
92 sizeof (struct exehci_softc), exehci_match, exehci_attach,
93 exehci_detach
94 };
95
96 struct cfdriver exehci_cd = {
97 NULL, "exehci", DV_DULL
98 };
99
100 void exehci_setup(struct exehci_softc *);
101
102 int
exehci_match(struct device * parent,void * match,void * aux)103 exehci_match(struct device *parent, void *match, void *aux)
104 {
105 struct fdt_attach_args *faa = aux;
106
107 return OF_is_compatible(faa->fa_node, "samsung,exynos4210-ehci");
108 }
109
110 void
exehci_attach(struct device * parent,struct device * self,void * aux)111 exehci_attach(struct device *parent, struct device *self, void *aux)
112 {
113 struct exehci_softc *sc = (struct exehci_softc *)self;
114 struct fdt_attach_args *faa = aux;
115 usbd_status r;
116 char *devname = sc->sc.sc_bus.bdev.dv_xname;
117 uint32_t phys[2];
118 uint32_t phy_reg[2];
119 int node;
120
121 if (faa->fa_nreg < 1)
122 return;
123
124 node = OF_child(faa->fa_node);
125 if (node == 0)
126 node = faa->fa_node;
127
128 if (OF_getpropintarray(node, "phys", phys,
129 sizeof(phys)) != sizeof(phys))
130 return;
131
132 sc->sc_phy = OF_getnodebyphandle(phys[0]);
133 if (sc->sc_phy == 0)
134 return;
135
136 if (OF_getpropintarray(sc->sc_phy, "reg", phy_reg,
137 sizeof(phy_reg)) != sizeof(phy_reg))
138 return;
139
140 sc->sc.iot = faa->fa_iot;
141 sc->sc.sc_bus.dmatag = faa->fa_dmat;
142 sc->sc.sc_size = faa->fa_reg[0].size;
143
144 /* Map I/O space */
145 if (bus_space_map(sc->sc.iot, faa->fa_reg[0].addr,
146 faa->fa_reg[0].size, 0, &sc->sc.ioh)) {
147 printf(": cannot map mem space\n");
148 goto out;
149 }
150
151 if (bus_space_map(sc->sc.iot, phy_reg[0],
152 phy_reg[1], 0, &sc->ph_ioh)) {
153 printf(": cannot map mem space\n");
154 goto mem0;
155 }
156
157 printf("\n");
158
159 clock_enable_all(faa->fa_node);
160
161 exehci_setup(sc);
162
163 sc->sc_ih = arm_intr_establish_fdt(faa->fa_node, IPL_USB,
164 ehci_intr, &sc->sc, devname);
165 if (sc->sc_ih == NULL) {
166 printf(": unable to establish interrupt\n");
167 goto mem1;
168 }
169
170 strlcpy(sc->sc.sc_vendor, "Exynos 5", sizeof(sc->sc.sc_vendor));
171 r = ehci_init(&sc->sc);
172 if (r != USBD_NORMAL_COMPLETION) {
173 printf("%s: init failed, error=%d\n", devname, r);
174 goto intr;
175 }
176
177 config_found(self, &sc->sc.sc_bus, usbctlprint);
178
179 goto out;
180
181 intr:
182 arm_intr_disestablish(sc->sc_ih);
183 sc->sc_ih = NULL;
184 mem1:
185 bus_space_unmap(sc->sc.iot, sc->ph_ioh, phy_reg[1]);
186 mem0:
187 bus_space_unmap(sc->sc.iot, sc->sc.ioh, sc->sc.sc_size);
188 sc->sc.sc_size = 0;
189 out:
190 return;
191 }
192
193 int
exehci_detach(struct device * self,int flags)194 exehci_detach(struct device *self, int flags)
195 {
196 struct exehci_softc *sc = (struct exehci_softc *)self;
197 int rv;
198
199 rv = ehci_detach(self, flags);
200 if (rv)
201 return rv;
202
203 if (sc->sc_ih != NULL) {
204 arm_intr_disestablish(sc->sc_ih);
205 sc->sc_ih = NULL;
206 }
207
208 if (sc->sc.sc_size) {
209 bus_space_unmap(sc->sc.iot, sc->sc.ioh, sc->sc.sc_size);
210 sc->sc.sc_size = 0;
211 }
212
213 return 0;
214 }
215
216 void
exehci_setup(struct exehci_softc * sc)217 exehci_setup(struct exehci_softc *sc)
218 {
219 struct regmap *pmurm, *sysrm;
220 uint32_t pmureg, sysreg;
221 bus_size_t offset;
222 uint32_t val;
223 int node;
224
225 #if 0
226 /* VBUS, GPIO_X11, only on SMDK5250 and Chromebooks */
227 exgpio_set_dir(0xa9, EXGPIO_DIR_OUT);
228 exgpio_set_bit(0xa9);
229 delay(3000);
230 #endif
231
232 /* Enable host mode. */
233 sysreg = OF_getpropint(sc->sc_phy, "samsung,sysreg-phandle", 0);
234 sysrm = regmap_byphandle(sysreg);
235 if (sysrm) {
236 val = regmap_read_4(sysrm, USB20PHY_CFG);
237 val |= USB20PHY_CFG_HOST_LINK_EN;
238 regmap_write_4(sysrm, USB20PHY_CFG, val);
239 }
240
241 /* Power up the PHY block. */
242 pmureg = OF_getpropint(sc->sc_phy, "samsung,pmureg-phandle", 0);
243 pmurm = regmap_byphandle(pmureg);
244 if (pmurm) {
245 node = OF_getnodebyphandle(pmureg);
246 if (OF_is_compatible(node, "samsung,exynos5250-pmu"))
247 offset = USB_HOST_POWER_5250;
248 else
249 offset = USB_HOST_POWER_54XX;
250
251 val = regmap_read_4(pmurm, offset);
252 val |= USB_HOST_POWER_EN;
253 regmap_write_4(pmurm, offset, val);
254 }
255
256 delay(10000);
257
258 /* Setting up host and device simultaneously */
259 val = bus_space_read_4(sc->sc.iot, sc->ph_ioh, USBPHY_CTRL0);
260 val &= ~(HOST_CTRL0_FSEL_MASK |
261 /* HOST Phy setting */
262 HOST_CTRL0_PHYSWRST |
263 HOST_CTRL0_PHYSWRSTALL |
264 HOST_CTRL0_SIDDQ |
265 HOST_CTRL0_FORCESUSPEND |
266 HOST_CTRL0_FORCESLEEP);
267 val |= (/* Setting up the ref freq */
268 CLK_24MHZ << 16 |
269 HOST_CTRL0_COMMONON_N |
270 /* HOST Phy setting */
271 HOST_CTRL0_LINKSWRST |
272 HOST_CTRL0_UTMISWRST);
273 bus_space_write_4(sc->sc.iot, sc->ph_ioh, USBPHY_CTRL0, val);
274 delay(10000);
275 bus_space_write_4(sc->sc.iot, sc->ph_ioh, USBPHY_CTRL0,
276 bus_space_read_4(sc->sc.iot, sc->ph_ioh, USBPHY_CTRL0) &
277 ~(HOST_CTRL0_LINKSWRST | HOST_CTRL0_UTMISWRST));
278 delay(20000);
279
280 /* EHCI Ctrl setting */
281 bus_space_write_4(sc->sc.iot, sc->ph_ioh, EHCI_CTRL,
282 bus_space_read_4(sc->sc.iot, sc->ph_ioh, EHCI_CTRL) |
283 EHCI_CTRL_ENAINCRXALIGN |
284 EHCI_CTRL_ENAINCR4 |
285 EHCI_CTRL_ENAINCR8 |
286 EHCI_CTRL_ENAINCR16);
287
288 #if 0
289 /* HSIC USB Hub initialization. */
290 if (1) {
291 exgpio_set_dir(0xc8, EXGPIO_DIR_OUT);
292 exgpio_clear_bit(0xc8);
293 delay(1000);
294 exgpio_set_bit(0xc8);
295 delay(5000);
296
297 val = bus_space_read_4(sc->sc.iot, sc->ph_ioh, HSICPHY_CTRL1);
298 val &= ~(HOST_CTRL0_SIDDQ |
299 HOST_CTRL0_FORCESLEEP |
300 HOST_CTRL0_FORCESUSPEND);
301 bus_space_write_4(sc->sc.iot, sc->ph_ioh, HSICPHY_CTRL1, val);
302 val |= HOST_CTRL0_PHYSWRST;
303 bus_space_write_4(sc->sc.iot, sc->ph_ioh, HSICPHY_CTRL1, val);
304 delay(1000);
305 val &= ~HOST_CTRL0_PHYSWRST;
306 bus_space_write_4(sc->sc.iot, sc->ph_ioh, HSICPHY_CTRL1, val);
307 }
308 #endif
309
310 /* PHY clock and power setup time */
311 delay(50000);
312 }
313