aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb
diff options
context:
space:
mode:
authorAlan Stern <stern@rowland.harvard.edu>2005-10-03 16:36:29 -0400
committerGreg Kroah-Hartman <gregkh@suse.de>2005-10-28 19:47:44 -0400
commitbb200f6eac6372839921be1fe87c1f5c292a7bd6 (patch)
treed55cd6ac5d8b52b96ad766cbd8f31ca85e0e4f8b /drivers/usb
parenta922c68732725866c88457026cf06a7620846506 (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')
-rw-r--r--drivers/usb/host/pci-quirks.c142
-rw-r--r--drivers/usb/host/uhci-hcd.c76
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
87static 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 */
98void 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}
122EXPORT_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 */
130int 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 /* 168reset_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); 173EXPORT_SYMBOL_GPL(uhci_check_and_reset_hc);
135 if (val & 0xbf) 174
136 pci_write_config_word(pdev, UHCI_USBLEGSUP, UHCI_USBLEGSUP_DEFAULT); 175static 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
140static void __devinit quirk_usb_handoff_ohci(struct pci_dev *pdev) 190static 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
104extern void uhci_reset_hc(struct pci_dev *pdev, unsigned long base);
105extern 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 */
108static void reset_hc(struct uhci_hcd *uhci) 110static 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 */
154static void hc_died(struct uhci_hcd *uhci) 133static 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 */
164static void check_and_reset_hc(struct uhci_hcd *uhci) 144static 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
201reset_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