From: Tony Lindgren Date: Thu, 1 Feb 2007 22:41:05 +0000 (-0800) Subject: MUSB_HDRC: Allow selecting OTG, peripheral or host mode via sysfs X-Git-Tag: v2.6.21-omap1~13 X-Git-Url: http://pilppa.com/gitweb/?a=commitdiff_plain;h=5d5380ba40e000b539f2729d9511a1d650e637ab;p=linux-2.6-omap-h63xx.git MUSB_HDRC: Allow selecting OTG, peripheral or host mode via sysfs 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 --- diff --git a/drivers/usb/musb/musbdefs.h b/drivers/usb/musb/musbdefs.h index f4be08048f2..f641297356e 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 d208db85ec0..14fbeba2b8f 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 eeaeb164c41..9f01329a6cb 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) {