summaryrefslogtreecommitdiffstats
path: root/drivers/scsi/libsas
diff options
context:
space:
mode:
authorJames Bottomley <James.Bottomley@steeleye.com>2006-09-06 19:28:07 -0500
committerJames Bottomley <jejb@mulgrave.il.steeleye.com>2006-09-07 15:20:23 -0500
commita01e70e570a72b8a8c9a58062e4f5bdcd3986222 (patch)
treed2b8b5e0e69d14805ac98033561597de6e24d5c6 /drivers/scsi/libsas
parentd24e1eeb3a16e4944288c2f3bf082e1513f4b425 (diff)
[SCSI] aci94xx: implement link rate setting
This patch implements the ability to set the minimum and maximum linkrates for both libsas (for expanders) and aic94xx (for the host phys). It also tidies up the setting of the hardware min and max to make sure they're updated when the expander emits a change broadcast. Signed-off-by: James Bottomley <James.Bottomley@SteelEye.com>
Diffstat (limited to 'drivers/scsi/libsas')
-rw-r--r--drivers/scsi/libsas/sas_expander.c20
-rw-r--r--drivers/scsi/libsas/sas_init.c44
-rw-r--r--drivers/scsi/libsas/sas_internal.h2
-rw-r--r--drivers/scsi/libsas/sas_phy.c15
4 files changed, 64 insertions, 17 deletions
diff --git a/drivers/scsi/libsas/sas_expander.c b/drivers/scsi/libsas/sas_expander.c
index 02e796ee027..30b8014bcc7 100644
--- a/drivers/scsi/libsas/sas_expander.c
+++ b/drivers/scsi/libsas/sas_expander.c
@@ -187,10 +187,10 @@ static void sas_set_ex_phy(struct domain_device *dev, int phy_id,
phy->phy->identify.initiator_port_protocols = phy->attached_iproto;
phy->phy->identify.target_port_protocols = phy->attached_tproto;
phy->phy->identify.phy_identifier = phy_id;
- phy->phy->minimum_linkrate_hw = SAS_LINK_RATE_1_5_GBPS;
- phy->phy->maximum_linkrate_hw = SAS_LINK_RATE_3_0_GBPS;
- phy->phy->minimum_linkrate = SAS_LINK_RATE_1_5_GBPS;
- phy->phy->maximum_linkrate = SAS_LINK_RATE_3_0_GBPS;
+ phy->phy->minimum_linkrate_hw = dr->hmin_linkrate;
+ phy->phy->maximum_linkrate_hw = dr->hmax_linkrate;
+ phy->phy->minimum_linkrate = dr->pmin_linkrate;
+ phy->phy->maximum_linkrate = dr->pmax_linkrate;
phy->phy->negotiated_linkrate = phy->linkrate;
if (!rediscover)
@@ -404,7 +404,8 @@ out:
#define PC_RESP_SIZE 8
int sas_smp_phy_control(struct domain_device *dev, int phy_id,
- enum phy_func phy_func)
+ enum phy_func phy_func,
+ struct sas_phy_linkrates *rates)
{
u8 *pc_req;
u8 *pc_resp;
@@ -423,6 +424,10 @@ int sas_smp_phy_control(struct domain_device *dev, int phy_id,
pc_req[1] = SMP_PHY_CONTROL;
pc_req[9] = phy_id;
pc_req[10]= phy_func;
+ if (rates) {
+ pc_req[32] = rates->minimum_linkrate << 4;
+ pc_req[33] = rates->maximum_linkrate << 4;
+ }
res = smp_execute_task(dev, pc_req, PC_REQ_SIZE, pc_resp,PC_RESP_SIZE);
@@ -436,7 +441,7 @@ static void sas_ex_disable_phy(struct domain_device *dev, int phy_id)
struct expander_device *ex = &dev->ex_dev;
struct ex_phy *phy = &ex->ex_phy[phy_id];
- sas_smp_phy_control(dev, phy_id, PHY_FUNC_DISABLE);
+ sas_smp_phy_control(dev, phy_id, PHY_FUNC_DISABLE, NULL);
phy->linkrate = SAS_PHY_DISABLED;
}
@@ -731,7 +736,7 @@ static int sas_ex_discover_dev(struct domain_device *dev, int phy_id)
/* Phy state */
if (ex_phy->linkrate == SAS_SATA_SPINUP_HOLD) {
- if (!sas_smp_phy_control(dev, phy_id, PHY_FUNC_LINK_RESET))
+ if (!sas_smp_phy_control(dev, phy_id, PHY_FUNC_LINK_RESET, NULL))
res = sas_ex_phy_discover(dev, phy_id);
if (res)
return res;
@@ -1706,6 +1711,7 @@ static int sas_rediscover_dev(struct domain_device *dev, int phy_id)
SAS_ADDR(phy->attached_sas_addr)) {
SAS_DPRINTK("ex %016llx phy 0x%x broadcast flutter\n",
SAS_ADDR(dev->sas_addr), phy_id);
+ sas_ex_phy_discover(dev, phy_id);
} else
res = sas_discover_new(dev, phy_id);
out:
diff --git a/drivers/scsi/libsas/sas_init.c b/drivers/scsi/libsas/sas_init.c
index b961664b810..c836a237fb7 100644
--- a/drivers/scsi/libsas/sas_init.c
+++ b/drivers/scsi/libsas/sas_init.c
@@ -159,17 +159,57 @@ static int sas_phy_reset(struct sas_phy *phy, int hard_reset)
struct sas_internal *i =
to_sas_internal(sas_ha->core.shost->transportt);
- ret = i->dft->lldd_control_phy(asd_phy, reset_type);
+ ret = i->dft->lldd_control_phy(asd_phy, reset_type, NULL);
} else {
struct sas_rphy *rphy = dev_to_rphy(phy->dev.parent);
struct domain_device *ddev = sas_find_dev_by_rphy(rphy);
- ret = sas_smp_phy_control(ddev, phy->number, reset_type);
+ ret = sas_smp_phy_control(ddev, phy->number, reset_type, NULL);
}
return ret;
}
+static int sas_set_phy_speed(struct sas_phy *phy,
+ struct sas_phy_linkrates *rates)
+{
+ int ret;
+
+ if ((rates->minimum_linkrate &&
+ rates->minimum_linkrate > phy->maximum_linkrate) ||
+ (rates->maximum_linkrate &&
+ rates->maximum_linkrate < phy->minimum_linkrate))
+ return -EINVAL;
+
+ if (rates->minimum_linkrate &&
+ rates->minimum_linkrate < phy->minimum_linkrate_hw)
+ rates->minimum_linkrate = phy->minimum_linkrate_hw;
+
+ if (rates->maximum_linkrate &&
+ rates->maximum_linkrate > phy->maximum_linkrate_hw)
+ rates->maximum_linkrate = phy->maximum_linkrate_hw;
+
+ if (scsi_is_sas_phy_local(phy)) {
+ struct Scsi_Host *shost = dev_to_shost(phy->dev.parent);
+ struct sas_ha_struct *sas_ha = SHOST_TO_SAS_HA(shost);
+ struct asd_sas_phy *asd_phy = sas_ha->sas_phy[phy->number];
+ struct sas_internal *i =
+ to_sas_internal(sas_ha->core.shost->transportt);
+
+ ret = i->dft->lldd_control_phy(asd_phy, PHY_FUNC_SET_LINK_RATE,
+ rates);
+ } else {
+ struct sas_rphy *rphy = dev_to_rphy(phy->dev.parent);
+ struct domain_device *ddev = sas_find_dev_by_rphy(rphy);
+ ret = sas_smp_phy_control(ddev, phy->number,
+ PHY_FUNC_LINK_RESET, rates);
+
+ }
+
+ return ret;
+}
+
static struct sas_function_template sft = {
.phy_reset = sas_phy_reset,
+ .set_phy_speed = sas_set_phy_speed,
.get_linkerrors = sas_get_linkerrors,
};
diff --git a/drivers/scsi/libsas/sas_internal.h b/drivers/scsi/libsas/sas_internal.h
index 0d69ede4b94..bffcee47492 100644
--- a/drivers/scsi/libsas/sas_internal.h
+++ b/drivers/scsi/libsas/sas_internal.h
@@ -70,7 +70,7 @@ int sas_notify_lldd_dev_found(struct domain_device *);
void sas_notify_lldd_dev_gone(struct domain_device *);
int sas_smp_phy_control(struct domain_device *dev, int phy_id,
- enum phy_func phy_func);
+ enum phy_func phy_func, struct sas_phy_linkrates *);
int sas_smp_get_phy_events(struct sas_phy *phy);
struct domain_device *sas_find_dev_by_rphy(struct sas_rphy *rphy);
diff --git a/drivers/scsi/libsas/sas_phy.c b/drivers/scsi/libsas/sas_phy.c
index 024ab00e70d..9340cdbae4a 100644
--- a/drivers/scsi/libsas/sas_phy.c
+++ b/drivers/scsi/libsas/sas_phy.c
@@ -67,13 +67,14 @@ static void sas_phye_oob_error(void *data)
switch (phy->error) {
case 1:
case 2:
- i->dft->lldd_control_phy(phy, PHY_FUNC_HARD_RESET);
+ i->dft->lldd_control_phy(phy, PHY_FUNC_HARD_RESET,
+ NULL);
break;
case 3:
default:
phy->error = 0;
phy->enabled = 0;
- i->dft->lldd_control_phy(phy, PHY_FUNC_DISABLE);
+ i->dft->lldd_control_phy(phy, PHY_FUNC_DISABLE, NULL);
break;
}
}
@@ -90,7 +91,7 @@ static void sas_phye_spinup_hold(void *data)
&phy->phy_events_pending);
phy->error = 0;
- i->dft->lldd_control_phy(phy, PHY_FUNC_RELEASE_SPINUP_HOLD);
+ i->dft->lldd_control_phy(phy, PHY_FUNC_RELEASE_SPINUP_HOLD, NULL);
}
/* ---------- Phy class registration ---------- */
@@ -144,10 +145,10 @@ int sas_register_phys(struct sas_ha_struct *sas_ha)
phy->phy->identify.target_port_protocols = phy->tproto;
phy->phy->identify.sas_address = SAS_ADDR(sas_ha->sas_addr);
phy->phy->identify.phy_identifier = i;
- phy->phy->minimum_linkrate_hw = SAS_LINK_RATE_1_5_GBPS;
- phy->phy->maximum_linkrate_hw = SAS_LINK_RATE_3_0_GBPS;
- phy->phy->minimum_linkrate = SAS_LINK_RATE_1_5_GBPS;
- phy->phy->maximum_linkrate = SAS_LINK_RATE_3_0_GBPS;
+ phy->phy->minimum_linkrate_hw = SAS_LINK_RATE_UNKNOWN;
+ phy->phy->maximum_linkrate_hw = SAS_LINK_RATE_UNKNOWN;
+ phy->phy->minimum_linkrate = SAS_LINK_RATE_UNKNOWN;
+ phy->phy->maximum_linkrate = SAS_LINK_RATE_UNKNOWN;
phy->phy->negotiated_linkrate = SAS_LINK_RATE_UNKNOWN;
sas_phy_add(phy->phy);