1/* $NetBSD$ */
2
3/*-
4 * Copyright (c) 1998 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
9 * NASA Ames Research Center.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
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.
19 *
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.
31 */
32
33/*
34 * Copyright (c) 1994 Peter Galbavy.  All rights reserved.
35 *
36 * Redistribution and use in source and binary forms, with or without
37 * modification, are permitted provided that the following conditions
38 * are met:
39 * 1. Redistributions of source code must retain the above copyright
40 *    notice, this list of conditions and the following disclaimer.
41 * 2. Redistributions in binary form must reproduce the above copyright
42 *    notice, this list of conditions and the following disclaimer in the
43 *    documentation and/or other materials provided with the distribution.
44 * 3. All advertising materials mentioning features or use of this software
45 *    must display the following acknowledgement:
46 *	This product includes software developed by Peter Galbavy.
47 * 4. The name of the author may not be used to endorse or promote products
48 *    derived from this software without specific prior written permission.
49 *
50 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
51 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
52 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
53 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
54 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
55 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
56 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
57 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
58 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
59 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
60 */
61
62#include <sys/cdefs.h>
63__KERNEL_RCSID(0, "$NetBSD$");
64
65#include <sys/param.h>
66#include <sys/systm.h>
67#include <sys/device.h>
68#include <sys/buf.h>
69
70#include <dev/scsipi/scsi_all.h>
71#include <dev/scsipi/scsipi_all.h>
72#include <dev/scsipi/scsiconf.h>
73
74#include <dev/ic/ncr53c9xreg.h>
75#include <dev/ic/ncr53c9xvar.h>
76
77#include <sys/bus.h>
78
79#include <dev/tc/tcvar.h>
80#include <dev/tc/tcdsreg.h>
81#include <dev/tc/tcdsvar.h>
82
83struct asc_softc {
84	struct ncr53c9x_softc sc_ncr53c9x;	/* glue to MI code */
85	bus_space_tag_t sc_bst;			/* bus space tag */
86	bus_space_handle_t sc_scsi_bsh;		/* ASC register handle */
87	bus_dma_tag_t sc_dmat;			/* bus DMA tag */
88	bus_dmamap_t sc_dmamap;			/* bus dmamap */
89	uint8_t **sc_dmaaddr;
90	size_t *sc_dmalen;
91	size_t sc_dmasize;
92	unsigned sc_flags;
93#define	ASC_ISPULLUP		0x01
94#define	ASC_DMAACTIVE		0x02
95#define	ASC_MAPLOADED		0x04
96	struct tcds_slotconfig *sc_tcds;	/* DMA/slot info lives here */
97};
98
99static int  asc_tcds_match(device_t, cfdata_t, void *);
100static void asc_tcds_attach(device_t, device_t, void *);
101
102CFATTACH_DECL_NEW(asc_tcds, sizeof(struct asc_softc),
103    asc_tcds_match, asc_tcds_attach, NULL, NULL);
104
105/*
106 * Functions and the switch for the MI code.
107 */
108static uint8_t	asc_read_reg(struct ncr53c9x_softc *, int);
109static void	asc_write_reg(struct ncr53c9x_softc *, int, uint8_t);
110static int	tcds_dma_isintr(struct ncr53c9x_softc *);
111static void	tcds_dma_reset(struct ncr53c9x_softc *);
112static int	tcds_dma_intr(struct ncr53c9x_softc *);
113static int	tcds_dma_setup(struct ncr53c9x_softc *, uint8_t **,
114		    size_t *, int, size_t *);
115static void	tcds_dma_go(struct ncr53c9x_softc *);
116static void	tcds_dma_stop(struct ncr53c9x_softc *);
117static int	tcds_dma_isactive(struct ncr53c9x_softc *);
118static void	tcds_clear_latched_intr(struct ncr53c9x_softc *);
119
120static struct ncr53c9x_glue asc_tcds_glue = {
121	asc_read_reg,
122	asc_write_reg,
123	tcds_dma_isintr,
124	tcds_dma_reset,
125	tcds_dma_intr,
126	tcds_dma_setup,
127	tcds_dma_go,
128	tcds_dma_stop,
129	tcds_dma_isactive,
130	tcds_clear_latched_intr,
131};
132
133static int
134asc_tcds_match(device_t parent, cfdata_t cf, void *aux)
135{
136
137	/* We always exist. */
138	return 1;
139}
140
141#define DMAMAX(a)	(PAGE_SIZE - ((a) & (PAGE_SIZE - 1)))
142
143/*
144 * Attach this instance, and then all the sub-devices
145 */
146static void
147asc_tcds_attach(device_t parent, device_t self, void *aux)
148{
149	struct asc_softc *asc = device_private(self);
150	struct ncr53c9x_softc *sc = &asc->sc_ncr53c9x;
151	struct tcdsdev_attach_args *tcdsdev = aux;
152	int error;
153
154	/*
155	 * Set up glue for MI code early; we use some of it here.
156	 */
157	sc->sc_dev = self;
158	sc->sc_glue = &asc_tcds_glue;
159
160	asc->sc_bst = tcdsdev->tcdsda_bst;
161	asc->sc_scsi_bsh = tcdsdev->tcdsda_bsh;
162	asc->sc_tcds = tcdsdev->tcdsda_sc;
163
164	/*
165	 * The TCDS ASIC cannot DMA across 8k boundaries, and this
166	 * driver is written such that each DMA segment gets a new
167	 * call to tcds_dma_setup().  Thus, the DMA map only needs
168	 * to support 8k transfers.
169	 */
170	asc->sc_dmat = tcdsdev->tcdsda_dmat;
171	if ((error = bus_dmamap_create(asc->sc_dmat, PAGE_SIZE, 1, PAGE_SIZE,
172	    PAGE_SIZE, BUS_DMA_NOWAIT, &asc->sc_dmamap)) < 0) {
173		aprint_error(": failed to create DMA map, error = %d\n", error);
174		return;
175	}
176
177	sc->sc_id = tcdsdev->tcdsda_id;
178	sc->sc_freq = tcdsdev->tcdsda_freq;
179
180	/* gimme MHz */
181	sc->sc_freq /= 1000000;
182
183	tcds_intr_establish(parent, tcdsdev->tcdsda_chip, ncr53c9x_intr, sc);
184
185	/*
186	 * XXX More of this should be in ncr53c9x_attach(), but
187	 * XXX should we really poke around the chip that much in
188	 * XXX the MI code?  Think about this more...
189	 */
190
191	/*
192	 * Set up static configuration info.
193	 */
194	sc->sc_cfg1 = sc->sc_id | NCRCFG1_PARENB;
195	sc->sc_cfg2 = NCRCFG2_SCSI2;
196	sc->sc_cfg3 = NCRCFG3_CDB;
197	if (sc->sc_freq > 25)
198		sc->sc_cfg3 |= NCRF9XCFG3_FCLK;
199	sc->sc_rev = tcdsdev->tcdsda_variant;
200	if (tcdsdev->tcdsda_fast) {
201		sc->sc_features |= NCR_F_FASTSCSI;
202		sc->sc_cfg3_fscsi = NCRF9XCFG3_FSCSI;
203	}
204
205	/*
206	 * XXX minsync and maxxfer _should_ be set up in MI code,
207	 * XXX but it appears to have some dependency on what sort
208	 * XXX of DMA we're hooked up to, etc.
209	 */
210
211	/*
212	 * This is the value used to start sync negotiations
213	 * Note that the NCR register "SYNCTP" is programmed
214	 * in "clocks per byte", and has a minimum value of 4.
215	 * The SCSI period used in negotiation is one-fourth
216	 * of the time (in nanoseconds) needed to transfer one byte.
217	 * Since the chip's clock is given in MHz, we have the following
218	 * formula: 4 * period = (1000 / freq) * 4
219	 */
220	sc->sc_minsync = (1000 / sc->sc_freq) * tcdsdev->tcdsda_period / 4;
221
222	sc->sc_maxxfer = 64 * 1024;
223
224	/* Do the common parts of attachment. */
225	sc->sc_adapter.adapt_minphys = minphys;
226	sc->sc_adapter.adapt_request = ncr53c9x_scsipi_request;
227	ncr53c9x_attach(sc);
228}
229
230static void
231tcds_dma_reset(struct ncr53c9x_softc *sc)
232{
233	struct asc_softc *asc = (struct asc_softc *)sc;
234
235	/* TCDS SCSI disable/reset/enable. */
236	tcds_scsi_reset(asc->sc_tcds);			/* XXX */
237
238	if (asc->sc_flags & ASC_MAPLOADED)
239		bus_dmamap_unload(asc->sc_dmat, asc->sc_dmamap);
240	asc->sc_flags &= ~(ASC_DMAACTIVE|ASC_MAPLOADED);
241}
242
243/*
244 * start a DMA transfer or keep it going
245 */
246int
247tcds_dma_setup(struct ncr53c9x_softc *sc, uint8_t **addr, size_t *len,
248    int ispullup, size_t *dmasize)
249{
250	struct asc_softc *asc = (struct asc_softc *)sc;
251	struct tcds_slotconfig *tcds = asc->sc_tcds;
252	size_t size;
253	uint32_t dic;
254
255	NCR_DMA(("tcds_dma %d: start %d@%p,%s\n", tcds->sc_slot,
256	    (int)*asc->sc_dmalen, *asc->sc_dmaaddr,
257	    (ispullup) ? "IN" : "OUT"));
258
259	/*
260	 * the rules say we cannot transfer more than the limit
261	 * of this DMA chip (64k) and we cannot cross a 8k boundary.
262	 */
263	size = min(*dmasize, DMAMAX((size_t)*addr));
264	asc->sc_dmaaddr = addr;
265	asc->sc_dmalen = len;
266	asc->sc_flags = (ispullup) ? ASC_ISPULLUP : 0;
267	*dmasize = asc->sc_dmasize = size;
268
269	NCR_DMA(("dma_start: dmasize = %d\n", (int)size));
270
271	if (size == 0)
272		return 0;
273
274	if (bus_dmamap_load(asc->sc_dmat, asc->sc_dmamap, *addr, size,
275	    NULL, BUS_DMA_NOWAIT | (ispullup ? BUS_DMA_READ : BUS_DMA_WRITE))) {
276		/*
277		 * XXX Should return an error, here, but the upper-layer
278		 * XXX doesn't check the return value!
279		 */
280		panic("%s: dmamap load failed", __func__);
281	}
282
283	/* synchronize dmamap contents with memory image */
284	bus_dmamap_sync(asc->sc_dmat, asc->sc_dmamap, 0, size,
285	    (ispullup) ? BUS_DMASYNC_PREREAD : BUS_DMASYNC_PREWRITE);
286
287	/* load address, set/clear unaligned transfer and read/write bits. */
288	bus_space_write_4(tcds->sc_bst, tcds->sc_bsh, tcds->sc_sda,
289	    asc->sc_dmamap->dm_segs[0].ds_addr >> 2);
290	dic = bus_space_read_4(tcds->sc_bst, tcds->sc_bsh, tcds->sc_dic);
291	dic &= ~TCDS_DIC_ADDRMASK;
292	dic |= asc->sc_dmamap->dm_segs[0].ds_addr & TCDS_DIC_ADDRMASK;
293	if (ispullup)
294		dic |= TCDS_DIC_WRITE;
295	else
296		dic &= ~TCDS_DIC_WRITE;
297	bus_space_write_4(tcds->sc_bst, tcds->sc_bsh, tcds->sc_dic, dic);
298
299	asc->sc_flags |= ASC_MAPLOADED;
300	return 0;
301}
302
303static void
304tcds_dma_go(struct ncr53c9x_softc *sc)
305{
306	struct asc_softc *asc = (struct asc_softc *)sc;
307
308	/* mark unit as DMA-active */
309	asc->sc_flags |= ASC_DMAACTIVE;
310
311	/* start DMA */
312	tcds_dma_enable(asc->sc_tcds, 1);
313}
314
315static void
316tcds_dma_stop(struct ncr53c9x_softc *sc)
317{
318#if 0
319	struct asc_softc *asc = (struct asc_softc *)sc;
320#endif
321
322	/*
323	 * XXX STOP DMA HERE!
324	 */
325}
326
327/*
328 * Pseudo (chained) interrupt from the asc driver to kick the
329 * current running DMA transfer. Called from ncr53c9x_intr()
330 * for now.
331 *
332 * return 1 if it was a DMA continue.
333 */
334static int
335tcds_dma_intr(struct ncr53c9x_softc *sc)
336{
337	struct asc_softc *asc = (struct asc_softc *)sc;
338	struct tcds_slotconfig *tcds = asc->sc_tcds;
339	int trans, resid;
340	uint32_t tcl, tcm;
341	uint32_t dud, dudmask, *addr;
342	bus_addr_t pa;
343
344	NCR_DMA(("tcds_dma %d: intr", tcds->sc_slot));
345
346	if (tcds_scsi_iserr(tcds))
347		return 0;
348
349	/* This is an "assertion" :) */
350	if ((asc->sc_flags & ASC_DMAACTIVE) == 0)
351		panic("%s: DMA wasn't active", __func__);
352
353	/* DMA has stopped */
354	tcds_dma_enable(tcds, 0);
355	asc->sc_flags &= ~ASC_DMAACTIVE;
356
357	if (asc->sc_dmasize == 0) {
358		/* A "Transfer Pad" operation completed */
359		tcl = NCR_READ_REG(sc, NCR_TCL);
360		tcm = NCR_READ_REG(sc, NCR_TCM);
361		NCR_DMA(("dma_intr: discarded %d bytes (tcl=%d, tcm=%d)\n",
362		    tcl | (tcm << 8), tcl, tcm));
363		return 0;
364	}
365
366	resid = 0;
367	if ((asc->sc_flags & ASC_ISPULLUP) == 0 &&
368	    (resid = (NCR_READ_REG(sc, NCR_FFLAG) & NCRFIFO_FF)) != 0) {
369		NCR_DMA(("dma_intr: empty esp FIFO of %d ", resid));
370		DELAY(1);
371	}
372
373	resid += (tcl = NCR_READ_REG(sc, NCR_TCL));
374	resid += (tcm = NCR_READ_REG(sc, NCR_TCM)) << 8;
375
376	trans = asc->sc_dmasize - resid;
377	if (trans < 0) {			/* transferred < 0 ? */
378		printf("tcds_dma %d: xfer (%d) > req (%d)\n",
379		    tcds->sc_slot, trans, (int)asc->sc_dmasize);
380		trans = asc->sc_dmasize;
381	}
382
383	NCR_DMA(("dma_intr: tcl=%d, tcm=%d; trans=%d, resid=%d\n",
384	    tcl, tcm, trans, resid));
385
386	*asc->sc_dmalen -= trans;
387	*asc->sc_dmaaddr += trans;
388
389	bus_dmamap_sync(asc->sc_dmat, asc->sc_dmamap,
390	    0, asc->sc_dmamap->dm_mapsize,
391	    (sc->sc_flags & ASC_ISPULLUP) ?
392	    BUS_DMASYNC_POSTREAD : BUS_DMASYNC_POSTWRITE);
393
394	/*
395	 * Clean up unaligned DMAs into main memory.
396	 */
397	if (asc->sc_flags & ASC_ISPULLUP) {
398		/* Handle unaligned starting address, length. */
399		dud = bus_space_read_4(tcds->sc_bst,
400		    tcds->sc_bsh, tcds->sc_dud0);
401		if ((dud & TCDS_DUD0_VALIDBITS) != 0) {
402			addr = (uint32_t *)((paddr_t)*asc->sc_dmaaddr & ~0x3);
403			dudmask = 0;
404			if (dud & TCDS_DUD0_VALID00)
405				panic("%s: dud0 byte 0 valid", __func__);
406			if (dud & TCDS_DUD0_VALID01)
407				dudmask |= TCDS_DUD_BYTE01;
408			if (dud & TCDS_DUD0_VALID10)
409				dudmask |= TCDS_DUD_BYTE10;
410#ifdef DIAGNOSTIC
411			if (dud & TCDS_DUD0_VALID11)
412				dudmask |= TCDS_DUD_BYTE11;
413#endif
414			NCR_DMA(("dud0 at %p dudmask 0x%x\n",
415			    addr, dudmask));
416			*addr = (*addr & ~dudmask) | (dud & dudmask);
417		}
418		dud = bus_space_read_4(tcds->sc_bst,
419		    tcds->sc_bsh, tcds->sc_dud1);
420		if ((dud & TCDS_DUD1_VALIDBITS) != 0) {
421			pa = bus_space_read_4(tcds->sc_bst, tcds->sc_bsh,
422			    tcds->sc_sda) << 2;
423			dudmask = 0;
424			if (dud & TCDS_DUD1_VALID00)
425				dudmask |= TCDS_DUD_BYTE00;
426			if (dud & TCDS_DUD1_VALID01)
427				dudmask |= TCDS_DUD_BYTE01;
428			if (dud & TCDS_DUD1_VALID10)
429				dudmask |= TCDS_DUD_BYTE10;
430#ifdef DIAGNOSTIC
431			if (dud & TCDS_DUD1_VALID11)
432				panic("%s: dud1 byte 3 valid", __func__);
433#endif
434			NCR_DMA(("dud1 at 0x%lx dudmask 0x%x\n",
435			    pa, dudmask));
436			/* XXX Fix TC_PHYS_TO_UNCACHED() */
437#if defined(__alpha__)
438			addr = (uint32_t *)ALPHA_PHYS_TO_K0SEG(pa);
439#elif defined(__mips__)
440			addr = (uint32_t *)MIPS_PHYS_TO_KSEG1(pa);
441#elif defined(__vax__)
442			addr = (uint32_t *)VAX_PHYS_TO_S0(pa);
443#else
444#error TURBOchannel only exists on DECs, folks...
445#endif
446			*addr = (*addr & ~dudmask) | (dud & dudmask);
447		}
448		/* XXX deal with saved residual byte? */
449	}
450
451	bus_dmamap_unload(asc->sc_dmat, asc->sc_dmamap);
452	asc->sc_flags &= ~ASC_MAPLOADED;
453
454	return 0;
455}
456
457/*
458 * Glue functions.
459 */
460static uint8_t
461asc_read_reg(struct ncr53c9x_softc *sc, int reg)
462{
463	struct asc_softc *asc = (struct asc_softc *)sc;
464	uint32_t v;
465
466	v = bus_space_read_4(asc->sc_bst, asc->sc_scsi_bsh,
467	    reg * sizeof(uint32_t));
468
469	return v & 0xff;
470}
471
472static void
473asc_write_reg(struct ncr53c9x_softc *sc, int reg, u_char val)
474{
475	struct asc_softc *asc = (struct asc_softc *)sc;
476
477	bus_space_write_4(asc->sc_bst, asc->sc_scsi_bsh,
478	    reg * sizeof(uint32_t), val);
479}
480
481static int
482tcds_dma_isintr(struct ncr53c9x_softc *sc)
483{
484	struct asc_softc *asc = (struct asc_softc *)sc;
485	int x;
486
487	x = tcds_scsi_isintr(asc->sc_tcds, 1);
488
489	/* XXX */
490	return x;
491}
492
493static int
494tcds_dma_isactive(struct ncr53c9x_softc *sc)
495{
496	struct asc_softc *asc = (struct asc_softc *)sc;
497
498	return (asc->sc_flags & ASC_DMAACTIVE) != 0;
499}
500
501static void
502tcds_clear_latched_intr(struct ncr53c9x_softc *sc)
503{
504	struct asc_softc *asc = (struct asc_softc *)sc;
505
506	/* Clear the TCDS interrupt bit. */
507	(void)tcds_scsi_isintr(asc->sc_tcds, 1);
508}
509