1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 /* 30 * apic_introp.c: 31 * Has code for Advanced DDI interrupt framework support. 32 */ 33 34 #include <sys/cpuvar.h> 35 #include <sys/psm.h> 36 #include "apic.h" 37 #include <sys/sunddi.h> 38 #include <sys/ddi_impldefs.h> 39 #include <sys/trap.h> 40 #include <sys/pci.h> 41 #include <sys/pci_intr_lib.h> 42 43 /* 44 * Local Function Prototypes 45 */ 46 int apic_pci_msi_enable_vector(dev_info_t *, int, int, 47 int, int, int); 48 apic_irq_t *apic_find_irq(dev_info_t *, struct intrspec *, int); 49 static int apic_get_pending(apic_irq_t *, int); 50 static void apic_clear_mask(apic_irq_t *); 51 static void apic_set_mask(apic_irq_t *); 52 static uchar_t apic_find_multi_vectors(int, int); 53 int apic_navail_vector(dev_info_t *, int); 54 int apic_alloc_vectors(dev_info_t *, int, int, int, int); 55 void apic_free_vectors(dev_info_t *, int, int, int, int); 56 int apic_intr_ops(dev_info_t *, ddi_intr_handle_impl_t *, 57 psm_intr_op_t, int *); 58 59 extern int intr_clear(void); 60 extern void intr_restore(uint_t); 61 extern uchar_t apic_bind_intr(dev_info_t *, int, uchar_t, uchar_t); 62 extern int apic_allocate_irq(int); 63 extern int apic_introp_xlate(dev_info_t *, struct intrspec *, int); 64 65 /* 66 * MSI support flag: 67 * reflects whether MSI is supported at APIC level 68 * it can also be patched through /etc/system 69 * 70 * 0 = default value - don't know and need to call apic_check_msi_support() 71 * to find out then set it accordingly 72 * 1 = supported 73 * -1 = not supported 74 */ 75 int apic_support_msi = 0; 76 77 /* Multiple vector support for MSI */ 78 int apic_multi_msi_enable = 1; 79 int apic_multi_msi_max = 2; 80 81 extern uchar_t apic_ipltopri[MAXIPL+1]; 82 extern uchar_t apic_vector_to_irq[APIC_MAX_VECTOR+1]; 83 extern int apic_max_device_irq; 84 extern int apic_min_device_irq; 85 extern apic_irq_t *apic_irq_table[APIC_MAX_VECTOR+1]; 86 extern volatile uint32_t *apicadr; /* virtual addr of local APIC */ 87 extern volatile int32_t *apicioadr[MAX_IO_APIC]; 88 extern lock_t apic_ioapic_lock; 89 extern kmutex_t airq_mutex; 90 extern apic_cpus_info_t *apic_cpus; 91 extern int apic_first_avail_irq; 92 93 94 /* 95 * apic_pci_msi_enable_vector: 96 * Set the address/data fields in the MSI/X capability structure 97 * XXX: MSI-X support 98 */ 99 /* ARGSUSED */ 100 int 101 apic_pci_msi_enable_vector(dev_info_t *dip, int type, int inum, int vector, 102 int count, int target_apic_id) 103 { 104 uint64_t msi_addr, msi_data; 105 106 DDI_INTR_IMPLDBG((CE_CONT, "apic_pci_msi_enable_vector: dip=0x%p\n" 107 "\tdriver = %s, inum=0x%x vector=0x%x apicid=0x%x\n", (void *)dip, 108 ddi_driver_name(dip), inum, vector, target_apic_id)); 109 110 /* MSI Address */ 111 msi_addr = (MSI_ADDR_HDR | (target_apic_id << MSI_ADDR_DEST_SHIFT)); 112 msi_addr |= ((MSI_ADDR_RH_FIXED << MSI_ADDR_RH_SHIFT) | 113 (MSI_ADDR_DM_PHYSICAL << MSI_ADDR_DM_SHIFT)); 114 115 /* MSI Data: MSI is edge triggered according to spec */ 116 msi_data = ((MSI_DATA_TM_EDGE << MSI_DATA_TM_SHIFT) | vector); 117 118 DDI_INTR_IMPLDBG((CE_CONT, "apic_pci_msi_enable_vector: addr=0x%lx " 119 "data=0x%lx\n", (long)msi_addr, (long)msi_data)); 120 121 if (pci_msi_configure(dip, type, count, inum, msi_addr, msi_data) != 122 DDI_SUCCESS) { 123 DDI_INTR_IMPLDBG((CE_CONT, "apic_pci_msi_enable_vector: " 124 "pci_msi_configure failed\n")); 125 return (PSM_FAILURE); 126 } 127 128 return (PSM_SUCCESS); 129 } 130 131 132 /* 133 * This function returns the no. of vectors available for the pri. 134 * dip is not used at this moment. If we really don't need that, 135 * it will be removed. 136 */ 137 /*ARGSUSED*/ 138 int 139 apic_navail_vector(dev_info_t *dip, int pri) 140 { 141 int lowest, highest, i, navail, count; 142 143 DDI_INTR_IMPLDBG((CE_CONT, "apic_navail_vector: dip: %p, pri: %x\n", 144 (void *)dip, pri)); 145 146 highest = apic_ipltopri[pri] + APIC_VECTOR_MASK; 147 lowest = apic_ipltopri[pri - 1] + APIC_VECTOR_PER_IPL; 148 navail = count = 0; 149 150 /* It has to be contiguous */ 151 for (i = lowest; i < highest; i++) { 152 count = 0; 153 while ((apic_vector_to_irq[i] == APIC_RESV_IRQ) && 154 (i < highest)) { 155 if ((i == T_FASTTRAP) || (i == APIC_SPUR_INTR)) 156 break; 157 count++; 158 i++; 159 } 160 if (count > navail) 161 navail = count; 162 } 163 return (navail); 164 } 165 166 static uchar_t 167 apic_find_multi_vectors(int pri, int count) 168 { 169 int lowest, highest, i, navail, start; 170 171 DDI_INTR_IMPLDBG((CE_CONT, "apic_find_mult: pri: %x, count: %x\n", 172 pri, count)); 173 174 highest = apic_ipltopri[pri] + APIC_VECTOR_MASK; 175 lowest = apic_ipltopri[pri - 1] + APIC_VECTOR_PER_IPL; 176 navail = 0; 177 178 /* It has to be contiguous */ 179 for (i = lowest; i < highest; i++) { 180 navail = 0; 181 start = i; 182 while ((apic_vector_to_irq[i] == APIC_RESV_IRQ) && 183 (i < highest)) { 184 if ((i == T_FASTTRAP) || (i == APIC_SPUR_INTR)) 185 break; 186 navail++; 187 if (navail >= count) 188 return (start); 189 i++; 190 } 191 } 192 return (0); 193 } 194 195 196 /* 197 * It finds the apic_irq_t associates with the dip, ispec and type. 198 */ 199 apic_irq_t * 200 apic_find_irq(dev_info_t *dip, struct intrspec *ispec, int type) 201 { 202 apic_irq_t *irqp; 203 int i; 204 205 DDI_INTR_IMPLDBG((CE_CONT, "apic_find_irq: dip=0x%p vec=0x%x " 206 "ipl=0x%x type=0x%x\n", (void *)dip, ispec->intrspec_vec, 207 ispec->intrspec_pri, type)); 208 209 for (i = apic_min_device_irq; i <= apic_max_device_irq; i++) { 210 if ((irqp = apic_irq_table[i]) == NULL) 211 continue; 212 if ((irqp->airq_dip == dip) && 213 (irqp->airq_origirq == ispec->intrspec_vec) && 214 (irqp->airq_ipl == ispec->intrspec_pri)) { 215 if (DDI_INTR_IS_MSI_OR_MSIX(type)) { 216 if (APIC_IS_MSI_OR_MSIX_INDEX(irqp-> 217 airq_mps_intr_index)) 218 return (irqp); 219 } else 220 return (irqp); 221 } 222 } 223 DDI_INTR_IMPLDBG((CE_CONT, "apic_find_irq: return NULL\n")); 224 return (NULL); 225 } 226 227 228 /* 229 * This function will return the pending bit of the irqp. 230 * It either comes from the IRR register of the APIC or the RDT 231 * entry of the I/O APIC. 232 * For the IRR to work, it needs to be to its binding CPU 233 */ 234 static int 235 apic_get_pending(apic_irq_t *irqp, int type) 236 { 237 int bit, index, irr, pending; 238 int intin_no; 239 volatile int32_t *ioapic; 240 241 DDI_INTR_IMPLDBG((CE_CONT, "apic_get_pending: irqp: %p, cpuid: %x " 242 "type: %x\n", (void *)irqp, irqp->airq_cpu & ~IRQ_USER_BOUND, 243 type)); 244 245 /* need to get on the bound cpu */ 246 mutex_enter(&cpu_lock); 247 affinity_set(irqp->airq_cpu & ~IRQ_USER_BOUND); 248 249 index = irqp->airq_vector / 32; 250 bit = irqp->airq_vector % 32; 251 irr = apicadr[APIC_IRR_REG + index]; 252 253 affinity_clear(); 254 mutex_exit(&cpu_lock); 255 256 pending = (irr & (1 << bit)) ? 1 : 0; 257 if (!pending && (type == DDI_INTR_TYPE_FIXED)) { 258 /* check I/O APIC for fixed interrupt */ 259 intin_no = irqp->airq_intin_no; 260 ioapic = apicioadr[irqp->airq_ioapicindex]; 261 pending = (READ_IOAPIC_RDT_ENTRY_LOW_DWORD(ioapic, intin_no) & 262 AV_PENDING) ? 1 : 0; 263 } 264 return (pending); 265 } 266 267 268 /* 269 * This function will clear the mask for the interrupt on the I/O APIC 270 */ 271 static void 272 apic_clear_mask(apic_irq_t *irqp) 273 { 274 int intin_no; 275 int iflag; 276 int32_t rdt_entry; 277 volatile int32_t *ioapic; 278 279 DDI_INTR_IMPLDBG((CE_CONT, "apic_clear_mask: irqp: %p\n", 280 (void *)irqp)); 281 282 intin_no = irqp->airq_intin_no; 283 ioapic = apicioadr[irqp->airq_ioapicindex]; 284 285 iflag = intr_clear(); 286 lock_set(&apic_ioapic_lock); 287 288 rdt_entry = READ_IOAPIC_RDT_ENTRY_LOW_DWORD(ioapic, intin_no); 289 290 /* clear mask */ 291 WRITE_IOAPIC_RDT_ENTRY_LOW_DWORD(ioapic, intin_no, 292 ((~AV_MASK) & rdt_entry)); 293 294 lock_clear(&apic_ioapic_lock); 295 intr_restore(iflag); 296 } 297 298 299 /* 300 * This function will mask the interrupt on the I/O APIC 301 */ 302 static void 303 apic_set_mask(apic_irq_t *irqp) 304 { 305 int intin_no; 306 volatile int32_t *ioapic; 307 int iflag; 308 int32_t rdt_entry; 309 310 DDI_INTR_IMPLDBG((CE_CONT, "apic_set_mask: irqp: %p\n", (void *)irqp)); 311 312 intin_no = irqp->airq_intin_no; 313 ioapic = apicioadr[irqp->airq_ioapicindex]; 314 315 iflag = intr_clear(); 316 317 lock_set(&apic_ioapic_lock); 318 319 rdt_entry = READ_IOAPIC_RDT_ENTRY_LOW_DWORD(ioapic, intin_no); 320 321 /* mask it */ 322 WRITE_IOAPIC_RDT_ENTRY_LOW_DWORD(ioapic, intin_no, 323 (AV_MASK | rdt_entry)); 324 325 lock_clear(&apic_ioapic_lock); 326 intr_restore(iflag); 327 } 328 329 330 /* 331 * This function allocate "count" vector(s) for the given "dip/pri/type" 332 */ 333 int 334 apic_alloc_vectors(dev_info_t *dip, int inum, int count, int pri, int type) 335 { 336 int rcount, i; 337 uchar_t start, irqno, cpu; 338 short idx; 339 major_t major; 340 apic_irq_t *irqptr; 341 342 /* for MSI/X only */ 343 if (!DDI_INTR_IS_MSI_OR_MSIX(type)) 344 return (0); 345 346 DDI_INTR_IMPLDBG((CE_CONT, "apic_alloc_vectors: dip=0x%p type=%d " 347 "inum=0x%x pri=0x%x count=0x%x\n", 348 (void *)dip, type, inum, pri, count)); 349 350 if (count > 1) { 351 if (apic_multi_msi_enable == 0) 352 count = 1; 353 else if (count > apic_multi_msi_max) 354 count = apic_multi_msi_max; 355 } 356 357 if ((rcount = apic_navail_vector(dip, pri)) > count) 358 rcount = count; 359 360 mutex_enter(&airq_mutex); 361 362 for (start = 0; rcount > 0; rcount--) { 363 if ((start = apic_find_multi_vectors(pri, rcount)) != 0) 364 break; 365 } 366 367 if (start == 0) { 368 /* no vector available */ 369 mutex_exit(&airq_mutex); 370 return (0); 371 } 372 373 idx = (short)((type == DDI_INTR_TYPE_MSI) ? MSI_INDEX : MSIX_INDEX); 374 major = (dip != NULL) ? ddi_name_to_major(ddi_get_name(dip)) : 0; 375 for (i = 0; i < rcount; i++) { 376 if ((irqno = apic_allocate_irq(apic_first_avail_irq)) == 377 (uchar_t)-1) { 378 mutex_exit(&airq_mutex); 379 DDI_INTR_IMPLDBG((CE_CONT, "apic_alloc_vectors: " 380 "apic_allocate_irq failed\n")); 381 return (i); 382 } 383 apic_max_device_irq = max(irqno, apic_max_device_irq); 384 apic_min_device_irq = min(irqno, apic_min_device_irq); 385 irqptr = apic_irq_table[irqno]; 386 #ifdef DEBUG 387 if (apic_vector_to_irq[start + i] != APIC_RESV_IRQ) 388 DDI_INTR_IMPLDBG((CE_CONT, "apic_alloc_vectors: " 389 "apic_vector_to_irq is not APIC_RESV_IRQ\n")); 390 #endif 391 apic_vector_to_irq[start + i] = (uchar_t)irqno; 392 393 irqptr->airq_vector = (uchar_t)(start + i); 394 irqptr->airq_ioapicindex = (uchar_t)inum; /* start */ 395 irqptr->airq_intin_no = (uchar_t)rcount; 396 irqptr->airq_ipl = pri; 397 irqptr->airq_vector = start + i; 398 irqptr->airq_origirq = (uchar_t)(inum + i); 399 irqptr->airq_share_id = 0; 400 irqptr->airq_mps_intr_index = idx; 401 irqptr->airq_dip = dip; 402 irqptr->airq_major = major; 403 if (i == 0) /* they all bound to the same cpu */ 404 cpu = irqptr->airq_cpu = apic_bind_intr(dip, irqno, 405 0xff, 0xff); 406 else 407 irqptr->airq_cpu = cpu; 408 DDI_INTR_IMPLDBG((CE_CONT, "apic_alloc_vectors: irq=0x%x " 409 "dip=0x%p vector=0x%x origirq=0x%x pri=0x%x\n", irqno, 410 (void *)irqptr->airq_dip, irqptr->airq_vector, 411 irqptr->airq_origirq, pri)); 412 } 413 mutex_exit(&airq_mutex); 414 return (rcount); 415 } 416 417 418 void 419 apic_free_vectors(dev_info_t *dip, int inum, int count, int pri, int type) 420 { 421 int i; 422 apic_irq_t *irqptr; 423 struct intrspec ispec; 424 425 DDI_INTR_IMPLDBG((CE_CONT, "apic_free_vectors: dip: %p inum: %x " 426 "count: %x pri: %x type: %x\n", 427 (void *)dip, inum, count, pri, type)); 428 429 /* for MSI/X only */ 430 if (!DDI_INTR_IS_MSI_OR_MSIX(type)) 431 return; 432 433 for (i = 0; i < count; i++) { 434 DDI_INTR_IMPLDBG((CE_CONT, "apic_free_vectors: inum=0x%x " 435 "pri=0x%x count=0x%x\n", inum, pri, count)); 436 ispec.intrspec_vec = inum + i; 437 ispec.intrspec_pri = pri; 438 if ((irqptr = apic_find_irq(dip, &ispec, type)) == NULL) { 439 DDI_INTR_IMPLDBG((CE_CONT, "apic_free_vectors: " 440 "dip=0x%p inum=0x%x pri=0x%x apic_find_irq() " 441 "failed\n", (void *)dip, inum, pri)); 442 continue; 443 } 444 irqptr->airq_mps_intr_index = FREE_INDEX; 445 apic_vector_to_irq[irqptr->airq_vector] = APIC_RESV_IRQ; 446 } 447 } 448 449 450 /* 451 * check whether the system supports MSI 452 * 453 * If PCI-E capability is found, then this must be a PCI-E system. 454 * Since MSI is required for PCI-E system, it returns PSM_SUCCESS 455 * to indicate this system supports MSI. 456 */ 457 int 458 apic_check_msi_support(dev_info_t *dip) 459 { 460 461 dev_info_t *rootdip; 462 char dev_type[16]; 463 int dev_len; 464 465 DDI_INTR_IMPLDBG((CE_CONT, "apic_check_msi_support: dip: 0x%p\n", 466 (void *)dip)); 467 468 /* check whether the device or its ancestors have PCI-E capability */ 469 for (rootdip = ddi_root_node(); dip != rootdip; 470 dip = ddi_get_parent(dip)) { 471 472 DDI_INTR_IMPLDBG((CE_CONT, "apic_check_msi_support: dip: 0x%p," 473 " driver: %s, binding: %s, nodename: %s\n", (void *)dip, 474 ddi_driver_name(dip), ddi_binding_name(dip), 475 ddi_node_name(dip))); 476 dev_len = sizeof (dev_type); 477 if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 478 "device_type", (caddr_t)dev_type, &dev_len) 479 != DDI_PROP_SUCCESS) 480 continue; 481 if (strcmp(dev_type, "pciex") == 0) 482 return (PSM_SUCCESS); 483 } 484 485 /* MSI is not supported on this system */ 486 DDI_INTR_IMPLDBG((CE_CONT, "apic_check_msi_support: no 'pciex' " 487 "device_type found\n")); 488 return (PSM_FAILURE); 489 } 490 491 /* 492 * This function provides external interface to the nexus for all 493 * functionalities related to the new DDI interrupt framework. 494 * 495 * Input: 496 * dip - pointer to the dev_info structure of the requested device 497 * hdlp - pointer to the internal interrupt handle structure for the 498 * requested interrupt 499 * intr_op - opcode for this call 500 * result - pointer to the integer that will hold the result to be 501 * passed back if return value is PSM_SUCCESS 502 * 503 * Output: 504 * return value is either PSM_SUCCESS or PSM_FAILURE 505 */ 506 int 507 apic_intr_ops(dev_info_t *dip, ddi_intr_handle_impl_t *hdlp, 508 psm_intr_op_t intr_op, int *result) 509 { 510 int cap; 511 int count_vec; 512 int old_priority; 513 int new_priority; 514 apic_irq_t *irqp; 515 struct intrspec *ispec, intr_spec; 516 517 DDI_INTR_IMPLDBG((CE_CONT, "apic_intr_ops: dip: %p hdlp: %p " 518 "intr_op: %x\n", (void *)dip, (void *)hdlp, intr_op)); 519 520 ispec = &intr_spec; 521 ispec->intrspec_pri = hdlp->ih_pri; 522 ispec->intrspec_vec = hdlp->ih_inum; 523 ispec->intrspec_func = hdlp->ih_cb_func; 524 525 switch (intr_op) { 526 case PSM_INTR_OP_CHECK_MSI: 527 /* 528 * Check MSI/X is supported or not at APIC level and 529 * masked off the MSI/X bits in hdlp->ih_type if not 530 * supported before return. If MSI/X is supported, 531 * leave the ih_type unchanged and return. 532 * 533 * hdlp->ih_type passed in from the nexus has all the 534 * interrupt types supported by the device. 535 */ 536 if (apic_support_msi == 0) { 537 /* 538 * if apic_support_msi is not set, call 539 * apic_check_msi_support() to check whether msi 540 * is supported first 541 */ 542 if (apic_check_msi_support(dip) == PSM_SUCCESS) 543 apic_support_msi = 1; 544 else 545 apic_support_msi = -1; 546 } 547 if (apic_support_msi == 1) 548 *result = hdlp->ih_type; 549 else 550 *result = hdlp->ih_type & ~(DDI_INTR_TYPE_MSI | 551 DDI_INTR_TYPE_MSIX); 552 break; 553 case PSM_INTR_OP_ALLOC_VECTORS: 554 *result = apic_alloc_vectors(dip, hdlp->ih_inum, 555 hdlp->ih_scratch1, hdlp->ih_pri, hdlp->ih_type); 556 break; 557 case PSM_INTR_OP_FREE_VECTORS: 558 apic_free_vectors(dip, hdlp->ih_inum, hdlp->ih_scratch1, 559 hdlp->ih_pri, hdlp->ih_type); 560 break; 561 case PSM_INTR_OP_NAVAIL_VECTORS: 562 *result = apic_navail_vector(dip, hdlp->ih_pri); 563 break; 564 case PSM_INTR_OP_XLATE_VECTOR: 565 ispec = (struct intrspec *)hdlp->ih_private; 566 *result = apic_introp_xlate(dip, ispec, hdlp->ih_type); 567 break; 568 case PSM_INTR_OP_GET_PENDING: 569 if ((irqp = apic_find_irq(dip, ispec, hdlp->ih_type)) == NULL) 570 return (PSM_FAILURE); 571 *result = apic_get_pending(irqp, hdlp->ih_type); 572 break; 573 case PSM_INTR_OP_CLEAR_MASK: 574 if (hdlp->ih_type != DDI_INTR_TYPE_FIXED) 575 return (PSM_FAILURE); 576 irqp = apic_find_irq(dip, ispec, hdlp->ih_type); 577 if (irqp == NULL) 578 return (PSM_FAILURE); 579 apic_clear_mask(irqp); 580 break; 581 case PSM_INTR_OP_SET_MASK: 582 if (hdlp->ih_type != DDI_INTR_TYPE_FIXED) 583 return (PSM_FAILURE); 584 if ((irqp = apic_find_irq(dip, ispec, hdlp->ih_type)) == NULL) 585 return (PSM_FAILURE); 586 apic_set_mask(irqp); 587 break; 588 case PSM_INTR_OP_GET_CAP: 589 cap = DDI_INTR_FLAG_PENDING; 590 if (hdlp->ih_type == DDI_INTR_TYPE_FIXED) 591 cap |= DDI_INTR_FLAG_MASKABLE; 592 *result = cap; 593 break; 594 case PSM_INTR_OP_GET_SHARED: 595 if (hdlp->ih_type != DDI_INTR_TYPE_FIXED) 596 return (PSM_FAILURE); 597 if ((irqp = apic_find_irq(dip, ispec, hdlp->ih_type)) == NULL) 598 return (PSM_FAILURE); 599 *result = irqp->airq_share ? 1: 0; 600 break; 601 case PSM_INTR_OP_SET_PRI: 602 old_priority = hdlp->ih_pri; /* save old value */ 603 new_priority = *(int *)result; /* try the new value */ 604 605 /* First, check if "hdlp->ih_scratch1" vectors exist? */ 606 if (apic_navail_vector(dip, new_priority) < hdlp->ih_scratch1) 607 return (PSM_FAILURE); 608 609 /* Now allocate the vectors */ 610 count_vec = apic_alloc_vectors(dip, hdlp->ih_inum, 611 hdlp->ih_scratch1, new_priority, hdlp->ih_type); 612 613 /* Did we get fewer vectors? */ 614 if (count_vec != hdlp->ih_scratch1) { 615 apic_free_vectors(dip, hdlp->ih_inum, count_vec, 616 new_priority, hdlp->ih_type); 617 return (PSM_FAILURE); 618 } 619 620 /* Finally, free the previously allocated vectors */ 621 apic_free_vectors(dip, hdlp->ih_inum, count_vec, 622 old_priority, hdlp->ih_type); 623 hdlp->ih_pri = new_priority; /* set the new value */ 624 break; 625 case PSM_INTR_OP_SET_CAP: 626 default: 627 return (PSM_FAILURE); 628 } 629 return (PSM_SUCCESS); 630 } 631