/*	$NetBSD: busfuncs.c,v 1.13 2022/05/18 13:56:31 andvar Exp $	*/

/*-
 * Copyright (c) 1998 The NetBSD Foundation, Inc.
 * All rights reserved.
 *
 * This code is derived from software contributed to The NetBSD Foundation
 * by Ignatios Souvatzis.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: busfuncs.c,v 1.13 2022/05/18 13:56:31 andvar Exp $");

/*
 * Amiga bus access methods for data widths > 1
 * XXX currently, only 16bit methods are defined
 */

#include <sys/bus.h>

bsr(amiga_contiguous_read_2, u_int16_t);
bsw(amiga_contiguous_write_2, u_int16_t);
bsrm(amiga_contiguous_read_multi_2, u_int16_t);
bswm(amiga_contiguous_write_multi_2, u_int16_t);
bsrm(amiga_contiguous_read_region_2, u_int16_t);
bswm(amiga_contiguous_write_region_2, u_int16_t);
bssr(amiga_contiguous_set_region_2, u_int16_t);
bscr(amiga_contiguous_copy_region_2, u_int16_t);

bsr(amiga_interleaved_read_2, u_int16_t);
bsw(amiga_interleaved_write_2, u_int16_t);
bsrm(amiga_interleaved_read_multi_2, u_int16_t);
bswm(amiga_interleaved_write_multi_2, u_int16_t);
bsrm(amiga_interleaved_read_region_2, u_int16_t);
bswm(amiga_interleaved_write_region_2, u_int16_t);
bssr(amiga_interleaved_set_region_2, u_int16_t);
bscr(amiga_interleaved_copy_region_2, u_int16_t);

bsr(amiga_interleaved_wordaccess_read_2, u_int16_t);
bsw(amiga_interleaved_wordaccess_write_2, u_int16_t);
bsrm(amiga_interleaved_wordaccess_read_multi_2, u_int16_t);
bswm(amiga_interleaved_wordaccess_write_multi_2, u_int16_t);
bsrm(amiga_interleaved_wordaccess_read_region_2, u_int16_t);
bswm(amiga_interleaved_wordaccess_write_region_2, u_int16_t);
bssr(amiga_interleaved_wordaccess_set_region_2, u_int16_t);
bscr(amiga_interleaved_wordaccess_copy_region_2, u_int16_t);

const struct amiga_bus_space_methods amiga_contiguous_methods = {
	amiga_contiguous_read_2,
	amiga_contiguous_write_2,
	amiga_contiguous_read_multi_2,
	amiga_contiguous_write_multi_2,
	amiga_contiguous_read_region_2,
	amiga_contiguous_write_region_2,
	/* next two identical to the above here */
	amiga_contiguous_read_region_2,
	amiga_contiguous_write_region_2,
	amiga_contiguous_set_region_2,
	amiga_contiguous_copy_region_2
};

const struct amiga_bus_space_methods amiga_interleaved_methods = {
	amiga_interleaved_read_2,
	amiga_interleaved_write_2,
	amiga_interleaved_read_multi_2,
	amiga_interleaved_write_multi_2,
	amiga_interleaved_read_region_2,
	amiga_interleaved_write_region_2,
	/* next two identical to the above here */
	amiga_interleaved_read_region_2,
	amiga_interleaved_write_region_2,
	amiga_interleaved_set_region_2,
	amiga_interleaved_copy_region_2
};

const struct amiga_bus_space_methods amiga_interleaved_wordaccess_methods = {
	amiga_interleaved_wordaccess_read_2,
	amiga_interleaved_wordaccess_write_2,
	amiga_interleaved_wordaccess_read_multi_2,
	amiga_interleaved_wordaccess_write_multi_2,
	amiga_interleaved_wordaccess_read_region_2,
	amiga_interleaved_wordaccess_write_region_2,
	/* next two identical to the above here */
	amiga_interleaved_wordaccess_read_region_2,	/* region_stream */
	amiga_interleaved_wordaccess_write_region_2,
	amiga_interleaved_wordaccess_set_region_2,
	amiga_interleaved_wordaccess_copy_region_2
};

/*
 * Contiguous methods
 * Be sure to only create word accesses.
 * For busses that _need_ split byte accesses, use the interleaved functions.
 */

u_int16_t
amiga_contiguous_read_2(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o)
{
	/* ARGSUSED */
	u_int16_t x;

	x  = * (u_int16_t *) (h + o); /* only used if t->stride == 0 */
	amiga_bus_reorder_protect();
	return x;
}

void
amiga_contiguous_write_2(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o, u_int16_t v)
{
	/* ARGSUSED */
	* (u_int16_t *) (h + o) = v;
	amiga_bus_reorder_protect();
}

void
amiga_contiguous_read_multi_2(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o, u_int16_t *p, bus_size_t s)
{
	/* ARGSUSED */
	volatile u_int16_t *q = (volatile u_int16_t *)(h + o);

	while (s-- > 0) {
		*p++ =  *q;
		amiga_bus_reorder_protect();
	}
}

void
amiga_contiguous_write_multi_2(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o, const u_int16_t *p, bus_size_t s)
{
	/* ARGSUSED */
	volatile u_int16_t *q = (volatile u_int16_t *)(h + o);

	while (s-- > 0) {
		*q = *p++;
		amiga_bus_reorder_protect();
	}
}

void
amiga_contiguous_read_region_2(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o, u_int16_t *p, bus_size_t s)
{
	/* ARGSUSED */
	volatile u_int16_t *q = (volatile u_int16_t *)(h + o);

	while (s-- > 0) {
		*p++ =  *q++;
		amiga_bus_reorder_protect();
	}
}

void
amiga_contiguous_write_region_2(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o, const u_int16_t *p, bus_size_t s)
{
	/* ARGSUSED */
	volatile u_int16_t *q = (volatile u_int16_t *)(h + o);

	while (s-- > 0) {
		*q++ = *p++;
		amiga_bus_reorder_protect();
	}
}

void
amiga_contiguous_set_region_2(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o, u_int16_t v, bus_size_t s)
{
	/* ARGSUSED */
	volatile u_int16_t *q = (volatile u_int16_t *)(h + o);

	while (s-- > 0) {
		*q++ = v;
		amiga_bus_reorder_protect();
	}
}

void
amiga_contiguous_copy_region_2(bus_space_tag_t t, bus_space_handle_t srch, bus_size_t srco, bus_space_handle_t dsth, bus_size_t dsto, bus_size_t s)
{
	/* ARGSUSED */
	volatile u_int16_t *p = (volatile u_int16_t *)(srch + srco);
	volatile u_int16_t *q = (volatile u_int16_t *)(dsth + dsto);

	while (s-- > 0) {
		*q++ = *p++;
		amiga_bus_reorder_protect();
	}
}

/*
 * Interleaved methods.
 * These use single-byte accesses. In case of stride = 0, the contiguous
 * methods are preferred, as they only create word accesses.
 */

u_int16_t
amiga_interleaved_read_2(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o)
{
	volatile u_int8_t *q;
	u_int16_t x;
	int step;

	step = 1 << t->stride;
	q = (volatile u_int8_t *)(h + (o << t->stride));
	x = ((*q) << 8) | *(q + step);
	amiga_bus_reorder_protect();
	return x;
}

void
amiga_interleaved_write_2(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o, u_int16_t v)
{
	volatile u_int8_t *q;
	int step;

	step = 1 << t->stride;
	q = (volatile u_int8_t *)(h + (o << t->stride));

	*q = v >> 8;
	*(q+step) = v;
	amiga_bus_reorder_protect();
}

void
amiga_interleaved_read_multi_2(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o, u_int16_t *p, bus_size_t s)
{
	volatile u_int8_t *q;
	int step;

	step = 1 << t->stride;
	q = (volatile u_int8_t *)(h + (o << t->stride));

	while (s-- > 0) {
		*p++ =  ((*q)<<8) | *(q+step);
		amiga_bus_reorder_protect();
	}
}

void
amiga_interleaved_write_multi_2(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o, const u_int16_t *p, bus_size_t s)
{
	volatile u_int8_t *q;
	int step;
	u_int16_t v;

	step = 1 << t->stride;
	q = (volatile u_int8_t *)(h + (o << t->stride));

	while (s-- > 0) {
		v = *p++;
		*q 		= v>>8;
		*(q + step)	= v;
		amiga_bus_reorder_protect();
	}
}

void
amiga_interleaved_read_region_2(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o, u_int16_t *p, bus_size_t s)
{
	volatile u_int8_t *q;
	int step;
	u_int16_t v;

	step = 1 << t->stride;
	q = (volatile u_int8_t *)(h + (o << t->stride));

	while (s-- > 0) {
		v = (*q) << 8;
		q += step;
		v |= *q;
		amiga_bus_reorder_protect();
		q += step;
		*p++ =  v;
	}
}

void
amiga_interleaved_write_region_2(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o, const u_int16_t *p, bus_size_t s)
{
	volatile u_int8_t *q;
	int step;
	u_int16_t v;

	step = 1 << t->stride;
	q = (volatile u_int8_t *)(h + (o << t->stride));

	while (s-- > 0) {
		v = *p++;
		*q = v >> 8;
		q += step;
		*q = v;
		amiga_bus_reorder_protect();
		q += step;
	}
}

void
amiga_interleaved_set_region_2(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o, u_int16_t v, bus_size_t s)
{
	int step;
	volatile u_int16_t *q = (volatile u_int16_t *)(h + o);

	step = 1 << t->stride;

	while (s-- > 0) {
		*q = v;
		amiga_bus_reorder_protect();
		q += step;
	}
}

void
amiga_interleaved_copy_region_2(bus_space_tag_t t, bus_space_handle_t srch, bus_size_t srco, bus_space_handle_t dsth, bus_size_t dsto, bus_size_t s)
{
	int step;
	volatile u_int16_t *p = (volatile u_int16_t *)(srch + srco);
	volatile u_int16_t *q = (volatile u_int16_t *)(dsth + dsto);

	step = 1 << t->stride;

	while (s-- > 0) {
		*q = *p;
		amiga_bus_reorder_protect();
		p += step;
		q += step;
	}
}

/*
 * Interleaved_wordaccess methods. Have a stride, but translate
 * word accesses to word accesses at the target address.
 */

u_int16_t
amiga_interleaved_wordaccess_read_2(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o)
{
	/* ARGSUSED */
	u_int16_t x;

	x = * (u_int16_t *) (h + (o << t->stride));
	amiga_bus_reorder_protect();
	return x;
}

void
amiga_interleaved_wordaccess_write_2(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o, u_int16_t v)
{
	/* ARGSUSED */
	* (u_int16_t *) (h + (o << t->stride)) = v;
	amiga_bus_reorder_protect();
}

void
amiga_interleaved_wordaccess_read_multi_2(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o, u_int16_t *p, bus_size_t s)
{
	/* ARGSUSED */
	volatile u_int16_t *q;

	q = (volatile u_int16_t *)(h + (o << t->stride));

	while (s-- > 0) {
		*p++ =  *q;
		amiga_bus_reorder_protect();
	}
}

void
amiga_interleaved_wordaccess_write_multi_2(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o, const u_int16_t *p, bus_size_t s)
{
	/* ARGSUSED */
	volatile u_int16_t *q;

	q = (volatile u_int16_t *)(h + (o << t->stride));

	while (s-- > 0) {
		*q = *p++;
		amiga_bus_reorder_protect();
	}
}

void
amiga_interleaved_wordaccess_read_region_2(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o, u_int16_t *p, bus_size_t s)
{
	/* ARGSUSED */
	volatile u_int16_t *q;
	int step;

	q = (volatile u_int16_t *)(h + (o << t->stride));
	step = (1 << t->stride) / sizeof(u_int16_t);

	while (s-- > 0) {
		*p++ =  *q;
		amiga_bus_reorder_protect();
		q += step;
	}
}

void
amiga_interleaved_wordaccess_write_region_2(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o, const u_int16_t *p, bus_size_t s)
{
	/* ARGSUSED */
	volatile u_int16_t *q;
	int step;

	q = (volatile u_int16_t *)(h + (o << t->stride));
	step = (1 << t->stride) / sizeof(u_int16_t);

	while (s-- > 0) {
		*q = *p++;
		amiga_bus_reorder_protect();
		q += step;
	}
}

void
amiga_interleaved_wordaccess_set_region_2(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o, u_int16_t v, bus_size_t s)
{
	/* ARGSUSED */
	volatile u_int16_t *q;
	int step;

	q = (volatile u_int16_t *)(h + (o << t->stride));
	step = (1 << t->stride) / sizeof(u_int16_t);

	while (s-- > 0) {
		*q = v;
		amiga_bus_reorder_protect();
		q += step;
	}
}

void
amiga_interleaved_wordaccess_copy_region_2(bus_space_tag_t t, bus_space_handle_t srch, bus_size_t srco, bus_space_handle_t dsth, bus_size_t dsto, bus_size_t s)
{
	int step;
	/* ARGSUSED */
	volatile u_int16_t *p;
	volatile u_int16_t *q;

	p = (volatile u_int16_t *)(srch + (srco << t->stride));
	q = (volatile u_int16_t *)(dsth + (dsto << t->stride));
	step = (1 << t->stride) / sizeof(u_int16_t);

	while (s-- > 0) {
		*q = *p;
		amiga_bus_reorder_protect();
		q += step;
		p += step;
	}
}
