SRU Hardy/Intrepid Bug: #257931 - USB: quirk PLL power down mode

classic Classic list List threaded Threaded
3 messages Options
Reply | Threaded
Open this post in threaded view
|

SRU Hardy/Intrepid Bug: #257931 - USB: quirk PLL power down mode

Tim Gardner-2

--
Tim Gardner [hidden email]

From e01a35f75c193cdb7e129151f91db5b381ddda31 Mon Sep 17 00:00:00 2001
From: Libin Yang <[hidden email]>
Date: Fri, 8 Aug 2008 15:03:31 +0800
Subject: [PATCH] USB: quirk PLL power down mode
 Bug: #257931

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 <[hidden email]>
Signed-off-by: Libin Yang <[hidden email]>
Signed-off-by: David Brownell <[hidden email]>
Signed-off-by: Greg Kroah-Hartman <[hidden email]>
Signed-off-by: Tim Gardner <[hidden email]>
---
 drivers/usb/host/ohci-hcd.c |   17 ++++++
 drivers/usb/host/ohci-pci.c |  132 +++++++++++++++++++++++++++++++++++++++++++
 drivers/usb/host/ohci-q.c   |    6 ++
 drivers/usb/host/ohci.h     |   10 +++
 4 files changed, 165 insertions(+), 0 deletions(-)

diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c
index ecfe800..60f7549 100644
--- a/drivers/usb/host/ohci-hcd.c
+++ b/drivers/usb/host/ohci-hcd.c
@@ -85,6 +85,21 @@ static void ohci_stop (struct usb_hcd *hcd);
 static int ohci_restart (struct ohci_hcd *ohci);
 #endif
 
+#ifdef CONFIG_PCI
+static void quirk_amd_pll(int state);
+static void amd_iso_dev_put(void);
+#else
+static inline void quirk_amd_pll(int state)
+{
+ return;
+}
+static inline void amd_iso_dev_put(void)
+{
+ return;
+}
+#endif
+
+
 #include "ohci-hub.c"
 #include "ohci-dbg.c"
 #include "ohci-mem.c"
@@ -885,6 +900,8 @@ static void ohci_stop (struct usb_hcd *hcd)
 
  if (quirk_zfmicro(ohci))
  del_timer(&ohci->unlink_watchdog);
+ if (quirk_amdiso(ohci))
+ amd_iso_dev_put();
 
  remove_debug_files (ohci);
  ohci_mem_cleanup (ohci);
diff --git a/drivers/usb/host/ohci-pci.c b/drivers/usb/host/ohci-pci.c
index d0360f6..54c8130 100644
--- a/drivers/usb/host/ohci-pci.c
+++ b/drivers/usb/host/ohci-pci.c
@@ -18,6 +18,28 @@
 #error "This file is PCI bus glue.  CONFIG_PCI must be defined."
 #endif
 
+#include <linux/pci.h>
+#include <linux/io.h>
+
+
+/* constants used to work around PM-related transfer
+ * glitches in some AMD 700 series southbridges
+ */
+#define AB_REG_BAR 0xf0
+#define AB_INDX(addr) ((addr) + 0x00)
+#define AB_DATA(addr) ((addr) + 0x04)
+#define AX_INDXC 0X30
+#define AX_DATAC 0x34
+
+#define NB_PCIE_INDX_ADDR 0xe0
+#define NB_PCIE_INDX_DATA 0xe4
+#define PCIE_P_CNTL 0x10040
+#define BIF_NB 0x10002
+
+static struct pci_dev *amd_smbus_dev;
+static struct pci_dev *amd_hb_dev;
+static int amd_ohci_iso_count;
+
 /*-------------------------------------------------------------------------*/
 
 static int broken_suspend(struct usb_hcd *hcd)
@@ -143,6 +165,103 @@ static int ohci_quirk_nec(struct usb_hcd *hcd)
  return 0;
 }
 
+static int ohci_quirk_amd700(struct usb_hcd *hcd)
+{
+ struct ohci_hcd *ohci = hcd_to_ohci(hcd);
+ u8 rev = 0;
+
+ if (!amd_smbus_dev)
+ amd_smbus_dev = pci_get_device(PCI_VENDOR_ID_ATI,
+ PCI_DEVICE_ID_ATI_SBX00_SMBUS, NULL);
+ if (!amd_smbus_dev)
+ return 0;
+
+ pci_read_config_byte(amd_smbus_dev, PCI_REVISION_ID, &rev);
+ if ((rev > 0x3b) || (rev < 0x30)) {
+ pci_dev_put(amd_smbus_dev);
+ amd_smbus_dev = NULL;
+ return 0;
+ }
+
+ amd_ohci_iso_count++;
+
+ if (!amd_hb_dev)
+ amd_hb_dev = pci_get_device(PCI_VENDOR_ID_AMD, 0x9600, NULL);
+
+ ohci->flags |= OHCI_QUIRK_AMD_ISO;
+ ohci_dbg(ohci, "enabled AMD ISO transfers quirk\n");
+
+ return 0;
+}
+
+/*
+ * The hardware normally enables the A-link power management feature, which
+ * lets the system lower the power consumption in idle states.
+ *
+ * Assume the system is configured to have USB 1.1 ISO transfers going
+ * to or from a USB device.  Without this quirk, that stream may stutter
+ * or have breaks occasionally.  For transfers going to speakers, this
+ * makes a very audible mess...
+ *
+ * That audio playback corruption is due to the audio stream getting
+ * interrupted occasionally when the link goes in lower power state
+ * This USB quirk prevents the link going into that lower power state
+ * during audio playback or other ISO operations.
+ */
+static void quirk_amd_pll(int on)
+{
+ u32 addr;
+ u32 val;
+ u32 bit = (on > 0) ? 1 : 0;
+
+ pci_read_config_dword(amd_smbus_dev, AB_REG_BAR, &addr);
+
+ /* BIT names/meanings are NDA-protected, sorry ... */
+
+ outl(AX_INDXC, AB_INDX(addr));
+ outl(0x40, AB_DATA(addr));
+ outl(AX_DATAC, AB_INDX(addr));
+ val = inl(AB_DATA(addr));
+ val &= ~((1 << 3) | (1 << 4) | (1 << 9));
+ val |= (bit << 3) | ((!bit) << 4) | ((!bit) << 9);
+ outl(val, AB_DATA(addr));
+
+ if (amd_hb_dev) {
+ addr = PCIE_P_CNTL;
+ pci_write_config_dword(amd_hb_dev, NB_PCIE_INDX_ADDR, addr);
+
+ pci_read_config_dword(amd_hb_dev, NB_PCIE_INDX_DATA, &val);
+ val &= ~(1 | (1 << 3) | (1 << 4) | (1 << 9) | (1 << 12));
+ val |= bit | (bit << 3) | (bit << 12);
+ val |= ((!bit) << 4) | ((!bit) << 9);
+ pci_write_config_dword(amd_hb_dev, NB_PCIE_INDX_DATA, val);
+
+ addr = BIF_NB;
+ pci_write_config_dword(amd_hb_dev, NB_PCIE_INDX_ADDR, addr);
+
+ pci_read_config_dword(amd_hb_dev, NB_PCIE_INDX_DATA, &val);
+ val &= ~(1 << 8);
+ val |= bit << 8;
+ pci_write_config_dword(amd_hb_dev, NB_PCIE_INDX_DATA, val);
+ }
+}
+
+static void amd_iso_dev_put(void)
+{
+ amd_ohci_iso_count--;
+ if (amd_ohci_iso_count == 0) {
+ if (amd_smbus_dev) {
+ pci_dev_put(amd_smbus_dev);
+ amd_smbus_dev = NULL;
+ }
+ if (amd_hb_dev) {
+ pci_dev_put(amd_hb_dev);
+ amd_hb_dev = NULL;
+ }
+ }
+
+}
+
 /* List of quirks for OHCI */
 static const struct pci_device_id ohci_pci_quirks[] = {
  {
@@ -181,6 +300,19 @@ static const struct pci_device_id ohci_pci_quirks[] = {
  PCI_DEVICE(PCI_VENDOR_ID_ITE, 0x8152),
  .driver_data = (unsigned long) broken_suspend,
  },
+ {
+ PCI_DEVICE(PCI_VENDOR_ID_ATI, 0x4397),
+ .driver_data = (unsigned long)ohci_quirk_amd700,
+ },
+ {
+ PCI_DEVICE(PCI_VENDOR_ID_ATI, 0x4398),
+ .driver_data = (unsigned long)ohci_quirk_amd700,
+ },
+ {
+ PCI_DEVICE(PCI_VENDOR_ID_ATI, 0x4399),
+ .driver_data = (unsigned long)ohci_quirk_amd700,
+ },
+
  /* FIXME for some of the early AMD 760 southbridges, OHCI
  * won't work at all.  blacklist them.
  */
diff --git a/drivers/usb/host/ohci-q.c b/drivers/usb/host/ohci-q.c
index 5181732..2ea1a7f 100644
--- a/drivers/usb/host/ohci-q.c
+++ b/drivers/usb/host/ohci-q.c
@@ -49,6 +49,9 @@ __acquires(ohci->lock)
  switch (usb_pipetype (urb->pipe)) {
  case PIPE_ISOCHRONOUS:
  ohci_to_hcd(ohci)->self.bandwidth_isoc_reqs--;
+ if (ohci_to_hcd(ohci)->self.bandwidth_isoc_reqs == 0
+ && quirk_amdiso(ohci))
+ quirk_amd_pll(1);
  break;
  case PIPE_INTERRUPT:
  ohci_to_hcd(ohci)->self.bandwidth_int_reqs--;
@@ -680,6 +683,9 @@ static void td_submit_urb (
  data + urb->iso_frame_desc [cnt].offset,
  urb->iso_frame_desc [cnt].length, urb, cnt);
  }
+ if (ohci_to_hcd(ohci)->self.bandwidth_isoc_reqs == 0
+ && quirk_amdiso(ohci))
+ quirk_amd_pll(0);
  periodic = ohci_to_hcd(ohci)->self.bandwidth_isoc_reqs++ == 0
  && ohci_to_hcd(ohci)->self.bandwidth_int_reqs == 0;
  break;
diff --git a/drivers/usb/host/ohci.h b/drivers/usb/host/ohci.h
index 47c5c66..7d1040c 100644
--- a/drivers/usb/host/ohci.h
+++ b/drivers/usb/host/ohci.h
@@ -399,6 +399,8 @@ struct ohci_hcd {
 #define OHCI_QUIRK_ZFMICRO 0x20 /* Compaq ZFMicro chipset*/
 #define OHCI_QUIRK_NEC 0x40 /* lost interrupts */
 #define OHCI_QUIRK_FRAME_NO 0x80 /* no big endian frame_no shift */
+#define OHCI_QUIRK_HUB_POWER 0x100 /* distrust firmware power/oc setup */
+#define OHCI_QUIRK_AMD_ISO 0x200 /* ISO transfers*/
  // there are also chip quirks/bugs in init logic
 
  struct work_struct nec_work; /* Worker for NEC quirk */
@@ -419,6 +421,10 @@ static inline int quirk_zfmicro(struct ohci_hcd *ohci)
 {
  return ohci->flags & OHCI_QUIRK_ZFMICRO;
 }
+static inline int quirk_amdiso(struct ohci_hcd *ohci)
+{
+ return ohci->flags & OHCI_QUIRK_AMD_ISO;
+}
 #else
 static inline int quirk_nec(struct ohci_hcd *ohci)
 {
@@ -428,6 +434,10 @@ static inline int quirk_zfmicro(struct ohci_hcd *ohci)
 {
  return 0;
 }
+static inline int quirk_amdiso(struct ohci_hcd *ohci)
+{
+ return 0;
+}
 #endif
 
 /* convert between an hcd pointer and the corresponding ohci_hcd */
--
1.5.4.3


--
kernel-team mailing list
[hidden email]
https://lists.ubuntu.com/mailman/listinfo/kernel-team
Reply | Threaded
Open this post in threaded view
|

Re: SRU Hardy/Intrepid Bug: #257931 - USB: quirk PLL power down mode

Ben Collins-4
On Thu, 2008-08-14 at 10:39 -0600, Tim Gardner wrote:

ACK


--
kernel-team mailing list
[hidden email]
https://lists.ubuntu.com/mailman/listinfo/kernel-team
Reply | Threaded
Open this post in threaded view
|

Re: SRU Hardy/Intrepid Bug: #257931 - USB: quirk PLL power down mode

Stefan Bader-2
In reply to this post by Tim Gardner-2
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1


ACK

When all other means of communication fail, try words!


-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.6 (GNU/Linux)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org

iD8DBQFIpMNqP+TjRTJVqvQRAqr7AJ9av+/1obFypuP5+He5nl0hZHXNAgCcDnXW
BUCD10XGDHQLtj4RYyuiElI=
=FWTx
-----END PGP SIGNATURE-----

--
kernel-team mailing list
[hidden email]
https://lists.ubuntu.com/mailman/listinfo/kernel-team