From a6ffd2cec8538c191fd40acc0ca4f015e01118dd Mon Sep 17 00:00:00 2001
From: z3deverp <z3@vmsv-debian.(none)>
Date: Thu, 21 May 2009 23:31:57 +0900
Subject: [PATCH] Automatic switch USB host/client mode

---
 arch/arm/mach-pxa/include/mach/sha_pon.h |    2 +
 arch/arm/mach-pxa/sha_pon.c              |  174 +++++++++++++++++++++++++++---
 2 files changed, 159 insertions(+), 17 deletions(-)

diff --git a/arch/arm/mach-pxa/include/mach/sha_pon.h b/arch/arm/mach-pxa/include/mach/sha_pon.h
old mode 100644
new mode 100755
index acbbebf..5d69201
--- a/arch/arm/mach-pxa/include/mach/sha_pon.h
+++ b/arch/arm/mach-pxa/include/mach/sha_pon.h
@@ -170,6 +170,8 @@ void sha_pon_gpio_bit_set(unsigned cpld_bitoffset, int value);
 #define SHA_PON007_I_DET_HEADSET_ANSWER	114	/*Headset answer key: 0/1=pressed/released*/
 #define SHA_PON007_I_DET_HEADSET_CONNECT	116	/*Headset: 0/1=connected/not connected*/
 
+#define SHA_PON011_I_DET_USB_MiniA	41	/*USB miniA cable: 0/1=Inserted/Ejected*/
+
 
 
 #if 0	/* not understand what to do  */
diff --git a/arch/arm/mach-pxa/sha_pon.c b/arch/arm/mach-pxa/sha_pon.c
index 1563a54..8c1122f 100755
--- a/arch/arm/mach-pxa/sha_pon.c
+++ b/arch/arm/mach-pxa/sha_pon.c
@@ -11,7 +11,6 @@
  * published by the Free Software Foundation.
  */
 
-#define	UDC	0	/* 0:disable udc driver */
 #define	TS	0	/* 0:disable touchscreen driver */
 
 #include <linux/init.h>
@@ -36,9 +35,7 @@
 #include <mach/mfp-pxa27x.h>
 #include <mach/pxa27x-udc.h>
 #include <mach/pxafb.h>
-#if UDC
 #include <mach/udc.h>
-#endif
 #include <mach/ohci.h>
 #include <mach/i2c.h>
 #include <mach/mmc.h>
@@ -152,11 +149,11 @@ int sha_pon_gpio_bit_get(unsigned gpio)
 		if( gpio < 32 )
 			data = GPLR0;
 		else if( gpio < 64 )
-			data = GPLR0;
+			data = GPLR1;
 		else if( gpio < 96 )
-			data = GPLR0;
+			data = GPLR2;
 		else
-			data = GPLR0;
+			data = GPLR3;
 		
 	} else if( gpio < SHA_PON_GPIO_OFFSET_CPLD_R ) {
 		gpio -= SHA_PON_GPIO_OFFSET_CPLD_W;
@@ -248,7 +245,7 @@ static struct sysdev_class sha_pon_cpld_sysclass = {
 };
 
 
-void sha_pon_init_cpld(void)
+static void sha_pon_init_cpld(void)
 {
 	int a;
 	
@@ -269,6 +266,153 @@ void sha_pon_init_cpld(void)
 }
 
 
+#define	SHA_PON_USB_STOP	0
+#define	SHA_PON_USB_HOST	1
+#define	SHA_PON_USB_CLIENT	2
+
+
+static int sha_pon003_usbscan(int mode_now)
+{
+	if( sha_pon_gpio_bit_get(SHA_PON003_I_DET_USB_VBUS) == 0 ) {
+		/* VBUS decected */
+		return( SHA_PON_USB_CLIENT );
+	}
+	
+	return( SHA_PON_USB_HOST );	/* normally host */
+}
+
+
+static int sha_pon007_usbscan(int mode_now)
+{
+	if( sha_pon_gpio_bit_get(SHA_PON007_I_DET_USB_MiniA) == 0 ) {
+		/* miniA cable inserted */
+		return( SHA_PON_USB_HOST );
+		
+	}
+	if( mode_now != SHA_PON_USB_HOST &&
+		sha_pon_gpio_bit_get(SHA_PON007_I_DET_USB_VBUS) == 0 ) {
+		/* VBUS decected */
+		return( SHA_PON_USB_CLIENT );
+	}
+	
+	return( SHA_PON_USB_STOP );	/* normally stop */
+}
+
+
+static void sha_pon007_usbpower(int mode_now)
+{
+	if( mode_now == SHA_PON_USB_HOST) {
+		sha_pon_gpio_bit_set( SHA_PON007_O_USB_POWER, 1 );	/* power ON */
+	} else {
+		sha_pon_gpio_bit_set( SHA_PON007_O_USB_POWER, 0 );	/* power OFF */
+	}
+}
+
+
+static int sha_pon011_usbscan(int mode_now)
+{
+	if( sha_pon_gpio_bit_get(SHA_PON011_I_DET_USB_MiniA) == 0 ) {
+		/* miniA cable inserted */
+		return( SHA_PON_USB_HOST );
+		
+	}
+	return( SHA_PON_USB_CLIENT );	/* normally client */
+}
+
+
+#define	USBSCAN_POLLRATE		msecs_to_jiffies(100)
+#define	USBSCAN_POLLRATE_LONG		msecs_to_jiffies(500)
+#define	USBSCAN_DISCONNECT		msecs_to_jiffies(5000)
+
+static int	sha_pon_usbmode_now, sha_pon_usbmode1, sha_pon_usbmode2, sha_pon_usbmode3;
+static int	(*sha_pon_usbscanfunc)(int mode_now);
+static void	(*sha_pon_usbpowerfunc)(int mode_now);
+
+static void sha_pon_usbport_poll(unsigned long data);
+static struct timer_list usb_timer = {
+	.function	= (void *)sha_pon_usbport_poll,
+	.data		= 0,
+};
+
+
+static void sha_pon_usbport_poll(unsigned long data)
+{
+	sha_pon_usbmode3 = sha_pon_usbmode2;
+	sha_pon_usbmode2 = sha_pon_usbmode1;
+	if( sha_pon_usbscanfunc == NULL ) {
+		sha_pon_usbmode1 = SHA_PON_USB_HOST;	/* normally host */
+	} else {
+		sha_pon_usbmode1 = sha_pon_usbscanfunc(sha_pon_usbmode_now);
+	}
+	
+	if( sha_pon_usbmode1 != sha_pon_usbmode_now &&
+			sha_pon_usbmode1 == sha_pon_usbmode2 &&
+			sha_pon_usbmode1 == sha_pon_usbmode3 ) {
+		/* change mode at 3 times of same status */
+		sha_pon_usbmode_now = sha_pon_usbmode1;
+		
+		printk( KERN_WARNING "(sha_pon_usbport_poll: USB port mode=%s)\n",
+			(sha_pon_usbmode_now==SHA_PON_USB_STOP)?"Stop":
+			(sha_pon_usbmode_now==SHA_PON_USB_HOST)?"Host":
+			(sha_pon_usbmode_now==SHA_PON_USB_CLIENT)?"Client":"not defined");
+		
+		if( sha_pon_usbpowerfunc == NULL ) {
+			printk( KERN_WARNING "(sha_pon_usbport_poll: Not supported USB power control)\n");
+		} else {
+			sha_pon_usbpowerfunc( sha_pon_usbmode_now );
+		}
+		switch( sha_pon_usbmode_now ) {
+		case SHA_PON_USB_HOST:
+			/* enable USB port2, Differencial, Host mode, D+/D- Pulldown */
+			UP2OCR = UP2OCR_HXOE | UP2OCR_HXS | UP2OCR_DMPDE | UP2OCR_DPPDE;
+			break;
+		case SHA_PON_USB_CLIENT:
+			/* enable USB port2, Differencial, Device mode, D+ Pullup(Full speed) */
+			UP2OCR = UP2OCR_HXOE | UP2OCR_DPPUE;
+			break;
+		default:
+			/* disable USB port2 */
+			UP2OCR = 0;
+			break;
+		}
+	}
+	if( sha_pon_usbmode_now == SHA_PON_USB_STOP ) {
+		mod_timer(&usb_timer, jiffies + USBSCAN_POLLRATE_LONG);
+	} else {
+		mod_timer(&usb_timer, jiffies + USBSCAN_POLLRATE);
+	}
+}
+
+
+static void sha_pon_init_usbport(void)
+{
+	/* disable USB port2 */
+	UP2OCR = 0;
+	
+	sha_pon_usbmode_now = SHA_PON_USB_STOP;
+	sha_pon_usbmode1 = SHA_PON_USB_STOP;
+	sha_pon_usbmode2 = SHA_PON_USB_STOP;
+	sha_pon_usbmode3 = SHA_PON_USB_STOP;
+	
+	sha_pon_usbscanfunc = NULL;
+	sha_pon_usbpowerfunc = NULL;
+	if ( machine_is_sha_pon003() || machine_is_sha_pon004() ) {
+		sha_pon_usbscanfunc = sha_pon003_usbscan;
+	}
+	if( machine_is_sha_pon007() ) {
+		sha_pon_usbscanfunc = sha_pon007_usbscan;
+		sha_pon_usbpowerfunc = sha_pon007_usbpower;
+		sha_pon_usbpowerfunc( sha_pon_usbmode_now );
+	}
+	
+	if( machine_is_sha_pon011() ) {
+		sha_pon_usbscanfunc = sha_pon011_usbscan;
+	}
+	
+	init_timer(&usb_timer);
+	mod_timer(&usb_timer, jiffies + USBSCAN_DISCONNECT);
+}
+
 
 /*
  * Keyboard Device
@@ -412,7 +556,7 @@ static struct platform_device sha_pon011ts_device = {
 /* W-SIM */
 static void sha_pon_powerup_wsim(void)
 {
-#if 0	/* comment out, because of 003 hanged. use /sys/class/gpio/*** control. */
+#if 0	/* comment out, because of 003 hanged. use /sys/class/gpio/xxx control. */
 
 	/*power off at sha_pon_init_cpld(). */
 	
@@ -425,10 +569,10 @@ static void sha_pon_powerup_wsim(void)
 }
 
 
-#if UDC
 /* USB gadget */
 static void sha_pon_udc_command(int cmd)
 {
+#if 0	/* replace to sha_pon_init_usbport() */
 	switch( cmd ) {
 	case PXA2XX_UDC_CMD_CONNECT:
 		/* enable USB port2, Differencial, Device mode, D+ Pullup(Full speed) */
@@ -439,12 +583,12 @@ static void sha_pon_udc_command(int cmd)
 		UP2OCR = 0;
 		break;
 	}
+#endif
 }
 
 static struct pxa2xx_udc_mach_info sha_pon_udc_info = {
 	.udc_command = sha_pon_udc_command,
 };
-#endif
 
 
 /* USB Host */
@@ -570,9 +714,7 @@ static void __init sha_pon003_init(void)
 	pxa2xx_mfp_config(ARRAY_AND_SIZE(sha_pon_mfp));
 	sha_pon_init_cpld();
 	set_pxa_fb_info(&sharp_ls037v7dw01);
-#if UDC
 	pxa_set_udc_info(&sha_pon_udc_info);
-#endif
 	pxa_set_ohci_info(&sha_pon_usb_info);
 #if TS
 	sha_pon_ssp_set_machinfo(&sha_pon003ssp_machinfo);
@@ -583,6 +725,7 @@ static void __init sha_pon003_init(void)
 	pxa_set_ac97_info(&sha_pon_audio_info);
 */
 	platform_add_devices(devices003, ARRAY_SIZE(devices003));
+	sha_pon_init_usbport();
 	sha_pon_powerup_wsim();
 }
 
@@ -592,10 +735,7 @@ static void __init sha_pon007_init(void)
 	pxa2xx_mfp_config(ARRAY_AND_SIZE(sha_pon_mfp));
 	sha_pon_init_cpld();
 	set_pxa_fb_info(&sharp_ls028v7pw01);
-
-#if UDC
 	pxa_set_udc_info(&sha_pon_udc_info);
-#endif
 	pxa_set_ohci_info(&sha_pon_usb_info);
 #if TS
 	sha_pon_ssp_set_machinfo(&sha_pon007ssp_machinfo);
@@ -606,6 +746,7 @@ static void __init sha_pon007_init(void)
 	pxa_set_ac97_info(&sha_pon_audio_info);
 */
 	platform_add_devices(devices007, ARRAY_SIZE(devices007));
+	sha_pon_init_usbport();
 	sha_pon_powerup_wsim();
 }
 
@@ -615,9 +756,7 @@ static void __init sha_pon011_init(void)
 	pxa2xx_mfp_config(ARRAY_AND_SIZE(sha_pon_mfp));
 	sha_pon_init_cpld();
 	set_pxa_fb_info(&pxa_sharp_ws011sh);
-#if UDC
 	pxa_set_udc_info(&sha_pon_udc_info);
-#endif
 	pxa_set_ohci_info(&sha_pon_usb_info);
 #if TS
 	sha_pon_ssp_set_machinfo(&sha_pon011ssp_machinfo);
@@ -629,6 +768,7 @@ static void __init sha_pon011_init(void)
 	pxa_set_ficp_info(&sha_pon_irda_info);
 */
 	platform_add_devices(devices011, ARRAY_SIZE(devices011));
+	sha_pon_init_usbport();
 	sha_pon_powerup_wsim();
 }
 
-- 
1.4.4.4

