summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJonas Aberg <jonas.aberg@stericsson.com>2012-01-24 11:08:39 +0530
committerPhilippe Langlais <philippe.langlais@stericsson.com>2012-05-22 11:03:17 +0200
commit30600535dfe60c8092d799fdfa3fc16f47393861 (patch)
tree15439e297b78e83f0d1500483758f9868a4c5b99
parent395aea0fb7e8317b35530551cbb03d9e2fc962b7 (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>
-rw-r--r--arch/arm/mach-ux500/board-mop500.c2
-rw-r--r--arch/arm/mach-ux500/include/mach/usb.h6
-rw-r--r--arch/arm/mach-ux500/usb.c4
-rw-r--r--drivers/usb/musb/musb_core.c6
-rw-r--r--drivers/usb/musb/ux500.c117
-rw-r--r--drivers/usb/otg/ab8500-usb.c35
6 files changed, 119 insertions, 51 deletions
diff --git a/arch/arm/mach-ux500/board-mop500.c b/arch/arm/mach-ux500/board-mop500.c
index 5c1da28427b..77d03c1fbd0 100644
--- a/arch/arm/mach-ux500/board-mop500.c
+++ b/arch/arm/mach-ux500/board-mop500.c
@@ -53,7 +53,6 @@
#include "devices-db8500.h"
#include "board-mop500.h"
#include "board-mop500-regulators.h"
-#include "board-ux500-usb.h"
static struct gpio_led snowball_led_array[] = {
{
@@ -195,7 +194,6 @@ static struct ab8500_platform_data ab8500_platdata = {
.regulator = ab8500_regulators,
.num_regulator = ARRAY_SIZE(ab8500_regulators),
.gpio = &ab8500_gpio_pdata,
- .usb = &abx500_usbgpio_plat_data,
};
static struct resource ab8500_resources[] = {
diff --git a/arch/arm/mach-ux500/include/mach/usb.h b/arch/arm/mach-ux500/include/mach/usb.h
index 2d1e54070f2..700d9dbb1be 100644
--- a/arch/arm/mach-ux500/include/mach/usb.h
+++ b/arch/arm/mach-ux500/include/mach/usb.h
@@ -12,6 +12,8 @@
#define UX500_MUSB_DMA_NUM_RX_CHANNELS 8
#define UX500_MUSB_DMA_NUM_TX_CHANNELS 8
+struct musb;
+
struct ux500_musb_board_data {
void **dma_rx_param_array;
void **dma_tx_param_array;
@@ -23,6 +25,7 @@ struct ux500_musb_board_data {
void ux500_add_usb(struct device *parent, resource_size_t base,
int irq, int *dma_rx_cfg, int *dma_tx_cfg);
+/* Only used for u5500 */
struct abx500_usbgpio_platform_data {
int (*get)(struct device *device);
void (*enable)(void);
@@ -30,6 +33,5 @@ struct abx500_usbgpio_platform_data {
void (*put)(void);
int usb_cs;
};
-
-void ux500_restore_context(void);
+void ux500_restore_context(struct musb *musb);
#endif
diff --git a/arch/arm/mach-ux500/usb.c b/arch/arm/mach-ux500/usb.c
index ca84e040e99..fb1f6775154 100644
--- a/arch/arm/mach-ux500/usb.c
+++ b/arch/arm/mach-ux500/usb.c
@@ -11,6 +11,7 @@
#include <plat/ste_dma40.h>
#include <mach/hardware.h>
#include <mach/usb.h>
+#include <mach/pm.h>
#include <plat/pincfg.h>
#include "pins.h"
#include "board-ux500-usb.h"
@@ -170,6 +171,9 @@ struct platform_device ux500_musb_device = {
.platform_data = &musb_platform_data,
.dma_mask = &ux500_musb_dmamask,
.coherent_dma_mask = DMA_BIT_MASK(32),
+#ifdef CONFIG_UX500_SOC_DB8500
+ .pm_domain = &ux500_dev_power_domain,
+#endif
},
.num_resources = ARRAY_SIZE(usb_resources),
.resource = usb_resources,
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);