aboutsummaryrefslogtreecommitdiffstats
path: root/usb/usb-ohci-avoids-root-hub-timer-polling.patch
blob: 67de2c21e9abde108b9ba11db5ba1c6ec74ef8e6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
From linux-usb-devel-admin@lists.sourceforge.net Tue May 16 05:23:39 2006
From: David Brownell <david-b@pacbell.net>
Cc: Andrew Morton <akpm@osdl.org>
Message-Id: <200605121849.09003.david-b@pacbell.net>
Subject: USB: OHCI avoids root hub timer polling
Date: Fri, 2 Jun 2006 09:19:37 -0700

This teaches OHCI to use the root hub status change (RHSC) IRQ, bypassing
the root hub timer except to trigger autosuspend.  Avoiding that timer means
that mechanisms like "dynamic tick" or "variable scheduler timeouts" can
now can be used to leave the CPU in low modes for longer intervals.

If there are any OHCI implementations where INTR_RHSC doesn't work, they
will need to turn off the "uses_new_polling" flag.

Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
Cc: Rafael Wysocki <rjw@sisk.pl>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>

---
 drivers/usb/host/ohci-at91.c    |    2 -
 drivers/usb/host/ohci-au1xxx.c  |    1 
 drivers/usb/host/ohci-hcd.c     |   26 ++++++++++++++++-------
 drivers/usb/host/ohci-hub.c     |   45 +++++++++++++++++++++++++++-------------
 drivers/usb/host/ohci-lh7a404.c |    1 
 drivers/usb/host/ohci-omap.c    |    1 
 drivers/usb/host/ohci-pci.c     |    1 
 drivers/usb/host/ohci-ppc-soc.c |    1 
 drivers/usb/host/ohci-pxa27x.c  |    1 
 drivers/usb/host/ohci-s3c2410.c |    1 
 drivers/usb/host/ohci-sa1111.c  |    1 
 drivers/usb/host/ohci.h         |    2 -
 12 files changed, 59 insertions(+), 24 deletions(-)

--- gregkh-2.6.orig/drivers/usb/host/ohci-at91.c
+++ gregkh-2.6/drivers/usb/host/ohci-at91.c
@@ -230,7 +230,7 @@ static const struct hc_driver ohci_at91_
 	 */
 	.hub_status_data =	ohci_hub_status_data,
 	.hub_control =		ohci_hub_control,
-
+	.hub_irq_enable =	ohci_rhsc_enable,
 #ifdef CONFIG_PM
 	.bus_suspend =		ohci_bus_suspend,
 	.bus_resume =		ohci_bus_resume,
--- gregkh-2.6.orig/drivers/usb/host/ohci-au1xxx.c
+++ gregkh-2.6/drivers/usb/host/ohci-au1xxx.c
@@ -292,6 +292,7 @@ static const struct hc_driver ohci_au1xx
 	 */
 	.hub_status_data =	ohci_hub_status_data,
 	.hub_control =		ohci_hub_control,
+	.hub_irq_enable =	ohci_rhsc_enable,
 #ifdef	CONFIG_PM
 	.bus_suspend =		ohci_bus_suspend,
 	.bus_resume =		ohci_bus_resume,
--- gregkh-2.6.orig/drivers/usb/host/ohci-hcd.c
+++ gregkh-2.6/drivers/usb/host/ohci-hcd.c
@@ -101,7 +101,7 @@
 
 #include "../core/hcd.h"
 
-#define DRIVER_VERSION "2005 April 22"
+#define DRIVER_VERSION "2006 May 24"
 #define DRIVER_AUTHOR "Roman Weissgaerber, David Brownell"
 #define DRIVER_DESC "USB 1.1 'Open' Host Controller (OHCI) Driver"
 
@@ -110,7 +110,7 @@
 #undef OHCI_VERBOSE_DEBUG	/* not always helpful */
 
 /* For initializing controller (mask in an HCFS mode too) */
-#define	OHCI_CONTROL_INIT 	OHCI_CTRL_CBSR
+#define	OHCI_CONTROL_INIT	OHCI_CTRL_CBSR
 #define	OHCI_INTR_INIT \
 	(OHCI_INTR_MIE | OHCI_INTR_UE | OHCI_INTR_RD | OHCI_INTR_WDH)
 
@@ -446,7 +446,7 @@ static int ohci_init (struct ohci_hcd *o
 
 	disable (ohci);
 	ohci->regs = hcd->regs;
-	ohci->next_statechange = jiffies;
+	ohci->next_statechange = jiffies + msecs_to_jiffies (250);
 
 	/* REVISIT this BIOS handshake is now moved into PCI "quirks", and
 	 * was never needed for most non-PCI systems ... remove the code?
@@ -636,11 +636,12 @@ retry:
 			ohci_readl (ohci, &ohci->regs->periodicstart));
 		return -EOVERFLOW;
 	}
+	hcd->uses_new_polling = 1;
 
- 	/* start controller operations */
+	/* start controller operations */
 	ohci->hc_control &= OHCI_CTRL_RWC;
- 	ohci->hc_control |= OHCI_CONTROL_INIT | OHCI_USB_OPER;
- 	ohci_writel (ohci, ohci->hc_control, &ohci->regs->control);
+	ohci->hc_control |= OHCI_CONTROL_INIT | OHCI_USB_OPER;
+	ohci_writel (ohci, ohci->hc_control, &ohci->regs->control);
 	hcd->state = HC_STATE_RUNNING;
 
 	/* wake on ConnectStatusChange, matching external hubs */
@@ -648,7 +649,7 @@ retry:
 
 	/* Choose the interrupts we care about now, others later on demand */
 	mask = OHCI_INTR_INIT;
-	ohci_writel (ohci, mask, &ohci->regs->intrstatus);
+	ohci_writel (ohci, ~0, &ohci->regs->intrstatus);
 	ohci_writel (ohci, mask, &ohci->regs->intrenable);
 
 	/* handle root hub init quirks ... */
@@ -709,7 +710,16 @@ static irqreturn_t ohci_irq (struct usb_
 	/* interrupt for some other device? */
 	} else if ((ints &= ohci_readl (ohci, &regs->intrenable)) == 0) {
 		return IRQ_NOTMINE;
-	} 
+	}
+
+	if (ints & OHCI_INTR_RHSC) {
+		ohci_vdbg (ohci, "rhsc\n");
+		/* some silicon won't clear RHSC before khubd kicks in */
+		ohci_writel (ohci, OHCI_INTR_RHSC, &regs->intrdisable);
+		hcd->poll_rh = 1;
+		ohci_writel (ohci, OHCI_INTR_RHSC, &regs->intrstatus);
+		usb_hcd_poll_rh_status(hcd);
+	}
 
 	if (ints & OHCI_INTR_UE) {
 		disable (ohci);
--- gregkh-2.6.orig/drivers/usb/host/ohci-hub.c
+++ gregkh-2.6/drivers/usb/host/ohci-hub.c
@@ -36,6 +36,14 @@
 
 /*-------------------------------------------------------------------------*/
 
+/* hcd->hub_irq_enable() ? */
+static void ohci_rhsc_enable (struct usb_hcd *hcd)
+{
+	struct ohci_hcd		*ohci = hcd_to_ohci (hcd);
+
+	ohci_writel (ohci, OHCI_INTR_RHSC, &ohci->regs->intrenable);
+}
+
 #ifdef	CONFIG_PM
 
 #define OHCI_SCHED_ENABLES \
@@ -123,6 +131,9 @@ static int ohci_bus_suspend (struct usb_
 	/* no resumes until devices finish suspending */
 	ohci->next_statechange = jiffies + msecs_to_jiffies (5);
 
+	/* RHSC or RD irq will wakeup; no timer polling */
+	hcd->poll_rh = 0;
+
 done:
 	/* external suspend vs self autosuspend ... same effect */
 	if (status == 0)
@@ -349,35 +360,41 @@ ohci_hub_status_data (struct usb_hcd *hc
 			continue;
 		}
 
-		/* can suspend if no ports are enabled; or if all all
+
+		/* can suspend if no ports are enabled; or if all the
 		 * enabled ports are suspended AND remote wakeup is on.
 		 */
 		if (!(status & RH_PS_CCS))
 			continue;
-		if ((status & RH_PS_PSS) && can_suspend)
-			continue;
-		can_suspend = 0;
+		if (!(status & RH_PS_PSS))
+			can_suspend = 0;
 	}
+
 done:
 	spin_unlock_irqrestore (&ohci->lock, flags);
 
 #ifdef CONFIG_PM
-	/* save power by suspending idle root hubs;
-	 * INTR_RD wakes us when there's work
+	/* save power by suspending idle root hubs and avoiding timer polls;
+	 * INTR_RD or INTR_RHSC wakes us when there's work, and polling is
+	 * used only to defer autosuspend.
 	 */
 	if (can_suspend
 			&& !changed
 			&& !ohci->ed_rm_list
 			&& ((OHCI_CTRL_HCFS | OHCI_SCHED_ENABLES)
 					& ohci->hc_control)
-				== OHCI_USB_OPER
-			&& time_after (jiffies, ohci->next_statechange)
-			&& usb_trylock_device (hcd->self.root_hub) == 0
-			) {
-		ohci_vdbg (ohci, "autosuspend\n");
-		(void) ohci_bus_suspend (hcd);
-		usb_unlock_device (hcd->self.root_hub);
-	}
+				== OHCI_USB_OPER) {
+		/* if we can't yet autosuspend, poll until we can */
+		if (time_after (jiffies, ohci->next_statechange)
+				&& usb_trylock_device (hcd->self.root_hub) == 0
+				) {
+			ohci_vdbg (ohci, "autosuspend\n");
+			(void) ohci_bus_suspend (hcd);
+			usb_unlock_device (hcd->self.root_hub);
+		} else
+			hcd->poll_rh = 1;
+	} else
+		hcd->poll_rh = 0;
 #endif
 
 	return changed ? length : 0;
--- gregkh-2.6.orig/drivers/usb/host/ohci-lh7a404.c
+++ gregkh-2.6/drivers/usb/host/ohci-lh7a404.c
@@ -196,6 +196,7 @@ static const struct hc_driver ohci_lh7a4
 	 */
 	.hub_status_data =	ohci_hub_status_data,
 	.hub_control =		ohci_hub_control,
+	.hub_irq_enable =	ohci_rhsc_enable,
 #ifdef	CONFIG_PM
 	.bus_suspend =		ohci_bus_suspend,
 	.bus_resume =		ohci_bus_resume,
--- gregkh-2.6.orig/drivers/usb/host/ohci-omap.c
+++ gregkh-2.6/drivers/usb/host/ohci-omap.c
@@ -429,6 +429,7 @@ static const struct hc_driver ohci_omap_
 	 */
 	.hub_status_data =	ohci_hub_status_data,
 	.hub_control =		ohci_hub_control,
+	.hub_irq_enable =	ohci_rhsc_enable,
 #ifdef	CONFIG_PM
 	.bus_suspend =		ohci_bus_suspend,
 	.bus_resume =		ohci_bus_resume,
--- gregkh-2.6.orig/drivers/usb/host/ohci-pci.c
+++ gregkh-2.6/drivers/usb/host/ohci-pci.c
@@ -194,6 +194,7 @@ static const struct hc_driver ohci_pci_h
 	 */
 	.hub_status_data =	ohci_hub_status_data,
 	.hub_control =		ohci_hub_control,
+	.hub_irq_enable =	ohci_rhsc_enable,
 #ifdef	CONFIG_PM
 	.bus_suspend =		ohci_bus_suspend,
 	.bus_resume =		ohci_bus_resume,
--- gregkh-2.6.orig/drivers/usb/host/ohci-ppc-soc.c
+++ gregkh-2.6/drivers/usb/host/ohci-ppc-soc.c
@@ -166,6 +166,7 @@ static const struct hc_driver ohci_ppc_s
 	 */
 	.hub_status_data =	ohci_hub_status_data,
 	.hub_control =		ohci_hub_control,
+	.hub_irq_enable =	ohci_rhsc_enable,
 #ifdef	CONFIG_PM
 	.bus_suspend =		ohci_bus_suspend,
 	.bus_resume =		ohci_bus_resume,
--- gregkh-2.6.orig/drivers/usb/host/ohci-pxa27x.c
+++ gregkh-2.6/drivers/usb/host/ohci-pxa27x.c
@@ -288,6 +288,7 @@ static const struct hc_driver ohci_pxa27
 	 */
 	.hub_status_data =	ohci_hub_status_data,
 	.hub_control =		ohci_hub_control,
+	.hub_irq_enable =	ohci_rhsc_enable,
 #ifdef  CONFIG_PM
 	.bus_suspend =		ohci_bus_suspend,
 	.bus_resume =		ohci_bus_resume,
--- gregkh-2.6.orig/drivers/usb/host/ohci-s3c2410.c
+++ gregkh-2.6/drivers/usb/host/ohci-s3c2410.c
@@ -465,6 +465,7 @@ static const struct hc_driver ohci_s3c24
 	 */
 	.hub_status_data =	ohci_s3c2410_hub_status_data,
 	.hub_control =		ohci_s3c2410_hub_control,
+	.hub_irq_enable =	ohci_rhsc_enable,
 #ifdef	CONFIG_PM
 	.bus_suspend =		ohci_bus_suspend,
 	.bus_resume =		ohci_bus_resume,
--- gregkh-2.6.orig/drivers/usb/host/ohci-sa1111.c
+++ gregkh-2.6/drivers/usb/host/ohci-sa1111.c
@@ -235,6 +235,7 @@ static const struct hc_driver ohci_sa111
 	 */
 	.hub_status_data =	ohci_hub_status_data,
 	.hub_control =		ohci_hub_control,
+	.hub_irq_enable =	ohci_rhsc_enable,
 #ifdef	CONFIG_PM
 	.bus_suspend =		ohci_bus_suspend,
 	.bus_resume =		ohci_bus_resume,
--- gregkh-2.6.orig/drivers/usb/host/ohci.h
+++ gregkh-2.6/drivers/usb/host/ohci.h
@@ -385,7 +385,7 @@ struct ohci_hcd {
 	 */
 	int			num_ports;
 	int			load [NUM_INTS];
-	u32 			hc_control;	/* copy of hc control reg */
+	u32			hc_control;	/* copy of hc control reg */
 	unsigned long		next_statechange;	/* suspend/resume */
 	u32			fminterval;		/* saved register */