psm_common.c revision 0f2cfdbb2dd724b1f649ce31d87b7964153dc0bc
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <sys/ddi_impldefs.h>
#include <sys/psm_common.h>
/* Global configurables */
char *psm_module_name; /* used to store name of psm module */
/*
* acpi_irq_check_elcr: when set elcr will also be consulted for building
* the reserved irq list. When 0 (false), the existing state of the ELCR
* is ignored when selecting a vector during IRQ translation, and the ELCR
* is programmed to the proper setting for the type of bus (level-triggered
* for PCI, edge-triggered for non-PCI). When non-zero (true), vectors
* set to edge-mode will not be used when in PIC-mode. The default value
* is 0 (false). Note that ACPI's SCI vector is always set to conform to
* ACPI-specification regardless of this.
*
*/
int acpi_irq_check_elcr = 0;
int psm_verbose = 0;
#define PSM_VERBOSE_IRQ(fmt) \
if (psm_verbose & PSM_VERBOSE_IRQ_FLAG) \
#define PSM_VERBOSE_POWEROFF(fmt) \
if (psm_verbose & PSM_VERBOSE_POWEROFF_FLAG || \
#define PSM_VERBOSE_POWEROFF_PAUSE(fmt) \
if (psm_verbose & PSM_VERBOSE_POWEROFF_FLAG || \
prom_printf fmt; \
(void) goany(); \
}
/* Local storage */
static kmutex_t acpi_irq_cache_mutex;
/*
* irq_cache_table is a list that serves a two-key cache. It is used
* interrupt lnk <-> irq cache.
*/
static irq_cache_t *irq_cache_table;
#define IRQ_CACHE_INITLEN 20
static int irq_cache_len = 0;
static int irq_cache_valid = 0;
extern int goany(void);
#define NEXT_PRT_ITEM(p) \
(ACPI_PCI_ROUTING_TABLE *)(((char *)(p)) + (p)->Length)
static int
{
int status;
int dev_adr;
/*
* Get the IRQ routing table
*/
return (ACPI_PSM_FAILURE);
}
continue;
/* NULL Source name means index is GSIV */
} else
break;
}
return (status);
}
/*
*
* If the interrupt link device is already configured,
* stores polarity and sensitivity in the structure pointed to by
* intr_flagp, and irqno in the value pointed to by pci_irqp.
*
* Returns:
* ACPI_PSM_SUCCESS if the interrupt link device is already configured.
* ACPI_PSM_PARTIAL if configuration is needed.
* ACPI_PSM_FAILURE in case of error.
*
* When two devices share the same interrupt link device, and the
* link device is already configured (i.e. found in the irq cache)
* we need to use the already configured irq instead of reconfiguring
* the link device.
*/
static int
{
int status;
/*
* Convert the passed-in link device name to a handle
*/
return (ACPI_PSM_FAILURE);
}
/*
* Assume that the link device is invalid if no _CRS method
* exists, since _CRS method is a required method
*/
return (ACPI_PSM_FAILURE);
}
" for device %s, instance #%d, irq no %d\n",
return (ACPI_PSM_SUCCESS);
} else {
}
return (ACPI_PSM_PARTIAL);
}
}
int
{
return (ACPI_PSM_FAILURE);
}
return (ACPI_PSM_SUCCESS);
}
/*
*/
int
{
int len;
return (-1);
if (len < (sizeof (pci_regspec_t) / sizeof (int))) {
return (-1);
}
return (0);
}
/*
* Build the reserved ISA irq list, and store it in the table pointed to by
* reserved_irqs_table. The caller is responsible for allocating this table
* with a minimum of MAX_ISA_IRQ + 1 entries.
*
* The routine looks in the device tree at the subtree rooted at /isa
* for each of the devices under that node, if an interrupts property
* is present, its values are used to "reserve" irqs so that later ACPI
* configuration won't choose those irqs.
*
* In addition, if acpi_irq_check_elcr is set, will use ELCR register
* to identify reserved IRQs.
*/
void
{
dev_info_t *isa_child = 0;
int i;
/* Initialize the reserved ISA IRQs: */
for (i = 0; i <= MAX_ISA_IRQ; i++)
reserved_irqs_table[i] = 0;
if (acpi_irq_check_elcr) {
/* valid ELCR */
for (i = 0; i <= MAX_ISA_IRQ; i++)
if (!ELCR_LEVEL(elcrval, i))
reserved_irqs_table[i] = 1;
}
}
/* always check the isa devinfo nodes */
if (isanode != 0) { /* Found ISA */
int *intrs; /* Interrupt values */
/* Load first child: */
while (isa_child != 0) { /* Iterate over /isa children */
/* if child has any interrupts, save them */
== DDI_PROP_SUCCESS) {
/*
* iterate over child interrupt list, adding
* them to the reserved irq list
*/
while (intcnt-- > 0) {
/*
* Each value MUST be <= MAX_ISA_IRQ
*/
continue;
}
}
}
/* The isa node was held by ddi_find_devinfo, so release it */
}
/*
* Reserve IRQ14 & IRQ15 for IDE. It shouldn't be hard-coded
* here but there's no other way to find the irqs for
* legacy-mode ata (since it's hard-coded in pci-ide also).
*/
}
/*
* Examine devinfo node to determine if it is a PCI-PCI bridge
*
* Returns:
* 0 if not a bridge or error
* 1 if a bridge
*/
static int
{
int rv = 0;
PCI_CONF_SUBCLASS) == PCI_BRIDGE_PCI));
}
return (rv);
}
/*
* Examines ACPI node for presence of _PRT object
*
* Returns:
* 0 if no _PRT or error
* 1 if _PRT is present
*/
static int
{
}
/*
* Look first for an ACPI PCI bus node matching busid, then for a _PRT on the
* parent node; then drop into the bridge-chasing code (which will also
* look for _PRTs on the way up the tree of bridges)
*
* Stores polarity and sensitivity in the structure pointed to by
* intr_flagp, and irqno in the value pointed to by pci_irqp. *
* Returns:
* ACPI_PSM_SUCCESS on success.
* ACPI_PSM_PARTIAL to indicate need to configure the interrupt
* link device.
* ACPI_PSM_FAILURE if an error prevented the system from
* obtaining irq information for dip.
*/
int
{
while (curdip != ddi_root_node()) {
break;
}
}
/* if we got here, we need to traverse a bridge upwards */
if (!psm_is_pci_bridge(parentdip))
break;
/*
* This is the rotating scheme that Compaq is using
* and documented in the PCI-PCI spec. Also, if the
* PCI-PCI bridge is behind another PCI-PCI bridge,
* then it needs to keep ascending until an interrupt
* entry is found or the top is reached
*/
}
/*
* We should never, ever get here; didn't find a _PRT
*/
return (ACPI_PSM_FAILURE);
}
/*
* Sets the irq resource of the lnk object to the requested irq value.
*
* Returns ACPI_PSM_SUCCESS on success, ACPI_PSM_FAILURE upon failure.
*/
int
{
/*
* Fetch the possible resources for the link
*/
return (ACPI_PSM_FAILURE);
}
/*
* Find an IRQ resource descriptor to use as template
*/
/*
* Allocate enough room for this resource entry
* and one end tag following it
*/
break; /* drop out of the loop */
}
}
/*
* We're done with the PRS values, toss 'em lest we forget
*/
return (ACPI_PSM_FAILURE);
/*
* The Interrupts[] array is always at least one entry
* long; see the definition of ACPI_RESOURCE.
*/
case ACPI_RESOURCE_TYPE_IRQ:
break;
break;
}
return (ACPI_PSM_FAILURE);
}
return (ACPI_PSM_SUCCESS);
} else
return (ACPI_PSM_FAILURE);
}
/*
*
*/
static int
{
switch (el) {
case ACPI_EDGE_SENSITIVE:
return (INTR_EL_EDGE);
case ACPI_LEVEL_SENSITIVE:
return (INTR_EL_LEVEL);
default:
/* el is a single bit; should never reach here */
return (INTR_EL_CONFORM);
}
}
/*
*
*/
static int
{
switch (po) {
case ACPI_ACTIVE_HIGH:
return (INTR_PO_ACTIVE_HIGH);
case ACPI_ACTIVE_LOW:
return (INTR_PO_ACTIVE_LOW);
default:
/* po is a single bit; should never reach here */
return (INTR_PO_CONFORM);
}
}
/*
* Retrieves the current irq setting for the interrrupt link device.
*
* Stores polarity and sensitivity in the structure pointed to by
* intr_flagp, and irqno in the value pointed to by pci_irqp.
*
* Returns ACPI_PSM_SUCCESS on success, ACPI_PSM_FAILURE upon failure.
*/
int
{
int irq;
int status = ACPI_PSM_FAILURE;
"present or disabled, status 0x%x",
return (ACPI_PSM_FAILURE);
}
" evaluation failed"));
return (ACPI_PSM_FAILURE);
}
irq = -1;
if (irq > 0) {
" from _CRS "));
break;
}
" from _CRS "));
break;
}
if (irq > 0) {
" from _CRS "));
break;
}
" from _CRS "));
break;
}
}
}
if (status == ACPI_PSM_SUCCESS) {
}
return (status);
}
/*
* Searches for the given IRQ in the irqlist passed in.
*
* If multiple matches exist, this returns true on the first match.
* Returns the interrupt flags, if a match was found, in `intr_flagp' if
* it's passed in non-NULL
*/
int
{
int found = 0;
int i;
if (intr_flagp)
found = 1;
break; /* out of for() */
}
}
}
}
/*
* Frees the irqlist allocated by acpi_get_possible_irq_resource.
* It takes a count of number of entries in the list.
*/
void
{
/* Free the irq list */
sizeof (int32_t));
}
}
/*
* Creates a new entry in the given irqlist with the information passed in.
*/
static void
{
}
/*
* Retrieves a list of possible interrupt settings for the interrupt link
* device.
*
* Stores polarity and sensitivity in the structure pointed to by intr_flagp.
* Updates value pointed to by irqlistp with the address of a table it
* allocates. where interrupt numbers are stored. Stores the number of entries
* in this table in the value pointed to by num_entriesp;
*
* Each element in this table is of type int32_t. The table should be later
* freed by caller via acpi_free_irq_list().
*
* Returns ACPI_PSM_SUCCESS on success and ACPI_PSM_FAILURE upon failure
*/
int
{
int status;
void *tmplist;
return (ACPI_PSM_FAILURE);
}
/*
* Scan the resources looking for an interrupt resource
*/
*irqlistp = 0;
case ACPI_RESOURCE_TYPE_IRQ:
break;
break;
default:
continue;
}
return (ACPI_PSM_FAILURE);
}
/* NEEDSWORK: move this into add_irqlist_entry someday */
KM_SLEEP);
for (i = 0; i < irqlist_len; i++)
else
&intr_flags);
}
}
/*
* Adds a new cache entry to the irq cache which maps an irq and
* interrupt link device object.
*/
void
{
int newsize;
if (irq_cache_valid >= irq_cache_len) {
/* initially, or re-, allocate array */
newsize = (irq_cache_len ?
if (irq_cache_len != 0) {
/* realloc: copy data, free old */
irq_cache_len * sizeof (irq_cache_t));
irq_cache_len * sizeof (irq_cache_t));
}
}
}
/*
*
* If info is found, stores polarity and sensitivity in the structure
* pointed to by intr_flagp, and irqno in the value pointed to by pci_irqp,
* and returns ACPI_PSM_SUCCESS.
* Otherwise, ACPI_PSM_FAILURE is returned.
*/
int
{
int i;
int ret = ACPI_PSM_FAILURE;
irqcachep++, i++)
break;
}
return (ret);
}
/*
* Searches the irq caches for the given interrupt lnk device object.
*
* If info is found, stores polarity and sensitivity in the structure
* pointed to by intr_flagp, and irqno in the value pointed to by pci_irqp,
* and returns ACPI_PSM_SUCCESS.
* Otherwise, ACPI_PSM_FAILURE is returned.
*/
int
{
int i;
int ret = ACPI_PSM_FAILURE;
return (ACPI_PSM_FAILURE);
irqcachep++, i++)
break;
}
return (ret);
}
int
acpi_poweroff(void)
{
extern int acpica_powering_off;
PSM_VERBOSE_POWEROFF(("acpi_poweroff: starting poweroff\n"));
acpica_powering_off = 1;
return (1);
return (1);
}
/* we should be off; if we get here it's an error */
PSM_VERBOSE_POWEROFF(("acpi_poweroff: failed to actually power off\n"));
return (1);
}
/*
* psm_set_elcr() sets ELCR bit for specified vector
*/
void
{
if (val) {
/* set bit to force level-triggered mode */
} else {
/* clear bit to force edge-triggered mode */
}
}
/*
* psm_get_elcr() returns status of ELCR bit for specific vector
*/
int
psm_get_elcr(int vecno)
{
}