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