/* $FreeBSD$ */
/* $NecBSD: busiosubr.c,v 1.30.4.4 1999/08/28 02:25:35 honda Exp $ */
/* $NetBSD$ */
/*-
* [NetBSD for NEC PC-98 series]
* Copyright (c) 1996, 1997, 1998
* NetBSD/pc98 porting staff. All rights reserved.
*
* [Ported for FreeBSD]
* Copyright (c) 2001
* TAKAHASHI Yoshihiro. All rights reserved.
*
* 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.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
*/
/*
* Copyright (c) 1997, 1998
* Naofumi HONDA. All rights reserved.
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <machine/bus.h>
static MALLOC_DEFINE(M_BUSSPACEHANDLE, "busspacehandle", "Bus space handle");
_BUS_SPACE_CALL_FUNCS_PROTO(SBUS_DA_io,u_int8_t,1)
_BUS_SPACE_CALL_FUNCS_PROTO(SBUS_DA_io,u_int16_t,2)
_BUS_SPACE_CALL_FUNCS_PROTO(SBUS_DA_io,u_int32_t,4)
_BUS_SPACE_CALL_FUNCS_PROTO(SBUS_DA_mem,u_int8_t,1)
_BUS_SPACE_CALL_FUNCS_PROTO(SBUS_DA_mem,u_int16_t,2)
_BUS_SPACE_CALL_FUNCS_PROTO(SBUS_DA_mem,u_int32_t,4)
_BUS_SPACE_CALL_FUNCS_PROTO(SBUS_RA_io,u_int8_t,1)
_BUS_SPACE_CALL_FUNCS_PROTO(SBUS_RA_io,u_int16_t,2)
_BUS_SPACE_CALL_FUNCS_PROTO(SBUS_RA_io,u_int32_t,4)
_BUS_SPACE_CALL_FUNCS_PROTO(SBUS_RA_mem,u_int8_t,1)
_BUS_SPACE_CALL_FUNCS_PROTO(SBUS_RA_mem,u_int16_t,2)
_BUS_SPACE_CALL_FUNCS_PROTO(SBUS_RA_mem,u_int32_t,4)
struct bus_space_tag SBUS_io_space_tag = {
BUS_SPACE_TAG_IO,
/* direct bus access methods */
{
_BUS_SPACE_CALL_FUNCS_TAB(SBUS_DA_io,u_int8_t,1),
_BUS_SPACE_CALL_FUNCS_TAB(SBUS_DA_io,u_int16_t,2),
_BUS_SPACE_CALL_FUNCS_TAB(SBUS_DA_io,u_int32_t,4),
},
/* relocate bus access methods */
{
_BUS_SPACE_CALL_FUNCS_TAB(SBUS_RA_io,u_int8_t,1),
_BUS_SPACE_CALL_FUNCS_TAB(SBUS_RA_io,u_int16_t,2),
_BUS_SPACE_CALL_FUNCS_TAB(SBUS_RA_io,u_int32_t,4),
}
};
struct bus_space_tag SBUS_mem_space_tag = {
BUS_SPACE_TAG_MEM,
/* direct bus access methods */
{
_BUS_SPACE_CALL_FUNCS_TAB(SBUS_DA_mem,u_int8_t,1),
_BUS_SPACE_CALL_FUNCS_TAB(SBUS_DA_mem,u_int16_t,2),
_BUS_SPACE_CALL_FUNCS_TAB(SBUS_DA_mem,u_int32_t,4),
},
/* relocate bus access methods */
{
_BUS_SPACE_CALL_FUNCS_TAB(SBUS_RA_mem,u_int8_t,1),
_BUS_SPACE_CALL_FUNCS_TAB(SBUS_RA_mem,u_int16_t,2),
_BUS_SPACE_CALL_FUNCS_TAB(SBUS_RA_mem,u_int32_t,4),
}
};
#include "opt_mecia.h"
#ifdef DEV_MECIA
_BUS_SPACE_CALL_FUNCS_PROTO(NEPC_DA_io,u_int16_t,2)
_BUS_SPACE_CALL_FUNCS_PROTO(NEPC_DA_io,u_int32_t,4)
_BUS_SPACE_CALL_FUNCS_PROTO(NEPC_RA_io,u_int16_t,2)
_BUS_SPACE_CALL_FUNCS_PROTO(NEPC_RA_io,u_int32_t,4)
struct bus_space_tag NEPC_io_space_tag = {
BUS_SPACE_TAG_IO,
/* direct bus access methods */
{
_BUS_SPACE_CALL_FUNCS_TAB(SBUS_DA_io,u_int8_t,1),
_BUS_SPACE_CALL_FUNCS_TAB(NEPC_DA_io,u_int16_t,2),
_BUS_SPACE_CALL_FUNCS_TAB(NEPC_DA_io,u_int32_t,4),
},
/* relocate bus access methods */
{
_BUS_SPACE_CALL_FUNCS_TAB(SBUS_RA_io,u_int8_t,1),
_BUS_SPACE_CALL_FUNCS_TAB(NEPC_RA_io,u_int16_t,2),
_BUS_SPACE_CALL_FUNCS_TAB(NEPC_RA_io,u_int32_t,4),
}
};
struct bus_space_tag NEPC_mem_space_tag = {
BUS_SPACE_TAG_MEM,
/* direct bus access methods */
{
_BUS_SPACE_CALL_FUNCS_TAB(SBUS_DA_mem,u_int8_t,1),
_BUS_SPACE_CALL_FUNCS_TAB(SBUS_DA_mem,u_int16_t,2),
_BUS_SPACE_CALL_FUNCS_TAB(SBUS_DA_mem,u_int32_t,4),
},
/* relocate bus access methods */
{
_BUS_SPACE_CALL_FUNCS_TAB(SBUS_RA_mem,u_int8_t,1),
_BUS_SPACE_CALL_FUNCS_TAB(SBUS_RA_mem,u_int16_t,2),
_BUS_SPACE_CALL_FUNCS_TAB(SBUS_RA_mem,u_int32_t,4),
}
};
#endif /* DEV_MECIA */
/*************************************************************************
* map init
*************************************************************************/
static __inline void
bus_space_iat_init(bus_space_handle_t bsh)
{
int i;
for (i = 0; i < bsh->bsh_maxiatsz; i++)
bsh->bsh_iat[i] = bsh->bsh_base + i;
}
/*************************************************************************
* handle allocation
*************************************************************************/
int
i386_bus_space_handle_alloc(bus_space_tag_t t, bus_addr_t bpa, bus_size_t size,
bus_space_handle_t *bshp)
{
bus_space_handle_t bsh;
bsh = (bus_space_handle_t) malloc(sizeof (*bsh), M_BUSSPACEHANDLE,
M_NOWAIT | M_ZERO);
if (bsh == NULL)
return ENOMEM;
bsh->bsh_maxiatsz = BUS_SPACE_IAT_MAXSIZE;
bsh->bsh_iatsz = 0;
bsh->bsh_base = bpa;
bsh->bsh_sz = size;
bsh->bsh_res = NULL;
bsh->bsh_ressz = 0;
bus_space_iat_init(bsh);
bsh->bsh_bam = t->bs_da; /* default: direct access */
*bshp = bsh;
return 0;
}
void
i386_bus_space_handle_free(bus_space_tag_t t, bus_space_handle_t bsh,
size_t size)
{
free(bsh, M_BUSSPACEHANDLE);
}
/*************************************************************************
* map
*************************************************************************/
int
i386_memio_map(bus_space_tag_t t, bus_addr_t bpa, bus_size_t size, int flags,
bus_space_handle_t *bshp)
{
return i386_bus_space_handle_alloc(t, bpa, size, bshp);
}
void
i386_memio_unmap(bus_space_tag_t t, bus_space_handle_t bsh, bus_size_t size)
{
i386_bus_space_handle_free(t, bsh, bsh->bsh_sz);
}
void
i386_memio_free(bus_space_tag_t t, bus_space_handle_t bsh, bus_size_t size)
{
/* i386_memio_unmap() does all that we need to do. */
i386_memio_unmap(t, bsh, bsh->bsh_sz);
}
int
i386_memio_map_load(bus_space_tag_t t, bus_space_handle_t bsh,
bus_size_t size, bus_space_iat_t iat, u_int flags __unused)
{
int i;
if (size > bsh->bsh_maxiatsz) {
printf("i386_memio_map_load: map size too large\n");
return EINVAL;
}
for (i = 0; i < bsh->bsh_maxiatsz; i++) {
if (i < size)
bsh->bsh_iat[i] = iat[i];
else
bsh->bsh_iat[i] = 0;
bsh->bsh_iat[i] += bsh->bsh_base;
}
bsh->bsh_iatsz = size;
bsh->bsh_bam = t->bs_ra; /* relocate access */
return 0;
}
int
i386_memio_subregion(bus_space_tag_t t, bus_space_handle_t pbsh,
bus_size_t offset, bus_size_t size,
bus_space_handle_t *tbshp)
{
int i, error = 0;
bus_space_handle_t bsh;
bus_addr_t pbase;
pbase = pbsh->bsh_base + offset;
switch (t->bs_tag) {
case BUS_SPACE_TAG_IO:
if (pbsh->bsh_iatsz > 0) {
if (offset >= pbsh->bsh_iatsz ||
offset + size > pbsh->bsh_iatsz)
return EINVAL;
pbase = pbsh->bsh_base;
}
break;
case BUS_SPACE_TAG_MEM:
if (pbsh->bsh_iatsz > 0)
return EINVAL;
if (offset > pbsh->bsh_sz || offset + size > pbsh->bsh_sz)
return EINVAL;
break;
default:
panic("i386_memio_subregion: bad bus space tag");
break;
}
error = i386_bus_space_handle_alloc(t, pbase, size, &bsh);
if (error != 0)
return error;
switch (t->bs_tag) {
case BUS_SPACE_TAG_IO:
if (pbsh->bsh_iatsz > 0) {
for (i = 0; i < size; i ++)
bsh->bsh_iat[i] = pbsh->bsh_iat[i + offset];
bsh->bsh_iatsz = size;
} else if (pbsh->bsh_base > bsh->bsh_base ||
pbsh->bsh_base + pbsh->bsh_sz <
bsh->bsh_base + bsh->bsh_sz) {
i386_bus_space_handle_free(t, bsh, size);
return EINVAL;
}
break;
case BUS_SPACE_TAG_MEM:
break;
}
if (pbsh->bsh_iatsz > 0)
bsh->bsh_bam = t->bs_ra; /* relocate access */
*tbshp = bsh;
return error;
}
int
i386_memio_compare(bus_space_tag_t t1, bus_space_handle_t bsh1,
bus_space_tag_t t2, bus_space_handle_t bsh2)
{
int i;
if (t1->bs_tag != t2->bs_tag)
return (1);
if (bsh1->bsh_base != bsh2->bsh_base)
return (1);
if (bsh1->bsh_sz != bsh2->bsh_sz)
return (1);
if (bsh1->bsh_bam.bs_read_1 != bsh2->bsh_bam.bs_read_1) /* XXX */
return (1);
if (bsh1->bsh_iatsz != bsh2->bsh_iatsz)
return (1);
for (i = 0; i < bsh1->bsh_iatsz; i++) {
if (bsh1->bsh_iat[i] != bsh2->bsh_iat[i])
return (1);
}
return (0);
}