| .. | .. |
|---|
| 18 | 18 | #include <linux/clk.h> |
|---|
| 19 | 19 | #include <linux/dma-mapping.h> |
|---|
| 20 | 20 | #include <linux/err.h> |
|---|
| 21 | | -#include <linux/gpio.h> |
|---|
| 21 | +#include <linux/gpio/consumer.h> |
|---|
| 22 | 22 | #include <linux/io.h> |
|---|
| 23 | 23 | #include <linux/jiffies.h> |
|---|
| 24 | 24 | #include <linux/kernel.h> |
|---|
| .. | .. |
|---|
| 53 | 53 | |
|---|
| 54 | 54 | #define DRIVER_DESC "OHCI OMAP driver" |
|---|
| 55 | 55 | |
|---|
| 56 | | -#ifdef CONFIG_TPS65010 |
|---|
| 57 | | -#include <linux/mfd/tps65010.h> |
|---|
| 58 | | -#else |
|---|
| 59 | | - |
|---|
| 60 | | -#define LOW 0 |
|---|
| 61 | | -#define HIGH 1 |
|---|
| 62 | | - |
|---|
| 63 | | -#define GPIO1 1 |
|---|
| 64 | | - |
|---|
| 65 | | -static inline int tps65010_set_gpio_out_value(unsigned gpio, unsigned value) |
|---|
| 66 | | -{ |
|---|
| 67 | | - return 0; |
|---|
| 68 | | -} |
|---|
| 69 | | - |
|---|
| 70 | | -#endif |
|---|
| 71 | | - |
|---|
| 72 | | -static struct clk *usb_host_ck; |
|---|
| 73 | | -static struct clk *usb_dc_ck; |
|---|
| 56 | +struct ohci_omap_priv { |
|---|
| 57 | + struct clk *usb_host_ck; |
|---|
| 58 | + struct clk *usb_dc_ck; |
|---|
| 59 | + struct gpio_desc *power; |
|---|
| 60 | + struct gpio_desc *overcurrent; |
|---|
| 61 | +}; |
|---|
| 74 | 62 | |
|---|
| 75 | 63 | static const char hcd_name[] = "ohci-omap"; |
|---|
| 76 | 64 | static struct hc_driver __read_mostly ohci_omap_hc_driver; |
|---|
| 77 | 65 | |
|---|
| 78 | | -static void omap_ohci_clock_power(int on) |
|---|
| 66 | +#define hcd_to_ohci_omap_priv(h) \ |
|---|
| 67 | + ((struct ohci_omap_priv *)hcd_to_ohci(h)->priv) |
|---|
| 68 | + |
|---|
| 69 | +static void omap_ohci_clock_power(struct ohci_omap_priv *priv, int on) |
|---|
| 79 | 70 | { |
|---|
| 80 | 71 | if (on) { |
|---|
| 81 | | - clk_enable(usb_dc_ck); |
|---|
| 82 | | - clk_enable(usb_host_ck); |
|---|
| 72 | + clk_enable(priv->usb_dc_ck); |
|---|
| 73 | + clk_enable(priv->usb_host_ck); |
|---|
| 83 | 74 | /* guesstimate for T5 == 1x 32K clock + APLL lock time */ |
|---|
| 84 | 75 | udelay(100); |
|---|
| 85 | 76 | } else { |
|---|
| 86 | | - clk_disable(usb_host_ck); |
|---|
| 87 | | - clk_disable(usb_dc_ck); |
|---|
| 77 | + clk_disable(priv->usb_host_ck); |
|---|
| 78 | + clk_disable(priv->usb_dc_ck); |
|---|
| 88 | 79 | } |
|---|
| 89 | 80 | } |
|---|
| 90 | 81 | |
|---|
| .. | .. |
|---|
| 92 | 83 | * Board specific gang-switched transceiver power on/off. |
|---|
| 93 | 84 | * NOTE: OSK supplies power from DC, not battery. |
|---|
| 94 | 85 | */ |
|---|
| 95 | | -static int omap_ohci_transceiver_power(int on) |
|---|
| 86 | +static int omap_ohci_transceiver_power(struct ohci_omap_priv *priv, int on) |
|---|
| 96 | 87 | { |
|---|
| 97 | 88 | if (on) { |
|---|
| 98 | 89 | if (machine_is_omap_innovator() && cpu_is_omap1510()) |
|---|
| 99 | 90 | __raw_writeb(__raw_readb(INNOVATOR_FPGA_CAM_USB_CONTROL) |
|---|
| 100 | 91 | | ((1 << 5/*usb1*/) | (1 << 3/*usb2*/)), |
|---|
| 101 | 92 | INNOVATOR_FPGA_CAM_USB_CONTROL); |
|---|
| 102 | | - else if (machine_is_omap_osk()) |
|---|
| 103 | | - tps65010_set_gpio_out_value(GPIO1, LOW); |
|---|
| 93 | + else if (priv->power) |
|---|
| 94 | + gpiod_set_value_cansleep(priv->power, 0); |
|---|
| 104 | 95 | } else { |
|---|
| 105 | 96 | if (machine_is_omap_innovator() && cpu_is_omap1510()) |
|---|
| 106 | 97 | __raw_writeb(__raw_readb(INNOVATOR_FPGA_CAM_USB_CONTROL) |
|---|
| 107 | 98 | & ~((1 << 5/*usb1*/) | (1 << 3/*usb2*/)), |
|---|
| 108 | 99 | INNOVATOR_FPGA_CAM_USB_CONTROL); |
|---|
| 109 | | - else if (machine_is_omap_osk()) |
|---|
| 110 | | - tps65010_set_gpio_out_value(GPIO1, HIGH); |
|---|
| 100 | + else if (priv->power) |
|---|
| 101 | + gpiod_set_value_cansleep(priv->power, 1); |
|---|
| 111 | 102 | } |
|---|
| 112 | 103 | |
|---|
| 113 | 104 | return 0; |
|---|
| .. | .. |
|---|
| 196 | 187 | { |
|---|
| 197 | 188 | struct ohci_hcd *ohci = hcd_to_ohci(hcd); |
|---|
| 198 | 189 | struct omap_usb_config *config = dev_get_platdata(hcd->self.controller); |
|---|
| 190 | + struct ohci_omap_priv *priv = hcd_to_ohci_omap_priv(hcd); |
|---|
| 199 | 191 | int need_transceiver = (config->otg != 0); |
|---|
| 200 | 192 | int ret; |
|---|
| 201 | 193 | |
|---|
| .. | .. |
|---|
| 235 | 227 | } |
|---|
| 236 | 228 | #endif |
|---|
| 237 | 229 | |
|---|
| 238 | | - omap_ohci_clock_power(1); |
|---|
| 230 | + omap_ohci_clock_power(priv, 1); |
|---|
| 239 | 231 | |
|---|
| 240 | 232 | if (cpu_is_omap15xx()) { |
|---|
| 241 | 233 | omap_1510_local_bus_power(1); |
|---|
| .. | .. |
|---|
| 266 | 258 | |
|---|
| 267 | 259 | /* gpio9 for overcurrent detction */ |
|---|
| 268 | 260 | omap_cfg_reg(W8_1610_GPIO9); |
|---|
| 269 | | - gpio_request(9, "OHCI overcurrent"); |
|---|
| 270 | | - gpio_direction_input(9); |
|---|
| 271 | 261 | |
|---|
| 272 | 262 | /* for paranoia's sake: disable USB.PUEN */ |
|---|
| 273 | 263 | omap_cfg_reg(W4_USB_HIGHZ); |
|---|
| .. | .. |
|---|
| 281 | 271 | } |
|---|
| 282 | 272 | |
|---|
| 283 | 273 | /* FIXME hub_wq hub requests should manage power switching */ |
|---|
| 284 | | - omap_ohci_transceiver_power(1); |
|---|
| 274 | + omap_ohci_transceiver_power(priv, 1); |
|---|
| 285 | 275 | |
|---|
| 286 | 276 | /* board init will have already handled HMC and mux setup. |
|---|
| 287 | 277 | * any external transceiver should already be initialized |
|---|
| .. | .. |
|---|
| 305 | 295 | { |
|---|
| 306 | 296 | int retval, irq; |
|---|
| 307 | 297 | struct usb_hcd *hcd = 0; |
|---|
| 298 | + struct ohci_omap_priv *priv; |
|---|
| 308 | 299 | |
|---|
| 309 | 300 | if (pdev->num_resources != 2) { |
|---|
| 310 | 301 | dev_err(&pdev->dev, "invalid num_resources: %i\n", |
|---|
| .. | .. |
|---|
| 318 | 309 | return -ENODEV; |
|---|
| 319 | 310 | } |
|---|
| 320 | 311 | |
|---|
| 321 | | - usb_host_ck = clk_get(&pdev->dev, "usb_hhc_ck"); |
|---|
| 322 | | - if (IS_ERR(usb_host_ck)) |
|---|
| 323 | | - return PTR_ERR(usb_host_ck); |
|---|
| 324 | | - |
|---|
| 325 | | - if (!cpu_is_omap15xx()) |
|---|
| 326 | | - usb_dc_ck = clk_get(&pdev->dev, "usb_dc_ck"); |
|---|
| 327 | | - else |
|---|
| 328 | | - usb_dc_ck = clk_get(&pdev->dev, "lb_ck"); |
|---|
| 329 | | - |
|---|
| 330 | | - if (IS_ERR(usb_dc_ck)) { |
|---|
| 331 | | - clk_put(usb_host_ck); |
|---|
| 332 | | - return PTR_ERR(usb_dc_ck); |
|---|
| 333 | | - } |
|---|
| 334 | | - |
|---|
| 335 | | - |
|---|
| 336 | 312 | hcd = usb_create_hcd(&ohci_omap_hc_driver, &pdev->dev, |
|---|
| 337 | 313 | dev_name(&pdev->dev)); |
|---|
| 338 | | - if (!hcd) { |
|---|
| 339 | | - retval = -ENOMEM; |
|---|
| 340 | | - goto err0; |
|---|
| 341 | | - } |
|---|
| 314 | + if (!hcd) |
|---|
| 315 | + return -ENOMEM; |
|---|
| 316 | + |
|---|
| 342 | 317 | hcd->rsrc_start = pdev->resource[0].start; |
|---|
| 343 | 318 | hcd->rsrc_len = pdev->resource[0].end - pdev->resource[0].start + 1; |
|---|
| 319 | + priv = hcd_to_ohci_omap_priv(hcd); |
|---|
| 320 | + |
|---|
| 321 | + /* Obtain two optional GPIO lines */ |
|---|
| 322 | + priv->power = devm_gpiod_get_optional(&pdev->dev, "power", GPIOD_ASIS); |
|---|
| 323 | + if (IS_ERR(priv->power)) { |
|---|
| 324 | + retval = PTR_ERR(priv->power); |
|---|
| 325 | + goto err_put_hcd; |
|---|
| 326 | + } |
|---|
| 327 | + if (priv->power) |
|---|
| 328 | + gpiod_set_consumer_name(priv->power, "OHCI power"); |
|---|
| 329 | + |
|---|
| 330 | + /* |
|---|
| 331 | + * This "overcurrent" GPIO line isn't really used in the code, |
|---|
| 332 | + * but has a designated hardware function. |
|---|
| 333 | + * TODO: implement proper overcurrent handling. |
|---|
| 334 | + */ |
|---|
| 335 | + priv->overcurrent = devm_gpiod_get_optional(&pdev->dev, "overcurrent", |
|---|
| 336 | + GPIOD_IN); |
|---|
| 337 | + if (IS_ERR(priv->overcurrent)) { |
|---|
| 338 | + retval = PTR_ERR(priv->overcurrent); |
|---|
| 339 | + goto err_put_hcd; |
|---|
| 340 | + } |
|---|
| 341 | + if (priv->overcurrent) |
|---|
| 342 | + gpiod_set_consumer_name(priv->overcurrent, "OHCI overcurrent"); |
|---|
| 343 | + |
|---|
| 344 | + priv->usb_host_ck = clk_get(&pdev->dev, "usb_hhc_ck"); |
|---|
| 345 | + if (IS_ERR(priv->usb_host_ck)) { |
|---|
| 346 | + retval = PTR_ERR(priv->usb_host_ck); |
|---|
| 347 | + goto err_put_hcd; |
|---|
| 348 | + } |
|---|
| 349 | + |
|---|
| 350 | + if (!cpu_is_omap15xx()) |
|---|
| 351 | + priv->usb_dc_ck = clk_get(&pdev->dev, "usb_dc_ck"); |
|---|
| 352 | + else |
|---|
| 353 | + priv->usb_dc_ck = clk_get(&pdev->dev, "lb_ck"); |
|---|
| 354 | + |
|---|
| 355 | + if (IS_ERR(priv->usb_dc_ck)) { |
|---|
| 356 | + retval = PTR_ERR(priv->usb_dc_ck); |
|---|
| 357 | + goto err_put_host_ck; |
|---|
| 358 | + } |
|---|
| 344 | 359 | |
|---|
| 345 | 360 | if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) { |
|---|
| 346 | 361 | dev_dbg(&pdev->dev, "request_mem_region failed\n"); |
|---|
| 347 | 362 | retval = -EBUSY; |
|---|
| 348 | | - goto err1; |
|---|
| 363 | + goto err_put_dc_ck; |
|---|
| 349 | 364 | } |
|---|
| 350 | 365 | |
|---|
| 351 | 366 | hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len); |
|---|
| .. | .. |
|---|
| 370 | 385 | iounmap(hcd->regs); |
|---|
| 371 | 386 | err2: |
|---|
| 372 | 387 | release_mem_region(hcd->rsrc_start, hcd->rsrc_len); |
|---|
| 373 | | -err1: |
|---|
| 388 | +err_put_dc_ck: |
|---|
| 389 | + clk_put(priv->usb_dc_ck); |
|---|
| 390 | +err_put_host_ck: |
|---|
| 391 | + clk_put(priv->usb_host_ck); |
|---|
| 392 | +err_put_hcd: |
|---|
| 374 | 393 | usb_put_hcd(hcd); |
|---|
| 375 | | -err0: |
|---|
| 376 | | - clk_put(usb_dc_ck); |
|---|
| 377 | | - clk_put(usb_host_ck); |
|---|
| 378 | 394 | return retval; |
|---|
| 379 | 395 | } |
|---|
| 380 | 396 | |
|---|
| .. | .. |
|---|
| 393 | 409 | static int ohci_hcd_omap_remove(struct platform_device *pdev) |
|---|
| 394 | 410 | { |
|---|
| 395 | 411 | struct usb_hcd *hcd = platform_get_drvdata(pdev); |
|---|
| 412 | + struct ohci_omap_priv *priv = hcd_to_ohci_omap_priv(hcd); |
|---|
| 396 | 413 | |
|---|
| 397 | 414 | dev_dbg(hcd->self.controller, "stopping USB Controller\n"); |
|---|
| 398 | 415 | usb_remove_hcd(hcd); |
|---|
| 399 | | - omap_ohci_clock_power(0); |
|---|
| 416 | + omap_ohci_clock_power(priv, 0); |
|---|
| 400 | 417 | if (!IS_ERR_OR_NULL(hcd->usb_phy)) { |
|---|
| 401 | 418 | (void) otg_set_host(hcd->usb_phy->otg, 0); |
|---|
| 402 | 419 | usb_put_phy(hcd->usb_phy); |
|---|
| 403 | 420 | } |
|---|
| 404 | | - if (machine_is_omap_osk()) |
|---|
| 405 | | - gpio_free(9); |
|---|
| 406 | 421 | iounmap(hcd->regs); |
|---|
| 407 | 422 | release_mem_region(hcd->rsrc_start, hcd->rsrc_len); |
|---|
| 423 | + clk_put(priv->usb_dc_ck); |
|---|
| 424 | + clk_put(priv->usb_host_ck); |
|---|
| 408 | 425 | usb_put_hcd(hcd); |
|---|
| 409 | | - clk_put(usb_dc_ck); |
|---|
| 410 | | - clk_put(usb_host_ck); |
|---|
| 411 | 426 | return 0; |
|---|
| 412 | 427 | } |
|---|
| 413 | 428 | |
|---|
| .. | .. |
|---|
| 419 | 434 | { |
|---|
| 420 | 435 | struct usb_hcd *hcd = platform_get_drvdata(pdev); |
|---|
| 421 | 436 | struct ohci_hcd *ohci = hcd_to_ohci(hcd); |
|---|
| 437 | + struct ohci_omap_priv *priv = hcd_to_ohci_omap_priv(hcd); |
|---|
| 422 | 438 | bool do_wakeup = device_may_wakeup(&pdev->dev); |
|---|
| 423 | 439 | int ret; |
|---|
| 424 | 440 | |
|---|
| .. | .. |
|---|
| 430 | 446 | if (ret) |
|---|
| 431 | 447 | return ret; |
|---|
| 432 | 448 | |
|---|
| 433 | | - omap_ohci_clock_power(0); |
|---|
| 449 | + omap_ohci_clock_power(priv, 0); |
|---|
| 434 | 450 | return ret; |
|---|
| 435 | 451 | } |
|---|
| 436 | 452 | |
|---|
| .. | .. |
|---|
| 438 | 454 | { |
|---|
| 439 | 455 | struct usb_hcd *hcd = platform_get_drvdata(dev); |
|---|
| 440 | 456 | struct ohci_hcd *ohci = hcd_to_ohci(hcd); |
|---|
| 457 | + struct ohci_omap_priv *priv = hcd_to_ohci_omap_priv(hcd); |
|---|
| 441 | 458 | |
|---|
| 442 | 459 | if (time_before(jiffies, ohci->next_statechange)) |
|---|
| 443 | 460 | msleep(5); |
|---|
| 444 | 461 | ohci->next_statechange = jiffies; |
|---|
| 445 | 462 | |
|---|
| 446 | | - omap_ohci_clock_power(1); |
|---|
| 463 | + omap_ohci_clock_power(priv, 1); |
|---|
| 447 | 464 | ohci_resume(hcd, false); |
|---|
| 448 | 465 | return 0; |
|---|
| 449 | 466 | } |
|---|
| .. | .. |
|---|
| 470 | 487 | |
|---|
| 471 | 488 | static const struct ohci_driver_overrides omap_overrides __initconst = { |
|---|
| 472 | 489 | .product_desc = "OMAP OHCI", |
|---|
| 473 | | - .reset = ohci_omap_reset |
|---|
| 490 | + .reset = ohci_omap_reset, |
|---|
| 491 | + .extra_priv_size = sizeof(struct ohci_omap_priv), |
|---|
| 474 | 492 | }; |
|---|
| 475 | 493 | |
|---|
| 476 | 494 | static int __init ohci_omap_init(void) |
|---|