#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: dvma.c,v 1.43 2023/12/20 05:18:00 thorpej Exp $");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/proc.h>
#include <sys/vmem.h>
#include <sys/buf.h>
#include <sys/vnode.h>
#include <sys/core.h>
#include <sys/exec.h>
#include <uvm/uvm.h>
#define _SUN68K_BUS_DMA_PRIVATE
#include <machine/autoconf.h>
#include <machine/bus.h>
#include <machine/cpu.h>
#include <machine/dvma.h>
#include <machine/pmap.h>
#include <machine/pte.h>
#include <sun3/sun3/control.h>
#include <sun3/sun3/machdep.h>
#define DVMA_MAP_END (DVMA_MAP_BASE + DVMA_MAP_AVAIL)
vmem_t *dvma_arena;
vsize_t dvma_segmap_size = 6 * NBSG;
void
dvma_init(void)
{
vaddr_t segmap_addr;
phys_map = kmem_alloc(sizeof(struct vm_map), KM_SLEEP);
uvm_map_setup(phys_map, DVMA_MAP_BASE, DVMA_MAP_END, 0);
phys_map->pmap = pmap_kernel();
segmap_addr = uvm_km_alloc(phys_map, dvma_segmap_size, 0,
UVM_KMF_VAONLY | UVM_KMF_WAITVA);
if (segmap_addr != DVMA_MAP_BASE)
panic("dvma_init: unable to allocate DVMA segments");
dvma_arena = vmem_create("dvma", segmap_addr, dvma_segmap_size,
PAGE_SIZE,
NULL,
NULL,
NULL,
0,
VM_SLEEP,
IPL_VM);
}
void *
dvma_malloc(size_t bytes)
{
void *new_mem;
vsize_t new_size;
if (bytes == 0)
return NULL;
new_size = m68k_round_page(bytes);
new_mem = (void *)uvm_km_alloc(phys_map, new_size, 0, UVM_KMF_WIRED);
if (new_mem == 0)
panic("dvma_malloc: no space in phys_map");
return new_mem;
}
void
dvma_free(void *addr, size_t size)
{
vsize_t sz = m68k_round_page(size);
uvm_km_free(phys_map, (vaddr_t)addr, sz, UVM_KMF_WIRED);
}
u_long
dvma_kvtopa(void *kva, int bustype)
{
u_long addr, mask;
addr = (u_long)kva;
if ((addr & DVMA_MAP_BASE) != DVMA_MAP_BASE)
panic("dvma_kvtopa: bad dmva addr=0x%lx", addr);
switch (bustype) {
case BUS_OBIO:
case BUS_OBMEM:
mask = DVMA_OBIO_SLAVE_MASK;
break;
default:
mask = DVMA_VME_SLAVE_MASK;
break;
}
return addr & mask;
}
void *
dvma_mapin(void *kva, int len, int canwait )
{
vaddr_t seg_kva;
vsize_t seg_len, seg_off;
vaddr_t v, x;
int s, sme, error;
vmem_addr_t seg_dma;
seg_kva = (vaddr_t)kva;
seg_len = (vsize_t)len;
seg_off = seg_kva & SEGOFSET;
seg_kva -= seg_off;
seg_len = sun3_round_seg(seg_len + seg_off);
const vm_flag_t vmflags = VM_INSTANTFIT |
( VM_NOSLEEP);
error = vmem_xalloc(dvma_arena, seg_len,
NBSG,
0,
0,
VMEM_ADDR_MIN,
VMEM_ADDR_MAX,
vmflags,
&seg_dma);
if (error)
return NULL;
KASSERT((seg_dma & SEGOFSET) == 0);
s = splvm();
v = seg_kva;
x = seg_dma;
while (seg_len > 0) {
sme = get_segmap(v);
KASSERT(sme != SEGINV);
#ifdef HAVECACHE
if (cache_size)
cache_flush_segment(v);
#endif
set_segmap_allctx(x, sme);
v += NBSG;
x += NBSG;
seg_len -= NBSG;
}
seg_dma += seg_off;
splx(s);
return (void *)seg_dma;
}
void
dvma_mapout(void *dma, int len)
{
vaddr_t seg_dma;
vsize_t seg_len, seg_off;
vaddr_t v, x;
int sme __diagused;
int s;
seg_dma = (vaddr_t)dma;
seg_len = (vsize_t)len;
seg_off = seg_dma & SEGOFSET;
seg_dma -= seg_off;
seg_len = sun3_round_seg(seg_len + seg_off);
s = splvm();
v = seg_dma;
x = v + seg_len;
while (v < x) {
sme = get_segmap(v);
KASSERT(sme != SEGINV);
#ifdef HAVECACHE
if (cache_size)
cache_flush_segment(v);
#endif
set_segmap_allctx(v, SEGINV);
v += NBSG;
}
splx(s);
vmem_xfree(dvma_arena, seg_dma, seg_len);
}
int
_bus_dmamap_load_raw(bus_dma_tag_t t, bus_dmamap_t map, bus_dma_segment_t *segs,
int nsegs, bus_size_t size, int flags)
{
panic("_bus_dmamap_load_raw(): not implemented yet.");
}
int
_bus_dmamap_load(bus_dma_tag_t t, bus_dmamap_t map, void *buf,
bus_size_t buflen, struct proc *p, int flags)
{
vaddr_t kva;
vmem_addr_t dva;
vsize_t off, sgsize;
paddr_t pa;
pmap_t pmap;
int error, rv __diagused;
map->dm_nsegs = 0;
map->dm_mapsize = 0;
if (buflen > map->_dm_size)
return EINVAL;
kva = (vaddr_t)buf;
off = kva & PGOFSET;
sgsize = round_page(off + buflen);
const vm_flag_t vmflags = VM_INSTANTFIT |
((flags & BUS_DMA_NOWAIT) ? VM_NOSLEEP : VM_SLEEP);
error = vmem_xalloc(dvma_arena, sgsize,
0,
0,
0,
VMEM_ADDR_MIN,
VMEM_ADDR_MAX,
vmflags,
&dva);
if (error)
return ENOMEM;
map->dm_segs[0].ds_addr = dva + off;
map->dm_segs[0].ds_len = buflen;
map->dm_segs[0]._ds_va = dva;
map->dm_segs[0]._ds_sgsize = sgsize;
if (p != NULL)
pmap = p->p_vmspace->vm_map.pmap;
else
pmap = pmap_kernel();
while (sgsize > 0) {
rv = pmap_extract(pmap, kva, &pa);
#ifdef DIAGNOSTIC
if (rv == false)
panic("%s: unmapped VA", __func__);
#endif
pmap_enter(pmap_kernel(), dva, pa | PMAP_NC,
VM_PROT_READ | VM_PROT_WRITE, PMAP_WIRED);
kva += PAGE_SIZE;
dva += PAGE_SIZE;
sgsize -= PAGE_SIZE;
}
map->dm_nsegs = 1;
map->dm_mapsize = map->dm_segs[0].ds_len;
return 0;
}
void
_bus_dmamap_unload(bus_dma_tag_t t, bus_dmamap_t map)
{
bus_dma_segment_t *segs;
vaddr_t dva;
vsize_t sgsize;
#ifdef DIAGNOSTIC
if (map->dm_nsegs != 1)
panic("%s: invalid nsegs = %d", __func__, map->dm_nsegs);
#endif
segs = map->dm_segs;
dva = segs[0]._ds_va & ~PGOFSET;
sgsize = segs[0]._ds_sgsize;
pmap_remove(pmap_kernel(), dva, dva + sgsize);
pmap_update(pmap_kernel());
vmem_xfree(dvma_arena, dva, sgsize);
map->dm_mapsize = 0;
map->dm_nsegs = 0;
}