Support switching of USB mode between host and device port

Index: linux-2.6.20.4/arch/arm/mach-s3c2410/usb-modeswitch.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.20.4/arch/arm/mach-s3c2410/usb-modeswitch.c	2007-04-01 12:36:49.000000000 +0200
@@ -0,0 +1,33 @@
+/*
+ * linux/arch/arm/mach-s3c2410/usb-modeswitch.c
+ *
+ * S3C2410 USB Host/Device switch
+ *
+ * Copyright (C) 2007 by OpenMoko, Inc.
+ * Author: Harald Welte <laforge@openmoko.org>
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ *
+ */
+
+void s3c2410_usb_enable_host(int on)
+{
+	s3c2410_modify_misccr(S3C2410_MISCCR_HSBHOST,
+			      on ? S3C2410_MISCCR_USBHOST : 0);
+}
+
+
Index: linux-2.6.20.4/drivers/usb/host/ohci-s3c2410.c
===================================================================
--- linux-2.6.20.4.orig/drivers/usb/host/ohci-s3c2410.c	2007-04-01 12:33:01.000000000 +0200
+++ linux-2.6.20.4/drivers/usb/host/ohci-s3c2410.c	2007-04-01 12:36:49.000000000 +0200
@@ -24,6 +24,7 @@
 
 #include <asm/hardware.h>
 #include <asm/arch/usb-control.h>
+#include <asm/arch/regs-gpio.h>
 
 #define valid_port(idx) ((idx) == 1 || (idx) == 2)
 
@@ -308,6 +309,36 @@
 	local_irq_restore(flags);
 }
 
+/* switching of USB pads */
+static ssize_t show_usb_mode(struct device *dev, struct device_attribute *attr,
+			     char *buf)
+{
+	if (__raw_readl(S3C24XX_MISCCR) & S3C2410_MISCCR_USBHOST)
+		return sprintf(buf, "host");
+
+	return sprintf(buf, "device");
+}
+
+static ssize_t set_usb_mode(struct device *dev, struct device_attribute *attr,
+			    const char *buf, size_t count)
+{
+	if (!strcmp(buf, "host")) {
+		s3c2410_modify_misccr(S3C2410_MISCCR_USBHOST,
+				      S3C2410_MISCCR_USBHOST);
+		/* FIXME: 
+		 * - call machine-specific disable-pullup function i
+		 * - enable +Vbus (if hardware supports it)
+		 */
+	} else if (!strcmp(buf, "device")) {
+		s3c2410_modify_misccr(S3C2410_MISCCR_USBHOST, 0);
+	} else
+		return -EINVAL;
+
+	return count;
+}
+
+static DEVICE_ATTR(usb_mode, S_IRUGO | S_IWUSR, show_usb_mode, set_usb_mode);
+
 /* may be called without controller electrically present */
 /* may be called with controller, bus, and devices active */
 
@@ -325,6 +356,7 @@
 static void
 usb_hcd_s3c2410_remove (struct usb_hcd *hcd, struct platform_device *dev)
 {
+	device_remove_file(&dev->dev, &dev_attr_usb_mode);
 	usb_remove_hcd(hcd);
 	s3c2410_stop_hc(dev);
 	iounmap(hcd->regs);
@@ -392,8 +424,15 @@
 	if (retval != 0)
 		goto err_ioremap;
 
+	retval = device_create_file(&dev->dev, &dev_attr_usb_mode);
+	if (retval != 0)
+		goto err_hcd;
+
 	return 0;
 
+ err_hcd:
+	usb_remove_hcd(hcd);
+
  err_ioremap:
 	s3c2410_stop_hc(dev);
 	iounmap(hcd->regs);
