aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLibin Yang <Libin.Yang@amd.com>2008-08-08 03:03:31 -0400
committerGreg Kroah-Hartman <gregkh@suse.de>2008-08-13 20:32:58 -0400
commitab1666c1364a209e6141d7c14e47a42b5f00eca2 (patch)
treee71ab24f20b36762d9b6b2fc9565bdd848cbdd97
parente12cc34527dcd945597c860c25aba92883a4a6a4 (diff)
USB: quirk PLL power down mode
On some AMD 700 series southbridges, ISO OUT transfers (such as audio playback through speakers) on the USB OHCI controller may be corrupted when an A-Link express power saving feature is active. PLL power down mode in conjunction with link power management feature L1 being enabled is the bad combination ... this patch prevents them from being enabled when ISO transfers are pending. Signed-off-by: Crane Cai <crane.cai@amd.com> Signed-off-by: Libin Yang <libin.yang@amd.com> Signed-off-by: David Brownell <dbrownell@users.sourceforge.net> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
-rw-r--r--drivers/usb/host/ohci-hcd.c17
-rw-r--r--drivers/usb/host/ohci-pci.c132
-rw-r--r--drivers/usb/host/ohci-q.c6
-rw-r--r--drivers/usb/host/ohci.h9
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);
86static int ohci_restart (struct ohci_hcd *ohci); 86static int ohci_restart (struct ohci_hcd *ohci);
87#endif 87#endif
88 88
89#ifdef CONFIG_PCI
90static void quirk_amd_pll(int state);
91static void amd_iso_dev_put(void);
92#else
93static inline void quirk_amd_pll(int state)
94{
95 return;
96}
97static 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
39static struct pci_dev *amd_smbus_dev;
40static struct pci_dev *amd_hb_dev;
41static int amd_ohci_iso_count;
42
21/*-------------------------------------------------------------------------*/ 43/*-------------------------------------------------------------------------*/
22 44
23static int broken_suspend(struct usb_hcd *hcd) 45static 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
168static 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 */
211static 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
249static 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 */
147static const struct pci_device_id ohci_pci_quirks[] = { 266static 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}
432static inline int quirk_amdiso(struct ohci_hcd *ohci)
433{
434 return ohci->flags & OHCI_QUIRK_AMD_ISO;
435}
431#else 436#else
432static inline int quirk_nec(struct ohci_hcd *ohci) 437static 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}
445static 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 */