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