17a364d25Sschwartz /*
27a364d25Sschwartz * CDDL HEADER START
37a364d25Sschwartz *
47a364d25Sschwartz * The contents of this file are subject to the terms of the
5d48713b8Sesolom * Common Development and Distribution License (the "License").
6d48713b8Sesolom * You may not use this file except in compliance with the License.
77a364d25Sschwartz *
87a364d25Sschwartz * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97a364d25Sschwartz * or http://www.opensolaris.org/os/licensing.
107a364d25Sschwartz * See the License for the specific language governing permissions
117a364d25Sschwartz * and limitations under the License.
127a364d25Sschwartz *
137a364d25Sschwartz * When distributing Covered Code, include this CDDL HEADER in each
147a364d25Sschwartz * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157a364d25Sschwartz * If applicable, add the following below this CDDL HEADER, with the
167a364d25Sschwartz * fields enclosed by brackets "[]" replaced with your own identifying
177a364d25Sschwartz * information: Portions Copyright [yyyy] [name of copyright owner]
187a364d25Sschwartz *
197a364d25Sschwartz * CDDL HEADER END
207a364d25Sschwartz */
217a364d25Sschwartz /*
225cd376e8SJimmy Vetayases * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
237a364d25Sschwartz */
247a364d25Sschwartz /*
257a364d25Sschwartz * Kstat support for X86 PCI driver
267a364d25Sschwartz */
277a364d25Sschwartz
287a364d25Sschwartz #include <sys/conf.h>
297a364d25Sschwartz #include <sys/mach_intr.h>
307a364d25Sschwartz #include <sys/psm.h>
317a364d25Sschwartz #include <sys/clock.h>
32ae115bc7Smrj #include <sys/apic.h>
337a364d25Sschwartz #include <io/pci/pci_var.h>
347a364d25Sschwartz
357a364d25Sschwartz typedef struct pci_kstat_private {
367a364d25Sschwartz ddi_intr_handle_impl_t *hdlp;
377a364d25Sschwartz dev_info_t *rootnex_dip;
387a364d25Sschwartz } pci_kstat_private_t;
397a364d25Sschwartz
407a364d25Sschwartz static struct {
417a364d25Sschwartz kstat_named_t ihks_name;
427a364d25Sschwartz kstat_named_t ihks_type;
437a364d25Sschwartz kstat_named_t ihks_cpu;
447a364d25Sschwartz kstat_named_t ihks_pil;
457a364d25Sschwartz kstat_named_t ihks_time;
467a364d25Sschwartz kstat_named_t ihks_ino;
477a364d25Sschwartz kstat_named_t ihks_cookie;
487a364d25Sschwartz kstat_named_t ihks_devpath;
497a364d25Sschwartz kstat_named_t ihks_buspath;
507a364d25Sschwartz } pci_ks_template = {
517a364d25Sschwartz { "name", KSTAT_DATA_CHAR },
527a364d25Sschwartz { "type", KSTAT_DATA_CHAR },
537a364d25Sschwartz { "cpu", KSTAT_DATA_UINT64 },
547a364d25Sschwartz { "pil", KSTAT_DATA_UINT64 },
557a364d25Sschwartz { "time", KSTAT_DATA_UINT64 },
567a364d25Sschwartz { "ino", KSTAT_DATA_UINT64 },
577a364d25Sschwartz { "cookie", KSTAT_DATA_UINT64 },
587a364d25Sschwartz { "devpath", KSTAT_DATA_STRING },
597a364d25Sschwartz { "buspath", KSTAT_DATA_STRING },
607a364d25Sschwartz };
617a364d25Sschwartz
62d48713b8Sesolom static char ih_devpath[MAXPATHLEN];
63d48713b8Sesolom static char ih_buspath[MAXPATHLEN];
647a364d25Sschwartz static uint32_t pci_ks_inst;
657a364d25Sschwartz static kmutex_t pci_ks_template_lock;
667a364d25Sschwartz
67*7ff178cdSJimmy Vetayases extern int (*psm_intr_ops)(dev_info_t *, ddi_intr_handle_impl_t *,
68*7ff178cdSJimmy Vetayases psm_intr_op_t, int *);
69*7ff178cdSJimmy Vetayases
707a364d25Sschwartz /*ARGSUSED*/
717a364d25Sschwartz static int
pci_ih_ks_update(kstat_t * ksp,int rw)727a364d25Sschwartz pci_ih_ks_update(kstat_t *ksp, int rw)
737a364d25Sschwartz {
747a364d25Sschwartz pci_kstat_private_t *private_data =
757a364d25Sschwartz (pci_kstat_private_t *)ksp->ks_private;
767a364d25Sschwartz dev_info_t *rootnex_dip = private_data->rootnex_dip;
77*7ff178cdSJimmy Vetayases ddi_intr_handle_impl_t tmp_hdl, *ih_p = private_data->hdlp;
787a364d25Sschwartz dev_info_t *dip = ih_p->ih_dip;
792917a9c9Sschwartz int maxlen = sizeof (pci_ks_template.ihks_name.value.c);
807a364d25Sschwartz apic_get_intr_t intrinfo;
817a364d25Sschwartz
827a364d25Sschwartz (void) snprintf(pci_ks_template.ihks_name.value.c, maxlen, "%s%d",
837a364d25Sschwartz ddi_driver_name(dip), ddi_get_instance(dip));
84e1d9f4e6Sschwartz (void) ddi_pathname(dip, ih_devpath);
85e1d9f4e6Sschwartz (void) ddi_pathname(rootnex_dip, ih_buspath);
86e1d9f4e6Sschwartz kstat_named_setstr(&pci_ks_template.ihks_devpath, ih_devpath);
87e1d9f4e6Sschwartz kstat_named_setstr(&pci_ks_template.ihks_buspath, ih_buspath);
887a364d25Sschwartz
897a364d25Sschwartz /*
907a364d25Sschwartz * Note that although possibly multiple vectors can map to an IRQ, the
917a364d25Sschwartz * vector returned below will always be the same for a given IRQ
927a364d25Sschwartz * specified, and so all kstats for a given IRQ will report the same
937a364d25Sschwartz * value for the ino field.
94e1d9f4e6Sschwartz *
95e1d9f4e6Sschwartz * ---
96e1d9f4e6Sschwartz *
97e1d9f4e6Sschwartz * Check for the enabled state, since kstats are set up when the ISR is
98e1d9f4e6Sschwartz * added, not enabled. There may be a period where interrupts are not
99e1d9f4e6Sschwartz * enabled even though they may have been added.
100e1d9f4e6Sschwartz *
101e1d9f4e6Sschwartz * It is also possible that the vector is for a dummy interrupt.
102e1d9f4e6Sschwartz * pci_get_intr_from_vecirq will return failure in this case.
1037a364d25Sschwartz */
104*7ff178cdSJimmy Vetayases bcopy(ih_p, &tmp_hdl, sizeof (ddi_intr_handle_impl_t));
105*7ff178cdSJimmy Vetayases tmp_hdl.ih_private = (void *)&intrinfo;
106*7ff178cdSJimmy Vetayases intrinfo.avgi_cpu_id = 0; /* In case psm_intr_ops fails */
1077a364d25Sschwartz intrinfo.avgi_req_flags = PSMGI_REQ_CPUID | PSMGI_REQ_VECTOR;
108*7ff178cdSJimmy Vetayases intrinfo.avgi_req_flags |= PSMGI_INTRBY_DEFAULT;
109*7ff178cdSJimmy Vetayases
110e1d9f4e6Sschwartz if ((ih_p->ih_state != DDI_IHDL_STATE_ENABLE) ||
111*7ff178cdSJimmy Vetayases ((*psm_intr_ops)(NULL, &tmp_hdl, PSM_INTR_OP_GET_INTR, NULL) !=
112e1d9f4e6Sschwartz DDI_SUCCESS) ||
113e1d9f4e6Sschwartz (intrinfo.avgi_cpu_id & PSMGI_CPU_FLAGS)) {
114e1d9f4e6Sschwartz
115e1d9f4e6Sschwartz (void) strcpy(pci_ks_template.ihks_type.value.c, "disabled");
116e1d9f4e6Sschwartz pci_ks_template.ihks_pil.value.ui64 = 0;
117e1d9f4e6Sschwartz pci_ks_template.ihks_time.value.ui64 = 0;
118e1d9f4e6Sschwartz pci_ks_template.ihks_cookie.value.ui64 = 0;
1197a364d25Sschwartz pci_ks_template.ihks_cpu.value.ui64 = 0;
120e1d9f4e6Sschwartz pci_ks_template.ihks_ino.value.ui64 = 0;
121e1d9f4e6Sschwartz
122e1d9f4e6Sschwartz /* Interrupt is user-bound. Remove kstat. */
123e1d9f4e6Sschwartz if (intrinfo.avgi_cpu_id & PSMGI_CPU_FLAGS)
124e1d9f4e6Sschwartz (void) taskq_dispatch(system_taskq,
125e1d9f4e6Sschwartz (void (*)(void *))pci_kstat_delete, ksp, TQ_SLEEP);
126e1d9f4e6Sschwartz
127e1d9f4e6Sschwartz return (0);
1287a364d25Sschwartz }
1297a364d25Sschwartz
130e1d9f4e6Sschwartz /*
131e1d9f4e6Sschwartz * Interrupt is valid (not a dummy), not user-bound to a specific cpu,
132e1d9f4e6Sschwartz * and enabled. Update kstat fields.
133e1d9f4e6Sschwartz */
1342917a9c9Sschwartz switch (ih_p->ih_type) {
1352917a9c9Sschwartz case DDI_INTR_TYPE_MSI:
1362917a9c9Sschwartz (void) strcpy(pci_ks_template.ihks_type.value.c, "msi");
1372917a9c9Sschwartz break;
1382917a9c9Sschwartz case DDI_INTR_TYPE_MSIX:
1392917a9c9Sschwartz (void) strcpy(pci_ks_template.ihks_type.value.c, "msix");
1402917a9c9Sschwartz break;
1412917a9c9Sschwartz default:
1422917a9c9Sschwartz (void) strcpy(pci_ks_template.ihks_type.value.c, "fixed");
1432917a9c9Sschwartz break;
1442917a9c9Sschwartz }
145e1d9f4e6Sschwartz pci_ks_template.ihks_pil.value.ui64 = ih_p->ih_pri;
146e1d9f4e6Sschwartz pci_ks_template.ihks_time.value.ui64 =
147e1d9f4e6Sschwartz ((ihdl_plat_t *)ih_p->ih_private)->ip_ticks;
148843e1988Sjohnlev scalehrtime((hrtime_t *)&pci_ks_template.ihks_time.value.ui64);
149e1d9f4e6Sschwartz pci_ks_template.ihks_cookie.value.ui64 = ih_p->ih_vector;
150e1d9f4e6Sschwartz /* CPU won't be user bound at this point. */
151e1d9f4e6Sschwartz pci_ks_template.ihks_cpu.value.ui64 = intrinfo.avgi_cpu_id;
152e1d9f4e6Sschwartz pci_ks_template.ihks_ino.value.ui64 = intrinfo.avgi_vector;
1537a364d25Sschwartz
1547a364d25Sschwartz return (0);
1557a364d25Sschwartz }
1567a364d25Sschwartz
1577a364d25Sschwartz
pci_kstat_create(kstat_t ** kspp,dev_info_t * rootnex_dip,ddi_intr_handle_impl_t * hdlp)1587a364d25Sschwartz void pci_kstat_create(kstat_t **kspp, dev_info_t *rootnex_dip,
1597a364d25Sschwartz ddi_intr_handle_impl_t *hdlp)
1607a364d25Sschwartz {
1617a364d25Sschwartz pci_kstat_private_t *private_data;
1627a364d25Sschwartz
1637a364d25Sschwartz *kspp = kstat_create("pci_intrs", atomic_inc_32_nv(&pci_ks_inst),
164d89fccd8Sschwartz _MODULE_NAME, "interrupts", KSTAT_TYPE_NAMED,
1657a364d25Sschwartz sizeof (pci_ks_template) / sizeof (kstat_named_t),
1667a364d25Sschwartz KSTAT_FLAG_VIRTUAL);
1677a364d25Sschwartz if (*kspp != NULL) {
1687a364d25Sschwartz
1697a364d25Sschwartz private_data =
1707a364d25Sschwartz kmem_zalloc(sizeof (pci_kstat_private_t), KM_SLEEP);
1717a364d25Sschwartz private_data->hdlp = hdlp;
1727a364d25Sschwartz private_data->rootnex_dip = rootnex_dip;
1737a364d25Sschwartz
1747a364d25Sschwartz (*kspp)->ks_private = private_data;
1757a364d25Sschwartz (*kspp)->ks_data_size += MAXPATHLEN * 2;
1767a364d25Sschwartz (*kspp)->ks_lock = &pci_ks_template_lock;
1777a364d25Sschwartz (*kspp)->ks_data = &pci_ks_template;
1787a364d25Sschwartz (*kspp)->ks_update = pci_ih_ks_update;
1797a364d25Sschwartz kstat_install(*kspp);
1807a364d25Sschwartz }
1817a364d25Sschwartz }
1827a364d25Sschwartz
183e1d9f4e6Sschwartz
184e1d9f4e6Sschwartz /*
185e1d9f4e6Sschwartz * This function is invoked in two ways:
186e1d9f4e6Sschwartz * - Thru taskq thread via pci_ih_ks_update to remove kstats for user-bound
187e1d9f4e6Sschwartz * interrupts.
188e1d9f4e6Sschwartz * - From the REMISR introp when an interrupt is being removed.
189e1d9f4e6Sschwartz */
1907a364d25Sschwartz void
pci_kstat_delete(kstat_t * ksp)1917a364d25Sschwartz pci_kstat_delete(kstat_t *ksp)
1927a364d25Sschwartz {
1937a364d25Sschwartz pci_kstat_private_t *kstat_private;
194e1d9f4e6Sschwartz ddi_intr_handle_impl_t *hdlp;
1957a364d25Sschwartz
1967a364d25Sschwartz if (ksp) {
1977a364d25Sschwartz kstat_private = ksp->ks_private;
198e1d9f4e6Sschwartz hdlp = kstat_private->hdlp;
199e1d9f4e6Sschwartz ((ihdl_plat_t *)hdlp->ih_private)->ip_ksp = NULL;
2007a364d25Sschwartz
2017a364d25Sschwartz /*
2027a364d25Sschwartz * Delete the kstat before removing the private pointer, to
2037a364d25Sschwartz * prevent a kstat update from coming after private is freed.
2047a364d25Sschwartz */
2057a364d25Sschwartz kstat_delete(ksp);
2067a364d25Sschwartz
2077a364d25Sschwartz kmem_free(kstat_private, sizeof (pci_kstat_private_t));
2087a364d25Sschwartz }
2097a364d25Sschwartz }
210