diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/usb/host/ehci-hcd.c | 6 | ||||
-rw-r--r-- | drivers/usb/host/ehci-sched.c | 17 | ||||
-rw-r--r-- | drivers/usb/host/ehci-sysfs.c | 104 | ||||
-rw-r--r-- | drivers/usb/host/ehci.h | 2 |
4 files changed, 115 insertions, 14 deletions
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 8306155de9e5..4ee62bef3723 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c | |||
@@ -572,6 +572,12 @@ static int ehci_init(struct usb_hcd *hcd) | |||
572 | hcc_params = ehci_readl(ehci, &ehci->caps->hcc_params); | 572 | hcc_params = ehci_readl(ehci, &ehci->caps->hcc_params); |
573 | 573 | ||
574 | /* | 574 | /* |
575 | * by default set standard 80% (== 100 usec/uframe) max periodic | ||
576 | * bandwidth as required by USB 2.0 | ||
577 | */ | ||
578 | ehci->uframe_periodic_max = 100; | ||
579 | |||
580 | /* | ||
575 | * hw default: 1K periodic list heads, one per frame. | 581 | * hw default: 1K periodic list heads, one per frame. |
576 | * periodic_size can shrink by USBCMD update if hcc_params allows. | 582 | * periodic_size can shrink by USBCMD update if hcc_params allows. |
577 | */ | 583 | */ |
diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c index 6c9fbe352f73..2abf8543f083 100644 --- a/drivers/usb/host/ehci-sched.c +++ b/drivers/usb/host/ehci-sched.c | |||
@@ -172,7 +172,7 @@ periodic_usecs (struct ehci_hcd *ehci, unsigned frame, unsigned uframe) | |||
172 | } | 172 | } |
173 | } | 173 | } |
174 | #ifdef DEBUG | 174 | #ifdef DEBUG |
175 | if (usecs > 100) | 175 | if (usecs > ehci->uframe_periodic_max) |
176 | ehci_err (ehci, "uframe %d sched overrun: %d usecs\n", | 176 | ehci_err (ehci, "uframe %d sched overrun: %d usecs\n", |
177 | frame * 8 + uframe, usecs); | 177 | frame * 8 + uframe, usecs); |
178 | #endif | 178 | #endif |
@@ -709,11 +709,8 @@ static int check_period ( | |||
709 | if (uframe >= 8) | 709 | if (uframe >= 8) |
710 | return 0; | 710 | return 0; |
711 | 711 | ||
712 | /* | 712 | /* convert "usecs we need" to "max already claimed" */ |
713 | * 80% periodic == 100 usec/uframe available | 713 | usecs = ehci->uframe_periodic_max - usecs; |
714 | * convert "usecs we need" to "max already claimed" | ||
715 | */ | ||
716 | usecs = 100 - usecs; | ||
717 | 714 | ||
718 | /* we "know" 2 and 4 uframe intervals were rejected; so | 715 | /* we "know" 2 and 4 uframe intervals were rejected; so |
719 | * for period 0, check _every_ microframe in the schedule. | 716 | * for period 0, check _every_ microframe in the schedule. |
@@ -1286,9 +1283,9 @@ itd_slot_ok ( | |||
1286 | { | 1283 | { |
1287 | uframe %= period; | 1284 | uframe %= period; |
1288 | do { | 1285 | do { |
1289 | /* can't commit more than 80% periodic == 100 usec */ | 1286 | /* can't commit more than uframe_periodic_max usec */ |
1290 | if (periodic_usecs (ehci, uframe >> 3, uframe & 0x7) | 1287 | if (periodic_usecs (ehci, uframe >> 3, uframe & 0x7) |
1291 | > (100 - usecs)) | 1288 | > (ehci->uframe_periodic_max - usecs)) |
1292 | return 0; | 1289 | return 0; |
1293 | 1290 | ||
1294 | /* we know urb->interval is 2^N uframes */ | 1291 | /* we know urb->interval is 2^N uframes */ |
@@ -1345,7 +1342,7 @@ sitd_slot_ok ( | |||
1345 | #endif | 1342 | #endif |
1346 | 1343 | ||
1347 | /* check starts (OUT uses more than one) */ | 1344 | /* check starts (OUT uses more than one) */ |
1348 | max_used = 100 - stream->usecs; | 1345 | max_used = ehci->uframe_periodic_max - stream->usecs; |
1349 | for (tmp = stream->raw_mask & 0xff; tmp; tmp >>= 1, uf++) { | 1346 | for (tmp = stream->raw_mask & 0xff; tmp; tmp >>= 1, uf++) { |
1350 | if (periodic_usecs (ehci, frame, uf) > max_used) | 1347 | if (periodic_usecs (ehci, frame, uf) > max_used) |
1351 | return 0; | 1348 | return 0; |
@@ -1354,7 +1351,7 @@ sitd_slot_ok ( | |||
1354 | /* for IN, check CSPLIT */ | 1351 | /* for IN, check CSPLIT */ |
1355 | if (stream->c_usecs) { | 1352 | if (stream->c_usecs) { |
1356 | uf = uframe & 7; | 1353 | uf = uframe & 7; |
1357 | max_used = 100 - stream->c_usecs; | 1354 | max_used = ehci->uframe_periodic_max - stream->c_usecs; |
1358 | do { | 1355 | do { |
1359 | tmp = 1 << uf; | 1356 | tmp = 1 << uf; |
1360 | tmp <<= 8; | 1357 | tmp <<= 8; |
diff --git a/drivers/usb/host/ehci-sysfs.c b/drivers/usb/host/ehci-sysfs.c index 29824a98aec9..14ced00ba220 100644 --- a/drivers/usb/host/ehci-sysfs.c +++ b/drivers/usb/host/ehci-sysfs.c | |||
@@ -74,21 +74,117 @@ static ssize_t store_companion(struct device *dev, | |||
74 | } | 74 | } |
75 | static DEVICE_ATTR(companion, 0644, show_companion, store_companion); | 75 | static DEVICE_ATTR(companion, 0644, show_companion, store_companion); |
76 | 76 | ||
77 | |||
78 | /* | ||
79 | * Display / Set uframe_periodic_max | ||
80 | */ | ||
81 | static ssize_t show_uframe_periodic_max(struct device *dev, | ||
82 | struct device_attribute *attr, | ||
83 | char *buf) | ||
84 | { | ||
85 | struct ehci_hcd *ehci; | ||
86 | int n; | ||
87 | |||
88 | ehci = hcd_to_ehci(bus_to_hcd(dev_get_drvdata(dev))); | ||
89 | n = scnprintf(buf, PAGE_SIZE, "%d\n", ehci->uframe_periodic_max); | ||
90 | return n; | ||
91 | } | ||
92 | |||
93 | |||
94 | static ssize_t store_uframe_periodic_max(struct device *dev, | ||
95 | struct device_attribute *attr, | ||
96 | const char *buf, size_t count) | ||
97 | { | ||
98 | struct ehci_hcd *ehci; | ||
99 | unsigned uframe_periodic_max; | ||
100 | unsigned frame, uframe; | ||
101 | unsigned short allocated_max; | ||
102 | unsigned long flags; | ||
103 | ssize_t ret; | ||
104 | |||
105 | ehci = hcd_to_ehci(bus_to_hcd(dev_get_drvdata(dev))); | ||
106 | if (kstrtouint(buf, 0, &uframe_periodic_max) < 0) | ||
107 | return -EINVAL; | ||
108 | |||
109 | if (uframe_periodic_max < 100 || uframe_periodic_max >= 125) { | ||
110 | ehci_info(ehci, "rejecting invalid request for " | ||
111 | "uframe_periodic_max=%u\n", uframe_periodic_max); | ||
112 | return -EINVAL; | ||
113 | } | ||
114 | |||
115 | ret = -EINVAL; | ||
116 | |||
117 | /* | ||
118 | * lock, so that our checking does not race with possible periodic | ||
119 | * bandwidth allocation through submitting new urbs. | ||
120 | */ | ||
121 | spin_lock_irqsave (&ehci->lock, flags); | ||
122 | |||
123 | /* | ||
124 | * for request to decrease max periodic bandwidth, we have to check | ||
125 | * every microframe in the schedule to see whether the decrease is | ||
126 | * possible. | ||
127 | */ | ||
128 | if (uframe_periodic_max < ehci->uframe_periodic_max) { | ||
129 | allocated_max = 0; | ||
130 | |||
131 | for (frame = 0; frame < ehci->periodic_size; ++frame) | ||
132 | for (uframe = 0; uframe < 7; ++uframe) | ||
133 | allocated_max = max(allocated_max, | ||
134 | periodic_usecs (ehci, frame, uframe)); | ||
135 | |||
136 | if (allocated_max > uframe_periodic_max) { | ||
137 | ehci_info(ehci, | ||
138 | "cannot decrease uframe_periodic_max becase " | ||
139 | "periodic bandwidth is already allocated " | ||
140 | "(%u > %u)\n", | ||
141 | allocated_max, uframe_periodic_max); | ||
142 | goto out_unlock; | ||
143 | } | ||
144 | } | ||
145 | |||
146 | /* increasing is always ok */ | ||
147 | |||
148 | ehci_info(ehci, "setting max periodic bandwidth to %u%% " | ||
149 | "(== %u usec/uframe)\n", | ||
150 | 100*uframe_periodic_max/125, uframe_periodic_max); | ||
151 | |||
152 | if (uframe_periodic_max != 100) | ||
153 | ehci_warn(ehci, "max periodic bandwidth set is non-standard\n"); | ||
154 | |||
155 | ehci->uframe_periodic_max = uframe_periodic_max; | ||
156 | ret = count; | ||
157 | |||
158 | out_unlock: | ||
159 | spin_unlock_irqrestore (&ehci->lock, flags); | ||
160 | return ret; | ||
161 | } | ||
162 | static DEVICE_ATTR(uframe_periodic_max, 0644, show_uframe_periodic_max, store_uframe_periodic_max); | ||
163 | |||
164 | |||
77 | static inline int create_sysfs_files(struct ehci_hcd *ehci) | 165 | static inline int create_sysfs_files(struct ehci_hcd *ehci) |
78 | { | 166 | { |
167 | struct device *controller = ehci_to_hcd(ehci)->self.controller; | ||
79 | int i = 0; | 168 | int i = 0; |
80 | 169 | ||
81 | /* with integrated TT there is no companion! */ | 170 | /* with integrated TT there is no companion! */ |
82 | if (!ehci_is_TDI(ehci)) | 171 | if (!ehci_is_TDI(ehci)) |
83 | i = device_create_file(ehci_to_hcd(ehci)->self.controller, | 172 | i = device_create_file(controller, &dev_attr_companion); |
84 | &dev_attr_companion); | 173 | if (i) |
174 | goto out; | ||
175 | |||
176 | i = device_create_file(controller, &dev_attr_uframe_periodic_max); | ||
177 | out: | ||
85 | return i; | 178 | return i; |
86 | } | 179 | } |
87 | 180 | ||
88 | static inline void remove_sysfs_files(struct ehci_hcd *ehci) | 181 | static inline void remove_sysfs_files(struct ehci_hcd *ehci) |
89 | { | 182 | { |
183 | struct device *controller = ehci_to_hcd(ehci)->self.controller; | ||
184 | |||
90 | /* with integrated TT there is no companion! */ | 185 | /* with integrated TT there is no companion! */ |
91 | if (!ehci_is_TDI(ehci)) | 186 | if (!ehci_is_TDI(ehci)) |
92 | device_remove_file(ehci_to_hcd(ehci)->self.controller, | 187 | device_remove_file(controller, &dev_attr_companion); |
93 | &dev_attr_companion); | 188 | |
189 | device_remove_file(controller, &dev_attr_uframe_periodic_max); | ||
94 | } | 190 | } |
diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index bd6ff489baf9..fa3129fe1ee0 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h | |||
@@ -87,6 +87,8 @@ struct ehci_hcd { /* one per controller */ | |||
87 | union ehci_shadow *pshadow; /* mirror hw periodic table */ | 87 | union ehci_shadow *pshadow; /* mirror hw periodic table */ |
88 | int next_uframe; /* scan periodic, start here */ | 88 | int next_uframe; /* scan periodic, start here */ |
89 | unsigned periodic_sched; /* periodic activity count */ | 89 | unsigned periodic_sched; /* periodic activity count */ |
90 | unsigned uframe_periodic_max; /* max periodic time per uframe */ | ||
91 | |||
90 | 92 | ||
91 | /* list of itds & sitds completed while clock_frame was still active */ | 93 | /* list of itds & sitds completed while clock_frame was still active */ |
92 | struct list_head cached_itd_list; | 94 | struct list_head cached_itd_list; |