1 /* $NetBSD: wdsc.c,v 1.25 2008/05/10 15:31:05 martin Exp $ */
4 * Copyright (c) 2009 Stephen M. Rumble
5 * Copyright (c) 2001 Wayne Knowles
8 * This code is derived from software contributed to The NetBSD Foundation
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. All advertising materials mentioning features or use of this software
20 * must display the following acknowledgement:
21 * This product includes software developed by the NetBSD
22 * Foundation, Inc. and its contributors.
23 * 4. Neither the name of The NetBSD Foundation nor the names of its
24 * contributors may be used to endorse or promote products derived
25 * from this software without specific prior written permission.
27 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
28 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
29 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
30 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
31 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
32 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
33 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
34 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
35 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
36 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
37 * POSSIBILITY OF SUCH DAMAGE.
40 #include <sys/cdefs.h>
41 __KERNEL_RCSID(0, "$NetBSD: wdsc.c,v 1.25 2008/05/10 15:31:05 martin Exp $");
43 #include <sys/param.h>
44 #include <sys/systm.h>
45 #include <sys/kernel.h>
46 #include <sys/device.h>
49 #include <dev/scsipi/scsi_all.h>
50 #include <dev/scsipi/scsipi_all.h>
51 #include <dev/scsipi/scsiconf.h>
53 #include <machine/cpu.h>
54 #include <machine/bus.h>
55 #include <machine/autoconf.h>
56 #include <machine/machtype.h>
57 #include <machine/sysconf.h>
59 #include <sgimips/ioc/oiocreg.h>
60 #include <sgimips/ioc/oiocvar.h>
62 #include <dev/ic/wd33c93reg.h>
63 #include <dev/ic/wd33c93var.h>
69 struct wd33c93_softc sc_wd33c93
; /* Must be first */
70 struct evcnt sc_intrcnt
; /* Interrupt counter */
71 bus_space_handle_t sc_sh
;
72 bus_space_tag_t sc_st
;
73 bus_dma_tag_t sc_dmat
;
74 bus_dmamap_t sc_dmamap
;
76 #define WDSC_DMA_ACTIVE 0x1
77 #define WDSC_DMA_MAPLOADED 0x2
78 struct oioc_dma_softc
{
79 bus_space_tag_t sc_bst
;
80 bus_space_handle_t sc_bsh
;
81 bus_dma_tag_t sc_dmat
;
86 bus_dmamap_t sc_dmamap
;
87 ssize_t sc_dlen
; /* # bytes transfered */
92 void oiocsc_attach (device_t
, device_t
, void *);
93 int oiocsc_match (device_t
, struct cfdata
*, void *);
95 CFATTACH_DECL_NEW(oiocsc
, sizeof(struct oiocsc_softc
),
96 oiocsc_match
, oiocsc_attach
, NULL
, NULL
);
98 int oiocsc_dmasetup (struct wd33c93_softc
*, void **, size_t *,
100 int oiocsc_dmago (struct wd33c93_softc
*);
101 void oiocsc_dmastop (struct wd33c93_softc
*);
102 void oiocsc_reset (struct wd33c93_softc
*);
103 int oiocsc_dmaintr (void *);
104 int oiocsc_scsiintr (void *);
107 * Always present on IP4, IP6, IP10.
110 oiocsc_match(device_t parent
, struct cfdata
*cf
, void *aux
)
112 struct oioc_attach_args
*oa
= aux
;
114 if (strcmp(oa
->oa_name
, cf
->cf_name
) == 0)
121 * Attach the wdsc driver
124 oiocsc_attach(device_t parent
, device_t self
, void *aux
)
126 struct oiocsc_softc
*osc
= device_private(self
);
127 struct wd33c93_softc
*sc
= &osc
->sc_wd33c93
;
128 struct oioc_attach_args
*oa
= aux
;
132 sc
->sc_regt
= oa
->oa_st
;
133 osc
->sc_st
= oa
->oa_st
;
134 osc
->sc_sh
= oa
->oa_sh
;
135 osc
->sc_dmat
= oa
->oa_dmat
;
137 if ((err
= bus_space_subregion(oa
->oa_st
, oa
->oa_sh
, OIOC_WD33C93_ASR
,
138 OIOC_WD33C93_ASR_SIZE
, &sc
->sc_asr_regh
)) != 0) {
139 printf(": unable to map regs, err=%d\n", err
);
143 if ((err
= bus_space_subregion(oa
->oa_st
, oa
->oa_sh
, OIOC_WD33C93_DATA
,
144 OIOC_WD33C93_DATA_SIZE
, &sc
->sc_data_regh
)) != 0) {
145 printf(": unable to map regs, err=%d\n", err
);
149 if (bus_dmamap_create(osc
->sc_dmat
,
150 OIOC_SCSI_DMA_NSEGS
* PAGE_SIZE
,
151 OIOC_SCSI_DMA_NSEGS
, PAGE_SIZE
, PAGE_SIZE
,
152 BUS_DMA_WAITOK
, &osc
->sc_dmamap
) != 0) {
153 printf(": failed to create dmamap\n");
157 sc
->sc_dmasetup
= oiocsc_dmasetup
;
158 sc
->sc_dmago
= oiocsc_dmago
;
159 sc
->sc_dmastop
= oiocsc_dmastop
;
160 sc
->sc_reset
= oiocsc_reset
;
162 sc
->sc_adapter
.adapt_request
= wd33c93_scsi_request
;
163 sc
->sc_adapter
.adapt_minphys
= minphys
;
165 sc
->sc_id
= 0; /* Host ID = 0 */
166 sc
->sc_clkfreq
= 100; /* 10MHz */
168 /* Disable DMA - it's not ready for prime time, see oiocsc_dmasetup */
170 sc
->sc_dmamode
= (oa
->oa_burst_dma
) ?
171 SBIC_CTL_BURST_DMA
: SBIC_CTL_DMA
;
176 evcnt_attach_dynamic(&osc
->sc_intrcnt
, EVCNT_TYPE_INTR
, NULL
,
177 device_xname(sc
->sc_dev
), "intr");
179 if ((cpu_intr_establish(oa
->oa_irq
, IPL_BIO
,
180 oiocsc_scsiintr
, sc
)) == NULL
) {
181 printf(": unable to establish interrupt!\n");
189 * Prime the hardware for a DMA transfer
191 * Requires splbio() interrupts to be disabled by the caller
193 * XXX- I'm not sure if this works properly yet. Primarily, I'm not sure
194 * that all ds_addr's after the first will be page aligned. If they're
195 * not, we apparently cannot use this DMA engine...
197 * Unfortunately, I'm getting mutex panics while testing with EFS (haven't
198 * tried FFS), so I cannot yet confirm whether this works or not.
201 oiocsc_dmasetup(struct wd33c93_softc
*dev
, void **addr
, size_t *len
, int datain
,
204 struct oiocsc_softc
*osc
= (void *)dev
;
205 struct oioc_dma_softc
*dsc
= &osc
->sc_oiocdma
;
209 KASSERT((osc
->sc_flags
& WDSC_DMA_ACTIVE
) == 0);
212 count
= dsc
->sc_dlen
= *len
;
215 bus_dmamap_t dmamap
= osc
->sc_dmamap
;
217 KASSERT((osc
->sc_flags
& WDSC_DMA_MAPLOADED
) == 0);
219 /* Build list of physical addresses for this transfer */
220 if ((err
= bus_dmamap_load(osc
->sc_dmat
, osc
->sc_dmamap
,
222 NULL
/* kernel address */,
223 BUS_DMA_NOWAIT
)) != 0)
224 panic("%s: bus_dmamap_load err=%d",
225 device_xname(dev
->sc_dev
), err
);
228 * We can map the contiguous buffer with up to 256 pages.
229 * The DMA low address register contains a 12-bit offset for
230 * the first page (in case the buffer isn't aligned). The 256
231 * high registers contain 16 bits each for page numbers.
233 * We will fill in the high registers here. The low register
234 * fires off the DMA engine and is set in oiocsc_dmago.
236 dsc
->sc_dmalow
= dmamap
->dm_segs
[0].ds_addr
&
237 OIOC_SCSI_DMA_LOW_ADDR_MASK
;
239 KASSERT(dmamap
->dm_nsegs
<= OIOC_SCSI_DMA_NSEGS
);
241 for (i
= 0; i
< dmamap
->dm_nsegs
; i
++) {
244 pgnum
= dmamap
->dm_segs
[i
].ds_addr
>>
245 OIOC_SCSI_DMA_HIGH_SHFT
;
247 bus_space_write_2(osc
->sc_st
, osc
->sc_sh
,
248 OIOC_SCSI_DMA_HIGH(i
), pgnum
);
251 osc
->sc_flags
|= WDSC_DMA_MAPLOADED
;
254 dsc
->sc_dmalow
|= OIOC_SCSI_DMA_LOW_ADDR_DMADIR
;
261 * Prime the hardware for the next DMA transfer
264 oiocsc_dmago(struct wd33c93_softc
*dev
)
266 struct oiocsc_softc
*osc
= (void *)dev
;
267 struct oioc_dma_softc
*dsc
= &osc
->sc_oiocdma
;
269 if (dsc
->sc_dlen
== 0)
272 KASSERT((osc
->sc_flags
& WDSC_DMA_ACTIVE
) == 0);
273 KASSERT((osc
->sc_flags
& WDSC_DMA_MAPLOADED
));
275 osc
->sc_flags
|= WDSC_DMA_ACTIVE
;
277 bus_dmamap_sync(osc
->sc_dmat
, osc
->sc_dmamap
, 0,
278 osc
->sc_dmamap
->dm_mapsize
,
279 BUS_DMASYNC_PREREAD
|BUS_DMASYNC_PREWRITE
);
282 bus_space_write_2(osc
->sc_st
, osc
->sc_sh
,
283 OIOC_SCSI_DMA_LOW
, dsc
->sc_dmalow
);
285 return(osc
->sc_dmamap
->dm_mapsize
);
289 * Stop DMA and unload active DMA maps
292 oiocsc_dmastop(struct wd33c93_softc
*dev
)
294 struct oiocsc_softc
*osc
= (void *)dev
;
296 if (osc
->sc_flags
& WDSC_DMA_ACTIVE
) {
297 /* Stop DMA, flush and sync */
298 bus_space_write_4(osc
->sc_st
, osc
->sc_sh
,
299 OIOC_SCSI_DMA_FLUSH
, 0);
300 bus_dmamap_sync(osc
->sc_dmat
, osc
->sc_dmamap
, 0,
301 osc
->sc_dmamap
->dm_mapsize
,
302 BUS_DMASYNC_POSTREAD
|BUS_DMASYNC_POSTWRITE
);
304 if (osc
->sc_flags
& WDSC_DMA_MAPLOADED
)
305 bus_dmamap_unload(osc
->sc_dmat
, osc
->sc_dmamap
);
306 osc
->sc_flags
&= ~(WDSC_DMA_ACTIVE
| WDSC_DMA_MAPLOADED
);
310 * Reset the controller.
313 oiocsc_reset(struct wd33c93_softc
*dev
)
315 struct oiocsc_softc
*osc
= (void *)dev
;
317 /* hard reset the chip */
318 bus_space_read_4(osc
->sc_st
, osc
->sc_sh
, OIOC_SCSI_RESET_ON
);
320 bus_space_read_4(osc
->sc_st
, osc
->sc_sh
, OIOC_SCSI_RESET_OFF
);
325 * WD33c93 SCSI controller interrupt
328 oiocsc_scsiintr(void *arg
)
330 struct wd33c93_softc
*dev
= arg
;
331 struct oiocsc_softc
*osc
= arg
;
334 found
= wd33c93_intr(dev
);
336 osc
->sc_intrcnt
.ev_count
++;