|
18 | 18 | #error "This file is PCI bus glue. CONFIG_PCI must be defined."
|
19 | 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 | 45 | static int broken_suspend(struct usb_hcd *hcd)
|
@@ -143,6 +165,103 @@ static int ohci_quirk_nec(struct usb_hcd *hcd)
|
143 | 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 | 265 | /* List of quirks for OHCI */
|
147 | 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 | 300 | PCI_DEVICE(PCI_VENDOR_ID_ITE, 0x8152),
|
182 | 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 | 316 | /* FIXME for some of the early AMD 760 southbridges, OHCI
|
185 | 317 | * won't work at all. blacklist them.
|
186 | 318 | */
|
|
0 commit comments