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