Commit 2b33b202 authored by David S. Miller's avatar David S. Miller
Browse files

Merge branch 'Bugfixes-in-Microsemi-Ocelot-switch-driver'



Vladimir Oltean says:

====================
Bugfixes in Microsemi Ocelot switch driver

This is a series of 8 assorted patches for "net", on the drivers for the
VSC7514 MIPS switch (Ocelot-1), the VSC9953 PowerPC (Seville), and a few
more that are common to all supported devices since they are in the
common library portion.
====================

Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 5f6857e8 e5fb512d
Loading
Loading
Loading
Loading
+7 −1
Original line number Diff line number Diff line
@@ -585,7 +585,10 @@ static int felix_setup(struct dsa_switch *ds)
	if (err)
		return err;

	ocelot_init(ocelot);
	err = ocelot_init(ocelot);
	if (err)
		return err;

	if (ocelot->ptp) {
		err = ocelot_init_timestamp(ocelot, &ocelot_ptp_clock_info);
		if (err) {
@@ -640,10 +643,13 @@ static void felix_teardown(struct dsa_switch *ds)
{
	struct ocelot *ocelot = ds->priv;
	struct felix *felix = ocelot_to_felix(ocelot);
	int port;

	if (felix->info->mdio_bus_free)
		felix->info->mdio_bus_free(ocelot);

	for (port = 0; port < ocelot->num_phys_ports; port++)
		ocelot_deinit_port(ocelot, port);
	ocelot_deinit_timestamp(ocelot);
	/* stop workqueue thread */
	ocelot_deinit(ocelot);
+1 −1
Original line number Diff line number Diff line
@@ -1008,7 +1008,7 @@ static const struct felix_info seville_info_vsc9953 = {
	.vcap_is2_keys		= vsc9953_vcap_is2_keys,
	.vcap_is2_actions	= vsc9953_vcap_is2_actions,
	.vcap			= vsc9953_vcap_props,
	.shared_queue_sz	= 128 * 1024,
	.shared_queue_sz	= 2048 * 1024,
	.num_mact_rows		= 2048,
	.num_ports		= 10,
	.mdio_bus_alloc		= vsc9953_mdio_bus_alloc,
+15 −9
Original line number Diff line number Diff line
@@ -421,10 +421,15 @@ int ocelot_port_add_txtstamp_skb(struct ocelot_port *ocelot_port,

	if (ocelot->ptp && shinfo->tx_flags & SKBTX_HW_TSTAMP &&
	    ocelot_port->ptp_cmd == IFH_REW_OP_TWO_STEP_PTP) {
		spin_lock(&ocelot_port->ts_id_lock);

		shinfo->tx_flags |= SKBTX_IN_PROGRESS;
		/* Store timestamp ID in cb[0] of sk_buff */
		skb->cb[0] = ocelot_port->ts_id % 4;
		skb->cb[0] = ocelot_port->ts_id;
		ocelot_port->ts_id = (ocelot_port->ts_id + 1) % 4;
		skb_queue_tail(&ocelot_port->tx_skbs, skb);

		spin_unlock(&ocelot_port->ts_id_lock);
		return 0;
	}
	return -ENODATA;
@@ -1300,6 +1305,7 @@ void ocelot_init_port(struct ocelot *ocelot, int port)
	struct ocelot_port *ocelot_port = ocelot->ports[port];

	skb_queue_head_init(&ocelot_port->tx_skbs);
	spin_lock_init(&ocelot_port->ts_id_lock);

	/* Basic L2 initialization */

@@ -1544,18 +1550,18 @@ EXPORT_SYMBOL(ocelot_init);

void ocelot_deinit(struct ocelot *ocelot)
{
	struct ocelot_port *port;
	int i;

	cancel_delayed_work(&ocelot->stats_work);
	destroy_workqueue(ocelot->stats_queue);
	mutex_destroy(&ocelot->stats_lock);

	for (i = 0; i < ocelot->num_phys_ports; i++) {
		port = ocelot->ports[i];
		skb_queue_purge(&port->tx_skbs);
	}
}
EXPORT_SYMBOL(ocelot_deinit);

void ocelot_deinit_port(struct ocelot *ocelot, int port)
{
	struct ocelot_port *ocelot_port = ocelot->ports[port];

	skb_queue_purge(&ocelot_port->tx_skbs);
}
EXPORT_SYMBOL(ocelot_deinit_port);

MODULE_LICENSE("Dual MIT/GPL");
+6 −6
Original line number Diff line number Diff line
@@ -330,6 +330,7 @@ static int ocelot_port_xmit(struct sk_buff *skb, struct net_device *dev)
	u8 grp = 0; /* Send everything on CPU group 0 */
	unsigned int i, count, last;
	int port = priv->chip_port;
	bool do_tstamp;

	val = ocelot_read(ocelot, QS_INJ_STATUS);
	if (!(val & QS_INJ_STATUS_FIFO_RDY(BIT(grp))) ||
@@ -344,10 +345,12 @@ static int ocelot_port_xmit(struct sk_buff *skb, struct net_device *dev)
	info.vid = skb_vlan_tag_get(skb);

	/* Check if timestamping is needed */
	do_tstamp = (ocelot_port_add_txtstamp_skb(ocelot_port, skb) == 0);

	if (ocelot->ptp && shinfo->tx_flags & SKBTX_HW_TSTAMP) {
		info.rew_op = ocelot_port->ptp_cmd;
		if (ocelot_port->ptp_cmd == IFH_REW_OP_TWO_STEP_PTP)
			info.rew_op |= (ocelot_port->ts_id  % 4) << 3;
			info.rew_op |= skb->cb[0] << 3;
	}

	ocelot_gen_ifh(ifh, &info);
@@ -380,12 +383,9 @@ static int ocelot_port_xmit(struct sk_buff *skb, struct net_device *dev)
	dev->stats.tx_packets++;
	dev->stats.tx_bytes += skb->len;

	if (!ocelot_port_add_txtstamp_skb(ocelot_port, skb)) {
		ocelot_port->ts_id++;
		return NETDEV_TX_OK;
	}

	if (!do_tstamp)
		dev_kfree_skb_any(skb);

	return NETDEV_TX_OK;
}

+137 −96
Original line number Diff line number Diff line
@@ -896,11 +896,137 @@ static struct ptp_clock_info ocelot_ptp_clock_info = {
	.enable		= ocelot_ptp_enable,
};

static void mscc_ocelot_release_ports(struct ocelot *ocelot)
{
	int port;

	for (port = 0; port < ocelot->num_phys_ports; port++) {
		struct ocelot_port_private *priv;
		struct ocelot_port *ocelot_port;

		ocelot_port = ocelot->ports[port];
		if (!ocelot_port)
			continue;

		ocelot_deinit_port(ocelot, port);

		priv = container_of(ocelot_port, struct ocelot_port_private,
				    port);

		unregister_netdev(priv->dev);
		free_netdev(priv->dev);
	}
}

static int mscc_ocelot_init_ports(struct platform_device *pdev,
				  struct device_node *ports)
{
	struct ocelot *ocelot = platform_get_drvdata(pdev);
	struct device_node *portnp;
	int err;

	ocelot->ports = devm_kcalloc(ocelot->dev, ocelot->num_phys_ports,
				     sizeof(struct ocelot_port *), GFP_KERNEL);
	if (!ocelot->ports)
		return -ENOMEM;

	/* No NPI port */
	ocelot_configure_cpu(ocelot, -1, OCELOT_TAG_PREFIX_NONE,
			     OCELOT_TAG_PREFIX_NONE);

	for_each_available_child_of_node(ports, portnp) {
		struct ocelot_port_private *priv;
		struct ocelot_port *ocelot_port;
		struct device_node *phy_node;
		phy_interface_t phy_mode;
		struct phy_device *phy;
		struct regmap *target;
		struct resource *res;
		struct phy *serdes;
		char res_name[8];
		u32 port;

		if (of_property_read_u32(portnp, "reg", &port))
			continue;

		snprintf(res_name, sizeof(res_name), "port%d", port);

		res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
						   res_name);
		target = ocelot_regmap_init(ocelot, res);
		if (IS_ERR(target))
			continue;

		phy_node = of_parse_phandle(portnp, "phy-handle", 0);
		if (!phy_node)
			continue;

		phy = of_phy_find_device(phy_node);
		of_node_put(phy_node);
		if (!phy)
			continue;

		err = ocelot_probe_port(ocelot, port, target, phy);
		if (err) {
			of_node_put(portnp);
			return err;
		}

		ocelot_port = ocelot->ports[port];
		priv = container_of(ocelot_port, struct ocelot_port_private,
				    port);

		of_get_phy_mode(portnp, &phy_mode);

		ocelot_port->phy_mode = phy_mode;

		switch (ocelot_port->phy_mode) {
		case PHY_INTERFACE_MODE_NA:
			continue;
		case PHY_INTERFACE_MODE_SGMII:
			break;
		case PHY_INTERFACE_MODE_QSGMII:
			/* Ensure clock signals and speed is set on all
			 * QSGMII links
			 */
			ocelot_port_writel(ocelot_port,
					   DEV_CLOCK_CFG_LINK_SPEED
					   (OCELOT_SPEED_1000),
					   DEV_CLOCK_CFG);
			break;
		default:
			dev_err(ocelot->dev,
				"invalid phy mode for port%d, (Q)SGMII only\n",
				port);
			of_node_put(portnp);
			return -EINVAL;
		}

		serdes = devm_of_phy_get(ocelot->dev, portnp, NULL);
		if (IS_ERR(serdes)) {
			err = PTR_ERR(serdes);
			if (err == -EPROBE_DEFER)
				dev_dbg(ocelot->dev, "deferring probe\n");
			else
				dev_err(ocelot->dev,
					"missing SerDes phys for port%d\n",
					port);

			of_node_put(portnp);
			return err;
		}

		priv->serdes = serdes;
	}

	return 0;
}

static int mscc_ocelot_probe(struct platform_device *pdev)
{
	struct device_node *np = pdev->dev.of_node;
	struct device_node *ports, *portnp;
	int err, irq_xtr, irq_ptp_rdy;
	struct device_node *ports;
	struct ocelot *ocelot;
	struct regmap *hsio;
	unsigned int i;
@@ -985,20 +1111,24 @@ static int mscc_ocelot_probe(struct platform_device *pdev)

	ports = of_get_child_by_name(np, "ethernet-ports");
	if (!ports) {
		dev_err(&pdev->dev, "no ethernet-ports child node found\n");
		dev_err(ocelot->dev, "no ethernet-ports child node found\n");
		return -ENODEV;
	}

	ocelot->num_phys_ports = of_get_child_count(ports);

	ocelot->ports = devm_kcalloc(&pdev->dev, ocelot->num_phys_ports,
				     sizeof(struct ocelot_port *), GFP_KERNEL);

	ocelot->vcap_is2_keys = vsc7514_vcap_is2_keys;
	ocelot->vcap_is2_actions = vsc7514_vcap_is2_actions;
	ocelot->vcap = vsc7514_vcap_props;

	ocelot_init(ocelot);
	err = ocelot_init(ocelot);
	if (err)
		goto out_put_ports;

	err = mscc_ocelot_init_ports(pdev, ports);
	if (err)
		goto out_put_ports;

	if (ocelot->ptp) {
		err = ocelot_init_timestamp(ocelot, &ocelot_ptp_clock_info);
		if (err) {
@@ -1008,96 +1138,6 @@ static int mscc_ocelot_probe(struct platform_device *pdev)
		}
	}

	/* No NPI port */
	ocelot_configure_cpu(ocelot, -1, OCELOT_TAG_PREFIX_NONE,
			     OCELOT_TAG_PREFIX_NONE);

	for_each_available_child_of_node(ports, portnp) {
		struct ocelot_port_private *priv;
		struct ocelot_port *ocelot_port;
		struct device_node *phy_node;
		phy_interface_t phy_mode;
		struct phy_device *phy;
		struct regmap *target;
		struct resource *res;
		struct phy *serdes;
		char res_name[8];
		u32 port;

		if (of_property_read_u32(portnp, "reg", &port))
			continue;

		snprintf(res_name, sizeof(res_name), "port%d", port);

		res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
						   res_name);
		target = ocelot_regmap_init(ocelot, res);
		if (IS_ERR(target))
			continue;

		phy_node = of_parse_phandle(portnp, "phy-handle", 0);
		if (!phy_node)
			continue;

		phy = of_phy_find_device(phy_node);
		of_node_put(phy_node);
		if (!phy)
			continue;

		err = ocelot_probe_port(ocelot, port, target, phy);
		if (err) {
			of_node_put(portnp);
			goto out_put_ports;
		}

		ocelot_port = ocelot->ports[port];
		priv = container_of(ocelot_port, struct ocelot_port_private,
				    port);

		of_get_phy_mode(portnp, &phy_mode);

		ocelot_port->phy_mode = phy_mode;

		switch (ocelot_port->phy_mode) {
		case PHY_INTERFACE_MODE_NA:
			continue;
		case PHY_INTERFACE_MODE_SGMII:
			break;
		case PHY_INTERFACE_MODE_QSGMII:
			/* Ensure clock signals and speed is set on all
			 * QSGMII links
			 */
			ocelot_port_writel(ocelot_port,
					   DEV_CLOCK_CFG_LINK_SPEED
					   (OCELOT_SPEED_1000),
					   DEV_CLOCK_CFG);
			break;
		default:
			dev_err(ocelot->dev,
				"invalid phy mode for port%d, (Q)SGMII only\n",
				port);
			of_node_put(portnp);
			err = -EINVAL;
			goto out_put_ports;
		}

		serdes = devm_of_phy_get(ocelot->dev, portnp, NULL);
		if (IS_ERR(serdes)) {
			err = PTR_ERR(serdes);
			if (err == -EPROBE_DEFER)
				dev_dbg(ocelot->dev, "deferring probe\n");
			else
				dev_err(ocelot->dev,
					"missing SerDes phys for port%d\n",
					port);

			of_node_put(portnp);
			goto out_put_ports;
		}

		priv->serdes = serdes;
	}

	register_netdevice_notifier(&ocelot_netdevice_nb);
	register_switchdev_notifier(&ocelot_switchdev_nb);
	register_switchdev_blocking_notifier(&ocelot_switchdev_blocking_nb);
@@ -1114,6 +1154,7 @@ static int mscc_ocelot_remove(struct platform_device *pdev)
	struct ocelot *ocelot = platform_get_drvdata(pdev);

	ocelot_deinit_timestamp(ocelot);
	mscc_ocelot_release_ports(ocelot);
	ocelot_deinit(ocelot);
	unregister_switchdev_blocking_notifier(&ocelot_switchdev_blocking_nb);
	unregister_switchdev_notifier(&ocelot_switchdev_nb);
Loading