Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
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     /* Reversed from USB PCAPs, no idea what these mean. */
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");