diff options
author | Jonas Aberg <jonas.aberg@stericsson.com> | 2012-01-24 11:08:39 +0530 |
---|---|---|
committer | Philippe Langlais <philippe.langlais@stericsson.com> | 2012-05-22 11:03:17 +0200 |
commit | 30600535dfe60c8092d799fdfa3fc16f47393861 (patch) | |
tree | 15439e297b78e83f0d1500483758f9868a4c5b99 /drivers/usb | |
parent | 395aea0fb7e8317b35530551cbb03d9e2fc962b7 (diff) |
u8500: musb: Add pm runtime support
Add pm runtime support for musb, including both pin and
clock handling.
ST-Ericsson Linux next: -
ST-Ericsson ID: 370128, 375498
ST-Ericsson FOSS-OUT ID: Trivial
Change-Id: Idf72a6f226d1309d5a13b358e653ee97469e5ae6
Signed-off-by: Jonas Aaberg <jonas.aberg@stericsson.com>
Signed-off-by: Sakethram Bommisetti <sakethram.bommisetti@stericsson.com>
Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/44709
Reviewed-by: Srinidhi KASAGAR <srinidhi.kasagar@stericsson.com>
Diffstat (limited to 'drivers/usb')
-rw-r--r-- | drivers/usb/musb/musb_core.c | 6 | ||||
-rw-r--r-- | drivers/usb/musb/ux500.c | 117 | ||||
-rw-r--r-- | drivers/usb/otg/ab8500-usb.c | 35 |
3 files changed, 111 insertions, 47 deletions
diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index 5faac98ca30..c5bf28afd86 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -2371,8 +2371,12 @@ static const struct dev_pm_ops musb_dev_pm_ops = { .runtime_suspend = musb_runtime_suspend, .runtime_resume = musb_runtime_resume, }; - +#ifdef CONFIG_UX500_SOC_DB8500 +#define MUSB_DEV_PM_OPS (&musb_dev_pm_ops) +#else #define MUSB_DEV_PM_OPS NULL +#endif + #else #define MUSB_DEV_PM_OPS NULL #endif diff --git a/drivers/usb/musb/ux500.c b/drivers/usb/musb/ux500.c index ae6be2eaa09..5d0c5eda128 100644 --- a/drivers/usb/musb/ux500.c +++ b/drivers/usb/musb/ux500.c @@ -25,6 +25,8 @@ #include <linux/clk.h> #include <linux/io.h> #include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <mach/id.h> #include <mach/usb.h> #include "musb_core.h" @@ -40,19 +42,23 @@ struct ux500_glue { #define glue_to_musb(g) platform_get_drvdata(g->musb) static struct timer_list notify_timer; -struct musb_context_registers context; +static struct musb_context_registers context; +static bool context_stored; struct musb *_musb; -void ux500_store_context(struct musb *musb) + +static void ux500_store_context(struct musb *musb) { #ifdef CONFIG_PM int i; void __iomem *musb_base; void __iomem *epio; - if (musb != NULL) - _musb = musb; - else - return; + if (cpu_is_u5500()) { + if (musb != NULL) + _musb = musb; + else + return; + } musb_base = musb->mregs; @@ -124,17 +130,21 @@ void ux500_store_context(struct musb *musb) musb_read_rxhubport(musb_base, i); } } + context_stored = true; #endif } -void ux500_restore_context(void) + +void ux500_restore_context(struct musb *musb) { #ifdef CONFIG_PM int i; - struct musb *musb; void __iomem *musb_base; void __iomem *ep_target_regs; void __iomem *epio; + if (!context_stored) + return; + if (_musb != NULL) musb = _musb; else @@ -222,7 +232,8 @@ static void musb_notify_idle(unsigned long _musb) unsigned long flags; u8 devctl; - + dev_dbg(musb->controller, "musb_notify_idle %s", + otg_state_string(musb->xceiv->state)); spin_lock_irqsave(&musb->lock, flags); devctl = musb_readb(musb->mregs, MUSB_DEVCTL); @@ -235,6 +246,10 @@ static void musb_notify_idle(unsigned long _musb) musb->xceiv->state = OTG_STATE_A_IDLE; MUSB_HST_MODE(musb); } + if (cpu_is_u8500()) { + pm_runtime_mark_last_busy(musb->controller); + pm_runtime_put_autosuspend(musb->controller); + } break; case OTG_STATE_A_SUSPEND: @@ -250,7 +265,14 @@ static int musb_otg_notifications(struct notifier_block *nb, { struct musb *musb = container_of(nb, struct musb, nb); + dev_dbg(musb->controller, "musb_otg_notifications %ld %s\n", + event, otg_state_string(musb->xceiv->state)); switch (event) { + + case USB_EVENT_PREPARE: + pm_runtime_get_sync(musb->controller); + ux500_restore_context(musb); + break; case USB_EVENT_ID: case USB_EVENT_RIDA: dev_dbg(musb->controller, "ID GND\n"); @@ -263,12 +285,17 @@ static int musb_otg_notifications(struct notifier_block *nb, dev_dbg(musb->controller, "VBUS Connect\n"); break; - +/* case USB_EVENT_RIDB: FIXME, not yet managed */ case USB_EVENT_NONE: dev_dbg(musb->controller, "VBUS Disconnect\n"); - if (is_otg_enabled(musb)) + if (is_otg_enabled(musb) && musb->is_host) ux500_musb_set_vbus(musb, 0); - + else + musb->xceiv->state = OTG_STATE_B_IDLE; + break; + case USB_EVENT_CLEAN: + pm_runtime_mark_last_busy(musb->controller); + pm_runtime_put_autosuspend(musb->controller); break; default: dev_dbg(musb->controller, "ID float\n"); @@ -321,11 +348,9 @@ static void ux500_musb_set_vbus(struct musb *musb, int is_on) /* NOTE: we're skipping A_WAIT_VFALL -> A_IDLE and * jumping right to B_IDLE... */ - musb->xceiv->default_a = 0; musb->xceiv->state = OTG_STATE_B_IDLE; devctl &= ~MUSB_DEVCTL_SESSION; - MUSB_DEV_MODE(musb); } musb_writeb(musb->mregs, MUSB_DEVCTL, devctl); @@ -400,12 +425,13 @@ static struct usb_ep *ux500_musb_configure_endpoints(struct musb *musb, static int ux500_musb_init(struct musb *musb) { int status; + musb->xceiv = usb_get_transceiver(); if (!musb->xceiv) { pr_err("HS USB OTG: no transceiver configured\n"); return -ENODEV; } - + pm_runtime_get_noresume(musb->controller); musb->nb.notifier_call = musb_otg_notifications; status = otg_register_notifier(musb->xceiv, &musb->nb); @@ -418,6 +444,7 @@ static int ux500_musb_init(struct musb *musb) return 0; err1: + pm_runtime_disable(musb->controller); return status; } @@ -516,12 +543,12 @@ static int __devinit ux500_probe(struct platform_device *pdev) dev_err(&pdev->dev, "failed to register musb device\n"); goto err4; } + pm_runtime_enable(&pdev->dev); return 0; - err4: - clk_disable(clk); - + if (cpu_is_u5500()) + clk_disable(clk); err3: clk_put(clk); @@ -541,8 +568,12 @@ static int __devexit ux500_remove(struct platform_device *pdev) platform_device_del(glue->musb); platform_device_put(glue->musb); - clk_disable(glue->clk); + if (cpu_is_u5500()) + clk_disable(glue->clk); clk_put(glue->clk); + pm_runtime_put(&pdev->dev); + pm_runtime_disable(&pdev->dev); + kfree(glue); return 0; @@ -562,8 +593,15 @@ static int ux500_suspend(struct device *dev) struct musb *musb = glue_to_musb(glue); usb_phy_set_suspend(musb->xceiv, 1); - clk_disable(glue->clk); + if (cpu_is_u5500()) + /* + * Since this clock is in the APE domain, it will + * automatically be disabled on suspend. + * (And enabled on resume automatically.) + */ + clk_disable(glue->clk); + dev_dbg(dev, "ux500_suspend\n"); return 0; } @@ -578,20 +616,51 @@ static int ux500_resume(struct device *dev) { struct ux500_glue *glue = dev_get_drvdata(dev); struct musb *musb = glue_to_musb(glue); - int ret; + + if (cpu_is_u5500()) + /* No point in propagating errors on resume */ + (void) clk_enable(glue->clk); + dev_dbg(dev, "ux500_resume\n"); + + usb_phy_set_suspend(musb->xceiv, 0); + + return 0; +} +#ifdef CONFIG_UX500_SOC_DB8500 +static int ux500_musb_runtime_resume(struct device *dev) +{ + struct ux500_glue *glue = dev_get_drvdata(dev); + int ret; + + if (cpu_is_u5500()) + return 0; ret = clk_enable(glue->clk); if (ret) { - dev_err(dev, "failed to enable clock\n"); + dev_dbg(dev, "Unable to enable clk\n"); return ret; } + dev_dbg(dev, "ux500_musb_runtime_resume\n"); + return 0; +} - usb_phy_set_suspend(musb->xceiv, 0); +static int ux500_musb_runtime_suspend(struct device *dev) +{ + struct ux500_glue *glue = dev_get_drvdata(dev); + if (cpu_is_u5500()) + return 0; + + clk_disable(glue->clk); + dev_dbg(dev, "ux500_musb_runtime_suspend\n"); return 0; } - +#endif static const struct dev_pm_ops ux500_pm_ops = { +#ifdef CONFIG_UX500_SOC_DB8500 + SET_RUNTIME_PM_OPS(ux500_musb_runtime_suspend, + ux500_musb_runtime_resume, NULL) +#endif .suspend = ux500_suspend, .resume = ux500_resume, }; diff --git a/drivers/usb/otg/ab8500-usb.c b/drivers/usb/otg/ab8500-usb.c index 5c5b6a9dcdb..659fca15976 100644 --- a/drivers/usb/otg/ab8500-usb.c +++ b/drivers/usb/otg/ab8500-usb.c @@ -29,19 +29,15 @@ #include <linux/notifier.h> #include <linux/interrupt.h> #include <linux/delay.h> +#include <linux/io.h> #include <linux/clk.h> #include <linux/err.h> #include <linux/mfd/abx500.h> #include <linux/mfd/abx500/ab8500.h> -#include <linux/regulator/consumer.h> #include <linux/mfd/dbx500-prcmu.h> #include <linux/kernel_stat.h> #include <linux/pm_qos.h> -#include <asm/io.h> - -#include <mach/usb.h> - #define AB8500_MAIN_WD_CTRL_REG 0x01 #define AB8500_USB_LINE_STAT_REG 0x80 #define AB8500_USB_PHY_CTRL_REG 0x8A @@ -126,7 +122,6 @@ struct ab8500_usb { struct regulator *v_ape; struct regulator *v_musb; struct regulator *v_ulpi; - struct abx500_usbgpio_platform_data *usb_gpio; struct delayed_work work_usb_workaround; bool sysfs_flag; }; @@ -245,7 +240,6 @@ static void ab8500_usb_phy_enable(struct ab8500_usb *ab, bool sel_host) bit = sel_host ? AB8500_BIT_PHY_CTRL_HOST_EN : AB8500_BIT_PHY_CTRL_DEVICE_EN; - ab->usb_gpio->enable(); clk_enable(ab->sysclk); ab8500_usb_regulator_ctrl(ab, sel_host, true); @@ -299,8 +293,6 @@ static void ab8500_usb_phy_disable(struct ab8500_usb *ab, bool sel_host) ab8500_usb_regulator_ctrl(ab, sel_host, false); - ab->usb_gpio->disable(); - prcmu_qos_update_requirement(PRCMU_QOS_APE_OPP, (char *)dev_name(ab->dev), 50); @@ -360,12 +352,16 @@ static int ab8500_usb_link_status_update(struct ab8500_usb *ab) if (ab->mode == USB_HOST) { ab->mode = USB_PERIPHERAL; ab8500_usb_host_phy_dis(ab); - ux500_restore_context(); + atomic_notifier_call_chain(&ab->otg.notifier, + USB_EVENT_PREPARE, + &ab->vbus_draw); ab8500_usb_peri_phy_en(ab); } if (ab->mode == USB_IDLE) { ab->mode = USB_PERIPHERAL; - ux500_restore_context(); + atomic_notifier_call_chain(&ab->otg.notifier, + USB_EVENT_PREPARE, + &ab->vbus_draw); ab8500_usb_peri_phy_en(ab); } if (event != USB_EVENT_RIDC) @@ -378,12 +374,16 @@ static int ab8500_usb_link_status_update(struct ab8500_usb *ab) if (ab->mode == USB_PERIPHERAL) { ab->mode = USB_HOST; ab8500_usb_peri_phy_dis(ab); - ux500_restore_context(); + atomic_notifier_call_chain(&ab->otg.notifier, + USB_EVENT_PREPARE, + &ab->vbus_draw); ab8500_usb_host_phy_en(ab); } if (ab->mode == USB_IDLE) { ab->mode = USB_HOST; - ux500_restore_context(); + atomic_notifier_call_chain(&ab->otg.notifier, + USB_EVENT_PREPARE, + &ab->vbus_draw); ab8500_usb_host_phy_en(ab); } ab->phy.otg->default_a = true; @@ -794,8 +794,6 @@ static int __devinit ab8500_usb_probe(struct platform_device *pdev) { struct ab8500_usb *ab; struct usb_otg *otg; - struct ab8500_platform_data *ab8500_pdata = - dev_get_platdata(pdev->dev.parent); int err; int rev; int ret = -1; @@ -831,7 +829,6 @@ static int __devinit ab8500_usb_probe(struct platform_device *pdev) otg->phy = &ab->phy; otg->set_host = ab8500_usb_set_host; otg->set_peripheral = ab8500_usb_set_peripheral; - ab->usb_gpio = ab8500_pdata->usb; ab->sysfs_flag = true; platform_set_drvdata(pdev, ab); @@ -917,10 +914,6 @@ static int __devinit ab8500_usb_probe(struct platform_device *pdev) /* Needed to enable ID detection. */ ab8500_usb_wd_workaround(ab); - err = ab->usb_gpio->get(ab->dev); - if (err < 0) - goto fail3; - prcmu_qos_add_requirement(PRCMU_QOS_APE_OPP, (char *)dev_name(ab->dev), 50); dev_info(&pdev->dev, "revision 0x%2x driver initialized\n", ab->rev); @@ -969,8 +962,6 @@ static int __devexit ab8500_usb_remove(struct platform_device *pdev) ab8500_usb_regulator_put(ab); - ab->usb_gpio->put(); - platform_set_drvdata(pdev, NULL); kfree(ab->phy.otg); |