Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 108457
b: refs/heads/master
c: ab1666c
h: refs/heads/master
i:
  108455: f320333
v: v3
  • Loading branch information
Libin Yang authored and Greg Kroah-Hartman committed Aug 14, 2008
1 parent 2797b03 commit 1e10ee6
Show file tree
Hide file tree
Showing 5 changed files with 165 additions and 1 deletion.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: e12cc34527dcd945597c860c25aba92883a4a6a4
refs/heads/master: ab1666c1364a209e6141d7c14e47a42b5f00eca2
17 changes: 17 additions & 0 deletions trunk/drivers/usb/host/ohci-hcd.c
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,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"
Expand Down Expand Up @@ -886,6 +901,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);
Expand Down
132 changes: 132 additions & 0 deletions trunk/drivers/usb/host/ohci-pci.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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[] = {
{
Expand Down Expand Up @@ -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.
*/
Expand Down
6 changes: 6 additions & 0 deletions trunk/drivers/usb/host/ohci-q.c
Original file line number Diff line number Diff line change
Expand Up @@ -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--;
Expand Down Expand Up @@ -677,6 +680,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;
Expand Down
9 changes: 9 additions & 0 deletions trunk/drivers/usb/host/ohci.h
Original file line number Diff line number Diff line change
Expand Up @@ -401,6 +401,7 @@ struct ohci_hcd {
#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 */
Expand Down Expand Up @@ -428,6 +429,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)
{
Expand All @@ -437,6 +442,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 */
Expand Down

0 comments on commit 1e10ee6

Please sign in to comment.