1 /* $NetBSD: asc_ioasic.c,v 1.22 2009/08/12 14:06:13 tsutsui Exp $ */
4 * Copyright (c) 2000 The NetBSD Foundation, Inc.
7 * This code is derived from software contributed to The NetBSD Foundation
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.
32 #include <sys/cdefs.h> /* RCS ID & Copyright macro defns */
33 __KERNEL_RCSID(0, "$NetBSD: asc_ioasic.c,v 1.22 2009/08/12 14:06:13 tsutsui Exp $");
35 #include <sys/types.h>
36 #include <sys/param.h>
37 #include <sys/systm.h>
38 #include <sys/device.h>
41 #include <uvm/uvm_extern.h>
43 #include <dev/scsipi/scsi_all.h>
44 #include <dev/scsipi/scsipi_all.h>
45 #include <dev/scsipi/scsiconf.h>
46 #include <dev/scsipi/scsi_message.h>
48 #include <machine/bus.h>
50 #include <dev/ic/ncr53c9xreg.h>
51 #include <dev/ic/ncr53c9xvar.h>
53 #include <dev/tc/tcvar.h>
54 #include <dev/tc/ioasicvar.h>
55 #include <dev/tc/ioasicreg.h>
58 struct ncr53c9x_softc sc_ncr53c9x
; /* glue to MI code */
59 bus_space_tag_t sc_bst
; /* bus space tag */
60 bus_space_handle_t sc_bsh
; /* bus space handle */
61 bus_space_handle_t sc_scsi_bsh
; /* ASC register handle */
62 bus_dma_tag_t sc_dmat
; /* bus dma tag */
63 bus_dmamap_t sc_dmamap
; /* bus dmamap */
67 unsigned int sc_flags
;
68 #define ASC_ISPULLUP 0x0001
69 #define ASC_DMAACTIVE 0x0002
70 #define ASC_MAPLOADED 0x0004
73 #define ASC_READ_REG(asc, reg) \
74 ((uint8_t)bus_space_read_4((asc)->sc_bst, (asc)->sc_scsi_bsh, \
75 (reg) * sizeof(uint32_t)))
76 #define ASC_WRITE_REG(asc, reg, val) \
77 bus_space_write_4((asc)->sc_bst, (asc)->sc_scsi_bsh, \
78 (reg) * sizeof(uint32_t), (uint8_t)(val))
80 static int asc_ioasic_match(device_t
, cfdata_t
, void *);
81 static void asc_ioasic_attach(device_t
, device_t
, void *);
83 CFATTACH_DECL_NEW(asc_ioasic
, sizeof(struct asc_softc
),
84 asc_ioasic_match
, asc_ioasic_attach
, NULL
, NULL
);
86 static uint8_t asc_read_reg(struct ncr53c9x_softc
*, int);
87 static void asc_write_reg(struct ncr53c9x_softc
*, int, u_char
);
88 static int asc_dma_isintr(struct ncr53c9x_softc
*sc
);
89 static void asc_ioasic_reset(struct ncr53c9x_softc
*);
90 static int asc_ioasic_intr(struct ncr53c9x_softc
*);
91 static int asc_ioasic_setup(struct ncr53c9x_softc
*,
92 uint8_t **, size_t *, int, size_t *);
93 static void asc_ioasic_go(struct ncr53c9x_softc
*);
94 static void asc_ioasic_stop(struct ncr53c9x_softc
*);
95 static int asc_dma_isactive(struct ncr53c9x_softc
*);
97 static struct ncr53c9x_glue asc_ioasic_glue
= {
111 asc_ioasic_match(device_t parent
, cfdata_t cf
, void *aux
)
113 struct ioasicdev_attach_args
*d
= aux
;
115 if (strncmp("asc", d
->iada_modname
, TC_ROM_LLEN
))
122 asc_ioasic_attach(device_t parent
, device_t self
, void *aux
)
124 struct asc_softc
*asc
= device_private(self
);
125 struct ncr53c9x_softc
*sc
= &asc
->sc_ncr53c9x
;
126 struct ioasicdev_attach_args
*d
= aux
;
127 struct ioasic_softc
*isc
= device_private(parent
);
130 * Set up glue for MI code early; we use some of it here.
133 sc
->sc_glue
= &asc_ioasic_glue
;
134 asc
->sc_bst
= isc
->sc_bst
;
135 asc
->sc_bsh
= isc
->sc_bsh
;
136 if (bus_space_subregion(asc
->sc_bst
, asc
->sc_bsh
,
137 IOASIC_SLOT_12_START
, 0x100, &asc
->sc_scsi_bsh
)) {
138 aprint_error(": failed to map device registers\n");
141 asc
->sc_dmat
= isc
->sc_dmat
;
142 if (bus_dmamap_create(asc
->sc_dmat
, PAGE_SIZE
* 2,
143 2, PAGE_SIZE
, PAGE_SIZE
, BUS_DMA_NOWAIT
,
145 aprint_error(": failed to create DMA map\n");
150 sc
->sc_freq
= 25000000;
153 sc
->sc_freq
/= 1000000;
155 ioasic_intr_establish(parent
, d
->iada_cookie
, TC_IPL_BIO
,
159 * XXX More of this should be in ncr53c9x_attach(), but
160 * XXX should we really poke around the chip that much in
161 * XXX the MI code? Think about this more...
165 * Set up static configuration info.
167 sc
->sc_cfg1
= sc
->sc_id
| NCRCFG1_PARENB
;
168 sc
->sc_cfg2
= NCRCFG2_SCSI2
;
170 sc
->sc_rev
= NCR_VARIANT_NCR53C94
;
173 * XXX minsync and maxxfer _should_ be set up in MI code,
174 * XXX but it appears to have some dependency on what sort
175 * XXX of DMA we're hooked up to, etc.
179 * This is the value used to start sync negotiations
180 * Note that the NCR register "SYNCTP" is programmed
181 * in "clocks per byte", and has a minimum value of 4.
182 * The SCSI period used in negotiation is one-fourth
183 * of the time (in nanoseconds) needed to transfer one byte.
184 * Since the chip's clock is given in MHz, we have the following
185 * formula: 4 * period = (1000 / freq) * 4
187 sc
->sc_minsync
= (1000 / sc
->sc_freq
) * 5 / 4 ;
188 sc
->sc_maxxfer
= 64 * 1024;
190 /* Do the common parts of attachment. */
191 sc
->sc_adapter
.adapt_minphys
= minphys
;
192 sc
->sc_adapter
.adapt_request
= ncr53c9x_scsipi_request
;
197 asc_ioasic_reset(struct ncr53c9x_softc
*sc
)
199 struct asc_softc
*asc
= (struct asc_softc
*)sc
;
202 ssr
= bus_space_read_4(asc
->sc_bst
, asc
->sc_bsh
, IOASIC_CSR
);
203 ssr
&= ~IOASIC_CSR_DMAEN_SCSI
;
204 bus_space_write_4(asc
->sc_bst
, asc
->sc_bsh
, IOASIC_CSR
, ssr
);
205 bus_space_write_4(asc
->sc_bst
, asc
->sc_bsh
, IOASIC_SCSI_SCR
, 0);
207 if (asc
->sc_flags
& ASC_MAPLOADED
)
208 bus_dmamap_unload(asc
->sc_dmat
, asc
->sc_dmamap
);
209 asc
->sc_flags
&= ~(ASC_DMAACTIVE
|ASC_MAPLOADED
);
212 #define TWOPAGE(a) (PAGE_SIZE*2 - ((a) & (PAGE_SIZE-1)))
215 asc_ioasic_setup(struct ncr53c9x_softc
*sc
, uint8_t **addr
, size_t *len
,
216 int ispullup
, size_t *dmasize
)
218 struct asc_softc
*asc
= (struct asc_softc
*)sc
;
219 uint32_t ssr
, scr
, *p
;
223 NCR_DMA(("%s: start %d@%p,%s\n", sc
->sc_dev
.dv_xname
,
224 *asc
->sc_dmalen
, *asc
->sc_dmaaddr
, ispullup
? "IN" : "OUT"));
226 /* upto two 4KB pages */
227 size
= min(*dmasize
, TWOPAGE((size_t)*addr
));
228 asc
->sc_dmaaddr
= addr
;
229 asc
->sc_dmalen
= len
;
230 asc
->sc_dmasize
= size
;
231 asc
->sc_flags
= (ispullup
) ? ASC_ISPULLUP
: 0;
232 *dmasize
= size
; /* return trimmed transfer size */
234 /* stop DMA engine first */
235 ssr
= bus_space_read_4(asc
->sc_bst
, asc
->sc_bsh
, IOASIC_CSR
);
236 ssr
&= ~IOASIC_CSR_DMAEN_SCSI
;
237 bus_space_write_4(asc
->sc_bst
, asc
->sc_bsh
, IOASIC_CSR
, ssr
);
239 /* have dmamap for the transfering addresses */
240 if (bus_dmamap_load(asc
->sc_dmat
, asc
->sc_dmamap
,
241 *addr
, size
, NULL
/* kernel address */, BUS_DMA_NOWAIT
))
242 panic("%s: cannot allocate DMA address",
243 device_xname(sc
->sc_dev
));
245 /* take care of 8B constraint on starting address */
248 /* comfortably aligned to 8B boundary */
252 /* truncate to the boundary */
253 p
= (uint32_t *)(cp
& ~7);
254 /* how many 16bit quantities in subject */
256 /* trim down physical address too */
257 asc
->sc_dmamap
->dm_segs
[0].ds_addr
&= ~7;
258 asc
->sc_dmamap
->dm_segs
[0].ds_len
+= (cp
& 6);
259 if ((asc
->sc_flags
& ASC_ISPULLUP
) == 0) {
260 /* push down to SCSI device */
262 /* round up physical address in this case */
263 asc
->sc_dmamap
->dm_segs
[0].ds_addr
+= 8;
264 /* don't care excess cache flush */
266 /* pack fixup data in SDR0/SDR1 pair and instruct SCR */
267 bus_space_write_4(asc
->sc_bst
, asc
->sc_bsh
,
268 IOASIC_SCSI_SDR0
, p
[0]);
269 bus_space_write_4(asc
->sc_bst
, asc
->sc_bsh
,
270 IOASIC_SCSI_SDR1
, p
[1]);
272 bus_space_write_4(asc
->sc_bst
, asc
->sc_bsh
,
274 IOASIC_DMA_ADDR(asc
->sc_dmamap
->dm_segs
[0].ds_addr
));
275 bus_space_write_4(asc
->sc_bst
, asc
->sc_bsh
,
277 (asc
->sc_dmamap
->dm_nsegs
== 1) ?
278 ~0 : IOASIC_DMA_ADDR(asc
->sc_dmamap
->dm_segs
[1].ds_addr
));
279 bus_space_write_4(asc
->sc_bst
, asc
->sc_bsh
, IOASIC_SCSI_SCR
, scr
);
281 /* synchronize dmamap contents with memory image */
282 bus_dmamap_sync(asc
->sc_dmat
, asc
->sc_dmamap
,
284 (ispullup
) ? BUS_DMASYNC_PREREAD
: BUS_DMASYNC_PREWRITE
);
286 asc
->sc_flags
|= ASC_MAPLOADED
;
291 asc_ioasic_go(struct ncr53c9x_softc
*sc
)
293 struct asc_softc
*asc
= (struct asc_softc
*)sc
;
296 ssr
= bus_space_read_4(asc
->sc_bst
, asc
->sc_bsh
, IOASIC_CSR
);
297 if (asc
->sc_flags
& ASC_ISPULLUP
)
298 ssr
|= IOASIC_CSR_SCSI_DIR
;
300 /* ULTRIX does in this way */
301 ssr
&= ~IOASIC_CSR_SCSI_DIR
;
302 bus_space_write_4(asc
->sc_bst
, asc
->sc_bsh
, IOASIC_CSR
, ssr
);
303 ssr
= bus_space_read_4(asc
->sc_bst
, asc
->sc_bsh
, IOASIC_CSR
);
305 ssr
|= IOASIC_CSR_DMAEN_SCSI
;
306 bus_space_write_4(asc
->sc_bst
, asc
->sc_bsh
, IOASIC_CSR
, ssr
);
307 asc
->sc_flags
|= ASC_DMAACTIVE
;
311 asc_ioasic_intr(struct ncr53c9x_softc
*sc
)
313 struct asc_softc
*asc
= (struct asc_softc
*)sc
;
314 ssize_t trans
, resid
;
315 u_int tcl
, tcm
, ssr
, scr
, intr
;
317 if ((asc
->sc_flags
& ASC_DMAACTIVE
) == 0)
318 panic("%s: DMA wasn't active", __func__
);
320 #define IOASIC_ASC_ERRORS \
321 (IOASIC_INTR_SCSI_PTR_LOAD|IOASIC_INTR_SCSI_OVRUN|IOASIC_INTR_SCSI_READ_E)
323 * When doing polled I/O, the SCSI bits in the interrupt register won't
324 * get cleared by the interrupt processing. This will cause the DMA
325 * address registers to not load on the next DMA transfer.
326 * Check for these bits here, and clear them if needed.
328 intr
= bus_space_read_4(asc
->sc_bst
, asc
->sc_bsh
, IOASIC_INTR
);
329 if ((intr
& IOASIC_ASC_ERRORS
) != 0) {
330 intr
&= ~IOASIC_ASC_ERRORS
;
331 bus_space_write_4(asc
->sc_bst
, asc
->sc_bsh
, IOASIC_INTR
, intr
);
334 /* DMA has stopped */
335 ssr
= bus_space_read_4(asc
->sc_bst
, asc
->sc_bsh
, IOASIC_CSR
);
336 ssr
&= ~IOASIC_CSR_DMAEN_SCSI
;
337 bus_space_write_4(asc
->sc_bst
, asc
->sc_bsh
, IOASIC_CSR
, ssr
);
339 asc
->sc_flags
&= ~ASC_DMAACTIVE
;
341 if (asc
->sc_dmasize
== 0) {
342 /* A "Transfer Pad" operation completed */
343 tcl
= ASC_READ_REG(asc
, NCR_TCL
);
344 tcm
= ASC_READ_REG(asc
, NCR_TCM
);
345 NCR_DMA(("ioasic_intr: discarded %d bytes (tcl=%d, tcm=%d)\n",
346 tcl
| (tcm
<< 8), tcl
, tcm
));
351 if ((asc
->sc_flags
& ASC_ISPULLUP
) == 0 &&
352 (resid
= (ASC_READ_REG(asc
, NCR_FFLAG
) & NCRFIFO_FF
)) != 0) {
353 NCR_DMA(("ioasic_intr: empty FIFO of %d ", resid
));
357 resid
+= (tcl
= ASC_READ_REG(asc
, NCR_TCL
));
358 resid
+= (tcm
= ASC_READ_REG(asc
, NCR_TCM
)) << 8;
360 trans
= asc
->sc_dmasize
- resid
;
361 if (trans
< 0) { /* transferred < 0 ? */
362 printf("ioasic_intr: xfer (%zd) > req (%zu)\n",
363 trans
, asc
->sc_dmasize
);
364 trans
= asc
->sc_dmasize
;
366 NCR_DMA(("ioasic_intr: tcl=%d, tcm=%d; trans=%d, resid=%d\n",
367 tcl
, tcm
, trans
, resid
));
369 bus_dmamap_sync(asc
->sc_dmat
, asc
->sc_dmamap
,
371 (asc
->sc_flags
& ASC_ISPULLUP
) ?
372 BUS_DMASYNC_POSTREAD
: BUS_DMASYNC_POSTWRITE
);
374 scr
= bus_space_read_4(asc
->sc_bst
, asc
->sc_bsh
, IOASIC_SCSI_SCR
);
375 if ((asc
->sc_flags
& ASC_ISPULLUP
) && scr
!= 0) {
376 uint32_t sdr
[2], ptr
;
378 sdr
[0] = bus_space_read_4(asc
->sc_bst
, asc
->sc_bsh
,
380 sdr
[1] = bus_space_read_4(asc
->sc_bst
, asc
->sc_bsh
,
382 ptr
= bus_space_read_4(asc
->sc_bst
, asc
->sc_bsh
,
384 ptr
= (ptr
>> 3) & 0x1ffffffc;
387 * 2 -> short[0] + short[1]
388 * 3 -> short[0] + short[1] + short[2]
390 scr
&= IOASIC_SCR_WORD
;
391 memcpy((void *)MIPS_PHYS_TO_KSEG0(ptr
), sdr
, scr
<< 1);
394 bus_dmamap_unload(asc
->sc_dmat
, asc
->sc_dmamap
);
395 asc
->sc_flags
&= ~ASC_MAPLOADED
;
397 *asc
->sc_dmalen
-= trans
;
398 *asc
->sc_dmaaddr
+= trans
;
405 asc_ioasic_stop(struct ncr53c9x_softc
*sc
)
407 struct asc_softc
*asc
= (struct asc_softc
*)sc
;
409 if (asc
->sc_flags
& ASC_MAPLOADED
) {
410 bus_dmamap_sync(asc
->sc_dmat
, asc
->sc_dmamap
,
412 (asc
->sc_flags
& ASC_ISPULLUP
) ?
413 BUS_DMASYNC_POSTREAD
: BUS_DMASYNC_POSTWRITE
);
414 bus_dmamap_unload(asc
->sc_dmat
, asc
->sc_dmamap
);
417 asc
->sc_flags
&= ~(ASC_DMAACTIVE
|ASC_MAPLOADED
);
421 asc_read_reg(struct ncr53c9x_softc
*sc
, int reg
)
423 struct asc_softc
*asc
= (struct asc_softc
*)sc
;
425 return ASC_READ_REG(asc
, reg
);
429 asc_write_reg(struct ncr53c9x_softc
*sc
, int reg
, uint8_t val
)
431 struct asc_softc
*asc
= (struct asc_softc
*)sc
;
433 ASC_WRITE_REG(asc
, reg
, val
);
437 asc_dma_isintr(struct ncr53c9x_softc
*sc
)
439 struct asc_softc
*asc
= (struct asc_softc
*)sc
;
441 return (ASC_READ_REG(asc
, NCR_STAT
) & NCRSTAT_INT
) != 0;
445 asc_dma_isactive(struct ncr53c9x_softc
*sc
)
447 struct asc_softc
*asc
= (struct asc_softc
*)sc
;
449 return (asc
->sc_flags
& ASC_DMAACTIVE
) != 0;