#pragma ident "%Z%%M% %I% %E% SMI"
#include <sys/types.h>
#include <sys/iommutsb.h>
#include <sys/systm.h>
#include <sys/sysmacros.h>
#include <sys/debug.h>
#include <sys/bootconf.h>
#include <sys/mutex.h>
#include <sys/platform_module.h>
#include <sys/cmn_err.h>
static uint_t
resolve_tsb_size(pgcnt_t phys_mem_size)
{
if (phys_mem_size <= 0x1000)
return (0x10000);
else if (phys_mem_size <= 0x4000)
return (0x40000);
else if (phys_mem_size <= 0x10000)
return (0x80000);
else
return (0x100000);
}
#define MIN_TSB_BYTES 0x2000
#define MAX_TSB_BYTES 0x100000
#define MAX_IOMMU_PER_AGENT 2
#define MAX_TSB_ALLOC (MAX_UPA * MAX_IOMMU_PER_AGENT)
static kmutex_t iommu_tsb_avail_lock;
static uint16_t iommu_tsb_avail[MAX_TSB_ALLOC];
#define IOMMU_TSB_INUSE 0x8000u
static uint_t iommu_tsb_num;
#ifdef DEBUG
static uint_t iommu_tsb_nfree;
#endif
static caddr_t iommu_tsb_base;
static uint_t iommu_tsb_size;
uint_t niommu_tsbs;
uint_t iommu_tsb_size_min = MIN_TSB_BYTES;
uint_t iommu_tsb_size_max = MAX_TSB_BYTES;
caddr_t
iommu_tsb_init(caddr_t alloc_base)
{
size_t total_size;
caddr_t base = (caddr_t)roundup((uintptr_t)alloc_base, MMU_PAGESIZE);
uint_t tsb_min, tsb_max;
uint_t tsb_size;
uint_t ntsbs;
tsb_size = resolve_tsb_size(physinstalled);
tsb_min = MAX(iommu_tsb_size_min, MIN_TSB_BYTES);
tsb_max = MIN(iommu_tsb_size_max, MAX_TSB_BYTES);
if (tsb_min <= tsb_max) {
uint_t sz;
for (sz = MAX_TSB_BYTES; !(sz & tsb_min); sz >>= 1)
;
tsb_min = sz;
for (sz = MAX_TSB_BYTES; !(sz & tsb_max); sz >>= 1)
;
tsb_max = sz;
tsb_size = MIN(tsb_size, tsb_max);
tsb_size = MAX(tsb_size, tsb_min);
} else
cmn_err(CE_WARN,
"iommutsb: bad iommu_tsb_size_min/max value pair");
iommu_tsb_size = tsb_size;
if (&set_platform_tsb_spares)
ntsbs = set_platform_tsb_spares();
else
ntsbs = 0;
ntsbs = MAX(ntsbs, niommu_tsbs);
ntsbs = MIN(ntsbs, MAX_TSB_ALLOC);
total_size = ntsbs * tsb_size;
if (total_size == 0)
return (alloc_base);
if ((iommu_tsb_base = (caddr_t)BOP_ALLOC(bootops, base,
total_size, MMU_PAGESIZE)) == NULL)
cmn_err(CE_PANIC, "Cannot allocate IOMMU TSB arrays");
ASSERT(iommu_tsb_base == base);
iommu_tsb_num = ntsbs;
#ifdef DEBUG
iommu_tsb_nfree = iommu_tsb_num;
#endif
return (base + total_size);
}
uint16_t
iommu_tsb_alloc(uint16_t id)
{
uint16_t tsbc;
uint_t i;
tsbc = IOMMU_TSB_COOKIE_NONE;
mutex_enter(&iommu_tsb_avail_lock);
for (i = 0; i < iommu_tsb_num; i++) {
if (iommu_tsb_avail[i] == 0) {
iommu_tsb_avail[i] = IOMMU_TSB_INUSE | id;
tsbc = (uint16_t)i;
#ifdef DEBUG
ASSERT(iommu_tsb_nfree != 0);
iommu_tsb_nfree--;
#endif
break;
}
}
mutex_exit(&iommu_tsb_avail_lock);
return (tsbc);
}
void
iommu_tsb_free(uint16_t tsbc)
{
ASSERT(tsbc != IOMMU_TSB_COOKIE_NONE);
ASSERT(tsbc < iommu_tsb_num);
mutex_enter(&iommu_tsb_avail_lock);
if (iommu_tsb_avail[tsbc] == 0) {
cmn_err(CE_PANIC, "iommu_tsb_free(%d): tsb not in use", tsbc);
}
iommu_tsb_avail[tsbc] = 0;
#ifdef DEBUG
ASSERT(iommu_tsb_nfree < iommu_tsb_num);
iommu_tsb_nfree++;
#endif
mutex_exit(&iommu_tsb_avail_lock);
}
uint_t
iommu_tsb_cookie_to_size(uint16_t tsbc)
{
ASSERT(tsbc != IOMMU_TSB_COOKIE_NONE);
ASSERT(tsbc < iommu_tsb_num);
ASSERT(iommu_tsb_avail[tsbc] != 0);
return (iommu_tsb_size);
}
uint64_t *
iommu_tsb_cookie_to_va(uint16_t tsbc)
{
ASSERT(tsbc != IOMMU_TSB_COOKIE_NONE);
ASSERT(tsbc < iommu_tsb_num);
ASSERT(iommu_tsb_avail[tsbc] != 0);
return ((uint64_t *)(iommu_tsb_base + (tsbc * iommu_tsb_size)));
}