diff options
Diffstat (limited to 'drivers/mfd/omap-usb-tll.c')
-rw-r--r-- | drivers/mfd/omap-usb-tll.c | 243 |
1 files changed, 134 insertions, 109 deletions
diff --git a/drivers/mfd/omap-usb-tll.c b/drivers/mfd/omap-usb-tll.c index eb869153206d..0aef1a768880 100644 --- a/drivers/mfd/omap-usb-tll.c +++ b/drivers/mfd/omap-usb-tll.c | |||
@@ -54,10 +54,13 @@ | |||
54 | 54 | ||
55 | #define OMAP_TLL_CHANNEL_CONF(num) (0x040 + 0x004 * num) | 55 | #define OMAP_TLL_CHANNEL_CONF(num) (0x040 + 0x004 * num) |
56 | #define OMAP_TLL_CHANNEL_CONF_FSLSMODE_SHIFT 24 | 56 | #define OMAP_TLL_CHANNEL_CONF_FSLSMODE_SHIFT 24 |
57 | #define OMAP_TLL_CHANNEL_CONF_DRVVBUS (1 << 16) | ||
58 | #define OMAP_TLL_CHANNEL_CONF_CHRGVBUS (1 << 15) | ||
57 | #define OMAP_TLL_CHANNEL_CONF_ULPINOBITSTUFF (1 << 11) | 59 | #define OMAP_TLL_CHANNEL_CONF_ULPINOBITSTUFF (1 << 11) |
58 | #define OMAP_TLL_CHANNEL_CONF_ULPI_ULPIAUTOIDLE (1 << 10) | 60 | #define OMAP_TLL_CHANNEL_CONF_ULPI_ULPIAUTOIDLE (1 << 10) |
59 | #define OMAP_TLL_CHANNEL_CONF_UTMIAUTOIDLE (1 << 9) | 61 | #define OMAP_TLL_CHANNEL_CONF_UTMIAUTOIDLE (1 << 9) |
60 | #define OMAP_TLL_CHANNEL_CONF_ULPIDDRMODE (1 << 8) | 62 | #define OMAP_TLL_CHANNEL_CONF_ULPIDDRMODE (1 << 8) |
63 | #define OMAP_TLL_CHANNEL_CONF_MODE_TRANSPARENT_UTMI (2 << 1) | ||
61 | #define OMAP_TLL_CHANNEL_CONF_CHANMODE_FSLS (1 << 1) | 64 | #define OMAP_TLL_CHANNEL_CONF_CHANMODE_FSLS (1 << 1) |
62 | #define OMAP_TLL_CHANNEL_CONF_CHANEN (1 << 0) | 65 | #define OMAP_TLL_CHANNEL_CONF_CHANEN (1 << 0) |
63 | 66 | ||
@@ -92,21 +95,25 @@ | |||
92 | #define OMAP_USBTLL_REV1 0x00000015 /* OMAP3 */ | 95 | #define OMAP_USBTLL_REV1 0x00000015 /* OMAP3 */ |
93 | #define OMAP_USBTLL_REV2 0x00000018 /* OMAP 3630 */ | 96 | #define OMAP_USBTLL_REV2 0x00000018 /* OMAP 3630 */ |
94 | #define OMAP_USBTLL_REV3 0x00000004 /* OMAP4 */ | 97 | #define OMAP_USBTLL_REV3 0x00000004 /* OMAP4 */ |
98 | #define OMAP_USBTLL_REV4 0x00000006 /* OMAP5 */ | ||
95 | 99 | ||
96 | #define is_ehci_tll_mode(x) (x == OMAP_EHCI_PORT_MODE_TLL) | 100 | #define is_ehci_tll_mode(x) (x == OMAP_EHCI_PORT_MODE_TLL) |
97 | 101 | ||
102 | /* only PHY and UNUSED modes don't need TLL */ | ||
103 | #define omap_usb_mode_needs_tll(x) ((x) != OMAP_USBHS_PORT_MODE_UNUSED &&\ | ||
104 | (x) != OMAP_EHCI_PORT_MODE_PHY) | ||
105 | |||
98 | struct usbtll_omap { | 106 | struct usbtll_omap { |
99 | struct clk *usbtll_p1_fck; | 107 | int nch; /* num. of channels */ |
100 | struct clk *usbtll_p2_fck; | 108 | struct usbhs_omap_platform_data *pdata; |
101 | struct usbtll_omap_platform_data platdata; | 109 | struct clk **ch_clk; |
102 | /* secure the register updates */ | ||
103 | spinlock_t lock; | ||
104 | }; | 110 | }; |
105 | 111 | ||
106 | /*-------------------------------------------------------------------------*/ | 112 | /*-------------------------------------------------------------------------*/ |
107 | 113 | ||
108 | const char usbtll_driver_name[] = USBTLL_DRIVER_NAME; | 114 | static const char usbtll_driver_name[] = USBTLL_DRIVER_NAME; |
109 | struct platform_device *tll_pdev; | 115 | static struct device *tll_dev; |
116 | static DEFINE_SPINLOCK(tll_lock); /* serialize access to tll_dev */ | ||
110 | 117 | ||
111 | /*-------------------------------------------------------------------------*/ | 118 | /*-------------------------------------------------------------------------*/ |
112 | 119 | ||
@@ -203,84 +210,84 @@ static unsigned ohci_omap3_fslsmode(enum usbhs_omap_port_mode mode) | |||
203 | static int usbtll_omap_probe(struct platform_device *pdev) | 210 | static int usbtll_omap_probe(struct platform_device *pdev) |
204 | { | 211 | { |
205 | struct device *dev = &pdev->dev; | 212 | struct device *dev = &pdev->dev; |
206 | struct usbtll_omap_platform_data *pdata = dev->platform_data; | 213 | struct usbhs_omap_platform_data *pdata = dev->platform_data; |
207 | void __iomem *base; | 214 | void __iomem *base; |
208 | struct resource *res; | 215 | struct resource *res; |
209 | struct usbtll_omap *tll; | 216 | struct usbtll_omap *tll; |
210 | unsigned reg; | 217 | unsigned reg; |
211 | unsigned long flags; | ||
212 | int ret = 0; | 218 | int ret = 0; |
213 | int i, ver, count; | 219 | int i, ver; |
220 | bool needs_tll; | ||
214 | 221 | ||
215 | dev_dbg(dev, "starting TI HSUSB TLL Controller\n"); | 222 | dev_dbg(dev, "starting TI HSUSB TLL Controller\n"); |
216 | 223 | ||
217 | tll = kzalloc(sizeof(struct usbtll_omap), GFP_KERNEL); | 224 | tll = devm_kzalloc(dev, sizeof(struct usbtll_omap), GFP_KERNEL); |
218 | if (!tll) { | 225 | if (!tll) { |
219 | dev_err(dev, "Memory allocation failed\n"); | 226 | dev_err(dev, "Memory allocation failed\n"); |
220 | ret = -ENOMEM; | 227 | return -ENOMEM; |
221 | goto end; | ||
222 | } | 228 | } |
223 | 229 | ||
224 | spin_lock_init(&tll->lock); | 230 | if (!pdata) { |
225 | 231 | dev_err(dev, "Platform data missing\n"); | |
226 | for (i = 0; i < OMAP3_HS_USB_PORTS; i++) | 232 | return -ENODEV; |
227 | tll->platdata.port_mode[i] = pdata->port_mode[i]; | ||
228 | |||
229 | tll->usbtll_p1_fck = clk_get(dev, "usb_tll_hs_usb_ch0_clk"); | ||
230 | if (IS_ERR(tll->usbtll_p1_fck)) { | ||
231 | ret = PTR_ERR(tll->usbtll_p1_fck); | ||
232 | dev_err(dev, "usbtll_p1_fck failed error:%d\n", ret); | ||
233 | goto err_tll; | ||
234 | } | 233 | } |
235 | 234 | ||
236 | tll->usbtll_p2_fck = clk_get(dev, "usb_tll_hs_usb_ch1_clk"); | 235 | tll->pdata = pdata; |
237 | if (IS_ERR(tll->usbtll_p2_fck)) { | ||
238 | ret = PTR_ERR(tll->usbtll_p2_fck); | ||
239 | dev_err(dev, "usbtll_p2_fck failed error:%d\n", ret); | ||
240 | goto err_usbtll_p1_fck; | ||
241 | } | ||
242 | 236 | ||
243 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 237 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
244 | if (!res) { | 238 | base = devm_request_and_ioremap(dev, res); |
245 | dev_err(dev, "usb tll get resource failed\n"); | ||
246 | ret = -ENODEV; | ||
247 | goto err_usbtll_p2_fck; | ||
248 | } | ||
249 | |||
250 | base = ioremap(res->start, resource_size(res)); | ||
251 | if (!base) { | 239 | if (!base) { |
252 | dev_err(dev, "TLL ioremap failed\n"); | 240 | ret = -EADDRNOTAVAIL; |
253 | ret = -ENOMEM; | 241 | dev_err(dev, "Resource request/ioremap failed:%d\n", ret); |
254 | goto err_usbtll_p2_fck; | 242 | return ret; |
255 | } | 243 | } |
256 | 244 | ||
257 | platform_set_drvdata(pdev, tll); | 245 | platform_set_drvdata(pdev, tll); |
258 | pm_runtime_enable(dev); | 246 | pm_runtime_enable(dev); |
259 | pm_runtime_get_sync(dev); | 247 | pm_runtime_get_sync(dev); |
260 | 248 | ||
261 | spin_lock_irqsave(&tll->lock, flags); | ||
262 | |||
263 | ver = usbtll_read(base, OMAP_USBTLL_REVISION); | 249 | ver = usbtll_read(base, OMAP_USBTLL_REVISION); |
264 | switch (ver) { | 250 | switch (ver) { |
265 | case OMAP_USBTLL_REV1: | 251 | case OMAP_USBTLL_REV1: |
266 | case OMAP_USBTLL_REV2: | 252 | case OMAP_USBTLL_REV4: |
267 | count = OMAP_TLL_CHANNEL_COUNT; | 253 | tll->nch = OMAP_TLL_CHANNEL_COUNT; |
268 | break; | 254 | break; |
255 | case OMAP_USBTLL_REV2: | ||
269 | case OMAP_USBTLL_REV3: | 256 | case OMAP_USBTLL_REV3: |
270 | count = OMAP_REV2_TLL_CHANNEL_COUNT; | 257 | tll->nch = OMAP_REV2_TLL_CHANNEL_COUNT; |
271 | break; | 258 | break; |
272 | default: | 259 | default: |
273 | dev_err(dev, "TLL version failed\n"); | 260 | tll->nch = OMAP_TLL_CHANNEL_COUNT; |
274 | ret = -ENODEV; | 261 | dev_dbg(dev, |
275 | goto err_ioremap; | 262 | "USB TLL Rev : 0x%x not recognized, assuming %d channels\n", |
263 | ver, tll->nch); | ||
264 | break; | ||
276 | } | 265 | } |
277 | 266 | ||
278 | if (is_ehci_tll_mode(pdata->port_mode[0]) || | 267 | tll->ch_clk = devm_kzalloc(dev, sizeof(struct clk * [tll->nch]), |
279 | is_ehci_tll_mode(pdata->port_mode[1]) || | 268 | GFP_KERNEL); |
280 | is_ehci_tll_mode(pdata->port_mode[2]) || | 269 | if (!tll->ch_clk) { |
281 | is_ohci_port(pdata->port_mode[0]) || | 270 | ret = -ENOMEM; |
282 | is_ohci_port(pdata->port_mode[1]) || | 271 | dev_err(dev, "Couldn't allocate memory for channel clocks\n"); |
283 | is_ohci_port(pdata->port_mode[2])) { | 272 | goto err_clk_alloc; |
273 | } | ||
274 | |||
275 | for (i = 0; i < tll->nch; i++) { | ||
276 | char clkname[] = "usb_tll_hs_usb_chx_clk"; | ||
277 | |||
278 | snprintf(clkname, sizeof(clkname), | ||
279 | "usb_tll_hs_usb_ch%d_clk", i); | ||
280 | tll->ch_clk[i] = clk_get(dev, clkname); | ||
281 | |||
282 | if (IS_ERR(tll->ch_clk[i])) | ||
283 | dev_dbg(dev, "can't get clock : %s\n", clkname); | ||
284 | } | ||
285 | |||
286 | needs_tll = false; | ||
287 | for (i = 0; i < tll->nch; i++) | ||
288 | needs_tll |= omap_usb_mode_needs_tll(pdata->port_mode[i]); | ||
289 | |||
290 | if (needs_tll) { | ||
284 | 291 | ||
285 | /* Program Common TLL register */ | 292 | /* Program Common TLL register */ |
286 | reg = usbtll_read(base, OMAP_TLL_SHARED_CONF); | 293 | reg = usbtll_read(base, OMAP_TLL_SHARED_CONF); |
@@ -292,7 +299,7 @@ static int usbtll_omap_probe(struct platform_device *pdev) | |||
292 | usbtll_write(base, OMAP_TLL_SHARED_CONF, reg); | 299 | usbtll_write(base, OMAP_TLL_SHARED_CONF, reg); |
293 | 300 | ||
294 | /* Enable channels now */ | 301 | /* Enable channels now */ |
295 | for (i = 0; i < count; i++) { | 302 | for (i = 0; i < tll->nch; i++) { |
296 | reg = usbtll_read(base, OMAP_TLL_CHANNEL_CONF(i)); | 303 | reg = usbtll_read(base, OMAP_TLL_CHANNEL_CONF(i)); |
297 | 304 | ||
298 | if (is_ohci_port(pdata->port_mode[i])) { | 305 | if (is_ohci_port(pdata->port_mode[i])) { |
@@ -308,6 +315,15 @@ static int usbtll_omap_probe(struct platform_device *pdev) | |||
308 | reg &= ~(OMAP_TLL_CHANNEL_CONF_UTMIAUTOIDLE | 315 | reg &= ~(OMAP_TLL_CHANNEL_CONF_UTMIAUTOIDLE |
309 | | OMAP_TLL_CHANNEL_CONF_ULPINOBITSTUFF | 316 | | OMAP_TLL_CHANNEL_CONF_ULPINOBITSTUFF |
310 | | OMAP_TLL_CHANNEL_CONF_ULPIDDRMODE); | 317 | | OMAP_TLL_CHANNEL_CONF_ULPIDDRMODE); |
318 | } else if (pdata->port_mode[i] == | ||
319 | OMAP_EHCI_PORT_MODE_HSIC) { | ||
320 | /* | ||
321 | * HSIC Mode requires UTMI port configurations | ||
322 | */ | ||
323 | reg |= OMAP_TLL_CHANNEL_CONF_DRVVBUS | ||
324 | | OMAP_TLL_CHANNEL_CONF_CHRGVBUS | ||
325 | | OMAP_TLL_CHANNEL_CONF_MODE_TRANSPARENT_UTMI | ||
326 | | OMAP_TLL_CHANNEL_CONF_ULPINOBITSTUFF; | ||
311 | } else { | 327 | } else { |
312 | continue; | 328 | continue; |
313 | } | 329 | } |
@@ -320,25 +336,18 @@ static int usbtll_omap_probe(struct platform_device *pdev) | |||
320 | } | 336 | } |
321 | } | 337 | } |
322 | 338 | ||
323 | err_ioremap: | ||
324 | spin_unlock_irqrestore(&tll->lock, flags); | ||
325 | iounmap(base); | ||
326 | pm_runtime_put_sync(dev); | 339 | pm_runtime_put_sync(dev); |
327 | tll_pdev = pdev; | 340 | /* only after this can omap_tll_enable/disable work */ |
328 | if (!ret) | 341 | spin_lock(&tll_lock); |
329 | goto end; | 342 | tll_dev = dev; |
330 | pm_runtime_disable(dev); | 343 | spin_unlock(&tll_lock); |
331 | 344 | ||
332 | err_usbtll_p2_fck: | 345 | return 0; |
333 | clk_put(tll->usbtll_p2_fck); | ||
334 | |||
335 | err_usbtll_p1_fck: | ||
336 | clk_put(tll->usbtll_p1_fck); | ||
337 | 346 | ||
338 | err_tll: | 347 | err_clk_alloc: |
339 | kfree(tll); | 348 | pm_runtime_put_sync(dev); |
349 | pm_runtime_disable(dev); | ||
340 | 350 | ||
341 | end: | ||
342 | return ret; | 351 | return ret; |
343 | } | 352 | } |
344 | 353 | ||
@@ -351,36 +360,42 @@ end: | |||
351 | static int usbtll_omap_remove(struct platform_device *pdev) | 360 | static int usbtll_omap_remove(struct platform_device *pdev) |
352 | { | 361 | { |
353 | struct usbtll_omap *tll = platform_get_drvdata(pdev); | 362 | struct usbtll_omap *tll = platform_get_drvdata(pdev); |
363 | int i; | ||
364 | |||
365 | spin_lock(&tll_lock); | ||
366 | tll_dev = NULL; | ||
367 | spin_unlock(&tll_lock); | ||
368 | |||
369 | for (i = 0; i < tll->nch; i++) | ||
370 | if (!IS_ERR(tll->ch_clk[i])) | ||
371 | clk_put(tll->ch_clk[i]); | ||
354 | 372 | ||
355 | clk_put(tll->usbtll_p2_fck); | ||
356 | clk_put(tll->usbtll_p1_fck); | ||
357 | pm_runtime_disable(&pdev->dev); | 373 | pm_runtime_disable(&pdev->dev); |
358 | kfree(tll); | ||
359 | return 0; | 374 | return 0; |
360 | } | 375 | } |
361 | 376 | ||
362 | static int usbtll_runtime_resume(struct device *dev) | 377 | static int usbtll_runtime_resume(struct device *dev) |
363 | { | 378 | { |
364 | struct usbtll_omap *tll = dev_get_drvdata(dev); | 379 | struct usbtll_omap *tll = dev_get_drvdata(dev); |
365 | struct usbtll_omap_platform_data *pdata = &tll->platdata; | 380 | struct usbhs_omap_platform_data *pdata = tll->pdata; |
366 | unsigned long flags; | 381 | int i; |
367 | 382 | ||
368 | dev_dbg(dev, "usbtll_runtime_resume\n"); | 383 | dev_dbg(dev, "usbtll_runtime_resume\n"); |
369 | 384 | ||
370 | if (!pdata) { | 385 | for (i = 0; i < tll->nch; i++) { |
371 | dev_dbg(dev, "missing platform_data\n"); | 386 | if (omap_usb_mode_needs_tll(pdata->port_mode[i])) { |
372 | return -ENODEV; | 387 | int r; |
373 | } | ||
374 | |||
375 | spin_lock_irqsave(&tll->lock, flags); | ||
376 | 388 | ||
377 | if (is_ehci_tll_mode(pdata->port_mode[0])) | 389 | if (IS_ERR(tll->ch_clk[i])) |
378 | clk_enable(tll->usbtll_p1_fck); | 390 | continue; |
379 | |||
380 | if (is_ehci_tll_mode(pdata->port_mode[1])) | ||
381 | clk_enable(tll->usbtll_p2_fck); | ||
382 | 391 | ||
383 | spin_unlock_irqrestore(&tll->lock, flags); | 392 | r = clk_enable(tll->ch_clk[i]); |
393 | if (r) { | ||
394 | dev_err(dev, | ||
395 | "Error enabling ch %d clock: %d\n", i, r); | ||
396 | } | ||
397 | } | ||
398 | } | ||
384 | 399 | ||
385 | return 0; | 400 | return 0; |
386 | } | 401 | } |
@@ -388,26 +403,18 @@ static int usbtll_runtime_resume(struct device *dev) | |||
388 | static int usbtll_runtime_suspend(struct device *dev) | 403 | static int usbtll_runtime_suspend(struct device *dev) |
389 | { | 404 | { |
390 | struct usbtll_omap *tll = dev_get_drvdata(dev); | 405 | struct usbtll_omap *tll = dev_get_drvdata(dev); |
391 | struct usbtll_omap_platform_data *pdata = &tll->platdata; | 406 | struct usbhs_omap_platform_data *pdata = tll->pdata; |
392 | unsigned long flags; | 407 | int i; |
393 | 408 | ||
394 | dev_dbg(dev, "usbtll_runtime_suspend\n"); | 409 | dev_dbg(dev, "usbtll_runtime_suspend\n"); |
395 | 410 | ||
396 | if (!pdata) { | 411 | for (i = 0; i < tll->nch; i++) { |
397 | dev_dbg(dev, "missing platform_data\n"); | 412 | if (omap_usb_mode_needs_tll(pdata->port_mode[i])) { |
398 | return -ENODEV; | 413 | if (!IS_ERR(tll->ch_clk[i])) |
414 | clk_disable(tll->ch_clk[i]); | ||
415 | } | ||
399 | } | 416 | } |
400 | 417 | ||
401 | spin_lock_irqsave(&tll->lock, flags); | ||
402 | |||
403 | if (is_ehci_tll_mode(pdata->port_mode[0])) | ||
404 | clk_disable(tll->usbtll_p1_fck); | ||
405 | |||
406 | if (is_ehci_tll_mode(pdata->port_mode[1])) | ||
407 | clk_disable(tll->usbtll_p2_fck); | ||
408 | |||
409 | spin_unlock_irqrestore(&tll->lock, flags); | ||
410 | |||
411 | return 0; | 418 | return 0; |
412 | } | 419 | } |
413 | 420 | ||
@@ -429,21 +436,39 @@ static struct platform_driver usbtll_omap_driver = { | |||
429 | 436 | ||
430 | int omap_tll_enable(void) | 437 | int omap_tll_enable(void) |
431 | { | 438 | { |
432 | if (!tll_pdev) { | 439 | int ret; |
433 | pr_err("missing omap usbhs tll platform_data\n"); | 440 | |
434 | return -ENODEV; | 441 | spin_lock(&tll_lock); |
442 | |||
443 | if (!tll_dev) { | ||
444 | pr_err("%s: OMAP USB TLL not initialized\n", __func__); | ||
445 | ret = -ENODEV; | ||
446 | } else { | ||
447 | ret = pm_runtime_get_sync(tll_dev); | ||
435 | } | 448 | } |
436 | return pm_runtime_get_sync(&tll_pdev->dev); | 449 | |
450 | spin_unlock(&tll_lock); | ||
451 | |||
452 | return ret; | ||
437 | } | 453 | } |
438 | EXPORT_SYMBOL_GPL(omap_tll_enable); | 454 | EXPORT_SYMBOL_GPL(omap_tll_enable); |
439 | 455 | ||
440 | int omap_tll_disable(void) | 456 | int omap_tll_disable(void) |
441 | { | 457 | { |
442 | if (!tll_pdev) { | 458 | int ret; |
443 | pr_err("missing omap usbhs tll platform_data\n"); | 459 | |
444 | return -ENODEV; | 460 | spin_lock(&tll_lock); |
461 | |||
462 | if (!tll_dev) { | ||
463 | pr_err("%s: OMAP USB TLL not initialized\n", __func__); | ||
464 | ret = -ENODEV; | ||
465 | } else { | ||
466 | ret = pm_runtime_put_sync(tll_dev); | ||
445 | } | 467 | } |
446 | return pm_runtime_put_sync(&tll_pdev->dev); | 468 | |
469 | spin_unlock(&tll_lock); | ||
470 | |||
471 | return ret; | ||
447 | } | 472 | } |
448 | EXPORT_SYMBOL_GPL(omap_tll_disable); | 473 | EXPORT_SYMBOL_GPL(omap_tll_disable); |
449 | 474 | ||