#include <sys/types.h>
#include <sys/conf.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/ddi_impldefs.h>
#include <sys/cmn_err.h>
#include <vm/hat_sfmmu.h>
#include <sys/iommu.h>
#include <sys/iocache.h>
#include <sys/sysiosbus.h>
#include <sys/nexusdebug.h>
#include <sys/debug.h>
#define IOCACHE_REGISTERS_DEBUG 0x1
#define IOCACHE_SYNC_DEBUG 0x2
#define IOCACHE_DIAG_REG_DEBUG 0x4
#define IOCACHE_SYNC_FAIL_DEBUG 0x8
#define MAX_RETRY 10
int stream_buf_on = 1;
int stream_buf_sync_using_diag = 36;
int
stream_buf_init(struct sbus_soft_state *softsp, caddr_t address)
{
uchar_t version;
#ifdef DEBUG
debug_info = 1;
debug_print_level = 0;
#endif
version = (uchar_t)(*softsp->sysio_ctrl_reg >> SYSIO_VER_SHIFT);
version &= 0xf;
if (stream_buf_on == 0 || version == 0) {
softsp->stream_buf_off = STREAM_BUF_OFF;
if (version == 0)
cmn_err(CE_CONT, "Disabling streaming buffer due to "
"SYSIO Rev %d.\n", version);
return (DDI_SUCCESS);
}
#define REG_ADDR(b, o) (uint64_t *)((caddr_t)(b) + (o))
softsp->str_buf_ctrl_reg = REG_ADDR(address, OFF_STR_BUF_CTRL_REG);
softsp->str_buf_flush_reg = REG_ADDR(address, OFF_STR_BUF_FLUSH_REG);
softsp->str_buf_sync_reg = REG_ADDR(address, OFF_STR_BUF_SYNC_REG);
softsp->str_buf_pg_tag_diag = REG_ADDR(address, STR_BUF_PAGE_TAG_DIAG);
#undef REG_ADDR
DPRINTF(IOCACHE_REGISTERS_DEBUG, ("Streaming buffer control reg: 0x%p, "
"Streaming buffer flush reg: 0x%p, Streaming buffer sync reg: 0x%p",
(void *)softsp->str_buf_ctrl_reg, (void *)softsp->str_buf_flush_reg,
(void *)softsp->str_buf_sync_reg));
mutex_init(&softsp->sync_reg_lock, NULL, MUTEX_DEFAULT, NULL);
softsp->stream_buf_off = 0;
(void) stream_buf_resume_init(softsp);
return (DDI_SUCCESS);
}
int
stream_buf_uninit(struct sbus_soft_state *softsp)
{
softsp->stream_buf_off = 1;
*softsp->str_buf_ctrl_reg = STREAM_BUF_DISABLE;
return (DDI_SUCCESS);
}
int
stream_buf_resume_init(struct sbus_soft_state *softsp)
{
uchar_t version;
version = (uchar_t)(*softsp->sysio_ctrl_reg >> SYSIO_VER_SHIFT);
version &= 0xf;
if (stream_buf_on == 0 || version == 0) {
softsp->stream_buf_off = STREAM_BUF_OFF;
return (DDI_SUCCESS);
}
*softsp->str_buf_ctrl_reg = STREAM_BUF_ENABLE;
return (DDI_SUCCESS);
}
#define SCACHE_NSEC_WAIT (10ull * NANOSEC)
#define SCACHE_SPIN 10000000
void
sync_stream_buf(struct sbus_soft_state *softsp, ioaddr_t addr, uint_t npages,
int *sync_flag, uint64_t phys_sync_flag)
{
#ifndef lint
volatile uint64_t tmp;
#endif
int cntr = 0;
if (softsp->stream_buf_off != 0)
return;
DPRINTF(IOCACHE_SYNC_DEBUG, ("sync_stream_buf: ioaddr 0x%x, page cnt "
"0x%x, sync flag 0x%p, sync flag pf 0x%lx\n", addr, npages,
(void *)sync_flag, phys_sync_flag));
ASSERT(npages > (uint_t)0);
mutex_enter(&softsp->sync_reg_lock);
*sync_flag = 0;
if (npages > stream_buf_sync_using_diag) {
int i;
volatile uint64_t *reg_addr;
uint64_t reg;
uint_t ioaddr;
uint_t hiaddr = addr + (npages * IOMMU_PAGESIZE);
int do_sync = 0;
for (i = 0, reg_addr = softsp->str_buf_pg_tag_diag;
i < STREAM_CACHE_LINES; i++, reg_addr++) {
reg = *reg_addr;
#ifdef DEBUG
{
uint_t hi, lo;
hi = (uint_t)(reg >> 32);
lo = (uint_t)(reg & 0xffffffff);
DPRINTF(IOCACHE_DIAG_REG_DEBUG,
("IO cache line diag "
"reg addr 0x%p, hi0x%x lo0x%x\n",
(void *)reg_addr, hi, lo));
}
#endif
if (reg & STR_PG_VALID) {
ioaddr = (uint_t)reg << STR_PG_SHIFT;
DPRINTF(IOCACHE_DIAG_REG_DEBUG, ("ioaddr 0x%x, "
"range base 0x%x, range extent 0x%x\n",
ioaddr, addr,
addr + (npages * IOMMU_PAGESIZE)));
if (ioaddr >= addr && ioaddr <= hiaddr) {
*softsp->str_buf_flush_reg = (uint64_t)
ioaddr;
do_sync = 1;
}
}
}
if (!do_sync) {
mutex_exit(&softsp->sync_reg_lock);
return;
}
} else {
do {
*softsp->str_buf_flush_reg = (uint64_t)addr;
addr += IOMMU_PAGESIZE;
npages--;
} while (npages > (uint_t)0);
}
*softsp->str_buf_sync_reg = phys_sync_flag;
#ifndef lint
tmp = *softsp->sbus_ctrl_reg;
#endif
while (!*((volatile int *)sync_flag)) {
if (cntr++ == SCACHE_SPIN) {
hrtime_t nsec_start, nsectowait, nsec_current;
nsectowait = SCACHE_NSEC_WAIT;
nsec_current = nsec_start = gethrtime();
while (!*((volatile int *)sync_flag)) {
nsec_current = gethrtime();
if ((nsec_current - nsec_start) > nsectowait &&
!*((volatile int *)sync_flag)) {
panic("streaming buffer timed out");
}
}
}
}
mutex_exit(&softsp->sync_reg_lock);
}