diff options
Diffstat (limited to 'drivers/usb/host/ohci-pci.c')
-rw-r--r-- | drivers/usb/host/ohci-pci.c | 133 |
1 files changed, 132 insertions, 1 deletions
diff --git a/drivers/usb/host/ohci-pci.c b/drivers/usb/host/ohci-pci.c index 4696cc912e16..a9c2ae36c7ad 100644 --- a/drivers/usb/host/ohci-pci.c +++ b/drivers/usb/host/ohci-pci.c | |||
@@ -18,6 +18,28 @@ | |||
18 | #error "This file is PCI bus glue. CONFIG_PCI must be defined." | 18 | #error "This file is PCI bus glue. CONFIG_PCI must be defined." |
19 | #endif | 19 | #endif |
20 | 20 | ||
21 | #include <linux/pci.h> | ||
22 | #include <linux/io.h> | ||
23 | |||
24 | |||
25 | /* constants used to work around PM-related transfer | ||
26 | * glitches in some AMD 700 series southbridges | ||
27 | */ | ||
28 | #define AB_REG_BAR 0xf0 | ||
29 | #define AB_INDX(addr) ((addr) + 0x00) | ||
30 | #define AB_DATA(addr) ((addr) + 0x04) | ||
31 | #define AX_INDXC 0X30 | ||
32 | #define AX_DATAC 0x34 | ||
33 | |||
34 | #define NB_PCIE_INDX_ADDR 0xe0 | ||
35 | #define NB_PCIE_INDX_DATA 0xe4 | ||
36 | #define PCIE_P_CNTL 0x10040 | ||
37 | #define BIF_NB 0x10002 | ||
38 | |||
39 | static struct pci_dev *amd_smbus_dev; | ||
40 | static struct pci_dev *amd_hb_dev; | ||
41 | static int amd_ohci_iso_count; | ||
42 | |||
21 | /*-------------------------------------------------------------------------*/ | 43 | /*-------------------------------------------------------------------------*/ |
22 | 44 | ||
23 | static int broken_suspend(struct usb_hcd *hcd) | 45 | static int broken_suspend(struct usb_hcd *hcd) |
@@ -143,6 +165,103 @@ static int ohci_quirk_nec(struct usb_hcd *hcd) | |||
143 | return 0; | 165 | return 0; |
144 | } | 166 | } |
145 | 167 | ||
168 | static int ohci_quirk_amd700(struct usb_hcd *hcd) | ||
169 | { | ||
170 | struct ohci_hcd *ohci = hcd_to_ohci(hcd); | ||
171 | u8 rev = 0; | ||
172 | |||
173 | if (!amd_smbus_dev) | ||
174 | amd_smbus_dev = pci_get_device(PCI_VENDOR_ID_ATI, | ||
175 | PCI_DEVICE_ID_ATI_SBX00_SMBUS, NULL); | ||
176 | if (!amd_smbus_dev) | ||
177 | return 0; | ||
178 | |||
179 | pci_read_config_byte(amd_smbus_dev, PCI_REVISION_ID, &rev); | ||
180 | if ((rev > 0x3b) || (rev < 0x30)) { | ||
181 | pci_dev_put(amd_smbus_dev); | ||
182 | amd_smbus_dev = NULL; | ||
183 | return 0; | ||
184 | } | ||
185 | |||
186 | amd_ohci_iso_count++; | ||
187 | |||
188 | if (!amd_hb_dev) | ||
189 | amd_hb_dev = pci_get_device(PCI_VENDOR_ID_AMD, 0x9600, NULL); | ||
190 | |||
191 | ohci->flags |= OHCI_QUIRK_AMD_ISO; | ||
192 | ohci_dbg(ohci, "enabled AMD ISO transfers quirk\n"); | ||
193 | |||
194 | return 0; | ||
195 | } | ||
196 | |||
197 | /* | ||
198 | * The hardware normally enables the A-link power management feature, which | ||
199 | * lets the system lower the power consumption in idle states. | ||
200 | * | ||
201 | * Assume the system is configured to have USB 1.1 ISO transfers going | ||
202 | * to or from a USB device. Without this quirk, that stream may stutter | ||
203 | * or have breaks occasionally. For transfers going to speakers, this | ||
204 | * makes a very audible mess... | ||
205 | * | ||
206 | * That audio playback corruption is due to the audio stream getting | ||
207 | * interrupted occasionally when the link goes in lower power state | ||
208 | * This USB quirk prevents the link going into that lower power state | ||
209 | * during audio playback or other ISO operations. | ||
210 | */ | ||
211 | static void quirk_amd_pll(int on) | ||
212 | { | ||
213 | u32 addr; | ||
214 | u32 val; | ||
215 | u32 bit = (on > 0) ? 1 : 0; | ||
216 | |||
217 | pci_read_config_dword(amd_smbus_dev, AB_REG_BAR, &addr); | ||
218 | |||
219 | /* BIT names/meanings are NDA-protected, sorry ... */ | ||
220 | |||
221 | outl(AX_INDXC, AB_INDX(addr)); | ||
222 | outl(0x40, AB_DATA(addr)); | ||
223 | outl(AX_DATAC, AB_INDX(addr)); | ||
224 | val = inl(AB_DATA(addr)); | ||
225 | val &= ~((1 << 3) | (1 << 4) | (1 << 9)); | ||
226 | val |= (bit << 3) | ((!bit) << 4) | ((!bit) << 9); | ||
227 | outl(val, AB_DATA(addr)); | ||
228 | |||
229 | if (amd_hb_dev) { | ||
230 | addr = PCIE_P_CNTL; | ||
231 | pci_write_config_dword(amd_hb_dev, NB_PCIE_INDX_ADDR, addr); | ||
232 | |||
233 | pci_read_config_dword(amd_hb_dev, NB_PCIE_INDX_DATA, &val); | ||
234 | val &= ~(1 | (1 << 3) | (1 << 4) | (1 << 9) | (1 << 12)); | ||
235 | val |= bit | (bit << 3) | (bit << 12); | ||
236 | val |= ((!bit) << 4) | ((!bit) << 9); | ||
237 | pci_write_config_dword(amd_hb_dev, NB_PCIE_INDX_DATA, val); | ||
238 | |||
239 | addr = BIF_NB; | ||
240 | pci_write_config_dword(amd_hb_dev, NB_PCIE_INDX_ADDR, addr); | ||
241 | |||
242 | pci_read_config_dword(amd_hb_dev, NB_PCIE_INDX_DATA, &val); | ||
243 | val &= ~(1 << 8); | ||
244 | val |= bit << 8; | ||
245 | pci_write_config_dword(amd_hb_dev, NB_PCIE_INDX_DATA, val); | ||
246 | } | ||
247 | } | ||
248 | |||
249 | static void amd_iso_dev_put(void) | ||
250 | { | ||
251 | amd_ohci_iso_count--; | ||
252 | if (amd_ohci_iso_count == 0) { | ||
253 | if (amd_smbus_dev) { | ||
254 | pci_dev_put(amd_smbus_dev); | ||
255 | amd_smbus_dev = NULL; | ||
256 | } | ||
257 | if (amd_hb_dev) { | ||
258 | pci_dev_put(amd_hb_dev); | ||
259 | amd_hb_dev = NULL; | ||
260 | } | ||
261 | } | ||
262 | |||
263 | } | ||
264 | |||
146 | /* List of quirks for OHCI */ | 265 | /* List of quirks for OHCI */ |
147 | static const struct pci_device_id ohci_pci_quirks[] = { | 266 | static const struct pci_device_id ohci_pci_quirks[] = { |
148 | { | 267 | { |
@@ -181,6 +300,19 @@ static const struct pci_device_id ohci_pci_quirks[] = { | |||
181 | PCI_DEVICE(PCI_VENDOR_ID_ITE, 0x8152), | 300 | PCI_DEVICE(PCI_VENDOR_ID_ITE, 0x8152), |
182 | .driver_data = (unsigned long) broken_suspend, | 301 | .driver_data = (unsigned long) broken_suspend, |
183 | }, | 302 | }, |
303 | { | ||
304 | PCI_DEVICE(PCI_VENDOR_ID_ATI, 0x4397), | ||
305 | .driver_data = (unsigned long)ohci_quirk_amd700, | ||
306 | }, | ||
307 | { | ||
308 | PCI_DEVICE(PCI_VENDOR_ID_ATI, 0x4398), | ||
309 | .driver_data = (unsigned long)ohci_quirk_amd700, | ||
310 | }, | ||
311 | { | ||
312 | PCI_DEVICE(PCI_VENDOR_ID_ATI, 0x4399), | ||
313 | .driver_data = (unsigned long)ohci_quirk_amd700, | ||
314 | }, | ||
315 | |||
184 | /* FIXME for some of the early AMD 760 southbridges, OHCI | 316 | /* FIXME for some of the early AMD 760 southbridges, OHCI |
185 | * won't work at all. blacklist them. | 317 | * won't work at all. blacklist them. |
186 | */ | 318 | */ |
@@ -327,7 +459,6 @@ static const struct hc_driver ohci_pci_hc_driver = { | |||
327 | */ | 459 | */ |
328 | .hub_status_data = ohci_hub_status_data, | 460 | .hub_status_data = ohci_hub_status_data, |
329 | .hub_control = ohci_hub_control, | 461 | .hub_control = ohci_hub_control, |
330 | .hub_irq_enable = ohci_rhsc_enable, | ||
331 | #ifdef CONFIG_PM | 462 | #ifdef CONFIG_PM |
332 | .bus_suspend = ohci_bus_suspend, | 463 | .bus_suspend = ohci_bus_suspend, |
333 | .bus_resume = ohci_bus_resume, | 464 | .bus_resume = ohci_bus_resume, |