isp_netbsd.c revision 1.10 1 /* $NetBSD: isp_netbsd.c,v 1.10 1999/02/09 00:42:22 mjacob Exp $ */
2 /* release_02_05_99 */
3 /*
4 * Platform (NetBSD) dependent common attachment code for Qlogic adapters.
5 *
6 *---------------------------------------
7 * Copyright (c) 1997, 1998 by Matthew Jacob
8 * NASA/Ames Research Center
9 * All rights reserved.
10 *---------------------------------------
11 *
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
14 * are met:
15 * 1. Redistributions of source code must retain the above copyright
16 * notice immediately at the beginning of the file, without modification,
17 * this list of conditions, and the following disclaimer.
18 * 2. Redistributions in binary form must reproduce the above copyright
19 * notice, this list of conditions and the following disclaimer in the
20 * documentation and/or other materials provided with the distribution.
21 * 3. The name of the author may not be used to endorse or promote products
22 * derived from this software without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
28 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
35 *
36 * The author may be reached via electronic communications at
37 *
38 * mjacob (at) nas.nasa.gov
39 * mjacob (at) feral.com
40 *
41 * or, via United States Postal Address
42 *
43 * Matthew Jacob
44 * Feral Software
45 * 2339 3rd Street
46 * Suite 24
47 * San Francisco, CA, 94107
48 */
49
50 #include <dev/ic/isp_netbsd.h>
51
52 static void ispminphys __P((struct buf *));
53 static int32_t ispcmd __P((ISP_SCSI_XFER_T *));
54
55 static struct scsipi_device isp_dev = { NULL, NULL, NULL, NULL };
56 static int isp_poll __P((struct ispsoftc *, ISP_SCSI_XFER_T *, int));
57 static void isp_watch __P((void *));
58 static void isp_internal_restart __P((void *));
59
60 #define FC_OPENINGS RQUEST_QUEUE_LEN / (MAX_FC_TARG-1)
61 #define PI_OPENINGS RQUEST_QUEUE_LEN / (MAX_TARGETS-1)
62
63 /*
64 * Complete attachment of hardware, include subdevices.
65 */
66 void
67 isp_attach(isp)
68 struct ispsoftc *isp;
69 {
70
71 isp->isp_osinfo._adapter.scsipi_cmd = ispcmd;
72 isp->isp_osinfo._adapter.scsipi_minphys = ispminphys;
73
74 isp->isp_state = ISP_RUNSTATE;
75 isp->isp_osinfo._link.scsipi_scsi.channel = SCSI_CHANNEL_ONLY_ONE;
76 isp->isp_osinfo._link.adapter_softc = isp;
77 isp->isp_osinfo._link.device = &isp_dev;
78 isp->isp_osinfo._link.adapter = &isp->isp_osinfo._adapter;
79 TAILQ_INIT(&isp->isp_osinfo.waitq);
80
81 if (isp->isp_type & ISP_HA_FC) {
82 isp->isp_osinfo._link.scsipi_scsi.max_target = MAX_FC_TARG-1;
83 #ifdef SCCLUN
84 /*
85 * 16 bits worth, but let's be reasonable..
86 */
87 isp->isp_osinfo._link.scsipi_scsi.max_lun = 255;
88 #else
89 isp->isp_osinfo._link.scsipi_scsi.max_lun = 15;
90 #endif
91 isp->isp_osinfo._link.openings = FC_OPENINGS;
92 isp->isp_osinfo._link.scsipi_scsi.adapter_target =
93 ((fcparam *)isp->isp_param)->isp_loopid;
94 } else {
95 isp->isp_osinfo._link.openings = PI_OPENINGS;
96 isp->isp_osinfo._link.scsipi_scsi.max_target = MAX_TARGETS-1;
97 if (isp->isp_bustype == ISP_BT_SBUS) {
98 isp->isp_osinfo._link.scsipi_scsi.max_lun = 7;
99 } else {
100 /*
101 * Too much target breakage at present.
102 */
103 #if 0
104 if (isp->isp_fwrev >= ISP_FW_REV(7,55))
105 isp->isp_osinfo._link.scsipi_scsi.max_lun = 31;
106 else
107 #endif
108 isp->isp_osinfo._link.scsipi_scsi.max_lun = 7;
109 }
110 isp->isp_osinfo._link.scsipi_scsi.adapter_target =
111 ((sdparam *)isp->isp_param)->isp_initiator_id;
112 }
113 if (isp->isp_osinfo._link.openings < 2)
114 isp->isp_osinfo._link.openings = 2;
115 isp->isp_osinfo._link.type = BUS_SCSI;
116
117 /*
118 * Send a SCSI Bus Reset (used to be done as part of attach,
119 * but now left to the OS outer layers).
120 *
121 * XXX: For now, skip resets for FC because the method by which
122 * XXX: we deal with loop down after issuing resets (which causes
123 * XXX: port logouts for all devices) needs interrupts to run so
124 * XXX: that async events happen.
125 */
126
127 if (isp->isp_type & ISP_HA_SCSI)
128 (void) isp_control(isp, ISPCTL_RESET_BUS, NULL);
129
130 /*
131 * Start the watchdog.
132 */
133 isp->isp_dogactive = 1;
134 timeout(isp_watch, isp, 30 * hz);
135
136 /*
137 * And attach children (if any).
138 */
139 config_found((void *)isp, &isp->isp_osinfo._link, scsiprint);
140 }
141
142 /*
143 * minphys our xfers
144 *
145 * Unfortunately, the buffer pointer describes the target device- not the
146 * adapter device, so we can't use the pointer to find out what kind of
147 * adapter we are and adjust accordingly.
148 */
149
150 static void
151 ispminphys(bp)
152 struct buf *bp;
153 {
154 /*
155 * XX: Only the 1020 has a 24 bit limit.
156 */
157 if (bp->b_bcount >= (1 << 24)) {
158 bp->b_bcount = (1 << 24);
159 }
160 minphys(bp);
161 }
162
163 static int
164 ispcmd(xs)
165 ISP_SCSI_XFER_T *xs;
166 {
167 struct ispsoftc *isp;
168 int result;
169 int s;
170
171 isp = XS_ISP(xs);
172 s = splbio();
173 /*
174 * This is less efficient than I would like in that the
175 * majority of cases will have to do some pointer deferences
176 * to find out that things don't need to be updated.
177 */
178 if ((xs->flags & SCSI_AUTOCONF) == 0 && (isp->isp_type & ISP_HA_SCSI)) {
179 sdparam *sdp = isp->isp_param;
180 if (sdp->isp_devparam[XS_TGT(xs)].dev_flags !=
181 sdp->isp_devparam[XS_TGT(xs)].cur_dflags) {
182 u_int16_t f = DPARM_WIDE|DPARM_SYNC|DPARM_TQING;
183 if (xs->sc_link->quirks & SDEV_NOSYNC)
184 f &= ~DPARM_SYNC;
185 if (xs->sc_link->quirks & SDEV_NOWIDE)
186 f &= ~DPARM_WIDE;
187 if (xs->sc_link->quirks & SDEV_NOTAG)
188 f &= ~DPARM_TQING;
189 sdp->isp_devparam[XS_TGT(xs)].dev_flags &=
190 ~(DPARM_WIDE|DPARM_SYNC|DPARM_TQING);
191 sdp->isp_devparam[XS_TGT(xs)].dev_flags |= f;
192 sdp->isp_devparam[XS_TGT(xs)].dev_update = 1;
193 isp->isp_update = 1;
194 }
195 }
196 /*
197 * Check for queue blockage...
198 */
199
200 if (isp->isp_osinfo.blocked) {
201 if (xs->flags & SCSI_POLL) {
202 xs->error = XS_DRIVER_STUFFUP;
203 splx(s);
204 return (TRY_AGAIN_LATER);
205 }
206 TAILQ_INSERT_TAIL(&isp->isp_osinfo.waitq, xs, adapter_q);
207 splx(s);
208 return (CMD_QUEUED);
209 }
210
211 result = ispscsicmd(xs);
212 if (result != CMD_QUEUED || (xs->flags & SCSI_POLL) == 0) {
213 (void) splx(s);
214 return (result);
215 }
216
217 /*
218 * If we can't use interrupts, poll on completion.
219 */
220 if (isp_poll(isp, xs, XS_TIME(xs))) {
221 /*
222 * If no other error occurred but we didn't finish,
223 * something bad happened.
224 */
225 if (XS_IS_CMD_DONE(xs) == 0) {
226 isp->isp_nactive--;
227 if (isp->isp_nactive < 0)
228 isp->isp_nactive = 0;
229 if (XS_NOERR(xs)) {
230 isp_lostcmd(isp, xs);
231 XS_SETERR(xs, HBA_BOTCH);
232 }
233 }
234 }
235 (void) splx(s);
236 return (CMD_COMPLETE);
237 }
238
239 static int
240 isp_poll(isp, xs, mswait)
241 struct ispsoftc *isp;
242 ISP_SCSI_XFER_T *xs;
243 int mswait;
244 {
245
246 while (mswait) {
247 /* Try the interrupt handling routine */
248 (void)isp_intr((void *)isp);
249
250 /* See if the xs is now done */
251 if (XS_IS_CMD_DONE(xs)) {
252 return (0);
253 }
254 SYS_DELAY(1000); /* wait one millisecond */
255 mswait--;
256 }
257 return (1);
258 }
259
260 static void
261 isp_watch(arg)
262 void *arg;
263 {
264 int i;
265 struct ispsoftc *isp = arg;
266 ISP_SCSI_XFER_T *xs;
267 ISP_ILOCKVAL_DECL;
268
269 /*
270 * Look for completely dead commands (but not polled ones).
271 */
272 ISP_ILOCK(isp);
273 for (i = 0; i < RQUEST_QUEUE_LEN; i++) {
274 if ((xs = (ISP_SCSI_XFER_T *) isp->isp_xflist[i]) == NULL) {
275 continue;
276 }
277 if (XS_TIME(xs) == 0) {
278 continue;
279 }
280 XS_TIME(xs) -= (WATCH_INTERVAL * 1000);
281 /*
282 * Avoid later thinking that this
283 * transaction is not being timed.
284 * Then give ourselves to watchdog
285 * periods of grace.
286 */
287 if (XS_TIME(xs) == 0) {
288 XS_TIME(xs) = 1;
289 } else if (XS_TIME(xs) > -(2 * WATCH_INTERVAL * 1000)) {
290 continue;
291 }
292 if (isp_control(isp, ISPCTL_ABORT_CMD, xs)) {
293 printf("%s: isp_watch failed to abort command\n",
294 isp->isp_name);
295 isp_restart(isp);
296 break;
297 }
298 }
299 timeout(isp_watch, isp, WATCH_INTERVAL * hz);
300 isp->isp_dogactive = 1;
301 ISP_IUNLOCK(isp);
302 }
303
304 /*
305 * Free any associated resources prior to decommissioning and
306 * set the card to a known state (so it doesn't wake up and kick
307 * us when we aren't expecting it to).
308 *
309 * Locks are held before coming here.
310 */
311 void
312 isp_uninit(isp)
313 struct ispsoftc *isp;
314 {
315 ISP_ILOCKVAL_DECL;
316 ISP_ILOCK(isp);
317 /*
318 * Leave with interrupts disabled.
319 */
320 DISABLE_INTS(isp);
321
322 /*
323 * Turn off the watchdog (if active).
324 */
325 if (isp->isp_dogactive) {
326 untimeout(isp_watch, isp);
327 isp->isp_dogactive = 0;
328 }
329
330 ISP_IUNLOCK(isp);
331 }
332
333 /*
334 * Restart function after a LOOP UP event (e.g.),
335 * done as a timeout for some hysteresis.
336 */
337 static void
338 isp_internal_restart(arg)
339 void *arg;
340 {
341 struct ispsoftc *isp = arg;
342 int result, nrestarted = 0, s = splbio();
343
344 if (isp->isp_osinfo.blocked == 0) {
345 struct scsipi_xfer *xs;
346 while ((xs = TAILQ_FIRST(&isp->isp_osinfo.waitq)) != NULL) {
347 TAILQ_REMOVE(&isp->isp_osinfo.waitq, xs, adapter_q);
348 if ((result = ispscsicmd(xs)) != CMD_QUEUED) {
349 printf("%s: botched command restart (0x%x)\n",
350 isp->isp_name, result);
351 xs->flags |= ITSDONE;
352 if (xs->error == XS_NOERROR)
353 xs->error = XS_DRIVER_STUFFUP;
354 scsipi_done(xs);
355 }
356 nrestarted++;
357 }
358 printf("%s: requeued %d commands\n", isp->isp_name, nrestarted);
359 }
360 (void) splx(s);
361 }
362
363 int
364 isp_async(isp, cmd, arg)
365 struct ispsoftc *isp;
366 ispasync_t cmd;
367 void *arg;
368 {
369 int s = splbio();
370 switch (cmd) {
371 case ISPASYNC_LOOP_DOWN:
372 /*
373 * Hopefully we get here in time to minimize the number
374 * of commands we are firing off that are sure to die.
375 */
376 isp->isp_osinfo.blocked = 1;
377 break;
378 case ISPASYNC_LOOP_UP:
379 isp->isp_osinfo.blocked = 0;
380 timeout(isp_internal_restart, isp, 1);
381 break;
382 case ISPASYNC_NEW_TGT_PARAMS:
383 if (isp->isp_type & ISP_HA_SCSI) {
384 sdparam *sdp = isp->isp_param;
385 char *wt;
386 int ns, flags, tgt;
387
388 tgt = *((int *) arg);
389
390 flags = sdp->isp_devparam[tgt].dev_flags;
391 if (flags & DPARM_SYNC) {
392 ns = sdp->isp_devparam[tgt].sync_period * 4;
393 } else {
394 ns = 0;
395 }
396 switch (flags & (DPARM_WIDE|DPARM_TQING)) {
397 case DPARM_WIDE:
398 wt = ", 16 bit wide\n";
399 break;
400 case DPARM_TQING:
401 wt = ", Tagged Queueing Enabled\n";
402 break;
403 case DPARM_WIDE|DPARM_TQING:
404 wt = ", 16 bit wide, Tagged Queueing Enabled\n";
405 break;
406 default:
407 wt = "\n";
408 break;
409 }
410 if (ns) {
411 printf("%s: Target %d at %dMHz Max Offset %d%s",
412 isp->isp_name, tgt, 1000 / ns,
413 sdp->isp_devparam[tgt].sync_offset, wt);
414 } else {
415 printf("%s: Target %d Async Mode%s",
416 isp->isp_name, tgt, wt);
417 }
418 }
419 break;
420 default:
421 break;
422 }
423 (void) splx(s);
424 return (0);
425 }
426