/*
* 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 (c) 2009-2010, Intel Corporation.
* All rights reserved.
*/
/*
* Platform specific device enumerator for ACPI specific devices.
* "x86 system devices" refers to the suite of hardware components which are
* common to the x86 platform and play important roles in the system
* architecture but can't be enumerated/discovered through industry-standard
* bus specifications. Examples of these x86 system devices include:
* * Memory device
* * Non-PCI discoverable IOMMU or DMA Remapping Engine
* * Non-PCI discoverable IOxAPIC
* * Non-PCI discoverable HPET (High Precision Event Timer)
* * ACPI defined devices, including power button, sleep button, battery etc.
*
* as SMBIOS tables, MPS tables and ACPI tables since their discovery isn't
* covered by any industry-standard bus specifications.
*
* In order to aid Solaris in flexibly managing x86 system devices,
* x86 system devices are placed into a specific firmware device
*
* This driver populates the firmware device subtree with ACPI-discoverable
* system devices if possible. To achieve that, the ACPI object
* namespace is abstracted as ACPI virtual buses which host system devices.
* Another nexus driver for the ACPI virtual bus will manage all devices
* connected to it.
*
* For more detailed information, please refer to PSARC/2009/104.
*/
#include <sys/ddi_subrdefs.h>
#include <sys/acpidev_dr.h>
#include <sys/acpidev_impl.h>
int acpidev_options = 0;
int acpidev_debug = 0;
/* ACPI device autoconfig global status */
typedef enum acpidev_status {
/* Boot time ACPI device enumerator. */
static void acpidev_boot_probe(int type);
/* DDI module auto configuration interface */
extern struct mod_ops mod_miscops;
"ACPI device enumerator"
};
(void *)&modlmisc,
};
int
_init(void)
{
int err;
sizeof (acpidev_object_type_mask));
} else {
}
return (err);
}
int
_fini(void)
{
/* No support for module unload. */
return (EBUSY);
}
int
{
}
/* Check blacklists and load platform specific driver modules. */
static ACPI_STATUS
{
return (AE_OK);
}
/* Unload platform specific driver modules. */
static void
{
}
/* Unregister all device class drivers from the device driver lists. */
static void
acpidev_class_list_fini(void)
{
if ((acpidev_options & ACPIDEV_OUSER_NO_PCI) == 0) {
}
if ((acpidev_options & ACPIDEV_OUSER_NO_MEM) == 0) {
}
if (acpidev_options & ACPIDEV_OUSER_NO_CPU) {
}
if ((acpidev_options & ACPIDEV_OUSER_NO_CONTAINER) == 0) {
}
}
/* Register all device class drivers onto the driver lists. */
static ACPI_STATUS
{
/* Set bit in mask for supported object types. */
/*
* Register the ACPI scope class driver onto the class driver lists.
* Currently only ACPI scope objects under ACPI root node, such as _PR,
* _SB, _TZ etc, need to be handled, so only register the scope class
* driver onto the root list.
*/
&acpidev_class_scope, B_FALSE))) {
goto error_out;
}
/*
* Register the ACPI device class driver onto the class driver lists.
* The ACPI device class driver should be registered at the tail to
* handle all device objects which haven't been handled by other
*/
&acpidev_class_device, B_TRUE))) {
goto error_root_device;
}
&acpidev_class_device, B_TRUE))) {
goto error_device_device;
}
/* Check and register support for ACPI container device. */
if ((acpidev_options & ACPIDEV_OUSER_NO_CONTAINER) == 0) {
B_FALSE))) {
goto error_device_container;
}
*fp |= ACPI_DEVCFG_CONTAINER;
}
/* Check and register support for ACPI CPU device. */
if ((acpidev_options & ACPIDEV_OUSER_NO_CPU) == 0) {
/* Handle ACPI CPU Device */
goto error_device_cpu;
}
/* Handle ACPI Processor under _PR */
goto error_scope_cpu;
}
/* House-keeping for CPU scan */
goto error_root_cpu;
}
*fp |= ACPI_DEVCFG_CPU;
}
/* Check support of ACPI memory devices. */
if ((acpidev_options & ACPIDEV_OUSER_NO_MEM) == 0) {
/*
* Register the ACPI memory class driver onto the
* acpidev_class_list_device list because ACPI module
* class driver uses that list.
*/
B_FALSE))) {
goto error_device_memory;
}
*fp |= ACPI_DEVCFG_MEMORY;
}
if ((acpidev_options & ACPIDEV_OUSER_NO_PCI) == 0) {
/*
* the acpidev_class_list_device class list because ACPI
* module class driver uses that list.
*/
B_FALSE))) {
goto error_device_pci;
}
/*
* acpidev_class_list_scope class list.
*/
B_FALSE))) {
goto error_scope_pci;
}
*fp |= ACPI_DEVCFG_PCI;
}
/* Check blacklist and load platform specific modules. */
if (ACPI_FAILURE(rc)) {
"or load pratform modules.");
goto error_plat;
}
return (AE_OK);
if ((acpidev_options & ACPIDEV_OUSER_NO_PCI) == 0) {
}
if ((acpidev_options & ACPIDEV_OUSER_NO_PCI) == 0) {
}
if ((acpidev_options & ACPIDEV_OUSER_NO_MEM) == 0) {
}
if (acpidev_options & ACPIDEV_OUSER_NO_CPU) {
}
if (acpidev_options & ACPIDEV_OUSER_NO_CPU) {
}
if (acpidev_options & ACPIDEV_OUSER_NO_CPU) {
}
if ((acpidev_options & ACPIDEV_OUSER_NO_CONTAINER) == 0) {
}
"!acpidev: failed to register built-in class drivers.");
*fp = 0;
return (AE_ERROR);
}
/*
* Called in single threaded context during boot, no protection for
* reentrance.
*/
static ACPI_STATUS
acpidev_create_root_node(void)
{
char *compatibles[] = {
};
/* Query whether device node already exists. */
"disable driver.", ACPIDEV_NODE_NAME_ROOT);
return (AE_ALREADY_EXISTS);
}
/* Create the device node if it doesn't exist. */
if (rv != NDI_SUCCESS) {
"for ACPI root with errcode %d.", rv);
return (AE_ERROR);
}
/* Build cross reference between dip and ACPI object. */
(void) ddi_remove_child(dip, 0);
return (AE_ERROR);
}
/* Set device properties. */
if (rv == NDI_SUCCESS) {
}
if (rv != DDI_SUCCESS) {
"!acpidev: failed to set device property for /devices/%s.",
goto error_out;
}
/* Manually create an object handle for the root node */
"handle for the root node.");
goto error_out;
}
/* Bind device driver. */
(void) ndi_devi_bind_driver(dip, 0);
return (AE_OK);
(void) ddi_remove_child(dip, 0);
return (AE_ERROR);
}
static void
acpidev_initialize(void)
{
int rc;
/* Check whether it has already been initialized. */
if (acpidev_status == ACPIDEV_STATUS_DISABLED) {
"disabled by user.\n");
return;
} else if (acpidev_status != ACPIDEV_STATUS_UNKNOWN) {
"!acpidev: initialization called more than once.");
return;
}
/* Check whether ACPI device autoconfig has been disabled by user. */
if (rc == DDI_SUCCESS) {
"disabled by user.\n");
return;
}
}
/* Initialize acpica subsystem. */
if (ACPI_FAILURE(acpica_init())) {
"!acpidev: failed to initialize acpica subsystem.");
return;
}
/* Check ACPICA subsystem status. */
"initialized, ACPI device autoconfig will be disabled.");
return;
}
/* Converts acpidev-options from type string to int, if any */
long data;
if (rc == 0) {
(void) e_ddi_prop_remove(DDI_DEV_T_NONE,
ddi_root_node(), "acpidev-options");
(void) e_ddi_prop_update_int(DDI_DEV_T_NONE,
}
}
/* Get acpidev_options user options. */
/* Check whether ACPI based DR has been disabled by user. */
if (rc == DDI_SUCCESS) {
"disabled by user.\n");
acpidev_dr_enable = 0;
}
}
/* Register all device class drivers. */
"!acpidev: failed to initalize class driver lists.");
return;
}
if (ACPI_FAILURE(acpidev_create_root_node())) {
"for acpi device tree.");
return;
}
/* Notify acpica to enable ACPI device auto configuration. */
}
/*
* Probe devices in ACPI namespace which can't be enumerated by other methods
* at boot time.
*/
static ACPI_STATUS
{
"object in acpi_boot_probe_device().");
return (AE_ERROR);
}
/* Enumerate ACPI devices. */
if (ACPI_FAILURE(rc)) {
"under ACPI root node.");
}
return (rc);
}
/*
* Platform specific device prober for ACPI virtual bus.
* It will be called in single-threaded environment to enumerate devices in
* ACPI namespace at boot time.
*/
static void
{
/* Initialize subsystem on first pass. */
if (type == 0) {
if (acpidev_status != ACPIDEV_STATUS_INITIALIZED &&
"initalization failure.");
}
}
/* Probe ACPI devices */
if (ACPI_SUCCESS(rc)) {
/*
* Support of DR operations will be disabled
* if failed to initialize DR subsystem.
*/
"initialize DR subsystem.");
}
} else {
"devices during boot.");
}
if (ACPI_SUCCESS(rc)) {
} else {
"ACPI devices during boot.");
}
} else if (acpidev_status != ACPIDEV_STATUS_FAILED &&
"!acpidev: invalid ACPI device autoconfig global status.");
}
}
{
int circ;
/* Validate parameter first. */
"!acpidev: infop is NULL in acpidev_probe_child().");
return (AE_BAD_PARAMETER);
}
"in acpidev_probe_child().");
return (AE_BAD_PARAMETER);
}
"acpidev_probe_child().");
return (AE_BAD_PARAMETER);
}
"!acpidev: pdip is NULL in acpidev_probe_child().");
return (AE_BAD_PARAMETER);
}
/* Call pre-probe callback functions. */
continue;
}
"device of type %s under %s.",
}
}
/* Walk child objects. */
/* Skip object if we're not interested in it. */
continue;
}
/* It's another hotplug-capable board, skip it. */
continue;
}
/* Allocate the walk info structure. */
"walk info child object of %s.",
/* Mark error and continue to handle next child. */
continue;
}
/*
* Remember the class list used to handle this object.
* It should be the same list for different passes of scans.
*/
}
/* Call registered process callbacks. */
continue;
}
if (ACPI_FAILURE(res)) {
"process object of type %s under %s.",
}
}
/* Free resources. */
}
/* Call post-probe callback functions. */
continue;
}
"device of type %s under %s.",
}
}
return (rc);
}
{
char *devname;
/* Validate parameters first. */
"!acpidev: infop is NULL in acpidev_process_object().");
return (AE_BAD_PARAMETER);
}
"acpidev_process_object().");
return (AE_BAD_PARAMETER);
}
return (AE_BAD_PARAMETER);
}
/*
* Check whether the object has already been handled.
* Tag and child dip pointer are used to indicate the object has been
* handled by the ACPI auto configure driver. It has the
* following usages:
* 1) Prevent creating dip for objects which already have a dip
* when reloading the ACPI auto configure driver.
* 2) Prevent creating multiple dips for ACPI objects with ACPI
* aliases. Currently ACPICA framework has no way to tell whether
* an object is an alias or not for some types of object. So tag
* is used to indicate that the object has been handled.
* 3) Prevent multiple class drivers from creating multiple devices for
* the same ACPI object.
*/
if ((flags & ACPIDEV_PROCESS_FLAG_CREATE) &&
(flags & ACPIDEV_PROCESS_FLAG_CHECK) &&
"!acpidev: device has already been created for object %s.",
return (AE_ALREADY_EXISTS);
}
/*
* Determine action according to following rules based on device
* status returned by _STA method. Please refer to ACPI3.0b section
* 6.3.1 and 6.5.1.
* present functioning enabled Action
* 0 0 x Do nothing
* 1 x 0 Do nothing
* 1 x 1 Create node and scan child
* x 1 0 Do nothing
* x 1 1 Create node and scan child
*/
} else {
}
}
/*
* Need to scan for hotplug-capable boards even if object
* doesn't exist or has been disabled during the first pass.
* So just disable creating device node and keep on scanning.
*/
} else {
return (AE_NOT_EXIST);
}
}
if (flags & ACPIDEV_PROCESS_FLAG_CREATE) {
/*
* Put the device into offline state if its parent is in
* offline state.
*/
if (DEVI_IS_DEVICE_OFFLINE(pdip)) {
}
}
/* Evaluate filtering rules and generate device name. */
if (flags & ACPIDEV_PROCESS_FLAG_CREATE) {
} else {
}
/* Create device if requested. */
if ((flags & ACPIDEV_PROCESS_FLAG_CREATE) &&
int ret;
/*
* Allocate dip and set default properties.
* Properties can be overriden in class specific init routines.
*/
&dip);
if (ret != NDI_SUCCESS) {
"!acpidev: failed to set device property for %s.",
(void) ddi_remove_child(dip, 0);
return (AE_ERROR);
}
/* Build cross reference between dip and ACPI object. */
if ((flags & ACPIDEV_PROCESS_FLAG_NOTAG) == 0 &&
"!acpidev: failed to tag object %s.",
(void) ddi_remove_child(dip, 0);
return (AE_ERROR);
}
/* Call class specific initialization callback. */
"!acpidev: failed to initialize device %s.",
if ((flags & ACPIDEV_PROCESS_FLAG_NOTAG) == 0) {
}
(void) ddi_remove_child(dip, 0);
return (AE_ERROR);
}
/* Set device into offline state if requested. */
if (flags & ACPIDEV_PROCESS_FLAG_OFFLINE) {
}
/* Mark status */
/* Hold reference count on class driver. */
if ((flags & ACPIDEV_PROCESS_FLAG_NOTAG) == 0) {
}
/* Bind device driver. */
if ((flags & ACPIDEV_PROCESS_FLAG_NOBIND) != 0) {
} else {
(void) ndi_devi_bind_driver(dip, 0);
}
/* Hold reference on branch when hot-adding devices. */
if (flags & ACPIDEV_PROCESS_FLAG_HOLDBRANCH) {
}
}
/* Free resources */
/* Recursively scan child objects if requested. */
switch (res) {
case ACPIDEV_FILTER_DEFAULT:
/* FALLTHROUGH */
case ACPIDEV_FILTER_SCAN:
/* Check if we need to scan child. */
if ((flags & ACPIDEV_PROCESS_FLAG_SCAN) &&
/* probe child object. */
if (ACPI_FAILURE(rc)) {
"!acpidev: failed to probe subtree of %s.",
}
/* Mark object as scanned. */
}
break;
case ACPIDEV_FILTER_CREATE:
/* FALLTHROUGH */
case ACPIDEV_FILTER_CONTINUE:
/* FALLTHROUGH */
case ACPIDEV_FILTER_SKIP:
break;
case ACPIDEV_FILTER_FAILED:
"!acpidev: failed to probe device for %s.",
break;
default:
"!acpidev: unknown filter result code %d.", res);
break;
}
return (rc);
}
{
return (ACPIDEV_FILTER_CONTINUE);
return (ACPIDEV_FILTER_CONTINUE);
}
}
return (afrp->adf_retcode);
}
{
/* Evaluate filtering rules. */
} else {
}
if (res == ACPIDEV_FILTER_DEFAULT ||
res == ACPIDEV_FILTER_SCAN) {
break;
}
}
return (res);
}
acpidev_root_node(void)
{
return (acpidev_root_dip);
}
{
"!acpidev: invalid parameter in acpidev_register_class().");
return (AE_BAD_PARAMETER);
"!acpidev: class driver %s version mismatch.",
return (AE_BAD_DATA);
}
/* Check for duplicated item. */
"!acpidev: register duplicate class driver %s.",
break;
}
}
if (ACPI_SUCCESS(rc)) {
if (tail) {
while (*listpp) {
}
}
}
if (ACPI_FAILURE(rc)) {
}
return (rc);
}
{
"in acpidev_unregister_class().");
return (AE_BAD_PARAMETER);
}
break;
}
}
"in acpidev_unregister_class().",
rc = AE_NOT_FOUND;
"in acpidev_unregister_class()..",
} else {
}
return (rc);
}