diff options
| author | Greg Kroah-Hartman <gregkh@suse.de> | 2010-12-16 10:05:06 -0800 | 
|---|---|---|
| committer | Greg Kroah-Hartman <gregkh@suse.de> | 2010-12-16 10:05:06 -0800 | 
| commit | 36facadd9ea98f8415d0dbb63e0763b7ee9d3911 (patch) | |
| tree | 99dea00b332ed852f2b0a4923b581dd723f03634 /drivers/usb/musb/am35x.c | |
| parent | 2faa83e2a519abea1055d156ce1b42b8fa57e87b (diff) | |
| parent | 0b83ae960cd7d4a5ee02786ecf41ab45688999bf (diff) | |
| download | olio-linux-3.10-36facadd9ea98f8415d0dbb63e0763b7ee9d3911.tar.xz olio-linux-3.10-36facadd9ea98f8415d0dbb63e0763b7ee9d3911.zip  | |
Merge branch 'usb-next' into musb-merge
* usb-next: (132 commits)
  USB: uas: Use GFP_NOIO instead of GFP_KERNEL in I/O submission path
  USB: uas: Ensure we only bind to a UAS interface
  USB: uas: Rename sense pipe and sense urb to status pipe and status urb
  USB: uas: Use kzalloc instead of kmalloc
  USB: uas: Fix up the Sense IU
  usb: musb: core: kill unneeded #include's
  DA8xx: assign name to MUSB IRQ resource
  usb: gadget: g_ncm added
  usb: gadget: f_ncm.c added
  usb: gadget: u_ether: prepare for NCM
  usb: pch_udc: Fix setup transfers with data out
  usb: pch_udc: Fix compile error, warnings and checkpatch warnings
  usb: add ab8500 usb transceiver driver
  USB: gadget: Implement runtime PM for MSM bus glue driver
  USB: gadget: Implement runtime PM for ci13xxx gadget
  USB: gadget: Add USB controller driver for MSM SoC
  USB: gadget: Introduce ci13xxx_udc_driver struct
  USB: gadget: Initialize ci13xxx gadget device's coherent DMA mask
  USB: gadget: Fix "scheduling while atomic" bugs in ci13xxx_udc
  USB: gadget: Separate out PCI bus code from ci13xxx_udc
  ...
Diffstat (limited to 'drivers/usb/musb/am35x.c')
| -rw-r--r-- | drivers/usb/musb/am35x.c | 410 | 
1 files changed, 270 insertions, 140 deletions
diff --git a/drivers/usb/musb/am35x.c b/drivers/usb/musb/am35x.c index b0aabf3a606..d5a3da37c90 100644 --- a/drivers/usb/musb/am35x.c +++ b/drivers/usb/musb/am35x.c @@ -29,8 +29,9 @@  #include <linux/init.h>  #include <linux/clk.h>  #include <linux/io.h> +#include <linux/platform_device.h> +#include <linux/dma-mapping.h> -#include <plat/control.h>  #include <plat/usb.h>  #include "musb_core.h" @@ -80,51 +81,18 @@  #define USB_MENTOR_CORE_OFFSET	0x400 -static inline void phy_on(void) -{ -	unsigned long timeout = jiffies + msecs_to_jiffies(100); -	u32 devconf2; - -	/* -	 * Start the on-chip PHY and its PLL. -	 */ -	devconf2 = omap_ctrl_readl(AM35XX_CONTROL_DEVCONF2); - -	devconf2 &= ~(CONF2_RESET | CONF2_PHYPWRDN | CONF2_OTGPWRDN); -	devconf2 |= CONF2_PHY_PLLON; - -	omap_ctrl_writel(devconf2, AM35XX_CONTROL_DEVCONF2); - -	DBG(1, "Waiting for PHY clock good...\n"); -	while (!(omap_ctrl_readl(AM35XX_CONTROL_DEVCONF2) -			& CONF2_PHYCLKGD)) { -		cpu_relax(); - -		if (time_after(jiffies, timeout)) { -			DBG(1, "musb PHY clock good timed out\n"); -			break; -		} -	} -} - -static inline void phy_off(void) -{ -	u32 devconf2; - -	/* -	 * Power down the on-chip PHY. -	 */ -	devconf2 = omap_ctrl_readl(AM35XX_CONTROL_DEVCONF2); - -	devconf2 &= ~CONF2_PHY_PLLON; -	devconf2 |=  CONF2_PHYPWRDN | CONF2_OTGPWRDN; -	omap_ctrl_writel(devconf2, AM35XX_CONTROL_DEVCONF2); -} +struct am35x_glue { +	struct device		*dev; +	struct platform_device	*musb; +	struct clk		*phy_clk; +	struct clk		*clk; +}; +#define glue_to_musb(g)		platform_get_drvdata(g->musb)  /* - * musb_platform_enable - enable interrupts + * am35x_musb_enable - enable interrupts   */ -void musb_platform_enable(struct musb *musb) +static void am35x_musb_enable(struct musb *musb)  {  	void __iomem *reg_base = musb->ctrl_base;  	u32 epmask; @@ -143,9 +111,9 @@ void musb_platform_enable(struct musb *musb)  }  /* - * musb_platform_disable - disable HDRC and flush interrupts + * am35x_musb_disable - disable HDRC and flush interrupts   */ -void musb_platform_disable(struct musb *musb) +static void am35x_musb_disable(struct musb *musb)  {  	void __iomem *reg_base = musb->ctrl_base; @@ -162,7 +130,7 @@ void musb_platform_disable(struct musb *musb)  #define portstate(stmt)  #endif -static void am35x_set_vbus(struct musb *musb, int is_on) +static void am35x_musb_set_vbus(struct musb *musb, int is_on)  {  	WARN_ON(is_on && is_peripheral_active(musb));  } @@ -221,7 +189,7 @@ static void otg_timer(unsigned long _musb)  	spin_unlock_irqrestore(&musb->lock, flags);  } -void musb_platform_try_idle(struct musb *musb, unsigned long timeout) +static void am35x_musb_try_idle(struct musb *musb, unsigned long timeout)  {  	static unsigned long last_timer; @@ -251,13 +219,16 @@ void musb_platform_try_idle(struct musb *musb, unsigned long timeout)  	mod_timer(&otg_workaround, timeout);  } -static irqreturn_t am35x_interrupt(int irq, void *hci) +static irqreturn_t am35x_musb_interrupt(int irq, void *hci)  {  	struct musb  *musb = hci;  	void __iomem *reg_base = musb->ctrl_base; +	struct device *dev = musb->controller; +	struct musb_hdrc_platform_data *plat = dev->platform_data; +	struct omap_musb_board_data *data = plat->board_data;  	unsigned long flags;  	irqreturn_t ret = IRQ_NONE; -	u32 epintr, usbintr, lvl_intr; +	u32 epintr, usbintr;  	spin_lock_irqsave(&musb->lock, flags); @@ -346,9 +317,8 @@ eoi:  	/* EOI needs to be written for the IRQ to be re-asserted. */  	if (ret == IRQ_HANDLED || epintr || usbintr) {  		/* clear level interrupt */ -		lvl_intr = omap_ctrl_readl(AM35XX_CONTROL_LVL_INTR_CLEAR); -		lvl_intr |= AM35XX_USBOTGSS_INT_CLR; -		omap_ctrl_writel(lvl_intr, AM35XX_CONTROL_LVL_INTR_CLEAR); +		if (data->clear_irq) +			data->clear_irq();  		/* write EOI */  		musb_writel(reg_base, USB_END_OF_INTR_REG, 0);  	} @@ -362,137 +332,85 @@ eoi:  	return ret;  } -int musb_platform_set_mode(struct musb *musb, u8 musb_mode) +static int am35x_musb_set_mode(struct musb *musb, u8 musb_mode)  { -	u32 devconf2 = omap_ctrl_readl(AM35XX_CONTROL_DEVCONF2); +	struct device *dev = musb->controller; +	struct musb_hdrc_platform_data *plat = dev->platform_data; +	struct omap_musb_board_data *data = plat->board_data; +	int     retval = 0; -	devconf2 &= ~CONF2_OTGMODE; -	switch (musb_mode) { -#ifdef	CONFIG_USB_MUSB_HDRC_HCD -	case MUSB_HOST:		/* Force VBUS valid, ID = 0 */ -		devconf2 |= CONF2_FORCE_HOST; -		break; -#endif -#ifdef	CONFIG_USB_GADGET_MUSB_HDRC -	case MUSB_PERIPHERAL:	/* Force VBUS valid, ID = 1 */ -		devconf2 |= CONF2_FORCE_DEVICE; -		break; -#endif -#ifdef	CONFIG_USB_MUSB_OTG -	case MUSB_OTG:		/* Don't override the VBUS/ID comparators */ -		devconf2 |= CONF2_NO_OVERRIDE; -		break; -#endif -	default: -		DBG(2, "Trying to set unsupported mode %u\n", musb_mode); -	} +	if (data->set_mode) +		data->set_mode(musb_mode); +	else +		retval = -EIO; -	omap_ctrl_writel(devconf2, AM35XX_CONTROL_DEVCONF2); -	return 0; +	return retval;  } -int __init musb_platform_init(struct musb *musb, void *board_data) +static int am35x_musb_init(struct musb *musb)  { +	struct device *dev = musb->controller; +	struct musb_hdrc_platform_data *plat = dev->platform_data; +	struct omap_musb_board_data *data = plat->board_data;  	void __iomem *reg_base = musb->ctrl_base; -	u32 rev, lvl_intr, sw_reset; -	int status; +	u32 rev;  	musb->mregs += USB_MENTOR_CORE_OFFSET; -	clk_enable(musb->clock); -	DBG(2, "musb->clock=%lud\n", clk_get_rate(musb->clock)); - -	musb->phy_clock = clk_get(musb->controller, "fck"); -	if (IS_ERR(musb->phy_clock)) { -		status = PTR_ERR(musb->phy_clock); -		goto exit0; -	} -	clk_enable(musb->phy_clock); -	DBG(2, "musb->phy_clock=%lud\n", clk_get_rate(musb->phy_clock)); -  	/* Returns zero if e.g. not clocked */  	rev = musb_readl(reg_base, USB_REVISION_REG); -	if (!rev) { -		status = -ENODEV; -		goto exit1; -	} +	if (!rev) +		return -ENODEV;  	usb_nop_xceiv_register();  	musb->xceiv = otg_get_transceiver(); -	if (!musb->xceiv) { -		status = -ENODEV; -		goto exit1; -	} +	if (!musb->xceiv) +		return -ENODEV;  	if (is_host_enabled(musb))  		setup_timer(&otg_workaround, otg_timer, (unsigned long) musb); -	musb->board_set_vbus = am35x_set_vbus; - -	/* Global reset */ -	sw_reset = omap_ctrl_readl(AM35XX_CONTROL_IP_SW_RESET); - -	sw_reset |= AM35XX_USBOTGSS_SW_RST; -	omap_ctrl_writel(sw_reset, AM35XX_CONTROL_IP_SW_RESET); - -	sw_reset &= ~AM35XX_USBOTGSS_SW_RST; -	omap_ctrl_writel(sw_reset, AM35XX_CONTROL_IP_SW_RESET); +	/* Reset the musb */ +	if (data->reset) +		data->reset();  	/* Reset the controller */  	musb_writel(reg_base, USB_CTRL_REG, AM35X_SOFT_RESET_MASK);  	/* Start the on-chip PHY and its PLL. */ -	phy_on(); +	if (data->set_phy_power) +		data->set_phy_power(1);  	msleep(5); -	musb->isr = am35x_interrupt; +	musb->isr = am35x_musb_interrupt;  	/* clear level interrupt */ -	lvl_intr = omap_ctrl_readl(AM35XX_CONTROL_LVL_INTR_CLEAR); -	lvl_intr |= AM35XX_USBOTGSS_INT_CLR; -	omap_ctrl_writel(lvl_intr, AM35XX_CONTROL_LVL_INTR_CLEAR); +	if (data->clear_irq) +		data->clear_irq(); +  	return 0; -exit1: -	clk_disable(musb->phy_clock); -	clk_put(musb->phy_clock); -exit0: -	clk_disable(musb->clock); -	return status;  } -int musb_platform_exit(struct musb *musb) +static int am35x_musb_exit(struct musb *musb)  { +	struct device *dev = musb->controller; +	struct musb_hdrc_platform_data *plat = dev->platform_data; +	struct omap_musb_board_data *data = plat->board_data; +  	if (is_host_enabled(musb))  		del_timer_sync(&otg_workaround); -	phy_off(); +	/* Shutdown the on-chip PHY and its PLL. */ +	if (data->set_phy_power) +		data->set_phy_power(0);  	otg_put_transceiver(musb->xceiv);  	usb_nop_xceiv_unregister(); -	clk_disable(musb->clock); - -	clk_disable(musb->phy_clock); -	clk_put(musb->phy_clock); -  	return 0;  } -#ifdef CONFIG_PM -void musb_platform_save_context(struct musb *musb, -	struct musb_context_registers *musb_context) -{ -	phy_off(); -} - -void musb_platform_restore_context(struct musb *musb, -	struct musb_context_registers *musb_context) -{ -	phy_on(); -} -#endif -  /* AM35x supports only 32bit read operation */  void musb_read_fifo(struct musb_hw_ep *hw_ep, u16 len, u8 *dst)  { @@ -522,3 +440,215 @@ void musb_read_fifo(struct musb_hw_ep *hw_ep, u16 len, u8 *dst)  		memcpy(dst, &val, len);  	}  } + +static const struct musb_platform_ops am35x_ops = { +	.init		= am35x_musb_init, +	.exit		= am35x_musb_exit, + +	.enable		= am35x_musb_enable, +	.disable	= am35x_musb_disable, + +	.set_mode	= am35x_musb_set_mode, +	.try_idle	= am35x_musb_try_idle, + +	.set_vbus	= am35x_musb_set_vbus, +}; + +static u64 am35x_dmamask = DMA_BIT_MASK(32); + +static int __init am35x_probe(struct platform_device *pdev) +{ +	struct musb_hdrc_platform_data	*pdata = pdev->dev.platform_data; +	struct platform_device		*musb; +	struct am35x_glue		*glue; + +	struct clk			*phy_clk; +	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; +	} + +	musb = platform_device_alloc("musb-hdrc", -1); +	if (!musb) { +		dev_err(&pdev->dev, "failed to allocate musb device\n"); +		goto err1; +	} + +	phy_clk = clk_get(&pdev->dev, "fck"); +	if (IS_ERR(phy_clk)) { +		dev_err(&pdev->dev, "failed to get PHY clock\n"); +		ret = PTR_ERR(phy_clk); +		goto err2; +	} + +	clk = clk_get(&pdev->dev, "ick"); +	if (IS_ERR(clk)) { +		dev_err(&pdev->dev, "failed to get clock\n"); +		ret = PTR_ERR(clk); +		goto err3; +	} + +	ret = clk_enable(phy_clk); +	if (ret) { +		dev_err(&pdev->dev, "failed to enable PHY clock\n"); +		goto err4; +	} + +	ret = clk_enable(clk); +	if (ret) { +		dev_err(&pdev->dev, "failed to enable clock\n"); +		goto err5; +	} + +	musb->dev.parent		= &pdev->dev; +	musb->dev.dma_mask		= &am35x_dmamask; +	musb->dev.coherent_dma_mask	= am35x_dmamask; + +	glue->dev			= &pdev->dev; +	glue->musb			= musb; +	glue->phy_clk			= phy_clk; +	glue->clk			= clk; + +	pdata->platform_ops		= &am35x_ops; + +	platform_set_drvdata(pdev, glue); + +	ret = platform_device_add_resources(musb, pdev->resource, +			pdev->num_resources); +	if (ret) { +		dev_err(&pdev->dev, "failed to add resources\n"); +		goto err6; +	} + +	ret = platform_device_add_data(musb, pdata, sizeof(*pdata)); +	if (ret) { +		dev_err(&pdev->dev, "failed to add platform_data\n"); +		goto err6; +	} + +	ret = platform_device_add(musb); +	if (ret) { +		dev_err(&pdev->dev, "failed to register musb device\n"); +		goto err6; +	} + +	return 0; + +err6: +	clk_disable(clk); + +err5: +	clk_disable(phy_clk); + +err4: +	clk_put(clk); + +err3: +	clk_put(phy_clk); + +err2: +	platform_device_put(musb); + +err1: +	kfree(glue); + +err0: +	return ret; +} + +static int __exit am35x_remove(struct platform_device *pdev) +{ +	struct am35x_glue	*glue = platform_get_drvdata(pdev); + +	platform_device_del(glue->musb); +	platform_device_put(glue->musb); +	clk_disable(glue->clk); +	clk_disable(glue->phy_clk); +	clk_put(glue->clk); +	clk_put(glue->phy_clk); +	kfree(glue); + +	return 0; +} + +#ifdef CONFIG_PM +static int am35x_suspend(struct device *dev) +{ +	struct am35x_glue	*glue = dev_get_drvdata(dev); +	struct musb_hdrc_platform_data *plat = dev->platform_data; +	struct omap_musb_board_data *data = plat->board_data; + +	/* Shutdown the on-chip PHY and its PLL. */ +	if (data->set_phy_power) +		data->set_phy_power(0); + +	clk_disable(glue->phy_clk); +	clk_disable(glue->clk); + +	return 0; +} + +static int am35x_resume(struct device *dev) +{ +	struct am35x_glue	*glue = dev_get_drvdata(dev); +	struct musb_hdrc_platform_data *plat = dev->platform_data; +	struct omap_musb_board_data *data = plat->board_data; +	int			ret; + +	/* Start the on-chip PHY and its PLL. */ +	if (data->set_phy_power) +		data->set_phy_power(1); + +	ret = clk_enable(glue->phy_clk); +	if (ret) { +		dev_err(dev, "failed to enable PHY clock\n"); +		return ret; +	} + +	ret = clk_enable(glue->clk); +	if (ret) { +		dev_err(dev, "failed to enable clock\n"); +		return ret; +	} + +	return 0; +} + +static struct dev_pm_ops am35x_pm_ops = { +	.suspend	= am35x_suspend, +	.resume		= am35x_resume, +}; + +#define DEV_PM_OPS	&am35x_pm_ops +#else +#define DEV_PM_OPS	NULL +#endif + +static struct platform_driver am35x_driver = { +	.remove		= __exit_p(am35x_remove), +	.driver		= { +		.name	= "musb-am35x", +		.pm	= DEV_PM_OPS, +	}, +}; + +MODULE_DESCRIPTION("AM35x MUSB Glue Layer"); +MODULE_AUTHOR("Ajay Kumar Gupta <ajay.gupta@ti.com>"); +MODULE_LICENSE("GPL v2"); + +static int __init am35x_init(void) +{ +	return platform_driver_probe(&am35x_driver, am35x_probe); +} +subsys_initcall(am35x_init); + +static void __exit am35x_exit(void) +{ +	platform_driver_unregister(&am35x_driver); +} +module_exit(am35x_exit);  |