int asd_clear_nexus_ha(struct sas_ha_struct *sas_ha);
/* ---------- Phy Management ---------- */
-int asd_control_phy(struct asd_sas_phy *phy, enum phy_func func);
+int asd_control_phy(struct asd_sas_phy *phy, enum phy_func func, void *arg);
#endif
static inline void get_lrate_mode(struct asd_phy *phy, u8 oob_mode)
{
+ struct sas_phy *sas_phy = phy->sas_phy.phy;
+
switch (oob_mode & 7) {
case PHY_SPEED_60:
/* FIXME: sas transport class doesn't have this */
phy->sas_phy.phy->negotiated_linkrate = SAS_LINK_RATE_1_5_GBPS;
break;
}
+ sas_phy->negotiated_linkrate = phy->sas_phy.linkrate;
+ sas_phy->maximum_linkrate_hw = SAS_LINK_RATE_3_0_GBPS;
+ sas_phy->minimum_linkrate_hw = SAS_LINK_RATE_1_5_GBPS;
+ sas_phy->maximum_linkrate = phy->phy_desc->max_sas_lrate;
+ sas_phy->minimum_linkrate = phy->phy_desc->min_sas_lrate;
+
if (oob_mode & SAS_MODE)
phy->sas_phy.oob_mode = SAS_OOB_MODE;
else if (oob_mode & SATA_MODE)
[PHY_FUNC_RELEASE_SPINUP_HOLD] = RELEASE_SPINUP_HOLD,
};
-int asd_control_phy(struct asd_sas_phy *phy, enum phy_func func)
+int asd_control_phy(struct asd_sas_phy *phy, enum phy_func func, void *arg)
{
struct asd_ha_struct *asd_ha = phy->ha->lldd_ha;
+ struct asd_phy_desc *pd = asd_ha->phys[phy->id].phy_desc;
struct asd_ascb *ascb;
+ struct sas_phy_linkrates *rates;
int res = 1;
- if (func == PHY_FUNC_CLEAR_ERROR_LOG)
+ switch (func) {
+ case PHY_FUNC_CLEAR_ERROR_LOG:
return -ENOSYS;
+ case PHY_FUNC_SET_LINK_RATE:
+ rates = arg;
+ if (rates->minimum_linkrate) {
+ pd->min_sas_lrate = rates->minimum_linkrate;
+ pd->min_sata_lrate = rates->minimum_linkrate;
+ }
+ if (rates->maximum_linkrate) {
+ pd->max_sas_lrate = rates->maximum_linkrate;
+ pd->max_sata_lrate = rates->maximum_linkrate;
+ }
+ func = PHY_FUNC_LINK_RESET;
+ break;
+ default:
+ break;
+ }
ascb = asd_ascb_alloc_list(asd_ha, &res, GFP_KERNEL);
if (!ascb)
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)
#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;
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);
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;
}
/* 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;
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:
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,
};
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);
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;
}
}
&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 ---------- */
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);
int (*lldd_clear_nexus_ha)(struct sas_ha_struct *);
/* Phy management */
- int (*lldd_control_phy)(struct asd_sas_phy *, enum phy_func);
+ int (*lldd_control_phy)(struct asd_sas_phy *, enum phy_func, void *);
};
extern int sas_register_ha(struct sas_ha_struct *);
PHY_FUNC_CLEAR_AFFIL,
PHY_FUNC_TX_SATA_PS_SIGNAL,
PHY_FUNC_RELEASE_SPINUP_HOLD = 0x10, /* LOCAL PORT ONLY! */
+ PHY_FUNC_SET_LINK_RATE,
};
/* SAS LLDD would need to report only _very_few_ of those, like BROADCAST.