diff options
Diffstat (limited to 'drivers/usb/host/ohci-pci.c')
-rw-r--r-- | drivers/usb/host/ohci-pci.c | 264 |
1 files changed, 264 insertions, 0 deletions
diff --git a/drivers/usb/host/ohci-pci.c b/drivers/usb/host/ohci-pci.c new file mode 100644 index 000000000000..b611582e6bcf --- /dev/null +++ b/drivers/usb/host/ohci-pci.c | |||
@@ -0,0 +1,264 @@ | |||
1 | /* | ||
2 | * OHCI HCD (Host Controller Driver) for USB. | ||
3 | * | ||
4 | * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at> | ||
5 | * (C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net> | ||
6 | * | ||
7 | * [ Initialisation is based on Linus' ] | ||
8 | * [ uhci code and gregs ohci fragments ] | ||
9 | * [ (C) Copyright 1999 Linus Torvalds ] | ||
10 | * [ (C) Copyright 1999 Gregory P. Smith] | ||
11 | * | ||
12 | * PCI Bus Glue | ||
13 | * | ||
14 | * This file is licenced under the GPL. | ||
15 | */ | ||
16 | |||
17 | #ifdef CONFIG_PMAC_PBOOK | ||
18 | #include <asm/machdep.h> | ||
19 | #include <asm/pmac_feature.h> | ||
20 | #include <asm/pci-bridge.h> | ||
21 | #include <asm/prom.h> | ||
22 | #ifndef CONFIG_PM | ||
23 | # define CONFIG_PM | ||
24 | #endif | ||
25 | #endif | ||
26 | |||
27 | #ifndef CONFIG_PCI | ||
28 | #error "This file is PCI bus glue. CONFIG_PCI must be defined." | ||
29 | #endif | ||
30 | |||
31 | /*-------------------------------------------------------------------------*/ | ||
32 | |||
33 | static int | ||
34 | ohci_pci_reset (struct usb_hcd *hcd) | ||
35 | { | ||
36 | struct ohci_hcd *ohci = hcd_to_ohci (hcd); | ||
37 | |||
38 | ohci_hcd_init (ohci); | ||
39 | return ohci_init (ohci); | ||
40 | } | ||
41 | |||
42 | static int __devinit | ||
43 | ohci_pci_start (struct usb_hcd *hcd) | ||
44 | { | ||
45 | struct ohci_hcd *ohci = hcd_to_ohci (hcd); | ||
46 | int ret; | ||
47 | |||
48 | if(hcd->self.controller && hcd->self.controller->bus == &pci_bus_type) { | ||
49 | struct pci_dev *pdev = to_pci_dev(hcd->self.controller); | ||
50 | |||
51 | /* AMD 756, for most chips (early revs), corrupts register | ||
52 | * values on read ... so enable the vendor workaround. | ||
53 | */ | ||
54 | if (pdev->vendor == PCI_VENDOR_ID_AMD | ||
55 | && pdev->device == 0x740c) { | ||
56 | ohci->flags = OHCI_QUIRK_AMD756; | ||
57 | ohci_info (ohci, "AMD756 erratum 4 workaround\n"); | ||
58 | // also somewhat erratum 10 (suspend/resume issues) | ||
59 | } | ||
60 | |||
61 | /* FIXME for some of the early AMD 760 southbridges, OHCI | ||
62 | * won't work at all. blacklist them. | ||
63 | */ | ||
64 | |||
65 | /* Apple's OHCI driver has a lot of bizarre workarounds | ||
66 | * for this chip. Evidently control and bulk lists | ||
67 | * can get confused. (B&W G3 models, and ...) | ||
68 | */ | ||
69 | else if (pdev->vendor == PCI_VENDOR_ID_OPTI | ||
70 | && pdev->device == 0xc861) { | ||
71 | ohci_info (ohci, | ||
72 | "WARNING: OPTi workarounds unavailable\n"); | ||
73 | } | ||
74 | |||
75 | /* Check for NSC87560. We have to look at the bridge (fn1) to | ||
76 | * identify the USB (fn2). This quirk might apply to more or | ||
77 | * even all NSC stuff. | ||
78 | */ | ||
79 | else if (pdev->vendor == PCI_VENDOR_ID_NS) { | ||
80 | struct pci_dev *b; | ||
81 | |||
82 | b = pci_find_slot (pdev->bus->number, | ||
83 | PCI_DEVFN (PCI_SLOT (pdev->devfn), 1)); | ||
84 | if (b && b->device == PCI_DEVICE_ID_NS_87560_LIO | ||
85 | && b->vendor == PCI_VENDOR_ID_NS) { | ||
86 | ohci->flags |= OHCI_QUIRK_SUPERIO; | ||
87 | ohci_info (ohci, "Using NSC SuperIO setup\n"); | ||
88 | } | ||
89 | } | ||
90 | } | ||
91 | |||
92 | /* NOTE: there may have already been a first reset, to | ||
93 | * keep bios/smm irqs from making trouble | ||
94 | */ | ||
95 | if ((ret = ohci_run (ohci)) < 0) { | ||
96 | ohci_err (ohci, "can't start\n"); | ||
97 | ohci_stop (hcd); | ||
98 | return ret; | ||
99 | } | ||
100 | return 0; | ||
101 | } | ||
102 | |||
103 | #ifdef CONFIG_PM | ||
104 | |||
105 | static int ohci_pci_suspend (struct usb_hcd *hcd, u32 state) | ||
106 | { | ||
107 | struct ohci_hcd *ohci = hcd_to_ohci (hcd); | ||
108 | |||
109 | /* suspend root hub, hoping it keeps power during suspend */ | ||
110 | if (time_before (jiffies, ohci->next_statechange)) | ||
111 | msleep (100); | ||
112 | |||
113 | #ifdef CONFIG_USB_SUSPEND | ||
114 | (void) usb_suspend_device (hcd->self.root_hub, state); | ||
115 | #else | ||
116 | usb_lock_device (hcd->self.root_hub); | ||
117 | (void) ohci_hub_suspend (hcd); | ||
118 | usb_unlock_device (hcd->self.root_hub); | ||
119 | #endif | ||
120 | |||
121 | /* let things settle down a bit */ | ||
122 | msleep (100); | ||
123 | |||
124 | #ifdef CONFIG_PMAC_PBOOK | ||
125 | if (_machine == _MACH_Pmac) { | ||
126 | struct device_node *of_node; | ||
127 | |||
128 | /* Disable USB PAD & cell clock */ | ||
129 | of_node = pci_device_to_OF_node (to_pci_dev(hcd->self.controller)); | ||
130 | if (of_node) | ||
131 | pmac_call_feature(PMAC_FTR_USB_ENABLE, of_node, 0, 0); | ||
132 | } | ||
133 | #endif /* CONFIG_PMAC_PBOOK */ | ||
134 | return 0; | ||
135 | } | ||
136 | |||
137 | |||
138 | static int ohci_pci_resume (struct usb_hcd *hcd) | ||
139 | { | ||
140 | struct ohci_hcd *ohci = hcd_to_ohci (hcd); | ||
141 | int retval = 0; | ||
142 | |||
143 | #ifdef CONFIG_PMAC_PBOOK | ||
144 | if (_machine == _MACH_Pmac) { | ||
145 | struct device_node *of_node; | ||
146 | |||
147 | /* Re-enable USB PAD & cell clock */ | ||
148 | of_node = pci_device_to_OF_node (to_pci_dev(hcd->self.controller)); | ||
149 | if (of_node) | ||
150 | pmac_call_feature (PMAC_FTR_USB_ENABLE, of_node, 0, 1); | ||
151 | } | ||
152 | #endif /* CONFIG_PMAC_PBOOK */ | ||
153 | |||
154 | /* resume root hub */ | ||
155 | if (time_before (jiffies, ohci->next_statechange)) | ||
156 | msleep (100); | ||
157 | #ifdef CONFIG_USB_SUSPEND | ||
158 | /* get extra cleanup even if remote wakeup isn't in use */ | ||
159 | retval = usb_resume_device (hcd->self.root_hub); | ||
160 | #else | ||
161 | usb_lock_device (hcd->self.root_hub); | ||
162 | retval = ohci_hub_resume (hcd); | ||
163 | usb_unlock_device (hcd->self.root_hub); | ||
164 | #endif | ||
165 | |||
166 | return retval; | ||
167 | } | ||
168 | |||
169 | #endif /* CONFIG_PM */ | ||
170 | |||
171 | |||
172 | /*-------------------------------------------------------------------------*/ | ||
173 | |||
174 | static const struct hc_driver ohci_pci_hc_driver = { | ||
175 | .description = hcd_name, | ||
176 | .product_desc = "OHCI Host Controller", | ||
177 | .hcd_priv_size = sizeof(struct ohci_hcd), | ||
178 | |||
179 | /* | ||
180 | * generic hardware linkage | ||
181 | */ | ||
182 | .irq = ohci_irq, | ||
183 | .flags = HCD_MEMORY | HCD_USB11, | ||
184 | |||
185 | /* | ||
186 | * basic lifecycle operations | ||
187 | */ | ||
188 | .reset = ohci_pci_reset, | ||
189 | .start = ohci_pci_start, | ||
190 | #ifdef CONFIG_PM | ||
191 | .suspend = ohci_pci_suspend, | ||
192 | .resume = ohci_pci_resume, | ||
193 | #endif | ||
194 | .stop = ohci_stop, | ||
195 | |||
196 | /* | ||
197 | * managing i/o requests and associated device resources | ||
198 | */ | ||
199 | .urb_enqueue = ohci_urb_enqueue, | ||
200 | .urb_dequeue = ohci_urb_dequeue, | ||
201 | .endpoint_disable = ohci_endpoint_disable, | ||
202 | |||
203 | /* | ||
204 | * scheduling support | ||
205 | */ | ||
206 | .get_frame_number = ohci_get_frame, | ||
207 | |||
208 | /* | ||
209 | * root hub support | ||
210 | */ | ||
211 | .hub_status_data = ohci_hub_status_data, | ||
212 | .hub_control = ohci_hub_control, | ||
213 | #ifdef CONFIG_USB_SUSPEND | ||
214 | .hub_suspend = ohci_hub_suspend, | ||
215 | .hub_resume = ohci_hub_resume, | ||
216 | #endif | ||
217 | .start_port_reset = ohci_start_port_reset, | ||
218 | }; | ||
219 | |||
220 | /*-------------------------------------------------------------------------*/ | ||
221 | |||
222 | |||
223 | static const struct pci_device_id pci_ids [] = { { | ||
224 | /* handle any USB OHCI controller */ | ||
225 | PCI_DEVICE_CLASS((PCI_CLASS_SERIAL_USB << 8) | 0x10, ~0), | ||
226 | .driver_data = (unsigned long) &ohci_pci_hc_driver, | ||
227 | }, { /* end: all zeroes */ } | ||
228 | }; | ||
229 | MODULE_DEVICE_TABLE (pci, pci_ids); | ||
230 | |||
231 | /* pci driver glue; this is a "new style" PCI driver module */ | ||
232 | static struct pci_driver ohci_pci_driver = { | ||
233 | .name = (char *) hcd_name, | ||
234 | .id_table = pci_ids, | ||
235 | |||
236 | .probe = usb_hcd_pci_probe, | ||
237 | .remove = usb_hcd_pci_remove, | ||
238 | |||
239 | #ifdef CONFIG_PM | ||
240 | .suspend = usb_hcd_pci_suspend, | ||
241 | .resume = usb_hcd_pci_resume, | ||
242 | #endif | ||
243 | }; | ||
244 | |||
245 | |||
246 | static int __init ohci_hcd_pci_init (void) | ||
247 | { | ||
248 | printk (KERN_DEBUG "%s: " DRIVER_INFO " (PCI)\n", hcd_name); | ||
249 | if (usb_disabled()) | ||
250 | return -ENODEV; | ||
251 | |||
252 | pr_debug ("%s: block sizes: ed %Zd td %Zd\n", hcd_name, | ||
253 | sizeof (struct ed), sizeof (struct td)); | ||
254 | return pci_register_driver (&ohci_pci_driver); | ||
255 | } | ||
256 | module_init (ohci_hcd_pci_init); | ||
257 | |||
258 | /*-------------------------------------------------------------------------*/ | ||
259 | |||
260 | static void __exit ohci_hcd_pci_cleanup (void) | ||
261 | { | ||
262 | pci_unregister_driver (&ohci_pci_driver); | ||
263 | } | ||
264 | module_exit (ohci_hcd_pci_cleanup); | ||