linux/drivers/net/phy/mdio-mvusb.c
<<
>>
Prefs
   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
  77        mdio = devm_mdiobus_alloc_size(dev, sizeof(*mvusb));
  78        if (!mdio)
  79                return -ENOMEM;
  80
  81        mvusb = mdio->priv;
  82        mvusb->mdio = mdio;
  83        mvusb->udev = usb_get_dev(interface_to_usbdev(interface));
  84
  85        /* Reversed from USB PCAPs, no idea what these mean. */
  86        mvusb->buf[MVUSB_CMD_PREAMBLE0] = cpu_to_le16(0xe800);
  87        mvusb->buf[MVUSB_CMD_PREAMBLE1] = cpu_to_le16(0x0001);
  88
  89        snprintf(mdio->id, MII_BUS_ID_SIZE, "mvusb-%s", dev_name(dev));
  90        mdio->name = mdio->id;
  91        mdio->parent = dev;
  92        mdio->read = mvusb_mdio_read;
  93        mdio->write = mvusb_mdio_write;
  94
  95        usb_set_intfdata(interface, mvusb);
  96        return of_mdiobus_register(mdio, dev->of_node);
  97}
  98
  99static void mvusb_mdio_disconnect(struct usb_interface *interface)
 100{
 101        struct mvusb_mdio *mvusb = usb_get_intfdata(interface);
 102        struct usb_device *udev = mvusb->udev;
 103
 104        mdiobus_unregister(mvusb->mdio);
 105        usb_set_intfdata(interface, NULL);
 106        usb_put_dev(udev);
 107}
 108
 109static struct usb_driver mvusb_mdio_driver = {
 110        .name       = "mvusb_mdio",
 111        .id_table   = mvusb_mdio_table,
 112        .probe      = mvusb_mdio_probe,
 113        .disconnect = mvusb_mdio_disconnect,
 114};
 115
 116module_usb_driver(mvusb_mdio_driver);
 117
 118MODULE_AUTHOR("Tobias Waldekranz <tobias@waldekranz.com>");
 119MODULE_DESCRIPTION("Marvell USB MDIO Adapter");
 120MODULE_LICENSE("GPL");
 121