#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: bus_space.c,v 1.35 2024/02/11 10:36:40 andvar Exp $");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/extent.h>
#include <sys/bus.h>
#include <uvm/uvm_extern.h>
#include <mips/cache.h>
#include <mips/locore.h>
#include <mips/pte.h>
#include <machine/bus_space_hpcmips.h>
#ifdef BUS_SPACE_DEBUG
#define DPRINTF(arg) printf arg
#else
#define DPRINTF(arg)
#endif
#define MAX_BUSSPACE_TAG 10
bus_space_handle_t __hpcmips_cacheable(struct bus_space_tag_hpcmips*,
bus_addr_t, bus_size_t, int);
bus_space_protos(_);
bus_space_protos(bs_notimpl);
static struct bus_space_tag_hpcmips __bus_space[MAX_BUSSPACE_TAG];
static int __bus_space_index;
static struct bus_space_tag_hpcmips __sys_bus_space = {
{
NULL,
{
__bs_map,
__bs_unmap,
__bs_subregion,
__bs_alloc,
__bs_free,
bs_notimpl_bs_vaddr,
bs_notimpl_bs_mmap,
__bs_barrier,
__bs_peek,
__bs_poke,
__bs_r_1,
__bs_r_2,
__bs_r_4,
bs_notimpl_bs_r_8,
__bs_rm_1,
__bs_rm_2,
__bs_rm_4,
bs_notimpl_bs_rm_8,
__bs_rr_1,
__bs_rr_2,
__bs_rr_4,
bs_notimpl_bs_rr_8,
__bs_w_1,
__bs_w_2,
__bs_w_4,
bs_notimpl_bs_w_8,
__bs_wm_1,
__bs_wm_2,
__bs_wm_4,
bs_notimpl_bs_wm_8,
__bs_wr_1,
__bs_wr_2,
__bs_wr_4,
bs_notimpl_bs_wr_8,
__bs_sm_1,
__bs_sm_2,
__bs_sm_4,
bs_notimpl_bs_sm_8,
__bs_sr_1,
__bs_sr_2,
__bs_sr_4,
bs_notimpl_bs_sr_8,
__bs_c_1,
__bs_c_2,
__bs_c_4,
bs_notimpl_bs_c_8,
},
},
"whole bus space",
0,
0xffffffff,
NULL,
};
static bus_space_tag_t __sys_bus_space_tag = &__sys_bus_space.bst;
bus_space_tag_t
hpcmips_system_bus_space(void)
{
return (__sys_bus_space_tag);
}
struct bus_space_tag_hpcmips *
hpcmips_system_bus_space_hpcmips(void)
{
return (&__sys_bus_space);
}
struct bus_space_tag_hpcmips *
hpcmips_alloc_bus_space_tag(void)
{
if (__bus_space_index >= MAX_BUSSPACE_TAG) {
panic("hpcmips_internal_alloc_bus_space_tag: tag full.");
}
return (&__bus_space[__bus_space_index++]);
}
void
hpcmips_init_bus_space(struct bus_space_tag_hpcmips *t,
struct bus_space_tag_hpcmips *basetag,
const char *name, u_int32_t base, u_int32_t size)
{
u_int32_t pa, endpa;
vaddr_t va;
if (basetag != NULL)
memcpy(t, basetag, sizeof(struct bus_space_tag_hpcmips));
strncpy(t->name, name, sizeof(t->name));
t->name[sizeof(t->name) - 1] = '\0';
t->base = base;
t->size = size;
if (t->base >= 0x20000000) {
pa = mips_trunc_page(t->base);
endpa = mips_round_page(t->base + t->size);
if (!(va = uvm_km_alloc(kernel_map, endpa - pa, 0,
UVM_KMF_VAONLY))) {
panic("hpcmips_init_bus_space_extent:"
"can't allocate kernel virtual");
}
DPRINTF(("pa:0x%08x -> kv:0x%08x+0x%08x",
(unsigned int)t->base, (unsigned int)va, t->size));
t->base = va;
for (; pa < endpa; pa += PAGE_SIZE, va += PAGE_SIZE) {
pmap_kenter_pa(va, pa, VM_PROT_READ | VM_PROT_WRITE, 0);
}
pmap_update(pmap_kernel());
}
t->extent = (void*)extent_create(t->name, t->base,
t->base + t->size,
0, 0, EX_NOWAIT);
if (!t->extent) {
panic("hpcmips_init_bus_space_extent:"
"unable to allocate %s map", t->name);
}
}
static bool
mips_pte_cachechange(struct pmap *pmap, vaddr_t sva, vaddr_t eva,
pt_entry_t *ptep, uintptr_t flags)
{
mips_dcache_wbinv_range(sva, eva - sva);
for (; sva < eva; sva += PAGE_SIZE) {
pt_entry_t pte = pte_cached_change(*ptep, flags);
*ptep = pte;
tlb_update_addr(sva, KERNEL_PID, pte, 0);
}
return false;
}
bus_space_handle_t
__hpcmips_cacheable(struct bus_space_tag_hpcmips *t, bus_addr_t bpa,
bus_size_t size, int cacheable)
{
if (t->base >= MIPS_KSEG2_START) {
const vaddr_t sva = mips_trunc_page(bpa);
const vaddr_t eva = mips_round_page(bpa + size);
pmap_pte_process(pmap_kernel(), sva, eva,
mips_pte_cachechange, cacheable);
return bpa;
}
return cacheable ? MIPS_PHYS_TO_KSEG0(bpa) : MIPS_PHYS_TO_KSEG1(bpa);
}
int
__bs_map(bus_space_tag_t tx, bus_addr_t bpa, bus_size_t size, int flags,
bus_space_handle_t *bshp)
{
struct bus_space_tag_hpcmips *t = (struct bus_space_tag_hpcmips *)tx;
int err;
int cacheable = flags & BUS_SPACE_MAP_CACHEABLE;
DPRINTF(("\tbus_space_map:%#lx(%#lx)+%#lx\n",
bpa, bpa + t->base, size));
if (!t->extent) {
DPRINTF(("bus_space_map: map temporary region:"
"0x%08lx-0x%08lx\n", bpa, bpa+size));
bpa += t->base;
} else {
bpa += t->base;
if ((err = extent_alloc_region(t->extent, bpa, size,
EX_NOWAIT|EX_MALLOCOK))) {
DPRINTF(("\tbus_space_map: "
"extent_alloc_region() failed\n"));
return (err);
}
}
*bshp = __hpcmips_cacheable(t, bpa, size, cacheable);
return (0);
}
void
__bs_unmap(bus_space_tag_t tx, bus_space_handle_t bsh, bus_size_t size)
{
struct bus_space_tag_hpcmips *t = (struct bus_space_tag_hpcmips *)tx;
int err;
u_int32_t addr;
if (!t->extent) {
return;
}
if (t->base < MIPS_KSEG2_START) {
addr = MIPS_KSEG1_TO_PHYS(bsh);
} else {
addr = bsh;
}
if ((err = extent_free(t->extent, addr, size, EX_NOWAIT))) {
DPRINTF(("warning: %#lx-%#lx of %s space lost\n",
bsh, bsh+size, t->name));
}
}
int
__bs_subregion(bus_space_tag_t t, bus_space_handle_t bsh,
bus_size_t offset, bus_size_t size, bus_space_handle_t *nbshp)
{
*nbshp = bsh + offset;
return (0);
}
int
__bs_alloc(bus_space_tag_t tx, bus_addr_t rstart, bus_addr_t rend,
bus_size_t size, bus_size_t alignment, bus_size_t boundary, int flags,
bus_addr_t *bpap, bus_space_handle_t *bshp)
{
struct bus_space_tag_hpcmips *t = (struct bus_space_tag_hpcmips *)tx;
int cacheable = flags & BUS_SPACE_MAP_CACHEABLE;
u_long bpa;
int err;
if (!t->extent)
panic("bus_space_alloc: no extent");
rstart += t->base;
rend += t->base;
if ((err = extent_alloc_subregion(t->extent, rstart, rend, size,
alignment, boundary, EX_FAST|EX_NOWAIT|EX_MALLOCOK, &bpa))) {
return (err);
}
DPRINTF(("\tbus_space_alloc:%#lx(%#lx)+%#lx\n", bpa,
bpa - t->base, size));
*bshp = __hpcmips_cacheable(t, bpa, size, cacheable);
if (bpap) {
*bpap = bpa;
}
return (0);
}
void
__bs_free(bus_space_tag_t t, bus_space_handle_t bsh, bus_size_t size)
{
bus_space_unmap(t, bsh, size);
}
void
__bs_barrier(bus_space_tag_t t, bus_space_handle_t bsh, bus_size_t offset,
bus_size_t len, int flags)
{
wbflush();
}
int
__bs_peek(bus_space_tag_t t, bus_space_handle_t bsh, bus_size_t offset,
size_t size, void *ptr)
{
u_int32_t tmp;
if (badaddr((void *)(bsh + offset), size))
return (-1);
if (ptr == NULL)
ptr = &tmp;
switch(size) {
case 1:
*((u_int8_t *)ptr) = bus_space_read_1(t, bsh, offset);
break;
case 2:
*((u_int16_t *)ptr) = bus_space_read_2(t, bsh, offset);
break;
case 4:
*((u_int32_t *)ptr) = bus_space_read_4(t, bsh, offset);
break;
default:
panic("bus_space_peek: bad size, %d", size);
}
return (0);
}
int
__bs_poke(bus_space_tag_t t, bus_space_handle_t bsh, bus_size_t offset,
size_t size, u_int32_t val)
{
if (badaddr((void *)(bsh + offset), size))
return (-1);
switch(size) {
case 1:
bus_space_write_1(t, bsh, offset, val);
break;
case 2:
bus_space_write_2(t, bsh, offset, val);
break;
case 4:
bus_space_write_4(t, bsh, offset, val);
break;
default:
panic("bus_space_poke: bad size, %d", size);
}
return (0);
}
u_int8_t
__bs_r_1(bus_space_tag_t t, bus_space_handle_t bsh, bus_size_t offset)
{
wbflush();
return (*(volatile u_int8_t *)(bsh + offset));
}
u_int16_t
__bs_r_2(bus_space_tag_t t, bus_space_handle_t bsh, bus_size_t offset)
{
wbflush();
return (*(volatile u_int16_t *)(bsh + offset));
}
u_int32_t
__bs_r_4(bus_space_tag_t t, bus_space_handle_t bsh, bus_size_t offset)
{
wbflush();
return (*(volatile u_int32_t *)(bsh + offset));
}
void
__bs_rm_1(bus_space_tag_t t, bus_space_handle_t bsh, bus_size_t offset,
u_int8_t *addr, bus_size_t count) {
while (count--)
*addr++ = bus_space_read_1(t, bsh, offset);
}
void
__bs_rm_2(bus_space_tag_t t, bus_space_handle_t bsh, bus_size_t offset,
u_int16_t *addr, bus_size_t count)
{
while (count--)
*addr++ = bus_space_read_2(t, bsh, offset);
}
void
__bs_rm_4(bus_space_tag_t t, bus_space_handle_t bsh, bus_size_t offset,
u_int32_t *addr, bus_size_t count)
{
while (count--)
*addr++ = bus_space_read_4(t, bsh, offset);
}
void
__bs_rr_1(bus_space_tag_t t, bus_space_handle_t bsh, bus_size_t offset,
u_int8_t *addr, bus_size_t count)
{
while (count--) {
*addr++ = bus_space_read_1(t, bsh, offset);
offset += sizeof(*addr);
}
}
void
__bs_rr_2(bus_space_tag_t t, bus_space_handle_t bsh, bus_size_t offset,
u_int16_t *addr, bus_size_t count)
{
while (count--) {
*addr++ = bus_space_read_2(t, bsh, offset);
offset += sizeof(*addr);
}
}
void
__bs_rr_4(bus_space_tag_t t, bus_space_handle_t bsh, bus_size_t offset,
u_int32_t *addr, bus_size_t count)
{
while (count--) {
*addr++ = bus_space_read_4(t, bsh, offset);
offset += sizeof(*addr);
}
}
void
__bs_w_1(bus_space_tag_t t, bus_space_handle_t bsh, bus_size_t offset,
u_int8_t value)
{
*(volatile u_int8_t *)(bsh + offset) = value;
wbflush();
}
void
__bs_w_2(bus_space_tag_t t, bus_space_handle_t bsh, bus_size_t offset,
u_int16_t value)
{
*(volatile u_int16_t *)(bsh + offset) = value;
wbflush();
}
void
__bs_w_4(bus_space_tag_t t, bus_space_handle_t bsh, bus_size_t offset,
u_int32_t value)
{
*(volatile u_int32_t *)(bsh + offset) = value;
wbflush();
}
void
__bs_wm_1(bus_space_tag_t t, bus_space_handle_t bsh, bus_size_t offset,
const u_int8_t *addr, bus_size_t count)
{
while (count--)
bus_space_write_1(t, bsh, offset, *addr++);
}
void
__bs_wm_2(bus_space_tag_t t, bus_space_handle_t bsh, bus_size_t offset,
const u_int16_t *addr, bus_size_t count)
{
while (count--)
bus_space_write_2(t, bsh, offset, *addr++);
}
void
__bs_wm_4(bus_space_tag_t t, bus_space_handle_t bsh, bus_size_t offset,
const u_int32_t *addr, bus_size_t count)
{
while (count--)
bus_space_write_4(t, bsh, offset, *addr++);
}
void
__bs_wr_1(bus_space_tag_t t, bus_space_handle_t bsh, bus_size_t offset,
const u_int8_t *addr, bus_size_t count)
{
while (count--) {
bus_space_write_1(t, bsh, offset, *addr++);
offset += sizeof(*addr);
}
}
void
__bs_wr_2(bus_space_tag_t t, bus_space_handle_t bsh, bus_size_t offset,
const u_int16_t *addr, bus_size_t count)
{
while (count--) {
bus_space_write_2(t, bsh, offset, *addr++);
offset += sizeof(*addr);
}
}
void
__bs_wr_4(bus_space_tag_t t, bus_space_handle_t bsh, bus_size_t offset,
const u_int32_t *addr, bus_size_t count)
{
while (count--) {
bus_space_write_4(t, bsh, offset, *addr++);
offset += sizeof(*addr);
}
}
void
__bs_sm_1(bus_space_tag_t t, bus_space_handle_t bsh,
bus_size_t offset, u_int8_t value, bus_size_t count)
{
while (count--)
bus_space_write_1(t, bsh, offset, value);
}
void
__bs_sm_2(bus_space_tag_t t, bus_space_handle_t bsh,
bus_size_t offset, u_int16_t value, bus_size_t count)
{
while (count--)
bus_space_write_2(t, bsh, offset, value);
}
void
__bs_sm_4(bus_space_tag_t t, bus_space_handle_t bsh,
bus_size_t offset, u_int32_t value, bus_size_t count)
{
while (count--)
bus_space_write_4(t, bsh, offset, value);
}
void
__bs_sr_1(bus_space_tag_t t, bus_space_handle_t bsh,
bus_size_t offset, u_int8_t value, bus_size_t count)
{
while (count--) {
bus_space_write_1(t, bsh, offset, value);
offset += (value);
}
}
void
__bs_sr_2(bus_space_tag_t t, bus_space_handle_t bsh,
bus_size_t offset, u_int16_t value, bus_size_t count)
{
while (count--) {
bus_space_write_2(t, bsh, offset, value);
offset += (value);
}
}
void
__bs_sr_4(bus_space_tag_t t, bus_space_handle_t bsh,
bus_size_t offset, u_int32_t value, bus_size_t count)
{
while (count--) {
bus_space_write_4(t, bsh, offset, value);
offset += (value);
}
}
#define __bs_c_n(n) \
void __CONCAT(__bs_c_,n)(bus_space_tag_t t, bus_space_handle_t h1, \
bus_size_t o1, bus_space_handle_t h2, bus_size_t o2, bus_size_t c) \
{ \
bus_size_t o; \
\
if ((h1 + o1) >= (h2 + o2)) { \
\
for (o = 0; c != 0; c--, o += n) \
__CONCAT(bus_space_write_,n)(t, h2, o2 + o, \
__CONCAT(bus_space_read_,n)(t, h1, o1 + o));\
} else { \
\
for (o = (c - 1) * n; c != 0; c--, o -= n) \
__CONCAT(bus_space_write_,n)(t, h2, o2 + o, \
__CONCAT(bus_space_read_,n)(t, h1, o1 + o));\
} \
}
__bs_c_n(1)
__bs_c_n(2)
__bs_c_n(4)