mdio-gpio.c 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. /*
  2. * GPIO based MDIO bitbang driver.
  3. * Supports OpenFirmware.
  4. *
  5. * Copyright (c) 2008 CSE Semaphore Belgium.
  6. * by Laurent Pinchart <laurentp@cse-semaphore.com>
  7. *
  8. * Copyright (C) 2008, Paulius Zaleckas <paulius.zaleckas@teltonika.lt>
  9. *
  10. * Based on earlier work by
  11. *
  12. * Copyright (c) 2003 Intracom S.A.
  13. * by Pantelis Antoniou <panto@intracom.gr>
  14. *
  15. * 2005 (c) MontaVista Software, Inc.
  16. * Vitaly Bordug <vbordug@ru.mvista.com>
  17. *
  18. * This file is licensed under the terms of the GNU General Public License
  19. * version 2. This program is licensed "as is" without any warranty of any
  20. * kind, whether express or implied.
  21. */
  22. #include <linux/module.h>
  23. #include <linux/slab.h>
  24. #include <linux/interrupt.h>
  25. #include <linux/platform_device.h>
  26. #include <linux/mdio-bitbang.h>
  27. #include <linux/mdio-gpio.h>
  28. #include <linux/gpio.h>
  29. #include <linux/gpio/consumer.h>
  30. #include <linux/of_gpio.h>
  31. #include <linux/of_mdio.h>
  32. struct mdio_gpio_info {
  33. struct mdiobb_ctrl ctrl;
  34. struct gpio_desc *mdc, *mdio, *mdo;
  35. };
  36. static int mdio_gpio_get_data(struct device *dev,
  37. struct mdio_gpio_info *bitbang)
  38. {
  39. bitbang->mdc = devm_gpiod_get_index(dev, NULL, MDIO_GPIO_MDC,
  40. GPIOD_OUT_LOW);
  41. if (IS_ERR(bitbang->mdc))
  42. return PTR_ERR(bitbang->mdc);
  43. bitbang->mdio = devm_gpiod_get_index(dev, NULL, MDIO_GPIO_MDIO,
  44. GPIOD_IN);
  45. if (IS_ERR(bitbang->mdio))
  46. return PTR_ERR(bitbang->mdio);
  47. bitbang->mdo = devm_gpiod_get_index_optional(dev, NULL, MDIO_GPIO_MDO,
  48. GPIOD_OUT_LOW);
  49. return PTR_ERR_OR_ZERO(bitbang->mdo);
  50. }
  51. static void mdio_dir(struct mdiobb_ctrl *ctrl, int dir)
  52. {
  53. struct mdio_gpio_info *bitbang =
  54. container_of(ctrl, struct mdio_gpio_info, ctrl);
  55. if (bitbang->mdo) {
  56. /* Separate output pin. Always set its value to high
  57. * when changing direction. If direction is input,
  58. * assume the pin serves as pull-up. If direction is
  59. * output, the default value is high.
  60. */
  61. gpiod_set_value(bitbang->mdo, 1);
  62. return;
  63. }
  64. if (dir)
  65. gpiod_direction_output(bitbang->mdio, 1);
  66. else
  67. gpiod_direction_input(bitbang->mdio);
  68. }
  69. static int mdio_get(struct mdiobb_ctrl *ctrl)
  70. {
  71. struct mdio_gpio_info *bitbang =
  72. container_of(ctrl, struct mdio_gpio_info, ctrl);
  73. return gpiod_get_value(bitbang->mdio);
  74. }
  75. static void mdio_set(struct mdiobb_ctrl *ctrl, int what)
  76. {
  77. struct mdio_gpio_info *bitbang =
  78. container_of(ctrl, struct mdio_gpio_info, ctrl);
  79. if (bitbang->mdo)
  80. gpiod_set_value(bitbang->mdo, what);
  81. else
  82. gpiod_set_value(bitbang->mdio, what);
  83. }
  84. static void mdc_set(struct mdiobb_ctrl *ctrl, int what)
  85. {
  86. struct mdio_gpio_info *bitbang =
  87. container_of(ctrl, struct mdio_gpio_info, ctrl);
  88. gpiod_set_value(bitbang->mdc, what);
  89. }
  90. static const struct mdiobb_ops mdio_gpio_ops = {
  91. .owner = THIS_MODULE,
  92. .set_mdc = mdc_set,
  93. .set_mdio_dir = mdio_dir,
  94. .set_mdio_data = mdio_set,
  95. .get_mdio_data = mdio_get,
  96. };
  97. static struct mii_bus *mdio_gpio_bus_init(struct device *dev,
  98. struct mdio_gpio_info *bitbang,
  99. int bus_id)
  100. {
  101. struct mii_bus *new_bus;
  102. bitbang->ctrl.ops = &mdio_gpio_ops;
  103. new_bus = alloc_mdio_bitbang(&bitbang->ctrl);
  104. if (!new_bus)
  105. return NULL;
  106. new_bus->name = "GPIO Bitbanged MDIO";
  107. new_bus->parent = dev;
  108. if (bus_id != -1)
  109. snprintf(new_bus->id, MII_BUS_ID_SIZE, "gpio-%x", bus_id);
  110. else
  111. strncpy(new_bus->id, "gpio", MII_BUS_ID_SIZE);
  112. dev_set_drvdata(dev, new_bus);
  113. return new_bus;
  114. }
  115. static void mdio_gpio_bus_deinit(struct device *dev)
  116. {
  117. struct mii_bus *bus = dev_get_drvdata(dev);
  118. free_mdio_bitbang(bus);
  119. }
  120. static void mdio_gpio_bus_destroy(struct device *dev)
  121. {
  122. struct mii_bus *bus = dev_get_drvdata(dev);
  123. mdiobus_unregister(bus);
  124. mdio_gpio_bus_deinit(dev);
  125. }
  126. static int mdio_gpio_probe(struct platform_device *pdev)
  127. {
  128. struct mdio_gpio_info *bitbang;
  129. struct mii_bus *new_bus;
  130. int ret, bus_id;
  131. bitbang = devm_kzalloc(&pdev->dev, sizeof(*bitbang), GFP_KERNEL);
  132. if (!bitbang)
  133. return -ENOMEM;
  134. ret = mdio_gpio_get_data(&pdev->dev, bitbang);
  135. if (ret)
  136. return ret;
  137. if (pdev->dev.of_node) {
  138. bus_id = of_alias_get_id(pdev->dev.of_node, "mdio-gpio");
  139. if (bus_id < 0) {
  140. dev_warn(&pdev->dev, "failed to get alias id\n");
  141. bus_id = 0;
  142. }
  143. } else {
  144. bus_id = pdev->id;
  145. }
  146. new_bus = mdio_gpio_bus_init(&pdev->dev, bitbang, bus_id);
  147. if (!new_bus)
  148. return -ENODEV;
  149. if (pdev->dev.of_node)
  150. ret = of_mdiobus_register(new_bus, pdev->dev.of_node);
  151. else
  152. ret = mdiobus_register(new_bus);
  153. if (ret)
  154. mdio_gpio_bus_deinit(&pdev->dev);
  155. return ret;
  156. }
  157. static int mdio_gpio_remove(struct platform_device *pdev)
  158. {
  159. mdio_gpio_bus_destroy(&pdev->dev);
  160. return 0;
  161. }
  162. static const struct of_device_id mdio_gpio_of_match[] = {
  163. { .compatible = "virtual,mdio-gpio", },
  164. { /* sentinel */ }
  165. };
  166. MODULE_DEVICE_TABLE(of, mdio_gpio_of_match);
  167. static struct platform_driver mdio_gpio_driver = {
  168. .probe = mdio_gpio_probe,
  169. .remove = mdio_gpio_remove,
  170. .driver = {
  171. .name = "mdio-gpio",
  172. .of_match_table = mdio_gpio_of_match,
  173. },
  174. };
  175. module_platform_driver(mdio_gpio_driver);
  176. MODULE_ALIAS("platform:mdio-gpio");
  177. MODULE_AUTHOR("Laurent Pinchart, Paulius Zaleckas");
  178. MODULE_LICENSE("GPL");
  179. MODULE_DESCRIPTION("Generic driver for MDIO bus emulation using GPIO");