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 mvusb->buf[MVUSB_CMD_ADDR] = cpu_to_le16(0xa400 | (dev << 5) | reg);
38
39 err = usb_bulk_msg(usb_dev: mvusb->udev, usb_sndbulkpipe(mvusb->udev, 2),
40 data: mvusb->buf, len: 6, actual_length: &alen, timeout: 100);
41 if (err)
42 return err;
43
44 err = usb_bulk_msg(usb_dev: mvusb->udev, usb_rcvbulkpipe(mvusb->udev, 6),
45 data: &mvusb->buf[MVUSB_CMD_VAL], len: 2, actual_length: &alen, timeout: 100);
46 if (err)
47 return err;
48
49 return le16_to_cpu(mvusb->buf[MVUSB_CMD_VAL]);
50}
51
52static int mvusb_mdio_write(struct mii_bus *mdio, int dev, int reg, u16 val)
53{
54 struct mvusb_mdio *mvusb = mdio->priv;
55 int alen;
56
57 mvusb->buf[MVUSB_CMD_ADDR] = cpu_to_le16(0x8000 | (dev << 5) | reg);
58 mvusb->buf[MVUSB_CMD_VAL] = cpu_to_le16(val);
59
60 return usb_bulk_msg(usb_dev: mvusb->udev, usb_sndbulkpipe(mvusb->udev, 2),
61 data: mvusb->buf, len: 8, actual_length: &alen, timeout: 100);
62}
63
64static int mvusb_mdio_probe(struct usb_interface *interface,
65 const struct usb_device_id *id)
66{
67 struct device *dev = &interface->dev;
68 struct mvusb_mdio *mvusb;
69 struct mii_bus *mdio;
70 int ret;
71
72 mdio = devm_mdiobus_alloc_size(dev, sizeof_priv: sizeof(*mvusb));
73 if (!mdio)
74 return -ENOMEM;
75
76 mvusb = mdio->priv;
77 mvusb->mdio = mdio;
78 mvusb->udev = usb_get_dev(interface_to_usbdev(interface));
79
80 /* Reversed from USB PCAPs, no idea what these mean. */
81 mvusb->buf[MVUSB_CMD_PREAMBLE0] = cpu_to_le16(0xe800);
82 mvusb->buf[MVUSB_CMD_PREAMBLE1] = cpu_to_le16(0x0001);
83
84 snprintf(buf: mdio->id, MII_BUS_ID_SIZE, fmt: "mvusb-%s", dev_name(dev));
85 mdio->name = mdio->id;
86 mdio->parent = dev;
87 mdio->read = mvusb_mdio_read;
88 mdio->write = mvusb_mdio_write;
89
90 usb_set_intfdata(intf: interface, data: mvusb);
91 ret = of_mdiobus_register(mdio, np: dev->of_node);
92 if (ret)
93 goto put_dev;
94
95 return 0;
96
97put_dev:
98 usb_put_dev(dev: mvusb->udev);
99 return ret;
100}
101
102static void mvusb_mdio_disconnect(struct usb_interface *interface)
103{
104 struct mvusb_mdio *mvusb = usb_get_intfdata(intf: interface);
105 struct usb_device *udev = mvusb->udev;
106
107 mdiobus_unregister(bus: mvusb->mdio);
108 usb_set_intfdata(intf: interface, NULL);
109 usb_put_dev(dev: udev);
110}
111
112static struct usb_driver mvusb_mdio_driver = {
113 .name = "mvusb_mdio",
114 .id_table = mvusb_mdio_table,
115 .probe = mvusb_mdio_probe,
116 .disconnect = mvusb_mdio_disconnect,
117};
118
119module_usb_driver(mvusb_mdio_driver);
120
121MODULE_AUTHOR("Tobias Waldekranz <tobias@waldekranz.com>");
122MODULE_DESCRIPTION("Marvell USB MDIO Adapter");
123MODULE_LICENSE("GPL");
124

source code of linux/drivers/net/mdio/mdio-mvusb.c