0001
0002
0003 #include <linux/kernel.h>
0004 #include <linux/module.h>
0005 #include <linux/of_mdio.h>
0006 #include <linux/phy.h>
0007 #include <linux/usb.h>
0008
0009 #define USB_MARVELL_VID 0x1286
0010
0011 static const struct usb_device_id mvusb_mdio_table[] = {
0012 { USB_DEVICE(USB_MARVELL_VID, 0x1fa4) },
0013
0014 {}
0015 };
0016 MODULE_DEVICE_TABLE(usb, mvusb_mdio_table);
0017
0018 enum {
0019 MVUSB_CMD_PREAMBLE0,
0020 MVUSB_CMD_PREAMBLE1,
0021 MVUSB_CMD_ADDR,
0022 MVUSB_CMD_VAL,
0023 };
0024
0025 struct mvusb_mdio {
0026 struct usb_device *udev;
0027 struct mii_bus *mdio;
0028
0029 __le16 buf[4];
0030 };
0031
0032 static int mvusb_mdio_read(struct mii_bus *mdio, int dev, int reg)
0033 {
0034 struct mvusb_mdio *mvusb = mdio->priv;
0035 int err, alen;
0036
0037 if (dev & MII_ADDR_C45)
0038 return -EOPNOTSUPP;
0039
0040 mvusb->buf[MVUSB_CMD_ADDR] = cpu_to_le16(0xa400 | (dev << 5) | reg);
0041
0042 err = usb_bulk_msg(mvusb->udev, usb_sndbulkpipe(mvusb->udev, 2),
0043 mvusb->buf, 6, &alen, 100);
0044 if (err)
0045 return err;
0046
0047 err = usb_bulk_msg(mvusb->udev, usb_rcvbulkpipe(mvusb->udev, 6),
0048 &mvusb->buf[MVUSB_CMD_VAL], 2, &alen, 100);
0049 if (err)
0050 return err;
0051
0052 return le16_to_cpu(mvusb->buf[MVUSB_CMD_VAL]);
0053 }
0054
0055 static int mvusb_mdio_write(struct mii_bus *mdio, int dev, int reg, u16 val)
0056 {
0057 struct mvusb_mdio *mvusb = mdio->priv;
0058 int alen;
0059
0060 if (dev & MII_ADDR_C45)
0061 return -EOPNOTSUPP;
0062
0063 mvusb->buf[MVUSB_CMD_ADDR] = cpu_to_le16(0x8000 | (dev << 5) | reg);
0064 mvusb->buf[MVUSB_CMD_VAL] = cpu_to_le16(val);
0065
0066 return usb_bulk_msg(mvusb->udev, usb_sndbulkpipe(mvusb->udev, 2),
0067 mvusb->buf, 8, &alen, 100);
0068 }
0069
0070 static int mvusb_mdio_probe(struct usb_interface *interface,
0071 const struct usb_device_id *id)
0072 {
0073 struct device *dev = &interface->dev;
0074 struct mvusb_mdio *mvusb;
0075 struct mii_bus *mdio;
0076
0077 mdio = devm_mdiobus_alloc_size(dev, sizeof(*mvusb));
0078 if (!mdio)
0079 return -ENOMEM;
0080
0081 mvusb = mdio->priv;
0082 mvusb->mdio = mdio;
0083 mvusb->udev = usb_get_dev(interface_to_usbdev(interface));
0084
0085
0086 mvusb->buf[MVUSB_CMD_PREAMBLE0] = cpu_to_le16(0xe800);
0087 mvusb->buf[MVUSB_CMD_PREAMBLE1] = cpu_to_le16(0x0001);
0088
0089 snprintf(mdio->id, MII_BUS_ID_SIZE, "mvusb-%s", dev_name(dev));
0090 mdio->name = mdio->id;
0091 mdio->parent = dev;
0092 mdio->read = mvusb_mdio_read;
0093 mdio->write = mvusb_mdio_write;
0094
0095 usb_set_intfdata(interface, mvusb);
0096 return of_mdiobus_register(mdio, dev->of_node);
0097 }
0098
0099 static void mvusb_mdio_disconnect(struct usb_interface *interface)
0100 {
0101 struct mvusb_mdio *mvusb = usb_get_intfdata(interface);
0102 struct usb_device *udev = mvusb->udev;
0103
0104 mdiobus_unregister(mvusb->mdio);
0105 usb_set_intfdata(interface, NULL);
0106 usb_put_dev(udev);
0107 }
0108
0109 static struct usb_driver mvusb_mdio_driver = {
0110 .name = "mvusb_mdio",
0111 .id_table = mvusb_mdio_table,
0112 .probe = mvusb_mdio_probe,
0113 .disconnect = mvusb_mdio_disconnect,
0114 };
0115
0116 module_usb_driver(mvusb_mdio_driver);
0117
0118 MODULE_AUTHOR("Tobias Waldekranz <tobias@waldekranz.com>");
0119 MODULE_DESCRIPTION("Marvell USB MDIO Adapter");
0120 MODULE_LICENSE("GPL");