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 (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 /*
  22  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  23  * Use is subject to license terms.
  24  */
  25 
  26 #define PSMI_1_7
  27 
  28 #include <sys/mutex.h>
  29 #include <sys/types.h>
  30 #include <sys/time.h>
  31 #include <sys/machlock.h>
  32 #include <sys/smp_impldefs.h>
  33 #include <sys/uadmin.h>
  34 #include <sys/promif.h>
  35 #include <sys/psm.h>
  36 #include <sys/pit.h>
  37 #include <sys/apic.h>
  38 #include <sys/psm_common.h>
  39 #include <sys/atomic.h>
  40 #include <sys/archsystm.h>
  41 
  42 #define NSEC_IN_SEC             1000000000
  43 
  44 /*
  45  * Local Function Prototypes
  46  */
  47 static void uppc_softinit(void);
  48 static void uppc_picinit();
  49 static int uppc_post_cpu_start(void);
  50 static int uppc_clkinit(int);
  51 static int uppc_addspl(int irqno, int ipl, int min_ipl, int max_ipl);
  52 static int uppc_delspl(int irqno, int ipl, int min_ipl, int max_ipl);
  53 static processorid_t uppc_get_next_processorid(processorid_t cpu_id);
  54 static int uppc_get_clockirq(int ipl);
  55 static int uppc_probe(void);
  56 static int uppc_translate_irq(dev_info_t *dip, int irqno);
  57 static void uppc_shutdown(int cmd, int fcn);
  58 static void uppc_preshutdown(int cmd, int fcn);
  59 static int uppc_state(psm_state_request_t *request);
  60 static int uppc_init_acpi(void);
  61 static void uppc_setspl(int);
  62 static int uppc_intr_enter(int, int *);
  63 static void uppc_intr_exit(int, int);
  64 static hrtime_t uppc_gethrtime();
  65 
  66 static int uppc_acpi_irq_configure(acpi_psm_lnk_t *acpipsmlnkp, dev_info_t *dip,
  67     int *pci_irqp, iflag_t *intr_flagp);
  68 
  69 /*
  70  * Global Data
  71  */
  72 static struct standard_pic pics0;
  73 int uppc_use_acpi = 1;  /* Use ACPI by default */
  74 int uppc_enable_acpi = 0;
  75 
  76 
  77 /*
  78  * For interrupt link devices, if uppc_unconditional_srs is set, an irq resource
  79  * will be assigned (via _SRS). If it is not set, use the current
  80  * irq setting (via _CRS), but only if that irq is in the set of possible
  81  * irqs (returned by _PRS) for the device.
  82  */
  83 int uppc_unconditional_srs = 1;
  84 
  85 /*
  86  * For interrupt link devices, if uppc_prefer_crs is set when we are
  87  * assigning an IRQ resource to a device, prefer the current IRQ setting
  88  * over other possible irq settings under same conditions.
  89  */
  90 int uppc_prefer_crs = 1;
  91 
  92 int uppc_verbose = 0;
  93 
  94 /* flag definitions for uppc_verbose */
  95 #define UPPC_VERBOSE_IRQ_FLAG                   0x00000001
  96 #define UPPC_VERBOSE_POWEROFF_FLAG              0x00000002
  97 #define UPPC_VERBOSE_POWEROFF_PAUSE_FLAG        0x00000004
  98 
  99 
 100 #define UPPC_VERBOSE_IRQ(fmt) \
 101         if (uppc_verbose & UPPC_VERBOSE_IRQ_FLAG) \
 102                 cmn_err fmt;
 103 
 104 #define UPPC_VERBOSE_POWEROFF(fmt) \
 105         if (uppc_verbose & UPPC_VERBOSE_POWEROFF_FLAG) \
 106                 prom_printf fmt;
 107 
 108 uchar_t uppc_reserved_irqlist[MAX_ISA_IRQ + 1];
 109 
 110 static uint16_t uppc_irq_shared_table[MAX_ISA_IRQ + 1];
 111 
 112 /*
 113  * Contains SCI irqno from FADT after initialization
 114  */
 115 static int uppc_sci = -1;
 116 
 117 /*
 118  * Local Static Data
 119  */
 120 
 121 static lock_t uppc_gethrtime_lock;
 122 static hrtime_t uppc_lasthrtime;
 123 
 124 
 125 #ifdef UPPC_DEBUG
 126 #define DENT    0x0001
 127 
 128 static int      uppc_debug = 0;
 129 
 130 
 131 #endif
 132 
 133 
 134 static struct   psm_ops uppc_ops = {
 135         uppc_probe,                             /* psm_probe            */
 136 
 137         uppc_softinit,                          /* psm_init             */
 138         uppc_picinit,                           /* psm_picinit          */
 139         uppc_intr_enter,                        /* psm_intr_enter       */
 140         uppc_intr_exit,                         /* psm_intr_exit        */
 141         uppc_setspl,                            /* psm_setspl           */
 142         uppc_addspl,                            /* psm_addspl           */
 143         uppc_delspl,                            /* psm_delspl           */
 144         (int (*)(processorid_t))NULL,           /* psm_disable_intr     */
 145         (void (*)(processorid_t))NULL,          /* psm_enable_intr      */
 146         (int (*)(int))NULL,                     /* psm_softlvl_to_irq   */
 147         (void (*)(int))NULL,                    /* psm_set_softintr     */
 148         (void (*)(processorid_t))NULL,          /* psm_set_idlecpu      */
 149         (void (*)(processorid_t))NULL,          /* psm_unset_idlecpu    */
 150 
 151         uppc_clkinit,                           /* psm_clkinit          */
 152         uppc_get_clockirq,                      /* psm_get_clockirq     */
 153         (void (*)(void))NULL,                   /* psm_hrtimeinit       */
 154         uppc_gethrtime,                         /* psm_gethrtime        */
 155 
 156         uppc_get_next_processorid,              /* psm_get_next_processorid */
 157         (int (*)(processorid_t, caddr_t))NULL,  /* psm_cpu_start        */
 158         uppc_post_cpu_start,                    /* psm_post_cpu_start   */
 159         uppc_shutdown,                          /* psm_shutdown         */
 160         (int (*)(int, int))NULL,                /* psm_get_ipivect      */
 161         (void (*)(processorid_t, int))NULL,     /* psm_send_ipi         */
 162 
 163         uppc_translate_irq,                     /* psm_translate_irq    */
 164 
 165         (void (*)(int, char *))NULL,            /* psm_notify_error     */
 166         (void (*)(int msg))NULL,                /* psm_notify_func      */
 167         (void (*)(hrtime_t time))NULL,          /* psm_timer_reprogram  */
 168         (void (*)(void))NULL,                   /* psm_timer_enable     */
 169         (void (*)(void))NULL,                   /* psm_timer_disable    */
 170         (void (*)(void *arg))NULL,              /* psm_post_cyclic_setup */
 171         uppc_preshutdown,                       /* psm_preshutdown      */
 172 
 173         (int (*)(dev_info_t *, ddi_intr_handle_impl_t *,
 174             psm_intr_op_t, int *))NULL,         /* psm_intr_ops         */
 175 
 176         uppc_state,                             /* psm_state            */
 177         (int (*)(psm_cpu_request_t *))NULL      /* psm_cpu_ops          */
 178 };
 179 
 180 
 181 static struct   psm_info uppc_info = {
 182         PSM_INFO_VER01_7,       /* version                              */
 183         PSM_OWN_SYS_DEFAULT,    /* ownership                            */
 184         (struct psm_ops *)&uppc_ops, /* operation                   */
 185         "uppc",                 /* machine name                         */
 186         "UniProcessor PC",      /* machine descriptions                 */
 187 };
 188 
 189 /*
 190  * Configuration Data
 191  */
 192 
 193 /*
 194  * This is the loadable module wrapper.
 195  */
 196 #include <sys/modctl.h>
 197 
 198 static void *uppc_hdlp;
 199 
 200 int
 201 _init(void)
 202 {
 203         return (psm_mod_init(&uppc_hdlp, &uppc_info));
 204 }
 205 
 206 int
 207 _fini(void)
 208 {
 209         return (psm_mod_fini(&uppc_hdlp, &uppc_info));
 210 }
 211 
 212 int
 213 _info(struct modinfo *modinfop)
 214 {
 215         return (psm_mod_info(&uppc_hdlp, &uppc_info, modinfop));
 216 }
 217 
 218 /*
 219  * Autoconfiguration Routines
 220  */
 221 
 222 static int
 223 uppc_probe(void)
 224 {
 225 
 226 
 227         return (PSM_SUCCESS);
 228 }
 229 
 230 static void
 231 uppc_softinit(void)
 232 {
 233         struct standard_pic *pp;
 234         int i;
 235 
 236         pp = &pics0;
 237 
 238 
 239         if (uppc_use_acpi && uppc_init_acpi()) {
 240                 build_reserved_irqlist((uchar_t *)uppc_reserved_irqlist);
 241                 for (i = 0; i <= MAX_ISA_IRQ; i++)
 242                         uppc_irq_shared_table[i] = 0;
 243                 uppc_enable_acpi = 1;
 244         }
 245 
 246         /*
 247          * initialize the ipl mask
 248          */
 249         for (i = 0; i < (MAXIPL << 1); i += 2) {
 250                 /* enable slave lines on master */
 251                 pp->c_iplmask[i] = 0xff;
 252                 pp->c_iplmask[i+1] = (0xff & ~(1 << MASTERLINE));
 253         }
 254 }
 255 
 256 /*ARGSUSED*/
 257 static int
 258 uppc_clkinit(int hertz)
 259 {
 260         ulong_t clkticks = PIT_HZ / hz;
 261 
 262         if (hertz == 0)
 263                 return (0);     /* One shot mode not supported */
 264 
 265         /*
 266          * program timer 0
 267          */
 268         outb(PITCTL_PORT, (PIT_C0|PIT_NDIVMODE|PIT_READMODE));
 269         outb(PITCTR0_PORT, (uchar_t)clkticks);
 270         outb(PITCTR0_PORT, (uchar_t)(clkticks>>8));
 271 
 272         return (NSEC_IN_SEC / hertz);
 273 }
 274 
 275 static void
 276 uppc_picinit()
 277 {
 278         picsetup();
 279 
 280         /*
 281          * If a valid SCI is present, manually addspl()
 282          * since we're not set-up early enough in boot
 283          * to do it "conventionally" (via add_avintr)
 284          */
 285         if (uppc_sci >= 0)
 286                 (void) uppc_addspl(uppc_sci, SCI_IPL, SCI_IPL, SCI_IPL);
 287 }
 288 
 289 static int
 290 uppc_post_cpu_start(void)
 291 {
 292         /*
 293          * On uppc machines psm_post_cpu_start is called during S3 resume
 294          * on the boot cpu from assembly, using the ap_mlsetup vector.
 295          */
 296 
 297         /*
 298          * Init master and slave pic
 299          */
 300         picsetup();
 301 
 302         /*
 303          * program timer 0
 304          */
 305         (void) uppc_clkinit(hz);
 306 
 307         return (PSM_SUCCESS);
 308 }
 309 
 310 /*ARGSUSED3*/
 311 static int
 312 uppc_addspl(int irqno, int ipl, int min_ipl, int max_ipl)
 313 {
 314         struct standard_pic *pp;
 315         int i;
 316         int startidx;
 317         uchar_t vectmask;
 318 
 319         if (irqno <= MAX_ISA_IRQ)
 320                 atomic_add_16(&uppc_irq_shared_table[irqno], 1);
 321 
 322         if (ipl != min_ipl)
 323                 return (0);
 324 
 325         if (irqno > 7) {
 326                 vectmask = 1 << (irqno - 8);
 327                 startidx = (ipl << 1);
 328         } else {
 329                 vectmask = 1 << irqno;
 330                 startidx = (ipl << 1) + 1;
 331         }
 332 
 333         /*
 334          * mask intr same or above ipl
 335          * level MAXIPL has all intr off as init. default
 336          */
 337         pp = &pics0;
 338         for (i = startidx; i < (MAXIPL << 1); i += 2) {
 339                 if (pp->c_iplmask[i] & vectmask)
 340                         break;
 341                 pp->c_iplmask[i] |= vectmask;
 342         }
 343 
 344         /*
 345          * unmask intr below ipl
 346          */
 347         for (i = startidx-2; i >= 0; i -= 2) {
 348                 if (!(pp->c_iplmask[i] & vectmask))
 349                         break;
 350                 pp->c_iplmask[i] &= ~vectmask;
 351         }
 352         return (0);
 353 }
 354 
 355 static int
 356 uppc_delspl(int irqno, int ipl, int min_ipl, int max_ipl)
 357 {
 358         struct standard_pic *pp;
 359         int i;
 360         uchar_t vectmask;
 361 
 362         if (irqno <= MAX_ISA_IRQ)
 363                 atomic_add_16(&uppc_irq_shared_table[irqno], -1);
 364 
 365         /*
 366          * skip if we are not deleting the last handler
 367          * and the ipl is higher than minimum
 368          */
 369         if ((max_ipl != PSM_INVALID_IPL) && (ipl >= min_ipl))
 370                 return (0);
 371 
 372         if (irqno > 7) {
 373                 vectmask = 1 << (irqno - 8);
 374                 i = 0;
 375         } else {
 376                 vectmask = 1 << irqno;
 377                 i = 1;
 378         }
 379 
 380         pp = &pics0;
 381 
 382         /*
 383          * check any handlers left for this irqno
 384          */
 385         if (max_ipl != PSM_INVALID_IPL) {
 386                 /*
 387                  * unmasks all levels below the lowest priority
 388                  */
 389                 i += ((min_ipl - 1) << 1);
 390                 for (; i >= 0; i -= 2) {
 391                         if (!(pp->c_iplmask[i] & vectmask))
 392                                 break;
 393                         pp->c_iplmask[i] &= ~vectmask;
 394                 }
 395         } else {
 396                 /*
 397                  * set mask to all levels
 398                  */
 399                 for (; i < (MAXIPL << 1); i += 2) {
 400                         if (pp->c_iplmask[i] & vectmask)
 401                                 break;
 402                         pp->c_iplmask[i] |= vectmask;
 403                 }
 404         }
 405         return (0);
 406 }
 407 
 408 static processorid_t
 409 uppc_get_next_processorid(processorid_t cpu_id)
 410 {
 411         if (cpu_id == -1)
 412                 return (0);
 413         return (-1);
 414 }
 415 
 416 /*ARGSUSED*/
 417 static int
 418 uppc_get_clockirq(int ipl)
 419 {
 420         return (CLOCK_VECTOR);
 421 }
 422 
 423 
 424 static int
 425 uppc_init_acpi(void)
 426 {
 427         int verboseflags = 0;
 428         int     sci;
 429         iflag_t sci_flags;
 430 
 431         /*
 432          * Process SCI configuration here; this may return
 433          * an error if acpi-user-options has specified
 434          * legacy mode (use ACPI without ACPI mode or SCI)
 435          */
 436         if (acpica_get_sci(&sci, &sci_flags) != AE_OK)
 437                 sci = -1;
 438 
 439         /*
 440          * Initialize sub-system - if error is returns, ACPI is not
 441          * used.
 442          */
 443         if (acpica_init() != AE_OK)
 444                 return (0);
 445 
 446         /*
 447          * uppc implies system is in PIC mode; set edge/level
 448          * via ELCR based on return value from get_sci; this
 449          * will default to level/low if no override present,
 450          * as recommended by Intel ACPI CA team.
 451          */
 452         if (sci >= 0) {
 453                 ASSERT((sci_flags.intr_el == INTR_EL_LEVEL) ||
 454                     (sci_flags.intr_el == INTR_EL_EDGE));
 455 
 456                 psm_set_elcr(sci, sci_flags.intr_el == INTR_EL_LEVEL);
 457         }
 458 
 459         /*
 460          * Remember SCI for later use
 461          */
 462         uppc_sci = sci;
 463 
 464         if (uppc_verbose & UPPC_VERBOSE_IRQ_FLAG)
 465                 verboseflags |= PSM_VERBOSE_IRQ_FLAG;
 466 
 467         if (uppc_verbose & UPPC_VERBOSE_POWEROFF_FLAG)
 468                 verboseflags |= PSM_VERBOSE_POWEROFF_FLAG;
 469 
 470         if (uppc_verbose & UPPC_VERBOSE_POWEROFF_PAUSE_FLAG)
 471                 verboseflags |= PSM_VERBOSE_POWEROFF_PAUSE_FLAG;
 472 
 473         if (acpi_psm_init(uppc_info.p_mach_idstring, verboseflags) ==
 474             ACPI_PSM_FAILURE) {
 475                 return (0);
 476         }
 477 
 478         return (1);
 479 }
 480 
 481 
 482 static void
 483 uppc_preshutdown(int cmd, int fcn)
 484 {
 485         UPPC_VERBOSE_POWEROFF(("uppc_preshutdown(%d,%d);\n", cmd, fcn));
 486 
 487 }
 488 
 489 static void
 490 uppc_shutdown(int cmd, int fcn)
 491 {
 492         UPPC_VERBOSE_POWEROFF(("uppc_shutdown(%d,%d);\n", cmd, fcn));
 493 
 494         /*
 495          * Return if passed a command other than A_SHUTDOWN or
 496          * if we're not using ACPI.
 497          */
 498         if ((cmd != A_SHUTDOWN) || (!uppc_enable_acpi))
 499                 return;
 500 
 501         /*
 502          * Switch system back into Legacy-Mode if using ACPI and
 503          * not powering-off.  Some BIOSes need to remain in ACPI-mode
 504          * for power-off to succeed (Dell Dimension 4600)
 505          */
 506         if (fcn != AD_POWEROFF) {
 507                 (void) AcpiDisable();
 508                 return;
 509         }
 510 
 511         (void) acpi_poweroff();
 512 }
 513 
 514 
 515 static int
 516 uppc_acpi_enter_picmode(void)
 517 {
 518         ACPI_OBJECT_LIST        arglist;
 519         ACPI_OBJECT             arg;
 520         ACPI_STATUS             status;
 521 
 522         /* Setup parameter object */
 523         arglist.Count = 1;
 524         arglist.Pointer = &arg;
 525         arg.Type = ACPI_TYPE_INTEGER;
 526         arg.Integer.Value = ACPI_PIC_MODE;
 527 
 528         status = AcpiEvaluateObject(NULL, "\\_PIC", &arglist, NULL);
 529         if (ACPI_FAILURE(status))
 530                 return (PSM_FAILURE);
 531         else
 532                 return (PSM_SUCCESS);
 533 }
 534 
 535 
 536 struct pic_state {
 537         int8_t          mmask;
 538         int8_t          smask;
 539         uint16_t        elcr;
 540 };
 541 
 542 
 543 static void
 544 pic_save_state(struct pic_state *sp)
 545 {
 546         struct standard_pic *pp;
 547         int     vecno;
 548 
 549         /*
 550          * Only the PIC masks and the ELCR can be saved;
 551          * other 8259 state is write-only
 552          */
 553 
 554         /*
 555          * save current master and slave interrupt mask
 556          */
 557         pp = &pics0;
 558         sp->smask = pp->c_curmask[0];
 559         sp->mmask = pp->c_curmask[1];
 560 
 561         /*
 562          * save edge/level configuration for isa interrupts
 563          */
 564         sp->elcr = 0;
 565         for (vecno = 0; vecno <= MAX_ISA_IRQ; vecno++)
 566                 sp->elcr |= psm_get_elcr(vecno) << vecno;
 567 }
 568 
 569 static void
 570 pic_restore_state(struct pic_state *sp)
 571 {
 572         int     vecno;
 573 
 574         /* Restore master and slave interrupt masks */
 575         outb(SIMR_PORT, sp->smask);
 576         outb(MIMR_PORT, sp->mmask);
 577 
 578         /* Read master to allow pics to settle */
 579         (void) inb(MIMR_PORT);
 580 
 581         /* Restore edge/level configuration for isa interupts */
 582         for (vecno = 0; vecno <= MAX_ISA_IRQ; vecno++)
 583                 psm_set_elcr(vecno, sp->elcr & (1 << vecno));
 584 
 585         /* Reenter PIC mode before restoring LNK devices */
 586         (void) uppc_acpi_enter_picmode();
 587 
 588         /* Restore ACPI link device mappings */
 589         acpi_restore_link_devices();
 590 }
 591 
 592 static int
 593 uppc_state(psm_state_request_t *rp)
 594 {
 595         switch (rp->psr_cmd) {
 596         case PSM_STATE_ALLOC:
 597                 rp->req.psm_state_req.psr_state =
 598                     kmem_zalloc(sizeof (struct pic_state), KM_NOSLEEP);
 599                 if (rp->req.psm_state_req.psr_state == NULL)
 600                         return (ENOMEM);
 601                 rp->req.psm_state_req.psr_state_size =
 602                     sizeof (struct pic_state);
 603                 return (0);
 604         case PSM_STATE_FREE:
 605                 kmem_free(rp->req.psm_state_req.psr_state,
 606                     rp->req.psm_state_req.psr_state_size);
 607                 return (0);
 608         case PSM_STATE_SAVE:
 609                 pic_save_state(rp->req.psm_state_req.psr_state);
 610                 return (0);
 611         case PSM_STATE_RESTORE:
 612                 pic_restore_state(rp->req.psm_state_req.psr_state);
 613                 return (0);
 614         default:
 615                 return (EINVAL);
 616         }
 617 }
 618 
 619 
 620 static int
 621 uppc_acpi_translate_pci_irq(dev_info_t *dip, int busid, int devid,
 622     int ipin, int *pci_irqp, iflag_t *intr_flagp)
 623 {
 624         int status;
 625         acpi_psm_lnk_t acpipsmlnk;
 626 
 627         if ((status = acpi_get_irq_cache_ent(busid, devid, ipin, pci_irqp,
 628             intr_flagp)) == ACPI_PSM_SUCCESS) {
 629                 UPPC_VERBOSE_IRQ((CE_CONT, "!uppc: Found irqno %d "
 630                     "from cache for device %s, instance #%d\n", *pci_irqp,
 631                     ddi_get_name(dip), ddi_get_instance(dip)));
 632                 return (status);
 633         }
 634 
 635         bzero(&acpipsmlnk, sizeof (acpi_psm_lnk_t));
 636 
 637         if ((status = acpi_translate_pci_irq(dip, ipin, pci_irqp,
 638             intr_flagp, &acpipsmlnk)) == ACPI_PSM_FAILURE) {
 639                 UPPC_VERBOSE_IRQ((CE_CONT, "!uppc: "
 640                     " acpi_translate_pci_irq failed for device %s, instance"
 641                     " #%d\n", ddi_get_name(dip), ddi_get_instance(dip)));
 642 
 643                 return (status);
 644         }
 645 
 646         if (status == ACPI_PSM_PARTIAL && acpipsmlnk.lnkobj != NULL) {
 647                 status = uppc_acpi_irq_configure(&acpipsmlnk, dip, pci_irqp,
 648                     intr_flagp);
 649                 if (status != ACPI_PSM_SUCCESS) {
 650                         status = acpi_get_current_irq_resource(&acpipsmlnk,
 651                             pci_irqp, intr_flagp);
 652                 }
 653         }
 654 
 655         if (status == ACPI_PSM_SUCCESS) {
 656                 acpi_new_irq_cache_ent(busid, devid, ipin, *pci_irqp,
 657                     intr_flagp, &acpipsmlnk);
 658                 psm_set_elcr(*pci_irqp, 1);     /* set IRQ to PCI mode */
 659 
 660                 UPPC_VERBOSE_IRQ((CE_CONT, "!uppc: [ACPI] "
 661                     "new irq %d for device %s, instance #%d\n",
 662                     *pci_irqp, ddi_get_name(dip), ddi_get_instance(dip)));
 663         }
 664 
 665         return (status);
 666 }
 667 
 668 /*
 669  * Configures the irq for the interrupt link device identified by
 670  * acpipsmlnkp.
 671  *
 672  * Gets the current and the list of possible irq settings for the
 673  * device. If uppc_unconditional_srs is not set, and the current
 674  * resource setting is in the list of possible irq settings,
 675  * current irq resource setting is passed to the caller.
 676  *
 677  * Otherwise, picks an irq number from the list of possible irq
 678  * settings, and sets the irq of the device to this value.
 679  * If prefer_crs is set, among a set of irq numbers in the list that have
 680  * the least number of devices sharing the interrupt, we pick current irq
 681  * resource setting if it is a member of this set.
 682  *
 683  * Passes the irq number in the value pointed to by pci_irqp, and
 684  * polarity and sensitivity in the structure pointed to by dipintrflagp
 685  * to the caller.
 686  *
 687  * Note that if setting the irq resource failed, but successfuly obtained
 688  * the current irq resource settings, passes the current irq resources
 689  * and considers it a success.
 690  *
 691  * Returns:
 692  * ACPI_PSM_SUCCESS on success.
 693  *
 694  * ACPI_PSM_FAILURE if an error occured during the configuration or
 695  * if a suitable irq was not found for this device, or if setting the
 696  * irq resource and obtaining the current resource fails.
 697  *
 698  */
 699 static int
 700 uppc_acpi_irq_configure(acpi_psm_lnk_t *acpipsmlnkp, dev_info_t *dip,
 701     int *pci_irqp, iflag_t *dipintr_flagp)
 702 {
 703         int i, min_share, foundnow, done = 0;
 704         int32_t irq;
 705         int32_t share_irq = -1;
 706         int32_t chosen_irq = -1;
 707         int cur_irq = -1;
 708         acpi_irqlist_t *irqlistp;
 709         acpi_irqlist_t *irqlistent;
 710 
 711         if ((acpi_get_possible_irq_resources(acpipsmlnkp, &irqlistp))
 712             == ACPI_PSM_FAILURE) {
 713                 UPPC_VERBOSE_IRQ((CE_WARN, "!uppc: Unable to determine "
 714                     "or assign IRQ for device %s, instance #%d: The system was "
 715                     "unable to get the list of potential IRQs from ACPI.",
 716                     ddi_get_name(dip), ddi_get_instance(dip)));
 717 
 718                 return (ACPI_PSM_FAILURE);
 719         }
 720 
 721         if ((acpi_get_current_irq_resource(acpipsmlnkp, &cur_irq,
 722             dipintr_flagp) == ACPI_PSM_SUCCESS) && (!uppc_unconditional_srs) &&
 723             (cur_irq > 0)) {
 724 
 725                 if (acpi_irqlist_find_irq(irqlistp, cur_irq, NULL)
 726                     == ACPI_PSM_SUCCESS) {
 727 
 728                         acpi_free_irqlist(irqlistp);
 729                         ASSERT(pci_irqp != NULL);
 730                         *pci_irqp = cur_irq;
 731                         return (ACPI_PSM_SUCCESS);
 732                 }
 733                 UPPC_VERBOSE_IRQ((CE_WARN, "!uppc: Could not find the "
 734                     "current irq %d for device %s, instance #%d in ACPI's "
 735                     "list of possible irqs for this device. Picking one from "
 736                     " the latter list.", cur_irq, ddi_get_name(dip),
 737                     ddi_get_instance(dip)));
 738 
 739         }
 740 
 741         irqlistent = irqlistp;
 742         min_share = 255;
 743 
 744         while (irqlistent != NULL) {
 745 
 746                 for (foundnow = 0, i = 0; i < irqlistent->num_irqs; i++) {
 747 
 748                         irq = irqlistp->irqs[i];
 749 
 750                         if ((irq > MAX_ISA_IRQ) ||
 751                             (irqlistent->intr_flags.intr_el == INTR_EL_EDGE) ||
 752                             (irq == 0))
 753                                 continue;
 754 
 755                         if (uppc_reserved_irqlist[irq])
 756                                 continue;
 757 
 758                         if (uppc_irq_shared_table[irq] == 0) {
 759                                 chosen_irq = irq;
 760                                 foundnow = 1;
 761                                 if (!(uppc_prefer_crs) || (irq == cur_irq)) {
 762                                         done = 1;
 763                                         break;
 764                                 }
 765                         }
 766 
 767                         if ((uppc_irq_shared_table[irq] < min_share) ||
 768                             ((uppc_irq_shared_table[irq] == min_share) &&
 769                             (cur_irq == irq) && (uppc_prefer_crs))) {
 770                                 min_share = uppc_irq_shared_table[irq];
 771                                 share_irq = irq;
 772                                 foundnow = 1;
 773                         }
 774                 }
 775 
 776                 /* If we found an IRQ in the inner loop, save the details */
 777                 if (foundnow && ((chosen_irq != -1) || (share_irq != -1))) {
 778                         /*
 779                          * Copy the acpi_prs_private_t and flags from this
 780                          * irq list entry, since we found an irq from this
 781                          * entry.
 782                          */
 783                         acpipsmlnkp->acpi_prs_prv = irqlistent->acpi_prs_prv;
 784                         *dipintr_flagp = irqlistent->intr_flags;
 785                 }
 786 
 787                 if (done)
 788                         break;
 789 
 790                 /* Load the next entry in the irqlist */
 791                 irqlistent = irqlistent->next;
 792         }
 793 
 794         acpi_free_irqlist(irqlistp);
 795 
 796         if (chosen_irq != -1)
 797                 irq = chosen_irq;
 798         else if (share_irq != -1)
 799                 irq = share_irq;
 800         else {
 801                 UPPC_VERBOSE_IRQ((CE_CONT, "!uppc: Could not find a "
 802                     "suitable irq from the list of possible irqs for device "
 803                     "%s, instance #%d in ACPI's list of possible\n",
 804                     ddi_get_name(dip), ddi_get_instance(dip)));
 805 
 806                 return (ACPI_PSM_FAILURE);
 807         }
 808 
 809 
 810         UPPC_VERBOSE_IRQ((CE_CONT, "!uppc: Setting irq %d for device %s "
 811             "instance #%d\n", irq, ddi_get_name(dip), ddi_get_instance(dip)));
 812 
 813         if ((acpi_set_irq_resource(acpipsmlnkp, irq)) == ACPI_PSM_SUCCESS) {
 814                 /*
 815                  * setting irq was successful, check to make sure CRS
 816                  * reflects that. If CRS does not agree with what we
 817                  * set, return the irq that was set.
 818                  */
 819 
 820                 if (acpi_get_current_irq_resource(acpipsmlnkp, &cur_irq,
 821                     dipintr_flagp) == ACPI_PSM_SUCCESS) {
 822 
 823                         if (cur_irq != irq)
 824                                 UPPC_VERBOSE_IRQ((CE_WARN, "!uppc: "
 825                                     "IRQ resource set (irqno %d) for device %s "
 826                                     "instance #%d, differs from current "
 827                                     "setting irqno %d",
 828                                     irq, ddi_get_name(dip),
 829                                     ddi_get_instance(dip), cur_irq));
 830                 }
 831                 /*
 832                  * return the irq that was set, and not what CRS reports,
 833                  * since CRS has been seen to be bogus on some systems
 834                  */
 835                 cur_irq = irq;
 836         } else {
 837                 UPPC_VERBOSE_IRQ((CE_WARN, "!uppc: set resource irq %d "
 838                     "failed for device %s instance #%d",
 839                     irq, ddi_get_name(dip), ddi_get_instance(dip)));
 840                 if (cur_irq == -1)
 841                         return (ACPI_PSM_FAILURE);
 842         }
 843 
 844         ASSERT(pci_irqp != NULL);
 845         *pci_irqp = cur_irq;
 846         return (ACPI_PSM_SUCCESS);
 847 }
 848 
 849 
 850 /*ARGSUSED*/
 851 static int
 852 uppc_translate_irq(dev_info_t *dip, int irqno)
 853 {
 854         char dev_type[16];
 855         int dev_len, pci_irq, devid, busid;
 856         ddi_acc_handle_t cfg_handle;
 857         uchar_t ipin, iline;
 858         iflag_t intr_flag;
 859 
 860         if (dip == NULL) {
 861                 UPPC_VERBOSE_IRQ((CE_CONT, "!uppc: irqno = %d"
 862                     " dip = NULL\n", irqno));
 863                 return (irqno);
 864         }
 865 
 866         if (!uppc_enable_acpi) {
 867                 return (irqno);
 868         }
 869 
 870         dev_len = sizeof (dev_type);
 871         if (ddi_getlongprop_buf(DDI_DEV_T_ANY, ddi_get_parent(dip),
 872             DDI_PROP_DONTPASS, "device_type", (caddr_t)dev_type,
 873             &dev_len) != DDI_PROP_SUCCESS) {
 874                 UPPC_VERBOSE_IRQ((CE_CONT, "!uppc: irqno %d"
 875                     "device %s instance %d no device_type\n", irqno,
 876                     ddi_get_name(dip), ddi_get_instance(dip)));
 877                 return (irqno);
 878         }
 879 
 880         if ((strcmp(dev_type, "pci") == 0) ||
 881             (strcmp(dev_type, "pciex") == 0)) {
 882 
 883                 /* pci device */
 884                 if (acpica_get_bdf(dip, &busid, &devid, NULL) != 0)
 885                         return (irqno);
 886 
 887                 if (pci_config_setup(dip, &cfg_handle) != DDI_SUCCESS)
 888                         return (irqno);
 889 
 890                 ipin = pci_config_get8(cfg_handle, PCI_CONF_IPIN) - PCI_INTA;
 891                 iline = pci_config_get8(cfg_handle, PCI_CONF_ILINE);
 892                 if (uppc_acpi_translate_pci_irq(dip, busid, devid,
 893                     ipin, &pci_irq, &intr_flag) == ACPI_PSM_SUCCESS) {
 894 
 895                         UPPC_VERBOSE_IRQ((CE_CONT, "!uppc: [ACPI] new irq "
 896                             "%d old irq %d device %s, instance %d\n", pci_irq,
 897                             irqno, ddi_get_name(dip), ddi_get_instance(dip)));
 898 
 899                         /*
 900                          * Make sure pci_irq is within range.
 901                          * Otherwise, fall through and return irqno.
 902                          */
 903                         if (pci_irq <= MAX_ISA_IRQ) {
 904                                 if (iline != pci_irq) {
 905                                         /*
 906                                          * Update the device's ILINE byte,
 907                                          * in case uppc_acpi_translate_pci_irq
 908                                          * has choosen a different pci_irq
 909                                          * than the BIOS has configured.
 910                                          * Some chipsets use the value in
 911                                          * ILINE to control interrupt routing,
 912                                          * in conflict with the PCI spec.
 913                                          */
 914                                         pci_config_put8(cfg_handle,
 915                                             PCI_CONF_ILINE, pci_irq);
 916                                 }
 917                                 pci_config_teardown(&cfg_handle);
 918                                 return (pci_irq);
 919                         }
 920                 }
 921                 pci_config_teardown(&cfg_handle);
 922 
 923                 /* FALLTHRU to common case - returning irqno */
 924         } else {
 925                 /* non-PCI; assumes ISA-style edge-triggered */
 926                 psm_set_elcr(irqno, 0);         /* set IRQ to ISA mode */
 927 
 928                 UPPC_VERBOSE_IRQ((CE_CONT, "!uppc: non-pci,"
 929                     "irqno %d device %s instance %d\n", irqno,
 930                     ddi_get_name(dip), ddi_get_instance(dip)));
 931         }
 932 
 933         return (irqno);
 934 }
 935 
 936 /*
 937  * uppc_intr_enter() raises the ipl to the level of the current interrupt,
 938  * and sends EOI to the pics.
 939  * If interrupt is 7 or 15 and not spurious interrupt, send specific EOI
 940  * else send non-specific EOI
 941  * uppc_intr_enter() returns the new priority level,
 942  * or -1 for spurious interrupt
 943  */
 944 static int
 945 uppc_intr_enter(int ipl, int *vector)
 946 {
 947         int newipl;
 948         int intno;
 949 
 950         intno = (*vector);
 951 
 952         ASSERT(intno < 256);
 953 
 954         newipl = autovect[intno].avh_hi_pri;
 955 
 956         /*
 957          * During wait_till_seen() periods when interrupt vector is being
 958          * removed in remove_av(), the removed hardware interrupt could
 959          * trigger and got here with newipl 0.  It has to send EOI
 960          * as usual but no need to call setspl and returns -1 like spurious.
 961          */
 962         if ((intno & 7) != 7) {
 963                 if (newipl)
 964                         uppc_setspl(newipl);
 965                 outb(MCMD_PORT, PIC_NSEOI);
 966                 if (intno >= 8) {
 967                         outb(SCMD_PORT, PIC_NSEOI);
 968                 }
 969         } else { /* int was 7 or 15 */
 970                 if (newipl && newipl <= ipl) { /* Check for spurious int */
 971                         if (intno != 7)
 972                                 outb(MCMD_PORT, PIC_NSEOI);
 973                         return (-1); /* Spurious int */
 974                 } else {
 975                         if (newipl)
 976                                 uppc_setspl(newipl);
 977                         if (intno != 7) {
 978                                 outb(MCMD_PORT, PIC_NSEOI);
 979                                 outb(SCMD_PORT, PIC_SEOI_LVL7);
 980                         } else  {
 981                                 outb(MCMD_PORT, PIC_SEOI_LVL7);
 982                         }
 983                 }
 984         }
 985 
 986         if (newipl)
 987                 return (newipl);
 988         else
 989                 return (-1); /* not real spurious int */
 990 }
 991 
 992 /*
 993  * uppc_intr_exit() restores the old interrupt
 994  * priority level after processing an interrupt.
 995  * It is called with interrupts disabled, and does not enable interrupts.
 996  */
 997 /* ARGSUSED */
 998 static void
 999 uppc_intr_exit(int ipl, int vector)
1000 {
1001         uppc_setspl(ipl);
1002 }
1003 
1004 /*
1005  * uppc_setspl() loads new interrupt masks into the pics
1006  * based on input ipl.
1007  */
1008 /* ARGSUSED */
1009 static void
1010 uppc_setspl(int ipl)
1011 {
1012         struct standard_pic *pp;
1013         uint8_t smask, mmask;
1014         uint8_t cursmask, curmmask;
1015 
1016         pp = &pics0;
1017         smask = pp->c_iplmask[ipl * 2];
1018         mmask = pp->c_iplmask[ipl * 2 + 1];
1019         cursmask = pp->c_curmask[0];
1020         curmmask = pp->c_curmask[1];
1021         if (cursmask == smask && curmmask == mmask)
1022                 return;
1023         pp->c_curmask[0] = smask;
1024         pp->c_curmask[1] = mmask;
1025 
1026         if (cursmask != smask) {
1027                 /*
1028                  * program new slave pic mask
1029                  */
1030                 outb(SIMR_PORT, smask);
1031         }
1032         if (curmmask != mmask) {
1033                 /*
1034                  * program new master pic mask
1035                  */
1036                 outb(MIMR_PORT, mmask);
1037         }
1038         /*
1039          * read master to allow pics to settle
1040          */
1041         (void) inb(MIMR_PORT);
1042 }
1043 
1044 /*
1045  * uppc_gethrtime() returns high resolution timer value
1046  */
1047 static hrtime_t
1048 uppc_gethrtime()
1049 {
1050         hrtime_t timeval, temp;
1051         unsigned int ctr0;
1052         ulong_t oflags;
1053 
1054         oflags = intr_clear(); /* disable ints */
1055         lock_set(&uppc_gethrtime_lock);
1056 retry:
1057         temp = hrtime_base;
1058         outb(PITCTL_PORT, 0);   /* latch counter 0 */
1059         /*
1060          * read counter 0
1061          */
1062         ctr0 = inb(PITCTR0_PORT);
1063         ctr0 |= inb(PITCTR0_PORT) << 8;
1064         timeval = (hrtime_t)ctr0 * (NANOSEC / PIT_HZ);
1065         if (temp != hrtime_base)
1066                 goto retry;
1067         timeval -= temp;
1068         if (timeval < uppc_lasthrtime)
1069                 timeval = uppc_lasthrtime;
1070         uppc_lasthrtime = timeval;
1071         lock_clear(&uppc_gethrtime_lock);
1072         intr_restore(oflags);
1073         return (timeval);
1074 }