Commit 35c891e1 authored by David S. Miller's avatar David S. Miller
Browse files

Merge branch 'New-DSA-driver-for-VSC9953-Seville-switch'

Vladimir Oltean says:

====================
New DSA driver for VSC9953 Seville switch

Looking at the Felix and Ocelot drivers, Maxim asked if it would be
possible to use them as a base for a new driver for the Seville switch
inside NXP T1040. Turns out, it is! The result is that the mscc_felix
driver was extended to probe on Seville.

The biggest challenge seems to be getting register read/write API
generic enough to cover such wild bitfield variations between hardware
generations.

Currently, both felix and seville are built under the same kernel config
option (NET_DSA_MSCC_FELIX). This has both some advantages (no need to
duplicate the Lynx PCS code from felix_vsc9959.c) and some disadvantages
(Seville needs to depend on PCI and on ENETC_MDIO). This will be further
refined as time progresses.

The driver has been completely reviewed. Previous submission was here,
it wasn't accepted due to a conflict with Mark Brown's tree, very late
in the release cycle:

https://patchwork.ozlabs.org/project/netdev/cover/20200531122640.1375715-1-olteanv@gmail.com/



So this is more of a repost, with the only changes being related to
rebasing on top of the cleanup I had to do in Ocelot.
====================

Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 5d037b4d 77710929
Loading
Loading
Loading
Loading
+101 −4
Original line number Diff line number Diff line
@@ -4,10 +4,15 @@ Microchip Ocelot switch driver family
Felix
-----

The VSC9959 core is currently the only switch supported by the driver, and is
found in the NXP LS1028A. It is a PCI device, part of the larger ENETC root
complex. As a result, the ethernet-switch node is a sub-node of the PCIe root
complex node and its "reg" property conforms to the parent node bindings:
Currently the switches supported by the felix driver are:

- VSC9959 (Felix)
- VSC9953 (Seville)

The VSC9959 switch is found in the NXP LS1028A. It is a PCI device, part of the
larger ENETC root complex. As a result, the ethernet-switch node is a sub-node
of the PCIe root complex node and its "reg" property conforms to the parent
node bindings:

* reg: Specifies PCIe Device Number and Function Number of the endpoint device,
  in this case for the Ethernet L2Switch it is PF5 (of device 0, bus 0).
@@ -114,3 +119,95 @@ Example:
		};
	};
};

The VSC9953 switch is found inside NXP T1040. It is a platform device with the
following required properties:

- compatible:
	Must be "mscc,vsc9953-switch".

Supported PHY interface types (appropriate SerDes protocol setting changes are
needed in the RCW binary):

* phy_mode = "internal": on ports 8 and 9
* phy_mode = "sgmii": on ports 0, 1, 2, 3, 4, 5, 6, 7
* phy_mode = "qsgmii": on ports 0, 1, 2, 3, 4, 5, 6, 7

Example:

&soc {
	ethernet-switch@800000 {
		#address-cells = <0x1>;
		#size-cells = <0x0>;
		compatible = "mscc,vsc9953-switch";
		little-endian;
		reg = <0x800000 0x290000>;

		ports {
			#address-cells = <0x1>;
			#size-cells = <0x0>;

			port@0 {
				reg = <0x0>;
				label = "swp0";
			};

			port@1 {
				reg = <0x1>;
				label = "swp1";
			};

			port@2 {
				reg = <0x2>;
				label = "swp2";
			};

			port@3 {
				reg = <0x3>;
				label = "swp3";
			};

			port@4 {
				reg = <0x4>;
				label = "swp4";
			};

			port@5 {
				reg = <0x5>;
				label = "swp5";
			};

			port@6 {
				reg = <0x6>;
				label = "swp6";
			};

			port@7 {
				reg = <0x7>;
				label = "swp7";
			};

			port@8 {
				reg = <0x8>;
				phy-mode = "internal";
				ethernet = <&enet0>;

				fixed-link {
					speed = <2500>;
					full-duplex;
				};
			};

			port@9 {
				reg = <0x9>;
				phy-mode = "internal";
				status = "disabled";

				fixed-link {
					speed = <2500>;
					full-duplex;
				};
			};
		};
	};
};
+8 −4
Original line number Diff line number Diff line
@@ -10,7 +10,11 @@ config NET_DSA_MSCC_FELIX
	select NET_DSA_TAG_OCELOT
	select FSL_ENETC_MDIO
	help
	  This driver supports the VSC9959 network switch, which is a member of
	  the Vitesse / Microsemi / Microchip Ocelot family of switching cores.
	  It is embedded as a PCIe function of the NXP LS1028A ENETC integrated
	  endpoint.
	  This driver supports network switches from the the Vitesse /
	  Microsemi / Microchip Ocelot family of switching cores that are
	  connected to their host CPU via Ethernet.
	  The following switches are supported:
	  - VSC9959 (Felix): embedded as a PCIe function of the NXP LS1028A
	    ENETC integrated endpoint.
	  - VSC9953 (Seville): embedded as a platform device on the
	    NXP T1040 SoC.
+2 −1
Original line number Diff line number Diff line
@@ -3,4 +3,5 @@ obj-$(CONFIG_NET_DSA_MSCC_FELIX) += mscc_felix.o

mscc_felix-objs := \
	felix.o \
	felix_vsc9959.o
	felix_vsc9959.o \
	seville_vsc9953.o
+48 −184
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0
/* Copyright 2019 NXP Semiconductors
 *
 * This is an umbrella module for all network switches that are
 * register-compatible with Ocelot and that perform I/O to their host CPU
 * through an NPI (Node Processor Interface) Ethernet port.
 */
#include <uapi/linux/if_bridge.h>
#include <soc/mscc/ocelot_vcap.h>
@@ -9,6 +13,7 @@
#include <soc/mscc/ocelot_ana.h>
#include <soc/mscc/ocelot_ptp.h>
#include <soc/mscc/ocelot.h>
#include <linux/platform_device.h>
#include <linux/packing.h>
#include <linux/module.h>
#include <linux/of_net.h>
@@ -185,37 +190,10 @@ static void felix_phylink_validate(struct dsa_switch *ds, int port,
				   struct phylink_link_state *state)
{
	struct ocelot *ocelot = ds->priv;
	struct ocelot_port *ocelot_port = ocelot->ports[port];
	__ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, };

	if (state->interface != PHY_INTERFACE_MODE_NA &&
	    state->interface != ocelot_port->phy_mode) {
		bitmap_zero(supported, __ETHTOOL_LINK_MODE_MASK_NBITS);
		return;
	}

	phylink_set_port_modes(mask);
	phylink_set(mask, Autoneg);
	phylink_set(mask, Pause);
	phylink_set(mask, Asym_Pause);
	phylink_set(mask, 10baseT_Half);
	phylink_set(mask, 10baseT_Full);
	phylink_set(mask, 100baseT_Half);
	phylink_set(mask, 100baseT_Full);
	phylink_set(mask, 1000baseT_Half);
	phylink_set(mask, 1000baseT_Full);

	if (state->interface == PHY_INTERFACE_MODE_INTERNAL ||
	    state->interface == PHY_INTERFACE_MODE_2500BASEX ||
	    state->interface == PHY_INTERFACE_MODE_USXGMII) {
		phylink_set(mask, 2500baseT_Full);
		phylink_set(mask, 2500baseX_Full);
	}
	struct felix *felix = ocelot_to_felix(ocelot);

	bitmap_and(supported, supported, mask,
		   __ETHTOOL_LINK_MODE_MASK_NBITS);
	bitmap_and(state->advertising, state->advertising, mask,
		   __ETHTOOL_LINK_MODE_MASK_NBITS);
	if (felix->info->phylink_validate)
		felix->info->phylink_validate(ocelot, port, supported, state);
}

static int felix_phylink_mac_pcs_get_state(struct dsa_switch *ds, int port,
@@ -249,8 +227,7 @@ static void felix_phylink_mac_link_down(struct dsa_switch *ds, int port,
	struct ocelot_port *ocelot_port = ocelot->ports[port];

	ocelot_port_writel(ocelot_port, 0, DEV_MAC_ENA_CFG);
	ocelot_rmw_rix(ocelot, 0, QSYS_SWITCH_PORT_MODE_PORT_ENA,
		       QSYS_SWITCH_PORT_MODE, port);
	ocelot_fields_write(ocelot, port, QSYS_SWITCH_PORT_MODE_PORT_ENA, 0);
}

static void felix_phylink_mac_link_up(struct dsa_switch *ds, int port,
@@ -326,10 +303,8 @@ static void felix_phylink_mac_link_up(struct dsa_switch *ds, int port,
			 ANA_PORT_PORT_CFG, port);

	/* Core: Enable port for frame transfer */
	ocelot_write_rix(ocelot, QSYS_SWITCH_PORT_MODE_INGRESS_DROP_MODE |
			 QSYS_SWITCH_PORT_MODE_SCH_NEXT_CFG(1) |
			 QSYS_SWITCH_PORT_MODE_PORT_ENA,
			 QSYS_SWITCH_PORT_MODE, port);
	ocelot_fields_write(ocelot, port,
			    QSYS_SWITCH_PORT_MODE_PORT_ENA, 1);

	if (felix->info->pcs_link_up)
		felix->info->pcs_link_up(ocelot, port, link_an_mode, interface,
@@ -459,7 +434,6 @@ static int felix_init_structs(struct felix *felix, int num_phys_ports)
{
	struct ocelot *ocelot = &felix->ocelot;
	phy_interface_t *port_phy_modes;
	resource_size_t switch_base;
	struct resource res;
	int port, i, err;

@@ -490,9 +464,6 @@ static int felix_init_structs(struct felix *felix, int num_phys_ports)
		return err;
	}

	switch_base = pci_resource_start(felix->pdev,
					 felix->info->switch_pci_bar);

	for (i = 0; i < TARGET_MAX; i++) {
		struct regmap *target;

@@ -501,8 +472,8 @@ static int felix_init_structs(struct felix *felix, int num_phys_ports)

		memcpy(&res, &felix->info->target_io_res[i], sizeof(res));
		res.flags = IORESOURCE_MEM;
		res.start += switch_base;
		res.end += switch_base;
		res.start += felix->switch_base;
		res.end += felix->switch_base;

		target = ocelot_regmap_init(ocelot, &res);
		if (IS_ERR(target)) {
@@ -524,7 +495,8 @@ static int felix_init_structs(struct felix *felix, int num_phys_ports)

	for (port = 0; port < num_phys_ports; port++) {
		struct ocelot_port *ocelot_port;
		void __iomem *port_regs;
		struct regmap *target;
		u8 *template;

		ocelot_port = devm_kzalloc(ocelot->dev,
					   sizeof(struct ocelot_port),
@@ -538,21 +510,34 @@ static int felix_init_structs(struct felix *felix, int num_phys_ports)

		memcpy(&res, &felix->info->port_io_res[port], sizeof(res));
		res.flags = IORESOURCE_MEM;
		res.start += switch_base;
		res.end += switch_base;
		res.start += felix->switch_base;
		res.end += felix->switch_base;

		target = ocelot_regmap_init(ocelot, &res);
		if (IS_ERR(target)) {
			dev_err(ocelot->dev,
				"Failed to map memory space for port %d\n",
				port);
			kfree(port_phy_modes);
			return PTR_ERR(target);
		}

		port_regs = devm_ioremap_resource(ocelot->dev, &res);
		if (IS_ERR(port_regs)) {
		template = devm_kzalloc(ocelot->dev, OCELOT_TAG_LEN,
					GFP_KERNEL);
		if (!template) {
			dev_err(ocelot->dev,
				"failed to map registers for port %d\n", port);
				"Failed to allocate memory for DSA tag\n");
			kfree(port_phy_modes);
			return PTR_ERR(port_regs);
			return -ENOMEM;
		}

		ocelot_port->phy_mode = port_phy_modes[port];
		ocelot_port->ocelot = ocelot;
		ocelot_port->regs = port_regs;
		ocelot_port->target = target;
		ocelot_port->xmit_template = template;
		ocelot->ports[port] = ocelot_port;

		felix->info->xmit_template_populate(ocelot, port);
	}

	kfree(port_phy_modes);
@@ -791,7 +776,7 @@ static int felix_port_setup_tc(struct dsa_switch *ds, int port,
		return -EOPNOTSUPP;
}

static const struct dsa_switch_ops felix_switch_ops = {
const struct dsa_switch_ops felix_switch_ops = {
	.get_tag_protocol	= felix_get_tag_protocol,
	.setup			= felix_setup,
	.teardown		= felix_teardown,
@@ -834,149 +819,28 @@ static const struct dsa_switch_ops felix_switch_ops = {
	.port_setup_tc          = felix_port_setup_tc,
};

static struct felix_info *felix_instance_tbl[] = {
	[FELIX_INSTANCE_VSC9959] = &felix_info_vsc9959,
};

static irqreturn_t felix_irq_handler(int irq, void *data)
{
	struct ocelot *ocelot = (struct ocelot *)data;

	/* The INTB interrupt is used for both PTP TX timestamp interrupt
	 * and preemption status change interrupt on each port.
	 *
	 * - Get txtstamp if have
	 * - TODO: handle preemption. Without handling it, driver may get
	 *   interrupt storm.
	 */

	ocelot_get_txtstamp(ocelot);

	return IRQ_HANDLED;
}

static int felix_pci_probe(struct pci_dev *pdev,
			   const struct pci_device_id *id)
static int __init felix_init(void)
{
	enum felix_instance instance = id->driver_data;
	struct dsa_switch *ds;
	struct ocelot *ocelot;
	struct felix *felix;
	int err;

	if (pdev->dev.of_node && !of_device_is_available(pdev->dev.of_node)) {
		dev_info(&pdev->dev, "device is disabled, skipping\n");
		return -ENODEV;
	}

	err = pci_enable_device(pdev);
	if (err) {
		dev_err(&pdev->dev, "device enable failed\n");
		goto err_pci_enable;
	}

	/* set up for high or low dma */
	err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
	if (err) {
		err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
		if (err) {
			dev_err(&pdev->dev,
				"DMA configuration failed: 0x%x\n", err);
			goto err_dma;
		}
	}

	felix = kzalloc(sizeof(struct felix), GFP_KERNEL);
	if (!felix) {
		err = -ENOMEM;
		dev_err(&pdev->dev, "Failed to allocate driver memory\n");
		goto err_alloc_felix;
	}

	pci_set_drvdata(pdev, felix);
	ocelot = &felix->ocelot;
	ocelot->dev = &pdev->dev;
	felix->pdev = pdev;
	felix->info = felix_instance_tbl[instance];

	pci_set_master(pdev);

	err = devm_request_threaded_irq(&pdev->dev, pdev->irq, NULL,
					&felix_irq_handler, IRQF_ONESHOT,
					"felix-intb", ocelot);
	if (err) {
		dev_err(&pdev->dev, "Failed to request irq\n");
		goto err_alloc_irq;
	}

	ocelot->ptp = 1;

	ds = kzalloc(sizeof(struct dsa_switch), GFP_KERNEL);
	if (!ds) {
		err = -ENOMEM;
		dev_err(&pdev->dev, "Failed to allocate DSA switch\n");
		goto err_alloc_ds;
	}

	ds->dev = &pdev->dev;
	ds->num_ports = felix->info->num_ports;
	ds->num_tx_queues = felix->info->num_tx_queues;
	ds->ops = &felix_switch_ops;
	ds->priv = ocelot;
	felix->ds = ds;
	err = pci_register_driver(&felix_vsc9959_pci_driver);
	if (err)
		return err;

	err = dsa_register_switch(ds);
	if (err) {
		dev_err(&pdev->dev, "Failed to register DSA switch: %d\n", err);
		goto err_register_ds;
	}
	err = platform_driver_register(&seville_vsc9953_driver);
	if (err)
		return err;

	return 0;

err_register_ds:
	kfree(ds);
err_alloc_ds:
err_alloc_irq:
err_alloc_felix:
	kfree(felix);
err_dma:
	pci_disable_device(pdev);
err_pci_enable:
	return err;
}
module_init(felix_init);

static void felix_pci_remove(struct pci_dev *pdev)
static void __exit felix_exit(void)
{
	struct felix *felix;

	felix = pci_get_drvdata(pdev);

	dsa_unregister_switch(felix->ds);

	kfree(felix->ds);
	kfree(felix);

	pci_disable_device(pdev);
	pci_unregister_driver(&felix_vsc9959_pci_driver);
	platform_driver_unregister(&seville_vsc9953_driver);
}

static struct pci_device_id felix_ids[] = {
	{
		/* NXP LS1028A */
		PCI_DEVICE(PCI_VENDOR_ID_FREESCALE, 0xEEF0),
		.driver_data = FELIX_INSTANCE_VSC9959,
	},
	{ 0, }
};
MODULE_DEVICE_TABLE(pci, felix_ids);

static struct pci_driver felix_pci_driver = {
	.name		= KBUILD_MODNAME,
	.id_table	= felix_ids,
	.probe		= felix_pci_probe,
	.remove		= felix_pci_remove,
};

module_pci_driver(felix_pci_driver);
module_exit(felix_exit);

MODULE_DESCRIPTION("Felix Switch driver");
MODULE_LICENSE("GPL v2");
+21 −7
Original line number Diff line number Diff line
@@ -37,28 +37,42 @@ struct felix_info {
			       int speed, int duplex);
	void	(*pcs_link_state)(struct ocelot *ocelot, int port,
				  struct phylink_link_state *state);
	void	(*phylink_validate)(struct ocelot *ocelot, int port,
				    unsigned long *supported,
				    struct phylink_link_state *state);
	int	(*prevalidate_phy_mode)(struct ocelot *ocelot, int port,
					phy_interface_t phy_mode);
	int	(*port_setup_tc)(struct dsa_switch *ds, int port,
				 enum tc_setup_type type, void *type_data);
	void	(*port_sched_speed_set)(struct ocelot *ocelot, int port,
					u32 speed);
	void	(*xmit_template_populate)(struct ocelot *ocelot, int port);
};

extern struct felix_info		felix_info_vsc9959;

enum felix_instance {
	FELIX_INSTANCE_VSC9959		= 0,
};
extern const struct dsa_switch_ops felix_switch_ops;
extern struct pci_driver felix_vsc9959_pci_driver;
extern struct platform_driver seville_vsc9953_driver;

/* DSA glue / front-end for struct ocelot */
struct felix {
	struct dsa_switch		*ds;
	struct pci_dev			*pdev;
	struct felix_info		*info;
	const struct felix_info		*info;
	struct ocelot			ocelot;
	struct mii_bus			*imdio;
	struct phy_device		**pcs;
	resource_size_t			switch_base;
	resource_size_t			imdio_base;
};

void vsc9959_pcs_link_state(struct ocelot *ocelot, int port,
			    struct phylink_link_state *state);
void vsc9959_pcs_config(struct ocelot *ocelot, int port,
			unsigned int link_an_mode,
			const struct phylink_link_state *state);
void vsc9959_pcs_link_up(struct ocelot *ocelot, int port,
			 unsigned int link_an_mode,
			 phy_interface_t interface,
			 int speed, int duplex);
void vsc9959_mdio_bus_free(struct ocelot *ocelot);

#endif
Loading