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

Commit 50d5c606 authored by Kashyap, Desai's avatar Kashyap, Desai Committed by James Bottomley
Browse files

[SCSI] mpt2sas: Added phy_enable and set_phy_speed sysfs callback support.



Added new callbacks phy_enable and set_phy_speed in the
mpt2sas_transport_functions template. This will allow end user to
enable/disable phys and change links rates using the SysFS interface.
Current implementation only supports direct attached phys, but we
could in the future add support for expander based phys.
A new subroutine mpt2sas_config_set_sas_iounit_pg1 was added;
this wrapper function used to send request to controller firmware to modify
the phys and link rates. A new subroutine _transport_find_local_phy was added;
a function for easly obtaining the local phy object for direct attached.

Example to disable a phy
echo 0 > /sys/class/phy3:0/enable

Example to enable the same phy
echo 1 > /sys/class/phy3:0/enable

Example to change the link rate to 1.5
#echo "1.5 Gbit" > /sys/class/phy3:0/maximum_linkrate
#cat /sys/class/phy3:0/negotiated_linkrate
1.5 Gbit

Example to change the link rate to 3.0
#echo "3.0 Gbit" > /sys/class/phy3:0/maximum_linkrate
#cat /sys/class/phy3:0/negotiated_linkrate
3.0 Gbit

Example to change the link rate to 6.0
#echo "6.0 Gbit" > /sys/class/phy3:0/maximum_linkrate
#cat /sys/class/phy3:0/negotiated_linkrate
6.0 Gbit

Signed-off-by: default avatarKashyap Desai <kashyap.desai@lsi.com>
Reviewed-by: default avatarEric Moore <eric.moore@lsi.com>
Signed-off-by: default avatarJames Bottomley <James.Bottomley@suse.de>
parent f7c95ef0
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -853,6 +853,8 @@ int mpt2sas_config_set_iounit_pg1(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t
    *mpi_reply, Mpi2IOUnitPage1_t *config_page);
int mpt2sas_config_get_sas_iounit_pg1(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t
    *mpi_reply, Mpi2SasIOUnitPage1_t *config_page, u16 sz);
int mpt2sas_config_set_sas_iounit_pg1(struct MPT2SAS_ADAPTER *ioc,
    Mpi2ConfigReply_t *mpi_reply, Mpi2SasIOUnitPage1_t *config_page, u16 sz);
int mpt2sas_config_get_ioc_pg8(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t
    *mpi_reply, Mpi2IOCPage8_t *config_page);
int mpt2sas_config_get_expander_pg0(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t
+48 −3
Original line number Diff line number Diff line
@@ -324,7 +324,9 @@ _config_request(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigRequest_t
		if (r != 0)
			goto out;
		if (mpi_request->Action ==
		    MPI2_CONFIG_ACTION_PAGE_WRITE_CURRENT) {
		    MPI2_CONFIG_ACTION_PAGE_WRITE_CURRENT ||
		    mpi_request->Action ==
		    MPI2_CONFIG_ACTION_PAGE_WRITE_NVRAM) {
			ioc->base_add_sg_single(&mpi_request->PageBufferSGE,
			    MPT2_CONFIG_COMMON_WRITE_SGLFLAGS | mem.sz,
			    mem.page_dma);
@@ -882,7 +884,7 @@ mpt2sas_config_get_sas_iounit_pg0(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t
}

/**
 * mpt2sas_config_get_sas_iounit_pg1 - obtain sas iounit page 0
 * mpt2sas_config_get_sas_iounit_pg1 - obtain sas iounit page 1
 * @ioc: per adapter object
 * @mpi_reply: reply mf payload returned from firmware
 * @config_page: contents of the config page
@@ -907,7 +909,7 @@ mpt2sas_config_get_sas_iounit_pg1(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t
	mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED;
	mpi_request.ExtPageType = MPI2_CONFIG_EXTPAGETYPE_SAS_IO_UNIT;
	mpi_request.Header.PageNumber = 1;
	mpi_request.Header.PageVersion = MPI2_SASIOUNITPAGE0_PAGEVERSION;
	mpi_request.Header.PageVersion = MPI2_SASIOUNITPAGE1_PAGEVERSION;
	mpt2sas_base_build_zero_len_sge(ioc, &mpi_request.PageBufferSGE);
	r = _config_request(ioc, &mpi_request, mpi_reply,
	    MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0);
@@ -921,6 +923,49 @@ mpt2sas_config_get_sas_iounit_pg1(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t
	return r;
}

/**
 * mpt2sas_config_set_sas_iounit_pg1 - send sas iounit page 1
 * @ioc: per adapter object
 * @mpi_reply: reply mf payload returned from firmware
 * @config_page: contents of the config page
 * @sz: size of buffer passed in config_page
 * Context: sleep.
 *
 * Calling function should call config_get_number_hba_phys prior to
 * this function, so enough memory is allocated for config_page.
 *
 * Returns 0 for success, non-zero for failure.
 */
int
mpt2sas_config_set_sas_iounit_pg1(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t
    *mpi_reply, Mpi2SasIOUnitPage1_t *config_page, u16 sz)
{
	Mpi2ConfigRequest_t mpi_request;
	int r;

	memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
	mpi_request.Function = MPI2_FUNCTION_CONFIG;
	mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
	mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED;
	mpi_request.ExtPageType = MPI2_CONFIG_EXTPAGETYPE_SAS_IO_UNIT;
	mpi_request.Header.PageNumber = 1;
	mpi_request.Header.PageVersion = MPI2_SASIOUNITPAGE1_PAGEVERSION;
	mpt2sas_base_build_zero_len_sge(ioc, &mpi_request.PageBufferSGE);
	r = _config_request(ioc, &mpi_request, mpi_reply,
	    MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0);
	if (r)
		goto out;

	mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_WRITE_CURRENT;
	_config_request(ioc, &mpi_request, mpi_reply,
	    MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, sz);
	mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_WRITE_NVRAM;
	r = _config_request(ioc, &mpi_request, mpi_reply,
	    MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, sz);
 out:
	return r;
}

/**
 * mpt2sas_config_get_expander_pg0 - obtain expander page 0
 * @ioc: per adapter object
+182 −14
Original line number Diff line number Diff line
@@ -855,6 +855,17 @@ rphy_to_ioc(struct sas_rphy *rphy)
	return shost_priv(shost);
}

static struct _sas_phy *
_transport_find_local_phy(struct MPT2SAS_ADAPTER *ioc, struct sas_phy *phy)
{
	int i;

	for (i = 0; i < ioc->sas_hba.num_phys; i++)
		if (ioc->sas_hba.phy[i].phy == phy)
			return(&ioc->sas_hba.phy[i]);
	return NULL;
}

/**
 * _transport_get_linkerrors -
 * @phy: The sas phy object
@@ -870,14 +881,8 @@ _transport_get_linkerrors(struct sas_phy *phy)
	struct _sas_phy *mpt2sas_phy;
	Mpi2ConfigReply_t mpi_reply;
	Mpi2SasPhyPage1_t phy_pg1;
	int i;

	for (i = 0, mpt2sas_phy = NULL; i < ioc->sas_hba.num_phys &&
	    !mpt2sas_phy; i++) {
		if (ioc->sas_hba.phy[i].phy != phy)
			continue;
		mpt2sas_phy = &ioc->sas_hba.phy[i];
	}
	mpt2sas_phy = _transport_find_local_phy(ioc, phy);

	if (!mpt2sas_phy) /* this phy not on sas_host */
		return -EINVAL;
@@ -971,14 +976,8 @@ _transport_phy_reset(struct sas_phy *phy, int hard_reset)
	struct _sas_phy *mpt2sas_phy;
	Mpi2SasIoUnitControlReply_t mpi_reply;
	Mpi2SasIoUnitControlRequest_t mpi_request;
	int i;

	for (i = 0, mpt2sas_phy = NULL; i < ioc->sas_hba.num_phys &&
	    !mpt2sas_phy; i++) {
		if (ioc->sas_hba.phy[i].phy != phy)
			continue;
		mpt2sas_phy = &ioc->sas_hba.phy[i];
	}
	mpt2sas_phy = _transport_find_local_phy(ioc, phy);

	if (!mpt2sas_phy) /* this phy not on sas_host */
		return -EINVAL;
@@ -1005,6 +1004,173 @@ _transport_phy_reset(struct sas_phy *phy, int hard_reset)
	return 0;
}

/**
 * _transport_phy_enable - enable/disable phys
 * @phy: The sas phy object
 * @enable: enable phy when true
 *
 * Only support sas_host direct attached phys.
 * Returns 0 for success, non-zero for failure.
 */
static int
_transport_phy_enable(struct sas_phy *phy, int enable)
{
	struct MPT2SAS_ADAPTER *ioc = phy_to_ioc(phy);
	struct _sas_phy *mpt2sas_phy;
	Mpi2SasIOUnitPage1_t *sas_iounit_pg1 = NULL;
	Mpi2ConfigReply_t mpi_reply;
	u16 ioc_status;
	u16 sz;
	int rc = 0;

	mpt2sas_phy = _transport_find_local_phy(ioc, phy);

	if (!mpt2sas_phy) /* this phy not on sas_host */
		return -EINVAL;

	/* sas_iounit page 1 */
	sz = offsetof(Mpi2SasIOUnitPage1_t, PhyData) + (ioc->sas_hba.num_phys *
	    sizeof(Mpi2SasIOUnit1PhyData_t));
	sas_iounit_pg1 = kzalloc(sz, GFP_KERNEL);
	if (!sas_iounit_pg1) {
		printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
		    ioc->name, __FILE__, __LINE__, __func__);
		rc = -ENOMEM;
		goto out;
	}
	if ((mpt2sas_config_get_sas_iounit_pg1(ioc, &mpi_reply,
	    sas_iounit_pg1, sz))) {
		printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
		    ioc->name, __FILE__, __LINE__, __func__);
		rc = -ENXIO;
		goto out;
	}
	ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
	    MPI2_IOCSTATUS_MASK;
	if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
		printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
		    ioc->name, __FILE__, __LINE__, __func__);
		rc = -EIO;
		goto out;
	}

	if (enable)
		sas_iounit_pg1->PhyData[mpt2sas_phy->phy_id].PhyFlags
		    &= ~MPI2_SASIOUNIT1_PHYFLAGS_PHY_DISABLE;
	else
		sas_iounit_pg1->PhyData[mpt2sas_phy->phy_id].PhyFlags
		    |= MPI2_SASIOUNIT1_PHYFLAGS_PHY_DISABLE;

	mpt2sas_config_set_sas_iounit_pg1(ioc, &mpi_reply, sas_iounit_pg1, sz);

 out:
	kfree(sas_iounit_pg1);
	return rc;
}

/**
 * _transport_phy_speed - set phy min/max link rates
 * @phy: The sas phy object
 * @rates: rates defined in sas_phy_linkrates
 *
 * Only support sas_host direct attached phys.
 * Returns 0 for success, non-zero for failure.
 */
static int
_transport_phy_speed(struct sas_phy *phy, struct sas_phy_linkrates *rates)
{
	struct MPT2SAS_ADAPTER *ioc = phy_to_ioc(phy);
	struct _sas_phy *mpt2sas_phy;
	Mpi2SasIOUnitPage1_t *sas_iounit_pg1 = NULL;
	Mpi2SasPhyPage0_t phy_pg0;
	Mpi2ConfigReply_t mpi_reply;
	u16 ioc_status;
	u16 sz;
	int i;
	int rc = 0;

	mpt2sas_phy = _transport_find_local_phy(ioc, phy);

	if (!mpt2sas_phy) /* this phy not on sas_host */
		return -EINVAL;

	if (!rates->minimum_linkrate)
		rates->minimum_linkrate = phy->minimum_linkrate;
	else if (rates->minimum_linkrate < phy->minimum_linkrate_hw)
		rates->minimum_linkrate = phy->minimum_linkrate_hw;

	if (!rates->maximum_linkrate)
		rates->maximum_linkrate = phy->maximum_linkrate;
	else if (rates->maximum_linkrate > phy->maximum_linkrate_hw)
		rates->maximum_linkrate = phy->maximum_linkrate_hw;

	/* sas_iounit page 1 */
	sz = offsetof(Mpi2SasIOUnitPage1_t, PhyData) + (ioc->sas_hba.num_phys *
	    sizeof(Mpi2SasIOUnit1PhyData_t));
	sas_iounit_pg1 = kzalloc(sz, GFP_KERNEL);
	if (!sas_iounit_pg1) {
		printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
		    ioc->name, __FILE__, __LINE__, __func__);
		rc = -ENOMEM;
		goto out;
	}
	if ((mpt2sas_config_get_sas_iounit_pg1(ioc, &mpi_reply,
	    sas_iounit_pg1, sz))) {
		printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
		    ioc->name, __FILE__, __LINE__, __func__);
		rc = -ENXIO;
		goto out;
	}
	ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
	    MPI2_IOCSTATUS_MASK;
	if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
		printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
		    ioc->name, __FILE__, __LINE__, __func__);
		rc = -EIO;
		goto out;
	}

	for (i = 0; i < ioc->sas_hba.num_phys; i++) {
		if (mpt2sas_phy->phy_id != i) {
			sas_iounit_pg1->PhyData[i].MaxMinLinkRate =
			    (ioc->sas_hba.phy[i].phy->minimum_linkrate +
			    (ioc->sas_hba.phy[i].phy->maximum_linkrate << 4));
		} else {
			sas_iounit_pg1->PhyData[i].MaxMinLinkRate =
			    (rates->minimum_linkrate +
			    (rates->maximum_linkrate << 4));
		}
	}

	if (mpt2sas_config_set_sas_iounit_pg1(ioc, &mpi_reply, sas_iounit_pg1,
	    sz)) {
		printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
		    ioc->name, __FILE__, __LINE__, __func__);
		rc = -ENXIO;
		goto out;
	}

	/* link reset */
	_transport_phy_reset(phy, 0);

	/* read phy page 0, then update the rates in the sas transport phy */
	if (!mpt2sas_config_get_phy_pg0(ioc, &mpi_reply, &phy_pg0,
	    mpt2sas_phy->phy_id)) {
		phy->minimum_linkrate = _transport_convert_phy_link_rate(
		    phy_pg0.ProgrammedLinkRate & MPI2_SAS_PRATE_MIN_RATE_MASK);
		phy->maximum_linkrate = _transport_convert_phy_link_rate(
		    phy_pg0.ProgrammedLinkRate >> 4);
		phy->negotiated_linkrate = _transport_convert_phy_link_rate(
		    phy_pg0.NegotiatedLinkRate &
		    MPI2_SAS_NEG_LINK_RATE_MASK_PHYSICAL);
	}

 out:
	kfree(sas_iounit_pg1);
	return rc;
}


/**
 * _transport_smp_handler - transport portal for smp passthru
 * @shost: shost object
@@ -1207,6 +1373,8 @@ struct sas_function_template mpt2sas_transport_functions = {
	.get_enclosure_identifier = _transport_get_enclosure_identifier,
	.get_bay_identifier	= _transport_get_bay_identifier,
	.phy_reset		= _transport_phy_reset,
	.phy_enable		= _transport_phy_enable,
	.set_phy_speed		= _transport_phy_speed,
	.smp_handler		= _transport_smp_handler,
};