1 /* $NetBSD: uha.c,v 1.43 2009/03/14 15:36:17 dsl Exp $ */
4 * Copyright (c) 1997, 1998 The NetBSD Foundation, Inc.
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Charles M. Hannum and by Jason R. Thorpe of the Numerical Aerospace
9 * Simulation Facility, NASA Ames Research Center.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
20 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
21 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
24 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 * POSSIBILITY OF SUCH DAMAGE.
34 * Ported for use with the UltraStor 14f by Gary Close (gclose@wvnvms.wvnet.edu)
35 * Slight fixes to timeouts to run with the 34F
36 * Thanks to Julian Elischer for advice and help with this port.
38 * Originally written by Julian Elischer (julian@tfs.com)
39 * for TRW Financial Systems for use under the MACH(2.5) operating system.
41 * TRW Financial Systems, in accordance with their agreement with Carnegie
42 * Mellon University, makes this software available to CMU to distribute
43 * or use in any manner that they see fit as long as this message is kept with
44 * the software. For this reason TFS also grants any other persons or
45 * organisations permission to use or modify this software.
47 * TFS supplies this software to be publicly redistributed
48 * on the understanding that TFS is not responsible for the correct
49 * functioning of this software in any circumstances.
51 * commenced: Sun Sep 27 18:14:01 PDT 1992
52 * slight mod to make work with 34F as well: Wed Jun 2 18:05:48 WST 1993
55 #include <sys/cdefs.h>
56 __KERNEL_RCSID(0, "$NetBSD: uha.c,v 1.43 2009/03/14 15:36:17 dsl Exp $");
62 #define integrate static inline
65 #include <sys/param.h>
66 #include <sys/systm.h>
67 #include <sys/kernel.h>
68 #include <sys/errno.h>
69 #include <sys/ioctl.h>
70 #include <sys/device.h>
71 #include <sys/malloc.h>
75 #include <uvm/uvm_extern.h>
80 #include <dev/scsipi/scsi_all.h>
81 #include <dev/scsipi/scsipi_all.h>
82 #include <dev/scsipi/scsiconf.h>
84 #include <dev/ic/uhareg.h>
85 #include <dev/ic/uhavar.h>
88 #define Debugger() panic("should call debugger here (uha.c)")
91 #define UHA_MAXXFER ((UHA_NSEG - 1) << PGSHIFT)
93 integrate
void uha_reset_mscp(struct uha_softc
*, struct uha_mscp
*);
94 void uha_free_mscp(struct uha_softc
*, struct uha_mscp
*);
95 integrate
int uha_init_mscp(struct uha_softc
*, struct uha_mscp
*);
96 struct uha_mscp
*uha_get_mscp(struct uha_softc
*);
97 void uhaminphys(struct buf
*);
98 void uha_scsipi_request(struct scsipi_channel
*, scsipi_adapter_req_t
, void *);
99 int uha_create_mscps(struct uha_softc
*, struct uha_mscp
*, int);
101 #define UHA_ABORT_TIMEOUT 2000 /* time to wait for abort (mSec) */
104 * Attach all the sub-devices we can find
107 uha_attach(struct uha_softc
*sc
, struct uha_probe_data
*upd
)
109 struct scsipi_adapter
*adapt
= &sc
->sc_adapter
;
110 struct scsipi_channel
*chan
= &sc
->sc_channel
;
111 bus_dma_segment_t seg
;
114 TAILQ_INIT(&sc
->sc_free_mscp
);
119 * Fill in the scsipi_adapter.
121 memset(adapt
, 0, sizeof(*adapt
));
122 adapt
->adapt_dev
= &sc
->sc_dev
;
123 adapt
->adapt_nchannels
= 1;
124 /* adapt_openings initialized below */
125 /* adapt_max_periph initialized below */
126 adapt
->adapt_request
= uha_scsipi_request
;
127 adapt
->adapt_minphys
= uhaminphys
;
130 * Fill in the scsipi_channel.
132 memset(chan
, 0, sizeof(*chan
));
133 chan
->chan_adapter
= adapt
;
134 chan
->chan_bustype
= &scsi_bustype
;
135 chan
->chan_channel
= 0;
136 chan
->chan_ntargets
= 8;
137 chan
->chan_nluns
= 8;
138 chan
->chan_id
= upd
->sc_scsi_dev
;
140 #define MSCPSIZE (UHA_MSCP_MAX * sizeof(struct uha_mscp))
143 * Allocate the MSCPs.
145 if ((error
= bus_dmamem_alloc(sc
->sc_dmat
, MSCPSIZE
,
146 PAGE_SIZE
, 0, &seg
, 1, &rseg
, BUS_DMA_NOWAIT
)) != 0) {
147 aprint_error_dev(&sc
->sc_dev
, "unable to allocate mscps, error = %d\n",
151 if ((error
= bus_dmamem_map(sc
->sc_dmat
, &seg
, rseg
,
152 MSCPSIZE
, (void **)&sc
->sc_mscps
,
153 BUS_DMA_NOWAIT
|BUS_DMA_COHERENT
)) != 0) {
154 aprint_error_dev(&sc
->sc_dev
, "unable to map mscps, error = %d\n",
160 * Create and load the DMA map used for the mscps.
162 if ((error
= bus_dmamap_create(sc
->sc_dmat
, MSCPSIZE
,
163 1, MSCPSIZE
, 0, BUS_DMA_NOWAIT
| sc
->sc_dmaflags
,
164 &sc
->sc_dmamap_mscp
)) != 0) {
165 aprint_error_dev(&sc
->sc_dev
, "unable to create mscp DMA map, error = %d\n",
169 if ((error
= bus_dmamap_load(sc
->sc_dmat
, sc
->sc_dmamap_mscp
,
170 sc
->sc_mscps
, MSCPSIZE
, NULL
, BUS_DMA_NOWAIT
)) != 0) {
171 aprint_error_dev(&sc
->sc_dev
, "unable to load mscp DMA map, error = %d\n",
179 * Initialize the mscps.
181 i
= uha_create_mscps(sc
, sc
->sc_mscps
, UHA_MSCP_MAX
);
183 aprint_error_dev(&sc
->sc_dev
, "unable to create mscps\n");
185 } else if (i
!= UHA_MSCP_MAX
) {
186 aprint_error_dev(&sc
->sc_dev
, "WARNING: only %d of %d mscps created\n",
190 adapt
->adapt_openings
= i
;
191 adapt
->adapt_max_periph
= adapt
->adapt_openings
;
194 * ask the adapter what subunits are present
196 config_found(&sc
->sc_dev
, &sc
->sc_channel
, scsiprint
);
200 uha_reset_mscp(struct uha_softc
*sc
, struct uha_mscp
*mscp
)
207 * A mscp (and hence a mbx-out) is put onto the free list.
210 uha_free_mscp(struct uha_softc
*sc
, struct uha_mscp
*mscp
)
215 uha_reset_mscp(sc
, mscp
);
216 TAILQ_INSERT_HEAD(&sc
->sc_free_mscp
, mscp
, chain
);
221 uha_init_mscp(struct uha_softc
*sc
, struct uha_mscp
*mscp
)
223 bus_dma_tag_t dmat
= sc
->sc_dmat
;
227 * Create the DMA map for this MSCP.
229 error
= bus_dmamap_create(dmat
, UHA_MAXXFER
, UHA_NSEG
, UHA_MAXXFER
,
230 0, BUS_DMA_NOWAIT
|BUS_DMA_ALLOCNOW
| sc
->sc_dmaflags
,
233 aprint_error_dev(&sc
->sc_dev
, "can't create mscp DMA map, error = %d\n",
239 * put in the phystokv hash table
240 * Never gets taken out.
242 mscp
->hashkey
= sc
->sc_dmamap_mscp
->dm_segs
[0].ds_addr
+
244 hashnum
= MSCP_HASH(mscp
->hashkey
);
245 mscp
->nexthash
= sc
->sc_mscphash
[hashnum
];
246 sc
->sc_mscphash
[hashnum
] = mscp
;
247 uha_reset_mscp(sc
, mscp
);
252 * Create a set of MSCPs and add them to the free list.
255 uha_create_mscps(struct uha_softc
*sc
, struct uha_mscp
*mscpstore
, int count
)
257 struct uha_mscp
*mscp
;
260 memset(mscpstore
, 0, sizeof(struct uha_mscp
) * count
);
261 for (i
= 0; i
< count
; i
++) {
262 mscp
= &mscpstore
[i
];
263 if ((error
= uha_init_mscp(sc
, mscp
)) != 0) {
264 aprint_error_dev(&sc
->sc_dev
, "unable to initialize mscp, error = %d\n",
268 TAILQ_INSERT_TAIL(&sc
->sc_free_mscp
, mscp
, chain
);
277 * If there are none, see if we can allocate a new one. If so, put it in the
278 * hash table too otherwise either return an error or sleep.
281 uha_get_mscp(struct uha_softc
*sc
)
283 struct uha_mscp
*mscp
;
287 mscp
= TAILQ_FIRST(&sc
->sc_free_mscp
);
289 TAILQ_REMOVE(&sc
->sc_free_mscp
, mscp
, chain
);
290 mscp
->flags
|= MSCP_ALLOC
;
297 * given a physical address, find the mscp that it corresponds to.
300 uha_mscp_phys_kv(struct uha_softc
*sc
, u_long mscp_phys
)
302 int hashnum
= MSCP_HASH(mscp_phys
);
303 struct uha_mscp
*mscp
= sc
->sc_mscphash
[hashnum
];
306 if (mscp
->hashkey
== mscp_phys
)
308 mscp
= mscp
->nexthash
;
314 * We have a mscp which has been processed by the adaptor, now we look to see
315 * how the operation went.
318 uha_done(struct uha_softc
*sc
, struct uha_mscp
*mscp
)
320 bus_dma_tag_t dmat
= sc
->sc_dmat
;
321 struct scsi_sense_data
*s1
, *s2
;
322 struct scsipi_xfer
*xs
= mscp
->xs
;
324 SC_DEBUG(xs
->xs_periph
, SCSIPI_DB2
, ("uha_done\n"));
326 bus_dmamap_sync(dmat
, sc
->sc_dmamap_mscp
,
327 UHA_MSCP_OFF(mscp
), sizeof(struct uha_mscp
),
328 BUS_DMASYNC_POSTREAD
|BUS_DMASYNC_POSTWRITE
);
331 * If we were a data transfer, unload the map that described
335 bus_dmamap_sync(dmat
, mscp
->dmamap_xfer
, 0,
336 mscp
->dmamap_xfer
->dm_mapsize
,
337 (xs
->xs_control
& XS_CTL_DATA_IN
) ? BUS_DMASYNC_POSTREAD
:
338 BUS_DMASYNC_POSTWRITE
);
339 bus_dmamap_unload(dmat
, mscp
->dmamap_xfer
);
343 * Otherwise, put the results of the operation
344 * into the xfer and call whoever started it
346 if ((mscp
->flags
& MSCP_ALLOC
) == 0) {
347 aprint_error_dev(&sc
->sc_dev
, "exiting ccb not allocated!\n");
351 if (xs
->error
== XS_NOERROR
) {
352 if (mscp
->host_stat
!= UHA_NO_ERR
) {
353 switch (mscp
->host_stat
) {
354 case UHA_SBUS_TIMEOUT
: /* No response */
355 xs
->error
= XS_SELTIMEOUT
;
357 default: /* Other scsi protocol messes */
358 aprint_error_dev(&sc
->sc_dev
, "host_stat %x\n",
360 xs
->error
= XS_DRIVER_STUFFUP
;
362 } else if (mscp
->target_stat
!= SCSI_OK
) {
363 switch (mscp
->target_stat
) {
365 s1
= &mscp
->mscp_sense
;
366 s2
= &xs
->sense
.scsi_sense
;
368 xs
->error
= XS_SENSE
;
374 aprint_error_dev(&sc
->sc_dev
, "target_stat %x\n",
376 xs
->error
= XS_DRIVER_STUFFUP
;
381 uha_free_mscp(sc
, mscp
);
386 uhaminphys(struct buf
*bp
)
389 if (bp
->b_bcount
> UHA_MAXXFER
)
390 bp
->b_bcount
= UHA_MAXXFER
;
395 * start a scsi operation given the command and the data address. Also
396 * needs the unit, target and lu.
400 uha_scsipi_request(struct scsipi_channel
*chan
, scsipi_adapter_req_t req
, void *arg
)
402 struct scsipi_xfer
*xs
;
403 struct scsipi_periph
*periph
;
404 struct uha_softc
*sc
= (void *)chan
->chan_adapter
->adapt_dev
;
405 bus_dma_tag_t dmat
= sc
->sc_dmat
;
406 struct uha_mscp
*mscp
;
407 int error
, seg
, flags
, s
;
411 case ADAPTER_REQ_RUN_XFER
:
413 periph
= xs
->xs_periph
;
414 flags
= xs
->xs_control
;
416 SC_DEBUG(periph
, SCSIPI_DB2
, ("uha_scsipi_request\n"));
418 /* Get an MSCP to use. */
419 mscp
= uha_get_mscp(sc
);
422 * This should never happen as we track the resources
426 scsipi_printaddr(periph
);
427 printf("unable to allocate mscp\n");
428 panic("uha_scsipi_request");
433 mscp
->timeout
= xs
->timeout
;
436 * Put all the arguments for the xfer in the mscp
438 if (flags
& XS_CTL_RESET
) {
439 mscp
->opcode
= UHA_SDR
;
442 if (xs
->cmdlen
> sizeof(mscp
->scsi_cmd
)) {
443 aprint_error_dev(&sc
->sc_dev
, "cmdlen %d too large for MSCP\n",
445 xs
->error
= XS_DRIVER_STUFFUP
;
448 mscp
->opcode
= UHA_TSP
;
449 /* XXX Not for tapes. */
451 memcpy(&mscp
->scsi_cmd
, xs
->cmd
, mscp
->scsi_cmd_length
);
453 mscp
->xdir
= UHA_SDET
;
456 mscp
->target
= periph
->periph_target
;
457 mscp
->lun
= periph
->periph_lun
;
458 mscp
->scsi_cmd_length
= xs
->cmdlen
;
459 mscp
->sense_ptr
= sc
->sc_dmamap_mscp
->dm_segs
[0].ds_addr
+
460 UHA_MSCP_OFF(mscp
) + offsetof(struct uha_mscp
, mscp_sense
);
461 mscp
->req_sense_length
= sizeof(mscp
->mscp_sense
);
462 mscp
->host_stat
= 0x00;
463 mscp
->target_stat
= 0x00;
468 if (flags
& SCSI_DATA_UIO
) {
469 error
= bus_dmamap_load_uio(dmat
,
470 mscp
->dmamap_xfer
, (struct uio
*)xs
->data
,
471 ((flags
& XS_CTL_NOSLEEP
) ? BUS_DMA_NOWAIT
:
472 BUS_DMA_WAITOK
) | BUS_DMA_STREAMING
|
473 ((flags
& XS_CTL_DATA_IN
) ? BUS_DMA_READ
:
478 error
= bus_dmamap_load(dmat
,
479 mscp
->dmamap_xfer
, xs
->data
, xs
->datalen
,
481 ((flags
& XS_CTL_NOSLEEP
) ? BUS_DMA_NOWAIT
:
482 BUS_DMA_WAITOK
) | BUS_DMA_STREAMING
|
483 ((flags
& XS_CTL_DATA_IN
) ? BUS_DMA_READ
:
493 xs
->error
= XS_RESOURCE_SHORTAGE
;
497 xs
->error
= XS_DRIVER_STUFFUP
;
498 aprint_error_dev(&sc
->sc_dev
, "error %d loading DMA map\n",
501 uha_free_mscp(sc
, mscp
);
506 bus_dmamap_sync(dmat
, mscp
->dmamap_xfer
, 0,
507 mscp
->dmamap_xfer
->dm_mapsize
,
508 (flags
& XS_CTL_DATA_IN
) ? BUS_DMASYNC_PREREAD
:
509 BUS_DMASYNC_PREWRITE
);
512 * Load the hardware scatter/gather map with the
513 * contents of the DMA map.
516 seg
< mscp
->dmamap_xfer
->dm_nsegs
; seg
++) {
517 mscp
->uha_dma
[seg
].seg_addr
=
518 mscp
->dmamap_xfer
->dm_segs
[seg
].ds_addr
;
519 mscp
->uha_dma
[seg
].seg_len
=
520 mscp
->dmamap_xfer
->dm_segs
[seg
].ds_len
;
524 sc
->sc_dmamap_mscp
->dm_segs
[0].ds_addr
+
525 UHA_MSCP_OFF(mscp
) + offsetof(struct uha_mscp
,
527 mscp
->data_length
= xs
->datalen
;
530 } else { /* No data xfer, use non S/G values */
531 mscp
->data_addr
= (physaddr
)0;
532 mscp
->data_length
= 0;
537 mscp
->link_addr
= (physaddr
)0;
539 bus_dmamap_sync(sc
->sc_dmat
, sc
->sc_dmamap_mscp
,
540 UHA_MSCP_OFF(mscp
), sizeof(struct uha_mscp
),
541 BUS_DMASYNC_PREREAD
|BUS_DMASYNC_PREWRITE
);
544 (sc
->start_mbox
)(sc
, mscp
);
547 if ((flags
& XS_CTL_POLL
) == 0)
551 * If we can't use interrupts, poll on completion
553 if ((sc
->poll
)(sc
, xs
, mscp
->timeout
)) {
555 if ((sc
->poll
)(sc
, xs
, mscp
->timeout
))
560 case ADAPTER_REQ_GROW_RESOURCES
:
561 /* XXX Not supported. */
564 case ADAPTER_REQ_SET_XFER_MODE
:
566 * We can't really do this (the UltraStor controllers
567 * have their own config).
569 * XXX How do we query the config?
575 uha_timeout(void *arg
)
577 struct uha_mscp
*mscp
= arg
;
578 struct scsipi_xfer
*xs
= mscp
->xs
;
579 struct scsipi_periph
*periph
= xs
->xs_periph
;
580 struct uha_softc
*sc
=
581 (void *)periph
->periph_channel
->chan_adapter
->adapt_dev
;
584 scsipi_printaddr(periph
);
589 if (mscp
->flags
& MSCP_ABORT
) {
590 /* abort timed out */
592 /* XXX Must reset! */
594 /* abort the operation that has timed out */
596 mscp
->xs
->error
= XS_TIMEOUT
;
597 mscp
->timeout
= UHA_ABORT_TIMEOUT
;
598 mscp
->flags
|= MSCP_ABORT
;
599 (sc
->start_mbox
)(sc
, mscp
);