diff options
Diffstat (limited to 'drivers/scsi/aic94xx')
-rw-r--r-- | drivers/scsi/aic94xx/aic94xx.h | 1 | ||||
-rw-r--r-- | drivers/scsi/aic94xx/aic94xx_init.c | 2 | ||||
-rw-r--r-- | drivers/scsi/aic94xx/aic94xx_tmf.c | 58 |
3 files changed, 56 insertions, 5 deletions
diff --git a/drivers/scsi/aic94xx/aic94xx.h b/drivers/scsi/aic94xx/aic94xx.h index 32f513b1b78a..eb8efdcefe48 100644 --- a/drivers/scsi/aic94xx/aic94xx.h +++ b/drivers/scsi/aic94xx/aic94xx.h | |||
@@ -102,6 +102,7 @@ int asd_abort_task_set(struct domain_device *, u8 *lun); | |||
102 | int asd_clear_aca(struct domain_device *, u8 *lun); | 102 | int asd_clear_aca(struct domain_device *, u8 *lun); |
103 | int asd_clear_task_set(struct domain_device *, u8 *lun); | 103 | int asd_clear_task_set(struct domain_device *, u8 *lun); |
104 | int asd_lu_reset(struct domain_device *, u8 *lun); | 104 | int asd_lu_reset(struct domain_device *, u8 *lun); |
105 | int asd_I_T_nexus_reset(struct domain_device *dev); | ||
105 | int asd_query_task(struct sas_task *); | 106 | int asd_query_task(struct sas_task *); |
106 | 107 | ||
107 | /* ---------- Adapter and Port management ---------- */ | 108 | /* ---------- Adapter and Port management ---------- */ |
diff --git a/drivers/scsi/aic94xx/aic94xx_init.c b/drivers/scsi/aic94xx/aic94xx_init.c index 5d761eb67442..88d1e731b65e 100644 --- a/drivers/scsi/aic94xx/aic94xx_init.c +++ b/drivers/scsi/aic94xx/aic94xx_init.c | |||
@@ -1003,7 +1003,7 @@ static struct sas_domain_function_template aic94xx_transport_functions = { | |||
1003 | .lldd_abort_task_set = asd_abort_task_set, | 1003 | .lldd_abort_task_set = asd_abort_task_set, |
1004 | .lldd_clear_aca = asd_clear_aca, | 1004 | .lldd_clear_aca = asd_clear_aca, |
1005 | .lldd_clear_task_set = asd_clear_task_set, | 1005 | .lldd_clear_task_set = asd_clear_task_set, |
1006 | .lldd_I_T_nexus_reset = NULL, | 1006 | .lldd_I_T_nexus_reset = asd_I_T_nexus_reset, |
1007 | .lldd_lu_reset = asd_lu_reset, | 1007 | .lldd_lu_reset = asd_lu_reset, |
1008 | .lldd_query_task = asd_query_task, | 1008 | .lldd_query_task = asd_query_task, |
1009 | 1009 | ||
diff --git a/drivers/scsi/aic94xx/aic94xx_tmf.c b/drivers/scsi/aic94xx/aic94xx_tmf.c index 144f5ad20453..d684c7432e63 100644 --- a/drivers/scsi/aic94xx/aic94xx_tmf.c +++ b/drivers/scsi/aic94xx/aic94xx_tmf.c | |||
@@ -140,8 +140,14 @@ int asd_clear_nexus_port(struct asd_sas_port *port) | |||
140 | CLEAR_NEXUS_POST; | 140 | CLEAR_NEXUS_POST; |
141 | } | 141 | } |
142 | 142 | ||
143 | #if 0 | 143 | enum clear_nexus_phase { |
144 | static int asd_clear_nexus_I_T(struct domain_device *dev) | 144 | NEXUS_PHASE_PRE, |
145 | NEXUS_PHASE_POST, | ||
146 | NEXUS_PHASE_RESUME, | ||
147 | }; | ||
148 | |||
149 | static int asd_clear_nexus_I_T(struct domain_device *dev, | ||
150 | enum clear_nexus_phase phase) | ||
145 | { | 151 | { |
146 | struct asd_ha_struct *asd_ha = dev->port->ha->lldd_ha; | 152 | struct asd_ha_struct *asd_ha = dev->port->ha->lldd_ha; |
147 | struct asd_ascb *ascb; | 153 | struct asd_ascb *ascb; |
@@ -150,12 +156,56 @@ static int asd_clear_nexus_I_T(struct domain_device *dev) | |||
150 | 156 | ||
151 | CLEAR_NEXUS_PRE; | 157 | CLEAR_NEXUS_PRE; |
152 | scb->clear_nexus.nexus = NEXUS_I_T; | 158 | scb->clear_nexus.nexus = NEXUS_I_T; |
153 | scb->clear_nexus.flags = SEND_Q | EXEC_Q | NOTINQ; | 159 | switch (phase) { |
160 | case NEXUS_PHASE_PRE: | ||
161 | scb->clear_nexus.flags = EXEC_Q | SUSPEND_TX; | ||
162 | break; | ||
163 | case NEXUS_PHASE_POST: | ||
164 | scb->clear_nexus.flags = SEND_Q | NOTINQ; | ||
165 | break; | ||
166 | case NEXUS_PHASE_RESUME: | ||
167 | scb->clear_nexus.flags = RESUME_TX; | ||
168 | } | ||
154 | scb->clear_nexus.conn_handle = cpu_to_le16((u16)(unsigned long) | 169 | scb->clear_nexus.conn_handle = cpu_to_le16((u16)(unsigned long) |
155 | dev->lldd_dev); | 170 | dev->lldd_dev); |
156 | CLEAR_NEXUS_POST; | 171 | CLEAR_NEXUS_POST; |
157 | } | 172 | } |
158 | #endif | 173 | |
174 | int asd_I_T_nexus_reset(struct domain_device *dev) | ||
175 | { | ||
176 | int res, tmp_res, i; | ||
177 | struct sas_phy *phy = sas_find_local_phy(dev); | ||
178 | /* Standard mandates link reset for ATA (type 0) and | ||
179 | * hard reset for SSP (type 1) */ | ||
180 | int reset_type = (dev->dev_type == SATA_DEV || | ||
181 | (dev->tproto & SAS_PROTOCOL_STP)) ? 0 : 1; | ||
182 | |||
183 | asd_clear_nexus_I_T(dev, NEXUS_PHASE_PRE); | ||
184 | /* send a hard reset */ | ||
185 | ASD_DPRINTK("sending %s reset to %s\n", | ||
186 | reset_type ? "hard" : "soft", phy->dev.bus_id); | ||
187 | res = sas_phy_reset(phy, reset_type); | ||
188 | if (res == TMF_RESP_FUNC_COMPLETE) { | ||
189 | /* wait for the maximum settle time */ | ||
190 | msleep(500); | ||
191 | /* clear all outstanding commands (keep nexus suspended) */ | ||
192 | asd_clear_nexus_I_T(dev, NEXUS_PHASE_POST); | ||
193 | } | ||
194 | for (i = 0 ; i < 3; i++) { | ||
195 | tmp_res = asd_clear_nexus_I_T(dev, NEXUS_PHASE_RESUME); | ||
196 | if (tmp_res == TC_RESUME) | ||
197 | return res; | ||
198 | msleep(500); | ||
199 | } | ||
200 | |||
201 | /* This is a bit of a problem: the sequencer is still suspended | ||
202 | * and is refusing to resume. Hope it will resume on a bigger hammer | ||
203 | * or the disk is lost */ | ||
204 | dev_printk(KERN_ERR, &phy->dev, | ||
205 | "Failed to resume nexus after reset 0x%x\n", tmp_res); | ||
206 | |||
207 | return TMF_RESP_FUNC_FAILED; | ||
208 | } | ||
159 | 209 | ||
160 | static int asd_clear_nexus_I_T_L(struct domain_device *dev, u8 *lun) | 210 | static int asd_clear_nexus_I_T_L(struct domain_device *dev, u8 *lun) |
161 | { | 211 | { |