#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: bus_space.c,v 1.14 2023/12/20 06:36:02 thorpej Exp $");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/vmem_impl.h>
#include <uvm/uvm_extern.h>
#include <sys/bus.h>
#define bus_space_read(BYTES,BITS) \
__CONCAT3(uint,BITS,_t) \
__CONCAT(bus_space_read_,BYTES)(bus_space_tag_t bst, \
bus_space_handle_t bsh, bus_size_t offset) \
{ \
return (*(volatile __CONCAT3(uint,BITS,_t) *) \
(bsh + (offset << __CONCAT(bst->bs_stride_,BYTES)))); \
}
bus_space_read(1,8)
bus_space_read(2,16)
bus_space_read(4,32)
bus_space_read(8,64)
#define bus_space_read_multi(BYTES,BITS) \
void \
__CONCAT(bus_space_read_multi_,BYTES)(bus_space_tag_t bst, \
bus_space_handle_t bsh, bus_size_t offset, \
__CONCAT3(uint,BITS,_t) *datap, bus_size_t count) \
{ \
volatile __CONCAT3(uint,BITS,_t) *p = \
(volatile __CONCAT3(uint,BITS,_t) *) \
(bsh + (offset << __CONCAT(bst->bs_stride_,BYTES))); \
\
for (; count > 0; --count) \
*datap++ = *p; \
}
bus_space_read_multi(1,8)
bus_space_read_multi(2,16)
bus_space_read_multi(4,32)
bus_space_read_multi(8,64)
#define bus_space_read_region(BYTES,BITS) \
void \
__CONCAT(bus_space_read_region_,BYTES)(bus_space_tag_t bst, \
bus_space_handle_t bsh, bus_size_t offset, \
__CONCAT3(uint,BITS,_t) *datap, bus_size_t count) \
{ \
int stride = 1 << __CONCAT(bst->bs_stride_,BYTES); \
volatile __CONCAT3(uint,BITS,_t) *p = \
(volatile __CONCAT3(uint,BITS,_t) *) \
(bsh + (offset << __CONCAT(bst->bs_stride_,BYTES))); \
\
for (; count > 0; --count) { \
*datap++ = *p; \
p += stride; \
} \
}
bus_space_read_region(1,8)
bus_space_read_region(2,16)
bus_space_read_region(4,32)
bus_space_read_region(8,64)
#define bus_space_write(BYTES,BITS) \
void \
__CONCAT(bus_space_write_,BYTES)(bus_space_tag_t bst, \
bus_space_handle_t bsh, \
bus_size_t offset, __CONCAT3(uint,BITS,_t) data) \
{ \
*(volatile __CONCAT3(uint,BITS,_t) *) \
(bsh + (offset << __CONCAT(bst->bs_stride_,BYTES))) = data; \
}
bus_space_write(1,8)
bus_space_write(2,16)
bus_space_write(4,32)
bus_space_write(8,64)
#define bus_space_write_multi(BYTES,BITS) \
void \
__CONCAT(bus_space_write_multi_,BYTES)(bus_space_tag_t bst, \
bus_space_handle_t bsh, bus_size_t offset, \
const __CONCAT3(uint,BITS,_t) *datap, bus_size_t count) \
{ \
volatile __CONCAT3(uint,BITS,_t) *p = \
(volatile __CONCAT3(uint,BITS,_t) *) \
(bsh + (offset << __CONCAT(bst->bs_stride_,BYTES))); \
\
for (; count > 0; --count) \
*p = *datap++; \
}
bus_space_write_multi(1,8)
bus_space_write_multi(2,16)
bus_space_write_multi(4,32)
bus_space_write_multi(8,64)
#define bus_space_write_region(BYTES,BITS) \
void \
__CONCAT(bus_space_write_region_,BYTES)(bus_space_tag_t bst, \
bus_space_handle_t bsh, bus_size_t offset, \
const __CONCAT3(uint,BITS,_t) *datap, bus_size_t count) \
{ \
int stride = 1 << __CONCAT(bst->bs_stride_,BYTES); \
volatile __CONCAT3(uint,BITS,_t) *p = \
(volatile __CONCAT3(uint,BITS,_t) *) \
(bsh + (offset << __CONCAT(bst->bs_stride_,BYTES))); \
\
for (; count > 0; --count) { \
*p = *datap++; \
p += stride; \
} \
}
bus_space_write_region(1,8)
bus_space_write_region(2,16)
bus_space_write_region(4,32)
bus_space_write_region(8,64)
#define bus_space_set_multi(BYTES,BITS) \
void \
__CONCAT(bus_space_set_multi_,BYTES)(bus_space_tag_t bst, \
bus_space_handle_t bsh, bus_size_t offset, \
const __CONCAT3(uint,BITS,_t) data, bus_size_t count) \
{ \
volatile __CONCAT3(uint,BITS,_t) *p = \
(volatile __CONCAT3(uint,BITS,_t) *) \
(bsh + (offset << __CONCAT(bst->bs_stride_,BYTES))); \
\
for (; count > 0; --count) \
*p = data; \
}
bus_space_set_multi(1,8)
bus_space_set_multi(2,16)
bus_space_set_multi(4,32)
bus_space_set_multi(8,64)
#define bus_space_set_region(BYTES,BITS) \
void \
__CONCAT(bus_space_set_region_,BYTES)(bus_space_tag_t bst, \
bus_space_handle_t bsh, bus_size_t offset, \
__CONCAT3(uint,BITS,_t) data, bus_size_t count) \
{ \
int stride = 1 << __CONCAT(bst->bs_stride_,BYTES); \
volatile __CONCAT3(uint,BITS,_t) *p = \
(volatile __CONCAT3(uint,BITS,_t) *) \
(bsh + (offset << __CONCAT(bst->bs_stride_,BYTES))); \
\
for (; count > 0; --count) { \
*p = data; \
p += stride; \
} \
}
bus_space_set_region(1,8)
bus_space_set_region(2,16)
bus_space_set_region(4,32)
bus_space_set_region(8,64)
#define bus_space_copy_region(BYTES,BITS) \
void \
__CONCAT(bus_space_copy_region_,BYTES)(bus_space_tag_t bst, \
bus_space_handle_t srcbsh, bus_size_t srcoffset, \
bus_space_handle_t dstbsh, bus_size_t dstoffset, bus_size_t count) \
{ \
int stride = 1 << __CONCAT(bst->bs_stride_,BYTES); \
volatile __CONCAT3(uint,BITS,_t) *srcp = \
(volatile __CONCAT3(uint,BITS,_t) *) \
(srcbsh + (srcoffset << __CONCAT(bst->bs_stride_,BYTES))); \
volatile __CONCAT3(uint,BITS,_t) *dstp = \
(volatile __CONCAT3(uint,BITS,_t) *) \
(dstbsh + (dstoffset << __CONCAT(bst->bs_stride_,BYTES))); \
bus_size_t offset; \
\
if (srcp >= dstp) { \
\
for (offset = 0; count > 0; --count, offset += stride) \
dstp[offset] = srcp[offset]; \
} else { \
\
offset = (count << __CONCAT(bst->bs_stride_,BYTES)) \
- stride; \
for (; count > 0; --count, offset -= stride) \
dstp[offset] = srcp[offset]; \
} \
}
bus_space_copy_region(1,8)
bus_space_copy_region(2,16)
bus_space_copy_region(4,32)
bus_space_copy_region(8,64)
void
arc_bus_space_init(bus_space_tag_t bst, const char *name, paddr_t paddr,
vaddr_t vaddr, bus_addr_t start, bus_size_t size)
{
bst->bs_name = name;
bst->bs_arena = NULL;
bst->bs_start = start;
bst->bs_size = size;
bst->bs_pbase = paddr;
bst->bs_vbase = vaddr;
bst->bs_compose_handle = arc_bus_space_compose_handle;
bst->bs_dispose_handle = arc_bus_space_dispose_handle;
bst->bs_paddr = arc_bus_space_paddr;
bst->bs_map = arc_bus_space_map;
bst->bs_unmap = arc_bus_space_unmap;
bst->bs_subregion = arc_bus_space_subregion;
bst->bs_mmap = arc_bus_space_mmap;
bst->bs_alloc = arc_bus_space_alloc;
bst->bs_free = arc_bus_space_free;
bst->bs_aux = NULL;
bst->bs_stride_1 = 0;
bst->bs_stride_2 = 0;
bst->bs_stride_4 = 0;
bst->bs_stride_8 = 0;
}
void
arc_bus_space_init_arena(bus_space_tag_t bst, struct vmem *arena_store,
struct vmem_btag *btag_store, unsigned int btag_count)
{
int error __diagused;
bst->bs_arena = vmem_init(arena_store,
bst->bs_name,
0,
0,
1,
NULL,
NULL,
NULL,
0,
VM_NOSLEEP | VM_PRIVTAGS,
IPL_NONE);
KASSERT(bst->bs_arena != NULL);
vmem_add_bts(bst->bs_arena, btag_store, btag_count);
error = vmem_add(bst->bs_arena, bst->bs_start, bst->bs_size,
VM_NOSLEEP);
KASSERT(error == 0);
}
void
arc_bus_space_set_aligned_stride(bus_space_tag_t bst,
unsigned int alignment_shift)
{
bst->bs_stride_1 = alignment_shift;
if (alignment_shift > 0)
--alignment_shift;
bst->bs_stride_2 = alignment_shift;
if (alignment_shift > 0)
--alignment_shift;
bst->bs_stride_4 = alignment_shift;
if (alignment_shift > 0)
--alignment_shift;
bst->bs_stride_8 = alignment_shift;
}
int
arc_bus_space_compose_handle(bus_space_tag_t bst, bus_addr_t addr,
bus_size_t size, int flags, bus_space_handle_t *bshp)
{
bus_space_handle_t bsh = bst->bs_vbase + (addr - bst->bs_start);
if ((flags & BUS_SPACE_MAP_CACHEABLE) == 0) {
*bshp = bsh;
return 0;
}
if (bsh < MIPS_KSEG1_START)
panic("arc_bus_space_compose_handle: bad address 0x%x", bsh);
if (bsh < MIPS_KSEG2_START) {
*bshp = MIPS_PHYS_TO_KSEG0(MIPS_KSEG1_TO_PHYS(bsh));
return 0;
}
#ifdef DIAGNOSTIC
printf("arc_bus_space_compose_handle: ignore cacheable 0x%x\n", bsh);
#endif
*bshp = bsh;
return 0;
}
int
arc_bus_space_dispose_handle(bus_space_tag_t bst, bus_space_handle_t bsh,
bus_size_t size)
{
return 0;
}
int
arc_bus_space_paddr(bus_space_tag_t bst, bus_space_handle_t bsh, paddr_t *pap)
{
if (bsh < MIPS_KSEG0_START)
panic("arc_bus_space_paddr(0x%qx): bad address",
(unsigned long long) bsh);
else if (bsh < MIPS_KSEG1_START)
*pap = MIPS_KSEG0_TO_PHYS(bsh);
else if (bsh < MIPS_KSEG2_START)
*pap = MIPS_KSEG1_TO_PHYS(bsh);
else {
*pap = bst->bs_pbase + (bsh - bst->bs_vbase);
}
return 0;
}
int
arc_bus_space_map(bus_space_tag_t bst, bus_addr_t addr, bus_size_t size,
int flags, bus_space_handle_t *bshp)
{
int err;
if (addr < bst->bs_start || addr + size > bst->bs_start + bst->bs_size)
return EINVAL;
if (bst->bs_arena != NULL) {
err = vmem_xalloc_addr(bst->bs_arena, addr, size, VM_NOSLEEP);
if (err)
return err;
}
return bus_space_compose_handle(bst, addr, size, flags, bshp);
}
void
arc_bus_space_unmap(bus_space_tag_t bst, bus_space_handle_t bsh,
bus_size_t size)
{
if (bst->bs_arena != NULL) {
paddr_t pa;
bus_addr_t addr;
int err;
err = bus_space_paddr(bst, bsh, &pa);
if (err)
panic("arc_bus_space_unmap: %s va 0x%qx: error %d",
bst->bs_name, (unsigned long long) bsh, err);
addr = (bus_size_t)(pa - bst->bs_pbase) + bst->bs_start;
vmem_xfree(bst->bs_arena, addr, size);
}
bus_space_dispose_handle(bst, bsh, size);
}
int
arc_bus_space_subregion(bus_space_tag_t bst, bus_space_handle_t bsh,
bus_size_t offset, bus_size_t size, bus_space_handle_t *nbshp)
{
*nbshp = bsh + offset;
return 0;
}
paddr_t
arc_bus_space_mmap(bus_space_tag_t bst, bus_addr_t addr, off_t off, int prot,
int flags)
{
if (addr < bst->bs_start ||
(addr + off) >= (bst->bs_start + bst->bs_size))
return -1;
return mips_btop(bst->bs_pbase + (addr - bst->bs_start) + off);
}
int
arc_bus_space_alloc(bus_space_tag_t bst, bus_addr_t start, bus_addr_t end,
bus_size_t size, bus_size_t align, bus_size_t boundary, int flags,
bus_addr_t *addrp, bus_space_handle_t *bshp)
{
vmem_addr_t addr;
int err;
if (bst->bs_arena == NULL)
panic("arc_bus_space_alloc: vmem arena %s not available",
bst->bs_name);
if (start < bst->bs_start ||
start + size > bst->bs_start + bst->bs_size)
return EINVAL;
err = vmem_xalloc(bst->bs_arena, size,
align,
0,
boundary,
start,
end,
VM_BESTFIT | VM_NOSLEEP,
&addr);
if (err)
return err;
*addrp = addr;
return bus_space_compose_handle(bst, addr, size, flags, bshp);
}