summaryrefslogtreecommitdiffstats
path: root/common/usb.c
diff options
context:
space:
mode:
authorRemy Bohmer <linux@bohmer.net>2008-09-16 14:55:42 +0200
committerMarkus Klotzbuecher <mk@denx.de>2008-09-17 15:41:05 +0200
commitbe19d324edc1a1d7f393d24e10d164cd94c91a00 (patch)
treeb8def4593f28fadb96b1ab713a8a65a2ace03806 /common/usb.c
parent87b4ef560cf2da4ccc9e59711ad1ff7fafe96670 (diff)
downloadtalos-obmc-uboot-be19d324edc1a1d7f393d24e10d164cd94c91a00.tar.gz
talos-obmc-uboot-be19d324edc1a1d7f393d24e10d164cd94c91a00.zip
Fix for USB sticks not working on ARM while using GCC 4.x compilers
The GCC-compiler makes an optimisation error while optimising the routine usb_set_maxpacket(). This should be fixed in the compiler in the first place, but there lots of compilers out there that makes this error, that it is probably wiser to workaround it in U-boot itself. What happens is that the register r3 is used as loop-counter 'i', but gets overwritten later on. From there it starts using register r3 for several other things and the assembler code is becoming a big mess. This is clearly a compiler bug. This error occurs on at least several versions of Code Sourcery Lite compilers for ARM. Like the Edition 2008q1, and 2008q3, It has also been seen on other compilers, while compiling for armv4t, or armv5te with Os, O1 and O2. We work around it by splitting up this routine in 2 parts, and making sure that the split out part is NOT inlined any longer. This will make GCC spit out assembler that do not show this problem. Another possibility is to adapt the Makefile to stop optimisation for the complete file. I think this solution is nicer. Signed-off-by: Remy Bohmer <linux@bohmer.net> Signed-off-by: Markus Klotzbuecher <mk@denx.de>
Diffstat (limited to 'common/usb.c')
-rw-r--r--common/usb.c73
1 files changed, 46 insertions, 27 deletions
diff --git a/common/usb.c b/common/usb.c
index 52e5964c77..4d64ccb438 100644
--- a/common/usb.c
+++ b/common/usb.c
@@ -245,40 +245,59 @@ int usb_maxpacket(struct usb_device *dev,unsigned long pipe)
return(dev->epmaxpacketin[((pipe>>15) & 0xf)]);
}
+/* The routine usb_set_maxpacket_ep() is extracted from the loop of routine
+ * usb_set_maxpacket(), because the optimizer of GCC 4.x chokes on this routine
+ * when it is inlined in 1 single routine. What happens is that the register r3
+ * is used as loop-count 'i', but gets overwritten later on.
+ * This is clearly a compiler bug, but it is easier to workaround it here than
+ * to update the compiler (Occurs with at least several GCC 4.{1,2},x
+ * CodeSourcery compilers like e.g. 2007q3, 2008q1, 2008q3 lite editions on ARM)
+ */
+static void __attribute__((noinline))
+usb_set_maxpacket_ep(struct usb_device *dev, struct usb_endpoint_descriptor *ep)
+{
+ int b;
+
+ b = ep->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
+
+ if ((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
+ USB_ENDPOINT_XFER_CONTROL) {
+ /* Control => bidirectional */
+ dev->epmaxpacketout[b] = ep->wMaxPacketSize;
+ dev->epmaxpacketin [b] = ep->wMaxPacketSize;
+ USB_PRINTF("##Control EP epmaxpacketout/in[%d] = %d\n",
+ b, dev->epmaxpacketin[b]);
+ } else {
+ if ((ep->bEndpointAddress & 0x80) == 0) {
+ /* OUT Endpoint */
+ if (ep->wMaxPacketSize > dev->epmaxpacketout[b]) {
+ dev->epmaxpacketout[b] = ep->wMaxPacketSize;
+ USB_PRINTF("##EP epmaxpacketout[%d] = %d\n",
+ b, dev->epmaxpacketout[b]);
+ }
+ } else {
+ /* IN Endpoint */
+ if (ep->wMaxPacketSize > dev->epmaxpacketin[b]) {
+ dev->epmaxpacketin[b] = ep->wMaxPacketSize;
+ USB_PRINTF("##EP epmaxpacketin[%d] = %d\n",
+ b, dev->epmaxpacketin[b]);
+ }
+ } /* if out */
+ } /* if control */
+}
+
/*
* set the max packed value of all endpoints in the given configuration
*/
int usb_set_maxpacket(struct usb_device *dev)
{
- int i,ii,b;
- struct usb_endpoint_descriptor *ep;
+ int i, ii;
- for(i=0; i<dev->config.bNumInterfaces;i++) {
- for(ii=0; ii<dev->config.if_desc[i].bNumEndpoints; ii++) {
- ep = &dev->config.if_desc[i].ep_desc[ii];
- b=ep->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
+ for (i = 0; i < dev->config.bNumInterfaces; i++)
+ for (ii = 0; ii < dev->config.if_desc[i].bNumEndpoints; ii++)
+ usb_set_maxpacket_ep(dev,
+ &dev->config.if_desc[i].ep_desc[ii]);
- if((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)==USB_ENDPOINT_XFER_CONTROL) { /* Control => bidirectional */
- dev->epmaxpacketout[b] = ep->wMaxPacketSize;
- dev->epmaxpacketin [b] = ep->wMaxPacketSize;
- USB_PRINTF("##Control EP epmaxpacketout/in[%d] = %d\n",b,dev->epmaxpacketin[b]);
- }
- else {
- if ((ep->bEndpointAddress & 0x80)==0) { /* OUT Endpoint */
- if(ep->wMaxPacketSize > dev->epmaxpacketout[b]) {
- dev->epmaxpacketout[b] = ep->wMaxPacketSize;
- USB_PRINTF("##EP epmaxpacketout[%d] = %d\n",b,dev->epmaxpacketout[b]);
- }
- }
- else { /* IN Endpoint */
- if(ep->wMaxPacketSize > dev->epmaxpacketin[b]) {
- dev->epmaxpacketin[b] = ep->wMaxPacketSize;
- USB_PRINTF("##EP epmaxpacketin[%d] = %d\n",b,dev->epmaxpacketin[b]);
- }
- } /* if out */
- } /* if control */
- } /* for each endpoint */
- }
return 0;
}
OpenPOWER on IntegriCloud