diff options
author | Alan Stern <stern@rowland.harvard.edu> | 2005-10-03 16:36:29 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2005-10-28 19:47:44 -0400 |
commit | bb200f6eac6372839921be1fe87c1f5c292a7bd6 (patch) | |
tree | d55cd6ac5d8b52b96ad766cbd8f31ca85e0e4f8b /drivers/usb/host/pci-quirks.c | |
parent | a922c68732725866c88457026cf06a7620846506 (diff) |
[PATCH] UHCI: unify BIOS handoff and driver reset code
This patch (as574) updates the PCI BIOS usb-handoff code for UHCI
controllers, making it work like the reset routines in uhci-hcd. This
allows uhci-hcd to drop its own routines in favor of the new ones
(code-sharing).
Once the patch is merged we can turn the usb-handoff option on
permanently, as far as UHCI is concerned. OHCI and EHCI may still have
some issues.
Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb/host/pci-quirks.c')
-rw-r--r-- | drivers/usb/host/pci-quirks.c | 142 |
1 files changed, 96 insertions, 46 deletions
diff --git a/drivers/usb/host/pci-quirks.c b/drivers/usb/host/pci-quirks.c index 49f7381a6fd3..f7411ca48835 100644 --- a/drivers/usb/host/pci-quirks.c +++ b/drivers/usb/host/pci-quirks.c | |||
@@ -9,6 +9,12 @@ | |||
9 | */ | 9 | */ |
10 | 10 | ||
11 | #include <linux/config.h> | 11 | #include <linux/config.h> |
12 | #ifdef CONFIG_USB_DEBUG | ||
13 | #define DEBUG | ||
14 | #else | ||
15 | #undef DEBUG | ||
16 | #endif | ||
17 | |||
12 | #include <linux/types.h> | 18 | #include <linux/types.h> |
13 | #include <linux/kernel.h> | 19 | #include <linux/kernel.h> |
14 | #include <linux/pci.h> | 20 | #include <linux/pci.h> |
@@ -46,13 +52,14 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_2, qui | |||
46 | 52 | ||
47 | #define UHCI_USBLEGSUP 0xc0 /* legacy support */ | 53 | #define UHCI_USBLEGSUP 0xc0 /* legacy support */ |
48 | #define UHCI_USBCMD 0 /* command register */ | 54 | #define UHCI_USBCMD 0 /* command register */ |
49 | #define UHCI_USBSTS 2 /* status register */ | ||
50 | #define UHCI_USBINTR 4 /* interrupt register */ | 55 | #define UHCI_USBINTR 4 /* interrupt register */ |
51 | #define UHCI_USBLEGSUP_DEFAULT 0x2000 /* only PIRQ enable set */ | 56 | #define UHCI_USBLEGSUP_RWC 0x8f00 /* the R/WC bits */ |
52 | #define UHCI_USBCMD_RUN (1 << 0) /* RUN/STOP bit */ | 57 | #define UHCI_USBLEGSUP_RO 0x5040 /* R/O and reserved bits */ |
53 | #define UHCI_USBCMD_GRESET (1 << 2) /* Global reset */ | 58 | #define UHCI_USBCMD_RUN 0x0001 /* RUN/STOP bit */ |
54 | #define UHCI_USBCMD_CONFIGURE (1 << 6) /* config semaphore */ | 59 | #define UHCI_USBCMD_HCRESET 0x0002 /* Host Controller reset */ |
55 | #define UHCI_USBSTS_HALTED (1 << 5) /* HCHalted bit */ | 60 | #define UHCI_USBCMD_EGSM 0x0008 /* Global Suspend Mode */ |
61 | #define UHCI_USBCMD_CONFIGURE 0x0040 /* Config Flag */ | ||
62 | #define UHCI_USBINTR_RESUME 0x0002 /* Resume interrupt enable */ | ||
56 | 63 | ||
57 | #define OHCI_CONTROL 0x04 | 64 | #define OHCI_CONTROL 0x04 |
58 | #define OHCI_CMDSTATUS 0x08 | 65 | #define OHCI_CMDSTATUS 0x08 |
@@ -84,57 +91,100 @@ static int __init usb_handoff_early(char *str) | |||
84 | } | 91 | } |
85 | __setup("usb-handoff", usb_handoff_early); | 92 | __setup("usb-handoff", usb_handoff_early); |
86 | 93 | ||
87 | static void __devinit quirk_usb_handoff_uhci(struct pci_dev *pdev) | 94 | /* |
95 | * Make sure the controller is completely inactive, unable to | ||
96 | * generate interrupts or do DMA. | ||
97 | */ | ||
98 | void uhci_reset_hc(struct pci_dev *pdev, unsigned long base) | ||
88 | { | 99 | { |
89 | unsigned long base = 0; | 100 | /* Turn off PIRQ enable and SMI enable. (This also turns off the |
90 | int wait_time, delta; | 101 | * BIOS's USB Legacy Support.) Turn off all the R/WC bits too. |
91 | u16 val, sts; | 102 | */ |
92 | int i; | 103 | pci_write_config_word(pdev, UHCI_USBLEGSUP, UHCI_USBLEGSUP_RWC); |
93 | 104 | ||
94 | for (i = 0; i < PCI_ROM_RESOURCE; i++) | 105 | /* Reset the HC - this will force us to get a |
95 | if ((pci_resource_flags(pdev, i) & IORESOURCE_IO)) { | 106 | * new notification of any already connected |
96 | base = pci_resource_start(pdev, i); | 107 | * ports due to the virtual disconnect that it |
97 | break; | 108 | * implies. |
98 | } | 109 | */ |
110 | outw(UHCI_USBCMD_HCRESET, base + UHCI_USBCMD); | ||
111 | mb(); | ||
112 | udelay(5); | ||
113 | if (inw(base + UHCI_USBCMD) & UHCI_USBCMD_HCRESET) | ||
114 | dev_warn(&pdev->dev, "HCRESET not completed yet!\n"); | ||
115 | |||
116 | /* Just to be safe, disable interrupt requests and | ||
117 | * make sure the controller is stopped. | ||
118 | */ | ||
119 | outw(0, base + UHCI_USBINTR); | ||
120 | outw(0, base + UHCI_USBCMD); | ||
121 | } | ||
122 | EXPORT_SYMBOL_GPL(uhci_reset_hc); | ||
99 | 123 | ||
100 | if (!base) | 124 | /* |
101 | return; | 125 | * Initialize a controller that was newly discovered or has just been |
126 | * resumed. In either case we can't be sure of its previous state. | ||
127 | * | ||
128 | * Returns: 1 if the controller was reset, 0 otherwise. | ||
129 | */ | ||
130 | int uhci_check_and_reset_hc(struct pci_dev *pdev, unsigned long base) | ||
131 | { | ||
132 | u16 legsup; | ||
133 | unsigned int cmd, intr; | ||
102 | 134 | ||
103 | /* | 135 | /* |
104 | * stop controller | 136 | * When restarting a suspended controller, we expect all the |
137 | * settings to be the same as we left them: | ||
138 | * | ||
139 | * PIRQ and SMI disabled, no R/W bits set in USBLEGSUP; | ||
140 | * Controller is stopped and configured with EGSM set; | ||
141 | * No interrupts enabled except possibly Resume Detect. | ||
142 | * | ||
143 | * If any of these conditions are violated we do a complete reset. | ||
105 | */ | 144 | */ |
106 | sts = inw(base + UHCI_USBSTS); | 145 | pci_read_config_word(pdev, UHCI_USBLEGSUP, &legsup); |
107 | val = inw(base + UHCI_USBCMD); | 146 | if (legsup & ~(UHCI_USBLEGSUP_RO | UHCI_USBLEGSUP_RWC)) { |
108 | val &= ~(u16)(UHCI_USBCMD_RUN | UHCI_USBCMD_CONFIGURE); | 147 | dev_dbg(&pdev->dev, "%s: legsup = 0x%04x\n", |
109 | outw(val, base + UHCI_USBCMD); | 148 | __FUNCTION__, legsup); |
149 | goto reset_needed; | ||
150 | } | ||
110 | 151 | ||
111 | /* | 152 | cmd = inw(base + UHCI_USBCMD); |
112 | * wait while it stops if it was running | 153 | if ((cmd & UHCI_USBCMD_RUN) || !(cmd & UHCI_USBCMD_CONFIGURE) || |
113 | */ | 154 | !(cmd & UHCI_USBCMD_EGSM)) { |
114 | if ((sts & UHCI_USBSTS_HALTED) == 0) | 155 | dev_dbg(&pdev->dev, "%s: cmd = 0x%04x\n", |
115 | { | 156 | __FUNCTION__, cmd); |
116 | wait_time = 1000; | 157 | goto reset_needed; |
117 | delta = 100; | 158 | } |
118 | 159 | ||
119 | do { | 160 | intr = inw(base + UHCI_USBINTR); |
120 | outw(0x1f, base + UHCI_USBSTS); | 161 | if (intr & (~UHCI_USBINTR_RESUME)) { |
121 | udelay(delta); | 162 | dev_dbg(&pdev->dev, "%s: intr = 0x%04x\n", |
122 | wait_time -= delta; | 163 | __FUNCTION__, intr); |
123 | val = inw(base + UHCI_USBSTS); | 164 | goto reset_needed; |
124 | if (val & UHCI_USBSTS_HALTED) | ||
125 | break; | ||
126 | } while (wait_time > 0); | ||
127 | } | 165 | } |
166 | return 0; | ||
128 | 167 | ||
129 | /* | 168 | reset_needed: |
130 | * disable interrupts & legacy support | 169 | dev_dbg(&pdev->dev, "Performing full reset\n"); |
131 | */ | 170 | uhci_reset_hc(pdev, base); |
132 | outw(0, base + UHCI_USBINTR); | 171 | return 1; |
133 | outw(0x1f, base + UHCI_USBSTS); | 172 | } |
134 | pci_read_config_word(pdev, UHCI_USBLEGSUP, &val); | 173 | EXPORT_SYMBOL_GPL(uhci_check_and_reset_hc); |
135 | if (val & 0xbf) | 174 | |
136 | pci_write_config_word(pdev, UHCI_USBLEGSUP, UHCI_USBLEGSUP_DEFAULT); | 175 | static void __devinit quirk_usb_handoff_uhci(struct pci_dev *pdev) |
176 | { | ||
177 | unsigned long base = 0; | ||
178 | int i; | ||
179 | |||
180 | for (i = 0; i < PCI_ROM_RESOURCE; i++) | ||
181 | if ((pci_resource_flags(pdev, i) & IORESOURCE_IO)) { | ||
182 | base = pci_resource_start(pdev, i); | ||
183 | break; | ||
184 | } | ||
137 | 185 | ||
186 | if (base) | ||
187 | uhci_check_and_reset_hc(pdev, base); | ||
138 | } | 188 | } |
139 | 189 | ||
140 | static void __devinit quirk_usb_handoff_ohci(struct pci_dev *pdev) | 190 | static void __devinit quirk_usb_handoff_ohci(struct pci_dev *pdev) |