]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/commitdiff
MUSB_HDRC: Allow selecting OTG, peripheral or host mode via sysfs
authorTony Lindgren <tony@atomide.com>
Thu, 1 Feb 2007 22:41:05 +0000 (14:41 -0800)
committerTony Lindgren <tony@atomide.com>
Fri, 4 May 2007 16:54:22 +0000 (09:54 -0700)
This can be used on systems that don't use ID pin, or have mini-B
connector.

For example, to force N800 into host mode with non-standard mini-B
to mini-B cable connected to a powered hub:

Note that connected mini-A cable cannot be forced to peripheral
mode as the ID pin is grounded by the cable. Also note that
any VBUS load above the 100mA will cause the host mode to fail.

Signed-off-by: Tony Lindgren <tony@atomide.com>
drivers/usb/musb/musbdefs.h
drivers/usb/musb/plat_uds.c
drivers/usb/musb/tusb6010.c

index f4be08048f29f212404d415675c840328685adb8..f641297356e5cd1f28c31aeafd09507c87e518de 100644 (file)
@@ -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);
index d208db85ec067269b9871cdd89ac33120cbdaa26..14fbeba2b8fc97069ff0510d30f3c3e0cb38a6e6 100644 (file)
@@ -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)
index eeaeb164c41d0caef3b161b5890eec75acdfe9fa..9f01329a6cb03a03504ad9761b00bd7a4f9e98ba 100644 (file)
@@ -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)
 {