Commit | Line | Data |
---|---|---|
1550dfd9 MD |
1 | /*- |
2 | * Copyright (c) 1998 The NetBSD Foundation, Inc. | |
3 | * All rights reserved. | |
4 | * | |
5 | * This code is derived from software contributed to The NetBSD Foundation | |
6 | * by Lennart Augustsson (augustss@carlstedt.se) at | |
7 | * Carlstedt Research & Technology. | |
8 | * | |
9 | * Redistribution and use in source and binary forms, with or without | |
10 | * modification, are permitted provided that the following conditions | |
11 | * are met: | |
12 | * 1. Redistributions of source code must retain the above copyright | |
13 | * notice, this list of conditions and the following disclaimer. | |
14 | * 2. Redistributions in binary form must reproduce the above copyright | |
15 | * notice, this list of conditions and the following disclaimer in the | |
16 | * documentation and/or other materials provided with the distribution. | |
17 | * 3. All advertising materials mentioning features or use of this software | |
18 | * must display the following acknowledgement: | |
19 | * This product includes software developed by the NetBSD | |
20 | * Foundation, Inc. and its contributors. | |
21 | * 4. Neither the name of The NetBSD Foundation nor the names of its | |
22 | * contributors may be used to endorse or promote products derived | |
23 | * from this software without specific prior written permission. | |
24 | * | |
25 | * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS | |
26 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED | |
27 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | |
28 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS | |
29 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | |
30 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | |
31 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | |
32 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | |
33 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | |
34 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | |
35 | * POSSIBILITY OF SUCH DAMAGE. | |
36 | * | |
37 | * $FreeBSD: src/sys/dev/usb/ohci_pci.c,v 1.38 2003/12/22 15:18:46 shiba Exp $ | |
1f7ab7c9 | 38 | * $DragonFly: src/sys/bus/usb/ohci_pci.c,v 1.5 2006/10/25 20:55:52 dillon Exp $ |
1550dfd9 MD |
39 | */ |
40 | ||
41 | /* | |
42 | * USB Open Host Controller driver. | |
43 | * | |
44 | * OHCI spec: http://www.intel.com/design/usb/ohci11d.pdf | |
45 | */ | |
46 | ||
47 | /* The low level controller code for OHCI has been split into | |
48 | * PCI probes and OHCI specific code. This was done to facilitate the | |
49 | * sharing of code between *BSD's | |
50 | */ | |
51 | ||
52 | #include "opt_bus.h" | |
53 | ||
54 | #include <sys/param.h> | |
55 | #include <sys/systm.h> | |
56 | #include <sys/kernel.h> | |
57 | #include <sys/module.h> | |
58 | #include <sys/bus.h> | |
59 | #include <sys/queue.h> | |
1550dfd9 | 60 | #include <sys/rman.h> |
1550dfd9 MD |
61 | |
62 | #include <bus/pci/pcivar.h> | |
63 | #include <bus/pci/pcireg.h> | |
64 | ||
65 | #include <bus/usb/usb.h> | |
66 | #include <bus/usb/usbdi.h> | |
67 | #include <bus/usb/usbdivar.h> | |
68 | #include <bus/usb/usb_mem.h> | |
69 | ||
70 | #include <bus/usb/ohcireg.h> | |
71 | #include <bus/usb/ohcivar.h> | |
72 | ||
73 | #define PCI_OHCI_VENDORID_ACERLABS 0x10b9 | |
74 | #define PCI_OHCI_VENDORID_AMD 0x1022 | |
75 | #define PCI_OHCI_VENDORID_APPLE 0x106b | |
76 | #define PCI_OHCI_VENDORID_CMDTECH 0x1095 | |
77 | #define PCI_OHCI_VENDORID_NEC 0x1033 | |
78 | #define PCI_OHCI_VENDORID_NVIDIA 0x12D2 | |
79 | #define PCI_OHCI_VENDORID_NVIDIA2 0x10DE | |
80 | #define PCI_OHCI_VENDORID_OPTI 0x1045 | |
81 | #define PCI_OHCI_VENDORID_SIS 0x1039 | |
82 | ||
83 | #define PCI_OHCI_DEVICEID_ALADDIN_V 0x523710b9 | |
84 | static const char *ohci_device_aladdin_v = "AcerLabs M5237 (Aladdin-V) USB controller"; | |
85 | ||
86 | #define PCI_OHCI_DEVICEID_AMD756 0x740c1022 | |
87 | static const char *ohci_device_amd756 = "AMD-756 USB Controller"; | |
88 | ||
89 | #define PCI_OHCI_DEVICEID_AMD766 0x74141022 | |
90 | static const char *ohci_device_amd766 = "AMD-766 USB Controller"; | |
91 | ||
92 | #define PCI_OHCI_DEVICEID_FIRELINK 0xc8611045 | |
93 | static const char *ohci_device_firelink = "OPTi 82C861 (FireLink) USB controller"; | |
94 | ||
95 | #define PCI_OHCI_DEVICEID_NEC 0x00351033 | |
96 | static const char *ohci_device_nec = "NEC uPD 9210 USB controller"; | |
97 | ||
98 | #define PCI_OHCI_DEVICEID_NFORCE3 0x00d710de | |
99 | static const char *ohci_device_nforce3 = "nVidia nForce3 USB Controller"; | |
100 | ||
101 | #define PCI_OHCI_DEVICEID_USB0670 0x06701095 | |
102 | static const char *ohci_device_usb0670 = "CMD Tech 670 (USB0670) USB controller"; | |
103 | ||
104 | #define PCI_OHCI_DEVICEID_USB0673 0x06731095 | |
105 | static const char *ohci_device_usb0673 = "CMD Tech 673 (USB0673) USB controller"; | |
106 | ||
107 | #define PCI_OHCI_DEVICEID_SIS5571 0x70011039 | |
108 | static const char *ohci_device_sis5571 = "SiS 5571 USB controller"; | |
109 | ||
110 | #define PCI_OHCI_DEVICEID_KEYLARGO 0x0019106b | |
111 | static const char *ohci_device_keylargo = "Apple KeyLargo USB controller"; | |
112 | ||
113 | static const char *ohci_device_generic = "OHCI (generic) USB controller"; | |
114 | ||
115 | #define PCI_OHCI_BASE_REG 0x10 | |
116 | ||
117 | ||
118 | static int ohci_pci_attach(device_t self); | |
119 | static int ohci_pci_detach(device_t self); | |
120 | static int ohci_pci_suspend(device_t self); | |
121 | static int ohci_pci_resume(device_t self); | |
122 | ||
123 | static int | |
124 | ohci_pci_suspend(device_t self) | |
125 | { | |
126 | ohci_softc_t *sc = device_get_softc(self); | |
127 | int err; | |
128 | ||
129 | err = bus_generic_suspend(self); | |
130 | if (err) | |
131 | return err; | |
132 | ohci_power(PWR_SUSPEND, sc); | |
133 | ||
134 | return 0; | |
135 | } | |
136 | ||
137 | static int | |
138 | ohci_pci_resume(device_t self) | |
139 | { | |
140 | ohci_softc_t *sc = device_get_softc(self); | |
141 | u_int32_t reg, int_line; | |
142 | ||
143 | if (pci_get_powerstate(self) != PCI_POWERSTATE_D0) { | |
144 | device_printf(self, "chip is in D%d mode " | |
145 | "-- setting to D0\n", pci_get_powerstate(self)); | |
146 | reg = pci_read_config(self, PCI_CBMEM, 4); | |
147 | int_line = pci_read_config(self, PCIR_INTLINE, 4); | |
148 | pci_set_powerstate(self, PCI_POWERSTATE_D0); | |
149 | pci_write_config(self, PCI_CBMEM, reg, 4); | |
150 | pci_write_config(self, PCIR_INTLINE, int_line, 4); | |
151 | } | |
152 | ||
153 | ohci_power(PWR_RESUME, sc); | |
154 | bus_generic_resume(self); | |
155 | ||
156 | return 0; | |
157 | } | |
158 | ||
159 | static const char * | |
160 | ohci_pci_match(device_t self) | |
161 | { | |
162 | u_int32_t device_id = pci_get_devid(self); | |
163 | ||
164 | switch (device_id) { | |
165 | case PCI_OHCI_DEVICEID_ALADDIN_V: | |
166 | return (ohci_device_aladdin_v); | |
167 | case PCI_OHCI_DEVICEID_AMD756: | |
168 | return (ohci_device_amd756); | |
169 | case PCI_OHCI_DEVICEID_AMD766: | |
170 | return (ohci_device_amd766); | |
171 | case PCI_OHCI_DEVICEID_USB0670: | |
172 | return (ohci_device_usb0670); | |
173 | case PCI_OHCI_DEVICEID_USB0673: | |
174 | return (ohci_device_usb0673); | |
175 | case PCI_OHCI_DEVICEID_FIRELINK: | |
176 | return (ohci_device_firelink); | |
177 | case PCI_OHCI_DEVICEID_NEC: | |
178 | return (ohci_device_nec); | |
179 | case PCI_OHCI_DEVICEID_NFORCE3: | |
180 | return (ohci_device_nforce3); | |
181 | case PCI_OHCI_DEVICEID_SIS5571: | |
182 | return (ohci_device_sis5571); | |
183 | case PCI_OHCI_DEVICEID_KEYLARGO: | |
184 | return (ohci_device_keylargo); | |
185 | default: | |
186 | if (pci_get_class(self) == PCIC_SERIALBUS | |
187 | && pci_get_subclass(self) == PCIS_SERIALBUS_USB | |
188 | && pci_get_progif(self) == PCI_INTERFACE_OHCI) { | |
189 | return (ohci_device_generic); | |
190 | } | |
191 | } | |
192 | ||
193 | return NULL; /* dunno */ | |
194 | } | |
195 | ||
196 | static int | |
197 | ohci_pci_probe(device_t self) | |
198 | { | |
199 | const char *desc = ohci_pci_match(self); | |
200 | ||
201 | if (desc) { | |
202 | device_set_desc(self, desc); | |
203 | return 0; | |
204 | } else { | |
205 | return ENXIO; | |
206 | } | |
207 | } | |
208 | ||
209 | static int | |
210 | ohci_pci_attach(device_t self) | |
211 | { | |
212 | ohci_softc_t *sc = device_get_softc(self); | |
213 | int err; | |
214 | int rid; | |
215 | ||
216 | /* XXX where does it say so in the spec? */ | |
217 | sc->sc_bus.usbrev = USBREV_1_0; | |
218 | ||
219 | pci_enable_busmaster(self); | |
220 | ||
221 | rid = PCI_CBMEM; | |
222 | sc->io_res = bus_alloc_resource(self, SYS_RES_MEMORY, &rid, | |
223 | 0, ~0, 1, RF_ACTIVE); | |
224 | if (!sc->io_res) { | |
225 | device_printf(self, "Could not map memory\n"); | |
226 | return ENXIO; | |
227 | } | |
228 | sc->iot = rman_get_bustag(sc->io_res); | |
229 | sc->ioh = rman_get_bushandle(sc->io_res); | |
230 | ||
231 | rid = 0; | |
232 | sc->irq_res = bus_alloc_resource(self, SYS_RES_IRQ, &rid, 0, ~0, 1, | |
233 | RF_SHAREABLE | RF_ACTIVE); | |
234 | if (sc->irq_res == NULL) { | |
235 | device_printf(self, "Could not allocate irq\n"); | |
236 | ohci_pci_detach(self); | |
237 | return ENXIO; | |
238 | } | |
239 | sc->sc_bus.bdev = device_add_child(self, "usb", -1); | |
240 | if (!sc->sc_bus.bdev) { | |
241 | device_printf(self, "Could not add USB device\n"); | |
242 | ohci_pci_detach(self); | |
243 | return ENOMEM; | |
244 | } | |
245 | device_set_ivars(sc->sc_bus.bdev, sc); | |
246 | ||
247 | /* ohci_pci_match will never return NULL if ohci_pci_probe succeeded */ | |
248 | device_set_desc(sc->sc_bus.bdev, ohci_pci_match(self)); | |
249 | switch (pci_get_vendor(self)) { | |
250 | case PCI_OHCI_VENDORID_ACERLABS: | |
251 | sprintf(sc->sc_vendor, "AcerLabs"); | |
252 | break; | |
253 | case PCI_OHCI_VENDORID_AMD: | |
254 | sprintf(sc->sc_vendor, "AMD"); | |
255 | break; | |
256 | case PCI_OHCI_VENDORID_APPLE: | |
257 | sprintf(sc->sc_vendor, "Apple"); | |
258 | break; | |
259 | case PCI_OHCI_VENDORID_CMDTECH: | |
260 | sprintf(sc->sc_vendor, "CMDTECH"); | |
261 | break; | |
262 | case PCI_OHCI_VENDORID_NEC: | |
263 | sprintf(sc->sc_vendor, "NEC"); | |
264 | break; | |
265 | case PCI_OHCI_VENDORID_NVIDIA: | |
266 | case PCI_OHCI_VENDORID_NVIDIA2: | |
267 | sprintf(sc->sc_vendor, "nVidia"); | |
268 | break; | |
269 | case PCI_OHCI_VENDORID_OPTI: | |
270 | sprintf(sc->sc_vendor, "OPTi"); | |
271 | break; | |
272 | case PCI_OHCI_VENDORID_SIS: | |
273 | sprintf(sc->sc_vendor, "SiS"); | |
274 | break; | |
275 | default: | |
276 | if (bootverbose) | |
277 | device_printf(self, "(New OHCI DeviceId=0x%08x)\n", | |
278 | pci_get_devid(self)); | |
279 | sprintf(sc->sc_vendor, "(0x%04x)", pci_get_vendor(self)); | |
280 | } | |
281 | ||
ee61f228 | 282 | err = bus_setup_intr(self, sc->irq_res, 0, |
e9cb6d99 | 283 | (driver_intr_t *) ohci_intr, sc, &sc->ih, NULL); |
1550dfd9 MD |
284 | if (err) { |
285 | device_printf(self, "Could not setup irq, %d\n", err); | |
286 | sc->ih = NULL; | |
287 | ohci_pci_detach(self); | |
288 | return ENXIO; | |
289 | } | |
290 | err = ohci_init(sc); | |
291 | if (!err) | |
292 | err = device_probe_and_attach(sc->sc_bus.bdev); | |
1550dfd9 MD |
293 | if (err) { |
294 | device_printf(self, "USB init failed\n"); | |
295 | ohci_pci_detach(self); | |
296 | return EIO; | |
297 | } | |
3d583874 | 298 | ohci_init_intrs(sc); |
1550dfd9 MD |
299 | return 0; |
300 | } | |
301 | ||
302 | static int | |
303 | ohci_pci_detach(device_t self) | |
304 | { | |
305 | ohci_softc_t *sc = device_get_softc(self); | |
306 | ||
307 | /* | |
308 | * XXX this code is not yet fit to be used as detach for the OHCI | |
309 | * controller | |
310 | */ | |
311 | ||
312 | /* | |
313 | * disable interrupts that might have been switched on in ohci_init | |
314 | */ | |
315 | if (sc->iot && sc->ioh) | |
316 | bus_space_write_4(sc->iot, sc->ioh, | |
317 | OHCI_INTERRUPT_DISABLE, OHCI_ALL_INTRS); | |
318 | ||
319 | if (sc->irq_res && sc->ih) { | |
320 | int err = bus_teardown_intr(self, sc->irq_res, sc->ih); | |
321 | ||
322 | if (err) | |
323 | /* XXX or should we panic? */ | |
324 | device_printf(self, "Could not tear down irq, %d\n", | |
325 | err); | |
326 | sc->ih = NULL; | |
327 | } | |
328 | if (sc->sc_bus.bdev) { | |
329 | device_delete_child(self, sc->sc_bus.bdev); | |
330 | sc->sc_bus.bdev = NULL; | |
331 | } | |
332 | if (sc->irq_res) { | |
333 | bus_release_resource(self, SYS_RES_IRQ, 0, sc->irq_res); | |
334 | sc->irq_res = NULL; | |
335 | } | |
336 | if (sc->io_res) { | |
337 | bus_release_resource(self, SYS_RES_MEMORY, PCI_CBMEM, sc->io_res); | |
338 | sc->io_res = NULL; | |
339 | sc->iot = 0; | |
340 | sc->ioh = 0; | |
341 | } | |
342 | return 0; | |
343 | } | |
344 | ||
345 | static device_method_t ohci_methods[] = { | |
346 | /* Device interface */ | |
347 | DEVMETHOD(device_probe, ohci_pci_probe), | |
348 | DEVMETHOD(device_attach, ohci_pci_attach), | |
349 | DEVMETHOD(device_suspend, ohci_pci_suspend), | |
350 | DEVMETHOD(device_resume, ohci_pci_resume), | |
351 | DEVMETHOD(device_shutdown, bus_generic_shutdown), | |
352 | ||
353 | /* Bus interface */ | |
354 | DEVMETHOD(bus_print_child, bus_generic_print_child), | |
355 | ||
356 | {0, 0} | |
357 | }; | |
358 | ||
359 | static driver_t ohci_driver = { | |
360 | "ohci", | |
361 | ohci_methods, | |
362 | sizeof(ohci_softc_t), | |
363 | }; | |
364 | ||
365 | static devclass_t ohci_devclass; | |
366 | ||
367 | DRIVER_MODULE(ohci, pci, ohci_driver, ohci_devclass, 0, 0); | |
368 | DRIVER_MODULE(ohci, cardbus, ohci_driver, ohci_devclass, 0, 0); |