xref: /openbsd/sys/dev/ic/uha.c (revision fc61954a)
1 /*	$OpenBSD: uha.c,v 1.24 2014/09/14 14:17:25 jsg 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 <uvm/uvm_extern.h>
72 
73 #include <machine/bus.h>
74 #include <machine/intr.h>
75 
76 #include <scsi/scsi_all.h>
77 #include <scsi/scsiconf.h>
78 
79 #include <dev/ic/uhareg.h>
80 #include <dev/ic/uhavar.h>
81 
82 #define KVTOPHYS(x)	vtophys((vaddr_t)x)
83 
84 integrate void uha_reset_mscp(struct uha_softc *, struct uha_mscp *);
85 void uha_mscp_free(void *, void *);
86 integrate void uha_init_mscp(struct uha_softc *, struct uha_mscp *);
87 void *uha_mscp_alloc(void *);
88 void uhaminphys(struct buf *, struct scsi_link *);
89 void uha_scsi_cmd(struct scsi_xfer *);
90 
91 struct scsi_adapter uha_switch = {
92 	uha_scsi_cmd,
93 	uhaminphys,
94 	0,
95 	0,
96 };
97 
98 struct cfdriver uha_cd = {
99 	NULL, "uha", DV_DULL
100 };
101 
102 #define	UHA_ABORT_TIMEOUT	2000	/* time to wait for abort (mSec) */
103 
104 #ifdef __OpenBSD__
105 int	uhaprint(void *, const char *);
106 
107 int
108 uhaprint(aux, name)
109 	void *aux;
110 	const char *name;
111 {
112 
113 	if (name != NULL)
114 		printf("%s: scsibus ", name);
115 	return UNCONF;
116 }
117 #endif
118 
119 /*
120  * Attach all the sub-devices we can find
121  */
122 void
123 uha_attach(sc)
124 	struct uha_softc *sc;
125 {
126 	struct scsibus_attach_args saa;
127 
128 	(sc->init)(sc);
129 	SLIST_INIT(&sc->sc_free_mscp);
130 
131 	mtx_init(&sc->sc_mscp_mtx, IPL_BIO);
132 	scsi_iopool_init(&sc->sc_iopool, sc, uha_mscp_alloc, uha_mscp_free);
133 
134 	/*
135 	 * fill in the prototype scsi_link.
136 	 */
137 	sc->sc_link.adapter_softc = sc;
138 	sc->sc_link.adapter_target = sc->sc_scsi_dev;
139 	sc->sc_link.adapter = &uha_switch;
140 	sc->sc_link.openings = 2;
141 	sc->sc_link.pool = &sc->sc_iopool;
142 
143 	bzero(&saa, sizeof(saa));
144 	saa.saa_sc_link = &sc->sc_link;
145 
146 	/*
147 	 * ask the adapter what subunits are present
148 	 */
149 	config_found(&sc->sc_dev, &saa, uhaprint);
150 }
151 
152 integrate void
153 uha_reset_mscp(sc, mscp)
154 	struct uha_softc *sc;
155 	struct uha_mscp *mscp;
156 {
157 
158 	mscp->flags = 0;
159 }
160 
161 /*
162  * A mscp (and hence a mbx-out) is put onto the free list.
163  */
164 void
165 uha_mscp_free(xsc, xmscp)
166 	void *xsc, *xmscp;
167 {
168 	struct uha_softc *sc = xsc;
169 	struct uha_mscp *mscp = xmscp;
170 
171 	uha_reset_mscp(sc, mscp);
172 
173 	mtx_enter(&sc->sc_mscp_mtx);
174 	SLIST_INSERT_HEAD(&sc->sc_free_mscp, mscp, chain);
175 	mtx_leave(&sc->sc_mscp_mtx);
176 }
177 
178 integrate void
179 uha_init_mscp(sc, mscp)
180 	struct uha_softc *sc;
181 	struct uha_mscp *mscp;
182 {
183 	int hashnum;
184 
185 	bzero(mscp, sizeof(struct uha_mscp));
186 	/*
187 	 * put in the phystokv hash table
188 	 * Never gets taken out.
189 	 */
190 	mscp->hashkey = KVTOPHYS(mscp);
191 	hashnum = MSCP_HASH(mscp->hashkey);
192 	mscp->nexthash = sc->sc_mscphash[hashnum];
193 	sc->sc_mscphash[hashnum] = mscp;
194 	uha_reset_mscp(sc, mscp);
195 }
196 
197 /*
198  * Get a free mscp
199  */
200 void *
201 uha_mscp_alloc(xsc)
202 	void *xsc;
203 {
204 	struct uha_softc *sc = xsc;
205 	struct uha_mscp *mscp;
206 
207 	mtx_enter(&sc->sc_mscp_mtx);
208 	mscp = SLIST_FIRST(&sc->sc_free_mscp);
209 	if (mscp) {
210 		SLIST_REMOVE_HEAD(&sc->sc_free_mscp, chain);
211 		mscp->flags |= MSCP_ALLOC;
212 	}
213 	mtx_leave(&sc->sc_mscp_mtx);
214 
215 	return (mscp);
216 }
217 
218 /*
219  * given a physical address, find the mscp that it corresponds to.
220  */
221 struct uha_mscp *
222 uha_mscp_phys_kv(sc, mscp_phys)
223 	struct uha_softc *sc;
224 	u_long mscp_phys;
225 {
226 	int hashnum = MSCP_HASH(mscp_phys);
227 	struct uha_mscp *mscp = sc->sc_mscphash[hashnum];
228 
229 	while (mscp) {
230 		if (mscp->hashkey == mscp_phys)
231 			break;
232 		mscp = mscp->nexthash;
233 	}
234 	return (mscp);
235 }
236 
237 /*
238  * We have a mscp which has been processed by the adaptor, now we look to see
239  * how the operation went.
240  */
241 void
242 uha_done(sc, mscp)
243 	struct uha_softc *sc;
244 	struct uha_mscp *mscp;
245 {
246 	struct scsi_sense_data *s1, *s2;
247 	struct scsi_xfer *xs = mscp->xs;
248 
249 	SC_DEBUG(xs->sc_link, SDEV_DB2, ("uha_done\n"));
250 	/*
251 	 * Otherwise, put the results of the operation
252 	 * into the xfer and call whoever started it
253 	 */
254 	if ((mscp->flags & MSCP_ALLOC) == 0) {
255 		panic("%s: exiting ccb not allocated!", sc->sc_dev.dv_xname);
256 		return;
257 	}
258 	if (xs->error == XS_NOERROR) {
259 		if (mscp->host_stat != UHA_NO_ERR) {
260 			switch (mscp->host_stat) {
261 			case UHA_SBUS_TIMEOUT:		/* No response */
262 				xs->error = XS_SELTIMEOUT;
263 				break;
264 			default:	/* Other scsi protocol messes */
265 				printf("%s: host_stat %x\n",
266 				    sc->sc_dev.dv_xname, mscp->host_stat);
267 				xs->error = XS_DRIVER_STUFFUP;
268 			}
269 		} else if (mscp->target_stat != SCSI_OK) {
270 			switch (mscp->target_stat) {
271 			case SCSI_CHECK:
272 				s1 = &mscp->mscp_sense;
273 				s2 = &xs->sense;
274 				*s2 = *s1;
275 				xs->error = XS_SENSE;
276 				break;
277 			case SCSI_BUSY:
278 				xs->error = XS_BUSY;
279 				break;
280 			default:
281 				printf("%s: target_stat %x\n",
282 				    sc->sc_dev.dv_xname, mscp->target_stat);
283 				xs->error = XS_DRIVER_STUFFUP;
284 			}
285 		} else
286 			xs->resid = 0;
287 	}
288 
289 	scsi_done(xs);
290 }
291 
292 void
293 uhaminphys(struct buf *bp, struct scsi_link *sl)
294 {
295 	if (bp->b_bcount > ((UHA_NSEG - 1) << PGSHIFT))
296 		bp->b_bcount = ((UHA_NSEG - 1) << PGSHIFT);
297 	minphys(bp);
298 }
299 
300 /*
301  * start a scsi operation given the command and the data address.  Also
302  * needs the unit, target and lu.
303  */
304 void
305 uha_scsi_cmd(xs)
306 	struct scsi_xfer *xs;
307 {
308 	struct scsi_link *sc_link = xs->sc_link;
309 	struct uha_softc *sc = sc_link->adapter_softc;
310 	struct uha_mscp *mscp;
311 	struct uha_dma_seg *sg;
312 	int seg;		/* scatter gather seg being worked on */
313 	u_long thiskv, thisphys, nextphys;
314 	int bytes_this_seg, bytes_this_page, datalen, flags;
315 	int s;
316 
317 	SC_DEBUG(sc_link, SDEV_DB2, ("uha_scsi_cmd\n"));
318 	/*
319 	 * get a mscp (mbox-out) to use. If the transfer
320 	 * is from a buf (possibly from interrupt time)
321 	 * then we can't allow it to sleep
322 	 */
323 	flags = xs->flags;
324 	mscp = xs->io;
325 
326 	mscp->xs = xs;
327 	mscp->timeout = xs->timeout;
328 	timeout_set(&xs->stimeout, uha_timeout, xs);
329 
330 	/*
331 	 * Put all the arguments for the xfer in the mscp
332 	 */
333 	if (flags & SCSI_RESET) {
334 		mscp->opcode = UHA_SDR;
335 		mscp->ca = 0x01;
336 	} else {
337 		mscp->opcode = UHA_TSP;
338 		/* XXX Not for tapes. */
339 		mscp->ca = 0x01;
340 		bcopy(xs->cmd, &mscp->scsi_cmd, mscp->scsi_cmd_length);
341 	}
342 	mscp->xdir = UHA_SDET;
343 	mscp->dcn = 0x00;
344 	mscp->chan = 0x00;
345 	mscp->target = sc_link->target;
346 	mscp->lun = sc_link->lun;
347 	mscp->scsi_cmd_length = xs->cmdlen;
348 	mscp->sense_ptr = KVTOPHYS(&mscp->mscp_sense);
349 	mscp->req_sense_length = sizeof(mscp->mscp_sense);
350 	mscp->host_stat = 0x00;
351 	mscp->target_stat = 0x00;
352 
353 	if (xs->datalen) {
354 		sg = mscp->uha_dma;
355 		seg = 0;
356 
357 		/*
358 		 * Set up the scatter gather block
359 		 */
360 		SC_DEBUG(sc_link, SDEV_DB4,
361 		    ("%d @0x%x:- ", xs->datalen, xs->data));
362 		datalen = xs->datalen;
363 		thiskv = (int) xs->data;
364 		thisphys = KVTOPHYS(thiskv);
365 
366 		while (datalen && seg < UHA_NSEG) {
367 			bytes_this_seg = 0;
368 
369 			/* put in the base address */
370 			sg->seg_addr = thisphys;
371 
372 			SC_DEBUGN(sc_link, SDEV_DB4, ("0x%x", thisphys));
373 
374 			/* do it at least once */
375 			nextphys = thisphys;
376 			while (datalen && thisphys == nextphys) {
377 				/*
378 				 * This page is contiguous (physically)
379 				 * with the last, just extend the
380 				 * length
381 				 */
382 				/* how far to the end of the page */
383 				nextphys = (thisphys & ~PGOFSET) + NBPG;
384 				bytes_this_page = nextphys - thisphys;
385 				/**** or the data ****/
386 				bytes_this_page = min(bytes_this_page,
387 						      datalen);
388 				bytes_this_seg += bytes_this_page;
389 				datalen -= bytes_this_page;
390 
391 				/* get more ready for the next page */
392 				thiskv = (thiskv & ~PGOFSET) + NBPG;
393 				if (datalen)
394 					thisphys = KVTOPHYS(thiskv);
395 			}
396 			/*
397 			 * next page isn't contiguous, finish the seg
398 			 */
399 			SC_DEBUGN(sc_link, SDEV_DB4,
400 			    ("(0x%x)", bytes_this_seg));
401 			sg->seg_len = bytes_this_seg;
402 			sg++;
403 			seg++;
404 		}
405 
406 		SC_DEBUGN(sc_link, SDEV_DB4, ("\n"));
407 		if (datalen) {
408 			/*
409 			 * there's still data, must have run out of segs!
410 			 */
411 			printf("%s: uha_scsi_cmd, more than %d dma segs\n",
412 			    sc->sc_dev.dv_xname, UHA_NSEG);
413 			goto bad;
414 		}
415 		mscp->data_addr = KVTOPHYS(mscp->uha_dma);
416 		mscp->data_length = xs->datalen;
417 		mscp->sgth = 0x01;
418 		mscp->sg_num = seg;
419 	} else {		/* No data xfer, use non S/G values */
420 		mscp->data_addr = (physaddr)0;
421 		mscp->data_length = 0;
422 		mscp->sgth = 0x00;
423 		mscp->sg_num = 0;
424 	}
425 	mscp->link_id = 0;
426 	mscp->link_addr = (physaddr)0;
427 
428 	s = splbio();
429 	(sc->start_mbox)(sc, mscp);
430 	splx(s);
431 
432 	/*
433 	 * Usually return SUCCESSFULLY QUEUED
434 	 */
435 	if ((flags & SCSI_POLL) == 0)
436 		return;
437 
438 	/*
439 	 * If we can't use interrupts, poll on completion
440 	 */
441 	if ((sc->poll)(sc, xs, mscp->timeout)) {
442 		uha_timeout(mscp);
443 		if ((sc->poll)(sc, xs, mscp->timeout))
444 			uha_timeout(mscp);
445 	}
446 	return;
447 
448 bad:
449 	xs->error = XS_DRIVER_STUFFUP;
450 	scsi_done(xs);
451 	return;
452 }
453 
454 void
455 uha_timeout(arg)
456 	void *arg;
457 {
458 	struct uha_mscp *mscp = arg;
459 	struct scsi_xfer *xs = mscp->xs;
460 	struct scsi_link *sc_link = xs->sc_link;
461 	struct uha_softc *sc = sc_link->adapter_softc;
462 	int s;
463 
464 	sc_print_addr(sc_link);
465 	printf("timed out");
466 
467 	s = splbio();
468 
469 	if (mscp->flags & MSCP_ABORT) {
470 		/* abort timed out */
471 		printf(" AGAIN\n");
472 		/* XXX Must reset! */
473 	} else {
474 		/* abort the operation that has timed out */
475 		printf("\n");
476 		mscp->xs->error = XS_TIMEOUT;
477 		mscp->timeout = UHA_ABORT_TIMEOUT;
478 		mscp->flags |= MSCP_ABORT;
479 		(sc->start_mbox)(sc, mscp);
480 	}
481 
482 	splx(s);
483 }
484