xref: /OK3568_Linux_fs/kernel/drivers/scsi/3w-xxxx.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun /*
2*4882a593Smuzhiyun    3w-xxxx.c -- 3ware Storage Controller device driver for Linux.
3*4882a593Smuzhiyun 
4*4882a593Smuzhiyun    Written By: Adam Radford <aradford@gmail.com>
5*4882a593Smuzhiyun    Modifications By: Joel Jacobson <linux@3ware.com>
6*4882a593Smuzhiyun    		     Arnaldo Carvalho de Melo <acme@conectiva.com.br>
7*4882a593Smuzhiyun                      Brad Strand <linux@3ware.com>
8*4882a593Smuzhiyun 
9*4882a593Smuzhiyun    Copyright (C) 1999-2010 3ware Inc.
10*4882a593Smuzhiyun 
11*4882a593Smuzhiyun    Kernel compatibility By: 	Andre Hedrick <andre@suse.com>
12*4882a593Smuzhiyun    Non-Copyright (C) 2000	Andre Hedrick <andre@suse.com>
13*4882a593Smuzhiyun 
14*4882a593Smuzhiyun    Further tiny build fixes and trivial hoovering    Alan Cox
15*4882a593Smuzhiyun 
16*4882a593Smuzhiyun    This program is free software; you can redistribute it and/or modify
17*4882a593Smuzhiyun    it under the terms of the GNU General Public License as published by
18*4882a593Smuzhiyun    the Free Software Foundation; version 2 of the License.
19*4882a593Smuzhiyun 
20*4882a593Smuzhiyun    This program is distributed in the hope that it will be useful,
21*4882a593Smuzhiyun    but WITHOUT ANY WARRANTY; without even the implied warranty of
22*4882a593Smuzhiyun    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23*4882a593Smuzhiyun    GNU General Public License for more details.
24*4882a593Smuzhiyun 
25*4882a593Smuzhiyun    NO WARRANTY
26*4882a593Smuzhiyun    THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
27*4882a593Smuzhiyun    CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
28*4882a593Smuzhiyun    LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
29*4882a593Smuzhiyun    MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
30*4882a593Smuzhiyun    solely responsible for determining the appropriateness of using and
31*4882a593Smuzhiyun    distributing the Program and assumes all risks associated with its
32*4882a593Smuzhiyun    exercise of rights under this Agreement, including but not limited to
33*4882a593Smuzhiyun    the risks and costs of program errors, damage to or loss of data,
34*4882a593Smuzhiyun    programs or equipment, and unavailability or interruption of operations.
35*4882a593Smuzhiyun 
36*4882a593Smuzhiyun    DISCLAIMER OF LIABILITY
37*4882a593Smuzhiyun    NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
38*4882a593Smuzhiyun    DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
39*4882a593Smuzhiyun    DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
40*4882a593Smuzhiyun    ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
41*4882a593Smuzhiyun    TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
42*4882a593Smuzhiyun    USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
43*4882a593Smuzhiyun    HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
44*4882a593Smuzhiyun 
45*4882a593Smuzhiyun    You should have received a copy of the GNU General Public License
46*4882a593Smuzhiyun    along with this program; if not, write to the Free Software
47*4882a593Smuzhiyun    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
48*4882a593Smuzhiyun 
49*4882a593Smuzhiyun    Bugs/Comments/Suggestions should be mailed to:
50*4882a593Smuzhiyun 
51*4882a593Smuzhiyun    aradford@gmail.com
52*4882a593Smuzhiyun 
53*4882a593Smuzhiyun 
54*4882a593Smuzhiyun    History
55*4882a593Smuzhiyun    -------
56*4882a593Smuzhiyun    0.1.000 -     Initial release.
57*4882a593Smuzhiyun    0.4.000 -     Added support for Asynchronous Event Notification through
58*4882a593Smuzhiyun                  ioctls for 3DM.
59*4882a593Smuzhiyun    1.0.000 -     Added DPO & FUA bit support for WRITE_10 & WRITE_6 cdb
60*4882a593Smuzhiyun                  to disable drive write-cache before writes.
61*4882a593Smuzhiyun    1.1.000 -     Fixed performance bug with DPO & FUA not existing for WRITE_6.
62*4882a593Smuzhiyun    1.2.000 -     Added support for clean shutdown notification/feature table.
63*4882a593Smuzhiyun    1.02.00.001 - Added support for full command packet posts through ioctls
64*4882a593Smuzhiyun                  for 3DM.
65*4882a593Smuzhiyun                  Bug fix so hot spare drives don't show up.
66*4882a593Smuzhiyun    1.02.00.002 - Fix bug with tw_setfeature() call that caused oops on some
67*4882a593Smuzhiyun                  systems.
68*4882a593Smuzhiyun    08/21/00    - release previously allocated resources on failure at
69*4882a593Smuzhiyun                  tw_allocate_memory (acme)
70*4882a593Smuzhiyun    1.02.00.003 - Fix tw_interrupt() to report error to scsi layer when
71*4882a593Smuzhiyun                  controller status is non-zero.
72*4882a593Smuzhiyun                  Added handling of request_sense opcode.
73*4882a593Smuzhiyun                  Fix possible null pointer dereference in
74*4882a593Smuzhiyun                  tw_reset_device_extension()
75*4882a593Smuzhiyun    1.02.00.004 - Add support for device id of 3ware 7000 series controllers.
76*4882a593Smuzhiyun                  Make tw_setfeature() call with interrupts disabled.
77*4882a593Smuzhiyun                  Register interrupt handler before enabling interrupts.
78*4882a593Smuzhiyun                  Clear attention interrupt before draining aen queue.
79*4882a593Smuzhiyun    1.02.00.005 - Allocate bounce buffers and custom queue depth for raid5 for
80*4882a593Smuzhiyun                  6000 and 5000 series controllers.
81*4882a593Smuzhiyun                  Reduce polling mdelays causing problems on some systems.
82*4882a593Smuzhiyun                  Fix use_sg = 1 calculation bug.
83*4882a593Smuzhiyun                  Check for scsi_register returning NULL.
84*4882a593Smuzhiyun                  Add aen count to /proc/scsi/3w-xxxx.
85*4882a593Smuzhiyun                  Remove aen code unit masking in tw_aen_complete().
86*4882a593Smuzhiyun    1.02.00.006 - Remove unit from printk in tw_scsi_eh_abort(), causing
87*4882a593Smuzhiyun                  possible oops.
88*4882a593Smuzhiyun                  Fix possible null pointer dereference in tw_scsi_queue()
89*4882a593Smuzhiyun                  if done function pointer was invalid.
90*4882a593Smuzhiyun    1.02.00.007 - Fix possible null pointer dereferences in tw_ioctl().
91*4882a593Smuzhiyun                  Remove check for invalid done function pointer from
92*4882a593Smuzhiyun                  tw_scsi_queue().
93*4882a593Smuzhiyun    1.02.00.008 - Set max sectors per io to TW_MAX_SECTORS in tw_findcards().
94*4882a593Smuzhiyun                  Add tw_decode_error() for printing readable error messages.
95*4882a593Smuzhiyun                  Print some useful information on certain aen codes.
96*4882a593Smuzhiyun                  Add tw_decode_bits() for interpreting status register output.
97*4882a593Smuzhiyun                  Make scsi_set_pci_device() for kernels >= 2.4.4
98*4882a593Smuzhiyun                  Fix bug where aen's could be lost before a reset.
99*4882a593Smuzhiyun                  Re-add spinlocks in tw_scsi_detect().
100*4882a593Smuzhiyun                  Fix possible null pointer dereference in tw_aen_drain_queue()
101*4882a593Smuzhiyun                  during initialization.
102*4882a593Smuzhiyun                  Clear pci parity errors during initialization and during io.
103*4882a593Smuzhiyun    1.02.00.009 - Remove redundant increment in tw_state_request_start().
104*4882a593Smuzhiyun                  Add ioctl support for direct ATA command passthru.
105*4882a593Smuzhiyun                  Add entire aen code string list.
106*4882a593Smuzhiyun    1.02.00.010 - Cleanup queueing code, fix jbod thoughput.
107*4882a593Smuzhiyun                  Fix get_param for specific units.
108*4882a593Smuzhiyun    1.02.00.011 - Fix bug in tw_aen_complete() where aen's could be lost.
109*4882a593Smuzhiyun                  Fix tw_aen_drain_queue() to display useful info at init.
110*4882a593Smuzhiyun                  Set tw_host->max_id for 12 port cards.
111*4882a593Smuzhiyun                  Add ioctl support for raw command packet post from userspace
112*4882a593Smuzhiyun                  with sglist fragments (parameter and io).
113*4882a593Smuzhiyun    1.02.00.012 - Fix read capacity to under report by 1 sector to fix get
114*4882a593Smuzhiyun                  last sector ioctl.
115*4882a593Smuzhiyun    1.02.00.013 - Fix bug where more AEN codes weren't coming out during
116*4882a593Smuzhiyun                  driver initialization.
117*4882a593Smuzhiyun                  Improved handling of PCI aborts.
118*4882a593Smuzhiyun    1.02.00.014 - Fix bug in tw_findcards() where AEN code could be lost.
119*4882a593Smuzhiyun                  Increase timeout in tw_aen_drain_queue() to 30 seconds.
120*4882a593Smuzhiyun    1.02.00.015 - Re-write raw command post with data ioctl method.
121*4882a593Smuzhiyun                  Remove raid5 bounce buffers for raid5 for 6XXX for kernel 2.5
122*4882a593Smuzhiyun                  Add tw_map/unmap_scsi_sg/single_data() for kernel 2.5
123*4882a593Smuzhiyun                  Replace io_request_lock with host_lock for kernel 2.5
124*4882a593Smuzhiyun                  Set max_cmd_len to 16 for 3dm for kernel 2.5
125*4882a593Smuzhiyun    1.02.00.016 - Set host->max_sectors back up to 256.
126*4882a593Smuzhiyun    1.02.00.017 - Modified pci parity error handling/clearing from config space
127*4882a593Smuzhiyun                  during initialization.
128*4882a593Smuzhiyun    1.02.00.018 - Better handling of request sense opcode and sense information
129*4882a593Smuzhiyun                  for failed commands.  Add tw_decode_sense().
130*4882a593Smuzhiyun                  Replace all mdelay()'s with scsi_sleep().
131*4882a593Smuzhiyun    1.02.00.019 - Revert mdelay's and scsi_sleep's, this caused problems on
132*4882a593Smuzhiyun                  some SMP systems.
133*4882a593Smuzhiyun    1.02.00.020 - Add pci_set_dma_mask(), rewrite kmalloc()/virt_to_bus() to
134*4882a593Smuzhiyun                  pci_alloc/free_consistent().
135*4882a593Smuzhiyun                  Better alignment checking in tw_allocate_memory().
136*4882a593Smuzhiyun                  Cleanup tw_initialize_device_extension().
137*4882a593Smuzhiyun    1.02.00.021 - Bump cmd_per_lun in SHT to 255 for better jbod performance.
138*4882a593Smuzhiyun                  Improve handling of errors in tw_interrupt().
139*4882a593Smuzhiyun                  Add handling/clearing of controller queue error.
140*4882a593Smuzhiyun                  Empty stale responses before draining aen queue.
141*4882a593Smuzhiyun                  Fix tw_scsi_eh_abort() to not reset on every io abort.
142*4882a593Smuzhiyun                  Set can_queue in SHT to 255 to prevent hang from AEN.
143*4882a593Smuzhiyun    1.02.00.022 - Fix possible null pointer dereference in tw_scsi_release().
144*4882a593Smuzhiyun    1.02.00.023 - Fix bug in tw_aen_drain_queue() where unit # was always zero.
145*4882a593Smuzhiyun    1.02.00.024 - Add severity levels to AEN strings.
146*4882a593Smuzhiyun    1.02.00.025 - Fix command interrupt spurious error messages.
147*4882a593Smuzhiyun                  Fix bug in raw command post with data ioctl method.
148*4882a593Smuzhiyun                  Fix bug where rollcall sometimes failed with cable errors.
149*4882a593Smuzhiyun                  Print unit # on all command timeouts.
150*4882a593Smuzhiyun    1.02.00.026 - Fix possible infinite retry bug with power glitch induced
151*4882a593Smuzhiyun                  drive timeouts.
152*4882a593Smuzhiyun                  Cleanup some AEN severity levels.
153*4882a593Smuzhiyun    1.02.00.027 - Add drive not supported AEN code for SATA controllers.
154*4882a593Smuzhiyun                  Remove spurious unknown ioctl error message.
155*4882a593Smuzhiyun    1.02.00.028 - Fix bug where multiple controllers with no units were the
156*4882a593Smuzhiyun                  same card number.
157*4882a593Smuzhiyun                  Fix bug where cards were being shut down more than once.
158*4882a593Smuzhiyun    1.02.00.029 - Add missing pci_free_consistent() in tw_allocate_memory().
159*4882a593Smuzhiyun                  Replace pci_map_single() with pci_map_page() for highmem.
160*4882a593Smuzhiyun                  Check for tw_setfeature() failure.
161*4882a593Smuzhiyun    1.02.00.030 - Make driver 64-bit clean.
162*4882a593Smuzhiyun    1.02.00.031 - Cleanup polling timeouts/routines in several places.
163*4882a593Smuzhiyun                  Add support for mode sense opcode.
164*4882a593Smuzhiyun                  Add support for cache mode page.
165*4882a593Smuzhiyun                  Add support for synchronize cache opcode.
166*4882a593Smuzhiyun    1.02.00.032 - Fix small multicard rollcall bug.
167*4882a593Smuzhiyun                  Make driver stay loaded with no units for hot add/swap.
168*4882a593Smuzhiyun                  Add support for "twe" character device for ioctls.
169*4882a593Smuzhiyun                  Clean up request_id queueing code.
170*4882a593Smuzhiyun                  Fix tw_scsi_queue() spinlocks.
171*4882a593Smuzhiyun    1.02.00.033 - Fix tw_aen_complete() to not queue 'queue empty' AEN's.
172*4882a593Smuzhiyun                  Initialize queues correctly when loading with no valid units.
173*4882a593Smuzhiyun    1.02.00.034 - Fix tw_decode_bits() to handle multiple errors.
174*4882a593Smuzhiyun                  Add support for user configurable cmd_per_lun.
175*4882a593Smuzhiyun                  Add support for sht->slave_configure().
176*4882a593Smuzhiyun    1.02.00.035 - Improve tw_allocate_memory() memory allocation.
177*4882a593Smuzhiyun                  Fix tw_chrdev_ioctl() to sleep correctly.
178*4882a593Smuzhiyun    1.02.00.036 - Increase character ioctl timeout to 60 seconds.
179*4882a593Smuzhiyun    1.02.00.037 - Fix tw_ioctl() to handle all non-data ATA passthru cmds
180*4882a593Smuzhiyun                  for 'smartmontools' support.
181*4882a593Smuzhiyun    1.26.00.038 - Roll driver minor version to 26 to denote kernel 2.6.
182*4882a593Smuzhiyun                  Add support for cmds_per_lun module parameter.
183*4882a593Smuzhiyun    1.26.00.039 - Fix bug in tw_chrdev_ioctl() polling code.
184*4882a593Smuzhiyun                  Fix data_buffer_length usage in tw_chrdev_ioctl().
185*4882a593Smuzhiyun                  Update contact information.
186*4882a593Smuzhiyun    1.26.02.000 - Convert driver to pci_driver format.
187*4882a593Smuzhiyun    1.26.02.001 - Increase max ioctl buffer size to 512 sectors.
188*4882a593Smuzhiyun                  Make tw_scsi_queue() return 0 for 'Unknown scsi opcode'.
189*4882a593Smuzhiyun                  Fix tw_remove() to free irq handler/unregister_chrdev()
190*4882a593Smuzhiyun                  before shutting down card.
191*4882a593Smuzhiyun                  Change to new 'change_queue_depth' api.
192*4882a593Smuzhiyun                  Fix 'handled=1' ISR usage, remove bogus IRQ check.
193*4882a593Smuzhiyun    1.26.02.002 - Free irq handler in __tw_shutdown().
194*4882a593Smuzhiyun                  Turn on RCD bit for caching mode page.
195*4882a593Smuzhiyun                  Serialize reset code.
196*4882a593Smuzhiyun    1.26.02.003 - Force 60 second timeout default.
197*4882a593Smuzhiyun */
198*4882a593Smuzhiyun 
199*4882a593Smuzhiyun #include <linux/module.h>
200*4882a593Smuzhiyun #include <linux/reboot.h>
201*4882a593Smuzhiyun #include <linux/spinlock.h>
202*4882a593Smuzhiyun #include <linux/interrupt.h>
203*4882a593Smuzhiyun #include <linux/moduleparam.h>
204*4882a593Smuzhiyun #include <linux/errno.h>
205*4882a593Smuzhiyun #include <linux/types.h>
206*4882a593Smuzhiyun #include <linux/delay.h>
207*4882a593Smuzhiyun #include <linux/gfp.h>
208*4882a593Smuzhiyun #include <linux/pci.h>
209*4882a593Smuzhiyun #include <linux/time.h>
210*4882a593Smuzhiyun #include <linux/mutex.h>
211*4882a593Smuzhiyun #include <asm/io.h>
212*4882a593Smuzhiyun #include <asm/irq.h>
213*4882a593Smuzhiyun #include <linux/uaccess.h>
214*4882a593Smuzhiyun #include <scsi/scsi.h>
215*4882a593Smuzhiyun #include <scsi/scsi_host.h>
216*4882a593Smuzhiyun #include <scsi/scsi_tcq.h>
217*4882a593Smuzhiyun #include <scsi/scsi_cmnd.h>
218*4882a593Smuzhiyun #include <scsi/scsi_eh.h>
219*4882a593Smuzhiyun #include "3w-xxxx.h"
220*4882a593Smuzhiyun 
221*4882a593Smuzhiyun /* Globals */
222*4882a593Smuzhiyun #define TW_DRIVER_VERSION "1.26.02.003"
223*4882a593Smuzhiyun static DEFINE_MUTEX(tw_mutex);
224*4882a593Smuzhiyun static TW_Device_Extension *tw_device_extension_list[TW_MAX_SLOT];
225*4882a593Smuzhiyun static int tw_device_extension_count = 0;
226*4882a593Smuzhiyun static int twe_major = -1;
227*4882a593Smuzhiyun 
228*4882a593Smuzhiyun /* Module parameters */
229*4882a593Smuzhiyun MODULE_AUTHOR("LSI");
230*4882a593Smuzhiyun MODULE_DESCRIPTION("3ware Storage Controller Linux Driver");
231*4882a593Smuzhiyun MODULE_LICENSE("GPL");
232*4882a593Smuzhiyun MODULE_VERSION(TW_DRIVER_VERSION);
233*4882a593Smuzhiyun 
234*4882a593Smuzhiyun /* Function prototypes */
235*4882a593Smuzhiyun static int tw_reset_device_extension(TW_Device_Extension *tw_dev);
236*4882a593Smuzhiyun 
237*4882a593Smuzhiyun /* Functions */
238*4882a593Smuzhiyun 
239*4882a593Smuzhiyun /* This function will check the status register for unexpected bits */
tw_check_bits(u32 status_reg_value)240*4882a593Smuzhiyun static int tw_check_bits(u32 status_reg_value)
241*4882a593Smuzhiyun {
242*4882a593Smuzhiyun 	if ((status_reg_value & TW_STATUS_EXPECTED_BITS) != TW_STATUS_EXPECTED_BITS) {
243*4882a593Smuzhiyun 		dprintk(KERN_WARNING "3w-xxxx: tw_check_bits(): No expected bits (0x%x).\n", status_reg_value);
244*4882a593Smuzhiyun 		return 1;
245*4882a593Smuzhiyun 	}
246*4882a593Smuzhiyun 	if ((status_reg_value & TW_STATUS_UNEXPECTED_BITS) != 0) {
247*4882a593Smuzhiyun 		dprintk(KERN_WARNING "3w-xxxx: tw_check_bits(): Found unexpected bits (0x%x).\n", status_reg_value);
248*4882a593Smuzhiyun 		return 1;
249*4882a593Smuzhiyun 	}
250*4882a593Smuzhiyun 
251*4882a593Smuzhiyun 	return 0;
252*4882a593Smuzhiyun } /* End tw_check_bits() */
253*4882a593Smuzhiyun 
254*4882a593Smuzhiyun /* This function will print readable messages from status register errors */
tw_decode_bits(TW_Device_Extension * tw_dev,u32 status_reg_value,int print_host)255*4882a593Smuzhiyun static int tw_decode_bits(TW_Device_Extension *tw_dev, u32 status_reg_value, int print_host)
256*4882a593Smuzhiyun {
257*4882a593Smuzhiyun 	char host[16];
258*4882a593Smuzhiyun 
259*4882a593Smuzhiyun 	dprintk(KERN_WARNING "3w-xxxx: tw_decode_bits()\n");
260*4882a593Smuzhiyun 
261*4882a593Smuzhiyun 	if (print_host)
262*4882a593Smuzhiyun 		sprintf(host, " scsi%d:", tw_dev->host->host_no);
263*4882a593Smuzhiyun 	else
264*4882a593Smuzhiyun 		host[0] = '\0';
265*4882a593Smuzhiyun 
266*4882a593Smuzhiyun 	if (status_reg_value & TW_STATUS_PCI_PARITY_ERROR) {
267*4882a593Smuzhiyun 		printk(KERN_WARNING "3w-xxxx:%s PCI Parity Error: clearing.\n", host);
268*4882a593Smuzhiyun 		outl(TW_CONTROL_CLEAR_PARITY_ERROR, TW_CONTROL_REG_ADDR(tw_dev));
269*4882a593Smuzhiyun 	}
270*4882a593Smuzhiyun 
271*4882a593Smuzhiyun 	if (status_reg_value & TW_STATUS_PCI_ABORT) {
272*4882a593Smuzhiyun 		printk(KERN_WARNING "3w-xxxx:%s PCI Abort: clearing.\n", host);
273*4882a593Smuzhiyun 		outl(TW_CONTROL_CLEAR_PCI_ABORT, TW_CONTROL_REG_ADDR(tw_dev));
274*4882a593Smuzhiyun 		pci_write_config_word(tw_dev->tw_pci_dev, PCI_STATUS, TW_PCI_CLEAR_PCI_ABORT);
275*4882a593Smuzhiyun 	}
276*4882a593Smuzhiyun 
277*4882a593Smuzhiyun 	if (status_reg_value & TW_STATUS_QUEUE_ERROR) {
278*4882a593Smuzhiyun 		printk(KERN_WARNING "3w-xxxx:%s Controller Queue Error: clearing.\n", host);
279*4882a593Smuzhiyun 		outl(TW_CONTROL_CLEAR_QUEUE_ERROR, TW_CONTROL_REG_ADDR(tw_dev));
280*4882a593Smuzhiyun 	}
281*4882a593Smuzhiyun 
282*4882a593Smuzhiyun 	if (status_reg_value & TW_STATUS_SBUF_WRITE_ERROR) {
283*4882a593Smuzhiyun 		printk(KERN_WARNING "3w-xxxx:%s SBUF Write Error: clearing.\n", host);
284*4882a593Smuzhiyun 		outl(TW_CONTROL_CLEAR_SBUF_WRITE_ERROR, TW_CONTROL_REG_ADDR(tw_dev));
285*4882a593Smuzhiyun 	}
286*4882a593Smuzhiyun 
287*4882a593Smuzhiyun 	if (status_reg_value & TW_STATUS_MICROCONTROLLER_ERROR) {
288*4882a593Smuzhiyun 		if (tw_dev->reset_print == 0) {
289*4882a593Smuzhiyun 			printk(KERN_WARNING "3w-xxxx:%s Microcontroller Error: clearing.\n", host);
290*4882a593Smuzhiyun 			tw_dev->reset_print = 1;
291*4882a593Smuzhiyun 		}
292*4882a593Smuzhiyun 		return 1;
293*4882a593Smuzhiyun 	}
294*4882a593Smuzhiyun 
295*4882a593Smuzhiyun 	return 0;
296*4882a593Smuzhiyun } /* End tw_decode_bits() */
297*4882a593Smuzhiyun 
298*4882a593Smuzhiyun /* This function will poll the status register for a flag */
tw_poll_status(TW_Device_Extension * tw_dev,u32 flag,int seconds)299*4882a593Smuzhiyun static int tw_poll_status(TW_Device_Extension *tw_dev, u32 flag, int seconds)
300*4882a593Smuzhiyun {
301*4882a593Smuzhiyun 	u32 status_reg_value;
302*4882a593Smuzhiyun 	unsigned long before;
303*4882a593Smuzhiyun 	int retval = 1;
304*4882a593Smuzhiyun 
305*4882a593Smuzhiyun 	status_reg_value = inl(TW_STATUS_REG_ADDR(tw_dev));
306*4882a593Smuzhiyun 	before = jiffies;
307*4882a593Smuzhiyun 
308*4882a593Smuzhiyun 	if (tw_check_bits(status_reg_value))
309*4882a593Smuzhiyun 		tw_decode_bits(tw_dev, status_reg_value, 0);
310*4882a593Smuzhiyun 
311*4882a593Smuzhiyun 	while ((status_reg_value & flag) != flag) {
312*4882a593Smuzhiyun 		status_reg_value = inl(TW_STATUS_REG_ADDR(tw_dev));
313*4882a593Smuzhiyun 
314*4882a593Smuzhiyun 		if (tw_check_bits(status_reg_value))
315*4882a593Smuzhiyun 			tw_decode_bits(tw_dev, status_reg_value, 0);
316*4882a593Smuzhiyun 
317*4882a593Smuzhiyun 		if (time_after(jiffies, before + HZ * seconds))
318*4882a593Smuzhiyun 			goto out;
319*4882a593Smuzhiyun 
320*4882a593Smuzhiyun 		msleep(50);
321*4882a593Smuzhiyun 	}
322*4882a593Smuzhiyun 	retval = 0;
323*4882a593Smuzhiyun out:
324*4882a593Smuzhiyun 	return retval;
325*4882a593Smuzhiyun } /* End tw_poll_status() */
326*4882a593Smuzhiyun 
327*4882a593Smuzhiyun /* This function will poll the status register for disappearance of a flag */
tw_poll_status_gone(TW_Device_Extension * tw_dev,u32 flag,int seconds)328*4882a593Smuzhiyun static int tw_poll_status_gone(TW_Device_Extension *tw_dev, u32 flag, int seconds)
329*4882a593Smuzhiyun {
330*4882a593Smuzhiyun 	u32 status_reg_value;
331*4882a593Smuzhiyun 	unsigned long before;
332*4882a593Smuzhiyun 	int retval = 1;
333*4882a593Smuzhiyun 
334*4882a593Smuzhiyun 	status_reg_value = inl(TW_STATUS_REG_ADDR(tw_dev));
335*4882a593Smuzhiyun 	before = jiffies;
336*4882a593Smuzhiyun 
337*4882a593Smuzhiyun 	if (tw_check_bits(status_reg_value))
338*4882a593Smuzhiyun 		tw_decode_bits(tw_dev, status_reg_value, 0);
339*4882a593Smuzhiyun 
340*4882a593Smuzhiyun 	while ((status_reg_value & flag) != 0) {
341*4882a593Smuzhiyun 		status_reg_value = inl(TW_STATUS_REG_ADDR(tw_dev));
342*4882a593Smuzhiyun 
343*4882a593Smuzhiyun 		if (tw_check_bits(status_reg_value))
344*4882a593Smuzhiyun 			tw_decode_bits(tw_dev, status_reg_value, 0);
345*4882a593Smuzhiyun 
346*4882a593Smuzhiyun 		if (time_after(jiffies, before + HZ * seconds))
347*4882a593Smuzhiyun 			goto out;
348*4882a593Smuzhiyun 
349*4882a593Smuzhiyun 		msleep(50);
350*4882a593Smuzhiyun 	}
351*4882a593Smuzhiyun 	retval = 0;
352*4882a593Smuzhiyun out:
353*4882a593Smuzhiyun 	return retval;
354*4882a593Smuzhiyun } /* End tw_poll_status_gone() */
355*4882a593Smuzhiyun 
356*4882a593Smuzhiyun /* This function will attempt to post a command packet to the board */
tw_post_command_packet(TW_Device_Extension * tw_dev,int request_id)357*4882a593Smuzhiyun static int tw_post_command_packet(TW_Device_Extension *tw_dev, int request_id)
358*4882a593Smuzhiyun {
359*4882a593Smuzhiyun 	u32 status_reg_value;
360*4882a593Smuzhiyun 	unsigned long command_que_value;
361*4882a593Smuzhiyun 
362*4882a593Smuzhiyun 	dprintk(KERN_NOTICE "3w-xxxx: tw_post_command_packet()\n");
363*4882a593Smuzhiyun 	command_que_value = tw_dev->command_packet_physical_address[request_id];
364*4882a593Smuzhiyun 	status_reg_value = inl(TW_STATUS_REG_ADDR(tw_dev));
365*4882a593Smuzhiyun 
366*4882a593Smuzhiyun 	if (tw_check_bits(status_reg_value)) {
367*4882a593Smuzhiyun 		dprintk(KERN_WARNING "3w-xxxx: tw_post_command_packet(): Unexpected bits.\n");
368*4882a593Smuzhiyun 		tw_decode_bits(tw_dev, status_reg_value, 1);
369*4882a593Smuzhiyun 	}
370*4882a593Smuzhiyun 
371*4882a593Smuzhiyun 	if ((status_reg_value & TW_STATUS_COMMAND_QUEUE_FULL) == 0) {
372*4882a593Smuzhiyun 		/* We successfully posted the command packet */
373*4882a593Smuzhiyun 		outl(command_que_value, TW_COMMAND_QUEUE_REG_ADDR(tw_dev));
374*4882a593Smuzhiyun 		tw_dev->state[request_id] = TW_S_POSTED;
375*4882a593Smuzhiyun 		tw_dev->posted_request_count++;
376*4882a593Smuzhiyun 		if (tw_dev->posted_request_count > tw_dev->max_posted_request_count) {
377*4882a593Smuzhiyun 			tw_dev->max_posted_request_count = tw_dev->posted_request_count;
378*4882a593Smuzhiyun 		}
379*4882a593Smuzhiyun 	} else {
380*4882a593Smuzhiyun 		/* Couldn't post the command packet, so we do it in the isr */
381*4882a593Smuzhiyun 		if (tw_dev->state[request_id] != TW_S_PENDING) {
382*4882a593Smuzhiyun 			tw_dev->state[request_id] = TW_S_PENDING;
383*4882a593Smuzhiyun 			tw_dev->pending_request_count++;
384*4882a593Smuzhiyun 			if (tw_dev->pending_request_count > tw_dev->max_pending_request_count) {
385*4882a593Smuzhiyun 				tw_dev->max_pending_request_count = tw_dev->pending_request_count;
386*4882a593Smuzhiyun 			}
387*4882a593Smuzhiyun 			tw_dev->pending_queue[tw_dev->pending_tail] = request_id;
388*4882a593Smuzhiyun 			if (tw_dev->pending_tail == TW_Q_LENGTH-1) {
389*4882a593Smuzhiyun 				tw_dev->pending_tail = TW_Q_START;
390*4882a593Smuzhiyun 			} else {
391*4882a593Smuzhiyun 				tw_dev->pending_tail = tw_dev->pending_tail + 1;
392*4882a593Smuzhiyun 			}
393*4882a593Smuzhiyun 		}
394*4882a593Smuzhiyun 		TW_UNMASK_COMMAND_INTERRUPT(tw_dev);
395*4882a593Smuzhiyun 		return 1;
396*4882a593Smuzhiyun 	}
397*4882a593Smuzhiyun 	return 0;
398*4882a593Smuzhiyun } /* End tw_post_command_packet() */
399*4882a593Smuzhiyun 
400*4882a593Smuzhiyun /* This function will return valid sense buffer information for failed cmds */
tw_decode_sense(TW_Device_Extension * tw_dev,int request_id,int fill_sense)401*4882a593Smuzhiyun static int tw_decode_sense(TW_Device_Extension *tw_dev, int request_id, int fill_sense)
402*4882a593Smuzhiyun {
403*4882a593Smuzhiyun 	int i;
404*4882a593Smuzhiyun 	TW_Command *command;
405*4882a593Smuzhiyun 
406*4882a593Smuzhiyun         dprintk(KERN_WARNING "3w-xxxx: tw_decode_sense()\n");
407*4882a593Smuzhiyun 	command = (TW_Command *)tw_dev->command_packet_virtual_address[request_id];
408*4882a593Smuzhiyun 
409*4882a593Smuzhiyun 	printk(KERN_WARNING "3w-xxxx: scsi%d: Command failed: status = 0x%x, flags = 0x%x, unit #%d.\n", tw_dev->host->host_no, command->status, command->flags, TW_UNIT_OUT(command->unit__hostid));
410*4882a593Smuzhiyun 
411*4882a593Smuzhiyun 	/* Attempt to return intelligent sense information */
412*4882a593Smuzhiyun 	if (fill_sense) {
413*4882a593Smuzhiyun 		if ((command->status == 0xc7) || (command->status == 0xcb)) {
414*4882a593Smuzhiyun 			for (i = 0; i < ARRAY_SIZE(tw_sense_table); i++) {
415*4882a593Smuzhiyun 				if (command->flags == tw_sense_table[i][0]) {
416*4882a593Smuzhiyun 
417*4882a593Smuzhiyun 					/* Valid bit and 'current errors' */
418*4882a593Smuzhiyun 					tw_dev->srb[request_id]->sense_buffer[0] = (0x1 << 7 | 0x70);
419*4882a593Smuzhiyun 
420*4882a593Smuzhiyun 					/* Sense key */
421*4882a593Smuzhiyun 					tw_dev->srb[request_id]->sense_buffer[2] = tw_sense_table[i][1];
422*4882a593Smuzhiyun 
423*4882a593Smuzhiyun 					/* Additional sense length */
424*4882a593Smuzhiyun 					tw_dev->srb[request_id]->sense_buffer[7] = 0xa; /* 10 bytes */
425*4882a593Smuzhiyun 
426*4882a593Smuzhiyun 					/* Additional sense code */
427*4882a593Smuzhiyun 					tw_dev->srb[request_id]->sense_buffer[12] = tw_sense_table[i][2];
428*4882a593Smuzhiyun 
429*4882a593Smuzhiyun 					/* Additional sense code qualifier */
430*4882a593Smuzhiyun 					tw_dev->srb[request_id]->sense_buffer[13] = tw_sense_table[i][3];
431*4882a593Smuzhiyun 
432*4882a593Smuzhiyun 					tw_dev->srb[request_id]->result = (DID_OK << 16) | (CHECK_CONDITION << 1);
433*4882a593Smuzhiyun 					return TW_ISR_DONT_RESULT; /* Special case for isr to not over-write result */
434*4882a593Smuzhiyun 				}
435*4882a593Smuzhiyun 			}
436*4882a593Smuzhiyun 		}
437*4882a593Smuzhiyun 
438*4882a593Smuzhiyun 		/* If no table match, error so we get a reset */
439*4882a593Smuzhiyun 		return 1;
440*4882a593Smuzhiyun 	}
441*4882a593Smuzhiyun 
442*4882a593Smuzhiyun 	return 0;
443*4882a593Smuzhiyun } /* End tw_decode_sense() */
444*4882a593Smuzhiyun 
445*4882a593Smuzhiyun /* This function will report controller error status */
tw_check_errors(TW_Device_Extension * tw_dev)446*4882a593Smuzhiyun static int tw_check_errors(TW_Device_Extension *tw_dev)
447*4882a593Smuzhiyun {
448*4882a593Smuzhiyun 	u32 status_reg_value;
449*4882a593Smuzhiyun 
450*4882a593Smuzhiyun 	status_reg_value = inl(TW_STATUS_REG_ADDR(tw_dev));
451*4882a593Smuzhiyun 
452*4882a593Smuzhiyun 	if (TW_STATUS_ERRORS(status_reg_value) || tw_check_bits(status_reg_value)) {
453*4882a593Smuzhiyun 		tw_decode_bits(tw_dev, status_reg_value, 0);
454*4882a593Smuzhiyun 		return 1;
455*4882a593Smuzhiyun 	}
456*4882a593Smuzhiyun 
457*4882a593Smuzhiyun 	return 0;
458*4882a593Smuzhiyun } /* End tw_check_errors() */
459*4882a593Smuzhiyun 
460*4882a593Smuzhiyun /* This function will empty the response que */
tw_empty_response_que(TW_Device_Extension * tw_dev)461*4882a593Smuzhiyun static void tw_empty_response_que(TW_Device_Extension *tw_dev)
462*4882a593Smuzhiyun {
463*4882a593Smuzhiyun 	u32 status_reg_value, response_que_value;
464*4882a593Smuzhiyun 
465*4882a593Smuzhiyun 	status_reg_value = inl(TW_STATUS_REG_ADDR(tw_dev));
466*4882a593Smuzhiyun 
467*4882a593Smuzhiyun 	while ((status_reg_value & TW_STATUS_RESPONSE_QUEUE_EMPTY) == 0) {
468*4882a593Smuzhiyun 		response_que_value = inl(TW_RESPONSE_QUEUE_REG_ADDR(tw_dev));
469*4882a593Smuzhiyun 		status_reg_value = inl(TW_STATUS_REG_ADDR(tw_dev));
470*4882a593Smuzhiyun 	}
471*4882a593Smuzhiyun } /* End tw_empty_response_que() */
472*4882a593Smuzhiyun 
473*4882a593Smuzhiyun /* This function will free a request_id */
tw_state_request_finish(TW_Device_Extension * tw_dev,int request_id)474*4882a593Smuzhiyun static void tw_state_request_finish(TW_Device_Extension *tw_dev, int request_id)
475*4882a593Smuzhiyun {
476*4882a593Smuzhiyun 	tw_dev->free_queue[tw_dev->free_tail] = request_id;
477*4882a593Smuzhiyun 	tw_dev->state[request_id] = TW_S_FINISHED;
478*4882a593Smuzhiyun 	tw_dev->free_tail = (tw_dev->free_tail + 1) % TW_Q_LENGTH;
479*4882a593Smuzhiyun } /* End tw_state_request_finish() */
480*4882a593Smuzhiyun 
481*4882a593Smuzhiyun /* This function will assign an available request_id */
tw_state_request_start(TW_Device_Extension * tw_dev,int * request_id)482*4882a593Smuzhiyun static void tw_state_request_start(TW_Device_Extension *tw_dev, int *request_id)
483*4882a593Smuzhiyun {
484*4882a593Smuzhiyun 	*request_id = tw_dev->free_queue[tw_dev->free_head];
485*4882a593Smuzhiyun 	tw_dev->free_head = (tw_dev->free_head + 1) % TW_Q_LENGTH;
486*4882a593Smuzhiyun 	tw_dev->state[*request_id] = TW_S_STARTED;
487*4882a593Smuzhiyun } /* End tw_state_request_start() */
488*4882a593Smuzhiyun 
489*4882a593Smuzhiyun /* Show some statistics about the card */
tw_show_stats(struct device * dev,struct device_attribute * attr,char * buf)490*4882a593Smuzhiyun static ssize_t tw_show_stats(struct device *dev, struct device_attribute *attr,
491*4882a593Smuzhiyun 			     char *buf)
492*4882a593Smuzhiyun {
493*4882a593Smuzhiyun 	struct Scsi_Host *host = class_to_shost(dev);
494*4882a593Smuzhiyun 	TW_Device_Extension *tw_dev = (TW_Device_Extension *)host->hostdata;
495*4882a593Smuzhiyun 	unsigned long flags = 0;
496*4882a593Smuzhiyun 	ssize_t len;
497*4882a593Smuzhiyun 
498*4882a593Smuzhiyun 	spin_lock_irqsave(tw_dev->host->host_lock, flags);
499*4882a593Smuzhiyun 	len = snprintf(buf, PAGE_SIZE, "3w-xxxx Driver version: %s\n"
500*4882a593Smuzhiyun 		       "Current commands posted:   %4d\n"
501*4882a593Smuzhiyun 		       "Max commands posted:       %4d\n"
502*4882a593Smuzhiyun 		       "Current pending commands:  %4d\n"
503*4882a593Smuzhiyun 		       "Max pending commands:      %4d\n"
504*4882a593Smuzhiyun 		       "Last sgl length:           %4d\n"
505*4882a593Smuzhiyun 		       "Max sgl length:            %4d\n"
506*4882a593Smuzhiyun 		       "Last sector count:         %4d\n"
507*4882a593Smuzhiyun 		       "Max sector count:          %4d\n"
508*4882a593Smuzhiyun 		       "SCSI Host Resets:          %4d\n"
509*4882a593Smuzhiyun 		       "AEN's:                     %4d\n",
510*4882a593Smuzhiyun 		       TW_DRIVER_VERSION,
511*4882a593Smuzhiyun 		       tw_dev->posted_request_count,
512*4882a593Smuzhiyun 		       tw_dev->max_posted_request_count,
513*4882a593Smuzhiyun 		       tw_dev->pending_request_count,
514*4882a593Smuzhiyun 		       tw_dev->max_pending_request_count,
515*4882a593Smuzhiyun 		       tw_dev->sgl_entries,
516*4882a593Smuzhiyun 		       tw_dev->max_sgl_entries,
517*4882a593Smuzhiyun 		       tw_dev->sector_count,
518*4882a593Smuzhiyun 		       tw_dev->max_sector_count,
519*4882a593Smuzhiyun 		       tw_dev->num_resets,
520*4882a593Smuzhiyun 		       tw_dev->aen_count);
521*4882a593Smuzhiyun 	spin_unlock_irqrestore(tw_dev->host->host_lock, flags);
522*4882a593Smuzhiyun 	return len;
523*4882a593Smuzhiyun } /* End tw_show_stats() */
524*4882a593Smuzhiyun 
525*4882a593Smuzhiyun /* Create sysfs 'stats' entry */
526*4882a593Smuzhiyun static struct device_attribute tw_host_stats_attr = {
527*4882a593Smuzhiyun 	.attr = {
528*4882a593Smuzhiyun 		.name = 	"stats",
529*4882a593Smuzhiyun 		.mode =		S_IRUGO,
530*4882a593Smuzhiyun 	},
531*4882a593Smuzhiyun 	.show = tw_show_stats
532*4882a593Smuzhiyun };
533*4882a593Smuzhiyun 
534*4882a593Smuzhiyun /* Host attributes initializer */
535*4882a593Smuzhiyun static struct device_attribute *tw_host_attrs[] = {
536*4882a593Smuzhiyun 	&tw_host_stats_attr,
537*4882a593Smuzhiyun 	NULL,
538*4882a593Smuzhiyun };
539*4882a593Smuzhiyun 
540*4882a593Smuzhiyun /* This function will read the aen queue from the isr */
tw_aen_read_queue(TW_Device_Extension * tw_dev,int request_id)541*4882a593Smuzhiyun static int tw_aen_read_queue(TW_Device_Extension *tw_dev, int request_id)
542*4882a593Smuzhiyun {
543*4882a593Smuzhiyun 	TW_Command *command_packet;
544*4882a593Smuzhiyun 	TW_Param *param;
545*4882a593Smuzhiyun 	unsigned long command_que_value;
546*4882a593Smuzhiyun 	u32 status_reg_value;
547*4882a593Smuzhiyun 	unsigned long param_value = 0;
548*4882a593Smuzhiyun 
549*4882a593Smuzhiyun 	dprintk(KERN_NOTICE "3w-xxxx: tw_aen_read_queue()\n");
550*4882a593Smuzhiyun 
551*4882a593Smuzhiyun 	status_reg_value = inl(TW_STATUS_REG_ADDR(tw_dev));
552*4882a593Smuzhiyun 	if (tw_check_bits(status_reg_value)) {
553*4882a593Smuzhiyun 		dprintk(KERN_WARNING "3w-xxxx: tw_aen_read_queue(): Unexpected bits.\n");
554*4882a593Smuzhiyun 		tw_decode_bits(tw_dev, status_reg_value, 1);
555*4882a593Smuzhiyun 		return 1;
556*4882a593Smuzhiyun 	}
557*4882a593Smuzhiyun 	if (tw_dev->command_packet_virtual_address[request_id] == NULL) {
558*4882a593Smuzhiyun 		printk(KERN_WARNING "3w-xxxx: tw_aen_read_queue(): Bad command packet virtual address.\n");
559*4882a593Smuzhiyun 		return 1;
560*4882a593Smuzhiyun 	}
561*4882a593Smuzhiyun 	command_packet = (TW_Command *)tw_dev->command_packet_virtual_address[request_id];
562*4882a593Smuzhiyun 	memset(command_packet, 0, sizeof(TW_Sector));
563*4882a593Smuzhiyun 	command_packet->opcode__sgloffset = TW_OPSGL_IN(2, TW_OP_GET_PARAM);
564*4882a593Smuzhiyun 	command_packet->size = 4;
565*4882a593Smuzhiyun 	command_packet->request_id = request_id;
566*4882a593Smuzhiyun 	command_packet->status = 0;
567*4882a593Smuzhiyun 	command_packet->flags = 0;
568*4882a593Smuzhiyun 	command_packet->byte6.parameter_count = 1;
569*4882a593Smuzhiyun 	command_que_value = tw_dev->command_packet_physical_address[request_id];
570*4882a593Smuzhiyun 	if (command_que_value == 0) {
571*4882a593Smuzhiyun 		printk(KERN_WARNING "3w-xxxx: tw_aen_read_queue(): Bad command packet physical address.\n");
572*4882a593Smuzhiyun 		return 1;
573*4882a593Smuzhiyun 	}
574*4882a593Smuzhiyun 	/* Now setup the param */
575*4882a593Smuzhiyun 	if (tw_dev->alignment_virtual_address[request_id] == NULL) {
576*4882a593Smuzhiyun 		printk(KERN_WARNING "3w-xxxx: tw_aen_read_queue(): Bad alignment virtual address.\n");
577*4882a593Smuzhiyun 		return 1;
578*4882a593Smuzhiyun 	}
579*4882a593Smuzhiyun 	param = (TW_Param *)tw_dev->alignment_virtual_address[request_id];
580*4882a593Smuzhiyun 	memset(param, 0, sizeof(TW_Sector));
581*4882a593Smuzhiyun 	param->table_id = 0x401; /* AEN table */
582*4882a593Smuzhiyun 	param->parameter_id = 2; /* Unit code */
583*4882a593Smuzhiyun 	param->parameter_size_bytes = 2;
584*4882a593Smuzhiyun 	param_value = tw_dev->alignment_physical_address[request_id];
585*4882a593Smuzhiyun 	if (param_value == 0) {
586*4882a593Smuzhiyun 		printk(KERN_WARNING "3w-xxxx: tw_aen_read_queue(): Bad alignment physical address.\n");
587*4882a593Smuzhiyun 		return 1;
588*4882a593Smuzhiyun 	}
589*4882a593Smuzhiyun 	command_packet->byte8.param.sgl[0].address = param_value;
590*4882a593Smuzhiyun 	command_packet->byte8.param.sgl[0].length = sizeof(TW_Sector);
591*4882a593Smuzhiyun 
592*4882a593Smuzhiyun 	/* Now post the command packet */
593*4882a593Smuzhiyun 	if ((status_reg_value & TW_STATUS_COMMAND_QUEUE_FULL) == 0) {
594*4882a593Smuzhiyun 		dprintk(KERN_WARNING "3w-xxxx: tw_aen_read_queue(): Post succeeded.\n");
595*4882a593Smuzhiyun 		tw_dev->srb[request_id] = NULL; /* Flag internal command */
596*4882a593Smuzhiyun 		tw_dev->state[request_id] = TW_S_POSTED;
597*4882a593Smuzhiyun 		outl(command_que_value, TW_COMMAND_QUEUE_REG_ADDR(tw_dev));
598*4882a593Smuzhiyun 	} else {
599*4882a593Smuzhiyun 		printk(KERN_WARNING "3w-xxxx: tw_aen_read_queue(): Post failed, will retry.\n");
600*4882a593Smuzhiyun 		return 1;
601*4882a593Smuzhiyun 	}
602*4882a593Smuzhiyun 
603*4882a593Smuzhiyun 	return 0;
604*4882a593Smuzhiyun } /* End tw_aen_read_queue() */
605*4882a593Smuzhiyun 
606*4882a593Smuzhiyun /* This function will complete an aen request from the isr */
tw_aen_complete(TW_Device_Extension * tw_dev,int request_id)607*4882a593Smuzhiyun static int tw_aen_complete(TW_Device_Extension *tw_dev, int request_id)
608*4882a593Smuzhiyun {
609*4882a593Smuzhiyun 	TW_Param *param;
610*4882a593Smuzhiyun 	unsigned short aen;
611*4882a593Smuzhiyun 	int error = 0, table_max = 0;
612*4882a593Smuzhiyun 
613*4882a593Smuzhiyun 	dprintk(KERN_WARNING "3w-xxxx: tw_aen_complete()\n");
614*4882a593Smuzhiyun 	if (tw_dev->alignment_virtual_address[request_id] == NULL) {
615*4882a593Smuzhiyun 		printk(KERN_WARNING "3w-xxxx: tw_aen_complete(): Bad alignment virtual address.\n");
616*4882a593Smuzhiyun 		return 1;
617*4882a593Smuzhiyun 	}
618*4882a593Smuzhiyun 	param = (TW_Param *)tw_dev->alignment_virtual_address[request_id];
619*4882a593Smuzhiyun 	aen = *(unsigned short *)(param->data);
620*4882a593Smuzhiyun 	dprintk(KERN_NOTICE "3w-xxxx: tw_aen_complete(): Queue'd code 0x%x\n", aen);
621*4882a593Smuzhiyun 
622*4882a593Smuzhiyun 	/* Print some useful info when certain aen codes come out */
623*4882a593Smuzhiyun 	if (aen == 0x0ff) {
624*4882a593Smuzhiyun 		printk(KERN_WARNING "3w-xxxx: scsi%d: AEN: INFO: AEN queue overflow.\n", tw_dev->host->host_no);
625*4882a593Smuzhiyun 	} else {
626*4882a593Smuzhiyun 		table_max = ARRAY_SIZE(tw_aen_string);
627*4882a593Smuzhiyun 		if ((aen & 0x0ff) < table_max) {
628*4882a593Smuzhiyun 			if ((tw_aen_string[aen & 0xff][strlen(tw_aen_string[aen & 0xff])-1]) == '#') {
629*4882a593Smuzhiyun 				printk(KERN_WARNING "3w-xxxx: scsi%d: AEN: %s%d.\n", tw_dev->host->host_no, tw_aen_string[aen & 0xff], aen >> 8);
630*4882a593Smuzhiyun 			} else {
631*4882a593Smuzhiyun 				if (aen != 0x0)
632*4882a593Smuzhiyun 					printk(KERN_WARNING "3w-xxxx: scsi%d: AEN: %s.\n", tw_dev->host->host_no, tw_aen_string[aen & 0xff]);
633*4882a593Smuzhiyun 			}
634*4882a593Smuzhiyun 		} else {
635*4882a593Smuzhiyun 			printk(KERN_WARNING "3w-xxxx: scsi%d: Received AEN %d.\n", tw_dev->host->host_no, aen);
636*4882a593Smuzhiyun 		}
637*4882a593Smuzhiyun 	}
638*4882a593Smuzhiyun 	if (aen != TW_AEN_QUEUE_EMPTY) {
639*4882a593Smuzhiyun 		tw_dev->aen_count++;
640*4882a593Smuzhiyun 
641*4882a593Smuzhiyun 		/* Now queue the code */
642*4882a593Smuzhiyun 		tw_dev->aen_queue[tw_dev->aen_tail] = aen;
643*4882a593Smuzhiyun 		if (tw_dev->aen_tail == TW_Q_LENGTH - 1) {
644*4882a593Smuzhiyun 			tw_dev->aen_tail = TW_Q_START;
645*4882a593Smuzhiyun 		} else {
646*4882a593Smuzhiyun 			tw_dev->aen_tail = tw_dev->aen_tail + 1;
647*4882a593Smuzhiyun 		}
648*4882a593Smuzhiyun 		if (tw_dev->aen_head == tw_dev->aen_tail) {
649*4882a593Smuzhiyun 			if (tw_dev->aen_head == TW_Q_LENGTH - 1) {
650*4882a593Smuzhiyun 				tw_dev->aen_head = TW_Q_START;
651*4882a593Smuzhiyun 			} else {
652*4882a593Smuzhiyun 				tw_dev->aen_head = tw_dev->aen_head + 1;
653*4882a593Smuzhiyun 			}
654*4882a593Smuzhiyun 		}
655*4882a593Smuzhiyun 
656*4882a593Smuzhiyun 		error = tw_aen_read_queue(tw_dev, request_id);
657*4882a593Smuzhiyun 		if (error) {
658*4882a593Smuzhiyun 			printk(KERN_WARNING "3w-xxxx: scsi%d: Error completing AEN.\n", tw_dev->host->host_no);
659*4882a593Smuzhiyun 			tw_dev->state[request_id] = TW_S_COMPLETED;
660*4882a593Smuzhiyun 			tw_state_request_finish(tw_dev, request_id);
661*4882a593Smuzhiyun 		}
662*4882a593Smuzhiyun 	} else {
663*4882a593Smuzhiyun 		tw_dev->state[request_id] = TW_S_COMPLETED;
664*4882a593Smuzhiyun 		tw_state_request_finish(tw_dev, request_id);
665*4882a593Smuzhiyun 	}
666*4882a593Smuzhiyun 
667*4882a593Smuzhiyun 	return 0;
668*4882a593Smuzhiyun } /* End tw_aen_complete() */
669*4882a593Smuzhiyun 
670*4882a593Smuzhiyun /* This function will drain the aen queue after a soft reset */
tw_aen_drain_queue(TW_Device_Extension * tw_dev)671*4882a593Smuzhiyun static int tw_aen_drain_queue(TW_Device_Extension *tw_dev)
672*4882a593Smuzhiyun {
673*4882a593Smuzhiyun 	TW_Command *command_packet;
674*4882a593Smuzhiyun 	TW_Param *param;
675*4882a593Smuzhiyun 	int request_id = 0;
676*4882a593Smuzhiyun 	unsigned long command_que_value;
677*4882a593Smuzhiyun 	unsigned long param_value;
678*4882a593Smuzhiyun 	TW_Response_Queue response_queue;
679*4882a593Smuzhiyun 	unsigned short aen;
680*4882a593Smuzhiyun 	unsigned short aen_code;
681*4882a593Smuzhiyun 	int finished = 0;
682*4882a593Smuzhiyun 	int first_reset = 0;
683*4882a593Smuzhiyun 	int queue = 0;
684*4882a593Smuzhiyun 	int found = 0, table_max = 0;
685*4882a593Smuzhiyun 
686*4882a593Smuzhiyun 	dprintk(KERN_NOTICE "3w-xxxx: tw_aen_drain_queue()\n");
687*4882a593Smuzhiyun 
688*4882a593Smuzhiyun 	if (tw_poll_status(tw_dev, TW_STATUS_ATTENTION_INTERRUPT | TW_STATUS_MICROCONTROLLER_READY, 30)) {
689*4882a593Smuzhiyun 		dprintk(KERN_WARNING "3w-xxxx: tw_aen_drain_queue(): No attention interrupt for card %d.\n", tw_device_extension_count);
690*4882a593Smuzhiyun 		return 1;
691*4882a593Smuzhiyun 	}
692*4882a593Smuzhiyun 	TW_CLEAR_ATTENTION_INTERRUPT(tw_dev);
693*4882a593Smuzhiyun 
694*4882a593Smuzhiyun 	/* Empty response queue */
695*4882a593Smuzhiyun 	tw_empty_response_que(tw_dev);
696*4882a593Smuzhiyun 
697*4882a593Smuzhiyun 	/* Initialize command packet */
698*4882a593Smuzhiyun 	if (tw_dev->command_packet_virtual_address[request_id] == NULL) {
699*4882a593Smuzhiyun 		printk(KERN_WARNING "3w-xxxx: tw_aen_drain_queue(): Bad command packet virtual address.\n");
700*4882a593Smuzhiyun 		return 1;
701*4882a593Smuzhiyun 	}
702*4882a593Smuzhiyun 	command_packet = (TW_Command *)tw_dev->command_packet_virtual_address[request_id];
703*4882a593Smuzhiyun 	memset(command_packet, 0, sizeof(TW_Sector));
704*4882a593Smuzhiyun 	command_packet->opcode__sgloffset = TW_OPSGL_IN(2, TW_OP_GET_PARAM);
705*4882a593Smuzhiyun 	command_packet->size = 4;
706*4882a593Smuzhiyun 	command_packet->request_id = request_id;
707*4882a593Smuzhiyun 	command_packet->status = 0;
708*4882a593Smuzhiyun 	command_packet->flags = 0;
709*4882a593Smuzhiyun 	command_packet->byte6.parameter_count = 1;
710*4882a593Smuzhiyun 	command_que_value = tw_dev->command_packet_physical_address[request_id];
711*4882a593Smuzhiyun 	if (command_que_value == 0) {
712*4882a593Smuzhiyun 		printk(KERN_WARNING "3w-xxxx: tw_aen_drain_queue(): Bad command packet physical address.\n");
713*4882a593Smuzhiyun 		return 1;
714*4882a593Smuzhiyun 	}
715*4882a593Smuzhiyun 
716*4882a593Smuzhiyun 	/* Now setup the param */
717*4882a593Smuzhiyun 	if (tw_dev->alignment_virtual_address[request_id] == NULL) {
718*4882a593Smuzhiyun 		printk(KERN_WARNING "3w-xxxx: tw_aen_drain_queue(): Bad alignment virtual address.\n");
719*4882a593Smuzhiyun 		return 1;
720*4882a593Smuzhiyun 	}
721*4882a593Smuzhiyun 	param = (TW_Param *)tw_dev->alignment_virtual_address[request_id];
722*4882a593Smuzhiyun 	memset(param, 0, sizeof(TW_Sector));
723*4882a593Smuzhiyun 	param->table_id = 0x401; /* AEN table */
724*4882a593Smuzhiyun 	param->parameter_id = 2; /* Unit code */
725*4882a593Smuzhiyun 	param->parameter_size_bytes = 2;
726*4882a593Smuzhiyun 	param_value = tw_dev->alignment_physical_address[request_id];
727*4882a593Smuzhiyun 	if (param_value == 0) {
728*4882a593Smuzhiyun 		printk(KERN_WARNING "3w-xxxx: tw_aen_drain_queue(): Bad alignment physical address.\n");
729*4882a593Smuzhiyun 		return 1;
730*4882a593Smuzhiyun 	}
731*4882a593Smuzhiyun 	command_packet->byte8.param.sgl[0].address = param_value;
732*4882a593Smuzhiyun 	command_packet->byte8.param.sgl[0].length = sizeof(TW_Sector);
733*4882a593Smuzhiyun 
734*4882a593Smuzhiyun 	/* Now drain the controller's aen queue */
735*4882a593Smuzhiyun 	do {
736*4882a593Smuzhiyun 		/* Post command packet */
737*4882a593Smuzhiyun 		outl(command_que_value, TW_COMMAND_QUEUE_REG_ADDR(tw_dev));
738*4882a593Smuzhiyun 
739*4882a593Smuzhiyun 		/* Now poll for completion */
740*4882a593Smuzhiyun 		if (tw_poll_status_gone(tw_dev, TW_STATUS_RESPONSE_QUEUE_EMPTY, 30) == 0) {
741*4882a593Smuzhiyun 			response_queue.value = inl(TW_RESPONSE_QUEUE_REG_ADDR(tw_dev));
742*4882a593Smuzhiyun 			request_id = TW_RESID_OUT(response_queue.response_id);
743*4882a593Smuzhiyun 
744*4882a593Smuzhiyun 			if (request_id != 0) {
745*4882a593Smuzhiyun 				/* Unexpected request id */
746*4882a593Smuzhiyun 				printk(KERN_WARNING "3w-xxxx: tw_aen_drain_queue(): Unexpected request id.\n");
747*4882a593Smuzhiyun 				return 1;
748*4882a593Smuzhiyun 			}
749*4882a593Smuzhiyun 
750*4882a593Smuzhiyun 			if (command_packet->status != 0) {
751*4882a593Smuzhiyun 				if (command_packet->flags != TW_AEN_TABLE_UNDEFINED) {
752*4882a593Smuzhiyun 					/* Bad response */
753*4882a593Smuzhiyun 					tw_decode_sense(tw_dev, request_id, 0);
754*4882a593Smuzhiyun 					return 1;
755*4882a593Smuzhiyun 				} else {
756*4882a593Smuzhiyun 					/* We know this is a 3w-1x00, and doesn't support aen's */
757*4882a593Smuzhiyun 					return 0;
758*4882a593Smuzhiyun 				}
759*4882a593Smuzhiyun 			}
760*4882a593Smuzhiyun 
761*4882a593Smuzhiyun 			/* Now check the aen */
762*4882a593Smuzhiyun 			aen = *(unsigned short *)(param->data);
763*4882a593Smuzhiyun 			aen_code = (aen & 0x0ff);
764*4882a593Smuzhiyun 			queue = 0;
765*4882a593Smuzhiyun 			switch (aen_code) {
766*4882a593Smuzhiyun 				case TW_AEN_QUEUE_EMPTY:
767*4882a593Smuzhiyun 					dprintk(KERN_WARNING "3w-xxxx: AEN: %s.\n", tw_aen_string[aen & 0xff]);
768*4882a593Smuzhiyun 					if (first_reset != 1) {
769*4882a593Smuzhiyun 						return 1;
770*4882a593Smuzhiyun 					} else {
771*4882a593Smuzhiyun 						finished = 1;
772*4882a593Smuzhiyun 					}
773*4882a593Smuzhiyun 					break;
774*4882a593Smuzhiyun 				case TW_AEN_SOFT_RESET:
775*4882a593Smuzhiyun 					if (first_reset == 0) {
776*4882a593Smuzhiyun 						first_reset = 1;
777*4882a593Smuzhiyun 					} else {
778*4882a593Smuzhiyun 						printk(KERN_WARNING "3w-xxxx: AEN: %s.\n", tw_aen_string[aen & 0xff]);
779*4882a593Smuzhiyun 						tw_dev->aen_count++;
780*4882a593Smuzhiyun 						queue = 1;
781*4882a593Smuzhiyun 					}
782*4882a593Smuzhiyun 					break;
783*4882a593Smuzhiyun 				default:
784*4882a593Smuzhiyun 					if (aen == 0x0ff) {
785*4882a593Smuzhiyun 						printk(KERN_WARNING "3w-xxxx: AEN: INFO: AEN queue overflow.\n");
786*4882a593Smuzhiyun 					} else {
787*4882a593Smuzhiyun 						table_max = ARRAY_SIZE(tw_aen_string);
788*4882a593Smuzhiyun 						if ((aen & 0x0ff) < table_max) {
789*4882a593Smuzhiyun 							if ((tw_aen_string[aen & 0xff][strlen(tw_aen_string[aen & 0xff])-1]) == '#') {
790*4882a593Smuzhiyun 								printk(KERN_WARNING "3w-xxxx: AEN: %s%d.\n", tw_aen_string[aen & 0xff], aen >> 8);
791*4882a593Smuzhiyun 							} else {
792*4882a593Smuzhiyun 								printk(KERN_WARNING "3w-xxxx: AEN: %s.\n", tw_aen_string[aen & 0xff]);
793*4882a593Smuzhiyun 							}
794*4882a593Smuzhiyun 						} else
795*4882a593Smuzhiyun 							printk(KERN_WARNING "3w-xxxx: Received AEN %d.\n", aen);
796*4882a593Smuzhiyun 					}
797*4882a593Smuzhiyun 					tw_dev->aen_count++;
798*4882a593Smuzhiyun 					queue = 1;
799*4882a593Smuzhiyun 			}
800*4882a593Smuzhiyun 
801*4882a593Smuzhiyun 			/* Now put the aen on the aen_queue */
802*4882a593Smuzhiyun 			if (queue == 1) {
803*4882a593Smuzhiyun 				tw_dev->aen_queue[tw_dev->aen_tail] = aen;
804*4882a593Smuzhiyun 				if (tw_dev->aen_tail == TW_Q_LENGTH - 1) {
805*4882a593Smuzhiyun 					tw_dev->aen_tail = TW_Q_START;
806*4882a593Smuzhiyun 				} else {
807*4882a593Smuzhiyun 					tw_dev->aen_tail = tw_dev->aen_tail + 1;
808*4882a593Smuzhiyun 				}
809*4882a593Smuzhiyun 				if (tw_dev->aen_head == tw_dev->aen_tail) {
810*4882a593Smuzhiyun 					if (tw_dev->aen_head == TW_Q_LENGTH - 1) {
811*4882a593Smuzhiyun 						tw_dev->aen_head = TW_Q_START;
812*4882a593Smuzhiyun 					} else {
813*4882a593Smuzhiyun 						tw_dev->aen_head = tw_dev->aen_head + 1;
814*4882a593Smuzhiyun 					}
815*4882a593Smuzhiyun 				}
816*4882a593Smuzhiyun 			}
817*4882a593Smuzhiyun 			found = 1;
818*4882a593Smuzhiyun 		}
819*4882a593Smuzhiyun 		if (found == 0) {
820*4882a593Smuzhiyun 			printk(KERN_WARNING "3w-xxxx: tw_aen_drain_queue(): Response never received.\n");
821*4882a593Smuzhiyun 			return 1;
822*4882a593Smuzhiyun 		}
823*4882a593Smuzhiyun 	} while (finished == 0);
824*4882a593Smuzhiyun 
825*4882a593Smuzhiyun 	return 0;
826*4882a593Smuzhiyun } /* End tw_aen_drain_queue() */
827*4882a593Smuzhiyun 
828*4882a593Smuzhiyun /* This function will allocate memory */
tw_allocate_memory(TW_Device_Extension * tw_dev,int size,int which)829*4882a593Smuzhiyun static int tw_allocate_memory(TW_Device_Extension *tw_dev, int size, int which)
830*4882a593Smuzhiyun {
831*4882a593Smuzhiyun 	int i;
832*4882a593Smuzhiyun 	dma_addr_t dma_handle;
833*4882a593Smuzhiyun 	unsigned long *cpu_addr = NULL;
834*4882a593Smuzhiyun 
835*4882a593Smuzhiyun 	dprintk(KERN_NOTICE "3w-xxxx: tw_allocate_memory()\n");
836*4882a593Smuzhiyun 
837*4882a593Smuzhiyun 	cpu_addr = dma_alloc_coherent(&tw_dev->tw_pci_dev->dev,
838*4882a593Smuzhiyun 			size * TW_Q_LENGTH, &dma_handle, GFP_KERNEL);
839*4882a593Smuzhiyun 	if (cpu_addr == NULL) {
840*4882a593Smuzhiyun 		printk(KERN_WARNING "3w-xxxx: dma_alloc_coherent() failed.\n");
841*4882a593Smuzhiyun 		return 1;
842*4882a593Smuzhiyun 	}
843*4882a593Smuzhiyun 
844*4882a593Smuzhiyun 	if ((unsigned long)cpu_addr % (tw_dev->tw_pci_dev->device == TW_DEVICE_ID ? TW_ALIGNMENT_6000 : TW_ALIGNMENT_7000)) {
845*4882a593Smuzhiyun 		printk(KERN_WARNING "3w-xxxx: Couldn't allocate correctly aligned memory.\n");
846*4882a593Smuzhiyun 		dma_free_coherent(&tw_dev->tw_pci_dev->dev, size * TW_Q_LENGTH,
847*4882a593Smuzhiyun 				cpu_addr, dma_handle);
848*4882a593Smuzhiyun 		return 1;
849*4882a593Smuzhiyun 	}
850*4882a593Smuzhiyun 
851*4882a593Smuzhiyun 	memset(cpu_addr, 0, size*TW_Q_LENGTH);
852*4882a593Smuzhiyun 
853*4882a593Smuzhiyun 	for (i=0;i<TW_Q_LENGTH;i++) {
854*4882a593Smuzhiyun 		switch(which) {
855*4882a593Smuzhiyun 		case 0:
856*4882a593Smuzhiyun 			tw_dev->command_packet_physical_address[i] = dma_handle+(i*size);
857*4882a593Smuzhiyun 			tw_dev->command_packet_virtual_address[i] = (unsigned long *)((unsigned char *)cpu_addr + (i*size));
858*4882a593Smuzhiyun 			break;
859*4882a593Smuzhiyun 		case 1:
860*4882a593Smuzhiyun 			tw_dev->alignment_physical_address[i] = dma_handle+(i*size);
861*4882a593Smuzhiyun 			tw_dev->alignment_virtual_address[i] = (unsigned long *)((unsigned char *)cpu_addr + (i*size));
862*4882a593Smuzhiyun 			break;
863*4882a593Smuzhiyun 		default:
864*4882a593Smuzhiyun 			printk(KERN_WARNING "3w-xxxx: tw_allocate_memory(): case slip in tw_allocate_memory()\n");
865*4882a593Smuzhiyun 			return 1;
866*4882a593Smuzhiyun 		}
867*4882a593Smuzhiyun 	}
868*4882a593Smuzhiyun 
869*4882a593Smuzhiyun 	return 0;
870*4882a593Smuzhiyun } /* End tw_allocate_memory() */
871*4882a593Smuzhiyun 
872*4882a593Smuzhiyun /* This function handles ioctl for the character device */
tw_chrdev_ioctl(struct file * file,unsigned int cmd,unsigned long arg)873*4882a593Smuzhiyun static long tw_chrdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
874*4882a593Smuzhiyun {
875*4882a593Smuzhiyun 	int request_id;
876*4882a593Smuzhiyun 	dma_addr_t dma_handle;
877*4882a593Smuzhiyun 	unsigned short tw_aen_code;
878*4882a593Smuzhiyun 	unsigned long flags;
879*4882a593Smuzhiyun 	unsigned int data_buffer_length = 0;
880*4882a593Smuzhiyun 	unsigned long data_buffer_length_adjusted = 0;
881*4882a593Smuzhiyun 	struct inode *inode = file_inode(file);
882*4882a593Smuzhiyun 	unsigned long *cpu_addr;
883*4882a593Smuzhiyun 	long timeout;
884*4882a593Smuzhiyun 	TW_New_Ioctl *tw_ioctl;
885*4882a593Smuzhiyun 	TW_Passthru *passthru;
886*4882a593Smuzhiyun 	TW_Device_Extension *tw_dev = tw_device_extension_list[iminor(inode)];
887*4882a593Smuzhiyun 	int retval = -EFAULT;
888*4882a593Smuzhiyun 	void __user *argp = (void __user *)arg;
889*4882a593Smuzhiyun 
890*4882a593Smuzhiyun 	dprintk(KERN_WARNING "3w-xxxx: tw_chrdev_ioctl()\n");
891*4882a593Smuzhiyun 
892*4882a593Smuzhiyun 	mutex_lock(&tw_mutex);
893*4882a593Smuzhiyun 	/* Only let one of these through at a time */
894*4882a593Smuzhiyun 	if (mutex_lock_interruptible(&tw_dev->ioctl_lock)) {
895*4882a593Smuzhiyun 		mutex_unlock(&tw_mutex);
896*4882a593Smuzhiyun 		return -EINTR;
897*4882a593Smuzhiyun 	}
898*4882a593Smuzhiyun 
899*4882a593Smuzhiyun 	/* First copy down the buffer length */
900*4882a593Smuzhiyun 	if (copy_from_user(&data_buffer_length, argp, sizeof(unsigned int)))
901*4882a593Smuzhiyun 		goto out;
902*4882a593Smuzhiyun 
903*4882a593Smuzhiyun 	/* Check size */
904*4882a593Smuzhiyun 	if (data_buffer_length > TW_MAX_IOCTL_SECTORS * 512) {
905*4882a593Smuzhiyun 		retval = -EINVAL;
906*4882a593Smuzhiyun 		goto out;
907*4882a593Smuzhiyun 	}
908*4882a593Smuzhiyun 
909*4882a593Smuzhiyun 	/* Hardware can only do multiple of 512 byte transfers */
910*4882a593Smuzhiyun 	data_buffer_length_adjusted = (data_buffer_length + 511) & ~511;
911*4882a593Smuzhiyun 
912*4882a593Smuzhiyun 	/* Now allocate ioctl buf memory */
913*4882a593Smuzhiyun 	cpu_addr = dma_alloc_coherent(&tw_dev->tw_pci_dev->dev, data_buffer_length_adjusted+sizeof(TW_New_Ioctl) - 1, &dma_handle, GFP_KERNEL);
914*4882a593Smuzhiyun 	if (cpu_addr == NULL) {
915*4882a593Smuzhiyun 		retval = -ENOMEM;
916*4882a593Smuzhiyun 		goto out;
917*4882a593Smuzhiyun 	}
918*4882a593Smuzhiyun 
919*4882a593Smuzhiyun 	tw_ioctl = (TW_New_Ioctl *)cpu_addr;
920*4882a593Smuzhiyun 
921*4882a593Smuzhiyun 	/* Now copy down the entire ioctl */
922*4882a593Smuzhiyun 	if (copy_from_user(tw_ioctl, argp, data_buffer_length + sizeof(TW_New_Ioctl) - 1))
923*4882a593Smuzhiyun 		goto out2;
924*4882a593Smuzhiyun 
925*4882a593Smuzhiyun 	passthru = (TW_Passthru *)&tw_ioctl->firmware_command;
926*4882a593Smuzhiyun 
927*4882a593Smuzhiyun 	/* See which ioctl we are doing */
928*4882a593Smuzhiyun 	switch (cmd) {
929*4882a593Smuzhiyun 		case TW_OP_NOP:
930*4882a593Smuzhiyun 			dprintk(KERN_WARNING "3w-xxxx: tw_chrdev_ioctl(): caught TW_OP_NOP.\n");
931*4882a593Smuzhiyun 			break;
932*4882a593Smuzhiyun 		case TW_OP_AEN_LISTEN:
933*4882a593Smuzhiyun 			dprintk(KERN_WARNING "3w-xxxx: tw_chrdev_ioctl(): caught TW_AEN_LISTEN.\n");
934*4882a593Smuzhiyun 			memset(tw_ioctl->data_buffer, 0, data_buffer_length);
935*4882a593Smuzhiyun 
936*4882a593Smuzhiyun 			spin_lock_irqsave(tw_dev->host->host_lock, flags);
937*4882a593Smuzhiyun 			if (tw_dev->aen_head == tw_dev->aen_tail) {
938*4882a593Smuzhiyun 				tw_aen_code = TW_AEN_QUEUE_EMPTY;
939*4882a593Smuzhiyun 			} else {
940*4882a593Smuzhiyun 				tw_aen_code = tw_dev->aen_queue[tw_dev->aen_head];
941*4882a593Smuzhiyun 				if (tw_dev->aen_head == TW_Q_LENGTH - 1) {
942*4882a593Smuzhiyun 					tw_dev->aen_head = TW_Q_START;
943*4882a593Smuzhiyun 				} else {
944*4882a593Smuzhiyun 					tw_dev->aen_head = tw_dev->aen_head + 1;
945*4882a593Smuzhiyun 				}
946*4882a593Smuzhiyun 			}
947*4882a593Smuzhiyun 			spin_unlock_irqrestore(tw_dev->host->host_lock, flags);
948*4882a593Smuzhiyun 			memcpy(tw_ioctl->data_buffer, &tw_aen_code, sizeof(tw_aen_code));
949*4882a593Smuzhiyun 			break;
950*4882a593Smuzhiyun 		case TW_CMD_PACKET_WITH_DATA:
951*4882a593Smuzhiyun 			dprintk(KERN_WARNING "3w-xxxx: tw_chrdev_ioctl(): caught TW_CMD_PACKET_WITH_DATA.\n");
952*4882a593Smuzhiyun 			spin_lock_irqsave(tw_dev->host->host_lock, flags);
953*4882a593Smuzhiyun 
954*4882a593Smuzhiyun 			tw_state_request_start(tw_dev, &request_id);
955*4882a593Smuzhiyun 
956*4882a593Smuzhiyun 			/* Flag internal command */
957*4882a593Smuzhiyun 			tw_dev->srb[request_id] = NULL;
958*4882a593Smuzhiyun 
959*4882a593Smuzhiyun 			/* Flag chrdev ioctl */
960*4882a593Smuzhiyun 			tw_dev->chrdev_request_id = request_id;
961*4882a593Smuzhiyun 
962*4882a593Smuzhiyun 			tw_ioctl->firmware_command.request_id = request_id;
963*4882a593Smuzhiyun 
964*4882a593Smuzhiyun 			/* Load the sg list */
965*4882a593Smuzhiyun 			switch (TW_SGL_OUT(tw_ioctl->firmware_command.opcode__sgloffset)) {
966*4882a593Smuzhiyun 			case 2:
967*4882a593Smuzhiyun 				tw_ioctl->firmware_command.byte8.param.sgl[0].address = dma_handle + sizeof(TW_New_Ioctl) - 1;
968*4882a593Smuzhiyun 				tw_ioctl->firmware_command.byte8.param.sgl[0].length = data_buffer_length_adjusted;
969*4882a593Smuzhiyun 				break;
970*4882a593Smuzhiyun 			case 3:
971*4882a593Smuzhiyun 				tw_ioctl->firmware_command.byte8.io.sgl[0].address = dma_handle + sizeof(TW_New_Ioctl) - 1;
972*4882a593Smuzhiyun 				tw_ioctl->firmware_command.byte8.io.sgl[0].length = data_buffer_length_adjusted;
973*4882a593Smuzhiyun 				break;
974*4882a593Smuzhiyun 			case 5:
975*4882a593Smuzhiyun 				passthru->sg_list[0].address = dma_handle + sizeof(TW_New_Ioctl) - 1;
976*4882a593Smuzhiyun 				passthru->sg_list[0].length = data_buffer_length_adjusted;
977*4882a593Smuzhiyun 				break;
978*4882a593Smuzhiyun 			}
979*4882a593Smuzhiyun 
980*4882a593Smuzhiyun 			memcpy(tw_dev->command_packet_virtual_address[request_id], &(tw_ioctl->firmware_command), sizeof(TW_Command));
981*4882a593Smuzhiyun 
982*4882a593Smuzhiyun 			/* Now post the command packet to the controller */
983*4882a593Smuzhiyun 			tw_post_command_packet(tw_dev, request_id);
984*4882a593Smuzhiyun 			spin_unlock_irqrestore(tw_dev->host->host_lock, flags);
985*4882a593Smuzhiyun 
986*4882a593Smuzhiyun 			timeout = TW_IOCTL_CHRDEV_TIMEOUT*HZ;
987*4882a593Smuzhiyun 
988*4882a593Smuzhiyun 			/* Now wait for the command to complete */
989*4882a593Smuzhiyun 			timeout = wait_event_timeout(tw_dev->ioctl_wqueue, tw_dev->chrdev_request_id == TW_IOCTL_CHRDEV_FREE, timeout);
990*4882a593Smuzhiyun 
991*4882a593Smuzhiyun 			/* We timed out, and didn't get an interrupt */
992*4882a593Smuzhiyun 			if (tw_dev->chrdev_request_id != TW_IOCTL_CHRDEV_FREE) {
993*4882a593Smuzhiyun 				/* Now we need to reset the board */
994*4882a593Smuzhiyun 				printk(KERN_WARNING "3w-xxxx: scsi%d: Character ioctl (0x%x) timed out, resetting card.\n", tw_dev->host->host_no, cmd);
995*4882a593Smuzhiyun 				retval = -EIO;
996*4882a593Smuzhiyun 				if (tw_reset_device_extension(tw_dev)) {
997*4882a593Smuzhiyun 					printk(KERN_WARNING "3w-xxxx: tw_chrdev_ioctl(): Reset failed for card %d.\n", tw_dev->host->host_no);
998*4882a593Smuzhiyun 				}
999*4882a593Smuzhiyun 				goto out2;
1000*4882a593Smuzhiyun 			}
1001*4882a593Smuzhiyun 
1002*4882a593Smuzhiyun 			/* Now copy in the command packet response */
1003*4882a593Smuzhiyun 			memcpy(&(tw_ioctl->firmware_command), tw_dev->command_packet_virtual_address[request_id], sizeof(TW_Command));
1004*4882a593Smuzhiyun 
1005*4882a593Smuzhiyun 			/* Now complete the io */
1006*4882a593Smuzhiyun 			spin_lock_irqsave(tw_dev->host->host_lock, flags);
1007*4882a593Smuzhiyun 			tw_dev->posted_request_count--;
1008*4882a593Smuzhiyun 			tw_dev->state[request_id] = TW_S_COMPLETED;
1009*4882a593Smuzhiyun 			tw_state_request_finish(tw_dev, request_id);
1010*4882a593Smuzhiyun 			spin_unlock_irqrestore(tw_dev->host->host_lock, flags);
1011*4882a593Smuzhiyun 			break;
1012*4882a593Smuzhiyun 		default:
1013*4882a593Smuzhiyun 			retval = -ENOTTY;
1014*4882a593Smuzhiyun 			goto out2;
1015*4882a593Smuzhiyun 	}
1016*4882a593Smuzhiyun 
1017*4882a593Smuzhiyun 	/* Now copy the response to userspace */
1018*4882a593Smuzhiyun 	if (copy_to_user(argp, tw_ioctl, sizeof(TW_New_Ioctl) + data_buffer_length - 1))
1019*4882a593Smuzhiyun 		goto out2;
1020*4882a593Smuzhiyun 	retval = 0;
1021*4882a593Smuzhiyun out2:
1022*4882a593Smuzhiyun 	/* Now free ioctl buf memory */
1023*4882a593Smuzhiyun 	dma_free_coherent(&tw_dev->tw_pci_dev->dev, data_buffer_length_adjusted+sizeof(TW_New_Ioctl) - 1, cpu_addr, dma_handle);
1024*4882a593Smuzhiyun out:
1025*4882a593Smuzhiyun 	mutex_unlock(&tw_dev->ioctl_lock);
1026*4882a593Smuzhiyun 	mutex_unlock(&tw_mutex);
1027*4882a593Smuzhiyun 	return retval;
1028*4882a593Smuzhiyun } /* End tw_chrdev_ioctl() */
1029*4882a593Smuzhiyun 
1030*4882a593Smuzhiyun /* This function handles open for the character device */
1031*4882a593Smuzhiyun /* NOTE that this function races with remove. */
tw_chrdev_open(struct inode * inode,struct file * file)1032*4882a593Smuzhiyun static int tw_chrdev_open(struct inode *inode, struct file *file)
1033*4882a593Smuzhiyun {
1034*4882a593Smuzhiyun 	unsigned int minor_number;
1035*4882a593Smuzhiyun 
1036*4882a593Smuzhiyun 	dprintk(KERN_WARNING "3w-xxxx: tw_ioctl_open()\n");
1037*4882a593Smuzhiyun 
1038*4882a593Smuzhiyun 	if (!capable(CAP_SYS_ADMIN))
1039*4882a593Smuzhiyun 		return -EACCES;
1040*4882a593Smuzhiyun 
1041*4882a593Smuzhiyun 	minor_number = iminor(inode);
1042*4882a593Smuzhiyun 	if (minor_number >= tw_device_extension_count)
1043*4882a593Smuzhiyun 		return -ENODEV;
1044*4882a593Smuzhiyun 
1045*4882a593Smuzhiyun 	return 0;
1046*4882a593Smuzhiyun } /* End tw_chrdev_open() */
1047*4882a593Smuzhiyun 
1048*4882a593Smuzhiyun /* File operations struct for character device */
1049*4882a593Smuzhiyun static const struct file_operations tw_fops = {
1050*4882a593Smuzhiyun 	.owner		= THIS_MODULE,
1051*4882a593Smuzhiyun 	.unlocked_ioctl	= tw_chrdev_ioctl,
1052*4882a593Smuzhiyun 	.compat_ioctl   = compat_ptr_ioctl,
1053*4882a593Smuzhiyun 	.open		= tw_chrdev_open,
1054*4882a593Smuzhiyun 	.release	= NULL,
1055*4882a593Smuzhiyun 	.llseek		= noop_llseek,
1056*4882a593Smuzhiyun };
1057*4882a593Smuzhiyun 
1058*4882a593Smuzhiyun /* This function will free up device extension resources */
tw_free_device_extension(TW_Device_Extension * tw_dev)1059*4882a593Smuzhiyun static void tw_free_device_extension(TW_Device_Extension *tw_dev)
1060*4882a593Smuzhiyun {
1061*4882a593Smuzhiyun 	dprintk(KERN_NOTICE "3w-xxxx: tw_free_device_extension()\n");
1062*4882a593Smuzhiyun 
1063*4882a593Smuzhiyun 	/* Free command packet and generic buffer memory */
1064*4882a593Smuzhiyun 	if (tw_dev->command_packet_virtual_address[0])
1065*4882a593Smuzhiyun 		dma_free_coherent(&tw_dev->tw_pci_dev->dev,
1066*4882a593Smuzhiyun 				sizeof(TW_Command) * TW_Q_LENGTH,
1067*4882a593Smuzhiyun 				tw_dev->command_packet_virtual_address[0],
1068*4882a593Smuzhiyun 				tw_dev->command_packet_physical_address[0]);
1069*4882a593Smuzhiyun 
1070*4882a593Smuzhiyun 	if (tw_dev->alignment_virtual_address[0])
1071*4882a593Smuzhiyun 		dma_free_coherent(&tw_dev->tw_pci_dev->dev,
1072*4882a593Smuzhiyun 				sizeof(TW_Sector) * TW_Q_LENGTH,
1073*4882a593Smuzhiyun 				tw_dev->alignment_virtual_address[0],
1074*4882a593Smuzhiyun 				tw_dev->alignment_physical_address[0]);
1075*4882a593Smuzhiyun } /* End tw_free_device_extension() */
1076*4882a593Smuzhiyun 
1077*4882a593Smuzhiyun /* This function will send an initconnection command to controller */
tw_initconnection(TW_Device_Extension * tw_dev,int message_credits)1078*4882a593Smuzhiyun static int tw_initconnection(TW_Device_Extension *tw_dev, int message_credits)
1079*4882a593Smuzhiyun {
1080*4882a593Smuzhiyun 	unsigned long command_que_value;
1081*4882a593Smuzhiyun 	TW_Command  *command_packet;
1082*4882a593Smuzhiyun 	TW_Response_Queue response_queue;
1083*4882a593Smuzhiyun 	int request_id = 0;
1084*4882a593Smuzhiyun 
1085*4882a593Smuzhiyun 	dprintk(KERN_NOTICE "3w-xxxx: tw_initconnection()\n");
1086*4882a593Smuzhiyun 
1087*4882a593Smuzhiyun 	/* Initialize InitConnection command packet */
1088*4882a593Smuzhiyun 	if (tw_dev->command_packet_virtual_address[request_id] == NULL) {
1089*4882a593Smuzhiyun 		printk(KERN_WARNING "3w-xxxx: tw_initconnection(): Bad command packet virtual address.\n");
1090*4882a593Smuzhiyun 		return 1;
1091*4882a593Smuzhiyun 	}
1092*4882a593Smuzhiyun 
1093*4882a593Smuzhiyun 	command_packet = (TW_Command *)tw_dev->command_packet_virtual_address[request_id];
1094*4882a593Smuzhiyun 	memset(command_packet, 0, sizeof(TW_Sector));
1095*4882a593Smuzhiyun 	command_packet->opcode__sgloffset = TW_OPSGL_IN(0, TW_OP_INIT_CONNECTION);
1096*4882a593Smuzhiyun 	command_packet->size = TW_INIT_COMMAND_PACKET_SIZE;
1097*4882a593Smuzhiyun 	command_packet->request_id = request_id;
1098*4882a593Smuzhiyun 	command_packet->status = 0x0;
1099*4882a593Smuzhiyun 	command_packet->flags = 0x0;
1100*4882a593Smuzhiyun 	command_packet->byte6.message_credits = message_credits;
1101*4882a593Smuzhiyun 	command_packet->byte8.init_connection.response_queue_pointer = 0x0;
1102*4882a593Smuzhiyun 	command_que_value = tw_dev->command_packet_physical_address[request_id];
1103*4882a593Smuzhiyun 
1104*4882a593Smuzhiyun 	if (command_que_value == 0) {
1105*4882a593Smuzhiyun 		printk(KERN_WARNING "3w-xxxx: tw_initconnection(): Bad command packet physical address.\n");
1106*4882a593Smuzhiyun 		return 1;
1107*4882a593Smuzhiyun 	}
1108*4882a593Smuzhiyun 
1109*4882a593Smuzhiyun 	/* Send command packet to the board */
1110*4882a593Smuzhiyun 	outl(command_que_value, TW_COMMAND_QUEUE_REG_ADDR(tw_dev));
1111*4882a593Smuzhiyun 
1112*4882a593Smuzhiyun 	/* Poll for completion */
1113*4882a593Smuzhiyun 	if (tw_poll_status_gone(tw_dev, TW_STATUS_RESPONSE_QUEUE_EMPTY, 30) == 0) {
1114*4882a593Smuzhiyun 		response_queue.value = inl(TW_RESPONSE_QUEUE_REG_ADDR(tw_dev));
1115*4882a593Smuzhiyun 		request_id = TW_RESID_OUT(response_queue.response_id);
1116*4882a593Smuzhiyun 
1117*4882a593Smuzhiyun 		if (request_id != 0) {
1118*4882a593Smuzhiyun 			/* unexpected request id */
1119*4882a593Smuzhiyun 			printk(KERN_WARNING "3w-xxxx: tw_initconnection(): Unexpected request id.\n");
1120*4882a593Smuzhiyun 			return 1;
1121*4882a593Smuzhiyun 		}
1122*4882a593Smuzhiyun 		if (command_packet->status != 0) {
1123*4882a593Smuzhiyun 			/* bad response */
1124*4882a593Smuzhiyun 			tw_decode_sense(tw_dev, request_id, 0);
1125*4882a593Smuzhiyun 			return 1;
1126*4882a593Smuzhiyun 		}
1127*4882a593Smuzhiyun 	}
1128*4882a593Smuzhiyun 	return 0;
1129*4882a593Smuzhiyun } /* End tw_initconnection() */
1130*4882a593Smuzhiyun 
1131*4882a593Smuzhiyun /* Set a value in the features table */
tw_setfeature(TW_Device_Extension * tw_dev,int parm,int param_size,unsigned char * val)1132*4882a593Smuzhiyun static int tw_setfeature(TW_Device_Extension *tw_dev, int parm, int param_size,
1133*4882a593Smuzhiyun                   unsigned char *val)
1134*4882a593Smuzhiyun {
1135*4882a593Smuzhiyun 	TW_Param *param;
1136*4882a593Smuzhiyun 	TW_Command  *command_packet;
1137*4882a593Smuzhiyun 	TW_Response_Queue response_queue;
1138*4882a593Smuzhiyun 	int request_id = 0;
1139*4882a593Smuzhiyun 	unsigned long command_que_value;
1140*4882a593Smuzhiyun 	unsigned long param_value;
1141*4882a593Smuzhiyun 
1142*4882a593Smuzhiyun   	/* Initialize SetParam command packet */
1143*4882a593Smuzhiyun 	if (tw_dev->command_packet_virtual_address[request_id] == NULL) {
1144*4882a593Smuzhiyun 		printk(KERN_WARNING "3w-xxxx: tw_setfeature(): Bad command packet virtual address.\n");
1145*4882a593Smuzhiyun 		return 1;
1146*4882a593Smuzhiyun 	}
1147*4882a593Smuzhiyun 	command_packet = (TW_Command *)tw_dev->command_packet_virtual_address[request_id];
1148*4882a593Smuzhiyun 	memset(command_packet, 0, sizeof(TW_Sector));
1149*4882a593Smuzhiyun 	param = (TW_Param *)tw_dev->alignment_virtual_address[request_id];
1150*4882a593Smuzhiyun 
1151*4882a593Smuzhiyun 	command_packet->opcode__sgloffset = TW_OPSGL_IN(2, TW_OP_SET_PARAM);
1152*4882a593Smuzhiyun 	param->table_id = 0x404;  /* Features table */
1153*4882a593Smuzhiyun 	param->parameter_id = parm;
1154*4882a593Smuzhiyun 	param->parameter_size_bytes = param_size;
1155*4882a593Smuzhiyun 	memcpy(param->data, val, param_size);
1156*4882a593Smuzhiyun 
1157*4882a593Smuzhiyun 	param_value = tw_dev->alignment_physical_address[request_id];
1158*4882a593Smuzhiyun 	if (param_value == 0) {
1159*4882a593Smuzhiyun 		printk(KERN_WARNING "3w-xxxx: tw_setfeature(): Bad alignment physical address.\n");
1160*4882a593Smuzhiyun 		tw_dev->state[request_id] = TW_S_COMPLETED;
1161*4882a593Smuzhiyun 		tw_state_request_finish(tw_dev, request_id);
1162*4882a593Smuzhiyun 		tw_dev->srb[request_id]->result = (DID_OK << 16);
1163*4882a593Smuzhiyun 		tw_dev->srb[request_id]->scsi_done(tw_dev->srb[request_id]);
1164*4882a593Smuzhiyun 	}
1165*4882a593Smuzhiyun 	command_packet->byte8.param.sgl[0].address = param_value;
1166*4882a593Smuzhiyun 	command_packet->byte8.param.sgl[0].length = sizeof(TW_Sector);
1167*4882a593Smuzhiyun 
1168*4882a593Smuzhiyun 	command_packet->size = 4;
1169*4882a593Smuzhiyun 	command_packet->request_id = request_id;
1170*4882a593Smuzhiyun 	command_packet->byte6.parameter_count = 1;
1171*4882a593Smuzhiyun 
1172*4882a593Smuzhiyun   	command_que_value = tw_dev->command_packet_physical_address[request_id];
1173*4882a593Smuzhiyun 	if (command_que_value == 0) {
1174*4882a593Smuzhiyun 		printk(KERN_WARNING "3w-xxxx: tw_setfeature(): Bad command packet physical address.\n");
1175*4882a593Smuzhiyun 		return 1;
1176*4882a593Smuzhiyun 	}
1177*4882a593Smuzhiyun 
1178*4882a593Smuzhiyun 	/* Send command packet to the board */
1179*4882a593Smuzhiyun 	outl(command_que_value, TW_COMMAND_QUEUE_REG_ADDR(tw_dev));
1180*4882a593Smuzhiyun 
1181*4882a593Smuzhiyun 	/* Poll for completion */
1182*4882a593Smuzhiyun 	if (tw_poll_status_gone(tw_dev, TW_STATUS_RESPONSE_QUEUE_EMPTY, 30) == 0) {
1183*4882a593Smuzhiyun 		response_queue.value = inl(TW_RESPONSE_QUEUE_REG_ADDR(tw_dev));
1184*4882a593Smuzhiyun 		request_id = TW_RESID_OUT(response_queue.response_id);
1185*4882a593Smuzhiyun 
1186*4882a593Smuzhiyun 		if (request_id != 0) {
1187*4882a593Smuzhiyun 			/* unexpected request id */
1188*4882a593Smuzhiyun 			printk(KERN_WARNING "3w-xxxx: tw_setfeature(): Unexpected request id.\n");
1189*4882a593Smuzhiyun 			return 1;
1190*4882a593Smuzhiyun 		}
1191*4882a593Smuzhiyun 		if (command_packet->status != 0) {
1192*4882a593Smuzhiyun 			/* bad response */
1193*4882a593Smuzhiyun 			tw_decode_sense(tw_dev, request_id, 0);
1194*4882a593Smuzhiyun 			return 1;
1195*4882a593Smuzhiyun 		}
1196*4882a593Smuzhiyun 	}
1197*4882a593Smuzhiyun 
1198*4882a593Smuzhiyun 	return 0;
1199*4882a593Smuzhiyun } /* End tw_setfeature() */
1200*4882a593Smuzhiyun 
1201*4882a593Smuzhiyun /* This function will reset a controller */
tw_reset_sequence(TW_Device_Extension * tw_dev)1202*4882a593Smuzhiyun static int tw_reset_sequence(TW_Device_Extension *tw_dev)
1203*4882a593Smuzhiyun {
1204*4882a593Smuzhiyun 	int error = 0;
1205*4882a593Smuzhiyun 	int tries = 0;
1206*4882a593Smuzhiyun 	unsigned char c = 1;
1207*4882a593Smuzhiyun 
1208*4882a593Smuzhiyun 	/* Reset the board */
1209*4882a593Smuzhiyun 	while (tries < TW_MAX_RESET_TRIES) {
1210*4882a593Smuzhiyun 		TW_SOFT_RESET(tw_dev);
1211*4882a593Smuzhiyun 
1212*4882a593Smuzhiyun 		error = tw_aen_drain_queue(tw_dev);
1213*4882a593Smuzhiyun 		if (error) {
1214*4882a593Smuzhiyun 			printk(KERN_WARNING "3w-xxxx: scsi%d: AEN drain failed, retrying.\n", tw_dev->host->host_no);
1215*4882a593Smuzhiyun 			tries++;
1216*4882a593Smuzhiyun 			continue;
1217*4882a593Smuzhiyun 		}
1218*4882a593Smuzhiyun 
1219*4882a593Smuzhiyun 		/* Check for controller errors */
1220*4882a593Smuzhiyun 		if (tw_check_errors(tw_dev)) {
1221*4882a593Smuzhiyun 			printk(KERN_WARNING "3w-xxxx: scsi%d: Controller errors found, retrying.\n", tw_dev->host->host_no);
1222*4882a593Smuzhiyun 			tries++;
1223*4882a593Smuzhiyun 			continue;
1224*4882a593Smuzhiyun 		}
1225*4882a593Smuzhiyun 
1226*4882a593Smuzhiyun 		/* Now the controller is in a good state */
1227*4882a593Smuzhiyun 		break;
1228*4882a593Smuzhiyun 	}
1229*4882a593Smuzhiyun 
1230*4882a593Smuzhiyun 	if (tries >= TW_MAX_RESET_TRIES) {
1231*4882a593Smuzhiyun 		printk(KERN_WARNING "3w-xxxx: scsi%d: Controller errors, card not responding, check all cabling.\n", tw_dev->host->host_no);
1232*4882a593Smuzhiyun 		return 1;
1233*4882a593Smuzhiyun 	}
1234*4882a593Smuzhiyun 
1235*4882a593Smuzhiyun 	error = tw_initconnection(tw_dev, TW_INIT_MESSAGE_CREDITS);
1236*4882a593Smuzhiyun 	if (error) {
1237*4882a593Smuzhiyun 		printk(KERN_WARNING "3w-xxxx: scsi%d: Connection initialization failed.\n", tw_dev->host->host_no);
1238*4882a593Smuzhiyun 		return 1;
1239*4882a593Smuzhiyun 	}
1240*4882a593Smuzhiyun 
1241*4882a593Smuzhiyun 	error = tw_setfeature(tw_dev, 2, 1, &c);
1242*4882a593Smuzhiyun 	if (error) {
1243*4882a593Smuzhiyun 		printk(KERN_WARNING "3w-xxxx: Unable to set features for card, probable old firmware or card.\n");
1244*4882a593Smuzhiyun 	}
1245*4882a593Smuzhiyun 
1246*4882a593Smuzhiyun 	return 0;
1247*4882a593Smuzhiyun } /* End tw_reset_sequence() */
1248*4882a593Smuzhiyun 
1249*4882a593Smuzhiyun /* This function will initialize the fields of a device extension */
tw_initialize_device_extension(TW_Device_Extension * tw_dev)1250*4882a593Smuzhiyun static int tw_initialize_device_extension(TW_Device_Extension *tw_dev)
1251*4882a593Smuzhiyun {
1252*4882a593Smuzhiyun 	int i, error=0;
1253*4882a593Smuzhiyun 
1254*4882a593Smuzhiyun 	dprintk(KERN_NOTICE "3w-xxxx: tw_initialize_device_extension()\n");
1255*4882a593Smuzhiyun 
1256*4882a593Smuzhiyun 	/* Initialize command packet buffers */
1257*4882a593Smuzhiyun 	error = tw_allocate_memory(tw_dev, sizeof(TW_Command), 0);
1258*4882a593Smuzhiyun 	if (error) {
1259*4882a593Smuzhiyun 		printk(KERN_WARNING "3w-xxxx: Command packet memory allocation failed.\n");
1260*4882a593Smuzhiyun 		return 1;
1261*4882a593Smuzhiyun 	}
1262*4882a593Smuzhiyun 
1263*4882a593Smuzhiyun 	/* Initialize generic buffer */
1264*4882a593Smuzhiyun 	error = tw_allocate_memory(tw_dev, sizeof(TW_Sector), 1);
1265*4882a593Smuzhiyun 	if (error) {
1266*4882a593Smuzhiyun 		printk(KERN_WARNING "3w-xxxx: Generic memory allocation failed.\n");
1267*4882a593Smuzhiyun 		return 1;
1268*4882a593Smuzhiyun 	}
1269*4882a593Smuzhiyun 
1270*4882a593Smuzhiyun 	for (i=0;i<TW_Q_LENGTH;i++) {
1271*4882a593Smuzhiyun 		tw_dev->free_queue[i] = i;
1272*4882a593Smuzhiyun 		tw_dev->state[i] = TW_S_INITIAL;
1273*4882a593Smuzhiyun 	}
1274*4882a593Smuzhiyun 
1275*4882a593Smuzhiyun 	tw_dev->pending_head = TW_Q_START;
1276*4882a593Smuzhiyun 	tw_dev->pending_tail = TW_Q_START;
1277*4882a593Smuzhiyun 	tw_dev->chrdev_request_id = TW_IOCTL_CHRDEV_FREE;
1278*4882a593Smuzhiyun 
1279*4882a593Smuzhiyun 	mutex_init(&tw_dev->ioctl_lock);
1280*4882a593Smuzhiyun 	init_waitqueue_head(&tw_dev->ioctl_wqueue);
1281*4882a593Smuzhiyun 
1282*4882a593Smuzhiyun 	return 0;
1283*4882a593Smuzhiyun } /* End tw_initialize_device_extension() */
1284*4882a593Smuzhiyun 
1285*4882a593Smuzhiyun /* This function will reset a device extension */
tw_reset_device_extension(TW_Device_Extension * tw_dev)1286*4882a593Smuzhiyun static int tw_reset_device_extension(TW_Device_Extension *tw_dev)
1287*4882a593Smuzhiyun {
1288*4882a593Smuzhiyun 	int i = 0;
1289*4882a593Smuzhiyun 	struct scsi_cmnd *srb;
1290*4882a593Smuzhiyun 	unsigned long flags = 0;
1291*4882a593Smuzhiyun 
1292*4882a593Smuzhiyun 	dprintk(KERN_NOTICE "3w-xxxx: tw_reset_device_extension()\n");
1293*4882a593Smuzhiyun 
1294*4882a593Smuzhiyun 	set_bit(TW_IN_RESET, &tw_dev->flags);
1295*4882a593Smuzhiyun 	TW_DISABLE_INTERRUPTS(tw_dev);
1296*4882a593Smuzhiyun 	TW_MASK_COMMAND_INTERRUPT(tw_dev);
1297*4882a593Smuzhiyun 	spin_lock_irqsave(tw_dev->host->host_lock, flags);
1298*4882a593Smuzhiyun 
1299*4882a593Smuzhiyun 	/* Abort all requests that are in progress */
1300*4882a593Smuzhiyun 	for (i=0;i<TW_Q_LENGTH;i++) {
1301*4882a593Smuzhiyun 		if ((tw_dev->state[i] != TW_S_FINISHED) &&
1302*4882a593Smuzhiyun 		    (tw_dev->state[i] != TW_S_INITIAL) &&
1303*4882a593Smuzhiyun 		    (tw_dev->state[i] != TW_S_COMPLETED)) {
1304*4882a593Smuzhiyun 			srb = tw_dev->srb[i];
1305*4882a593Smuzhiyun 			if (srb != NULL) {
1306*4882a593Smuzhiyun 				srb->result = (DID_RESET << 16);
1307*4882a593Smuzhiyun 				scsi_dma_unmap(srb);
1308*4882a593Smuzhiyun 				srb->scsi_done(srb);
1309*4882a593Smuzhiyun 			}
1310*4882a593Smuzhiyun 		}
1311*4882a593Smuzhiyun 	}
1312*4882a593Smuzhiyun 
1313*4882a593Smuzhiyun 	/* Reset queues and counts */
1314*4882a593Smuzhiyun 	for (i=0;i<TW_Q_LENGTH;i++) {
1315*4882a593Smuzhiyun 		tw_dev->free_queue[i] = i;
1316*4882a593Smuzhiyun 		tw_dev->state[i] = TW_S_INITIAL;
1317*4882a593Smuzhiyun 	}
1318*4882a593Smuzhiyun 	tw_dev->free_head = TW_Q_START;
1319*4882a593Smuzhiyun 	tw_dev->free_tail = TW_Q_START;
1320*4882a593Smuzhiyun 	tw_dev->posted_request_count = 0;
1321*4882a593Smuzhiyun 	tw_dev->pending_request_count = 0;
1322*4882a593Smuzhiyun 	tw_dev->pending_head = TW_Q_START;
1323*4882a593Smuzhiyun 	tw_dev->pending_tail = TW_Q_START;
1324*4882a593Smuzhiyun 	tw_dev->reset_print = 0;
1325*4882a593Smuzhiyun 
1326*4882a593Smuzhiyun 	spin_unlock_irqrestore(tw_dev->host->host_lock, flags);
1327*4882a593Smuzhiyun 
1328*4882a593Smuzhiyun 	if (tw_reset_sequence(tw_dev)) {
1329*4882a593Smuzhiyun 		printk(KERN_WARNING "3w-xxxx: scsi%d: Reset sequence failed.\n", tw_dev->host->host_no);
1330*4882a593Smuzhiyun 		return 1;
1331*4882a593Smuzhiyun 	}
1332*4882a593Smuzhiyun 
1333*4882a593Smuzhiyun 	TW_ENABLE_AND_CLEAR_INTERRUPTS(tw_dev);
1334*4882a593Smuzhiyun 	clear_bit(TW_IN_RESET, &tw_dev->flags);
1335*4882a593Smuzhiyun 	tw_dev->chrdev_request_id = TW_IOCTL_CHRDEV_FREE;
1336*4882a593Smuzhiyun 
1337*4882a593Smuzhiyun 	return 0;
1338*4882a593Smuzhiyun } /* End tw_reset_device_extension() */
1339*4882a593Smuzhiyun 
1340*4882a593Smuzhiyun /* This funciton returns unit geometry in cylinders/heads/sectors */
tw_scsi_biosparam(struct scsi_device * sdev,struct block_device * bdev,sector_t capacity,int geom[])1341*4882a593Smuzhiyun static int tw_scsi_biosparam(struct scsi_device *sdev, struct block_device *bdev,
1342*4882a593Smuzhiyun 		sector_t capacity, int geom[])
1343*4882a593Smuzhiyun {
1344*4882a593Smuzhiyun 	int heads, sectors, cylinders;
1345*4882a593Smuzhiyun 	TW_Device_Extension *tw_dev;
1346*4882a593Smuzhiyun 
1347*4882a593Smuzhiyun 	dprintk(KERN_NOTICE "3w-xxxx: tw_scsi_biosparam()\n");
1348*4882a593Smuzhiyun 	tw_dev = (TW_Device_Extension *)sdev->host->hostdata;
1349*4882a593Smuzhiyun 
1350*4882a593Smuzhiyun 	heads = 64;
1351*4882a593Smuzhiyun 	sectors = 32;
1352*4882a593Smuzhiyun 	cylinders = sector_div(capacity, heads * sectors);
1353*4882a593Smuzhiyun 
1354*4882a593Smuzhiyun 	if (capacity >= 0x200000) {
1355*4882a593Smuzhiyun 		heads = 255;
1356*4882a593Smuzhiyun 		sectors = 63;
1357*4882a593Smuzhiyun 		cylinders = sector_div(capacity, heads * sectors);
1358*4882a593Smuzhiyun 	}
1359*4882a593Smuzhiyun 
1360*4882a593Smuzhiyun 	dprintk(KERN_NOTICE "3w-xxxx: tw_scsi_biosparam(): heads = %d, sectors = %d, cylinders = %d\n", heads, sectors, cylinders);
1361*4882a593Smuzhiyun 	geom[0] = heads;
1362*4882a593Smuzhiyun 	geom[1] = sectors;
1363*4882a593Smuzhiyun 	geom[2] = cylinders;
1364*4882a593Smuzhiyun 
1365*4882a593Smuzhiyun 	return 0;
1366*4882a593Smuzhiyun } /* End tw_scsi_biosparam() */
1367*4882a593Smuzhiyun 
1368*4882a593Smuzhiyun /* This is the new scsi eh reset function */
tw_scsi_eh_reset(struct scsi_cmnd * SCpnt)1369*4882a593Smuzhiyun static int tw_scsi_eh_reset(struct scsi_cmnd *SCpnt)
1370*4882a593Smuzhiyun {
1371*4882a593Smuzhiyun 	TW_Device_Extension *tw_dev=NULL;
1372*4882a593Smuzhiyun 	int retval = FAILED;
1373*4882a593Smuzhiyun 
1374*4882a593Smuzhiyun 	tw_dev = (TW_Device_Extension *)SCpnt->device->host->hostdata;
1375*4882a593Smuzhiyun 
1376*4882a593Smuzhiyun 	tw_dev->num_resets++;
1377*4882a593Smuzhiyun 
1378*4882a593Smuzhiyun 	sdev_printk(KERN_WARNING, SCpnt->device,
1379*4882a593Smuzhiyun 		"WARNING: Command (0x%x) timed out, resetting card.\n",
1380*4882a593Smuzhiyun 		SCpnt->cmnd[0]);
1381*4882a593Smuzhiyun 
1382*4882a593Smuzhiyun 	/* Make sure we are not issuing an ioctl or resetting from ioctl */
1383*4882a593Smuzhiyun 	mutex_lock(&tw_dev->ioctl_lock);
1384*4882a593Smuzhiyun 
1385*4882a593Smuzhiyun 	/* Now reset the card and some of the device extension data */
1386*4882a593Smuzhiyun 	if (tw_reset_device_extension(tw_dev)) {
1387*4882a593Smuzhiyun 		printk(KERN_WARNING "3w-xxxx: scsi%d: Reset failed.\n", tw_dev->host->host_no);
1388*4882a593Smuzhiyun 		goto out;
1389*4882a593Smuzhiyun 	}
1390*4882a593Smuzhiyun 
1391*4882a593Smuzhiyun 	retval = SUCCESS;
1392*4882a593Smuzhiyun out:
1393*4882a593Smuzhiyun 	mutex_unlock(&tw_dev->ioctl_lock);
1394*4882a593Smuzhiyun 	return retval;
1395*4882a593Smuzhiyun } /* End tw_scsi_eh_reset() */
1396*4882a593Smuzhiyun 
1397*4882a593Smuzhiyun /* This function handles scsi inquiry commands */
tw_scsiop_inquiry(TW_Device_Extension * tw_dev,int request_id)1398*4882a593Smuzhiyun static int tw_scsiop_inquiry(TW_Device_Extension *tw_dev, int request_id)
1399*4882a593Smuzhiyun {
1400*4882a593Smuzhiyun 	TW_Param *param;
1401*4882a593Smuzhiyun 	TW_Command *command_packet;
1402*4882a593Smuzhiyun 	unsigned long command_que_value;
1403*4882a593Smuzhiyun 	unsigned long param_value;
1404*4882a593Smuzhiyun 
1405*4882a593Smuzhiyun 	dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_inquiry()\n");
1406*4882a593Smuzhiyun 
1407*4882a593Smuzhiyun 	/* Initialize command packet */
1408*4882a593Smuzhiyun 	command_packet = (TW_Command *)tw_dev->command_packet_virtual_address[request_id];
1409*4882a593Smuzhiyun 	if (command_packet == NULL) {
1410*4882a593Smuzhiyun 		printk(KERN_WARNING "3w-xxxx: tw_scsiop_inquiry(): Bad command packet virtual address.\n");
1411*4882a593Smuzhiyun 		return 1;
1412*4882a593Smuzhiyun 	}
1413*4882a593Smuzhiyun 	memset(command_packet, 0, sizeof(TW_Sector));
1414*4882a593Smuzhiyun 	command_packet->opcode__sgloffset = TW_OPSGL_IN(2, TW_OP_GET_PARAM);
1415*4882a593Smuzhiyun 	command_packet->size = 4;
1416*4882a593Smuzhiyun 	command_packet->request_id = request_id;
1417*4882a593Smuzhiyun 	command_packet->status = 0;
1418*4882a593Smuzhiyun 	command_packet->flags = 0;
1419*4882a593Smuzhiyun 	command_packet->byte6.parameter_count = 1;
1420*4882a593Smuzhiyun 
1421*4882a593Smuzhiyun 	/* Now setup the param */
1422*4882a593Smuzhiyun 	if (tw_dev->alignment_virtual_address[request_id] == NULL) {
1423*4882a593Smuzhiyun 		printk(KERN_WARNING "3w-xxxx: tw_scsiop_inquiry(): Bad alignment virtual address.\n");
1424*4882a593Smuzhiyun 		return 1;
1425*4882a593Smuzhiyun 	}
1426*4882a593Smuzhiyun 	param = (TW_Param *)tw_dev->alignment_virtual_address[request_id];
1427*4882a593Smuzhiyun 	memset(param, 0, sizeof(TW_Sector));
1428*4882a593Smuzhiyun 	param->table_id = 3;	 /* unit summary table */
1429*4882a593Smuzhiyun 	param->parameter_id = 3; /* unitsstatus parameter */
1430*4882a593Smuzhiyun 	param->parameter_size_bytes = TW_MAX_UNITS;
1431*4882a593Smuzhiyun 	param_value = tw_dev->alignment_physical_address[request_id];
1432*4882a593Smuzhiyun 	if (param_value == 0) {
1433*4882a593Smuzhiyun 		printk(KERN_WARNING "3w-xxxx: tw_scsiop_inquiry(): Bad alignment physical address.\n");
1434*4882a593Smuzhiyun 		return 1;
1435*4882a593Smuzhiyun 	}
1436*4882a593Smuzhiyun 
1437*4882a593Smuzhiyun 	command_packet->byte8.param.sgl[0].address = param_value;
1438*4882a593Smuzhiyun 	command_packet->byte8.param.sgl[0].length = sizeof(TW_Sector);
1439*4882a593Smuzhiyun 	command_que_value = tw_dev->command_packet_physical_address[request_id];
1440*4882a593Smuzhiyun 	if (command_que_value == 0) {
1441*4882a593Smuzhiyun 		printk(KERN_WARNING "3w-xxxx: tw_scsiop_inquiry(): Bad command packet physical address.\n");
1442*4882a593Smuzhiyun 		return 1;
1443*4882a593Smuzhiyun 	}
1444*4882a593Smuzhiyun 
1445*4882a593Smuzhiyun 	/* Now try to post the command packet */
1446*4882a593Smuzhiyun 	tw_post_command_packet(tw_dev, request_id);
1447*4882a593Smuzhiyun 
1448*4882a593Smuzhiyun 	return 0;
1449*4882a593Smuzhiyun } /* End tw_scsiop_inquiry() */
1450*4882a593Smuzhiyun 
tw_transfer_internal(TW_Device_Extension * tw_dev,int request_id,void * data,unsigned int len)1451*4882a593Smuzhiyun static void tw_transfer_internal(TW_Device_Extension *tw_dev, int request_id,
1452*4882a593Smuzhiyun 				 void *data, unsigned int len)
1453*4882a593Smuzhiyun {
1454*4882a593Smuzhiyun 	scsi_sg_copy_from_buffer(tw_dev->srb[request_id], data, len);
1455*4882a593Smuzhiyun }
1456*4882a593Smuzhiyun 
1457*4882a593Smuzhiyun /* This function is called by the isr to complete an inquiry command */
tw_scsiop_inquiry_complete(TW_Device_Extension * tw_dev,int request_id)1458*4882a593Smuzhiyun static int tw_scsiop_inquiry_complete(TW_Device_Extension *tw_dev, int request_id)
1459*4882a593Smuzhiyun {
1460*4882a593Smuzhiyun 	unsigned char *is_unit_present;
1461*4882a593Smuzhiyun 	unsigned char request_buffer[36];
1462*4882a593Smuzhiyun 	TW_Param *param;
1463*4882a593Smuzhiyun 
1464*4882a593Smuzhiyun 	dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_inquiry_complete()\n");
1465*4882a593Smuzhiyun 
1466*4882a593Smuzhiyun 	memset(request_buffer, 0, sizeof(request_buffer));
1467*4882a593Smuzhiyun 	request_buffer[0] = TYPE_DISK; /* Peripheral device type */
1468*4882a593Smuzhiyun 	request_buffer[1] = 0;	       /* Device type modifier */
1469*4882a593Smuzhiyun 	request_buffer[2] = 0;	       /* No ansi/iso compliance */
1470*4882a593Smuzhiyun 	request_buffer[4] = 31;	       /* Additional length */
1471*4882a593Smuzhiyun 	memcpy(&request_buffer[8], "3ware   ", 8);	 /* Vendor ID */
1472*4882a593Smuzhiyun 	sprintf(&request_buffer[16], "Logical Disk %-2d ", tw_dev->srb[request_id]->device->id);
1473*4882a593Smuzhiyun 	memcpy(&request_buffer[32], TW_DRIVER_VERSION, 3);
1474*4882a593Smuzhiyun 	tw_transfer_internal(tw_dev, request_id, request_buffer,
1475*4882a593Smuzhiyun 			     sizeof(request_buffer));
1476*4882a593Smuzhiyun 
1477*4882a593Smuzhiyun 	param = (TW_Param *)tw_dev->alignment_virtual_address[request_id];
1478*4882a593Smuzhiyun 	if (param == NULL) {
1479*4882a593Smuzhiyun 		printk(KERN_WARNING "3w-xxxx: tw_scsiop_inquiry_complete(): Bad alignment virtual address.\n");
1480*4882a593Smuzhiyun 		return 1;
1481*4882a593Smuzhiyun 	}
1482*4882a593Smuzhiyun 	is_unit_present = &(param->data[0]);
1483*4882a593Smuzhiyun 
1484*4882a593Smuzhiyun 	if (is_unit_present[tw_dev->srb[request_id]->device->id] & TW_UNIT_ONLINE) {
1485*4882a593Smuzhiyun 		tw_dev->is_unit_present[tw_dev->srb[request_id]->device->id] = 1;
1486*4882a593Smuzhiyun 	} else {
1487*4882a593Smuzhiyun 		tw_dev->is_unit_present[tw_dev->srb[request_id]->device->id] = 0;
1488*4882a593Smuzhiyun 		tw_dev->srb[request_id]->result = (DID_BAD_TARGET << 16);
1489*4882a593Smuzhiyun 		return TW_ISR_DONT_RESULT;
1490*4882a593Smuzhiyun 	}
1491*4882a593Smuzhiyun 
1492*4882a593Smuzhiyun 	return 0;
1493*4882a593Smuzhiyun } /* End tw_scsiop_inquiry_complete() */
1494*4882a593Smuzhiyun 
1495*4882a593Smuzhiyun /* This function handles scsi mode_sense commands */
tw_scsiop_mode_sense(TW_Device_Extension * tw_dev,int request_id)1496*4882a593Smuzhiyun static int tw_scsiop_mode_sense(TW_Device_Extension *tw_dev, int request_id)
1497*4882a593Smuzhiyun {
1498*4882a593Smuzhiyun 	TW_Param *param;
1499*4882a593Smuzhiyun 	TW_Command *command_packet;
1500*4882a593Smuzhiyun 	unsigned long command_que_value;
1501*4882a593Smuzhiyun 	unsigned long param_value;
1502*4882a593Smuzhiyun 
1503*4882a593Smuzhiyun 	dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_mode_sense()\n");
1504*4882a593Smuzhiyun 
1505*4882a593Smuzhiyun 	/* Only page control = 0, page code = 0x8 (cache page) supported */
1506*4882a593Smuzhiyun 	if (tw_dev->srb[request_id]->cmnd[2] != 0x8) {
1507*4882a593Smuzhiyun 		tw_dev->state[request_id] = TW_S_COMPLETED;
1508*4882a593Smuzhiyun 		tw_state_request_finish(tw_dev, request_id);
1509*4882a593Smuzhiyun 		tw_dev->srb[request_id]->result = (DID_OK << 16);
1510*4882a593Smuzhiyun 		tw_dev->srb[request_id]->scsi_done(tw_dev->srb[request_id]);
1511*4882a593Smuzhiyun 		return 0;
1512*4882a593Smuzhiyun 	}
1513*4882a593Smuzhiyun 
1514*4882a593Smuzhiyun 	/* Now read firmware cache setting for this unit */
1515*4882a593Smuzhiyun 	command_packet = (TW_Command *)tw_dev->command_packet_virtual_address[request_id];
1516*4882a593Smuzhiyun 	if (command_packet == NULL) {
1517*4882a593Smuzhiyun 		printk(KERN_WARNING "3w-xxxx: tw_scsiop_mode_sense(): Bad command packet virtual address.\n");
1518*4882a593Smuzhiyun 		return 1;
1519*4882a593Smuzhiyun 	}
1520*4882a593Smuzhiyun 
1521*4882a593Smuzhiyun 	/* Setup the command packet */
1522*4882a593Smuzhiyun 	memset(command_packet, 0, sizeof(TW_Sector));
1523*4882a593Smuzhiyun 	command_packet->opcode__sgloffset = TW_OPSGL_IN(2, TW_OP_GET_PARAM);
1524*4882a593Smuzhiyun 	command_packet->size = 4;
1525*4882a593Smuzhiyun 	command_packet->request_id = request_id;
1526*4882a593Smuzhiyun 	command_packet->status = 0;
1527*4882a593Smuzhiyun 	command_packet->flags = 0;
1528*4882a593Smuzhiyun 	command_packet->byte6.parameter_count = 1;
1529*4882a593Smuzhiyun 
1530*4882a593Smuzhiyun 	/* Setup the param */
1531*4882a593Smuzhiyun 	if (tw_dev->alignment_virtual_address[request_id] == NULL) {
1532*4882a593Smuzhiyun 		printk(KERN_WARNING "3w-xxxx: tw_scsiop_mode_sense(): Bad alignment virtual address.\n");
1533*4882a593Smuzhiyun 		return 1;
1534*4882a593Smuzhiyun 	}
1535*4882a593Smuzhiyun 
1536*4882a593Smuzhiyun 	param = (TW_Param *)tw_dev->alignment_virtual_address[request_id];
1537*4882a593Smuzhiyun 	memset(param, 0, sizeof(TW_Sector));
1538*4882a593Smuzhiyun 	param->table_id = TW_UNIT_INFORMATION_TABLE_BASE + tw_dev->srb[request_id]->device->id;
1539*4882a593Smuzhiyun 	param->parameter_id = 7; /* unit flags */
1540*4882a593Smuzhiyun 	param->parameter_size_bytes = 1;
1541*4882a593Smuzhiyun 	param_value = tw_dev->alignment_physical_address[request_id];
1542*4882a593Smuzhiyun 	if (param_value == 0) {
1543*4882a593Smuzhiyun 		printk(KERN_WARNING "3w-xxxx: tw_scsiop_mode_sense(): Bad alignment physical address.\n");
1544*4882a593Smuzhiyun 		return 1;
1545*4882a593Smuzhiyun 	}
1546*4882a593Smuzhiyun 
1547*4882a593Smuzhiyun 	command_packet->byte8.param.sgl[0].address = param_value;
1548*4882a593Smuzhiyun 	command_packet->byte8.param.sgl[0].length = sizeof(TW_Sector);
1549*4882a593Smuzhiyun 	command_que_value = tw_dev->command_packet_physical_address[request_id];
1550*4882a593Smuzhiyun 	if (command_que_value == 0) {
1551*4882a593Smuzhiyun 		printk(KERN_WARNING "3w-xxxx: tw_scsiop_mode_sense(): Bad command packet physical address.\n");
1552*4882a593Smuzhiyun 		return 1;
1553*4882a593Smuzhiyun 	}
1554*4882a593Smuzhiyun 
1555*4882a593Smuzhiyun 	/* Now try to post the command packet */
1556*4882a593Smuzhiyun 	tw_post_command_packet(tw_dev, request_id);
1557*4882a593Smuzhiyun 
1558*4882a593Smuzhiyun 	return 0;
1559*4882a593Smuzhiyun } /* End tw_scsiop_mode_sense() */
1560*4882a593Smuzhiyun 
1561*4882a593Smuzhiyun /* This function is called by the isr to complete a mode sense command */
tw_scsiop_mode_sense_complete(TW_Device_Extension * tw_dev,int request_id)1562*4882a593Smuzhiyun static int tw_scsiop_mode_sense_complete(TW_Device_Extension *tw_dev, int request_id)
1563*4882a593Smuzhiyun {
1564*4882a593Smuzhiyun 	TW_Param *param;
1565*4882a593Smuzhiyun 	unsigned char *flags;
1566*4882a593Smuzhiyun 	unsigned char request_buffer[8];
1567*4882a593Smuzhiyun 
1568*4882a593Smuzhiyun 	dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_mode_sense_complete()\n");
1569*4882a593Smuzhiyun 
1570*4882a593Smuzhiyun 	param = (TW_Param *)tw_dev->alignment_virtual_address[request_id];
1571*4882a593Smuzhiyun 	if (param == NULL) {
1572*4882a593Smuzhiyun 		printk(KERN_WARNING "3w-xxxx: tw_scsiop_mode_sense_complete(): Bad alignment virtual address.\n");
1573*4882a593Smuzhiyun 		return 1;
1574*4882a593Smuzhiyun 	}
1575*4882a593Smuzhiyun 	flags = (char *)&(param->data[0]);
1576*4882a593Smuzhiyun 	memset(request_buffer, 0, sizeof(request_buffer));
1577*4882a593Smuzhiyun 
1578*4882a593Smuzhiyun 	request_buffer[0] = 0xf;        /* mode data length */
1579*4882a593Smuzhiyun 	request_buffer[1] = 0;          /* default medium type */
1580*4882a593Smuzhiyun 	request_buffer[2] = 0x10;       /* dpo/fua support on */
1581*4882a593Smuzhiyun 	request_buffer[3] = 0;          /* no block descriptors */
1582*4882a593Smuzhiyun 	request_buffer[4] = 0x8;        /* caching page */
1583*4882a593Smuzhiyun 	request_buffer[5] = 0xa;        /* page length */
1584*4882a593Smuzhiyun 	if (*flags & 0x1)
1585*4882a593Smuzhiyun 		request_buffer[6] = 0x5;        /* WCE on, RCD on */
1586*4882a593Smuzhiyun 	else
1587*4882a593Smuzhiyun 		request_buffer[6] = 0x1;        /* WCE off, RCD on */
1588*4882a593Smuzhiyun 	tw_transfer_internal(tw_dev, request_id, request_buffer,
1589*4882a593Smuzhiyun 			     sizeof(request_buffer));
1590*4882a593Smuzhiyun 
1591*4882a593Smuzhiyun 	return 0;
1592*4882a593Smuzhiyun } /* End tw_scsiop_mode_sense_complete() */
1593*4882a593Smuzhiyun 
1594*4882a593Smuzhiyun /* This function handles scsi read_capacity commands */
tw_scsiop_read_capacity(TW_Device_Extension * tw_dev,int request_id)1595*4882a593Smuzhiyun static int tw_scsiop_read_capacity(TW_Device_Extension *tw_dev, int request_id)
1596*4882a593Smuzhiyun {
1597*4882a593Smuzhiyun 	TW_Param *param;
1598*4882a593Smuzhiyun 	TW_Command *command_packet;
1599*4882a593Smuzhiyun 	unsigned long command_que_value;
1600*4882a593Smuzhiyun 	unsigned long param_value;
1601*4882a593Smuzhiyun 
1602*4882a593Smuzhiyun 	dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_read_capacity()\n");
1603*4882a593Smuzhiyun 
1604*4882a593Smuzhiyun 	/* Initialize command packet */
1605*4882a593Smuzhiyun 	command_packet = (TW_Command *)tw_dev->command_packet_virtual_address[request_id];
1606*4882a593Smuzhiyun 
1607*4882a593Smuzhiyun 	if (command_packet == NULL) {
1608*4882a593Smuzhiyun 		dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_read_capacity(): Bad command packet virtual address.\n");
1609*4882a593Smuzhiyun 		return 1;
1610*4882a593Smuzhiyun 	}
1611*4882a593Smuzhiyun 	memset(command_packet, 0, sizeof(TW_Sector));
1612*4882a593Smuzhiyun 	command_packet->opcode__sgloffset = TW_OPSGL_IN(2, TW_OP_GET_PARAM);
1613*4882a593Smuzhiyun 	command_packet->size = 4;
1614*4882a593Smuzhiyun 	command_packet->request_id = request_id;
1615*4882a593Smuzhiyun 	command_packet->unit__hostid = TW_UNITHOST_IN(0, tw_dev->srb[request_id]->device->id);
1616*4882a593Smuzhiyun 	command_packet->status = 0;
1617*4882a593Smuzhiyun 	command_packet->flags = 0;
1618*4882a593Smuzhiyun 	command_packet->byte6.block_count = 1;
1619*4882a593Smuzhiyun 
1620*4882a593Smuzhiyun 	/* Now setup the param */
1621*4882a593Smuzhiyun 	if (tw_dev->alignment_virtual_address[request_id] == NULL) {
1622*4882a593Smuzhiyun 		dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_read_capacity(): Bad alignment virtual address.\n");
1623*4882a593Smuzhiyun 		return 1;
1624*4882a593Smuzhiyun 	}
1625*4882a593Smuzhiyun 	param = (TW_Param *)tw_dev->alignment_virtual_address[request_id];
1626*4882a593Smuzhiyun 	memset(param, 0, sizeof(TW_Sector));
1627*4882a593Smuzhiyun 	param->table_id = TW_UNIT_INFORMATION_TABLE_BASE +
1628*4882a593Smuzhiyun 	tw_dev->srb[request_id]->device->id;
1629*4882a593Smuzhiyun 	param->parameter_id = 4;	/* unitcapacity parameter */
1630*4882a593Smuzhiyun 	param->parameter_size_bytes = 4;
1631*4882a593Smuzhiyun 	param_value = tw_dev->alignment_physical_address[request_id];
1632*4882a593Smuzhiyun 	if (param_value == 0) {
1633*4882a593Smuzhiyun 		dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_read_capacity(): Bad alignment physical address.\n");
1634*4882a593Smuzhiyun 		return 1;
1635*4882a593Smuzhiyun 	}
1636*4882a593Smuzhiyun 
1637*4882a593Smuzhiyun 	command_packet->byte8.param.sgl[0].address = param_value;
1638*4882a593Smuzhiyun 	command_packet->byte8.param.sgl[0].length = sizeof(TW_Sector);
1639*4882a593Smuzhiyun 	command_que_value = tw_dev->command_packet_physical_address[request_id];
1640*4882a593Smuzhiyun 	if (command_que_value == 0) {
1641*4882a593Smuzhiyun 		dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_read_capacity(): Bad command packet physical address.\n");
1642*4882a593Smuzhiyun 		return 1;
1643*4882a593Smuzhiyun 	}
1644*4882a593Smuzhiyun 
1645*4882a593Smuzhiyun 	/* Now try to post the command to the board */
1646*4882a593Smuzhiyun 	tw_post_command_packet(tw_dev, request_id);
1647*4882a593Smuzhiyun 
1648*4882a593Smuzhiyun 	return 0;
1649*4882a593Smuzhiyun } /* End tw_scsiop_read_capacity() */
1650*4882a593Smuzhiyun 
1651*4882a593Smuzhiyun /* This function is called by the isr to complete a readcapacity command */
tw_scsiop_read_capacity_complete(TW_Device_Extension * tw_dev,int request_id)1652*4882a593Smuzhiyun static int tw_scsiop_read_capacity_complete(TW_Device_Extension *tw_dev, int request_id)
1653*4882a593Smuzhiyun {
1654*4882a593Smuzhiyun 	unsigned char *param_data;
1655*4882a593Smuzhiyun 	u32 capacity;
1656*4882a593Smuzhiyun 	char buff[8];
1657*4882a593Smuzhiyun 	TW_Param *param;
1658*4882a593Smuzhiyun 
1659*4882a593Smuzhiyun 	dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_read_capacity_complete()\n");
1660*4882a593Smuzhiyun 
1661*4882a593Smuzhiyun 	memset(buff, 0, sizeof(buff));
1662*4882a593Smuzhiyun 	param = (TW_Param *)tw_dev->alignment_virtual_address[request_id];
1663*4882a593Smuzhiyun 	if (param == NULL) {
1664*4882a593Smuzhiyun 		printk(KERN_WARNING "3w-xxxx: tw_scsiop_read_capacity_complete(): Bad alignment virtual address.\n");
1665*4882a593Smuzhiyun 		return 1;
1666*4882a593Smuzhiyun 	}
1667*4882a593Smuzhiyun 	param_data = &(param->data[0]);
1668*4882a593Smuzhiyun 
1669*4882a593Smuzhiyun 	capacity = (param_data[3] << 24) | (param_data[2] << 16) |
1670*4882a593Smuzhiyun 		   (param_data[1] << 8) | param_data[0];
1671*4882a593Smuzhiyun 
1672*4882a593Smuzhiyun 	/* Subtract one sector to fix get last sector ioctl */
1673*4882a593Smuzhiyun 	capacity -= 1;
1674*4882a593Smuzhiyun 
1675*4882a593Smuzhiyun 	dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_read_capacity_complete(): Capacity = 0x%x.\n", capacity);
1676*4882a593Smuzhiyun 
1677*4882a593Smuzhiyun 	/* Number of LBA's */
1678*4882a593Smuzhiyun 	buff[0] = (capacity >> 24);
1679*4882a593Smuzhiyun 	buff[1] = (capacity >> 16) & 0xff;
1680*4882a593Smuzhiyun 	buff[2] = (capacity >> 8) & 0xff;
1681*4882a593Smuzhiyun 	buff[3] = capacity & 0xff;
1682*4882a593Smuzhiyun 
1683*4882a593Smuzhiyun 	/* Block size in bytes (512) */
1684*4882a593Smuzhiyun 	buff[4] = (TW_BLOCK_SIZE >> 24);
1685*4882a593Smuzhiyun 	buff[5] = (TW_BLOCK_SIZE >> 16) & 0xff;
1686*4882a593Smuzhiyun 	buff[6] = (TW_BLOCK_SIZE >> 8) & 0xff;
1687*4882a593Smuzhiyun 	buff[7] = TW_BLOCK_SIZE & 0xff;
1688*4882a593Smuzhiyun 
1689*4882a593Smuzhiyun 	tw_transfer_internal(tw_dev, request_id, buff, sizeof(buff));
1690*4882a593Smuzhiyun 
1691*4882a593Smuzhiyun 	return 0;
1692*4882a593Smuzhiyun } /* End tw_scsiop_read_capacity_complete() */
1693*4882a593Smuzhiyun 
1694*4882a593Smuzhiyun /* This function handles scsi read or write commands */
tw_scsiop_read_write(TW_Device_Extension * tw_dev,int request_id)1695*4882a593Smuzhiyun static int tw_scsiop_read_write(TW_Device_Extension *tw_dev, int request_id)
1696*4882a593Smuzhiyun {
1697*4882a593Smuzhiyun 	TW_Command *command_packet;
1698*4882a593Smuzhiyun 	unsigned long command_que_value;
1699*4882a593Smuzhiyun 	u32 lba = 0x0, num_sectors = 0x0;
1700*4882a593Smuzhiyun 	int i, use_sg;
1701*4882a593Smuzhiyun 	struct scsi_cmnd *srb;
1702*4882a593Smuzhiyun 	struct scatterlist *sglist, *sg;
1703*4882a593Smuzhiyun 
1704*4882a593Smuzhiyun 	dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_read_write()\n");
1705*4882a593Smuzhiyun 
1706*4882a593Smuzhiyun 	srb = tw_dev->srb[request_id];
1707*4882a593Smuzhiyun 
1708*4882a593Smuzhiyun 	sglist = scsi_sglist(srb);
1709*4882a593Smuzhiyun 	if (!sglist) {
1710*4882a593Smuzhiyun 		printk(KERN_WARNING "3w-xxxx: tw_scsiop_read_write(): Request buffer NULL.\n");
1711*4882a593Smuzhiyun 		return 1;
1712*4882a593Smuzhiyun 	}
1713*4882a593Smuzhiyun 
1714*4882a593Smuzhiyun 	/* Initialize command packet */
1715*4882a593Smuzhiyun 	command_packet = (TW_Command *)tw_dev->command_packet_virtual_address[request_id];
1716*4882a593Smuzhiyun 	if (command_packet == NULL) {
1717*4882a593Smuzhiyun 		dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_read_write(): Bad command packet virtual address.\n");
1718*4882a593Smuzhiyun 		return 1;
1719*4882a593Smuzhiyun 	}
1720*4882a593Smuzhiyun 
1721*4882a593Smuzhiyun 	if (srb->cmnd[0] == READ_6 || srb->cmnd[0] == READ_10) {
1722*4882a593Smuzhiyun 		command_packet->opcode__sgloffset = TW_OPSGL_IN(3, TW_OP_READ);
1723*4882a593Smuzhiyun 	} else {
1724*4882a593Smuzhiyun 		command_packet->opcode__sgloffset = TW_OPSGL_IN(3, TW_OP_WRITE);
1725*4882a593Smuzhiyun 	}
1726*4882a593Smuzhiyun 
1727*4882a593Smuzhiyun 	command_packet->size = 3;
1728*4882a593Smuzhiyun 	command_packet->request_id = request_id;
1729*4882a593Smuzhiyun 	command_packet->unit__hostid = TW_UNITHOST_IN(0, srb->device->id);
1730*4882a593Smuzhiyun 	command_packet->status = 0;
1731*4882a593Smuzhiyun 	command_packet->flags = 0;
1732*4882a593Smuzhiyun 
1733*4882a593Smuzhiyun 	if (srb->cmnd[0] == WRITE_10) {
1734*4882a593Smuzhiyun 		if ((srb->cmnd[1] & 0x8) || (srb->cmnd[1] & 0x10))
1735*4882a593Smuzhiyun 			command_packet->flags = 1;
1736*4882a593Smuzhiyun 	}
1737*4882a593Smuzhiyun 
1738*4882a593Smuzhiyun 	if (srb->cmnd[0] == READ_6 || srb->cmnd[0] == WRITE_6) {
1739*4882a593Smuzhiyun 		lba = ((u32)srb->cmnd[1] << 16) | ((u32)srb->cmnd[2] << 8) | (u32)srb->cmnd[3];
1740*4882a593Smuzhiyun 		num_sectors = (u32)srb->cmnd[4];
1741*4882a593Smuzhiyun 	} else {
1742*4882a593Smuzhiyun 		lba = ((u32)srb->cmnd[2] << 24) | ((u32)srb->cmnd[3] << 16) | ((u32)srb->cmnd[4] << 8) | (u32)srb->cmnd[5];
1743*4882a593Smuzhiyun 		num_sectors = (u32)srb->cmnd[8] | ((u32)srb->cmnd[7] << 8);
1744*4882a593Smuzhiyun 	}
1745*4882a593Smuzhiyun 
1746*4882a593Smuzhiyun 	/* Update sector statistic */
1747*4882a593Smuzhiyun 	tw_dev->sector_count = num_sectors;
1748*4882a593Smuzhiyun 	if (tw_dev->sector_count > tw_dev->max_sector_count)
1749*4882a593Smuzhiyun 		tw_dev->max_sector_count = tw_dev->sector_count;
1750*4882a593Smuzhiyun 
1751*4882a593Smuzhiyun 	dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_read_write(): lba = 0x%x num_sectors = 0x%x\n", lba, num_sectors);
1752*4882a593Smuzhiyun 	command_packet->byte8.io.lba = lba;
1753*4882a593Smuzhiyun 	command_packet->byte6.block_count = num_sectors;
1754*4882a593Smuzhiyun 
1755*4882a593Smuzhiyun 	use_sg = scsi_dma_map(srb);
1756*4882a593Smuzhiyun 	if (use_sg <= 0)
1757*4882a593Smuzhiyun 		return 1;
1758*4882a593Smuzhiyun 
1759*4882a593Smuzhiyun 	scsi_for_each_sg(tw_dev->srb[request_id], sg, use_sg, i) {
1760*4882a593Smuzhiyun 		command_packet->byte8.io.sgl[i].address = sg_dma_address(sg);
1761*4882a593Smuzhiyun 		command_packet->byte8.io.sgl[i].length = sg_dma_len(sg);
1762*4882a593Smuzhiyun 		command_packet->size+=2;
1763*4882a593Smuzhiyun 	}
1764*4882a593Smuzhiyun 
1765*4882a593Smuzhiyun 	/* Update SG statistics */
1766*4882a593Smuzhiyun 	tw_dev->sgl_entries = scsi_sg_count(tw_dev->srb[request_id]);
1767*4882a593Smuzhiyun 	if (tw_dev->sgl_entries > tw_dev->max_sgl_entries)
1768*4882a593Smuzhiyun 		tw_dev->max_sgl_entries = tw_dev->sgl_entries;
1769*4882a593Smuzhiyun 
1770*4882a593Smuzhiyun 	command_que_value = tw_dev->command_packet_physical_address[request_id];
1771*4882a593Smuzhiyun 	if (command_que_value == 0) {
1772*4882a593Smuzhiyun 		dprintk(KERN_WARNING "3w-xxxx: tw_scsiop_read_write(): Bad command packet physical address.\n");
1773*4882a593Smuzhiyun 		return 1;
1774*4882a593Smuzhiyun 	}
1775*4882a593Smuzhiyun 
1776*4882a593Smuzhiyun 	/* Now try to post the command to the board */
1777*4882a593Smuzhiyun 	tw_post_command_packet(tw_dev, request_id);
1778*4882a593Smuzhiyun 
1779*4882a593Smuzhiyun 	return 0;
1780*4882a593Smuzhiyun } /* End tw_scsiop_read_write() */
1781*4882a593Smuzhiyun 
1782*4882a593Smuzhiyun /* This function will handle the request sense scsi command */
tw_scsiop_request_sense(TW_Device_Extension * tw_dev,int request_id)1783*4882a593Smuzhiyun static int tw_scsiop_request_sense(TW_Device_Extension *tw_dev, int request_id)
1784*4882a593Smuzhiyun {
1785*4882a593Smuzhiyun 	char request_buffer[18];
1786*4882a593Smuzhiyun 
1787*4882a593Smuzhiyun 	dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_request_sense()\n");
1788*4882a593Smuzhiyun 
1789*4882a593Smuzhiyun 	memset(request_buffer, 0, sizeof(request_buffer));
1790*4882a593Smuzhiyun 	request_buffer[0] = 0x70; /* Immediate fixed format */
1791*4882a593Smuzhiyun 	request_buffer[7] = 10;	/* minimum size per SPC: 18 bytes */
1792*4882a593Smuzhiyun 	/* leave all other fields zero, giving effectively NO_SENSE return */
1793*4882a593Smuzhiyun 	tw_transfer_internal(tw_dev, request_id, request_buffer,
1794*4882a593Smuzhiyun 			     sizeof(request_buffer));
1795*4882a593Smuzhiyun 
1796*4882a593Smuzhiyun 	tw_dev->state[request_id] = TW_S_COMPLETED;
1797*4882a593Smuzhiyun 	tw_state_request_finish(tw_dev, request_id);
1798*4882a593Smuzhiyun 
1799*4882a593Smuzhiyun 	/* If we got a request_sense, we probably want a reset, return error */
1800*4882a593Smuzhiyun 	tw_dev->srb[request_id]->result = (DID_ERROR << 16);
1801*4882a593Smuzhiyun 	tw_dev->srb[request_id]->scsi_done(tw_dev->srb[request_id]);
1802*4882a593Smuzhiyun 
1803*4882a593Smuzhiyun 	return 0;
1804*4882a593Smuzhiyun } /* End tw_scsiop_request_sense() */
1805*4882a593Smuzhiyun 
1806*4882a593Smuzhiyun /* This function will handle synchronize cache scsi command */
tw_scsiop_synchronize_cache(TW_Device_Extension * tw_dev,int request_id)1807*4882a593Smuzhiyun static int tw_scsiop_synchronize_cache(TW_Device_Extension *tw_dev, int request_id)
1808*4882a593Smuzhiyun {
1809*4882a593Smuzhiyun 	TW_Command *command_packet;
1810*4882a593Smuzhiyun 	unsigned long command_que_value;
1811*4882a593Smuzhiyun 
1812*4882a593Smuzhiyun 	dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_synchronize_cache()\n");
1813*4882a593Smuzhiyun 
1814*4882a593Smuzhiyun 	/* Send firmware flush command for this unit */
1815*4882a593Smuzhiyun 	command_packet = (TW_Command *)tw_dev->command_packet_virtual_address[request_id];
1816*4882a593Smuzhiyun 	if (command_packet == NULL) {
1817*4882a593Smuzhiyun 		printk(KERN_WARNING "3w-xxxx: tw_scsiop_synchronize_cache(): Bad command packet virtual address.\n");
1818*4882a593Smuzhiyun 		return 1;
1819*4882a593Smuzhiyun 	}
1820*4882a593Smuzhiyun 
1821*4882a593Smuzhiyun 	/* Setup the command packet */
1822*4882a593Smuzhiyun 	memset(command_packet, 0, sizeof(TW_Sector));
1823*4882a593Smuzhiyun 	command_packet->opcode__sgloffset = TW_OPSGL_IN(0, TW_OP_FLUSH_CACHE);
1824*4882a593Smuzhiyun 	command_packet->size = 2;
1825*4882a593Smuzhiyun 	command_packet->request_id = request_id;
1826*4882a593Smuzhiyun 	command_packet->unit__hostid = TW_UNITHOST_IN(0, tw_dev->srb[request_id]->device->id);
1827*4882a593Smuzhiyun 	command_packet->status = 0;
1828*4882a593Smuzhiyun 	command_packet->flags = 0;
1829*4882a593Smuzhiyun 	command_packet->byte6.parameter_count = 1;
1830*4882a593Smuzhiyun 	command_que_value = tw_dev->command_packet_physical_address[request_id];
1831*4882a593Smuzhiyun 	if (command_que_value == 0) {
1832*4882a593Smuzhiyun 		printk(KERN_WARNING "3w-xxxx: tw_scsiop_synchronize_cache(): Bad command packet physical address.\n");
1833*4882a593Smuzhiyun 		return 1;
1834*4882a593Smuzhiyun 	}
1835*4882a593Smuzhiyun 
1836*4882a593Smuzhiyun 	/* Now try to post the command packet */
1837*4882a593Smuzhiyun 	tw_post_command_packet(tw_dev, request_id);
1838*4882a593Smuzhiyun 
1839*4882a593Smuzhiyun 	return 0;
1840*4882a593Smuzhiyun } /* End tw_scsiop_synchronize_cache() */
1841*4882a593Smuzhiyun 
1842*4882a593Smuzhiyun /* This function will handle test unit ready scsi command */
tw_scsiop_test_unit_ready(TW_Device_Extension * tw_dev,int request_id)1843*4882a593Smuzhiyun static int tw_scsiop_test_unit_ready(TW_Device_Extension *tw_dev, int request_id)
1844*4882a593Smuzhiyun {
1845*4882a593Smuzhiyun 	TW_Param *param;
1846*4882a593Smuzhiyun 	TW_Command *command_packet;
1847*4882a593Smuzhiyun 	unsigned long command_que_value;
1848*4882a593Smuzhiyun 	unsigned long param_value;
1849*4882a593Smuzhiyun 
1850*4882a593Smuzhiyun 	dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_test_unit_ready()\n");
1851*4882a593Smuzhiyun 
1852*4882a593Smuzhiyun 	/* Initialize command packet */
1853*4882a593Smuzhiyun 	command_packet = (TW_Command *)tw_dev->command_packet_virtual_address[request_id];
1854*4882a593Smuzhiyun 	if (command_packet == NULL) {
1855*4882a593Smuzhiyun 		printk(KERN_WARNING "3w-xxxx: tw_scsiop_test_unit_ready(): Bad command packet virtual address.\n");
1856*4882a593Smuzhiyun 		return 1;
1857*4882a593Smuzhiyun 	}
1858*4882a593Smuzhiyun 	memset(command_packet, 0, sizeof(TW_Sector));
1859*4882a593Smuzhiyun 	command_packet->opcode__sgloffset = TW_OPSGL_IN(2, TW_OP_GET_PARAM);
1860*4882a593Smuzhiyun 	command_packet->size = 4;
1861*4882a593Smuzhiyun 	command_packet->request_id = request_id;
1862*4882a593Smuzhiyun 	command_packet->status = 0;
1863*4882a593Smuzhiyun 	command_packet->flags = 0;
1864*4882a593Smuzhiyun 	command_packet->byte6.parameter_count = 1;
1865*4882a593Smuzhiyun 
1866*4882a593Smuzhiyun 	/* Now setup the param */
1867*4882a593Smuzhiyun 	if (tw_dev->alignment_virtual_address[request_id] == NULL) {
1868*4882a593Smuzhiyun 		printk(KERN_WARNING "3w-xxxx: tw_scsiop_test_unit_ready(): Bad alignment virtual address.\n");
1869*4882a593Smuzhiyun 		return 1;
1870*4882a593Smuzhiyun 	}
1871*4882a593Smuzhiyun 	param = (TW_Param *)tw_dev->alignment_virtual_address[request_id];
1872*4882a593Smuzhiyun 	memset(param, 0, sizeof(TW_Sector));
1873*4882a593Smuzhiyun 	param->table_id = 3;	 /* unit summary table */
1874*4882a593Smuzhiyun 	param->parameter_id = 3; /* unitsstatus parameter */
1875*4882a593Smuzhiyun 	param->parameter_size_bytes = TW_MAX_UNITS;
1876*4882a593Smuzhiyun 	param_value = tw_dev->alignment_physical_address[request_id];
1877*4882a593Smuzhiyun 	if (param_value == 0) {
1878*4882a593Smuzhiyun 		printk(KERN_WARNING "3w-xxxx: tw_scsiop_test_unit_ready(): Bad alignment physical address.\n");
1879*4882a593Smuzhiyun 		return 1;
1880*4882a593Smuzhiyun 	}
1881*4882a593Smuzhiyun 
1882*4882a593Smuzhiyun 	command_packet->byte8.param.sgl[0].address = param_value;
1883*4882a593Smuzhiyun 	command_packet->byte8.param.sgl[0].length = sizeof(TW_Sector);
1884*4882a593Smuzhiyun 	command_que_value = tw_dev->command_packet_physical_address[request_id];
1885*4882a593Smuzhiyun 	if (command_que_value == 0) {
1886*4882a593Smuzhiyun 		printk(KERN_WARNING "3w-xxxx: tw_scsiop_test_unit_ready(): Bad command packet physical address.\n");
1887*4882a593Smuzhiyun 		return 1;
1888*4882a593Smuzhiyun 	}
1889*4882a593Smuzhiyun 
1890*4882a593Smuzhiyun 	/* Now try to post the command packet */
1891*4882a593Smuzhiyun 	tw_post_command_packet(tw_dev, request_id);
1892*4882a593Smuzhiyun 
1893*4882a593Smuzhiyun 	return 0;
1894*4882a593Smuzhiyun } /* End tw_scsiop_test_unit_ready() */
1895*4882a593Smuzhiyun 
1896*4882a593Smuzhiyun /* This function is called by the isr to complete a testunitready command */
tw_scsiop_test_unit_ready_complete(TW_Device_Extension * tw_dev,int request_id)1897*4882a593Smuzhiyun static int tw_scsiop_test_unit_ready_complete(TW_Device_Extension *tw_dev, int request_id)
1898*4882a593Smuzhiyun {
1899*4882a593Smuzhiyun 	unsigned char *is_unit_present;
1900*4882a593Smuzhiyun 	TW_Param *param;
1901*4882a593Smuzhiyun 
1902*4882a593Smuzhiyun 	dprintk(KERN_WARNING "3w-xxxx: tw_scsiop_test_unit_ready_complete()\n");
1903*4882a593Smuzhiyun 
1904*4882a593Smuzhiyun 	param = (TW_Param *)tw_dev->alignment_virtual_address[request_id];
1905*4882a593Smuzhiyun 	if (param == NULL) {
1906*4882a593Smuzhiyun 		printk(KERN_WARNING "3w-xxxx: tw_scsiop_test_unit_ready_complete(): Bad alignment virtual address.\n");
1907*4882a593Smuzhiyun 		return 1;
1908*4882a593Smuzhiyun 	}
1909*4882a593Smuzhiyun 	is_unit_present = &(param->data[0]);
1910*4882a593Smuzhiyun 
1911*4882a593Smuzhiyun 	if (is_unit_present[tw_dev->srb[request_id]->device->id] & TW_UNIT_ONLINE) {
1912*4882a593Smuzhiyun 		tw_dev->is_unit_present[tw_dev->srb[request_id]->device->id] = 1;
1913*4882a593Smuzhiyun 	} else {
1914*4882a593Smuzhiyun 		tw_dev->is_unit_present[tw_dev->srb[request_id]->device->id] = 0;
1915*4882a593Smuzhiyun 		tw_dev->srb[request_id]->result = (DID_BAD_TARGET << 16);
1916*4882a593Smuzhiyun 		return TW_ISR_DONT_RESULT;
1917*4882a593Smuzhiyun 	}
1918*4882a593Smuzhiyun 
1919*4882a593Smuzhiyun 	return 0;
1920*4882a593Smuzhiyun } /* End tw_scsiop_test_unit_ready_complete() */
1921*4882a593Smuzhiyun 
1922*4882a593Smuzhiyun /* This is the main scsi queue function to handle scsi opcodes */
tw_scsi_queue_lck(struct scsi_cmnd * SCpnt,void (* done)(struct scsi_cmnd *))1923*4882a593Smuzhiyun static int tw_scsi_queue_lck(struct scsi_cmnd *SCpnt, void (*done)(struct scsi_cmnd *))
1924*4882a593Smuzhiyun {
1925*4882a593Smuzhiyun 	unsigned char *command = SCpnt->cmnd;
1926*4882a593Smuzhiyun 	int request_id = 0;
1927*4882a593Smuzhiyun 	int retval = 1;
1928*4882a593Smuzhiyun 	TW_Device_Extension *tw_dev = (TW_Device_Extension *)SCpnt->device->host->hostdata;
1929*4882a593Smuzhiyun 
1930*4882a593Smuzhiyun 	/* If we are resetting due to timed out ioctl, report as busy */
1931*4882a593Smuzhiyun 	if (test_bit(TW_IN_RESET, &tw_dev->flags))
1932*4882a593Smuzhiyun 		return SCSI_MLQUEUE_HOST_BUSY;
1933*4882a593Smuzhiyun 
1934*4882a593Smuzhiyun 	/* Save done function into struct scsi_cmnd */
1935*4882a593Smuzhiyun 	SCpnt->scsi_done = done;
1936*4882a593Smuzhiyun 
1937*4882a593Smuzhiyun 	/* Queue the command and get a request id */
1938*4882a593Smuzhiyun 	tw_state_request_start(tw_dev, &request_id);
1939*4882a593Smuzhiyun 
1940*4882a593Smuzhiyun 	/* Save the scsi command for use by the ISR */
1941*4882a593Smuzhiyun 	tw_dev->srb[request_id] = SCpnt;
1942*4882a593Smuzhiyun 
1943*4882a593Smuzhiyun 	switch (*command) {
1944*4882a593Smuzhiyun 		case READ_10:
1945*4882a593Smuzhiyun 		case READ_6:
1946*4882a593Smuzhiyun 		case WRITE_10:
1947*4882a593Smuzhiyun 		case WRITE_6:
1948*4882a593Smuzhiyun 			dprintk(KERN_NOTICE "3w-xxxx: tw_scsi_queue(): caught READ/WRITE.\n");
1949*4882a593Smuzhiyun 			retval = tw_scsiop_read_write(tw_dev, request_id);
1950*4882a593Smuzhiyun 			break;
1951*4882a593Smuzhiyun 		case TEST_UNIT_READY:
1952*4882a593Smuzhiyun 			dprintk(KERN_NOTICE "3w-xxxx: tw_scsi_queue(): caught TEST_UNIT_READY.\n");
1953*4882a593Smuzhiyun 			retval = tw_scsiop_test_unit_ready(tw_dev, request_id);
1954*4882a593Smuzhiyun 			break;
1955*4882a593Smuzhiyun 		case INQUIRY:
1956*4882a593Smuzhiyun 			dprintk(KERN_NOTICE "3w-xxxx: tw_scsi_queue(): caught INQUIRY.\n");
1957*4882a593Smuzhiyun 			retval = tw_scsiop_inquiry(tw_dev, request_id);
1958*4882a593Smuzhiyun 			break;
1959*4882a593Smuzhiyun 		case READ_CAPACITY:
1960*4882a593Smuzhiyun 			dprintk(KERN_NOTICE "3w-xxxx: tw_scsi_queue(): caught READ_CAPACITY.\n");
1961*4882a593Smuzhiyun 			retval = tw_scsiop_read_capacity(tw_dev, request_id);
1962*4882a593Smuzhiyun 			break;
1963*4882a593Smuzhiyun 	        case REQUEST_SENSE:
1964*4882a593Smuzhiyun 		        dprintk(KERN_NOTICE "3w-xxxx: tw_scsi_queue(): caught REQUEST_SENSE.\n");
1965*4882a593Smuzhiyun 		        retval = tw_scsiop_request_sense(tw_dev, request_id);
1966*4882a593Smuzhiyun 		        break;
1967*4882a593Smuzhiyun 		case MODE_SENSE:
1968*4882a593Smuzhiyun 			dprintk(KERN_NOTICE "3w-xxxx: tw_scsi_queue(): caught MODE_SENSE.\n");
1969*4882a593Smuzhiyun 			retval = tw_scsiop_mode_sense(tw_dev, request_id);
1970*4882a593Smuzhiyun 			break;
1971*4882a593Smuzhiyun 		case SYNCHRONIZE_CACHE:
1972*4882a593Smuzhiyun 			dprintk(KERN_NOTICE "3w-xxxx: tw_scsi_queue(): caught SYNCHRONIZE_CACHE.\n");
1973*4882a593Smuzhiyun 			retval = tw_scsiop_synchronize_cache(tw_dev, request_id);
1974*4882a593Smuzhiyun 			break;
1975*4882a593Smuzhiyun 		case TW_IOCTL:
1976*4882a593Smuzhiyun 			printk(KERN_WARNING "3w-xxxx: SCSI_IOCTL_SEND_COMMAND deprecated, please update your 3ware tools.\n");
1977*4882a593Smuzhiyun 			break;
1978*4882a593Smuzhiyun 		default:
1979*4882a593Smuzhiyun 			printk(KERN_NOTICE "3w-xxxx: scsi%d: Unknown scsi opcode: 0x%x\n", tw_dev->host->host_no, *command);
1980*4882a593Smuzhiyun 			tw_dev->state[request_id] = TW_S_COMPLETED;
1981*4882a593Smuzhiyun 			tw_state_request_finish(tw_dev, request_id);
1982*4882a593Smuzhiyun 			SCpnt->result = (DRIVER_SENSE << 24) | SAM_STAT_CHECK_CONDITION;
1983*4882a593Smuzhiyun 			scsi_build_sense_buffer(1, SCpnt->sense_buffer, ILLEGAL_REQUEST, 0x20, 0);
1984*4882a593Smuzhiyun 			done(SCpnt);
1985*4882a593Smuzhiyun 			retval = 0;
1986*4882a593Smuzhiyun 	}
1987*4882a593Smuzhiyun 	if (retval) {
1988*4882a593Smuzhiyun 		tw_dev->state[request_id] = TW_S_COMPLETED;
1989*4882a593Smuzhiyun 		tw_state_request_finish(tw_dev, request_id);
1990*4882a593Smuzhiyun 		SCpnt->result = (DID_ERROR << 16);
1991*4882a593Smuzhiyun 		done(SCpnt);
1992*4882a593Smuzhiyun 		retval = 0;
1993*4882a593Smuzhiyun 	}
1994*4882a593Smuzhiyun 	return retval;
1995*4882a593Smuzhiyun } /* End tw_scsi_queue() */
1996*4882a593Smuzhiyun 
DEF_SCSI_QCMD(tw_scsi_queue)1997*4882a593Smuzhiyun static DEF_SCSI_QCMD(tw_scsi_queue)
1998*4882a593Smuzhiyun 
1999*4882a593Smuzhiyun /* This function is the interrupt service routine */
2000*4882a593Smuzhiyun static irqreturn_t tw_interrupt(int irq, void *dev_instance)
2001*4882a593Smuzhiyun {
2002*4882a593Smuzhiyun 	int request_id;
2003*4882a593Smuzhiyun 	u32 status_reg_value;
2004*4882a593Smuzhiyun 	TW_Device_Extension *tw_dev = (TW_Device_Extension *)dev_instance;
2005*4882a593Smuzhiyun 	TW_Response_Queue response_que;
2006*4882a593Smuzhiyun 	int error = 0, retval = 0;
2007*4882a593Smuzhiyun 	TW_Command *command_packet;
2008*4882a593Smuzhiyun 	int handled = 0;
2009*4882a593Smuzhiyun 
2010*4882a593Smuzhiyun 	/* Get the host lock for io completions */
2011*4882a593Smuzhiyun 	spin_lock(tw_dev->host->host_lock);
2012*4882a593Smuzhiyun 
2013*4882a593Smuzhiyun 	/* Read the registers */
2014*4882a593Smuzhiyun 	status_reg_value = inl(TW_STATUS_REG_ADDR(tw_dev));
2015*4882a593Smuzhiyun 
2016*4882a593Smuzhiyun 	/* Check if this is our interrupt, otherwise bail */
2017*4882a593Smuzhiyun 	if (!(status_reg_value & TW_STATUS_VALID_INTERRUPT))
2018*4882a593Smuzhiyun 		goto tw_interrupt_bail;
2019*4882a593Smuzhiyun 
2020*4882a593Smuzhiyun 	handled = 1;
2021*4882a593Smuzhiyun 
2022*4882a593Smuzhiyun 	/* If we are resetting, bail */
2023*4882a593Smuzhiyun 	if (test_bit(TW_IN_RESET, &tw_dev->flags))
2024*4882a593Smuzhiyun 		goto tw_interrupt_bail;
2025*4882a593Smuzhiyun 
2026*4882a593Smuzhiyun 	/* Check controller for errors */
2027*4882a593Smuzhiyun 	if (tw_check_bits(status_reg_value)) {
2028*4882a593Smuzhiyun 		dprintk(KERN_WARNING "3w-xxxx: tw_interrupt(): Unexpected bits.\n");
2029*4882a593Smuzhiyun 		if (tw_decode_bits(tw_dev, status_reg_value, 1)) {
2030*4882a593Smuzhiyun 			TW_CLEAR_ALL_INTERRUPTS(tw_dev);
2031*4882a593Smuzhiyun 			goto tw_interrupt_bail;
2032*4882a593Smuzhiyun 		}
2033*4882a593Smuzhiyun 	}
2034*4882a593Smuzhiyun 
2035*4882a593Smuzhiyun 	/* Handle host interrupt */
2036*4882a593Smuzhiyun 	if (status_reg_value & TW_STATUS_HOST_INTERRUPT) {
2037*4882a593Smuzhiyun 		dprintk(KERN_NOTICE "3w-xxxx: tw_interrupt(): Received host interrupt.\n");
2038*4882a593Smuzhiyun 		TW_CLEAR_HOST_INTERRUPT(tw_dev);
2039*4882a593Smuzhiyun 	}
2040*4882a593Smuzhiyun 
2041*4882a593Smuzhiyun 	/* Handle attention interrupt */
2042*4882a593Smuzhiyun 	if (status_reg_value & TW_STATUS_ATTENTION_INTERRUPT) {
2043*4882a593Smuzhiyun 		dprintk(KERN_NOTICE "3w-xxxx: tw_interrupt(): Received attention interrupt.\n");
2044*4882a593Smuzhiyun 		TW_CLEAR_ATTENTION_INTERRUPT(tw_dev);
2045*4882a593Smuzhiyun 		tw_state_request_start(tw_dev, &request_id);
2046*4882a593Smuzhiyun 		error = tw_aen_read_queue(tw_dev, request_id);
2047*4882a593Smuzhiyun 		if (error) {
2048*4882a593Smuzhiyun 			printk(KERN_WARNING "3w-xxxx: scsi%d: Error reading aen queue.\n", tw_dev->host->host_no);
2049*4882a593Smuzhiyun 			tw_dev->state[request_id] = TW_S_COMPLETED;
2050*4882a593Smuzhiyun 			tw_state_request_finish(tw_dev, request_id);
2051*4882a593Smuzhiyun 		}
2052*4882a593Smuzhiyun 	}
2053*4882a593Smuzhiyun 
2054*4882a593Smuzhiyun 	/* Handle command interrupt */
2055*4882a593Smuzhiyun 	if (status_reg_value & TW_STATUS_COMMAND_INTERRUPT) {
2056*4882a593Smuzhiyun 		/* Drain as many pending commands as we can */
2057*4882a593Smuzhiyun 		while (tw_dev->pending_request_count > 0) {
2058*4882a593Smuzhiyun 			request_id = tw_dev->pending_queue[tw_dev->pending_head];
2059*4882a593Smuzhiyun 			if (tw_dev->state[request_id] != TW_S_PENDING) {
2060*4882a593Smuzhiyun 				printk(KERN_WARNING "3w-xxxx: scsi%d: Found request id that wasn't pending.\n", tw_dev->host->host_no);
2061*4882a593Smuzhiyun 				break;
2062*4882a593Smuzhiyun 			}
2063*4882a593Smuzhiyun 			if (tw_post_command_packet(tw_dev, request_id)==0) {
2064*4882a593Smuzhiyun 				if (tw_dev->pending_head == TW_Q_LENGTH-1) {
2065*4882a593Smuzhiyun 					tw_dev->pending_head = TW_Q_START;
2066*4882a593Smuzhiyun 				} else {
2067*4882a593Smuzhiyun 					tw_dev->pending_head = tw_dev->pending_head + 1;
2068*4882a593Smuzhiyun 				}
2069*4882a593Smuzhiyun 				tw_dev->pending_request_count--;
2070*4882a593Smuzhiyun 			} else {
2071*4882a593Smuzhiyun 				/* If we get here, we will continue re-posting on the next command interrupt */
2072*4882a593Smuzhiyun 				break;
2073*4882a593Smuzhiyun 			}
2074*4882a593Smuzhiyun 		}
2075*4882a593Smuzhiyun 		/* If there are no more pending requests, we mask command interrupt */
2076*4882a593Smuzhiyun 		if (tw_dev->pending_request_count == 0)
2077*4882a593Smuzhiyun 			TW_MASK_COMMAND_INTERRUPT(tw_dev);
2078*4882a593Smuzhiyun 	}
2079*4882a593Smuzhiyun 
2080*4882a593Smuzhiyun 	/* Handle response interrupt */
2081*4882a593Smuzhiyun 	if (status_reg_value & TW_STATUS_RESPONSE_INTERRUPT) {
2082*4882a593Smuzhiyun 		/* Drain the response queue from the board */
2083*4882a593Smuzhiyun 		while ((status_reg_value & TW_STATUS_RESPONSE_QUEUE_EMPTY) == 0) {
2084*4882a593Smuzhiyun 			/* Read response queue register */
2085*4882a593Smuzhiyun 			response_que.value = inl(TW_RESPONSE_QUEUE_REG_ADDR(tw_dev));
2086*4882a593Smuzhiyun 			request_id = TW_RESID_OUT(response_que.response_id);
2087*4882a593Smuzhiyun 			command_packet = (TW_Command *)tw_dev->command_packet_virtual_address[request_id];
2088*4882a593Smuzhiyun 			error = 0;
2089*4882a593Smuzhiyun 
2090*4882a593Smuzhiyun 			/* Check for bad response */
2091*4882a593Smuzhiyun 			if (command_packet->status != 0) {
2092*4882a593Smuzhiyun 				/* If internal command, don't error, don't fill sense */
2093*4882a593Smuzhiyun 				if (tw_dev->srb[request_id] == NULL) {
2094*4882a593Smuzhiyun 					tw_decode_sense(tw_dev, request_id, 0);
2095*4882a593Smuzhiyun 				} else {
2096*4882a593Smuzhiyun 					error = tw_decode_sense(tw_dev, request_id, 1);
2097*4882a593Smuzhiyun 				}
2098*4882a593Smuzhiyun 			}
2099*4882a593Smuzhiyun 
2100*4882a593Smuzhiyun 			/* Check for correct state */
2101*4882a593Smuzhiyun 			if (tw_dev->state[request_id] != TW_S_POSTED) {
2102*4882a593Smuzhiyun 				if (tw_dev->srb[request_id] != NULL) {
2103*4882a593Smuzhiyun 					printk(KERN_WARNING "3w-xxxx: scsi%d: Received a request id that wasn't posted.\n", tw_dev->host->host_no);
2104*4882a593Smuzhiyun 					error = 1;
2105*4882a593Smuzhiyun 				}
2106*4882a593Smuzhiyun 			}
2107*4882a593Smuzhiyun 
2108*4882a593Smuzhiyun 			dprintk(KERN_NOTICE "3w-xxxx: tw_interrupt(): Response queue request id: %d.\n", request_id);
2109*4882a593Smuzhiyun 
2110*4882a593Smuzhiyun 			/* Check for internal command completion */
2111*4882a593Smuzhiyun 			if (tw_dev->srb[request_id] == NULL) {
2112*4882a593Smuzhiyun 				dprintk(KERN_WARNING "3w-xxxx: tw_interrupt(): Found internally posted command.\n");
2113*4882a593Smuzhiyun 				/* Check for chrdev ioctl completion */
2114*4882a593Smuzhiyun 				if (request_id != tw_dev->chrdev_request_id) {
2115*4882a593Smuzhiyun 					retval = tw_aen_complete(tw_dev, request_id);
2116*4882a593Smuzhiyun 					if (retval) {
2117*4882a593Smuzhiyun 						printk(KERN_WARNING "3w-xxxx: scsi%d: Error completing aen.\n", tw_dev->host->host_no);
2118*4882a593Smuzhiyun 					}
2119*4882a593Smuzhiyun 				} else {
2120*4882a593Smuzhiyun 					tw_dev->chrdev_request_id = TW_IOCTL_CHRDEV_FREE;
2121*4882a593Smuzhiyun 					wake_up(&tw_dev->ioctl_wqueue);
2122*4882a593Smuzhiyun 				}
2123*4882a593Smuzhiyun 			} else {
2124*4882a593Smuzhiyun 				switch (tw_dev->srb[request_id]->cmnd[0]) {
2125*4882a593Smuzhiyun 				case READ_10:
2126*4882a593Smuzhiyun 				case READ_6:
2127*4882a593Smuzhiyun 					dprintk(KERN_NOTICE "3w-xxxx: tw_interrupt(): caught READ_10/READ_6\n");
2128*4882a593Smuzhiyun 					break;
2129*4882a593Smuzhiyun 				case WRITE_10:
2130*4882a593Smuzhiyun 				case WRITE_6:
2131*4882a593Smuzhiyun 					dprintk(KERN_NOTICE "3w-xxxx: tw_interrupt(): caught WRITE_10/WRITE_6\n");
2132*4882a593Smuzhiyun 					break;
2133*4882a593Smuzhiyun 				case TEST_UNIT_READY:
2134*4882a593Smuzhiyun 					dprintk(KERN_NOTICE "3w-xxxx: tw_interrupt(): caught TEST_UNIT_READY\n");
2135*4882a593Smuzhiyun 					error = tw_scsiop_test_unit_ready_complete(tw_dev, request_id);
2136*4882a593Smuzhiyun 					break;
2137*4882a593Smuzhiyun 				case INQUIRY:
2138*4882a593Smuzhiyun 					dprintk(KERN_NOTICE "3w-xxxx: tw_interrupt(): caught INQUIRY\n");
2139*4882a593Smuzhiyun 					error = tw_scsiop_inquiry_complete(tw_dev, request_id);
2140*4882a593Smuzhiyun 					break;
2141*4882a593Smuzhiyun 				case READ_CAPACITY:
2142*4882a593Smuzhiyun 					dprintk(KERN_NOTICE "3w-xxxx: tw_interrupt(): caught READ_CAPACITY\n");
2143*4882a593Smuzhiyun 					error = tw_scsiop_read_capacity_complete(tw_dev, request_id);
2144*4882a593Smuzhiyun 					break;
2145*4882a593Smuzhiyun 				case MODE_SENSE:
2146*4882a593Smuzhiyun 					dprintk(KERN_NOTICE "3w-xxxx: tw_interrupt(): caught MODE_SENSE\n");
2147*4882a593Smuzhiyun 					error = tw_scsiop_mode_sense_complete(tw_dev, request_id);
2148*4882a593Smuzhiyun 					break;
2149*4882a593Smuzhiyun 				case SYNCHRONIZE_CACHE:
2150*4882a593Smuzhiyun 					dprintk(KERN_NOTICE "3w-xxxx: tw_interrupt(): caught SYNCHRONIZE_CACHE\n");
2151*4882a593Smuzhiyun 					break;
2152*4882a593Smuzhiyun 				default:
2153*4882a593Smuzhiyun 					printk(KERN_WARNING "3w-xxxx: case slip in tw_interrupt()\n");
2154*4882a593Smuzhiyun 					error = 1;
2155*4882a593Smuzhiyun 				}
2156*4882a593Smuzhiyun 
2157*4882a593Smuzhiyun 				/* If no error command was a success */
2158*4882a593Smuzhiyun 				if (error == 0) {
2159*4882a593Smuzhiyun 					tw_dev->srb[request_id]->result = (DID_OK << 16);
2160*4882a593Smuzhiyun 				}
2161*4882a593Smuzhiyun 
2162*4882a593Smuzhiyun 				/* If error, command failed */
2163*4882a593Smuzhiyun 				if (error == 1) {
2164*4882a593Smuzhiyun 					/* Ask for a host reset */
2165*4882a593Smuzhiyun 					tw_dev->srb[request_id]->result = (DID_OK << 16) | (CHECK_CONDITION << 1);
2166*4882a593Smuzhiyun 				}
2167*4882a593Smuzhiyun 
2168*4882a593Smuzhiyun 				/* Now complete the io */
2169*4882a593Smuzhiyun 				if ((error != TW_ISR_DONT_COMPLETE)) {
2170*4882a593Smuzhiyun 					scsi_dma_unmap(tw_dev->srb[request_id]);
2171*4882a593Smuzhiyun 					tw_dev->srb[request_id]->scsi_done(tw_dev->srb[request_id]);
2172*4882a593Smuzhiyun 					tw_dev->state[request_id] = TW_S_COMPLETED;
2173*4882a593Smuzhiyun 					tw_state_request_finish(tw_dev, request_id);
2174*4882a593Smuzhiyun 					tw_dev->posted_request_count--;
2175*4882a593Smuzhiyun 				}
2176*4882a593Smuzhiyun 			}
2177*4882a593Smuzhiyun 
2178*4882a593Smuzhiyun 			/* Check for valid status after each drain */
2179*4882a593Smuzhiyun 			status_reg_value = inl(TW_STATUS_REG_ADDR(tw_dev));
2180*4882a593Smuzhiyun 			if (tw_check_bits(status_reg_value)) {
2181*4882a593Smuzhiyun 				dprintk(KERN_WARNING "3w-xxxx: tw_interrupt(): Unexpected bits.\n");
2182*4882a593Smuzhiyun 				if (tw_decode_bits(tw_dev, status_reg_value, 1)) {
2183*4882a593Smuzhiyun 					TW_CLEAR_ALL_INTERRUPTS(tw_dev);
2184*4882a593Smuzhiyun 					goto tw_interrupt_bail;
2185*4882a593Smuzhiyun 				}
2186*4882a593Smuzhiyun 			}
2187*4882a593Smuzhiyun 		}
2188*4882a593Smuzhiyun 	}
2189*4882a593Smuzhiyun 
2190*4882a593Smuzhiyun tw_interrupt_bail:
2191*4882a593Smuzhiyun 	spin_unlock(tw_dev->host->host_lock);
2192*4882a593Smuzhiyun 	return IRQ_RETVAL(handled);
2193*4882a593Smuzhiyun } /* End tw_interrupt() */
2194*4882a593Smuzhiyun 
2195*4882a593Smuzhiyun /* This function tells the controller to shut down */
__tw_shutdown(TW_Device_Extension * tw_dev)2196*4882a593Smuzhiyun static void __tw_shutdown(TW_Device_Extension *tw_dev)
2197*4882a593Smuzhiyun {
2198*4882a593Smuzhiyun 	/* Disable interrupts */
2199*4882a593Smuzhiyun 	TW_DISABLE_INTERRUPTS(tw_dev);
2200*4882a593Smuzhiyun 
2201*4882a593Smuzhiyun 	/* Free up the IRQ */
2202*4882a593Smuzhiyun 	free_irq(tw_dev->tw_pci_dev->irq, tw_dev);
2203*4882a593Smuzhiyun 
2204*4882a593Smuzhiyun 	printk(KERN_WARNING "3w-xxxx: Shutting down host %d.\n", tw_dev->host->host_no);
2205*4882a593Smuzhiyun 
2206*4882a593Smuzhiyun 	/* Tell the card we are shutting down */
2207*4882a593Smuzhiyun 	if (tw_initconnection(tw_dev, 1)) {
2208*4882a593Smuzhiyun 		printk(KERN_WARNING "3w-xxxx: Connection shutdown failed.\n");
2209*4882a593Smuzhiyun 	} else {
2210*4882a593Smuzhiyun 		printk(KERN_WARNING "3w-xxxx: Shutdown complete.\n");
2211*4882a593Smuzhiyun 	}
2212*4882a593Smuzhiyun 
2213*4882a593Smuzhiyun 	/* Clear all interrupts just before exit */
2214*4882a593Smuzhiyun 	TW_ENABLE_AND_CLEAR_INTERRUPTS(tw_dev);
2215*4882a593Smuzhiyun } /* End __tw_shutdown() */
2216*4882a593Smuzhiyun 
2217*4882a593Smuzhiyun /* Wrapper for __tw_shutdown */
tw_shutdown(struct pci_dev * pdev)2218*4882a593Smuzhiyun static void tw_shutdown(struct pci_dev *pdev)
2219*4882a593Smuzhiyun {
2220*4882a593Smuzhiyun 	struct Scsi_Host *host = pci_get_drvdata(pdev);
2221*4882a593Smuzhiyun 	TW_Device_Extension *tw_dev = (TW_Device_Extension *)host->hostdata;
2222*4882a593Smuzhiyun 
2223*4882a593Smuzhiyun 	__tw_shutdown(tw_dev);
2224*4882a593Smuzhiyun } /* End tw_shutdown() */
2225*4882a593Smuzhiyun 
2226*4882a593Smuzhiyun /* This function gets called when a disk is coming online */
tw_slave_configure(struct scsi_device * sdev)2227*4882a593Smuzhiyun static int tw_slave_configure(struct scsi_device *sdev)
2228*4882a593Smuzhiyun {
2229*4882a593Smuzhiyun 	/* Force 60 second timeout */
2230*4882a593Smuzhiyun 	blk_queue_rq_timeout(sdev->request_queue, 60 * HZ);
2231*4882a593Smuzhiyun 
2232*4882a593Smuzhiyun 	return 0;
2233*4882a593Smuzhiyun } /* End tw_slave_configure() */
2234*4882a593Smuzhiyun 
2235*4882a593Smuzhiyun static struct scsi_host_template driver_template = {
2236*4882a593Smuzhiyun 	.module			= THIS_MODULE,
2237*4882a593Smuzhiyun 	.name			= "3ware Storage Controller",
2238*4882a593Smuzhiyun 	.queuecommand		= tw_scsi_queue,
2239*4882a593Smuzhiyun 	.eh_host_reset_handler	= tw_scsi_eh_reset,
2240*4882a593Smuzhiyun 	.bios_param		= tw_scsi_biosparam,
2241*4882a593Smuzhiyun 	.change_queue_depth	= scsi_change_queue_depth,
2242*4882a593Smuzhiyun 	.can_queue		= TW_Q_LENGTH-2,
2243*4882a593Smuzhiyun 	.slave_configure	= tw_slave_configure,
2244*4882a593Smuzhiyun 	.this_id		= -1,
2245*4882a593Smuzhiyun 	.sg_tablesize		= TW_MAX_SGL_LENGTH,
2246*4882a593Smuzhiyun 	.max_sectors		= TW_MAX_SECTORS,
2247*4882a593Smuzhiyun 	.cmd_per_lun		= TW_MAX_CMDS_PER_LUN,
2248*4882a593Smuzhiyun 	.shost_attrs		= tw_host_attrs,
2249*4882a593Smuzhiyun 	.emulated		= 1,
2250*4882a593Smuzhiyun 	.no_write_same		= 1,
2251*4882a593Smuzhiyun };
2252*4882a593Smuzhiyun 
2253*4882a593Smuzhiyun /* This function will probe and initialize a card */
tw_probe(struct pci_dev * pdev,const struct pci_device_id * dev_id)2254*4882a593Smuzhiyun static int tw_probe(struct pci_dev *pdev, const struct pci_device_id *dev_id)
2255*4882a593Smuzhiyun {
2256*4882a593Smuzhiyun 	struct Scsi_Host *host = NULL;
2257*4882a593Smuzhiyun 	TW_Device_Extension *tw_dev;
2258*4882a593Smuzhiyun 	int retval = -ENODEV;
2259*4882a593Smuzhiyun 
2260*4882a593Smuzhiyun 	retval = pci_enable_device(pdev);
2261*4882a593Smuzhiyun 	if (retval) {
2262*4882a593Smuzhiyun 		printk(KERN_WARNING "3w-xxxx: Failed to enable pci device.");
2263*4882a593Smuzhiyun 		goto out_disable_device;
2264*4882a593Smuzhiyun 	}
2265*4882a593Smuzhiyun 
2266*4882a593Smuzhiyun 	pci_set_master(pdev);
2267*4882a593Smuzhiyun 
2268*4882a593Smuzhiyun 	retval = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
2269*4882a593Smuzhiyun 	if (retval) {
2270*4882a593Smuzhiyun 		printk(KERN_WARNING "3w-xxxx: Failed to set dma mask.");
2271*4882a593Smuzhiyun 		goto out_disable_device;
2272*4882a593Smuzhiyun 	}
2273*4882a593Smuzhiyun 
2274*4882a593Smuzhiyun 	host = scsi_host_alloc(&driver_template, sizeof(TW_Device_Extension));
2275*4882a593Smuzhiyun 	if (!host) {
2276*4882a593Smuzhiyun 		printk(KERN_WARNING "3w-xxxx: Failed to allocate memory for device extension.");
2277*4882a593Smuzhiyun 		retval = -ENOMEM;
2278*4882a593Smuzhiyun 		goto out_disable_device;
2279*4882a593Smuzhiyun 	}
2280*4882a593Smuzhiyun 	tw_dev = (TW_Device_Extension *)host->hostdata;
2281*4882a593Smuzhiyun 
2282*4882a593Smuzhiyun 	/* Save values to device extension */
2283*4882a593Smuzhiyun 	tw_dev->host = host;
2284*4882a593Smuzhiyun 	tw_dev->tw_pci_dev = pdev;
2285*4882a593Smuzhiyun 
2286*4882a593Smuzhiyun 	if (tw_initialize_device_extension(tw_dev)) {
2287*4882a593Smuzhiyun 		printk(KERN_WARNING "3w-xxxx: Failed to initialize device extension.");
2288*4882a593Smuzhiyun 		retval = -ENOMEM;
2289*4882a593Smuzhiyun 		goto out_free_device_extension;
2290*4882a593Smuzhiyun 	}
2291*4882a593Smuzhiyun 
2292*4882a593Smuzhiyun 	/* Request IO regions */
2293*4882a593Smuzhiyun 	retval = pci_request_regions(pdev, "3w-xxxx");
2294*4882a593Smuzhiyun 	if (retval) {
2295*4882a593Smuzhiyun 		printk(KERN_WARNING "3w-xxxx: Failed to get mem region.");
2296*4882a593Smuzhiyun 		goto out_free_device_extension;
2297*4882a593Smuzhiyun 	}
2298*4882a593Smuzhiyun 
2299*4882a593Smuzhiyun 	/* Save base address */
2300*4882a593Smuzhiyun 	tw_dev->base_addr = pci_resource_start(pdev, 0);
2301*4882a593Smuzhiyun 	if (!tw_dev->base_addr) {
2302*4882a593Smuzhiyun 		printk(KERN_WARNING "3w-xxxx: Failed to get io address.");
2303*4882a593Smuzhiyun 		retval = -ENOMEM;
2304*4882a593Smuzhiyun 		goto out_release_mem_region;
2305*4882a593Smuzhiyun 	}
2306*4882a593Smuzhiyun 
2307*4882a593Smuzhiyun 	/* Disable interrupts on the card */
2308*4882a593Smuzhiyun 	TW_DISABLE_INTERRUPTS(tw_dev);
2309*4882a593Smuzhiyun 
2310*4882a593Smuzhiyun 	/* Initialize the card */
2311*4882a593Smuzhiyun 	if (tw_reset_sequence(tw_dev))
2312*4882a593Smuzhiyun 		goto out_release_mem_region;
2313*4882a593Smuzhiyun 
2314*4882a593Smuzhiyun 	/* Set host specific parameters */
2315*4882a593Smuzhiyun 	host->max_id = TW_MAX_UNITS;
2316*4882a593Smuzhiyun 	host->max_cmd_len = TW_MAX_CDB_LEN;
2317*4882a593Smuzhiyun 
2318*4882a593Smuzhiyun 	/* Luns and channels aren't supported by adapter */
2319*4882a593Smuzhiyun 	host->max_lun = 0;
2320*4882a593Smuzhiyun 	host->max_channel = 0;
2321*4882a593Smuzhiyun 
2322*4882a593Smuzhiyun 	/* Register the card with the kernel SCSI layer */
2323*4882a593Smuzhiyun 	retval = scsi_add_host(host, &pdev->dev);
2324*4882a593Smuzhiyun 	if (retval) {
2325*4882a593Smuzhiyun 		printk(KERN_WARNING "3w-xxxx: scsi add host failed");
2326*4882a593Smuzhiyun 		goto out_release_mem_region;
2327*4882a593Smuzhiyun 	}
2328*4882a593Smuzhiyun 
2329*4882a593Smuzhiyun 	pci_set_drvdata(pdev, host);
2330*4882a593Smuzhiyun 
2331*4882a593Smuzhiyun 	printk(KERN_WARNING "3w-xxxx: scsi%d: Found a 3ware Storage Controller at 0x%x, IRQ: %d.\n", host->host_no, tw_dev->base_addr, pdev->irq);
2332*4882a593Smuzhiyun 
2333*4882a593Smuzhiyun 	/* Now setup the interrupt handler */
2334*4882a593Smuzhiyun 	retval = request_irq(pdev->irq, tw_interrupt, IRQF_SHARED, "3w-xxxx", tw_dev);
2335*4882a593Smuzhiyun 	if (retval) {
2336*4882a593Smuzhiyun 		printk(KERN_WARNING "3w-xxxx: Error requesting IRQ.");
2337*4882a593Smuzhiyun 		goto out_remove_host;
2338*4882a593Smuzhiyun 	}
2339*4882a593Smuzhiyun 
2340*4882a593Smuzhiyun 	tw_device_extension_list[tw_device_extension_count] = tw_dev;
2341*4882a593Smuzhiyun 	tw_device_extension_count++;
2342*4882a593Smuzhiyun 
2343*4882a593Smuzhiyun 	/* Re-enable interrupts on the card */
2344*4882a593Smuzhiyun 	TW_ENABLE_AND_CLEAR_INTERRUPTS(tw_dev);
2345*4882a593Smuzhiyun 
2346*4882a593Smuzhiyun 	/* Finally, scan the host */
2347*4882a593Smuzhiyun 	scsi_scan_host(host);
2348*4882a593Smuzhiyun 
2349*4882a593Smuzhiyun 	if (twe_major == -1) {
2350*4882a593Smuzhiyun 		if ((twe_major = register_chrdev (0, "twe", &tw_fops)) < 0)
2351*4882a593Smuzhiyun 			printk(KERN_WARNING "3w-xxxx: Failed to register character device.");
2352*4882a593Smuzhiyun 	}
2353*4882a593Smuzhiyun 	return 0;
2354*4882a593Smuzhiyun 
2355*4882a593Smuzhiyun out_remove_host:
2356*4882a593Smuzhiyun 	scsi_remove_host(host);
2357*4882a593Smuzhiyun out_release_mem_region:
2358*4882a593Smuzhiyun 	pci_release_regions(pdev);
2359*4882a593Smuzhiyun out_free_device_extension:
2360*4882a593Smuzhiyun 	tw_free_device_extension(tw_dev);
2361*4882a593Smuzhiyun 	scsi_host_put(host);
2362*4882a593Smuzhiyun out_disable_device:
2363*4882a593Smuzhiyun 	pci_disable_device(pdev);
2364*4882a593Smuzhiyun 
2365*4882a593Smuzhiyun 	return retval;
2366*4882a593Smuzhiyun } /* End tw_probe() */
2367*4882a593Smuzhiyun 
2368*4882a593Smuzhiyun /* This function is called to remove a device */
tw_remove(struct pci_dev * pdev)2369*4882a593Smuzhiyun static void tw_remove(struct pci_dev *pdev)
2370*4882a593Smuzhiyun {
2371*4882a593Smuzhiyun 	struct Scsi_Host *host = pci_get_drvdata(pdev);
2372*4882a593Smuzhiyun 	TW_Device_Extension *tw_dev = (TW_Device_Extension *)host->hostdata;
2373*4882a593Smuzhiyun 
2374*4882a593Smuzhiyun 	scsi_remove_host(tw_dev->host);
2375*4882a593Smuzhiyun 
2376*4882a593Smuzhiyun 	/* Unregister character device */
2377*4882a593Smuzhiyun 	if (twe_major >= 0) {
2378*4882a593Smuzhiyun 		unregister_chrdev(twe_major, "twe");
2379*4882a593Smuzhiyun 		twe_major = -1;
2380*4882a593Smuzhiyun 	}
2381*4882a593Smuzhiyun 
2382*4882a593Smuzhiyun 	/* Shutdown the card */
2383*4882a593Smuzhiyun 	__tw_shutdown(tw_dev);
2384*4882a593Smuzhiyun 
2385*4882a593Smuzhiyun 	/* Free up the mem region */
2386*4882a593Smuzhiyun 	pci_release_regions(pdev);
2387*4882a593Smuzhiyun 
2388*4882a593Smuzhiyun 	/* Free up device extension resources */
2389*4882a593Smuzhiyun 	tw_free_device_extension(tw_dev);
2390*4882a593Smuzhiyun 
2391*4882a593Smuzhiyun 	scsi_host_put(tw_dev->host);
2392*4882a593Smuzhiyun 	pci_disable_device(pdev);
2393*4882a593Smuzhiyun 	tw_device_extension_count--;
2394*4882a593Smuzhiyun } /* End tw_remove() */
2395*4882a593Smuzhiyun 
2396*4882a593Smuzhiyun /* PCI Devices supported by this driver */
2397*4882a593Smuzhiyun static struct pci_device_id tw_pci_tbl[] = {
2398*4882a593Smuzhiyun 	{ PCI_VENDOR_ID_3WARE, PCI_DEVICE_ID_3WARE_1000,
2399*4882a593Smuzhiyun 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
2400*4882a593Smuzhiyun 	{ PCI_VENDOR_ID_3WARE, PCI_DEVICE_ID_3WARE_7000,
2401*4882a593Smuzhiyun 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
2402*4882a593Smuzhiyun 	{ }
2403*4882a593Smuzhiyun };
2404*4882a593Smuzhiyun MODULE_DEVICE_TABLE(pci, tw_pci_tbl);
2405*4882a593Smuzhiyun 
2406*4882a593Smuzhiyun /* pci_driver initializer */
2407*4882a593Smuzhiyun static struct pci_driver tw_driver = {
2408*4882a593Smuzhiyun 	.name		= "3w-xxxx",
2409*4882a593Smuzhiyun 	.id_table	= tw_pci_tbl,
2410*4882a593Smuzhiyun 	.probe		= tw_probe,
2411*4882a593Smuzhiyun 	.remove		= tw_remove,
2412*4882a593Smuzhiyun 	.shutdown	= tw_shutdown,
2413*4882a593Smuzhiyun };
2414*4882a593Smuzhiyun 
2415*4882a593Smuzhiyun /* This function is called on driver initialization */
tw_init(void)2416*4882a593Smuzhiyun static int __init tw_init(void)
2417*4882a593Smuzhiyun {
2418*4882a593Smuzhiyun 	printk(KERN_WARNING "3ware Storage Controller device driver for Linux v%s.\n", TW_DRIVER_VERSION);
2419*4882a593Smuzhiyun 
2420*4882a593Smuzhiyun 	return pci_register_driver(&tw_driver);
2421*4882a593Smuzhiyun } /* End tw_init() */
2422*4882a593Smuzhiyun 
2423*4882a593Smuzhiyun /* This function is called on driver exit */
tw_exit(void)2424*4882a593Smuzhiyun static void __exit tw_exit(void)
2425*4882a593Smuzhiyun {
2426*4882a593Smuzhiyun 	pci_unregister_driver(&tw_driver);
2427*4882a593Smuzhiyun } /* End tw_exit() */
2428*4882a593Smuzhiyun 
2429*4882a593Smuzhiyun module_init(tw_init);
2430*4882a593Smuzhiyun module_exit(tw_exit);
2431*4882a593Smuzhiyun 
2432