xref: /netbsd-src/sys/arch/sgimips/ioc/oiocsc.c (revision a0403cde04b791b433359b195a4146330fcbfe5f)
1*a0403cdeSmsaitoh /*	$NetBSD: oiocsc.c,v 1.4 2019/12/27 09:41:50 msaitoh Exp $	*/
2b610ccb0Srumble 
3b610ccb0Srumble /*
4b610ccb0Srumble  * Copyright (c) 2009 Stephen M. Rumble
5b610ccb0Srumble  * Copyright (c) 2001 Wayne Knowles
6b610ccb0Srumble  * All rights reserved.
7b610ccb0Srumble  *
8b610ccb0Srumble  * This code is derived from software contributed to The NetBSD Foundation
9b610ccb0Srumble  * by Wayne Knowles
10b610ccb0Srumble  *
11b610ccb0Srumble  * Redistribution and use in source and binary forms, with or without
12b610ccb0Srumble  * modification, are permitted provided that the following conditions
13b610ccb0Srumble  * are met:
14b610ccb0Srumble  * 1. Redistributions of source code must retain the above copyright
15b610ccb0Srumble  *    notice, this list of conditions and the following disclaimer.
16b610ccb0Srumble  * 2. Redistributions in binary form must reproduce the above copyright
17b610ccb0Srumble  *    notice, this list of conditions and the following disclaimer in the
18b610ccb0Srumble  *    documentation and/or other materials provided with the distribution.
19b610ccb0Srumble  * 3. All advertising materials mentioning features or use of this software
20b610ccb0Srumble  *    must display the following acknowledgement:
21b610ccb0Srumble  *        This product includes software developed by the NetBSD
22b610ccb0Srumble  *        Foundation, Inc. and its contributors.
23b610ccb0Srumble  * 4. Neither the name of The NetBSD Foundation nor the names of its
24b610ccb0Srumble  *    contributors may be used to endorse or promote products derived
25b610ccb0Srumble  *    from this software without specific prior written permission.
26b610ccb0Srumble  *
27b610ccb0Srumble  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
28b610ccb0Srumble  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
29b610ccb0Srumble  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
30b610ccb0Srumble  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
31b610ccb0Srumble  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
32b610ccb0Srumble  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
33b610ccb0Srumble  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
34b610ccb0Srumble  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
35b610ccb0Srumble  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
36b610ccb0Srumble  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
37b610ccb0Srumble  * POSSIBILITY OF SUCH DAMAGE.
38b610ccb0Srumble  */
39b610ccb0Srumble 
40b610ccb0Srumble #include <sys/cdefs.h>
41*a0403cdeSmsaitoh __KERNEL_RCSID(0, "$NetBSD: oiocsc.c,v 1.4 2019/12/27 09:41:50 msaitoh Exp $");
42b610ccb0Srumble 
43b610ccb0Srumble #include <sys/param.h>
44b610ccb0Srumble #include <sys/systm.h>
45b610ccb0Srumble #include <sys/kernel.h>
46b610ccb0Srumble #include <sys/device.h>
47b610ccb0Srumble #include <sys/buf.h>
48b610ccb0Srumble 
49b610ccb0Srumble #include <dev/scsipi/scsi_all.h>
50b610ccb0Srumble #include <dev/scsipi/scsipi_all.h>
51b610ccb0Srumble #include <dev/scsipi/scsiconf.h>
52b610ccb0Srumble 
53b610ccb0Srumble #include <machine/cpu.h>
54cf10107dSdyoung #include <sys/bus.h>
55b610ccb0Srumble #include <machine/autoconf.h>
56b610ccb0Srumble #include <machine/machtype.h>
57b610ccb0Srumble #include <machine/sysconf.h>
58b610ccb0Srumble 
59b610ccb0Srumble #include <sgimips/ioc/oiocreg.h>
60b610ccb0Srumble #include <sgimips/ioc/oiocvar.h>
61b610ccb0Srumble 
62b610ccb0Srumble #include <dev/ic/wd33c93reg.h>
63b610ccb0Srumble #include <dev/ic/wd33c93var.h>
64b610ccb0Srumble 
65b610ccb0Srumble #include <opt_kgdb.h>
66b610ccb0Srumble #include <sys/kgdb.h>
67b610ccb0Srumble 
68b610ccb0Srumble struct oiocsc_softc {
69b610ccb0Srumble 	struct wd33c93_softc	sc_wd33c93; /* Must be first */
70b610ccb0Srumble 	struct evcnt		sc_intrcnt; /* Interrupt counter */
71b610ccb0Srumble 	bus_space_handle_t	sc_sh;
72b610ccb0Srumble 	bus_space_tag_t		sc_st;
73b610ccb0Srumble 	bus_dma_tag_t		sc_dmat;
74b610ccb0Srumble 	bus_dmamap_t		sc_dmamap;
75b610ccb0Srumble 	int			sc_flags;
76b610ccb0Srumble #define	WDSC_DMA_ACTIVE			0x1
77b610ccb0Srumble #define	WDSC_DMA_MAPLOADED		0x2
78b610ccb0Srumble 	struct oioc_dma_softc {
79b610ccb0Srumble 		bus_space_tag_t		sc_bst;
80b610ccb0Srumble 		bus_space_handle_t	sc_bsh;
81b610ccb0Srumble 		bus_dma_tag_t		sc_dmat;
82b610ccb0Srumble 
83b610ccb0Srumble 		uint32_t		sc_flags;
84b610ccb0Srumble 		uint32_t		sc_dmalow;
85b610ccb0Srumble 		int			sc_ndesc;
86b610ccb0Srumble 		bus_dmamap_t		sc_dmamap;
87*a0403cdeSmsaitoh 		ssize_t			sc_dlen;    /* # bytes transferred */
88b610ccb0Srumble 	} sc_oiocdma;
89b610ccb0Srumble };
90b610ccb0Srumble 
91b610ccb0Srumble 
92b610ccb0Srumble void	oiocsc_attach	(device_t, device_t, void *);
93b610ccb0Srumble int	oiocsc_match	(device_t, struct cfdata *, void *);
94b610ccb0Srumble 
95b610ccb0Srumble CFATTACH_DECL_NEW(oiocsc, sizeof(struct oiocsc_softc),
96b610ccb0Srumble     oiocsc_match, oiocsc_attach, NULL, NULL);
97b610ccb0Srumble 
98b610ccb0Srumble int	oiocsc_dmasetup	(struct wd33c93_softc *, void **, size_t *,
99b610ccb0Srumble 				int, size_t *);
100b610ccb0Srumble int	oiocsc_dmago	(struct wd33c93_softc *);
101b610ccb0Srumble void	oiocsc_dmastop	(struct wd33c93_softc *);
102b610ccb0Srumble void	oiocsc_reset	(struct wd33c93_softc *);
103b610ccb0Srumble int	oiocsc_dmaintr	(void *);
104b610ccb0Srumble int	oiocsc_scsiintr	(void *);
105b610ccb0Srumble 
106b610ccb0Srumble /*
107b610ccb0Srumble  * Always present on IP4, IP6, IP10.
108b610ccb0Srumble  */
109b610ccb0Srumble int
oiocsc_match(device_t parent,struct cfdata * cf,void * aux)110b610ccb0Srumble oiocsc_match(device_t parent, struct cfdata *cf, void *aux)
111b610ccb0Srumble {
112b610ccb0Srumble 	struct oioc_attach_args *oa = aux;
113b610ccb0Srumble 
114b610ccb0Srumble 	if (strcmp(oa->oa_name, cf->cf_name) == 0)
115b610ccb0Srumble 		return (1);
116b610ccb0Srumble 
117b610ccb0Srumble 	return (0);
118b610ccb0Srumble }
119b610ccb0Srumble 
120b610ccb0Srumble /*
121b610ccb0Srumble  * Attach the wdsc driver
122b610ccb0Srumble  */
123b610ccb0Srumble void
oiocsc_attach(device_t parent,device_t self,void * aux)124b610ccb0Srumble oiocsc_attach(device_t parent, device_t self, void *aux)
125b610ccb0Srumble {
126b610ccb0Srumble 	struct oiocsc_softc *osc = device_private(self);
127b610ccb0Srumble 	struct wd33c93_softc *sc = &osc->sc_wd33c93;
128b610ccb0Srumble 	struct oioc_attach_args *oa = aux;
129b610ccb0Srumble 	int err;
130b610ccb0Srumble 
131b610ccb0Srumble 	sc->sc_dev   = self;
132b610ccb0Srumble 	sc->sc_regt  = oa->oa_st;
133b610ccb0Srumble 	osc->sc_st   = oa->oa_st;
134b610ccb0Srumble 	osc->sc_sh   = oa->oa_sh;
135b610ccb0Srumble 	osc->sc_dmat = oa->oa_dmat;
136b610ccb0Srumble 
137b610ccb0Srumble 	if ((err = bus_space_subregion(oa->oa_st, oa->oa_sh, OIOC_WD33C93_ASR,
138b610ccb0Srumble 	    OIOC_WD33C93_ASR_SIZE, &sc->sc_asr_regh)) != 0) {
139b610ccb0Srumble 		printf(": unable to map regs, err=%d\n", err);
140b610ccb0Srumble 		return;
141b610ccb0Srumble 	}
142b610ccb0Srumble 
143b610ccb0Srumble 	if ((err = bus_space_subregion(oa->oa_st, oa->oa_sh, OIOC_WD33C93_DATA,
144b610ccb0Srumble 	    OIOC_WD33C93_DATA_SIZE, &sc->sc_data_regh)) != 0) {
145b610ccb0Srumble 		printf(": unable to map regs, err=%d\n", err);
146b610ccb0Srumble 		return;
147b610ccb0Srumble 	}
148b610ccb0Srumble 
149b610ccb0Srumble 	if (bus_dmamap_create(osc->sc_dmat,
150b610ccb0Srumble 	    OIOC_SCSI_DMA_NSEGS * PAGE_SIZE,
151b610ccb0Srumble 	    OIOC_SCSI_DMA_NSEGS, PAGE_SIZE, PAGE_SIZE,
152b610ccb0Srumble 	    BUS_DMA_WAITOK, &osc->sc_dmamap) != 0) {
153b610ccb0Srumble 		printf(": failed to create dmamap\n");
154b610ccb0Srumble 		return;
155b610ccb0Srumble 	}
156b610ccb0Srumble 
157b610ccb0Srumble 	sc->sc_dmasetup = oiocsc_dmasetup;
158b610ccb0Srumble 	sc->sc_dmago    = oiocsc_dmago;
159b610ccb0Srumble 	sc->sc_dmastop  = oiocsc_dmastop;
160b610ccb0Srumble 	sc->sc_reset	= oiocsc_reset;
161b610ccb0Srumble 
162b610ccb0Srumble 	sc->sc_adapter.adapt_request = wd33c93_scsi_request;
163b610ccb0Srumble 	sc->sc_adapter.adapt_minphys = minphys;
164b610ccb0Srumble 
165b610ccb0Srumble 	sc->sc_id = 0;					/* Host ID = 0 */
166b610ccb0Srumble 	sc->sc_clkfreq = 100;				/* 10MHz */
167b610ccb0Srumble 
168b610ccb0Srumble 	/* Disable DMA - it's not ready for prime time, see oiocsc_dmasetup */
169b610ccb0Srumble #if 0
170b610ccb0Srumble 	sc->sc_dmamode = (oa->oa_burst_dma) ?
171b610ccb0Srumble 	    SBIC_CTL_BURST_DMA : SBIC_CTL_DMA;
172b610ccb0Srumble #else
173b610ccb0Srumble 	sc->sc_dmamode = 0;
174b610ccb0Srumble #endif
175b610ccb0Srumble 
176b610ccb0Srumble 	evcnt_attach_dynamic(&osc->sc_intrcnt, EVCNT_TYPE_INTR, NULL,
177b610ccb0Srumble 	    device_xname(sc->sc_dev), "intr");
178b610ccb0Srumble 
179b610ccb0Srumble 	if ((cpu_intr_establish(oa->oa_irq, IPL_BIO,
180b610ccb0Srumble 	     oiocsc_scsiintr, sc)) == NULL) {
181b610ccb0Srumble 		printf(": unable to establish interrupt!\n");
182b610ccb0Srumble 		return;
183b610ccb0Srumble 	}
184b610ccb0Srumble 
185b610ccb0Srumble 	wd33c93_attach(sc);
186b610ccb0Srumble }
187b610ccb0Srumble 
188b610ccb0Srumble /*
189b610ccb0Srumble  * Prime the hardware for a DMA transfer
190b610ccb0Srumble  *
191b610ccb0Srumble  * Requires splbio() interrupts to be disabled by the caller
192b610ccb0Srumble  *
193b610ccb0Srumble  * XXX- I'm not sure if this works properly yet. Primarily, I'm not sure
194b610ccb0Srumble  *      that all ds_addr's after the first will be page aligned. If they're
195b610ccb0Srumble  *      not, we apparently cannot use this DMA engine...
196b610ccb0Srumble  *
197b610ccb0Srumble  *      Unfortunately, I'm getting mutex panics while testing with EFS (haven't
198b610ccb0Srumble  *      tried FFS), so I cannot yet confirm whether this works or not.
199b610ccb0Srumble  */
200b610ccb0Srumble int
oiocsc_dmasetup(struct wd33c93_softc * dev,void ** addr,size_t * len,int datain,size_t * dmasize)201b610ccb0Srumble oiocsc_dmasetup(struct wd33c93_softc *dev, void **addr, size_t *len, int datain,
202b610ccb0Srumble     size_t *dmasize)
203b610ccb0Srumble {
204b610ccb0Srumble 	struct oiocsc_softc *osc = (void *)dev;
205b610ccb0Srumble 	struct oioc_dma_softc *dsc = &osc->sc_oiocdma;
206b610ccb0Srumble 	int count, err, i;
207b610ccb0Srumble 	void *vaddr;
208b610ccb0Srumble 
209b610ccb0Srumble 	KASSERT((osc->sc_flags & WDSC_DMA_ACTIVE) == 0);
210b610ccb0Srumble 
211b610ccb0Srumble 	vaddr = *addr;
212b610ccb0Srumble 	count = dsc->sc_dlen = *len;
213b610ccb0Srumble 
214b610ccb0Srumble 	if (count) {
215b610ccb0Srumble 		bus_dmamap_t dmamap = osc->sc_dmamap;
216b610ccb0Srumble 
217b610ccb0Srumble 		KASSERT((osc->sc_flags & WDSC_DMA_MAPLOADED) == 0);
218b610ccb0Srumble 
219b610ccb0Srumble 		/* Build list of physical addresses for this transfer */
220b610ccb0Srumble 		if ((err = bus_dmamap_load(osc->sc_dmat, osc->sc_dmamap,
221b610ccb0Srumble 				vaddr, count,
222b610ccb0Srumble 				NULL /* kernel address */,
223b610ccb0Srumble 				BUS_DMA_NOWAIT)) != 0)
224b610ccb0Srumble 			panic("%s: bus_dmamap_load err=%d",
225b610ccb0Srumble 			      device_xname(dev->sc_dev), err);
226b610ccb0Srumble 
227b610ccb0Srumble 		/*
228b610ccb0Srumble 		 * We can map the contiguous buffer with up to 256 pages.
229b610ccb0Srumble 		 * The DMA low address register contains a 12-bit offset for
230b610ccb0Srumble 		 * the first page (in case the buffer isn't aligned). The 256
231b610ccb0Srumble 		 * high registers contain 16 bits each for page numbers.
232b610ccb0Srumble 		 *
233b610ccb0Srumble 		 * We will fill in the high registers here. The low register
234b610ccb0Srumble 		 * fires off the DMA engine and is set in oiocsc_dmago.
235b610ccb0Srumble 		 */
236b610ccb0Srumble 		dsc->sc_dmalow = dmamap->dm_segs[0].ds_addr &
237b610ccb0Srumble 		    OIOC_SCSI_DMA_LOW_ADDR_MASK;
238b610ccb0Srumble 
239b610ccb0Srumble 		KASSERT(dmamap->dm_nsegs <= OIOC_SCSI_DMA_NSEGS);
240b610ccb0Srumble 
241b610ccb0Srumble 		for (i = 0; i < dmamap->dm_nsegs; i++) {
242b610ccb0Srumble 			uint16_t pgnum;
243b610ccb0Srumble 
244b610ccb0Srumble 			pgnum = dmamap->dm_segs[i].ds_addr >>
245b610ccb0Srumble 			    OIOC_SCSI_DMA_HIGH_SHFT;
246b610ccb0Srumble 
247b610ccb0Srumble 			bus_space_write_2(osc->sc_st, osc->sc_sh,
248b610ccb0Srumble 			    OIOC_SCSI_DMA_HIGH(i), pgnum);
249b610ccb0Srumble 		}
250b610ccb0Srumble 
251b610ccb0Srumble 		osc->sc_flags |= WDSC_DMA_MAPLOADED;
252b610ccb0Srumble 
253b610ccb0Srumble 		if (datain)
254b610ccb0Srumble 			dsc->sc_dmalow |= OIOC_SCSI_DMA_LOW_ADDR_DMADIR;
255b610ccb0Srumble 	}
256b610ccb0Srumble 
257b610ccb0Srumble 	return (count);
258b610ccb0Srumble }
259b610ccb0Srumble 
260b610ccb0Srumble /*
261b610ccb0Srumble  * Prime the hardware for the next DMA transfer
262b610ccb0Srumble  */
263b610ccb0Srumble int
oiocsc_dmago(struct wd33c93_softc * dev)264b610ccb0Srumble oiocsc_dmago(struct wd33c93_softc *dev)
265b610ccb0Srumble {
266b610ccb0Srumble 	struct oiocsc_softc *osc = (void *)dev;
267b610ccb0Srumble 	struct oioc_dma_softc *dsc = &osc->sc_oiocdma;
268b610ccb0Srumble 
269b610ccb0Srumble 	if (dsc->sc_dlen == 0)
270b610ccb0Srumble 		return(0);
271b610ccb0Srumble 
272b610ccb0Srumble 	KASSERT((osc->sc_flags & WDSC_DMA_ACTIVE) == 0);
273b610ccb0Srumble 	KASSERT((osc->sc_flags & WDSC_DMA_MAPLOADED));
274b610ccb0Srumble 
275b610ccb0Srumble 	osc->sc_flags |= WDSC_DMA_ACTIVE;
276b610ccb0Srumble 
277b610ccb0Srumble 	bus_dmamap_sync(osc->sc_dmat, osc->sc_dmamap, 0,
278b610ccb0Srumble 	    		osc->sc_dmamap->dm_mapsize,
279b610ccb0Srumble 			BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE);
280b610ccb0Srumble 
281b610ccb0Srumble 	/* Blastoff! */
282b610ccb0Srumble 	bus_space_write_2(osc->sc_st, osc->sc_sh,
283b610ccb0Srumble 	    OIOC_SCSI_DMA_LOW, dsc->sc_dmalow);
284b610ccb0Srumble 
285b610ccb0Srumble 	return(osc->sc_dmamap->dm_mapsize);
286b610ccb0Srumble }
287b610ccb0Srumble 
288b610ccb0Srumble /*
289b610ccb0Srumble  * Stop DMA and unload active DMA maps
290b610ccb0Srumble  */
291b610ccb0Srumble void
oiocsc_dmastop(struct wd33c93_softc * dev)292b610ccb0Srumble oiocsc_dmastop(struct wd33c93_softc *dev)
293b610ccb0Srumble {
294b610ccb0Srumble 	struct oiocsc_softc *osc = (void *)dev;
295b610ccb0Srumble 
296b610ccb0Srumble 	if (osc->sc_flags & WDSC_DMA_ACTIVE) {
297b610ccb0Srumble 		/* Stop DMA, flush and sync */
298b610ccb0Srumble 		bus_space_write_4(osc->sc_st, osc->sc_sh,
299b610ccb0Srumble 		    OIOC_SCSI_DMA_FLUSH, 0);
300b610ccb0Srumble 		bus_dmamap_sync(osc->sc_dmat, osc->sc_dmamap, 0,
301b610ccb0Srumble 		    osc->sc_dmamap->dm_mapsize,
302b610ccb0Srumble 		    BUS_DMASYNC_POSTREAD|BUS_DMASYNC_POSTWRITE);
303b610ccb0Srumble 	}
304b610ccb0Srumble 	if (osc->sc_flags & WDSC_DMA_MAPLOADED)
305b610ccb0Srumble 		bus_dmamap_unload(osc->sc_dmat, osc->sc_dmamap);
306b610ccb0Srumble 	osc->sc_flags &= ~(WDSC_DMA_ACTIVE | WDSC_DMA_MAPLOADED);
307b610ccb0Srumble }
308b610ccb0Srumble 
309b610ccb0Srumble /*
310b610ccb0Srumble  * Reset the controller.
311b610ccb0Srumble  */
312b610ccb0Srumble void
oiocsc_reset(struct wd33c93_softc * dev)313b610ccb0Srumble oiocsc_reset(struct wd33c93_softc *dev)
314b610ccb0Srumble {
315b610ccb0Srumble 	struct oiocsc_softc *osc = (void *)dev;
316b610ccb0Srumble 
317b610ccb0Srumble 	/* hard reset the chip */
318b610ccb0Srumble 	bus_space_read_4(osc->sc_st, osc->sc_sh, OIOC_SCSI_RESET_ON);
319b610ccb0Srumble 	delay(1000);
320b610ccb0Srumble 	bus_space_read_4(osc->sc_st, osc->sc_sh, OIOC_SCSI_RESET_OFF);
321b610ccb0Srumble 	delay(1000);
322b610ccb0Srumble }
323b610ccb0Srumble 
324b610ccb0Srumble /*
325b610ccb0Srumble  * WD33c93 SCSI controller interrupt
326b610ccb0Srumble  */
327b610ccb0Srumble int
oiocsc_scsiintr(void * arg)328b610ccb0Srumble oiocsc_scsiintr(void *arg)
329b610ccb0Srumble {
330b610ccb0Srumble 	struct wd33c93_softc *dev = arg;
331b610ccb0Srumble 	struct oiocsc_softc *osc = arg;
332b610ccb0Srumble 	int found;
333b610ccb0Srumble 
334b610ccb0Srumble 	found = wd33c93_intr(dev);
335b610ccb0Srumble 	if (found)
336b610ccb0Srumble 		osc->sc_intrcnt.ev_count++;
337b610ccb0Srumble 	return(found);
338b610ccb0Srumble }
339