From bd8191cc8a74018e255eb3efff5e02dc305a5ed1 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 27 Aug 2015 04:35:14 +0200 Subject: ACPI / property: Add routine for extraction of _DSD properties Move the extraction of _DSD properties from acpi_init_properties() to a separate routine called acpi_extract_properties() to make the subsequent changes more straightforward. Signed-off-by: Rafael J. Wysocki Tested-by: Mika Westerberg diff --git a/drivers/acpi/property.c b/drivers/acpi/property.c index 6d99450..8163b4b 100644 --- a/drivers/acpi/property.c +++ b/drivers/acpi/property.c @@ -100,34 +100,13 @@ static void acpi_init_of_compatible(struct acpi_device *adev) adev->flags.of_compatible_ok = 1; } -void acpi_init_properties(struct acpi_device *adev) +static bool acpi_extract_properties(const union acpi_object *desc, + struct acpi_device_data *data) { - struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER }; - bool acpi_of = false; - struct acpi_hardware_id *hwid; - const union acpi_object *desc; - acpi_status status; int i; - /* - * Check if ACPI_DT_NAMESPACE_HID is present and inthat case we fill in - * Device Tree compatible properties for this device. - */ - list_for_each_entry(hwid, &adev->pnp.ids, list) { - if (!strcmp(hwid->id, ACPI_DT_NAMESPACE_HID)) { - acpi_of = true; - break; - } - } - - status = acpi_evaluate_object_typed(adev->handle, "_DSD", NULL, &buf, - ACPI_TYPE_PACKAGE); - if (ACPI_FAILURE(status)) - goto out; - - desc = buf.pointer; if (desc->package.count % 2) - goto fail; + return false; /* Look for the device properties UUID. */ for (i = 0; i < desc->package.count; i += 2) { @@ -154,18 +133,44 @@ void acpi_init_properties(struct acpi_device *adev) if (!acpi_properties_format_valid(properties)) break; - adev->data.pointer = buf.pointer; - adev->data.properties = properties; + data->properties = properties; + return true; + } - if (acpi_of) - acpi_init_of_compatible(adev); + return false; +} - goto out; +void acpi_init_properties(struct acpi_device *adev) +{ + struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER }; + struct acpi_hardware_id *hwid; + acpi_status status; + bool acpi_of = false; + + /* + * Check if ACPI_DT_NAMESPACE_HID is present and inthat case we fill in + * Device Tree compatible properties for this device. + */ + list_for_each_entry(hwid, &adev->pnp.ids, list) { + if (!strcmp(hwid->id, ACPI_DT_NAMESPACE_HID)) { + acpi_of = true; + break; + } } - fail: - dev_dbg(&adev->dev, "Returned _DSD data is not valid, skipping\n"); - ACPI_FREE(buf.pointer); + status = acpi_evaluate_object_typed(adev->handle, "_DSD", NULL, &buf, + ACPI_TYPE_PACKAGE); + if (ACPI_FAILURE(status)) + goto out; + + if (acpi_extract_properties(buf.pointer, &adev->data)) { + adev->data.pointer = buf.pointer; + if (acpi_of) + acpi_init_of_compatible(adev); + } else { + acpi_handle_debug(adev->handle, "Invalid _DSD data, skipping\n"); + ACPI_FREE(buf.pointer); + } out: if (acpi_of && !adev->flags.of_compatible_ok) -- cgit v0.10.2 From 445b0eb058f5f31c844a731cb82e7441d0d9e578 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 27 Aug 2015 04:36:14 +0200 Subject: ACPI / property: Add support for data-only subnodes In some cases, the information expressed via device properties is hierarchical by nature. For example, the properties of a composite device consisting of multiple semi-dependent components may need to be represented in the form of a tree of property data sets corresponding to specific components of the device. Unfortunately, using ACPI device objects for this purpose turns out to be problematic, mostly due to the assumption made by some operating systems (that platform firmware generally needs to work with) that each device object in the ACPI namespace represents a device requiring a separate driver. That assumption leads to complications which reportedly are impractically difficult to overcome and a different approach is needed for the sake of interoperability. The approach implemented here is based on extending _DSD via pointers (links) to additional ACPI objects returning data packages formatted in accordance with the _DSD formatting rules defined by Section 6.2.5 of ACPI 6. Those additional objects are referred to as data-only subnodes of the device object containing the _DSD pointing to them. The links to them need to be located in a separate section of the _DSD data package following UUID dbb8e3e6-5886-4ba6-8795-1319f52a966b referred to as the Hierarchical Data Extension UUID as defined in [1]. Each of them is represented by a package of two strings. The first string in that package (the key) is regarded as the name of the data-only subnode pointed to by the link. The second string in it (the target) is expected to hold the ACPI namespace path (possibly utilizing the usual ACPI namespace search rules) of an ACPI object evaluating to a data package extending the _DSD. The device properties initialization code follows those links, creates a struct acpi_data_node object for each of them to store the data returned by the ACPI object pointed to by it and processes those data recursively (which may lead to the creation of more struct acpi_data_node objects if the returned data package contains the Hierarchical Data Extension UUID section with more links in it). All of the struct acpi_data_node objects are present until the the ACPI device object containing the _DSD with links to them is deleted and they are deleted along with that object. [1]: http://www.uefi.org/sites/default/files/resources/_DSD-hierarchical-data-extension-UUID-v1.pdf Signed-off-by: Rafael J. Wysocki Tested-by: Mika Westerberg diff --git a/drivers/acpi/property.c b/drivers/acpi/property.c index 8163b4b..17c436d 100644 --- a/drivers/acpi/property.c +++ b/drivers/acpi/property.c @@ -24,6 +24,115 @@ static const u8 prp_uuid[16] = { 0x14, 0xd8, 0xff, 0xda, 0xba, 0x6e, 0x8c, 0x4d, 0x8a, 0x91, 0xbc, 0x9b, 0xbf, 0x4a, 0xa3, 0x01 }; +/* ACPI _DSD data subnodes UUID: dbb8e3e6-5886-4ba6-8795-1319f52a966b */ +static const u8 ads_uuid[16] = { + 0xe6, 0xe3, 0xb8, 0xdb, 0x86, 0x58, 0xa6, 0x4b, + 0x87, 0x95, 0x13, 0x19, 0xf5, 0x2a, 0x96, 0x6b +}; + +static bool acpi_enumerate_nondev_subnodes(acpi_handle scope, + const union acpi_object *desc, + struct acpi_device_data *data); +static bool acpi_extract_properties(const union acpi_object *desc, + struct acpi_device_data *data); + +static bool acpi_nondev_subnode_ok(acpi_handle scope, + const union acpi_object *link, + struct list_head *list) +{ + struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER }; + struct acpi_data_node *dn; + acpi_handle handle; + acpi_status status; + + dn = kzalloc(sizeof(*dn), GFP_KERNEL); + if (!dn) + return false; + + dn->name = link->package.elements[0].string.pointer; + dn->fwnode.type = FWNODE_ACPI_DATA; + INIT_LIST_HEAD(&dn->data.subnodes); + + status = acpi_get_handle(scope, link->package.elements[1].string.pointer, + &handle); + if (ACPI_FAILURE(status)) + goto fail; + + status = acpi_evaluate_object_typed(handle, NULL, NULL, &buf, + ACPI_TYPE_PACKAGE); + if (ACPI_FAILURE(status)) + goto fail; + + if (acpi_extract_properties(buf.pointer, &dn->data)) + dn->data.pointer = buf.pointer; + + if (acpi_enumerate_nondev_subnodes(scope, buf.pointer, &dn->data)) + dn->data.pointer = buf.pointer; + + if (dn->data.pointer) { + list_add_tail(&dn->sibling, list); + return true; + } + + acpi_handle_debug(handle, "Invalid properties/subnodes data, skipping\n"); + + fail: + ACPI_FREE(buf.pointer); + kfree(dn); + return false; +} + +static int acpi_add_nondev_subnodes(acpi_handle scope, + const union acpi_object *links, + struct list_head *list) +{ + bool ret = false; + int i; + + for (i = 0; i < links->package.count; i++) { + const union acpi_object *link; + + link = &links->package.elements[i]; + /* Only two elements allowed, both must be strings. */ + if (link->package.count == 2 + && link->package.elements[0].type == ACPI_TYPE_STRING + && link->package.elements[1].type == ACPI_TYPE_STRING + && acpi_nondev_subnode_ok(scope, link, list)) + ret = true; + } + + return ret; +} + +static bool acpi_enumerate_nondev_subnodes(acpi_handle scope, + const union acpi_object *desc, + struct acpi_device_data *data) +{ + int i; + + /* Look for the ACPI data subnodes UUID. */ + for (i = 0; i < desc->package.count; i += 2) { + const union acpi_object *uuid, *links; + + uuid = &desc->package.elements[i]; + links = &desc->package.elements[i + 1]; + + /* + * The first element must be a UUID and the second one must be + * a package. + */ + if (uuid->type != ACPI_TYPE_BUFFER || uuid->buffer.length != 16 + || links->type != ACPI_TYPE_PACKAGE) + break; + + if (memcmp(uuid->buffer.pointer, ads_uuid, sizeof(ads_uuid))) + continue; + + return acpi_add_nondev_subnodes(scope, links, &data->subnodes); + } + + return false; +} static bool acpi_property_value_ok(const union acpi_object *value) { @@ -147,6 +256,8 @@ void acpi_init_properties(struct acpi_device *adev) acpi_status status; bool acpi_of = false; + INIT_LIST_HEAD(&adev->data.subnodes); + /* * Check if ACPI_DT_NAMESPACE_HID is present and inthat case we fill in * Device Tree compatible properties for this device. @@ -167,7 +278,11 @@ void acpi_init_properties(struct acpi_device *adev) adev->data.pointer = buf.pointer; if (acpi_of) acpi_init_of_compatible(adev); - } else { + } + if (acpi_enumerate_nondev_subnodes(adev->handle, buf.pointer, &adev->data)) + adev->data.pointer = buf.pointer; + + if (!adev->data.pointer) { acpi_handle_debug(adev->handle, "Invalid _DSD data, skipping\n"); ACPI_FREE(buf.pointer); } @@ -178,8 +293,24 @@ void acpi_init_properties(struct acpi_device *adev) ACPI_DT_NAMESPACE_HID " requires 'compatible' property\n"); } +static void acpi_destroy_nondev_subnodes(struct list_head *list) +{ + struct acpi_data_node *dn, *next; + + if (list_empty(list)) + return; + + list_for_each_entry_safe_reverse(dn, next, list, sibling) { + acpi_destroy_nondev_subnodes(&dn->data.subnodes); + list_del(&dn->sibling); + ACPI_FREE((void *)dn->data.pointer); + kfree(dn); + } +} + void acpi_free_properties(struct acpi_device *adev) { + acpi_destroy_nondev_subnodes(&adev->data.subnodes); ACPI_FREE((void *)adev->data.pointer); adev->data.of_compatible = NULL; adev->data.pointer = NULL; diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index 5ba8fb6..79cfee6 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -343,6 +343,7 @@ struct acpi_device_data { const union acpi_object *pointer; const union acpi_object *properties; const union acpi_object *of_compatible; + struct list_head subnodes; }; struct acpi_gpio_mapping; @@ -378,6 +379,14 @@ struct acpi_device { void (*remove)(struct acpi_device *); }; +/* Non-device subnode */ +struct acpi_data_node { + const char *name; + struct fwnode_handle fwnode; + struct acpi_device_data data; + struct list_head sibling; +}; + static inline bool acpi_check_dma(struct acpi_device *adev, bool *coherent) { bool ret = false; diff --git a/include/linux/fwnode.h b/include/linux/fwnode.h index 0408545..b08d6ba 100644 --- a/include/linux/fwnode.h +++ b/include/linux/fwnode.h @@ -16,6 +16,7 @@ enum fwnode_type { FWNODE_INVALID = 0, FWNODE_OF, FWNODE_ACPI, + FWNODE_ACPI_DATA, FWNODE_PDATA, }; -- cgit v0.10.2 From 263b4c1a64bc12470684aeaf7c44f03d31716819 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 27 Aug 2015 04:37:19 +0200 Subject: ACPI / property: Expose data-only subnodes via sysfs Add infrastructure needed to expose data-only subnodes of ACPI device objects introduced previously via sysfs. Each data-only subnode is represented as a sysfs directory under the directory corresponding to its parent object (a device or a data-only subnode). Each of them has a "path" attribute (containing the full ACPI namespace path to the object the subnode data come from) at this time. Signed-off-by: Rafael J. Wysocki Tested-by: Mika Westerberg diff --git a/drivers/acpi/device_sysfs.c b/drivers/acpi/device_sysfs.c index 4ab4582..707cf62 100644 --- a/drivers/acpi/device_sysfs.c +++ b/drivers/acpi/device_sysfs.c @@ -26,6 +26,106 @@ #include "internal.h" +static ssize_t acpi_object_path(acpi_handle handle, char *buf) +{ + struct acpi_buffer path = {ACPI_ALLOCATE_BUFFER, NULL}; + int result; + + result = acpi_get_name(handle, ACPI_FULL_PATHNAME, &path); + if (result) + return result; + + result = sprintf(buf, "%s\n", (char*)path.pointer); + kfree(path.pointer); + return result; +} + +struct acpi_data_node_attr { + struct attribute attr; + ssize_t (*show)(struct acpi_data_node *, char *); + ssize_t (*store)(struct acpi_data_node *, const char *, size_t count); +}; + +#define DATA_NODE_ATTR(_name) \ + static struct acpi_data_node_attr data_node_##_name = \ + __ATTR(_name, 0444, data_node_show_##_name, NULL) + +static ssize_t data_node_show_path(struct acpi_data_node *dn, char *buf) +{ + return acpi_object_path(dn->handle, buf); +} + +DATA_NODE_ATTR(path); + +static struct attribute *acpi_data_node_default_attrs[] = { + &data_node_path.attr, + NULL +}; + +#define to_data_node(k) container_of(k, struct acpi_data_node, kobj) +#define to_attr(a) container_of(a, struct acpi_data_node_attr, attr) + +static ssize_t acpi_data_node_attr_show(struct kobject *kobj, + struct attribute *attr, char *buf) +{ + struct acpi_data_node *dn = to_data_node(kobj); + struct acpi_data_node_attr *dn_attr = to_attr(attr); + + return dn_attr->show ? dn_attr->show(dn, buf) : -ENXIO; +} + +static const struct sysfs_ops acpi_data_node_sysfs_ops = { + .show = acpi_data_node_attr_show, +}; + +static void acpi_data_node_release(struct kobject *kobj) +{ + struct acpi_data_node *dn = to_data_node(kobj); + complete(&dn->kobj_done); +} + +static struct kobj_type acpi_data_node_ktype = { + .sysfs_ops = &acpi_data_node_sysfs_ops, + .default_attrs = acpi_data_node_default_attrs, + .release = acpi_data_node_release, +}; + +static void acpi_expose_nondev_subnodes(struct kobject *kobj, + struct acpi_device_data *data) +{ + struct list_head *list = &data->subnodes; + struct acpi_data_node *dn; + + if (list_empty(list)) + return; + + list_for_each_entry(dn, list, sibling) { + int ret; + + init_completion(&dn->kobj_done); + ret = kobject_init_and_add(&dn->kobj, &acpi_data_node_ktype, + kobj, dn->name); + if (ret) + acpi_handle_err(dn->handle, "Failed to expose (%d)\n", ret); + else + acpi_expose_nondev_subnodes(&dn->kobj, &dn->data); + } +} + +static void acpi_hide_nondev_subnodes(struct acpi_device_data *data) +{ + struct list_head *list = &data->subnodes; + struct acpi_data_node *dn; + + if (list_empty(list)) + return; + + list_for_each_entry_reverse(dn, list, sibling) { + acpi_hide_nondev_subnodes(&dn->data); + kobject_put(&dn->kobj); + } +} + /** * create_pnp_modalias - Create hid/cid(s) string for modalias and uevent * @acpi_dev: ACPI device object. @@ -323,20 +423,12 @@ static ssize_t acpi_device_adr_show(struct device *dev, } static DEVICE_ATTR(adr, 0444, acpi_device_adr_show, NULL); -static ssize_t -acpi_device_path_show(struct device *dev, struct device_attribute *attr, char *buf) { +static ssize_t acpi_device_path_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ struct acpi_device *acpi_dev = to_acpi_device(dev); - struct acpi_buffer path = {ACPI_ALLOCATE_BUFFER, NULL}; - int result; - - result = acpi_get_name(acpi_dev->handle, ACPI_FULL_PATHNAME, &path); - if (result) - goto end; - result = sprintf(buf, "%s\n", (char*)path.pointer); - kfree(path.pointer); -end: - return result; + return acpi_object_path(acpi_dev->handle, buf); } static DEVICE_ATTR(path, 0444, acpi_device_path_show, NULL); @@ -475,6 +567,8 @@ int acpi_device_setup_files(struct acpi_device *dev) &dev_attr_real_power_state); } + acpi_expose_nondev_subnodes(&dev->dev.kobj, &dev->data); + end: return result; } @@ -485,6 +579,8 @@ end: */ void acpi_device_remove_files(struct acpi_device *dev) { + acpi_hide_nondev_subnodes(&dev->data); + if (dev->flags.power_manageable) { device_remove_file(&dev->dev, &dev_attr_power_state); if (dev->power.flags.power_resources) diff --git a/drivers/acpi/property.c b/drivers/acpi/property.c index 17c436d..333f914 100644 --- a/drivers/acpi/property.c +++ b/drivers/acpi/property.c @@ -64,12 +64,13 @@ static bool acpi_nondev_subnode_ok(acpi_handle scope, goto fail; if (acpi_extract_properties(buf.pointer, &dn->data)) - dn->data.pointer = buf.pointer; + dn->handle = handle; if (acpi_enumerate_nondev_subnodes(scope, buf.pointer, &dn->data)) - dn->data.pointer = buf.pointer; + dn->handle = handle; - if (dn->data.pointer) { + if (dn->handle) { + dn->data.pointer = buf.pointer; list_add_tail(&dn->sibling, list); return true; } @@ -302,6 +303,7 @@ static void acpi_destroy_nondev_subnodes(struct list_head *list) list_for_each_entry_safe_reverse(dn, next, list, sibling) { acpi_destroy_nondev_subnodes(&dn->data.subnodes); + wait_for_completion(&dn->kobj_done); list_del(&dn->sibling); ACPI_FREE((void *)dn->data.pointer); kfree(dn); diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index 79cfee6..e0d7c19 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -382,9 +382,12 @@ struct acpi_device { /* Non-device subnode */ struct acpi_data_node { const char *name; + acpi_handle handle; struct fwnode_handle fwnode; struct acpi_device_data data; struct list_head sibling; + struct kobject kobj; + struct completion kobj_done; }; static inline bool acpi_check_dma(struct acpi_device *adev, bool *coherent) -- cgit v0.10.2 From 3a7a2ab839ad18c2d542b40f4a647c98d068e55a Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 27 Aug 2015 04:40:05 +0200 Subject: ACPI / property: Extend fwnode_property_* to data-only subnodes Modify is_acpi_node() to return "true" for ACPI data-only subnodes as well as for ACPI device objects and change the name of to_acpi_node() to to_acpi_device_node() so it is clear that it covers ACPI device objects only. Accordingly, introduce to_acpi_data_node() to cover data-only subnodes in an analogous way. With that, make the fwnode_property_* family of functions work with ACPI data-only subnodes introduced previously. Signed-off-by: Rafael J. Wysocki Tested-by: Mika Westerberg diff --git a/drivers/acpi/property.c b/drivers/acpi/property.c index 333f914..e785517 100644 --- a/drivers/acpi/property.c +++ b/drivers/acpi/property.c @@ -19,6 +19,11 @@ #include "internal.h" +static int acpi_data_get_property_array(struct acpi_device_data *data, + const char *name, + acpi_object_type type, + const union acpi_object **obj); + /* ACPI _DSD device properties UUID: daffd814-6eba-4d8c-8a91-bc9bbf4aa301 */ static const u8 prp_uuid[16] = { 0x14, 0xd8, 0xff, 0xda, 0xba, 0x6e, 0x8c, 0x4d, @@ -191,8 +196,8 @@ static void acpi_init_of_compatible(struct acpi_device *adev) const union acpi_object *of_compatible; int ret; - ret = acpi_dev_get_property_array(adev, "compatible", ACPI_TYPE_STRING, - &of_compatible); + ret = acpi_data_get_property_array(&adev->data, "compatible", + ACPI_TYPE_STRING, &of_compatible); if (ret) { ret = acpi_dev_get_property(adev, "compatible", ACPI_TYPE_STRING, &of_compatible); @@ -320,8 +325,8 @@ void acpi_free_properties(struct acpi_device *adev) } /** - * acpi_dev_get_property - return an ACPI property with given name - * @adev: ACPI device to get property + * acpi_data_get_property - return an ACPI property with given name + * @data: ACPI device deta object to get the property from * @name: Name of the property * @type: Expected property type * @obj: Location to store the property value (if not %NULL) @@ -330,26 +335,27 @@ void acpi_free_properties(struct acpi_device *adev) * object at the location pointed to by @obj if found. * * Callers must not attempt to free the returned objects. These objects will be - * freed by the ACPI core automatically during the removal of @adev. + * freed by the ACPI core automatically during the removal of @data. * * Return: %0 if property with @name has been found (success), * %-EINVAL if the arguments are invalid, * %-ENODATA if the property doesn't exist, * %-EPROTO if the property value type doesn't match @type. */ -int acpi_dev_get_property(struct acpi_device *adev, const char *name, - acpi_object_type type, const union acpi_object **obj) +static int acpi_data_get_property(struct acpi_device_data *data, + const char *name, acpi_object_type type, + const union acpi_object **obj) { const union acpi_object *properties; int i; - if (!adev || !name) + if (!data || !name) return -EINVAL; - if (!adev->data.pointer || !adev->data.properties) + if (!data->pointer || !data->properties) return -ENODATA; - properties = adev->data.properties; + properties = data->properties; for (i = 0; i < properties->package.count; i++) { const union acpi_object *propname, *propvalue; const union acpi_object *property; @@ -370,11 +376,50 @@ int acpi_dev_get_property(struct acpi_device *adev, const char *name, } return -ENODATA; } + +/** + * acpi_dev_get_property - return an ACPI property with given name. + * @adev: ACPI device to get the property from. + * @name: Name of the property. + * @type: Expected property type. + * @obj: Location to store the property value (if not %NULL). + */ +int acpi_dev_get_property(struct acpi_device *adev, const char *name, + acpi_object_type type, const union acpi_object **obj) +{ + return adev ? acpi_data_get_property(&adev->data, name, type, obj) : -EINVAL; +} EXPORT_SYMBOL_GPL(acpi_dev_get_property); +static struct acpi_device_data *acpi_device_data_of_node(struct fwnode_handle *fwnode) +{ + if (fwnode->type == FWNODE_ACPI) { + struct acpi_device *adev = to_acpi_device_node(fwnode); + return &adev->data; + } else if (fwnode->type == FWNODE_ACPI_DATA) { + struct acpi_data_node *dn = to_acpi_data_node(fwnode); + return &dn->data; + } + return NULL; +} + /** - * acpi_dev_get_property_array - return an ACPI array property with given name - * @adev: ACPI device to get property + * acpi_node_prop_get - return an ACPI property with given name. + * @fwnode: Firmware node to get the property from. + * @propname: Name of the property. + * @valptr: Location to store a pointer to the property value (if not %NULL). + */ +int acpi_node_prop_get(struct fwnode_handle *fwnode, const char *propname, + void **valptr) +{ + return acpi_data_get_property(acpi_device_data_of_node(fwnode), + propname, ACPI_TYPE_ANY, + (const union acpi_object **)valptr); +} + +/** + * acpi_data_get_property_array - return an ACPI array property with given name + * @adev: ACPI data object to get the property from * @name: Name of the property * @type: Expected type of array elements * @obj: Location to store a pointer to the property value (if not NULL) @@ -383,7 +428,7 @@ EXPORT_SYMBOL_GPL(acpi_dev_get_property); * ACPI object at the location pointed to by @obj if found. * * Callers must not attempt to free the returned objects. Those objects will be - * freed by the ACPI core automatically during the removal of @adev. + * freed by the ACPI core automatically during the removal of @data. * * Return: %0 if array property (package) with @name has been found (success), * %-EINVAL if the arguments are invalid, @@ -391,14 +436,15 @@ EXPORT_SYMBOL_GPL(acpi_dev_get_property); * %-EPROTO if the property is not a package or the type of its elements * doesn't match @type. */ -int acpi_dev_get_property_array(struct acpi_device *adev, const char *name, - acpi_object_type type, - const union acpi_object **obj) +static int acpi_data_get_property_array(struct acpi_device_data *data, + const char *name, + acpi_object_type type, + const union acpi_object **obj) { const union acpi_object *prop; int ret, i; - ret = acpi_dev_get_property(adev, name, ACPI_TYPE_PACKAGE, &prop); + ret = acpi_data_get_property(data, name, ACPI_TYPE_PACKAGE, &prop); if (ret) return ret; @@ -413,7 +459,6 @@ int acpi_dev_get_property_array(struct acpi_device *adev, const char *name, return 0; } -EXPORT_SYMBOL_GPL(acpi_dev_get_property_array); /** * acpi_dev_get_property_reference - returns handle to the referenced object @@ -518,15 +563,9 @@ int acpi_dev_get_property_reference(struct acpi_device *adev, } EXPORT_SYMBOL_GPL(acpi_dev_get_property_reference); -int acpi_dev_prop_get(struct acpi_device *adev, const char *propname, - void **valptr) -{ - return acpi_dev_get_property(adev, propname, ACPI_TYPE_ANY, - (const union acpi_object **)valptr); -} - -int acpi_dev_prop_read_single(struct acpi_device *adev, const char *propname, - enum dev_prop_type proptype, void *val) +static int acpi_data_prop_read_single(struct acpi_device_data *data, + const char *propname, + enum dev_prop_type proptype, void *val) { const union acpi_object *obj; int ret; @@ -535,7 +574,7 @@ int acpi_dev_prop_read_single(struct acpi_device *adev, const char *propname, return -EINVAL; if (proptype >= DEV_PROP_U8 && proptype <= DEV_PROP_U64) { - ret = acpi_dev_get_property(adev, propname, ACPI_TYPE_INTEGER, &obj); + ret = acpi_data_get_property(data, propname, ACPI_TYPE_INTEGER, &obj); if (ret) return ret; @@ -560,7 +599,7 @@ int acpi_dev_prop_read_single(struct acpi_device *adev, const char *propname, break; } } else if (proptype == DEV_PROP_STRING) { - ret = acpi_dev_get_property(adev, propname, ACPI_TYPE_STRING, &obj); + ret = acpi_data_get_property(data, propname, ACPI_TYPE_STRING, &obj); if (ret) return ret; @@ -571,6 +610,12 @@ int acpi_dev_prop_read_single(struct acpi_device *adev, const char *propname, return ret; } +int acpi_dev_prop_read_single(struct acpi_device *adev, const char *propname, + enum dev_prop_type proptype, void *val) +{ + return adev ? acpi_data_prop_read_single(&adev->data, propname, proptype, val) : -EINVAL; +} + static int acpi_copy_property_array_u8(const union acpi_object *items, u8 *val, size_t nval) { @@ -647,20 +692,22 @@ static int acpi_copy_property_array_string(const union acpi_object *items, return 0; } -int acpi_dev_prop_read(struct acpi_device *adev, const char *propname, - enum dev_prop_type proptype, void *val, size_t nval) +static int acpi_data_prop_read(struct acpi_device_data *data, + const char *propname, + enum dev_prop_type proptype, + void *val, size_t nval) { const union acpi_object *obj; const union acpi_object *items; int ret; if (val && nval == 1) { - ret = acpi_dev_prop_read_single(adev, propname, proptype, val); + ret = acpi_data_prop_read_single(data, propname, proptype, val); if (!ret) return ret; } - ret = acpi_dev_get_property_array(adev, propname, ACPI_TYPE_ANY, &obj); + ret = acpi_data_get_property_array(data, propname, ACPI_TYPE_ANY, &obj); if (ret) return ret; @@ -696,3 +743,28 @@ int acpi_dev_prop_read(struct acpi_device *adev, const char *propname, } return ret; } + +int acpi_dev_prop_read(struct acpi_device *adev, const char *propname, + enum dev_prop_type proptype, void *val, size_t nval) +{ + return adev ? acpi_data_prop_read(&adev->data, propname, proptype, val, nval) : -EINVAL; +} + +/** + * acpi_node_prop_read - retrieve the value of an ACPI property with given name. + * @fwnode: Firmware node to get the property from. + * @propname: Name of the property. + * @proptype: Expected property type. + * @val: Location to store the property value (if not %NULL). + * @nval: Size of the array pointed to by @val. + * + * If @val is %NULL, return the number of array elements comprising the value + * of the property. Otherwise, read at most @nval values to the array at the + * location pointed to by @val. + */ +int acpi_node_prop_read(struct fwnode_handle *fwnode, const char *propname, + enum dev_prop_type proptype, void *val, size_t nval) +{ + return acpi_data_prop_read(acpi_device_data_of_node(fwnode), + propname, proptype, val, nval); +} diff --git a/drivers/base/property.c b/drivers/base/property.c index 2d75366..ca11816 100644 --- a/drivers/base/property.c +++ b/drivers/base/property.c @@ -134,7 +134,7 @@ bool fwnode_property_present(struct fwnode_handle *fwnode, const char *propname) if (is_of_node(fwnode)) return of_property_read_bool(to_of_node(fwnode), propname); else if (is_acpi_node(fwnode)) - return !acpi_dev_prop_get(to_acpi_node(fwnode), propname, NULL); + return !acpi_node_prop_get(fwnode, propname, NULL); return !!pset_prop_get(to_pset(fwnode), propname); } @@ -298,8 +298,8 @@ EXPORT_SYMBOL_GPL(device_property_read_string); _ret_ = OF_DEV_PROP_READ_ARRAY(to_of_node(_fwnode_), _propname_, \ _type_, _val_, _nval_); \ else if (is_acpi_node(_fwnode_)) \ - _ret_ = acpi_dev_prop_read(to_acpi_node(_fwnode_), _propname_, \ - _proptype_, _val_, _nval_); \ + _ret_ = acpi_node_prop_read(_fwnode_, _propname_, _proptype_, \ + _val_, _nval_); \ else if (is_pset(_fwnode_)) \ _ret_ = pset_prop_read_array(to_pset(_fwnode_), _propname_, \ _proptype_, _val_, _nval_); \ @@ -440,8 +440,8 @@ int fwnode_property_read_string_array(struct fwnode_handle *fwnode, propname, val, nval) : of_property_count_strings(to_of_node(fwnode), propname); else if (is_acpi_node(fwnode)) - return acpi_dev_prop_read(to_acpi_node(fwnode), propname, - DEV_PROP_STRING, val, nval); + return acpi_node_prop_read(fwnode, propname, DEV_PROP_STRING, + val, nval); else if (is_pset(fwnode)) return pset_prop_read_array(to_pset(fwnode), propname, DEV_PROP_STRING, val, nval); @@ -470,8 +470,8 @@ int fwnode_property_read_string(struct fwnode_handle *fwnode, if (is_of_node(fwnode)) return of_property_read_string(to_of_node(fwnode), propname, val); else if (is_acpi_node(fwnode)) - return acpi_dev_prop_read(to_acpi_node(fwnode), propname, - DEV_PROP_STRING, val, 1); + return acpi_node_prop_read(fwnode, propname, DEV_PROP_STRING, + val, 1); return pset_prop_read_array(to_pset(fwnode), propname, DEV_PROP_STRING, val, 1); @@ -495,7 +495,7 @@ struct fwnode_handle *device_get_next_child_node(struct device *dev, } else if (IS_ENABLED(CONFIG_ACPI)) { struct acpi_device *node; - node = acpi_get_next_child(dev, to_acpi_node(child)); + node = acpi_get_next_child(dev, to_acpi_device_node(child)); if (node) return acpi_fwnode_handle(node); } diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 980c1f8..f43e808 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -2083,11 +2083,11 @@ struct gpio_desc *fwnode_get_named_gpiod(struct fwnode_handle *fwnode, &flags); if (!IS_ERR(desc)) active_low = flags & OF_GPIO_ACTIVE_LOW; - } else if (is_acpi_node(fwnode)) { + } else if (is_acpi_device_node(fwnode)) { struct acpi_gpio_info info; - desc = acpi_get_gpiod_by_index(to_acpi_node(fwnode), propname, 0, - &info); + desc = acpi_get_gpiod_by_index(to_acpi_device_node(fwnode), + propname, 0, &info); if (!IS_ERR(desc)) active_low = info.active_low; } diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index e0d7c19..e234725 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -425,15 +425,32 @@ static inline bool acpi_check_dma(struct acpi_device *adev, bool *coherent) static inline bool is_acpi_node(struct fwnode_handle *fwnode) { + return fwnode && (fwnode->type == FWNODE_ACPI + || fwnode->type == FWNODE_ACPI_DATA); +} + +static inline bool is_acpi_device_node(struct fwnode_handle *fwnode) +{ return fwnode && fwnode->type == FWNODE_ACPI; } -static inline struct acpi_device *to_acpi_node(struct fwnode_handle *fwnode) +static inline struct acpi_device *to_acpi_device_node(struct fwnode_handle *fwnode) { - return is_acpi_node(fwnode) ? + return is_acpi_device_node(fwnode) ? container_of(fwnode, struct acpi_device, fwnode) : NULL; } +static inline bool is_acpi_data_node(struct fwnode_handle *fwnode) +{ + return fwnode && fwnode->type == FWNODE_ACPI_DATA; +} + +static inline struct acpi_data_node *to_acpi_data_node(struct fwnode_handle *fwnode) +{ + return is_acpi_data_node(fwnode) ? + container_of(fwnode, struct acpi_data_node, fwnode) : NULL; +} + static inline struct fwnode_handle *acpi_fwnode_handle(struct acpi_device *adev) { return &adev->fwnode; diff --git a/include/linux/acpi.h b/include/linux/acpi.h index 7235c48..6be94ba 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -49,7 +49,7 @@ static inline acpi_handle acpi_device_handle(struct acpi_device *adev) return adev ? adev->handle : NULL; } -#define ACPI_COMPANION(dev) to_acpi_node((dev)->fwnode) +#define ACPI_COMPANION(dev) to_acpi_device_node((dev)->fwnode) #define ACPI_COMPANION_SET(dev, adev) set_primary_fwnode(dev, (adev) ? \ acpi_fwnode_handle(adev) : NULL) #define ACPI_HANDLE(dev) acpi_device_handle(ACPI_COMPANION(dev)) @@ -69,7 +69,7 @@ static inline acpi_handle acpi_device_handle(struct acpi_device *adev) static inline bool has_acpi_companion(struct device *dev) { - return is_acpi_node(dev->fwnode); + return is_acpi_device_node(dev->fwnode); } static inline void acpi_preset_companion(struct device *dev, @@ -461,7 +461,22 @@ static inline bool is_acpi_node(struct fwnode_handle *fwnode) return false; } -static inline struct acpi_device *to_acpi_node(struct fwnode_handle *fwnode) +static inline bool is_acpi_device_node(struct fwnode_handle *fwnode) +{ + return false; +} + +static inline struct acpi_device *to_acpi_device_node(struct fwnode_handle *fwnode) +{ + return NULL; +} + +static inline bool is_acpi_data_node(struct fwnode_handle *fwnode) +{ + return false; +} + +static inline struct acpi_data_node *to_acpi_data_node(struct fwnode_handle *fwnode) { return NULL; } @@ -743,17 +758,16 @@ struct acpi_reference_args { #ifdef CONFIG_ACPI int acpi_dev_get_property(struct acpi_device *adev, const char *name, acpi_object_type type, const union acpi_object **obj); -int acpi_dev_get_property_array(struct acpi_device *adev, const char *name, - acpi_object_type type, - const union acpi_object **obj); int acpi_dev_get_property_reference(struct acpi_device *adev, const char *name, size_t index, struct acpi_reference_args *args); -int acpi_dev_prop_get(struct acpi_device *adev, const char *propname, - void **valptr); +int acpi_node_prop_get(struct fwnode_handle *fwnode, const char *propname, + void **valptr); int acpi_dev_prop_read_single(struct acpi_device *adev, const char *propname, enum dev_prop_type proptype, void *val); +int acpi_node_prop_read(struct fwnode_handle *fwnode, const char *propname, + enum dev_prop_type proptype, void *val, size_t nval); int acpi_dev_prop_read(struct acpi_device *adev, const char *propname, enum dev_prop_type proptype, void *val, size_t nval); @@ -766,13 +780,7 @@ static inline int acpi_dev_get_property(struct acpi_device *adev, { return -ENXIO; } -static inline int acpi_dev_get_property_array(struct acpi_device *adev, - const char *name, - acpi_object_type type, - const union acpi_object **obj) -{ - return -ENXIO; -} + static inline int acpi_dev_get_property_reference(struct acpi_device *adev, const char *name, const char *cells_name, size_t index, struct acpi_reference_args *args) @@ -780,6 +788,13 @@ static inline int acpi_dev_get_property_reference(struct acpi_device *adev, return -ENXIO; } +static inline int acpi_node_prop_get(struct fwnode_handle *fwnode, + const char *propname, + void **valptr) +{ + return -ENXIO; +} + static inline int acpi_dev_prop_get(struct acpi_device *adev, const char *propname, void **valptr) @@ -795,6 +810,14 @@ static inline int acpi_dev_prop_read_single(struct acpi_device *adev, return -ENXIO; } +static inline int acpi_node_prop_read(struct fwnode_handle *fwnode, + const char *propname, + enum dev_prop_type proptype, + void *val, size_t nval) +{ + return -ENXIO; +} + static inline int acpi_dev_prop_read(struct acpi_device *adev, const char *propname, enum dev_prop_type proptype, -- cgit v0.10.2 From d079524a33977dc08ea15650a21a1664a7313941 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 27 Aug 2015 04:41:33 +0200 Subject: ACPI / gpio: Split acpi_get_gpiod_by_index() Split acpi_get_gpiod_by_index() into three smaller routines to allow the subsequent change of the generic firmware node properties code to be more strarightforward. Signed-off-by: Rafael J. Wysocki Tested-by: Mika Westerberg Acked-by: Linus Walleij diff --git a/drivers/gpio/gpiolib-acpi.c b/drivers/gpio/gpiolib-acpi.c index 143a9bd..a38cf16 100644 --- a/drivers/gpio/gpiolib-acpi.c +++ b/drivers/gpio/gpiolib-acpi.c @@ -389,6 +389,8 @@ struct acpi_gpio_lookup { struct acpi_gpio_info info; int index; int pin_index; + bool active_low; + struct acpi_device *adev; struct gpio_desc *desc; int n; }; @@ -425,6 +427,59 @@ static int acpi_find_gpio(struct acpi_resource *ares, void *data) return 1; } +static int acpi_gpio_resource_lookup(struct acpi_gpio_lookup *lookup, + struct acpi_gpio_info *info) +{ + struct list_head res_list; + int ret; + + INIT_LIST_HEAD(&res_list); + + ret = acpi_dev_get_resources(lookup->adev, &res_list, acpi_find_gpio, + lookup); + if (ret < 0) + return ret; + + acpi_dev_free_resource_list(&res_list); + + if (!lookup->desc) + return -ENOENT; + + if (info) { + *info = lookup->info; + if (lookup->active_low) + info->active_low = lookup->active_low; + } + return 0; +} + +static int acpi_gpio_property_lookup(struct acpi_device *adev, + const char *propname, int index, + struct acpi_gpio_lookup *lookup) +{ + struct acpi_reference_args args; + int ret; + + memset(&args, 0, sizeof(args)); + ret = acpi_dev_get_property_reference(adev, propname, index, &args); + if (ret && !acpi_get_driver_gpio_data(adev, propname, index, &args)) + return ret; + + /* + * The property was found and resolved, so need to lookup the GPIO based + * on returned args. + */ + lookup->adev = args.adev; + if (args.nargs >= 2) { + lookup->index = args.args[0]; + lookup->pin_index = args.args[1]; + /* 3rd argument, if present is used to specify active_low. */ + if (args.nargs >= 3) + lookup->active_low = !!args.args[2]; + } + return 0; +} + /** * acpi_get_gpiod_by_index() - get a GPIO descriptor from device resources * @adev: pointer to a ACPI device to get GPIO from @@ -452,8 +507,6 @@ struct gpio_desc *acpi_get_gpiod_by_index(struct acpi_device *adev, struct acpi_gpio_info *info) { struct acpi_gpio_lookup lookup; - struct list_head resource_list; - bool active_low = false; int ret; if (!adev) @@ -463,58 +516,22 @@ struct gpio_desc *acpi_get_gpiod_by_index(struct acpi_device *adev, lookup.index = index; if (propname) { - struct acpi_reference_args args; - dev_dbg(&adev->dev, "GPIO: looking up %s\n", propname); - memset(&args, 0, sizeof(args)); - ret = acpi_dev_get_property_reference(adev, propname, - index, &args); - if (ret) { - bool found = acpi_get_driver_gpio_data(adev, propname, - index, &args); - if (!found) - return ERR_PTR(ret); - } + ret = acpi_gpio_property_lookup(adev, propname, index, &lookup); + if (ret) + return ERR_PTR(ret); - /* - * The property was found and resolved so need to - * lookup the GPIO based on returned args instead. - */ - adev = args.adev; - if (args.nargs >= 2) { - lookup.index = args.args[0]; - lookup.pin_index = args.args[1]; - /* - * 3rd argument, if present is used to - * specify active_low. - */ - if (args.nargs >= 3) - active_low = !!args.args[2]; - } - - dev_dbg(&adev->dev, "GPIO: _DSD returned %s %zd %llu %llu %llu\n", - dev_name(&adev->dev), args.nargs, - args.args[0], args.args[1], args.args[2]); + dev_dbg(&adev->dev, "GPIO: _DSD returned %s %d %d %u\n", + dev_name(&lookup.adev->dev), lookup.index, + lookup.pin_index, lookup.active_low); } else { dev_dbg(&adev->dev, "GPIO: looking up %d in _CRS\n", index); + lookup.adev = adev; } - INIT_LIST_HEAD(&resource_list); - ret = acpi_dev_get_resources(adev, &resource_list, acpi_find_gpio, - &lookup); - if (ret < 0) - return ERR_PTR(ret); - - acpi_dev_free_resource_list(&resource_list); - - if (lookup.desc && info) { - *info = lookup.info; - if (active_low) - info->active_low = active_low; - } - - return lookup.desc ? lookup.desc : ERR_PTR(-ENOENT); + ret = acpi_gpio_resource_lookup(&lookup, info); + return ret ? ERR_PTR(ret) : lookup.desc; } /** -- cgit v0.10.2 From 504a33749971c36c54ba5ccb1364872dee1f17a7 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 27 Aug 2015 04:42:33 +0200 Subject: ACPI / property: Extend device_get_next_child_node() to data-only nodes Make device_get_next_child_node() work with ACPI data-only subnodes introduced previously. Namely, replace acpi_get_next_child() with acpi_get_next_subnode() that can handle (and return) child device objects as well as child data-only subnodes of the given device and modify the ACPI part of the GPIO subsystem to handle data-only subnodes returned by it. To that end, introduce acpi_node_get_gpiod() taking a struct fwnode_handle pointer as the first argument. That argument may point to an ACPI device object as well as to a data-only subnode and the function should do the right thing (ie. look for the matching GPIO descriptor correctly) in either case. Next, modify fwnode_get_named_gpiod() to use acpi_node_get_gpiod() instead of acpi_get_gpiod_by_index() which automatically causes devm_get_gpiod_from_child() to work with ACPI data-only subnodes that may be returned by device_get_next_child_node() which in turn is required by the users of that function (the gpio_keys_polled and gpio-leds drivers). Signed-off-by: Rafael J. Wysocki Tested-by: Mika Westerberg Acked-by: Linus Walleij diff --git a/drivers/acpi/property.c b/drivers/acpi/property.c index e785517..1465443 100644 --- a/drivers/acpi/property.c +++ b/drivers/acpi/property.c @@ -461,9 +461,9 @@ static int acpi_data_get_property_array(struct acpi_device_data *data, } /** - * acpi_dev_get_property_reference - returns handle to the referenced object - * @adev: ACPI device to get property - * @name: Name of the property + * acpi_data_get_property_reference - returns handle to the referenced object + * @data: ACPI device data object containing the property + * @propname: Name of the property * @index: Index of the reference to return * @args: Location to store the returned reference with optional arguments * @@ -477,16 +477,16 @@ static int acpi_data_get_property_array(struct acpi_device_data *data, * * Return: %0 on success, negative error code on failure. */ -int acpi_dev_get_property_reference(struct acpi_device *adev, - const char *name, size_t index, - struct acpi_reference_args *args) +static int acpi_data_get_property_reference(struct acpi_device_data *data, + const char *propname, size_t index, + struct acpi_reference_args *args) { const union acpi_object *element, *end; const union acpi_object *obj; struct acpi_device *device; int ret, idx = 0; - ret = acpi_dev_get_property(adev, name, ACPI_TYPE_ANY, &obj); + ret = acpi_data_get_property(data, propname, ACPI_TYPE_ANY, &obj); if (ret) return ret; @@ -561,7 +561,23 @@ int acpi_dev_get_property_reference(struct acpi_device *adev, return -EPROTO; } -EXPORT_SYMBOL_GPL(acpi_dev_get_property_reference); + +/** + * acpi_node_get_property_reference - get a handle to the referenced object. + * @fwnode: Firmware node to get the property from. + * @propname: Name of the property. + * @index: Index of the reference to return. + * @args: Location to store the returned reference with optional arguments. + */ +int acpi_node_get_property_reference(struct fwnode_handle *fwnode, + const char *name, size_t index, + struct acpi_reference_args *args) +{ + struct acpi_device_data *data = acpi_device_data_of_node(fwnode); + + return data ? acpi_data_get_property_reference(data, name, index, args) : -EINVAL; +} +EXPORT_SYMBOL_GPL(acpi_node_get_property_reference); static int acpi_data_prop_read_single(struct acpi_device_data *data, const char *propname, @@ -768,3 +784,59 @@ int acpi_node_prop_read(struct fwnode_handle *fwnode, const char *propname, return acpi_data_prop_read(acpi_device_data_of_node(fwnode), propname, proptype, val, nval); } + +/** + * acpi_get_next_subnode - Return the next child node handle for a device. + * @dev: Device to find the next child node for. + * @child: Handle to one of the device's child nodes or a null handle. + */ +struct fwnode_handle *acpi_get_next_subnode(struct device *dev, + struct fwnode_handle *child) +{ + struct acpi_device *adev = ACPI_COMPANION(dev); + struct list_head *head, *next; + + if (!adev) + return NULL; + + if (!child || child->type == FWNODE_ACPI) { + head = &adev->children; + if (list_empty(head)) + goto nondev; + + if (child) { + adev = to_acpi_device_node(child); + next = adev->node.next; + if (next == head) { + child = NULL; + goto nondev; + } + adev = list_entry(next, struct acpi_device, node); + } else { + adev = list_first_entry(head, struct acpi_device, node); + } + return acpi_fwnode_handle(adev); + } + + nondev: + if (!child || child->type == FWNODE_ACPI_DATA) { + struct acpi_data_node *dn; + + head = &adev->data.subnodes; + if (list_empty(head)) + return NULL; + + if (child) { + dn = to_acpi_data_node(child); + next = dn->sibling.next; + if (next == head) + return NULL; + + dn = list_entry(next, struct acpi_data_node, sibling); + } else { + dn = list_first_entry(head, struct acpi_data_node, sibling); + } + return &dn->fwnode; + } + return NULL; +} diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 01136b8..d1ce377 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -695,26 +695,6 @@ int acpi_device_add(struct acpi_device *device, return result; } -struct acpi_device *acpi_get_next_child(struct device *dev, - struct acpi_device *child) -{ - struct acpi_device *adev = ACPI_COMPANION(dev); - struct list_head *head, *next; - - if (!adev) - return NULL; - - head = &adev->children; - if (list_empty(head)) - return NULL; - - if (!child) - return list_first_entry(head, struct acpi_device, node); - - next = child->node.next; - return next == head ? NULL : list_entry(next, struct acpi_device, node); -} - /* -------------------------------------------------------------------------- Device Enumeration -------------------------------------------------------------------------- */ diff --git a/drivers/base/property.c b/drivers/base/property.c index ca11816..660ecec 100644 --- a/drivers/base/property.c +++ b/drivers/base/property.c @@ -493,11 +493,7 @@ struct fwnode_handle *device_get_next_child_node(struct device *dev, if (node) return &node->fwnode; } else if (IS_ENABLED(CONFIG_ACPI)) { - struct acpi_device *node; - - node = acpi_get_next_child(dev, to_acpi_device_node(child)); - if (node) - return acpi_fwnode_handle(node); + return acpi_get_next_subnode(dev, child); } return NULL; } diff --git a/drivers/gpio/gpiolib-acpi.c b/drivers/gpio/gpiolib-acpi.c index a38cf16..69a8362 100644 --- a/drivers/gpio/gpiolib-acpi.c +++ b/drivers/gpio/gpiolib-acpi.c @@ -453,7 +453,7 @@ static int acpi_gpio_resource_lookup(struct acpi_gpio_lookup *lookup, return 0; } -static int acpi_gpio_property_lookup(struct acpi_device *adev, +static int acpi_gpio_property_lookup(struct fwnode_handle *fwnode, const char *propname, int index, struct acpi_gpio_lookup *lookup) { @@ -461,10 +461,16 @@ static int acpi_gpio_property_lookup(struct acpi_device *adev, int ret; memset(&args, 0, sizeof(args)); - ret = acpi_dev_get_property_reference(adev, propname, index, &args); - if (ret && !acpi_get_driver_gpio_data(adev, propname, index, &args)) - return ret; + ret = acpi_node_get_property_reference(fwnode, propname, index, &args); + if (ret) { + struct acpi_device *adev = to_acpi_device_node(fwnode); + if (!adev) + return ret; + + if (!acpi_get_driver_gpio_data(adev, propname, index, &args)) + return ret; + } /* * The property was found and resolved, so need to lookup the GPIO based * on returned args. @@ -518,7 +524,8 @@ struct gpio_desc *acpi_get_gpiod_by_index(struct acpi_device *adev, if (propname) { dev_dbg(&adev->dev, "GPIO: looking up %s\n", propname); - ret = acpi_gpio_property_lookup(adev, propname, index, &lookup); + ret = acpi_gpio_property_lookup(acpi_fwnode_handle(adev), + propname, index, &lookup); if (ret) return ERR_PTR(ret); @@ -535,6 +542,47 @@ struct gpio_desc *acpi_get_gpiod_by_index(struct acpi_device *adev, } /** + * acpi_node_get_gpiod() - get a GPIO descriptor from ACPI resources + * @fwnode: pointer to an ACPI firmware node to get the GPIO information from + * @propname: Property name of the GPIO + * @index: index of GpioIo/GpioInt resource (starting from %0) + * @info: info pointer to fill in (optional) + * + * If @fwnode is an ACPI device object, call %acpi_get_gpiod_by_index() for it. + * Otherwise (ie. it is a data-only non-device object), use the property-based + * GPIO lookup to get to the GPIO resource with the relevant information and use + * that to obtain the GPIO descriptor to return. + */ +struct gpio_desc *acpi_node_get_gpiod(struct fwnode_handle *fwnode, + const char *propname, int index, + struct acpi_gpio_info *info) +{ + struct acpi_gpio_lookup lookup; + struct acpi_device *adev; + int ret; + + adev = to_acpi_device_node(fwnode); + if (adev) + return acpi_get_gpiod_by_index(adev, propname, index, info); + + if (!is_acpi_data_node(fwnode)) + return ERR_PTR(-ENODEV); + + if (!propname) + return ERR_PTR(-EINVAL); + + memset(&lookup, 0, sizeof(lookup)); + lookup.index = index; + + ret = acpi_gpio_property_lookup(fwnode, propname, index, &lookup); + if (ret) + return ERR_PTR(ret); + + ret = acpi_gpio_resource_lookup(&lookup, info); + return ret ? ERR_PTR(ret) : lookup.desc; +} + +/** * acpi_dev_gpio_irq_get() - Find GpioInt and translate it to Linux IRQ number * @adev: pointer to a ACPI device to get IRQ from * @index: index of GpioInt resource (starting from %0) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index f43e808..7d61b50 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -2083,11 +2083,10 @@ struct gpio_desc *fwnode_get_named_gpiod(struct fwnode_handle *fwnode, &flags); if (!IS_ERR(desc)) active_low = flags & OF_GPIO_ACTIVE_LOW; - } else if (is_acpi_device_node(fwnode)) { + } else if (is_acpi_node(fwnode)) { struct acpi_gpio_info info; - desc = acpi_get_gpiod_by_index(to_acpi_device_node(fwnode), - propname, 0, &info); + desc = acpi_node_get_gpiod(fwnode, propname, 0, &info); if (!IS_ERR(desc)) active_low = info.active_low; } diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h index bf34300..e69c715 100644 --- a/drivers/gpio/gpiolib.h +++ b/drivers/gpio/gpiolib.h @@ -42,6 +42,9 @@ void acpi_gpiochip_free_interrupts(struct gpio_chip *chip); struct gpio_desc *acpi_get_gpiod_by_index(struct acpi_device *adev, const char *propname, int index, struct acpi_gpio_info *info); +struct gpio_desc *acpi_node_get_gpiod(struct fwnode_handle *fwnode, + const char *propname, int index, + struct acpi_gpio_info *info); int acpi_gpio_count(struct device *dev, const char *con_id); #else @@ -60,7 +63,12 @@ acpi_get_gpiod_by_index(struct acpi_device *adev, const char *propname, { return ERR_PTR(-ENOSYS); } - +static inline struct gpio_desc * +acpi_node_get_gpiod(struct fwnode_handle *fwnode, const char *propname, + int index, struct acpi_gpio_info *info) +{ + return ERR_PTR(-ENXIO); +} static inline int acpi_gpio_count(struct device *dev, const char *con_id) { return -ENODEV; diff --git a/include/linux/acpi.h b/include/linux/acpi.h index 6be94ba..865d948 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -758,9 +758,9 @@ struct acpi_reference_args { #ifdef CONFIG_ACPI int acpi_dev_get_property(struct acpi_device *adev, const char *name, acpi_object_type type, const union acpi_object **obj); -int acpi_dev_get_property_reference(struct acpi_device *adev, - const char *name, size_t index, - struct acpi_reference_args *args); +int acpi_node_get_property_reference(struct fwnode_handle *fwnode, + const char *name, size_t index, + struct acpi_reference_args *args); int acpi_node_prop_get(struct fwnode_handle *fwnode, const char *propname, void **valptr); @@ -771,8 +771,8 @@ int acpi_node_prop_read(struct fwnode_handle *fwnode, const char *propname, int acpi_dev_prop_read(struct acpi_device *adev, const char *propname, enum dev_prop_type proptype, void *val, size_t nval); -struct acpi_device *acpi_get_next_child(struct device *dev, - struct acpi_device *child); +struct fwnode_handle *acpi_get_next_subnode(struct device *dev, + struct fwnode_handle *subnode); #else static inline int acpi_dev_get_property(struct acpi_device *adev, const char *name, acpi_object_type type, @@ -781,7 +781,7 @@ static inline int acpi_dev_get_property(struct acpi_device *adev, return -ENXIO; } -static inline int acpi_dev_get_property_reference(struct acpi_device *adev, +static inline int acpi_node_get_property_reference(struct fwnode_handle *fwnode, const char *name, const char *cells_name, size_t index, struct acpi_reference_args *args) { @@ -826,12 +826,11 @@ static inline int acpi_dev_prop_read(struct acpi_device *adev, return -ENXIO; } -static inline struct acpi_device *acpi_get_next_child(struct device *dev, - struct acpi_device *child) +static inline struct fwnode_handle *acpi_get_next_subnode(struct device *dev, + struct fwnode_handle *subnode) { return NULL; } - #endif #endif /*_LINUX_ACPI_H*/ -- cgit v0.10.2 From 1840995c52d44bec8c20d6c07a706dc1499da9da Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Fri, 4 Sep 2015 13:47:22 +0530 Subject: PM / OPP: reuse of_parse_phandle() We already have a better API to get the opp descriptor block's node from cpu-node. Lets reuse that instead of creating our own routines for the same stuff. That cleans the code a lot. This also kills a check we had earlier (as we are using the generic API now). Earlier we used to check if the operating-points-v2 property contained multiple phandles instead of a single phandle. Killing this check isn't an issue because, we only parse the first entry with of_parse_phandle(). So, if a user passes multiple phandles, its really his problem :) Reviewed-by: Stephen Boyd Signed-off-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki diff --git a/drivers/base/power/opp.c b/drivers/base/power/opp.c index 28cd75c..c3a7386 100644 --- a/drivers/base/power/opp.c +++ b/drivers/base/power/opp.c @@ -1281,69 +1281,33 @@ void of_cpumask_free_opp_table(cpumask_var_t cpumask) } EXPORT_SYMBOL_GPL(of_cpumask_free_opp_table); -/* Returns opp descriptor node from its phandle. Caller must do of_node_put() */ -static struct device_node * -_of_get_opp_desc_node_from_prop(struct device *dev, const struct property *prop) -{ - struct device_node *opp_np; - - opp_np = of_find_node_by_phandle(be32_to_cpup(prop->value)); - if (!opp_np) { - dev_err(dev, "%s: Prop: %s contains invalid opp desc phandle\n", - __func__, prop->name); - return ERR_PTR(-EINVAL); - } - - return opp_np; -} - -/* Returns opp descriptor node for a device. Caller must do of_node_put() */ +/* Returns opp descriptor node for a device, caller must do of_node_put() */ static struct device_node *_of_get_opp_desc_node(struct device *dev) { - const struct property *prop; - - prop = of_find_property(dev->of_node, "operating-points-v2", NULL); - if (!prop) - return ERR_PTR(-ENODEV); - if (!prop->value) - return ERR_PTR(-ENODATA); - /* * TODO: Support for multiple OPP tables. * * There should be only ONE phandle present in "operating-points-v2" * property. */ - if (prop->length != sizeof(__be32)) { - dev_err(dev, "%s: Invalid opp desc phandle\n", __func__); - return ERR_PTR(-EINVAL); - } - return _of_get_opp_desc_node_from_prop(dev, prop); + return of_parse_phandle(dev->of_node, "operating-points-v2", 0); } /* Initializes OPP tables based on new bindings */ static int _of_init_opp_table_v2(struct device *dev, - const struct property *prop) + struct device_node *opp_np) { - struct device_node *opp_np, *np; + struct device_node *np; struct device_opp *dev_opp; int ret = 0, count = 0; - if (!prop->value) - return -ENODATA; - - /* Get opp node */ - opp_np = _of_get_opp_desc_node_from_prop(dev, prop); - if (IS_ERR(opp_np)) - return PTR_ERR(opp_np); - dev_opp = _managed_opp(opp_np); if (dev_opp) { /* OPPs are already managed */ if (!_add_list_dev(dev, dev_opp)) ret = -ENOMEM; - goto put_opp_np; + return ret; } /* We have opp-list node now, iterate over it and add OPPs */ @@ -1359,10 +1323,8 @@ static int _of_init_opp_table_v2(struct device *dev, } /* There should be one of more OPP defined */ - if (WARN_ON(!count)) { - ret = -ENOENT; - goto put_opp_np; - } + if (WARN_ON(!count)) + return -ENOENT; dev_opp = _find_device_opp(dev); if (WARN_ON(IS_ERR(dev_opp))) { @@ -1373,13 +1335,10 @@ static int _of_init_opp_table_v2(struct device *dev, dev_opp->np = opp_np; dev_opp->shared_opp = of_property_read_bool(opp_np, "opp-shared"); - of_node_put(opp_np); return 0; free_table: of_free_opp_table(dev); -put_opp_np: - of_node_put(opp_np); return ret; } @@ -1446,14 +1405,15 @@ static int _of_init_opp_table_v1(struct device *dev) */ int of_init_opp_table(struct device *dev) { - const struct property *prop; + struct device_node *opp_np; + int ret; /* * OPPs have two version of bindings now. The older one is deprecated, * try for the new binding first. */ - prop = of_find_property(dev->of_node, "operating-points-v2", NULL); - if (!prop) { + opp_np = _of_get_opp_desc_node(dev); + if (!opp_np) { /* * Try old-deprecated bindings for backward compatibility with * older dtbs. @@ -1461,7 +1421,10 @@ int of_init_opp_table(struct device *dev) return _of_init_opp_table_v1(dev); } - return _of_init_opp_table_v2(dev, prop); + ret = _of_init_opp_table_v2(dev, opp_np); + of_node_put(opp_np); + + return ret; } EXPORT_SYMBOL_GPL(of_init_opp_table); @@ -1550,7 +1513,7 @@ int of_get_cpus_sharing_opps(struct device *cpu_dev, cpumask_var_t cpumask) /* Get OPP descriptor node */ np = _of_get_opp_desc_node(cpu_dev); - if (IS_ERR(np)) { + if (!np) { dev_dbg(cpu_dev, "%s: Couldn't find opp node: %ld\n", __func__, PTR_ERR(np)); return -ENOENT; @@ -1574,7 +1537,7 @@ int of_get_cpus_sharing_opps(struct device *cpu_dev, cpumask_var_t cpumask) /* Get OPP descriptor node */ tmp_np = _of_get_opp_desc_node(tcpu_dev); - if (IS_ERR(tmp_np)) { + if (!tmp_np) { dev_err(tcpu_dev, "%s: Couldn't find opp node: %ld\n", __func__, PTR_ERR(tmp_np)); ret = PTR_ERR(tmp_np); -- cgit v0.10.2 From f0489a5ef4d011e29f78021ad13a543e8769d619 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Fri, 4 Sep 2015 13:47:23 +0530 Subject: PM / OPP: Rename opp init/free table routines free-table routines are opposite of init-table ones, and must be named to make that clear. Opposite of 'init' is 'exit', but those doesn't suit really well. Replace 'init' with 'add' and 'free' with 'remove'. Reported-by: Pavel Machek Reviewed-by: Stephen Boyd Signed-off-by: Viresh Kumar Acked-by: Shawn Guo Signed-off-by: Rafael J. Wysocki diff --git a/arch/arm/mach-imx/mach-imx6q.c b/arch/arm/mach-imx/mach-imx6q.c index 9602cc1..3286eec 100644 --- a/arch/arm/mach-imx/mach-imx6q.c +++ b/arch/arm/mach-imx/mach-imx6q.c @@ -350,7 +350,7 @@ static void __init imx6q_opp_init(void) return; } - if (of_init_opp_table(cpu_dev)) { + if (of_add_opp_table(cpu_dev)) { pr_warn("failed to init OPP table\n"); goto put_node; } diff --git a/drivers/base/power/opp.c b/drivers/base/power/opp.c index c3a7386..9f0a292 100644 --- a/drivers/base/power/opp.c +++ b/drivers/base/power/opp.c @@ -828,8 +828,8 @@ static int _opp_add(struct device *dev, struct dev_pm_opp *new_opp, * The opp is made available by default and it can be controlled using * dev_pm_opp_enable/disable functions and may be removed by dev_pm_opp_remove. * - * NOTE: "dynamic" parameter impacts OPPs added by the of_init_opp_table and - * freed by of_free_opp_table. + * NOTE: "dynamic" parameter impacts OPPs added by the of_add_opp_table and + * freed by of_remove_opp_table. * * Locking: The internal device_opp and opp structures are RCU protected. * Hence this function internally uses RCU updater strategy with mutex locks @@ -1213,7 +1213,7 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_get_notifier); #ifdef CONFIG_OF /** - * of_free_opp_table() - Free OPP table entries created from static DT entries + * of_remove_opp_table() - Free OPP table entries created from static DT entries * @dev: device pointer used to lookup device OPPs. * * Free OPPs created using static entries present in DT. @@ -1224,7 +1224,7 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_get_notifier); * that this function is *NOT* called under RCU protection or in contexts where * mutex cannot be locked. */ -void of_free_opp_table(struct device *dev) +void of_remove_opp_table(struct device *dev) { struct device_opp *dev_opp; struct dev_pm_opp *opp, *tmp; @@ -1259,9 +1259,9 @@ void of_free_opp_table(struct device *dev) unlock: mutex_unlock(&dev_opp_list_lock); } -EXPORT_SYMBOL_GPL(of_free_opp_table); +EXPORT_SYMBOL_GPL(of_remove_opp_table); -void of_cpumask_free_opp_table(cpumask_var_t cpumask) +void of_cpumask_remove_opp_table(cpumask_var_t cpumask) { struct device *cpu_dev; int cpu; @@ -1276,10 +1276,10 @@ void of_cpumask_free_opp_table(cpumask_var_t cpumask) continue; } - of_free_opp_table(cpu_dev); + of_remove_opp_table(cpu_dev); } } -EXPORT_SYMBOL_GPL(of_cpumask_free_opp_table); +EXPORT_SYMBOL_GPL(of_cpumask_remove_opp_table); /* Returns opp descriptor node for a device, caller must do of_node_put() */ static struct device_node *_of_get_opp_desc_node(struct device *dev) @@ -1295,8 +1295,7 @@ static struct device_node *_of_get_opp_desc_node(struct device *dev) } /* Initializes OPP tables based on new bindings */ -static int _of_init_opp_table_v2(struct device *dev, - struct device_node *opp_np) +static int _of_add_opp_table_v2(struct device *dev, struct device_node *opp_np) { struct device_node *np; struct device_opp *dev_opp; @@ -1338,13 +1337,13 @@ static int _of_init_opp_table_v2(struct device *dev, return 0; free_table: - of_free_opp_table(dev); + of_remove_opp_table(dev); return ret; } /* Initializes OPP tables based on old-deprecated bindings */ -static int _of_init_opp_table_v1(struct device *dev) +static int _of_add_opp_table_v1(struct device *dev) { const struct property *prop; const __be32 *val; @@ -1381,7 +1380,7 @@ static int _of_init_opp_table_v1(struct device *dev) } /** - * of_init_opp_table() - Initialize opp table from device tree + * of_add_opp_table() - Initialize opp table from device tree * @dev: device pointer used to lookup device OPPs. * * Register the initial OPP table with the OPP library for given device. @@ -1403,7 +1402,7 @@ static int _of_init_opp_table_v1(struct device *dev) * -ENODATA when empty 'operating-points' property is found * -EINVAL when invalid entries are found in opp-v2 table */ -int of_init_opp_table(struct device *dev) +int of_add_opp_table(struct device *dev) { struct device_node *opp_np; int ret; @@ -1418,17 +1417,17 @@ int of_init_opp_table(struct device *dev) * Try old-deprecated bindings for backward compatibility with * older dtbs. */ - return _of_init_opp_table_v1(dev); + return _of_add_opp_table_v1(dev); } - ret = _of_init_opp_table_v2(dev, opp_np); + ret = _of_add_opp_table_v2(dev, opp_np); of_node_put(opp_np); return ret; } -EXPORT_SYMBOL_GPL(of_init_opp_table); +EXPORT_SYMBOL_GPL(of_add_opp_table); -int of_cpumask_init_opp_table(cpumask_var_t cpumask) +int of_cpumask_add_opp_table(cpumask_var_t cpumask) { struct device *cpu_dev; int cpu, ret = 0; @@ -1443,20 +1442,20 @@ int of_cpumask_init_opp_table(cpumask_var_t cpumask) continue; } - ret = of_init_opp_table(cpu_dev); + ret = of_add_opp_table(cpu_dev); if (ret) { pr_err("%s: couldn't find opp table for cpu:%d, %d\n", __func__, cpu, ret); /* Free all other OPPs */ - of_cpumask_free_opp_table(cpumask); + of_cpumask_remove_opp_table(cpumask); break; } } return ret; } -EXPORT_SYMBOL_GPL(of_cpumask_init_opp_table); +EXPORT_SYMBOL_GPL(of_cpumask_add_opp_table); /* Required only for V1 bindings, as v2 can manage it from DT itself */ int set_cpus_sharing_opps(struct device *cpu_dev, cpumask_var_t cpumask) diff --git a/drivers/cpufreq/arm_big_little.h b/drivers/cpufreq/arm_big_little.h index a211f7d..d7ac320 100644 --- a/drivers/cpufreq/arm_big_little.h +++ b/drivers/cpufreq/arm_big_little.h @@ -28,7 +28,7 @@ struct cpufreq_arm_bL_ops { /* * This must set opp table for cpu_dev in a similar way as done by - * of_init_opp_table(). + * of_add_opp_table(). */ int (*init_opp_table)(struct device *cpu_dev); diff --git a/drivers/cpufreq/arm_big_little_dt.c b/drivers/cpufreq/arm_big_little_dt.c index 36d91db..47ac0ff 100644 --- a/drivers/cpufreq/arm_big_little_dt.c +++ b/drivers/cpufreq/arm_big_little_dt.c @@ -54,7 +54,7 @@ static int dt_init_opp_table(struct device *cpu_dev) return -ENOENT; } - ret = of_init_opp_table(cpu_dev); + ret = of_add_opp_table(cpu_dev); of_node_put(np); return ret; @@ -82,7 +82,7 @@ static struct cpufreq_arm_bL_ops dt_bL_ops = { .name = "dt-bl", .get_transition_latency = dt_get_transition_latency, .init_opp_table = dt_init_opp_table, - .free_opp_table = of_free_opp_table, + .free_opp_table = of_remove_opp_table, }; static int generic_bL_probe(struct platform_device *pdev) diff --git a/drivers/cpufreq/cpufreq-dt.c b/drivers/cpufreq/cpufreq-dt.c index 7c0d70e..df8333b 100644 --- a/drivers/cpufreq/cpufreq-dt.c +++ b/drivers/cpufreq/cpufreq-dt.c @@ -238,7 +238,7 @@ static int cpufreq_init(struct cpufreq_policy *policy) * * OPPs might be populated at runtime, don't check for error here */ - of_cpumask_init_opp_table(policy->cpus); + of_cpumask_add_opp_table(policy->cpus); /* * But we need OPP table to function so if it is not there let's @@ -368,7 +368,7 @@ out_free_cpufreq_table: out_free_priv: kfree(priv); out_free_opp: - of_cpumask_free_opp_table(policy->cpus); + of_cpumask_remove_opp_table(policy->cpus); out_node_put: of_node_put(np); out_put_reg_clk: @@ -385,7 +385,7 @@ static int cpufreq_exit(struct cpufreq_policy *policy) cpufreq_cooling_unregister(priv->cdev); dev_pm_opp_free_cpufreq_table(priv->cpu_dev, &policy->freq_table); - of_cpumask_free_opp_table(policy->related_cpus); + of_cpumask_remove_opp_table(policy->related_cpus); clk_put(policy->clk); if (!IS_ERR(priv->cpu_reg)) regulator_put(priv->cpu_reg); diff --git a/drivers/cpufreq/exynos5440-cpufreq.c b/drivers/cpufreq/exynos5440-cpufreq.c index 21a90ed..fc46813 100644 --- a/drivers/cpufreq/exynos5440-cpufreq.c +++ b/drivers/cpufreq/exynos5440-cpufreq.c @@ -360,7 +360,7 @@ static int exynos_cpufreq_probe(struct platform_device *pdev) goto err_put_node; } - ret = of_init_opp_table(dvfs_info->dev); + ret = of_add_opp_table(dvfs_info->dev); if (ret) { dev_err(dvfs_info->dev, "failed to init OPP table: %d\n", ret); goto err_put_node; @@ -424,7 +424,7 @@ static int exynos_cpufreq_probe(struct platform_device *pdev) err_free_table: dev_pm_opp_free_cpufreq_table(dvfs_info->dev, &dvfs_info->freq_table); err_free_opp: - of_free_opp_table(dvfs_info->dev); + of_remove_opp_table(dvfs_info->dev); err_put_node: of_node_put(np); dev_err(&pdev->dev, "%s: failed initialization\n", __func__); @@ -435,7 +435,7 @@ static int exynos_cpufreq_remove(struct platform_device *pdev) { cpufreq_unregister_driver(&exynos_driver); dev_pm_opp_free_cpufreq_table(dvfs_info->dev, &dvfs_info->freq_table); - of_free_opp_table(dvfs_info->dev); + of_remove_opp_table(dvfs_info->dev); return 0; } diff --git a/drivers/cpufreq/imx6q-cpufreq.c b/drivers/cpufreq/imx6q-cpufreq.c index 380a90d..3802765 100644 --- a/drivers/cpufreq/imx6q-cpufreq.c +++ b/drivers/cpufreq/imx6q-cpufreq.c @@ -202,7 +202,7 @@ static int imx6q_cpufreq_probe(struct platform_device *pdev) */ num = dev_pm_opp_get_opp_count(cpu_dev); if (num < 0) { - ret = of_init_opp_table(cpu_dev); + ret = of_add_opp_table(cpu_dev); if (ret < 0) { dev_err(cpu_dev, "failed to init OPP table: %d\n", ret); goto put_reg; @@ -312,7 +312,7 @@ free_freq_table: dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table); out_free_opp: if (free_opp) - of_free_opp_table(cpu_dev); + of_remove_opp_table(cpu_dev); put_reg: if (!IS_ERR(arm_reg)) regulator_put(arm_reg); @@ -340,7 +340,7 @@ static int imx6q_cpufreq_remove(struct platform_device *pdev) cpufreq_unregister_driver(&imx6q_cpufreq_driver); dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table); if (free_opp) - of_free_opp_table(cpu_dev); + of_remove_opp_table(cpu_dev); regulator_put(arm_reg); if (!IS_ERR(pu_reg)) regulator_put(pu_reg); diff --git a/drivers/cpufreq/mt8173-cpufreq.c b/drivers/cpufreq/mt8173-cpufreq.c index 49caed2..519e831 100644 --- a/drivers/cpufreq/mt8173-cpufreq.c +++ b/drivers/cpufreq/mt8173-cpufreq.c @@ -344,7 +344,7 @@ static int mtk_cpu_dvfs_info_init(struct mtk_cpu_dvfs_info *info, int cpu) /* Both presence and absence of sram regulator are valid cases. */ sram_reg = regulator_get_exclusive(cpu_dev, "sram"); - ret = of_init_opp_table(cpu_dev); + ret = of_add_opp_table(cpu_dev); if (ret) { pr_warn("no OPP table for cpu%d\n", cpu); goto out_free_resources; @@ -378,7 +378,7 @@ static int mtk_cpu_dvfs_info_init(struct mtk_cpu_dvfs_info *info, int cpu) return 0; out_free_opp_table: - of_free_opp_table(cpu_dev); + of_remove_opp_table(cpu_dev); out_free_resources: if (!IS_ERR(proc_reg)) @@ -404,7 +404,7 @@ static void mtk_cpu_dvfs_info_release(struct mtk_cpu_dvfs_info *info) if (!IS_ERR(info->inter_clk)) clk_put(info->inter_clk); - of_free_opp_table(info->cpu_dev); + of_remove_opp_table(info->cpu_dev); } static int mtk_cpufreq_init(struct cpufreq_policy *policy) diff --git a/include/linux/pm_opp.h b/include/linux/pm_opp.h index e817722..20adcb4 100644 --- a/include/linux/pm_opp.h +++ b/include/linux/pm_opp.h @@ -132,28 +132,28 @@ static inline struct srcu_notifier_head *dev_pm_opp_get_notifier( #endif /* CONFIG_PM_OPP */ #if defined(CONFIG_PM_OPP) && defined(CONFIG_OF) -int of_init_opp_table(struct device *dev); -void of_free_opp_table(struct device *dev); -int of_cpumask_init_opp_table(cpumask_var_t cpumask); -void of_cpumask_free_opp_table(cpumask_var_t cpumask); +int of_add_opp_table(struct device *dev); +void of_remove_opp_table(struct device *dev); +int of_cpumask_add_opp_table(cpumask_var_t cpumask); +void of_cpumask_remove_opp_table(cpumask_var_t cpumask); int of_get_cpus_sharing_opps(struct device *cpu_dev, cpumask_var_t cpumask); int set_cpus_sharing_opps(struct device *cpu_dev, cpumask_var_t cpumask); #else -static inline int of_init_opp_table(struct device *dev) +static inline int of_add_opp_table(struct device *dev) { return -EINVAL; } -static inline void of_free_opp_table(struct device *dev) +static inline void of_remove_opp_table(struct device *dev) { } -static inline int of_cpumask_init_opp_table(cpumask_var_t cpumask) +static inline int of_cpumask_add_opp_table(cpumask_var_t cpumask) { return -ENOSYS; } -static inline void of_cpumask_free_opp_table(cpumask_var_t cpumask) +static inline void of_cpumask_remove_opp_table(cpumask_var_t cpumask) { } -- cgit v0.10.2 From 8f8d37b2537a28b5b2e3cb60dfc85a2a1303f99b Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Fri, 4 Sep 2015 13:47:24 +0530 Subject: PM / OPP: Prefix exported opp routines with dev_pm_opp_ That's the naming convention followed in most of opp core, but few routines didn't follow this, fix them. Reviewed-by: Stephen Boyd Signed-off-by: Viresh Kumar Acked-by: Shawn Guo Signed-off-by: Rafael J. Wysocki diff --git a/arch/arm/mach-imx/mach-imx6q.c b/arch/arm/mach-imx/mach-imx6q.c index 3286eec..3878494b 100644 --- a/arch/arm/mach-imx/mach-imx6q.c +++ b/arch/arm/mach-imx/mach-imx6q.c @@ -350,7 +350,7 @@ static void __init imx6q_opp_init(void) return; } - if (of_add_opp_table(cpu_dev)) { + if (dev_pm_opp_of_add_table(cpu_dev)) { pr_warn("failed to init OPP table\n"); goto put_node; } diff --git a/drivers/base/power/opp.c b/drivers/base/power/opp.c index 9f0a292..aeff1cf 100644 --- a/drivers/base/power/opp.c +++ b/drivers/base/power/opp.c @@ -828,8 +828,8 @@ static int _opp_add(struct device *dev, struct dev_pm_opp *new_opp, * The opp is made available by default and it can be controlled using * dev_pm_opp_enable/disable functions and may be removed by dev_pm_opp_remove. * - * NOTE: "dynamic" parameter impacts OPPs added by the of_add_opp_table and - * freed by of_remove_opp_table. + * NOTE: "dynamic" parameter impacts OPPs added by the dev_pm_opp_of_add_table + * and freed by dev_pm_opp_of_remove_table. * * Locking: The internal device_opp and opp structures are RCU protected. * Hence this function internally uses RCU updater strategy with mutex locks @@ -1213,7 +1213,8 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_get_notifier); #ifdef CONFIG_OF /** - * of_remove_opp_table() - Free OPP table entries created from static DT entries + * dev_pm_opp_of_remove_table() - Free OPP table entries created from static DT + * entries * @dev: device pointer used to lookup device OPPs. * * Free OPPs created using static entries present in DT. @@ -1224,7 +1225,7 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_get_notifier); * that this function is *NOT* called under RCU protection or in contexts where * mutex cannot be locked. */ -void of_remove_opp_table(struct device *dev) +void dev_pm_opp_of_remove_table(struct device *dev) { struct device_opp *dev_opp; struct dev_pm_opp *opp, *tmp; @@ -1259,9 +1260,9 @@ void of_remove_opp_table(struct device *dev) unlock: mutex_unlock(&dev_opp_list_lock); } -EXPORT_SYMBOL_GPL(of_remove_opp_table); +EXPORT_SYMBOL_GPL(dev_pm_opp_of_remove_table); -void of_cpumask_remove_opp_table(cpumask_var_t cpumask) +void dev_pm_opp_of_cpumask_remove_table(cpumask_var_t cpumask) { struct device *cpu_dev; int cpu; @@ -1276,10 +1277,10 @@ void of_cpumask_remove_opp_table(cpumask_var_t cpumask) continue; } - of_remove_opp_table(cpu_dev); + dev_pm_opp_of_remove_table(cpu_dev); } } -EXPORT_SYMBOL_GPL(of_cpumask_remove_opp_table); +EXPORT_SYMBOL_GPL(dev_pm_opp_of_cpumask_remove_table); /* Returns opp descriptor node for a device, caller must do of_node_put() */ static struct device_node *_of_get_opp_desc_node(struct device *dev) @@ -1337,7 +1338,7 @@ static int _of_add_opp_table_v2(struct device *dev, struct device_node *opp_np) return 0; free_table: - of_remove_opp_table(dev); + dev_pm_opp_of_remove_table(dev); return ret; } @@ -1380,7 +1381,7 @@ static int _of_add_opp_table_v1(struct device *dev) } /** - * of_add_opp_table() - Initialize opp table from device tree + * dev_pm_opp_of_add_table() - Initialize opp table from device tree * @dev: device pointer used to lookup device OPPs. * * Register the initial OPP table with the OPP library for given device. @@ -1402,7 +1403,7 @@ static int _of_add_opp_table_v1(struct device *dev) * -ENODATA when empty 'operating-points' property is found * -EINVAL when invalid entries are found in opp-v2 table */ -int of_add_opp_table(struct device *dev) +int dev_pm_opp_of_add_table(struct device *dev) { struct device_node *opp_np; int ret; @@ -1425,9 +1426,9 @@ int of_add_opp_table(struct device *dev) return ret; } -EXPORT_SYMBOL_GPL(of_add_opp_table); +EXPORT_SYMBOL_GPL(dev_pm_opp_of_add_table); -int of_cpumask_add_opp_table(cpumask_var_t cpumask) +int dev_pm_opp_of_cpumask_add_table(cpumask_var_t cpumask) { struct device *cpu_dev; int cpu, ret = 0; @@ -1442,23 +1443,23 @@ int of_cpumask_add_opp_table(cpumask_var_t cpumask) continue; } - ret = of_add_opp_table(cpu_dev); + ret = dev_pm_opp_of_add_table(cpu_dev); if (ret) { pr_err("%s: couldn't find opp table for cpu:%d, %d\n", __func__, cpu, ret); /* Free all other OPPs */ - of_cpumask_remove_opp_table(cpumask); + dev_pm_opp_of_cpumask_remove_table(cpumask); break; } } return ret; } -EXPORT_SYMBOL_GPL(of_cpumask_add_opp_table); +EXPORT_SYMBOL_GPL(dev_pm_opp_of_cpumask_add_table); /* Required only for V1 bindings, as v2 can manage it from DT itself */ -int set_cpus_sharing_opps(struct device *cpu_dev, cpumask_var_t cpumask) +int dev_pm_opp_set_sharing_cpus(struct device *cpu_dev, cpumask_var_t cpumask) { struct device_list_opp *list_dev; struct device_opp *dev_opp; @@ -1496,7 +1497,7 @@ out_rcu_read_unlock: return 0; } -EXPORT_SYMBOL_GPL(set_cpus_sharing_opps); +EXPORT_SYMBOL_GPL(dev_pm_opp_set_sharing_cpus); /* * Works only for OPP v2 bindings. @@ -1504,7 +1505,7 @@ EXPORT_SYMBOL_GPL(set_cpus_sharing_opps); * cpumask should be already set to mask of cpu_dev->id. * Returns -ENOENT if operating-points-v2 bindings aren't supported. */ -int of_get_cpus_sharing_opps(struct device *cpu_dev, cpumask_var_t cpumask) +int dev_pm_opp_of_get_sharing_cpus(struct device *cpu_dev, cpumask_var_t cpumask) { struct device_node *np, *tmp_np; struct device *tcpu_dev; @@ -1554,5 +1555,5 @@ put_cpu_node: of_node_put(np); return ret; } -EXPORT_SYMBOL_GPL(of_get_cpus_sharing_opps); +EXPORT_SYMBOL_GPL(dev_pm_opp_of_get_sharing_cpus); #endif diff --git a/drivers/cpufreq/arm_big_little.h b/drivers/cpufreq/arm_big_little.h index d7ac320..b88889d 100644 --- a/drivers/cpufreq/arm_big_little.h +++ b/drivers/cpufreq/arm_big_little.h @@ -28,7 +28,7 @@ struct cpufreq_arm_bL_ops { /* * This must set opp table for cpu_dev in a similar way as done by - * of_add_opp_table(). + * dev_pm_opp_of_add_table(). */ int (*init_opp_table)(struct device *cpu_dev); diff --git a/drivers/cpufreq/arm_big_little_dt.c b/drivers/cpufreq/arm_big_little_dt.c index 47ac0ff..16ddeef 100644 --- a/drivers/cpufreq/arm_big_little_dt.c +++ b/drivers/cpufreq/arm_big_little_dt.c @@ -54,7 +54,7 @@ static int dt_init_opp_table(struct device *cpu_dev) return -ENOENT; } - ret = of_add_opp_table(cpu_dev); + ret = dev_pm_opp_of_add_table(cpu_dev); of_node_put(np); return ret; @@ -82,7 +82,7 @@ static struct cpufreq_arm_bL_ops dt_bL_ops = { .name = "dt-bl", .get_transition_latency = dt_get_transition_latency, .init_opp_table = dt_init_opp_table, - .free_opp_table = of_remove_opp_table, + .free_opp_table = dev_pm_opp_of_remove_table, }; static int generic_bL_probe(struct platform_device *pdev) diff --git a/drivers/cpufreq/cpufreq-dt.c b/drivers/cpufreq/cpufreq-dt.c index df8333b..90d6408 100644 --- a/drivers/cpufreq/cpufreq-dt.c +++ b/drivers/cpufreq/cpufreq-dt.c @@ -216,7 +216,7 @@ static int cpufreq_init(struct cpufreq_policy *policy) } /* Get OPP-sharing information from "operating-points-v2" bindings */ - ret = of_get_cpus_sharing_opps(cpu_dev, policy->cpus); + ret = dev_pm_opp_of_get_sharing_cpus(cpu_dev, policy->cpus); if (ret) { /* * operating-points-v2 not supported, fallback to old method of @@ -238,7 +238,7 @@ static int cpufreq_init(struct cpufreq_policy *policy) * * OPPs might be populated at runtime, don't check for error here */ - of_cpumask_add_opp_table(policy->cpus); + dev_pm_opp_of_cpumask_add_table(policy->cpus); /* * But we need OPP table to function so if it is not there let's @@ -261,7 +261,7 @@ static int cpufreq_init(struct cpufreq_policy *policy) * OPP tables are initialized only for policy->cpu, do it for * others as well. */ - ret = set_cpus_sharing_opps(cpu_dev, policy->cpus); + ret = dev_pm_opp_set_sharing_cpus(cpu_dev, policy->cpus); if (ret) dev_err(cpu_dev, "%s: failed to mark OPPs as shared: %d\n", __func__, ret); @@ -368,7 +368,7 @@ out_free_cpufreq_table: out_free_priv: kfree(priv); out_free_opp: - of_cpumask_remove_opp_table(policy->cpus); + dev_pm_opp_of_cpumask_remove_table(policy->cpus); out_node_put: of_node_put(np); out_put_reg_clk: @@ -385,7 +385,7 @@ static int cpufreq_exit(struct cpufreq_policy *policy) cpufreq_cooling_unregister(priv->cdev); dev_pm_opp_free_cpufreq_table(priv->cpu_dev, &policy->freq_table); - of_cpumask_remove_opp_table(policy->related_cpus); + dev_pm_opp_of_cpumask_remove_table(policy->related_cpus); clk_put(policy->clk); if (!IS_ERR(priv->cpu_reg)) regulator_put(priv->cpu_reg); diff --git a/drivers/cpufreq/exynos5440-cpufreq.c b/drivers/cpufreq/exynos5440-cpufreq.c index fc46813..c0f3373 100644 --- a/drivers/cpufreq/exynos5440-cpufreq.c +++ b/drivers/cpufreq/exynos5440-cpufreq.c @@ -360,7 +360,7 @@ static int exynos_cpufreq_probe(struct platform_device *pdev) goto err_put_node; } - ret = of_add_opp_table(dvfs_info->dev); + ret = dev_pm_opp_of_add_table(dvfs_info->dev); if (ret) { dev_err(dvfs_info->dev, "failed to init OPP table: %d\n", ret); goto err_put_node; @@ -424,7 +424,7 @@ static int exynos_cpufreq_probe(struct platform_device *pdev) err_free_table: dev_pm_opp_free_cpufreq_table(dvfs_info->dev, &dvfs_info->freq_table); err_free_opp: - of_remove_opp_table(dvfs_info->dev); + dev_pm_opp_of_remove_table(dvfs_info->dev); err_put_node: of_node_put(np); dev_err(&pdev->dev, "%s: failed initialization\n", __func__); @@ -435,7 +435,7 @@ static int exynos_cpufreq_remove(struct platform_device *pdev) { cpufreq_unregister_driver(&exynos_driver); dev_pm_opp_free_cpufreq_table(dvfs_info->dev, &dvfs_info->freq_table); - of_remove_opp_table(dvfs_info->dev); + dev_pm_opp_of_remove_table(dvfs_info->dev); return 0; } diff --git a/drivers/cpufreq/imx6q-cpufreq.c b/drivers/cpufreq/imx6q-cpufreq.c index 3802765..84fbc8e 100644 --- a/drivers/cpufreq/imx6q-cpufreq.c +++ b/drivers/cpufreq/imx6q-cpufreq.c @@ -202,7 +202,7 @@ static int imx6q_cpufreq_probe(struct platform_device *pdev) */ num = dev_pm_opp_get_opp_count(cpu_dev); if (num < 0) { - ret = of_add_opp_table(cpu_dev); + ret = dev_pm_opp_of_add_table(cpu_dev); if (ret < 0) { dev_err(cpu_dev, "failed to init OPP table: %d\n", ret); goto put_reg; @@ -312,7 +312,7 @@ free_freq_table: dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table); out_free_opp: if (free_opp) - of_remove_opp_table(cpu_dev); + dev_pm_opp_of_remove_table(cpu_dev); put_reg: if (!IS_ERR(arm_reg)) regulator_put(arm_reg); @@ -340,7 +340,7 @@ static int imx6q_cpufreq_remove(struct platform_device *pdev) cpufreq_unregister_driver(&imx6q_cpufreq_driver); dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table); if (free_opp) - of_remove_opp_table(cpu_dev); + dev_pm_opp_of_remove_table(cpu_dev); regulator_put(arm_reg); if (!IS_ERR(pu_reg)) regulator_put(pu_reg); diff --git a/drivers/cpufreq/mt8173-cpufreq.c b/drivers/cpufreq/mt8173-cpufreq.c index 519e831..83001dc 100644 --- a/drivers/cpufreq/mt8173-cpufreq.c +++ b/drivers/cpufreq/mt8173-cpufreq.c @@ -344,7 +344,7 @@ static int mtk_cpu_dvfs_info_init(struct mtk_cpu_dvfs_info *info, int cpu) /* Both presence and absence of sram regulator are valid cases. */ sram_reg = regulator_get_exclusive(cpu_dev, "sram"); - ret = of_add_opp_table(cpu_dev); + ret = dev_pm_opp_of_add_table(cpu_dev); if (ret) { pr_warn("no OPP table for cpu%d\n", cpu); goto out_free_resources; @@ -378,7 +378,7 @@ static int mtk_cpu_dvfs_info_init(struct mtk_cpu_dvfs_info *info, int cpu) return 0; out_free_opp_table: - of_remove_opp_table(cpu_dev); + dev_pm_opp_of_remove_table(cpu_dev); out_free_resources: if (!IS_ERR(proc_reg)) @@ -404,7 +404,7 @@ static void mtk_cpu_dvfs_info_release(struct mtk_cpu_dvfs_info *info) if (!IS_ERR(info->inter_clk)) clk_put(info->inter_clk); - of_remove_opp_table(info->cpu_dev); + dev_pm_opp_of_remove_table(info->cpu_dev); } static int mtk_cpufreq_init(struct cpufreq_policy *policy) diff --git a/include/linux/pm_opp.h b/include/linux/pm_opp.h index 20adcb4..9a2e503 100644 --- a/include/linux/pm_opp.h +++ b/include/linux/pm_opp.h @@ -132,37 +132,37 @@ static inline struct srcu_notifier_head *dev_pm_opp_get_notifier( #endif /* CONFIG_PM_OPP */ #if defined(CONFIG_PM_OPP) && defined(CONFIG_OF) -int of_add_opp_table(struct device *dev); -void of_remove_opp_table(struct device *dev); -int of_cpumask_add_opp_table(cpumask_var_t cpumask); -void of_cpumask_remove_opp_table(cpumask_var_t cpumask); -int of_get_cpus_sharing_opps(struct device *cpu_dev, cpumask_var_t cpumask); -int set_cpus_sharing_opps(struct device *cpu_dev, cpumask_var_t cpumask); +int dev_pm_opp_of_add_table(struct device *dev); +void dev_pm_opp_of_remove_table(struct device *dev); +int dev_pm_opp_of_cpumask_add_table(cpumask_var_t cpumask); +void dev_pm_opp_of_cpumask_remove_table(cpumask_var_t cpumask); +int dev_pm_opp_of_get_sharing_cpus(struct device *cpu_dev, cpumask_var_t cpumask); +int dev_pm_opp_set_sharing_cpus(struct device *cpu_dev, cpumask_var_t cpumask); #else -static inline int of_add_opp_table(struct device *dev) +static inline int dev_pm_opp_of_add_table(struct device *dev) { return -EINVAL; } -static inline void of_remove_opp_table(struct device *dev) +static inline void dev_pm_opp_of_remove_table(struct device *dev) { } -static inline int of_cpumask_add_opp_table(cpumask_var_t cpumask) +static inline int dev_pm_opp_of_cpumask_add_table(cpumask_var_t cpumask) { return -ENOSYS; } -static inline void of_cpumask_remove_opp_table(cpumask_var_t cpumask) +static inline void dev_pm_opp_of_cpumask_remove_table(cpumask_var_t cpumask) { } -static inline int of_get_cpus_sharing_opps(struct device *cpu_dev, cpumask_var_t cpumask) +static inline int dev_pm_opp_of_get_sharing_cpus(struct device *cpu_dev, cpumask_var_t cpumask) { return -ENOSYS; } -static inline int set_cpus_sharing_opps(struct device *cpu_dev, cpumask_var_t cpumask) +static inline int dev_pm_opp_set_sharing_cpus(struct device *cpu_dev, cpumask_var_t cpumask) { return -ENOSYS; } -- cgit v0.10.2 From 33692dc381f9b89ddfc408631bf670ac2fd08ffc Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Fri, 4 Sep 2015 13:47:25 +0530 Subject: PM / OPP: Move opp core to its own directory OPP code is expanding and is already present in multiple directories (cpufreq and power). Lets move it to its own directory, to manage it better. This also moves/renames the cpufreq_opp file to cpu.c, as it will contain helpers for cpu device. Its not just about cpufreq, other frameworks can use OPPs as well. Reviewed-by: Stephen Boyd Signed-off-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki diff --git a/drivers/base/power/Makefile b/drivers/base/power/Makefile index f94a6cc..5998c53 100644 --- a/drivers/base/power/Makefile +++ b/drivers/base/power/Makefile @@ -1,7 +1,7 @@ obj-$(CONFIG_PM) += sysfs.o generic_ops.o common.o qos.o runtime.o wakeirq.o obj-$(CONFIG_PM_SLEEP) += main.o wakeup.o obj-$(CONFIG_PM_TRACE_RTC) += trace.o -obj-$(CONFIG_PM_OPP) += opp.o +obj-$(CONFIG_PM_OPP) += opp/ obj-$(CONFIG_PM_GENERIC_DOMAINS) += domain.o domain_governor.o obj-$(CONFIG_HAVE_CLK) += clock_ops.o diff --git a/drivers/base/power/opp.c b/drivers/base/power/opp.c deleted file mode 100644 index aeff1cf..0000000 --- a/drivers/base/power/opp.c +++ /dev/null @@ -1,1559 +0,0 @@ -/* - * Generic OPP Interface - * - * Copyright (C) 2009-2010 Texas Instruments Incorporated. - * Nishanth Menon - * Romit Dasgupta - * Kevin Hilman - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* - * Internal data structure organization with the OPP layer library is as - * follows: - * dev_opp_list (root) - * |- device 1 (represents voltage domain 1) - * | |- opp 1 (availability, freq, voltage) - * | |- opp 2 .. - * ... ... - * | `- opp n .. - * |- device 2 (represents the next voltage domain) - * ... - * `- device m (represents mth voltage domain) - * device 1, 2.. are represented by dev_opp structure while each opp - * is represented by the opp structure. - */ - -/** - * struct dev_pm_opp - Generic OPP description structure - * @node: opp list node. The nodes are maintained throughout the lifetime - * of boot. It is expected only an optimal set of OPPs are - * added to the library by the SoC framework. - * RCU usage: opp list is traversed with RCU locks. node - * modification is possible realtime, hence the modifications - * are protected by the dev_opp_list_lock for integrity. - * IMPORTANT: the opp nodes should be maintained in increasing - * order. - * @dynamic: not-created from static DT entries. - * @available: true/false - marks if this OPP as available or not - * @turbo: true if turbo (boost) OPP - * @rate: Frequency in hertz - * @u_volt: Target voltage in microvolts corresponding to this OPP - * @u_volt_min: Minimum voltage in microvolts corresponding to this OPP - * @u_volt_max: Maximum voltage in microvolts corresponding to this OPP - * @u_amp: Maximum current drawn by the device in microamperes - * @clock_latency_ns: Latency (in nanoseconds) of switching to this OPP's - * frequency from any other OPP's frequency. - * @dev_opp: points back to the device_opp struct this opp belongs to - * @rcu_head: RCU callback head used for deferred freeing - * @np: OPP's device node. - * - * This structure stores the OPP information for a given device. - */ -struct dev_pm_opp { - struct list_head node; - - bool available; - bool dynamic; - bool turbo; - unsigned long rate; - - unsigned long u_volt; - unsigned long u_volt_min; - unsigned long u_volt_max; - unsigned long u_amp; - unsigned long clock_latency_ns; - - struct device_opp *dev_opp; - struct rcu_head rcu_head; - - struct device_node *np; -}; - -/** - * struct device_list_opp - devices managed by 'struct device_opp' - * @node: list node - * @dev: device to which the struct object belongs - * @rcu_head: RCU callback head used for deferred freeing - * - * This is an internal data structure maintaining the list of devices that are - * managed by 'struct device_opp'. - */ -struct device_list_opp { - struct list_head node; - const struct device *dev; - struct rcu_head rcu_head; -}; - -/** - * struct device_opp - Device opp structure - * @node: list node - contains the devices with OPPs that - * have been registered. Nodes once added are not modified in this - * list. - * RCU usage: nodes are not modified in the list of device_opp, - * however addition is possible and is secured by dev_opp_list_lock - * @srcu_head: notifier head to notify the OPP availability changes. - * @rcu_head: RCU callback head used for deferred freeing - * @dev_list: list of devices that share these OPPs - * @opp_list: list of opps - * @np: struct device_node pointer for opp's DT node. - * @shared_opp: OPP is shared between multiple devices. - * - * This is an internal data structure maintaining the link to opps attached to - * a device. This structure is not meant to be shared to users as it is - * meant for book keeping and private to OPP library. - * - * Because the opp structures can be used from both rcu and srcu readers, we - * need to wait for the grace period of both of them before freeing any - * resources. And so we have used kfree_rcu() from within call_srcu() handlers. - */ -struct device_opp { - struct list_head node; - - struct srcu_notifier_head srcu_head; - struct rcu_head rcu_head; - struct list_head dev_list; - struct list_head opp_list; - - struct device_node *np; - unsigned long clock_latency_ns_max; - bool shared_opp; - struct dev_pm_opp *suspend_opp; -}; - -/* - * The root of the list of all devices. All device_opp structures branch off - * from here, with each device_opp containing the list of opp it supports in - * various states of availability. - */ -static LIST_HEAD(dev_opp_list); -/* Lock to allow exclusive modification to the device and opp lists */ -static DEFINE_MUTEX(dev_opp_list_lock); - -#define opp_rcu_lockdep_assert() \ -do { \ - RCU_LOCKDEP_WARN(!rcu_read_lock_held() && \ - !lockdep_is_held(&dev_opp_list_lock), \ - "Missing rcu_read_lock() or " \ - "dev_opp_list_lock protection"); \ -} while (0) - -static struct device_list_opp *_find_list_dev(const struct device *dev, - struct device_opp *dev_opp) -{ - struct device_list_opp *list_dev; - - list_for_each_entry(list_dev, &dev_opp->dev_list, node) - if (list_dev->dev == dev) - return list_dev; - - return NULL; -} - -static struct device_opp *_managed_opp(const struct device_node *np) -{ - struct device_opp *dev_opp; - - list_for_each_entry_rcu(dev_opp, &dev_opp_list, node) { - if (dev_opp->np == np) { - /* - * Multiple devices can point to the same OPP table and - * so will have same node-pointer, np. - * - * But the OPPs will be considered as shared only if the - * OPP table contains a "opp-shared" property. - */ - return dev_opp->shared_opp ? dev_opp : NULL; - } - } - - return NULL; -} - -/** - * _find_device_opp() - find device_opp struct using device pointer - * @dev: device pointer used to lookup device OPPs - * - * Search list of device OPPs for one containing matching device. Does a RCU - * reader operation to grab the pointer needed. - * - * Return: pointer to 'struct device_opp' if found, otherwise -ENODEV or - * -EINVAL based on type of error. - * - * Locking: This function must be called under rcu_read_lock(). device_opp - * is a RCU protected pointer. This means that device_opp is valid as long - * as we are under RCU lock. - */ -static struct device_opp *_find_device_opp(struct device *dev) -{ - struct device_opp *dev_opp; - - if (IS_ERR_OR_NULL(dev)) { - pr_err("%s: Invalid parameters\n", __func__); - return ERR_PTR(-EINVAL); - } - - list_for_each_entry_rcu(dev_opp, &dev_opp_list, node) - if (_find_list_dev(dev, dev_opp)) - return dev_opp; - - return ERR_PTR(-ENODEV); -} - -/** - * dev_pm_opp_get_voltage() - Gets the voltage corresponding to an available opp - * @opp: opp for which voltage has to be returned for - * - * Return: voltage in micro volt corresponding to the opp, else - * return 0 - * - * Locking: This function must be called under rcu_read_lock(). opp is a rcu - * protected pointer. This means that opp which could have been fetched by - * opp_find_freq_{exact,ceil,floor} functions is valid as long as we are - * under RCU lock. The pointer returned by the opp_find_freq family must be - * used in the same section as the usage of this function with the pointer - * prior to unlocking with rcu_read_unlock() to maintain the integrity of the - * pointer. - */ -unsigned long dev_pm_opp_get_voltage(struct dev_pm_opp *opp) -{ - struct dev_pm_opp *tmp_opp; - unsigned long v = 0; - - opp_rcu_lockdep_assert(); - - tmp_opp = rcu_dereference(opp); - if (IS_ERR_OR_NULL(tmp_opp) || !tmp_opp->available) - pr_err("%s: Invalid parameters\n", __func__); - else - v = tmp_opp->u_volt; - - return v; -} -EXPORT_SYMBOL_GPL(dev_pm_opp_get_voltage); - -/** - * dev_pm_opp_get_freq() - Gets the frequency corresponding to an available opp - * @opp: opp for which frequency has to be returned for - * - * Return: frequency in hertz corresponding to the opp, else - * return 0 - * - * Locking: This function must be called under rcu_read_lock(). opp is a rcu - * protected pointer. This means that opp which could have been fetched by - * opp_find_freq_{exact,ceil,floor} functions is valid as long as we are - * under RCU lock. The pointer returned by the opp_find_freq family must be - * used in the same section as the usage of this function with the pointer - * prior to unlocking with rcu_read_unlock() to maintain the integrity of the - * pointer. - */ -unsigned long dev_pm_opp_get_freq(struct dev_pm_opp *opp) -{ - struct dev_pm_opp *tmp_opp; - unsigned long f = 0; - - opp_rcu_lockdep_assert(); - - tmp_opp = rcu_dereference(opp); - if (IS_ERR_OR_NULL(tmp_opp) || !tmp_opp->available) - pr_err("%s: Invalid parameters\n", __func__); - else - f = tmp_opp->rate; - - return f; -} -EXPORT_SYMBOL_GPL(dev_pm_opp_get_freq); - -/** - * dev_pm_opp_is_turbo() - Returns if opp is turbo OPP or not - * @opp: opp for which turbo mode is being verified - * - * Turbo OPPs are not for normal use, and can be enabled (under certain - * conditions) for short duration of times to finish high throughput work - * quickly. Running on them for longer times may overheat the chip. - * - * Return: true if opp is turbo opp, else false. - * - * Locking: This function must be called under rcu_read_lock(). opp is a rcu - * protected pointer. This means that opp which could have been fetched by - * opp_find_freq_{exact,ceil,floor} functions is valid as long as we are - * under RCU lock. The pointer returned by the opp_find_freq family must be - * used in the same section as the usage of this function with the pointer - * prior to unlocking with rcu_read_unlock() to maintain the integrity of the - * pointer. - */ -bool dev_pm_opp_is_turbo(struct dev_pm_opp *opp) -{ - struct dev_pm_opp *tmp_opp; - - opp_rcu_lockdep_assert(); - - tmp_opp = rcu_dereference(opp); - if (IS_ERR_OR_NULL(tmp_opp) || !tmp_opp->available) { - pr_err("%s: Invalid parameters\n", __func__); - return false; - } - - return tmp_opp->turbo; -} -EXPORT_SYMBOL_GPL(dev_pm_opp_is_turbo); - -/** - * dev_pm_opp_get_max_clock_latency() - Get max clock latency in nanoseconds - * @dev: device for which we do this operation - * - * Return: This function returns the max clock latency in nanoseconds. - * - * Locking: This function takes rcu_read_lock(). - */ -unsigned long dev_pm_opp_get_max_clock_latency(struct device *dev) -{ - struct device_opp *dev_opp; - unsigned long clock_latency_ns; - - rcu_read_lock(); - - dev_opp = _find_device_opp(dev); - if (IS_ERR(dev_opp)) - clock_latency_ns = 0; - else - clock_latency_ns = dev_opp->clock_latency_ns_max; - - rcu_read_unlock(); - return clock_latency_ns; -} -EXPORT_SYMBOL_GPL(dev_pm_opp_get_max_clock_latency); - -/** - * dev_pm_opp_get_suspend_opp() - Get suspend opp - * @dev: device for which we do this operation - * - * Return: This function returns pointer to the suspend opp if it is - * defined and available, otherwise it returns NULL. - * - * Locking: This function must be called under rcu_read_lock(). opp is a rcu - * protected pointer. The reason for the same is that the opp pointer which is - * returned will remain valid for use with opp_get_{voltage, freq} only while - * under the locked area. The pointer returned must be used prior to unlocking - * with rcu_read_unlock() to maintain the integrity of the pointer. - */ -struct dev_pm_opp *dev_pm_opp_get_suspend_opp(struct device *dev) -{ - struct device_opp *dev_opp; - - opp_rcu_lockdep_assert(); - - dev_opp = _find_device_opp(dev); - if (IS_ERR(dev_opp) || !dev_opp->suspend_opp || - !dev_opp->suspend_opp->available) - return NULL; - - return dev_opp->suspend_opp; -} -EXPORT_SYMBOL_GPL(dev_pm_opp_get_suspend_opp); - -/** - * dev_pm_opp_get_opp_count() - Get number of opps available in the opp list - * @dev: device for which we do this operation - * - * Return: This function returns the number of available opps if there are any, - * else returns 0 if none or the corresponding error value. - * - * Locking: This function takes rcu_read_lock(). - */ -int dev_pm_opp_get_opp_count(struct device *dev) -{ - struct device_opp *dev_opp; - struct dev_pm_opp *temp_opp; - int count = 0; - - rcu_read_lock(); - - dev_opp = _find_device_opp(dev); - if (IS_ERR(dev_opp)) { - count = PTR_ERR(dev_opp); - dev_err(dev, "%s: device OPP not found (%d)\n", - __func__, count); - goto out_unlock; - } - - list_for_each_entry_rcu(temp_opp, &dev_opp->opp_list, node) { - if (temp_opp->available) - count++; - } - -out_unlock: - rcu_read_unlock(); - return count; -} -EXPORT_SYMBOL_GPL(dev_pm_opp_get_opp_count); - -/** - * dev_pm_opp_find_freq_exact() - search for an exact frequency - * @dev: device for which we do this operation - * @freq: frequency to search for - * @available: true/false - match for available opp - * - * Return: Searches for exact match in the opp list and returns pointer to the - * matching opp if found, else returns ERR_PTR in case of error and should - * be handled using IS_ERR. Error return values can be: - * EINVAL: for bad pointer - * ERANGE: no match found for search - * ENODEV: if device not found in list of registered devices - * - * Note: available is a modifier for the search. if available=true, then the - * match is for exact matching frequency and is available in the stored OPP - * table. if false, the match is for exact frequency which is not available. - * - * This provides a mechanism to enable an opp which is not available currently - * or the opposite as well. - * - * Locking: This function must be called under rcu_read_lock(). opp is a rcu - * protected pointer. The reason for the same is that the opp pointer which is - * returned will remain valid for use with opp_get_{voltage, freq} only while - * under the locked area. The pointer returned must be used prior to unlocking - * with rcu_read_unlock() to maintain the integrity of the pointer. - */ -struct dev_pm_opp *dev_pm_opp_find_freq_exact(struct device *dev, - unsigned long freq, - bool available) -{ - struct device_opp *dev_opp; - struct dev_pm_opp *temp_opp, *opp = ERR_PTR(-ERANGE); - - opp_rcu_lockdep_assert(); - - dev_opp = _find_device_opp(dev); - if (IS_ERR(dev_opp)) { - int r = PTR_ERR(dev_opp); - dev_err(dev, "%s: device OPP not found (%d)\n", __func__, r); - return ERR_PTR(r); - } - - list_for_each_entry_rcu(temp_opp, &dev_opp->opp_list, node) { - if (temp_opp->available == available && - temp_opp->rate == freq) { - opp = temp_opp; - break; - } - } - - return opp; -} -EXPORT_SYMBOL_GPL(dev_pm_opp_find_freq_exact); - -/** - * dev_pm_opp_find_freq_ceil() - Search for an rounded ceil freq - * @dev: device for which we do this operation - * @freq: Start frequency - * - * Search for the matching ceil *available* OPP from a starting freq - * for a device. - * - * Return: matching *opp and refreshes *freq accordingly, else returns - * ERR_PTR in case of error and should be handled using IS_ERR. Error return - * values can be: - * EINVAL: for bad pointer - * ERANGE: no match found for search - * ENODEV: if device not found in list of registered devices - * - * Locking: This function must be called under rcu_read_lock(). opp is a rcu - * protected pointer. The reason for the same is that the opp pointer which is - * returned will remain valid for use with opp_get_{voltage, freq} only while - * under the locked area. The pointer returned must be used prior to unlocking - * with rcu_read_unlock() to maintain the integrity of the pointer. - */ -struct dev_pm_opp *dev_pm_opp_find_freq_ceil(struct device *dev, - unsigned long *freq) -{ - struct device_opp *dev_opp; - struct dev_pm_opp *temp_opp, *opp = ERR_PTR(-ERANGE); - - opp_rcu_lockdep_assert(); - - if (!dev || !freq) { - dev_err(dev, "%s: Invalid argument freq=%p\n", __func__, freq); - return ERR_PTR(-EINVAL); - } - - dev_opp = _find_device_opp(dev); - if (IS_ERR(dev_opp)) - return ERR_CAST(dev_opp); - - list_for_each_entry_rcu(temp_opp, &dev_opp->opp_list, node) { - if (temp_opp->available && temp_opp->rate >= *freq) { - opp = temp_opp; - *freq = opp->rate; - break; - } - } - - return opp; -} -EXPORT_SYMBOL_GPL(dev_pm_opp_find_freq_ceil); - -/** - * dev_pm_opp_find_freq_floor() - Search for a rounded floor freq - * @dev: device for which we do this operation - * @freq: Start frequency - * - * Search for the matching floor *available* OPP from a starting freq - * for a device. - * - * Return: matching *opp and refreshes *freq accordingly, else returns - * ERR_PTR in case of error and should be handled using IS_ERR. Error return - * values can be: - * EINVAL: for bad pointer - * ERANGE: no match found for search - * ENODEV: if device not found in list of registered devices - * - * Locking: This function must be called under rcu_read_lock(). opp is a rcu - * protected pointer. The reason for the same is that the opp pointer which is - * returned will remain valid for use with opp_get_{voltage, freq} only while - * under the locked area. The pointer returned must be used prior to unlocking - * with rcu_read_unlock() to maintain the integrity of the pointer. - */ -struct dev_pm_opp *dev_pm_opp_find_freq_floor(struct device *dev, - unsigned long *freq) -{ - struct device_opp *dev_opp; - struct dev_pm_opp *temp_opp, *opp = ERR_PTR(-ERANGE); - - opp_rcu_lockdep_assert(); - - if (!dev || !freq) { - dev_err(dev, "%s: Invalid argument freq=%p\n", __func__, freq); - return ERR_PTR(-EINVAL); - } - - dev_opp = _find_device_opp(dev); - if (IS_ERR(dev_opp)) - return ERR_CAST(dev_opp); - - list_for_each_entry_rcu(temp_opp, &dev_opp->opp_list, node) { - if (temp_opp->available) { - /* go to the next node, before choosing prev */ - if (temp_opp->rate > *freq) - break; - else - opp = temp_opp; - } - } - if (!IS_ERR(opp)) - *freq = opp->rate; - - return opp; -} -EXPORT_SYMBOL_GPL(dev_pm_opp_find_freq_floor); - -/* List-dev Helpers */ -static void _kfree_list_dev_rcu(struct rcu_head *head) -{ - struct device_list_opp *list_dev; - - list_dev = container_of(head, struct device_list_opp, rcu_head); - kfree_rcu(list_dev, rcu_head); -} - -static void _remove_list_dev(struct device_list_opp *list_dev, - struct device_opp *dev_opp) -{ - list_del(&list_dev->node); - call_srcu(&dev_opp->srcu_head.srcu, &list_dev->rcu_head, - _kfree_list_dev_rcu); -} - -static struct device_list_opp *_add_list_dev(const struct device *dev, - struct device_opp *dev_opp) -{ - struct device_list_opp *list_dev; - - list_dev = kzalloc(sizeof(*list_dev), GFP_KERNEL); - if (!list_dev) - return NULL; - - /* Initialize list-dev */ - list_dev->dev = dev; - list_add_rcu(&list_dev->node, &dev_opp->dev_list); - - return list_dev; -} - -/** - * _add_device_opp() - Find device OPP table or allocate a new one - * @dev: device for which we do this operation - * - * It tries to find an existing table first, if it couldn't find one, it - * allocates a new OPP table and returns that. - * - * Return: valid device_opp pointer if success, else NULL. - */ -static struct device_opp *_add_device_opp(struct device *dev) -{ - struct device_opp *dev_opp; - struct device_list_opp *list_dev; - - /* Check for existing list for 'dev' first */ - dev_opp = _find_device_opp(dev); - if (!IS_ERR(dev_opp)) - return dev_opp; - - /* - * Allocate a new device OPP table. In the infrequent case where a new - * device is needed to be added, we pay this penalty. - */ - dev_opp = kzalloc(sizeof(*dev_opp), GFP_KERNEL); - if (!dev_opp) - return NULL; - - INIT_LIST_HEAD(&dev_opp->dev_list); - - list_dev = _add_list_dev(dev, dev_opp); - if (!list_dev) { - kfree(dev_opp); - return NULL; - } - - srcu_init_notifier_head(&dev_opp->srcu_head); - INIT_LIST_HEAD(&dev_opp->opp_list); - - /* Secure the device list modification */ - list_add_rcu(&dev_opp->node, &dev_opp_list); - return dev_opp; -} - -/** - * _kfree_device_rcu() - Free device_opp RCU handler - * @head: RCU head - */ -static void _kfree_device_rcu(struct rcu_head *head) -{ - struct device_opp *device_opp = container_of(head, struct device_opp, rcu_head); - - kfree_rcu(device_opp, rcu_head); -} - -/** - * _remove_device_opp() - Removes a device OPP table - * @dev_opp: device OPP table to be removed. - * - * Removes/frees device OPP table it it doesn't contain any OPPs. - */ -static void _remove_device_opp(struct device_opp *dev_opp) -{ - struct device_list_opp *list_dev; - - if (!list_empty(&dev_opp->opp_list)) - return; - - list_dev = list_first_entry(&dev_opp->dev_list, struct device_list_opp, - node); - - _remove_list_dev(list_dev, dev_opp); - - /* dev_list must be empty now */ - WARN_ON(!list_empty(&dev_opp->dev_list)); - - list_del_rcu(&dev_opp->node); - call_srcu(&dev_opp->srcu_head.srcu, &dev_opp->rcu_head, - _kfree_device_rcu); -} - -/** - * _kfree_opp_rcu() - Free OPP RCU handler - * @head: RCU head - */ -static void _kfree_opp_rcu(struct rcu_head *head) -{ - struct dev_pm_opp *opp = container_of(head, struct dev_pm_opp, rcu_head); - - kfree_rcu(opp, rcu_head); -} - -/** - * _opp_remove() - Remove an OPP from a table definition - * @dev_opp: points back to the device_opp struct this opp belongs to - * @opp: pointer to the OPP to remove - * @notify: OPP_EVENT_REMOVE notification should be sent or not - * - * This function removes an opp definition from the opp list. - * - * Locking: The internal device_opp and opp structures are RCU protected. - * It is assumed that the caller holds required mutex for an RCU updater - * strategy. - */ -static void _opp_remove(struct device_opp *dev_opp, - struct dev_pm_opp *opp, bool notify) -{ - /* - * Notify the changes in the availability of the operable - * frequency/voltage list. - */ - if (notify) - srcu_notifier_call_chain(&dev_opp->srcu_head, OPP_EVENT_REMOVE, opp); - list_del_rcu(&opp->node); - call_srcu(&dev_opp->srcu_head.srcu, &opp->rcu_head, _kfree_opp_rcu); - - _remove_device_opp(dev_opp); -} - -/** - * dev_pm_opp_remove() - Remove an OPP from OPP list - * @dev: device for which we do this operation - * @freq: OPP to remove with matching 'freq' - * - * This function removes an opp from the opp list. - * - * Locking: The internal device_opp and opp structures are RCU protected. - * Hence this function internally uses RCU updater strategy with mutex locks - * to keep the integrity of the internal data structures. Callers should ensure - * that this function is *NOT* called under RCU protection or in contexts where - * mutex cannot be locked. - */ -void dev_pm_opp_remove(struct device *dev, unsigned long freq) -{ - struct dev_pm_opp *opp; - struct device_opp *dev_opp; - bool found = false; - - /* Hold our list modification lock here */ - mutex_lock(&dev_opp_list_lock); - - dev_opp = _find_device_opp(dev); - if (IS_ERR(dev_opp)) - goto unlock; - - list_for_each_entry(opp, &dev_opp->opp_list, node) { - if (opp->rate == freq) { - found = true; - break; - } - } - - if (!found) { - dev_warn(dev, "%s: Couldn't find OPP with freq: %lu\n", - __func__, freq); - goto unlock; - } - - _opp_remove(dev_opp, opp, true); -unlock: - mutex_unlock(&dev_opp_list_lock); -} -EXPORT_SYMBOL_GPL(dev_pm_opp_remove); - -static struct dev_pm_opp *_allocate_opp(struct device *dev, - struct device_opp **dev_opp) -{ - struct dev_pm_opp *opp; - - /* allocate new OPP node */ - opp = kzalloc(sizeof(*opp), GFP_KERNEL); - if (!opp) - return NULL; - - INIT_LIST_HEAD(&opp->node); - - *dev_opp = _add_device_opp(dev); - if (!*dev_opp) { - kfree(opp); - return NULL; - } - - return opp; -} - -static int _opp_add(struct device *dev, struct dev_pm_opp *new_opp, - struct device_opp *dev_opp) -{ - struct dev_pm_opp *opp; - struct list_head *head = &dev_opp->opp_list; - - /* - * Insert new OPP in order of increasing frequency and discard if - * already present. - * - * Need to use &dev_opp->opp_list in the condition part of the 'for' - * loop, don't replace it with head otherwise it will become an infinite - * loop. - */ - list_for_each_entry_rcu(opp, &dev_opp->opp_list, node) { - if (new_opp->rate > opp->rate) { - head = &opp->node; - continue; - } - - if (new_opp->rate < opp->rate) - break; - - /* Duplicate OPPs */ - dev_warn(dev, "%s: duplicate OPPs detected. Existing: freq: %lu, volt: %lu, enabled: %d. New: freq: %lu, volt: %lu, enabled: %d\n", - __func__, opp->rate, opp->u_volt, opp->available, - new_opp->rate, new_opp->u_volt, new_opp->available); - - return opp->available && new_opp->u_volt == opp->u_volt ? - 0 : -EEXIST; - } - - new_opp->dev_opp = dev_opp; - list_add_rcu(&new_opp->node, head); - - return 0; -} - -/** - * _opp_add_dynamic() - Allocate a dynamic OPP. - * @dev: device for which we do this operation - * @freq: Frequency in Hz for this OPP - * @u_volt: Voltage in uVolts for this OPP - * @dynamic: Dynamically added OPPs. - * - * This function adds an opp definition to the opp list and returns status. - * The opp is made available by default and it can be controlled using - * dev_pm_opp_enable/disable functions and may be removed by dev_pm_opp_remove. - * - * NOTE: "dynamic" parameter impacts OPPs added by the dev_pm_opp_of_add_table - * and freed by dev_pm_opp_of_remove_table. - * - * Locking: The internal device_opp and opp structures are RCU protected. - * Hence this function internally uses RCU updater strategy with mutex locks - * to keep the integrity of the internal data structures. Callers should ensure - * that this function is *NOT* called under RCU protection or in contexts where - * mutex cannot be locked. - * - * Return: - * 0 On success OR - * Duplicate OPPs (both freq and volt are same) and opp->available - * -EEXIST Freq are same and volt are different OR - * Duplicate OPPs (both freq and volt are same) and !opp->available - * -ENOMEM Memory allocation failure - */ -static int _opp_add_dynamic(struct device *dev, unsigned long freq, - long u_volt, bool dynamic) -{ - struct device_opp *dev_opp; - struct dev_pm_opp *new_opp; - int ret; - - /* Hold our list modification lock here */ - mutex_lock(&dev_opp_list_lock); - - new_opp = _allocate_opp(dev, &dev_opp); - if (!new_opp) { - ret = -ENOMEM; - goto unlock; - } - - /* populate the opp table */ - new_opp->rate = freq; - new_opp->u_volt = u_volt; - new_opp->available = true; - new_opp->dynamic = dynamic; - - ret = _opp_add(dev, new_opp, dev_opp); - if (ret) - goto free_opp; - - mutex_unlock(&dev_opp_list_lock); - - /* - * Notify the changes in the availability of the operable - * frequency/voltage list. - */ - srcu_notifier_call_chain(&dev_opp->srcu_head, OPP_EVENT_ADD, new_opp); - return 0; - -free_opp: - _opp_remove(dev_opp, new_opp, false); -unlock: - mutex_unlock(&dev_opp_list_lock); - return ret; -} - -/* TODO: Support multiple regulators */ -static int opp_get_microvolt(struct dev_pm_opp *opp, struct device *dev) -{ - u32 microvolt[3] = {0}; - int count, ret; - - count = of_property_count_u32_elems(opp->np, "opp-microvolt"); - if (!count) - return 0; - - /* There can be one or three elements here */ - if (count != 1 && count != 3) { - dev_err(dev, "%s: Invalid number of elements in opp-microvolt property (%d)\n", - __func__, count); - return -EINVAL; - } - - ret = of_property_read_u32_array(opp->np, "opp-microvolt", microvolt, - count); - if (ret) { - dev_err(dev, "%s: error parsing opp-microvolt: %d\n", __func__, - ret); - return -EINVAL; - } - - opp->u_volt = microvolt[0]; - opp->u_volt_min = microvolt[1]; - opp->u_volt_max = microvolt[2]; - - return 0; -} - -/** - * _opp_add_static_v2() - Allocate static OPPs (As per 'v2' DT bindings) - * @dev: device for which we do this operation - * @np: device node - * - * This function adds an opp definition to the opp list and returns status. The - * opp can be controlled using dev_pm_opp_enable/disable functions and may be - * removed by dev_pm_opp_remove. - * - * Locking: The internal device_opp and opp structures are RCU protected. - * Hence this function internally uses RCU updater strategy with mutex locks - * to keep the integrity of the internal data structures. Callers should ensure - * that this function is *NOT* called under RCU protection or in contexts where - * mutex cannot be locked. - * - * Return: - * 0 On success OR - * Duplicate OPPs (both freq and volt are same) and opp->available - * -EEXIST Freq are same and volt are different OR - * Duplicate OPPs (both freq and volt are same) and !opp->available - * -ENOMEM Memory allocation failure - * -EINVAL Failed parsing the OPP node - */ -static int _opp_add_static_v2(struct device *dev, struct device_node *np) -{ - struct device_opp *dev_opp; - struct dev_pm_opp *new_opp; - u64 rate; - u32 val; - int ret; - - /* Hold our list modification lock here */ - mutex_lock(&dev_opp_list_lock); - - new_opp = _allocate_opp(dev, &dev_opp); - if (!new_opp) { - ret = -ENOMEM; - goto unlock; - } - - ret = of_property_read_u64(np, "opp-hz", &rate); - if (ret < 0) { - dev_err(dev, "%s: opp-hz not found\n", __func__); - goto free_opp; - } - - /* - * Rate is defined as an unsigned long in clk API, and so casting - * explicitly to its type. Must be fixed once rate is 64 bit - * guaranteed in clk API. - */ - new_opp->rate = (unsigned long)rate; - new_opp->turbo = of_property_read_bool(np, "turbo-mode"); - - new_opp->np = np; - new_opp->dynamic = false; - new_opp->available = true; - - if (!of_property_read_u32(np, "clock-latency-ns", &val)) - new_opp->clock_latency_ns = val; - - ret = opp_get_microvolt(new_opp, dev); - if (ret) - goto free_opp; - - if (!of_property_read_u32(new_opp->np, "opp-microamp", &val)) - new_opp->u_amp = val; - - ret = _opp_add(dev, new_opp, dev_opp); - if (ret) - goto free_opp; - - /* OPP to select on device suspend */ - if (of_property_read_bool(np, "opp-suspend")) { - if (dev_opp->suspend_opp) - dev_warn(dev, "%s: Multiple suspend OPPs found (%lu %lu)\n", - __func__, dev_opp->suspend_opp->rate, - new_opp->rate); - else - dev_opp->suspend_opp = new_opp; - } - - if (new_opp->clock_latency_ns > dev_opp->clock_latency_ns_max) - dev_opp->clock_latency_ns_max = new_opp->clock_latency_ns; - - mutex_unlock(&dev_opp_list_lock); - - pr_debug("%s: turbo:%d rate:%lu uv:%lu uvmin:%lu uvmax:%lu latency:%lu\n", - __func__, new_opp->turbo, new_opp->rate, new_opp->u_volt, - new_opp->u_volt_min, new_opp->u_volt_max, - new_opp->clock_latency_ns); - - /* - * Notify the changes in the availability of the operable - * frequency/voltage list. - */ - srcu_notifier_call_chain(&dev_opp->srcu_head, OPP_EVENT_ADD, new_opp); - return 0; - -free_opp: - _opp_remove(dev_opp, new_opp, false); -unlock: - mutex_unlock(&dev_opp_list_lock); - return ret; -} - -/** - * dev_pm_opp_add() - Add an OPP table from a table definitions - * @dev: device for which we do this operation - * @freq: Frequency in Hz for this OPP - * @u_volt: Voltage in uVolts for this OPP - * - * This function adds an opp definition to the opp list and returns status. - * The opp is made available by default and it can be controlled using - * dev_pm_opp_enable/disable functions. - * - * Locking: The internal device_opp and opp structures are RCU protected. - * Hence this function internally uses RCU updater strategy with mutex locks - * to keep the integrity of the internal data structures. Callers should ensure - * that this function is *NOT* called under RCU protection or in contexts where - * mutex cannot be locked. - * - * Return: - * 0 On success OR - * Duplicate OPPs (both freq and volt are same) and opp->available - * -EEXIST Freq are same and volt are different OR - * Duplicate OPPs (both freq and volt are same) and !opp->available - * -ENOMEM Memory allocation failure - */ -int dev_pm_opp_add(struct device *dev, unsigned long freq, unsigned long u_volt) -{ - return _opp_add_dynamic(dev, freq, u_volt, true); -} -EXPORT_SYMBOL_GPL(dev_pm_opp_add); - -/** - * _opp_set_availability() - helper to set the availability of an opp - * @dev: device for which we do this operation - * @freq: OPP frequency to modify availability - * @availability_req: availability status requested for this opp - * - * Set the availability of an OPP with an RCU operation, opp_{enable,disable} - * share a common logic which is isolated here. - * - * Return: -EINVAL for bad pointers, -ENOMEM if no memory available for the - * copy operation, returns 0 if no modifcation was done OR modification was - * successful. - * - * Locking: The internal device_opp and opp structures are RCU protected. - * Hence this function internally uses RCU updater strategy with mutex locks to - * keep the integrity of the internal data structures. Callers should ensure - * that this function is *NOT* called under RCU protection or in contexts where - * mutex locking or synchronize_rcu() blocking calls cannot be used. - */ -static int _opp_set_availability(struct device *dev, unsigned long freq, - bool availability_req) -{ - struct device_opp *dev_opp; - struct dev_pm_opp *new_opp, *tmp_opp, *opp = ERR_PTR(-ENODEV); - int r = 0; - - /* keep the node allocated */ - new_opp = kmalloc(sizeof(*new_opp), GFP_KERNEL); - if (!new_opp) - return -ENOMEM; - - mutex_lock(&dev_opp_list_lock); - - /* Find the device_opp */ - dev_opp = _find_device_opp(dev); - if (IS_ERR(dev_opp)) { - r = PTR_ERR(dev_opp); - dev_warn(dev, "%s: Device OPP not found (%d)\n", __func__, r); - goto unlock; - } - - /* Do we have the frequency? */ - list_for_each_entry(tmp_opp, &dev_opp->opp_list, node) { - if (tmp_opp->rate == freq) { - opp = tmp_opp; - break; - } - } - if (IS_ERR(opp)) { - r = PTR_ERR(opp); - goto unlock; - } - - /* Is update really needed? */ - if (opp->available == availability_req) - goto unlock; - /* copy the old data over */ - *new_opp = *opp; - - /* plug in new node */ - new_opp->available = availability_req; - - list_replace_rcu(&opp->node, &new_opp->node); - mutex_unlock(&dev_opp_list_lock); - call_srcu(&dev_opp->srcu_head.srcu, &opp->rcu_head, _kfree_opp_rcu); - - /* Notify the change of the OPP availability */ - if (availability_req) - srcu_notifier_call_chain(&dev_opp->srcu_head, OPP_EVENT_ENABLE, - new_opp); - else - srcu_notifier_call_chain(&dev_opp->srcu_head, OPP_EVENT_DISABLE, - new_opp); - - return 0; - -unlock: - mutex_unlock(&dev_opp_list_lock); - kfree(new_opp); - return r; -} - -/** - * dev_pm_opp_enable() - Enable a specific OPP - * @dev: device for which we do this operation - * @freq: OPP frequency to enable - * - * Enables a provided opp. If the operation is valid, this returns 0, else the - * corresponding error value. It is meant to be used for users an OPP available - * after being temporarily made unavailable with dev_pm_opp_disable. - * - * Locking: The internal device_opp and opp structures are RCU protected. - * Hence this function indirectly uses RCU and mutex locks to keep the - * integrity of the internal data structures. Callers should ensure that - * this function is *NOT* called under RCU protection or in contexts where - * mutex locking or synchronize_rcu() blocking calls cannot be used. - * - * Return: -EINVAL for bad pointers, -ENOMEM if no memory available for the - * copy operation, returns 0 if no modifcation was done OR modification was - * successful. - */ -int dev_pm_opp_enable(struct device *dev, unsigned long freq) -{ - return _opp_set_availability(dev, freq, true); -} -EXPORT_SYMBOL_GPL(dev_pm_opp_enable); - -/** - * dev_pm_opp_disable() - Disable a specific OPP - * @dev: device for which we do this operation - * @freq: OPP frequency to disable - * - * Disables a provided opp. If the operation is valid, this returns - * 0, else the corresponding error value. It is meant to be a temporary - * control by users to make this OPP not available until the circumstances are - * right to make it available again (with a call to dev_pm_opp_enable). - * - * Locking: The internal device_opp and opp structures are RCU protected. - * Hence this function indirectly uses RCU and mutex locks to keep the - * integrity of the internal data structures. Callers should ensure that - * this function is *NOT* called under RCU protection or in contexts where - * mutex locking or synchronize_rcu() blocking calls cannot be used. - * - * Return: -EINVAL for bad pointers, -ENOMEM if no memory available for the - * copy operation, returns 0 if no modifcation was done OR modification was - * successful. - */ -int dev_pm_opp_disable(struct device *dev, unsigned long freq) -{ - return _opp_set_availability(dev, freq, false); -} -EXPORT_SYMBOL_GPL(dev_pm_opp_disable); - -/** - * dev_pm_opp_get_notifier() - find notifier_head of the device with opp - * @dev: device pointer used to lookup device OPPs. - * - * Return: pointer to notifier head if found, otherwise -ENODEV or - * -EINVAL based on type of error casted as pointer. value must be checked - * with IS_ERR to determine valid pointer or error result. - * - * Locking: This function must be called under rcu_read_lock(). dev_opp is a RCU - * protected pointer. The reason for the same is that the opp pointer which is - * returned will remain valid for use with opp_get_{voltage, freq} only while - * under the locked area. The pointer returned must be used prior to unlocking - * with rcu_read_unlock() to maintain the integrity of the pointer. - */ -struct srcu_notifier_head *dev_pm_opp_get_notifier(struct device *dev) -{ - struct device_opp *dev_opp = _find_device_opp(dev); - - if (IS_ERR(dev_opp)) - return ERR_CAST(dev_opp); /* matching type */ - - return &dev_opp->srcu_head; -} -EXPORT_SYMBOL_GPL(dev_pm_opp_get_notifier); - -#ifdef CONFIG_OF -/** - * dev_pm_opp_of_remove_table() - Free OPP table entries created from static DT - * entries - * @dev: device pointer used to lookup device OPPs. - * - * Free OPPs created using static entries present in DT. - * - * Locking: The internal device_opp and opp structures are RCU protected. - * Hence this function indirectly uses RCU updater strategy with mutex locks - * to keep the integrity of the internal data structures. Callers should ensure - * that this function is *NOT* called under RCU protection or in contexts where - * mutex cannot be locked. - */ -void dev_pm_opp_of_remove_table(struct device *dev) -{ - struct device_opp *dev_opp; - struct dev_pm_opp *opp, *tmp; - - /* Hold our list modification lock here */ - mutex_lock(&dev_opp_list_lock); - - /* Check for existing list for 'dev' */ - dev_opp = _find_device_opp(dev); - if (IS_ERR(dev_opp)) { - int error = PTR_ERR(dev_opp); - - if (error != -ENODEV) - WARN(1, "%s: dev_opp: %d\n", - IS_ERR_OR_NULL(dev) ? - "Invalid device" : dev_name(dev), - error); - goto unlock; - } - - /* Find if dev_opp manages a single device */ - if (list_is_singular(&dev_opp->dev_list)) { - /* Free static OPPs */ - list_for_each_entry_safe(opp, tmp, &dev_opp->opp_list, node) { - if (!opp->dynamic) - _opp_remove(dev_opp, opp, true); - } - } else { - _remove_list_dev(_find_list_dev(dev, dev_opp), dev_opp); - } - -unlock: - mutex_unlock(&dev_opp_list_lock); -} -EXPORT_SYMBOL_GPL(dev_pm_opp_of_remove_table); - -void dev_pm_opp_of_cpumask_remove_table(cpumask_var_t cpumask) -{ - struct device *cpu_dev; - int cpu; - - WARN_ON(cpumask_empty(cpumask)); - - for_each_cpu(cpu, cpumask) { - cpu_dev = get_cpu_device(cpu); - if (!cpu_dev) { - pr_err("%s: failed to get cpu%d device\n", __func__, - cpu); - continue; - } - - dev_pm_opp_of_remove_table(cpu_dev); - } -} -EXPORT_SYMBOL_GPL(dev_pm_opp_of_cpumask_remove_table); - -/* Returns opp descriptor node for a device, caller must do of_node_put() */ -static struct device_node *_of_get_opp_desc_node(struct device *dev) -{ - /* - * TODO: Support for multiple OPP tables. - * - * There should be only ONE phandle present in "operating-points-v2" - * property. - */ - - return of_parse_phandle(dev->of_node, "operating-points-v2", 0); -} - -/* Initializes OPP tables based on new bindings */ -static int _of_add_opp_table_v2(struct device *dev, struct device_node *opp_np) -{ - struct device_node *np; - struct device_opp *dev_opp; - int ret = 0, count = 0; - - dev_opp = _managed_opp(opp_np); - if (dev_opp) { - /* OPPs are already managed */ - if (!_add_list_dev(dev, dev_opp)) - ret = -ENOMEM; - return ret; - } - - /* We have opp-list node now, iterate over it and add OPPs */ - for_each_available_child_of_node(opp_np, np) { - count++; - - ret = _opp_add_static_v2(dev, np); - if (ret) { - dev_err(dev, "%s: Failed to add OPP, %d\n", __func__, - ret); - goto free_table; - } - } - - /* There should be one of more OPP defined */ - if (WARN_ON(!count)) - return -ENOENT; - - dev_opp = _find_device_opp(dev); - if (WARN_ON(IS_ERR(dev_opp))) { - ret = PTR_ERR(dev_opp); - goto free_table; - } - - dev_opp->np = opp_np; - dev_opp->shared_opp = of_property_read_bool(opp_np, "opp-shared"); - - return 0; - -free_table: - dev_pm_opp_of_remove_table(dev); - - return ret; -} - -/* Initializes OPP tables based on old-deprecated bindings */ -static int _of_add_opp_table_v1(struct device *dev) -{ - const struct property *prop; - const __be32 *val; - int nr; - - prop = of_find_property(dev->of_node, "operating-points", NULL); - if (!prop) - return -ENODEV; - if (!prop->value) - return -ENODATA; - - /* - * Each OPP is a set of tuples consisting of frequency and - * voltage like . - */ - nr = prop->length / sizeof(u32); - if (nr % 2) { - dev_err(dev, "%s: Invalid OPP list\n", __func__); - return -EINVAL; - } - - val = prop->value; - while (nr) { - unsigned long freq = be32_to_cpup(val++) * 1000; - unsigned long volt = be32_to_cpup(val++); - - if (_opp_add_dynamic(dev, freq, volt, false)) - dev_warn(dev, "%s: Failed to add OPP %ld\n", - __func__, freq); - nr -= 2; - } - - return 0; -} - -/** - * dev_pm_opp_of_add_table() - Initialize opp table from device tree - * @dev: device pointer used to lookup device OPPs. - * - * Register the initial OPP table with the OPP library for given device. - * - * Locking: The internal device_opp and opp structures are RCU protected. - * Hence this function indirectly uses RCU updater strategy with mutex locks - * to keep the integrity of the internal data structures. Callers should ensure - * that this function is *NOT* called under RCU protection or in contexts where - * mutex cannot be locked. - * - * Return: - * 0 On success OR - * Duplicate OPPs (both freq and volt are same) and opp->available - * -EEXIST Freq are same and volt are different OR - * Duplicate OPPs (both freq and volt are same) and !opp->available - * -ENOMEM Memory allocation failure - * -ENODEV when 'operating-points' property is not found or is invalid data - * in device node. - * -ENODATA when empty 'operating-points' property is found - * -EINVAL when invalid entries are found in opp-v2 table - */ -int dev_pm_opp_of_add_table(struct device *dev) -{ - struct device_node *opp_np; - int ret; - - /* - * OPPs have two version of bindings now. The older one is deprecated, - * try for the new binding first. - */ - opp_np = _of_get_opp_desc_node(dev); - if (!opp_np) { - /* - * Try old-deprecated bindings for backward compatibility with - * older dtbs. - */ - return _of_add_opp_table_v1(dev); - } - - ret = _of_add_opp_table_v2(dev, opp_np); - of_node_put(opp_np); - - return ret; -} -EXPORT_SYMBOL_GPL(dev_pm_opp_of_add_table); - -int dev_pm_opp_of_cpumask_add_table(cpumask_var_t cpumask) -{ - struct device *cpu_dev; - int cpu, ret = 0; - - WARN_ON(cpumask_empty(cpumask)); - - for_each_cpu(cpu, cpumask) { - cpu_dev = get_cpu_device(cpu); - if (!cpu_dev) { - pr_err("%s: failed to get cpu%d device\n", __func__, - cpu); - continue; - } - - ret = dev_pm_opp_of_add_table(cpu_dev); - if (ret) { - pr_err("%s: couldn't find opp table for cpu:%d, %d\n", - __func__, cpu, ret); - - /* Free all other OPPs */ - dev_pm_opp_of_cpumask_remove_table(cpumask); - break; - } - } - - return ret; -} -EXPORT_SYMBOL_GPL(dev_pm_opp_of_cpumask_add_table); - -/* Required only for V1 bindings, as v2 can manage it from DT itself */ -int dev_pm_opp_set_sharing_cpus(struct device *cpu_dev, cpumask_var_t cpumask) -{ - struct device_list_opp *list_dev; - struct device_opp *dev_opp; - struct device *dev; - int cpu, ret = 0; - - rcu_read_lock(); - - dev_opp = _find_device_opp(cpu_dev); - if (IS_ERR(dev_opp)) { - ret = -EINVAL; - goto out_rcu_read_unlock; - } - - for_each_cpu(cpu, cpumask) { - if (cpu == cpu_dev->id) - continue; - - dev = get_cpu_device(cpu); - if (!dev) { - dev_err(cpu_dev, "%s: failed to get cpu%d device\n", - __func__, cpu); - continue; - } - - list_dev = _add_list_dev(dev, dev_opp); - if (!list_dev) { - dev_err(dev, "%s: failed to add list-dev for cpu%d device\n", - __func__, cpu); - continue; - } - } -out_rcu_read_unlock: - rcu_read_unlock(); - - return 0; -} -EXPORT_SYMBOL_GPL(dev_pm_opp_set_sharing_cpus); - -/* - * Works only for OPP v2 bindings. - * - * cpumask should be already set to mask of cpu_dev->id. - * Returns -ENOENT if operating-points-v2 bindings aren't supported. - */ -int dev_pm_opp_of_get_sharing_cpus(struct device *cpu_dev, cpumask_var_t cpumask) -{ - struct device_node *np, *tmp_np; - struct device *tcpu_dev; - int cpu, ret = 0; - - /* Get OPP descriptor node */ - np = _of_get_opp_desc_node(cpu_dev); - if (!np) { - dev_dbg(cpu_dev, "%s: Couldn't find opp node: %ld\n", __func__, - PTR_ERR(np)); - return -ENOENT; - } - - /* OPPs are shared ? */ - if (!of_property_read_bool(np, "opp-shared")) - goto put_cpu_node; - - for_each_possible_cpu(cpu) { - if (cpu == cpu_dev->id) - continue; - - tcpu_dev = get_cpu_device(cpu); - if (!tcpu_dev) { - dev_err(cpu_dev, "%s: failed to get cpu%d device\n", - __func__, cpu); - ret = -ENODEV; - goto put_cpu_node; - } - - /* Get OPP descriptor node */ - tmp_np = _of_get_opp_desc_node(tcpu_dev); - if (!tmp_np) { - dev_err(tcpu_dev, "%s: Couldn't find opp node: %ld\n", - __func__, PTR_ERR(tmp_np)); - ret = PTR_ERR(tmp_np); - goto put_cpu_node; - } - - /* CPUs are sharing opp node */ - if (np == tmp_np) - cpumask_set_cpu(cpu, cpumask); - - of_node_put(tmp_np); - } - -put_cpu_node: - of_node_put(np); - return ret; -} -EXPORT_SYMBOL_GPL(dev_pm_opp_of_get_sharing_cpus); -#endif diff --git a/drivers/base/power/opp/Makefile b/drivers/base/power/opp/Makefile new file mode 100644 index 0000000..33c1e18 --- /dev/null +++ b/drivers/base/power/opp/Makefile @@ -0,0 +1,2 @@ +ccflags-$(CONFIG_DEBUG_DRIVER) := -DDEBUG +obj-y += core.o cpu.o diff --git a/drivers/base/power/opp/core.c b/drivers/base/power/opp/core.c new file mode 100644 index 0000000..aeff1cf --- /dev/null +++ b/drivers/base/power/opp/core.c @@ -0,0 +1,1559 @@ +/* + * Generic OPP Interface + * + * Copyright (C) 2009-2010 Texas Instruments Incorporated. + * Nishanth Menon + * Romit Dasgupta + * Kevin Hilman + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Internal data structure organization with the OPP layer library is as + * follows: + * dev_opp_list (root) + * |- device 1 (represents voltage domain 1) + * | |- opp 1 (availability, freq, voltage) + * | |- opp 2 .. + * ... ... + * | `- opp n .. + * |- device 2 (represents the next voltage domain) + * ... + * `- device m (represents mth voltage domain) + * device 1, 2.. are represented by dev_opp structure while each opp + * is represented by the opp structure. + */ + +/** + * struct dev_pm_opp - Generic OPP description structure + * @node: opp list node. The nodes are maintained throughout the lifetime + * of boot. It is expected only an optimal set of OPPs are + * added to the library by the SoC framework. + * RCU usage: opp list is traversed with RCU locks. node + * modification is possible realtime, hence the modifications + * are protected by the dev_opp_list_lock for integrity. + * IMPORTANT: the opp nodes should be maintained in increasing + * order. + * @dynamic: not-created from static DT entries. + * @available: true/false - marks if this OPP as available or not + * @turbo: true if turbo (boost) OPP + * @rate: Frequency in hertz + * @u_volt: Target voltage in microvolts corresponding to this OPP + * @u_volt_min: Minimum voltage in microvolts corresponding to this OPP + * @u_volt_max: Maximum voltage in microvolts corresponding to this OPP + * @u_amp: Maximum current drawn by the device in microamperes + * @clock_latency_ns: Latency (in nanoseconds) of switching to this OPP's + * frequency from any other OPP's frequency. + * @dev_opp: points back to the device_opp struct this opp belongs to + * @rcu_head: RCU callback head used for deferred freeing + * @np: OPP's device node. + * + * This structure stores the OPP information for a given device. + */ +struct dev_pm_opp { + struct list_head node; + + bool available; + bool dynamic; + bool turbo; + unsigned long rate; + + unsigned long u_volt; + unsigned long u_volt_min; + unsigned long u_volt_max; + unsigned long u_amp; + unsigned long clock_latency_ns; + + struct device_opp *dev_opp; + struct rcu_head rcu_head; + + struct device_node *np; +}; + +/** + * struct device_list_opp - devices managed by 'struct device_opp' + * @node: list node + * @dev: device to which the struct object belongs + * @rcu_head: RCU callback head used for deferred freeing + * + * This is an internal data structure maintaining the list of devices that are + * managed by 'struct device_opp'. + */ +struct device_list_opp { + struct list_head node; + const struct device *dev; + struct rcu_head rcu_head; +}; + +/** + * struct device_opp - Device opp structure + * @node: list node - contains the devices with OPPs that + * have been registered. Nodes once added are not modified in this + * list. + * RCU usage: nodes are not modified in the list of device_opp, + * however addition is possible and is secured by dev_opp_list_lock + * @srcu_head: notifier head to notify the OPP availability changes. + * @rcu_head: RCU callback head used for deferred freeing + * @dev_list: list of devices that share these OPPs + * @opp_list: list of opps + * @np: struct device_node pointer for opp's DT node. + * @shared_opp: OPP is shared between multiple devices. + * + * This is an internal data structure maintaining the link to opps attached to + * a device. This structure is not meant to be shared to users as it is + * meant for book keeping and private to OPP library. + * + * Because the opp structures can be used from both rcu and srcu readers, we + * need to wait for the grace period of both of them before freeing any + * resources. And so we have used kfree_rcu() from within call_srcu() handlers. + */ +struct device_opp { + struct list_head node; + + struct srcu_notifier_head srcu_head; + struct rcu_head rcu_head; + struct list_head dev_list; + struct list_head opp_list; + + struct device_node *np; + unsigned long clock_latency_ns_max; + bool shared_opp; + struct dev_pm_opp *suspend_opp; +}; + +/* + * The root of the list of all devices. All device_opp structures branch off + * from here, with each device_opp containing the list of opp it supports in + * various states of availability. + */ +static LIST_HEAD(dev_opp_list); +/* Lock to allow exclusive modification to the device and opp lists */ +static DEFINE_MUTEX(dev_opp_list_lock); + +#define opp_rcu_lockdep_assert() \ +do { \ + RCU_LOCKDEP_WARN(!rcu_read_lock_held() && \ + !lockdep_is_held(&dev_opp_list_lock), \ + "Missing rcu_read_lock() or " \ + "dev_opp_list_lock protection"); \ +} while (0) + +static struct device_list_opp *_find_list_dev(const struct device *dev, + struct device_opp *dev_opp) +{ + struct device_list_opp *list_dev; + + list_for_each_entry(list_dev, &dev_opp->dev_list, node) + if (list_dev->dev == dev) + return list_dev; + + return NULL; +} + +static struct device_opp *_managed_opp(const struct device_node *np) +{ + struct device_opp *dev_opp; + + list_for_each_entry_rcu(dev_opp, &dev_opp_list, node) { + if (dev_opp->np == np) { + /* + * Multiple devices can point to the same OPP table and + * so will have same node-pointer, np. + * + * But the OPPs will be considered as shared only if the + * OPP table contains a "opp-shared" property. + */ + return dev_opp->shared_opp ? dev_opp : NULL; + } + } + + return NULL; +} + +/** + * _find_device_opp() - find device_opp struct using device pointer + * @dev: device pointer used to lookup device OPPs + * + * Search list of device OPPs for one containing matching device. Does a RCU + * reader operation to grab the pointer needed. + * + * Return: pointer to 'struct device_opp' if found, otherwise -ENODEV or + * -EINVAL based on type of error. + * + * Locking: This function must be called under rcu_read_lock(). device_opp + * is a RCU protected pointer. This means that device_opp is valid as long + * as we are under RCU lock. + */ +static struct device_opp *_find_device_opp(struct device *dev) +{ + struct device_opp *dev_opp; + + if (IS_ERR_OR_NULL(dev)) { + pr_err("%s: Invalid parameters\n", __func__); + return ERR_PTR(-EINVAL); + } + + list_for_each_entry_rcu(dev_opp, &dev_opp_list, node) + if (_find_list_dev(dev, dev_opp)) + return dev_opp; + + return ERR_PTR(-ENODEV); +} + +/** + * dev_pm_opp_get_voltage() - Gets the voltage corresponding to an available opp + * @opp: opp for which voltage has to be returned for + * + * Return: voltage in micro volt corresponding to the opp, else + * return 0 + * + * Locking: This function must be called under rcu_read_lock(). opp is a rcu + * protected pointer. This means that opp which could have been fetched by + * opp_find_freq_{exact,ceil,floor} functions is valid as long as we are + * under RCU lock. The pointer returned by the opp_find_freq family must be + * used in the same section as the usage of this function with the pointer + * prior to unlocking with rcu_read_unlock() to maintain the integrity of the + * pointer. + */ +unsigned long dev_pm_opp_get_voltage(struct dev_pm_opp *opp) +{ + struct dev_pm_opp *tmp_opp; + unsigned long v = 0; + + opp_rcu_lockdep_assert(); + + tmp_opp = rcu_dereference(opp); + if (IS_ERR_OR_NULL(tmp_opp) || !tmp_opp->available) + pr_err("%s: Invalid parameters\n", __func__); + else + v = tmp_opp->u_volt; + + return v; +} +EXPORT_SYMBOL_GPL(dev_pm_opp_get_voltage); + +/** + * dev_pm_opp_get_freq() - Gets the frequency corresponding to an available opp + * @opp: opp for which frequency has to be returned for + * + * Return: frequency in hertz corresponding to the opp, else + * return 0 + * + * Locking: This function must be called under rcu_read_lock(). opp is a rcu + * protected pointer. This means that opp which could have been fetched by + * opp_find_freq_{exact,ceil,floor} functions is valid as long as we are + * under RCU lock. The pointer returned by the opp_find_freq family must be + * used in the same section as the usage of this function with the pointer + * prior to unlocking with rcu_read_unlock() to maintain the integrity of the + * pointer. + */ +unsigned long dev_pm_opp_get_freq(struct dev_pm_opp *opp) +{ + struct dev_pm_opp *tmp_opp; + unsigned long f = 0; + + opp_rcu_lockdep_assert(); + + tmp_opp = rcu_dereference(opp); + if (IS_ERR_OR_NULL(tmp_opp) || !tmp_opp->available) + pr_err("%s: Invalid parameters\n", __func__); + else + f = tmp_opp->rate; + + return f; +} +EXPORT_SYMBOL_GPL(dev_pm_opp_get_freq); + +/** + * dev_pm_opp_is_turbo() - Returns if opp is turbo OPP or not + * @opp: opp for which turbo mode is being verified + * + * Turbo OPPs are not for normal use, and can be enabled (under certain + * conditions) for short duration of times to finish high throughput work + * quickly. Running on them for longer times may overheat the chip. + * + * Return: true if opp is turbo opp, else false. + * + * Locking: This function must be called under rcu_read_lock(). opp is a rcu + * protected pointer. This means that opp which could have been fetched by + * opp_find_freq_{exact,ceil,floor} functions is valid as long as we are + * under RCU lock. The pointer returned by the opp_find_freq family must be + * used in the same section as the usage of this function with the pointer + * prior to unlocking with rcu_read_unlock() to maintain the integrity of the + * pointer. + */ +bool dev_pm_opp_is_turbo(struct dev_pm_opp *opp) +{ + struct dev_pm_opp *tmp_opp; + + opp_rcu_lockdep_assert(); + + tmp_opp = rcu_dereference(opp); + if (IS_ERR_OR_NULL(tmp_opp) || !tmp_opp->available) { + pr_err("%s: Invalid parameters\n", __func__); + return false; + } + + return tmp_opp->turbo; +} +EXPORT_SYMBOL_GPL(dev_pm_opp_is_turbo); + +/** + * dev_pm_opp_get_max_clock_latency() - Get max clock latency in nanoseconds + * @dev: device for which we do this operation + * + * Return: This function returns the max clock latency in nanoseconds. + * + * Locking: This function takes rcu_read_lock(). + */ +unsigned long dev_pm_opp_get_max_clock_latency(struct device *dev) +{ + struct device_opp *dev_opp; + unsigned long clock_latency_ns; + + rcu_read_lock(); + + dev_opp = _find_device_opp(dev); + if (IS_ERR(dev_opp)) + clock_latency_ns = 0; + else + clock_latency_ns = dev_opp->clock_latency_ns_max; + + rcu_read_unlock(); + return clock_latency_ns; +} +EXPORT_SYMBOL_GPL(dev_pm_opp_get_max_clock_latency); + +/** + * dev_pm_opp_get_suspend_opp() - Get suspend opp + * @dev: device for which we do this operation + * + * Return: This function returns pointer to the suspend opp if it is + * defined and available, otherwise it returns NULL. + * + * Locking: This function must be called under rcu_read_lock(). opp is a rcu + * protected pointer. The reason for the same is that the opp pointer which is + * returned will remain valid for use with opp_get_{voltage, freq} only while + * under the locked area. The pointer returned must be used prior to unlocking + * with rcu_read_unlock() to maintain the integrity of the pointer. + */ +struct dev_pm_opp *dev_pm_opp_get_suspend_opp(struct device *dev) +{ + struct device_opp *dev_opp; + + opp_rcu_lockdep_assert(); + + dev_opp = _find_device_opp(dev); + if (IS_ERR(dev_opp) || !dev_opp->suspend_opp || + !dev_opp->suspend_opp->available) + return NULL; + + return dev_opp->suspend_opp; +} +EXPORT_SYMBOL_GPL(dev_pm_opp_get_suspend_opp); + +/** + * dev_pm_opp_get_opp_count() - Get number of opps available in the opp list + * @dev: device for which we do this operation + * + * Return: This function returns the number of available opps if there are any, + * else returns 0 if none or the corresponding error value. + * + * Locking: This function takes rcu_read_lock(). + */ +int dev_pm_opp_get_opp_count(struct device *dev) +{ + struct device_opp *dev_opp; + struct dev_pm_opp *temp_opp; + int count = 0; + + rcu_read_lock(); + + dev_opp = _find_device_opp(dev); + if (IS_ERR(dev_opp)) { + count = PTR_ERR(dev_opp); + dev_err(dev, "%s: device OPP not found (%d)\n", + __func__, count); + goto out_unlock; + } + + list_for_each_entry_rcu(temp_opp, &dev_opp->opp_list, node) { + if (temp_opp->available) + count++; + } + +out_unlock: + rcu_read_unlock(); + return count; +} +EXPORT_SYMBOL_GPL(dev_pm_opp_get_opp_count); + +/** + * dev_pm_opp_find_freq_exact() - search for an exact frequency + * @dev: device for which we do this operation + * @freq: frequency to search for + * @available: true/false - match for available opp + * + * Return: Searches for exact match in the opp list and returns pointer to the + * matching opp if found, else returns ERR_PTR in case of error and should + * be handled using IS_ERR. Error return values can be: + * EINVAL: for bad pointer + * ERANGE: no match found for search + * ENODEV: if device not found in list of registered devices + * + * Note: available is a modifier for the search. if available=true, then the + * match is for exact matching frequency and is available in the stored OPP + * table. if false, the match is for exact frequency which is not available. + * + * This provides a mechanism to enable an opp which is not available currently + * or the opposite as well. + * + * Locking: This function must be called under rcu_read_lock(). opp is a rcu + * protected pointer. The reason for the same is that the opp pointer which is + * returned will remain valid for use with opp_get_{voltage, freq} only while + * under the locked area. The pointer returned must be used prior to unlocking + * with rcu_read_unlock() to maintain the integrity of the pointer. + */ +struct dev_pm_opp *dev_pm_opp_find_freq_exact(struct device *dev, + unsigned long freq, + bool available) +{ + struct device_opp *dev_opp; + struct dev_pm_opp *temp_opp, *opp = ERR_PTR(-ERANGE); + + opp_rcu_lockdep_assert(); + + dev_opp = _find_device_opp(dev); + if (IS_ERR(dev_opp)) { + int r = PTR_ERR(dev_opp); + dev_err(dev, "%s: device OPP not found (%d)\n", __func__, r); + return ERR_PTR(r); + } + + list_for_each_entry_rcu(temp_opp, &dev_opp->opp_list, node) { + if (temp_opp->available == available && + temp_opp->rate == freq) { + opp = temp_opp; + break; + } + } + + return opp; +} +EXPORT_SYMBOL_GPL(dev_pm_opp_find_freq_exact); + +/** + * dev_pm_opp_find_freq_ceil() - Search for an rounded ceil freq + * @dev: device for which we do this operation + * @freq: Start frequency + * + * Search for the matching ceil *available* OPP from a starting freq + * for a device. + * + * Return: matching *opp and refreshes *freq accordingly, else returns + * ERR_PTR in case of error and should be handled using IS_ERR. Error return + * values can be: + * EINVAL: for bad pointer + * ERANGE: no match found for search + * ENODEV: if device not found in list of registered devices + * + * Locking: This function must be called under rcu_read_lock(). opp is a rcu + * protected pointer. The reason for the same is that the opp pointer which is + * returned will remain valid for use with opp_get_{voltage, freq} only while + * under the locked area. The pointer returned must be used prior to unlocking + * with rcu_read_unlock() to maintain the integrity of the pointer. + */ +struct dev_pm_opp *dev_pm_opp_find_freq_ceil(struct device *dev, + unsigned long *freq) +{ + struct device_opp *dev_opp; + struct dev_pm_opp *temp_opp, *opp = ERR_PTR(-ERANGE); + + opp_rcu_lockdep_assert(); + + if (!dev || !freq) { + dev_err(dev, "%s: Invalid argument freq=%p\n", __func__, freq); + return ERR_PTR(-EINVAL); + } + + dev_opp = _find_device_opp(dev); + if (IS_ERR(dev_opp)) + return ERR_CAST(dev_opp); + + list_for_each_entry_rcu(temp_opp, &dev_opp->opp_list, node) { + if (temp_opp->available && temp_opp->rate >= *freq) { + opp = temp_opp; + *freq = opp->rate; + break; + } + } + + return opp; +} +EXPORT_SYMBOL_GPL(dev_pm_opp_find_freq_ceil); + +/** + * dev_pm_opp_find_freq_floor() - Search for a rounded floor freq + * @dev: device for which we do this operation + * @freq: Start frequency + * + * Search for the matching floor *available* OPP from a starting freq + * for a device. + * + * Return: matching *opp and refreshes *freq accordingly, else returns + * ERR_PTR in case of error and should be handled using IS_ERR. Error return + * values can be: + * EINVAL: for bad pointer + * ERANGE: no match found for search + * ENODEV: if device not found in list of registered devices + * + * Locking: This function must be called under rcu_read_lock(). opp is a rcu + * protected pointer. The reason for the same is that the opp pointer which is + * returned will remain valid for use with opp_get_{voltage, freq} only while + * under the locked area. The pointer returned must be used prior to unlocking + * with rcu_read_unlock() to maintain the integrity of the pointer. + */ +struct dev_pm_opp *dev_pm_opp_find_freq_floor(struct device *dev, + unsigned long *freq) +{ + struct device_opp *dev_opp; + struct dev_pm_opp *temp_opp, *opp = ERR_PTR(-ERANGE); + + opp_rcu_lockdep_assert(); + + if (!dev || !freq) { + dev_err(dev, "%s: Invalid argument freq=%p\n", __func__, freq); + return ERR_PTR(-EINVAL); + } + + dev_opp = _find_device_opp(dev); + if (IS_ERR(dev_opp)) + return ERR_CAST(dev_opp); + + list_for_each_entry_rcu(temp_opp, &dev_opp->opp_list, node) { + if (temp_opp->available) { + /* go to the next node, before choosing prev */ + if (temp_opp->rate > *freq) + break; + else + opp = temp_opp; + } + } + if (!IS_ERR(opp)) + *freq = opp->rate; + + return opp; +} +EXPORT_SYMBOL_GPL(dev_pm_opp_find_freq_floor); + +/* List-dev Helpers */ +static void _kfree_list_dev_rcu(struct rcu_head *head) +{ + struct device_list_opp *list_dev; + + list_dev = container_of(head, struct device_list_opp, rcu_head); + kfree_rcu(list_dev, rcu_head); +} + +static void _remove_list_dev(struct device_list_opp *list_dev, + struct device_opp *dev_opp) +{ + list_del(&list_dev->node); + call_srcu(&dev_opp->srcu_head.srcu, &list_dev->rcu_head, + _kfree_list_dev_rcu); +} + +static struct device_list_opp *_add_list_dev(const struct device *dev, + struct device_opp *dev_opp) +{ + struct device_list_opp *list_dev; + + list_dev = kzalloc(sizeof(*list_dev), GFP_KERNEL); + if (!list_dev) + return NULL; + + /* Initialize list-dev */ + list_dev->dev = dev; + list_add_rcu(&list_dev->node, &dev_opp->dev_list); + + return list_dev; +} + +/** + * _add_device_opp() - Find device OPP table or allocate a new one + * @dev: device for which we do this operation + * + * It tries to find an existing table first, if it couldn't find one, it + * allocates a new OPP table and returns that. + * + * Return: valid device_opp pointer if success, else NULL. + */ +static struct device_opp *_add_device_opp(struct device *dev) +{ + struct device_opp *dev_opp; + struct device_list_opp *list_dev; + + /* Check for existing list for 'dev' first */ + dev_opp = _find_device_opp(dev); + if (!IS_ERR(dev_opp)) + return dev_opp; + + /* + * Allocate a new device OPP table. In the infrequent case where a new + * device is needed to be added, we pay this penalty. + */ + dev_opp = kzalloc(sizeof(*dev_opp), GFP_KERNEL); + if (!dev_opp) + return NULL; + + INIT_LIST_HEAD(&dev_opp->dev_list); + + list_dev = _add_list_dev(dev, dev_opp); + if (!list_dev) { + kfree(dev_opp); + return NULL; + } + + srcu_init_notifier_head(&dev_opp->srcu_head); + INIT_LIST_HEAD(&dev_opp->opp_list); + + /* Secure the device list modification */ + list_add_rcu(&dev_opp->node, &dev_opp_list); + return dev_opp; +} + +/** + * _kfree_device_rcu() - Free device_opp RCU handler + * @head: RCU head + */ +static void _kfree_device_rcu(struct rcu_head *head) +{ + struct device_opp *device_opp = container_of(head, struct device_opp, rcu_head); + + kfree_rcu(device_opp, rcu_head); +} + +/** + * _remove_device_opp() - Removes a device OPP table + * @dev_opp: device OPP table to be removed. + * + * Removes/frees device OPP table it it doesn't contain any OPPs. + */ +static void _remove_device_opp(struct device_opp *dev_opp) +{ + struct device_list_opp *list_dev; + + if (!list_empty(&dev_opp->opp_list)) + return; + + list_dev = list_first_entry(&dev_opp->dev_list, struct device_list_opp, + node); + + _remove_list_dev(list_dev, dev_opp); + + /* dev_list must be empty now */ + WARN_ON(!list_empty(&dev_opp->dev_list)); + + list_del_rcu(&dev_opp->node); + call_srcu(&dev_opp->srcu_head.srcu, &dev_opp->rcu_head, + _kfree_device_rcu); +} + +/** + * _kfree_opp_rcu() - Free OPP RCU handler + * @head: RCU head + */ +static void _kfree_opp_rcu(struct rcu_head *head) +{ + struct dev_pm_opp *opp = container_of(head, struct dev_pm_opp, rcu_head); + + kfree_rcu(opp, rcu_head); +} + +/** + * _opp_remove() - Remove an OPP from a table definition + * @dev_opp: points back to the device_opp struct this opp belongs to + * @opp: pointer to the OPP to remove + * @notify: OPP_EVENT_REMOVE notification should be sent or not + * + * This function removes an opp definition from the opp list. + * + * Locking: The internal device_opp and opp structures are RCU protected. + * It is assumed that the caller holds required mutex for an RCU updater + * strategy. + */ +static void _opp_remove(struct device_opp *dev_opp, + struct dev_pm_opp *opp, bool notify) +{ + /* + * Notify the changes in the availability of the operable + * frequency/voltage list. + */ + if (notify) + srcu_notifier_call_chain(&dev_opp->srcu_head, OPP_EVENT_REMOVE, opp); + list_del_rcu(&opp->node); + call_srcu(&dev_opp->srcu_head.srcu, &opp->rcu_head, _kfree_opp_rcu); + + _remove_device_opp(dev_opp); +} + +/** + * dev_pm_opp_remove() - Remove an OPP from OPP list + * @dev: device for which we do this operation + * @freq: OPP to remove with matching 'freq' + * + * This function removes an opp from the opp list. + * + * Locking: The internal device_opp and opp structures are RCU protected. + * Hence this function internally uses RCU updater strategy with mutex locks + * to keep the integrity of the internal data structures. Callers should ensure + * that this function is *NOT* called under RCU protection or in contexts where + * mutex cannot be locked. + */ +void dev_pm_opp_remove(struct device *dev, unsigned long freq) +{ + struct dev_pm_opp *opp; + struct device_opp *dev_opp; + bool found = false; + + /* Hold our list modification lock here */ + mutex_lock(&dev_opp_list_lock); + + dev_opp = _find_device_opp(dev); + if (IS_ERR(dev_opp)) + goto unlock; + + list_for_each_entry(opp, &dev_opp->opp_list, node) { + if (opp->rate == freq) { + found = true; + break; + } + } + + if (!found) { + dev_warn(dev, "%s: Couldn't find OPP with freq: %lu\n", + __func__, freq); + goto unlock; + } + + _opp_remove(dev_opp, opp, true); +unlock: + mutex_unlock(&dev_opp_list_lock); +} +EXPORT_SYMBOL_GPL(dev_pm_opp_remove); + +static struct dev_pm_opp *_allocate_opp(struct device *dev, + struct device_opp **dev_opp) +{ + struct dev_pm_opp *opp; + + /* allocate new OPP node */ + opp = kzalloc(sizeof(*opp), GFP_KERNEL); + if (!opp) + return NULL; + + INIT_LIST_HEAD(&opp->node); + + *dev_opp = _add_device_opp(dev); + if (!*dev_opp) { + kfree(opp); + return NULL; + } + + return opp; +} + +static int _opp_add(struct device *dev, struct dev_pm_opp *new_opp, + struct device_opp *dev_opp) +{ + struct dev_pm_opp *opp; + struct list_head *head = &dev_opp->opp_list; + + /* + * Insert new OPP in order of increasing frequency and discard if + * already present. + * + * Need to use &dev_opp->opp_list in the condition part of the 'for' + * loop, don't replace it with head otherwise it will become an infinite + * loop. + */ + list_for_each_entry_rcu(opp, &dev_opp->opp_list, node) { + if (new_opp->rate > opp->rate) { + head = &opp->node; + continue; + } + + if (new_opp->rate < opp->rate) + break; + + /* Duplicate OPPs */ + dev_warn(dev, "%s: duplicate OPPs detected. Existing: freq: %lu, volt: %lu, enabled: %d. New: freq: %lu, volt: %lu, enabled: %d\n", + __func__, opp->rate, opp->u_volt, opp->available, + new_opp->rate, new_opp->u_volt, new_opp->available); + + return opp->available && new_opp->u_volt == opp->u_volt ? + 0 : -EEXIST; + } + + new_opp->dev_opp = dev_opp; + list_add_rcu(&new_opp->node, head); + + return 0; +} + +/** + * _opp_add_dynamic() - Allocate a dynamic OPP. + * @dev: device for which we do this operation + * @freq: Frequency in Hz for this OPP + * @u_volt: Voltage in uVolts for this OPP + * @dynamic: Dynamically added OPPs. + * + * This function adds an opp definition to the opp list and returns status. + * The opp is made available by default and it can be controlled using + * dev_pm_opp_enable/disable functions and may be removed by dev_pm_opp_remove. + * + * NOTE: "dynamic" parameter impacts OPPs added by the dev_pm_opp_of_add_table + * and freed by dev_pm_opp_of_remove_table. + * + * Locking: The internal device_opp and opp structures are RCU protected. + * Hence this function internally uses RCU updater strategy with mutex locks + * to keep the integrity of the internal data structures. Callers should ensure + * that this function is *NOT* called under RCU protection or in contexts where + * mutex cannot be locked. + * + * Return: + * 0 On success OR + * Duplicate OPPs (both freq and volt are same) and opp->available + * -EEXIST Freq are same and volt are different OR + * Duplicate OPPs (both freq and volt are same) and !opp->available + * -ENOMEM Memory allocation failure + */ +static int _opp_add_dynamic(struct device *dev, unsigned long freq, + long u_volt, bool dynamic) +{ + struct device_opp *dev_opp; + struct dev_pm_opp *new_opp; + int ret; + + /* Hold our list modification lock here */ + mutex_lock(&dev_opp_list_lock); + + new_opp = _allocate_opp(dev, &dev_opp); + if (!new_opp) { + ret = -ENOMEM; + goto unlock; + } + + /* populate the opp table */ + new_opp->rate = freq; + new_opp->u_volt = u_volt; + new_opp->available = true; + new_opp->dynamic = dynamic; + + ret = _opp_add(dev, new_opp, dev_opp); + if (ret) + goto free_opp; + + mutex_unlock(&dev_opp_list_lock); + + /* + * Notify the changes in the availability of the operable + * frequency/voltage list. + */ + srcu_notifier_call_chain(&dev_opp->srcu_head, OPP_EVENT_ADD, new_opp); + return 0; + +free_opp: + _opp_remove(dev_opp, new_opp, false); +unlock: + mutex_unlock(&dev_opp_list_lock); + return ret; +} + +/* TODO: Support multiple regulators */ +static int opp_get_microvolt(struct dev_pm_opp *opp, struct device *dev) +{ + u32 microvolt[3] = {0}; + int count, ret; + + count = of_property_count_u32_elems(opp->np, "opp-microvolt"); + if (!count) + return 0; + + /* There can be one or three elements here */ + if (count != 1 && count != 3) { + dev_err(dev, "%s: Invalid number of elements in opp-microvolt property (%d)\n", + __func__, count); + return -EINVAL; + } + + ret = of_property_read_u32_array(opp->np, "opp-microvolt", microvolt, + count); + if (ret) { + dev_err(dev, "%s: error parsing opp-microvolt: %d\n", __func__, + ret); + return -EINVAL; + } + + opp->u_volt = microvolt[0]; + opp->u_volt_min = microvolt[1]; + opp->u_volt_max = microvolt[2]; + + return 0; +} + +/** + * _opp_add_static_v2() - Allocate static OPPs (As per 'v2' DT bindings) + * @dev: device for which we do this operation + * @np: device node + * + * This function adds an opp definition to the opp list and returns status. The + * opp can be controlled using dev_pm_opp_enable/disable functions and may be + * removed by dev_pm_opp_remove. + * + * Locking: The internal device_opp and opp structures are RCU protected. + * Hence this function internally uses RCU updater strategy with mutex locks + * to keep the integrity of the internal data structures. Callers should ensure + * that this function is *NOT* called under RCU protection or in contexts where + * mutex cannot be locked. + * + * Return: + * 0 On success OR + * Duplicate OPPs (both freq and volt are same) and opp->available + * -EEXIST Freq are same and volt are different OR + * Duplicate OPPs (both freq and volt are same) and !opp->available + * -ENOMEM Memory allocation failure + * -EINVAL Failed parsing the OPP node + */ +static int _opp_add_static_v2(struct device *dev, struct device_node *np) +{ + struct device_opp *dev_opp; + struct dev_pm_opp *new_opp; + u64 rate; + u32 val; + int ret; + + /* Hold our list modification lock here */ + mutex_lock(&dev_opp_list_lock); + + new_opp = _allocate_opp(dev, &dev_opp); + if (!new_opp) { + ret = -ENOMEM; + goto unlock; + } + + ret = of_property_read_u64(np, "opp-hz", &rate); + if (ret < 0) { + dev_err(dev, "%s: opp-hz not found\n", __func__); + goto free_opp; + } + + /* + * Rate is defined as an unsigned long in clk API, and so casting + * explicitly to its type. Must be fixed once rate is 64 bit + * guaranteed in clk API. + */ + new_opp->rate = (unsigned long)rate; + new_opp->turbo = of_property_read_bool(np, "turbo-mode"); + + new_opp->np = np; + new_opp->dynamic = false; + new_opp->available = true; + + if (!of_property_read_u32(np, "clock-latency-ns", &val)) + new_opp->clock_latency_ns = val; + + ret = opp_get_microvolt(new_opp, dev); + if (ret) + goto free_opp; + + if (!of_property_read_u32(new_opp->np, "opp-microamp", &val)) + new_opp->u_amp = val; + + ret = _opp_add(dev, new_opp, dev_opp); + if (ret) + goto free_opp; + + /* OPP to select on device suspend */ + if (of_property_read_bool(np, "opp-suspend")) { + if (dev_opp->suspend_opp) + dev_warn(dev, "%s: Multiple suspend OPPs found (%lu %lu)\n", + __func__, dev_opp->suspend_opp->rate, + new_opp->rate); + else + dev_opp->suspend_opp = new_opp; + } + + if (new_opp->clock_latency_ns > dev_opp->clock_latency_ns_max) + dev_opp->clock_latency_ns_max = new_opp->clock_latency_ns; + + mutex_unlock(&dev_opp_list_lock); + + pr_debug("%s: turbo:%d rate:%lu uv:%lu uvmin:%lu uvmax:%lu latency:%lu\n", + __func__, new_opp->turbo, new_opp->rate, new_opp->u_volt, + new_opp->u_volt_min, new_opp->u_volt_max, + new_opp->clock_latency_ns); + + /* + * Notify the changes in the availability of the operable + * frequency/voltage list. + */ + srcu_notifier_call_chain(&dev_opp->srcu_head, OPP_EVENT_ADD, new_opp); + return 0; + +free_opp: + _opp_remove(dev_opp, new_opp, false); +unlock: + mutex_unlock(&dev_opp_list_lock); + return ret; +} + +/** + * dev_pm_opp_add() - Add an OPP table from a table definitions + * @dev: device for which we do this operation + * @freq: Frequency in Hz for this OPP + * @u_volt: Voltage in uVolts for this OPP + * + * This function adds an opp definition to the opp list and returns status. + * The opp is made available by default and it can be controlled using + * dev_pm_opp_enable/disable functions. + * + * Locking: The internal device_opp and opp structures are RCU protected. + * Hence this function internally uses RCU updater strategy with mutex locks + * to keep the integrity of the internal data structures. Callers should ensure + * that this function is *NOT* called under RCU protection or in contexts where + * mutex cannot be locked. + * + * Return: + * 0 On success OR + * Duplicate OPPs (both freq and volt are same) and opp->available + * -EEXIST Freq are same and volt are different OR + * Duplicate OPPs (both freq and volt are same) and !opp->available + * -ENOMEM Memory allocation failure + */ +int dev_pm_opp_add(struct device *dev, unsigned long freq, unsigned long u_volt) +{ + return _opp_add_dynamic(dev, freq, u_volt, true); +} +EXPORT_SYMBOL_GPL(dev_pm_opp_add); + +/** + * _opp_set_availability() - helper to set the availability of an opp + * @dev: device for which we do this operation + * @freq: OPP frequency to modify availability + * @availability_req: availability status requested for this opp + * + * Set the availability of an OPP with an RCU operation, opp_{enable,disable} + * share a common logic which is isolated here. + * + * Return: -EINVAL for bad pointers, -ENOMEM if no memory available for the + * copy operation, returns 0 if no modifcation was done OR modification was + * successful. + * + * Locking: The internal device_opp and opp structures are RCU protected. + * Hence this function internally uses RCU updater strategy with mutex locks to + * keep the integrity of the internal data structures. Callers should ensure + * that this function is *NOT* called under RCU protection or in contexts where + * mutex locking or synchronize_rcu() blocking calls cannot be used. + */ +static int _opp_set_availability(struct device *dev, unsigned long freq, + bool availability_req) +{ + struct device_opp *dev_opp; + struct dev_pm_opp *new_opp, *tmp_opp, *opp = ERR_PTR(-ENODEV); + int r = 0; + + /* keep the node allocated */ + new_opp = kmalloc(sizeof(*new_opp), GFP_KERNEL); + if (!new_opp) + return -ENOMEM; + + mutex_lock(&dev_opp_list_lock); + + /* Find the device_opp */ + dev_opp = _find_device_opp(dev); + if (IS_ERR(dev_opp)) { + r = PTR_ERR(dev_opp); + dev_warn(dev, "%s: Device OPP not found (%d)\n", __func__, r); + goto unlock; + } + + /* Do we have the frequency? */ + list_for_each_entry(tmp_opp, &dev_opp->opp_list, node) { + if (tmp_opp->rate == freq) { + opp = tmp_opp; + break; + } + } + if (IS_ERR(opp)) { + r = PTR_ERR(opp); + goto unlock; + } + + /* Is update really needed? */ + if (opp->available == availability_req) + goto unlock; + /* copy the old data over */ + *new_opp = *opp; + + /* plug in new node */ + new_opp->available = availability_req; + + list_replace_rcu(&opp->node, &new_opp->node); + mutex_unlock(&dev_opp_list_lock); + call_srcu(&dev_opp->srcu_head.srcu, &opp->rcu_head, _kfree_opp_rcu); + + /* Notify the change of the OPP availability */ + if (availability_req) + srcu_notifier_call_chain(&dev_opp->srcu_head, OPP_EVENT_ENABLE, + new_opp); + else + srcu_notifier_call_chain(&dev_opp->srcu_head, OPP_EVENT_DISABLE, + new_opp); + + return 0; + +unlock: + mutex_unlock(&dev_opp_list_lock); + kfree(new_opp); + return r; +} + +/** + * dev_pm_opp_enable() - Enable a specific OPP + * @dev: device for which we do this operation + * @freq: OPP frequency to enable + * + * Enables a provided opp. If the operation is valid, this returns 0, else the + * corresponding error value. It is meant to be used for users an OPP available + * after being temporarily made unavailable with dev_pm_opp_disable. + * + * Locking: The internal device_opp and opp structures are RCU protected. + * Hence this function indirectly uses RCU and mutex locks to keep the + * integrity of the internal data structures. Callers should ensure that + * this function is *NOT* called under RCU protection or in contexts where + * mutex locking or synchronize_rcu() blocking calls cannot be used. + * + * Return: -EINVAL for bad pointers, -ENOMEM if no memory available for the + * copy operation, returns 0 if no modifcation was done OR modification was + * successful. + */ +int dev_pm_opp_enable(struct device *dev, unsigned long freq) +{ + return _opp_set_availability(dev, freq, true); +} +EXPORT_SYMBOL_GPL(dev_pm_opp_enable); + +/** + * dev_pm_opp_disable() - Disable a specific OPP + * @dev: device for which we do this operation + * @freq: OPP frequency to disable + * + * Disables a provided opp. If the operation is valid, this returns + * 0, else the corresponding error value. It is meant to be a temporary + * control by users to make this OPP not available until the circumstances are + * right to make it available again (with a call to dev_pm_opp_enable). + * + * Locking: The internal device_opp and opp structures are RCU protected. + * Hence this function indirectly uses RCU and mutex locks to keep the + * integrity of the internal data structures. Callers should ensure that + * this function is *NOT* called under RCU protection or in contexts where + * mutex locking or synchronize_rcu() blocking calls cannot be used. + * + * Return: -EINVAL for bad pointers, -ENOMEM if no memory available for the + * copy operation, returns 0 if no modifcation was done OR modification was + * successful. + */ +int dev_pm_opp_disable(struct device *dev, unsigned long freq) +{ + return _opp_set_availability(dev, freq, false); +} +EXPORT_SYMBOL_GPL(dev_pm_opp_disable); + +/** + * dev_pm_opp_get_notifier() - find notifier_head of the device with opp + * @dev: device pointer used to lookup device OPPs. + * + * Return: pointer to notifier head if found, otherwise -ENODEV or + * -EINVAL based on type of error casted as pointer. value must be checked + * with IS_ERR to determine valid pointer or error result. + * + * Locking: This function must be called under rcu_read_lock(). dev_opp is a RCU + * protected pointer. The reason for the same is that the opp pointer which is + * returned will remain valid for use with opp_get_{voltage, freq} only while + * under the locked area. The pointer returned must be used prior to unlocking + * with rcu_read_unlock() to maintain the integrity of the pointer. + */ +struct srcu_notifier_head *dev_pm_opp_get_notifier(struct device *dev) +{ + struct device_opp *dev_opp = _find_device_opp(dev); + + if (IS_ERR(dev_opp)) + return ERR_CAST(dev_opp); /* matching type */ + + return &dev_opp->srcu_head; +} +EXPORT_SYMBOL_GPL(dev_pm_opp_get_notifier); + +#ifdef CONFIG_OF +/** + * dev_pm_opp_of_remove_table() - Free OPP table entries created from static DT + * entries + * @dev: device pointer used to lookup device OPPs. + * + * Free OPPs created using static entries present in DT. + * + * Locking: The internal device_opp and opp structures are RCU protected. + * Hence this function indirectly uses RCU updater strategy with mutex locks + * to keep the integrity of the internal data structures. Callers should ensure + * that this function is *NOT* called under RCU protection or in contexts where + * mutex cannot be locked. + */ +void dev_pm_opp_of_remove_table(struct device *dev) +{ + struct device_opp *dev_opp; + struct dev_pm_opp *opp, *tmp; + + /* Hold our list modification lock here */ + mutex_lock(&dev_opp_list_lock); + + /* Check for existing list for 'dev' */ + dev_opp = _find_device_opp(dev); + if (IS_ERR(dev_opp)) { + int error = PTR_ERR(dev_opp); + + if (error != -ENODEV) + WARN(1, "%s: dev_opp: %d\n", + IS_ERR_OR_NULL(dev) ? + "Invalid device" : dev_name(dev), + error); + goto unlock; + } + + /* Find if dev_opp manages a single device */ + if (list_is_singular(&dev_opp->dev_list)) { + /* Free static OPPs */ + list_for_each_entry_safe(opp, tmp, &dev_opp->opp_list, node) { + if (!opp->dynamic) + _opp_remove(dev_opp, opp, true); + } + } else { + _remove_list_dev(_find_list_dev(dev, dev_opp), dev_opp); + } + +unlock: + mutex_unlock(&dev_opp_list_lock); +} +EXPORT_SYMBOL_GPL(dev_pm_opp_of_remove_table); + +void dev_pm_opp_of_cpumask_remove_table(cpumask_var_t cpumask) +{ + struct device *cpu_dev; + int cpu; + + WARN_ON(cpumask_empty(cpumask)); + + for_each_cpu(cpu, cpumask) { + cpu_dev = get_cpu_device(cpu); + if (!cpu_dev) { + pr_err("%s: failed to get cpu%d device\n", __func__, + cpu); + continue; + } + + dev_pm_opp_of_remove_table(cpu_dev); + } +} +EXPORT_SYMBOL_GPL(dev_pm_opp_of_cpumask_remove_table); + +/* Returns opp descriptor node for a device, caller must do of_node_put() */ +static struct device_node *_of_get_opp_desc_node(struct device *dev) +{ + /* + * TODO: Support for multiple OPP tables. + * + * There should be only ONE phandle present in "operating-points-v2" + * property. + */ + + return of_parse_phandle(dev->of_node, "operating-points-v2", 0); +} + +/* Initializes OPP tables based on new bindings */ +static int _of_add_opp_table_v2(struct device *dev, struct device_node *opp_np) +{ + struct device_node *np; + struct device_opp *dev_opp; + int ret = 0, count = 0; + + dev_opp = _managed_opp(opp_np); + if (dev_opp) { + /* OPPs are already managed */ + if (!_add_list_dev(dev, dev_opp)) + ret = -ENOMEM; + return ret; + } + + /* We have opp-list node now, iterate over it and add OPPs */ + for_each_available_child_of_node(opp_np, np) { + count++; + + ret = _opp_add_static_v2(dev, np); + if (ret) { + dev_err(dev, "%s: Failed to add OPP, %d\n", __func__, + ret); + goto free_table; + } + } + + /* There should be one of more OPP defined */ + if (WARN_ON(!count)) + return -ENOENT; + + dev_opp = _find_device_opp(dev); + if (WARN_ON(IS_ERR(dev_opp))) { + ret = PTR_ERR(dev_opp); + goto free_table; + } + + dev_opp->np = opp_np; + dev_opp->shared_opp = of_property_read_bool(opp_np, "opp-shared"); + + return 0; + +free_table: + dev_pm_opp_of_remove_table(dev); + + return ret; +} + +/* Initializes OPP tables based on old-deprecated bindings */ +static int _of_add_opp_table_v1(struct device *dev) +{ + const struct property *prop; + const __be32 *val; + int nr; + + prop = of_find_property(dev->of_node, "operating-points", NULL); + if (!prop) + return -ENODEV; + if (!prop->value) + return -ENODATA; + + /* + * Each OPP is a set of tuples consisting of frequency and + * voltage like . + */ + nr = prop->length / sizeof(u32); + if (nr % 2) { + dev_err(dev, "%s: Invalid OPP list\n", __func__); + return -EINVAL; + } + + val = prop->value; + while (nr) { + unsigned long freq = be32_to_cpup(val++) * 1000; + unsigned long volt = be32_to_cpup(val++); + + if (_opp_add_dynamic(dev, freq, volt, false)) + dev_warn(dev, "%s: Failed to add OPP %ld\n", + __func__, freq); + nr -= 2; + } + + return 0; +} + +/** + * dev_pm_opp_of_add_table() - Initialize opp table from device tree + * @dev: device pointer used to lookup device OPPs. + * + * Register the initial OPP table with the OPP library for given device. + * + * Locking: The internal device_opp and opp structures are RCU protected. + * Hence this function indirectly uses RCU updater strategy with mutex locks + * to keep the integrity of the internal data structures. Callers should ensure + * that this function is *NOT* called under RCU protection or in contexts where + * mutex cannot be locked. + * + * Return: + * 0 On success OR + * Duplicate OPPs (both freq and volt are same) and opp->available + * -EEXIST Freq are same and volt are different OR + * Duplicate OPPs (both freq and volt are same) and !opp->available + * -ENOMEM Memory allocation failure + * -ENODEV when 'operating-points' property is not found or is invalid data + * in device node. + * -ENODATA when empty 'operating-points' property is found + * -EINVAL when invalid entries are found in opp-v2 table + */ +int dev_pm_opp_of_add_table(struct device *dev) +{ + struct device_node *opp_np; + int ret; + + /* + * OPPs have two version of bindings now. The older one is deprecated, + * try for the new binding first. + */ + opp_np = _of_get_opp_desc_node(dev); + if (!opp_np) { + /* + * Try old-deprecated bindings for backward compatibility with + * older dtbs. + */ + return _of_add_opp_table_v1(dev); + } + + ret = _of_add_opp_table_v2(dev, opp_np); + of_node_put(opp_np); + + return ret; +} +EXPORT_SYMBOL_GPL(dev_pm_opp_of_add_table); + +int dev_pm_opp_of_cpumask_add_table(cpumask_var_t cpumask) +{ + struct device *cpu_dev; + int cpu, ret = 0; + + WARN_ON(cpumask_empty(cpumask)); + + for_each_cpu(cpu, cpumask) { + cpu_dev = get_cpu_device(cpu); + if (!cpu_dev) { + pr_err("%s: failed to get cpu%d device\n", __func__, + cpu); + continue; + } + + ret = dev_pm_opp_of_add_table(cpu_dev); + if (ret) { + pr_err("%s: couldn't find opp table for cpu:%d, %d\n", + __func__, cpu, ret); + + /* Free all other OPPs */ + dev_pm_opp_of_cpumask_remove_table(cpumask); + break; + } + } + + return ret; +} +EXPORT_SYMBOL_GPL(dev_pm_opp_of_cpumask_add_table); + +/* Required only for V1 bindings, as v2 can manage it from DT itself */ +int dev_pm_opp_set_sharing_cpus(struct device *cpu_dev, cpumask_var_t cpumask) +{ + struct device_list_opp *list_dev; + struct device_opp *dev_opp; + struct device *dev; + int cpu, ret = 0; + + rcu_read_lock(); + + dev_opp = _find_device_opp(cpu_dev); + if (IS_ERR(dev_opp)) { + ret = -EINVAL; + goto out_rcu_read_unlock; + } + + for_each_cpu(cpu, cpumask) { + if (cpu == cpu_dev->id) + continue; + + dev = get_cpu_device(cpu); + if (!dev) { + dev_err(cpu_dev, "%s: failed to get cpu%d device\n", + __func__, cpu); + continue; + } + + list_dev = _add_list_dev(dev, dev_opp); + if (!list_dev) { + dev_err(dev, "%s: failed to add list-dev for cpu%d device\n", + __func__, cpu); + continue; + } + } +out_rcu_read_unlock: + rcu_read_unlock(); + + return 0; +} +EXPORT_SYMBOL_GPL(dev_pm_opp_set_sharing_cpus); + +/* + * Works only for OPP v2 bindings. + * + * cpumask should be already set to mask of cpu_dev->id. + * Returns -ENOENT if operating-points-v2 bindings aren't supported. + */ +int dev_pm_opp_of_get_sharing_cpus(struct device *cpu_dev, cpumask_var_t cpumask) +{ + struct device_node *np, *tmp_np; + struct device *tcpu_dev; + int cpu, ret = 0; + + /* Get OPP descriptor node */ + np = _of_get_opp_desc_node(cpu_dev); + if (!np) { + dev_dbg(cpu_dev, "%s: Couldn't find opp node: %ld\n", __func__, + PTR_ERR(np)); + return -ENOENT; + } + + /* OPPs are shared ? */ + if (!of_property_read_bool(np, "opp-shared")) + goto put_cpu_node; + + for_each_possible_cpu(cpu) { + if (cpu == cpu_dev->id) + continue; + + tcpu_dev = get_cpu_device(cpu); + if (!tcpu_dev) { + dev_err(cpu_dev, "%s: failed to get cpu%d device\n", + __func__, cpu); + ret = -ENODEV; + goto put_cpu_node; + } + + /* Get OPP descriptor node */ + tmp_np = _of_get_opp_desc_node(tcpu_dev); + if (!tmp_np) { + dev_err(tcpu_dev, "%s: Couldn't find opp node: %ld\n", + __func__, PTR_ERR(tmp_np)); + ret = PTR_ERR(tmp_np); + goto put_cpu_node; + } + + /* CPUs are sharing opp node */ + if (np == tmp_np) + cpumask_set_cpu(cpu, cpumask); + + of_node_put(tmp_np); + } + +put_cpu_node: + of_node_put(np); + return ret; +} +EXPORT_SYMBOL_GPL(dev_pm_opp_of_get_sharing_cpus); +#endif diff --git a/drivers/base/power/opp/cpu.c b/drivers/base/power/opp/cpu.c new file mode 100644 index 0000000..0dd0330 --- /dev/null +++ b/drivers/base/power/opp/cpu.c @@ -0,0 +1,116 @@ +/* + * Generic OPP helper interface for CPU device + * + * Copyright (C) 2009-2014 Texas Instruments Incorporated. + * Nishanth Menon + * Romit Dasgupta + * Kevin Hilman + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_CPU_FREQ +/** + * dev_pm_opp_init_cpufreq_table() - create a cpufreq table for a device + * @dev: device for which we do this operation + * @table: Cpufreq table returned back to caller + * + * Generate a cpufreq table for a provided device- this assumes that the + * opp list is already initialized and ready for usage. + * + * This function allocates required memory for the cpufreq table. It is + * expected that the caller does the required maintenance such as freeing + * the table as required. + * + * Returns -EINVAL for bad pointers, -ENODEV if the device is not found, -ENOMEM + * if no memory available for the operation (table is not populated), returns 0 + * if successful and table is populated. + * + * WARNING: It is important for the callers to ensure refreshing their copy of + * the table if any of the mentioned functions have been invoked in the interim. + * + * Locking: The internal device_opp and opp structures are RCU protected. + * Since we just use the regular accessor functions to access the internal data + * structures, we use RCU read lock inside this function. As a result, users of + * this function DONOT need to use explicit locks for invoking. + */ +int dev_pm_opp_init_cpufreq_table(struct device *dev, + struct cpufreq_frequency_table **table) +{ + struct dev_pm_opp *opp; + struct cpufreq_frequency_table *freq_table = NULL; + int i, max_opps, ret = 0; + unsigned long rate; + + rcu_read_lock(); + + max_opps = dev_pm_opp_get_opp_count(dev); + if (max_opps <= 0) { + ret = max_opps ? max_opps : -ENODATA; + goto out; + } + + freq_table = kcalloc((max_opps + 1), sizeof(*freq_table), GFP_ATOMIC); + if (!freq_table) { + ret = -ENOMEM; + goto out; + } + + for (i = 0, rate = 0; i < max_opps; i++, rate++) { + /* find next rate */ + opp = dev_pm_opp_find_freq_ceil(dev, &rate); + if (IS_ERR(opp)) { + ret = PTR_ERR(opp); + goto out; + } + freq_table[i].driver_data = i; + freq_table[i].frequency = rate / 1000; + + /* Is Boost/turbo opp ? */ + if (dev_pm_opp_is_turbo(opp)) + freq_table[i].flags = CPUFREQ_BOOST_FREQ; + } + + freq_table[i].driver_data = i; + freq_table[i].frequency = CPUFREQ_TABLE_END; + + *table = &freq_table[0]; + +out: + rcu_read_unlock(); + if (ret) + kfree(freq_table); + + return ret; +} +EXPORT_SYMBOL_GPL(dev_pm_opp_init_cpufreq_table); + +/** + * dev_pm_opp_free_cpufreq_table() - free the cpufreq table + * @dev: device for which we do this operation + * @table: table to free + * + * Free up the table allocated by dev_pm_opp_init_cpufreq_table + */ +void dev_pm_opp_free_cpufreq_table(struct device *dev, + struct cpufreq_frequency_table **table) +{ + if (!table) + return; + + kfree(*table); + *table = NULL; +} +EXPORT_SYMBOL_GPL(dev_pm_opp_free_cpufreq_table); +#endif /* CONFIG_CPU_FREQ */ diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile index 4134038..be46ce0 100644 --- a/drivers/cpufreq/Makefile +++ b/drivers/cpufreq/Makefile @@ -1,6 +1,5 @@ # CPUfreq core obj-$(CONFIG_CPU_FREQ) += cpufreq.o freq_table.o -obj-$(CONFIG_PM_OPP) += cpufreq_opp.o # CPUfreq stats obj-$(CONFIG_CPU_FREQ_STAT) += cpufreq_stats.o diff --git a/drivers/cpufreq/cpufreq_opp.c b/drivers/cpufreq/cpufreq_opp.c deleted file mode 100644 index 0f5e6d5..0000000 --- a/drivers/cpufreq/cpufreq_opp.c +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Generic OPP helper interface for CPUFreq drivers - * - * Copyright (C) 2009-2014 Texas Instruments Incorporated. - * Nishanth Menon - * Romit Dasgupta - * Kevin Hilman - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/** - * dev_pm_opp_init_cpufreq_table() - create a cpufreq table for a device - * @dev: device for which we do this operation - * @table: Cpufreq table returned back to caller - * - * Generate a cpufreq table for a provided device- this assumes that the - * opp list is already initialized and ready for usage. - * - * This function allocates required memory for the cpufreq table. It is - * expected that the caller does the required maintenance such as freeing - * the table as required. - * - * Returns -EINVAL for bad pointers, -ENODEV if the device is not found, -ENOMEM - * if no memory available for the operation (table is not populated), returns 0 - * if successful and table is populated. - * - * WARNING: It is important for the callers to ensure refreshing their copy of - * the table if any of the mentioned functions have been invoked in the interim. - * - * Locking: The internal device_opp and opp structures are RCU protected. - * Since we just use the regular accessor functions to access the internal data - * structures, we use RCU read lock inside this function. As a result, users of - * this function DONOT need to use explicit locks for invoking. - */ -int dev_pm_opp_init_cpufreq_table(struct device *dev, - struct cpufreq_frequency_table **table) -{ - struct dev_pm_opp *opp; - struct cpufreq_frequency_table *freq_table = NULL; - int i, max_opps, ret = 0; - unsigned long rate; - - rcu_read_lock(); - - max_opps = dev_pm_opp_get_opp_count(dev); - if (max_opps <= 0) { - ret = max_opps ? max_opps : -ENODATA; - goto out; - } - - freq_table = kcalloc((max_opps + 1), sizeof(*freq_table), GFP_ATOMIC); - if (!freq_table) { - ret = -ENOMEM; - goto out; - } - - for (i = 0, rate = 0; i < max_opps; i++, rate++) { - /* find next rate */ - opp = dev_pm_opp_find_freq_ceil(dev, &rate); - if (IS_ERR(opp)) { - ret = PTR_ERR(opp); - goto out; - } - freq_table[i].driver_data = i; - freq_table[i].frequency = rate / 1000; - - /* Is Boost/turbo opp ? */ - if (dev_pm_opp_is_turbo(opp)) - freq_table[i].flags = CPUFREQ_BOOST_FREQ; - } - - freq_table[i].driver_data = i; - freq_table[i].frequency = CPUFREQ_TABLE_END; - - *table = &freq_table[0]; - -out: - rcu_read_unlock(); - if (ret) - kfree(freq_table); - - return ret; -} -EXPORT_SYMBOL_GPL(dev_pm_opp_init_cpufreq_table); - -/** - * dev_pm_opp_free_cpufreq_table() - free the cpufreq table - * @dev: device for which we do this operation - * @table: table to free - * - * Free up the table allocated by dev_pm_opp_init_cpufreq_table - */ -void dev_pm_opp_free_cpufreq_table(struct device *dev, - struct cpufreq_frequency_table **table) -{ - if (!table) - return; - - kfree(*table); - *table = NULL; -} -EXPORT_SYMBOL_GPL(dev_pm_opp_free_cpufreq_table); -- cgit v0.10.2 From f59d3ee8480d30f41914cb4bed5086237e8507b0 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Fri, 4 Sep 2015 13:47:26 +0530 Subject: PM / OPP: Move cpu specific code to opp/cpu.c Move cpu device specific code out of generic opp library, and add it to cpu.c. Along with that, create a core-internal opp.h header, which will be used to share structures and function prototypes within opp core. Reviewed-by: Stephen Boyd Signed-off-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki diff --git a/drivers/base/power/opp/core.c b/drivers/base/power/opp/core.c index aeff1cf..a731fa6 100644 --- a/drivers/base/power/opp/core.c +++ b/drivers/base/power/opp/core.c @@ -11,131 +11,14 @@ * published by the Free Software Foundation. */ -#include -#include #include #include #include #include -#include -#include -#include -#include #include #include -/* - * Internal data structure organization with the OPP layer library is as - * follows: - * dev_opp_list (root) - * |- device 1 (represents voltage domain 1) - * | |- opp 1 (availability, freq, voltage) - * | |- opp 2 .. - * ... ... - * | `- opp n .. - * |- device 2 (represents the next voltage domain) - * ... - * `- device m (represents mth voltage domain) - * device 1, 2.. are represented by dev_opp structure while each opp - * is represented by the opp structure. - */ - -/** - * struct dev_pm_opp - Generic OPP description structure - * @node: opp list node. The nodes are maintained throughout the lifetime - * of boot. It is expected only an optimal set of OPPs are - * added to the library by the SoC framework. - * RCU usage: opp list is traversed with RCU locks. node - * modification is possible realtime, hence the modifications - * are protected by the dev_opp_list_lock for integrity. - * IMPORTANT: the opp nodes should be maintained in increasing - * order. - * @dynamic: not-created from static DT entries. - * @available: true/false - marks if this OPP as available or not - * @turbo: true if turbo (boost) OPP - * @rate: Frequency in hertz - * @u_volt: Target voltage in microvolts corresponding to this OPP - * @u_volt_min: Minimum voltage in microvolts corresponding to this OPP - * @u_volt_max: Maximum voltage in microvolts corresponding to this OPP - * @u_amp: Maximum current drawn by the device in microamperes - * @clock_latency_ns: Latency (in nanoseconds) of switching to this OPP's - * frequency from any other OPP's frequency. - * @dev_opp: points back to the device_opp struct this opp belongs to - * @rcu_head: RCU callback head used for deferred freeing - * @np: OPP's device node. - * - * This structure stores the OPP information for a given device. - */ -struct dev_pm_opp { - struct list_head node; - - bool available; - bool dynamic; - bool turbo; - unsigned long rate; - - unsigned long u_volt; - unsigned long u_volt_min; - unsigned long u_volt_max; - unsigned long u_amp; - unsigned long clock_latency_ns; - - struct device_opp *dev_opp; - struct rcu_head rcu_head; - - struct device_node *np; -}; - -/** - * struct device_list_opp - devices managed by 'struct device_opp' - * @node: list node - * @dev: device to which the struct object belongs - * @rcu_head: RCU callback head used for deferred freeing - * - * This is an internal data structure maintaining the list of devices that are - * managed by 'struct device_opp'. - */ -struct device_list_opp { - struct list_head node; - const struct device *dev; - struct rcu_head rcu_head; -}; - -/** - * struct device_opp - Device opp structure - * @node: list node - contains the devices with OPPs that - * have been registered. Nodes once added are not modified in this - * list. - * RCU usage: nodes are not modified in the list of device_opp, - * however addition is possible and is secured by dev_opp_list_lock - * @srcu_head: notifier head to notify the OPP availability changes. - * @rcu_head: RCU callback head used for deferred freeing - * @dev_list: list of devices that share these OPPs - * @opp_list: list of opps - * @np: struct device_node pointer for opp's DT node. - * @shared_opp: OPP is shared between multiple devices. - * - * This is an internal data structure maintaining the link to opps attached to - * a device. This structure is not meant to be shared to users as it is - * meant for book keeping and private to OPP library. - * - * Because the opp structures can be used from both rcu and srcu readers, we - * need to wait for the grace period of both of them before freeing any - * resources. And so we have used kfree_rcu() from within call_srcu() handlers. - */ -struct device_opp { - struct list_head node; - - struct srcu_notifier_head srcu_head; - struct rcu_head rcu_head; - struct list_head dev_list; - struct list_head opp_list; - - struct device_node *np; - unsigned long clock_latency_ns_max; - bool shared_opp; - struct dev_pm_opp *suspend_opp; -}; +#include "opp.h" /* * The root of the list of all devices. All device_opp structures branch off @@ -200,7 +83,7 @@ static struct device_opp *_managed_opp(const struct device_node *np) * is a RCU protected pointer. This means that device_opp is valid as long * as we are under RCU lock. */ -static struct device_opp *_find_device_opp(struct device *dev) +struct device_opp *_find_device_opp(struct device *dev) { struct device_opp *dev_opp; @@ -579,8 +462,8 @@ static void _remove_list_dev(struct device_list_opp *list_dev, _kfree_list_dev_rcu); } -static struct device_list_opp *_add_list_dev(const struct device *dev, - struct device_opp *dev_opp) +struct device_list_opp *_add_list_dev(const struct device *dev, + struct device_opp *dev_opp) { struct device_list_opp *list_dev; @@ -1262,28 +1145,8 @@ unlock: } EXPORT_SYMBOL_GPL(dev_pm_opp_of_remove_table); -void dev_pm_opp_of_cpumask_remove_table(cpumask_var_t cpumask) -{ - struct device *cpu_dev; - int cpu; - - WARN_ON(cpumask_empty(cpumask)); - - for_each_cpu(cpu, cpumask) { - cpu_dev = get_cpu_device(cpu); - if (!cpu_dev) { - pr_err("%s: failed to get cpu%d device\n", __func__, - cpu); - continue; - } - - dev_pm_opp_of_remove_table(cpu_dev); - } -} -EXPORT_SYMBOL_GPL(dev_pm_opp_of_cpumask_remove_table); - /* Returns opp descriptor node for a device, caller must do of_node_put() */ -static struct device_node *_of_get_opp_desc_node(struct device *dev) +struct device_node *_of_get_opp_desc_node(struct device *dev) { /* * TODO: Support for multiple OPP tables. @@ -1427,133 +1290,4 @@ int dev_pm_opp_of_add_table(struct device *dev) return ret; } EXPORT_SYMBOL_GPL(dev_pm_opp_of_add_table); - -int dev_pm_opp_of_cpumask_add_table(cpumask_var_t cpumask) -{ - struct device *cpu_dev; - int cpu, ret = 0; - - WARN_ON(cpumask_empty(cpumask)); - - for_each_cpu(cpu, cpumask) { - cpu_dev = get_cpu_device(cpu); - if (!cpu_dev) { - pr_err("%s: failed to get cpu%d device\n", __func__, - cpu); - continue; - } - - ret = dev_pm_opp_of_add_table(cpu_dev); - if (ret) { - pr_err("%s: couldn't find opp table for cpu:%d, %d\n", - __func__, cpu, ret); - - /* Free all other OPPs */ - dev_pm_opp_of_cpumask_remove_table(cpumask); - break; - } - } - - return ret; -} -EXPORT_SYMBOL_GPL(dev_pm_opp_of_cpumask_add_table); - -/* Required only for V1 bindings, as v2 can manage it from DT itself */ -int dev_pm_opp_set_sharing_cpus(struct device *cpu_dev, cpumask_var_t cpumask) -{ - struct device_list_opp *list_dev; - struct device_opp *dev_opp; - struct device *dev; - int cpu, ret = 0; - - rcu_read_lock(); - - dev_opp = _find_device_opp(cpu_dev); - if (IS_ERR(dev_opp)) { - ret = -EINVAL; - goto out_rcu_read_unlock; - } - - for_each_cpu(cpu, cpumask) { - if (cpu == cpu_dev->id) - continue; - - dev = get_cpu_device(cpu); - if (!dev) { - dev_err(cpu_dev, "%s: failed to get cpu%d device\n", - __func__, cpu); - continue; - } - - list_dev = _add_list_dev(dev, dev_opp); - if (!list_dev) { - dev_err(dev, "%s: failed to add list-dev for cpu%d device\n", - __func__, cpu); - continue; - } - } -out_rcu_read_unlock: - rcu_read_unlock(); - - return 0; -} -EXPORT_SYMBOL_GPL(dev_pm_opp_set_sharing_cpus); - -/* - * Works only for OPP v2 bindings. - * - * cpumask should be already set to mask of cpu_dev->id. - * Returns -ENOENT if operating-points-v2 bindings aren't supported. - */ -int dev_pm_opp_of_get_sharing_cpus(struct device *cpu_dev, cpumask_var_t cpumask) -{ - struct device_node *np, *tmp_np; - struct device *tcpu_dev; - int cpu, ret = 0; - - /* Get OPP descriptor node */ - np = _of_get_opp_desc_node(cpu_dev); - if (!np) { - dev_dbg(cpu_dev, "%s: Couldn't find opp node: %ld\n", __func__, - PTR_ERR(np)); - return -ENOENT; - } - - /* OPPs are shared ? */ - if (!of_property_read_bool(np, "opp-shared")) - goto put_cpu_node; - - for_each_possible_cpu(cpu) { - if (cpu == cpu_dev->id) - continue; - - tcpu_dev = get_cpu_device(cpu); - if (!tcpu_dev) { - dev_err(cpu_dev, "%s: failed to get cpu%d device\n", - __func__, cpu); - ret = -ENODEV; - goto put_cpu_node; - } - - /* Get OPP descriptor node */ - tmp_np = _of_get_opp_desc_node(tcpu_dev); - if (!tmp_np) { - dev_err(tcpu_dev, "%s: Couldn't find opp node: %ld\n", - __func__, PTR_ERR(tmp_np)); - ret = PTR_ERR(tmp_np); - goto put_cpu_node; - } - - /* CPUs are sharing opp node */ - if (np == tmp_np) - cpumask_set_cpu(cpu, cpumask); - - of_node_put(tmp_np); - } - -put_cpu_node: - of_node_put(np); - return ret; -} -EXPORT_SYMBOL_GPL(dev_pm_opp_of_get_sharing_cpus); #endif diff --git a/drivers/base/power/opp/cpu.c b/drivers/base/power/opp/cpu.c index 0dd0330..3d946b5 100644 --- a/drivers/base/power/opp/cpu.c +++ b/drivers/base/power/opp/cpu.c @@ -10,17 +10,18 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ +#include #include -#include #include #include #include -#include -#include -#include +#include #include +#include "opp.h" + #ifdef CONFIG_CPU_FREQ + /** * dev_pm_opp_init_cpufreq_table() - create a cpufreq table for a device * @dev: device for which we do this operation @@ -114,3 +115,154 @@ void dev_pm_opp_free_cpufreq_table(struct device *dev, } EXPORT_SYMBOL_GPL(dev_pm_opp_free_cpufreq_table); #endif /* CONFIG_CPU_FREQ */ + +/* Required only for V1 bindings, as v2 can manage it from DT itself */ +int dev_pm_opp_set_sharing_cpus(struct device *cpu_dev, cpumask_var_t cpumask) +{ + struct device_list_opp *list_dev; + struct device_opp *dev_opp; + struct device *dev; + int cpu, ret = 0; + + rcu_read_lock(); + + dev_opp = _find_device_opp(cpu_dev); + if (IS_ERR(dev_opp)) { + ret = -EINVAL; + goto out_rcu_read_unlock; + } + + for_each_cpu(cpu, cpumask) { + if (cpu == cpu_dev->id) + continue; + + dev = get_cpu_device(cpu); + if (!dev) { + dev_err(cpu_dev, "%s: failed to get cpu%d device\n", + __func__, cpu); + continue; + } + + list_dev = _add_list_dev(dev, dev_opp); + if (!list_dev) { + dev_err(dev, "%s: failed to add list-dev for cpu%d device\n", + __func__, cpu); + continue; + } + } +out_rcu_read_unlock: + rcu_read_unlock(); + + return 0; +} +EXPORT_SYMBOL_GPL(dev_pm_opp_set_sharing_cpus); + +#ifdef CONFIG_OF +void dev_pm_opp_of_cpumask_remove_table(cpumask_var_t cpumask) +{ + struct device *cpu_dev; + int cpu; + + WARN_ON(cpumask_empty(cpumask)); + + for_each_cpu(cpu, cpumask) { + cpu_dev = get_cpu_device(cpu); + if (!cpu_dev) { + pr_err("%s: failed to get cpu%d device\n", __func__, + cpu); + continue; + } + + dev_pm_opp_of_remove_table(cpu_dev); + } +} +EXPORT_SYMBOL_GPL(dev_pm_opp_of_cpumask_remove_table); + +int dev_pm_opp_of_cpumask_add_table(cpumask_var_t cpumask) +{ + struct device *cpu_dev; + int cpu, ret = 0; + + WARN_ON(cpumask_empty(cpumask)); + + for_each_cpu(cpu, cpumask) { + cpu_dev = get_cpu_device(cpu); + if (!cpu_dev) { + pr_err("%s: failed to get cpu%d device\n", __func__, + cpu); + continue; + } + + ret = dev_pm_opp_of_add_table(cpu_dev); + if (ret) { + pr_err("%s: couldn't find opp table for cpu:%d, %d\n", + __func__, cpu, ret); + + /* Free all other OPPs */ + dev_pm_opp_of_cpumask_remove_table(cpumask); + break; + } + } + + return ret; +} +EXPORT_SYMBOL_GPL(dev_pm_opp_of_cpumask_add_table); + +/* + * Works only for OPP v2 bindings. + * + * cpumask should be already set to mask of cpu_dev->id. + * Returns -ENOENT if operating-points-v2 bindings aren't supported. + */ +int dev_pm_opp_of_get_sharing_cpus(struct device *cpu_dev, cpumask_var_t cpumask) +{ + struct device_node *np, *tmp_np; + struct device *tcpu_dev; + int cpu, ret = 0; + + /* Get OPP descriptor node */ + np = _of_get_opp_desc_node(cpu_dev); + if (!np) { + dev_dbg(cpu_dev, "%s: Couldn't find opp node: %ld\n", __func__, + PTR_ERR(np)); + return -ENOENT; + } + + /* OPPs are shared ? */ + if (!of_property_read_bool(np, "opp-shared")) + goto put_cpu_node; + + for_each_possible_cpu(cpu) { + if (cpu == cpu_dev->id) + continue; + + tcpu_dev = get_cpu_device(cpu); + if (!tcpu_dev) { + dev_err(cpu_dev, "%s: failed to get cpu%d device\n", + __func__, cpu); + ret = -ENODEV; + goto put_cpu_node; + } + + /* Get OPP descriptor node */ + tmp_np = _of_get_opp_desc_node(tcpu_dev); + if (!tmp_np) { + dev_err(tcpu_dev, "%s: Couldn't find opp node: %ld\n", + __func__, PTR_ERR(tmp_np)); + ret = PTR_ERR(tmp_np); + goto put_cpu_node; + } + + /* CPUs are sharing opp node */ + if (np == tmp_np) + cpumask_set_cpu(cpu, cpumask); + + of_node_put(tmp_np); + } + +put_cpu_node: + of_node_put(np); + return ret; +} +EXPORT_SYMBOL_GPL(dev_pm_opp_of_get_sharing_cpus); +#endif diff --git a/drivers/base/power/opp/opp.h b/drivers/base/power/opp/opp.h new file mode 100644 index 0000000..dcb38f7 --- /dev/null +++ b/drivers/base/power/opp/opp.h @@ -0,0 +1,143 @@ +/* + * Generic OPP Interface + * + * Copyright (C) 2009-2010 Texas Instruments Incorporated. + * Nishanth Menon + * Romit Dasgupta + * Kevin Hilman + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __DRIVER_OPP_H__ +#define __DRIVER_OPP_H__ + +#include +#include +#include +#include +#include +#include + +/* + * Internal data structure organization with the OPP layer library is as + * follows: + * dev_opp_list (root) + * |- device 1 (represents voltage domain 1) + * | |- opp 1 (availability, freq, voltage) + * | |- opp 2 .. + * ... ... + * | `- opp n .. + * |- device 2 (represents the next voltage domain) + * ... + * `- device m (represents mth voltage domain) + * device 1, 2.. are represented by dev_opp structure while each opp + * is represented by the opp structure. + */ + +/** + * struct dev_pm_opp - Generic OPP description structure + * @node: opp list node. The nodes are maintained throughout the lifetime + * of boot. It is expected only an optimal set of OPPs are + * added to the library by the SoC framework. + * RCU usage: opp list is traversed with RCU locks. node + * modification is possible realtime, hence the modifications + * are protected by the dev_opp_list_lock for integrity. + * IMPORTANT: the opp nodes should be maintained in increasing + * order. + * @dynamic: not-created from static DT entries. + * @available: true/false - marks if this OPP as available or not + * @turbo: true if turbo (boost) OPP + * @rate: Frequency in hertz + * @u_volt: Target voltage in microvolts corresponding to this OPP + * @u_volt_min: Minimum voltage in microvolts corresponding to this OPP + * @u_volt_max: Maximum voltage in microvolts corresponding to this OPP + * @u_amp: Maximum current drawn by the device in microamperes + * @clock_latency_ns: Latency (in nanoseconds) of switching to this OPP's + * frequency from any other OPP's frequency. + * @dev_opp: points back to the device_opp struct this opp belongs to + * @rcu_head: RCU callback head used for deferred freeing + * @np: OPP's device node. + * + * This structure stores the OPP information for a given device. + */ +struct dev_pm_opp { + struct list_head node; + + bool available; + bool dynamic; + bool turbo; + unsigned long rate; + + unsigned long u_volt; + unsigned long u_volt_min; + unsigned long u_volt_max; + unsigned long u_amp; + unsigned long clock_latency_ns; + + struct device_opp *dev_opp; + struct rcu_head rcu_head; + + struct device_node *np; +}; + +/** + * struct device_list_opp - devices managed by 'struct device_opp' + * @node: list node + * @dev: device to which the struct object belongs + * @rcu_head: RCU callback head used for deferred freeing + * + * This is an internal data structure maintaining the list of devices that are + * managed by 'struct device_opp'. + */ +struct device_list_opp { + struct list_head node; + const struct device *dev; + struct rcu_head rcu_head; +}; + +/** + * struct device_opp - Device opp structure + * @node: list node - contains the devices with OPPs that + * have been registered. Nodes once added are not modified in this + * list. + * RCU usage: nodes are not modified in the list of device_opp, + * however addition is possible and is secured by dev_opp_list_lock + * @srcu_head: notifier head to notify the OPP availability changes. + * @rcu_head: RCU callback head used for deferred freeing + * @dev_list: list of devices that share these OPPs + * @opp_list: list of opps + * @np: struct device_node pointer for opp's DT node. + * @shared_opp: OPP is shared between multiple devices. + * + * This is an internal data structure maintaining the link to opps attached to + * a device. This structure is not meant to be shared to users as it is + * meant for book keeping and private to OPP library. + * + * Because the opp structures can be used from both rcu and srcu readers, we + * need to wait for the grace period of both of them before freeing any + * resources. And so we have used kfree_rcu() from within call_srcu() handlers. + */ +struct device_opp { + struct list_head node; + + struct srcu_notifier_head srcu_head; + struct rcu_head rcu_head; + struct list_head dev_list; + struct list_head opp_list; + + struct device_node *np; + unsigned long clock_latency_ns_max; + bool shared_opp; + struct dev_pm_opp *suspend_opp; +}; + +/* Routines internal to opp core */ +struct device_opp *_find_device_opp(struct device *dev); +struct device_list_opp *_add_list_dev(const struct device *dev, + struct device_opp *dev_opp); +struct device_node *_of_get_opp_desc_node(struct device *dev); + +#endif /* __DRIVER_OPP_H__ */ -- cgit v0.10.2 From 636c19d38920caee61d694ef306d110d33935038 Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Wed, 9 Sep 2015 23:59:41 +0200 Subject: ACPI / scan: constify first argument of struct acpi_scan_handler::match One wouldn't expect a "match" function modify the string it searches for, and indeed the only instance of the struct acpi_scan_handler::match callback, acpi_pnp_match, can easily be changed. While there, update its helper matching_id(). This is also preparation for constifying struct acpi_hardware_id::id. Signed-off-by: Rasmus Villemoes Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/acpi_pnp.c b/drivers/acpi/acpi_pnp.c index c58940b..48fc3ad 100644 --- a/drivers/acpi/acpi_pnp.c +++ b/drivers/acpi/acpi_pnp.c @@ -316,7 +316,7 @@ static const struct acpi_device_id acpi_pnp_device_ids[] = { {""}, }; -static bool matching_id(char *idstr, char *list_id) +static bool matching_id(const char *idstr, const char *list_id) { int i; @@ -333,7 +333,7 @@ static bool matching_id(char *idstr, char *list_id) return true; } -static bool acpi_pnp_match(char *idstr, const struct acpi_device_id **matchid) +static bool acpi_pnp_match(const char *idstr, const struct acpi_device_id **matchid) { const struct acpi_device_id *devid; diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index 5ba8fb6..9a1b46c 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -129,7 +129,7 @@ static inline struct acpi_hotplug_profile *to_acpi_hotplug_profile( struct acpi_scan_handler { const struct acpi_device_id *ids; struct list_head list_node; - bool (*match)(char *idstr, const struct acpi_device_id **matchid); + bool (*match)(const char *idstr, const struct acpi_device_id **matchid); int (*attach)(struct acpi_device *dev, const struct acpi_device_id *id); void (*detach)(struct acpi_device *dev); void (*bind)(struct device *phys_dev); -- cgit v0.10.2 From 844142c3f80c66fb2c311b118d60abdfe02322cb Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Wed, 9 Sep 2015 23:59:42 +0200 Subject: ACPI / scan: constify struct acpi_hardware_id::id This is preparation for using kstrdup_const to initialize that member. Signed-off-by: Rasmus Villemoes Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 01136b8..a3eaf20 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -1472,7 +1472,7 @@ bool acpi_device_is_present(struct acpi_device *adev) } static bool acpi_scan_handler_matching(struct acpi_scan_handler *handler, - char *idstr, + const char *idstr, const struct acpi_device_id **matchid) { const struct acpi_device_id *devid; @@ -1491,7 +1491,7 @@ static bool acpi_scan_handler_matching(struct acpi_scan_handler *handler, return false; } -static struct acpi_scan_handler *acpi_scan_match_handler(char *idstr, +static struct acpi_scan_handler *acpi_scan_match_handler(const char *idstr, const struct acpi_device_id **matchid) { struct acpi_scan_handler *handler; diff --git a/drivers/pnp/pnpacpi/core.c b/drivers/pnp/pnpacpi/core.c index 5153d1d..9113876 100644 --- a/drivers/pnp/pnpacpi/core.c +++ b/drivers/pnp/pnpacpi/core.c @@ -207,7 +207,7 @@ struct pnp_protocol pnpacpi_protocol = { }; EXPORT_SYMBOL(pnpacpi_protocol); -static char *__init pnpacpi_get_id(struct acpi_device *device) +static const char *__init pnpacpi_get_id(struct acpi_device *device) { struct acpi_hardware_id *id; @@ -222,7 +222,7 @@ static char *__init pnpacpi_get_id(struct acpi_device *device) static int __init pnpacpi_add_device(struct acpi_device *device) { struct pnp_dev *dev; - char *pnpid; + const char *pnpid; struct acpi_hardware_id *id; int error; diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index 9a1b46c..fec0029 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -227,7 +227,7 @@ typedef char acpi_device_class[20]; struct acpi_hardware_id { struct list_head list; - char *id; + const char *id; }; struct acpi_pnp_type { -- cgit v0.10.2 From 6a0d12ef993c5291047d72e88d73d0aa2b967335 Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Wed, 9 Sep 2015 23:59:43 +0200 Subject: ACPI / scan: use kstrdup_const() in acpi_add_id() Empirically, acpi_add_id is mostly called with string literals, so using kstrdup_const for initializing struct acpi_hardware_id::id saves a little run-time memory and a string copy. Signed-off-by: Rasmus Villemoes Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index a3eaf20..afaac47 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -1184,7 +1184,7 @@ static void acpi_add_id(struct acpi_device_pnp *pnp, const char *dev_id) if (!id) return; - id->id = kstrdup(dev_id, GFP_KERNEL); + id->id = kstrdup_const(dev_id, GFP_KERNEL); if (!id->id) { kfree(id); return; @@ -1322,7 +1322,7 @@ void acpi_free_pnp_ids(struct acpi_device_pnp *pnp) struct acpi_hardware_id *id, *tmp; list_for_each_entry_safe(id, tmp, &pnp->ids, list) { - kfree(id->id); + kfree_const(id->id); kfree(id); } kfree(pnp->unique_id); -- cgit v0.10.2 From c33cab60bc0e05ddf1ef1b2cba8d16dd010f600a Mon Sep 17 00:00:00 2001 From: Rami Rosen Date: Thu, 10 Sep 2015 23:48:09 +0300 Subject: ACPI: change init_acpi_device_notify() to return void This patch changes the type of the return value of the init_acpi_device_notify() method to be void, as this method never fails and its return value is never used. Signed-off-by: Rami Rosen Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/glue.c b/drivers/acpi/glue.c index b9657af..1470ae4 100644 --- a/drivers/acpi/glue.c +++ b/drivers/acpi/glue.c @@ -351,13 +351,12 @@ static int acpi_platform_notify_remove(struct device *dev) return 0; } -int __init init_acpi_device_notify(void) +void __init init_acpi_device_notify(void) { if (platform_notify || platform_notify_remove) { printk(KERN_ERR PREFIX "Can't use platform_notify\n"); - return 0; + return; } platform_notify = acpi_platform_notify; platform_notify_remove = acpi_platform_notify_remove; - return 0; } diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index 9e42621..e72c6a6 100644 --- a/drivers/acpi/internal.h +++ b/drivers/acpi/internal.h @@ -21,7 +21,7 @@ #define PREFIX "ACPI: " acpi_status acpi_os_initialize1(void); -int init_acpi_device_notify(void); +void init_acpi_device_notify(void); int acpi_scan_init(void); void acpi_pci_root_init(void); void acpi_pci_link_init(void); -- cgit v0.10.2 From f934c745079ea3a15e8619350faf681dad42cc91 Mon Sep 17 00:00:00 2001 From: Rami Rosen Date: Fri, 11 Sep 2015 17:49:22 +0300 Subject: ACPI: change acpi_sleep_proc_init() to return void This patch changes the type of the return value of the acpi_sleep_proc_init() method to be void, as this method never fails and its return value is never used. Signed-off-by: Rami Rosen \ [ rjw : Fixed up the static inline stub ] Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index e72c6a6..c31787b 100644 --- a/drivers/acpi/internal.h +++ b/drivers/acpi/internal.h @@ -179,13 +179,13 @@ static inline int acpi_sleep_init(void) { return -ENXIO; } #endif #ifdef CONFIG_ACPI_SLEEP -int acpi_sleep_proc_init(void); +void acpi_sleep_proc_init(void); int suspend_nvs_alloc(void); void suspend_nvs_free(void); int suspend_nvs_save(void); void suspend_nvs_restore(void); #else -static inline int acpi_sleep_proc_init(void) { return 0; } +static inline void acpi_sleep_proc_init(void) {} static inline int suspend_nvs_alloc(void) { return 0; } static inline void suspend_nvs_free(void) {} static inline int suspend_nvs_save(void) { return 0; } diff --git a/drivers/acpi/proc.c b/drivers/acpi/proc.c index 75c28ea..2a35815 100644 --- a/drivers/acpi/proc.c +++ b/drivers/acpi/proc.c @@ -144,11 +144,9 @@ static const struct file_operations acpi_system_wakeup_device_fops = { .release = single_release, }; -int __init acpi_sleep_proc_init(void) +void __init acpi_sleep_proc_init(void) { /* 'wakeup device' [R/W] */ proc_create("wakeup", S_IFREG | S_IRUGO | S_IWUSR, acpi_root_dir, &acpi_system_wakeup_device_fops); - - return 0; } -- cgit v0.10.2 From a6f5f0dd4e21191ce35030dd4d6421e1cca10ee4 Mon Sep 17 00:00:00 2001 From: Alexandra Yates Date: Tue, 15 Sep 2015 10:32:46 -0700 Subject: PM / sleep: Report interrupt that caused system wakeup Add a sysfs attribute, /sys/power/pm_wakeup_irq, reporting the IRQ number of the first wakeup interrupt (that is, the first interrupt from an IRQ line armed for system wakeup) seen by the kernel during the most recent system suspend/resume cycle. This feature will be useful for system wakeup diagnostics of spurious wakeup interrupts. Signed-off-by: Alexandra Yates [ rjw: Fixed up pm_wakeup_irq definition ] Signed-off-by: Rafael J. Wysocki diff --git a/Documentation/ABI/testing/sysfs-power b/Documentation/ABI/testing/sysfs-power index f455181..50b368d 100644 --- a/Documentation/ABI/testing/sysfs-power +++ b/Documentation/ABI/testing/sysfs-power @@ -256,3 +256,15 @@ Description: Writing a "1" enables this printing while writing a "0" disables it. The default value is "0". Reading from this file will display the current value. + +What: /sys/power/pm_wakeup_irq +Date: April 2015 +Contact: Alexandra Yates +Description: + The /sys/power/pm_wakeup_irq file reports to user space the IRQ + number of the first wakeup interrupt (that is, the first + interrupt from an IRQ line armed for system wakeup) seen by the + kernel during the most recent system suspend/resume cycle. + + This output is useful for system wakeup diagnostics of spurious + wakeup interrupts. diff --git a/drivers/base/power/wakeup.c b/drivers/base/power/wakeup.c index 51f15bc..3b361ec 100644 --- a/drivers/base/power/wakeup.c +++ b/drivers/base/power/wakeup.c @@ -25,6 +25,9 @@ */ bool events_check_enabled __read_mostly; +/* First wakeup IRQ seen by the kernel in the last cycle. */ +unsigned int pm_wakeup_irq __read_mostly; + /* If set and the system is suspending, terminate the suspend. */ static bool pm_abort_suspend __read_mostly; @@ -868,6 +871,15 @@ EXPORT_SYMBOL_GPL(pm_system_wakeup); void pm_wakeup_clear(void) { pm_abort_suspend = false; + pm_wakeup_irq = 0; +} + +void pm_system_irq_wakeup(unsigned int irq_number) +{ + if (pm_wakeup_irq == 0) { + pm_wakeup_irq = irq_number; + pm_system_wakeup(); + } } /** diff --git a/include/linux/suspend.h b/include/linux/suspend.h index 5efe743..33aaf9a 100644 --- a/include/linux/suspend.h +++ b/include/linux/suspend.h @@ -387,10 +387,12 @@ extern int unregister_pm_notifier(struct notifier_block *nb); /* drivers/base/power/wakeup.c */ extern bool events_check_enabled; +extern unsigned int pm_wakeup_irq; extern bool pm_wakeup_pending(void); extern void pm_system_wakeup(void); extern void pm_wakeup_clear(void); +extern void pm_system_irq_wakeup(unsigned int irq_number); extern bool pm_get_wakeup_count(unsigned int *count, bool block); extern bool pm_save_wakeup_count(unsigned int count); extern void pm_wakep_autosleep_enabled(bool set); @@ -440,6 +442,7 @@ static inline int unregister_pm_notifier(struct notifier_block *nb) static inline bool pm_wakeup_pending(void) { return false; } static inline void pm_system_wakeup(void) {} static inline void pm_wakeup_clear(void) {} +static inline void pm_system_irq_wakeup(unsigned int irq_number) {} static inline void lock_system_sleep(void) {} static inline void unlock_system_sleep(void) {} diff --git a/kernel/irq/pm.c b/kernel/irq/pm.c index 21c6261..e80c440 100644 --- a/kernel/irq/pm.c +++ b/kernel/irq/pm.c @@ -21,7 +21,7 @@ bool irq_pm_check_wakeup(struct irq_desc *desc) desc->istate |= IRQS_SUSPENDED | IRQS_PENDING; desc->depth++; irq_disable(desc); - pm_system_wakeup(); + pm_system_irq_wakeup(irq_desc_get_irq(desc)); return true; } return false; diff --git a/kernel/power/main.c b/kernel/power/main.c index 63d395b..b2dd4d9 100644 --- a/kernel/power/main.c +++ b/kernel/power/main.c @@ -272,6 +272,22 @@ static inline void pm_print_times_init(void) { pm_print_times_enabled = !!initcall_debug; } + +static ssize_t pm_wakeup_irq_show(struct kobject *kobj, + struct kobj_attribute *attr, + char *buf) +{ + return pm_wakeup_irq ? sprintf(buf, "%u\n", pm_wakeup_irq) : -ENODATA; +} + +static ssize_t pm_wakeup_irq_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t n) +{ + return -EINVAL; +} +power_attr(pm_wakeup_irq); + #else /* !CONFIG_PM_SLEEP_DEBUG */ static inline void pm_print_times_init(void) {} #endif /* CONFIG_PM_SLEEP_DEBUG */ @@ -604,6 +620,7 @@ static struct attribute * g[] = { #endif #ifdef CONFIG_PM_SLEEP_DEBUG &pm_print_times_attr.attr, + &pm_wakeup_irq_attr.attr, #endif #endif #ifdef CONFIG_FREEZER -- cgit v0.10.2 From 3f5c8d3187852b1cbed8546169e6293d6d421751 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Mon, 14 Sep 2015 17:37:35 +0300 Subject: device property: Add fwnode_property_match_string() Sometimes it is useful to be able to extract an index of certain string value from an array of strings. A typical use case is to give a name to a DMA channel, PWM, clock and so on. Provide an implementation using unified device property accessors that follows of_property_match_string() but works for all supported fwnodes. Signed-off-by: Mika Westerberg Signed-off-by: Rafael J. Wysocki diff --git a/drivers/base/property.c b/drivers/base/property.c index 660ecec..de40623 100644 --- a/drivers/base/property.c +++ b/drivers/base/property.c @@ -287,6 +287,28 @@ int device_property_read_string(struct device *dev, const char *propname, } EXPORT_SYMBOL_GPL(device_property_read_string); +/** + * device_property_match_string - find a string in an array and return index + * @dev: Device to get the property of + * @propname: Name of the property holding the array + * @string: String to look for + * + * Find a given string in a string array and if it is found return the + * index back. + * + * Return: %0 if the property was found (success), + * %-EINVAL if given arguments are not valid, + * %-ENODATA if the property does not have a value, + * %-EPROTO if the property is not an array of strings, + * %-ENXIO if no suitable firmware interface is present. + */ +int device_property_match_string(struct device *dev, const char *propname, + const char *string) +{ + return fwnode_property_match_string(dev_fwnode(dev), propname, string); +} +EXPORT_SYMBOL_GPL(device_property_match_string); + #define OF_DEV_PROP_READ_ARRAY(node, propname, type, val, nval) \ (val) ? of_property_read_##type##_array((node), (propname), (val), (nval)) \ : of_property_count_elems_of_size((node), (propname), sizeof(type)) @@ -479,6 +501,52 @@ int fwnode_property_read_string(struct fwnode_handle *fwnode, EXPORT_SYMBOL_GPL(fwnode_property_read_string); /** + * fwnode_property_match_string - find a string in an array and return index + * @fwnode: Firmware node to get the property of + * @propname: Name of the property holding the array + * @string: String to look for + * + * Find a given string in a string array and if it is found return the + * index back. + * + * Return: %0 if the property was found (success), + * %-EINVAL if given arguments are not valid, + * %-ENODATA if the property does not have a value, + * %-EPROTO if the property is not an array of strings, + * %-ENXIO if no suitable firmware interface is present. + */ +int fwnode_property_match_string(struct fwnode_handle *fwnode, + const char *propname, const char *string) +{ + const char **values; + int nval, ret, i; + + nval = fwnode_property_read_string_array(fwnode, propname, NULL, 0); + if (nval < 0) + return nval; + + values = kcalloc(nval, sizeof(*values), GFP_KERNEL); + if (!values) + return -ENOMEM; + + ret = fwnode_property_read_string_array(fwnode, propname, values, nval); + if (ret < 0) + goto out; + + ret = -ENODATA; + for (i = 0; i < nval; i++) { + if (!strcmp(values[i], string)) { + ret = i; + break; + } + } +out: + kfree(values); + return ret; +} +EXPORT_SYMBOL_GPL(fwnode_property_match_string); + +/** * device_get_next_child_node - Return the next child node handle for a device * @dev: Device to find the next child node for. * @child: Handle to one of the device's child nodes or a null handle. diff --git a/include/linux/property.h b/include/linux/property.h index a59c6ee..463de52 100644 --- a/include/linux/property.h +++ b/include/linux/property.h @@ -40,6 +40,8 @@ int device_property_read_string_array(struct device *dev, const char *propname, const char **val, size_t nval); int device_property_read_string(struct device *dev, const char *propname, const char **val); +int device_property_match_string(struct device *dev, + const char *propname, const char *string); bool fwnode_property_present(struct fwnode_handle *fwnode, const char *propname); int fwnode_property_read_u8_array(struct fwnode_handle *fwnode, @@ -59,6 +61,8 @@ int fwnode_property_read_string_array(struct fwnode_handle *fwnode, size_t nval); int fwnode_property_read_string(struct fwnode_handle *fwnode, const char *propname, const char **val); +int fwnode_property_match_string(struct fwnode_handle *fwnode, + const char *propname, const char *string); struct fwnode_handle *device_get_next_child_node(struct device *dev, struct fwnode_handle *child); -- cgit v0.10.2 From 3f4232ee8b53cf2266d227af299aa511d2d91d11 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Mon, 14 Sep 2015 17:37:36 +0300 Subject: acpi-dma: Add support for "dma-names" device property The current implementation hard codes the two supported channels so that "tx" is always 0 and "rx" is always 1. This is because there has been no suitable way in ACPI to name resources. With _DSD device properties we can finally do this: Device (SPI1) { Name (_CRS, ResourceTemplate () { ... FixedDMA (0x0000, 0x0000, Width32bit) FixedDMA (0x0001, 0x0001, Width32bit) }) Name (_DSD, Package () { ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"), Package () { Package () {"dma-names", Package () {"tx", "rx"}} }, }) } The names "tx" and "rx" now provide index of the FixedDMA resource in question. Modify acpi_dma_request_slave_chan_by_name() so that it looks for "dma-names" property first and only then fall back using hardcoded indices. The DT "dma-names" binding that we reuse for ACPI is documented in Documentation/devicetree/bindings/dma/dma.txt. Signed-off-by: Mika Westerberg Acked-by: Andy Shevchenko Acked-by: Vinod Koul Signed-off-by: Rafael J. Wysocki diff --git a/drivers/dma/acpi-dma.c b/drivers/dma/acpi-dma.c index 5a63564..981a38f 100644 --- a/drivers/dma/acpi-dma.c +++ b/drivers/dma/acpi-dma.c @@ -21,6 +21,7 @@ #include #include #include +#include static LIST_HEAD(acpi_dma_list); static DEFINE_MUTEX(acpi_dma_lock); @@ -413,21 +414,29 @@ EXPORT_SYMBOL_GPL(acpi_dma_request_slave_chan_by_index); * translate the names "tx" and "rx" here based on the most common case where * the first FixedDMA descriptor is TX and second is RX. * + * If the device has "dma-names" property the FixedDMA descriptor indices + * are retrieved based on those. Otherwise the function falls back using + * hardcoded indices. + * * Return: * Pointer to appropriate dma channel on success or an error pointer. */ struct dma_chan *acpi_dma_request_slave_chan_by_name(struct device *dev, const char *name) { - size_t index; - - if (!strcmp(name, "tx")) - index = 0; - else if (!strcmp(name, "rx")) - index = 1; - else - return ERR_PTR(-ENODEV); + int index; + + index = device_property_match_string(dev, "dma-names", name); + if (index < 0) { + if (!strcmp(name, "tx")) + index = 0; + else if (!strcmp(name, "rx")) + index = 1; + else + return ERR_PTR(-ENODEV); + } + dev_dbg(dev, "found DMA channel \"%s\" at index %d\n", name, index); return acpi_dma_request_slave_chan_by_index(dev, index); } EXPORT_SYMBOL_GPL(acpi_dma_request_slave_chan_by_name); -- cgit v0.10.2 From 7236214c88454d96de7633e07a8314644d529f26 Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Wed, 9 Sep 2015 23:42:06 +0200 Subject: PM / wakeup: wakeup_source_create: use kstrdup_const Using kstrdup_const allows us to save a little runtime memory (and a string copy) in the common case where name is a string literal. Signed-off-by: Rasmus Villemoes Signed-off-by: Rafael J. Wysocki diff --git a/drivers/base/power/wakeup.c b/drivers/base/power/wakeup.c index 3b361ec..a1e0b9a 100644 --- a/drivers/base/power/wakeup.c +++ b/drivers/base/power/wakeup.c @@ -94,7 +94,7 @@ struct wakeup_source *wakeup_source_create(const char *name) if (!ws) return NULL; - wakeup_source_prepare(ws, name ? kstrdup(name, GFP_KERNEL) : NULL); + wakeup_source_prepare(ws, name ? kstrdup_const(name, GFP_KERNEL) : NULL); return ws; } EXPORT_SYMBOL_GPL(wakeup_source_create); @@ -157,7 +157,7 @@ void wakeup_source_destroy(struct wakeup_source *ws) wakeup_source_drop(ws); wakeup_source_record(ws); - kfree(ws->name); + kfree_const(ws->name); kfree(ws); } EXPORT_SYMBOL_GPL(wakeup_source_destroy); -- cgit v0.10.2 From 78ce6dbfa317c85cfb8a8dc0b5ee588645c795c1 Mon Sep 17 00:00:00 2001 From: Luis de Bethencourt Date: Wed, 16 Sep 2015 22:24:46 +0200 Subject: cpufreq: integrator: Fix module autoload for OF platform driver This platform driver has a OF device ID table but the OF module alias information is not created so module autoloading won't work. Signed-off-by: Luis de Bethencourt Acked-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki diff --git a/drivers/cpufreq/integrator-cpufreq.c b/drivers/cpufreq/integrator-cpufreq.c index 2faa421..79e3ff2 100644 --- a/drivers/cpufreq/integrator-cpufreq.c +++ b/drivers/cpufreq/integrator-cpufreq.c @@ -221,6 +221,8 @@ static const struct of_device_id integrator_cpufreq_match[] = { { }, }; +MODULE_DEVICE_TABLE(of, integrator_cpufreq_match); + static struct platform_driver integrator_cpufreq_driver = { .driver = { .name = "integrator-cpufreq", -- cgit v0.10.2 From 0700c047f66101c7d0d23e3583f1ae9c3c2dd71c Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Thu, 24 Sep 2015 14:54:48 +0800 Subject: ACPI / EC: Fix query handler related issues 1. acpi_ec_remove_query_handlers() This patch refines the query handler removal logic implemented in acpi_ec_remove_query_handler(), making it to invoke new acpi_ec_remove_query_handlers() API, and ensuring all other removal code paths to invoke the new API to honor the reference count of the query handlers. 2. acpi_ec_get_query_handler_by_value() This patch also refines the query handler search logic originally implemented in acpi_ec_query(), collecting it into acpi_ec_get_query_handler_by_value(). And since schedule_work() can ensure the serilization of acpi_ec_event_handler(), we needn't put the mutex_lock() around schedule_work(). Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index 42c66b6..2db0912 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -929,6 +929,23 @@ acpi_ec_get_query_handler(struct acpi_ec_query_handler *handler) return handler; } +static struct acpi_ec_query_handler * +acpi_ec_get_query_handler_by_value(struct acpi_ec *ec, u8 value) +{ + struct acpi_ec_query_handler *handler; + bool found = false; + + mutex_lock(&ec->mutex); + list_for_each_entry(handler, &ec->list, node) { + if (value == handler->query_bit) { + found = true; + break; + } + } + mutex_unlock(&ec->mutex); + return found ? acpi_ec_get_query_handler(handler) : NULL; +} + static void acpi_ec_query_handler_release(struct kref *kref) { struct acpi_ec_query_handler *handler = @@ -964,14 +981,15 @@ int acpi_ec_add_query_handler(struct acpi_ec *ec, u8 query_bit, } EXPORT_SYMBOL_GPL(acpi_ec_add_query_handler); -void acpi_ec_remove_query_handler(struct acpi_ec *ec, u8 query_bit) +static void acpi_ec_remove_query_handlers(struct acpi_ec *ec, + bool remove_all, u8 query_bit) { struct acpi_ec_query_handler *handler, *tmp; LIST_HEAD(free_list); mutex_lock(&ec->mutex); list_for_each_entry_safe(handler, tmp, &ec->list, node) { - if (query_bit == handler->query_bit) { + if (remove_all || query_bit == handler->query_bit) { list_del_init(&handler->node); list_add(&handler->node, &free_list); } @@ -980,6 +998,11 @@ void acpi_ec_remove_query_handler(struct acpi_ec *ec, u8 query_bit) list_for_each_entry_safe(handler, tmp, &free_list, node) acpi_ec_put_query_handler(handler); } + +void acpi_ec_remove_query_handler(struct acpi_ec *ec, u8 query_bit) +{ + acpi_ec_remove_query_handlers(ec, false, query_bit); +} EXPORT_SYMBOL_GPL(acpi_ec_remove_query_handler); static struct acpi_ec_query *acpi_ec_create_query(u8 *pval) @@ -1025,7 +1048,6 @@ static int acpi_ec_query(struct acpi_ec *ec, u8 *data) { u8 value = 0; int result; - struct acpi_ec_query_handler *handler; struct acpi_ec_query *q; q = acpi_ec_create_query(&value); @@ -1043,25 +1065,26 @@ static int acpi_ec_query(struct acpi_ec *ec, u8 *data) if (result) goto err_exit; - mutex_lock(&ec->mutex); - result = -ENODATA; - list_for_each_entry(handler, &ec->list, node) { - if (value == handler->query_bit) { - result = 0; - q->handler = acpi_ec_get_query_handler(handler); - ec_dbg_evt("Query(0x%02x) scheduled", - q->handler->query_bit); - /* - * It is reported that _Qxx are evaluated in a - * parallel way on Windows: - * https://bugzilla.kernel.org/show_bug.cgi?id=94411 - */ - if (!schedule_work(&q->work)) - result = -EBUSY; - break; - } + q->handler = acpi_ec_get_query_handler_by_value(ec, value); + if (!q->handler) { + result = -ENODATA; + goto err_exit; + } + + /* + * It is reported that _Qxx are evaluated in a parallel way on + * Windows: + * https://bugzilla.kernel.org/show_bug.cgi?id=94411 + * + * Put this log entry before schedule_work() in order to make + * it appearing before any other log entries occurred during the + * work queue execution. + */ + ec_dbg_evt("Query(0x%02x) scheduled", value); + if (!schedule_work(&q->work)) { + ec_dbg_evt("Query(0x%02x) overlapped", value); + result = -EBUSY; } - mutex_unlock(&ec->mutex); err_exit: if (result && q) @@ -1354,19 +1377,13 @@ static int acpi_ec_add(struct acpi_device *device) static int acpi_ec_remove(struct acpi_device *device) { struct acpi_ec *ec; - struct acpi_ec_query_handler *handler, *tmp; if (!device) return -EINVAL; ec = acpi_driver_data(device); ec_remove_handlers(ec); - mutex_lock(&ec->mutex); - list_for_each_entry_safe(handler, tmp, &ec->list, node) { - list_del(&handler->node); - kfree(handler); - } - mutex_unlock(&ec->mutex); + acpi_ec_remove_query_handlers(ec, true, 0); release_region(ec->data_addr, 1); release_region(ec->command_addr, 1); device->driver_data = NULL; -- cgit v0.10.2 From 611975470740b38084610e0a64ff4c6ff5aec89a Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Thu, 24 Sep 2015 14:54:54 +0800 Subject: ACPI / EC: Fix a race issue in acpi_ec_guard_event() In acpi_ec_guard_event(), EC transaction state machine variables should be checked with the EC spinlock locked. The bug doesn't trigger any real issue now because this bug can only occur when the ec_event_clearing=event mode is applied while there is no user currently using this mode. Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index 2db0912..f61a7c8 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -441,17 +441,31 @@ static void acpi_ec_complete_query(struct acpi_ec *ec) static bool acpi_ec_guard_event(struct acpi_ec *ec) { + bool guarded = true; + unsigned long flags; + + spin_lock_irqsave(&ec->lock, flags); + /* + * If firmware SCI_EVT clearing timing is "event", we actually + * don't know when the SCI_EVT will be cleared by firmware after + * evaluating _Qxx, so we need to re-check SCI_EVT after waiting an + * acceptable period. + * + * The guarding period begins when EC_FLAGS_QUERY_PENDING is + * flagged, which means SCI_EVT check has just been performed. + * But if the current transaction is ACPI_EC_COMMAND_QUERY, the + * guarding should have already been performed (via + * EC_FLAGS_QUERY_GUARDING) and should not be applied so that the + * ACPI_EC_COMMAND_QUERY transaction can be transitioned into + * ACPI_EC_COMMAND_POLL state immediately. + */ if (ec_event_clearing == ACPI_EC_EVT_TIMING_STATUS || ec_event_clearing == ACPI_EC_EVT_TIMING_QUERY || !test_bit(EC_FLAGS_QUERY_PENDING, &ec->flags) || (ec->curr && ec->curr->command == ACPI_EC_COMMAND_QUERY)) - return false; - - /* - * Postpone the query submission to allow the firmware to proceed, - * we shouldn't check SCI_EVT before the firmware reflagging it. - */ - return true; + guarded = false; + spin_unlock_irqrestore(&ec->lock, flags); + return guarded; } static int ec_transaction_polled(struct acpi_ec *ec) @@ -597,6 +611,7 @@ static int ec_guard(struct acpi_ec *ec) unsigned long guard = usecs_to_jiffies(ec_polling_guard); unsigned long timeout = ec->timestamp + guard; + /* Ensure guarding period before polling EC status */ do { if (ec_busy_polling) { /* Perform busy polling */ @@ -606,11 +621,13 @@ static int ec_guard(struct acpi_ec *ec) } else { /* * Perform wait polling - * - * For SCI_EVT clearing timing of "event", - * performing guarding before re-checking the - * SCI_EVT. Otherwise, such guarding is not needed - * due to the old practices. + * 1. Wait the transaction to be completed by the + * GPE handler after the transaction enters + * ACPI_EC_COMMAND_POLL state. + * 2. A special guarding logic is also required + * for event clearing mode "event" before the + * transaction enters ACPI_EC_COMMAND_POLL + * state. */ if (!ec_transaction_polled(ec) && !acpi_ec_guard_event(ec)) @@ -620,7 +637,6 @@ static int ec_guard(struct acpi_ec *ec) guard)) return 0; } - /* Guard the register accesses for the polling modes */ } while (time_before(jiffies, timeout)); return -ETIME; } -- cgit v0.10.2 From c6f7b48e7e21989f4cfc996837d55c595d5dbf87 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Thu, 10 Sep 2015 16:00:50 +0200 Subject: PM / Domains: Remove name based API for genpd As all users of the named based APIs now have converted to the non-named based APIs, the time has come to remove them. Signed-off-by: Ulf Hansson Acked-by: Geert Uytterhoeven Acked-by: Kevin Hilman Signed-off-by: Rafael J. Wysocki diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index 16550c6..a46427e 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -53,24 +53,6 @@ static LIST_HEAD(gpd_list); static DEFINE_MUTEX(gpd_list_lock); -static struct generic_pm_domain *pm_genpd_lookup_name(const char *domain_name) -{ - struct generic_pm_domain *genpd = NULL, *gpd; - - if (IS_ERR_OR_NULL(domain_name)) - return NULL; - - mutex_lock(&gpd_list_lock); - list_for_each_entry(gpd, &gpd_list, gpd_list_node) { - if (!strcmp(gpd->name, domain_name)) { - genpd = gpd; - break; - } - } - mutex_unlock(&gpd_list_lock); - return genpd; -} - /* * Get the generic PM domain for a particular struct device. * This validates the struct device pointer, the PM domain pointer, @@ -295,18 +277,6 @@ int pm_genpd_poweron(struct generic_pm_domain *genpd) return ret; } -/** - * pm_genpd_name_poweron - Restore power to a given PM domain and its masters. - * @domain_name: Name of the PM domain to power up. - */ -int pm_genpd_name_poweron(const char *domain_name) -{ - struct generic_pm_domain *genpd; - - genpd = pm_genpd_lookup_name(domain_name); - return genpd ? pm_genpd_poweron(genpd) : -EINVAL; -} - static int genpd_save_dev(struct generic_pm_domain *genpd, struct device *dev) { return GENPD_DEV_TIMED_CALLBACK(genpd, int, save_state, dev, @@ -1317,18 +1287,6 @@ int __pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev, } /** - * __pm_genpd_name_add_device - Find I/O PM domain and add a device to it. - * @domain_name: Name of the PM domain to add the device to. - * @dev: Device to be added. - * @td: Set of PM QoS timing parameters to attach to the device. - */ -int __pm_genpd_name_add_device(const char *domain_name, struct device *dev, - struct gpd_timing_data *td) -{ - return __pm_genpd_add_device(pm_genpd_lookup_name(domain_name), dev, td); -} - -/** * pm_genpd_remove_device - Remove a device from an I/O PM domain. * @genpd: PM domain to remove the device from. * @dev: Device to be removed. @@ -1429,35 +1387,6 @@ int pm_genpd_add_subdomain(struct generic_pm_domain *genpd, } /** - * pm_genpd_add_subdomain_names - Add a subdomain to an I/O PM domain. - * @master_name: Name of the master PM domain to add the subdomain to. - * @subdomain_name: Name of the subdomain to be added. - */ -int pm_genpd_add_subdomain_names(const char *master_name, - const char *subdomain_name) -{ - struct generic_pm_domain *master = NULL, *subdomain = NULL, *gpd; - - if (IS_ERR_OR_NULL(master_name) || IS_ERR_OR_NULL(subdomain_name)) - return -EINVAL; - - mutex_lock(&gpd_list_lock); - list_for_each_entry(gpd, &gpd_list, gpd_list_node) { - if (!master && !strcmp(gpd->name, master_name)) - master = gpd; - - if (!subdomain && !strcmp(gpd->name, subdomain_name)) - subdomain = gpd; - - if (master && subdomain) - break; - } - mutex_unlock(&gpd_list_lock); - - return pm_genpd_add_subdomain(master, subdomain); -} - -/** * pm_genpd_remove_subdomain - Remove a subdomain from an I/O PM domain. * @genpd: Master PM domain to remove the subdomain from. * @subdomain: Subdomain to be removed. @@ -1566,16 +1495,6 @@ int pm_genpd_attach_cpuidle(struct generic_pm_domain *genpd, int state) } /** - * pm_genpd_name_attach_cpuidle - Find PM domain and connect cpuidle to it. - * @name: Name of the domain to connect to cpuidle. - * @state: cpuidle state this domain can manipulate. - */ -int pm_genpd_name_attach_cpuidle(const char *name, int state) -{ - return pm_genpd_attach_cpuidle(pm_genpd_lookup_name(name), state); -} - -/** * pm_genpd_detach_cpuidle - Remove the cpuidle connection from a PM domain. * @genpd: PM domain to remove the cpuidle connection from. * @@ -1613,15 +1532,6 @@ int pm_genpd_detach_cpuidle(struct generic_pm_domain *genpd) return ret; } -/** - * pm_genpd_name_detach_cpuidle - Find PM domain and disconnect cpuidle from it. - * @name: Name of the domain to disconnect cpuidle from. - */ -int pm_genpd_name_detach_cpuidle(const char *name) -{ - return pm_genpd_detach_cpuidle(pm_genpd_lookup_name(name)); -} - /* Default device callbacks for generic PM domains. */ /** diff --git a/include/linux/pm_domain.h b/include/linux/pm_domain.h index b1cf7e7..443fc19 100644 --- a/include/linux/pm_domain.h +++ b/include/linux/pm_domain.h @@ -125,27 +125,18 @@ extern int __pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev, struct gpd_timing_data *td); -extern int __pm_genpd_name_add_device(const char *domain_name, - struct device *dev, - struct gpd_timing_data *td); - extern int pm_genpd_remove_device(struct generic_pm_domain *genpd, struct device *dev); extern int pm_genpd_add_subdomain(struct generic_pm_domain *genpd, struct generic_pm_domain *new_subdomain); -extern int pm_genpd_add_subdomain_names(const char *master_name, - const char *subdomain_name); extern int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd, struct generic_pm_domain *target); extern int pm_genpd_attach_cpuidle(struct generic_pm_domain *genpd, int state); -extern int pm_genpd_name_attach_cpuidle(const char *name, int state); extern int pm_genpd_detach_cpuidle(struct generic_pm_domain *genpd); -extern int pm_genpd_name_detach_cpuidle(const char *name); extern void pm_genpd_init(struct generic_pm_domain *genpd, struct dev_power_governor *gov, bool is_off); extern int pm_genpd_poweron(struct generic_pm_domain *genpd); -extern int pm_genpd_name_poweron(const char *domain_name); extern void pm_genpd_poweroff_unused(void); extern struct dev_power_governor simple_qos_governor; @@ -166,12 +157,6 @@ static inline int __pm_genpd_add_device(struct generic_pm_domain *genpd, { return -ENOSYS; } -static inline int __pm_genpd_name_add_device(const char *domain_name, - struct device *dev, - struct gpd_timing_data *td) -{ - return -ENOSYS; -} static inline int pm_genpd_remove_device(struct generic_pm_domain *genpd, struct device *dev) { @@ -182,11 +167,6 @@ static inline int pm_genpd_add_subdomain(struct generic_pm_domain *genpd, { return -ENOSYS; } -static inline int pm_genpd_add_subdomain_names(const char *master_name, - const char *subdomain_name) -{ - return -ENOSYS; -} static inline int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd, struct generic_pm_domain *target) { @@ -196,18 +176,10 @@ static inline int pm_genpd_attach_cpuidle(struct generic_pm_domain *genpd, int s { return -ENOSYS; } -static inline int pm_genpd_name_attach_cpuidle(const char *name, int state) -{ - return -ENOSYS; -} static inline int pm_genpd_detach_cpuidle(struct generic_pm_domain *genpd) { return -ENOSYS; } -static inline int pm_genpd_name_detach_cpuidle(const char *name) -{ - return -ENOSYS; -} static inline void pm_genpd_init(struct generic_pm_domain *genpd, struct dev_power_governor *gov, bool is_off) { @@ -216,10 +188,6 @@ static inline int pm_genpd_poweron(struct generic_pm_domain *genpd) { return -ENOSYS; } -static inline int pm_genpd_name_poweron(const char *domain_name) -{ - return -ENOSYS; -} static inline void pm_genpd_poweroff_unused(void) {} #endif @@ -229,12 +197,6 @@ static inline int pm_genpd_add_device(struct generic_pm_domain *genpd, return __pm_genpd_add_device(genpd, dev, NULL); } -static inline int pm_genpd_name_add_device(const char *domain_name, - struct device *dev) -{ - return __pm_genpd_name_add_device(domain_name, dev, NULL); -} - #ifdef CONFIG_PM_GENERIC_DOMAINS_SLEEP extern void pm_genpd_syscore_poweroff(struct device *dev); extern void pm_genpd_syscore_poweron(struct device *dev); -- cgit v0.10.2 From cea3ad93d9a5e054d916f1ad71da02cb306e4828 Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Tue, 1 Sep 2015 20:37:49 +0200 Subject: PM / Domains: Remove cpuidle attach The power domains code allows to tie a cpuidle state with a power domain. Preventing the cpuidle framework to enter a specific idle state by disabling from the power domain framework is a good idea. Unfortunately, the current implementation has some gaps with a SMP system and a complex cpuidle implementation. Enabling a power domain wakes up all the cpus even if a cpu does not belong to the power domain. There is some work to do a logical representation with the power domains of the hardware dependencies (eg. a cpu belongs to a power domains, these power domains belong to a higher power domain for a cluster, etc ...). A new code relying on the genpd hierarchy to disable the idle states would make more sense. As the unique user of this code has been removed, let's wipe out this code to prevent new user and to have a clean place to put a new implementation. Signed-off-by: Daniel Lezcano Acked-by: Kevin Hilman Signed-off-by: Rafael J. Wysocki diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index a46427e..a544887 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -122,19 +122,6 @@ static void genpd_sd_counter_inc(struct generic_pm_domain *genpd) smp_mb__after_atomic(); } -static void genpd_recalc_cpu_exit_latency(struct generic_pm_domain *genpd) -{ - s64 usecs64; - - if (!genpd->cpuidle_data) - return; - - usecs64 = genpd->power_on_latency_ns; - do_div(usecs64, NSEC_PER_USEC); - usecs64 += genpd->cpuidle_data->saved_exit_latency; - genpd->cpuidle_data->idle_state->exit_latency = usecs64; -} - static int genpd_power_on(struct generic_pm_domain *genpd, bool timed) { ktime_t time_start; @@ -158,7 +145,6 @@ static int genpd_power_on(struct generic_pm_domain *genpd, bool timed) genpd->power_on_latency_ns = elapsed_ns; genpd->max_off_time_changed = true; - genpd_recalc_cpu_exit_latency(genpd); pr_debug("%s: Power-%s latency exceeded, new value %lld ns\n", genpd->name, "on", elapsed_ns); @@ -222,13 +208,6 @@ static int __pm_genpd_poweron(struct generic_pm_domain *genpd) || (genpd->prepared_count > 0 && genpd->suspend_power_off)) return 0; - if (genpd->cpuidle_data) { - cpuidle_pause_and_lock(); - genpd->cpuidle_data->idle_state->disabled = true; - cpuidle_resume_and_unlock(); - goto out; - } - /* * The list is guaranteed not to change while the loop below is being * executed, unless one of the masters' .power_on() callbacks fiddles @@ -381,21 +360,6 @@ static int pm_genpd_poweroff(struct generic_pm_domain *genpd) return -EAGAIN; } - if (genpd->cpuidle_data) { - /* - * If cpuidle_data is set, cpuidle should turn the domain off - * when the CPU in it is idle. In that case we don't decrement - * the subdomain counts of the master domains, so that power is - * not removed from the current domain prematurely as a result - * of cutting off the masters' power. - */ - genpd->status = GPD_STATE_POWER_OFF; - cpuidle_pause_and_lock(); - genpd->cpuidle_data->idle_state->disabled = false; - cpuidle_resume_and_unlock(); - return 0; - } - if (genpd->power_off) { int ret; @@ -1433,105 +1397,6 @@ out: return ret; } -/** - * pm_genpd_attach_cpuidle - Connect the given PM domain with cpuidle. - * @genpd: PM domain to be connected with cpuidle. - * @state: cpuidle state this domain can disable/enable. - * - * Make a PM domain behave as though it contained a CPU core, that is, instead - * of calling its power down routine it will enable the given cpuidle state so - * that the cpuidle subsystem can power it down (if possible and desirable). - */ -int pm_genpd_attach_cpuidle(struct generic_pm_domain *genpd, int state) -{ - struct cpuidle_driver *cpuidle_drv; - struct gpd_cpuidle_data *cpuidle_data; - struct cpuidle_state *idle_state; - int ret = 0; - - if (IS_ERR_OR_NULL(genpd) || state < 0) - return -EINVAL; - - mutex_lock(&genpd->lock); - - if (genpd->cpuidle_data) { - ret = -EEXIST; - goto out; - } - cpuidle_data = kzalloc(sizeof(*cpuidle_data), GFP_KERNEL); - if (!cpuidle_data) { - ret = -ENOMEM; - goto out; - } - cpuidle_drv = cpuidle_driver_ref(); - if (!cpuidle_drv) { - ret = -ENODEV; - goto err_drv; - } - if (cpuidle_drv->state_count <= state) { - ret = -EINVAL; - goto err; - } - idle_state = &cpuidle_drv->states[state]; - if (!idle_state->disabled) { - ret = -EAGAIN; - goto err; - } - cpuidle_data->idle_state = idle_state; - cpuidle_data->saved_exit_latency = idle_state->exit_latency; - genpd->cpuidle_data = cpuidle_data; - genpd_recalc_cpu_exit_latency(genpd); - - out: - mutex_unlock(&genpd->lock); - return ret; - - err: - cpuidle_driver_unref(); - - err_drv: - kfree(cpuidle_data); - goto out; -} - -/** - * pm_genpd_detach_cpuidle - Remove the cpuidle connection from a PM domain. - * @genpd: PM domain to remove the cpuidle connection from. - * - * Remove the cpuidle connection set up by pm_genpd_attach_cpuidle() from the - * given PM domain. - */ -int pm_genpd_detach_cpuidle(struct generic_pm_domain *genpd) -{ - struct gpd_cpuidle_data *cpuidle_data; - struct cpuidle_state *idle_state; - int ret = 0; - - if (IS_ERR_OR_NULL(genpd)) - return -EINVAL; - - mutex_lock(&genpd->lock); - - cpuidle_data = genpd->cpuidle_data; - if (!cpuidle_data) { - ret = -ENODEV; - goto out; - } - idle_state = cpuidle_data->idle_state; - if (!idle_state->disabled) { - ret = -EAGAIN; - goto out; - } - idle_state->exit_latency = cpuidle_data->saved_exit_latency; - cpuidle_driver_unref(); - genpd->cpuidle_data = NULL; - kfree(cpuidle_data); - - out: - mutex_unlock(&genpd->lock); - return ret; -} - /* Default device callbacks for generic PM domains. */ /** diff --git a/include/linux/pm_domain.h b/include/linux/pm_domain.h index 443fc19..384b1d1 100644 --- a/include/linux/pm_domain.h +++ b/include/linux/pm_domain.h @@ -15,7 +15,6 @@ #include #include #include -#include /* Defines used for the flags field in the struct generic_pm_domain */ #define GENPD_FLAG_PM_CLK (1U << 0) /* PM domain uses PM clk */ @@ -38,11 +37,6 @@ struct gpd_dev_ops { bool (*active_wakeup)(struct device *dev); }; -struct gpd_cpuidle_data { - unsigned int saved_exit_latency; - struct cpuidle_state *idle_state; -}; - struct generic_pm_domain { struct dev_pm_domain domain; /* PM domain operations */ struct list_head gpd_list_node; /* Node in the global PM domains list */ @@ -68,7 +62,6 @@ struct generic_pm_domain { s64 max_off_time_ns; /* Maximum allowed "suspended" time. */ bool max_off_time_changed; bool cached_power_down_ok; - struct gpd_cpuidle_data *cpuidle_data; int (*attach_dev)(struct generic_pm_domain *domain, struct device *dev); void (*detach_dev)(struct generic_pm_domain *domain, @@ -131,8 +124,6 @@ extern int pm_genpd_add_subdomain(struct generic_pm_domain *genpd, struct generic_pm_domain *new_subdomain); extern int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd, struct generic_pm_domain *target); -extern int pm_genpd_attach_cpuidle(struct generic_pm_domain *genpd, int state); -extern int pm_genpd_detach_cpuidle(struct generic_pm_domain *genpd); extern void pm_genpd_init(struct generic_pm_domain *genpd, struct dev_power_governor *gov, bool is_off); @@ -172,14 +163,6 @@ static inline int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd, { return -ENOSYS; } -static inline int pm_genpd_attach_cpuidle(struct generic_pm_domain *genpd, int st) -{ - return -ENOSYS; -} -static inline int pm_genpd_detach_cpuidle(struct generic_pm_domain *genpd) -{ - return -ENOSYS; -} static inline void pm_genpd_init(struct generic_pm_domain *genpd, struct dev_power_governor *gov, bool is_off) { -- cgit v0.10.2 From 03d5eec000973e80b1a1ccdef16ed8206621c3e4 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Tue, 8 Sep 2015 07:10:34 +0530 Subject: cpufreq: conservative: remove 'enable' field Conservative governor has its own 'enable' field to check if conservative governor is used for a CPU or not This can be checked by policy->governor with 'cpufreq_gov_conservative' and so this field can be dropped. Because its not guaranteed that dbs_info->cdbs.shared will a valid pointer for all CPUs (will be NULL for CPUs that don't use ondemand/conservative governors), we can't use it anymore. Lets get policy with cpufreq_cpu_get_raw() instead. Signed-off-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki diff --git a/drivers/cpufreq/cpufreq_conservative.c b/drivers/cpufreq/cpufreq_conservative.c index 84a1506..1fa1deb 100644 --- a/drivers/cpufreq/cpufreq_conservative.c +++ b/drivers/cpufreq/cpufreq_conservative.c @@ -23,6 +23,19 @@ static DEFINE_PER_CPU(struct cs_cpu_dbs_info_s, cs_cpu_dbs_info); +static int cs_cpufreq_governor_dbs(struct cpufreq_policy *policy, + unsigned int event); + +#ifndef CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE +static +#endif +struct cpufreq_governor cpufreq_gov_conservative = { + .name = "conservative", + .governor = cs_cpufreq_governor_dbs, + .max_transition_latency = TRANSITION_LATENCY_LIMIT, + .owner = THIS_MODULE, +}; + static inline unsigned int get_freq_target(struct cs_dbs_tuners *cs_tuners, struct cpufreq_policy *policy) { @@ -119,12 +132,14 @@ static int dbs_cpufreq_notifier(struct notifier_block *nb, unsigned long val, struct cpufreq_freqs *freq = data; struct cs_cpu_dbs_info_s *dbs_info = &per_cpu(cs_cpu_dbs_info, freq->cpu); - struct cpufreq_policy *policy; + struct cpufreq_policy *policy = cpufreq_cpu_get_raw(freq->cpu); - if (!dbs_info->enable) + if (!policy) return 0; - policy = dbs_info->cdbs.shared->policy; + /* policy isn't governed by conservative governor */ + if (policy->governor != &cpufreq_gov_conservative) + return 0; /* * we only care if our internally tracked freq moves outside the 'valid' @@ -367,16 +382,6 @@ static int cs_cpufreq_governor_dbs(struct cpufreq_policy *policy, return cpufreq_governor_dbs(policy, &cs_dbs_cdata, event); } -#ifndef CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE -static -#endif -struct cpufreq_governor cpufreq_gov_conservative = { - .name = "conservative", - .governor = cs_cpufreq_governor_dbs, - .max_transition_latency = TRANSITION_LATENCY_LIMIT, - .owner = THIS_MODULE, -}; - static int __init cpufreq_gov_dbs_init(void) { return cpufreq_register_governor(&cpufreq_gov_conservative); diff --git a/drivers/cpufreq/cpufreq_governor.c b/drivers/cpufreq/cpufreq_governor.c index 939197f..750626d 100644 --- a/drivers/cpufreq/cpufreq_governor.c +++ b/drivers/cpufreq/cpufreq_governor.c @@ -463,7 +463,6 @@ static int cpufreq_governor_start(struct cpufreq_policy *policy, cdata->get_cpu_dbs_info_s(cpu); cs_dbs_info->down_skip = 0; - cs_dbs_info->enable = 1; cs_dbs_info->requested_freq = policy->cur; } else { struct od_ops *od_ops = cdata->gov_ops; @@ -482,9 +481,7 @@ static int cpufreq_governor_start(struct cpufreq_policy *policy, static int cpufreq_governor_stop(struct cpufreq_policy *policy, struct dbs_data *dbs_data) { - struct common_dbs_data *cdata = dbs_data->cdata; - unsigned int cpu = policy->cpu; - struct cpu_dbs_info *cdbs = cdata->get_cpu_cdbs(cpu); + struct cpu_dbs_info *cdbs = dbs_data->cdata->get_cpu_cdbs(policy->cpu); struct cpu_common_dbs_info *shared = cdbs->shared; /* State should be equivalent to START */ @@ -493,13 +490,6 @@ static int cpufreq_governor_stop(struct cpufreq_policy *policy, gov_cancel_work(dbs_data, policy); - if (cdata->governor == GOV_CONSERVATIVE) { - struct cs_cpu_dbs_info_s *cs_dbs_info = - cdata->get_cpu_dbs_info_s(cpu); - - cs_dbs_info->enable = 0; - } - shared->policy = NULL; mutex_destroy(&shared->timer_mutex); return 0; diff --git a/drivers/cpufreq/cpufreq_governor.h b/drivers/cpufreq/cpufreq_governor.h index 50f1717..5621bb0 100644 --- a/drivers/cpufreq/cpufreq_governor.h +++ b/drivers/cpufreq/cpufreq_governor.h @@ -170,7 +170,6 @@ struct cs_cpu_dbs_info_s { struct cpu_dbs_info cdbs; unsigned int down_skip; unsigned int requested_freq; - unsigned int enable:1; }; /* Per policy Governors sysfs tunables */ -- cgit v0.10.2 From febf63cf616083e9a4ee05e2c28163b074890f36 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Tue, 8 Sep 2015 19:13:23 +0200 Subject: cpufreq: tegra20: remove superfluous CONFIG_PM ifdefs CONFIG_PM ifdefs are superfluous and can be removed. Signed-off-by: Bartlomiej Zolnierkiewicz Acked-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki diff --git a/drivers/cpufreq/tegra20-cpufreq.c b/drivers/cpufreq/tegra20-cpufreq.c index 8084c7f..2bd6284 100644 --- a/drivers/cpufreq/tegra20-cpufreq.c +++ b/drivers/cpufreq/tegra20-cpufreq.c @@ -175,9 +175,7 @@ static struct cpufreq_driver tegra_cpufreq_driver = { .exit = tegra_cpu_exit, .name = "tegra", .attr = cpufreq_generic_attr, -#ifdef CONFIG_PM .suspend = cpufreq_generic_suspend, -#endif }; static int __init tegra_cpufreq_init(void) -- cgit v0.10.2 From a35fc5a33b62a6c658b8ffe7544c966c1776d128 Mon Sep 17 00:00:00 2001 From: Bai Ping Date: Fri, 11 Sep 2015 23:41:05 +0800 Subject: cpufreq: imx: update the clock switch flow to support imx6ul For i.MX6UL, the clock switch flow is slightly different from other i.MX6 SOCs. It has a 'secondary_sel' clk that will be used when the CPU freq is higher than 396MHz. So the clock switch flow in 'set_target' callback need to update to support i.MX6UL in the common i.MX6 SOC cpufreq driver. Signed-off-by: Bai Ping Acked-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki diff --git a/drivers/cpufreq/imx6q-cpufreq.c b/drivers/cpufreq/imx6q-cpufreq.c index 380a90d..9b4a7bd 100644 --- a/drivers/cpufreq/imx6q-cpufreq.c +++ b/drivers/cpufreq/imx6q-cpufreq.c @@ -30,6 +30,10 @@ static struct clk *pll1_sw_clk; static struct clk *step_clk; static struct clk *pll2_pfd2_396m_clk; +/* clk used by i.MX6UL */ +static struct clk *pll2_bus_clk; +static struct clk *secondary_sel_clk; + static struct device *cpu_dev; static bool free_opp; static struct cpufreq_frequency_table *freq_table; @@ -91,16 +95,36 @@ static int imx6q_set_target(struct cpufreq_policy *policy, unsigned int index) * The setpoints are selected per PLL/PDF frequencies, so we need to * reprogram PLL for frequency scaling. The procedure of reprogramming * PLL1 is as below. - * + * For i.MX6UL, it has a secondary clk mux, the cpu frequency change + * flow is slightly different from other i.MX6 OSC. + * The cpu frequeny change flow for i.MX6(except i.MX6UL) is as below: * - Enable pll2_pfd2_396m_clk and reparent pll1_sw_clk to it * - Reprogram pll1_sys_clk and reparent pll1_sw_clk back to it * - Disable pll2_pfd2_396m_clk */ - clk_set_parent(step_clk, pll2_pfd2_396m_clk); - clk_set_parent(pll1_sw_clk, step_clk); - if (freq_hz > clk_get_rate(pll2_pfd2_396m_clk)) { - clk_set_rate(pll1_sys_clk, new_freq * 1000); + if (of_machine_is_compatible("fsl,imx6ul")) { + /* + * When changing pll1_sw_clk's parent to pll1_sys_clk, + * CPU may run at higher than 528MHz, this will lead to + * the system unstable if the voltage is lower than the + * voltage of 528MHz, so lower the CPU frequency to one + * half before changing CPU frequency. + */ + clk_set_rate(arm_clk, (old_freq >> 1) * 1000); clk_set_parent(pll1_sw_clk, pll1_sys_clk); + if (freq_hz > clk_get_rate(pll2_pfd2_396m_clk)) + clk_set_parent(secondary_sel_clk, pll2_bus_clk); + else + clk_set_parent(secondary_sel_clk, pll2_pfd2_396m_clk); + clk_set_parent(step_clk, secondary_sel_clk); + clk_set_parent(pll1_sw_clk, step_clk); + } else { + clk_set_parent(step_clk, pll2_pfd2_396m_clk); + clk_set_parent(pll1_sw_clk, step_clk); + if (freq_hz > clk_get_rate(pll2_pfd2_396m_clk)) { + clk_set_rate(pll1_sys_clk, new_freq * 1000); + clk_set_parent(pll1_sw_clk, pll1_sys_clk); + } } /* Ensure the arm clock divider is what we expect */ @@ -186,6 +210,16 @@ static int imx6q_cpufreq_probe(struct platform_device *pdev) goto put_clk; } + if (of_machine_is_compatible("fsl,imx6ul")) { + pll2_bus_clk = clk_get(cpu_dev, "pll2_bus"); + secondary_sel_clk = clk_get(cpu_dev, "secondary_sel"); + if (IS_ERR(pll2_bus_clk) || IS_ERR(secondary_sel_clk)) { + dev_err(cpu_dev, "failed to get clocks specific to imx6ul\n"); + ret = -ENOENT; + goto put_clk; + } + } + arm_reg = regulator_get(cpu_dev, "arm"); pu_reg = regulator_get_optional(cpu_dev, "pu"); soc_reg = regulator_get(cpu_dev, "soc"); @@ -331,6 +365,10 @@ put_clk: clk_put(step_clk); if (!IS_ERR(pll2_pfd2_396m_clk)) clk_put(pll2_pfd2_396m_clk); + if (!IS_ERR(pll2_bus_clk)) + clk_put(pll2_bus_clk); + if (!IS_ERR(secondary_sel_clk)) + clk_put(secondary_sel_clk); of_node_put(np); return ret; } @@ -350,6 +388,8 @@ static int imx6q_cpufreq_remove(struct platform_device *pdev) clk_put(pll1_sw_clk); clk_put(step_clk); clk_put(pll2_pfd2_396m_clk); + clk_put(pll2_bus_clk); + clk_put(secondary_sel_clk); return 0; } -- cgit v0.10.2 From d43b1b6f8e5b3219d4d02ea7fadcf67fecf78b1a Mon Sep 17 00:00:00 2001 From: Shilpasri G Bhat Date: Mon, 14 Sep 2015 14:01:47 +0530 Subject: cpufreq : powernv: Report Pmax throttling if capped below nominal frequency Log a 'critical' message if the max frequency is reduced below nominal frequency. We already log 'info' message if the max frequency is capped below turbo frequency. CPU should guarantee atleast nominal frequency, but not turbo frequency in all system configurations and environments. So report the pmax throttling with severity when Pmax is dipped below nominal frequency. Signed-off-by: Shilpasri G Bhat Acked-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki diff --git a/drivers/cpufreq/powernv-cpufreq.c b/drivers/cpufreq/powernv-cpufreq.c index 64994e1..cb50138 100644 --- a/drivers/cpufreq/powernv-cpufreq.c +++ b/drivers/cpufreq/powernv-cpufreq.c @@ -327,8 +327,14 @@ static void powernv_cpufreq_throttle_check(void *data) if (chips[i].throttled) goto next; chips[i].throttled = true; - pr_info("CPU %d on Chip %u has Pmax reduced to %d\n", cpu, - chips[i].id, pmsr_pmax); + if (pmsr_pmax < powernv_pstate_info.nominal) + pr_crit("CPU %d on Chip %u has Pmax reduced below nominal frequency (%d < %d)\n", + cpu, chips[i].id, pmsr_pmax, + powernv_pstate_info.nominal); + else + pr_info("CPU %d on Chip %u has Pmax reduced below turbo frequency (%d < %d)\n", + cpu, chips[i].id, pmsr_pmax, + powernv_pstate_info.max); } else if (chips[i].throttled) { chips[i].throttled = false; pr_info("CPU %d on Chip %u has Pmax restored to %d\n", cpu, -- cgit v0.10.2 From 1462206cbccebf2b61e05cf786d458afe49d5509 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Sun, 27 Sep 2015 15:20:30 +0200 Subject: PM / domains: Drop unused label Drop the "out" label in __pm_genpd_poweron() that's not used any more. Signed-off-by: Rafael J. Wysocki diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index a544887..cf4950d 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -227,7 +227,6 @@ static int __pm_genpd_poweron(struct generic_pm_domain *genpd) if (ret) goto err; - out: genpd->status = GPD_STATE_ACTIVE; return 0; -- cgit v0.10.2 From e647b532275bb357e87272e052fccf5fcdb36a17 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Mon, 28 Sep 2015 15:49:12 +0100 Subject: ACPI: Add early device probing infrastructure IRQ controllers and timers are the two types of device the kernel requires before being able to use the device driver model. ACPI so far lacks a proper probing infrastructure similar to the one we have with DT, where we're able to declare IRQ chips and clocksources inside the driver code, and let the core code pick it up and call us back on a match. This leads to all kind of really ugly hacks all over the arm64 code and even in the ACPI layer. In order to allow some basic probing based on the ACPI tables, introduce "struct acpi_probe_entry" which contains just enough data and callbacks to match a table, an optional subtable, and call a probe function. A driver can, at build time, register itself and expect being called if the right entry exists in the ACPI table. A acpi_probe_device_table() is provided, taking an identifier for a set of acpi_prove_entries, and iterating over the registered entries. Signed-off-by: Marc Zyngier Reviewed-by: Lorenzo Pieralisi Reviewed-by: Hanjun Guo Acked-by: Thomas Gleixner Tested-by: Hanjun Guo Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index d1ce377..1d6b55a 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -1913,3 +1913,42 @@ int __init acpi_scan_init(void) mutex_unlock(&acpi_scan_lock); return result; } + +static struct acpi_probe_entry *ape; +static int acpi_probe_count; +static DEFINE_SPINLOCK(acpi_probe_lock); + +static int __init acpi_match_madt(struct acpi_subtable_header *header, + const unsigned long end) +{ + if (!ape->subtable_valid || ape->subtable_valid(header, ape)) + if (!ape->probe_subtbl(header, end)) + acpi_probe_count++; + + return 0; +} + +int __init __acpi_probe_device_table(struct acpi_probe_entry *ap_head, int nr) +{ + int count = 0; + + if (acpi_disabled) + return 0; + + spin_lock(&acpi_probe_lock); + for (ape = ap_head; nr; ape++, nr--) { + if (ACPI_COMPARE_NAME(ACPI_SIG_MADT, ape->id)) { + acpi_probe_count = 0; + acpi_table_parse_madt(ape->type, acpi_match_madt, 0); + count += acpi_probe_count; + } else { + int res; + res = acpi_table_parse(ape->id, ape->probe_table); + if (!res) + count++; + } + } + spin_unlock(&acpi_probe_lock); + + return count; +} diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h index 1781e54..efd7ed1 100644 --- a/include/asm-generic/vmlinux.lds.h +++ b/include/asm-generic/vmlinux.lds.h @@ -181,6 +181,16 @@ #define CPUIDLE_METHOD_OF_TABLES() OF_TABLE(CONFIG_CPU_IDLE, cpuidle_method) #define EARLYCON_OF_TABLES() OF_TABLE(CONFIG_SERIAL_EARLYCON, earlycon) +#ifdef CONFIG_ACPI +#define ACPI_PROBE_TABLE(name) \ + . = ALIGN(8); \ + VMLINUX_SYMBOL(__##name##_acpi_probe_table) = .; \ + *(__##name##_acpi_probe_table) \ + VMLINUX_SYMBOL(__##name##_acpi_probe_table_end) = .; +#else +#define ACPI_PROBE_TABLE(name) +#endif + #define KERNEL_DTB() \ STRUCT_ALIGN(); \ VMLINUX_SYMBOL(__dtb_start) = .; \ diff --git a/include/linux/acpi.h b/include/linux/acpi.h index 865d948..815f5f6 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -773,6 +773,61 @@ int acpi_dev_prop_read(struct acpi_device *adev, const char *propname, struct fwnode_handle *acpi_get_next_subnode(struct device *dev, struct fwnode_handle *subnode); + +struct acpi_probe_entry; +typedef bool (*acpi_probe_entry_validate_subtbl)(struct acpi_subtable_header *, + struct acpi_probe_entry *); + +#define ACPI_TABLE_ID_LEN 5 + +/** + * struct acpi_probe_entry - boot-time probing entry + * @id: ACPI table name + * @type: Optional subtable type to match + * (if @id contains subtables) + * @subtable_valid: Optional callback to check the validity of + * the subtable + * @probe_table: Callback to the driver being probed when table + * match is successful + * @probe_subtbl: Callback to the driver being probed when table and + * subtable match (and optional callback is successful) + * @driver_data: Sideband data provided back to the driver + */ +struct acpi_probe_entry { + __u8 id[ACPI_TABLE_ID_LEN]; + __u8 type; + acpi_probe_entry_validate_subtbl subtable_valid; + union { + acpi_tbl_table_handler probe_table; + acpi_tbl_entry_handler probe_subtbl; + }; + kernel_ulong_t driver_data; +}; + +#define ACPI_DECLARE_PROBE_ENTRY(table, name, table_id, subtable, valid, data, fn) \ + static const struct acpi_probe_entry __acpi_probe_##name \ + __used __section(__##table##_acpi_probe_table) \ + = { \ + .id = table_id, \ + .type = subtable, \ + .subtable_valid = valid, \ + .probe_table = (acpi_tbl_table_handler)fn, \ + .driver_data = data, \ + } + +#define ACPI_PROBE_TABLE(name) __##name##_acpi_probe_table +#define ACPI_PROBE_TABLE_END(name) __##name##_acpi_probe_table_end + +int __acpi_probe_device_table(struct acpi_probe_entry *start, int nr); + +#define acpi_probe_device_table(t) \ + ({ \ + extern struct acpi_probe_entry ACPI_PROBE_TABLE(t), \ + ACPI_PROBE_TABLE_END(t); \ + __acpi_probe_device_table(&ACPI_PROBE_TABLE(t), \ + (&ACPI_PROBE_TABLE_END(t) - \ + &ACPI_PROBE_TABLE(t))); \ + }) #else static inline int acpi_dev_get_property(struct acpi_device *adev, const char *name, acpi_object_type type, @@ -831,6 +886,17 @@ static inline struct fwnode_handle *acpi_get_next_subnode(struct device *dev, { return NULL; } + +#define ACPI_DECLARE_PROBE_ENTRY(table, name, table_id, subtable, validate, data, fn) \ + static const void * __acpi_table_##name[] \ + __attribute__((unused)) \ + = { (void *) table_id, \ + (void *) subtable, \ + (void *) valid, \ + (void *) fn, \ + (void *) data } + +#define acpi_probe_device_table(t) ({ int __r = 0; __r;}) #endif #endif /*_LINUX_ACPI_H*/ -- cgit v0.10.2 From 46e589a391809627144e6bee93d71d73fe915db2 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Mon, 28 Sep 2015 15:49:13 +0100 Subject: irqchip / ACPI: Add probing infrastructure for ACPI-based irqchips DT enjoys a rather nice probing infrastructure for irqchips, while ACPI is so far stuck into a very distant past. This patch introduces a declarative API, allowing irqchips to be self-contained and be called when a particular entry is matched in the MADT table. Signed-off-by: Marc Zyngier Acked-by: Catalin Marinas Reviewed-by: Hanjun Guo Acked-by: Thomas Gleixner Tested-by: Hanjun Guo Signed-off-by: Rafael J. Wysocki diff --git a/arch/arm64/include/asm/irq.h b/arch/arm64/include/asm/irq.h index bbb251b..1a1037a 100644 --- a/arch/arm64/include/asm/irq.h +++ b/arch/arm64/include/asm/irq.h @@ -10,15 +10,4 @@ struct pt_regs; extern void migrate_irqs(void); extern void set_handle_irq(void (*handle_irq)(struct pt_regs *)); -static inline void acpi_irq_init(void) -{ - /* - * Hardcode ACPI IRQ chip initialization to GICv2 for now. - * Proper irqchip infrastructure will be implemented along with - * incoming GICv2m|GICv3|ITS bits. - */ - acpi_gic_init(); -} -#define acpi_irq_init acpi_irq_init - #endif diff --git a/drivers/irqchip/irqchip.c b/drivers/irqchip/irqchip.c index afd1af3..1ee773e 100644 --- a/drivers/irqchip/irqchip.c +++ b/drivers/irqchip/irqchip.c @@ -8,7 +8,7 @@ * warranty of any kind, whether express or implied. */ -#include +#include #include #include #include @@ -27,6 +27,8 @@ extern struct of_device_id __irqchip_of_table[]; void __init irqchip_init(void) { of_irq_init(__irqchip_of_table); - - acpi_irq_init(); +#if defined(CONFIG_ARM64) && defined(CONFIG_ACPI) + acpi_gic_init(); /* Temporary hack */ +#endif + acpi_probe_device_table(irqchip); } diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h index efd7ed1..cd836cd 100644 --- a/include/asm-generic/vmlinux.lds.h +++ b/include/asm-generic/vmlinux.lds.h @@ -524,6 +524,7 @@ CPUIDLE_METHOD_OF_TABLES() \ KERNEL_DTB() \ IRQCHIP_OF_MATCH_TABLE() \ + ACPI_PROBE_TABLE(irqchip) \ EARLYCON_TABLE() \ EARLYCON_OF_TABLES() diff --git a/include/linux/acpi_irq.h b/include/linux/acpi_irq.h deleted file mode 100644 index f10c872..0000000 --- a/include/linux/acpi_irq.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef _LINUX_ACPI_IRQ_H -#define _LINUX_ACPI_IRQ_H - -#include - -#ifndef acpi_irq_init -static inline void acpi_irq_init(void) { } -#endif - -#endif /* _LINUX_ACPI_IRQ_H */ diff --git a/include/linux/irqchip.h b/include/linux/irqchip.h index 6388873..89c34b2 100644 --- a/include/linux/irqchip.h +++ b/include/linux/irqchip.h @@ -11,6 +11,7 @@ #ifndef _LINUX_IRQCHIP_H #define _LINUX_IRQCHIP_H +#include #include /* @@ -25,6 +26,22 @@ */ #define IRQCHIP_DECLARE(name, compat, fn) OF_DECLARE_2(irqchip, name, compat, fn) +/* + * This macro must be used by the different irqchip drivers to declare + * the association between their version and their initialization function. + * + * @name: name that must be unique accross all IRQCHIP_ACPI_DECLARE of the + * same file. + * @subtable: Subtable to be identified in MADT + * @validate: Function to be called on that subtable to check its validity. + * Can be NULL. + * @data: data to be checked by the validate function. + * @fn: initialization function + */ +#define IRQCHIP_ACPI_DECLARE(name, subtable, validate, data, fn) \ + ACPI_DECLARE_PROBE_ENTRY(irqchip, name, ACPI_SIG_MADT, \ + subtable, validate, data, fn) + #ifdef CONFIG_IRQCHIP void irqchip_init(void); #else -- cgit v0.10.2 From f26527b1428f379fbd7edf779854c3b41bc0b3e5 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Mon, 28 Sep 2015 15:49:14 +0100 Subject: irqchip / GIC: Convert the GIC driver to ACPI probing Now that we have a basic infrastructure to register irqchips and call them on discovery of a matching entry in MADT, convert the GIC driver to this new probing method. It ends up being a code deletion party, which is a rather good thing. Signed-off-by: Marc Zyngier Acked-by: Catalin Marinas Reviewed-by: Hanjun Guo Acked-by: Thomas Gleixner Tested-by: Hanjun Guo Signed-off-by: Rafael J. Wysocki diff --git a/arch/arm64/include/asm/acpi.h b/arch/arm64/include/asm/acpi.h index 208cec0..6894205 100644 --- a/arch/arm64/include/asm/acpi.h +++ b/arch/arm64/include/asm/acpi.h @@ -12,7 +12,6 @@ #ifndef _ASM_ACPI_H #define _ASM_ACPI_H -#include #include #include diff --git a/arch/arm64/include/asm/irq.h b/arch/arm64/include/asm/irq.h index 1a1037a..94c5367 100644 --- a/arch/arm64/include/asm/irq.h +++ b/arch/arm64/include/asm/irq.h @@ -1,8 +1,6 @@ #ifndef __ASM_IRQ_H #define __ASM_IRQ_H -#include - #include struct pt_regs; diff --git a/arch/arm64/kernel/acpi.c b/arch/arm64/kernel/acpi.c index 19de753..d6463bb 100644 --- a/arch/arm64/kernel/acpi.c +++ b/arch/arm64/kernel/acpi.c @@ -205,28 +205,3 @@ void __init acpi_boot_table_init(void) disable_acpi(); } } - -void __init acpi_gic_init(void) -{ - struct acpi_table_header *table; - acpi_status status; - acpi_size tbl_size; - int err; - - if (acpi_disabled) - return; - - status = acpi_get_table_with_size(ACPI_SIG_MADT, 0, &table, &tbl_size); - if (ACPI_FAILURE(status)) { - const char *msg = acpi_format_exception(status); - - pr_err("Failed to get MADT table, %s\n", msg); - return; - } - - err = gic_v2_acpi_init(table); - if (err) - pr_err("Failed to initialize GIC IRQ controller"); - - early_acpi_os_unmap_memory((char *)table, tbl_size); -} diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c index 982c09c..d4add30 100644 --- a/drivers/irqchip/irq-gic.c +++ b/drivers/irqchip/irq-gic.c @@ -41,7 +41,6 @@ #include #include #include -#include #include #include @@ -1195,7 +1194,7 @@ IRQCHIP_DECLARE(msm_qgic2, "qcom,msm-qgic2", gic_of_init); #endif #ifdef CONFIG_ACPI -static phys_addr_t dist_phy_base, cpu_phy_base __initdata; +static phys_addr_t cpu_phy_base __initdata; static int __init gic_acpi_parse_madt_cpu(struct acpi_subtable_header *header, @@ -1223,60 +1222,56 @@ gic_acpi_parse_madt_cpu(struct acpi_subtable_header *header, return 0; } -static int __init -gic_acpi_parse_madt_distributor(struct acpi_subtable_header *header, - const unsigned long end) +/* The things you have to do to just *count* something... */ +static int __init acpi_dummy_func(struct acpi_subtable_header *header, + const unsigned long end) { - struct acpi_madt_generic_distributor *dist; + return 0; +} - dist = (struct acpi_madt_generic_distributor *)header; +static bool __init acpi_gic_redist_is_present(void) +{ + return acpi_table_parse_madt(ACPI_MADT_TYPE_GENERIC_REDISTRIBUTOR, + acpi_dummy_func, 0) > 0; +} - if (BAD_MADT_ENTRY(dist, end)) - return -EINVAL; +static bool __init gic_validate_dist(struct acpi_subtable_header *header, + struct acpi_probe_entry *ape) +{ + struct acpi_madt_generic_distributor *dist; + dist = (struct acpi_madt_generic_distributor *)header; - dist_phy_base = dist->base_address; - return 0; + return (dist->version == ape->driver_data && + (dist->version != ACPI_MADT_GIC_VERSION_NONE || + !acpi_gic_redist_is_present())); } -int __init -gic_v2_acpi_init(struct acpi_table_header *table) +#define ACPI_GICV2_DIST_MEM_SIZE (SZ_4K) +#define ACPI_GIC_CPU_IF_MEM_SIZE (SZ_8K) + +static int __init gic_v2_acpi_init(struct acpi_subtable_header *header, + const unsigned long end) { + struct acpi_madt_generic_distributor *dist; void __iomem *cpu_base, *dist_base; int count; /* Collect CPU base addresses */ - count = acpi_parse_entries(ACPI_SIG_MADT, - sizeof(struct acpi_table_madt), - gic_acpi_parse_madt_cpu, table, - ACPI_MADT_TYPE_GENERIC_INTERRUPT, 0); + count = acpi_table_parse_madt(ACPI_MADT_TYPE_GENERIC_INTERRUPT, + gic_acpi_parse_madt_cpu, 0); if (count <= 0) { pr_err("No valid GICC entries exist\n"); return -EINVAL; } - /* - * Find distributor base address. We expect one distributor entry since - * ACPI 5.1 spec neither support multi-GIC instances nor GIC cascade. - */ - count = acpi_parse_entries(ACPI_SIG_MADT, - sizeof(struct acpi_table_madt), - gic_acpi_parse_madt_distributor, table, - ACPI_MADT_TYPE_GENERIC_DISTRIBUTOR, 0); - if (count <= 0) { - pr_err("No valid GICD entries exist\n"); - return -EINVAL; - } else if (count > 1) { - pr_err("More than one GICD entry detected\n"); - return -EINVAL; - } - cpu_base = ioremap(cpu_phy_base, ACPI_GIC_CPU_IF_MEM_SIZE); if (!cpu_base) { pr_err("Unable to map GICC registers\n"); return -ENOMEM; } - dist_base = ioremap(dist_phy_base, ACPI_GICV2_DIST_MEM_SIZE); + dist = (struct acpi_madt_generic_distributor *)header; + dist_base = ioremap(dist->base_address, ACPI_GICV2_DIST_MEM_SIZE); if (!dist_base) { pr_err("Unable to map GICD registers\n"); iounmap(cpu_base); @@ -1302,4 +1297,10 @@ gic_v2_acpi_init(struct acpi_table_header *table) acpi_irq_model = ACPI_IRQ_MODEL_GIC; return 0; } +IRQCHIP_ACPI_DECLARE(gic_v2, ACPI_MADT_TYPE_GENERIC_DISTRIBUTOR, + gic_validate_dist, ACPI_MADT_GIC_VERSION_V2, + gic_v2_acpi_init); +IRQCHIP_ACPI_DECLARE(gic_v2_maybe, ACPI_MADT_TYPE_GENERIC_DISTRIBUTOR, + gic_validate_dist, ACPI_MADT_GIC_VERSION_NONE, + gic_v2_acpi_init); #endif diff --git a/drivers/irqchip/irqchip.c b/drivers/irqchip/irqchip.c index 1ee773e..2b35e68 100644 --- a/drivers/irqchip/irqchip.c +++ b/drivers/irqchip/irqchip.c @@ -27,8 +27,5 @@ extern struct of_device_id __irqchip_of_table[]; void __init irqchip_init(void) { of_irq_init(__irqchip_of_table); -#if defined(CONFIG_ARM64) && defined(CONFIG_ACPI) - acpi_gic_init(); /* Temporary hack */ -#endif acpi_probe_device_table(irqchip); } diff --git a/include/linux/irqchip/arm-gic-acpi.h b/include/linux/irqchip/arm-gic-acpi.h deleted file mode 100644 index de3419e..0000000 --- a/include/linux/irqchip/arm-gic-acpi.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (C) 2014, Linaro Ltd. - * Author: Tomasz Nowicki - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#ifndef ARM_GIC_ACPI_H_ -#define ARM_GIC_ACPI_H_ - -#ifdef CONFIG_ACPI - -/* - * Hard code here, we can not get memory size from MADT (but FDT does), - * Actually no need to do that, because this size can be inferred - * from GIC spec. - */ -#define ACPI_GICV2_DIST_MEM_SIZE (SZ_4K) -#define ACPI_GIC_CPU_IF_MEM_SIZE (SZ_8K) - -struct acpi_table_header; - -int gic_v2_acpi_init(struct acpi_table_header *table); -void acpi_gic_init(void); -#else -static inline void acpi_gic_init(void) { } -#endif - -#endif /* ARM_GIC_ACPI_H_ */ -- cgit v0.10.2 From c625f76a9910b9d51df5d6ca40a8da0684326996 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Mon, 28 Sep 2015 15:49:15 +0100 Subject: clocksource / ACPI: Add probing infrastructure for ACPI-based clocksources DT enjoys a rather nice probing infrastructure for clocksources, while ACPI is so far stuck into a very distant past. This patch introduces a declarative API, allowing clocksources to be self-contained and be called when parsing the GTDT table. Signed-off-by: Marc Zyngier Acked-by: Thomas Gleixner Tested-by: Hanjun Guo Signed-off-by: Rafael J. Wysocki diff --git a/drivers/clocksource/clksrc-of.c b/drivers/clocksource/clksrc-of.c index 0093a8e..a2105bd 100644 --- a/drivers/clocksource/clksrc-of.c +++ b/drivers/clocksource/clksrc-of.c @@ -14,6 +14,7 @@ * along with this program. If not, see . */ +#include #include #include #include @@ -38,6 +39,9 @@ void __init clocksource_of_init(void) init_func(np); clocksources++; } + + clocksources += acpi_probe_device_table(clksrc); + if (!clocksources) pr_crit("%s: no matching clocksources found\n", __func__); } diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h index cd836cd..c4bd0e2 100644 --- a/include/asm-generic/vmlinux.lds.h +++ b/include/asm-generic/vmlinux.lds.h @@ -525,6 +525,7 @@ KERNEL_DTB() \ IRQCHIP_OF_MATCH_TABLE() \ ACPI_PROBE_TABLE(irqchip) \ + ACPI_PROBE_TABLE(clksrc) \ EARLYCON_TABLE() \ EARLYCON_OF_TABLES() diff --git a/include/linux/clocksource.h b/include/linux/clocksource.h index 278dd27..8cde048 100644 --- a/include/linux/clocksource.h +++ b/include/linux/clocksource.h @@ -258,4 +258,7 @@ void acpi_generic_timer_init(void); static inline void acpi_generic_timer_init(void) { } #endif +#define CLOCKSOURCE_ACPI_DECLARE(name, table_id, fn) \ + ACPI_DECLARE_PROBE_ENTRY(clksrc, name, table_id, 0, NULL, 0, fn) + #endif /* _LINUX_CLOCKSOURCE_H */ -- cgit v0.10.2 From aad83b15aa21f2d9e46b978b27bc63989e61202d Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Mon, 28 Sep 2015 15:49:16 +0100 Subject: clocksource: Add new CLKSRC_{PROBE,ACPI} config symbols The clocksource probing infrastructure currently depends on CONFIG_CLKSRC_OF, which depends on CONFIG_OF. In order to make this infrastructure selectable even if CONFIG_OF is not selected, introduce a new CONFIG_CLKSRC_PROBE (which allow the infrastructure to be compiled in), and CONFIG_CLKSRC_ACPI (which is the pendent of CONFIG_CLKSRC_OF for ACPI). Signed-off-by: Marc Zyngier Acked-by: Thomas Gleixner Tested-by: Hanjun Guo Signed-off-by: Rafael J. Wysocki diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig index a7726db..fefedf1 100644 --- a/drivers/clocksource/Kconfig +++ b/drivers/clocksource/Kconfig @@ -2,6 +2,14 @@ menu "Clock Source drivers" config CLKSRC_OF bool + select CLKSRC_PROBE + +config CLKSRC_ACPI + bool + select CLKSRC_PROBE + +config CLKSRC_PROBE + bool config CLKSRC_I8253 bool diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile index 5c00863..b47be92 100644 --- a/drivers/clocksource/Makefile +++ b/drivers/clocksource/Makefile @@ -1,4 +1,4 @@ -obj-$(CONFIG_CLKSRC_OF) += clksrc-of.o +obj-$(CONFIG_CLKSRC_PROBE) += clksrc-of.o obj-$(CONFIG_ATMEL_PIT) += timer-atmel-pit.o obj-$(CONFIG_ATMEL_ST) += timer-atmel-st.o obj-$(CONFIG_ATMEL_TCB_CLKSRC) += tcb_clksrc.o diff --git a/include/linux/clocksource.h b/include/linux/clocksource.h index 8cde048..6eab6ab 100644 --- a/include/linux/clocksource.h +++ b/include/linux/clocksource.h @@ -246,7 +246,7 @@ extern int clocksource_i8253_init(void); #define CLOCKSOURCE_OF_DECLARE(name, compat, fn) \ OF_DECLARE_1(clksrc, name, compat, fn) -#ifdef CONFIG_CLKSRC_OF +#ifdef CONFIG_CLKSRC_PROBE extern void clocksource_of_init(void); #else static inline void clocksource_of_init(void) {} -- cgit v0.10.2 From ae281cbd2689200329afe2474b2f39f3f6eb54b9 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Mon, 28 Sep 2015 15:49:17 +0100 Subject: clocksource / arm_arch_timer: Convert to ACPI probing It is now absolutely trivial to convert the arch timer driver to use ACPI probing, just like its DT counterpart. Let's enjoy another crapectomy. Signed-off-by: Marc Zyngier Acked-by: Catalin Marinas Acked-by: Thomas Gleixner Tested-by: Hanjun Guo Signed-off-by: Rafael J. Wysocki diff --git a/arch/arm64/kernel/time.c b/arch/arm64/kernel/time.c index 149151f..819a105 100644 --- a/arch/arm64/kernel/time.c +++ b/arch/arm64/kernel/time.c @@ -71,12 +71,6 @@ void __init time_init(void) tick_setup_hrtimer_broadcast(); - /* - * Since ACPI or FDT will only one be available in the system, - * we can use acpi_generic_timer_init() here safely - */ - acpi_generic_timer_init(); - arch_timer_rate = arch_timer_get_rate(); if (!arch_timer_rate) panic("Unable to initialise architected timer.\n"); diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig index fefedf1..1a9c5dc 100644 --- a/drivers/clocksource/Kconfig +++ b/drivers/clocksource/Kconfig @@ -131,6 +131,7 @@ config CLKSRC_STM32 config ARM_ARCH_TIMER bool select CLKSRC_OF if OF + select CLKSRC_ACPI if ACPI config ARM_ARCH_TIMER_EVTSTREAM bool "Support for ARM architected timer event stream generation" diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c index d6e3e49..c64d543 100644 --- a/drivers/clocksource/arm_arch_timer.c +++ b/drivers/clocksource/arm_arch_timer.c @@ -864,13 +864,5 @@ static int __init arch_timer_acpi_init(struct acpi_table_header *table) arch_timer_init(); return 0; } - -/* Initialize all the generic timers presented in GTDT */ -void __init acpi_generic_timer_init(void) -{ - if (acpi_disabled) - return; - - acpi_table_parse(ACPI_SIG_GTDT, arch_timer_acpi_init); -} +CLOCKSOURCE_ACPI_DECLARE(arch_timer, ACPI_SIG_GTDT, arch_timer_acpi_init); #endif diff --git a/include/linux/clocksource.h b/include/linux/clocksource.h index 6eab6ab..116645f 100644 --- a/include/linux/clocksource.h +++ b/include/linux/clocksource.h @@ -252,12 +252,6 @@ extern void clocksource_of_init(void); static inline void clocksource_of_init(void) {} #endif -#ifdef CONFIG_ACPI -void acpi_generic_timer_init(void); -#else -static inline void acpi_generic_timer_init(void) { } -#endif - #define CLOCKSOURCE_ACPI_DECLARE(name, table_id, fn) \ ACPI_DECLARE_PROBE_ENTRY(clksrc, name, table_id, 0, NULL, 0, fn) -- cgit v0.10.2 From 3722ed2380ad6e89eaf81fcf93f06d605e740435 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Mon, 28 Sep 2015 15:49:18 +0100 Subject: clocksource: cosmetic: Drop OF 'dependency' from symbols Seeing the 'of' characters in a symbol that is being called from ACPI seems to freak out people. So let's do a bit of pointless renaming so that these folks do feel at home. Signed-off-by: Marc Zyngier Acked-by: Catalin Marinas Reviewed-by: Hanjun Guo Acked-by: Thomas Gleixner Tested-by: Hanjun Guo Signed-off-by: Rafael J. Wysocki diff --git a/arch/arm/kernel/time.c b/arch/arm/kernel/time.c index a66e37e..97b22fa 100644 --- a/arch/arm/kernel/time.c +++ b/arch/arm/kernel/time.c @@ -120,6 +120,6 @@ void __init time_init(void) #ifdef CONFIG_COMMON_CLK of_clk_init(NULL); #endif - clocksource_of_init(); + clocksource_probe(); } } diff --git a/arch/arm/mach-omap2/timer.c b/arch/arm/mach-omap2/timer.c index a556551..bef4183 100644 --- a/arch/arm/mach-omap2/timer.c +++ b/arch/arm/mach-omap2/timer.c @@ -647,7 +647,7 @@ static OMAP_SYS_32K_TIMER_INIT(4, 1, "timer_32k_ck", "ti,timer-alwon", void __init omap4_local_timer_init(void) { omap4_sync32k_timer_init(); - clocksource_of_init(); + clocksource_probe(); } #else void __init omap4_local_timer_init(void) @@ -663,7 +663,7 @@ void __init omap5_realtime_timer_init(void) omap4_sync32k_timer_init(); realtime_counter_init(); - clocksource_of_init(); + clocksource_probe(); } #endif /* CONFIG_SOC_OMAP5 || CONFIG_SOC_DRA7XX */ diff --git a/arch/arm/mach-rockchip/rockchip.c b/arch/arm/mach-rockchip/rockchip.c index b6cf3b4..251c7b9 100644 --- a/arch/arm/mach-rockchip/rockchip.c +++ b/arch/arm/mach-rockchip/rockchip.c @@ -67,7 +67,7 @@ static void __init rockchip_timer_init(void) } of_clk_init(NULL); - clocksource_of_init(); + clocksource_probe(); } static void __init rockchip_dt_init(void) diff --git a/arch/arm/mach-shmobile/setup-r8a7779.c b/arch/arm/mach-shmobile/setup-r8a7779.c index 6bfa640..1e572a9 100644 --- a/arch/arm/mach-shmobile/setup-r8a7779.c +++ b/arch/arm/mach-shmobile/setup-r8a7779.c @@ -97,7 +97,7 @@ static u32 __init r8a7779_read_mode_pins(void) static void __init r8a7779_init_time(void) { r8a7779_clocks_init(r8a7779_read_mode_pins()); - clocksource_of_init(); + clocksource_probe(); } static const char *const r8a7779_compat_dt[] __initconst = { diff --git a/arch/arm/mach-shmobile/setup-rcar-gen2.c b/arch/arm/mach-shmobile/setup-rcar-gen2.c index aa33392..9eccde3 100644 --- a/arch/arm/mach-shmobile/setup-rcar-gen2.c +++ b/arch/arm/mach-shmobile/setup-rcar-gen2.c @@ -128,7 +128,7 @@ void __init rcar_gen2_timer_init(void) #endif /* CONFIG_ARM_ARCH_TIMER */ rcar_gen2_clocks_init(mode); - clocksource_of_init(); + clocksource_probe(); } struct memory_reserve_config { diff --git a/arch/arm/mach-spear/spear13xx.c b/arch/arm/mach-spear/spear13xx.c index b7afce6..ca2f6a8 100644 --- a/arch/arm/mach-spear/spear13xx.c +++ b/arch/arm/mach-spear/spear13xx.c @@ -124,5 +124,5 @@ void __init spear13xx_timer_init(void) clk_put(pclk); spear_setup_of_timer(); - clocksource_of_init(); + clocksource_probe(); } diff --git a/arch/arm/mach-sunxi/sunxi.c b/arch/arm/mach-sunxi/sunxi.c index 65bab28..223c9e9 100644 --- a/arch/arm/mach-sunxi/sunxi.c +++ b/arch/arm/mach-sunxi/sunxi.c @@ -46,7 +46,7 @@ static void __init sun6i_timer_init(void) of_clk_init(NULL); if (IS_ENABLED(CONFIG_RESET_CONTROLLER)) sun6i_reset_init(); - clocksource_of_init(); + clocksource_probe(); } DT_MACHINE_START(SUN6I_DT, "Allwinner sun6i (A31) Family") diff --git a/arch/arm/mach-u300/core.c b/arch/arm/mach-u300/core.c index 35670b1..546338b 100644 --- a/arch/arm/mach-u300/core.c +++ b/arch/arm/mach-u300/core.c @@ -408,7 +408,7 @@ static const char * u300_board_compat[] = { DT_MACHINE_START(U300_DT, "U300 S335/B335 (Device Tree)") .map_io = u300_map_io, .init_irq = u300_init_irq_dt, - .init_time = clocksource_of_init, + .init_time = clocksource_probe, .init_machine = u300_init_machine_dt, .restart = u300_restart, .dt_compat = u300_board_compat, diff --git a/arch/arm/mach-ux500/timer.c b/arch/arm/mach-ux500/timer.c index ff28d8a..8d2d233 100644 --- a/arch/arm/mach-ux500/timer.c +++ b/arch/arm/mach-ux500/timer.c @@ -44,5 +44,5 @@ void __init ux500_timer_init(void) dt_fail: clksrc_dbx500_prcmu_init(prcmu_timer_base); - clocksource_of_init(); + clocksource_probe(); } diff --git a/arch/arm/mach-zynq/common.c b/arch/arm/mach-zynq/common.c index 5a6e4e2..6f39d03 100644 --- a/arch/arm/mach-zynq/common.c +++ b/arch/arm/mach-zynq/common.c @@ -154,7 +154,7 @@ static void __init zynq_timer_init(void) zynq_clock_init(); of_clk_init(NULL); - clocksource_of_init(); + clocksource_probe(); } static struct map_desc zynq_cortex_a9_scu_map __initdata = { diff --git a/arch/arm64/kernel/time.c b/arch/arm64/kernel/time.c index 819a105..13339b6 100644 --- a/arch/arm64/kernel/time.c +++ b/arch/arm64/kernel/time.c @@ -67,7 +67,7 @@ void __init time_init(void) u32 arch_timer_rate; of_clk_init(NULL); - clocksource_of_init(); + clocksource_probe(); tick_setup_hrtimer_broadcast(); diff --git a/arch/microblaze/kernel/setup.c b/arch/microblaze/kernel/setup.c index ab5b488..89a2a93 100644 --- a/arch/microblaze/kernel/setup.c +++ b/arch/microblaze/kernel/setup.c @@ -194,7 +194,7 @@ void __init time_init(void) { of_clk_init(NULL); setup_cpuinfo_clk(); - clocksource_of_init(); + clocksource_probe(); } #ifdef CONFIG_DEBUG_FS diff --git a/arch/mips/pistachio/time.c b/arch/mips/pistachio/time.c index 8a37734..1022201 100644 --- a/arch/mips/pistachio/time.c +++ b/arch/mips/pistachio/time.c @@ -39,7 +39,7 @@ void __init plat_time_init(void) struct clk *clk; of_clk_init(NULL); - clocksource_of_init(); + clocksource_probe(); np = of_get_cpu_node(0, NULL); if (!np) { diff --git a/arch/mips/ralink/clk.c b/arch/mips/ralink/clk.c index feb5a9b..25c4a61 100644 --- a/arch/mips/ralink/clk.c +++ b/arch/mips/ralink/clk.c @@ -75,5 +75,5 @@ void __init plat_time_init(void) pr_info("CPU Clock: %ldMHz\n", clk_get_rate(clk) / 1000000); mips_hpt_frequency = clk_get_rate(clk) / 2; clk_put(clk); - clocksource_of_init(); + clocksource_probe(); } diff --git a/arch/nios2/kernel/time.c b/arch/nios2/kernel/time.c index bbc3f91..e835dda 100644 --- a/arch/nios2/kernel/time.c +++ b/arch/nios2/kernel/time.c @@ -324,7 +324,7 @@ void __init time_init(void) if (count < 2) panic("%d timer is found, it needs 2 timers in system\n", count); - clocksource_of_init(); + clocksource_probe(); } CLOCKSOURCE_OF_DECLARE(nios2_timer, ALTR_TIMER_COMPATIBLE, nios2_time_init); diff --git a/arch/xtensa/kernel/time.c b/arch/xtensa/kernel/time.c index b97767d..b9ad9fe 100644 --- a/arch/xtensa/kernel/time.c +++ b/arch/xtensa/kernel/time.c @@ -148,7 +148,7 @@ void __init time_init(void) local_timer_setup(0); setup_irq(this_cpu_ptr(&ccount_timer)->evt.irq, &timer_irqaction); sched_clock_register(ccount_sched_clock_read, 32, ccount_freq); - clocksource_of_init(); + clocksource_probe(); } /* diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile index b47be92..51856d5 100644 --- a/drivers/clocksource/Makefile +++ b/drivers/clocksource/Makefile @@ -1,4 +1,4 @@ -obj-$(CONFIG_CLKSRC_PROBE) += clksrc-of.o +obj-$(CONFIG_CLKSRC_PROBE) += clksrc-probe.o obj-$(CONFIG_ATMEL_PIT) += timer-atmel-pit.o obj-$(CONFIG_ATMEL_ST) += timer-atmel-st.o obj-$(CONFIG_ATMEL_TCB_CLKSRC) += tcb_clksrc.o diff --git a/drivers/clocksource/clksrc-of.c b/drivers/clocksource/clksrc-of.c deleted file mode 100644 index a2105bd..0000000 --- a/drivers/clocksource/clksrc-of.c +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include -#include -#include - -extern struct of_device_id __clksrc_of_table[]; - -static const struct of_device_id __clksrc_of_table_sentinel - __used __section(__clksrc_of_table_end); - -void __init clocksource_of_init(void) -{ - struct device_node *np; - const struct of_device_id *match; - of_init_fn_1 init_func; - unsigned clocksources = 0; - - for_each_matching_node_and_match(np, __clksrc_of_table, &match) { - if (!of_device_is_available(np)) - continue; - - init_func = match->data; - init_func(np); - clocksources++; - } - - clocksources += acpi_probe_device_table(clksrc); - - if (!clocksources) - pr_crit("%s: no matching clocksources found\n", __func__); -} diff --git a/drivers/clocksource/clksrc-probe.c b/drivers/clocksource/clksrc-probe.c new file mode 100644 index 0000000..7cb6c92 --- /dev/null +++ b/drivers/clocksource/clksrc-probe.c @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include + +extern struct of_device_id __clksrc_of_table[]; + +static const struct of_device_id __clksrc_of_table_sentinel + __used __section(__clksrc_of_table_end); + +void __init clocksource_probe(void) +{ + struct device_node *np; + const struct of_device_id *match; + of_init_fn_1 init_func; + unsigned clocksources = 0; + + for_each_matching_node_and_match(np, __clksrc_of_table, &match) { + if (!of_device_is_available(np)) + continue; + + init_func = match->data; + init_func(np); + clocksources++; + } + + clocksources += acpi_probe_device_table(clksrc); + + if (!clocksources) + pr_crit("%s: no matching clocksources found\n", __func__); +} diff --git a/include/linux/clocksource.h b/include/linux/clocksource.h index 116645f..7784b59 100644 --- a/include/linux/clocksource.h +++ b/include/linux/clocksource.h @@ -247,9 +247,9 @@ extern int clocksource_i8253_init(void); OF_DECLARE_1(clksrc, name, compat, fn) #ifdef CONFIG_CLKSRC_PROBE -extern void clocksource_of_init(void); +extern void clocksource_probe(void); #else -static inline void clocksource_of_init(void) {} +static inline void clocksource_probe(void) {} #endif #define CLOCKSOURCE_ACPI_DECLARE(name, table_id, fn) \ -- cgit v0.10.2 From a6eed752f5fb40990eb28fddb2b93258fb7e3be0 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Mon, 21 Sep 2015 19:26:02 +0300 Subject: PM / OPP: passing NULL to PTR_ERR() The code was using PTR_ERR(NULL) which causes a static checker warning. I have fixed up the printks and changed the return to -ENOENT. Signed-off-by: Dan Carpenter Acked-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki diff --git a/drivers/base/power/opp/cpu.c b/drivers/base/power/opp/cpu.c index 3d946b5..7654c56 100644 --- a/drivers/base/power/opp/cpu.c +++ b/drivers/base/power/opp/cpu.c @@ -223,8 +223,7 @@ int dev_pm_opp_of_get_sharing_cpus(struct device *cpu_dev, cpumask_var_t cpumask /* Get OPP descriptor node */ np = _of_get_opp_desc_node(cpu_dev); if (!np) { - dev_dbg(cpu_dev, "%s: Couldn't find opp node: %ld\n", __func__, - PTR_ERR(np)); + dev_dbg(cpu_dev, "%s: Couldn't find cpu_dev node.\n", __func__); return -ENOENT; } @@ -247,9 +246,9 @@ int dev_pm_opp_of_get_sharing_cpus(struct device *cpu_dev, cpumask_var_t cpumask /* Get OPP descriptor node */ tmp_np = _of_get_opp_desc_node(tcpu_dev); if (!tmp_np) { - dev_err(tcpu_dev, "%s: Couldn't find opp node: %ld\n", - __func__, PTR_ERR(tmp_np)); - ret = PTR_ERR(tmp_np); + dev_err(tcpu_dev, "%s: Couldn't find tcpu_dev node.\n", + __func__); + ret = -ENOENT; goto put_cpu_node; } -- cgit v0.10.2 From 2cef548adf58e9a58a411948b98edb9a3980dbe6 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 30 Sep 2015 01:10:24 +0200 Subject: PCI / PM: Avoid resuming more devices during system suspend Commit bac2a909a096 (PCI / PM: Avoid resuming PCI devices during system suspend) introduced a mechanism by which some PCI devices that were runtime-suspended at the system suspend time might be left in that state for the duration of the system suspend-resume cycle. However, it overlooked devices that were marked as capable of waking up the system just because PME support was detected in their PCI config space. Namely, in that case, device_can_wakeup(dev) returns 'true' for the device and if the device is not configured for system wakeup, device_may_wakeup(dev) returns 'false' and it will be resumed during system suspend even though configuring it for system wakeup may not really make sense at all. To avoid this problem, simply disable PME for PCI devices that have not been configured for system wakeup and are runtime-suspended at the system suspend time for the duration of the suspend-resume cycle. If the device is in D3cold, its config space is not available and it shouldn't be written to, but that's only possible if the device has platform PM support and the platform code is responsible for checking whether or not the device's configuration is suitable for system suspend in that case. Signed-off-by: Rafael J. Wysocki diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index 108a311..8dfb144 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -684,10 +684,21 @@ static int pci_pm_prepare(struct device *dev) return pci_dev_keep_suspended(to_pci_dev(dev)); } +static void pci_pm_complete(struct device *dev) +{ + struct device_driver *drv = dev->driver; + struct pci_dev *pci_dev = to_pci_dev(dev); + + pci_dev_complete_resume(pci_dev); + + if (drv && drv->pm && drv->pm->complete) + drv->pm->complete(dev); +} #else /* !CONFIG_PM_SLEEP */ #define pci_pm_prepare NULL +#define pci_pm_complete NULL #endif /* !CONFIG_PM_SLEEP */ @@ -1218,6 +1229,7 @@ static int pci_pm_runtime_idle(struct device *dev) static const struct dev_pm_ops pci_dev_pm_ops = { .prepare = pci_pm_prepare, + .complete = pci_pm_complete, .suspend = pci_pm_suspend, .resume = pci_pm_resume, .freeze = pci_pm_freeze, diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 6a9a111..78693fc 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -1710,15 +1710,7 @@ static void pci_pme_list_scan(struct work_struct *work) mutex_unlock(&pci_pme_list_mutex); } -/** - * pci_pme_active - enable or disable PCI device's PME# function - * @dev: PCI device to handle. - * @enable: 'true' to enable PME# generation; 'false' to disable it. - * - * The caller must verify that the device is capable of generating PME# before - * calling this function with @enable equal to 'true'. - */ -void pci_pme_active(struct pci_dev *dev, bool enable) +static void __pci_pme_active(struct pci_dev *dev, bool enable) { u16 pmcsr; @@ -1732,6 +1724,19 @@ void pci_pme_active(struct pci_dev *dev, bool enable) pmcsr &= ~PCI_PM_CTRL_PME_ENABLE; pci_write_config_word(dev, dev->pm_cap + PCI_PM_CTRL, pmcsr); +} + +/** + * pci_pme_active - enable or disable PCI device's PME# function + * @dev: PCI device to handle. + * @enable: 'true' to enable PME# generation; 'false' to disable it. + * + * The caller must verify that the device is capable of generating PME# before + * calling this function with @enable equal to 'true'. + */ +void pci_pme_active(struct pci_dev *dev, bool enable) +{ + __pci_pme_active(dev, enable); /* * PCI (as opposed to PCIe) PME requires that the device have @@ -2032,17 +2037,60 @@ EXPORT_SYMBOL_GPL(pci_dev_run_wake); * reconfigured due to wakeup settings difference between system and runtime * suspend and the current power state of it is suitable for the upcoming * (system) transition. + * + * If the device is not configured for system wakeup, disable PME for it before + * returning 'true' to prevent it from waking up the system unnecessarily. */ bool pci_dev_keep_suspended(struct pci_dev *pci_dev) { struct device *dev = &pci_dev->dev; if (!pm_runtime_suspended(dev) - || (device_can_wakeup(dev) && !device_may_wakeup(dev)) + || pci_target_state(pci_dev) != pci_dev->current_state || platform_pci_need_resume(pci_dev)) return false; - return pci_target_state(pci_dev) == pci_dev->current_state; + /* + * At this point the device is good to go unless it's been configured + * to generate PME at the runtime suspend time, but it is not supposed + * to wake up the system. In that case, simply disable PME for it + * (it will have to be re-enabled on exit from system resume). + * + * If the device's power state is D3cold and the platform check above + * hasn't triggered, the device's configuration is suitable and we don't + * need to manipulate it at all. + */ + spin_lock_irq(&dev->power.lock); + + if (pm_runtime_suspended(dev) && pci_dev->current_state < PCI_D3cold && + !device_may_wakeup(dev)) + __pci_pme_active(pci_dev, false); + + spin_unlock_irq(&dev->power.lock); + return true; +} + +/** + * pci_dev_complete_resume - Finalize resume from system sleep for a device. + * @pci_dev: Device to handle. + * + * If the device is runtime suspended and wakeup-capable, enable PME for it as + * it might have been disabled during the prepare phase of system suspend if + * the device was not configured for system wakeup. + */ +void pci_dev_complete_resume(struct pci_dev *pci_dev) +{ + struct device *dev = &pci_dev->dev; + + if (!pci_dev_run_wake(pci_dev)) + return; + + spin_lock_irq(&dev->power.lock); + + if (pm_runtime_suspended(dev) && pci_dev->current_state < PCI_D3cold) + __pci_pme_active(pci_dev, true); + + spin_unlock_irq(&dev->power.lock); } void pci_config_pm_runtime_get(struct pci_dev *pdev) diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 24ba9dc..037e787 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -75,6 +75,7 @@ void pci_disable_enabled_device(struct pci_dev *dev); int pci_finish_runtime_suspend(struct pci_dev *dev); int __pci_pme_wakeup(struct pci_dev *dev, void *ign); bool pci_dev_keep_suspended(struct pci_dev *dev); +void pci_dev_complete_resume(struct pci_dev *pci_dev); void pci_config_pm_runtime_get(struct pci_dev *dev); void pci_config_pm_runtime_put(struct pci_dev *dev); void pci_pm_init(struct pci_dev *dev); -- cgit v0.10.2 From 337aadff8e4567e39669e07d9a88b789d78458b5 Mon Sep 17 00:00:00 2001 From: Ashwin Chaugule Date: Fri, 2 Oct 2015 10:01:19 -0400 Subject: ACPI: Introduce CPU performance controls using CPPC CPPC stands for Collaborative Processor Performance Controls and is defined in the ACPI v5.0+ spec. It describes CPU performance controls on an abstract and continuous scale allowing the platform (e.g. remote power processor) to flexibly optimize CPU performance with its knowledge of power budgets and other architecture specific knowledge. This patch adds a shim which exports commonly used functions to get and set CPPC specific controls for each CPU. This enables CPUFreq drivers to gather per CPU performance data and use with exisiting governors or even allows for customized governors which are implemented inside CPUFreq drivers. Signed-off-by: Ashwin Chaugule Reviewed-by: Al Stone Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index 5d1015c..93a04ee 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -197,6 +197,20 @@ config ACPI_PROCESSOR_IDLE bool select CPU_IDLE +config ACPI_CPPC_LIB + bool + depends on ACPI_PROCESSOR + depends on !ACPI_CPU_FREQ_PSS + select MAILBOX + select PCC + help + If this option is enabled, this file implements common functionality + to parse CPPC tables as described in the ACPI 5.1+ spec. The + routines implemented are meant to be used by other + drivers to control CPU performance using CPPC semantics. + If your platform does not support CPPC in firmware, + leave this option disabled. + config ACPI_PROCESSOR tristate "Processor" depends on X86 || IA64 diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile index b5e7cd8..675eaf3 100644 --- a/drivers/acpi/Makefile +++ b/drivers/acpi/Makefile @@ -78,6 +78,7 @@ obj-$(CONFIG_ACPI_HED) += hed.o obj-$(CONFIG_ACPI_EC_DEBUGFS) += ec_sys.o obj-$(CONFIG_ACPI_CUSTOM_METHOD)+= custom_method.o obj-$(CONFIG_ACPI_BGRT) += bgrt.o +obj-$(CONFIG_ACPI_CPPC_LIB) += cppc_acpi.o # processor has its own "processor." module_param namespace processor-y := processor_driver.o diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c new file mode 100644 index 0000000..0bbf84b --- /dev/null +++ b/drivers/acpi/cppc_acpi.c @@ -0,0 +1,734 @@ +/* + * CPPC (Collaborative Processor Performance Control) methods used by CPUfreq drivers. + * + * (C) Copyright 2014, 2015 Linaro Ltd. + * Author: Ashwin Chaugule + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; version 2 + * of the License. + * + * CPPC describes a few methods for controlling CPU performance using + * information from a per CPU table called CPC. This table is described in + * the ACPI v5.0+ specification. The table consists of a list of + * registers which may be memory mapped or hardware registers and also may + * include some static integer values. + * + * CPU performance is on an abstract continuous scale as against a discretized + * P-state scale which is tied to CPU frequency only. In brief, the basic + * operation involves: + * + * - OS makes a CPU performance request. (Can provide min and max bounds) + * + * - Platform (such as BMC) is free to optimize request within requested bounds + * depending on power/thermal budgets etc. + * + * - Platform conveys its decision back to OS + * + * The communication between OS and platform occurs through another medium + * called (PCC) Platform Communication Channel. This is a generic mailbox like + * mechanism which includes doorbell semantics to indicate register updates. + * See drivers/mailbox/pcc.c for details on PCC. + * + * Finer details about the PCC and CPPC spec are available in the ACPI v5.1 and + * above specifications. + */ + +#define pr_fmt(fmt) "ACPI CPPC: " fmt + +#include +#include + +#include +/* + * Lock to provide mutually exclusive access to the PCC + * channel. e.g. When the remote updates the shared region + * with new data, the reader needs to be protected from + * other CPUs activity on the same channel. + */ +static DEFINE_SPINLOCK(pcc_lock); + +/* + * The cpc_desc structure contains the ACPI register details + * as described in the per CPU _CPC tables. The details + * include the type of register (e.g. PCC, System IO, FFH etc.) + * and destination addresses which lets us READ/WRITE CPU performance + * information using the appropriate I/O methods. + */ +static DEFINE_PER_CPU(struct cpc_desc *, cpc_desc_ptr); + +/* This layer handles all the PCC specifics for CPPC. */ +static struct mbox_chan *pcc_channel; +static void __iomem *pcc_comm_addr; +static u64 comm_base_addr; +static int pcc_subspace_idx = -1; +static u16 pcc_cmd_delay; +static bool pcc_channel_acquired; + +/* + * Arbitrary Retries in case the remote processor is slow to respond + * to PCC commands. + */ +#define NUM_RETRIES 500 + +static int send_pcc_cmd(u16 cmd) +{ + int retries, result = -EIO; + struct acpi_pcct_hw_reduced *pcct_ss = pcc_channel->con_priv; + struct acpi_pcct_shared_memory *generic_comm_base = + (struct acpi_pcct_shared_memory *) pcc_comm_addr; + u32 cmd_latency = pcct_ss->latency; + + /* Min time OS should wait before sending next command. */ + udelay(pcc_cmd_delay); + + /* Write to the shared comm region. */ + writew(cmd, &generic_comm_base->command); + + /* Flip CMD COMPLETE bit */ + writew(0, &generic_comm_base->status); + + /* Ring doorbell */ + result = mbox_send_message(pcc_channel, &cmd); + if (result < 0) { + pr_err("Err sending PCC mbox message. cmd:%d, ret:%d\n", + cmd, result); + return result; + } + + /* Wait for a nominal time to let platform process command. */ + udelay(cmd_latency); + + /* Retry in case the remote processor was too slow to catch up. */ + for (retries = NUM_RETRIES; retries > 0; retries--) { + if (readw_relaxed(&generic_comm_base->status) & PCC_CMD_COMPLETE) { + result = 0; + break; + } + } + + mbox_client_txdone(pcc_channel, result); + return result; +} + +static void cppc_chan_tx_done(struct mbox_client *cl, void *msg, int ret) +{ + if (ret) + pr_debug("TX did not complete: CMD sent:%x, ret:%d\n", + *(u16 *)msg, ret); + else + pr_debug("TX completed. CMD sent:%x, ret:%d\n", + *(u16 *)msg, ret); +} + +struct mbox_client cppc_mbox_cl = { + .tx_done = cppc_chan_tx_done, + .knows_txdone = true, +}; + +static int acpi_get_psd(struct cpc_desc *cpc_ptr, acpi_handle handle) +{ + int result = -EFAULT; + acpi_status status = AE_OK; + struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL}; + struct acpi_buffer format = {sizeof("NNNNN"), "NNNNN"}; + struct acpi_buffer state = {0, NULL}; + union acpi_object *psd = NULL; + struct acpi_psd_package *pdomain; + + status = acpi_evaluate_object_typed(handle, "_PSD", NULL, &buffer, + ACPI_TYPE_PACKAGE); + if (ACPI_FAILURE(status)) + return -ENODEV; + + psd = buffer.pointer; + if (!psd || psd->package.count != 1) { + pr_debug("Invalid _PSD data\n"); + goto end; + } + + pdomain = &(cpc_ptr->domain_info); + + state.length = sizeof(struct acpi_psd_package); + state.pointer = pdomain; + + status = acpi_extract_package(&(psd->package.elements[0]), + &format, &state); + if (ACPI_FAILURE(status)) { + pr_debug("Invalid _PSD data for CPU:%d\n", cpc_ptr->cpu_id); + goto end; + } + + if (pdomain->num_entries != ACPI_PSD_REV0_ENTRIES) { + pr_debug("Unknown _PSD:num_entries for CPU:%d\n", cpc_ptr->cpu_id); + goto end; + } + + if (pdomain->revision != ACPI_PSD_REV0_REVISION) { + pr_debug("Unknown _PSD:revision for CPU: %d\n", cpc_ptr->cpu_id); + goto end; + } + + if (pdomain->coord_type != DOMAIN_COORD_TYPE_SW_ALL && + pdomain->coord_type != DOMAIN_COORD_TYPE_SW_ANY && + pdomain->coord_type != DOMAIN_COORD_TYPE_HW_ALL) { + pr_debug("Invalid _PSD:coord_type for CPU:%d\n", cpc_ptr->cpu_id); + goto end; + } + + result = 0; +end: + kfree(buffer.pointer); + return result; +} + +/** + * acpi_get_psd_map - Map the CPUs in a common freq domain. + * @all_cpu_data: Ptrs to CPU specific CPPC data including PSD info. + * + * Return: 0 for success or negative value for err. + */ +int acpi_get_psd_map(struct cpudata **all_cpu_data) +{ + int count_target; + int retval = 0; + unsigned int i, j; + cpumask_var_t covered_cpus; + struct cpudata *pr, *match_pr; + struct acpi_psd_package *pdomain; + struct acpi_psd_package *match_pdomain; + struct cpc_desc *cpc_ptr, *match_cpc_ptr; + + if (!zalloc_cpumask_var(&covered_cpus, GFP_KERNEL)) + return -ENOMEM; + + /* + * Now that we have _PSD data from all CPUs, lets setup P-state + * domain info. + */ + for_each_possible_cpu(i) { + pr = all_cpu_data[i]; + if (!pr) + continue; + + if (cpumask_test_cpu(i, covered_cpus)) + continue; + + cpc_ptr = per_cpu(cpc_desc_ptr, i); + if (!cpc_ptr) + continue; + + pdomain = &(cpc_ptr->domain_info); + cpumask_set_cpu(i, pr->shared_cpu_map); + cpumask_set_cpu(i, covered_cpus); + if (pdomain->num_processors <= 1) + continue; + + /* Validate the Domain info */ + count_target = pdomain->num_processors; + if (pdomain->coord_type == DOMAIN_COORD_TYPE_SW_ALL) + pr->shared_type = CPUFREQ_SHARED_TYPE_ALL; + else if (pdomain->coord_type == DOMAIN_COORD_TYPE_HW_ALL) + pr->shared_type = CPUFREQ_SHARED_TYPE_HW; + else if (pdomain->coord_type == DOMAIN_COORD_TYPE_SW_ANY) + pr->shared_type = CPUFREQ_SHARED_TYPE_ANY; + + for_each_possible_cpu(j) { + if (i == j) + continue; + + match_cpc_ptr = per_cpu(cpc_desc_ptr, j); + if (!match_cpc_ptr) + continue; + + match_pdomain = &(match_cpc_ptr->domain_info); + if (match_pdomain->domain != pdomain->domain) + continue; + + /* Here i and j are in the same domain */ + if (match_pdomain->num_processors != count_target) { + retval = -EFAULT; + goto err_ret; + } + + if (pdomain->coord_type != match_pdomain->coord_type) { + retval = -EFAULT; + goto err_ret; + } + + cpumask_set_cpu(j, covered_cpus); + cpumask_set_cpu(j, pr->shared_cpu_map); + } + + for_each_possible_cpu(j) { + if (i == j) + continue; + + match_pr = all_cpu_data[j]; + if (!match_pr) + continue; + + match_cpc_ptr = per_cpu(cpc_desc_ptr, j); + if (!match_cpc_ptr) + continue; + + match_pdomain = &(match_cpc_ptr->domain_info); + if (match_pdomain->domain != pdomain->domain) + continue; + + match_pr->shared_type = pr->shared_type; + cpumask_copy(match_pr->shared_cpu_map, + pr->shared_cpu_map); + } + } + +err_ret: + for_each_possible_cpu(i) { + pr = all_cpu_data[i]; + if (!pr) + continue; + + /* Assume no coordination on any error parsing domain info */ + if (retval) { + cpumask_clear(pr->shared_cpu_map); + cpumask_set_cpu(i, pr->shared_cpu_map); + pr->shared_type = CPUFREQ_SHARED_TYPE_ALL; + } + } + + free_cpumask_var(covered_cpus); + return retval; +} +EXPORT_SYMBOL_GPL(acpi_get_psd_map); + +static int register_pcc_channel(unsigned pcc_subspace_idx) +{ + struct acpi_pcct_subspace *cppc_ss; + unsigned int len; + + if (pcc_subspace_idx >= 0) { + pcc_channel = pcc_mbox_request_channel(&cppc_mbox_cl, + pcc_subspace_idx); + + if (IS_ERR(pcc_channel)) { + pr_err("Failed to find PCC communication channel\n"); + return -ENODEV; + } + + /* + * The PCC mailbox controller driver should + * have parsed the PCCT (global table of all + * PCC channels) and stored pointers to the + * subspace communication region in con_priv. + */ + cppc_ss = pcc_channel->con_priv; + + if (!cppc_ss) { + pr_err("No PCC subspace found for CPPC\n"); + return -ENODEV; + } + + /* + * This is the shared communication region + * for the OS and Platform to communicate over. + */ + comm_base_addr = cppc_ss->base_address; + len = cppc_ss->length; + pcc_cmd_delay = cppc_ss->min_turnaround_time; + + pcc_comm_addr = acpi_os_ioremap(comm_base_addr, len); + if (!pcc_comm_addr) { + pr_err("Failed to ioremap PCC comm region mem\n"); + return -ENOMEM; + } + + /* Set flag so that we dont come here for each CPU. */ + pcc_channel_acquired = true; + } + + return 0; +} + +/* + * An example CPC table looks like the following. + * + * Name(_CPC, Package() + * { + * 17, + * NumEntries + * 1, + * // Revision + * ResourceTemplate(){Register(PCC, 32, 0, 0x120, 2)}, + * // Highest Performance + * ResourceTemplate(){Register(PCC, 32, 0, 0x124, 2)}, + * // Nominal Performance + * ResourceTemplate(){Register(PCC, 32, 0, 0x128, 2)}, + * // Lowest Nonlinear Performance + * ResourceTemplate(){Register(PCC, 32, 0, 0x12C, 2)}, + * // Lowest Performance + * ResourceTemplate(){Register(PCC, 32, 0, 0x130, 2)}, + * // Guaranteed Performance Register + * ResourceTemplate(){Register(PCC, 32, 0, 0x110, 2)}, + * // Desired Performance Register + * ResourceTemplate(){Register(SystemMemory, 0, 0, 0, 0)}, + * .. + * .. + * .. + * + * } + * Each Register() encodes how to access that specific register. + * e.g. a sample PCC entry has the following encoding: + * + * Register ( + * PCC, + * AddressSpaceKeyword + * 8, + * //RegisterBitWidth + * 8, + * //RegisterBitOffset + * 0x30, + * //RegisterAddress + * 9 + * //AccessSize (subspace ID) + * 0 + * ) + * } + */ + +/** + * acpi_cppc_processor_probe - Search for per CPU _CPC objects. + * @pr: Ptr to acpi_processor containing this CPUs logical Id. + * + * Return: 0 for success or negative value for err. + */ +int acpi_cppc_processor_probe(struct acpi_processor *pr) +{ + struct acpi_buffer output = {ACPI_ALLOCATE_BUFFER, NULL}; + union acpi_object *out_obj, *cpc_obj; + struct cpc_desc *cpc_ptr; + struct cpc_reg *gas_t; + acpi_handle handle = pr->handle; + unsigned int num_ent, i, cpc_rev; + acpi_status status; + int ret = -EFAULT; + + /* Parse the ACPI _CPC table for this cpu. */ + status = acpi_evaluate_object_typed(handle, "_CPC", NULL, &output, + ACPI_TYPE_PACKAGE); + if (ACPI_FAILURE(status)) { + ret = -ENODEV; + goto out_buf_free; + } + + out_obj = (union acpi_object *) output.pointer; + + cpc_ptr = kzalloc(sizeof(struct cpc_desc), GFP_KERNEL); + if (!cpc_ptr) { + ret = -ENOMEM; + goto out_buf_free; + } + + /* First entry is NumEntries. */ + cpc_obj = &out_obj->package.elements[0]; + if (cpc_obj->type == ACPI_TYPE_INTEGER) { + num_ent = cpc_obj->integer.value; + } else { + pr_debug("Unexpected entry type(%d) for NumEntries\n", + cpc_obj->type); + goto out_free; + } + + /* Only support CPPCv2. Bail otherwise. */ + if (num_ent != CPPC_NUM_ENT) { + pr_debug("Firmware exports %d entries. Expected: %d\n", + num_ent, CPPC_NUM_ENT); + goto out_free; + } + + /* Second entry should be revision. */ + cpc_obj = &out_obj->package.elements[1]; + if (cpc_obj->type == ACPI_TYPE_INTEGER) { + cpc_rev = cpc_obj->integer.value; + } else { + pr_debug("Unexpected entry type(%d) for Revision\n", + cpc_obj->type); + goto out_free; + } + + if (cpc_rev != CPPC_REV) { + pr_debug("Firmware exports revision:%d. Expected:%d\n", + cpc_rev, CPPC_REV); + goto out_free; + } + + /* Iterate through remaining entries in _CPC */ + for (i = 2; i < num_ent; i++) { + cpc_obj = &out_obj->package.elements[i]; + + if (cpc_obj->type == ACPI_TYPE_INTEGER) { + cpc_ptr->cpc_regs[i-2].type = ACPI_TYPE_INTEGER; + cpc_ptr->cpc_regs[i-2].cpc_entry.int_value = cpc_obj->integer.value; + } else if (cpc_obj->type == ACPI_TYPE_BUFFER) { + gas_t = (struct cpc_reg *) + cpc_obj->buffer.pointer; + + /* + * The PCC Subspace index is encoded inside + * the CPC table entries. The same PCC index + * will be used for all the PCC entries, + * so extract it only once. + */ + if (gas_t->space_id == ACPI_ADR_SPACE_PLATFORM_COMM) { + if (pcc_subspace_idx < 0) + pcc_subspace_idx = gas_t->access_width; + else if (pcc_subspace_idx != gas_t->access_width) { + pr_debug("Mismatched PCC ids.\n"); + goto out_free; + } + } else if (gas_t->space_id != ACPI_ADR_SPACE_SYSTEM_MEMORY) { + /* Support only PCC and SYS MEM type regs */ + pr_debug("Unsupported register type: %d\n", gas_t->space_id); + goto out_free; + } + + cpc_ptr->cpc_regs[i-2].type = ACPI_TYPE_BUFFER; + memcpy(&cpc_ptr->cpc_regs[i-2].cpc_entry.reg, gas_t, sizeof(*gas_t)); + } else { + pr_debug("Err in entry:%d in CPC table of CPU:%d \n", i, pr->id); + goto out_free; + } + } + /* Store CPU Logical ID */ + cpc_ptr->cpu_id = pr->id; + + /* Plug it into this CPUs CPC descriptor. */ + per_cpu(cpc_desc_ptr, pr->id) = cpc_ptr; + + /* Parse PSD data for this CPU */ + ret = acpi_get_psd(cpc_ptr, handle); + if (ret) + goto out_free; + + /* Register PCC channel once for all CPUs. */ + if (!pcc_channel_acquired) { + ret = register_pcc_channel(pcc_subspace_idx); + if (ret) + goto out_free; + } + + /* Everything looks okay */ + pr_debug("Parsed CPC struct for CPU: %d\n", pr->id); + + kfree(output.pointer); + return 0; + +out_free: + cpc_ptr = per_cpu(cpc_desc_ptr, pr->id); + kfree(cpc_ptr); + +out_buf_free: + kfree(output.pointer); + return ret; +} +EXPORT_SYMBOL_GPL(acpi_cppc_processor_probe); + +/** + * acpi_cppc_processor_exit - Cleanup CPC structs. + * @pr: Ptr to acpi_processor containing this CPUs logical Id. + * + * Return: Void + */ +void acpi_cppc_processor_exit(struct acpi_processor *pr) +{ + struct cpc_desc *cpc_ptr; + cpc_ptr = per_cpu(cpc_desc_ptr, pr->id); + kfree(cpc_ptr); +} +EXPORT_SYMBOL_GPL(acpi_cppc_processor_exit); + +static u64 get_phys_addr(struct cpc_reg *reg) +{ + /* PCC communication addr space begins at byte offset 0x8. */ + if (reg->space_id == ACPI_ADR_SPACE_PLATFORM_COMM) + return (u64)comm_base_addr + 0x8 + reg->address; + else + return reg->address; +} + +static void cpc_read(struct cpc_reg *reg, u64 *val) +{ + u64 addr = get_phys_addr(reg); + + acpi_os_read_memory((acpi_physical_address)addr, + val, reg->bit_width); +} + +static void cpc_write(struct cpc_reg *reg, u64 val) +{ + u64 addr = get_phys_addr(reg); + + acpi_os_write_memory((acpi_physical_address)addr, + val, reg->bit_width); +} + +/** + * cppc_get_perf_caps - Get a CPUs performance capabilities. + * @cpunum: CPU from which to get capabilities info. + * @perf_caps: ptr to cppc_perf_caps. See cppc_acpi.h + * + * Return: 0 for success with perf_caps populated else -ERRNO. + */ +int cppc_get_perf_caps(int cpunum, struct cppc_perf_caps *perf_caps) +{ + struct cpc_desc *cpc_desc = per_cpu(cpc_desc_ptr, cpunum); + struct cpc_register_resource *highest_reg, *lowest_reg, *ref_perf, + *nom_perf; + u64 high, low, ref, nom; + int ret = 0; + + if (!cpc_desc) { + pr_debug("No CPC descriptor for CPU:%d\n", cpunum); + return -ENODEV; + } + + highest_reg = &cpc_desc->cpc_regs[HIGHEST_PERF]; + lowest_reg = &cpc_desc->cpc_regs[LOWEST_PERF]; + ref_perf = &cpc_desc->cpc_regs[REFERENCE_PERF]; + nom_perf = &cpc_desc->cpc_regs[NOMINAL_PERF]; + + spin_lock(&pcc_lock); + + /* Are any of the regs PCC ?*/ + if ((highest_reg->cpc_entry.reg.space_id == ACPI_ADR_SPACE_PLATFORM_COMM) || + (lowest_reg->cpc_entry.reg.space_id == ACPI_ADR_SPACE_PLATFORM_COMM) || + (ref_perf->cpc_entry.reg.space_id == ACPI_ADR_SPACE_PLATFORM_COMM) || + (nom_perf->cpc_entry.reg.space_id == ACPI_ADR_SPACE_PLATFORM_COMM)) { + /* Ring doorbell once to update PCC subspace */ + if (send_pcc_cmd(CMD_READ)) { + ret = -EIO; + goto out_err; + } + } + + cpc_read(&highest_reg->cpc_entry.reg, &high); + perf_caps->highest_perf = high; + + cpc_read(&lowest_reg->cpc_entry.reg, &low); + perf_caps->lowest_perf = low; + + cpc_read(&ref_perf->cpc_entry.reg, &ref); + perf_caps->reference_perf = ref; + + cpc_read(&nom_perf->cpc_entry.reg, &nom); + perf_caps->nominal_perf = nom; + + if (!ref) + perf_caps->reference_perf = perf_caps->nominal_perf; + + if (!high || !low || !nom) + ret = -EFAULT; + +out_err: + spin_unlock(&pcc_lock); + return ret; +} +EXPORT_SYMBOL_GPL(cppc_get_perf_caps); + +/** + * cppc_get_perf_ctrs - Read a CPUs performance feedback counters. + * @cpunum: CPU from which to read counters. + * @perf_fb_ctrs: ptr to cppc_perf_fb_ctrs. See cppc_acpi.h + * + * Return: 0 for success with perf_fb_ctrs populated else -ERRNO. + */ +int cppc_get_perf_ctrs(int cpunum, struct cppc_perf_fb_ctrs *perf_fb_ctrs) +{ + struct cpc_desc *cpc_desc = per_cpu(cpc_desc_ptr, cpunum); + struct cpc_register_resource *delivered_reg, *reference_reg; + u64 delivered, reference; + int ret = 0; + + if (!cpc_desc) { + pr_debug("No CPC descriptor for CPU:%d\n", cpunum); + return -ENODEV; + } + + delivered_reg = &cpc_desc->cpc_regs[DELIVERED_CTR]; + reference_reg = &cpc_desc->cpc_regs[REFERENCE_CTR]; + + spin_lock(&pcc_lock); + + /* Are any of the regs PCC ?*/ + if ((delivered_reg->cpc_entry.reg.space_id == ACPI_ADR_SPACE_PLATFORM_COMM) || + (reference_reg->cpc_entry.reg.space_id == ACPI_ADR_SPACE_PLATFORM_COMM)) { + /* Ring doorbell once to update PCC subspace */ + if (send_pcc_cmd(CMD_READ)) { + ret = -EIO; + goto out_err; + } + } + + cpc_read(&delivered_reg->cpc_entry.reg, &delivered); + cpc_read(&reference_reg->cpc_entry.reg, &reference); + + if (!delivered || !reference) { + ret = -EFAULT; + goto out_err; + } + + perf_fb_ctrs->delivered = delivered; + perf_fb_ctrs->reference = reference; + + perf_fb_ctrs->delivered -= perf_fb_ctrs->prev_delivered; + perf_fb_ctrs->reference -= perf_fb_ctrs->prev_reference; + + perf_fb_ctrs->prev_delivered = delivered; + perf_fb_ctrs->prev_reference = reference; + +out_err: + spin_unlock(&pcc_lock); + return ret; +} +EXPORT_SYMBOL_GPL(cppc_get_perf_ctrs); + +/** + * cppc_set_perf - Set a CPUs performance controls. + * @cpu: CPU for which to set performance controls. + * @perf_ctrls: ptr to cppc_perf_ctrls. See cppc_acpi.h + * + * Return: 0 for success, -ERRNO otherwise. + */ +int cppc_set_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls) +{ + struct cpc_desc *cpc_desc = per_cpu(cpc_desc_ptr, cpu); + struct cpc_register_resource *desired_reg; + int ret = 0; + + if (!cpc_desc) { + pr_debug("No CPC descriptor for CPU:%d\n", cpu); + return -ENODEV; + } + + desired_reg = &cpc_desc->cpc_regs[DESIRED_PERF]; + + spin_lock(&pcc_lock); + + /* + * Skip writing MIN/MAX until Linux knows how to come up with + * useful values. + */ + cpc_write(&desired_reg->cpc_entry.reg, perf_ctrls->desired_perf); + + /* Is this a PCC reg ?*/ + if (desired_reg->cpc_entry.reg.space_id == ACPI_ADR_SPACE_PLATFORM_COMM) { + /* Ring doorbell so Remote can get our perf request. */ + if (send_pcc_cmd(CMD_WRITE)) + ret = -EIO; + } + + spin_unlock(&pcc_lock); + + return ret; +} +EXPORT_SYMBOL_GPL(cppc_set_perf); diff --git a/include/acpi/cppc_acpi.h b/include/acpi/cppc_acpi.h new file mode 100644 index 0000000..717a298 --- /dev/null +++ b/include/acpi/cppc_acpi.h @@ -0,0 +1,138 @@ +/* + * CPPC (Collaborative Processor Performance Control) methods used + * by CPUfreq drivers. + * + * (C) Copyright 2014, 2015 Linaro Ltd. + * Author: Ashwin Chaugule + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; version 2 + * of the License. + */ + +#ifndef _CPPC_ACPI_H +#define _CPPC_ACPI_H + +#include +#include +#include +#include + +#include + +/* Only support CPPCv2 for now. */ +#define CPPC_NUM_ENT 21 +#define CPPC_REV 2 + +#define PCC_CMD_COMPLETE 1 +#define MAX_CPC_REG_ENT 19 + +/* CPPC specific PCC commands. */ +#define CMD_READ 0 +#define CMD_WRITE 1 + +/* Each register has the folowing format. */ +struct cpc_reg { + u8 descriptor; + u16 length; + u8 space_id; + u8 bit_width; + u8 bit_offset; + u8 access_width; + u64 __iomem address; +} __packed; + +/* + * Each entry in the CPC table is either + * of type ACPI_TYPE_BUFFER or + * ACPI_TYPE_INTEGER. + */ +struct cpc_register_resource { + acpi_object_type type; + union { + struct cpc_reg reg; + u64 int_value; + } cpc_entry; +}; + +/* Container to hold the CPC details for each CPU */ +struct cpc_desc { + int num_entries; + int version; + int cpu_id; + struct cpc_register_resource cpc_regs[MAX_CPC_REG_ENT]; + struct acpi_psd_package domain_info; +}; + +/* These are indexes into the per-cpu cpc_regs[]. Order is important. */ +enum cppc_regs { + HIGHEST_PERF, + NOMINAL_PERF, + LOW_NON_LINEAR_PERF, + LOWEST_PERF, + GUARANTEED_PERF, + DESIRED_PERF, + MIN_PERF, + MAX_PERF, + PERF_REDUC_TOLERANCE, + TIME_WINDOW, + CTR_WRAP_TIME, + REFERENCE_CTR, + DELIVERED_CTR, + PERF_LIMITED, + ENABLE, + AUTO_SEL_ENABLE, + AUTO_ACT_WINDOW, + ENERGY_PERF, + REFERENCE_PERF, +}; + +/* + * Categorization of registers as described + * in the ACPI v.5.1 spec. + * XXX: Only filling up ones which are used by governors + * today. + */ +struct cppc_perf_caps { + u32 highest_perf; + u32 nominal_perf; + u32 reference_perf; + u32 lowest_perf; +}; + +struct cppc_perf_ctrls { + u32 max_perf; + u32 min_perf; + u32 desired_perf; +}; + +struct cppc_perf_fb_ctrs { + u64 reference; + u64 prev_reference; + u64 delivered; + u64 prev_delivered; +}; + +/* Per CPU container for runtime CPPC management. */ +struct cpudata { + int cpu; + struct cppc_perf_caps perf_caps; + struct cppc_perf_ctrls perf_ctrls; + struct cppc_perf_fb_ctrs perf_fb_ctrs; + struct cpufreq_policy *cur_policy; + unsigned int shared_type; + cpumask_var_t shared_cpu_map; +}; + +extern int cppc_get_perf_ctrs(int cpu, struct cppc_perf_fb_ctrs *perf_fb_ctrs); +extern int cppc_set_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls); +extern int cppc_get_perf_caps(int cpu, struct cppc_perf_caps *caps); +extern int acpi_get_psd_map(struct cpudata **); + +/* Methods to interact with the PCC mailbox controller. */ +extern struct mbox_chan * + pcc_mbox_request_channel(struct mbox_client *, unsigned int); +extern int mbox_send_message(struct mbox_chan *chan, void *mssg); + +#endif /* _CPPC_ACPI_H*/ -- cgit v0.10.2 From 5477fb3bd1e8e43299761850318fe0057172a24a Mon Sep 17 00:00:00 2001 From: Ashwin Chaugule Date: Fri, 2 Oct 2015 10:04:01 -0400 Subject: ACPI / CPPC: Add a CPUFreq driver for use with CPPC This driver utilizes the methods introduced in a previous patch titled - "ACPI: Introduce CPU performance controls using CPPC" and enables usage with existing CPUFreq governors. Signed-off-by: Ashwin Chaugule Reviewed-by: Al Stone Signed-off-by: Rafael J. Wysocki diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm index cd0391e..642fd49 100644 --- a/drivers/cpufreq/Kconfig.arm +++ b/drivers/cpufreq/Kconfig.arm @@ -227,3 +227,20 @@ config ARM_PXA2xx_CPUFREQ This add the CPUFreq driver support for Intel PXA2xx SOCs. If in doubt, say N. + +config ACPI_CPPC_CPUFREQ + tristate "CPUFreq driver based on the ACPI CPPC spec" + depends on ACPI + select ACPI_CPPC_LIB + default n + help + This adds a CPUFreq driver which uses CPPC methods + as described in the ACPIv5.1 spec. CPPC stands for + Collaborative Processor Performance Controls. It + is based on an abstract continuous scale of CPU + performance values which allows the remote power + processor to flexibly optimize for power and + performance. CPPC relies on power management firmware + support for its operation. + + If in doubt, say N. diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile index 4134038..47a7ebb 100644 --- a/drivers/cpufreq/Makefile +++ b/drivers/cpufreq/Makefile @@ -76,6 +76,8 @@ obj-$(CONFIG_ARM_SPEAR_CPUFREQ) += spear-cpufreq.o obj-$(CONFIG_ARM_TEGRA20_CPUFREQ) += tegra20-cpufreq.o obj-$(CONFIG_ARM_TEGRA124_CPUFREQ) += tegra124-cpufreq.o obj-$(CONFIG_ARM_VEXPRESS_SPC_CPUFREQ) += vexpress-spc-cpufreq.o +obj-$(CONFIG_ACPI_CPPC_CPUFREQ) += cppc_cpufreq.o + ################################################################################## # PowerPC platform drivers diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c new file mode 100644 index 0000000..93c219f --- /dev/null +++ b/drivers/cpufreq/cppc_cpufreq.c @@ -0,0 +1,176 @@ +/* + * CPPC (Collaborative Processor Performance Control) driver for + * interfacing with the CPUfreq layer and governors. See + * cppc_acpi.c for CPPC specific methods. + * + * (C) Copyright 2014, 2015 Linaro Ltd. + * Author: Ashwin Chaugule + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; version 2 + * of the License. + */ + +#define pr_fmt(fmt) "CPPC Cpufreq:" fmt + +#include +#include +#include +#include +#include +#include + +#include + +/* + * These structs contain information parsed from per CPU + * ACPI _CPC structures. + * e.g. For each CPU the highest, lowest supported + * performance capabilities, desired performance level + * requested etc. + */ +static struct cpudata **all_cpu_data; + +static int cppc_cpufreq_set_target(struct cpufreq_policy *policy, + unsigned int target_freq, + unsigned int relation) +{ + struct cpudata *cpu; + struct cpufreq_freqs freqs; + int ret = 0; + + cpu = all_cpu_data[policy->cpu]; + + cpu->perf_ctrls.desired_perf = target_freq; + freqs.old = policy->cur; + freqs.new = target_freq; + + cpufreq_freq_transition_begin(policy, &freqs); + ret = cppc_set_perf(cpu->cpu, &cpu->perf_ctrls); + cpufreq_freq_transition_end(policy, &freqs, ret != 0); + + if (ret) + pr_debug("Failed to set target on CPU:%d. ret:%d\n", + cpu->cpu, ret); + + return ret; +} + +static int cppc_verify_policy(struct cpufreq_policy *policy) +{ + cpufreq_verify_within_cpu_limits(policy); + return 0; +} + +static void cppc_cpufreq_stop_cpu(struct cpufreq_policy *policy) +{ + int cpu_num = policy->cpu; + struct cpudata *cpu = all_cpu_data[cpu_num]; + int ret; + + cpu->perf_ctrls.desired_perf = cpu->perf_caps.lowest_perf; + + ret = cppc_set_perf(cpu_num, &cpu->perf_ctrls); + if (ret) + pr_debug("Err setting perf value:%d on CPU:%d. ret:%d\n", + cpu->perf_caps.lowest_perf, cpu_num, ret); +} + +static int cppc_cpufreq_cpu_init(struct cpufreq_policy *policy) +{ + struct cpudata *cpu; + unsigned int cpu_num = policy->cpu; + int ret = 0; + + cpu = all_cpu_data[policy->cpu]; + + cpu->cpu = cpu_num; + ret = cppc_get_perf_caps(policy->cpu, &cpu->perf_caps); + + if (ret) { + pr_debug("Err reading CPU%d perf capabilities. ret:%d\n", + cpu_num, ret); + return ret; + } + + policy->min = cpu->perf_caps.lowest_perf; + policy->max = cpu->perf_caps.highest_perf; + policy->cpuinfo.min_freq = policy->min; + policy->cpuinfo.max_freq = policy->max; + + if (policy->shared_type == CPUFREQ_SHARED_TYPE_ANY) + cpumask_copy(policy->cpus, cpu->shared_cpu_map); + else { + /* Support only SW_ANY for now. */ + pr_debug("Unsupported CPU co-ord type\n"); + return -EFAULT; + } + + cpumask_set_cpu(policy->cpu, policy->cpus); + cpu->cur_policy = policy; + + /* Set policy->cur to max now. The governors will adjust later. */ + policy->cur = cpu->perf_ctrls.desired_perf = cpu->perf_caps.highest_perf; + + ret = cppc_set_perf(cpu_num, &cpu->perf_ctrls); + if (ret) + pr_debug("Err setting perf value:%d on CPU:%d. ret:%d\n", + cpu->perf_caps.highest_perf, cpu_num, ret); + + return ret; +} + +static struct cpufreq_driver cppc_cpufreq_driver = { + .flags = CPUFREQ_CONST_LOOPS, + .verify = cppc_verify_policy, + .target = cppc_cpufreq_set_target, + .init = cppc_cpufreq_cpu_init, + .stop_cpu = cppc_cpufreq_stop_cpu, + .name = "cppc_cpufreq", +}; + +static int __init cppc_cpufreq_init(void) +{ + int i, ret = 0; + struct cpudata *cpu; + + if (acpi_disabled) + return -ENODEV; + + all_cpu_data = kzalloc(sizeof(void *) * num_possible_cpus(), GFP_KERNEL); + if (!all_cpu_data) + return -ENOMEM; + + for_each_possible_cpu(i) { + all_cpu_data[i] = kzalloc(sizeof(struct cpudata), GFP_KERNEL); + if (!all_cpu_data[i]) + goto out; + + cpu = all_cpu_data[i]; + if (!zalloc_cpumask_var(&cpu->shared_cpu_map, GFP_KERNEL)) + goto out; + } + + ret = acpi_get_psd_map(all_cpu_data); + if (ret) { + pr_debug("Error parsing PSD data. Aborting cpufreq registration.\n"); + goto out; + } + + ret = cpufreq_register_driver(&cppc_cpufreq_driver); + if (ret) + goto out; + + return ret; + +out: + for_each_possible_cpu(i) + if (all_cpu_data[i]) + kfree(all_cpu_data[i]); + + kfree(all_cpu_data); + return -ENODEV; +} + +late_initcall(cppc_cpufreq_init); -- cgit v0.10.2 From 56cdc2a05c248c9dfe2480813fc34a4e6d7a77db Mon Sep 17 00:00:00 2001 From: Ashwin Chaugule Date: Wed, 9 Sep 2015 16:27:06 -0400 Subject: ACPI: Add weak routines for ACPI CPU Hotplug Add weak functions for architectures which do not support hot-adding and removing CPUs which aren't detected at bootup. (e.g. via MADT). This helps preserve the Kconfig dependency from: commit cbfc1bae55bb ("[ACPI] ACPI_HOTPLUG_CPU Kconfig dependency update") prevent: HOTPLUG_CPU=y ACPI_PROCESSOR=y ACPI_HOTPLUG_CPU=n Signed-off-by: Ashwin Chaugule Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/acpi_processor.c b/drivers/acpi/acpi_processor.c index 985b8a8..6979186 100644 --- a/drivers/acpi/acpi_processor.c +++ b/drivers/acpi/acpi_processor.c @@ -164,6 +164,24 @@ static int acpi_processor_errata(void) -------------------------------------------------------------------------- */ #ifdef CONFIG_ACPI_HOTPLUG_CPU +int __weak acpi_map_cpu(acpi_handle handle, + phys_cpuid_t physid, int *pcpu) +{ + return -ENODEV; +} + +int __weak acpi_unmap_cpu(int cpu) +{ + return -ENODEV; +} + +int __weak arch_register_cpu(int cpu) +{ + return -ENODEV; +} + +void __weak arch_unregister_cpu(int cpu) {} + static int acpi_processor_hotadd_init(struct acpi_processor *pr) { unsigned long long sta; -- cgit v0.10.2 From 4f2f757351df44c7109a7b93653bed9b8fe10e6b Mon Sep 17 00:00:00 2001 From: Ashwin Chaugule Date: Wed, 9 Sep 2015 16:27:07 -0400 Subject: CPPC: Probe for CPPC tables for each ACPI Processor object For each detected ACPI Processor object (ACPI0007), search its device handle for CPPC specific tables (i.e. _CPC) and extract CPU specific performance capabilities. Signed-off-by: Ashwin Chaugule Reviewed-by: Al Stone Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/processor_driver.c b/drivers/acpi/processor_driver.c index 51e658f..f4e02ae 100644 --- a/drivers/acpi/processor_driver.c +++ b/drivers/acpi/processor_driver.c @@ -242,6 +242,10 @@ static int __acpi_processor_start(struct acpi_device *device) if (pr->flags.need_hotplug_init) return 0; + result = acpi_cppc_processor_probe(pr); + if (result) + return -ENODEV; + if (!cpuidle_get_driver() || cpuidle_get_driver() == &acpi_idle_driver) acpi_processor_power_init(pr); @@ -287,6 +291,8 @@ static int acpi_processor_stop(struct device *dev) acpi_pss_perf_exit(pr, device); + acpi_cppc_processor_exit(pr); + return 0; } diff --git a/include/acpi/processor.h b/include/acpi/processor.h index ff5f135..07fb100 100644 --- a/include/acpi/processor.h +++ b/include/acpi/processor.h @@ -311,6 +311,20 @@ phys_cpuid_t acpi_get_phys_id(acpi_handle, int type, u32 acpi_id); int acpi_map_cpuid(phys_cpuid_t phys_id, u32 acpi_id); int acpi_get_cpuid(acpi_handle, int type, u32 acpi_id); +#ifdef CONFIG_ACPI_CPPC_LIB +extern int acpi_cppc_processor_probe(struct acpi_processor *pr); +extern void acpi_cppc_processor_exit(struct acpi_processor *pr); +#else +static inline int acpi_cppc_processor_probe(struct acpi_processor *pr) +{ + return 0; +} +static inline void acpi_cppc_processor_exit(struct acpi_processor *pr) +{ + return; +} +#endif /* CONFIG_ACPI_CPPC_LIB */ + /* in processor_pdc.c */ void acpi_processor_set_pdc(acpi_handle handle); -- cgit v0.10.2 From ad806ea66c7cdfa10152c58aa710c9c26ca0d159 Mon Sep 17 00:00:00 2001 From: Ashwin Chaugule Date: Wed, 9 Sep 2015 16:27:08 -0400 Subject: ACPI: Allow selection of the ACPI processor driver for ARM64 Now that the ACPI processor driver has been decoupled from the C states and P states functionality, make it selectable on ARM64 so that it can be used by others e.g. CPPC. The C states and P states code is selected only on X86 or IA64 until the relevant support is added on ARM64. Signed-off-by: Ashwin Chaugule Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index 93a04ee..ce97a59 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -213,9 +213,9 @@ config ACPI_CPPC_LIB config ACPI_PROCESSOR tristate "Processor" - depends on X86 || IA64 - select ACPI_PROCESSOR_IDLE - select ACPI_CPU_FREQ_PSS + depends on X86 || IA64 || ARM64 + select ACPI_PROCESSOR_IDLE if X86 || IA64 + select ACPI_CPU_FREQ_PSS if X86 || IA64 default y help This driver adds support for the ACPI Processor package. It is required -- cgit v0.10.2 From f96b3c4f34b294a2293a5aa1d55e12e66aee055d Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Tue, 29 Sep 2015 15:12:37 +0200 Subject: PM / Domains: Remove in_progress counter from struct generic_pm_domain Commit ba2bbfbf6307 ("PM / Domains: Remove intermediate states..") changed the power off sequence (pm_genpd_poweroff()), which from locking point of view means the genpd mutex is held throughout the sequence. The above change means the in_progress counter can't be updated while pm_genpd_poweroff() is executing, which allows us to remove the counter. Instead we inform pm_genpd_poweroff() via a bool parameter, to indicate whether we call it from the scheduled work or from the ->runtime_suspend() callback, since that all that matters. Signed-off-by: Ulf Hansson Reviewed-by: Lina Iyer Signed-off-by: Rafael J. Wysocki diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index cf4950d..c785f2e 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -315,11 +315,12 @@ static int genpd_dev_pm_qos_notifier(struct notifier_block *nb, /** * pm_genpd_poweroff - Remove power from a given PM domain. * @genpd: PM domain to power down. + * @is_async: PM domain is powered down from a scheduled work * * If all of the @genpd's devices have been suspended and all of its subdomains * have been powered down, remove power from @genpd. */ -static int pm_genpd_poweroff(struct generic_pm_domain *genpd) +static int pm_genpd_poweroff(struct generic_pm_domain *genpd, bool is_async) { struct pm_domain_data *pdd; struct gpd_link *link; @@ -351,7 +352,7 @@ static int pm_genpd_poweroff(struct generic_pm_domain *genpd) not_suspended++; } - if (not_suspended > genpd->in_progress) + if (not_suspended > 1 || (not_suspended == 1 && is_async)) return -EBUSY; if (genpd->gov && genpd->gov->power_down_ok) { @@ -399,7 +400,7 @@ static void genpd_power_off_work_fn(struct work_struct *work) genpd = container_of(work, struct generic_pm_domain, power_off_work); mutex_lock(&genpd->lock); - pm_genpd_poweroff(genpd); + pm_genpd_poweroff(genpd, true); mutex_unlock(&genpd->lock); } @@ -445,9 +446,7 @@ static int pm_genpd_runtime_suspend(struct device *dev) return 0; mutex_lock(&genpd->lock); - genpd->in_progress++; - pm_genpd_poweroff(genpd); - genpd->in_progress--; + pm_genpd_poweroff(genpd, false); mutex_unlock(&genpd->lock); return 0; @@ -1462,7 +1461,6 @@ void pm_genpd_init(struct generic_pm_domain *genpd, mutex_init(&genpd->lock); genpd->gov = gov; INIT_WORK(&genpd->power_off_work, genpd_power_off_work_fn); - genpd->in_progress = 0; atomic_set(&genpd->sd_count, 0); genpd->status = is_off ? GPD_STATE_POWER_OFF : GPD_STATE_ACTIVE; genpd->device_count = 0; diff --git a/include/linux/pm_domain.h b/include/linux/pm_domain.h index 384b1d1..0eaa730 100644 --- a/include/linux/pm_domain.h +++ b/include/linux/pm_domain.h @@ -47,7 +47,6 @@ struct generic_pm_domain { struct dev_power_governor *gov; struct work_struct power_off_work; const char *name; - unsigned int in_progress; /* Number of devices being suspended now */ atomic_t sd_count; /* Number of subdomains with power "on" */ enum gpd_status status; /* Current state of the domain */ unsigned int device_count; /* Number of devices */ -- cgit v0.10.2 From 2376692416b73c51d6f3ba5588b8f19cec617d31 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Tue, 6 Oct 2015 14:27:30 +0200 Subject: soc: dove: Let genpd deal with disabling of unused PM domains Genpd tries from a late_initcall to disable unused PM domains, which makes it unnecessary to also manage this from any earlier phase when for example initializing a genpd. Signed-off-by: Ulf Hansson Signed-off-by: Rafael J. Wysocki diff --git a/drivers/soc/dove/pmu.c b/drivers/soc/dove/pmu.c index 052aecf..abd0879 100644 --- a/drivers/soc/dove/pmu.c +++ b/drivers/soc/dove/pmu.c @@ -396,7 +396,6 @@ int __init dove_init_pmu(void) __pmu_domain_register(domain, np); } - pm_genpd_poweroff_unused(); /* Loss of the interrupt controller is not a fatal error. */ parent_irq = irq_of_parse_and_map(pmu->of_node, 0); -- cgit v0.10.2 From bb4b72fc63d4c1c2ccd5e5af95e48b77d6cad80c Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Tue, 6 Oct 2015 14:27:42 +0200 Subject: PM / Domains: Remove pm_genpd_poweroff_unused() API As the last user of the pm_genpd_poweroff_unused() API has moved into relying on genpd to deal with this internally from a late_initcall, let's remove the API. Signed-off-by: Ulf Hansson Signed-off-by: Rafael J. Wysocki diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index c785f2e..dcd451c 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -501,15 +501,15 @@ static int __init pd_ignore_unused_setup(char *__unused) __setup("pd_ignore_unused", pd_ignore_unused_setup); /** - * pm_genpd_poweroff_unused - Power off all PM domains with no devices in use. + * genpd_poweroff_unused - Power off all PM domains with no devices in use. */ -void pm_genpd_poweroff_unused(void) +static int __init genpd_poweroff_unused(void) { struct generic_pm_domain *genpd; if (pd_ignore_unused) { pr_warn("genpd: Not disabling unused power domains\n"); - return; + return 0; } mutex_lock(&gpd_list_lock); @@ -518,11 +518,7 @@ void pm_genpd_poweroff_unused(void) genpd_queue_power_off_work(genpd); mutex_unlock(&gpd_list_lock); -} -static int __init genpd_poweroff_unused(void) -{ - pm_genpd_poweroff_unused(); return 0; } late_initcall(genpd_poweroff_unused); diff --git a/include/linux/pm_domain.h b/include/linux/pm_domain.h index 0eaa730..a736f0f 100644 --- a/include/linux/pm_domain.h +++ b/include/linux/pm_domain.h @@ -125,9 +125,7 @@ extern int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd, struct generic_pm_domain *target); extern void pm_genpd_init(struct generic_pm_domain *genpd, struct dev_power_governor *gov, bool is_off); - extern int pm_genpd_poweron(struct generic_pm_domain *genpd); -extern void pm_genpd_poweroff_unused(void); extern struct dev_power_governor simple_qos_governor; extern struct dev_power_governor pm_domain_always_on_gov; @@ -170,7 +168,6 @@ static inline int pm_genpd_poweron(struct generic_pm_domain *genpd) { return -ENOSYS; } -static inline void pm_genpd_poweroff_unused(void) {} #endif static inline int pm_genpd_add_device(struct generic_pm_domain *genpd, -- cgit v0.10.2 From ea823c7cbffa7bae311d78761866ac4db344c89b Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Tue, 6 Oct 2015 14:27:51 +0200 Subject: PM / Domains: Remove pm_genpd_poweron() API Once genpd could be configured to be built with CONFIG_PM_RUNTIME unset (nowadays CONFIG_PM), the pm_genpd_poweron() API served a purpose, since it allowed users to power on a PM domain. As such configuration no longer is supported, users shall solely rely on using some of the runtime PM APIs to power on a PM domain. Signed-off-by: Ulf Hansson Signed-off-by: Rafael J. Wysocki diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index dcd451c..8724b47 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -192,6 +192,8 @@ static void genpd_queue_power_off_work(struct generic_pm_domain *genpd) queue_work(pm_wq, &genpd->power_off_work); } +static int pm_genpd_poweron(struct generic_pm_domain *genpd); + /** * __pm_genpd_poweron - Restore power to a given PM domain and its masters. * @genpd: PM domain to power up. @@ -245,7 +247,7 @@ static int __pm_genpd_poweron(struct generic_pm_domain *genpd) * pm_genpd_poweron - Restore power to a given PM domain and its masters. * @genpd: PM domain to power up. */ -int pm_genpd_poweron(struct generic_pm_domain *genpd) +static int pm_genpd_poweron(struct generic_pm_domain *genpd) { int ret; diff --git a/include/linux/pm_domain.h b/include/linux/pm_domain.h index a736f0f..f4dd810 100644 --- a/include/linux/pm_domain.h +++ b/include/linux/pm_domain.h @@ -125,7 +125,6 @@ extern int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd, struct generic_pm_domain *target); extern void pm_genpd_init(struct generic_pm_domain *genpd, struct dev_power_governor *gov, bool is_off); -extern int pm_genpd_poweron(struct generic_pm_domain *genpd); extern struct dev_power_governor simple_qos_governor; extern struct dev_power_governor pm_domain_always_on_gov; @@ -164,10 +163,6 @@ static inline void pm_genpd_init(struct generic_pm_domain *genpd, struct dev_power_governor *gov, bool is_off) { } -static inline int pm_genpd_poweron(struct generic_pm_domain *genpd) -{ - return -ENOSYS; -} #endif static inline int pm_genpd_add_device(struct generic_pm_domain *genpd, -- cgit v0.10.2 From c2df86ea924a8548f54ce90e46fdd6c9495119b2 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 30 Sep 2015 02:44:29 +0200 Subject: PM / sleep: Drop pm_request_idle() from pm_generic_complete() The pm_request_idle() in pm_generic_complete() is pointless as it is called with the runtime PM usage counter different from zero (bumped up by the core during the prepare phase of system suspend) and the core calls pm_runtime_put() for all devices after executing their complete callbacks, so drop it. This allows the PCI PM layer to use pm_generic_complete() too. Signed-off-by: Rafael J. Wysocki Reviewed-by: Ulf Hansson diff --git a/drivers/base/power/generic_ops.c b/drivers/base/power/generic_ops.c index 96a92db..b2ed606 100644 --- a/drivers/base/power/generic_ops.c +++ b/drivers/base/power/generic_ops.c @@ -296,11 +296,5 @@ void pm_generic_complete(struct device *dev) if (drv && drv->pm && drv->pm->complete) drv->pm->complete(dev); - - /* - * Let runtime PM try to suspend devices that haven't been in use before - * going into the system-wide sleep state we're resuming from. - */ - pm_request_idle(dev); } #endif /* CONFIG_PM_SLEEP */ diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index 8dfb144..c9ce307 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -686,13 +686,8 @@ static int pci_pm_prepare(struct device *dev) static void pci_pm_complete(struct device *dev) { - struct device_driver *drv = dev->driver; - struct pci_dev *pci_dev = to_pci_dev(dev); - - pci_dev_complete_resume(pci_dev); - - if (drv && drv->pm && drv->pm->complete) - drv->pm->complete(dev); + pci_dev_complete_resume(to_pci_dev(dev)); + pm_generic_complete(dev); } #else /* !CONFIG_PM_SLEEP */ -- cgit v0.10.2 From ef25ba0476015908ef5960f9faac149ddf34ede0 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 7 Oct 2015 00:49:34 +0200 Subject: PM / sleep: Add flags to indicate platform firmware involvement There are quite a few cases in which device drivers, bus types or even the PM core itself may benefit from knowing whether or not the platform firmware will be involved in the upcoming system power transition (during system suspend) or whether or not it was involved in it (during system resume). For this reason, introduce global system suspend flags that can be used by the platform code to expose that information for the benefit of the other parts of the kernel and make the ACPI core set them as appropriate. Users of the new flags will be added later. Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/sleep.c b/drivers/acpi/sleep.c index 2f0d4db..a0b4d781 100644 --- a/drivers/acpi/sleep.c +++ b/drivers/acpi/sleep.c @@ -487,6 +487,8 @@ static int acpi_suspend_begin(suspend_state_t pm_state) pr_err("ACPI does not support sleep state S%u\n", acpi_state); return -ENOSYS; } + if (acpi_state > ACPI_STATE_S1) + pm_set_suspend_via_firmware(); acpi_pm_start(acpi_state); return 0; @@ -522,6 +524,7 @@ static int acpi_suspend_enter(suspend_state_t pm_state) if (error) return error; pr_info(PREFIX "Low-level resume complete\n"); + pm_set_resume_via_firmware(); break; } trace_suspend_resume(TPS("acpi_suspend"), acpi_state, false); diff --git a/include/linux/suspend.h b/include/linux/suspend.h index 33aaf9a..8b6ec7e 100644 --- a/include/linux/suspend.h +++ b/include/linux/suspend.h @@ -202,6 +202,36 @@ struct platform_freeze_ops { extern void suspend_set_ops(const struct platform_suspend_ops *ops); extern int suspend_valid_only_mem(suspend_state_t state); +extern unsigned int pm_suspend_global_flags; + +#define PM_SUSPEND_FLAG_FW_SUSPEND (1 << 0) +#define PM_SUSPEND_FLAG_FW_RESUME (1 << 1) + +static inline void pm_suspend_clear_flags(void) +{ + pm_suspend_global_flags = 0; +} + +static inline void pm_set_suspend_via_firmware(void) +{ + pm_suspend_global_flags |= PM_SUSPEND_FLAG_FW_SUSPEND; +} + +static inline void pm_set_resume_via_firmware(void) +{ + pm_suspend_global_flags |= PM_SUSPEND_FLAG_FW_RESUME; +} + +static inline bool pm_suspend_via_firmware(void) +{ + return !!(pm_suspend_global_flags & PM_SUSPEND_FLAG_FW_SUSPEND); +} + +static inline bool pm_resume_via_firmware(void) +{ + return !!(pm_suspend_global_flags & PM_SUSPEND_FLAG_FW_RESUME); +} + /* Suspend-to-idle state machnine. */ enum freeze_state { FREEZE_STATE_NONE, /* Not suspended/suspending. */ @@ -241,6 +271,12 @@ extern int pm_suspend(suspend_state_t state); #else /* !CONFIG_SUSPEND */ #define suspend_valid_only_mem NULL +static inline void pm_suspend_clear_flags(void) {} +static inline void pm_set_suspend_via_firmware(void) {} +static inline void pm_set_resume_via_firmware(void) {} +static inline bool pm_suspend_via_firmware(void) { return false; } +static inline bool pm_resume_via_firmware(void) { return false; } + static inline void suspend_set_ops(const struct platform_suspend_ops *ops) {} static inline int pm_suspend(suspend_state_t state) { return -ENOSYS; } static inline bool idle_should_freeze(void) { return false; } diff --git a/kernel/power/suspend.c b/kernel/power/suspend.c index 7e4cda4..f9fe133 100644 --- a/kernel/power/suspend.c +++ b/kernel/power/suspend.c @@ -35,6 +35,9 @@ const char *pm_labels[] = { "mem", "standby", "freeze", NULL }; const char *pm_states[PM_SUSPEND_MAX]; +unsigned int pm_suspend_global_flags; +EXPORT_SYMBOL_GPL(pm_suspend_global_flags); + static const struct platform_suspend_ops *suspend_ops; static const struct platform_freeze_ops *freeze_ops; static DECLARE_WAIT_QUEUE_HEAD(suspend_freeze_wait_head); @@ -493,6 +496,7 @@ static int enter_state(suspend_state_t state) #endif pr_debug("PM: Preparing system for sleep (%s)\n", pm_states[state]); + pm_suspend_clear_flags(); error = suspend_prepare(state); if (error) goto Unlock; -- cgit v0.10.2 From 58a1fbbb2ee873dd1fe327e80bc7b08e80866269 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 7 Oct 2015 00:50:24 +0200 Subject: PM / PCI / ACPI: Kick devices that might have been reset by firmware There is a concern that if the platform firmware was involved in the system resume that's being completed, some devices might have been reset by it and if those devices had the power.direct_complete flag set during the preceding suspend transition, they may stay in a reset-power-on state indefinitely (until they are runtime-resumed and then suspended again). That may not be a big deal from the individual device's perspective, but if the system is an SoC, it may be prevented from entering deep SoC-wide low-power states on idle because of that. The devices that are most likely to be affected by this issue are PCI devices and ACPI-enumerated devices using the general ACPI PM domain, so to prevent it from happening for those devices, force a runtime resume for them if they have their power.direct_complete flags set and the platform firmware was involved in the resume transition currently in progress. Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/acpi_lpss.c b/drivers/acpi/acpi_lpss.c index f51bd0d..f9e0d09 100644 --- a/drivers/acpi/acpi_lpss.c +++ b/drivers/acpi/acpi_lpss.c @@ -664,7 +664,7 @@ static struct dev_pm_domain acpi_lpss_pm_domain = { #ifdef CONFIG_PM #ifdef CONFIG_PM_SLEEP .prepare = acpi_subsys_prepare, - .complete = acpi_subsys_complete, + .complete = pm_complete_with_resume_check, .suspend = acpi_subsys_suspend, .suspend_late = acpi_lpss_suspend_late, .resume_early = acpi_lpss_resume_early, diff --git a/drivers/acpi/device_pm.c b/drivers/acpi/device_pm.c index 4806b7f..08a02cd 100644 --- a/drivers/acpi/device_pm.c +++ b/drivers/acpi/device_pm.c @@ -963,23 +963,6 @@ int acpi_subsys_prepare(struct device *dev) EXPORT_SYMBOL_GPL(acpi_subsys_prepare); /** - * acpi_subsys_complete - Finalize device's resume during system resume. - * @dev: Device to handle. - */ -void acpi_subsys_complete(struct device *dev) -{ - pm_generic_complete(dev); - /* - * If the device had been runtime-suspended before the system went into - * the sleep state it is going out of and it has never been resumed till - * now, resume it in case the firmware powered it up. - */ - if (dev->power.direct_complete) - pm_request_resume(dev); -} -EXPORT_SYMBOL_GPL(acpi_subsys_complete); - -/** * acpi_subsys_suspend - Run the device driver's suspend callback. * @dev: Device to handle. * @@ -1047,7 +1030,7 @@ static struct dev_pm_domain acpi_general_pm_domain = { .runtime_resume = acpi_subsys_runtime_resume, #ifdef CONFIG_PM_SLEEP .prepare = acpi_subsys_prepare, - .complete = acpi_subsys_complete, + .complete = pm_complete_with_resume_check, .suspend = acpi_subsys_suspend, .suspend_late = acpi_subsys_suspend_late, .resume_early = acpi_subsys_resume_early, diff --git a/drivers/base/power/generic_ops.c b/drivers/base/power/generic_ops.c index b2ed606..07c3c4a 100644 --- a/drivers/base/power/generic_ops.c +++ b/drivers/base/power/generic_ops.c @@ -9,6 +9,7 @@ #include #include #include +#include #ifdef CONFIG_PM /** @@ -297,4 +298,26 @@ void pm_generic_complete(struct device *dev) if (drv && drv->pm && drv->pm->complete) drv->pm->complete(dev); } + +/** + * pm_complete_with_resume_check - Complete a device power transition. + * @dev: Device to handle. + * + * Complete a device power transition during a system-wide power transition and + * optionally schedule a runtime resume of the device if the system resume in + * progress has been initated by the platform firmware and the device had its + * power.direct_complete flag set. + */ +void pm_complete_with_resume_check(struct device *dev) +{ + pm_generic_complete(dev); + /* + * If the device had been runtime-suspended before the system went into + * the sleep state it is going out of and it has never been resumed till + * now, resume it in case the firmware powered it up. + */ + if (dev->power.direct_complete && pm_resume_via_firmware()) + pm_request_resume(dev); +} +EXPORT_SYMBOL_GPL(pm_complete_with_resume_check); #endif /* CONFIG_PM_SLEEP */ diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index c9ce307..306124b 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -687,7 +687,7 @@ static int pci_pm_prepare(struct device *dev) static void pci_pm_complete(struct device *dev) { pci_dev_complete_resume(to_pci_dev(dev)); - pm_generic_complete(dev); + pm_complete_with_resume_check(dev); } #else /* !CONFIG_PM_SLEEP */ diff --git a/include/linux/pm.h b/include/linux/pm.h index 35d599e..528be67 100644 --- a/include/linux/pm.h +++ b/include/linux/pm.h @@ -732,6 +732,7 @@ extern int pm_generic_poweroff_noirq(struct device *dev); extern int pm_generic_poweroff_late(struct device *dev); extern int pm_generic_poweroff(struct device *dev); extern void pm_generic_complete(struct device *dev); +extern void pm_complete_with_resume_check(struct device *dev); #else /* !CONFIG_PM_SLEEP */ -- cgit v0.10.2 From 1c5dd13459fe7caaeda82f7be878720c978d26f2 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 7 Oct 2015 03:03:57 +0200 Subject: input: i8042: Avoid resetting controller on system suspend/resume If the upcoming system suspend is not going to be handled by the platform firmware, like in the suspend-to-idle case, it is not necessary to reset the controller in i8042_pm_suspend(), so avoid doing that. Moreover, if the system resume currently in progress has not been started by the platform firmware, like in the suspend-to-idle case, i8042_controller_resume() need not be called by i8042_pm_resume(), so avoid doing that too in that case. Additionally, try to catch the event that woke up the system by calling the interrupt handler early during system resume if it has not been started by the platform firmware. Signed-off-by: Rafael J. Wysocki Acked-by: Dmitry Torokhov diff --git a/drivers/input/serio/i8042.c b/drivers/input/serio/i8042.c index db91de5..4541957 100644 --- a/drivers/input/serio/i8042.c +++ b/drivers/input/serio/i8042.c @@ -24,6 +24,7 @@ #include #include #include +#include #include @@ -1170,7 +1171,8 @@ static int i8042_pm_suspend(struct device *dev) { int i; - i8042_controller_reset(true); + if (pm_suspend_via_firmware()) + i8042_controller_reset(true); /* Set up serio interrupts for system wakeup. */ for (i = 0; i < I8042_NUM_PORTS; i++) { @@ -1183,8 +1185,17 @@ static int i8042_pm_suspend(struct device *dev) return 0; } +static int i8042_pm_resume_noirq(struct device *dev) +{ + if (!pm_resume_via_firmware()) + i8042_interrupt(0, NULL); + + return 0; +} + static int i8042_pm_resume(struct device *dev) { + bool force_reset; int i; for (i = 0; i < I8042_NUM_PORTS; i++) { @@ -1195,11 +1206,21 @@ static int i8042_pm_resume(struct device *dev) } /* - * On resume from S2R we always try to reset the controller - * to bring it in a sane state. (In case of S2D we expect - * BIOS to reset the controller for us.) + * If platform firmware was not going to be involved in suspend, we did + * not restore the controller state to whatever it had been at boot + * time, so we do not need to do anything. */ - return i8042_controller_resume(true); + if (!pm_suspend_via_firmware()) + return 0; + + /* + * We only need to reset the controller if we are resuming after handing + * off control to the platform firmware, otherwise we can simply restore + * the mode. + */ + force_reset = pm_resume_via_firmware(); + + return i8042_controller_resume(force_reset); } static int i8042_pm_thaw(struct device *dev) @@ -1223,6 +1244,7 @@ static int i8042_pm_restore(struct device *dev) static const struct dev_pm_ops i8042_pm_ops = { .suspend = i8042_pm_suspend, + .resume_noirq = i8042_pm_resume_noirq, .resume = i8042_pm_resume, .thaw = i8042_pm_thaw, .poweroff = i8042_pm_reset, -- cgit v0.10.2 From e625742f9c6e5d01b5c46efcc2870893735badf3 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Tue, 13 Oct 2015 10:57:13 +0530 Subject: cpufreq: Drop redundant check for inactive policies We just made sure policy->cpu is online and this check will always fail as the policy is active. Drop it. Signed-off-by: Viresh Kumar Acked-by: Saravana Kannan Signed-off-by: Rafael J. Wysocki diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index 25c4c15..8701dc5 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -843,18 +843,11 @@ static ssize_t store(struct kobject *kobj, struct attribute *attr, down_write(&policy->rwsem); - /* Updating inactive policies is invalid, so avoid doing that. */ - if (unlikely(policy_is_inactive(policy))) { - ret = -EBUSY; - goto unlock_policy_rwsem; - } - if (fattr->store) ret = fattr->store(policy, buf, count); else ret = -EIO; -unlock_policy_rwsem: up_write(&policy->rwsem); unlock: put_online_cpus(); -- cgit v0.10.2 From 7420aa4fed8cc7f3f7d7cc89ea1028fddfdacac8 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Tue, 13 Oct 2015 09:58:02 +0200 Subject: PM / Domains: Rename *pm_genpd_poweron|poweroff() This is another step to get consistent names of functions in genpd. Let's rename the internal *pm_genpd_poweron|poweroff() into *genpd_poweron|poweroff(). Signed-off-by: Ulf Hansson Signed-off-by: Rafael J. Wysocki diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index 8724b47..6e1bcde 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -181,10 +181,10 @@ static int genpd_power_off(struct generic_pm_domain *genpd, bool timed) } /** - * genpd_queue_power_off_work - Queue up the execution of pm_genpd_poweroff(). + * genpd_queue_power_off_work - Queue up the execution of genpd_poweroff(). * @genpd: PM domait to power off. * - * Queue up the execution of pm_genpd_poweroff() unless it's already been done + * Queue up the execution of genpd_poweroff() unless it's already been done * before. */ static void genpd_queue_power_off_work(struct generic_pm_domain *genpd) @@ -192,16 +192,16 @@ static void genpd_queue_power_off_work(struct generic_pm_domain *genpd) queue_work(pm_wq, &genpd->power_off_work); } -static int pm_genpd_poweron(struct generic_pm_domain *genpd); +static int genpd_poweron(struct generic_pm_domain *genpd); /** - * __pm_genpd_poweron - Restore power to a given PM domain and its masters. + * __genpd_poweron - Restore power to a given PM domain and its masters. * @genpd: PM domain to power up. * * Restore power to @genpd and all of its masters so that it is possible to * resume a device belonging to it. */ -static int __pm_genpd_poweron(struct generic_pm_domain *genpd) +static int __genpd_poweron(struct generic_pm_domain *genpd) { struct gpd_link *link; int ret = 0; @@ -218,7 +218,7 @@ static int __pm_genpd_poweron(struct generic_pm_domain *genpd) list_for_each_entry(link, &genpd->slave_links, slave_node) { genpd_sd_counter_inc(link->master); - ret = pm_genpd_poweron(link->master); + ret = genpd_poweron(link->master); if (ret) { genpd_sd_counter_dec(link->master); goto err; @@ -244,15 +244,15 @@ static int __pm_genpd_poweron(struct generic_pm_domain *genpd) } /** - * pm_genpd_poweron - Restore power to a given PM domain and its masters. + * genpd_poweron - Restore power to a given PM domain and its masters. * @genpd: PM domain to power up. */ -static int pm_genpd_poweron(struct generic_pm_domain *genpd) +static int genpd_poweron(struct generic_pm_domain *genpd) { int ret; mutex_lock(&genpd->lock); - ret = __pm_genpd_poweron(genpd); + ret = __genpd_poweron(genpd); mutex_unlock(&genpd->lock); return ret; } @@ -315,14 +315,14 @@ static int genpd_dev_pm_qos_notifier(struct notifier_block *nb, } /** - * pm_genpd_poweroff - Remove power from a given PM domain. + * genpd_poweroff - Remove power from a given PM domain. * @genpd: PM domain to power down. * @is_async: PM domain is powered down from a scheduled work * * If all of the @genpd's devices have been suspended and all of its subdomains * have been powered down, remove power from @genpd. */ -static int pm_genpd_poweroff(struct generic_pm_domain *genpd, bool is_async) +static int genpd_poweroff(struct generic_pm_domain *genpd, bool is_async) { struct pm_domain_data *pdd; struct gpd_link *link; @@ -370,10 +370,10 @@ static int pm_genpd_poweroff(struct generic_pm_domain *genpd, bool is_async) /* * If sd_count > 0 at this point, one of the subdomains hasn't - * managed to call pm_genpd_poweron() for the master yet after - * incrementing it. In that case pm_genpd_poweron() will wait + * managed to call genpd_poweron() for the master yet after + * incrementing it. In that case genpd_poweron() will wait * for us to drop the lock, so we can call .power_off() and let - * the pm_genpd_poweron() restore power for us (this shouldn't + * the genpd_poweron() restore power for us (this shouldn't * happen very often). */ ret = genpd_power_off(genpd, true); @@ -402,7 +402,7 @@ static void genpd_power_off_work_fn(struct work_struct *work) genpd = container_of(work, struct generic_pm_domain, power_off_work); mutex_lock(&genpd->lock); - pm_genpd_poweroff(genpd, true); + genpd_poweroff(genpd, true); mutex_unlock(&genpd->lock); } @@ -448,7 +448,7 @@ static int pm_genpd_runtime_suspend(struct device *dev) return 0; mutex_lock(&genpd->lock); - pm_genpd_poweroff(genpd, false); + genpd_poweroff(genpd, false); mutex_unlock(&genpd->lock); return 0; @@ -481,7 +481,7 @@ static int pm_genpd_runtime_resume(struct device *dev) } mutex_lock(&genpd->lock); - ret = __pm_genpd_poweron(genpd); + ret = __genpd_poweron(genpd); mutex_unlock(&genpd->lock); if (ret) @@ -694,7 +694,7 @@ static int pm_genpd_prepare(struct device *dev) /* * The PM domain must be in the GPD_STATE_ACTIVE state at this point, - * so pm_genpd_poweron() will return immediately, but if the device + * so genpd_poweron() will return immediately, but if the device * is suspended (e.g. it's been stopped by genpd_stop_dev()), we need * to make it operational. */ @@ -1793,7 +1793,7 @@ int genpd_dev_pm_attach(struct device *dev) dev->pm_domain->detach = genpd_dev_pm_detach; dev->pm_domain->sync = genpd_dev_pm_sync; - ret = pm_genpd_poweron(pd); + ret = genpd_poweron(pd); out: return ret ? -EPROBE_DEFER : 0; -- cgit v0.10.2 From d439e64f22ce0eea681ae90c71f584d3a0145ded Mon Sep 17 00:00:00 2001 From: Geliang Tang Date: Tue, 29 Sep 2015 20:36:58 -0700 Subject: PM / hibernate: fix a comment typo Just fix a typo in a function name in kerneldoc comments. Signed-off-by: Geliang Tang Acked-by: Pavel Machek Signed-off-by: Rafael J. Wysocki diff --git a/kernel/power/hibernate.c b/kernel/power/hibernate.c index 690f78f..b7342a2 100644 --- a/kernel/power/hibernate.c +++ b/kernel/power/hibernate.c @@ -733,7 +733,7 @@ int hibernate(void) * contents of memory is restored from the saved image. * * If this is successful, control reappears in the restored target kernel in - * hibernation_snaphot() which returns to hibernate(). Otherwise, the routine + * hibernation_snapshot() which returns to hibernate(). Otherwise, the routine * attempts to recover gracefully and make the kernel return to the normal mode * of operation. */ -- cgit v0.10.2 From fa743d96e76c3c74aa379ba828f10119b87ff88c Mon Sep 17 00:00:00 2001 From: Luis de Bethencourt Date: Fri, 2 Oct 2015 15:27:57 +0100 Subject: PM / AVS: rockchip-io: Fix module autoload for OF platform driver This platform driver has a OF device ID table but the OF module alias information is not created so module autoloading won't work. Signed-off-by: Luis de Bethencourt Reviewed-by: Heiko Stuebner Acked-by: Kevin Hilman Signed-off-by: Rafael J. Wysocki diff --git a/drivers/power/avs/rockchip-io-domain.c b/drivers/power/avs/rockchip-io-domain.c index 2e30002..8099456 100644 --- a/drivers/power/avs/rockchip-io-domain.c +++ b/drivers/power/avs/rockchip-io-domain.c @@ -271,6 +271,7 @@ static const struct of_device_id rockchip_iodomain_match[] = { }, { /* sentinel */ }, }; +MODULE_DEVICE_TABLE(of, rockchip_iodomain_match); static int rockchip_iodomain_probe(struct platform_device *pdev) { -- cgit v0.10.2 From 9b3fedde27d3d63055c43c05e8254e252e58ba48 Mon Sep 17 00:00:00 2001 From: Lukasz Anaczkowski Date: Wed, 9 Sep 2015 15:47:28 +0200 Subject: ACPI / tables: Add acpi_subtable_proc to ACPI table parsers ACPI subtable parsing needs to be extended to allow two or more handlers to be run in the same ACPI table walk, thus adding acpi_subtable_proc structure which stores () ACPI table id () handler that processes table () counter how many items has been processed and passing it to acpi_parse_entries_array() and acpi_table_parse_entries_array(). This is needed to fix CPU enumeration when APIC/X2APIC entries are interleaved. Signed-off-by: Lukasz Anaczkowski Reviewed-by: Marc Zyngier Tested-by: Marc Zyngier Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/tables.c b/drivers/acpi/tables.c index 17a6fa0..c1ff58d 100644 --- a/drivers/acpi/tables.c +++ b/drivers/acpi/tables.c @@ -210,20 +210,39 @@ void acpi_table_print_madt_entry(struct acpi_subtable_header *header) } } -int __init -acpi_parse_entries(char *id, unsigned long table_size, - acpi_tbl_entry_handler handler, +/** + * acpi_parse_entries_array - for each proc_num find a suitable subtable + * + * @id: table id (for debugging purposes) + * @table_size: single entry size + * @table_header: where does the table start? + * @proc: array of acpi_subtable_proc struct containing entry id + * and associated handler with it + * @proc_num: how big proc is? + * @max_entries: how many entries can we process? + * + * For each proc_num find a subtable with proc->id and run proc->handler + * on it. Assumption is that there's only single handler for particular + * entry id. + * + * On success returns sum of all matching entries for all proc handlers. + * Otherwise, -ENODEV or -EINVAL is returned. + */ +static int __init +acpi_parse_entries_array(char *id, unsigned long table_size, struct acpi_table_header *table_header, - int entry_id, unsigned int max_entries) + struct acpi_subtable_proc *proc, int proc_num, + unsigned int max_entries) { struct acpi_subtable_header *entry; - int count = 0; unsigned long table_end; + int count = 0; + int i; if (acpi_disabled) return -ENODEV; - if (!id || !handler) + if (!id) return -EINVAL; if (!table_size) @@ -243,20 +262,27 @@ acpi_parse_entries(char *id, unsigned long table_size, while (((unsigned long)entry) + sizeof(struct acpi_subtable_header) < table_end) { - if (entry->type == entry_id - && (!max_entries || count < max_entries)) { - if (handler(entry, table_end)) + if (max_entries && count >= max_entries) + break; + + for (i = 0; i < proc_num; i++) { + if (entry->type != proc[i].id) + continue; + if (!proc->handler || proc[i].handler(entry, table_end)) return -EINVAL; - count++; + proc->count++; + break; } + if (i != proc_num) + count++; /* * If entry->length is 0, break from this loop to avoid * infinite loop. */ if (entry->length == 0) { - pr_err("[%4.4s:0x%02x] Invalid zero length\n", id, entry_id); + pr_err("[%4.4s:0x%02x] Invalid zero length\n", id, proc->id); return -EINVAL; } @@ -266,17 +292,32 @@ acpi_parse_entries(char *id, unsigned long table_size, if (max_entries && count > max_entries) { pr_warn("[%4.4s:0x%02x] ignored %i entries of %i found\n", - id, entry_id, count - max_entries, count); + id, proc->id, count - max_entries, count); } return count; } int __init -acpi_table_parse_entries(char *id, +acpi_parse_entries(char *id, + unsigned long table_size, + acpi_tbl_entry_handler handler, + struct acpi_table_header *table_header, + int entry_id, unsigned int max_entries) +{ + struct acpi_subtable_proc proc = { + .id = entry_id, + .handler = handler, + }; + + return acpi_parse_entries_array(id, table_size, table_header, + &proc, 1, max_entries); +} + +int __init +acpi_table_parse_entries_array(char *id, unsigned long table_size, - int entry_id, - acpi_tbl_entry_handler handler, + struct acpi_subtable_proc *proc, int proc_num, unsigned int max_entries) { struct acpi_table_header *table_header = NULL; @@ -287,7 +328,7 @@ acpi_table_parse_entries(char *id, if (acpi_disabled) return -ENODEV; - if (!id || !handler) + if (!id) return -EINVAL; if (!strncmp(id, ACPI_SIG_MADT, 4)) @@ -299,14 +340,30 @@ acpi_table_parse_entries(char *id, return -ENODEV; } - count = acpi_parse_entries(id, table_size, handler, table_header, - entry_id, max_entries); + count = acpi_parse_entries_array(id, table_size, table_header, + proc, proc_num, max_entries); early_acpi_os_unmap_memory((char *)table_header, tbl_size); return count; } int __init +acpi_table_parse_entries(char *id, + unsigned long table_size, + int entry_id, + acpi_tbl_entry_handler handler, + unsigned int max_entries) +{ + struct acpi_subtable_proc proc = { + .id = entry_id, + .handler = handler, + }; + + return acpi_table_parse_entries_array(id, table_size, &proc, 1, + max_entries); +} + +int __init acpi_table_parse_madt(enum acpi_madt_type id, acpi_tbl_entry_handler handler, unsigned int max_entries) { diff --git a/include/linux/acpi.h b/include/linux/acpi.h index 7235c48..b0299f8 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -131,6 +131,12 @@ static inline void acpi_initrd_override(void *data, size_t size) (!entry) || (unsigned long)entry + sizeof(*entry) > end || \ ((struct acpi_subtable_header *)entry)->length < sizeof(*entry)) +struct acpi_subtable_proc { + int id; + acpi_tbl_entry_handler handler; + int count; +}; + char * __acpi_map_table (unsigned long phys_addr, unsigned long size); void __acpi_unmap_table(char *map, unsigned long size); int early_acpi_boot_init(void); @@ -146,9 +152,16 @@ int __init acpi_parse_entries(char *id, unsigned long table_size, struct acpi_table_header *table_header, int entry_id, unsigned int max_entries); int __init acpi_table_parse_entries(char *id, unsigned long table_size, - int entry_id, - acpi_tbl_entry_handler handler, - unsigned int max_entries); + int entry_id, + acpi_tbl_entry_handler handler, + unsigned int max_entries); +int __init acpi_table_parse_entries(char *id, unsigned long table_size, + int entry_id, + acpi_tbl_entry_handler handler, + unsigned int max_entries); +int __init acpi_table_parse_entries_array(char *id, unsigned long table_size, + struct acpi_subtable_proc *proc, int proc_num, + unsigned int max_entries); int acpi_table_parse_madt(enum acpi_madt_type id, acpi_tbl_entry_handler handler, unsigned int max_entries); -- cgit v0.10.2 From d81056b5278c520f1dede99bd59c14366ac8b1e1 Mon Sep 17 00:00:00 2001 From: Lukasz Anaczkowski Date: Wed, 9 Sep 2015 15:47:29 +0200 Subject: x86, ACPI: Handle apic/x2apic entries in MADT in correct order ACPI specifies the following rules when listing APIC IDs: (1) Boot processor is listed first (2) For multi-threaded processors, BIOS should list the first logical processor of each of the individual multi-threaded processors in MADT before listing any of the second logical processors. (3) APIC IDs < 0xFF should be listed in APIC subtable, APIC IDs >= 0xFF should be listed in X2APIC subtable Because of above, when there's more than 0xFF logical CPUs, BIOS interleaves APIC/X2APIC subtables. Assuming, there's 72 cores, 72 hyper-threads each, 288 CPUs total, listing is like this: APIC (0,4,8, .., 252) X2APIC (258,260,264, .. 284) APIC (1,5,9,...,253) X2APIC (259,261,265,...,285) APIC (2,6,10,...,254) X2APIC (260,262,266,..,286) APIC (3,7,11,...,251) X2APIC (255,261,262,266,..,287) Now, before this patch, due to how ACPI MADT subtables were parsed (BSP then X2APIC then APIC), kernel enumerated CPUs in reverted order (i.e. high APIC IDs were getting low logical IDs, and low APIC IDs were getting high logical IDs). This is wrong for the following reasons: () it's hard to predict how cores and threads are enumerated () when it's hard to predict, s/w threads cannot be properly affinitized causing significant performance impact due to e.g. inproper cache sharing () enumeration is inconsistent with how threads are enumerated on other Intel Xeon processors So, order in which MADT APIC/X2APIC handlers are passed is reverse and both handlers are passed to be called during same MADT table to walk to achieve correct CPU enumeration. In scenario when someone boots kernel with options 'maxcpus=72 nox2apic', in result less cores may be booted, since some of the CPUs the kernel will try to use will have APIC ID >= 0xFF. In such case, one should not pass 'nox2apic'. Disclimer: code parsing MADT APIC/X2APIC has not been touched since 2009, when X2APIC support was initially added. I do not know why MADT parsing code was added in the reversed order in the first place. I guess it didn't matter at that time since nobody cared about cores with APIC IDs >= 0xFF, right? This patch is based on work of "Yinghai Lu " previously published at https://lkml.org/lkml/2013/1/21/563 Here's the explanation why parsing interface needs to be changed and why simpler approach will not work https://lkml.org/lkml/2015/9/7/285 Signed-off-by: Lukasz Anaczkowski Acked-by: Thomas Gleixner (commit message) Signed-off-by: Rafael J. Wysocki diff --git a/arch/x86/kernel/acpi/boot.c b/arch/x86/kernel/acpi/boot.c index ded848c..e759076 100644 --- a/arch/x86/kernel/acpi/boot.c +++ b/arch/x86/kernel/acpi/boot.c @@ -976,6 +976,8 @@ static int __init acpi_parse_madt_lapic_entries(void) { int count; int x2count = 0; + int ret; + struct acpi_subtable_proc madt_proc[2]; if (!cpu_has_apic) return -ENODEV; @@ -999,10 +1001,22 @@ static int __init acpi_parse_madt_lapic_entries(void) acpi_parse_sapic, MAX_LOCAL_APIC); if (!count) { - x2count = acpi_table_parse_madt(ACPI_MADT_TYPE_LOCAL_X2APIC, - acpi_parse_x2apic, MAX_LOCAL_APIC); - count = acpi_table_parse_madt(ACPI_MADT_TYPE_LOCAL_APIC, - acpi_parse_lapic, MAX_LOCAL_APIC); + memset(madt_proc, 0, sizeof(madt_proc)); + madt_proc[0].id = ACPI_MADT_TYPE_LOCAL_APIC; + madt_proc[0].handler = acpi_parse_lapic; + madt_proc[1].id = ACPI_MADT_TYPE_LOCAL_X2APIC; + madt_proc[1].handler = acpi_parse_x2apic; + ret = acpi_table_parse_entries_array(ACPI_SIG_MADT, + sizeof(struct acpi_table_madt), + madt_proc, ARRAY_SIZE(madt_proc), MAX_LOCAL_APIC); + if (ret < 0) { + printk(KERN_ERR PREFIX + "Error parsing LAPIC/X2APIC entries\n"); + return ret; + } + + x2count = madt_proc[0].count; + count = madt_proc[1].count; } if (!count && !x2count) { printk(KERN_ERR PREFIX "No LAPIC entries present\n"); -- cgit v0.10.2 From 362414d9d2824ee0f4910a73e880860a90e75c3c Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 22 Sep 2015 15:29:25 +0300 Subject: ACPI / tables: test the correct variable The intent was to test "proc[i].handler" instead of "proc->handler". Signed-off-by: Dan Carpenter Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/tables.c b/drivers/acpi/tables.c index c1ff58d..6c0f079 100644 --- a/drivers/acpi/tables.c +++ b/drivers/acpi/tables.c @@ -268,7 +268,8 @@ acpi_parse_entries_array(char *id, unsigned long table_size, for (i = 0; i < proc_num; i++) { if (entry->type != proc[i].id) continue; - if (!proc->handler || proc[i].handler(entry, table_end)) + if (!proc[i].handler || + proc[i].handler(entry, table_end)) return -EINVAL; proc->count++; -- cgit v0.10.2 From 6a35fc2d6c22bafe45117cdc5d8cee332244edbb Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Wed, 14 Oct 2015 16:11:59 -0700 Subject: cpufreq: intel_pstate: get P1 from TAR when available After Ivybridge, the max non turbo ratio obtained from platform info msr is not always guaranteed P1 on client platforms. The max non turbo activation ratio (TAR), determines the max for the current level of TDP. The ratio in platform info is physical max. The TAR MSR can be locked, so updating this value is not possible on all platforms. This change gets this ratio from MSR TURBO_ACTIVATION_RATIO if available, but also do some sanity checking to make sure that this value is correct. The sanity check involves reading the TDP ratio for the current tdp control value when platform has configurable TDP present and matching TAC with this. Signed-off-by: Srinivas Pandruvada Acked-by: Kristen Carlson Accardi Signed-off-by: Rafael J. Wysocki diff --git a/arch/x86/include/asm/msr-index.h b/arch/x86/include/asm/msr-index.h index b8c14bb..9f39056 100644 --- a/arch/x86/include/asm/msr-index.h +++ b/arch/x86/include/asm/msr-index.h @@ -206,6 +206,13 @@ #define MSR_GFX_PERF_LIMIT_REASONS 0x000006B0 #define MSR_RING_PERF_LIMIT_REASONS 0x000006B1 +/* Config TDP MSRs */ +#define MSR_CONFIG_TDP_NOMINAL 0x00000648 +#define MSR_CONFIG_TDP_LEVEL1 0x00000649 +#define MSR_CONFIG_TDP_LEVEL2 0x0000064A +#define MSR_CONFIG_TDP_CONTROL 0x0000064B +#define MSR_TURBO_ACTIVATION_RATIO 0x0000064C + /* Hardware P state interface */ #define MSR_PPERF 0x0000064e #define MSR_PERF_LIMIT_REASONS 0x0000064f diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c index 3af9dd7..da92d02 100644 --- a/drivers/cpufreq/intel_pstate.c +++ b/drivers/cpufreq/intel_pstate.c @@ -43,7 +43,6 @@ #define int_tofp(X) ((int64_t)(X) << FRAC_BITS) #define fp_toint(X) ((X) >> FRAC_BITS) - static inline int32_t mul_fp(int32_t x, int32_t y) { return ((int64_t)x * (int64_t)y) >> FRAC_BITS; @@ -593,10 +592,42 @@ static int core_get_min_pstate(void) static int core_get_max_pstate(void) { - u64 value; + u64 tar; + u64 plat_info; + int max_pstate; + int err; + + rdmsrl(MSR_PLATFORM_INFO, plat_info); + max_pstate = (plat_info >> 8) & 0xFF; + + err = rdmsrl_safe(MSR_TURBO_ACTIVATION_RATIO, &tar); + if (!err) { + /* Do some sanity checking for safety */ + if (plat_info & 0x600000000) { + u64 tdp_ctrl; + u64 tdp_ratio; + int tdp_msr; + + err = rdmsrl_safe(MSR_CONFIG_TDP_CONTROL, &tdp_ctrl); + if (err) + goto skip_tar; + + tdp_msr = MSR_CONFIG_TDP_NOMINAL + tdp_ctrl; + err = rdmsrl_safe(tdp_msr, &tdp_ratio); + if (err) + goto skip_tar; + + if (tdp_ratio - 1 == tar) { + max_pstate = tar; + pr_debug("max_pstate=TAC %x\n", max_pstate); + } else { + goto skip_tar; + } + } + } - rdmsrl(MSR_PLATFORM_INFO, value); - return (value >> 8) & 0xFF; +skip_tar: + return max_pstate; } static int core_get_turbo_pstate(void) -- cgit v0.10.2 From 3bcc6fa971c06151d6bf90cb0dc80807f71b93f6 Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Wed, 14 Oct 2015 16:12:00 -0700 Subject: cpufreq: intel-pstate: Use separate max pstate for scaling Systems with configurable TDP have multiple max non turbo p state. Intel P state uses max non turbo P state for scaling. But using the real max non turbo p state causes underestimation of next P state. So using the physical max non turbo P state as before for scaling. Signed-off-by: Srinivas Pandruvada Acked-by: Kristen Carlson Accardi Signed-off-by: Rafael J. Wysocki diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c index da92d02..1369afd 100644 --- a/drivers/cpufreq/intel_pstate.c +++ b/drivers/cpufreq/intel_pstate.c @@ -77,6 +77,7 @@ struct pstate_data { int current_pstate; int min_pstate; int max_pstate; + int max_pstate_physical; int scaling; int turbo_pstate; }; @@ -126,6 +127,7 @@ struct pstate_adjust_policy { struct pstate_funcs { int (*get_max)(void); + int (*get_max_physical)(void); int (*get_min)(void); int (*get_turbo)(void); int (*get_scaling)(void); @@ -590,6 +592,14 @@ static int core_get_min_pstate(void) return (value >> 40) & 0xFF; } +static int core_get_max_pstate_physical(void) +{ + u64 value; + + rdmsrl(MSR_PLATFORM_INFO, value); + return (value >> 8) & 0xFF; +} + static int core_get_max_pstate(void) { u64 tar; @@ -683,6 +693,7 @@ static struct cpu_defaults core_params = { }, .funcs = { .get_max = core_get_max_pstate, + .get_max_physical = core_get_max_pstate_physical, .get_min = core_get_min_pstate, .get_turbo = core_get_turbo_pstate, .get_scaling = core_get_scaling, @@ -701,6 +712,7 @@ static struct cpu_defaults byt_params = { }, .funcs = { .get_max = byt_get_max_pstate, + .get_max_physical = byt_get_max_pstate, .get_min = byt_get_min_pstate, .get_turbo = byt_get_turbo_pstate, .set = byt_set_pstate, @@ -720,6 +732,7 @@ static struct cpu_defaults knl_params = { }, .funcs = { .get_max = core_get_max_pstate, + .get_max_physical = core_get_max_pstate_physical, .get_min = core_get_min_pstate, .get_turbo = knl_get_turbo_pstate, .get_scaling = core_get_scaling, @@ -774,6 +787,7 @@ static void intel_pstate_get_cpu_pstates(struct cpudata *cpu) { cpu->pstate.min_pstate = pstate_funcs.get_min(); cpu->pstate.max_pstate = pstate_funcs.get_max(); + cpu->pstate.max_pstate_physical = pstate_funcs.get_max_physical(); cpu->pstate.turbo_pstate = pstate_funcs.get_turbo(); cpu->pstate.scaling = pstate_funcs.get_scaling(); @@ -792,7 +806,8 @@ static inline void intel_pstate_calc_busy(struct cpudata *cpu) sample->freq = fp_toint( mul_fp(int_tofp( - cpu->pstate.max_pstate * cpu->pstate.scaling / 100), + cpu->pstate.max_pstate_physical * + cpu->pstate.scaling / 100), core_pct)); sample->core_pct_busy = (int32_t)core_pct; @@ -860,7 +875,7 @@ static inline int32_t intel_pstate_get_scaled_busy(struct cpudata *cpu) * specified pstate. */ core_busy = cpu->sample.core_pct_busy; - max_pstate = int_tofp(cpu->pstate.max_pstate); + max_pstate = int_tofp(cpu->pstate.max_pstate_physical); current_pstate = int_tofp(cpu->pstate.current_pstate); core_busy = mul_fp(core_busy, div_fp(max_pstate, current_pstate)); @@ -1144,6 +1159,7 @@ static void copy_pid_params(struct pstate_adjust_policy *policy) static void copy_cpu_funcs(struct pstate_funcs *funcs) { pstate_funcs.get_max = funcs->get_max; + pstate_funcs.get_max_physical = funcs->get_max_physical; pstate_funcs.get_min = funcs->get_min; pstate_funcs.get_turbo = funcs->get_turbo; pstate_funcs.get_scaling = funcs->get_scaling; -- cgit v0.10.2 From 37afb00032424d684a48d649fcfb8b5e4f17c409 Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Wed, 14 Oct 2015 16:12:01 -0700 Subject: cpufreq: intel_pstate: Use ACPI perf configuration Use ACPI _PSS to limit the Intel P State turbo, max and min ratios. This driver uses acpi processor perf lib calls to register performance. The following logic is used to adjust Intel P state driver limits: - If there is no turbo entry in _PSS, then disable Intel P state turbo and limit to non turbo max - If the non turbo max ratio is more than _PSS max non turbo value, then set the max non turbo ratio to _PSS non turbo max - If the min ratio is less than _PSS min then change the min ratio matching _PSS min - Scale the _PSS turbo frequency to max turbo frequency based on control value. This feature can be disabled by using kernel parameters: intel_pstate=no_acpi Signed-off-by: Srinivas Pandruvada Acked-by: Kristen Carlson Accardi Signed-off-by: Rafael J. Wysocki diff --git a/drivers/cpufreq/Kconfig.x86 b/drivers/cpufreq/Kconfig.x86 index c59bdcb..adbd1de 100644 --- a/drivers/cpufreq/Kconfig.x86 +++ b/drivers/cpufreq/Kconfig.x86 @@ -5,6 +5,7 @@ config X86_INTEL_PSTATE bool "Intel P state control" depends on X86 + select ACPI_PROCESSOR if ACPI help This driver provides a P state for Intel core processors. The driver implements an internal governor and will become diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c index 1369afd..041cb41 100644 --- a/drivers/cpufreq/intel_pstate.c +++ b/drivers/cpufreq/intel_pstate.c @@ -34,6 +34,10 @@ #include #include +#if IS_ENABLED(CONFIG_ACPI) +#include +#endif + #define BYT_RATIOS 0x66a #define BYT_VIDS 0x66b #define BYT_TURBO_RATIOS 0x66c @@ -113,6 +117,9 @@ struct cpudata { u64 prev_mperf; u64 prev_tsc; struct sample sample; +#if IS_ENABLED(CONFIG_ACPI) + struct acpi_processor_performance acpi_perf_data; +#endif }; static struct cpudata **all_cpu_data; @@ -143,6 +150,7 @@ struct cpu_defaults { static struct pstate_adjust_policy pid_params; static struct pstate_funcs pstate_funcs; static int hwp_active; +static int no_acpi_perf; struct perf_limits { int no_turbo; @@ -170,6 +178,153 @@ static struct perf_limits limits = { .min_sysfs_pct = 0, }; +#if IS_ENABLED(CONFIG_ACPI) +/* + * The max target pstate ratio is a 8 bit value in both PLATFORM_INFO MSR and + * in TURBO_RATIO_LIMIT MSR, which pstate driver stores in max_pstate and + * max_turbo_pstate fields. The PERF_CTL MSR contains 16 bit value for P state + * ratio, out of it only high 8 bits are used. For example 0x1700 is setting + * target ratio 0x17. The _PSS control value stores in a format which can be + * directly written to PERF_CTL MSR. But in intel_pstate driver this shift + * occurs during write to PERF_CTL (E.g. for cores core_set_pstate()). + * This function converts the _PSS control value to intel pstate driver format + * for comparison and assignment. + */ +static int convert_to_native_pstate_format(struct cpudata *cpu, int index) +{ + return cpu->acpi_perf_data.states[index].control >> 8; +} + +static int intel_pstate_init_perf_limits(struct cpufreq_policy *policy) +{ + struct cpudata *cpu; + int ret; + bool turbo_absent = false; + int max_pstate_index; + int min_pss_ctl, max_pss_ctl, turbo_pss_ctl; + int i; + + cpu = all_cpu_data[policy->cpu]; + + pr_debug("intel_pstate: default limits 0x%x 0x%x 0x%x\n", + cpu->pstate.min_pstate, cpu->pstate.max_pstate, + cpu->pstate.turbo_pstate); + + if (!cpu->acpi_perf_data.shared_cpu_map && + zalloc_cpumask_var_node(&cpu->acpi_perf_data.shared_cpu_map, + GFP_KERNEL, cpu_to_node(policy->cpu))) { + return -ENOMEM; + } + + ret = acpi_processor_register_performance(&cpu->acpi_perf_data, + policy->cpu); + if (ret) + return ret; + + /* + * Check if the control value in _PSS is for PERF_CTL MSR, which should + * guarantee that the states returned by it map to the states in our + * list directly. + */ + if (cpu->acpi_perf_data.control_register.space_id != + ACPI_ADR_SPACE_FIXED_HARDWARE) + return -EIO; + + pr_debug("intel_pstate: CPU%u - ACPI _PSS perf data\n", policy->cpu); + for (i = 0; i < cpu->acpi_perf_data.state_count; i++) + pr_debug(" %cP%d: %u MHz, %u mW, 0x%x\n", + (i == cpu->acpi_perf_data.state ? '*' : ' '), i, + (u32) cpu->acpi_perf_data.states[i].core_frequency, + (u32) cpu->acpi_perf_data.states[i].power, + (u32) cpu->acpi_perf_data.states[i].control); + + /* + * If there is only one entry _PSS, simply ignore _PSS and continue as + * usual without taking _PSS into account + */ + if (cpu->acpi_perf_data.state_count < 2) + return 0; + + turbo_pss_ctl = convert_to_native_pstate_format(cpu, 0); + min_pss_ctl = convert_to_native_pstate_format(cpu, + cpu->acpi_perf_data.state_count - 1); + /* Check if there is a turbo freq in _PSS */ + if (turbo_pss_ctl <= cpu->pstate.max_pstate && + turbo_pss_ctl > cpu->pstate.min_pstate) { + pr_debug("intel_pstate: no turbo range exists in _PSS\n"); + limits.no_turbo = limits.turbo_disabled = 1; + cpu->pstate.turbo_pstate = cpu->pstate.max_pstate; + turbo_absent = true; + } + + /* Check if the max non turbo p state < Intel P state max */ + max_pstate_index = turbo_absent ? 0 : 1; + max_pss_ctl = convert_to_native_pstate_format(cpu, max_pstate_index); + if (max_pss_ctl < cpu->pstate.max_pstate && + max_pss_ctl > cpu->pstate.min_pstate) + cpu->pstate.max_pstate = max_pss_ctl; + + /* check If min perf > Intel P State min */ + if (min_pss_ctl > cpu->pstate.min_pstate && + min_pss_ctl < cpu->pstate.max_pstate) { + cpu->pstate.min_pstate = min_pss_ctl; + policy->cpuinfo.min_freq = min_pss_ctl * cpu->pstate.scaling; + } + + if (turbo_absent) + policy->cpuinfo.max_freq = cpu->pstate.max_pstate * + cpu->pstate.scaling; + else { + policy->cpuinfo.max_freq = cpu->pstate.turbo_pstate * + cpu->pstate.scaling; + /* + * The _PSS table doesn't contain whole turbo frequency range. + * This just contains +1 MHZ above the max non turbo frequency, + * with control value corresponding to max turbo ratio. But + * when cpufreq set policy is called, it will call with this + * max frequency, which will cause a reduced performance as + * this driver uses real max turbo frequency as the max + * frequeny. So correct this frequency in _PSS table to + * correct max turbo frequency based on the turbo ratio. + * Also need to convert to MHz as _PSS freq is in MHz. + */ + cpu->acpi_perf_data.states[0].core_frequency = + turbo_pss_ctl * 100; + } + + pr_debug("intel_pstate: Updated limits using _PSS 0x%x 0x%x 0x%x\n", + cpu->pstate.min_pstate, cpu->pstate.max_pstate, + cpu->pstate.turbo_pstate); + pr_debug("intel_pstate: policy max_freq=%d Khz min_freq = %d KHz\n", + policy->cpuinfo.max_freq, policy->cpuinfo.min_freq); + + return 0; +} + +static int intel_pstate_exit_perf_limits(struct cpufreq_policy *policy) +{ + struct cpudata *cpu; + + if (!no_acpi_perf) + return 0; + + cpu = all_cpu_data[policy->cpu]; + acpi_processor_unregister_performance(policy->cpu); + return 0; +} + +#else +static int intel_pstate_init_perf_limits(struct cpufreq_policy *policy) +{ + return 0; +} + +static int intel_pstate_exit_perf_limits(struct cpufreq_policy *policy) +{ + return 0; +} +#endif + static inline void pid_reset(struct _pid *pid, int setpoint, int busy, int deadband, int integral) { pid->setpoint = setpoint; @@ -1115,18 +1270,30 @@ static int intel_pstate_cpu_init(struct cpufreq_policy *policy) policy->cpuinfo.min_freq = cpu->pstate.min_pstate * cpu->pstate.scaling; policy->cpuinfo.max_freq = cpu->pstate.turbo_pstate * cpu->pstate.scaling; + if (!no_acpi_perf) + intel_pstate_init_perf_limits(policy); + /* + * If there is no acpi perf data or error, we ignore and use Intel P + * state calculated limits, So this is not fatal error. + */ policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL; cpumask_set_cpu(policy->cpu, policy->cpus); return 0; } +static int intel_pstate_cpu_exit(struct cpufreq_policy *policy) +{ + return intel_pstate_exit_perf_limits(policy); +} + static struct cpufreq_driver intel_pstate_driver = { .flags = CPUFREQ_CONST_LOOPS, .verify = intel_pstate_verify_policy, .setpolicy = intel_pstate_set_policy, .get = intel_pstate_get, .init = intel_pstate_cpu_init, + .exit = intel_pstate_cpu_exit, .stop_cpu = intel_pstate_stop_cpu, .name = "intel_pstate", }; @@ -1168,7 +1335,6 @@ static void copy_cpu_funcs(struct pstate_funcs *funcs) } #if IS_ENABLED(CONFIG_ACPI) -#include static bool intel_pstate_no_acpi_pss(void) { @@ -1360,6 +1526,9 @@ static int __init intel_pstate_setup(char *str) force_load = 1; if (!strcmp(str, "hwp_only")) hwp_only = 1; + if (!strcmp(str, "no_acpi")) + no_acpi_perf = 1; + return 0; } early_param("intel_pstate", intel_pstate_setup); -- cgit v0.10.2 From 053f56def57bfaef14c97d268ef6bc4ebe952720 Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Wed, 14 Oct 2015 16:12:02 -0700 Subject: Documentation: kernel_parameters for Intel P state driver Added new option "no_acpi" for not using ACPI processor performance control objects in Intel P state driver. Signed-off-by: Srinivas Pandruvada Acked-by: Kristen Carlson Accardi Signed-off-by: Rafael J. Wysocki diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 22a4b68..9b75e2a 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -1546,6 +1546,9 @@ bytes respectively. Such letter suffixes can also be entirely omitted. hwp_only Only load intel_pstate on systems which support hardware P state control (HWP) if available. + no_acpi + Don't use ACPI processor performance control objects + _PSS and _PPC specified limits. intremap= [X86-64, Intel-IOMMU] on enable Interrupt Remapping (default) -- cgit v0.10.2 From 4ef45148701917fbc08a7c05bc6a3bb0c0573047 Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Wed, 14 Oct 2015 16:12:03 -0700 Subject: cpufreq: intel_pstate: Avoid calculation for max/min When requested from cpufreq to set policy, look into _pss and get control values, instead of using max/min perf calculations. These calculation misses next control state in boundary conditions. Signed-off-by: Srinivas Pandruvada Acked-by: Kristen Carlson Accardi Signed-off-by: Rafael J. Wysocki diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c index 041cb41..c568226 100644 --- a/drivers/cpufreq/intel_pstate.c +++ b/drivers/cpufreq/intel_pstate.c @@ -163,6 +163,8 @@ struct perf_limits { int max_sysfs_pct; int min_policy_pct; int min_sysfs_pct; + int max_perf_ctl; + int min_perf_ctl; }; static struct perf_limits limits = { @@ -176,6 +178,8 @@ static struct perf_limits limits = { .max_sysfs_pct = 100, .min_policy_pct = 0, .min_sysfs_pct = 0, + .max_perf_ctl = 0, + .min_perf_ctl = 0, }; #if IS_ENABLED(CONFIG_ACPI) @@ -909,12 +913,23 @@ static void intel_pstate_get_min_max(struct cpudata *cpu, int *min, int *max) * policy, or by cpu specific default values determined through * experimentation. */ - max_perf_adj = fp_toint(mul_fp(int_tofp(max_perf), limits.max_perf)); - *max = clamp_t(int, max_perf_adj, - cpu->pstate.min_pstate, cpu->pstate.turbo_pstate); + if (limits.max_perf_ctl && limits.max_sysfs_pct >= + limits.max_policy_pct) { + *max = limits.max_perf_ctl; + } else { + max_perf_adj = fp_toint(mul_fp(int_tofp(max_perf), + limits.max_perf)); + *max = clamp_t(int, max_perf_adj, cpu->pstate.min_pstate, + cpu->pstate.turbo_pstate); + } - min_perf = fp_toint(mul_fp(int_tofp(max_perf), limits.min_perf)); - *min = clamp_t(int, min_perf, cpu->pstate.min_pstate, max_perf); + if (limits.min_perf_ctl) { + *min = limits.min_perf_ctl; + } else { + min_perf = fp_toint(mul_fp(int_tofp(max_perf), + limits.min_perf)); + *min = clamp_t(int, min_perf, cpu->pstate.min_pstate, max_perf); + } } static void intel_pstate_set_pstate(struct cpudata *cpu, int pstate, bool force) @@ -1184,6 +1199,12 @@ static unsigned int intel_pstate_get(unsigned int cpu_num) static int intel_pstate_set_policy(struct cpufreq_policy *policy) { +#if IS_ENABLED(CONFIG_ACPI) + struct cpudata *cpu; + int i; +#endif + pr_debug("intel_pstate: %s max %u policy->max %u\n", __func__, + policy->cpuinfo.max_freq, policy->max); if (!policy->cpuinfo.max_freq) return -ENODEV; @@ -1196,6 +1217,8 @@ static int intel_pstate_set_policy(struct cpufreq_policy *policy) limits.max_perf_pct = 100; limits.max_perf = int_tofp(1); limits.no_turbo = 0; + limits.max_perf_ctl = 0; + limits.min_perf_ctl = 0; return 0; } @@ -1216,6 +1239,23 @@ static int intel_pstate_set_policy(struct cpufreq_policy *policy) limits.min_perf = div_fp(int_tofp(limits.min_perf_pct), int_tofp(100)); limits.max_perf = div_fp(int_tofp(limits.max_perf_pct), int_tofp(100)); +#if IS_ENABLED(CONFIG_ACPI) + cpu = all_cpu_data[policy->cpu]; + for (i = 0; i < cpu->acpi_perf_data.state_count; i++) { + int control; + + control = convert_to_native_pstate_format(cpu, i); + if (control * cpu->pstate.scaling == policy->max) + limits.max_perf_ctl = control; + if (control * cpu->pstate.scaling == policy->min) + limits.min_perf_ctl = control; + } + + pr_debug("intel_pstate: max %u policy_max %u perf_ctl [0x%x-0x%x]\n", + policy->cpuinfo.max_freq, policy->max, limits.min_perf_ctl, + limits.max_perf_ctl); +#endif + if (hwp_active) intel_pstate_hwp_set(); -- cgit v0.10.2 From 91236ecc74a25431138f71b6d52e130cd0f774b3 Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Wed, 14 Oct 2015 14:29:36 +0800 Subject: ACPI/PCI: Enhance ACPI core to support sparse IO space Enhance ACPI resource parsing interfaces to support sparse IO space, which will be used to share common code between x86 and IA64 later. Tested-by: Tony Luck Signed-off-by: Jiang Liu Reviewed-by: Hanjun Guo Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/resource.c b/drivers/acpi/resource.c index 15d22db..cdc5c25 100644 --- a/drivers/acpi/resource.c +++ b/drivers/acpi/resource.c @@ -119,7 +119,7 @@ bool acpi_dev_resource_memory(struct acpi_resource *ares, struct resource *res) EXPORT_SYMBOL_GPL(acpi_dev_resource_memory); static void acpi_dev_ioresource_flags(struct resource *res, u64 len, - u8 io_decode) + u8 io_decode, u8 translation_type) { res->flags = IORESOURCE_IO; @@ -131,6 +131,8 @@ static void acpi_dev_ioresource_flags(struct resource *res, u64 len, if (io_decode == ACPI_DECODE_16) res->flags |= IORESOURCE_IO_16BIT_ADDR; + if (translation_type == ACPI_SPARSE_TRANSLATION) + res->flags |= IORESOURCE_IO_SPARSE; } static void acpi_dev_get_ioresource(struct resource *res, u64 start, u64 len, @@ -138,7 +140,7 @@ static void acpi_dev_get_ioresource(struct resource *res, u64 start, u64 len, { res->start = start; res->end = start + len - 1; - acpi_dev_ioresource_flags(res, len, io_decode); + acpi_dev_ioresource_flags(res, len, io_decode, 0); } /** @@ -231,7 +233,8 @@ static bool acpi_decode_space(struct resource_win *win, acpi_dev_memresource_flags(res, len, wp); break; case ACPI_IO_RANGE: - acpi_dev_ioresource_flags(res, len, iodec); + acpi_dev_ioresource_flags(res, len, iodec, + addr->info.io.translation_type); break; case ACPI_BUS_NUMBER_RANGE: res->flags = IORESOURCE_BUS; diff --git a/include/linux/ioport.h b/include/linux/ioport.h index 388e3ae..24bea08 100644 --- a/include/linux/ioport.h +++ b/include/linux/ioport.h @@ -94,6 +94,7 @@ struct resource { /* PnP I/O specific bits (IORESOURCE_BITS) */ #define IORESOURCE_IO_16BIT_ADDR (1<<0) #define IORESOURCE_IO_FIXED (1<<1) +#define IORESOURCE_IO_SPARSE (1<<2) /* PCI ROM control bits (IORESOURCE_BITS) */ #define IORESOURCE_ROM_ENABLE (1<<0) /* ROM is enabled, same as PCI_ROM_ADDRESS_ENABLE */ -- cgit v0.10.2 From 3772aea7d6f36cfa1dae17f04ffed64b4d747aab Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Wed, 14 Oct 2015 14:29:37 +0800 Subject: ia64/PCI/ACPI: Use common ACPI resource parsing interface for host bridge Use common ACPI resource parsing interface to parse ACPI resources for PCI host bridge, so we could share more code between IA64 and x86. Later we will consolidate arch specific implementations into ACPI core. Tested-by: Tony Luck Signed-off-by: Jiang Liu Reviewed-by: Hanjun Guo Signed-off-by: Rafael J. Wysocki diff --git a/arch/ia64/pci/pci.c b/arch/ia64/pci/pci.c index 7cc3be9..d20db9e 100644 --- a/arch/ia64/pci/pci.c +++ b/arch/ia64/pci/pci.c @@ -115,29 +115,12 @@ struct pci_ops pci_root_ops = { .write = pci_write, }; -/* Called by ACPI when it finds a new root bus. */ - -static struct pci_controller *alloc_pci_controller(int seg) -{ - struct pci_controller *controller; - - controller = kzalloc(sizeof(*controller), GFP_KERNEL); - if (!controller) - return NULL; - - controller->segment = seg; - return controller; -} - struct pci_root_info { + struct pci_controller controller; struct acpi_device *bridge; - struct pci_controller *controller; struct list_head resources; - struct resource *res; - resource_size_t *res_offset; - unsigned int res_num; struct list_head io_resources; - char *name; + char name[16]; }; static unsigned int @@ -168,11 +151,11 @@ new_space (u64 phys_base, int sparse) return i; } -static u64 add_io_space(struct pci_root_info *info, - struct acpi_resource_address64 *addr) +static int add_io_space(struct device *dev, struct pci_root_info *info, + struct resource_entry *entry) { struct iospace_resource *iospace; - struct resource *resource; + struct resource *resource, *res = entry->res; char *name; unsigned long base, min, max, base_port; unsigned int sparse = 0, space_nr, len; @@ -180,27 +163,24 @@ static u64 add_io_space(struct pci_root_info *info, len = strlen(info->name) + 32; iospace = kzalloc(sizeof(*iospace) + len, GFP_KERNEL); if (!iospace) { - dev_err(&info->bridge->dev, - "PCI: No memory for %s I/O port space\n", - info->name); - goto out; + dev_err(dev, "PCI: No memory for %s I/O port space\n", + info->name); + return -ENOMEM; } - name = (char *)(iospace + 1); - - min = addr->address.minimum; - max = min + addr->address.address_length - 1; - if (addr->info.io.translation_type == ACPI_SPARSE_TRANSLATION) + if (res->flags & IORESOURCE_IO_SPARSE) sparse = 1; - - space_nr = new_space(addr->address.translation_offset, sparse); + space_nr = new_space(entry->offset, sparse); if (space_nr == ~0) goto free_resource; + name = (char *)(iospace + 1); + min = res->start - entry->offset; + max = res->end - entry->offset; base = __pa(io_space[space_nr].mmio_base); base_port = IO_SPACE_BASE(space_nr); snprintf(name, len, "%s I/O Ports %08lx-%08lx", info->name, - base_port + min, base_port + max); + base_port + min, base_port + max); /* * The SDM guarantees the legacy 0-64K space is sparse, but if the @@ -216,153 +196,195 @@ static u64 add_io_space(struct pci_root_info *info, resource->start = base + (sparse ? IO_SPACE_SPARSE_ENCODING(min) : min); resource->end = base + (sparse ? IO_SPACE_SPARSE_ENCODING(max) : max); if (insert_resource(&iomem_resource, resource)) { - dev_err(&info->bridge->dev, - "can't allocate host bridge io space resource %pR\n", - resource); + dev_err(dev, + "can't allocate host bridge io space resource %pR\n", + resource); goto free_resource; } + entry->offset = base_port; + res->start = min + base_port; + res->end = max + base_port; list_add_tail(&iospace->list, &info->io_resources); - return base_port; + + return 0; free_resource: kfree(iospace); -out: - return ~0; + return -ENOSPC; +} + +/* + * An IO port or MMIO resource assigned to a PCI host bridge may be + * consumed by the host bridge itself or available to its child + * bus/devices. The ACPI specification defines a bit (Producer/Consumer) + * to tell whether the resource is consumed by the host bridge itself, + * but firmware hasn't used that bit consistently, so we can't rely on it. + * + * On x86 and IA64 platforms, all IO port and MMIO resources are assumed + * to be available to child bus/devices except one special case: + * IO port [0xCF8-0xCFF] is consumed by the host bridge itself + * to access PCI configuration space. + * + * So explicitly filter out PCI CFG IO ports[0xCF8-0xCFF]. + */ +static bool resource_is_pcicfg_ioport(struct resource *res) +{ + return (res->flags & IORESOURCE_IO) && + res->start == 0xCF8 && res->end == 0xCFF; } -static acpi_status resource_to_window(struct acpi_resource *resource, - struct acpi_resource_address64 *addr) +static int +probe_pci_root_info(struct pci_root_info *info, struct acpi_device *device, + int busnum, int domain) { - acpi_status status; + int ret; + struct list_head *list = &info->resources; + struct resource_entry *entry, *tmp; - /* - * We're only interested in _CRS descriptors that are - * - address space descriptors for memory or I/O space - * - non-zero size - */ - status = acpi_resource_to_address64(resource, addr); - if (ACPI_SUCCESS(status) && - (addr->resource_type == ACPI_MEMORY_RANGE || - addr->resource_type == ACPI_IO_RANGE) && - addr->address.address_length) - return AE_OK; - - return AE_ERROR; -} - -static acpi_status count_window(struct acpi_resource *resource, void *data) -{ - unsigned int *windows = (unsigned int *) data; - struct acpi_resource_address64 addr; - acpi_status status; - - status = resource_to_window(resource, &addr); - if (ACPI_SUCCESS(status)) - (*windows)++; - - return AE_OK; -} - -static acpi_status add_window(struct acpi_resource *res, void *data) -{ - struct pci_root_info *info = data; - struct resource *resource; - struct acpi_resource_address64 addr; - acpi_status status; - unsigned long flags, offset = 0; - struct resource *root; - - /* Return AE_OK for non-window resources to keep scanning for more */ - status = resource_to_window(res, &addr); - if (!ACPI_SUCCESS(status)) - return AE_OK; - - if (addr.resource_type == ACPI_MEMORY_RANGE) { - flags = IORESOURCE_MEM; - root = &iomem_resource; - offset = addr.address.translation_offset; - } else if (addr.resource_type == ACPI_IO_RANGE) { - flags = IORESOURCE_IO; - root = &ioport_resource; - offset = add_io_space(info, &addr); - if (offset == ~0) - return AE_OK; - } else - return AE_OK; - - resource = &info->res[info->res_num]; - resource->name = info->name; - resource->flags = flags; - resource->start = addr.address.minimum + offset; - resource->end = resource->start + addr.address.address_length - 1; - info->res_offset[info->res_num] = offset; - - if (insert_resource(root, resource)) { - dev_err(&info->bridge->dev, - "can't allocate host bridge window %pR\n", - resource); - } else { - if (offset) - dev_info(&info->bridge->dev, "host bridge window %pR " - "(PCI address [%#llx-%#llx])\n", - resource, - resource->start - offset, - resource->end - offset); - else - dev_info(&info->bridge->dev, - "host bridge window %pR\n", resource); - } - /* HP's firmware has a hack to work around a Windows bug. - * Ignore these tiny memory ranges */ - if (!((resource->flags & IORESOURCE_MEM) && - (resource->end - resource->start < 16))) - pci_add_resource_offset(&info->resources, resource, - info->res_offset[info->res_num]); + ret = acpi_dev_get_resources(device, list, + acpi_dev_filter_resource_type_cb, + (void *)(IORESOURCE_IO | IORESOURCE_MEM)); + if (ret < 0) + dev_warn(&device->dev, + "failed to parse _CRS method, error code %d\n", ret); + else if (ret == 0) + dev_dbg(&device->dev, + "no IO and memory resources present in _CRS\n"); + else + resource_list_for_each_entry_safe(entry, tmp, list) { + if ((entry->res->flags & IORESOURCE_DISABLED) || + resource_is_pcicfg_ioport(entry->res)) + resource_list_destroy_entry(entry); + else + entry->res->name = info->name; + } - info->res_num++; - return AE_OK; + return ret; } -static void free_pci_root_info_res(struct pci_root_info *info) -{ - struct iospace_resource *iospace, *tmp; +static void validate_resources(struct device *dev, struct list_head *resources, + unsigned long type) +{ + LIST_HEAD(list); + struct resource *res1, *res2, *root = NULL; + struct resource_entry *tmp, *entry, *entry2; + + BUG_ON((type & (IORESOURCE_MEM | IORESOURCE_IO)) == 0); + root = (type & IORESOURCE_MEM) ? &iomem_resource : &ioport_resource; + + list_splice_init(resources, &list); + resource_list_for_each_entry_safe(entry, tmp, &list) { + bool free = false; + resource_size_t end; + + res1 = entry->res; + if (!(res1->flags & type)) + goto next; + + /* Exclude non-addressable range or non-addressable portion */ + end = min(res1->end, root->end); + if (end <= res1->start) { + dev_info(dev, "host bridge window %pR (ignored, not CPU addressable)\n", + res1); + free = true; + goto next; + } else if (res1->end != end) { + dev_info(dev, "host bridge window %pR ([%#llx-%#llx] ignored, not CPU addressable)\n", + res1, (unsigned long long)end + 1, + (unsigned long long)res1->end); + res1->end = end; + } - list_for_each_entry_safe(iospace, tmp, &info->io_resources, list) - kfree(iospace); + resource_list_for_each_entry(entry2, resources) { + res2 = entry2->res; + if (!(res2->flags & type)) + continue; + + /* + * I don't like throwing away windows because then + * our resources no longer match the ACPI _CRS, but + * the kernel resource tree doesn't allow overlaps. + */ + if (resource_overlaps(res1, res2)) { + res2->start = min(res1->start, res2->start); + res2->end = max(res1->end, res2->end); + dev_info(dev, "host bridge window expanded to %pR; %pR ignored\n", + res2, res1); + free = true; + goto next; + } + } - kfree(info->name); - kfree(info->res); - info->res = NULL; - kfree(info->res_offset); - info->res_offset = NULL; - info->res_num = 0; - kfree(info->controller); - info->controller = NULL; +next: + resource_list_del(entry); + if (free) + resource_list_free_entry(entry); + else + resource_list_add_tail(entry, resources); + } +} + +static void add_resources(struct pci_root_info *info, struct device *dev) +{ + struct resource_entry *entry, *tmp; + struct resource *res, *conflict, *root = NULL; + struct list_head *list = &info->resources; + + validate_resources(dev, list, IORESOURCE_MEM); + validate_resources(dev, list, IORESOURCE_IO); + + resource_list_for_each_entry_safe(entry, tmp, list) { + res = entry->res; + if (res->flags & IORESOURCE_MEM) { + root = &iomem_resource; + /* + * HP's firmware has a hack to work around a Windows + * bug. Ignore these tiny memory ranges. + */ + if (resource_size(res) <= 16) { + resource_list_destroy_entry(entry); + continue; + } + } else if (res->flags & IORESOURCE_IO) { + root = &ioport_resource; + if (add_io_space(&info->bridge->dev, info, entry)) { + resource_list_destroy_entry(entry); + continue; + } + } else { + BUG_ON(res); + } + + conflict = insert_resource_conflict(root, res); + if (conflict) { + dev_info(dev, + "ignoring host bridge window %pR (conflicts with %s %pR)\n", + res, conflict->name, conflict); + resource_list_destroy_entry(entry); + } + } } static void __release_pci_root_info(struct pci_root_info *info) { - int i; struct resource *res; - struct iospace_resource *iospace; + struct iospace_resource *iospace, *tmp; + struct resource_entry *entry, *tentry; - list_for_each_entry(iospace, &info->io_resources, list) + list_for_each_entry_safe(iospace, tmp, &info->io_resources, list) { release_resource(&iospace->res); + kfree(iospace); + } - for (i = 0; i < info->res_num; i++) { - res = &info->res[i]; - - if (!res->parent) - continue; - - if (!(res->flags & (IORESOURCE_MEM | IORESOURCE_IO))) - continue; - - release_resource(res); + resource_list_for_each_entry_safe(entry, tentry, &info->resources) { + res = entry->res; + if (res->parent && + (res->flags & (IORESOURCE_MEM | IORESOURCE_IO))) + release_resource(res); + resource_list_destroy_entry(entry); } - free_pci_root_info_res(info); kfree(info); } @@ -373,99 +395,49 @@ static void release_pci_root_info(struct pci_host_bridge *bridge) __release_pci_root_info(info); } -static int -probe_pci_root_info(struct pci_root_info *info, struct acpi_device *device, - int busnum, int domain) -{ - char *name; - - name = kmalloc(16, GFP_KERNEL); - if (!name) - return -ENOMEM; - - sprintf(name, "PCI Bus %04x:%02x", domain, busnum); - info->bridge = device; - info->name = name; - - acpi_walk_resources(device->handle, METHOD_NAME__CRS, count_window, - &info->res_num); - if (info->res_num) { - info->res = - kzalloc_node(sizeof(*info->res) * info->res_num, - GFP_KERNEL, info->controller->node); - if (!info->res) { - kfree(name); - return -ENOMEM; - } - - info->res_offset = - kzalloc_node(sizeof(*info->res_offset) * info->res_num, - GFP_KERNEL, info->controller->node); - if (!info->res_offset) { - kfree(name); - kfree(info->res); - info->res = NULL; - return -ENOMEM; - } - - info->res_num = 0; - acpi_walk_resources(device->handle, METHOD_NAME__CRS, - add_window, info); - } else - kfree(name); - - return 0; -} - struct pci_bus *pci_acpi_scan_root(struct acpi_pci_root *root) { struct acpi_device *device = root->device; int domain = root->segment; int bus = root->secondary.start; - struct pci_controller *controller; - struct pci_root_info *info = NULL; - int busnum = root->secondary.start; + struct pci_root_info *info; struct pci_bus *pbus; int ret; - controller = alloc_pci_controller(domain); - if (!controller) - return NULL; - - controller->companion = device; - controller->node = acpi_get_node(device->handle); - info = kzalloc(sizeof(*info), GFP_KERNEL); if (!info) { dev_err(&device->dev, - "pci_bus %04x:%02x: ignored (out of memory)\n", - domain, busnum); - kfree(controller); + "pci_bus %04x:%02x: ignored (out of memory)\n", + domain, bus); return NULL; } - info->controller = controller; - INIT_LIST_HEAD(&info->io_resources); + info->controller.segment = domain; + info->controller.companion = device; + info->controller.node = acpi_get_node(device->handle); + info->bridge = device; INIT_LIST_HEAD(&info->resources); + INIT_LIST_HEAD(&info->io_resources); + snprintf(info->name, sizeof(info->name), + "PCI Bus %04x:%02x", domain, bus); - ret = probe_pci_root_info(info, device, busnum, domain); - if (ret) { - kfree(info->controller); + ret = probe_pci_root_info(info, device, bus, domain); + if (ret <= 0) { kfree(info); return NULL; } - /* insert busn resource at first */ + add_resources(info, &info->bridge->dev); pci_add_resource(&info->resources, &root->secondary); + /* * See arch/x86/pci/acpi.c. * The desired pci bus might already be scanned in a quirk. We * should handle the case here, but it appears that IA64 hasn't * such quirk. So we just ignore the case now. */ - pbus = pci_create_root_bus(NULL, bus, &pci_root_ops, controller, - &info->resources); + pbus = pci_create_root_bus(NULL, bus, &pci_root_ops, + &info->controller, &info->resources); if (!pbus) { - pci_free_resource_list(&info->resources); __release_pci_root_info(info); return NULL; } -- cgit v0.10.2 From 3f7abdefc07755d67e2b2b63608d3128f6e0b3c5 Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Wed, 14 Oct 2015 14:29:38 +0800 Subject: ia64/PCI: Use common struct resource_entry to replace struct iospace_resource Use common struct resource_entry to replace private struct iospace_resource. Signed-off-by: Jiang Liu Reviewed-by: Hanjun Guo Signed-off-by: Rafael J. Wysocki diff --git a/arch/ia64/include/asm/pci.h b/arch/ia64/include/asm/pci.h index 36d2c1e3..07039d1 100644 --- a/arch/ia64/include/asm/pci.h +++ b/arch/ia64/include/asm/pci.h @@ -64,11 +64,6 @@ extern int pci_mmap_legacy_page_range(struct pci_bus *bus, #define pci_legacy_read platform_pci_legacy_read #define pci_legacy_write platform_pci_legacy_write -struct iospace_resource { - struct list_head list; - struct resource res; -}; - struct pci_controller { struct acpi_device *companion; void *iommu; diff --git a/arch/ia64/pci/pci.c b/arch/ia64/pci/pci.c index d20db9e..b1846b8 100644 --- a/arch/ia64/pci/pci.c +++ b/arch/ia64/pci/pci.c @@ -154,14 +154,14 @@ new_space (u64 phys_base, int sparse) static int add_io_space(struct device *dev, struct pci_root_info *info, struct resource_entry *entry) { - struct iospace_resource *iospace; + struct resource_entry *iospace; struct resource *resource, *res = entry->res; char *name; unsigned long base, min, max, base_port; unsigned int sparse = 0, space_nr, len; len = strlen(info->name) + 32; - iospace = kzalloc(sizeof(*iospace) + len, GFP_KERNEL); + iospace = resource_list_create_entry(NULL, len); if (!iospace) { dev_err(dev, "PCI: No memory for %s I/O port space\n", info->name); @@ -190,7 +190,7 @@ static int add_io_space(struct device *dev, struct pci_root_info *info, if (space_nr == 0) sparse = 1; - resource = &iospace->res; + resource = iospace->res; resource->name = name; resource->flags = IORESOURCE_MEM; resource->start = base + (sparse ? IO_SPACE_SPARSE_ENCODING(min) : min); @@ -205,12 +205,12 @@ static int add_io_space(struct device *dev, struct pci_root_info *info, entry->offset = base_port; res->start = min + base_port; res->end = max + base_port; - list_add_tail(&iospace->list, &info->io_resources); + resource_list_add_tail(iospace, &info->io_resources); return 0; free_resource: - kfree(iospace); + resource_list_free_entry(iospace); return -ENOSPC; } @@ -369,12 +369,11 @@ static void add_resources(struct pci_root_info *info, struct device *dev) static void __release_pci_root_info(struct pci_root_info *info) { struct resource *res; - struct iospace_resource *iospace, *tmp; struct resource_entry *entry, *tentry; - list_for_each_entry_safe(iospace, tmp, &info->io_resources, list) { - release_resource(&iospace->res); - kfree(iospace); + resource_list_for_each_entry_safe(entry, tentry, &info->io_resources) { + release_resource(entry->res); + resource_list_destroy_entry(entry); } resource_list_for_each_entry_safe(entry, tentry, &info->resources) { -- cgit v0.10.2 From 2c204383a2922cd6b79b9d78680a049a2144fbcc Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Wed, 14 Oct 2015 14:29:39 +0800 Subject: PCI/ACPI: Add interface acpi_pci_root_create() Introduce common interface acpi_pci_root_create() and related data structures to create PCI root bus for ACPI PCI host bridges. It will be used to kill duplicated arch specific code for IA64 and x86. It may also help ARM64 in future. Reviewed-by: Lorenzo Pieralisi Tested-by: Tony Luck Signed-off-by: Jiang Liu Acked-by: Bjorn Helgaas Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c index 393706a..850d7bf 100644 --- a/drivers/acpi/pci_root.c +++ b/drivers/acpi/pci_root.c @@ -652,6 +652,210 @@ static void acpi_pci_root_remove(struct acpi_device *device) kfree(root); } +/* + * Following code to support acpi_pci_root_create() is copied from + * arch/x86/pci/acpi.c and modified so it could be reused by x86, IA64 + * and ARM64. + */ +static void acpi_pci_root_validate_resources(struct device *dev, + struct list_head *resources, + unsigned long type) +{ + LIST_HEAD(list); + struct resource *res1, *res2, *root = NULL; + struct resource_entry *tmp, *entry, *entry2; + + BUG_ON((type & (IORESOURCE_MEM | IORESOURCE_IO)) == 0); + root = (type & IORESOURCE_MEM) ? &iomem_resource : &ioport_resource; + + list_splice_init(resources, &list); + resource_list_for_each_entry_safe(entry, tmp, &list) { + bool free = false; + resource_size_t end; + + res1 = entry->res; + if (!(res1->flags & type)) + goto next; + + /* Exclude non-addressable range or non-addressable portion */ + end = min(res1->end, root->end); + if (end <= res1->start) { + dev_info(dev, "host bridge window %pR (ignored, not CPU addressable)\n", + res1); + free = true; + goto next; + } else if (res1->end != end) { + dev_info(dev, "host bridge window %pR ([%#llx-%#llx] ignored, not CPU addressable)\n", + res1, (unsigned long long)end + 1, + (unsigned long long)res1->end); + res1->end = end; + } + + resource_list_for_each_entry(entry2, resources) { + res2 = entry2->res; + if (!(res2->flags & type)) + continue; + + /* + * I don't like throwing away windows because then + * our resources no longer match the ACPI _CRS, but + * the kernel resource tree doesn't allow overlaps. + */ + if (resource_overlaps(res1, res2)) { + res2->start = min(res1->start, res2->start); + res2->end = max(res1->end, res2->end); + dev_info(dev, "host bridge window expanded to %pR; %pR ignored\n", + res2, res1); + free = true; + goto next; + } + } + +next: + resource_list_del(entry); + if (free) + resource_list_free_entry(entry); + else + resource_list_add_tail(entry, resources); + } +} + +int acpi_pci_probe_root_resources(struct acpi_pci_root_info *info) +{ + int ret; + struct list_head *list = &info->resources; + struct acpi_device *device = info->bridge; + struct resource_entry *entry, *tmp; + unsigned long flags; + + flags = IORESOURCE_IO | IORESOURCE_MEM | IORESOURCE_MEM_8AND16BIT; + ret = acpi_dev_get_resources(device, list, + acpi_dev_filter_resource_type_cb, + (void *)flags); + if (ret < 0) + dev_warn(&device->dev, + "failed to parse _CRS method, error code %d\n", ret); + else if (ret == 0) + dev_dbg(&device->dev, + "no IO and memory resources present in _CRS\n"); + else { + resource_list_for_each_entry_safe(entry, tmp, list) { + if (entry->res->flags & IORESOURCE_DISABLED) + resource_list_destroy_entry(entry); + else + entry->res->name = info->name; + } + acpi_pci_root_validate_resources(&device->dev, list, + IORESOURCE_MEM); + acpi_pci_root_validate_resources(&device->dev, list, + IORESOURCE_IO); + } + + return ret; +} + +static void pci_acpi_root_add_resources(struct acpi_pci_root_info *info) +{ + struct resource_entry *entry, *tmp; + struct resource *res, *conflict, *root = NULL; + + resource_list_for_each_entry_safe(entry, tmp, &info->resources) { + res = entry->res; + if (res->flags & IORESOURCE_MEM) + root = &iomem_resource; + else if (res->flags & IORESOURCE_IO) + root = &ioport_resource; + else + continue; + + conflict = insert_resource_conflict(root, res); + if (conflict) { + dev_info(&info->bridge->dev, + "ignoring host bridge window %pR (conflicts with %s %pR)\n", + res, conflict->name, conflict); + resource_list_destroy_entry(entry); + } + } +} + +static void __acpi_pci_root_release_info(struct acpi_pci_root_info *info) +{ + struct resource *res; + struct resource_entry *entry, *tmp; + + if (!info) + return; + + resource_list_for_each_entry_safe(entry, tmp, &info->resources) { + res = entry->res; + if (res->parent && + (res->flags & (IORESOURCE_MEM | IORESOURCE_IO))) + release_resource(res); + resource_list_destroy_entry(entry); + } + + info->ops->release_info(info); +} + +static void acpi_pci_root_release_info(struct pci_host_bridge *bridge) +{ + struct resource *res; + struct resource_entry *entry; + + resource_list_for_each_entry(entry, &bridge->windows) { + res = entry->res; + if (res->parent && + (res->flags & (IORESOURCE_MEM | IORESOURCE_IO))) + release_resource(res); + } + __acpi_pci_root_release_info(bridge->release_data); +} + +struct pci_bus *acpi_pci_root_create(struct acpi_pci_root *root, + struct acpi_pci_root_ops *ops, + struct acpi_pci_root_info *info, + void *sysdata) +{ + int ret, busnum = root->secondary.start; + struct acpi_device *device = root->device; + int node = acpi_get_node(device->handle); + struct pci_bus *bus; + + info->root = root; + info->bridge = device; + info->ops = ops; + INIT_LIST_HEAD(&info->resources); + snprintf(info->name, sizeof(info->name), "PCI Bus %04x:%02x", + root->segment, busnum); + + if (ops->init_info && ops->init_info(info)) + goto out_release_info; + if (ops->prepare_resources) + ret = ops->prepare_resources(info); + else + ret = acpi_pci_probe_root_resources(info); + if (ret < 0) + goto out_release_info; + + pci_acpi_root_add_resources(info); + pci_add_resource(&info->resources, &root->secondary); + bus = pci_create_root_bus(NULL, busnum, ops->pci_ops, + sysdata, &info->resources); + if (!bus) + goto out_release_info; + + pci_scan_child_bus(bus); + pci_set_host_bridge_release(to_pci_host_bridge(bus->bridge), + acpi_pci_root_release_info, info); + if (node != NUMA_NO_NODE) + dev_printk(KERN_DEBUG, &bus->dev, "on NUMA node %d\n", node); + return bus; + +out_release_info: + __acpi_pci_root_release_info(info); + return NULL; +} + void __init acpi_pci_root_init(void) { acpi_hest_init(); diff --git a/include/linux/pci-acpi.h b/include/linux/pci-acpi.h index a965efa..89ab057 100644 --- a/include/linux/pci-acpi.h +++ b/include/linux/pci-acpi.h @@ -52,6 +52,30 @@ static inline acpi_handle acpi_pci_get_bridge_handle(struct pci_bus *pbus) return ACPI_HANDLE(dev); } +struct acpi_pci_root; +struct acpi_pci_root_ops; + +struct acpi_pci_root_info { + struct acpi_pci_root *root; + struct acpi_device *bridge; + struct acpi_pci_root_ops *ops; + struct list_head resources; + char name[16]; +}; + +struct acpi_pci_root_ops { + struct pci_ops *pci_ops; + int (*init_info)(struct acpi_pci_root_info *info); + void (*release_info)(struct acpi_pci_root_info *info); + int (*prepare_resources)(struct acpi_pci_root_info *info); +}; + +extern int acpi_pci_probe_root_resources(struct acpi_pci_root_info *info); +extern struct pci_bus *acpi_pci_root_create(struct acpi_pci_root *root, + struct acpi_pci_root_ops *ops, + struct acpi_pci_root_info *info, + void *sd); + void acpi_pci_add_bus(struct pci_bus *bus); void acpi_pci_remove_bus(struct pci_bus *bus); -- cgit v0.10.2 From a3669868d99c8647d780895d83e74d9a921eba2b Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Wed, 14 Oct 2015 14:29:40 +0800 Subject: ACPI/PCI: Reset acpi_root_dev->domain to 0 when pci_ignore_seg is set Reset acpi_root_dev->domain to 0 when pci_ignore_seg is set to keep consistence between ACPI PCI root device and PCI host bridge device. Acked-by: Bjorn Helgaas Signed-off-by: Jiang Liu Signed-off-by: Rafael J. Wysocki diff --git a/arch/x86/pci/acpi.c b/arch/x86/pci/acpi.c index ff99117..5bc0185 100644 --- a/arch/x86/pci/acpi.c +++ b/arch/x86/pci/acpi.c @@ -401,7 +401,7 @@ struct pci_bus *pci_acpi_scan_root(struct acpi_pci_root *root) int node; if (pci_ignore_seg) - domain = 0; + root->segment = domain = 0; if (domain && !pci_domains_supported) { printk(KERN_WARNING "pci_bus %04x:%02x: " -- cgit v0.10.2 From 4d6b4e69a245e9df4b84dba387596086cb66887d Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Wed, 14 Oct 2015 14:29:41 +0800 Subject: x86/PCI/ACPI: Use common interface to support PCI host bridge Use common interface to simplify ACPI PCI host bridge implementation. Signed-off-by: Jiang Liu Reviewed-by: Hanjun Guo Acked-by: Bjorn Helgaas Signed-off-by: Rafael J. Wysocki diff --git a/arch/x86/pci/acpi.c b/arch/x86/pci/acpi.c index 5bc0185..3cd6983 100644 --- a/arch/x86/pci/acpi.c +++ b/arch/x86/pci/acpi.c @@ -4,16 +4,15 @@ #include #include #include +#include #include #include struct pci_root_info { - struct acpi_device *bridge; - char name[16]; + struct acpi_pci_root_info common; struct pci_sysdata sd; #ifdef CONFIG_PCI_MMCONFIG bool mcfg_added; - u16 segment; u8 start_bus; u8 end_bus; #endif @@ -178,15 +177,18 @@ static int check_segment(u16 seg, struct device *dev, char *estr) return 0; } -static int setup_mcfg_map(struct pci_root_info *info, u16 seg, u8 start, - u8 end, phys_addr_t addr) +static int setup_mcfg_map(struct acpi_pci_root_info *ci) { - int result; - struct device *dev = &info->bridge->dev; + int result, seg; + struct pci_root_info *info; + struct acpi_pci_root *root = ci->root; + struct device *dev = &ci->bridge->dev; - info->start_bus = start; - info->end_bus = end; + info = container_of(ci, struct pci_root_info, common); + info->start_bus = (u8)root->secondary.start; + info->end_bus = (u8)root->secondary.end; info->mcfg_added = false; + seg = info->sd.domain; /* return success if MMCFG is not in use */ if (raw_pci_ext_ops && raw_pci_ext_ops != &pci_mmcfg) @@ -195,7 +197,8 @@ static int setup_mcfg_map(struct pci_root_info *info, u16 seg, u8 start, if (!(pci_probe & PCI_PROBE_MMCONF)) return check_segment(seg, dev, "MMCONFIG is disabled,"); - result = pci_mmconfig_insert(dev, seg, start, end, addr); + result = pci_mmconfig_insert(dev, seg, info->start_bus, info->end_bus, + root->mcfg_addr); if (result == 0) { /* enable MMCFG if it hasn't been enabled yet */ if (raw_pci_ext_ops == NULL) @@ -208,134 +211,55 @@ static int setup_mcfg_map(struct pci_root_info *info, u16 seg, u8 start, return 0; } -static void teardown_mcfg_map(struct pci_root_info *info) +static void teardown_mcfg_map(struct acpi_pci_root_info *ci) { + struct pci_root_info *info; + + info = container_of(ci, struct pci_root_info, common); if (info->mcfg_added) { - pci_mmconfig_delete(info->segment, info->start_bus, - info->end_bus); + pci_mmconfig_delete(info->sd.domain, + info->start_bus, info->end_bus); info->mcfg_added = false; } } #else -static int setup_mcfg_map(struct pci_root_info *info, - u16 seg, u8 start, u8 end, - phys_addr_t addr) +static int setup_mcfg_map(struct acpi_pci_root_info *ci) { return 0; } -static void teardown_mcfg_map(struct pci_root_info *info) + +static void teardown_mcfg_map(struct acpi_pci_root_info *ci) { } #endif -static void validate_resources(struct device *dev, struct list_head *crs_res, - unsigned long type) +static int pci_acpi_root_get_node(struct acpi_pci_root *root) { - LIST_HEAD(list); - struct resource *res1, *res2, *root = NULL; - struct resource_entry *tmp, *entry, *entry2; - - BUG_ON((type & (IORESOURCE_MEM | IORESOURCE_IO)) == 0); - root = (type & IORESOURCE_MEM) ? &iomem_resource : &ioport_resource; - - list_splice_init(crs_res, &list); - resource_list_for_each_entry_safe(entry, tmp, &list) { - bool free = false; - resource_size_t end; - - res1 = entry->res; - if (!(res1->flags & type)) - goto next; - - /* Exclude non-addressable range or non-addressable portion */ - end = min(res1->end, root->end); - if (end <= res1->start) { - dev_info(dev, "host bridge window %pR (ignored, not CPU addressable)\n", - res1); - free = true; - goto next; - } else if (res1->end != end) { - dev_info(dev, "host bridge window %pR ([%#llx-%#llx] ignored, not CPU addressable)\n", - res1, (unsigned long long)end + 1, - (unsigned long long)res1->end); - res1->end = end; - } - - resource_list_for_each_entry(entry2, crs_res) { - res2 = entry2->res; - if (!(res2->flags & type)) - continue; - - /* - * I don't like throwing away windows because then - * our resources no longer match the ACPI _CRS, but - * the kernel resource tree doesn't allow overlaps. - */ - if (resource_overlaps(res1, res2)) { - res2->start = min(res1->start, res2->start); - res2->end = max(res1->end, res2->end); - dev_info(dev, "host bridge window expanded to %pR; %pR ignored\n", - res2, res1); - free = true; - goto next; - } - } + int busnum = root->secondary.start; + struct acpi_device *device = root->device; + int node = acpi_get_node(device->handle); -next: - resource_list_del(entry); - if (free) - resource_list_free_entry(entry); - else - resource_list_add_tail(entry, crs_res); + if (node == NUMA_NO_NODE) { + node = x86_pci_root_bus_node(busnum); + if (node != 0 && node != NUMA_NO_NODE) + dev_info(&device->dev, FW_BUG "no _PXM; falling back to node %d from hardware (may be inconsistent with ACPI node numbers)\n", + node); } + if (node != NUMA_NO_NODE && !node_online(node)) + node = NUMA_NO_NODE; + + return node; } -static void add_resources(struct pci_root_info *info, - struct list_head *resources, - struct list_head *crs_res) +static int pci_acpi_root_init_info(struct acpi_pci_root_info *ci) { - struct resource_entry *entry, *tmp; - struct resource *res, *conflict, *root = NULL; - - validate_resources(&info->bridge->dev, crs_res, IORESOURCE_MEM); - validate_resources(&info->bridge->dev, crs_res, IORESOURCE_IO); - - resource_list_for_each_entry_safe(entry, tmp, crs_res) { - res = entry->res; - if (res->flags & IORESOURCE_MEM) - root = &iomem_resource; - else if (res->flags & IORESOURCE_IO) - root = &ioport_resource; - else - BUG_ON(res); - - conflict = insert_resource_conflict(root, res); - if (conflict) { - dev_info(&info->bridge->dev, - "ignoring host bridge window %pR (conflicts with %s %pR)\n", - res, conflict->name, conflict); - resource_list_destroy_entry(entry); - } - } - - list_splice_tail(crs_res, resources); + return setup_mcfg_map(ci); } -static void release_pci_root_info(struct pci_host_bridge *bridge) +static void pci_acpi_root_release_info(struct acpi_pci_root_info *ci) { - struct resource *res; - struct resource_entry *entry; - struct pci_root_info *info = bridge->release_data; - - resource_list_for_each_entry(entry, &bridge->windows) { - res = entry->res; - if (res->parent && - (res->flags & (IORESOURCE_MEM | IORESOURCE_IO))) - release_resource(res); - } - - teardown_mcfg_map(info); - kfree(info); + teardown_mcfg_map(ci); + kfree(container_of(ci, struct pci_root_info, common)); } /* @@ -358,47 +282,44 @@ static bool resource_is_pcicfg_ioport(struct resource *res) res->start == 0xCF8 && res->end == 0xCFF; } -static void probe_pci_root_info(struct pci_root_info *info, - struct acpi_device *device, - int busnum, int domain, - struct list_head *list) +static int pci_acpi_root_prepare_resources(struct acpi_pci_root_info *ci) { - int ret; + struct acpi_device *device = ci->bridge; + int busnum = ci->root->secondary.start; struct resource_entry *entry, *tmp; + int status; - sprintf(info->name, "PCI Bus %04x:%02x", domain, busnum); - info->bridge = device; - ret = acpi_dev_get_resources(device, list, - acpi_dev_filter_resource_type_cb, - (void *)(IORESOURCE_IO | IORESOURCE_MEM)); - if (ret < 0) - dev_warn(&device->dev, - "failed to parse _CRS method, error code %d\n", ret); - else if (ret == 0) - dev_dbg(&device->dev, - "no IO and memory resources present in _CRS\n"); - else - resource_list_for_each_entry_safe(entry, tmp, list) { - if ((entry->res->flags & IORESOURCE_DISABLED) || - resource_is_pcicfg_ioport(entry->res)) + status = acpi_pci_probe_root_resources(ci); + if (pci_use_crs) { + resource_list_for_each_entry_safe(entry, tmp, &ci->resources) + if (resource_is_pcicfg_ioport(entry->res)) resource_list_destroy_entry(entry); - else - entry->res->name = info->name; - } + return status; + } + + resource_list_for_each_entry_safe(entry, tmp, &ci->resources) { + dev_printk(KERN_DEBUG, &device->dev, + "host bridge window %pR (ignored)\n", entry->res); + resource_list_destroy_entry(entry); + } + x86_pci_root_bus_resources(busnum, &ci->resources); + + return 0; } +static struct acpi_pci_root_ops acpi_pci_root_ops = { + .pci_ops = &pci_root_ops, + .init_info = pci_acpi_root_init_info, + .release_info = pci_acpi_root_release_info, + .prepare_resources = pci_acpi_root_prepare_resources, +}; + struct pci_bus *pci_acpi_scan_root(struct acpi_pci_root *root) { - struct acpi_device *device = root->device; - struct pci_root_info *info; int domain = root->segment; int busnum = root->secondary.start; - struct resource_entry *res_entry; - LIST_HEAD(crs_res); - LIST_HEAD(resources); + int node = pci_acpi_root_get_node(root); struct pci_bus *bus; - struct pci_sysdata *sd; - int node; if (pci_ignore_seg) root->segment = domain = 0; @@ -410,71 +331,33 @@ struct pci_bus *pci_acpi_scan_root(struct acpi_pci_root *root) return NULL; } - node = acpi_get_node(device->handle); - if (node == NUMA_NO_NODE) { - node = x86_pci_root_bus_node(busnum); - if (node != 0 && node != NUMA_NO_NODE) - dev_info(&device->dev, FW_BUG "no _PXM; falling back to node %d from hardware (may be inconsistent with ACPI node numbers)\n", - node); - } - - if (node != NUMA_NO_NODE && !node_online(node)) - node = NUMA_NO_NODE; - - info = kzalloc_node(sizeof(*info), GFP_KERNEL, node); - if (!info) { - printk(KERN_WARNING "pci_bus %04x:%02x: " - "ignored (out of memory)\n", domain, busnum); - return NULL; - } - - sd = &info->sd; - sd->domain = domain; - sd->node = node; - sd->companion = device; - bus = pci_find_bus(domain, busnum); if (bus) { /* * If the desired bus has been scanned already, replace * its bus->sysdata. */ - memcpy(bus->sysdata, sd, sizeof(*sd)); - kfree(info); - } else { - /* insert busn res at first */ - pci_add_resource(&resources, &root->secondary); + struct pci_sysdata sd = { + .domain = domain, + .node = node, + .companion = root->device + }; - /* - * _CRS with no apertures is normal, so only fall back to - * defaults or native bridge info if we're ignoring _CRS. - */ - probe_pci_root_info(info, device, busnum, domain, &crs_res); - if (pci_use_crs) { - add_resources(info, &resources, &crs_res); - } else { - resource_list_for_each_entry(res_entry, &crs_res) - dev_printk(KERN_DEBUG, &device->dev, - "host bridge window %pR (ignored)\n", - res_entry->res); - resource_list_free(&crs_res); - x86_pci_root_bus_resources(busnum, &resources); - } - - if (!setup_mcfg_map(info, domain, (u8)root->secondary.start, - (u8)root->secondary.end, root->mcfg_addr)) - bus = pci_create_root_bus(NULL, busnum, &pci_root_ops, - sd, &resources); - - if (bus) { - pci_scan_child_bus(bus); - pci_set_host_bridge_release( - to_pci_host_bridge(bus->bridge), - release_pci_root_info, info); - } else { - resource_list_free(&resources); - teardown_mcfg_map(info); - kfree(info); + memcpy(bus->sysdata, &sd, sizeof(sd)); + } else { + struct pci_root_info *info; + + info = kzalloc_node(sizeof(*info), GFP_KERNEL, node); + if (!info) + dev_err(&root->device->dev, + "pci_bus %04x:%02x: ignored (out of memory)\n", + domain, busnum); + else { + info->sd.domain = domain; + info->sd.node = node; + info->sd.companion = root->device; + bus = acpi_pci_root_create(root, &acpi_pci_root_ops, + &info->common, &info->sd); } } @@ -487,9 +370,6 @@ struct pci_bus *pci_acpi_scan_root(struct acpi_pci_root *root) pcie_bus_configure_settings(child); } - if (bus && node != NUMA_NO_NODE) - dev_printk(KERN_DEBUG, &bus->dev, "on NUMA node %d\n", node); - return bus; } -- cgit v0.10.2 From 02715e86b21955f107f376d84d165424ba9cd372 Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Wed, 14 Oct 2015 14:29:42 +0800 Subject: ia64/PCI/ACPI: Use common interface to support PCI host bridge Use common interface to simplify PCI host bridge implementation. Tested-by: Tony Luck Signed-off-by: Jiang Liu Signed-off-by: Rafael J. Wysocki diff --git a/arch/ia64/pci/pci.c b/arch/ia64/pci/pci.c index b1846b8..8f6ac2f 100644 --- a/arch/ia64/pci/pci.c +++ b/arch/ia64/pci/pci.c @@ -116,15 +116,12 @@ struct pci_ops pci_root_ops = { }; struct pci_root_info { + struct acpi_pci_root_info common; struct pci_controller controller; - struct acpi_device *bridge; - struct list_head resources; struct list_head io_resources; - char name[16]; }; -static unsigned int -new_space (u64 phys_base, int sparse) +static unsigned int new_space(u64 phys_base, int sparse) { u64 mmio_base; int i; @@ -160,11 +157,11 @@ static int add_io_space(struct device *dev, struct pci_root_info *info, unsigned long base, min, max, base_port; unsigned int sparse = 0, space_nr, len; - len = strlen(info->name) + 32; + len = strlen(info->common.name) + 32; iospace = resource_list_create_entry(NULL, len); if (!iospace) { dev_err(dev, "PCI: No memory for %s I/O port space\n", - info->name); + info->common.name); return -ENOMEM; } @@ -179,7 +176,7 @@ static int add_io_space(struct device *dev, struct pci_root_info *info, max = res->end - entry->offset; base = __pa(io_space[space_nr].mmio_base); base_port = IO_SPACE_BASE(space_nr); - snprintf(name, len, "%s I/O Ports %08lx-%08lx", info->name, + snprintf(name, len, "%s I/O Ports %08lx-%08lx", info->common.name, base_port + min, base_port + max); /* @@ -234,217 +231,81 @@ static bool resource_is_pcicfg_ioport(struct resource *res) res->start == 0xCF8 && res->end == 0xCFF; } -static int -probe_pci_root_info(struct pci_root_info *info, struct acpi_device *device, - int busnum, int domain) +static int pci_acpi_root_prepare_resources(struct acpi_pci_root_info *ci) { - int ret; - struct list_head *list = &info->resources; + struct device *dev = &ci->bridge->dev; + struct pci_root_info *info; + struct resource *res; struct resource_entry *entry, *tmp; - - ret = acpi_dev_get_resources(device, list, - acpi_dev_filter_resource_type_cb, - (void *)(IORESOURCE_IO | IORESOURCE_MEM)); - if (ret < 0) - dev_warn(&device->dev, - "failed to parse _CRS method, error code %d\n", ret); - else if (ret == 0) - dev_dbg(&device->dev, - "no IO and memory resources present in _CRS\n"); - else - resource_list_for_each_entry_safe(entry, tmp, list) { - if ((entry->res->flags & IORESOURCE_DISABLED) || - resource_is_pcicfg_ioport(entry->res)) - resource_list_destroy_entry(entry); - else - entry->res->name = info->name; - } - - return ret; -} - -static void validate_resources(struct device *dev, struct list_head *resources, - unsigned long type) -{ - LIST_HEAD(list); - struct resource *res1, *res2, *root = NULL; - struct resource_entry *tmp, *entry, *entry2; - - BUG_ON((type & (IORESOURCE_MEM | IORESOURCE_IO)) == 0); - root = (type & IORESOURCE_MEM) ? &iomem_resource : &ioport_resource; - - list_splice_init(resources, &list); - resource_list_for_each_entry_safe(entry, tmp, &list) { - bool free = false; - resource_size_t end; - - res1 = entry->res; - if (!(res1->flags & type)) - goto next; - - /* Exclude non-addressable range or non-addressable portion */ - end = min(res1->end, root->end); - if (end <= res1->start) { - dev_info(dev, "host bridge window %pR (ignored, not CPU addressable)\n", - res1); - free = true; - goto next; - } else if (res1->end != end) { - dev_info(dev, "host bridge window %pR ([%#llx-%#llx] ignored, not CPU addressable)\n", - res1, (unsigned long long)end + 1, - (unsigned long long)res1->end); - res1->end = end; - } - - resource_list_for_each_entry(entry2, resources) { - res2 = entry2->res; - if (!(res2->flags & type)) - continue; - - /* - * I don't like throwing away windows because then - * our resources no longer match the ACPI _CRS, but - * the kernel resource tree doesn't allow overlaps. - */ - if (resource_overlaps(res1, res2)) { - res2->start = min(res1->start, res2->start); - res2->end = max(res1->end, res2->end); - dev_info(dev, "host bridge window expanded to %pR; %pR ignored\n", - res2, res1); - free = true; - goto next; + int status; + + status = acpi_pci_probe_root_resources(ci); + if (status > 0) { + info = container_of(ci, struct pci_root_info, common); + resource_list_for_each_entry_safe(entry, tmp, &ci->resources) { + res = entry->res; + if (res->flags & IORESOURCE_MEM) { + /* + * HP's firmware has a hack to work around a + * Windows bug. Ignore these tiny memory ranges. + */ + if (resource_size(res) <= 16) { + resource_list_del(entry); + insert_resource(&iomem_resource, + entry->res); + resource_list_add_tail(entry, + &info->io_resources); + } + } else if (res->flags & IORESOURCE_IO) { + if (resource_is_pcicfg_ioport(entry->res)) + resource_list_destroy_entry(entry); + else if (add_io_space(dev, info, entry)) + resource_list_destroy_entry(entry); } } - -next: - resource_list_del(entry); - if (free) - resource_list_free_entry(entry); - else - resource_list_add_tail(entry, resources); } -} -static void add_resources(struct pci_root_info *info, struct device *dev) -{ - struct resource_entry *entry, *tmp; - struct resource *res, *conflict, *root = NULL; - struct list_head *list = &info->resources; - - validate_resources(dev, list, IORESOURCE_MEM); - validate_resources(dev, list, IORESOURCE_IO); - - resource_list_for_each_entry_safe(entry, tmp, list) { - res = entry->res; - if (res->flags & IORESOURCE_MEM) { - root = &iomem_resource; - /* - * HP's firmware has a hack to work around a Windows - * bug. Ignore these tiny memory ranges. - */ - if (resource_size(res) <= 16) { - resource_list_destroy_entry(entry); - continue; - } - } else if (res->flags & IORESOURCE_IO) { - root = &ioport_resource; - if (add_io_space(&info->bridge->dev, info, entry)) { - resource_list_destroy_entry(entry); - continue; - } - } else { - BUG_ON(res); - } - - conflict = insert_resource_conflict(root, res); - if (conflict) { - dev_info(dev, - "ignoring host bridge window %pR (conflicts with %s %pR)\n", - res, conflict->name, conflict); - resource_list_destroy_entry(entry); - } - } + return status; } -static void __release_pci_root_info(struct pci_root_info *info) +static void pci_acpi_root_release_info(struct acpi_pci_root_info *ci) { - struct resource *res; - struct resource_entry *entry, *tentry; + struct pci_root_info *info; + struct resource_entry *entry, *tmp; - resource_list_for_each_entry_safe(entry, tentry, &info->io_resources) { + info = container_of(ci, struct pci_root_info, common); + resource_list_for_each_entry_safe(entry, tmp, &info->io_resources) { release_resource(entry->res); resource_list_destroy_entry(entry); } - - resource_list_for_each_entry_safe(entry, tentry, &info->resources) { - res = entry->res; - if (res->parent && - (res->flags & (IORESOURCE_MEM | IORESOURCE_IO))) - release_resource(res); - resource_list_destroy_entry(entry); - } - kfree(info); } -static void release_pci_root_info(struct pci_host_bridge *bridge) -{ - struct pci_root_info *info = bridge->release_data; - - __release_pci_root_info(info); -} +static struct acpi_pci_root_ops pci_acpi_root_ops = { + .pci_ops = &pci_root_ops, + .release_info = pci_acpi_root_release_info, + .prepare_resources = pci_acpi_root_prepare_resources, +}; struct pci_bus *pci_acpi_scan_root(struct acpi_pci_root *root) { struct acpi_device *device = root->device; - int domain = root->segment; - int bus = root->secondary.start; struct pci_root_info *info; - struct pci_bus *pbus; - int ret; info = kzalloc(sizeof(*info), GFP_KERNEL); if (!info) { dev_err(&device->dev, "pci_bus %04x:%02x: ignored (out of memory)\n", - domain, bus); + root->segment, (int)root->secondary.start); return NULL; } - info->controller.segment = domain; + info->controller.segment = root->segment; info->controller.companion = device; info->controller.node = acpi_get_node(device->handle); - info->bridge = device; - INIT_LIST_HEAD(&info->resources); INIT_LIST_HEAD(&info->io_resources); - snprintf(info->name, sizeof(info->name), - "PCI Bus %04x:%02x", domain, bus); - - ret = probe_pci_root_info(info, device, bus, domain); - if (ret <= 0) { - kfree(info); - return NULL; - } - add_resources(info, &info->bridge->dev); - pci_add_resource(&info->resources, &root->secondary); - - /* - * See arch/x86/pci/acpi.c. - * The desired pci bus might already be scanned in a quirk. We - * should handle the case here, but it appears that IA64 hasn't - * such quirk. So we just ignore the case now. - */ - pbus = pci_create_root_bus(NULL, bus, &pci_root_ops, - &info->controller, &info->resources); - if (!pbus) { - __release_pci_root_info(info); - return NULL; - } - - pci_set_host_bridge_release(to_pci_host_bridge(pbus->bridge), - release_pci_root_info, info); - pci_scan_child_bus(pbus); - return pbus; + return acpi_pci_root_create(root, &pci_acpi_root_ops, + &info->common, &info->controller); } int pcibios_root_bridge_prepare(struct pci_host_bridge *bridge) -- cgit v0.10.2 From 89e7b2553ae182a4b2a19e2e99a59ad0d3ccb1ed Mon Sep 17 00:00:00 2001 From: Amy Wiles Date: Wed, 14 Oct 2015 08:09:15 -0700 Subject: powercap / RAPL: Enable Broxton RAPL support Broxton CPU ID for the RAPL driver. Signed-off-by: Amy Wiles [ rjw: Subject and changelog. ] Signed-off-by: Rafael J. Wysocki diff --git a/drivers/powercap/intel_rapl.c b/drivers/powercap/intel_rapl.c index 5efacd0..cc97f08 100644 --- a/drivers/powercap/intel_rapl.c +++ b/drivers/powercap/intel_rapl.c @@ -1102,6 +1102,7 @@ static const struct x86_cpu_id rapl_ids[] __initconst = { RAPL_CPU(0x4A, rapl_defaults_tng),/* Tangier */ RAPL_CPU(0x56, rapl_defaults_core),/* Future Xeon */ RAPL_CPU(0x5A, rapl_defaults_ann),/* Annidale */ + RAPL_CPU(0X5C, rapl_defaults_core),/* Broxton */ RAPL_CPU(0x5E, rapl_defaults_core),/* Skylake-H/S */ RAPL_CPU(0x57, rapl_defaults_hsw_server),/* Knights Landing */ {} -- cgit v0.10.2 From 51443fbf3d2cde16011b994252c8004ebcd66fb0 Mon Sep 17 00:00:00 2001 From: Prarit Bhargava Date: Thu, 15 Oct 2015 07:34:15 -0400 Subject: cpufreq: intel_pstate: Fix intel_pstate powersave min_perf_pct value On systems that initialize the intel_pstate driver with the performance governor, and then switch to the powersave governor will not transition to lower cpu frequencies until /sys/devices/system/cpu/intel_pstate/min_perf_pct is set to a low value. The behavior of governor switching changed after commit a04759924e25 ("[cpufreq] intel_pstate: honor user space min_perf_pct override on resume"). The commit introduced tracking of performance percentage changes via sysfs in order to restore userspace changes during suspend/resume. The problem occurs because the global values of the newly introduced max_sysfs_pct and min_sysfs_pct are not lowered on the governor change and this causes the powersave governor to inherit the performance governor's settings. A simple change would have been to reset max_sysfs_pct to 100 and min_sysfs_pct to 0 on a governor change, which fixes the problem with governor switching. However, since we cannot break userspace[1] the fix is now to give each governor its own limits storage area so that governor specific changes are tracked. I successfully tested this by booting with both the performance governor and the powersave governor by default, and switching between the two governors (while monitoring /sys/devices/system/cpu/intel_pstate/ values, and looking at the output of cpupower frequency-info). Suspend/Resume testing was performed by Doug Smythies. [1] Systems which suspend/resume using the unmaintained pm-utils package will always transition to the performance governor before the suspend and after the resume. This means a system using the powersave governor will go from powersave to performance, then suspend/resume, performance to powersave. The simple change during governor changes would have been overwritten when the governor changed before and after the suspend/resume. I have submitted https://bugzilla.redhat.com/show_bug.cgi?id=1271225 against Fedora to remove the 94cpufreq file that causes the problem. It should be noted that pm-utils is obsoleted with newer versions of systemd. Signed-off-by: Prarit Bhargava Acked-by: Kristen Carlson Accardi Signed-off-by: Rafael J. Wysocki diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c index 6a5a221..93a3c63 100644 --- a/drivers/cpufreq/intel_pstate.c +++ b/drivers/cpufreq/intel_pstate.c @@ -167,7 +167,20 @@ struct perf_limits { int min_perf_ctl; }; -static struct perf_limits limits = { +static struct perf_limits performance_limits = { + .no_turbo = 0, + .turbo_disabled = 0, + .max_perf_pct = 100, + .max_perf = int_tofp(1), + .min_perf_pct = 100, + .min_perf = int_tofp(1), + .max_policy_pct = 100, + .max_sysfs_pct = 100, + .min_policy_pct = 0, + .min_sysfs_pct = 0, +}; + +static struct perf_limits powersave_limits = { .no_turbo = 0, .turbo_disabled = 0, .max_perf_pct = 100, @@ -182,6 +195,12 @@ static struct perf_limits limits = { .min_perf_ctl = 0, }; +#ifdef CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE +static struct perf_limits *limits = &performance_limits; +#else +static struct perf_limits *limits = &powersave_limits; +#endif + #if IS_ENABLED(CONFIG_ACPI) /* * The max target pstate ratio is a 8 bit value in both PLATFORM_INFO MSR and @@ -256,7 +275,7 @@ static int intel_pstate_init_perf_limits(struct cpufreq_policy *policy) if (turbo_pss_ctl <= cpu->pstate.max_pstate && turbo_pss_ctl > cpu->pstate.min_pstate) { pr_debug("intel_pstate: no turbo range exists in _PSS\n"); - limits.no_turbo = limits.turbo_disabled = 1; + limits->no_turbo = limits->turbo_disabled = 1; cpu->pstate.turbo_pstate = cpu->pstate.max_pstate; turbo_absent = true; } @@ -415,7 +434,7 @@ static inline void update_turbo_state(void) cpu = all_cpu_data[0]; rdmsrl(MSR_IA32_MISC_ENABLE, misc_en); - limits.turbo_disabled = + limits->turbo_disabled = (misc_en & MSR_IA32_MISC_ENABLE_TURBO_DISABLE || cpu->pstate.max_pstate == cpu->pstate.turbo_pstate); } @@ -434,14 +453,14 @@ static void intel_pstate_hwp_set(void) for_each_online_cpu(cpu) { rdmsrl_on_cpu(cpu, MSR_HWP_REQUEST, &value); - adj_range = limits.min_perf_pct * range / 100; + adj_range = limits->min_perf_pct * range / 100; min = hw_min + adj_range; value &= ~HWP_MIN_PERF(~0L); value |= HWP_MIN_PERF(min); - adj_range = limits.max_perf_pct * range / 100; + adj_range = limits->max_perf_pct * range / 100; max = hw_min + adj_range; - if (limits.no_turbo) { + if (limits->no_turbo) { hw_max = HWP_GUARANTEED_PERF(cap); if (hw_max < max) max = hw_max; @@ -510,7 +529,7 @@ static void __init intel_pstate_debug_expose_params(void) static ssize_t show_##file_name \ (struct kobject *kobj, struct attribute *attr, char *buf) \ { \ - return sprintf(buf, "%u\n", limits.object); \ + return sprintf(buf, "%u\n", limits->object); \ } static ssize_t show_turbo_pct(struct kobject *kobj, @@ -546,10 +565,10 @@ static ssize_t show_no_turbo(struct kobject *kobj, ssize_t ret; update_turbo_state(); - if (limits.turbo_disabled) - ret = sprintf(buf, "%u\n", limits.turbo_disabled); + if (limits->turbo_disabled) + ret = sprintf(buf, "%u\n", limits->turbo_disabled); else - ret = sprintf(buf, "%u\n", limits.no_turbo); + ret = sprintf(buf, "%u\n", limits->no_turbo); return ret; } @@ -565,12 +584,12 @@ static ssize_t store_no_turbo(struct kobject *a, struct attribute *b, return -EINVAL; update_turbo_state(); - if (limits.turbo_disabled) { + if (limits->turbo_disabled) { pr_warn("intel_pstate: Turbo disabled by BIOS or unavailable on processor\n"); return -EPERM; } - limits.no_turbo = clamp_t(int, input, 0, 1); + limits->no_turbo = clamp_t(int, input, 0, 1); if (hwp_active) intel_pstate_hwp_set(); @@ -588,11 +607,15 @@ static ssize_t store_max_perf_pct(struct kobject *a, struct attribute *b, if (ret != 1) return -EINVAL; - limits.max_sysfs_pct = clamp_t(int, input, 0 , 100); - limits.max_perf_pct = min(limits.max_policy_pct, limits.max_sysfs_pct); - limits.max_perf_pct = max(limits.min_policy_pct, limits.max_perf_pct); - limits.max_perf_pct = max(limits.min_perf_pct, limits.max_perf_pct); - limits.max_perf = div_fp(int_tofp(limits.max_perf_pct), int_tofp(100)); + limits->max_sysfs_pct = clamp_t(int, input, 0 , 100); + limits->max_perf_pct = min(limits->max_policy_pct, + limits->max_sysfs_pct); + limits->max_perf_pct = max(limits->min_policy_pct, + limits->max_perf_pct); + limits->max_perf_pct = max(limits->min_perf_pct, + limits->max_perf_pct); + limits->max_perf = div_fp(int_tofp(limits->max_perf_pct), + int_tofp(100)); if (hwp_active) intel_pstate_hwp_set(); @@ -609,11 +632,15 @@ static ssize_t store_min_perf_pct(struct kobject *a, struct attribute *b, if (ret != 1) return -EINVAL; - limits.min_sysfs_pct = clamp_t(int, input, 0 , 100); - limits.min_perf_pct = max(limits.min_policy_pct, limits.min_sysfs_pct); - limits.min_perf_pct = min(limits.max_policy_pct, limits.min_perf_pct); - limits.min_perf_pct = min(limits.max_perf_pct, limits.min_perf_pct); - limits.min_perf = div_fp(int_tofp(limits.min_perf_pct), int_tofp(100)); + limits->min_sysfs_pct = clamp_t(int, input, 0 , 100); + limits->min_perf_pct = max(limits->min_policy_pct, + limits->min_sysfs_pct); + limits->min_perf_pct = min(limits->max_policy_pct, + limits->min_perf_pct); + limits->min_perf_pct = min(limits->max_perf_pct, + limits->min_perf_pct); + limits->min_perf = div_fp(int_tofp(limits->min_perf_pct), + int_tofp(100)); if (hwp_active) intel_pstate_hwp_set(); @@ -693,7 +720,7 @@ static void byt_set_pstate(struct cpudata *cpudata, int pstate) u32 vid; val = (u64)pstate << 8; - if (limits.no_turbo && !limits.turbo_disabled) + if (limits->no_turbo && !limits->turbo_disabled) val |= (u64)1 << 32; vid_fp = cpudata->vid.min + mul_fp( @@ -822,7 +849,7 @@ static void core_set_pstate(struct cpudata *cpudata, int pstate) u64 val; val = (u64)pstate << 8; - if (limits.no_turbo && !limits.turbo_disabled) + if (limits->no_turbo && !limits->turbo_disabled) val |= (u64)1 << 32; wrmsrl_on_cpu(cpudata->cpu, MSR_IA32_PERF_CTL, val); @@ -905,7 +932,7 @@ static void intel_pstate_get_min_max(struct cpudata *cpu, int *min, int *max) int max_perf_adj; int min_perf; - if (limits.no_turbo || limits.turbo_disabled) + if (limits->no_turbo || limits->turbo_disabled) max_perf = cpu->pstate.max_pstate; /* @@ -913,21 +940,21 @@ static void intel_pstate_get_min_max(struct cpudata *cpu, int *min, int *max) * policy, or by cpu specific default values determined through * experimentation. */ - if (limits.max_perf_ctl && limits.max_sysfs_pct >= - limits.max_policy_pct) { - *max = limits.max_perf_ctl; + if (limits->max_perf_ctl && limits->max_sysfs_pct >= + limits->max_policy_pct) { + *max = limits->max_perf_ctl; } else { max_perf_adj = fp_toint(mul_fp(int_tofp(max_perf), - limits.max_perf)); + limits->max_perf)); *max = clamp_t(int, max_perf_adj, cpu->pstate.min_pstate, cpu->pstate.turbo_pstate); } - if (limits.min_perf_ctl) { - *min = limits.min_perf_ctl; + if (limits->min_perf_ctl) { + *min = limits->min_perf_ctl; } else { min_perf = fp_toint(mul_fp(int_tofp(max_perf), - limits.min_perf)); + limits->min_perf)); *min = clamp_t(int, min_perf, cpu->pstate.min_pstate, max_perf); } } @@ -1215,34 +1242,35 @@ static int intel_pstate_set_policy(struct cpufreq_policy *policy) if (policy->policy == CPUFREQ_POLICY_PERFORMANCE && policy->max >= policy->cpuinfo.max_freq) { - limits.min_policy_pct = 100; - limits.min_perf_pct = 100; - limits.min_perf = int_tofp(1); - limits.max_policy_pct = 100; - limits.max_perf_pct = 100; - limits.max_perf = int_tofp(1); - limits.no_turbo = 0; - limits.max_perf_ctl = 0; - limits.min_perf_ctl = 0; + pr_debug("intel_pstate: set performance\n"); + limits = &performance_limits; return 0; } - limits.min_policy_pct = (policy->min * 100) / policy->cpuinfo.max_freq; - limits.min_policy_pct = clamp_t(int, limits.min_policy_pct, 0 , 100); - limits.max_policy_pct = (policy->max * 100) / policy->cpuinfo.max_freq; - limits.max_policy_pct = clamp_t(int, limits.max_policy_pct, 0 , 100); + pr_debug("intel_pstate: set powersave\n"); + limits = &powersave_limits; + limits->min_policy_pct = (policy->min * 100) / policy->cpuinfo.max_freq; + limits->min_policy_pct = clamp_t(int, limits->min_policy_pct, 0 , 100); + limits->max_policy_pct = (policy->max * 100) / policy->cpuinfo.max_freq; + limits->max_policy_pct = clamp_t(int, limits->max_policy_pct, 0 , 100); /* Normalize user input to [min_policy_pct, max_policy_pct] */ - limits.min_perf_pct = max(limits.min_policy_pct, limits.min_sysfs_pct); - limits.min_perf_pct = min(limits.max_policy_pct, limits.min_perf_pct); - limits.max_perf_pct = min(limits.max_policy_pct, limits.max_sysfs_pct); - limits.max_perf_pct = max(limits.min_policy_pct, limits.max_perf_pct); + limits->min_perf_pct = max(limits->min_policy_pct, + limits->min_sysfs_pct); + limits->min_perf_pct = min(limits->max_policy_pct, + limits->min_perf_pct); + limits->max_perf_pct = min(limits->max_policy_pct, + limits->max_sysfs_pct); + limits->max_perf_pct = max(limits->min_policy_pct, + limits->max_perf_pct); /* Make sure min_perf_pct <= max_perf_pct */ - limits.min_perf_pct = min(limits.max_perf_pct, limits.min_perf_pct); + limits->min_perf_pct = min(limits->max_perf_pct, limits->min_perf_pct); - limits.min_perf = div_fp(int_tofp(limits.min_perf_pct), int_tofp(100)); - limits.max_perf = div_fp(int_tofp(limits.max_perf_pct), int_tofp(100)); + limits->min_perf = div_fp(int_tofp(limits->min_perf_pct), + int_tofp(100)); + limits->max_perf = div_fp(int_tofp(limits->max_perf_pct), + int_tofp(100)); #if IS_ENABLED(CONFIG_ACPI) cpu = all_cpu_data[policy->cpu]; @@ -1251,14 +1279,14 @@ static int intel_pstate_set_policy(struct cpufreq_policy *policy) control = convert_to_native_pstate_format(cpu, i); if (control * cpu->pstate.scaling == policy->max) - limits.max_perf_ctl = control; + limits->max_perf_ctl = control; if (control * cpu->pstate.scaling == policy->min) - limits.min_perf_ctl = control; + limits->min_perf_ctl = control; } pr_debug("intel_pstate: max %u policy_max %u perf_ctl [0x%x-0x%x]\n", - policy->cpuinfo.max_freq, policy->max, limits.min_perf_ctl, - limits.max_perf_ctl); + policy->cpuinfo.max_freq, policy->max, limits->min_perf_ctl, + limits->max_perf_ctl); #endif if (hwp_active) @@ -1303,7 +1331,7 @@ static int intel_pstate_cpu_init(struct cpufreq_policy *policy) cpu = all_cpu_data[policy->cpu]; - if (limits.min_perf_pct == 100 && limits.max_perf_pct == 100) + if (limits->min_perf_pct == 100 && limits->max_perf_pct == 100) policy->policy = CPUFREQ_POLICY_PERFORMANCE; else policy->policy = CPUFREQ_POLICY_POWERSAVE; -- cgit v0.10.2 From 205ad97fc5a6386214323641dd28b822cb6fc624 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 20 Oct 2015 18:32:27 +0200 Subject: ACPI / property: Fix subnode lookup scope for data-only subnodes The correct scope for looking up the objects to generate data packages for data-only subnodes pointed to by another data-only subnode is the scope of the parent of that subnode and not the scope containing the _DSD object at the top of the hierarchy (the latter works only if all of the objects returning data-only subnode packages in a given hierarchy are in the same scope). Fix the code to work as expected. Fixes: 445b0eb058f5 (ACPI / property: Add support for data-only subnodes) Signed-off-by: Rafael J. Wysocki Reviewed-by: Mika Westerberg Tested-by: Mika Westerberg diff --git a/drivers/acpi/property.c b/drivers/acpi/property.c index 1465443..88f4306 100644 --- a/drivers/acpi/property.c +++ b/drivers/acpi/property.c @@ -71,7 +71,14 @@ static bool acpi_nondev_subnode_ok(acpi_handle scope, if (acpi_extract_properties(buf.pointer, &dn->data)) dn->handle = handle; - if (acpi_enumerate_nondev_subnodes(scope, buf.pointer, &dn->data)) + /* + * The scope for the subnode object lookup is the one of the namespace + * node (device) containing the object that has returned the package. + * That is, it's the scope of that object's parent. + */ + status = acpi_get_parent(handle, &scope); + if (ACPI_SUCCESS(status) + && acpi_enumerate_nondev_subnodes(scope, buf.pointer, &dn->data)) dn->handle = handle; if (dn->handle) { -- cgit v0.10.2 From 93556118c0679d9ed25e108a94f2b893160fce54 Mon Sep 17 00:00:00 2001 From: Bob Moore Date: Mon, 19 Oct 2015 10:24:26 +0800 Subject: ACPICA: Remove unnecessary conditional compilation ACPICA commit eea1f0e561893b6d6417913b2d224082fe3a0a5e Remove use of ACPI_DEBUGGER and ACPI_DISASSEMBLER where these defines are used around entire modules. Note: This type of code also causes problems with IDEs. Link: https://github.com/acpica/acpica/commit/eea1f0e5 Signed-off-by: Bob Moore Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/acpica/Makefile b/drivers/acpi/acpica/Makefile index fedcc16..ac78d76 100644 --- a/drivers/acpi/acpica/Makefile +++ b/drivers/acpi/acpica/Makefile @@ -123,7 +123,6 @@ acpi-y += \ rsaddr.o \ rscalc.o \ rscreate.o \ - rsdump.o \ rsdumpinfo.o \ rsinfo.o \ rsio.o \ @@ -179,6 +178,7 @@ acpi-y += \ utxfmutex.o acpi-$(ACPI_FUTURE_USAGE) += \ + rsdump.o \ utcache.o \ utfileio.o \ utprint.o \ diff --git a/drivers/acpi/acpica/acdebug.h b/drivers/acpi/acpica/acdebug.h index eb2e926..c928ba4 100644 --- a/drivers/acpi/acpica/acdebug.h +++ b/drivers/acpi/acpica/acdebug.h @@ -44,6 +44,12 @@ #ifndef __ACDEBUG_H__ #define __ACDEBUG_H__ +/* The debugger is used in conjunction with the disassembler most of time */ + +#ifdef ACPI_DISASSEMBLER +#include "acdisasm.h" +#endif + #define ACPI_DEBUG_BUFFER_SIZE 0x4000 /* 16K buffer for return objects */ struct acpi_db_command_info { diff --git a/drivers/acpi/acpica/rsdump.c b/drivers/acpi/acpica/rsdump.c index c428bb3..2a09288 100644 --- a/drivers/acpi/acpica/rsdump.c +++ b/drivers/acpi/acpica/rsdump.c @@ -51,7 +51,6 @@ ACPI_MODULE_NAME("rsdump") /* * All functions in this module are used by the AML Debugger only */ -#if defined(ACPI_DEBUGGER) /* Local prototypes */ static void acpi_rs_out_string(char *title, char *value); @@ -565,5 +564,3 @@ static void acpi_rs_dump_word_list(u16 length, u16 *data) acpi_os_printf("%25s%2.2X : %4.4X\n", "Word", i, data[i]); } } - -#endif diff --git a/include/acpi/platform/acenv.h b/include/acpi/platform/acenv.h index ec00e2b..15ef08c 100644 --- a/include/acpi/platform/acenv.h +++ b/include/acpi/platform/acenv.h @@ -142,7 +142,7 @@ #ifdef ACPI_LIBRARY #define ACPI_USE_LOCAL_CACHE -#define ACPI_FUTURE_USAGE +#define ACPI_FULL_DEBUG #endif /* Common for all ACPICA applications */ -- cgit v0.10.2 From 69058ddf28ce86296b1526d0953417f29b6df179 Mon Sep 17 00:00:00 2001 From: Bob Moore Date: Mon, 19 Oct 2015 10:24:32 +0800 Subject: ACPICA: iASL: Add symbolic operator support for Index() operator ACPICA commit fbe67c46830f10c839941f8512cac5bddcb86bd3 Index (XXXX, 2) is now supported by XXXX [2] This patch doesn't affect Linux kernel. Link: https://github.com/acpica/acpica/commit/fbe67c46 Signed-off-by: Bob Moore Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/acpica/aclocal.h b/drivers/acpi/acpica/aclocal.h index 6f70826..8a66fef 100644 --- a/drivers/acpi/acpica/aclocal.h +++ b/drivers/acpi/acpica/aclocal.h @@ -848,7 +848,7 @@ struct acpi_parse_state { #define ACPI_PARSEOP_PARAMLIST 0x02 #define ACPI_PARSEOP_EMPTY_TERMLIST 0x04 #define ACPI_PARSEOP_PREDEF_CHECKED 0x08 -#define ACPI_PARSEOP_SPECIAL 0x10 +#define ACPI_PARSEOP_CLOSING_PAREN 0x10 #define ACPI_PARSEOP_COMPOUND 0x20 #define ACPI_PARSEOP_ASSIGNMENT 0x40 -- cgit v0.10.2 From 92dc96196e96470eb9be7bc3f61f12f1571a2d30 Mon Sep 17 00:00:00 2001 From: Bob Moore Date: Mon, 19 Oct 2015 10:24:38 +0800 Subject: ACPICA: Update exception code for "file not found" error ACPICA commit ac1564c26d239348ef13455f61d5616f3961ff43 Used by the ACPICA applications. This patch is a bit broken due to non-portable inclusion as on some platforms, there is no such a header file for their lib-c exports. Fortunately, Linux doesn't compile utfileio.c for either the kernel space ACPICA core (drivers/acpi/acpica) or the userspace ACPICA tools (tools/power/acpi) for now, so it's safe to leave this patch as it is. Link: https://github.com/acpica/acpica/commit/ac1564c2 Signed-off-by: Bob Moore Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/acpica/utfileio.c b/drivers/acpi/acpica/utfileio.c index 75a94f5..d435b7b 100644 --- a/drivers/acpi/acpica/utfileio.c +++ b/drivers/acpi/acpica/utfileio.c @@ -45,6 +45,7 @@ #include "accommon.h" #include "actables.h" #include "acapps.h" +#include "errno.h" #ifdef ACPI_ASL_COMPILER #include "aslcompiler.h" @@ -301,6 +302,11 @@ acpi_ut_read_table_from_file(char *filename, struct acpi_table_header ** table) file = fopen(filename, "rb"); if (!file) { perror("Could not open input file"); + + if (errno == ENOENT) { + return (AE_NOT_EXIST); + } + return (status); } -- cgit v0.10.2 From cd64bbf8ce7b27d4a88d533883b59f79c6bbc9f8 Mon Sep 17 00:00:00 2001 From: Bob Moore Date: Mon, 19 Oct 2015 10:24:45 +0800 Subject: ACPICA: Debugger: Update mutexes used for multithreaded debugger ACPICA commit 6b2701f619040e803313363f516b200e362a9100 Make these mutex objects independent of the deadlock detection mechanism. This mechanism caused failures with the multithread debugger. This patch doesn't affect Linux kernel as debugger is currently not fully functioning in the Linux kernel. And the further debugger cleanups will take care of handling debugger command signalling correctly instead of using such kind of mutexes. So it is safe to leave this patch as it is. Link: https://github.com/acpica/acpica/commit/6b2701f6 Signed-off-by: Bob Moore Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/acpica/acglobal.h b/drivers/acpi/acpica/acglobal.h index 4dde37c..71a3c40 100644 --- a/drivers/acpi/acpica/acglobal.h +++ b/drivers/acpi/acpica/acglobal.h @@ -358,6 +358,9 @@ ACPI_GLOBAL(u16, acpi_gbl_node_type_count_misc); ACPI_GLOBAL(u32, acpi_gbl_num_nodes); ACPI_GLOBAL(u32, acpi_gbl_num_objects); +ACPI_GLOBAL(acpi_mutex, acpi_gbl_db_command_ready); +ACPI_GLOBAL(acpi_mutex, acpi_gbl_db_command_complete); + #endif /* ACPI_DEBUGGER */ /***************************************************************************** diff --git a/drivers/acpi/acpica/aclocal.h b/drivers/acpi/acpica/aclocal.h index 8a66fef..918f70d 100644 --- a/drivers/acpi/acpica/aclocal.h +++ b/drivers/acpi/acpica/aclocal.h @@ -83,10 +83,8 @@ union acpi_parse_object; #define ACPI_MTX_EVENTS 3 /* Data for ACPI events */ #define ACPI_MTX_CACHES 4 /* Internal caches, general purposes */ #define ACPI_MTX_MEMORY 5 /* Debug memory tracking lists */ -#define ACPI_MTX_DEBUG_CMD_COMPLETE 6 /* AML debugger */ -#define ACPI_MTX_DEBUG_CMD_READY 7 /* AML debugger */ -#define ACPI_MAX_MUTEX 7 +#define ACPI_MAX_MUTEX 5 #define ACPI_NUM_MUTEX ACPI_MAX_MUTEX+1 /* Lock structure for reader/writer interfaces */ diff --git a/drivers/acpi/acpica/utdecode.c b/drivers/acpi/acpica/utdecode.c index 988e23b..d452a78 100644 --- a/drivers/acpi/acpica/utdecode.c +++ b/drivers/acpi/acpica/utdecode.c @@ -407,8 +407,6 @@ static char *acpi_gbl_mutex_names[ACPI_NUM_MUTEX] = { "ACPI_MTX_Events", "ACPI_MTX_Caches", "ACPI_MTX_Memory", - "ACPI_MTX_CommandComplete", - "ACPI_MTX_CommandReady" }; char *acpi_ut_get_mutex_name(u32 mutex_id) diff --git a/drivers/acpi/acpica/utmutex.c b/drivers/acpi/acpica/utmutex.c index 37b8b58..ce406e3 100644 --- a/drivers/acpi/acpica/utmutex.c +++ b/drivers/acpi/acpica/utmutex.c @@ -108,6 +108,21 @@ acpi_status acpi_ut_mutex_initialize(void) /* Create the reader/writer lock for namespace access */ status = acpi_ut_create_rw_lock(&acpi_gbl_namespace_rw_lock); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } +#ifdef ACPI_DEBUGGER + + /* Debugger Support */ + + status = acpi_os_create_mutex(&acpi_gbl_db_command_ready); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + status = acpi_os_create_mutex(&acpi_gbl_db_command_complete); +#endif + return_ACPI_STATUS(status); } @@ -147,6 +162,12 @@ void acpi_ut_mutex_terminate(void) /* Delete the reader/writer lock */ acpi_ut_delete_rw_lock(&acpi_gbl_namespace_rw_lock); + +#ifdef ACPI_DEBUGGER + acpi_os_delete_mutex(acpi_gbl_db_command_ready); + acpi_os_delete_mutex(acpi_gbl_db_command_complete); +#endif + return_VOID; } diff --git a/include/acpi/platform/acenv.h b/include/acpi/platform/acenv.h index 15ef08c..056f245 100644 --- a/include/acpi/platform/acenv.h +++ b/include/acpi/platform/acenv.h @@ -304,11 +304,11 @@ * multi-threaded if ACPI_APPLICATION is not set. */ #ifndef DEBUGGER_THREADING -#ifdef ACPI_APPLICATION -#define DEBUGGER_THREADING DEBUGGER_SINGLE_THREADED +#if !defined (ACPI_APPLICATION) || defined (ACPI_EXEC_APP) +#define DEBUGGER_THREADING DEBUGGER_MULTI_THREADED #else -#define DEBUGGER_THREADING DEBUGGER_MULTI_THREADED +#define DEBUGGER_THREADING DEBUGGER_SINGLE_THREADED #endif #endif /* !DEBUGGER_THREADING */ -- cgit v0.10.2 From ca321d1ca6723ed0e04edd09de49c92b24e3648e Mon Sep 17 00:00:00 2001 From: Bob Moore Date: Mon, 19 Oct 2015 10:24:52 +0800 Subject: ACPICA: Update NFIT table to rename a flags field ACPICA commit 534deab97fb416a13bfede15c538e2c9eac9384a Updated one of the memory subtable flags to clarify. Link: https://github.com/acpica/acpica/commit/534deab9 Signed-off-by: Bob Moore Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/nfit.c b/drivers/acpi/nfit.c index c1b8d03..bc18aa2 100644 --- a/drivers/acpi/nfit.c +++ b/drivers/acpi/nfit.c @@ -706,7 +706,7 @@ static ssize_t flags_show(struct device *dev, flags & ACPI_NFIT_MEM_SAVE_FAILED ? "save_fail " : "", flags & ACPI_NFIT_MEM_RESTORE_FAILED ? "restore_fail " : "", flags & ACPI_NFIT_MEM_FLUSH_FAILED ? "flush_fail " : "", - flags & ACPI_NFIT_MEM_ARMED ? "not_armed " : "", + flags & ACPI_NFIT_MEM_NOT_ARMED ? "not_armed " : "", flags & ACPI_NFIT_MEM_HEALTH_OBSERVED ? "smart_event " : ""); } static DEVICE_ATTR_RO(flags); @@ -815,7 +815,7 @@ static int acpi_nfit_register_dimms(struct acpi_nfit_desc *acpi_desc) flags |= NDD_ALIASING; mem_flags = __to_nfit_memdev(nfit_mem)->flags; - if (mem_flags & ACPI_NFIT_MEM_ARMED) + if (mem_flags & ACPI_NFIT_MEM_NOT_ARMED) flags |= NDD_UNARMED; rc = acpi_nfit_add_dimm(acpi_desc, nfit_mem, device_handle); @@ -839,7 +839,7 @@ static int acpi_nfit_register_dimms(struct acpi_nfit_desc *acpi_desc) mem_flags & ACPI_NFIT_MEM_SAVE_FAILED ? " save_fail" : "", mem_flags & ACPI_NFIT_MEM_RESTORE_FAILED ? " restore_fail":"", mem_flags & ACPI_NFIT_MEM_FLUSH_FAILED ? " flush_fail" : "", - mem_flags & ACPI_NFIT_MEM_ARMED ? " not_armed" : ""); + mem_flags & ACPI_NFIT_MEM_NOT_ARMED ? " not_armed" : ""); } diff --git a/drivers/acpi/nfit.h b/drivers/acpi/nfit.h index 7e74015..329a1eb 100644 --- a/drivers/acpi/nfit.h +++ b/drivers/acpi/nfit.h @@ -24,7 +24,7 @@ #define UUID_NFIT_DIMM "4309ac30-0d11-11e4-9191-0800200c9a66" #define ACPI_NFIT_MEM_FAILED_MASK (ACPI_NFIT_MEM_SAVE_FAILED \ | ACPI_NFIT_MEM_RESTORE_FAILED | ACPI_NFIT_MEM_FLUSH_FAILED \ - | ACPI_NFIT_MEM_ARMED) + | ACPI_NFIT_MEM_NOT_ARMED) enum nfit_uuids { NFIT_SPA_VOLATILE, diff --git a/include/acpi/actbl1.h b/include/acpi/actbl1.h index fcd5709..1bb979e 100644 --- a/include/acpi/actbl1.h +++ b/include/acpi/actbl1.h @@ -1012,7 +1012,7 @@ struct acpi_nfit_memory_map { #define ACPI_NFIT_MEM_SAVE_FAILED (1) /* 00: Last SAVE to Memory Device failed */ #define ACPI_NFIT_MEM_RESTORE_FAILED (1<<1) /* 01: Last RESTORE from Memory Device failed */ #define ACPI_NFIT_MEM_FLUSH_FAILED (1<<2) /* 02: Platform flush failed */ -#define ACPI_NFIT_MEM_ARMED (1<<3) /* 03: Memory Device observed to be not armed */ +#define ACPI_NFIT_MEM_NOT_ARMED (1<<3) /* 03: Memory Device is not armed */ #define ACPI_NFIT_MEM_HEALTH_OBSERVED (1<<4) /* 04: Memory Device observed SMART/health events */ #define ACPI_NFIT_MEM_HEALTH_ENABLED (1<<5) /* 05: SMART/health events enabled */ -- cgit v0.10.2 From a5922a1f738b077c9f341f4bf3c02be436d39687 Mon Sep 17 00:00:00 2001 From: Bob Moore Date: Mon, 19 Oct 2015 10:24:58 +0800 Subject: ACPICA: Improve typechecking, both compile-time and runtime ACPICA commit 8d0f96e2a11a4ceabb2cae4b41e0ce1f4d3786b9 Adds much stricter typechecking in the iASL compiler, and also adds some additional checking in the interpreter. Link: https://github.com/acpica/acpica/commit/8d0f96e2 Signed-off-by: Bob Moore Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/acpica/aclocal.h b/drivers/acpi/acpica/aclocal.h index 918f70d..4e41b43 100644 --- a/drivers/acpi/acpica/aclocal.h +++ b/drivers/acpi/acpica/aclocal.h @@ -285,13 +285,17 @@ acpi_status(*acpi_internal_method) (struct acpi_walk_state * walk_state); #define ACPI_BTYPE_BUFFER_FIELD 0x00002000 #define ACPI_BTYPE_DDB_HANDLE 0x00004000 #define ACPI_BTYPE_DEBUG_OBJECT 0x00008000 -#define ACPI_BTYPE_REFERENCE 0x00010000 +#define ACPI_BTYPE_REFERENCE_OBJECT 0x00010000 /* From Index(), ref_of(), etc (type6_opcodes) */ #define ACPI_BTYPE_RESOURCE 0x00020000 +#define ACPI_BTYPE_NAMED_REFERENCE 0x00040000 /* Generic unresolved Name or Namepath */ #define ACPI_BTYPE_COMPUTE_DATA (ACPI_BTYPE_INTEGER | ACPI_BTYPE_STRING | ACPI_BTYPE_BUFFER) #define ACPI_BTYPE_DATA (ACPI_BTYPE_COMPUTE_DATA | ACPI_BTYPE_PACKAGE) -#define ACPI_BTYPE_DATA_REFERENCE (ACPI_BTYPE_DATA | ACPI_BTYPE_REFERENCE | ACPI_BTYPE_DDB_HANDLE) + + /* Used by Copy, de_ref_of, Store, Printf, Fprintf */ + +#define ACPI_BTYPE_DATA_REFERENCE (ACPI_BTYPE_DATA | ACPI_BTYPE_REFERENCE_OBJECT | ACPI_BTYPE_DDB_HANDLE) #define ACPI_BTYPE_DEVICE_OBJECTS (ACPI_BTYPE_DEVICE | ACPI_BTYPE_THERMAL | ACPI_BTYPE_PROCESSOR) #define ACPI_BTYPE_OBJECTS_AND_REFS 0x0001FFFF /* ARG or LOCAL */ #define ACPI_BTYPE_ALL_OBJECTS 0x0000FFFF diff --git a/drivers/acpi/acpica/acopcode.h b/drivers/acpi/acpica/acopcode.h index fd85ad0..f9acf92 100644 --- a/drivers/acpi/acpica/acopcode.h +++ b/drivers/acpi/acpica/acopcode.h @@ -211,7 +211,7 @@ #define ARGI_ARG4 ARG_NONE #define ARGI_ARG5 ARG_NONE #define ARGI_ARG6 ARG_NONE -#define ARGI_BANK_FIELD_OP ARGI_INVALID_OPCODE +#define ARGI_BANK_FIELD_OP ARGI_LIST1 (ARGI_INTEGER) #define ARGI_BIT_AND_OP ARGI_LIST3 (ARGI_INTEGER, ARGI_INTEGER, ARGI_TARGETREF) #define ARGI_BIT_NAND_OP ARGI_LIST3 (ARGI_INTEGER, ARGI_INTEGER, ARGI_TARGETREF) #define ARGI_BIT_NOR_OP ARGI_LIST3 (ARGI_INTEGER, ARGI_INTEGER, ARGI_TARGETREF) @@ -307,7 +307,7 @@ #define ARGI_SLEEP_OP ARGI_LIST1 (ARGI_INTEGER) #define ARGI_STALL_OP ARGI_LIST1 (ARGI_INTEGER) #define ARGI_STATICSTRING_OP ARGI_INVALID_OPCODE -#define ARGI_STORE_OP ARGI_LIST2 (ARGI_DATAREFOBJ, ARGI_TARGETREF) +#define ARGI_STORE_OP ARGI_LIST2 (ARGI_DATAREFOBJ, ARGI_STORE_TARGET) #define ARGI_STRING_OP ARGI_INVALID_OPCODE #define ARGI_SUBTRACT_OP ARGI_LIST3 (ARGI_INTEGER, ARGI_INTEGER, ARGI_TARGETREF) #define ARGI_THERMAL_ZONE_OP ARGI_INVALID_OPCODE diff --git a/drivers/acpi/acpica/amlcode.h b/drivers/acpi/acpica/amlcode.h index be9fd00..883f20c 100644 --- a/drivers/acpi/acpica/amlcode.h +++ b/drivers/acpi/acpica/amlcode.h @@ -277,14 +277,15 @@ #define ARGI_TARGETREF 0x0F /* Target, subject to implicit conversion */ #define ARGI_FIXED_TARGET 0x10 /* Target, no implicit conversion */ #define ARGI_SIMPLE_TARGET 0x11 /* Name, Local, Arg -- no implicit conversion */ +#define ARGI_STORE_TARGET 0x12 /* Target for store is TARGETREF + package objects */ /* Multiple/complex types */ -#define ARGI_DATAOBJECT 0x12 /* Buffer, String, package or reference to a node - Used only by size_of operator */ -#define ARGI_COMPLEXOBJ 0x13 /* Buffer, String, or package (Used by INDEX op only) */ -#define ARGI_REF_OR_STRING 0x14 /* Reference or String (Used by DEREFOF op only) */ -#define ARGI_REGION_OR_BUFFER 0x15 /* Used by LOAD op only */ -#define ARGI_DATAREFOBJ 0x16 +#define ARGI_DATAOBJECT 0x13 /* Buffer, String, package or reference to a node - Used only by size_of operator */ +#define ARGI_COMPLEXOBJ 0x14 /* Buffer, String, or package (Used by INDEX op only) */ +#define ARGI_REF_OR_STRING 0x15 /* Reference or String (Used by DEREFOF op only) */ +#define ARGI_REGION_OR_BUFFER 0x16 /* Used by LOAD op only */ +#define ARGI_DATAREFOBJ 0x17 /* Note: types above can expand to 0x1F maximum */ diff --git a/drivers/acpi/acpica/exconvrt.c b/drivers/acpi/acpica/exconvrt.c index 075d654..1e4c5b6 100644 --- a/drivers/acpi/acpica/exconvrt.c +++ b/drivers/acpi/acpica/exconvrt.c @@ -618,6 +618,7 @@ acpi_ex_convert_to_target_type(acpi_object_type destination_type, break; case ARGI_TARGETREF: + case ARGI_STORE_TARGET: switch (destination_type) { case ACPI_TYPE_INTEGER: diff --git a/drivers/acpi/acpica/exresolv.c b/drivers/acpi/acpica/exresolv.c index 7b10912..a1afe1a 100644 --- a/drivers/acpi/acpica/exresolv.c +++ b/drivers/acpi/acpica/exresolv.c @@ -209,7 +209,6 @@ acpi_ex_resolve_object_to_value(union acpi_operand_object **stack_ptr, * (i.e., dereference the package index) * Delete the ref object, increment the returned object */ - acpi_ut_remove_reference(stack_desc); acpi_ut_add_reference(obj_desc); *stack_ptr = obj_desc; } else { diff --git a/drivers/acpi/acpica/exresop.c b/drivers/acpi/acpica/exresop.c index d2964af..424442d 100644 --- a/drivers/acpi/acpica/exresop.c +++ b/drivers/acpi/acpica/exresop.c @@ -307,6 +307,8 @@ acpi_ex_resolve_operands(u16 opcode, case ARGI_TARGETREF: /* Allows implicit conversion rules before store */ case ARGI_FIXED_TARGET: /* No implicit conversion before store to target */ case ARGI_SIMPLE_TARGET: /* Name, Local, or arg - no implicit conversion */ + case ARGI_STORE_TARGET: + /* * Need an operand of type ACPI_TYPE_LOCAL_REFERENCE * A Namespace Node is OK as-is diff --git a/drivers/acpi/acpica/exstore.c b/drivers/acpi/acpica/exstore.c index a7eee24..c076e91 100644 --- a/drivers/acpi/acpica/exstore.c +++ b/drivers/acpi/acpica/exstore.c @@ -137,7 +137,7 @@ acpi_ex_store(union acpi_operand_object *source_desc, /* Destination is not a Reference object */ ACPI_ERROR((AE_INFO, - "Target is not a Reference or Constant object - %s [%p]", + "Target is not a Reference or Constant object - [%s] %p", acpi_ut_get_object_type_name(dest_desc), dest_desc)); @@ -189,7 +189,7 @@ acpi_ex_store(union acpi_operand_object *source_desc, * displayed and otherwise has no effect -- see ACPI Specification */ ACPI_DEBUG_PRINT((ACPI_DB_EXEC, - "**** Write to Debug Object: Object %p %s ****:\n\n", + "**** Write to Debug Object: Object %p [%s] ****:\n\n", source_desc, acpi_ut_get_object_type_name(source_desc))); @@ -341,7 +341,7 @@ acpi_ex_store_object_to_index(union acpi_operand_object *source_desc, /* All other types are invalid */ ACPI_ERROR((AE_INFO, - "Source must be Integer/Buffer/String type, not %s", + "Source must be type [Integer/Buffer/String], found [%s]", acpi_ut_get_object_type_name(source_desc))); return_ACPI_STATUS(AE_AML_OPERAND_TYPE); } @@ -352,8 +352,9 @@ acpi_ex_store_object_to_index(union acpi_operand_object *source_desc, break; default: - ACPI_ERROR((AE_INFO, "Target is not a Package or BufferField")); - status = AE_AML_OPERAND_TYPE; + ACPI_ERROR((AE_INFO, + "Target is not of type [Package/BufferField]")); + status = AE_AML_TARGET_TYPE; break; } @@ -373,20 +374,20 @@ acpi_ex_store_object_to_index(union acpi_operand_object *source_desc, * * DESCRIPTION: Store the object to the named object. * - * The Assignment of an object to a named object is handled here - * The value passed in will replace the current value (if any) - * with the input value. + * The assignment of an object to a named object is handled here. + * The value passed in will replace the current value (if any) + * with the input value. * - * When storing into an object the data is converted to the - * target object type then stored in the object. This means - * that the target object type (for an initialized target) will - * not be changed by a store operation. A copy_object can change - * the target type, however. + * When storing into an object the data is converted to the + * target object type then stored in the object. This means + * that the target object type (for an initialized target) will + * not be changed by a store operation. A copy_object can change + * the target type, however. * - * The implicit_conversion flag is set to NO/FALSE only when - * storing to an arg_x -- as per the rules of the ACPI spec. + * The implicit_conversion flag is set to NO/FALSE only when + * storing to an arg_x -- as per the rules of the ACPI spec. * - * Assumes parameters are already validated. + * Assumes parameters are already validated. * ******************************************************************************/ @@ -408,11 +409,75 @@ acpi_ex_store_object_to_node(union acpi_operand_object *source_desc, target_type = acpi_ns_get_type(node); target_desc = acpi_ns_get_attached_object(node); - ACPI_DEBUG_PRINT((ACPI_DB_EXEC, "Storing %p (%s) to node %p (%s)\n", + ACPI_DEBUG_PRINT((ACPI_DB_EXEC, "Storing %p [%s] to node %p [%s]\n", source_desc, acpi_ut_get_object_type_name(source_desc), node, acpi_ut_get_type_name(target_type))); + /* Only limited target types possible for everything except copy_object */ + + if (walk_state->opcode != AML_COPY_OP) { + /* + * Only copy_object allows all object types to be overwritten. For + * target_ref(s), there are restrictions on the object types that + * are allowed. + * + * Allowable operations/typing for Store: + * + * 1) Simple Store + * Integer --> Integer (Named/Local/Arg) + * String --> String (Named/Local/Arg) + * Buffer --> Buffer (Named/Local/Arg) + * Package --> Package (Named/Local/Arg) + * + * 2) Store with implicit conversion + * Integer --> String or Buffer (Named) + * String --> Integer or Buffer (Named) + * Buffer --> Integer or String (Named) + */ + switch (target_type) { + case ACPI_TYPE_PACKAGE: + /* + * Here, can only store a package to an existing package. + * Storing a package to a Local/Arg is OK, and handled + * elsewhere. + */ + if (walk_state->opcode == AML_STORE_OP) { + if (source_desc->common.type != + ACPI_TYPE_PACKAGE) { + ACPI_ERROR((AE_INFO, + "Cannot assign type [%s] to [Package] " + "(source must be type Pkg)", + acpi_ut_get_object_type_name + (source_desc))); + + return_ACPI_STATUS(AE_AML_TARGET_TYPE); + } + break; + } + + /* Fallthrough */ + + case ACPI_TYPE_DEVICE: + case ACPI_TYPE_EVENT: + case ACPI_TYPE_MUTEX: + case ACPI_TYPE_REGION: + case ACPI_TYPE_POWER: + case ACPI_TYPE_PROCESSOR: + case ACPI_TYPE_THERMAL: + + ACPI_ERROR((AE_INFO, + "Target must be [Buffer/Integer/String/Reference], found [%s] (%4.4s)", + acpi_ut_get_type_name(node->type), + node->name.ascii)); + + return_ACPI_STATUS(AE_AML_TARGET_TYPE); + + default: + break; + } + } + /* * Resolve the source object to an actual value * (If it is a reference object) @@ -425,13 +490,13 @@ acpi_ex_store_object_to_node(union acpi_operand_object *source_desc, /* Do the actual store operation */ switch (target_type) { - case ACPI_TYPE_INTEGER: - case ACPI_TYPE_STRING: - case ACPI_TYPE_BUFFER: /* * The simple data types all support implicit source operand * conversion before the store. */ + case ACPI_TYPE_INTEGER: + case ACPI_TYPE_STRING: + case ACPI_TYPE_BUFFER: if ((walk_state->opcode == AML_COPY_OP) || !implicit_conversion) { /* @@ -467,7 +532,7 @@ acpi_ex_store_object_to_node(union acpi_operand_object *source_desc, new_desc->common.type); ACPI_DEBUG_PRINT((ACPI_DB_EXEC, - "Store %s into %s via Convert/Attach\n", + "Store type [%s] into [%s] via Convert/Attach\n", acpi_ut_get_object_type_name (source_desc), acpi_ut_get_object_type_name @@ -491,15 +556,12 @@ acpi_ex_store_object_to_node(union acpi_operand_object *source_desc, default: /* - * No conversions for all other types. Directly store a copy of - * the source object. This is the ACPI spec-defined behavior for - * the copy_object operator. + * copy_object operator: No conversions for all other types. + * Instead, directly store a copy of the source object. * - * NOTE: For the Store operator, this is a departure from the - * ACPI spec, which states "If conversion is impossible, abort - * the running control method". Instead, this code implements - * "If conversion is impossible, treat the Store operation as - * a CopyObject". + * This is the ACPI spec-defined behavior for the copy_object + * operator. (Note, for this default case, all normal + * Store/Target operations exited above with an error). */ status = acpi_ex_store_direct_to_node(source_desc, node, walk_state); diff --git a/drivers/acpi/acpica/exstoren.c b/drivers/acpi/acpica/exstoren.c index 3101607..d1841de 100644 --- a/drivers/acpi/acpica/exstoren.c +++ b/drivers/acpi/acpica/exstoren.c @@ -122,9 +122,10 @@ acpi_ex_resolve_object(union acpi_operand_object **source_desc_ptr, /* Conversion successful but still not a valid type */ ACPI_ERROR((AE_INFO, - "Cannot assign type %s to %s (must be type Int/Str/Buf)", + "Cannot assign type [%s] to [%s] (must be type Int/Str/Buf)", acpi_ut_get_object_type_name(source_desc), acpi_ut_get_type_name(target_type))); + status = AE_AML_OPERAND_TYPE; } break; @@ -275,7 +276,7 @@ acpi_ex_store_object_to_object(union acpi_operand_object *source_desc, /* * All other types come here. */ - ACPI_WARNING((AE_INFO, "Store into type %s not implemented", + ACPI_WARNING((AE_INFO, "Store into type [%s] not implemented", acpi_ut_get_object_type_name(dest_desc))); status = AE_NOT_IMPLEMENTED; diff --git a/drivers/acpi/acpica/nspredef.c b/drivers/acpi/acpica/nspredef.c index 0eb5431..0c20980 100644 --- a/drivers/acpi/acpica/nspredef.c +++ b/drivers/acpi/acpica/nspredef.c @@ -226,7 +226,7 @@ acpi_ns_check_object_type(struct acpi_evaluate_info *info, { union acpi_operand_object *return_object = *return_object_ptr; acpi_status status = AE_OK; - char type_buffer[48]; /* Room for 5 types */ + char type_buffer[96]; /* Room for 10 types */ /* A Namespace node should not get here, but make sure */ diff --git a/drivers/acpi/acpica/utdecode.c b/drivers/acpi/acpica/utdecode.c index d452a78..ecaaaff 100644 --- a/drivers/acpi/acpica/utdecode.c +++ b/drivers/acpi/acpica/utdecode.c @@ -232,12 +232,27 @@ char *acpi_ut_get_type_name(acpi_object_type type) char *acpi_ut_get_object_type_name(union acpi_operand_object *obj_desc) { + ACPI_FUNCTION_TRACE(ut_get_object_type_name); if (!obj_desc) { - return ("[NULL Object Descriptor]"); + ACPI_DEBUG_PRINT((ACPI_DB_EXEC, "Null Object Descriptor\n")); + return_PTR("[NULL Object Descriptor]"); } - return (acpi_ut_get_type_name(obj_desc->common.type)); + /* These descriptor types share a common area */ + + if ((ACPI_GET_DESCRIPTOR_TYPE(obj_desc) != ACPI_DESC_TYPE_OPERAND) && + (ACPI_GET_DESCRIPTOR_TYPE(obj_desc) != ACPI_DESC_TYPE_NAMED)) { + ACPI_DEBUG_PRINT((ACPI_DB_EXEC, + "Invalid object descriptor type: 0x%2.2X [%s] (%p)\n", + ACPI_GET_DESCRIPTOR_TYPE(obj_desc), + acpi_ut_get_descriptor_name(obj_desc), + obj_desc)); + + return_PTR("Invalid object"); + } + + return_PTR(acpi_ut_get_type_name(obj_desc->common.type)); } /******************************************************************************* diff --git a/include/acpi/acexcep.h b/include/acpi/acexcep.h index 9f20eb4a..204f581 100644 --- a/include/acpi/acexcep.h +++ b/include/acpi/acexcep.h @@ -193,8 +193,9 @@ struct acpi_exception_info { #define AE_AML_ILLEGAL_ADDRESS EXCEP_AML (0x0020) #define AE_AML_INFINITE_LOOP EXCEP_AML (0x0021) #define AE_AML_UNINITIALIZED_NODE EXCEP_AML (0x0022) +#define AE_AML_TARGET_TYPE EXCEP_AML (0x0023) -#define AE_CODE_AML_MAX 0x0022 +#define AE_CODE_AML_MAX 0x0023 /* * Internal exceptions used for control @@ -358,7 +359,9 @@ static const struct acpi_exception_info acpi_gbl_exception_names_aml[] = { EXCEP_TXT("AE_AML_INFINITE_LOOP", "An apparent infinite AML While loop, method was aborted"), EXCEP_TXT("AE_AML_UNINITIALIZED_NODE", - "A namespace node is uninitialized or unresolved") + "A namespace node is uninitialized or unresolved"), + EXCEP_TXT("AE_AML_TARGET_TYPE", + "A target operand of an incorrect type was encountered") }; static const struct acpi_exception_info acpi_gbl_exception_names_ctrl[] = { -- cgit v0.10.2 From 842e71332eebf847133ea8e749c80849dae623c0 Mon Sep 17 00:00:00 2001 From: Bob Moore Date: Mon, 19 Oct 2015 10:25:04 +0800 Subject: ACPICA: iASL: General cleanup of the file suffix #defines ACPICA commit bed456ed2976bdaafdef406b982fdf6c539befc0 Removed some extraneous defines, reordered others. Link: https://github.com/acpica/acpica/commit/bed456ed Signed-off-by: Bob Moore Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/acpica/acapps.h b/drivers/acpi/acpica/acapps.h index e9f0833..e4cc48f 100644 --- a/drivers/acpi/acpica/acapps.h +++ b/drivers/acpi/acpica/acapps.h @@ -88,7 +88,7 @@ acpi_os_printf (" %-18s%s\n", name, description); #define FILE_SUFFIX_DISASSEMBLY "dsl" -#define ACPI_TABLE_FILE_SUFFIX ".dat" +#define FILE_SUFFIX_BINARY_TABLE ".dat" /* Needs the dot */ /* * getopt diff --git a/tools/power/acpi/tools/acpidump/apfiles.c b/tools/power/acpi/tools/acpidump/apfiles.c index a37f970..a1c62de 100644 --- a/tools/power/acpi/tools/acpidump/apfiles.c +++ b/tools/power/acpi/tools/acpidump/apfiles.c @@ -150,7 +150,7 @@ int ap_write_to_binary_file(struct acpi_table_header *table, u32 instance) strcat(filename, instance_str); } - strcat(filename, ACPI_TABLE_FILE_SUFFIX); + strcat(filename, FILE_SUFFIX_BINARY_TABLE); if (gbl_verbose_mode) { acpi_log_error -- cgit v0.10.2 From 9957510255724c1c746c9a6264c849e9fdd4cd24 Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Mon, 19 Oct 2015 10:25:20 +0800 Subject: ACPICA: Linuxize: Export debugger files to Linux ACPICA commit bc2d3daa4bd429611451f28800def9fea55e63de This patch exports debugger files to Linux. Link: https://github.com/acpica/acpica/commit/bc2d3daa Signed-off-by: Lv Zheng Signed-off-by: Bob Moore Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/acpica/dbcmds.c b/drivers/acpi/acpica/dbcmds.c new file mode 100644 index 0000000..30414b3 --- /dev/null +++ b/drivers/acpi/acpica/dbcmds.c @@ -0,0 +1,1187 @@ +/******************************************************************************* + * + * Module Name: dbcmds - Miscellaneous debug commands and output routines + * + ******************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acevents.h" +#include "acdebug.h" +#include "acnamesp.h" +#include "acresrc.h" +#include "actables.h" + +#define _COMPONENT ACPI_CA_DEBUGGER +ACPI_MODULE_NAME("dbcmds") + +/* Local prototypes */ +static void +acpi_dm_compare_aml_resources(u8 *aml1_buffer, + acpi_rsdesc_size aml1_buffer_length, + u8 *aml2_buffer, + acpi_rsdesc_size aml2_buffer_length); + +static acpi_status +acpi_dm_test_resource_conversion(struct acpi_namespace_node *node, char *name); + +static acpi_status +acpi_db_resource_callback(struct acpi_resource *resource, void *context); + +static acpi_status +acpi_db_device_resources(acpi_handle obj_handle, + u32 nesting_level, void *context, void **return_value); + +static void acpi_db_do_one_sleep_state(u8 sleep_state); + +static char *acpi_db_trace_method_name = NULL; + +/******************************************************************************* + * + * FUNCTION: acpi_db_convert_to_node + * + * PARAMETERS: in_string - String to convert + * + * RETURN: Pointer to a NS node + * + * DESCRIPTION: Convert a string to a valid NS pointer. Handles numeric or + * alphanumeric strings. + * + ******************************************************************************/ + +struct acpi_namespace_node *acpi_db_convert_to_node(char *in_string) +{ + struct acpi_namespace_node *node; + acpi_size address; + + if ((*in_string >= 0x30) && (*in_string <= 0x39)) { + + /* Numeric argument, convert */ + + address = strtoul(in_string, NULL, 16); + node = ACPI_TO_POINTER(address); + if (!acpi_os_readable(node, sizeof(struct acpi_namespace_node))) { + acpi_os_printf("Address %p is invalid", node); + return (NULL); + } + + /* Make sure pointer is valid NS node */ + + if (ACPI_GET_DESCRIPTOR_TYPE(node) != ACPI_DESC_TYPE_NAMED) { + acpi_os_printf + ("Address %p is not a valid namespace node [%s]\n", + node, acpi_ut_get_descriptor_name(node)); + return (NULL); + } + } else { + /* + * Alpha argument: The parameter is a name string that must be + * resolved to a Namespace object. + */ + node = acpi_db_local_ns_lookup(in_string); + if (!node) { + acpi_os_printf + ("Could not find [%s] in namespace, defaulting to root node\n", + in_string); + node = acpi_gbl_root_node; + } + } + + return (node); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_sleep + * + * PARAMETERS: object_arg - Desired sleep state (0-5). NULL means + * invoke all possible sleep states. + * + * RETURN: Status + * + * DESCRIPTION: Simulate sleep/wake sequences + * + ******************************************************************************/ + +acpi_status acpi_db_sleep(char *object_arg) +{ + u8 sleep_state; + u32 i; + + ACPI_FUNCTION_TRACE(acpi_db_sleep); + + /* Null input (no arguments) means to invoke all sleep states */ + + if (!object_arg) { + acpi_os_printf("Invoking all possible sleep states, 0-%d\n", + ACPI_S_STATES_MAX); + + for (i = 0; i <= ACPI_S_STATES_MAX; i++) { + acpi_db_do_one_sleep_state((u8)i); + } + + return_ACPI_STATUS(AE_OK); + } + + /* Convert argument to binary and invoke the sleep state */ + + sleep_state = (u8)strtoul(object_arg, NULL, 0); + acpi_db_do_one_sleep_state(sleep_state); + return_ACPI_STATUS(AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_do_one_sleep_state + * + * PARAMETERS: sleep_state - Desired sleep state (0-5) + * + * RETURN: None + * + * DESCRIPTION: Simulate a sleep/wake sequence + * + ******************************************************************************/ + +static void acpi_db_do_one_sleep_state(u8 sleep_state) +{ + acpi_status status; + u8 sleep_type_a; + u8 sleep_type_b; + + /* Validate parameter */ + + if (sleep_state > ACPI_S_STATES_MAX) { + acpi_os_printf("Sleep state %d out of range (%d max)\n", + sleep_state, ACPI_S_STATES_MAX); + return; + } + + acpi_os_printf("\n---- Invoking sleep state S%d (%s):\n", + sleep_state, acpi_gbl_sleep_state_names[sleep_state]); + + /* Get the values for the sleep type registers (for display only) */ + + status = + acpi_get_sleep_type_data(sleep_state, &sleep_type_a, &sleep_type_b); + if (ACPI_FAILURE(status)) { + acpi_os_printf("Could not evaluate [%s] method, %s\n", + acpi_gbl_sleep_state_names[sleep_state], + acpi_format_exception(status)); + return; + } + + acpi_os_printf + ("Register values for sleep state S%d: Sleep-A: %.2X, Sleep-B: %.2X\n", + sleep_state, sleep_type_a, sleep_type_b); + + /* Invoke the various sleep/wake interfaces */ + + acpi_os_printf("**** Sleep: Prepare to sleep (S%d) ****\n", + sleep_state); + status = acpi_enter_sleep_state_prep(sleep_state); + if (ACPI_FAILURE(status)) { + goto error_exit; + } + + acpi_os_printf("**** Sleep: Going to sleep (S%d) ****\n", sleep_state); + status = acpi_enter_sleep_state(sleep_state); + if (ACPI_FAILURE(status)) { + goto error_exit; + } + + acpi_os_printf("**** Wake: Prepare to return from sleep (S%d) ****\n", + sleep_state); + status = acpi_leave_sleep_state_prep(sleep_state); + if (ACPI_FAILURE(status)) { + goto error_exit; + } + + acpi_os_printf("**** Wake: Return from sleep (S%d) ****\n", + sleep_state); + status = acpi_leave_sleep_state(sleep_state); + if (ACPI_FAILURE(status)) { + goto error_exit; + } + + return; + +error_exit: + ACPI_EXCEPTION((AE_INFO, status, "During invocation of sleep state S%d", + sleep_state)); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_display_locks + * + * PARAMETERS: None + * + * RETURN: None + * + * DESCRIPTION: Display information about internal mutexes. + * + ******************************************************************************/ + +void acpi_db_display_locks(void) +{ + u32 i; + + for (i = 0; i < ACPI_MAX_MUTEX; i++) { + acpi_os_printf("%26s : %s\n", acpi_ut_get_mutex_name(i), + acpi_gbl_mutex_info[i].thread_id == + ACPI_MUTEX_NOT_ACQUIRED ? "Locked" : "Unlocked"); + } +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_display_table_info + * + * PARAMETERS: table_arg - Name of table to be displayed + * + * RETURN: None + * + * DESCRIPTION: Display information about loaded tables. Current + * implementation displays all loaded tables. + * + ******************************************************************************/ + +void acpi_db_display_table_info(char *table_arg) +{ + u32 i; + struct acpi_table_desc *table_desc; + acpi_status status; + + /* Header */ + + acpi_os_printf("Idx ID Status Type " + "TableHeader (Sig, Address, Length, Misc)\n"); + + /* Walk the entire root table list */ + + for (i = 0; i < acpi_gbl_root_table_list.current_table_count; i++) { + table_desc = &acpi_gbl_root_table_list.tables[i]; + + /* Index and Table ID */ + + acpi_os_printf("%3u %.2u ", i, table_desc->owner_id); + + /* Decode the table flags */ + + if (!(table_desc->flags & ACPI_TABLE_IS_LOADED)) { + acpi_os_printf("NotLoaded "); + } else { + acpi_os_printf(" Loaded "); + } + + switch (table_desc->flags & ACPI_TABLE_ORIGIN_MASK) { + case ACPI_TABLE_ORIGIN_EXTERNAL_VIRTUAL: + + acpi_os_printf("External/virtual "); + break; + + case ACPI_TABLE_ORIGIN_INTERNAL_PHYSICAL: + + acpi_os_printf("Internal/physical "); + break; + + case ACPI_TABLE_ORIGIN_INTERNAL_VIRTUAL: + + acpi_os_printf("Internal/virtual "); + break; + + default: + + acpi_os_printf("INVALID TYPE "); + break; + } + + /* Make sure that the table is mapped */ + + status = acpi_tb_validate_table(table_desc); + if (ACPI_FAILURE(status)) { + return; + } + + /* Dump the table header */ + + if (table_desc->pointer) { + acpi_tb_print_table_header(table_desc->address, + table_desc->pointer); + } else { + /* If the pointer is null, the table has been unloaded */ + + ACPI_INFO((AE_INFO, "%4.4s - Table has been unloaded", + table_desc->signature.ascii)); + } + } +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_unload_acpi_table + * + * PARAMETERS: object_name - Namespace pathname for an object that + * is owned by the table to be unloaded + * + * RETURN: None + * + * DESCRIPTION: Unload an ACPI table, via any namespace node that is owned + * by the table. + * + ******************************************************************************/ + +void acpi_db_unload_acpi_table(char *object_name) +{ + struct acpi_namespace_node *node; + acpi_status status; + + /* Translate name to an Named object */ + + node = acpi_db_convert_to_node(object_name); + if (!node) { + return; + } + + status = acpi_unload_parent_table(ACPI_CAST_PTR(acpi_handle, node)); + if (ACPI_SUCCESS(status)) { + acpi_os_printf("Parent of [%s] (%p) unloaded and uninstalled\n", + object_name, node); + } else { + acpi_os_printf("%s, while unloading parent table of [%s]\n", + acpi_format_exception(status), object_name); + } +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_send_notify + * + * PARAMETERS: name - Name of ACPI object where to send notify + * value - Value of the notify to send. + * + * RETURN: None + * + * DESCRIPTION: Send an ACPI notification. The value specified is sent to the + * named object as an ACPI notify. + * + ******************************************************************************/ + +void acpi_db_send_notify(char *name, u32 value) +{ + struct acpi_namespace_node *node; + acpi_status status; + + /* Translate name to an Named object */ + + node = acpi_db_convert_to_node(name); + if (!node) { + return; + } + + /* Dispatch the notify if legal */ + + if (acpi_ev_is_notify_object(node)) { + status = acpi_ev_queue_notify_request(node, value); + if (ACPI_FAILURE(status)) { + acpi_os_printf("Could not queue notify\n"); + } + } else { + acpi_os_printf("Named object [%4.4s] Type %s, " + "must be Device/Thermal/Processor type\n", + acpi_ut_get_node_name(node), + acpi_ut_get_type_name(node->type)); + } +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_display_interfaces + * + * PARAMETERS: action_arg - Null, "install", or "remove" + * interface_name_arg - Name for install/remove options + * + * RETURN: None + * + * DESCRIPTION: Display or modify the global _OSI interface list + * + ******************************************************************************/ + +void acpi_db_display_interfaces(char *action_arg, char *interface_name_arg) +{ + struct acpi_interface_info *next_interface; + char *sub_string; + acpi_status status; + + /* If no arguments, just display current interface list */ + + if (!action_arg) { + (void)acpi_os_acquire_mutex(acpi_gbl_osi_mutex, + ACPI_WAIT_FOREVER); + + next_interface = acpi_gbl_supported_interfaces; + while (next_interface) { + if (!(next_interface->flags & ACPI_OSI_INVALID)) { + acpi_os_printf("%s\n", next_interface->name); + } + + next_interface = next_interface->next; + } + + acpi_os_release_mutex(acpi_gbl_osi_mutex); + return; + } + + /* If action_arg exists, so must interface_name_arg */ + + if (!interface_name_arg) { + acpi_os_printf("Missing Interface Name argument\n"); + return; + } + + /* Uppercase the action for match below */ + + acpi_ut_strupr(action_arg); + + /* install - install an interface */ + + sub_string = strstr("INSTALL", action_arg); + if (sub_string) { + status = acpi_install_interface(interface_name_arg); + if (ACPI_FAILURE(status)) { + acpi_os_printf("%s, while installing \"%s\"\n", + acpi_format_exception(status), + interface_name_arg); + } + return; + } + + /* remove - remove an interface */ + + sub_string = strstr("REMOVE", action_arg); + if (sub_string) { + status = acpi_remove_interface(interface_name_arg); + if (ACPI_FAILURE(status)) { + acpi_os_printf("%s, while removing \"%s\"\n", + acpi_format_exception(status), + interface_name_arg); + } + return; + } + + /* Invalid action_arg */ + + acpi_os_printf("Invalid action argument: %s\n", action_arg); + return; +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_display_template + * + * PARAMETERS: buffer_arg - Buffer name or address + * + * RETURN: None + * + * DESCRIPTION: Dump a buffer that contains a resource template + * + ******************************************************************************/ + +void acpi_db_display_template(char *buffer_arg) +{ + struct acpi_namespace_node *node; + acpi_status status; + struct acpi_buffer return_buffer; + + /* Translate buffer_arg to an Named object */ + + node = acpi_db_convert_to_node(buffer_arg); + if (!node || (node == acpi_gbl_root_node)) { + acpi_os_printf("Invalid argument: %s\n", buffer_arg); + return; + } + + /* We must have a buffer object */ + + if (node->type != ACPI_TYPE_BUFFER) { + acpi_os_printf + ("Not a Buffer object, cannot be a template: %s\n", + buffer_arg); + return; + } + + return_buffer.length = ACPI_DEBUG_BUFFER_SIZE; + return_buffer.pointer = acpi_gbl_db_buffer; + + /* Attempt to convert the raw buffer to a resource list */ + + status = acpi_rs_create_resource_list(node->object, &return_buffer); + + acpi_db_set_output_destination(ACPI_DB_REDIRECTABLE_OUTPUT); + acpi_dbg_level |= ACPI_LV_RESOURCES; + + if (ACPI_FAILURE(status)) { + acpi_os_printf + ("Could not convert Buffer to a resource list: %s, %s\n", + buffer_arg, acpi_format_exception(status)); + goto dump_buffer; + } + + /* Now we can dump the resource list */ + + acpi_rs_dump_resource_list(ACPI_CAST_PTR(struct acpi_resource, + return_buffer.pointer)); + +dump_buffer: + acpi_os_printf("\nRaw data buffer:\n"); + acpi_ut_debug_dump_buffer((u8 *)node->object->buffer.pointer, + node->object->buffer.length, + DB_BYTE_DISPLAY, ACPI_UINT32_MAX); + + acpi_db_set_output_destination(ACPI_DB_CONSOLE_OUTPUT); + return; +} + +/******************************************************************************* + * + * FUNCTION: acpi_dm_compare_aml_resources + * + * PARAMETERS: aml1_buffer - Contains first resource list + * aml1_buffer_length - Length of first resource list + * aml2_buffer - Contains second resource list + * aml2_buffer_length - Length of second resource list + * + * RETURN: None + * + * DESCRIPTION: Compare two AML resource lists, descriptor by descriptor (in + * order to isolate a miscompare to an individual resource) + * + ******************************************************************************/ + +static void +acpi_dm_compare_aml_resources(u8 *aml1_buffer, + acpi_rsdesc_size aml1_buffer_length, + u8 *aml2_buffer, + acpi_rsdesc_size aml2_buffer_length) +{ + u8 *aml1; + u8 *aml2; + u8 *aml1_end; + u8 *aml2_end; + acpi_rsdesc_size aml1_length; + acpi_rsdesc_size aml2_length; + acpi_rsdesc_size offset = 0; + u8 resource_type; + u32 count = 0; + u32 i; + + /* Compare overall buffer sizes (may be different due to size rounding) */ + + if (aml1_buffer_length != aml2_buffer_length) { + acpi_os_printf("**** Buffer length mismatch in converted " + "AML: Original %X, New %X ****\n", + aml1_buffer_length, aml2_buffer_length); + } + + aml1 = aml1_buffer; + aml2 = aml2_buffer; + aml1_end = aml1_buffer + aml1_buffer_length; + aml2_end = aml2_buffer + aml2_buffer_length; + + /* Walk the descriptor lists, comparing each descriptor */ + + while ((aml1 < aml1_end) && (aml2 < aml2_end)) { + + /* Get the lengths of each descriptor */ + + aml1_length = acpi_ut_get_descriptor_length(aml1); + aml2_length = acpi_ut_get_descriptor_length(aml2); + resource_type = acpi_ut_get_resource_type(aml1); + + /* Check for descriptor length match */ + + if (aml1_length != aml2_length) { + acpi_os_printf + ("**** Length mismatch in descriptor [%.2X] type %2.2X, " + "Offset %8.8X Len1 %X, Len2 %X ****\n", count, + resource_type, offset, aml1_length, aml2_length); + } + + /* Check for descriptor byte match */ + + else if (memcmp(aml1, aml2, aml1_length)) { + acpi_os_printf + ("**** Data mismatch in descriptor [%.2X] type %2.2X, " + "Offset %8.8X ****\n", count, resource_type, + offset); + + for (i = 0; i < aml1_length; i++) { + if (aml1[i] != aml2[i]) { + acpi_os_printf + ("Mismatch at byte offset %.2X: is %2.2X, " + "should be %2.2X\n", i, aml2[i], + aml1[i]); + } + } + } + + /* Exit on end_tag descriptor */ + + if (resource_type == ACPI_RESOURCE_NAME_END_TAG) { + return; + } + + /* Point to next descriptor in each buffer */ + + count++; + offset += aml1_length; + aml1 += aml1_length; + aml2 += aml2_length; + } +} + +/******************************************************************************* + * + * FUNCTION: acpi_dm_test_resource_conversion + * + * PARAMETERS: node - Parent device node + * name - resource method name (_CRS) + * + * RETURN: Status + * + * DESCRIPTION: Compare the original AML with a conversion of the AML to + * internal resource list, then back to AML. + * + ******************************************************************************/ + +static acpi_status +acpi_dm_test_resource_conversion(struct acpi_namespace_node *node, char *name) +{ + acpi_status status; + struct acpi_buffer return_buffer; + struct acpi_buffer resource_buffer; + struct acpi_buffer new_aml; + union acpi_object *original_aml; + + acpi_os_printf("Resource Conversion Comparison:\n"); + + new_aml.length = ACPI_ALLOCATE_LOCAL_BUFFER; + return_buffer.length = ACPI_ALLOCATE_LOCAL_BUFFER; + resource_buffer.length = ACPI_ALLOCATE_LOCAL_BUFFER; + + /* Get the original _CRS AML resource template */ + + status = acpi_evaluate_object(node, name, NULL, &return_buffer); + if (ACPI_FAILURE(status)) { + acpi_os_printf("Could not obtain %s: %s\n", + name, acpi_format_exception(status)); + return (status); + } + + /* Get the AML resource template, converted to internal resource structs */ + + status = acpi_get_current_resources(node, &resource_buffer); + if (ACPI_FAILURE(status)) { + acpi_os_printf("AcpiGetCurrentResources failed: %s\n", + acpi_format_exception(status)); + goto exit1; + } + + /* Convert internal resource list to external AML resource template */ + + status = acpi_rs_create_aml_resources(&resource_buffer, &new_aml); + if (ACPI_FAILURE(status)) { + acpi_os_printf("AcpiRsCreateAmlResources failed: %s\n", + acpi_format_exception(status)); + goto exit2; + } + + /* Compare original AML to the newly created AML resource list */ + + original_aml = return_buffer.pointer; + + acpi_dm_compare_aml_resources(original_aml->buffer.pointer, + (acpi_rsdesc_size) original_aml->buffer. + length, new_aml.pointer, + (acpi_rsdesc_size) new_aml.length); + + /* Cleanup and exit */ + + ACPI_FREE(new_aml.pointer); +exit2: + ACPI_FREE(resource_buffer.pointer); +exit1: + ACPI_FREE(return_buffer.pointer); + return (status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_resource_callback + * + * PARAMETERS: acpi_walk_resource_callback + * + * RETURN: Status + * + * DESCRIPTION: Simple callback to exercise acpi_walk_resources and + * acpi_walk_resource_buffer. + * + ******************************************************************************/ + +static acpi_status +acpi_db_resource_callback(struct acpi_resource *resource, void *context) +{ + + return (AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_device_resources + * + * PARAMETERS: acpi_walk_callback + * + * RETURN: Status + * + * DESCRIPTION: Display the _PRT/_CRS/_PRS resources for a device object. + * + ******************************************************************************/ + +static acpi_status +acpi_db_device_resources(acpi_handle obj_handle, + u32 nesting_level, void *context, void **return_value) +{ + struct acpi_namespace_node *node; + struct acpi_namespace_node *prt_node = NULL; + struct acpi_namespace_node *crs_node = NULL; + struct acpi_namespace_node *prs_node = NULL; + struct acpi_namespace_node *aei_node = NULL; + char *parent_path; + struct acpi_buffer return_buffer; + acpi_status status; + + node = ACPI_CAST_PTR(struct acpi_namespace_node, obj_handle); + parent_path = acpi_ns_get_external_pathname(node); + if (!parent_path) { + return (AE_NO_MEMORY); + } + + /* Get handles to the resource methods for this device */ + + (void)acpi_get_handle(node, METHOD_NAME__PRT, + ACPI_CAST_PTR(acpi_handle, &prt_node)); + (void)acpi_get_handle(node, METHOD_NAME__CRS, + ACPI_CAST_PTR(acpi_handle, &crs_node)); + (void)acpi_get_handle(node, METHOD_NAME__PRS, + ACPI_CAST_PTR(acpi_handle, &prs_node)); + (void)acpi_get_handle(node, METHOD_NAME__AEI, + ACPI_CAST_PTR(acpi_handle, &aei_node)); + + if (!prt_node && !crs_node && !prs_node && !aei_node) { + goto cleanup; /* Nothing to do */ + } + + acpi_os_printf("\nDevice: %s\n", parent_path); + + /* Prepare for a return object of arbitrary size */ + + return_buffer.pointer = acpi_gbl_db_buffer; + return_buffer.length = ACPI_DEBUG_BUFFER_SIZE; + + /* _PRT */ + + if (prt_node) { + acpi_os_printf("Evaluating _PRT\n"); + + status = + acpi_evaluate_object(prt_node, NULL, NULL, &return_buffer); + if (ACPI_FAILURE(status)) { + acpi_os_printf("Could not evaluate _PRT: %s\n", + acpi_format_exception(status)); + goto get_crs; + } + + return_buffer.pointer = acpi_gbl_db_buffer; + return_buffer.length = ACPI_DEBUG_BUFFER_SIZE; + + status = acpi_get_irq_routing_table(node, &return_buffer); + if (ACPI_FAILURE(status)) { + acpi_os_printf("GetIrqRoutingTable failed: %s\n", + acpi_format_exception(status)); + goto get_crs; + } + + acpi_rs_dump_irq_list(ACPI_CAST_PTR(u8, acpi_gbl_db_buffer)); + } + + /* _CRS */ + +get_crs: + if (crs_node) { + acpi_os_printf("Evaluating _CRS\n"); + + return_buffer.pointer = acpi_gbl_db_buffer; + return_buffer.length = ACPI_DEBUG_BUFFER_SIZE; + + status = + acpi_evaluate_object(crs_node, NULL, NULL, &return_buffer); + if (ACPI_FAILURE(status)) { + acpi_os_printf("Could not evaluate _CRS: %s\n", + acpi_format_exception(status)); + goto get_prs; + } + + /* This code exercises the acpi_walk_resources interface */ + + status = acpi_walk_resources(node, METHOD_NAME__CRS, + acpi_db_resource_callback, NULL); + if (ACPI_FAILURE(status)) { + acpi_os_printf("AcpiWalkResources failed: %s\n", + acpi_format_exception(status)); + goto get_prs; + } + + /* Get the _CRS resource list (test ALLOCATE buffer) */ + + return_buffer.pointer = NULL; + return_buffer.length = ACPI_ALLOCATE_LOCAL_BUFFER; + + status = acpi_get_current_resources(node, &return_buffer); + if (ACPI_FAILURE(status)) { + acpi_os_printf("AcpiGetCurrentResources failed: %s\n", + acpi_format_exception(status)); + goto get_prs; + } + + /* This code exercises the acpi_walk_resource_buffer interface */ + + status = acpi_walk_resource_buffer(&return_buffer, + acpi_db_resource_callback, + NULL); + if (ACPI_FAILURE(status)) { + acpi_os_printf("AcpiWalkResourceBuffer failed: %s\n", + acpi_format_exception(status)); + goto end_crs; + } + + /* Dump the _CRS resource list */ + + acpi_rs_dump_resource_list(ACPI_CAST_PTR(struct acpi_resource, + return_buffer. + pointer)); + + /* + * Perform comparison of original AML to newly created AML. This + * tests both the AML->Resource conversion and the Resource->AML + * conversion. + */ + (void)acpi_dm_test_resource_conversion(node, METHOD_NAME__CRS); + + /* Execute _SRS with the resource list */ + + acpi_os_printf("Evaluating _SRS\n"); + + status = acpi_set_current_resources(node, &return_buffer); + if (ACPI_FAILURE(status)) { + acpi_os_printf("AcpiSetCurrentResources failed: %s\n", + acpi_format_exception(status)); + goto end_crs; + } + +end_crs: + ACPI_FREE(return_buffer.pointer); + } + + /* _PRS */ + +get_prs: + if (prs_node) { + acpi_os_printf("Evaluating _PRS\n"); + + return_buffer.pointer = acpi_gbl_db_buffer; + return_buffer.length = ACPI_DEBUG_BUFFER_SIZE; + + status = + acpi_evaluate_object(prs_node, NULL, NULL, &return_buffer); + if (ACPI_FAILURE(status)) { + acpi_os_printf("Could not evaluate _PRS: %s\n", + acpi_format_exception(status)); + goto get_aei; + } + + return_buffer.pointer = acpi_gbl_db_buffer; + return_buffer.length = ACPI_DEBUG_BUFFER_SIZE; + + status = acpi_get_possible_resources(node, &return_buffer); + if (ACPI_FAILURE(status)) { + acpi_os_printf("AcpiGetPossibleResources failed: %s\n", + acpi_format_exception(status)); + goto get_aei; + } + + acpi_rs_dump_resource_list(ACPI_CAST_PTR + (struct acpi_resource, + acpi_gbl_db_buffer)); + } + + /* _AEI */ + +get_aei: + if (aei_node) { + acpi_os_printf("Evaluating _AEI\n"); + + return_buffer.pointer = acpi_gbl_db_buffer; + return_buffer.length = ACPI_DEBUG_BUFFER_SIZE; + + status = + acpi_evaluate_object(aei_node, NULL, NULL, &return_buffer); + if (ACPI_FAILURE(status)) { + acpi_os_printf("Could not evaluate _AEI: %s\n", + acpi_format_exception(status)); + goto cleanup; + } + + return_buffer.pointer = acpi_gbl_db_buffer; + return_buffer.length = ACPI_DEBUG_BUFFER_SIZE; + + status = acpi_get_event_resources(node, &return_buffer); + if (ACPI_FAILURE(status)) { + acpi_os_printf("AcpiGetEventResources failed: %s\n", + acpi_format_exception(status)); + goto cleanup; + } + + acpi_rs_dump_resource_list(ACPI_CAST_PTR + (struct acpi_resource, + acpi_gbl_db_buffer)); + } + +cleanup: + ACPI_FREE(parent_path); + return (AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_display_resources + * + * PARAMETERS: object_arg - String object name or object pointer. + * NULL or "*" means "display resources for + * all devices" + * + * RETURN: None + * + * DESCRIPTION: Display the resource objects associated with a device. + * + ******************************************************************************/ + +void acpi_db_display_resources(char *object_arg) +{ + struct acpi_namespace_node *node; + + acpi_db_set_output_destination(ACPI_DB_REDIRECTABLE_OUTPUT); + acpi_dbg_level |= ACPI_LV_RESOURCES; + + /* Asterisk means "display resources for all devices" */ + + if (!object_arg || (!strcmp(object_arg, "*"))) { + (void)acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, + ACPI_UINT32_MAX, + acpi_db_device_resources, NULL, NULL, + NULL); + } else { + /* Convert string to object pointer */ + + node = acpi_db_convert_to_node(object_arg); + if (node) { + if (node->type != ACPI_TYPE_DEVICE) { + acpi_os_printf + ("%4.4s: Name is not a device object (%s)\n", + node->name.ascii, + acpi_ut_get_type_name(node->type)); + } else { + (void)acpi_db_device_resources(node, 0, NULL, + NULL); + } + } + } + + acpi_db_set_output_destination(ACPI_DB_CONSOLE_OUTPUT); +} + +#if (!ACPI_REDUCED_HARDWARE) +/******************************************************************************* + * + * FUNCTION: acpi_db_generate_gpe + * + * PARAMETERS: gpe_arg - Raw GPE number, ascii string + * block_arg - GPE block number, ascii string + * 0 or 1 for FADT GPE blocks + * + * RETURN: None + * + * DESCRIPTION: Simulate firing of a GPE + * + ******************************************************************************/ + +void acpi_db_generate_gpe(char *gpe_arg, char *block_arg) +{ + u32 block_number = 0; + u32 gpe_number; + struct acpi_gpe_event_info *gpe_event_info; + + gpe_number = strtoul(gpe_arg, NULL, 0); + + /* + * If no block arg, or block arg == 0 or 1, use the FADT-defined + * GPE blocks. + */ + if (block_arg) { + block_number = strtoul(block_arg, NULL, 0); + if (block_number == 1) { + block_number = 0; + } + } + + gpe_event_info = + acpi_ev_get_gpe_event_info(ACPI_TO_POINTER(block_number), + gpe_number); + if (!gpe_event_info) { + acpi_os_printf("Invalid GPE\n"); + return; + } + + (void)acpi_ev_gpe_dispatch(NULL, gpe_event_info, gpe_number); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_generate_sci + * + * PARAMETERS: None + * + * RETURN: None + * + * DESCRIPTION: Simulate an SCI -- just call the SCI dispatch. + * + ******************************************************************************/ + +void acpi_db_generate_sci(void) +{ + acpi_ev_sci_dispatch(); +} + +#endif /* !ACPI_REDUCED_HARDWARE */ + +/******************************************************************************* + * + * FUNCTION: acpi_db_trace + * + * PARAMETERS: enable_arg - ENABLE/AML to enable tracer + * DISABLE to disable tracer + * method_arg - Method to trace + * once_arg - Whether trace once + * + * RETURN: None + * + * DESCRIPTION: Control method tracing facility + * + ******************************************************************************/ + +void acpi_db_trace(char *enable_arg, char *method_arg, char *once_arg) +{ + u32 debug_level = 0; + u32 debug_layer = 0; + u32 flags = 0; + + if (enable_arg) { + acpi_ut_strupr(enable_arg); + } + + if (once_arg) { + acpi_ut_strupr(once_arg); + } + + if (method_arg) { + if (acpi_db_trace_method_name) { + ACPI_FREE(acpi_db_trace_method_name); + acpi_db_trace_method_name = NULL; + } + + acpi_db_trace_method_name = + ACPI_ALLOCATE(strlen(method_arg) + 1); + if (!acpi_db_trace_method_name) { + acpi_os_printf("Failed to allocate method name (%s)\n", + method_arg); + return; + } + + strcpy(acpi_db_trace_method_name, method_arg); + } + + if (!strcmp(enable_arg, "ENABLE") || + !strcmp(enable_arg, "METHOD") || !strcmp(enable_arg, "OPCODE")) { + if (!strcmp(enable_arg, "ENABLE")) { + + /* Inherit current console settings */ + + debug_level = acpi_gbl_db_console_debug_level; + debug_layer = acpi_dbg_layer; + } else { + /* Restrict console output to trace points only */ + + debug_level = ACPI_LV_TRACE_POINT; + debug_layer = ACPI_EXECUTER; + } + + flags = ACPI_TRACE_ENABLED; + + if (!strcmp(enable_arg, "OPCODE")) { + flags |= ACPI_TRACE_OPCODE; + } + + if (once_arg && !strcmp(once_arg, "ONCE")) { + flags |= ACPI_TRACE_ONESHOT; + } + } + + (void)acpi_debug_trace(acpi_db_trace_method_name, + debug_level, debug_layer, flags); +} diff --git a/drivers/acpi/acpica/dbconvert.c b/drivers/acpi/acpica/dbconvert.c new file mode 100644 index 0000000..a71632c --- /dev/null +++ b/drivers/acpi/acpica/dbconvert.c @@ -0,0 +1,484 @@ +/******************************************************************************* + * + * Module Name: dbconvert - debugger miscellaneous conversion routines + * + ******************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acdebug.h" + +#define _COMPONENT ACPI_CA_DEBUGGER +ACPI_MODULE_NAME("dbconvert") + +#define DB_DEFAULT_PKG_ELEMENTS 33 +/******************************************************************************* + * + * FUNCTION: acpi_db_hex_char_to_value + * + * PARAMETERS: hex_char - Ascii Hex digit, 0-9|a-f|A-F + * return_value - Where the converted value is returned + * + * RETURN: Status + * + * DESCRIPTION: Convert a single hex character to a 4-bit number (0-16). + * + ******************************************************************************/ +acpi_status acpi_db_hex_char_to_value(int hex_char, u8 *return_value) +{ + u8 value; + + /* Digit must be ascii [0-9a-fA-F] */ + + if (!isxdigit(hex_char)) { + return (AE_BAD_HEX_CONSTANT); + } + + if (hex_char <= 0x39) { + value = (u8)(hex_char - 0x30); + } else { + value = (u8)(toupper(hex_char) - 0x37); + } + + *return_value = value; + return (AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_hex_byte_to_binary + * + * PARAMETERS: hex_byte - Double hex digit (0x00 - 0xFF) in format: + * hi_byte then lo_byte. + * return_value - Where the converted value is returned + * + * RETURN: Status + * + * DESCRIPTION: Convert two hex characters to an 8 bit number (0 - 255). + * + ******************************************************************************/ + +static acpi_status acpi_db_hex_byte_to_binary(char *hex_byte, u8 *return_value) +{ + u8 local0; + u8 local1; + acpi_status status; + + /* High byte */ + + status = acpi_db_hex_char_to_value(hex_byte[0], &local0); + if (ACPI_FAILURE(status)) { + return (status); + } + + /* Low byte */ + + status = acpi_db_hex_char_to_value(hex_byte[1], &local1); + if (ACPI_FAILURE(status)) { + return (status); + } + + *return_value = (u8)((local0 << 4) | local1); + return (AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_convert_to_buffer + * + * PARAMETERS: string - Input string to be converted + * object - Where the buffer object is returned + * + * RETURN: Status + * + * DESCRIPTION: Convert a string to a buffer object. String is treated a list + * of buffer elements, each separated by a space or comma. + * + ******************************************************************************/ + +static acpi_status +acpi_db_convert_to_buffer(char *string, union acpi_object *object) +{ + u32 i; + u32 j; + u32 length; + u8 *buffer; + acpi_status status; + + /* Generate the final buffer length */ + + for (i = 0, length = 0; string[i];) { + i += 2; + length++; + + while (string[i] && ((string[i] == ',') || (string[i] == ' '))) { + i++; + } + } + + buffer = ACPI_ALLOCATE(length); + if (!buffer) { + return (AE_NO_MEMORY); + } + + /* Convert the command line bytes to the buffer */ + + for (i = 0, j = 0; string[i];) { + status = acpi_db_hex_byte_to_binary(&string[i], &buffer[j]); + if (ACPI_FAILURE(status)) { + ACPI_FREE(buffer); + return (status); + } + + j++; + i += 2; + while (string[i] && ((string[i] == ',') || (string[i] == ' '))) { + i++; + } + } + + object->type = ACPI_TYPE_BUFFER; + object->buffer.pointer = buffer; + object->buffer.length = length; + return (AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_convert_to_package + * + * PARAMETERS: string - Input string to be converted + * object - Where the package object is returned + * + * RETURN: Status + * + * DESCRIPTION: Convert a string to a package object. Handles nested packages + * via recursion with acpi_db_convert_to_object. + * + ******************************************************************************/ + +acpi_status acpi_db_convert_to_package(char *string, union acpi_object * object) +{ + char *this; + char *next; + u32 i; + acpi_object_type type; + union acpi_object *elements; + acpi_status status; + + elements = + ACPI_ALLOCATE_ZEROED(DB_DEFAULT_PKG_ELEMENTS * + sizeof(union acpi_object)); + + this = string; + for (i = 0; i < (DB_DEFAULT_PKG_ELEMENTS - 1); i++) { + this = acpi_db_get_next_token(this, &next, &type); + if (!this) { + break; + } + + /* Recursive call to convert each package element */ + + status = acpi_db_convert_to_object(type, this, &elements[i]); + if (ACPI_FAILURE(status)) { + acpi_db_delete_objects(i + 1, elements); + ACPI_FREE(elements); + return (status); + } + + this = next; + } + + object->type = ACPI_TYPE_PACKAGE; + object->package.count = i; + object->package.elements = elements; + return (AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_convert_to_object + * + * PARAMETERS: type - Object type as determined by parser + * string - Input string to be converted + * object - Where the new object is returned + * + * RETURN: Status + * + * DESCRIPTION: Convert a typed and tokenized string to an union acpi_object. Typing: + * 1) String objects were surrounded by quotes. + * 2) Buffer objects were surrounded by parentheses. + * 3) Package objects were surrounded by brackets "[]". + * 4) All standalone tokens are treated as integers. + * + ******************************************************************************/ + +acpi_status +acpi_db_convert_to_object(acpi_object_type type, + char *string, union acpi_object * object) +{ + acpi_status status = AE_OK; + + switch (type) { + case ACPI_TYPE_STRING: + + object->type = ACPI_TYPE_STRING; + object->string.pointer = string; + object->string.length = (u32)strlen(string); + break; + + case ACPI_TYPE_BUFFER: + + status = acpi_db_convert_to_buffer(string, object); + break; + + case ACPI_TYPE_PACKAGE: + + status = acpi_db_convert_to_package(string, object); + break; + + default: + + object->type = ACPI_TYPE_INTEGER; + status = acpi_ut_strtoul64(string, 16, &object->integer.value); + break; + } + + return (status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_encode_pld_buffer + * + * PARAMETERS: pld_info - _PLD buffer struct (Using local struct) + * + * RETURN: Encode _PLD buffer suitable for return value from _PLD + * + * DESCRIPTION: Bit-packs a _PLD buffer struct. Used to test the _PLD macros + * + ******************************************************************************/ + +u8 *acpi_db_encode_pld_buffer(struct acpi_pld_info *pld_info) +{ + u32 *buffer; + u32 dword; + + buffer = ACPI_ALLOCATE_ZEROED(ACPI_PLD_BUFFER_SIZE); + if (!buffer) { + return (NULL); + } + + /* First 32 bits */ + + dword = 0; + ACPI_PLD_SET_REVISION(&dword, pld_info->revision); + ACPI_PLD_SET_IGNORE_COLOR(&dword, pld_info->ignore_color); + ACPI_PLD_SET_RED(&dword, pld_info->red); + ACPI_PLD_SET_GREEN(&dword, pld_info->green); + ACPI_PLD_SET_BLUE(&dword, pld_info->blue); + ACPI_MOVE_32_TO_32(&buffer[0], &dword); + + /* Second 32 bits */ + + dword = 0; + ACPI_PLD_SET_WIDTH(&dword, pld_info->width); + ACPI_PLD_SET_HEIGHT(&dword, pld_info->height); + ACPI_MOVE_32_TO_32(&buffer[1], &dword); + + /* Third 32 bits */ + + dword = 0; + ACPI_PLD_SET_USER_VISIBLE(&dword, pld_info->user_visible); + ACPI_PLD_SET_DOCK(&dword, pld_info->dock); + ACPI_PLD_SET_LID(&dword, pld_info->lid); + ACPI_PLD_SET_PANEL(&dword, pld_info->panel); + ACPI_PLD_SET_VERTICAL(&dword, pld_info->vertical_position); + ACPI_PLD_SET_HORIZONTAL(&dword, pld_info->horizontal_position); + ACPI_PLD_SET_SHAPE(&dword, pld_info->shape); + ACPI_PLD_SET_ORIENTATION(&dword, pld_info->group_orientation); + ACPI_PLD_SET_TOKEN(&dword, pld_info->group_token); + ACPI_PLD_SET_POSITION(&dword, pld_info->group_position); + ACPI_PLD_SET_BAY(&dword, pld_info->bay); + ACPI_MOVE_32_TO_32(&buffer[2], &dword); + + /* Fourth 32 bits */ + + dword = 0; + ACPI_PLD_SET_EJECTABLE(&dword, pld_info->ejectable); + ACPI_PLD_SET_OSPM_EJECT(&dword, pld_info->ospm_eject_required); + ACPI_PLD_SET_CABINET(&dword, pld_info->cabinet_number); + ACPI_PLD_SET_CARD_CAGE(&dword, pld_info->card_cage_number); + ACPI_PLD_SET_REFERENCE(&dword, pld_info->reference); + ACPI_PLD_SET_ROTATION(&dword, pld_info->rotation); + ACPI_PLD_SET_ORDER(&dword, pld_info->order); + ACPI_MOVE_32_TO_32(&buffer[3], &dword); + + if (pld_info->revision >= 2) { + + /* Fifth 32 bits */ + + dword = 0; + ACPI_PLD_SET_VERT_OFFSET(&dword, pld_info->vertical_offset); + ACPI_PLD_SET_HORIZ_OFFSET(&dword, pld_info->horizontal_offset); + ACPI_MOVE_32_TO_32(&buffer[4], &dword); + } + + return (ACPI_CAST_PTR(u8, buffer)); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_dump_pld_buffer + * + * PARAMETERS: obj_desc - Object returned from _PLD method + * + * RETURN: None. + * + * DESCRIPTION: Dumps formatted contents of a _PLD return buffer. + * + ******************************************************************************/ + +#define ACPI_PLD_OUTPUT "%20s : %-6X\n" + +void acpi_db_dump_pld_buffer(union acpi_object *obj_desc) +{ + union acpi_object *buffer_desc; + struct acpi_pld_info *pld_info; + u8 *new_buffer; + acpi_status status; + + /* Object must be of type Package with at least one Buffer element */ + + if (obj_desc->type != ACPI_TYPE_PACKAGE) { + return; + } + + buffer_desc = &obj_desc->package.elements[0]; + if (buffer_desc->type != ACPI_TYPE_BUFFER) { + return; + } + + /* Convert _PLD buffer to local _PLD struct */ + + status = acpi_decode_pld_buffer(buffer_desc->buffer.pointer, + buffer_desc->buffer.length, &pld_info); + if (ACPI_FAILURE(status)) { + return; + } + + /* Encode local _PLD struct back to a _PLD buffer */ + + new_buffer = acpi_db_encode_pld_buffer(pld_info); + if (!new_buffer) { + return; + } + + /* The two bit-packed buffers should match */ + + if (memcmp(new_buffer, buffer_desc->buffer.pointer, + buffer_desc->buffer.length)) { + acpi_os_printf + ("Converted _PLD buffer does not compare. New:\n"); + + acpi_ut_dump_buffer(new_buffer, + buffer_desc->buffer.length, DB_BYTE_DISPLAY, + 0); + } + + /* First 32-bit dword */ + + acpi_os_printf(ACPI_PLD_OUTPUT, "PLD_Revision", pld_info->revision); + acpi_os_printf(ACPI_PLD_OUTPUT, "PLD_IgnoreColor", + pld_info->ignore_color); + acpi_os_printf(ACPI_PLD_OUTPUT, "PLD_Red", pld_info->red); + acpi_os_printf(ACPI_PLD_OUTPUT, "PLD_Green", pld_info->green); + acpi_os_printf(ACPI_PLD_OUTPUT, "PLD_Blue", pld_info->blue); + + /* Second 32-bit dword */ + + acpi_os_printf(ACPI_PLD_OUTPUT, "PLD_Width", pld_info->width); + acpi_os_printf(ACPI_PLD_OUTPUT, "PLD_Height", pld_info->height); + + /* Third 32-bit dword */ + + acpi_os_printf(ACPI_PLD_OUTPUT, "PLD_UserVisible", + pld_info->user_visible); + acpi_os_printf(ACPI_PLD_OUTPUT, "PLD_Dock", pld_info->dock); + acpi_os_printf(ACPI_PLD_OUTPUT, "PLD_Lid", pld_info->lid); + acpi_os_printf(ACPI_PLD_OUTPUT, "PLD_Panel", pld_info->panel); + acpi_os_printf(ACPI_PLD_OUTPUT, "PLD_VerticalPosition", + pld_info->vertical_position); + acpi_os_printf(ACPI_PLD_OUTPUT, "PLD_HorizontalPosition", + pld_info->horizontal_position); + acpi_os_printf(ACPI_PLD_OUTPUT, "PLD_Shape", pld_info->shape); + acpi_os_printf(ACPI_PLD_OUTPUT, "PLD_GroupOrientation", + pld_info->group_orientation); + acpi_os_printf(ACPI_PLD_OUTPUT, "PLD_GroupToken", + pld_info->group_token); + acpi_os_printf(ACPI_PLD_OUTPUT, "PLD_GroupPosition", + pld_info->group_position); + acpi_os_printf(ACPI_PLD_OUTPUT, "PLD_Bay", pld_info->bay); + + /* Fourth 32-bit dword */ + + acpi_os_printf(ACPI_PLD_OUTPUT, "PLD_Ejectable", pld_info->ejectable); + acpi_os_printf(ACPI_PLD_OUTPUT, "PLD_EjectRequired", + pld_info->ospm_eject_required); + acpi_os_printf(ACPI_PLD_OUTPUT, "PLD_CabinetNumber", + pld_info->cabinet_number); + acpi_os_printf(ACPI_PLD_OUTPUT, "PLD_CardCageNumber", + pld_info->card_cage_number); + acpi_os_printf(ACPI_PLD_OUTPUT, "PLD_Reference", pld_info->reference); + acpi_os_printf(ACPI_PLD_OUTPUT, "PLD_Rotation", pld_info->rotation); + acpi_os_printf(ACPI_PLD_OUTPUT, "PLD_Order", pld_info->order); + + /* Fifth 32-bit dword */ + + if (buffer_desc->buffer.length > 16) { + acpi_os_printf(ACPI_PLD_OUTPUT, "PLD_VerticalOffset", + pld_info->vertical_offset); + acpi_os_printf(ACPI_PLD_OUTPUT, "PLD_HorizontalOffset", + pld_info->horizontal_offset); + } + + ACPI_FREE(pld_info); + ACPI_FREE(new_buffer); +} diff --git a/drivers/acpi/acpica/dbdisply.c b/drivers/acpi/acpica/dbdisply.c new file mode 100644 index 0000000..672977e --- /dev/null +++ b/drivers/acpi/acpica/dbdisply.c @@ -0,0 +1,1108 @@ +/******************************************************************************* + * + * Module Name: dbdisply - debug display commands + * + ******************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "amlcode.h" +#include "acdispat.h" +#include "acnamesp.h" +#include "acparser.h" +#include "acinterp.h" +#include "acdebug.h" + +#define _COMPONENT ACPI_CA_DEBUGGER +ACPI_MODULE_NAME("dbdisply") + +/* Local prototypes */ +static void acpi_db_dump_parser_descriptor(union acpi_parse_object *op); + +static void *acpi_db_get_pointer(void *target); + +static acpi_status +acpi_db_display_non_root_handlers(acpi_handle obj_handle, + u32 nesting_level, + void *context, void **return_value); + +/* + * System handler information. + * Used for Handlers command, in acpi_db_display_handlers. + */ +#define ACPI_PREDEFINED_PREFIX "%25s (%.2X) : " +#define ACPI_HANDLER_NAME_STRING "%30s : " +#define ACPI_HANDLER_PRESENT_STRING "%-9s (%p)\n" +#define ACPI_HANDLER_PRESENT_STRING2 "%-9s (%p)" +#define ACPI_HANDLER_NOT_PRESENT_STRING "%-9s\n" + +/* All predefined Address Space IDs */ + +static acpi_adr_space_type acpi_gbl_space_id_list[] = { + ACPI_ADR_SPACE_SYSTEM_MEMORY, + ACPI_ADR_SPACE_SYSTEM_IO, + ACPI_ADR_SPACE_PCI_CONFIG, + ACPI_ADR_SPACE_EC, + ACPI_ADR_SPACE_SMBUS, + ACPI_ADR_SPACE_CMOS, + ACPI_ADR_SPACE_PCI_BAR_TARGET, + ACPI_ADR_SPACE_IPMI, + ACPI_ADR_SPACE_GPIO, + ACPI_ADR_SPACE_GSBUS, + ACPI_ADR_SPACE_DATA_TABLE, + ACPI_ADR_SPACE_FIXED_HARDWARE +}; + +/* Global handler information */ + +typedef struct acpi_handler_info { + void *handler; + char *name; + +} acpi_handler_info; + +static struct acpi_handler_info acpi_gbl_handler_list[] = { + {&acpi_gbl_global_notify[0].handler, "System Notifications"}, + {&acpi_gbl_global_notify[1].handler, "Device Notifications"}, + {&acpi_gbl_table_handler, "ACPI Table Events"}, + {&acpi_gbl_exception_handler, "Control Method Exceptions"}, + {&acpi_gbl_interface_handler, "OSI Invocations"} +}; + +/******************************************************************************* + * + * FUNCTION: acpi_db_get_pointer + * + * PARAMETERS: target - Pointer to string to be converted + * + * RETURN: Converted pointer + * + * DESCRIPTION: Convert an ascii pointer value to a real value + * + ******************************************************************************/ + +static void *acpi_db_get_pointer(void *target) +{ + void *obj_ptr; + acpi_size address; + + address = strtoul(target, NULL, 16); + obj_ptr = ACPI_TO_POINTER(address); + return (obj_ptr); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_dump_parser_descriptor + * + * PARAMETERS: op - A parser Op descriptor + * + * RETURN: None + * + * DESCRIPTION: Display a formatted parser object + * + ******************************************************************************/ + +static void acpi_db_dump_parser_descriptor(union acpi_parse_object *op) +{ + const struct acpi_opcode_info *info; + + info = acpi_ps_get_opcode_info(op->common.aml_opcode); + + acpi_os_printf("Parser Op Descriptor:\n"); + acpi_os_printf("%20.20s : %4.4X\n", "Opcode", op->common.aml_opcode); + + ACPI_DEBUG_ONLY_MEMBERS(acpi_os_printf("%20.20s : %s\n", "Opcode Name", + info->name)); + + acpi_os_printf("%20.20s : %p\n", "Value/ArgList", op->common.value.arg); + acpi_os_printf("%20.20s : %p\n", "Parent", op->common.parent); + acpi_os_printf("%20.20s : %p\n", "NextOp", op->common.next); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_decode_and_display_object + * + * PARAMETERS: target - String with object to be displayed. Names + * and hex pointers are supported. + * output_type - Byte, Word, Dword, or Qword (B|W|D|Q) + * + * RETURN: None + * + * DESCRIPTION: Display a formatted ACPI object + * + ******************************************************************************/ + +void acpi_db_decode_and_display_object(char *target, char *output_type) +{ + void *obj_ptr; + struct acpi_namespace_node *node; + union acpi_operand_object *obj_desc; + u32 display = DB_BYTE_DISPLAY; + char buffer[80]; + struct acpi_buffer ret_buf; + acpi_status status; + u32 size; + + if (!target) { + return; + } + + /* Decode the output type */ + + if (output_type) { + acpi_ut_strupr(output_type); + if (output_type[0] == 'W') { + display = DB_WORD_DISPLAY; + } else if (output_type[0] == 'D') { + display = DB_DWORD_DISPLAY; + } else if (output_type[0] == 'Q') { + display = DB_QWORD_DISPLAY; + } + } + + ret_buf.length = sizeof(buffer); + ret_buf.pointer = buffer; + + /* Differentiate between a number and a name */ + + if ((target[0] >= 0x30) && (target[0] <= 0x39)) { + obj_ptr = acpi_db_get_pointer(target); + if (!acpi_os_readable(obj_ptr, 16)) { + acpi_os_printf + ("Address %p is invalid in this address space\n", + obj_ptr); + return; + } + + /* Decode the object type */ + + switch (ACPI_GET_DESCRIPTOR_TYPE(obj_ptr)) { + case ACPI_DESC_TYPE_NAMED: + + /* This is a namespace Node */ + + if (!acpi_os_readable + (obj_ptr, sizeof(struct acpi_namespace_node))) { + acpi_os_printf + ("Cannot read entire Named object at address %p\n", + obj_ptr); + return; + } + + node = obj_ptr; + goto dump_node; + + case ACPI_DESC_TYPE_OPERAND: + + /* This is a ACPI OPERAND OBJECT */ + + if (!acpi_os_readable + (obj_ptr, sizeof(union acpi_operand_object))) { + acpi_os_printf + ("Cannot read entire ACPI object at address %p\n", + obj_ptr); + return; + } + + acpi_ut_debug_dump_buffer(obj_ptr, + sizeof(union + acpi_operand_object), + display, ACPI_UINT32_MAX); + acpi_ex_dump_object_descriptor(obj_ptr, 1); + break; + + case ACPI_DESC_TYPE_PARSER: + + /* This is a Parser Op object */ + + if (!acpi_os_readable + (obj_ptr, sizeof(union acpi_parse_object))) { + acpi_os_printf + ("Cannot read entire Parser object at address %p\n", + obj_ptr); + return; + } + + acpi_ut_debug_dump_buffer(obj_ptr, + sizeof(union + acpi_parse_object), + display, ACPI_UINT32_MAX); + acpi_db_dump_parser_descriptor((union acpi_parse_object + *)obj_ptr); + break; + + default: + + /* Is not a recognizeable object */ + + acpi_os_printf + ("Not a known ACPI internal object, descriptor type %2.2X\n", + ACPI_GET_DESCRIPTOR_TYPE(obj_ptr)); + + size = 16; + if (acpi_os_readable(obj_ptr, 64)) { + size = 64; + } + + /* Just dump some memory */ + + acpi_ut_debug_dump_buffer(obj_ptr, size, display, + ACPI_UINT32_MAX); + break; + } + + return; + } + + /* The parameter is a name string that must be resolved to a Named obj */ + + node = acpi_db_local_ns_lookup(target); + if (!node) { + return; + } + +dump_node: + /* Now dump the NS node */ + + status = acpi_get_name(node, ACPI_FULL_PATHNAME_NO_TRAILING, &ret_buf); + if (ACPI_FAILURE(status)) { + acpi_os_printf("Could not convert name to pathname\n"); + } + + else { + acpi_os_printf("Object (%p) Pathname: %s\n", + node, (char *)ret_buf.pointer); + } + + if (!acpi_os_readable(node, sizeof(struct acpi_namespace_node))) { + acpi_os_printf("Invalid Named object at address %p\n", node); + return; + } + + acpi_ut_debug_dump_buffer((void *)node, + sizeof(struct acpi_namespace_node), display, + ACPI_UINT32_MAX); + acpi_ex_dump_namespace_node(node, 1); + + obj_desc = acpi_ns_get_attached_object(node); + if (obj_desc) { + acpi_os_printf("\nAttached Object (%p):\n", obj_desc); + if (!acpi_os_readable + (obj_desc, sizeof(union acpi_operand_object))) { + acpi_os_printf + ("Invalid internal ACPI Object at address %p\n", + obj_desc); + return; + } + + acpi_ut_debug_dump_buffer((void *)obj_desc, + sizeof(union acpi_operand_object), + display, ACPI_UINT32_MAX); + acpi_ex_dump_object_descriptor(obj_desc, 1); + } +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_display_method_info + * + * PARAMETERS: start_op - Root of the control method parse tree + * + * RETURN: None + * + * DESCRIPTION: Display information about the current method + * + ******************************************************************************/ + +void acpi_db_display_method_info(union acpi_parse_object *start_op) +{ + struct acpi_walk_state *walk_state; + union acpi_operand_object *obj_desc; + struct acpi_namespace_node *node; + union acpi_parse_object *root_op; + union acpi_parse_object *op; + const struct acpi_opcode_info *op_info; + u32 num_ops = 0; + u32 num_operands = 0; + u32 num_operators = 0; + u32 num_remaining_ops = 0; + u32 num_remaining_operands = 0; + u32 num_remaining_operators = 0; + u8 count_remaining = FALSE; + + walk_state = acpi_ds_get_current_walk_state(acpi_gbl_current_walk_list); + if (!walk_state) { + acpi_os_printf("There is no method currently executing\n"); + return; + } + + obj_desc = walk_state->method_desc; + node = walk_state->method_node; + + acpi_os_printf("Currently executing control method is [%4.4s]\n", + acpi_ut_get_node_name(node)); + acpi_os_printf("%X Arguments, SyncLevel = %X\n", + (u32)obj_desc->method.param_count, + (u32)obj_desc->method.sync_level); + + root_op = start_op; + while (root_op->common.parent) { + root_op = root_op->common.parent; + } + + op = root_op; + + while (op) { + if (op == start_op) { + count_remaining = TRUE; + } + + num_ops++; + if (count_remaining) { + num_remaining_ops++; + } + + /* Decode the opcode */ + + op_info = acpi_ps_get_opcode_info(op->common.aml_opcode); + switch (op_info->class) { + case AML_CLASS_ARGUMENT: + + if (count_remaining) { + num_remaining_operands++; + } + + num_operands++; + break; + + case AML_CLASS_UNKNOWN: + + /* Bad opcode or ASCII character */ + + continue; + + default: + + if (count_remaining) { + num_remaining_operators++; + } + + num_operators++; + break; + } + + op = acpi_ps_get_depth_next(start_op, op); + } + + acpi_os_printf + ("Method contains: %X AML Opcodes - %X Operators, %X Operands\n", + num_ops, num_operators, num_operands); + + acpi_os_printf + ("Remaining to execute: %X AML Opcodes - %X Operators, %X Operands\n", + num_remaining_ops, num_remaining_operators, + num_remaining_operands); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_display_locals + * + * PARAMETERS: None + * + * RETURN: None + * + * DESCRIPTION: Display all locals for the currently running control method + * + ******************************************************************************/ + +void acpi_db_display_locals(void) +{ + struct acpi_walk_state *walk_state; + + walk_state = acpi_ds_get_current_walk_state(acpi_gbl_current_walk_list); + if (!walk_state) { + acpi_os_printf("There is no method currently executing\n"); + return; + } + + acpi_db_decode_locals(walk_state); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_display_arguments + * + * PARAMETERS: None + * + * RETURN: None + * + * DESCRIPTION: Display all arguments for the currently running control method + * + ******************************************************************************/ + +void acpi_db_display_arguments(void) +{ + struct acpi_walk_state *walk_state; + + walk_state = acpi_ds_get_current_walk_state(acpi_gbl_current_walk_list); + if (!walk_state) { + acpi_os_printf("There is no method currently executing\n"); + return; + } + + acpi_db_decode_arguments(walk_state); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_display_results + * + * PARAMETERS: None + * + * RETURN: None + * + * DESCRIPTION: Display current contents of a method result stack + * + ******************************************************************************/ + +void acpi_db_display_results(void) +{ + u32 i; + struct acpi_walk_state *walk_state; + union acpi_operand_object *obj_desc; + u32 result_count = 0; + struct acpi_namespace_node *node; + union acpi_generic_state *frame; + u32 index; /* Index onto current frame */ + + walk_state = acpi_ds_get_current_walk_state(acpi_gbl_current_walk_list); + if (!walk_state) { + acpi_os_printf("There is no method currently executing\n"); + return; + } + + obj_desc = walk_state->method_desc; + node = walk_state->method_node; + + if (walk_state->results) { + result_count = walk_state->result_count; + } + + acpi_os_printf("Method [%4.4s] has %X stacked result objects\n", + acpi_ut_get_node_name(node), result_count); + + /* From the top element of result stack */ + + frame = walk_state->results; + index = (result_count - 1) % ACPI_RESULTS_FRAME_OBJ_NUM; + + for (i = 0; i < result_count; i++) { + obj_desc = frame->results.obj_desc[index]; + acpi_os_printf("Result%u: ", i); + acpi_db_display_internal_object(obj_desc, walk_state); + + if (index == 0) { + frame = frame->results.next; + index = ACPI_RESULTS_FRAME_OBJ_NUM; + } + + index--; + } +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_display_calling_tree + * + * PARAMETERS: None + * + * RETURN: None + * + * DESCRIPTION: Display current calling tree of nested control methods + * + ******************************************************************************/ + +void acpi_db_display_calling_tree(void) +{ + struct acpi_walk_state *walk_state; + struct acpi_namespace_node *node; + + walk_state = acpi_ds_get_current_walk_state(acpi_gbl_current_walk_list); + if (!walk_state) { + acpi_os_printf("There is no method currently executing\n"); + return; + } + + node = walk_state->method_node; + acpi_os_printf("Current Control Method Call Tree\n"); + + while (walk_state) { + node = walk_state->method_node; + acpi_os_printf(" [%4.4s]\n", acpi_ut_get_node_name(node)); + + walk_state = walk_state->next; + } +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_display_object_type + * + * PARAMETERS: name - User entered NS node handle or name + * + * RETURN: None + * + * DESCRIPTION: Display type of an arbitrary NS node + * + ******************************************************************************/ + +void acpi_db_display_object_type(char *name) +{ + struct acpi_namespace_node *node; + struct acpi_device_info *info; + acpi_status status; + u32 i; + + node = acpi_db_convert_to_node(name); + if (!node) { + return; + } + + status = acpi_get_object_info(ACPI_CAST_PTR(acpi_handle, node), &info); + if (ACPI_FAILURE(status)) { + acpi_os_printf("Could not get object info, %s\n", + acpi_format_exception(status)); + return; + } + + if (info->valid & ACPI_VALID_ADR) { + acpi_os_printf("ADR: %8.8X%8.8X, STA: %8.8X, Flags: %X\n", + ACPI_FORMAT_UINT64(info->address), + info->current_status, info->flags); + } + if (info->valid & ACPI_VALID_SXDS) { + acpi_os_printf("S1D-%2.2X S2D-%2.2X S3D-%2.2X S4D-%2.2X\n", + info->highest_dstates[0], + info->highest_dstates[1], + info->highest_dstates[2], + info->highest_dstates[3]); + } + if (info->valid & ACPI_VALID_SXWS) { + acpi_os_printf + ("S0W-%2.2X S1W-%2.2X S2W-%2.2X S3W-%2.2X S4W-%2.2X\n", + info->lowest_dstates[0], info->lowest_dstates[1], + info->lowest_dstates[2], info->lowest_dstates[3], + info->lowest_dstates[4]); + } + + if (info->valid & ACPI_VALID_HID) { + acpi_os_printf("HID: %s\n", info->hardware_id.string); + } + + if (info->valid & ACPI_VALID_UID) { + acpi_os_printf("UID: %s\n", info->unique_id.string); + } + + if (info->valid & ACPI_VALID_SUB) { + acpi_os_printf("SUB: %s\n", info->subsystem_id.string); + } + + if (info->valid & ACPI_VALID_CID) { + for (i = 0; i < info->compatible_id_list.count; i++) { + acpi_os_printf("CID %u: %s\n", i, + info->compatible_id_list.ids[i].string); + } + } + + ACPI_FREE(info); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_display_result_object + * + * PARAMETERS: obj_desc - Object to be displayed + * walk_state - Current walk state + * + * RETURN: None + * + * DESCRIPTION: Display the result of an AML opcode + * + * Note: Curently only displays the result object if we are single stepping. + * However, this output may be useful in other contexts and could be enabled + * to do so if needed. + * + ******************************************************************************/ + +void +acpi_db_display_result_object(union acpi_operand_object *obj_desc, + struct acpi_walk_state *walk_state) +{ + + /* Only display if single stepping */ + + if (!acpi_gbl_cm_single_step) { + return; + } + + acpi_os_printf("ResultObj: "); + acpi_db_display_internal_object(obj_desc, walk_state); + acpi_os_printf("\n"); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_display_argument_object + * + * PARAMETERS: obj_desc - Object to be displayed + * walk_state - Current walk state + * + * RETURN: None + * + * DESCRIPTION: Display the result of an AML opcode + * + ******************************************************************************/ + +void +acpi_db_display_argument_object(union acpi_operand_object *obj_desc, + struct acpi_walk_state *walk_state) +{ + + if (!acpi_gbl_cm_single_step) { + return; + } + + acpi_os_printf("ArgObj: "); + acpi_db_display_internal_object(obj_desc, walk_state); +} + +#if (!ACPI_REDUCED_HARDWARE) +/******************************************************************************* + * + * FUNCTION: acpi_db_display_gpes + * + * PARAMETERS: None + * + * RETURN: None + * + * DESCRIPTION: Display the current GPE structures + * + ******************************************************************************/ + +void acpi_db_display_gpes(void) +{ + struct acpi_gpe_block_info *gpe_block; + struct acpi_gpe_xrupt_info *gpe_xrupt_info; + struct acpi_gpe_event_info *gpe_event_info; + struct acpi_gpe_register_info *gpe_register_info; + char *gpe_type; + struct acpi_gpe_notify_info *notify; + u32 gpe_index; + u32 block = 0; + u32 i; + u32 j; + u32 count; + char buffer[80]; + struct acpi_buffer ret_buf; + acpi_status status; + + ret_buf.length = sizeof(buffer); + ret_buf.pointer = buffer; + + block = 0; + + /* Walk the GPE lists */ + + gpe_xrupt_info = acpi_gbl_gpe_xrupt_list_head; + while (gpe_xrupt_info) { + gpe_block = gpe_xrupt_info->gpe_block_list_head; + while (gpe_block) { + status = acpi_get_name(gpe_block->node, + ACPI_FULL_PATHNAME_NO_TRAILING, + &ret_buf); + if (ACPI_FAILURE(status)) { + acpi_os_printf + ("Could not convert name to pathname\n"); + } + + if (gpe_block->node == acpi_gbl_fadt_gpe_device) { + gpe_type = "FADT-defined GPE block"; + } else { + gpe_type = "GPE Block Device"; + } + + acpi_os_printf + ("\nBlock %u - Info %p DeviceNode %p [%s] - %s\n", + block, gpe_block, gpe_block->node, buffer, + gpe_type); + + acpi_os_printf(" Registers: %u (%u GPEs)\n", + gpe_block->register_count, + gpe_block->gpe_count); + + acpi_os_printf + (" GPE range: 0x%X to 0x%X on interrupt %u\n", + gpe_block->block_base_number, + gpe_block->block_base_number + + (gpe_block->gpe_count - 1), + gpe_xrupt_info->interrupt_number); + + acpi_os_printf + (" RegisterInfo: %p Status %8.8X%8.8X Enable %8.8X%8.8X\n", + gpe_block->register_info, + ACPI_FORMAT_UINT64(gpe_block->register_info-> + status_address.address), + ACPI_FORMAT_UINT64(gpe_block->register_info-> + enable_address.address)); + + acpi_os_printf(" EventInfo: %p\n", + gpe_block->event_info); + + /* Examine each GPE Register within the block */ + + for (i = 0; i < gpe_block->register_count; i++) { + gpe_register_info = + &gpe_block->register_info[i]; + + acpi_os_printf(" Reg %u: (GPE %.2X-%.2X) " + "RunEnable %2.2X WakeEnable %2.2X" + " Status %8.8X%8.8X Enable %8.8X%8.8X\n", + i, + gpe_register_info-> + base_gpe_number, + gpe_register_info-> + base_gpe_number + + (ACPI_GPE_REGISTER_WIDTH - 1), + gpe_register_info-> + enable_for_run, + gpe_register_info-> + enable_for_wake, + ACPI_FORMAT_UINT64 + (gpe_register_info-> + status_address.address), + ACPI_FORMAT_UINT64 + (gpe_register_info-> + enable_address.address)); + + /* Now look at the individual GPEs in this byte register */ + + for (j = 0; j < ACPI_GPE_REGISTER_WIDTH; j++) { + gpe_index = + (i * ACPI_GPE_REGISTER_WIDTH) + j; + gpe_event_info = + &gpe_block->event_info[gpe_index]; + + if (ACPI_GPE_DISPATCH_TYPE + (gpe_event_info->flags) == + ACPI_GPE_DISPATCH_NONE) { + + /* This GPE is not used (no method or handler), ignore it */ + + continue; + } + + acpi_os_printf + (" GPE %.2X: %p RunRefs %2.2X Flags %2.2X (", + gpe_block->block_base_number + + gpe_index, gpe_event_info, + gpe_event_info->runtime_count, + gpe_event_info->flags); + + /* Decode the flags byte */ + + if (gpe_event_info-> + flags & ACPI_GPE_LEVEL_TRIGGERED) { + acpi_os_printf("Level, "); + } else { + acpi_os_printf("Edge, "); + } + + if (gpe_event_info-> + flags & ACPI_GPE_CAN_WAKE) { + acpi_os_printf("CanWake, "); + } else { + acpi_os_printf("RunOnly, "); + } + + switch (ACPI_GPE_DISPATCH_TYPE + (gpe_event_info->flags)) { + case ACPI_GPE_DISPATCH_NONE: + + acpi_os_printf("NotUsed"); + break; + + case ACPI_GPE_DISPATCH_METHOD: + + acpi_os_printf("Method"); + break; + + case ACPI_GPE_DISPATCH_HANDLER: + + acpi_os_printf("Handler"); + break; + + case ACPI_GPE_DISPATCH_NOTIFY: + + count = 0; + notify = + gpe_event_info->dispatch. + notify_list; + while (notify) { + count++; + notify = notify->next; + } + + acpi_os_printf + ("Implicit Notify on %u devices", + count); + break; + + case ACPI_GPE_DISPATCH_RAW_HANDLER: + + acpi_os_printf("RawHandler"); + break; + + default: + + acpi_os_printf("UNKNOWN: %X", + ACPI_GPE_DISPATCH_TYPE + (gpe_event_info-> + flags)); + break; + } + + acpi_os_printf(")\n"); + } + } + + block++; + gpe_block = gpe_block->next; + } + + gpe_xrupt_info = gpe_xrupt_info->next; + } +} +#endif /* !ACPI_REDUCED_HARDWARE */ + +/******************************************************************************* + * + * FUNCTION: acpi_db_display_handlers + * + * PARAMETERS: None + * + * RETURN: None + * + * DESCRIPTION: Display the currently installed global handlers + * + ******************************************************************************/ + +void acpi_db_display_handlers(void) +{ + union acpi_operand_object *obj_desc; + union acpi_operand_object *handler_obj; + acpi_adr_space_type space_id; + u32 i; + + /* Operation region handlers */ + + acpi_os_printf("\nOperation Region Handlers at the namespace root:\n"); + + obj_desc = acpi_ns_get_attached_object(acpi_gbl_root_node); + if (obj_desc) { + for (i = 0; i < ACPI_ARRAY_LENGTH(acpi_gbl_space_id_list); i++) { + space_id = acpi_gbl_space_id_list[i]; + handler_obj = obj_desc->device.handler; + + acpi_os_printf(ACPI_PREDEFINED_PREFIX, + acpi_ut_get_region_name((u8)space_id), + space_id); + + while (handler_obj) { + if (acpi_gbl_space_id_list[i] == + handler_obj->address_space.space_id) { + acpi_os_printf + (ACPI_HANDLER_PRESENT_STRING, + (handler_obj->address_space. + handler_flags & + ACPI_ADDR_HANDLER_DEFAULT_INSTALLED) + ? "Default" : "User", + handler_obj->address_space. + handler); + + goto found_handler; + } + + handler_obj = handler_obj->address_space.next; + } + + /* There is no handler for this space_id */ + + acpi_os_printf("None\n"); + +found_handler: ; + } + + /* Find all handlers for user-defined space_IDs */ + + handler_obj = obj_desc->device.handler; + while (handler_obj) { + if (handler_obj->address_space.space_id >= + ACPI_USER_REGION_BEGIN) { + acpi_os_printf(ACPI_PREDEFINED_PREFIX, + "User-defined ID", + handler_obj->address_space. + space_id); + acpi_os_printf(ACPI_HANDLER_PRESENT_STRING, + (handler_obj->address_space. + handler_flags & + ACPI_ADDR_HANDLER_DEFAULT_INSTALLED) + ? "Default" : "User", + handler_obj->address_space. + handler); + } + + handler_obj = handler_obj->address_space.next; + } + } +#if (!ACPI_REDUCED_HARDWARE) + + /* Fixed event handlers */ + + acpi_os_printf("\nFixed Event Handlers:\n"); + + for (i = 0; i < ACPI_NUM_FIXED_EVENTS; i++) { + acpi_os_printf(ACPI_PREDEFINED_PREFIX, + acpi_ut_get_event_name(i), i); + if (acpi_gbl_fixed_event_handlers[i].handler) { + acpi_os_printf(ACPI_HANDLER_PRESENT_STRING, "User", + acpi_gbl_fixed_event_handlers[i]. + handler); + } else { + acpi_os_printf(ACPI_HANDLER_NOT_PRESENT_STRING, "None"); + } + } + +#endif /* !ACPI_REDUCED_HARDWARE */ + + /* Miscellaneous global handlers */ + + acpi_os_printf("\nMiscellaneous Global Handlers:\n"); + + for (i = 0; i < ACPI_ARRAY_LENGTH(acpi_gbl_handler_list); i++) { + acpi_os_printf(ACPI_HANDLER_NAME_STRING, + acpi_gbl_handler_list[i].name); + + if (acpi_gbl_handler_list[i].handler) { + acpi_os_printf(ACPI_HANDLER_PRESENT_STRING, "User", + acpi_gbl_handler_list[i].handler); + } else { + acpi_os_printf(ACPI_HANDLER_NOT_PRESENT_STRING, "None"); + } + } + + /* Other handlers that are installed throughout the namespace */ + + acpi_os_printf("\nOperation Region Handlers for specific devices:\n"); + + (void)acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, + ACPI_UINT32_MAX, + acpi_db_display_non_root_handlers, NULL, NULL, + NULL); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_display_non_root_handlers + * + * PARAMETERS: acpi_walk_callback + * + * RETURN: Status + * + * DESCRIPTION: Display information about all handlers installed for a + * device object. + * + ******************************************************************************/ + +static acpi_status +acpi_db_display_non_root_handlers(acpi_handle obj_handle, + u32 nesting_level, + void *context, void **return_value) +{ + struct acpi_namespace_node *node = + ACPI_CAST_PTR(struct acpi_namespace_node, obj_handle); + union acpi_operand_object *obj_desc; + union acpi_operand_object *handler_obj; + char *pathname; + + obj_desc = acpi_ns_get_attached_object(node); + if (!obj_desc) { + return (AE_OK); + } + + pathname = acpi_ns_get_external_pathname(node); + if (!pathname) { + return (AE_OK); + } + + /* Display all handlers associated with this device */ + + handler_obj = obj_desc->device.handler; + while (handler_obj) { + acpi_os_printf(ACPI_PREDEFINED_PREFIX, + acpi_ut_get_region_name((u8)handler_obj-> + address_space.space_id), + handler_obj->address_space.space_id); + + acpi_os_printf(ACPI_HANDLER_PRESENT_STRING2, + (handler_obj->address_space.handler_flags & + ACPI_ADDR_HANDLER_DEFAULT_INSTALLED) ? "Default" + : "User", handler_obj->address_space.handler); + + acpi_os_printf(" Device Name: %s (%p)\n", pathname, node); + + handler_obj = handler_obj->address_space.next; + } + + ACPI_FREE(pathname); + return (AE_OK); +} diff --git a/drivers/acpi/acpica/dbexec.c b/drivers/acpi/acpica/dbexec.c new file mode 100644 index 0000000..8eef298 --- /dev/null +++ b/drivers/acpi/acpica/dbexec.c @@ -0,0 +1,753 @@ +/******************************************************************************* + * + * Module Name: dbexec - debugger control method execution + * + ******************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acdebug.h" +#include "acnamesp.h" + +#define _COMPONENT ACPI_CA_DEBUGGER +ACPI_MODULE_NAME("dbexec") + +static struct acpi_db_method_info acpi_gbl_db_method_info; + +/* Local prototypes */ + +static acpi_status +acpi_db_execute_method(struct acpi_db_method_info *info, + struct acpi_buffer *return_obj); + +static acpi_status acpi_db_execute_setup(struct acpi_db_method_info *info); + +static u32 acpi_db_get_outstanding_allocations(void); + +static void ACPI_SYSTEM_XFACE acpi_db_method_thread(void *context); + +static acpi_status +acpi_db_execution_walk(acpi_handle obj_handle, + u32 nesting_level, void *context, void **return_value); + +/******************************************************************************* + * + * FUNCTION: acpi_db_delete_objects + * + * PARAMETERS: count - Count of objects in the list + * objects - Array of ACPI_OBJECTs to be deleted + * + * RETURN: None + * + * DESCRIPTION: Delete a list of ACPI_OBJECTS. Handles packages and nested + * packages via recursion. + * + ******************************************************************************/ + +void acpi_db_delete_objects(u32 count, union acpi_object *objects) +{ + u32 i; + + for (i = 0; i < count; i++) { + switch (objects[i].type) { + case ACPI_TYPE_BUFFER: + + ACPI_FREE(objects[i].buffer.pointer); + break; + + case ACPI_TYPE_PACKAGE: + + /* Recursive call to delete package elements */ + + acpi_db_delete_objects(objects[i].package.count, + objects[i].package.elements); + + /* Free the elements array */ + + ACPI_FREE(objects[i].package.elements); + break; + + default: + + break; + } + } +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_execute_method + * + * PARAMETERS: info - Valid info segment + * return_obj - Where to put return object + * + * RETURN: Status + * + * DESCRIPTION: Execute a control method. + * + ******************************************************************************/ + +static acpi_status +acpi_db_execute_method(struct acpi_db_method_info *info, + struct acpi_buffer *return_obj) +{ + acpi_status status; + struct acpi_object_list param_objects; + union acpi_object params[ACPI_DEBUGGER_MAX_ARGS + 1]; + u32 i; + + ACPI_FUNCTION_TRACE(db_execute_method); + + if (acpi_gbl_db_output_to_file && !acpi_dbg_level) { + acpi_os_printf("Warning: debug output is not enabled!\n"); + } + + param_objects.count = 0; + param_objects.pointer = NULL; + + /* Pass through any command-line arguments */ + + if (info->args && info->args[0]) { + + /* Get arguments passed on the command line */ + + for (i = 0; (info->args[i] && *(info->args[i])); i++) { + + /* Convert input string (token) to an actual union acpi_object */ + + status = acpi_db_convert_to_object(info->types[i], + info->args[i], + ¶ms[i]); + if (ACPI_FAILURE(status)) { + ACPI_EXCEPTION((AE_INFO, status, + "While parsing method arguments")); + goto cleanup; + } + } + + param_objects.count = i; + param_objects.pointer = params; + } + + /* Prepare for a return object of arbitrary size */ + + return_obj->pointer = acpi_gbl_db_buffer; + return_obj->length = ACPI_DEBUG_BUFFER_SIZE; + + /* Do the actual method execution */ + + acpi_gbl_method_executing = TRUE; + status = acpi_evaluate_object(NULL, info->pathname, + ¶m_objects, return_obj); + + acpi_gbl_cm_single_step = FALSE; + acpi_gbl_method_executing = FALSE; + + if (ACPI_FAILURE(status)) { + ACPI_EXCEPTION((AE_INFO, status, + "while executing %s from debugger", + info->pathname)); + + if (status == AE_BUFFER_OVERFLOW) { + ACPI_ERROR((AE_INFO, + "Possible overflow of internal debugger " + "buffer (size 0x%X needed 0x%X)", + ACPI_DEBUG_BUFFER_SIZE, + (u32)return_obj->length)); + } + } + +cleanup: + acpi_db_delete_objects(param_objects.count, params); + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_execute_setup + * + * PARAMETERS: info - Valid method info + * + * RETURN: None + * + * DESCRIPTION: Setup info segment prior to method execution + * + ******************************************************************************/ + +static acpi_status acpi_db_execute_setup(struct acpi_db_method_info *info) +{ + acpi_status status; + + ACPI_FUNCTION_NAME(db_execute_setup); + + /* Catenate the current scope to the supplied name */ + + info->pathname[0] = 0; + if ((info->name[0] != '\\') && (info->name[0] != '/')) { + if (acpi_ut_safe_strcat(info->pathname, sizeof(info->pathname), + acpi_gbl_db_scope_buf)) { + status = AE_BUFFER_OVERFLOW; + goto error_exit; + } + } + + if (acpi_ut_safe_strcat(info->pathname, sizeof(info->pathname), + info->name)) { + status = AE_BUFFER_OVERFLOW; + goto error_exit; + } + + acpi_db_prep_namestring(info->pathname); + + acpi_db_set_output_destination(ACPI_DB_DUPLICATE_OUTPUT); + acpi_os_printf("Evaluating %s\n", info->pathname); + + if (info->flags & EX_SINGLE_STEP) { + acpi_gbl_cm_single_step = TRUE; + acpi_db_set_output_destination(ACPI_DB_CONSOLE_OUTPUT); + } + + else { + /* No single step, allow redirection to a file */ + + acpi_db_set_output_destination(ACPI_DB_REDIRECTABLE_OUTPUT); + } + + return (AE_OK); + +error_exit: + + ACPI_EXCEPTION((AE_INFO, status, "During setup for method execution")); + return (status); +} + +#ifdef ACPI_DBG_TRACK_ALLOCATIONS +u32 acpi_db_get_cache_info(struct acpi_memory_list *cache) +{ + + return (cache->total_allocated - cache->total_freed - + cache->current_depth); +} +#endif + +/******************************************************************************* + * + * FUNCTION: acpi_db_get_outstanding_allocations + * + * PARAMETERS: None + * + * RETURN: Current global allocation count minus cache entries + * + * DESCRIPTION: Determine the current number of "outstanding" allocations -- + * those allocations that have not been freed and also are not + * in one of the various object caches. + * + ******************************************************************************/ + +static u32 acpi_db_get_outstanding_allocations(void) +{ + u32 outstanding = 0; + +#ifdef ACPI_DBG_TRACK_ALLOCATIONS + + outstanding += acpi_db_get_cache_info(acpi_gbl_state_cache); + outstanding += acpi_db_get_cache_info(acpi_gbl_ps_node_cache); + outstanding += acpi_db_get_cache_info(acpi_gbl_ps_node_ext_cache); + outstanding += acpi_db_get_cache_info(acpi_gbl_operand_cache); +#endif + + return (outstanding); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_execution_walk + * + * PARAMETERS: WALK_CALLBACK + * + * RETURN: Status + * + * DESCRIPTION: Execute a control method. Name is relative to the current + * scope. + * + ******************************************************************************/ + +static acpi_status +acpi_db_execution_walk(acpi_handle obj_handle, + u32 nesting_level, void *context, void **return_value) +{ + union acpi_operand_object *obj_desc; + struct acpi_namespace_node *node = + (struct acpi_namespace_node *)obj_handle; + struct acpi_buffer return_obj; + acpi_status status; + + obj_desc = acpi_ns_get_attached_object(node); + if (obj_desc->method.param_count) { + return (AE_OK); + } + + return_obj.pointer = NULL; + return_obj.length = ACPI_ALLOCATE_BUFFER; + + acpi_ns_print_node_pathname(node, "Evaluating"); + + /* Do the actual method execution */ + + acpi_os_printf("\n"); + acpi_gbl_method_executing = TRUE; + + status = acpi_evaluate_object(node, NULL, NULL, &return_obj); + + acpi_os_printf("Evaluation of [%4.4s] returned %s\n", + acpi_ut_get_node_name(node), + acpi_format_exception(status)); + + acpi_gbl_method_executing = FALSE; + return (AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_execute + * + * PARAMETERS: name - Name of method to execute + * args - Parameters to the method + * Types - + * flags - single step/no single step + * + * RETURN: None + * + * DESCRIPTION: Execute a control method. Name is relative to the current + * scope. + * + ******************************************************************************/ + +void +acpi_db_execute(char *name, char **args, acpi_object_type * types, u32 flags) +{ + acpi_status status; + struct acpi_buffer return_obj; + char *name_string; + +#ifdef ACPI_DEBUG_OUTPUT + u32 previous_allocations; + u32 allocations; + + /* Memory allocation tracking */ + + previous_allocations = acpi_db_get_outstanding_allocations(); +#endif + + if (*name == '*') { + (void)acpi_walk_namespace(ACPI_TYPE_METHOD, ACPI_ROOT_OBJECT, + ACPI_UINT32_MAX, + acpi_db_execution_walk, NULL, NULL, + NULL); + return; + } else { + name_string = ACPI_ALLOCATE(strlen(name) + 1); + if (!name_string) { + return; + } + + memset(&acpi_gbl_db_method_info, 0, + sizeof(struct acpi_db_method_info)); + + strcpy(name_string, name); + acpi_ut_strupr(name_string); + acpi_gbl_db_method_info.name = name_string; + acpi_gbl_db_method_info.args = args; + acpi_gbl_db_method_info.types = types; + acpi_gbl_db_method_info.flags = flags; + + return_obj.pointer = NULL; + return_obj.length = ACPI_ALLOCATE_BUFFER; + + status = acpi_db_execute_setup(&acpi_gbl_db_method_info); + if (ACPI_FAILURE(status)) { + ACPI_FREE(name_string); + return; + } + + /* Get the NS node, determines existence also */ + + status = acpi_get_handle(NULL, acpi_gbl_db_method_info.pathname, + &acpi_gbl_db_method_info.method); + if (ACPI_SUCCESS(status)) { + status = + acpi_db_execute_method(&acpi_gbl_db_method_info, + &return_obj); + } + ACPI_FREE(name_string); + } + + /* + * Allow any handlers in separate threads to complete. + * (Such as Notify handlers invoked from AML executed above). + */ + acpi_os_sleep((u64)10); + +#ifdef ACPI_DEBUG_OUTPUT + + /* Memory allocation tracking */ + + allocations = + acpi_db_get_outstanding_allocations() - previous_allocations; + + acpi_db_set_output_destination(ACPI_DB_DUPLICATE_OUTPUT); + + if (allocations > 0) { + acpi_os_printf + ("0x%X Outstanding allocations after evaluation of %s\n", + allocations, acpi_gbl_db_method_info.pathname); + } +#endif + + if (ACPI_FAILURE(status)) { + acpi_os_printf("Evaluation of %s failed with status %s\n", + acpi_gbl_db_method_info.pathname, + acpi_format_exception(status)); + } else { + /* Display a return object, if any */ + + if (return_obj.length) { + acpi_os_printf("Evaluation of %s returned object %p, " + "external buffer length %X\n", + acpi_gbl_db_method_info.pathname, + return_obj.pointer, + (u32)return_obj.length); + + acpi_db_dump_external_object(return_obj.pointer, 1); + + /* Dump a _PLD buffer if present */ + + if (ACPI_COMPARE_NAME + ((ACPI_CAST_PTR + (struct acpi_namespace_node, + acpi_gbl_db_method_info.method)->name.ascii), + METHOD_NAME__PLD)) { + acpi_db_dump_pld_buffer(return_obj.pointer); + } + } else { + acpi_os_printf + ("No object was returned from evaluation of %s\n", + acpi_gbl_db_method_info.pathname); + } + } + + acpi_db_set_output_destination(ACPI_DB_CONSOLE_OUTPUT); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_method_thread + * + * PARAMETERS: context - Execution info segment + * + * RETURN: None + * + * DESCRIPTION: Debugger execute thread. Waits for a command line, then + * simply dispatches it. + * + ******************************************************************************/ + +static void ACPI_SYSTEM_XFACE acpi_db_method_thread(void *context) +{ + acpi_status status; + struct acpi_db_method_info *info = context; + struct acpi_db_method_info local_info; + u32 i; + u8 allow; + struct acpi_buffer return_obj; + + /* + * acpi_gbl_db_method_info.Arguments will be passed as method arguments. + * Prevent acpi_gbl_db_method_info from being modified by multiple threads + * concurrently. + * + * Note: The arguments we are passing are used by the ASL test suite + * (aslts). Do not change them without updating the tests. + */ + (void)acpi_os_wait_semaphore(info->info_gate, 1, ACPI_WAIT_FOREVER); + + if (info->init_args) { + acpi_db_uint32_to_hex_string(info->num_created, + info->index_of_thread_str); + acpi_db_uint32_to_hex_string((u32)acpi_os_get_thread_id(), + info->id_of_thread_str); + } + + if (info->threads && (info->num_created < info->num_threads)) { + info->threads[info->num_created++] = acpi_os_get_thread_id(); + } + + local_info = *info; + local_info.args = local_info.arguments; + local_info.arguments[0] = local_info.num_threads_str; + local_info.arguments[1] = local_info.id_of_thread_str; + local_info.arguments[2] = local_info.index_of_thread_str; + local_info.arguments[3] = NULL; + + local_info.types = local_info.arg_types; + + (void)acpi_os_signal_semaphore(info->info_gate, 1); + + for (i = 0; i < info->num_loops; i++) { + status = acpi_db_execute_method(&local_info, &return_obj); + if (ACPI_FAILURE(status)) { + acpi_os_printf + ("%s During evaluation of %s at iteration %X\n", + acpi_format_exception(status), info->pathname, i); + if (status == AE_ABORT_METHOD) { + break; + } + } +#if 0 + if ((i % 100) == 0) { + acpi_os_printf("%u loops, Thread 0x%x\n", + i, acpi_os_get_thread_id()); + } + + if (return_obj.length) { + acpi_os_printf + ("Evaluation of %s returned object %p Buflen %X\n", + info->pathname, return_obj.pointer, + (u32)return_obj.length); + acpi_db_dump_external_object(return_obj.pointer, 1); + } +#endif + } + + /* Signal our completion */ + + allow = 0; + (void)acpi_os_wait_semaphore(info->thread_complete_gate, + 1, ACPI_WAIT_FOREVER); + info->num_completed++; + + if (info->num_completed == info->num_threads) { + + /* Do signal for main thread once only */ + allow = 1; + } + + (void)acpi_os_signal_semaphore(info->thread_complete_gate, 1); + + if (allow) { + status = acpi_os_signal_semaphore(info->main_thread_gate, 1); + if (ACPI_FAILURE(status)) { + acpi_os_printf + ("Could not signal debugger thread sync semaphore, %s\n", + acpi_format_exception(status)); + } + } +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_create_execution_threads + * + * PARAMETERS: num_threads_arg - Number of threads to create + * num_loops_arg - Loop count for the thread(s) + * method_name_arg - Control method to execute + * + * RETURN: None + * + * DESCRIPTION: Create threads to execute method(s) + * + ******************************************************************************/ + +void +acpi_db_create_execution_threads(char *num_threads_arg, + char *num_loops_arg, char *method_name_arg) +{ + acpi_status status; + u32 num_threads; + u32 num_loops; + u32 i; + u32 size; + acpi_mutex main_thread_gate; + acpi_mutex thread_complete_gate; + acpi_mutex info_gate; + + /* Get the arguments */ + + num_threads = strtoul(num_threads_arg, NULL, 0); + num_loops = strtoul(num_loops_arg, NULL, 0); + + if (!num_threads || !num_loops) { + acpi_os_printf("Bad argument: Threads %X, Loops %X\n", + num_threads, num_loops); + return; + } + + /* + * Create the semaphore for synchronization of + * the created threads with the main thread. + */ + status = acpi_os_create_semaphore(1, 0, &main_thread_gate); + if (ACPI_FAILURE(status)) { + acpi_os_printf("Could not create semaphore for " + "synchronization with the main thread, %s\n", + acpi_format_exception(status)); + return; + } + + /* + * Create the semaphore for synchronization + * between the created threads. + */ + status = acpi_os_create_semaphore(1, 1, &thread_complete_gate); + if (ACPI_FAILURE(status)) { + acpi_os_printf("Could not create semaphore for " + "synchronization between the created threads, %s\n", + acpi_format_exception(status)); + + (void)acpi_os_delete_semaphore(main_thread_gate); + return; + } + + status = acpi_os_create_semaphore(1, 1, &info_gate); + if (ACPI_FAILURE(status)) { + acpi_os_printf("Could not create semaphore for " + "synchronization of AcpiGbl_DbMethodInfo, %s\n", + acpi_format_exception(status)); + + (void)acpi_os_delete_semaphore(thread_complete_gate); + (void)acpi_os_delete_semaphore(main_thread_gate); + return; + } + + memset(&acpi_gbl_db_method_info, 0, sizeof(struct acpi_db_method_info)); + + /* Array to store IDs of threads */ + + acpi_gbl_db_method_info.num_threads = num_threads; + size = sizeof(acpi_thread_id) * acpi_gbl_db_method_info.num_threads; + + acpi_gbl_db_method_info.threads = acpi_os_allocate(size); + if (acpi_gbl_db_method_info.threads == NULL) { + acpi_os_printf("No memory for thread IDs array\n"); + (void)acpi_os_delete_semaphore(main_thread_gate); + (void)acpi_os_delete_semaphore(thread_complete_gate); + (void)acpi_os_delete_semaphore(info_gate); + return; + } + memset(acpi_gbl_db_method_info.threads, 0, size); + + /* Setup the context to be passed to each thread */ + + acpi_gbl_db_method_info.name = method_name_arg; + acpi_gbl_db_method_info.flags = 0; + acpi_gbl_db_method_info.num_loops = num_loops; + acpi_gbl_db_method_info.main_thread_gate = main_thread_gate; + acpi_gbl_db_method_info.thread_complete_gate = thread_complete_gate; + acpi_gbl_db_method_info.info_gate = info_gate; + + /* Init arguments to be passed to method */ + + acpi_gbl_db_method_info.init_args = 1; + acpi_gbl_db_method_info.args = acpi_gbl_db_method_info.arguments; + acpi_gbl_db_method_info.arguments[0] = + acpi_gbl_db_method_info.num_threads_str; + acpi_gbl_db_method_info.arguments[1] = + acpi_gbl_db_method_info.id_of_thread_str; + acpi_gbl_db_method_info.arguments[2] = + acpi_gbl_db_method_info.index_of_thread_str; + acpi_gbl_db_method_info.arguments[3] = NULL; + + acpi_gbl_db_method_info.types = acpi_gbl_db_method_info.arg_types; + acpi_gbl_db_method_info.arg_types[0] = ACPI_TYPE_INTEGER; + acpi_gbl_db_method_info.arg_types[1] = ACPI_TYPE_INTEGER; + acpi_gbl_db_method_info.arg_types[2] = ACPI_TYPE_INTEGER; + + acpi_db_uint32_to_hex_string(num_threads, + acpi_gbl_db_method_info.num_threads_str); + + status = acpi_db_execute_setup(&acpi_gbl_db_method_info); + if (ACPI_FAILURE(status)) { + goto cleanup_and_exit; + } + + /* Get the NS node, determines existence also */ + + status = acpi_get_handle(NULL, acpi_gbl_db_method_info.pathname, + &acpi_gbl_db_method_info.method); + if (ACPI_FAILURE(status)) { + acpi_os_printf("%s Could not get handle for %s\n", + acpi_format_exception(status), + acpi_gbl_db_method_info.pathname); + goto cleanup_and_exit; + } + + /* Create the threads */ + + acpi_os_printf("Creating %X threads to execute %X times each\n", + num_threads, num_loops); + + for (i = 0; i < (num_threads); i++) { + status = + acpi_os_execute(OSL_DEBUGGER_THREAD, acpi_db_method_thread, + &acpi_gbl_db_method_info); + if (ACPI_FAILURE(status)) { + break; + } + } + + /* Wait for all threads to complete */ + + (void)acpi_os_wait_semaphore(main_thread_gate, 1, ACPI_WAIT_FOREVER); + + acpi_db_set_output_destination(ACPI_DB_DUPLICATE_OUTPUT); + acpi_os_printf("All threads (%X) have completed\n", num_threads); + acpi_db_set_output_destination(ACPI_DB_CONSOLE_OUTPUT); + +cleanup_and_exit: + + /* Cleanup and exit */ + + (void)acpi_os_delete_semaphore(main_thread_gate); + (void)acpi_os_delete_semaphore(thread_complete_gate); + (void)acpi_os_delete_semaphore(info_gate); + + acpi_os_free(acpi_gbl_db_method_info.threads); + acpi_gbl_db_method_info.threads = NULL; +} diff --git a/drivers/acpi/acpica/dbfileio.c b/drivers/acpi/acpica/dbfileio.c new file mode 100644 index 0000000..d0e6b20 --- /dev/null +++ b/drivers/acpi/acpica/dbfileio.c @@ -0,0 +1,256 @@ +/******************************************************************************* + * + * Module Name: dbfileio - Debugger file I/O commands. These can't usually + * be used when running the debugger in Ring 0 (Kernel mode) + * + ******************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acdebug.h" +#include "actables.h" + +#define _COMPONENT ACPI_CA_DEBUGGER +ACPI_MODULE_NAME("dbfileio") + +#ifdef ACPI_DEBUGGER +/******************************************************************************* + * + * FUNCTION: acpi_db_close_debug_file + * + * PARAMETERS: None + * + * RETURN: None + * + * DESCRIPTION: If open, close the current debug output file + * + ******************************************************************************/ +void acpi_db_close_debug_file(void) +{ + +#ifdef ACPI_APPLICATION + + if (acpi_gbl_debug_file) { + fclose(acpi_gbl_debug_file); + acpi_gbl_debug_file = NULL; + acpi_gbl_db_output_to_file = FALSE; + acpi_os_printf("Debug output file %s closed\n", + acpi_gbl_db_debug_filename); + } +#endif +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_open_debug_file + * + * PARAMETERS: name - Filename to open + * + * RETURN: None + * + * DESCRIPTION: Open a file where debug output will be directed. + * + ******************************************************************************/ + +void acpi_db_open_debug_file(char *name) +{ + +#ifdef ACPI_APPLICATION + + acpi_db_close_debug_file(); + acpi_gbl_debug_file = fopen(name, "w+"); + if (!acpi_gbl_debug_file) { + acpi_os_printf("Could not open debug file %s\n", name); + return; + } + + acpi_os_printf("Debug output file %s opened\n", name); + strncpy(acpi_gbl_db_debug_filename, name, + sizeof(acpi_gbl_db_debug_filename)); + acpi_gbl_db_output_to_file = TRUE; + +#endif +} +#endif + +#ifdef ACPI_APPLICATION +#include "acapps.h" + +/******************************************************************************* + * + * FUNCTION: ae_local_load_table + * + * PARAMETERS: table - pointer to a buffer containing the entire + * table to be loaded + * + * RETURN: Status + * + * DESCRIPTION: This function is called to load a table from the caller's + * buffer. The buffer must contain an entire ACPI Table including + * a valid header. The header fields will be verified, and if it + * is determined that the table is invalid, the call will fail. + * + ******************************************************************************/ + +static acpi_status ae_local_load_table(struct acpi_table_header *table) +{ + acpi_status status = AE_OK; + + ACPI_FUNCTION_TRACE(ae_local_load_table); + +#if 0 +/* struct acpi_table_desc table_info; */ + + if (!table) { + return_ACPI_STATUS(AE_BAD_PARAMETER); + } + + table_info.pointer = table; + status = acpi_tb_recognize_table(&table_info, ACPI_TABLE_ALL); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + /* Install the new table into the local data structures */ + + status = acpi_tb_init_table_descriptor(&table_info); + if (ACPI_FAILURE(status)) { + if (status == AE_ALREADY_EXISTS) { + + /* Table already exists, no error */ + + status = AE_OK; + } + + /* Free table allocated by acpi_tb_get_table */ + + acpi_tb_delete_single_table(&table_info); + return_ACPI_STATUS(status); + } +#if (!defined (ACPI_NO_METHOD_EXECUTION) && !defined (ACPI_CONSTANT_EVAL_ONLY)) + + status = + acpi_ns_load_table(table_info.installed_desc, acpi_gbl_root_node); + if (ACPI_FAILURE(status)) { + + /* Uninstall table and free the buffer */ + + acpi_tb_delete_tables_by_type(ACPI_TABLE_ID_DSDT); + return_ACPI_STATUS(status); + } +#endif +#endif + + return_ACPI_STATUS(status); +} +#endif + +/******************************************************************************* + * + * FUNCTION: acpi_db_get_table_from_file + * + * PARAMETERS: filename - File where table is located + * return_table - Where a pointer to the table is returned + * + * RETURN: Status + * + * DESCRIPTION: Load an ACPI table from a file + * + ******************************************************************************/ + +acpi_status +acpi_db_get_table_from_file(char *filename, + struct acpi_table_header **return_table, + u8 must_be_aml_file) +{ +#ifdef ACPI_APPLICATION + acpi_status status; + struct acpi_table_header *table; + u8 is_aml_table = TRUE; + + status = acpi_ut_read_table_from_file(filename, &table); + if (ACPI_FAILURE(status)) { + return (status); + } + + if (must_be_aml_file) { + is_aml_table = acpi_ut_is_aml_table(table); + if (!is_aml_table) { + ACPI_EXCEPTION((AE_INFO, AE_OK, + "Input for -e is not an AML table: " + "\"%4.4s\" (must be DSDT/SSDT)", + table->signature)); + return (AE_TYPE); + } + } + + if (is_aml_table) { + + /* Attempt to recognize and install the table */ + + status = ae_local_load_table(table); + if (ACPI_FAILURE(status)) { + if (status == AE_ALREADY_EXISTS) { + acpi_os_printf + ("Table %4.4s is already installed\n", + table->signature); + } else { + acpi_os_printf("Could not install table, %s\n", + acpi_format_exception(status)); + } + + return (status); + } + + acpi_tb_print_table_header(0, table); + + fprintf(stderr, + "Acpi table [%4.4s] successfully installed and loaded\n", + table->signature); + } + + acpi_gbl_acpi_hardware_present = FALSE; + if (return_table) { + *return_table = table; + } + +#endif /* ACPI_APPLICATION */ + return (AE_OK); +} diff --git a/drivers/acpi/acpica/dbhistry.c b/drivers/acpi/acpica/dbhistry.c new file mode 100644 index 0000000..9c66a9e --- /dev/null +++ b/drivers/acpi/acpica/dbhistry.c @@ -0,0 +1,239 @@ +/****************************************************************************** + * + * Module Name: dbhistry - debugger HISTORY command + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acdebug.h" + +#define _COMPONENT ACPI_CA_DEBUGGER +ACPI_MODULE_NAME("dbhistry") + +#define HI_NO_HISTORY 0 +#define HI_RECORD_HISTORY 1 +#define HISTORY_SIZE 40 +typedef struct history_info { + char *command; + u32 cmd_num; + +} HISTORY_INFO; + +static HISTORY_INFO acpi_gbl_history_buffer[HISTORY_SIZE]; +static u16 acpi_gbl_lo_history = 0; +static u16 acpi_gbl_num_history = 0; +static u16 acpi_gbl_next_history_index = 0; +u32 acpi_gbl_next_cmd_num = 1; + +/******************************************************************************* + * + * FUNCTION: acpi_db_add_to_history + * + * PARAMETERS: command_line - Command to add + * + * RETURN: None + * + * DESCRIPTION: Add a command line to the history buffer. + * + ******************************************************************************/ + +void acpi_db_add_to_history(char *command_line) +{ + u16 cmd_len; + u16 buffer_len; + + /* Put command into the next available slot */ + + cmd_len = (u16)strlen(command_line); + if (!cmd_len) { + return; + } + + if (acpi_gbl_history_buffer[acpi_gbl_next_history_index].command != + NULL) { + buffer_len = + (u16) + strlen(acpi_gbl_history_buffer[acpi_gbl_next_history_index]. + command); + + if (cmd_len > buffer_len) { + acpi_os_free(acpi_gbl_history_buffer + [acpi_gbl_next_history_index].command); + acpi_gbl_history_buffer[acpi_gbl_next_history_index]. + command = acpi_os_allocate(cmd_len + 1); + } + } else { + acpi_gbl_history_buffer[acpi_gbl_next_history_index].command = + acpi_os_allocate(cmd_len + 1); + } + + strcpy(acpi_gbl_history_buffer[acpi_gbl_next_history_index].command, + command_line); + + acpi_gbl_history_buffer[acpi_gbl_next_history_index].cmd_num = + acpi_gbl_next_cmd_num; + + /* Adjust indexes */ + + if ((acpi_gbl_num_history == HISTORY_SIZE) && + (acpi_gbl_next_history_index == acpi_gbl_lo_history)) { + acpi_gbl_lo_history++; + if (acpi_gbl_lo_history >= HISTORY_SIZE) { + acpi_gbl_lo_history = 0; + } + } + + acpi_gbl_next_history_index++; + if (acpi_gbl_next_history_index >= HISTORY_SIZE) { + acpi_gbl_next_history_index = 0; + } + + acpi_gbl_next_cmd_num++; + if (acpi_gbl_num_history < HISTORY_SIZE) { + acpi_gbl_num_history++; + } +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_display_history + * + * PARAMETERS: None + * + * RETURN: None + * + * DESCRIPTION: Display the contents of the history buffer + * + ******************************************************************************/ + +void acpi_db_display_history(void) +{ + u32 i; + u16 history_index; + + history_index = acpi_gbl_lo_history; + + /* Dump entire history buffer */ + + for (i = 0; i < acpi_gbl_num_history; i++) { + if (acpi_gbl_history_buffer[history_index].command) { + acpi_os_printf("%3ld %s\n", + acpi_gbl_history_buffer[history_index]. + cmd_num, + acpi_gbl_history_buffer[history_index]. + command); + } + + history_index++; + if (history_index >= HISTORY_SIZE) { + history_index = 0; + } + } +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_get_from_history + * + * PARAMETERS: command_num_arg - String containing the number of the + * command to be retrieved + * + * RETURN: Pointer to the retrieved command. Null on error. + * + * DESCRIPTION: Get a command from the history buffer + * + ******************************************************************************/ + +char *acpi_db_get_from_history(char *command_num_arg) +{ + u32 cmd_num; + + if (command_num_arg == NULL) { + cmd_num = acpi_gbl_next_cmd_num - 1; + } + + else { + cmd_num = strtoul(command_num_arg, NULL, 0); + } + + return (acpi_db_get_history_by_index(cmd_num)); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_get_history_by_index + * + * PARAMETERS: cmd_num - Index of the desired history entry. + * Values are 0...(acpi_gbl_next_cmd_num - 1) + * + * RETURN: Pointer to the retrieved command. Null on error. + * + * DESCRIPTION: Get a command from the history buffer + * + ******************************************************************************/ + +char *acpi_db_get_history_by_index(u32 cmd_num) +{ + u32 i; + u16 history_index; + + /* Search history buffer */ + + history_index = acpi_gbl_lo_history; + for (i = 0; i < acpi_gbl_num_history; i++) { + if (acpi_gbl_history_buffer[history_index].cmd_num == cmd_num) { + + /* Found the command, return it */ + + return (acpi_gbl_history_buffer[history_index].command); + } + + /* History buffer is circular */ + + history_index++; + if (history_index >= HISTORY_SIZE) { + history_index = 0; + } + } + + acpi_os_printf("Invalid history number: %u\n", history_index); + return (NULL); +} diff --git a/drivers/acpi/acpica/dbinput.c b/drivers/acpi/acpica/dbinput.c new file mode 100644 index 0000000..7f1b6ec --- /dev/null +++ b/drivers/acpi/acpica/dbinput.c @@ -0,0 +1,1274 @@ +/******************************************************************************* + * + * Module Name: dbinput - user front-end to the AML debugger + * + ******************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acdebug.h" + +#define _COMPONENT ACPI_CA_DEBUGGER +ACPI_MODULE_NAME("dbinput") + +/* Local prototypes */ +static u32 acpi_db_get_line(char *input_buffer); + +static u32 acpi_db_match_command(char *user_command); + +static void acpi_db_single_thread(void); + +static void acpi_db_display_command_info(char *command, u8 display_all); + +static void acpi_db_display_help(char *command); + +static u8 +acpi_db_match_command_help(char *command, + const struct acpi_db_command_help *help); + +/* + * Top-level debugger commands. + * + * This list of commands must match the string table below it + */ +enum acpi_ex_debugger_commands { + CMD_NOT_FOUND = 0, + CMD_NULL, + CMD_ALLOCATIONS, + CMD_ARGS, + CMD_ARGUMENTS, + CMD_BREAKPOINT, + CMD_BUSINFO, + CMD_CALL, + CMD_DEBUG, + CMD_DISASSEMBLE, + CMD_DISASM, + CMD_DUMP, + CMD_EVALUATE, + CMD_EXECUTE, + CMD_EXIT, + CMD_FIND, + CMD_GO, + CMD_HANDLERS, + CMD_HELP, + CMD_HELP2, + CMD_HISTORY, + CMD_HISTORY_EXE, + CMD_HISTORY_LAST, + CMD_INFORMATION, + CMD_INTEGRITY, + CMD_INTO, + CMD_LEVEL, + CMD_LIST, + CMD_LOCALS, + CMD_LOCKS, + CMD_METHODS, + CMD_NAMESPACE, + CMD_NOTIFY, + CMD_OBJECTS, + CMD_OSI, + CMD_OWNER, + CMD_PATHS, + CMD_PREDEFINED, + CMD_PREFIX, + CMD_QUIT, + CMD_REFERENCES, + CMD_RESOURCES, + CMD_RESULTS, + CMD_SET, + CMD_STATS, + CMD_STOP, + CMD_TABLES, + CMD_TEMPLATE, + CMD_TRACE, + CMD_TREE, + CMD_TYPE, +#ifdef ACPI_APPLICATION + CMD_ENABLEACPI, + CMD_EVENT, + CMD_GPE, + CMD_GPES, + CMD_SCI, + CMD_SLEEP, + + CMD_CLOSE, + CMD_LOAD, + CMD_OPEN, + CMD_UNLOAD, + + CMD_TERMINATE, + CMD_THREADS, + + CMD_TEST, +#endif +}; + +#define CMD_FIRST_VALID 2 + +/* Second parameter is the required argument count */ + +static const struct acpi_db_command_info acpi_gbl_db_commands[] = { + {"", 0}, + {"", 0}, + {"ALLOCATIONS", 0}, + {"ARGS", 0}, + {"ARGUMENTS", 0}, + {"BREAKPOINT", 1}, + {"BUSINFO", 0}, + {"CALL", 0}, + {"DEBUG", 1}, + {"DISASSEMBLE", 1}, + {"DISASM", 1}, + {"DUMP", 1}, + {"EVALUATE", 1}, + {"EXECUTE", 1}, + {"EXIT", 0}, + {"FIND", 1}, + {"GO", 0}, + {"HANDLERS", 0}, + {"HELP", 0}, + {"?", 0}, + {"HISTORY", 0}, + {"!", 1}, + {"!!", 0}, + {"INFORMATION", 0}, + {"INTEGRITY", 0}, + {"INTO", 0}, + {"LEVEL", 0}, + {"LIST", 0}, + {"LOCALS", 0}, + {"LOCKS", 0}, + {"METHODS", 0}, + {"NAMESPACE", 0}, + {"NOTIFY", 2}, + {"OBJECTS", 0}, + {"OSI", 0}, + {"OWNER", 1}, + {"PATHS", 0}, + {"PREDEFINED", 0}, + {"PREFIX", 0}, + {"QUIT", 0}, + {"REFERENCES", 1}, + {"RESOURCES", 0}, + {"RESULTS", 0}, + {"SET", 3}, + {"STATS", 1}, + {"STOP", 0}, + {"TABLES", 0}, + {"TEMPLATE", 1}, + {"TRACE", 1}, + {"TREE", 0}, + {"TYPE", 1}, +#ifdef ACPI_APPLICATION + {"ENABLEACPI", 0}, + {"EVENT", 1}, + {"GPE", 1}, + {"GPES", 0}, + {"SCI", 0}, + {"SLEEP", 0}, + + {"CLOSE", 0}, + {"LOAD", 1}, + {"OPEN", 1}, + {"UNLOAD", 1}, + + {"TERMINATE", 0}, + {"THREADS", 3}, + + {"TEST", 1}, +#endif + {NULL, 0} +}; + +/* + * Help for all debugger commands. First argument is the number of lines + * of help to output for the command. + */ +static const struct acpi_db_command_help acpi_gbl_db_command_help[] = { + {0, "\nGeneral-Purpose Commands:", "\n"}, + {1, " Allocations", "Display list of current memory allocations\n"}, + {2, " Dump
|", "\n"}, + {0, " [Byte|Word|Dword|Qword]", + "Display ACPI objects or memory\n"}, + {1, " Handlers", "Info about global handlers\n"}, + {1, " Help [Command]", "This help screen or individual command\n"}, + {1, " History", "Display command history buffer\n"}, + {1, " Level ] [console]", + "Get/Set debug level for file or console\n"}, + {1, " Locks", "Current status of internal mutexes\n"}, + {1, " Osi [Install|Remove ]", + "Display or modify global _OSI list\n"}, + {1, " Quit or Exit", "Exit this command\n"}, + {8, " Stats ", + "Display namespace and memory statistics\n"}, + {1, " Allocations", "Display list of current memory allocations\n"}, + {1, " Memory", "Dump internal memory lists\n"}, + {1, " Misc", "Namespace search and mutex stats\n"}, + {1, " Objects", "Summary of namespace objects\n"}, + {1, " Sizes", "Sizes for each of the internal objects\n"}, + {1, " Stack", "Display CPU stack usage\n"}, + {1, " Tables", "Info about current ACPI table(s)\n"}, + {1, " Tables", "Display info about loaded ACPI tables\n"}, + {1, " ! ", "Execute command from history buffer\n"}, + {1, " !!", "Execute last command again\n"}, + + {0, "\nNamespace Access Commands:", "\n"}, + {1, " Businfo", "Display system bus info\n"}, + {1, " Disassemble ", "Disassemble a control method\n"}, + {1, " Find (? is wildcard)", + "Find ACPI name(s) with wildcards\n"}, + {1, " Integrity", "Validate namespace integrity\n"}, + {1, " Methods", "Display list of loaded control methods\n"}, + {1, " Namespace [Object] [Depth]", + "Display loaded namespace tree/subtree\n"}, + {1, " Notify ", "Send a notification on Object\n"}, + {1, " Objects [ObjectType]", + "Display summary of all objects or just given type\n"}, + {1, " Owner [Depth]", + "Display loaded namespace by object owner\n"}, + {1, " Paths", "Display full pathnames of namespace objects\n"}, + {1, " Predefined", "Check all predefined names\n"}, + {1, " Prefix []", "Set or Get current execution prefix\n"}, + {1, " References ", "Find all references to object at addr\n"}, + {1, " Resources [DeviceName]", + "Display Device resources (no arg = all devices)\n"}, + {1, " Set N ", "Set value for named integer\n"}, + {1, " Template ", "Format/dump a Buffer/ResourceTemplate\n"}, + {1, " Type ", "Display object type\n"}, + + {0, "\nControl Method Execution Commands:", "\n"}, + {1, " Arguments (or Args)", "Display method arguments\n"}, + {1, " Breakpoint ", "Set an AML execution breakpoint\n"}, + {1, " Call", "Run to next control method invocation\n"}, + {1, " Debug [Arguments]", "Single Step a control method\n"}, + {6, " Evaluate", "Synonym for Execute\n"}, + {5, " Execute [Arguments]", "Execute control method\n"}, + {1, " Hex Integer", "Integer method argument\n"}, + {1, " \"Ascii String\"", "String method argument\n"}, + {1, " (Hex Byte List)", "Buffer method argument\n"}, + {1, " [Package Element List]", "Package method argument\n"}, + {1, " Go", "Allow method to run to completion\n"}, + {1, " Information", "Display info about the current method\n"}, + {1, " Into", "Step into (not over) a method call\n"}, + {1, " List [# of Aml Opcodes]", "Display method ASL statements\n"}, + {1, " Locals", "Display method local variables\n"}, + {1, " Results", "Display method result stack\n"}, + {1, " Set <#> ", "Set method data (Arguments/Locals)\n"}, + {1, " Stop", "Terminate control method\n"}, + {5, " Trace [] [Once]", + "Trace control method execution\n"}, + {1, " Enable", "Enable all messages\n"}, + {1, " Disable", "Disable tracing\n"}, + {1, " Method", "Enable method execution messages\n"}, + {1, " Opcode", "Enable opcode execution messages\n"}, + {1, " Tree", "Display control method calling tree\n"}, + {1, " ", "Single step next AML opcode (over calls)\n"}, + +#ifdef ACPI_APPLICATION + {0, "\nHardware Simulation Commands:", "\n"}, + {1, " EnableAcpi", "Enable ACPI (hardware) mode\n"}, + {1, " Event ", "Generate AcpiEvent (Fixed/GPE)\n"}, + {1, " Gpe [GpeBlockDevice]", "Simulate a GPE\n"}, + {1, " Gpes", "Display info on all GPE devices\n"}, + {1, " Sci", "Generate an SCI\n"}, + {1, " Sleep [SleepState]", "Simulate sleep/wake sequence(s) (0-5)\n"}, + + {0, "\nFile I/O Commands:", "\n"}, + {1, " Close", "Close debug output file\n"}, + {1, " Load ", "Load ACPI table from a file\n"}, + {1, " Open ", "Open a file for debug output\n"}, + {1, " Unload ", + "Unload an ACPI table via namespace object\n"}, + + {0, "\nUser Space Commands:", "\n"}, + {1, " Terminate", "Delete namespace and all internal objects\n"}, + {1, " Thread ", + "Spawn threads to execute method(s)\n"}, + + {0, "\nDebug Test Commands:", "\n"}, + {3, " Test ", "Invoke a debug test\n"}, + {1, " Objects", "Read/write/compare all namespace data objects\n"}, + {1, " Predefined", + "Execute all ACPI predefined names (_STA, etc.)\n"}, +#endif + {0, NULL, NULL} +}; + +/******************************************************************************* + * + * FUNCTION: acpi_db_match_command_help + * + * PARAMETERS: command - Command string to match + * help - Help table entry to attempt match + * + * RETURN: TRUE if command matched, FALSE otherwise + * + * DESCRIPTION: Attempt to match a command in the help table in order to + * print help information for a single command. + * + ******************************************************************************/ + +static u8 +acpi_db_match_command_help(char *command, + const struct acpi_db_command_help *help) +{ + char *invocation = help->invocation; + u32 line_count; + + /* Valid commands in the help table begin with a couple of spaces */ + + if (*invocation != ' ') { + return (FALSE); + } + + while (*invocation == ' ') { + invocation++; + } + + /* Match command name (full command or substring) */ + + while ((*command) && (*invocation) && (*invocation != ' ')) { + if (tolower((int)*command) != tolower((int)*invocation)) { + return (FALSE); + } + + invocation++; + command++; + } + + /* Print the appropriate number of help lines */ + + line_count = help->line_count; + while (line_count) { + acpi_os_printf("%-38s : %s", help->invocation, + help->description); + help++; + line_count--; + } + + return (TRUE); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_display_command_info + * + * PARAMETERS: command - Command string to match + * display_all - Display all matching commands, or just + * the first one (substring match) + * + * RETURN: None + * + * DESCRIPTION: Display help information for a Debugger command. + * + ******************************************************************************/ + +static void acpi_db_display_command_info(char *command, u8 display_all) +{ + const struct acpi_db_command_help *next; + u8 matched; + + next = acpi_gbl_db_command_help; + while (next->invocation) { + matched = acpi_db_match_command_help(command, next); + if (!display_all && matched) { + return; + } + + next++; + } +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_display_help + * + * PARAMETERS: command - Optional command string to display help. + * if not specified, all debugger command + * help strings are displayed + * + * RETURN: None + * + * DESCRIPTION: Display help for a single debugger command, or all of them. + * + ******************************************************************************/ + +static void acpi_db_display_help(char *command) +{ + const struct acpi_db_command_help *next = acpi_gbl_db_command_help; + + if (!command) { + + /* No argument to help, display help for all commands */ + + while (next->invocation) { + acpi_os_printf("%-38s%s", next->invocation, + next->description); + next++; + } + } else { + /* Display help for all commands that match the subtring */ + + acpi_db_display_command_info(command, TRUE); + } +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_get_next_token + * + * PARAMETERS: string - Command buffer + * next - Return value, end of next token + * + * RETURN: Pointer to the start of the next token. + * + * DESCRIPTION: Command line parsing. Get the next token on the command line + * + ******************************************************************************/ + +char *acpi_db_get_next_token(char *string, + char **next, acpi_object_type * return_type) +{ + char *start; + u32 depth; + acpi_object_type type = ACPI_TYPE_INTEGER; + + /* At end of buffer? */ + + if (!string || !(*string)) { + return (NULL); + } + + /* Remove any spaces at the beginning */ + + if (*string == ' ') { + while (*string && (*string == ' ')) { + string++; + } + + if (!(*string)) { + return (NULL); + } + } + + switch (*string) { + case '"': + + /* This is a quoted string, scan until closing quote */ + + string++; + start = string; + type = ACPI_TYPE_STRING; + + /* Find end of string */ + + while (*string && (*string != '"')) { + string++; + } + break; + + case '(': + + /* This is the start of a buffer, scan until closing paren */ + + string++; + start = string; + type = ACPI_TYPE_BUFFER; + + /* Find end of buffer */ + + while (*string && (*string != ')')) { + string++; + } + break; + + case '[': + + /* This is the start of a package, scan until closing bracket */ + + string++; + depth = 1; + start = string; + type = ACPI_TYPE_PACKAGE; + + /* Find end of package (closing bracket) */ + + while (*string) { + + /* Handle String package elements */ + + if (*string == '"') { + /* Find end of string */ + + string++; + while (*string && (*string != '"')) { + string++; + } + if (!(*string)) { + break; + } + } else if (*string == '[') { + depth++; /* A nested package declaration */ + } else if (*string == ']') { + depth--; + if (depth == 0) { /* Found final package closing bracket */ + break; + } + } + + string++; + } + break; + + default: + + start = string; + + /* Find end of token */ + + while (*string && (*string != ' ')) { + string++; + } + break; + } + + if (!(*string)) { + *next = NULL; + } else { + *string = 0; + *next = string + 1; + } + + *return_type = type; + return (start); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_get_line + * + * PARAMETERS: input_buffer - Command line buffer + * + * RETURN: Count of arguments to the command + * + * DESCRIPTION: Get the next command line from the user. Gets entire line + * up to the next newline + * + ******************************************************************************/ + +static u32 acpi_db_get_line(char *input_buffer) +{ + u32 i; + u32 count; + char *next; + char *this; + + if (acpi_ut_safe_strcpy + (acpi_gbl_db_parsed_buf, sizeof(acpi_gbl_db_parsed_buf), + input_buffer)) { + acpi_os_printf + ("Buffer overflow while parsing input line (max %u characters)\n", + sizeof(acpi_gbl_db_parsed_buf)); + return (0); + } + + this = acpi_gbl_db_parsed_buf; + for (i = 0; i < ACPI_DEBUGGER_MAX_ARGS; i++) { + acpi_gbl_db_args[i] = acpi_db_get_next_token(this, &next, + &acpi_gbl_db_arg_types + [i]); + if (!acpi_gbl_db_args[i]) { + break; + } + + this = next; + } + + /* Uppercase the actual command */ + + if (acpi_gbl_db_args[0]) { + acpi_ut_strupr(acpi_gbl_db_args[0]); + } + + count = i; + if (count) { + count--; /* Number of args only */ + } + + return (count); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_match_command + * + * PARAMETERS: user_command - User command line + * + * RETURN: Index into command array, -1 if not found + * + * DESCRIPTION: Search command array for a command match + * + ******************************************************************************/ + +static u32 acpi_db_match_command(char *user_command) +{ + u32 i; + + if (!user_command || user_command[0] == 0) { + return (CMD_NULL); + } + + for (i = CMD_FIRST_VALID; acpi_gbl_db_commands[i].name; i++) { + if (strstr(acpi_gbl_db_commands[i].name, user_command) == + acpi_gbl_db_commands[i].name) { + return (i); + } + } + + /* Command not recognized */ + + return (CMD_NOT_FOUND); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_command_dispatch + * + * PARAMETERS: input_buffer - Command line buffer + * walk_state - Current walk + * op - Current (executing) parse op + * + * RETURN: Status + * + * DESCRIPTION: Command dispatcher. + * + ******************************************************************************/ + +acpi_status +acpi_db_command_dispatch(char *input_buffer, + struct acpi_walk_state * walk_state, + union acpi_parse_object * op) +{ + u32 temp; + u32 command_index; + u32 param_count; + char *command_line; + acpi_status status = AE_CTRL_TRUE; + + /* If acpi_terminate has been called, terminate this thread */ + + if (acpi_gbl_db_terminate_threads) { + return (AE_CTRL_TERMINATE); + } + + /* Find command and add to the history buffer */ + + param_count = acpi_db_get_line(input_buffer); + command_index = acpi_db_match_command(acpi_gbl_db_args[0]); + temp = 0; + + /* + * We don't want to add the !! command to the history buffer. It + * would cause an infinite loop because it would always be the + * previous command. + */ + if (command_index != CMD_HISTORY_LAST) { + acpi_db_add_to_history(input_buffer); + } + + /* Verify that we have the minimum number of params */ + + if (param_count < acpi_gbl_db_commands[command_index].min_args) { + acpi_os_printf + ("%u parameters entered, [%s] requires %u parameters\n", + param_count, acpi_gbl_db_commands[command_index].name, + acpi_gbl_db_commands[command_index].min_args); + + acpi_db_display_command_info(acpi_gbl_db_commands + [command_index].name, FALSE); + return (AE_CTRL_TRUE); + } + + /* Decode and dispatch the command */ + + switch (command_index) { + case CMD_NULL: + + if (op) { + return (AE_OK); + } + break; + + case CMD_ALLOCATIONS: + +#ifdef ACPI_DBG_TRACK_ALLOCATIONS + acpi_ut_dump_allocations((u32)-1, NULL); +#endif + break; + + case CMD_ARGS: + case CMD_ARGUMENTS: + + acpi_db_display_arguments(); + break; + + case CMD_BREAKPOINT: + + acpi_db_set_method_breakpoint(acpi_gbl_db_args[1], walk_state, + op); + break; + + case CMD_BUSINFO: + + acpi_db_get_bus_info(); + break; + + case CMD_CALL: + + acpi_db_set_method_call_breakpoint(op); + status = AE_OK; + break; + + case CMD_DEBUG: + + acpi_db_execute(acpi_gbl_db_args[1], + &acpi_gbl_db_args[2], &acpi_gbl_db_arg_types[2], + EX_SINGLE_STEP); + break; + + case CMD_DISASSEMBLE: + case CMD_DISASM: + + (void)acpi_db_disassemble_method(acpi_gbl_db_args[1]); + break; + + case CMD_DUMP: + + acpi_db_decode_and_display_object(acpi_gbl_db_args[1], + acpi_gbl_db_args[2]); + break; + + case CMD_EVALUATE: + case CMD_EXECUTE: + + acpi_db_execute(acpi_gbl_db_args[1], + &acpi_gbl_db_args[2], &acpi_gbl_db_arg_types[2], + EX_NO_SINGLE_STEP); + break; + + case CMD_FIND: + + status = acpi_db_find_name_in_namespace(acpi_gbl_db_args[1]); + break; + + case CMD_GO: + + acpi_gbl_cm_single_step = FALSE; + return (AE_OK); + + case CMD_HANDLERS: + + acpi_db_display_handlers(); + break; + + case CMD_HELP: + case CMD_HELP2: + + acpi_db_display_help(acpi_gbl_db_args[1]); + break; + + case CMD_HISTORY: + + acpi_db_display_history(); + break; + + case CMD_HISTORY_EXE: /* ! command */ + + command_line = acpi_db_get_from_history(acpi_gbl_db_args[1]); + if (!command_line) { + return (AE_CTRL_TRUE); + } + + status = acpi_db_command_dispatch(command_line, walk_state, op); + return (status); + + case CMD_HISTORY_LAST: /* !! command */ + + command_line = acpi_db_get_from_history(NULL); + if (!command_line) { + return (AE_CTRL_TRUE); + } + + status = acpi_db_command_dispatch(command_line, walk_state, op); + return (status); + + case CMD_INFORMATION: + + acpi_db_display_method_info(op); + break; + + case CMD_INTEGRITY: + + acpi_db_check_integrity(); + break; + + case CMD_INTO: + + if (op) { + acpi_gbl_cm_single_step = TRUE; + return (AE_OK); + } + break; + + case CMD_LEVEL: + + if (param_count == 0) { + acpi_os_printf + ("Current debug level for file output is: %8.8lX\n", + acpi_gbl_db_debug_level); + acpi_os_printf + ("Current debug level for console output is: %8.8lX\n", + acpi_gbl_db_console_debug_level); + } else if (param_count == 2) { + temp = acpi_gbl_db_console_debug_level; + acpi_gbl_db_console_debug_level = + strtoul(acpi_gbl_db_args[1], NULL, 16); + acpi_os_printf + ("Debug Level for console output was %8.8lX, now %8.8lX\n", + temp, acpi_gbl_db_console_debug_level); + } else { + temp = acpi_gbl_db_debug_level; + acpi_gbl_db_debug_level = + strtoul(acpi_gbl_db_args[1], NULL, 16); + acpi_os_printf + ("Debug Level for file output was %8.8lX, now %8.8lX\n", + temp, acpi_gbl_db_debug_level); + } + break; + + case CMD_LIST: + + acpi_db_disassemble_aml(acpi_gbl_db_args[1], op); + break; + + case CMD_LOCKS: + + acpi_db_display_locks(); + break; + + case CMD_LOCALS: + + acpi_db_display_locals(); + break; + + case CMD_METHODS: + + status = acpi_db_display_objects("METHOD", acpi_gbl_db_args[1]); + break; + + case CMD_NAMESPACE: + + acpi_db_dump_namespace(acpi_gbl_db_args[1], + acpi_gbl_db_args[2]); + break; + + case CMD_NOTIFY: + + temp = strtoul(acpi_gbl_db_args[2], NULL, 0); + acpi_db_send_notify(acpi_gbl_db_args[1], temp); + break; + + case CMD_OBJECTS: + + acpi_ut_strupr(acpi_gbl_db_args[1]); + status = + acpi_db_display_objects(acpi_gbl_db_args[1], + acpi_gbl_db_args[2]); + break; + + case CMD_OSI: + + acpi_db_display_interfaces(acpi_gbl_db_args[1], + acpi_gbl_db_args[2]); + break; + + case CMD_OWNER: + + acpi_db_dump_namespace_by_owner(acpi_gbl_db_args[1], + acpi_gbl_db_args[2]); + break; + + case CMD_PATHS: + + acpi_db_dump_namespace_paths(); + break; + + case CMD_PREFIX: + + acpi_db_set_scope(acpi_gbl_db_args[1]); + break; + + case CMD_REFERENCES: + + acpi_db_find_references(acpi_gbl_db_args[1]); + break; + + case CMD_RESOURCES: + + acpi_db_display_resources(acpi_gbl_db_args[1]); + break; + + case CMD_RESULTS: + + acpi_db_display_results(); + break; + + case CMD_SET: + + acpi_db_set_method_data(acpi_gbl_db_args[1], + acpi_gbl_db_args[2], + acpi_gbl_db_args[3]); + break; + + case CMD_STATS: + + status = acpi_db_display_statistics(acpi_gbl_db_args[1]); + break; + + case CMD_STOP: + + return (AE_NOT_IMPLEMENTED); + + case CMD_TABLES: + + acpi_db_display_table_info(acpi_gbl_db_args[1]); + break; + + case CMD_TEMPLATE: + + acpi_db_display_template(acpi_gbl_db_args[1]); + break; + + case CMD_TRACE: + + acpi_db_trace(acpi_gbl_db_args[1], acpi_gbl_db_args[2], + acpi_gbl_db_args[3]); + break; + + case CMD_TREE: + + acpi_db_display_calling_tree(); + break; + + case CMD_TYPE: + + acpi_db_display_object_type(acpi_gbl_db_args[1]); + break; + +#ifdef ACPI_APPLICATION + + /* Hardware simulation commands. */ + + case CMD_ENABLEACPI: +#if (!ACPI_REDUCED_HARDWARE) + + status = acpi_enable(); + if (ACPI_FAILURE(status)) { + acpi_os_printf("AcpiEnable failed (Status=%X)\n", + status); + return (status); + } +#endif /* !ACPI_REDUCED_HARDWARE */ + break; + + case CMD_EVENT: + + acpi_os_printf("Event command not implemented\n"); + break; + + case CMD_GPE: + + acpi_db_generate_gpe(acpi_gbl_db_args[1], acpi_gbl_db_args[2]); + break; + + case CMD_GPES: + + acpi_db_display_gpes(); + break; + + case CMD_SCI: + + acpi_db_generate_sci(); + break; + + case CMD_SLEEP: + + status = acpi_db_sleep(acpi_gbl_db_args[1]); + break; + + /* File I/O commands. */ + + case CMD_CLOSE: + + acpi_db_close_debug_file(); + break; + + case CMD_LOAD: + + status = + acpi_db_get_table_from_file(acpi_gbl_db_args[1], NULL, + FALSE); + break; + + case CMD_OPEN: + + acpi_db_open_debug_file(acpi_gbl_db_args[1]); + break; + + /* User space commands. */ + + case CMD_TERMINATE: + + acpi_db_set_output_destination(ACPI_DB_REDIRECTABLE_OUTPUT); + acpi_ut_subsystem_shutdown(); + + /* + * TBD: [Restructure] Need some way to re-initialize without + * re-creating the semaphores! + */ + + /* acpi_initialize (NULL); */ + break; + + case CMD_THREADS: + + acpi_db_create_execution_threads(acpi_gbl_db_args[1], + acpi_gbl_db_args[2], + acpi_gbl_db_args[3]); + break; + + /* Debug test commands. */ + + case CMD_PREDEFINED: + + acpi_db_check_predefined_names(); + break; + + case CMD_TEST: + + acpi_db_execute_test(acpi_gbl_db_args[1]); + break; + + case CMD_UNLOAD: + + acpi_db_unload_acpi_table(acpi_gbl_db_args[1]); + break; +#endif + + case CMD_EXIT: + case CMD_QUIT: + + if (op) { + acpi_os_printf("Method execution terminated\n"); + return (AE_CTRL_TERMINATE); + } + + if (!acpi_gbl_db_output_to_file) { + acpi_dbg_level = ACPI_DEBUG_DEFAULT; + } +#ifdef ACPI_APPLICATION + acpi_db_close_debug_file(); +#endif + acpi_gbl_db_terminate_threads = TRUE; + return (AE_CTRL_TERMINATE); + + case CMD_NOT_FOUND: + default: + + acpi_os_printf("%s: unknown command\n", acpi_gbl_db_args[0]); + return (AE_CTRL_TRUE); + } + + if (ACPI_SUCCESS(status)) { + status = AE_CTRL_TRUE; + } + + return (status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_execute_thread + * + * PARAMETERS: context - Not used + * + * RETURN: None + * + * DESCRIPTION: Debugger execute thread. Waits for a command line, then + * simply dispatches it. + * + ******************************************************************************/ + +void ACPI_SYSTEM_XFACE acpi_db_execute_thread(void *context) +{ + acpi_status status = AE_OK; + acpi_status Mstatus; + + while (status != AE_CTRL_TERMINATE) { + acpi_gbl_method_executing = FALSE; + acpi_gbl_step_to_next_call = FALSE; + + Mstatus = acpi_os_acquire_mutex(acpi_gbl_db_command_ready, + ACPI_WAIT_FOREVER); + if (ACPI_FAILURE(Mstatus)) { + return; + } + + status = + acpi_db_command_dispatch(acpi_gbl_db_line_buf, NULL, NULL); + + acpi_os_release_mutex(acpi_gbl_db_command_complete); + } +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_single_thread + * + * PARAMETERS: None + * + * RETURN: None + * + * DESCRIPTION: Debugger execute thread. Waits for a command line, then + * simply dispatches it. + * + ******************************************************************************/ + +static void acpi_db_single_thread(void) +{ + + acpi_gbl_method_executing = FALSE; + acpi_gbl_step_to_next_call = FALSE; + + (void)acpi_db_command_dispatch(acpi_gbl_db_line_buf, NULL, NULL); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_user_commands + * + * PARAMETERS: prompt - User prompt (depends on mode) + * op - Current executing parse op + * + * RETURN: None + * + * DESCRIPTION: Command line execution for the AML debugger. Commands are + * matched and dispatched here. + * + ******************************************************************************/ + +acpi_status acpi_db_user_commands(char prompt, union acpi_parse_object *op) +{ + acpi_status status = AE_OK; + + acpi_os_printf("\n"); + + /* TBD: [Restructure] Need a separate command line buffer for step mode */ + + while (!acpi_gbl_db_terminate_threads) { + + /* Force output to console until a command is entered */ + + acpi_db_set_output_destination(ACPI_DB_CONSOLE_OUTPUT); + + /* Different prompt if method is executing */ + + if (!acpi_gbl_method_executing) { + acpi_os_printf("%1c ", ACPI_DEBUGGER_COMMAND_PROMPT); + } else { + acpi_os_printf("%1c ", ACPI_DEBUGGER_EXECUTE_PROMPT); + } + + /* Get the user input line */ + + status = acpi_os_get_line(acpi_gbl_db_line_buf, + ACPI_DB_LINE_BUFFER_SIZE, NULL); + if (ACPI_FAILURE(status)) { + ACPI_EXCEPTION((AE_INFO, status, + "While parsing command line")); + return (status); + } + + /* Check for single or multithreaded debug */ + + if (acpi_gbl_debugger_configuration & DEBUGGER_MULTI_THREADED) { + /* + * Signal the debug thread that we have a command to execute, + * and wait for the command to complete. + */ + acpi_os_release_mutex(acpi_gbl_db_command_ready); + if (ACPI_FAILURE(status)) { + return (status); + } + + status = + acpi_os_acquire_mutex(acpi_gbl_db_command_complete, + ACPI_WAIT_FOREVER); + if (ACPI_FAILURE(status)) { + return (status); + } + } else { + /* Just call to the command line interpreter */ + + acpi_db_single_thread(); + } + } + + /* Shut down the debugger */ + + acpi_terminate_debugger(); + + /* + * Only this thread (the original thread) should actually terminate the + * subsystem, because all the semaphores are deleted during termination + */ + status = acpi_terminate(); + return (status); +} diff --git a/drivers/acpi/acpica/dbmethod.c b/drivers/acpi/acpica/dbmethod.c new file mode 100644 index 0000000..01e5a71 --- /dev/null +++ b/drivers/acpi/acpica/dbmethod.c @@ -0,0 +1,369 @@ +/******************************************************************************* + * + * Module Name: dbmethod - Debug commands for control methods + * + ******************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acdispat.h" +#include "acnamesp.h" +#include "acdebug.h" +#include "acparser.h" +#include "acpredef.h" + +#define _COMPONENT ACPI_CA_DEBUGGER +ACPI_MODULE_NAME("dbmethod") + +/******************************************************************************* + * + * FUNCTION: acpi_db_set_method_breakpoint + * + * PARAMETERS: location - AML offset of breakpoint + * walk_state - Current walk info + * op - Current Op (from parse walk) + * + * RETURN: None + * + * DESCRIPTION: Set a breakpoint in a control method at the specified + * AML offset + * + ******************************************************************************/ +void +acpi_db_set_method_breakpoint(char *location, + struct acpi_walk_state *walk_state, + union acpi_parse_object *op) +{ + u32 address; + u32 aml_offset; + + if (!op) { + acpi_os_printf("There is no method currently executing\n"); + return; + } + + /* Get and verify the breakpoint address */ + + address = strtoul(location, NULL, 16); + aml_offset = (u32)ACPI_PTR_DIFF(op->common.aml, + walk_state->parser_state.aml_start); + if (address <= aml_offset) { + acpi_os_printf("Breakpoint %X is beyond current address %X\n", + address, aml_offset); + } + + /* Save breakpoint in current walk */ + + walk_state->user_breakpoint = address; + acpi_os_printf("Breakpoint set at AML offset %X\n", address); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_set_method_call_breakpoint + * + * PARAMETERS: op - Current Op (from parse walk) + * + * RETURN: None + * + * DESCRIPTION: Set a breakpoint in a control method at the specified + * AML offset + * + ******************************************************************************/ + +void acpi_db_set_method_call_breakpoint(union acpi_parse_object *op) +{ + + if (!op) { + acpi_os_printf("There is no method currently executing\n"); + return; + } + + acpi_gbl_step_to_next_call = TRUE; +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_set_method_data + * + * PARAMETERS: type_arg - L for local, A for argument + * index_arg - which one + * value_arg - Value to set. + * + * RETURN: None + * + * DESCRIPTION: Set a local or argument for the running control method. + * NOTE: only object supported is Number. + * + ******************************************************************************/ + +void acpi_db_set_method_data(char *type_arg, char *index_arg, char *value_arg) +{ + char type; + u32 index; + u32 value; + struct acpi_walk_state *walk_state; + union acpi_operand_object *obj_desc; + acpi_status status; + struct acpi_namespace_node *node; + + /* Validate type_arg */ + + acpi_ut_strupr(type_arg); + type = type_arg[0]; + if ((type != 'L') && (type != 'A') && (type != 'N')) { + acpi_os_printf("Invalid SET operand: %s\n", type_arg); + return; + } + + value = strtoul(value_arg, NULL, 16); + + if (type == 'N') { + node = acpi_db_convert_to_node(index_arg); + if (!node) { + return; + } + + if (node->type != ACPI_TYPE_INTEGER) { + acpi_os_printf("Can only set Integer nodes\n"); + return; + } + obj_desc = node->object; + obj_desc->integer.value = value; + return; + } + + /* Get the index and value */ + + index = strtoul(index_arg, NULL, 16); + + walk_state = acpi_ds_get_current_walk_state(acpi_gbl_current_walk_list); + if (!walk_state) { + acpi_os_printf("There is no method currently executing\n"); + return; + } + + /* Create and initialize the new object */ + + obj_desc = acpi_ut_create_integer_object((u64)value); + if (!obj_desc) { + acpi_os_printf("Could not create an internal object\n"); + return; + } + + /* Store the new object into the target */ + + switch (type) { + case 'A': + + /* Set a method argument */ + + if (index > ACPI_METHOD_MAX_ARG) { + acpi_os_printf("Arg%u - Invalid argument name\n", + index); + goto cleanup; + } + + status = acpi_ds_store_object_to_local(ACPI_REFCLASS_ARG, + index, obj_desc, + walk_state); + if (ACPI_FAILURE(status)) { + goto cleanup; + } + + obj_desc = walk_state->arguments[index].object; + + acpi_os_printf("Arg%u: ", index); + acpi_db_display_internal_object(obj_desc, walk_state); + break; + + case 'L': + + /* Set a method local */ + + if (index > ACPI_METHOD_MAX_LOCAL) { + acpi_os_printf + ("Local%u - Invalid local variable name\n", index); + goto cleanup; + } + + status = acpi_ds_store_object_to_local(ACPI_REFCLASS_LOCAL, + index, obj_desc, + walk_state); + if (ACPI_FAILURE(status)) { + goto cleanup; + } + + obj_desc = walk_state->local_variables[index].object; + + acpi_os_printf("Local%u: ", index); + acpi_db_display_internal_object(obj_desc, walk_state); + break; + + default: + + break; + } + +cleanup: + acpi_ut_remove_reference(obj_desc); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_disassemble_aml + * + * PARAMETERS: statements - Number of statements to disassemble + * op - Current Op (from parse walk) + * + * RETURN: None + * + * DESCRIPTION: Display disassembled AML (ASL) starting from Op for the number + * of statements specified. + * + ******************************************************************************/ + +void acpi_db_disassemble_aml(char *statements, union acpi_parse_object *op) +{ + u32 num_statements = 8; + + if (!op) { + acpi_os_printf("There is no method currently executing\n"); + return; + } + + if (statements) { + num_statements = strtoul(statements, NULL, 0); + } +#ifdef ACPI_DISASSEMBLER + acpi_dm_disassemble(NULL, op, num_statements); +#endif +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_disassemble_method + * + * PARAMETERS: name - Name of control method + * + * RETURN: None + * + * DESCRIPTION: Display disassembled AML (ASL) starting from Op for the number + * of statements specified. + * + ******************************************************************************/ + +acpi_status acpi_db_disassemble_method(char *name) +{ + acpi_status status; + union acpi_parse_object *op; + struct acpi_walk_state *walk_state; + union acpi_operand_object *obj_desc; + struct acpi_namespace_node *method; + + method = acpi_db_convert_to_node(name); + if (!method) { + return (AE_BAD_PARAMETER); + } + + if (method->type != ACPI_TYPE_METHOD) { + ACPI_ERROR((AE_INFO, "%s (%s): Object must be a control method", + name, acpi_ut_get_type_name(method->type))); + return (AE_BAD_PARAMETER); + } + + obj_desc = method->object; + + op = acpi_ps_create_scope_op(obj_desc->method.aml_start); + if (!op) { + return (AE_NO_MEMORY); + } + + /* Create and initialize a new walk state */ + + walk_state = acpi_ds_create_walk_state(0, op, NULL, NULL); + if (!walk_state) { + return (AE_NO_MEMORY); + } + + status = acpi_ds_init_aml_walk(walk_state, op, NULL, + obj_desc->method.aml_start, + obj_desc->method.aml_length, NULL, + ACPI_IMODE_LOAD_PASS1); + if (ACPI_FAILURE(status)) { + return (status); + } + + status = acpi_ut_allocate_owner_id(&obj_desc->method.owner_id); + walk_state->owner_id = obj_desc->method.owner_id; + + /* Push start scope on scope stack and make it current */ + + status = acpi_ds_scope_stack_push(method, method->type, walk_state); + if (ACPI_FAILURE(status)) { + return (status); + } + + /* Parse the entire method AML including deferred operators */ + + walk_state->parse_flags &= ~ACPI_PARSE_DELETE_TREE; + walk_state->parse_flags |= ACPI_PARSE_DISASSEMBLE; + + status = acpi_ps_parse_aml(walk_state); + +#ifdef ACPI_DISASSEMBLER + (void)acpi_dm_parse_deferred_ops(op); + + /* Now we can disassemble the method */ + + acpi_gbl_dm_opt_verbose = FALSE; + acpi_dm_disassemble(NULL, op, 0); + acpi_gbl_dm_opt_verbose = TRUE; +#endif + + acpi_ps_delete_parse_tree(op); + + /* Method cleanup */ + + acpi_ns_delete_namespace_subtree(method); + acpi_ns_delete_namespace_by_owner(obj_desc->method.owner_id); + acpi_ut_release_owner_id(&obj_desc->method.owner_id); + return (AE_OK); +} diff --git a/drivers/acpi/acpica/dbnames.c b/drivers/acpi/acpica/dbnames.c new file mode 100644 index 0000000..04ff1eb --- /dev/null +++ b/drivers/acpi/acpica/dbnames.c @@ -0,0 +1,947 @@ +/******************************************************************************* + * + * Module Name: dbnames - Debugger commands for the acpi namespace + * + ******************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acnamesp.h" +#include "acdebug.h" +#include "acpredef.h" + +#define _COMPONENT ACPI_CA_DEBUGGER +ACPI_MODULE_NAME("dbnames") + +/* Local prototypes */ +static acpi_status +acpi_db_walk_and_match_name(acpi_handle obj_handle, + u32 nesting_level, + void *context, void **return_value); + +static acpi_status +acpi_db_walk_for_predefined_names(acpi_handle obj_handle, + u32 nesting_level, + void *context, void **return_value); + +static acpi_status +acpi_db_walk_for_specific_objects(acpi_handle obj_handle, + u32 nesting_level, + void *context, void **return_value); + +static acpi_status +acpi_db_walk_for_object_counts(acpi_handle obj_handle, + u32 nesting_level, + void *context, void **return_value); + +static acpi_status +acpi_db_integrity_walk(acpi_handle obj_handle, + u32 nesting_level, void *context, void **return_value); + +static acpi_status +acpi_db_walk_for_references(acpi_handle obj_handle, + u32 nesting_level, + void *context, void **return_value); + +static acpi_status +acpi_db_bus_walk(acpi_handle obj_handle, + u32 nesting_level, void *context, void **return_value); + +/* + * Arguments for the Objects command + * These object types map directly to the ACPI_TYPES + */ +static struct acpi_db_argument_info acpi_db_object_types[] = { + {"ANY"}, + {"INTEGERS"}, + {"STRINGS"}, + {"BUFFERS"}, + {"PACKAGES"}, + {"FIELDS"}, + {"DEVICES"}, + {"EVENTS"}, + {"METHODS"}, + {"MUTEXES"}, + {"REGIONS"}, + {"POWERRESOURCES"}, + {"PROCESSORS"}, + {"THERMALZONES"}, + {"BUFFERFIELDS"}, + {"DDBHANDLES"}, + {"DEBUG"}, + {"REGIONFIELDS"}, + {"BANKFIELDS"}, + {"INDEXFIELDS"}, + {"REFERENCES"}, + {"ALIASES"}, + {"METHODALIASES"}, + {"NOTIFY"}, + {"ADDRESSHANDLER"}, + {"RESOURCE"}, + {"RESOURCEFIELD"}, + {"SCOPES"}, + {NULL} /* Must be null terminated */ +}; + +/******************************************************************************* + * + * FUNCTION: acpi_db_set_scope + * + * PARAMETERS: name - New scope path + * + * RETURN: Status + * + * DESCRIPTION: Set the "current scope" as maintained by this utility. + * The scope is used as a prefix to ACPI paths. + * + ******************************************************************************/ + +void acpi_db_set_scope(char *name) +{ + acpi_status status; + struct acpi_namespace_node *node; + + if (!name || name[0] == 0) { + acpi_os_printf("Current scope: %s\n", acpi_gbl_db_scope_buf); + return; + } + + acpi_db_prep_namestring(name); + + if (ACPI_IS_ROOT_PREFIX(name[0])) { + + /* Validate new scope from the root */ + + status = acpi_ns_get_node(acpi_gbl_root_node, name, + ACPI_NS_NO_UPSEARCH, &node); + if (ACPI_FAILURE(status)) { + goto error_exit; + } + + acpi_gbl_db_scope_buf[0] = 0; + } else { + /* Validate new scope relative to old scope */ + + status = acpi_ns_get_node(acpi_gbl_db_scope_node, name, + ACPI_NS_NO_UPSEARCH, &node); + if (ACPI_FAILURE(status)) { + goto error_exit; + } + } + + /* Build the final pathname */ + + if (acpi_ut_safe_strcat + (acpi_gbl_db_scope_buf, sizeof(acpi_gbl_db_scope_buf), name)) { + status = AE_BUFFER_OVERFLOW; + goto error_exit; + } + + if (acpi_ut_safe_strcat + (acpi_gbl_db_scope_buf, sizeof(acpi_gbl_db_scope_buf), "\\")) { + status = AE_BUFFER_OVERFLOW; + goto error_exit; + } + + acpi_gbl_db_scope_node = node; + acpi_os_printf("New scope: %s\n", acpi_gbl_db_scope_buf); + return; + +error_exit: + + acpi_os_printf("Could not attach scope: %s, %s\n", + name, acpi_format_exception(status)); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_dump_namespace + * + * PARAMETERS: start_arg - Node to begin namespace dump + * depth_arg - Maximum tree depth to be dumped + * + * RETURN: None + * + * DESCRIPTION: Dump entire namespace or a subtree. Each node is displayed + * with type and other information. + * + ******************************************************************************/ + +void acpi_db_dump_namespace(char *start_arg, char *depth_arg) +{ + acpi_handle subtree_entry = acpi_gbl_root_node; + u32 max_depth = ACPI_UINT32_MAX; + + /* No argument given, just start at the root and dump entire namespace */ + + if (start_arg) { + subtree_entry = acpi_db_convert_to_node(start_arg); + if (!subtree_entry) { + return; + } + + /* Now we can check for the depth argument */ + + if (depth_arg) { + max_depth = strtoul(depth_arg, NULL, 0); + } + } + + acpi_db_set_output_destination(ACPI_DB_DUPLICATE_OUTPUT); + acpi_os_printf("ACPI Namespace (from %4.4s (%p) subtree):\n", + ((struct acpi_namespace_node *)subtree_entry)->name. + ascii, subtree_entry); + + /* Display the subtree */ + + acpi_db_set_output_destination(ACPI_DB_REDIRECTABLE_OUTPUT); + acpi_ns_dump_objects(ACPI_TYPE_ANY, ACPI_DISPLAY_SUMMARY, max_depth, + ACPI_OWNER_ID_MAX, subtree_entry); + acpi_db_set_output_destination(ACPI_DB_CONSOLE_OUTPUT); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_dump_namespace_paths + * + * PARAMETERS: None + * + * RETURN: None + * + * DESCRIPTION: Dump entire namespace with full object pathnames and object + * type information. Alternative to "namespace" command. + * + ******************************************************************************/ + +void acpi_db_dump_namespace_paths(void) +{ + + acpi_db_set_output_destination(ACPI_DB_DUPLICATE_OUTPUT); + acpi_os_printf("ACPI Namespace (from root):\n"); + + /* Display the entire namespace */ + + acpi_db_set_output_destination(ACPI_DB_REDIRECTABLE_OUTPUT); + acpi_ns_dump_object_paths(ACPI_TYPE_ANY, ACPI_DISPLAY_SUMMARY, + ACPI_UINT32_MAX, ACPI_OWNER_ID_MAX, + acpi_gbl_root_node); + + acpi_db_set_output_destination(ACPI_DB_CONSOLE_OUTPUT); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_dump_namespace_by_owner + * + * PARAMETERS: owner_arg - Owner ID whose nodes will be displayed + * depth_arg - Maximum tree depth to be dumped + * + * RETURN: None + * + * DESCRIPTION: Dump elements of the namespace that are owned by the owner_id. + * + ******************************************************************************/ + +void acpi_db_dump_namespace_by_owner(char *owner_arg, char *depth_arg) +{ + acpi_handle subtree_entry = acpi_gbl_root_node; + u32 max_depth = ACPI_UINT32_MAX; + acpi_owner_id owner_id; + + owner_id = (acpi_owner_id) strtoul(owner_arg, NULL, 0); + + /* Now we can check for the depth argument */ + + if (depth_arg) { + max_depth = strtoul(depth_arg, NULL, 0); + } + + acpi_db_set_output_destination(ACPI_DB_DUPLICATE_OUTPUT); + acpi_os_printf("ACPI Namespace by owner %X:\n", owner_id); + + /* Display the subtree */ + + acpi_db_set_output_destination(ACPI_DB_REDIRECTABLE_OUTPUT); + acpi_ns_dump_objects(ACPI_TYPE_ANY, ACPI_DISPLAY_SUMMARY, max_depth, + owner_id, subtree_entry); + acpi_db_set_output_destination(ACPI_DB_CONSOLE_OUTPUT); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_walk_and_match_name + * + * PARAMETERS: Callback from walk_namespace + * + * RETURN: Status + * + * DESCRIPTION: Find a particular name/names within the namespace. Wildcards + * are supported -- '?' matches any character. + * + ******************************************************************************/ + +static acpi_status +acpi_db_walk_and_match_name(acpi_handle obj_handle, + u32 nesting_level, + void *context, void **return_value) +{ + acpi_status status; + char *requested_name = (char *)context; + u32 i; + struct acpi_buffer buffer; + struct acpi_walk_info info; + + /* Check for a name match */ + + for (i = 0; i < 4; i++) { + + /* Wildcard support */ + + if ((requested_name[i] != '?') && + (requested_name[i] != ((struct acpi_namespace_node *) + obj_handle)->name.ascii[i])) { + + /* No match, just exit */ + + return (AE_OK); + } + } + + /* Get the full pathname to this object */ + + buffer.length = ACPI_ALLOCATE_LOCAL_BUFFER; + status = acpi_ns_handle_to_pathname(obj_handle, &buffer, TRUE); + if (ACPI_FAILURE(status)) { + acpi_os_printf("Could Not get pathname for object %p\n", + obj_handle); + } else { + info.owner_id = ACPI_OWNER_ID_MAX; + info.debug_level = ACPI_UINT32_MAX; + info.display_type = ACPI_DISPLAY_SUMMARY | ACPI_DISPLAY_SHORT; + + acpi_os_printf("%32s", (char *)buffer.pointer); + (void)acpi_ns_dump_one_object(obj_handle, nesting_level, &info, + NULL); + ACPI_FREE(buffer.pointer); + } + + return (AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_find_name_in_namespace + * + * PARAMETERS: name_arg - The 4-character ACPI name to find. + * wildcards are supported. + * + * RETURN: None + * + * DESCRIPTION: Search the namespace for a given name (with wildcards) + * + ******************************************************************************/ + +acpi_status acpi_db_find_name_in_namespace(char *name_arg) +{ + char acpi_name[5] = "____"; + char *acpi_name_ptr = acpi_name; + + if (strlen(name_arg) > ACPI_NAME_SIZE) { + acpi_os_printf("Name must be no longer than 4 characters\n"); + return (AE_OK); + } + + /* Pad out name with underscores as necessary to create a 4-char name */ + + acpi_ut_strupr(name_arg); + while (*name_arg) { + *acpi_name_ptr = *name_arg; + acpi_name_ptr++; + name_arg++; + } + + /* Walk the namespace from the root */ + + (void)acpi_walk_namespace(ACPI_TYPE_ANY, ACPI_ROOT_OBJECT, + ACPI_UINT32_MAX, acpi_db_walk_and_match_name, + NULL, acpi_name, NULL); + + acpi_db_set_output_destination(ACPI_DB_CONSOLE_OUTPUT); + return (AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_walk_for_predefined_names + * + * PARAMETERS: Callback from walk_namespace + * + * RETURN: Status + * + * DESCRIPTION: Detect and display predefined ACPI names (names that start with + * an underscore) + * + ******************************************************************************/ + +static acpi_status +acpi_db_walk_for_predefined_names(acpi_handle obj_handle, + u32 nesting_level, + void *context, void **return_value) +{ + struct acpi_namespace_node *node = + (struct acpi_namespace_node *)obj_handle; + u32 *count = (u32 *)context; + const union acpi_predefined_info *predefined; + const union acpi_predefined_info *package = NULL; + char *pathname; + char string_buffer[48]; + + predefined = acpi_ut_match_predefined_method(node->name.ascii); + if (!predefined) { + return (AE_OK); + } + + pathname = acpi_ns_get_external_pathname(node); + if (!pathname) { + return (AE_OK); + } + + /* If method returns a package, the info is in the next table entry */ + + if (predefined->info.expected_btypes & ACPI_RTYPE_PACKAGE) { + package = predefined + 1; + } + + acpi_ut_get_expected_return_types(string_buffer, + predefined->info.expected_btypes); + + acpi_os_printf("%-32s Arguments %X, Return Types: %s", pathname, + METHOD_GET_ARG_COUNT(predefined->info.argument_list), + string_buffer); + + if (package) { + acpi_os_printf(" (PkgType %2.2X, ObjType %2.2X, Count %2.2X)", + package->ret_info.type, + package->ret_info.object_type1, + package->ret_info.count1); + } + + acpi_os_printf("\n"); + + /* Check that the declared argument count matches the ACPI spec */ + + acpi_ns_check_acpi_compliance(pathname, node, predefined); + + ACPI_FREE(pathname); + (*count)++; + return (AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_check_predefined_names + * + * PARAMETERS: None + * + * RETURN: None + * + * DESCRIPTION: Validate all predefined names in the namespace + * + ******************************************************************************/ + +void acpi_db_check_predefined_names(void) +{ + u32 count = 0; + + /* Search all nodes in namespace */ + + (void)acpi_walk_namespace(ACPI_TYPE_ANY, ACPI_ROOT_OBJECT, + ACPI_UINT32_MAX, + acpi_db_walk_for_predefined_names, NULL, + (void *)&count, NULL); + + acpi_os_printf("Found %u predefined names in the namespace\n", count); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_walk_for_object_counts + * + * PARAMETERS: Callback from walk_namespace + * + * RETURN: Status + * + * DESCRIPTION: Display short info about objects in the namespace + * + ******************************************************************************/ + +static acpi_status +acpi_db_walk_for_object_counts(acpi_handle obj_handle, + u32 nesting_level, + void *context, void **return_value) +{ + struct acpi_object_info *info = (struct acpi_object_info *)context; + struct acpi_namespace_node *node = + (struct acpi_namespace_node *)obj_handle; + + if (node->type > ACPI_TYPE_NS_NODE_MAX) { + acpi_os_printf("[%4.4s]: Unknown object type %X\n", + node->name.ascii, node->type); + } else { + info->types[node->type]++; + } + + return (AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_walk_for_specific_objects + * + * PARAMETERS: Callback from walk_namespace + * + * RETURN: Status + * + * DESCRIPTION: Display short info about objects in the namespace + * + ******************************************************************************/ + +static acpi_status +acpi_db_walk_for_specific_objects(acpi_handle obj_handle, + u32 nesting_level, + void *context, void **return_value) +{ + struct acpi_walk_info *info = (struct acpi_walk_info *)context; + struct acpi_buffer buffer; + acpi_status status; + + info->count++; + + /* Get and display the full pathname to this object */ + + buffer.length = ACPI_ALLOCATE_LOCAL_BUFFER; + status = acpi_ns_handle_to_pathname(obj_handle, &buffer, TRUE); + if (ACPI_FAILURE(status)) { + acpi_os_printf("Could Not get pathname for object %p\n", + obj_handle); + return (AE_OK); + } + + acpi_os_printf("%32s", (char *)buffer.pointer); + ACPI_FREE(buffer.pointer); + + /* Dump short info about the object */ + + (void)acpi_ns_dump_one_object(obj_handle, nesting_level, info, NULL); + return (AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_display_objects + * + * PARAMETERS: obj_type_arg - Type of object to display + * display_count_arg - Max depth to display + * + * RETURN: None + * + * DESCRIPTION: Display objects in the namespace of the requested type + * + ******************************************************************************/ + +acpi_status acpi_db_display_objects(char *obj_type_arg, char *display_count_arg) +{ + struct acpi_walk_info info; + acpi_object_type type; + struct acpi_object_info *object_info; + u32 i; + u32 total_objects = 0; + + /* No argument means display summary/count of all object types */ + + if (!obj_type_arg) { + object_info = + ACPI_ALLOCATE_ZEROED(sizeof(struct acpi_object_info)); + + /* Walk the namespace from the root */ + + (void)acpi_walk_namespace(ACPI_TYPE_ANY, ACPI_ROOT_OBJECT, + ACPI_UINT32_MAX, + acpi_db_walk_for_object_counts, NULL, + (void *)object_info, NULL); + + acpi_os_printf("\nSummary of namespace objects:\n\n"); + + for (i = 0; i < ACPI_TOTAL_TYPES; i++) { + acpi_os_printf("%8u %s\n", object_info->types[i], + acpi_ut_get_type_name(i)); + + total_objects += object_info->types[i]; + } + + acpi_os_printf("\n%8u Total namespace objects\n\n", + total_objects); + + ACPI_FREE(object_info); + return (AE_OK); + } + + /* Get the object type */ + + type = acpi_db_match_argument(obj_type_arg, acpi_db_object_types); + if (type == ACPI_TYPE_NOT_FOUND) { + acpi_os_printf("Invalid or unsupported argument\n"); + return (AE_OK); + } + + acpi_db_set_output_destination(ACPI_DB_DUPLICATE_OUTPUT); + acpi_os_printf + ("Objects of type [%s] defined in the current ACPI Namespace:\n", + acpi_ut_get_type_name(type)); + + acpi_db_set_output_destination(ACPI_DB_REDIRECTABLE_OUTPUT); + + info.count = 0; + info.owner_id = ACPI_OWNER_ID_MAX; + info.debug_level = ACPI_UINT32_MAX; + info.display_type = ACPI_DISPLAY_SUMMARY | ACPI_DISPLAY_SHORT; + + /* Walk the namespace from the root */ + + (void)acpi_walk_namespace(type, ACPI_ROOT_OBJECT, ACPI_UINT32_MAX, + acpi_db_walk_for_specific_objects, NULL, + (void *)&info, NULL); + + acpi_os_printf + ("\nFound %u objects of type [%s] in the current ACPI Namespace\n", + info.count, acpi_ut_get_type_name(type)); + + acpi_db_set_output_destination(ACPI_DB_CONSOLE_OUTPUT); + return (AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_integrity_walk + * + * PARAMETERS: Callback from walk_namespace + * + * RETURN: Status + * + * DESCRIPTION: Examine one NS node for valid values. + * + ******************************************************************************/ + +static acpi_status +acpi_db_integrity_walk(acpi_handle obj_handle, + u32 nesting_level, void *context, void **return_value) +{ + struct acpi_integrity_info *info = + (struct acpi_integrity_info *)context; + struct acpi_namespace_node *node = + (struct acpi_namespace_node *)obj_handle; + union acpi_operand_object *object; + u8 alias = TRUE; + + info->nodes++; + + /* Verify the NS node, and dereference aliases */ + + while (alias) { + if (ACPI_GET_DESCRIPTOR_TYPE(node) != ACPI_DESC_TYPE_NAMED) { + acpi_os_printf + ("Invalid Descriptor Type for Node %p [%s] - " + "is %2.2X should be %2.2X\n", node, + acpi_ut_get_descriptor_name(node), + ACPI_GET_DESCRIPTOR_TYPE(node), + ACPI_DESC_TYPE_NAMED); + return (AE_OK); + } + + if ((node->type == ACPI_TYPE_LOCAL_ALIAS) || + (node->type == ACPI_TYPE_LOCAL_METHOD_ALIAS)) { + node = (struct acpi_namespace_node *)node->object; + } else { + alias = FALSE; + } + } + + if (node->type > ACPI_TYPE_LOCAL_MAX) { + acpi_os_printf("Invalid Object Type for Node %p, Type = %X\n", + node, node->type); + return (AE_OK); + } + + if (!acpi_ut_valid_acpi_name(node->name.ascii)) { + acpi_os_printf("Invalid AcpiName for Node %p\n", node); + return (AE_OK); + } + + object = acpi_ns_get_attached_object(node); + if (object) { + info->objects++; + if (ACPI_GET_DESCRIPTOR_TYPE(object) != ACPI_DESC_TYPE_OPERAND) { + acpi_os_printf + ("Invalid Descriptor Type for Object %p [%s]\n", + object, acpi_ut_get_descriptor_name(object)); + } + } + + return (AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_check_integrity + * + * PARAMETERS: None + * + * RETURN: None + * + * DESCRIPTION: Check entire namespace for data structure integrity + * + ******************************************************************************/ + +void acpi_db_check_integrity(void) +{ + struct acpi_integrity_info info = { 0, 0 }; + + /* Search all nodes in namespace */ + + (void)acpi_walk_namespace(ACPI_TYPE_ANY, ACPI_ROOT_OBJECT, + ACPI_UINT32_MAX, acpi_db_integrity_walk, NULL, + (void *)&info, NULL); + + acpi_os_printf("Verified %u namespace nodes with %u Objects\n", + info.nodes, info.objects); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_walk_for_references + * + * PARAMETERS: Callback from walk_namespace + * + * RETURN: Status + * + * DESCRIPTION: Check if this namespace object refers to the target object + * that is passed in as the context value. + * + * Note: Currently doesn't check subobjects within the Node's object + * + ******************************************************************************/ + +static acpi_status +acpi_db_walk_for_references(acpi_handle obj_handle, + u32 nesting_level, + void *context, void **return_value) +{ + union acpi_operand_object *obj_desc = + (union acpi_operand_object *)context; + struct acpi_namespace_node *node = + (struct acpi_namespace_node *)obj_handle; + + /* Check for match against the namespace node itself */ + + if (node == (void *)obj_desc) { + acpi_os_printf("Object is a Node [%4.4s]\n", + acpi_ut_get_node_name(node)); + } + + /* Check for match against the object attached to the node */ + + if (acpi_ns_get_attached_object(node) == obj_desc) { + acpi_os_printf("Reference at Node->Object %p [%4.4s]\n", + node, acpi_ut_get_node_name(node)); + } + + return (AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_find_references + * + * PARAMETERS: object_arg - String with hex value of the object + * + * RETURN: None + * + * DESCRIPTION: Search namespace for all references to the input object + * + ******************************************************************************/ + +void acpi_db_find_references(char *object_arg) +{ + union acpi_operand_object *obj_desc; + acpi_size address; + + /* Convert string to object pointer */ + + address = strtoul(object_arg, NULL, 16); + obj_desc = ACPI_TO_POINTER(address); + + /* Search all nodes in namespace */ + + (void)acpi_walk_namespace(ACPI_TYPE_ANY, ACPI_ROOT_OBJECT, + ACPI_UINT32_MAX, acpi_db_walk_for_references, + NULL, (void *)obj_desc, NULL); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_bus_walk + * + * PARAMETERS: Callback from walk_namespace + * + * RETURN: Status + * + * DESCRIPTION: Display info about device objects that have a corresponding + * _PRT method. + * + ******************************************************************************/ + +static acpi_status +acpi_db_bus_walk(acpi_handle obj_handle, + u32 nesting_level, void *context, void **return_value) +{ + struct acpi_namespace_node *node = + (struct acpi_namespace_node *)obj_handle; + acpi_status status; + struct acpi_buffer buffer; + struct acpi_namespace_node *temp_node; + struct acpi_device_info *info; + u32 i; + + if ((node->type != ACPI_TYPE_DEVICE) && + (node->type != ACPI_TYPE_PROCESSOR)) { + return (AE_OK); + } + + /* Exit if there is no _PRT under this device */ + + status = acpi_get_handle(node, METHOD_NAME__PRT, + ACPI_CAST_PTR(acpi_handle, &temp_node)); + if (ACPI_FAILURE(status)) { + return (AE_OK); + } + + /* Get the full path to this device object */ + + buffer.length = ACPI_ALLOCATE_LOCAL_BUFFER; + status = acpi_ns_handle_to_pathname(obj_handle, &buffer, TRUE); + if (ACPI_FAILURE(status)) { + acpi_os_printf("Could Not get pathname for object %p\n", + obj_handle); + return (AE_OK); + } + + status = acpi_get_object_info(obj_handle, &info); + if (ACPI_FAILURE(status)) { + return (AE_OK); + } + + /* Display the full path */ + + acpi_os_printf("%-32s Type %X", (char *)buffer.pointer, node->type); + ACPI_FREE(buffer.pointer); + + if (info->flags & ACPI_PCI_ROOT_BRIDGE) { + acpi_os_printf(" - Is PCI Root Bridge"); + } + acpi_os_printf("\n"); + + /* _PRT info */ + + acpi_os_printf("_PRT: %p\n", temp_node); + + /* Dump _ADR, _HID, _UID, _CID */ + + if (info->valid & ACPI_VALID_ADR) { + acpi_os_printf("_ADR: %8.8X%8.8X\n", + ACPI_FORMAT_UINT64(info->address)); + } else { + acpi_os_printf("_ADR: \n"); + } + + if (info->valid & ACPI_VALID_HID) { + acpi_os_printf("_HID: %s\n", info->hardware_id.string); + } else { + acpi_os_printf("_HID: \n"); + } + + if (info->valid & ACPI_VALID_UID) { + acpi_os_printf("_UID: %s\n", info->unique_id.string); + } else { + acpi_os_printf("_UID: \n"); + } + + if (info->valid & ACPI_VALID_CID) { + for (i = 0; i < info->compatible_id_list.count; i++) { + acpi_os_printf("_CID: %s\n", + info->compatible_id_list.ids[i].string); + } + } else { + acpi_os_printf("_CID: \n"); + } + + ACPI_FREE(info); + return (AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_get_bus_info + * + * PARAMETERS: None + * + * RETURN: None + * + * DESCRIPTION: Display info about system busses. + * + ******************************************************************************/ + +void acpi_db_get_bus_info(void) +{ + /* Search all nodes in namespace */ + + (void)acpi_walk_namespace(ACPI_TYPE_ANY, ACPI_ROOT_OBJECT, + ACPI_UINT32_MAX, acpi_db_bus_walk, NULL, NULL, + NULL); +} diff --git a/drivers/acpi/acpica/dbobject.c b/drivers/acpi/acpica/dbobject.c new file mode 100644 index 0000000..116f6db8 --- /dev/null +++ b/drivers/acpi/acpica/dbobject.c @@ -0,0 +1,533 @@ +/******************************************************************************* + * + * Module Name: dbobject - ACPI object decode and display + * + ******************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acnamesp.h" +#include "acdebug.h" + +#define _COMPONENT ACPI_CA_DEBUGGER +ACPI_MODULE_NAME("dbobject") + +/* Local prototypes */ +static void acpi_db_decode_node(struct acpi_namespace_node *node); + +/******************************************************************************* + * + * FUNCTION: acpi_db_dump_method_info + * + * PARAMETERS: status - Method execution status + * walk_state - Current state of the parse tree walk + * + * RETURN: None + * + * DESCRIPTION: Called when a method has been aborted because of an error. + * Dumps the method execution stack, and the method locals/args, + * and disassembles the AML opcode that failed. + * + ******************************************************************************/ + +void +acpi_db_dump_method_info(acpi_status status, struct acpi_walk_state *walk_state) +{ + struct acpi_thread_state *thread; + + /* Ignore control codes, they are not errors */ + + if ((status & AE_CODE_MASK) == AE_CODE_CONTROL) { + return; + } + + /* We may be executing a deferred opcode */ + + if (walk_state->deferred_node) { + acpi_os_printf("Executing subtree for Buffer/Package/Region\n"); + return; + } + + /* + * If there is no Thread, we are not actually executing a method. + * This can happen when the iASL compiler calls the interpreter + * to perform constant folding. + */ + thread = walk_state->thread; + if (!thread) { + return; + } + + /* Display the method locals and arguments */ + + acpi_os_printf("\n"); + acpi_db_decode_locals(walk_state); + acpi_os_printf("\n"); + acpi_db_decode_arguments(walk_state); + acpi_os_printf("\n"); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_decode_internal_object + * + * PARAMETERS: obj_desc - Object to be displayed + * + * RETURN: None + * + * DESCRIPTION: Short display of an internal object. Numbers/Strings/Buffers. + * + ******************************************************************************/ + +void acpi_db_decode_internal_object(union acpi_operand_object *obj_desc) +{ + u32 i; + + if (!obj_desc) { + acpi_os_printf(" Uninitialized"); + return; + } + + if (ACPI_GET_DESCRIPTOR_TYPE(obj_desc) != ACPI_DESC_TYPE_OPERAND) { + acpi_os_printf(" %p [%s]", obj_desc, + acpi_ut_get_descriptor_name(obj_desc)); + return; + } + + acpi_os_printf(" %s", acpi_ut_get_object_type_name(obj_desc)); + + switch (obj_desc->common.type) { + case ACPI_TYPE_INTEGER: + + acpi_os_printf(" %8.8X%8.8X", + ACPI_FORMAT_UINT64(obj_desc->integer.value)); + break; + + case ACPI_TYPE_STRING: + + acpi_os_printf("(%u) \"%.24s", + obj_desc->string.length, + obj_desc->string.pointer); + + if (obj_desc->string.length > 24) { + acpi_os_printf("..."); + } else { + acpi_os_printf("\""); + } + break; + + case ACPI_TYPE_BUFFER: + + acpi_os_printf("(%u)", obj_desc->buffer.length); + for (i = 0; (i < 8) && (i < obj_desc->buffer.length); i++) { + acpi_os_printf(" %2.2X", obj_desc->buffer.pointer[i]); + } + break; + + default: + + acpi_os_printf(" %p", obj_desc); + break; + } +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_decode_node + * + * PARAMETERS: node - Object to be displayed + * + * RETURN: None + * + * DESCRIPTION: Short display of a namespace node + * + ******************************************************************************/ + +static void acpi_db_decode_node(struct acpi_namespace_node *node) +{ + + acpi_os_printf(" Name %4.4s", + acpi_ut_get_node_name(node)); + + if (node->flags & ANOBJ_METHOD_ARG) { + acpi_os_printf(" [Method Arg]"); + } + if (node->flags & ANOBJ_METHOD_LOCAL) { + acpi_os_printf(" [Method Local]"); + } + + switch (node->type) { + + /* These types have no attached object */ + + case ACPI_TYPE_DEVICE: + + acpi_os_printf(" Device"); + break; + + case ACPI_TYPE_THERMAL: + + acpi_os_printf(" Thermal Zone"); + break; + + default: + + acpi_db_decode_internal_object(acpi_ns_get_attached_object + (node)); + break; + } +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_display_internal_object + * + * PARAMETERS: obj_desc - Object to be displayed + * walk_state - Current walk state + * + * RETURN: None + * + * DESCRIPTION: Short display of an internal object + * + ******************************************************************************/ + +void +acpi_db_display_internal_object(union acpi_operand_object *obj_desc, + struct acpi_walk_state *walk_state) +{ + u8 type; + + acpi_os_printf("%p ", obj_desc); + + if (!obj_desc) { + acpi_os_printf("\n"); + return; + } + + /* Decode the object type */ + + switch (ACPI_GET_DESCRIPTOR_TYPE(obj_desc)) { + case ACPI_DESC_TYPE_PARSER: + + acpi_os_printf(" "); + break; + + case ACPI_DESC_TYPE_NAMED: + + acpi_db_decode_node((struct acpi_namespace_node *)obj_desc); + break; + + case ACPI_DESC_TYPE_OPERAND: + + type = obj_desc->common.type; + if (type > ACPI_TYPE_LOCAL_MAX) { + acpi_os_printf(" Type %X [Invalid Type]", (u32)type); + return; + } + + /* Decode the ACPI object type */ + + switch (obj_desc->common.type) { + case ACPI_TYPE_LOCAL_REFERENCE: + + acpi_os_printf("[%s] ", + acpi_ut_get_reference_name(obj_desc)); + + /* Decode the refererence */ + + switch (obj_desc->reference.class) { + case ACPI_REFCLASS_LOCAL: + + acpi_os_printf("%X ", + obj_desc->reference.value); + if (walk_state) { + obj_desc = walk_state->local_variables + [obj_desc->reference.value].object; + acpi_os_printf("%p", obj_desc); + acpi_db_decode_internal_object + (obj_desc); + } + break; + + case ACPI_REFCLASS_ARG: + + acpi_os_printf("%X ", + obj_desc->reference.value); + if (walk_state) { + obj_desc = walk_state->arguments + [obj_desc->reference.value].object; + acpi_os_printf("%p", obj_desc); + acpi_db_decode_internal_object + (obj_desc); + } + break; + + case ACPI_REFCLASS_INDEX: + + switch (obj_desc->reference.target_type) { + case ACPI_TYPE_BUFFER_FIELD: + + acpi_os_printf("%p", + obj_desc->reference. + object); + acpi_db_decode_internal_object + (obj_desc->reference.object); + break; + + case ACPI_TYPE_PACKAGE: + + acpi_os_printf("%p", + obj_desc->reference. + where); + if (!obj_desc->reference.where) { + acpi_os_printf + (" Uninitialized WHERE pointer"); + } else { + acpi_db_decode_internal_object(* + (obj_desc-> + reference. + where)); + } + break; + + default: + + acpi_os_printf + ("Unknown index target type"); + break; + } + break; + + case ACPI_REFCLASS_REFOF: + + if (!obj_desc->reference.object) { + acpi_os_printf + ("Uninitialized reference subobject pointer"); + break; + } + + /* Reference can be to a Node or an Operand object */ + + switch (ACPI_GET_DESCRIPTOR_TYPE + (obj_desc->reference.object)) { + case ACPI_DESC_TYPE_NAMED: + + acpi_db_decode_node(obj_desc->reference. + object); + break; + + case ACPI_DESC_TYPE_OPERAND: + + acpi_db_decode_internal_object + (obj_desc->reference.object); + break; + + default: + break; + } + break; + + case ACPI_REFCLASS_NAME: + + acpi_db_decode_node(obj_desc->reference.node); + break; + + case ACPI_REFCLASS_DEBUG: + case ACPI_REFCLASS_TABLE: + + acpi_os_printf("\n"); + break; + + default: /* Unknown reference class */ + + acpi_os_printf("%2.2X\n", + obj_desc->reference.class); + break; + } + break; + + default: + + acpi_os_printf(" "); + acpi_db_decode_internal_object(obj_desc); + break; + } + break; + + default: + + acpi_os_printf(" [%s]", + acpi_ut_get_descriptor_name(obj_desc)); + break; + } + + acpi_os_printf("\n"); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_decode_locals + * + * PARAMETERS: walk_state - State for current method + * + * RETURN: None + * + * DESCRIPTION: Display all locals for the currently running control method + * + ******************************************************************************/ + +void acpi_db_decode_locals(struct acpi_walk_state *walk_state) +{ + u32 i; + union acpi_operand_object *obj_desc; + struct acpi_namespace_node *node; + u8 display_locals = FALSE; + + obj_desc = walk_state->method_desc; + node = walk_state->method_node; + + if (!node) { + acpi_os_printf + ("No method node (Executing subtree for buffer or opregion)\n"); + return; + } + + if (node->type != ACPI_TYPE_METHOD) { + acpi_os_printf("Executing subtree for Buffer/Package/Region\n"); + return; + } + + /* Are any locals actually set? */ + + for (i = 0; i < ACPI_METHOD_NUM_LOCALS; i++) { + obj_desc = walk_state->local_variables[i].object; + if (obj_desc) { + display_locals = TRUE; + break; + } + } + + /* If any are set, only display the ones that are set */ + + if (display_locals) { + acpi_os_printf + ("\nInitialized Local Variables for method [%4.4s]:\n", + acpi_ut_get_node_name(node)); + + for (i = 0; i < ACPI_METHOD_NUM_LOCALS; i++) { + obj_desc = walk_state->local_variables[i].object; + if (obj_desc) { + acpi_os_printf(" Local%X: ", i); + acpi_db_display_internal_object(obj_desc, + walk_state); + } + } + } else { + acpi_os_printf + ("No Local Variables are initialized for method [%4.4s]\n", + acpi_ut_get_node_name(node)); + } +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_decode_arguments + * + * PARAMETERS: walk_state - State for current method + * + * RETURN: None + * + * DESCRIPTION: Display all arguments for the currently running control method + * + ******************************************************************************/ + +void acpi_db_decode_arguments(struct acpi_walk_state *walk_state) +{ + u32 i; + union acpi_operand_object *obj_desc; + struct acpi_namespace_node *node; + u8 display_args = FALSE; + + node = walk_state->method_node; + obj_desc = walk_state->method_desc; + + if (!node) { + acpi_os_printf + ("No method node (Executing subtree for buffer or opregion)\n"); + return; + } + + if (node->type != ACPI_TYPE_METHOD) { + acpi_os_printf("Executing subtree for Buffer/Package/Region\n"); + return; + } + + /* Are any arguments actually set? */ + + for (i = 0; i < ACPI_METHOD_NUM_ARGS; i++) { + obj_desc = walk_state->arguments[i].object; + if (obj_desc) { + display_args = TRUE; + break; + } + } + + /* If any are set, only display the ones that are set */ + + if (display_args) { + acpi_os_printf("Initialized Arguments for Method [%4.4s]: " + "(%X arguments defined for method invocation)\n", + acpi_ut_get_node_name(node), + obj_desc->method.param_count); + + for (i = 0; i < ACPI_METHOD_NUM_ARGS; i++) { + obj_desc = walk_state->arguments[i].object; + if (obj_desc) { + acpi_os_printf(" Arg%u: ", i); + acpi_db_display_internal_object(obj_desc, + walk_state); + } + } + } else { + acpi_os_printf + ("No Arguments are initialized for method [%4.4s]\n", + acpi_ut_get_node_name(node)); + } +} diff --git a/drivers/acpi/acpica/dbstats.c b/drivers/acpi/acpica/dbstats.c new file mode 100644 index 0000000..4ba0a20 --- /dev/null +++ b/drivers/acpi/acpica/dbstats.c @@ -0,0 +1,546 @@ +/******************************************************************************* + * + * Module Name: dbstats - Generation and display of ACPI table statistics + * + ******************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acdebug.h" +#include "acnamesp.h" + +#define _COMPONENT ACPI_CA_DEBUGGER +ACPI_MODULE_NAME("dbstats") + +/* Local prototypes */ +static void acpi_db_count_namespace_objects(void); + +static void acpi_db_enumerate_object(union acpi_operand_object *obj_desc); + +static acpi_status +acpi_db_classify_one_object(acpi_handle obj_handle, + u32 nesting_level, + void *context, void **return_value); + +#if defined ACPI_DBG_TRACK_ALLOCATIONS || defined ACPI_USE_LOCAL_CACHE +static void acpi_db_list_info(struct acpi_memory_list *list); +#endif + +/* + * Statistics subcommands + */ +static struct acpi_db_argument_info acpi_db_stat_types[] = { + {"ALLOCATIONS"}, + {"OBJECTS"}, + {"MEMORY"}, + {"MISC"}, + {"TABLES"}, + {"SIZES"}, + {"STACK"}, + {NULL} /* Must be null terminated */ +}; + +#define CMD_STAT_ALLOCATIONS 0 +#define CMD_STAT_OBJECTS 1 +#define CMD_STAT_MEMORY 2 +#define CMD_STAT_MISC 3 +#define CMD_STAT_TABLES 4 +#define CMD_STAT_SIZES 5 +#define CMD_STAT_STACK 6 + +#if defined ACPI_DBG_TRACK_ALLOCATIONS || defined ACPI_USE_LOCAL_CACHE +/******************************************************************************* + * + * FUNCTION: acpi_db_list_info + * + * PARAMETERS: list - Memory list/cache to be displayed + * + * RETURN: None + * + * DESCRIPTION: Display information about the input memory list or cache. + * + ******************************************************************************/ + +static void acpi_db_list_info(struct acpi_memory_list *list) +{ +#ifdef ACPI_DBG_TRACK_ALLOCATIONS + u32 outstanding; +#endif + + acpi_os_printf("\n%s\n", list->list_name); + + /* max_depth > 0 indicates a cache object */ + + if (list->max_depth > 0) { + acpi_os_printf + (" Cache: [Depth MaxD Avail Size] " + "%8.2X %8.2X %8.2X %8.2X\n", list->current_depth, + list->max_depth, list->max_depth - list->current_depth, + (list->current_depth * list->object_size)); + } +#ifdef ACPI_DBG_TRACK_ALLOCATIONS + if (list->max_depth > 0) { + acpi_os_printf + (" Cache: [Requests Hits Misses ObjSize] " + "%8.2X %8.2X %8.2X %8.2X\n", list->requests, list->hits, + list->requests - list->hits, list->object_size); + } + + outstanding = acpi_db_get_cache_info(list); + + if (list->object_size) { + acpi_os_printf + (" Mem: [Alloc Free Max CurSize Outstanding] " + "%8.2X %8.2X %8.2X %8.2X %8.2X\n", list->total_allocated, + list->total_freed, list->max_occupied, + outstanding * list->object_size, outstanding); + } else { + acpi_os_printf + (" Mem: [Alloc Free Max CurSize Outstanding Total] " + "%8.2X %8.2X %8.2X %8.2X %8.2X %8.2X\n", + list->total_allocated, list->total_freed, + list->max_occupied, list->current_total_size, outstanding, + list->total_size); + } +#endif +} +#endif + +/******************************************************************************* + * + * FUNCTION: acpi_db_enumerate_object + * + * PARAMETERS: obj_desc - Object to be counted + * + * RETURN: None + * + * DESCRIPTION: Add this object to the global counts, by object type. + * Limited recursion handles subobjects and packages, and this + * is probably acceptable within the AML debugger only. + * + ******************************************************************************/ + +static void acpi_db_enumerate_object(union acpi_operand_object *obj_desc) +{ + u32 i; + + if (!obj_desc) { + return; + } + + /* Enumerate this object first */ + + acpi_gbl_num_objects++; + + if (obj_desc->common.type > ACPI_TYPE_NS_NODE_MAX) { + acpi_gbl_obj_type_count_misc++; + } else { + acpi_gbl_obj_type_count[obj_desc->common.type]++; + } + + /* Count the sub-objects */ + + switch (obj_desc->common.type) { + case ACPI_TYPE_PACKAGE: + + for (i = 0; i < obj_desc->package.count; i++) { + acpi_db_enumerate_object(obj_desc->package.elements[i]); + } + break; + + case ACPI_TYPE_DEVICE: + + acpi_db_enumerate_object(obj_desc->device.notify_list[0]); + acpi_db_enumerate_object(obj_desc->device.notify_list[1]); + acpi_db_enumerate_object(obj_desc->device.handler); + break; + + case ACPI_TYPE_BUFFER_FIELD: + + if (acpi_ns_get_secondary_object(obj_desc)) { + acpi_gbl_obj_type_count[ACPI_TYPE_BUFFER_FIELD]++; + } + break; + + case ACPI_TYPE_REGION: + + acpi_gbl_obj_type_count[ACPI_TYPE_LOCAL_REGION_FIELD]++; + acpi_db_enumerate_object(obj_desc->region.handler); + break; + + case ACPI_TYPE_POWER: + + acpi_db_enumerate_object(obj_desc->power_resource. + notify_list[0]); + acpi_db_enumerate_object(obj_desc->power_resource. + notify_list[1]); + break; + + case ACPI_TYPE_PROCESSOR: + + acpi_db_enumerate_object(obj_desc->processor.notify_list[0]); + acpi_db_enumerate_object(obj_desc->processor.notify_list[1]); + acpi_db_enumerate_object(obj_desc->processor.handler); + break; + + case ACPI_TYPE_THERMAL: + + acpi_db_enumerate_object(obj_desc->thermal_zone.notify_list[0]); + acpi_db_enumerate_object(obj_desc->thermal_zone.notify_list[1]); + acpi_db_enumerate_object(obj_desc->thermal_zone.handler); + break; + + default: + + break; + } +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_classify_one_object + * + * PARAMETERS: Callback for walk_namespace + * + * RETURN: Status + * + * DESCRIPTION: Enumerate both the object descriptor (including subobjects) and + * the parent namespace node. + * + ******************************************************************************/ + +static acpi_status +acpi_db_classify_one_object(acpi_handle obj_handle, + u32 nesting_level, + void *context, void **return_value) +{ + struct acpi_namespace_node *node; + union acpi_operand_object *obj_desc; + u32 type; + + acpi_gbl_num_nodes++; + + node = (struct acpi_namespace_node *)obj_handle; + obj_desc = acpi_ns_get_attached_object(node); + + acpi_db_enumerate_object(obj_desc); + + type = node->type; + if (type > ACPI_TYPE_NS_NODE_MAX) { + acpi_gbl_node_type_count_misc++; + } else { + acpi_gbl_node_type_count[type]++; + } + + return (AE_OK); + +#ifdef ACPI_FUTURE_IMPLEMENTATION + + /* TBD: These need to be counted during the initial parsing phase */ + + if (acpi_ps_is_named_op(op->opcode)) { + num_nodes++; + } + + if (is_method) { + num_method_elements++; + } + + num_grammar_elements++; + op = acpi_ps_get_depth_next(root, op); + + size_of_parse_tree = (num_grammar_elements - num_method_elements) * + (u32)sizeof(union acpi_parse_object); + size_of_method_trees = + num_method_elements * (u32)sizeof(union acpi_parse_object); + size_of_node_entries = + num_nodes * (u32)sizeof(struct acpi_namespace_node); + size_of_acpi_objects = + num_nodes * (u32)sizeof(union acpi_operand_object); +#endif +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_count_namespace_objects + * + * PARAMETERS: None + * + * RETURN: None + * + * DESCRIPTION: Count and classify the entire namespace, including all + * namespace nodes and attached objects. + * + ******************************************************************************/ + +static void acpi_db_count_namespace_objects(void) +{ + u32 i; + + acpi_gbl_num_nodes = 0; + acpi_gbl_num_objects = 0; + + acpi_gbl_obj_type_count_misc = 0; + for (i = 0; i < (ACPI_TYPE_NS_NODE_MAX - 1); i++) { + acpi_gbl_obj_type_count[i] = 0; + acpi_gbl_node_type_count[i] = 0; + } + + (void)acpi_ns_walk_namespace(ACPI_TYPE_ANY, ACPI_ROOT_OBJECT, + ACPI_UINT32_MAX, FALSE, + acpi_db_classify_one_object, NULL, NULL, + NULL); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_display_statistics + * + * PARAMETERS: type_arg - Subcommand + * + * RETURN: Status + * + * DESCRIPTION: Display various statistics + * + ******************************************************************************/ + +acpi_status acpi_db_display_statistics(char *type_arg) +{ + u32 i; + u32 temp; + + acpi_ut_strupr(type_arg); + temp = acpi_db_match_argument(type_arg, acpi_db_stat_types); + if (temp == ACPI_TYPE_NOT_FOUND) { + acpi_os_printf("Invalid or unsupported argument\n"); + return (AE_OK); + } + + switch (temp) { + case CMD_STAT_ALLOCATIONS: + +#ifdef ACPI_DBG_TRACK_ALLOCATIONS + acpi_ut_dump_allocation_info(); +#endif + break; + + case CMD_STAT_TABLES: + + acpi_os_printf("ACPI Table Information (not implemented):\n\n"); + break; + + case CMD_STAT_OBJECTS: + + acpi_db_count_namespace_objects(); + + acpi_os_printf + ("\nObjects defined in the current namespace:\n\n"); + + acpi_os_printf("%16.16s %10.10s %10.10s\n", + "ACPI_TYPE", "NODES", "OBJECTS"); + + for (i = 0; i < ACPI_TYPE_NS_NODE_MAX; i++) { + acpi_os_printf("%16.16s % 10ld% 10ld\n", + acpi_ut_get_type_name(i), + acpi_gbl_node_type_count[i], + acpi_gbl_obj_type_count[i]); + } + acpi_os_printf("%16.16s % 10ld% 10ld\n", "Misc/Unknown", + acpi_gbl_node_type_count_misc, + acpi_gbl_obj_type_count_misc); + + acpi_os_printf("%16.16s % 10ld% 10ld\n", "TOTALS:", + acpi_gbl_num_nodes, acpi_gbl_num_objects); + break; + + case CMD_STAT_MEMORY: + +#ifdef ACPI_DBG_TRACK_ALLOCATIONS + acpi_os_printf + ("\n----Object Statistics (all in hex)---------\n"); + + acpi_db_list_info(acpi_gbl_global_list); + acpi_db_list_info(acpi_gbl_ns_node_list); +#endif + +#ifdef ACPI_USE_LOCAL_CACHE + acpi_os_printf + ("\n----Cache Statistics (all in hex)---------\n"); + acpi_db_list_info(acpi_gbl_operand_cache); + acpi_db_list_info(acpi_gbl_ps_node_cache); + acpi_db_list_info(acpi_gbl_ps_node_ext_cache); + acpi_db_list_info(acpi_gbl_state_cache); +#endif + + break; + + case CMD_STAT_MISC: + + acpi_os_printf("\nMiscellaneous Statistics:\n\n"); + acpi_os_printf("Calls to AcpiPsFind:.. ........% 7ld\n", + acpi_gbl_ps_find_count); + acpi_os_printf("Calls to AcpiNsLookup:..........% 7ld\n", + acpi_gbl_ns_lookup_count); + + acpi_os_printf("\n"); + + acpi_os_printf("Mutex usage:\n\n"); + for (i = 0; i < ACPI_NUM_MUTEX; i++) { + acpi_os_printf("%-28s: % 7ld\n", + acpi_ut_get_mutex_name(i), + acpi_gbl_mutex_info[i].use_count); + } + break; + + case CMD_STAT_SIZES: + + acpi_os_printf("\nInternal object sizes:\n\n"); + + acpi_os_printf("Common %3d\n", + sizeof(struct acpi_object_common)); + acpi_os_printf("Number %3d\n", + sizeof(struct acpi_object_integer)); + acpi_os_printf("String %3d\n", + sizeof(struct acpi_object_string)); + acpi_os_printf("Buffer %3d\n", + sizeof(struct acpi_object_buffer)); + acpi_os_printf("Package %3d\n", + sizeof(struct acpi_object_package)); + acpi_os_printf("BufferField %3d\n", + sizeof(struct acpi_object_buffer_field)); + acpi_os_printf("Device %3d\n", + sizeof(struct acpi_object_device)); + acpi_os_printf("Event %3d\n", + sizeof(struct acpi_object_event)); + acpi_os_printf("Method %3d\n", + sizeof(struct acpi_object_method)); + acpi_os_printf("Mutex %3d\n", + sizeof(struct acpi_object_mutex)); + acpi_os_printf("Region %3d\n", + sizeof(struct acpi_object_region)); + acpi_os_printf("PowerResource %3d\n", + sizeof(struct acpi_object_power_resource)); + acpi_os_printf("Processor %3d\n", + sizeof(struct acpi_object_processor)); + acpi_os_printf("ThermalZone %3d\n", + sizeof(struct acpi_object_thermal_zone)); + acpi_os_printf("RegionField %3d\n", + sizeof(struct acpi_object_region_field)); + acpi_os_printf("BankField %3d\n", + sizeof(struct acpi_object_bank_field)); + acpi_os_printf("IndexField %3d\n", + sizeof(struct acpi_object_index_field)); + acpi_os_printf("Reference %3d\n", + sizeof(struct acpi_object_reference)); + acpi_os_printf("Notify %3d\n", + sizeof(struct acpi_object_notify_handler)); + acpi_os_printf("AddressSpace %3d\n", + sizeof(struct acpi_object_addr_handler)); + acpi_os_printf("Extra %3d\n", + sizeof(struct acpi_object_extra)); + acpi_os_printf("Data %3d\n", + sizeof(struct acpi_object_data)); + + acpi_os_printf("\n"); + + acpi_os_printf("ParseObject %3d\n", + sizeof(struct acpi_parse_obj_common)); + acpi_os_printf("ParseObjectNamed %3d\n", + sizeof(struct acpi_parse_obj_named)); + acpi_os_printf("ParseObjectAsl %3d\n", + sizeof(struct acpi_parse_obj_asl)); + acpi_os_printf("OperandObject %3d\n", + sizeof(union acpi_operand_object)); + acpi_os_printf("NamespaceNode %3d\n", + sizeof(struct acpi_namespace_node)); + acpi_os_printf("AcpiObject %3d\n", + sizeof(union acpi_object)); + + acpi_os_printf("\n"); + + acpi_os_printf("Generic State %3d\n", + sizeof(union acpi_generic_state)); + acpi_os_printf("Common State %3d\n", + sizeof(struct acpi_common_state)); + acpi_os_printf("Control State %3d\n", + sizeof(struct acpi_control_state)); + acpi_os_printf("Update State %3d\n", + sizeof(struct acpi_update_state)); + acpi_os_printf("Scope State %3d\n", + sizeof(struct acpi_scope_state)); + acpi_os_printf("Parse Scope %3d\n", + sizeof(struct acpi_pscope_state)); + acpi_os_printf("Package State %3d\n", + sizeof(struct acpi_pkg_state)); + acpi_os_printf("Thread State %3d\n", + sizeof(struct acpi_thread_state)); + acpi_os_printf("Result Values %3d\n", + sizeof(struct acpi_result_values)); + acpi_os_printf("Notify Info %3d\n", + sizeof(struct acpi_notify_info)); + break; + + case CMD_STAT_STACK: +#if defined(ACPI_DEBUG_OUTPUT) + + temp = + (u32)ACPI_PTR_DIFF(acpi_gbl_entry_stack_pointer, + acpi_gbl_lowest_stack_pointer); + + acpi_os_printf("\nSubsystem Stack Usage:\n\n"); + acpi_os_printf("Entry Stack Pointer %p\n", + acpi_gbl_entry_stack_pointer); + acpi_os_printf("Lowest Stack Pointer %p\n", + acpi_gbl_lowest_stack_pointer); + acpi_os_printf("Stack Use %X (%u)\n", temp, + temp); + acpi_os_printf("Deepest Procedure Nesting %u\n", + acpi_gbl_deepest_nesting); +#endif + break; + + default: + + break; + } + + acpi_os_printf("\n"); + return (AE_OK); +} diff --git a/drivers/acpi/acpica/dbtest.c b/drivers/acpi/acpica/dbtest.c new file mode 100644 index 0000000..10ea8bf --- /dev/null +++ b/drivers/acpi/acpica/dbtest.c @@ -0,0 +1,1057 @@ +/******************************************************************************* + * + * Module Name: dbtest - Various debug-related tests + * + ******************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acdebug.h" +#include "acnamesp.h" +#include "acpredef.h" + +#define _COMPONENT ACPI_CA_DEBUGGER +ACPI_MODULE_NAME("dbtest") + +/* Local prototypes */ +static void acpi_db_test_all_objects(void); + +static acpi_status +acpi_db_test_one_object(acpi_handle obj_handle, + u32 nesting_level, void *context, void **return_value); + +static acpi_status +acpi_db_test_integer_type(struct acpi_namespace_node *node, u32 bit_length); + +static acpi_status +acpi_db_test_buffer_type(struct acpi_namespace_node *node, u32 bit_length); + +static acpi_status +acpi_db_test_string_type(struct acpi_namespace_node *node, u32 byte_length); + +static acpi_status +acpi_db_read_from_object(struct acpi_namespace_node *node, + acpi_object_type expected_type, + union acpi_object **value); + +static acpi_status +acpi_db_write_to_object(struct acpi_namespace_node *node, + union acpi_object *value); + +static void acpi_db_evaluate_all_predefined_names(char *count_arg); + +static acpi_status +acpi_db_evaluate_one_predefined_name(acpi_handle obj_handle, + u32 nesting_level, + void *context, void **return_value); + +/* + * Test subcommands + */ +static struct acpi_db_argument_info acpi_db_test_types[] = { + {"OBJECTS"}, + {"PREDEFINED"}, + {NULL} /* Must be null terminated */ +}; + +#define CMD_TEST_OBJECTS 0 +#define CMD_TEST_PREDEFINED 1 + +#define BUFFER_FILL_VALUE 0xFF + +/* + * Support for the special debugger read/write control methods. + * These methods are installed into the current namespace and are + * used to read and write the various namespace objects. The point + * is to force the AML interpreter do all of the work. + */ +#define ACPI_DB_READ_METHOD "\\_T98" +#define ACPI_DB_WRITE_METHOD "\\_T99" + +static acpi_handle read_handle = NULL; +static acpi_handle write_handle = NULL; + +/* ASL Definitions of the debugger read/write control methods */ + +#if 0 +definition_block("ssdt.aml", "SSDT", 2, "Intel", "DEBUG", 0x00000001) +{ + method(_T98, 1, not_serialized) { /* Read */ + return (de_ref_of(arg0)) + } +} + +definition_block("ssdt2.aml", "SSDT", 2, "Intel", "DEBUG", 0x00000001) +{ + method(_T99, 2, not_serialized) { /* Write */ + store(arg1, arg0) + } +} +#endif + +static unsigned char read_method_code[] = { + 0x53, 0x53, 0x44, 0x54, 0x2E, 0x00, 0x00, 0x00, /* 00000000 "SSDT...." */ + 0x02, 0xC9, 0x49, 0x6E, 0x74, 0x65, 0x6C, 0x00, /* 00000008 "..Intel." */ + 0x44, 0x45, 0x42, 0x55, 0x47, 0x00, 0x00, 0x00, /* 00000010 "DEBUG..." */ + 0x01, 0x00, 0x00, 0x00, 0x49, 0x4E, 0x54, 0x4C, /* 00000018 "....INTL" */ + 0x18, 0x12, 0x13, 0x20, 0x14, 0x09, 0x5F, 0x54, /* 00000020 "... .._T" */ + 0x39, 0x38, 0x01, 0xA4, 0x83, 0x68 /* 00000028 "98...h" */ +}; + +static unsigned char write_method_code[] = { + 0x53, 0x53, 0x44, 0x54, 0x2E, 0x00, 0x00, 0x00, /* 00000000 "SSDT...." */ + 0x02, 0x15, 0x49, 0x6E, 0x74, 0x65, 0x6C, 0x00, /* 00000008 "..Intel." */ + 0x44, 0x45, 0x42, 0x55, 0x47, 0x00, 0x00, 0x00, /* 00000010 "DEBUG..." */ + 0x01, 0x00, 0x00, 0x00, 0x49, 0x4E, 0x54, 0x4C, /* 00000018 "....INTL" */ + 0x18, 0x12, 0x13, 0x20, 0x14, 0x09, 0x5F, 0x54, /* 00000020 "... .._T" */ + 0x39, 0x39, 0x02, 0x70, 0x69, 0x68 /* 00000028 "99.pih" */ +}; + +/******************************************************************************* + * + * FUNCTION: acpi_db_execute_test + * + * PARAMETERS: type_arg - Subcommand + * + * RETURN: None + * + * DESCRIPTION: Execute various debug tests. + * + * Note: Code is prepared for future expansion of the TEST command. + * + ******************************************************************************/ + +void acpi_db_execute_test(char *type_arg) +{ + u32 temp; + + acpi_ut_strupr(type_arg); + temp = acpi_db_match_argument(type_arg, acpi_db_test_types); + if (temp == ACPI_TYPE_NOT_FOUND) { + acpi_os_printf("Invalid or unsupported argument\n"); + return; + } + + switch (temp) { + case CMD_TEST_OBJECTS: + + acpi_db_test_all_objects(); + break; + + case CMD_TEST_PREDEFINED: + + acpi_db_evaluate_all_predefined_names(NULL); + break; + + default: + break; + } +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_test_all_objects + * + * PARAMETERS: None + * + * RETURN: None + * + * DESCRIPTION: This test implements the OBJECTS subcommand. It exercises the + * namespace by reading/writing/comparing all data objects such + * as integers, strings, buffers, fields, buffer fields, etc. + * + ******************************************************************************/ + +static void acpi_db_test_all_objects(void) +{ + acpi_status status; + + /* Install the debugger read-object control method if necessary */ + + if (!read_handle) { + status = acpi_install_method(read_method_code); + if (ACPI_FAILURE(status)) { + acpi_os_printf + ("%s, Could not install debugger read method\n", + acpi_format_exception(status)); + return; + } + + status = + acpi_get_handle(NULL, ACPI_DB_READ_METHOD, &read_handle); + if (ACPI_FAILURE(status)) { + acpi_os_printf + ("Could not obtain handle for debug method %s\n", + ACPI_DB_READ_METHOD); + return; + } + } + + /* Install the debugger write-object control method if necessary */ + + if (!write_handle) { + status = acpi_install_method(write_method_code); + if (ACPI_FAILURE(status)) { + acpi_os_printf + ("%s, Could not install debugger write method\n", + acpi_format_exception(status)); + return; + } + + status = + acpi_get_handle(NULL, ACPI_DB_WRITE_METHOD, &write_handle); + if (ACPI_FAILURE(status)) { + acpi_os_printf + ("Could not obtain handle for debug method %s\n", + ACPI_DB_WRITE_METHOD); + return; + } + } + + /* Walk the entire namespace, testing each supported named data object */ + + (void)acpi_walk_namespace(ACPI_TYPE_ANY, ACPI_ROOT_OBJECT, + ACPI_UINT32_MAX, acpi_db_test_one_object, + NULL, NULL, NULL); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_test_one_object + * + * PARAMETERS: acpi_walk_callback + * + * RETURN: Status + * + * DESCRIPTION: Test one namespace object. Supported types are Integer, + * String, Buffer, buffer_field, and field_unit. All other object + * types are simply ignored. + * + * Note: Support for Packages is not implemented. + * + ******************************************************************************/ + +static acpi_status +acpi_db_test_one_object(acpi_handle obj_handle, + u32 nesting_level, void *context, void **return_value) +{ + struct acpi_namespace_node *node; + union acpi_operand_object *obj_desc; + union acpi_operand_object *region_obj; + acpi_object_type local_type; + u32 bit_length = 0; + u32 byte_length = 0; + acpi_status status = AE_OK; + + node = ACPI_CAST_PTR(struct acpi_namespace_node, obj_handle); + obj_desc = node->object; + + /* + * For the supported types, get the actual bit length or + * byte length. Map the type to one of Integer/String/Buffer. + */ + switch (node->type) { + case ACPI_TYPE_INTEGER: + + /* Integer width is either 32 or 64 */ + + local_type = ACPI_TYPE_INTEGER; + bit_length = acpi_gbl_integer_bit_width; + break; + + case ACPI_TYPE_STRING: + + local_type = ACPI_TYPE_STRING; + byte_length = obj_desc->string.length; + break; + + case ACPI_TYPE_BUFFER: + + local_type = ACPI_TYPE_BUFFER; + byte_length = obj_desc->buffer.length; + bit_length = byte_length * 8; + break; + + case ACPI_TYPE_FIELD_UNIT: + case ACPI_TYPE_BUFFER_FIELD: + case ACPI_TYPE_LOCAL_REGION_FIELD: + case ACPI_TYPE_LOCAL_INDEX_FIELD: + case ACPI_TYPE_LOCAL_BANK_FIELD: + + local_type = ACPI_TYPE_INTEGER; + if (obj_desc) { + /* + * Returned object will be a Buffer if the field length + * is larger than the size of an Integer (32 or 64 bits + * depending on the DSDT version). + */ + bit_length = obj_desc->common_field.bit_length; + byte_length = ACPI_ROUND_BITS_UP_TO_BYTES(bit_length); + if (bit_length > acpi_gbl_integer_bit_width) { + local_type = ACPI_TYPE_BUFFER; + } + } + break; + + default: + + /* Ignore all other types */ + + return (AE_OK); + } + + /* Emit the common prefix: Type:Name */ + + acpi_os_printf("%14s: %4.4s", + acpi_ut_get_type_name(node->type), node->name.ascii); + if (!obj_desc) { + acpi_os_printf(" Ignoring, no attached object\n"); + return (AE_OK); + } + + /* + * Check for unsupported region types. Note: acpi_exec simulates + * access to system_memory, system_IO, PCI_Config, and EC. + */ + switch (node->type) { + case ACPI_TYPE_LOCAL_REGION_FIELD: + + region_obj = obj_desc->field.region_obj; + switch (region_obj->region.space_id) { + case ACPI_ADR_SPACE_SYSTEM_MEMORY: + case ACPI_ADR_SPACE_SYSTEM_IO: + case ACPI_ADR_SPACE_PCI_CONFIG: + case ACPI_ADR_SPACE_EC: + + break; + + default: + + acpi_os_printf + (" %s space is not supported [%4.4s]\n", + acpi_ut_get_region_name(region_obj->region. + space_id), + region_obj->region.node->name.ascii); + return (AE_OK); + } + break; + + default: + break; + } + + /* At this point, we have resolved the object to one of the major types */ + + switch (local_type) { + case ACPI_TYPE_INTEGER: + + status = acpi_db_test_integer_type(node, bit_length); + break; + + case ACPI_TYPE_STRING: + + status = acpi_db_test_string_type(node, byte_length); + break; + + case ACPI_TYPE_BUFFER: + + status = acpi_db_test_buffer_type(node, bit_length); + break; + + default: + + acpi_os_printf(" Ignoring, type not implemented (%2.2X)", + local_type); + break; + } + + switch (node->type) { + case ACPI_TYPE_LOCAL_REGION_FIELD: + + region_obj = obj_desc->field.region_obj; + acpi_os_printf(" (%s)", + acpi_ut_get_region_name(region_obj->region. + space_id)); + break; + + default: + break; + } + + acpi_os_printf("\n"); + return (status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_test_integer_type + * + * PARAMETERS: node - Parent NS node for the object + * bit_length - Actual length of the object. Used for + * support of arbitrary length field_unit + * and buffer_field objects. + * + * RETURN: Status + * + * DESCRIPTION: Test read/write for an Integer-valued object. Performs a + * write/read/compare of an arbitrary new value, then performs + * a write/read/compare of the original value. + * + ******************************************************************************/ + +static acpi_status +acpi_db_test_integer_type(struct acpi_namespace_node *node, u32 bit_length) +{ + union acpi_object *temp1 = NULL; + union acpi_object *temp2 = NULL; + union acpi_object *temp3 = NULL; + union acpi_object write_value; + u64 value_to_write; + acpi_status status; + + if (bit_length > 64) { + acpi_os_printf(" Invalid length for an Integer: %u", + bit_length); + return (AE_OK); + } + + /* Read the original value */ + + status = acpi_db_read_from_object(node, ACPI_TYPE_INTEGER, &temp1); + if (ACPI_FAILURE(status)) { + return (status); + } + + acpi_os_printf(" (%4.4X/%3.3X) %8.8X%8.8X", + bit_length, ACPI_ROUND_BITS_UP_TO_BYTES(bit_length), + ACPI_FORMAT_UINT64(temp1->integer.value)); + + value_to_write = ACPI_UINT64_MAX >> (64 - bit_length); + if (temp1->integer.value == value_to_write) { + value_to_write = 0; + } + + /* Write a new value */ + + write_value.type = ACPI_TYPE_INTEGER; + write_value.integer.value = value_to_write; + status = acpi_db_write_to_object(node, &write_value); + if (ACPI_FAILURE(status)) { + goto exit; + } + + /* Ensure that we can read back the new value */ + + status = acpi_db_read_from_object(node, ACPI_TYPE_INTEGER, &temp2); + if (ACPI_FAILURE(status)) { + goto exit; + } + + if (temp2->integer.value != value_to_write) { + acpi_os_printf(" MISMATCH 2: %8.8X%8.8X, expecting %8.8X%8.8X", + ACPI_FORMAT_UINT64(temp2->integer.value), + ACPI_FORMAT_UINT64(value_to_write)); + } + + /* Write back the original value */ + + write_value.integer.value = temp1->integer.value; + status = acpi_db_write_to_object(node, &write_value); + if (ACPI_FAILURE(status)) { + goto exit; + } + + /* Ensure that we can read back the original value */ + + status = acpi_db_read_from_object(node, ACPI_TYPE_INTEGER, &temp3); + if (ACPI_FAILURE(status)) { + goto exit; + } + + if (temp3->integer.value != temp1->integer.value) { + acpi_os_printf(" MISMATCH 3: %8.8X%8.8X, expecting %8.8X%8.8X", + ACPI_FORMAT_UINT64(temp3->integer.value), + ACPI_FORMAT_UINT64(temp1->integer.value)); + } + +exit: + if (temp1) { + acpi_os_free(temp1); + } + if (temp2) { + acpi_os_free(temp2); + } + if (temp3) { + acpi_os_free(temp3); + } + return (AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_test_buffer_type + * + * PARAMETERS: node - Parent NS node for the object + * bit_length - Actual length of the object. + * + * RETURN: Status + * + * DESCRIPTION: Test read/write for an Buffer-valued object. Performs a + * write/read/compare of an arbitrary new value, then performs + * a write/read/compare of the original value. + * + ******************************************************************************/ + +static acpi_status +acpi_db_test_buffer_type(struct acpi_namespace_node *node, u32 bit_length) +{ + union acpi_object *temp1 = NULL; + union acpi_object *temp2 = NULL; + union acpi_object *temp3 = NULL; + u8 *buffer; + union acpi_object write_value; + acpi_status status; + u32 byte_length; + u32 i; + u8 extra_bits; + + byte_length = ACPI_ROUND_BITS_UP_TO_BYTES(bit_length); + if (byte_length == 0) { + acpi_os_printf(" Ignoring zero length buffer"); + return (AE_OK); + } + + /* Allocate a local buffer */ + + buffer = ACPI_ALLOCATE_ZEROED(byte_length); + if (!buffer) { + return (AE_NO_MEMORY); + } + + /* Read the original value */ + + status = acpi_db_read_from_object(node, ACPI_TYPE_BUFFER, &temp1); + if (ACPI_FAILURE(status)) { + goto exit; + } + + /* Emit a few bytes of the buffer */ + + acpi_os_printf(" (%4.4X/%3.3X)", bit_length, temp1->buffer.length); + for (i = 0; ((i < 4) && (i < byte_length)); i++) { + acpi_os_printf(" %2.2X", temp1->buffer.pointer[i]); + } + acpi_os_printf("... "); + + /* + * Write a new value. + * + * Handle possible extra bits at the end of the buffer. Can + * happen for field_units larger than an integer, but the bit + * count is not an integral number of bytes. Zero out the + * unused bits. + */ + memset(buffer, BUFFER_FILL_VALUE, byte_length); + extra_bits = bit_length % 8; + if (extra_bits) { + buffer[byte_length - 1] = ACPI_MASK_BITS_ABOVE(extra_bits); + } + + write_value.type = ACPI_TYPE_BUFFER; + write_value.buffer.length = byte_length; + write_value.buffer.pointer = buffer; + + status = acpi_db_write_to_object(node, &write_value); + if (ACPI_FAILURE(status)) { + goto exit; + } + + /* Ensure that we can read back the new value */ + + status = acpi_db_read_from_object(node, ACPI_TYPE_BUFFER, &temp2); + if (ACPI_FAILURE(status)) { + goto exit; + } + + if (memcmp(temp2->buffer.pointer, buffer, byte_length)) { + acpi_os_printf(" MISMATCH 2: New buffer value"); + } + + /* Write back the original value */ + + write_value.buffer.length = byte_length; + write_value.buffer.pointer = temp1->buffer.pointer; + + status = acpi_db_write_to_object(node, &write_value); + if (ACPI_FAILURE(status)) { + goto exit; + } + + /* Ensure that we can read back the original value */ + + status = acpi_db_read_from_object(node, ACPI_TYPE_BUFFER, &temp3); + if (ACPI_FAILURE(status)) { + goto exit; + } + + if (memcmp(temp1->buffer.pointer, temp3->buffer.pointer, byte_length)) { + acpi_os_printf(" MISMATCH 3: While restoring original buffer"); + } + +exit: + ACPI_FREE(buffer); + if (temp1) { + acpi_os_free(temp1); + } + if (temp2) { + acpi_os_free(temp2); + } + if (temp3) { + acpi_os_free(temp3); + } + return (status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_test_string_type + * + * PARAMETERS: node - Parent NS node for the object + * byte_length - Actual length of the object. + * + * RETURN: Status + * + * DESCRIPTION: Test read/write for an String-valued object. Performs a + * write/read/compare of an arbitrary new value, then performs + * a write/read/compare of the original value. + * + ******************************************************************************/ + +static acpi_status +acpi_db_test_string_type(struct acpi_namespace_node *node, u32 byte_length) +{ + union acpi_object *temp1 = NULL; + union acpi_object *temp2 = NULL; + union acpi_object *temp3 = NULL; + char *value_to_write = "Test String from AML Debugger"; + union acpi_object write_value; + acpi_status status; + + /* Read the original value */ + + status = acpi_db_read_from_object(node, ACPI_TYPE_STRING, &temp1); + if (ACPI_FAILURE(status)) { + return (status); + } + + acpi_os_printf(" (%4.4X/%3.3X) \"%s\"", (temp1->string.length * 8), + temp1->string.length, temp1->string.pointer); + + /* Write a new value */ + + write_value.type = ACPI_TYPE_STRING; + write_value.string.length = strlen(value_to_write); + write_value.string.pointer = value_to_write; + + status = acpi_db_write_to_object(node, &write_value); + if (ACPI_FAILURE(status)) { + goto exit; + } + + /* Ensure that we can read back the new value */ + + status = acpi_db_read_from_object(node, ACPI_TYPE_STRING, &temp2); + if (ACPI_FAILURE(status)) { + goto exit; + } + + if (strcmp(temp2->string.pointer, value_to_write)) { + acpi_os_printf(" MISMATCH 2: %s, expecting %s", + temp2->string.pointer, value_to_write); + } + + /* Write back the original value */ + + write_value.string.length = strlen(temp1->string.pointer); + write_value.string.pointer = temp1->string.pointer; + + status = acpi_db_write_to_object(node, &write_value); + if (ACPI_FAILURE(status)) { + goto exit; + } + + /* Ensure that we can read back the original value */ + + status = acpi_db_read_from_object(node, ACPI_TYPE_STRING, &temp3); + if (ACPI_FAILURE(status)) { + goto exit; + } + + if (strcmp(temp1->string.pointer, temp3->string.pointer)) { + acpi_os_printf(" MISMATCH 3: %s, expecting %s", + temp3->string.pointer, temp1->string.pointer); + } + +exit: + if (temp1) { + acpi_os_free(temp1); + } + if (temp2) { + acpi_os_free(temp2); + } + if (temp3) { + acpi_os_free(temp3); + } + return (status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_read_from_object + * + * PARAMETERS: node - Parent NS node for the object + * expected_type - Object type expected from the read + * value - Where the value read is returned + * + * RETURN: Status + * + * DESCRIPTION: Performs a read from the specified object by invoking the + * special debugger control method that reads the object. Thus, + * the AML interpreter is doing all of the work, increasing the + * validity of the test. + * + ******************************************************************************/ + +static acpi_status +acpi_db_read_from_object(struct acpi_namespace_node *node, + acpi_object_type expected_type, + union acpi_object **value) +{ + union acpi_object *ret_value; + struct acpi_object_list param_objects; + union acpi_object params[2]; + struct acpi_buffer return_obj; + acpi_status status; + + params[0].type = ACPI_TYPE_LOCAL_REFERENCE; + params[0].reference.actual_type = node->type; + params[0].reference.handle = ACPI_CAST_PTR(acpi_handle, node); + + param_objects.count = 1; + param_objects.pointer = params; + + return_obj.length = ACPI_ALLOCATE_BUFFER; + + acpi_gbl_method_executing = TRUE; + status = acpi_evaluate_object(read_handle, NULL, + ¶m_objects, &return_obj); + acpi_gbl_method_executing = FALSE; + + if (ACPI_FAILURE(status)) { + acpi_os_printf("Could not read from object, %s", + acpi_format_exception(status)); + return (status); + } + + ret_value = (union acpi_object *)return_obj.pointer; + + switch (ret_value->type) { + case ACPI_TYPE_INTEGER: + case ACPI_TYPE_BUFFER: + case ACPI_TYPE_STRING: + /* + * Did we receive the type we wanted? Most important for the + * Integer/Buffer case (when a field is larger than an Integer, + * it should return a Buffer). + */ + if (ret_value->type != expected_type) { + acpi_os_printf + (" Type mismatch: Expected %s, Received %s", + acpi_ut_get_type_name(expected_type), + acpi_ut_get_type_name(ret_value->type)); + + return (AE_TYPE); + } + + *value = ret_value; + break; + + default: + + acpi_os_printf(" Unsupported return object type, %s", + acpi_ut_get_type_name(ret_value->type)); + + acpi_os_free(return_obj.pointer); + return (AE_TYPE); + } + + return (status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_write_to_object + * + * PARAMETERS: node - Parent NS node for the object + * value - Value to be written + * + * RETURN: Status + * + * DESCRIPTION: Performs a write to the specified object by invoking the + * special debugger control method that writes the object. Thus, + * the AML interpreter is doing all of the work, increasing the + * validity of the test. + * + ******************************************************************************/ + +static acpi_status +acpi_db_write_to_object(struct acpi_namespace_node *node, + union acpi_object *value) +{ + struct acpi_object_list param_objects; + union acpi_object params[2]; + acpi_status status; + + params[0].type = ACPI_TYPE_LOCAL_REFERENCE; + params[0].reference.actual_type = node->type; + params[0].reference.handle = ACPI_CAST_PTR(acpi_handle, node); + + /* Copy the incoming user parameter */ + + memcpy(¶ms[1], value, sizeof(union acpi_object)); + + param_objects.count = 2; + param_objects.pointer = params; + + acpi_gbl_method_executing = TRUE; + status = acpi_evaluate_object(write_handle, NULL, ¶m_objects, NULL); + acpi_gbl_method_executing = FALSE; + + if (ACPI_FAILURE(status)) { + acpi_os_printf("Could not write to object, %s", + acpi_format_exception(status)); + } + + return (status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_evaluate_all_predefined_names + * + * PARAMETERS: count_arg - Max number of methods to execute + * + * RETURN: None + * + * DESCRIPTION: Namespace batch execution. Execute predefined names in the + * namespace, up to the max count, if specified. + * + ******************************************************************************/ + +static void acpi_db_evaluate_all_predefined_names(char *count_arg) +{ + struct acpi_db_execute_walk info; + + info.count = 0; + info.max_count = ACPI_UINT32_MAX; + + if (count_arg) { + info.max_count = strtoul(count_arg, NULL, 0); + } + + /* Search all nodes in namespace */ + + (void)acpi_walk_namespace(ACPI_TYPE_ANY, ACPI_ROOT_OBJECT, + ACPI_UINT32_MAX, + acpi_db_evaluate_one_predefined_name, NULL, + (void *)&info, NULL); + + acpi_os_printf("Evaluated %u predefined names in the namespace\n", + info.count); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_evaluate_one_predefined_name + * + * PARAMETERS: Callback from walk_namespace + * + * RETURN: Status + * + * DESCRIPTION: Batch execution module. Currently only executes predefined + * ACPI names. + * + ******************************************************************************/ + +static acpi_status +acpi_db_evaluate_one_predefined_name(acpi_handle obj_handle, + u32 nesting_level, + void *context, void **return_value) +{ + struct acpi_namespace_node *node = + (struct acpi_namespace_node *)obj_handle; + struct acpi_db_execute_walk *info = + (struct acpi_db_execute_walk *)context; + char *pathname; + const union acpi_predefined_info *predefined; + struct acpi_device_info *obj_info; + struct acpi_object_list param_objects; + union acpi_object params[ACPI_METHOD_NUM_ARGS]; + union acpi_object *this_param; + struct acpi_buffer return_obj; + acpi_status status; + u16 arg_type_list; + u8 arg_count; + u8 arg_type; + u32 i; + + /* The name must be a predefined ACPI name */ + + predefined = acpi_ut_match_predefined_method(node->name.ascii); + if (!predefined) { + return (AE_OK); + } + + if (node->type == ACPI_TYPE_LOCAL_SCOPE) { + return (AE_OK); + } + + pathname = acpi_ns_get_external_pathname(node); + if (!pathname) { + return (AE_OK); + } + + /* Get the object info for number of method parameters */ + + status = acpi_get_object_info(obj_handle, &obj_info); + if (ACPI_FAILURE(status)) { + ACPI_FREE(pathname); + return (status); + } + + param_objects.count = 0; + param_objects.pointer = NULL; + + if (obj_info->type == ACPI_TYPE_METHOD) { + + /* Setup default parameters (with proper types) */ + + arg_type_list = predefined->info.argument_list; + arg_count = METHOD_GET_ARG_COUNT(arg_type_list); + + /* + * Setup the ACPI-required number of arguments, regardless of what + * the actual method defines. If there is a difference, then the + * method is wrong and a warning will be issued during execution. + */ + this_param = params; + for (i = 0; i < arg_count; i++) { + arg_type = METHOD_GET_NEXT_TYPE(arg_type_list); + this_param->type = arg_type; + + switch (arg_type) { + case ACPI_TYPE_INTEGER: + + this_param->integer.value = 1; + break; + + case ACPI_TYPE_STRING: + + this_param->string.pointer = + "This is the default argument string"; + this_param->string.length = + strlen(this_param->string.pointer); + break; + + case ACPI_TYPE_BUFFER: + + this_param->buffer.pointer = (u8 *)params; /* just a garbage buffer */ + this_param->buffer.length = 48; + break; + + case ACPI_TYPE_PACKAGE: + + this_param->package.elements = NULL; + this_param->package.count = 0; + break; + + default: + + acpi_os_printf + ("%s: Unsupported argument type: %u\n", + pathname, arg_type); + break; + } + + this_param++; + } + + param_objects.count = arg_count; + param_objects.pointer = params; + } + + ACPI_FREE(obj_info); + return_obj.pointer = NULL; + return_obj.length = ACPI_ALLOCATE_BUFFER; + + /* Do the actual method execution */ + + acpi_gbl_method_executing = TRUE; + + status = acpi_evaluate_object(node, NULL, ¶m_objects, &return_obj); + + acpi_os_printf("%-32s returned %s\n", + pathname, acpi_format_exception(status)); + acpi_gbl_method_executing = FALSE; + ACPI_FREE(pathname); + + /* Ignore status from method execution */ + + status = AE_OK; + + /* Update count, check if we have executed enough methods */ + + info->count++; + if (info->count >= info->max_count) { + status = AE_CTRL_TERMINATE; + } + + return (status); +} diff --git a/drivers/acpi/acpica/dbutils.c b/drivers/acpi/acpica/dbutils.c new file mode 100644 index 0000000..86790e0 --- /dev/null +++ b/drivers/acpi/acpica/dbutils.c @@ -0,0 +1,457 @@ +/******************************************************************************* + * + * Module Name: dbutils - AML debugger utilities + * + ******************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acnamesp.h" +#include "acdebug.h" + +#define _COMPONENT ACPI_CA_DEBUGGER +ACPI_MODULE_NAME("dbutils") + +/* Local prototypes */ +#ifdef ACPI_OBSOLETE_FUNCTIONS +acpi_status acpi_db_second_pass_parse(union acpi_parse_object *root); + +void acpi_db_dump_buffer(u32 address); +#endif + +static char *gbl_hex_to_ascii = "0123456789ABCDEF"; + +/******************************************************************************* + * + * FUNCTION: acpi_db_match_argument + * + * PARAMETERS: user_argument - User command line + * arguments - Array of commands to match against + * + * RETURN: Index into command array or ACPI_TYPE_NOT_FOUND if not found + * + * DESCRIPTION: Search command array for a command match + * + ******************************************************************************/ + +acpi_object_type +acpi_db_match_argument(char *user_argument, + struct acpi_db_argument_info *arguments) +{ + u32 i; + + if (!user_argument || user_argument[0] == 0) { + return (ACPI_TYPE_NOT_FOUND); + } + + for (i = 0; arguments[i].name; i++) { + if (strstr(arguments[i].name, user_argument) == + arguments[i].name) { + return (i); + } + } + + /* Argument not recognized */ + + return (ACPI_TYPE_NOT_FOUND); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_set_output_destination + * + * PARAMETERS: output_flags - Current flags word + * + * RETURN: None + * + * DESCRIPTION: Set the current destination for debugger output. Also sets + * the debug output level accordingly. + * + ******************************************************************************/ + +void acpi_db_set_output_destination(u32 output_flags) +{ + + acpi_gbl_db_output_flags = (u8)output_flags; + + if ((output_flags & ACPI_DB_REDIRECTABLE_OUTPUT) && + acpi_gbl_db_output_to_file) { + acpi_dbg_level = acpi_gbl_db_debug_level; + } else { + acpi_dbg_level = acpi_gbl_db_console_debug_level; + } +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_dump_external_object + * + * PARAMETERS: obj_desc - External ACPI object to dump + * level - Nesting level. + * + * RETURN: None + * + * DESCRIPTION: Dump the contents of an ACPI external object + * + ******************************************************************************/ + +void acpi_db_dump_external_object(union acpi_object *obj_desc, u32 level) +{ + u32 i; + + if (!obj_desc) { + acpi_os_printf("[Null Object]\n"); + return; + } + + for (i = 0; i < level; i++) { + acpi_os_printf(" "); + } + + switch (obj_desc->type) { + case ACPI_TYPE_ANY: + + acpi_os_printf("[Null Object] (Type=0)\n"); + break; + + case ACPI_TYPE_INTEGER: + + acpi_os_printf("[Integer] = %8.8X%8.8X\n", + ACPI_FORMAT_UINT64(obj_desc->integer.value)); + break; + + case ACPI_TYPE_STRING: + + acpi_os_printf("[String] Length %.2X = ", + obj_desc->string.length); + acpi_ut_print_string(obj_desc->string.pointer, ACPI_UINT8_MAX); + acpi_os_printf("\n"); + break; + + case ACPI_TYPE_BUFFER: + + acpi_os_printf("[Buffer] Length %.2X = ", + obj_desc->buffer.length); + if (obj_desc->buffer.length) { + if (obj_desc->buffer.length > 16) { + acpi_os_printf("\n"); + } + acpi_ut_debug_dump_buffer(ACPI_CAST_PTR + (u8, + obj_desc->buffer.pointer), + obj_desc->buffer.length, + DB_BYTE_DISPLAY, _COMPONENT); + } else { + acpi_os_printf("\n"); + } + break; + + case ACPI_TYPE_PACKAGE: + + acpi_os_printf("[Package] Contains %u Elements:\n", + obj_desc->package.count); + + for (i = 0; i < obj_desc->package.count; i++) { + acpi_db_dump_external_object(&obj_desc->package. + elements[i], level + 1); + } + break; + + case ACPI_TYPE_LOCAL_REFERENCE: + + acpi_os_printf("[Object Reference] = "); + acpi_db_display_internal_object(obj_desc->reference.handle, + NULL); + break; + + case ACPI_TYPE_PROCESSOR: + + acpi_os_printf("[Processor]\n"); + break; + + case ACPI_TYPE_POWER: + + acpi_os_printf("[Power Resource]\n"); + break; + + default: + + acpi_os_printf("[Unknown Type] %X\n", obj_desc->type); + break; + } +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_prep_namestring + * + * PARAMETERS: name - String to prepare + * + * RETURN: None + * + * DESCRIPTION: Translate all forward slashes and dots to backslashes. + * + ******************************************************************************/ + +void acpi_db_prep_namestring(char *name) +{ + + if (!name) { + return; + } + + acpi_ut_strupr(name); + + /* Convert a leading forward slash to a backslash */ + + if (*name == '/') { + *name = '\\'; + } + + /* Ignore a leading backslash, this is the root prefix */ + + if (ACPI_IS_ROOT_PREFIX(*name)) { + name++; + } + + /* Convert all slash path separators to dots */ + + while (*name) { + if ((*name == '/') || (*name == '\\')) { + *name = '.'; + } + + name++; + } +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_local_ns_lookup + * + * PARAMETERS: name - Name to lookup + * + * RETURN: Pointer to a namespace node, null on failure + * + * DESCRIPTION: Lookup a name in the ACPI namespace + * + * Note: Currently begins search from the root. Could be enhanced to use + * the current prefix (scope) node as the search beginning point. + * + ******************************************************************************/ + +struct acpi_namespace_node *acpi_db_local_ns_lookup(char *name) +{ + char *internal_path; + acpi_status status; + struct acpi_namespace_node *node = NULL; + + acpi_db_prep_namestring(name); + + /* Build an internal namestring */ + + status = acpi_ns_internalize_name(name, &internal_path); + if (ACPI_FAILURE(status)) { + acpi_os_printf("Invalid namestring: %s\n", name); + return (NULL); + } + + /* + * Lookup the name. + * (Uses root node as the search starting point) + */ + status = acpi_ns_lookup(NULL, internal_path, ACPI_TYPE_ANY, + ACPI_IMODE_EXECUTE, + ACPI_NS_NO_UPSEARCH | ACPI_NS_DONT_OPEN_SCOPE, + NULL, &node); + if (ACPI_FAILURE(status)) { + acpi_os_printf("Could not locate name: %s, %s\n", + name, acpi_format_exception(status)); + } + + ACPI_FREE(internal_path); + return (node); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_uint32_to_hex_string + * + * PARAMETERS: value - The value to be converted to string + * buffer - Buffer for result (not less than 11 bytes) + * + * RETURN: None + * + * DESCRIPTION: Convert the unsigned 32-bit value to the hexadecimal image + * + * NOTE: It is the caller's responsibility to ensure that the length of buffer + * is sufficient. + * + ******************************************************************************/ + +void acpi_db_uint32_to_hex_string(u32 value, char *buffer) +{ + int i; + + if (value == 0) { + strcpy(buffer, "0"); + return; + } + + buffer[8] = '\0'; + + for (i = 7; i >= 0; i--) { + buffer[i] = gbl_hex_to_ascii[value & 0x0F]; + value = value >> 4; + } +} + +#ifdef ACPI_OBSOLETE_FUNCTIONS +/******************************************************************************* + * + * FUNCTION: acpi_db_second_pass_parse + * + * PARAMETERS: root - Root of the parse tree + * + * RETURN: Status + * + * DESCRIPTION: Second pass parse of the ACPI tables. We need to wait until + * second pass to parse the control methods + * + ******************************************************************************/ + +acpi_status acpi_db_second_pass_parse(union acpi_parse_object *root) +{ + union acpi_parse_object *op = root; + union acpi_parse_object *method; + union acpi_parse_object *search_op; + union acpi_parse_object *start_op; + acpi_status status = AE_OK; + u32 base_aml_offset; + struct acpi_walk_state *walk_state; + + ACPI_FUNCTION_ENTRY(); + + acpi_os_printf("Pass two parse ....\n"); + + while (op) { + if (op->common.aml_opcode == AML_METHOD_OP) { + method = op; + + /* Create a new walk state for the parse */ + + walk_state = + acpi_ds_create_walk_state(0, NULL, NULL, NULL); + if (!walk_state) { + return (AE_NO_MEMORY); + } + + /* Init the Walk State */ + + walk_state->parser_state.aml = + walk_state->parser_state.aml_start = + method->named.data; + walk_state->parser_state.aml_end = + walk_state->parser_state.pkg_end = + method->named.data + method->named.length; + walk_state->parser_state.start_scope = op; + + walk_state->descending_callback = + acpi_ds_load1_begin_op; + walk_state->ascending_callback = acpi_ds_load1_end_op; + + /* Perform the AML parse */ + + status = acpi_ps_parse_aml(walk_state); + + base_aml_offset = + (method->common.value.arg)->common.aml_offset + 1; + start_op = (method->common.value.arg)->common.next; + search_op = start_op; + + while (search_op) { + search_op->common.aml_offset += base_aml_offset; + search_op = + acpi_ps_get_depth_next(start_op, search_op); + } + } + + if (op->common.aml_opcode == AML_REGION_OP) { + + /* TBD: [Investigate] this isn't quite the right thing to do! */ + /* + * + * Method = (ACPI_DEFERRED_OP *) Op; + * Status = acpi_ps_parse_aml (Op, Method->Body, Method->body_length); + */ + } + + if (ACPI_FAILURE(status)) { + break; + } + + op = acpi_ps_get_depth_next(root, op); + } + + return (status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_dump_buffer + * + * PARAMETERS: address - Pointer to the buffer + * + * RETURN: None + * + * DESCRIPTION: Print a portion of a buffer + * + ******************************************************************************/ + +void acpi_db_dump_buffer(u32 address) +{ + + acpi_os_printf("\nLocation %X:\n", address); + + acpi_dbg_level |= ACPI_LV_TABLES; + acpi_ut_debug_dump_buffer(ACPI_TO_POINTER(address), 64, DB_BYTE_DISPLAY, + ACPI_UINT32_MAX); +} +#endif diff --git a/drivers/acpi/acpica/dbxface.c b/drivers/acpi/acpica/dbxface.c new file mode 100644 index 0000000..26023bd --- /dev/null +++ b/drivers/acpi/acpica/dbxface.c @@ -0,0 +1,467 @@ +/******************************************************************************* + * + * Module Name: dbxface - AML Debugger external interfaces + * + ******************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "amlcode.h" +#include "acdebug.h" + +#define _COMPONENT ACPI_CA_DEBUGGER +ACPI_MODULE_NAME("dbxface") + +/* Local prototypes */ +static acpi_status +acpi_db_start_command(struct acpi_walk_state *walk_state, + union acpi_parse_object *op); + +#ifdef ACPI_OBSOLETE_FUNCTIONS +void acpi_db_method_end(struct acpi_walk_state *walk_state); +#endif + +/******************************************************************************* + * + * FUNCTION: acpi_db_start_command + * + * PARAMETERS: walk_state - Current walk + * op - Current executing Op, from AML interpreter + * + * RETURN: Status + * + * DESCRIPTION: Enter debugger command loop + * + ******************************************************************************/ + +static acpi_status +acpi_db_start_command(struct acpi_walk_state *walk_state, + union acpi_parse_object *op) +{ + acpi_status status; + + /* TBD: [Investigate] are there namespace locking issues here? */ + + /* acpi_ut_release_mutex (ACPI_MTX_NAMESPACE); */ + + /* Go into the command loop and await next user command */ + + acpi_gbl_method_executing = TRUE; + status = AE_CTRL_TRUE; + while (status == AE_CTRL_TRUE) { + if (acpi_gbl_debugger_configuration == DEBUGGER_MULTI_THREADED) { + + /* Handshake with the front-end that gets user command lines */ + + acpi_os_release_mutex(acpi_gbl_db_command_complete); + + status = + acpi_os_acquire_mutex(acpi_gbl_db_command_ready, + ACPI_WAIT_FOREVER); + if (ACPI_FAILURE(status)) { + return (status); + } + } else { + /* Single threaded, we must get a command line ourselves */ + + /* Force output to console until a command is entered */ + + acpi_db_set_output_destination(ACPI_DB_CONSOLE_OUTPUT); + + /* Different prompt if method is executing */ + + if (!acpi_gbl_method_executing) { + acpi_os_printf("%1c ", + ACPI_DEBUGGER_COMMAND_PROMPT); + } else { + acpi_os_printf("%1c ", + ACPI_DEBUGGER_EXECUTE_PROMPT); + } + + /* Get the user input line */ + + status = acpi_os_get_line(acpi_gbl_db_line_buf, + ACPI_DB_LINE_BUFFER_SIZE, + NULL); + if (ACPI_FAILURE(status)) { + ACPI_EXCEPTION((AE_INFO, status, + "While parsing command line")); + return (status); + } + } + + status = + acpi_db_command_dispatch(acpi_gbl_db_line_buf, walk_state, + op); + } + + /* acpi_ut_acquire_mutex (ACPI_MTX_NAMESPACE); */ + + return (status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_db_single_step + * + * PARAMETERS: walk_state - Current walk + * op - Current executing op (from aml interpreter) + * opcode_class - Class of the current AML Opcode + * + * RETURN: Status + * + * DESCRIPTION: Called just before execution of an AML opcode. + * + ******************************************************************************/ + +acpi_status +acpi_db_single_step(struct acpi_walk_state * walk_state, + union acpi_parse_object * op, u32 opcode_class) +{ + union acpi_parse_object *next; + acpi_status status = AE_OK; + u32 original_debug_level; + union acpi_parse_object *display_op; + union acpi_parse_object *parent_op; + u32 aml_offset; + + ACPI_FUNCTION_ENTRY(); + + /* Check the abort flag */ + + if (acpi_gbl_abort_method) { + acpi_gbl_abort_method = FALSE; + return (AE_ABORT_METHOD); + } + + aml_offset = (u32)ACPI_PTR_DIFF(op->common.aml, + walk_state->parser_state.aml_start); + + /* Check for single-step breakpoint */ + + if (walk_state->method_breakpoint && + (walk_state->method_breakpoint <= aml_offset)) { + + /* Check if the breakpoint has been reached or passed */ + /* Hit the breakpoint, resume single step, reset breakpoint */ + + acpi_os_printf("***Break*** at AML offset %X\n", aml_offset); + acpi_gbl_cm_single_step = TRUE; + acpi_gbl_step_to_next_call = FALSE; + walk_state->method_breakpoint = 0; + } + + /* Check for user breakpoint (Must be on exact Aml offset) */ + + else if (walk_state->user_breakpoint && + (walk_state->user_breakpoint == aml_offset)) { + acpi_os_printf("***UserBreakpoint*** at AML offset %X\n", + aml_offset); + acpi_gbl_cm_single_step = TRUE; + acpi_gbl_step_to_next_call = FALSE; + walk_state->method_breakpoint = 0; + } + + /* + * Check if this is an opcode that we are interested in -- + * namely, opcodes that have arguments + */ + if (op->common.aml_opcode == AML_INT_NAMEDFIELD_OP) { + return (AE_OK); + } + + switch (opcode_class) { + case AML_CLASS_UNKNOWN: + case AML_CLASS_ARGUMENT: /* constants, literals, etc. do nothing */ + + return (AE_OK); + + default: + + /* All other opcodes -- continue */ + break; + } + + /* + * Under certain debug conditions, display this opcode and its operands + */ + if ((acpi_gbl_db_output_to_file) || + (acpi_gbl_cm_single_step) || (acpi_dbg_level & ACPI_LV_PARSE)) { + if ((acpi_gbl_db_output_to_file) || + (acpi_dbg_level & ACPI_LV_PARSE)) { + acpi_os_printf + ("\n[AmlDebug] Next AML Opcode to execute:\n"); + } + + /* + * Display this op (and only this op - zero out the NEXT field + * temporarily, and disable parser trace output for the duration of + * the display because we don't want the extraneous debug output) + */ + original_debug_level = acpi_dbg_level; + acpi_dbg_level &= ~(ACPI_LV_PARSE | ACPI_LV_FUNCTIONS); + next = op->common.next; + op->common.next = NULL; + + display_op = op; + parent_op = op->common.parent; + if (parent_op) { + if ((walk_state->control_state) && + (walk_state->control_state->common.state == + ACPI_CONTROL_PREDICATE_EXECUTING)) { + /* + * We are executing the predicate of an IF or WHILE statement + * Search upwards for the containing IF or WHILE so that the + * entire predicate can be displayed. + */ + while (parent_op) { + if ((parent_op->common.aml_opcode == + AML_IF_OP) + || (parent_op->common.aml_opcode == + AML_WHILE_OP)) { + display_op = parent_op; + break; + } + parent_op = parent_op->common.parent; + } + } else { + while (parent_op) { + if ((parent_op->common.aml_opcode == + AML_IF_OP) + || (parent_op->common.aml_opcode == + AML_ELSE_OP) + || (parent_op->common.aml_opcode == + AML_SCOPE_OP) + || (parent_op->common.aml_opcode == + AML_METHOD_OP) + || (parent_op->common.aml_opcode == + AML_WHILE_OP)) { + break; + } + display_op = parent_op; + parent_op = parent_op->common.parent; + } + } + } + + /* Now we can display it */ + +#ifdef ACPI_DISASSEMBLER + acpi_dm_disassemble(walk_state, display_op, ACPI_UINT32_MAX); +#endif + + if ((op->common.aml_opcode == AML_IF_OP) || + (op->common.aml_opcode == AML_WHILE_OP)) { + if (walk_state->control_state->common.value) { + acpi_os_printf + ("Predicate = [True], IF block was executed\n"); + } else { + acpi_os_printf + ("Predicate = [False], Skipping IF block\n"); + } + } else if (op->common.aml_opcode == AML_ELSE_OP) { + acpi_os_printf + ("Predicate = [False], ELSE block was executed\n"); + } + + /* Restore everything */ + + op->common.next = next; + acpi_os_printf("\n"); + if ((acpi_gbl_db_output_to_file) || + (acpi_dbg_level & ACPI_LV_PARSE)) { + acpi_os_printf("\n"); + } + acpi_dbg_level = original_debug_level; + } + + /* If we are not single stepping, just continue executing the method */ + + if (!acpi_gbl_cm_single_step) { + return (AE_OK); + } + + /* + * If we are executing a step-to-call command, + * Check if this is a method call. + */ + if (acpi_gbl_step_to_next_call) { + if (op->common.aml_opcode != AML_INT_METHODCALL_OP) { + + /* Not a method call, just keep executing */ + + return (AE_OK); + } + + /* Found a method call, stop executing */ + + acpi_gbl_step_to_next_call = FALSE; + } + + /* + * If the next opcode is a method call, we will "step over" it + * by default. + */ + if (op->common.aml_opcode == AML_INT_METHODCALL_OP) { + + /* Force no more single stepping while executing called method */ + + acpi_gbl_cm_single_step = FALSE; + + /* + * Set the breakpoint on/before the call, it will stop execution + * as soon as we return + */ + walk_state->method_breakpoint = 1; /* Must be non-zero! */ + } + + status = acpi_db_start_command(walk_state, op); + + /* User commands complete, continue execution of the interrupted method */ + + return (status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_initialize_debugger + * + * PARAMETERS: None + * + * RETURN: Status + * + * DESCRIPTION: Init and start debugger + * + ******************************************************************************/ + +acpi_status acpi_initialize_debugger(void) +{ + acpi_status status; + + ACPI_FUNCTION_TRACE(acpi_initialize_debugger); + + /* Init globals */ + + acpi_gbl_db_buffer = NULL; + acpi_gbl_db_filename = NULL; + acpi_gbl_db_output_to_file = FALSE; + + acpi_gbl_db_debug_level = ACPI_LV_VERBOSITY2; + acpi_gbl_db_console_debug_level = ACPI_NORMAL_DEFAULT | ACPI_LV_TABLES; + acpi_gbl_db_output_flags = ACPI_DB_CONSOLE_OUTPUT; + + acpi_gbl_db_opt_no_ini_methods = FALSE; + + acpi_gbl_db_buffer = acpi_os_allocate(ACPI_DEBUG_BUFFER_SIZE); + if (!acpi_gbl_db_buffer) { + return_ACPI_STATUS(AE_NO_MEMORY); + } + memset(acpi_gbl_db_buffer, 0, ACPI_DEBUG_BUFFER_SIZE); + + /* Initial scope is the root */ + + acpi_gbl_db_scope_buf[0] = AML_ROOT_PREFIX; + acpi_gbl_db_scope_buf[1] = 0; + acpi_gbl_db_scope_node = acpi_gbl_root_node; + + /* + * If configured for multi-thread support, the debug executor runs in + * a separate thread so that the front end can be in another address + * space, environment, or even another machine. + */ + if (acpi_gbl_debugger_configuration & DEBUGGER_MULTI_THREADED) { + + /* These were created with one unit, grab it */ + + status = acpi_os_acquire_mutex(acpi_gbl_db_command_complete, + ACPI_WAIT_FOREVER); + if (ACPI_FAILURE(status)) { + acpi_os_printf("Could not get debugger mutex\n"); + return_ACPI_STATUS(status); + } + + status = acpi_os_acquire_mutex(acpi_gbl_db_command_ready, + ACPI_WAIT_FOREVER); + if (ACPI_FAILURE(status)) { + acpi_os_printf("Could not get debugger mutex\n"); + return_ACPI_STATUS(status); + } + + /* Create the debug execution thread to execute commands */ + + status = acpi_os_execute(OSL_DEBUGGER_THREAD, + acpi_db_execute_thread, NULL); + if (ACPI_FAILURE(status)) { + ACPI_EXCEPTION((AE_INFO, status, + "Could not start debugger thread")); + return_ACPI_STATUS(status); + } + } + + return_ACPI_STATUS(AE_OK); +} + +ACPI_EXPORT_SYMBOL(acpi_initialize_debugger) + +/******************************************************************************* + * + * FUNCTION: acpi_terminate_debugger + * + * PARAMETERS: None + * + * RETURN: None + * + * DESCRIPTION: Stop debugger + * + ******************************************************************************/ +void acpi_terminate_debugger(void) +{ + + if (acpi_gbl_db_buffer) { + acpi_os_free(acpi_gbl_db_buffer); + acpi_gbl_db_buffer = NULL; + } + + /* Ensure that debug output is now disabled */ + + acpi_gbl_db_output_flags = ACPI_DB_DISABLE_OUTPUT; +} + +ACPI_EXPORT_SYMBOL(acpi_terminate_debugger) -- cgit v0.10.2 From af08f9cc5073eee875016d28730c99ec86da4198 Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Mon, 19 Oct 2015 10:25:32 +0800 Subject: ACPICA: Debugger: Fix "quit/exit" command by cleaning up user commands termination logic ACPICA commit 0dd68e16274cd38224aa4781eddc57dc2cbaa108 The quit/exit commands shouldn't invoke acpi_terminate_debugger() and acpi_terminate() right in the user command loop, because when the debugger exits, the kernel ACPI subsystem shouldn't be terminated (acpi_terminate()) and the debugger should only be terminated by its users (acpi_terminate_debugger()) rather than being terminated itself. Leaving such invocations causes kernel panic when the debugger is shipped in the Linux kernel. This patch fixes this issue. Lv Zheng. Link: https://github.com/acpica/acpica/commit/0dd68e16 Signed-off-by: Lv Zheng Signed-off-by: Bob Moore Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/acpica/acglobal.h b/drivers/acpi/acpica/acglobal.h index 71a3c40..e576865 100644 --- a/drivers/acpi/acpica/acglobal.h +++ b/drivers/acpi/acpica/acglobal.h @@ -325,7 +325,6 @@ ACPI_GLOBAL(struct acpi_external_file *, acpi_gbl_external_file_list); #ifdef ACPI_DEBUGGER -ACPI_INIT_GLOBAL(u8, acpi_gbl_db_terminate_threads, FALSE); ACPI_INIT_GLOBAL(u8, acpi_gbl_abort_method, FALSE); ACPI_INIT_GLOBAL(u8, acpi_gbl_method_executing, FALSE); @@ -337,6 +336,8 @@ ACPI_GLOBAL(char *, acpi_gbl_db_filename); ACPI_GLOBAL(u32, acpi_gbl_db_debug_level); ACPI_GLOBAL(u32, acpi_gbl_db_console_debug_level); ACPI_GLOBAL(struct acpi_namespace_node *, acpi_gbl_db_scope_node); +ACPI_GLOBAL(u8, acpi_gbl_db_terminate_loop); +ACPI_GLOBAL(u8, acpi_gbl_db_threads_terminated); ACPI_GLOBAL(char *, acpi_gbl_db_args[ACPI_DEBUGGER_MAX_ARGS]); ACPI_GLOBAL(acpi_object_type, acpi_gbl_db_arg_types[ACPI_DEBUGGER_MAX_ARGS]); diff --git a/drivers/acpi/acpica/dbinput.c b/drivers/acpi/acpica/dbinput.c index 7f1b6ec..f8cddd6 100644 --- a/drivers/acpi/acpica/dbinput.c +++ b/drivers/acpi/acpica/dbinput.c @@ -694,7 +694,7 @@ acpi_db_command_dispatch(char *input_buffer, /* If acpi_terminate has been called, terminate this thread */ - if (acpi_gbl_db_terminate_threads) { + if (acpi_gbl_db_terminate_loop) { return (AE_CTRL_TERMINATE); } @@ -1116,7 +1116,7 @@ acpi_db_command_dispatch(char *input_buffer, #ifdef ACPI_APPLICATION acpi_db_close_debug_file(); #endif - acpi_gbl_db_terminate_threads = TRUE; + acpi_gbl_db_terminate_loop = TRUE; return (AE_CTRL_TERMINATE); case CMD_NOT_FOUND: @@ -1166,6 +1166,7 @@ void ACPI_SYSTEM_XFACE acpi_db_execute_thread(void *context) acpi_os_release_mutex(acpi_gbl_db_command_complete); } + acpi_gbl_db_threads_terminated = TRUE; } /******************************************************************************* @@ -1212,7 +1213,7 @@ acpi_status acpi_db_user_commands(char prompt, union acpi_parse_object *op) /* TBD: [Restructure] Need a separate command line buffer for step mode */ - while (!acpi_gbl_db_terminate_threads) { + while (!acpi_gbl_db_terminate_loop) { /* Force output to console until a command is entered */ @@ -1261,14 +1262,5 @@ acpi_status acpi_db_user_commands(char prompt, union acpi_parse_object *op) } } - /* Shut down the debugger */ - - acpi_terminate_debugger(); - - /* - * Only this thread (the original thread) should actually terminate the - * subsystem, because all the semaphores are deleted during termination - */ - status = acpi_terminate(); return (status); } diff --git a/drivers/acpi/acpica/dbxface.c b/drivers/acpi/acpica/dbxface.c index 26023bd..bef5f4e 100644 --- a/drivers/acpi/acpica/dbxface.c +++ b/drivers/acpi/acpica/dbxface.c @@ -401,6 +401,10 @@ acpi_status acpi_initialize_debugger(void) acpi_gbl_db_scope_buf[1] = 0; acpi_gbl_db_scope_node = acpi_gbl_root_node; + /* Initialize user commands loop */ + + acpi_gbl_db_terminate_loop = FALSE; + /* * If configured for multi-thread support, the debug executor runs in * a separate thread so that the front end can be in another address @@ -426,11 +430,13 @@ acpi_status acpi_initialize_debugger(void) /* Create the debug execution thread to execute commands */ + acpi_gbl_db_threads_terminated = FALSE; status = acpi_os_execute(OSL_DEBUGGER_THREAD, acpi_db_execute_thread, NULL); if (ACPI_FAILURE(status)) { ACPI_EXCEPTION((AE_INFO, status, "Could not start debugger thread")); + acpi_gbl_db_threads_terminated = TRUE; return_ACPI_STATUS(status); } } @@ -454,6 +460,20 @@ ACPI_EXPORT_SYMBOL(acpi_initialize_debugger) void acpi_terminate_debugger(void) { + /* Terminate the AML Debugger */ + + acpi_gbl_db_terminate_loop = TRUE; + + if (acpi_gbl_debugger_configuration & DEBUGGER_MULTI_THREADED) { + acpi_os_release_mutex(acpi_gbl_db_command_ready); + + /* Wait the AML Debugger threads */ + + while (!acpi_gbl_db_threads_terminated) { + acpi_os_sleep(100); + } + } + if (acpi_gbl_db_buffer) { acpi_os_free(acpi_gbl_db_buffer); acpi_gbl_db_buffer = NULL; diff --git a/drivers/acpi/acpica/utinit.c b/drivers/acpi/acpica/utinit.c index 28ab3a1..d8699df 100644 --- a/drivers/acpi/acpica/utinit.c +++ b/drivers/acpi/acpica/utinit.c @@ -241,8 +241,6 @@ acpi_status acpi_ut_init_globals(void) acpi_gbl_disable_mem_tracking = FALSE; #endif - ACPI_DEBUGGER_EXEC(acpi_gbl_db_terminate_threads = FALSE); - return_ACPI_STATUS(AE_OK); } diff --git a/drivers/acpi/acpica/utxface.c b/drivers/acpi/acpica/utxface.c index 4f33281..f183daf 100644 --- a/drivers/acpi/acpica/utxface.c +++ b/drivers/acpi/acpica/utxface.c @@ -80,10 +80,6 @@ acpi_status __init acpi_terminate(void) acpi_gbl_startup_flags = 0; ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Shutting down ACPI Subsystem\n")); - /* Terminate the AML Debugger if present */ - - ACPI_DEBUGGER_EXEC(acpi_gbl_db_terminate_threads = TRUE); - /* Shutdown and free all resources */ acpi_ut_subsystem_shutdown(); -- cgit v0.10.2 From 086ab742ac97b45ee64507b32b3d1ecbdbc56b39 Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Mon, 19 Oct 2015 10:25:42 +0800 Subject: ACPICA: Debugger: Fix "terminate" command by cleaning up subsystem shutdown logic ACPICA commit 7e823714911480be47e310fb1b3590d289b9fd99 Segmentation fault can be seen for executing the "terminate" command. This is because acpi_ut_subsystem_shutdown() is errnously called multiple times. This patch cleans up acpi_ut_subsystem_shutdown() logics to fix this issue. Lv Zheng. Link: https://github.com/acpica/acpica/commit/7e823714 Signed-off-by: Lv Zheng Signed-off-by: Bob Moore Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/acpica/dbinput.c b/drivers/acpi/acpica/dbinput.c index f8cddd6..0480254 100644 --- a/drivers/acpi/acpica/dbinput.c +++ b/drivers/acpi/acpica/dbinput.c @@ -1074,6 +1074,7 @@ acpi_db_command_dispatch(char *input_buffer, * re-creating the semaphores! */ + acpi_gbl_db_terminate_loop = TRUE; /* acpi_initialize (NULL); */ break; @@ -1151,7 +1152,7 @@ void ACPI_SYSTEM_XFACE acpi_db_execute_thread(void *context) acpi_status status = AE_OK; acpi_status Mstatus; - while (status != AE_CTRL_TERMINATE) { + while (status != AE_CTRL_TERMINATE && !acpi_gbl_db_terminate_loop) { acpi_gbl_method_executing = FALSE; acpi_gbl_step_to_next_call = FALSE; diff --git a/drivers/acpi/acpica/utinit.c b/drivers/acpi/acpica/utinit.c index d8699df..ccd0745 100644 --- a/drivers/acpi/acpica/utinit.c +++ b/drivers/acpi/acpica/utinit.c @@ -282,6 +282,19 @@ void acpi_ut_subsystem_shutdown(void) { ACPI_FUNCTION_TRACE(ut_subsystem_shutdown); + /* Just exit if subsystem is already shutdown */ + + if (acpi_gbl_shutdown) { + ACPI_ERROR((AE_INFO, "ACPI Subsystem is already terminated")); + return_VOID; + } + + /* Subsystem appears active, go ahead and shut it down */ + + acpi_gbl_shutdown = TRUE; + acpi_gbl_startup_flags = 0; + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Shutting down ACPI Subsystem\n")); + #ifndef ACPI_ASL_COMPILER /* Close the acpi_event Handling */ diff --git a/drivers/acpi/acpica/utxface.c b/drivers/acpi/acpica/utxface.c index f183daf..a6b0eb0 100644 --- a/drivers/acpi/acpica/utxface.c +++ b/drivers/acpi/acpica/utxface.c @@ -67,19 +67,6 @@ acpi_status __init acpi_terminate(void) ACPI_FUNCTION_TRACE(acpi_terminate); - /* Just exit if subsystem is already shutdown */ - - if (acpi_gbl_shutdown) { - ACPI_ERROR((AE_INFO, "ACPI Subsystem is already terminated")); - return_ACPI_STATUS(AE_OK); - } - - /* Subsystem appears active, go ahead and shut it down */ - - acpi_gbl_shutdown = TRUE; - acpi_gbl_startup_flags = 0; - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Shutting down ACPI Subsystem\n")); - /* Shutdown and free all resources */ acpi_ut_subsystem_shutdown(); -- cgit v0.10.2 From f988f24ee7931d3487b36cc0c29164296bf2191e Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Mon, 19 Oct 2015 10:25:50 +0800 Subject: ACPICA: Debugger: Add thread ID support so that single step mode can only apply to the debugger thread When the debugger is running in the kernel mode, acpi_db_single_step() may also be invoked by the kernel runtime code path but the single stepping command prompt may be erronously logged as the kernel logs and runtime code path cannot proceed. This patch fixes this issue by adding acpi_gbl_db_thread_id for the debugger thread and preventing acpi_db_single_step() to be invoked from other threads. It is not suitable to add acpi_thread_id parameter for acpi_os_execute() as the function may be implemented as work queue on some hosts. So it is better to let the hosts invoke acpi_set_debugger_thread_id(). Currently acpiexec is not configured as DEBUGGER_MULTI_THREADED, but we can do this. When we do this, it is better to invoke acpi_set_debugger_thread_id() in acpi_os_execute() when the execution type is OSL_DEBUGGER_MAIN_THREAD. The support should look like: create_thread(&tid); if (type == OSL_DEBUGGER_MAIN_THREAD) acpi_set_debugger_thread_id(tid); resume_thread(tid); Similarly, semop() may be used for pthread implementation. But this patch simply skips debugger thread ID check for application instead of introducing such complications as there is no need to skip acpi_db_single_step() for an application debugger - acpiexec. Note that the debugger thread ID can also be used by acpi_os_printf() to filter out debugger output. Lv Zheng. Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/acpica/acglobal.h b/drivers/acpi/acpica/acglobal.h index e576865..faa9760 100644 --- a/drivers/acpi/acpica/acglobal.h +++ b/drivers/acpi/acpica/acglobal.h @@ -327,6 +327,7 @@ ACPI_GLOBAL(struct acpi_external_file *, acpi_gbl_external_file_list); ACPI_INIT_GLOBAL(u8, acpi_gbl_abort_method, FALSE); ACPI_INIT_GLOBAL(u8, acpi_gbl_method_executing, FALSE); +ACPI_INIT_GLOBAL(acpi_thread_id, acpi_gbl_db_thread_id, ACPI_INVALID_THREAD_ID); ACPI_GLOBAL(u8, acpi_gbl_db_opt_no_ini_methods); ACPI_GLOBAL(u8, acpi_gbl_db_opt_no_region_support); diff --git a/drivers/acpi/acpica/aclocal.h b/drivers/acpi/acpica/aclocal.h index 4e41b43..e1dd784 100644 --- a/drivers/acpi/acpica/aclocal.h +++ b/drivers/acpi/acpica/aclocal.h @@ -109,6 +109,14 @@ struct acpi_rw_lock { #define ACPI_MUTEX_NOT_ACQUIRED (acpi_thread_id) 0 +/* This Thread ID means an invalid thread ID */ + +#ifdef ACPI_OS_INVALID_THREAD_ID +#define ACPI_INVALID_THREAD_ID ACPI_OS_INVALID_THREAD_ID +#else +#define ACPI_INVALID_THREAD_ID ((acpi_thread_id) 0xFFFFFFFF) +#endif + /* Table for the global mutexes */ struct acpi_mutex_info { diff --git a/drivers/acpi/acpica/dbexec.c b/drivers/acpi/acpica/dbexec.c index 8eef298..258e615 100644 --- a/drivers/acpi/acpica/dbexec.c +++ b/drivers/acpi/acpica/dbexec.c @@ -725,7 +725,8 @@ acpi_db_create_execution_threads(char *num_threads_arg, for (i = 0; i < (num_threads); i++) { status = - acpi_os_execute(OSL_DEBUGGER_THREAD, acpi_db_method_thread, + acpi_os_execute(OSL_DEBUGGER_EXEC_THREAD, + acpi_db_method_thread, &acpi_gbl_db_method_info); if (ACPI_FAILURE(status)) { break; diff --git a/drivers/acpi/acpica/dbxface.c b/drivers/acpi/acpica/dbxface.c index bef5f4e..342298a 100644 --- a/drivers/acpi/acpica/dbxface.c +++ b/drivers/acpi/acpica/dbxface.c @@ -164,6 +164,12 @@ acpi_db_single_step(struct acpi_walk_state * walk_state, ACPI_FUNCTION_ENTRY(); +#ifndef ACPI_APPLICATION + if (acpi_gbl_db_thread_id != acpi_os_get_thread_id()) { + return (AE_OK); + } +#endif + /* Check the abort flag */ if (acpi_gbl_abort_method) { @@ -431,7 +437,7 @@ acpi_status acpi_initialize_debugger(void) /* Create the debug execution thread to execute commands */ acpi_gbl_db_threads_terminated = FALSE; - status = acpi_os_execute(OSL_DEBUGGER_THREAD, + status = acpi_os_execute(OSL_DEBUGGER_MAIN_THREAD, acpi_db_execute_thread, NULL); if (ACPI_FAILURE(status)) { ACPI_EXCEPTION((AE_INFO, status, @@ -439,6 +445,8 @@ acpi_status acpi_initialize_debugger(void) acpi_gbl_db_threads_terminated = TRUE; return_ACPI_STATUS(status); } + } else { + acpi_gbl_db_thread_id = acpi_os_get_thread_id(); } return_ACPI_STATUS(AE_OK); @@ -485,3 +493,21 @@ void acpi_terminate_debugger(void) } ACPI_EXPORT_SYMBOL(acpi_terminate_debugger) + +/******************************************************************************* + * + * FUNCTION: acpi_set_debugger_thread_id + * + * PARAMETERS: thread_id - Debugger thread ID + * + * RETURN: None + * + * DESCRIPTION: Set debugger thread ID + * + ******************************************************************************/ +void acpi_set_debugger_thread_id(acpi_thread_id thread_id) +{ + acpi_gbl_db_thread_id = thread_id; +} + +ACPI_EXPORT_SYMBOL(acpi_set_debugger_thread_id) diff --git a/include/acpi/acpiosxf.h b/include/acpi/acpiosxf.h index a54ad1c..fbc2baf 100644 --- a/include/acpi/acpiosxf.h +++ b/include/acpi/acpiosxf.h @@ -55,7 +55,8 @@ typedef enum { OSL_GLOBAL_LOCK_HANDLER, OSL_NOTIFY_HANDLER, OSL_GPE_HANDLER, - OSL_DEBUGGER_THREAD, + OSL_DEBUGGER_MAIN_THREAD, + OSL_DEBUGGER_EXEC_THREAD, OSL_EC_POLL_HANDLER, OSL_EC_BURST_HANDLER } acpi_execute_type; diff --git a/include/acpi/acpixf.h b/include/acpi/acpixf.h index c33eeab..f837adf 100644 --- a/include/acpi/acpixf.h +++ b/include/acpi/acpixf.h @@ -939,4 +939,6 @@ ACPI_EXTERNAL_RETURN_STATUS(acpi_status void **data, void (*callback)(void *))) +void acpi_set_debugger_thread_id(acpi_thread_id thread_id); + #endif /* __ACXFACE_H__ */ -- cgit v0.10.2 From 4d946f7970e51d80f8358e0a619dfb17d89e0920 Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Mon, 19 Oct 2015 10:25:56 +0800 Subject: ACPI: Enable build of AML interpreter debugger This patch enables ACPICA debugger files using a configurable CONFIG_ACPI_DEBUGGER configuration item. Those debugger related code that was originally masked as ACPI_FUTURE_USAGE now gets unmasked. Necessary OSL stubs are also added in this patch: 1. acpi_os_readable(): This should be arch specific in Linux, while this patch doesn't introduce real implementation and a complex mechanism to allow architecture specific acpi_os_readable() to be implemented to validate the address. It may be done by future commits. 2. acpi_os_get_line(): This is used to obtain debugger command input. This patch only introduces a simple KDB concept example in it and the example should be co-working with the code implemented in acpi_os_printf(). Since this KDB example won't be compiled unless ENABLE_DEBUGGER is defined and it seems Linux has already stopped to use ENABLE_DEBUGGER, thus do not expect it can work properly. This patch also cleans up all other ACPI_FUTURE_USAGE surroundings accordingly. 1. Since linkage error can be automatically detected, declaration in the headers needn't be surrounded by ACPI_FUTURE_USAGE. So only the following separate exported fuction bodies are masked by this macro (other exported fucntions may have already been masked at entire module level via drivers/acpi/acpica/Makefile): acpi_install_exception_handler() acpi_subsystem_status() acpi_get_system_info() acpi_get_statistics() acpi_install_initialization_handler() 2. Since strip can automatically zap the no-user functions, functions that are not marked with ACPI_EXPORT_SYMBOL() needn't get surrounded by ACPI_FUTURE_USAGE. So the following function which is not used by Linux kernel now won't get surrounded by this macro: acpi_ps_get_name() Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index 5d1015c..706c2e9 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -57,6 +57,15 @@ config ACPI_SYSTEM_POWER_STATES_SUPPORT config ACPI_CCA_REQUIRED bool +config ACPI_DEBUGGER + bool "In-kernel debugger (EXPERIMENTAL)" + select ACPI_DEBUG + help + Enable in-kernel debugging facilities: statistics, internal + object dump, single step control method execution. + This is still under development, currently enabling this only + results in the compilation of the ACPICA debugger files. + config ACPI_SLEEP bool depends on SUSPEND || HIBERNATION diff --git a/drivers/acpi/acpica/Makefile b/drivers/acpi/acpica/Makefile index ac78d76..885936f 100644 --- a/drivers/acpi/acpica/Makefile +++ b/drivers/acpi/acpica/Makefile @@ -177,8 +177,24 @@ acpi-y += \ utxferror.o \ utxfmutex.o -acpi-$(ACPI_FUTURE_USAGE) += \ +acpi-$(CONFIG_ACPI_DEBUGGER) += \ + dbcmds.o \ + dbconvert.o \ + dbdisply.o \ + dbexec.o \ + dbhistry.o \ + dbinput.o \ + dbmethod.o \ + dbnames.o \ + dbobject.o \ + dbstats.o \ + dbutils.o \ + dbxface.o \ rsdump.o \ + +acpi-$(ACPI_FUTURE_USAGE) += \ + dbfileio.o \ + dbtest.o \ utcache.o \ utfileio.o \ utprint.o \ diff --git a/drivers/acpi/acpica/acinterp.h b/drivers/acpi/acpica/acinterp.h index e820ed8..e9e936e 100644 --- a/drivers/acpi/acpica/acinterp.h +++ b/drivers/acpi/acpica/acinterp.h @@ -397,12 +397,10 @@ void acpi_ex_dump_operands(union acpi_operand_object **operands, const char *opcode_name, u32 num_opcodes); -#ifdef ACPI_FUTURE_USAGE void acpi_ex_dump_object_descriptor(union acpi_operand_object *object, u32 flags); void acpi_ex_dump_namespace_node(struct acpi_namespace_node *node, u32 flags); -#endif /* ACPI_FUTURE_USAGE */ /* * exnames - AML namestring support diff --git a/drivers/acpi/acpica/acnamesp.h b/drivers/acpi/acpica/acnamesp.h index ea0d907..5d261c9 100644 --- a/drivers/acpi/acpica/acnamesp.h +++ b/drivers/acpi/acpica/acnamesp.h @@ -193,9 +193,7 @@ acpi_ns_convert_to_resource(union acpi_operand_object *original_object, /* * nsdump - Namespace dump/print utilities */ -#ifdef ACPI_FUTURE_USAGE void acpi_ns_dump_tables(acpi_handle search_base, u32 max_depth); -#endif /* ACPI_FUTURE_USAGE */ void acpi_ns_dump_entry(acpi_handle handle, u32 debug_level); @@ -208,7 +206,6 @@ acpi_status acpi_ns_dump_one_object(acpi_handle obj_handle, u32 level, void *context, void **return_value); -#ifdef ACPI_FUTURE_USAGE void acpi_ns_dump_objects(acpi_object_type type, u8 display_type, @@ -220,7 +217,6 @@ acpi_ns_dump_object_paths(acpi_object_type type, u8 display_type, u32 max_depth, acpi_owner_id owner_id, acpi_handle start_handle); -#endif /* ACPI_FUTURE_USAGE */ /* * nseval - Namespace evaluation functions diff --git a/drivers/acpi/acpica/acparser.h b/drivers/acpi/acpica/acparser.h index 6021ccf..8fc8c7c 100644 --- a/drivers/acpi/acpica/acparser.h +++ b/drivers/acpi/acpica/acparser.h @@ -194,10 +194,8 @@ union acpi_parse_object *acpi_ps_find(union acpi_parse_object *scope, union acpi_parse_object *acpi_ps_get_arg(union acpi_parse_object *op, u32 argn); -#ifdef ACPI_FUTURE_USAGE union acpi_parse_object *acpi_ps_get_depth_next(union acpi_parse_object *origin, union acpi_parse_object *op); -#endif /* ACPI_FUTURE_USAGE */ /* * pswalk - parse tree walk routines @@ -235,9 +233,7 @@ void acpi_ps_free_op(union acpi_parse_object *op); u8 acpi_ps_is_leading_char(u32 c); -#ifdef ACPI_FUTURE_USAGE u32 acpi_ps_get_name(union acpi_parse_object *op); -#endif /* ACPI_FUTURE_USAGE */ void acpi_ps_set_name(union acpi_parse_object *op, u32 name); diff --git a/drivers/acpi/acpica/acutils.h b/drivers/acpi/acpica/acutils.h index fb2aa50..8b8fef6 100644 --- a/drivers/acpi/acpica/acutils.h +++ b/drivers/acpi/acpica/acutils.h @@ -635,9 +635,7 @@ void acpi_ut_free_and_track(void *address, u32 component, const char *module, u32 line); -#ifdef ACPI_FUTURE_USAGE void acpi_ut_dump_allocation_info(void); -#endif /* ACPI_FUTURE_USAGE */ void acpi_ut_dump_allocations(u32 component, const char *module); diff --git a/drivers/acpi/acpica/evxface.c b/drivers/acpi/acpica/evxface.c index 81f2d9e..07d22bf 100644 --- a/drivers/acpi/acpica/evxface.c +++ b/drivers/acpi/acpica/evxface.c @@ -405,7 +405,7 @@ cleanup: } ACPI_EXPORT_SYMBOL(acpi_install_exception_handler) -#endif /* ACPI_FUTURE_USAGE */ +#endif #if (!ACPI_REDUCED_HARDWARE) /******************************************************************************* diff --git a/drivers/acpi/acpica/nsdump.c b/drivers/acpi/acpica/nsdump.c index 0f1daba..37aa5c4 100644 --- a/drivers/acpi/acpica/nsdump.c +++ b/drivers/acpi/acpica/nsdump.c @@ -60,7 +60,6 @@ acpi_ns_dump_one_device(acpi_handle obj_handle, #if defined(ACPI_DEBUG_OUTPUT) || defined(ACPI_DEBUGGER) -#ifdef ACPI_FUTURE_USAGE static acpi_status acpi_ns_dump_one_object_path(acpi_handle obj_handle, u32 level, void *context, void **return_value); @@ -68,7 +67,6 @@ acpi_ns_dump_one_object_path(acpi_handle obj_handle, static acpi_status acpi_ns_get_max_depth(acpi_handle obj_handle, u32 level, void *context, void **return_value); -#endif /* ACPI_FUTURE_USAGE */ /******************************************************************************* * @@ -625,7 +623,6 @@ cleanup: return (AE_OK); } -#ifdef ACPI_FUTURE_USAGE /******************************************************************************* * * FUNCTION: acpi_ns_dump_objects @@ -680,9 +677,7 @@ acpi_ns_dump_objects(acpi_object_type type, (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE); } -#endif /* ACPI_FUTURE_USAGE */ -#ifdef ACPI_FUTURE_USAGE /******************************************************************************* * * FUNCTION: acpi_ns_dump_one_object_path, acpi_ns_get_max_depth @@ -810,7 +805,6 @@ acpi_ns_dump_object_paths(acpi_object_type type, (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE); } -#endif /* ACPI_FUTURE_USAGE */ /******************************************************************************* * diff --git a/drivers/acpi/acpica/pstree.c b/drivers/acpi/acpica/pstree.c index 89984f3..cf2f2fa 100644 --- a/drivers/acpi/acpica/pstree.c +++ b/drivers/acpi/acpica/pstree.c @@ -183,7 +183,6 @@ acpi_ps_append_arg(union acpi_parse_object *op, union acpi_parse_object *arg) } } -#ifdef ACPI_FUTURE_USAGE /******************************************************************************* * * FUNCTION: acpi_ps_get_depth_next @@ -317,4 +316,3 @@ union acpi_parse_object *acpi_ps_get_child(union acpi_parse_object *op) return (child); } #endif -#endif /* ACPI_FUTURE_USAGE */ diff --git a/drivers/acpi/acpica/psutils.c b/drivers/acpi/acpica/psutils.c index 183cc1e..71d2877 100644 --- a/drivers/acpi/acpica/psutils.c +++ b/drivers/acpi/acpica/psutils.c @@ -205,7 +205,6 @@ u8 acpi_ps_is_leading_char(u32 c) /* * Get op's name (4-byte name segment) or 0 if unnamed */ -#ifdef ACPI_FUTURE_USAGE u32 acpi_ps_get_name(union acpi_parse_object * op) { @@ -219,7 +218,6 @@ u32 acpi_ps_get_name(union acpi_parse_object * op) return (op->named.name); } -#endif /* ACPI_FUTURE_USAGE */ /* * Set op's name diff --git a/drivers/acpi/acpica/rsutils.c b/drivers/acpi/acpica/rsutils.c index 52b024d..9486992 100644 --- a/drivers/acpi/acpica/rsutils.c +++ b/drivers/acpi/acpica/rsutils.c @@ -564,7 +564,6 @@ acpi_rs_get_crs_method_data(struct acpi_namespace_node *node, * ******************************************************************************/ -#ifdef ACPI_FUTURE_USAGE acpi_status acpi_rs_get_prs_method_data(struct acpi_namespace_node *node, struct acpi_buffer *ret_buffer) @@ -596,7 +595,6 @@ acpi_rs_get_prs_method_data(struct acpi_namespace_node *node, acpi_ut_remove_reference(obj_desc); return_ACPI_STATUS(status); } -#endif /* ACPI_FUTURE_USAGE */ /******************************************************************************* * diff --git a/drivers/acpi/acpica/rsxface.c b/drivers/acpi/acpica/rsxface.c index de51f83..1e8cd57 100644 --- a/drivers/acpi/acpica/rsxface.c +++ b/drivers/acpi/acpica/rsxface.c @@ -220,7 +220,7 @@ acpi_get_current_resources(acpi_handle device_handle, } ACPI_EXPORT_SYMBOL(acpi_get_current_resources) -#ifdef ACPI_FUTURE_USAGE + /******************************************************************************* * * FUNCTION: acpi_get_possible_resources @@ -262,7 +262,7 @@ acpi_get_possible_resources(acpi_handle device_handle, } ACPI_EXPORT_SYMBOL(acpi_get_possible_resources) -#endif /* ACPI_FUTURE_USAGE */ + /******************************************************************************* * * FUNCTION: acpi_set_current_resources diff --git a/drivers/acpi/acpica/utxface.c b/drivers/acpi/acpica/utxface.c index a6b0eb0..f9c8f9c 100644 --- a/drivers/acpi/acpica/utxface.c +++ b/drivers/acpi/acpica/utxface.c @@ -253,7 +253,7 @@ acpi_install_initialization_handler(acpi_init_handler handler, u32 function) } ACPI_EXPORT_SYMBOL(acpi_install_initialization_handler) -#endif /* ACPI_FUTURE_USAGE */ +#endif /***************************************************************************** * diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c index 739a4a6..3272915 100644 --- a/drivers/acpi/osl.c +++ b/drivers/acpi/osl.c @@ -66,8 +66,6 @@ struct acpi_os_dpc { /* stuff for debugger support */ int acpi_in_debugger; EXPORT_SYMBOL(acpi_in_debugger); - -extern char line_buf[80]; #endif /*ENABLE_DEBUGGER */ static int (*__acpi_os_prepare_sleep)(u8 sleep_state, u32 pm1a_ctrl, @@ -1345,15 +1343,13 @@ acpi_status acpi_os_signal_semaphore(acpi_handle handle, u32 units) return AE_OK; } -#ifdef ACPI_FUTURE_USAGE -u32 acpi_os_get_line(char *buffer) +acpi_status acpi_os_get_line(char *buffer, u32 buffer_length, u32 *bytes_read) { - #ifdef ENABLE_DEBUGGER if (acpi_in_debugger) { u32 chars; - kdb_read(buffer, sizeof(line_buf)); + kdb_read(buffer, buffer_length); /* remove the CR kdb includes */ chars = strlen(buffer) - 1; @@ -1361,9 +1357,8 @@ u32 acpi_os_get_line(char *buffer) } #endif - return 0; + return AE_OK; } -#endif /* ACPI_FUTURE_USAGE */ acpi_status acpi_os_signal(u32 function, void *info) { diff --git a/include/acpi/acpixf.h b/include/acpi/acpixf.h index f837adf..ded9fa3 100644 --- a/include/acpi/acpixf.h +++ b/include/acpi/acpixf.h @@ -393,15 +393,11 @@ ACPI_EXTERNAL_RETURN_STATUS(acpi_status __init acpi_terminate(void)) */ ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status acpi_enable(void)) ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status acpi_disable(void)) -#ifdef ACPI_FUTURE_USAGE ACPI_EXTERNAL_RETURN_STATUS(acpi_status acpi_subsystem_status(void)) -#endif -#ifdef ACPI_FUTURE_USAGE ACPI_EXTERNAL_RETURN_STATUS(acpi_status acpi_get_system_info(struct acpi_buffer *ret_buffer)) -#endif ACPI_EXTERNAL_RETURN_STATUS(acpi_status acpi_get_statistics(struct acpi_statistics *stats)) ACPI_EXTERNAL_RETURN_PTR(const char @@ -625,11 +621,9 @@ ACPI_EXTERNAL_RETURN_STATUS(acpi_status space_id, acpi_adr_space_handler handler)) -#ifdef ACPI_FUTURE_USAGE ACPI_EXTERNAL_RETURN_STATUS(acpi_status acpi_install_exception_handler (acpi_exception_handler handler)) -#endif ACPI_EXTERNAL_RETURN_STATUS(acpi_status acpi_install_interface_handler (acpi_interface_handler handler)) @@ -750,12 +744,10 @@ ACPI_EXTERNAL_RETURN_STATUS(acpi_status acpi_get_current_resources(acpi_handle device, struct acpi_buffer *ret_buffer)) -#ifdef ACPI_FUTURE_USAGE ACPI_EXTERNAL_RETURN_STATUS(acpi_status acpi_get_possible_resources(acpi_handle device, struct acpi_buffer *ret_buffer)) -#endif ACPI_EXTERNAL_RETURN_STATUS(acpi_status acpi_get_event_resources(acpi_handle device_handle, struct acpi_buffer @@ -844,7 +836,6 @@ ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status /* * ACPI Timer interfaces */ -#ifdef ACPI_FUTURE_USAGE ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status acpi_get_timer_resolution(u32 *resolution)) ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status acpi_get_timer(u32 *ticks)) @@ -853,7 +844,6 @@ ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status acpi_get_timer_duration(u32 start_ticks, u32 end_ticks, u32 *time_elapsed)) -#endif /* ACPI_FUTURE_USAGE */ /* * Error/Warning output diff --git a/include/acpi/platform/aclinux.h b/include/acpi/platform/aclinux.h index 74ba46c..323e5da 100644 --- a/include/acpi/platform/aclinux.h +++ b/include/acpi/platform/aclinux.h @@ -63,12 +63,16 @@ #define ACPI_USE_SYSTEM_INTTYPES -/* Compile for reduced hardware mode only with this kernel config */ +/* Kernel specific ACPICA configuration */ #ifdef CONFIG_ACPI_REDUCED_HARDWARE_ONLY #define ACPI_REDUCED_HARDWARE 1 #endif +#ifdef CONFIG_ACPI_DEBUGGER +#define ACPI_DEBUGGER +#endif + #include #include #include @@ -151,7 +155,6 @@ * OSL interfaces used by utilities */ #define ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_redirect_output -#define ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_get_line #define ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_get_table_by_name #define ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_get_table_by_index #define ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_get_table_by_address diff --git a/include/acpi/platform/aclinuxex.h b/include/acpi/platform/aclinuxex.h index acedc3f..fd6d70f 100644 --- a/include/acpi/platform/aclinuxex.h +++ b/include/acpi/platform/aclinuxex.h @@ -124,6 +124,11 @@ static inline acpi_thread_id acpi_os_get_thread_id(void) lock ? AE_OK : AE_NO_MEMORY; \ }) +static inline u8 acpi_os_readable(void *pointer, acpi_size length) +{ + return TRUE; +} + /* * OSL interfaces added by Linux */ -- cgit v0.10.2 From aaa93a618b3f1a94f70b09f9e22481d4a51976d0 Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Mon, 19 Oct 2015 10:26:02 +0800 Subject: ACPICA: Debugger: Fix dead lock issue ocurred in single stepping mode ACPICA commit 35273add90da19cd8790fdb5735f52e3c9861684 When single step execution is not ended, executing another control methods leads to dead locks around interpreter lock/namespace lock/method serialization lock. So we should only allow one execution from the debugger at same time. Lv Zheng. Link: https://github.com/acpica/acpica/commit/35273add Signed-off-by: Lv Zheng Signed-off-by: Bob Moore Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/acpica/dbexec.c b/drivers/acpi/acpica/dbexec.c index 258e615..d713e2d 100644 --- a/drivers/acpi/acpica/dbexec.c +++ b/drivers/acpi/acpica/dbexec.c @@ -370,7 +370,17 @@ acpi_db_execute(char *name, char **args, acpi_object_type * types, u32 flags) #ifdef ACPI_DEBUG_OUTPUT u32 previous_allocations; u32 allocations; +#endif + /* + * Allow one execution to be performed by debugger or single step + * execution will be dead locked by the interpreter mutexes. + */ + if (acpi_gbl_method_executing) { + acpi_os_printf("Only one debugger execution is allowed.\n"); + return; + } +#ifdef ACPI_DEBUG_OUTPUT /* Memory allocation tracking */ previous_allocations = acpi_db_get_outstanding_allocations(); -- cgit v0.10.2 From b31968828352ecae41d47aaa703e16c1ba06bfd8 Mon Sep 17 00:00:00 2001 From: Bob Moore Date: Mon, 19 Oct 2015 10:26:08 +0800 Subject: ACPICA: Update version to 20150930 ACPICA commit e9c75ca267262326e80d49a290e8387a5963e2d2 Version 20150930. Link: https://github.com/acpica/acpica/commit/e9c75ca2 Signed-off-by: Bob Moore Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki diff --git a/include/acpi/acpixf.h b/include/acpi/acpixf.h index ded9fa3..3aaaa86 100644 --- a/include/acpi/acpixf.h +++ b/include/acpi/acpixf.h @@ -46,7 +46,7 @@ /* Current ACPICA subsystem version in YYYYMMDD format */ -#define ACPI_CA_VERSION 0x20150818 +#define ACPI_CA_VERSION 0x20150930 #include #include -- cgit v0.10.2 From da1a64f80d8946d51744e48d0ba138c1b17a4852 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 3 Oct 2015 00:24:25 +0100 Subject: cpuidle: mvebu: clean up multiple platform drivers There's no need to use multiple platform drivers, especially when we want to do something different in the probe, but we still use a common probe function. We can use the platform ID system to only register one platform driver, but have it match several devices, and give us the CPU idle driver via the ID's driver_data. Signed-off-by: Russell King Signed-off-by: Daniel Lezcano diff --git a/drivers/cpuidle/cpuidle-mvebu-v7.c b/drivers/cpuidle/cpuidle-mvebu-v7.c index 980151f..facd7d3 100644 --- a/drivers/cpuidle/cpuidle-mvebu-v7.c +++ b/drivers/cpuidle/cpuidle-mvebu-v7.c @@ -99,44 +99,39 @@ static struct cpuidle_driver armada38x_idle_driver = { static int mvebu_v7_cpuidle_probe(struct platform_device *pdev) { - mvebu_v7_cpu_suspend = pdev->dev.platform_data; + const struct platform_device_id *id = pdev->id_entry; - if (!strcmp(pdev->dev.driver->name, "cpuidle-armada-xp")) - return cpuidle_register(&armadaxp_idle_driver, NULL); - else if (!strcmp(pdev->dev.driver->name, "cpuidle-armada-370")) - return cpuidle_register(&armada370_idle_driver, NULL); - else if (!strcmp(pdev->dev.driver->name, "cpuidle-armada-38x")) - return cpuidle_register(&armada38x_idle_driver, NULL); - else + if (!id) return -EINVAL; -} -static struct platform_driver armadaxp_cpuidle_plat_driver = { - .driver = { - .name = "cpuidle-armada-xp", - }, - .probe = mvebu_v7_cpuidle_probe, -}; + mvebu_v7_cpu_suspend = pdev->dev.platform_data; -module_platform_driver(armadaxp_cpuidle_plat_driver); + return cpuidle_register((struct cpuidle_driver *)id->driver_data, NULL); +} -static struct platform_driver armada370_cpuidle_plat_driver = { - .driver = { +static const struct platform_device_id mvebu_cpuidle_ids[] = { + { + .name = "cpuidle-armada-xp", + .driver_data = (unsigned long)&armadaxp_idle_driver, + }, { .name = "cpuidle-armada-370", + .driver_data = (unsigned long)&armada370_idle_driver, + }, { + .name = "cpuidle-armada-38x", + .driver_data = (unsigned long)&armada38x_idle_driver, }, - .probe = mvebu_v7_cpuidle_probe, + {} }; -module_platform_driver(armada370_cpuidle_plat_driver); - -static struct platform_driver armada38x_cpuidle_plat_driver = { +static struct platform_driver mvebu_cpuidle_driver = { + .probe = mvebu_v7_cpuidle_probe, .driver = { - .name = "cpuidle-armada-38x", + .name = "cpuidle-mbevu", }, - .probe = mvebu_v7_cpuidle_probe, + .id_table = mvebu_cpuidle_ids, }; -module_platform_driver(armada38x_cpuidle_plat_driver); +module_platform_driver(mvebu_cpuidle_driver); MODULE_AUTHOR("Gregory CLEMENT "); MODULE_DESCRIPTION("Marvell EBU v7 cpuidle driver"); -- cgit v0.10.2 From ab319939a58678c19a68ff10c8a08cef462a3ba6 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 3 Oct 2015 00:24:30 +0100 Subject: cpuidle: mvebu: disable the bind/unbind attributes and use builtin_platform_driver As the driver doesn't support unbinding, nor does it support arbitary binding of devices, disable the bind/unbind attributes for this driver. Also, as the driver has no remove function, it can never be modular, so use builtin_platform_driver() to avoid the module exit boilerplate. Signed-off-by: Russell King Signed-off-by: Daniel Lezcano diff --git a/drivers/cpuidle/cpuidle-mvebu-v7.c b/drivers/cpuidle/cpuidle-mvebu-v7.c index facd7d3..01a8569 100644 --- a/drivers/cpuidle/cpuidle-mvebu-v7.c +++ b/drivers/cpuidle/cpuidle-mvebu-v7.c @@ -127,11 +127,12 @@ static struct platform_driver mvebu_cpuidle_driver = { .probe = mvebu_v7_cpuidle_probe, .driver = { .name = "cpuidle-mbevu", + .suppress_bind_attrs = true, }, .id_table = mvebu_cpuidle_ids, }; -module_platform_driver(mvebu_cpuidle_driver); +builtin_platform_driver(mvebu_cpuidle_driver); MODULE_AUTHOR("Gregory CLEMENT "); MODULE_DESCRIPTION("Marvell EBU v7 cpuidle driver"); -- cgit v0.10.2 From 49e4b84333f338d4f183f28f1f3c1131b9fb2b5a Mon Sep 17 00:00:00 2001 From: Chen Yu Date: Sun, 25 Oct 2015 01:02:19 +0800 Subject: ACPI: Use correct IRQ when uninstalling ACPI interrupt handler Currently when the system is trying to uninstall the ACPI interrupt handler, it uses acpi_gbl_FADT.sci_interrupt as the IRQ number. However, the IRQ number that the ACPI interrupt handled is installed for comes from acpi_gsi_to_irq() and that is the number that should be used for the handler removal. Fix this problem by using the mapped IRQ returned from acpi_gsi_to_irq() as appropriate. Cc: All applicable Acked-by: Lv Zheng Signed-off-by: Chen Yu Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c index 739a4a6..2a25919 100644 --- a/drivers/acpi/osl.c +++ b/drivers/acpi/osl.c @@ -81,6 +81,7 @@ static struct workqueue_struct *kacpid_wq; static struct workqueue_struct *kacpi_notify_wq; static struct workqueue_struct *kacpi_hotplug_wq; static bool acpi_os_initialized; +unsigned int acpi_sci_irq = INVALID_ACPI_IRQ; /* * This list of permanent mappings is for memory that may be accessed from @@ -856,17 +857,19 @@ acpi_os_install_interrupt_handler(u32 gsi, acpi_osd_handler handler, acpi_irq_handler = NULL; return AE_NOT_ACQUIRED; } + acpi_sci_irq = irq; return AE_OK; } -acpi_status acpi_os_remove_interrupt_handler(u32 irq, acpi_osd_handler handler) +acpi_status acpi_os_remove_interrupt_handler(u32 gsi, acpi_osd_handler handler) { - if (irq != acpi_gbl_FADT.sci_interrupt) + if (gsi != acpi_gbl_FADT.sci_interrupt || !acpi_sci_irq_valid()) return AE_BAD_PARAMETER; - free_irq(irq, acpi_irq); + free_irq(acpi_sci_irq, acpi_irq); acpi_irq_handler = NULL; + acpi_sci_irq = INVALID_ACPI_IRQ; return AE_OK; } diff --git a/include/linux/acpi.h b/include/linux/acpi.h index 43856d1..1ae6ba0 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -193,6 +193,12 @@ int acpi_ioapic_registered(acpi_handle handle, u32 gsi_base); void acpi_irq_stats_init(void); extern u32 acpi_irq_handled; extern u32 acpi_irq_not_handled; +extern unsigned int acpi_sci_irq; +#define INVALID_ACPI_IRQ ((unsigned)-1) +static inline bool acpi_sci_irq_valid(void) +{ + return acpi_sci_irq != INVALID_ACPI_IRQ; +} extern int sbf_port; extern unsigned long acpi_realmode_flags; -- cgit v0.10.2 From efb1cf7d28b8aeacec53e9ba8f3f2809c5cb9686 Mon Sep 17 00:00:00 2001 From: Chen Yu Date: Sun, 25 Oct 2015 01:02:36 +0800 Subject: ACPI: Using correct irq when waiting for events When the system is waiting for GPE/fixed event handler to finish, it uses acpi_gbl_FADT.sci_interrupt directly as the IRQ number. However, the remapped IRQ returned by acpi_gsi_to_irq() should be passed to synchronize_hardirq() instead of it. Cc: 3.19+ # 3.19+ Acked-by: Lv Zheng Signed-off-by: Chen Yu Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c index 2a25919..2f6e3c6 100644 --- a/drivers/acpi/osl.c +++ b/drivers/acpi/osl.c @@ -1183,8 +1183,8 @@ void acpi_os_wait_events_complete(void) * Make sure the GPE handler or the fixed event handler is not used * on another CPU after removal. */ - if (acpi_irq_handler) - synchronize_hardirq(acpi_gbl_FADT.sci_interrupt); + if (acpi_sci_irq_valid()) + synchronize_hardirq(acpi_sci_irq); flush_workqueue(kacpid_wq); flush_workqueue(kacpi_notify_wq); } -- cgit v0.10.2 From 8c01275e0cdf1959aa25c322fd5870c097733195 Mon Sep 17 00:00:00 2001 From: Chen Yu Date: Sun, 25 Oct 2015 01:02:46 +0800 Subject: ACPI / PM: Fix incorrect wakeup IRQ setting during suspend-to-idle For an ACPI compatible system, the SCI (ACPI System Control Interrupt) is used to wake the system up from suspend-to-idle. Once the CPU is woken up by the SCI, the interrupt handler will first check if the current IRQ has been configured for system wakeup, so irq_pm_check_wakeup() is invoked to validate the IRQ number. However, during suspend-to-idle, enable_irq_wake() is called for acpi_gbl_FADT.sci_interrupt, although the IRQ number that the SCI handler has been installed for should be passed to it instead. Thus, if acpi_gbl_FADT.sci_interrupt happens to be different from that number, ACPI interrupts will not be able to wake up the system from sleep. Fix this problem by passing the IRQ number returned by acpi_gsi_to_irq() to enable_irq_wake() instead of acpi_gbl_FADT.sci_interrupt. Cc: 3.18+ # 3.18+ Acked-by: Lv Zheng Signed-off-by: Chen Yu Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/sleep.c b/drivers/acpi/sleep.c index 2f0d4db..3fe1fbe 100644 --- a/drivers/acpi/sleep.c +++ b/drivers/acpi/sleep.c @@ -632,14 +632,16 @@ static int acpi_freeze_prepare(void) acpi_enable_wakeup_devices(ACPI_STATE_S0); acpi_enable_all_wakeup_gpes(); acpi_os_wait_events_complete(); - enable_irq_wake(acpi_gbl_FADT.sci_interrupt); + if (acpi_sci_irq_valid()) + enable_irq_wake(acpi_sci_irq); return 0; } static void acpi_freeze_restore(void) { acpi_disable_wakeup_devices(ACPI_STATE_S0); - disable_irq_wake(acpi_gbl_FADT.sci_interrupt); + if (acpi_sci_irq_valid()) + disable_irq_wake(acpi_sci_irq); acpi_enable_all_runtime_gpes(); } -- cgit v0.10.2 From 21657471688508ced523b3bf0a763d73d546ad1a Mon Sep 17 00:00:00 2001 From: Jiri Kosina Date: Mon, 26 Oct 2015 04:02:01 +0100 Subject: ACPI / PAD: power_saving_thread() is not freezable power_saving_thread() calls try_to_freeze(), but the thread doesn't mark itself freezable through set_freezable(), so the try_to_freeze() call is useless. Signed-off-by: Jiri Kosina Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/acpi_pad.c b/drivers/acpi/acpi_pad.c index ae307ff..8ea8211 100644 --- a/drivers/acpi/acpi_pad.c +++ b/drivers/acpi/acpi_pad.c @@ -148,8 +148,6 @@ static int power_saving_thread(void *data) while (!kthread_should_stop()) { unsigned long expire_time; - try_to_freeze(); - /* round robin to cpus */ expire_time = last_jiffies + round_robin_time * HZ; if (time_before(expire_time, jiffies)) { -- cgit v0.10.2 From 32c0b2f606a4b71fc7b7cfa479cf47648854d9d9 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 22 Oct 2015 22:52:59 +0300 Subject: ACPI / CPPC: signedness bug in register_pcc_channel() The "pcc_subspace_idx" is -1 if it hasn't been initialized yet. We need it to be signed. Fixes: 337aadff8e45 (ACPI: Introduce CPU performance controls using CPPC) Signed-off-by: Dan Carpenter Acked-by: Ashwin Chaugule Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c index 0bbf84b..2ed5516 100644 --- a/drivers/acpi/cppc_acpi.c +++ b/drivers/acpi/cppc_acpi.c @@ -302,7 +302,7 @@ err_ret: } EXPORT_SYMBOL_GPL(acpi_get_psd_map); -static int register_pcc_channel(unsigned pcc_subspace_idx) +static int register_pcc_channel(int pcc_subspace_idx) { struct acpi_pcct_subspace *cppc_ss; unsigned int len; -- cgit v0.10.2 From 4219853aef645d29b122daa8a8b7bd792f1cc7c9 Mon Sep 17 00:00:00 2001 From: Ashwin Chaugule Date: Fri, 23 Oct 2015 05:02:52 -0400 Subject: ACPI / CPPC: Fix potential memory leak Commit 337aadff8e45 (ACPI: Introduce CPU performance controls using CPPC) leads to the following static checker warning: drivers/acpi/cppc_acpi.c:527 acpi_cppc_processor_probe() warn: overwrite may leak 'cpc_ptr' Fix the warning by removing the bogus per-CPU pointer dereference. Fixes: 337aadff8e45 (ACPI: Introduce CPU performance controls using CPPC) Reported-by: Dan Carpenter Signed-off-by: Ashwin Chaugule Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c index 2ed5516..3c083d2 100644 --- a/drivers/acpi/cppc_acpi.c +++ b/drivers/acpi/cppc_acpi.c @@ -524,7 +524,6 @@ int acpi_cppc_processor_probe(struct acpi_processor *pr) return 0; out_free: - cpc_ptr = per_cpu(cpc_desc_ptr, pr->id); kfree(cpc_ptr); out_buf_free: -- cgit v0.10.2 From 584d8d1eb123b8be1274bf69f3ce07cee848d40d Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 21 Oct 2015 13:45:03 +0200 Subject: ACPI / video: Add a quirk to force native backlight on Lenovo IdeaPad S405 The Lenovo IdeaPad S405 is a not "Windows8 ready" machine which still has a broken ACPI video backlight implementation. Add a quirk to force use of native backlight on this machine. Link: https://bugzilla.redhat.com/show_bug.cgi?id=1201530 Signed-off-by: Hans de Goede Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/video_detect.c b/drivers/acpi/video_detect.c index 2922f1f..0d3a384 100644 --- a/drivers/acpi/video_detect.c +++ b/drivers/acpi/video_detect.c @@ -244,6 +244,15 @@ static const struct dmi_system_id video_detect_dmi_table[] = { /* Non win8 machines which need native backlight nevertheless */ { + /* https://bugzilla.redhat.com/show_bug.cgi?id=1201530 */ + .callback = video_detect_force_native, + .ident = "Lenovo Ideapad S405", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_BOARD_NAME, "Lenovo IdeaPad S405"), + }, + }, + { /* https://bugzilla.redhat.com/show_bug.cgi?id=1187004 */ .callback = video_detect_force_native, .ident = "Lenovo Ideapad Z570", -- cgit v0.10.2 From bc1e49df345cf9be38ba29ead45a12f86ceb5fe2 Mon Sep 17 00:00:00 2001 From: Insu Yun Date: Thu, 15 Oct 2015 12:19:30 -0400 Subject: ACPI / sysfs: correctly check failing memory allocation Since kobject_create_and_add() can fail under memory pressure, its return value needs to be checked against NULL before passing it to sysfs_create_file(). Signed-off-by: Insu Yun [ rjw: Subject & changelog ] Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/sysfs.c b/drivers/acpi/sysfs.c index 40a4265..0243d37 100644 --- a/drivers/acpi/sysfs.c +++ b/drivers/acpi/sysfs.c @@ -878,6 +878,9 @@ int __init acpi_sysfs_init(void) return result; hotplug_kobj = kobject_create_and_add("hotplug", acpi_kobj); + if (!hotplug_kobj) + return -ENOMEM; + result = sysfs_create_file(hotplug_kobj, &force_remove_attr.attr); if (result) return result; -- cgit v0.10.2 From e58d154b60488a67278d4d54853ef04d30b06251 Mon Sep 17 00:00:00 2001 From: Rami Rosen Date: Sat, 26 Sep 2015 19:27:57 +0300 Subject: ACPI / Documentation: add copy_dsdt to ACPI format options commit aa2110cb1a75 (ACPI: add boot option acpi=copy_dsdt to fix corrupt DSDT) added copy_dsdt as an ACPI boot option, but did not add it to ACPI format options in Documentation/kernel-parameters.txt. Fix that. Signed-off-by: Rami Rosen [ rjw: Subject & changelog ] Signed-off-by: Rafael J. Wysocki diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 22a4b68..bbf4ee6c 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -167,7 +167,8 @@ bytes respectively. Such letter suffixes can also be entirely omitted. acpi= [HW,ACPI,X86,ARM64] Advanced Configuration and Power Interface - Format: { force | off | strict | noirq | rsdt } + Format: { force | off | strict | noirq | rsdt | + copy_dsdt } force -- enable ACPI if default was off off -- disable ACPI if default was on noirq -- do not use ACPI for IRQ routing -- cgit v0.10.2 From a61756165116563064ab94183713509b546c428b Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Mon, 19 Oct 2015 10:16:07 +0200 Subject: PM / clk: Fix broken build due to non-matching code and header #ifdefs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If an architecture's main Kconfig file doesn't include kernel/power/Kconfig, but CONFIG_PM=y and HAVE_CLK=y (e.g. m68knommu allmodconfig): drivers/base/power/clock_ops.c: In function ‘__pm_clk_add’: drivers/base/power/clock_ops.c:106: error: ‘struct pm_subsys_data’ has no member named ‘clock_list’ drivers/base/power/clock_ops.c: At top level: drivers/base/power/clock_ops.c:120: error: redefinition of ‘pm_clk_add’ include/linux/pm_clock.h:64: error: previous definition of ‘pm_clk_add’ was here drivers/base/power/clock_ops.c:135: error: redefinition of ‘pm_clk_add_clk’ include/linux/pm_clock.h:69: error: previous definition of ‘pm_clk_add_clk’ was here drivers/base/power/clock_ops.c:171: error: redefinition of ‘pm_clk_remove’ include/linux/pm_clock.h:73: error: previous definition of ‘pm_clk_remove’ was here drivers/base/power/clock_ops.c: In function ‘pm_clk_remove’: drivers/base/power/clock_ops.c:180: error: ‘struct pm_subsys_data’ has no member named ‘clock_list’ drivers/base/power/clock_ops.c:180: error: ‘struct pm_subsys_data’ has no member named ‘clock_list’ drivers/base/power/clock_ops.c: At top level: drivers/base/power/clock_ops.c:207: error: redefinition of ‘pm_clk_init’ include/linux/pm_clock.h:54: error: previous definition of ‘pm_clk_init’ was here drivers/base/power/clock_ops.c: In function ‘pm_clk_init’: drivers/base/power/clock_ops.c:210: error: ‘struct pm_subsys_data’ has no member named ‘clock_list’ drivers/base/power/clock_ops.c: At top level: drivers/base/power/clock_ops.c:221: error: redefinition of ‘pm_clk_create’ include/linux/pm_clock.h:57: error: previous definition of ‘pm_clk_create’ was here drivers/base/power/clock_ops.c:234: error: redefinition of ‘pm_clk_destroy’ include/linux/pm_clock.h:61: error: previous definition of ‘pm_clk_destroy’ was here drivers/base/power/clock_ops.c: In function ‘pm_clk_destroy’: drivers/base/power/clock_ops.c:246: error: ‘struct pm_subsys_data’ has no member named ‘clock_list’ drivers/base/power/clock_ops.c:246: error: ‘struct pm_subsys_data’ has no member named ‘clock_list’ drivers/base/power/clock_ops.c: At top level: drivers/base/power/clock_ops.c:263: error: expected identifier or ‘(’ before ‘void’ drivers/base/power/clock_ops.c:263: error: expected ‘)’ before numeric constant drivers/base/power/clock_ops.c:293: error: expected identifier or ‘(’ before ‘void’ drivers/base/power/clock_ops.c:293: error: expected ‘)’ before numeric constant drivers/base/power/clock_ops.c: In function ‘pm_clk_runtime_suspend’: drivers/base/power/clock_ops.c:384: error: called object ‘0u’ is not a function drivers/base/power/clock_ops.c: In function ‘pm_clk_runtime_resume’: drivers/base/power/clock_ops.c:400: error: called object ‘0u’ is not a function This happens because: - drivers/base/power/clock_ops.c depends on CONFIG_HAVE_CLK, - the failing code inside clock_ops.c additionally depends on CONFIG_PM, - the forward declarations and other definitions in depend on CONFIG_PM_CLK, - CONFIG_PM_CLK is defined as PM && HAVE_CLK in kernel/power/Kconfig, but it is not included on all architectures. Fix this by protecting the failing code inside clock_ops.c by CONFIG_PM_CLK instead of CONFIG_PM, so it matches . Signed-off-by: Geert Uytterhoeven Acked-by: Pavel Machek Signed-off-by: Rafael J. Wysocki diff --git a/drivers/base/power/clock_ops.c b/drivers/base/power/clock_ops.c index 652b5a3..fd0973b 100644 --- a/drivers/base/power/clock_ops.c +++ b/drivers/base/power/clock_ops.c @@ -17,7 +17,7 @@ #include #include -#ifdef CONFIG_PM +#ifdef CONFIG_PM_CLK enum pce_status { PCE_STATUS_NONE = 0, @@ -404,7 +404,7 @@ int pm_clk_runtime_resume(struct device *dev) return pm_generic_runtime_resume(dev); } -#else /* !CONFIG_PM */ +#else /* !CONFIG_PM_CLK */ /** * enable_clock - Enable a device clock. @@ -484,7 +484,7 @@ static int pm_clk_notify(struct notifier_block *nb, return 0; } -#endif /* !CONFIG_PM */ +#endif /* !CONFIG_PM_CLK */ /** * pm_clk_add_notifier - Add bus type notifier for power management clocks. -- cgit v0.10.2 From 51cda844892fded75d3ad07d0233e73572eba2f3 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Thu, 15 Oct 2015 17:02:06 +0200 Subject: PM / Domains: Don't measure ->start|stop() latency in system PM callbacks Measure latency does by itself contribute to an increased latency, thus we should avoid it when it isn't needed. Genpd measures latencies in the system PM phase for the ->start|stop() callbacks and is thus affecting the system PM suspend/resume time. Moreover these latencies are validated only at runtime PM suspend/resume. To this reasoning, let's decide to leave these measurements out of the system PM phase. There should be plenty of occasions during runtime PM to perform these measurements anyway. Signed-off-by: Ulf Hansson Reviewed-by: Lina Iyer Acked-by: Kevin Hilman Signed-off-by: Rafael J. Wysocki diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index 6e1bcde..a1c3ec4 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -90,8 +90,12 @@ static struct generic_pm_domain *dev_to_genpd(struct device *dev) return pd_to_genpd(dev->pm_domain); } -static int genpd_stop_dev(struct generic_pm_domain *genpd, struct device *dev) +static int genpd_stop_dev(struct generic_pm_domain *genpd, struct device *dev, + bool timed) { + if (!timed) + return GENPD_DEV_CALLBACK(genpd, int, stop, dev); + return GENPD_DEV_TIMED_CALLBACK(genpd, int, stop, dev, stop_latency_ns, "stop"); } @@ -434,7 +438,7 @@ static int pm_genpd_runtime_suspend(struct device *dev) if (ret) return ret; - ret = genpd_stop_dev(genpd, dev); + ret = genpd_stop_dev(genpd, dev, true); if (ret) { genpd_restore_dev(genpd, dev, true); return ret; @@ -779,7 +783,7 @@ static int pm_genpd_suspend_noirq(struct device *dev) || (dev->power.wakeup_path && genpd_dev_active_wakeup(genpd, dev))) return 0; - genpd_stop_dev(genpd, dev); + genpd_stop_dev(genpd, dev, false); /* * Since all of the "noirq" callbacks are executed sequentially, it is @@ -820,7 +824,7 @@ static int pm_genpd_resume_noirq(struct device *dev) pm_genpd_sync_poweron(genpd, true); genpd->suspended_count--; - return genpd_start_dev(genpd, dev, true); + return genpd_start_dev(genpd, dev, false); } /** @@ -928,7 +932,7 @@ static int pm_genpd_freeze_noirq(struct device *dev) if (IS_ERR(genpd)) return -EINVAL; - return genpd->suspend_power_off ? 0 : genpd_stop_dev(genpd, dev); + return genpd->suspend_power_off ? 0 : genpd_stop_dev(genpd, dev, false); } /** @@ -948,7 +952,8 @@ static int pm_genpd_thaw_noirq(struct device *dev) if (IS_ERR(genpd)) return -EINVAL; - return genpd->suspend_power_off ? 0 : genpd_start_dev(genpd, dev, true); + return genpd->suspend_power_off ? + 0 : genpd_start_dev(genpd, dev, false); } /** @@ -1042,7 +1047,7 @@ static int pm_genpd_restore_noirq(struct device *dev) pm_genpd_sync_poweron(genpd, true); - return genpd_start_dev(genpd, dev, true); + return genpd_start_dev(genpd, dev, false); } /** -- cgit v0.10.2 From 2b1d88cda32f81685bae45c00bf517f77bcda3cd Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Thu, 15 Oct 2015 17:02:19 +0200 Subject: PM / Domains: Merge measurements for PM QoS device latencies Measure latency does by itself contribute to an increased latency, thus we should avoid it when it isn't needed. By merging the latency measurements for the ->save_state() and the ->stop() callbacks, we get one measurement instead of two and we get one value to store instead of two. Let's also apply the likewise change for the ->start() and ->restore_state() callbacks. Signed-off-by: Ulf Hansson Reviewed-by: Lina Iyer Acked-by: Kevin Hilman Signed-off-by: Rafael J. Wysocki diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index a1c3ec4..a7dfdf9 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -34,22 +34,6 @@ __ret; \ }) -#define GENPD_DEV_TIMED_CALLBACK(genpd, type, callback, dev, field, name) \ -({ \ - ktime_t __start = ktime_get(); \ - type __retval = GENPD_DEV_CALLBACK(genpd, type, callback, dev); \ - s64 __elapsed = ktime_to_ns(ktime_sub(ktime_get(), __start)); \ - struct gpd_timing_data *__td = &dev_gpd_data(dev)->td; \ - if (!__retval && __elapsed > __td->field) { \ - __td->field = __elapsed; \ - dev_dbg(dev, name " latency exceeded, new value %lld ns\n", \ - __elapsed); \ - genpd->max_off_time_changed = true; \ - __td->constraint_changed = true; \ - } \ - __retval; \ -}) - static LIST_HEAD(gpd_list); static DEFINE_MUTEX(gpd_list_lock); @@ -90,24 +74,14 @@ static struct generic_pm_domain *dev_to_genpd(struct device *dev) return pd_to_genpd(dev->pm_domain); } -static int genpd_stop_dev(struct generic_pm_domain *genpd, struct device *dev, - bool timed) +static int genpd_stop_dev(struct generic_pm_domain *genpd, struct device *dev) { - if (!timed) - return GENPD_DEV_CALLBACK(genpd, int, stop, dev); - - return GENPD_DEV_TIMED_CALLBACK(genpd, int, stop, dev, - stop_latency_ns, "stop"); + return GENPD_DEV_CALLBACK(genpd, int, stop, dev); } -static int genpd_start_dev(struct generic_pm_domain *genpd, struct device *dev, - bool timed) +static int genpd_start_dev(struct generic_pm_domain *genpd, struct device *dev) { - if (!timed) - return GENPD_DEV_CALLBACK(genpd, int, start, dev); - - return GENPD_DEV_TIMED_CALLBACK(genpd, int, start, dev, - start_latency_ns, "start"); + return GENPD_DEV_CALLBACK(genpd, int, start, dev); } static bool genpd_sd_counter_dec(struct generic_pm_domain *genpd) @@ -263,19 +237,13 @@ static int genpd_poweron(struct generic_pm_domain *genpd) static int genpd_save_dev(struct generic_pm_domain *genpd, struct device *dev) { - return GENPD_DEV_TIMED_CALLBACK(genpd, int, save_state, dev, - save_state_latency_ns, "state save"); + return GENPD_DEV_CALLBACK(genpd, int, save_state, dev); } static int genpd_restore_dev(struct generic_pm_domain *genpd, - struct device *dev, bool timed) + struct device *dev) { - if (!timed) - return GENPD_DEV_CALLBACK(genpd, int, restore_state, dev); - - return GENPD_DEV_TIMED_CALLBACK(genpd, int, restore_state, dev, - restore_state_latency_ns, - "state restore"); + return GENPD_DEV_CALLBACK(genpd, int, restore_state, dev); } static int genpd_dev_pm_qos_notifier(struct notifier_block *nb, @@ -422,6 +390,9 @@ static int pm_genpd_runtime_suspend(struct device *dev) { struct generic_pm_domain *genpd; bool (*stop_ok)(struct device *__dev); + struct gpd_timing_data *td = &dev_gpd_data(dev)->td; + ktime_t time_start; + s64 elapsed_ns; int ret; dev_dbg(dev, "%s()\n", __func__); @@ -434,16 +405,29 @@ static int pm_genpd_runtime_suspend(struct device *dev) if (stop_ok && !stop_ok(dev)) return -EBUSY; + /* Measure suspend latency. */ + time_start = ktime_get(); + ret = genpd_save_dev(genpd, dev); if (ret) return ret; - ret = genpd_stop_dev(genpd, dev, true); + ret = genpd_stop_dev(genpd, dev); if (ret) { - genpd_restore_dev(genpd, dev, true); + genpd_restore_dev(genpd, dev); return ret; } + /* Update suspend latency value if the measured time exceeds it. */ + elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start)); + if (elapsed_ns > td->suspend_latency_ns) { + td->suspend_latency_ns = elapsed_ns; + dev_dbg(dev, "suspend latency exceeded, %lld ns\n", + elapsed_ns); + genpd->max_off_time_changed = true; + td->constraint_changed = true; + } + /* * If power.irq_safe is set, this routine will be run with interrupts * off, so it can't use mutexes. @@ -469,6 +453,9 @@ static int pm_genpd_runtime_suspend(struct device *dev) static int pm_genpd_runtime_resume(struct device *dev) { struct generic_pm_domain *genpd; + struct gpd_timing_data *td = &dev_gpd_data(dev)->td; + ktime_t time_start; + s64 elapsed_ns; int ret; bool timed = true; @@ -492,8 +479,24 @@ static int pm_genpd_runtime_resume(struct device *dev) return ret; out: - genpd_start_dev(genpd, dev, timed); - genpd_restore_dev(genpd, dev, timed); + /* Measure resume latency. */ + if (timed) + time_start = ktime_get(); + + genpd_start_dev(genpd, dev); + genpd_restore_dev(genpd, dev); + + /* Update resume latency value if the measured time exceeds it. */ + if (timed) { + elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start)); + if (elapsed_ns > td->resume_latency_ns) { + td->resume_latency_ns = elapsed_ns; + dev_dbg(dev, "resume latency exceeded, %lld ns\n", + elapsed_ns); + genpd->max_off_time_changed = true; + td->constraint_changed = true; + } + } return 0; } @@ -783,7 +786,7 @@ static int pm_genpd_suspend_noirq(struct device *dev) || (dev->power.wakeup_path && genpd_dev_active_wakeup(genpd, dev))) return 0; - genpd_stop_dev(genpd, dev, false); + genpd_stop_dev(genpd, dev); /* * Since all of the "noirq" callbacks are executed sequentially, it is @@ -824,7 +827,7 @@ static int pm_genpd_resume_noirq(struct device *dev) pm_genpd_sync_poweron(genpd, true); genpd->suspended_count--; - return genpd_start_dev(genpd, dev, false); + return genpd_start_dev(genpd, dev); } /** @@ -932,7 +935,7 @@ static int pm_genpd_freeze_noirq(struct device *dev) if (IS_ERR(genpd)) return -EINVAL; - return genpd->suspend_power_off ? 0 : genpd_stop_dev(genpd, dev, false); + return genpd->suspend_power_off ? 0 : genpd_stop_dev(genpd, dev); } /** @@ -953,7 +956,7 @@ static int pm_genpd_thaw_noirq(struct device *dev) return -EINVAL; return genpd->suspend_power_off ? - 0 : genpd_start_dev(genpd, dev, false); + 0 : genpd_start_dev(genpd, dev); } /** @@ -1047,7 +1050,7 @@ static int pm_genpd_restore_noirq(struct device *dev) pm_genpd_sync_poweron(genpd, true); - return genpd_start_dev(genpd, dev, false); + return genpd_start_dev(genpd, dev); } /** diff --git a/drivers/base/power/domain_governor.c b/drivers/base/power/domain_governor.c index 85e17ba..e60dd12 100644 --- a/drivers/base/power/domain_governor.c +++ b/drivers/base/power/domain_governor.c @@ -77,10 +77,8 @@ static bool default_stop_ok(struct device *dev) dev_update_qos_constraint); if (constraint_ns > 0) { - constraint_ns -= td->save_state_latency_ns + - td->stop_latency_ns + - td->start_latency_ns + - td->restore_state_latency_ns; + constraint_ns -= td->suspend_latency_ns + + td->resume_latency_ns; if (constraint_ns == 0) return false; } diff --git a/include/linux/pm_domain.h b/include/linux/pm_domain.h index f4dd810..ba4ced3 100644 --- a/include/linux/pm_domain.h +++ b/include/linux/pm_domain.h @@ -81,10 +81,8 @@ struct gpd_link { }; struct gpd_timing_data { - s64 stop_latency_ns; - s64 start_latency_ns; - s64 save_state_latency_ns; - s64 restore_state_latency_ns; + s64 suspend_latency_ns; + s64 resume_latency_ns; s64 effective_constraint_ns; bool constraint_changed; bool cached_stop_ok; -- cgit v0.10.2 From 083701b13c79a908fb400dc24aaa80aad07ac462 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Tue, 13 Oct 2015 13:39:01 +0530 Subject: cpufreq: ondemand: Drop unnecessary locks from update_sampling_rate() 'timer_mutex' is required to sync work-handlers of policy->cpus. update_sampling_rate() is just canceling the works and queuing them again. This isn't protecting anything at all in update_sampling_rate() and is not gonna be of any use. Even if a work-handler is already running for a CPU, cancel_delayed_work_sync() will wait for it to finish. Drop these unnecessary locks. Reviewed-by: Preeti U Murthy Signed-off-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki diff --git a/drivers/cpufreq/cpufreq_ondemand.c b/drivers/cpufreq/cpufreq_ondemand.c index 1fa9088..03ac6ce 100644 --- a/drivers/cpufreq/cpufreq_ondemand.c +++ b/drivers/cpufreq/cpufreq_ondemand.c @@ -267,27 +267,19 @@ static void update_sampling_rate(struct dbs_data *dbs_data, dbs_info = &per_cpu(od_cpu_dbs_info, cpu); cpufreq_cpu_put(policy); - mutex_lock(&dbs_info->cdbs.shared->timer_mutex); - - if (!delayed_work_pending(&dbs_info->cdbs.dwork)) { - mutex_unlock(&dbs_info->cdbs.shared->timer_mutex); + if (!delayed_work_pending(&dbs_info->cdbs.dwork)) continue; - } next_sampling = jiffies + usecs_to_jiffies(new_rate); appointed_at = dbs_info->cdbs.dwork.timer.expires; if (time_before(next_sampling, appointed_at)) { - - mutex_unlock(&dbs_info->cdbs.shared->timer_mutex); cancel_delayed_work_sync(&dbs_info->cdbs.dwork); - mutex_lock(&dbs_info->cdbs.shared->timer_mutex); gov_queue_work(dbs_data, policy, usecs_to_jiffies(new_rate), true); } - mutex_unlock(&dbs_info->cdbs.shared->timer_mutex); } } -- cgit v0.10.2 From 0998a03a3a4d35290993f86ffed0e311e8dd78f5 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 15 Oct 2015 21:35:21 +0530 Subject: cpufreq: Use cpumask_copy instead of cpumask_or to copy a mask ->related_cpus is empty at this point of time and copying ->cpus to it or orring ->related_cpus with ->cpus would result in the same value. But cpumask_copy makes it rather clear. Reviewed-by: Saravana Kannan Signed-off-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index 8701dc5..16b9e81 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -1214,7 +1214,7 @@ static int cpufreq_online(unsigned int cpu) if (new_policy) { /* related_cpus should at least include policy->cpus. */ - cpumask_or(policy->related_cpus, policy->related_cpus, policy->cpus); + cpumask_copy(policy->related_cpus, policy->cpus); /* Remember CPUs present at the policy creation time. */ cpumask_and(policy->real_cpus, policy->cpus, cpu_present_mask); } -- cgit v0.10.2 From 8eec1020f0c0c03f7219ed50cf1b754be49dd448 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 15 Oct 2015 21:35:22 +0530 Subject: cpufreq: create cpu/cpufreq at boot time Later patches will need to create policy specific directories in /sys/devices/system/cpu/cpufreq/ directory and so the cpufreq directory wouldn't be ever empty. And so no fun creating/destroying it on need basis anymore. Create it once on system boot. Reviewed-by: Saravana Kannan Signed-off-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index 16b9e81..c1fd57d 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -876,43 +876,15 @@ static struct kobj_type ktype_cpufreq = { struct kobject *cpufreq_global_kobject; EXPORT_SYMBOL(cpufreq_global_kobject); -static int cpufreq_global_kobject_usage; - -int cpufreq_get_global_kobject(void) -{ - if (!cpufreq_global_kobject_usage++) - return kobject_add(cpufreq_global_kobject, - &cpu_subsys.dev_root->kobj, "%s", "cpufreq"); - - return 0; -} -EXPORT_SYMBOL(cpufreq_get_global_kobject); - -void cpufreq_put_global_kobject(void) -{ - if (!--cpufreq_global_kobject_usage) - kobject_del(cpufreq_global_kobject); -} -EXPORT_SYMBOL(cpufreq_put_global_kobject); - int cpufreq_sysfs_create_file(const struct attribute *attr) { - int ret = cpufreq_get_global_kobject(); - - if (!ret) { - ret = sysfs_create_file(cpufreq_global_kobject, attr); - if (ret) - cpufreq_put_global_kobject(); - } - - return ret; + return sysfs_create_file(cpufreq_global_kobject, attr); } EXPORT_SYMBOL(cpufreq_sysfs_create_file); void cpufreq_sysfs_remove_file(const struct attribute *attr) { sysfs_remove_file(cpufreq_global_kobject, attr); - cpufreq_put_global_kobject(); } EXPORT_SYMBOL(cpufreq_sysfs_remove_file); @@ -2582,7 +2554,7 @@ static int __init cpufreq_core_init(void) if (cpufreq_disabled()) return -ENODEV; - cpufreq_global_kobject = kobject_create(); + cpufreq_global_kobject = kobject_create_and_add("cpufreq", &cpu_subsys.dev_root->kobj); BUG_ON(!cpufreq_global_kobject); register_syscore_ops(&cpufreq_syscore_ops); diff --git a/drivers/cpufreq/cpufreq_governor.c b/drivers/cpufreq/cpufreq_governor.c index 750626d..11258c4 100644 --- a/drivers/cpufreq/cpufreq_governor.c +++ b/drivers/cpufreq/cpufreq_governor.c @@ -348,29 +348,21 @@ static int cpufreq_governor_init(struct cpufreq_policy *policy, set_sampling_rate(dbs_data, max(dbs_data->min_sampling_rate, latency * LATENCY_MULTIPLIER)); - if (!have_governor_per_policy()) { - if (WARN_ON(cpufreq_get_global_kobject())) { - ret = -EINVAL; - goto cdata_exit; - } + if (!have_governor_per_policy()) cdata->gdbs_data = dbs_data; - } ret = sysfs_create_group(get_governor_parent_kobj(policy), get_sysfs_attr(dbs_data)); if (ret) - goto put_kobj; + goto reset_gdbs_data; policy->governor_data = dbs_data; return 0; -put_kobj: - if (!have_governor_per_policy()) { +reset_gdbs_data: + if (!have_governor_per_policy()) cdata->gdbs_data = NULL; - cpufreq_put_global_kobject(); - } -cdata_exit: cdata->exit(dbs_data, !policy->governor->initialized); free_common_dbs_info: free_common_dbs_info(policy, cdata); @@ -394,10 +386,8 @@ static int cpufreq_governor_exit(struct cpufreq_policy *policy, sysfs_remove_group(get_governor_parent_kobj(policy), get_sysfs_attr(dbs_data)); - if (!have_governor_per_policy()) { + if (!have_governor_per_policy()) cdata->gdbs_data = NULL; - cpufreq_put_global_kobject(); - } cdata->exit(dbs_data, policy->governor->initialized == 1); kfree(dbs_data); diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h index dca22de..338bf0e 100644 --- a/include/linux/cpufreq.h +++ b/include/linux/cpufreq.h @@ -149,8 +149,6 @@ static inline bool policy_is_shared(struct cpufreq_policy *policy) /* /sys/devices/system/cpu/cpufreq: entry point for global variables */ extern struct kobject *cpufreq_global_kobject; -int cpufreq_get_global_kobject(void); -void cpufreq_put_global_kobject(void); int cpufreq_sysfs_create_file(const struct attribute *attr); void cpufreq_sysfs_remove_file(const struct attribute *attr); -- cgit v0.10.2 From c82bd44437f5d53d1654d9e36a9e4e55610f6624 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 15 Oct 2015 21:35:23 +0530 Subject: cpufreq: remove cpufreq_sysfs_{create|remove}_file() They don't do anything special now, remove the unnecessary wrapper. Reviewed-by: Saravana Kannan Signed-off-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index c1fd57d..04222e7 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -873,21 +873,6 @@ static struct kobj_type ktype_cpufreq = { .release = cpufreq_sysfs_release, }; -struct kobject *cpufreq_global_kobject; -EXPORT_SYMBOL(cpufreq_global_kobject); - -int cpufreq_sysfs_create_file(const struct attribute *attr) -{ - return sysfs_create_file(cpufreq_global_kobject, attr); -} -EXPORT_SYMBOL(cpufreq_sysfs_create_file); - -void cpufreq_sysfs_remove_file(const struct attribute *attr) -{ - sysfs_remove_file(cpufreq_global_kobject, attr); -} -EXPORT_SYMBOL(cpufreq_sysfs_remove_file); - static int add_cpu_dev_symlink(struct cpufreq_policy *policy, int cpu) { struct device *cpu_dev; @@ -2390,7 +2375,7 @@ static int create_boost_sysfs_file(void) if (!cpufreq_driver->set_boost) cpufreq_driver->set_boost = cpufreq_boost_set_sw; - ret = cpufreq_sysfs_create_file(&boost.attr); + ret = sysfs_create_file(cpufreq_global_kobject, &boost.attr); if (ret) pr_err("%s: cannot register global BOOST sysfs file\n", __func__); @@ -2401,7 +2386,7 @@ static int create_boost_sysfs_file(void) static void remove_boost_sysfs_file(void) { if (cpufreq_boost_supported()) - cpufreq_sysfs_remove_file(&boost.attr); + sysfs_remove_file(cpufreq_global_kobject, &boost.attr); } int cpufreq_enable_boost_support(void) @@ -2549,6 +2534,9 @@ static struct syscore_ops cpufreq_syscore_ops = { .shutdown = cpufreq_suspend, }; +struct kobject *cpufreq_global_kobject; +EXPORT_SYMBOL(cpufreq_global_kobject); + static int __init cpufreq_core_init(void) { if (cpufreq_disabled()) diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h index 338bf0e..9623218 100644 --- a/include/linux/cpufreq.h +++ b/include/linux/cpufreq.h @@ -149,8 +149,6 @@ static inline bool policy_is_shared(struct cpufreq_policy *policy) /* /sys/devices/system/cpu/cpufreq: entry point for global variables */ extern struct kobject *cpufreq_global_kobject; -int cpufreq_sysfs_create_file(const struct attribute *attr); -void cpufreq_sysfs_remove_file(const struct attribute *attr); #ifdef CONFIG_CPU_FREQ unsigned int cpufreq_get(unsigned int cpu); -- cgit v0.10.2 From 96bdda61f58b70431bbe8a3e49794c8210f7691b Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 15 Oct 2015 21:35:24 +0530 Subject: cpufreq: create cpu/cpufreq/policyX directories The cpufreq sysfs interface had been a bit inconsistent as one of the CPUs for a policy had a real directory within its sysfs 'cpuX' directory and all other CPUs had links to it. That also made the code a bit complex as we need to take care of moving the sysfs directory if the CPU containing the real directory is getting physically hot-unplugged. Solve this by creating 'policyX' directories (per-policy) in /sys/devices/system/cpu/cpufreq/ directory, where X is the CPU for which the policy was first created. This also removes the need of keeping kobj_cpu and we can remove it now. Suggested-by: Saravana Kannan Signed-off-by: Viresh Kumar Reviewed-by: Saravana Kannan Acked-by: is more of a general agreement from the person that he is Reviewed-by: is a more strict tag and implies that the reviewer has Signed-off-by: Rafael J. Wysocki diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index 04222e7..4fa2215 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -910,9 +910,6 @@ static int cpufreq_add_dev_symlink(struct cpufreq_policy *policy) /* Some related CPUs might not be present (physically hotplugged) */ for_each_cpu(j, policy->real_cpus) { - if (j == policy->kobj_cpu) - continue; - ret = add_cpu_dev_symlink(policy, j); if (ret) break; @@ -926,12 +923,8 @@ static void cpufreq_remove_dev_symlink(struct cpufreq_policy *policy) unsigned int j; /* Some related CPUs might not be present (physically hotplugged) */ - for_each_cpu(j, policy->real_cpus) { - if (j == policy->kobj_cpu) - continue; - + for_each_cpu(j, policy->real_cpus) remove_cpu_dev_symlink(policy, j); - } } static int cpufreq_add_dev_interface(struct cpufreq_policy *policy) @@ -1047,8 +1040,8 @@ static struct cpufreq_policy *cpufreq_policy_alloc(unsigned int cpu) if (!zalloc_cpumask_var(&policy->real_cpus, GFP_KERNEL)) goto err_free_rcpumask; - ret = kobject_init_and_add(&policy->kobj, &ktype_cpufreq, &dev->kobj, - "cpufreq"); + ret = kobject_init_and_add(&policy->kobj, &ktype_cpufreq, + cpufreq_global_kobject, "policy%u", cpu); if (ret) { pr_err("%s: failed to init policy->kobj: %d\n", __func__, ret); goto err_free_real_cpus; @@ -1062,10 +1055,6 @@ static struct cpufreq_policy *cpufreq_policy_alloc(unsigned int cpu) INIT_WORK(&policy->update, handle_update); policy->cpu = cpu; - - /* Set this once on allocation */ - policy->kobj_cpu = cpu; - return policy; err_free_real_cpus: @@ -1417,22 +1406,7 @@ static void cpufreq_remove_dev(struct device *dev, struct subsys_interface *sif) return; } - if (cpu != policy->kobj_cpu) { - remove_cpu_dev_symlink(policy, cpu); - } else { - /* - * The CPU owning the policy object is going away. Move it to - * another suitable CPU. - */ - unsigned int new_cpu = cpumask_first(policy->real_cpus); - struct device *new_dev = get_cpu_device(new_cpu); - - dev_dbg(dev, "%s: Moving policy object to CPU%u\n", __func__, new_cpu); - - sysfs_remove_link(&new_dev->kobj, "cpufreq"); - policy->kobj_cpu = new_cpu; - WARN_ON(kobject_move(&policy->kobj, &new_dev->kobj)); - } + remove_cpu_dev_symlink(policy, cpu); } static void handle_update(struct work_struct *work) diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h index 9623218..ef4c5b1 100644 --- a/include/linux/cpufreq.h +++ b/include/linux/cpufreq.h @@ -65,7 +65,6 @@ struct cpufreq_policy { unsigned int shared_type; /* ACPI: ANY or ALL affected CPUs should set cpufreq */ unsigned int cpu; /* cpu managing this policy, must be online */ - unsigned int kobj_cpu; /* cpu managing sysfs files, can be offline */ struct clk *clk; struct cpufreq_cpuinfo cpuinfo;/* see above */ -- cgit v0.10.2 From 3510fac4549201919c565250fdff5cfa63db9e86 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Fri, 16 Oct 2015 12:41:12 +0530 Subject: cpufreq: postfix policy directory with the first CPU in related_cpus The sysfs policy directory is postfixed currently with the CPU number for which the policy was created, which isn't necessarily the first CPU in related_cpus mask. To make it more consistent and predictable, lets postfix the policy with the first cpu in related-cpus mask. Suggested-by: Saravana Kannan Signed-off-by: Viresh Kumar Reviewed-by: Saravana Kannan Signed-off-by: Rafael J. Wysocki diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index 4fa2215..7c48e73 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -1022,7 +1022,6 @@ static struct cpufreq_policy *cpufreq_policy_alloc(unsigned int cpu) { struct device *dev = get_cpu_device(cpu); struct cpufreq_policy *policy; - int ret; if (WARN_ON(!dev)) return NULL; @@ -1040,13 +1039,7 @@ static struct cpufreq_policy *cpufreq_policy_alloc(unsigned int cpu) if (!zalloc_cpumask_var(&policy->real_cpus, GFP_KERNEL)) goto err_free_rcpumask; - ret = kobject_init_and_add(&policy->kobj, &ktype_cpufreq, - cpufreq_global_kobject, "policy%u", cpu); - if (ret) { - pr_err("%s: failed to init policy->kobj: %d\n", __func__, ret); - goto err_free_real_cpus; - } - + kobject_init(&policy->kobj, &ktype_cpufreq); INIT_LIST_HEAD(&policy->policy_list); init_rwsem(&policy->rwsem); spin_lock_init(&policy->transition_lock); @@ -1057,8 +1050,6 @@ static struct cpufreq_policy *cpufreq_policy_alloc(unsigned int cpu) policy->cpu = cpu; return policy; -err_free_real_cpus: - free_cpumask_var(policy->real_cpus); err_free_rcpumask: free_cpumask_var(policy->related_cpus); err_free_cpumask: @@ -1163,6 +1154,16 @@ static int cpufreq_online(unsigned int cpu) cpumask_copy(policy->related_cpus, policy->cpus); /* Remember CPUs present at the policy creation time. */ cpumask_and(policy->real_cpus, policy->cpus, cpu_present_mask); + + /* Name and add the kobject */ + ret = kobject_add(&policy->kobj, cpufreq_global_kobject, + "policy%u", + cpumask_first(policy->related_cpus)); + if (ret) { + pr_err("%s: failed to add policy->kobj: %d\n", __func__, + ret); + goto out_exit_policy; + } } /* -- cgit v0.10.2