Commit | Line | Data |
---|---|---|
550a7375 FB |
1 | /* |
2 | * Copyright (C) 2005-2007 by Texas Instruments | |
3 | * Some code has been taken from tusb6010.c | |
4 | * Copyrights for that are attributable to: | |
5 | * Copyright (C) 2006 Nokia Corporation | |
550a7375 FB |
6 | * Tony Lindgren <tony@atomide.com> |
7 | * | |
8 | * This file is part of the Inventra Controller Driver for Linux. | |
9 | * | |
10 | * The Inventra Controller Driver for Linux is free software; you | |
11 | * can redistribute it and/or modify it under the terms of the GNU | |
12 | * General Public License version 2 as published by the Free Software | |
13 | * Foundation. | |
14 | * | |
15 | * The Inventra Controller Driver for Linux is distributed in | |
16 | * the hope that it will be useful, but WITHOUT ANY WARRANTY; | |
17 | * without even the implied warranty of MERCHANTABILITY or | |
18 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public | |
19 | * License for more details. | |
20 | * | |
21 | * You should have received a copy of the GNU General Public License | |
22 | * along with The Inventra Controller Driver for Linux ; if not, | |
23 | * write to the Free Software Foundation, Inc., 59 Temple Place, | |
24 | * Suite 330, Boston, MA 02111-1307 USA | |
25 | * | |
26 | */ | |
27 | #include <linux/module.h> | |
28 | #include <linux/kernel.h> | |
29 | #include <linux/sched.h> | |
550a7375 FB |
30 | #include <linux/init.h> |
31 | #include <linux/list.h> | |
32 | #include <linux/clk.h> | |
33 | #include <linux/io.h> | |
34 | ||
35 | #include <asm/mach-types.h> | |
0590d587 | 36 | #include <mach/hardware.h> |
ce491cf8 | 37 | #include <plat/mux.h> |
550a7375 FB |
38 | |
39 | #include "musb_core.h" | |
40 | #include "omap2430.h" | |
41 | ||
42 | #ifdef CONFIG_ARCH_OMAP3430 | |
43 | #define get_cpu_rev() 2 | |
44 | #endif | |
45 | ||
550a7375 FB |
46 | |
47 | static struct timer_list musb_idle_timer; | |
48 | ||
49 | static void musb_do_idle(unsigned long _musb) | |
50 | { | |
51 | struct musb *musb = (void *)_musb; | |
52 | unsigned long flags; | |
eef767b7 | 53 | #ifdef CONFIG_USB_MUSB_HDRC_HCD |
550a7375 | 54 | u8 power; |
eef767b7 | 55 | #endif |
550a7375 FB |
56 | u8 devctl; |
57 | ||
550a7375 FB |
58 | spin_lock_irqsave(&musb->lock, flags); |
59 | ||
71783e0d DB |
60 | devctl = musb_readb(musb->mregs, MUSB_DEVCTL); |
61 | ||
84e250ff | 62 | switch (musb->xceiv->state) { |
550a7375 FB |
63 | case OTG_STATE_A_WAIT_BCON: |
64 | devctl &= ~MUSB_DEVCTL_SESSION; | |
65 | musb_writeb(musb->mregs, MUSB_DEVCTL, devctl); | |
66 | ||
67 | devctl = musb_readb(musb->mregs, MUSB_DEVCTL); | |
68 | if (devctl & MUSB_DEVCTL_BDEVICE) { | |
84e250ff | 69 | musb->xceiv->state = OTG_STATE_B_IDLE; |
550a7375 FB |
70 | MUSB_DEV_MODE(musb); |
71 | } else { | |
84e250ff | 72 | musb->xceiv->state = OTG_STATE_A_IDLE; |
550a7375 FB |
73 | MUSB_HST_MODE(musb); |
74 | } | |
75 | break; | |
76 | #ifdef CONFIG_USB_MUSB_HDRC_HCD | |
77 | case OTG_STATE_A_SUSPEND: | |
78 | /* finish RESUME signaling? */ | |
79 | if (musb->port1_status & MUSB_PORT_STAT_RESUME) { | |
80 | power = musb_readb(musb->mregs, MUSB_POWER); | |
81 | power &= ~MUSB_POWER_RESUME; | |
82 | DBG(1, "root port resume stopped, power %02x\n", power); | |
83 | musb_writeb(musb->mregs, MUSB_POWER, power); | |
84 | musb->is_active = 1; | |
85 | musb->port1_status &= ~(USB_PORT_STAT_SUSPEND | |
86 | | MUSB_PORT_STAT_RESUME); | |
87 | musb->port1_status |= USB_PORT_STAT_C_SUSPEND << 16; | |
88 | usb_hcd_poll_rh_status(musb_to_hcd(musb)); | |
89 | /* NOTE: it might really be A_WAIT_BCON ... */ | |
84e250ff | 90 | musb->xceiv->state = OTG_STATE_A_HOST; |
550a7375 FB |
91 | } |
92 | break; | |
93 | #endif | |
94 | #ifdef CONFIG_USB_MUSB_HDRC_HCD | |
95 | case OTG_STATE_A_HOST: | |
96 | devctl = musb_readb(musb->mregs, MUSB_DEVCTL); | |
97 | if (devctl & MUSB_DEVCTL_BDEVICE) | |
84e250ff | 98 | musb->xceiv->state = OTG_STATE_B_IDLE; |
550a7375 | 99 | else |
84e250ff | 100 | musb->xceiv->state = OTG_STATE_A_WAIT_BCON; |
550a7375 FB |
101 | #endif |
102 | default: | |
103 | break; | |
104 | } | |
105 | spin_unlock_irqrestore(&musb->lock, flags); | |
106 | } | |
107 | ||
108 | ||
109 | void musb_platform_try_idle(struct musb *musb, unsigned long timeout) | |
110 | { | |
111 | unsigned long default_timeout = jiffies + msecs_to_jiffies(3); | |
112 | static unsigned long last_timer; | |
113 | ||
114 | if (timeout == 0) | |
115 | timeout = default_timeout; | |
116 | ||
117 | /* Never idle if active, or when VBUS timeout is not set as host */ | |
118 | if (musb->is_active || ((musb->a_wait_bcon == 0) | |
84e250ff | 119 | && (musb->xceiv->state == OTG_STATE_A_WAIT_BCON))) { |
550a7375 FB |
120 | DBG(4, "%s active, deleting timer\n", otg_state_string(musb)); |
121 | del_timer(&musb_idle_timer); | |
122 | last_timer = jiffies; | |
123 | return; | |
124 | } | |
125 | ||
126 | if (time_after(last_timer, timeout)) { | |
127 | if (!timer_pending(&musb_idle_timer)) | |
128 | last_timer = timeout; | |
129 | else { | |
130 | DBG(4, "Longer idle timer already pending, ignoring\n"); | |
131 | return; | |
132 | } | |
133 | } | |
134 | last_timer = timeout; | |
135 | ||
136 | DBG(4, "%s inactive, for idle timer for %lu ms\n", | |
137 | otg_state_string(musb), | |
138 | (unsigned long)jiffies_to_msecs(timeout - jiffies)); | |
139 | mod_timer(&musb_idle_timer, timeout); | |
140 | } | |
141 | ||
142 | void musb_platform_enable(struct musb *musb) | |
143 | { | |
144 | } | |
145 | void musb_platform_disable(struct musb *musb) | |
146 | { | |
147 | } | |
148 | static void omap_vbus_power(struct musb *musb, int is_on, int sleeping) | |
149 | { | |
150 | } | |
151 | ||
152 | static void omap_set_vbus(struct musb *musb, int is_on) | |
153 | { | |
154 | u8 devctl; | |
155 | /* HDRC controls CPEN, but beware current surges during device | |
156 | * connect. They can trigger transient overcurrent conditions | |
157 | * that must be ignored. | |
158 | */ | |
159 | ||
160 | devctl = musb_readb(musb->mregs, MUSB_DEVCTL); | |
161 | ||
162 | if (is_on) { | |
163 | musb->is_active = 1; | |
84e250ff DB |
164 | musb->xceiv->default_a = 1; |
165 | musb->xceiv->state = OTG_STATE_A_WAIT_VRISE; | |
550a7375 FB |
166 | devctl |= MUSB_DEVCTL_SESSION; |
167 | ||
168 | MUSB_HST_MODE(musb); | |
169 | } else { | |
170 | musb->is_active = 0; | |
171 | ||
172 | /* NOTE: we're skipping A_WAIT_VFALL -> A_IDLE and | |
173 | * jumping right to B_IDLE... | |
174 | */ | |
175 | ||
84e250ff DB |
176 | musb->xceiv->default_a = 0; |
177 | musb->xceiv->state = OTG_STATE_B_IDLE; | |
550a7375 FB |
178 | devctl &= ~MUSB_DEVCTL_SESSION; |
179 | ||
180 | MUSB_DEV_MODE(musb); | |
181 | } | |
182 | musb_writeb(musb->mregs, MUSB_DEVCTL, devctl); | |
183 | ||
184 | DBG(1, "VBUS %s, devctl %02x " | |
185 | /* otg %3x conf %08x prcm %08x */ "\n", | |
186 | otg_state_string(musb), | |
187 | musb_readb(musb->mregs, MUSB_DEVCTL)); | |
188 | } | |
550a7375 FB |
189 | |
190 | static int musb_platform_resume(struct musb *musb); | |
191 | ||
96a274d1 | 192 | int musb_platform_set_mode(struct musb *musb, u8 musb_mode) |
550a7375 FB |
193 | { |
194 | u8 devctl = musb_readb(musb->mregs, MUSB_DEVCTL); | |
195 | ||
196 | devctl |= MUSB_DEVCTL_SESSION; | |
197 | musb_writeb(musb->mregs, MUSB_DEVCTL, devctl); | |
198 | ||
96a274d1 | 199 | return 0; |
550a7375 FB |
200 | } |
201 | ||
202 | int __init musb_platform_init(struct musb *musb) | |
203 | { | |
204 | u32 l; | |
205 | ||
206 | #if defined(CONFIG_ARCH_OMAP2430) | |
207 | omap_cfg_reg(AE5_2430_USB0HS_STP); | |
208 | #endif | |
209 | ||
84e250ff DB |
210 | /* We require some kind of external transceiver, hooked |
211 | * up through ULPI. TWL4030-family PMICs include one, | |
212 | * which needs a driver, drivers aren't always needed. | |
213 | */ | |
214 | musb->xceiv = otg_get_transceiver(); | |
215 | if (!musb->xceiv) { | |
216 | pr_err("HS USB OTG: no transceiver configured\n"); | |
217 | return -ENODEV; | |
218 | } | |
219 | ||
550a7375 FB |
220 | musb_platform_resume(musb); |
221 | ||
8573e6a6 | 222 | l = musb_readl(musb->mregs, OTG_SYSCONFIG); |
550a7375 FB |
223 | l &= ~ENABLEWAKEUP; /* disable wakeup */ |
224 | l &= ~NOSTDBY; /* remove possible nostdby */ | |
225 | l |= SMARTSTDBY; /* enable smart standby */ | |
226 | l &= ~AUTOIDLE; /* disable auto idle */ | |
227 | l &= ~NOIDLE; /* remove possible noidle */ | |
228 | l |= SMARTIDLE; /* enable smart idle */ | |
9a4b5e36 NM |
229 | /* |
230 | * MUSB AUTOIDLE don't work in 3430. | |
231 | * Workaround by Richard Woodruff/TI | |
232 | */ | |
233 | if (!cpu_is_omap3430()) | |
234 | l |= AUTOIDLE; /* enable auto idle */ | |
8573e6a6 | 235 | musb_writel(musb->mregs, OTG_SYSCONFIG, l); |
550a7375 | 236 | |
8573e6a6 | 237 | l = musb_readl(musb->mregs, OTG_INTERFSEL); |
550a7375 | 238 | l |= ULPI_12PIN; |
8573e6a6 | 239 | musb_writel(musb->mregs, OTG_INTERFSEL, l); |
550a7375 FB |
240 | |
241 | pr_debug("HS USB OTG: revision 0x%x, sysconfig 0x%02x, " | |
242 | "sysstatus 0x%x, intrfsel 0x%x, simenable 0x%x\n", | |
8573e6a6 FB |
243 | musb_readl(musb->mregs, OTG_REVISION), |
244 | musb_readl(musb->mregs, OTG_SYSCONFIG), | |
245 | musb_readl(musb->mregs, OTG_SYSSTATUS), | |
246 | musb_readl(musb->mregs, OTG_INTERFSEL), | |
247 | musb_readl(musb->mregs, OTG_SIMENABLE)); | |
550a7375 FB |
248 | |
249 | omap_vbus_power(musb, musb->board_mode == MUSB_HOST, 1); | |
250 | ||
251 | if (is_host_enabled(musb)) | |
252 | musb->board_set_vbus = omap_set_vbus; | |
550a7375 FB |
253 | |
254 | setup_timer(&musb_idle_timer, musb_do_idle, (unsigned long) musb); | |
255 | ||
256 | return 0; | |
257 | } | |
258 | ||
4f712e01 | 259 | #ifdef CONFIG_PM |
8573e6a6 FB |
260 | void musb_platform_save_context(struct musb *musb, |
261 | struct musb_context_registers *musb_context) | |
4f712e01 | 262 | { |
8573e6a6 FB |
263 | musb_context->otg_sysconfig = musb_readl(musb->mregs, OTG_SYSCONFIG); |
264 | musb_context->otg_forcestandby = musb_readl(musb->mregs, OTG_FORCESTDBY); | |
4f712e01 AKG |
265 | } |
266 | ||
8573e6a6 FB |
267 | void musb_platform_restore_context(struct musb *musb, |
268 | struct musb_context_registers *musb_context) | |
4f712e01 | 269 | { |
8573e6a6 FB |
270 | musb_writel(musb->mregs, OTG_SYSCONFIG, musb_context->otg_sysconfig); |
271 | musb_writel(musb->mregs, OTG_FORCESTDBY, musb_context->otg_forcestandby); | |
4f712e01 AKG |
272 | } |
273 | #endif | |
274 | ||
550a7375 FB |
275 | int musb_platform_suspend(struct musb *musb) |
276 | { | |
277 | u32 l; | |
278 | ||
279 | if (!musb->clock) | |
280 | return 0; | |
281 | ||
282 | /* in any role */ | |
8573e6a6 | 283 | l = musb_readl(musb->mregs, OTG_FORCESTDBY); |
550a7375 | 284 | l |= ENABLEFORCE; /* enable MSTANDBY */ |
8573e6a6 | 285 | musb_writel(musb->mregs, OTG_FORCESTDBY, l); |
550a7375 | 286 | |
8573e6a6 | 287 | l = musb_readl(musb->mregs, OTG_SYSCONFIG); |
550a7375 | 288 | l |= ENABLEWAKEUP; /* enable wakeup */ |
8573e6a6 | 289 | musb_writel(musb->mregs, OTG_SYSCONFIG, l); |
550a7375 | 290 | |
84e250ff | 291 | otg_set_suspend(musb->xceiv, 1); |
550a7375 FB |
292 | |
293 | if (musb->set_clock) | |
294 | musb->set_clock(musb->clock, 0); | |
295 | else | |
296 | clk_disable(musb->clock); | |
297 | ||
298 | return 0; | |
299 | } | |
300 | ||
301 | static int musb_platform_resume(struct musb *musb) | |
302 | { | |
303 | u32 l; | |
304 | ||
305 | if (!musb->clock) | |
306 | return 0; | |
307 | ||
84e250ff | 308 | otg_set_suspend(musb->xceiv, 0); |
550a7375 FB |
309 | |
310 | if (musb->set_clock) | |
311 | musb->set_clock(musb->clock, 1); | |
312 | else | |
313 | clk_enable(musb->clock); | |
314 | ||
8573e6a6 | 315 | l = musb_readl(musb->mregs, OTG_SYSCONFIG); |
550a7375 | 316 | l &= ~ENABLEWAKEUP; /* disable wakeup */ |
8573e6a6 | 317 | musb_writel(musb->mregs, OTG_SYSCONFIG, l); |
550a7375 | 318 | |
8573e6a6 | 319 | l = musb_readl(musb->mregs, OTG_FORCESTDBY); |
550a7375 | 320 | l &= ~ENABLEFORCE; /* disable MSTANDBY */ |
8573e6a6 | 321 | musb_writel(musb->mregs, OTG_FORCESTDBY, l); |
550a7375 FB |
322 | |
323 | return 0; | |
324 | } | |
325 | ||
326 | ||
327 | int musb_platform_exit(struct musb *musb) | |
328 | { | |
329 | ||
330 | omap_vbus_power(musb, 0 /*off*/, 1); | |
331 | ||
332 | musb_platform_suspend(musb); | |
333 | ||
334 | clk_put(musb->clock); | |
ff85494b | 335 | musb->clock = NULL; |
550a7375 FB |
336 | |
337 | return 0; | |
338 | } |