xref: /openbsd/sys/dev/ic/uha.c (revision 891d7ab6)
1 /*	$OpenBSD: uha.c,v 1.22 2011/04/03 12:42:36 krw Exp $	*/
2 /*	$NetBSD: uha.c,v 1.3 1996/10/13 01:37:29 christos Exp $	*/
3 
4 #undef UHADEBUG
5 #ifdef DDB
6 #define	integrate
7 #else
8 #define	integrate	static inline
9 #endif
10 
11 /*
12  * Copyright (c) 1994, 1996 Charles M. Hannum.  All rights reserved.
13  *
14  * Redistribution and use in source and binary forms, with or without
15  * modification, are permitted provided that the following conditions
16  * are met:
17  * 1. Redistributions of source code must retain the above copyright
18  *    notice, this list of conditions and the following disclaimer.
19  * 2. Redistributions in binary form must reproduce the above copyright
20  *    notice, this list of conditions and the following disclaimer in the
21  *    documentation and/or other materials provided with the distribution.
22  * 3. All advertising materials mentioning features or use of this software
23  *    must display the following acknowledgement:
24  *	This product includes software developed by Charles M. Hannum.
25  * 4. The name of the author may not be used to endorse or promote products
26  *    derived from this software without specific prior written permission.
27  *
28  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
29  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
30  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
31  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
32  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
33  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
34  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
35  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
36  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
37  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38  */
39 
40 /*
41  * Ported for use with the UltraStor 14f by Gary Close (gclose@wvnvms.wvnet.edu)
42  * Slight fixes to timeouts to run with the 34F
43  * Thanks to Julian Elischer for advice and help with this port.
44  *
45  * Originally written by Julian Elischer (julian@tfs.com)
46  * for TRW Financial Systems for use under the MACH(2.5) operating system.
47  *
48  * TRW Financial Systems, in accordance with their agreement with Carnegie
49  * Mellon University, makes this software available to CMU to distribute
50  * or use in any manner that they see fit as long as this message is kept with
51  * the software. For this reason TFS also grants any other persons or
52  * organisations permission to use or modify this software.
53  *
54  * TFS supplies this software to be publicly redistributed
55  * on the understanding that TFS is not responsible for the correct
56  * functioning of this software in any circumstances.
57  *
58  * commenced: Sun Sep 27 18:14:01 PDT 1992
59  * slight mod to make work with 34F as well: Wed Jun  2 18:05:48 WST 1993
60  */
61 
62 #include <sys/types.h>
63 #include <sys/param.h>
64 #include <sys/systm.h>
65 #include <sys/kernel.h>
66 #include <sys/errno.h>
67 #include <sys/ioctl.h>
68 #include <sys/device.h>
69 #include <sys/malloc.h>
70 #include <sys/buf.h>
71 #include <sys/proc.h>
72 #include <uvm/uvm_extern.h>
73 
74 #include <machine/bus.h>
75 #include <machine/intr.h>
76 
77 #include <scsi/scsi_all.h>
78 #include <scsi/scsiconf.h>
79 
80 #include <dev/ic/uhareg.h>
81 #include <dev/ic/uhavar.h>
82 
83 #define KVTOPHYS(x)	vtophys((vaddr_t)x)
84 
85 integrate void uha_reset_mscp(struct uha_softc *, struct uha_mscp *);
86 void uha_mscp_free(void *, void *);
87 integrate void uha_init_mscp(struct uha_softc *, struct uha_mscp *);
88 void *uha_mscp_alloc(void *);
89 void uhaminphys(struct buf *, struct scsi_link *);
90 void uha_scsi_cmd(struct scsi_xfer *);
91 
92 struct scsi_adapter uha_switch = {
93 	uha_scsi_cmd,
94 	uhaminphys,
95 	0,
96 	0,
97 };
98 
99 struct cfdriver uha_cd = {
100 	NULL, "uha", DV_DULL
101 };
102 
103 #define	UHA_ABORT_TIMEOUT	2000	/* time to wait for abort (mSec) */
104 
105 #ifdef __OpenBSD__
106 int	uhaprint(void *, const char *);
107 
108 int
109 uhaprint(aux, name)
110 	void *aux;
111 	const char *name;
112 {
113 
114 	if (name != NULL)
115 		printf("%s: scsibus ", name);
116 	return UNCONF;
117 }
118 #endif
119 
120 /*
121  * Attach all the sub-devices we can find
122  */
123 void
124 uha_attach(sc)
125 	struct uha_softc *sc;
126 {
127 	struct scsibus_attach_args saa;
128 
129 	(sc->init)(sc);
130 	SLIST_INIT(&sc->sc_free_mscp);
131 
132 	mtx_init(&sc->sc_mscp_mtx, IPL_BIO);
133 	scsi_iopool_init(&sc->sc_iopool, sc, uha_mscp_alloc, uha_mscp_free);
134 
135 	/*
136 	 * fill in the prototype scsi_link.
137 	 */
138 	sc->sc_link.adapter_softc = sc;
139 	sc->sc_link.adapter_target = sc->sc_scsi_dev;
140 	sc->sc_link.adapter = &uha_switch;
141 	sc->sc_link.openings = 2;
142 	sc->sc_link.pool = &sc->sc_iopool;
143 
144 	bzero(&saa, sizeof(saa));
145 	saa.saa_sc_link = &sc->sc_link;
146 
147 	/*
148 	 * ask the adapter what subunits are present
149 	 */
150 	config_found(&sc->sc_dev, &saa, uhaprint);
151 }
152 
153 integrate void
154 uha_reset_mscp(sc, mscp)
155 	struct uha_softc *sc;
156 	struct uha_mscp *mscp;
157 {
158 
159 	mscp->flags = 0;
160 }
161 
162 /*
163  * A mscp (and hence a mbx-out) is put onto the free list.
164  */
165 void
166 uha_mscp_free(xsc, xmscp)
167 	void *xsc, *xmscp;
168 {
169 	struct uha_softc *sc = xmscp;
170 	struct uha_mscp *mscp;
171 
172 	uha_reset_mscp(sc, mscp);
173 
174 	mtx_enter(&sc->sc_mscp_mtx);
175 	SLIST_INSERT_HEAD(&sc->sc_free_mscp, mscp, chain);
176 	mtx_leave(&sc->sc_mscp_mtx);
177 }
178 
179 integrate void
180 uha_init_mscp(sc, mscp)
181 	struct uha_softc *sc;
182 	struct uha_mscp *mscp;
183 {
184 	int hashnum;
185 
186 	bzero(mscp, sizeof(struct uha_mscp));
187 	/*
188 	 * put in the phystokv hash table
189 	 * Never gets taken out.
190 	 */
191 	mscp->hashkey = KVTOPHYS(mscp);
192 	hashnum = MSCP_HASH(mscp->hashkey);
193 	mscp->nexthash = sc->sc_mscphash[hashnum];
194 	sc->sc_mscphash[hashnum] = mscp;
195 	uha_reset_mscp(sc, mscp);
196 }
197 
198 /*
199  * Get a free mscp
200  */
201 void *
202 uha_mscp_alloc(xsc)
203 	void *xsc;
204 {
205 	struct uha_softc *sc = xsc;
206 	struct uha_mscp *mscp;
207 
208 	mtx_enter(&sc->sc_mscp_mtx);
209 	mscp = SLIST_FIRST(&sc->sc_free_mscp);
210 	if (mscp) {
211 		SLIST_REMOVE_HEAD(&sc->sc_free_mscp, chain);
212 		mscp->flags |= MSCP_ALLOC;
213 	}
214 	mtx_leave(&sc->sc_mscp_mtx);
215 
216 	return (mscp);
217 }
218 
219 /*
220  * given a physical address, find the mscp that it corresponds to.
221  */
222 struct uha_mscp *
223 uha_mscp_phys_kv(sc, mscp_phys)
224 	struct uha_softc *sc;
225 	u_long mscp_phys;
226 {
227 	int hashnum = MSCP_HASH(mscp_phys);
228 	struct uha_mscp *mscp = sc->sc_mscphash[hashnum];
229 
230 	while (mscp) {
231 		if (mscp->hashkey == mscp_phys)
232 			break;
233 		mscp = mscp->nexthash;
234 	}
235 	return (mscp);
236 }
237 
238 /*
239  * We have a mscp which has been processed by the adaptor, now we look to see
240  * how the operation went.
241  */
242 void
243 uha_done(sc, mscp)
244 	struct uha_softc *sc;
245 	struct uha_mscp *mscp;
246 {
247 	struct scsi_sense_data *s1, *s2;
248 	struct scsi_xfer *xs = mscp->xs;
249 
250 	SC_DEBUG(xs->sc_link, SDEV_DB2, ("uha_done\n"));
251 	/*
252 	 * Otherwise, put the results of the operation
253 	 * into the xfer and call whoever started it
254 	 */
255 	if ((mscp->flags & MSCP_ALLOC) == 0) {
256 		panic("%s: exiting ccb not allocated!", sc->sc_dev.dv_xname);
257 		return;
258 	}
259 	if (xs->error == XS_NOERROR) {
260 		if (mscp->host_stat != UHA_NO_ERR) {
261 			switch (mscp->host_stat) {
262 			case UHA_SBUS_TIMEOUT:		/* No response */
263 				xs->error = XS_SELTIMEOUT;
264 				break;
265 			default:	/* Other scsi protocol messes */
266 				printf("%s: host_stat %x\n",
267 				    sc->sc_dev.dv_xname, mscp->host_stat);
268 				xs->error = XS_DRIVER_STUFFUP;
269 			}
270 		} else if (mscp->target_stat != SCSI_OK) {
271 			switch (mscp->target_stat) {
272 			case SCSI_CHECK:
273 				s1 = &mscp->mscp_sense;
274 				s2 = &xs->sense;
275 				*s2 = *s1;
276 				xs->error = XS_SENSE;
277 				break;
278 			case SCSI_BUSY:
279 				xs->error = XS_BUSY;
280 				break;
281 			default:
282 				printf("%s: target_stat %x\n",
283 				    sc->sc_dev.dv_xname, mscp->target_stat);
284 				xs->error = XS_DRIVER_STUFFUP;
285 			}
286 		} else
287 			xs->resid = 0;
288 	}
289 
290 	scsi_done(xs);
291 }
292 
293 void
294 uhaminphys(struct buf *bp, struct scsi_link *sl)
295 {
296 	if (bp->b_bcount > ((UHA_NSEG - 1) << PGSHIFT))
297 		bp->b_bcount = ((UHA_NSEG - 1) << PGSHIFT);
298 	minphys(bp);
299 }
300 
301 /*
302  * start a scsi operation given the command and the data address.  Also
303  * needs the unit, target and lu.
304  */
305 void
306 uha_scsi_cmd(xs)
307 	struct scsi_xfer *xs;
308 {
309 	struct scsi_link *sc_link = xs->sc_link;
310 	struct uha_softc *sc = sc_link->adapter_softc;
311 	struct uha_mscp *mscp;
312 	struct uha_dma_seg *sg;
313 	int seg;		/* scatter gather seg being worked on */
314 	u_long thiskv, thisphys, nextphys;
315 	int bytes_this_seg, bytes_this_page, datalen, flags;
316 	int s;
317 
318 	SC_DEBUG(sc_link, SDEV_DB2, ("uha_scsi_cmd\n"));
319 	/*
320 	 * get a mscp (mbox-out) to use. If the transfer
321 	 * is from a buf (possibly from interrupt time)
322 	 * then we can't allow it to sleep
323 	 */
324 	flags = xs->flags;
325 	mscp = xs->io;
326 
327 	mscp->xs = xs;
328 	mscp->timeout = xs->timeout;
329 	timeout_set(&xs->stimeout, uha_timeout, xs);
330 
331 	/*
332 	 * Put all the arguments for the xfer in the mscp
333 	 */
334 	if (flags & SCSI_RESET) {
335 		mscp->opcode = UHA_SDR;
336 		mscp->ca = 0x01;
337 	} else {
338 		mscp->opcode = UHA_TSP;
339 		/* XXX Not for tapes. */
340 		mscp->ca = 0x01;
341 		bcopy(xs->cmd, &mscp->scsi_cmd, mscp->scsi_cmd_length);
342 	}
343 	mscp->xdir = UHA_SDET;
344 	mscp->dcn = 0x00;
345 	mscp->chan = 0x00;
346 	mscp->target = sc_link->target;
347 	mscp->lun = sc_link->lun;
348 	mscp->scsi_cmd_length = xs->cmdlen;
349 	mscp->sense_ptr = KVTOPHYS(&mscp->mscp_sense);
350 	mscp->req_sense_length = sizeof(mscp->mscp_sense);
351 	mscp->host_stat = 0x00;
352 	mscp->target_stat = 0x00;
353 
354 	if (xs->datalen) {
355 		sg = mscp->uha_dma;
356 		seg = 0;
357 
358 		/*
359 		 * Set up the scatter gather block
360 		 */
361 		SC_DEBUG(sc_link, SDEV_DB4,
362 		    ("%d @0x%x:- ", xs->datalen, xs->data));
363 		datalen = xs->datalen;
364 		thiskv = (int) xs->data;
365 		thisphys = KVTOPHYS(thiskv);
366 
367 		while (datalen && seg < UHA_NSEG) {
368 			bytes_this_seg = 0;
369 
370 			/* put in the base address */
371 			sg->seg_addr = thisphys;
372 
373 			SC_DEBUGN(sc_link, SDEV_DB4, ("0x%x", thisphys));
374 
375 			/* do it at least once */
376 			nextphys = thisphys;
377 			while (datalen && thisphys == nextphys) {
378 				/*
379 				 * This page is contiguous (physically)
380 				 * with the last, just extend the
381 				 * length
382 				 */
383 				/* how far to the end of the page */
384 				nextphys = (thisphys & ~PGOFSET) + NBPG;
385 				bytes_this_page = nextphys - thisphys;
386 				/**** or the data ****/
387 				bytes_this_page = min(bytes_this_page,
388 						      datalen);
389 				bytes_this_seg += bytes_this_page;
390 				datalen -= bytes_this_page;
391 
392 				/* get more ready for the next page */
393 				thiskv = (thiskv & ~PGOFSET) + NBPG;
394 				if (datalen)
395 					thisphys = KVTOPHYS(thiskv);
396 			}
397 			/*
398 			 * next page isn't contiguous, finish the seg
399 			 */
400 			SC_DEBUGN(sc_link, SDEV_DB4,
401 			    ("(0x%x)", bytes_this_seg));
402 			sg->seg_len = bytes_this_seg;
403 			sg++;
404 			seg++;
405 		}
406 
407 		SC_DEBUGN(sc_link, SDEV_DB4, ("\n"));
408 		if (datalen) {
409 			/*
410 			 * there's still data, must have run out of segs!
411 			 */
412 			printf("%s: uha_scsi_cmd, more than %d dma segs\n",
413 			    sc->sc_dev.dv_xname, UHA_NSEG);
414 			goto bad;
415 		}
416 		mscp->data_addr = KVTOPHYS(mscp->uha_dma);
417 		mscp->data_length = xs->datalen;
418 		mscp->sgth = 0x01;
419 		mscp->sg_num = seg;
420 	} else {		/* No data xfer, use non S/G values */
421 		mscp->data_addr = (physaddr)0;
422 		mscp->data_length = 0;
423 		mscp->sgth = 0x00;
424 		mscp->sg_num = 0;
425 	}
426 	mscp->link_id = 0;
427 	mscp->link_addr = (physaddr)0;
428 
429 	s = splbio();
430 	(sc->start_mbox)(sc, mscp);
431 	splx(s);
432 
433 	/*
434 	 * Usually return SUCCESSFULLY QUEUED
435 	 */
436 	if ((flags & SCSI_POLL) == 0)
437 		return;
438 
439 	/*
440 	 * If we can't use interrupts, poll on completion
441 	 */
442 	if ((sc->poll)(sc, xs, mscp->timeout)) {
443 		uha_timeout(mscp);
444 		if ((sc->poll)(sc, xs, mscp->timeout))
445 			uha_timeout(mscp);
446 	}
447 	return;
448 
449 bad:
450 	xs->error = XS_DRIVER_STUFFUP;
451 	scsi_done(xs);
452 	return;
453 }
454 
455 void
456 uha_timeout(arg)
457 	void *arg;
458 {
459 	struct uha_mscp *mscp = arg;
460 	struct scsi_xfer *xs = mscp->xs;
461 	struct scsi_link *sc_link = xs->sc_link;
462 	struct uha_softc *sc = sc_link->adapter_softc;
463 	int s;
464 
465 	sc_print_addr(sc_link);
466 	printf("timed out");
467 
468 	s = splbio();
469 
470 	if (mscp->flags & MSCP_ABORT) {
471 		/* abort timed out */
472 		printf(" AGAIN\n");
473 		/* XXX Must reset! */
474 	} else {
475 		/* abort the operation that has timed out */
476 		printf("\n");
477 		mscp->xs->error = XS_TIMEOUT;
478 		mscp->timeout = UHA_ABORT_TIMEOUT;
479 		mscp->flags |= MSCP_ABORT;
480 		(sc->start_mbox)(sc, mscp);
481 	}
482 
483 	splx(s);
484 }
485