Merge branches 'acpi-pnp', 'acpi-soc', 'pm-domains' and 'pm-sleep'
* acpi-pnp: ACPI / PNP: Reserve ACPI resources at the fs_initcall_sync stage * acpi-soc: ACPI / LPSS: Fix up acpi_lpss_create_device() * pm-domains: PM / Domains: Avoid infinite loops in attach/detach code * pm-sleep: PM / hibernate: clarify resume documentation
This commit is contained in:
Коммит
d0aee67fa1
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -309,9 +309,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
|
||||
|
@ -507,13 +504,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 *))
|
||||
|
|
Загрузка…
Ссылка в новой задаче