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) {