[PATCH 1/3] fix-broken-usb-handed-to-linux.patch

Andy Green andy at openmoko.com
Fri Aug 1 08:54:13 CEST 2008

The USB callback for udc / enumeration notification is very time
critical.  Recently, we started using this for PMU actions, which
occur on the ridiculously slow I2C bus and block the USB stack for
the duration.

This caused a very bad behaviour in the USB Device hardware of
spamming the USB bus at 6MHz for the entire session, even after
Linux had booted, rendering the USB interface not operational.
The chances were about 50:50 per boot you would have broken USB.

This patch removes the I2C action from the time critical path
and instead takes care to track it only if we are spinning
waiting for enumeration because we have a bad battery level.
The I2C transaction then takes place out of any time critical

Enumeration is lost anyway as soon as we enter Linux where the
device is enumerated fresh and the Linux pcf50633 driver will
handle it.

One of the results of Insane Spam Mode on USB is not being able
to enumerate properly with host PC over USB.  And that leads
directly to not being able to charge the device for the whole

Signed-off-by: Andy Green <andy at openmoko.com>

 board/neo1973/common/udc.c  |   14 ++++++++++++--
 board/neo1973/gta02/gta02.c |    6 ++++++
 2 files changed, 18 insertions(+), 2 deletions(-)

diff --git a/board/neo1973/common/udc.c b/board/neo1973/common/udc.c
index 1cf888d..d547cb8 100644
--- a/board/neo1973/common/udc.c
+++ b/board/neo1973/common/udc.c
@@ -5,6 +5,8 @@
 #include <pcf50606.h>
 #include <pcf50633.h>
+int udc_usb_maxcurrent = 0;
 void udc_ctrl(enum usbd_event event, int param)
 	S3C24X0_GPIO * const gpio = S3C24X0_GetBase_GPIO();
@@ -26,10 +28,18 @@ void udc_ctrl(enum usbd_event event, int param)
 #elif defined(CONFIG_GTA02_REVISION)
+		/* if we take time out here to do the excrutiatingly slow
+		 * I2C transaction to the PMU to change current limit, it
+		 * gives us 50:50 chance of trashing the USB connection for
+		 * the whole session, including through Linux.
+		 *
+		 * Therefore we track what we're allowed and update it on the
+		 * I2C bus elsewhere when it changes.
+		 */
 		if (param)
-			pcf50633_usb_maxcurrent(500);
+			udc_usb_maxcurrent = 500;
-			pcf50633_usb_maxcurrent(0);
+			udc_usb_maxcurrent = 0;
diff --git a/board/neo1973/gta02/gta02.c b/board/neo1973/gta02/gta02.c
index e77359b..c4766b0 100644
--- a/board/neo1973/gta02/gta02.c
+++ b/board/neo1973/gta02/gta02.c
@@ -74,6 +74,8 @@ unsigned int neo1973_wakeup_cause;
 extern unsigned char booted_from_nand;
 extern unsigned char booted_from_nor;
 extern int nobootdelay;
+extern int udc_usb_maxcurrent;
 char __cfg_prompt[20] = "GTA02vXX # ";
@@ -395,6 +397,10 @@ static void wait_for_power(void)
 	while (1) {
+		/* track what the time-critical udc callback allows us */
+		if (pcf50633_usb_last_maxcurrent != udc_usb_maxcurrent)
+			pcf50633_usb_maxcurrent(udc_usb_maxcurrent);
 		/* we have plenty of external power -> try to boot */
 		if (pcf50633_usb_last_maxcurrent >= 500)

More information about the openmoko-kernel mailing list