Commit | Line | Data |
---|---|---|
5fd54ace | 1 | // SPDX-License-Identifier: GPL-2.0 |
550a7375 FB |
2 | /* |
3 | * Copyright (C) 2005-2007 by Texas Instruments | |
4 | * Some code has been taken from tusb6010.c | |
5 | * Copyrights for that are attributable to: | |
6 | * Copyright (C) 2006 Nokia Corporation | |
550a7375 FB |
7 | * Tony Lindgren <tony@atomide.com> |
8 | * | |
9 | * This file is part of the Inventra Controller Driver for Linux. | |
550a7375 FB |
10 | */ |
11 | #include <linux/module.h> | |
12 | #include <linux/kernel.h> | |
13 | #include <linux/sched.h> | |
550a7375 FB |
14 | #include <linux/init.h> |
15 | #include <linux/list.h> | |
550a7375 | 16 | #include <linux/io.h> |
00a0b1d5 | 17 | #include <linux/of.h> |
ffbe2fea | 18 | #include <linux/of_irq.h> |
dc09886b FB |
19 | #include <linux/platform_device.h> |
20 | #include <linux/dma-mapping.h> | |
207b0e1f HH |
21 | #include <linux/pm_runtime.h> |
22 | #include <linux/err.h> | |
12a19b5f | 23 | #include <linux/delay.h> |
8055555f | 24 | #include <linux/usb/musb.h> |
14da699b | 25 | #include <linux/phy/omap_control_phy.h> |
8934d3e4 | 26 | #include <linux/of_platform.h> |
550a7375 | 27 | |
550a7375 FB |
28 | #include "musb_core.h" |
29 | #include "omap2430.h" | |
30 | ||
a3cee12a FB |
31 | struct omap2430_glue { |
32 | struct device *dev; | |
33 | struct platform_device *musb; | |
8055555f | 34 | enum musb_vbus_id_status status; |
1e5acb8d | 35 | struct work_struct omap_musb_mailbox_work; |
ca784be3 | 36 | struct device *control_otghs; |
62d472d8 TL |
37 | unsigned int is_runtime_suspended:1; |
38 | unsigned int needs_resume:1; | |
68d9f95d | 39 | unsigned int phy_suspended:1; |
a3cee12a | 40 | }; |
c20aebb9 | 41 | #define glue_to_musb(g) platform_get_drvdata(g->musb) |
a3cee12a | 42 | |
4b58ed11 | 43 | static struct omap2430_glue *_glue; |
c9721438 | 44 | |
c20aebb9 FB |
45 | static inline void omap2430_low_level_exit(struct musb *musb) |
46 | { | |
47 | u32 l; | |
48 | ||
49 | /* in any role */ | |
50 | l = musb_readl(musb->mregs, OTG_FORCESTDBY); | |
51 | l |= ENABLEFORCE; /* enable MSTANDBY */ | |
52 | musb_writel(musb->mregs, OTG_FORCESTDBY, l); | |
c20aebb9 FB |
53 | } |
54 | ||
55 | static inline void omap2430_low_level_init(struct musb *musb) | |
56 | { | |
57 | u32 l; | |
58 | ||
c20aebb9 FB |
59 | l = musb_readl(musb->mregs, OTG_FORCESTDBY); |
60 | l &= ~ENABLEFORCE; /* disable MSTANDBY */ | |
61 | musb_writel(musb->mregs, OTG_FORCESTDBY, l); | |
62 | } | |
63 | ||
12b7db2b | 64 | static int omap2430_musb_mailbox(enum musb_vbus_id_status status) |
594632ef | 65 | { |
c9721438 | 66 | struct omap2430_glue *glue = _glue; |
712d8efa | 67 | |
f8c4b0e7 AK |
68 | if (!glue) { |
69 | pr_err("%s: musb core is not yet initialized\n", __func__); | |
12b7db2b | 70 | return -EPROBE_DEFER; |
f8c4b0e7 AK |
71 | } |
72 | glue->status = status; | |
73 | ||
74 | if (!glue_to_musb(glue)) { | |
80ab72e1 | 75 | pr_err("%s: musb core is not yet ready\n", __func__); |
12b7db2b | 76 | return -EPROBE_DEFER; |
c9721438 | 77 | } |
712d8efa | 78 | |
c9721438 | 79 | schedule_work(&glue->omap_musb_mailbox_work); |
12b7db2b TL |
80 | |
81 | return 0; | |
712d8efa VP |
82 | } |
83 | ||
98827105 TL |
84 | /* |
85 | * HDRC controls CPEN, but beware current surges during device connect. | |
86 | * They can trigger transient overcurrent conditions that must be ignored. | |
87 | * | |
88 | * Note that we're skipping A_WAIT_VFALL -> A_IDLE and jumping right to B_IDLE | |
89 | * as set by musb_set_peripheral(). | |
90 | */ | |
c9721438 | 91 | static void omap_musb_set_mailbox(struct omap2430_glue *glue) |
712d8efa | 92 | { |
1e5acb8d | 93 | struct musb *musb = glue_to_musb(glue); |
98827105 | 94 | int error; |
594632ef | 95 | |
ae909fe4 | 96 | pm_runtime_get_sync(musb->controller); |
98827105 TL |
97 | |
98 | dev_dbg(musb->controller, "VBUS %s, devctl %02x\n", | |
99 | usb_otg_state_string(musb->xceiv->otg->state), | |
100 | musb_readb(musb->mregs, MUSB_DEVCTL)); | |
101 | ||
c9721438 | 102 | switch (glue->status) { |
8055555f | 103 | case MUSB_ID_GROUND: |
ae909fe4 | 104 | dev_dbg(musb->controller, "ID GND\n"); |
15f1122f | 105 | switch (musb->xceiv->otg->state) { |
98827105 TL |
106 | case OTG_STATE_A_IDLE: |
107 | error = musb_set_host(musb); | |
108 | if (error) | |
109 | break; | |
110 | musb->xceiv->otg->state = OTG_STATE_A_WAIT_VRISE; | |
df561f66 | 111 | fallthrough; |
15f1122f TL |
112 | case OTG_STATE_A_WAIT_VRISE: |
113 | case OTG_STATE_A_WAIT_BCON: | |
114 | case OTG_STATE_A_HOST: | |
15f1122f TL |
115 | /* |
116 | * On multiple ID ground interrupts just keep enabling | |
117 | * VBUS. At least cpcap VBUS shuts down otherwise. | |
118 | */ | |
98827105 | 119 | otg_set_vbus(musb->xceiv->otg, 1); |
15f1122f TL |
120 | break; |
121 | default: | |
122 | musb->xceiv->otg->state = OTG_STATE_A_IDLE; | |
123 | musb->xceiv->last_event = USB_EVENT_ID; | |
124 | if (musb->gadget_driver) { | |
125 | omap_control_usb_set_mode(glue->control_otghs, | |
126 | USB_MODE_HOST); | |
98827105 | 127 | otg_set_vbus(musb->xceiv->otg, 1); |
15f1122f TL |
128 | } |
129 | break; | |
594632ef HH |
130 | } |
131 | break; | |
132 | ||
8055555f | 133 | case MUSB_VBUS_VALID: |
ae909fe4 | 134 | dev_dbg(musb->controller, "VBUS Connect\n"); |
594632ef | 135 | |
e47d9254 | 136 | musb->xceiv->otg->state = OTG_STATE_B_IDLE; |
c9721438 | 137 | musb->xceiv->last_event = USB_EVENT_VBUS; |
ca784be3 | 138 | omap_control_usb_set_mode(glue->control_otghs, USB_MODE_DEVICE); |
594632ef HH |
139 | break; |
140 | ||
8055555f TL |
141 | case MUSB_ID_FLOAT: |
142 | case MUSB_VBUS_OFF: | |
ae909fe4 | 143 | dev_dbg(musb->controller, "VBUS Disconnect\n"); |
594632ef | 144 | |
c9721438 | 145 | musb->xceiv->last_event = USB_EVENT_NONE; |
98827105 TL |
146 | musb_set_peripheral(musb); |
147 | otg_set_vbus(musb->xceiv->otg, 0); | |
ca784be3 KVA |
148 | omap_control_usb_set_mode(glue->control_otghs, |
149 | USB_MODE_DISCONNECT); | |
594632ef HH |
150 | break; |
151 | default: | |
ae909fe4 | 152 | dev_dbg(musb->controller, "ID float\n"); |
594632ef | 153 | } |
ae909fe4 TL |
154 | pm_runtime_mark_last_busy(musb->controller); |
155 | pm_runtime_put_autosuspend(musb->controller); | |
6fa7178c PR |
156 | atomic_notifier_call_chain(&musb->xceiv->notifier, |
157 | musb->xceiv->last_event, NULL); | |
594632ef HH |
158 | } |
159 | ||
c9721438 KVA |
160 | |
161 | static void omap_musb_mailbox_work(struct work_struct *mailbox_work) | |
162 | { | |
163 | struct omap2430_glue *glue = container_of(mailbox_work, | |
164 | struct omap2430_glue, omap_musb_mailbox_work); | |
8b2bc2c9 | 165 | |
c9721438 KVA |
166 | omap_musb_set_mailbox(glue); |
167 | } | |
168 | ||
baef653a PDS |
169 | static irqreturn_t omap2430_musb_interrupt(int irq, void *__hci) |
170 | { | |
171 | unsigned long flags; | |
172 | irqreturn_t retval = IRQ_NONE; | |
173 | struct musb *musb = __hci; | |
174 | ||
175 | spin_lock_irqsave(&musb->lock, flags); | |
176 | ||
177 | musb->int_usb = musb_readb(musb->mregs, MUSB_INTRUSB); | |
178 | musb->int_tx = musb_readw(musb->mregs, MUSB_INTRTX); | |
179 | musb->int_rx = musb_readw(musb->mregs, MUSB_INTRRX); | |
180 | ||
181 | if (musb->int_usb || musb->int_tx || musb->int_rx) | |
182 | retval = musb_interrupt(musb); | |
183 | ||
184 | spin_unlock_irqrestore(&musb->lock, flags); | |
185 | ||
186 | return retval; | |
187 | } | |
188 | ||
743411b3 | 189 | static int omap2430_musb_init(struct musb *musb) |
550a7375 | 190 | { |
ad579699 S |
191 | u32 l; |
192 | int status = 0; | |
ea65df57 | 193 | struct device *dev = musb->controller; |
c1a7d67c | 194 | struct musb_hdrc_platform_data *plat = dev_get_platdata(dev); |
ea65df57 | 195 | struct omap_musb_board_data *data = plat->board_data; |
550a7375 | 196 | |
84e250ff DB |
197 | /* We require some kind of external transceiver, hooked |
198 | * up through ULPI. TWL4030-family PMICs include one, | |
199 | * which needs a driver, drivers aren't always needed. | |
200 | */ | |
5ee1787e JH |
201 | musb->phy = devm_phy_get(dev->parent, "usb2-phy"); |
202 | ||
203 | /* We can't totally remove musb->xceiv as of now because | |
204 | * musb core uses xceiv.state and xceiv.otg. Once we have | |
205 | * a separate state machine to handle otg, these can be moved | |
206 | * out of xceiv and then we can start using the generic PHY | |
207 | * framework | |
208 | */ | |
209 | musb->xceiv = devm_usb_get_phy_by_phandle(dev->parent, "usb-phy", 0); | |
b16604f2 | 210 | |
a90199bb FB |
211 | if (IS_ERR(musb->xceiv)) { |
212 | status = PTR_ERR(musb->xceiv); | |
213 | ||
214 | if (status == -ENXIO) | |
215 | return status; | |
216 | ||
dc66ba8d | 217 | dev_dbg(dev, "HS USB OTG: no transceiver configured\n"); |
25736e0c | 218 | return -EPROBE_DEFER; |
84e250ff DB |
219 | } |
220 | ||
3e3101d5 | 221 | if (IS_ERR(musb->phy)) { |
71127a79 | 222 | dev_err(dev, "HS USB OTG: no PHY configured\n"); |
3e3101d5 KVA |
223 | return PTR_ERR(musb->phy); |
224 | } | |
baef653a | 225 | musb->isr = omap2430_musb_interrupt; |
a83e17d0 | 226 | phy_init(musb->phy); |
d8e5f0ec | 227 | phy_power_on(musb->phy); |
baef653a | 228 | |
8573e6a6 | 229 | l = musb_readl(musb->mregs, OTG_INTERFSEL); |
de2e1b0c MM |
230 | |
231 | if (data->interface_type == MUSB_INTERFACE_UTMI) { | |
232 | /* OMAP4 uses Internal PHY GS70 which uses UTMI interface */ | |
233 | l &= ~ULPI_12PIN; /* Disable ULPI */ | |
234 | l |= UTMI_8BIT; /* Enable UTMI */ | |
235 | } else { | |
236 | l |= ULPI_12PIN; | |
237 | } | |
238 | ||
8573e6a6 | 239 | musb_writel(musb->mregs, OTG_INTERFSEL, l); |
550a7375 | 240 | |
71127a79 | 241 | dev_dbg(dev, "HS USB OTG: revision 0x%x, sysconfig 0x%02x, " |
550a7375 | 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 | 248 | |
550a7375 FB |
249 | return 0; |
250 | } | |
251 | ||
002eda13 HH |
252 | static void omap2430_musb_enable(struct musb *musb) |
253 | { | |
002eda13 | 254 | struct device *dev = musb->controller; |
c9721438 | 255 | struct omap2430_glue *glue = dev_get_drvdata(dev->parent); |
a83e17d0 | 256 | |
8b359cbc TL |
257 | if (glue->status == MUSB_UNKNOWN) |
258 | glue->status = MUSB_VBUS_OFF; | |
259 | omap_musb_set_mailbox(glue); | |
002eda13 HH |
260 | } |
261 | ||
262 | static void omap2430_musb_disable(struct musb *musb) | |
263 | { | |
c9721438 KVA |
264 | struct device *dev = musb->controller; |
265 | struct omap2430_glue *glue = dev_get_drvdata(dev->parent); | |
266 | ||
8055555f | 267 | if (glue->status != MUSB_UNKNOWN) |
ca784be3 KVA |
268 | omap_control_usb_set_mode(glue->control_otghs, |
269 | USB_MODE_DISCONNECT); | |
550a7375 FB |
270 | } |
271 | ||
743411b3 | 272 | static int omap2430_musb_exit(struct musb *musb) |
550a7375 | 273 | { |
a83e17d0 TL |
274 | struct device *dev = musb->controller; |
275 | struct omap2430_glue *glue = dev_get_drvdata(dev->parent); | |
550a7375 | 276 | |
c20aebb9 | 277 | omap2430_low_level_exit(musb); |
d8e5f0ec | 278 | phy_power_off(musb->phy); |
3e3101d5 | 279 | phy_exit(musb->phy); |
a83e17d0 TL |
280 | musb->phy = NULL; |
281 | cancel_work_sync(&glue->omap_musb_mailbox_work); | |
c20aebb9 | 282 | |
550a7375 FB |
283 | return 0; |
284 | } | |
743411b3 | 285 | |
f7ec9437 | 286 | static const struct musb_platform_ops omap2430_ops = { |
f8e9f34f | 287 | .quirks = MUSB_DMA_INVENTRA, |
7f6283ed TL |
288 | #ifdef CONFIG_USB_INVENTRA_DMA |
289 | .dma_init = musbhs_dma_controller_create, | |
290 | .dma_exit = musbhs_dma_controller_destroy, | |
291 | #endif | |
743411b3 FB |
292 | .init = omap2430_musb_init, |
293 | .exit = omap2430_musb_exit, | |
294 | ||
002eda13 HH |
295 | .enable = omap2430_musb_enable, |
296 | .disable = omap2430_musb_disable, | |
8055555f TL |
297 | |
298 | .phy_callback = omap2430_musb_mailbox, | |
743411b3 | 299 | }; |
dc09886b FB |
300 | |
301 | static u64 omap2430_dmamask = DMA_BIT_MASK(32); | |
302 | ||
41ac7b3a | 303 | static int omap2430_probe(struct platform_device *pdev) |
dc09886b | 304 | { |
c1a7d67c | 305 | struct musb_hdrc_platform_data *pdata = dev_get_platdata(&pdev->dev); |
00a0b1d5 | 306 | struct omap_musb_board_data *data; |
dc09886b | 307 | struct platform_device *musb; |
a3cee12a | 308 | struct omap2430_glue *glue; |
00a0b1d5 KVA |
309 | struct device_node *np = pdev->dev.of_node; |
310 | struct musb_hdrc_config *config; | |
5ee1787e JH |
311 | struct device_node *control_node; |
312 | struct platform_device *control_pdev; | |
606bf4d5 | 313 | int ret = -ENOMEM, val; |
ffbe2fea | 314 | bool populate_irqs = false; |
dc09886b | 315 | |
5ee1787e JH |
316 | if (!np) |
317 | return -ENODEV; | |
318 | ||
b1183c24 | 319 | glue = devm_kzalloc(&pdev->dev, sizeof(*glue), GFP_KERNEL); |
af1bdfc9 | 320 | if (!glue) |
a3cee12a | 321 | goto err0; |
a3cee12a | 322 | |
2f771164 | 323 | musb = platform_device_alloc("musb-hdrc", PLATFORM_DEVID_AUTO); |
dc09886b FB |
324 | if (!musb) { |
325 | dev_err(&pdev->dev, "failed to allocate musb device\n"); | |
2f771164 | 326 | goto err0; |
dc09886b FB |
327 | } |
328 | ||
329 | musb->dev.parent = &pdev->dev; | |
330 | musb->dev.dma_mask = &omap2430_dmamask; | |
331 | musb->dev.coherent_dma_mask = omap2430_dmamask; | |
332 | ||
ffbe2fea TL |
333 | /* |
334 | * Legacy SoCs using omap_device get confused if node is moved | |
335 | * because of interconnect properties mixed into the node. | |
336 | */ | |
a3927e1a | 337 | if (of_property_present(np, "ti,hwmods")) { |
ffbe2fea TL |
338 | dev_warn(&pdev->dev, "please update to probe with ti-sysc\n"); |
339 | populate_irqs = true; | |
340 | } else { | |
341 | device_set_of_node_from_dev(&musb->dev, &pdev->dev); | |
342 | } | |
343 | of_node_put(np); | |
344 | ||
a3cee12a FB |
345 | glue->dev = &pdev->dev; |
346 | glue->musb = musb; | |
8055555f | 347 | glue->status = MUSB_UNKNOWN; |
8934d3e4 | 348 | glue->control_otghs = ERR_PTR(-ENODEV); |
a3cee12a | 349 | |
5ee1787e JH |
350 | pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); |
351 | if (!pdata) | |
352 | goto err2; | |
353 | ||
354 | data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); | |
355 | if (!data) | |
356 | goto err2; | |
357 | ||
358 | config = devm_kzalloc(&pdev->dev, sizeof(*config), GFP_KERNEL); | |
359 | if (!config) | |
360 | goto err2; | |
361 | ||
362 | of_property_read_u32(np, "mode", (u32 *)&pdata->mode); | |
363 | of_property_read_u32(np, "interface-type", | |
364 | (u32 *)&data->interface_type); | |
365 | of_property_read_u32(np, "num-eps", (u32 *)&config->num_eps); | |
366 | of_property_read_u32(np, "ram-bits", (u32 *)&config->ram_bits); | |
367 | of_property_read_u32(np, "power", (u32 *)&pdata->power); | |
368 | ||
369 | ret = of_property_read_u32(np, "multipoint", &val); | |
370 | if (!ret && val) | |
371 | config->multipoint = true; | |
372 | ||
373 | pdata->board_data = data; | |
374 | pdata->config = config; | |
375 | ||
376 | control_node = of_parse_phandle(np, "ctrl-module", 0); | |
377 | if (control_node) { | |
378 | control_pdev = of_find_device_by_node(control_node); | |
424bef51 | 379 | of_node_put(control_node); |
5ee1787e JH |
380 | if (!control_pdev) { |
381 | dev_err(&pdev->dev, "Failed to get control device\n"); | |
382 | ret = -EINVAL; | |
2f771164 | 383 | goto err2; |
ca784be3 | 384 | } |
5ee1787e | 385 | glue->control_otghs = &control_pdev->dev; |
ca784be3 | 386 | } |
5ee1787e | 387 | |
f7ec9437 FB |
388 | pdata->platform_ops = &omap2430_ops; |
389 | ||
a3cee12a | 390 | platform_set_drvdata(pdev, glue); |
dc09886b | 391 | |
c9721438 KVA |
392 | /* |
393 | * REVISIT if we ever have two instances of the wrapper, we will be | |
394 | * in big trouble | |
395 | */ | |
396 | _glue = glue; | |
397 | ||
1e5acb8d KVA |
398 | INIT_WORK(&glue->omap_musb_mailbox_work, omap_musb_mailbox_work); |
399 | ||
9879c81b | 400 | ret = platform_device_add_resources(musb, pdev->resource, pdev->num_resources); |
dc09886b FB |
401 | if (ret) { |
402 | dev_err(&pdev->dev, "failed to add resources\n"); | |
65b3d52d | 403 | goto err2; |
dc09886b FB |
404 | } |
405 | ||
ffbe2fea TL |
406 | if (populate_irqs) { |
407 | struct resource musb_res[3]; | |
408 | struct resource *res; | |
409 | int i = 0; | |
410 | ||
411 | memset(musb_res, 0, sizeof(*musb_res) * ARRAY_SIZE(musb_res)); | |
412 | ||
413 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | |
bd449ad8 YY |
414 | if (!res) { |
415 | ret = -EINVAL; | |
ffbe2fea | 416 | goto err2; |
bd449ad8 | 417 | } |
ffbe2fea TL |
418 | |
419 | musb_res[i].start = res->start; | |
420 | musb_res[i].end = res->end; | |
421 | musb_res[i].flags = res->flags; | |
422 | musb_res[i].name = res->name; | |
423 | i++; | |
424 | ||
425 | ret = of_irq_get_byname(np, "mc"); | |
426 | if (ret > 0) { | |
427 | musb_res[i].start = ret; | |
428 | musb_res[i].flags = IORESOURCE_IRQ; | |
429 | musb_res[i].name = "mc"; | |
430 | i++; | |
431 | } | |
432 | ||
433 | ret = of_irq_get_byname(np, "dma"); | |
434 | if (ret > 0) { | |
435 | musb_res[i].start = ret; | |
436 | musb_res[i].flags = IORESOURCE_IRQ; | |
437 | musb_res[i].name = "dma"; | |
438 | i++; | |
439 | } | |
440 | ||
441 | ret = platform_device_add_resources(musb, musb_res, i); | |
442 | if (ret) { | |
443 | dev_err(&pdev->dev, "failed to add IRQ resources\n"); | |
444 | goto err2; | |
445 | } | |
446 | } | |
447 | ||
dc09886b FB |
448 | ret = platform_device_add_data(musb, pdata, sizeof(*pdata)); |
449 | if (ret) { | |
450 | dev_err(&pdev->dev, "failed to add platform_data\n"); | |
65b3d52d | 451 | goto err2; |
dc09886b FB |
452 | } |
453 | ||
87326e85 | 454 | pm_runtime_enable(glue->dev); |
3006dc8c | 455 | |
dc09886b FB |
456 | ret = platform_device_add(musb); |
457 | if (ret) { | |
458 | dev_err(&pdev->dev, "failed to register musb device\n"); | |
536d599d | 459 | goto err3; |
dc09886b FB |
460 | } |
461 | ||
207b0e1f | 462 | return 0; |
03491761 | 463 | |
536d599d TL |
464 | err3: |
465 | pm_runtime_disable(glue->dev); | |
466 | ||
65b3d52d | 467 | err2: |
b1183c24 | 468 | platform_device_put(musb); |
a3cee12a | 469 | |
dc09886b FB |
470 | err0: |
471 | return ret; | |
472 | } | |
473 | ||
cb020bf5 | 474 | static void omap2430_remove(struct platform_device *pdev) |
dc09886b | 475 | { |
21f77bee | 476 | struct omap2430_glue *glue = platform_get_drvdata(pdev); |
dc09886b | 477 | |
f039df58 | 478 | platform_device_unregister(glue->musb); |
03e43528 | 479 | pm_runtime_disable(glue->dev); |
dc09886b FB |
480 | } |
481 | ||
c20aebb9 | 482 | #ifdef CONFIG_PM |
c20aebb9 | 483 | |
7acc6197 | 484 | static int omap2430_runtime_suspend(struct device *dev) |
c20aebb9 FB |
485 | { |
486 | struct omap2430_glue *glue = dev_get_drvdata(dev); | |
487 | struct musb *musb = glue_to_musb(glue); | |
488 | ||
a83e17d0 TL |
489 | if (!musb) |
490 | return 0; | |
e25bec16 | 491 | |
a83e17d0 TL |
492 | musb->context.otg_interfsel = musb_readl(musb->mregs, |
493 | OTG_INTERFSEL); | |
494 | ||
495 | omap2430_low_level_exit(musb); | |
c20aebb9 | 496 | |
68d9f95d TL |
497 | if (!glue->phy_suspended) { |
498 | phy_power_off(musb->phy); | |
499 | phy_exit(musb->phy); | |
500 | } | |
10ac7e77 | 501 | |
62d472d8 TL |
502 | glue->is_runtime_suspended = 1; |
503 | ||
c20aebb9 FB |
504 | return 0; |
505 | } | |
506 | ||
7acc6197 | 507 | static int omap2430_runtime_resume(struct device *dev) |
c20aebb9 FB |
508 | { |
509 | struct omap2430_glue *glue = dev_get_drvdata(dev); | |
510 | struct musb *musb = glue_to_musb(glue); | |
c20aebb9 | 511 | |
8f2279d5 | 512 | if (!musb) |
a83e17d0 | 513 | return 0; |
8f2279d5 | 514 | |
68d9f95d TL |
515 | if (!glue->phy_suspended) { |
516 | phy_init(musb->phy); | |
517 | phy_power_on(musb->phy); | |
518 | } | |
10ac7e77 | 519 | |
8f2279d5 TL |
520 | omap2430_low_level_init(musb); |
521 | musb_writel(musb->mregs, OTG_INTERFSEL, | |
522 | musb->context.otg_interfsel); | |
c20aebb9 | 523 | |
ce3ab650 TL |
524 | /* Wait for musb to get oriented. Otherwise we can get babble */ |
525 | usleep_range(200000, 250000); | |
526 | ||
62d472d8 TL |
527 | glue->is_runtime_suspended = 0; |
528 | ||
c20aebb9 FB |
529 | return 0; |
530 | } | |
531 | ||
68d9f95d | 532 | /* I2C and SPI PHYs need to be suspended before the glue layer */ |
62d472d8 | 533 | static int omap2430_suspend(struct device *dev) |
68d9f95d TL |
534 | { |
535 | struct omap2430_glue *glue = dev_get_drvdata(dev); | |
536 | struct musb *musb = glue_to_musb(glue); | |
537 | ||
538 | phy_power_off(musb->phy); | |
539 | phy_exit(musb->phy); | |
540 | glue->phy_suspended = 1; | |
541 | ||
542 | return 0; | |
543 | } | |
544 | ||
545 | /* Glue layer needs to be suspended after musb_suspend() */ | |
546 | static int omap2430_suspend_late(struct device *dev) | |
62d472d8 TL |
547 | { |
548 | struct omap2430_glue *glue = dev_get_drvdata(dev); | |
549 | ||
550 | if (glue->is_runtime_suspended) | |
551 | return 0; | |
552 | ||
553 | glue->needs_resume = 1; | |
554 | ||
555 | return omap2430_runtime_suspend(dev); | |
556 | } | |
557 | ||
68d9f95d | 558 | static int omap2430_resume_early(struct device *dev) |
62d472d8 TL |
559 | { |
560 | struct omap2430_glue *glue = dev_get_drvdata(dev); | |
561 | ||
562 | if (!glue->needs_resume) | |
563 | return 0; | |
564 | ||
565 | glue->needs_resume = 0; | |
566 | ||
567 | return omap2430_runtime_resume(dev); | |
568 | } | |
569 | ||
68d9f95d TL |
570 | static int omap2430_resume(struct device *dev) |
571 | { | |
572 | struct omap2430_glue *glue = dev_get_drvdata(dev); | |
573 | struct musb *musb = glue_to_musb(glue); | |
574 | ||
575 | phy_init(musb->phy); | |
576 | phy_power_on(musb->phy); | |
577 | glue->phy_suspended = 0; | |
578 | ||
579 | return 0; | |
580 | } | |
581 | ||
c0927fea | 582 | static const struct dev_pm_ops omap2430_pm_ops = { |
7acc6197 HH |
583 | .runtime_suspend = omap2430_runtime_suspend, |
584 | .runtime_resume = omap2430_runtime_resume, | |
62d472d8 | 585 | .suspend = omap2430_suspend, |
68d9f95d TL |
586 | .suspend_late = omap2430_suspend_late, |
587 | .resume_early = omap2430_resume_early, | |
62d472d8 | 588 | .resume = omap2430_resume, |
c20aebb9 FB |
589 | }; |
590 | ||
591 | #define DEV_PM_OPS (&omap2430_pm_ops) | |
592 | #else | |
593 | #define DEV_PM_OPS NULL | |
594 | #endif | |
595 | ||
00a0b1d5 KVA |
596 | #ifdef CONFIG_OF |
597 | static const struct of_device_id omap2430_id_table[] = { | |
598 | { | |
599 | .compatible = "ti,omap4-musb" | |
600 | }, | |
601 | { | |
602 | .compatible = "ti,omap3-musb" | |
603 | }, | |
604 | {}, | |
605 | }; | |
606 | MODULE_DEVICE_TABLE(of, omap2430_id_table); | |
607 | #endif | |
608 | ||
dc09886b | 609 | static struct platform_driver omap2430_driver = { |
e9e8c85e | 610 | .probe = omap2430_probe, |
9a0749d6 | 611 | .remove = omap2430_remove, |
dc09886b FB |
612 | .driver = { |
613 | .name = "musb-omap2430", | |
c20aebb9 | 614 | .pm = DEV_PM_OPS, |
00a0b1d5 | 615 | .of_match_table = of_match_ptr(omap2430_id_table), |
dc09886b FB |
616 | }, |
617 | }; | |
618 | ||
aec373c1 TL |
619 | module_platform_driver(omap2430_driver); |
620 | ||
dc09886b FB |
621 | MODULE_DESCRIPTION("OMAP2PLUS MUSB Glue Layer"); |
622 | MODULE_AUTHOR("Felipe Balbi <balbi@ti.com>"); | |
623 | MODULE_LICENSE("GPL v2"); |