diff mbox series

[v2,2/2] net: phy: marvell smi2usb mdio controller

Message ID 20200319135952.16258-2-tobias@waldekranz.com
State Changes Requested
Delegated to: David Miller
Headers show
Series [v2,1/2] dt-bindings: net: add marvell smi2usb bindings | expand

Commit Message

Tobias Waldekranz March 19, 2020, 1:59 p.m. UTC
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

Comments

Andrew Lunn March 19, 2020, 3:49 p.m. UTC | #1
On Thu, Mar 19, 2020 at 02:59:52PM +0100, Tobias Waldekranz wrote:
> 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

Hi Tobias

Where does the name mii2usb come from? To me, it seems to be the wrong
way around, it is USB to MII. I suppose the Marvell Switch team could
of given it this name, for them the switch is the centre of their
world, and things connect to it?

I'm just wondering if we should actually ignore Marvell and call it
usb2mii?

I also think there should be a marvell prefix in the name, since were
could be other implementations of USB/MII. mvusb2mii?

Do you know how this is implemented? Is it a product you can purchase?
Or a microcontroller on the board which implements this? It would be
an interesting product, especially on x86 machines which generally end
up doing bit-banging because of the lack of drivers using kernel MDIO.

> +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;
> +

...


> +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));
> +}

I don't know enough about USB. Does disconnect have the same semantics
remove()? You used devm_mdiobus_alloc_size() to allocate the bus
structure. Will it get freed after disconnect? I've had USB devices
connected via flaky USB hubs and they have repeatedly disappeared and
reappeared. I wonder if in that case you are leaking memory if
disconnect does not release the memory?

> +	usb_put_intf(interface);
> +	usb_put_dev(interface_to_usbdev(interface));
> +}

Another USB novice question. Is this safe? Could the put of interface
cause it to be destroyed? Then interface_to_usbdev() is called on
invalid memory?

Maybe this should be cross posted to a USB mailing list, so we can get
the USB aspects reviewed. The MDIO bits seem good to me.

	   Andrew
Tobias Waldekranz March 19, 2020, 10:35 p.m. UTC | #2
On Thu, Mar 19, 2020 at 04:49:37PM +0100, Andrew Lunn wrote:
> On Thu, Mar 19, 2020 at 02:59:52PM +0100, Tobias Waldekranz wrote:
> > 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
> 
> Hi Tobias
> 
> Where does the name mii2usb come from? To me, it seems to be the wrong
> way around, it is USB to MII. I suppose the Marvell Switch team could
> of given it this name, for them the switch is the centre of their
> world, and things connect to it?

The name is indeed coming from Marvell. They use the term SMI over
MDIO in most of their software and documentation. I had the same
reaction to the name regarding the ordering of the terms, but felt it
was best to go with the vendor's choice.

> I'm just wondering if we should actually ignore Marvell and call it
> usb2mii?
> 
> I also think there should be a marvell prefix in the name, since were
> could be other implementations of USB/MII. mvusb2mii?

You're absolutely right that there should be an mv prefix in
there. Calling it usb2mii seems like a misnomer though. At least for
me, MII relates more to the data interface between a MAC and a PHY,
whereas MDIO or SMI refers to the control interface (MDC/MDIO).

How about just mdio-mvusb?

> Do you know how this is implemented? Is it a product you can purchase?
> Or a microcontroller on the board which implements this? It would be
> an interesting product, especially on x86 machines which generally end
> up doing bit-banging because of the lack of drivers using kernel MDIO.

On the 88E6390X-DB, I know that there is a chip by the USB port that
is probably either an MCU or a small FPGA. I can have a closer look at
it when I'm at the office tomorrow if you'd like. I also remember
seeing some docs from Marvell which seemed to indicate that they have
a standalone product providing only the USB-to-MDIO functionality.

The x86 use-case is interesting. It would be even more so if there was
some way of loading a DSA DT fragment so that you could hook it up to
your machine's Ethernet port.

> > +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;
> > +
> 
> ...
> 
> 
> > +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));
> > +}
> 
> I don't know enough about USB. Does disconnect have the same semantics
> remove()? You used devm_mdiobus_alloc_size() to allocate the bus
> structure. Will it get freed after disconnect? I've had USB devices
> connected via flaky USB hubs and they have repeatedly disappeared and
> reappeared. I wonder if in that case you are leaking memory if
> disconnect does not release the memory?

Disclaimer: This is my first ever USB driver.

I assumed that since we're removing 'interface', 'interface->dev' will
be removed as well and thus calling all devm hooks.

> > +	usb_put_intf(interface);
> > +	usb_put_dev(interface_to_usbdev(interface));
> > +}
> 
> Another USB novice question. Is this safe? Could the put of interface
> cause it to be destroyed? Then interface_to_usbdev() is called on
> invalid memory?

That does indeed look scary. I inverted the order of the calls to the
_get_ functions, which I got from the USB skeleton driver. I'll try to
review some other drivers to see if I can figure this out.

> Maybe this should be cross posted to a USB mailing list, so we can get
> the USB aspects reviewed. The MDIO bits seem good to me.

Good idea. Any chance you can help an LKML rookie out? How does one go
about that? Do I simply reply to this thread and add the USB list, or
do I post the patches again as a new series? Any special tags? Is
there any documentation available?

> 	   Andrew
Andrew Lunn March 19, 2020, 11 p.m. UTC | #3
Hi Tobias

> How about just mdio-mvusb?

Yes, i like that.

> On the 88E6390X-DB, I know that there is a chip by the USB port that
> is probably either an MCU or a small FPGA. I can have a closer look at
> it when I'm at the office tomorrow if you'd like. I also remember
> seeing some docs from Marvell which seemed to indicate that they have
> a standalone product providing only the USB-to-MDIO functionality.

I would be interested in knowing more.

> The x86 use-case is interesting. It would be even more so if there was
> some way of loading a DSA DT fragment so that you could hook it up to
> your machine's Ethernet port.

We don't have that at the moment. But so long as you only need
internal copper PHYs, it is possible to use a platform device and it
all just works.

> > > +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;
> > > +
> > 
> > ...
> > 
> > 
> > > +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));
> > > +}
> > 
> > I don't know enough about USB. Does disconnect have the same semantics
> > remove()? You used devm_mdiobus_alloc_size() to allocate the bus
> > structure. Will it get freed after disconnect? I've had USB devices
> > connected via flaky USB hubs and they have repeatedly disappeared and
> > reappeared. I wonder if in that case you are leaking memory if
> > disconnect does not release the memory?
> 
> Disclaimer: This is my first ever USB driver.

And i've only ever written one which has been merged.

> I assumed that since we're removing 'interface', 'interface->dev' will
> be removed as well and thus calling all devm hooks.
> 
> > > +	usb_put_intf(interface);
> > > +	usb_put_dev(interface_to_usbdev(interface));
> > > +}
> > 
> > Another USB novice question. Is this safe? Could the put of interface
> > cause it to be destroyed? Then interface_to_usbdev() is called on
> > invalid memory?
> 
> That does indeed look scary. I inverted the order of the calls to the
> _get_ functions, which I got from the USB skeleton driver. I'll try to
> review some other drivers to see if I can figure this out.
> 
> > Maybe this should be cross posted to a USB mailing list, so we can get
> > the USB aspects reviewed. The MDIO bits seem good to me.
> 
> Good idea. Any chance you can help an LKML rookie out? How does one go
> about that? Do I simply reply to this thread and add the USB list, or
> do I post the patches again as a new series? Any special tags? Is
> there any documentation available?

I would fixup the naming and repost. You can put whatever comments you
want under the --- marker. So say this driver should be merged via
netdev, but you would appreciate reviews of the USB parts from USB
maintainers. linux-usb@vger.kernel.org would be the correct list to
add.

     Andrew
Tobias Waldekranz March 20, 2020, 2:44 p.m. UTC | #4
Hi Andrew,

> > How about just mdio-mvusb?
>
> 
> Yes, i like that.

ACK.

> > On the 88E6390X-DB, I know that there is a chip by the USB port that
> > is probably either an MCU or a small FPGA. I can have a closer look at
> > it when I'm at the office tomorrow if you'd like. I also remember
> > seeing some docs from Marvell which seemed to indicate that they have
> > a standalone product providing only the USB-to-MDIO functionality.
>
> 
> I would be interested in knowing more.

It seems like they are using the Cypress FX2 controller
(CY7C68013). I've used it before on USB device projects. If I remember
correctly it has an 8052 core, a USB2 controller and some low-speed
I/O blocks. Couldn't locate the slide deck about a standalone device
unfortunately.

> I would fixup the naming and repost. You can put whatever comments you
> want under the --- marker. So say this driver should be merged via
> netdev, but you would appreciate reviews of the USB parts from USB
> maintainers. linux-usb@vger.kernel.org would be the correct list to
> add.

Great. Just to make sure I've understood: I'll send v3 with _both_
netdev and linux-usb in "To:"?

Thanks,
wkz
Andrew Lunn March 21, 2020, 12:29 a.m. UTC | #5
On Fri, Mar 20, 2020 at 03:44:34PM +0100, Tobias Waldekranz wrote:
> Hi Andrew,
> 
> > > How about just mdio-mvusb?
> >
> > 
> > Yes, i like that.
> 
> ACK.
> 
> > > On the 88E6390X-DB, I know that there is a chip by the USB port that
> > > is probably either an MCU or a small FPGA. I can have a closer look at
> > > it when I'm at the office tomorrow if you'd like. I also remember
> > > seeing some docs from Marvell which seemed to indicate that they have
> > > a standalone product providing only the USB-to-MDIO functionality.
> >
> > 
> > I would be interested in knowing more.
> 
> It seems like they are using the Cypress FX2 controller
> (CY7C68013). I've used it before on USB device projects. If I remember
> correctly it has an 8052 core, a USB2 controller and some low-speed
> I/O blocks. Couldn't locate the slide deck about a standalone device
> unfortunately.
> 
> > I would fixup the naming and repost. You can put whatever comments you
> > want under the --- marker. So say this driver should be merged via
> > netdev, but you would appreciate reviews of the USB parts from USB
> > maintainers. linux-usb@vger.kernel.org would be the correct list to
> > add.
> 
> Great. Just to make sure I've understood: I'll send v3 with _both_
> netdev and linux-usb in "To:"?

Hi Tobias

I normally use To: for the maintainer i expect to merge the patch,
i.e. DaveM, and Cc: for lists and other maintainers who should review
it.

	Andrew
diff mbox series

Patch

diff --git a/MAINTAINERS b/MAINTAINERS
index 83bb7ce3e23e..a7771e577832 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -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
diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig
index cc7f1df855da..ddde79c6f354 100644
--- a/drivers/net/phy/Kconfig
+++ b/drivers/net/phy/Kconfig
@@ -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
diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile
index 70774ab474e6..fcbe4bd26747 100644
--- a/drivers/net/phy/Makefile
+++ b/drivers/net/phy/Makefile
@@ -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
diff --git a/drivers/net/phy/mdio-smi2usb.c b/drivers/net/phy/mdio-smi2usb.c
new file mode 100644
index 000000000000..c4f7f555a091
--- /dev/null
+++ b/drivers/net/phy/mdio-smi2usb.c
@@ -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");