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>
This commit is contained in:
Родитель
bc57381e63
Коммит
b002ff6e26
|
@ -21,6 +21,14 @@
|
||||||
#include <linux/sysfs.h>
|
#include <linux/sysfs.h>
|
||||||
#include "./common.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
|
* platform call back
|
||||||
*
|
*
|
||||||
|
@ -203,6 +211,7 @@ static void usbhsc_notify_hotplug(struct work_struct *work)
|
||||||
dev_dbg(&pdev->dev, "%s enable\n", __func__);
|
dev_dbg(&pdev->dev, "%s enable\n", __func__);
|
||||||
|
|
||||||
/* power on */
|
/* power on */
|
||||||
|
if (usbhsc_flags_has(priv, USBHSF_RUNTIME_PWCTRL))
|
||||||
usbhsc_power_ctrl(priv, enable);
|
usbhsc_power_ctrl(priv, enable);
|
||||||
|
|
||||||
/* module start */
|
/* module start */
|
||||||
|
@ -215,6 +224,7 @@ static void usbhsc_notify_hotplug(struct work_struct *work)
|
||||||
usbhs_mod_call(priv, stop, priv);
|
usbhs_mod_call(priv, stop, priv);
|
||||||
|
|
||||||
/* power off */
|
/* power off */
|
||||||
|
if (usbhsc_flags_has(priv, USBHSF_RUNTIME_PWCTRL))
|
||||||
usbhsc_power_ctrl(priv, enable);
|
usbhsc_power_ctrl(priv, enable);
|
||||||
|
|
||||||
usbhs_mod_change(priv, -1);
|
usbhs_mod_change(priv, -1);
|
||||||
|
@ -252,8 +262,7 @@ static int __devinit usbhs_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
/* check platform information */
|
/* check platform information */
|
||||||
if (!info ||
|
if (!info ||
|
||||||
!info->platform_callback.get_id ||
|
!info->platform_callback.get_id) {
|
||||||
!info->platform_callback.get_vbus) {
|
|
||||||
dev_err(&pdev->dev, "no platform information\n");
|
dev_err(&pdev->dev, "no platform information\n");
|
||||||
return -EINVAL;
|
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);
|
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
|
* priv settings
|
||||||
*/
|
*/
|
||||||
|
@ -338,10 +352,16 @@ static int __devinit usbhs_probe(struct platform_device *pdev)
|
||||||
/* reset phy for connection */
|
/* reset phy for connection */
|
||||||
usbhs_platform_call(priv, phy_reset, pdev);
|
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
|
* manual call notify_hotplug for cold plug
|
||||||
*/
|
*/
|
||||||
pm_runtime_enable(&pdev->dev);
|
|
||||||
ret = usbhsc_drvcllbck_notify_hotplug(pdev);
|
ret = usbhsc_drvcllbck_notify_hotplug(pdev);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto probe_end_call_remove;
|
goto probe_end_call_remove;
|
||||||
|
@ -376,9 +396,11 @@ static int __devexit usbhs_remove(struct platform_device *pdev)
|
||||||
|
|
||||||
dfunc->notify_hotplug = NULL;
|
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_platform_call(priv, hardware_exit, pdev);
|
||||||
usbhs_pipe_remove(priv);
|
usbhs_pipe_remove(priv);
|
||||||
|
|
|
@ -105,6 +105,7 @@ struct usbhs_priv;
|
||||||
#define SACKE (1 << 4) /* Setup Transaction ACK Interrupt Enable */
|
#define SACKE (1 << 4) /* Setup Transaction ACK Interrupt Enable */
|
||||||
|
|
||||||
/* INTSTS0 */
|
/* INTSTS0 */
|
||||||
|
#define VBINT (1 << 15) /* VBUS0_0 and VBUS1_0 Interrupt Status */
|
||||||
#define DVST (1 << 12) /* Device State Transition Interrupt Status */
|
#define DVST (1 << 12) /* Device State Transition Interrupt Status */
|
||||||
#define CTRT (1 << 11) /* Control Stage Interrupt Status */
|
#define CTRT (1 << 11) /* Control Stage Interrupt Status */
|
||||||
#define BEMP (1 << 10) /* Buffer Empty Interrupt Status */
|
#define BEMP (1 << 10) /* Buffer Empty Interrupt Status */
|
||||||
|
@ -182,6 +183,8 @@ struct usbhs_priv {
|
||||||
|
|
||||||
spinlock_t lock;
|
spinlock_t lock;
|
||||||
|
|
||||||
|
u32 flags;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* module control
|
* module control
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -20,6 +20,48 @@
|
||||||
#include "./mod.h"
|
#include "./mod.h"
|
||||||
|
|
||||||
#define usbhs_priv_to_modinfo(priv) (&priv->mod_info)
|
#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
|
* host / gadget functions
|
||||||
|
@ -227,6 +269,9 @@ static irqreturn_t usbhs_interrupt(int irq, void *data)
|
||||||
* see also
|
* see also
|
||||||
* usbhs_irq_setting_update
|
* usbhs_irq_setting_update
|
||||||
*/
|
*/
|
||||||
|
if (irq_state.intsts0 & VBINT)
|
||||||
|
usbhs_mod_info_call(priv, irq_vbus, priv, &irq_state);
|
||||||
|
|
||||||
if (irq_state.intsts0 & DVST)
|
if (irq_state.intsts0 & DVST)
|
||||||
usbhs_mod_call(priv, irq_dev_state, priv, &irq_state);
|
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)
|
void usbhs_irq_callback_update(struct usbhs_priv *priv, struct usbhs_mod *mod)
|
||||||
{
|
{
|
||||||
u16 intenb0 = 0;
|
u16 intenb0 = 0;
|
||||||
|
struct usbhs_mod_info *info = usbhs_priv_to_modinfo(priv);
|
||||||
|
|
||||||
usbhs_write(priv, INTENB0, 0);
|
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
|
* it don't enable DVSE (intenb0) here
|
||||||
* but "mod->irq_dev_state" will be called.
|
* but "mod->irq_dev_state" will be called.
|
||||||
*/
|
*/
|
||||||
|
if (info->irq_vbus)
|
||||||
|
intenb0 |= VBSE;
|
||||||
|
|
||||||
if (mod) {
|
if (mod) {
|
||||||
if (mod->irq_ctrl_stage)
|
if (mod->irq_ctrl_stage)
|
||||||
|
|
|
@ -68,6 +68,19 @@ struct usbhs_mod {
|
||||||
struct usbhs_mod_info {
|
struct usbhs_mod_info {
|
||||||
struct usbhs_mod *mod[USBHS_MAX];
|
struct usbhs_mod *mod[USBHS_MAX];
|
||||||
struct usbhs_mod *curt; /* current mod */
|
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);
|
int usbhs_mod_probe(struct usbhs_priv *priv);
|
||||||
void usbhs_mod_remove(struct usbhs_priv *priv);
|
void usbhs_mod_remove(struct usbhs_priv *priv);
|
||||||
|
|
||||||
|
void usbhs_mod_autonomy_mode(struct usbhs_priv *priv);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* status functions
|
* status functions
|
||||||
*/
|
*/
|
||||||
|
|
Загрузка…
Ссылка в новой задаче