summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKuninori Morimoto <kuninori.morimoto.gx@renesas.com>2011-04-28 11:41:20 +0400
committerGreg Kroah-Hartman <gregkh@suse.de>2011-04-30 04:24:35 +0400
commitb002ff6e268b6024d6927a1ce330a14ca162b6ab (patch)
tree83bd5dde6f872396cd42762b59f96cf9c024d30a
parentbc57381e634782009b1cb2e86b18013699ada576 (diff)
downloadlinux-b002ff6e268b6024d6927a1ce330a14ca162b6ab.tar.xz
usb: renesas_usbhs: add autonomy mode
Current renesas_usbhs was designed to save power when USB is not connected. And it assumed platform uses callback to notify connection/disconnection by external interrupt. But some SuperH / platform board doesn't have such feature. This patch adds autonomy mode which detect USB connection/disconnection by internal interrupt. But power will be always ON when autonomy mode is selected. Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
-rw-r--r--drivers/usb/renesas_usbhs/common.c36
-rw-r--r--drivers/usb/renesas_usbhs/common.h3
-rw-r--r--drivers/usb/renesas_usbhs/mod.c48
-rw-r--r--drivers/usb/renesas_usbhs/mod.h15
4 files changed, 95 insertions, 7 deletions
diff --git a/drivers/usb/renesas_usbhs/common.c b/drivers/usb/renesas_usbhs/common.c
index 9a75a45687bb..34e68e0205c5 100644
--- a/drivers/usb/renesas_usbhs/common.c
+++ b/drivers/usb/renesas_usbhs/common.c
@@ -21,6 +21,14 @@
#include <linux/sysfs.h>
#include "./common.h"
+#define USBHSF_RUNTIME_PWCTRL (1 << 0)
+
+/* status */
+#define usbhsc_flags_init(p) do {(p)->flags = 0; } while (0)
+#define usbhsc_flags_set(p, b) ((p)->flags |= (b))
+#define usbhsc_flags_clr(p, b) ((p)->flags &= ~(b))
+#define usbhsc_flags_has(p, b) ((p)->flags & (b))
+
/*
* platform call back
*
@@ -203,7 +211,8 @@ static void usbhsc_notify_hotplug(struct work_struct *work)
dev_dbg(&pdev->dev, "%s enable\n", __func__);
/* power on */
- usbhsc_power_ctrl(priv, enable);
+ if (usbhsc_flags_has(priv, USBHSF_RUNTIME_PWCTRL))
+ usbhsc_power_ctrl(priv, enable);
/* module start */
usbhs_mod_call(priv, start, priv);
@@ -215,7 +224,8 @@ static void usbhsc_notify_hotplug(struct work_struct *work)
usbhs_mod_call(priv, stop, priv);
/* power off */
- usbhsc_power_ctrl(priv, enable);
+ if (usbhsc_flags_has(priv, USBHSF_RUNTIME_PWCTRL))
+ usbhsc_power_ctrl(priv, enable);
usbhs_mod_change(priv, -1);
@@ -252,8 +262,7 @@ static int __devinit usbhs_probe(struct platform_device *pdev)
/* check platform information */
if (!info ||
- !info->platform_callback.get_id ||
- !info->platform_callback.get_vbus) {
+ !info->platform_callback.get_id) {
dev_err(&pdev->dev, "no platform information\n");
return -EINVAL;
}
@@ -296,6 +305,11 @@ static int __devinit usbhs_probe(struct platform_device *pdev)
priv->dparam->pipe_size = ARRAY_SIZE(usbhsc_default_pipe_type);
}
+ /* FIXME */
+ /* runtime power control ? */
+ if (priv->pfunc->get_vbus)
+ usbhsc_flags_set(priv, USBHSF_RUNTIME_PWCTRL);
+
/*
* priv settings
*/
@@ -338,10 +352,16 @@ static int __devinit usbhs_probe(struct platform_device *pdev)
/* reset phy for connection */
usbhs_platform_call(priv, phy_reset, pdev);
+ /* power control */
+ pm_runtime_enable(&pdev->dev);
+ if (!usbhsc_flags_has(priv, USBHSF_RUNTIME_PWCTRL)) {
+ usbhsc_power_ctrl(priv, 1);
+ usbhs_mod_autonomy_mode(priv);
+ }
+
/*
* manual call notify_hotplug for cold plug
*/
- pm_runtime_enable(&pdev->dev);
ret = usbhsc_drvcllbck_notify_hotplug(pdev);
if (ret < 0)
goto probe_end_call_remove;
@@ -376,9 +396,11 @@ static int __devexit usbhs_remove(struct platform_device *pdev)
dfunc->notify_hotplug = NULL;
- pm_runtime_disable(&pdev->dev);
+ /* power off */
+ if (!usbhsc_flags_has(priv, USBHSF_RUNTIME_PWCTRL))
+ usbhsc_power_ctrl(priv, 0);
- usbhsc_bus_ctrl(priv, 0);
+ pm_runtime_disable(&pdev->dev);
usbhs_platform_call(priv, hardware_exit, pdev);
usbhs_pipe_remove(priv);
diff --git a/drivers/usb/renesas_usbhs/common.h b/drivers/usb/renesas_usbhs/common.h
index 0157eb805cf6..0aadcb402764 100644
--- a/drivers/usb/renesas_usbhs/common.h
+++ b/drivers/usb/renesas_usbhs/common.h
@@ -105,6 +105,7 @@ struct usbhs_priv;
#define SACKE (1 << 4) /* Setup Transaction ACK Interrupt Enable */
/* INTSTS0 */
+#define VBINT (1 << 15) /* VBUS0_0 and VBUS1_0 Interrupt Status */
#define DVST (1 << 12) /* Device State Transition Interrupt Status */
#define CTRT (1 << 11) /* Control Stage Interrupt Status */
#define BEMP (1 << 10) /* Buffer Empty Interrupt Status */
@@ -182,6 +183,8 @@ struct usbhs_priv {
spinlock_t lock;
+ u32 flags;
+
/*
* module control
*/
diff --git a/drivers/usb/renesas_usbhs/mod.c b/drivers/usb/renesas_usbhs/mod.c
index d0f5f67e0749..a577f8f4064c 100644
--- a/drivers/usb/renesas_usbhs/mod.c
+++ b/drivers/usb/renesas_usbhs/mod.c
@@ -20,6 +20,48 @@
#include "./mod.h"
#define usbhs_priv_to_modinfo(priv) (&priv->mod_info)
+#define usbhs_mod_info_call(priv, func, param...) \
+({ \
+ struct usbhs_mod_info *info; \
+ info = usbhs_priv_to_modinfo(priv); \
+ !info->func ? 0 : \
+ info->func(param); \
+})
+
+/*
+ * autonomy
+ *
+ * these functions are used if platform doesn't have external phy.
+ * -> there is no "notify_hotplug" callback from platform
+ * -> call "notify_hotplug" by itself
+ * -> use own interrupt to connect/disconnect
+ * -> it mean module clock is always ON
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+static int usbhsm_autonomy_get_vbus(struct platform_device *pdev)
+{
+ struct usbhs_priv *priv = usbhs_pdev_to_priv(pdev);
+
+ return VBSTS & usbhs_read(priv, INTSTS0);
+}
+
+static int usbhsm_autonomy_irq_vbus(struct usbhs_priv *priv,
+ struct usbhs_irq_state *irq_state)
+{
+ struct platform_device *pdev = usbhs_priv_to_pdev(priv);
+
+ return usbhsc_drvcllbck_notify_hotplug(pdev);
+}
+
+void usbhs_mod_autonomy_mode(struct usbhs_priv *priv)
+{
+ struct usbhs_mod_info *info = usbhs_priv_to_modinfo(priv);
+
+ info->irq_vbus = usbhsm_autonomy_irq_vbus;
+ priv->pfunc->get_vbus = usbhsm_autonomy_get_vbus;
+
+ usbhs_irq_callback_update(priv, NULL);
+}
/*
* host / gadget functions
@@ -227,6 +269,9 @@ static irqreturn_t usbhs_interrupt(int irq, void *data)
* see also
* usbhs_irq_setting_update
*/
+ if (irq_state.intsts0 & VBINT)
+ usbhs_mod_info_call(priv, irq_vbus, priv, &irq_state);
+
if (irq_state.intsts0 & DVST)
usbhs_mod_call(priv, irq_dev_state, priv, &irq_state);
@@ -245,6 +290,7 @@ static irqreturn_t usbhs_interrupt(int irq, void *data)
void usbhs_irq_callback_update(struct usbhs_priv *priv, struct usbhs_mod *mod)
{
u16 intenb0 = 0;
+ struct usbhs_mod_info *info = usbhs_priv_to_modinfo(priv);
usbhs_write(priv, INTENB0, 0);
@@ -260,6 +306,8 @@ void usbhs_irq_callback_update(struct usbhs_priv *priv, struct usbhs_mod *mod)
* it don't enable DVSE (intenb0) here
* but "mod->irq_dev_state" will be called.
*/
+ if (info->irq_vbus)
+ intenb0 |= VBSE;
if (mod) {
if (mod->irq_ctrl_stage)
diff --git a/drivers/usb/renesas_usbhs/mod.h b/drivers/usb/renesas_usbhs/mod.h
index 8644191e164f..5c845a28a21c 100644
--- a/drivers/usb/renesas_usbhs/mod.h
+++ b/drivers/usb/renesas_usbhs/mod.h
@@ -68,6 +68,19 @@ struct usbhs_mod {
struct usbhs_mod_info {
struct usbhs_mod *mod[USBHS_MAX];
struct usbhs_mod *curt; /* current mod */
+
+ /*
+ * INTSTS0 :: VBINT
+ *
+ * This function will be used as autonomy mode
+ * when platform cannot call notify_hotplug.
+ *
+ * This callback cannot be member of "struct usbhs_mod"
+ * because it will be used even though
+ * host/gadget has not been selected.
+ */
+ int (*irq_vbus)(struct usbhs_priv *priv,
+ struct usbhs_irq_state *irq_state);
};
/*
@@ -81,6 +94,8 @@ int usbhs_mod_change(struct usbhs_priv *priv, int id);
int usbhs_mod_probe(struct usbhs_priv *priv);
void usbhs_mod_remove(struct usbhs_priv *priv);
+void usbhs_mod_autonomy_mode(struct usbhs_priv *priv);
+
/*
* status functions
*/