diff options
Diffstat (limited to 'drivers/usb/host/ehci-timer.c')
-rw-r--r-- | drivers/usb/host/ehci-timer.c | 80 |
1 files changed, 80 insertions, 0 deletions
diff --git a/drivers/usb/host/ehci-timer.c b/drivers/usb/host/ehci-timer.c index f6279e2883a8..ecd3296157c6 100644 --- a/drivers/usb/host/ehci-timer.c +++ b/drivers/usb/host/ehci-timer.c | |||
@@ -16,6 +16,28 @@ | |||
16 | 16 | ||
17 | /*-------------------------------------------------------------------------*/ | 17 | /*-------------------------------------------------------------------------*/ |
18 | 18 | ||
19 | /* Set a bit in the USBCMD register */ | ||
20 | static void ehci_set_command_bit(struct ehci_hcd *ehci, u32 bit) | ||
21 | { | ||
22 | ehci->command |= bit; | ||
23 | ehci_writel(ehci, ehci->command, &ehci->regs->command); | ||
24 | |||
25 | /* unblock posted write */ | ||
26 | ehci_readl(ehci, &ehci->regs->command); | ||
27 | } | ||
28 | |||
29 | /* Clear a bit in the USBCMD register */ | ||
30 | static void ehci_clear_command_bit(struct ehci_hcd *ehci, u32 bit) | ||
31 | { | ||
32 | ehci->command &= ~bit; | ||
33 | ehci_writel(ehci, ehci->command, &ehci->regs->command); | ||
34 | |||
35 | /* unblock posted write */ | ||
36 | ehci_readl(ehci, &ehci->regs->command); | ||
37 | } | ||
38 | |||
39 | /*-------------------------------------------------------------------------*/ | ||
40 | |||
19 | /* | 41 | /* |
20 | * EHCI timer support... Now using hrtimers. | 42 | * EHCI timer support... Now using hrtimers. |
21 | * | 43 | * |
@@ -45,6 +67,8 @@ | |||
45 | * the event types indexed by enum ehci_hrtimer_event in ehci.h. | 67 | * the event types indexed by enum ehci_hrtimer_event in ehci.h. |
46 | */ | 68 | */ |
47 | static unsigned event_delays_ns[] = { | 69 | static unsigned event_delays_ns[] = { |
70 | 1 * NSEC_PER_MSEC, /* EHCI_HRTIMER_POLL_PSS */ | ||
71 | 10 * NSEC_PER_MSEC, /* EHCI_HRTIMER_DISABLE_PERIODIC */ | ||
48 | }; | 72 | }; |
49 | 73 | ||
50 | /* Enable a pending hrtimer event */ | 74 | /* Enable a pending hrtimer event */ |
@@ -67,12 +91,68 @@ static void ehci_enable_event(struct ehci_hcd *ehci, unsigned event, | |||
67 | } | 91 | } |
68 | 92 | ||
69 | 93 | ||
94 | /* Poll the STS_PSS status bit; see when it agrees with CMD_PSE */ | ||
95 | static void ehci_poll_PSS(struct ehci_hcd *ehci) | ||
96 | { | ||
97 | unsigned actual, want; | ||
98 | |||
99 | /* Don't do anything if the controller isn't running (e.g., died) */ | ||
100 | if (ehci->rh_state != EHCI_RH_RUNNING) | ||
101 | return; | ||
102 | |||
103 | want = (ehci->command & CMD_PSE) ? STS_PSS : 0; | ||
104 | actual = ehci_readl(ehci, &ehci->regs->status) & STS_PSS; | ||
105 | |||
106 | if (want != actual) { | ||
107 | |||
108 | /* Poll again later, but give up after about 20 ms */ | ||
109 | if (ehci->PSS_poll_count++ < 20) { | ||
110 | ehci_enable_event(ehci, EHCI_HRTIMER_POLL_PSS, true); | ||
111 | return; | ||
112 | } | ||
113 | ehci_warn(ehci, "Waited too long for the periodic schedule status, giving up\n"); | ||
114 | } | ||
115 | ehci->PSS_poll_count = 0; | ||
116 | |||
117 | /* The status is up-to-date; restart or stop the schedule as needed */ | ||
118 | if (want == 0) { /* Stopped */ | ||
119 | free_cached_lists(ehci); | ||
120 | if (ehci->periodic_count > 0) { | ||
121 | |||
122 | /* make sure ehci_work scans these */ | ||
123 | ehci->next_uframe = ehci_read_frame_index(ehci) | ||
124 | & ((ehci->periodic_size << 3) - 1); | ||
125 | ehci_set_command_bit(ehci, CMD_PSE); | ||
126 | } | ||
127 | |||
128 | } else { /* Running */ | ||
129 | if (ehci->periodic_count == 0) { | ||
130 | |||
131 | /* Turn off the schedule after a while */ | ||
132 | ehci_enable_event(ehci, EHCI_HRTIMER_DISABLE_PERIODIC, | ||
133 | true); | ||
134 | } | ||
135 | } | ||
136 | } | ||
137 | |||
138 | /* Turn off the periodic schedule after a brief delay */ | ||
139 | static void ehci_disable_PSE(struct ehci_hcd *ehci) | ||
140 | { | ||
141 | ehci_clear_command_bit(ehci, CMD_PSE); | ||
142 | |||
143 | /* Poll to see when it actually stops */ | ||
144 | ehci_enable_event(ehci, EHCI_HRTIMER_POLL_PSS, true); | ||
145 | } | ||
146 | |||
147 | |||
70 | /* | 148 | /* |
71 | * Handler functions for the hrtimer event types. | 149 | * Handler functions for the hrtimer event types. |
72 | * Keep this array in the same order as the event types indexed by | 150 | * Keep this array in the same order as the event types indexed by |
73 | * enum ehci_hrtimer_event in ehci.h. | 151 | * enum ehci_hrtimer_event in ehci.h. |
74 | */ | 152 | */ |
75 | static void (*event_handlers[])(struct ehci_hcd *) = { | 153 | static void (*event_handlers[])(struct ehci_hcd *) = { |
154 | ehci_poll_PSS, /* EHCI_HRTIMER_POLL_PSS */ | ||
155 | ehci_disable_PSE, /* EHCI_HRTIMER_DISABLE_PERIODIC */ | ||
76 | }; | 156 | }; |
77 | 157 | ||
78 | static enum hrtimer_restart ehci_hrtimer_func(struct hrtimer *t) | 158 | static enum hrtimer_restart ehci_hrtimer_func(struct hrtimer *t) |