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