• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
11 static const struct usb_device_id mvusb_mdio_table[] = {
12 	{ USB_DEVICE(USB_MARVELL_VID, 0x1fa4) },
13 
14 	{}
15 };
16 MODULE_DEVICE_TABLE(usb, mvusb_mdio_table);
17 
18 enum {
19 	MVUSB_CMD_PREAMBLE0,
20 	MVUSB_CMD_PREAMBLE1,
21 	MVUSB_CMD_ADDR,
22 	MVUSB_CMD_VAL,
23 };
24 
25 struct mvusb_mdio {
26 	struct usb_device *udev;
27 	struct mii_bus *mdio;
28 
29 	__le16 buf[4];
30 };
31 
mvusb_mdio_read(struct mii_bus * mdio,int dev,int reg)32 static 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 
mvusb_mdio_write(struct mii_bus * mdio,int dev,int reg,u16 val)55 static 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 
mvusb_mdio_probe(struct usb_interface * interface,const struct usb_device_id * id)70 static 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 	int ret;
77 
78 	mdio = devm_mdiobus_alloc_size(dev, sizeof(*mvusb));
79 	if (!mdio)
80 		return -ENOMEM;
81 
82 	mvusb = mdio->priv;
83 	mvusb->mdio = mdio;
84 	mvusb->udev = usb_get_dev(interface_to_usbdev(interface));
85 
86 	/* Reversed from USB PCAPs, no idea what these mean. */
87 	mvusb->buf[MVUSB_CMD_PREAMBLE0] = cpu_to_le16(0xe800);
88 	mvusb->buf[MVUSB_CMD_PREAMBLE1] = cpu_to_le16(0x0001);
89 
90 	snprintf(mdio->id, MII_BUS_ID_SIZE, "mvusb-%s", dev_name(dev));
91 	mdio->name = mdio->id;
92 	mdio->parent = dev;
93 	mdio->read = mvusb_mdio_read;
94 	mdio->write = mvusb_mdio_write;
95 
96 	usb_set_intfdata(interface, mvusb);
97 	ret = of_mdiobus_register(mdio, dev->of_node);
98 	if (ret)
99 		goto put_dev;
100 
101 	return 0;
102 
103 put_dev:
104 	usb_put_dev(mvusb->udev);
105 	return ret;
106 }
107 
mvusb_mdio_disconnect(struct usb_interface * interface)108 static void mvusb_mdio_disconnect(struct usb_interface *interface)
109 {
110 	struct mvusb_mdio *mvusb = usb_get_intfdata(interface);
111 	struct usb_device *udev = mvusb->udev;
112 
113 	mdiobus_unregister(mvusb->mdio);
114 	usb_set_intfdata(interface, NULL);
115 	usb_put_dev(udev);
116 }
117 
118 static struct usb_driver mvusb_mdio_driver = {
119 	.name       = "mvusb_mdio",
120 	.id_table   = mvusb_mdio_table,
121 	.probe      = mvusb_mdio_probe,
122 	.disconnect = mvusb_mdio_disconnect,
123 };
124 
125 module_usb_driver(mvusb_mdio_driver);
126 
127 MODULE_AUTHOR("Tobias Waldekranz <tobias@waldekranz.com>");
128 MODULE_DESCRIPTION("Marvell USB MDIO Adapter");
129 MODULE_LICENSE("GPL");
130