Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b002ff6e authored by Kuninori Morimoto's avatar Kuninori Morimoto Committed by Greg Kroah-Hartman
Browse files

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: default avatarKuninori Morimoto <kuninori.morimoto.gx@renesas.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent bc57381e
Loading
Loading
Loading
Loading
+29 −7
Original line number Diff line number Diff line
@@ -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,6 +211,7 @@ static void usbhsc_notify_hotplug(struct work_struct *work)
		dev_dbg(&pdev->dev, "%s enable\n", __func__);

		/* power on */
		if (usbhsc_flags_has(priv, USBHSF_RUNTIME_PWCTRL))
			usbhsc_power_ctrl(priv, enable);

		/* module start */
@@ -215,6 +224,7 @@ static void usbhsc_notify_hotplug(struct work_struct *work)
		usbhs_mod_call(priv, stop, priv);

		/* power off */
		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);
+3 −0
Original line number Diff line number Diff line
@@ -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
	 */
+48 −0
Original line number Diff line number Diff line
@@ -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)
+15 −0
Original line number Diff line number Diff line
@@ -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
 */