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?
|
||||
|
||||
A: No. You can suspend successfully, but you'll not be able to
|
||||
resume. uswsusp should be able to work with LVM. See suspend.sf.net.
|
||||
A: Yes and No. You can suspend successfully, but the kernel will not be able
|
||||
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
|
||||
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_base = ioremap(rentry->res->start,
|
||||
pdata->mmio_size);
|
||||
if (!pdata->mmio_base)
|
||||
goto err_out;
|
||||
break;
|
||||
}
|
||||
|
||||
acpi_dev_free_resource_list(&resource_list);
|
||||
|
||||
if (!pdata->mmio_base) {
|
||||
ret = -ENOMEM;
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
pdata->dev_desc = dev_desc;
|
||||
|
||||
if (dev_desc->setup)
|
||||
|
@ -175,10 +175,14 @@ static void __init acpi_request_region (struct acpi_generic_address *gas,
|
||||
if (!addr || !length)
|
||||
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 PM1a_EVT_BLK");
|
||||
@ -207,7 +211,10 @@ static void __init acpi_reserve_resources(void)
|
||||
if (!(acpi_gbl_FADT.gpe1_block_length & 0x1))
|
||||
acpi_request_region(&acpi_gbl_FADT.xgpe1_block,
|
||||
acpi_gbl_FADT.gpe1_block_length, "ACPI GPE1_BLK");
|
||||
|
||||
return 0;
|
||||
}
|
||||
fs_initcall_sync(acpi_reserve_resources);
|
||||
|
||||
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_reserve_resources();
|
||||
kacpid_wq = alloc_workqueue("kacpid", 0, 1);
|
||||
kacpi_notify_wq = alloc_workqueue("kacpi_notify", 0, 1);
|
||||
kacpi_hotplug_wq = alloc_ordered_workqueue("kacpi_hotplug", 0);
|
||||
|
@ -26,7 +26,6 @@
|
||||
#include <linux/device.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#ifdef CONFIG_X86
|
||||
@ -622,164 +621,3 @@ int acpi_dev_filter_resource_type(struct acpi_resource *ares,
|
||||
return (type & types) ? 0 : 1;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
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(
|
||||
struct acpi_device *device,
|
||||
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) {
|
||||
/* First, check the ACPI/PNP IDs provided by the caller. */
|
||||
for (id = ids; id->id[0]; id++)
|
||||
if (!strcmp((char *) id->id, hwid->id))
|
||||
for (id = ids; id->id[0] || id->cls; id++) {
|
||||
if (id->id[0] && !strcmp((char *) id->id, hwid->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
|
||||
@ -2101,6 +2127,8 @@ static void acpi_set_pnp_ids(acpi_handle handle, struct acpi_device_pnp *pnp,
|
||||
if (info->valid & ACPI_VALID_UID)
|
||||
pnp->unique_id = kstrdup(info->unique_id.string,
|
||||
GFP_KERNEL);
|
||||
if (info->valid & ACPI_VALID_CLS)
|
||||
acpi_add_id(pnp, info->class_code.string);
|
||||
|
||||
kfree(info);
|
||||
|
||||
|
@ -48,7 +48,7 @@ config ATA_VERBOSE_ERROR
|
||||
|
||||
config ATA_ACPI
|
||||
bool "ATA ACPI Support"
|
||||
depends on ACPI && PCI
|
||||
depends on ACPI
|
||||
default y
|
||||
help
|
||||
This option adds support for ATA-related ACPI objects.
|
||||
|
@ -20,6 +20,8 @@
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/libata.h>
|
||||
#include <linux/ahci_platform.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/pci_ids.h>
|
||||
#include "ahci.h"
|
||||
|
||||
#define DRV_NAME "ahci"
|
||||
@ -79,12 +81,19 @@ static const struct of_device_id 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 = {
|
||||
.probe = ahci_probe,
|
||||
.remove = ata_platform_remove_one,
|
||||
.driver = {
|
||||
.name = DRV_NAME,
|
||||
.of_match_table = ahci_of_match,
|
||||
.acpi_match_table = ahci_acpi_match,
|
||||
.pm = &ahci_pm_ops,
|
||||
},
|
||||
};
|
||||
|
@ -6,6 +6,7 @@
|
||||
* This file is released under the GPLv2.
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/platform_device.h>
|
||||
@ -19,6 +20,8 @@
|
||||
#include <linux/suspend.h>
|
||||
#include <linux/export.h>
|
||||
|
||||
#define GENPD_RETRY_MAX_MS 250 /* Approximate */
|
||||
|
||||
#define GENPD_DEV_CALLBACK(genpd, type, callback, dev) \
|
||||
({ \
|
||||
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)
|
||||
{
|
||||
struct generic_pm_domain *pd;
|
||||
unsigned int i;
|
||||
int ret = 0;
|
||||
|
||||
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);
|
||||
|
||||
while (1) {
|
||||
for (i = 1; i < GENPD_RETRY_MAX_MS; i <<= 1) {
|
||||
ret = pm_genpd_remove_device(pd, dev);
|
||||
if (ret != -EAGAIN)
|
||||
break;
|
||||
|
||||
mdelay(i);
|
||||
cond_resched();
|
||||
}
|
||||
|
||||
@ -2183,6 +2189,7 @@ int genpd_dev_pm_attach(struct device *dev)
|
||||
{
|
||||
struct of_phandle_args pd_args;
|
||||
struct generic_pm_domain *pd;
|
||||
unsigned int i;
|
||||
int ret;
|
||||
|
||||
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);
|
||||
|
||||
while (1) {
|
||||
for (i = 1; i < GENPD_RETRY_MAX_MS; i <<= 1) {
|
||||
ret = pm_genpd_add_device(pd, dev);
|
||||
if (ret != -EAGAIN)
|
||||
break;
|
||||
|
||||
mdelay(i);
|
||||
cond_resched();
|
||||
}
|
||||
|
||||
|
@ -45,14 +45,12 @@ static int dev_pm_attach_wake_irq(struct device *dev, int irq,
|
||||
return -EEXIST;
|
||||
}
|
||||
|
||||
dev->power.wakeirq = wirq;
|
||||
spin_unlock_irqrestore(&dev->power.lock, flags);
|
||||
|
||||
err = device_wakeup_attach_irq(dev, wirq);
|
||||
if (err)
|
||||
return err;
|
||||
if (!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;
|
||||
|
||||
spin_lock_irqsave(&dev->power.lock, flags);
|
||||
device_wakeup_detach_irq(dev);
|
||||
dev->power.wakeirq = NULL;
|
||||
spin_unlock_irqrestore(&dev->power.lock, flags);
|
||||
|
||||
device_wakeup_detach_irq(dev);
|
||||
if (wirq->dedicated_irq)
|
||||
free_irq(wirq->irq, wirq);
|
||||
kfree(wirq);
|
||||
|
@ -281,32 +281,25 @@ EXPORT_SYMBOL_GPL(device_wakeup_enable);
|
||||
* Attach a device wakeirq to the wakeup source so the device
|
||||
* wake IRQ can be configured automatically for suspend and
|
||||
* resume.
|
||||
*
|
||||
* Call under the device's power.lock lock.
|
||||
*/
|
||||
int device_wakeup_attach_irq(struct device *dev,
|
||||
struct wake_irq *wakeirq)
|
||||
{
|
||||
struct wakeup_source *ws;
|
||||
int ret = 0;
|
||||
|
||||
spin_lock_irq(&dev->power.lock);
|
||||
ws = dev->power.wakeup;
|
||||
if (!ws) {
|
||||
dev_err(dev, "forgot to call call device_init_wakeup?\n");
|
||||
ret = -EINVAL;
|
||||
goto unlock;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (ws->wakeirq) {
|
||||
ret = -EEXIST;
|
||||
goto unlock;
|
||||
}
|
||||
if (ws->wakeirq)
|
||||
return -EEXIST;
|
||||
|
||||
ws->wakeirq = wakeirq;
|
||||
|
||||
unlock:
|
||||
spin_unlock_irq(&dev->power.lock);
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -314,20 +307,16 @@ unlock:
|
||||
* @dev: Device to handle
|
||||
*
|
||||
* Removes a device wakeirq from the wakeup source.
|
||||
*
|
||||
* Call under the device's power.lock lock.
|
||||
*/
|
||||
void device_wakeup_detach_irq(struct device *dev)
|
||||
{
|
||||
struct wakeup_source *ws;
|
||||
|
||||
spin_lock_irq(&dev->power.lock);
|
||||
ws = dev->power.wakeup;
|
||||
if (!ws)
|
||||
goto unlock;
|
||||
|
||||
ws->wakeirq = NULL;
|
||||
|
||||
unlock:
|
||||
spin_unlock_irq(&dev->power.lock);
|
||||
if (ws)
|
||||
ws->wakeirq = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -7,7 +7,6 @@
|
||||
* Bjorn Helgaas <bjorn.helgaas@hp.com>
|
||||
*/
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/pnp.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/init.h>
|
||||
@ -23,41 +22,25 @@ static const struct pnp_device_id pnp_dev_table[] = {
|
||||
{"", 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)
|
||||
{
|
||||
char *regionid;
|
||||
const char *pnpid = dev_name(&dev->dev);
|
||||
resource_size_t start = r->start, end = r->end;
|
||||
bool reserved;
|
||||
struct resource *res;
|
||||
|
||||
regionid = kmalloc(16, GFP_KERNEL);
|
||||
if (!regionid)
|
||||
return;
|
||||
|
||||
snprintf(regionid, 16, "pnp %s", pnpid);
|
||||
reserved = __reserve_range(start, end - start + 1, !!port, regionid);
|
||||
if (!reserved)
|
||||
if (port)
|
||||
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);
|
||||
|
||||
/*
|
||||
@ -66,7 +49,7 @@ static void reserve_range(struct pnp_dev *dev, struct resource *r, int port)
|
||||
* have double reservations.
|
||||
*/
|
||||
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)
|
||||
|
@ -58,6 +58,19 @@ static inline acpi_handle acpi_device_handle(struct acpi_device *adev)
|
||||
acpi_fwnode_handle(adev) : NULL)
|
||||
#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)
|
||||
{
|
||||
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_reserve_region(u64 start, unsigned int length, u8 space_id,
|
||||
unsigned long flags, char *desc);
|
||||
|
||||
#ifdef CONFIG_HIBERNATION
|
||||
void __init acpi_no_s4_hw_signature(void);
|
||||
#endif
|
||||
@ -446,6 +456,7 @@ struct platform_device *acpi_create_platform_device(struct acpi_device *);
|
||||
#define ACPI_COMPANION(dev) (NULL)
|
||||
#define ACPI_COMPANION_SET(dev, adev) do { } while (0)
|
||||
#define ACPI_HANDLE(dev) (NULL)
|
||||
#define ACPI_DEVICE_CLASS(_cls, _msk) .cls = (0), .cls_msk = (0),
|
||||
|
||||
struct fwnode_handle;
|
||||
|
||||
@ -507,13 +518,6 @@ static inline int acpi_check_region(resource_size_t start, resource_size_t n,
|
||||
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;
|
||||
static inline int acpi_table_parse(char *id,
|
||||
int (*handler)(struct acpi_table_header *))
|
||||
|
@ -189,6 +189,8 @@ struct css_device_id {
|
||||
struct acpi_device_id {
|
||||
__u8 id[ACPI_ID_LEN];
|
||||
kernel_ulong_t driver_data;
|
||||
__u32 cls;
|
||||
__u32 cls_msk;
|
||||
};
|
||||
|
||||
#define PNP_ID_LEN 8
|
||||
|
@ -63,6 +63,8 @@ int main(void)
|
||||
|
||||
DEVID(acpi_device_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_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);
|
||||
|
||||
/* 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,
|
||||
void *symval, char *alias)
|
||||
{
|
||||
DEF_FIELD_ADDR(symval, acpi_device_id, id);
|
||||
sprintf(alias, "acpi*:%s:*", *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);
|
||||
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;
|
||||
}
|
||||
ADD_TO_DEVTABLE("acpi", acpi_device_id, do_acpi_entry);
|
||||
|
Loading…
Reference in New Issue
Block a user