1 | /* $NetBSD: xhci_pci.c,v 1.7 2016/10/13 20:05:06 jdolecek Exp $ */ |
2 | /* OpenBSD: xhci_pci.c,v 1.4 2014/07/12 17:38:51 yuo Exp */ |
3 | |
4 | /* |
5 | * Copyright (c) 1998 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) at |
10 | * Carlstedt Research & Technology. |
11 | * |
12 | * Redistribution and use in source and binary forms, with or without |
13 | * modification, are permitted provided that the following conditions |
14 | * are met: |
15 | * 1. Redistributions of source code must retain the above copyright |
16 | * notice, this list of conditions and the following disclaimer. |
17 | * 2. Redistributions in binary form must reproduce the above copyright |
18 | * notice, this list of conditions and the following disclaimer in the |
19 | * documentation and/or other materials provided with the distribution. |
20 | * |
21 | * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS |
22 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
23 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
24 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS |
25 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
26 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
27 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
28 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
29 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
30 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
31 | * POSSIBILITY OF SUCH DAMAGE. |
32 | */ |
33 | |
34 | #include <sys/cdefs.h> |
35 | __KERNEL_RCSID(0, "$NetBSD: xhci_pci.c,v 1.7 2016/10/13 20:05:06 jdolecek Exp $" ); |
36 | |
37 | #include <sys/param.h> |
38 | #include <sys/systm.h> |
39 | #include <sys/kernel.h> |
40 | #include <sys/device.h> |
41 | #include <sys/proc.h> |
42 | #include <sys/queue.h> |
43 | |
44 | #include <sys/bus.h> |
45 | |
46 | #include <dev/pci/pcivar.h> |
47 | #include <dev/pci/pcidevs.h> |
48 | |
49 | #include <dev/usb/usb.h> |
50 | #include <dev/usb/usbdi.h> |
51 | #include <dev/usb/usbdivar.h> |
52 | #include <dev/usb/usb_mem.h> |
53 | |
54 | #include <dev/usb/xhcireg.h> |
55 | #include <dev/usb/xhcivar.h> |
56 | |
57 | struct xhci_pci_softc { |
58 | struct xhci_softc sc_xhci; |
59 | pci_chipset_tag_t sc_pc; |
60 | pcitag_t sc_tag; |
61 | void *sc_ih; |
62 | pci_intr_handle_t *sc_pihp; |
63 | }; |
64 | |
65 | static int |
66 | xhci_pci_match(device_t parent, cfdata_t match, void *aux) |
67 | { |
68 | struct pci_attach_args *pa = (struct pci_attach_args *) aux; |
69 | |
70 | if (PCI_CLASS(pa->pa_class) == PCI_CLASS_SERIALBUS && |
71 | PCI_SUBCLASS(pa->pa_class) == PCI_SUBCLASS_SERIALBUS_USB && |
72 | PCI_INTERFACE(pa->pa_class) == PCI_INTERFACE_XHCI) |
73 | return 1; |
74 | |
75 | return 0; |
76 | } |
77 | |
78 | static int |
79 | xhci_pci_port_route(struct xhci_pci_softc *psc) |
80 | { |
81 | struct xhci_softc * const sc = &psc->sc_xhci; |
82 | |
83 | pcireg_t val; |
84 | |
85 | /* |
86 | * Check USB3 Port Routing Mask register that indicates the ports |
87 | * can be changed from OS, and turn on by USB3 Port SS Enable register. |
88 | */ |
89 | val = pci_conf_read(psc->sc_pc, psc->sc_tag, PCI_XHCI_INTEL_USB3PRM); |
90 | aprint_debug_dev(sc->sc_dev, |
91 | "USB3PRM / USB3.0 configurable ports: 0x%08x\n" , val); |
92 | |
93 | pci_conf_write(psc->sc_pc, psc->sc_tag, PCI_XHCI_INTEL_USB3_PSSEN, val); |
94 | val = pci_conf_read(psc->sc_pc, psc->sc_tag,PCI_XHCI_INTEL_USB3_PSSEN); |
95 | aprint_debug_dev(sc->sc_dev, |
96 | "USB3_PSSEN / Enabled USB3.0 ports under xHCI: 0x%08x\n" , val); |
97 | |
98 | /* |
99 | * Check USB2 Port Routing Mask register that indicates the USB2.0 |
100 | * ports to be controlled by xHCI HC, and switch them to xHCI HC. |
101 | */ |
102 | val = pci_conf_read(psc->sc_pc, psc->sc_tag, PCI_XHCI_INTEL_USB2PRM); |
103 | aprint_debug_dev(sc->sc_dev, |
104 | "XUSB2PRM / USB2.0 ports can switch from EHCI to xHCI:" |
105 | "0x%08x\n" , val); |
106 | pci_conf_write(psc->sc_pc, psc->sc_tag, PCI_XHCI_INTEL_XUSB2PR, val); |
107 | val = pci_conf_read(psc->sc_pc, psc->sc_tag, PCI_XHCI_INTEL_XUSB2PR); |
108 | aprint_debug_dev(sc->sc_dev, |
109 | "XUSB2PR / USB2.0 ports under xHCI: 0x%08x\n" , val); |
110 | |
111 | return 0; |
112 | } |
113 | |
114 | static void |
115 | xhci_pci_attach(device_t parent, device_t self, void *aux) |
116 | { |
117 | struct xhci_pci_softc * const psc = device_private(self); |
118 | struct xhci_softc * const sc = &psc->sc_xhci; |
119 | struct pci_attach_args *const pa = (struct pci_attach_args *)aux; |
120 | const pci_chipset_tag_t pc = pa->pa_pc; |
121 | const pcitag_t tag = pa->pa_tag; |
122 | char const *intrstr; |
123 | pcireg_t csr, memtype; |
124 | int err; |
125 | uint32_t hccparams; |
126 | char intrbuf[PCI_INTRSTR_LEN]; |
127 | |
128 | sc->sc_dev = self; |
129 | sc->sc_bus.ub_hcpriv = sc; |
130 | |
131 | pci_aprint_devinfo(pa, "USB Controller" ); |
132 | |
133 | /* Check for quirks */ |
134 | sc->sc_quirks = 0; |
135 | |
136 | /* check if memory space access is enabled */ |
137 | csr = pci_conf_read(pc, tag, PCI_COMMAND_STATUS_REG); |
138 | #ifdef DEBUG |
139 | printf("%s: csr: %08x\n" , __func__, csr); |
140 | #endif |
141 | if ((csr & PCI_COMMAND_MEM_ENABLE) == 0) { |
142 | aprint_error_dev(self, "memory access is disabled\n" ); |
143 | return; |
144 | } |
145 | |
146 | /* map MMIO registers */ |
147 | memtype = pci_mapreg_type(pa->pa_pc, pa->pa_tag, PCI_CBMEM); |
148 | switch (memtype) { |
149 | case PCI_MAPREG_TYPE_MEM | PCI_MAPREG_MEM_TYPE_32BIT: |
150 | case PCI_MAPREG_TYPE_MEM | PCI_MAPREG_MEM_TYPE_64BIT: |
151 | if (pci_mapreg_map(pa, PCI_CBMEM, memtype, 0, |
152 | &sc->sc_iot, &sc->sc_ioh, NULL, &sc->sc_ios)) { |
153 | sc->sc_ios = 0; |
154 | aprint_error_dev(self, "can't map mem space\n" ); |
155 | return; |
156 | } |
157 | break; |
158 | default: |
159 | aprint_error_dev(self, "BAR not 64 or 32-bit MMIO\n" ); |
160 | return; |
161 | } |
162 | |
163 | psc->sc_pc = pc; |
164 | psc->sc_tag = tag; |
165 | |
166 | hccparams = bus_space_read_4(sc->sc_iot, sc->sc_ioh, XHCI_HCCPARAMS); |
167 | |
168 | if (pci_dma64_available(pa) && (XHCI_HCC_AC64(hccparams) != 0)) |
169 | sc->sc_bus.ub_dmatag = pa->pa_dmat64; |
170 | else |
171 | sc->sc_bus.ub_dmatag = pa->pa_dmat; |
172 | |
173 | /* Enable the device. */ |
174 | pci_conf_write(pc, tag, PCI_COMMAND_STATUS_REG, |
175 | csr | PCI_COMMAND_MASTER_ENABLE); |
176 | |
177 | /* Allocate and establish the interrupt. */ |
178 | if (pci_intr_alloc(pa, &psc->sc_pihp, NULL, 0)) { |
179 | aprint_error_dev(self, "can't allocate handler\n" ); |
180 | goto fail; |
181 | } |
182 | intrstr = pci_intr_string(pc, psc->sc_pihp[0], intrbuf, |
183 | sizeof(intrbuf)); |
184 | psc->sc_ih = pci_intr_establish_xname(pc, psc->sc_pihp[0], IPL_USB, |
185 | xhci_intr, sc, device_xname(sc->sc_dev)); |
186 | if (psc->sc_ih == NULL) { |
187 | aprint_error_dev(self, "couldn't establish interrupt" ); |
188 | if (intrstr != NULL) |
189 | aprint_error(" at %s" , intrstr); |
190 | aprint_error("\n" ); |
191 | goto fail; |
192 | } |
193 | aprint_normal_dev(self, "interrupting at %s\n" , intrstr); |
194 | |
195 | /* Figure out vendor for root hub descriptor. */ |
196 | sc->sc_id_vendor = PCI_VENDOR(pa->pa_id); |
197 | pci_findvendor(sc->sc_vendor, sizeof(sc->sc_vendor), |
198 | sc->sc_id_vendor); |
199 | |
200 | /* Intel chipset requires SuperSpeed enable and USB2 port routing */ |
201 | switch (PCI_VENDOR(pa->pa_id)) { |
202 | case PCI_VENDOR_INTEL: |
203 | sc->sc_quirks |= XHCI_QUIRK_INTEL; |
204 | break; |
205 | default: |
206 | break; |
207 | } |
208 | |
209 | err = xhci_init(sc); |
210 | if (err) { |
211 | aprint_error_dev(self, "init failed, error=%d\n" , err); |
212 | goto fail; |
213 | } |
214 | |
215 | if ((sc->sc_quirks & XHCI_QUIRK_INTEL) != 0) |
216 | xhci_pci_port_route(psc); |
217 | |
218 | if (!pmf_device_register1(self, xhci_suspend, xhci_resume, |
219 | xhci_shutdown)) |
220 | aprint_error_dev(self, "couldn't establish power handler\n" ); |
221 | |
222 | /* Attach usb device. */ |
223 | sc->sc_child = config_found(self, &sc->sc_bus, usbctlprint); |
224 | return; |
225 | |
226 | fail: |
227 | if (psc->sc_ih) { |
228 | pci_intr_release(psc->sc_pc, psc->sc_pihp, 1); |
229 | psc->sc_ih = NULL; |
230 | } |
231 | if (sc->sc_ios) { |
232 | bus_space_unmap(sc->sc_iot, sc->sc_ioh, sc->sc_ios); |
233 | sc->sc_ios = 0; |
234 | } |
235 | return; |
236 | } |
237 | |
238 | static int |
239 | xhci_pci_detach(device_t self, int flags) |
240 | { |
241 | struct xhci_pci_softc * const psc = device_private(self); |
242 | struct xhci_softc * const sc = &psc->sc_xhci; |
243 | int rv; |
244 | |
245 | rv = xhci_detach(sc, flags); |
246 | if (rv) |
247 | return rv; |
248 | |
249 | pmf_device_deregister(self); |
250 | |
251 | xhci_shutdown(self, flags); |
252 | |
253 | if (sc->sc_ios) { |
254 | #if 0 |
255 | /* Disable interrupts, so we don't get any spurious ones. */ |
256 | bus_space_write_4(sc->sc_iot, sc->sc_ioh, |
257 | OHCI_INTERRUPT_DISABLE, OHCI_ALL_INTRS); |
258 | #endif |
259 | } |
260 | |
261 | if (psc->sc_ih != NULL) { |
262 | pci_intr_release(psc->sc_pc, psc->sc_pihp, 1); |
263 | psc->sc_ih = NULL; |
264 | } |
265 | if (sc->sc_ios) { |
266 | bus_space_unmap(sc->sc_iot, sc->sc_ioh, sc->sc_ios); |
267 | sc->sc_ios = 0; |
268 | } |
269 | |
270 | return 0; |
271 | } |
272 | |
273 | CFATTACH_DECL3_NEW(xhci_pci, sizeof(struct xhci_pci_softc), |
274 | xhci_pci_match, xhci_pci_attach, xhci_pci_detach, xhci_activate, NULL, |
275 | xhci_childdet, DVF_DETACH_SHUTDOWN); |
276 | |