diff options
Diffstat (limited to 'drivers/usb/musb/davinci.c')
| -rw-r--r-- | drivers/usb/musb/davinci.c | 254 | 
1 files changed, 187 insertions, 67 deletions
diff --git a/drivers/usb/musb/davinci.c b/drivers/usb/musb/davinci.c index 6e67629f50c..de8492b06e4 100644 --- a/drivers/usb/musb/davinci.c +++ b/drivers/usb/musb/davinci.c @@ -24,17 +24,18 @@  #include <linux/module.h>  #include <linux/kernel.h>  #include <linux/sched.h> -#include <linux/init.h>  #include <linux/list.h>  #include <linux/delay.h>  #include <linux/clk.h> +#include <linux/err.h>  #include <linux/io.h>  #include <linux/gpio.h> +#include <linux/platform_device.h> +#include <linux/dma-mapping.h> +#include <linux/usb/usb_phy_generic.h> -#include <mach/hardware.h> -#include <mach/memory.h> -#include <mach/gpio.h>  #include <mach/cputype.h> +#include <mach/hardware.h>  #include <asm/mach-types.h> @@ -51,6 +52,12 @@  #define USB_PHY_CTRL	IO_ADDRESS(USBPHY_CTL_PADDR)  #define DM355_DEEPSLEEP	IO_ADDRESS(DM355_DEEPSLEEP_PADDR) +struct davinci_glue { +	struct device		*dev; +	struct platform_device	*musb; +	struct clk		*clk; +}; +  /* REVISIT (PM) we should be able to keep the PHY in low power mode most   * of the time (24 MHZ oscillator and PLL off, etc) by setting POWER.D0   * and, when in host mode, autosuspending idle root ports... PHYPLLON @@ -83,7 +90,7 @@ static inline void phy_off(void)  static int dma_off = 1; -void musb_platform_enable(struct musb *musb) +static void davinci_musb_enable(struct musb *musb)  {  	u32	tmp, old, val; @@ -108,15 +115,14 @@ void musb_platform_enable(struct musb *musb)  		dma_off = 0;  	/* force a DRVVBUS irq so we can start polling for ID change */ -	if (is_otg_enabled(musb)) -		musb_writel(musb->ctrl_base, DAVINCI_USB_INT_SET_REG, +	musb_writel(musb->ctrl_base, DAVINCI_USB_INT_SET_REG,  			DAVINCI_INTR_DRVVBUS << DAVINCI_USB_USBINT_SHIFT);  }  /*   * Disable the HDRC and flush interrupts   */ -void musb_platform_disable(struct musb *musb) +static void davinci_musb_disable(struct musb *musb)  {  	/* because we don't set CTRLR.UINT, "important" to:  	 *  - not read/write INTRUSB/INTRUSBE @@ -135,12 +141,7 @@ void musb_platform_disable(struct musb *musb)  } -#ifdef CONFIG_USB_MUSB_HDRC_HCD  #define	portstate(stmt)		stmt -#else -#define	portstate(stmt) -#endif -  /*   * VBUS SWITCHING IS BOARD-SPECIFIC ... at least for the DM6446 EVM, @@ -167,7 +168,7 @@ static void evm_deferred_drvvbus(struct work_struct *ignored)  #endif	/* EVM */ -static void davinci_source_power(struct musb *musb, int is_on, int immediate) +static void davinci_musb_source_power(struct musb *musb, int is_on, int immediate)  {  #ifdef CONFIG_MACH_DAVINCI_EVM  	if (is_on) @@ -190,10 +191,10 @@ static void davinci_source_power(struct musb *musb, int is_on, int immediate)  #endif  } -static void davinci_set_vbus(struct musb *musb, int is_on) +static void davinci_musb_set_vbus(struct musb *musb, int is_on)  {  	WARN_ON(is_on && is_peripheral_active(musb)); -	davinci_source_power(musb, is_on, 0); +	davinci_musb_source_power(musb, is_on, 0);  } @@ -212,7 +213,8 @@ static void otg_timer(unsigned long _musb)  	* status change events (from the transceiver) otherwise.  	 */  	devctl = musb_readb(mregs, MUSB_DEVCTL); -	DBG(7, "poll devctl %02x (%s)\n", devctl, otg_state_string(musb)); +	dev_dbg(musb->controller, "poll devctl %02x (%s)\n", devctl, +		usb_otg_state_string(musb->xceiv->state));  	spin_lock_irqsave(&musb->lock, flags);  	switch (musb->xceiv->state) { @@ -231,10 +233,8 @@ static void otg_timer(unsigned long _musb)  			MUSB_INTR_VBUSERROR << DAVINCI_USB_USBINT_SHIFT);  		break;  	case OTG_STATE_B_IDLE: -		if (!is_peripheral_enabled(musb)) -			break; - -		/* There's no ID-changed IRQ, so we have no good way to tell +		/* +		 * There's no ID-changed IRQ, so we have no good way to tell  		 * when to switch to the A-Default state machine (by setting  		 * the DEVCTL.SESSION flag).  		 * @@ -259,11 +259,12 @@ static void otg_timer(unsigned long _musb)  	spin_unlock_irqrestore(&musb->lock, flags);  } -static irqreturn_t davinci_interrupt(int irq, void *__hci) +static irqreturn_t davinci_musb_interrupt(int irq, void *__hci)  {  	unsigned long	flags;  	irqreturn_t	retval = IRQ_NONE;  	struct musb	*musb = __hci; +	struct usb_otg	*otg = musb->xceiv->otg;  	void __iomem	*tibase = musb->ctrl_base;  	struct cppi	*cppi;  	u32		tmp; @@ -289,7 +290,7 @@ static irqreturn_t davinci_interrupt(int irq, void *__hci)  	/* ack and handle non-CPPI interrupts */  	tmp = musb_readl(tibase, DAVINCI_USB_INT_SRC_MASKED_REG);  	musb_writel(tibase, DAVINCI_USB_INT_SRC_CLR_REG, tmp); -	DBG(4, "IRQ %08x\n", tmp); +	dev_dbg(musb->controller, "IRQ %08x\n", tmp);  	musb->int_rx = (tmp & DAVINCI_USB_RXINT_MASK)  			>> DAVINCI_USB_RXINT_SHIFT; @@ -311,8 +312,7 @@ static irqreturn_t davinci_interrupt(int irq, void *__hci)  		u8	devctl = musb_readb(mregs, MUSB_DEVCTL);  		int	err = musb->int_usb & MUSB_INTR_VBUSERROR; -		err = is_host_enabled(musb) -				&& (musb->int_usb & MUSB_INTR_VBUSERROR); +		err = musb->int_usb & MUSB_INTR_VBUSERROR;  		if (err) {  			/* The Mentor core doesn't debounce VBUS as needed  			 * to cope with device connect current spikes. This @@ -328,16 +328,16 @@ static irqreturn_t davinci_interrupt(int irq, void *__hci)  			musb->xceiv->state = OTG_STATE_A_WAIT_VFALL;  			mod_timer(&otg_workaround, jiffies + POLL_SECONDS * HZ);  			WARNING("VBUS error workaround (delay coming)\n"); -		} else if (is_host_enabled(musb) && drvvbus) { +		} else if (drvvbus) {  			MUSB_HST_MODE(musb); -			musb->xceiv->default_a = 1; +			otg->default_a = 1;  			musb->xceiv->state = OTG_STATE_A_WAIT_VRISE;  			portstate(musb->port1_status |= USB_PORT_STAT_POWER);  			del_timer(&otg_workaround);  		} else {  			musb->is_active = 0;  			MUSB_DEV_MODE(musb); -			musb->xceiv->default_a = 0; +			otg->default_a = 0;  			musb->xceiv->state = OTG_STATE_B_IDLE;  			portstate(musb->port1_status &= ~USB_PORT_STAT_POWER);  		} @@ -345,10 +345,10 @@ static irqreturn_t davinci_interrupt(int irq, void *__hci)  		/* NOTE:  this must complete poweron within 100 msec  		 * (OTG_TIME_A_WAIT_VRISE) but we don't check for that.  		 */ -		davinci_source_power(musb, drvvbus, 0); -		DBG(2, "VBUS %s (%s)%s, devctl %02x\n", +		davinci_musb_source_power(musb, drvvbus, 0); +		dev_dbg(musb->controller, "VBUS %s (%s)%s, devctl %02x\n",  				drvvbus ? "on" : "off", -				otg_state_string(musb), +				usb_otg_state_string(musb->xceiv->state),  				err ? " ERROR" : "",  				devctl);  		retval = IRQ_HANDLED; @@ -361,8 +361,7 @@ static irqreturn_t davinci_interrupt(int irq, void *__hci)  	musb_writel(tibase, DAVINCI_USB_EOI_REG, 0);  	/* poll for ID change */ -	if (is_otg_enabled(musb) -			&& musb->xceiv->state == OTG_STATE_B_IDLE) +	if (musb->xceiv->state == OTG_STATE_B_IDLE)  		mod_timer(&otg_workaround, jiffies + POLL_SECONDS * HZ);  	spin_unlock_irqrestore(&musb->lock, flags); @@ -370,36 +369,34 @@ static irqreturn_t davinci_interrupt(int irq, void *__hci)  	return retval;  } -int musb_platform_set_mode(struct musb *musb, u8 mode) +static int davinci_musb_set_mode(struct musb *musb, u8 mode)  {  	/* EVM can't do this (right?) */  	return -EIO;  } -int __init musb_platform_init(struct musb *musb, void *board_data) +static int davinci_musb_init(struct musb *musb)  {  	void __iomem	*tibase = musb->ctrl_base;  	u32		revision; +	int 		ret = -ENODEV; -	usb_nop_xceiv_register(); -	musb->xceiv = otg_get_transceiver(); -	if (!musb->xceiv) -		return -ENODEV; +	musb->xceiv = usb_get_phy(USB_PHY_TYPE_USB2); +	if (IS_ERR_OR_NULL(musb->xceiv)) { +		ret = -EPROBE_DEFER; +		goto unregister; +	}  	musb->mregs += DAVINCI_BASE_OFFSET; -	clk_enable(musb->clock); -  	/* returns zero if e.g. not clocked */  	revision = musb_readl(tibase, DAVINCI_USB_VERSION_REG);  	if (revision == 0)  		goto fail; -	if (is_host_enabled(musb)) -		setup_timer(&otg_workaround, otg_timer, (unsigned long) musb); +	setup_timer(&otg_workaround, otg_timer, (unsigned long) musb); -	musb->board_set_vbus = davinci_set_vbus; -	davinci_source_power(musb, 0, 1); +	davinci_musb_source_power(musb, 0, 1);  	/* dm355 EVM swaps D+/D- for signal integrity, and  	 * is clocked from the main 24 MHz crystal. @@ -418,12 +415,7 @@ int __init musb_platform_init(struct musb *musb, void *board_data)  	if (cpu_is_davinci_dm355()) {  		u32	deepsleep = __raw_readl(DM355_DEEPSLEEP); -		if (is_host_enabled(musb)) { -			deepsleep &= ~DRVVBUS_OVERRIDE; -		} else { -			deepsleep &= ~DRVVBUS_FORCE; -			deepsleep |= DRVVBUS_OVERRIDE; -		} +		deepsleep &= ~DRVVBUS_FORCE;  		__raw_writel(deepsleep, DM355_DEEPSLEEP);  	} @@ -440,21 +432,19 @@ int __init musb_platform_init(struct musb *musb, void *board_data)  		revision, __raw_readl(USB_PHY_CTRL),  		musb_readb(tibase, DAVINCI_USB_CTRL_REG)); -	musb->isr = davinci_interrupt; +	musb->isr = davinci_musb_interrupt;  	return 0;  fail: -	clk_disable(musb->clock); - -	otg_put_transceiver(musb->xceiv); -	usb_nop_xceiv_unregister(); -	return -ENODEV; +	usb_put_phy(musb->xceiv); +unregister: +	usb_phy_generic_unregister(); +	return ret;  } -int musb_platform_exit(struct musb *musb) +static int davinci_musb_exit(struct musb *musb)  { -	if (is_host_enabled(musb)) -		del_timer_sync(&otg_workaround); +	del_timer_sync(&otg_workaround);  	/* force VBUS off */  	if (cpu_is_davinci_dm355()) { @@ -465,10 +455,10 @@ int musb_platform_exit(struct musb *musb)  		__raw_writel(deepsleep, DM355_DEEPSLEEP);  	} -	davinci_source_power(musb, 0 /*off*/, 1); +	davinci_musb_source_power(musb, 0 /*off*/, 1);  	/* delay, to avoid problems with module reload */ -	if (is_host_enabled(musb) && musb->xceiv->default_a) { +	if (musb->xceiv->otg->default_a) {  		int	maxdelay = 30;  		u8	devctl, warn = 0; @@ -481,7 +471,7 @@ int musb_platform_exit(struct musb *musb)  				break;  			if ((devctl & MUSB_DEVCTL_VBUS) != warn) {  				warn = devctl & MUSB_DEVCTL_VBUS; -				DBG(1, "VBUS %d\n", +				dev_dbg(musb->controller, "VBUS %d\n",  					warn >> MUSB_DEVCTL_VBUS_SHIFT);  			}  			msleep(1000); @@ -490,15 +480,145 @@ int musb_platform_exit(struct musb *musb)  		/* in OTG mode, another host might be connected */  		if (devctl & MUSB_DEVCTL_VBUS) -			DBG(1, "VBUS off timeout (devctl %02x)\n", devctl); +			dev_dbg(musb->controller, "VBUS off timeout (devctl %02x)\n", devctl);  	}  	phy_off(); -	clk_disable(musb->clock); +	usb_put_phy(musb->xceiv); + +	return 0; +} + +static const struct musb_platform_ops davinci_ops = { +	.init		= davinci_musb_init, +	.exit		= davinci_musb_exit, + +	.enable		= davinci_musb_enable, +	.disable	= davinci_musb_disable, + +	.set_mode	= davinci_musb_set_mode, + +	.set_vbus	= davinci_musb_set_vbus, +}; + +static const struct platform_device_info davinci_dev_info = { +	.name		= "musb-hdrc", +	.id		= PLATFORM_DEVID_AUTO, +	.dma_mask	= DMA_BIT_MASK(32), +}; + +static int davinci_probe(struct platform_device *pdev) +{ +	struct resource			musb_resources[3]; +	struct musb_hdrc_platform_data	*pdata = dev_get_platdata(&pdev->dev); +	struct platform_device		*musb; +	struct davinci_glue		*glue; +	struct platform_device_info	pinfo; +	struct clk			*clk; + +	int				ret = -ENOMEM; + +	glue = kzalloc(sizeof(*glue), GFP_KERNEL); +	if (!glue) { +		dev_err(&pdev->dev, "failed to allocate glue context\n"); +		goto err0; +	} + +	clk = clk_get(&pdev->dev, "usb"); +	if (IS_ERR(clk)) { +		dev_err(&pdev->dev, "failed to get clock\n"); +		ret = PTR_ERR(clk); +		goto err3; +	} + +	ret = clk_enable(clk); +	if (ret) { +		dev_err(&pdev->dev, "failed to enable clock\n"); +		goto err4; +	} + +	glue->dev			= &pdev->dev; +	glue->clk			= clk; + +	pdata->platform_ops		= &davinci_ops; + +	usb_phy_generic_register(); +	platform_set_drvdata(pdev, glue); + +	memset(musb_resources, 0x00, sizeof(*musb_resources) * +			ARRAY_SIZE(musb_resources)); + +	musb_resources[0].name = pdev->resource[0].name; +	musb_resources[0].start = pdev->resource[0].start; +	musb_resources[0].end = pdev->resource[0].end; +	musb_resources[0].flags = pdev->resource[0].flags; + +	musb_resources[1].name = pdev->resource[1].name; +	musb_resources[1].start = pdev->resource[1].start; +	musb_resources[1].end = pdev->resource[1].end; +	musb_resources[1].flags = pdev->resource[1].flags; + +	/* +	 * For DM6467 3 resources are passed. A placeholder for the 3rd +	 * resource is always there, so it's safe to always copy it... +	 */ +	musb_resources[2].name = pdev->resource[2].name; +	musb_resources[2].start = pdev->resource[2].start; +	musb_resources[2].end = pdev->resource[2].end; +	musb_resources[2].flags = pdev->resource[2].flags; + +	pinfo = davinci_dev_info; +	pinfo.parent = &pdev->dev; +	pinfo.res = musb_resources; +	pinfo.num_res = ARRAY_SIZE(musb_resources); +	pinfo.data = pdata; +	pinfo.size_data = sizeof(*pdata); + +	glue->musb = musb = platform_device_register_full(&pinfo); +	if (IS_ERR(musb)) { +		ret = PTR_ERR(musb); +		dev_err(&pdev->dev, "failed to register musb device: %d\n", ret); +		goto err5; +	} + +	return 0; + +err5: +	clk_disable(clk); + +err4: +	clk_put(clk); -	otg_put_transceiver(musb->xceiv); -	usb_nop_xceiv_unregister(); +err3: +	kfree(glue); + +err0: +	return ret; +} + +static int davinci_remove(struct platform_device *pdev) +{ +	struct davinci_glue		*glue = platform_get_drvdata(pdev); + +	platform_device_unregister(glue->musb); +	usb_phy_generic_unregister(); +	clk_disable(glue->clk); +	clk_put(glue->clk); +	kfree(glue);  	return 0;  } + +static struct platform_driver davinci_driver = { +	.probe		= davinci_probe, +	.remove		= davinci_remove, +	.driver		= { +		.name	= "musb-davinci", +	}, +}; + +MODULE_DESCRIPTION("DaVinci MUSB Glue Layer"); +MODULE_AUTHOR("Felipe Balbi <balbi@ti.com>"); +MODULE_LICENSE("GPL v2"); +module_platform_driver(davinci_driver);  | 
