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