diff options
author | Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> | 2011-04-28 03:41:20 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2011-04-29 20:24:35 -0400 |
commit | b002ff6e268b6024d6927a1ce330a14ca162b6ab (patch) | |
tree | 83bd5dde6f872396cd42762b59f96cf9c024d30a | |
parent | bc57381e634782009b1cb2e86b18013699ada576 (diff) |
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.c | 36 | ||||
-rw-r--r-- | drivers/usb/renesas_usbhs/common.h | 3 | ||||
-rw-r--r-- | drivers/usb/renesas_usbhs/mod.c | 48 | ||||
-rw-r--r-- | 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 9a75a45687b..34e68e0205c 100644 --- a/drivers/usb/renesas_usbhs/common.c +++ b/drivers/usb/renesas_usbhs/common.c | |||
@@ -21,6 +21,14 @@ | |||
21 | #include <linux/sysfs.h> | 21 | #include <linux/sysfs.h> |
22 | #include "./common.h" | 22 | #include "./common.h" |
23 | 23 | ||
24 | #define USBHSF_RUNTIME_PWCTRL (1 << 0) | ||
25 | |||
26 | /* status */ | ||
27 | #define usbhsc_flags_init(p) do {(p)->flags = 0; } while (0) | ||
28 | #define usbhsc_flags_set(p, b) ((p)->flags |= (b)) | ||
29 | #define usbhsc_flags_clr(p, b) ((p)->flags &= ~(b)) | ||
30 | #define usbhsc_flags_has(p, b) ((p)->flags & (b)) | ||
31 | |||
24 | /* | 32 | /* |
25 | * platform call back | 33 | * platform call back |
26 | * | 34 | * |
@@ -203,7 +211,8 @@ static void usbhsc_notify_hotplug(struct work_struct *work) | |||
203 | dev_dbg(&pdev->dev, "%s enable\n", __func__); | 211 | dev_dbg(&pdev->dev, "%s enable\n", __func__); |
204 | 212 | ||
205 | /* power on */ | 213 | /* power on */ |
206 | usbhsc_power_ctrl(priv, enable); | 214 | if (usbhsc_flags_has(priv, USBHSF_RUNTIME_PWCTRL)) |
215 | usbhsc_power_ctrl(priv, enable); | ||
207 | 216 | ||
208 | /* module start */ | 217 | /* module start */ |
209 | usbhs_mod_call(priv, start, priv); | 218 | usbhs_mod_call(priv, start, priv); |
@@ -215,7 +224,8 @@ static void usbhsc_notify_hotplug(struct work_struct *work) | |||
215 | usbhs_mod_call(priv, stop, priv); | 224 | usbhs_mod_call(priv, stop, priv); |
216 | 225 | ||
217 | /* power off */ | 226 | /* power off */ |
218 | usbhsc_power_ctrl(priv, enable); | 227 | if (usbhsc_flags_has(priv, USBHSF_RUNTIME_PWCTRL)) |
228 | usbhsc_power_ctrl(priv, enable); | ||
219 | 229 | ||
220 | usbhs_mod_change(priv, -1); | 230 | usbhs_mod_change(priv, -1); |
221 | 231 | ||
@@ -252,8 +262,7 @@ static int __devinit usbhs_probe(struct platform_device *pdev) | |||
252 | 262 | ||
253 | /* check platform information */ | 263 | /* check platform information */ |
254 | if (!info || | 264 | if (!info || |
255 | !info->platform_callback.get_id || | 265 | !info->platform_callback.get_id) { |
256 | !info->platform_callback.get_vbus) { | ||
257 | dev_err(&pdev->dev, "no platform information\n"); | 266 | dev_err(&pdev->dev, "no platform information\n"); |
258 | return -EINVAL; | 267 | return -EINVAL; |
259 | } | 268 | } |
@@ -296,6 +305,11 @@ static int __devinit usbhs_probe(struct platform_device *pdev) | |||
296 | priv->dparam->pipe_size = ARRAY_SIZE(usbhsc_default_pipe_type); | 305 | priv->dparam->pipe_size = ARRAY_SIZE(usbhsc_default_pipe_type); |
297 | } | 306 | } |
298 | 307 | ||
308 | /* FIXME */ | ||
309 | /* runtime power control ? */ | ||
310 | if (priv->pfunc->get_vbus) | ||
311 | usbhsc_flags_set(priv, USBHSF_RUNTIME_PWCTRL); | ||
312 | |||
299 | /* | 313 | /* |
300 | * priv settings | 314 | * priv settings |
301 | */ | 315 | */ |
@@ -338,10 +352,16 @@ static int __devinit usbhs_probe(struct platform_device *pdev) | |||
338 | /* reset phy for connection */ | 352 | /* reset phy for connection */ |
339 | usbhs_platform_call(priv, phy_reset, pdev); | 353 | usbhs_platform_call(priv, phy_reset, pdev); |
340 | 354 | ||
355 | /* power control */ | ||
356 | pm_runtime_enable(&pdev->dev); | ||
357 | if (!usbhsc_flags_has(priv, USBHSF_RUNTIME_PWCTRL)) { | ||
358 | usbhsc_power_ctrl(priv, 1); | ||
359 | usbhs_mod_autonomy_mode(priv); | ||
360 | } | ||
361 | |||
341 | /* | 362 | /* |
342 | * manual call notify_hotplug for cold plug | 363 | * manual call notify_hotplug for cold plug |
343 | */ | 364 | */ |
344 | pm_runtime_enable(&pdev->dev); | ||
345 | ret = usbhsc_drvcllbck_notify_hotplug(pdev); | 365 | ret = usbhsc_drvcllbck_notify_hotplug(pdev); |
346 | if (ret < 0) | 366 | if (ret < 0) |
347 | goto probe_end_call_remove; | 367 | goto probe_end_call_remove; |
@@ -376,9 +396,11 @@ static int __devexit usbhs_remove(struct platform_device *pdev) | |||
376 | 396 | ||
377 | dfunc->notify_hotplug = NULL; | 397 | dfunc->notify_hotplug = NULL; |
378 | 398 | ||
379 | pm_runtime_disable(&pdev->dev); | 399 | /* power off */ |
400 | if (!usbhsc_flags_has(priv, USBHSF_RUNTIME_PWCTRL)) | ||
401 | usbhsc_power_ctrl(priv, 0); | ||
380 | 402 | ||
381 | usbhsc_bus_ctrl(priv, 0); | 403 | pm_runtime_disable(&pdev->dev); |
382 | 404 | ||
383 | usbhs_platform_call(priv, hardware_exit, pdev); | 405 | usbhs_platform_call(priv, hardware_exit, pdev); |
384 | usbhs_pipe_remove(priv); | 406 | usbhs_pipe_remove(priv); |
diff --git a/drivers/usb/renesas_usbhs/common.h b/drivers/usb/renesas_usbhs/common.h index 0157eb805cf..0aadcb40276 100644 --- a/drivers/usb/renesas_usbhs/common.h +++ b/drivers/usb/renesas_usbhs/common.h | |||
@@ -105,6 +105,7 @@ struct usbhs_priv; | |||
105 | #define SACKE (1 << 4) /* Setup Transaction ACK Interrupt Enable */ | 105 | #define SACKE (1 << 4) /* Setup Transaction ACK Interrupt Enable */ |
106 | 106 | ||
107 | /* INTSTS0 */ | 107 | /* INTSTS0 */ |
108 | #define VBINT (1 << 15) /* VBUS0_0 and VBUS1_0 Interrupt Status */ | ||
108 | #define DVST (1 << 12) /* Device State Transition Interrupt Status */ | 109 | #define DVST (1 << 12) /* Device State Transition Interrupt Status */ |
109 | #define CTRT (1 << 11) /* Control Stage Interrupt Status */ | 110 | #define CTRT (1 << 11) /* Control Stage Interrupt Status */ |
110 | #define BEMP (1 << 10) /* Buffer Empty Interrupt Status */ | 111 | #define BEMP (1 << 10) /* Buffer Empty Interrupt Status */ |
@@ -182,6 +183,8 @@ struct usbhs_priv { | |||
182 | 183 | ||
183 | spinlock_t lock; | 184 | spinlock_t lock; |
184 | 185 | ||
186 | u32 flags; | ||
187 | |||
185 | /* | 188 | /* |
186 | * module control | 189 | * module control |
187 | */ | 190 | */ |
diff --git a/drivers/usb/renesas_usbhs/mod.c b/drivers/usb/renesas_usbhs/mod.c index d0f5f67e074..a577f8f4064 100644 --- a/drivers/usb/renesas_usbhs/mod.c +++ b/drivers/usb/renesas_usbhs/mod.c | |||
@@ -20,6 +20,48 @@ | |||
20 | #include "./mod.h" | 20 | #include "./mod.h" |
21 | 21 | ||
22 | #define usbhs_priv_to_modinfo(priv) (&priv->mod_info) | 22 | #define usbhs_priv_to_modinfo(priv) (&priv->mod_info) |
23 | #define usbhs_mod_info_call(priv, func, param...) \ | ||
24 | ({ \ | ||
25 | struct usbhs_mod_info *info; \ | ||
26 | info = usbhs_priv_to_modinfo(priv); \ | ||
27 | !info->func ? 0 : \ | ||
28 | info->func(param); \ | ||
29 | }) | ||
30 | |||
31 | /* | ||
32 | * autonomy | ||
33 | * | ||
34 | * these functions are used if platform doesn't have external phy. | ||
35 | * -> there is no "notify_hotplug" callback from platform | ||
36 | * -> call "notify_hotplug" by itself | ||
37 | * -> use own interrupt to connect/disconnect | ||
38 | * -> it mean module clock is always ON | ||
39 | * ~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
40 | */ | ||
41 | static int usbhsm_autonomy_get_vbus(struct platform_device *pdev) | ||
42 | { | ||
43 | struct usbhs_priv *priv = usbhs_pdev_to_priv(pdev); | ||
44 | |||
45 | return VBSTS & usbhs_read(priv, INTSTS0); | ||
46 | } | ||
47 | |||
48 | static int usbhsm_autonomy_irq_vbus(struct usbhs_priv *priv, | ||
49 | struct usbhs_irq_state *irq_state) | ||
50 | { | ||
51 | struct platform_device *pdev = usbhs_priv_to_pdev(priv); | ||
52 | |||
53 | return usbhsc_drvcllbck_notify_hotplug(pdev); | ||
54 | } | ||
55 | |||
56 | void usbhs_mod_autonomy_mode(struct usbhs_priv *priv) | ||
57 | { | ||
58 | struct usbhs_mod_info *info = usbhs_priv_to_modinfo(priv); | ||
59 | |||
60 | info->irq_vbus = usbhsm_autonomy_irq_vbus; | ||
61 | priv->pfunc->get_vbus = usbhsm_autonomy_get_vbus; | ||
62 | |||
63 | usbhs_irq_callback_update(priv, NULL); | ||
64 | } | ||
23 | 65 | ||
24 | /* | 66 | /* |
25 | * host / gadget functions | 67 | * host / gadget functions |
@@ -227,6 +269,9 @@ static irqreturn_t usbhs_interrupt(int irq, void *data) | |||
227 | * see also | 269 | * see also |
228 | * usbhs_irq_setting_update | 270 | * usbhs_irq_setting_update |
229 | */ | 271 | */ |
272 | if (irq_state.intsts0 & VBINT) | ||
273 | usbhs_mod_info_call(priv, irq_vbus, priv, &irq_state); | ||
274 | |||
230 | if (irq_state.intsts0 & DVST) | 275 | if (irq_state.intsts0 & DVST) |
231 | usbhs_mod_call(priv, irq_dev_state, priv, &irq_state); | 276 | usbhs_mod_call(priv, irq_dev_state, priv, &irq_state); |
232 | 277 | ||
@@ -245,6 +290,7 @@ static irqreturn_t usbhs_interrupt(int irq, void *data) | |||
245 | void usbhs_irq_callback_update(struct usbhs_priv *priv, struct usbhs_mod *mod) | 290 | void usbhs_irq_callback_update(struct usbhs_priv *priv, struct usbhs_mod *mod) |
246 | { | 291 | { |
247 | u16 intenb0 = 0; | 292 | u16 intenb0 = 0; |
293 | struct usbhs_mod_info *info = usbhs_priv_to_modinfo(priv); | ||
248 | 294 | ||
249 | usbhs_write(priv, INTENB0, 0); | 295 | usbhs_write(priv, INTENB0, 0); |
250 | 296 | ||
@@ -260,6 +306,8 @@ void usbhs_irq_callback_update(struct usbhs_priv *priv, struct usbhs_mod *mod) | |||
260 | * it don't enable DVSE (intenb0) here | 306 | * it don't enable DVSE (intenb0) here |
261 | * but "mod->irq_dev_state" will be called. | 307 | * but "mod->irq_dev_state" will be called. |
262 | */ | 308 | */ |
309 | if (info->irq_vbus) | ||
310 | intenb0 |= VBSE; | ||
263 | 311 | ||
264 | if (mod) { | 312 | if (mod) { |
265 | if (mod->irq_ctrl_stage) | 313 | if (mod->irq_ctrl_stage) |
diff --git a/drivers/usb/renesas_usbhs/mod.h b/drivers/usb/renesas_usbhs/mod.h index 8644191e164..5c845a28a21 100644 --- a/drivers/usb/renesas_usbhs/mod.h +++ b/drivers/usb/renesas_usbhs/mod.h | |||
@@ -68,6 +68,19 @@ struct usbhs_mod { | |||
68 | struct usbhs_mod_info { | 68 | struct usbhs_mod_info { |
69 | struct usbhs_mod *mod[USBHS_MAX]; | 69 | struct usbhs_mod *mod[USBHS_MAX]; |
70 | struct usbhs_mod *curt; /* current mod */ | 70 | struct usbhs_mod *curt; /* current mod */ |
71 | |||
72 | /* | ||
73 | * INTSTS0 :: VBINT | ||
74 | * | ||
75 | * This function will be used as autonomy mode | ||
76 | * when platform cannot call notify_hotplug. | ||
77 | * | ||
78 | * This callback cannot be member of "struct usbhs_mod" | ||
79 | * because it will be used even though | ||
80 | * host/gadget has not been selected. | ||
81 | */ | ||
82 | int (*irq_vbus)(struct usbhs_priv *priv, | ||
83 | struct usbhs_irq_state *irq_state); | ||
71 | }; | 84 | }; |
72 | 85 | ||
73 | /* | 86 | /* |
@@ -81,6 +94,8 @@ int usbhs_mod_change(struct usbhs_priv *priv, int id); | |||
81 | int usbhs_mod_probe(struct usbhs_priv *priv); | 94 | int usbhs_mod_probe(struct usbhs_priv *priv); |
82 | void usbhs_mod_remove(struct usbhs_priv *priv); | 95 | void usbhs_mod_remove(struct usbhs_priv *priv); |
83 | 96 | ||
97 | void usbhs_mod_autonomy_mode(struct usbhs_priv *priv); | ||
98 | |||
84 | /* | 99 | /* |
85 | * status functions | 100 | * status functions |
86 | */ | 101 | */ |