diff --git a/drivers/usb/musb/musbdefs.h b/drivers/usb/musb/musbdefs.h
index f4be08048f29f212404d415675c840328685adb8..f641297356e5cd1f28c31aeafd09507c87e518de 100644
--- a/drivers/usb/musb/musbdefs.h
+++ b/drivers/usb/musb/musbdefs.h
@@ -506,9 +506,11 @@ extern void musb_platform_disable(struct musb *musb);
 #ifdef CONFIG_USB_TUSB6010
 extern void musb_platform_try_idle(struct musb *musb);
 extern int musb_platform_get_vbus_status(struct musb *musb);
+extern void musb_platform_set_mode(struct musb *musb, u8 musb_mode);
 #else
 #define musb_platform_try_idle(x)		do {} while (0)
 #define musb_platform_get_vbus_status(x)	0
+#define musb_platform_set_mode(x, y)		do {} while (0)
 #endif
 
 extern int __init musb_platform_init(struct musb *musb);
diff --git a/drivers/usb/musb/plat_uds.c b/drivers/usb/musb/plat_uds.c
index d208db85ec067269b9871cdd89ac33120cbdaa26..14fbeba2b8fc97069ff0510d30f3c3e0cb38a6e6 100644
--- a/drivers/usb/musb/plat_uds.c
+++ b/drivers/usb/musb/plat_uds.c
@@ -1511,7 +1511,26 @@ musb_mode_show(struct device *dev, struct device_attribute *attr, char *buf)
 
 	return ret;
 }
-static DEVICE_ATTR(mode, S_IRUGO, musb_mode_show, NULL);
+
+static ssize_t
+musb_mode_store(struct device *dev, struct device_attribute *attr,
+		const char *buf, size_t n)
+{
+	struct musb	*musb = dev_to_musb(dev);
+	unsigned long	flags;
+
+	spin_lock_irqsave(&musb->Lock, flags);
+	if (!strncmp(buf, "host", 4))
+		musb_platform_set_mode(musb, MUSB_HOST);
+	if (!strncmp(buf, "peripheral", 10))
+		musb_platform_set_mode(musb, MUSB_PERIPHERAL);
+	if (!strncmp(buf, "otg", 3))
+		musb_platform_set_mode(musb, MUSB_OTG);
+	spin_unlock_irqrestore(&musb->Lock, flags);
+
+	return n;
+}
+static DEVICE_ATTR(mode, 0644, musb_mode_show, musb_mode_store);
 
 static ssize_t
 musb_cable_show(struct device *dev, struct device_attribute *attr, char *buf)
diff --git a/drivers/usb/musb/tusb6010.c b/drivers/usb/musb/tusb6010.c
index eeaeb164c41d0caef3b161b5890eec75acdfe9fa..9f01329a6cb03a03504ad9761b00bd7a4f9e98ba 100644
--- a/drivers/usb/musb/tusb6010.c
+++ b/drivers/usb/musb/tusb6010.c
@@ -400,6 +400,88 @@ static void tusb_source_power(struct musb *musb, int is_on)
 		conf, prcm);
 }
 
+/*
+ * Sets the mode to OTG, peripheral or host by changing the ID detection.
+ * Caller must take care of locking.
+ *
+ * Note that if a mini-A cable is plugged in the ID line will stay down as
+ * the weak ID pull-up is not able to pull the ID up.
+ *
+ * REVISIT: It would be possible to add support for changing between host
+ * and peripheral modes in non-OTG configurations by reconfiguring hardware
+ * and then setting musb->board_mode. For now, only support OTG mode.
+ */
+void musb_platform_set_mode(struct musb *musb, u8 musb_mode)
+{
+	void __iomem	*base = musb->ctrl_base;
+	u32		otg_stat, phy_otg_ena, phy_otg_ctrl, dev_conf;
+	int		vbus = 0;
+
+	if (musb->board_mode != MUSB_OTG) {
+		ERR("Changing mode currently only supported in OTG mode\n");
+		return;
+	}
+
+	otg_stat = musb_readl(base, TUSB_DEV_OTG_STAT);
+	phy_otg_ena = musb_readl(base, TUSB_PHY_OTG_CTRL_ENABLE);
+	phy_otg_ctrl = musb_readl(base, TUSB_PHY_OTG_CTRL);
+	dev_conf = musb_readl(base, TUSB_DEV_CONF);
+
+	switch (musb_mode) {
+
+#ifdef CONFIG_USB_MUSB_HDRC_HCD
+	case MUSB_HOST:		/* Disable PHY ID detect, ground ID */
+		if (!(otg_stat & TUSB_DEV_OTG_STAT_ID_STATUS)) {
+			ERR("Already in host mode otg_stat: %08x\n", otg_stat);
+			return;
+		}
+		phy_otg_ena |= TUSB_PHY_OTG_CTRL_OTG_ID_PULLUP;
+		phy_otg_ctrl &= ~TUSB_PHY_OTG_CTRL_OTG_ID_PULLUP;
+		dev_conf |= TUSB_DEV_CONF_ID_SEL;
+		dev_conf &= ~TUSB_DEV_CONF_SOFT_ID;
+		vbus = 1;
+		break;
+#endif
+
+#ifdef CONFIG_USB_GADGET_MUSB_HDRC
+	case MUSB_PERIPHERAL:	/* Disable PHY ID detect, keep ID pull-up on */
+		if (otg_stat & TUSB_DEV_OTG_STAT_ID_STATUS) {
+			ERR("Already in peripheral mode otg_stat: %08x\n",
+				otg_stat);
+			return;
+		}
+		phy_otg_ena |= TUSB_PHY_OTG_CTRL_OTG_ID_PULLUP;
+		phy_otg_ctrl |= TUSB_PHY_OTG_CTRL_OTG_ID_PULLUP;
+		dev_conf |= (TUSB_DEV_CONF_ID_SEL | TUSB_DEV_CONF_SOFT_ID);
+		break;
+#endif
+
+#ifdef CONFIG_USB_MUSB_OTG
+	case MUSB_OTG:		/* Use PHY ID detection */
+		phy_otg_ena &= ~TUSB_PHY_OTG_CTRL_OTG_ID_PULLUP;
+		phy_otg_ctrl &= ~TUSB_PHY_OTG_CTRL_OTG_ID_PULLUP;
+		dev_conf &= ~(TUSB_DEV_CONF_ID_SEL | TUSB_DEV_CONF_SOFT_ID);
+		break;
+#endif
+
+	default:
+		DBG(2, "Trying to set unknown mode %i\n", musb_mode);
+	}
+
+	musb_writel(base, TUSB_PHY_OTG_CTRL_ENABLE,
+			TUSB_PHY_OTG_CTRL_WRPROTECT | phy_otg_ena);
+	musb_writel(base, TUSB_PHY_OTG_CTRL,
+			TUSB_PHY_OTG_CTRL_WRPROTECT | phy_otg_ctrl);
+	musb_writel(base, TUSB_DEV_CONF, dev_conf);
+
+	msleep(1);
+	otg_stat = musb_readl(base, TUSB_DEV_OTG_STAT);
+	if ((musb_mode == MUSB_PERIPHERAL) &&
+		!(otg_stat & TUSB_DEV_OTG_STAT_ID_STATUS))
+			ERR("Cannot be peripheral with mini-A cable "
+			"otg_stat: %08x\n", otg_stat);
+}
+
 static inline void
 tusb_otg_ints(struct musb *musb, u32 int_src, void __iomem *base)
 {