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 | |
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')
-rw-r--r-- | drivers/usb/host/pci-quirks.c | 142 | ||||
-rw-r--r-- | drivers/usb/host/uhci-hcd.c | 76 |
2 files changed, 106 insertions, 112 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) |
diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c index 34c9dbc6a156..6df555e3d97c 100644 --- a/drivers/usb/host/uhci-hcd.c +++ b/drivers/usb/host/uhci-hcd.c | |||
@@ -101,37 +101,16 @@ static void uhci_get_current_frame_number(struct uhci_hcd *uhci); | |||
101 | #include "uhci-q.c" | 101 | #include "uhci-q.c" |
102 | #include "uhci-hub.c" | 102 | #include "uhci-hub.c" |
103 | 103 | ||
104 | extern void uhci_reset_hc(struct pci_dev *pdev, unsigned long base); | ||
105 | extern int uhci_check_and_reset_hc(struct pci_dev *pdev, unsigned long base); | ||
106 | |||
104 | /* | 107 | /* |
105 | * Make sure the controller is completely inactive, unable to | 108 | * Finish up a host controller reset and update the recorded state. |
106 | * generate interrupts or do DMA. | ||
107 | */ | 109 | */ |
108 | static void reset_hc(struct uhci_hcd *uhci) | 110 | static void finish_reset(struct uhci_hcd *uhci) |
109 | { | 111 | { |
110 | int port; | 112 | int port; |
111 | 113 | ||
112 | /* Turn off PIRQ enable and SMI enable. (This also turns off the | ||
113 | * BIOS's USB Legacy Support.) Turn off all the R/WC bits too. | ||
114 | */ | ||
115 | pci_write_config_word(to_pci_dev(uhci_dev(uhci)), USBLEGSUP, | ||
116 | USBLEGSUP_RWC); | ||
117 | |||
118 | /* Reset the HC - this will force us to get a | ||
119 | * new notification of any already connected | ||
120 | * ports due to the virtual disconnect that it | ||
121 | * implies. | ||
122 | */ | ||
123 | outw(USBCMD_HCRESET, uhci->io_addr + USBCMD); | ||
124 | mb(); | ||
125 | udelay(5); | ||
126 | if (inw(uhci->io_addr + USBCMD) & USBCMD_HCRESET) | ||
127 | dev_warn(uhci_dev(uhci), "HCRESET not completed yet!\n"); | ||
128 | |||
129 | /* Just to be safe, disable interrupt requests and | ||
130 | * make sure the controller is stopped. | ||
131 | */ | ||
132 | outw(0, uhci->io_addr + USBINTR); | ||
133 | outw(0, uhci->io_addr + USBCMD); | ||
134 | |||
135 | /* HCRESET doesn't affect the Suspend, Reset, and Resume Detect | 114 | /* HCRESET doesn't affect the Suspend, Reset, and Resume Detect |
136 | * bits in the port status and control registers. | 115 | * bits in the port status and control registers. |
137 | * We have to clear them by hand. | 116 | * We have to clear them by hand. |
@@ -153,7 +132,8 @@ static void reset_hc(struct uhci_hcd *uhci) | |||
153 | */ | 132 | */ |
154 | static void hc_died(struct uhci_hcd *uhci) | 133 | static void hc_died(struct uhci_hcd *uhci) |
155 | { | 134 | { |
156 | reset_hc(uhci); | 135 | uhci_reset_hc(to_pci_dev(uhci_dev(uhci)), uhci->io_addr); |
136 | finish_reset(uhci); | ||
157 | uhci->hc_inaccessible = 1; | 137 | uhci->hc_inaccessible = 1; |
158 | } | 138 | } |
159 | 139 | ||
@@ -163,44 +143,8 @@ static void hc_died(struct uhci_hcd *uhci) | |||
163 | */ | 143 | */ |
164 | static void check_and_reset_hc(struct uhci_hcd *uhci) | 144 | static void check_and_reset_hc(struct uhci_hcd *uhci) |
165 | { | 145 | { |
166 | u16 legsup; | 146 | if (uhci_check_and_reset_hc(to_pci_dev(uhci_dev(uhci)), uhci->io_addr)) |
167 | unsigned int cmd, intr; | 147 | finish_reset(uhci); |
168 | |||
169 | /* | ||
170 | * When restarting a suspended controller, we expect all the | ||
171 | * settings to be the same as we left them: | ||
172 | * | ||
173 | * PIRQ and SMI disabled, no R/W bits set in USBLEGSUP; | ||
174 | * Controller is stopped and configured with EGSM set; | ||
175 | * No interrupts enabled except possibly Resume Detect. | ||
176 | * | ||
177 | * If any of these conditions are violated we do a complete reset. | ||
178 | */ | ||
179 | pci_read_config_word(to_pci_dev(uhci_dev(uhci)), USBLEGSUP, &legsup); | ||
180 | if (legsup & ~(USBLEGSUP_RO | USBLEGSUP_RWC)) { | ||
181 | dev_dbg(uhci_dev(uhci), "%s: legsup = 0x%04x\n", | ||
182 | __FUNCTION__, legsup); | ||
183 | goto reset_needed; | ||
184 | } | ||
185 | |||
186 | cmd = inw(uhci->io_addr + USBCMD); | ||
187 | if ((cmd & USBCMD_RS) || !(cmd & USBCMD_CF) || !(cmd & USBCMD_EGSM)) { | ||
188 | dev_dbg(uhci_dev(uhci), "%s: cmd = 0x%04x\n", | ||
189 | __FUNCTION__, cmd); | ||
190 | goto reset_needed; | ||
191 | } | ||
192 | |||
193 | intr = inw(uhci->io_addr + USBINTR); | ||
194 | if (intr & (~USBINTR_RESUME)) { | ||
195 | dev_dbg(uhci_dev(uhci), "%s: intr = 0x%04x\n", | ||
196 | __FUNCTION__, intr); | ||
197 | goto reset_needed; | ||
198 | } | ||
199 | return; | ||
200 | |||
201 | reset_needed: | ||
202 | dev_dbg(uhci_dev(uhci), "Performing full reset\n"); | ||
203 | reset_hc(uhci); | ||
204 | } | 148 | } |
205 | 149 | ||
206 | /* | 150 | /* |
@@ -714,7 +658,7 @@ static void uhci_stop(struct usb_hcd *hcd) | |||
714 | 658 | ||
715 | spin_lock_irq(&uhci->lock); | 659 | spin_lock_irq(&uhci->lock); |
716 | if (!uhci->hc_inaccessible) | 660 | if (!uhci->hc_inaccessible) |
717 | reset_hc(uhci); | 661 | hc_died(uhci); |
718 | uhci_scan_schedule(uhci, NULL); | 662 | uhci_scan_schedule(uhci, NULL); |
719 | spin_unlock_irq(&uhci->lock); | 663 | spin_unlock_irq(&uhci->lock); |
720 | 664 | ||