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	mvusb->buf[MVUSB_CMD_ADDR] = cpu_to_le16(0xa400 | (dev << 5) | reg);
 38
 39	err = usb_bulk_msg(mvusb->udev, usb_sndbulkpipe(mvusb->udev, 2),
 40			   mvusb->buf, 6, &alen, 100);
 41	if (err)
 42		return err;
 43
 44	err = usb_bulk_msg(mvusb->udev, usb_rcvbulkpipe(mvusb->udev, 6),
 45			   &mvusb->buf[MVUSB_CMD_VAL], 2, &alen, 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(mvusb->udev, usb_sndbulkpipe(mvusb->udev, 2),
 61			    mvusb->buf, 8, &alen, 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(*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(mdio->id, MII_BUS_ID_SIZE, "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(interface, mvusb);
 91	ret = of_mdiobus_register(mdio, dev->of_node);
 92	if (ret)
 93		goto put_dev;
 94
 95	return 0;
 96
 97put_dev:
 98	usb_put_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(interface);
105	struct usb_device *udev = mvusb->udev;
106
107	mdiobus_unregister(mvusb->mdio);
108	usb_set_intfdata(interface, NULL);
109	usb_put_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");