mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2025-04-15 02:37:34 +07:00
Power management and ACPI material for v4.2-rc2
- Fix for an ACPI resources management regression introduced during the 4.1 cycle (that unfortunately went into -stable) effectively reverting the bad commit along with the recent fixups on top of it and using an alternative approach to address the underlying issue (Rafael J Wysocki). - Fix for a memory leak and an incorrect return value in an error code path in the ACPI LPSS (Low-Power Subsystem) driver (Rafael J Wysocki). - Fix for a leftover dangling pointer in an error code path in the new wakeup IRQ support code (Rafael J Wysocki). - Fix to prevent infinite loops (due to errors in other places) from happening in the core generic PM domains support code (Geert Uytterhoeven). - Hibernation documentation update/clarification (Uwe Geuder). - Support for _CLS-based device enumeration in the ACPI core and in the ATA subsystem (Suravee Suthikulpanit). / -----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.22 (GNU/Linux) iQIcBAABCAAGBQJVnZFwAAoJEILEb/54YlRxuysQALZ3y6QAtVFWurwOLXg0Zwoy qoBWlfCRCEKsiLQNe1sDK+dqmfi+ZXvsEqm4y/4uU8dsJdTv1MByaX0aQxP0qmOj Lre9AGNq28JxtTmU2o00qjLW9d9WIQKJn1N51+qqGKTVoqijmEcVlz5xVnqoJmBQ s0Af2kAzG7CLFtNzPI6RbOPMm7S0VEiI/WQhKZXWqGheppfDaRG+wl0pKPv95G73 7wCG+yklSi4j5u2Aox/x1samOy+c+S/l2VgU+Decnv0ccPlChYBCMOzWSJuFn5v7 qVEvUrFYc2ItQElzaqTWc1uEPzf0yg/S4pWJjLBADpDAUBQNXExmaQ2wLlzgR00/ K/cGUqCn0APcw4tjZjVlGvTHmJg6ivdeceQuLdgxGoGgvph9V9JlHD9zmzFLqAXl 3VCJiuHnT9QyfEcAkOhZUvO5WcX892fNpHYe/riLQOll9ODwoWCr+TofGfIvHKZp 1XQ1r68rBpsZnnb5JpYT8F/LgaKa4nWZboLroFWi9SZ0jYdH4U7k88C2bCYpaZGz S07l4GEICSuTPHpQNzshzYw6lWJqoXPFQ5okqnZxwNDWAE30ZII0LQwasuupmuR4 S1fFi30YTI23tiDEZRWgVWbMPD++jZdQoflBZq5Lo8s4EfPD5yS+LtxACzktDlM0 fq6bcUUO85Weo+zNxEa3 =qomE -----END PGP SIGNATURE----- Merge tag 'pm+acpi-4.2-rc2' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm Pull power management and ACPI updates from Rafael Wysocki: "These are fixes on top of the previous PM+ACPI pull requests (including one fix for a 4.1 regression) and two commits adding _CLS-based device enumeration support to the ACPI core and the ATA subsystem that waited for the latest ACPICA changes to be merged. Specifics: - Fix for an ACPI resources management regression introduced during the 4.1 cycle (that unfortunately went into -stable) effectively reverting the bad commit along with the recent fixups on top of it and using an alternative approach to address the underlying issue (Rafael J Wysocki). - Fix for a memory leak and an incorrect return value in an error code path in the ACPI LPSS (Low-Power Subsystem) driver (Rafael J Wysocki). - Fix for a leftover dangling pointer in an error code path in the new wakeup IRQ support code (Rafael J Wysocki). - Fix to prevent infinite loops (due to errors in other places) from happening in the core generic PM domains support code (Geert Uytterhoeven). - Hibernation documentation update/clarification (Uwe Geuder). - Support for _CLS-based device enumeration in the ACPI core and in the ATA subsystem (Suravee Suthikulpanit)" * tag 'pm+acpi-4.2-rc2' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm: PM / wakeirq: Avoid setting power.wakeirq too hastily ata: ahci_platform: Add ACPI _CLS matching ACPI / scan: Add support for ACPI _CLS device matching PM / hibernate: clarify resume documentation PM / Domains: Avoid infinite loops in attach/detach code ACPI / LPSS: Fix up acpi_lpss_create_device() ACPI / PNP: Reserve ACPI resources at the fs_initcall_sync stage
This commit is contained in:
commit
883a2dfd6f
@ -410,8 +410,17 @@ Documentation/usb/persist.txt.
|
|||||||
|
|
||||||
Q: Can I suspend-to-disk using a swap partition under LVM?
|
Q: Can I suspend-to-disk using a swap partition under LVM?
|
||||||
|
|
||||||
A: No. You can suspend successfully, but you'll not be able to
|
A: Yes and No. You can suspend successfully, but the kernel will not be able
|
||||||
resume. uswsusp should be able to work with LVM. See suspend.sf.net.
|
to resume on its own. You need an initramfs that can recognize the resume
|
||||||
|
situation, activate the logical volume containing the swap volume (but not
|
||||||
|
touch any filesystems!), and eventually call
|
||||||
|
|
||||||
|
echo -n "$major:$minor" > /sys/power/resume
|
||||||
|
|
||||||
|
where $major and $minor are the respective major and minor device numbers of
|
||||||
|
the swap volume.
|
||||||
|
|
||||||
|
uswsusp works with LVM, too. See http://suspend.sourceforge.net/
|
||||||
|
|
||||||
Q: I upgraded the kernel from 2.6.15 to 2.6.16. Both kernels were
|
Q: I upgraded the kernel from 2.6.15 to 2.6.16. Both kernels were
|
||||||
compiled with the similar configuration files. Anyway I found that
|
compiled with the similar configuration files. Anyway I found that
|
||||||
|
@ -352,13 +352,16 @@ static int acpi_lpss_create_device(struct acpi_device *adev,
|
|||||||
pdata->mmio_size = resource_size(rentry->res);
|
pdata->mmio_size = resource_size(rentry->res);
|
||||||
pdata->mmio_base = ioremap(rentry->res->start,
|
pdata->mmio_base = ioremap(rentry->res->start,
|
||||||
pdata->mmio_size);
|
pdata->mmio_size);
|
||||||
if (!pdata->mmio_base)
|
|
||||||
goto err_out;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
acpi_dev_free_resource_list(&resource_list);
|
acpi_dev_free_resource_list(&resource_list);
|
||||||
|
|
||||||
|
if (!pdata->mmio_base) {
|
||||||
|
ret = -ENOMEM;
|
||||||
|
goto err_out;
|
||||||
|
}
|
||||||
|
|
||||||
pdata->dev_desc = dev_desc;
|
pdata->dev_desc = dev_desc;
|
||||||
|
|
||||||
if (dev_desc->setup)
|
if (dev_desc->setup)
|
||||||
|
@ -175,10 +175,14 @@ static void __init acpi_request_region (struct acpi_generic_address *gas,
|
|||||||
if (!addr || !length)
|
if (!addr || !length)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
acpi_reserve_region(addr, length, gas->space_id, 0, desc);
|
/* Resources are never freed */
|
||||||
|
if (gas->space_id == ACPI_ADR_SPACE_SYSTEM_IO)
|
||||||
|
request_region(addr, length, desc);
|
||||||
|
else if (gas->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY)
|
||||||
|
request_mem_region(addr, length, desc);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void __init acpi_reserve_resources(void)
|
static int __init acpi_reserve_resources(void)
|
||||||
{
|
{
|
||||||
acpi_request_region(&acpi_gbl_FADT.xpm1a_event_block, acpi_gbl_FADT.pm1_event_length,
|
acpi_request_region(&acpi_gbl_FADT.xpm1a_event_block, acpi_gbl_FADT.pm1_event_length,
|
||||||
"ACPI PM1a_EVT_BLK");
|
"ACPI PM1a_EVT_BLK");
|
||||||
@ -207,7 +211,10 @@ static void __init acpi_reserve_resources(void)
|
|||||||
if (!(acpi_gbl_FADT.gpe1_block_length & 0x1))
|
if (!(acpi_gbl_FADT.gpe1_block_length & 0x1))
|
||||||
acpi_request_region(&acpi_gbl_FADT.xgpe1_block,
|
acpi_request_region(&acpi_gbl_FADT.xgpe1_block,
|
||||||
acpi_gbl_FADT.gpe1_block_length, "ACPI GPE1_BLK");
|
acpi_gbl_FADT.gpe1_block_length, "ACPI GPE1_BLK");
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
fs_initcall_sync(acpi_reserve_resources);
|
||||||
|
|
||||||
void acpi_os_printf(const char *fmt, ...)
|
void acpi_os_printf(const char *fmt, ...)
|
||||||
{
|
{
|
||||||
@ -1862,7 +1869,6 @@ acpi_status __init acpi_os_initialize(void)
|
|||||||
|
|
||||||
acpi_status __init acpi_os_initialize1(void)
|
acpi_status __init acpi_os_initialize1(void)
|
||||||
{
|
{
|
||||||
acpi_reserve_resources();
|
|
||||||
kacpid_wq = alloc_workqueue("kacpid", 0, 1);
|
kacpid_wq = alloc_workqueue("kacpid", 0, 1);
|
||||||
kacpi_notify_wq = alloc_workqueue("kacpi_notify", 0, 1);
|
kacpi_notify_wq = alloc_workqueue("kacpi_notify", 0, 1);
|
||||||
kacpi_hotplug_wq = alloc_ordered_workqueue("kacpi_hotplug", 0);
|
kacpi_hotplug_wq = alloc_ordered_workqueue("kacpi_hotplug", 0);
|
||||||
|
@ -26,7 +26,6 @@
|
|||||||
#include <linux/device.h>
|
#include <linux/device.h>
|
||||||
#include <linux/export.h>
|
#include <linux/export.h>
|
||||||
#include <linux/ioport.h>
|
#include <linux/ioport.h>
|
||||||
#include <linux/list.h>
|
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
|
|
||||||
#ifdef CONFIG_X86
|
#ifdef CONFIG_X86
|
||||||
@ -622,164 +621,3 @@ int acpi_dev_filter_resource_type(struct acpi_resource *ares,
|
|||||||
return (type & types) ? 0 : 1;
|
return (type & types) ? 0 : 1;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(acpi_dev_filter_resource_type);
|
EXPORT_SYMBOL_GPL(acpi_dev_filter_resource_type);
|
||||||
|
|
||||||
struct reserved_region {
|
|
||||||
struct list_head node;
|
|
||||||
u64 start;
|
|
||||||
u64 end;
|
|
||||||
};
|
|
||||||
|
|
||||||
static LIST_HEAD(reserved_io_regions);
|
|
||||||
static LIST_HEAD(reserved_mem_regions);
|
|
||||||
|
|
||||||
static int request_range(u64 start, u64 end, u8 space_id, unsigned long flags,
|
|
||||||
char *desc)
|
|
||||||
{
|
|
||||||
unsigned int length = end - start + 1;
|
|
||||||
struct resource *res;
|
|
||||||
|
|
||||||
res = space_id == ACPI_ADR_SPACE_SYSTEM_IO ?
|
|
||||||
request_region(start, length, desc) :
|
|
||||||
request_mem_region(start, length, desc);
|
|
||||||
if (!res)
|
|
||||||
return -EIO;
|
|
||||||
|
|
||||||
res->flags &= ~flags;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int add_region_before(u64 start, u64 end, u8 space_id,
|
|
||||||
unsigned long flags, char *desc,
|
|
||||||
struct list_head *head)
|
|
||||||
{
|
|
||||||
struct reserved_region *reg;
|
|
||||||
int error;
|
|
||||||
|
|
||||||
reg = kmalloc(sizeof(*reg), GFP_KERNEL);
|
|
||||||
if (!reg)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
error = request_range(start, end, space_id, flags, desc);
|
|
||||||
if (error) {
|
|
||||||
kfree(reg);
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
|
|
||||||
reg->start = start;
|
|
||||||
reg->end = end;
|
|
||||||
list_add_tail(®->node, head);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* acpi_reserve_region - Reserve an I/O or memory region as a system resource.
|
|
||||||
* @start: Starting address of the region.
|
|
||||||
* @length: Length of the region.
|
|
||||||
* @space_id: Identifier of address space to reserve the region from.
|
|
||||||
* @flags: Resource flags to clear for the region after requesting it.
|
|
||||||
* @desc: Region description (for messages).
|
|
||||||
*
|
|
||||||
* Reserve an I/O or memory region as a system resource to prevent others from
|
|
||||||
* using it. If the new region overlaps with one of the regions (in the given
|
|
||||||
* address space) already reserved by this routine, only the non-overlapping
|
|
||||||
* parts of it will be reserved.
|
|
||||||
*
|
|
||||||
* Returned is either 0 (success) or a negative error code indicating a resource
|
|
||||||
* reservation problem. It is the code of the first encountered error, but the
|
|
||||||
* routine doesn't abort until it has attempted to request all of the parts of
|
|
||||||
* the new region that don't overlap with other regions reserved previously.
|
|
||||||
*
|
|
||||||
* The resources requested by this routine are never released.
|
|
||||||
*/
|
|
||||||
int acpi_reserve_region(u64 start, unsigned int length, u8 space_id,
|
|
||||||
unsigned long flags, char *desc)
|
|
||||||
{
|
|
||||||
struct list_head *regions;
|
|
||||||
struct reserved_region *reg;
|
|
||||||
u64 end = start + length - 1;
|
|
||||||
int ret = 0, error = 0;
|
|
||||||
|
|
||||||
if (space_id == ACPI_ADR_SPACE_SYSTEM_IO)
|
|
||||||
regions = &reserved_io_regions;
|
|
||||||
else if (space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY)
|
|
||||||
regions = &reserved_mem_regions;
|
|
||||||
else
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
if (list_empty(regions))
|
|
||||||
return add_region_before(start, end, space_id, flags, desc, regions);
|
|
||||||
|
|
||||||
list_for_each_entry(reg, regions, node)
|
|
||||||
if (reg->start == end + 1) {
|
|
||||||
/* The new region can be prepended to this one. */
|
|
||||||
ret = request_range(start, end, space_id, flags, desc);
|
|
||||||
if (!ret)
|
|
||||||
reg->start = start;
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
} else if (reg->start > end) {
|
|
||||||
/* No overlap. Add the new region here and get out. */
|
|
||||||
return add_region_before(start, end, space_id, flags,
|
|
||||||
desc, ®->node);
|
|
||||||
} else if (reg->end == start - 1) {
|
|
||||||
goto combine;
|
|
||||||
} else if (reg->end >= start) {
|
|
||||||
goto overlap;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* The new region goes after the last existing one. */
|
|
||||||
return add_region_before(start, end, space_id, flags, desc, regions);
|
|
||||||
|
|
||||||
overlap:
|
|
||||||
/*
|
|
||||||
* The new region overlaps an existing one.
|
|
||||||
*
|
|
||||||
* The head part of the new region immediately preceding the existing
|
|
||||||
* overlapping one can be combined with it right away.
|
|
||||||
*/
|
|
||||||
if (reg->start > start) {
|
|
||||||
error = request_range(start, reg->start - 1, space_id, flags, desc);
|
|
||||||
if (error)
|
|
||||||
ret = error;
|
|
||||||
else
|
|
||||||
reg->start = start;
|
|
||||||
}
|
|
||||||
|
|
||||||
combine:
|
|
||||||
/*
|
|
||||||
* The new region is adjacent to an existing one. If it extends beyond
|
|
||||||
* that region all the way to the next one, it is possible to combine
|
|
||||||
* all three of them.
|
|
||||||
*/
|
|
||||||
while (reg->end < end) {
|
|
||||||
struct reserved_region *next = NULL;
|
|
||||||
u64 a = reg->end + 1, b = end;
|
|
||||||
|
|
||||||
if (!list_is_last(®->node, regions)) {
|
|
||||||
next = list_next_entry(reg, node);
|
|
||||||
if (next->start <= end)
|
|
||||||
b = next->start - 1;
|
|
||||||
}
|
|
||||||
error = request_range(a, b, space_id, flags, desc);
|
|
||||||
if (!error) {
|
|
||||||
if (next && next->start == b + 1) {
|
|
||||||
reg->end = next->end;
|
|
||||||
list_del(&next->node);
|
|
||||||
kfree(next);
|
|
||||||
} else {
|
|
||||||
reg->end = end;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else if (next) {
|
|
||||||
if (!ret)
|
|
||||||
ret = error;
|
|
||||||
|
|
||||||
reg = next;
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret ? ret : error;
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL_GPL(acpi_reserve_region);
|
|
||||||
|
@ -1019,6 +1019,29 @@ static bool acpi_of_match_device(struct acpi_device *adev,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool __acpi_match_device_cls(const struct acpi_device_id *id,
|
||||||
|
struct acpi_hardware_id *hwid)
|
||||||
|
{
|
||||||
|
int i, msk, byte_shift;
|
||||||
|
char buf[3];
|
||||||
|
|
||||||
|
if (!id->cls)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/* Apply class-code bitmask, before checking each class-code byte */
|
||||||
|
for (i = 1; i <= 3; i++) {
|
||||||
|
byte_shift = 8 * (3 - i);
|
||||||
|
msk = (id->cls_msk >> byte_shift) & 0xFF;
|
||||||
|
if (!msk)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
sprintf(buf, "%02x", (id->cls >> byte_shift) & msk);
|
||||||
|
if (strncmp(buf, &hwid->id[(i - 1) * 2], 2))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
static const struct acpi_device_id *__acpi_match_device(
|
static const struct acpi_device_id *__acpi_match_device(
|
||||||
struct acpi_device *device,
|
struct acpi_device *device,
|
||||||
const struct acpi_device_id *ids,
|
const struct acpi_device_id *ids,
|
||||||
@ -1036,9 +1059,12 @@ static const struct acpi_device_id *__acpi_match_device(
|
|||||||
|
|
||||||
list_for_each_entry(hwid, &device->pnp.ids, list) {
|
list_for_each_entry(hwid, &device->pnp.ids, list) {
|
||||||
/* First, check the ACPI/PNP IDs provided by the caller. */
|
/* First, check the ACPI/PNP IDs provided by the caller. */
|
||||||
for (id = ids; id->id[0]; id++)
|
for (id = ids; id->id[0] || id->cls; id++) {
|
||||||
if (!strcmp((char *) id->id, hwid->id))
|
if (id->id[0] && !strcmp((char *) id->id, hwid->id))
|
||||||
return id;
|
return id;
|
||||||
|
else if (id->cls && __acpi_match_device_cls(id, hwid))
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Next, check ACPI_DT_NAMESPACE_HID and try to match the
|
* Next, check ACPI_DT_NAMESPACE_HID and try to match the
|
||||||
@ -2101,6 +2127,8 @@ static void acpi_set_pnp_ids(acpi_handle handle, struct acpi_device_pnp *pnp,
|
|||||||
if (info->valid & ACPI_VALID_UID)
|
if (info->valid & ACPI_VALID_UID)
|
||||||
pnp->unique_id = kstrdup(info->unique_id.string,
|
pnp->unique_id = kstrdup(info->unique_id.string,
|
||||||
GFP_KERNEL);
|
GFP_KERNEL);
|
||||||
|
if (info->valid & ACPI_VALID_CLS)
|
||||||
|
acpi_add_id(pnp, info->class_code.string);
|
||||||
|
|
||||||
kfree(info);
|
kfree(info);
|
||||||
|
|
||||||
|
@ -48,7 +48,7 @@ config ATA_VERBOSE_ERROR
|
|||||||
|
|
||||||
config ATA_ACPI
|
config ATA_ACPI
|
||||||
bool "ATA ACPI Support"
|
bool "ATA ACPI Support"
|
||||||
depends on ACPI && PCI
|
depends on ACPI
|
||||||
default y
|
default y
|
||||||
help
|
help
|
||||||
This option adds support for ATA-related ACPI objects.
|
This option adds support for ATA-related ACPI objects.
|
||||||
|
@ -20,6 +20,8 @@
|
|||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <linux/libata.h>
|
#include <linux/libata.h>
|
||||||
#include <linux/ahci_platform.h>
|
#include <linux/ahci_platform.h>
|
||||||
|
#include <linux/acpi.h>
|
||||||
|
#include <linux/pci_ids.h>
|
||||||
#include "ahci.h"
|
#include "ahci.h"
|
||||||
|
|
||||||
#define DRV_NAME "ahci"
|
#define DRV_NAME "ahci"
|
||||||
@ -79,12 +81,19 @@ static const struct of_device_id ahci_of_match[] = {
|
|||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(of, ahci_of_match);
|
MODULE_DEVICE_TABLE(of, ahci_of_match);
|
||||||
|
|
||||||
|
static const struct acpi_device_id ahci_acpi_match[] = {
|
||||||
|
{ ACPI_DEVICE_CLASS(PCI_CLASS_STORAGE_SATA_AHCI, 0xffffff) },
|
||||||
|
{},
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(acpi, ahci_acpi_match);
|
||||||
|
|
||||||
static struct platform_driver ahci_driver = {
|
static struct platform_driver ahci_driver = {
|
||||||
.probe = ahci_probe,
|
.probe = ahci_probe,
|
||||||
.remove = ata_platform_remove_one,
|
.remove = ata_platform_remove_one,
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = DRV_NAME,
|
.name = DRV_NAME,
|
||||||
.of_match_table = ahci_of_match,
|
.of_match_table = ahci_of_match,
|
||||||
|
.acpi_match_table = ahci_acpi_match,
|
||||||
.pm = &ahci_pm_ops,
|
.pm = &ahci_pm_ops,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
* This file is released under the GPLv2.
|
* This file is released under the GPLv2.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <linux/delay.h>
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
@ -19,6 +20,8 @@
|
|||||||
#include <linux/suspend.h>
|
#include <linux/suspend.h>
|
||||||
#include <linux/export.h>
|
#include <linux/export.h>
|
||||||
|
|
||||||
|
#define GENPD_RETRY_MAX_MS 250 /* Approximate */
|
||||||
|
|
||||||
#define GENPD_DEV_CALLBACK(genpd, type, callback, dev) \
|
#define GENPD_DEV_CALLBACK(genpd, type, callback, dev) \
|
||||||
({ \
|
({ \
|
||||||
type (*__routine)(struct device *__d); \
|
type (*__routine)(struct device *__d); \
|
||||||
@ -2131,6 +2134,7 @@ EXPORT_SYMBOL_GPL(of_genpd_get_from_provider);
|
|||||||
static void genpd_dev_pm_detach(struct device *dev, bool power_off)
|
static void genpd_dev_pm_detach(struct device *dev, bool power_off)
|
||||||
{
|
{
|
||||||
struct generic_pm_domain *pd;
|
struct generic_pm_domain *pd;
|
||||||
|
unsigned int i;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
pd = pm_genpd_lookup_dev(dev);
|
pd = pm_genpd_lookup_dev(dev);
|
||||||
@ -2139,10 +2143,12 @@ static void genpd_dev_pm_detach(struct device *dev, bool power_off)
|
|||||||
|
|
||||||
dev_dbg(dev, "removing from PM domain %s\n", pd->name);
|
dev_dbg(dev, "removing from PM domain %s\n", pd->name);
|
||||||
|
|
||||||
while (1) {
|
for (i = 1; i < GENPD_RETRY_MAX_MS; i <<= 1) {
|
||||||
ret = pm_genpd_remove_device(pd, dev);
|
ret = pm_genpd_remove_device(pd, dev);
|
||||||
if (ret != -EAGAIN)
|
if (ret != -EAGAIN)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
mdelay(i);
|
||||||
cond_resched();
|
cond_resched();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2183,6 +2189,7 @@ int genpd_dev_pm_attach(struct device *dev)
|
|||||||
{
|
{
|
||||||
struct of_phandle_args pd_args;
|
struct of_phandle_args pd_args;
|
||||||
struct generic_pm_domain *pd;
|
struct generic_pm_domain *pd;
|
||||||
|
unsigned int i;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (!dev->of_node)
|
if (!dev->of_node)
|
||||||
@ -2218,10 +2225,12 @@ int genpd_dev_pm_attach(struct device *dev)
|
|||||||
|
|
||||||
dev_dbg(dev, "adding to PM domain %s\n", pd->name);
|
dev_dbg(dev, "adding to PM domain %s\n", pd->name);
|
||||||
|
|
||||||
while (1) {
|
for (i = 1; i < GENPD_RETRY_MAX_MS; i <<= 1) {
|
||||||
ret = pm_genpd_add_device(pd, dev);
|
ret = pm_genpd_add_device(pd, dev);
|
||||||
if (ret != -EAGAIN)
|
if (ret != -EAGAIN)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
mdelay(i);
|
||||||
cond_resched();
|
cond_resched();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,14 +45,12 @@ static int dev_pm_attach_wake_irq(struct device *dev, int irq,
|
|||||||
return -EEXIST;
|
return -EEXIST;
|
||||||
}
|
}
|
||||||
|
|
||||||
dev->power.wakeirq = wirq;
|
|
||||||
spin_unlock_irqrestore(&dev->power.lock, flags);
|
|
||||||
|
|
||||||
err = device_wakeup_attach_irq(dev, wirq);
|
err = device_wakeup_attach_irq(dev, wirq);
|
||||||
if (err)
|
if (!err)
|
||||||
return err;
|
dev->power.wakeirq = wirq;
|
||||||
|
|
||||||
return 0;
|
spin_unlock_irqrestore(&dev->power.lock, flags);
|
||||||
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -105,10 +103,10 @@ void dev_pm_clear_wake_irq(struct device *dev)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
spin_lock_irqsave(&dev->power.lock, flags);
|
spin_lock_irqsave(&dev->power.lock, flags);
|
||||||
|
device_wakeup_detach_irq(dev);
|
||||||
dev->power.wakeirq = NULL;
|
dev->power.wakeirq = NULL;
|
||||||
spin_unlock_irqrestore(&dev->power.lock, flags);
|
spin_unlock_irqrestore(&dev->power.lock, flags);
|
||||||
|
|
||||||
device_wakeup_detach_irq(dev);
|
|
||||||
if (wirq->dedicated_irq)
|
if (wirq->dedicated_irq)
|
||||||
free_irq(wirq->irq, wirq);
|
free_irq(wirq->irq, wirq);
|
||||||
kfree(wirq);
|
kfree(wirq);
|
||||||
|
@ -281,32 +281,25 @@ EXPORT_SYMBOL_GPL(device_wakeup_enable);
|
|||||||
* Attach a device wakeirq to the wakeup source so the device
|
* Attach a device wakeirq to the wakeup source so the device
|
||||||
* wake IRQ can be configured automatically for suspend and
|
* wake IRQ can be configured automatically for suspend and
|
||||||
* resume.
|
* resume.
|
||||||
|
*
|
||||||
|
* Call under the device's power.lock lock.
|
||||||
*/
|
*/
|
||||||
int device_wakeup_attach_irq(struct device *dev,
|
int device_wakeup_attach_irq(struct device *dev,
|
||||||
struct wake_irq *wakeirq)
|
struct wake_irq *wakeirq)
|
||||||
{
|
{
|
||||||
struct wakeup_source *ws;
|
struct wakeup_source *ws;
|
||||||
int ret = 0;
|
|
||||||
|
|
||||||
spin_lock_irq(&dev->power.lock);
|
|
||||||
ws = dev->power.wakeup;
|
ws = dev->power.wakeup;
|
||||||
if (!ws) {
|
if (!ws) {
|
||||||
dev_err(dev, "forgot to call call device_init_wakeup?\n");
|
dev_err(dev, "forgot to call call device_init_wakeup?\n");
|
||||||
ret = -EINVAL;
|
return -EINVAL;
|
||||||
goto unlock;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ws->wakeirq) {
|
if (ws->wakeirq)
|
||||||
ret = -EEXIST;
|
return -EEXIST;
|
||||||
goto unlock;
|
|
||||||
}
|
|
||||||
|
|
||||||
ws->wakeirq = wakeirq;
|
ws->wakeirq = wakeirq;
|
||||||
|
return 0;
|
||||||
unlock:
|
|
||||||
spin_unlock_irq(&dev->power.lock);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -314,20 +307,16 @@ int device_wakeup_attach_irq(struct device *dev,
|
|||||||
* @dev: Device to handle
|
* @dev: Device to handle
|
||||||
*
|
*
|
||||||
* Removes a device wakeirq from the wakeup source.
|
* Removes a device wakeirq from the wakeup source.
|
||||||
|
*
|
||||||
|
* Call under the device's power.lock lock.
|
||||||
*/
|
*/
|
||||||
void device_wakeup_detach_irq(struct device *dev)
|
void device_wakeup_detach_irq(struct device *dev)
|
||||||
{
|
{
|
||||||
struct wakeup_source *ws;
|
struct wakeup_source *ws;
|
||||||
|
|
||||||
spin_lock_irq(&dev->power.lock);
|
|
||||||
ws = dev->power.wakeup;
|
ws = dev->power.wakeup;
|
||||||
if (!ws)
|
if (ws)
|
||||||
goto unlock;
|
|
||||||
|
|
||||||
ws->wakeirq = NULL;
|
ws->wakeirq = NULL;
|
||||||
|
|
||||||
unlock:
|
|
||||||
spin_unlock_irq(&dev->power.lock);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -7,7 +7,6 @@
|
|||||||
* Bjorn Helgaas <bjorn.helgaas@hp.com>
|
* Bjorn Helgaas <bjorn.helgaas@hp.com>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/acpi.h>
|
|
||||||
#include <linux/pnp.h>
|
#include <linux/pnp.h>
|
||||||
#include <linux/device.h>
|
#include <linux/device.h>
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
@ -23,41 +22,25 @@ static const struct pnp_device_id pnp_dev_table[] = {
|
|||||||
{"", 0}
|
{"", 0}
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifdef CONFIG_ACPI
|
|
||||||
static bool __reserve_range(u64 start, unsigned int length, bool io, char *desc)
|
|
||||||
{
|
|
||||||
u8 space_id = io ? ACPI_ADR_SPACE_SYSTEM_IO : ACPI_ADR_SPACE_SYSTEM_MEMORY;
|
|
||||||
return !acpi_reserve_region(start, length, space_id, IORESOURCE_BUSY, desc);
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
static bool __reserve_range(u64 start, unsigned int length, bool io, char *desc)
|
|
||||||
{
|
|
||||||
struct resource *res;
|
|
||||||
|
|
||||||
res = io ? request_region(start, length, desc) :
|
|
||||||
request_mem_region(start, length, desc);
|
|
||||||
if (res) {
|
|
||||||
res->flags &= ~IORESOURCE_BUSY;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static void reserve_range(struct pnp_dev *dev, struct resource *r, int port)
|
static void reserve_range(struct pnp_dev *dev, struct resource *r, int port)
|
||||||
{
|
{
|
||||||
char *regionid;
|
char *regionid;
|
||||||
const char *pnpid = dev_name(&dev->dev);
|
const char *pnpid = dev_name(&dev->dev);
|
||||||
resource_size_t start = r->start, end = r->end;
|
resource_size_t start = r->start, end = r->end;
|
||||||
bool reserved;
|
struct resource *res;
|
||||||
|
|
||||||
regionid = kmalloc(16, GFP_KERNEL);
|
regionid = kmalloc(16, GFP_KERNEL);
|
||||||
if (!regionid)
|
if (!regionid)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
snprintf(regionid, 16, "pnp %s", pnpid);
|
snprintf(regionid, 16, "pnp %s", pnpid);
|
||||||
reserved = __reserve_range(start, end - start + 1, !!port, regionid);
|
if (port)
|
||||||
if (!reserved)
|
res = request_region(start, end - start + 1, regionid);
|
||||||
|
else
|
||||||
|
res = request_mem_region(start, end - start + 1, regionid);
|
||||||
|
if (res)
|
||||||
|
res->flags &= ~IORESOURCE_BUSY;
|
||||||
|
else
|
||||||
kfree(regionid);
|
kfree(regionid);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -66,7 +49,7 @@ static void reserve_range(struct pnp_dev *dev, struct resource *r, int port)
|
|||||||
* have double reservations.
|
* have double reservations.
|
||||||
*/
|
*/
|
||||||
dev_info(&dev->dev, "%pR %s reserved\n", r,
|
dev_info(&dev->dev, "%pR %s reserved\n", r,
|
||||||
reserved ? "has been" : "could not be");
|
res ? "has been" : "could not be");
|
||||||
}
|
}
|
||||||
|
|
||||||
static void reserve_resources_of_dev(struct pnp_dev *dev)
|
static void reserve_resources_of_dev(struct pnp_dev *dev)
|
||||||
|
@ -58,6 +58,19 @@ static inline acpi_handle acpi_device_handle(struct acpi_device *adev)
|
|||||||
acpi_fwnode_handle(adev) : NULL)
|
acpi_fwnode_handle(adev) : NULL)
|
||||||
#define ACPI_HANDLE(dev) acpi_device_handle(ACPI_COMPANION(dev))
|
#define ACPI_HANDLE(dev) acpi_device_handle(ACPI_COMPANION(dev))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ACPI_DEVICE_CLASS - macro used to describe an ACPI device with
|
||||||
|
* the PCI-defined class-code information
|
||||||
|
*
|
||||||
|
* @_cls : the class, subclass, prog-if triple for this device
|
||||||
|
* @_msk : the class mask for this device
|
||||||
|
*
|
||||||
|
* This macro is used to create a struct acpi_device_id that matches a
|
||||||
|
* specific PCI class. The .id and .driver_data fields will be left
|
||||||
|
* initialized with the default value.
|
||||||
|
*/
|
||||||
|
#define ACPI_DEVICE_CLASS(_cls, _msk) .cls = (_cls), .cls_msk = (_msk),
|
||||||
|
|
||||||
static inline bool has_acpi_companion(struct device *dev)
|
static inline bool has_acpi_companion(struct device *dev)
|
||||||
{
|
{
|
||||||
return is_acpi_node(dev->fwnode);
|
return is_acpi_node(dev->fwnode);
|
||||||
@ -309,9 +322,6 @@ int acpi_check_region(resource_size_t start, resource_size_t n,
|
|||||||
|
|
||||||
int acpi_resources_are_enforced(void);
|
int acpi_resources_are_enforced(void);
|
||||||
|
|
||||||
int acpi_reserve_region(u64 start, unsigned int length, u8 space_id,
|
|
||||||
unsigned long flags, char *desc);
|
|
||||||
|
|
||||||
#ifdef CONFIG_HIBERNATION
|
#ifdef CONFIG_HIBERNATION
|
||||||
void __init acpi_no_s4_hw_signature(void);
|
void __init acpi_no_s4_hw_signature(void);
|
||||||
#endif
|
#endif
|
||||||
@ -446,6 +456,7 @@ struct platform_device *acpi_create_platform_device(struct acpi_device *);
|
|||||||
#define ACPI_COMPANION(dev) (NULL)
|
#define ACPI_COMPANION(dev) (NULL)
|
||||||
#define ACPI_COMPANION_SET(dev, adev) do { } while (0)
|
#define ACPI_COMPANION_SET(dev, adev) do { } while (0)
|
||||||
#define ACPI_HANDLE(dev) (NULL)
|
#define ACPI_HANDLE(dev) (NULL)
|
||||||
|
#define ACPI_DEVICE_CLASS(_cls, _msk) .cls = (0), .cls_msk = (0),
|
||||||
|
|
||||||
struct fwnode_handle;
|
struct fwnode_handle;
|
||||||
|
|
||||||
@ -507,13 +518,6 @@ static inline int acpi_check_region(resource_size_t start, resource_size_t n,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int acpi_reserve_region(u64 start, unsigned int length,
|
|
||||||
u8 space_id, unsigned long flags,
|
|
||||||
char *desc)
|
|
||||||
{
|
|
||||||
return -ENXIO;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct acpi_table_header;
|
struct acpi_table_header;
|
||||||
static inline int acpi_table_parse(char *id,
|
static inline int acpi_table_parse(char *id,
|
||||||
int (*handler)(struct acpi_table_header *))
|
int (*handler)(struct acpi_table_header *))
|
||||||
|
@ -189,6 +189,8 @@ struct css_device_id {
|
|||||||
struct acpi_device_id {
|
struct acpi_device_id {
|
||||||
__u8 id[ACPI_ID_LEN];
|
__u8 id[ACPI_ID_LEN];
|
||||||
kernel_ulong_t driver_data;
|
kernel_ulong_t driver_data;
|
||||||
|
__u32 cls;
|
||||||
|
__u32 cls_msk;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define PNP_ID_LEN 8
|
#define PNP_ID_LEN 8
|
||||||
|
@ -63,6 +63,8 @@ int main(void)
|
|||||||
|
|
||||||
DEVID(acpi_device_id);
|
DEVID(acpi_device_id);
|
||||||
DEVID_FIELD(acpi_device_id, id);
|
DEVID_FIELD(acpi_device_id, id);
|
||||||
|
DEVID_FIELD(acpi_device_id, cls);
|
||||||
|
DEVID_FIELD(acpi_device_id, cls_msk);
|
||||||
|
|
||||||
DEVID(pnp_device_id);
|
DEVID(pnp_device_id);
|
||||||
DEVID_FIELD(pnp_device_id, id);
|
DEVID_FIELD(pnp_device_id, id);
|
||||||
|
@ -523,12 +523,40 @@ static int do_serio_entry(const char *filename,
|
|||||||
}
|
}
|
||||||
ADD_TO_DEVTABLE("serio", serio_device_id, do_serio_entry);
|
ADD_TO_DEVTABLE("serio", serio_device_id, do_serio_entry);
|
||||||
|
|
||||||
/* looks like: "acpi:ACPI0003 or acpi:PNP0C0B" or "acpi:LNXVIDEO" */
|
/* looks like: "acpi:ACPI0003" or "acpi:PNP0C0B" or "acpi:LNXVIDEO" or
|
||||||
|
* "acpi:bbsspp" (bb=base-class, ss=sub-class, pp=prog-if)
|
||||||
|
*
|
||||||
|
* NOTE: Each driver should use one of the following : _HID, _CIDs
|
||||||
|
* or _CLS. Also, bb, ss, and pp can be substituted with ??
|
||||||
|
* as don't care byte.
|
||||||
|
*/
|
||||||
static int do_acpi_entry(const char *filename,
|
static int do_acpi_entry(const char *filename,
|
||||||
void *symval, char *alias)
|
void *symval, char *alias)
|
||||||
{
|
{
|
||||||
DEF_FIELD_ADDR(symval, acpi_device_id, id);
|
DEF_FIELD_ADDR(symval, acpi_device_id, id);
|
||||||
|
DEF_FIELD_ADDR(symval, acpi_device_id, cls);
|
||||||
|
DEF_FIELD_ADDR(symval, acpi_device_id, cls_msk);
|
||||||
|
|
||||||
|
if (id && strlen((const char *)*id))
|
||||||
sprintf(alias, "acpi*:%s:*", *id);
|
sprintf(alias, "acpi*:%s:*", *id);
|
||||||
|
else if (cls) {
|
||||||
|
int i, byte_shift, cnt = 0;
|
||||||
|
unsigned int msk;
|
||||||
|
|
||||||
|
sprintf(&alias[cnt], "acpi*:");
|
||||||
|
cnt = 6;
|
||||||
|
for (i = 1; i <= 3; i++) {
|
||||||
|
byte_shift = 8 * (3-i);
|
||||||
|
msk = (*cls_msk >> byte_shift) & 0xFF;
|
||||||
|
if (msk)
|
||||||
|
sprintf(&alias[cnt], "%02x",
|
||||||
|
(*cls >> byte_shift) & 0xFF);
|
||||||
|
else
|
||||||
|
sprintf(&alias[cnt], "??");
|
||||||
|
cnt += 2;
|
||||||
|
}
|
||||||
|
sprintf(&alias[cnt], ":*");
|
||||||
|
}
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
ADD_TO_DEVTABLE("acpi", acpi_device_id, do_acpi_entry);
|
ADD_TO_DEVTABLE("acpi", acpi_device_id, do_acpi_entry);
|
||||||
|
Loading…
Reference in New Issue
Block a user