#include <sys/conf.h>
#include <sys/mach_intr.h>
#include <sys/psm.h>
#include <sys/clock.h>
#include <sys/apic.h>
#include <io/pci/pci_var.h>
typedef struct pci_kstat_private {
ddi_intr_handle_impl_t *hdlp;
dev_info_t *rootnex_dip;
} pci_kstat_private_t;
static struct {
kstat_named_t ihks_name;
kstat_named_t ihks_type;
kstat_named_t ihks_cpu;
kstat_named_t ihks_pil;
kstat_named_t ihks_time;
kstat_named_t ihks_ino;
kstat_named_t ihks_cookie;
kstat_named_t ihks_devpath;
kstat_named_t ihks_buspath;
} pci_ks_template = {
{ "name", KSTAT_DATA_CHAR },
{ "type", KSTAT_DATA_CHAR },
{ "cpu", KSTAT_DATA_UINT64 },
{ "pil", KSTAT_DATA_UINT64 },
{ "time", KSTAT_DATA_UINT64 },
{ "ino", KSTAT_DATA_UINT64 },
{ "cookie", KSTAT_DATA_UINT64 },
{ "devpath", KSTAT_DATA_STRING },
{ "buspath", KSTAT_DATA_STRING },
};
static char ih_devpath[MAXPATHLEN];
static char ih_buspath[MAXPATHLEN];
static uint32_t pci_ks_inst;
static kmutex_t pci_ks_template_lock;
extern int (*psm_intr_ops)(dev_info_t *, ddi_intr_handle_impl_t *,
psm_intr_op_t, int *);
static int
pci_ih_ks_update(kstat_t *ksp, int rw)
{
pci_kstat_private_t *private_data =
(pci_kstat_private_t *)ksp->ks_private;
dev_info_t *rootnex_dip = private_data->rootnex_dip;
ddi_intr_handle_impl_t tmp_hdl, *ih_p = private_data->hdlp;
dev_info_t *dip = ih_p->ih_dip;
int maxlen = sizeof (pci_ks_template.ihks_name.value.c);
apic_get_intr_t intrinfo;
(void) snprintf(pci_ks_template.ihks_name.value.c, maxlen, "%s%d",
ddi_driver_name(dip), ddi_get_instance(dip));
(void) ddi_pathname(dip, ih_devpath);
(void) ddi_pathname(rootnex_dip, ih_buspath);
kstat_named_setstr(&pci_ks_template.ihks_devpath, ih_devpath);
kstat_named_setstr(&pci_ks_template.ihks_buspath, ih_buspath);
bcopy(ih_p, &tmp_hdl, sizeof (ddi_intr_handle_impl_t));
tmp_hdl.ih_private = (void *)&intrinfo;
intrinfo.avgi_cpu_id = 0;
intrinfo.avgi_req_flags = PSMGI_REQ_CPUID | PSMGI_REQ_VECTOR;
intrinfo.avgi_req_flags |= PSMGI_INTRBY_DEFAULT;
if ((ih_p->ih_state != DDI_IHDL_STATE_ENABLE) ||
((*psm_intr_ops)(NULL, &tmp_hdl, PSM_INTR_OP_GET_INTR, NULL) !=
DDI_SUCCESS) ||
(intrinfo.avgi_cpu_id & PSMGI_CPU_FLAGS)) {
(void) strcpy(pci_ks_template.ihks_type.value.c, "disabled");
pci_ks_template.ihks_pil.value.ui64 = 0;
pci_ks_template.ihks_time.value.ui64 = 0;
pci_ks_template.ihks_cookie.value.ui64 = 0;
pci_ks_template.ihks_cpu.value.ui64 = 0;
pci_ks_template.ihks_ino.value.ui64 = 0;
if (intrinfo.avgi_cpu_id & PSMGI_CPU_FLAGS)
(void) taskq_dispatch(system_taskq,
(void (*)(void *))pci_kstat_delete, ksp, TQ_SLEEP);
return (0);
}
switch (ih_p->ih_type) {
case DDI_INTR_TYPE_MSI:
(void) strcpy(pci_ks_template.ihks_type.value.c, "msi");
break;
case DDI_INTR_TYPE_MSIX:
(void) strcpy(pci_ks_template.ihks_type.value.c, "msix");
break;
default:
(void) strcpy(pci_ks_template.ihks_type.value.c, "fixed");
break;
}
pci_ks_template.ihks_pil.value.ui64 = ih_p->ih_pri;
pci_ks_template.ihks_time.value.ui64 =
((ihdl_plat_t *)ih_p->ih_private)->ip_ticks;
scalehrtime((hrtime_t *)&pci_ks_template.ihks_time.value.ui64);
pci_ks_template.ihks_cookie.value.ui64 = ih_p->ih_vector;
pci_ks_template.ihks_cpu.value.ui64 = intrinfo.avgi_cpu_id;
pci_ks_template.ihks_ino.value.ui64 = intrinfo.avgi_vector;
return (0);
}
void pci_kstat_create(kstat_t **kspp, dev_info_t *rootnex_dip,
ddi_intr_handle_impl_t *hdlp)
{
pci_kstat_private_t *private_data;
*kspp = kstat_create("pci_intrs", atomic_inc_32_nv(&pci_ks_inst),
_MODULE_NAME, "interrupts", KSTAT_TYPE_NAMED,
sizeof (pci_ks_template) / sizeof (kstat_named_t),
KSTAT_FLAG_VIRTUAL);
if (*kspp != NULL) {
private_data =
kmem_zalloc(sizeof (pci_kstat_private_t), KM_SLEEP);
private_data->hdlp = hdlp;
private_data->rootnex_dip = rootnex_dip;
(*kspp)->ks_private = private_data;
(*kspp)->ks_data_size += MAXPATHLEN * 2;
(*kspp)->ks_lock = &pci_ks_template_lock;
(*kspp)->ks_data = &pci_ks_template;
(*kspp)->ks_update = pci_ih_ks_update;
kstat_install(*kspp);
}
}
void
pci_kstat_delete(kstat_t *ksp)
{
pci_kstat_private_t *kstat_private;
ddi_intr_handle_impl_t *hdlp;
if (ksp) {
kstat_private = ksp->ks_private;
hdlp = kstat_private->hdlp;
((ihdl_plat_t *)hdlp->ih_private)->ip_ksp = NULL;
kstat_delete(ksp);
kmem_free(kstat_private, sizeof (pci_kstat_private_t));
}
}