[PATCH 5/5] usb: renesas_usbhs: add autonomy mode

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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@xxxxxxxxxxx>
---
 drivers/usb/renesas_usbhs/common.c |   36 +++++++++++++++++++++-----
 drivers/usb/renesas_usbhs/common.h |    3 ++
 drivers/usb/renesas_usbhs/mod.c    |   48 ++++++++++++++++++++++++++++++++++++
 drivers/usb/renesas_usbhs/mod.h    |   15 +++++++++++
 4 files changed, 95 insertions(+), 7 deletions(-)

diff --git a/drivers/usb/renesas_usbhs/common.c b/drivers/usb/renesas_usbhs/common.c
index 9a75a45..34e68e0 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 0157eb8..0aadcb4 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 d0f5f67..a577f8f 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 8644191..5c845a2 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
  */
-- 
1.7.1

--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux