|
@@ -66,11 +66,18 @@ struct acpi_pci_root {
|
|
struct acpi_device * device;
|
|
struct acpi_device * device;
|
|
struct acpi_pci_id id;
|
|
struct acpi_pci_id id;
|
|
struct pci_bus *bus;
|
|
struct pci_bus *bus;
|
|
|
|
+
|
|
|
|
+ u32 osc_support_set; /* _OSC state of support bits */
|
|
|
|
+ u32 osc_control_set; /* _OSC state of control bits */
|
|
|
|
+ u32 osc_control_qry; /* the latest _OSC query result */
|
|
|
|
+
|
|
|
|
+ u32 osc_queried:1; /* has _OSC control been queried? */
|
|
};
|
|
};
|
|
|
|
|
|
static LIST_HEAD(acpi_pci_roots);
|
|
static LIST_HEAD(acpi_pci_roots);
|
|
|
|
|
|
static struct acpi_pci_driver *sub_driver;
|
|
static struct acpi_pci_driver *sub_driver;
|
|
|
|
+static DEFINE_MUTEX(osc_lock);
|
|
|
|
|
|
int acpi_pci_register_driver(struct acpi_pci_driver *driver)
|
|
int acpi_pci_register_driver(struct acpi_pci_driver *driver)
|
|
{
|
|
{
|
|
@@ -185,6 +192,175 @@ static void acpi_pci_bridge_scan(struct acpi_device *device)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static u8 OSC_UUID[16] = {0x5B, 0x4D, 0xDB, 0x33, 0xF7, 0x1F, 0x1C, 0x40,
|
|
|
|
+ 0x96, 0x57, 0x74, 0x41, 0xC0, 0x3D, 0xD7, 0x66};
|
|
|
|
+
|
|
|
|
+static acpi_status acpi_pci_run_osc(acpi_handle handle,
|
|
|
|
+ const u32 *capbuf, u32 *retval)
|
|
|
|
+{
|
|
|
|
+ acpi_status status;
|
|
|
|
+ struct acpi_object_list input;
|
|
|
|
+ union acpi_object in_params[4];
|
|
|
|
+ struct acpi_buffer output = {ACPI_ALLOCATE_BUFFER, NULL};
|
|
|
|
+ union acpi_object *out_obj;
|
|
|
|
+ u32 errors;
|
|
|
|
+
|
|
|
|
+ /* Setting up input parameters */
|
|
|
|
+ input.count = 4;
|
|
|
|
+ input.pointer = in_params;
|
|
|
|
+ in_params[0].type = ACPI_TYPE_BUFFER;
|
|
|
|
+ in_params[0].buffer.length = 16;
|
|
|
|
+ in_params[0].buffer.pointer = OSC_UUID;
|
|
|
|
+ in_params[1].type = ACPI_TYPE_INTEGER;
|
|
|
|
+ in_params[1].integer.value = 1;
|
|
|
|
+ in_params[2].type = ACPI_TYPE_INTEGER;
|
|
|
|
+ in_params[2].integer.value = 3;
|
|
|
|
+ in_params[3].type = ACPI_TYPE_BUFFER;
|
|
|
|
+ in_params[3].buffer.length = 12;
|
|
|
|
+ in_params[3].buffer.pointer = (u8 *)capbuf;
|
|
|
|
+
|
|
|
|
+ status = acpi_evaluate_object(handle, "_OSC", &input, &output);
|
|
|
|
+ if (ACPI_FAILURE(status))
|
|
|
|
+ return status;
|
|
|
|
+
|
|
|
|
+ if (!output.length)
|
|
|
|
+ return AE_NULL_OBJECT;
|
|
|
|
+
|
|
|
|
+ out_obj = output.pointer;
|
|
|
|
+ if (out_obj->type != ACPI_TYPE_BUFFER) {
|
|
|
|
+ printk(KERN_DEBUG "_OSC evaluation returned wrong type\n");
|
|
|
|
+ status = AE_TYPE;
|
|
|
|
+ goto out_kfree;
|
|
|
|
+ }
|
|
|
|
+ /* Need to ignore the bit0 in result code */
|
|
|
|
+ errors = *((u32 *)out_obj->buffer.pointer) & ~(1 << 0);
|
|
|
|
+ if (errors) {
|
|
|
|
+ if (errors & OSC_REQUEST_ERROR)
|
|
|
|
+ printk(KERN_DEBUG "_OSC request failed\n");
|
|
|
|
+ if (errors & OSC_INVALID_UUID_ERROR)
|
|
|
|
+ printk(KERN_DEBUG "_OSC invalid UUID\n");
|
|
|
|
+ if (errors & OSC_INVALID_REVISION_ERROR)
|
|
|
|
+ printk(KERN_DEBUG "_OSC invalid revision\n");
|
|
|
|
+ if (errors & OSC_CAPABILITIES_MASK_ERROR) {
|
|
|
|
+ if (capbuf[OSC_QUERY_TYPE] & OSC_QUERY_ENABLE)
|
|
|
|
+ goto out_success;
|
|
|
|
+ printk(KERN_DEBUG
|
|
|
|
+ "Firmware did not grant requested _OSC control\n");
|
|
|
|
+ status = AE_SUPPORT;
|
|
|
|
+ goto out_kfree;
|
|
|
|
+ }
|
|
|
|
+ status = AE_ERROR;
|
|
|
|
+ goto out_kfree;
|
|
|
|
+ }
|
|
|
|
+out_success:
|
|
|
|
+ *retval = *((u32 *)(out_obj->buffer.pointer + 8));
|
|
|
|
+ status = AE_OK;
|
|
|
|
+
|
|
|
|
+out_kfree:
|
|
|
|
+ kfree(output.pointer);
|
|
|
|
+ return status;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static acpi_status acpi_pci_query_osc(struct acpi_pci_root *root, u32 flags)
|
|
|
|
+{
|
|
|
|
+ acpi_status status;
|
|
|
|
+ u32 support_set, result, capbuf[3];
|
|
|
|
+
|
|
|
|
+ /* do _OSC query for all possible controls */
|
|
|
|
+ support_set = root->osc_support_set | (flags & OSC_SUPPORT_MASKS);
|
|
|
|
+ capbuf[OSC_QUERY_TYPE] = OSC_QUERY_ENABLE;
|
|
|
|
+ capbuf[OSC_SUPPORT_TYPE] = support_set;
|
|
|
|
+ capbuf[OSC_CONTROL_TYPE] = OSC_CONTROL_MASKS;
|
|
|
|
+
|
|
|
|
+ status = acpi_pci_run_osc(root->device->handle, capbuf, &result);
|
|
|
|
+ if (ACPI_SUCCESS(status)) {
|
|
|
|
+ root->osc_support_set = support_set;
|
|
|
|
+ root->osc_control_qry = result;
|
|
|
|
+ root->osc_queried = 1;
|
|
|
|
+ }
|
|
|
|
+ return status;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static acpi_status acpi_pci_osc_support(struct acpi_pci_root *root, u32 flags)
|
|
|
|
+{
|
|
|
|
+ acpi_status status;
|
|
|
|
+ acpi_handle tmp;
|
|
|
|
+
|
|
|
|
+ status = acpi_get_handle(root->device->handle, "_OSC", &tmp);
|
|
|
|
+ if (ACPI_FAILURE(status))
|
|
|
|
+ return status;
|
|
|
|
+ mutex_lock(&osc_lock);
|
|
|
|
+ status = acpi_pci_query_osc(root, flags);
|
|
|
|
+ mutex_unlock(&osc_lock);
|
|
|
|
+ return status;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static struct acpi_pci_root *acpi_pci_find_root(acpi_handle handle)
|
|
|
|
+{
|
|
|
|
+ struct acpi_pci_root *root;
|
|
|
|
+ list_for_each_entry(root, &acpi_pci_roots, node) {
|
|
|
|
+ if (root->device->handle == handle)
|
|
|
|
+ return root;
|
|
|
|
+ }
|
|
|
|
+ return NULL;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * pci_osc_control_set - commit requested control to Firmware
|
|
|
|
+ * @handle: acpi_handle for the target ACPI object
|
|
|
|
+ * @flags: driver's requested control bits
|
|
|
|
+ *
|
|
|
|
+ * Attempt to take control from Firmware on requested control bits.
|
|
|
|
+ **/
|
|
|
|
+acpi_status pci_osc_control_set(acpi_handle handle, u32 flags)
|
|
|
|
+{
|
|
|
|
+ acpi_status status;
|
|
|
|
+ u32 control_req, result, capbuf[3];
|
|
|
|
+ acpi_handle tmp;
|
|
|
|
+ struct acpi_pci_root *root;
|
|
|
|
+
|
|
|
|
+ status = acpi_get_handle(handle, "_OSC", &tmp);
|
|
|
|
+ if (ACPI_FAILURE(status))
|
|
|
|
+ return status;
|
|
|
|
+
|
|
|
|
+ control_req = (flags & OSC_CONTROL_MASKS);
|
|
|
|
+ if (!control_req)
|
|
|
|
+ return AE_TYPE;
|
|
|
|
+
|
|
|
|
+ root = acpi_pci_find_root(handle);
|
|
|
|
+ if (!root)
|
|
|
|
+ return AE_NOT_EXIST;
|
|
|
|
+
|
|
|
|
+ mutex_lock(&osc_lock);
|
|
|
|
+ /* No need to evaluate _OSC if the control was already granted. */
|
|
|
|
+ if ((root->osc_control_set & control_req) == control_req)
|
|
|
|
+ goto out;
|
|
|
|
+
|
|
|
|
+ /* Need to query controls first before requesting them */
|
|
|
|
+ if (!root->osc_queried) {
|
|
|
|
+ status = acpi_pci_query_osc(root, root->osc_support_set);
|
|
|
|
+ if (ACPI_FAILURE(status))
|
|
|
|
+ goto out;
|
|
|
|
+ }
|
|
|
|
+ if ((root->osc_control_qry & control_req) != control_req) {
|
|
|
|
+ printk(KERN_DEBUG
|
|
|
|
+ "Firmware did not grant requested _OSC control\n");
|
|
|
|
+ status = AE_SUPPORT;
|
|
|
|
+ goto out;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ capbuf[OSC_QUERY_TYPE] = 0;
|
|
|
|
+ capbuf[OSC_SUPPORT_TYPE] = root->osc_support_set;
|
|
|
|
+ capbuf[OSC_CONTROL_TYPE] = root->osc_control_set | control_req;
|
|
|
|
+ status = acpi_pci_run_osc(handle, capbuf, &result);
|
|
|
|
+ if (ACPI_SUCCESS(status))
|
|
|
|
+ root->osc_control_set = result;
|
|
|
|
+out:
|
|
|
|
+ mutex_unlock(&osc_lock);
|
|
|
|
+ return status;
|
|
|
|
+}
|
|
|
|
+EXPORT_SYMBOL(pci_osc_control_set);
|
|
|
|
+
|
|
static int __devinit acpi_pci_root_add(struct acpi_device *device)
|
|
static int __devinit acpi_pci_root_add(struct acpi_device *device)
|
|
{
|
|
{
|
|
int result = 0;
|
|
int result = 0;
|
|
@@ -217,7 +393,7 @@ static int __devinit acpi_pci_root_add(struct acpi_device *device)
|
|
* PCI domains, so we indicate this in _OSC support capabilities.
|
|
* PCI domains, so we indicate this in _OSC support capabilities.
|
|
*/
|
|
*/
|
|
flags = base_flags = OSC_PCI_SEGMENT_GROUPS_SUPPORT;
|
|
flags = base_flags = OSC_PCI_SEGMENT_GROUPS_SUPPORT;
|
|
- pci_acpi_osc_support(device->handle, flags);
|
|
|
|
|
|
+ acpi_pci_osc_support(root, flags);
|
|
|
|
|
|
/*
|
|
/*
|
|
* Segment
|
|
* Segment
|
|
@@ -353,7 +529,7 @@ static int __devinit acpi_pci_root_add(struct acpi_device *device)
|
|
if (pci_msi_enabled())
|
|
if (pci_msi_enabled())
|
|
flags |= OSC_MSI_SUPPORT;
|
|
flags |= OSC_MSI_SUPPORT;
|
|
if (flags != base_flags)
|
|
if (flags != base_flags)
|
|
- pci_acpi_osc_support(device->handle, flags);
|
|
|
|
|
|
+ acpi_pci_osc_support(root, flags);
|
|
|
|
|
|
end:
|
|
end:
|
|
if (result) {
|
|
if (result) {
|