wdsc.c revision 1.2 1 /* $NetBSD: wdsc.c,v 1.2 1996/04/26 19:00:20 chuck 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 <scsi/scsi_all.h>
45 #include <scsi/scsiconf.h>
46
47 #include <mvme68k/dev/dmavar.h>
48 #include <mvme68k/dev/pccreg.h>
49 #include <mvme68k/dev/pccvar.h>
50 #include <mvme68k/dev/sbicreg.h>
51 #include <mvme68k/dev/sbicvar.h>
52 #include <mvme68k/dev/wdscreg.h>
53
54 int wdscprint __P((void *auxp, char *));
55 void wdsc_pcc_attach __P((struct device *, struct device *, void *));
56 int wdsc_pcc_match __P((struct device *, void *, void *));
57
58 void wdsc_enintr __P((struct sbic_softc *));
59 int wdsc_dmago __P((struct sbic_softc *, char *, int, int));
60 int wdsc_dmanext __P((struct sbic_softc *));
61 void wdsc_dmastop __P((struct sbic_softc *));
62 int wdsc_dmaintr __P((void *));
63 int wdsc_scsiintr __P((void *));
64
65 struct scsi_adapter wdsc_scsiswitch = {
66 sbic_scsicmd,
67 sbic_minphys,
68 0, /* no lun support */
69 0, /* no lun support */
70 };
71
72 struct scsi_device wdsc_scsidev = {
73 NULL, /* use default error handler */
74 NULL, /* do not have a start functio */
75 NULL, /* have no async handler */
76 NULL, /* Use default done routine */
77 };
78
79 struct cfattach wdsc_pcc_ca = {
80 sizeof(struct sbic_softc), wdsc_pcc_match, wdsc_pcc_attach
81 };
82
83 struct cfdriver wdsc_cd = {
84 NULL, "wdsc", DV_DULL, NULL, 0
85 };
86
87 /*
88 * Define 'scsi_nosync = 0x00' to enable sync SCSI mode.
89 * This is untested as yet, use at your own risk...
90 */
91 u_long scsi_nosync = 0xff;
92 int shift_nosync = 0;
93
94 /*
95 * Match for SCSI devices on the onboard WD33C93 chip
96 */
97 int
98 wdsc_pcc_match(pdp, match, auxp)
99 struct device *pdp;
100 void *match, *auxp;
101 {
102 struct cfdata *cf = match;
103 struct pcc_attach_args *pa = auxp;
104
105 if (strcmp(pa->pa_name, wdsc_cd.cd_name))
106 return (0);
107
108 pa->pa_ipl = cf->pcccf_ipl;
109 return (1);
110 }
111
112 /*
113 * Attach the wdsc driver
114 */
115 void
116 wdsc_pcc_attach(pdp, dp, auxp)
117 struct device *pdp, *dp;
118 void *auxp;
119 {
120 struct sbic_softc *sc = (struct sbic_softc *)dp;
121 struct pcc_attach_args *pa = auxp;
122 static int attached = 0;
123
124 if ( attached )
125 panic("wdsc: Driver already attached!");
126
127 attached = 1;
128
129 sc->sc_enintr = wdsc_enintr;
130 sc->sc_dmago = wdsc_dmago;
131 sc->sc_dmanext = wdsc_dmanext;
132 sc->sc_dmastop = wdsc_dmastop;
133 sc->sc_dmacmd = 0;
134
135 sc->sc_link.adapter_softc = sc;
136 sc->sc_link.adapter_target = 7;
137 sc->sc_link.adapter = &wdsc_scsiswitch;
138 sc->sc_link.device = &wdsc_scsidev;
139 sc->sc_link.openings = 2;
140
141 printf(": WD33C93 SCSI, target %d\n", sc->sc_link.adapter_target);
142
143 sc->sc_cregs = (volatile void *)sys_pcc;
144 sc->sc_sbicp = (sbic_regmap_p) PCC_VADDR(pa->pa_offset);
145
146 /*
147 * Eveything is a valid dma address.
148 */
149 sc->sc_dmamask = 0;
150
151 /*
152 * The onboard WD33C93 of the '147 is usually clocked at 10MHz...
153 * (We use 10 times this for accuracy in later calculations)
154 */
155 sc->sc_clkfreq = 100;
156
157 /*
158 * Initialise the hardware
159 */
160 sbicinit(sc);
161
162 /*
163 * Fix up the interrupts
164 */
165 sc->sc_ipl = pa->pa_ipl & PCC_IMASK;
166
167 sys_pcc->scsi_int = sc->sc_ipl | PCC_ICLEAR;
168 sys_pcc->dma_int = sc->sc_ipl | PCC_ICLEAR;
169 sys_pcc->dma_csr = 0;
170
171 pccintr_establish(PCCV_SCSID, wdsc_dmaintr, sc->sc_ipl, sc);
172 pccintr_establish(PCCV_SCSIP, wdsc_scsiintr, sc->sc_ipl, sc);
173
174 sys_pcc->scsi_int = sc->sc_ipl | PCC_IENABLE | PCC_ICLEAR;
175
176 /*
177 * Attach all scsi units on us
178 */
179 (void)config_found(dp, &sc->sc_link, wdscprint);
180 }
181
182 /*
183 * print diag if pnp is NULL else just extra
184 */
185 int
186 wdscprint(auxp, pnp)
187 void *auxp;
188 char *pnp;
189 {
190
191 /* Only scsibusses can attach to wdscs...easy. */
192 if (pnp)
193 printf("scsibus at %s", pnp);
194
195 return (UNCONF);
196 }
197
198
199 /*
200 * Enable DMA interrupts
201 */
202 void
203 wdsc_enintr(dev)
204 struct sbic_softc *dev;
205 {
206 volatile struct pcc *pc = dev->sc_cregs;
207
208 dev->sc_flags |= SBICF_INTR;
209
210 pc->dma_int = dev->sc_ipl | PCC_IENABLE | PCC_ICLEAR;
211 }
212
213 /*
214 * Prime the hardware for a DMA transfer
215 */
216 int
217 wdsc_dmago(dev, addr, count, flags)
218 struct sbic_softc *dev;
219 char *addr;
220 int count, flags;
221 {
222 volatile struct pcc *pc = dev->sc_cregs;
223
224 /*
225 * Set up the command word based on flags
226 */
227 if ( (flags & DMAGO_READ) == 0 )
228 dev->sc_dmacmd = DMAC_CSR_ENABLE | DMAC_CSR_WRITE;
229 else
230 dev->sc_dmacmd = DMAC_CSR_ENABLE;
231
232 dev->sc_flags |= SBICF_INTR;
233 dev->sc_tcnt = dev->sc_cur->dc_count << 1;
234
235 /*
236 * Prime the hardware.
237 * Note, it's probably not necessary to do this here, since dmanext
238 * is called just prior to the actual transfer.
239 */
240 pc->dma_csr = 0;
241 pc->dma_int = dev->sc_ipl | PCC_IENABLE | PCC_ICLEAR;
242 pc->dma_daddr = (unsigned long)dev->sc_cur->dc_addr;
243 pc->dma_bcnt = (unsigned long)dev->sc_tcnt | (1 << 24);
244 pc->dma_csr = dev->sc_dmacmd;
245
246 return(dev->sc_tcnt);
247 }
248
249 /*
250 * Prime the hardware for the next DMA transfer
251 */
252 int
253 wdsc_dmanext(dev)
254 struct sbic_softc *dev;
255 {
256 volatile struct pcc *pc = dev->sc_cregs;
257
258 if ( dev->sc_cur > dev->sc_last ) {
259 /*
260 * Shouldn't happen !!
261 */
262 printf("wdsc_dmanext at end !!!\n");
263 wdsc_dmastop(dev);
264 return(0);
265 }
266
267 dev->sc_tcnt = dev->sc_cur->dc_count << 1;
268
269 /*
270 * Load the next DMA address
271 */
272 pc->dma_csr = 0;
273 pc->dma_int = dev->sc_ipl | PCC_IENABLE | PCC_ICLEAR;
274 pc->dma_daddr = (unsigned long)dev->sc_cur->dc_addr;
275 pc->dma_bcnt = (unsigned long)dev->sc_tcnt | (1 << 24);
276 pc->dma_csr = dev->sc_dmacmd;
277
278 return(dev->sc_tcnt);
279 }
280
281 /*
282 * Stop DMA, and disable interrupts
283 */
284 void
285 wdsc_dmastop(dev)
286 struct sbic_softc *dev;
287 {
288 volatile struct pcc *pc = dev->sc_cregs;
289 int s;
290
291 s = splbio();
292
293 pc->dma_csr = 0;
294 pc->dma_int = dev->sc_ipl | PCC_ICLEAR;
295
296 splx(s);
297 }
298
299 /*
300 * Come here following a DMA interrupt
301 */
302 int
303 wdsc_dmaintr(arg)
304 void *arg;
305 {
306 struct sbic_softc *dev = arg;
307 volatile struct pcc *pc = dev->sc_cregs;
308 int found = 0;
309
310 /*
311 * Really a DMA interrupt?
312 */
313 if ( (pc->dma_int & 0x80) == 0 )
314 return(0);
315
316 /*
317 * Was it a completion interrupt?
318 * XXXSCW Note: Support for other DMA interrupts is required, eg. buserr
319 */
320 if ( pc->dma_csr & DMAC_CSR_DONE ) {
321 ++found;
322
323 pc->dma_int = dev->sc_ipl | PCC_IENABLE | PCC_ICLEAR;
324 }
325
326 return(found);
327 }
328
329 /*
330 * Come here for SCSI interrupts
331 */
332 int
333 wdsc_scsiintr(arg)
334 void *arg;
335 {
336 struct sbic_softc *dev = arg;
337 volatile struct pcc *pc = dev->sc_cregs;
338 int found;
339
340 /*
341 * Really a SCSI interrupt?
342 */
343 if ( (pc->scsi_int & 0x80) == 0 )
344 return(0);
345
346 /*
347 * Go handle it
348 */
349 found = sbicintr(dev);
350
351 /*
352 * Acknowledge and clear the interrupt
353 */
354 pc->scsi_int = dev->sc_ipl | PCC_IENABLE | PCC_ICLEAR;
355
356 return(found);
357 }
358