1 /* $NetBSD: uhci_pci.c,v 1.48 2009/11/26 15:17:10 njoly Exp $ */
4 * Copyright (c) 1998 The NetBSD Foundation, Inc.
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Lennart Augustsson (lennart@augustsson.net) at
9 * Carlstedt Research & Technology.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
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.
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.
33 #include <sys/cdefs.h>
34 __KERNEL_RCSID(0, "$NetBSD: uhci_pci.c,v 1.48 2009/11/26 15:17:10 njoly Exp $");
38 #include <sys/param.h>
39 #include <sys/systm.h>
40 #include <sys/kernel.h>
41 #include <sys/device.h>
43 #include <sys/queue.h>
47 #include <dev/pci/pcivar.h>
48 #include <dev/pci/usb_pci.h>
50 #include <dev/usb/usb.h>
51 #include <dev/usb/usbdi.h>
52 #include <dev/usb/usbdivar.h>
53 #include <dev/usb/usb_mem.h>
55 #include <dev/usb/uhcireg.h>
56 #include <dev/usb/uhcivar.h>
58 static bool uhci_pci_resume(device_t
, pmf_qual_t
);
60 struct uhci_pci_softc
{
63 struct usb_pci sc_pci
;
65 pci_chipset_tag_t sc_pc
;
67 void *sc_ih
; /* interrupt vectoring */
71 uhci_pci_match(device_t parent
, cfdata_t match
, void *aux
)
73 struct pci_attach_args
*pa
= (struct pci_attach_args
*) aux
;
75 if (PCI_CLASS(pa
->pa_class
) == PCI_CLASS_SERIALBUS
&&
76 PCI_SUBCLASS(pa
->pa_class
) == PCI_SUBCLASS_SERIALBUS_USB
&&
77 PCI_INTERFACE(pa
->pa_class
) == PCI_INTERFACE_UHCI
)
84 uhci_pci_attach(device_t parent
, device_t self
, void *aux
)
86 struct uhci_pci_softc
*sc
= device_private(self
);
87 struct pci_attach_args
*pa
= (struct pci_attach_args
*)aux
;
88 pci_chipset_tag_t pc
= pa
->pa_pc
;
89 pcitag_t tag
= pa
->pa_tag
;
99 sc
->sc
.sc_bus
.hci_private
= sc
;
103 pci_devinfo(pa
->pa_id
, pa
->pa_class
, 0, devinfo
, sizeof(devinfo
));
104 aprint_normal(": %s (rev. 0x%02x)\n",
105 devinfo
, PCI_REVISION(pa
->pa_class
));
107 /* Map I/O registers */
108 if (pci_mapreg_map(pa
, PCI_CBIO
, PCI_MAPREG_TYPE_IO
, 0,
109 &sc
->sc
.iot
, &sc
->sc
.ioh
, NULL
, &sc
->sc
.sc_size
)) {
110 aprint_error_dev(self
, "can't map i/o space\n");
115 * Disable interrupts, so we don't get any spurious ones.
116 * Acknowledge all pending interrupts.
118 bus_space_write_2(sc
->sc
.iot
, sc
->sc
.ioh
, UHCI_INTR
, 0);
119 bus_space_write_2(sc
->sc
.iot
, sc
->sc
.ioh
, UHCI_STS
,
120 bus_space_read_2(sc
->sc
.iot
, sc
->sc
.ioh
, UHCI_STS
));
124 sc
->sc
.sc_bus
.dmatag
= pa
->pa_dmat
;
126 /* Enable the device. */
127 csr
= pci_conf_read(pc
, tag
, PCI_COMMAND_STATUS_REG
);
128 pci_conf_write(pc
, tag
, PCI_COMMAND_STATUS_REG
,
129 csr
| PCI_COMMAND_MASTER_ENABLE
);
131 /* Map and establish the interrupt. */
132 if (pci_intr_map(pa
, &ih
)) {
133 aprint_error_dev(self
, "couldn't map interrupt\n");
136 intrstr
= pci_intr_string(pc
, ih
);
137 sc
->sc_ih
= pci_intr_establish(pc
, ih
, IPL_USB
, uhci_intr
, sc
);
138 if (sc
->sc_ih
== NULL
) {
139 aprint_error_dev(self
, "couldn't establish interrupt");
141 aprint_error(" at %s", intrstr
);
145 aprint_normal_dev(self
, "interrupting at %s\n", intrstr
);
148 * Set LEGSUP register to its default value.
149 * This can re-enable or trigger interrupts, so protect against
150 * them and explicitly disable and ACK them afterwards.
153 pci_conf_write(pc
, tag
, PCI_LEGSUP
, PCI_LEGSUP_USBPIRQDEN
);
154 bus_space_write_2(sc
->sc
.iot
, sc
->sc
.ioh
, UHCI_INTR
, 0);
155 bus_space_write_2(sc
->sc
.iot
, sc
->sc
.ioh
, UHCI_STS
,
156 bus_space_read_2(sc
->sc
.iot
, sc
->sc
.ioh
, UHCI_STS
));
159 switch(pci_conf_read(pc
, tag
, PCI_USBREV
) & PCI_USBREV_MASK
) {
160 case PCI_USBREV_PRE_1_0
:
161 sc
->sc
.sc_bus
.usbrev
= USBREV_PRE_1_0
;
164 sc
->sc
.sc_bus
.usbrev
= USBREV_1_0
;
167 sc
->sc
.sc_bus
.usbrev
= USBREV_1_1
;
170 sc
->sc
.sc_bus
.usbrev
= USBREV_UNKNOWN
;
174 /* Figure out vendor for root hub descriptor. */
175 vendor
= pci_findvendor(pa
->pa_id
);
176 sc
->sc
.sc_id_vendor
= PCI_VENDOR(pa
->pa_id
);
178 strlcpy(sc
->sc
.sc_vendor
, vendor
, sizeof(sc
->sc
.sc_vendor
));
180 snprintf(sc
->sc
.sc_vendor
, sizeof(sc
->sc
.sc_vendor
),
181 "vendor 0x%04x", PCI_VENDOR(pa
->pa_id
));
183 r
= uhci_init(&sc
->sc
);
184 if (r
!= USBD_NORMAL_COMPLETION
) {
185 aprint_error_dev(self
, "init failed, error=%d\n", r
);
190 usb_pci_add(&sc
->sc_pci
, pa
, self
);
193 if (!pmf_device_register(self
, uhci_suspend
, uhci_pci_resume
))
194 aprint_error_dev(self
, "couldn't establish power handler\n");
196 /* Attach usb device. */
197 sc
->sc
.sc_child
= config_found(self
, &sc
->sc
.sc_bus
, usbctlprint
);
201 uhci_pci_detach(device_t self
, int flags
)
203 struct uhci_pci_softc
*sc
= device_private(self
);
206 pmf_device_deregister(self
);
208 rv
= uhci_detach(&sc
->sc
, flags
);
212 /* disable interrupts and acknowledge any pending */
213 bus_space_write_2(sc
->sc
.iot
, sc
->sc
.ioh
, UHCI_INTR
, 0);
214 bus_space_write_2(sc
->sc
.iot
, sc
->sc
.ioh
, UHCI_STS
,
215 bus_space_read_2(sc
->sc
.iot
, sc
->sc
.ioh
, UHCI_STS
));
217 if (sc
->sc_ih
!= NULL
) {
218 pci_intr_disestablish(sc
->sc_pc
, sc
->sc_ih
);
221 if (sc
->sc
.sc_size
) {
222 bus_space_unmap(sc
->sc
.iot
, sc
->sc
.ioh
, sc
->sc
.sc_size
);
226 usb_pci_rem(&sc
->sc_pci
);
232 uhci_pci_resume(device_t dv
, pmf_qual_t qual
)
234 struct uhci_pci_softc
*sc
= device_private(dv
);
236 /* Set LEGSUP register to its default value. */
237 pci_conf_write(sc
->sc_pc
, sc
->sc_tag
, PCI_LEGSUP
,
238 PCI_LEGSUP_USBPIRQDEN
);
240 return uhci_resume(dv
, qual
);
243 CFATTACH_DECL3_NEW(uhci_pci
, sizeof(struct uhci_pci_softc
),
244 uhci_pci_match
, uhci_pci_attach
, uhci_pci_detach
, uhci_activate
,
245 NULL
, uhci_childdet
, DVF_DETACH_SHUTDOWN
);