#pragma ident "%Z%%M% %I% %E% SMI"
#include <sys/sysmacros.h>
#include <sys/machsystm.h>
#include <sys/cpuvar.h>
#include <sys/ddi_implfuncs.h>
#include <px_csr.h>
#include <px_regs.h>
#include <px_obj.h>
#include <sys/pci_tools.h>
#include <px_tools_var.h>
#include <px_asm_4u.h>
#include <px_lib4u.h>
#include <px_tools_ext.h>
int pxtool_delay_usec = 10000;
int pxtool_num_inos = INTERRUPT_MAPPING_ENTRIES;
typedef union {
uint64_t u64;
uint32_t u32;
uint16_t u16;
uint8_t u8;
} peek_poke_value_t;
static int
pxtool_safe_phys_peek(px_t *px_p, boolean_t type, size_t size, uint64_t paddr,
uint64_t *value_p)
{
px_pec_t *pec_p = px_p->px_pec_p;
pxu_t *pxu_p = (pxu_t *)px_p->px_plat_p;
on_trap_data_t otd;
peek_poke_value_t peek_value;
int err = DDI_SUCCESS;
mutex_enter(&pec_p->pec_pokefault_mutex);
pec_p->pec_safeacc_type = DDI_FM_ERR_PEEK;
pxu_p->pcitool_addr = (caddr_t)(paddr & px_paddr_mask);
if (!on_trap(&otd, OT_DATA_ACCESS)) {
otd.ot_trampoline = (uintptr_t)&peek_fault;
err = px_phys_peek_4u(size, paddr, &peek_value.u64, type);
} else
err = DDI_FAILURE;
no_trap();
if ((err == DDI_FAILURE) && (pxtool_delay_usec > 0))
delay(drv_usectohz(pxtool_delay_usec));
pec_p->pec_safeacc_type = DDI_FM_ERR_UNEXPECTED;
pxu_p->pcitool_addr = NULL;
mutex_exit(&pec_p->pec_pokefault_mutex);
if (err != DDI_FAILURE) {
switch (size) {
case 8:
*value_p = peek_value.u64;
break;
case 4:
*value_p = (uint64_t)peek_value.u32;
break;
case 2:
*value_p = (uint64_t)peek_value.u16;
break;
case 1:
*value_p = (uint64_t)peek_value.u8;
break;
default:
err = DDI_FAILURE;
}
}
return (err);
}
static int
pxtool_safe_phys_poke(px_t *px_p, boolean_t type, size_t size, uint64_t paddr,
uint64_t value)
{
on_trap_data_t otd;
pxu_t *pxu_p = (pxu_t *)px_p->px_plat_p;
px_pec_t *pec_p = px_p->px_pec_p;
peek_poke_value_t poke_value;
int err = DDI_SUCCESS;
switch (size) {
case 8:
poke_value.u64 = value;
break;
case 4:
poke_value.u32 = (uint32_t)value;
break;
case 2:
poke_value.u16 = (uint16_t)value;
break;
case 1:
poke_value.u8 = (uint8_t)value;
break;
default:
return (DDI_FAILURE);
}
mutex_enter(&pec_p->pec_pokefault_mutex);
pec_p->pec_ontrap_data = &otd;
pec_p->pec_safeacc_type = DDI_FM_ERR_POKE;
pxu_p->pcitool_addr = (caddr_t)(paddr & px_paddr_mask);
if (!on_trap(&otd, OT_DATA_ACCESS)) {
otd.ot_trampoline = (uintptr_t)&poke_fault;
err = px_phys_poke_4u(size, paddr, &poke_value.u64, type);
} else
err = DDI_FAILURE;
px_lib_clr_errs(px_p, 0, paddr);
if (otd.ot_trap & OT_DATA_ACCESS)
err = DDI_FAILURE;
no_trap();
pec_p->pec_ontrap_data = NULL;
if (pxtool_delay_usec > 0)
delay(drv_usectohz(pxtool_delay_usec));
pec_p->pec_safeacc_type = DDI_FM_ERR_UNEXPECTED;
pxu_p->pcitool_addr = NULL;
mutex_exit(&pec_p->pec_pokefault_mutex);
return (err);
}
static int
pxtool_access(px_t *px_p, pcitool_reg_t *prg_p, uint64_t *data_p,
boolean_t is_write)
{
dev_info_t *dip = px_p->px_dip;
uint64_t phys_addr = prg_p->phys_addr;
boolean_t endian = PCITOOL_ACC_IS_BIG_ENDIAN(prg_p->acc_attr);
size_t size = PCITOOL_ACC_ATTR_SIZE(prg_p->acc_attr);
int rval = SUCCESS;
if (!IS_P2ALIGNED(phys_addr, size)) {
DBG(DBG_TOOLS, dip, "not aligned.\n");
prg_p->status = PCITOOL_NOT_ALIGNED;
rval = EINVAL;
} else if (is_write) {
DBG(DBG_PHYS_ACC, dip,
"%d byte %s pxtool_safe_phys_poke at addr 0x%" PRIx64 "\n",
size, (endian ? "BE" : "LE"), phys_addr);
if (pxtool_safe_phys_poke(px_p, endian, size, phys_addr,
*data_p) != DDI_SUCCESS) {
DBG(DBG_PHYS_ACC, dip,
"%d byte %s pxtool_safe_phys_poke at addr "
"0x%" PRIx64 " failed\n",
size, (endian ? "BE" : "LE"), phys_addr);
prg_p->status = PCITOOL_INVALID_ADDRESS;
rval = EFAULT;
}
} else {
DBG(DBG_PHYS_ACC, dip,
"%d byte %s pxtool_safe_phys_peek at addr 0x%" PRIx64 "\n",
size, (endian ? "BE" : "LE"), phys_addr);
if (pxtool_safe_phys_peek(px_p, endian, size, phys_addr,
data_p) != DDI_SUCCESS) {
DBG(DBG_PHYS_ACC, dip,
"%d byte %s pxtool_safe_phys_peek at addr "
"0x%" PRIx64 " failed\n",
size, (endian ? "BE" : "LE"), phys_addr);
prg_p->status = PCITOOL_INVALID_ADDRESS;
rval = EFAULT;
}
}
return (rval);
}
int
pxtool_pcicfg_access(px_t *px_p, pcitool_reg_t *prg_p,
uint64_t *data_p, boolean_t is_write)
{
return (pxtool_access(px_p, prg_p, data_p, is_write));
}
int
pxtool_pciiomem_access(px_t *px_p, pcitool_reg_t *prg_p,
uint64_t *data_p, boolean_t is_write)
{
return (pxtool_access(px_p, prg_p, data_p, is_write));
}
int
pxtool_dev_reg_ops_platchk(dev_info_t *dip, pcitool_reg_t *prg_p)
{
if (ddi_get_child(dip) == NULL) {
DBG(DBG_TOOLS, dip,
"pxtool_dev_reg_ops set/get reg: nexus has no devs!\n");
prg_p->status = PCITOOL_IO_ERROR;
return (ENXIO);
}
return (SUCCESS);
}
int
pxtool_bus_reg_ops(dev_info_t *dip, void *arg, int cmd, int mode)
{
pcitool_reg_t prg;
uint64_t base_addr;
uint32_t reglen;
px_t *px_p = DIP_TO_STATE(dip);
px_nexus_regspec_t *px_rp = NULL;
uint32_t numbanks = 0;
boolean_t write_flag = B_FALSE;
uint32_t rval = 0;
if (cmd == PCITOOL_NEXUS_SET_REG)
write_flag = B_TRUE;
DBG(DBG_TOOLS, dip, "pxtool_bus_reg_ops set/get reg\n");
if (ddi_copyin(arg, &prg, sizeof (pcitool_reg_t), mode) !=
DDI_SUCCESS) {
DBG(DBG_TOOLS, dip, "Error reading arguments\n");
return (EFAULT);
}
if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
"reg", (int **)&px_rp, ®len) == DDI_SUCCESS) {
if (((reglen * sizeof (int)) %
sizeof (px_nexus_regspec_t)) != 0) {
DBG(DBG_TOOLS, dip, "reg prop not well-formed");
prg.status = PCITOOL_REGPROP_NOTWELLFORMED;
rval = EIO;
goto done;
}
}
numbanks = (reglen * sizeof (int)) / sizeof (px_nexus_regspec_t);
if (prg.barnum >= numbanks) {
prg.status = PCITOOL_OUT_OF_RANGE;
rval = EINVAL;
goto done;
}
base_addr = px_rp[prg.barnum].phys_addr;
prg.phys_addr = base_addr + prg.offset;
DBG(DBG_TOOLS, dip, "pxtool_bus_reg_ops: nexus: base:0x%" PRIx64 ", "
"offset:0x%" PRIx64 ", addr:0x%" PRIx64 ", max_offset:"
"0x%" PRIx64 "\n",
base_addr, prg.offset, prg.phys_addr, px_rp[prg.barnum].size);
if (prg.offset >= px_rp[prg.barnum].size) {
prg.status = PCITOOL_OUT_OF_RANGE;
rval = EINVAL;
goto done;
}
rval = pxtool_access(px_p, &prg, &prg.data, write_flag);
done:
if (px_rp != NULL)
ddi_prop_free(px_rp);
prg.drvr_version = PCITOOL_VERSION;
if (ddi_copyout(&prg, arg, sizeof (pcitool_reg_t),
mode) != DDI_SUCCESS) {
DBG(DBG_TOOLS, dip, "Copyout failed.\n");
return (EFAULT);
}
return (rval);
}