17c478bd9Sstevel@tonic-gate /* 27c478bd9Sstevel@tonic-gate * CDDL HEADER START 37c478bd9Sstevel@tonic-gate * 47c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the 5102cb92eSjohnny * Common Development and Distribution License (the "License"). 6102cb92eSjohnny * You may not use this file except in compliance with the License. 77c478bd9Sstevel@tonic-gate * 87c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 97c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 107c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions 117c478bd9Sstevel@tonic-gate * and limitations under the License. 127c478bd9Sstevel@tonic-gate * 137c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 147c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 157c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 167c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 177c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 187c478bd9Sstevel@tonic-gate * 197c478bd9Sstevel@tonic-gate * CDDL HEADER END 207c478bd9Sstevel@tonic-gate */ 217c478bd9Sstevel@tonic-gate /* 2218061c65Sgd * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 237c478bd9Sstevel@tonic-gate * Use is subject to license terms. 247c478bd9Sstevel@tonic-gate */ 257c478bd9Sstevel@tonic-gate 267c478bd9Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 277c478bd9Sstevel@tonic-gate 287c478bd9Sstevel@tonic-gate /* 297c478bd9Sstevel@tonic-gate * PX nexus interrupt handling: 307c478bd9Sstevel@tonic-gate * PX device interrupt handler wrapper 317c478bd9Sstevel@tonic-gate * PIL lookup routine 327c478bd9Sstevel@tonic-gate * PX device interrupt related initchild code 337c478bd9Sstevel@tonic-gate */ 347c478bd9Sstevel@tonic-gate 357c478bd9Sstevel@tonic-gate #include <sys/types.h> 367c478bd9Sstevel@tonic-gate #include <sys/kmem.h> 377c478bd9Sstevel@tonic-gate #include <sys/async.h> 387c478bd9Sstevel@tonic-gate #include <sys/spl.h> 397c478bd9Sstevel@tonic-gate #include <sys/sunddi.h> 40f8d2de6bSjchu #include <sys/fm/protocol.h> 41f8d2de6bSjchu #include <sys/fm/util.h> 427c478bd9Sstevel@tonic-gate #include <sys/machsystm.h> /* e_ddi_nodeid_to_dip() */ 437c478bd9Sstevel@tonic-gate #include <sys/ddi_impldefs.h> 447c478bd9Sstevel@tonic-gate #include <sys/sdt.h> 457c478bd9Sstevel@tonic-gate #include <sys/atomic.h> 467c478bd9Sstevel@tonic-gate #include "px_obj.h" 47f8d2de6bSjchu #include <sys/ontrap.h> 48f8d2de6bSjchu #include <sys/membar.h> 496d44af1bSesolom #include <sys/clock.h> 507c478bd9Sstevel@tonic-gate 517c478bd9Sstevel@tonic-gate /* 527c478bd9Sstevel@tonic-gate * interrupt jabber: 537c478bd9Sstevel@tonic-gate * 547c478bd9Sstevel@tonic-gate * When an interrupt line is jabbering, every time the state machine for the 557c478bd9Sstevel@tonic-gate * associated ino is idled, a new mondo will be sent and the ino will go into 567c478bd9Sstevel@tonic-gate * the pending state again. The mondo will cause a new call to 577c478bd9Sstevel@tonic-gate * px_intr_wrapper() which normally idles the ino's state machine which would 587c478bd9Sstevel@tonic-gate * precipitate another trip round the loop. 597c478bd9Sstevel@tonic-gate * 607c478bd9Sstevel@tonic-gate * The loop can be broken by preventing the ino's state machine from being 617c478bd9Sstevel@tonic-gate * idled when an interrupt line is jabbering. See the comment at the 627c478bd9Sstevel@tonic-gate * beginning of px_intr_wrapper() explaining how the 'interrupt jabber 637c478bd9Sstevel@tonic-gate * protection' code does this. 647c478bd9Sstevel@tonic-gate */ 657c478bd9Sstevel@tonic-gate 667c478bd9Sstevel@tonic-gate /*LINTLIBRARY*/ 677c478bd9Sstevel@tonic-gate 687c478bd9Sstevel@tonic-gate /* 697c478bd9Sstevel@tonic-gate * If the unclaimed interrupt count has reached the limit set by 707c478bd9Sstevel@tonic-gate * pci_unclaimed_intr_max within the time limit, then all interrupts 717c478bd9Sstevel@tonic-gate * on this ino is blocked by not idling the interrupt state machine. 727c478bd9Sstevel@tonic-gate */ 737c478bd9Sstevel@tonic-gate static int 74b0fc0e77Sgovinda px_spurintr(px_ino_pil_t *ipil_p) 757c478bd9Sstevel@tonic-gate { 76b0fc0e77Sgovinda px_ino_t *ino_p = ipil_p->ipil_ino_p; 77b0fc0e77Sgovinda px_ih_t *ih_p = ipil_p->ipil_ih_start; 78b0fc0e77Sgovinda px_t *px_p = ino_p->ino_ib_p->ib_px_p; 79b0fc0e77Sgovinda char *err_fmt_str; 80b0fc0e77Sgovinda boolean_t blocked = B_FALSE; 81b0fc0e77Sgovinda int i; 827c478bd9Sstevel@tonic-gate 83b0fc0e77Sgovinda if (ino_p->ino_unclaimed_intrs > px_unclaimed_intr_max) 847c478bd9Sstevel@tonic-gate return (DDI_INTR_CLAIMED); 857c478bd9Sstevel@tonic-gate 86b0fc0e77Sgovinda if (!ino_p->ino_unclaimed_intrs) 877c478bd9Sstevel@tonic-gate ino_p->ino_spurintr_begin = ddi_get_lbolt(); 887c478bd9Sstevel@tonic-gate 89b0fc0e77Sgovinda ino_p->ino_unclaimed_intrs++; 907c478bd9Sstevel@tonic-gate 91b0fc0e77Sgovinda if (ino_p->ino_unclaimed_intrs <= px_unclaimed_intr_max) 927c478bd9Sstevel@tonic-gate goto clear; 937c478bd9Sstevel@tonic-gate 947c478bd9Sstevel@tonic-gate if (drv_hztousec(ddi_get_lbolt() - ino_p->ino_spurintr_begin) 957c478bd9Sstevel@tonic-gate > px_spurintr_duration) { 96b0fc0e77Sgovinda ino_p->ino_unclaimed_intrs = 0; 977c478bd9Sstevel@tonic-gate goto clear; 987c478bd9Sstevel@tonic-gate } 997c478bd9Sstevel@tonic-gate err_fmt_str = "%s%d: ino 0x%x blocked"; 100b0fc0e77Sgovinda blocked = B_TRUE; 1017c478bd9Sstevel@tonic-gate goto warn; 1027c478bd9Sstevel@tonic-gate clear: 1037c478bd9Sstevel@tonic-gate err_fmt_str = "!%s%d: spurious interrupt from ino 0x%x"; 1047c478bd9Sstevel@tonic-gate warn: 1057c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, err_fmt_str, NAMEINST(px_p->px_dip), ino_p->ino_ino); 106b0fc0e77Sgovinda for (i = 0; i < ipil_p->ipil_ih_size; i++, ih_p = ih_p->ih_next) 1077c478bd9Sstevel@tonic-gate cmn_err(CE_CONT, "!%s-%d#%x ", NAMEINST(ih_p->ih_dip), 1087c478bd9Sstevel@tonic-gate ih_p->ih_inum); 1097c478bd9Sstevel@tonic-gate cmn_err(CE_CONT, "!\n"); 110b0fc0e77Sgovinda 111b0fc0e77Sgovinda /* Clear the pending state */ 112b0fc0e77Sgovinda if (blocked == B_FALSE) { 113b0fc0e77Sgovinda if (px_lib_intr_setstate(px_p->px_dip, ino_p->ino_sysino, 114b0fc0e77Sgovinda INTR_IDLE_STATE) != DDI_SUCCESS) 115b0fc0e77Sgovinda return (DDI_INTR_UNCLAIMED); 116b0fc0e77Sgovinda } 117b0fc0e77Sgovinda 1187c478bd9Sstevel@tonic-gate return (DDI_INTR_CLAIMED); 1197c478bd9Sstevel@tonic-gate } 1207c478bd9Sstevel@tonic-gate 1217c478bd9Sstevel@tonic-gate extern uint64_t intr_get_time(void); 1227c478bd9Sstevel@tonic-gate 1237c478bd9Sstevel@tonic-gate /* 124a195726fSgovinda * px_intx_intr (INTx or legacy interrupt handler) 1257c478bd9Sstevel@tonic-gate * 1267c478bd9Sstevel@tonic-gate * This routine is used as wrapper around interrupt handlers installed by child 1277c478bd9Sstevel@tonic-gate * device drivers. This routine invokes the driver interrupt handlers and 1287c478bd9Sstevel@tonic-gate * examines the return codes. 1297c478bd9Sstevel@tonic-gate * 1307c478bd9Sstevel@tonic-gate * There is a count of unclaimed interrupts kept on a per-ino basis. If at 1317c478bd9Sstevel@tonic-gate * least one handler claims the interrupt then the counter is halved and the 1327c478bd9Sstevel@tonic-gate * interrupt state machine is idled. If no handler claims the interrupt then 1337c478bd9Sstevel@tonic-gate * the counter is incremented by one and the state machine is idled. 1347c478bd9Sstevel@tonic-gate * If the count ever reaches the limit value set by pci_unclaimed_intr_max 1357c478bd9Sstevel@tonic-gate * then the interrupt state machine is not idled thus preventing any further 1367c478bd9Sstevel@tonic-gate * interrupts on that ino. The state machine will only be idled again if a 1377c478bd9Sstevel@tonic-gate * handler is subsequently added or removed. 1387c478bd9Sstevel@tonic-gate * 1397c478bd9Sstevel@tonic-gate * return value: DDI_INTR_CLAIMED if any handlers claimed the interrupt, 1407c478bd9Sstevel@tonic-gate * DDI_INTR_UNCLAIMED otherwise. 1417c478bd9Sstevel@tonic-gate */ 1427c478bd9Sstevel@tonic-gate uint_t 1437c478bd9Sstevel@tonic-gate px_intx_intr(caddr_t arg) 1447c478bd9Sstevel@tonic-gate { 145b0fc0e77Sgovinda px_ino_pil_t *ipil_p = (px_ino_pil_t *)arg; 146b0fc0e77Sgovinda px_ino_t *ino_p = ipil_p->ipil_ino_p; 1477c478bd9Sstevel@tonic-gate px_t *px_p = ino_p->ino_ib_p->ib_px_p; 148b0fc0e77Sgovinda px_ih_t *ih_p = ipil_p->ipil_ih_start; 149b0fc0e77Sgovinda ushort_t pil = ipil_p->ipil_pil; 150b0fc0e77Sgovinda uint_t result = 0, r = DDI_INTR_UNCLAIMED; 1517c478bd9Sstevel@tonic-gate int i; 1527c478bd9Sstevel@tonic-gate 1537c478bd9Sstevel@tonic-gate DBG(DBG_INTX_INTR, px_p->px_dip, "px_intx_intr:" 1547c478bd9Sstevel@tonic-gate "ino=%x sysino=%llx pil=%x ih_size=%x ih_lst=%x\n", 155b0fc0e77Sgovinda ino_p->ino_ino, ino_p->ino_sysino, ipil_p->ipil_pil, 156b0fc0e77Sgovinda ipil_p->ipil_ih_size, ipil_p->ipil_ih_head); 1577c478bd9Sstevel@tonic-gate 158b0fc0e77Sgovinda for (i = 0; i < ipil_p->ipil_ih_size; i++, ih_p = ih_p->ih_next) { 1597c478bd9Sstevel@tonic-gate dev_info_t *dip = ih_p->ih_dip; 1607c478bd9Sstevel@tonic-gate uint_t (*handler)() = ih_p->ih_handler; 1617c478bd9Sstevel@tonic-gate caddr_t arg1 = ih_p->ih_handler_arg1; 1627c478bd9Sstevel@tonic-gate caddr_t arg2 = ih_p->ih_handler_arg2; 1637c478bd9Sstevel@tonic-gate 1647c478bd9Sstevel@tonic-gate if (ih_p->ih_intr_state == PX_INTR_STATE_DISABLE) { 1657c478bd9Sstevel@tonic-gate DBG(DBG_INTX_INTR, px_p->px_dip, 1667c478bd9Sstevel@tonic-gate "px_intx_intr: %s%d interrupt %d is disabled\n", 1677c478bd9Sstevel@tonic-gate ddi_driver_name(dip), ddi_get_instance(dip), 1687c478bd9Sstevel@tonic-gate ino_p->ino_ino); 1697c478bd9Sstevel@tonic-gate 1707c478bd9Sstevel@tonic-gate continue; 1717c478bd9Sstevel@tonic-gate } 1727c478bd9Sstevel@tonic-gate 1737c478bd9Sstevel@tonic-gate DBG(DBG_INTX_INTR, px_p->px_dip, "px_intx_intr:" 1747c478bd9Sstevel@tonic-gate "ino=%x handler=%p arg1 =%p arg2 = %p\n", 1757c478bd9Sstevel@tonic-gate ino_p->ino_ino, handler, arg1, arg2); 1767c478bd9Sstevel@tonic-gate 1777c478bd9Sstevel@tonic-gate DTRACE_PROBE4(interrupt__start, dev_info_t, dip, 1787c478bd9Sstevel@tonic-gate void *, handler, caddr_t, arg1, caddr_t, arg2); 1797c478bd9Sstevel@tonic-gate 1807c478bd9Sstevel@tonic-gate r = (*handler)(arg1, arg2); 1817c478bd9Sstevel@tonic-gate 1827c478bd9Sstevel@tonic-gate /* 1837c478bd9Sstevel@tonic-gate * Account for time used by this interrupt. Protect against 1847c478bd9Sstevel@tonic-gate * conflicting writes to ih_ticks from ib_intr_dist_all() by 1857c478bd9Sstevel@tonic-gate * using atomic ops. 1867c478bd9Sstevel@tonic-gate */ 1877c478bd9Sstevel@tonic-gate 188b0fc0e77Sgovinda if (pil <= LOCK_LEVEL) 1897c478bd9Sstevel@tonic-gate atomic_add_64(&ih_p->ih_ticks, intr_get_time()); 1907c478bd9Sstevel@tonic-gate 1917c478bd9Sstevel@tonic-gate DTRACE_PROBE4(interrupt__complete, dev_info_t, dip, 1927c478bd9Sstevel@tonic-gate void *, handler, caddr_t, arg1, int, r); 1937c478bd9Sstevel@tonic-gate 1947c478bd9Sstevel@tonic-gate result += r; 1957c478bd9Sstevel@tonic-gate 1967c478bd9Sstevel@tonic-gate if (px_check_all_handlers) 1977c478bd9Sstevel@tonic-gate continue; 1987c478bd9Sstevel@tonic-gate if (result) 1997c478bd9Sstevel@tonic-gate break; 2007c478bd9Sstevel@tonic-gate } 2017c478bd9Sstevel@tonic-gate 202b0fc0e77Sgovinda if (result) 203b0fc0e77Sgovinda ino_p->ino_claimed |= (1 << pil); 204b0fc0e77Sgovinda 205b0fc0e77Sgovinda /* Interrupt can only be cleared after all pil levels are handled */ 206b0fc0e77Sgovinda if (pil != ino_p->ino_lopil) 207b0fc0e77Sgovinda return (DDI_INTR_CLAIMED); 208b0fc0e77Sgovinda 209b0fc0e77Sgovinda if (!ino_p->ino_claimed) { 210b0fc0e77Sgovinda if (px_unclaimed_intr_block) 211b0fc0e77Sgovinda return (px_spurintr(ipil_p)); 212b0fc0e77Sgovinda } 2137c478bd9Sstevel@tonic-gate 214b0fc0e77Sgovinda ino_p->ino_unclaimed_intrs = 0; 215b0fc0e77Sgovinda ino_p->ino_claimed = 0; 2167c478bd9Sstevel@tonic-gate 2177c478bd9Sstevel@tonic-gate /* Clear the pending state */ 218b0fc0e77Sgovinda if (px_lib_intr_setstate(px_p->px_dip, 2197c478bd9Sstevel@tonic-gate ino_p->ino_sysino, INTR_IDLE_STATE) != DDI_SUCCESS) 2207c478bd9Sstevel@tonic-gate return (DDI_INTR_UNCLAIMED); 2217c478bd9Sstevel@tonic-gate 2227c478bd9Sstevel@tonic-gate return (DDI_INTR_CLAIMED); 2237c478bd9Sstevel@tonic-gate } 2247c478bd9Sstevel@tonic-gate 2257c478bd9Sstevel@tonic-gate /* 226a195726fSgovinda * px_msiq_intr (MSI/X or PCIe MSG interrupt handler) 2277c478bd9Sstevel@tonic-gate * 2287c478bd9Sstevel@tonic-gate * This routine is used as wrapper around interrupt handlers installed by child 2297c478bd9Sstevel@tonic-gate * device drivers. This routine invokes the driver interrupt handlers and 2307c478bd9Sstevel@tonic-gate * examines the return codes. 2317c478bd9Sstevel@tonic-gate * 2327c478bd9Sstevel@tonic-gate * There is a count of unclaimed interrupts kept on a per-ino basis. If at 2337c478bd9Sstevel@tonic-gate * least one handler claims the interrupt then the counter is halved and the 2347c478bd9Sstevel@tonic-gate * interrupt state machine is idled. If no handler claims the interrupt then 2357c478bd9Sstevel@tonic-gate * the counter is incremented by one and the state machine is idled. 2367c478bd9Sstevel@tonic-gate * If the count ever reaches the limit value set by pci_unclaimed_intr_max 2377c478bd9Sstevel@tonic-gate * then the interrupt state machine is not idled thus preventing any further 2387c478bd9Sstevel@tonic-gate * interrupts on that ino. The state machine will only be idled again if a 2397c478bd9Sstevel@tonic-gate * handler is subsequently added or removed. 2407c478bd9Sstevel@tonic-gate * 2417c478bd9Sstevel@tonic-gate * return value: DDI_INTR_CLAIMED if any handlers claimed the interrupt, 2427c478bd9Sstevel@tonic-gate * DDI_INTR_UNCLAIMED otherwise. 2437c478bd9Sstevel@tonic-gate */ 2447c478bd9Sstevel@tonic-gate uint_t 2457c478bd9Sstevel@tonic-gate px_msiq_intr(caddr_t arg) 2467c478bd9Sstevel@tonic-gate { 247b0fc0e77Sgovinda px_ino_pil_t *ipil_p = (px_ino_pil_t *)arg; 248b0fc0e77Sgovinda px_ino_t *ino_p = ipil_p->ipil_ino_p; 2497c478bd9Sstevel@tonic-gate px_t *px_p = ino_p->ino_ib_p->ib_px_p; 2507c478bd9Sstevel@tonic-gate px_msiq_state_t *msiq_state_p = &px_p->px_ib_p->ib_msiq_state; 2517c478bd9Sstevel@tonic-gate px_msiq_t *msiq_p = ino_p->ino_msiq_p; 2527c478bd9Sstevel@tonic-gate dev_info_t *dip = px_p->px_dip; 253b0fc0e77Sgovinda ushort_t pil = ipil_p->ipil_pil; 2547c478bd9Sstevel@tonic-gate msiq_rec_t msiq_rec, *msiq_rec_p = &msiq_rec; 255023ccc1eSegillett msiqhead_t *curr_head_p; 256023ccc1eSegillett msiqtail_t curr_tail_index; 2577c478bd9Sstevel@tonic-gate msgcode_t msg_code; 2587c478bd9Sstevel@tonic-gate px_ih_t *ih_p; 259b0fc0e77Sgovinda uint_t ret = DDI_INTR_UNCLAIMED; 260b0fc0e77Sgovinda int i, j; 2617c478bd9Sstevel@tonic-gate 2627c478bd9Sstevel@tonic-gate DBG(DBG_MSIQ_INTR, dip, "px_msiq_intr: msiq_id =%x ino=%x pil=%x " 2637c478bd9Sstevel@tonic-gate "ih_size=%x ih_lst=%x\n", msiq_p->msiq_id, ino_p->ino_ino, 264b0fc0e77Sgovinda ipil_p->ipil_pil, ipil_p->ipil_ih_size, ipil_p->ipil_ih_head); 265b0fc0e77Sgovinda 266b0fc0e77Sgovinda /* 267b0fc0e77Sgovinda * The px_msiq_intr() handles multiple interrupt priorities and it 268b0fc0e77Sgovinda * will set msiq->msiq_rec2process to the number of MSIQ records to 269b0fc0e77Sgovinda * process while handling the highest priority interrupt. Subsequent 270b0fc0e77Sgovinda * lower priority interrupts will just process any unprocessed MSIQ 271b0fc0e77Sgovinda * records or will just return immediately. 272b0fc0e77Sgovinda */ 273b0fc0e77Sgovinda if (msiq_p->msiq_recs2process == 0) { 274b0fc0e77Sgovinda /* Read current MSIQ tail index */ 275b0fc0e77Sgovinda px_lib_msiq_gettail(dip, msiq_p->msiq_id, &curr_tail_index); 276b0fc0e77Sgovinda msiq_p->msiq_new_head_index = msiq_p->msiq_curr_head_index; 277b0fc0e77Sgovinda 278b0fc0e77Sgovinda if (curr_tail_index < msiq_p->msiq_curr_head_index) 279b0fc0e77Sgovinda curr_tail_index += msiq_state_p->msiq_rec_cnt; 2807c478bd9Sstevel@tonic-gate 281b0fc0e77Sgovinda msiq_p->msiq_recs2process = curr_tail_index - 282b0fc0e77Sgovinda msiq_p->msiq_curr_head_index; 283b0fc0e77Sgovinda } 284b0fc0e77Sgovinda 285b0fc0e77Sgovinda DBG(DBG_MSIQ_INTR, dip, "px_msiq_intr: curr_head %x new_head %x " 286b0fc0e77Sgovinda "rec2process %x\n", msiq_p->msiq_curr_head_index, 287b0fc0e77Sgovinda msiq_p->msiq_new_head_index, msiq_p->msiq_recs2process); 2887c478bd9Sstevel@tonic-gate 289b0fc0e77Sgovinda /* If all MSIQ records are already processed, just return immediately */ 290b0fc0e77Sgovinda if ((msiq_p->msiq_new_head_index - msiq_p->msiq_curr_head_index) 291b0fc0e77Sgovinda == msiq_p->msiq_recs2process) 292b0fc0e77Sgovinda goto intr_done; 293b0fc0e77Sgovinda 294b0fc0e77Sgovinda curr_head_p = (msiqhead_t *)((caddr_t)msiq_p->msiq_base_p + 295b0fc0e77Sgovinda (msiq_p->msiq_curr_head_index * sizeof (msiq_rec_t))); 2967c478bd9Sstevel@tonic-gate 2977c478bd9Sstevel@tonic-gate /* 298023ccc1eSegillett * Calculate the number of recs to process by taking the difference 299023ccc1eSegillett * between the head and tail pointers. For all records we always 300023ccc1eSegillett * verify that we have a valid record type before we do any processing. 301b0fc0e77Sgovinda * If triggered, we should always have at least one valid record. 3027c478bd9Sstevel@tonic-gate */ 303b0fc0e77Sgovinda for (i = 0; i < msiq_p->msiq_recs2process; i++) { 304b0fc0e77Sgovinda /* Read next MSIQ record */ 305023ccc1eSegillett px_lib_get_msiq_rec(dip, curr_head_p, msiq_rec_p); 306023ccc1eSegillett 3077c478bd9Sstevel@tonic-gate DBG(DBG_MSIQ_INTR, dip, "px_msiq_intr: MSIQ RECORD, " 3087c478bd9Sstevel@tonic-gate "msiq_rec_type 0x%llx msiq_rec_rid 0x%llx\n", 3097c478bd9Sstevel@tonic-gate msiq_rec_p->msiq_rec_type, msiq_rec_p->msiq_rec_rid); 3107c478bd9Sstevel@tonic-gate 311023ccc1eSegillett if (!msiq_rec_p->msiq_rec_type) 312b0fc0e77Sgovinda goto next_rec; 3137c478bd9Sstevel@tonic-gate 3147c478bd9Sstevel@tonic-gate /* Check MSIQ record type */ 3157c478bd9Sstevel@tonic-gate switch (msiq_rec_p->msiq_rec_type) { 3167c478bd9Sstevel@tonic-gate case MSG_REC: 3177c478bd9Sstevel@tonic-gate msg_code = msiq_rec_p->msiq_rec_data.msg.msg_code; 3187c478bd9Sstevel@tonic-gate DBG(DBG_MSIQ_INTR, dip, "px_msiq_intr: PCIE MSG " 3197c478bd9Sstevel@tonic-gate "record, msg type 0x%x\n", msg_code); 3207c478bd9Sstevel@tonic-gate break; 3217c478bd9Sstevel@tonic-gate case MSI32_REC: 3227c478bd9Sstevel@tonic-gate case MSI64_REC: 3237c478bd9Sstevel@tonic-gate msg_code = msiq_rec_p->msiq_rec_data.msi.msi_data; 3247c478bd9Sstevel@tonic-gate DBG(DBG_MSIQ_INTR, dip, "px_msiq_intr: MSI record, " 3257c478bd9Sstevel@tonic-gate "msi 0x%x\n", msg_code); 3267c478bd9Sstevel@tonic-gate 3277c478bd9Sstevel@tonic-gate /* Clear MSI state */ 3287c478bd9Sstevel@tonic-gate px_lib_msi_setstate(dip, (msinum_t)msg_code, 3297c478bd9Sstevel@tonic-gate PCI_MSI_STATE_IDLE); 3307c478bd9Sstevel@tonic-gate break; 3317c478bd9Sstevel@tonic-gate default: 3327c478bd9Sstevel@tonic-gate msg_code = 0; 3337c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "%s%d: px_msiq_intr: 0x%x MSIQ " 3347c478bd9Sstevel@tonic-gate "record type is not supported", 3357c478bd9Sstevel@tonic-gate ddi_driver_name(dip), ddi_get_instance(dip), 3367c478bd9Sstevel@tonic-gate msiq_rec_p->msiq_rec_type); 337b0fc0e77Sgovinda 3387c478bd9Sstevel@tonic-gate goto next_rec; 3397c478bd9Sstevel@tonic-gate } 3407c478bd9Sstevel@tonic-gate 3417c478bd9Sstevel@tonic-gate /* 3427c478bd9Sstevel@tonic-gate * Scan through px_ih_t linked list, searching for the 3437c478bd9Sstevel@tonic-gate * right px_ih_t, matching MSIQ record data. 3447c478bd9Sstevel@tonic-gate */ 345b0fc0e77Sgovinda for (j = 0, ih_p = ipil_p->ipil_ih_start; 346b0fc0e77Sgovinda ih_p && (j < ipil_p->ipil_ih_size) && 34707f14c08Sgovinda ((ih_p->ih_msg_code != msg_code) || 34807f14c08Sgovinda (ih_p->ih_rec_type != msiq_rec_p->msiq_rec_type)); 3492917a9c9Sschwartz ih_p = ih_p->ih_next, j++) 3502917a9c9Sschwartz ; 3517c478bd9Sstevel@tonic-gate 3527c478bd9Sstevel@tonic-gate if ((ih_p->ih_msg_code == msg_code) && 3537c478bd9Sstevel@tonic-gate (ih_p->ih_rec_type == msiq_rec_p->msiq_rec_type)) { 3547c478bd9Sstevel@tonic-gate dev_info_t *dip = ih_p->ih_dip; 3557c478bd9Sstevel@tonic-gate uint_t (*handler)() = ih_p->ih_handler; 3567c478bd9Sstevel@tonic-gate caddr_t arg1 = ih_p->ih_handler_arg1; 3577c478bd9Sstevel@tonic-gate caddr_t arg2 = ih_p->ih_handler_arg2; 3587c478bd9Sstevel@tonic-gate 3597c478bd9Sstevel@tonic-gate DBG(DBG_MSIQ_INTR, dip, "px_msiq_intr: ino=%x data=%x " 3607c478bd9Sstevel@tonic-gate "handler=%p arg1 =%p arg2=%p\n", ino_p->ino_ino, 3617c478bd9Sstevel@tonic-gate msg_code, handler, arg1, arg2); 3627c478bd9Sstevel@tonic-gate 3637c478bd9Sstevel@tonic-gate DTRACE_PROBE4(interrupt__start, dev_info_t, dip, 3647c478bd9Sstevel@tonic-gate void *, handler, caddr_t, arg1, caddr_t, arg2); 3657c478bd9Sstevel@tonic-gate 366f8d2de6bSjchu /* 367f8d2de6bSjchu * Special case for PCIE Error Messages. 368f8d2de6bSjchu * The current frame work doesn't fit PCIE Err Msgs 369f8d2de6bSjchu * This should be fixed when PCIE MESSAGES as a whole 370f8d2de6bSjchu * is architected correctly. 371f8d2de6bSjchu */ 372f8d2de6bSjchu if ((msg_code == PCIE_MSG_CODE_ERR_COR) || 373f8d2de6bSjchu (msg_code == PCIE_MSG_CODE_ERR_NONFATAL) || 374f8d2de6bSjchu (msg_code == PCIE_MSG_CODE_ERR_FATAL)) { 375f8d2de6bSjchu ret = px_err_fabric_intr(px_p, msg_code, 376f8d2de6bSjchu msiq_rec_p->msiq_rec_rid); 377f8d2de6bSjchu } else 378f8d2de6bSjchu ret = (*handler)(arg1, arg2); 3797c478bd9Sstevel@tonic-gate 3807c478bd9Sstevel@tonic-gate /* 3817c478bd9Sstevel@tonic-gate * Account for time used by this interrupt. Protect 3827c478bd9Sstevel@tonic-gate * against conflicting writes to ih_ticks from 3837c478bd9Sstevel@tonic-gate * ib_intr_dist_all() by using atomic ops. 3847c478bd9Sstevel@tonic-gate */ 3857c478bd9Sstevel@tonic-gate 386b0fc0e77Sgovinda if (pil <= LOCK_LEVEL) 3877c478bd9Sstevel@tonic-gate atomic_add_64(&ih_p->ih_ticks, intr_get_time()); 3887c478bd9Sstevel@tonic-gate 3897c478bd9Sstevel@tonic-gate DTRACE_PROBE4(interrupt__complete, dev_info_t, dip, 3907c478bd9Sstevel@tonic-gate void *, handler, caddr_t, arg1, int, ret); 391023ccc1eSegillett 392b0fc0e77Sgovinda msiq_p->msiq_new_head_index++; 393b0fc0e77Sgovinda px_lib_clr_msiq_rec(dip, curr_head_p); 3947c478bd9Sstevel@tonic-gate } else { 3957c478bd9Sstevel@tonic-gate DBG(DBG_MSIQ_INTR, dip, "px_msiq_intr:" 396023ccc1eSegillett "No matching MSIQ record found\n"); 3977c478bd9Sstevel@tonic-gate } 3987c478bd9Sstevel@tonic-gate next_rec: 399023ccc1eSegillett /* Get the pointer next EQ record */ 400023ccc1eSegillett curr_head_p = (msiqhead_t *) 401023ccc1eSegillett ((caddr_t)curr_head_p + sizeof (msiq_rec_t)); 402023ccc1eSegillett 403023ccc1eSegillett /* Check for overflow condition */ 404023ccc1eSegillett if (curr_head_p >= (msiqhead_t *)((caddr_t)msiq_p->msiq_base_p 405b0fc0e77Sgovinda + (msiq_state_p->msiq_rec_cnt * sizeof (msiq_rec_t)))) 406023ccc1eSegillett curr_head_p = (msiqhead_t *)msiq_p->msiq_base_p; 4077c478bd9Sstevel@tonic-gate } 4087c478bd9Sstevel@tonic-gate 409b0fc0e77Sgovinda DBG(DBG_MSIQ_INTR, dip, "px_msiq_intr: No of MSIQ recs processed %x\n", 410b0fc0e77Sgovinda (msiq_p->msiq_new_head_index - msiq_p->msiq_curr_head_index)); 411023ccc1eSegillett 412b0fc0e77Sgovinda DBG(DBG_MSIQ_INTR, dip, "px_msiq_intr: curr_head %x new_head %x " 413b0fc0e77Sgovinda "rec2process %x\n", msiq_p->msiq_curr_head_index, 414b0fc0e77Sgovinda msiq_p->msiq_new_head_index, msiq_p->msiq_recs2process); 415b0fc0e77Sgovinda 416b0fc0e77Sgovinda /* ino_claimed used just for debugging purpose */ 417b0fc0e77Sgovinda if (ret) 418b0fc0e77Sgovinda ino_p->ino_claimed |= (1 << pil); 419b0fc0e77Sgovinda 420b0fc0e77Sgovinda intr_done: 421b0fc0e77Sgovinda /* Interrupt can only be cleared after all pil levels are handled */ 422b0fc0e77Sgovinda if (pil != ino_p->ino_lopil) 423b0fc0e77Sgovinda return (DDI_INTR_CLAIMED); 424b0fc0e77Sgovinda 425b0fc0e77Sgovinda if (msiq_p->msiq_new_head_index <= msiq_p->msiq_curr_head_index) { 426b0fc0e77Sgovinda if (px_unclaimed_intr_block) 427b0fc0e77Sgovinda return (px_spurintr(ipil_p)); 428023ccc1eSegillett } 4297c478bd9Sstevel@tonic-gate 4307c478bd9Sstevel@tonic-gate /* Update MSIQ head index with no of MSIQ records processed */ 431b0fc0e77Sgovinda if (msiq_p->msiq_new_head_index >= msiq_state_p->msiq_rec_cnt) 432b0fc0e77Sgovinda msiq_p->msiq_new_head_index -= msiq_state_p->msiq_rec_cnt; 4337c478bd9Sstevel@tonic-gate 434b0fc0e77Sgovinda msiq_p->msiq_curr_head_index = msiq_p->msiq_new_head_index; 435b0fc0e77Sgovinda px_lib_msiq_sethead(dip, msiq_p->msiq_id, msiq_p->msiq_new_head_index); 436b0fc0e77Sgovinda 437b0fc0e77Sgovinda msiq_p->msiq_new_head_index = 0; 438b0fc0e77Sgovinda msiq_p->msiq_recs2process = 0; 439b0fc0e77Sgovinda ino_p->ino_claimed = 0; 4407c478bd9Sstevel@tonic-gate 4417c478bd9Sstevel@tonic-gate /* Clear the pending state */ 4427c478bd9Sstevel@tonic-gate if (px_lib_intr_setstate(dip, ino_p->ino_sysino, 4437c478bd9Sstevel@tonic-gate INTR_IDLE_STATE) != DDI_SUCCESS) 4447c478bd9Sstevel@tonic-gate return (DDI_INTR_UNCLAIMED); 4457c478bd9Sstevel@tonic-gate 4467c478bd9Sstevel@tonic-gate return (DDI_INTR_CLAIMED); 4477c478bd9Sstevel@tonic-gate } 4487c478bd9Sstevel@tonic-gate 4497c478bd9Sstevel@tonic-gate dev_info_t * 4507c478bd9Sstevel@tonic-gate px_get_my_childs_dip(dev_info_t *dip, dev_info_t *rdip) 4517c478bd9Sstevel@tonic-gate { 4527c478bd9Sstevel@tonic-gate dev_info_t *cdip = rdip; 4537c478bd9Sstevel@tonic-gate 4547c478bd9Sstevel@tonic-gate for (; ddi_get_parent(cdip) != dip; cdip = ddi_get_parent(cdip)) 4557c478bd9Sstevel@tonic-gate ; 4567c478bd9Sstevel@tonic-gate 4577c478bd9Sstevel@tonic-gate return (cdip); 4587c478bd9Sstevel@tonic-gate } 4597c478bd9Sstevel@tonic-gate 4607c478bd9Sstevel@tonic-gate /* Default class to pil value mapping */ 4617c478bd9Sstevel@tonic-gate px_class_val_t px_default_pil [] = { 4627c478bd9Sstevel@tonic-gate {0x000000, 0xff0000, 0x1}, /* Class code for pre-2.0 devices */ 4637c478bd9Sstevel@tonic-gate {0x010000, 0xff0000, 0x4}, /* Mass Storage Controller */ 4647c478bd9Sstevel@tonic-gate {0x020000, 0xff0000, 0x6}, /* Network Controller */ 4657c478bd9Sstevel@tonic-gate {0x030000, 0xff0000, 0x9}, /* Display Controller */ 46618061c65Sgd {0x040000, 0xff0000, 0x8}, /* Multimedia Controller */ 46744bb982bSgovinda {0x050000, 0xff0000, 0x9}, /* Memory Controller */ 46844bb982bSgovinda {0x060000, 0xff0000, 0x9}, /* Bridge Controller */ 4697c478bd9Sstevel@tonic-gate {0x0c0000, 0xffff00, 0x9}, /* Serial Bus, FireWire (IEEE 1394) */ 4707c478bd9Sstevel@tonic-gate {0x0c0100, 0xffff00, 0x4}, /* Serial Bus, ACCESS.bus */ 4717c478bd9Sstevel@tonic-gate {0x0c0200, 0xffff00, 0x4}, /* Serial Bus, SSA */ 4727c478bd9Sstevel@tonic-gate {0x0c0300, 0xffff00, 0x9}, /* Serial Bus Universal Serial Bus */ 4737c478bd9Sstevel@tonic-gate {0x0c0400, 0xffff00, 0x6}, /* Serial Bus, Fibre Channel */ 4747c478bd9Sstevel@tonic-gate {0x0c0600, 0xffff00, 0x6} /* Serial Bus, Infiniband */ 4757c478bd9Sstevel@tonic-gate }; 4767c478bd9Sstevel@tonic-gate 4777c478bd9Sstevel@tonic-gate /* 4787c478bd9Sstevel@tonic-gate * Default class to intr_weight value mapping (% of CPU). A driver.conf 4797c478bd9Sstevel@tonic-gate * entry on or above the pci node like 4807c478bd9Sstevel@tonic-gate * 4817c478bd9Sstevel@tonic-gate * pci-class-intr-weights= 0x020000, 0xff0000, 30; 4827c478bd9Sstevel@tonic-gate * 4837c478bd9Sstevel@tonic-gate * can be used to augment or override entries in the default table below. 4847c478bd9Sstevel@tonic-gate * 4857c478bd9Sstevel@tonic-gate * NB: The values below give NICs preference on redistribution, and provide 4867c478bd9Sstevel@tonic-gate * NICs some isolation from other interrupt sources. We need better interfaces 4877c478bd9Sstevel@tonic-gate * that allow the NIC driver to identify a specific NIC instance as high 4887c478bd9Sstevel@tonic-gate * bandwidth, and thus deserving of separation from other low bandwidth 4897c478bd9Sstevel@tonic-gate * NICs additional isolation from other interrupt sources. 4907c478bd9Sstevel@tonic-gate * 4917c478bd9Sstevel@tonic-gate * NB: We treat Infiniband like a NIC. 4927c478bd9Sstevel@tonic-gate */ 4937c478bd9Sstevel@tonic-gate px_class_val_t px_default_intr_weight [] = { 4947c478bd9Sstevel@tonic-gate {0x020000, 0xff0000, 35}, /* Network Controller */ 4957c478bd9Sstevel@tonic-gate {0x010000, 0xff0000, 10}, /* Mass Storage Controller */ 4967c478bd9Sstevel@tonic-gate {0x0c0400, 0xffff00, 10}, /* Serial Bus, Fibre Channel */ 4977c478bd9Sstevel@tonic-gate {0x0c0600, 0xffff00, 50} /* Serial Bus, Infiniband */ 4987c478bd9Sstevel@tonic-gate }; 4997c478bd9Sstevel@tonic-gate 5007c478bd9Sstevel@tonic-gate static uint32_t 5017c478bd9Sstevel@tonic-gate px_match_class_val(uint32_t key, px_class_val_t *rec_p, int nrec, 5027c478bd9Sstevel@tonic-gate uint32_t default_val) 5037c478bd9Sstevel@tonic-gate { 5047c478bd9Sstevel@tonic-gate int i; 5057c478bd9Sstevel@tonic-gate 5067c478bd9Sstevel@tonic-gate for (i = 0; i < nrec; rec_p++, i++) { 5077c478bd9Sstevel@tonic-gate if ((rec_p->class_code & rec_p->class_mask) == 5087c478bd9Sstevel@tonic-gate (key & rec_p->class_mask)) 5097c478bd9Sstevel@tonic-gate return (rec_p->class_val); 5107c478bd9Sstevel@tonic-gate } 5117c478bd9Sstevel@tonic-gate 5127c478bd9Sstevel@tonic-gate return (default_val); 5137c478bd9Sstevel@tonic-gate } 5147c478bd9Sstevel@tonic-gate 5157c478bd9Sstevel@tonic-gate /* 5167c478bd9Sstevel@tonic-gate * px_class_to_val 5177c478bd9Sstevel@tonic-gate * 5187c478bd9Sstevel@tonic-gate * Return the configuration value, based on class code and sub class code, 5197c478bd9Sstevel@tonic-gate * from the specified property based or default px_class_val_t table. 5207c478bd9Sstevel@tonic-gate */ 5217c478bd9Sstevel@tonic-gate uint32_t 5227c478bd9Sstevel@tonic-gate px_class_to_val(dev_info_t *rdip, char *property_name, px_class_val_t *rec_p, 5237c478bd9Sstevel@tonic-gate int nrec, uint32_t default_val) 5247c478bd9Sstevel@tonic-gate { 5257c478bd9Sstevel@tonic-gate int property_len; 5267c478bd9Sstevel@tonic-gate uint32_t class_code; 5277c478bd9Sstevel@tonic-gate px_class_val_t *conf; 5287c478bd9Sstevel@tonic-gate uint32_t val = default_val; 5297c478bd9Sstevel@tonic-gate 5307c478bd9Sstevel@tonic-gate /* 5317c478bd9Sstevel@tonic-gate * Use the "class-code" property to get the base and sub class 5327c478bd9Sstevel@tonic-gate * codes for the requesting device. 5337c478bd9Sstevel@tonic-gate */ 5347c478bd9Sstevel@tonic-gate class_code = (uint32_t)ddi_prop_get_int(DDI_DEV_T_ANY, rdip, 5357c478bd9Sstevel@tonic-gate DDI_PROP_DONTPASS, "class-code", -1); 5367c478bd9Sstevel@tonic-gate 5377c478bd9Sstevel@tonic-gate if (class_code == -1) 5387c478bd9Sstevel@tonic-gate return (val); 5397c478bd9Sstevel@tonic-gate 5407c478bd9Sstevel@tonic-gate /* look up the val from the default table */ 5417c478bd9Sstevel@tonic-gate val = px_match_class_val(class_code, rec_p, nrec, val); 5427c478bd9Sstevel@tonic-gate 5437c478bd9Sstevel@tonic-gate /* see if there is a more specific property specified value */ 5447c478bd9Sstevel@tonic-gate if (ddi_getlongprop(DDI_DEV_T_ANY, rdip, DDI_PROP_NOTPROM, 5457c478bd9Sstevel@tonic-gate property_name, (caddr_t)&conf, &property_len)) 5467c478bd9Sstevel@tonic-gate return (val); 5477c478bd9Sstevel@tonic-gate 5487c478bd9Sstevel@tonic-gate if ((property_len % sizeof (px_class_val_t)) == 0) 5497c478bd9Sstevel@tonic-gate val = px_match_class_val(class_code, conf, 5507c478bd9Sstevel@tonic-gate property_len / sizeof (px_class_val_t), val); 5517c478bd9Sstevel@tonic-gate kmem_free(conf, property_len); 5527c478bd9Sstevel@tonic-gate return (val); 5537c478bd9Sstevel@tonic-gate } 5547c478bd9Sstevel@tonic-gate 5557c478bd9Sstevel@tonic-gate /* px_class_to_pil: return the pil for a given device. */ 5567c478bd9Sstevel@tonic-gate uint32_t 5577c478bd9Sstevel@tonic-gate px_class_to_pil(dev_info_t *rdip) 5587c478bd9Sstevel@tonic-gate { 5597c478bd9Sstevel@tonic-gate uint32_t pil; 5607c478bd9Sstevel@tonic-gate 5617c9e29aaSgovinda /* Default pil is 1 */ 5627c478bd9Sstevel@tonic-gate pil = px_class_to_val(rdip, 5637c478bd9Sstevel@tonic-gate "pci-class-priorities", px_default_pil, 5647c9e29aaSgovinda sizeof (px_default_pil) / sizeof (px_class_val_t), 1); 5657c478bd9Sstevel@tonic-gate 5667c9e29aaSgovinda /* Range check the result */ 5677c478bd9Sstevel@tonic-gate if (pil >= 0xf) 5687c9e29aaSgovinda pil = 1; 5697c478bd9Sstevel@tonic-gate 5707c478bd9Sstevel@tonic-gate return (pil); 5717c478bd9Sstevel@tonic-gate } 5727c478bd9Sstevel@tonic-gate 5737c478bd9Sstevel@tonic-gate /* px_class_to_intr_weight: return the intr_weight for a given device. */ 5747c478bd9Sstevel@tonic-gate static int32_t 5757c478bd9Sstevel@tonic-gate px_class_to_intr_weight(dev_info_t *rdip) 5767c478bd9Sstevel@tonic-gate { 5777c478bd9Sstevel@tonic-gate int32_t intr_weight; 5787c478bd9Sstevel@tonic-gate 5797c478bd9Sstevel@tonic-gate /* default weight is 0% */ 5807c478bd9Sstevel@tonic-gate intr_weight = px_class_to_val(rdip, 5817c478bd9Sstevel@tonic-gate "pci-class-intr-weights", px_default_intr_weight, 5827c478bd9Sstevel@tonic-gate sizeof (px_default_intr_weight) / sizeof (px_class_val_t), 0); 5837c478bd9Sstevel@tonic-gate 5847c478bd9Sstevel@tonic-gate /* range check the result */ 5857c478bd9Sstevel@tonic-gate if (intr_weight < 0) 5867c478bd9Sstevel@tonic-gate intr_weight = 0; 5877c478bd9Sstevel@tonic-gate if (intr_weight > 1000) 5887c478bd9Sstevel@tonic-gate intr_weight = 1000; 5897c478bd9Sstevel@tonic-gate 5907c478bd9Sstevel@tonic-gate return (intr_weight); 5917c478bd9Sstevel@tonic-gate } 5927c478bd9Sstevel@tonic-gate 5937c478bd9Sstevel@tonic-gate /* ARGSUSED */ 5947c478bd9Sstevel@tonic-gate int 5957c478bd9Sstevel@tonic-gate px_intx_ops(dev_info_t *dip, dev_info_t *rdip, ddi_intr_op_t intr_op, 5967c478bd9Sstevel@tonic-gate ddi_intr_handle_impl_t *hdlp, void *result) 5977c478bd9Sstevel@tonic-gate { 598a195726fSgovinda px_t *px_p = DIP_TO_STATE(dip); 599a195726fSgovinda int ret = DDI_SUCCESS; 6007c478bd9Sstevel@tonic-gate 6017c478bd9Sstevel@tonic-gate DBG(DBG_INTROPS, dip, "px_intx_ops: dip=%x rdip=%x intr_op=%x " 6027c478bd9Sstevel@tonic-gate "handle=%p\n", dip, rdip, intr_op, hdlp); 6037c478bd9Sstevel@tonic-gate 6047c478bd9Sstevel@tonic-gate switch (intr_op) { 6057c478bd9Sstevel@tonic-gate case DDI_INTROP_GETCAP: 6067c478bd9Sstevel@tonic-gate ret = pci_intx_get_cap(rdip, (int *)result); 6077c478bd9Sstevel@tonic-gate break; 6087c478bd9Sstevel@tonic-gate case DDI_INTROP_SETCAP: 6097c478bd9Sstevel@tonic-gate DBG(DBG_INTROPS, dip, "px_intx_ops: SetCap is not supported\n"); 6107c478bd9Sstevel@tonic-gate ret = DDI_ENOTSUP; 6117c478bd9Sstevel@tonic-gate break; 6127c478bd9Sstevel@tonic-gate case DDI_INTROP_ALLOC: 6137c478bd9Sstevel@tonic-gate *(int *)result = hdlp->ih_scratch1; 6147c478bd9Sstevel@tonic-gate break; 6157c478bd9Sstevel@tonic-gate case DDI_INTROP_FREE: 6167c478bd9Sstevel@tonic-gate break; 6177c478bd9Sstevel@tonic-gate case DDI_INTROP_GETPRI: 618a195726fSgovinda *(int *)result = hdlp->ih_pri ? 619a195726fSgovinda hdlp->ih_pri : px_class_to_pil(rdip); 6207c478bd9Sstevel@tonic-gate break; 6217c478bd9Sstevel@tonic-gate case DDI_INTROP_SETPRI: 6227c478bd9Sstevel@tonic-gate break; 6237c478bd9Sstevel@tonic-gate case DDI_INTROP_ADDISR: 6247c478bd9Sstevel@tonic-gate ret = px_add_intx_intr(dip, rdip, hdlp); 6257c478bd9Sstevel@tonic-gate break; 6267c478bd9Sstevel@tonic-gate case DDI_INTROP_REMISR: 6277c478bd9Sstevel@tonic-gate ret = px_rem_intx_intr(dip, rdip, hdlp); 6287c478bd9Sstevel@tonic-gate break; 6297c478bd9Sstevel@tonic-gate case DDI_INTROP_ENABLE: 6307c478bd9Sstevel@tonic-gate ret = px_ib_update_intr_state(px_p, rdip, hdlp->ih_inum, 631b0fc0e77Sgovinda hdlp->ih_vector, hdlp->ih_pri, PX_INTR_STATE_ENABLE, 0, 0); 6327c478bd9Sstevel@tonic-gate break; 6337c478bd9Sstevel@tonic-gate case DDI_INTROP_DISABLE: 6347c478bd9Sstevel@tonic-gate ret = px_ib_update_intr_state(px_p, rdip, hdlp->ih_inum, 635b0fc0e77Sgovinda hdlp->ih_vector, hdlp->ih_pri, PX_INTR_STATE_DISABLE, 0, 0); 6367c478bd9Sstevel@tonic-gate break; 6377c478bd9Sstevel@tonic-gate case DDI_INTROP_SETMASK: 6387c478bd9Sstevel@tonic-gate ret = pci_intx_set_mask(rdip); 6397c478bd9Sstevel@tonic-gate break; 6407c478bd9Sstevel@tonic-gate case DDI_INTROP_CLRMASK: 6417c478bd9Sstevel@tonic-gate ret = pci_intx_clr_mask(rdip); 6427c478bd9Sstevel@tonic-gate break; 6437c478bd9Sstevel@tonic-gate case DDI_INTROP_GETPENDING: 6447c478bd9Sstevel@tonic-gate ret = pci_intx_get_pending(rdip, (int *)result); 6457c478bd9Sstevel@tonic-gate break; 6467c478bd9Sstevel@tonic-gate case DDI_INTROP_NINTRS: 6477c478bd9Sstevel@tonic-gate case DDI_INTROP_NAVAIL: 648a54f81fbSanish *(int *)result = i_ddi_get_intx_nintrs(rdip); 6497c478bd9Sstevel@tonic-gate break; 6507c478bd9Sstevel@tonic-gate default: 6517c478bd9Sstevel@tonic-gate ret = DDI_ENOTSUP; 6527c478bd9Sstevel@tonic-gate break; 6537c478bd9Sstevel@tonic-gate } 6547c478bd9Sstevel@tonic-gate 6557c478bd9Sstevel@tonic-gate return (ret); 6567c478bd9Sstevel@tonic-gate } 6577c478bd9Sstevel@tonic-gate 6587c478bd9Sstevel@tonic-gate /* ARGSUSED */ 6597c478bd9Sstevel@tonic-gate int 6607c478bd9Sstevel@tonic-gate px_msix_ops(dev_info_t *dip, dev_info_t *rdip, ddi_intr_op_t intr_op, 6617c478bd9Sstevel@tonic-gate ddi_intr_handle_impl_t *hdlp, void *result) 6627c478bd9Sstevel@tonic-gate { 6637c478bd9Sstevel@tonic-gate px_t *px_p = DIP_TO_STATE(dip); 6647c478bd9Sstevel@tonic-gate px_msi_state_t *msi_state_p = &px_p->px_ib_p->ib_msi_state; 6659c75c6bfSgovinda msiq_rec_type_t msiq_rec_type; 6669c75c6bfSgovinda msi_type_t msi_type; 6679c75c6bfSgovinda uint64_t msi_addr; 6687c478bd9Sstevel@tonic-gate msinum_t msi_num; 6697c478bd9Sstevel@tonic-gate msiqid_t msiq_id; 6707c478bd9Sstevel@tonic-gate uint_t nintrs; 6717c478bd9Sstevel@tonic-gate int i, ret = DDI_SUCCESS; 6727c478bd9Sstevel@tonic-gate 6737c478bd9Sstevel@tonic-gate DBG(DBG_INTROPS, dip, "px_msix_ops: dip=%x rdip=%x intr_op=%x " 6747c478bd9Sstevel@tonic-gate "handle=%p\n", dip, rdip, intr_op, hdlp); 6757c478bd9Sstevel@tonic-gate 6769c75c6bfSgovinda /* Check for MSI64 support */ 67707f14c08Sgovinda if ((hdlp->ih_cap & DDI_INTR_FLAG_MSI64) && msi_state_p->msi_addr64) { 6789c75c6bfSgovinda msiq_rec_type = MSI64_REC; 6799c75c6bfSgovinda msi_type = MSI64_TYPE; 68007f14c08Sgovinda msi_addr = msi_state_p->msi_addr64; 6819c75c6bfSgovinda } else { 6829c75c6bfSgovinda msiq_rec_type = MSI32_REC; 6839c75c6bfSgovinda msi_type = MSI32_TYPE; 6849c75c6bfSgovinda msi_addr = msi_state_p->msi_addr32; 6859c75c6bfSgovinda } 6869c75c6bfSgovinda 6877c478bd9Sstevel@tonic-gate switch (intr_op) { 6887c478bd9Sstevel@tonic-gate case DDI_INTROP_GETCAP: 6897c478bd9Sstevel@tonic-gate ret = pci_msi_get_cap(rdip, hdlp->ih_type, (int *)result); 6907c478bd9Sstevel@tonic-gate break; 6917c478bd9Sstevel@tonic-gate case DDI_INTROP_SETCAP: 6927c478bd9Sstevel@tonic-gate DBG(DBG_INTROPS, dip, "px_msix_ops: SetCap is not supported\n"); 6937c478bd9Sstevel@tonic-gate ret = DDI_ENOTSUP; 6947c478bd9Sstevel@tonic-gate break; 6957c478bd9Sstevel@tonic-gate case DDI_INTROP_ALLOC: 6967c478bd9Sstevel@tonic-gate /* 6977c478bd9Sstevel@tonic-gate * We need to restrict this allocation in future 6987c478bd9Sstevel@tonic-gate * based on Resource Management policies. 6997c478bd9Sstevel@tonic-gate */ 7007c478bd9Sstevel@tonic-gate if ((ret = px_msi_alloc(px_p, rdip, hdlp->ih_inum, 70120036fe5Segillett hdlp->ih_scratch1, (uintptr_t)hdlp->ih_scratch2, &msi_num, 70220036fe5Segillett (int *)result)) != DDI_SUCCESS) { 70320036fe5Segillett DBG(DBG_INTROPS, dip, "px_msix_ops: allocation " 70420036fe5Segillett "failed, rdip 0x%p type 0x%d inum 0x%x " 70520036fe5Segillett "count 0x%x\n", rdip, hdlp->ih_type, hdlp->ih_inum, 70620036fe5Segillett hdlp->ih_scratch1); 7077c478bd9Sstevel@tonic-gate 7087c478bd9Sstevel@tonic-gate return (ret); 7097c478bd9Sstevel@tonic-gate } 7107c478bd9Sstevel@tonic-gate 71120036fe5Segillett if ((hdlp->ih_type == DDI_INTR_TYPE_MSIX) && 71220036fe5Segillett (i_ddi_get_msix(rdip) == NULL)) { 71320036fe5Segillett ddi_intr_msix_t *msix_p; 71420036fe5Segillett 71520036fe5Segillett if (msix_p = pci_msix_init(rdip)) { 71620036fe5Segillett i_ddi_set_msix(rdip, msix_p); 71720036fe5Segillett break; 71820036fe5Segillett } 71920036fe5Segillett 72020036fe5Segillett DBG(DBG_INTROPS, dip, "px_msix_ops: MSI-X allocation " 72120036fe5Segillett "failed, rdip 0x%p inum 0x%x\n", rdip, 72220036fe5Segillett hdlp->ih_inum); 72320036fe5Segillett 72420036fe5Segillett (void) px_msi_free(px_p, rdip, hdlp->ih_inum, 72520036fe5Segillett hdlp->ih_scratch1); 72620036fe5Segillett 72720036fe5Segillett return (DDI_FAILURE); 72820036fe5Segillett } 72920036fe5Segillett 7307c478bd9Sstevel@tonic-gate break; 7317c478bd9Sstevel@tonic-gate case DDI_INTROP_FREE: 73295003185Segillett (void) pci_msi_disable_mode(rdip, hdlp->ih_type, NULL); 7337c478bd9Sstevel@tonic-gate (void) pci_msi_unconfigure(rdip, hdlp->ih_type, hdlp->ih_inum); 73420036fe5Segillett 73520036fe5Segillett if (hdlp->ih_type == DDI_INTR_TYPE_MSI) 73620036fe5Segillett goto msi_free; 73720036fe5Segillett 73820036fe5Segillett if (hdlp->ih_flags & DDI_INTR_MSIX_DUP) 73920036fe5Segillett break; 74020036fe5Segillett 74120036fe5Segillett if (((i_ddi_intr_get_current_nintrs(hdlp->ih_dip) - 1) == 0) && 74220036fe5Segillett (i_ddi_get_msix(rdip))) { 74320036fe5Segillett pci_msix_fini(i_ddi_get_msix(rdip)); 74420036fe5Segillett i_ddi_set_msix(rdip, NULL); 74520036fe5Segillett } 74620036fe5Segillett msi_free: 7477c478bd9Sstevel@tonic-gate (void) px_msi_free(px_p, rdip, hdlp->ih_inum, 7487c478bd9Sstevel@tonic-gate hdlp->ih_scratch1); 7497c478bd9Sstevel@tonic-gate break; 7507c478bd9Sstevel@tonic-gate case DDI_INTROP_GETPRI: 7517c478bd9Sstevel@tonic-gate *(int *)result = hdlp->ih_pri ? 7527c478bd9Sstevel@tonic-gate hdlp->ih_pri : px_class_to_pil(rdip); 7537c478bd9Sstevel@tonic-gate break; 7547c478bd9Sstevel@tonic-gate case DDI_INTROP_SETPRI: 7557c478bd9Sstevel@tonic-gate break; 7567c478bd9Sstevel@tonic-gate case DDI_INTROP_ADDISR: 7577c478bd9Sstevel@tonic-gate if ((ret = px_msi_get_msinum(px_p, hdlp->ih_dip, 7587c478bd9Sstevel@tonic-gate hdlp->ih_inum, &msi_num)) != DDI_SUCCESS) 7597c478bd9Sstevel@tonic-gate return (ret); 7607c478bd9Sstevel@tonic-gate 7617c478bd9Sstevel@tonic-gate if ((ret = px_add_msiq_intr(dip, rdip, hdlp, 7629c75c6bfSgovinda msiq_rec_type, msi_num, &msiq_id)) != DDI_SUCCESS) { 7637c478bd9Sstevel@tonic-gate DBG(DBG_INTROPS, dip, "px_msix_ops: Add MSI handler " 7647c478bd9Sstevel@tonic-gate "failed, rdip 0x%p msi 0x%x\n", rdip, msi_num); 7657c478bd9Sstevel@tonic-gate return (ret); 7667c478bd9Sstevel@tonic-gate } 7677c478bd9Sstevel@tonic-gate 7687c478bd9Sstevel@tonic-gate DBG(DBG_INTROPS, dip, "px_msix_ops: msiq used 0x%x\n", msiq_id); 7697c478bd9Sstevel@tonic-gate 7707c478bd9Sstevel@tonic-gate if ((ret = px_lib_msi_setmsiq(dip, msi_num, 7719c75c6bfSgovinda msiq_id, msi_type)) != DDI_SUCCESS) { 7727c478bd9Sstevel@tonic-gate (void) px_rem_msiq_intr(dip, rdip, 7739c75c6bfSgovinda hdlp, msiq_rec_type, msi_num, msiq_id); 7747c478bd9Sstevel@tonic-gate return (ret); 7757c478bd9Sstevel@tonic-gate } 7767c478bd9Sstevel@tonic-gate 7777c478bd9Sstevel@tonic-gate if ((ret = px_lib_msi_setstate(dip, msi_num, 7787c478bd9Sstevel@tonic-gate PCI_MSI_STATE_IDLE)) != DDI_SUCCESS) { 7797c478bd9Sstevel@tonic-gate (void) px_rem_msiq_intr(dip, rdip, 7809c75c6bfSgovinda hdlp, msiq_rec_type, msi_num, msiq_id); 7817c478bd9Sstevel@tonic-gate return (ret); 7827c478bd9Sstevel@tonic-gate } 7837c478bd9Sstevel@tonic-gate 7847c478bd9Sstevel@tonic-gate hdlp->ih_vector = msi_num; 7857c478bd9Sstevel@tonic-gate break; 7867c478bd9Sstevel@tonic-gate case DDI_INTROP_DUPVEC: 78720036fe5Segillett DBG(DBG_INTROPS, dip, "px_msix_ops: dupisr - inum: %x, " 78820036fe5Segillett "new_vector: %x\n", hdlp->ih_inum, hdlp->ih_scratch1); 78920036fe5Segillett 79020036fe5Segillett ret = pci_msix_dup(hdlp->ih_dip, hdlp->ih_inum, 79120036fe5Segillett hdlp->ih_scratch1); 7927c478bd9Sstevel@tonic-gate break; 7937c478bd9Sstevel@tonic-gate case DDI_INTROP_REMISR: 7947c478bd9Sstevel@tonic-gate msi_num = hdlp->ih_vector; 7957c478bd9Sstevel@tonic-gate 7967c478bd9Sstevel@tonic-gate if ((ret = px_lib_msi_getmsiq(dip, msi_num, 7977c478bd9Sstevel@tonic-gate &msiq_id)) != DDI_SUCCESS) 7987c478bd9Sstevel@tonic-gate return (ret); 7997c478bd9Sstevel@tonic-gate 8007c478bd9Sstevel@tonic-gate if ((ret = px_lib_msi_setstate(dip, msi_num, 8019c75c6bfSgovinda PCI_MSI_STATE_IDLE)) != DDI_SUCCESS) 8027c478bd9Sstevel@tonic-gate return (ret); 8037c478bd9Sstevel@tonic-gate 8047c478bd9Sstevel@tonic-gate ret = px_rem_msiq_intr(dip, rdip, 8059c75c6bfSgovinda hdlp, msiq_rec_type, msi_num, msiq_id); 8067c478bd9Sstevel@tonic-gate 8077c478bd9Sstevel@tonic-gate hdlp->ih_vector = 0; 8087c478bd9Sstevel@tonic-gate break; 8097c478bd9Sstevel@tonic-gate case DDI_INTROP_ENABLE: 8107c478bd9Sstevel@tonic-gate msi_num = hdlp->ih_vector; 8117c478bd9Sstevel@tonic-gate 8127c478bd9Sstevel@tonic-gate if ((ret = px_lib_msi_setvalid(dip, msi_num, 8137c478bd9Sstevel@tonic-gate PCI_MSI_VALID)) != DDI_SUCCESS) 8147c478bd9Sstevel@tonic-gate return (ret); 8157c478bd9Sstevel@tonic-gate 81695003185Segillett if ((pci_is_msi_enabled(rdip, hdlp->ih_type) != DDI_SUCCESS) || 81795003185Segillett (hdlp->ih_type == DDI_INTR_TYPE_MSIX)) { 8187c478bd9Sstevel@tonic-gate nintrs = i_ddi_intr_get_current_nintrs(hdlp->ih_dip); 8197c478bd9Sstevel@tonic-gate 8207c478bd9Sstevel@tonic-gate if ((ret = pci_msi_configure(rdip, hdlp->ih_type, 8219c75c6bfSgovinda nintrs, hdlp->ih_inum, msi_addr, 82295003185Segillett hdlp->ih_type == DDI_INTR_TYPE_MSIX ? 82395003185Segillett msi_num : msi_num & ~(nintrs - 1))) != DDI_SUCCESS) 8247c478bd9Sstevel@tonic-gate return (ret); 8257c478bd9Sstevel@tonic-gate 82695003185Segillett if ((ret = pci_msi_enable_mode(rdip, hdlp->ih_type)) 82795003185Segillett != DDI_SUCCESS) 8287c478bd9Sstevel@tonic-gate return (ret); 8297c478bd9Sstevel@tonic-gate } 8307c478bd9Sstevel@tonic-gate 83136fe4a92Segillett if ((ret = pci_msi_clr_mask(rdip, hdlp->ih_type, 83236fe4a92Segillett hdlp->ih_inum)) != DDI_SUCCESS) 83336fe4a92Segillett return (ret); 83436fe4a92Segillett 83520036fe5Segillett if (hdlp->ih_flags & DDI_INTR_MSIX_DUP) 83620036fe5Segillett break; 83720036fe5Segillett 83836fe4a92Segillett if ((ret = px_lib_msi_getmsiq(dip, msi_num, 83936fe4a92Segillett &msiq_id)) != DDI_SUCCESS) 84036fe4a92Segillett return (ret); 84136fe4a92Segillett 84236fe4a92Segillett ret = px_ib_update_intr_state(px_p, rdip, hdlp->ih_inum, 843b0fc0e77Sgovinda px_msiqid_to_devino(px_p, msiq_id), hdlp->ih_pri, 844b0fc0e77Sgovinda PX_INTR_STATE_ENABLE, msiq_rec_type, msi_num); 8457c478bd9Sstevel@tonic-gate 8467c478bd9Sstevel@tonic-gate break; 8477c478bd9Sstevel@tonic-gate case DDI_INTROP_DISABLE: 8487c478bd9Sstevel@tonic-gate msi_num = hdlp->ih_vector; 8497c478bd9Sstevel@tonic-gate 8507c478bd9Sstevel@tonic-gate if ((ret = pci_msi_set_mask(rdip, hdlp->ih_type, 8517c478bd9Sstevel@tonic-gate hdlp->ih_inum)) != DDI_SUCCESS) 8527c478bd9Sstevel@tonic-gate return (ret); 8537c478bd9Sstevel@tonic-gate 85436fe4a92Segillett if ((ret = px_lib_msi_setvalid(dip, msi_num, 85536fe4a92Segillett PCI_MSI_INVALID)) != DDI_SUCCESS) 85636fe4a92Segillett return (ret); 85736fe4a92Segillett 85820036fe5Segillett if (hdlp->ih_flags & DDI_INTR_MSIX_DUP) 85920036fe5Segillett break; 86020036fe5Segillett 86136fe4a92Segillett if ((ret = px_lib_msi_getmsiq(dip, msi_num, 86236fe4a92Segillett &msiq_id)) != DDI_SUCCESS) 86336fe4a92Segillett return (ret); 86436fe4a92Segillett 86536fe4a92Segillett ret = px_ib_update_intr_state(px_p, rdip, 86636fe4a92Segillett hdlp->ih_inum, px_msiqid_to_devino(px_p, msiq_id), 867b0fc0e77Sgovinda hdlp->ih_pri, PX_INTR_STATE_DISABLE, msiq_rec_type, 868b0fc0e77Sgovinda msi_num); 86936fe4a92Segillett 8707c478bd9Sstevel@tonic-gate break; 8717c478bd9Sstevel@tonic-gate case DDI_INTROP_BLOCKENABLE: 8727c478bd9Sstevel@tonic-gate nintrs = i_ddi_intr_get_current_nintrs(hdlp->ih_dip); 8737c478bd9Sstevel@tonic-gate msi_num = hdlp->ih_vector; 8747c478bd9Sstevel@tonic-gate 8757c478bd9Sstevel@tonic-gate if ((ret = pci_msi_configure(rdip, hdlp->ih_type, 8769c75c6bfSgovinda nintrs, hdlp->ih_inum, msi_addr, 8777c478bd9Sstevel@tonic-gate msi_num & ~(nintrs - 1))) != DDI_SUCCESS) 8787c478bd9Sstevel@tonic-gate return (ret); 8797c478bd9Sstevel@tonic-gate 8807c478bd9Sstevel@tonic-gate for (i = 0; i < nintrs; i++, msi_num++) { 8817c478bd9Sstevel@tonic-gate if ((ret = px_lib_msi_setvalid(dip, msi_num, 8827c478bd9Sstevel@tonic-gate PCI_MSI_VALID)) != DDI_SUCCESS) 8837c478bd9Sstevel@tonic-gate return (ret); 88436fe4a92Segillett 88536fe4a92Segillett if ((ret = px_lib_msi_getmsiq(dip, msi_num, 88636fe4a92Segillett &msiq_id)) != DDI_SUCCESS) 88736fe4a92Segillett return (ret); 88836fe4a92Segillett 88936fe4a92Segillett if ((ret = px_ib_update_intr_state(px_p, rdip, 89036fe4a92Segillett hdlp->ih_inum + i, px_msiqid_to_devino(px_p, 891b0fc0e77Sgovinda msiq_id), hdlp->ih_pri, PX_INTR_STATE_ENABLE, 892b0fc0e77Sgovinda msiq_rec_type, msi_num)) != DDI_SUCCESS) 89336fe4a92Segillett return (ret); 8947c478bd9Sstevel@tonic-gate } 8957c478bd9Sstevel@tonic-gate 89695003185Segillett ret = pci_msi_enable_mode(rdip, hdlp->ih_type); 8977c478bd9Sstevel@tonic-gate break; 8987c478bd9Sstevel@tonic-gate case DDI_INTROP_BLOCKDISABLE: 8997c478bd9Sstevel@tonic-gate nintrs = i_ddi_intr_get_current_nintrs(hdlp->ih_dip); 9007c478bd9Sstevel@tonic-gate msi_num = hdlp->ih_vector; 9017c478bd9Sstevel@tonic-gate 9027c478bd9Sstevel@tonic-gate if ((ret = pci_msi_disable_mode(rdip, hdlp->ih_type, 90395003185Segillett hdlp->ih_cap & DDI_INTR_FLAG_BLOCK)) != DDI_SUCCESS) 9047c478bd9Sstevel@tonic-gate return (ret); 9057c478bd9Sstevel@tonic-gate 9067c478bd9Sstevel@tonic-gate for (i = 0; i < nintrs; i++, msi_num++) { 9077c478bd9Sstevel@tonic-gate if ((ret = px_lib_msi_setvalid(dip, msi_num, 9087c478bd9Sstevel@tonic-gate PCI_MSI_INVALID)) != DDI_SUCCESS) 9097c478bd9Sstevel@tonic-gate return (ret); 91036fe4a92Segillett 91136fe4a92Segillett if ((ret = px_lib_msi_getmsiq(dip, msi_num, 91236fe4a92Segillett &msiq_id)) != DDI_SUCCESS) 91336fe4a92Segillett return (ret); 91436fe4a92Segillett 91536fe4a92Segillett if ((ret = px_ib_update_intr_state(px_p, rdip, 91636fe4a92Segillett hdlp->ih_inum + i, px_msiqid_to_devino(px_p, 917b0fc0e77Sgovinda msiq_id), hdlp->ih_pri, PX_INTR_STATE_DISABLE, 918b0fc0e77Sgovinda msiq_rec_type, msi_num)) != DDI_SUCCESS) 91936fe4a92Segillett return (ret); 9207c478bd9Sstevel@tonic-gate } 9217c478bd9Sstevel@tonic-gate 9227c478bd9Sstevel@tonic-gate break; 9237c478bd9Sstevel@tonic-gate case DDI_INTROP_SETMASK: 9247c478bd9Sstevel@tonic-gate ret = pci_msi_set_mask(rdip, hdlp->ih_type, hdlp->ih_inum); 9257c478bd9Sstevel@tonic-gate break; 9267c478bd9Sstevel@tonic-gate case DDI_INTROP_CLRMASK: 9277c478bd9Sstevel@tonic-gate ret = pci_msi_clr_mask(rdip, hdlp->ih_type, hdlp->ih_inum); 9287c478bd9Sstevel@tonic-gate break; 9297c478bd9Sstevel@tonic-gate case DDI_INTROP_GETPENDING: 9307c478bd9Sstevel@tonic-gate ret = pci_msi_get_pending(rdip, hdlp->ih_type, 9317c478bd9Sstevel@tonic-gate hdlp->ih_inum, (int *)result); 9327c478bd9Sstevel@tonic-gate break; 9337c478bd9Sstevel@tonic-gate case DDI_INTROP_NINTRS: 9347c478bd9Sstevel@tonic-gate ret = pci_msi_get_nintrs(rdip, hdlp->ih_type, (int *)result); 9357c478bd9Sstevel@tonic-gate break; 9367c478bd9Sstevel@tonic-gate case DDI_INTROP_NAVAIL: 9377c478bd9Sstevel@tonic-gate /* XXX - a new interface may be needed */ 9387c478bd9Sstevel@tonic-gate ret = pci_msi_get_nintrs(rdip, hdlp->ih_type, (int *)result); 9397c478bd9Sstevel@tonic-gate break; 9407c478bd9Sstevel@tonic-gate default: 9417c478bd9Sstevel@tonic-gate ret = DDI_ENOTSUP; 9427c478bd9Sstevel@tonic-gate break; 9437c478bd9Sstevel@tonic-gate } 9447c478bd9Sstevel@tonic-gate 9457c478bd9Sstevel@tonic-gate return (ret); 9467c478bd9Sstevel@tonic-gate } 9477c478bd9Sstevel@tonic-gate 9486d44af1bSesolom static struct { 9496d44af1bSesolom kstat_named_t pxintr_ks_name; 9506d44af1bSesolom kstat_named_t pxintr_ks_type; 9516d44af1bSesolom kstat_named_t pxintr_ks_cpu; 9526d44af1bSesolom kstat_named_t pxintr_ks_pil; 9536d44af1bSesolom kstat_named_t pxintr_ks_time; 9546d44af1bSesolom kstat_named_t pxintr_ks_ino; 9556d44af1bSesolom kstat_named_t pxintr_ks_cookie; 9566d44af1bSesolom kstat_named_t pxintr_ks_devpath; 9576d44af1bSesolom kstat_named_t pxintr_ks_buspath; 9586d44af1bSesolom } pxintr_ks_template = { 9596d44af1bSesolom { "name", KSTAT_DATA_CHAR }, 9606d44af1bSesolom { "type", KSTAT_DATA_CHAR }, 9616d44af1bSesolom { "cpu", KSTAT_DATA_UINT64 }, 9626d44af1bSesolom { "pil", KSTAT_DATA_UINT64 }, 9636d44af1bSesolom { "time", KSTAT_DATA_UINT64 }, 9646d44af1bSesolom { "ino", KSTAT_DATA_UINT64 }, 9656d44af1bSesolom { "cookie", KSTAT_DATA_UINT64 }, 9666d44af1bSesolom { "devpath", KSTAT_DATA_STRING }, 9676d44af1bSesolom { "buspath", KSTAT_DATA_STRING }, 9686d44af1bSesolom }; 9696d44af1bSesolom 9706d44af1bSesolom static uint32_t pxintr_ks_instance; 971d48713b8Sesolom static char ih_devpath[MAXPATHLEN]; 972d48713b8Sesolom static char ih_buspath[MAXPATHLEN]; 9736d44af1bSesolom kmutex_t pxintr_ks_template_lock; 9746d44af1bSesolom 9756d44af1bSesolom int 9766d44af1bSesolom px_ks_update(kstat_t *ksp, int rw) 9776d44af1bSesolom { 9786d44af1bSesolom px_ih_t *ih_p = ksp->ks_private; 9796d44af1bSesolom int maxlen = sizeof (pxintr_ks_template.pxintr_ks_name.value.c); 980b0fc0e77Sgovinda px_ino_pil_t *ipil_p = ih_p->ih_ipil_p; 981b0fc0e77Sgovinda px_ino_t *ino_p = ipil_p->ipil_ino_p; 982b0fc0e77Sgovinda px_t *px_p = ino_p->ino_ib_p->ib_px_p; 9836d44af1bSesolom devino_t ino; 9846d44af1bSesolom sysino_t sysino; 9856d44af1bSesolom 986b0fc0e77Sgovinda ino = ino_p->ino_ino; 987*d8d130aeSanbui if (px_lib_intr_devino_to_sysino(px_p->px_dip, ino, &sysino) != 988*d8d130aeSanbui DDI_SUCCESS) { 989*d8d130aeSanbui cmn_err(CE_WARN, "px_ks_update: px_lib_intr_devino_to_sysino " 990*d8d130aeSanbui "failed"); 991*d8d130aeSanbui } 9926d44af1bSesolom 9936d44af1bSesolom (void) snprintf(pxintr_ks_template.pxintr_ks_name.value.c, maxlen, 9946d44af1bSesolom "%s%d", ddi_driver_name(ih_p->ih_dip), 9956d44af1bSesolom ddi_get_instance(ih_p->ih_dip)); 9966d44af1bSesolom 9976d44af1bSesolom (void) ddi_pathname(ih_p->ih_dip, ih_devpath); 9986d44af1bSesolom (void) ddi_pathname(px_p->px_dip, ih_buspath); 9996d44af1bSesolom kstat_named_setstr(&pxintr_ks_template.pxintr_ks_devpath, ih_devpath); 10006d44af1bSesolom kstat_named_setstr(&pxintr_ks_template.pxintr_ks_buspath, ih_buspath); 10016d44af1bSesolom 1002e1d9f4e6Sschwartz if (ih_p->ih_intr_state == PX_INTR_STATE_ENABLE) { 1003e1d9f4e6Sschwartz 10042917a9c9Sschwartz switch (i_ddi_intr_get_current_type(ih_p->ih_dip)) { 10052917a9c9Sschwartz case DDI_INTR_TYPE_MSI: 10062917a9c9Sschwartz (void) strcpy(pxintr_ks_template.pxintr_ks_type.value.c, 10072917a9c9Sschwartz "msi"); 10082917a9c9Sschwartz break; 10092917a9c9Sschwartz case DDI_INTR_TYPE_MSIX: 10102917a9c9Sschwartz (void) strcpy(pxintr_ks_template.pxintr_ks_type.value.c, 10112917a9c9Sschwartz "msix"); 10122917a9c9Sschwartz break; 10132917a9c9Sschwartz default: 10142917a9c9Sschwartz (void) strcpy(pxintr_ks_template.pxintr_ks_type.value.c, 10152917a9c9Sschwartz "fixed"); 10162917a9c9Sschwartz break; 10172917a9c9Sschwartz } 10182917a9c9Sschwartz 1019b0fc0e77Sgovinda pxintr_ks_template.pxintr_ks_cpu.value.ui64 = ino_p->ino_cpuid; 1020b0fc0e77Sgovinda pxintr_ks_template.pxintr_ks_pil.value.ui64 = ipil_p->ipil_pil; 1021e1d9f4e6Sschwartz pxintr_ks_template.pxintr_ks_time.value.ui64 = ih_p->ih_nsec + 1022e1d9f4e6Sschwartz (uint64_t)tick2ns((hrtime_t)ih_p->ih_ticks, 1023b0fc0e77Sgovinda ino_p->ino_cpuid); 1024e1d9f4e6Sschwartz pxintr_ks_template.pxintr_ks_ino.value.ui64 = ino; 1025e1d9f4e6Sschwartz pxintr_ks_template.pxintr_ks_cookie.value.ui64 = sysino; 1026e1d9f4e6Sschwartz } else { 1027e1d9f4e6Sschwartz (void) strcpy(pxintr_ks_template.pxintr_ks_type.value.c, 1028e1d9f4e6Sschwartz "disabled"); 1029e1d9f4e6Sschwartz pxintr_ks_template.pxintr_ks_cpu.value.ui64 = 0; 1030e1d9f4e6Sschwartz pxintr_ks_template.pxintr_ks_pil.value.ui64 = 0; 1031e1d9f4e6Sschwartz pxintr_ks_template.pxintr_ks_time.value.ui64 = 0; 1032e1d9f4e6Sschwartz pxintr_ks_template.pxintr_ks_ino.value.ui64 = 0; 1033e1d9f4e6Sschwartz pxintr_ks_template.pxintr_ks_cookie.value.ui64 = 0; 1034e1d9f4e6Sschwartz } 10356d44af1bSesolom return (0); 10366d44af1bSesolom } 10376d44af1bSesolom 10386d44af1bSesolom void 10396d44af1bSesolom px_create_intr_kstats(px_ih_t *ih_p) 10406d44af1bSesolom { 10416d44af1bSesolom msiq_rec_type_t rec_type = ih_p->ih_rec_type; 10426d44af1bSesolom 10436d44af1bSesolom ASSERT(ih_p->ih_ksp == NULL); 10446d44af1bSesolom 10456d44af1bSesolom /* 10466d44af1bSesolom * Create pci_intrs::: kstats for all ih types except messages, 10476d44af1bSesolom * which represent unusual conditions and don't need to be tracked. 10486d44af1bSesolom */ 10496d44af1bSesolom if (rec_type == 0 || rec_type == MSI32_REC || rec_type == MSI64_REC) { 10506d44af1bSesolom ih_p->ih_ksp = kstat_create("pci_intrs", 10516d44af1bSesolom atomic_inc_32_nv(&pxintr_ks_instance), "config", 10526d44af1bSesolom "interrupts", KSTAT_TYPE_NAMED, 10536d44af1bSesolom sizeof (pxintr_ks_template) / sizeof (kstat_named_t), 10546d44af1bSesolom KSTAT_FLAG_VIRTUAL); 10556d44af1bSesolom } 10566d44af1bSesolom if (ih_p->ih_ksp != NULL) { 10576d44af1bSesolom ih_p->ih_ksp->ks_data_size += MAXPATHLEN * 2; 10586d44af1bSesolom ih_p->ih_ksp->ks_lock = &pxintr_ks_template_lock; 10596d44af1bSesolom ih_p->ih_ksp->ks_data = &pxintr_ks_template; 10606d44af1bSesolom ih_p->ih_ksp->ks_private = ih_p; 10616d44af1bSesolom ih_p->ih_ksp->ks_update = px_ks_update; 10626d44af1bSesolom } 10636d44af1bSesolom } 10646d44af1bSesolom 1065a195726fSgovinda /* 1066a195726fSgovinda * px_add_intx_intr: 1067a195726fSgovinda * 1068a195726fSgovinda * This function is called to register INTx and legacy hardware 1069a195726fSgovinda * interrupt pins interrupts. 1070a195726fSgovinda */ 10717c478bd9Sstevel@tonic-gate int 10727c478bd9Sstevel@tonic-gate px_add_intx_intr(dev_info_t *dip, dev_info_t *rdip, 10737c478bd9Sstevel@tonic-gate ddi_intr_handle_impl_t *hdlp) 10747c478bd9Sstevel@tonic-gate { 10757c478bd9Sstevel@tonic-gate px_t *px_p = INST_TO_STATE(ddi_get_instance(dip)); 10767c478bd9Sstevel@tonic-gate px_ib_t *ib_p = px_p->px_ib_p; 10777c478bd9Sstevel@tonic-gate devino_t ino; 10787c478bd9Sstevel@tonic-gate px_ih_t *ih_p; 1079b0fc0e77Sgovinda px_ino_t *ino_p; 1080b0fc0e77Sgovinda px_ino_pil_t *ipil_p, *ipil_list; 10817c478bd9Sstevel@tonic-gate int32_t weight; 10827c478bd9Sstevel@tonic-gate int ret = DDI_SUCCESS; 10837c478bd9Sstevel@tonic-gate 10847c478bd9Sstevel@tonic-gate ino = hdlp->ih_vector; 10857c478bd9Sstevel@tonic-gate 10867c478bd9Sstevel@tonic-gate DBG(DBG_A_INTX, dip, "px_add_intx_intr: rdip=%s%d ino=%x " 10877c478bd9Sstevel@tonic-gate "handler=%x arg1=%x arg2=%x\n", ddi_driver_name(rdip), 10887c478bd9Sstevel@tonic-gate ddi_get_instance(rdip), ino, hdlp->ih_cb_func, 10897c478bd9Sstevel@tonic-gate hdlp->ih_cb_arg1, hdlp->ih_cb_arg2); 10907c478bd9Sstevel@tonic-gate 10917c478bd9Sstevel@tonic-gate ih_p = px_ib_alloc_ih(rdip, hdlp->ih_inum, 10927c478bd9Sstevel@tonic-gate hdlp->ih_cb_func, hdlp->ih_cb_arg1, hdlp->ih_cb_arg2, 0, 0); 10937c478bd9Sstevel@tonic-gate 10947c478bd9Sstevel@tonic-gate mutex_enter(&ib_p->ib_ino_lst_mutex); 10957c478bd9Sstevel@tonic-gate 1096b0fc0e77Sgovinda ino_p = px_ib_locate_ino(ib_p, ino); 1097b0fc0e77Sgovinda ipil_list = ino_p ? ino_p->ino_ipil_p : NULL; 1098b0fc0e77Sgovinda 1099b0fc0e77Sgovinda /* Sharing ino */ 1100b0fc0e77Sgovinda if (ino_p && (ipil_p = px_ib_ino_locate_ipil(ino_p, hdlp->ih_pri))) { 1101b0fc0e77Sgovinda if (px_ib_intr_locate_ih(ipil_p, rdip, hdlp->ih_inum, 0, 0)) { 11027c478bd9Sstevel@tonic-gate DBG(DBG_A_INTX, dip, "px_add_intx_intr: " 1103b0fc0e77Sgovinda "dup intr #%d\n", hdlp->ih_inum); 11047c478bd9Sstevel@tonic-gate 11057c478bd9Sstevel@tonic-gate ret = DDI_FAILURE; 11067c478bd9Sstevel@tonic-gate goto fail1; 11077c478bd9Sstevel@tonic-gate } 11087c478bd9Sstevel@tonic-gate 11097c478bd9Sstevel@tonic-gate /* Save mondo value in hdlp */ 11107c478bd9Sstevel@tonic-gate hdlp->ih_vector = ino_p->ino_sysino; 11117c478bd9Sstevel@tonic-gate 1112b0fc0e77Sgovinda if ((ret = px_ib_ino_add_intr(px_p, ipil_p, 1113b0fc0e77Sgovinda ih_p)) != DDI_SUCCESS) 11147c478bd9Sstevel@tonic-gate goto fail1; 11157c478bd9Sstevel@tonic-gate 1116b0fc0e77Sgovinda goto ino_done; 1117b0fc0e77Sgovinda } 11187c478bd9Sstevel@tonic-gate 1119b0fc0e77Sgovinda if (hdlp->ih_pri == 0) 1120b0fc0e77Sgovinda hdlp->ih_pri = px_class_to_pil(rdip); 11217c478bd9Sstevel@tonic-gate 1122b0fc0e77Sgovinda ipil_p = px_ib_new_ino_pil(ib_p, ino, hdlp->ih_pri, ih_p); 1123b0fc0e77Sgovinda ino_p = ipil_p->ipil_ino_p; 11247c478bd9Sstevel@tonic-gate 1125b0fc0e77Sgovinda /* Save mondo value in hdlp */ 1126b0fc0e77Sgovinda hdlp->ih_vector = ino_p->ino_sysino; 11277c478bd9Sstevel@tonic-gate 1128b0fc0e77Sgovinda DBG(DBG_A_INTX, dip, "px_add_intx_intr: pil=0x%x mondo=0x%x\n", 1129b0fc0e77Sgovinda hdlp->ih_pri, hdlp->ih_vector); 11307c478bd9Sstevel@tonic-gate 1131b0fc0e77Sgovinda DDI_INTR_ASSIGN_HDLR_N_ARGS(hdlp, 1132b0fc0e77Sgovinda (ddi_intr_handler_t *)px_intx_intr, (caddr_t)ipil_p, NULL); 1133b0fc0e77Sgovinda 1134b0fc0e77Sgovinda ret = i_ddi_add_ivintr(hdlp); 11357c478bd9Sstevel@tonic-gate 1136b0fc0e77Sgovinda /* 1137b0fc0e77Sgovinda * Restore original interrupt handler 1138b0fc0e77Sgovinda * and arguments in interrupt handle. 1139b0fc0e77Sgovinda */ 1140b0fc0e77Sgovinda DDI_INTR_ASSIGN_HDLR_N_ARGS(hdlp, ih_p->ih_handler, 1141b0fc0e77Sgovinda ih_p->ih_handler_arg1, ih_p->ih_handler_arg2); 11427c478bd9Sstevel@tonic-gate 1143b0fc0e77Sgovinda if (ret != DDI_SUCCESS) 1144b0fc0e77Sgovinda goto fail2; 11457c478bd9Sstevel@tonic-gate 1146b0fc0e77Sgovinda /* Save the pil for this ino */ 1147b0fc0e77Sgovinda ipil_p->ipil_pil = hdlp->ih_pri; 1148b0fc0e77Sgovinda 1149b0fc0e77Sgovinda /* Select cpu, saving it for sharing and removal */ 1150b0fc0e77Sgovinda if (ipil_list == NULL) { 11517c478bd9Sstevel@tonic-gate ino_p->ino_cpuid = intr_dist_cpuid(); 11527c478bd9Sstevel@tonic-gate 11537c478bd9Sstevel@tonic-gate /* Enable interrupt */ 11547c478bd9Sstevel@tonic-gate px_ib_intr_enable(px_p, ino_p->ino_cpuid, ino); 11557c478bd9Sstevel@tonic-gate } 11567c478bd9Sstevel@tonic-gate 1157b0fc0e77Sgovinda ino_done: 1158b0fc0e77Sgovinda /* Add weight to the cpu that we are already targeting */ 11597c478bd9Sstevel@tonic-gate weight = px_class_to_intr_weight(rdip); 11607c478bd9Sstevel@tonic-gate intr_dist_cpuid_add_device_weight(ino_p->ino_cpuid, rdip, weight); 11617c478bd9Sstevel@tonic-gate 1162b0fc0e77Sgovinda ih_p->ih_ipil_p = ipil_p; 11636d44af1bSesolom px_create_intr_kstats(ih_p); 11647c478bd9Sstevel@tonic-gate if (ih_p->ih_ksp) 11657c478bd9Sstevel@tonic-gate kstat_install(ih_p->ih_ksp); 11667c478bd9Sstevel@tonic-gate mutex_exit(&ib_p->ib_ino_lst_mutex); 11677c478bd9Sstevel@tonic-gate 11687c478bd9Sstevel@tonic-gate DBG(DBG_A_INTX, dip, "px_add_intx_intr: done! Interrupt 0x%x pil=%x\n", 11697c478bd9Sstevel@tonic-gate ino_p->ino_sysino, hdlp->ih_pri); 11707c478bd9Sstevel@tonic-gate 11717c478bd9Sstevel@tonic-gate return (ret); 11727c478bd9Sstevel@tonic-gate fail2: 1173b0fc0e77Sgovinda px_ib_delete_ino_pil(ib_p, ipil_p); 11747c478bd9Sstevel@tonic-gate fail1: 11757c478bd9Sstevel@tonic-gate if (ih_p->ih_config_handle) 11767c478bd9Sstevel@tonic-gate pci_config_teardown(&ih_p->ih_config_handle); 11777c478bd9Sstevel@tonic-gate 11787c478bd9Sstevel@tonic-gate mutex_exit(&ib_p->ib_ino_lst_mutex); 11797c478bd9Sstevel@tonic-gate kmem_free(ih_p, sizeof (px_ih_t)); 11807c478bd9Sstevel@tonic-gate 11817c478bd9Sstevel@tonic-gate DBG(DBG_A_INTX, dip, "px_add_intx_intr: Failed! Interrupt 0x%x " 11827c478bd9Sstevel@tonic-gate "pil=%x\n", ino_p->ino_sysino, hdlp->ih_pri); 11837c478bd9Sstevel@tonic-gate 11847c478bd9Sstevel@tonic-gate return (ret); 11857c478bd9Sstevel@tonic-gate } 11867c478bd9Sstevel@tonic-gate 1187a195726fSgovinda /* 1188a195726fSgovinda * px_rem_intx_intr: 1189a195726fSgovinda * 1190a195726fSgovinda * This function is called to unregister INTx and legacy hardware 1191a195726fSgovinda * interrupt pins interrupts. 1192a195726fSgovinda */ 11937c478bd9Sstevel@tonic-gate int 11947c478bd9Sstevel@tonic-gate px_rem_intx_intr(dev_info_t *dip, dev_info_t *rdip, 11957c478bd9Sstevel@tonic-gate ddi_intr_handle_impl_t *hdlp) 11967c478bd9Sstevel@tonic-gate { 11977c478bd9Sstevel@tonic-gate px_t *px_p = INST_TO_STATE(ddi_get_instance(dip)); 11987c478bd9Sstevel@tonic-gate px_ib_t *ib_p = px_p->px_ib_p; 11997c478bd9Sstevel@tonic-gate devino_t ino; 12007c478bd9Sstevel@tonic-gate cpuid_t curr_cpu; 1201b0fc0e77Sgovinda px_ino_t *ino_p; 1202b0fc0e77Sgovinda px_ino_pil_t *ipil_p; 12037c478bd9Sstevel@tonic-gate px_ih_t *ih_p; 12047c478bd9Sstevel@tonic-gate int ret = DDI_SUCCESS; 12057c478bd9Sstevel@tonic-gate 12067c478bd9Sstevel@tonic-gate ino = hdlp->ih_vector; 12077c478bd9Sstevel@tonic-gate 12087c478bd9Sstevel@tonic-gate DBG(DBG_R_INTX, dip, "px_rem_intx_intr: rdip=%s%d ino=%x\n", 12097c478bd9Sstevel@tonic-gate ddi_driver_name(rdip), ddi_get_instance(rdip), ino); 12107c478bd9Sstevel@tonic-gate 12117c478bd9Sstevel@tonic-gate mutex_enter(&ib_p->ib_ino_lst_mutex); 12127c478bd9Sstevel@tonic-gate 12137c478bd9Sstevel@tonic-gate ino_p = px_ib_locate_ino(ib_p, ino); 1214b0fc0e77Sgovinda ipil_p = px_ib_ino_locate_ipil(ino_p, hdlp->ih_pri); 1215b0fc0e77Sgovinda ih_p = px_ib_intr_locate_ih(ipil_p, rdip, hdlp->ih_inum, 0, 0); 12167c478bd9Sstevel@tonic-gate 12177c478bd9Sstevel@tonic-gate /* Get the current cpu */ 12187c478bd9Sstevel@tonic-gate if ((ret = px_lib_intr_gettarget(px_p->px_dip, ino_p->ino_sysino, 12197c478bd9Sstevel@tonic-gate &curr_cpu)) != DDI_SUCCESS) 12207c478bd9Sstevel@tonic-gate goto fail; 12217c478bd9Sstevel@tonic-gate 1222b0fc0e77Sgovinda if ((ret = px_ib_ino_rem_intr(px_p, ipil_p, ih_p)) != DDI_SUCCESS) 12237c478bd9Sstevel@tonic-gate goto fail; 12247c478bd9Sstevel@tonic-gate 12257c478bd9Sstevel@tonic-gate intr_dist_cpuid_rem_device_weight(ino_p->ino_cpuid, rdip); 12267c478bd9Sstevel@tonic-gate 1227b0fc0e77Sgovinda if (ipil_p->ipil_ih_size == 0) { 12287c478bd9Sstevel@tonic-gate hdlp->ih_vector = ino_p->ino_sysino; 12297c478bd9Sstevel@tonic-gate i_ddi_rem_ivintr(hdlp); 12307c478bd9Sstevel@tonic-gate 1231b0fc0e77Sgovinda px_ib_delete_ino_pil(ib_p, ipil_p); 1232b0fc0e77Sgovinda } 1233b0fc0e77Sgovinda 1234b0fc0e77Sgovinda if (ino_p->ino_ipil_size == 0) { 1235b0fc0e77Sgovinda kmem_free(ino_p, sizeof (px_ino_t)); 12367c478bd9Sstevel@tonic-gate } else { 12370d2a6fcfSegillett /* Re-enable interrupt only if mapping register still shared */ 12380d2a6fcfSegillett PX_INTR_ENABLE(px_p->px_dip, ino_p->ino_sysino, curr_cpu); 12397c478bd9Sstevel@tonic-gate } 12407c478bd9Sstevel@tonic-gate 12417c478bd9Sstevel@tonic-gate fail: 12427c478bd9Sstevel@tonic-gate mutex_exit(&ib_p->ib_ino_lst_mutex); 12437c478bd9Sstevel@tonic-gate return (ret); 12447c478bd9Sstevel@tonic-gate } 12457c478bd9Sstevel@tonic-gate 1246a195726fSgovinda /* 1247a195726fSgovinda * px_add_msiq_intr: 1248a195726fSgovinda * 1249a195726fSgovinda * This function is called to register MSI/Xs and PCIe message interrupts. 1250a195726fSgovinda */ 12517c478bd9Sstevel@tonic-gate int 12527c478bd9Sstevel@tonic-gate px_add_msiq_intr(dev_info_t *dip, dev_info_t *rdip, 12537c478bd9Sstevel@tonic-gate ddi_intr_handle_impl_t *hdlp, msiq_rec_type_t rec_type, 12547c478bd9Sstevel@tonic-gate msgcode_t msg_code, msiqid_t *msiq_id_p) 12557c478bd9Sstevel@tonic-gate { 12567c478bd9Sstevel@tonic-gate px_t *px_p = INST_TO_STATE(ddi_get_instance(dip)); 12577c478bd9Sstevel@tonic-gate px_ib_t *ib_p = px_p->px_ib_p; 12587c478bd9Sstevel@tonic-gate px_msiq_state_t *msiq_state_p = &ib_p->ib_msiq_state; 12597c478bd9Sstevel@tonic-gate devino_t ino; 12607c478bd9Sstevel@tonic-gate px_ih_t *ih_p; 1261b0fc0e77Sgovinda px_ino_t *ino_p; 1262b0fc0e77Sgovinda px_ino_pil_t *ipil_p, *ipil_list; 12637c478bd9Sstevel@tonic-gate int32_t weight; 12647c478bd9Sstevel@tonic-gate int ret = DDI_SUCCESS; 12657c478bd9Sstevel@tonic-gate 12667c478bd9Sstevel@tonic-gate DBG(DBG_MSIQ, dip, "px_add_msiq_intr: rdip=%s%d handler=%x " 12677c478bd9Sstevel@tonic-gate "arg1=%x arg2=%x\n", ddi_driver_name(rdip), ddi_get_instance(rdip), 12687c478bd9Sstevel@tonic-gate hdlp->ih_cb_func, hdlp->ih_cb_arg1, hdlp->ih_cb_arg2); 12697c478bd9Sstevel@tonic-gate 12707c478bd9Sstevel@tonic-gate if ((ret = px_msiq_alloc(px_p, rec_type, msiq_id_p)) != DDI_SUCCESS) { 12717c478bd9Sstevel@tonic-gate DBG(DBG_MSIQ, dip, "px_add_msiq_intr: " 12727c478bd9Sstevel@tonic-gate "msiq allocation failed\n"); 12737c478bd9Sstevel@tonic-gate return (ret); 12747c478bd9Sstevel@tonic-gate } 12757c478bd9Sstevel@tonic-gate 12767c478bd9Sstevel@tonic-gate ino = px_msiqid_to_devino(px_p, *msiq_id_p); 12777c478bd9Sstevel@tonic-gate 12787c478bd9Sstevel@tonic-gate ih_p = px_ib_alloc_ih(rdip, hdlp->ih_inum, hdlp->ih_cb_func, 12797c478bd9Sstevel@tonic-gate hdlp->ih_cb_arg1, hdlp->ih_cb_arg2, rec_type, msg_code); 12807c478bd9Sstevel@tonic-gate 12817c478bd9Sstevel@tonic-gate mutex_enter(&ib_p->ib_ino_lst_mutex); 12827c478bd9Sstevel@tonic-gate 1283b0fc0e77Sgovinda ino_p = px_ib_locate_ino(ib_p, ino); 1284b0fc0e77Sgovinda ipil_list = ino_p ? ino_p->ino_ipil_p : NULL; 1285b0fc0e77Sgovinda 1286b0fc0e77Sgovinda /* Sharing ino */ 1287b0fc0e77Sgovinda if (ino_p && (ipil_p = px_ib_ino_locate_ipil(ino_p, hdlp->ih_pri))) { 1288b0fc0e77Sgovinda if (px_ib_intr_locate_ih(ipil_p, rdip, 1289b0fc0e77Sgovinda hdlp->ih_inum, rec_type, msg_code)) { 12907c478bd9Sstevel@tonic-gate DBG(DBG_MSIQ, dip, "px_add_msiq_intr: " 1291b0fc0e77Sgovinda "dup intr #%d\n", hdlp->ih_inum); 12927c478bd9Sstevel@tonic-gate 12937c478bd9Sstevel@tonic-gate ret = DDI_FAILURE; 12947c478bd9Sstevel@tonic-gate goto fail1; 12957c478bd9Sstevel@tonic-gate } 12967c478bd9Sstevel@tonic-gate 1297b0fc0e77Sgovinda /* Save mondo value in hdlp */ 1298b0fc0e77Sgovinda hdlp->ih_vector = ino_p->ino_sysino; 1299b0fc0e77Sgovinda 1300b0fc0e77Sgovinda if ((ret = px_ib_ino_add_intr(px_p, ipil_p, 1301b0fc0e77Sgovinda ih_p)) != DDI_SUCCESS) 13027c478bd9Sstevel@tonic-gate goto fail1; 13037c478bd9Sstevel@tonic-gate 1304b0fc0e77Sgovinda goto ino_done; 1305b0fc0e77Sgovinda } 13067c478bd9Sstevel@tonic-gate 1307b0fc0e77Sgovinda if (hdlp->ih_pri == 0) 1308b0fc0e77Sgovinda hdlp->ih_pri = px_class_to_pil(rdip); 13097c478bd9Sstevel@tonic-gate 1310b0fc0e77Sgovinda ipil_p = px_ib_new_ino_pil(ib_p, ino, hdlp->ih_pri, ih_p); 1311b0fc0e77Sgovinda ino_p = ipil_p->ipil_ino_p; 13127c478bd9Sstevel@tonic-gate 1313b0fc0e77Sgovinda ino_p->ino_msiq_p = msiq_state_p->msiq_p + 1314b0fc0e77Sgovinda (*msiq_id_p - msiq_state_p->msiq_1st_msiq_id); 13157c478bd9Sstevel@tonic-gate 1316b0fc0e77Sgovinda /* Save mondo value in hdlp */ 1317b0fc0e77Sgovinda hdlp->ih_vector = ino_p->ino_sysino; 13187c478bd9Sstevel@tonic-gate 1319b0fc0e77Sgovinda DBG(DBG_MSIQ, dip, "px_add_msiq_intr: pil=0x%x mondo=0x%x\n", 1320b0fc0e77Sgovinda hdlp->ih_pri, hdlp->ih_vector); 13217c478bd9Sstevel@tonic-gate 1322b0fc0e77Sgovinda DDI_INTR_ASSIGN_HDLR_N_ARGS(hdlp, 1323b0fc0e77Sgovinda (ddi_intr_handler_t *)px_msiq_intr, (caddr_t)ipil_p, NULL); 1324b0fc0e77Sgovinda 1325b0fc0e77Sgovinda ret = i_ddi_add_ivintr(hdlp); 1326b0fc0e77Sgovinda 1327b0fc0e77Sgovinda /* 1328b0fc0e77Sgovinda * Restore original interrupt handler 1329b0fc0e77Sgovinda * and arguments in interrupt handle. 1330b0fc0e77Sgovinda */ 1331b0fc0e77Sgovinda DDI_INTR_ASSIGN_HDLR_N_ARGS(hdlp, ih_p->ih_handler, 1332b0fc0e77Sgovinda ih_p->ih_handler_arg1, ih_p->ih_handler_arg2); 13337c478bd9Sstevel@tonic-gate 1334b0fc0e77Sgovinda if (ret != DDI_SUCCESS) 1335b0fc0e77Sgovinda goto fail2; 13367c478bd9Sstevel@tonic-gate 1337b0fc0e77Sgovinda /* Save the pil for this ino */ 1338b0fc0e77Sgovinda ipil_p->ipil_pil = hdlp->ih_pri; 1339b0fc0e77Sgovinda 1340b0fc0e77Sgovinda /* Select cpu, saving it for sharing and removal */ 1341b0fc0e77Sgovinda if (ipil_list == NULL) { 1342b0fc0e77Sgovinda ino_p->ino_cpuid = intr_dist_cpuid(); 13437c478bd9Sstevel@tonic-gate 13447c478bd9Sstevel@tonic-gate /* Enable MSIQ */ 13457c478bd9Sstevel@tonic-gate px_lib_msiq_setstate(dip, *msiq_id_p, PCI_MSIQ_STATE_IDLE); 13467c478bd9Sstevel@tonic-gate px_lib_msiq_setvalid(dip, *msiq_id_p, PCI_MSIQ_VALID); 13477c478bd9Sstevel@tonic-gate 13487c478bd9Sstevel@tonic-gate /* Enable interrupt */ 1349b0fc0e77Sgovinda px_ib_intr_enable(px_p, ino_p->ino_cpuid, ino); 13507c478bd9Sstevel@tonic-gate } 13517c478bd9Sstevel@tonic-gate 1352b0fc0e77Sgovinda ino_done: 1353b0fc0e77Sgovinda /* Add weight to the cpu that we are already targeting */ 13547c478bd9Sstevel@tonic-gate weight = px_class_to_intr_weight(rdip); 13557c478bd9Sstevel@tonic-gate intr_dist_cpuid_add_device_weight(ino_p->ino_cpuid, rdip, weight); 13567c478bd9Sstevel@tonic-gate 1357b0fc0e77Sgovinda ih_p->ih_ipil_p = ipil_p; 13586d44af1bSesolom px_create_intr_kstats(ih_p); 13597c478bd9Sstevel@tonic-gate if (ih_p->ih_ksp) 13607c478bd9Sstevel@tonic-gate kstat_install(ih_p->ih_ksp); 13617c478bd9Sstevel@tonic-gate mutex_exit(&ib_p->ib_ino_lst_mutex); 13627c478bd9Sstevel@tonic-gate 13637c478bd9Sstevel@tonic-gate DBG(DBG_MSIQ, dip, "px_add_msiq_intr: done! Interrupt 0x%x pil=%x\n", 13647c478bd9Sstevel@tonic-gate ino_p->ino_sysino, hdlp->ih_pri); 13657c478bd9Sstevel@tonic-gate 13667c478bd9Sstevel@tonic-gate return (ret); 13677c478bd9Sstevel@tonic-gate fail2: 1368b0fc0e77Sgovinda px_ib_delete_ino_pil(ib_p, ipil_p); 13697c478bd9Sstevel@tonic-gate fail1: 13707c478bd9Sstevel@tonic-gate if (ih_p->ih_config_handle) 13717c478bd9Sstevel@tonic-gate pci_config_teardown(&ih_p->ih_config_handle); 13727c478bd9Sstevel@tonic-gate 13737c478bd9Sstevel@tonic-gate mutex_exit(&ib_p->ib_ino_lst_mutex); 13747c478bd9Sstevel@tonic-gate kmem_free(ih_p, sizeof (px_ih_t)); 13757c478bd9Sstevel@tonic-gate 13767c478bd9Sstevel@tonic-gate DBG(DBG_MSIQ, dip, "px_add_msiq_intr: Failed! Interrupt 0x%x pil=%x\n", 13777c478bd9Sstevel@tonic-gate ino_p->ino_sysino, hdlp->ih_pri); 13787c478bd9Sstevel@tonic-gate 13797c478bd9Sstevel@tonic-gate return (ret); 13807c478bd9Sstevel@tonic-gate } 13817c478bd9Sstevel@tonic-gate 1382a195726fSgovinda /* 1383a195726fSgovinda * px_rem_msiq_intr: 1384a195726fSgovinda * 1385a195726fSgovinda * This function is called to unregister MSI/Xs and PCIe message interrupts. 1386a195726fSgovinda */ 13877c478bd9Sstevel@tonic-gate int 13887c478bd9Sstevel@tonic-gate px_rem_msiq_intr(dev_info_t *dip, dev_info_t *rdip, 13897c478bd9Sstevel@tonic-gate ddi_intr_handle_impl_t *hdlp, msiq_rec_type_t rec_type, 13907c478bd9Sstevel@tonic-gate msgcode_t msg_code, msiqid_t msiq_id) 13917c478bd9Sstevel@tonic-gate { 13927c478bd9Sstevel@tonic-gate px_t *px_p = INST_TO_STATE(ddi_get_instance(dip)); 13937c478bd9Sstevel@tonic-gate px_ib_t *ib_p = px_p->px_ib_p; 13947c478bd9Sstevel@tonic-gate devino_t ino = px_msiqid_to_devino(px_p, msiq_id); 13957c478bd9Sstevel@tonic-gate cpuid_t curr_cpu; 1396b0fc0e77Sgovinda px_ino_t *ino_p; 1397b0fc0e77Sgovinda px_ino_pil_t *ipil_p; 13987c478bd9Sstevel@tonic-gate px_ih_t *ih_p; 13997c478bd9Sstevel@tonic-gate int ret = DDI_SUCCESS; 14007c478bd9Sstevel@tonic-gate 14017c478bd9Sstevel@tonic-gate DBG(DBG_MSIQ, dip, "px_rem_msiq_intr: rdip=%s%d msiq_id=%x ino=%x\n", 14027c478bd9Sstevel@tonic-gate ddi_driver_name(rdip), ddi_get_instance(rdip), msiq_id, ino); 14037c478bd9Sstevel@tonic-gate 14047c478bd9Sstevel@tonic-gate mutex_enter(&ib_p->ib_ino_lst_mutex); 14057c478bd9Sstevel@tonic-gate 14067c478bd9Sstevel@tonic-gate ino_p = px_ib_locate_ino(ib_p, ino); 1407b0fc0e77Sgovinda ipil_p = px_ib_ino_locate_ipil(ino_p, hdlp->ih_pri); 1408b0fc0e77Sgovinda ih_p = px_ib_intr_locate_ih(ipil_p, rdip, hdlp->ih_inum, rec_type, 1409b0fc0e77Sgovinda msg_code); 14107c478bd9Sstevel@tonic-gate 14117c478bd9Sstevel@tonic-gate /* Get the current cpu */ 14127c478bd9Sstevel@tonic-gate if ((ret = px_lib_intr_gettarget(px_p->px_dip, ino_p->ino_sysino, 14137c478bd9Sstevel@tonic-gate &curr_cpu)) != DDI_SUCCESS) 14147c478bd9Sstevel@tonic-gate goto fail; 14157c478bd9Sstevel@tonic-gate 1416b0fc0e77Sgovinda if ((ret = px_ib_ino_rem_intr(px_p, ipil_p, ih_p)) != DDI_SUCCESS) 14177c478bd9Sstevel@tonic-gate goto fail; 14187c478bd9Sstevel@tonic-gate 14197c478bd9Sstevel@tonic-gate intr_dist_cpuid_rem_device_weight(ino_p->ino_cpuid, rdip); 14207c478bd9Sstevel@tonic-gate 1421b0fc0e77Sgovinda if (ipil_p->ipil_ih_size == 0) { 14227c478bd9Sstevel@tonic-gate hdlp->ih_vector = ino_p->ino_sysino; 14237c478bd9Sstevel@tonic-gate i_ddi_rem_ivintr(hdlp); 14247c478bd9Sstevel@tonic-gate 1425b0fc0e77Sgovinda px_ib_delete_ino_pil(ib_p, ipil_p); 1426b0fc0e77Sgovinda 1427b0fc0e77Sgovinda if (ino_p->ino_ipil_size == 0) 1428b0fc0e77Sgovinda px_lib_msiq_setvalid(dip, 1429b0fc0e77Sgovinda px_devino_to_msiqid(px_p, ino), PCI_MSIQ_INVALID); 14307c478bd9Sstevel@tonic-gate 14317c478bd9Sstevel@tonic-gate (void) px_msiq_free(px_p, msiq_id); 1432b0fc0e77Sgovinda } 1433b0fc0e77Sgovinda 1434b0fc0e77Sgovinda if (ino_p->ino_ipil_size == 0) { 1435b0fc0e77Sgovinda kmem_free(ino_p, sizeof (px_ino_t)); 14367c478bd9Sstevel@tonic-gate } else { 14370d2a6fcfSegillett /* Re-enable interrupt only if mapping register still shared */ 14380d2a6fcfSegillett PX_INTR_ENABLE(px_p->px_dip, ino_p->ino_sysino, curr_cpu); 14397c478bd9Sstevel@tonic-gate } 14407c478bd9Sstevel@tonic-gate 14417c478bd9Sstevel@tonic-gate fail: 14427c478bd9Sstevel@tonic-gate mutex_exit(&ib_p->ib_ino_lst_mutex); 14437c478bd9Sstevel@tonic-gate return (ret); 14447c478bd9Sstevel@tonic-gate } 1445