1 /* $NetBSD: ncr.c,v 1.46 2008/04/28 20:23:39 martin Exp $ */
4 * Copyright (c) 1996 The NetBSD Foundation, Inc.
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Adam Glass, David Jones, Gordon W. Ross, and Jens A. Nilsson.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
33 * This file contains the machine-dependent parts of the NCR-5380
34 * controller. The machine-independent parts are in ncr5380sbc.c.
40 * This code is based on arch/sun3/dev/si*
41 * Written by David Jones, Gordon Ross, and Adam Glass.
44 #include <sys/cdefs.h>
45 __KERNEL_RCSID(0, "$NetBSD: ncr.c,v 1.46 2008/04/28 20:23:39 martin Exp $");
47 #include <sys/param.h>
48 #include <sys/systm.h>
49 #include <sys/errno.h>
50 #include <sys/kernel.h>
51 #include <sys/malloc.h>
52 #include <sys/device.h>
56 #include <dev/scsipi/scsi_all.h>
57 #include <dev/scsipi/scsipi_all.h>
58 #include <dev/scsipi/scsipi_debug.h>
59 #include <dev/scsipi/scsiconf.h>
61 #include <dev/ic/ncr5380reg.h>
62 #include <dev/ic/ncr5380var.h>
64 #include <machine/cpu.h>
65 #include <machine/vsbus.h>
66 #include <machine/bus.h>
67 #include <machine/sid.h>
68 #include <machine/scb.h>
69 #include <machine/clock.h>
73 #define MIN_DMA_LEN 128
75 struct si_dma_handle
{
85 struct ncr5380_softc ncr_sc
;
86 struct evcnt ncr_intrcnt
;
92 struct si_dma_handle ncr_dma
[SCI_OPENINGS
];
93 struct vsbus_dma sc_vd
;
94 int onlyscsi
; /* This machine needs no queueing */
97 static int ncr_dmasize
;
99 static int si_vsbus_match(device_t
, cfdata_t
, void *);
100 static void si_vsbus_attach(device_t
, device_t
, void *);
101 static void si_minphys(struct buf
*);
103 static void si_dma_alloc(struct ncr5380_softc
*);
104 static void si_dma_free(struct ncr5380_softc
*);
105 static void si_dma_setup(struct ncr5380_softc
*);
106 static void si_dma_start(struct ncr5380_softc
*);
107 static void si_dma_poll(struct ncr5380_softc
*);
108 static void si_dma_eop(struct ncr5380_softc
*);
109 static void si_dma_stop(struct ncr5380_softc
*);
110 static void si_dma_go(void *);
112 CFATTACH_DECL_NEW(si_vsbus
, sizeof(struct si_softc
),
113 si_vsbus_match
, si_vsbus_attach
, NULL
, NULL
);
116 si_vsbus_match(device_t parent
, cfdata_t cf
, void *aux
)
118 struct vsbus_attach_args
* const va
= aux
;
119 volatile char *si_csr
= (char *) va
->va_addr
;
121 if (vax_boardtype
== VAX_BTYP_49
|| vax_boardtype
== VAX_BTYP_46
122 || vax_boardtype
== VAX_BTYP_48
|| vax_boardtype
== VAX_BTYP_53
)
124 /* This is the way Linux autoprobes the interrupt MK-990321 */
128 si_csr
[4] = 5; /* 0xcf */
134 si_vsbus_attach(device_t parent
, device_t self
, void *aux
)
136 struct vsbus_attach_args
* const va
= aux
;
137 struct si_softc
* const sc
= device_private(self
);
138 struct ncr5380_softc
* const ncr_sc
= &sc
->ncr_sc
;
141 ncr_sc
->sc_dev
= self
;
143 scb_vecalloc(va
->va_cvec
, (void (*)(void *)) ncr5380_intr
, sc
,
144 SCB_ISTACK
, &sc
->ncr_intrcnt
);
145 evcnt_attach_dynamic(&sc
->ncr_intrcnt
, EVCNT_TYPE_INTR
, NULL
,
146 device_xname(self
), "intr");
150 * On VS3100, split the 128K block between the two devices.
151 * On VS2000, don't care for now.
153 #define DMASIZE (64*1024)
154 if (va
->va_paddr
& 0x100) { /* Secondary SCSI controller */
155 sc
->ncr_off
= DMASIZE
;
158 sc
->ncr_addr
= (void *)va
->va_dmaaddr
;
159 ncr_dmasize
= min(va
->va_dmasize
, MAXPHYS
);
162 * MD function pointers used by the MI code.
164 ncr_sc
->sc_dma_alloc
= si_dma_alloc
;
165 ncr_sc
->sc_dma_free
= si_dma_free
;
166 ncr_sc
->sc_dma_setup
= si_dma_setup
;
167 ncr_sc
->sc_dma_start
= si_dma_start
;
168 ncr_sc
->sc_dma_poll
= si_dma_poll
;
169 ncr_sc
->sc_dma_eop
= si_dma_eop
;
170 ncr_sc
->sc_dma_stop
= si_dma_stop
;
172 /* DMA control register offsets */
173 sc
->ncr_dmaaddr
= 32; /* DMA address in buffer, longword */
174 sc
->ncr_dmacount
= 64; /* DMA count register */
175 sc
->ncr_dmadir
= 68; /* Direction of DMA transfer */
177 ncr_sc
->sc_pio_out
= ncr5380_pio_out
;
178 ncr_sc
->sc_pio_in
= ncr5380_pio_in
;
180 ncr_sc
->sc_min_dma_len
= MIN_DMA_LEN
;
183 * Initialize fields used by the MI code.
185 /* ncr_sc->sc_regt = Unused on VAX */
186 ncr_sc
->sc_regh
= vax_map_physmem(va
->va_paddr
, 1);
188 /* Register offsets */
198 ncr_sc
->sc_rev
= NCR_VARIANT_NCR5380
;
200 ncr_sc
->sc_no_disconnect
= 0xff;
203 * Get the SCSI chip target address out of NVRAM.
204 * This do not apply to the VS2000.
206 tweak
= clk_tweak
+ (va
->va_paddr
& 0x100 ? 3 : 0);
207 if (vax_boardtype
== VAX_BTYP_410
)
210 target
= (clk_page
[0xbc/2] >> tweak
) & 7;
213 aprint_normal_dev(self
, "NCR5380, SCSI ID %d\n", target
);
215 ncr_sc
->sc_adapter
.adapt_minphys
= si_minphys
;
216 ncr_sc
->sc_channel
.chan_id
= target
;
219 * Init the vsbus DMA resource queue struct */
220 sc
->sc_vd
.vd_go
= si_dma_go
;
221 sc
->sc_vd
.vd_arg
= sc
;
224 * Initialize si board itself.
226 ncr5380_attach(ncr_sc
);
230 * Adjust the max transfer size. The DMA buffer is only 16k on VS2000.
233 si_minphys(struct buf
*bp
)
235 if (bp
->b_bcount
> ncr_dmasize
)
236 bp
->b_bcount
= ncr_dmasize
;
240 si_dma_alloc(struct ncr5380_softc
*ncr_sc
)
242 struct si_softc
*sc
= (struct si_softc
*)ncr_sc
;
243 struct sci_req
*sr
= ncr_sc
->sc_current
;
244 struct scsipi_xfer
*xs
= sr
->sr_xs
;
245 struct si_dma_handle
*dh
;
249 if (sr
->sr_dma_hand
!= NULL
)
250 panic("si_dma_alloc: already have DMA handle");
253 /* Polled transfers shouldn't allocate a DMA handle. */
254 if (sr
->sr_flags
& SR_IMMED
)
257 xlen
= ncr_sc
->sc_datalen
;
259 /* Make sure our caller checked sc_min_dma_len. */
260 if (xlen
< MIN_DMA_LEN
)
261 panic("si_dma_alloc: len=0x%x", xlen
);
264 * Find free PDMA handle. Guaranteed to find one since we
265 * have as many PDMA handles as the driver has processes.
268 for (i
= 0; i
< SCI_OPENINGS
; i
++) {
269 if ((sc
->ncr_dma
[i
].dh_flags
& SIDH_BUSY
) == 0)
272 panic("sbc: no free PDMA handles");
274 dh
= &sc
->ncr_dma
[i
];
275 dh
->dh_flags
= SIDH_BUSY
;
276 dh
->dh_addr
= ncr_sc
->sc_dataptr
;
278 dh
->dh_proc
= xs
->bp
->b_proc
;
280 /* Remember dest buffer parameters */
281 if (xs
->xs_control
& XS_CTL_DATA_OUT
)
282 dh
->dh_flags
|= SIDH_OUT
;
284 sr
->sr_dma_hand
= dh
;
288 si_dma_free(struct ncr5380_softc
*ncr_sc
)
290 struct sci_req
*sr
= ncr_sc
->sc_current
;
291 struct si_dma_handle
*dh
= sr
->sr_dma_hand
;
293 if (dh
->dh_flags
& SIDH_BUSY
)
296 printf("si_dma_free: free'ing unused buffer\n");
298 sr
->sr_dma_hand
= NULL
;
302 si_dma_setup(struct ncr5380_softc
*ncr_sc
)
304 /* Do nothing here */
308 si_dma_start(struct ncr5380_softc
*ncr_sc
)
310 struct si_softc
*sc
= (struct si_softc
*)ncr_sc
;
312 /* Just put on queue; will call go() from below */
316 vsbus_dma_start(&sc
->sc_vd
);
320 * go() routine called when another transfer somewhere is finished.
325 struct ncr5380_softc
*ncr_sc
= arg
;
326 struct si_softc
*sc
= (struct si_softc
*)ncr_sc
;
327 struct sci_req
*sr
= ncr_sc
->sc_current
;
328 struct si_dma_handle
*dh
= sr
->sr_dma_hand
;
331 * Set the VAX-DMA-specific registers, and copy the data if
332 * it is directed "outbound".
334 if (dh
->dh_flags
& SIDH_OUT
) {
335 vsbus_copyfromproc(dh
->dh_proc
, dh
->dh_addr
,
336 (char *)sc
->ncr_addr
+ sc
->ncr_off
, dh
->dh_len
);
337 bus_space_write_1(ncr_sc
->sc_regt
, ncr_sc
->sc_regh
,
340 bus_space_write_1(ncr_sc
->sc_regt
, ncr_sc
->sc_regh
,
343 bus_space_write_4(ncr_sc
->sc_regt
, ncr_sc
->sc_regh
,
344 sc
->ncr_dmacount
, -dh
->dh_len
);
345 bus_space_write_4(ncr_sc
->sc_regt
, ncr_sc
->sc_regh
,
346 sc
->ncr_dmaaddr
, sc
->ncr_off
);
348 * Now from the 5380-internal DMA registers.
350 if (dh
->dh_flags
& SIDH_OUT
) {
351 NCR5380_WRITE(ncr_sc
, sci_tcmd
, PHASE_DATA_OUT
);
352 NCR5380_WRITE(ncr_sc
, sci_icmd
, SCI_ICMD_DATA
);
353 NCR5380_WRITE(ncr_sc
, sci_mode
, NCR5380_READ(ncr_sc
, sci_mode
)
354 | SCI_MODE_DMA
| SCI_MODE_DMA_IE
);
355 NCR5380_WRITE(ncr_sc
, sci_dma_send
, 0);
357 NCR5380_WRITE(ncr_sc
, sci_tcmd
, PHASE_DATA_IN
);
358 NCR5380_WRITE(ncr_sc
, sci_icmd
, 0);
359 NCR5380_WRITE(ncr_sc
, sci_mode
, NCR5380_READ(ncr_sc
, sci_mode
)
360 | SCI_MODE_DMA
| SCI_MODE_DMA_IE
);
361 NCR5380_WRITE(ncr_sc
, sci_irecv
, 0);
363 ncr_sc
->sc_state
|= NCR_DOINGDMA
;
370 si_dma_poll(struct ncr5380_softc
*ncr_sc
)
372 printf("si_dma_poll\n");
379 si_dma_eop(struct ncr5380_softc
*ncr_sc
)
381 printf("si_dma_eop\n");
385 si_dma_stop(struct ncr5380_softc
*ncr_sc
)
387 struct si_softc
*sc
= (struct si_softc
*)ncr_sc
;
388 struct sci_req
*sr
= ncr_sc
->sc_current
;
389 struct si_dma_handle
*dh
= sr
->sr_dma_hand
;
392 if (ncr_sc
->sc_state
& NCR_DOINGDMA
)
393 ncr_sc
->sc_state
&= ~NCR_DOINGDMA
;
396 * Sometimes the FIFO buffer isn't drained when the
397 * interrupt is posted. Just loop here and hope that
398 * it will drain soon.
400 for (i
= 0; i
< 20000; i
++) {
401 count
= bus_space_read_4(ncr_sc
->sc_regt
,
402 ncr_sc
->sc_regh
, sc
->ncr_dmacount
);
408 if (((dh
->dh_flags
& SIDH_OUT
) == 0)) {
409 vsbus_copytoproc(dh
->dh_proc
,
410 (char *)sc
->ncr_addr
+ sc
->ncr_off
,
411 dh
->dh_addr
, dh
->dh_len
);
413 ncr_sc
->sc_dataptr
+= dh
->dh_len
;
414 ncr_sc
->sc_datalen
-= dh
->dh_len
;
417 NCR5380_WRITE(ncr_sc
, sci_mode
, NCR5380_READ(ncr_sc
, sci_mode
) &
418 ~(SCI_MODE_DMA
| SCI_MODE_DMA_IE
));
419 NCR5380_WRITE(ncr_sc
, sci_icmd
, 0);
420 if (sc
->onlyscsi
== 0)
421 vsbus_dma_intr(); /* Try to start more transfers */