148cf85617db9171c1743cdefb1badb8d6ae3a05
[reactos.git] / reactos / drivers / usb / cromwell / core / hcd-pci.c
1 /*
2 * (C) Copyright David Brownell 2000-2002
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License as published by the
6 * Free Software Foundation; either version 2 of the License, or (at your
7 * option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12 * for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software Foundation,
16 * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17 */
18
19 #if 0
20 #include <linux/config.h>
21
22 #ifdef CONFIG_USB_DEBUG
23 #define DEBUG
24 #else
25 #undef DEBUG
26 #endif
27
28 #include <linux/kernel.h>
29 #include <linux/module.h>
30 #include <linux/pci.h>
31 #include <asm/io.h>
32 #include <asm/irq.h>
33 #include <linux/usb.h>
34 #include "hcd.h"
35 #else
36 #define DEBUG
37 #include "../usb_wrapper.h"
38 #include "hcd.h"
39 #endif
40
41
42 /* PCI-based HCs are normal, but custom bus glue should be ok */
43
44
45 /*-------------------------------------------------------------------------*/
46
47 /* configure so an HC device and id are always provided */
48 /* always called with process context; sleeping is OK */
49
50 /**
51 * usb_hcd_pci_probe - initialize PCI-based HCDs
52 * @dev: USB Host Controller being probed
53 * @id: pci hotplug id connecting controller to HCD framework
54 * Context: !in_interrupt()
55 *
56 * Allocates basic PCI resources for this USB host controller, and
57 * then invokes the start() method for the HCD associated with it
58 * through the hotplug entry's driver_data.
59 *
60 * Store this function in the HCD's struct pci_driver as probe().
61 */
62 int STDCALL usb_hcd_pci_probe (struct pci_dev *dev, const struct pci_device_id *id)
63 {
64 struct hc_driver *driver;
65 PHYSICAL_ADDRESS resource;
66 unsigned long len;
67 void *base;
68 struct usb_hcd *hcd;
69 int retval, region;
70 char buf [8];
71 //char *bufp = buf;
72
73 printk("usbcore: usb_hcd_pci_probe() called\n");
74
75 if (usb_disabled())
76 return -ENODEV;
77
78 if (!id || !(driver = (struct hc_driver *) id->driver_data))
79 return -EINVAL;
80
81 if (pci_enable_device (dev) < 0)
82 return -ENODEV;
83
84 if (!dev->irq) {
85 err ("Found HC with no IRQ. Check BIOS/PCI %s setup!",
86 dev->slot_name);
87 return -ENODEV;
88 }
89
90 if (driver->flags & HCD_MEMORY) { // EHCI, OHCI
91 region = 0;
92 resource = pci_resource_start (dev, 0);
93 len = pci_resource_len (dev, 0);
94 if (!request_mem_region (resource, len, driver->description)) {
95 dbg ("controller already in use");
96 return -EBUSY;
97 }
98 base = ioremap_nocache (resource, len);
99 if (base == NULL) {
100 dbg ("error mapping memory");
101 retval = -EFAULT;
102 clean_1:
103 release_mem_region (resource, len);
104 err ("init %s fail, %d", dev->slot_name, retval);
105 return retval;
106 }
107
108 } else { // UHCI
109 //resource = 0;
110 len = 0;
111 for (region = 0; region < PCI_ROM_RESOURCE; region++) {
112 if (!(pci_resource_flags (dev, region) & IORESOURCE_IO))
113 continue;
114
115 resource = pci_resource_start (dev, region);
116 len = pci_resource_len (dev, region);
117 if (request_region (resource, len,
118 driver->description))
119 break;
120 }
121 if (region == PCI_ROM_RESOURCE) {
122 dbg ("no i/o regions available");
123 return -EBUSY;
124 }
125 base = (void *) (ULONG_PTR)resource.u.LowPart;
126 }
127
128 // driver->start(), later on, will transfer device from
129 // control by SMM/BIOS to control by Linux (if needed)
130
131 pci_set_master (dev);
132
133 hcd = driver->hcd_alloc ();
134 if (hcd == NULL){
135 dbg ("hcd alloc fail");
136 retval = -ENOMEM;
137 clean_2:
138 if (driver->flags & HCD_MEMORY) {
139 iounmap (base);
140 goto clean_1;
141 } else {
142 release_region (resource, len);
143 err ("init %s fail, %d", dev->slot_name, retval);
144 return retval;
145 }
146 }
147 pci_set_drvdata (dev, hcd);
148 hcd->driver = driver;
149 hcd->description = driver->description;
150 hcd->pdev = dev;
151 hcd->self.bus_name = dev->slot_name;
152 hcd->product_desc = dev->dev.name;
153 hcd->self.controller = &dev->dev;
154 hcd->controller = hcd->self.controller;
155
156 if ((retval = hcd_buffer_create (hcd)) != 0) {
157 clean_3:
158 driver->hcd_free (hcd);
159 goto clean_2;
160 }
161
162 dev_info (hcd->controller, "%s\n", hcd->product_desc);
163
164 #ifndef __sparc__
165 sprintf (buf, "%d", dev->irq);
166 #else
167 bufp = __irq_itoa(dev->irq);
168 #endif
169 if (request_irq (dev->irq, usb_hcd_irq, SA_SHIRQ, hcd->description, hcd)
170 != 0) {
171 dev_err (hcd->controller,
172 "request interrupt %s failed\n", buf);
173 retval = -EBUSY;
174 goto clean_3;
175 }
176 hcd->irq = dev->irq;
177
178 hcd->regs = base;
179 hcd->region = region;
180 dev_info (hcd->controller, "irq %s, %s %p\n", buf,
181 (driver->flags & HCD_MEMORY) ? "pci mem" : "io base",
182 base);
183
184 usb_bus_init (&hcd->self);
185 hcd->self.op = &usb_hcd_operations;
186 hcd->self.hcpriv = (void *) hcd;
187
188 INIT_LIST_HEAD (&hcd->dev_list);
189
190 usb_register_bus (&hcd->self);
191
192 if ((retval = driver->start (hcd)) < 0)
193 usb_hcd_pci_remove (dev);
194
195 return retval;
196 }
197 EXPORT_SYMBOL (usb_hcd_pci_probe);
198
199
200 /* may be called without controller electrically present */
201 /* may be called with controller, bus, and devices active */
202
203 /**
204 * usb_hcd_pci_remove - shutdown processing for PCI-based HCDs
205 * @dev: USB Host Controller being removed
206 * Context: !in_interrupt()
207 *
208 * Reverses the effect of usb_hcd_pci_probe(), first invoking
209 * the HCD's stop() method. It is always called from a thread
210 * context, normally "rmmod", "apmd", or something similar.
211 *
212 * Store this function in the HCD's struct pci_driver as remove().
213 */
214 void STDCALL usb_hcd_pci_remove (struct pci_dev *dev)
215 {
216 struct usb_hcd *hcd;
217 struct usb_device *hub;
218
219 hcd = pci_get_drvdata(dev);
220 if (!hcd)
221 return;
222 dev_info (hcd->controller, "remove, state %x\n", hcd->state);
223
224 if (in_interrupt ())
225 BUG ();
226
227 hub = hcd->self.root_hub;
228 hcd->state = USB_STATE_QUIESCING;
229
230 dev_dbg (hcd->controller, "roothub graceful disconnect\n");
231 usb_disconnect (&hub);
232
233 hcd->driver->stop (hcd);
234 hcd_buffer_destroy (hcd);
235 hcd->state = USB_STATE_HALT;
236 pci_set_drvdata (dev, 0);
237
238 free_irq (hcd->irq, hcd);
239 if (hcd->driver->flags & HCD_MEMORY) {
240 iounmap (hcd->regs);
241 release_mem_region (pci_resource_start (dev, 0),
242 pci_resource_len (dev, 0));
243 } else {
244 release_region (pci_resource_start (dev, hcd->region),
245 pci_resource_len (dev, hcd->region));
246 }
247
248 usb_deregister_bus (&hcd->self);
249 if (atomic_read (&hcd->self.refcnt) != 1) {
250 dev_warn (hcd->controller,
251 "dangling refs (%d) to bus %d!\n",
252 atomic_read (&hcd->self.refcnt) - 1,
253 hcd->self.busnum);
254 }
255 hcd->driver->hcd_free (hcd);
256 }
257 EXPORT_SYMBOL (usb_hcd_pci_remove);
258
259
260 #ifdef CONFIG_PM
261
262 /*
263 * Some "sleep" power levels imply updating struct usb_driver
264 * to include a callback asking hcds to do their bit by checking
265 * if all the drivers can suspend. Gets involved with remote wakeup.
266 *
267 * If there are pending urbs, then HCs will need to access memory,
268 * causing extra power drain. New sleep()/wakeup() PM calls might
269 * be needed, beyond PCI suspend()/resume(). The root hub timer
270 * still be accessing memory though ...
271 *
272 * FIXME: USB should have some power budgeting support working with
273 * all kinds of hubs.
274 *
275 * FIXME: This assumes only D0->D3 suspend and D3->D0 resume.
276 * D1 and D2 states should do something, yes?
277 *
278 * FIXME: Should provide generic enable_wake(), calling pci_enable_wake()
279 * for all supported states, so that USB remote wakeup can work for any
280 * devices that support it (and are connected via powered hubs).
281 *
282 * FIXME: resume doesn't seem to work right any more...
283 */
284
285
286 // 2.4 kernels have issued concurrent resumes (w/APM)
287 // we defend against that error; PCI doesn't yet.
288
289 /**
290 * usb_hcd_pci_suspend - power management suspend of a PCI-based HCD
291 * @dev: USB Host Controller being suspended
292 *
293 * Store this function in the HCD's struct pci_driver as suspend().
294 */
295 int usb_hcd_pci_suspend (struct pci_dev *dev, u32 state)
296 {
297 struct usb_hcd *hcd;
298 int retval;
299
300 hcd = pci_get_drvdata(dev);
301 dev_info (hcd->controller, "suspend to state %d\n", state);
302
303 pci_save_state (dev, hcd->pci_state);
304
305 // FIXME for all connected devices, leaf-to-root:
306 // driver->suspend()
307 // proposed "new 2.5 driver model" will automate that
308
309 /* driver may want to disable DMA etc */
310 retval = hcd->driver->suspend (hcd, state);
311 hcd->state = USB_STATE_SUSPENDED;
312
313 pci_set_power_state (dev, state);
314 return retval;
315 }
316 EXPORT_SYMBOL (usb_hcd_pci_suspend);
317
318 /**
319 * usb_hcd_pci_resume - power management resume of a PCI-based HCD
320 * @dev: USB Host Controller being resumed
321 *
322 * Store this function in the HCD's struct pci_driver as resume().
323 */
324 int usb_hcd_pci_resume (struct pci_dev *dev)
325 {
326 struct usb_hcd *hcd;
327 int retval;
328
329 hcd = pci_get_drvdata(dev);
330 dev_info (hcd->controller, "resume\n");
331
332 /* guard against multiple resumes (APM bug?) */
333 atomic_inc (&hcd->resume_count);
334 if (atomic_read (&hcd->resume_count) != 1) {
335 dev_err (hcd->controller, "concurrent PCI resumes\n");
336 retval = 0;
337 goto done;
338 }
339
340 retval = -EBUSY;
341 if (hcd->state != USB_STATE_SUSPENDED) {
342 dev_dbg (hcd->controller, "can't resume, not suspended!\n");
343 goto done;
344 }
345 hcd->state = USB_STATE_RESUMING;
346
347 pci_set_power_state (dev, 0);
348 pci_restore_state (dev, hcd->pci_state);
349
350 retval = hcd->driver->resume (hcd);
351 if (!HCD_IS_RUNNING (hcd->state)) {
352 dev_dbg (hcd->controller, "resume fail, retval %d\n", retval);
353 usb_hc_died (hcd);
354 // FIXME: recover, reset etc.
355 } else {
356 // FIXME for all connected devices, root-to-leaf:
357 // driver->resume ();
358 // proposed "new 2.5 driver model" will automate that
359 }
360
361 done:
362 atomic_dec (&hcd->resume_count);
363 return retval;
364 }
365 EXPORT_SYMBOL (usb_hcd_pci_resume);
366
367 #endif /* CONFIG_PM */
368
369