Message ID | 1312899309-24067-6-git-send-email-keshava_mgowda@ti.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
On Tue, Aug 09, 2011 at 07:45:09PM +0530, Keshava Munegowda wrote: > From: Keshava Munegowda <Keshava_mgowda@ti.com> > > The usbhs core driver does not enable/disable the intefrace and typo: interface > fucntional clocks; These clocks are handled by hwmod and runtime pm, typo: functional > hence insted of the clock enable/disable, the runtime pm APIS are > used. however,the port clocks are handled by the usbhs core. > > Signed-off-by: Keshava Munegowda <keshava_mgowda@ti.com> ... > @@ -913,12 +598,15 @@ static int usbhs_enable(struct device *dev) > (pdata->ehci_data->reset_gpio_port[1], 1); > } > > -end_count: > - omap->count++; > + pm_runtime_put_sync(dev); > spin_unlock_irqrestore(&omap->lock, flags); Is pm_runtime_irq_safe() needed (else I think runtime PM callbacks may re-enable IRQs... or there's the new *_suspend runtime PM calls that may avoid this)? ... > @@ -266,10 +261,12 @@ static int ehci_hcd_omap_remove(struct platform_device *pdev) > struct usb_hcd *hcd = dev_get_drvdata(dev); > > usb_remove_hcd(hcd); > - omap_usbhs_disable(dev); > disable_put_regulator(dev->platform_data); > - iounmap(hcd->regs); > usb_put_hcd(hcd); > + iounmap(hcd->regs); usb_put_hcd may release the hcd, needs to be after the deref for iounmap. > + pm_runtime_put_sync(dev); > + pm_runtime_disable(dev); Todd -- To unsubscribe from this list: send the line "unsubscribe linux-omap" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Hi, On Tue, Aug 9, 2011 at 10:15 PM, Keshava Munegowda <keshava_mgowda@ti.com> wrote: > From: Keshava Munegowda <Keshava_mgowda@ti.com> > > The usbhs core driver does not enable/disable the intefrace and > fucntional clocks; These clocks are handled by hwmod and runtime pm, > hence insted of the clock enable/disable, the runtime pm APIS are > used. however,the port clocks are handled by the usbhs core. > > Signed-off-by: Keshava Munegowda <keshava_mgowda@ti.com> > --- > arch/arm/plat-omap/include/plat/usb.h | 3 - > drivers/mfd/omap-usb-host.c | 722 +++++++++++++-------------------- > drivers/usb/host/ehci-omap.c | 19 +- > drivers/usb/host/ohci-omap3.c | 14 +- > 4 files changed, 286 insertions(+), 472 deletions(-) > > diff --git a/arch/arm/plat-omap/include/plat/usb.h b/arch/arm/plat-omap/include/plat/usb.h > index 17d3c93..2b66dc2 100644 > --- a/arch/arm/plat-omap/include/plat/usb.h > +++ b/arch/arm/plat-omap/include/plat/usb.h > @@ -100,9 +100,6 @@ extern void usb_musb_init(struct omap_musb_board_data *board_data); > > extern void usbhs_init(const struct usbhs_omap_board_data *pdata); > > -extern int omap_usbhs_enable(struct device *dev); > -extern void omap_usbhs_disable(struct device *dev); > - > extern int omap4430_phy_power(struct device *dev, int ID, int on); > extern int omap4430_phy_set_clk(struct device *dev, int on); > extern int omap4430_phy_init(struct device *dev); > diff --git a/drivers/mfd/omap-usb-host.c b/drivers/mfd/omap-usb-host.c > index 5def51c..39cfae6 100644 > --- a/drivers/mfd/omap-usb-host.c > +++ b/drivers/mfd/omap-usb-host.c > @@ -26,6 +26,7 @@ > #include <linux/spinlock.h> > #include <linux/gpio.h> > #include <plat/usb.h> > +#include <linux/pm_runtime.h> > > #define USBHS_DRIVER_NAME "usbhs_omap" > #define OMAP_EHCI_DEVICE "ehci-omap" > @@ -146,9 +147,6 @@ > > > struct usbhs_hcd_omap { > - struct clk *usbhost_ick; > - struct clk *usbhost_hs_fck; > - struct clk *usbhost_fs_fck; > struct clk *xclk60mhsp1_ck; > struct clk *xclk60mhsp2_ck; > struct clk *utmi_p1_fck; > @@ -158,8 +156,6 @@ struct usbhs_hcd_omap { > struct clk *usbhost_p2_fck; > struct clk *usbtll_p2_fck; > struct clk *init_60m_fclk; > - struct clk *usbtll_fck; > - struct clk *usbtll_ick; > > void __iomem *uhh_base; > void __iomem *tll_base; > @@ -168,7 +164,6 @@ struct usbhs_hcd_omap { > > u32 usbhs_rev; > spinlock_t lock; > - int count; > }; > /*-------------------------------------------------------------------------*/ > > @@ -318,269 +313,6 @@ err_end: > return ret; > } > > -/** > - * usbhs_omap_probe - initialize TI-based HCDs > - * > - * Allocates basic resources for this USB host controller. > - */ > -static int __devinit usbhs_omap_probe(struct platform_device *pdev) > -{ > - struct device *dev = &pdev->dev; > - struct usbhs_omap_platform_data *pdata = dev->platform_data; > - struct usbhs_hcd_omap *omap; > - struct resource *res; > - int ret = 0; > - int i; > - > - if (!pdata) { > - dev_err(dev, "Missing platform data\n"); > - ret = -ENOMEM; > - goto end_probe; > - } > - > - omap = kzalloc(sizeof(*omap), GFP_KERNEL); > - if (!omap) { > - dev_err(dev, "Memory allocation failed\n"); > - ret = -ENOMEM; > - goto end_probe; > - } > - > - spin_lock_init(&omap->lock); > - > - for (i = 0; i < OMAP3_HS_USB_PORTS; i++) > - omap->platdata.port_mode[i] = pdata->port_mode[i]; > - > - omap->platdata.ehci_data = pdata->ehci_data; > - omap->platdata.ohci_data = pdata->ohci_data; > - > - omap->usbhost_ick = clk_get(dev, "usbhost_ick"); > - if (IS_ERR(omap->usbhost_ick)) { > - ret = PTR_ERR(omap->usbhost_ick); > - dev_err(dev, "usbhost_ick failed error:%d\n", ret); > - goto err_end; > - } > - > - omap->usbhost_hs_fck = clk_get(dev, "hs_fck"); > - if (IS_ERR(omap->usbhost_hs_fck)) { > - ret = PTR_ERR(omap->usbhost_hs_fck); > - dev_err(dev, "usbhost_hs_fck failed error:%d\n", ret); > - goto err_usbhost_ick; > - } > - > - omap->usbhost_fs_fck = clk_get(dev, "fs_fck"); > - if (IS_ERR(omap->usbhost_fs_fck)) { > - ret = PTR_ERR(omap->usbhost_fs_fck); > - dev_err(dev, "usbhost_fs_fck failed error:%d\n", ret); > - goto err_usbhost_hs_fck; > - } > - > - omap->usbtll_fck = clk_get(dev, "usbtll_fck"); > - if (IS_ERR(omap->usbtll_fck)) { > - ret = PTR_ERR(omap->usbtll_fck); > - dev_err(dev, "usbtll_fck failed error:%d\n", ret); > - goto err_usbhost_fs_fck; > - } > - > - omap->usbtll_ick = clk_get(dev, "usbtll_ick"); > - if (IS_ERR(omap->usbtll_ick)) { > - ret = PTR_ERR(omap->usbtll_ick); > - dev_err(dev, "usbtll_ick failed error:%d\n", ret); > - goto err_usbtll_fck; > - } > - > - omap->utmi_p1_fck = clk_get(dev, "utmi_p1_gfclk"); > - if (IS_ERR(omap->utmi_p1_fck)) { > - ret = PTR_ERR(omap->utmi_p1_fck); > - dev_err(dev, "utmi_p1_gfclk failed error:%d\n", ret); > - goto err_usbtll_ick; > - } > - > - omap->xclk60mhsp1_ck = clk_get(dev, "xclk60mhsp1_ck"); > - if (IS_ERR(omap->xclk60mhsp1_ck)) { > - ret = PTR_ERR(omap->xclk60mhsp1_ck); > - dev_err(dev, "xclk60mhsp1_ck failed error:%d\n", ret); > - goto err_utmi_p1_fck; > - } > - > - omap->utmi_p2_fck = clk_get(dev, "utmi_p2_gfclk"); > - if (IS_ERR(omap->utmi_p2_fck)) { > - ret = PTR_ERR(omap->utmi_p2_fck); > - dev_err(dev, "utmi_p2_gfclk failed error:%d\n", ret); > - goto err_xclk60mhsp1_ck; > - } > - > - omap->xclk60mhsp2_ck = clk_get(dev, "xclk60mhsp2_ck"); > - if (IS_ERR(omap->xclk60mhsp2_ck)) { > - ret = PTR_ERR(omap->xclk60mhsp2_ck); > - dev_err(dev, "xclk60mhsp2_ck failed error:%d\n", ret); > - goto err_utmi_p2_fck; > - } > - > - omap->usbhost_p1_fck = clk_get(dev, "usb_host_hs_utmi_p1_clk"); > - if (IS_ERR(omap->usbhost_p1_fck)) { > - ret = PTR_ERR(omap->usbhost_p1_fck); > - dev_err(dev, "usbhost_p1_fck failed error:%d\n", ret); > - goto err_xclk60mhsp2_ck; > - } > - > - omap->usbtll_p1_fck = clk_get(dev, "usb_tll_hs_usb_ch0_clk"); > - if (IS_ERR(omap->usbtll_p1_fck)) { > - ret = PTR_ERR(omap->usbtll_p1_fck); > - dev_err(dev, "usbtll_p1_fck failed error:%d\n", ret); > - goto err_usbhost_p1_fck; > - } > - > - omap->usbhost_p2_fck = clk_get(dev, "usb_host_hs_utmi_p2_clk"); > - if (IS_ERR(omap->usbhost_p2_fck)) { > - ret = PTR_ERR(omap->usbhost_p2_fck); > - dev_err(dev, "usbhost_p2_fck failed error:%d\n", ret); > - goto err_usbtll_p1_fck; > - } > - > - omap->usbtll_p2_fck = clk_get(dev, "usb_tll_hs_usb_ch1_clk"); > - if (IS_ERR(omap->usbtll_p2_fck)) { > - ret = PTR_ERR(omap->usbtll_p2_fck); > - dev_err(dev, "usbtll_p2_fck failed error:%d\n", ret); > - goto err_usbhost_p2_fck; > - } > - > - omap->init_60m_fclk = clk_get(dev, "init_60m_fclk"); > - if (IS_ERR(omap->init_60m_fclk)) { > - ret = PTR_ERR(omap->init_60m_fclk); > - dev_err(dev, "init_60m_fclk failed error:%d\n", ret); > - goto err_usbtll_p2_fck; > - } > - > - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "uhh"); > - if (!res) { > - dev_err(dev, "UHH EHCI get resource failed\n"); > - ret = -ENODEV; > - goto err_init_60m_fclk; > - } > - > - omap->uhh_base = ioremap(res->start, resource_size(res)); > - if (!omap->uhh_base) { > - dev_err(dev, "UHH ioremap failed\n"); > - ret = -ENOMEM; > - goto err_init_60m_fclk; > - } > - > - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "tll"); > - if (!res) { > - dev_err(dev, "UHH EHCI get resource failed\n"); > - ret = -ENODEV; > - goto err_tll; > - } > - > - omap->tll_base = ioremap(res->start, resource_size(res)); > - if (!omap->tll_base) { > - dev_err(dev, "TLL ioremap failed\n"); > - ret = -ENOMEM; > - goto err_tll; > - } > - > - platform_set_drvdata(pdev, omap); > - > - ret = omap_usbhs_alloc_children(pdev); > - if (ret) { > - dev_err(dev, "omap_usbhs_alloc_children failed\n"); > - goto err_alloc; > - } > - > - goto end_probe; > - > -err_alloc: > - iounmap(omap->tll_base); > - > -err_tll: > - iounmap(omap->uhh_base); > - > -err_init_60m_fclk: > - clk_put(omap->init_60m_fclk); > - > -err_usbtll_p2_fck: > - clk_put(omap->usbtll_p2_fck); > - > -err_usbhost_p2_fck: > - clk_put(omap->usbhost_p2_fck); > - > -err_usbtll_p1_fck: > - clk_put(omap->usbtll_p1_fck); > - > -err_usbhost_p1_fck: > - clk_put(omap->usbhost_p1_fck); > - > -err_xclk60mhsp2_ck: > - clk_put(omap->xclk60mhsp2_ck); > - > -err_utmi_p2_fck: > - clk_put(omap->utmi_p2_fck); > - > -err_xclk60mhsp1_ck: > - clk_put(omap->xclk60mhsp1_ck); > - > -err_utmi_p1_fck: > - clk_put(omap->utmi_p1_fck); > - > -err_usbtll_ick: > - clk_put(omap->usbtll_ick); > - > -err_usbtll_fck: > - clk_put(omap->usbtll_fck); > - > -err_usbhost_fs_fck: > - clk_put(omap->usbhost_fs_fck); > - > -err_usbhost_hs_fck: > - clk_put(omap->usbhost_hs_fck); > - > -err_usbhost_ick: > - clk_put(omap->usbhost_ick); > - > -err_end: > - kfree(omap); > - > -end_probe: > - return ret; > -} > - > -/** > - * usbhs_omap_remove - shutdown processing for UHH & TLL HCDs > - * @pdev: USB Host Controller being removed > - * > - * Reverses the effect of usbhs_omap_probe(). > - */ > -static int __devexit usbhs_omap_remove(struct platform_device *pdev) > -{ > - struct usbhs_hcd_omap *omap = platform_get_drvdata(pdev); > - > - if (omap->count != 0) { > - dev_err(&pdev->dev, > - "Either EHCI or OHCI is still using usbhs core\n"); > - return -EBUSY; > - } > - > - iounmap(omap->tll_base); > - iounmap(omap->uhh_base); > - clk_put(omap->init_60m_fclk); > - clk_put(omap->usbtll_p2_fck); > - clk_put(omap->usbhost_p2_fck); > - clk_put(omap->usbtll_p1_fck); > - clk_put(omap->usbhost_p1_fck); > - clk_put(omap->xclk60mhsp2_ck); > - clk_put(omap->utmi_p2_fck); > - clk_put(omap->xclk60mhsp1_ck); > - clk_put(omap->utmi_p1_fck); > - clk_put(omap->usbtll_ick); > - clk_put(omap->usbtll_fck); > - clk_put(omap->usbhost_fs_fck); > - clk_put(omap->usbhost_hs_fck); > - clk_put(omap->usbhost_ick); > - kfree(omap); > - > - return 0; > -} > - > static bool is_ohci_port(enum usbhs_omap_port_mode pmode) > { > switch (pmode) { > @@ -689,30 +421,70 @@ static void usbhs_omap_tll_init(struct device *dev, u8 tll_channel_count) > } > } > > -static int usbhs_enable(struct device *dev) > +static int usbhs_runtime_resume(struct device *dev) > { > struct usbhs_hcd_omap *omap = dev_get_drvdata(dev); > struct usbhs_omap_platform_data *pdata = &omap->platdata; > - unsigned long flags = 0; > - int ret = 0; > - unsigned long timeout; > - unsigned reg; > > - dev_dbg(dev, "starting TI HSUSB Controller\n"); > + dev_dbg(dev, "usbhs_runtime_resume\n"); > + > + if (!pdata) { > + dev_dbg(dev, "missing platform_data\n"); > + return -ENODEV; > + } > + > + if (is_ehci_tll_mode(pdata->port_mode[0])) { > + clk_enable(omap->usbhost_p1_fck); > + clk_enable(omap->usbtll_p1_fck); > + } > + if (is_ehci_tll_mode(pdata->port_mode[1])) { > + clk_enable(omap->usbhost_p2_fck); > + clk_enable(omap->usbtll_p2_fck); > + } > + clk_enable(omap->utmi_p1_fck); > + clk_enable(omap->utmi_p2_fck); > + > + return 0; > +} > + > +static int usbhs_runtime_suspend(struct device *dev) > +{ > + struct usbhs_hcd_omap *omap = dev_get_drvdata(dev); > + struct usbhs_omap_platform_data *pdata = &omap->platdata; > + > + dev_dbg(dev, "usbhs_runtime_suspend\n"); > + > if (!pdata) { > dev_dbg(dev, "missing platform_data\n"); > return -ENODEV; > } > > + if (is_ehci_tll_mode(pdata->port_mode[0])) { > + clk_disable(omap->usbhost_p1_fck); > + clk_disable(omap->usbtll_p1_fck); > + } > + if (is_ehci_tll_mode(pdata->port_mode[1])) { > + clk_disable(omap->usbhost_p2_fck); > + clk_disable(omap->usbtll_p2_fck); > + } > + clk_disable(omap->utmi_p2_fck); > + clk_disable(omap->utmi_p1_fck); > + > + return 0; > +} > + > +static void omap_usbhs_init(struct device *dev) > +{ > + struct usbhs_hcd_omap *omap = dev_get_drvdata(dev); > + struct usbhs_omap_platform_data *pdata = &omap->platdata; > + unsigned long flags = 0; > + unsigned reg; > + > + dev_dbg(dev, "starting TI HSUSB Controller\n"); > + > spin_lock_irqsave(&omap->lock, flags); > - if (omap->count > 0) > - goto end_count; > > - clk_enable(omap->usbhost_ick); > - clk_enable(omap->usbhost_hs_fck); > - clk_enable(omap->usbhost_fs_fck); > - clk_enable(omap->usbtll_fck); > - clk_enable(omap->usbtll_ick); > + pm_runtime_get_sync(dev); > > if (pdata->ehci_data->phy_reset) { > if (gpio_is_valid(pdata->ehci_data->reset_gpio_port[0])) { > @@ -736,50 +508,6 @@ static int usbhs_enable(struct device *dev) > omap->usbhs_rev = usbhs_read(omap->uhh_base, OMAP_UHH_REVISION); > dev_dbg(dev, "OMAP UHH_REVISION 0x%x\n", omap->usbhs_rev); > > - /* perform TLL soft reset, and wait until reset is complete */ > - usbhs_write(omap->tll_base, OMAP_USBTLL_SYSCONFIG, > - OMAP_USBTLL_SYSCONFIG_SOFTRESET); > - > - /* Wait for TLL reset to complete */ > - timeout = jiffies + msecs_to_jiffies(1000); > - while (!(usbhs_read(omap->tll_base, OMAP_USBTLL_SYSSTATUS) > - & OMAP_USBTLL_SYSSTATUS_RESETDONE)) { > - cpu_relax(); > - > - if (time_after(jiffies, timeout)) { > - dev_dbg(dev, "operation timed out\n"); > - ret = -EINVAL; > - goto err_tll; > - } > - } > - > - dev_dbg(dev, "TLL RESET DONE\n"); > - > - /* (1<<3) = no idle mode only for initial debugging */ > - usbhs_write(omap->tll_base, OMAP_USBTLL_SYSCONFIG, > - OMAP_USBTLL_SYSCONFIG_ENAWAKEUP | > - OMAP_USBTLL_SYSCONFIG_SIDLEMODE | > - OMAP_USBTLL_SYSCONFIG_AUTOIDLE); > - > - /* Put UHH in NoIdle/NoStandby mode */ > - reg = usbhs_read(omap->uhh_base, OMAP_UHH_SYSCONFIG); > - if (is_omap_usbhs_rev1(omap)) { > - reg |= (OMAP_UHH_SYSCONFIG_ENAWAKEUP > - | OMAP_UHH_SYSCONFIG_SIDLEMODE > - | OMAP_UHH_SYSCONFIG_CACTIVITY > - | OMAP_UHH_SYSCONFIG_MIDLEMODE); > - reg &= ~OMAP_UHH_SYSCONFIG_AUTOIDLE; > - > - > - } else if (is_omap_usbhs_rev2(omap)) { > - reg &= ~OMAP4_UHH_SYSCONFIG_IDLEMODE_CLEAR; > - reg |= OMAP4_UHH_SYSCONFIG_NOIDLE; > - reg &= ~OMAP4_UHH_SYSCONFIG_STDBYMODE_CLEAR; > - reg |= OMAP4_UHH_SYSCONFIG_NOSTDBY; > - } > - > - usbhs_write(omap->uhh_base, OMAP_UHH_SYSCONFIG, reg); > - > reg = usbhs_read(omap->uhh_base, OMAP_UHH_HOSTCONFIG); > /* setup ULPI bypass and burst configurations */ > reg |= (OMAP_UHH_HOSTCONFIG_INCR4_BURST_EN > @@ -825,49 +553,6 @@ static int usbhs_enable(struct device *dev) > reg &= ~OMAP4_P1_MODE_CLEAR; > reg &= ~OMAP4_P2_MODE_CLEAR; > > - if (is_ehci_phy_mode(pdata->port_mode[0])) { > - ret = clk_set_parent(omap->utmi_p1_fck, > - omap->xclk60mhsp1_ck); > - if (ret != 0) { > - dev_err(dev, "xclk60mhsp1_ck set parent" > - "failed error:%d\n", ret); > - goto err_tll; > - } > - } else if (is_ehci_tll_mode(pdata->port_mode[0])) { > - ret = clk_set_parent(omap->utmi_p1_fck, > - omap->init_60m_fclk); > - if (ret != 0) { > - dev_err(dev, "init_60m_fclk set parent" > - "failed error:%d\n", ret); > - goto err_tll; > - } > - clk_enable(omap->usbhost_p1_fck); > - clk_enable(omap->usbtll_p1_fck); > - } > - > - if (is_ehci_phy_mode(pdata->port_mode[1])) { > - ret = clk_set_parent(omap->utmi_p2_fck, > - omap->xclk60mhsp2_ck); > - if (ret != 0) { > - dev_err(dev, "xclk60mhsp1_ck set parent" > - "failed error:%d\n", ret); > - goto err_tll; > - } > - } else if (is_ehci_tll_mode(pdata->port_mode[1])) { > - ret = clk_set_parent(omap->utmi_p2_fck, > - omap->init_60m_fclk); > - if (ret != 0) { > - dev_err(dev, "init_60m_fclk set parent" > - "failed error:%d\n", ret); > - goto err_tll; > - } > - clk_enable(omap->usbhost_p2_fck); > - clk_enable(omap->usbtll_p2_fck); > - } > - > - clk_enable(omap->utmi_p1_fck); > - clk_enable(omap->utmi_p2_fck); > - > if (is_ehci_tll_mode(pdata->port_mode[0]) || > (is_ohci_port(pdata->port_mode[0]))) > reg |= OMAP4_P1_MODE_TLL; > @@ -913,12 +598,15 @@ static int usbhs_enable(struct device *dev) > (pdata->ehci_data->reset_gpio_port[1], 1); > } > > -end_count: > - omap->count++; > + pm_runtime_put_sync(dev); > spin_unlock_irqrestore(&omap->lock, flags); > - return 0; > +} > + > +static void omap_usbhs_deinit(struct device *dev) > +{ > + struct usbhs_hcd_omap *omap = dev_get_drvdata(dev); > + struct usbhs_omap_platform_data *pdata = &omap->platdata; > > -err_tll: > if (pdata->ehci_data->phy_reset) { > if (gpio_is_valid(pdata->ehci_data->reset_gpio_port[0])) > gpio_free(pdata->ehci_data->reset_gpio_port[0]); > @@ -926,123 +614,257 @@ err_tll: > if (gpio_is_valid(pdata->ehci_data->reset_gpio_port[1])) > gpio_free(pdata->ehci_data->reset_gpio_port[1]); > } > - > - clk_disable(omap->usbtll_ick); > - clk_disable(omap->usbtll_fck); > - clk_disable(omap->usbhost_fs_fck); > - clk_disable(omap->usbhost_hs_fck); > - clk_disable(omap->usbhost_ick); > - spin_unlock_irqrestore(&omap->lock, flags); > - return ret; > } > > -static void usbhs_disable(struct device *dev) > + > +/** > + * usbhs_omap_probe - initialize TI-based HCDs > + * > + * Allocates basic resources for this USB host controller. > + */ > +static int __devinit usbhs_omap_probe(struct platform_device *pdev) > { > - struct usbhs_hcd_omap *omap = dev_get_drvdata(dev); > - struct usbhs_omap_platform_data *pdata = &omap->platdata; > - unsigned long flags = 0; > - unsigned long timeout; > + struct device *dev = &pdev->dev; > + struct usbhs_omap_platform_data *pdata = dev->platform_data; > + struct usbhs_hcd_omap *omap; > + struct resource *res; > + int ret = 0; > + int i; > > - dev_dbg(dev, "stopping TI HSUSB Controller\n"); > + if (!pdata) { > + dev_err(dev, "Missing platform data\n"); > + ret = -ENOMEM; > + goto end_probe; > + } > > - spin_lock_irqsave(&omap->lock, flags); > + omap = kzalloc(sizeof(*omap), GFP_KERNEL); > + if (!omap) { > + dev_err(dev, "Memory allocation failed\n"); > + ret = -ENOMEM; > + goto end_probe; > + } > + > + spin_lock_init(&omap->lock); > + > + for (i = 0; i < OMAP3_HS_USB_PORTS; i++) > + omap->platdata.port_mode[i] = pdata->port_mode[i]; > > - if (omap->count == 0) > - goto end_disble; > + omap->platdata.ehci_data = pdata->ehci_data; > + omap->platdata.ohci_data = pdata->ohci_data; > > - omap->count--; > + pm_runtime_enable(dev); > > - if (omap->count != 0) > - goto end_disble; > + omap->utmi_p1_fck = clk_get(dev, "utmi_p1_gfclk"); > + if (IS_ERR(omap->utmi_p1_fck)) { > + ret = PTR_ERR(omap->utmi_p1_fck); > + dev_err(dev, "utmi_p1_gfclk failed error:%d\n", ret); > + goto err_end; > + } > + > + omap->xclk60mhsp1_ck = clk_get(dev, "xclk60mhsp1_ck"); > + if (IS_ERR(omap->xclk60mhsp1_ck)) { > + ret = PTR_ERR(omap->xclk60mhsp1_ck); > + dev_err(dev, "xclk60mhsp1_ck failed error:%d\n", ret); > + goto err_utmi_p1_fck; > + } > > - /* Reset OMAP modules for insmod/rmmod to work */ > - usbhs_write(omap->uhh_base, OMAP_UHH_SYSCONFIG, > - is_omap_usbhs_rev2(omap) ? > - OMAP4_UHH_SYSCONFIG_SOFTRESET : > - OMAP_UHH_SYSCONFIG_SOFTRESET); > + omap->utmi_p2_fck = clk_get(dev, "utmi_p2_gfclk"); > + if (IS_ERR(omap->utmi_p2_fck)) { > + ret = PTR_ERR(omap->utmi_p2_fck); > + dev_err(dev, "utmi_p2_gfclk failed error:%d\n", ret); > + goto err_xclk60mhsp1_ck; > + } > > - timeout = jiffies + msecs_to_jiffies(100); > - while (!(usbhs_read(omap->uhh_base, OMAP_UHH_SYSSTATUS) > - & (1 << 0))) { > - cpu_relax(); > + omap->xclk60mhsp2_ck = clk_get(dev, "xclk60mhsp2_ck"); > + if (IS_ERR(omap->xclk60mhsp2_ck)) { > + ret = PTR_ERR(omap->xclk60mhsp2_ck); > + dev_err(dev, "xclk60mhsp2_ck failed error:%d\n", ret); > + goto err_utmi_p2_fck; > + } > > - if (time_after(jiffies, timeout)) > - dev_dbg(dev, "operation timed out\n"); > + omap->usbhost_p1_fck = clk_get(dev, "usb_host_hs_utmi_p1_clk"); > + if (IS_ERR(omap->usbhost_p1_fck)) { > + ret = PTR_ERR(omap->usbhost_p1_fck); > + dev_err(dev, "usbhost_p1_fck failed error:%d\n", ret); > + goto err_xclk60mhsp2_ck; > } > > - while (!(usbhs_read(omap->uhh_base, OMAP_UHH_SYSSTATUS) > - & (1 << 1))) { > - cpu_relax(); > + omap->usbtll_p1_fck = clk_get(dev, "usb_tll_hs_usb_ch0_clk"); > + if (IS_ERR(omap->usbtll_p1_fck)) { > + ret = PTR_ERR(omap->usbtll_p1_fck); > + dev_err(dev, "usbtll_p1_fck failed error:%d\n", ret); > + goto err_usbhost_p1_fck; > + } > > - if (time_after(jiffies, timeout)) > - dev_dbg(dev, "operation timed out\n"); > + omap->usbhost_p2_fck = clk_get(dev, "usb_host_hs_utmi_p2_clk"); > + if (IS_ERR(omap->usbhost_p2_fck)) { > + ret = PTR_ERR(omap->usbhost_p2_fck); > + dev_err(dev, "usbhost_p2_fck failed error:%d\n", ret); > + goto err_usbtll_p1_fck; > } > > - while (!(usbhs_read(omap->uhh_base, OMAP_UHH_SYSSTATUS) > - & (1 << 2))) { > - cpu_relax(); > + omap->usbtll_p2_fck = clk_get(dev, "usb_tll_hs_usb_ch1_clk"); > + if (IS_ERR(omap->usbtll_p2_fck)) { > + ret = PTR_ERR(omap->usbtll_p2_fck); > + dev_err(dev, "usbtll_p2_fck failed error:%d\n", ret); > + goto err_usbhost_p2_fck; > + } > > - if (time_after(jiffies, timeout)) > - dev_dbg(dev, "operation timed out\n"); > + omap->init_60m_fclk = clk_get(dev, "init_60m_fclk"); > + if (IS_ERR(omap->init_60m_fclk)) { > + ret = PTR_ERR(omap->init_60m_fclk); > + dev_err(dev, "init_60m_fclk failed error:%d\n", ret); > + goto err_usbtll_p2_fck; > } > > - usbhs_write(omap->tll_base, OMAP_USBTLL_SYSCONFIG, (1 << 1)); > + if (is_ehci_phy_mode(pdata->port_mode[0])) { > + /* for OMAP3 , the clk set paretn fails */ > + ret = clk_set_parent(omap->utmi_p1_fck, > + omap->xclk60mhsp1_ck); > + if (ret != 0) > + dev_err(dev, "xclk60mhsp1_ck set parent" > + "failed error:%d\n", ret); > + } else if (is_ehci_tll_mode(pdata->port_mode[0])) { > + ret = clk_set_parent(omap->utmi_p1_fck, > + omap->init_60m_fclk); > + if (ret != 0) > + dev_err(dev, "init_60m_fclk set parent" > + "failed error:%d\n", ret); > + } > > - while (!(usbhs_read(omap->tll_base, OMAP_USBTLL_SYSSTATUS) > - & (1 << 0))) { > - cpu_relax(); > + if (is_ehci_phy_mode(pdata->port_mode[1])) { > + ret = clk_set_parent(omap->utmi_p2_fck, > + omap->xclk60mhsp2_ck); > + if (ret != 0) > + dev_err(dev, "xclk60mhsp2_ck set parent" > + "failed error:%d\n", ret); > + } else if (is_ehci_tll_mode(pdata->port_mode[1])) { > + ret = clk_set_parent(omap->utmi_p2_fck, > + omap->init_60m_fclk); > + if (ret != 0) > + dev_err(dev, "init_60m_fclk set parent" > + "failed error:%d\n", ret); > + } > > - if (time_after(jiffies, timeout)) > - dev_dbg(dev, "operation timed out\n"); > + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "uhh"); > + if (!res) { > + dev_err(dev, "UHH EHCI get resource failed\n"); > + ret = -ENODEV; > + goto err_init_60m_fclk; > } > > - if (is_omap_usbhs_rev2(omap)) { > - if (is_ehci_tll_mode(pdata->port_mode[0])) > - clk_disable(omap->usbtll_p1_fck); > - if (is_ehci_tll_mode(pdata->port_mode[1])) > - clk_disable(omap->usbtll_p2_fck); > - clk_disable(omap->utmi_p2_fck); > - clk_disable(omap->utmi_p1_fck); > + omap->uhh_base = ioremap(res->start, resource_size(res)); > + if (!omap->uhh_base) { > + dev_err(dev, "UHH ioremap failed\n"); > + ret = -ENOMEM; > + goto err_init_60m_fclk; > } > > - clk_disable(omap->usbtll_ick); > - clk_disable(omap->usbtll_fck); > - clk_disable(omap->usbhost_fs_fck); > - clk_disable(omap->usbhost_hs_fck); > - clk_disable(omap->usbhost_ick); > + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "tll"); > + if (!res) { > + dev_err(dev, "UHH EHCI get resource failed\n"); > + ret = -ENODEV; > + goto err_tll; > + } > > - /* The gpio_free migh sleep; so unlock the spinlock */ > - spin_unlock_irqrestore(&omap->lock, flags); > + omap->tll_base = ioremap(res->start, resource_size(res)); > + if (!omap->tll_base) { > + dev_err(dev, "TLL ioremap failed\n"); > + ret = -ENOMEM; > + goto err_tll; > + } > > - if (pdata->ehci_data->phy_reset) { > - if (gpio_is_valid(pdata->ehci_data->reset_gpio_port[0])) > - gpio_free(pdata->ehci_data->reset_gpio_port[0]); > + platform_set_drvdata(pdev, omap); > > - if (gpio_is_valid(pdata->ehci_data->reset_gpio_port[1])) > - gpio_free(pdata->ehci_data->reset_gpio_port[1]); > + ret = omap_usbhs_alloc_children(pdev); > + if (ret) { > + dev_err(dev, "omap_usbhs_alloc_children failed\n"); > + goto err_alloc; > } > - return; > > -end_disble: > - spin_unlock_irqrestore(&omap->lock, flags); > -} > + omap_usbhs_init(dev); > > -int omap_usbhs_enable(struct device *dev) > -{ > - return usbhs_enable(dev->parent); > + goto end_probe; > + > +err_alloc: > + iounmap(omap->tll_base); > + > +err_tll: > + iounmap(omap->uhh_base); > + > +err_init_60m_fclk: > + clk_put(omap->init_60m_fclk); > + > +err_usbtll_p2_fck: > + clk_put(omap->usbtll_p2_fck); > + > +err_usbhost_p2_fck: > + clk_put(omap->usbhost_p2_fck); > + > +err_usbtll_p1_fck: > + clk_put(omap->usbtll_p1_fck); > + > +err_usbhost_p1_fck: > + clk_put(omap->usbhost_p1_fck); > + > +err_xclk60mhsp2_ck: > + clk_put(omap->xclk60mhsp2_ck); > + > +err_utmi_p2_fck: > + clk_put(omap->utmi_p2_fck); > + > +err_xclk60mhsp1_ck: > + clk_put(omap->xclk60mhsp1_ck); > + > +err_utmi_p1_fck: > + clk_put(omap->utmi_p1_fck); > + > +err_end: > + pm_runtime_disable(dev); > + kfree(omap); > + > +end_probe: > + return ret; > } > -EXPORT_SYMBOL_GPL(omap_usbhs_enable); > > -void omap_usbhs_disable(struct device *dev) > +/** > + * usbhs_omap_remove - shutdown processing for UHH & TLL HCDs > + * @pdev: USB Host Controller being removed > + * > + * Reverses the effect of usbhs_omap_probe(). > + */ > +static int __devexit usbhs_omap_remove(struct platform_device *pdev) > { > - usbhs_disable(dev->parent); > + struct usbhs_hcd_omap *omap = platform_get_drvdata(pdev); > + > + omap_usbhs_deinit(&pdev->dev); > + iounmap(omap->tll_base); > + iounmap(omap->uhh_base); > + clk_put(omap->init_60m_fclk); > + clk_put(omap->usbtll_p2_fck); > + clk_put(omap->usbhost_p2_fck); > + clk_put(omap->usbtll_p1_fck); > + clk_put(omap->usbhost_p1_fck); > + clk_put(omap->xclk60mhsp2_ck); > + clk_put(omap->utmi_p2_fck); > + clk_put(omap->xclk60mhsp1_ck); > + clk_put(omap->utmi_p1_fck); > + pm_runtime_disable(&pdev->dev); > + kfree(omap); > + > + return 0; > } > -EXPORT_SYMBOL_GPL(omap_usbhs_disable); > + > +static const struct dev_pm_ops usbhsomap_dev_pm_ops = { > + .runtime_suspend = usbhs_runtime_suspend, > + .runtime_resume = usbhs_runtime_resume, > +}; > > static struct platform_driver usbhs_omap_driver = { > .driver = { > .name = (char *)usbhs_driver_name, > .owner = THIS_MODULE, > + .pm = &usbhsomap_dev_pm_ops, > }, > .remove = __exit_p(usbhs_omap_remove), > }; > diff --git a/drivers/usb/host/ehci-omap.c b/drivers/usb/host/ehci-omap.c > index 4524032..6551d30 100644 > --- a/drivers/usb/host/ehci-omap.c > +++ b/drivers/usb/host/ehci-omap.c > @@ -41,6 +41,7 @@ > #include <linux/usb/ulpi.h> > #include <plat/usb.h> > #include <linux/regulator/consumer.h> > +#include <linux/pm_runtime.h> > > /* EHCI Register Set */ > #define EHCI_INSNREG04 (0xA0) > @@ -190,11 +191,8 @@ static int ehci_hcd_omap_probe(struct platform_device *pdev) > } > } > > - ret = omap_usbhs_enable(dev); > - if (ret) { > - dev_err(dev, "failed to start usbhs with err %d\n", ret); > - goto err_enable; > - } > + pm_runtime_enable(dev); > + pm_runtime_get_sync(dev); > > /* > * An undocumented "feature" in the OMAP3 EHCI controller, > @@ -240,11 +238,8 @@ static int ehci_hcd_omap_probe(struct platform_device *pdev) > return 0; > > err_add_hcd: > - omap_usbhs_disable(dev); > - > -err_enable: > disable_put_regulator(pdata); > - usb_put_hcd(hcd); This seems to be put before 'err_io' and after pm_runtime_put_sync, doesn't it? > + pm_runtime_put_sync(dev); > > err_io: > iounmap(regs); > @@ -266,10 +261,12 @@ static int ehci_hcd_omap_remove(struct platform_device *pdev) > struct usb_hcd *hcd = dev_get_drvdata(dev); > > usb_remove_hcd(hcd); > - omap_usbhs_disable(dev); > disable_put_regulator(dev->platform_data); > - iounmap(hcd->regs); > usb_put_hcd(hcd); > + iounmap(hcd->regs); > + pm_runtime_put_sync(dev); > + pm_runtime_disable(dev); > + > return 0; > } > > diff --git a/drivers/usb/host/ohci-omap3.c b/drivers/usb/host/ohci-omap3.c > index 6048f2f..58e3dae 100644 > --- a/drivers/usb/host/ohci-omap3.c > +++ b/drivers/usb/host/ohci-omap3.c > @@ -31,6 +31,7 @@ > > #include <linux/platform_device.h> > #include <plat/usb.h> > +#include <linux/pm_runtime.h> > > /*-------------------------------------------------------------------------*/ > > @@ -172,11 +173,8 @@ static int __devinit ohci_hcd_omap3_probe(struct platform_device *pdev) > hcd->rsrc_len = resource_size(res); > hcd->regs = regs; > > - ret = omap_usbhs_enable(dev); > - if (ret) { > - dev_dbg(dev, "failed to start ohci\n"); > - goto err_end; > - } > + pm_runtime_enable(dev); > + pm_runtime_get_sync(dev); > > ohci_hcd_init(hcd_to_ohci(hcd)); > > @@ -189,7 +187,7 @@ static int __devinit ohci_hcd_omap3_probe(struct platform_device *pdev) > return 0; > > err_add_hcd: > - omap_usbhs_disable(dev); > + pm_runtime_get_sync(dev); should be pm_runtime_put_sync? > > err_end: > usb_put_hcd(hcd); > @@ -220,9 +218,9 @@ static int __devexit ohci_hcd_omap3_remove(struct platform_device *pdev) > > iounmap(hcd->regs); > usb_remove_hcd(hcd); > - omap_usbhs_disable(dev); > + pm_runtime_put_sync(dev); > + pm_runtime_disable(dev); > usb_put_hcd(hcd); > - > return 0; > } > > -- > 1.6.0.4 > > -- > To unsubscribe from this list: send the line "unsubscribe linux-omap" in > the body of a message to majordomo@vger.kernel.org > More majordomo info at http://vger.kernel.org/majordomo-info.html >
On Wed, Aug 10, 2011 at 10:01 PM, Todd Poynor <toddpoynor@google.com> wrote: > On Tue, Aug 09, 2011 at 07:45:09PM +0530, Keshava Munegowda wrote: >> From: Keshava Munegowda <Keshava_mgowda@ti.com> >> >> The usbhs core driver does not enable/disable the intefrace and > > > typo: interface > >> fucntional clocks; These clocks are handled by hwmod and runtime pm, > > > typo: functional > >> hence insted of the clock enable/disable, the runtime pm APIS are >> used. however,the port clocks are handled by the usbhs core. >> >> Signed-off-by: Keshava Munegowda <keshava_mgowda@ti.com> > > ... >> @@ -913,12 +598,15 @@ static int usbhs_enable(struct device *dev) >> (pdata->ehci_data->reset_gpio_port[1], 1); >> } >> >> -end_count: >> - omap->count++; >> + pm_runtime_put_sync(dev); >> spin_unlock_irqrestore(&omap->lock, flags); > > Is pm_runtime_irq_safe() needed (else I think runtime PM callbacks may > re-enable IRQs... or there's the new *_suspend runtime PM calls that > may avoid this)? pm_runtime_irq_safe() is not required; usbhs does not have a parent and it is the parent driver of ehci and ohci drivers. > > ... >> @@ -266,10 +261,12 @@ static int ehci_hcd_omap_remove(struct platform_device *pdev) >> struct usb_hcd *hcd = dev_get_drvdata(dev); >> >> usb_remove_hcd(hcd); >> - omap_usbhs_disable(dev); >> disable_put_regulator(dev->platform_data); >> - iounmap(hcd->regs); >> usb_put_hcd(hcd); >> + iounmap(hcd->regs); yes , I will do this. > > > usb_put_hcd may release the hcd, needs to be after the deref for > iounmap. > >> + pm_runtime_put_sync(dev); >> + pm_runtime_disable(dev); > > > Todd > -- To unsubscribe from this list: send the line "unsubscribe linux-omap" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
On Fri, Aug 12, 2011 at 12:20:21PM +0530, Munegowda, Keshava wrote: > On Wed, Aug 10, 2011 at 10:01 PM, Todd Poynor <toddpoynor@google.com> wrote: > >> @@ -913,12 +598,15 @@ static int usbhs_enable(struct device *dev) > >> (pdata->ehci_data->reset_gpio_port[1], 1); > >> } > >> > >> -end_count: > >> - omap->count++; > >> + pm_runtime_put_sync(dev); > >> spin_unlock_irqrestore(&omap->lock, flags); > > > > Is pm_runtime_irq_safe() needed (else I think runtime PM callbacks may > > re-enable IRQs... or there's the new *_suspend runtime PM calls that > > may avoid this)? > > pm_runtime_irq_safe() is not required; usbhs does not have a parent > and it is the parent driver of > ehci and ohci drivers. But the above expects IRQs to be disabled during the pm_runtime_put_sync, and synchronous calls can turn IRQs back on in rpm_idle: if (callback) { spin_unlock_irq(&dev->power.lock); callback(dev); I see other folks who know this better than me are discussing USB run time PM and might_sleep contexts, so I'll note this concern and let others chime in if they think there's a real problem here. Todd -- To unsubscribe from this list: send the line "unsubscribe linux-omap" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
On Fri, Aug 12, 2011 at 16:30, Todd Poynor <toddpoynor@google.com> wrote: > On Fri, Aug 12, 2011 at 12:20:21PM +0530, Munegowda, Keshava wrote: >> On Wed, Aug 10, 2011 at 10:01 PM, Todd Poynor <toddpoynor@google.com> wrote: >> >> @@ -913,12 +598,15 @@ static int usbhs_enable(struct device *dev) >> >> (pdata->ehci_data->reset_gpio_port[1], 1); >> >> } >> >> >> >> -end_count: >> >> - omap->count++; >> >> + pm_runtime_put_sync(dev); >> >> spin_unlock_irqrestore(&omap->lock, flags); >> > >> > Is pm_runtime_irq_safe() needed (else I think runtime PM callbacks may >> > re-enable IRQs... or there's the new *_suspend runtime PM calls that >> > may avoid this)? >> >> pm_runtime_irq_safe() is not required; usbhs does not have a parent >> and it is the parent driver of >> ehci and ohci drivers. > > But the above expects IRQs to be disabled during the > pm_runtime_put_sync, and synchronous calls can turn IRQs back on in > rpm_idle: > > if (callback) { > spin_unlock_irq(&dev->power.lock); > > callback(dev); > > I see other folks who know this better than me are discussing USB run > time PM and might_sleep contexts, so I'll note this concern and let > others chime in if they think there's a real problem here. It is rather easy with the following patch applied to pull this bug out! https://patchwork.kernel.org/patch/1001572/ Regards, Nishanth Menon -- To unsubscribe from this list: send the line "unsubscribe linux-omap" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
On Sat, Aug 13, 2011 at 3:00 AM, Todd Poynor <toddpoynor@google.com> wrote: > On Fri, Aug 12, 2011 at 12:20:21PM +0530, Munegowda, Keshava wrote: >> On Wed, Aug 10, 2011 at 10:01 PM, Todd Poynor <toddpoynor@google.com> wrote: >> >> @@ -913,12 +598,15 @@ static int usbhs_enable(struct device *dev) >> >> (pdata->ehci_data->reset_gpio_port[1], 1); >> >> } >> >> >> >> -end_count: >> >> - omap->count++; >> >> + pm_runtime_put_sync(dev); >> >> spin_unlock_irqrestore(&omap->lock, flags); >> > >> > Is pm_runtime_irq_safe() needed (else I think runtime PM callbacks may >> > re-enable IRQs... or there's the new *_suspend runtime PM calls that >> > may avoid this)? >> >> pm_runtime_irq_safe() is not required; usbhs does not have a parent >> and it is the parent driver of >> ehci and ohci drivers. > > But the above expects IRQs to be disabled during the > pm_runtime_put_sync, and synchronous calls can turn IRQs back on in > rpm_idle: > > if (callback) { > spin_unlock_irq(&dev->power.lock); > > callback(dev); > > I see other folks who know this better than me are discussing USB run > time PM and might_sleep contexts, so I'll note this concern and let > others chime in if they think there's a real problem here. Thanks, I think I should protect the critical section of call backs here. > > > Todd > -- To unsubscribe from this list: send the line "unsubscribe linux-omap" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Todd Poynor <toddpoynor@google.com> writes: > On Fri, Aug 12, 2011 at 12:20:21PM +0530, Munegowda, Keshava wrote: >> On Wed, Aug 10, 2011 at 10:01 PM, Todd Poynor <toddpoynor@google.com> wrote: >> >> @@ -913,12 +598,15 @@ static int usbhs_enable(struct device *dev) >> >> (pdata->ehci_data->reset_gpio_port[1], 1); >> >> } >> >> >> >> -end_count: >> >> - omap->count++; >> >> + pm_runtime_put_sync(dev); >> >> spin_unlock_irqrestore(&omap->lock, flags); >> > >> > Is pm_runtime_irq_safe() needed (else I think runtime PM callbacks may >> > re-enable IRQs... or there's the new *_suspend runtime PM calls that >> > may avoid this)? >> >> pm_runtime_irq_safe() is not required; usbhs does not have a parent >> and it is the parent driver of >> ehci and ohci drivers. > > But the above expects IRQs to be disabled during the > pm_runtime_put_sync, and synchronous calls can turn IRQs back on in > rpm_idle: > > if (callback) { > spin_unlock_irq(&dev->power.lock); > > callback(dev); > > I see other folks who know this better than me are discussing USB run > time PM and might_sleep contexts, so I'll note this concern and let > others chime in if they think there's a real problem here. FYI... The commit below fixes this mainline (merged as of v3.1-rc4). Kevin commit 02b26774afebb2d62695ba3230319d70d8c6cc2d Author: Kevin Hilman <khilman@ti.com> Date: Fri Aug 5 21:45:20 2011 +0200 PM / Runtime: Allow _put_sync() from interrupts-disabled context Currently the use of pm_runtime_put_sync() is not safe from interrupts-disabled context because rpm_idle() will release the spinlock and enable interrupts for the idle callbacks. This enables interrupts during a time where interrupts were expected to be disabled, and can have strange side effects on drivers that expected interrupts to be disabled. This is not a bug since the documentation clearly states that only _put_sync_suspend() is safe in IRQ-safe mode. However, pm_runtime_put_sync() could be made safe when in IRQ-safe mode by releasing the spinlock but not re-enabling interrupts, which is what this patch aims to do. Problem was found when using some buggy drivers that set pm_runtime_irq_safe() and used _put_sync() in interrupts-disabled context. Reported-by: Colin Cross <ccross@google.com> Tested-by: Nishanth Menon <nm@ti.com> Signed-off-by: Kevin Hilman <khilman@ti.com> Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl> -- To unsubscribe from this list: send the line "unsubscribe linux-omap" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Keshava Munegowda <keshava_mgowda@ti.com> writes: > From: Keshava Munegowda <Keshava_mgowda@ti.com> > > The usbhs core driver does not enable/disable the intefrace and > fucntional clocks; These clocks are handled by hwmod and runtime pm, > hence insted of the clock enable/disable, the runtime pm APIS are > used. however,the port clocks are handled by the usbhs core. > > Signed-off-by: Keshava Munegowda <keshava_mgowda@ti.com> General comment: all usage of pm_runtime_put_sync() can likely be replaced by the asynchronous versions. I don't currently see why the synchronous verions are needed here. Other than that, the runtime PM parts of this look OK to me. After changing to asynchonous puts, feel free to add: Reviewed-by: Kevin Hilman <khilman@ti.com> Also, after a quick glance, it looks like this version of the series addresses the problems seen by Jassi Brar with the TLL reset[1]. Please confirm. Speaking of which, it's helpful to Cc folks who have had comments on previous versions of your series so they are sure they're previous issues are addressed. I've Cc'd Jassi Brar. Thanks, Kevin [1] http://marc.info/?l=linux-omap&m=130921260703865&w=2 -- To unsubscribe from this list: send the line "unsubscribe linux-omap" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Kevin Hilman <khilman@ti.com> writes: > Keshava Munegowda <keshava_mgowda@ti.com> writes: > >> From: Keshava Munegowda <Keshava_mgowda@ti.com> >> >> The usbhs core driver does not enable/disable the intefrace and >> fucntional clocks; These clocks are handled by hwmod and runtime pm, >> hence insted of the clock enable/disable, the runtime pm APIS are >> used. however,the port clocks are handled by the usbhs core. >> >> Signed-off-by: Keshava Munegowda <keshava_mgowda@ti.com> > > General comment: all usage of pm_runtime_put_sync() can likely be > replaced by the asynchronous versions. I don't currently see why the > synchronous verions are needed here. > > Other than that, the runtime PM parts of this look OK to me. After > changing to asynchonous puts, feel free to add: > > Reviewed-by: Kevin Hilman <khilman@ti.com> oops, this should've been in response to your v8 version. Kevin > > Also, after a quick glance, it looks like this version of the series > addresses the problems seen by Jassi Brar with the TLL reset[1]. Please > confirm. > > Speaking of which, it's helpful to Cc folks who have had comments on > previous versions of your series so they are sure they're previous > issues are addressed. I've Cc'd Jassi Brar. > > Thanks, > > Kevin > > [1] http://marc.info/?l=linux-omap&m=130921260703865&w=2 -- To unsubscribe from this list: send the line "unsubscribe linux-omap" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
On Tue, Aug 30, 2011 at 2:17 AM, Kevin Hilman <khilman@ti.com> wrote: > Kevin Hilman <khilman@ti.com> writes: > >> Keshava Munegowda <keshava_mgowda@ti.com> writes: >> >>> From: Keshava Munegowda <Keshava_mgowda@ti.com> >>> >>> The usbhs core driver does not enable/disable the intefrace and >>> fucntional clocks; These clocks are handled by hwmod and runtime pm, >>> hence insted of the clock enable/disable, the runtime pm APIS are >>> used. however,the port clocks are handled by the usbhs core. >>> >>> Signed-off-by: Keshava Munegowda <keshava_mgowda@ti.com> >> >> General comment: all usage of pm_runtime_put_sync() can likely be >> replaced by the asynchronous versions. I don't currently see why the >> synchronous verions are needed here. >> >> Other than that, the runtime PM parts of this look OK to me. After >> changing to asynchonous puts, feel free to add: >> >> Reviewed-by: Kevin Hilman <khilman@ti.com> > > oops, this should've been in response to your v8 version. > > Kevin > >> >> Also, after a quick glance, it looks like this version of the series >> addresses the problems seen by Jassi Brar with the TLL reset[1]. Please >> confirm. >> >> Speaking of which, it's helpful to Cc folks who have had comments on >> previous versions of your series so they are sure they're previous >> issues are addressed. I've Cc'd Jassi Brar. >> >> Thanks, >> >> Kevin >> >> [1] http://marc.info/?l=linux-omap&m=130921260703865&w=2 > Thanks for review; yes, its reworks of complete runtime pm of usbhs as suggested by Jassi Brar. balbi, sameo this patch series available at the branch kmg-usbhs-pm of code repository : git://gitorious.org/~kmg/mirrors/kmg-usbhs-pm.git Regards Keshava -- To unsubscribe from this list: send the line "unsubscribe linux-omap" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
diff --git a/arch/arm/plat-omap/include/plat/usb.h b/arch/arm/plat-omap/include/plat/usb.h index 17d3c93..2b66dc2 100644 --- a/arch/arm/plat-omap/include/plat/usb.h +++ b/arch/arm/plat-omap/include/plat/usb.h @@ -100,9 +100,6 @@ extern void usb_musb_init(struct omap_musb_board_data *board_data); extern void usbhs_init(const struct usbhs_omap_board_data *pdata); -extern int omap_usbhs_enable(struct device *dev); -extern void omap_usbhs_disable(struct device *dev); - extern int omap4430_phy_power(struct device *dev, int ID, int on); extern int omap4430_phy_set_clk(struct device *dev, int on); extern int omap4430_phy_init(struct device *dev); diff --git a/drivers/mfd/omap-usb-host.c b/drivers/mfd/omap-usb-host.c index 5def51c..39cfae6 100644 --- a/drivers/mfd/omap-usb-host.c +++ b/drivers/mfd/omap-usb-host.c @@ -26,6 +26,7 @@ #include <linux/spinlock.h> #include <linux/gpio.h> #include <plat/usb.h> +#include <linux/pm_runtime.h> #define USBHS_DRIVER_NAME "usbhs_omap" #define OMAP_EHCI_DEVICE "ehci-omap" @@ -146,9 +147,6 @@ struct usbhs_hcd_omap { - struct clk *usbhost_ick; - struct clk *usbhost_hs_fck; - struct clk *usbhost_fs_fck; struct clk *xclk60mhsp1_ck; struct clk *xclk60mhsp2_ck; struct clk *utmi_p1_fck; @@ -158,8 +156,6 @@ struct usbhs_hcd_omap { struct clk *usbhost_p2_fck; struct clk *usbtll_p2_fck; struct clk *init_60m_fclk; - struct clk *usbtll_fck; - struct clk *usbtll_ick; void __iomem *uhh_base; void __iomem *tll_base; @@ -168,7 +164,6 @@ struct usbhs_hcd_omap { u32 usbhs_rev; spinlock_t lock; - int count; }; /*-------------------------------------------------------------------------*/ @@ -318,269 +313,6 @@ err_end: return ret; } -/** - * usbhs_omap_probe - initialize TI-based HCDs - * - * Allocates basic resources for this USB host controller. - */ -static int __devinit usbhs_omap_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct usbhs_omap_platform_data *pdata = dev->platform_data; - struct usbhs_hcd_omap *omap; - struct resource *res; - int ret = 0; - int i; - - if (!pdata) { - dev_err(dev, "Missing platform data\n"); - ret = -ENOMEM; - goto end_probe; - } - - omap = kzalloc(sizeof(*omap), GFP_KERNEL); - if (!omap) { - dev_err(dev, "Memory allocation failed\n"); - ret = -ENOMEM; - goto end_probe; - } - - spin_lock_init(&omap->lock); - - for (i = 0; i < OMAP3_HS_USB_PORTS; i++) - omap->platdata.port_mode[i] = pdata->port_mode[i]; - - omap->platdata.ehci_data = pdata->ehci_data; - omap->platdata.ohci_data = pdata->ohci_data; - - omap->usbhost_ick = clk_get(dev, "usbhost_ick"); - if (IS_ERR(omap->usbhost_ick)) { - ret = PTR_ERR(omap->usbhost_ick); - dev_err(dev, "usbhost_ick failed error:%d\n", ret); - goto err_end; - } - - omap->usbhost_hs_fck = clk_get(dev, "hs_fck"); - if (IS_ERR(omap->usbhost_hs_fck)) { - ret = PTR_ERR(omap->usbhost_hs_fck); - dev_err(dev, "usbhost_hs_fck failed error:%d\n", ret); - goto err_usbhost_ick; - } - - omap->usbhost_fs_fck = clk_get(dev, "fs_fck"); - if (IS_ERR(omap->usbhost_fs_fck)) { - ret = PTR_ERR(omap->usbhost_fs_fck); - dev_err(dev, "usbhost_fs_fck failed error:%d\n", ret); - goto err_usbhost_hs_fck; - } - - omap->usbtll_fck = clk_get(dev, "usbtll_fck"); - if (IS_ERR(omap->usbtll_fck)) { - ret = PTR_ERR(omap->usbtll_fck); - dev_err(dev, "usbtll_fck failed error:%d\n", ret); - goto err_usbhost_fs_fck; - } - - omap->usbtll_ick = clk_get(dev, "usbtll_ick"); - if (IS_ERR(omap->usbtll_ick)) { - ret = PTR_ERR(omap->usbtll_ick); - dev_err(dev, "usbtll_ick failed error:%d\n", ret); - goto err_usbtll_fck; - } - - omap->utmi_p1_fck = clk_get(dev, "utmi_p1_gfclk"); - if (IS_ERR(omap->utmi_p1_fck)) { - ret = PTR_ERR(omap->utmi_p1_fck); - dev_err(dev, "utmi_p1_gfclk failed error:%d\n", ret); - goto err_usbtll_ick; - } - - omap->xclk60mhsp1_ck = clk_get(dev, "xclk60mhsp1_ck"); - if (IS_ERR(omap->xclk60mhsp1_ck)) { - ret = PTR_ERR(omap->xclk60mhsp1_ck); - dev_err(dev, "xclk60mhsp1_ck failed error:%d\n", ret); - goto err_utmi_p1_fck; - } - - omap->utmi_p2_fck = clk_get(dev, "utmi_p2_gfclk"); - if (IS_ERR(omap->utmi_p2_fck)) { - ret = PTR_ERR(omap->utmi_p2_fck); - dev_err(dev, "utmi_p2_gfclk failed error:%d\n", ret); - goto err_xclk60mhsp1_ck; - } - - omap->xclk60mhsp2_ck = clk_get(dev, "xclk60mhsp2_ck"); - if (IS_ERR(omap->xclk60mhsp2_ck)) { - ret = PTR_ERR(omap->xclk60mhsp2_ck); - dev_err(dev, "xclk60mhsp2_ck failed error:%d\n", ret); - goto err_utmi_p2_fck; - } - - omap->usbhost_p1_fck = clk_get(dev, "usb_host_hs_utmi_p1_clk"); - if (IS_ERR(omap->usbhost_p1_fck)) { - ret = PTR_ERR(omap->usbhost_p1_fck); - dev_err(dev, "usbhost_p1_fck failed error:%d\n", ret); - goto err_xclk60mhsp2_ck; - } - - omap->usbtll_p1_fck = clk_get(dev, "usb_tll_hs_usb_ch0_clk"); - if (IS_ERR(omap->usbtll_p1_fck)) { - ret = PTR_ERR(omap->usbtll_p1_fck); - dev_err(dev, "usbtll_p1_fck failed error:%d\n", ret); - goto err_usbhost_p1_fck; - } - - omap->usbhost_p2_fck = clk_get(dev, "usb_host_hs_utmi_p2_clk"); - if (IS_ERR(omap->usbhost_p2_fck)) { - ret = PTR_ERR(omap->usbhost_p2_fck); - dev_err(dev, "usbhost_p2_fck failed error:%d\n", ret); - goto err_usbtll_p1_fck; - } - - omap->usbtll_p2_fck = clk_get(dev, "usb_tll_hs_usb_ch1_clk"); - if (IS_ERR(omap->usbtll_p2_fck)) { - ret = PTR_ERR(omap->usbtll_p2_fck); - dev_err(dev, "usbtll_p2_fck failed error:%d\n", ret); - goto err_usbhost_p2_fck; - } - - omap->init_60m_fclk = clk_get(dev, "init_60m_fclk"); - if (IS_ERR(omap->init_60m_fclk)) { - ret = PTR_ERR(omap->init_60m_fclk); - dev_err(dev, "init_60m_fclk failed error:%d\n", ret); - goto err_usbtll_p2_fck; - } - - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "uhh"); - if (!res) { - dev_err(dev, "UHH EHCI get resource failed\n"); - ret = -ENODEV; - goto err_init_60m_fclk; - } - - omap->uhh_base = ioremap(res->start, resource_size(res)); - if (!omap->uhh_base) { - dev_err(dev, "UHH ioremap failed\n"); - ret = -ENOMEM; - goto err_init_60m_fclk; - } - - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "tll"); - if (!res) { - dev_err(dev, "UHH EHCI get resource failed\n"); - ret = -ENODEV; - goto err_tll; - } - - omap->tll_base = ioremap(res->start, resource_size(res)); - if (!omap->tll_base) { - dev_err(dev, "TLL ioremap failed\n"); - ret = -ENOMEM; - goto err_tll; - } - - platform_set_drvdata(pdev, omap); - - ret = omap_usbhs_alloc_children(pdev); - if (ret) { - dev_err(dev, "omap_usbhs_alloc_children failed\n"); - goto err_alloc; - } - - goto end_probe; - -err_alloc: - iounmap(omap->tll_base); - -err_tll: - iounmap(omap->uhh_base); - -err_init_60m_fclk: - clk_put(omap->init_60m_fclk); - -err_usbtll_p2_fck: - clk_put(omap->usbtll_p2_fck); - -err_usbhost_p2_fck: - clk_put(omap->usbhost_p2_fck); - -err_usbtll_p1_fck: - clk_put(omap->usbtll_p1_fck); - -err_usbhost_p1_fck: - clk_put(omap->usbhost_p1_fck); - -err_xclk60mhsp2_ck: - clk_put(omap->xclk60mhsp2_ck); - -err_utmi_p2_fck: - clk_put(omap->utmi_p2_fck); - -err_xclk60mhsp1_ck: - clk_put(omap->xclk60mhsp1_ck); - -err_utmi_p1_fck: - clk_put(omap->utmi_p1_fck); - -err_usbtll_ick: - clk_put(omap->usbtll_ick); - -err_usbtll_fck: - clk_put(omap->usbtll_fck); - -err_usbhost_fs_fck: - clk_put(omap->usbhost_fs_fck); - -err_usbhost_hs_fck: - clk_put(omap->usbhost_hs_fck); - -err_usbhost_ick: - clk_put(omap->usbhost_ick); - -err_end: - kfree(omap); - -end_probe: - return ret; -} - -/** - * usbhs_omap_remove - shutdown processing for UHH & TLL HCDs - * @pdev: USB Host Controller being removed - * - * Reverses the effect of usbhs_omap_probe(). - */ -static int __devexit usbhs_omap_remove(struct platform_device *pdev) -{ - struct usbhs_hcd_omap *omap = platform_get_drvdata(pdev); - - if (omap->count != 0) { - dev_err(&pdev->dev, - "Either EHCI or OHCI is still using usbhs core\n"); - return -EBUSY; - } - - iounmap(omap->tll_base); - iounmap(omap->uhh_base); - clk_put(omap->init_60m_fclk); - clk_put(omap->usbtll_p2_fck); - clk_put(omap->usbhost_p2_fck); - clk_put(omap->usbtll_p1_fck); - clk_put(omap->usbhost_p1_fck); - clk_put(omap->xclk60mhsp2_ck); - clk_put(omap->utmi_p2_fck); - clk_put(omap->xclk60mhsp1_ck); - clk_put(omap->utmi_p1_fck); - clk_put(omap->usbtll_ick); - clk_put(omap->usbtll_fck); - clk_put(omap->usbhost_fs_fck); - clk_put(omap->usbhost_hs_fck); - clk_put(omap->usbhost_ick); - kfree(omap); - - return 0; -} - static bool is_ohci_port(enum usbhs_omap_port_mode pmode) { switch (pmode) { @@ -689,30 +421,70 @@ static void usbhs_omap_tll_init(struct device *dev, u8 tll_channel_count) } } -static int usbhs_enable(struct device *dev) +static int usbhs_runtime_resume(struct device *dev) { struct usbhs_hcd_omap *omap = dev_get_drvdata(dev); struct usbhs_omap_platform_data *pdata = &omap->platdata; - unsigned long flags = 0; - int ret = 0; - unsigned long timeout; - unsigned reg; - dev_dbg(dev, "starting TI HSUSB Controller\n"); + dev_dbg(dev, "usbhs_runtime_resume\n"); + + if (!pdata) { + dev_dbg(dev, "missing platform_data\n"); + return -ENODEV; + } + + if (is_ehci_tll_mode(pdata->port_mode[0])) { + clk_enable(omap->usbhost_p1_fck); + clk_enable(omap->usbtll_p1_fck); + } + if (is_ehci_tll_mode(pdata->port_mode[1])) { + clk_enable(omap->usbhost_p2_fck); + clk_enable(omap->usbtll_p2_fck); + } + clk_enable(omap->utmi_p1_fck); + clk_enable(omap->utmi_p2_fck); + + return 0; +} + +static int usbhs_runtime_suspend(struct device *dev) +{ + struct usbhs_hcd_omap *omap = dev_get_drvdata(dev); + struct usbhs_omap_platform_data *pdata = &omap->platdata; + + dev_dbg(dev, "usbhs_runtime_suspend\n"); + if (!pdata) { dev_dbg(dev, "missing platform_data\n"); return -ENODEV; } + if (is_ehci_tll_mode(pdata->port_mode[0])) { + clk_disable(omap->usbhost_p1_fck); + clk_disable(omap->usbtll_p1_fck); + } + if (is_ehci_tll_mode(pdata->port_mode[1])) { + clk_disable(omap->usbhost_p2_fck); + clk_disable(omap->usbtll_p2_fck); + } + clk_disable(omap->utmi_p2_fck); + clk_disable(omap->utmi_p1_fck); + + return 0; +} + +static void omap_usbhs_init(struct device *dev) +{ + struct usbhs_hcd_omap *omap = dev_get_drvdata(dev); + struct usbhs_omap_platform_data *pdata = &omap->platdata; + unsigned long flags = 0; + unsigned reg; + + dev_dbg(dev, "starting TI HSUSB Controller\n"); + spin_lock_irqsave(&omap->lock, flags); - if (omap->count > 0) - goto end_count; - clk_enable(omap->usbhost_ick); - clk_enable(omap->usbhost_hs_fck); - clk_enable(omap->usbhost_fs_fck); - clk_enable(omap->usbtll_fck); - clk_enable(omap->usbtll_ick); + pm_runtime_get_sync(dev); if (pdata->ehci_data->phy_reset) { if (gpio_is_valid(pdata->ehci_data->reset_gpio_port[0])) { @@ -736,50 +508,6 @@ static int usbhs_enable(struct device *dev) omap->usbhs_rev = usbhs_read(omap->uhh_base, OMAP_UHH_REVISION); dev_dbg(dev, "OMAP UHH_REVISION 0x%x\n", omap->usbhs_rev); - /* perform TLL soft reset, and wait until reset is complete */ - usbhs_write(omap->tll_base, OMAP_USBTLL_SYSCONFIG, - OMAP_USBTLL_SYSCONFIG_SOFTRESET); - - /* Wait for TLL reset to complete */ - timeout = jiffies + msecs_to_jiffies(1000); - while (!(usbhs_read(omap->tll_base, OMAP_USBTLL_SYSSTATUS) - & OMAP_USBTLL_SYSSTATUS_RESETDONE)) { - cpu_relax(); - - if (time_after(jiffies, timeout)) { - dev_dbg(dev, "operation timed out\n"); - ret = -EINVAL; - goto err_tll; - } - } - - dev_dbg(dev, "TLL RESET DONE\n"); - - /* (1<<3) = no idle mode only for initial debugging */ - usbhs_write(omap->tll_base, OMAP_USBTLL_SYSCONFIG, - OMAP_USBTLL_SYSCONFIG_ENAWAKEUP | - OMAP_USBTLL_SYSCONFIG_SIDLEMODE | - OMAP_USBTLL_SYSCONFIG_AUTOIDLE); - - /* Put UHH in NoIdle/NoStandby mode */ - reg = usbhs_read(omap->uhh_base, OMAP_UHH_SYSCONFIG); - if (is_omap_usbhs_rev1(omap)) { - reg |= (OMAP_UHH_SYSCONFIG_ENAWAKEUP - | OMAP_UHH_SYSCONFIG_SIDLEMODE - | OMAP_UHH_SYSCONFIG_CACTIVITY - | OMAP_UHH_SYSCONFIG_MIDLEMODE); - reg &= ~OMAP_UHH_SYSCONFIG_AUTOIDLE; - - - } else if (is_omap_usbhs_rev2(omap)) { - reg &= ~OMAP4_UHH_SYSCONFIG_IDLEMODE_CLEAR; - reg |= OMAP4_UHH_SYSCONFIG_NOIDLE; - reg &= ~OMAP4_UHH_SYSCONFIG_STDBYMODE_CLEAR; - reg |= OMAP4_UHH_SYSCONFIG_NOSTDBY; - } - - usbhs_write(omap->uhh_base, OMAP_UHH_SYSCONFIG, reg); - reg = usbhs_read(omap->uhh_base, OMAP_UHH_HOSTCONFIG); /* setup ULPI bypass and burst configurations */ reg |= (OMAP_UHH_HOSTCONFIG_INCR4_BURST_EN @@ -825,49 +553,6 @@ static int usbhs_enable(struct device *dev) reg &= ~OMAP4_P1_MODE_CLEAR; reg &= ~OMAP4_P2_MODE_CLEAR; - if (is_ehci_phy_mode(pdata->port_mode[0])) { - ret = clk_set_parent(omap->utmi_p1_fck, - omap->xclk60mhsp1_ck); - if (ret != 0) { - dev_err(dev, "xclk60mhsp1_ck set parent" - "failed error:%d\n", ret); - goto err_tll; - } - } else if (is_ehci_tll_mode(pdata->port_mode[0])) { - ret = clk_set_parent(omap->utmi_p1_fck, - omap->init_60m_fclk); - if (ret != 0) { - dev_err(dev, "init_60m_fclk set parent" - "failed error:%d\n", ret); - goto err_tll; - } - clk_enable(omap->usbhost_p1_fck); - clk_enable(omap->usbtll_p1_fck); - } - - if (is_ehci_phy_mode(pdata->port_mode[1])) { - ret = clk_set_parent(omap->utmi_p2_fck, - omap->xclk60mhsp2_ck); - if (ret != 0) { - dev_err(dev, "xclk60mhsp1_ck set parent" - "failed error:%d\n", ret); - goto err_tll; - } - } else if (is_ehci_tll_mode(pdata->port_mode[1])) { - ret = clk_set_parent(omap->utmi_p2_fck, - omap->init_60m_fclk); - if (ret != 0) { - dev_err(dev, "init_60m_fclk set parent" - "failed error:%d\n", ret); - goto err_tll; - } - clk_enable(omap->usbhost_p2_fck); - clk_enable(omap->usbtll_p2_fck); - } - - clk_enable(omap->utmi_p1_fck); - clk_enable(omap->utmi_p2_fck); - if (is_ehci_tll_mode(pdata->port_mode[0]) || (is_ohci_port(pdata->port_mode[0]))) reg |= OMAP4_P1_MODE_TLL; @@ -913,12 +598,15 @@ static int usbhs_enable(struct device *dev) (pdata->ehci_data->reset_gpio_port[1], 1); } -end_count: - omap->count++; + pm_runtime_put_sync(dev); spin_unlock_irqrestore(&omap->lock, flags); - return 0; +} + +static void omap_usbhs_deinit(struct device *dev) +{ + struct usbhs_hcd_omap *omap = dev_get_drvdata(dev); + struct usbhs_omap_platform_data *pdata = &omap->platdata; -err_tll: if (pdata->ehci_data->phy_reset) { if (gpio_is_valid(pdata->ehci_data->reset_gpio_port[0])) gpio_free(pdata->ehci_data->reset_gpio_port[0]); @@ -926,123 +614,257 @@ err_tll: if (gpio_is_valid(pdata->ehci_data->reset_gpio_port[1])) gpio_free(pdata->ehci_data->reset_gpio_port[1]); } - - clk_disable(omap->usbtll_ick); - clk_disable(omap->usbtll_fck); - clk_disable(omap->usbhost_fs_fck); - clk_disable(omap->usbhost_hs_fck); - clk_disable(omap->usbhost_ick); - spin_unlock_irqrestore(&omap->lock, flags); - return ret; } -static void usbhs_disable(struct device *dev) + +/** + * usbhs_omap_probe - initialize TI-based HCDs + * + * Allocates basic resources for this USB host controller. + */ +static int __devinit usbhs_omap_probe(struct platform_device *pdev) { - struct usbhs_hcd_omap *omap = dev_get_drvdata(dev); - struct usbhs_omap_platform_data *pdata = &omap->platdata; - unsigned long flags = 0; - unsigned long timeout; + struct device *dev = &pdev->dev; + struct usbhs_omap_platform_data *pdata = dev->platform_data; + struct usbhs_hcd_omap *omap; + struct resource *res; + int ret = 0; + int i; - dev_dbg(dev, "stopping TI HSUSB Controller\n"); + if (!pdata) { + dev_err(dev, "Missing platform data\n"); + ret = -ENOMEM; + goto end_probe; + } - spin_lock_irqsave(&omap->lock, flags); + omap = kzalloc(sizeof(*omap), GFP_KERNEL); + if (!omap) { + dev_err(dev, "Memory allocation failed\n"); + ret = -ENOMEM; + goto end_probe; + } + + spin_lock_init(&omap->lock); + + for (i = 0; i < OMAP3_HS_USB_PORTS; i++) + omap->platdata.port_mode[i] = pdata->port_mode[i]; - if (omap->count == 0) - goto end_disble; + omap->platdata.ehci_data = pdata->ehci_data; + omap->platdata.ohci_data = pdata->ohci_data; - omap->count--; + pm_runtime_enable(dev); - if (omap->count != 0) - goto end_disble; + omap->utmi_p1_fck = clk_get(dev, "utmi_p1_gfclk"); + if (IS_ERR(omap->utmi_p1_fck)) { + ret = PTR_ERR(omap->utmi_p1_fck); + dev_err(dev, "utmi_p1_gfclk failed error:%d\n", ret); + goto err_end; + } + + omap->xclk60mhsp1_ck = clk_get(dev, "xclk60mhsp1_ck"); + if (IS_ERR(omap->xclk60mhsp1_ck)) { + ret = PTR_ERR(omap->xclk60mhsp1_ck); + dev_err(dev, "xclk60mhsp1_ck failed error:%d\n", ret); + goto err_utmi_p1_fck; + } - /* Reset OMAP modules for insmod/rmmod to work */ - usbhs_write(omap->uhh_base, OMAP_UHH_SYSCONFIG, - is_omap_usbhs_rev2(omap) ? - OMAP4_UHH_SYSCONFIG_SOFTRESET : - OMAP_UHH_SYSCONFIG_SOFTRESET); + omap->utmi_p2_fck = clk_get(dev, "utmi_p2_gfclk"); + if (IS_ERR(omap->utmi_p2_fck)) { + ret = PTR_ERR(omap->utmi_p2_fck); + dev_err(dev, "utmi_p2_gfclk failed error:%d\n", ret); + goto err_xclk60mhsp1_ck; + } - timeout = jiffies + msecs_to_jiffies(100); - while (!(usbhs_read(omap->uhh_base, OMAP_UHH_SYSSTATUS) - & (1 << 0))) { - cpu_relax(); + omap->xclk60mhsp2_ck = clk_get(dev, "xclk60mhsp2_ck"); + if (IS_ERR(omap->xclk60mhsp2_ck)) { + ret = PTR_ERR(omap->xclk60mhsp2_ck); + dev_err(dev, "xclk60mhsp2_ck failed error:%d\n", ret); + goto err_utmi_p2_fck; + } - if (time_after(jiffies, timeout)) - dev_dbg(dev, "operation timed out\n"); + omap->usbhost_p1_fck = clk_get(dev, "usb_host_hs_utmi_p1_clk"); + if (IS_ERR(omap->usbhost_p1_fck)) { + ret = PTR_ERR(omap->usbhost_p1_fck); + dev_err(dev, "usbhost_p1_fck failed error:%d\n", ret); + goto err_xclk60mhsp2_ck; } - while (!(usbhs_read(omap->uhh_base, OMAP_UHH_SYSSTATUS) - & (1 << 1))) { - cpu_relax(); + omap->usbtll_p1_fck = clk_get(dev, "usb_tll_hs_usb_ch0_clk"); + if (IS_ERR(omap->usbtll_p1_fck)) { + ret = PTR_ERR(omap->usbtll_p1_fck); + dev_err(dev, "usbtll_p1_fck failed error:%d\n", ret); + goto err_usbhost_p1_fck; + } - if (time_after(jiffies, timeout)) - dev_dbg(dev, "operation timed out\n"); + omap->usbhost_p2_fck = clk_get(dev, "usb_host_hs_utmi_p2_clk"); + if (IS_ERR(omap->usbhost_p2_fck)) { + ret = PTR_ERR(omap->usbhost_p2_fck); + dev_err(dev, "usbhost_p2_fck failed error:%d\n", ret); + goto err_usbtll_p1_fck; } - while (!(usbhs_read(omap->uhh_base, OMAP_UHH_SYSSTATUS) - & (1 << 2))) { - cpu_relax(); + omap->usbtll_p2_fck = clk_get(dev, "usb_tll_hs_usb_ch1_clk"); + if (IS_ERR(omap->usbtll_p2_fck)) { + ret = PTR_ERR(omap->usbtll_p2_fck); + dev_err(dev, "usbtll_p2_fck failed error:%d\n", ret); + goto err_usbhost_p2_fck; + } - if (time_after(jiffies, timeout)) - dev_dbg(dev, "operation timed out\n"); + omap->init_60m_fclk = clk_get(dev, "init_60m_fclk"); + if (IS_ERR(omap->init_60m_fclk)) { + ret = PTR_ERR(omap->init_60m_fclk); + dev_err(dev, "init_60m_fclk failed error:%d\n", ret); + goto err_usbtll_p2_fck; } - usbhs_write(omap->tll_base, OMAP_USBTLL_SYSCONFIG, (1 << 1)); + if (is_ehci_phy_mode(pdata->port_mode[0])) { + /* for OMAP3 , the clk set paretn fails */ + ret = clk_set_parent(omap->utmi_p1_fck, + omap->xclk60mhsp1_ck); + if (ret != 0) + dev_err(dev, "xclk60mhsp1_ck set parent" + "failed error:%d\n", ret); + } else if (is_ehci_tll_mode(pdata->port_mode[0])) { + ret = clk_set_parent(omap->utmi_p1_fck, + omap->init_60m_fclk); + if (ret != 0) + dev_err(dev, "init_60m_fclk set parent" + "failed error:%d\n", ret); + } - while (!(usbhs_read(omap->tll_base, OMAP_USBTLL_SYSSTATUS) - & (1 << 0))) { - cpu_relax(); + if (is_ehci_phy_mode(pdata->port_mode[1])) { + ret = clk_set_parent(omap->utmi_p2_fck, + omap->xclk60mhsp2_ck); + if (ret != 0) + dev_err(dev, "xclk60mhsp2_ck set parent" + "failed error:%d\n", ret); + } else if (is_ehci_tll_mode(pdata->port_mode[1])) { + ret = clk_set_parent(omap->utmi_p2_fck, + omap->init_60m_fclk); + if (ret != 0) + dev_err(dev, "init_60m_fclk set parent" + "failed error:%d\n", ret); + } - if (time_after(jiffies, timeout)) - dev_dbg(dev, "operation timed out\n"); + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "uhh"); + if (!res) { + dev_err(dev, "UHH EHCI get resource failed\n"); + ret = -ENODEV; + goto err_init_60m_fclk; } - if (is_omap_usbhs_rev2(omap)) { - if (is_ehci_tll_mode(pdata->port_mode[0])) - clk_disable(omap->usbtll_p1_fck); - if (is_ehci_tll_mode(pdata->port_mode[1])) - clk_disable(omap->usbtll_p2_fck); - clk_disable(omap->utmi_p2_fck); - clk_disable(omap->utmi_p1_fck); + omap->uhh_base = ioremap(res->start, resource_size(res)); + if (!omap->uhh_base) { + dev_err(dev, "UHH ioremap failed\n"); + ret = -ENOMEM; + goto err_init_60m_fclk; } - clk_disable(omap->usbtll_ick); - clk_disable(omap->usbtll_fck); - clk_disable(omap->usbhost_fs_fck); - clk_disable(omap->usbhost_hs_fck); - clk_disable(omap->usbhost_ick); + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "tll"); + if (!res) { + dev_err(dev, "UHH EHCI get resource failed\n"); + ret = -ENODEV; + goto err_tll; + } - /* The gpio_free migh sleep; so unlock the spinlock */ - spin_unlock_irqrestore(&omap->lock, flags); + omap->tll_base = ioremap(res->start, resource_size(res)); + if (!omap->tll_base) { + dev_err(dev, "TLL ioremap failed\n"); + ret = -ENOMEM; + goto err_tll; + } - if (pdata->ehci_data->phy_reset) { - if (gpio_is_valid(pdata->ehci_data->reset_gpio_port[0])) - gpio_free(pdata->ehci_data->reset_gpio_port[0]); + platform_set_drvdata(pdev, omap); - if (gpio_is_valid(pdata->ehci_data->reset_gpio_port[1])) - gpio_free(pdata->ehci_data->reset_gpio_port[1]); + ret = omap_usbhs_alloc_children(pdev); + if (ret) { + dev_err(dev, "omap_usbhs_alloc_children failed\n"); + goto err_alloc; } - return; -end_disble: - spin_unlock_irqrestore(&omap->lock, flags); -} + omap_usbhs_init(dev); -int omap_usbhs_enable(struct device *dev) -{ - return usbhs_enable(dev->parent); + goto end_probe; + +err_alloc: + iounmap(omap->tll_base); + +err_tll: + iounmap(omap->uhh_base); + +err_init_60m_fclk: + clk_put(omap->init_60m_fclk); + +err_usbtll_p2_fck: + clk_put(omap->usbtll_p2_fck); + +err_usbhost_p2_fck: + clk_put(omap->usbhost_p2_fck); + +err_usbtll_p1_fck: + clk_put(omap->usbtll_p1_fck); + +err_usbhost_p1_fck: + clk_put(omap->usbhost_p1_fck); + +err_xclk60mhsp2_ck: + clk_put(omap->xclk60mhsp2_ck); + +err_utmi_p2_fck: + clk_put(omap->utmi_p2_fck); + +err_xclk60mhsp1_ck: + clk_put(omap->xclk60mhsp1_ck); + +err_utmi_p1_fck: + clk_put(omap->utmi_p1_fck); + +err_end: + pm_runtime_disable(dev); + kfree(omap); + +end_probe: + return ret; } -EXPORT_SYMBOL_GPL(omap_usbhs_enable); -void omap_usbhs_disable(struct device *dev) +/** + * usbhs_omap_remove - shutdown processing for UHH & TLL HCDs + * @pdev: USB Host Controller being removed + * + * Reverses the effect of usbhs_omap_probe(). + */ +static int __devexit usbhs_omap_remove(struct platform_device *pdev) { - usbhs_disable(dev->parent); + struct usbhs_hcd_omap *omap = platform_get_drvdata(pdev); + + omap_usbhs_deinit(&pdev->dev); + iounmap(omap->tll_base); + iounmap(omap->uhh_base); + clk_put(omap->init_60m_fclk); + clk_put(omap->usbtll_p2_fck); + clk_put(omap->usbhost_p2_fck); + clk_put(omap->usbtll_p1_fck); + clk_put(omap->usbhost_p1_fck); + clk_put(omap->xclk60mhsp2_ck); + clk_put(omap->utmi_p2_fck); + clk_put(omap->xclk60mhsp1_ck); + clk_put(omap->utmi_p1_fck); + pm_runtime_disable(&pdev->dev); + kfree(omap); + + return 0; } -EXPORT_SYMBOL_GPL(omap_usbhs_disable); + +static const struct dev_pm_ops usbhsomap_dev_pm_ops = { + .runtime_suspend = usbhs_runtime_suspend, + .runtime_resume = usbhs_runtime_resume, +}; static struct platform_driver usbhs_omap_driver = { .driver = { .name = (char *)usbhs_driver_name, .owner = THIS_MODULE, + .pm = &usbhsomap_dev_pm_ops, }, .remove = __exit_p(usbhs_omap_remove), }; diff --git a/drivers/usb/host/ehci-omap.c b/drivers/usb/host/ehci-omap.c index 4524032..6551d30 100644 --- a/drivers/usb/host/ehci-omap.c +++ b/drivers/usb/host/ehci-omap.c @@ -41,6 +41,7 @@ #include <linux/usb/ulpi.h> #include <plat/usb.h> #include <linux/regulator/consumer.h> +#include <linux/pm_runtime.h> /* EHCI Register Set */ #define EHCI_INSNREG04 (0xA0) @@ -190,11 +191,8 @@ static int ehci_hcd_omap_probe(struct platform_device *pdev) } } - ret = omap_usbhs_enable(dev); - if (ret) { - dev_err(dev, "failed to start usbhs with err %d\n", ret); - goto err_enable; - } + pm_runtime_enable(dev); + pm_runtime_get_sync(dev); /* * An undocumented "feature" in the OMAP3 EHCI controller, @@ -240,11 +238,8 @@ static int ehci_hcd_omap_probe(struct platform_device *pdev) return 0; err_add_hcd: - omap_usbhs_disable(dev); - -err_enable: disable_put_regulator(pdata); - usb_put_hcd(hcd); + pm_runtime_put_sync(dev); err_io: iounmap(regs); @@ -266,10 +261,12 @@ static int ehci_hcd_omap_remove(struct platform_device *pdev) struct usb_hcd *hcd = dev_get_drvdata(dev); usb_remove_hcd(hcd); - omap_usbhs_disable(dev); disable_put_regulator(dev->platform_data); - iounmap(hcd->regs); usb_put_hcd(hcd); + iounmap(hcd->regs); + pm_runtime_put_sync(dev); + pm_runtime_disable(dev); + return 0; } diff --git a/drivers/usb/host/ohci-omap3.c b/drivers/usb/host/ohci-omap3.c index 6048f2f..58e3dae 100644 --- a/drivers/usb/host/ohci-omap3.c +++ b/drivers/usb/host/ohci-omap3.c @@ -31,6 +31,7 @@ #include <linux/platform_device.h> #include <plat/usb.h> +#include <linux/pm_runtime.h> /*-------------------------------------------------------------------------*/ @@ -172,11 +173,8 @@ static int __devinit ohci_hcd_omap3_probe(struct platform_device *pdev) hcd->rsrc_len = resource_size(res); hcd->regs = regs; - ret = omap_usbhs_enable(dev); - if (ret) { - dev_dbg(dev, "failed to start ohci\n"); - goto err_end; - } + pm_runtime_enable(dev); + pm_runtime_get_sync(dev); ohci_hcd_init(hcd_to_ohci(hcd)); @@ -189,7 +187,7 @@ static int __devinit ohci_hcd_omap3_probe(struct platform_device *pdev) return 0; err_add_hcd: - omap_usbhs_disable(dev); + pm_runtime_get_sync(dev); err_end: usb_put_hcd(hcd); @@ -220,9 +218,9 @@ static int __devexit ohci_hcd_omap3_remove(struct platform_device *pdev) iounmap(hcd->regs); usb_remove_hcd(hcd); - omap_usbhs_disable(dev); + pm_runtime_put_sync(dev); + pm_runtime_disable(dev); usb_put_hcd(hcd); - return 0; }