Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 18b338f5 authored by Weilin Chang's avatar Weilin Chang Committed by David S. Miller
Browse files

liquidio: support use of ethtool to set link speed of CN23XX-225 cards



Support setting the link speed of CN23XX-225 cards (which can do 25Gbps or
10Gbps) via ethtool_ops.set_link_ksettings.

Also fix the function assigned to ethtool_ops.get_link_ksettings to use the
new link_ksettings api completely (instead of partially via
ethtool_convert_legacy_u32_to_link_mode).

Signed-off-by: default avatarWeilin Chang <weilin.chang@cavium.com>
Acked-by: default avatarRaghu Vatsavayi <raghu.vatsavayi@cavium.com>
Signed-off-by: default avatarFelix Manlunas <felix.manlunas@cavium.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 7f625bdf
Loading
Loading
Loading
Loading
+196 −0
Original line number Original line Diff line number Diff line
@@ -1481,3 +1481,199 @@ int octnet_get_link_stats(struct net_device *netdev)


	return 0;
	return 0;
}
}

static void liquidio_nic_seapi_ctl_callback(struct octeon_device *oct,
					    u32 status,
					    void *buf)
{
	struct liquidio_nic_seapi_ctl_context *ctx;
	struct octeon_soft_command *sc = buf;

	ctx = sc->ctxptr;

	oct = lio_get_device(ctx->octeon_id);
	if (status) {
		dev_err(&oct->pci_dev->dev, "%s: instruction failed. Status: %llx\n",
			__func__,
			CVM_CAST64(status));
	}
	ctx->status = status;
	complete(&ctx->complete);
}

int liquidio_set_speed(struct lio *lio, int speed)
{
	struct liquidio_nic_seapi_ctl_context *ctx;
	struct octeon_device *oct = lio->oct_dev;
	struct oct_nic_seapi_resp *resp;
	struct octeon_soft_command *sc;
	union octnet_cmd *ncmd;
	u32 ctx_size;
	int retval;
	u32 var;

	if (oct->speed_setting == speed)
		return 0;

	if (!OCTEON_CN23XX_PF(oct)) {
		dev_err(&oct->pci_dev->dev, "%s: SET SPEED only for PF\n",
			__func__);
		return -EOPNOTSUPP;
	}

	ctx_size = sizeof(struct liquidio_nic_seapi_ctl_context);
	sc = octeon_alloc_soft_command(oct, OCTNET_CMD_SIZE,
				       sizeof(struct oct_nic_seapi_resp),
				       ctx_size);
	if (!sc)
		return -ENOMEM;

	ncmd = sc->virtdptr;
	ctx  = sc->ctxptr;
	resp = sc->virtrptr;
	memset(resp, 0, sizeof(struct oct_nic_seapi_resp));

	ctx->octeon_id = lio_get_device_id(oct);
	ctx->status = 0;
	init_completion(&ctx->complete);

	ncmd->u64 = 0;
	ncmd->s.cmd = SEAPI_CMD_SPEED_SET;
	ncmd->s.param1 = speed;

	octeon_swap_8B_data((u64 *)ncmd, (OCTNET_CMD_SIZE >> 3));

	sc->iq_no = lio->linfo.txpciq[0].s.q_no;

	octeon_prepare_soft_command(oct, sc, OPCODE_NIC,
				    OPCODE_NIC_UBOOT_CTL, 0, 0, 0);

	sc->callback = liquidio_nic_seapi_ctl_callback;
	sc->callback_arg = sc;
	sc->wait_time = 5000;

	retval = octeon_send_soft_command(oct, sc);
	if (retval == IQ_SEND_FAILED) {
		dev_info(&oct->pci_dev->dev, "Failed to send soft command\n");
		retval = -EBUSY;
	} else {
		/* Wait for response or timeout */
		if (wait_for_completion_timeout(&ctx->complete,
						msecs_to_jiffies(10000)) == 0) {
			dev_err(&oct->pci_dev->dev, "%s: sc timeout\n",
				__func__);
			octeon_free_soft_command(oct, sc);
			return -EINTR;
		}

		retval = resp->status;

		if (retval) {
			dev_err(&oct->pci_dev->dev, "%s failed, retval=%d\n",
				__func__, retval);
			octeon_free_soft_command(oct, sc);
			return -EIO;
		}

		var = be32_to_cpu((__force __be32)resp->speed);
		if (var != speed) {
			dev_err(&oct->pci_dev->dev,
				"%s: setting failed speed= %x, expect %x\n",
				__func__, var, speed);
		}

		oct->speed_setting = var;
	}

	octeon_free_soft_command(oct, sc);

	return retval;
}

int liquidio_get_speed(struct lio *lio)
{
	struct liquidio_nic_seapi_ctl_context *ctx;
	struct octeon_device *oct = lio->oct_dev;
	struct oct_nic_seapi_resp *resp;
	struct octeon_soft_command *sc;
	union octnet_cmd *ncmd;
	u32 ctx_size;
	int retval;

	ctx_size = sizeof(struct liquidio_nic_seapi_ctl_context);
	sc = octeon_alloc_soft_command(oct, OCTNET_CMD_SIZE,
				       sizeof(struct oct_nic_seapi_resp),
				       ctx_size);
	if (!sc)
		return -ENOMEM;

	ncmd = sc->virtdptr;
	ctx  = sc->ctxptr;
	resp = sc->virtrptr;
	memset(resp, 0, sizeof(struct oct_nic_seapi_resp));

	ctx->octeon_id = lio_get_device_id(oct);
	ctx->status = 0;
	init_completion(&ctx->complete);

	ncmd->u64 = 0;
	ncmd->s.cmd = SEAPI_CMD_SPEED_GET;

	octeon_swap_8B_data((u64 *)ncmd, (OCTNET_CMD_SIZE >> 3));

	sc->iq_no = lio->linfo.txpciq[0].s.q_no;

	octeon_prepare_soft_command(oct, sc, OPCODE_NIC,
				    OPCODE_NIC_UBOOT_CTL, 0, 0, 0);

	sc->callback = liquidio_nic_seapi_ctl_callback;
	sc->callback_arg = sc;
	sc->wait_time = 5000;

	retval = octeon_send_soft_command(oct, sc);
	if (retval == IQ_SEND_FAILED) {
		dev_info(&oct->pci_dev->dev, "Failed to send soft command\n");
		oct->no_speed_setting = 1;
		oct->speed_setting = 25;

		retval = -EBUSY;
	} else {
		if (wait_for_completion_timeout(&ctx->complete,
						msecs_to_jiffies(10000)) == 0) {
			dev_err(&oct->pci_dev->dev, "%s: sc timeout\n",
				__func__);

			oct->speed_setting = 25;
			oct->no_speed_setting = 1;

			octeon_free_soft_command(oct, sc);

			return -EINTR;
		}
		retval = resp->status;
		if (retval) {
			dev_err(&oct->pci_dev->dev,
				"%s failed retval=%d\n", __func__, retval);
			oct->no_speed_setting = 1;
			oct->speed_setting = 25;
			octeon_free_soft_command(oct, sc);
			retval = -EIO;
		} else {
			u32 var;

			var = be32_to_cpu((__force __be32)resp->speed);
			oct->speed_setting = var;
			if (var == 0xffff) {
				oct->no_speed_setting = 1;
				/* unable to access boot variables
				 * get the default value based on the NIC type
				 */
				oct->speed_setting = 25;
			}
		}
	}

	octeon_free_soft_command(oct, sc);

	return retval;
}
+171 −24
Original line number Original line Diff line number Diff line
@@ -230,46 +230,147 @@ static int lio_get_link_ksettings(struct net_device *netdev,
	struct lio *lio = GET_LIO(netdev);
	struct lio *lio = GET_LIO(netdev);
	struct octeon_device *oct = lio->oct_dev;
	struct octeon_device *oct = lio->oct_dev;
	struct oct_link_info *linfo;
	struct oct_link_info *linfo;
	u32 supported = 0, advertising = 0;


	linfo = &lio->linfo;
	linfo = &lio->linfo;


	ethtool_link_ksettings_zero_link_mode(ecmd, supported);
	ethtool_link_ksettings_zero_link_mode(ecmd, advertising);

	switch (linfo->link.s.phy_type) {
	switch (linfo->link.s.phy_type) {
	case LIO_PHY_PORT_TP:
	case LIO_PHY_PORT_TP:
		ecmd->base.port = PORT_TP;
		ecmd->base.port = PORT_TP;
		supported = (SUPPORTED_10000baseT_Full |
			     SUPPORTED_TP | SUPPORTED_Pause);
		advertising = (ADVERTISED_10000baseT_Full | ADVERTISED_Pause);
		ecmd->base.autoneg = AUTONEG_DISABLE;
		ecmd->base.autoneg = AUTONEG_DISABLE;
		break;
		ethtool_link_ksettings_add_link_mode(ecmd, supported, TP);

		ethtool_link_ksettings_add_link_mode(ecmd, supported, Pause);
	case LIO_PHY_PORT_FIBRE:
		ethtool_link_ksettings_add_link_mode(ecmd, supported,
		ecmd->base.port = PORT_FIBRE;
						     10000baseT_Full);


		if (linfo->link.s.speed == SPEED_10000) {
		ethtool_link_ksettings_add_link_mode(ecmd, advertising, Pause);
			supported = SUPPORTED_10000baseT_Full;
		ethtool_link_ksettings_add_link_mode(ecmd, advertising,
			advertising = ADVERTISED_10000baseT_Full;
						     10000baseT_Full);
		}


		supported |= SUPPORTED_FIBRE | SUPPORTED_Pause;
		advertising |= ADVERTISED_Pause;
		ecmd->base.autoneg = AUTONEG_DISABLE;
		break;
		break;
	}


	case LIO_PHY_PORT_FIBRE:
		if (linfo->link.s.if_mode == INTERFACE_MODE_XAUI ||
		if (linfo->link.s.if_mode == INTERFACE_MODE_XAUI ||
		    linfo->link.s.if_mode == INTERFACE_MODE_RXAUI ||
		    linfo->link.s.if_mode == INTERFACE_MODE_RXAUI ||
		    linfo->link.s.if_mode == INTERFACE_MODE_XLAUI ||
		    linfo->link.s.if_mode == INTERFACE_MODE_XLAUI ||
		    linfo->link.s.if_mode == INTERFACE_MODE_XFI) {
		    linfo->link.s.if_mode == INTERFACE_MODE_XFI) {
		ethtool_convert_legacy_u32_to_link_mode(
			dev_dbg(&oct->pci_dev->dev, "ecmd->base.transceiver is XCVR_EXTERNAL\n");
			ecmd->link_modes.supported, supported);
		ethtool_convert_legacy_u32_to_link_mode(
			ecmd->link_modes.advertising, advertising);
		} else {
		} else {
		dev_err(&oct->pci_dev->dev, "Unknown link interface reported %d\n",
			dev_err(&oct->pci_dev->dev, "Unknown link interface mode: %d\n",
				linfo->link.s.if_mode);
				linfo->link.s.if_mode);
		}
		}


		ecmd->base.port = PORT_FIBRE;
		ecmd->base.autoneg = AUTONEG_DISABLE;
		ethtool_link_ksettings_add_link_mode(ecmd, supported, FIBRE);

		ethtool_link_ksettings_add_link_mode(ecmd, supported, Pause);
		ethtool_link_ksettings_add_link_mode(ecmd, advertising, Pause);
		if (oct->subsystem_id == OCTEON_CN2350_25GB_SUBSYS_ID ||
		    oct->subsystem_id == OCTEON_CN2360_25GB_SUBSYS_ID) {
			if (OCTEON_CN23XX_PF(oct)) {
				ethtool_link_ksettings_add_link_mode
					(ecmd, supported, 25000baseSR_Full);
				ethtool_link_ksettings_add_link_mode
					(ecmd, supported, 25000baseKR_Full);
				ethtool_link_ksettings_add_link_mode
					(ecmd, supported, 25000baseCR_Full);

				if (oct->no_speed_setting == 0)  {
					ethtool_link_ksettings_add_link_mode
						(ecmd, supported,
						 10000baseSR_Full);
					ethtool_link_ksettings_add_link_mode
						(ecmd, supported,
						 10000baseKR_Full);
					ethtool_link_ksettings_add_link_mode
						(ecmd, supported,
						 10000baseCR_Full);
				}

				if (oct->no_speed_setting == 0)
					liquidio_get_speed(lio);
				else
					oct->speed_setting = 25;

				if (oct->speed_setting == 10) {
					ethtool_link_ksettings_add_link_mode
						(ecmd, advertising,
						 10000baseSR_Full);
					ethtool_link_ksettings_add_link_mode
						(ecmd, advertising,
						 10000baseKR_Full);
					ethtool_link_ksettings_add_link_mode
						(ecmd, advertising,
						 10000baseCR_Full);
				}
				if (oct->speed_setting == 25) {
					ethtool_link_ksettings_add_link_mode
						(ecmd, advertising,
						 25000baseSR_Full);
					ethtool_link_ksettings_add_link_mode
						(ecmd, advertising,
						 25000baseKR_Full);
					ethtool_link_ksettings_add_link_mode
						(ecmd, advertising,
						 25000baseCR_Full);
				}
			} else { /* VF */
				if (linfo->link.s.speed == 10000) {
					ethtool_link_ksettings_add_link_mode
						(ecmd, supported,
						 10000baseSR_Full);
					ethtool_link_ksettings_add_link_mode
						(ecmd, supported,
						 10000baseKR_Full);
					ethtool_link_ksettings_add_link_mode
						(ecmd, supported,
						 10000baseCR_Full);

					ethtool_link_ksettings_add_link_mode
						(ecmd, advertising,
						 10000baseSR_Full);
					ethtool_link_ksettings_add_link_mode
						(ecmd, advertising,
						 10000baseKR_Full);
					ethtool_link_ksettings_add_link_mode
						(ecmd, advertising,
						 10000baseCR_Full);
				}

				if (linfo->link.s.speed == 25000) {
					ethtool_link_ksettings_add_link_mode
						(ecmd, supported,
						 25000baseSR_Full);
					ethtool_link_ksettings_add_link_mode
						(ecmd, supported,
						 25000baseKR_Full);
					ethtool_link_ksettings_add_link_mode
						(ecmd, supported,
						 25000baseCR_Full);

					ethtool_link_ksettings_add_link_mode
						(ecmd, advertising,
						 25000baseSR_Full);
					ethtool_link_ksettings_add_link_mode
						(ecmd, advertising,
						 25000baseKR_Full);
					ethtool_link_ksettings_add_link_mode
						(ecmd, advertising,
						 25000baseCR_Full);
				}
			}
		} else {
			ethtool_link_ksettings_add_link_mode(ecmd, supported,
							     10000baseT_Full);
			ethtool_link_ksettings_add_link_mode(ecmd, advertising,
							     10000baseT_Full);
		}
		break;
	}

	if (linfo->link.s.link_up) {
	if (linfo->link.s.link_up) {
		ecmd->base.speed = linfo->link.s.speed;
		ecmd->base.speed = linfo->link.s.speed;
		ecmd->base.duplex = linfo->link.s.duplex;
		ecmd->base.duplex = linfo->link.s.duplex;
@@ -281,6 +382,51 @@ static int lio_get_link_ksettings(struct net_device *netdev,
	return 0;
	return 0;
}
}


static int lio_set_link_ksettings(struct net_device *netdev,
				  const struct ethtool_link_ksettings *ecmd)
{
	const int speed = ecmd->base.speed;
	struct lio *lio = GET_LIO(netdev);
	struct oct_link_info *linfo;
	struct octeon_device *oct;
	u32 is25G = 0;

	oct = lio->oct_dev;

	linfo = &lio->linfo;

	if (oct->subsystem_id == OCTEON_CN2350_25GB_SUBSYS_ID ||
	    oct->subsystem_id == OCTEON_CN2360_25GB_SUBSYS_ID) {
		is25G = 1;
	} else {
		return -EOPNOTSUPP;
	}

	if (oct->no_speed_setting) {
		dev_err(&oct->pci_dev->dev, "%s: Changing speed is not supported\n",
			__func__);
		return -EOPNOTSUPP;
	}

	if ((ecmd->base.duplex != DUPLEX_UNKNOWN &&
	     ecmd->base.duplex != linfo->link.s.duplex) ||
	     ecmd->base.autoneg != AUTONEG_DISABLE ||
	    (ecmd->base.speed != 10000 && ecmd->base.speed != 25000 &&
	     ecmd->base.speed != SPEED_UNKNOWN))
		return -EOPNOTSUPP;

	if ((oct->speed_boot == speed / 1000) &&
	    oct->speed_boot == oct->speed_setting)
		return 0;

	liquidio_set_speed(lio, speed / 1000);

	dev_dbg(&oct->pci_dev->dev, "Port speed is set to %dG\n",
		oct->speed_setting);

	return 0;
}

static void
static void
lio_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *drvinfo)
lio_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *drvinfo)
{
{
@@ -2966,6 +3112,7 @@ static int lio_set_priv_flags(struct net_device *netdev, u32 flags)


static const struct ethtool_ops lio_ethtool_ops = {
static const struct ethtool_ops lio_ethtool_ops = {
	.get_link_ksettings	= lio_get_link_ksettings,
	.get_link_ksettings	= lio_get_link_ksettings,
	.set_link_ksettings	= lio_set_link_ksettings,
	.get_link		= ethtool_op_get_link,
	.get_link		= ethtool_op_get_link,
	.get_drvinfo		= lio_get_drvinfo,
	.get_drvinfo		= lio_get_drvinfo,
	.get_ringparam		= lio_ethtool_get_ringparam,
	.get_ringparam		= lio_ethtool_get_ringparam,
+20 −0
Original line number Original line Diff line number Diff line
@@ -912,6 +912,9 @@ liquidio_probe(struct pci_dev *pdev,
	/* set linux specific device pointer */
	/* set linux specific device pointer */
	oct_dev->pci_dev = (void *)pdev;
	oct_dev->pci_dev = (void *)pdev;


	oct_dev->subsystem_id = pdev->subsystem_vendor |
		(pdev->subsystem_device << 16);

	hs = &handshake[oct_dev->octeon_id];
	hs = &handshake[oct_dev->octeon_id];
	init_completion(&hs->init);
	init_completion(&hs->init);
	init_completion(&hs->started);
	init_completion(&hs->started);
@@ -3664,6 +3667,23 @@ static int setup_nic_devices(struct octeon_device *octeon_dev)
			"NIC ifidx:%d Setup successful\n", i);
			"NIC ifidx:%d Setup successful\n", i);


		octeon_free_soft_command(octeon_dev, sc);
		octeon_free_soft_command(octeon_dev, sc);

		if (octeon_dev->subsystem_id ==
			OCTEON_CN2350_25GB_SUBSYS_ID ||
		    octeon_dev->subsystem_id ==
			OCTEON_CN2360_25GB_SUBSYS_ID) {
			liquidio_get_speed(lio);

			if (octeon_dev->speed_setting == 0) {
				octeon_dev->speed_setting = 25;
				octeon_dev->no_speed_setting = 1;
			}
		} else {
			octeon_dev->no_speed_setting = 1;
			octeon_dev->speed_setting = 10;
		}
		octeon_dev->speed_boot = octeon_dev->speed_setting;

	}
	}


	devlink = devlink_alloc(&liquidio_devlink_ops,
	devlink = devlink_alloc(&liquidio_devlink_ops,
+5 −0
Original line number Original line Diff line number Diff line
@@ -411,6 +411,9 @@ liquidio_vf_probe(struct pci_dev *pdev,
	/* set linux specific device pointer */
	/* set linux specific device pointer */
	oct_dev->pci_dev = pdev;
	oct_dev->pci_dev = pdev;


	oct_dev->subsystem_id = pdev->subsystem_vendor |
		(pdev->subsystem_device << 16);

	if (octeon_device_init(oct_dev)) {
	if (octeon_device_init(oct_dev)) {
		liquidio_vf_remove(pdev);
		liquidio_vf_remove(pdev);
		return -ENOMEM;
		return -ENOMEM;
@@ -2199,6 +2202,8 @@ static int setup_nic_devices(struct octeon_device *octeon_dev)
			"NIC ifidx:%d Setup successful\n", i);
			"NIC ifidx:%d Setup successful\n", i);


		octeon_free_soft_command(octeon_dev, sc);
		octeon_free_soft_command(octeon_dev, sc);

		octeon_dev->no_speed_setting = 1;
	}
	}


	return 0;
	return 0;
+4 −0
Original line number Original line Diff line number Diff line
@@ -93,6 +93,7 @@ enum octeon_tag_type {


#define OPCODE_NIC_VF_REP_PKT          0x15
#define OPCODE_NIC_VF_REP_PKT          0x15
#define OPCODE_NIC_VF_REP_CMD          0x16
#define OPCODE_NIC_VF_REP_CMD          0x16
#define OPCODE_NIC_UBOOT_CTL           0x17


#define CORE_DRV_TEST_SCATTER_OP    0xFFF5
#define CORE_DRV_TEST_SCATTER_OP    0xFFF5


@@ -249,6 +250,9 @@ static inline void add_sg_size(struct octeon_sg_entry *sg_entry,
#define   OCTNET_CMD_VLAN_FILTER_ENABLE 0x1
#define   OCTNET_CMD_VLAN_FILTER_ENABLE 0x1
#define   OCTNET_CMD_VLAN_FILTER_DISABLE 0x0
#define   OCTNET_CMD_VLAN_FILTER_DISABLE 0x0


#define   SEAPI_CMD_SPEED_SET           0x2
#define   SEAPI_CMD_SPEED_GET           0x3

#define   LIO_CMD_WAIT_TM 100
#define   LIO_CMD_WAIT_TM 100


/* RX(packets coming from wire) Checksum verification flags */
/* RX(packets coming from wire) Checksum verification flags */
Loading