Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  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");