xref: /netbsd-src/sys/arch/arc/isa/isadma.c (revision 8ff6f65dafc58186614d149001c905dec4c934a2)
1*8ff6f65dSthorpej /*	$NetBSD: isadma.c,v 1.13 2023/12/20 06:36:02 thorpej Exp $	*/
25009ff6dSsoda /*	$OpenBSD: isadma.c,v 1.2 1996/11/23 21:45:34 kstailey Exp $	*/
3564df9b6Ssoda /*	NetBSD: isadma.c,v 1.19 1996/04/29 20:03:26 christos Exp 	*/
45009ff6dSsoda 
5a4183603Slukem #include <sys/cdefs.h>
6*8ff6f65dSthorpej __KERNEL_RCSID(0, "$NetBSD: isadma.c,v 1.13 2023/12/20 06:36:02 thorpej Exp $");
7a4183603Slukem 
85009ff6dSsoda #include <sys/param.h>
95009ff6dSsoda #include <sys/systm.h>
105009ff6dSsoda #include <sys/device.h>
115009ff6dSsoda #include <sys/file.h>
125009ff6dSsoda #include <sys/buf.h>
135009ff6dSsoda #include <sys/syslog.h>
145009ff6dSsoda #include <sys/uio.h>
155009ff6dSsoda 
1647ef8ee9Smrg #include <uvm/uvm_extern.h>
175009ff6dSsoda 
185009ff6dSsoda #include <machine/pio.h>
195009ff6dSsoda 
205009ff6dSsoda #include <dev/isa/isareg.h>
215009ff6dSsoda #include <dev/isa/isavar.h>
225009ff6dSsoda #include <dev/isa/isadmavar.h>
235009ff6dSsoda #include <arch/arc/isa/isadmareg.h> /*XXX*/
245009ff6dSsoda 
255009ff6dSsoda struct dma_info {
265009ff6dSsoda 	int flags;
275009ff6dSsoda 	int active;
2853524e44Schristos 	void *addr;
2971f6ef9eSsoda 	bus_size_t nbytes;
305009ff6dSsoda 	struct isadma_seg phys[1];
315009ff6dSsoda };
325009ff6dSsoda 
335009ff6dSsoda static struct isadma_softc *isadma_sc;	/*XXX ugly */
345009ff6dSsoda static struct dma_info dma_info[8];
357fe2a5a0Stsutsui static uint8_t dma_finished;
365009ff6dSsoda 
375009ff6dSsoda /* high byte of address is stored in this port for i-th dma channel */
385009ff6dSsoda static int dmapageport[2][4] = {
395009ff6dSsoda 	{0x87, 0x83, 0x81, 0x82},
405009ff6dSsoda 	{0x8f, 0x8b, 0x89, 0x8a}
415009ff6dSsoda };
425009ff6dSsoda 
437fe2a5a0Stsutsui static uint8_t dmamode[4] = {
445009ff6dSsoda 	DMA37MD_READ | DMA37MD_SINGLE,
455009ff6dSsoda 	DMA37MD_WRITE | DMA37MD_SINGLE,
465009ff6dSsoda 	DMA37MD_READ | DMA37MD_LOOP,
475009ff6dSsoda 	DMA37MD_WRITE | DMA37MD_LOOP
485009ff6dSsoda };
495009ff6dSsoda 
500f31d9deStsutsui static int isadmamatch(device_t, cfdata_t, void *);
510f31d9deStsutsui static void isadmaattach(device_t, device_t, void *);
525009ff6dSsoda 
535009ff6dSsoda struct isadma_softc {
540f31d9deStsutsui 	device_t sc_dev;
55564df9b6Ssoda 	bus_space_tag_t sc_iot;
56564df9b6Ssoda 	bus_space_handle_t sc_ioh1;
57564df9b6Ssoda 	bus_space_handle_t sc_ioh2;
585009ff6dSsoda }
595009ff6dSsoda 
600f31d9deStsutsui CFATTACH_DECL_NEW(isadma, sizeof(struct isadma_softc),
61c5e91d44Sthorpej     isadmamatch, isadmaattach, NULL, NULL);
625009ff6dSsoda 
635009ff6dSsoda struct cfdriver isadma_cd = {
645009ff6dSsoda 	NULL, "isadma", DV_DULL, 1
655009ff6dSsoda };
665009ff6dSsoda 
670f31d9deStsutsui static int
isadmamatch(device_t parent,cfdata_t cf,void * aux)680f31d9deStsutsui isadmamatch(device_t parent, cfdata_t cf, void *aux)
695009ff6dSsoda {
705009ff6dSsoda 	struct isa_attach_args *ia = aux;
715009ff6dSsoda 
725009ff6dSsoda 	/* Sure we exist */
735009ff6dSsoda 	ia->ia_iosize = 0;
747fe2a5a0Stsutsui 	return 1;
755009ff6dSsoda }
765009ff6dSsoda 
770f31d9deStsutsui static void
isadmaattach(device_t parent,device_t self,void * aux)780f31d9deStsutsui isadmaattach(device_t parent, device_t self, void *aux)
795009ff6dSsoda {
800f31d9deStsutsui 	struct isadma_softc *sc = device_private(self);
815009ff6dSsoda 	struct isa_attach_args *ia = aux;
82564df9b6Ssoda 	bus_space_tag_t iot;
83564df9b6Ssoda 	bus_space_handle_t ioh;
845009ff6dSsoda 
850f31d9deStsutsui 	sc->sc_dev = self;
860f31d9deStsutsui 
870f31d9deStsutsui 	aprint_normal("\n");
885009ff6dSsoda 
89564df9b6Ssoda 	iot = sc->sc_iot = ia->ia_iot;
90564df9b6Ssoda 	if (bus_space_map(iot, IO_DMA1, DMA_NREGS, 0, &ioh))
910f31d9deStsutsui 		panic("%s: couldn't map I/O ports", __func__);
925009ff6dSsoda 	sc->sc_ioh1 = ioh;
93564df9b6Ssoda 	if (bus_space_map(iot, IO_DMA2, DMA_NREGS*2, 0, &ioh))
940f31d9deStsutsui 		panic("%s: couldn't map I/O ports", __func__);
955009ff6dSsoda 	sc->sc_ioh2 = ioh;
965009ff6dSsoda 	isadma_sc = sc;
975009ff6dSsoda }
985009ff6dSsoda 
995009ff6dSsoda /*
1005009ff6dSsoda  * isadma_cascade(): program 8237 DMA controller channel to accept
1015009ff6dSsoda  * external dma control by a board.
1025009ff6dSsoda  */
1035009ff6dSsoda void
isadma_cascade(int chan)1047fe2a5a0Stsutsui isadma_cascade(int chan)
1055009ff6dSsoda {
1065009ff6dSsoda 	struct isadma_softc *sc = isadma_sc;
107564df9b6Ssoda 	bus_space_tag_t iot = sc->sc_iot;
1085009ff6dSsoda 
1095009ff6dSsoda #ifdef ISADMA_DEBUG
1105009ff6dSsoda 	if (chan < 0 || chan > 7)
1110f31d9deStsutsui 		panic("%s: impossible request", __func__);
1125009ff6dSsoda #endif
1135009ff6dSsoda 
1145009ff6dSsoda 	/* set dma channel mode, and set dma channel mode */
1155009ff6dSsoda 	if ((chan & 4) == 0) {
1167fe2a5a0Stsutsui 		bus_space_write_1(iot, sc->sc_ioh1, DMA1_MODE,
1177fe2a5a0Stsutsui 		    chan | DMA37MD_CASCADE);
118564df9b6Ssoda 		bus_space_write_1(iot, sc->sc_ioh1, DMA1_SMSK, chan);
1195009ff6dSsoda 	} else {
1205009ff6dSsoda 		chan &= 3;
1215009ff6dSsoda 
1227fe2a5a0Stsutsui 		bus_space_write_1(iot, sc->sc_ioh2, DMA2_MODE,
1237fe2a5a0Stsutsui 		    chan | DMA37MD_CASCADE);
124564df9b6Ssoda 		bus_space_write_1(iot, sc->sc_ioh2, DMA2_SMSK, chan);
1255009ff6dSsoda 	}
1265009ff6dSsoda }
1275009ff6dSsoda 
1285009ff6dSsoda /*
1295009ff6dSsoda  * isadma_start(): program 8237 DMA controller channel, avoid page alignment
1305009ff6dSsoda  * problems by using a bounce buffer.
1315009ff6dSsoda  */
1325009ff6dSsoda void
isadma_start(void * addr,bus_size_t nbytes,int chan,int flags)13353524e44Schristos isadma_start(void *addr, bus_size_t nbytes, int chan, int flags)
1345009ff6dSsoda {
1355009ff6dSsoda 	struct dma_info *di;
1365009ff6dSsoda 	int waport;
1375009ff6dSsoda 	int mflags;
1385009ff6dSsoda 	struct isadma_softc *sc = isadma_sc;
139564df9b6Ssoda 	bus_space_tag_t iot = sc->sc_iot;
140564df9b6Ssoda 	bus_space_handle_t ioh;
1415009ff6dSsoda 
1425009ff6dSsoda #ifdef ISADMA_DEBUG
1435009ff6dSsoda 	if (chan < 0 || chan > 7 ||
1445009ff6dSsoda 	    (((flags & DMAMODE_READ) != 0) + ((flags & DMAMODE_WRITE) != 0) +
1455009ff6dSsoda 	    ((flags & DMAMODE_LOOP) != 0) != 1) ||
1465009ff6dSsoda 	    ((chan & 4) ? (nbytes >= (1<<17) || nbytes & 1 || (u_int)addr & 1) :
1475009ff6dSsoda 	    (nbytes >= (1<<16))))
1480f31d9deStsutsui 		panic("%s: impossible request", __func__);
1495009ff6dSsoda #endif
1505009ff6dSsoda 
1515009ff6dSsoda 	di = dma_info+chan;
1525009ff6dSsoda 	if (di->active) {
1530f31d9deStsutsui 		log(LOG_ERR,"%s: old request active on %d\n", __func__, chan);
1545009ff6dSsoda 		isadma_abort(chan);
1555009ff6dSsoda 	}
1565009ff6dSsoda 
1575009ff6dSsoda 	di->flags = flags;
1585009ff6dSsoda 	di->active = 1;
1595009ff6dSsoda 	di->addr = addr;
1605009ff6dSsoda 	di->nbytes = nbytes;
1615009ff6dSsoda 
1625009ff6dSsoda 	mflags = ISADMA_MAP_WAITOK | ISADMA_MAP_BOUNCE | ISADMA_MAP_CONTIG;
1635009ff6dSsoda 	mflags |= (chan & 4) ? ISADMA_MAP_16BIT : ISADMA_MAP_8BIT;
1645009ff6dSsoda 
1655009ff6dSsoda 	if (isadma_map(addr, nbytes, di->phys, mflags) != 1)
1660f31d9deStsutsui 		panic("%s: cannot map", __func__);
1675009ff6dSsoda 
1685009ff6dSsoda 	/* XXX Will this do what we want with DMAMODE_LOOP?  */
1695009ff6dSsoda 	if ((flags & DMAMODE_READ) == 0)
1705009ff6dSsoda 		isadma_copytobuf(addr, nbytes, 1, di->phys);
1715009ff6dSsoda 
1725009ff6dSsoda 	dma_finished &= ~(1 << chan);
1735009ff6dSsoda 
1745009ff6dSsoda 	if ((chan & 4) == 0) {
1755009ff6dSsoda 		ioh = sc->sc_ioh1;
1765009ff6dSsoda 		/*
1775009ff6dSsoda 		 * Program one of DMA channels 0..3.  These are
1785009ff6dSsoda 		 * byte mode channels.
1795009ff6dSsoda 		 */
1805009ff6dSsoda 		/* set dma channel mode, and reset address ff */
181564df9b6Ssoda 		bus_space_write_1(iot, ioh, DMA1_MODE, chan | dmamode[flags]);
182564df9b6Ssoda 		bus_space_write_1(iot, ioh, DMA1_FFC, 0);
1835009ff6dSsoda 
1845009ff6dSsoda 		/* send start address */
1855009ff6dSsoda 		waport = DMA1_CHN(chan);
1865009ff6dSsoda 		outb(dmapageport[0][chan], di->phys[0].addr>>16);
1875009ff6dSsoda 		outb(waport, di->phys[0].addr);
1885009ff6dSsoda 		outb(waport, di->phys[0].addr>>8);
1895009ff6dSsoda 
1905009ff6dSsoda 		/* send count */
1915009ff6dSsoda 		outb(waport + 1, --nbytes);
1925009ff6dSsoda 		outb(waport + 1, nbytes>>8);
1935009ff6dSsoda 
1945009ff6dSsoda 		/* unmask channel */
195564df9b6Ssoda 		bus_space_write_1(iot, ioh, DMA1_SMSK, chan | DMA37SM_CLEAR);
1965009ff6dSsoda 	} else {
1975009ff6dSsoda 		ioh = sc->sc_ioh2;
1985009ff6dSsoda 		/*
1995009ff6dSsoda 		 * Program one of DMA channels 4..7.  These are
2005009ff6dSsoda 		 * word mode channels.
2015009ff6dSsoda 		 */
2025009ff6dSsoda 		/* set dma channel mode, and reset address ff */
2037fe2a5a0Stsutsui 		bus_space_write_1(iot, ioh, DMA2_MODE,
2047fe2a5a0Stsutsui 		    (chan & 3) | dmamode[flags]);
205564df9b6Ssoda 		bus_space_write_1(iot, ioh, DMA2_FFC, 0);
2065009ff6dSsoda 
2075009ff6dSsoda 		/* send start address */
2085009ff6dSsoda 		waport = DMA2_CHN(chan & 3);
2095009ff6dSsoda 		outb(dmapageport[1][chan], di->phys[0].addr >> 16);
2105009ff6dSsoda 		outb(waport, di->phys[0].addr >> 1);
2115009ff6dSsoda 		outb(waport, di->phys[0].addr >> 9);
2125009ff6dSsoda 
2135009ff6dSsoda 		/* send count */
2145009ff6dSsoda 		nbytes >>= 1;
2155009ff6dSsoda 		outb(waport + 2, --nbytes);
2165009ff6dSsoda 		outb(waport + 2, nbytes>>8);
2175009ff6dSsoda 
2185009ff6dSsoda 		/* unmask channel */
2197fe2a5a0Stsutsui 		bus_space_write_1(iot, ioh, DMA2_SMSK,
2207fe2a5a0Stsutsui 		    (chan & 3) | DMA37SM_CLEAR);
2215009ff6dSsoda 	}
2225009ff6dSsoda }
2235009ff6dSsoda 
2245009ff6dSsoda void
isadma_abort(int chan)2257fe2a5a0Stsutsui isadma_abort(int chan)
2265009ff6dSsoda {
2275009ff6dSsoda 	struct dma_info *di;
2285009ff6dSsoda 	struct isadma_softc *sc = isadma_sc;
229564df9b6Ssoda 	bus_space_tag_t iot = sc->sc_iot;
2305009ff6dSsoda 
2315009ff6dSsoda #ifdef ISADMA_DEBUG
2325009ff6dSsoda 	if (chan < 0 || chan > 7)
2330f31d9deStsutsui 		panic("%s: impossible request", __func__);
2345009ff6dSsoda #endif
2355009ff6dSsoda 
2365009ff6dSsoda 	di = dma_info+chan;
2375009ff6dSsoda 	if (! di->active) {
2380f31d9deStsutsui 		log(LOG_ERR,"%s: no request active on %d\n", __func__, chan);
2395009ff6dSsoda 		return;
2405009ff6dSsoda 	}
2415009ff6dSsoda 
2425009ff6dSsoda 	/* mask channel */
2435009ff6dSsoda 	if ((chan & 4) == 0)
2447fe2a5a0Stsutsui 		bus_space_write_1(iot, sc->sc_ioh1, DMA1_SMSK,
2457fe2a5a0Stsutsui 		    DMA37SM_SET | chan);
2465009ff6dSsoda 	else
2477fe2a5a0Stsutsui 		bus_space_write_1(iot, sc->sc_ioh2, DMA2_SMSK,
2487fe2a5a0Stsutsui 		    DMA37SM_SET | (chan & 3));
2495009ff6dSsoda 
2505009ff6dSsoda 	isadma_unmap(di->addr, di->nbytes, 1, di->phys);
2515009ff6dSsoda 	di->active = 0;
2525009ff6dSsoda }
2535009ff6dSsoda 
2545009ff6dSsoda int
isadma_finished(int chan)2557fe2a5a0Stsutsui isadma_finished(int chan)
2565009ff6dSsoda {
2575009ff6dSsoda 	struct isadma_softc *sc = isadma_sc;
258564df9b6Ssoda 	bus_space_tag_t iot = sc->sc_iot;
2595009ff6dSsoda 
2605009ff6dSsoda #ifdef ISADMA_DEBUG
2615009ff6dSsoda 	if (chan < 0 || chan > 7)
2620f31d9deStsutsui 		panic("%s: impossible request", __func__);
2635009ff6dSsoda #endif
2645009ff6dSsoda 
2655009ff6dSsoda 	/* check that the terminal count was reached */
2665009ff6dSsoda 	if ((chan & 4) == 0)
2677fe2a5a0Stsutsui 		dma_finished |=
2687fe2a5a0Stsutsui 		    bus_space_read_1(iot, sc->sc_ioh1, DMA1_SR) & 0x0f;
2695009ff6dSsoda 	else
2707fe2a5a0Stsutsui 		dma_finished |=
2717fe2a5a0Stsutsui 		    (bus_space_read_1(iot, sc->sc_ioh2, DMA2_SR) & 0x0f) << 4;
2725009ff6dSsoda 
2737fe2a5a0Stsutsui 	return (dma_finished & (1 << chan)) != 0;
2745009ff6dSsoda }
2755009ff6dSsoda 
2765009ff6dSsoda void
isadma_done(int chan)2777fe2a5a0Stsutsui isadma_done(int chan)
2785009ff6dSsoda {
2795009ff6dSsoda 	struct dma_info *di;
2805009ff6dSsoda 	u_char tc;
2815009ff6dSsoda 	struct isadma_softc *sc = isadma_sc;
282564df9b6Ssoda 	bus_space_tag_t iot = sc->sc_iot;
2835009ff6dSsoda 
2845009ff6dSsoda #ifdef DIAGNOSTIC
2855009ff6dSsoda 	if (chan < 0 || chan > 7)
2860f31d9deStsutsui 		panic("%s: impossible request", __func__);
2875009ff6dSsoda #endif
2885009ff6dSsoda 
2895009ff6dSsoda 	di = dma_info+chan;
2905009ff6dSsoda 	if (! di->active) {
2910f31d9deStsutsui 		log(LOG_ERR,"%s: no request active on %d\n", __func__, chan);
2925009ff6dSsoda 		return;
2935009ff6dSsoda 	}
2945009ff6dSsoda 
2955009ff6dSsoda 	/* check that the terminal count was reached */
2965009ff6dSsoda 	if ((chan & 4) == 0)
297564df9b6Ssoda 		tc = bus_space_read_1(iot, sc->sc_ioh1, DMA1_SR) & (1 << chan);
2985009ff6dSsoda 	else
2997fe2a5a0Stsutsui 		tc = bus_space_read_1(iot, sc->sc_ioh2, DMA2_SR) &
3007fe2a5a0Stsutsui 		    (1 << (chan & 3));
3015009ff6dSsoda 	if (tc == 0)
3025009ff6dSsoda 		/* XXX probably should panic or something */
3035009ff6dSsoda 		log(LOG_ERR, "dma channel %d not finished\n", chan);
3045009ff6dSsoda 
3055009ff6dSsoda 	/* mask channel */
3065009ff6dSsoda 	if ((chan & 4) == 0)
3077fe2a5a0Stsutsui 		bus_space_write_1(iot, sc->sc_ioh1, DMA1_SMSK,
3087fe2a5a0Stsutsui 		    DMA37SM_SET | chan);
3095009ff6dSsoda 	else
3107fe2a5a0Stsutsui 		bus_space_write_1(iot, sc->sc_ioh2, DMA2_SMSK,
3117fe2a5a0Stsutsui 		    DMA37SM_SET | (chan & 3));
3125009ff6dSsoda 
3135009ff6dSsoda 	/* XXX Will this do what we want with DMAMODE_LOOP?  */
3145009ff6dSsoda 	if (di->flags & DMAMODE_READ)
3155009ff6dSsoda 		isadma_copyfrombuf(di->addr, di->nbytes, 1, di->phys);
3165009ff6dSsoda 
3175009ff6dSsoda 	isadma_unmap(di->addr, di->nbytes, 1, di->phys);
3185009ff6dSsoda 	di->active = 0;
3195009ff6dSsoda }
320