xref: /openbsd-src/sys/dev/pci/ehci_pci.c (revision 4c1e55dc91edd6e69ccc60ce855900fbc12cf34f)
1 /*	$OpenBSD: ehci_pci.c,v 1.23 2011/04/26 00:37:34 deraadt 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 	ehci_softc_t		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 void	ehci_pci_givecontroller(struct ehci_pci_softc *);
79 void	ehci_pci_takecontroller(struct ehci_pci_softc *, int);
80 void	ehci_pci_shutdown(void *);
81 
82 struct cfattach ehci_pci_ca = {
83 	sizeof(struct ehci_pci_softc), ehci_pci_match, ehci_pci_attach,
84 	ehci_pci_detach, ehci_pci_activate
85 };
86 
87 int
88 ehci_pci_match(struct device *parent, void *match, void *aux)
89 {
90 	struct pci_attach_args *pa = (struct pci_attach_args *) aux;
91 
92 	if (PCI_CLASS(pa->pa_class) == PCI_CLASS_SERIALBUS &&
93 	    PCI_SUBCLASS(pa->pa_class) == PCI_SUBCLASS_SERIALBUS_USB &&
94 	    PCI_INTERFACE(pa->pa_class) == PCI_INTERFACE_EHCI)
95 		return (1);
96 
97 	return (0);
98 }
99 
100 void
101 ehci_pci_attach(struct device *parent, struct device *self, void *aux)
102 {
103 	struct ehci_pci_softc *sc = (struct ehci_pci_softc *)self;
104 	struct pci_attach_args *pa = (struct pci_attach_args *)aux;
105 	pci_chipset_tag_t pc = pa->pa_pc;
106 	pcitag_t tag = pa->pa_tag;
107 	char const *intrstr;
108 	pci_intr_handle_t ih;
109 	const char *vendor;
110 	char *devname = sc->sc.sc_bus.bdev.dv_xname;
111 	usbd_status r;
112 	int s;
113 
114 	/* Map I/O registers */
115 	if (pci_mapreg_map(pa, PCI_CBMEM, PCI_MAPREG_TYPE_MEM, 0,
116 			   &sc->sc.iot, &sc->sc.ioh, NULL, &sc->sc.sc_size, 0)) {
117 		printf(": can't map mem space\n");
118 		return;
119 	}
120 
121 	sc->sc_pc = pc;
122 	sc->sc_tag = tag;
123 	sc->sc.sc_bus.dmatag = pa->pa_dmat;
124 
125 	/* Disable interrupts, so we don't get any spurious ones. */
126 	s = splhardusb();
127 	sc->sc.sc_offs = EREAD1(&sc->sc, EHCI_CAPLENGTH);
128 	DPRINTF(("%s: offs=%d\n", devname, sc->sc.sc_offs));
129 	EOWRITE2(&sc->sc, EHCI_USBINTR, 0);
130 
131 	/* Handle quirks */
132 	switch (PCI_VENDOR(pa->pa_id)) {
133 	case PCI_VENDOR_ATI:
134 		if (PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_ATI_SB600_EHCI ||
135 		    (PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_ATI_SB700_EHCI &&
136 		     pci_find_device(NULL, ehci_sb700_match))) {
137 			pcireg_t value;
138 
139 			/* apply the ATI SB600/SB700 workaround */
140 			value = pci_conf_read(sc->sc_pc, sc->sc_tag,
141 			    EHCI_SBx00_WORKAROUND_REG);
142 			pci_conf_write(sc->sc_pc, sc->sc_tag,
143 			    EHCI_SBx00_WORKAROUND_REG, value |
144 			    EHCI_SBx00_WORKAROUND_ENABLE);
145 		}
146 		break;
147 
148 	case PCI_VENDOR_VIATECH:
149 		if (PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_VIATECH_VT6202 &&
150 		    (PCI_REVISION(pa->pa_class) & 0xf0) == 0x60) {
151 			pcireg_t value;
152 
153 			/*
154 			 * The VT6202 defaults to a 1 usec EHCI sleep time
155 			 * which hogs the PCI bus *badly*. Setting bit 5 of
156 			 * the register makes that sleep time use the conventional
157 			 * 10 usec.
158 			 */
159 			value = pci_conf_read(sc->sc_pc, sc->sc_tag,
160 			    EHCI_VT6202_WORKAROUND_REG);
161 			pci_conf_write(sc->sc_pc, sc->sc_tag,
162 			    EHCI_VT6202_WORKAROUND_REG, value | 0x20000000);
163 		}
164 		break;
165 	}
166 
167 	/* Map and establish the interrupt. */
168 	if (pci_intr_map(pa, &ih)) {
169 		printf(": couldn't map interrupt\n");
170 		goto unmap_ret;
171 	}
172 	intrstr = pci_intr_string(pc, ih);
173 	sc->sc_ih = pci_intr_establish(pc, ih, IPL_USB, ehci_intr, sc, devname);
174 	if (sc->sc_ih == NULL) {
175 		printf(": couldn't establish interrupt");
176 		if (intrstr != NULL)
177 			printf(" at %s", intrstr);
178 		printf("\n");
179 		goto unmap_ret;
180 	}
181 	printf(": %s\n", intrstr);
182 
183 	switch(pci_conf_read(pc, tag, PCI_USBREV) & PCI_USBREV_MASK) {
184 	case PCI_USBREV_PRE_1_0:
185 	case PCI_USBREV_1_0:
186 	case PCI_USBREV_1_1:
187 		sc->sc.sc_bus.usbrev = USBREV_UNKNOWN;
188 		printf("%s: pre-2.0 USB rev\n", devname);
189 		goto disestablish_ret;
190 	case PCI_USBREV_2_0:
191 		sc->sc.sc_bus.usbrev = USBREV_2_0;
192 		break;
193 	default:
194 		sc->sc.sc_bus.usbrev = USBREV_UNKNOWN;
195 		break;
196 	}
197 
198 	/* Figure out vendor for root hub descriptor. */
199 	vendor = pci_findvendor(pa->pa_id);
200 	sc->sc.sc_id_vendor = PCI_VENDOR(pa->pa_id);
201 	if (vendor)
202 		strlcpy(sc->sc.sc_vendor, vendor, sizeof(sc->sc.sc_vendor));
203 	else
204 		snprintf(sc->sc.sc_vendor, sizeof(sc->sc.sc_vendor),
205 		    "vendor 0x%04x", PCI_VENDOR(pa->pa_id));
206 
207 	/* Enable workaround for dropped interrupts as required */
208 	if (sc->sc.sc_id_vendor == PCI_VENDOR_VIATECH)
209 		sc->sc.sc_flags |= EHCIF_DROPPED_INTR_WORKAROUND;
210 
211 	ehci_pci_takecontroller(sc, 0);
212 	r = ehci_init(&sc->sc);
213 	if (r != USBD_NORMAL_COMPLETION) {
214 		printf("%s: init failed, error=%d\n", devname, r);
215 		goto disestablish_ret;
216 	}
217 
218 	sc->sc.sc_shutdownhook = shutdownhook_establish(ehci_pci_shutdown, sc);
219 	splx(s);
220 
221 	/* Attach usb device. */
222 	sc->sc.sc_child = config_found((void *)sc, &sc->sc.sc_bus,
223 				       usbctlprint);
224 
225 	return;
226 
227 disestablish_ret:
228 	pci_intr_disestablish(sc->sc_pc, sc->sc_ih);
229 unmap_ret:
230 	bus_space_unmap(sc->sc.iot, sc->sc.ioh, sc->sc.sc_size);
231 	splx(s);
232 }
233 
234 int
235 ehci_pci_activate(struct device *self, int act)
236 {
237 	struct ehci_pci_softc *sc = (struct ehci_pci_softc *)self;
238 
239 	/* On resume, take ownership from the BIOS */
240 	switch (act) {
241 	case DVACT_RESUME:
242 		ehci_pci_takecontroller(sc, 1);
243 		break;
244 	}
245 
246 	return ehci_activate(self, act);
247 }
248 
249 int
250 ehci_pci_detach(struct device *self, int flags)
251 {
252 	struct ehci_pci_softc *sc = (struct ehci_pci_softc *)self;
253 	int rv;
254 
255 	rv = ehci_detach(&sc->sc, flags);
256 	if (rv)
257 		return (rv);
258 	if (sc->sc_ih != NULL) {
259 		pci_intr_disestablish(sc->sc_pc, sc->sc_ih);
260 		sc->sc_ih = NULL;
261 	}
262 	if (sc->sc.sc_size) {
263 		bus_space_unmap(sc->sc.iot, sc->sc.ioh, sc->sc.sc_size);
264 		sc->sc.sc_size = 0;
265 	}
266 	return (0);
267 }
268 
269 #if 0	/* not used */
270 void
271 ehci_pci_givecontroller(struct ehci_pci_softc *sc)
272 {
273 	u_int32_t cparams, eec, legsup;
274 	int eecp;
275 
276 	cparams = EREAD4(&sc->sc, EHCI_HCCPARAMS);
277 	for (eecp = EHCI_HCC_EECP(cparams); eecp != 0;
278 	    eecp = EHCI_EECP_NEXT(eec)) {
279 		eec = pci_conf_read(sc->sc_pc, sc->sc_tag, eecp);
280 		if (EHCI_EECP_ID(eec) != EHCI_EC_LEGSUP)
281 			continue;
282 		legsup = eec;
283 		pci_conf_write(sc->sc_pc, sc->sc_tag, eecp,
284 		    legsup & ~EHCI_LEGSUP_OSOWNED);
285 	}
286 }
287 #endif
288 
289 void
290 ehci_pci_takecontroller(struct ehci_pci_softc *sc, int silent)
291 {
292 	u_int32_t cparams, eec, legsup;
293 	int eecp, i;
294 
295 	cparams = EREAD4(&sc->sc, EHCI_HCCPARAMS);
296 	/* Synchronise with the BIOS if it owns the controller. */
297 	for (eecp = EHCI_HCC_EECP(cparams); eecp != 0;
298 	    eecp = EHCI_EECP_NEXT(eec)) {
299 		eec = pci_conf_read(sc->sc_pc, sc->sc_tag, eecp);
300 		if (EHCI_EECP_ID(eec) != EHCI_EC_LEGSUP)
301 			continue;
302 		legsup = eec;
303 		if (legsup & EHCI_LEGSUP_BIOSOWNED) {
304 			pci_conf_write(sc->sc_pc, sc->sc_tag, eecp,
305 			    legsup | EHCI_LEGSUP_OSOWNED);
306 			DPRINTF(("%s: waiting for BIOS to give up control\n",
307 			    sc->sc.sc_bus.bdev.dv_xname));
308 			for (i = 0; i < 5000; i++) {
309 				legsup = pci_conf_read(sc->sc_pc, sc->sc_tag,
310 				    eecp);
311 				if ((legsup & EHCI_LEGSUP_BIOSOWNED) == 0)
312 					break;
313 				DELAY(1000);
314 			}
315 			if (silent == 0 && (legsup & EHCI_LEGSUP_BIOSOWNED))
316 				printf("%s: timed out waiting for BIOS\n",
317 				    sc->sc.sc_bus.bdev.dv_xname);
318 		}
319 	}
320 }
321 
322 void
323 ehci_pci_shutdown(void *v)
324 {
325 	struct ehci_pci_softc *sc = (struct ehci_pci_softc *)v;
326 
327 	ehci_shutdown(&sc->sc);
328 #if 0
329 	/* best not to do this anymore; BIOS SMM spins? */
330 	ehci_pci_givecontroller(sc);
331 #endif
332 }
333 
334 int
335 ehci_sb700_match(struct pci_attach_args *pa)
336 {
337 	if (PCI_VENDOR(pa->pa_id) == PCI_VENDOR_ATI &&
338 	    PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_ATI_SBX00_SMB &&
339 	    (PCI_REVISION(pa->pa_class) == 0x3a ||
340 	     PCI_REVISION(pa->pa_class) == 0x3b))
341 		return (1);
342 
343 	return (0);
344 }
345