1 /* $NetBSD: ehci_pci.c,v 1.45 2009/06/15 09:18:45 cegger Exp $ */
4 * Copyright (c) 2001, 2002 The NetBSD Foundation, Inc.
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Lennart Augustsson (lennart@augustsson.net).
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
32 #include <sys/cdefs.h>
33 __KERNEL_RCSID(0, "$NetBSD: ehci_pci.c,v 1.45 2009/06/15 09:18:45 cegger Exp $");
35 #include <sys/param.h>
36 #include <sys/systm.h>
37 #include <sys/kernel.h>
38 #include <sys/device.h>
40 #include <sys/queue.h>
44 #include <dev/pci/pcidevs.h>
45 #include <dev/pci/pcivar.h>
46 #include <dev/pci/usb_pci.h>
48 #include <dev/usb/usb.h>
49 #include <dev/usb/usbdi.h>
50 #include <dev/usb/usbdivar.h>
51 #include <dev/usb/usb_mem.h>
53 #include <dev/usb/ehcireg.h>
54 #include <dev/usb/ehcivar.h>
57 #define DPRINTF(x) if (ehcidebug) printf x
63 enum ehci_pci_quirk_flags
{
64 EHCI_PCI_QUIRK_AMD_SB600
= 0x1, /* always need a quirk */
65 EHCI_PCI_QUIRK_AMD_SB700
= 0x2, /* depends on the SMB revision */
68 static const struct pci_quirkdata ehci_pci_quirks
[] = {
69 { PCI_VENDOR_ATI
, PCI_PRODUCT_ATI_SB600_USB_EHCI
,
70 EHCI_PCI_QUIRK_AMD_SB600
},
71 { PCI_VENDOR_ATI
, PCI_PRODUCT_ATI_SB700_USB_EHCI
,
72 EHCI_PCI_QUIRK_AMD_SB700
},
75 static void ehci_release_ownership(ehci_softc_t
*sc
, pci_chipset_tag_t pc
,
77 static void ehci_get_ownership(ehci_softc_t
*sc
, pci_chipset_tag_t pc
,
79 static bool ehci_pci_suspend(device_t
, pmf_qual_t
);
80 static bool ehci_pci_resume(device_t
, pmf_qual_t
);
82 struct ehci_pci_softc
{
84 pci_chipset_tag_t sc_pc
;
86 void *sc_ih
; /* interrupt vectoring */
89 static int ehci_sb700_match(struct pci_attach_args
*pa
);
90 static int ehci_apply_amd_quirks(struct ehci_pci_softc
*sc
);
91 enum ehci_pci_quirk_flags
ehci_pci_lookup_quirkdata(pci_vendor_id_t
,
94 #define EHCI_MAX_BIOS_WAIT 1000 /* ms */
95 #define EHCI_SBx00_WORKAROUND_REG 0x50
96 #define EHCI_SBx00_WORKAROUND_ENABLE __BIT(27)
100 ehci_pci_match(device_t parent
, cfdata_t match
, void *aux
)
102 struct pci_attach_args
*pa
= (struct pci_attach_args
*) aux
;
104 if (PCI_CLASS(pa
->pa_class
) == PCI_CLASS_SERIALBUS
&&
105 PCI_SUBCLASS(pa
->pa_class
) == PCI_SUBCLASS_SERIALBUS_USB
&&
106 PCI_INTERFACE(pa
->pa_class
) == PCI_INTERFACE_EHCI
)
113 ehci_pci_attach(device_t parent
, device_t self
, void *aux
)
115 struct ehci_pci_softc
*sc
= device_private(self
);
116 struct pci_attach_args
*pa
= (struct pci_attach_args
*)aux
;
117 pci_chipset_tag_t pc
= pa
->pa_pc
;
118 pcitag_t tag
= pa
->pa_tag
;
120 pci_intr_handle_t ih
;
129 sc
->sc
.sc_dev
= self
;
130 sc
->sc
.sc_bus
.hci_private
= sc
;
132 aprint_naive(": USB controller\n");
134 pci_devinfo(pa
->pa_id
, pa
->pa_class
, 0, devinfo
, sizeof(devinfo
));
135 aprint_normal(": %s (rev. 0x%02x)\n", devinfo
,
136 PCI_REVISION(pa
->pa_class
));
138 /* Check for quirks */
139 quirk
= ehci_pci_lookup_quirkdata(PCI_VENDOR(pa
->pa_id
),
140 PCI_PRODUCT(pa
->pa_id
));
142 /* Map I/O registers */
143 if (pci_mapreg_map(pa
, PCI_CBMEM
, PCI_MAPREG_TYPE_MEM
, 0,
144 &sc
->sc
.iot
, &sc
->sc
.ioh
, NULL
, &sc
->sc
.sc_size
)) {
146 aprint_error_dev(self
, "can't map memory space\n");
150 /* Disable interrupts, so we don't get any spurious ones. */
151 sc
->sc
.sc_offs
= EREAD1(&sc
->sc
, EHCI_CAPLENGTH
);
152 DPRINTF(("%s: offs=%d\n", device_xname(self
), sc
->sc
.sc_offs
));
153 EOWRITE2(&sc
->sc
, EHCI_USBINTR
, 0);
157 sc
->sc
.sc_bus
.dmatag
= pa
->pa_dmat
;
161 case EHCI_PCI_QUIRK_AMD_SB600
:
162 ehci_apply_amd_quirks(sc
);
164 case EHCI_PCI_QUIRK_AMD_SB700
:
165 if (pci_find_device(NULL
, ehci_sb700_match
))
166 ehci_apply_amd_quirks(sc
);
170 /* Enable the device. */
171 csr
= pci_conf_read(pc
, tag
, PCI_COMMAND_STATUS_REG
);
172 pci_conf_write(pc
, tag
, PCI_COMMAND_STATUS_REG
,
173 csr
| PCI_COMMAND_MASTER_ENABLE
);
175 /* Map and establish the interrupt. */
176 if (pci_intr_map(pa
, &ih
)) {
177 aprint_error_dev(self
, "couldn't map interrupt\n");
184 intrstr
= pci_intr_string(pc
, ih
);
185 sc
->sc_ih
= pci_intr_establish(pc
, ih
, IPL_USB
, ehci_intr
, sc
);
186 if (sc
->sc_ih
== NULL
) {
187 aprint_error_dev(self
, "couldn't establish interrupt");
189 aprint_error(" at %s", intrstr
);
193 aprint_normal_dev(self
, "interrupting at %s\n", intrstr
);
195 switch(pci_conf_read(pc
, tag
, PCI_USBREV
) & PCI_USBREV_MASK
) {
196 case PCI_USBREV_PRE_1_0
:
199 sc
->sc
.sc_bus
.usbrev
= USBREV_UNKNOWN
;
200 aprint_verbose_dev(self
, "pre-2.0 USB rev\n");
203 sc
->sc
.sc_bus
.usbrev
= USBREV_2_0
;
206 sc
->sc
.sc_bus
.usbrev
= USBREV_UNKNOWN
;
210 /* Figure out vendor for root hub descriptor. */
211 vendor
= pci_findvendor(pa
->pa_id
);
212 sc
->sc
.sc_id_vendor
= PCI_VENDOR(pa
->pa_id
);
214 strlcpy(sc
->sc
.sc_vendor
, vendor
, sizeof(sc
->sc
.sc_vendor
));
216 snprintf(sc
->sc
.sc_vendor
, sizeof(sc
->sc
.sc_vendor
),
217 "vendor 0x%04x", PCI_VENDOR(pa
->pa_id
));
219 /* Enable workaround for dropped interrupts as required */
220 switch (sc
->sc
.sc_id_vendor
) {
222 case PCI_VENDOR_VIATECH
:
223 sc
->sc
.sc_flags
|= EHCIF_DROPPED_INTR_WORKAROUND
;
224 aprint_normal_dev(self
, "dropped intr workaround enabled\n");
231 * Find companion controllers. According to the spec they always
232 * have lower function numbers so they should be enumerated already.
235 TAILQ_FOREACH(up
, &ehci_pci_alldevs
, next
) {
236 if (up
->bus
== pa
->pa_bus
&& up
->device
== pa
->pa_device
) {
237 DPRINTF(("ehci_pci_attach: companion %s\n",
238 device_xname(up
->usb
)));
239 sc
->sc
.sc_comps
[ncomp
++] = up
->usb
;
240 if (ncomp
>= EHCI_COMPANION_MAX
)
244 sc
->sc
.sc_ncomp
= ncomp
;
246 ehci_get_ownership(&sc
->sc
, pc
, tag
);
248 r
= ehci_init(&sc
->sc
);
249 if (r
!= USBD_NORMAL_COMPLETION
) {
250 aprint_error_dev(self
, "init failed, error=%d\n", r
);
254 if (!pmf_device_register1(self
, ehci_pci_suspend
, ehci_pci_resume
,
256 aprint_error_dev(self
, "couldn't establish power handler\n");
258 /* Attach usb device. */
259 sc
->sc
.sc_child
= config_found(self
, &sc
->sc
.sc_bus
, usbctlprint
);
264 pci_intr_disestablish(sc
->sc_pc
, sc
->sc_ih
);
267 if (sc
->sc
.sc_size
) {
268 ehci_release_ownership(&sc
->sc
, sc
->sc_pc
, sc
->sc_tag
);
269 bus_space_unmap(sc
->sc
.iot
, sc
->sc
.ioh
, sc
->sc
.sc_size
);
276 ehci_pci_detach(device_t self
, int flags
)
278 struct ehci_pci_softc
*sc
= device_private(self
);
281 pmf_device_deregister(self
);
282 rv
= ehci_detach(&sc
->sc
, flags
);
286 /* disable interrupts */
287 EOWRITE2(&sc
->sc
, EHCI_USBINTR
, 0);
288 /* XXX grotty hack to flush the write */
289 (void)EOREAD2(&sc
->sc
, EHCI_USBINTR
);
291 if (sc
->sc_ih
!= NULL
) {
292 pci_intr_disestablish(sc
->sc_pc
, sc
->sc_ih
);
295 if (sc
->sc
.sc_size
) {
296 ehci_release_ownership(&sc
->sc
, sc
->sc_pc
, sc
->sc_tag
);
297 bus_space_unmap(sc
->sc
.iot
, sc
->sc
.ioh
, sc
->sc
.sc_size
);
304 CFATTACH_DECL3_NEW(ehci_pci
, sizeof(struct ehci_pci_softc
),
305 ehci_pci_match
, ehci_pci_attach
, ehci_pci_detach
, ehci_activate
, NULL
,
306 ehci_childdet
, DVF_DETACH_SHUTDOWN
);
310 ehci_dump_caps(ehci_softc_t
*sc
, pci_chipset_tag_t pc
, pcitag_t tag
)
312 uint32_t cparams
, legctlsts
, addr
, cap
, id
;
315 cparams
= EREAD4(sc
, EHCI_HCCPARAMS
);
316 addr
= EHCI_HCC_EECP(cparams
);
318 cap
= pci_conf_read(pc
, tag
, addr
);
319 id
= EHCI_CAP_GET_ID(cap
);
321 case EHCI_CAP_ID_LEGACY
:
322 legctlsts
= pci_conf_read(pc
, tag
,
323 addr
+ PCI_EHCI_USBLEGCTLSTS
);
324 printf("ehci_dump_caps: legsup=0x%08x "
325 "legctlsts=0x%08x\n", cap
, legctlsts
);
328 printf("ehci_dump_caps: cap=0x%08x\n", cap
);
333 addr
= EHCI_CAP_GET_NEXT(cap
);
339 ehci_release_ownership(ehci_softc_t
*sc
, pci_chipset_tag_t pc
, pcitag_t tag
)
341 const char *devname
= device_xname(sc
->sc_dev
);
342 uint32_t cparams
, addr
, cap
;
346 cparams
= EREAD4(sc
, EHCI_HCCPARAMS
);
347 addr
= EHCI_HCC_EECP(cparams
);
349 cap
= pci_conf_read(pc
, tag
, addr
);
350 if (EHCI_CAP_GET_ID(cap
) != EHCI_CAP_ID_LEGACY
)
352 legsup
= pci_conf_read(pc
, tag
, addr
+ PCI_EHCI_USBLEGSUP
);
353 pci_conf_write(pc
, tag
, addr
+ PCI_EHCI_USBLEGSUP
,
354 legsup
& ~EHCI_LEG_HC_OS_OWNED
);
358 aprint_normal("%s: broken extended capabilities "
359 "ignored\n", devname
);
362 addr
= EHCI_CAP_GET_NEXT(cap
);
367 ehci_get_ownership(ehci_softc_t
*sc
, pci_chipset_tag_t pc
, pcitag_t tag
)
369 const char *devname
= device_xname(sc
->sc_dev
);
370 uint32_t cparams
, addr
, cap
;
377 ehci_dump_caps(sc
, pc
, tag
);
379 cparams
= EREAD4(sc
, EHCI_HCCPARAMS
);
380 addr
= EHCI_HCC_EECP(cparams
);
382 cap
= pci_conf_read(pc
, tag
, addr
);
383 if (EHCI_CAP_GET_ID(cap
) != EHCI_CAP_ID_LEGACY
)
385 legsup
= pci_conf_read(pc
, tag
, addr
+ PCI_EHCI_USBLEGSUP
);
386 /* Ask BIOS to give up ownership */
387 pci_conf_write(pc
, tag
, addr
+ PCI_EHCI_USBLEGSUP
,
388 legsup
| EHCI_LEG_HC_OS_OWNED
);
389 if (legsup
& EHCI_LEG_HC_BIOS_OWNED
) {
390 for (ms
= 0; ms
< EHCI_MAX_BIOS_WAIT
; ms
++) {
391 legsup
= pci_conf_read(pc
, tag
,
392 addr
+ PCI_EHCI_USBLEGSUP
);
393 if (!(legsup
& EHCI_LEG_HC_BIOS_OWNED
))
397 if (ms
== EHCI_MAX_BIOS_WAIT
) {
398 aprint_normal("%s: BIOS refuses to give up "
399 "ownership, using force\n", devname
);
400 pci_conf_write(pc
, tag
,
401 addr
+ PCI_EHCI_USBLEGSUP
, 0);
403 aprint_verbose("%s: BIOS has given up "
404 "ownership\n", devname
);
408 pci_conf_write(pc
, tag
, addr
+ PCI_EHCI_USBLEGCTLSTS
,
409 EHCI_LEG_EXT_SMI_BAR
| EHCI_LEG_EXT_SMI_PCICMD
|
410 EHCI_LEG_EXT_SMI_OS_CHANGE
);
414 aprint_normal("%s: broken extended capabilities "
415 "ignored\n", devname
);
418 addr
= EHCI_CAP_GET_NEXT(cap
);
424 ehci_pci_suspend(device_t dv
, pmf_qual_t qual
)
426 struct ehci_pci_softc
*sc
= device_private(dv
);
428 ehci_suspend(dv
, qual
);
429 ehci_release_ownership(&sc
->sc
, sc
->sc_pc
, sc
->sc_tag
);
435 ehci_pci_resume(device_t dv
, pmf_qual_t qual
)
437 struct ehci_pci_softc
*sc
= device_private(dv
);
439 ehci_get_ownership(&sc
->sc
, sc
->sc_pc
, sc
->sc_tag
);
440 return ehci_resume(dv
, qual
);
444 ehci_sb700_match(struct pci_attach_args
*pa
)
446 if (!(PCI_VENDOR(pa
->pa_id
) == PCI_VENDOR_ATI
&&
447 PCI_PRODUCT(pa
->pa_id
) == PCI_PRODUCT_ATI_SB600_SMB
))
450 switch (PCI_REVISION(pa
->pa_class
)) {
460 ehci_apply_amd_quirks(struct ehci_pci_softc
*sc
)
464 aprint_normal_dev(sc
->sc
.sc_dev
,
465 "applying AMD SB600/SB700 USB freeze workaround\n");
466 value
= pci_conf_read(sc
->sc_pc
, sc
->sc_tag
, EHCI_SBx00_WORKAROUND_REG
);
467 pci_conf_write(sc
->sc_pc
, sc
->sc_tag
, EHCI_SBx00_WORKAROUND_REG
,
468 value
| EHCI_SBx00_WORKAROUND_ENABLE
);
473 enum ehci_pci_quirk_flags
474 ehci_pci_lookup_quirkdata(pci_vendor_id_t vendor
, pci_product_id_t product
)
478 for (i
= 0; i
< __arraycount(ehci_pci_quirks
); i
++) {
479 if (vendor
== ehci_pci_quirks
[i
].vendor
&&
480 product
== ehci_pci_quirks
[i
].product
)
481 return ehci_pci_quirks
[i
].quirks
;