aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJames Bottomley <James.Bottomley@steeleye.com>2006-09-06 20:28:07 -0400
committerJames Bottomley <jejb@mulgrave.il.steeleye.com>2006-09-07 16:20:23 -0400
commita01e70e570a72b8a8c9a58062e4f5bdcd3986222 (patch)
treed2b8b5e0e69d14805ac98033561597de6e24d5c6
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>
-rw-r--r--drivers/scsi/aic94xx/aic94xx.h2
-rw-r--r--drivers/scsi/aic94xx/aic94xx_scb.c30
-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
-rw-r--r--include/scsi/libsas.h2
-rw-r--r--include/scsi/sas.h1
8 files changed, 95 insertions, 21 deletions
diff --git a/drivers/scsi/aic94xx/aic94xx.h b/drivers/scsi/aic94xx/aic94xx.h
index cb7caf1c9ce1..1bd5b4ecf3d5 100644
--- a/drivers/scsi/aic94xx/aic94xx.h
+++ b/drivers/scsi/aic94xx/aic94xx.h
@@ -109,6 +109,6 @@ int asd_clear_nexus_port(struct asd_sas_port *port);
109int asd_clear_nexus_ha(struct sas_ha_struct *sas_ha); 109int asd_clear_nexus_ha(struct sas_ha_struct *sas_ha);
110 110
111/* ---------- Phy Management ---------- */ 111/* ---------- Phy Management ---------- */
112int asd_control_phy(struct asd_sas_phy *phy, enum phy_func func); 112int asd_control_phy(struct asd_sas_phy *phy, enum phy_func func, void *arg);
113 113
114#endif 114#endif
diff --git a/drivers/scsi/aic94xx/aic94xx_scb.c b/drivers/scsi/aic94xx/aic94xx_scb.c
index ef8ca08b545f..7ee49b51b724 100644
--- a/drivers/scsi/aic94xx/aic94xx_scb.c
+++ b/drivers/scsi/aic94xx/aic94xx_scb.c
@@ -52,6 +52,8 @@
52 52
53static inline void get_lrate_mode(struct asd_phy *phy, u8 oob_mode) 53static inline void get_lrate_mode(struct asd_phy *phy, u8 oob_mode)
54{ 54{
55 struct sas_phy *sas_phy = phy->sas_phy.phy;
56
55 switch (oob_mode & 7) { 57 switch (oob_mode & 7) {
56 case PHY_SPEED_60: 58 case PHY_SPEED_60:
57 /* FIXME: sas transport class doesn't have this */ 59 /* FIXME: sas transport class doesn't have this */
@@ -67,6 +69,12 @@ static inline void get_lrate_mode(struct asd_phy *phy, u8 oob_mode)
67 phy->sas_phy.phy->negotiated_linkrate = SAS_LINK_RATE_1_5_GBPS; 69 phy->sas_phy.phy->negotiated_linkrate = SAS_LINK_RATE_1_5_GBPS;
68 break; 70 break;
69 } 71 }
72 sas_phy->negotiated_linkrate = phy->sas_phy.linkrate;
73 sas_phy->maximum_linkrate_hw = SAS_LINK_RATE_3_0_GBPS;
74 sas_phy->minimum_linkrate_hw = SAS_LINK_RATE_1_5_GBPS;
75 sas_phy->maximum_linkrate = phy->phy_desc->max_sas_lrate;
76 sas_phy->minimum_linkrate = phy->phy_desc->min_sas_lrate;
77
70 if (oob_mode & SAS_MODE) 78 if (oob_mode & SAS_MODE)
71 phy->sas_phy.oob_mode = SAS_OOB_MODE; 79 phy->sas_phy.oob_mode = SAS_OOB_MODE;
72 else if (oob_mode & SATA_MODE) 80 else if (oob_mode & SATA_MODE)
@@ -710,14 +718,32 @@ static const int phy_func_table[] = {
710 [PHY_FUNC_RELEASE_SPINUP_HOLD] = RELEASE_SPINUP_HOLD, 718 [PHY_FUNC_RELEASE_SPINUP_HOLD] = RELEASE_SPINUP_HOLD,
711}; 719};
712 720
713int asd_control_phy(struct asd_sas_phy *phy, enum phy_func func) 721int asd_control_phy(struct asd_sas_phy *phy, enum phy_func func, void *arg)
714{ 722{
715 struct asd_ha_struct *asd_ha = phy->ha->lldd_ha; 723 struct asd_ha_struct *asd_ha = phy->ha->lldd_ha;
724 struct asd_phy_desc *pd = asd_ha->phys[phy->id].phy_desc;
716 struct asd_ascb *ascb; 725 struct asd_ascb *ascb;
726 struct sas_phy_linkrates *rates;
717 int res = 1; 727 int res = 1;
718 728
719 if (func == PHY_FUNC_CLEAR_ERROR_LOG) 729 switch (func) {
730 case PHY_FUNC_CLEAR_ERROR_LOG:
720 return -ENOSYS; 731 return -ENOSYS;
732 case PHY_FUNC_SET_LINK_RATE:
733 rates = arg;
734 if (rates->minimum_linkrate) {
735 pd->min_sas_lrate = rates->minimum_linkrate;
736 pd->min_sata_lrate = rates->minimum_linkrate;
737 }
738 if (rates->maximum_linkrate) {
739 pd->max_sas_lrate = rates->maximum_linkrate;
740 pd->max_sata_lrate = rates->maximum_linkrate;
741 }
742 func = PHY_FUNC_LINK_RESET;
743 break;
744 default:
745 break;
746 }
721 747
722 ascb = asd_ascb_alloc_list(asd_ha, &res, GFP_KERNEL); 748 ascb = asd_ascb_alloc_list(asd_ha, &res, GFP_KERNEL);
723 if (!ascb) 749 if (!ascb)
diff --git a/drivers/scsi/libsas/sas_expander.c b/drivers/scsi/libsas/sas_expander.c
index 02e796ee027e..30b8014bcc7a 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,
187 phy->phy->identify.initiator_port_protocols = phy->attached_iproto; 187 phy->phy->identify.initiator_port_protocols = phy->attached_iproto;
188 phy->phy->identify.target_port_protocols = phy->attached_tproto; 188 phy->phy->identify.target_port_protocols = phy->attached_tproto;
189 phy->phy->identify.phy_identifier = phy_id; 189 phy->phy->identify.phy_identifier = phy_id;
190 phy->phy->minimum_linkrate_hw = SAS_LINK_RATE_1_5_GBPS; 190 phy->phy->minimum_linkrate_hw = dr->hmin_linkrate;
191 phy->phy->maximum_linkrate_hw = SAS_LINK_RATE_3_0_GBPS; 191 phy->phy->maximum_linkrate_hw = dr->hmax_linkrate;
192 phy->phy->minimum_linkrate = SAS_LINK_RATE_1_5_GBPS; 192 phy->phy->minimum_linkrate = dr->pmin_linkrate;
193 phy->phy->maximum_linkrate = SAS_LINK_RATE_3_0_GBPS; 193 phy->phy->maximum_linkrate = dr->pmax_linkrate;
194 phy->phy->negotiated_linkrate = phy->linkrate; 194 phy->phy->negotiated_linkrate = phy->linkrate;
195 195
196 if (!rediscover) 196 if (!rediscover)
@@ -404,7 +404,8 @@ out:
404#define PC_RESP_SIZE 8 404#define PC_RESP_SIZE 8
405 405
406int sas_smp_phy_control(struct domain_device *dev, int phy_id, 406int sas_smp_phy_control(struct domain_device *dev, int phy_id,
407 enum phy_func phy_func) 407 enum phy_func phy_func,
408 struct sas_phy_linkrates *rates)
408{ 409{
409 u8 *pc_req; 410 u8 *pc_req;
410 u8 *pc_resp; 411 u8 *pc_resp;
@@ -423,6 +424,10 @@ int sas_smp_phy_control(struct domain_device *dev, int phy_id,
423 pc_req[1] = SMP_PHY_CONTROL; 424 pc_req[1] = SMP_PHY_CONTROL;
424 pc_req[9] = phy_id; 425 pc_req[9] = phy_id;
425 pc_req[10]= phy_func; 426 pc_req[10]= phy_func;
427 if (rates) {
428 pc_req[32] = rates->minimum_linkrate << 4;
429 pc_req[33] = rates->maximum_linkrate << 4;
430 }
426 431
427 res = smp_execute_task(dev, pc_req, PC_REQ_SIZE, pc_resp,PC_RESP_SIZE); 432 res = smp_execute_task(dev, pc_req, PC_REQ_SIZE, pc_resp,PC_RESP_SIZE);
428 433
@@ -436,7 +441,7 @@ static void sas_ex_disable_phy(struct domain_device *dev, int phy_id)
436 struct expander_device *ex = &dev->ex_dev; 441 struct expander_device *ex = &dev->ex_dev;
437 struct ex_phy *phy = &ex->ex_phy[phy_id]; 442 struct ex_phy *phy = &ex->ex_phy[phy_id];
438 443
439 sas_smp_phy_control(dev, phy_id, PHY_FUNC_DISABLE); 444 sas_smp_phy_control(dev, phy_id, PHY_FUNC_DISABLE, NULL);
440 phy->linkrate = SAS_PHY_DISABLED; 445 phy->linkrate = SAS_PHY_DISABLED;
441} 446}
442 447
@@ -731,7 +736,7 @@ static int sas_ex_discover_dev(struct domain_device *dev, int phy_id)
731 736
732 /* Phy state */ 737 /* Phy state */
733 if (ex_phy->linkrate == SAS_SATA_SPINUP_HOLD) { 738 if (ex_phy->linkrate == SAS_SATA_SPINUP_HOLD) {
734 if (!sas_smp_phy_control(dev, phy_id, PHY_FUNC_LINK_RESET)) 739 if (!sas_smp_phy_control(dev, phy_id, PHY_FUNC_LINK_RESET, NULL))
735 res = sas_ex_phy_discover(dev, phy_id); 740 res = sas_ex_phy_discover(dev, phy_id);
736 if (res) 741 if (res)
737 return res; 742 return res;
@@ -1706,6 +1711,7 @@ static int sas_rediscover_dev(struct domain_device *dev, int phy_id)
1706 SAS_ADDR(phy->attached_sas_addr)) { 1711 SAS_ADDR(phy->attached_sas_addr)) {
1707 SAS_DPRINTK("ex %016llx phy 0x%x broadcast flutter\n", 1712 SAS_DPRINTK("ex %016llx phy 0x%x broadcast flutter\n",
1708 SAS_ADDR(dev->sas_addr), phy_id); 1713 SAS_ADDR(dev->sas_addr), phy_id);
1714 sas_ex_phy_discover(dev, phy_id);
1709 } else 1715 } else
1710 res = sas_discover_new(dev, phy_id); 1716 res = sas_discover_new(dev, phy_id);
1711out: 1717out:
diff --git a/drivers/scsi/libsas/sas_init.c b/drivers/scsi/libsas/sas_init.c
index b961664b8106..c836a237fb79 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)
159 struct sas_internal *i = 159 struct sas_internal *i =
160 to_sas_internal(sas_ha->core.shost->transportt); 160 to_sas_internal(sas_ha->core.shost->transportt);
161 161
162 ret = i->dft->lldd_control_phy(asd_phy, reset_type); 162 ret = i->dft->lldd_control_phy(asd_phy, reset_type, NULL);
163 } else { 163 } else {
164 struct sas_rphy *rphy = dev_to_rphy(phy->dev.parent); 164 struct sas_rphy *rphy = dev_to_rphy(phy->dev.parent);
165 struct domain_device *ddev = sas_find_dev_by_rphy(rphy); 165 struct domain_device *ddev = sas_find_dev_by_rphy(rphy);
166 ret = sas_smp_phy_control(ddev, phy->number, reset_type); 166 ret = sas_smp_phy_control(ddev, phy->number, reset_type, NULL);
167 } 167 }
168 return ret; 168 return ret;
169} 169}
170 170
171static int sas_set_phy_speed(struct sas_phy *phy,
172 struct sas_phy_linkrates *rates)
173{
174 int ret;
175
176 if ((rates->minimum_linkrate &&
177 rates->minimum_linkrate > phy->maximum_linkrate) ||
178 (rates->maximum_linkrate &&
179 rates->maximum_linkrate < phy->minimum_linkrate))
180 return -EINVAL;
181
182 if (rates->minimum_linkrate &&
183 rates->minimum_linkrate < phy->minimum_linkrate_hw)
184 rates->minimum_linkrate = phy->minimum_linkrate_hw;
185
186 if (rates->maximum_linkrate &&
187 rates->maximum_linkrate > phy->maximum_linkrate_hw)
188 rates->maximum_linkrate = phy->maximum_linkrate_hw;
189
190 if (scsi_is_sas_phy_local(phy)) {
191 struct Scsi_Host *shost = dev_to_shost(phy->dev.parent);
192 struct sas_ha_struct *sas_ha = SHOST_TO_SAS_HA(shost);
193 struct asd_sas_phy *asd_phy = sas_ha->sas_phy[phy->number];
194 struct sas_internal *i =
195 to_sas_internal(sas_ha->core.shost->transportt);
196
197 ret = i->dft->lldd_control_phy(asd_phy, PHY_FUNC_SET_LINK_RATE,
198 rates);
199 } else {
200 struct sas_rphy *rphy = dev_to_rphy(phy->dev.parent);
201 struct domain_device *ddev = sas_find_dev_by_rphy(rphy);
202 ret = sas_smp_phy_control(ddev, phy->number,
203 PHY_FUNC_LINK_RESET, rates);
204
205 }
206
207 return ret;
208}
209
171static struct sas_function_template sft = { 210static struct sas_function_template sft = {
172 .phy_reset = sas_phy_reset, 211 .phy_reset = sas_phy_reset,
212 .set_phy_speed = sas_set_phy_speed,
173 .get_linkerrors = sas_get_linkerrors, 213 .get_linkerrors = sas_get_linkerrors,
174}; 214};
175 215
diff --git a/drivers/scsi/libsas/sas_internal.h b/drivers/scsi/libsas/sas_internal.h
index 0d69ede4b944..bffcee474921 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 *);
70void sas_notify_lldd_dev_gone(struct domain_device *); 70void sas_notify_lldd_dev_gone(struct domain_device *);
71 71
72int sas_smp_phy_control(struct domain_device *dev, int phy_id, 72int sas_smp_phy_control(struct domain_device *dev, int phy_id,
73 enum phy_func phy_func); 73 enum phy_func phy_func, struct sas_phy_linkrates *);
74int sas_smp_get_phy_events(struct sas_phy *phy); 74int sas_smp_get_phy_events(struct sas_phy *phy);
75 75
76struct domain_device *sas_find_dev_by_rphy(struct sas_rphy *rphy); 76struct 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 024ab00e70d2..9340cdbae4a3 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)
67 switch (phy->error) { 67 switch (phy->error) {
68 case 1: 68 case 1:
69 case 2: 69 case 2:
70 i->dft->lldd_control_phy(phy, PHY_FUNC_HARD_RESET); 70 i->dft->lldd_control_phy(phy, PHY_FUNC_HARD_RESET,
71 NULL);
71 break; 72 break;
72 case 3: 73 case 3:
73 default: 74 default:
74 phy->error = 0; 75 phy->error = 0;
75 phy->enabled = 0; 76 phy->enabled = 0;
76 i->dft->lldd_control_phy(phy, PHY_FUNC_DISABLE); 77 i->dft->lldd_control_phy(phy, PHY_FUNC_DISABLE, NULL);
77 break; 78 break;
78 } 79 }
79 } 80 }
@@ -90,7 +91,7 @@ static void sas_phye_spinup_hold(void *data)
90 &phy->phy_events_pending); 91 &phy->phy_events_pending);
91 92
92 phy->error = 0; 93 phy->error = 0;
93 i->dft->lldd_control_phy(phy, PHY_FUNC_RELEASE_SPINUP_HOLD); 94 i->dft->lldd_control_phy(phy, PHY_FUNC_RELEASE_SPINUP_HOLD, NULL);
94} 95}
95 96
96/* ---------- Phy class registration ---------- */ 97/* ---------- Phy class registration ---------- */
@@ -144,10 +145,10 @@ int sas_register_phys(struct sas_ha_struct *sas_ha)
144 phy->phy->identify.target_port_protocols = phy->tproto; 145 phy->phy->identify.target_port_protocols = phy->tproto;
145 phy->phy->identify.sas_address = SAS_ADDR(sas_ha->sas_addr); 146 phy->phy->identify.sas_address = SAS_ADDR(sas_ha->sas_addr);
146 phy->phy->identify.phy_identifier = i; 147 phy->phy->identify.phy_identifier = i;
147 phy->phy->minimum_linkrate_hw = SAS_LINK_RATE_1_5_GBPS; 148 phy->phy->minimum_linkrate_hw = SAS_LINK_RATE_UNKNOWN;
148 phy->phy->maximum_linkrate_hw = SAS_LINK_RATE_3_0_GBPS; 149 phy->phy->maximum_linkrate_hw = SAS_LINK_RATE_UNKNOWN;
149 phy->phy->minimum_linkrate = SAS_LINK_RATE_1_5_GBPS; 150 phy->phy->minimum_linkrate = SAS_LINK_RATE_UNKNOWN;
150 phy->phy->maximum_linkrate = SAS_LINK_RATE_3_0_GBPS; 151 phy->phy->maximum_linkrate = SAS_LINK_RATE_UNKNOWN;
151 phy->phy->negotiated_linkrate = SAS_LINK_RATE_UNKNOWN; 152 phy->phy->negotiated_linkrate = SAS_LINK_RATE_UNKNOWN;
152 153
153 sas_phy_add(phy->phy); 154 sas_phy_add(phy->phy);
diff --git a/include/scsi/libsas.h b/include/scsi/libsas.h
index 8d91313dd888..8e39982fc3db 100644
--- a/include/scsi/libsas.h
+++ b/include/scsi/libsas.h
@@ -586,7 +586,7 @@ struct sas_domain_function_template {
586 int (*lldd_clear_nexus_ha)(struct sas_ha_struct *); 586 int (*lldd_clear_nexus_ha)(struct sas_ha_struct *);
587 587
588 /* Phy management */ 588 /* Phy management */
589 int (*lldd_control_phy)(struct asd_sas_phy *, enum phy_func); 589 int (*lldd_control_phy)(struct asd_sas_phy *, enum phy_func, void *);
590}; 590};
591 591
592extern int sas_register_ha(struct sas_ha_struct *); 592extern int sas_register_ha(struct sas_ha_struct *);
diff --git a/include/scsi/sas.h b/include/scsi/sas.h
index 9c8a5b91ae64..2f4b6afa34fc 100644
--- a/include/scsi/sas.h
+++ b/include/scsi/sas.h
@@ -121,6 +121,7 @@ enum phy_func {
121 PHY_FUNC_CLEAR_AFFIL, 121 PHY_FUNC_CLEAR_AFFIL,
122 PHY_FUNC_TX_SATA_PS_SIGNAL, 122 PHY_FUNC_TX_SATA_PS_SIGNAL,
123 PHY_FUNC_RELEASE_SPINUP_HOLD = 0x10, /* LOCAL PORT ONLY! */ 123 PHY_FUNC_RELEASE_SPINUP_HOLD = 0x10, /* LOCAL PORT ONLY! */
124 PHY_FUNC_SET_LINK_RATE,
124}; 125};
125 126
126/* SAS LLDD would need to report only _very_few_ of those, like BROADCAST. 127/* SAS LLDD would need to report only _very_few_ of those, like BROADCAST.