diff options
| -rw-r--r-- | drivers/usb/host/ohci-hcd.c | 17 | ||||
| -rw-r--r-- | drivers/usb/host/ohci-pci.c | 132 | ||||
| -rw-r--r-- | drivers/usb/host/ohci-q.c | 6 | ||||
| -rw-r--r-- | drivers/usb/host/ohci.h | 9 |
4 files changed, 164 insertions, 0 deletions
diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c index 96fe76e9a6c4..89901962cbfd 100644 --- a/drivers/usb/host/ohci-hcd.c +++ b/drivers/usb/host/ohci-hcd.c | |||
| @@ -86,6 +86,21 @@ static void ohci_stop (struct usb_hcd *hcd); | |||
| 86 | static int ohci_restart (struct ohci_hcd *ohci); | 86 | static int ohci_restart (struct ohci_hcd *ohci); |
| 87 | #endif | 87 | #endif |
| 88 | 88 | ||
| 89 | #ifdef CONFIG_PCI | ||
| 90 | static void quirk_amd_pll(int state); | ||
| 91 | static void amd_iso_dev_put(void); | ||
| 92 | #else | ||
| 93 | static inline void quirk_amd_pll(int state) | ||
| 94 | { | ||
| 95 | return; | ||
| 96 | } | ||
| 97 | static inline void amd_iso_dev_put(void) | ||
| 98 | { | ||
| 99 | return; | ||
| 100 | } | ||
| 101 | #endif | ||
| 102 | |||
| 103 | |||
| 89 | #include "ohci-hub.c" | 104 | #include "ohci-hub.c" |
| 90 | #include "ohci-dbg.c" | 105 | #include "ohci-dbg.c" |
| 91 | #include "ohci-mem.c" | 106 | #include "ohci-mem.c" |
| @@ -886,6 +901,8 @@ static void ohci_stop (struct usb_hcd *hcd) | |||
| 886 | 901 | ||
| 887 | if (quirk_zfmicro(ohci)) | 902 | if (quirk_zfmicro(ohci)) |
| 888 | del_timer(&ohci->unlink_watchdog); | 903 | del_timer(&ohci->unlink_watchdog); |
| 904 | if (quirk_amdiso(ohci)) | ||
| 905 | amd_iso_dev_put(); | ||
| 889 | 906 | ||
| 890 | remove_debug_files (ohci); | 907 | remove_debug_files (ohci); |
| 891 | ohci_mem_cleanup (ohci); | 908 | ohci_mem_cleanup (ohci); |
diff --git a/drivers/usb/host/ohci-pci.c b/drivers/usb/host/ohci-pci.c index 4696cc912e16..083e8df0a817 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 | */ |
diff --git a/drivers/usb/host/ohci-q.c b/drivers/usb/host/ohci-q.c index 6a9b4c557953..c2d80f80448b 100644 --- a/drivers/usb/host/ohci-q.c +++ b/drivers/usb/host/ohci-q.c | |||
| @@ -49,6 +49,9 @@ __acquires(ohci->lock) | |||
| 49 | switch (usb_pipetype (urb->pipe)) { | 49 | switch (usb_pipetype (urb->pipe)) { |
| 50 | case PIPE_ISOCHRONOUS: | 50 | case PIPE_ISOCHRONOUS: |
| 51 | ohci_to_hcd(ohci)->self.bandwidth_isoc_reqs--; | 51 | ohci_to_hcd(ohci)->self.bandwidth_isoc_reqs--; |
| 52 | if (ohci_to_hcd(ohci)->self.bandwidth_isoc_reqs == 0 | ||
| 53 | && quirk_amdiso(ohci)) | ||
| 54 | quirk_amd_pll(1); | ||
| 52 | break; | 55 | break; |
| 53 | case PIPE_INTERRUPT: | 56 | case PIPE_INTERRUPT: |
| 54 | ohci_to_hcd(ohci)->self.bandwidth_int_reqs--; | 57 | ohci_to_hcd(ohci)->self.bandwidth_int_reqs--; |
| @@ -677,6 +680,9 @@ static void td_submit_urb ( | |||
| 677 | data + urb->iso_frame_desc [cnt].offset, | 680 | data + urb->iso_frame_desc [cnt].offset, |
| 678 | urb->iso_frame_desc [cnt].length, urb, cnt); | 681 | urb->iso_frame_desc [cnt].length, urb, cnt); |
| 679 | } | 682 | } |
| 683 | if (ohci_to_hcd(ohci)->self.bandwidth_isoc_reqs == 0 | ||
| 684 | && quirk_amdiso(ohci)) | ||
| 685 | quirk_amd_pll(0); | ||
| 680 | periodic = ohci_to_hcd(ohci)->self.bandwidth_isoc_reqs++ == 0 | 686 | periodic = ohci_to_hcd(ohci)->self.bandwidth_isoc_reqs++ == 0 |
| 681 | && ohci_to_hcd(ohci)->self.bandwidth_int_reqs == 0; | 687 | && ohci_to_hcd(ohci)->self.bandwidth_int_reqs == 0; |
| 682 | break; | 688 | break; |
diff --git a/drivers/usb/host/ohci.h b/drivers/usb/host/ohci.h index df5a8037dbaf..faf622eafce7 100644 --- a/drivers/usb/host/ohci.h +++ b/drivers/usb/host/ohci.h | |||
| @@ -401,6 +401,7 @@ struct ohci_hcd { | |||
| 401 | #define OHCI_QUIRK_NEC 0x40 /* lost interrupts */ | 401 | #define OHCI_QUIRK_NEC 0x40 /* lost interrupts */ |
| 402 | #define OHCI_QUIRK_FRAME_NO 0x80 /* no big endian frame_no shift */ | 402 | #define OHCI_QUIRK_FRAME_NO 0x80 /* no big endian frame_no shift */ |
| 403 | #define OHCI_QUIRK_HUB_POWER 0x100 /* distrust firmware power/oc setup */ | 403 | #define OHCI_QUIRK_HUB_POWER 0x100 /* distrust firmware power/oc setup */ |
| 404 | #define OHCI_QUIRK_AMD_ISO 0x200 /* ISO transfers*/ | ||
| 404 | // there are also chip quirks/bugs in init logic | 405 | // there are also chip quirks/bugs in init logic |
| 405 | 406 | ||
| 406 | struct work_struct nec_work; /* Worker for NEC quirk */ | 407 | struct work_struct nec_work; /* Worker for NEC quirk */ |
| @@ -428,6 +429,10 @@ static inline int quirk_zfmicro(struct ohci_hcd *ohci) | |||
| 428 | { | 429 | { |
| 429 | return ohci->flags & OHCI_QUIRK_ZFMICRO; | 430 | return ohci->flags & OHCI_QUIRK_ZFMICRO; |
| 430 | } | 431 | } |
| 432 | static inline int quirk_amdiso(struct ohci_hcd *ohci) | ||
| 433 | { | ||
| 434 | return ohci->flags & OHCI_QUIRK_AMD_ISO; | ||
| 435 | } | ||
| 431 | #else | 436 | #else |
| 432 | static inline int quirk_nec(struct ohci_hcd *ohci) | 437 | static inline int quirk_nec(struct ohci_hcd *ohci) |
| 433 | { | 438 | { |
| @@ -437,6 +442,10 @@ static inline int quirk_zfmicro(struct ohci_hcd *ohci) | |||
| 437 | { | 442 | { |
| 438 | return 0; | 443 | return 0; |
| 439 | } | 444 | } |
| 445 | static inline int quirk_amdiso(struct ohci_hcd *ohci) | ||
| 446 | { | ||
| 447 | return 0; | ||
| 448 | } | ||
| 440 | #endif | 449 | #endif |
| 441 | 450 | ||
| 442 | /* convert between an hcd pointer and the corresponding ohci_hcd */ | 451 | /* convert between an hcd pointer and the corresponding ohci_hcd */ |
