diff options
Diffstat (limited to 'drivers/usb/host/ohci-pci.c')
| -rw-r--r-- | drivers/usb/host/ohci-pci.c | 336 |
1 files changed, 59 insertions, 277 deletions
diff --git a/drivers/usb/host/ohci-pci.c b/drivers/usb/host/ohci-pci.c index 36ee9a666e9..bb150967572 100644 --- a/drivers/usb/host/ohci-pci.c +++ b/drivers/usb/host/ohci-pci.c @@ -14,31 +14,20 @@ * This file is licenced under the GPL. */ -#ifndef CONFIG_PCI -#error "This file is PCI bus glue. CONFIG_PCI must be defined." -#endif - -#include <linux/pci.h> #include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/usb.h> +#include <linux/usb/hcd.h> +#include "ohci.h" +#include "pci-quirks.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 DRIVER_DESC "OHCI PCI platform driver" -#define NB_PCIE_INDX_ADDR 0xe0 -#define NB_PCIE_INDX_DATA 0xe4 -#define PCIE_P_CNTL 0x10040 -#define BIF_NB 0x10002 +static const char hcd_name[] = "ohci-pci"; -static struct pci_dev *amd_smbus_dev; -static struct pci_dev *amd_hb_dev; -static int amd_ohci_iso_count; /*-------------------------------------------------------------------------*/ @@ -141,13 +130,6 @@ static void ohci_quirk_nec_worker(struct work_struct *work) struct ohci_hcd *ohci = container_of(work, struct ohci_hcd, nec_work); int status; - status = ohci_init(ohci); - if (status != 0) { - ohci_err(ohci, "Restarting NEC controller failed in %s, %d\n", - "ohci_init", status); - return; - } - status = ohci_restart(ohci); if (status != 0) ohci_err(ohci, "Restarting NEC controller failed in %s, %d\n", @@ -168,134 +150,20 @@ static int ohci_quirk_nec(struct usb_hcd *hcd) 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 (usb_amd_find_chipset_info()) + ohci->flags |= OHCI_QUIRK_AMD_PLL; /* SB800 needs pre-fetch fix */ - if ((rev >= 0x40) && (rev <= 0x4f)) { + if (usb_amd_prefetch_quirk()) { ohci->flags |= OHCI_QUIRK_AMD_PREFETCH; ohci_dbg(ohci, "enabled AMD prefetch quirk\n"); } - 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"); - + ohci->flags |= OHCI_QUIRK_GLOBAL_SUSPEND; return 0; } -/* nVidia controllers continue to drive Reset signalling on the bus - * even after system shutdown, wasting power. This flag tells the - * shutdown routine to leave the controller OPERATIONAL instead of RESET. - */ -static int ohci_quirk_nvidia_shutdown(struct usb_hcd *hcd) -{ - struct ohci_hcd *ohci = hcd_to_ohci(hcd); - - ohci->flags |= OHCI_QUIRK_SHUTDOWN; - ohci_dbg(ohci, "enabled nVidia shutdown 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; - } - } - -} - -static void sb800_prefetch(struct ohci_hcd *ohci, int on) -{ - struct pci_dev *pdev; - u16 misc; - - pdev = to_pci_dev(ohci_to_hcd(ohci)->self.controller); - pci_read_config_word(pdev, 0x50, &misc); - if (on == 0) - pci_write_config_word(pdev, 0x50, misc & 0xfcff); - else - pci_write_config_word(pdev, 0x50, misc | 0x0300); -} - /* List of quirks for OHCI */ static const struct pci_device_id ohci_pci_quirks[] = { { @@ -346,10 +214,6 @@ static const struct pci_device_id ohci_pci_quirks[] = { PCI_DEVICE(PCI_VENDOR_ID_ATI, 0x4399), .driver_data = (unsigned long)ohci_quirk_amd700, }, - { - PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID), - .driver_data = (unsigned long) ohci_quirk_nvidia_shutdown, - }, /* FIXME for some of the early AMD 760 southbridges, OHCI * won't work at all. blacklist them. @@ -361,10 +225,10 @@ static const struct pci_device_id ohci_pci_quirks[] = { static int ohci_pci_reset (struct usb_hcd *hcd) { struct ohci_hcd *ohci = hcd_to_ohci (hcd); + struct pci_dev *pdev = to_pci_dev(hcd->self.controller); int ret = 0; if (hcd->self.controller) { - struct pci_dev *pdev = to_pci_dev(hcd->self.controller); const struct pci_device_id *quirk_id; quirk_id = pci_match_id(ohci_pci_quirks, pdev); @@ -374,144 +238,33 @@ static int ohci_pci_reset (struct usb_hcd *hcd) ret = quirk(hcd); } } - if (ret == 0) { - ohci_hcd_init (ohci); - return ohci_init (ohci); - } - return ret; -} - - -static int __devinit ohci_pci_start (struct usb_hcd *hcd) -{ - struct ohci_hcd *ohci = hcd_to_ohci (hcd); - int ret; -#ifdef CONFIG_PM /* avoid warnings about unused pdev */ - if (hcd->self.controller) { - struct pci_dev *pdev = to_pci_dev(hcd->self.controller); - - /* RWC may not be set for add-in PCI cards, since boot - * firmware probably ignored them. This transfers PCI - * PM wakeup capabilities. - */ - if (device_can_wakeup(&pdev->dev)) - ohci->hc_control |= OHCI_CTRL_RWC; - } -#endif /* CONFIG_PM */ - - ret = ohci_run (ohci); - if (ret < 0) { - ohci_err (ohci, "can't start\n"); - ohci_stop (hcd); - } + if (ret == 0) + ret = ohci_setup(hcd); + /* + * After ohci setup RWC may not be set for add-in PCI cards. + * This transfers PCI PM wakeup capabilities. + */ + if (device_can_wakeup(&pdev->dev)) + ohci->hc_control |= OHCI_CTRL_RWC; return ret; } -#ifdef CONFIG_PM +static struct hc_driver __read_mostly ohci_pci_hc_driver; -static int ohci_pci_suspend(struct usb_hcd *hcd, bool do_wakeup) -{ - struct ohci_hcd *ohci = hcd_to_ohci (hcd); - unsigned long flags; - int rc = 0; - - /* Root hub was already suspended. Disable irq emission and - * mark HW unaccessible, bail out if RH has been resumed. Use - * the spinlock to properly synchronize with possible pending - * RH suspend or resume activity. - * - * This is still racy as hcd->state is manipulated outside of - * any locks =P But that will be a different fix. - */ - spin_lock_irqsave (&ohci->lock, flags); - if (hcd->state != HC_STATE_SUSPENDED) { - rc = -EINVAL; - goto bail; - } - ohci_writel(ohci, OHCI_INTR_MIE, &ohci->regs->intrdisable); - (void)ohci_readl(ohci, &ohci->regs->intrdisable); - - clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); - bail: - spin_unlock_irqrestore (&ohci->lock, flags); - - return rc; -} - - -static int ohci_pci_resume(struct usb_hcd *hcd, bool hibernated) -{ - set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); - - /* Make sure resume from hibernation re-enumerates everything */ - if (hibernated) - ohci_usb_reset(hcd_to_ohci(hcd)); - - ohci_finish_controller_resume(hcd); - return 0; -} - -#endif /* CONFIG_PM */ - - -/*-------------------------------------------------------------------------*/ - -static const struct hc_driver ohci_pci_hc_driver = { - .description = hcd_name, - .product_desc = "OHCI Host Controller", - .hcd_priv_size = sizeof(struct ohci_hcd), - - /* - * generic hardware linkage - */ - .irq = ohci_irq, - .flags = HCD_MEMORY | HCD_USB11, - - /* - * basic lifecycle operations - */ +static const struct ohci_driver_overrides pci_overrides __initconst = { + .product_desc = "OHCI PCI host controller", .reset = ohci_pci_reset, - .start = ohci_pci_start, - .stop = ohci_stop, - .shutdown = ohci_shutdown, - -#ifdef CONFIG_PM - .pci_suspend = ohci_pci_suspend, - .pci_resume = ohci_pci_resume, -#endif - - /* - * managing i/o requests and associated device resources - */ - .urb_enqueue = ohci_urb_enqueue, - .urb_dequeue = ohci_urb_dequeue, - .endpoint_disable = ohci_endpoint_disable, - - /* - * scheduling support - */ - .get_frame_number = ohci_get_frame, - - /* - * root hub support - */ - .hub_status_data = ohci_hub_status_data, - .hub_control = ohci_hub_control, -#ifdef CONFIG_PM - .bus_suspend = ohci_bus_suspend, - .bus_resume = ohci_bus_resume, -#endif - .start_port_reset = ohci_start_port_reset, }; -/*-------------------------------------------------------------------------*/ - - static const struct pci_device_id pci_ids [] = { { /* handle any USB OHCI controller */ PCI_DEVICE_CLASS(PCI_CLASS_SERIAL_USB_OHCI, ~0), .driver_data = (unsigned long) &ohci_pci_hc_driver, + }, { + /* The device in the ConneXT I/O hub has no class reg */ + PCI_VDEVICE(STMICRO, PCI_DEVICE_ID_STMICRO_USB_OHCI), + .driver_data = (unsigned long) &ohci_pci_hc_driver, }, { /* end: all zeroes */ } }; MODULE_DEVICE_TABLE (pci, pci_ids); @@ -525,9 +278,38 @@ static struct pci_driver ohci_pci_driver = { .remove = usb_hcd_pci_remove, .shutdown = usb_hcd_pci_shutdown, -#ifdef CONFIG_PM_SLEEP +#ifdef CONFIG_PM .driver = { .pm = &usb_hcd_pci_pm_ops }, #endif }; + +static int __init ohci_pci_init(void) +{ + if (usb_disabled()) + return -ENODEV; + + pr_info("%s: " DRIVER_DESC "\n", hcd_name); + + ohci_init_driver(&ohci_pci_hc_driver, &pci_overrides); + +#ifdef CONFIG_PM + /* Entries for the PCI suspend/resume callbacks are special */ + ohci_pci_hc_driver.pci_suspend = ohci_suspend; + ohci_pci_hc_driver.pci_resume = ohci_resume; +#endif + + return pci_register_driver(&ohci_pci_driver); +} +module_init(ohci_pci_init); + +static void __exit ohci_pci_cleanup(void) +{ + pci_unregister_driver(&ohci_pci_driver); +} +module_exit(ohci_pci_cleanup); + +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); +MODULE_SOFTDEP("pre: ehci_pci"); |
