[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: Kashyap Desai <kashyap.desai@lsi.com>
Reviewed-by: Eric Moore <eric.moore@lsi.com>
Signed-off-by: James Bottomley <James.Bottomley@suse.de>
This commit is contained in:
Kashyap, Desai 2009-12-16 18:55:26 +05:30 committed by James Bottomley
parent f7c95ef02b
commit 50d5c60634
3 changed files with 232 additions and 17 deletions

View File

@ -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

View File

@ -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

View File

@ -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,
};