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