1 /* $OpenBSD: mvxhci.c,v 1.4 2021/10/24 17:52:27 mpi Exp $ */
2 /*
3 * Copyright (c) 2017 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/types.h>
19 #include <sys/systm.h>
20 #include <sys/device.h>
21
22 #include <machine/bus.h>
23 #include <machine/fdt.h>
24
25 #include <armv7/marvell/mvmbusvar.h>
26
27 #include <dev/ofw/openfirm.h>
28 #include <dev/ofw/ofw_clock.h>
29 #include <dev/ofw/fdt.h>
30
31 #include <dev/usb/usb.h>
32 #include <dev/usb/usbdi.h>
33 #include <dev/usb/usbdivar.h>
34
35 #include <dev/usb/xhcireg.h>
36 #include <dev/usb/xhcivar.h>
37
38 #define MVXHCI_READ(sc, reg) \
39 bus_space_read_4((sc)->sc.iot, (sc)->mbus_ioh, (reg))
40 #define MVXHCI_WRITE(sc, reg, val) \
41 bus_space_write_4((sc)->sc.iot, (sc)->mbus_ioh, (reg), (val))
42
43 #define MVXHCI_NWINDOW 4
44 #define MVXHCI_CTRL(x) (0x0 + ((x) << 3))
45 #define MVXHCI_BASE(x) (0x4 + ((x) << 3))
46
47 #define MVXHCI_TARGET(target) (((target) & 0xf) << 4)
48 #define MVXHCI_ATTR(attr) (((attr) & 0xff) << 8)
49 #define MVXHCI_BASEADDR(base) ((base) & 0xffff0000)
50 #define MVXHCI_SIZE(size) (((size) - 1) & 0xffff0000)
51 #define MVXHCI_WINEN (1 << 0)
52
53 struct mvxhci_softc {
54 struct xhci_softc sc;
55 int sc_node;
56 bus_space_handle_t mbus_ioh;
57 void *sc_ih;
58 };
59
60 void mvxhci_wininit(struct mvxhci_softc *);
61
62 int mvxhci_match(struct device *, void *, void *);
63 void mvxhci_attach(struct device *, struct device *, void *);
64
65 const struct cfattach mvxhci_ca = {
66 sizeof (struct mvxhci_softc), mvxhci_match, mvxhci_attach
67 };
68
69 struct cfdriver mvxhci_cd = {
70 NULL, "mvxhci", DV_DULL
71 };
72
73 void
mvxhci_wininit(struct mvxhci_softc * sc)74 mvxhci_wininit(struct mvxhci_softc *sc)
75 {
76 int i;
77
78 if (mvmbus_dram_info == NULL)
79 panic("%s: mbus dram information not set up", __func__);
80
81 for (i = 0; i < MVXHCI_NWINDOW; i++) {
82 MVXHCI_WRITE(sc, MVXHCI_CTRL(i), 0);
83 MVXHCI_WRITE(sc, MVXHCI_BASE(i), 0);
84 }
85
86 for (i = 0; i < mvmbus_dram_info->numcs; i++) {
87 struct mbus_dram_window *win = &mvmbus_dram_info->cs[i];
88
89 MVXHCI_WRITE(sc, MVXHCI_CTRL(i),
90 MVXHCI_WINEN |
91 MVXHCI_TARGET(mvmbus_dram_info->targetid) |
92 MVXHCI_ATTR(win->attr) |
93 MVXHCI_SIZE(win->size));
94 MVXHCI_WRITE(sc, MVXHCI_BASE(i), MVXHCI_BASEADDR(win->base));
95 }
96 }
97
98 int
mvxhci_match(struct device * parent,void * match,void * aux)99 mvxhci_match(struct device *parent, void *match, void *aux)
100 {
101 struct fdt_attach_args *faa = aux;
102
103 return OF_is_compatible(faa->fa_node, "marvell,armada-375-xhci") ||
104 OF_is_compatible(faa->fa_node, "marvell,armada-380-xhci");
105 }
106
107 void
mvxhci_attach(struct device * parent,struct device * self,void * aux)108 mvxhci_attach(struct device *parent, struct device *self, void *aux)
109 {
110 struct mvxhci_softc *sc = (struct mvxhci_softc *)self;
111 struct fdt_attach_args *faa = aux;
112 int error;
113
114 if (faa->fa_nreg < 2) {
115 printf(": no registers\n");
116 return;
117 }
118
119 sc->sc_node = faa->fa_node;
120 sc->sc.iot = faa->fa_iot;
121 sc->sc.sc_bus.dmatag = faa->fa_dmat;
122 sc->sc.sc_size = faa->fa_reg[0].size;
123
124 if (bus_space_map(sc->sc.iot, faa->fa_reg[0].addr,
125 faa->fa_reg[0].size, 0, &sc->sc.ioh)) {
126 printf(": can't map registers\n");
127 return;
128 }
129
130 clock_enable_all(faa->fa_node);
131 reset_deassert_all(sc->sc_node);
132
133 if (bus_space_map(sc->sc.iot, faa->fa_reg[1].addr,
134 faa->fa_reg[1].size, 0, &sc->mbus_ioh)) {
135 printf(": can't map registers\n");
136 goto unmap;
137 }
138
139 /* Set up MBUS windows. */
140 mvxhci_wininit(sc);
141
142 bus_space_unmap(sc->sc.iot, sc->mbus_ioh, faa->fa_reg[1].size);
143
144 sc->sc_ih = arm_intr_establish_fdt(faa->fa_node, IPL_USB,
145 xhci_intr, &sc->sc, sc->sc.sc_bus.bdev.dv_xname);
146 if (sc->sc_ih == NULL) {
147 printf(": can't establish interrupt\n");
148 goto unmap;
149 }
150
151 strlcpy(sc->sc.sc_vendor, "Marvell", sizeof(sc->sc.sc_vendor));
152 if ((error = xhci_init(&sc->sc)) != 0) {
153 printf("%s: init failed, error=%d\n",
154 sc->sc.sc_bus.bdev.dv_xname, error);
155 goto disestablish_ret;
156 }
157
158 /* Attach usb device. */
159 config_found(self, &sc->sc.sc_bus, usbctlprint);
160
161 /* Now that the stack is ready, config' the HC and enable interrupts. */
162 xhci_config(&sc->sc);
163
164 return;
165
166 disestablish_ret:
167 arm_intr_disestablish_fdt(sc->sc_ih);
168 unmap:
169 bus_space_unmap(sc->sc.iot, sc->sc.ioh, sc->sc.sc_size);
170 }
171