/*	$OpenBSD: dbdma.c,v 1.10 2014/07/12 18:44:42 tedu Exp $	*/
/*	$NetBSD: dbdma.c,v 1.2 1998/08/21 16:13:28 tsubai Exp $	*/

/*
 * Copyright 1991-1998 by Open Software Foundation, Inc.
 *              All Rights Reserved
 *
 * Permission to use, copy, modify, and distribute this software and
 * its documentation for any purpose and without fee is hereby granted,
 * provided that the above copyright notice appears in all copies and
 * that both the copyright notice and this permission notice appear in
 * supporting documentation.
 *
 * OSF DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE.
 *
 * IN NO EVENT SHALL OSF BE LIABLE FOR ANY SPECIAL, INDIRECT, OR
 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
 * LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT,
 * NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
 * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 *
 */

#include <sys/param.h>
#include <sys/malloc.h>
#include <sys/systm.h>

#include <uvm/uvm_extern.h>

#include <machine/bus.h>
#include <macppc/dev/dbdma.h>

dbdma_command_t	*dbdma_alloc_commands = NULL;

void
dbdma_start(dbdma_regmap_t *dmap, dbdma_t dt)
{
	u_int32_t addr = dt->d_paddr;

	DBDMA_ST4_ENDIAN(&dmap->d_intselect, DBDMA_CLEAR_CNTRL((0xffff)));
	DBDMA_ST4_ENDIAN(&dmap->d_control, DBDMA_CLEAR_CNTRL((
	    DBDMA_CNTRL_ACTIVE |
	    DBDMA_CNTRL_DEAD |
	    DBDMA_CNTRL_WAKE |
	    DBDMA_CNTRL_FLUSH |
	    DBDMA_CNTRL_PAUSE |
	    DBDMA_CNTRL_RUN)));

	/* XXX time-bind it? */
	do {
		delay(10);
	} while (DBDMA_LD4_ENDIAN(&dmap->d_status) & DBDMA_CNTRL_ACTIVE);


	DBDMA_ST4_ENDIAN(&dmap->d_cmdptrhi, 0); /* 64-bit not yet */
	DBDMA_ST4_ENDIAN(&dmap->d_cmdptrlo, addr);

	DBDMA_ST4_ENDIAN(&dmap->d_control,
		DBDMA_SET_CNTRL(DBDMA_CNTRL_RUN|DBDMA_CNTRL_WAKE)|
		DBDMA_CLEAR_CNTRL(DBDMA_CNTRL_PAUSE|DBDMA_CNTRL_DEAD) );
}

void
dbdma_stop(dbdma_regmap_t *dmap)
{
	DBDMA_ST4_ENDIAN(&dmap->d_control, DBDMA_CLEAR_CNTRL(DBDMA_CNTRL_RUN) |
			  DBDMA_SET_CNTRL(DBDMA_CNTRL_FLUSH));

	while (DBDMA_LD4_ENDIAN(&dmap->d_status) &
		(DBDMA_CNTRL_ACTIVE|DBDMA_CNTRL_FLUSH));
}

void
dbdma_flush(dbdma_regmap_t *dmap)
{
	DBDMA_ST4_ENDIAN(&dmap->d_control, DBDMA_SET_CNTRL(DBDMA_CNTRL_FLUSH));

	/* XXX time-bind it? */
	while (DBDMA_LD4_ENDIAN(&dmap->d_status) & (DBDMA_CNTRL_FLUSH));
}

void
dbdma_reset(dbdma_regmap_t *dmap)
{
	DBDMA_ST4_ENDIAN(&dmap->d_control,
			 DBDMA_CLEAR_CNTRL( (DBDMA_CNTRL_ACTIVE	|
					     DBDMA_CNTRL_DEAD	|
					     DBDMA_CNTRL_WAKE	|
					     DBDMA_CNTRL_FLUSH	|
					     DBDMA_CNTRL_PAUSE	|
					     DBDMA_CNTRL_RUN      )));

	/* XXX time-bind it? */
	while (DBDMA_LD4_ENDIAN(&dmap->d_status) & DBDMA_CNTRL_RUN);
}

void
dbdma_continue(dbdma_regmap_t *dmap)
{
	DBDMA_ST4_ENDIAN(&dmap->d_control,
		DBDMA_SET_CNTRL(DBDMA_CNTRL_RUN | DBDMA_CNTRL_WAKE) |
		DBDMA_CLEAR_CNTRL(DBDMA_CNTRL_PAUSE | DBDMA_CNTRL_DEAD));
}

void
dbdma_pause(dbdma_regmap_t *dmap)
{
	DBDMA_ST4_ENDIAN(&dmap->d_control,DBDMA_SET_CNTRL(DBDMA_CNTRL_PAUSE));

	/* XXX time-bind it? */
	while (DBDMA_LD4_ENDIAN(&dmap->d_status) & DBDMA_CNTRL_ACTIVE);
}

dbdma_t
dbdma_alloc(bus_dma_tag_t dmat, int size)
{
	dbdma_t dt;
	int error;

	dt = malloc(sizeof *dt, M_DEVBUF, M_NOWAIT | M_ZERO);
	if (!dt)
		return (dt);

	dt->d_size = size *= sizeof(dbdma_command_t);
	dt->d_dmat = dmat;
	if ((error = bus_dmamem_alloc(dmat, size, NBPG, 0, dt->d_segs,
	    1, &dt->d_nsegs, BUS_DMA_NOWAIT)) != 0) {
		printf("dbdma: unable to allocate dma, error = %d\n", error);
	} else if ((error = bus_dmamem_map(dmat, dt->d_segs, dt->d_nsegs, size,
	    (caddr_t *)&dt->d_addr, BUS_DMA_NOWAIT | BUS_DMA_COHERENT)) != 0) {
		printf("dbdma: unable to map dma, error = %d\n", error);
	} else if ((error = bus_dmamap_create(dmat, dt->d_size, 1,
	    dt->d_size, 0, BUS_DMA_NOWAIT, &dt->d_map)) != 0) {
		printf("dbdma: unable to create dma map, error = %d\n", error);
	} else if ((error = bus_dmamap_load_raw(dmat, dt->d_map,
	    dt->d_segs, dt->d_nsegs, size, BUS_DMA_NOWAIT)) != 0) {
		printf("dbdma: unable to load dma map, error = %d\n", error);
	} else
		return dt;

	dbdma_free(dt);
	return (NULL);
}

void
dbdma_free(dbdma_t dt)
{
	if (dt->d_map)
		bus_dmamap_destroy(dt->d_dmat, dt->d_map);
	if (dt->d_addr)
		bus_dmamem_unmap(dt->d_dmat, (caddr_t)dt->d_addr, dt->d_size);
	if (dt->d_nsegs)
		bus_dmamem_free(dt->d_dmat, dt->d_segs, dt->d_nsegs);
	free(dt, M_DEVBUF, 0);
}
