#include <sys/types.h>
#include <sys/kmem.h>
#include <sys/sysmacros.h>
#include <sys/async.h>
#include <sys/systm.h>
#include <sys/intreg.h>
#include <sys/ivintr.h>
#include <sys/sunddi.h>
#include <sys/sunndi.h>
#include <sys/machsystm.h>
#include <sys/fm/util.h>
#include <sys/ddi_impldefs.h>
#include <sys/iommutsb.h>
#include <sys/spl.h>
#include <sys/fm/util.h>
#include <sys/fm/protocol.h>
#include <sys/fm/io/pci.h>
#include <sys/fm/io/sun4upci.h>
#include <sys/pci/pci_obj.h>
#include <sys/pci/pcipsy.h>
static uint32_t pci_identity_init(pci_t *pci_p);
static int pci_intr_setup(pci_t *pci_p);
static void pci_pbm_errstate_get(pci_t *pci_p, pbm_errstate_t *pbm_err_p);
static pci_ksinfo_t *pci_name_kstat;
int
pci_obj_setup(pci_t *pci_p)
{
pci_common_t *cmn_p;
int ret;
mutex_enter(&pci_global_mutex);
cmn_p = get_pci_common_soft_state(pci_p->pci_id);
if (cmn_p == NULL) {
uint_t id = pci_p->pci_id;
if (alloc_pci_common_soft_state(id) != DDI_SUCCESS) {
mutex_exit(&pci_global_mutex);
return (DDI_FAILURE);
}
cmn_p = get_pci_common_soft_state(id);
cmn_p->pci_common_id = id;
}
ASSERT((pci_p->pci_side == 0) || (pci_p->pci_side == 1));
if (cmn_p->pci_p[pci_p->pci_side]) {
pci_p->pci_side = PCI_OTHER_SIDE(pci_p->pci_side);
ASSERT(cmn_p->pci_p[pci_p->pci_side] == NULL);
}
cmn_p->pci_p[pci_p->pci_side] = pci_p;
pci_p->pci_common_p = cmn_p;
if (cmn_p->pci_common_refcnt == 0) {
if (pci_alloc_tsb(pci_p) != DDI_SUCCESS) {
cmn_p->pci_p[pci_p->pci_side] = NULL;
pci_p->pci_common_p = NULL;
free_pci_common_soft_state(cmn_p->pci_common_id);
mutex_exit(&pci_global_mutex);
return (DDI_FAILURE);
}
cmn_p->pci_common_tsb_cookie = pci_p->pci_tsb_cookie;
cmn_p->pci_chip_id = pci_identity_init(pci_p);
ib_create(pci_p);
cmn_p->pci_common_ib_p = pci_p->pci_ib_p;
cb_create(pci_p);
cmn_p->pci_common_cb_p = pci_p->pci_cb_p;
iommu_create(pci_p);
cmn_p->pci_common_iommu_p = pci_p->pci_iommu_p;
ecc_create(pci_p);
cmn_p->pci_common_ecc_p = pci_p->pci_ecc_p;
} else {
ASSERT(cmn_p->pci_common_refcnt == 1);
pci_p->pci_tsb_cookie = cmn_p->pci_common_tsb_cookie;
pci_p->pci_ib_p = cmn_p->pci_common_ib_p;
pci_p->pci_cb_p = cmn_p->pci_common_cb_p;
pci_p->pci_iommu_p = cmn_p->pci_common_iommu_p;
pci_p->pci_ecc_p = cmn_p->pci_common_ecc_p;
}
pbm_create(pci_p);
sc_create(pci_p);
pci_fm_create(pci_p);
if ((ret = pci_intr_setup(pci_p)) != DDI_SUCCESS)
goto done;
if (CHIP_TYPE(pci_p) == PCI_CHIP_PSYCHO)
pci_kstat_create(pci_p);
cmn_p->pci_common_attachcnt++;
cmn_p->pci_common_refcnt++;
done:
mutex_exit(&pci_global_mutex);
if (ret != DDI_SUCCESS)
cmn_err(CE_NOTE, "Interrupt register failure, returning 0x%x\n",
ret);
return (ret);
}
void
pci_obj_destroy(pci_t *pci_p)
{
pci_common_t *cmn_p;
mutex_enter(&pci_global_mutex);
cmn_p = pci_p->pci_common_p;
cmn_p->pci_common_refcnt--;
cmn_p->pci_common_attachcnt--;
pci_kstat_destroy(pci_p);
sc_destroy(pci_p);
pbm_destroy(pci_p);
pci_fm_destroy(pci_p);
if (cmn_p->pci_common_refcnt != 0) {
cmn_p->pci_p[pci_p->pci_side] = NULL;
mutex_exit(&pci_global_mutex);
return;
}
ecc_destroy(pci_p);
iommu_destroy(pci_p);
cb_destroy(pci_p);
ib_destroy(pci_p);
free_pci_common_soft_state(cmn_p->pci_common_id);
pci_intr_teardown(pci_p);
mutex_exit(&pci_global_mutex);
}
void
pci_obj_resume(pci_t *pci_p)
{
pci_common_t *cmn_p = pci_p->pci_common_p;
mutex_enter(&pci_global_mutex);
if (cmn_p->pci_common_attachcnt == 0) {
ib_configure(pci_p->pci_ib_p);
iommu_configure(pci_p->pci_iommu_p);
ecc_configure(pci_p);
ib_resume(pci_p->pci_ib_p);
}
pbm_configure(pci_p->pci_pbm_p);
sc_configure(pci_p->pci_sc_p);
if (cmn_p->pci_common_attachcnt == 0)
cb_resume(pci_p->pci_cb_p);
pbm_resume(pci_p->pci_pbm_p);
cmn_p->pci_common_attachcnt++;
mutex_exit(&pci_global_mutex);
}
void
pci_obj_suspend(pci_t *pci_p)
{
mutex_enter(&pci_global_mutex);
pbm_suspend(pci_p->pci_pbm_p);
if (!--pci_p->pci_common_p->pci_common_attachcnt) {
ib_suspend(pci_p->pci_ib_p);
cb_suspend(pci_p->pci_cb_p);
}
mutex_exit(&pci_global_mutex);
}
static uint32_t javelin_prom_fix[] = {0xfff800, 0, 0, 0x3f};
static int
pci_intr_setup(pci_t *pci_p)
{
extern char *platform;
dev_info_t *dip = pci_p->pci_dip;
pbm_t *pbm_p = pci_p->pci_pbm_p;
cb_t *cb_p = pci_p->pci_cb_p;
int i, no_of_intrs;
if (strcmp((const char *)&platform, "SUNW,Ultra-250") == 0)
(void) ddi_prop_create(DDI_DEV_T_NONE, dip, DDI_PROP_CANSLEEP,
"interrupt-map-mask", (caddr_t)javelin_prom_fix,
sizeof (javelin_prom_fix));
if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
"interrupts", (caddr_t)&pci_p->pci_inos,
&pci_p->pci_inos_len) != DDI_SUCCESS)
cmn_err(CE_PANIC, "%s%d: no interrupts property\n",
ddi_driver_name(dip), ddi_get_instance(dip));
i = ddi_getprop(DDI_DEV_T_ANY, dip, 0, "#interrupt-cells", 1);
i = CELLS_1275_TO_BYTES(i);
no_of_intrs = pci_p->pci_inos_len / i;
for (i = 0; i < no_of_intrs; i++)
pci_p->pci_inos[i] = IB_MONDO_TO_INO(pci_p->pci_inos[i]);
if (pci_p->pci_common_p->pci_common_refcnt == 0) {
cb_p->cb_no_of_inos = no_of_intrs;
if (i = cb_register_intr(pci_p))
goto teardown;
if (i = ecc_register_intr(pci_p))
goto teardown;
intr_dist_add(cb_intr_dist, cb_p);
cb_enable_intr(pci_p);
ecc_enable_intr(pci_p);
}
if (i = pbm_register_intr(pbm_p)) {
if (pci_p->pci_common_p->pci_common_refcnt == 0)
intr_dist_rem(cb_intr_dist, cb_p);
goto teardown;
}
intr_dist_add(pbm_intr_dist, pbm_p);
ib_intr_enable(pci_p, pci_p->pci_inos[CBNINTR_PBM]);
if (pci_p->pci_common_p->pci_common_refcnt == 0)
intr_dist_add_weighted(ib_intr_dist_all, pci_p->pci_ib_p);
return (DDI_SUCCESS);
teardown:
pci_intr_teardown(pci_p);
return (i);
}
void
pci_fix_ranges(pci_ranges_t *rng_p, int rng_entries)
{
int i;
for (i = 0; i < rng_entries; i++, rng_p++)
if ((rng_p->child_high & PCI_REG_ADDR_M) == PCI_ADDR_CONFIG)
rng_p->parent_low |= rng_p->child_high;
}
int
map_pci_registers(pci_t *pci_p, dev_info_t *dip)
{
ddi_device_acc_attr_t attr;
attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
attr.devacc_attr_endian_flags = DDI_NEVERSWAP_ACC;
if (ddi_regs_map_setup(dip, 0, &pci_p->pci_address[0], 0, 0,
&attr, &pci_p->pci_ac[0]) != DDI_SUCCESS) {
cmn_err(CE_WARN, "%s%d: unable to map reg entry 0\n",
ddi_driver_name(dip), ddi_get_instance(dip));
return (DDI_FAILURE);
}
if (pci_stream_buf_exists &&
ddi_regs_map_setup(dip, 2, &pci_p->pci_address[2], 0, 0,
&attr, &pci_p->pci_ac[2]) != DDI_SUCCESS) {
cmn_err(CE_WARN, "%s%d: unable to map reg entry 2\n",
ddi_driver_name(dip), ddi_get_instance(dip));
ddi_regs_map_free(&pci_p->pci_ac[0]);
return (DDI_FAILURE);
}
attr.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC;
if (ddi_regs_map_setup(dip, 1, &pci_p->pci_address[1], 0,
PCI_CONF_HDR_SIZE, &attr, &pci_p->pci_ac[1]) != DDI_SUCCESS) {
cmn_err(CE_WARN, "%s%d: unable to map reg entry 1\n",
ddi_driver_name(dip), ddi_get_instance(dip));
ddi_regs_map_free(&pci_p->pci_ac[0]);
if (pci_stream_buf_exists)
ddi_regs_map_free(&pci_p->pci_ac[2]);
return (DDI_FAILURE);
}
DEBUG3(DBG_ATTACH, dip, "address (%p,%p,%p)\n",
pci_p->pci_address[0], pci_p->pci_address[1],
pci_p->pci_address[2]);
return (DDI_SUCCESS);
}
void
unmap_pci_registers(pci_t *pci_p)
{
ddi_regs_map_free(&pci_p->pci_ac[0]);
ddi_regs_map_free(&pci_p->pci_ac[1]);
if (pci_stream_buf_exists)
ddi_regs_map_free(&pci_p->pci_ac[2]);
}
static uintptr_t
get_reg_base(pci_t *pci_p)
{
return ((uintptr_t)pci_p->pci_address[pci_stream_buf_exists ? 2 : 0]);
}
static uintptr_t
get_config_reg_base(pci_t *pci_p)
{
return ((uintptr_t)(pci_p->pci_address[1]));
}
uint64_t
ib_get_map_reg(ib_mondo_t mondo, uint32_t cpu_id)
{
return ((mondo) | (cpu_id << COMMON_INTR_MAP_REG_TID_SHIFT) |
COMMON_INTR_MAP_REG_VALID);
}
uint32_t
ib_map_reg_get_cpu(volatile uint64_t reg)
{
return ((reg & COMMON_INTR_MAP_REG_TID) >>
COMMON_INTR_MAP_REG_TID_SHIFT);
}
uint64_t *
ib_intr_map_reg_addr(ib_t *ib_p, ib_ino_t ino)
{
uint64_t *addr;
if (ino & 0x20)
addr = (uint64_t *)(ib_p->ib_obio_intr_map_regs +
(((uint_t)ino & 0x1f) << 3));
else
addr = (uint64_t *)(ib_p->ib_slot_intr_map_regs +
(((uint_t)ino & 0x3c) << 1));
return (addr);
}
uint64_t *
ib_clear_intr_reg_addr(ib_t *ib_p, ib_ino_t ino)
{
uint64_t *addr;
if (ino & 0x20)
addr = (uint64_t *)(ib_p->ib_obio_clear_intr_regs +
(((uint_t)ino & 0x1f) << 3));
else
addr = (uint64_t *)(ib_p->ib_slot_clear_intr_regs +
(((uint_t)ino & 0x1f) << 3));
return (addr);
}
void
ib_ino_map_reg_share(ib_t *ib_p, ib_ino_t ino, ib_ino_info_t *ino_p)
{
if (!IB_IS_OBIO_INO(ino)) {
ASSERT(ino_p->ino_slot_no < 8);
ib_p->ib_map_reg_counters[ino_p->ino_slot_no]++;
}
}
int
ib_ino_map_reg_unshare(ib_t *ib_p, ib_ino_t ino, ib_ino_info_t *ino_p)
{
ASSERT(IB_IS_OBIO_INO(ino) || ino_p->ino_slot_no < 8);
if (IB_IS_OBIO_INO(ino))
return (ino_p->ino_ipil_size);
else
return (--ib_p->ib_map_reg_counters[ino_p->ino_slot_no]);
}
void
pci_pbm_intr_dist(pbm_t *pbm_p)
{
}
uintptr_t
pci_ib_setup(ib_t *ib_p)
{
pci_t *pci_p = ib_p->ib_pci_p;
uintptr_t a = get_reg_base(pci_p);
ib_p->ib_ign = PCI_ID_TO_IGN(pci_p->pci_id);
ib_p->ib_max_ino = PSYCHO_MAX_INO;
ib_p->ib_slot_intr_map_regs = a + PSYCHO_IB_SLOT_INTR_MAP_REG_OFFSET;
ib_p->ib_obio_intr_map_regs = a + PSYCHO_IB_OBIO_INTR_MAP_REG_OFFSET;
ib_p->ib_obio_clear_intr_regs =
a + PSYCHO_IB_OBIO_CLEAR_INTR_REG_OFFSET;
return (a);
}
uint32_t
pci_xlate_intr(dev_info_t *dip, dev_info_t *rdip, ib_t *ib_p, uint32_t intr)
{
int32_t len;
dev_info_t *cdip;
pci_regspec_t *pci_rp;
uint32_t bus, dev, phys_hi;
if ((intr > PCI_INTD) || (intr < PCI_INTA))
goto done;
if (ddi_prop_exists(DDI_DEV_T_ANY, rdip, 0, "interrupt-map"))
goto done;
cdip = get_my_childs_dip(dip, rdip);
if (ddi_getlongprop(DDI_DEV_T_ANY, cdip, DDI_PROP_DONTPASS, "reg",
(caddr_t)&pci_rp, &len) != DDI_SUCCESS)
return (0);
phys_hi = pci_rp->pci_phys_hi;
kmem_free(pci_rp, len);
bus = PCI_REG_BUS_G(phys_hi);
dev = PCI_REG_DEV_G(phys_hi);
DEBUG3(DBG_IB, dip, "pci_xlate_intr: bus=%x, dev=%x, intr=%x\n",
bus, dev, intr);
intr--;
intr |= (bus & 0x80) ? ((dev - 1) << 2) : (0x10 | ((dev - 2) << 2));
DEBUG1(DBG_IB, dip, "pci_xlate_intr: done ino=%x\n", intr);
done:
return (IB_INO_TO_MONDO(ib_p, intr));
}
uint32_t
pci_intr_dist_cpuid(ib_t *ib_p, ib_ino_info_t *ino_p)
{
dev_info_t *rdip = ino_p->ino_ipil_p->ipil_ih_head->ih_dip;
dev_info_t *prdip = ddi_get_parent(rdip);
ib_ino_info_t *sino_p;
dev_info_t *sdip;
dev_info_t *psdip;
char *buf1 = NULL, *buf2 = NULL;
char *s1, *s2, *s3;
int l2;
int cpu_id;
if (strcmp(ddi_driver_name(prdip), "pcipsy") != 0)
goto newcpu;
s1 = ddi_get_name_addr(rdip);
if (s1 == NULL)
goto newcpu;
buf1 = kmem_alloc(MAXNAMELEN, KM_SLEEP);
buf2 = kmem_alloc(MAXNAMELEN, KM_SLEEP);
s1 = strcpy(buf1, s1);
s2 = strcpy(buf2, s1);
s1 = strrchr(s1, ',');
if (s1) {
*s1 = '\0';
s1 = buf1;
s2 = strrchr(s2, ',');
*(s2 + 1) = '\0';
s2 = buf2;
l2 = strlen(s2);
} else {
(void) strcat(s2, ",");
l2 = strlen(s2);
}
ASSERT(MUTEX_HELD(&ib_p->ib_ino_lst_mutex));
for (sino_p = ib_p->ib_ino_lst; sino_p; sino_p = sino_p->ino_next_p) {
if ((sino_p == ino_p) || (sino_p->ino_established == 0))
continue;
sdip = sino_p->ino_ipil_p->ipil_ih_head->ih_dip;
psdip = ddi_get_parent(sdip);
if (psdip != prdip)
continue;
if (strcmp(ddi_driver_name(psdip), "pcipsy") != 0)
continue;
s3 = ddi_get_name_addr(sdip);
if ((s1 && (strcmp(s1, s3) == 0)) ||
(strncmp(s2, s3, l2) == 0)) {
extern int intr_dist_debug;
if (intr_dist_debug)
cmn_err(CE_CONT, "intr_dist: "
"pcipsy`pci_intr_dist_cpuid "
"%s#%d %s: cpu %d established "
"by %s#%d %s\n", ddi_driver_name(rdip),
ddi_get_instance(rdip),
ddi_deviname(rdip, buf1), sino_p->ino_cpuid,
ddi_driver_name(sdip),
ddi_get_instance(sdip),
ddi_deviname(sdip, buf2));
break;
}
}
if (sino_p) {
cpu_id = sino_p->ino_cpuid;
goto out;
}
newcpu: cpu_id = intr_dist_cpuid();
out: if (buf1)
kmem_free(buf1, MAXNAMELEN);
if (buf2)
kmem_free(buf2, MAXNAMELEN);
return (cpu_id);
}
static void
cb_thermal_timeout(void *arg)
{
do_shutdown();
(void) timeout((void(*)(void *))power_down, NULL,
thermal_powerdown_delay * hz);
}
uint_t
cb_thermal_intr(caddr_t a)
{
cmn_err(CE_WARN, "pci: Thermal warning detected!\n");
if (pci_thermal_intr_fatal) {
(void) timeout(cb_thermal_timeout, NULL, 0);
}
return (DDI_INTR_CLAIMED);
}
void
pci_cb_teardown(pci_t *pci_p)
{
cb_t *cb_p = pci_p->pci_cb_p;
uint32_t mondo;
if (pci_p->pci_thermal_interrupt != -1) {
mondo = ((pci_p->pci_cb_p->cb_ign << PCI_INO_BITS) |
pci_p->pci_inos[CBNINTR_THERMAL]);
mondo = CB_MONDO_TO_XMONDO(pci_p->pci_cb_p, mondo);
cb_disable_nintr(cb_p, CBNINTR_THERMAL, IB_INTR_WAIT);
VERIFY(rem_ivintr(mondo, pci_pil[CBNINTR_THERMAL]) == 0);
}
}
int
cb_register_intr(pci_t *pci_p)
{
uint32_t mondo;
if (pci_p->pci_thermal_interrupt == -1)
return (DDI_SUCCESS);
mondo = ((pci_p->pci_cb_p->cb_ign << PCI_INO_BITS) |
pci_p->pci_inos[CBNINTR_THERMAL]);
mondo = CB_MONDO_TO_XMONDO(pci_p->pci_cb_p, mondo);
VERIFY(add_ivintr(mondo, pci_pil[CBNINTR_THERMAL],
(intrfunc)cb_thermal_intr, (caddr_t)pci_p->pci_cb_p,
NULL, NULL) == 0);
return (PCI_ATTACH_RETCODE(PCI_CB_OBJ, PCI_OBJ_INTR_ADD, DDI_SUCCESS));
}
void
cb_enable_intr(pci_t *pci_p)
{
if (pci_p->pci_thermal_interrupt != -1)
cb_enable_nintr(pci_p, CBNINTR_THERMAL);
}
uint64_t
cb_ino_to_map_pa(cb_t *cb_p, ib_ino_t ino)
{
return (cb_p->cb_map_pa + ((ino & 0x1f) << 3));
}
uint64_t
cb_ino_to_clr_pa(cb_t *cb_p, ib_ino_t ino)
{
return (cb_p->cb_clr_pa + ((ino & 0x1f) << 3));
}
int
cb_remove_xintr(pci_t *pci_p, dev_info_t *dip, dev_info_t *rdip,
ib_ino_t ino, ib_mondo_t mondo)
{
if (ino != pci_p->pci_inos[CBNINTR_THERMAL])
return (DDI_FAILURE);
cb_disable_nintr(pci_p->pci_cb_p, CBNINTR_THERMAL, IB_INTR_WAIT);
VERIFY(rem_ivintr(mondo, pci_pil[CBNINTR_THERMAL]) == 0);
DEBUG1(DBG_R_INTX, dip, "remove xintr %x\n", ino);
return (DDI_SUCCESS);
}
int
pci_ecc_add_intr(pci_t *pci_p, int inum, ecc_intr_info_t *eii_p)
{
uint32_t mondo;
mondo = ((pci_p->pci_cb_p->cb_ign << PCI_INO_BITS) |
pci_p->pci_inos[inum]);
mondo = CB_MONDO_TO_XMONDO(pci_p->pci_cb_p, mondo);
VERIFY(add_ivintr(mondo, pci_pil[inum], (intrfunc)ecc_intr,
(caddr_t)eii_p, NULL, NULL) == 0);
return (PCI_ATTACH_RETCODE(PCI_ECC_OBJ, PCI_OBJ_INTR_ADD, DDI_SUCCESS));
}
void
pci_ecc_rem_intr(pci_t *pci_p, int inum, ecc_intr_info_t *eii_p)
{
uint32_t mondo;
mondo = ((pci_p->pci_cb_p->cb_ign << PCI_INO_BITS) |
pci_p->pci_inos[inum]);
mondo = CB_MONDO_TO_XMONDO(pci_p->pci_cb_p, mondo);
VERIFY(rem_ivintr(mondo, pci_pil[inum]) == 0);
}
static int pbm_has_pass_1_cheerio(pci_t *pci_p);
void
pbm_configure(pbm_t *pbm_p)
{
pci_t *pci_p = pbm_p->pbm_pci_p;
cb_t *cb_p = pci_p->pci_cb_p;
dev_info_t *dip = pci_p->pci_dip;
int instance = ddi_get_instance(dip);
uint32_t mask = 1 << instance;
uint64_t l;
uint16_t s = 0;
l = lddphysio(cb_p->cb_base_pa + PSYCHO_CB_CONTROL_STATUS_REG_OFFSET);
l &= PSYCHO_CB_CONTROL_STATUS_VER;
l >>= PSYCHO_CB_CONTROL_STATUS_VER_SHIFT;
DEBUG2(DBG_ATTACH, dip, "cb_create: ver=%d, mask=%x\n", l, mask);
pci_rerun_disable = (uint32_t)-1;
switch (l) {
case 0:
DEBUG0(DBG_ATTACH, dip, "cb_create: psycho pass 1\n");
if (!pci_disable_pass1_workarounds) {
if (pbm_has_pass_1_cheerio(pci_p))
pci_bus_parking_enable &= ~mask;
pci_rerun_disable &= ~mask;
pci_retry_disable |= mask;
}
break;
case 1:
if (!pci_disable_pass2_workarounds) {
pci_bus_parking_enable &= ~mask;
pci_rerun_disable &= ~mask;
pci_retry_disable |= mask;
pci_dwsync_disable |= mask;
}
break;
case 2:
if (!pci_disable_pass3_workarounds) {
pci_dwsync_disable |= mask;
if (pbm_has_pass_1_cheerio(pci_p))
pci_bus_parking_enable &= ~mask;
}
break;
case 3:
if (!pci_disable_plus_workarounds) {
pci_dwsync_disable |= mask;
if (pbm_has_pass_1_cheerio(pci_p))
pci_bus_parking_enable &= ~mask;
}
break;
default:
if (!pci_disable_default_workarounds) {
pci_dwsync_disable |= mask;
if (pbm_has_pass_1_cheerio(pci_p))
pci_bus_parking_enable &= ~mask;
}
break;
}
l = (PSYCHO_PCI_AFSR_E_MASK << PSYCHO_PCI_AFSR_PE_SHIFT) |
(PSYCHO_PCI_AFSR_E_MASK << PSYCHO_PCI_AFSR_SE_SHIFT);
*pbm_p->pbm_async_flt_status_reg = l;
s = PCI_STAT_PERROR | PCI_STAT_S_PERROR |
PCI_STAT_R_MAST_AB | PCI_STAT_R_TARG_AB |
PCI_STAT_S_TARG_AB | PCI_STAT_S_PERROR;
DEBUG1(DBG_ATTACH, dip, "pbm_configure: conf status reg=%x\n", s);
pbm_p->pbm_config_header->ch_status_reg = s;
DEBUG1(DBG_ATTACH, dip, "pbm_configure: conf status reg==%x\n",
pbm_p->pbm_config_header->ch_status_reg);
l = *pbm_p->pbm_ctrl_reg;
DEBUG1(DBG_ATTACH, dip, "pbm_configure: ctrl reg==%llx\n", l);
if (l & COMMON_PCI_CTRL_SERR)
cmn_err(CE_WARN, "%s%d: SERR asserted on pci bus\n",
ddi_driver_name(dip), instance);
if (l & COMMON_PCI_CTRL_SPEED)
pbm_p->pbm_speed = PBM_SPEED_66MHZ;
else
pbm_p->pbm_speed = PBM_SPEED_33MHZ;
DEBUG1(DBG_ATTACH, dip, "pbm_configure: %d mhz\n",
pbm_p->pbm_speed == PBM_SPEED_66MHZ ? 66 : 33);
if (pci_error_intr_enable & mask)
l |= PSYCHO_PCI_CTRL_ERR_INT_EN;
else
l &= ~PSYCHO_PCI_CTRL_ERR_INT_EN;
pci_sbh_error_intr_enable &= ~mask;
l &= ~PSYCHO_PCI_CTRL_SBH_INT_EN;
if ((pci_bus_parking_enable & mask) &&
!ddi_prop_exists(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
"no-bus-parking"))
l |= PSYCHO_PCI_CTRL_ARB_PARK;
else
l &= ~PSYCHO_PCI_CTRL_ARB_PARK;
if (pci_p->pci_side == B)
l = (l & ~PSYCHO_PCI_CTRL_ARB_EN_MASK) | pci_b_arb_enable;
else
l = (l & ~PSYCHO_PCI_CTRL_ARB_EN_MASK) | pci_a_arb_enable;
l |= COMMON_PCI_CTRL_SERR;
l &= ~PSYCHO_PCI_CTRL_WAKEUP_EN;
DEBUG1(DBG_ATTACH, dip, "pbm_configure: ctrl reg=%llx\n", l);
*pbm_p->pbm_ctrl_reg = l;
l = *pbm_p->pbm_diag_reg;
DEBUG1(DBG_ATTACH, dip, "pbm_configure: PCI diag reg==%llx\n", l);
if (pci_retry_disable & mask)
l |= COMMON_PCI_DIAG_DIS_RETRY;
if (pci_retry_enable & mask)
l &= ~COMMON_PCI_DIAG_DIS_RETRY;
if (pci_intsync_disable & mask)
l |= COMMON_PCI_DIAG_DIS_INTSYNC;
else
l &= ~COMMON_PCI_DIAG_DIS_INTSYNC;
if (pci_dwsync_disable & mask)
l |= PSYCHO_PCI_DIAG_DIS_DWSYNC;
else
l &= ~PSYCHO_PCI_DIAG_DIS_DWSYNC;
DEBUG1(DBG_ATTACH, dip, "pbm_configure: PCI diag reg=%llx\n", l);
*pbm_p->pbm_diag_reg = l;
s = pci_perr_enable & mask ? PCI_COMM_PARITY_DETECT : 0;
s |= pci_serr_enable & mask ? PCI_COMM_SERR_ENABLE : 0;
DEBUG1(DBG_ATTACH, dip, "pbm_configure: conf command reg=%x\n", s);
pbm_p->pbm_config_header->ch_command_reg = s;
DEBUG1(DBG_ATTACH, dip, "pbm_configure: conf command reg==%x\n",
pbm_p->pbm_config_header->ch_command_reg);
if (pci_set_latency_timer_register) {
DEBUG1(DBG_ATTACH, dip,
"pbm_configure: set psycho latency timer to %x\n",
pci_latency_timer);
pbm_p->pbm_config_header->ch_latency_timer_reg =
pci_latency_timer;
}
(void) ndi_prop_update_int(DDI_DEV_T_ANY, dip, "latency-timer",
(int)pbm_p->pbm_config_header->ch_latency_timer_reg);
}
uint_t
pbm_disable_pci_errors(pbm_t *pbm_p)
{
pci_t *pci_p = pbm_p->pbm_pci_p;
ib_t *ib_p = pci_p->pci_ib_p;
*pbm_p->pbm_ctrl_reg &=
~(PSYCHO_PCI_CTRL_ERR_INT_EN | PSYCHO_PCI_CTRL_SBH_INT_EN);
ib_intr_disable(ib_p, pci_p->pci_inos[CBNINTR_PBM], IB_INTR_NOWAIT);
return (BF_NONE);
}
uint64_t
pci_sc_configure(pci_t *pci_p)
{
return (0);
}
void
pci_pbm_dma_sync(pbm_t *pbm_p, ib_ino_t ino)
{
uint64_t pa = pbm_p->pbm_sync_reg_pa;
if (pa)
(void) lddphysio(pa);
}
dvma_context_t
pci_iommu_get_dvma_context(iommu_t *iommu_p, dvma_addr_t dvma_pg_index)
{
ASSERT(0);
return (0);
}
void
pci_iommu_free_dvma_context(iommu_t *iommu_p, dvma_context_t ctx)
{
ASSERT(0);
}
void
pci_iommu_config(iommu_t *iommu_p, uint64_t iommu_ctl, uint64_t cfgpa)
{
volatile uint64_t *pbm_csr_p = (volatile uint64_t *)
get_pbm_reg_base(iommu_p->iommu_pci_p);
volatile uint64_t pbm_ctl = *pbm_csr_p;
volatile uint64_t *iommu_ctl_p = iommu_p->iommu_ctrl_reg;
volatile uint64_t tsb_bar_val = iommu_p->iommu_tsb_paddr;
volatile uint64_t *tsb_bar_p = iommu_p->iommu_tsb_base_addr_reg;
DEBUG2(DBG_ATTACH, iommu_p->iommu_pci_p->pci_dip,
"\npci_iommu_config: pbm_csr_p=%016llx pbm_ctl=%016llx",
pbm_csr_p, pbm_ctl);
DEBUG2(DBG_ATTACH|DBG_CONT, iommu_p->iommu_pci_p->pci_dip,
"\n\tiommu_ctl_p=%016llx iommu_ctl=%016llx",
iommu_ctl_p, iommu_ctl);
DEBUG2(DBG_ATTACH|DBG_CONT, iommu_p->iommu_pci_p->pci_dip,
"\n\tcfgpa=%016llx tsb_bar_val=%016llx", cfgpa, tsb_bar_val);
if (!cfgpa)
goto reprog;
*pbm_csr_p = (pbm_ctl >> 8) << 8;
(void) ldphysio(cfgpa);
reprog:
*tsb_bar_p = tsb_bar_val;
*iommu_ctl_p = iommu_ctl;
*pbm_csr_p = pbm_ctl;
pbm_ctl = *pbm_csr_p;
}
int
pci_sc_ctx_inv(dev_info_t *dip, sc_t *sc_p, ddi_dma_impl_t *mp)
{
ASSERT(0);
return (DDI_FAILURE);
}
void
pci_cb_setup(pci_t *pci_p)
{
uint64_t csr, csr_pa, pa;
cb_t *cb_p = pci_p->pci_cb_p;
cb_p->cb_ign = PCI_ID_TO_IGN(pci_p->pci_id);
pa = (uint64_t)hat_getpfnum(kas.a_hat, pci_p->pci_address[0]);
cb_p->cb_base_pa = pa = pa >> (32 - MMU_PAGESHIFT) << 32;
cb_p->cb_map_pa = pa + PSYCHO_IB_OBIO_INTR_MAP_REG_OFFSET;
cb_p->cb_clr_pa = pa + PSYCHO_IB_OBIO_CLEAR_INTR_REG_OFFSET;
cb_p->cb_obsta_pa = pa + COMMON_IB_OBIO_INTR_STATE_DIAG_REG;
csr_pa = pa + PSYCHO_CB_CONTROL_STATUS_REG_OFFSET;
csr = lddphysio(csr_pa);
if (csr & COMMON_CB_CONTROL_STATUS_APERR) {
csr |= COMMON_CB_CONTROL_STATUS_APERR;
cmn_err(CE_WARN, "clearing UPA address parity error\n");
}
csr |= COMMON_CB_CONTROL_STATUS_APCKEN;
csr &= ~COMMON_CB_CONTROL_STATUS_IAP;
stdphysio(csr_pa, csr);
}
void
pci_ecc_setup(ecc_t *ecc_p)
{
ecc_p->ecc_ue.ecc_errpndg_mask = 0;
ecc_p->ecc_ue.ecc_offset_mask = PSYCHO_ECC_UE_AFSR_DW_OFFSET;
ecc_p->ecc_ue.ecc_offset_shift = PSYCHO_ECC_UE_AFSR_DW_OFFSET_SHIFT;
ecc_p->ecc_ue.ecc_size_log2 = 3;
ecc_p->ecc_ce.ecc_errpndg_mask = 0;
ecc_p->ecc_ce.ecc_offset_mask = PSYCHO_ECC_CE_AFSR_DW_OFFSET;
ecc_p->ecc_ce.ecc_offset_shift = PSYCHO_ECC_CE_AFSR_DW_OFFSET_SHIFT;
ecc_p->ecc_ce.ecc_size_log2 = 3;
}
uintptr_t
pci_iommu_setup(iommu_t *iommu_p)
{
pci_dvma_range_prop_t *dvma_prop;
int dvma_prop_len;
pci_t *pci_p = iommu_p->iommu_pci_p;
dev_info_t *dip = pci_p->pci_dip;
uint_t tsb_size = iommu_tsb_cookie_to_size(pci_p->pci_tsb_cookie);
uint_t tsb_size_prop;
if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
"virtual-dma", (caddr_t)&dvma_prop, &dvma_prop_len) !=
DDI_PROP_SUCCESS)
goto tsb_done;
if (dvma_prop_len != sizeof (pci_dvma_range_prop_t)) {
cmn_err(CE_WARN, "%s%d: invalid virtual-dma property",
ddi_driver_name(dip), ddi_get_instance(dip));
goto tsb_end;
}
iommu_p->iommu_dvma_end = dvma_prop->dvma_base +
(dvma_prop->dvma_len - 1);
tsb_size_prop = IOMMU_BTOP(dvma_prop->dvma_len) * sizeof (uint64_t);
tsb_size = MIN(tsb_size_prop, tsb_size);
tsb_end:
kmem_free(dvma_prop, dvma_prop_len);
tsb_done:
iommu_p->iommu_tsb_size = iommu_tsb_size_encode(tsb_size);
if (CHIP_TYPE(pci_p) != PCI_CHIP_HUMMINGBIRD)
pci_preserve_iommu_tsb = 0;
iommu_p->iommu_ctx_bitmap = NULL;
iommu_p->iommu_flush_ctx_reg = NULL;
pci_use_contexts = 0;
pci_sc_use_contexts = 0;
return (get_reg_base(pci_p));
}
void
pci_iommu_teardown(iommu_t *iommu_p)
{
}
uintptr_t
get_pbm_reg_base(pci_t *pci_p)
{
return ((uintptr_t)(pci_p->pci_address[0] +
(pci_stream_buf_exists ? 0 : PSYCHO_PCI_PBM_REG_BASE)));
}
void
pci_post_uninit_child(pci_t *pci_p)
{
}
void
pci_pbm_setup(pbm_t *pbm_p)
{
pci_t *pci_p = pbm_p->pbm_pci_p;
uintptr_t a = get_pbm_reg_base(pci_p);
pbm_p->pbm_config_header =
(config_header_t *)get_config_reg_base(pci_p);
pbm_p->pbm_ctrl_reg = (uint64_t *)(a + PSYCHO_PCI_CTRL_REG_OFFSET);
pbm_p->pbm_diag_reg = (uint64_t *)(a + PSYCHO_PCI_DIAG_REG_OFFSET);
pbm_p->pbm_async_flt_status_reg =
(uint64_t *)(a + PSYCHO_PCI_ASYNC_FLT_STATUS_REG_OFFSET);
pbm_p->pbm_async_flt_addr_reg =
(uint64_t *)(a + PSYCHO_PCI_ASYNC_FLT_ADDR_REG_OFFSET);
if (CHIP_TYPE(pci_p) >= PCI_CHIP_SABRE)
pbm_p->pbm_sync_reg_pa =
pci_p->pci_cb_p->cb_base_pa + DMA_WRITE_SYNC_REG;
}
void
pci_pbm_teardown(pbm_t *pbm_p)
{
}
void
pci_sc_setup(sc_t *sc_p)
{
pci_t *pci_p = sc_p->sc_pci_p;
uintptr_t a = get_pbm_reg_base(pci_p);
sc_p->sc_ctrl_reg = (uint64_t *)(a + PSYCHO_SC_CTRL_REG_OFFSET);
sc_p->sc_invl_reg = (uint64_t *)(a + PSYCHO_SC_INVL_REG_OFFSET);
sc_p->sc_sync_reg = (uint64_t *)(a + PSYCHO_SC_SYNC_REG_OFFSET);
a = get_reg_base(pci_p);
if (pci_p->pci_bus_range.lo != 0) {
sc_p->sc_data_diag_acc = (uint64_t *)
(a + PSYCHO_SC_A_DATA_DIAG_OFFSET);
sc_p->sc_tag_diag_acc = (uint64_t *)
(a + PSYCHO_SC_A_TAG_DIAG_OFFSET);
sc_p->sc_ltag_diag_acc = (uint64_t *)
(a + PSYCHO_SC_A_LTAG_DIAG_OFFSET);
} else {
sc_p->sc_data_diag_acc = (uint64_t *)
(a + PSYCHO_SC_B_DATA_DIAG_OFFSET);
sc_p->sc_tag_diag_acc = (uint64_t *)
(a + PSYCHO_SC_B_TAG_DIAG_OFFSET);
sc_p->sc_ltag_diag_acc = (uint64_t *)
(a + PSYCHO_SC_B_LTAG_DIAG_OFFSET);
}
}
int
pci_get_numproxy(dev_info_t *dip)
{
return (ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
"#upa-interrupt-proxies", 1));
}
int
pci_get_portid(dev_info_t *dip)
{
return (ddi_getprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
"upa-portid", -1));
}
static int
pbm_has_pass_1_cheerio(pci_t *pci_p)
{
dev_info_t *cdip;
int found = 0;
char *s;
int rev;
cdip = ddi_get_child(pci_p->pci_dip);
while (cdip != NULL && found == 0) {
s = ddi_get_name(cdip);
if (strcmp(s, "ebus") == 0 || strcmp(s, "pci108e,1000") == 0) {
rev =
ddi_getprop(DDI_DEV_T_ANY, cdip, DDI_PROP_DONTPASS,
"revision-id", 0);
if (rev == 0)
found = 1;
}
cdip = ddi_get_next_sibling(cdip);
}
return (found);
}
pci_kev_mask_t
psycho_pci_events[] = {
{"dvma_stream_rd_a", 0x0}, {"dvma_stream_wr_a", 0x1},
{"dvma_const_rd_a", 0x2}, {"dvma_const_wr_a", 0x3},
{"dvma_stream_buf_mis_a", 0x4}, {"dvma_cycles_a", 0x5},
{"dvma_wd_xfr_a", 0x6}, {"pio_cycles_a", 0x7},
{"dvma_stream_rd_b", 0x8}, {"dvma_stream_wr_b", 0x9},
{"dvma_const_rd_b", 0xa}, {"dvma_const_wr_b", 0xb},
{"dvma_stream_buf_mis_b", 0xc}, {"dvma_cycles_b", 0xd},
{"dvma_wd_xfr_b", 0xe}, {"pio_cycles_b", 0xf},
{"dvma_tlb_misses", 0x10}, {"interrupts", 0x11},
{"upa_inter_nack", 0x12}, {"pio_reads", 0x13},
{"pio_writes", 0x14}, {"merge_buffer", 0x15},
{"dma_tbwalk_a", 0x16}, {"dma_stc_a", 0x17},
{"dma_tbwalk_b", 0x18}, {"dma_stc_b", 0x19},
{"clear_pic", 0x1f}
};
void
pci_kstat_init()
{
pci_name_kstat = (pci_ksinfo_t *)kmem_alloc(sizeof (pci_ksinfo_t),
KM_NOSLEEP);
if (pci_name_kstat == NULL) {
cmn_err(CE_WARN, "pcipsy : no space for kstat\n");
} else {
pci_name_kstat->pic_no_evs =
sizeof (psycho_pci_events) / sizeof (pci_kev_mask_t);
pci_name_kstat->pic_shift[0] = PSYCHO_SHIFT_PIC0;
pci_name_kstat->pic_shift[1] = PSYCHO_SHIFT_PIC1;
pci_create_name_kstat("pcip",
pci_name_kstat, psycho_pci_events);
}
}
void
pci_kstat_fini()
{
if (pci_name_kstat != NULL) {
pci_delete_name_kstat(pci_name_kstat);
kmem_free(pci_name_kstat, sizeof (pci_ksinfo_t));
pci_name_kstat = NULL;
}
}
void
pci_add_pci_kstat(pci_t *pci_p)
{
}
void
pci_rem_pci_kstat(pci_t *pci_p)
{
}
void
pci_add_upstream_kstat(pci_t *pci_p)
{
pci_common_t *cmn_p = pci_p->pci_common_p;
pci_cntr_pa_t *cntr_pa_p = &cmn_p->pci_cmn_uks_pa;
uint64_t regbase = va_to_pa((void *)get_reg_base(pci_p));
cntr_pa_p->pcr_pa = regbase + PSYCHO_PERF_PCR_OFFSET;
cntr_pa_p->pic_pa = regbase + PSYCHO_PERF_PIC_OFFSET;
cmn_p->pci_common_uksp = pci_create_cntr_kstat(pci_p, "pcip",
NUM_OF_PICS, pci_cntr_kstat_pa_update, cntr_pa_p);
}
static uint32_t
pci_identity_init(pci_t *pci_p)
{
dev_info_t *dip = pci_p->pci_dip;
char *name = ddi_binding_name(dip);
if (strcmp(name, "pci108e,8000") == 0)
return (CHIP_ID(PCI_CHIP_PSYCHO, 0x00, 0x00));
if (strcmp(name, "pci108e,a000") == 0)
return (CHIP_ID(PCI_CHIP_SABRE, 0x00, 0x00));
if (strcmp(name, "pci108e,a001") == 0)
return (CHIP_ID(PCI_CHIP_HUMMINGBIRD, 0x00, 0x00));
cmn_err(CE_CONT, "?%s%d:using default chip identity\n",
ddi_driver_name(dip), ddi_get_instance(dip));
return (CHIP_ID(PCI_CHIP_PSYCHO, 0x00, 0x00));
}
void
pci_post_init_child(pci_t *pci_p, dev_info_t *child)
{
}
int
pci_pbm_add_intr(pci_t *pci_p)
{
return (DDI_SUCCESS);
}
void
pci_pbm_rem_intr(pci_t *pci_p)
{
}
void
pci_pbm_suspend(pci_t *pci_p)
{
}
void
pci_pbm_resume(pci_t *pci_p)
{
}
int
ecc_ue_is_fatal(struct async_flt *ecc)
{
return (((uint_t)(ecc->flt_stat >> SABRE_UE_AFSR_PDTE_SHIFT) &
SABRE_UE_AFSR_E_PDTE) == 0);
}
void
pci_ecc_classify(uint64_t err, ecc_errstate_t *ecc_err_p)
{
struct async_flt *ecc = &ecc_err_p->ecc_aflt;
pci_common_t *cmn_p = ecc_err_p->ecc_ii_p.ecc_p->ecc_pci_cmn_p;
ASSERT(MUTEX_HELD(&cmn_p->pci_fm_mutex));
ecc_err_p->ecc_bridge_type = PCI_BRIDGE_TYPE(cmn_p);
ecc_err_p->ecc_dev_id = (ecc_err_p->ecc_afsr & PSYCHO_ECC_UE_AFSR_ID)
>> PSYCHO_ECC_UE_AFSR_ID_SHIFT;
ecc_err_p->ecc_dw_offset = (ecc_err_p->ecc_afsr &
PSYCHO_ECC_UE_AFSR_DW_OFFSET)
>> PSYCHO_ECC_UE_AFSR_DW_OFFSET_SHIFT;
if (err & COMMON_ECC_AFSR_E_PIO) {
if (ecc_err_p->ecc_ii_p.ecc_type == CBNINTR_UE) {
if (ecc_err_p->ecc_pri) {
ecc->flt_erpt_class = PCI_ECC_PIO_UE;
} else {
ecc->flt_erpt_class = PCI_ECC_SEC_PIO_UE;
}
ecc->flt_panic = ecc_ue_is_fatal(&ecc_err_p->ecc_aflt);
} else {
ecc->flt_erpt_class = ecc_err_p->ecc_pri ?
PCI_ECC_PIO_CE : PCI_ECC_SEC_PIO_CE;
return;
}
} else if (err & COMMON_ECC_AFSR_E_DRD) {
if (ecc_err_p->ecc_ii_p.ecc_type == CBNINTR_UE) {
if (ecc_err_p->ecc_pri) {
ecc->flt_erpt_class = PCI_ECC_DRD_UE;
} else {
ecc->flt_erpt_class = PCI_ECC_SEC_DRD_UE;
}
ecc->flt_panic = ecc_ue_is_fatal(&ecc_err_p->ecc_aflt);
} else {
ecc->flt_erpt_class = ecc_err_p->ecc_pri ?
PCI_ECC_DRD_CE : PCI_ECC_SEC_DRD_CE;
return;
}
} else if (err & COMMON_ECC_AFSR_E_DWR) {
if (ecc_err_p->ecc_ii_p.ecc_type == CBNINTR_UE) {
if (ecc_err_p->ecc_pri) {
ecc->flt_erpt_class = PCI_ECC_DWR_UE;
} else {
ecc->flt_erpt_class = PCI_ECC_SEC_DWR_UE;
}
ecc->flt_panic = ecc_ue_is_fatal(&ecc_err_p->ecc_aflt);
} else {
ecc->flt_erpt_class = ecc_err_p->ecc_pri ?
PCI_ECC_DWR_CE : PCI_ECC_SEC_DWR_CE;
return;
}
}
}
ushort_t
pci_ecc_get_synd(uint64_t afsr)
{
return ((ushort_t)((afsr & PSYCHO_ECC_CE_AFSR_SYND)
>> PSYCHO_ECC_CE_AFSR_SYND_SHIFT));
}
int
pci_pbm_classify(pbm_errstate_t *pbm_err_p)
{
uint32_t e;
int nerr = 0;
char **tmp_class;
if (pbm_err_p->pbm_pri) {
tmp_class = &pbm_err_p->pbm_pci.pci_err_class;
e = PBM_AFSR_TO_PRIERR(pbm_err_p->pbm_afsr);
pbm_err_p->pbm_log = FM_LOG_PCI;
} else {
tmp_class = &pbm_err_p->pbm_err_class;
e = PBM_AFSR_TO_SECERR(pbm_err_p->pbm_afsr);
pbm_err_p->pbm_log = FM_LOG_PBM;
}
if (e & PSYCHO_PCI_AFSR_E_MA) {
*tmp_class = pbm_err_p->pbm_pri ? PCI_MA : PCI_SEC_MA;
nerr++;
}
if (e & PSYCHO_PCI_AFSR_E_TA) {
*tmp_class = pbm_err_p->pbm_pri ? PCI_REC_TA : PCI_SEC_REC_TA;
nerr++;
}
if (e & PSYCHO_PCI_AFSR_E_RTRY) {
pbm_err_p->pbm_err_class = pbm_err_p->pbm_pri ?
PCI_PBM_RETRY : PCI_SEC_PBM_RETRY;
pbm_err_p->pbm_log = FM_LOG_PBM;
nerr++;
}
if (e & PSYCHO_PCI_AFSR_E_PERR) {
*tmp_class = pbm_err_p->pbm_pri ? PCI_MDPE : PCI_SEC_MDPE;
nerr++;
}
return (nerr);
}
static void
pci_clear_error(pci_t *pci_p, pbm_errstate_t *pbm_err_p)
{
pbm_t *pbm_p = pci_p->pci_pbm_p;
ASSERT(MUTEX_HELD(&pbm_p->pbm_pci_p->pci_common_p->pci_fm_mutex));
*pbm_p->pbm_ctrl_reg = pbm_err_p->pbm_ctl_stat;
*pbm_p->pbm_async_flt_status_reg = pbm_err_p->pbm_afsr;
pbm_p->pbm_config_header->ch_status_reg =
pbm_err_p->pbm_pci.pci_cfg_stat;
}
int
pci_pbm_err_handler(dev_info_t *dip, ddi_fm_error_t *derr,
const void *impl_data, int caller)
{
int fatal = 0;
int nonfatal = 0;
int unknown = 0;
uint32_t prierr, secerr;
pbm_errstate_t pbm_err;
char buf[FM_MAX_CLASS];
pci_t *pci_p = (pci_t *)impl_data;
pbm_t *pbm_p = pci_p->pci_pbm_p;
int ret = 0;
uint64_t pbm_ctl_stat;
uint16_t pci_cfg_stat;
ASSERT(MUTEX_HELD(&pci_p->pci_common_p->pci_fm_mutex));
pci_pbm_errstate_get(pci_p, &pbm_err);
derr->fme_ena = derr->fme_ena ? derr->fme_ena :
fm_ena_generate(0, FM_ENA_FMT1);
prierr = PBM_AFSR_TO_PRIERR(pbm_err.pbm_afsr);
secerr = PBM_AFSR_TO_SECERR(pbm_err.pbm_afsr);
if (derr->fme_flag == DDI_FM_ERR_EXPECTED) {
if (caller == PCI_TRAP_CALL) {
derr->fme_status = DDI_FM_NONFATAL;
nonfatal++;
goto done;
} else {
derr->fme_status = DDI_FM_NONFATAL;
ndi_fm_acc_err_set(pbm_p->pbm_excl_handle, derr);
nonfatal++;
goto done;
}
} else if (derr->fme_flag == DDI_FM_ERR_PEEK) {
nonfatal++;
goto done;
} else if (derr->fme_flag == DDI_FM_ERR_POKE) {
if ((prierr == PSYCHO_PCI_AFSR_E_MA) && !secerr &&
(pbm_err.pbm_pci.pci_cfg_stat & PCI_STAT_R_MAST_AB)) {
nonfatal++;
goto done;
}
if ((prierr == PSYCHO_PCI_AFSR_E_TA) && !secerr &&
(pbm_err.pbm_pci.pci_cfg_stat & PCI_STAT_R_TARG_AB)) {
nonfatal++;
goto done;
}
}
if (prierr || secerr) {
ret = pbm_afsr_report(dip, derr->fme_ena, &pbm_err);
if (ret == DDI_FM_FATAL)
fatal++;
else
nonfatal++;
}
ret = pci_cfg_report(dip, derr, &pbm_err.pbm_pci, caller, prierr);
if (ret == DDI_FM_FATAL)
fatal++;
else if (ret == DDI_FM_NONFATAL)
nonfatal++;
pbm_ctl_stat = pbm_err.pbm_ctl_stat;
pci_cfg_stat = pbm_err.pbm_pci.pci_cfg_stat;
if (pbm_ctl_stat & COMMON_PCI_CTRL_SERR) {
if (derr->fme_flag != DDI_FM_ERR_POKE) {
pbm_err.pbm_pci.pci_err_class = PCI_REC_SERR;
(void) snprintf(buf, FM_MAX_CLASS, "%s.%s",
PCI_ERROR_SUBCLASS, pbm_err.pbm_pci.pci_err_class);
ddi_fm_ereport_post(dip, buf, derr->fme_ena,
DDI_NOSLEEP, FM_VERSION, DATA_TYPE_UINT8, 0,
PCI_CONFIG_STATUS, DATA_TYPE_UINT16, pci_cfg_stat,
PCI_CONFIG_COMMAND, DATA_TYPE_UINT16,
pbm_err.pbm_pci.pci_cfg_comm, PCI_PA,
DATA_TYPE_UINT64, (uint64_t)0, NULL);
}
unknown++;
}
if (pbm_ctl_stat & COMMON_PCI_CTRL_SBH_ERR) {
if (pci_panic_on_sbh_errors)
fatal++;
else
nonfatal++;
pbm_err.pbm_err_class = PCI_PSY_SBH;
pbm_ereport_post(dip, derr->fme_ena, &pbm_err);
}
done:
ret = ndi_fm_handler_dispatch(dip, NULL, derr);
if (ret == DDI_FM_FATAL) {
fatal++;
} else if (ret == DDI_FM_NONFATAL) {
nonfatal++;
} else if (ret == DDI_FM_UNKNOWN) {
unknown++;
}
if (unknown && !nonfatal && !fatal)
fatal++;
pci_clear_error(pci_p, &pbm_err);
return (fatal ? DDI_FM_FATAL : (nonfatal ? DDI_FM_NONFATAL :
(unknown ? DDI_FM_UNKNOWN : DDI_FM_OK)));
}
int
pci_check_error(pci_t *pci_p)
{
pbm_t *pbm_p = pci_p->pci_pbm_p;
uint16_t pci_cfg_stat;
uint64_t pbm_ctl_stat, pbm_afsr;
ASSERT(MUTEX_HELD(&pci_p->pci_common_p->pci_fm_mutex));
pci_cfg_stat = pbm_p->pbm_config_header->ch_status_reg;
pbm_ctl_stat = *pbm_p->pbm_ctrl_reg;
pbm_afsr = *pbm_p->pbm_async_flt_status_reg;
if ((pci_cfg_stat & (PCI_STAT_S_PERROR | PCI_STAT_S_TARG_AB |
PCI_STAT_R_TARG_AB | PCI_STAT_R_MAST_AB |
PCI_STAT_S_SYSERR | PCI_STAT_PERROR)) ||
(pbm_ctl_stat & (COMMON_PCI_CTRL_SBH_ERR |
COMMON_PCI_CTRL_SERR)) ||
(PBM_AFSR_TO_PRIERR(pbm_afsr)))
return (1);
return (0);
}
static void
pci_pbm_errstate_get(pci_t *pci_p, pbm_errstate_t *pbm_err_p)
{
pbm_t *pbm_p = pci_p->pci_pbm_p;
ASSERT(MUTEX_HELD(&pci_p->pci_common_p->pci_fm_mutex));
bzero(pbm_err_p, sizeof (pbm_errstate_t));
pbm_err_p->pbm_bridge_type = PCI_BRIDGE_TYPE(pci_p->pci_common_p);
pbm_err_p->pbm_pci.pci_cfg_stat =
pbm_p->pbm_config_header->ch_status_reg;
pbm_err_p->pbm_ctl_stat = *pbm_p->pbm_ctrl_reg;
pbm_err_p->pbm_pci.pci_cfg_comm =
pbm_p->pbm_config_header->ch_command_reg;
pbm_err_p->pbm_afsr = *pbm_p->pbm_async_flt_status_reg;
pbm_err_p->pbm_afar = *pbm_p->pbm_async_flt_addr_reg;
pbm_err_p->pbm_pci.pci_pa = *pbm_p->pbm_async_flt_addr_reg;
}
void
pbm_clear_error(pbm_t *pbm_p)
{
uint64_t pbm_afsr, pbm_ctl_stat;
pbm_ctl_stat = *pbm_p->pbm_ctrl_reg;
pbm_afsr = *pbm_p->pbm_async_flt_status_reg;
while (((pbm_afsr >> PSYCHO_PCI_AFSR_PE_SHIFT) &
(PSYCHO_PCI_AFSR_E_MA | PSYCHO_PCI_AFSR_E_TA)) ||
(pbm_ctl_stat & COMMON_PCI_CTRL_SERR)) {
pbm_ctl_stat = *pbm_p->pbm_ctrl_reg;
pbm_afsr = *pbm_p->pbm_async_flt_status_reg;
}
}
void
pci_format_addr(dev_info_t *dip, uint64_t *afar, uint64_t afsr)
{
}
int
pci_bus_quiesce(pci_t *pci_p, dev_info_t *dip, void *result)
{
return (DDI_FAILURE);
}
int
pci_bus_unquiesce(pci_t *pci_p, dev_info_t *dip, void *result)
{
return (DDI_FAILURE);
}
int
pci_reloc_getkey(void)
{
return (0x100);
}
void
pci_vmem_free(iommu_t *iommu_p, ddi_dma_impl_t *mp, void *dvma_addr,
size_t npages)
{
pci_vmem_do_free(iommu_p, dvma_addr, npages,
(mp->dmai_flags & DMAI_FLAGS_VMEMCACHE));
}
void
pci_thermal_rem_intr(dev_info_t *rdip, uint_t inum)
{
pci_t *pci_p;
dev_info_t *pdip;
uint32_t dev_mondo, pci_mondo;
int instance;
for (pdip = ddi_get_parent(rdip); pdip; pdip = ddi_get_parent(pdip)) {
if (strcmp(ddi_driver_name(pdip), "pcipsy") == 0)
break;
}
if (!pdip) {
cmn_err(CE_WARN, "pci_thermal_rem_intr() no pcipsy parent\n");
return;
}
instance = ddi_get_instance(pdip);
pci_p = get_pci_soft_state(instance);
dev_mondo = pci_xlate_intr(pci_p->pci_dip, rdip, pci_p->pci_ib_p,
IB_MONDO_TO_INO(i_ddi_get_inum(rdip, inum)));
pci_mondo = ((pci_p->pci_cb_p->cb_ign << PCI_INO_BITS) |
pci_p->pci_inos[CBNINTR_THERMAL]);
pci_mondo = CB_MONDO_TO_XMONDO(pci_p->pci_cb_p, pci_mondo);
if (pci_mondo == dev_mondo) {
DEBUG2(DBG_ATTACH, rdip, "pci_thermal_rem_intr unregistered "
"for dip=%s%d:", ddi_driver_name(rdip),
ddi_get_instance(rdip));
VERIFY(rem_ivintr(pci_mondo, pci_pil[CBNINTR_THERMAL]) == 0);
}
}
dma_bypass_addr_t
pci_iommu_bypass_end_configure(void)
{
return ((dma_bypass_addr_t)UPA_IOMMU_BYPASS_END);
}