ohci-jz4740.c 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
  1. /*
  2. * Copyright (C) 2010, Lars-Peter Clausen <lars@metafoo.de>
  3. *
  4. * This program is free software; you can redistribute it and/or modify it
  5. * under the terms of the GNU General Public License as published by the
  6. * Free Software Foundation; either version 2 of the License, or (at your
  7. * option) any later version.
  8. *
  9. * You should have received a copy of the GNU General Public License along
  10. * with this program; if not, write to the Free Software Foundation, Inc.,
  11. * 675 Mass Ave, Cambridge, MA 02139, USA.
  12. *
  13. */
  14. #include <linux/platform_device.h>
  15. #include <linux/clk.h>
  16. #include <linux/regulator/consumer.h>
  17. struct jz4740_ohci_hcd {
  18. struct ohci_hcd ohci_hcd;
  19. struct regulator *vbus;
  20. bool vbus_enabled;
  21. struct clk *clk;
  22. };
  23. static inline struct jz4740_ohci_hcd *hcd_to_jz4740_hcd(struct usb_hcd *hcd)
  24. {
  25. return (struct jz4740_ohci_hcd *)(hcd->hcd_priv);
  26. }
  27. static inline struct usb_hcd *jz4740_hcd_to_hcd(struct jz4740_ohci_hcd *jz4740_ohci)
  28. {
  29. return container_of((void *)jz4740_ohci, struct usb_hcd, hcd_priv);
  30. }
  31. static int ohci_jz4740_start(struct usb_hcd *hcd)
  32. {
  33. struct ohci_hcd *ohci = hcd_to_ohci(hcd);
  34. int ret;
  35. ret = ohci_init(ohci);
  36. if (ret < 0)
  37. return ret;
  38. ohci->num_ports = 1;
  39. ret = ohci_run(ohci);
  40. if (ret < 0) {
  41. dev_err(hcd->self.controller, "Can not start %s",
  42. hcd->self.bus_name);
  43. ohci_stop(hcd);
  44. return ret;
  45. }
  46. return 0;
  47. }
  48. static int ohci_jz4740_set_vbus_power(struct jz4740_ohci_hcd *jz4740_ohci,
  49. bool enabled)
  50. {
  51. int ret = 0;
  52. if (!jz4740_ohci->vbus)
  53. return 0;
  54. if (enabled && !jz4740_ohci->vbus_enabled) {
  55. ret = regulator_enable(jz4740_ohci->vbus);
  56. if (ret)
  57. dev_err(jz4740_hcd_to_hcd(jz4740_ohci)->self.controller,
  58. "Could not power vbus\n");
  59. } else if (!enabled && jz4740_ohci->vbus_enabled) {
  60. ret = regulator_disable(jz4740_ohci->vbus);
  61. }
  62. if (ret == 0)
  63. jz4740_ohci->vbus_enabled = enabled;
  64. return ret;
  65. }
  66. static int ohci_jz4740_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
  67. u16 wIndex, char *buf, u16 wLength)
  68. {
  69. struct jz4740_ohci_hcd *jz4740_ohci = hcd_to_jz4740_hcd(hcd);
  70. int ret = 0;
  71. switch (typeReq) {
  72. case SetPortFeature:
  73. if (wValue == USB_PORT_FEAT_POWER)
  74. ret = ohci_jz4740_set_vbus_power(jz4740_ohci, true);
  75. break;
  76. case ClearPortFeature:
  77. if (wValue == USB_PORT_FEAT_POWER)
  78. ret = ohci_jz4740_set_vbus_power(jz4740_ohci, false);
  79. break;
  80. }
  81. if (ret)
  82. return ret;
  83. return ohci_hub_control(hcd, typeReq, wValue, wIndex, buf, wLength);
  84. }
  85. static const struct hc_driver ohci_jz4740_hc_driver = {
  86. .description = hcd_name,
  87. .product_desc = "JZ4740 OHCI",
  88. .hcd_priv_size = sizeof(struct jz4740_ohci_hcd),
  89. /*
  90. * generic hardware linkage
  91. */
  92. .irq = ohci_irq,
  93. .flags = HCD_USB11 | HCD_MEMORY,
  94. /*
  95. * basic lifecycle operations
  96. */
  97. .start = ohci_jz4740_start,
  98. .stop = ohci_stop,
  99. .shutdown = ohci_shutdown,
  100. /*
  101. * managing i/o requests and associated device resources
  102. */
  103. .urb_enqueue = ohci_urb_enqueue,
  104. .urb_dequeue = ohci_urb_dequeue,
  105. .endpoint_disable = ohci_endpoint_disable,
  106. /*
  107. * scheduling support
  108. */
  109. .get_frame_number = ohci_get_frame,
  110. /*
  111. * root hub support
  112. */
  113. .hub_status_data = ohci_hub_status_data,
  114. .hub_control = ohci_jz4740_hub_control,
  115. #ifdef CONFIG_PM
  116. .bus_suspend = ohci_bus_suspend,
  117. .bus_resume = ohci_bus_resume,
  118. #endif
  119. .start_port_reset = ohci_start_port_reset,
  120. };
  121. static int jz4740_ohci_probe(struct platform_device *pdev)
  122. {
  123. int ret;
  124. struct usb_hcd *hcd;
  125. struct jz4740_ohci_hcd *jz4740_ohci;
  126. struct resource *res;
  127. int irq;
  128. irq = platform_get_irq(pdev, 0);
  129. if (irq < 0) {
  130. dev_err(&pdev->dev, "Failed to get platform irq\n");
  131. return irq;
  132. }
  133. hcd = usb_create_hcd(&ohci_jz4740_hc_driver, &pdev->dev, "jz4740");
  134. if (!hcd) {
  135. dev_err(&pdev->dev, "Failed to create hcd.\n");
  136. return -ENOMEM;
  137. }
  138. jz4740_ohci = hcd_to_jz4740_hcd(hcd);
  139. res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
  140. hcd->regs = devm_ioremap_resource(&pdev->dev, res);
  141. if (IS_ERR(hcd->regs)) {
  142. ret = PTR_ERR(hcd->regs);
  143. goto err_free;
  144. }
  145. hcd->rsrc_start = res->start;
  146. hcd->rsrc_len = resource_size(res);
  147. jz4740_ohci->clk = devm_clk_get(&pdev->dev, "uhc");
  148. if (IS_ERR(jz4740_ohci->clk)) {
  149. ret = PTR_ERR(jz4740_ohci->clk);
  150. dev_err(&pdev->dev, "Failed to get clock: %d\n", ret);
  151. goto err_free;
  152. }
  153. jz4740_ohci->vbus = devm_regulator_get(&pdev->dev, "vbus");
  154. if (IS_ERR(jz4740_ohci->vbus))
  155. jz4740_ohci->vbus = NULL;
  156. clk_set_rate(jz4740_ohci->clk, 48000000);
  157. clk_enable(jz4740_ohci->clk);
  158. if (jz4740_ohci->vbus)
  159. ohci_jz4740_set_vbus_power(jz4740_ohci, true);
  160. platform_set_drvdata(pdev, hcd);
  161. ohci_hcd_init(hcd_to_ohci(hcd));
  162. ret = usb_add_hcd(hcd, irq, 0);
  163. if (ret) {
  164. dev_err(&pdev->dev, "Failed to add hcd: %d\n", ret);
  165. goto err_disable;
  166. }
  167. device_wakeup_enable(hcd->self.controller);
  168. return 0;
  169. err_disable:
  170. if (jz4740_ohci->vbus)
  171. regulator_disable(jz4740_ohci->vbus);
  172. clk_disable(jz4740_ohci->clk);
  173. err_free:
  174. usb_put_hcd(hcd);
  175. return ret;
  176. }
  177. static int jz4740_ohci_remove(struct platform_device *pdev)
  178. {
  179. struct usb_hcd *hcd = platform_get_drvdata(pdev);
  180. struct jz4740_ohci_hcd *jz4740_ohci = hcd_to_jz4740_hcd(hcd);
  181. usb_remove_hcd(hcd);
  182. if (jz4740_ohci->vbus)
  183. regulator_disable(jz4740_ohci->vbus);
  184. clk_disable(jz4740_ohci->clk);
  185. usb_put_hcd(hcd);
  186. return 0;
  187. }
  188. static struct platform_driver ohci_hcd_jz4740_driver = {
  189. .probe = jz4740_ohci_probe,
  190. .remove = jz4740_ohci_remove,
  191. .driver = {
  192. .name = "jz4740-ohci",
  193. },
  194. };
  195. MODULE_ALIAS("platform:jz4740-ohci");