7a364d25fde47aa82704b12b5251bf7fac37f02eschwartz/*
7a364d25fde47aa82704b12b5251bf7fac37f02eschwartz * CDDL HEADER START
7a364d25fde47aa82704b12b5251bf7fac37f02eschwartz *
7a364d25fde47aa82704b12b5251bf7fac37f02eschwartz * The contents of this file are subject to the terms of the
d48713b83f032afcef6785303e68f293eacd5671esolom * Common Development and Distribution License (the "License").
d48713b83f032afcef6785303e68f293eacd5671esolom * You may not use this file except in compliance with the License.
7a364d25fde47aa82704b12b5251bf7fac37f02eschwartz *
7a364d25fde47aa82704b12b5251bf7fac37f02eschwartz * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
7a364d25fde47aa82704b12b5251bf7fac37f02eschwartz * or http://www.opensolaris.org/os/licensing.
7a364d25fde47aa82704b12b5251bf7fac37f02eschwartz * See the License for the specific language governing permissions
7a364d25fde47aa82704b12b5251bf7fac37f02eschwartz * and limitations under the License.
7a364d25fde47aa82704b12b5251bf7fac37f02eschwartz *
7a364d25fde47aa82704b12b5251bf7fac37f02eschwartz * When distributing Covered Code, include this CDDL HEADER in each
7a364d25fde47aa82704b12b5251bf7fac37f02eschwartz * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
7a364d25fde47aa82704b12b5251bf7fac37f02eschwartz * If applicable, add the following below this CDDL HEADER, with the
7a364d25fde47aa82704b12b5251bf7fac37f02eschwartz * fields enclosed by brackets "[]" replaced with your own identifying
7a364d25fde47aa82704b12b5251bf7fac37f02eschwartz * information: Portions Copyright [yyyy] [name of copyright owner]
7a364d25fde47aa82704b12b5251bf7fac37f02eschwartz *
7a364d25fde47aa82704b12b5251bf7fac37f02eschwartz * CDDL HEADER END
7a364d25fde47aa82704b12b5251bf7fac37f02eschwartz */
7a364d25fde47aa82704b12b5251bf7fac37f02eschwartz/*
5cd376e8b7030707d78315f63adb4bb2b4d9963eJimmy Vetayases * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
7a364d25fde47aa82704b12b5251bf7fac37f02eschwartz */
7a364d25fde47aa82704b12b5251bf7fac37f02eschwartz/*
7a364d25fde47aa82704b12b5251bf7fac37f02eschwartz * Kstat support for X86 PCI driver
7a364d25fde47aa82704b12b5251bf7fac37f02eschwartz */
7a364d25fde47aa82704b12b5251bf7fac37f02eschwartz
7a364d25fde47aa82704b12b5251bf7fac37f02eschwartz#include <sys/conf.h>
7a364d25fde47aa82704b12b5251bf7fac37f02eschwartz#include <sys/mach_intr.h>
7a364d25fde47aa82704b12b5251bf7fac37f02eschwartz#include <sys/psm.h>
7a364d25fde47aa82704b12b5251bf7fac37f02eschwartz#include <sys/clock.h>
ae115bc77f6fcde83175c75b4206dc2e50747966mrj#include <sys/apic.h>
7a364d25fde47aa82704b12b5251bf7fac37f02eschwartz#include <io/pci/pci_var.h>
7a364d25fde47aa82704b12b5251bf7fac37f02eschwartz
7a364d25fde47aa82704b12b5251bf7fac37f02eschwartztypedef struct pci_kstat_private {
7a364d25fde47aa82704b12b5251bf7fac37f02eschwartz ddi_intr_handle_impl_t *hdlp;
7a364d25fde47aa82704b12b5251bf7fac37f02eschwartz dev_info_t *rootnex_dip;
7a364d25fde47aa82704b12b5251bf7fac37f02eschwartz} pci_kstat_private_t;
7a364d25fde47aa82704b12b5251bf7fac37f02eschwartz
7a364d25fde47aa82704b12b5251bf7fac37f02eschwartzstatic struct {
7a364d25fde47aa82704b12b5251bf7fac37f02eschwartz kstat_named_t ihks_name;
7a364d25fde47aa82704b12b5251bf7fac37f02eschwartz kstat_named_t ihks_type;
7a364d25fde47aa82704b12b5251bf7fac37f02eschwartz kstat_named_t ihks_cpu;
7a364d25fde47aa82704b12b5251bf7fac37f02eschwartz kstat_named_t ihks_pil;
7a364d25fde47aa82704b12b5251bf7fac37f02eschwartz kstat_named_t ihks_time;
7a364d25fde47aa82704b12b5251bf7fac37f02eschwartz kstat_named_t ihks_ino;
7a364d25fde47aa82704b12b5251bf7fac37f02eschwartz kstat_named_t ihks_cookie;
7a364d25fde47aa82704b12b5251bf7fac37f02eschwartz kstat_named_t ihks_devpath;
7a364d25fde47aa82704b12b5251bf7fac37f02eschwartz kstat_named_t ihks_buspath;
7a364d25fde47aa82704b12b5251bf7fac37f02eschwartz} pci_ks_template = {
7a364d25fde47aa82704b12b5251bf7fac37f02eschwartz { "name", KSTAT_DATA_CHAR },
7a364d25fde47aa82704b12b5251bf7fac37f02eschwartz { "type", KSTAT_DATA_CHAR },
7a364d25fde47aa82704b12b5251bf7fac37f02eschwartz { "cpu", KSTAT_DATA_UINT64 },
7a364d25fde47aa82704b12b5251bf7fac37f02eschwartz { "pil", KSTAT_DATA_UINT64 },
7a364d25fde47aa82704b12b5251bf7fac37f02eschwartz { "time", KSTAT_DATA_UINT64 },
7a364d25fde47aa82704b12b5251bf7fac37f02eschwartz { "ino", KSTAT_DATA_UINT64 },
7a364d25fde47aa82704b12b5251bf7fac37f02eschwartz { "cookie", KSTAT_DATA_UINT64 },
7a364d25fde47aa82704b12b5251bf7fac37f02eschwartz { "devpath", KSTAT_DATA_STRING },
7a364d25fde47aa82704b12b5251bf7fac37f02eschwartz { "buspath", KSTAT_DATA_STRING },
7a364d25fde47aa82704b12b5251bf7fac37f02eschwartz};
7a364d25fde47aa82704b12b5251bf7fac37f02eschwartz
d48713b83f032afcef6785303e68f293eacd5671esolomstatic char ih_devpath[MAXPATHLEN];
d48713b83f032afcef6785303e68f293eacd5671esolomstatic char ih_buspath[MAXPATHLEN];
7a364d25fde47aa82704b12b5251bf7fac37f02eschwartzstatic uint32_t pci_ks_inst;
7a364d25fde47aa82704b12b5251bf7fac37f02eschwartzstatic kmutex_t pci_ks_template_lock;
7a364d25fde47aa82704b12b5251bf7fac37f02eschwartz
7ff178cd8db129d385d3177eb20744d3b6efc59bJimmy Vetayasesextern int (*psm_intr_ops)(dev_info_t *, ddi_intr_handle_impl_t *,
7ff178cd8db129d385d3177eb20744d3b6efc59bJimmy Vetayases psm_intr_op_t, int *);
7ff178cd8db129d385d3177eb20744d3b6efc59bJimmy Vetayases
7a364d25fde47aa82704b12b5251bf7fac37f02eschwartz/*ARGSUSED*/
7a364d25fde47aa82704b12b5251bf7fac37f02eschwartzstatic int
7a364d25fde47aa82704b12b5251bf7fac37f02eschwartzpci_ih_ks_update(kstat_t *ksp, int rw)
7a364d25fde47aa82704b12b5251bf7fac37f02eschwartz{
2917a9c9c3eee6fcaedb239f5f68da01f4ed0da9schwartz pci_kstat_private_t *private_data =
2917a9c9c3eee6fcaedb239f5f68da01f4ed0da9schwartz (pci_kstat_private_t *)ksp->ks_private;
2917a9c9c3eee6fcaedb239f5f68da01f4ed0da9schwartz dev_info_t *rootnex_dip = private_data->rootnex_dip;
7ff178cd8db129d385d3177eb20744d3b6efc59bJimmy Vetayases ddi_intr_handle_impl_t tmp_hdl, *ih_p = private_data->hdlp;
2917a9c9c3eee6fcaedb239f5f68da01f4ed0da9schwartz dev_info_t *dip = ih_p->ih_dip;
2917a9c9c3eee6fcaedb239f5f68da01f4ed0da9schwartz int maxlen = sizeof (pci_ks_template.ihks_name.value.c);
7a364d25fde47aa82704b12b5251bf7fac37f02eschwartz apic_get_intr_t intrinfo;
7a364d25fde47aa82704b12b5251bf7fac37f02eschwartz
7a364d25fde47aa82704b12b5251bf7fac37f02eschwartz (void) snprintf(pci_ks_template.ihks_name.value.c, maxlen, "%s%d",
7a364d25fde47aa82704b12b5251bf7fac37f02eschwartz ddi_driver_name(dip), ddi_get_instance(dip));
e1d9f4e6832768425b218c917c09c9afaed8ae36schwartz (void) ddi_pathname(dip, ih_devpath);
e1d9f4e6832768425b218c917c09c9afaed8ae36schwartz (void) ddi_pathname(rootnex_dip, ih_buspath);
e1d9f4e6832768425b218c917c09c9afaed8ae36schwartz kstat_named_setstr(&pci_ks_template.ihks_devpath, ih_devpath);
e1d9f4e6832768425b218c917c09c9afaed8ae36schwartz kstat_named_setstr(&pci_ks_template.ihks_buspath, ih_buspath);
7a364d25fde47aa82704b12b5251bf7fac37f02eschwartz
7a364d25fde47aa82704b12b5251bf7fac37f02eschwartz /*
7a364d25fde47aa82704b12b5251bf7fac37f02eschwartz * Note that although possibly multiple vectors can map to an IRQ, the
7a364d25fde47aa82704b12b5251bf7fac37f02eschwartz * vector returned below will always be the same for a given IRQ
7a364d25fde47aa82704b12b5251bf7fac37f02eschwartz * specified, and so all kstats for a given IRQ will report the same
7a364d25fde47aa82704b12b5251bf7fac37f02eschwartz * value for the ino field.
e1d9f4e6832768425b218c917c09c9afaed8ae36schwartz *
e1d9f4e6832768425b218c917c09c9afaed8ae36schwartz * ---
e1d9f4e6832768425b218c917c09c9afaed8ae36schwartz *
e1d9f4e6832768425b218c917c09c9afaed8ae36schwartz * Check for the enabled state, since kstats are set up when the ISR is
e1d9f4e6832768425b218c917c09c9afaed8ae36schwartz * added, not enabled. There may be a period where interrupts are not
e1d9f4e6832768425b218c917c09c9afaed8ae36schwartz * enabled even though they may have been added.
e1d9f4e6832768425b218c917c09c9afaed8ae36schwartz *
e1d9f4e6832768425b218c917c09c9afaed8ae36schwartz * It is also possible that the vector is for a dummy interrupt.
e1d9f4e6832768425b218c917c09c9afaed8ae36schwartz * pci_get_intr_from_vecirq will return failure in this case.
7a364d25fde47aa82704b12b5251bf7fac37f02eschwartz */
7ff178cd8db129d385d3177eb20744d3b6efc59bJimmy Vetayases bcopy(ih_p, &tmp_hdl, sizeof (ddi_intr_handle_impl_t));
7ff178cd8db129d385d3177eb20744d3b6efc59bJimmy Vetayases tmp_hdl.ih_private = (void *)&intrinfo;
7ff178cd8db129d385d3177eb20744d3b6efc59bJimmy Vetayases intrinfo.avgi_cpu_id = 0; /* In case psm_intr_ops fails */
7a364d25fde47aa82704b12b5251bf7fac37f02eschwartz intrinfo.avgi_req_flags = PSMGI_REQ_CPUID | PSMGI_REQ_VECTOR;
7ff178cd8db129d385d3177eb20744d3b6efc59bJimmy Vetayases intrinfo.avgi_req_flags |= PSMGI_INTRBY_DEFAULT;
7ff178cd8db129d385d3177eb20744d3b6efc59bJimmy Vetayases
e1d9f4e6832768425b218c917c09c9afaed8ae36schwartz if ((ih_p->ih_state != DDI_IHDL_STATE_ENABLE) ||
7ff178cd8db129d385d3177eb20744d3b6efc59bJimmy Vetayases ((*psm_intr_ops)(NULL, &tmp_hdl, PSM_INTR_OP_GET_INTR, NULL) !=
2917a9c9c3eee6fcaedb239f5f68da01f4ed0da9schwartz DDI_SUCCESS) ||
e1d9f4e6832768425b218c917c09c9afaed8ae36schwartz (intrinfo.avgi_cpu_id & PSMGI_CPU_FLAGS)) {
e1d9f4e6832768425b218c917c09c9afaed8ae36schwartz
e1d9f4e6832768425b218c917c09c9afaed8ae36schwartz (void) strcpy(pci_ks_template.ihks_type.value.c, "disabled");
e1d9f4e6832768425b218c917c09c9afaed8ae36schwartz pci_ks_template.ihks_pil.value.ui64 = 0;
e1d9f4e6832768425b218c917c09c9afaed8ae36schwartz pci_ks_template.ihks_time.value.ui64 = 0;
e1d9f4e6832768425b218c917c09c9afaed8ae36schwartz pci_ks_template.ihks_cookie.value.ui64 = 0;
7a364d25fde47aa82704b12b5251bf7fac37f02eschwartz pci_ks_template.ihks_cpu.value.ui64 = 0;
e1d9f4e6832768425b218c917c09c9afaed8ae36schwartz pci_ks_template.ihks_ino.value.ui64 = 0;
e1d9f4e6832768425b218c917c09c9afaed8ae36schwartz
e1d9f4e6832768425b218c917c09c9afaed8ae36schwartz /* Interrupt is user-bound. Remove kstat. */
e1d9f4e6832768425b218c917c09c9afaed8ae36schwartz if (intrinfo.avgi_cpu_id & PSMGI_CPU_FLAGS)
e1d9f4e6832768425b218c917c09c9afaed8ae36schwartz (void) taskq_dispatch(system_taskq,
e1d9f4e6832768425b218c917c09c9afaed8ae36schwartz (void (*)(void *))pci_kstat_delete, ksp, TQ_SLEEP);
e1d9f4e6832768425b218c917c09c9afaed8ae36schwartz
e1d9f4e6832768425b218c917c09c9afaed8ae36schwartz return (0);
7a364d25fde47aa82704b12b5251bf7fac37f02eschwartz }
7a364d25fde47aa82704b12b5251bf7fac37f02eschwartz
e1d9f4e6832768425b218c917c09c9afaed8ae36schwartz /*
e1d9f4e6832768425b218c917c09c9afaed8ae36schwartz * Interrupt is valid (not a dummy), not user-bound to a specific cpu,
e1d9f4e6832768425b218c917c09c9afaed8ae36schwartz * and enabled. Update kstat fields.
e1d9f4e6832768425b218c917c09c9afaed8ae36schwartz */
2917a9c9c3eee6fcaedb239f5f68da01f4ed0da9schwartz switch (ih_p->ih_type) {
2917a9c9c3eee6fcaedb239f5f68da01f4ed0da9schwartz case DDI_INTR_TYPE_MSI:
2917a9c9c3eee6fcaedb239f5f68da01f4ed0da9schwartz (void) strcpy(pci_ks_template.ihks_type.value.c, "msi");
2917a9c9c3eee6fcaedb239f5f68da01f4ed0da9schwartz break;
2917a9c9c3eee6fcaedb239f5f68da01f4ed0da9schwartz case DDI_INTR_TYPE_MSIX:
2917a9c9c3eee6fcaedb239f5f68da01f4ed0da9schwartz (void) strcpy(pci_ks_template.ihks_type.value.c, "msix");
2917a9c9c3eee6fcaedb239f5f68da01f4ed0da9schwartz break;
2917a9c9c3eee6fcaedb239f5f68da01f4ed0da9schwartz default:
2917a9c9c3eee6fcaedb239f5f68da01f4ed0da9schwartz (void) strcpy(pci_ks_template.ihks_type.value.c, "fixed");
2917a9c9c3eee6fcaedb239f5f68da01f4ed0da9schwartz break;
2917a9c9c3eee6fcaedb239f5f68da01f4ed0da9schwartz }
e1d9f4e6832768425b218c917c09c9afaed8ae36schwartz pci_ks_template.ihks_pil.value.ui64 = ih_p->ih_pri;
e1d9f4e6832768425b218c917c09c9afaed8ae36schwartz pci_ks_template.ihks_time.value.ui64 =
e1d9f4e6832768425b218c917c09c9afaed8ae36schwartz ((ihdl_plat_t *)ih_p->ih_private)->ip_ticks;
843e19887f64dde75055cf8842fc4db2171eff45johnlev scalehrtime((hrtime_t *)&pci_ks_template.ihks_time.value.ui64);
e1d9f4e6832768425b218c917c09c9afaed8ae36schwartz pci_ks_template.ihks_cookie.value.ui64 = ih_p->ih_vector;
e1d9f4e6832768425b218c917c09c9afaed8ae36schwartz /* CPU won't be user bound at this point. */
e1d9f4e6832768425b218c917c09c9afaed8ae36schwartz pci_ks_template.ihks_cpu.value.ui64 = intrinfo.avgi_cpu_id;
e1d9f4e6832768425b218c917c09c9afaed8ae36schwartz pci_ks_template.ihks_ino.value.ui64 = intrinfo.avgi_vector;
7a364d25fde47aa82704b12b5251bf7fac37f02eschwartz
7a364d25fde47aa82704b12b5251bf7fac37f02eschwartz return (0);
7a364d25fde47aa82704b12b5251bf7fac37f02eschwartz}
7a364d25fde47aa82704b12b5251bf7fac37f02eschwartz
7a364d25fde47aa82704b12b5251bf7fac37f02eschwartz
7a364d25fde47aa82704b12b5251bf7fac37f02eschwartzvoid pci_kstat_create(kstat_t **kspp, dev_info_t *rootnex_dip,
7a364d25fde47aa82704b12b5251bf7fac37f02eschwartz ddi_intr_handle_impl_t *hdlp)
7a364d25fde47aa82704b12b5251bf7fac37f02eschwartz{
7a364d25fde47aa82704b12b5251bf7fac37f02eschwartz pci_kstat_private_t *private_data;
7a364d25fde47aa82704b12b5251bf7fac37f02eschwartz
7a364d25fde47aa82704b12b5251bf7fac37f02eschwartz *kspp = kstat_create("pci_intrs", atomic_inc_32_nv(&pci_ks_inst),
d89fccd8788afe1e920f842edd883fe192a1b8feschwartz _MODULE_NAME, "interrupts", KSTAT_TYPE_NAMED,
7a364d25fde47aa82704b12b5251bf7fac37f02eschwartz sizeof (pci_ks_template) / sizeof (kstat_named_t),
7a364d25fde47aa82704b12b5251bf7fac37f02eschwartz KSTAT_FLAG_VIRTUAL);
7a364d25fde47aa82704b12b5251bf7fac37f02eschwartz if (*kspp != NULL) {
7a364d25fde47aa82704b12b5251bf7fac37f02eschwartz
7a364d25fde47aa82704b12b5251bf7fac37f02eschwartz private_data =
7a364d25fde47aa82704b12b5251bf7fac37f02eschwartz kmem_zalloc(sizeof (pci_kstat_private_t), KM_SLEEP);
7a364d25fde47aa82704b12b5251bf7fac37f02eschwartz private_data->hdlp = hdlp;
7a364d25fde47aa82704b12b5251bf7fac37f02eschwartz private_data->rootnex_dip = rootnex_dip;
7a364d25fde47aa82704b12b5251bf7fac37f02eschwartz
7a364d25fde47aa82704b12b5251bf7fac37f02eschwartz (*kspp)->ks_private = private_data;
7a364d25fde47aa82704b12b5251bf7fac37f02eschwartz (*kspp)->ks_data_size += MAXPATHLEN * 2;
7a364d25fde47aa82704b12b5251bf7fac37f02eschwartz (*kspp)->ks_lock = &pci_ks_template_lock;
7a364d25fde47aa82704b12b5251bf7fac37f02eschwartz (*kspp)->ks_data = &pci_ks_template;
7a364d25fde47aa82704b12b5251bf7fac37f02eschwartz (*kspp)->ks_update = pci_ih_ks_update;
7a364d25fde47aa82704b12b5251bf7fac37f02eschwartz kstat_install(*kspp);
7a364d25fde47aa82704b12b5251bf7fac37f02eschwartz }
7a364d25fde47aa82704b12b5251bf7fac37f02eschwartz}
7a364d25fde47aa82704b12b5251bf7fac37f02eschwartz
e1d9f4e6832768425b218c917c09c9afaed8ae36schwartz
e1d9f4e6832768425b218c917c09c9afaed8ae36schwartz/*
e1d9f4e6832768425b218c917c09c9afaed8ae36schwartz * This function is invoked in two ways:
e1d9f4e6832768425b218c917c09c9afaed8ae36schwartz * - Thru taskq thread via pci_ih_ks_update to remove kstats for user-bound
e1d9f4e6832768425b218c917c09c9afaed8ae36schwartz * interrupts.
e1d9f4e6832768425b218c917c09c9afaed8ae36schwartz * - From the REMISR introp when an interrupt is being removed.
e1d9f4e6832768425b218c917c09c9afaed8ae36schwartz */
7a364d25fde47aa82704b12b5251bf7fac37f02eschwartzvoid
7a364d25fde47aa82704b12b5251bf7fac37f02eschwartzpci_kstat_delete(kstat_t *ksp)
7a364d25fde47aa82704b12b5251bf7fac37f02eschwartz{
2917a9c9c3eee6fcaedb239f5f68da01f4ed0da9schwartz pci_kstat_private_t *kstat_private;
2917a9c9c3eee6fcaedb239f5f68da01f4ed0da9schwartz ddi_intr_handle_impl_t *hdlp;
7a364d25fde47aa82704b12b5251bf7fac37f02eschwartz
7a364d25fde47aa82704b12b5251bf7fac37f02eschwartz if (ksp) {
7a364d25fde47aa82704b12b5251bf7fac37f02eschwartz kstat_private = ksp->ks_private;
e1d9f4e6832768425b218c917c09c9afaed8ae36schwartz hdlp = kstat_private->hdlp;
e1d9f4e6832768425b218c917c09c9afaed8ae36schwartz ((ihdl_plat_t *)hdlp->ih_private)->ip_ksp = NULL;
7a364d25fde47aa82704b12b5251bf7fac37f02eschwartz
7a364d25fde47aa82704b12b5251bf7fac37f02eschwartz /*
7a364d25fde47aa82704b12b5251bf7fac37f02eschwartz * Delete the kstat before removing the private pointer, to
7a364d25fde47aa82704b12b5251bf7fac37f02eschwartz * prevent a kstat update from coming after private is freed.
7a364d25fde47aa82704b12b5251bf7fac37f02eschwartz */
7a364d25fde47aa82704b12b5251bf7fac37f02eschwartz kstat_delete(ksp);
7a364d25fde47aa82704b12b5251bf7fac37f02eschwartz
e1d9f4e6832768425b218c917c09c9afaed8ae36schwartz kmem_free(kstat_private, sizeof (pci_kstat_private_t));
7a364d25fde47aa82704b12b5251bf7fac37f02eschwartz }
7a364d25fde47aa82704b12b5251bf7fac37f02eschwartz}