diff options
-rw-r--r-- | drivers/scsi/aic94xx/aic94xx.h | 2 | ||||
-rw-r--r-- | drivers/scsi/aic94xx/aic94xx_scb.c | 30 | ||||
-rw-r--r-- | drivers/scsi/libsas/sas_expander.c | 20 | ||||
-rw-r--r-- | drivers/scsi/libsas/sas_init.c | 44 | ||||
-rw-r--r-- | drivers/scsi/libsas/sas_internal.h | 2 | ||||
-rw-r--r-- | drivers/scsi/libsas/sas_phy.c | 15 | ||||
-rw-r--r-- | include/scsi/libsas.h | 2 | ||||
-rw-r--r-- | include/scsi/sas.h | 1 |
8 files changed, 95 insertions, 21 deletions
diff --git a/drivers/scsi/aic94xx/aic94xx.h b/drivers/scsi/aic94xx/aic94xx.h index cb7caf1c9ce..1bd5b4ecf3d 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); | |||
109 | int asd_clear_nexus_ha(struct sas_ha_struct *sas_ha); | 109 | int asd_clear_nexus_ha(struct sas_ha_struct *sas_ha); |
110 | 110 | ||
111 | /* ---------- Phy Management ---------- */ | 111 | /* ---------- Phy Management ---------- */ |
112 | int asd_control_phy(struct asd_sas_phy *phy, enum phy_func func); | 112 | int 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 ef8ca08b545..7ee49b51b72 100644 --- a/drivers/scsi/aic94xx/aic94xx_scb.c +++ b/drivers/scsi/aic94xx/aic94xx_scb.c | |||
@@ -52,6 +52,8 @@ | |||
52 | 52 | ||
53 | static inline void get_lrate_mode(struct asd_phy *phy, u8 oob_mode) | 53 | static 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 | ||
713 | int asd_control_phy(struct asd_sas_phy *phy, enum phy_func func) | 721 | int 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 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, | |||
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 | ||
406 | int sas_smp_phy_control(struct domain_device *dev, int phy_id, | 406 | int 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); |
1711 | out: | 1717 | 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) | |||
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 | ||
171 | static 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 | |||
171 | static struct sas_function_template sft = { | 210 | static 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 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 *); | |||
70 | void sas_notify_lldd_dev_gone(struct domain_device *); | 70 | void sas_notify_lldd_dev_gone(struct domain_device *); |
71 | 71 | ||
72 | int sas_smp_phy_control(struct domain_device *dev, int phy_id, | 72 | int 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 *); |
74 | int sas_smp_get_phy_events(struct sas_phy *phy); | 74 | int sas_smp_get_phy_events(struct sas_phy *phy); |
75 | 75 | ||
76 | struct domain_device *sas_find_dev_by_rphy(struct sas_rphy *rphy); | 76 | 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) | |||
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 8d91313dd88..8e39982fc3d 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 | ||
592 | extern int sas_register_ha(struct sas_ha_struct *); | 592 | extern int sas_register_ha(struct sas_ha_struct *); |
diff --git a/include/scsi/sas.h b/include/scsi/sas.h index 9c8a5b91ae6..2f4b6afa34f 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. |