mdio-mvusb.c 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. // SPDX-License-Identifier: GPL-2.0
  2. #include <linux/kernel.h>
  3. #include <linux/module.h>
  4. #include <linux/of_mdio.h>
  5. #include <linux/phy.h>
  6. #include <linux/usb.h>
  7. #define USB_MARVELL_VID 0x1286
  8. static const struct usb_device_id mvusb_mdio_table[] = {
  9. { USB_DEVICE(USB_MARVELL_VID, 0x1fa4) },
  10. {}
  11. };
  12. MODULE_DEVICE_TABLE(usb, mvusb_mdio_table);
  13. enum {
  14. MVUSB_CMD_PREAMBLE0,
  15. MVUSB_CMD_PREAMBLE1,
  16. MVUSB_CMD_ADDR,
  17. MVUSB_CMD_VAL,
  18. };
  19. struct mvusb_mdio {
  20. struct usb_device *udev;
  21. struct mii_bus *mdio;
  22. __le16 buf[4];
  23. };
  24. static int mvusb_mdio_read(struct mii_bus *mdio, int dev, int reg)
  25. {
  26. struct mvusb_mdio *mvusb = mdio->priv;
  27. int err, alen;
  28. mvusb->buf[MVUSB_CMD_ADDR] = cpu_to_le16(0xa400 | (dev << 5) | reg);
  29. err = usb_bulk_msg(mvusb->udev, usb_sndbulkpipe(mvusb->udev, 2),
  30. mvusb->buf, 6, &alen, 100);
  31. if (err)
  32. return err;
  33. err = usb_bulk_msg(mvusb->udev, usb_rcvbulkpipe(mvusb->udev, 6),
  34. &mvusb->buf[MVUSB_CMD_VAL], 2, &alen, 100);
  35. if (err)
  36. return err;
  37. return le16_to_cpu(mvusb->buf[MVUSB_CMD_VAL]);
  38. }
  39. static int mvusb_mdio_write(struct mii_bus *mdio, int dev, int reg, u16 val)
  40. {
  41. struct mvusb_mdio *mvusb = mdio->priv;
  42. int alen;
  43. mvusb->buf[MVUSB_CMD_ADDR] = cpu_to_le16(0x8000 | (dev << 5) | reg);
  44. mvusb->buf[MVUSB_CMD_VAL] = cpu_to_le16(val);
  45. return usb_bulk_msg(mvusb->udev, usb_sndbulkpipe(mvusb->udev, 2),
  46. mvusb->buf, 8, &alen, 100);
  47. }
  48. static int mvusb_mdio_probe(struct usb_interface *interface,
  49. const struct usb_device_id *id)
  50. {
  51. struct device *dev = &interface->dev;
  52. struct mvusb_mdio *mvusb;
  53. struct mii_bus *mdio;
  54. int ret;
  55. mdio = devm_mdiobus_alloc_size(dev, sizeof(*mvusb));
  56. if (!mdio)
  57. return -ENOMEM;
  58. mvusb = mdio->priv;
  59. mvusb->mdio = mdio;
  60. mvusb->udev = usb_get_dev(interface_to_usbdev(interface));
  61. /* Reversed from USB PCAPs, no idea what these mean. */
  62. mvusb->buf[MVUSB_CMD_PREAMBLE0] = cpu_to_le16(0xe800);
  63. mvusb->buf[MVUSB_CMD_PREAMBLE1] = cpu_to_le16(0x0001);
  64. snprintf(mdio->id, MII_BUS_ID_SIZE, "mvusb-%s", dev_name(dev));
  65. mdio->name = mdio->id;
  66. mdio->parent = dev;
  67. mdio->read = mvusb_mdio_read;
  68. mdio->write = mvusb_mdio_write;
  69. usb_set_intfdata(interface, mvusb);
  70. ret = of_mdiobus_register(mdio, dev->of_node);
  71. if (ret)
  72. goto put_dev;
  73. return 0;
  74. put_dev:
  75. usb_put_dev(mvusb->udev);
  76. return ret;
  77. }
  78. static void mvusb_mdio_disconnect(struct usb_interface *interface)
  79. {
  80. struct mvusb_mdio *mvusb = usb_get_intfdata(interface);
  81. struct usb_device *udev = mvusb->udev;
  82. mdiobus_unregister(mvusb->mdio);
  83. usb_set_intfdata(interface, NULL);
  84. usb_put_dev(udev);
  85. }
  86. static struct usb_driver mvusb_mdio_driver = {
  87. .name = "mvusb_mdio",
  88. .id_table = mvusb_mdio_table,
  89. .probe = mvusb_mdio_probe,
  90. .disconnect = mvusb_mdio_disconnect,
  91. };
  92. module_usb_driver(mvusb_mdio_driver);
  93. MODULE_AUTHOR("Tobias Waldekranz <tobias@waldekranz.com>");
  94. MODULE_DESCRIPTION("Marvell USB MDIO Adapter");
  95. MODULE_LICENSE("GPL");