#include <sys/param.h>
#include <sys/systm.h>
#include <sys/malloc.h>
#include <sys/extent.h>
#include <uvm/uvm_extern.h>
#include <machine/bus.h>
extern int ppc_malloc_ok;
extern struct extent *devio_ex;
int bus_mem_add_mapping(bus_addr_t, bus_size_t, int, bus_space_handle_t *);
bus_addr_t bus_space_unmap_p(bus_space_tag_t, bus_space_handle_t, bus_size_t);
int
bus_space_map(bus_space_tag_t t, bus_addr_t bpa, bus_size_t size,
int flags, bus_space_handle_t *bshp)
{
int error;
if (POWERPC_BUS_TAG_BASE(t) == 0) {
return EINVAL;
}
if (bpa < POWERPC_BUS_TAG_BASE(t))
bpa += POWERPC_BUS_TAG_BASE(t);
if ((error = extent_alloc_region(devio_ex, bpa, size, EX_NOWAIT |
(ppc_malloc_ok ? EX_MALLOCOK : 0))))
return error;
if ((error = bus_mem_add_mapping(bpa, size, flags, bshp))) {
if (extent_free(devio_ex, bpa, size, EX_NOWAIT |
(ppc_malloc_ok ? EX_MALLOCOK : 0)))
{
printf("bus_space_map: pa 0x%lx, size 0x%lx\n",
bpa, size);
printf("bus_space_map: can't free region\n");
}
}
return error;
}
bus_addr_t
bus_space_unmap_p(bus_space_tag_t t, bus_space_handle_t bsh, bus_size_t size)
{
bus_addr_t paddr;
pmap_extract(pmap_kernel(), bsh, &paddr);
bus_space_unmap((t), (bsh), (size));
return paddr ;
}
void
bus_space_unmap(bus_space_tag_t t, bus_space_handle_t bsh, bus_size_t size)
{
bus_addr_t sva;
bus_size_t off, len;
bus_addr_t bpa;
sva = trunc_page(bsh);
off = bsh - sva;
len = round_page(size+off);
if (pmap_extract(pmap_kernel(), sva, &bpa) == TRUE) {
if (extent_free(devio_ex, bpa | (bsh & PAGE_MASK), size,
EX_NOWAIT | (ppc_malloc_ok ? EX_MALLOCOK : 0)))
{
printf("bus_space_map: pa 0x%lx, size 0x%lx\n",
bpa, size);
printf("bus_space_map: can't free region\n");
}
}
pmap_kremove(sva, len);
pmap_update(pmap_kernel());
if (ppc_malloc_ok &&
((sva >= VM_MIN_KERNEL_ADDRESS) && (sva < VM_MAX_KERNEL_ADDRESS)))
km_free((void *)sva, len, &kv_any, &kp_none);
}
paddr_t
bus_space_mmap(bus_space_tag_t t, bus_addr_t bpa, off_t off, int prot,
int flags)
{
int pmapflags = PMAP_NOCACHE;
if (POWERPC_BUS_TAG_BASE(t) == 0)
return (-1);
if (bpa < POWERPC_BUS_TAG_BASE(t))
bpa += POWERPC_BUS_TAG_BASE(t);
if (flags & BUS_SPACE_MAP_CACHEABLE)
pmapflags &= ~PMAP_NOCACHE;
return ((bpa + off) | pmapflags);
}
vaddr_t ppc_kvm_stolen = VM_KERN_ADDRESS_SIZE;
int
bus_mem_add_mapping(bus_addr_t bpa, bus_size_t size, int flags,
bus_space_handle_t *bshp)
{
bus_addr_t vaddr;
bus_addr_t spa, epa;
bus_size_t off, len;
int pmapflags;
spa = trunc_page(bpa);
epa = bpa + size;
off = bpa - spa;
len = round_page(size+off);
#ifdef DIAGNOSTIC
if (epa <= spa && epa != 0)
panic("bus_mem_add_mapping: overflow");
#endif
if (ppc_malloc_ok == 0) {
vaddr = VM_MIN_KERNEL_ADDRESS + ppc_kvm_stolen;
ppc_kvm_stolen += len;
if (ppc_kvm_stolen > PPC_SEGMENT_LENGTH) {
panic("ppc_kvm_stolen, out of space");
}
} else {
vaddr = (vaddr_t)km_alloc(len, &kv_any, &kp_none, &kd_nowait);
if (vaddr == 0)
return (ENOMEM);
}
*bshp = vaddr + off;
if (flags & BUS_SPACE_MAP_CACHEABLE)
pmapflags = PMAP_WT;
else
pmapflags = PMAP_NOCACHE;
for (; len > 0; len -= PAGE_SIZE) {
pmap_kenter_pa(vaddr, spa | pmapflags, PROT_READ | PROT_WRITE);
spa += PAGE_SIZE;
vaddr += PAGE_SIZE;
}
pmap_update(pmap_kernel());
return (0);
}
int
bus_space_alloc(bus_space_tag_t tag, 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 *addrp, bus_space_handle_t *handlep)
{
panic("bus_space_alloc: unimplemented");
}
void
bus_space_free(bus_space_tag_t tag, bus_space_handle_t handle, bus_size_t size)
{
panic("bus_space_free: unimplemented");
}
void *
mapiodev(paddr_t pa, psize_t len)
{
paddr_t spa;
vaddr_t vaddr, va;
int off;
int size;
spa = trunc_page(pa);
off = pa - spa;
size = round_page(off+len);
if (ppc_malloc_ok == 0) {
va = VM_MIN_KERNEL_ADDRESS + ppc_kvm_stolen;
ppc_kvm_stolen += size;
if (ppc_kvm_stolen > PPC_SEGMENT_LENGTH) {
panic("ppc_kvm_stolen, out of space");
}
} else {
va = (vaddr_t)km_alloc(size, &kv_any, &kp_none, &kd_nowait);
if (va == 0)
return (NULL);
}
for (vaddr = va; size > 0; size -= PAGE_SIZE) {
pmap_kenter_pa(vaddr, spa, PROT_READ | PROT_WRITE);
spa += PAGE_SIZE;
vaddr += PAGE_SIZE;
}
pmap_update(pmap_kernel());
return (void *) (va+off);
}
void
unmapiodev(void *kva, psize_t p_size)
{
vaddr_t vaddr;
int size;
size = round_page(p_size);
vaddr = trunc_page((vaddr_t)kva);
pmap_kremove(vaddr, size);
pmap_update(pmap_kernel());
km_free((void *)vaddr, size, &kv_any, &kp_none);
}
#define _CONCAT(A,B) A ## B
#define __C(A,B) _CONCAT(A,B)
#define BUS_SPACE_COPY_N(BYTES,TYPE) \
void \
__C(bus_space_copy_,BYTES)(void *v, bus_space_handle_t h1, \
bus_size_t o1, bus_space_handle_t h2, bus_size_t o2, \
bus_size_t c) \
{ \
TYPE *src, *dst; \
int i; \
\
src = (TYPE *) (h1+o1); \
dst = (TYPE *) (h2+o2); \
\
if (h1 == h2 && o2 > o1) \
for (i = c-1; i >= 0; i--) \
dst[i] = src[i]; \
else \
for (i = 0; i < c; i++) \
dst[i] = src[i]; \
}
BUS_SPACE_COPY_N(1,u_int8_t)
BUS_SPACE_COPY_N(2,u_int16_t)
BUS_SPACE_COPY_N(4,u_int32_t)
void
bus_space_set_region_1(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o,
u_int8_t val, bus_size_t c)
{
u_int8_t *dst;
int i;
dst = (u_int8_t *) (h+o);
for (i = 0; i < c; i++)
dst[i] = val;
}
void
bus_space_set_region_2(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o,
u_int16_t val, bus_size_t c)
{
u_int16_t *dst;
int i;
dst = (u_int16_t *) (h+o);
val = swap16(val);
for (i = 0; i < c; i++)
dst[i] = val;
}
void
bus_space_set_region_4(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o,
u_int32_t val, bus_size_t c)
{
u_int32_t *dst;
int i;
dst = (u_int32_t *) (h+o);
val = swap32(val);
for (i = 0; i < c; i++)
dst[i] = val;
}
#define BUS_SPACE_READ_RAW_MULTI_N(BYTES,SHIFT,TYPE) \
void \
__C(bus_space_read_raw_multi_,BYTES)(bus_space_tag_t bst, \
bus_space_handle_t h, bus_addr_t o, u_int8_t *dst, bus_size_t size) \
{ \
TYPE *src; \
TYPE *rdst = (TYPE *)dst; \
int i; \
int count = size >> SHIFT; \
\
src = (TYPE *)(h+o); \
for (i = 0; i < count; i++) { \
rdst[i] = *src; \
__asm__("eieio"); \
} \
}
BUS_SPACE_READ_RAW_MULTI_N(2,1,u_int16_t)
BUS_SPACE_READ_RAW_MULTI_N(4,2,u_int32_t)
#define BUS_SPACE_WRITE_RAW_MULTI_N(BYTES,SHIFT,TYPE) \
void \
__C(bus_space_write_raw_multi_,BYTES)( bus_space_tag_t bst, \
bus_space_handle_t h, bus_addr_t o, const u_int8_t *src, \
bus_size_t size) \
{ \
int i; \
TYPE *dst; \
TYPE *rsrc = (TYPE *)src; \
int count = size >> SHIFT; \
\
dst = (TYPE *)(h+o); \
for (i = 0; i < count; i++) { \
*dst = rsrc[i]; \
__asm__("eieio"); \
} \
}
BUS_SPACE_WRITE_RAW_MULTI_N(2,1,u_int16_t)
BUS_SPACE_WRITE_RAW_MULTI_N(4,2,u_int32_t)
int
bus_space_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);
}