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