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