1916Sschwartz /*
2916Sschwartz * CDDL HEADER START
3916Sschwartz *
4916Sschwartz * The contents of this file are subject to the terms of the
51811Sesolom * Common Development and Distribution License (the "License").
61811Sesolom * You may not use this file except in compliance with the License.
7916Sschwartz *
8916Sschwartz * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9916Sschwartz * or http://www.opensolaris.org/os/licensing.
10916Sschwartz * See the License for the specific language governing permissions
11916Sschwartz * and limitations under the License.
12916Sschwartz *
13916Sschwartz * When distributing Covered Code, include this CDDL HEADER in each
14916Sschwartz * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15916Sschwartz * If applicable, add the following below this CDDL HEADER, with the
16916Sschwartz * fields enclosed by brackets "[]" replaced with your own identifying
17916Sschwartz * information: Portions Copyright [yyyy] [name of copyright owner]
18916Sschwartz *
19916Sschwartz * CDDL HEADER END
20916Sschwartz */
21916Sschwartz /*
22*12825SJimmy.Vetayases@oracle.com * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
23916Sschwartz */
24916Sschwartz /*
25916Sschwartz * Kstat support for X86 PCI driver
26916Sschwartz */
27916Sschwartz
28916Sschwartz #include <sys/conf.h>
29916Sschwartz #include <sys/mach_intr.h>
30916Sschwartz #include <sys/psm.h>
31916Sschwartz #include <sys/clock.h>
323446Smrj #include <sys/apic.h>
33916Sschwartz #include <io/pci/pci_var.h>
34916Sschwartz
35916Sschwartz typedef struct pci_kstat_private {
36916Sschwartz ddi_intr_handle_impl_t *hdlp;
37916Sschwartz dev_info_t *rootnex_dip;
38916Sschwartz } pci_kstat_private_t;
39916Sschwartz
40916Sschwartz static struct {
41916Sschwartz kstat_named_t ihks_name;
42916Sschwartz kstat_named_t ihks_type;
43916Sschwartz kstat_named_t ihks_cpu;
44916Sschwartz kstat_named_t ihks_pil;
45916Sschwartz kstat_named_t ihks_time;
46916Sschwartz kstat_named_t ihks_ino;
47916Sschwartz kstat_named_t ihks_cookie;
48916Sschwartz kstat_named_t ihks_devpath;
49916Sschwartz kstat_named_t ihks_buspath;
50916Sschwartz } pci_ks_template = {
51916Sschwartz { "name", KSTAT_DATA_CHAR },
52916Sschwartz { "type", KSTAT_DATA_CHAR },
53916Sschwartz { "cpu", KSTAT_DATA_UINT64 },
54916Sschwartz { "pil", KSTAT_DATA_UINT64 },
55916Sschwartz { "time", KSTAT_DATA_UINT64 },
56916Sschwartz { "ino", KSTAT_DATA_UINT64 },
57916Sschwartz { "cookie", KSTAT_DATA_UINT64 },
58916Sschwartz { "devpath", KSTAT_DATA_STRING },
59916Sschwartz { "buspath", KSTAT_DATA_STRING },
60916Sschwartz };
61916Sschwartz
621811Sesolom static char ih_devpath[MAXPATHLEN];
631811Sesolom static char ih_buspath[MAXPATHLEN];
64916Sschwartz static uint32_t pci_ks_inst;
65916Sschwartz static kmutex_t pci_ks_template_lock;
66916Sschwartz
6712683SJimmy.Vetayases@oracle.com extern int (*psm_intr_ops)(dev_info_t *, ddi_intr_handle_impl_t *,
6812683SJimmy.Vetayases@oracle.com psm_intr_op_t, int *);
6912683SJimmy.Vetayases@oracle.com
70916Sschwartz /*ARGSUSED*/
71916Sschwartz static int
pci_ih_ks_update(kstat_t * ksp,int rw)72916Sschwartz pci_ih_ks_update(kstat_t *ksp, int rw)
73916Sschwartz {
744397Sschwartz pci_kstat_private_t *private_data =
754397Sschwartz (pci_kstat_private_t *)ksp->ks_private;
764397Sschwartz dev_info_t *rootnex_dip = private_data->rootnex_dip;
7712683SJimmy.Vetayases@oracle.com ddi_intr_handle_impl_t tmp_hdl, *ih_p = private_data->hdlp;
784397Sschwartz dev_info_t *dip = ih_p->ih_dip;
794397Sschwartz int maxlen = sizeof (pci_ks_template.ihks_name.value.c);
80916Sschwartz apic_get_intr_t intrinfo;
81916Sschwartz
82916Sschwartz (void) snprintf(pci_ks_template.ihks_name.value.c, maxlen, "%s%d",
83916Sschwartz ddi_driver_name(dip), ddi_get_instance(dip));
841087Sschwartz (void) ddi_pathname(dip, ih_devpath);
851087Sschwartz (void) ddi_pathname(rootnex_dip, ih_buspath);
861087Sschwartz kstat_named_setstr(&pci_ks_template.ihks_devpath, ih_devpath);
871087Sschwartz kstat_named_setstr(&pci_ks_template.ihks_buspath, ih_buspath);
88916Sschwartz
89916Sschwartz /*
90916Sschwartz * Note that although possibly multiple vectors can map to an IRQ, the
91916Sschwartz * vector returned below will always be the same for a given IRQ
92916Sschwartz * specified, and so all kstats for a given IRQ will report the same
93916Sschwartz * value for the ino field.
941087Sschwartz *
951087Sschwartz * ---
961087Sschwartz *
971087Sschwartz * Check for the enabled state, since kstats are set up when the ISR is
981087Sschwartz * added, not enabled. There may be a period where interrupts are not
991087Sschwartz * enabled even though they may have been added.
1001087Sschwartz *
1011087Sschwartz * It is also possible that the vector is for a dummy interrupt.
1021087Sschwartz * pci_get_intr_from_vecirq will return failure in this case.
103916Sschwartz */
10412683SJimmy.Vetayases@oracle.com bcopy(ih_p, &tmp_hdl, sizeof (ddi_intr_handle_impl_t));
10512683SJimmy.Vetayases@oracle.com tmp_hdl.ih_private = (void *)&intrinfo;
10612683SJimmy.Vetayases@oracle.com intrinfo.avgi_cpu_id = 0; /* In case psm_intr_ops fails */
107916Sschwartz intrinfo.avgi_req_flags = PSMGI_REQ_CPUID | PSMGI_REQ_VECTOR;
10812683SJimmy.Vetayases@oracle.com intrinfo.avgi_req_flags |= PSMGI_INTRBY_DEFAULT;
10912683SJimmy.Vetayases@oracle.com
1101087Sschwartz if ((ih_p->ih_state != DDI_IHDL_STATE_ENABLE) ||
11112683SJimmy.Vetayases@oracle.com ((*psm_intr_ops)(NULL, &tmp_hdl, PSM_INTR_OP_GET_INTR, NULL) !=
1124397Sschwartz DDI_SUCCESS) ||
1131087Sschwartz (intrinfo.avgi_cpu_id & PSMGI_CPU_FLAGS)) {
1141087Sschwartz
1151087Sschwartz (void) strcpy(pci_ks_template.ihks_type.value.c, "disabled");
1161087Sschwartz pci_ks_template.ihks_pil.value.ui64 = 0;
1171087Sschwartz pci_ks_template.ihks_time.value.ui64 = 0;
1181087Sschwartz pci_ks_template.ihks_cookie.value.ui64 = 0;
119916Sschwartz pci_ks_template.ihks_cpu.value.ui64 = 0;
1201087Sschwartz pci_ks_template.ihks_ino.value.ui64 = 0;
1211087Sschwartz
1221087Sschwartz /* Interrupt is user-bound. Remove kstat. */
1231087Sschwartz if (intrinfo.avgi_cpu_id & PSMGI_CPU_FLAGS)
1241087Sschwartz (void) taskq_dispatch(system_taskq,
1251087Sschwartz (void (*)(void *))pci_kstat_delete, ksp, TQ_SLEEP);
1261087Sschwartz
1271087Sschwartz return (0);
128916Sschwartz }
129916Sschwartz
1301087Sschwartz /*
1311087Sschwartz * Interrupt is valid (not a dummy), not user-bound to a specific cpu,
1321087Sschwartz * and enabled. Update kstat fields.
1331087Sschwartz */
1344397Sschwartz switch (ih_p->ih_type) {
1354397Sschwartz case DDI_INTR_TYPE_MSI:
1364397Sschwartz (void) strcpy(pci_ks_template.ihks_type.value.c, "msi");
1374397Sschwartz break;
1384397Sschwartz case DDI_INTR_TYPE_MSIX:
1394397Sschwartz (void) strcpy(pci_ks_template.ihks_type.value.c, "msix");
1404397Sschwartz break;
1414397Sschwartz default:
1424397Sschwartz (void) strcpy(pci_ks_template.ihks_type.value.c, "fixed");
1434397Sschwartz break;
1444397Sschwartz }
1451087Sschwartz pci_ks_template.ihks_pil.value.ui64 = ih_p->ih_pri;
1461087Sschwartz pci_ks_template.ihks_time.value.ui64 =
1471087Sschwartz ((ihdl_plat_t *)ih_p->ih_private)->ip_ticks;
1485084Sjohnlev scalehrtime((hrtime_t *)&pci_ks_template.ihks_time.value.ui64);
1491087Sschwartz pci_ks_template.ihks_cookie.value.ui64 = ih_p->ih_vector;
1501087Sschwartz /* CPU won't be user bound at this point. */
1511087Sschwartz pci_ks_template.ihks_cpu.value.ui64 = intrinfo.avgi_cpu_id;
1521087Sschwartz pci_ks_template.ihks_ino.value.ui64 = intrinfo.avgi_vector;
153916Sschwartz
154916Sschwartz return (0);
155916Sschwartz }
156916Sschwartz
157916Sschwartz
pci_kstat_create(kstat_t ** kspp,dev_info_t * rootnex_dip,ddi_intr_handle_impl_t * hdlp)158916Sschwartz void pci_kstat_create(kstat_t **kspp, dev_info_t *rootnex_dip,
159916Sschwartz ddi_intr_handle_impl_t *hdlp)
160916Sschwartz {
161916Sschwartz pci_kstat_private_t *private_data;
162916Sschwartz
163916Sschwartz *kspp = kstat_create("pci_intrs", atomic_inc_32_nv(&pci_ks_inst),
1641813Sschwartz _MODULE_NAME, "interrupts", KSTAT_TYPE_NAMED,
165916Sschwartz sizeof (pci_ks_template) / sizeof (kstat_named_t),
166916Sschwartz KSTAT_FLAG_VIRTUAL);
167916Sschwartz if (*kspp != NULL) {
168916Sschwartz
169916Sschwartz private_data =
170916Sschwartz kmem_zalloc(sizeof (pci_kstat_private_t), KM_SLEEP);
171916Sschwartz private_data->hdlp = hdlp;
172916Sschwartz private_data->rootnex_dip = rootnex_dip;
173916Sschwartz
174916Sschwartz (*kspp)->ks_private = private_data;
175916Sschwartz (*kspp)->ks_data_size += MAXPATHLEN * 2;
176916Sschwartz (*kspp)->ks_lock = &pci_ks_template_lock;
177916Sschwartz (*kspp)->ks_data = &pci_ks_template;
178916Sschwartz (*kspp)->ks_update = pci_ih_ks_update;
179916Sschwartz kstat_install(*kspp);
180916Sschwartz }
181916Sschwartz }
182916Sschwartz
1831087Sschwartz
1841087Sschwartz /*
1851087Sschwartz * This function is invoked in two ways:
1861087Sschwartz * - Thru taskq thread via pci_ih_ks_update to remove kstats for user-bound
1871087Sschwartz * interrupts.
1881087Sschwartz * - From the REMISR introp when an interrupt is being removed.
1891087Sschwartz */
190916Sschwartz void
pci_kstat_delete(kstat_t * ksp)191916Sschwartz pci_kstat_delete(kstat_t *ksp)
192916Sschwartz {
1934397Sschwartz pci_kstat_private_t *kstat_private;
1944397Sschwartz ddi_intr_handle_impl_t *hdlp;
195916Sschwartz
196916Sschwartz if (ksp) {
197916Sschwartz kstat_private = ksp->ks_private;
1981087Sschwartz hdlp = kstat_private->hdlp;
1991087Sschwartz ((ihdl_plat_t *)hdlp->ih_private)->ip_ksp = NULL;
200916Sschwartz
201916Sschwartz /*
202916Sschwartz * Delete the kstat before removing the private pointer, to
203916Sschwartz * prevent a kstat update from coming after private is freed.
204916Sschwartz */
205916Sschwartz kstat_delete(ksp);
206916Sschwartz
2071087Sschwartz kmem_free(kstat_private, sizeof (pci_kstat_private_t));
208916Sschwartz }
209916Sschwartz }
210