xref: /netbsd-src/sys/arch/mvme68k/dev/wdsc.c (revision 3b435a73967be44dfb4a27315acd72bfacde430c)
1 /*	$NetBSD: wdsc.c,v 1.15 1999/02/20 00:12:02 scw Exp $	*/
2 
3 /*
4  * Copyright (c) 1996 Steve Woodford
5  * Copyright (c) 1982, 1990 The Regents of the University of California.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. All advertising materials mentioning features or use of this software
17  *    must display the following acknowledgement:
18  *  This product includes software developed by the University of
19  *  California, Berkeley and its contributors.
20  * 4. Neither the name of the University nor the names of its contributors
21  *    may be used to endorse or promote products derived from this software
22  *    without specific prior written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34  * SUCH DAMAGE.
35  *
36  *  @(#)wdsc.c
37  */
38 
39 #include <sys/param.h>
40 #include <sys/systm.h>
41 #include <sys/kernel.h>
42 #include <sys/device.h>
43 
44 #include <dev/scsipi/scsi_all.h>
45 #include <dev/scsipi/scsipi_all.h>
46 #include <dev/scsipi/scsiconf.h>
47 
48 #include <machine/autoconf.h>
49 
50 #include <mvme68k/dev/dmavar.h>
51 #include <mvme68k/dev/pccreg.h>
52 #include <mvme68k/dev/pccvar.h>
53 #include <mvme68k/dev/sbicreg.h>
54 #include <mvme68k/dev/sbicvar.h>
55 #include <mvme68k/dev/wdscreg.h>
56 
57 void    wdsc_pcc_attach __P((struct device *, struct device *, void *));
58 int     wdsc_pcc_match  __P((struct device *, struct cfdata *, void *));
59 
60 void    wdsc_enintr     __P((struct sbic_softc *));
61 int     wdsc_dmago      __P((struct sbic_softc *, char *, int, int));
62 int     wdsc_dmanext    __P((struct sbic_softc *));
63 void    wdsc_dmastop    __P((struct sbic_softc *));
64 int     wdsc_dmaintr    __P((void *));
65 int     wdsc_scsiintr   __P((void *));
66 
67 struct scsipi_device wdsc_scsidev = {
68     NULL,       /* use default error handler */
69     NULL,       /* do not have a start functio */
70     NULL,       /* have no async handler */
71     NULL,       /* Use default done routine */
72 };
73 
74 struct cfattach wdsc_pcc_ca = {
75 	sizeof(struct sbic_softc), wdsc_pcc_match, wdsc_pcc_attach
76 };
77 
78 extern struct cfdriver wdsc_cd;
79 
80 
81 /*
82  * Match for SCSI devices on the onboard WD33C93 chip
83  */
84 int
85 wdsc_pcc_match(pdp, cf, auxp)
86     struct device *pdp;
87 	struct cfdata *cf;
88     void *auxp;
89 {
90     struct pcc_attach_args *pa = auxp;
91 
92     if (strcmp(pa->pa_name, wdsc_cd.cd_name))
93 	return (0);
94 
95     pa->pa_ipl = cf->pcccf_ipl;
96     return (1);
97 }
98 
99 /*
100  * Attach the wdsc driver
101  */
102 void
103 wdsc_pcc_attach(pdp, dp, auxp)
104     struct device *pdp, *dp;
105     void *auxp;
106 {
107     struct sbic_softc   *sc = (struct sbic_softc *)dp;
108     struct pcc_attach_args *pa = auxp;
109     static int          attached = 0;
110     int tmp;
111 
112     if ( attached )
113         panic("wdsc: Driver already attached!");
114 
115     attached = 1;
116 
117     sc->sc_enintr  = wdsc_enintr;
118     sc->sc_dmago   = wdsc_dmago;
119     sc->sc_dmanext = wdsc_dmanext;
120     sc->sc_dmastop = wdsc_dmastop;
121     sc->sc_dmacmd  = 0;
122 
123     sc->sc_adapter.scsipi_cmd = sbic_scsicmd;
124     sc->sc_adapter.scsipi_minphys = sbic_minphys;
125 
126     sc->sc_link.scsipi_scsi.channel        = SCSI_CHANNEL_ONLY_ONE;
127     sc->sc_link.adapter_softc  = sc;
128     sc->sc_link.scsipi_scsi.adapter_target = 7;
129     sc->sc_link.adapter        = &sc->sc_adapter;
130     sc->sc_link.device         = &wdsc_scsidev;
131     sc->sc_link.openings       = 2;
132     sc->sc_link.scsipi_scsi.max_target     = 7;
133     sc->sc_link.scsipi_scsi.max_lun = 7;
134     sc->sc_link.type = BUS_SCSI;
135 
136     printf(": WD33C93 SCSI, target %d\n",
137 		sc->sc_link.scsipi_scsi.adapter_target);
138 
139     sc->sc_cregs = (volatile void *)sys_pcc;
140     sc->sc_sbicp = (sbic_regmap_p) PCC_VADDR(pa->pa_offset);
141 
142     /*
143      * Eveything is a valid dma address.
144      */
145     sc->sc_dmamask = 0;
146 
147     /*
148      * The onboard WD33C93 of the '147 is usually clocked at 10MHz...
149      * (We use 10 times this for accuracy in later calculations)
150      */
151     sc->sc_clkfreq = 100;
152 
153     /*
154      * Initialise the hardware
155      */
156     sbicinit(sc);
157 
158     /*
159      * Fix up the interrupts
160      */
161     sc->sc_ipl = pa->pa_ipl & PCC_IMASK;
162 
163     sys_pcc->scsi_int = sc->sc_ipl | PCC_ICLEAR;
164     sys_pcc->dma_int  = sc->sc_ipl | PCC_ICLEAR;
165     sys_pcc->dma_csr  = 0;
166 
167     pccintr_establish(PCCV_SCSID, wdsc_dmaintr,  sc->sc_ipl, sc);
168     pccintr_establish(PCCV_SCSIP, wdsc_scsiintr, sc->sc_ipl, sc);
169 
170     sys_pcc->scsi_int = sc->sc_ipl | PCC_IENABLE | PCC_ICLEAR;
171 
172     /*
173      * Attach all scsi units on us, watching for boot device
174      * (see dk_establish).
175      */
176     tmp = bootpart;
177     if (PCC_PADDR(pa->pa_offset) != bootaddr)
178 	bootpart = -1;		/* invalid flag to dk_establish */
179     (void)config_found(dp, &sc->sc_link, scsiprint);
180     bootpart = tmp;		/* restore old value */
181 }
182 
183 /*
184  * Enable DMA interrupts
185  */
186 void
187 wdsc_enintr(dev)
188     struct sbic_softc *dev;
189 {
190     volatile struct pcc *pc = dev->sc_cregs;
191 
192     dev->sc_flags |= SBICF_INTR;
193 
194     pc->dma_int = dev->sc_ipl | PCC_IENABLE | PCC_ICLEAR;
195 }
196 
197 /*
198  * Prime the hardware for a DMA transfer
199  */
200 int
201 wdsc_dmago(dev, addr, count, flags)
202     struct sbic_softc *dev;
203     char *addr;
204     int count, flags;
205 {
206     volatile struct pcc *pc = dev->sc_cregs;
207 
208     /*
209      * Set up the command word based on flags
210      */
211     if ( (flags & DMAGO_READ) == 0 )
212         dev->sc_dmacmd = DMAC_CSR_ENABLE | DMAC_CSR_WRITE;
213     else
214         dev->sc_dmacmd = DMAC_CSR_ENABLE;
215 
216     dev->sc_flags |= SBICF_INTR;
217     dev->sc_tcnt   = dev->sc_cur->dc_count << 1;
218 
219     /*
220      * Prime the hardware.
221      * Note, it's probably not necessary to do this here, since dmanext
222      * is called just prior to the actual transfer.
223      */
224     pc->dma_csr   = 0;
225     pc->dma_int   = dev->sc_ipl | PCC_IENABLE | PCC_ICLEAR;
226     pc->dma_daddr = (unsigned long)dev->sc_cur->dc_addr;
227     pc->dma_bcnt  = (unsigned long)dev->sc_tcnt | (1 << 24);
228     pc->dma_csr   = dev->sc_dmacmd;
229 
230     return(dev->sc_tcnt);
231 }
232 
233 /*
234  * Prime the hardware for the next DMA transfer
235  */
236 int
237 wdsc_dmanext(dev)
238     struct sbic_softc *dev;
239 {
240     volatile struct pcc *pc = dev->sc_cregs;
241 
242     if ( dev->sc_cur > dev->sc_last ) {
243         /*
244          * Shouldn't happen !!
245          */
246         printf("wdsc_dmanext at end !!!\n");
247         wdsc_dmastop(dev);
248         return(0);
249     }
250 
251     dev->sc_tcnt = dev->sc_cur->dc_count << 1;
252 
253     /*
254      * Load the next DMA address
255      */
256     pc->dma_csr   = 0;
257     pc->dma_int   = dev->sc_ipl | PCC_IENABLE | PCC_ICLEAR;
258     pc->dma_daddr = (unsigned long)dev->sc_cur->dc_addr;
259     pc->dma_bcnt  = (unsigned long)dev->sc_tcnt | (1 << 24);
260     pc->dma_csr   = dev->sc_dmacmd;
261 
262     return(dev->sc_tcnt);
263 }
264 
265 /*
266  * Stop DMA, and disable interrupts
267  */
268 void
269 wdsc_dmastop(dev)
270     struct sbic_softc *dev;
271 {
272     volatile struct pcc *pc = dev->sc_cregs;
273     int                 s;
274 
275     s = splbio();
276 
277     pc->dma_csr    = 0;
278     pc->dma_int    = dev->sc_ipl | PCC_ICLEAR;
279 
280     splx(s);
281 }
282 
283 /*
284  * Come here following a DMA interrupt
285  */
286 int
287 wdsc_dmaintr(arg)
288     void *arg;
289 {
290     struct sbic_softc *dev = arg;
291     volatile struct pcc *pc = dev->sc_cregs;
292     int                 found = 0;
293 
294     /*
295      * Really a DMA interrupt?
296      */
297     if ( (pc->dma_int & 0x80) == 0 )
298         return(0);
299 
300     /*
301      * Was it a completion interrupt?
302      * XXXSCW Note: Support for other DMA interrupts is required, eg. buserr
303      */
304     if ( pc->dma_csr & DMAC_CSR_DONE ) {
305         ++found;
306 
307         pc->dma_int = dev->sc_ipl | PCC_IENABLE | PCC_ICLEAR;
308     }
309 
310     return(found);
311 }
312 
313 /*
314  * Come here for SCSI interrupts
315  */
316 int
317 wdsc_scsiintr(arg)
318     void *arg;
319 {
320     struct sbic_softc *dev = arg;
321     volatile struct pcc *pc = dev->sc_cregs;
322     int                 found;
323 
324     /*
325      * Really a SCSI interrupt?
326      */
327     if ( (pc->scsi_int & 0x80) == 0 )
328         return(0);
329 
330     /*
331      * Go handle it
332      */
333     found = sbicintr(dev);
334 
335     /*
336      * Acknowledge and clear the interrupt
337      */
338     pc->scsi_int = dev->sc_ipl | PCC_IENABLE | PCC_ICLEAR;
339 
340     return(found);
341 }
342