blob: 93a16fa3c4baeab822056108f9e622e906b10d5c [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * linux/drivers/message/fusion/mptscsih.c
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04003 * For use with LSI Logic PCI chip/adapter(s)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004 * running LSI Logic Fusion MPT (Message Passing Technology) firmware.
5 *
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04006 * Copyright (c) 1999-2005 LSI Logic Corporation
Linus Torvalds1da177e2005-04-16 15:20:36 -07007 * (mailto:mpt_linux_developer@lsil.com)
8 *
Linus Torvalds1da177e2005-04-16 15:20:36 -07009 */
10/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
11/*
12 This program is free software; you can redistribute it and/or modify
13 it under the terms of the GNU General Public License as published by
14 the Free Software Foundation; version 2 of the License.
15
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License for more details.
20
21 NO WARRANTY
22 THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
23 CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
24 LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
25 MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
26 solely responsible for determining the appropriateness of using and
27 distributing the Program and assumes all risks associated with its
28 exercise of rights under this Agreement, including but not limited to
29 the risks and costs of program errors, damage to or loss of data,
30 programs or equipment, and unavailability or interruption of operations.
31
32 DISCLAIMER OF LIABILITY
33 NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
34 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
35 DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
36 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
37 TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
38 USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
39 HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
40
41 You should have received a copy of the GNU General Public License
42 along with this program; if not, write to the Free Software
43 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
44*/
45/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
46
47#include "linux_compat.h" /* linux-2.6 tweaks */
48#include <linux/module.h>
49#include <linux/kernel.h>
50#include <linux/init.h>
51#include <linux/errno.h>
52#include <linux/kdev_t.h>
53#include <linux/blkdev.h>
54#include <linux/delay.h> /* for mdelay */
55#include <linux/interrupt.h> /* needed for in_interrupt() proto */
56#include <linux/reboot.h> /* notifier code */
57#include <linux/sched.h>
58#include <linux/workqueue.h>
59
60#include <scsi/scsi.h>
61#include <scsi/scsi_cmnd.h>
62#include <scsi/scsi_device.h>
63#include <scsi/scsi_host.h>
64#include <scsi/scsi_tcq.h>
Moore, Eric Deane0fc15b2005-09-15 13:17:14 -060065#include <scsi/scsi_dbg.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070066
67#include "mptbase.h"
68#include "mptscsih.h"
69
70/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
71#define my_NAME "Fusion MPT SCSI Host driver"
72#define my_VERSION MPT_LINUX_VERSION_COMMON
73#define MYNAM "mptscsih"
74
75MODULE_AUTHOR(MODULEAUTHOR);
76MODULE_DESCRIPTION(my_NAME);
77MODULE_LICENSE("GPL");
78
Linus Torvalds1da177e2005-04-16 15:20:36 -070079/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
80
81typedef struct _BIG_SENSE_BUF {
82 u8 data[MPT_SENSE_BUFFER_ALLOC];
83} BIG_SENSE_BUF;
84
85#define MPT_SCANDV_GOOD (0x00000000) /* must be 0 */
86#define MPT_SCANDV_DID_RESET (0x00000001)
87#define MPT_SCANDV_SENSE (0x00000002)
88#define MPT_SCANDV_SOME_ERROR (0x00000004)
89#define MPT_SCANDV_SELECTION_TIMEOUT (0x00000008)
90#define MPT_SCANDV_ISSUE_SENSE (0x00000010)
91#define MPT_SCANDV_FALLBACK (0x00000020)
92
93#define MPT_SCANDV_MAX_RETRIES (10)
94
95#define MPT_ICFLAG_BUF_CAP 0x01 /* ReadBuffer Read Capacity format */
96#define MPT_ICFLAG_ECHO 0x02 /* ReadBuffer Echo buffer format */
Moore, Eric Dean466544d2005-09-14 18:09:10 -060097#define MPT_ICFLAG_EBOS 0x04 /* ReadBuffer Echo buffer has EBOS */
98#define MPT_ICFLAG_PHYS_DISK 0x08 /* Any SCSI IO but do Phys Disk Format */
99#define MPT_ICFLAG_TAGGED_CMD 0x10 /* Do tagged IO */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700100#define MPT_ICFLAG_DID_RESET 0x20 /* Bus Reset occurred with this command */
101#define MPT_ICFLAG_RESERVED 0x40 /* Reserved has been issued */
102
103typedef struct _internal_cmd {
104 char *data; /* data pointer */
105 dma_addr_t data_dma; /* data dma address */
106 int size; /* transfer size */
107 u8 cmd; /* SCSI Op Code */
108 u8 bus; /* bus number */
109 u8 id; /* SCSI ID (virtual) */
110 u8 lun;
111 u8 flags; /* Bit Field - See above */
112 u8 physDiskNum; /* Phys disk number, -1 else */
113 u8 rsvd2;
114 u8 rsvd;
115} INTERNAL_CMD;
116
117typedef struct _negoparms {
118 u8 width;
119 u8 offset;
120 u8 factor;
121 u8 flags;
122} NEGOPARMS;
123
124typedef struct _dv_parameters {
125 NEGOPARMS max;
126 NEGOPARMS now;
127 u8 cmd;
128 u8 id;
129 u16 pad1;
130} DVPARAMETERS;
131
Linus Torvalds1da177e2005-04-16 15:20:36 -0700132/*
133 * Other private/forward protos...
134 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -0400135int mptscsih_io_done(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *r);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700136static void mptscsih_report_queue_full(struct scsi_cmnd *sc, SCSIIOReply_t *pScsiReply, SCSIIORequest_t *pScsiReq);
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -0400137int mptscsih_taskmgmt_complete(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *r);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700138
139static int mptscsih_AddSGE(MPT_ADAPTER *ioc, struct scsi_cmnd *SCpnt,
140 SCSIIORequest_t *pReq, int req_idx);
141static void mptscsih_freeChainBuffers(MPT_ADAPTER *ioc, int req_idx);
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -0400142static void mptscsih_copy_sense_data(struct scsi_cmnd *sc, MPT_SCSI_HOST *hd, MPT_FRAME_HDR *mf, SCSIIOReply_t *pScsiReply);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700143static int mptscsih_tm_pending_wait(MPT_SCSI_HOST * hd);
144static int mptscsih_tm_wait_for_completion(MPT_SCSI_HOST * hd, ulong timeout );
145static u32 SCPNT_TO_LOOKUP_IDX(struct scsi_cmnd *sc);
146
147static int mptscsih_TMHandler(MPT_SCSI_HOST *hd, u8 type, u8 channel, u8 target, u8 lun, int ctx2abort, ulong timeout);
148static int mptscsih_IssueTaskMgmt(MPT_SCSI_HOST *hd, u8 type, u8 channel, u8 target, u8 lun, int ctx2abort, ulong timeout);
149
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -0400150int mptscsih_ioc_reset(MPT_ADAPTER *ioc, int post_reset);
151int mptscsih_event_process(MPT_ADAPTER *ioc, EventNotificationReply_t *pEvReply);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700152
Moore, Eric Deanc7c82982005-11-16 18:54:25 -0700153static void mptscsih_initTarget(MPT_SCSI_HOST *hd, VirtTarget *vtarget, u8 lun, char *data, int dlen);
154static void mptscsih_setTargetNegoParms(MPT_SCSI_HOST *hd, VirtTarget *vtarget, char byte56);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700155static void mptscsih_setDevicePage1Flags (u8 width, u8 factor, u8 offset, int *requestedPtr, int *configurationPtr, u8 flags);
Moore, Eric Deanc7c82982005-11-16 18:54:25 -0700156static void mptscsih_no_negotiate(MPT_SCSI_HOST *hd, struct scsi_cmnd *sc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700157static int mptscsih_writeSDP1(MPT_SCSI_HOST *hd, int portnum, int target, int flags);
158static int mptscsih_writeIOCPage4(MPT_SCSI_HOST *hd, int target_id, int bus);
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -0400159int mptscsih_scandv_complete(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *r);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700160static int mptscsih_do_cmd(MPT_SCSI_HOST *hd, INTERNAL_CMD *iocmd);
Moore, Eric Deanc7c82982005-11-16 18:54:25 -0700161static void mptscsih_synchronize_cache(MPT_SCSI_HOST *hd, VirtDevice *vdevice);
162static void mptscsih_negotiate_to_asyn_narrow(MPT_SCSI_HOST *hd, VirtTarget *vtarget);
Moore, Eric Deanf2ea8672005-11-16 18:54:23 -0700163static int mptscsih_is_phys_disk(MPT_ADAPTER *ioc, int id);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700164
Moore, Eric Dean466544d2005-09-14 18:09:10 -0600165static struct work_struct mptscsih_persistTask;
166
Linus Torvalds1da177e2005-04-16 15:20:36 -0700167#ifdef MPTSCSIH_ENABLE_DOMAIN_VALIDATION
168static int mptscsih_do_raid(MPT_SCSI_HOST *hd, u8 action, INTERNAL_CMD *io);
169static void mptscsih_domainValidation(void *hd);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700170static void mptscsih_qas_check(MPT_SCSI_HOST *hd, int id);
171static int mptscsih_doDv(MPT_SCSI_HOST *hd, int channel, int target);
172static void mptscsih_dv_parms(MPT_SCSI_HOST *hd, DVPARAMETERS *dv,void *pPage);
173static void mptscsih_fillbuf(char *buffer, int size, int index, int width);
Moore, Eric Dean466544d2005-09-14 18:09:10 -0600174static void mptscsih_set_dvflags_raid(MPT_SCSI_HOST *hd, int id);
Moore, Eric Deanc7c82982005-11-16 18:54:25 -0700175static void mptscsih_set_dvflags(MPT_SCSI_HOST *hd, struct scsi_cmnd *sc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700176#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700177
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -0400178void mptscsih_remove(struct pci_dev *);
Greg Kroah-Hartmand18c3db2005-06-23 17:35:56 -0700179void mptscsih_shutdown(struct pci_dev *);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700180#ifdef CONFIG_PM
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -0400181int mptscsih_suspend(struct pci_dev *pdev, pm_message_t state);
182int mptscsih_resume(struct pci_dev *pdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700183#endif
184
Linus Torvalds1da177e2005-04-16 15:20:36 -0700185#define SNS_LEN(scp) sizeof((scp)->sense_buffer)
186
187#ifdef MPTSCSIH_ENABLE_DOMAIN_VALIDATION
188/*
189 * Domain Validation task structure
190 */
191static DEFINE_SPINLOCK(dvtaskQ_lock);
192static int dvtaskQ_active = 0;
193static int dvtaskQ_release = 0;
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -0400194static struct work_struct dvTaskQ_task;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700195#endif
196
Linus Torvalds1da177e2005-04-16 15:20:36 -0700197/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
198/**
199 * mptscsih_add_sge - Place a simple SGE at address pAddr.
200 * @pAddr: virtual address for SGE
201 * @flagslength: SGE flags and data transfer length
202 * @dma_addr: Physical address
203 *
204 * This routine places a MPT request frame back on the MPT adapter's
205 * FreeQ.
206 */
207static inline void
208mptscsih_add_sge(char *pAddr, u32 flagslength, dma_addr_t dma_addr)
209{
210 if (sizeof(dma_addr_t) == sizeof(u64)) {
211 SGESimple64_t *pSge = (SGESimple64_t *) pAddr;
212 u32 tmp = dma_addr & 0xFFFFFFFF;
213
214 pSge->FlagsLength = cpu_to_le32(flagslength);
215 pSge->Address.Low = cpu_to_le32(tmp);
216 tmp = (u32) ((u64)dma_addr >> 32);
217 pSge->Address.High = cpu_to_le32(tmp);
218
219 } else {
220 SGESimple32_t *pSge = (SGESimple32_t *) pAddr;
221 pSge->FlagsLength = cpu_to_le32(flagslength);
222 pSge->Address = cpu_to_le32(dma_addr);
223 }
224} /* mptscsih_add_sge() */
225
226/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
227/**
228 * mptscsih_add_chain - Place a chain SGE at address pAddr.
229 * @pAddr: virtual address for SGE
230 * @next: nextChainOffset value (u32's)
231 * @length: length of next SGL segment
232 * @dma_addr: Physical address
233 *
234 * This routine places a MPT request frame back on the MPT adapter's
235 * FreeQ.
236 */
237static inline void
238mptscsih_add_chain(char *pAddr, u8 next, u16 length, dma_addr_t dma_addr)
239{
240 if (sizeof(dma_addr_t) == sizeof(u64)) {
241 SGEChain64_t *pChain = (SGEChain64_t *) pAddr;
242 u32 tmp = dma_addr & 0xFFFFFFFF;
243
244 pChain->Length = cpu_to_le16(length);
245 pChain->Flags = MPI_SGE_FLAGS_CHAIN_ELEMENT | mpt_addr_size();
246
247 pChain->NextChainOffset = next;
248
249 pChain->Address.Low = cpu_to_le32(tmp);
250 tmp = (u32) ((u64)dma_addr >> 32);
251 pChain->Address.High = cpu_to_le32(tmp);
252 } else {
253 SGEChain32_t *pChain = (SGEChain32_t *) pAddr;
254 pChain->Length = cpu_to_le16(length);
255 pChain->Flags = MPI_SGE_FLAGS_CHAIN_ELEMENT | mpt_addr_size();
256 pChain->NextChainOffset = next;
257 pChain->Address = cpu_to_le32(dma_addr);
258 }
259} /* mptscsih_add_chain() */
260
261/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
262/*
263 * mptscsih_getFreeChainBuffer - Function to get a free chain
264 * from the MPT_SCSI_HOST FreeChainQ.
265 * @ioc: Pointer to MPT_ADAPTER structure
266 * @req_idx: Index of the SCSI IO request frame. (output)
267 *
268 * return SUCCESS or FAILED
269 */
270static inline int
271mptscsih_getFreeChainBuffer(MPT_ADAPTER *ioc, int *retIndex)
272{
273 MPT_FRAME_HDR *chainBuf;
274 unsigned long flags;
275 int rc;
276 int chain_idx;
277
278 dsgprintk((MYIOC_s_INFO_FMT "getFreeChainBuffer called\n",
279 ioc->name));
280 spin_lock_irqsave(&ioc->FreeQlock, flags);
281 if (!list_empty(&ioc->FreeChainQ)) {
282 int offset;
283
284 chainBuf = list_entry(ioc->FreeChainQ.next, MPT_FRAME_HDR,
285 u.frame.linkage.list);
286 list_del(&chainBuf->u.frame.linkage.list);
287 offset = (u8 *)chainBuf - (u8 *)ioc->ChainBuffer;
288 chain_idx = offset / ioc->req_sz;
289 rc = SUCCESS;
Christoph Hellwigc6678e02005-08-18 16:24:53 +0200290 dsgprintk((MYIOC_s_ERR_FMT "getFreeChainBuffer chainBuf=%p ChainBuffer=%p offset=%d chain_idx=%d\n",
291 ioc->name, chainBuf, ioc->ChainBuffer, offset, chain_idx));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700292 } else {
293 rc = FAILED;
294 chain_idx = MPT_HOST_NO_CHAIN;
Christoph Hellwigc6678e02005-08-18 16:24:53 +0200295 dfailprintk((MYIOC_s_INFO_FMT "getFreeChainBuffer failed\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700296 ioc->name));
297 }
298 spin_unlock_irqrestore(&ioc->FreeQlock, flags);
299
300 *retIndex = chain_idx;
301 return rc;
302} /* mptscsih_getFreeChainBuffer() */
303
304/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
305/*
306 * mptscsih_AddSGE - Add a SGE (plus chain buffers) to the
307 * SCSIIORequest_t Message Frame.
308 * @ioc: Pointer to MPT_ADAPTER structure
309 * @SCpnt: Pointer to scsi_cmnd structure
310 * @pReq: Pointer to SCSIIORequest_t structure
311 *
312 * Returns ...
313 */
314static int
315mptscsih_AddSGE(MPT_ADAPTER *ioc, struct scsi_cmnd *SCpnt,
316 SCSIIORequest_t *pReq, int req_idx)
317{
318 char *psge;
319 char *chainSge;
320 struct scatterlist *sg;
321 int frm_sz;
322 int sges_left, sg_done;
323 int chain_idx = MPT_HOST_NO_CHAIN;
324 int sgeOffset;
325 int numSgeSlots, numSgeThisFrame;
326 u32 sgflags, sgdir, thisxfer = 0;
327 int chain_dma_off = 0;
328 int newIndex;
329 int ii;
330 dma_addr_t v2;
331 u32 RequestNB;
332
333 sgdir = le32_to_cpu(pReq->Control) & MPI_SCSIIO_CONTROL_DATADIRECTION_MASK;
334 if (sgdir == MPI_SCSIIO_CONTROL_WRITE) {
335 sgdir = MPT_TRANSFER_HOST_TO_IOC;
336 } else {
337 sgdir = MPT_TRANSFER_IOC_TO_HOST;
338 }
339
340 psge = (char *) &pReq->SGL;
341 frm_sz = ioc->req_sz;
342
343 /* Map the data portion, if any.
344 * sges_left = 0 if no data transfer.
345 */
346 if ( (sges_left = SCpnt->use_sg) ) {
347 sges_left = pci_map_sg(ioc->pcidev,
348 (struct scatterlist *) SCpnt->request_buffer,
349 SCpnt->use_sg,
350 SCpnt->sc_data_direction);
351 if (sges_left == 0)
352 return FAILED;
353 } else if (SCpnt->request_bufflen) {
354 SCpnt->SCp.dma_handle = pci_map_single(ioc->pcidev,
355 SCpnt->request_buffer,
356 SCpnt->request_bufflen,
357 SCpnt->sc_data_direction);
358 dsgprintk((MYIOC_s_INFO_FMT "SG: non-SG for %p, len=%d\n",
359 ioc->name, SCpnt, SCpnt->request_bufflen));
360 mptscsih_add_sge((char *) &pReq->SGL,
361 0xD1000000|MPT_SGE_FLAGS_ADDRESSING|sgdir|SCpnt->request_bufflen,
362 SCpnt->SCp.dma_handle);
363
364 return SUCCESS;
365 }
366
367 /* Handle the SG case.
368 */
369 sg = (struct scatterlist *) SCpnt->request_buffer;
370 sg_done = 0;
371 sgeOffset = sizeof(SCSIIORequest_t) - sizeof(SGE_IO_UNION);
372 chainSge = NULL;
373
374 /* Prior to entering this loop - the following must be set
375 * current MF: sgeOffset (bytes)
376 * chainSge (Null if original MF is not a chain buffer)
377 * sg_done (num SGE done for this MF)
378 */
379
380nextSGEset:
381 numSgeSlots = ((frm_sz - sgeOffset) / (sizeof(u32) + sizeof(dma_addr_t)) );
382 numSgeThisFrame = (sges_left < numSgeSlots) ? sges_left : numSgeSlots;
383
384 sgflags = MPT_SGE_FLAGS_SIMPLE_ELEMENT | MPT_SGE_FLAGS_ADDRESSING | sgdir;
385
386 /* Get first (num - 1) SG elements
387 * Skip any SG entries with a length of 0
388 * NOTE: at finish, sg and psge pointed to NEXT data/location positions
389 */
390 for (ii=0; ii < (numSgeThisFrame-1); ii++) {
391 thisxfer = sg_dma_len(sg);
392 if (thisxfer == 0) {
393 sg ++; /* Get next SG element from the OS */
394 sg_done++;
395 continue;
396 }
397
398 v2 = sg_dma_address(sg);
399 mptscsih_add_sge(psge, sgflags | thisxfer, v2);
400
401 sg++; /* Get next SG element from the OS */
402 psge += (sizeof(u32) + sizeof(dma_addr_t));
403 sgeOffset += (sizeof(u32) + sizeof(dma_addr_t));
404 sg_done++;
405 }
406
407 if (numSgeThisFrame == sges_left) {
408 /* Add last element, end of buffer and end of list flags.
409 */
410 sgflags |= MPT_SGE_FLAGS_LAST_ELEMENT |
411 MPT_SGE_FLAGS_END_OF_BUFFER |
412 MPT_SGE_FLAGS_END_OF_LIST;
413
414 /* Add last SGE and set termination flags.
415 * Note: Last SGE may have a length of 0 - which should be ok.
416 */
417 thisxfer = sg_dma_len(sg);
418
419 v2 = sg_dma_address(sg);
420 mptscsih_add_sge(psge, sgflags | thisxfer, v2);
421 /*
422 sg++;
423 psge += (sizeof(u32) + sizeof(dma_addr_t));
424 */
425 sgeOffset += (sizeof(u32) + sizeof(dma_addr_t));
426 sg_done++;
427
428 if (chainSge) {
429 /* The current buffer is a chain buffer,
430 * but there is not another one.
431 * Update the chain element
432 * Offset and Length fields.
433 */
434 mptscsih_add_chain((char *)chainSge, 0, sgeOffset, ioc->ChainBufferDMA + chain_dma_off);
435 } else {
436 /* The current buffer is the original MF
437 * and there is no Chain buffer.
438 */
439 pReq->ChainOffset = 0;
440 RequestNB = (((sgeOffset - 1) >> ioc->NBShiftFactor) + 1) & 0x03;
Christoph Hellwigc6678e02005-08-18 16:24:53 +0200441 dsgprintk((MYIOC_s_INFO_FMT
Linus Torvalds1da177e2005-04-16 15:20:36 -0700442 "Single Buffer RequestNB=%x, sgeOffset=%d\n", ioc->name, RequestNB, sgeOffset));
443 ioc->RequestNB[req_idx] = RequestNB;
444 }
445 } else {
446 /* At least one chain buffer is needed.
447 * Complete the first MF
448 * - last SGE element, set the LastElement bit
449 * - set ChainOffset (words) for orig MF
450 * (OR finish previous MF chain buffer)
451 * - update MFStructPtr ChainIndex
452 * - Populate chain element
453 * Also
454 * Loop until done.
455 */
456
457 dsgprintk((MYIOC_s_INFO_FMT "SG: Chain Required! sg done %d\n",
458 ioc->name, sg_done));
459
460 /* Set LAST_ELEMENT flag for last non-chain element
461 * in the buffer. Since psge points at the NEXT
462 * SGE element, go back one SGE element, update the flags
463 * and reset the pointer. (Note: sgflags & thisxfer are already
464 * set properly).
465 */
466 if (sg_done) {
467 u32 *ptmp = (u32 *) (psge - (sizeof(u32) + sizeof(dma_addr_t)));
468 sgflags = le32_to_cpu(*ptmp);
469 sgflags |= MPT_SGE_FLAGS_LAST_ELEMENT;
470 *ptmp = cpu_to_le32(sgflags);
471 }
472
473 if (chainSge) {
474 /* The current buffer is a chain buffer.
475 * chainSge points to the previous Chain Element.
476 * Update its chain element Offset and Length (must
477 * include chain element size) fields.
478 * Old chain element is now complete.
479 */
480 u8 nextChain = (u8) (sgeOffset >> 2);
481 sgeOffset += (sizeof(u32) + sizeof(dma_addr_t));
482 mptscsih_add_chain((char *)chainSge, nextChain, sgeOffset, ioc->ChainBufferDMA + chain_dma_off);
483 } else {
484 /* The original MF buffer requires a chain buffer -
485 * set the offset.
486 * Last element in this MF is a chain element.
487 */
488 pReq->ChainOffset = (u8) (sgeOffset >> 2);
489 RequestNB = (((sgeOffset - 1) >> ioc->NBShiftFactor) + 1) & 0x03;
490 dsgprintk((MYIOC_s_ERR_FMT "Chain Buffer Needed, RequestNB=%x sgeOffset=%d\n", ioc->name, RequestNB, sgeOffset));
491 ioc->RequestNB[req_idx] = RequestNB;
492 }
493
494 sges_left -= sg_done;
495
496
497 /* NOTE: psge points to the beginning of the chain element
498 * in current buffer. Get a chain buffer.
499 */
Christoph Hellwigc6678e02005-08-18 16:24:53 +0200500 if ((mptscsih_getFreeChainBuffer(ioc, &newIndex)) == FAILED) {
501 dfailprintk((MYIOC_s_INFO_FMT
502 "getFreeChainBuffer FAILED SCSI cmd=%02x (%p)\n",
503 ioc->name, pReq->CDB[0], SCpnt));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700504 return FAILED;
Christoph Hellwigc6678e02005-08-18 16:24:53 +0200505 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700506
507 /* Update the tracking arrays.
508 * If chainSge == NULL, update ReqToChain, else ChainToChain
509 */
510 if (chainSge) {
511 ioc->ChainToChain[chain_idx] = newIndex;
512 } else {
513 ioc->ReqToChain[req_idx] = newIndex;
514 }
515 chain_idx = newIndex;
516 chain_dma_off = ioc->req_sz * chain_idx;
517
518 /* Populate the chainSGE for the current buffer.
519 * - Set chain buffer pointer to psge and fill
520 * out the Address and Flags fields.
521 */
522 chainSge = (char *) psge;
523 dsgprintk((KERN_INFO " Current buff @ %p (index 0x%x)",
524 psge, req_idx));
525
526 /* Start the SGE for the next buffer
527 */
528 psge = (char *) (ioc->ChainBuffer + chain_dma_off);
529 sgeOffset = 0;
530 sg_done = 0;
531
532 dsgprintk((KERN_INFO " Chain buff @ %p (index 0x%x)\n",
533 psge, chain_idx));
534
535 /* Start the SGE for the next buffer
536 */
537
538 goto nextSGEset;
539 }
540
541 return SUCCESS;
542} /* mptscsih_AddSGE() */
543
544/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
545/*
546 * mptscsih_io_done - Main SCSI IO callback routine registered to
547 * Fusion MPT (base) driver
548 * @ioc: Pointer to MPT_ADAPTER structure
549 * @mf: Pointer to original MPT request frame
550 * @r: Pointer to MPT reply frame (NULL if TurboReply)
551 *
552 * This routine is called from mpt.c::mpt_interrupt() at the completion
553 * of any SCSI IO request.
554 * This routine is registered with the Fusion MPT (base) driver at driver
555 * load/init time via the mpt_register() API call.
556 *
557 * Returns 1 indicating alloc'd request frame ptr should be freed.
558 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -0400559int
Linus Torvalds1da177e2005-04-16 15:20:36 -0700560mptscsih_io_done(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *mr)
561{
562 struct scsi_cmnd *sc;
563 MPT_SCSI_HOST *hd;
564 SCSIIORequest_t *pScsiReq;
565 SCSIIOReply_t *pScsiReply;
566 u16 req_idx;
567
568 hd = (MPT_SCSI_HOST *) ioc->sh->hostdata;
569
570 req_idx = le16_to_cpu(mf->u.frame.hwhdr.msgctxu.fld.req_idx);
571 sc = hd->ScsiLookup[req_idx];
572 if (sc == NULL) {
573 MPIHeader_t *hdr = (MPIHeader_t *)mf;
574
575 /* Remark: writeSDP1 will use the ScsiDoneCtx
576 * If a SCSI I/O cmd, device disabled by OS and
577 * completion done. Cannot touch sc struct. Just free mem.
578 */
579 if (hdr->Function == MPI_FUNCTION_SCSI_IO_REQUEST)
580 printk(MYIOC_s_ERR_FMT "NULL ScsiCmd ptr!\n",
581 ioc->name);
582
583 mptscsih_freeChainBuffers(ioc, req_idx);
584 return 1;
585 }
586
Linus Torvalds1da177e2005-04-16 15:20:36 -0700587 sc->result = DID_OK << 16; /* Set default reply as OK */
588 pScsiReq = (SCSIIORequest_t *) mf;
589 pScsiReply = (SCSIIOReply_t *) mr;
590
Christoph Hellwigc6678e02005-08-18 16:24:53 +0200591 if((ioc->facts.MsgVersion >= MPI_VERSION_01_05) && pScsiReply){
592 dmfprintk((MYIOC_s_INFO_FMT
593 "ScsiDone (mf=%p,mr=%p,sc=%p,idx=%d,task-tag=%d)\n",
594 ioc->name, mf, mr, sc, req_idx, pScsiReply->TaskTag));
595 }else{
596 dmfprintk((MYIOC_s_INFO_FMT
597 "ScsiDone (mf=%p,mr=%p,sc=%p,idx=%d)\n",
598 ioc->name, mf, mr, sc, req_idx));
599 }
600
Linus Torvalds1da177e2005-04-16 15:20:36 -0700601 if (pScsiReply == NULL) {
602 /* special context reply handling */
603 ;
604 } else {
605 u32 xfer_cnt;
606 u16 status;
607 u8 scsi_state, scsi_status;
608
609 status = le16_to_cpu(pScsiReply->IOCStatus) & MPI_IOCSTATUS_MASK;
610 scsi_state = pScsiReply->SCSIState;
611 scsi_status = pScsiReply->SCSIStatus;
612 xfer_cnt = le32_to_cpu(pScsiReply->TransferCount);
613 sc->resid = sc->request_bufflen - xfer_cnt;
614
Moore, Eric Dean466544d2005-09-14 18:09:10 -0600615 /*
616 * if we get a data underrun indication, yet no data was
617 * transferred and the SCSI status indicates that the
618 * command was never started, change the data underrun
619 * to success
620 */
621 if (status == MPI_IOCSTATUS_SCSI_DATA_UNDERRUN && xfer_cnt == 0 &&
622 (scsi_status == MPI_SCSI_STATUS_BUSY ||
623 scsi_status == MPI_SCSI_STATUS_RESERVATION_CONFLICT ||
624 scsi_status == MPI_SCSI_STATUS_TASK_SET_FULL)) {
625 status = MPI_IOCSTATUS_SUCCESS;
626 }
627
Linus Torvalds1da177e2005-04-16 15:20:36 -0700628 dreplyprintk((KERN_NOTICE "Reply ha=%d id=%d lun=%d:\n"
629 "IOCStatus=%04xh SCSIState=%02xh SCSIStatus=%02xh\n"
630 "resid=%d bufflen=%d xfer_cnt=%d\n",
Moore, Eric Deanc7c82982005-11-16 18:54:25 -0700631 ioc->id, sc->device->id, sc->device->lun,
Moore, Eric Dean466544d2005-09-14 18:09:10 -0600632 status, scsi_state, scsi_status, sc->resid,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700633 sc->request_bufflen, xfer_cnt));
634
635 if (scsi_state & MPI_SCSI_STATE_AUTOSENSE_VALID)
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -0400636 mptscsih_copy_sense_data(sc, hd, mf, pScsiReply);
637
Linus Torvalds1da177e2005-04-16 15:20:36 -0700638 /*
639 * Look for + dump FCP ResponseInfo[]!
640 */
Moore, Eric Dean466544d2005-09-14 18:09:10 -0600641 if (scsi_state & MPI_SCSI_STATE_RESPONSE_INFO_VALID &&
642 pScsiReply->ResponseInfo) {
643 printk(KERN_NOTICE "ha=%d id=%d lun=%d: "
644 "FCP_ResponseInfo=%08xh\n",
Moore, Eric Deanc7c82982005-11-16 18:54:25 -0700645 ioc->id, sc->device->id, sc->device->lun,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700646 le32_to_cpu(pScsiReply->ResponseInfo));
647 }
648
649 switch(status) {
650 case MPI_IOCSTATUS_BUSY: /* 0x0002 */
651 /* CHECKME!
652 * Maybe: DRIVER_BUSY | SUGGEST_RETRY | DID_SOFT_ERROR (retry)
653 * But not: DID_BUS_BUSY lest one risk
654 * killing interrupt handler:-(
655 */
656 sc->result = SAM_STAT_BUSY;
657 break;
658
659 case MPI_IOCSTATUS_SCSI_INVALID_BUS: /* 0x0041 */
660 case MPI_IOCSTATUS_SCSI_INVALID_TARGETID: /* 0x0042 */
661 sc->result = DID_BAD_TARGET << 16;
662 break;
663
664 case MPI_IOCSTATUS_SCSI_DEVICE_NOT_THERE: /* 0x0043 */
665 /* Spoof to SCSI Selection Timeout! */
666 sc->result = DID_NO_CONNECT << 16;
667
668 if (hd->sel_timeout[pScsiReq->TargetID] < 0xFFFF)
669 hd->sel_timeout[pScsiReq->TargetID]++;
670 break;
671
672 case MPI_IOCSTATUS_SCSI_TASK_TERMINATED: /* 0x0048 */
673 case MPI_IOCSTATUS_SCSI_IOC_TERMINATED: /* 0x004B */
674 case MPI_IOCSTATUS_SCSI_EXT_TERMINATED: /* 0x004C */
675 /* Linux handles an unsolicited DID_RESET better
676 * than an unsolicited DID_ABORT.
677 */
678 sc->result = DID_RESET << 16;
679
680 /* GEM Workaround. */
Moore, Eric Deana9b29372005-11-16 18:54:20 -0700681 if (ioc->bus_type == SPI)
Moore, Eric Deanc7c82982005-11-16 18:54:25 -0700682 mptscsih_no_negotiate(hd, sc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700683 break;
684
685 case MPI_IOCSTATUS_SCSI_RESIDUAL_MISMATCH: /* 0x0049 */
Moore, Eric Dean466544d2005-09-14 18:09:10 -0600686 sc->resid = sc->request_bufflen - xfer_cnt;
687 if((xfer_cnt==0)||(sc->underflow > xfer_cnt))
688 sc->result=DID_SOFT_ERROR << 16;
689 else /* Sufficient data transfer occurred */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700690 sc->result = (DID_OK << 16) | scsi_status;
Moore, Eric Dean466544d2005-09-14 18:09:10 -0600691 dreplyprintk((KERN_NOTICE
692 "RESIDUAL_MISMATCH: result=%x on id=%d\n", sc->result, sc->device->id));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700693 break;
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -0400694
Linus Torvalds1da177e2005-04-16 15:20:36 -0700695 case MPI_IOCSTATUS_SCSI_DATA_UNDERRUN: /* 0x0045 */
696 /*
697 * Do upfront check for valid SenseData and give it
698 * precedence!
699 */
700 sc->result = (DID_OK << 16) | scsi_status;
701 if (scsi_state & MPI_SCSI_STATE_AUTOSENSE_VALID) {
702 /* Have already saved the status and sense data
703 */
704 ;
705 } else {
706 if (xfer_cnt < sc->underflow) {
Moore, Eric Dean466544d2005-09-14 18:09:10 -0600707 if (scsi_status == SAM_STAT_BUSY)
708 sc->result = SAM_STAT_BUSY;
709 else
710 sc->result = DID_SOFT_ERROR << 16;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700711 }
712 if (scsi_state & (MPI_SCSI_STATE_AUTOSENSE_FAILED | MPI_SCSI_STATE_NO_SCSI_STATUS)) {
713 /* What to do?
714 */
715 sc->result = DID_SOFT_ERROR << 16;
716 }
717 else if (scsi_state & MPI_SCSI_STATE_TERMINATED) {
718 /* Not real sure here either... */
719 sc->result = DID_RESET << 16;
720 }
721 }
722
723 dreplyprintk((KERN_NOTICE " sc->underflow={report ERR if < %02xh bytes xfer'd}\n",
724 sc->underflow));
725 dreplyprintk((KERN_NOTICE " ActBytesXferd=%02xh\n", xfer_cnt));
726 /* Report Queue Full
727 */
728 if (scsi_status == MPI_SCSI_STATUS_TASK_SET_FULL)
729 mptscsih_report_queue_full(sc, pScsiReply, pScsiReq);
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -0400730
Linus Torvalds1da177e2005-04-16 15:20:36 -0700731 break;
732
733 case MPI_IOCSTATUS_SCSI_RECOVERED_ERROR: /* 0x0040 */
734 case MPI_IOCSTATUS_SUCCESS: /* 0x0000 */
Moore, Eric Dean466544d2005-09-14 18:09:10 -0600735 if (scsi_status == MPI_SCSI_STATUS_BUSY)
736 sc->result = (DID_BUS_BUSY << 16) | scsi_status;
737 else
738 sc->result = (DID_OK << 16) | scsi_status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700739 if (scsi_state == 0) {
740 ;
741 } else if (scsi_state & MPI_SCSI_STATE_AUTOSENSE_VALID) {
742 /*
743 * If running against circa 200003dd 909 MPT f/w,
744 * may get this (AUTOSENSE_VALID) for actual TASK_SET_FULL
745 * (QUEUE_FULL) returned from device! --> get 0x0000?128
746 * and with SenseBytes set to 0.
747 */
748 if (pScsiReply->SCSIStatus == MPI_SCSI_STATUS_TASK_SET_FULL)
749 mptscsih_report_queue_full(sc, pScsiReply, pScsiReq);
750
751 }
752 else if (scsi_state &
753 (MPI_SCSI_STATE_AUTOSENSE_FAILED | MPI_SCSI_STATE_NO_SCSI_STATUS)
754 ) {
755 /*
756 * What to do?
757 */
758 sc->result = DID_SOFT_ERROR << 16;
759 }
760 else if (scsi_state & MPI_SCSI_STATE_TERMINATED) {
761 /* Not real sure here either... */
762 sc->result = DID_RESET << 16;
763 }
764 else if (scsi_state & MPI_SCSI_STATE_QUEUE_TAG_REJECTED) {
765 /* Device Inq. data indicates that it supports
766 * QTags, but rejects QTag messages.
767 * This command completed OK.
768 *
769 * Not real sure here either so do nothing... */
770 }
771
772 if (sc->result == MPI_SCSI_STATUS_TASK_SET_FULL)
773 mptscsih_report_queue_full(sc, pScsiReply, pScsiReq);
774
775 /* Add handling of:
776 * Reservation Conflict, Busy,
777 * Command Terminated, CHECK
778 */
779 break;
780
781 case MPI_IOCSTATUS_SCSI_PROTOCOL_ERROR: /* 0x0047 */
782 sc->result = DID_SOFT_ERROR << 16;
783 break;
784
785 case MPI_IOCSTATUS_INVALID_FUNCTION: /* 0x0001 */
786 case MPI_IOCSTATUS_INVALID_SGL: /* 0x0003 */
787 case MPI_IOCSTATUS_INTERNAL_ERROR: /* 0x0004 */
788 case MPI_IOCSTATUS_RESERVED: /* 0x0005 */
789 case MPI_IOCSTATUS_INSUFFICIENT_RESOURCES: /* 0x0006 */
790 case MPI_IOCSTATUS_INVALID_FIELD: /* 0x0007 */
791 case MPI_IOCSTATUS_INVALID_STATE: /* 0x0008 */
792 case MPI_IOCSTATUS_SCSI_DATA_OVERRUN: /* 0x0044 */
793 case MPI_IOCSTATUS_SCSI_IO_DATA_ERROR: /* 0x0046 */
794 case MPI_IOCSTATUS_SCSI_TASK_MGMT_FAILED: /* 0x004A */
795 default:
796 /*
797 * What to do?
798 */
799 sc->result = DID_SOFT_ERROR << 16;
800 break;
801
802 } /* switch(status) */
803
804 dreplyprintk((KERN_NOTICE " sc->result is %08xh\n", sc->result));
805 } /* end of address reply case */
806
807 /* Unmap the DMA buffers, if any. */
808 if (sc->use_sg) {
809 pci_unmap_sg(ioc->pcidev, (struct scatterlist *) sc->request_buffer,
810 sc->use_sg, sc->sc_data_direction);
811 } else if (sc->request_bufflen) {
812 pci_unmap_single(ioc->pcidev, sc->SCp.dma_handle,
813 sc->request_bufflen, sc->sc_data_direction);
814 }
815
816 hd->ScsiLookup[req_idx] = NULL;
817
818 sc->scsi_done(sc); /* Issue the command callback */
819
820 /* Free Chain buffers */
821 mptscsih_freeChainBuffers(ioc, req_idx);
822 return 1;
823}
824
Linus Torvalds1da177e2005-04-16 15:20:36 -0700825/*
826 * mptscsih_flush_running_cmds - For each command found, search
827 * Scsi_Host instance taskQ and reply to OS.
828 * Called only if recovering from a FW reload.
829 * @hd: Pointer to a SCSI HOST structure
830 *
831 * Returns: None.
832 *
833 * Must be called while new I/Os are being queued.
834 */
835static void
836mptscsih_flush_running_cmds(MPT_SCSI_HOST *hd)
837{
838 MPT_ADAPTER *ioc = hd->ioc;
839 struct scsi_cmnd *SCpnt;
840 MPT_FRAME_HDR *mf;
841 int ii;
842 int max = ioc->req_depth;
843
844 dprintk((KERN_INFO MYNAM ": flush_ScsiLookup called\n"));
845 for (ii= 0; ii < max; ii++) {
846 if ((SCpnt = hd->ScsiLookup[ii]) != NULL) {
847
848 /* Command found.
849 */
850
851 /* Null ScsiLookup index
852 */
853 hd->ScsiLookup[ii] = NULL;
854
855 mf = MPT_INDEX_2_MFPTR(ioc, ii);
856 dmfprintk(( "flush: ScsiDone (mf=%p,sc=%p)\n",
857 mf, SCpnt));
858
859 /* Set status, free OS resources (SG DMA buffers)
860 * Do OS callback
861 * Free driver resources (chain, msg buffers)
862 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -0400863 if (SCpnt->use_sg) {
864 pci_unmap_sg(ioc->pcidev,
865 (struct scatterlist *) SCpnt->request_buffer,
866 SCpnt->use_sg,
867 SCpnt->sc_data_direction);
868 } else if (SCpnt->request_bufflen) {
869 pci_unmap_single(ioc->pcidev,
870 SCpnt->SCp.dma_handle,
871 SCpnt->request_bufflen,
872 SCpnt->sc_data_direction);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700873 }
874 SCpnt->result = DID_RESET << 16;
875 SCpnt->host_scribble = NULL;
876
877 /* Free Chain buffers */
878 mptscsih_freeChainBuffers(ioc, ii);
879
880 /* Free Message frames */
881 mpt_free_msg_frame(ioc, mf);
882
883 SCpnt->scsi_done(SCpnt); /* Issue the command callback */
884 }
885 }
886
887 return;
888}
889
890/*
891 * mptscsih_search_running_cmds - Delete any commands associated
892 * with the specified target and lun. Function called only
893 * when a lun is disable by mid-layer.
894 * Do NOT access the referenced scsi_cmnd structure or
895 * members. Will cause either a paging or NULL ptr error.
Moore, Eric Deanc7c82982005-11-16 18:54:25 -0700896 * @hd: Pointer to a SCSI HOST structure
897 * @vdevice: per device private data
Linus Torvalds1da177e2005-04-16 15:20:36 -0700898 *
899 * Returns: None.
900 *
901 * Called from slave_destroy.
902 */
903static void
Moore, Eric Deanc7c82982005-11-16 18:54:25 -0700904mptscsih_search_running_cmds(MPT_SCSI_HOST *hd, VirtDevice *vdevice)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700905{
906 SCSIIORequest_t *mf = NULL;
907 int ii;
908 int max = hd->ioc->req_depth;
Moore, Eric Dean466544d2005-09-14 18:09:10 -0600909 struct scsi_cmnd *sc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700910
911 dsprintk((KERN_INFO MYNAM ": search_running target %d lun %d max %d\n",
Moore, Eric Deanc7c82982005-11-16 18:54:25 -0700912 vdevice->target_id, vdevice->lun, max));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700913
914 for (ii=0; ii < max; ii++) {
Moore, Eric Dean466544d2005-09-14 18:09:10 -0600915 if ((sc = hd->ScsiLookup[ii]) != NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700916
917 mf = (SCSIIORequest_t *)MPT_INDEX_2_MFPTR(hd->ioc, ii);
918
919 dsprintk(( "search_running: found (sc=%p, mf = %p) target %d, lun %d \n",
920 hd->ScsiLookup[ii], mf, mf->TargetID, mf->LUN[1]));
921
Moore, Eric Deanc7c82982005-11-16 18:54:25 -0700922 if ((mf->TargetID != ((u8)vdevice->target_id)) || (mf->LUN[1] != ((u8) vdevice->lun)))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700923 continue;
924
925 /* Cleanup
926 */
927 hd->ScsiLookup[ii] = NULL;
928 mptscsih_freeChainBuffers(hd->ioc, ii);
929 mpt_free_msg_frame(hd->ioc, (MPT_FRAME_HDR *)mf);
Moore, Eric Dean466544d2005-09-14 18:09:10 -0600930 if (sc->use_sg) {
931 pci_unmap_sg(hd->ioc->pcidev,
932 (struct scatterlist *) sc->request_buffer,
933 sc->use_sg,
934 sc->sc_data_direction);
935 } else if (sc->request_bufflen) {
936 pci_unmap_single(hd->ioc->pcidev,
937 sc->SCp.dma_handle,
938 sc->request_bufflen,
939 sc->sc_data_direction);
940 }
941 sc->host_scribble = NULL;
942 sc->result = DID_NO_CONNECT << 16;
943 sc->scsi_done(sc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700944 }
945 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700946 return;
947}
948
949/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
Linus Torvalds1da177e2005-04-16 15:20:36 -0700950
951/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
952/*
953 * mptscsih_report_queue_full - Report QUEUE_FULL status returned
954 * from a SCSI target device.
955 * @sc: Pointer to scsi_cmnd structure
956 * @pScsiReply: Pointer to SCSIIOReply_t
957 * @pScsiReq: Pointer to original SCSI request
958 *
959 * This routine periodically reports QUEUE_FULL status returned from a
960 * SCSI target device. It reports this to the console via kernel
961 * printk() API call, not more than once every 10 seconds.
962 */
963static void
964mptscsih_report_queue_full(struct scsi_cmnd *sc, SCSIIOReply_t *pScsiReply, SCSIIORequest_t *pScsiReq)
965{
966 long time = jiffies;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700967 MPT_SCSI_HOST *hd;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700968
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -0400969 if (sc->device == NULL)
970 return;
971 if (sc->device->host == NULL)
972 return;
973 if ((hd = (MPT_SCSI_HOST *)sc->device->host->hostdata) == NULL)
974 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700975
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -0400976 if (time - hd->last_queue_full > 10 * HZ) {
977 dprintk((MYIOC_s_WARN_FMT "Device (%d:%d:%d) reported QUEUE_FULL!\n",
978 hd->ioc->name, 0, sc->device->id, sc->device->lun));
979 hd->last_queue_full = time;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700980 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700981}
982
983/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
984/*
985 * mptscsih_remove - Removed scsi devices
986 * @pdev: Pointer to pci_dev structure
987 *
988 *
989 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -0400990void
Linus Torvalds1da177e2005-04-16 15:20:36 -0700991mptscsih_remove(struct pci_dev *pdev)
992{
993 MPT_ADAPTER *ioc = pci_get_drvdata(pdev);
994 struct Scsi_Host *host = ioc->sh;
995 MPT_SCSI_HOST *hd;
Moore, Eric Deanf2ea8672005-11-16 18:54:23 -0700996#ifdef MPTSCSIH_ENABLE_DOMAIN_VALIDATION
Linus Torvalds1da177e2005-04-16 15:20:36 -0700997 int count;
998 unsigned long flags;
Moore, Eric Deanc7c82982005-11-16 18:54:25 -0700999#endif
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001000 int sz1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001001
Moore, Eric Dean466544d2005-09-14 18:09:10 -06001002 if(!host) {
1003 mpt_detach(pdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001004 return;
Moore, Eric Dean466544d2005-09-14 18:09:10 -06001005 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001006
1007 scsi_remove_host(host);
1008
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001009 if((hd = (MPT_SCSI_HOST *)host->hostdata) == NULL)
1010 return;
1011
Linus Torvalds1da177e2005-04-16 15:20:36 -07001012#ifdef MPTSCSIH_ENABLE_DOMAIN_VALIDATION
1013 /* Check DV thread active */
1014 count = 10 * HZ;
1015 spin_lock_irqsave(&dvtaskQ_lock, flags);
1016 if (dvtaskQ_active) {
1017 spin_unlock_irqrestore(&dvtaskQ_lock, flags);
Nishanth Aravamudan65210182005-11-07 01:01:19 -08001018 while(dvtaskQ_active && --count)
1019 schedule_timeout_interruptible(1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001020 } else {
1021 spin_unlock_irqrestore(&dvtaskQ_lock, flags);
1022 }
1023 if (!count)
1024 printk(KERN_ERR MYNAM ": ERROR - DV thread still active!\n");
1025#if defined(MPT_DEBUG_DV) || defined(MPT_DEBUG_DV_TINY)
1026 else
1027 printk(KERN_ERR MYNAM ": DV thread orig %d, count %d\n", 10 * HZ, count);
1028#endif
1029#endif
1030
Greg Kroah-Hartmand18c3db2005-06-23 17:35:56 -07001031 mptscsih_shutdown(pdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001032
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001033 sz1=0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001034
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001035 if (hd->ScsiLookup != NULL) {
1036 sz1 = hd->ioc->req_depth * sizeof(void *);
1037 kfree(hd->ScsiLookup);
1038 hd->ScsiLookup = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001039 }
1040
Moore, Eric Dean d485eb82005-05-11 17:37:26 -06001041 /*
1042 * Free pointer array.
1043 */
1044 kfree(hd->Targets);
1045 hd->Targets = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001046
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001047 dprintk((MYIOC_s_INFO_FMT
1048 "Free'd ScsiLookup (%d) memory\n",
1049 hd->ioc->name, sz1));
1050
Moore, Eric Dean d485eb82005-05-11 17:37:26 -06001051 kfree(hd->info_kbuf);
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001052
1053 /* NULL the Scsi_Host pointer
1054 */
1055 hd->ioc->sh = NULL;
1056
1057 scsi_host_put(host);
1058
1059 mpt_detach(pdev);
Christoph Hellwigc6678e02005-08-18 16:24:53 +02001060
Linus Torvalds1da177e2005-04-16 15:20:36 -07001061}
1062
1063/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
1064/*
1065 * mptscsih_shutdown - reboot notifier
1066 *
1067 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001068void
Greg Kroah-Hartmand18c3db2005-06-23 17:35:56 -07001069mptscsih_shutdown(struct pci_dev *pdev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001070{
Greg Kroah-Hartmand18c3db2005-06-23 17:35:56 -07001071 MPT_ADAPTER *ioc = pci_get_drvdata(pdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001072 struct Scsi_Host *host = ioc->sh;
1073 MPT_SCSI_HOST *hd;
1074
1075 if(!host)
1076 return;
1077
1078 hd = (MPT_SCSI_HOST *)host->hostdata;
1079
Linus Torvalds1da177e2005-04-16 15:20:36 -07001080}
1081
1082#ifdef CONFIG_PM
1083/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
1084/*
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001085 * mptscsih_suspend - Fusion MPT scsi driver suspend routine.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001086 *
1087 *
1088 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001089int
Pavel Machek8d189f72005-04-16 15:25:28 -07001090mptscsih_suspend(struct pci_dev *pdev, pm_message_t state)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001091{
Greg Kroah-Hartmand18c3db2005-06-23 17:35:56 -07001092 mptscsih_shutdown(pdev);
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001093 return mpt_suspend(pdev,state);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001094}
1095
1096/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
1097/*
1098 * mptscsih_resume - Fusion MPT scsi driver resume routine.
1099 *
1100 *
1101 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001102int
Linus Torvalds1da177e2005-04-16 15:20:36 -07001103mptscsih_resume(struct pci_dev *pdev)
1104{
1105 MPT_ADAPTER *ioc = pci_get_drvdata(pdev);
1106 struct Scsi_Host *host = ioc->sh;
1107 MPT_SCSI_HOST *hd;
1108
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001109 mpt_resume(pdev);
Christoph Hellwigc6678e02005-08-18 16:24:53 +02001110
Linus Torvalds1da177e2005-04-16 15:20:36 -07001111 if(!host)
1112 return 0;
1113
1114 hd = (MPT_SCSI_HOST *)host->hostdata;
1115 if(!hd)
1116 return 0;
1117
1118#ifdef MPTSCSIH_ENABLE_DOMAIN_VALIDATION
1119 {
1120 unsigned long lflags;
1121 spin_lock_irqsave(&dvtaskQ_lock, lflags);
1122 if (!dvtaskQ_active) {
1123 dvtaskQ_active = 1;
1124 spin_unlock_irqrestore(&dvtaskQ_lock, lflags);
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001125 INIT_WORK(&dvTaskQ_task,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001126 mptscsih_domainValidation, (void *) hd);
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001127 schedule_work(&dvTaskQ_task);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001128 } else {
1129 spin_unlock_irqrestore(&dvtaskQ_lock, lflags);
1130 }
1131 }
1132#endif
1133 return 0;
1134}
1135
1136#endif
1137
Linus Torvalds1da177e2005-04-16 15:20:36 -07001138/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
1139/**
1140 * mptscsih_info - Return information about MPT adapter
1141 * @SChost: Pointer to Scsi_Host structure
1142 *
1143 * (linux scsi_host_template.info routine)
1144 *
1145 * Returns pointer to buffer where information was written.
1146 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001147const char *
Linus Torvalds1da177e2005-04-16 15:20:36 -07001148mptscsih_info(struct Scsi_Host *SChost)
1149{
1150 MPT_SCSI_HOST *h;
1151 int size = 0;
1152
Linus Torvalds1da177e2005-04-16 15:20:36 -07001153 h = (MPT_SCSI_HOST *)SChost->hostdata;
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001154
Linus Torvalds1da177e2005-04-16 15:20:36 -07001155 if (h) {
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001156 if (h->info_kbuf == NULL)
1157 if ((h->info_kbuf = kmalloc(0x1000 /* 4Kb */, GFP_KERNEL)) == NULL)
1158 return h->info_kbuf;
1159 h->info_kbuf[0] = '\0';
1160
1161 mpt_print_ioc_summary(h->ioc, h->info_kbuf, &size, 0, 0);
1162 h->info_kbuf[size-1] = '\0';
Linus Torvalds1da177e2005-04-16 15:20:36 -07001163 }
1164
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001165 return h->info_kbuf;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001166}
1167
1168struct info_str {
1169 char *buffer;
1170 int length;
1171 int offset;
1172 int pos;
1173};
1174
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001175static void
1176mptscsih_copy_mem_info(struct info_str *info, char *data, int len)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001177{
1178 if (info->pos + len > info->length)
1179 len = info->length - info->pos;
1180
1181 if (info->pos + len < info->offset) {
1182 info->pos += len;
1183 return;
1184 }
1185
1186 if (info->pos < info->offset) {
1187 data += (info->offset - info->pos);
1188 len -= (info->offset - info->pos);
1189 }
1190
1191 if (len > 0) {
1192 memcpy(info->buffer + info->pos, data, len);
1193 info->pos += len;
1194 }
1195}
1196
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001197static int
1198mptscsih_copy_info(struct info_str *info, char *fmt, ...)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001199{
1200 va_list args;
1201 char buf[81];
1202 int len;
1203
1204 va_start(args, fmt);
1205 len = vsprintf(buf, fmt, args);
1206 va_end(args);
1207
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001208 mptscsih_copy_mem_info(info, buf, len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001209 return len;
1210}
1211
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001212static int
1213mptscsih_host_info(MPT_ADAPTER *ioc, char *pbuf, off_t offset, int len)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001214{
1215 struct info_str info;
1216
1217 info.buffer = pbuf;
1218 info.length = len;
1219 info.offset = offset;
1220 info.pos = 0;
1221
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001222 mptscsih_copy_info(&info, "%s: %s, ", ioc->name, ioc->prod_name);
1223 mptscsih_copy_info(&info, "%s%08xh, ", MPT_FW_REV_MAGIC_ID_STRING, ioc->facts.FWVersion.Word);
1224 mptscsih_copy_info(&info, "Ports=%d, ", ioc->facts.NumberOfPorts);
1225 mptscsih_copy_info(&info, "MaxQ=%d\n", ioc->req_depth);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001226
1227 return ((info.pos > info.offset) ? info.pos - info.offset : 0);
1228}
1229
1230/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
1231/**
1232 * mptscsih_proc_info - Return information about MPT adapter
1233 *
1234 * (linux scsi_host_template.info routine)
1235 *
1236 * buffer: if write, user data; if read, buffer for user
1237 * length: if write, return length;
1238 * offset: if write, 0; if read, the current offset into the buffer from
1239 * the previous read.
1240 * hostno: scsi host number
1241 * func: if write = 1; if read = 0
1242 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001243int
Linus Torvalds1da177e2005-04-16 15:20:36 -07001244mptscsih_proc_info(struct Scsi_Host *host, char *buffer, char **start, off_t offset,
1245 int length, int func)
1246{
1247 MPT_SCSI_HOST *hd = (MPT_SCSI_HOST *)host->hostdata;
1248 MPT_ADAPTER *ioc = hd->ioc;
1249 int size = 0;
1250
1251 if (func) {
Christoph Hellwigc6678e02005-08-18 16:24:53 +02001252 /*
1253 * write is not supported
Linus Torvalds1da177e2005-04-16 15:20:36 -07001254 */
1255 } else {
1256 if (start)
1257 *start = buffer;
1258
1259 size = mptscsih_host_info(ioc, buffer, offset, length);
1260 }
1261
1262 return size;
1263}
1264
1265/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
1266#define ADD_INDEX_LOG(req_ent) do { } while(0)
1267
1268/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
1269/**
1270 * mptscsih_qcmd - Primary Fusion MPT SCSI initiator IO start routine.
1271 * @SCpnt: Pointer to scsi_cmnd structure
1272 * @done: Pointer SCSI mid-layer IO completion function
1273 *
1274 * (linux scsi_host_template.queuecommand routine)
1275 * This is the primary SCSI IO start routine. Create a MPI SCSIIORequest
1276 * from a linux scsi_cmnd request and send it to the IOC.
1277 *
1278 * Returns 0. (rtn value discarded by linux scsi mid-layer)
1279 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001280int
Linus Torvalds1da177e2005-04-16 15:20:36 -07001281mptscsih_qcmd(struct scsi_cmnd *SCpnt, void (*done)(struct scsi_cmnd *))
1282{
1283 MPT_SCSI_HOST *hd;
1284 MPT_FRAME_HDR *mf;
1285 SCSIIORequest_t *pScsiReq;
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07001286 VirtDevice *vdev = SCpnt->device->hostdata;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001287 int lun;
1288 u32 datalen;
1289 u32 scsictl;
1290 u32 scsidir;
1291 u32 cmd_len;
1292 int my_idx;
1293 int ii;
1294
1295 hd = (MPT_SCSI_HOST *) SCpnt->device->host->hostdata;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001296 lun = SCpnt->device->lun;
1297 SCpnt->scsi_done = done;
1298
Linus Torvalds1da177e2005-04-16 15:20:36 -07001299 dmfprintk((MYIOC_s_INFO_FMT "qcmd: SCpnt=%p, done()=%p\n",
1300 (hd && hd->ioc) ? hd->ioc->name : "ioc?", SCpnt, done));
1301
1302 if (hd->resetPending) {
1303 dtmprintk((MYIOC_s_WARN_FMT "qcmd: SCpnt=%p timeout + 60HZ\n",
1304 (hd && hd->ioc) ? hd->ioc->name : "ioc?", SCpnt));
1305 return SCSI_MLQUEUE_HOST_BUSY;
1306 }
1307
1308 /*
1309 * Put together a MPT SCSI request...
1310 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001311 if ((mf = mpt_get_msg_frame(hd->ioc->DoneCtx, hd->ioc)) == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001312 dprintk((MYIOC_s_WARN_FMT "QueueCmd, no msg frames!!\n",
1313 hd->ioc->name));
1314 return SCSI_MLQUEUE_HOST_BUSY;
1315 }
1316
1317 pScsiReq = (SCSIIORequest_t *) mf;
1318
1319 my_idx = le16_to_cpu(mf->u.frame.hwhdr.msgctxu.fld.req_idx);
1320
1321 ADD_INDEX_LOG(my_idx);
1322
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001323 /* TUR's being issued with scsictl=0x02000000 (DATA_IN)!
Linus Torvalds1da177e2005-04-16 15:20:36 -07001324 * Seems we may receive a buffer (datalen>0) even when there
1325 * will be no data transfer! GRRRRR...
1326 */
1327 if (SCpnt->sc_data_direction == DMA_FROM_DEVICE) {
1328 datalen = SCpnt->request_bufflen;
1329 scsidir = MPI_SCSIIO_CONTROL_READ; /* DATA IN (host<--ioc<--dev) */
1330 } else if (SCpnt->sc_data_direction == DMA_TO_DEVICE) {
1331 datalen = SCpnt->request_bufflen;
1332 scsidir = MPI_SCSIIO_CONTROL_WRITE; /* DATA OUT (host-->ioc-->dev) */
1333 } else {
1334 datalen = 0;
1335 scsidir = MPI_SCSIIO_CONTROL_NODATATRANSFER;
1336 }
1337
1338 /* Default to untagged. Once a target structure has been allocated,
1339 * use the Inquiry data to determine if device supports tagged.
1340 */
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07001341 if (vdev
1342 && (vdev->vtarget->tflags & MPT_TARGET_FLAGS_Q_YES)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001343 && (SCpnt->device->tagged_supported)) {
1344 scsictl = scsidir | MPI_SCSIIO_CONTROL_SIMPLEQ;
1345 } else {
1346 scsictl = scsidir | MPI_SCSIIO_CONTROL_UNTAGGED;
1347 }
1348
1349 /* Use the above information to set up the message frame
1350 */
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07001351 pScsiReq->TargetID = (u8) vdev->target_id;
1352 pScsiReq->Bus = vdev->bus_id;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001353 pScsiReq->ChainOffset = 0;
1354 pScsiReq->Function = MPI_FUNCTION_SCSI_IO_REQUEST;
1355 pScsiReq->CDBLength = SCpnt->cmd_len;
1356 pScsiReq->SenseBufferLength = MPT_SENSE_BUFFER_SIZE;
1357 pScsiReq->Reserved = 0;
1358 pScsiReq->MsgFlags = mpt_msg_flags();
1359 pScsiReq->LUN[0] = 0;
1360 pScsiReq->LUN[1] = lun;
1361 pScsiReq->LUN[2] = 0;
1362 pScsiReq->LUN[3] = 0;
1363 pScsiReq->LUN[4] = 0;
1364 pScsiReq->LUN[5] = 0;
1365 pScsiReq->LUN[6] = 0;
1366 pScsiReq->LUN[7] = 0;
1367 pScsiReq->Control = cpu_to_le32(scsictl);
1368
1369 /*
1370 * Write SCSI CDB into the message
1371 */
1372 cmd_len = SCpnt->cmd_len;
1373 for (ii=0; ii < cmd_len; ii++)
1374 pScsiReq->CDB[ii] = SCpnt->cmnd[ii];
1375
1376 for (ii=cmd_len; ii < 16; ii++)
1377 pScsiReq->CDB[ii] = 0;
1378
1379 /* DataLength */
1380 pScsiReq->DataLength = cpu_to_le32(datalen);
1381
1382 /* SenseBuffer low address */
1383 pScsiReq->SenseBufferLowAddr = cpu_to_le32(hd->ioc->sense_buf_low_dma
1384 + (my_idx * MPT_SENSE_BUFFER_ALLOC));
1385
1386 /* Now add the SG list
1387 * Always have a SGE even if null length.
1388 */
1389 if (datalen == 0) {
1390 /* Add a NULL SGE */
1391 mptscsih_add_sge((char *)&pScsiReq->SGL, MPT_SGE_FLAGS_SSIMPLE_READ | 0,
1392 (dma_addr_t) -1);
1393 } else {
1394 /* Add a 32 or 64 bit SGE */
1395 if (mptscsih_AddSGE(hd->ioc, SCpnt, pScsiReq, my_idx) != SUCCESS)
1396 goto fail;
1397 }
1398
1399 hd->ScsiLookup[my_idx] = SCpnt;
1400 SCpnt->host_scribble = NULL;
1401
1402#ifdef MPTSCSIH_ENABLE_DOMAIN_VALIDATION
Moore, Eric Deana9b29372005-11-16 18:54:20 -07001403 if (hd->ioc->bus_type == SPI) {
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07001404 int dvStatus = hd->ioc->spi_data.dvStatus[vdev->target_id];
Linus Torvalds1da177e2005-04-16 15:20:36 -07001405 int issueCmd = 1;
1406
1407 if (dvStatus || hd->ioc->spi_data.forceDv) {
1408
1409 if ((dvStatus & MPT_SCSICFG_NEED_DV) ||
1410 (hd->ioc->spi_data.forceDv & MPT_SCSICFG_NEED_DV)) {
1411 unsigned long lflags;
1412 /* Schedule DV if necessary */
1413 spin_lock_irqsave(&dvtaskQ_lock, lflags);
1414 if (!dvtaskQ_active) {
1415 dvtaskQ_active = 1;
1416 spin_unlock_irqrestore(&dvtaskQ_lock, lflags);
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001417 INIT_WORK(&dvTaskQ_task, mptscsih_domainValidation, (void *) hd);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001418
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001419 schedule_work(&dvTaskQ_task);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001420 } else {
1421 spin_unlock_irqrestore(&dvtaskQ_lock, lflags);
1422 }
1423 hd->ioc->spi_data.forceDv &= ~MPT_SCSICFG_NEED_DV;
1424 }
1425
1426 /* Trying to do DV to this target, extend timeout.
1427 * Wait to issue until flag is clear
1428 */
1429 if (dvStatus & MPT_SCSICFG_DV_PENDING) {
1430 mod_timer(&SCpnt->eh_timeout, jiffies + 40 * HZ);
1431 issueCmd = 0;
1432 }
1433
1434 /* Set the DV flags.
1435 */
1436 if (dvStatus & MPT_SCSICFG_DV_NOT_DONE)
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07001437 mptscsih_set_dvflags(hd, SCpnt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001438
1439 if (!issueCmd)
1440 goto fail;
1441 }
1442 }
1443#endif
1444
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001445 mpt_put_msg_frame(hd->ioc->DoneCtx, hd->ioc, mf);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001446 dmfprintk((MYIOC_s_INFO_FMT "Issued SCSI cmd (%p) mf=%p idx=%d\n",
1447 hd->ioc->name, SCpnt, mf, my_idx));
1448 DBG_DUMP_REQUEST_FRAME(mf)
1449 return 0;
1450
1451 fail:
Moore, Eric Dean466544d2005-09-14 18:09:10 -06001452 hd->ScsiLookup[my_idx] = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001453 mptscsih_freeChainBuffers(hd->ioc, my_idx);
1454 mpt_free_msg_frame(hd->ioc, mf);
1455 return SCSI_MLQUEUE_HOST_BUSY;
1456}
1457
1458/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
1459/*
1460 * mptscsih_freeChainBuffers - Function to free chain buffers associated
1461 * with a SCSI IO request
1462 * @hd: Pointer to the MPT_SCSI_HOST instance
1463 * @req_idx: Index of the SCSI IO request frame.
1464 *
1465 * Called if SG chain buffer allocation fails and mptscsih callbacks.
1466 * No return.
1467 */
1468static void
1469mptscsih_freeChainBuffers(MPT_ADAPTER *ioc, int req_idx)
1470{
1471 MPT_FRAME_HDR *chain;
1472 unsigned long flags;
1473 int chain_idx;
1474 int next;
1475
1476 /* Get the first chain index and reset
1477 * tracker state.
1478 */
1479 chain_idx = ioc->ReqToChain[req_idx];
1480 ioc->ReqToChain[req_idx] = MPT_HOST_NO_CHAIN;
1481
1482 while (chain_idx != MPT_HOST_NO_CHAIN) {
1483
1484 /* Save the next chain buffer index */
1485 next = ioc->ChainToChain[chain_idx];
1486
1487 /* Free this chain buffer and reset
1488 * tracker
1489 */
1490 ioc->ChainToChain[chain_idx] = MPT_HOST_NO_CHAIN;
1491
1492 chain = (MPT_FRAME_HDR *) (ioc->ChainBuffer
1493 + (chain_idx * ioc->req_sz));
1494
1495 spin_lock_irqsave(&ioc->FreeQlock, flags);
1496 list_add_tail(&chain->u.frame.linkage.list, &ioc->FreeChainQ);
1497 spin_unlock_irqrestore(&ioc->FreeQlock, flags);
1498
1499 dmfprintk((MYIOC_s_INFO_FMT "FreeChainBuffers (index %d)\n",
1500 ioc->name, chain_idx));
1501
1502 /* handle next */
1503 chain_idx = next;
1504 }
1505 return;
1506}
1507
1508/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
1509/*
1510 * Reset Handling
1511 */
1512
1513/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
1514/*
1515 * mptscsih_TMHandler - Generic handler for SCSI Task Management.
1516 * Fall through to mpt_HardResetHandler if: not operational, too many
1517 * failed TM requests or handshake failure.
1518 *
1519 * @ioc: Pointer to MPT_ADAPTER structure
1520 * @type: Task Management type
1521 * @target: Logical Target ID for reset (if appropriate)
1522 * @lun: Logical Unit for reset (if appropriate)
1523 * @ctx2abort: Context for the task to be aborted (if appropriate)
1524 *
1525 * Remark: Currently invoked from a non-interrupt thread (_bh).
1526 *
1527 * Remark: With old EH code, at most 1 SCSI TaskMgmt function per IOC
1528 * will be active.
1529 *
1530 * Returns 0 for SUCCESS or -1 if FAILED.
1531 */
1532static int
1533mptscsih_TMHandler(MPT_SCSI_HOST *hd, u8 type, u8 channel, u8 target, u8 lun, int ctx2abort, ulong timeout)
1534{
1535 MPT_ADAPTER *ioc;
1536 int rc = -1;
1537 int doTask = 1;
1538 u32 ioc_raw_state;
1539 unsigned long flags;
1540
1541 /* If FW is being reloaded currently, return success to
1542 * the calling function.
1543 */
1544 if (hd == NULL)
1545 return 0;
1546
1547 ioc = hd->ioc;
1548 if (ioc == NULL) {
1549 printk(KERN_ERR MYNAM " TMHandler" " NULL ioc!\n");
1550 return FAILED;
1551 }
1552 dtmprintk((MYIOC_s_INFO_FMT "TMHandler Entered!\n", ioc->name));
1553
1554 // SJR - CHECKME - Can we avoid this here?
1555 // (mpt_HardResetHandler has this check...)
1556 spin_lock_irqsave(&ioc->diagLock, flags);
1557 if ((ioc->diagPending) || (ioc->alt_ioc && ioc->alt_ioc->diagPending)) {
1558 spin_unlock_irqrestore(&ioc->diagLock, flags);
1559 return FAILED;
1560 }
1561 spin_unlock_irqrestore(&ioc->diagLock, flags);
1562
1563 /* Wait a fixed amount of time for the TM pending flag to be cleared.
1564 * If we time out and not bus reset, then we return a FAILED status to the caller.
1565 * The call to mptscsih_tm_pending_wait() will set the pending flag if we are
1566 * successful. Otherwise, reload the FW.
1567 */
1568 if (mptscsih_tm_pending_wait(hd) == FAILED) {
1569 if (type == MPI_SCSITASKMGMT_TASKTYPE_ABORT_TASK) {
Christoph Hellwigc6678e02005-08-18 16:24:53 +02001570 dtmprintk((KERN_INFO MYNAM ": %s: TMHandler abort: "
Linus Torvalds1da177e2005-04-16 15:20:36 -07001571 "Timed out waiting for last TM (%d) to complete! \n",
1572 hd->ioc->name, hd->tmPending));
1573 return FAILED;
1574 } else if (type == MPI_SCSITASKMGMT_TASKTYPE_TARGET_RESET) {
Christoph Hellwigc6678e02005-08-18 16:24:53 +02001575 dtmprintk((KERN_INFO MYNAM ": %s: TMHandler target reset: "
Linus Torvalds1da177e2005-04-16 15:20:36 -07001576 "Timed out waiting for last TM (%d) to complete! \n",
1577 hd->ioc->name, hd->tmPending));
1578 return FAILED;
1579 } else if (type == MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS) {
Christoph Hellwigc6678e02005-08-18 16:24:53 +02001580 dtmprintk((KERN_INFO MYNAM ": %s: TMHandler bus reset: "
Linus Torvalds1da177e2005-04-16 15:20:36 -07001581 "Timed out waiting for last TM (%d) to complete! \n",
1582 hd->ioc->name, hd->tmPending));
1583 if (hd->tmPending & (1 << MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS))
1584 return FAILED;
1585
1586 doTask = 0;
1587 }
1588 } else {
1589 spin_lock_irqsave(&hd->ioc->FreeQlock, flags);
1590 hd->tmPending |= (1 << type);
1591 spin_unlock_irqrestore(&hd->ioc->FreeQlock, flags);
1592 }
1593
1594 /* Is operational?
1595 */
1596 ioc_raw_state = mpt_GetIocState(hd->ioc, 0);
1597
1598#ifdef MPT_DEBUG_RESET
1599 if ((ioc_raw_state & MPI_IOC_STATE_MASK) != MPI_IOC_STATE_OPERATIONAL) {
1600 printk(MYIOC_s_WARN_FMT
1601 "TM Handler: IOC Not operational(0x%x)!\n",
1602 hd->ioc->name, ioc_raw_state);
1603 }
1604#endif
1605
1606 if (doTask && ((ioc_raw_state & MPI_IOC_STATE_MASK) == MPI_IOC_STATE_OPERATIONAL)
1607 && !(ioc_raw_state & MPI_DOORBELL_ACTIVE)) {
1608
1609 /* Isse the Task Mgmt request.
1610 */
1611 if (hd->hard_resets < -1)
1612 hd->hard_resets++;
1613 rc = mptscsih_IssueTaskMgmt(hd, type, channel, target, lun, ctx2abort, timeout);
1614 if (rc) {
1615 printk(MYIOC_s_INFO_FMT "Issue of TaskMgmt failed!\n", hd->ioc->name);
1616 } else {
1617 dtmprintk((MYIOC_s_INFO_FMT "Issue of TaskMgmt Successful!\n", hd->ioc->name));
1618 }
1619 }
1620
1621 /* Only fall through to the HRH if this is a bus reset
1622 */
1623 if ((type == MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS) && (rc ||
1624 ioc->reload_fw || (ioc->alt_ioc && ioc->alt_ioc->reload_fw))) {
1625 dtmprintk((MYIOC_s_INFO_FMT "Calling HardReset! \n",
1626 hd->ioc->name));
1627 rc = mpt_HardResetHandler(hd->ioc, CAN_SLEEP);
1628 }
1629
1630 dtmprintk((MYIOC_s_INFO_FMT "TMHandler rc = %d!\n", hd->ioc->name, rc));
1631
1632 return rc;
1633}
1634
1635
1636/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
1637/*
1638 * mptscsih_IssueTaskMgmt - Generic send Task Management function.
1639 * @hd: Pointer to MPT_SCSI_HOST structure
1640 * @type: Task Management type
1641 * @target: Logical Target ID for reset (if appropriate)
1642 * @lun: Logical Unit for reset (if appropriate)
1643 * @ctx2abort: Context for the task to be aborted (if appropriate)
1644 *
1645 * Remark: _HardResetHandler can be invoked from an interrupt thread (timer)
1646 * or a non-interrupt thread. In the former, must not call schedule().
1647 *
1648 * Not all fields are meaningfull for all task types.
1649 *
1650 * Returns 0 for SUCCESS, -999 for "no msg frames",
1651 * else other non-zero value returned.
1652 */
1653static int
1654mptscsih_IssueTaskMgmt(MPT_SCSI_HOST *hd, u8 type, u8 channel, u8 target, u8 lun, int ctx2abort, ulong timeout)
1655{
1656 MPT_FRAME_HDR *mf;
1657 SCSITaskMgmt_t *pScsiTm;
1658 int ii;
1659 int retval;
1660
1661 /* Return Fail to calling function if no message frames available.
1662 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001663 if ((mf = mpt_get_msg_frame(hd->ioc->TaskCtx, hd->ioc)) == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001664 dfailprintk((MYIOC_s_ERR_FMT "IssueTaskMgmt, no msg frames!!\n",
1665 hd->ioc->name));
Christoph Hellwigc6678e02005-08-18 16:24:53 +02001666 return FAILED;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001667 }
1668 dtmprintk((MYIOC_s_INFO_FMT "IssueTaskMgmt request @ %p\n",
1669 hd->ioc->name, mf));
1670
1671 /* Format the Request
1672 */
1673 pScsiTm = (SCSITaskMgmt_t *) mf;
1674 pScsiTm->TargetID = target;
1675 pScsiTm->Bus = channel;
1676 pScsiTm->ChainOffset = 0;
1677 pScsiTm->Function = MPI_FUNCTION_SCSI_TASK_MGMT;
1678
1679 pScsiTm->Reserved = 0;
1680 pScsiTm->TaskType = type;
1681 pScsiTm->Reserved1 = 0;
1682 pScsiTm->MsgFlags = (type == MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS)
1683 ? MPI_SCSITASKMGMT_MSGFLAGS_LIPRESET_RESET_OPTION : 0;
1684
1685 for (ii= 0; ii < 8; ii++) {
1686 pScsiTm->LUN[ii] = 0;
1687 }
1688 pScsiTm->LUN[1] = lun;
1689
1690 for (ii=0; ii < 7; ii++)
1691 pScsiTm->Reserved2[ii] = 0;
1692
1693 pScsiTm->TaskMsgContext = ctx2abort;
1694
Christoph Hellwigc6678e02005-08-18 16:24:53 +02001695 dtmprintk((MYIOC_s_INFO_FMT "IssueTaskMgmt: ctx2abort (0x%08x) type=%d\n",
1696 hd->ioc->name, ctx2abort, type));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001697
1698 DBG_DUMP_TM_REQUEST_FRAME((u32 *)pScsiTm);
1699
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001700 if ((retval = mpt_send_handshake_request(hd->ioc->TaskCtx, hd->ioc,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001701 sizeof(SCSITaskMgmt_t), (u32*)pScsiTm,
1702 CAN_SLEEP)) != 0) {
1703 dfailprintk((MYIOC_s_ERR_FMT "_send_handshake FAILED!"
1704 " (hd %p, ioc %p, mf %p) \n", hd->ioc->name, hd,
1705 hd->ioc, mf));
1706 mpt_free_msg_frame(hd->ioc, mf);
1707 return retval;
1708 }
1709
1710 if(mptscsih_tm_wait_for_completion(hd, timeout) == FAILED) {
1711 dfailprintk((MYIOC_s_ERR_FMT "_wait_for_completion FAILED!"
1712 " (hd %p, ioc %p, mf %p) \n", hd->ioc->name, hd,
1713 hd->ioc, mf));
1714 mpt_free_msg_frame(hd->ioc, mf);
1715 dtmprintk((MYIOC_s_INFO_FMT "Calling HardReset! \n",
1716 hd->ioc->name));
1717 retval = mpt_HardResetHandler(hd->ioc, CAN_SLEEP);
1718 }
1719
1720 return retval;
1721}
1722
1723/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
1724/**
1725 * mptscsih_abort - Abort linux scsi_cmnd routine, new_eh variant
1726 * @SCpnt: Pointer to scsi_cmnd structure, IO to be aborted
1727 *
1728 * (linux scsi_host_template.eh_abort_handler routine)
1729 *
1730 * Returns SUCCESS or FAILED.
1731 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001732int
Linus Torvalds1da177e2005-04-16 15:20:36 -07001733mptscsih_abort(struct scsi_cmnd * SCpnt)
1734{
1735 MPT_SCSI_HOST *hd;
1736 MPT_ADAPTER *ioc;
1737 MPT_FRAME_HDR *mf;
1738 u32 ctx2abort;
1739 int scpnt_idx;
Moore, Eric Dean466544d2005-09-14 18:09:10 -06001740 int retval;
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07001741 VirtDevice *vdev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001742
1743 /* If we can't locate our host adapter structure, return FAILED status.
1744 */
1745 if ((hd = (MPT_SCSI_HOST *) SCpnt->device->host->hostdata) == NULL) {
1746 SCpnt->result = DID_RESET << 16;
1747 SCpnt->scsi_done(SCpnt);
Moore, Eric Dean466544d2005-09-14 18:09:10 -06001748 dfailprintk((KERN_INFO MYNAM ": mptscsih_abort: "
Linus Torvalds1da177e2005-04-16 15:20:36 -07001749 "Can't locate host! (sc=%p)\n",
1750 SCpnt));
1751 return FAILED;
1752 }
1753
1754 ioc = hd->ioc;
Moore, Eric Dean466544d2005-09-14 18:09:10 -06001755 if (hd->resetPending) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001756 return FAILED;
Moore, Eric Dean466544d2005-09-14 18:09:10 -06001757 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001758
1759 if (hd->timeouts < -1)
1760 hd->timeouts++;
1761
1762 /* Find this command
1763 */
1764 if ((scpnt_idx = SCPNT_TO_LOOKUP_IDX(SCpnt)) < 0) {
Moore, Eric Dean466544d2005-09-14 18:09:10 -06001765 /* Cmd not found in ScsiLookup.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001766 * Do OS callback.
1767 */
1768 SCpnt->result = DID_RESET << 16;
Moore, Eric Dean466544d2005-09-14 18:09:10 -06001769 dtmprintk((KERN_INFO MYNAM ": %s: mptscsih_abort: "
Linus Torvalds1da177e2005-04-16 15:20:36 -07001770 "Command not in the active list! (sc=%p)\n",
1771 hd->ioc->name, SCpnt));
1772 return SUCCESS;
1773 }
1774
Moore, Eric Dean466544d2005-09-14 18:09:10 -06001775 printk(KERN_WARNING MYNAM ": %s: attempting task abort! (sc=%p)\n",
1776 hd->ioc->name, SCpnt);
1777 scsi_print_command(SCpnt);
1778
Linus Torvalds1da177e2005-04-16 15:20:36 -07001779 /* Most important! Set TaskMsgContext to SCpnt's MsgContext!
1780 * (the IO to be ABORT'd)
1781 *
1782 * NOTE: Since we do not byteswap MsgContext, we do not
1783 * swap it here either. It is an opaque cookie to
1784 * the controller, so it does not matter. -DaveM
1785 */
1786 mf = MPT_INDEX_2_MFPTR(hd->ioc, scpnt_idx);
1787 ctx2abort = mf->u.frame.hwhdr.msgctxu.MsgContext;
1788
1789 hd->abortSCpnt = SCpnt;
1790
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07001791 vdev = SCpnt->device->hostdata;
Moore, Eric Dean466544d2005-09-14 18:09:10 -06001792 retval = mptscsih_TMHandler(hd, MPI_SCSITASKMGMT_TASKTYPE_ABORT_TASK,
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07001793 vdev->bus_id, vdev->target_id, vdev->lun,
Moore, Eric Dean466544d2005-09-14 18:09:10 -06001794 ctx2abort, 2 /* 2 second timeout */);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001795
Moore, Eric Dean466544d2005-09-14 18:09:10 -06001796 printk (KERN_WARNING MYNAM ": %s: task abort: %s (sc=%p)\n",
1797 hd->ioc->name,
1798 ((retval == 0) ? "SUCCESS" : "FAILED" ), SCpnt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001799
Moore, Eric Dean466544d2005-09-14 18:09:10 -06001800 if (retval == 0)
1801 return SUCCESS;
1802
1803 if(retval != FAILED ) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001804 hd->tmPending = 0;
1805 hd->tmState = TM_STATE_NONE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001806 }
Moore, Eric Dean466544d2005-09-14 18:09:10 -06001807 return FAILED;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001808}
1809
1810/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
1811/**
1812 * mptscsih_dev_reset - Perform a SCSI TARGET_RESET! new_eh variant
1813 * @SCpnt: Pointer to scsi_cmnd structure, IO which reset is due to
1814 *
1815 * (linux scsi_host_template.eh_dev_reset_handler routine)
1816 *
1817 * Returns SUCCESS or FAILED.
1818 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001819int
Linus Torvalds1da177e2005-04-16 15:20:36 -07001820mptscsih_dev_reset(struct scsi_cmnd * SCpnt)
1821{
1822 MPT_SCSI_HOST *hd;
Moore, Eric Dean466544d2005-09-14 18:09:10 -06001823 int retval;
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07001824 VirtDevice *vdev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001825
1826 /* If we can't locate our host adapter structure, return FAILED status.
1827 */
1828 if ((hd = (MPT_SCSI_HOST *) SCpnt->device->host->hostdata) == NULL){
Moore, Eric Dean466544d2005-09-14 18:09:10 -06001829 dtmprintk((KERN_INFO MYNAM ": mptscsih_dev_reset: "
Linus Torvalds1da177e2005-04-16 15:20:36 -07001830 "Can't locate host! (sc=%p)\n",
1831 SCpnt));
1832 return FAILED;
1833 }
1834
1835 if (hd->resetPending)
1836 return FAILED;
1837
Moore, Eric Dean466544d2005-09-14 18:09:10 -06001838 printk(KERN_WARNING MYNAM ": %s: attempting target reset! (sc=%p)\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001839 hd->ioc->name, SCpnt);
Moore, Eric Dean466544d2005-09-14 18:09:10 -06001840 scsi_print_command(SCpnt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001841
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07001842 vdev = SCpnt->device->hostdata;
Moore, Eric Dean466544d2005-09-14 18:09:10 -06001843 retval = mptscsih_TMHandler(hd, MPI_SCSITASKMGMT_TASKTYPE_TARGET_RESET,
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07001844 vdev->bus_id, vdev->target_id,
Moore, Eric Dean466544d2005-09-14 18:09:10 -06001845 0, 0, 5 /* 5 second timeout */);
1846
1847 printk (KERN_WARNING MYNAM ": %s: target reset: %s (sc=%p)\n",
1848 hd->ioc->name,
1849 ((retval == 0) ? "SUCCESS" : "FAILED" ), SCpnt);
1850
1851 if (retval == 0)
1852 return SUCCESS;
1853
1854 if(retval != FAILED ) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001855 hd->tmPending = 0;
1856 hd->tmState = TM_STATE_NONE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001857 }
Moore, Eric Dean466544d2005-09-14 18:09:10 -06001858 return FAILED;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001859}
1860
1861/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
1862/**
1863 * mptscsih_bus_reset - Perform a SCSI BUS_RESET! new_eh variant
1864 * @SCpnt: Pointer to scsi_cmnd structure, IO which reset is due to
1865 *
1866 * (linux scsi_host_template.eh_bus_reset_handler routine)
1867 *
1868 * Returns SUCCESS or FAILED.
1869 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001870int
Linus Torvalds1da177e2005-04-16 15:20:36 -07001871mptscsih_bus_reset(struct scsi_cmnd * SCpnt)
1872{
1873 MPT_SCSI_HOST *hd;
Moore, Eric Dean466544d2005-09-14 18:09:10 -06001874 int retval;
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07001875 VirtDevice *vdev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001876
1877 /* If we can't locate our host adapter structure, return FAILED status.
1878 */
1879 if ((hd = (MPT_SCSI_HOST *) SCpnt->device->host->hostdata) == NULL){
Moore, Eric Dean466544d2005-09-14 18:09:10 -06001880 dtmprintk((KERN_INFO MYNAM ": mptscsih_bus_reset: "
Linus Torvalds1da177e2005-04-16 15:20:36 -07001881 "Can't locate host! (sc=%p)\n",
1882 SCpnt ) );
1883 return FAILED;
1884 }
1885
Moore, Eric Dean466544d2005-09-14 18:09:10 -06001886 printk(KERN_WARNING MYNAM ": %s: attempting bus reset! (sc=%p)\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001887 hd->ioc->name, SCpnt);
Moore, Eric Dean466544d2005-09-14 18:09:10 -06001888 scsi_print_command(SCpnt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001889
1890 if (hd->timeouts < -1)
1891 hd->timeouts++;
1892
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07001893 vdev = SCpnt->device->hostdata;
Moore, Eric Dean466544d2005-09-14 18:09:10 -06001894 retval = mptscsih_TMHandler(hd, MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS,
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07001895 vdev->bus_id, 0, 0, 0, 5 /* 5 second timeout */);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001896
Moore, Eric Dean466544d2005-09-14 18:09:10 -06001897 printk (KERN_WARNING MYNAM ": %s: bus reset: %s (sc=%p)\n",
1898 hd->ioc->name,
1899 ((retval == 0) ? "SUCCESS" : "FAILED" ), SCpnt);
1900
1901 if (retval == 0)
1902 return SUCCESS;
1903
1904 if(retval != FAILED ) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001905 hd->tmPending = 0;
1906 hd->tmState = TM_STATE_NONE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001907 }
Moore, Eric Dean466544d2005-09-14 18:09:10 -06001908 return FAILED;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001909}
1910
1911/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
1912/**
1913 * mptscsih_host_reset - Perform a SCSI host adapter RESET!
1914 * new_eh variant
1915 * @SCpnt: Pointer to scsi_cmnd structure, IO which reset is due to
1916 *
1917 * (linux scsi_host_template.eh_host_reset_handler routine)
1918 *
1919 * Returns SUCCESS or FAILED.
1920 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04001921int
Linus Torvalds1da177e2005-04-16 15:20:36 -07001922mptscsih_host_reset(struct scsi_cmnd *SCpnt)
1923{
1924 MPT_SCSI_HOST * hd;
1925 int status = SUCCESS;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001926
1927 /* If we can't locate the host to reset, then we failed. */
1928 if ((hd = (MPT_SCSI_HOST *) SCpnt->device->host->hostdata) == NULL){
Christoph Hellwigc6678e02005-08-18 16:24:53 +02001929 dtmprintk( ( KERN_INFO MYNAM ": mptscsih_host_reset: "
Linus Torvalds1da177e2005-04-16 15:20:36 -07001930 "Can't locate host! (sc=%p)\n",
1931 SCpnt ) );
1932 return FAILED;
1933 }
1934
Christoph Hellwigc6678e02005-08-18 16:24:53 +02001935 printk(KERN_WARNING MYNAM ": %s: Attempting host reset! (sc=%p)\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001936 hd->ioc->name, SCpnt);
1937
1938 /* If our attempts to reset the host failed, then return a failed
1939 * status. The host will be taken off line by the SCSI mid-layer.
1940 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001941 if (mpt_HardResetHandler(hd->ioc, CAN_SLEEP) < 0){
1942 status = FAILED;
1943 } else {
1944 /* Make sure TM pending is cleared and TM state is set to
1945 * NONE.
1946 */
1947 hd->tmPending = 0;
1948 hd->tmState = TM_STATE_NONE;
1949 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001950
Christoph Hellwigc6678e02005-08-18 16:24:53 +02001951 dtmprintk( ( KERN_INFO MYNAM ": mptscsih_host_reset: "
Linus Torvalds1da177e2005-04-16 15:20:36 -07001952 "Status = %s\n",
1953 (status == SUCCESS) ? "SUCCESS" : "FAILED" ) );
1954
1955 return status;
1956}
1957
1958/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
1959/**
1960 * mptscsih_tm_pending_wait - wait for pending task management request to
1961 * complete.
1962 * @hd: Pointer to MPT host structure.
1963 *
1964 * Returns {SUCCESS,FAILED}.
1965 */
1966static int
1967mptscsih_tm_pending_wait(MPT_SCSI_HOST * hd)
1968{
1969 unsigned long flags;
1970 int loop_count = 4 * 10; /* Wait 10 seconds */
1971 int status = FAILED;
1972
1973 do {
1974 spin_lock_irqsave(&hd->ioc->FreeQlock, flags);
1975 if (hd->tmState == TM_STATE_NONE) {
1976 hd->tmState = TM_STATE_IN_PROGRESS;
1977 hd->tmPending = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001978 spin_unlock_irqrestore(&hd->ioc->FreeQlock, flags);
Christoph Hellwigc6678e02005-08-18 16:24:53 +02001979 status = SUCCESS;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001980 break;
1981 }
1982 spin_unlock_irqrestore(&hd->ioc->FreeQlock, flags);
1983 msleep(250);
1984 } while (--loop_count);
1985
1986 return status;
1987}
1988
1989/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
1990/**
1991 * mptscsih_tm_wait_for_completion - wait for completion of TM task
1992 * @hd: Pointer to MPT host structure.
1993 *
1994 * Returns {SUCCESS,FAILED}.
1995 */
1996static int
1997mptscsih_tm_wait_for_completion(MPT_SCSI_HOST * hd, ulong timeout )
1998{
1999 unsigned long flags;
2000 int loop_count = 4 * timeout;
2001 int status = FAILED;
2002
2003 do {
2004 spin_lock_irqsave(&hd->ioc->FreeQlock, flags);
2005 if(hd->tmPending == 0) {
2006 status = SUCCESS;
Christoph Hellwigc6678e02005-08-18 16:24:53 +02002007 spin_unlock_irqrestore(&hd->ioc->FreeQlock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002008 break;
2009 }
2010 spin_unlock_irqrestore(&hd->ioc->FreeQlock, flags);
2011 msleep_interruptible(250);
2012 } while (--loop_count);
2013
2014 return status;
2015}
2016
2017/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
2018/**
2019 * mptscsih_taskmgmt_complete - Registered with Fusion MPT base driver
2020 * @ioc: Pointer to MPT_ADAPTER structure
2021 * @mf: Pointer to SCSI task mgmt request frame
2022 * @mr: Pointer to SCSI task mgmt reply frame
2023 *
2024 * This routine is called from mptbase.c::mpt_interrupt() at the completion
2025 * of any SCSI task management request.
2026 * This routine is registered with the MPT (base) driver at driver
2027 * load/init time via the mpt_register() API call.
2028 *
2029 * Returns 1 indicating alloc'd request frame ptr should be freed.
2030 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04002031int
Linus Torvalds1da177e2005-04-16 15:20:36 -07002032mptscsih_taskmgmt_complete(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *mr)
2033{
2034 SCSITaskMgmtReply_t *pScsiTmReply;
2035 SCSITaskMgmt_t *pScsiTmReq;
2036 MPT_SCSI_HOST *hd;
2037 unsigned long flags;
2038 u16 iocstatus;
2039 u8 tmType;
2040
2041 dtmprintk((MYIOC_s_WARN_FMT "TaskMgmt completed (mf=%p,mr=%p)\n",
2042 ioc->name, mf, mr));
2043 if (ioc->sh) {
2044 /* Depending on the thread, a timer is activated for
2045 * the TM request. Delete this timer on completion of TM.
2046 * Decrement count of outstanding TM requests.
2047 */
2048 hd = (MPT_SCSI_HOST *)ioc->sh->hostdata;
2049 } else {
2050 dtmprintk((MYIOC_s_WARN_FMT "TaskMgmt Complete: NULL Scsi Host Ptr\n",
2051 ioc->name));
2052 return 1;
2053 }
2054
2055 if (mr == NULL) {
2056 dtmprintk((MYIOC_s_WARN_FMT "ERROR! TaskMgmt Reply: NULL Request %p\n",
2057 ioc->name, mf));
2058 return 1;
2059 } else {
2060 pScsiTmReply = (SCSITaskMgmtReply_t*)mr;
2061 pScsiTmReq = (SCSITaskMgmt_t*)mf;
2062
2063 /* Figure out if this was ABORT_TASK, TARGET_RESET, or BUS_RESET! */
2064 tmType = pScsiTmReq->TaskType;
2065
2066 dtmprintk((MYIOC_s_WARN_FMT " TaskType = %d, TerminationCount=%d\n",
2067 ioc->name, tmType, le32_to_cpu(pScsiTmReply->TerminationCount)));
2068 DBG_DUMP_TM_REPLY_FRAME((u32 *)pScsiTmReply);
2069
2070 iocstatus = le16_to_cpu(pScsiTmReply->IOCStatus) & MPI_IOCSTATUS_MASK;
2071 dtmprintk((MYIOC_s_WARN_FMT " SCSI TaskMgmt (%d) IOCStatus=%04x IOCLogInfo=%08x\n",
2072 ioc->name, tmType, iocstatus, le32_to_cpu(pScsiTmReply->IOCLogInfo)));
2073 /* Error? (anything non-zero?) */
2074 if (iocstatus) {
2075
2076 /* clear flags and continue.
2077 */
2078 if (tmType == MPI_SCSITASKMGMT_TASKTYPE_ABORT_TASK)
2079 hd->abortSCpnt = NULL;
2080
2081 /* If an internal command is present
2082 * or the TM failed - reload the FW.
2083 * FC FW may respond FAILED to an ABORT
2084 */
2085 if (tmType == MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS) {
2086 if ((hd->cmdPtr) ||
2087 (iocstatus == MPI_IOCSTATUS_SCSI_TASK_MGMT_FAILED)) {
2088 if (mpt_HardResetHandler(ioc, NO_SLEEP) < 0) {
2089 printk((KERN_WARNING
2090 " Firmware Reload FAILED!!\n"));
2091 }
2092 }
2093 }
2094 } else {
2095 dtmprintk((MYIOC_s_WARN_FMT " TaskMgmt SUCCESS\n", ioc->name));
2096
2097 hd->abortSCpnt = NULL;
2098
2099 }
2100 }
2101
2102 spin_lock_irqsave(&ioc->FreeQlock, flags);
2103 hd->tmPending = 0;
2104 spin_unlock_irqrestore(&ioc->FreeQlock, flags);
2105 hd->tmState = TM_STATE_NONE;
2106
2107 return 1;
2108}
2109
2110/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
2111/*
2112 * This is anyones guess quite frankly.
2113 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04002114int
Linus Torvalds1da177e2005-04-16 15:20:36 -07002115mptscsih_bios_param(struct scsi_device * sdev, struct block_device *bdev,
2116 sector_t capacity, int geom[])
2117{
2118 int heads;
2119 int sectors;
2120 sector_t cylinders;
2121 ulong dummy;
2122
2123 heads = 64;
2124 sectors = 32;
2125
2126 dummy = heads * sectors;
2127 cylinders = capacity;
2128 sector_div(cylinders,dummy);
2129
2130 /*
2131 * Handle extended translation size for logical drives
2132 * > 1Gb
2133 */
2134 if ((ulong)capacity >= 0x200000) {
2135 heads = 255;
2136 sectors = 63;
2137 dummy = heads * sectors;
2138 cylinders = capacity;
2139 sector_div(cylinders,dummy);
2140 }
2141
2142 /* return result */
2143 geom[0] = heads;
2144 geom[1] = sectors;
2145 geom[2] = cylinders;
2146
2147 dprintk((KERN_NOTICE
2148 ": bios_param: Id=%i Lun=%i Channel=%i CHS=%i/%i/%i\n",
2149 sdev->id, sdev->lun,sdev->channel,(int)cylinders,heads,sectors));
2150
2151 return 0;
2152}
2153
2154/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
2155/*
2156 * OS entry point to allow host driver to alloc memory
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002157 * for each scsi target. Called once per device the bus scan.
Linus Torvalds1da177e2005-04-16 15:20:36 -07002158 * Return non-zero if allocation fails.
Linus Torvalds1da177e2005-04-16 15:20:36 -07002159 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04002160int
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002161mptscsih_target_alloc(struct scsi_target *starget)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002162{
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002163 VirtTarget *vtarget;
2164
2165 vtarget = kmalloc(sizeof(VirtTarget), GFP_KERNEL);
2166 if (!vtarget)
2167 return -ENOMEM;
2168 memset(vtarget, 0, sizeof(VirtTarget));
2169 starget->hostdata = vtarget;
2170 return 0;
2171}
2172
2173/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
2174/*
2175 * OS entry point to allow host driver to alloc memory
2176 * for each scsi device. Called once per device the bus scan.
2177 * Return non-zero if allocation fails.
2178 */
2179int
2180mptscsih_slave_alloc(struct scsi_device *sdev)
2181{
2182 struct Scsi_Host *host = sdev->host;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002183 MPT_SCSI_HOST *hd = (MPT_SCSI_HOST *)host->hostdata;
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002184 VirtTarget *vtarget;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002185 VirtDevice *vdev;
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002186 struct scsi_target *starget;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002187
2188 vdev = kmalloc(sizeof(VirtDevice), GFP_KERNEL);
2189 if (!vdev) {
2190 printk(MYIOC_s_ERR_FMT "slave_alloc kmalloc(%zd) FAILED!\n",
2191 hd->ioc->name, sizeof(VirtDevice));
2192 return -ENOMEM;
2193 }
2194
2195 memset(vdev, 0, sizeof(VirtDevice));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002196 vdev->ioc_id = hd->ioc->id;
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002197 vdev->target_id = sdev->id;
2198 vdev->bus_id = sdev->channel;
2199 vdev->lun = sdev->lun;
2200 sdev->hostdata = vdev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002201
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002202 starget = scsi_target(sdev);
2203 vtarget = starget->hostdata;
2204 vdev->vtarget = vtarget;
2205
2206 if (vtarget->num_luns == 0) {
2207 hd->Targets[sdev->id] = vtarget;
2208 vtarget->ioc_id = hd->ioc->id;
2209 vtarget->tflags = MPT_TARGET_FLAGS_Q_YES;
2210 vtarget->target_id = sdev->id;
2211 vtarget->bus_id = sdev->channel;
2212 if (hd->ioc->bus_type == SPI) {
2213 if (hd->ioc->raid_data.isRaid & (1 << sdev->id)) {
2214 vtarget->raidVolume = 1;
2215 ddvtprintk((KERN_INFO
2216 "RAID Volume @ id %d\n", sdev->id));
2217 }
2218 } else {
2219 vtarget->tflags |= MPT_TARGET_FLAGS_VALID_INQUIRY;
2220 }
2221 }
2222 vtarget->num_luns++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002223 return 0;
2224}
2225
Linus Torvalds1da177e2005-04-16 15:20:36 -07002226/*
2227 * OS entry point to allow for host driver to free allocated memory
2228 * Called if no device present or device being unloaded
2229 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04002230void
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002231mptscsih_target_destroy(struct scsi_target *starget)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002232{
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002233 if (starget->hostdata)
2234 kfree(starget->hostdata);
2235 starget->hostdata = NULL;
2236}
2237
2238/*
2239 * OS entry point to allow for host driver to free allocated memory
2240 * Called if no device present or device being unloaded
2241 */
2242void
2243mptscsih_slave_destroy(struct scsi_device *sdev)
2244{
2245 struct Scsi_Host *host = sdev->host;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002246 MPT_SCSI_HOST *hd = (MPT_SCSI_HOST *)host->hostdata;
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002247 VirtTarget *vtarget;
2248 VirtDevice *vdevice;
2249 struct scsi_target *starget;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002250
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002251 starget = scsi_target(sdev);
2252 vtarget = starget->hostdata;
2253 vdevice = sdev->hostdata;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002254
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002255 mptscsih_search_running_cmds(hd, vdevice);
2256 vtarget->luns[0] &= ~(1 << vdevice->lun);
2257 vtarget->num_luns--;
2258 if (vtarget->num_luns == 0) {
2259 mptscsih_negotiate_to_asyn_narrow(hd, vtarget);
2260 if (hd->ioc->bus_type == SPI) {
2261 if (mptscsih_is_phys_disk(hd->ioc, vtarget->target_id)) {
2262 hd->ioc->spi_data.forceDv |= MPT_SCSICFG_RELOAD_IOC_PG3;
2263 } else {
2264 hd->ioc->spi_data.dvStatus[vtarget->target_id] =
2265 MPT_SCSICFG_NEGOTIATE;
2266 if (!hd->negoNvram) {
2267 hd->ioc->spi_data.dvStatus[vtarget->target_id] |=
2268 MPT_SCSICFG_DV_NOT_DONE;
2269 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002270 }
2271 }
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002272 hd->Targets[sdev->id] = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002273 }
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002274 mptscsih_synchronize_cache(hd, vdevice);
2275 kfree(vdevice);
2276 sdev->hostdata = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002277}
2278
Moore, Eric Dean6e3815b2005-06-24 12:18:57 -06002279/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
2280/*
2281 * mptscsih_change_queue_depth - This function will set a devices queue depth
2282 * @sdev: per scsi_device pointer
2283 * @qdepth: requested queue depth
2284 *
2285 * Adding support for new 'change_queue_depth' api.
2286*/
2287int
2288mptscsih_change_queue_depth(struct scsi_device *sdev, int qdepth)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002289{
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002290 MPT_SCSI_HOST *hd = (MPT_SCSI_HOST *)sdev->host->hostdata;
2291 VirtTarget *vtarget;
2292 struct scsi_target *starget;
2293 int max_depth;
2294 int tagged;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002295
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002296 starget = scsi_target(sdev);
2297 vtarget = starget->hostdata;
Moore, Eric Dean6e3815b2005-06-24 12:18:57 -06002298
Moore, Eric Deana9b29372005-11-16 18:54:20 -07002299 if (hd->ioc->bus_type == SPI) {
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002300 if (vtarget->tflags & MPT_TARGET_FLAGS_VALID_INQUIRY) {
2301 if (!(vtarget->tflags & MPT_TARGET_FLAGS_Q_YES))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002302 max_depth = 1;
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002303 else if (((vtarget->inq_data[0] & 0x1f) == 0x00) &&
2304 (vtarget->minSyncFactor <= MPT_ULTRA160 ))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002305 max_depth = MPT_SCSI_CMD_PER_DEV_HIGH;
2306 else
2307 max_depth = MPT_SCSI_CMD_PER_DEV_LOW;
2308 } else {
2309 /* error case - No Inq. Data */
2310 max_depth = 1;
2311 }
2312 } else
2313 max_depth = MPT_SCSI_CMD_PER_DEV_HIGH;
2314
2315 if (qdepth > max_depth)
2316 qdepth = max_depth;
2317 if (qdepth == 1)
2318 tagged = 0;
2319 else
2320 tagged = MSG_SIMPLE_TAG;
2321
Moore, Eric Dean6e3815b2005-06-24 12:18:57 -06002322 scsi_adjust_queue_depth(sdev, tagged, qdepth);
2323 return sdev->queue_depth;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002324}
2325
Linus Torvalds1da177e2005-04-16 15:20:36 -07002326/*
2327 * OS entry point to adjust the queue_depths on a per-device basis.
2328 * Called once per device the bus scan. Use it to force the queue_depth
2329 * member to 1 if a device does not support Q tags.
2330 * Return non-zero if fails.
2331 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04002332int
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002333mptscsih_slave_configure(struct scsi_device *sdev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002334{
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002335 struct Scsi_Host *sh = sdev->host;
2336 VirtTarget *vtarget;
2337 VirtDevice *vdevice;
2338 struct scsi_target *starget;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002339 MPT_SCSI_HOST *hd = (MPT_SCSI_HOST *)sh->hostdata;
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002340 int indexed_lun, lun_index;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002341
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002342 starget = scsi_target(sdev);
2343 vtarget = starget->hostdata;
2344 vdevice = sdev->hostdata;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002345
2346 dsprintk((MYIOC_s_INFO_FMT
2347 "device @ %p, id=%d, LUN=%d, channel=%d\n",
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002348 hd->ioc->name, sdev, sdev->id, sdev->lun, sdev->channel));
2349 if (hd->ioc->bus_type == SPI)
2350 dsprintk((MYIOC_s_INFO_FMT
2351 "sdtr %d wdtr %d ppr %d inq length=%d\n",
2352 hd->ioc->name, sdev->sdtr, sdev->wdtr,
2353 sdev->ppr, sdev->inquiry_len));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002354
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002355 if (sdev->id > sh->max_id) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002356 /* error case, should never happen */
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002357 scsi_adjust_queue_depth(sdev, 0, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002358 goto slave_configure_exit;
2359 }
2360
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002361 vdevice->configured_lun=1;
2362 lun_index = (vdevice->lun >> 5); /* 32 luns per lun_index */
2363 indexed_lun = (vdevice->lun % 32);
2364 vtarget->luns[lun_index] |= (1 << indexed_lun);
2365 mptscsih_initTarget(hd, vtarget, sdev->lun, sdev->inquiry,
2366 sdev->inquiry_len );
2367 mptscsih_change_queue_depth(sdev, MPT_SCSI_CMD_PER_DEV_HIGH);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002368
2369 dsprintk((MYIOC_s_INFO_FMT
2370 "Queue depth=%d, tflags=%x\n",
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002371 hd->ioc->name, sdev->queue_depth, vtarget->tflags));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002372
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002373 if (hd->ioc->bus_type == SPI)
2374 dsprintk((MYIOC_s_INFO_FMT
2375 "negoFlags=%x, maxOffset=%x, SyncFactor=%x\n",
2376 hd->ioc->name, vtarget->negoFlags, vtarget->maxOffset,
2377 vtarget->minSyncFactor));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002378
2379slave_configure_exit:
2380
2381 dsprintk((MYIOC_s_INFO_FMT
2382 "tagged %d, simple %d, ordered %d\n",
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002383 hd->ioc->name,sdev->tagged_supported, sdev->simple_tags,
2384 sdev->ordered_tags));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002385
2386 return 0;
2387}
2388
Linus Torvalds1da177e2005-04-16 15:20:36 -07002389/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
2390/*
2391 * Private routines...
2392 */
2393
2394/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
2395/* Utility function to copy sense data from the scsi_cmnd buffer
2396 * to the FC and SCSI target structures.
2397 *
2398 */
2399static void
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04002400mptscsih_copy_sense_data(struct scsi_cmnd *sc, MPT_SCSI_HOST *hd, MPT_FRAME_HDR *mf, SCSIIOReply_t *pScsiReply)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002401{
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002402 VirtDevice *vdev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002403 SCSIIORequest_t *pReq;
2404 u32 sense_count = le32_to_cpu(pScsiReply->SenseCount);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002405
2406 /* Get target structure
2407 */
2408 pReq = (SCSIIORequest_t *) mf;
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002409 vdev = sc->device->hostdata;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002410
2411 if (sense_count) {
2412 u8 *sense_data;
2413 int req_index;
2414
2415 /* Copy the sense received into the scsi command block. */
2416 req_index = le16_to_cpu(mf->u.frame.hwhdr.msgctxu.fld.req_idx);
2417 sense_data = ((u8 *)hd->ioc->sense_buf_pool + (req_index * MPT_SENSE_BUFFER_ALLOC));
2418 memcpy(sc->sense_buffer, sense_data, SNS_LEN(sc));
2419
2420 /* Log SMART data (asc = 0x5D, non-IM case only) if required.
2421 */
2422 if ((hd->ioc->events) && (hd->ioc->eventTypes & (1 << MPI_EVENT_SCSI_DEVICE_STATUS_CHANGE))) {
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002423 if ((sense_data[12] == 0x5D) && (vdev->vtarget->raidVolume == 0)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002424 int idx;
2425 MPT_ADAPTER *ioc = hd->ioc;
2426
2427 idx = ioc->eventContext % ioc->eventLogSize;
2428 ioc->events[idx].event = MPI_EVENT_SCSI_DEVICE_STATUS_CHANGE;
2429 ioc->events[idx].eventContext = ioc->eventContext;
2430
2431 ioc->events[idx].data[0] = (pReq->LUN[1] << 24) ||
2432 (MPI_EVENT_SCSI_DEV_STAT_RC_SMART_DATA << 16) ||
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002433 (sc->device->channel << 8) || sc->device->id;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002434
2435 ioc->events[idx].data[1] = (sense_data[13] << 8) || sense_data[12];
2436
2437 ioc->eventContext++;
2438 }
2439 }
2440 } else {
2441 dprintk((MYIOC_s_INFO_FMT "Hmmm... SenseData len=0! (?)\n",
2442 hd->ioc->name));
2443 }
2444}
2445
2446static u32
2447SCPNT_TO_LOOKUP_IDX(struct scsi_cmnd *sc)
2448{
2449 MPT_SCSI_HOST *hd;
2450 int i;
2451
2452 hd = (MPT_SCSI_HOST *) sc->device->host->hostdata;
2453
2454 for (i = 0; i < hd->ioc->req_depth; i++) {
2455 if (hd->ScsiLookup[i] == sc) {
2456 return i;
2457 }
2458 }
2459
2460 return -1;
2461}
2462
2463/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04002464int
Linus Torvalds1da177e2005-04-16 15:20:36 -07002465mptscsih_ioc_reset(MPT_ADAPTER *ioc, int reset_phase)
2466{
2467 MPT_SCSI_HOST *hd;
2468 unsigned long flags;
Moore, Eric Dean466544d2005-09-14 18:09:10 -06002469 int ii;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002470
2471 dtmprintk((KERN_WARNING MYNAM
2472 ": IOC %s_reset routed to SCSI host driver!\n",
2473 reset_phase==MPT_IOC_SETUP_RESET ? "setup" : (
2474 reset_phase==MPT_IOC_PRE_RESET ? "pre" : "post")));
2475
2476 /* If a FW reload request arrives after base installed but
2477 * before all scsi hosts have been attached, then an alt_ioc
2478 * may have a NULL sh pointer.
2479 */
2480 if ((ioc->sh == NULL) || (ioc->sh->hostdata == NULL))
2481 return 0;
2482 else
2483 hd = (MPT_SCSI_HOST *) ioc->sh->hostdata;
2484
2485 if (reset_phase == MPT_IOC_SETUP_RESET) {
2486 dtmprintk((MYIOC_s_WARN_FMT "Setup-Diag Reset\n", ioc->name));
2487
2488 /* Clean Up:
2489 * 1. Set Hard Reset Pending Flag
2490 * All new commands go to doneQ
2491 */
2492 hd->resetPending = 1;
2493
2494 } else if (reset_phase == MPT_IOC_PRE_RESET) {
2495 dtmprintk((MYIOC_s_WARN_FMT "Pre-Diag Reset\n", ioc->name));
2496
2497 /* 2. Flush running commands
2498 * Clean ScsiLookup (and associated memory)
2499 * AND clean mytaskQ
2500 */
2501
2502 /* 2b. Reply to OS all known outstanding I/O commands.
2503 */
2504 mptscsih_flush_running_cmds(hd);
2505
2506 /* 2c. If there was an internal command that
2507 * has not completed, configuration or io request,
2508 * free these resources.
2509 */
2510 if (hd->cmdPtr) {
2511 del_timer(&hd->timer);
2512 mpt_free_msg_frame(ioc, hd->cmdPtr);
2513 }
2514
2515 dtmprintk((MYIOC_s_WARN_FMT "Pre-Reset complete.\n", ioc->name));
2516
2517 } else {
2518 dtmprintk((MYIOC_s_WARN_FMT "Post-Diag Reset\n", ioc->name));
2519
2520 /* Once a FW reload begins, all new OS commands are
2521 * redirected to the doneQ w/ a reset status.
2522 * Init all control structures.
2523 */
2524
2525 /* ScsiLookup initialization
2526 */
Moore, Eric Dean466544d2005-09-14 18:09:10 -06002527 for (ii=0; ii < hd->ioc->req_depth; ii++)
2528 hd->ScsiLookup[ii] = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002529
2530 /* 2. Chain Buffer initialization
2531 */
2532
Moore, Eric Deana9b29372005-11-16 18:54:20 -07002533 /* 4. Renegotiate to all devices, if SPI
Linus Torvalds1da177e2005-04-16 15:20:36 -07002534 */
Moore, Eric Deana9b29372005-11-16 18:54:20 -07002535 if (ioc->bus_type == SPI) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002536 dnegoprintk(("writeSDP1: ALL_IDS USE_NVRAM\n"));
2537 mptscsih_writeSDP1(hd, 0, 0, MPT_SCSICFG_ALL_IDS | MPT_SCSICFG_USE_NVRAM);
2538 }
2539
2540 /* 5. Enable new commands to be posted
2541 */
2542 spin_lock_irqsave(&ioc->FreeQlock, flags);
2543 hd->tmPending = 0;
2544 spin_unlock_irqrestore(&ioc->FreeQlock, flags);
2545 hd->resetPending = 0;
2546 hd->tmState = TM_STATE_NONE;
2547
2548 /* 6. If there was an internal command,
2549 * wake this process up.
2550 */
2551 if (hd->cmdPtr) {
2552 /*
2553 * Wake up the original calling thread
2554 */
2555 hd->pLocal = &hd->localReply;
2556 hd->pLocal->completion = MPT_SCANDV_DID_RESET;
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04002557 hd->scandv_wait_done = 1;
2558 wake_up(&hd->scandv_waitq);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002559 hd->cmdPtr = NULL;
2560 }
2561
2562 /* 7. Set flag to force DV and re-read IOC Page 3
2563 */
Moore, Eric Deana9b29372005-11-16 18:54:20 -07002564 if (ioc->bus_type == SPI) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002565 ioc->spi_data.forceDv = MPT_SCSICFG_NEED_DV | MPT_SCSICFG_RELOAD_IOC_PG3;
2566 ddvtprintk(("Set reload IOC Pg3 Flag\n"));
2567 }
2568
2569 dtmprintk((MYIOC_s_WARN_FMT "Post-Reset complete.\n", ioc->name));
2570
2571 }
2572
2573 return 1; /* currently means nothing really */
2574}
2575
2576/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
Moore, Eric Dean466544d2005-09-14 18:09:10 -06002577/* work queue thread to clear the persitency table */
2578static void
2579mptscsih_sas_persist_clear_table(void * arg)
2580{
2581 MPT_ADAPTER *ioc = (MPT_ADAPTER *)arg;
2582
2583 mptbase_sas_persist_operation(ioc, MPI_SAS_OP_CLEAR_NOT_PRESENT);
2584}
2585
2586/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04002587int
Linus Torvalds1da177e2005-04-16 15:20:36 -07002588mptscsih_event_process(MPT_ADAPTER *ioc, EventNotificationReply_t *pEvReply)
2589{
2590 MPT_SCSI_HOST *hd;
2591 u8 event = le32_to_cpu(pEvReply->Event) & 0xFF;
2592
2593 devtprintk((MYIOC_s_INFO_FMT "MPT event (=%02Xh) routed to SCSI host driver!\n",
2594 ioc->name, event));
2595
Moore, Eric Dean466544d2005-09-14 18:09:10 -06002596 if (ioc->sh == NULL ||
2597 ((hd = (MPT_SCSI_HOST *)ioc->sh->hostdata) == NULL))
2598 return 1;
2599
Linus Torvalds1da177e2005-04-16 15:20:36 -07002600 switch (event) {
2601 case MPI_EVENT_UNIT_ATTENTION: /* 03 */
2602 /* FIXME! */
2603 break;
2604 case MPI_EVENT_IOC_BUS_RESET: /* 04 */
2605 case MPI_EVENT_EXT_BUS_RESET: /* 05 */
Moore, Eric Deana9b29372005-11-16 18:54:20 -07002606 if (hd && (ioc->bus_type == SPI) && (hd->soft_resets < -1))
Moore, Eric Dean466544d2005-09-14 18:09:10 -06002607 hd->soft_resets++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002608 break;
2609 case MPI_EVENT_LOGOUT: /* 09 */
2610 /* FIXME! */
2611 break;
2612
2613 /*
2614 * CHECKME! Don't think we need to do
2615 * anything for these, but...
2616 */
2617 case MPI_EVENT_RESCAN: /* 06 */
2618 case MPI_EVENT_LINK_STATUS_CHANGE: /* 07 */
2619 case MPI_EVENT_LOOP_STATE_CHANGE: /* 08 */
2620 /*
2621 * CHECKME! Falling thru...
2622 */
2623 break;
2624
2625 case MPI_EVENT_INTEGRATED_RAID: /* 0B */
Moore, Eric Dean466544d2005-09-14 18:09:10 -06002626 {
Moore, Eric Deanf2ea8672005-11-16 18:54:23 -07002627#ifdef MPTSCSIH_ENABLE_DOMAIN_VALIDATION
Moore, Eric Dean466544d2005-09-14 18:09:10 -06002628 pMpiEventDataRaid_t pRaidEventData =
2629 (pMpiEventDataRaid_t) pEvReply->Data;
Moore, Eric Dean466544d2005-09-14 18:09:10 -06002630 /* Domain Validation Needed */
Moore, Eric Deana9b29372005-11-16 18:54:20 -07002631 if (ioc->bus_type == SPI &&
Moore, Eric Dean466544d2005-09-14 18:09:10 -06002632 pRaidEventData->ReasonCode ==
2633 MPI_EVENT_RAID_RC_DOMAIN_VAL_NEEDED)
2634 mptscsih_set_dvflags_raid(hd, pRaidEventData->PhysDiskNum);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002635#endif
Moore, Eric Dean466544d2005-09-14 18:09:10 -06002636 break;
2637 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002638
Moore, Eric Dean466544d2005-09-14 18:09:10 -06002639 /* Persistent table is full. */
2640 case MPI_EVENT_PERSISTENT_TABLE_FULL:
2641 INIT_WORK(&mptscsih_persistTask,
2642 mptscsih_sas_persist_clear_table,(void *)ioc);
2643 schedule_work(&mptscsih_persistTask);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002644 break;
2645
2646 case MPI_EVENT_NONE: /* 00 */
2647 case MPI_EVENT_LOG_DATA: /* 01 */
2648 case MPI_EVENT_STATE_CHANGE: /* 02 */
2649 case MPI_EVENT_EVENT_CHANGE: /* 0A */
2650 default:
2651 dprintk((KERN_INFO " Ignoring event (=%02Xh)\n", event));
2652 break;
2653 }
2654
2655 return 1; /* currently means nothing really */
2656}
2657
Linus Torvalds1da177e2005-04-16 15:20:36 -07002658/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
2659/*
2660 * mptscsih_initTarget - Target, LUN alloc/free functionality.
2661 * @hd: Pointer to MPT_SCSI_HOST structure
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002662 * @vtarget: per target private data
Linus Torvalds1da177e2005-04-16 15:20:36 -07002663 * @lun: SCSI LUN id
2664 * @data: Pointer to data
2665 * @dlen: Number of INQUIRY bytes
2666 *
2667 * NOTE: It's only SAFE to call this routine if data points to
2668 * sane & valid STANDARD INQUIRY data!
2669 *
2670 * Allocate and initialize memory for this target.
2671 * Save inquiry data.
2672 *
2673 */
2674static void
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002675mptscsih_initTarget(MPT_SCSI_HOST *hd, VirtTarget *vtarget, u8 lun, char *data, int dlen)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002676{
Moore, Eric Dean466544d2005-09-14 18:09:10 -06002677 SpiCfgData *pSpi;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002678 char data_56;
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002679 int inq_len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002680
2681 dinitprintk((MYIOC_s_INFO_FMT "initTarget bus=%d id=%d lun=%d hd=%p\n",
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002682 hd->ioc->name, vtarget->bus_id, vtarget->target_id, lun, hd));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002683
2684 /*
2685 * If the peripheral qualifier filter is enabled then if the target reports a 0x1
2686 * (i.e. The targer is capable of supporting the specified peripheral device type
2687 * on this logical unit; however, the physical device is not currently connected
Christoph Hellwigc6678e02005-08-18 16:24:53 +02002688 * to this logical unit) it will be converted to a 0x3 (i.e. The target is not
Linus Torvalds1da177e2005-04-16 15:20:36 -07002689 * capable of supporting a physical device on this logical unit). This is to work
2690 * around a bug in th emid-layer in some distributions in which the mid-layer will
2691 * continue to try to communicate to the LUN and evntually create a dummy LUN.
2692 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04002693 if (hd->mpt_pq_filter && dlen && (data[0] & 0xE0))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002694 data[0] |= 0x40;
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04002695
Linus Torvalds1da177e2005-04-16 15:20:36 -07002696 /* Is LUN supported? If so, upper 2 bits will be 0
2697 * in first byte of inquiry data.
2698 */
2699 if (data[0] & 0xe0)
2700 return;
2701
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002702 if (vtarget == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002703 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002704
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002705 if (data)
2706 vtarget->type = data[0];
Linus Torvalds1da177e2005-04-16 15:20:36 -07002707
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002708 if (hd->ioc->bus_type != SPI)
2709 return;
2710
2711 if ((data[0] == TYPE_PROCESSOR) && (hd->ioc->spi_data.Saf_Te)) {
2712 /* Treat all Processors as SAF-TE if
2713 * command line option is set */
2714 vtarget->tflags |= MPT_TARGET_FLAGS_SAF_TE_ISSUED;
2715 mptscsih_writeIOCPage4(hd, vtarget->target_id, vtarget->bus_id);
2716 }else if ((data[0] == TYPE_PROCESSOR) &&
2717 !(vtarget->tflags & MPT_TARGET_FLAGS_SAF_TE_ISSUED )) {
2718 if ( dlen > 49 ) {
2719 vtarget->tflags |= MPT_TARGET_FLAGS_VALID_INQUIRY;
2720 if ( data[44] == 'S' &&
2721 data[45] == 'A' &&
2722 data[46] == 'F' &&
2723 data[47] == '-' &&
2724 data[48] == 'T' &&
2725 data[49] == 'E' ) {
2726 vtarget->tflags |= MPT_TARGET_FLAGS_SAF_TE_ISSUED;
2727 mptscsih_writeIOCPage4(hd, vtarget->target_id, vtarget->bus_id);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002728 }
2729 }
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002730 }
2731 if (!(vtarget->tflags & MPT_TARGET_FLAGS_VALID_INQUIRY)) {
2732 inq_len = dlen < 8 ? dlen : 8;
2733 memcpy (vtarget->inq_data, data, inq_len);
2734 /* If have not done DV, set the DV flag.
2735 */
2736 pSpi = &hd->ioc->spi_data;
2737 if ((data[0] == TYPE_TAPE) || (data[0] == TYPE_PROCESSOR)) {
2738 if (pSpi->dvStatus[vtarget->target_id] & MPT_SCSICFG_DV_NOT_DONE)
2739 pSpi->dvStatus[vtarget->target_id] |= MPT_SCSICFG_NEED_DV;
2740 }
2741 vtarget->tflags |= MPT_TARGET_FLAGS_VALID_INQUIRY;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002742
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002743 data_56 = 0x0F; /* Default to full capabilities if Inq data length is < 57 */
2744 if (dlen > 56) {
2745 if ( (!(vtarget->tflags & MPT_TARGET_FLAGS_VALID_56))) {
2746 /* Update the target capabilities
Linus Torvalds1da177e2005-04-16 15:20:36 -07002747 */
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002748 data_56 = data[56];
2749 vtarget->tflags |= MPT_TARGET_FLAGS_VALID_56;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002750 }
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002751 }
2752 mptscsih_setTargetNegoParms(hd, vtarget, data_56);
2753 } else {
2754 /* Initial Inquiry may not request enough data bytes to
2755 * obtain byte 57. DV will; if target doesn't return
2756 * at least 57 bytes, data[56] will be zero. */
2757 if (dlen > 56) {
2758 if ( (!(vtarget->tflags & MPT_TARGET_FLAGS_VALID_56))) {
2759 /* Update the target capabilities
2760 */
2761 data_56 = data[56];
2762 vtarget->tflags |= MPT_TARGET_FLAGS_VALID_56;
2763 mptscsih_setTargetNegoParms(hd, vtarget, data_56);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002764 }
2765 }
2766 }
2767}
2768
2769/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
2770/*
2771 * Update the target negotiation parameters based on the
2772 * the Inquiry data, adapter capabilities, and NVRAM settings.
2773 *
2774 */
2775static void
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002776mptscsih_setTargetNegoParms(MPT_SCSI_HOST *hd, VirtTarget *target, char byte56)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002777{
Moore, Eric Dean466544d2005-09-14 18:09:10 -06002778 SpiCfgData *pspi_data = &hd->ioc->spi_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002779 int id = (int) target->target_id;
2780 int nvram;
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002781 VirtTarget *vtarget;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002782 int ii;
2783 u8 width = MPT_NARROW;
2784 u8 factor = MPT_ASYNC;
2785 u8 offset = 0;
2786 u8 version, nfactor;
2787 u8 noQas = 1;
2788
2789 target->negoFlags = pspi_data->noQas;
2790
2791 /* noQas == 0 => device supports QAS. Need byte 56 of Inq to determine
2792 * support. If available, default QAS to off and allow enabling.
2793 * If not available, default QAS to on, turn off for non-disks.
2794 */
2795
2796 /* Set flags based on Inquiry data
2797 */
2798 version = target->inq_data[2] & 0x07;
2799 if (version < 2) {
2800 width = 0;
2801 factor = MPT_ULTRA2;
2802 offset = pspi_data->maxSyncOffset;
2803 target->tflags &= ~MPT_TARGET_FLAGS_Q_YES;
2804 } else {
2805 if (target->inq_data[7] & 0x20) {
2806 width = 1;
2807 }
2808
2809 if (target->inq_data[7] & 0x10) {
2810 factor = pspi_data->minSyncFactor;
2811 if (target->tflags & MPT_TARGET_FLAGS_VALID_56) {
2812 /* bits 2 & 3 show Clocking support */
2813 if ((byte56 & 0x0C) == 0)
2814 factor = MPT_ULTRA2;
2815 else {
2816 if ((byte56 & 0x03) == 0)
2817 factor = MPT_ULTRA160;
2818 else {
2819 factor = MPT_ULTRA320;
2820 if (byte56 & 0x02)
2821 {
2822 ddvtprintk((KERN_INFO "Enabling QAS due to byte56=%02x on id=%d!\n", byte56, id));
2823 noQas = 0;
2824 }
2825 if (target->inq_data[0] == TYPE_TAPE) {
2826 if (byte56 & 0x01)
2827 target->negoFlags |= MPT_TAPE_NEGO_IDP;
2828 }
2829 }
2830 }
2831 } else {
2832 ddvtprintk((KERN_INFO "Enabling QAS on id=%d due to ~TARGET_FLAGS_VALID_56!\n", id));
2833 noQas = 0;
2834 }
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04002835
Linus Torvalds1da177e2005-04-16 15:20:36 -07002836 offset = pspi_data->maxSyncOffset;
2837
2838 /* If RAID, never disable QAS
2839 * else if non RAID, do not disable
2840 * QAS if bit 1 is set
2841 * bit 1 QAS support, non-raid only
2842 * bit 0 IU support
2843 */
2844 if (target->raidVolume == 1) {
2845 noQas = 0;
2846 }
2847 } else {
2848 factor = MPT_ASYNC;
2849 offset = 0;
2850 }
2851 }
2852
2853 if ( (target->inq_data[7] & 0x02) == 0) {
2854 target->tflags &= ~MPT_TARGET_FLAGS_Q_YES;
2855 }
2856
2857 /* Update tflags based on NVRAM settings. (SCSI only)
2858 */
2859 if (pspi_data->nvram && (pspi_data->nvram[id] != MPT_HOST_NVRAM_INVALID)) {
2860 nvram = pspi_data->nvram[id];
2861 nfactor = (nvram & MPT_NVRAM_SYNC_MASK) >> 8;
2862
2863 if (width)
2864 width = nvram & MPT_NVRAM_WIDE_DISABLE ? 0 : 1;
2865
2866 if (offset > 0) {
2867 /* Ensure factor is set to the
2868 * maximum of: adapter, nvram, inquiry
2869 */
2870 if (nfactor) {
2871 if (nfactor < pspi_data->minSyncFactor )
2872 nfactor = pspi_data->minSyncFactor;
2873
2874 factor = max(factor, nfactor);
2875 if (factor == MPT_ASYNC)
2876 offset = 0;
2877 } else {
2878 offset = 0;
2879 factor = MPT_ASYNC;
2880 }
2881 } else {
2882 factor = MPT_ASYNC;
2883 }
2884 }
2885
2886 /* Make sure data is consistent
2887 */
2888 if ((!width) && (factor < MPT_ULTRA2)) {
2889 factor = MPT_ULTRA2;
2890 }
2891
2892 /* Save the data to the target structure.
2893 */
2894 target->minSyncFactor = factor;
2895 target->maxOffset = offset;
2896 target->maxWidth = width;
2897
2898 target->tflags |= MPT_TARGET_FLAGS_VALID_NEGO;
2899
2900 /* Disable unused features.
2901 */
2902 if (!width)
2903 target->negoFlags |= MPT_TARGET_NO_NEGO_WIDE;
2904
2905 if (!offset)
2906 target->negoFlags |= MPT_TARGET_NO_NEGO_SYNC;
2907
2908 if ( factor > MPT_ULTRA320 )
2909 noQas = 0;
2910
2911 /* GEM, processor WORKAROUND
2912 */
2913 if ((target->inq_data[0] == TYPE_PROCESSOR) || (target->inq_data[0] > 0x08)) {
2914 target->negoFlags |= (MPT_TARGET_NO_NEGO_WIDE | MPT_TARGET_NO_NEGO_SYNC);
2915 pspi_data->dvStatus[id] |= MPT_SCSICFG_BLK_NEGO;
2916 } else {
2917 if (noQas && (pspi_data->noQas == 0)) {
2918 pspi_data->noQas |= MPT_TARGET_NO_NEGO_QAS;
2919 target->negoFlags |= MPT_TARGET_NO_NEGO_QAS;
2920
2921 /* Disable QAS in a mixed configuration case
2922 */
2923
2924 ddvtprintk((KERN_INFO "Disabling QAS due to noQas=%02x on id=%d!\n", noQas, id));
2925 for (ii = 0; ii < id; ii++) {
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002926 if ( (vtarget = hd->Targets[ii]) ) {
2927 vtarget->negoFlags |= MPT_TARGET_NO_NEGO_QAS;
2928 mptscsih_writeSDP1(hd, 0, ii, vtarget->negoFlags);
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04002929 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002930 }
2931 }
2932 }
2933
2934 /* Write SDP1 on this I/O to this target */
2935 if (pspi_data->dvStatus[id] & MPT_SCSICFG_NEGOTIATE) {
2936 ddvtprintk((KERN_INFO "MPT_SCSICFG_NEGOTIATE on id=%d!\n", id));
2937 mptscsih_writeSDP1(hd, 0, id, hd->negoNvram);
2938 pspi_data->dvStatus[id] &= ~MPT_SCSICFG_NEGOTIATE;
2939 } else if (pspi_data->dvStatus[id] & MPT_SCSICFG_BLK_NEGO) {
2940 ddvtprintk((KERN_INFO "MPT_SCSICFG_BLK_NEGO on id=%d!\n", id));
2941 mptscsih_writeSDP1(hd, 0, id, MPT_SCSICFG_BLK_NEGO);
2942 pspi_data->dvStatus[id] &= ~MPT_SCSICFG_BLK_NEGO;
2943 }
2944}
2945
2946/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
Linus Torvalds1da177e2005-04-16 15:20:36 -07002947/*
2948 * If no Target, bus reset on 1st I/O. Set the flag to
2949 * prevent any future negotiations to this device.
2950 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04002951static void
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002952mptscsih_no_negotiate(MPT_SCSI_HOST *hd, struct scsi_cmnd *sc)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002953{
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002954 VirtDevice *vdev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002955
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07002956 if ((vdev = sc->device->hostdata) != NULL)
2957 hd->ioc->spi_data.dvStatus[vdev->target_id] |= MPT_SCSICFG_BLK_NEGO;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002958 return;
2959}
2960
2961/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
2962/*
2963 * SCSI Config Page functionality ...
2964 */
2965/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
2966/* mptscsih_setDevicePage1Flags - add Requested and Configuration fields flags
2967 * based on width, factor and offset parameters.
2968 * @width: bus width
2969 * @factor: sync factor
2970 * @offset: sync offset
2971 * @requestedPtr: pointer to requested values (updated)
2972 * @configurationPtr: pointer to configuration values (updated)
2973 * @flags: flags to block WDTR or SDTR negotiation
2974 *
2975 * Return: None.
2976 *
2977 * Remark: Called by writeSDP1 and _dv_params
2978 */
2979static void
2980mptscsih_setDevicePage1Flags (u8 width, u8 factor, u8 offset, int *requestedPtr, int *configurationPtr, u8 flags)
2981{
2982 u8 nowide = flags & MPT_TARGET_NO_NEGO_WIDE;
2983 u8 nosync = flags & MPT_TARGET_NO_NEGO_SYNC;
2984
2985 *configurationPtr = 0;
2986 *requestedPtr = width ? MPI_SCSIDEVPAGE1_RP_WIDE : 0;
2987 *requestedPtr |= (offset << 16) | (factor << 8);
2988
2989 if (width && offset && !nowide && !nosync) {
2990 if (factor < MPT_ULTRA160) {
2991 *requestedPtr |= (MPI_SCSIDEVPAGE1_RP_IU + MPI_SCSIDEVPAGE1_RP_DT);
2992 if ((flags & MPT_TARGET_NO_NEGO_QAS) == 0)
2993 *requestedPtr |= MPI_SCSIDEVPAGE1_RP_QAS;
2994 if (flags & MPT_TAPE_NEGO_IDP)
2995 *requestedPtr |= 0x08000000;
2996 } else if (factor < MPT_ULTRA2) {
2997 *requestedPtr |= MPI_SCSIDEVPAGE1_RP_DT;
2998 }
2999 }
3000
3001 if (nowide)
3002 *configurationPtr |= MPI_SCSIDEVPAGE1_CONF_WDTR_DISALLOWED;
3003
3004 if (nosync)
3005 *configurationPtr |= MPI_SCSIDEVPAGE1_CONF_SDTR_DISALLOWED;
3006
3007 return;
3008}
3009
3010/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
3011/* mptscsih_writeSDP1 - write SCSI Device Page 1
3012 * @hd: Pointer to a SCSI Host Strucutre
3013 * @portnum: IOC port number
3014 * @target_id: writeSDP1 for single ID
3015 * @flags: MPT_SCSICFG_ALL_IDS, MPT_SCSICFG_USE_NVRAM, MPT_SCSICFG_BLK_NEGO
3016 *
3017 * Return: -EFAULT if read of config page header fails
3018 * or 0 if success.
3019 *
3020 * Remark: If a target has been found, the settings from the
3021 * target structure are used, else the device is set
3022 * to async/narrow.
3023 *
3024 * Remark: Called during init and after a FW reload.
3025 * Remark: We do not wait for a return, write pages sequentially.
3026 */
3027static int
3028mptscsih_writeSDP1(MPT_SCSI_HOST *hd, int portnum, int target_id, int flags)
3029{
3030 MPT_ADAPTER *ioc = hd->ioc;
3031 Config_t *pReq;
3032 SCSIDevicePage1_t *pData;
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07003033 VirtTarget *vtarget=NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003034 MPT_FRAME_HDR *mf;
3035 dma_addr_t dataDma;
3036 u16 req_idx;
3037 u32 frameOffset;
3038 u32 requested, configuration, flagsLength;
3039 int ii, nvram;
3040 int id = 0, maxid = 0;
3041 u8 width;
3042 u8 factor;
3043 u8 offset;
3044 u8 bus = 0;
3045 u8 negoFlags;
3046 u8 maxwidth, maxoffset, maxfactor;
3047
3048 if (ioc->spi_data.sdp1length == 0)
3049 return 0;
3050
3051 if (flags & MPT_SCSICFG_ALL_IDS) {
3052 id = 0;
3053 maxid = ioc->sh->max_id - 1;
3054 } else if (ioc->sh) {
3055 id = target_id;
3056 maxid = min_t(int, id, ioc->sh->max_id - 1);
3057 }
3058
3059 for (; id <= maxid; id++) {
3060
3061 if (id == ioc->pfacts[portnum].PortSCSIID)
3062 continue;
3063
3064 /* Use NVRAM to get adapter and target maximums
3065 * Data over-riden by target structure information, if present
3066 */
3067 maxwidth = ioc->spi_data.maxBusWidth;
3068 maxoffset = ioc->spi_data.maxSyncOffset;
3069 maxfactor = ioc->spi_data.minSyncFactor;
3070 if (ioc->spi_data.nvram && (ioc->spi_data.nvram[id] != MPT_HOST_NVRAM_INVALID)) {
3071 nvram = ioc->spi_data.nvram[id];
3072
3073 if (maxwidth)
3074 maxwidth = nvram & MPT_NVRAM_WIDE_DISABLE ? 0 : 1;
3075
3076 if (maxoffset > 0) {
3077 maxfactor = (nvram & MPT_NVRAM_SYNC_MASK) >> 8;
3078 if (maxfactor == 0) {
3079 /* Key for async */
3080 maxfactor = MPT_ASYNC;
3081 maxoffset = 0;
3082 } else if (maxfactor < ioc->spi_data.minSyncFactor) {
3083 maxfactor = ioc->spi_data.minSyncFactor;
3084 }
3085 } else
3086 maxfactor = MPT_ASYNC;
3087 }
3088
3089 /* Set the negotiation flags.
3090 */
3091 negoFlags = ioc->spi_data.noQas;
3092 if (!maxwidth)
3093 negoFlags |= MPT_TARGET_NO_NEGO_WIDE;
3094
3095 if (!maxoffset)
3096 negoFlags |= MPT_TARGET_NO_NEGO_SYNC;
3097
3098 if (flags & MPT_SCSICFG_USE_NVRAM) {
3099 width = maxwidth;
3100 factor = maxfactor;
3101 offset = maxoffset;
3102 } else {
3103 width = 0;
3104 factor = MPT_ASYNC;
3105 offset = 0;
3106 //negoFlags = 0;
3107 //negoFlags = MPT_TARGET_NO_NEGO_SYNC;
3108 }
3109
3110 /* If id is not a raid volume, get the updated
3111 * transmission settings from the target structure.
3112 */
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07003113 if (hd->Targets && (vtarget = hd->Targets[id]) && !vtarget->raidVolume) {
3114 width = vtarget->maxWidth;
3115 factor = vtarget->minSyncFactor;
3116 offset = vtarget->maxOffset;
3117 negoFlags = vtarget->negoFlags;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003118 }
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04003119
Linus Torvalds1da177e2005-04-16 15:20:36 -07003120#ifdef MPTSCSIH_ENABLE_DOMAIN_VALIDATION
3121 /* Force to async and narrow if DV has not been executed
3122 * for this ID
3123 */
3124 if ((hd->ioc->spi_data.dvStatus[id] & MPT_SCSICFG_DV_NOT_DONE) != 0) {
3125 width = 0;
3126 factor = MPT_ASYNC;
3127 offset = 0;
3128 }
3129#endif
3130
3131 if (flags & MPT_SCSICFG_BLK_NEGO)
Moore, Eric Dean466544d2005-09-14 18:09:10 -06003132 negoFlags |= MPT_TARGET_NO_NEGO_WIDE | MPT_TARGET_NO_NEGO_SYNC;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003133
3134 mptscsih_setDevicePage1Flags(width, factor, offset,
3135 &requested, &configuration, negoFlags);
3136 dnegoprintk(("writeSDP1: id=%d width=%d factor=%x offset=%x negoFlags=%x request=%x config=%x\n",
3137 target_id, width, factor, offset, negoFlags, requested, configuration));
3138
3139 /* Get a MF for this command.
3140 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04003141 if ((mf = mpt_get_msg_frame(ioc->DoneCtx, ioc)) == NULL) {
Christoph Hellwigc6678e02005-08-18 16:24:53 +02003142 dfailprintk((MYIOC_s_WARN_FMT "write SDP1: no msg frames!\n",
3143 ioc->name));
Linus Torvalds1da177e2005-04-16 15:20:36 -07003144 return -EAGAIN;
3145 }
3146
3147 ddvprintk((MYIOC_s_INFO_FMT "WriteSDP1 (mf=%p, id=%d, req=0x%x, cfg=0x%x)\n",
3148 hd->ioc->name, mf, id, requested, configuration));
3149
3150
3151 /* Set the request and the data pointers.
3152 * Request takes: 36 bytes (32 bit SGE)
3153 * SCSI Device Page 1 requires 16 bytes
3154 * 40 + 16 <= size of SCSI IO Request = 56 bytes
3155 * and MF size >= 64 bytes.
3156 * Place data at end of MF.
3157 */
3158 pReq = (Config_t *)mf;
3159
3160 req_idx = le16_to_cpu(mf->u.frame.hwhdr.msgctxu.fld.req_idx);
3161 frameOffset = ioc->req_sz - sizeof(SCSIDevicePage1_t);
3162
3163 pData = (SCSIDevicePage1_t *)((u8 *) mf + frameOffset);
3164 dataDma = ioc->req_frames_dma + (req_idx * ioc->req_sz) + frameOffset;
3165
3166 /* Complete the request frame (same for all requests).
3167 */
3168 pReq->Action = MPI_CONFIG_ACTION_PAGE_WRITE_CURRENT;
3169 pReq->Reserved = 0;
3170 pReq->ChainOffset = 0;
3171 pReq->Function = MPI_FUNCTION_CONFIG;
3172 pReq->ExtPageLength = 0;
3173 pReq->ExtPageType = 0;
3174 pReq->MsgFlags = 0;
3175 for (ii=0; ii < 8; ii++) {
3176 pReq->Reserved2[ii] = 0;
3177 }
3178 pReq->Header.PageVersion = ioc->spi_data.sdp1version;
3179 pReq->Header.PageLength = ioc->spi_data.sdp1length;
3180 pReq->Header.PageNumber = 1;
3181 pReq->Header.PageType = MPI_CONFIG_PAGETYPE_SCSI_DEVICE;
3182 pReq->PageAddress = cpu_to_le32(id | (bus << 8 ));
3183
3184 /* Add a SGE to the config request.
3185 */
3186 flagsLength = MPT_SGE_FLAGS_SSIMPLE_WRITE | ioc->spi_data.sdp1length * 4;
3187
3188 mpt_add_sge((char *)&pReq->PageBufferSGE, flagsLength, dataDma);
3189
3190 /* Set up the common data portion
3191 */
3192 pData->Header.PageVersion = pReq->Header.PageVersion;
3193 pData->Header.PageLength = pReq->Header.PageLength;
3194 pData->Header.PageNumber = pReq->Header.PageNumber;
3195 pData->Header.PageType = pReq->Header.PageType;
3196 pData->RequestedParameters = cpu_to_le32(requested);
3197 pData->Reserved = 0;
3198 pData->Configuration = cpu_to_le32(configuration);
3199
3200 dprintk((MYIOC_s_INFO_FMT
3201 "write SDP1: id %d pgaddr 0x%x req 0x%x config 0x%x\n",
3202 ioc->name, id, (id | (bus<<8)),
3203 requested, configuration));
3204
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04003205 mpt_put_msg_frame(ioc->DoneCtx, ioc, mf);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003206 }
3207
3208 return 0;
3209}
3210
3211/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
3212/* mptscsih_writeIOCPage4 - write IOC Page 4
3213 * @hd: Pointer to a SCSI Host Structure
3214 * @target_id: write IOC Page4 for this ID & Bus
3215 *
3216 * Return: -EAGAIN if unable to obtain a Message Frame
3217 * or 0 if success.
3218 *
3219 * Remark: We do not wait for a return, write pages sequentially.
3220 */
3221static int
3222mptscsih_writeIOCPage4(MPT_SCSI_HOST *hd, int target_id, int bus)
3223{
3224 MPT_ADAPTER *ioc = hd->ioc;
3225 Config_t *pReq;
3226 IOCPage4_t *IOCPage4Ptr;
3227 MPT_FRAME_HDR *mf;
3228 dma_addr_t dataDma;
3229 u16 req_idx;
3230 u32 frameOffset;
3231 u32 flagsLength;
3232 int ii;
3233
3234 /* Get a MF for this command.
3235 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04003236 if ((mf = mpt_get_msg_frame(ioc->DoneCtx, ioc)) == NULL) {
Christoph Hellwigc6678e02005-08-18 16:24:53 +02003237 dfailprintk((MYIOC_s_WARN_FMT "writeIOCPage4 : no msg frames!\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07003238 ioc->name));
3239 return -EAGAIN;
3240 }
3241
3242 /* Set the request and the data pointers.
3243 * Place data at end of MF.
3244 */
3245 pReq = (Config_t *)mf;
3246
3247 req_idx = le16_to_cpu(mf->u.frame.hwhdr.msgctxu.fld.req_idx);
3248 frameOffset = ioc->req_sz - sizeof(IOCPage4_t);
3249
3250 /* Complete the request frame (same for all requests).
3251 */
3252 pReq->Action = MPI_CONFIG_ACTION_PAGE_WRITE_CURRENT;
3253 pReq->Reserved = 0;
3254 pReq->ChainOffset = 0;
3255 pReq->Function = MPI_FUNCTION_CONFIG;
3256 pReq->ExtPageLength = 0;
3257 pReq->ExtPageType = 0;
3258 pReq->MsgFlags = 0;
3259 for (ii=0; ii < 8; ii++) {
3260 pReq->Reserved2[ii] = 0;
3261 }
3262
3263 IOCPage4Ptr = ioc->spi_data.pIocPg4;
3264 dataDma = ioc->spi_data.IocPg4_dma;
3265 ii = IOCPage4Ptr->ActiveSEP++;
3266 IOCPage4Ptr->SEP[ii].SEPTargetID = target_id;
3267 IOCPage4Ptr->SEP[ii].SEPBus = bus;
3268 pReq->Header = IOCPage4Ptr->Header;
3269 pReq->PageAddress = cpu_to_le32(target_id | (bus << 8 ));
3270
3271 /* Add a SGE to the config request.
3272 */
3273 flagsLength = MPT_SGE_FLAGS_SSIMPLE_WRITE |
3274 (IOCPage4Ptr->Header.PageLength + ii) * 4;
3275
3276 mpt_add_sge((char *)&pReq->PageBufferSGE, flagsLength, dataDma);
3277
3278 dinitprintk((MYIOC_s_INFO_FMT
3279 "writeIOCPage4: MaxSEP=%d ActiveSEP=%d id=%d bus=%d\n",
3280 ioc->name, IOCPage4Ptr->MaxSEP, IOCPage4Ptr->ActiveSEP, target_id, bus));
3281
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04003282 mpt_put_msg_frame(ioc->DoneCtx, ioc, mf);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003283
3284 return 0;
3285}
3286
3287/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
3288/*
3289 * Bus Scan and Domain Validation functionality ...
3290 */
3291
3292/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
3293/*
3294 * mptscsih_scandv_complete - Scan and DV callback routine registered
3295 * to Fustion MPT (base) driver.
3296 *
3297 * @ioc: Pointer to MPT_ADAPTER structure
3298 * @mf: Pointer to original MPT request frame
3299 * @mr: Pointer to MPT reply frame (NULL if TurboReply)
3300 *
3301 * This routine is called from mpt.c::mpt_interrupt() at the completion
3302 * of any SCSI IO request.
3303 * This routine is registered with the Fusion MPT (base) driver at driver
3304 * load/init time via the mpt_register() API call.
3305 *
3306 * Returns 1 indicating alloc'd request frame ptr should be freed.
3307 *
3308 * Remark: Sets a completion code and (possibly) saves sense data
3309 * in the IOC member localReply structure.
3310 * Used ONLY for DV and other internal commands.
3311 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04003312int
Linus Torvalds1da177e2005-04-16 15:20:36 -07003313mptscsih_scandv_complete(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *mr)
3314{
3315 MPT_SCSI_HOST *hd;
3316 SCSIIORequest_t *pReq;
3317 int completionCode;
3318 u16 req_idx;
3319
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04003320 hd = (MPT_SCSI_HOST *) ioc->sh->hostdata;
3321
Linus Torvalds1da177e2005-04-16 15:20:36 -07003322 if ((mf == NULL) ||
3323 (mf >= MPT_INDEX_2_MFPTR(ioc, ioc->req_depth))) {
3324 printk(MYIOC_s_ERR_FMT
3325 "ScanDvComplete, %s req frame ptr! (=%p)\n",
3326 ioc->name, mf?"BAD":"NULL", (void *) mf);
3327 goto wakeup;
3328 }
3329
Linus Torvalds1da177e2005-04-16 15:20:36 -07003330 del_timer(&hd->timer);
3331 req_idx = le16_to_cpu(mf->u.frame.hwhdr.msgctxu.fld.req_idx);
3332 hd->ScsiLookup[req_idx] = NULL;
3333 pReq = (SCSIIORequest_t *) mf;
3334
3335 if (mf != hd->cmdPtr) {
3336 printk(MYIOC_s_WARN_FMT "ScanDvComplete (mf=%p, cmdPtr=%p, idx=%d)\n",
3337 hd->ioc->name, (void *)mf, (void *) hd->cmdPtr, req_idx);
3338 }
3339 hd->cmdPtr = NULL;
3340
3341 ddvprintk((MYIOC_s_INFO_FMT "ScanDvComplete (mf=%p,mr=%p,idx=%d)\n",
3342 hd->ioc->name, mf, mr, req_idx));
3343
3344 hd->pLocal = &hd->localReply;
3345 hd->pLocal->scsiStatus = 0;
3346
3347 /* If target struct exists, clear sense valid flag.
3348 */
3349 if (mr == NULL) {
3350 completionCode = MPT_SCANDV_GOOD;
3351 } else {
3352 SCSIIOReply_t *pReply;
3353 u16 status;
3354 u8 scsi_status;
3355
3356 pReply = (SCSIIOReply_t *) mr;
3357
3358 status = le16_to_cpu(pReply->IOCStatus) & MPI_IOCSTATUS_MASK;
3359 scsi_status = pReply->SCSIStatus;
3360
3361 ddvtprintk((KERN_NOTICE " IOCStatus=%04xh, SCSIState=%02xh, SCSIStatus=%02xh, IOCLogInfo=%08xh\n",
3362 status, pReply->SCSIState, scsi_status,
3363 le32_to_cpu(pReply->IOCLogInfo)));
3364
3365 switch(status) {
3366
3367 case MPI_IOCSTATUS_SCSI_DEVICE_NOT_THERE: /* 0x0043 */
3368 completionCode = MPT_SCANDV_SELECTION_TIMEOUT;
3369 break;
3370
3371 case MPI_IOCSTATUS_SCSI_IO_DATA_ERROR: /* 0x0046 */
3372 case MPI_IOCSTATUS_SCSI_TASK_TERMINATED: /* 0x0048 */
3373 case MPI_IOCSTATUS_SCSI_IOC_TERMINATED: /* 0x004B */
3374 case MPI_IOCSTATUS_SCSI_EXT_TERMINATED: /* 0x004C */
3375 completionCode = MPT_SCANDV_DID_RESET;
3376 break;
3377
3378 case MPI_IOCSTATUS_SCSI_DATA_UNDERRUN: /* 0x0045 */
3379 case MPI_IOCSTATUS_SCSI_RECOVERED_ERROR: /* 0x0040 */
3380 case MPI_IOCSTATUS_SUCCESS: /* 0x0000 */
3381 if (pReply->Function == MPI_FUNCTION_CONFIG) {
3382 ConfigReply_t *pr = (ConfigReply_t *)mr;
3383 completionCode = MPT_SCANDV_GOOD;
3384 hd->pLocal->header.PageVersion = pr->Header.PageVersion;
3385 hd->pLocal->header.PageLength = pr->Header.PageLength;
3386 hd->pLocal->header.PageNumber = pr->Header.PageNumber;
3387 hd->pLocal->header.PageType = pr->Header.PageType;
3388
3389 } else if (pReply->Function == MPI_FUNCTION_RAID_ACTION) {
3390 /* If the RAID Volume request is successful,
3391 * return GOOD, else indicate that
3392 * some type of error occurred.
3393 */
3394 MpiRaidActionReply_t *pr = (MpiRaidActionReply_t *)mr;
Christoph Hellwig637fa992005-08-18 16:25:44 +02003395 if (le16_to_cpu(pr->ActionStatus) == MPI_RAID_ACTION_ASTATUS_SUCCESS)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003396 completionCode = MPT_SCANDV_GOOD;
3397 else
3398 completionCode = MPT_SCANDV_SOME_ERROR;
3399
3400 } else if (pReply->SCSIState & MPI_SCSI_STATE_AUTOSENSE_VALID) {
3401 u8 *sense_data;
3402 int sz;
3403
3404 /* save sense data in global structure
3405 */
3406 completionCode = MPT_SCANDV_SENSE;
3407 hd->pLocal->scsiStatus = scsi_status;
3408 sense_data = ((u8 *)hd->ioc->sense_buf_pool +
3409 (req_idx * MPT_SENSE_BUFFER_ALLOC));
3410
3411 sz = min_t(int, pReq->SenseBufferLength,
3412 SCSI_STD_SENSE_BYTES);
3413 memcpy(hd->pLocal->sense, sense_data, sz);
3414
3415 ddvprintk((KERN_NOTICE " Check Condition, sense ptr %p\n",
3416 sense_data));
3417 } else if (pReply->SCSIState & MPI_SCSI_STATE_AUTOSENSE_FAILED) {
3418 if (pReq->CDB[0] == INQUIRY)
3419 completionCode = MPT_SCANDV_ISSUE_SENSE;
3420 else
3421 completionCode = MPT_SCANDV_DID_RESET;
3422 }
3423 else if (pReply->SCSIState & MPI_SCSI_STATE_NO_SCSI_STATUS)
3424 completionCode = MPT_SCANDV_DID_RESET;
3425 else if (pReply->SCSIState & MPI_SCSI_STATE_TERMINATED)
3426 completionCode = MPT_SCANDV_DID_RESET;
3427 else {
3428 completionCode = MPT_SCANDV_GOOD;
3429 hd->pLocal->scsiStatus = scsi_status;
3430 }
3431 break;
3432
3433 case MPI_IOCSTATUS_SCSI_PROTOCOL_ERROR: /* 0x0047 */
3434 if (pReply->SCSIState & MPI_SCSI_STATE_TERMINATED)
3435 completionCode = MPT_SCANDV_DID_RESET;
3436 else
3437 completionCode = MPT_SCANDV_SOME_ERROR;
3438 break;
3439
3440 default:
3441 completionCode = MPT_SCANDV_SOME_ERROR;
3442 break;
3443
3444 } /* switch(status) */
3445
3446 ddvtprintk((KERN_NOTICE " completionCode set to %08xh\n",
3447 completionCode));
3448 } /* end of address reply case */
3449
3450 hd->pLocal->completion = completionCode;
3451
3452 /* MF and RF are freed in mpt_interrupt
3453 */
3454wakeup:
3455 /* Free Chain buffers (will never chain) in scan or dv */
3456 //mptscsih_freeChainBuffers(ioc, req_idx);
3457
3458 /*
3459 * Wake up the original calling thread
3460 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04003461 hd->scandv_wait_done = 1;
3462 wake_up(&hd->scandv_waitq);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003463
3464 return 1;
3465}
3466
3467/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
3468/* mptscsih_timer_expired - Call back for timer process.
3469 * Used only for dv functionality.
3470 * @data: Pointer to MPT_SCSI_HOST recast as an unsigned long
3471 *
3472 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04003473void
3474mptscsih_timer_expired(unsigned long data)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003475{
3476 MPT_SCSI_HOST *hd = (MPT_SCSI_HOST *) data;
3477
3478 ddvprintk((MYIOC_s_WARN_FMT "Timer Expired! Cmd %p\n", hd->ioc->name, hd->cmdPtr));
3479
3480 if (hd->cmdPtr) {
3481 MPIHeader_t *cmd = (MPIHeader_t *)hd->cmdPtr;
3482
3483 if (cmd->Function == MPI_FUNCTION_SCSI_IO_REQUEST) {
3484 /* Desire to issue a task management request here.
3485 * TM requests MUST be single threaded.
3486 * If old eh code and no TM current, issue request.
3487 * If new eh code, do nothing. Wait for OS cmd timeout
3488 * for bus reset.
3489 */
3490 ddvtprintk((MYIOC_s_NOTE_FMT "DV Cmd Timeout: NoOp\n", hd->ioc->name));
3491 } else {
3492 /* Perform a FW reload */
3493 if (mpt_HardResetHandler(hd->ioc, NO_SLEEP) < 0) {
3494 printk(MYIOC_s_WARN_FMT "Firmware Reload FAILED!\n", hd->ioc->name);
3495 }
3496 }
3497 } else {
3498 /* This should NEVER happen */
3499 printk(MYIOC_s_WARN_FMT "Null cmdPtr!!!!\n", hd->ioc->name);
3500 }
3501
3502 /* No more processing.
3503 * TM call will generate an interrupt for SCSI TM Management.
3504 * The FW will reply to all outstanding commands, callback will finish cleanup.
3505 * Hard reset clean-up will free all resources.
3506 */
3507 ddvprintk((MYIOC_s_WARN_FMT "Timer Expired Complete!\n", hd->ioc->name));
3508
3509 return;
3510}
3511
3512#ifdef MPTSCSIH_ENABLE_DOMAIN_VALIDATION
3513/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
3514/* mptscsih_do_raid - Format and Issue a RAID volume request message.
3515 * @hd: Pointer to scsi host structure
3516 * @action: What do be done.
3517 * @id: Logical target id.
3518 * @bus: Target locations bus.
3519 *
3520 * Returns: < 0 on a fatal error
3521 * 0 on success
3522 *
3523 * Remark: Wait to return until reply processed by the ISR.
3524 */
3525static int
3526mptscsih_do_raid(MPT_SCSI_HOST *hd, u8 action, INTERNAL_CMD *io)
3527{
3528 MpiRaidActionRequest_t *pReq;
3529 MPT_FRAME_HDR *mf;
3530 int in_isr;
3531
3532 in_isr = in_interrupt();
3533 if (in_isr) {
3534 dprintk((MYIOC_s_WARN_FMT "Internal raid request not allowed in ISR context!\n",
3535 hd->ioc->name));
3536 return -EPERM;
3537 }
3538
3539 /* Get and Populate a free Frame
3540 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04003541 if ((mf = mpt_get_msg_frame(hd->ioc->InternalCtx, hd->ioc)) == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003542 ddvprintk((MYIOC_s_WARN_FMT "_do_raid: no msg frames!\n",
3543 hd->ioc->name));
3544 return -EAGAIN;
3545 }
3546 pReq = (MpiRaidActionRequest_t *)mf;
3547 pReq->Action = action;
3548 pReq->Reserved1 = 0;
3549 pReq->ChainOffset = 0;
3550 pReq->Function = MPI_FUNCTION_RAID_ACTION;
3551 pReq->VolumeID = io->id;
3552 pReq->VolumeBus = io->bus;
3553 pReq->PhysDiskNum = io->physDiskNum;
3554 pReq->MsgFlags = 0;
3555 pReq->Reserved2 = 0;
3556 pReq->ActionDataWord = 0; /* Reserved for this action */
3557 //pReq->ActionDataSGE = 0;
3558
3559 mpt_add_sge((char *)&pReq->ActionDataSGE,
3560 MPT_SGE_FLAGS_SSIMPLE_READ | 0, (dma_addr_t) -1);
3561
3562 ddvprintk((MYIOC_s_INFO_FMT "RAID Volume action %x id %d\n",
3563 hd->ioc->name, action, io->id));
3564
3565 hd->pLocal = NULL;
3566 hd->timer.expires = jiffies + HZ*10; /* 10 second timeout */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04003567 hd->scandv_wait_done = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003568
3569 /* Save cmd pointer, for resource free if timeout or
3570 * FW reload occurs
3571 */
3572 hd->cmdPtr = mf;
3573
3574 add_timer(&hd->timer);
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04003575 mpt_put_msg_frame(hd->ioc->InternalCtx, hd->ioc, mf);
3576 wait_event(hd->scandv_waitq, hd->scandv_wait_done);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003577
3578 if ((hd->pLocal == NULL) || (hd->pLocal->completion != MPT_SCANDV_GOOD))
3579 return -1;
3580
3581 return 0;
3582}
3583#endif /* ~MPTSCSIH_ENABLE_DOMAIN_VALIDATION */
3584
3585/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
3586/**
3587 * mptscsih_do_cmd - Do internal command.
3588 * @hd: MPT_SCSI_HOST pointer
3589 * @io: INTERNAL_CMD pointer.
3590 *
3591 * Issue the specified internally generated command and do command
3592 * specific cleanup. For bus scan / DV only.
3593 * NOTES: If command is Inquiry and status is good,
3594 * initialize a target structure, save the data
3595 *
3596 * Remark: Single threaded access only.
3597 *
3598 * Return:
3599 * < 0 if an illegal command or no resources
3600 *
3601 * 0 if good
3602 *
3603 * > 0 if command complete but some type of completion error.
3604 */
3605static int
3606mptscsih_do_cmd(MPT_SCSI_HOST *hd, INTERNAL_CMD *io)
3607{
3608 MPT_FRAME_HDR *mf;
3609 SCSIIORequest_t *pScsiReq;
3610 SCSIIORequest_t ReqCopy;
3611 int my_idx, ii, dir;
3612 int rc, cmdTimeout;
3613 int in_isr;
3614 char cmdLen;
3615 char CDB[]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
3616 char cmd = io->cmd;
3617
3618 in_isr = in_interrupt();
3619 if (in_isr) {
3620 dprintk((MYIOC_s_WARN_FMT "Internal SCSI IO request not allowed in ISR context!\n",
3621 hd->ioc->name));
3622 return -EPERM;
3623 }
3624
3625
3626 /* Set command specific information
3627 */
3628 switch (cmd) {
3629 case INQUIRY:
3630 cmdLen = 6;
3631 dir = MPI_SCSIIO_CONTROL_READ;
3632 CDB[0] = cmd;
3633 CDB[4] = io->size;
3634 cmdTimeout = 10;
3635 break;
3636
3637 case TEST_UNIT_READY:
3638 cmdLen = 6;
3639 dir = MPI_SCSIIO_CONTROL_READ;
3640 cmdTimeout = 10;
3641 break;
3642
3643 case START_STOP:
3644 cmdLen = 6;
3645 dir = MPI_SCSIIO_CONTROL_READ;
3646 CDB[0] = cmd;
3647 CDB[4] = 1; /*Spin up the disk */
3648 cmdTimeout = 15;
3649 break;
3650
3651 case REQUEST_SENSE:
3652 cmdLen = 6;
3653 CDB[0] = cmd;
3654 CDB[4] = io->size;
3655 dir = MPI_SCSIIO_CONTROL_READ;
3656 cmdTimeout = 10;
3657 break;
3658
3659 case READ_BUFFER:
3660 cmdLen = 10;
3661 dir = MPI_SCSIIO_CONTROL_READ;
3662 CDB[0] = cmd;
3663 if (io->flags & MPT_ICFLAG_ECHO) {
3664 CDB[1] = 0x0A;
3665 } else {
3666 CDB[1] = 0x02;
3667 }
3668
3669 if (io->flags & MPT_ICFLAG_BUF_CAP) {
3670 CDB[1] |= 0x01;
3671 }
3672 CDB[6] = (io->size >> 16) & 0xFF;
3673 CDB[7] = (io->size >> 8) & 0xFF;
3674 CDB[8] = io->size & 0xFF;
3675 cmdTimeout = 10;
3676 break;
3677
3678 case WRITE_BUFFER:
3679 cmdLen = 10;
3680 dir = MPI_SCSIIO_CONTROL_WRITE;
3681 CDB[0] = cmd;
3682 if (io->flags & MPT_ICFLAG_ECHO) {
3683 CDB[1] = 0x0A;
3684 } else {
3685 CDB[1] = 0x02;
3686 }
3687 CDB[6] = (io->size >> 16) & 0xFF;
3688 CDB[7] = (io->size >> 8) & 0xFF;
3689 CDB[8] = io->size & 0xFF;
3690 cmdTimeout = 10;
3691 break;
3692
3693 case RESERVE:
3694 cmdLen = 6;
3695 dir = MPI_SCSIIO_CONTROL_READ;
3696 CDB[0] = cmd;
3697 cmdTimeout = 10;
3698 break;
3699
3700 case RELEASE:
3701 cmdLen = 6;
3702 dir = MPI_SCSIIO_CONTROL_READ;
3703 CDB[0] = cmd;
3704 cmdTimeout = 10;
3705 break;
3706
3707 case SYNCHRONIZE_CACHE:
3708 cmdLen = 10;
3709 dir = MPI_SCSIIO_CONTROL_READ;
3710 CDB[0] = cmd;
3711// CDB[1] = 0x02; /* set immediate bit */
3712 cmdTimeout = 10;
3713 break;
3714
3715 default:
3716 /* Error Case */
3717 return -EFAULT;
3718 }
3719
3720 /* Get and Populate a free Frame
3721 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04003722 if ((mf = mpt_get_msg_frame(hd->ioc->InternalCtx, hd->ioc)) == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003723 ddvprintk((MYIOC_s_WARN_FMT "No msg frames!\n",
3724 hd->ioc->name));
3725 return -EBUSY;
3726 }
3727
3728 pScsiReq = (SCSIIORequest_t *) mf;
3729
3730 /* Get the request index */
3731 my_idx = le16_to_cpu(mf->u.frame.hwhdr.msgctxu.fld.req_idx);
3732 ADD_INDEX_LOG(my_idx); /* for debug */
3733
3734 if (io->flags & MPT_ICFLAG_PHYS_DISK) {
3735 pScsiReq->TargetID = io->physDiskNum;
3736 pScsiReq->Bus = 0;
3737 pScsiReq->ChainOffset = 0;
3738 pScsiReq->Function = MPI_FUNCTION_RAID_SCSI_IO_PASSTHROUGH;
3739 } else {
3740 pScsiReq->TargetID = io->id;
3741 pScsiReq->Bus = io->bus;
3742 pScsiReq->ChainOffset = 0;
3743 pScsiReq->Function = MPI_FUNCTION_SCSI_IO_REQUEST;
3744 }
3745
3746 pScsiReq->CDBLength = cmdLen;
3747 pScsiReq->SenseBufferLength = MPT_SENSE_BUFFER_SIZE;
3748
3749 pScsiReq->Reserved = 0;
3750
3751 pScsiReq->MsgFlags = mpt_msg_flags();
3752 /* MsgContext set in mpt_get_msg_fram call */
3753
3754 for (ii=0; ii < 8; ii++)
3755 pScsiReq->LUN[ii] = 0;
3756 pScsiReq->LUN[1] = io->lun;
3757
3758 if (io->flags & MPT_ICFLAG_TAGGED_CMD)
3759 pScsiReq->Control = cpu_to_le32(dir | MPI_SCSIIO_CONTROL_SIMPLEQ);
3760 else
3761 pScsiReq->Control = cpu_to_le32(dir | MPI_SCSIIO_CONTROL_UNTAGGED);
3762
3763 if (cmd == REQUEST_SENSE) {
3764 pScsiReq->Control = cpu_to_le32(dir | MPI_SCSIIO_CONTROL_UNTAGGED);
3765 ddvprintk((MYIOC_s_INFO_FMT "Untagged! 0x%2x\n",
3766 hd->ioc->name, cmd));
3767 }
3768
3769 for (ii=0; ii < 16; ii++)
3770 pScsiReq->CDB[ii] = CDB[ii];
3771
3772 pScsiReq->DataLength = cpu_to_le32(io->size);
3773 pScsiReq->SenseBufferLowAddr = cpu_to_le32(hd->ioc->sense_buf_low_dma
3774 + (my_idx * MPT_SENSE_BUFFER_ALLOC));
3775
3776 ddvprintk((MYIOC_s_INFO_FMT "Sending Command 0x%x for (%d:%d:%d)\n",
3777 hd->ioc->name, cmd, io->bus, io->id, io->lun));
3778
3779 if (dir == MPI_SCSIIO_CONTROL_READ) {
3780 mpt_add_sge((char *) &pScsiReq->SGL,
3781 MPT_SGE_FLAGS_SSIMPLE_READ | io->size,
3782 io->data_dma);
3783 } else {
3784 mpt_add_sge((char *) &pScsiReq->SGL,
3785 MPT_SGE_FLAGS_SSIMPLE_WRITE | io->size,
3786 io->data_dma);
3787 }
3788
3789 /* The ISR will free the request frame, but we need
3790 * the information to initialize the target. Duplicate.
3791 */
3792 memcpy(&ReqCopy, pScsiReq, sizeof(SCSIIORequest_t));
3793
3794 /* Issue this command after:
3795 * finish init
3796 * add timer
3797 * Wait until the reply has been received
3798 * ScsiScanDvCtx callback function will
3799 * set hd->pLocal;
3800 * set scandv_wait_done and call wake_up
3801 */
3802 hd->pLocal = NULL;
3803 hd->timer.expires = jiffies + HZ*cmdTimeout;
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04003804 hd->scandv_wait_done = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003805
3806 /* Save cmd pointer, for resource free if timeout or
3807 * FW reload occurs
3808 */
3809 hd->cmdPtr = mf;
3810
3811 add_timer(&hd->timer);
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04003812 mpt_put_msg_frame(hd->ioc->InternalCtx, hd->ioc, mf);
3813 wait_event(hd->scandv_waitq, hd->scandv_wait_done);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003814
3815 if (hd->pLocal) {
3816 rc = hd->pLocal->completion;
3817 hd->pLocal->skip = 0;
3818
3819 /* Always set fatal error codes in some cases.
3820 */
3821 if (rc == MPT_SCANDV_SELECTION_TIMEOUT)
3822 rc = -ENXIO;
3823 else if (rc == MPT_SCANDV_SOME_ERROR)
3824 rc = -rc;
3825 } else {
3826 rc = -EFAULT;
3827 /* This should never happen. */
3828 ddvprintk((MYIOC_s_INFO_FMT "_do_cmd: Null pLocal!!!\n",
3829 hd->ioc->name));
3830 }
3831
3832 return rc;
3833}
3834
3835/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
3836/**
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07003837 * mptscsih_negotiate_to_asyn_narrow - Restore devices to default state
3838 * @hd: Pointer to a SCSI HOST structure
3839 * @vtarget: per device private data
Linus Torvalds1da177e2005-04-16 15:20:36 -07003840 *
3841 * Uses the ISR, but with special processing.
3842 * MUST be single-threaded.
3843 *
Linus Torvalds1da177e2005-04-16 15:20:36 -07003844 */
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07003845static void
3846mptscsih_negotiate_to_asyn_narrow(MPT_SCSI_HOST *hd, VirtTarget *vtarget)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003847{
3848 MPT_ADAPTER *ioc= hd->ioc;
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07003849 SCSIDevicePage1_t *pcfg1Data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003850 CONFIGPARMS cfg;
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07003851 dma_addr_t cfg1_dma_addr;
3852 ConfigPageHeader_t header;
3853 int id;
3854 int requested, configuration, data,i;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003855 u8 flags, factor;
3856
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07003857 if (ioc->bus_type != SPI)
3858 return;
3859
3860 if (!ioc->spi_data.sdp1length)
3861 return;
3862
3863 pcfg1Data = (SCSIDevicePage1_t *)pci_alloc_consistent(ioc->pcidev,
3864 ioc->spi_data.sdp1length * 4, &cfg1_dma_addr);
3865
3866 if (pcfg1Data == NULL)
3867 return;
3868
3869 header.PageVersion = ioc->spi_data.sdp1version;
3870 header.PageLength = ioc->spi_data.sdp1length;
3871 header.PageNumber = 1;
3872 header.PageType = MPI_CONFIG_PAGETYPE_SCSI_DEVICE;
3873 cfg.cfghdr.hdr = &header;
3874 cfg.physAddr = cfg1_dma_addr;
3875 cfg.action = MPI_CONFIG_ACTION_PAGE_WRITE_CURRENT;
3876 cfg.dir = 1;
3877 cfg.timeout = 0;
3878
3879 if (vtarget->raidVolume && ioc->raid_data.pIocPg3) {
3880 for (i = 0; i < ioc->raid_data.pIocPg3->NumPhysDisks; i++) {
3881 id = ioc->raid_data.pIocPg3->PhysDisk[i].PhysDiskID;
3882 flags = hd->ioc->spi_data.noQas;
3883 if (hd->ioc->spi_data.nvram && (hd->ioc->spi_data.nvram[id] != MPT_HOST_NVRAM_INVALID)) {
3884 data = hd->ioc->spi_data.nvram[id];
3885 if (data & MPT_NVRAM_WIDE_DISABLE)
3886 flags |= MPT_TARGET_NO_NEGO_WIDE;
3887 factor = (data & MPT_NVRAM_SYNC_MASK) >> MPT_NVRAM_SYNC_SHIFT;
3888 if ((factor == 0) || (factor == MPT_ASYNC))
3889 flags |= MPT_TARGET_NO_NEGO_SYNC;
3890 }
3891 mptscsih_setDevicePage1Flags(0, MPT_ASYNC, 0, &requested,
3892 &configuration, flags);
3893 dnegoprintk(("syncronize cache: id=%d width=0 factor=MPT_ASYNC "
3894 "offset=0 negoFlags=%x request=%x config=%x\n",
3895 id, flags, requested, configuration));
3896 pcfg1Data->RequestedParameters = cpu_to_le32(requested);
3897 pcfg1Data->Reserved = 0;
3898 pcfg1Data->Configuration = cpu_to_le32(configuration);
3899 cfg.pageAddr = (vtarget->bus_id<<8) | id;
3900 mpt_config(hd->ioc, &cfg);
3901 }
3902 } else {
3903 flags = vtarget->negoFlags;
3904 mptscsih_setDevicePage1Flags(0, MPT_ASYNC, 0, &requested,
3905 &configuration, flags);
3906 dnegoprintk(("syncronize cache: id=%d width=0 factor=MPT_ASYNC "
3907 "offset=0 negoFlags=%x request=%x config=%x\n",
3908 vtarget->target_id, flags, requested, configuration));
3909 pcfg1Data->RequestedParameters = cpu_to_le32(requested);
3910 pcfg1Data->Reserved = 0;
3911 pcfg1Data->Configuration = cpu_to_le32(configuration);
3912 cfg.pageAddr = (vtarget->bus_id<<8) | vtarget->target_id;
3913 mpt_config(hd->ioc, &cfg);
3914 }
3915
3916 if (pcfg1Data)
3917 pci_free_consistent(ioc->pcidev, header.PageLength * 4, pcfg1Data, cfg1_dma_addr);
3918}
3919
3920/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
3921/**
3922 * mptscsih_synchronize_cache - Send SYNCHRONIZE_CACHE to all disks.
3923 * @hd: Pointer to a SCSI HOST structure
3924 * @vtarget: per device private data
3925 * @lun: lun
3926 *
3927 * Uses the ISR, but with special processing.
3928 * MUST be single-threaded.
3929 *
3930 */
3931static void
3932mptscsih_synchronize_cache(MPT_SCSI_HOST *hd, VirtDevice *vdevice)
3933{
3934 INTERNAL_CMD iocmd;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003935
3936 /* Following parameters will not change
3937 * in this routine.
3938 */
3939 iocmd.cmd = SYNCHRONIZE_CACHE;
3940 iocmd.flags = 0;
3941 iocmd.physDiskNum = -1;
3942 iocmd.data = NULL;
3943 iocmd.data_dma = -1;
3944 iocmd.size = 0;
3945 iocmd.rsvd = iocmd.rsvd2 = 0;
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07003946 iocmd.bus = vdevice->bus_id;
3947 iocmd.id = vdevice->target_id;
3948 iocmd.lun = (u8)vdevice->lun;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003949
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07003950 if ((vdevice->vtarget->type & TYPE_DISK) &&
3951 (vdevice->configured_lun))
3952 mptscsih_do_cmd(hd, &iocmd);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003953}
3954
Moore, Eric Deanf2ea8672005-11-16 18:54:23 -07003955/* Search IOC page 3 to determine if this is hidden physical disk
3956 */
3957/* Search IOC page 3 to determine if this is hidden physical disk
3958 */
3959static int
3960mptscsih_is_phys_disk(MPT_ADAPTER *ioc, int id)
3961{
3962 int i;
3963
3964 if (!ioc->raid_data.isRaid || !ioc->raid_data.pIocPg3)
3965 return 0;
3966
3967 for (i = 0; i < ioc->raid_data.pIocPg3->NumPhysDisks; i++) {
3968 if (id == ioc->raid_data.pIocPg3->PhysDisk[i].PhysDiskID)
3969 return 1;
3970 }
3971
3972 return 0;
3973}
3974
Linus Torvalds1da177e2005-04-16 15:20:36 -07003975#ifdef MPTSCSIH_ENABLE_DOMAIN_VALIDATION
3976/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
3977/**
3978 * mptscsih_domainValidation - Top level handler for domain validation.
3979 * @hd: Pointer to MPT_SCSI_HOST structure.
3980 *
3981 * Uses the ISR, but with special processing.
3982 * Called from schedule, should not be in interrupt mode.
3983 * While thread alive, do dv for all devices needing dv
3984 *
3985 * Return: None.
3986 */
3987static void
3988mptscsih_domainValidation(void *arg)
3989{
3990 MPT_SCSI_HOST *hd;
3991 MPT_ADAPTER *ioc;
3992 unsigned long flags;
3993 int id, maxid, dvStatus, did;
3994 int ii, isPhysDisk;
3995
3996 spin_lock_irqsave(&dvtaskQ_lock, flags);
3997 dvtaskQ_active = 1;
3998 if (dvtaskQ_release) {
3999 dvtaskQ_active = 0;
4000 spin_unlock_irqrestore(&dvtaskQ_lock, flags);
4001 return;
4002 }
4003 spin_unlock_irqrestore(&dvtaskQ_lock, flags);
4004
4005 /* For this ioc, loop through all devices and do dv to each device.
4006 * When complete with this ioc, search through the ioc list, and
4007 * for each scsi ioc found, do dv for all devices. Exit when no
4008 * device needs dv.
4009 */
4010 did = 1;
4011 while (did) {
4012 did = 0;
4013 list_for_each_entry(ioc, &ioc_list, list) {
4014 spin_lock_irqsave(&dvtaskQ_lock, flags);
4015 if (dvtaskQ_release) {
4016 dvtaskQ_active = 0;
4017 spin_unlock_irqrestore(&dvtaskQ_lock, flags);
4018 return;
4019 }
4020 spin_unlock_irqrestore(&dvtaskQ_lock, flags);
4021
4022 msleep(250);
4023
Moore, Eric Deana9b29372005-11-16 18:54:20 -07004024 /* DV only to SPI adapters */
4025 if (ioc->bus_type != SPI)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004026 continue;
4027
4028 /* Make sure everything looks ok */
4029 if (ioc->sh == NULL)
4030 continue;
4031
4032 hd = (MPT_SCSI_HOST *) ioc->sh->hostdata;
4033 if (hd == NULL)
4034 continue;
4035
4036 if ((ioc->spi_data.forceDv & MPT_SCSICFG_RELOAD_IOC_PG3) != 0) {
4037 mpt_read_ioc_pg_3(ioc);
Moore, Eric Dean466544d2005-09-14 18:09:10 -06004038 if (ioc->raid_data.pIocPg3) {
4039 Ioc3PhysDisk_t *pPDisk = ioc->raid_data.pIocPg3->PhysDisk;
4040 int numPDisk = ioc->raid_data.pIocPg3->NumPhysDisks;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004041
4042 while (numPDisk) {
4043 if (ioc->spi_data.dvStatus[pPDisk->PhysDiskID] & MPT_SCSICFG_DV_NOT_DONE)
4044 ioc->spi_data.dvStatus[pPDisk->PhysDiskID] |= MPT_SCSICFG_NEED_DV;
4045
4046 pPDisk++;
4047 numPDisk--;
4048 }
4049 }
4050 ioc->spi_data.forceDv &= ~MPT_SCSICFG_RELOAD_IOC_PG3;
4051 }
4052
4053 maxid = min_t(int, ioc->sh->max_id, MPT_MAX_SCSI_DEVICES);
4054
4055 for (id = 0; id < maxid; id++) {
4056 spin_lock_irqsave(&dvtaskQ_lock, flags);
4057 if (dvtaskQ_release) {
4058 dvtaskQ_active = 0;
4059 spin_unlock_irqrestore(&dvtaskQ_lock, flags);
4060 return;
4061 }
4062 spin_unlock_irqrestore(&dvtaskQ_lock, flags);
4063 dvStatus = hd->ioc->spi_data.dvStatus[id];
4064
4065 if (dvStatus & MPT_SCSICFG_NEED_DV) {
4066 did++;
4067 hd->ioc->spi_data.dvStatus[id] |= MPT_SCSICFG_DV_PENDING;
4068 hd->ioc->spi_data.dvStatus[id] &= ~MPT_SCSICFG_NEED_DV;
4069
4070 msleep(250);
4071
4072 /* If hidden phys disk, block IO's to all
4073 * raid volumes
4074 * else, process normally
4075 */
4076 isPhysDisk = mptscsih_is_phys_disk(ioc, id);
4077 if (isPhysDisk) {
4078 for (ii=0; ii < MPT_MAX_SCSI_DEVICES; ii++) {
Moore, Eric Dean466544d2005-09-14 18:09:10 -06004079 if (hd->ioc->raid_data.isRaid & (1 << ii)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004080 hd->ioc->spi_data.dvStatus[ii] |= MPT_SCSICFG_DV_PENDING;
4081 }
4082 }
4083 }
4084
Moore, Eric Dean2a238ea2005-12-01 10:50:32 -07004085 if(mpt_alt_ioc_wait(hd->ioc)!=0) {
4086 ddvprintk((MYIOC_s_WARN_FMT "alt_ioc busy!\n",
4087 hd->ioc->name));
4088 continue;
4089 }
4090
Linus Torvalds1da177e2005-04-16 15:20:36 -07004091 if (mptscsih_doDv(hd, 0, id) == 1) {
4092 /* Untagged device was busy, try again
4093 */
4094 hd->ioc->spi_data.dvStatus[id] |= MPT_SCSICFG_NEED_DV;
4095 hd->ioc->spi_data.dvStatus[id] &= ~MPT_SCSICFG_DV_PENDING;
4096 } else {
4097 /* DV is complete. Clear flags.
4098 */
4099 hd->ioc->spi_data.dvStatus[id] &= ~(MPT_SCSICFG_DV_NOT_DONE | MPT_SCSICFG_DV_PENDING);
4100 }
4101
Moore, Eric Dean2a238ea2005-12-01 10:50:32 -07004102 spin_lock(&hd->ioc->initializing_hba_lock);
4103 hd->ioc->initializing_hba_lock_flag=0;
4104 spin_unlock(&hd->ioc->initializing_hba_lock);
4105
Linus Torvalds1da177e2005-04-16 15:20:36 -07004106 if (isPhysDisk) {
4107 for (ii=0; ii < MPT_MAX_SCSI_DEVICES; ii++) {
Moore, Eric Dean466544d2005-09-14 18:09:10 -06004108 if (hd->ioc->raid_data.isRaid & (1 << ii)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004109 hd->ioc->spi_data.dvStatus[ii] &= ~MPT_SCSICFG_DV_PENDING;
4110 }
4111 }
4112 }
4113
4114 if (hd->ioc->spi_data.noQas)
4115 mptscsih_qas_check(hd, id);
4116 }
4117 }
4118 }
4119 }
4120
4121 spin_lock_irqsave(&dvtaskQ_lock, flags);
4122 dvtaskQ_active = 0;
4123 spin_unlock_irqrestore(&dvtaskQ_lock, flags);
4124
4125 return;
4126}
4127
Linus Torvalds1da177e2005-04-16 15:20:36 -07004128/* Write SDP1 if no QAS has been enabled
4129 */
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04004130static void
4131mptscsih_qas_check(MPT_SCSI_HOST *hd, int id)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004132{
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07004133 VirtTarget *vtarget;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004134 int ii;
4135
4136 if (hd->Targets == NULL)
4137 return;
4138
4139 for (ii=0; ii < MPT_MAX_SCSI_DEVICES; ii++) {
4140 if (ii == id)
4141 continue;
4142
4143 if ((hd->ioc->spi_data.dvStatus[ii] & MPT_SCSICFG_DV_NOT_DONE) != 0)
4144 continue;
4145
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07004146 vtarget = hd->Targets[ii];
Linus Torvalds1da177e2005-04-16 15:20:36 -07004147
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07004148 if ((vtarget != NULL) && (!vtarget->raidVolume)) {
4149 if ((vtarget->negoFlags & hd->ioc->spi_data.noQas) == 0) {
4150 vtarget->negoFlags |= hd->ioc->spi_data.noQas;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004151 dnegoprintk(("writeSDP1: id=%d flags=0\n", id));
4152 mptscsih_writeSDP1(hd, 0, ii, 0);
4153 }
4154 } else {
4155 if (mptscsih_is_phys_disk(hd->ioc, ii) == 1) {
4156 dnegoprintk(("writeSDP1: id=%d SCSICFG_USE_NVRAM\n", id));
4157 mptscsih_writeSDP1(hd, 0, ii, MPT_SCSICFG_USE_NVRAM);
4158 }
4159 }
4160 }
4161 return;
4162}
4163
4164
4165
4166#define MPT_GET_NVRAM_VALS 0x01
4167#define MPT_UPDATE_MAX 0x02
4168#define MPT_SET_MAX 0x04
4169#define MPT_SET_MIN 0x08
4170#define MPT_FALLBACK 0x10
4171#define MPT_SAVE 0x20
4172
4173/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
4174/**
4175 * mptscsih_doDv - Perform domain validation to a target.
4176 * @hd: Pointer to MPT_SCSI_HOST structure.
4177 * @portnum: IOC port number.
4178 * @target: Physical ID of this target
4179 *
4180 * Uses the ISR, but with special processing.
4181 * MUST be single-threaded.
4182 * Test will exit if target is at async & narrow.
4183 *
4184 * Return: None.
4185 */
4186static int
4187mptscsih_doDv(MPT_SCSI_HOST *hd, int bus_number, int id)
4188{
4189 MPT_ADAPTER *ioc = hd->ioc;
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07004190 VirtTarget *vtarget;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004191 SCSIDevicePage1_t *pcfg1Data;
4192 SCSIDevicePage0_t *pcfg0Data;
4193 u8 *pbuf1;
4194 u8 *pbuf2;
4195 u8 *pDvBuf;
4196 dma_addr_t dvbuf_dma = -1;
4197 dma_addr_t buf1_dma = -1;
4198 dma_addr_t buf2_dma = -1;
4199 dma_addr_t cfg1_dma_addr = -1;
4200 dma_addr_t cfg0_dma_addr = -1;
4201 ConfigPageHeader_t header1;
4202 ConfigPageHeader_t header0;
4203 DVPARAMETERS dv;
4204 INTERNAL_CMD iocmd;
4205 CONFIGPARMS cfg;
4206 int dv_alloc = 0;
4207 int rc, sz = 0;
4208 int bufsize = 0;
4209 int dataBufSize = 0;
4210 int echoBufSize = 0;
4211 int notDone;
4212 int patt;
4213 int repeat;
4214 int retcode = 0;
4215 int nfactor = MPT_ULTRA320;
4216 char firstPass = 1;
4217 char doFallback = 0;
4218 char readPage0;
4219 char bus, lun;
4220 char inq0 = 0;
4221
4222 if (ioc->spi_data.sdp1length == 0)
4223 return 0;
4224
4225 if (ioc->spi_data.sdp0length == 0)
4226 return 0;
4227
4228 /* If multiple buses are used, require that the initiator
4229 * id be the same on all buses.
4230 */
4231 if (id == ioc->pfacts[0].PortSCSIID)
4232 return 0;
4233
4234 lun = 0;
4235 bus = (u8) bus_number;
4236 ddvtprintk((MYIOC_s_NOTE_FMT
4237 "DV started: bus=%d, id=%d dv @ %p\n",
4238 ioc->name, bus, id, &dv));
4239
4240 /* Prep DV structure
4241 */
4242 memset (&dv, 0, sizeof(DVPARAMETERS));
4243 dv.id = id;
4244
4245 /* Populate tmax with the current maximum
4246 * transfer parameters for this target.
4247 * Exit if narrow and async.
4248 */
4249 dv.cmd = MPT_GET_NVRAM_VALS;
4250 mptscsih_dv_parms(hd, &dv, NULL);
4251
4252 /* Prep SCSI IO structure
4253 */
4254 iocmd.id = id;
4255 iocmd.bus = bus;
4256 iocmd.lun = lun;
4257 iocmd.flags = 0;
4258 iocmd.physDiskNum = -1;
4259 iocmd.rsvd = iocmd.rsvd2 = 0;
4260
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07004261 vtarget = hd->Targets[id];
Linus Torvalds1da177e2005-04-16 15:20:36 -07004262
4263 /* Use tagged commands if possible.
4264 */
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07004265 if (vtarget) {
4266 if (vtarget->tflags & MPT_TARGET_FLAGS_Q_YES)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004267 iocmd.flags |= MPT_ICFLAG_TAGGED_CMD;
4268 else {
4269 if (hd->ioc->facts.FWVersion.Word < 0x01000600)
4270 return 0;
4271
4272 if ((hd->ioc->facts.FWVersion.Word >= 0x01010000) &&
4273 (hd->ioc->facts.FWVersion.Word < 0x01010B00))
4274 return 0;
4275 }
4276 }
4277
4278 /* Prep cfg structure
4279 */
4280 cfg.pageAddr = (bus<<8) | id;
Christoph Hellwig69218ee2005-08-18 16:26:15 +02004281 cfg.cfghdr.hdr = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004282
4283 /* Prep SDP0 header
4284 */
4285 header0.PageVersion = ioc->spi_data.sdp0version;
4286 header0.PageLength = ioc->spi_data.sdp0length;
4287 header0.PageNumber = 0;
4288 header0.PageType = MPI_CONFIG_PAGETYPE_SCSI_DEVICE;
4289
4290 /* Prep SDP1 header
4291 */
4292 header1.PageVersion = ioc->spi_data.sdp1version;
4293 header1.PageLength = ioc->spi_data.sdp1length;
4294 header1.PageNumber = 1;
4295 header1.PageType = MPI_CONFIG_PAGETYPE_SCSI_DEVICE;
4296
4297 if (header0.PageLength & 1)
4298 dv_alloc = (header0.PageLength * 4) + 4;
4299
4300 dv_alloc += (2048 + (header1.PageLength * 4));
4301
4302 pDvBuf = pci_alloc_consistent(ioc->pcidev, dv_alloc, &dvbuf_dma);
4303 if (pDvBuf == NULL)
4304 return 0;
4305
4306 sz = 0;
4307 pbuf1 = (u8 *)pDvBuf;
4308 buf1_dma = dvbuf_dma;
4309 sz +=1024;
4310
4311 pbuf2 = (u8 *) (pDvBuf + sz);
4312 buf2_dma = dvbuf_dma + sz;
4313 sz +=1024;
4314
4315 pcfg0Data = (SCSIDevicePage0_t *) (pDvBuf + sz);
4316 cfg0_dma_addr = dvbuf_dma + sz;
4317 sz += header0.PageLength * 4;
4318
4319 /* 8-byte alignment
4320 */
4321 if (header0.PageLength & 1)
4322 sz += 4;
4323
4324 pcfg1Data = (SCSIDevicePage1_t *) (pDvBuf + sz);
4325 cfg1_dma_addr = dvbuf_dma + sz;
4326
Christoph Hellwig69218ee2005-08-18 16:26:15 +02004327 /* Skip this ID? Set cfg.cfghdr.hdr to force config page write
Linus Torvalds1da177e2005-04-16 15:20:36 -07004328 */
4329 {
Moore, Eric Dean466544d2005-09-14 18:09:10 -06004330 SpiCfgData *pspi_data = &hd->ioc->spi_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004331 if (pspi_data->nvram && (pspi_data->nvram[id] != MPT_HOST_NVRAM_INVALID)) {
4332 /* Set the factor from nvram */
4333 nfactor = (pspi_data->nvram[id] & MPT_NVRAM_SYNC_MASK) >> 8;
4334 if (nfactor < pspi_data->minSyncFactor )
4335 nfactor = pspi_data->minSyncFactor;
4336
4337 if (!(pspi_data->nvram[id] & MPT_NVRAM_ID_SCAN_ENABLE) ||
4338 (pspi_data->PortFlags == MPI_SCSIPORTPAGE2_PORT_FLAGS_OFF_DV) ) {
4339
4340 ddvprintk((MYIOC_s_NOTE_FMT "DV Skipped: bus, id, lun (%d, %d, %d)\n",
4341 ioc->name, bus, id, lun));
4342
4343 dv.cmd = MPT_SET_MAX;
4344 mptscsih_dv_parms(hd, &dv, (void *)pcfg1Data);
Christoph Hellwig69218ee2005-08-18 16:26:15 +02004345 cfg.cfghdr.hdr = &header1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004346
4347 /* Save the final negotiated settings to
4348 * SCSI device page 1.
4349 */
4350 cfg.physAddr = cfg1_dma_addr;
4351 cfg.action = MPI_CONFIG_ACTION_PAGE_WRITE_CURRENT;
4352 cfg.dir = 1;
4353 mpt_config(hd->ioc, &cfg);
4354 goto target_done;
4355 }
4356 }
4357 }
4358
4359 /* Finish iocmd inititialization - hidden or visible disk? */
Moore, Eric Dean466544d2005-09-14 18:09:10 -06004360 if (ioc->raid_data.pIocPg3) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004361 /* Search IOC page 3 for matching id
4362 */
Moore, Eric Dean466544d2005-09-14 18:09:10 -06004363 Ioc3PhysDisk_t *pPDisk = ioc->raid_data.pIocPg3->PhysDisk;
4364 int numPDisk = ioc->raid_data.pIocPg3->NumPhysDisks;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004365
4366 while (numPDisk) {
4367 if (pPDisk->PhysDiskID == id) {
4368 /* match */
4369 iocmd.flags |= MPT_ICFLAG_PHYS_DISK;
4370 iocmd.physDiskNum = pPDisk->PhysDiskNum;
4371
4372 /* Quiesce the IM
4373 */
4374 if (mptscsih_do_raid(hd, MPI_RAID_ACTION_QUIESCE_PHYS_IO, &iocmd) < 0) {
4375 ddvprintk((MYIOC_s_ERR_FMT "RAID Queisce FAILED!\n", ioc->name));
4376 goto target_done;
4377 }
4378 break;
4379 }
4380 pPDisk++;
4381 numPDisk--;
4382 }
4383 }
4384
4385 /* RAID Volume ID's may double for a physical device. If RAID but
4386 * not a physical ID as well, skip DV.
4387 */
Moore, Eric Dean466544d2005-09-14 18:09:10 -06004388 if ((hd->ioc->raid_data.isRaid & (1 << id)) && !(iocmd.flags & MPT_ICFLAG_PHYS_DISK))
Linus Torvalds1da177e2005-04-16 15:20:36 -07004389 goto target_done;
4390
4391
4392 /* Basic Test.
4393 * Async & Narrow - Inquiry
4394 * Async & Narrow - Inquiry
4395 * Maximum transfer rate - Inquiry
4396 * Compare buffers:
4397 * If compare, test complete.
4398 * If miscompare and first pass, repeat
4399 * If miscompare and not first pass, fall back and repeat
4400 */
4401 hd->pLocal = NULL;
4402 readPage0 = 0;
4403 sz = SCSI_MAX_INQUIRY_BYTES;
4404 rc = MPT_SCANDV_GOOD;
4405 while (1) {
4406 ddvprintk((MYIOC_s_NOTE_FMT "DV: Start Basic test on id=%d\n", ioc->name, id));
4407 retcode = 0;
4408 dv.cmd = MPT_SET_MIN;
4409 mptscsih_dv_parms(hd, &dv, (void *)pcfg1Data);
4410
Christoph Hellwig69218ee2005-08-18 16:26:15 +02004411 cfg.cfghdr.hdr = &header1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004412 cfg.physAddr = cfg1_dma_addr;
4413 cfg.action = MPI_CONFIG_ACTION_PAGE_WRITE_CURRENT;
4414 cfg.dir = 1;
4415 if (mpt_config(hd->ioc, &cfg) != 0)
4416 goto target_done;
4417
4418 /* Wide - narrow - wide workaround case
4419 */
4420 if ((rc == MPT_SCANDV_ISSUE_SENSE) && dv.max.width) {
4421 /* Send an untagged command to reset disk Qs corrupted
4422 * when a parity error occurs on a Request Sense.
4423 */
4424 if ((hd->ioc->facts.FWVersion.Word >= 0x01000600) ||
4425 ((hd->ioc->facts.FWVersion.Word >= 0x01010000) &&
4426 (hd->ioc->facts.FWVersion.Word < 0x01010B00)) ) {
4427
4428 iocmd.cmd = REQUEST_SENSE;
4429 iocmd.data_dma = buf1_dma;
4430 iocmd.data = pbuf1;
4431 iocmd.size = 0x12;
4432 if (mptscsih_do_cmd(hd, &iocmd) < 0)
4433 goto target_done;
4434 else {
4435 if (hd->pLocal == NULL)
4436 goto target_done;
4437 rc = hd->pLocal->completion;
4438 if ((rc == MPT_SCANDV_GOOD) || (rc == MPT_SCANDV_SENSE)) {
4439 dv.max.width = 0;
4440 doFallback = 0;
4441 } else
4442 goto target_done;
4443 }
4444 } else
4445 goto target_done;
4446 }
4447
4448 iocmd.cmd = INQUIRY;
4449 iocmd.data_dma = buf1_dma;
4450 iocmd.data = pbuf1;
4451 iocmd.size = sz;
4452 memset(pbuf1, 0x00, sz);
4453 if (mptscsih_do_cmd(hd, &iocmd) < 0)
4454 goto target_done;
4455 else {
4456 if (hd->pLocal == NULL)
4457 goto target_done;
4458 rc = hd->pLocal->completion;
4459 if (rc == MPT_SCANDV_GOOD) {
4460 if (hd->pLocal->scsiStatus == SAM_STAT_BUSY) {
4461 if ((iocmd.flags & MPT_ICFLAG_TAGGED_CMD) == 0)
4462 retcode = 1;
4463 else
4464 retcode = 0;
4465
4466 goto target_done;
4467 }
4468 } else if (rc == MPT_SCANDV_SENSE) {
4469 ;
4470 } else {
4471 /* If first command doesn't complete
4472 * with a good status or with a check condition,
4473 * exit.
4474 */
4475 goto target_done;
4476 }
4477 }
4478
4479 /* Reset the size for disks
4480 */
4481 inq0 = (*pbuf1) & 0x1F;
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07004482 if ((inq0 == 0) && vtarget && !vtarget->raidVolume) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004483 sz = 0x40;
4484 iocmd.size = sz;
4485 }
4486
4487 /* Another GEM workaround. Check peripheral device type,
4488 * if PROCESSOR, quit DV.
4489 */
4490 if (inq0 == TYPE_PROCESSOR) {
4491 mptscsih_initTarget(hd,
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07004492 vtarget,
Linus Torvalds1da177e2005-04-16 15:20:36 -07004493 lun,
4494 pbuf1,
4495 sz);
4496 goto target_done;
4497 }
4498
4499 if (inq0 > 0x08)
4500 goto target_done;
4501
4502 if (mptscsih_do_cmd(hd, &iocmd) < 0)
4503 goto target_done;
4504
4505 if (sz == 0x40) {
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07004506 if ((vtarget->maxWidth == 1) && (vtarget->maxOffset) && (nfactor < 0x0A)
4507 && (vtarget->minSyncFactor > 0x09)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004508 if ((pbuf1[56] & 0x04) == 0)
4509 ;
4510 else if ((pbuf1[56] & 0x01) == 1) {
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07004511 vtarget->minSyncFactor =
Linus Torvalds1da177e2005-04-16 15:20:36 -07004512 nfactor > MPT_ULTRA320 ? nfactor : MPT_ULTRA320;
4513 } else {
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07004514 vtarget->minSyncFactor =
Linus Torvalds1da177e2005-04-16 15:20:36 -07004515 nfactor > MPT_ULTRA160 ? nfactor : MPT_ULTRA160;
4516 }
4517
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07004518 dv.max.factor = vtarget->minSyncFactor;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004519
4520 if ((pbuf1[56] & 0x02) == 0) {
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07004521 vtarget->negoFlags |= MPT_TARGET_NO_NEGO_QAS;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004522 hd->ioc->spi_data.noQas = MPT_TARGET_NO_NEGO_QAS;
Christoph Hellwigc6678e02005-08-18 16:24:53 +02004523 ddvprintk((MYIOC_s_NOTE_FMT
4524 "DV: Start Basic noQas on id=%d due to pbuf1[56]=%x\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07004525 ioc->name, id, pbuf1[56]));
4526 }
4527 }
4528 }
4529
4530 if (doFallback)
4531 dv.cmd = MPT_FALLBACK;
4532 else
4533 dv.cmd = MPT_SET_MAX;
4534
4535 mptscsih_dv_parms(hd, &dv, (void *)pcfg1Data);
4536 if (mpt_config(hd->ioc, &cfg) != 0)
4537 goto target_done;
4538
4539 if ((!dv.now.width) && (!dv.now.offset))
4540 goto target_done;
4541
4542 iocmd.cmd = INQUIRY;
4543 iocmd.data_dma = buf2_dma;
4544 iocmd.data = pbuf2;
4545 iocmd.size = sz;
4546 memset(pbuf2, 0x00, sz);
4547 if (mptscsih_do_cmd(hd, &iocmd) < 0)
4548 goto target_done;
4549 else if (hd->pLocal == NULL)
4550 goto target_done;
4551 else {
4552 /* Save the return code.
4553 * If this is the first pass,
4554 * read SCSI Device Page 0
4555 * and update the target max parameters.
4556 */
4557 rc = hd->pLocal->completion;
4558 doFallback = 0;
4559 if (rc == MPT_SCANDV_GOOD) {
4560 if (!readPage0) {
4561 u32 sdp0_info;
4562 u32 sdp0_nego;
4563
Christoph Hellwig69218ee2005-08-18 16:26:15 +02004564 cfg.cfghdr.hdr = &header0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004565 cfg.physAddr = cfg0_dma_addr;
4566 cfg.action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT;
4567 cfg.dir = 0;
4568
4569 if (mpt_config(hd->ioc, &cfg) != 0)
4570 goto target_done;
4571
4572 sdp0_info = le32_to_cpu(pcfg0Data->Information) & 0x0E;
4573 sdp0_nego = (le32_to_cpu(pcfg0Data->NegotiatedParameters) & 0xFF00 ) >> 8;
4574
4575 /* Quantum and Fujitsu workarounds.
4576 * Quantum: PPR U320 -> PPR reply with Ultra2 and wide
4577 * Fujitsu: PPR U320 -> Msg Reject and Ultra2 and wide
4578 * Resetart with a request for U160.
4579 */
4580 if ((dv.now.factor == MPT_ULTRA320) && (sdp0_nego == MPT_ULTRA2)) {
4581 doFallback = 1;
4582 } else {
4583 dv.cmd = MPT_UPDATE_MAX;
4584 mptscsih_dv_parms(hd, &dv, (void *)pcfg0Data);
4585 /* Update the SCSI device page 1 area
4586 */
4587 pcfg1Data->RequestedParameters = pcfg0Data->NegotiatedParameters;
4588 readPage0 = 1;
4589 }
4590 }
4591
4592 /* Quantum workaround. Restart this test will the fallback
4593 * flag set.
4594 */
4595 if (doFallback == 0) {
4596 if (memcmp(pbuf1, pbuf2, sz) != 0) {
4597 if (!firstPass)
4598 doFallback = 1;
4599 } else {
Christoph Hellwigc6678e02005-08-18 16:24:53 +02004600 ddvprintk((MYIOC_s_NOTE_FMT
Linus Torvalds1da177e2005-04-16 15:20:36 -07004601 "DV:Inquiry compared id=%d, calling initTarget\n", ioc->name, id));
4602 hd->ioc->spi_data.dvStatus[id] &= ~MPT_SCSICFG_DV_NOT_DONE;
4603 mptscsih_initTarget(hd,
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07004604 vtarget,
Linus Torvalds1da177e2005-04-16 15:20:36 -07004605 lun,
4606 pbuf1,
4607 sz);
4608 break; /* test complete */
4609 }
4610 }
4611
4612
4613 } else if (rc == MPT_SCANDV_ISSUE_SENSE)
4614 doFallback = 1; /* set fallback flag */
Christoph Hellwigc6678e02005-08-18 16:24:53 +02004615 else if ((rc == MPT_SCANDV_DID_RESET) ||
4616 (rc == MPT_SCANDV_SENSE) ||
Linus Torvalds1da177e2005-04-16 15:20:36 -07004617 (rc == MPT_SCANDV_FALLBACK))
4618 doFallback = 1; /* set fallback flag */
4619 else
4620 goto target_done;
4621
4622 firstPass = 0;
4623 }
4624 }
4625 ddvprintk((MYIOC_s_NOTE_FMT "DV: Basic test on id=%d completed OK.\n", ioc->name, id));
4626
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04004627 if (ioc->spi_data.mpt_dv == 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004628 goto target_done;
4629
4630 inq0 = (*pbuf1) & 0x1F;
4631
4632 /* Continue only for disks
4633 */
4634 if (inq0 != 0)
4635 goto target_done;
4636
4637 if ( ioc->spi_data.PortFlags == MPI_SCSIPORTPAGE2_PORT_FLAGS_BASIC_DV_ONLY )
4638 goto target_done;
4639
4640 /* Start the Enhanced Test.
4641 * 0) issue TUR to clear out check conditions
4642 * 1) read capacity of echo (regular) buffer
4643 * 2) reserve device
4644 * 3) do write-read-compare data pattern test
4645 * 4) release
4646 * 5) update nego parms to target struct
4647 */
Christoph Hellwig69218ee2005-08-18 16:26:15 +02004648 cfg.cfghdr.hdr = &header1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004649 cfg.physAddr = cfg1_dma_addr;
4650 cfg.action = MPI_CONFIG_ACTION_PAGE_WRITE_CURRENT;
4651 cfg.dir = 1;
4652
4653 iocmd.cmd = TEST_UNIT_READY;
4654 iocmd.data_dma = -1;
4655 iocmd.data = NULL;
4656 iocmd.size = 0;
4657 notDone = 1;
4658 while (notDone) {
4659 if (mptscsih_do_cmd(hd, &iocmd) < 0)
4660 goto target_done;
4661
4662 if (hd->pLocal == NULL)
4663 goto target_done;
4664
4665 rc = hd->pLocal->completion;
4666 if (rc == MPT_SCANDV_GOOD)
4667 notDone = 0;
4668 else if (rc == MPT_SCANDV_SENSE) {
4669 u8 skey = hd->pLocal->sense[2] & 0x0F;
4670 u8 asc = hd->pLocal->sense[12];
4671 u8 ascq = hd->pLocal->sense[13];
4672 ddvprintk((MYIOC_s_INFO_FMT
4673 "SenseKey:ASC:ASCQ = (%x:%02x:%02x)\n",
4674 ioc->name, skey, asc, ascq));
4675
4676 if (skey == UNIT_ATTENTION)
4677 notDone++; /* repeat */
4678 else if ((skey == NOT_READY) &&
4679 (asc == 0x04)&&(ascq == 0x01)) {
4680 /* wait then repeat */
4681 mdelay (2000);
4682 notDone++;
4683 } else if ((skey == NOT_READY) && (asc == 0x3A)) {
4684 /* no medium, try read test anyway */
4685 notDone = 0;
4686 } else {
4687 /* All other errors are fatal.
4688 */
4689 ddvprintk((MYIOC_s_INFO_FMT "DV: fatal error.",
4690 ioc->name));
4691 goto target_done;
4692 }
4693 } else
4694 goto target_done;
4695 }
4696
4697 iocmd.cmd = READ_BUFFER;
4698 iocmd.data_dma = buf1_dma;
4699 iocmd.data = pbuf1;
4700 iocmd.size = 4;
4701 iocmd.flags |= MPT_ICFLAG_BUF_CAP;
4702
4703 dataBufSize = 0;
4704 echoBufSize = 0;
4705 for (patt = 0; patt < 2; patt++) {
4706 if (patt == 0)
4707 iocmd.flags |= MPT_ICFLAG_ECHO;
4708 else
4709 iocmd.flags &= ~MPT_ICFLAG_ECHO;
4710
4711 notDone = 1;
4712 while (notDone) {
4713 bufsize = 0;
4714
4715 /* If not ready after 8 trials,
4716 * give up on this device.
4717 */
4718 if (notDone > 8)
4719 goto target_done;
4720
4721 if (mptscsih_do_cmd(hd, &iocmd) < 0)
4722 goto target_done;
4723 else if (hd->pLocal == NULL)
4724 goto target_done;
4725 else {
4726 rc = hd->pLocal->completion;
4727 ddvprintk(("ReadBuffer Comp Code %d", rc));
4728 ddvprintk((" buff: %0x %0x %0x %0x\n",
4729 pbuf1[0], pbuf1[1], pbuf1[2], pbuf1[3]));
4730
4731 if (rc == MPT_SCANDV_GOOD) {
4732 notDone = 0;
4733 if (iocmd.flags & MPT_ICFLAG_ECHO) {
4734 bufsize = ((pbuf1[2] & 0x1F) <<8) | pbuf1[3];
Moore, Eric Dean466544d2005-09-14 18:09:10 -06004735 if (pbuf1[0] & 0x01)
4736 iocmd.flags |= MPT_ICFLAG_EBOS;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004737 } else {
4738 bufsize = pbuf1[1]<<16 | pbuf1[2]<<8 | pbuf1[3];
4739 }
4740 } else if (rc == MPT_SCANDV_SENSE) {
4741 u8 skey = hd->pLocal->sense[2] & 0x0F;
4742 u8 asc = hd->pLocal->sense[12];
4743 u8 ascq = hd->pLocal->sense[13];
4744 ddvprintk((MYIOC_s_INFO_FMT
4745 "SenseKey:ASC:ASCQ = (%x:%02x:%02x)\n",
4746 ioc->name, skey, asc, ascq));
4747 if (skey == ILLEGAL_REQUEST) {
4748 notDone = 0;
4749 } else if (skey == UNIT_ATTENTION) {
4750 notDone++; /* repeat */
4751 } else if ((skey == NOT_READY) &&
4752 (asc == 0x04)&&(ascq == 0x01)) {
4753 /* wait then repeat */
4754 mdelay (2000);
4755 notDone++;
4756 } else {
4757 /* All other errors are fatal.
4758 */
4759 ddvprintk((MYIOC_s_INFO_FMT "DV: fatal error.",
4760 ioc->name));
4761 goto target_done;
4762 }
4763 } else {
4764 /* All other errors are fatal
4765 */
4766 goto target_done;
4767 }
4768 }
4769 }
4770
4771 if (iocmd.flags & MPT_ICFLAG_ECHO)
4772 echoBufSize = bufsize;
4773 else
4774 dataBufSize = bufsize;
4775 }
4776 sz = 0;
4777 iocmd.flags &= ~MPT_ICFLAG_BUF_CAP;
4778
4779 /* Use echo buffers if possible,
4780 * Exit if both buffers are 0.
4781 */
4782 if (echoBufSize > 0) {
4783 iocmd.flags |= MPT_ICFLAG_ECHO;
4784 if (dataBufSize > 0)
4785 bufsize = min(echoBufSize, dataBufSize);
4786 else
4787 bufsize = echoBufSize;
4788 } else if (dataBufSize == 0)
4789 goto target_done;
4790
4791 ddvprintk((MYIOC_s_INFO_FMT "%s Buffer Capacity %d\n", ioc->name,
4792 (iocmd.flags & MPT_ICFLAG_ECHO) ? "Echo" : " ", bufsize));
4793
4794 /* Data buffers for write-read-compare test max 1K.
4795 */
4796 sz = min(bufsize, 1024);
4797
4798 /* --- loop ----
4799 * On first pass, always issue a reserve.
4800 * On additional loops, only if a reset has occurred.
4801 * iocmd.flags indicates if echo or regular buffer
4802 */
4803 for (patt = 0; patt < 4; patt++) {
4804 ddvprintk(("Pattern %d\n", patt));
4805 if ((iocmd.flags & MPT_ICFLAG_RESERVED) && (iocmd.flags & MPT_ICFLAG_DID_RESET)) {
4806 iocmd.cmd = TEST_UNIT_READY;
4807 iocmd.data_dma = -1;
4808 iocmd.data = NULL;
4809 iocmd.size = 0;
4810 if (mptscsih_do_cmd(hd, &iocmd) < 0)
4811 goto target_done;
4812
4813 iocmd.cmd = RELEASE;
4814 iocmd.data_dma = -1;
4815 iocmd.data = NULL;
4816 iocmd.size = 0;
4817 if (mptscsih_do_cmd(hd, &iocmd) < 0)
4818 goto target_done;
4819 else if (hd->pLocal == NULL)
4820 goto target_done;
4821 else {
4822 rc = hd->pLocal->completion;
4823 ddvprintk(("Release rc %d\n", rc));
4824 if (rc == MPT_SCANDV_GOOD)
4825 iocmd.flags &= ~MPT_ICFLAG_RESERVED;
4826 else
4827 goto target_done;
4828 }
4829 iocmd.flags &= ~MPT_ICFLAG_RESERVED;
4830 }
4831 iocmd.flags &= ~MPT_ICFLAG_DID_RESET;
4832
Moore, Eric Dean466544d2005-09-14 18:09:10 -06004833 if (iocmd.flags & MPT_ICFLAG_EBOS)
4834 goto skip_Reserve;
4835
Linus Torvalds1da177e2005-04-16 15:20:36 -07004836 repeat = 5;
4837 while (repeat && (!(iocmd.flags & MPT_ICFLAG_RESERVED))) {
4838 iocmd.cmd = RESERVE;
4839 iocmd.data_dma = -1;
4840 iocmd.data = NULL;
4841 iocmd.size = 0;
4842 if (mptscsih_do_cmd(hd, &iocmd) < 0)
4843 goto target_done;
4844 else if (hd->pLocal == NULL)
4845 goto target_done;
4846 else {
4847 rc = hd->pLocal->completion;
4848 if (rc == MPT_SCANDV_GOOD) {
4849 iocmd.flags |= MPT_ICFLAG_RESERVED;
4850 } else if (rc == MPT_SCANDV_SENSE) {
4851 /* Wait if coming ready
4852 */
4853 u8 skey = hd->pLocal->sense[2] & 0x0F;
4854 u8 asc = hd->pLocal->sense[12];
4855 u8 ascq = hd->pLocal->sense[13];
4856 ddvprintk((MYIOC_s_INFO_FMT
4857 "DV: Reserve Failed: ", ioc->name));
4858 ddvprintk(("SenseKey:ASC:ASCQ = (%x:%02x:%02x)\n",
4859 skey, asc, ascq));
4860
4861 if ((skey == NOT_READY) && (asc == 0x04)&&
4862 (ascq == 0x01)) {
4863 /* wait then repeat */
4864 mdelay (2000);
4865 notDone++;
4866 } else {
4867 ddvprintk((MYIOC_s_INFO_FMT
4868 "DV: Reserved Failed.", ioc->name));
4869 goto target_done;
4870 }
4871 } else {
4872 ddvprintk((MYIOC_s_INFO_FMT "DV: Reserved Failed.",
4873 ioc->name));
4874 goto target_done;
4875 }
4876 }
4877 }
4878
Moore, Eric Dean466544d2005-09-14 18:09:10 -06004879skip_Reserve:
Linus Torvalds1da177e2005-04-16 15:20:36 -07004880 mptscsih_fillbuf(pbuf1, sz, patt, 1);
4881 iocmd.cmd = WRITE_BUFFER;
4882 iocmd.data_dma = buf1_dma;
4883 iocmd.data = pbuf1;
4884 iocmd.size = sz;
4885 if (mptscsih_do_cmd(hd, &iocmd) < 0)
4886 goto target_done;
4887 else if (hd->pLocal == NULL)
4888 goto target_done;
4889 else {
4890 rc = hd->pLocal->completion;
4891 if (rc == MPT_SCANDV_GOOD)
4892 ; /* Issue read buffer */
4893 else if (rc == MPT_SCANDV_DID_RESET) {
4894 /* If using echo buffers, reset to data buffers.
4895 * Else do Fallback and restart
4896 * this test (re-issue reserve
4897 * because of bus reset).
4898 */
4899 if ((iocmd.flags & MPT_ICFLAG_ECHO) && (dataBufSize >= bufsize)) {
4900 iocmd.flags &= ~MPT_ICFLAG_ECHO;
4901 } else {
4902 dv.cmd = MPT_FALLBACK;
4903 mptscsih_dv_parms(hd, &dv, (void *)pcfg1Data);
4904
4905 if (mpt_config(hd->ioc, &cfg) != 0)
4906 goto target_done;
4907
4908 if ((!dv.now.width) && (!dv.now.offset))
4909 goto target_done;
4910 }
4911
4912 iocmd.flags |= MPT_ICFLAG_DID_RESET;
4913 patt = -1;
4914 continue;
4915 } else if (rc == MPT_SCANDV_SENSE) {
4916 /* Restart data test if UA, else quit.
4917 */
4918 u8 skey = hd->pLocal->sense[2] & 0x0F;
4919 ddvprintk((MYIOC_s_INFO_FMT
4920 "SenseKey:ASC:ASCQ = (%x:%02x:%02x)\n", ioc->name, skey,
4921 hd->pLocal->sense[12], hd->pLocal->sense[13]));
4922 if (skey == UNIT_ATTENTION) {
4923 patt = -1;
4924 continue;
4925 } else if (skey == ILLEGAL_REQUEST) {
4926 if (iocmd.flags & MPT_ICFLAG_ECHO) {
4927 if (dataBufSize >= bufsize) {
4928 iocmd.flags &= ~MPT_ICFLAG_ECHO;
4929 patt = -1;
4930 continue;
4931 }
4932 }
4933 goto target_done;
4934 }
4935 else
4936 goto target_done;
4937 } else {
4938 /* fatal error */
4939 goto target_done;
4940 }
4941 }
4942
4943 iocmd.cmd = READ_BUFFER;
4944 iocmd.data_dma = buf2_dma;
4945 iocmd.data = pbuf2;
4946 iocmd.size = sz;
4947 if (mptscsih_do_cmd(hd, &iocmd) < 0)
4948 goto target_done;
4949 else if (hd->pLocal == NULL)
4950 goto target_done;
4951 else {
4952 rc = hd->pLocal->completion;
4953 if (rc == MPT_SCANDV_GOOD) {
4954 /* If buffers compare,
4955 * go to next pattern,
4956 * else, do a fallback and restart
4957 * data transfer test.
4958 */
4959 if (memcmp (pbuf1, pbuf2, sz) == 0) {
4960 ; /* goto next pattern */
4961 } else {
4962 /* Miscompare with Echo buffer, go to data buffer,
4963 * if that buffer exists.
4964 * Miscompare with Data buffer, check first 4 bytes,
4965 * some devices return capacity. Exit in this case.
4966 */
4967 if (iocmd.flags & MPT_ICFLAG_ECHO) {
4968 if (dataBufSize >= bufsize)
4969 iocmd.flags &= ~MPT_ICFLAG_ECHO;
4970 else
4971 goto target_done;
4972 } else {
4973 if (dataBufSize == (pbuf2[1]<<16 | pbuf2[2]<<8 | pbuf2[3])) {
4974 /* Argh. Device returning wrong data.
4975 * Quit DV for this device.
4976 */
4977 goto target_done;
4978 }
4979
4980 /* Had an actual miscompare. Slow down.*/
4981 dv.cmd = MPT_FALLBACK;
4982 mptscsih_dv_parms(hd, &dv, (void *)pcfg1Data);
4983
4984 if (mpt_config(hd->ioc, &cfg) != 0)
4985 goto target_done;
4986
4987 if ((!dv.now.width) && (!dv.now.offset))
4988 goto target_done;
4989 }
4990
4991 patt = -1;
4992 continue;
4993 }
4994 } else if (rc == MPT_SCANDV_DID_RESET) {
4995 /* Do Fallback and restart
4996 * this test (re-issue reserve
4997 * because of bus reset).
4998 */
4999 dv.cmd = MPT_FALLBACK;
5000 mptscsih_dv_parms(hd, &dv, (void *)pcfg1Data);
5001
5002 if (mpt_config(hd->ioc, &cfg) != 0)
5003 goto target_done;
5004
5005 if ((!dv.now.width) && (!dv.now.offset))
5006 goto target_done;
5007
5008 iocmd.flags |= MPT_ICFLAG_DID_RESET;
5009 patt = -1;
5010 continue;
5011 } else if (rc == MPT_SCANDV_SENSE) {
5012 /* Restart data test if UA, else quit.
5013 */
5014 u8 skey = hd->pLocal->sense[2] & 0x0F;
5015 ddvprintk((MYIOC_s_INFO_FMT
5016 "SenseKey:ASC:ASCQ = (%x:%02x:%02x)\n", ioc->name, skey,
5017 hd->pLocal->sense[12], hd->pLocal->sense[13]));
5018 if (skey == UNIT_ATTENTION) {
5019 patt = -1;
5020 continue;
5021 }
5022 else
5023 goto target_done;
5024 } else {
5025 /* fatal error */
5026 goto target_done;
5027 }
5028 }
5029
5030 } /* --- end of patt loop ---- */
5031
5032target_done:
5033 if (iocmd.flags & MPT_ICFLAG_RESERVED) {
5034 iocmd.cmd = RELEASE;
5035 iocmd.data_dma = -1;
5036 iocmd.data = NULL;
5037 iocmd.size = 0;
5038 if (mptscsih_do_cmd(hd, &iocmd) < 0)
5039 printk(MYIOC_s_INFO_FMT "DV: Release failed. id %d",
5040 ioc->name, id);
5041 else if (hd->pLocal) {
5042 if (hd->pLocal->completion == MPT_SCANDV_GOOD)
5043 iocmd.flags &= ~MPT_ICFLAG_RESERVED;
5044 } else {
5045 printk(MYIOC_s_INFO_FMT "DV: Release failed. id %d",
5046 ioc->name, id);
5047 }
5048 }
5049
5050
5051 /* Set if cfg1_dma_addr contents is valid
5052 */
Christoph Hellwig69218ee2005-08-18 16:26:15 +02005053 if ((cfg.cfghdr.hdr != NULL) && (retcode == 0)){
Linus Torvalds1da177e2005-04-16 15:20:36 -07005054 /* If disk, not U320, disable QAS
5055 */
5056 if ((inq0 == 0) && (dv.now.factor > MPT_ULTRA320)) {
5057 hd->ioc->spi_data.noQas = MPT_TARGET_NO_NEGO_QAS;
Christoph Hellwigc6678e02005-08-18 16:24:53 +02005058 ddvprintk((MYIOC_s_NOTE_FMT
Linus Torvalds1da177e2005-04-16 15:20:36 -07005059 "noQas set due to id=%d has factor=%x\n", ioc->name, id, dv.now.factor));
5060 }
5061
5062 dv.cmd = MPT_SAVE;
5063 mptscsih_dv_parms(hd, &dv, (void *)pcfg1Data);
5064
5065 /* Double writes to SDP1 can cause problems,
5066 * skip save of the final negotiated settings to
5067 * SCSI device page 1.
5068 *
Christoph Hellwig69218ee2005-08-18 16:26:15 +02005069 cfg.cfghdr.hdr = &header1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005070 cfg.physAddr = cfg1_dma_addr;
5071 cfg.action = MPI_CONFIG_ACTION_PAGE_WRITE_CURRENT;
5072 cfg.dir = 1;
5073 mpt_config(hd->ioc, &cfg);
5074 */
5075 }
5076
5077 /* If this is a RAID Passthrough, enable internal IOs
5078 */
5079 if (iocmd.flags & MPT_ICFLAG_PHYS_DISK) {
5080 if (mptscsih_do_raid(hd, MPI_RAID_ACTION_ENABLE_PHYS_IO, &iocmd) < 0)
5081 ddvprintk((MYIOC_s_ERR_FMT "RAID Enable FAILED!\n", ioc->name));
5082 }
5083
5084 /* Done with the DV scan of the current target
5085 */
5086 if (pDvBuf)
5087 pci_free_consistent(ioc->pcidev, dv_alloc, pDvBuf, dvbuf_dma);
5088
5089 ddvtprintk((MYIOC_s_INFO_FMT "DV Done id=%d\n",
5090 ioc->name, id));
5091
5092 return retcode;
5093}
5094
5095/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
5096/* mptscsih_dv_parms - perform a variety of operations on the
5097 * parameters used for negotiation.
5098 * @hd: Pointer to a SCSI host.
5099 * @dv: Pointer to a structure that contains the maximum and current
5100 * negotiated parameters.
5101 */
5102static void
5103mptscsih_dv_parms(MPT_SCSI_HOST *hd, DVPARAMETERS *dv,void *pPage)
5104{
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07005105 VirtTarget *vtarget;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005106 SCSIDevicePage0_t *pPage0;
5107 SCSIDevicePage1_t *pPage1;
5108 int val = 0, data, configuration;
5109 u8 width = 0;
5110 u8 offset = 0;
5111 u8 factor = 0;
5112 u8 negoFlags = 0;
5113 u8 cmd = dv->cmd;
5114 u8 id = dv->id;
5115
5116 switch (cmd) {
5117 case MPT_GET_NVRAM_VALS:
5118 ddvprintk((MYIOC_s_NOTE_FMT "Getting NVRAM: ",
5119 hd->ioc->name));
5120 /* Get the NVRAM values and save in tmax
5121 * If not an LVD bus, the adapter minSyncFactor has been
5122 * already throttled back.
5123 */
Moore, Eric Dean466544d2005-09-14 18:09:10 -06005124 negoFlags = hd->ioc->spi_data.noQas;
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07005125 if ((hd->Targets)&&((vtarget = hd->Targets[(int)id]) != NULL) && !vtarget->raidVolume) {
5126 width = vtarget->maxWidth;
5127 offset = vtarget->maxOffset;
5128 factor = vtarget->minSyncFactor;
5129 negoFlags |= vtarget->negoFlags;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005130 } else {
5131 if (hd->ioc->spi_data.nvram && (hd->ioc->spi_data.nvram[id] != MPT_HOST_NVRAM_INVALID)) {
5132 data = hd->ioc->spi_data.nvram[id];
5133 width = data & MPT_NVRAM_WIDE_DISABLE ? 0 : 1;
5134 if ((offset = hd->ioc->spi_data.maxSyncOffset) == 0)
5135 factor = MPT_ASYNC;
5136 else {
5137 factor = (data & MPT_NVRAM_SYNC_MASK) >> MPT_NVRAM_SYNC_SHIFT;
5138 if ((factor == 0) || (factor == MPT_ASYNC)){
5139 factor = MPT_ASYNC;
5140 offset = 0;
5141 }
5142 }
5143 } else {
5144 width = MPT_NARROW;
5145 offset = 0;
5146 factor = MPT_ASYNC;
5147 }
5148
5149 /* Set the negotiation flags */
Linus Torvalds1da177e2005-04-16 15:20:36 -07005150 if (!width)
5151 negoFlags |= MPT_TARGET_NO_NEGO_WIDE;
5152
5153 if (!offset)
5154 negoFlags |= MPT_TARGET_NO_NEGO_SYNC;
5155 }
5156
5157 /* limit by adapter capabilities */
5158 width = min(width, hd->ioc->spi_data.maxBusWidth);
5159 offset = min(offset, hd->ioc->spi_data.maxSyncOffset);
5160 factor = max(factor, hd->ioc->spi_data.minSyncFactor);
5161
5162 /* Check Consistency */
5163 if (offset && (factor < MPT_ULTRA2) && !width)
5164 factor = MPT_ULTRA2;
5165
5166 dv->max.width = width;
5167 dv->max.offset = offset;
5168 dv->max.factor = factor;
5169 dv->max.flags = negoFlags;
5170 ddvprintk((" id=%d width=%d factor=%x offset=%x flags=%x\n",
5171 id, width, factor, offset, negoFlags));
5172 break;
5173
5174 case MPT_UPDATE_MAX:
5175 ddvprintk((MYIOC_s_NOTE_FMT
5176 "Updating with SDP0 Data: ", hd->ioc->name));
5177 /* Update tmax values with those from Device Page 0.*/
5178 pPage0 = (SCSIDevicePage0_t *) pPage;
5179 if (pPage0) {
Christoph Hellwig637fa992005-08-18 16:25:44 +02005180 val = le32_to_cpu(pPage0->NegotiatedParameters);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005181 dv->max.width = val & MPI_SCSIDEVPAGE0_NP_WIDE ? 1 : 0;
5182 dv->max.offset = (val&MPI_SCSIDEVPAGE0_NP_NEG_SYNC_OFFSET_MASK) >> 16;
5183 dv->max.factor = (val&MPI_SCSIDEVPAGE0_NP_NEG_SYNC_PERIOD_MASK) >> 8;
5184 }
5185
5186 dv->now.width = dv->max.width;
5187 dv->now.offset = dv->max.offset;
5188 dv->now.factor = dv->max.factor;
5189 ddvprintk(("id=%d width=%d factor=%x offset=%x flags=%x\n",
5190 id, dv->now.width, dv->now.factor, dv->now.offset, dv->now.flags));
5191 break;
5192
5193 case MPT_SET_MAX:
5194 ddvprintk((MYIOC_s_NOTE_FMT "Setting Max: ",
5195 hd->ioc->name));
5196 /* Set current to the max values. Update the config page.*/
5197 dv->now.width = dv->max.width;
5198 dv->now.offset = dv->max.offset;
5199 dv->now.factor = dv->max.factor;
5200 dv->now.flags = dv->max.flags;
5201
5202 pPage1 = (SCSIDevicePage1_t *)pPage;
5203 if (pPage1) {
5204 mptscsih_setDevicePage1Flags (dv->now.width, dv->now.factor,
5205 dv->now.offset, &val, &configuration, dv->now.flags);
5206 dnegoprintk(("Setting Max: id=%d width=%d factor=%x offset=%x negoFlags=%x request=%x config=%x\n",
5207 id, dv->now.width, dv->now.factor, dv->now.offset, dv->now.flags, val, configuration));
Christoph Hellwig637fa992005-08-18 16:25:44 +02005208 pPage1->RequestedParameters = cpu_to_le32(val);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005209 pPage1->Reserved = 0;
Christoph Hellwig637fa992005-08-18 16:25:44 +02005210 pPage1->Configuration = cpu_to_le32(configuration);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005211 }
5212
Christoph Hellwig637fa992005-08-18 16:25:44 +02005213 ddvprintk(("id=%d width=%d factor=%x offset=%x negoFlags=%x request=%x configuration=%x\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07005214 id, dv->now.width, dv->now.factor, dv->now.offset, dv->now.flags, val, configuration));
5215 break;
5216
5217 case MPT_SET_MIN:
5218 ddvprintk((MYIOC_s_NOTE_FMT "Setting Min: ",
5219 hd->ioc->name));
5220 /* Set page to asynchronous and narrow
5221 * Do not update now, breaks fallback routine. */
5222 width = MPT_NARROW;
5223 offset = 0;
5224 factor = MPT_ASYNC;
5225 negoFlags = dv->max.flags;
5226
5227 pPage1 = (SCSIDevicePage1_t *)pPage;
5228 if (pPage1) {
5229 mptscsih_setDevicePage1Flags (width, factor,
5230 offset, &val, &configuration, negoFlags);
5231 dnegoprintk(("Setting Min: id=%d width=%d factor=%x offset=%x negoFlags=%x request=%x config=%x\n",
5232 id, width, factor, offset, negoFlags, val, configuration));
Christoph Hellwig637fa992005-08-18 16:25:44 +02005233 pPage1->RequestedParameters = cpu_to_le32(val);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005234 pPage1->Reserved = 0;
Christoph Hellwig637fa992005-08-18 16:25:44 +02005235 pPage1->Configuration = cpu_to_le32(configuration);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005236 }
5237 ddvprintk(("id=%d width=%d factor=%x offset=%x request=%x config=%x negoFlags=%x\n",
5238 id, width, factor, offset, val, configuration, negoFlags));
5239 break;
5240
5241 case MPT_FALLBACK:
5242 ddvprintk((MYIOC_s_NOTE_FMT
5243 "Fallback: Start: offset %d, factor %x, width %d \n",
5244 hd->ioc->name, dv->now.offset,
5245 dv->now.factor, dv->now.width));
5246 width = dv->now.width;
5247 offset = dv->now.offset;
5248 factor = dv->now.factor;
5249 if ((offset) && (dv->max.width)) {
5250 if (factor < MPT_ULTRA160)
5251 factor = MPT_ULTRA160;
5252 else if (factor < MPT_ULTRA2) {
5253 factor = MPT_ULTRA2;
5254 width = MPT_WIDE;
5255 } else if ((factor == MPT_ULTRA2) && width) {
5256 factor = MPT_ULTRA2;
5257 width = MPT_NARROW;
5258 } else if (factor < MPT_ULTRA) {
5259 factor = MPT_ULTRA;
5260 width = MPT_WIDE;
5261 } else if ((factor == MPT_ULTRA) && width) {
5262 width = MPT_NARROW;
5263 } else if (factor < MPT_FAST) {
5264 factor = MPT_FAST;
5265 width = MPT_WIDE;
5266 } else if ((factor == MPT_FAST) && width) {
5267 factor = MPT_FAST;
5268 width = MPT_NARROW;
5269 } else if (factor < MPT_SCSI) {
5270 factor = MPT_SCSI;
5271 width = MPT_WIDE;
5272 } else if ((factor == MPT_SCSI) && width) {
5273 factor = MPT_SCSI;
5274 width = MPT_NARROW;
5275 } else {
5276 factor = MPT_ASYNC;
5277 offset = 0;
5278 }
5279
5280 } else if (offset) {
5281 width = MPT_NARROW;
5282 if (factor < MPT_ULTRA)
5283 factor = MPT_ULTRA;
5284 else if (factor < MPT_FAST)
5285 factor = MPT_FAST;
5286 else if (factor < MPT_SCSI)
5287 factor = MPT_SCSI;
5288 else {
5289 factor = MPT_ASYNC;
5290 offset = 0;
5291 }
5292
5293 } else {
5294 width = MPT_NARROW;
5295 factor = MPT_ASYNC;
5296 }
5297 dv->max.flags |= MPT_TARGET_NO_NEGO_QAS;
5298 dv->max.flags &= ~MPT_TAPE_NEGO_IDP;
5299
5300 dv->now.width = width;
5301 dv->now.offset = offset;
5302 dv->now.factor = factor;
5303 dv->now.flags = dv->max.flags;
5304
5305 pPage1 = (SCSIDevicePage1_t *)pPage;
5306 if (pPage1) {
5307 mptscsih_setDevicePage1Flags (width, factor, offset, &val,
5308 &configuration, dv->now.flags);
Christoph Hellwig637fa992005-08-18 16:25:44 +02005309 dnegoprintk(("Finish: id=%d width=%d offset=%d factor=%x negoFlags=%x request=%x config=%x\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07005310 id, width, offset, factor, dv->now.flags, val, configuration));
5311
Christoph Hellwig637fa992005-08-18 16:25:44 +02005312 pPage1->RequestedParameters = cpu_to_le32(val);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005313 pPage1->Reserved = 0;
Christoph Hellwig637fa992005-08-18 16:25:44 +02005314 pPage1->Configuration = cpu_to_le32(configuration);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005315 }
5316
5317 ddvprintk(("Finish: id=%d offset=%d factor=%x width=%d request=%x config=%x\n",
5318 id, dv->now.offset, dv->now.factor, dv->now.width, val, configuration));
5319 break;
5320
5321 case MPT_SAVE:
5322 ddvprintk((MYIOC_s_NOTE_FMT
5323 "Saving to Target structure: ", hd->ioc->name));
5324 ddvprintk(("id=%d width=%x factor=%x offset=%d flags=%x\n",
5325 id, dv->now.width, dv->now.factor, dv->now.offset, dv->now.flags));
5326
5327 /* Save these values to target structures
5328 * or overwrite nvram (phys disks only).
5329 */
5330
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07005331 if ((hd->Targets)&&((vtarget = hd->Targets[(int)id]) != NULL) && !vtarget->raidVolume ) {
5332 vtarget->maxWidth = dv->now.width;
5333 vtarget->maxOffset = dv->now.offset;
5334 vtarget->minSyncFactor = dv->now.factor;
5335 vtarget->negoFlags = dv->now.flags;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005336 } else {
5337 /* Preserv all flags, use
5338 * read-modify-write algorithm
5339 */
5340 if (hd->ioc->spi_data.nvram) {
5341 data = hd->ioc->spi_data.nvram[id];
5342
5343 if (dv->now.width)
5344 data &= ~MPT_NVRAM_WIDE_DISABLE;
5345 else
5346 data |= MPT_NVRAM_WIDE_DISABLE;
5347
5348 if (!dv->now.offset)
5349 factor = MPT_ASYNC;
5350
5351 data &= ~MPT_NVRAM_SYNC_MASK;
5352 data |= (dv->now.factor << MPT_NVRAM_SYNC_SHIFT) & MPT_NVRAM_SYNC_MASK;
5353
5354 hd->ioc->spi_data.nvram[id] = data;
5355 }
5356 }
5357 break;
5358 }
5359}
5360
5361/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
5362/* mptscsih_fillbuf - fill a buffer with a special data pattern
5363 * cleanup. For bus scan only.
5364 *
5365 * @buffer: Pointer to data buffer to be filled.
5366 * @size: Number of bytes to fill
5367 * @index: Pattern index
5368 * @width: bus width, 0 (8 bits) or 1 (16 bits)
5369 */
5370static void
5371mptscsih_fillbuf(char *buffer, int size, int index, int width)
5372{
5373 char *ptr = buffer;
5374 int ii;
5375 char byte;
5376 short val;
5377
5378 switch (index) {
5379 case 0:
5380
5381 if (width) {
5382 /* Pattern: 0000 FFFF 0000 FFFF
5383 */
5384 for (ii=0; ii < size; ii++, ptr++) {
5385 if (ii & 0x02)
5386 *ptr = 0xFF;
5387 else
5388 *ptr = 0x00;
5389 }
5390 } else {
5391 /* Pattern: 00 FF 00 FF
5392 */
5393 for (ii=0; ii < size; ii++, ptr++) {
5394 if (ii & 0x01)
5395 *ptr = 0xFF;
5396 else
5397 *ptr = 0x00;
5398 }
5399 }
5400 break;
5401
5402 case 1:
5403 if (width) {
5404 /* Pattern: 5555 AAAA 5555 AAAA 5555
5405 */
5406 for (ii=0; ii < size; ii++, ptr++) {
5407 if (ii & 0x02)
5408 *ptr = 0xAA;
5409 else
5410 *ptr = 0x55;
5411 }
5412 } else {
5413 /* Pattern: 55 AA 55 AA 55
5414 */
5415 for (ii=0; ii < size; ii++, ptr++) {
5416 if (ii & 0x01)
5417 *ptr = 0xAA;
5418 else
5419 *ptr = 0x55;
5420 }
5421 }
5422 break;
5423
5424 case 2:
5425 /* Pattern: 00 01 02 03 04 05
5426 * ... FE FF 00 01..
5427 */
5428 for (ii=0; ii < size; ii++, ptr++)
5429 *ptr = (char) ii;
5430 break;
5431
5432 case 3:
5433 if (width) {
5434 /* Wide Pattern: FFFE 0001 FFFD 0002
5435 * ... 4000 DFFF 8000 EFFF
5436 */
5437 byte = 0;
5438 for (ii=0; ii < size/2; ii++) {
5439 /* Create the base pattern
5440 */
5441 val = (1 << byte);
5442 /* every 64 (0x40) bytes flip the pattern
5443 * since we fill 2 bytes / iteration,
5444 * test for ii = 0x20
5445 */
5446 if (ii & 0x20)
5447 val = ~(val);
5448
5449 if (ii & 0x01) {
5450 *ptr = (char)( (val & 0xFF00) >> 8);
5451 ptr++;
5452 *ptr = (char)(val & 0xFF);
5453 byte++;
5454 byte &= 0x0F;
5455 } else {
5456 val = ~val;
5457 *ptr = (char)( (val & 0xFF00) >> 8);
5458 ptr++;
5459 *ptr = (char)(val & 0xFF);
5460 }
5461
5462 ptr++;
5463 }
5464 } else {
5465 /* Narrow Pattern: FE 01 FD 02 FB 04
5466 * .. 7F 80 01 FE 02 FD ... 80 7F
5467 */
5468 byte = 0;
5469 for (ii=0; ii < size; ii++, ptr++) {
5470 /* Base pattern - first 32 bytes
5471 */
5472 if (ii & 0x01) {
5473 *ptr = (1 << byte);
5474 byte++;
5475 byte &= 0x07;
5476 } else {
5477 *ptr = (char) (~(1 << byte));
5478 }
5479
5480 /* Flip the pattern every 32 bytes
5481 */
5482 if (ii & 0x20)
5483 *ptr = ~(*ptr);
5484 }
5485 }
5486 break;
5487 }
5488}
Moore, Eric Deanf2ea8672005-11-16 18:54:23 -07005489
5490/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
5491/* If DV disabled (negoNvram set to USE_NVARM) or if not LUN 0, return.
5492 * Else set the NEED_DV flag after Read Capacity Issued (disks)
5493 * or Mode Sense (cdroms).
5494 *
5495 * Tapes, initTarget will set this flag on completion of Inquiry command.
5496 * Called only if DV_NOT_DONE flag is set
5497 */
5498static void
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07005499mptscsih_set_dvflags(MPT_SCSI_HOST *hd, struct scsi_cmnd *sc)
Moore, Eric Deanf2ea8672005-11-16 18:54:23 -07005500{
5501 MPT_ADAPTER *ioc = hd->ioc;
5502 u8 cmd;
5503 SpiCfgData *pSpi;
5504
5505 ddvtprintk((MYIOC_s_NOTE_FMT
5506 " set_dvflags: id=%d lun=%d negoNvram=%x cmd=%x\n",
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07005507 hd->ioc->name, sc->device->id, sc->device->lun , hd->negoNvram, sc->cmnd[0]));
Moore, Eric Deanf2ea8672005-11-16 18:54:23 -07005508
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07005509 if ((sc->device->lun != 0) || (hd->negoNvram != 0))
Moore, Eric Deanf2ea8672005-11-16 18:54:23 -07005510 return;
5511
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07005512 cmd = sc->cmnd[0];
Moore, Eric Deanf2ea8672005-11-16 18:54:23 -07005513
5514 if ((cmd == READ_CAPACITY) || (cmd == MODE_SENSE)) {
5515 pSpi = &ioc->spi_data;
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07005516 if ((ioc->raid_data.isRaid & (1 << sc->device->id)) && ioc->raid_data.pIocPg3) {
Moore, Eric Deanf2ea8672005-11-16 18:54:23 -07005517 /* Set NEED_DV for all hidden disks
5518 */
5519 Ioc3PhysDisk_t *pPDisk = ioc->raid_data.pIocPg3->PhysDisk;
5520 int numPDisk = ioc->raid_data.pIocPg3->NumPhysDisks;
5521
5522 while (numPDisk) {
5523 pSpi->dvStatus[pPDisk->PhysDiskID] |= MPT_SCSICFG_NEED_DV;
5524 ddvtprintk(("NEED_DV set for phys disk id %d\n", pPDisk->PhysDiskID));
5525 pPDisk++;
5526 numPDisk--;
5527 }
5528 }
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07005529 pSpi->dvStatus[sc->device->id] |= MPT_SCSICFG_NEED_DV;
5530 ddvtprintk(("NEED_DV set for visible disk id %d\n", sc->device->id));
Moore, Eric Deanf2ea8672005-11-16 18:54:23 -07005531 }
5532}
5533
5534/* mptscsih_raid_set_dv_flags()
5535 *
5536 * New or replaced disk. Set DV flag and schedule DV.
5537 */
5538static void
5539mptscsih_set_dvflags_raid(MPT_SCSI_HOST *hd, int id)
5540{
5541 MPT_ADAPTER *ioc = hd->ioc;
5542 SpiCfgData *pSpi = &ioc->spi_data;
5543 Ioc3PhysDisk_t *pPDisk;
5544 int numPDisk;
5545
5546 if (hd->negoNvram != 0)
5547 return;
5548
5549 ddvtprintk(("DV requested for phys disk id %d\n", id));
5550 if (ioc->raid_data.pIocPg3) {
5551 pPDisk = ioc->raid_data.pIocPg3->PhysDisk;
5552 numPDisk = ioc->raid_data.pIocPg3->NumPhysDisks;
5553 while (numPDisk) {
5554 if (id == pPDisk->PhysDiskNum) {
5555 pSpi->dvStatus[pPDisk->PhysDiskID] =
5556 (MPT_SCSICFG_NEED_DV | MPT_SCSICFG_DV_NOT_DONE);
5557 pSpi->forceDv = MPT_SCSICFG_NEED_DV;
5558 ddvtprintk(("NEED_DV set for phys disk id %d\n",
5559 pPDisk->PhysDiskID));
5560 break;
5561 }
5562 pPDisk++;
5563 numPDisk--;
5564 }
5565
5566 if (numPDisk == 0) {
5567 /* The physical disk that needs DV was not found
5568 * in the stored IOC Page 3. The driver must reload
5569 * this page. DV routine will set the NEED_DV flag for
5570 * all phys disks that have DV_NOT_DONE set.
5571 */
5572 pSpi->forceDv = MPT_SCSICFG_NEED_DV | MPT_SCSICFG_RELOAD_IOC_PG3;
5573 ddvtprintk(("phys disk %d not found. Setting reload IOC Pg3 Flag\n",id));
5574 }
5575 }
5576}
Linus Torvalds1da177e2005-04-16 15:20:36 -07005577#endif /* ~MPTSCSIH_ENABLE_DOMAIN_VALIDATION */
5578
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04005579EXPORT_SYMBOL(mptscsih_remove);
5580EXPORT_SYMBOL(mptscsih_shutdown);
5581#ifdef CONFIG_PM
5582EXPORT_SYMBOL(mptscsih_suspend);
5583EXPORT_SYMBOL(mptscsih_resume);
5584#endif
5585EXPORT_SYMBOL(mptscsih_proc_info);
5586EXPORT_SYMBOL(mptscsih_info);
5587EXPORT_SYMBOL(mptscsih_qcmd);
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07005588EXPORT_SYMBOL(mptscsih_target_alloc);
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04005589EXPORT_SYMBOL(mptscsih_slave_alloc);
Moore, Eric Deanc7c82982005-11-16 18:54:25 -07005590EXPORT_SYMBOL(mptscsih_target_destroy);
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04005591EXPORT_SYMBOL(mptscsih_slave_destroy);
5592EXPORT_SYMBOL(mptscsih_slave_configure);
5593EXPORT_SYMBOL(mptscsih_abort);
5594EXPORT_SYMBOL(mptscsih_dev_reset);
5595EXPORT_SYMBOL(mptscsih_bus_reset);
5596EXPORT_SYMBOL(mptscsih_host_reset);
5597EXPORT_SYMBOL(mptscsih_bios_param);
5598EXPORT_SYMBOL(mptscsih_io_done);
5599EXPORT_SYMBOL(mptscsih_taskmgmt_complete);
5600EXPORT_SYMBOL(mptscsih_scandv_complete);
5601EXPORT_SYMBOL(mptscsih_event_process);
5602EXPORT_SYMBOL(mptscsih_ioc_reset);
Moore, Eric Dean6e3815b2005-06-24 12:18:57 -06005603EXPORT_SYMBOL(mptscsih_change_queue_depth);
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04005604EXPORT_SYMBOL(mptscsih_timer_expired);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005605
Moore, Eric Dean 0d0c7972005-04-22 18:02:09 -04005606/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/