diff options
Diffstat (limited to 'drivers/usb/host/uhci-hcd.c')
-rw-r--r-- | drivers/usb/host/uhci-hcd.c | 84 |
1 files changed, 48 insertions, 36 deletions
diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c index e87692c31be4..49b9d390b95f 100644 --- a/drivers/usb/host/uhci-hcd.c +++ b/drivers/usb/host/uhci-hcd.c | |||
@@ -60,6 +60,11 @@ Randy Dunlap, Georg Acher, Deti Fliegl, Thomas Sailer, Roman Weissgaerber, \ | |||
60 | Alan Stern" | 60 | Alan Stern" |
61 | #define DRIVER_DESC "USB Universal Host Controller Interface driver" | 61 | #define DRIVER_DESC "USB Universal Host Controller Interface driver" |
62 | 62 | ||
63 | /* for flakey hardware, ignore overcurrent indicators */ | ||
64 | static int ignore_oc; | ||
65 | module_param(ignore_oc, bool, S_IRUGO); | ||
66 | MODULE_PARM_DESC(ignore_oc, "ignore hardware overcurrent indications"); | ||
67 | |||
63 | /* | 68 | /* |
64 | * debug = 0, no debugging messages | 69 | * debug = 0, no debugging messages |
65 | * debug = 1, dump failed URBs except for stalls | 70 | * debug = 1, dump failed URBs except for stalls |
@@ -87,6 +92,34 @@ static void suspend_rh(struct uhci_hcd *uhci, enum uhci_rh_state new_state); | |||
87 | static void wakeup_rh(struct uhci_hcd *uhci); | 92 | static void wakeup_rh(struct uhci_hcd *uhci); |
88 | static void uhci_get_current_frame_number(struct uhci_hcd *uhci); | 93 | static void uhci_get_current_frame_number(struct uhci_hcd *uhci); |
89 | 94 | ||
95 | /* | ||
96 | * Calculate the link pointer DMA value for the first Skeleton QH in a frame. | ||
97 | */ | ||
98 | static __le32 uhci_frame_skel_link(struct uhci_hcd *uhci, int frame) | ||
99 | { | ||
100 | int skelnum; | ||
101 | |||
102 | /* | ||
103 | * The interrupt queues will be interleaved as evenly as possible. | ||
104 | * There's not much to be done about period-1 interrupts; they have | ||
105 | * to occur in every frame. But we can schedule period-2 interrupts | ||
106 | * in odd-numbered frames, period-4 interrupts in frames congruent | ||
107 | * to 2 (mod 4), and so on. This way each frame only has two | ||
108 | * interrupt QHs, which will help spread out bandwidth utilization. | ||
109 | * | ||
110 | * ffs (Find First bit Set) does exactly what we need: | ||
111 | * 1,3,5,... => ffs = 0 => use skel_int2_qh = skelqh[8], | ||
112 | * 2,6,10,... => ffs = 1 => use skel_int4_qh = skelqh[7], etc. | ||
113 | * ffs >= 7 => not on any high-period queue, so use | ||
114 | * skel_int1_qh = skelqh[9]. | ||
115 | * Add in UHCI_NUMFRAMES to insure at least one bit is set. | ||
116 | */ | ||
117 | skelnum = 8 - (int) __ffs(frame | UHCI_NUMFRAMES); | ||
118 | if (skelnum <= 1) | ||
119 | skelnum = 9; | ||
120 | return UHCI_PTR_QH | cpu_to_le32(uhci->skelqh[skelnum]->dma_handle); | ||
121 | } | ||
122 | |||
90 | #include "uhci-debug.c" | 123 | #include "uhci-debug.c" |
91 | #include "uhci-q.c" | 124 | #include "uhci-q.c" |
92 | #include "uhci-hub.c" | 125 | #include "uhci-hub.c" |
@@ -169,6 +202,11 @@ static int resume_detect_interrupts_are_broken(struct uhci_hcd *uhci) | |||
169 | { | 202 | { |
170 | int port; | 203 | int port; |
171 | 204 | ||
205 | /* If we have to ignore overcurrent events then almost by definition | ||
206 | * we can't depend on resume-detect interrupts. */ | ||
207 | if (ignore_oc) | ||
208 | return 1; | ||
209 | |||
172 | switch (to_pci_dev(uhci_dev(uhci))->vendor) { | 210 | switch (to_pci_dev(uhci_dev(uhci))->vendor) { |
173 | default: | 211 | default: |
174 | break; | 212 | break; |
@@ -199,24 +237,16 @@ static int resume_detect_interrupts_are_broken(struct uhci_hcd *uhci) | |||
199 | 237 | ||
200 | static int remote_wakeup_is_broken(struct uhci_hcd *uhci) | 238 | static int remote_wakeup_is_broken(struct uhci_hcd *uhci) |
201 | { | 239 | { |
202 | static struct dmi_system_id broken_wakeup_table[] = { | ||
203 | { | ||
204 | .ident = "Asus A7V8X", | ||
205 | .matches = { | ||
206 | DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK"), | ||
207 | DMI_MATCH(DMI_BOARD_NAME, "A7V8X"), | ||
208 | DMI_MATCH(DMI_BOARD_VERSION, "REV 1.xx"), | ||
209 | } | ||
210 | }, | ||
211 | { } | ||
212 | }; | ||
213 | int port; | 240 | int port; |
241 | char *sys_info; | ||
242 | static char bad_Asus_board[] = "A7V8X"; | ||
214 | 243 | ||
215 | /* One of Asus's motherboards has a bug which causes it to | 244 | /* One of Asus's motherboards has a bug which causes it to |
216 | * wake up immediately from suspend-to-RAM if any of the ports | 245 | * wake up immediately from suspend-to-RAM if any of the ports |
217 | * are connected. In such cases we will not set EGSM. | 246 | * are connected. In such cases we will not set EGSM. |
218 | */ | 247 | */ |
219 | if (dmi_check_system(broken_wakeup_table)) { | 248 | sys_info = dmi_get_system_info(DMI_BOARD_NAME); |
249 | if (sys_info && !strcmp(sys_info, bad_Asus_board)) { | ||
220 | for (port = 0; port < uhci->rh_numports; ++port) { | 250 | for (port = 0; port < uhci->rh_numports; ++port) { |
221 | if (inw(uhci->io_addr + USBPORTSC1 + port * 2) & | 251 | if (inw(uhci->io_addr + USBPORTSC1 + port * 2) & |
222 | USBPORTSC_CCS) | 252 | USBPORTSC_CCS) |
@@ -255,7 +285,9 @@ __acquires(uhci->lock) | |||
255 | int_enable = USBINTR_RESUME; | 285 | int_enable = USBINTR_RESUME; |
256 | if (remote_wakeup_is_broken(uhci)) | 286 | if (remote_wakeup_is_broken(uhci)) |
257 | egsm_enable = 0; | 287 | egsm_enable = 0; |
258 | if (resume_detect_interrupts_are_broken(uhci) || !egsm_enable) | 288 | if (resume_detect_interrupts_are_broken(uhci) || !egsm_enable || |
289 | !device_may_wakeup( | ||
290 | &uhci_to_hcd(uhci)->self.root_hub->dev)) | ||
259 | uhci->working_RD = int_enable = 0; | 291 | uhci->working_RD = int_enable = 0; |
260 | 292 | ||
261 | outw(int_enable, uhci->io_addr + USBINTR); | 293 | outw(int_enable, uhci->io_addr + USBINTR); |
@@ -627,32 +659,11 @@ static int uhci_start(struct usb_hcd *hcd) | |||
627 | /* | 659 | /* |
628 | * Fill the frame list: make all entries point to the proper | 660 | * Fill the frame list: make all entries point to the proper |
629 | * interrupt queue. | 661 | * interrupt queue. |
630 | * | ||
631 | * The interrupt queues will be interleaved as evenly as possible. | ||
632 | * There's not much to be done about period-1 interrupts; they have | ||
633 | * to occur in every frame. But we can schedule period-2 interrupts | ||
634 | * in odd-numbered frames, period-4 interrupts in frames congruent | ||
635 | * to 2 (mod 4), and so on. This way each frame only has two | ||
636 | * interrupt QHs, which will help spread out bandwidth utilization. | ||
637 | */ | 662 | */ |
638 | for (i = 0; i < UHCI_NUMFRAMES; i++) { | 663 | for (i = 0; i < UHCI_NUMFRAMES; i++) { |
639 | int irq; | ||
640 | |||
641 | /* | ||
642 | * ffs (Find First bit Set) does exactly what we need: | ||
643 | * 1,3,5,... => ffs = 0 => use skel_int2_qh = skelqh[8], | ||
644 | * 2,6,10,... => ffs = 1 => use skel_int4_qh = skelqh[7], etc. | ||
645 | * ffs >= 7 => not on any high-period queue, so use | ||
646 | * skel_int1_qh = skelqh[9]. | ||
647 | * Add UHCI_NUMFRAMES to insure at least one bit is set. | ||
648 | */ | ||
649 | irq = 8 - (int) __ffs(i + UHCI_NUMFRAMES); | ||
650 | if (irq <= 1) | ||
651 | irq = 9; | ||
652 | 664 | ||
653 | /* Only place we don't use the frame list routines */ | 665 | /* Only place we don't use the frame list routines */ |
654 | uhci->frame[i] = UHCI_PTR_QH | | 666 | uhci->frame[i] = uhci_frame_skel_link(uhci, i); |
655 | cpu_to_le32(uhci->skelqh[irq]->dma_handle); | ||
656 | } | 667 | } |
657 | 668 | ||
658 | /* | 669 | /* |
@@ -921,7 +932,8 @@ static int __init uhci_hcd_init(void) | |||
921 | { | 932 | { |
922 | int retval = -ENOMEM; | 933 | int retval = -ENOMEM; |
923 | 934 | ||
924 | printk(KERN_INFO DRIVER_DESC " " DRIVER_VERSION "\n"); | 935 | printk(KERN_INFO DRIVER_DESC " " DRIVER_VERSION "%s\n", |
936 | ignore_oc ? ", overcurrent ignored" : ""); | ||
925 | 937 | ||
926 | if (usb_disabled()) | 938 | if (usb_disabled()) |
927 | return -ENODEV; | 939 | return -ENODEV; |