diff options
Diffstat (limited to 'drivers/usb/musb/ux500.c')
| -rw-r--r-- | drivers/usb/musb/ux500.c | 106 | 
1 files changed, 106 insertions, 0 deletions
diff --git a/drivers/usb/musb/ux500.c b/drivers/usb/musb/ux500.c index 13a39291376..2c80004e0a8 100644 --- a/drivers/usb/musb/ux500.c +++ b/drivers/usb/musb/ux500.c @@ -26,6 +26,7 @@  #include <linux/err.h>  #include <linux/io.h>  #include <linux/platform_device.h> +#include <linux/usb/musb-ux500.h>  #include "musb_core.h" @@ -36,6 +37,98 @@ struct ux500_glue {  };  #define glue_to_musb(g)	platform_get_drvdata(g->musb) +static void ux500_musb_set_vbus(struct musb *musb, int is_on) +{ +	u8            devctl; +	unsigned long timeout = jiffies + msecs_to_jiffies(1000); +	/* HDRC controls CPEN, but beware current surges during device +	 * connect.  They can trigger transient overcurrent conditions +	 * that must be ignored. +	 */ + +	devctl = musb_readb(musb->mregs, MUSB_DEVCTL); + +	if (is_on) { +		if (musb->xceiv->state == OTG_STATE_A_IDLE) { +			/* start the session */ +			devctl |= MUSB_DEVCTL_SESSION; +			musb_writeb(musb->mregs, MUSB_DEVCTL, devctl); +			/* +			 * Wait for the musb to set as A device to enable the +			 * VBUS +			 */ +			while (musb_readb(musb->mregs, MUSB_DEVCTL) & 0x80) { + +				if (time_after(jiffies, timeout)) { +					dev_err(musb->controller, +					"configured as A device timeout"); +					break; +				} +			} + +		} else { +			musb->is_active = 1; +			musb->xceiv->otg->default_a = 1; +			musb->xceiv->state = OTG_STATE_A_WAIT_VRISE; +			devctl |= MUSB_DEVCTL_SESSION; +			MUSB_HST_MODE(musb); +		} +	} else { +		musb->is_active = 0; + +		/* NOTE: we're skipping A_WAIT_VFALL -> A_IDLE and jumping +		 * right to B_IDLE... +		 */ +		musb->xceiv->otg->default_a = 0; +		devctl &= ~MUSB_DEVCTL_SESSION; +		MUSB_DEV_MODE(musb); +	} +	musb_writeb(musb->mregs, MUSB_DEVCTL, devctl); + +	/* +	 * Devctl values will be updated after vbus goes below +	 * session_valid. The time taken depends on the capacitance +	 * on VBUS line. The max discharge time can be upto 1 sec +	 * as per the spec. Typically on our platform, it is 200ms +	 */ +	if (!is_on) +		mdelay(200); + +	dev_dbg(musb->controller, "VBUS %s, devctl %02x\n", +		usb_otg_state_string(musb->xceiv->state), +		musb_readb(musb->mregs, MUSB_DEVCTL)); +} + +static int musb_otg_notifications(struct notifier_block *nb, +		unsigned long event, void *unused) +{ +	struct musb *musb = container_of(nb, struct musb, nb); + +	dev_dbg(musb->controller, "musb_otg_notifications %ld %s\n", +			event, usb_otg_state_string(musb->xceiv->state)); + +	switch (event) { +	case UX500_MUSB_ID: +		dev_dbg(musb->controller, "ID GND\n"); +		ux500_musb_set_vbus(musb, 1); +		break; +	case UX500_MUSB_VBUS: +		dev_dbg(musb->controller, "VBUS Connect\n"); +		break; +	case UX500_MUSB_NONE: +		dev_dbg(musb->controller, "VBUS Disconnect\n"); +		if (is_host_active(musb)) +			ux500_musb_set_vbus(musb, 0); +		else +			musb->xceiv->state = OTG_STATE_B_IDLE; +		break; +	default: +		dev_dbg(musb->controller, "ID float\n"); +		return NOTIFY_DONE; +	} +	return NOTIFY_OK; +} +  static irqreturn_t ux500_musb_interrupt(int irq, void *__hci)  {  	unsigned long   flags; @@ -58,12 +151,21 @@ static irqreturn_t ux500_musb_interrupt(int irq, void *__hci)  static int ux500_musb_init(struct musb *musb)  { +	int status; +  	musb->xceiv = usb_get_phy(USB_PHY_TYPE_USB2);  	if (IS_ERR_OR_NULL(musb->xceiv)) {  		pr_err("HS USB OTG: no transceiver configured\n");  		return -EPROBE_DEFER;  	} +	musb->nb.notifier_call = musb_otg_notifications; +	status = usb_register_notifier(musb->xceiv, &musb->nb); +	if (status < 0) { +		dev_dbg(musb->controller, "notification register failed\n"); +		return status; +	} +  	musb->isr = ux500_musb_interrupt;  	return 0; @@ -71,6 +173,8 @@ static int ux500_musb_init(struct musb *musb)  static int ux500_musb_exit(struct musb *musb)  { +	usb_unregister_notifier(musb->xceiv, &musb->nb); +  	usb_put_phy(musb->xceiv);  	return 0; @@ -79,6 +183,8 @@ static int ux500_musb_exit(struct musb *musb)  static const struct musb_platform_ops ux500_ops = {  	.init		= ux500_musb_init,  	.exit		= ux500_musb_exit, + +	.set_vbus	= ux500_musb_set_vbus,  };  static int ux500_probe(struct platform_device *pdev)  |