1227006Smarius/*-
2227006Smarius * Copyright (c) 2011 Marius Strobl <marius@FreeBSD.org>
3227006Smarius * All rights reserved.
4227006Smarius *
5227006Smarius * Redistribution and use in source and binary forms, with or without
6227006Smarius * modification, are permitted provided that the following conditions
7227006Smarius * are met:
8227006Smarius * 1. Redistributions of source code must retain the above copyright
9227006Smarius *    notice, this list of conditions and the following disclaimer.
10227006Smarius * 2. Redistributions in binary form must reproduce the above copyright
11227006Smarius *    notice, this list of conditions and the following disclaimer in the
12227006Smarius *    documentation and/or other materials provided with the distribution.
13227006Smarius *
14227006Smarius * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15227006Smarius * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16227006Smarius * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17227006Smarius * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18227006Smarius * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19227006Smarius * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20227006Smarius * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21227006Smarius * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22227006Smarius * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23227006Smarius * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24227006Smarius * SUCH DAMAGE.
25227006Smarius */
26227006Smarius
27227006Smarius/*	$NetBSD: pcscp.c,v 1.45 2010/11/13 13:52:08 uebayasi Exp $	*/
28227006Smarius
29227006Smarius/*-
30227006Smarius * Copyright (c) 1997, 1998, 1999 The NetBSD Foundation, Inc.
31227006Smarius * All rights reserved.
32227006Smarius *
33227006Smarius * This code is derived from software contributed to The NetBSD Foundation
34227006Smarius * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
35227006Smarius * NASA Ames Research Center; Izumi Tsutsui.
36227006Smarius *
37227006Smarius * Redistribution and use in source and binary forms, with or without
38227006Smarius * modification, are permitted provided that the following conditions
39227006Smarius * are met:
40227006Smarius * 1. Redistributions of source code must retain the above copyright
41227006Smarius *    notice, this list of conditions and the following disclaimer.
42227006Smarius * 2. Redistributions in binary form must reproduce the above copyright
43227006Smarius *    notice, this list of conditions and the following disclaimer in the
44227006Smarius *    documentation and/or other materials provided with the distribution.
45227006Smarius *
46227006Smarius * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
47227006Smarius * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
48227006Smarius * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
49227006Smarius * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
50227006Smarius * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
51227006Smarius * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
52227006Smarius * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
53227006Smarius * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
54227006Smarius * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
55227006Smarius * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
56227006Smarius * POSSIBILITY OF SUCH DAMAGE.
57227006Smarius */
58227006Smarius
59227006Smarius/*
60227006Smarius * esp_pci.c: device dependent code for AMD Am53c974 (PCscsi-PCI)
61227006Smarius * written by Izumi Tsutsui <tsutsui@NetBSD.org>
62227006Smarius *
63227006Smarius * Technical manual available at
64227006Smarius * http://www.amd.com/files/connectivitysolutions/networking/archivednetworking/19113.pdf
65227006Smarius */
66227006Smarius
67227006Smarius#include <sys/cdefs.h>
68227006Smarius__FBSDID("$FreeBSD$");
69227006Smarius
70227006Smarius#include <sys/param.h>
71227006Smarius#include <sys/systm.h>
72227006Smarius#include <sys/bus.h>
73227006Smarius#include <sys/endian.h>
74227006Smarius#include <sys/kernel.h>
75227006Smarius#include <sys/lock.h>
76227006Smarius#include <sys/module.h>
77227006Smarius#include <sys/mutex.h>
78227006Smarius#include <sys/resource.h>
79227006Smarius#include <sys/rman.h>
80227006Smarius
81227006Smarius#include <machine/bus.h>
82227006Smarius#include <machine/resource.h>
83227006Smarius
84227006Smarius#include <cam/cam.h>
85227006Smarius#include <cam/cam_ccb.h>
86227006Smarius#include <cam/scsi/scsi_all.h>
87227006Smarius
88227006Smarius#include <dev/pci/pcireg.h>
89227006Smarius#include <dev/pci/pcivar.h>
90227006Smarius
91227006Smarius#include <dev/esp/ncr53c9xreg.h>
92227006Smarius#include <dev/esp/ncr53c9xvar.h>
93227006Smarius
94227006Smarius#include <dev/esp/am53c974reg.h>
95227006Smarius
96227006Smarius#define	PCI_DEVICE_ID_AMD53C974	0x20201022
97227006Smarius
98227006Smariusstruct esp_pci_softc {
99227006Smarius	struct ncr53c9x_softc	sc_ncr53c9x;	/* glue to MI code */
100299050Sadrian	device_t		sc_dev;
101227006Smarius
102227006Smarius	struct resource *sc_res[2];
103227006Smarius#define	ESP_PCI_RES_INTR	0
104227006Smarius#define	ESP_PCI_RES_IO		1
105227006Smarius
106227006Smarius	bus_dma_tag_t		sc_pdmat;
107227006Smarius
108227006Smarius	bus_dma_tag_t		sc_xferdmat;	/* DMA tag for transfers */
109227006Smarius	bus_dmamap_t		sc_xferdmam;	/* DMA map for transfers */
110227006Smarius
111227006Smarius	void			*sc_ih;		/* interrupt handler */
112227006Smarius
113227006Smarius	size_t			sc_dmasize;	/* DMA size */
114227006Smarius	void			**sc_dmaaddr;	/* DMA address */
115227006Smarius	size_t			*sc_dmalen;	/* DMA length */
116227006Smarius	int			sc_active;	/* DMA state */
117227006Smarius	int			sc_datain;	/* DMA Data Direction */
118227006Smarius};
119227006Smarius
120227006Smariusstatic struct resource_spec esp_pci_res_spec[] = {
121227006Smarius	{ SYS_RES_IRQ, 0, RF_SHAREABLE | RF_ACTIVE },	/* ESP_PCI_RES_INTR */
122227006Smarius	{ SYS_RES_IOPORT, PCIR_BAR(0), RF_ACTIVE },	/* ESP_PCI_RES_IO */
123227006Smarius	{ -1, 0 }
124227006Smarius};
125227006Smarius
126227006Smarius#define	READ_DMAREG(sc, reg)						\
127227006Smarius	bus_read_4((sc)->sc_res[ESP_PCI_RES_IO], (reg))
128227006Smarius#define	WRITE_DMAREG(sc, reg, var)					\
129227006Smarius	bus_write_4((sc)->sc_res[ESP_PCI_RES_IO], (reg), (var))
130227006Smarius
131227006Smarius#define	READ_ESPREG(sc, reg)						\
132227006Smarius	bus_read_1((sc)->sc_res[ESP_PCI_RES_IO], (reg) << 2)
133227006Smarius#define	WRITE_ESPREG(sc, reg, val)					\
134227006Smarius	bus_write_1((sc)->sc_res[ESP_PCI_RES_IO], (reg) << 2, (val))
135227006Smarius
136227006Smariusstatic int	esp_pci_probe(device_t);
137227006Smariusstatic int	esp_pci_attach(device_t);
138227006Smariusstatic int	esp_pci_detach(device_t);
139227006Smariusstatic int	esp_pci_suspend(device_t);
140227006Smariusstatic int	esp_pci_resume(device_t);
141227006Smarius
142227006Smariusstatic device_method_t esp_pci_methods[] = {
143227006Smarius	DEVMETHOD(device_probe,		esp_pci_probe),
144227006Smarius	DEVMETHOD(device_attach,	esp_pci_attach),
145227006Smarius	DEVMETHOD(device_detach,	esp_pci_detach),
146227006Smarius	DEVMETHOD(device_suspend,	esp_pci_suspend),
147227006Smarius	DEVMETHOD(device_resume,	esp_pci_resume),
148227006Smarius
149227848Smarius	DEVMETHOD_END
150227006Smarius};
151227006Smarius
152227006Smariusstatic driver_t esp_pci_driver = {
153227006Smarius	"esp",
154227006Smarius	esp_pci_methods,
155227006Smarius	sizeof(struct esp_pci_softc)
156227006Smarius};
157227006Smarius
158227006SmariusDRIVER_MODULE(esp, pci, esp_pci_driver, esp_devclass, 0, 0);
159227006SmariusMODULE_DEPEND(esp, pci, 1, 1, 1);
160227006Smarius
161227006Smarius/*
162227006Smarius * Functions and the switch for the MI code
163227006Smarius */
164227006Smariusstatic void	esp_pci_dma_go(struct ncr53c9x_softc *);
165227006Smariusstatic int	esp_pci_dma_intr(struct ncr53c9x_softc *);
166227006Smariusstatic int	esp_pci_dma_isactive(struct ncr53c9x_softc *);
167227006Smarius
168227006Smariusstatic int	esp_pci_dma_isintr(struct ncr53c9x_softc *);
169227006Smariusstatic void	esp_pci_dma_reset(struct ncr53c9x_softc *);
170227006Smariusstatic int	esp_pci_dma_setup(struct ncr53c9x_softc *, void **, size_t *,
171227006Smarius		    int, size_t *);
172227006Smariusstatic void	esp_pci_dma_stop(struct ncr53c9x_softc *);
173227006Smariusstatic void	esp_pci_write_reg(struct ncr53c9x_softc *, int, uint8_t);
174227006Smariusstatic uint8_t	esp_pci_read_reg(struct ncr53c9x_softc *, int);
175227006Smariusstatic void	esp_pci_xfermap(void *arg, bus_dma_segment_t *segs, int nseg,
176227006Smarius		    int error);
177227006Smarius
178227006Smariusstatic struct ncr53c9x_glue esp_pci_glue = {
179227006Smarius	esp_pci_read_reg,
180227006Smarius	esp_pci_write_reg,
181227006Smarius	esp_pci_dma_isintr,
182227006Smarius	esp_pci_dma_reset,
183227006Smarius	esp_pci_dma_intr,
184227006Smarius	esp_pci_dma_setup,
185227006Smarius	esp_pci_dma_go,
186227006Smarius	esp_pci_dma_stop,
187227006Smarius	esp_pci_dma_isactive,
188227006Smarius};
189227006Smarius
190227006Smariusstatic int
191227006Smariusesp_pci_probe(device_t dev)
192227006Smarius{
193227006Smarius
194227006Smarius	if (pci_get_devid(dev) == PCI_DEVICE_ID_AMD53C974) {
195227006Smarius		device_set_desc(dev, "AMD Am53C974 Fast-SCSI");
196227006Smarius		return (BUS_PROBE_DEFAULT);
197227006Smarius	}
198227006Smarius
199227006Smarius	return (ENXIO);
200227006Smarius}
201227006Smarius
202227006Smarius/*
203227006Smarius * Attach this instance, and then all the sub-devices
204227006Smarius */
205227006Smariusstatic int
206227006Smariusesp_pci_attach(device_t dev)
207227006Smarius{
208227006Smarius	struct esp_pci_softc *esc;
209227006Smarius	struct ncr53c9x_softc *sc;
210227006Smarius	int error;
211227006Smarius
212227006Smarius	esc = device_get_softc(dev);
213227006Smarius	sc = &esc->sc_ncr53c9x;
214227006Smarius
215227006Smarius	NCR_LOCK_INIT(sc);
216227006Smarius
217227006Smarius	esc->sc_dev = dev;
218227006Smarius	sc->sc_glue = &esp_pci_glue;
219227006Smarius
220227006Smarius	pci_enable_busmaster(dev);
221227006Smarius
222227006Smarius	error = bus_alloc_resources(dev, esp_pci_res_spec, esc->sc_res);
223227006Smarius	if (error != 0) {
224227006Smarius		device_printf(dev, "failed to allocate resources\n");
225227006Smarius		bus_release_resources(dev, esp_pci_res_spec, esc->sc_res);
226227006Smarius		return (error);
227227006Smarius	}
228227006Smarius
229227006Smarius	error = bus_dma_tag_create(bus_get_dma_tag(dev), 1, 0,
230227006Smarius	    BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL,
231227006Smarius	    BUS_SPACE_MAXSIZE_32BIT, BUS_SPACE_UNRESTRICTED,
232227006Smarius	    BUS_SPACE_MAXSIZE_32BIT, 0, NULL, NULL, &esc->sc_pdmat);
233227006Smarius	if (error != 0) {
234227006Smarius		device_printf(dev, "cannot create parent DMA tag\n");
235227006Smarius		goto fail_res;
236227006Smarius	}
237227006Smarius
238227006Smarius	/*
239227006Smarius	 * XXX More of this should be in ncr53c9x_attach(), but
240227006Smarius	 * XXX should we really poke around the chip that much in
241227006Smarius	 * XXX the MI code?  Think about this more...
242227006Smarius	 */
243227006Smarius
244227006Smarius	/*
245227006Smarius	 * Set up static configuration info.
246227006Smarius	 *
247227006Smarius	 * XXX we should read the configuration from the EEPROM.
248227006Smarius	 */
249227006Smarius	sc->sc_id = 7;
250227006Smarius	sc->sc_cfg1 = sc->sc_id | NCRCFG1_PARENB;
251227006Smarius	sc->sc_cfg2 = NCRCFG2_SCSI2 | NCRCFG2_FE;
252227006Smarius	sc->sc_cfg3 = NCRAMDCFG3_IDM | NCRAMDCFG3_FCLK;
253227006Smarius	sc->sc_cfg4 = NCRAMDCFG4_GE12NS | NCRAMDCFG4_RADE;
254227006Smarius	sc->sc_rev = NCR_VARIANT_AM53C974;
255227006Smarius	sc->sc_features = NCR_F_FASTSCSI | NCR_F_DMASELECT;
256227006Smarius	sc->sc_cfg3_fscsi = NCRAMDCFG3_FSCSI;
257227006Smarius	sc->sc_freq = 40; /* MHz */
258227006Smarius
259227006Smarius	/*
260227006Smarius	 * This is the value used to start sync negotiations
261227006Smarius	 * Note that the NCR register "SYNCTP" is programmed
262227006Smarius	 * in "clocks per byte", and has a minimum value of 4.
263227006Smarius	 * The SCSI period used in negotiation is one-fourth
264227006Smarius	 * of the time (in nanoseconds) needed to transfer one byte.
265227006Smarius	 * Since the chip's clock is given in MHz, we have the following
266227006Smarius	 * formula: 4 * period = (1000 / freq) * 4
267227006Smarius	 */
268227006Smarius	sc->sc_minsync = 1000 / sc->sc_freq;
269227006Smarius
270227006Smarius	sc->sc_maxxfer = DFLTPHYS;	/* see below */
271227006Smarius	sc->sc_maxoffset = 15;
272227006Smarius	sc->sc_extended_geom = 1;
273227006Smarius
274227006Smarius#define	MDL_SEG_SIZE	0x1000	/* 4kbyte per segment */
275227006Smarius
276227006Smarius	/*
277227006Smarius	 * Create the DMA tag and map for the data transfers.
278227006Smarius	 *
279227006Smarius	 * Note: given that bus_dma(9) only adheres to the requested alignment
280227006Smarius	 * for the first segment (and that also only for bus_dmamem_alloc()ed
281227006Smarius	 * DMA maps) we can't use the Memory Descriptor List.  However, also
282227006Smarius	 * when not using the MDL, the maximum transfer size apparently is
283227006Smarius	 * limited to 4k so we have to split transfers up, which plain sucks.
284227006Smarius	 */
285227006Smarius	error = bus_dma_tag_create(esc->sc_pdmat, PAGE_SIZE, 0,
286227006Smarius	    BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL,
287227006Smarius	    MDL_SEG_SIZE, 1, MDL_SEG_SIZE, BUS_DMA_ALLOCNOW,
288227006Smarius	    busdma_lock_mutex, &sc->sc_lock, &esc->sc_xferdmat);
289227006Smarius	if (error != 0) {
290227006Smarius		device_printf(dev, "cannot create transfer DMA tag\n");
291227006Smarius		goto fail_pdmat;
292227006Smarius	}
293227006Smarius	error = bus_dmamap_create(esc->sc_xferdmat, 0, &esc->sc_xferdmam);
294227006Smarius	if (error != 0) {
295298955Spfg		device_printf(dev, "cannot create transfer DMA map\n");
296227006Smarius		goto fail_xferdmat;
297227006Smarius	}
298227006Smarius
299227006Smarius	error = bus_setup_intr(dev, esc->sc_res[ESP_PCI_RES_INTR],
300227006Smarius	    INTR_MPSAFE | INTR_TYPE_CAM, NULL, ncr53c9x_intr, sc,
301227006Smarius	    &esc->sc_ih);
302227006Smarius	if (error != 0) {
303227006Smarius		device_printf(dev, "cannot set up interrupt\n");
304227006Smarius		goto fail_xferdmam;
305227006Smarius	}
306227006Smarius
307227006Smarius	/* Do the common parts of attachment. */
308227006Smarius	sc->sc_dev = esc->sc_dev;
309227006Smarius	error = ncr53c9x_attach(sc);
310227006Smarius	if (error != 0) {
311227006Smarius		device_printf(esc->sc_dev, "ncr53c9x_attach failed\n");
312227006Smarius		goto fail_intr;
313227006Smarius	}
314227006Smarius
315227006Smarius	return (0);
316227006Smarius
317227006Smarius fail_intr:
318227006Smarius	 bus_teardown_intr(esc->sc_dev, esc->sc_res[ESP_PCI_RES_INTR],
319227006Smarius	    esc->sc_ih);
320227006Smarius fail_xferdmam:
321227006Smarius	bus_dmamap_destroy(esc->sc_xferdmat, esc->sc_xferdmam);
322227006Smarius fail_xferdmat:
323227006Smarius	bus_dma_tag_destroy(esc->sc_xferdmat);
324227006Smarius fail_pdmat:
325227006Smarius	bus_dma_tag_destroy(esc->sc_pdmat);
326227006Smarius fail_res:
327227006Smarius	bus_release_resources(dev, esp_pci_res_spec, esc->sc_res);
328227006Smarius	NCR_LOCK_DESTROY(sc);
329227006Smarius
330227006Smarius	return (error);
331227006Smarius}
332227006Smarius
333227006Smariusstatic int
334227006Smariusesp_pci_detach(device_t dev)
335227006Smarius{
336227006Smarius	struct ncr53c9x_softc *sc;
337227006Smarius	struct esp_pci_softc *esc;
338227006Smarius	int error;
339227006Smarius
340227006Smarius	esc = device_get_softc(dev);
341227006Smarius	sc = &esc->sc_ncr53c9x;
342227006Smarius
343227006Smarius	bus_teardown_intr(esc->sc_dev, esc->sc_res[ESP_PCI_RES_INTR],
344227006Smarius	    esc->sc_ih);
345227006Smarius	error = ncr53c9x_detach(sc);
346227006Smarius	if (error != 0)
347227006Smarius		return (error);
348227006Smarius	bus_dmamap_destroy(esc->sc_xferdmat, esc->sc_xferdmam);
349227006Smarius	bus_dma_tag_destroy(esc->sc_xferdmat);
350227006Smarius	bus_dma_tag_destroy(esc->sc_pdmat);
351227006Smarius	bus_release_resources(dev, esp_pci_res_spec, esc->sc_res);
352227006Smarius	NCR_LOCK_DESTROY(sc);
353227006Smarius
354227006Smarius	return (0);
355227006Smarius}
356227006Smarius
357227006Smariusstatic int
358227006Smariusesp_pci_suspend(device_t dev)
359227006Smarius{
360227006Smarius
361227006Smarius	return (ENXIO);
362227006Smarius}
363227006Smarius
364227006Smariusstatic int
365227006Smariusesp_pci_resume(device_t dev)
366227006Smarius{
367227006Smarius
368227006Smarius	return (ENXIO);
369227006Smarius}
370227006Smarius
371227006Smariusstatic void
372227006Smariusesp_pci_xfermap(void *arg, bus_dma_segment_t *segs, int nsegs, int error)
373227006Smarius{
374227006Smarius	struct esp_pci_softc *esc = (struct esp_pci_softc *)arg;
375227006Smarius
376227006Smarius	if (error != 0)
377227006Smarius		return;
378227006Smarius
379227006Smarius	KASSERT(nsegs == 1, ("%s: bad transfer segment count %d", __func__,
380227006Smarius	    nsegs));
381227006Smarius	KASSERT(segs[0].ds_len <= MDL_SEG_SIZE,
382227006Smarius	    ("%s: bad transfer segment length %ld", __func__,
383227006Smarius	    (long)segs[0].ds_len));
384227006Smarius
385227006Smarius	/* Program the DMA Starting Physical Address. */
386227006Smarius	WRITE_DMAREG(esc, DMA_SPA, segs[0].ds_addr);
387227006Smarius}
388227006Smarius
389227006Smarius/*
390227006Smarius * Glue functions
391227006Smarius */
392227006Smarius
393227006Smariusstatic uint8_t
394227006Smariusesp_pci_read_reg(struct ncr53c9x_softc *sc, int reg)
395227006Smarius{
396227006Smarius	struct esp_pci_softc *esc = (struct esp_pci_softc *)sc;
397227006Smarius
398227006Smarius	return (READ_ESPREG(esc, reg));
399227006Smarius}
400227006Smarius
401227006Smariusstatic void
402227006Smariusesp_pci_write_reg(struct ncr53c9x_softc *sc, int reg, uint8_t v)
403227006Smarius{
404227006Smarius	struct esp_pci_softc *esc = (struct esp_pci_softc *)sc;
405227006Smarius
406227006Smarius	WRITE_ESPREG(esc, reg, v);
407227006Smarius}
408227006Smarius
409227006Smariusstatic int
410227006Smariusesp_pci_dma_isintr(struct ncr53c9x_softc *sc)
411227006Smarius{
412227006Smarius	struct esp_pci_softc *esc = (struct esp_pci_softc *)sc;
413227006Smarius
414227006Smarius	return (READ_ESPREG(esc, NCR_STAT) & NCRSTAT_INT) != 0;
415227006Smarius}
416227006Smarius
417227006Smariusstatic void
418227006Smariusesp_pci_dma_reset(struct ncr53c9x_softc *sc)
419227006Smarius{
420227006Smarius	struct esp_pci_softc *esc = (struct esp_pci_softc *)sc;
421227006Smarius
422227006Smarius	WRITE_DMAREG(esc, DMA_CMD, DMACMD_IDLE);
423227006Smarius
424227006Smarius	esc->sc_active = 0;
425227006Smarius}
426227006Smarius
427227006Smariusstatic int
428227006Smariusesp_pci_dma_intr(struct ncr53c9x_softc *sc)
429227006Smarius{
430227006Smarius	struct esp_pci_softc *esc = (struct esp_pci_softc *)sc;
431227006Smarius	bus_dma_tag_t xferdmat;
432227006Smarius	bus_dmamap_t xferdmam;
433227006Smarius	size_t dmasize;
434227006Smarius	int datain, i, resid, trans;
435227006Smarius	uint32_t dmastat;
436227006Smarius	char *p = NULL;
437227006Smarius
438227006Smarius	xferdmat = esc->sc_xferdmat;
439227006Smarius	xferdmam = esc->sc_xferdmam;
440227006Smarius	datain = esc->sc_datain;
441227006Smarius
442227006Smarius	dmastat = READ_DMAREG(esc, DMA_STAT);
443227006Smarius
444227006Smarius	if ((dmastat & DMASTAT_ERR) != 0) {
445227006Smarius		/* XXX not tested... */
446227006Smarius		WRITE_DMAREG(esc, DMA_CMD, DMACMD_ABORT | (datain != 0 ?
447227006Smarius		    DMACMD_DIR : 0));
448227006Smarius
449227006Smarius		device_printf(esc->sc_dev, "DMA error detected; Aborting.\n");
450227006Smarius		bus_dmamap_sync(xferdmat, xferdmam, datain != 0 ?
451227006Smarius		    BUS_DMASYNC_POSTREAD : BUS_DMASYNC_POSTWRITE);
452227006Smarius		bus_dmamap_unload(xferdmat, xferdmam);
453227006Smarius		return (-1);
454227006Smarius	}
455227006Smarius
456227006Smarius	if ((dmastat & DMASTAT_ABT) != 0) {
457227006Smarius		/* XXX what should be done? */
458227006Smarius		device_printf(esc->sc_dev, "DMA aborted.\n");
459227006Smarius		WRITE_DMAREG(esc, DMA_CMD, DMACMD_IDLE | (datain != 0 ?
460227006Smarius		    DMACMD_DIR : 0));
461227006Smarius		esc->sc_active = 0;
462227006Smarius		return (0);
463227006Smarius	}
464227006Smarius
465227006Smarius	KASSERT(esc->sc_active != 0, ("%s: DMA wasn't active", __func__));
466227006Smarius
467227006Smarius	/* DMA has stopped. */
468227006Smarius
469227006Smarius	esc->sc_active = 0;
470227006Smarius
471227006Smarius	dmasize = esc->sc_dmasize;
472227006Smarius	if (dmasize == 0) {
473227006Smarius		/* A "Transfer Pad" operation completed. */
474227006Smarius		NCR_DMA(("%s: discarded %d bytes (tcl=%d, tcm=%d)\n",
475227006Smarius		    __func__, READ_ESPREG(esc, NCR_TCL) |
476227006Smarius		    (READ_ESPREG(esc, NCR_TCM) << 8),
477227006Smarius		    READ_ESPREG(esc, NCR_TCL), READ_ESPREG(esc, NCR_TCM)));
478227006Smarius		return (0);
479227006Smarius	}
480227006Smarius
481227006Smarius	resid = 0;
482227006Smarius	/*
483227006Smarius	 * If a transfer onto the SCSI bus gets interrupted by the device
484227006Smarius	 * (e.g. for a SAVEPOINTER message), the data in the FIFO counts
485227006Smarius	 * as residual since the ESP counter registers get decremented as
486227006Smarius	 * bytes are clocked into the FIFO.
487227006Smarius	 */
488227006Smarius	if (datain == 0 &&
489227006Smarius	    (resid = (READ_ESPREG(esc, NCR_FFLAG) & NCRFIFO_FF)) != 0)
490227006Smarius		NCR_DMA(("%s: empty esp FIFO of %d ", __func__, resid));
491227006Smarius
492227006Smarius	if ((sc->sc_espstat & NCRSTAT_TC) == 0) {
493227006Smarius		/*
494227006Smarius		 * "Terminal count" is off, so read the residue
495227006Smarius		 * out of the ESP counter registers.
496227006Smarius		 */
497227006Smarius		if (datain != 0) {
498227006Smarius			resid = READ_ESPREG(esc, NCR_FFLAG) & NCRFIFO_FF;
499227006Smarius			while (resid > 1)
500227006Smarius				resid =
501227006Smarius				    READ_ESPREG(esc, NCR_FFLAG) & NCRFIFO_FF;
502227006Smarius			WRITE_DMAREG(esc, DMA_CMD, DMACMD_BLAST | DMACMD_DIR);
503227006Smarius
504227006Smarius			for (i = 0; i < 0x8000; i++) /* XXX 0x8000 ? */
505227006Smarius				if ((READ_DMAREG(esc, DMA_STAT) &
506227006Smarius				    DMASTAT_BCMP) != 0)
507227006Smarius					break;
508227006Smarius
509227006Smarius			/* See the below comments... */
510227006Smarius			if (resid != 0)
511227006Smarius				p = *esc->sc_dmaaddr;
512227006Smarius		}
513227006Smarius
514227006Smarius		resid += READ_ESPREG(esc, NCR_TCL) |
515227006Smarius		    (READ_ESPREG(esc, NCR_TCM) << 8) |
516227006Smarius		    (READ_ESPREG(esc, NCR_TCH) << 16);
517227006Smarius	} else
518227006Smarius		while ((dmastat & DMASTAT_DONE) == 0)
519227006Smarius			dmastat = READ_DMAREG(esc, DMA_STAT);
520227006Smarius
521227006Smarius	WRITE_DMAREG(esc, DMA_CMD, DMACMD_IDLE | (datain != 0 ?
522227006Smarius	    DMACMD_DIR : 0));
523227006Smarius
524227006Smarius	/* Sync the transfer buffer. */
525227006Smarius	bus_dmamap_sync(xferdmat, xferdmam, datain != 0 ?
526227006Smarius	    BUS_DMASYNC_POSTREAD : BUS_DMASYNC_POSTWRITE);
527227006Smarius	bus_dmamap_unload(xferdmat, xferdmam);
528227006Smarius
529227006Smarius	trans = dmasize - resid;
530227006Smarius
531227006Smarius	/*
532227006Smarius	 * From the technical manual notes:
533227006Smarius	 *
534227006Smarius	 * "In some odd byte conditions, one residual byte will be left
535227006Smarius	 *  in the SCSI FIFO, and the FIFO flags will never count to 0.
536227006Smarius	 *  When this happens, the residual byte should be retrieved
537227006Smarius	 *  via PIO following completion of the BLAST operation."
538227006Smarius	 */
539227006Smarius	if (p != NULL) {
540227006Smarius		p += trans;
541227006Smarius		*p = READ_ESPREG(esc, NCR_FIFO);
542227006Smarius		trans++;
543227006Smarius	}
544227006Smarius
545227006Smarius	if (trans < 0) {			/* transferred < 0 ? */
546227006Smarius#if 0
547227006Smarius		/*
548227006Smarius		 * This situation can happen in perfectly normal operation
549227006Smarius		 * if the ESP is reselected while using DMA to select
550227006Smarius		 * another target.  As such, don't print the warning.
551227006Smarius		 */
552227006Smarius		device_printf(dev, "xfer (%d) > req (%d)\n", trans, dmasize);
553227006Smarius#endif
554227006Smarius		trans = dmasize;
555227006Smarius	}
556227006Smarius
557227006Smarius	NCR_DMA(("%s: tcl=%d, tcm=%d, tch=%d; trans=%d, resid=%d\n", __func__,
558227006Smarius	    READ_ESPREG(esc, NCR_TCL), READ_ESPREG(esc, NCR_TCM),
559227006Smarius	    READ_ESPREG(esc, NCR_TCH), trans, resid));
560227006Smarius
561227006Smarius	*esc->sc_dmalen -= trans;
562227006Smarius	*esc->sc_dmaaddr = (char *)*esc->sc_dmaaddr + trans;
563227006Smarius
564227006Smarius	return (0);
565227006Smarius}
566227006Smarius
567227006Smariusstatic int
568227006Smariusesp_pci_dma_setup(struct ncr53c9x_softc *sc, void **addr, size_t *len,
569227006Smarius    int datain, size_t *dmasize)
570227006Smarius{
571227006Smarius	struct esp_pci_softc *esc = (struct esp_pci_softc *)sc;
572227006Smarius	int error;
573227006Smarius
574227006Smarius	WRITE_DMAREG(esc, DMA_CMD, DMACMD_IDLE | (datain != 0 ? DMACMD_DIR :
575227006Smarius	    0));
576227006Smarius
577227006Smarius	*dmasize = esc->sc_dmasize = ulmin(*dmasize, MDL_SEG_SIZE);
578227006Smarius	esc->sc_dmaaddr = addr;
579227006Smarius	esc->sc_dmalen = len;
580227006Smarius	esc->sc_datain = datain;
581227006Smarius
582227006Smarius	/*
583227006Smarius	 * There's no need to set up DMA for a "Transfer Pad" operation.
584227006Smarius	 */
585227006Smarius	if (*dmasize == 0)
586227006Smarius		return (0);
587227006Smarius
588227006Smarius	/* Set the transfer length. */
589227006Smarius	WRITE_DMAREG(esc, DMA_STC, *dmasize);
590227006Smarius
591227006Smarius	/*
592227006Smarius	 * Load the transfer buffer and program the DMA address.
593227006Smarius	 * Note that the NCR53C9x core can't handle EINPROGRESS so we set
594227006Smarius	 * BUS_DMA_NOWAIT.
595227006Smarius	 */
596227006Smarius	error = bus_dmamap_load(esc->sc_xferdmat, esc->sc_xferdmam,
597227006Smarius	    *esc->sc_dmaaddr, *dmasize, esp_pci_xfermap, sc, BUS_DMA_NOWAIT);
598227006Smarius
599227006Smarius	return (error);
600227006Smarius}
601227006Smarius
602227006Smariusstatic void
603227006Smariusesp_pci_dma_go(struct ncr53c9x_softc *sc)
604227006Smarius{
605227006Smarius	struct esp_pci_softc *esc = (struct esp_pci_softc *)sc;
606227006Smarius	int datain;
607227006Smarius
608227006Smarius	datain = esc->sc_datain;
609227006Smarius
610227006Smarius	/* No DMA transfer for a "Transfer Pad" operation */
611227006Smarius	if (esc->sc_dmasize == 0)
612227006Smarius		return;
613227006Smarius
614227006Smarius	/* Sync the transfer buffer. */
615227006Smarius	bus_dmamap_sync(esc->sc_xferdmat, esc->sc_xferdmam, datain != 0 ?
616227006Smarius	    BUS_DMASYNC_PREREAD : BUS_DMASYNC_PREWRITE);
617227006Smarius
618227006Smarius	/* Set the DMA engine to the IDLE state. */
619227006Smarius	/* XXX DMA Transfer Interrupt Enable bit is broken? */
620227006Smarius	WRITE_DMAREG(esc, DMA_CMD, DMACMD_IDLE | /* DMACMD_INTE | */
621227006Smarius	    (datain != 0 ? DMACMD_DIR : 0));
622227006Smarius
623227006Smarius	/* Issue a DMA start command. */
624227006Smarius	WRITE_DMAREG(esc, DMA_CMD, DMACMD_START | /* DMACMD_INTE | */
625227006Smarius	    (datain != 0 ? DMACMD_DIR : 0));
626227006Smarius
627227006Smarius	esc->sc_active = 1;
628227006Smarius}
629227006Smarius
630227006Smariusstatic void
631227006Smariusesp_pci_dma_stop(struct ncr53c9x_softc *sc)
632227006Smarius{
633227006Smarius	struct esp_pci_softc *esc = (struct esp_pci_softc *)sc;
634227006Smarius
635227006Smarius	/* DMA stop */
636227006Smarius	/* XXX what should we do here ? */
637227006Smarius	WRITE_DMAREG(esc, DMA_CMD,
638227006Smarius	    DMACMD_ABORT | (esc->sc_datain != 0 ? DMACMD_DIR : 0));
639227006Smarius	bus_dmamap_unload(esc->sc_xferdmat, esc->sc_xferdmam);
640227006Smarius
641227006Smarius	esc->sc_active = 0;
642227006Smarius}
643227006Smarius
644227006Smariusstatic int
645227006Smariusesp_pci_dma_isactive(struct ncr53c9x_softc *sc)
646227006Smarius{
647227006Smarius	struct esp_pci_softc *esc = (struct esp_pci_softc *)sc;
648227006Smarius
649227006Smarius	/* XXX should we check esc->sc_active? */
650227006Smarius	if ((READ_DMAREG(esc, DMA_CMD) & DMACMD_CMD) != DMACMD_IDLE)
651227006Smarius		return (1);
652227006Smarius
653227006Smarius	return (0);
654227006Smarius}
655