xref: /netbsd-src/sys/dev/tc/asc_tc.c (revision ce099b40997c43048fb78bd578195f81d2456523)
1*ce099b40Smartin /* $NetBSD: asc_tc.c,v 1.34 2008/04/28 20:23:58 martin Exp $ */
2f976a714Ssimonb 
3f976a714Ssimonb /*-
4f976a714Ssimonb  * Copyright (c) 2000 The NetBSD Foundation, Inc.
5f976a714Ssimonb  * All rights reserved.
6f976a714Ssimonb  *
7f976a714Ssimonb  * This code is derived from software contributed to The NetBSD Foundation
8f976a714Ssimonb  * by Tohru Nishimura.
9f976a714Ssimonb  *
10f976a714Ssimonb  * Redistribution and use in source and binary forms, with or without
11f976a714Ssimonb  * modification, are permitted provided that the following conditions
12f976a714Ssimonb  * are met:
13f976a714Ssimonb  * 1. Redistributions of source code must retain the above copyright
14f976a714Ssimonb  *    notice, this list of conditions and the following disclaimer.
15f976a714Ssimonb  * 2. Redistributions in binary form must reproduce the above copyright
16f976a714Ssimonb  *    notice, this list of conditions and the following disclaimer in the
17f976a714Ssimonb  *    documentation and/or other materials provided with the distribution.
18f976a714Ssimonb  *
19f976a714Ssimonb  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20f976a714Ssimonb  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21f976a714Ssimonb  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22f976a714Ssimonb  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23f976a714Ssimonb  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24f976a714Ssimonb  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25f976a714Ssimonb  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26f976a714Ssimonb  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27f976a714Ssimonb  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28f976a714Ssimonb  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29f976a714Ssimonb  * POSSIBILITY OF SUCH DAMAGE.
30f976a714Ssimonb  */
31f976a714Ssimonb 
326a3181d3Slukem #include <sys/cdefs.h>
33*ce099b40Smartin __KERNEL_RCSID(0, "$NetBSD: asc_tc.c,v 1.34 2008/04/28 20:23:58 martin Exp $");
34f976a714Ssimonb 
35f976a714Ssimonb #include <sys/param.h>
36f976a714Ssimonb #include <sys/systm.h>
37f976a714Ssimonb #include <sys/device.h>
38f976a714Ssimonb #include <sys/buf.h>
39f976a714Ssimonb 
40f976a714Ssimonb #include <dev/scsipi/scsi_all.h>
41f976a714Ssimonb #include <dev/scsipi/scsipi_all.h>
42f976a714Ssimonb #include <dev/scsipi/scsiconf.h>
43f976a714Ssimonb #include <dev/scsipi/scsi_message.h>
44f976a714Ssimonb 
45a2a38285Sad #include <sys/bus.h>
46f976a714Ssimonb 
47f976a714Ssimonb #include <dev/ic/ncr53c9xreg.h>
48f976a714Ssimonb #include <dev/ic/ncr53c9xvar.h>
49f976a714Ssimonb 
50f976a714Ssimonb #include <dev/tc/tcvar.h>
51f976a714Ssimonb 
52f976a714Ssimonb struct asc_softc {
53f976a714Ssimonb 	struct ncr53c9x_softc sc_ncr53c9x;	/* glue to MI code */
54f976a714Ssimonb 	bus_space_tag_t sc_bst;
55f976a714Ssimonb 	bus_space_handle_t sc_bsh;
56f976a714Ssimonb 	bus_dma_tag_t sc_dmat;
57f976a714Ssimonb 	bus_dmamap_t sc_dmamap;
5878a1d236Stsutsui 	uint8_t **sc_dmaaddr;
59f976a714Ssimonb 	size_t	*sc_dmalen;
60f976a714Ssimonb 	size_t	sc_dmasize;
61f976a714Ssimonb 	int	sc_active;			/* DMA active ? */
62f976a714Ssimonb 	int	sc_ispullup;			/* DMA into main memory? */
63f976a714Ssimonb 
64f976a714Ssimonb 	/* XXX XXX XXX */
65dd46610bSyamt 	char *sc_base, *sc_bounce, *sc_target;
66f976a714Ssimonb };
67f976a714Ssimonb 
6878a1d236Stsutsui static int  asc_tc_match(device_t, cfdata_t, void *);
6978a1d236Stsutsui static void asc_tc_attach(device_t, device_t, void *);
70f976a714Ssimonb 
7178a1d236Stsutsui CFATTACH_DECL_NEW(asc_tc, sizeof(struct asc_softc),
72b75a007dSthorpej     asc_tc_match, asc_tc_attach, NULL, NULL);
73f976a714Ssimonb 
7478a1d236Stsutsui static uint8_t	asc_read_reg(struct ncr53c9x_softc *, int);
7578a1d236Stsutsui static void	asc_write_reg(struct ncr53c9x_softc *, int, uint8_t);
7618db93c7Sperry static int	asc_dma_isintr(struct ncr53c9x_softc *);
7718db93c7Sperry static void	asc_tc_reset(struct ncr53c9x_softc *);
7818db93c7Sperry static int	asc_tc_intr(struct ncr53c9x_softc *);
7978a1d236Stsutsui static int	asc_tc_setup(struct ncr53c9x_softc *, uint8_t **,
8018db93c7Sperry 		    size_t *, int, size_t *);
8118db93c7Sperry static void	asc_tc_go(struct ncr53c9x_softc *);
8218db93c7Sperry static void	asc_tc_stop(struct ncr53c9x_softc *);
8318db93c7Sperry static int	asc_dma_isactive(struct ncr53c9x_softc *);
84f976a714Ssimonb 
85240c203dSsimonb static struct ncr53c9x_glue asc_tc_glue = {
86f976a714Ssimonb 	asc_read_reg,
87f976a714Ssimonb 	asc_write_reg,
88f976a714Ssimonb 	asc_dma_isintr,
89240c203dSsimonb 	asc_tc_reset,
90240c203dSsimonb 	asc_tc_intr,
91240c203dSsimonb 	asc_tc_setup,
92240c203dSsimonb 	asc_tc_go,
93240c203dSsimonb 	asc_tc_stop,
94f976a714Ssimonb 	asc_dma_isactive,
9578a1d236Stsutsui 	NULL,
96f976a714Ssimonb };
97f976a714Ssimonb 
98f976a714Ssimonb /*
99f976a714Ssimonb  * Parameters specific to PMAZ-A TC option card.
100f976a714Ssimonb  */
101f976a714Ssimonb #define PMAZ_OFFSET_53C94	0x0		/* from module base */
102f976a714Ssimonb #define PMAZ_OFFSET_DMAR	0x40000		/* DMA Address Register */
103f976a714Ssimonb #define PMAZ_OFFSET_RAM		0x80000		/* 128KB SRAM buffer */
104f976a714Ssimonb #define PMAZ_OFFSET_ROM		0xc0000		/* diagnostic ROM */
105f976a714Ssimonb 
106f976a714Ssimonb #define PMAZ_RAM_SIZE		0x20000		/* 128k (32k*32) */
107f976a714Ssimonb #define PER_TGT_DMA_SIZE	((PMAZ_RAM_SIZE / 7) & ~(sizeof(int) - 1))
108f976a714Ssimonb 
109f976a714Ssimonb #define PMAZ_DMAR_WRITE		0x80000000	/* DMA direction bit */
110f976a714Ssimonb #define PMAZ_DMAR_MASK		0x1ffff		/* 17 bits, 128k */
111f976a714Ssimonb #define PMAZ_DMA_ADDR(x)	((unsigned long)(x) & PMAZ_DMAR_MASK)
112f976a714Ssimonb 
113f976a714Ssimonb static int
asc_tc_match(device_t parent,cfdata_t cfdata,void * aux)11478a1d236Stsutsui asc_tc_match(device_t parent, cfdata_t cfdata, void *aux)
115f976a714Ssimonb {
116f976a714Ssimonb 	struct tc_attach_args *d = aux;
117f976a714Ssimonb 
118f976a714Ssimonb 	if (strncmp("PMAZ-AA ", d->ta_modname, TC_ROM_LLEN))
11978a1d236Stsutsui 		return 0;
120f976a714Ssimonb 
12178a1d236Stsutsui 	return 1;
122f976a714Ssimonb }
123f976a714Ssimonb 
124f976a714Ssimonb static void
asc_tc_attach(device_t parent,device_t self,void * aux)12578a1d236Stsutsui asc_tc_attach(device_t parent, device_t self, void *aux)
126f976a714Ssimonb {
12707c30f82Sthorpej 	struct asc_softc *asc = device_private(self);
128f976a714Ssimonb 	struct ncr53c9x_softc *sc = &asc->sc_ncr53c9x;
12978a1d236Stsutsui 	struct tc_attach_args *ta = aux;
130f976a714Ssimonb 
131f976a714Ssimonb 	/*
132f976a714Ssimonb 	 * Set up glue for MI code early; we use some of it here.
133f976a714Ssimonb 	 */
13478a1d236Stsutsui 	sc->sc_dev = self;
135240c203dSsimonb 	sc->sc_glue = &asc_tc_glue;
136f976a714Ssimonb 	asc->sc_bst = ta->ta_memt;
137f976a714Ssimonb 	asc->sc_dmat = ta->ta_dmat;
138f976a714Ssimonb 	if (bus_space_map(asc->sc_bst, ta->ta_addr,
139f976a714Ssimonb 	    PMAZ_OFFSET_RAM + PMAZ_RAM_SIZE, 0, &asc->sc_bsh)) {
14078a1d236Stsutsui 		aprint_error(": unable to map device\n");
141f976a714Ssimonb 		return;
142f976a714Ssimonb 	}
14353524e44Schristos 	asc->sc_base = (void *)ta->ta_addr;	/* XXX XXX XXX */
144f976a714Ssimonb 
145f976a714Ssimonb 	tc_intr_establish(parent, ta->ta_cookie, IPL_BIO, ncr53c9x_intr, sc);
146f976a714Ssimonb 
147f976a714Ssimonb 	sc->sc_id = 7;
148f976a714Ssimonb 	sc->sc_freq = (ta->ta_busspeed) ? 25000000 : 12500000;
149f976a714Ssimonb 
15029849ba3Stsutsui 	/* gimme MHz */
151f976a714Ssimonb 	sc->sc_freq /= 1000000;
152f976a714Ssimonb 
153f976a714Ssimonb 	/*
154f976a714Ssimonb 	 * XXX More of this should be in ncr53c9x_attach(), but
155f976a714Ssimonb 	 * XXX should we really poke around the chip that much in
156f976a714Ssimonb 	 * XXX the MI code?  Think about this more...
157f976a714Ssimonb 	 */
158f976a714Ssimonb 
159f976a714Ssimonb 	/*
160f976a714Ssimonb 	 * Set up static configuration info.
161f976a714Ssimonb 	 */
162f976a714Ssimonb 	sc->sc_cfg1 = sc->sc_id | NCRCFG1_PARENB;
163f976a714Ssimonb 	sc->sc_cfg2 = NCRCFG2_SCSI2;
164f976a714Ssimonb 	sc->sc_cfg3 = 0;
165f976a714Ssimonb 	sc->sc_rev = NCR_VARIANT_NCR53C94;
166f976a714Ssimonb 
167f976a714Ssimonb 	/*
168f976a714Ssimonb 	 * XXX minsync and maxxfer _should_ be set up in MI code,
169f976a714Ssimonb 	 * XXX but it appears to have some dependency on what sort
170f976a714Ssimonb 	 * XXX of DMA we're hooked up to, etc.
171f976a714Ssimonb 	 */
172f976a714Ssimonb 
173f976a714Ssimonb 	/*
174f976a714Ssimonb 	 * This is the value used to start sync negotiations
175f976a714Ssimonb 	 * Note that the NCR register "SYNCTP" is programmed
176f976a714Ssimonb 	 * in "clocks per byte", and has a minimum value of 4.
177f976a714Ssimonb 	 * The SCSI period used in negotiation is one-fourth
178f976a714Ssimonb 	 * of the time (in nanoseconds) needed to transfer one byte.
179f976a714Ssimonb 	 * Since the chip's clock is given in MHz, we have the following
180f976a714Ssimonb 	 * formula: 4 * period = (1000 / freq) * 4
181f976a714Ssimonb 	 */
182f976a714Ssimonb 	sc->sc_minsync = (1000 / sc->sc_freq) * 5 / 4;
183f976a714Ssimonb 
184f976a714Ssimonb 	sc->sc_maxxfer = 64 * 1024;
185f976a714Ssimonb 
186f976a714Ssimonb 	/* Do the common parts of attachment. */
187f976a714Ssimonb 	sc->sc_adapter.adapt_minphys = minphys;
188f976a714Ssimonb 	sc->sc_adapter.adapt_request = ncr53c9x_scsipi_request;
189f976a714Ssimonb 	ncr53c9x_attach(sc);
190f976a714Ssimonb }
191f976a714Ssimonb 
192f976a714Ssimonb static void
asc_tc_reset(struct ncr53c9x_softc * sc)193c8dd740fSthorpej asc_tc_reset(struct ncr53c9x_softc *sc)
194f976a714Ssimonb {
195f976a714Ssimonb 	struct asc_softc *asc = (struct asc_softc *)sc;
196f976a714Ssimonb 
197f976a714Ssimonb 	asc->sc_active = 0;
198f976a714Ssimonb }
199f976a714Ssimonb 
200f976a714Ssimonb static int
asc_tc_intr(struct ncr53c9x_softc * sc)201c8dd740fSthorpej asc_tc_intr(struct ncr53c9x_softc *sc)
202f976a714Ssimonb {
203f976a714Ssimonb 	struct asc_softc *asc = (struct asc_softc *)sc;
204f976a714Ssimonb 	int trans, resid;
205f976a714Ssimonb 
206f976a714Ssimonb 	resid = 0;
207f976a714Ssimonb 	if (!asc->sc_ispullup &&
208f976a714Ssimonb 	    (resid = (NCR_READ_REG(sc, NCR_FFLAG) & NCRFIFO_FF)) != 0) {
209240c203dSsimonb 		NCR_DMA(("asc_tc_intr: empty FIFO of %d ", resid));
210f976a714Ssimonb 		DELAY(1);
211f976a714Ssimonb 	}
212f976a714Ssimonb 
213f976a714Ssimonb 	resid += NCR_READ_REG(sc, NCR_TCL);
214f976a714Ssimonb 	resid += NCR_READ_REG(sc, NCR_TCM) << 8;
215f976a714Ssimonb 
216f976a714Ssimonb 	trans = asc->sc_dmasize - resid;
217f976a714Ssimonb 
218f976a714Ssimonb 	if (asc->sc_ispullup)
219f976a714Ssimonb 		memcpy(asc->sc_target, asc->sc_bounce, trans);
220f976a714Ssimonb 	*asc->sc_dmalen -= trans;
221f976a714Ssimonb 	*asc->sc_dmaaddr += trans;
222f976a714Ssimonb 	asc->sc_active = 0;
223f976a714Ssimonb 
22478a1d236Stsutsui 	return 0;
225f976a714Ssimonb }
226f976a714Ssimonb 
227f976a714Ssimonb static int
asc_tc_setup(struct ncr53c9x_softc * sc,uint8_t ** addr,size_t * len,int datain,size_t * dmasize)22878a1d236Stsutsui asc_tc_setup(struct ncr53c9x_softc *sc, uint8_t **addr, size_t *len,
229c8dd740fSthorpej     int datain, size_t *dmasize)
230f976a714Ssimonb {
231f976a714Ssimonb 	struct asc_softc *asc = (struct asc_softc *)sc;
23278a1d236Stsutsui 	uint32_t tc_dmar;
233f976a714Ssimonb 	size_t size;
234f976a714Ssimonb 
23578a1d236Stsutsui 	asc->sc_dmaaddr = addr;
236f976a714Ssimonb 	asc->sc_dmalen = len;
237f976a714Ssimonb 	asc->sc_ispullup = datain;
238f976a714Ssimonb 
239240c203dSsimonb 	NCR_DMA(("asc_tc_setup: start %ld@%p, %s\n", (long)*asc->sc_dmalen,
240f976a714Ssimonb 	    *asc->sc_dmaaddr, datain ? "IN" : "OUT"));
241f976a714Ssimonb 
242f976a714Ssimonb 	size = *dmasize;
243f976a714Ssimonb 	if (size > PER_TGT_DMA_SIZE)
244f976a714Ssimonb 		size = PER_TGT_DMA_SIZE;
245f976a714Ssimonb 	*dmasize = asc->sc_dmasize = size;
246f976a714Ssimonb 
247240c203dSsimonb 	NCR_DMA(("asc_tc_setup: dmasize = %ld\n", (long)asc->sc_dmasize));
248f976a714Ssimonb 
249f976a714Ssimonb 	asc->sc_bounce = asc->sc_base + PMAZ_OFFSET_RAM;
250f976a714Ssimonb 	asc->sc_bounce += PER_TGT_DMA_SIZE *
251f976a714Ssimonb 	    sc->sc_nexus->xs->xs_periph->periph_target;
252f976a714Ssimonb 	asc->sc_target = *addr;
253f976a714Ssimonb 
254f976a714Ssimonb 	if (!asc->sc_ispullup)
255f976a714Ssimonb 		memcpy(asc->sc_bounce, asc->sc_target, size);
256f976a714Ssimonb 
257f976a714Ssimonb #if 1
258f976a714Ssimonb 	if (asc->sc_ispullup)
259f976a714Ssimonb 		tc_dmar = PMAZ_DMA_ADDR(asc->sc_bounce);
260f976a714Ssimonb 	else
261f976a714Ssimonb 		tc_dmar = PMAZ_DMAR_WRITE | PMAZ_DMA_ADDR(asc->sc_bounce);
262f976a714Ssimonb 	bus_space_write_4(asc->sc_bst, asc->sc_bsh, PMAZ_OFFSET_DMAR, tc_dmar);
263f976a714Ssimonb 	asc->sc_active = 1;
264f976a714Ssimonb #endif
26578a1d236Stsutsui 	return 0;
266f976a714Ssimonb }
267f976a714Ssimonb 
268f976a714Ssimonb static void
asc_tc_go(struct ncr53c9x_softc * sc)269c8dd740fSthorpej asc_tc_go(struct ncr53c9x_softc *sc)
270f976a714Ssimonb {
271f976a714Ssimonb #if 0
272f976a714Ssimonb 	struct asc_softc *asc = (struct asc_softc *)sc;
27378a1d236Stsutsui 	uint32_t tc_dmar;
274f976a714Ssimonb 
275f976a714Ssimonb 	if (asc->sc_ispullup)
276f976a714Ssimonb 		tc_dmar = PMAZ_DMA_ADDR(asc->sc_bounce);
277f976a714Ssimonb 	else
278f976a714Ssimonb 		tc_dmar = PMAZ_DMAR_WRITE | PMAZ_DMA_ADDR(asc->sc_bounce);
279f976a714Ssimonb 	bus_space_write_4(asc->sc_bst, asc->sc_bsh, PMAZ_OFFSET_DMAR, tc_dmar);
280f976a714Ssimonb 	asc->sc_active = 1;
281f976a714Ssimonb #endif
282f976a714Ssimonb }
283f976a714Ssimonb 
284f976a714Ssimonb /* NEVER CALLED BY MI 53C9x ENGINE INDEED */
285f976a714Ssimonb static void
asc_tc_stop(struct ncr53c9x_softc * sc)286c8dd740fSthorpej asc_tc_stop(struct ncr53c9x_softc *sc)
287f976a714Ssimonb {
288f976a714Ssimonb #if 0
289f976a714Ssimonb 	struct asc_softc *asc = (struct asc_softc *)sc;
290f976a714Ssimonb 
291f976a714Ssimonb 	if (asc->sc_ispullup)
292f976a714Ssimonb 		memcpy(asc->sc_target, asc->sc_bounce, asc->sc_dmasize);
293f976a714Ssimonb 	asc->sc_active = 0;
294f976a714Ssimonb #endif
295f976a714Ssimonb }
296f976a714Ssimonb 
297f976a714Ssimonb /*
298f976a714Ssimonb  * Glue functions.
299f976a714Ssimonb  */
30078a1d236Stsutsui static uint8_t
asc_read_reg(struct ncr53c9x_softc * sc,int reg)301c8dd740fSthorpej asc_read_reg(struct ncr53c9x_softc *sc, int reg)
302f976a714Ssimonb {
303f976a714Ssimonb 	struct asc_softc *asc = (struct asc_softc *)sc;
304f976a714Ssimonb 
30578a1d236Stsutsui 	return bus_space_read_4(asc->sc_bst, asc->sc_bsh,
30678a1d236Stsutsui 	    reg * sizeof(uint32_t)) & 0xff;
307f976a714Ssimonb }
308f976a714Ssimonb 
309f976a714Ssimonb static void
asc_write_reg(struct ncr53c9x_softc * sc,int reg,uint8_t val)31078a1d236Stsutsui asc_write_reg(struct ncr53c9x_softc *sc, int reg, uint8_t val)
311f976a714Ssimonb {
312f976a714Ssimonb 	struct asc_softc *asc = (struct asc_softc *)sc;
313f976a714Ssimonb 
314f976a714Ssimonb 	bus_space_write_4(asc->sc_bst, asc->sc_bsh,
31578a1d236Stsutsui 	    reg * sizeof(uint32_t), val);
316f976a714Ssimonb }
317f976a714Ssimonb 
318f976a714Ssimonb static int
asc_dma_isintr(struct ncr53c9x_softc * sc)319c8dd740fSthorpej asc_dma_isintr(struct ncr53c9x_softc *sc)
320f976a714Ssimonb {
32178a1d236Stsutsui 
32278a1d236Stsutsui 	return (NCR_READ_REG(sc, NCR_STAT) & NCRSTAT_INT) != 0;
323f976a714Ssimonb }
324f976a714Ssimonb 
325f976a714Ssimonb static int
asc_dma_isactive(struct ncr53c9x_softc * sc)326c8dd740fSthorpej asc_dma_isactive(struct ncr53c9x_softc *sc)
327f976a714Ssimonb {
328f976a714Ssimonb 	struct asc_softc *asc = (struct asc_softc *)sc;
329f976a714Ssimonb 
33078a1d236Stsutsui 	return asc->sc_active;
331f976a714Ssimonb }
332