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