aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/scsi
diff options
context:
space:
mode:
authorJames Bottomley <James.Bottomley@HansenPartnership.com>2008-02-24 00:37:26 -0500
committerJames Bottomley <James.Bottomley@HansenPartnership.com>2008-02-24 00:52:46 -0500
commit63edf49e67cac710826108697c4e8636c89abd17 (patch)
tree86855501b63c9717f2e31d3a7e378cc6a2faa87c /drivers/scsi
parent5319578ca38a8b90b6d0270c194c65d1dd8f7725 (diff)
[SCSI] aic94xx: plumb in I_T_nexus_reset task management function
Currently aic94xx has no exported I_T_nexus_reset function. This is a bit of a huge problem, since sas_ata relies on this function to perform an ATA phy reset and also it means that if abort fails, we really have no bigger hammer to hit everything with. Plumb in the I_T_nexus_reset by quiescing the sequencer, sending the correct phy reset (link for ATA and hard for SAS) and then carefully resuming the sequencer again. Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
Diffstat (limited to 'drivers/scsi')
-rw-r--r--drivers/scsi/aic94xx/aic94xx.h1
-rw-r--r--drivers/scsi/aic94xx/aic94xx_init.c2
-rw-r--r--drivers/scsi/aic94xx/aic94xx_tmf.c58
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);
102int asd_clear_aca(struct domain_device *, u8 *lun); 102int asd_clear_aca(struct domain_device *, u8 *lun);
103int asd_clear_task_set(struct domain_device *, u8 *lun); 103int asd_clear_task_set(struct domain_device *, u8 *lun);
104int asd_lu_reset(struct domain_device *, u8 *lun); 104int asd_lu_reset(struct domain_device *, u8 *lun);
105int asd_I_T_nexus_reset(struct domain_device *dev);
105int asd_query_task(struct sas_task *); 106int 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 143enum clear_nexus_phase {
144static int asd_clear_nexus_I_T(struct domain_device *dev) 144 NEXUS_PHASE_PRE,
145 NEXUS_PHASE_POST,
146 NEXUS_PHASE_RESUME,
147};
148
149static 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
174int 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
160static int asd_clear_nexus_I_T_L(struct domain_device *dev, u8 *lun) 210static int asd_clear_nexus_I_T_L(struct domain_device *dev, u8 *lun)
161{ 211{