@@ -10100,6 +10100,7 @@ MARVELL SMI2USB MDIO CONTROLLER DRIVER
M: Tobias Waldekranz <tobias@waldekranz.com>
L: netdev@vger.kernel.org
S: Maintained
+F: drivers/net/phy/mdio-smi2usb.c
F: Documentation/devicetree/bindings/net/marvell,smi2usb.yaml
MARVELL SOC MMC/SD/SDIO CONTROLLER DRIVER
@@ -189,6 +189,13 @@ config MDIO_OCTEON
buses. It is required by the Octeon and ThunderX ethernet device
drivers on some systems.
+config MDIO_SMI2USB
+ tristate "Marvell SMI2USB"
+ depends on OF_MDIO && USB
+ help
+ A USB to MDIO converter present on development boards for
+ Marvell's Link Street family of Ethernet switches.
+
config MDIO_SUN4I
tristate "Allwinner sun4i MDIO interface support"
depends on ARCH_SUNXI || COMPILE_TEST
@@ -41,6 +41,7 @@ obj-$(CONFIG_MDIO_IPQ8064) += mdio-ipq8064.o
obj-$(CONFIG_MDIO_MOXART) += mdio-moxart.o
obj-$(CONFIG_MDIO_MSCC_MIIM) += mdio-mscc-miim.o
obj-$(CONFIG_MDIO_OCTEON) += mdio-octeon.o
+obj-$(CONFIG_MDIO_SMI2USB) += mdio-smi2usb.o
obj-$(CONFIG_MDIO_SUN4I) += mdio-sun4i.o
obj-$(CONFIG_MDIO_THUNDER) += mdio-thunder.o
obj-$(CONFIG_MDIO_XGENE) += mdio-xgene.o
new file mode 100644
@@ -0,0 +1,137 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_mdio.h>
+#include <linux/phy.h>
+#include <linux/usb.h>
+
+#define USB_MARVELL_VID 0x1286
+
+static const struct usb_device_id smi2usb_table[] = {
+ { USB_DEVICE(USB_MARVELL_VID, 0x1fa4) },
+
+ {}
+};
+MODULE_DEVICE_TABLE(usb, smi2usb_table);
+
+enum {
+ SMI_CMD_PREAMBLE0,
+ SMI_CMD_PREAMBLE1,
+ SMI_CMD_ADDR,
+ SMI_CMD_VAL,
+};
+
+struct smi2usb {
+ struct usb_device *udev;
+ struct usb_interface *intf;
+
+ struct mii_bus *mdio;
+
+ __le16 buf[4];
+};
+
+static int smi2usb_mdio_read(struct mii_bus *mdio, int dev, int reg)
+{
+ struct smi2usb *smi = mdio->priv;
+ int err, alen;
+
+ if (dev & MII_ADDR_C45)
+ return -EOPNOTSUPP;
+
+ smi->buf[SMI_CMD_ADDR] = cpu_to_le16(0xa400 | (dev << 5) | reg);
+
+ err = usb_bulk_msg(smi->udev, usb_sndbulkpipe(smi->udev, 2),
+ smi->buf, 6, &alen, 100);
+ if (err)
+ return err;
+
+ err = usb_bulk_msg(smi->udev, usb_rcvbulkpipe(smi->udev, 6),
+ &smi->buf[SMI_CMD_VAL], 2, &alen, 100);
+ if (err)
+ return err;
+
+ return le16_to_cpu(smi->buf[SMI_CMD_VAL]);
+}
+
+static int smi2usb_mdio_write(struct mii_bus *mdio, int dev, int reg, u16 val)
+{
+ struct smi2usb *smi = mdio->priv;
+ int alen;
+
+ if (dev & MII_ADDR_C45)
+ return -EOPNOTSUPP;
+
+ smi->buf[SMI_CMD_ADDR] = cpu_to_le16(0x8000 | (dev << 5) | reg);
+ smi->buf[SMI_CMD_VAL] = cpu_to_le16(val);
+
+ return usb_bulk_msg(smi->udev, usb_sndbulkpipe(smi->udev, 2),
+ smi->buf, 8, &alen, 100);
+}
+
+static int smi2usb_probe(struct usb_interface *interface,
+ const struct usb_device_id *id)
+{
+ struct device *dev = &interface->dev;
+ struct mii_bus *mdio;
+ struct smi2usb *smi;
+ int err = -ENOMEM;
+
+ mdio = devm_mdiobus_alloc_size(dev, sizeof(*smi));
+ if (!mdio)
+ goto err;
+
+ smi = mdio->priv;
+ smi->mdio = mdio;
+ smi->udev = usb_get_dev(interface_to_usbdev(interface));
+ smi->intf = usb_get_intf(interface);
+
+ /* Reversed from USB PCAPs, no idea what these mean. */
+ smi->buf[SMI_CMD_PREAMBLE0] = cpu_to_le16(0xe800);
+ smi->buf[SMI_CMD_PREAMBLE1] = cpu_to_le16(0x0001);
+
+ usb_set_intfdata(interface, smi);
+
+ snprintf(mdio->id, MII_BUS_ID_SIZE, "smi2usb-%s", dev_name(dev));
+ mdio->name = mdio->id;
+ mdio->parent = dev;
+ mdio->read = smi2usb_mdio_read;
+ mdio->write = smi2usb_mdio_write;
+
+ err = of_mdiobus_register(mdio, dev->of_node);
+ if (err)
+ goto err_put;
+
+ return 0;
+
+err_put:
+ usb_put_intf(interface);
+ usb_put_dev(interface_to_usbdev(interface));
+err:
+ return err;
+}
+
+static void smi2usb_disconnect(struct usb_interface *interface)
+{
+ struct smi2usb *smi;
+
+ smi = usb_get_intfdata(interface);
+ mdiobus_unregister(smi->mdio);
+ usb_set_intfdata(interface, NULL);
+
+ usb_put_intf(interface);
+ usb_put_dev(interface_to_usbdev(interface));
+}
+
+static struct usb_driver smi2usb_driver = {
+ .name = "smi2usb",
+ .id_table = smi2usb_table,
+ .probe = smi2usb_probe,
+ .disconnect = smi2usb_disconnect,
+};
+
+module_usb_driver(smi2usb_driver);
+
+MODULE_AUTHOR("Tobias Waldekranz <tobias@waldekranz.com>");
+MODULE_DESCRIPTION("Marvell SMI2USB Adapter");
+MODULE_LICENSE("GPL");
An MDIO controller present on development boards for Marvell switches from the Link Street (88E6xxx) family. Using this module, you can use the following setup as a development platform for switchdev and DSA related work. .-------. .-----------------. | USB----USB | | SoC | | 88E6390X-DB ETH1-10 | ETH----ETH0 | '-------' '-----------------' Signed-off-by: Tobias Waldekranz <tobias@waldekranz.com> --- v1->v2: - Reverse christmas tree ordering of local variables. --- MAINTAINERS | 1 + drivers/net/phy/Kconfig | 7 ++ drivers/net/phy/Makefile | 1 + drivers/net/phy/mdio-smi2usb.c | 137 +++++++++++++++++++++++++++++++++ 4 files changed, 146 insertions(+) create mode 100644 drivers/net/phy/mdio-smi2usb.c