10Sstevel@tonic-gate /* 20Sstevel@tonic-gate * CDDL HEADER START 30Sstevel@tonic-gate * 40Sstevel@tonic-gate * The contents of this file are subject to the terms of the 51542Sjohnny * Common Development and Distribution License (the "License"). 61542Sjohnny * You may not use this file except in compliance with the License. 70Sstevel@tonic-gate * 80Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 90Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 100Sstevel@tonic-gate * See the License for the specific language governing permissions 110Sstevel@tonic-gate * and limitations under the License. 120Sstevel@tonic-gate * 130Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 140Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 150Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 160Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 170Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 180Sstevel@tonic-gate * 190Sstevel@tonic-gate * CDDL HEADER END 200Sstevel@tonic-gate */ 210Sstevel@tonic-gate /* 223780Segillett * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 230Sstevel@tonic-gate * Use is subject to license terms. 240Sstevel@tonic-gate */ 250Sstevel@tonic-gate 260Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 270Sstevel@tonic-gate 280Sstevel@tonic-gate /* 290Sstevel@tonic-gate * PX nexus interrupt handling: 300Sstevel@tonic-gate * PX device interrupt handler wrapper 310Sstevel@tonic-gate * PIL lookup routine 320Sstevel@tonic-gate * PX device interrupt related initchild code 330Sstevel@tonic-gate */ 340Sstevel@tonic-gate 350Sstevel@tonic-gate #include <sys/types.h> 360Sstevel@tonic-gate #include <sys/kmem.h> 370Sstevel@tonic-gate #include <sys/async.h> 380Sstevel@tonic-gate #include <sys/spl.h> 390Sstevel@tonic-gate #include <sys/sunddi.h> 4027Sjchu #include <sys/fm/protocol.h> 4127Sjchu #include <sys/fm/util.h> 420Sstevel@tonic-gate #include <sys/machsystm.h> /* e_ddi_nodeid_to_dip() */ 430Sstevel@tonic-gate #include <sys/ddi_impldefs.h> 440Sstevel@tonic-gate #include <sys/sdt.h> 450Sstevel@tonic-gate #include <sys/atomic.h> 460Sstevel@tonic-gate #include "px_obj.h" 4727Sjchu #include <sys/ontrap.h> 4827Sjchu #include <sys/membar.h> 4966Sesolom #include <sys/clock.h> 500Sstevel@tonic-gate 510Sstevel@tonic-gate /* 520Sstevel@tonic-gate * interrupt jabber: 530Sstevel@tonic-gate * 540Sstevel@tonic-gate * When an interrupt line is jabbering, every time the state machine for the 550Sstevel@tonic-gate * associated ino is idled, a new mondo will be sent and the ino will go into 560Sstevel@tonic-gate * the pending state again. The mondo will cause a new call to 570Sstevel@tonic-gate * px_intr_wrapper() which normally idles the ino's state machine which would 580Sstevel@tonic-gate * precipitate another trip round the loop. 590Sstevel@tonic-gate * 600Sstevel@tonic-gate * The loop can be broken by preventing the ino's state machine from being 610Sstevel@tonic-gate * idled when an interrupt line is jabbering. See the comment at the 620Sstevel@tonic-gate * beginning of px_intr_wrapper() explaining how the 'interrupt jabber 630Sstevel@tonic-gate * protection' code does this. 640Sstevel@tonic-gate */ 650Sstevel@tonic-gate 660Sstevel@tonic-gate /*LINTLIBRARY*/ 670Sstevel@tonic-gate 680Sstevel@tonic-gate /* 690Sstevel@tonic-gate * If the unclaimed interrupt count has reached the limit set by 700Sstevel@tonic-gate * pci_unclaimed_intr_max within the time limit, then all interrupts 710Sstevel@tonic-gate * on this ino is blocked by not idling the interrupt state machine. 720Sstevel@tonic-gate */ 730Sstevel@tonic-gate static int 742973Sgovinda px_spurintr(px_ino_pil_t *ipil_p) 750Sstevel@tonic-gate { 762973Sgovinda px_ino_t *ino_p = ipil_p->ipil_ino_p; 772973Sgovinda px_ih_t *ih_p = ipil_p->ipil_ih_start; 782973Sgovinda px_t *px_p = ino_p->ino_ib_p->ib_px_p; 792973Sgovinda char *err_fmt_str; 802973Sgovinda boolean_t blocked = B_FALSE; 812973Sgovinda int i; 820Sstevel@tonic-gate 832973Sgovinda if (ino_p->ino_unclaimed_intrs > px_unclaimed_intr_max) 840Sstevel@tonic-gate return (DDI_INTR_CLAIMED); 850Sstevel@tonic-gate 862973Sgovinda if (!ino_p->ino_unclaimed_intrs) 870Sstevel@tonic-gate ino_p->ino_spurintr_begin = ddi_get_lbolt(); 880Sstevel@tonic-gate 892973Sgovinda ino_p->ino_unclaimed_intrs++; 900Sstevel@tonic-gate 912973Sgovinda if (ino_p->ino_unclaimed_intrs <= px_unclaimed_intr_max) 920Sstevel@tonic-gate goto clear; 930Sstevel@tonic-gate 940Sstevel@tonic-gate if (drv_hztousec(ddi_get_lbolt() - ino_p->ino_spurintr_begin) 950Sstevel@tonic-gate > px_spurintr_duration) { 962973Sgovinda ino_p->ino_unclaimed_intrs = 0; 970Sstevel@tonic-gate goto clear; 980Sstevel@tonic-gate } 990Sstevel@tonic-gate err_fmt_str = "%s%d: ino 0x%x blocked"; 1002973Sgovinda blocked = B_TRUE; 1010Sstevel@tonic-gate goto warn; 1020Sstevel@tonic-gate clear: 1030Sstevel@tonic-gate err_fmt_str = "!%s%d: spurious interrupt from ino 0x%x"; 1040Sstevel@tonic-gate warn: 1050Sstevel@tonic-gate cmn_err(CE_WARN, err_fmt_str, NAMEINST(px_p->px_dip), ino_p->ino_ino); 1062973Sgovinda for (i = 0; i < ipil_p->ipil_ih_size; i++, ih_p = ih_p->ih_next) 1070Sstevel@tonic-gate cmn_err(CE_CONT, "!%s-%d#%x ", NAMEINST(ih_p->ih_dip), 1080Sstevel@tonic-gate ih_p->ih_inum); 1090Sstevel@tonic-gate cmn_err(CE_CONT, "!\n"); 1102973Sgovinda 1112973Sgovinda /* Clear the pending state */ 1122973Sgovinda if (blocked == B_FALSE) { 1132973Sgovinda if (px_lib_intr_setstate(px_p->px_dip, ino_p->ino_sysino, 1142973Sgovinda INTR_IDLE_STATE) != DDI_SUCCESS) 1152973Sgovinda return (DDI_INTR_UNCLAIMED); 1162973Sgovinda } 1172973Sgovinda 1180Sstevel@tonic-gate return (DDI_INTR_CLAIMED); 1190Sstevel@tonic-gate } 1200Sstevel@tonic-gate 1210Sstevel@tonic-gate extern uint64_t intr_get_time(void); 1220Sstevel@tonic-gate 1230Sstevel@tonic-gate /* 124693Sgovinda * px_intx_intr (INTx or legacy interrupt handler) 1250Sstevel@tonic-gate * 1260Sstevel@tonic-gate * This routine is used as wrapper around interrupt handlers installed by child 1270Sstevel@tonic-gate * device drivers. This routine invokes the driver interrupt handlers and 1280Sstevel@tonic-gate * examines the return codes. 1290Sstevel@tonic-gate * 1300Sstevel@tonic-gate * There is a count of unclaimed interrupts kept on a per-ino basis. If at 1310Sstevel@tonic-gate * least one handler claims the interrupt then the counter is halved and the 1320Sstevel@tonic-gate * interrupt state machine is idled. If no handler claims the interrupt then 1330Sstevel@tonic-gate * the counter is incremented by one and the state machine is idled. 1340Sstevel@tonic-gate * If the count ever reaches the limit value set by pci_unclaimed_intr_max 1350Sstevel@tonic-gate * then the interrupt state machine is not idled thus preventing any further 1360Sstevel@tonic-gate * interrupts on that ino. The state machine will only be idled again if a 1370Sstevel@tonic-gate * handler is subsequently added or removed. 1380Sstevel@tonic-gate * 1390Sstevel@tonic-gate * return value: DDI_INTR_CLAIMED if any handlers claimed the interrupt, 1400Sstevel@tonic-gate * DDI_INTR_UNCLAIMED otherwise. 1410Sstevel@tonic-gate */ 1420Sstevel@tonic-gate uint_t 1430Sstevel@tonic-gate px_intx_intr(caddr_t arg) 1440Sstevel@tonic-gate { 1452973Sgovinda px_ino_pil_t *ipil_p = (px_ino_pil_t *)arg; 1462973Sgovinda px_ino_t *ino_p = ipil_p->ipil_ino_p; 1470Sstevel@tonic-gate px_t *px_p = ino_p->ino_ib_p->ib_px_p; 1482973Sgovinda px_ih_t *ih_p = ipil_p->ipil_ih_start; 1492973Sgovinda ushort_t pil = ipil_p->ipil_pil; 1502973Sgovinda uint_t result = 0, r = DDI_INTR_UNCLAIMED; 1510Sstevel@tonic-gate int i; 1520Sstevel@tonic-gate 1530Sstevel@tonic-gate DBG(DBG_INTX_INTR, px_p->px_dip, "px_intx_intr:" 1540Sstevel@tonic-gate "ino=%x sysino=%llx pil=%x ih_size=%x ih_lst=%x\n", 1552973Sgovinda ino_p->ino_ino, ino_p->ino_sysino, ipil_p->ipil_pil, 1562973Sgovinda ipil_p->ipil_ih_size, ipil_p->ipil_ih_head); 1570Sstevel@tonic-gate 1582973Sgovinda for (i = 0; i < ipil_p->ipil_ih_size; i++, ih_p = ih_p->ih_next) { 1590Sstevel@tonic-gate dev_info_t *dip = ih_p->ih_dip; 1600Sstevel@tonic-gate uint_t (*handler)() = ih_p->ih_handler; 1610Sstevel@tonic-gate caddr_t arg1 = ih_p->ih_handler_arg1; 1620Sstevel@tonic-gate caddr_t arg2 = ih_p->ih_handler_arg2; 1630Sstevel@tonic-gate 1640Sstevel@tonic-gate if (ih_p->ih_intr_state == PX_INTR_STATE_DISABLE) { 1650Sstevel@tonic-gate DBG(DBG_INTX_INTR, px_p->px_dip, 1660Sstevel@tonic-gate "px_intx_intr: %s%d interrupt %d is disabled\n", 1670Sstevel@tonic-gate ddi_driver_name(dip), ddi_get_instance(dip), 1680Sstevel@tonic-gate ino_p->ino_ino); 1690Sstevel@tonic-gate 1700Sstevel@tonic-gate continue; 1710Sstevel@tonic-gate } 1720Sstevel@tonic-gate 1730Sstevel@tonic-gate DBG(DBG_INTX_INTR, px_p->px_dip, "px_intx_intr:" 1740Sstevel@tonic-gate "ino=%x handler=%p arg1 =%p arg2 = %p\n", 1750Sstevel@tonic-gate ino_p->ino_ino, handler, arg1, arg2); 1760Sstevel@tonic-gate 1770Sstevel@tonic-gate DTRACE_PROBE4(interrupt__start, dev_info_t, dip, 1780Sstevel@tonic-gate void *, handler, caddr_t, arg1, caddr_t, arg2); 1790Sstevel@tonic-gate 1800Sstevel@tonic-gate r = (*handler)(arg1, arg2); 1810Sstevel@tonic-gate 1820Sstevel@tonic-gate /* 1830Sstevel@tonic-gate * Account for time used by this interrupt. Protect against 1840Sstevel@tonic-gate * conflicting writes to ih_ticks from ib_intr_dist_all() by 1850Sstevel@tonic-gate * using atomic ops. 1860Sstevel@tonic-gate */ 1870Sstevel@tonic-gate 1882973Sgovinda if (pil <= LOCK_LEVEL) 1890Sstevel@tonic-gate atomic_add_64(&ih_p->ih_ticks, intr_get_time()); 1900Sstevel@tonic-gate 1910Sstevel@tonic-gate DTRACE_PROBE4(interrupt__complete, dev_info_t, dip, 1920Sstevel@tonic-gate void *, handler, caddr_t, arg1, int, r); 1930Sstevel@tonic-gate 1940Sstevel@tonic-gate result += r; 1950Sstevel@tonic-gate 1960Sstevel@tonic-gate if (px_check_all_handlers) 1970Sstevel@tonic-gate continue; 1980Sstevel@tonic-gate if (result) 1990Sstevel@tonic-gate break; 2000Sstevel@tonic-gate } 2010Sstevel@tonic-gate 2022973Sgovinda if (result) 2032973Sgovinda ino_p->ino_claimed |= (1 << pil); 2042973Sgovinda 2052973Sgovinda /* Interrupt can only be cleared after all pil levels are handled */ 2062973Sgovinda if (pil != ino_p->ino_lopil) 2072973Sgovinda return (DDI_INTR_CLAIMED); 2080Sstevel@tonic-gate 2092973Sgovinda if (!ino_p->ino_claimed) { 2102973Sgovinda if (px_unclaimed_intr_block) 2112973Sgovinda return (px_spurintr(ipil_p)); 2122973Sgovinda } 2132973Sgovinda 2142973Sgovinda ino_p->ino_unclaimed_intrs = 0; 2152973Sgovinda ino_p->ino_claimed = 0; 2160Sstevel@tonic-gate 2170Sstevel@tonic-gate /* Clear the pending state */ 2182973Sgovinda if (px_lib_intr_setstate(px_p->px_dip, 2190Sstevel@tonic-gate ino_p->ino_sysino, INTR_IDLE_STATE) != DDI_SUCCESS) 2200Sstevel@tonic-gate return (DDI_INTR_UNCLAIMED); 2210Sstevel@tonic-gate 2220Sstevel@tonic-gate return (DDI_INTR_CLAIMED); 2230Sstevel@tonic-gate } 2240Sstevel@tonic-gate 2250Sstevel@tonic-gate /* 226693Sgovinda * px_msiq_intr (MSI/X or PCIe MSG interrupt handler) 2270Sstevel@tonic-gate * 2280Sstevel@tonic-gate * This routine is used as wrapper around interrupt handlers installed by child 2290Sstevel@tonic-gate * device drivers. This routine invokes the driver interrupt handlers and 2300Sstevel@tonic-gate * examines the return codes. 2310Sstevel@tonic-gate * 2320Sstevel@tonic-gate * There is a count of unclaimed interrupts kept on a per-ino basis. If at 2330Sstevel@tonic-gate * least one handler claims the interrupt then the counter is halved and the 2340Sstevel@tonic-gate * interrupt state machine is idled. If no handler claims the interrupt then 2350Sstevel@tonic-gate * the counter is incremented by one and the state machine is idled. 2360Sstevel@tonic-gate * If the count ever reaches the limit value set by pci_unclaimed_intr_max 2370Sstevel@tonic-gate * then the interrupt state machine is not idled thus preventing any further 2380Sstevel@tonic-gate * interrupts on that ino. The state machine will only be idled again if a 2390Sstevel@tonic-gate * handler is subsequently added or removed. 2400Sstevel@tonic-gate * 2410Sstevel@tonic-gate * return value: DDI_INTR_CLAIMED if any handlers claimed the interrupt, 2420Sstevel@tonic-gate * DDI_INTR_UNCLAIMED otherwise. 2430Sstevel@tonic-gate */ 2440Sstevel@tonic-gate uint_t 2450Sstevel@tonic-gate px_msiq_intr(caddr_t arg) 2460Sstevel@tonic-gate { 2472973Sgovinda px_ino_pil_t *ipil_p = (px_ino_pil_t *)arg; 2482973Sgovinda px_ino_t *ino_p = ipil_p->ipil_ino_p; 2490Sstevel@tonic-gate px_t *px_p = ino_p->ino_ib_p->ib_px_p; 2500Sstevel@tonic-gate px_msiq_state_t *msiq_state_p = &px_p->px_ib_p->ib_msiq_state; 2510Sstevel@tonic-gate px_msiq_t *msiq_p = ino_p->ino_msiq_p; 2520Sstevel@tonic-gate dev_info_t *dip = px_p->px_dip; 2532973Sgovinda ushort_t pil = ipil_p->ipil_pil; 2540Sstevel@tonic-gate msiq_rec_t msiq_rec, *msiq_rec_p = &msiq_rec; 2552588Segillett msiqhead_t *curr_head_p; 2562588Segillett msiqtail_t curr_tail_index; 2570Sstevel@tonic-gate msgcode_t msg_code; 2580Sstevel@tonic-gate px_ih_t *ih_p; 2592973Sgovinda uint_t ret = DDI_INTR_UNCLAIMED; 2602973Sgovinda int i, j; 2610Sstevel@tonic-gate 2620Sstevel@tonic-gate DBG(DBG_MSIQ_INTR, dip, "px_msiq_intr: msiq_id =%x ino=%x pil=%x " 2630Sstevel@tonic-gate "ih_size=%x ih_lst=%x\n", msiq_p->msiq_id, ino_p->ino_ino, 2642973Sgovinda ipil_p->ipil_pil, ipil_p->ipil_ih_size, ipil_p->ipil_ih_head); 2652973Sgovinda 2662973Sgovinda /* 2672973Sgovinda * The px_msiq_intr() handles multiple interrupt priorities and it 2682973Sgovinda * will set msiq->msiq_rec2process to the number of MSIQ records to 2692973Sgovinda * process while handling the highest priority interrupt. Subsequent 2702973Sgovinda * lower priority interrupts will just process any unprocessed MSIQ 2712973Sgovinda * records or will just return immediately. 2722973Sgovinda */ 2732973Sgovinda if (msiq_p->msiq_recs2process == 0) { 2742973Sgovinda /* Read current MSIQ tail index */ 2752973Sgovinda px_lib_msiq_gettail(dip, msiq_p->msiq_id, &curr_tail_index); 2762973Sgovinda msiq_p->msiq_new_head_index = msiq_p->msiq_curr_head_index; 2770Sstevel@tonic-gate 2782973Sgovinda if (curr_tail_index < msiq_p->msiq_curr_head_index) 2792973Sgovinda curr_tail_index += msiq_state_p->msiq_rec_cnt; 2802973Sgovinda 2812973Sgovinda msiq_p->msiq_recs2process = curr_tail_index - 2822973Sgovinda msiq_p->msiq_curr_head_index; 2832973Sgovinda } 2840Sstevel@tonic-gate 2852973Sgovinda DBG(DBG_MSIQ_INTR, dip, "px_msiq_intr: curr_head %x new_head %x " 2862973Sgovinda "rec2process %x\n", msiq_p->msiq_curr_head_index, 2872973Sgovinda msiq_p->msiq_new_head_index, msiq_p->msiq_recs2process); 2882973Sgovinda 2892973Sgovinda /* If all MSIQ records are already processed, just return immediately */ 2902973Sgovinda if ((msiq_p->msiq_new_head_index - msiq_p->msiq_curr_head_index) 2912973Sgovinda == msiq_p->msiq_recs2process) 2922973Sgovinda goto intr_done; 2932973Sgovinda 2942973Sgovinda curr_head_p = (msiqhead_t *)((caddr_t)msiq_p->msiq_base_p + 2952973Sgovinda (msiq_p->msiq_curr_head_index * sizeof (msiq_rec_t))); 2960Sstevel@tonic-gate 2970Sstevel@tonic-gate /* 2982588Segillett * Calculate the number of recs to process by taking the difference 2992588Segillett * between the head and tail pointers. For all records we always 3002588Segillett * verify that we have a valid record type before we do any processing. 3012973Sgovinda * If triggered, we should always have at least one valid record. 3020Sstevel@tonic-gate */ 3032973Sgovinda for (i = 0; i < msiq_p->msiq_recs2process; i++) { 3042973Sgovinda /* Read next MSIQ record */ 3052588Segillett px_lib_get_msiq_rec(dip, curr_head_p, msiq_rec_p); 3062588Segillett 3070Sstevel@tonic-gate DBG(DBG_MSIQ_INTR, dip, "px_msiq_intr: MSIQ RECORD, " 3080Sstevel@tonic-gate "msiq_rec_type 0x%llx msiq_rec_rid 0x%llx\n", 3090Sstevel@tonic-gate msiq_rec_p->msiq_rec_type, msiq_rec_p->msiq_rec_rid); 3100Sstevel@tonic-gate 3112588Segillett if (!msiq_rec_p->msiq_rec_type) 3122973Sgovinda goto next_rec; 3130Sstevel@tonic-gate 3140Sstevel@tonic-gate /* Check MSIQ record type */ 3150Sstevel@tonic-gate switch (msiq_rec_p->msiq_rec_type) { 3160Sstevel@tonic-gate case MSG_REC: 3170Sstevel@tonic-gate msg_code = msiq_rec_p->msiq_rec_data.msg.msg_code; 3180Sstevel@tonic-gate DBG(DBG_MSIQ_INTR, dip, "px_msiq_intr: PCIE MSG " 3190Sstevel@tonic-gate "record, msg type 0x%x\n", msg_code); 3200Sstevel@tonic-gate break; 3210Sstevel@tonic-gate case MSI32_REC: 3220Sstevel@tonic-gate case MSI64_REC: 3230Sstevel@tonic-gate msg_code = msiq_rec_p->msiq_rec_data.msi.msi_data; 3240Sstevel@tonic-gate DBG(DBG_MSIQ_INTR, dip, "px_msiq_intr: MSI record, " 3250Sstevel@tonic-gate "msi 0x%x\n", msg_code); 3260Sstevel@tonic-gate 3270Sstevel@tonic-gate /* Clear MSI state */ 3280Sstevel@tonic-gate px_lib_msi_setstate(dip, (msinum_t)msg_code, 3290Sstevel@tonic-gate PCI_MSI_STATE_IDLE); 3300Sstevel@tonic-gate break; 3310Sstevel@tonic-gate default: 3320Sstevel@tonic-gate msg_code = 0; 3330Sstevel@tonic-gate cmn_err(CE_WARN, "%s%d: px_msiq_intr: 0x%x MSIQ " 3340Sstevel@tonic-gate "record type is not supported", 3350Sstevel@tonic-gate ddi_driver_name(dip), ddi_get_instance(dip), 3360Sstevel@tonic-gate msiq_rec_p->msiq_rec_type); 3372973Sgovinda 3380Sstevel@tonic-gate goto next_rec; 3390Sstevel@tonic-gate } 3400Sstevel@tonic-gate 3410Sstevel@tonic-gate /* 3420Sstevel@tonic-gate * Scan through px_ih_t linked list, searching for the 3430Sstevel@tonic-gate * right px_ih_t, matching MSIQ record data. 3440Sstevel@tonic-gate */ 3452973Sgovinda for (j = 0, ih_p = ipil_p->ipil_ih_start; 3462973Sgovinda ih_p && (j < ipil_p->ipil_ih_size) && 3471653Sgovinda ((ih_p->ih_msg_code != msg_code) || 3481653Sgovinda (ih_p->ih_rec_type != msiq_rec_p->msiq_rec_type)); 349*4397Sschwartz ih_p = ih_p->ih_next, j++) 350*4397Sschwartz ; 3510Sstevel@tonic-gate 3520Sstevel@tonic-gate if ((ih_p->ih_msg_code == msg_code) && 3530Sstevel@tonic-gate (ih_p->ih_rec_type == msiq_rec_p->msiq_rec_type)) { 3540Sstevel@tonic-gate dev_info_t *dip = ih_p->ih_dip; 3550Sstevel@tonic-gate uint_t (*handler)() = ih_p->ih_handler; 3560Sstevel@tonic-gate caddr_t arg1 = ih_p->ih_handler_arg1; 3570Sstevel@tonic-gate caddr_t arg2 = ih_p->ih_handler_arg2; 3580Sstevel@tonic-gate 3590Sstevel@tonic-gate DBG(DBG_MSIQ_INTR, dip, "px_msiq_intr: ino=%x data=%x " 3600Sstevel@tonic-gate "handler=%p arg1 =%p arg2=%p\n", ino_p->ino_ino, 3610Sstevel@tonic-gate msg_code, handler, arg1, arg2); 3620Sstevel@tonic-gate 3630Sstevel@tonic-gate DTRACE_PROBE4(interrupt__start, dev_info_t, dip, 3640Sstevel@tonic-gate void *, handler, caddr_t, arg1, caddr_t, arg2); 3650Sstevel@tonic-gate 36627Sjchu /* 36727Sjchu * Special case for PCIE Error Messages. 36827Sjchu * The current frame work doesn't fit PCIE Err Msgs 36927Sjchu * This should be fixed when PCIE MESSAGES as a whole 37027Sjchu * is architected correctly. 37127Sjchu */ 37227Sjchu if ((msg_code == PCIE_MSG_CODE_ERR_COR) || 37327Sjchu (msg_code == PCIE_MSG_CODE_ERR_NONFATAL) || 37427Sjchu (msg_code == PCIE_MSG_CODE_ERR_FATAL)) { 37527Sjchu ret = px_err_fabric_intr(px_p, msg_code, 37627Sjchu msiq_rec_p->msiq_rec_rid); 37727Sjchu } else 37827Sjchu ret = (*handler)(arg1, arg2); 3790Sstevel@tonic-gate 3800Sstevel@tonic-gate /* 3810Sstevel@tonic-gate * Account for time used by this interrupt. Protect 3820Sstevel@tonic-gate * against conflicting writes to ih_ticks from 3830Sstevel@tonic-gate * ib_intr_dist_all() by using atomic ops. 3840Sstevel@tonic-gate */ 3850Sstevel@tonic-gate 3862973Sgovinda if (pil <= LOCK_LEVEL) 3870Sstevel@tonic-gate atomic_add_64(&ih_p->ih_ticks, intr_get_time()); 3880Sstevel@tonic-gate 3890Sstevel@tonic-gate DTRACE_PROBE4(interrupt__complete, dev_info_t, dip, 3900Sstevel@tonic-gate void *, handler, caddr_t, arg1, int, ret); 3912588Segillett 3922973Sgovinda msiq_p->msiq_new_head_index++; 3932973Sgovinda px_lib_clr_msiq_rec(dip, curr_head_p); 3940Sstevel@tonic-gate } else { 3950Sstevel@tonic-gate DBG(DBG_MSIQ_INTR, dip, "px_msiq_intr:" 3962588Segillett "No matching MSIQ record found\n"); 3972588Segillett } 3982588Segillett next_rec: 3992588Segillett /* Get the pointer next EQ record */ 4002588Segillett curr_head_p = (msiqhead_t *) 4012588Segillett ((caddr_t)curr_head_p + sizeof (msiq_rec_t)); 4020Sstevel@tonic-gate 4032588Segillett /* Check for overflow condition */ 4042588Segillett if (curr_head_p >= (msiqhead_t *)((caddr_t)msiq_p->msiq_base_p 4052973Sgovinda + (msiq_state_p->msiq_rec_cnt * sizeof (msiq_rec_t)))) 4062588Segillett curr_head_p = (msiqhead_t *)msiq_p->msiq_base_p; 4070Sstevel@tonic-gate } 4080Sstevel@tonic-gate 4092973Sgovinda DBG(DBG_MSIQ_INTR, dip, "px_msiq_intr: No of MSIQ recs processed %x\n", 4102973Sgovinda (msiq_p->msiq_new_head_index - msiq_p->msiq_curr_head_index)); 4112973Sgovinda 4122973Sgovinda DBG(DBG_MSIQ_INTR, dip, "px_msiq_intr: curr_head %x new_head %x " 4132973Sgovinda "rec2process %x\n", msiq_p->msiq_curr_head_index, 4142973Sgovinda msiq_p->msiq_new_head_index, msiq_p->msiq_recs2process); 4152588Segillett 4162973Sgovinda /* ino_claimed used just for debugging purpose */ 4172973Sgovinda if (ret) 4182973Sgovinda ino_p->ino_claimed |= (1 << pil); 4192973Sgovinda 4202973Sgovinda intr_done: 4212973Sgovinda /* Interrupt can only be cleared after all pil levels are handled */ 4222973Sgovinda if (pil != ino_p->ino_lopil) 4232973Sgovinda return (DDI_INTR_CLAIMED); 4242973Sgovinda 4252973Sgovinda if (msiq_p->msiq_new_head_index <= msiq_p->msiq_curr_head_index) { 4262973Sgovinda if (px_unclaimed_intr_block) 4272973Sgovinda return (px_spurintr(ipil_p)); 4282588Segillett } 4290Sstevel@tonic-gate 4300Sstevel@tonic-gate /* Update MSIQ head index with no of MSIQ records processed */ 4312973Sgovinda if (msiq_p->msiq_new_head_index >= msiq_state_p->msiq_rec_cnt) 4322973Sgovinda msiq_p->msiq_new_head_index -= msiq_state_p->msiq_rec_cnt; 4330Sstevel@tonic-gate 4342973Sgovinda msiq_p->msiq_curr_head_index = msiq_p->msiq_new_head_index; 4352973Sgovinda px_lib_msiq_sethead(dip, msiq_p->msiq_id, msiq_p->msiq_new_head_index); 4362973Sgovinda 4372973Sgovinda msiq_p->msiq_new_head_index = 0; 4382973Sgovinda msiq_p->msiq_recs2process = 0; 4392973Sgovinda ino_p->ino_claimed = 0; 4400Sstevel@tonic-gate 4410Sstevel@tonic-gate /* Clear the pending state */ 4420Sstevel@tonic-gate if (px_lib_intr_setstate(dip, ino_p->ino_sysino, 4430Sstevel@tonic-gate INTR_IDLE_STATE) != DDI_SUCCESS) 4440Sstevel@tonic-gate return (DDI_INTR_UNCLAIMED); 4450Sstevel@tonic-gate 4460Sstevel@tonic-gate return (DDI_INTR_CLAIMED); 4470Sstevel@tonic-gate } 4480Sstevel@tonic-gate 4490Sstevel@tonic-gate dev_info_t * 4500Sstevel@tonic-gate px_get_my_childs_dip(dev_info_t *dip, dev_info_t *rdip) 4510Sstevel@tonic-gate { 4520Sstevel@tonic-gate dev_info_t *cdip = rdip; 4530Sstevel@tonic-gate 4540Sstevel@tonic-gate for (; ddi_get_parent(cdip) != dip; cdip = ddi_get_parent(cdip)) 4550Sstevel@tonic-gate ; 4560Sstevel@tonic-gate 4570Sstevel@tonic-gate return (cdip); 4580Sstevel@tonic-gate } 4590Sstevel@tonic-gate 4600Sstevel@tonic-gate /* Default class to pil value mapping */ 4610Sstevel@tonic-gate px_class_val_t px_default_pil [] = { 4620Sstevel@tonic-gate {0x000000, 0xff0000, 0x1}, /* Class code for pre-2.0 devices */ 4630Sstevel@tonic-gate {0x010000, 0xff0000, 0x4}, /* Mass Storage Controller */ 4640Sstevel@tonic-gate {0x020000, 0xff0000, 0x6}, /* Network Controller */ 4650Sstevel@tonic-gate {0x030000, 0xff0000, 0x9}, /* Display Controller */ 4660Sstevel@tonic-gate {0x040000, 0xff0000, 0x9}, /* Multimedia Controller */ 4671617Sgovinda {0x050000, 0xff0000, 0x9}, /* Memory Controller */ 4681617Sgovinda {0x060000, 0xff0000, 0x9}, /* Bridge Controller */ 4690Sstevel@tonic-gate {0x0c0000, 0xffff00, 0x9}, /* Serial Bus, FireWire (IEEE 1394) */ 4700Sstevel@tonic-gate {0x0c0100, 0xffff00, 0x4}, /* Serial Bus, ACCESS.bus */ 4710Sstevel@tonic-gate {0x0c0200, 0xffff00, 0x4}, /* Serial Bus, SSA */ 4720Sstevel@tonic-gate {0x0c0300, 0xffff00, 0x9}, /* Serial Bus Universal Serial Bus */ 4730Sstevel@tonic-gate {0x0c0400, 0xffff00, 0x6}, /* Serial Bus, Fibre Channel */ 4740Sstevel@tonic-gate {0x0c0600, 0xffff00, 0x6} /* Serial Bus, Infiniband */ 4750Sstevel@tonic-gate }; 4760Sstevel@tonic-gate 4770Sstevel@tonic-gate /* 4780Sstevel@tonic-gate * Default class to intr_weight value mapping (% of CPU). A driver.conf 4790Sstevel@tonic-gate * entry on or above the pci node like 4800Sstevel@tonic-gate * 4810Sstevel@tonic-gate * pci-class-intr-weights= 0x020000, 0xff0000, 30; 4820Sstevel@tonic-gate * 4830Sstevel@tonic-gate * can be used to augment or override entries in the default table below. 4840Sstevel@tonic-gate * 4850Sstevel@tonic-gate * NB: The values below give NICs preference on redistribution, and provide 4860Sstevel@tonic-gate * NICs some isolation from other interrupt sources. We need better interfaces 4870Sstevel@tonic-gate * that allow the NIC driver to identify a specific NIC instance as high 4880Sstevel@tonic-gate * bandwidth, and thus deserving of separation from other low bandwidth 4890Sstevel@tonic-gate * NICs additional isolation from other interrupt sources. 4900Sstevel@tonic-gate * 4910Sstevel@tonic-gate * NB: We treat Infiniband like a NIC. 4920Sstevel@tonic-gate */ 4930Sstevel@tonic-gate px_class_val_t px_default_intr_weight [] = { 4940Sstevel@tonic-gate {0x020000, 0xff0000, 35}, /* Network Controller */ 4950Sstevel@tonic-gate {0x010000, 0xff0000, 10}, /* Mass Storage Controller */ 4960Sstevel@tonic-gate {0x0c0400, 0xffff00, 10}, /* Serial Bus, Fibre Channel */ 4970Sstevel@tonic-gate {0x0c0600, 0xffff00, 50} /* Serial Bus, Infiniband */ 4980Sstevel@tonic-gate }; 4990Sstevel@tonic-gate 5000Sstevel@tonic-gate static uint32_t 5010Sstevel@tonic-gate px_match_class_val(uint32_t key, px_class_val_t *rec_p, int nrec, 5020Sstevel@tonic-gate uint32_t default_val) 5030Sstevel@tonic-gate { 5040Sstevel@tonic-gate int i; 5050Sstevel@tonic-gate 5060Sstevel@tonic-gate for (i = 0; i < nrec; rec_p++, i++) { 5070Sstevel@tonic-gate if ((rec_p->class_code & rec_p->class_mask) == 5080Sstevel@tonic-gate (key & rec_p->class_mask)) 5090Sstevel@tonic-gate return (rec_p->class_val); 5100Sstevel@tonic-gate } 5110Sstevel@tonic-gate 5120Sstevel@tonic-gate return (default_val); 5130Sstevel@tonic-gate } 5140Sstevel@tonic-gate 5150Sstevel@tonic-gate /* 5160Sstevel@tonic-gate * px_class_to_val 5170Sstevel@tonic-gate * 5180Sstevel@tonic-gate * Return the configuration value, based on class code and sub class code, 5190Sstevel@tonic-gate * from the specified property based or default px_class_val_t table. 5200Sstevel@tonic-gate */ 5210Sstevel@tonic-gate uint32_t 5220Sstevel@tonic-gate px_class_to_val(dev_info_t *rdip, char *property_name, px_class_val_t *rec_p, 5230Sstevel@tonic-gate int nrec, uint32_t default_val) 5240Sstevel@tonic-gate { 5250Sstevel@tonic-gate int property_len; 5260Sstevel@tonic-gate uint32_t class_code; 5270Sstevel@tonic-gate px_class_val_t *conf; 5280Sstevel@tonic-gate uint32_t val = default_val; 5290Sstevel@tonic-gate 5300Sstevel@tonic-gate /* 5310Sstevel@tonic-gate * Use the "class-code" property to get the base and sub class 5320Sstevel@tonic-gate * codes for the requesting device. 5330Sstevel@tonic-gate */ 5340Sstevel@tonic-gate class_code = (uint32_t)ddi_prop_get_int(DDI_DEV_T_ANY, rdip, 5350Sstevel@tonic-gate DDI_PROP_DONTPASS, "class-code", -1); 5360Sstevel@tonic-gate 5370Sstevel@tonic-gate if (class_code == -1) 5380Sstevel@tonic-gate return (val); 5390Sstevel@tonic-gate 5400Sstevel@tonic-gate /* look up the val from the default table */ 5410Sstevel@tonic-gate val = px_match_class_val(class_code, rec_p, nrec, val); 5420Sstevel@tonic-gate 5430Sstevel@tonic-gate /* see if there is a more specific property specified value */ 5440Sstevel@tonic-gate if (ddi_getlongprop(DDI_DEV_T_ANY, rdip, DDI_PROP_NOTPROM, 5450Sstevel@tonic-gate property_name, (caddr_t)&conf, &property_len)) 5460Sstevel@tonic-gate return (val); 5470Sstevel@tonic-gate 5480Sstevel@tonic-gate if ((property_len % sizeof (px_class_val_t)) == 0) 5490Sstevel@tonic-gate val = px_match_class_val(class_code, conf, 5500Sstevel@tonic-gate property_len / sizeof (px_class_val_t), val); 5510Sstevel@tonic-gate kmem_free(conf, property_len); 5520Sstevel@tonic-gate return (val); 5530Sstevel@tonic-gate } 5540Sstevel@tonic-gate 5550Sstevel@tonic-gate /* px_class_to_pil: return the pil for a given device. */ 5560Sstevel@tonic-gate uint32_t 5570Sstevel@tonic-gate px_class_to_pil(dev_info_t *rdip) 5580Sstevel@tonic-gate { 5590Sstevel@tonic-gate uint32_t pil; 5600Sstevel@tonic-gate 5613249Sgovinda /* Default pil is 1 */ 5620Sstevel@tonic-gate pil = px_class_to_val(rdip, 5630Sstevel@tonic-gate "pci-class-priorities", px_default_pil, 5643249Sgovinda sizeof (px_default_pil) / sizeof (px_class_val_t), 1); 5650Sstevel@tonic-gate 5663249Sgovinda /* Range check the result */ 5670Sstevel@tonic-gate if (pil >= 0xf) 5683249Sgovinda pil = 1; 5690Sstevel@tonic-gate 5700Sstevel@tonic-gate return (pil); 5710Sstevel@tonic-gate } 5720Sstevel@tonic-gate 5730Sstevel@tonic-gate /* px_class_to_intr_weight: return the intr_weight for a given device. */ 5740Sstevel@tonic-gate static int32_t 5750Sstevel@tonic-gate px_class_to_intr_weight(dev_info_t *rdip) 5760Sstevel@tonic-gate { 5770Sstevel@tonic-gate int32_t intr_weight; 5780Sstevel@tonic-gate 5790Sstevel@tonic-gate /* default weight is 0% */ 5800Sstevel@tonic-gate intr_weight = px_class_to_val(rdip, 5810Sstevel@tonic-gate "pci-class-intr-weights", px_default_intr_weight, 5820Sstevel@tonic-gate sizeof (px_default_intr_weight) / sizeof (px_class_val_t), 0); 5830Sstevel@tonic-gate 5840Sstevel@tonic-gate /* range check the result */ 5850Sstevel@tonic-gate if (intr_weight < 0) 5860Sstevel@tonic-gate intr_weight = 0; 5870Sstevel@tonic-gate if (intr_weight > 1000) 5880Sstevel@tonic-gate intr_weight = 1000; 5890Sstevel@tonic-gate 5900Sstevel@tonic-gate return (intr_weight); 5910Sstevel@tonic-gate } 5920Sstevel@tonic-gate 5930Sstevel@tonic-gate /* ARGSUSED */ 5940Sstevel@tonic-gate int 5950Sstevel@tonic-gate px_intx_ops(dev_info_t *dip, dev_info_t *rdip, ddi_intr_op_t intr_op, 5960Sstevel@tonic-gate ddi_intr_handle_impl_t *hdlp, void *result) 5970Sstevel@tonic-gate { 598693Sgovinda px_t *px_p = DIP_TO_STATE(dip); 599693Sgovinda int ret = DDI_SUCCESS; 6000Sstevel@tonic-gate 6010Sstevel@tonic-gate DBG(DBG_INTROPS, dip, "px_intx_ops: dip=%x rdip=%x intr_op=%x " 6020Sstevel@tonic-gate "handle=%p\n", dip, rdip, intr_op, hdlp); 6030Sstevel@tonic-gate 6040Sstevel@tonic-gate switch (intr_op) { 6050Sstevel@tonic-gate case DDI_INTROP_GETCAP: 6060Sstevel@tonic-gate ret = pci_intx_get_cap(rdip, (int *)result); 6070Sstevel@tonic-gate break; 6080Sstevel@tonic-gate case DDI_INTROP_SETCAP: 6090Sstevel@tonic-gate DBG(DBG_INTROPS, dip, "px_intx_ops: SetCap is not supported\n"); 6100Sstevel@tonic-gate ret = DDI_ENOTSUP; 6110Sstevel@tonic-gate break; 6120Sstevel@tonic-gate case DDI_INTROP_ALLOC: 6130Sstevel@tonic-gate *(int *)result = hdlp->ih_scratch1; 6140Sstevel@tonic-gate break; 6150Sstevel@tonic-gate case DDI_INTROP_FREE: 6160Sstevel@tonic-gate break; 6170Sstevel@tonic-gate case DDI_INTROP_GETPRI: 618693Sgovinda *(int *)result = hdlp->ih_pri ? 619693Sgovinda hdlp->ih_pri : px_class_to_pil(rdip); 6200Sstevel@tonic-gate break; 6210Sstevel@tonic-gate case DDI_INTROP_SETPRI: 6220Sstevel@tonic-gate break; 6230Sstevel@tonic-gate case DDI_INTROP_ADDISR: 6240Sstevel@tonic-gate ret = px_add_intx_intr(dip, rdip, hdlp); 6250Sstevel@tonic-gate break; 6260Sstevel@tonic-gate case DDI_INTROP_REMISR: 6270Sstevel@tonic-gate ret = px_rem_intx_intr(dip, rdip, hdlp); 6280Sstevel@tonic-gate break; 6290Sstevel@tonic-gate case DDI_INTROP_ENABLE: 6300Sstevel@tonic-gate ret = px_ib_update_intr_state(px_p, rdip, hdlp->ih_inum, 6312973Sgovinda hdlp->ih_vector, hdlp->ih_pri, PX_INTR_STATE_ENABLE, 0, 0); 6320Sstevel@tonic-gate break; 6330Sstevel@tonic-gate case DDI_INTROP_DISABLE: 6340Sstevel@tonic-gate ret = px_ib_update_intr_state(px_p, rdip, hdlp->ih_inum, 6352973Sgovinda hdlp->ih_vector, hdlp->ih_pri, PX_INTR_STATE_DISABLE, 0, 0); 6360Sstevel@tonic-gate break; 6370Sstevel@tonic-gate case DDI_INTROP_SETMASK: 6380Sstevel@tonic-gate ret = pci_intx_set_mask(rdip); 6390Sstevel@tonic-gate break; 6400Sstevel@tonic-gate case DDI_INTROP_CLRMASK: 6410Sstevel@tonic-gate ret = pci_intx_clr_mask(rdip); 6420Sstevel@tonic-gate break; 6430Sstevel@tonic-gate case DDI_INTROP_GETPENDING: 6440Sstevel@tonic-gate ret = pci_intx_get_pending(rdip, (int *)result); 6450Sstevel@tonic-gate break; 6460Sstevel@tonic-gate case DDI_INTROP_NINTRS: 6470Sstevel@tonic-gate case DDI_INTROP_NAVAIL: 6482580Sanish *(int *)result = i_ddi_get_intx_nintrs(rdip); 6490Sstevel@tonic-gate break; 6500Sstevel@tonic-gate default: 6510Sstevel@tonic-gate ret = DDI_ENOTSUP; 6520Sstevel@tonic-gate break; 6530Sstevel@tonic-gate } 6540Sstevel@tonic-gate 6550Sstevel@tonic-gate return (ret); 6560Sstevel@tonic-gate } 6570Sstevel@tonic-gate 6580Sstevel@tonic-gate /* ARGSUSED */ 6590Sstevel@tonic-gate int 6600Sstevel@tonic-gate px_msix_ops(dev_info_t *dip, dev_info_t *rdip, ddi_intr_op_t intr_op, 6610Sstevel@tonic-gate ddi_intr_handle_impl_t *hdlp, void *result) 6620Sstevel@tonic-gate { 6630Sstevel@tonic-gate px_t *px_p = DIP_TO_STATE(dip); 6640Sstevel@tonic-gate px_msi_state_t *msi_state_p = &px_p->px_ib_p->ib_msi_state; 665965Sgovinda msiq_rec_type_t msiq_rec_type; 666965Sgovinda msi_type_t msi_type; 667965Sgovinda uint64_t msi_addr; 6680Sstevel@tonic-gate msinum_t msi_num; 6690Sstevel@tonic-gate msiqid_t msiq_id; 6700Sstevel@tonic-gate uint_t nintrs; 6710Sstevel@tonic-gate int i, ret = DDI_SUCCESS; 6720Sstevel@tonic-gate 6730Sstevel@tonic-gate DBG(DBG_INTROPS, dip, "px_msix_ops: dip=%x rdip=%x intr_op=%x " 6740Sstevel@tonic-gate "handle=%p\n", dip, rdip, intr_op, hdlp); 6750Sstevel@tonic-gate 676965Sgovinda /* Check for MSI64 support */ 6771653Sgovinda if ((hdlp->ih_cap & DDI_INTR_FLAG_MSI64) && msi_state_p->msi_addr64) { 678965Sgovinda msiq_rec_type = MSI64_REC; 679965Sgovinda msi_type = MSI64_TYPE; 6801653Sgovinda msi_addr = msi_state_p->msi_addr64; 681965Sgovinda } else { 682965Sgovinda msiq_rec_type = MSI32_REC; 683965Sgovinda msi_type = MSI32_TYPE; 684965Sgovinda msi_addr = msi_state_p->msi_addr32; 685965Sgovinda } 686965Sgovinda 6870Sstevel@tonic-gate switch (intr_op) { 6880Sstevel@tonic-gate case DDI_INTROP_GETCAP: 6890Sstevel@tonic-gate ret = pci_msi_get_cap(rdip, hdlp->ih_type, (int *)result); 6900Sstevel@tonic-gate break; 6910Sstevel@tonic-gate case DDI_INTROP_SETCAP: 6920Sstevel@tonic-gate DBG(DBG_INTROPS, dip, "px_msix_ops: SetCap is not supported\n"); 6930Sstevel@tonic-gate ret = DDI_ENOTSUP; 6940Sstevel@tonic-gate break; 6950Sstevel@tonic-gate case DDI_INTROP_ALLOC: 6960Sstevel@tonic-gate /* 6970Sstevel@tonic-gate * We need to restrict this allocation in future 6980Sstevel@tonic-gate * based on Resource Management policies. 6990Sstevel@tonic-gate */ 7000Sstevel@tonic-gate if ((ret = px_msi_alloc(px_p, rdip, hdlp->ih_inum, 7011725Segillett hdlp->ih_scratch1, (uintptr_t)hdlp->ih_scratch2, &msi_num, 7021725Segillett (int *)result)) != DDI_SUCCESS) { 7031725Segillett DBG(DBG_INTROPS, dip, "px_msix_ops: allocation " 7041725Segillett "failed, rdip 0x%p type 0x%d inum 0x%x " 7051725Segillett "count 0x%x\n", rdip, hdlp->ih_type, hdlp->ih_inum, 7061725Segillett hdlp->ih_scratch1); 7070Sstevel@tonic-gate 7080Sstevel@tonic-gate return (ret); 7090Sstevel@tonic-gate } 7100Sstevel@tonic-gate 7111725Segillett if ((hdlp->ih_type == DDI_INTR_TYPE_MSIX) && 7121725Segillett (i_ddi_get_msix(rdip) == NULL)) { 7131725Segillett ddi_intr_msix_t *msix_p; 7141725Segillett 7151725Segillett if (msix_p = pci_msix_init(rdip)) { 7161725Segillett i_ddi_set_msix(rdip, msix_p); 7171725Segillett break; 7181725Segillett } 7191725Segillett 7201725Segillett DBG(DBG_INTROPS, dip, "px_msix_ops: MSI-X allocation " 7211725Segillett "failed, rdip 0x%p inum 0x%x\n", rdip, 7221725Segillett hdlp->ih_inum); 7231725Segillett 7241725Segillett (void) px_msi_free(px_p, rdip, hdlp->ih_inum, 7251725Segillett hdlp->ih_scratch1); 7261725Segillett 7271725Segillett return (DDI_FAILURE); 7281725Segillett } 7291725Segillett 7300Sstevel@tonic-gate break; 7310Sstevel@tonic-gate case DDI_INTROP_FREE: 7322755Segillett (void) pci_msi_disable_mode(rdip, hdlp->ih_type, NULL); 7330Sstevel@tonic-gate (void) pci_msi_unconfigure(rdip, hdlp->ih_type, hdlp->ih_inum); 7341725Segillett 7351725Segillett if (hdlp->ih_type == DDI_INTR_TYPE_MSI) 7361725Segillett goto msi_free; 7371725Segillett 7381725Segillett if (hdlp->ih_flags & DDI_INTR_MSIX_DUP) 7391725Segillett break; 7401725Segillett 7411725Segillett if (((i_ddi_intr_get_current_nintrs(hdlp->ih_dip) - 1) == 0) && 7421725Segillett (i_ddi_get_msix(rdip))) { 7431725Segillett pci_msix_fini(i_ddi_get_msix(rdip)); 7441725Segillett i_ddi_set_msix(rdip, NULL); 7451725Segillett } 7461725Segillett msi_free: 7470Sstevel@tonic-gate (void) px_msi_free(px_p, rdip, hdlp->ih_inum, 7480Sstevel@tonic-gate hdlp->ih_scratch1); 7490Sstevel@tonic-gate break; 7500Sstevel@tonic-gate case DDI_INTROP_GETPRI: 7510Sstevel@tonic-gate *(int *)result = hdlp->ih_pri ? 7520Sstevel@tonic-gate hdlp->ih_pri : px_class_to_pil(rdip); 7530Sstevel@tonic-gate break; 7540Sstevel@tonic-gate case DDI_INTROP_SETPRI: 7550Sstevel@tonic-gate break; 7560Sstevel@tonic-gate case DDI_INTROP_ADDISR: 7570Sstevel@tonic-gate if ((ret = px_msi_get_msinum(px_p, hdlp->ih_dip, 7580Sstevel@tonic-gate hdlp->ih_inum, &msi_num)) != DDI_SUCCESS) 7590Sstevel@tonic-gate return (ret); 7600Sstevel@tonic-gate 7610Sstevel@tonic-gate if ((ret = px_add_msiq_intr(dip, rdip, hdlp, 762965Sgovinda msiq_rec_type, msi_num, &msiq_id)) != DDI_SUCCESS) { 7630Sstevel@tonic-gate DBG(DBG_INTROPS, dip, "px_msix_ops: Add MSI handler " 7640Sstevel@tonic-gate "failed, rdip 0x%p msi 0x%x\n", rdip, msi_num); 7650Sstevel@tonic-gate return (ret); 7660Sstevel@tonic-gate } 7670Sstevel@tonic-gate 7680Sstevel@tonic-gate DBG(DBG_INTROPS, dip, "px_msix_ops: msiq used 0x%x\n", msiq_id); 7690Sstevel@tonic-gate 7700Sstevel@tonic-gate if ((ret = px_lib_msi_setmsiq(dip, msi_num, 771965Sgovinda msiq_id, msi_type)) != DDI_SUCCESS) { 7720Sstevel@tonic-gate (void) px_rem_msiq_intr(dip, rdip, 773965Sgovinda hdlp, msiq_rec_type, msi_num, msiq_id); 7740Sstevel@tonic-gate return (ret); 7750Sstevel@tonic-gate } 7760Sstevel@tonic-gate 7770Sstevel@tonic-gate if ((ret = px_lib_msi_setstate(dip, msi_num, 7780Sstevel@tonic-gate PCI_MSI_STATE_IDLE)) != DDI_SUCCESS) { 7790Sstevel@tonic-gate (void) px_rem_msiq_intr(dip, rdip, 780965Sgovinda hdlp, msiq_rec_type, msi_num, msiq_id); 7810Sstevel@tonic-gate return (ret); 7820Sstevel@tonic-gate } 7830Sstevel@tonic-gate 7840Sstevel@tonic-gate hdlp->ih_vector = msi_num; 7850Sstevel@tonic-gate break; 7860Sstevel@tonic-gate case DDI_INTROP_DUPVEC: 7871725Segillett DBG(DBG_INTROPS, dip, "px_msix_ops: dupisr - inum: %x, " 7881725Segillett "new_vector: %x\n", hdlp->ih_inum, hdlp->ih_scratch1); 7891725Segillett 7901725Segillett ret = pci_msix_dup(hdlp->ih_dip, hdlp->ih_inum, 7911725Segillett hdlp->ih_scratch1); 7920Sstevel@tonic-gate break; 7930Sstevel@tonic-gate case DDI_INTROP_REMISR: 7940Sstevel@tonic-gate msi_num = hdlp->ih_vector; 7950Sstevel@tonic-gate 7960Sstevel@tonic-gate if ((ret = px_lib_msi_getmsiq(dip, msi_num, 7970Sstevel@tonic-gate &msiq_id)) != DDI_SUCCESS) 7980Sstevel@tonic-gate return (ret); 7990Sstevel@tonic-gate 8000Sstevel@tonic-gate if ((ret = px_lib_msi_setstate(dip, msi_num, 801965Sgovinda PCI_MSI_STATE_IDLE)) != DDI_SUCCESS) 8020Sstevel@tonic-gate return (ret); 8030Sstevel@tonic-gate 8040Sstevel@tonic-gate ret = px_rem_msiq_intr(dip, rdip, 805965Sgovinda hdlp, msiq_rec_type, msi_num, msiq_id); 8060Sstevel@tonic-gate 8070Sstevel@tonic-gate hdlp->ih_vector = 0; 8080Sstevel@tonic-gate break; 8090Sstevel@tonic-gate case DDI_INTROP_ENABLE: 8100Sstevel@tonic-gate msi_num = hdlp->ih_vector; 8110Sstevel@tonic-gate 8120Sstevel@tonic-gate if ((ret = px_lib_msi_setvalid(dip, msi_num, 8130Sstevel@tonic-gate PCI_MSI_VALID)) != DDI_SUCCESS) 8140Sstevel@tonic-gate return (ret); 8150Sstevel@tonic-gate 8162755Segillett if ((pci_is_msi_enabled(rdip, hdlp->ih_type) != DDI_SUCCESS) || 8172755Segillett (hdlp->ih_type == DDI_INTR_TYPE_MSIX)) { 8180Sstevel@tonic-gate nintrs = i_ddi_intr_get_current_nintrs(hdlp->ih_dip); 8190Sstevel@tonic-gate 8200Sstevel@tonic-gate if ((ret = pci_msi_configure(rdip, hdlp->ih_type, 821965Sgovinda nintrs, hdlp->ih_inum, msi_addr, 8222755Segillett hdlp->ih_type == DDI_INTR_TYPE_MSIX ? 8232755Segillett msi_num : msi_num & ~(nintrs - 1))) != DDI_SUCCESS) 8240Sstevel@tonic-gate return (ret); 8250Sstevel@tonic-gate 8262755Segillett if ((ret = pci_msi_enable_mode(rdip, hdlp->ih_type)) 8272755Segillett != DDI_SUCCESS) 8280Sstevel@tonic-gate return (ret); 8290Sstevel@tonic-gate } 8300Sstevel@tonic-gate 831909Segillett if ((ret = pci_msi_clr_mask(rdip, hdlp->ih_type, 832909Segillett hdlp->ih_inum)) != DDI_SUCCESS) 833909Segillett return (ret); 834909Segillett 8351725Segillett if (hdlp->ih_flags & DDI_INTR_MSIX_DUP) 8361725Segillett break; 8371725Segillett 838909Segillett if ((ret = px_lib_msi_getmsiq(dip, msi_num, 839909Segillett &msiq_id)) != DDI_SUCCESS) 840909Segillett return (ret); 841909Segillett 842909Segillett ret = px_ib_update_intr_state(px_p, rdip, hdlp->ih_inum, 8432973Sgovinda px_msiqid_to_devino(px_p, msiq_id), hdlp->ih_pri, 8442973Sgovinda PX_INTR_STATE_ENABLE, msiq_rec_type, msi_num); 8450Sstevel@tonic-gate 8460Sstevel@tonic-gate break; 8470Sstevel@tonic-gate case DDI_INTROP_DISABLE: 8480Sstevel@tonic-gate msi_num = hdlp->ih_vector; 8490Sstevel@tonic-gate 8500Sstevel@tonic-gate if ((ret = pci_msi_set_mask(rdip, hdlp->ih_type, 8510Sstevel@tonic-gate hdlp->ih_inum)) != DDI_SUCCESS) 8520Sstevel@tonic-gate return (ret); 8530Sstevel@tonic-gate 854909Segillett if ((ret = px_lib_msi_setvalid(dip, msi_num, 855909Segillett PCI_MSI_INVALID)) != DDI_SUCCESS) 856909Segillett return (ret); 857909Segillett 8581725Segillett if (hdlp->ih_flags & DDI_INTR_MSIX_DUP) 8591725Segillett break; 8601725Segillett 861909Segillett if ((ret = px_lib_msi_getmsiq(dip, msi_num, 862909Segillett &msiq_id)) != DDI_SUCCESS) 863909Segillett return (ret); 864909Segillett 865909Segillett ret = px_ib_update_intr_state(px_p, rdip, 866909Segillett hdlp->ih_inum, px_msiqid_to_devino(px_p, msiq_id), 8672973Sgovinda hdlp->ih_pri, PX_INTR_STATE_DISABLE, msiq_rec_type, 8682973Sgovinda msi_num); 869909Segillett 8700Sstevel@tonic-gate break; 8710Sstevel@tonic-gate case DDI_INTROP_BLOCKENABLE: 8720Sstevel@tonic-gate nintrs = i_ddi_intr_get_current_nintrs(hdlp->ih_dip); 8730Sstevel@tonic-gate msi_num = hdlp->ih_vector; 8740Sstevel@tonic-gate 8750Sstevel@tonic-gate if ((ret = pci_msi_configure(rdip, hdlp->ih_type, 876965Sgovinda nintrs, hdlp->ih_inum, msi_addr, 8770Sstevel@tonic-gate msi_num & ~(nintrs - 1))) != DDI_SUCCESS) 8780Sstevel@tonic-gate return (ret); 8790Sstevel@tonic-gate 8800Sstevel@tonic-gate for (i = 0; i < nintrs; i++, msi_num++) { 8810Sstevel@tonic-gate if ((ret = px_lib_msi_setvalid(dip, msi_num, 8820Sstevel@tonic-gate PCI_MSI_VALID)) != DDI_SUCCESS) 8830Sstevel@tonic-gate return (ret); 884909Segillett 885909Segillett if ((ret = px_lib_msi_getmsiq(dip, msi_num, 886909Segillett &msiq_id)) != DDI_SUCCESS) 887909Segillett return (ret); 888909Segillett 889909Segillett if ((ret = px_ib_update_intr_state(px_p, rdip, 890909Segillett hdlp->ih_inum + i, px_msiqid_to_devino(px_p, 8912973Sgovinda msiq_id), hdlp->ih_pri, PX_INTR_STATE_ENABLE, 8922973Sgovinda msiq_rec_type, msi_num)) != DDI_SUCCESS) 893909Segillett return (ret); 8940Sstevel@tonic-gate } 8950Sstevel@tonic-gate 8962755Segillett ret = pci_msi_enable_mode(rdip, hdlp->ih_type); 8970Sstevel@tonic-gate break; 8980Sstevel@tonic-gate case DDI_INTROP_BLOCKDISABLE: 8990Sstevel@tonic-gate nintrs = i_ddi_intr_get_current_nintrs(hdlp->ih_dip); 9000Sstevel@tonic-gate msi_num = hdlp->ih_vector; 9010Sstevel@tonic-gate 9020Sstevel@tonic-gate if ((ret = pci_msi_disable_mode(rdip, hdlp->ih_type, 9032755Segillett hdlp->ih_cap & DDI_INTR_FLAG_BLOCK)) != DDI_SUCCESS) 9040Sstevel@tonic-gate return (ret); 9050Sstevel@tonic-gate 9060Sstevel@tonic-gate for (i = 0; i < nintrs; i++, msi_num++) { 9070Sstevel@tonic-gate if ((ret = px_lib_msi_setvalid(dip, msi_num, 9080Sstevel@tonic-gate PCI_MSI_INVALID)) != DDI_SUCCESS) 9090Sstevel@tonic-gate return (ret); 910909Segillett 911909Segillett if ((ret = px_lib_msi_getmsiq(dip, msi_num, 912909Segillett &msiq_id)) != DDI_SUCCESS) 913909Segillett return (ret); 914909Segillett 915909Segillett if ((ret = px_ib_update_intr_state(px_p, rdip, 916909Segillett hdlp->ih_inum + i, px_msiqid_to_devino(px_p, 9172973Sgovinda msiq_id), hdlp->ih_pri, PX_INTR_STATE_DISABLE, 9182973Sgovinda msiq_rec_type, msi_num)) != DDI_SUCCESS) 919909Segillett return (ret); 9200Sstevel@tonic-gate } 9210Sstevel@tonic-gate 9220Sstevel@tonic-gate break; 9230Sstevel@tonic-gate case DDI_INTROP_SETMASK: 9240Sstevel@tonic-gate ret = pci_msi_set_mask(rdip, hdlp->ih_type, hdlp->ih_inum); 9250Sstevel@tonic-gate break; 9260Sstevel@tonic-gate case DDI_INTROP_CLRMASK: 9270Sstevel@tonic-gate ret = pci_msi_clr_mask(rdip, hdlp->ih_type, hdlp->ih_inum); 9280Sstevel@tonic-gate break; 9290Sstevel@tonic-gate case DDI_INTROP_GETPENDING: 9300Sstevel@tonic-gate ret = pci_msi_get_pending(rdip, hdlp->ih_type, 9310Sstevel@tonic-gate hdlp->ih_inum, (int *)result); 9320Sstevel@tonic-gate break; 9330Sstevel@tonic-gate case DDI_INTROP_NINTRS: 9340Sstevel@tonic-gate ret = pci_msi_get_nintrs(rdip, hdlp->ih_type, (int *)result); 9350Sstevel@tonic-gate break; 9360Sstevel@tonic-gate case DDI_INTROP_NAVAIL: 9370Sstevel@tonic-gate /* XXX - a new interface may be needed */ 9380Sstevel@tonic-gate ret = pci_msi_get_nintrs(rdip, hdlp->ih_type, (int *)result); 9390Sstevel@tonic-gate break; 9400Sstevel@tonic-gate default: 9410Sstevel@tonic-gate ret = DDI_ENOTSUP; 9420Sstevel@tonic-gate break; 9430Sstevel@tonic-gate } 9440Sstevel@tonic-gate 9450Sstevel@tonic-gate return (ret); 9460Sstevel@tonic-gate } 9470Sstevel@tonic-gate 94866Sesolom static struct { 94966Sesolom kstat_named_t pxintr_ks_name; 95066Sesolom kstat_named_t pxintr_ks_type; 95166Sesolom kstat_named_t pxintr_ks_cpu; 95266Sesolom kstat_named_t pxintr_ks_pil; 95366Sesolom kstat_named_t pxintr_ks_time; 95466Sesolom kstat_named_t pxintr_ks_ino; 95566Sesolom kstat_named_t pxintr_ks_cookie; 95666Sesolom kstat_named_t pxintr_ks_devpath; 95766Sesolom kstat_named_t pxintr_ks_buspath; 95866Sesolom } pxintr_ks_template = { 95966Sesolom { "name", KSTAT_DATA_CHAR }, 96066Sesolom { "type", KSTAT_DATA_CHAR }, 96166Sesolom { "cpu", KSTAT_DATA_UINT64 }, 96266Sesolom { "pil", KSTAT_DATA_UINT64 }, 96366Sesolom { "time", KSTAT_DATA_UINT64 }, 96466Sesolom { "ino", KSTAT_DATA_UINT64 }, 96566Sesolom { "cookie", KSTAT_DATA_UINT64 }, 96666Sesolom { "devpath", KSTAT_DATA_STRING }, 96766Sesolom { "buspath", KSTAT_DATA_STRING }, 96866Sesolom }; 96966Sesolom 97066Sesolom static uint32_t pxintr_ks_instance; 9711811Sesolom static char ih_devpath[MAXPATHLEN]; 9721811Sesolom static char ih_buspath[MAXPATHLEN]; 97366Sesolom kmutex_t pxintr_ks_template_lock; 97466Sesolom 97566Sesolom int 97666Sesolom px_ks_update(kstat_t *ksp, int rw) 97766Sesolom { 97866Sesolom px_ih_t *ih_p = ksp->ks_private; 97966Sesolom int maxlen = sizeof (pxintr_ks_template.pxintr_ks_name.value.c); 9802973Sgovinda px_ino_pil_t *ipil_p = ih_p->ih_ipil_p; 9812973Sgovinda px_ino_t *ino_p = ipil_p->ipil_ino_p; 9822973Sgovinda px_t *px_p = ino_p->ino_ib_p->ib_px_p; 98366Sesolom devino_t ino; 98466Sesolom sysino_t sysino; 98566Sesolom 9862973Sgovinda ino = ino_p->ino_ino; 98766Sesolom (void) px_lib_intr_devino_to_sysino(px_p->px_dip, ino, &sysino); 98866Sesolom 98966Sesolom (void) snprintf(pxintr_ks_template.pxintr_ks_name.value.c, maxlen, 99066Sesolom "%s%d", ddi_driver_name(ih_p->ih_dip), 99166Sesolom ddi_get_instance(ih_p->ih_dip)); 99266Sesolom 99366Sesolom (void) ddi_pathname(ih_p->ih_dip, ih_devpath); 99466Sesolom (void) ddi_pathname(px_p->px_dip, ih_buspath); 99566Sesolom kstat_named_setstr(&pxintr_ks_template.pxintr_ks_devpath, ih_devpath); 99666Sesolom kstat_named_setstr(&pxintr_ks_template.pxintr_ks_buspath, ih_buspath); 99766Sesolom 9981087Sschwartz if (ih_p->ih_intr_state == PX_INTR_STATE_ENABLE) { 9991087Sschwartz 1000*4397Sschwartz switch (i_ddi_intr_get_current_type(ih_p->ih_dip)) { 1001*4397Sschwartz case DDI_INTR_TYPE_MSI: 1002*4397Sschwartz (void) strcpy(pxintr_ks_template.pxintr_ks_type.value.c, 1003*4397Sschwartz "msi"); 1004*4397Sschwartz break; 1005*4397Sschwartz case DDI_INTR_TYPE_MSIX: 1006*4397Sschwartz (void) strcpy(pxintr_ks_template.pxintr_ks_type.value.c, 1007*4397Sschwartz "msix"); 1008*4397Sschwartz break; 1009*4397Sschwartz default: 1010*4397Sschwartz (void) strcpy(pxintr_ks_template.pxintr_ks_type.value.c, 1011*4397Sschwartz "fixed"); 1012*4397Sschwartz break; 1013*4397Sschwartz } 1014*4397Sschwartz 10152973Sgovinda pxintr_ks_template.pxintr_ks_cpu.value.ui64 = ino_p->ino_cpuid; 10162973Sgovinda pxintr_ks_template.pxintr_ks_pil.value.ui64 = ipil_p->ipil_pil; 10171087Sschwartz pxintr_ks_template.pxintr_ks_time.value.ui64 = ih_p->ih_nsec + 10181087Sschwartz (uint64_t)tick2ns((hrtime_t)ih_p->ih_ticks, 10192973Sgovinda ino_p->ino_cpuid); 10201087Sschwartz pxintr_ks_template.pxintr_ks_ino.value.ui64 = ino; 10211087Sschwartz pxintr_ks_template.pxintr_ks_cookie.value.ui64 = sysino; 10221087Sschwartz } else { 10231087Sschwartz (void) strcpy(pxintr_ks_template.pxintr_ks_type.value.c, 10241087Sschwartz "disabled"); 10251087Sschwartz pxintr_ks_template.pxintr_ks_cpu.value.ui64 = 0; 10261087Sschwartz pxintr_ks_template.pxintr_ks_pil.value.ui64 = 0; 10271087Sschwartz pxintr_ks_template.pxintr_ks_time.value.ui64 = 0; 10281087Sschwartz pxintr_ks_template.pxintr_ks_ino.value.ui64 = 0; 10291087Sschwartz pxintr_ks_template.pxintr_ks_cookie.value.ui64 = 0; 10301087Sschwartz } 103166Sesolom return (0); 103266Sesolom } 103366Sesolom 103466Sesolom void 103566Sesolom px_create_intr_kstats(px_ih_t *ih_p) 103666Sesolom { 103766Sesolom msiq_rec_type_t rec_type = ih_p->ih_rec_type; 103866Sesolom 103966Sesolom ASSERT(ih_p->ih_ksp == NULL); 104066Sesolom 104166Sesolom /* 104266Sesolom * Create pci_intrs::: kstats for all ih types except messages, 104366Sesolom * which represent unusual conditions and don't need to be tracked. 104466Sesolom */ 104566Sesolom if (rec_type == 0 || rec_type == MSI32_REC || rec_type == MSI64_REC) { 104666Sesolom ih_p->ih_ksp = kstat_create("pci_intrs", 104766Sesolom atomic_inc_32_nv(&pxintr_ks_instance), "config", 104866Sesolom "interrupts", KSTAT_TYPE_NAMED, 104966Sesolom sizeof (pxintr_ks_template) / sizeof (kstat_named_t), 105066Sesolom KSTAT_FLAG_VIRTUAL); 105166Sesolom } 105266Sesolom if (ih_p->ih_ksp != NULL) { 105366Sesolom ih_p->ih_ksp->ks_data_size += MAXPATHLEN * 2; 105466Sesolom ih_p->ih_ksp->ks_lock = &pxintr_ks_template_lock; 105566Sesolom ih_p->ih_ksp->ks_data = &pxintr_ks_template; 105666Sesolom ih_p->ih_ksp->ks_private = ih_p; 105766Sesolom ih_p->ih_ksp->ks_update = px_ks_update; 105866Sesolom } 105966Sesolom } 106066Sesolom 1061693Sgovinda /* 1062693Sgovinda * px_add_intx_intr: 1063693Sgovinda * 1064693Sgovinda * This function is called to register INTx and legacy hardware 1065693Sgovinda * interrupt pins interrupts. 1066693Sgovinda */ 10670Sstevel@tonic-gate int 10680Sstevel@tonic-gate px_add_intx_intr(dev_info_t *dip, dev_info_t *rdip, 10690Sstevel@tonic-gate ddi_intr_handle_impl_t *hdlp) 10700Sstevel@tonic-gate { 10710Sstevel@tonic-gate px_t *px_p = INST_TO_STATE(ddi_get_instance(dip)); 10720Sstevel@tonic-gate px_ib_t *ib_p = px_p->px_ib_p; 10730Sstevel@tonic-gate devino_t ino; 10740Sstevel@tonic-gate px_ih_t *ih_p; 10752973Sgovinda px_ino_t *ino_p; 10762973Sgovinda px_ino_pil_t *ipil_p, *ipil_list; 10770Sstevel@tonic-gate int32_t weight; 10780Sstevel@tonic-gate int ret = DDI_SUCCESS; 10790Sstevel@tonic-gate 10800Sstevel@tonic-gate ino = hdlp->ih_vector; 10810Sstevel@tonic-gate 10820Sstevel@tonic-gate DBG(DBG_A_INTX, dip, "px_add_intx_intr: rdip=%s%d ino=%x " 10830Sstevel@tonic-gate "handler=%x arg1=%x arg2=%x\n", ddi_driver_name(rdip), 10840Sstevel@tonic-gate ddi_get_instance(rdip), ino, hdlp->ih_cb_func, 10850Sstevel@tonic-gate hdlp->ih_cb_arg1, hdlp->ih_cb_arg2); 10860Sstevel@tonic-gate 10870Sstevel@tonic-gate ih_p = px_ib_alloc_ih(rdip, hdlp->ih_inum, 10880Sstevel@tonic-gate hdlp->ih_cb_func, hdlp->ih_cb_arg1, hdlp->ih_cb_arg2, 0, 0); 10890Sstevel@tonic-gate 10900Sstevel@tonic-gate mutex_enter(&ib_p->ib_ino_lst_mutex); 10910Sstevel@tonic-gate 10922973Sgovinda ino_p = px_ib_locate_ino(ib_p, ino); 10932973Sgovinda ipil_list = ino_p ? ino_p->ino_ipil_p : NULL; 10942973Sgovinda 10952973Sgovinda /* Sharing ino */ 10962973Sgovinda if (ino_p && (ipil_p = px_ib_ino_locate_ipil(ino_p, hdlp->ih_pri))) { 10972973Sgovinda if (px_ib_intr_locate_ih(ipil_p, rdip, hdlp->ih_inum, 0, 0)) { 10980Sstevel@tonic-gate DBG(DBG_A_INTX, dip, "px_add_intx_intr: " 10992973Sgovinda "dup intr #%d\n", hdlp->ih_inum); 11000Sstevel@tonic-gate 11010Sstevel@tonic-gate ret = DDI_FAILURE; 11020Sstevel@tonic-gate goto fail1; 11030Sstevel@tonic-gate } 11040Sstevel@tonic-gate 11050Sstevel@tonic-gate /* Save mondo value in hdlp */ 11060Sstevel@tonic-gate hdlp->ih_vector = ino_p->ino_sysino; 11070Sstevel@tonic-gate 11082973Sgovinda if ((ret = px_ib_ino_add_intr(px_p, ipil_p, 11092973Sgovinda ih_p)) != DDI_SUCCESS) 11100Sstevel@tonic-gate goto fail1; 11112973Sgovinda 11122973Sgovinda goto ino_done; 11132973Sgovinda } 11140Sstevel@tonic-gate 11152973Sgovinda if (hdlp->ih_pri == 0) 11162973Sgovinda hdlp->ih_pri = px_class_to_pil(rdip); 11172973Sgovinda 11182973Sgovinda ipil_p = px_ib_new_ino_pil(ib_p, ino, hdlp->ih_pri, ih_p); 11192973Sgovinda ino_p = ipil_p->ipil_ino_p; 11200Sstevel@tonic-gate 11212973Sgovinda /* Save mondo value in hdlp */ 11222973Sgovinda hdlp->ih_vector = ino_p->ino_sysino; 11230Sstevel@tonic-gate 11242973Sgovinda DBG(DBG_A_INTX, dip, "px_add_intx_intr: pil=0x%x mondo=0x%x\n", 11252973Sgovinda hdlp->ih_pri, hdlp->ih_vector); 11260Sstevel@tonic-gate 11272973Sgovinda DDI_INTR_ASSIGN_HDLR_N_ARGS(hdlp, 11282973Sgovinda (ddi_intr_handler_t *)px_intx_intr, (caddr_t)ipil_p, NULL); 11290Sstevel@tonic-gate 11302973Sgovinda ret = i_ddi_add_ivintr(hdlp); 11310Sstevel@tonic-gate 11322973Sgovinda /* 11332973Sgovinda * Restore original interrupt handler 11342973Sgovinda * and arguments in interrupt handle. 11352973Sgovinda */ 11362973Sgovinda DDI_INTR_ASSIGN_HDLR_N_ARGS(hdlp, ih_p->ih_handler, 11372973Sgovinda ih_p->ih_handler_arg1, ih_p->ih_handler_arg2); 11380Sstevel@tonic-gate 11392973Sgovinda if (ret != DDI_SUCCESS) 11402973Sgovinda goto fail2; 11410Sstevel@tonic-gate 11422973Sgovinda /* Save the pil for this ino */ 11432973Sgovinda ipil_p->ipil_pil = hdlp->ih_pri; 11440Sstevel@tonic-gate 11452973Sgovinda /* Select cpu, saving it for sharing and removal */ 11462973Sgovinda if (ipil_list == NULL) { 11470Sstevel@tonic-gate ino_p->ino_cpuid = intr_dist_cpuid(); 11480Sstevel@tonic-gate 11490Sstevel@tonic-gate /* Enable interrupt */ 11500Sstevel@tonic-gate px_ib_intr_enable(px_p, ino_p->ino_cpuid, ino); 11510Sstevel@tonic-gate } 11520Sstevel@tonic-gate 11532973Sgovinda ino_done: 11542973Sgovinda /* Add weight to the cpu that we are already targeting */ 11550Sstevel@tonic-gate weight = px_class_to_intr_weight(rdip); 11560Sstevel@tonic-gate intr_dist_cpuid_add_device_weight(ino_p->ino_cpuid, rdip, weight); 11570Sstevel@tonic-gate 11582973Sgovinda ih_p->ih_ipil_p = ipil_p; 115966Sesolom px_create_intr_kstats(ih_p); 11600Sstevel@tonic-gate if (ih_p->ih_ksp) 11610Sstevel@tonic-gate kstat_install(ih_p->ih_ksp); 11620Sstevel@tonic-gate mutex_exit(&ib_p->ib_ino_lst_mutex); 11630Sstevel@tonic-gate 11640Sstevel@tonic-gate DBG(DBG_A_INTX, dip, "px_add_intx_intr: done! Interrupt 0x%x pil=%x\n", 11650Sstevel@tonic-gate ino_p->ino_sysino, hdlp->ih_pri); 11660Sstevel@tonic-gate 11670Sstevel@tonic-gate return (ret); 11680Sstevel@tonic-gate fail2: 11692973Sgovinda px_ib_delete_ino_pil(ib_p, ipil_p); 11700Sstevel@tonic-gate fail1: 11710Sstevel@tonic-gate if (ih_p->ih_config_handle) 11720Sstevel@tonic-gate pci_config_teardown(&ih_p->ih_config_handle); 11730Sstevel@tonic-gate 11740Sstevel@tonic-gate mutex_exit(&ib_p->ib_ino_lst_mutex); 11750Sstevel@tonic-gate kmem_free(ih_p, sizeof (px_ih_t)); 11760Sstevel@tonic-gate 11770Sstevel@tonic-gate DBG(DBG_A_INTX, dip, "px_add_intx_intr: Failed! Interrupt 0x%x " 11780Sstevel@tonic-gate "pil=%x\n", ino_p->ino_sysino, hdlp->ih_pri); 11790Sstevel@tonic-gate 11800Sstevel@tonic-gate return (ret); 11810Sstevel@tonic-gate } 11820Sstevel@tonic-gate 1183693Sgovinda /* 1184693Sgovinda * px_rem_intx_intr: 1185693Sgovinda * 1186693Sgovinda * This function is called to unregister INTx and legacy hardware 1187693Sgovinda * interrupt pins interrupts. 1188693Sgovinda */ 11890Sstevel@tonic-gate int 11900Sstevel@tonic-gate px_rem_intx_intr(dev_info_t *dip, dev_info_t *rdip, 11910Sstevel@tonic-gate ddi_intr_handle_impl_t *hdlp) 11920Sstevel@tonic-gate { 11930Sstevel@tonic-gate px_t *px_p = INST_TO_STATE(ddi_get_instance(dip)); 11940Sstevel@tonic-gate px_ib_t *ib_p = px_p->px_ib_p; 11950Sstevel@tonic-gate devino_t ino; 11960Sstevel@tonic-gate cpuid_t curr_cpu; 11972973Sgovinda px_ino_t *ino_p; 11982973Sgovinda px_ino_pil_t *ipil_p; 11990Sstevel@tonic-gate px_ih_t *ih_p; 12000Sstevel@tonic-gate int ret = DDI_SUCCESS; 12010Sstevel@tonic-gate 12020Sstevel@tonic-gate ino = hdlp->ih_vector; 12030Sstevel@tonic-gate 12040Sstevel@tonic-gate DBG(DBG_R_INTX, dip, "px_rem_intx_intr: rdip=%s%d ino=%x\n", 12050Sstevel@tonic-gate ddi_driver_name(rdip), ddi_get_instance(rdip), ino); 12060Sstevel@tonic-gate 12070Sstevel@tonic-gate mutex_enter(&ib_p->ib_ino_lst_mutex); 12080Sstevel@tonic-gate 12090Sstevel@tonic-gate ino_p = px_ib_locate_ino(ib_p, ino); 12102973Sgovinda ipil_p = px_ib_ino_locate_ipil(ino_p, hdlp->ih_pri); 12112973Sgovinda ih_p = px_ib_intr_locate_ih(ipil_p, rdip, hdlp->ih_inum, 0, 0); 12120Sstevel@tonic-gate 12130Sstevel@tonic-gate /* Get the current cpu */ 12140Sstevel@tonic-gate if ((ret = px_lib_intr_gettarget(px_p->px_dip, ino_p->ino_sysino, 12150Sstevel@tonic-gate &curr_cpu)) != DDI_SUCCESS) 12160Sstevel@tonic-gate goto fail; 12170Sstevel@tonic-gate 12182973Sgovinda if ((ret = px_ib_ino_rem_intr(px_p, ipil_p, ih_p)) != DDI_SUCCESS) 12190Sstevel@tonic-gate goto fail; 12200Sstevel@tonic-gate 12210Sstevel@tonic-gate intr_dist_cpuid_rem_device_weight(ino_p->ino_cpuid, rdip); 12220Sstevel@tonic-gate 12232973Sgovinda if (ipil_p->ipil_ih_size == 0) { 12240Sstevel@tonic-gate hdlp->ih_vector = ino_p->ino_sysino; 12250Sstevel@tonic-gate i_ddi_rem_ivintr(hdlp); 12260Sstevel@tonic-gate 12272973Sgovinda px_ib_delete_ino_pil(ib_p, ipil_p); 12282973Sgovinda } 12292973Sgovinda 12302973Sgovinda if (ino_p->ino_ipil_size == 0) { 12312973Sgovinda kmem_free(ino_p, sizeof (px_ino_t)); 12320Sstevel@tonic-gate } else { 12333780Segillett /* Re-enable interrupt only if mapping register still shared */ 12343780Segillett PX_INTR_ENABLE(px_p->px_dip, ino_p->ino_sysino, curr_cpu); 12350Sstevel@tonic-gate } 12360Sstevel@tonic-gate 12370Sstevel@tonic-gate fail: 12380Sstevel@tonic-gate mutex_exit(&ib_p->ib_ino_lst_mutex); 12390Sstevel@tonic-gate return (ret); 12400Sstevel@tonic-gate } 12410Sstevel@tonic-gate 1242693Sgovinda /* 1243693Sgovinda * px_add_msiq_intr: 1244693Sgovinda * 1245693Sgovinda * This function is called to register MSI/Xs and PCIe message interrupts. 1246693Sgovinda */ 12470Sstevel@tonic-gate int 12480Sstevel@tonic-gate px_add_msiq_intr(dev_info_t *dip, dev_info_t *rdip, 12490Sstevel@tonic-gate ddi_intr_handle_impl_t *hdlp, msiq_rec_type_t rec_type, 12500Sstevel@tonic-gate msgcode_t msg_code, msiqid_t *msiq_id_p) 12510Sstevel@tonic-gate { 12520Sstevel@tonic-gate px_t *px_p = INST_TO_STATE(ddi_get_instance(dip)); 12530Sstevel@tonic-gate px_ib_t *ib_p = px_p->px_ib_p; 12540Sstevel@tonic-gate px_msiq_state_t *msiq_state_p = &ib_p->ib_msiq_state; 12550Sstevel@tonic-gate devino_t ino; 12560Sstevel@tonic-gate px_ih_t *ih_p; 12572973Sgovinda px_ino_t *ino_p; 12582973Sgovinda px_ino_pil_t *ipil_p, *ipil_list; 12590Sstevel@tonic-gate int32_t weight; 12600Sstevel@tonic-gate int ret = DDI_SUCCESS; 12610Sstevel@tonic-gate 12620Sstevel@tonic-gate DBG(DBG_MSIQ, dip, "px_add_msiq_intr: rdip=%s%d handler=%x " 12630Sstevel@tonic-gate "arg1=%x arg2=%x\n", ddi_driver_name(rdip), ddi_get_instance(rdip), 12640Sstevel@tonic-gate hdlp->ih_cb_func, hdlp->ih_cb_arg1, hdlp->ih_cb_arg2); 12650Sstevel@tonic-gate 12660Sstevel@tonic-gate if ((ret = px_msiq_alloc(px_p, rec_type, msiq_id_p)) != DDI_SUCCESS) { 12670Sstevel@tonic-gate DBG(DBG_MSIQ, dip, "px_add_msiq_intr: " 12680Sstevel@tonic-gate "msiq allocation failed\n"); 12690Sstevel@tonic-gate return (ret); 12700Sstevel@tonic-gate } 12710Sstevel@tonic-gate 12720Sstevel@tonic-gate ino = px_msiqid_to_devino(px_p, *msiq_id_p); 12730Sstevel@tonic-gate 12740Sstevel@tonic-gate ih_p = px_ib_alloc_ih(rdip, hdlp->ih_inum, hdlp->ih_cb_func, 12750Sstevel@tonic-gate hdlp->ih_cb_arg1, hdlp->ih_cb_arg2, rec_type, msg_code); 12760Sstevel@tonic-gate 12770Sstevel@tonic-gate mutex_enter(&ib_p->ib_ino_lst_mutex); 12780Sstevel@tonic-gate 12792973Sgovinda ino_p = px_ib_locate_ino(ib_p, ino); 12802973Sgovinda ipil_list = ino_p ? ino_p->ino_ipil_p : NULL; 12812973Sgovinda 12822973Sgovinda /* Sharing ino */ 12832973Sgovinda if (ino_p && (ipil_p = px_ib_ino_locate_ipil(ino_p, hdlp->ih_pri))) { 12842973Sgovinda if (px_ib_intr_locate_ih(ipil_p, rdip, 12852973Sgovinda hdlp->ih_inum, rec_type, msg_code)) { 12860Sstevel@tonic-gate DBG(DBG_MSIQ, dip, "px_add_msiq_intr: " 12872973Sgovinda "dup intr #%d\n", hdlp->ih_inum); 12880Sstevel@tonic-gate 12890Sstevel@tonic-gate ret = DDI_FAILURE; 12900Sstevel@tonic-gate goto fail1; 12910Sstevel@tonic-gate } 12920Sstevel@tonic-gate 12930Sstevel@tonic-gate /* Save mondo value in hdlp */ 12940Sstevel@tonic-gate hdlp->ih_vector = ino_p->ino_sysino; 12950Sstevel@tonic-gate 12962973Sgovinda if ((ret = px_ib_ino_add_intr(px_p, ipil_p, 12972973Sgovinda ih_p)) != DDI_SUCCESS) 12982973Sgovinda goto fail1; 12992973Sgovinda 13002973Sgovinda goto ino_done; 13012973Sgovinda } 13022973Sgovinda 13032973Sgovinda if (hdlp->ih_pri == 0) 13042973Sgovinda hdlp->ih_pri = px_class_to_pil(rdip); 13050Sstevel@tonic-gate 13062973Sgovinda ipil_p = px_ib_new_ino_pil(ib_p, ino, hdlp->ih_pri, ih_p); 13072973Sgovinda ino_p = ipil_p->ipil_ino_p; 13082973Sgovinda 13092973Sgovinda ino_p->ino_msiq_p = msiq_state_p->msiq_p + 13102973Sgovinda (*msiq_id_p - msiq_state_p->msiq_1st_msiq_id); 13110Sstevel@tonic-gate 13122973Sgovinda /* Save mondo value in hdlp */ 13132973Sgovinda hdlp->ih_vector = ino_p->ino_sysino; 13142973Sgovinda 13152973Sgovinda DBG(DBG_MSIQ, dip, "px_add_msiq_intr: pil=0x%x mondo=0x%x\n", 13162973Sgovinda hdlp->ih_pri, hdlp->ih_vector); 13170Sstevel@tonic-gate 13182973Sgovinda DDI_INTR_ASSIGN_HDLR_N_ARGS(hdlp, 13192973Sgovinda (ddi_intr_handler_t *)px_msiq_intr, (caddr_t)ipil_p, NULL); 13202973Sgovinda 13212973Sgovinda ret = i_ddi_add_ivintr(hdlp); 13220Sstevel@tonic-gate 13232973Sgovinda /* 13242973Sgovinda * Restore original interrupt handler 13252973Sgovinda * and arguments in interrupt handle. 13262973Sgovinda */ 13272973Sgovinda DDI_INTR_ASSIGN_HDLR_N_ARGS(hdlp, ih_p->ih_handler, 13282973Sgovinda ih_p->ih_handler_arg1, ih_p->ih_handler_arg2); 13290Sstevel@tonic-gate 13302973Sgovinda if (ret != DDI_SUCCESS) 13312973Sgovinda goto fail2; 13322973Sgovinda 13332973Sgovinda /* Save the pil for this ino */ 13342973Sgovinda ipil_p->ipil_pil = hdlp->ih_pri; 13352973Sgovinda 13362973Sgovinda /* Select cpu, saving it for sharing and removal */ 13372973Sgovinda if (ipil_list == NULL) { 13382973Sgovinda ino_p->ino_cpuid = intr_dist_cpuid(); 13390Sstevel@tonic-gate 13400Sstevel@tonic-gate /* Enable MSIQ */ 13410Sstevel@tonic-gate px_lib_msiq_setstate(dip, *msiq_id_p, PCI_MSIQ_STATE_IDLE); 13420Sstevel@tonic-gate px_lib_msiq_setvalid(dip, *msiq_id_p, PCI_MSIQ_VALID); 13430Sstevel@tonic-gate 13440Sstevel@tonic-gate /* Enable interrupt */ 13452973Sgovinda px_ib_intr_enable(px_p, ino_p->ino_cpuid, ino); 13460Sstevel@tonic-gate } 13470Sstevel@tonic-gate 13482973Sgovinda ino_done: 13492973Sgovinda /* Add weight to the cpu that we are already targeting */ 13500Sstevel@tonic-gate weight = px_class_to_intr_weight(rdip); 13510Sstevel@tonic-gate intr_dist_cpuid_add_device_weight(ino_p->ino_cpuid, rdip, weight); 13520Sstevel@tonic-gate 13532973Sgovinda ih_p->ih_ipil_p = ipil_p; 135466Sesolom px_create_intr_kstats(ih_p); 13550Sstevel@tonic-gate if (ih_p->ih_ksp) 13560Sstevel@tonic-gate kstat_install(ih_p->ih_ksp); 13570Sstevel@tonic-gate mutex_exit(&ib_p->ib_ino_lst_mutex); 13580Sstevel@tonic-gate 13590Sstevel@tonic-gate DBG(DBG_MSIQ, dip, "px_add_msiq_intr: done! Interrupt 0x%x pil=%x\n", 13600Sstevel@tonic-gate ino_p->ino_sysino, hdlp->ih_pri); 13610Sstevel@tonic-gate 13620Sstevel@tonic-gate return (ret); 13630Sstevel@tonic-gate fail2: 13642973Sgovinda px_ib_delete_ino_pil(ib_p, ipil_p); 13650Sstevel@tonic-gate fail1: 13660Sstevel@tonic-gate if (ih_p->ih_config_handle) 13670Sstevel@tonic-gate pci_config_teardown(&ih_p->ih_config_handle); 13680Sstevel@tonic-gate 13690Sstevel@tonic-gate mutex_exit(&ib_p->ib_ino_lst_mutex); 13700Sstevel@tonic-gate kmem_free(ih_p, sizeof (px_ih_t)); 13710Sstevel@tonic-gate 13720Sstevel@tonic-gate DBG(DBG_MSIQ, dip, "px_add_msiq_intr: Failed! Interrupt 0x%x pil=%x\n", 13730Sstevel@tonic-gate ino_p->ino_sysino, hdlp->ih_pri); 13740Sstevel@tonic-gate 13750Sstevel@tonic-gate return (ret); 13760Sstevel@tonic-gate } 13770Sstevel@tonic-gate 1378693Sgovinda /* 1379693Sgovinda * px_rem_msiq_intr: 1380693Sgovinda * 1381693Sgovinda * This function is called to unregister MSI/Xs and PCIe message interrupts. 1382693Sgovinda */ 13830Sstevel@tonic-gate int 13840Sstevel@tonic-gate px_rem_msiq_intr(dev_info_t *dip, dev_info_t *rdip, 13850Sstevel@tonic-gate ddi_intr_handle_impl_t *hdlp, msiq_rec_type_t rec_type, 13860Sstevel@tonic-gate msgcode_t msg_code, msiqid_t msiq_id) 13870Sstevel@tonic-gate { 13880Sstevel@tonic-gate px_t *px_p = INST_TO_STATE(ddi_get_instance(dip)); 13890Sstevel@tonic-gate px_ib_t *ib_p = px_p->px_ib_p; 13900Sstevel@tonic-gate devino_t ino = px_msiqid_to_devino(px_p, msiq_id); 13910Sstevel@tonic-gate cpuid_t curr_cpu; 13922973Sgovinda px_ino_t *ino_p; 13932973Sgovinda px_ino_pil_t *ipil_p; 13940Sstevel@tonic-gate px_ih_t *ih_p; 13950Sstevel@tonic-gate int ret = DDI_SUCCESS; 13960Sstevel@tonic-gate 13970Sstevel@tonic-gate DBG(DBG_MSIQ, dip, "px_rem_msiq_intr: rdip=%s%d msiq_id=%x ino=%x\n", 13980Sstevel@tonic-gate ddi_driver_name(rdip), ddi_get_instance(rdip), msiq_id, ino); 13990Sstevel@tonic-gate 14000Sstevel@tonic-gate mutex_enter(&ib_p->ib_ino_lst_mutex); 14010Sstevel@tonic-gate 14020Sstevel@tonic-gate ino_p = px_ib_locate_ino(ib_p, ino); 14032973Sgovinda ipil_p = px_ib_ino_locate_ipil(ino_p, hdlp->ih_pri); 14042973Sgovinda ih_p = px_ib_intr_locate_ih(ipil_p, rdip, hdlp->ih_inum, rec_type, 14052973Sgovinda msg_code); 14060Sstevel@tonic-gate 14070Sstevel@tonic-gate /* Get the current cpu */ 14080Sstevel@tonic-gate if ((ret = px_lib_intr_gettarget(px_p->px_dip, ino_p->ino_sysino, 14090Sstevel@tonic-gate &curr_cpu)) != DDI_SUCCESS) 14100Sstevel@tonic-gate goto fail; 14110Sstevel@tonic-gate 14122973Sgovinda if ((ret = px_ib_ino_rem_intr(px_p, ipil_p, ih_p)) != DDI_SUCCESS) 14130Sstevel@tonic-gate goto fail; 14140Sstevel@tonic-gate 14150Sstevel@tonic-gate intr_dist_cpuid_rem_device_weight(ino_p->ino_cpuid, rdip); 14160Sstevel@tonic-gate 14172973Sgovinda if (ipil_p->ipil_ih_size == 0) { 14180Sstevel@tonic-gate hdlp->ih_vector = ino_p->ino_sysino; 14190Sstevel@tonic-gate i_ddi_rem_ivintr(hdlp); 14200Sstevel@tonic-gate 14212973Sgovinda px_ib_delete_ino_pil(ib_p, ipil_p); 14222973Sgovinda 14232973Sgovinda if (ino_p->ino_ipil_size == 0) 14242973Sgovinda px_lib_msiq_setvalid(dip, 14252973Sgovinda px_devino_to_msiqid(px_p, ino), PCI_MSIQ_INVALID); 14260Sstevel@tonic-gate 14270Sstevel@tonic-gate (void) px_msiq_free(px_p, msiq_id); 14282973Sgovinda } 14292973Sgovinda 14302973Sgovinda if (ino_p->ino_ipil_size == 0) { 14312973Sgovinda kmem_free(ino_p, sizeof (px_ino_t)); 14320Sstevel@tonic-gate } else { 14333780Segillett /* Re-enable interrupt only if mapping register still shared */ 14343780Segillett PX_INTR_ENABLE(px_p->px_dip, ino_p->ino_sysino, curr_cpu); 14350Sstevel@tonic-gate } 14360Sstevel@tonic-gate 14370Sstevel@tonic-gate fail: 14380Sstevel@tonic-gate mutex_exit(&ib_p->ib_ino_lst_mutex); 14390Sstevel@tonic-gate return (ret); 14400Sstevel@tonic-gate } 1441