Line data Source code
1 : /* $OpenBSD: ehci_pci.c,v 1.30 2016/07/20 09:48:06 mpi Exp $ */
2 : /* $NetBSD: ehci_pci.c,v 1.15 2004/04/23 21:13:06 itojun Exp $ */
3 :
4 : /*
5 : * Copyright (c) 2001, 2002 The NetBSD Foundation, Inc.
6 : * All rights reserved.
7 : *
8 : * This code is derived from software contributed to The NetBSD Foundation
9 : * by Lennart Augustsson (lennart@augustsson.net).
10 : *
11 : * Redistribution and use in source and binary forms, with or without
12 : * modification, are permitted provided that the following conditions
13 : * are met:
14 : * 1. Redistributions of source code must retain the above copyright
15 : * notice, this list of conditions and the following disclaimer.
16 : * 2. Redistributions in binary form must reproduce the above copyright
17 : * notice, this list of conditions and the following disclaimer in the
18 : * documentation and/or other materials provided with the distribution.
19 : *
20 : * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
21 : * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22 : * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23 : * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
24 : * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25 : * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26 : * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27 : * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28 : * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 : * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 : * POSSIBILITY OF SUCH DAMAGE.
31 : */
32 :
33 : #include <sys/param.h>
34 : #include <sys/systm.h>
35 : #include <sys/kernel.h>
36 : #include <sys/rwlock.h>
37 : #include <sys/device.h>
38 : #include <sys/timeout.h>
39 : #include <sys/queue.h>
40 :
41 : #include <machine/bus.h>
42 :
43 : #include <dev/pci/pcidevs.h>
44 : #include <dev/pci/pcivar.h>
45 :
46 : #include <dev/usb/usb.h>
47 : #include <dev/usb/usbdi.h>
48 : #include <dev/usb/usbdivar.h>
49 : #include <dev/usb/usb_mem.h>
50 :
51 : #include <dev/usb/ehcireg.h>
52 : #include <dev/usb/ehcivar.h>
53 :
54 : #ifdef EHCI_DEBUG
55 : #define DPRINTF(x) if (ehcidebug) printf x
56 : extern int ehcidebug;
57 : #else
58 : #define DPRINTF(x)
59 : #endif
60 :
61 : struct ehci_pci_softc {
62 : struct ehci_softc sc;
63 : pci_chipset_tag_t sc_pc;
64 : pcitag_t sc_tag;
65 : void *sc_ih; /* interrupt vectoring */
66 : };
67 :
68 : int ehci_sb700_match(struct pci_attach_args *pa);
69 :
70 : #define EHCI_SBx00_WORKAROUND_REG 0x50
71 : #define EHCI_SBx00_WORKAROUND_ENABLE (1 << 3)
72 : #define EHCI_VT6202_WORKAROUND_REG 0x48
73 :
74 : int ehci_pci_match(struct device *, void *, void *);
75 : void ehci_pci_attach(struct device *, struct device *, void *);
76 : int ehci_pci_detach(struct device *, int);
77 : int ehci_pci_activate(struct device *, int);
78 : #if 0
79 : void ehci_pci_givecontroller(struct ehci_pci_softc *);
80 : #endif
81 : void ehci_pci_takecontroller(struct ehci_pci_softc *, int);
82 :
83 : struct cfattach ehci_pci_ca = {
84 : sizeof(struct ehci_pci_softc), ehci_pci_match, ehci_pci_attach,
85 : ehci_pci_detach, ehci_pci_activate
86 : };
87 :
88 : int
89 0 : ehci_pci_match(struct device *parent, void *match, void *aux)
90 : {
91 0 : struct pci_attach_args *pa = (struct pci_attach_args *) aux;
92 :
93 0 : if (PCI_CLASS(pa->pa_class) == PCI_CLASS_SERIALBUS &&
94 0 : PCI_SUBCLASS(pa->pa_class) == PCI_SUBCLASS_SERIALBUS_USB &&
95 0 : PCI_INTERFACE(pa->pa_class) == PCI_INTERFACE_EHCI)
96 0 : return (1);
97 :
98 0 : return (0);
99 0 : }
100 :
101 : void
102 0 : ehci_pci_attach(struct device *parent, struct device *self, void *aux)
103 : {
104 0 : struct ehci_pci_softc *sc = (struct ehci_pci_softc *)self;
105 0 : struct pci_attach_args *pa = (struct pci_attach_args *)aux;
106 0 : pci_chipset_tag_t pc = pa->pa_pc;
107 0 : pcitag_t tag = pa->pa_tag;
108 : char const *intrstr;
109 0 : pci_intr_handle_t ih;
110 : const char *vendor;
111 0 : char *devname = sc->sc.sc_bus.bdev.dv_xname;
112 : usbd_status r;
113 : int s;
114 :
115 : /* Map I/O registers */
116 0 : if (pci_mapreg_map(pa, PCI_CBMEM, PCI_MAPREG_TYPE_MEM, 0,
117 0 : &sc->sc.iot, &sc->sc.ioh, NULL, &sc->sc.sc_size, 0)) {
118 0 : printf(": can't map mem space\n");
119 0 : return;
120 : }
121 :
122 0 : sc->sc_pc = pc;
123 0 : sc->sc_tag = tag;
124 0 : sc->sc.sc_bus.dmatag = pa->pa_dmat;
125 :
126 : /* Disable interrupts, so we don't get any spurious ones. */
127 0 : s = splhardusb();
128 0 : sc->sc.sc_offs = EREAD1(&sc->sc, EHCI_CAPLENGTH);
129 : DPRINTF(("%s: offs=%d\n", devname, sc->sc.sc_offs));
130 0 : EOWRITE2(&sc->sc, EHCI_USBINTR, 0);
131 :
132 : /* Handle quirks */
133 0 : switch (PCI_VENDOR(pa->pa_id)) {
134 : case PCI_VENDOR_ATI:
135 0 : if (PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_ATI_SB600_EHCI ||
136 0 : (PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_ATI_SB700_EHCI &&
137 0 : pci_find_device(NULL, ehci_sb700_match))) {
138 : pcireg_t value;
139 :
140 : /* apply the ATI SB600/SB700 workaround */
141 0 : value = pci_conf_read(sc->sc_pc, sc->sc_tag,
142 : EHCI_SBx00_WORKAROUND_REG);
143 0 : pci_conf_write(sc->sc_pc, sc->sc_tag,
144 0 : EHCI_SBx00_WORKAROUND_REG, value |
145 : EHCI_SBx00_WORKAROUND_ENABLE);
146 0 : }
147 : break;
148 :
149 : case PCI_VENDOR_VIATECH:
150 0 : if (PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_VIATECH_VT6202 &&
151 0 : (PCI_REVISION(pa->pa_class) & 0xf0) == 0x60) {
152 : pcireg_t value;
153 :
154 : /*
155 : * The VT6202 defaults to a 1 usec EHCI sleep time
156 : * which hogs the PCI bus *badly*. Setting bit 5 of
157 : * the register makes that sleep time use the conventional
158 : * 10 usec.
159 : */
160 0 : value = pci_conf_read(sc->sc_pc, sc->sc_tag,
161 : EHCI_VT6202_WORKAROUND_REG);
162 0 : pci_conf_write(sc->sc_pc, sc->sc_tag,
163 0 : EHCI_VT6202_WORKAROUND_REG, value | 0x20000000);
164 0 : }
165 : break;
166 : }
167 :
168 : /* Map and establish the interrupt. */
169 0 : if (pci_intr_map(pa, &ih)) {
170 0 : printf(": couldn't map interrupt\n");
171 0 : goto unmap_ret;
172 : }
173 0 : intrstr = pci_intr_string(pc, ih);
174 0 : sc->sc_ih = pci_intr_establish(pc, ih, IPL_USB | IPL_MPSAFE,
175 0 : ehci_intr, sc, devname);
176 0 : if (sc->sc_ih == NULL) {
177 0 : printf(": couldn't establish interrupt");
178 0 : if (intrstr != NULL)
179 0 : printf(" at %s", intrstr);
180 0 : printf("\n");
181 0 : goto unmap_ret;
182 : }
183 0 : printf(": %s\n", intrstr);
184 :
185 0 : switch(pci_conf_read(pc, tag, PCI_USBREV) & PCI_USBREV_MASK) {
186 : case PCI_USBREV_PRE_1_0:
187 : case PCI_USBREV_1_0:
188 : case PCI_USBREV_1_1:
189 0 : sc->sc.sc_bus.usbrev = USBREV_UNKNOWN;
190 0 : printf("%s: pre-2.0 USB rev\n", devname);
191 0 : goto disestablish_ret;
192 : case PCI_USBREV_2_0:
193 0 : sc->sc.sc_bus.usbrev = USBREV_2_0;
194 0 : break;
195 : default:
196 0 : sc->sc.sc_bus.usbrev = USBREV_UNKNOWN;
197 0 : break;
198 : }
199 :
200 : /* Figure out vendor for root hub descriptor. */
201 0 : vendor = pci_findvendor(pa->pa_id);
202 0 : sc->sc.sc_id_vendor = PCI_VENDOR(pa->pa_id);
203 0 : if (vendor)
204 0 : strlcpy(sc->sc.sc_vendor, vendor, sizeof(sc->sc.sc_vendor));
205 : else
206 0 : snprintf(sc->sc.sc_vendor, sizeof(sc->sc.sc_vendor),
207 0 : "vendor 0x%04x", PCI_VENDOR(pa->pa_id));
208 :
209 : /* Enable workaround for dropped interrupts as required */
210 0 : switch (sc->sc.sc_id_vendor) {
211 : case PCI_VENDOR_ATI:
212 : case PCI_VENDOR_VIATECH:
213 0 : sc->sc.sc_flags |= EHCIF_DROPPED_INTR_WORKAROUND;
214 0 : break;
215 : default:
216 : break;
217 : }
218 :
219 0 : ehci_pci_takecontroller(sc, 0);
220 0 : r = ehci_init(&sc->sc);
221 0 : if (r != USBD_NORMAL_COMPLETION) {
222 0 : printf("%s: init failed, error=%d\n", devname, r);
223 0 : goto disestablish_ret;
224 : }
225 :
226 : /* Attach usb device. */
227 0 : config_found(self, &sc->sc.sc_bus, usbctlprint);
228 0 : splx(s);
229 0 : return;
230 :
231 : disestablish_ret:
232 0 : pci_intr_disestablish(sc->sc_pc, sc->sc_ih);
233 : unmap_ret:
234 0 : bus_space_unmap(sc->sc.iot, sc->sc.ioh, sc->sc.sc_size);
235 0 : splx(s);
236 0 : }
237 :
238 : int
239 0 : ehci_pci_activate(struct device *self, int act)
240 : {
241 0 : struct ehci_pci_softc *sc = (struct ehci_pci_softc *)self;
242 : int rv;
243 :
244 0 : switch (act) {
245 : case DVACT_RESUME:
246 0 : ehci_pci_takecontroller(sc, 1);
247 0 : break;
248 : }
249 :
250 0 : rv = ehci_activate(self, act);
251 :
252 : #if 0
253 : switch (act) {
254 : case DVACT_POWERDOWN:
255 : ehci_pci_givecontroller(sc);
256 : break;
257 : }
258 : #endif
259 0 : return (rv);
260 : }
261 :
262 : int
263 0 : ehci_pci_detach(struct device *self, int flags)
264 : {
265 0 : struct ehci_pci_softc *sc = (struct ehci_pci_softc *)self;
266 : int rv;
267 :
268 0 : rv = ehci_detach(self, flags);
269 0 : if (rv)
270 0 : return (rv);
271 0 : if (sc->sc_ih != NULL) {
272 0 : pci_intr_disestablish(sc->sc_pc, sc->sc_ih);
273 0 : sc->sc_ih = NULL;
274 0 : }
275 0 : if (sc->sc.sc_size) {
276 0 : bus_space_unmap(sc->sc.iot, sc->sc.ioh, sc->sc.sc_size);
277 0 : sc->sc.sc_size = 0;
278 0 : }
279 0 : return (0);
280 0 : }
281 :
282 : #if 0
283 : void
284 : ehci_pci_givecontroller(struct ehci_pci_softc *sc)
285 : {
286 : u_int32_t cparams, eec, legsup;
287 : int eecp;
288 :
289 : cparams = EREAD4(&sc->sc, EHCI_HCCPARAMS);
290 : for (eecp = EHCI_HCC_EECP(cparams); eecp != 0;
291 : eecp = EHCI_EECP_NEXT(eec)) {
292 : eec = pci_conf_read(sc->sc_pc, sc->sc_tag, eecp);
293 : if (EHCI_EECP_ID(eec) != EHCI_EC_LEGSUP)
294 : continue;
295 : legsup = eec;
296 : pci_conf_write(sc->sc_pc, sc->sc_tag, eecp,
297 : legsup & ~EHCI_LEGSUP_OSOWNED);
298 : }
299 : }
300 : #endif
301 :
302 : void
303 0 : ehci_pci_takecontroller(struct ehci_pci_softc *sc, int silent)
304 : {
305 : u_int32_t cparams, eec, legsup;
306 : int eecp, i;
307 :
308 0 : cparams = EREAD4(&sc->sc, EHCI_HCCPARAMS);
309 : /* Synchronise with the BIOS if it owns the controller. */
310 0 : for (eecp = EHCI_HCC_EECP(cparams); eecp != 0;
311 0 : eecp = EHCI_EECP_NEXT(eec)) {
312 0 : eec = pci_conf_read(sc->sc_pc, sc->sc_tag, eecp);
313 0 : if (EHCI_EECP_ID(eec) != EHCI_EC_LEGSUP)
314 : continue;
315 : legsup = eec;
316 0 : if (legsup & EHCI_LEGSUP_BIOSOWNED) {
317 0 : pci_conf_write(sc->sc_pc, sc->sc_tag, eecp,
318 0 : legsup | EHCI_LEGSUP_OSOWNED);
319 : DPRINTF(("%s: waiting for BIOS to give up control\n",
320 : sc->sc.sc_bus.bdev.dv_xname));
321 0 : for (i = 0; i < 5000; i++) {
322 0 : legsup = pci_conf_read(sc->sc_pc, sc->sc_tag,
323 : eecp);
324 0 : if ((legsup & EHCI_LEGSUP_BIOSOWNED) == 0)
325 : break;
326 0 : DELAY(1000);
327 : }
328 0 : if (silent == 0 && (legsup & EHCI_LEGSUP_BIOSOWNED))
329 0 : printf("%s: timed out waiting for BIOS\n",
330 0 : sc->sc.sc_bus.bdev.dv_xname);
331 : }
332 : }
333 0 : }
334 :
335 : int
336 0 : ehci_sb700_match(struct pci_attach_args *pa)
337 : {
338 0 : if (PCI_VENDOR(pa->pa_id) == PCI_VENDOR_ATI &&
339 0 : PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_ATI_SBX00_SMB &&
340 0 : (PCI_REVISION(pa->pa_class) == 0x3a ||
341 0 : PCI_REVISION(pa->pa_class) == 0x3b))
342 0 : return (1);
343 :
344 0 : return (0);
345 0 : }
|