Merge branches 'acpi-iomem', 'acpi-pm' and 'acpi-sci' into acpi-next
authorRafael J. Wysocki <rjw@sisk.pl>
Thu, 24 Feb 2011 19:00:44 +0000 (20:00 +0100)
committerRafael J. Wysocki <rjw@sisk.pl>
Thu, 24 Feb 2011 19:00:44 +0000 (20:00 +0100)
drivers/acpi/acpica/aclocal.h
drivers/acpi/acpica/evgpe.c
drivers/acpi/acpica/evxfgpe.c
drivers/acpi/debugfs.c
drivers/acpi/nvs.c
drivers/acpi/osl.c
include/linux/acpi_io.h

index 54784bb42ceca0386be233e0f2653867d5c7bf90..edc25867ad9d92ca4a23ef6fc7aa9c5f66c5862a 100644 (file)
@@ -416,10 +416,15 @@ struct acpi_gpe_handler_info {
        u8 originally_enabled;  /* True if GPE was originally enabled */
 };
 
+struct acpi_gpe_notify_object {
+       struct acpi_namespace_node *node;
+       struct acpi_gpe_notify_object *next;
+};
+
 union acpi_gpe_dispatch_info {
        struct acpi_namespace_node *method_node;        /* Method node for this GPE level */
        struct acpi_gpe_handler_info *handler;  /* Installed GPE handler */
-       struct acpi_namespace_node *device_node;        /* Parent _PRW device for implicit notify */
+       struct acpi_gpe_notify_object device;   /* List of _PRW devices for implicit notify */
 };
 
 /*
index 14988a86066fb70eaf9985cc49fbe1ff5b99ab4c..f4725212eb488fd1b37a3c2cc21735dbeb2282ad 100644 (file)
@@ -457,6 +457,7 @@ static void ACPI_SYSTEM_XFACE acpi_ev_asynch_execute_gpe_method(void *context)
        acpi_status status;
        struct acpi_gpe_event_info *local_gpe_event_info;
        struct acpi_evaluate_info *info;
+       struct acpi_gpe_notify_object *notify_object;
 
        ACPI_FUNCTION_TRACE(ev_asynch_execute_gpe_method);
 
@@ -508,10 +509,18 @@ static void ACPI_SYSTEM_XFACE acpi_ev_asynch_execute_gpe_method(void *context)
                 * from this thread -- because handlers may in turn run other
                 * control methods.
                 */
-               status =
-                   acpi_ev_queue_notify_request(local_gpe_event_info->dispatch.
-                                                device_node,
-                                                ACPI_NOTIFY_DEVICE_WAKE);
+               status = acpi_ev_queue_notify_request(
+                               local_gpe_event_info->dispatch.device.node,
+                               ACPI_NOTIFY_DEVICE_WAKE);
+
+               notify_object = local_gpe_event_info->dispatch.device.next;
+               while (ACPI_SUCCESS(status) && notify_object) {
+                       status = acpi_ev_queue_notify_request(
+                                       notify_object->node,
+                                       ACPI_NOTIFY_DEVICE_WAKE);
+                       notify_object = notify_object->next;
+               }
+
                break;
 
        case ACPI_GPE_DISPATCH_METHOD:
index 3b20a3401b641d0a8039f5ecff7cd55f9d1cfc01..52aaff3df562b94ffbd73206ae0cf363001e42bd 100644 (file)
@@ -198,7 +198,9 @@ acpi_setup_gpe_for_wake(acpi_handle wake_device,
        acpi_status status = AE_BAD_PARAMETER;
        struct acpi_gpe_event_info *gpe_event_info;
        struct acpi_namespace_node *device_node;
+       struct acpi_gpe_notify_object *notify_object;
        acpi_cpu_flags flags;
+       u8 gpe_dispatch_mask;
 
        ACPI_FUNCTION_TRACE(acpi_setup_gpe_for_wake);
 
@@ -221,27 +223,49 @@ acpi_setup_gpe_for_wake(acpi_handle wake_device,
                goto unlock_and_exit;
        }
 
+       if (wake_device == ACPI_ROOT_OBJECT) {
+               goto out;
+       }
+
        /*
         * If there is no method or handler for this GPE, then the
         * wake_device will be notified whenever this GPE fires (aka
         * "implicit notify") Note: The GPE is assumed to be
         * level-triggered (for windows compatibility).
         */
-       if (((gpe_event_info->flags & ACPI_GPE_DISPATCH_MASK) ==
-             ACPI_GPE_DISPATCH_NONE) && (wake_device != ACPI_ROOT_OBJECT)) {
+       gpe_dispatch_mask = gpe_event_info->flags & ACPI_GPE_DISPATCH_MASK;
+       if (gpe_dispatch_mask != ACPI_GPE_DISPATCH_NONE
+           && gpe_dispatch_mask != ACPI_GPE_DISPATCH_NOTIFY) {
+               goto out;
+       }
 
-               /* Validate wake_device is of type Device */
+       /* Validate wake_device is of type Device */
 
-               device_node = ACPI_CAST_PTR(struct acpi_namespace_node,
-                                           wake_device);
-               if (device_node->type != ACPI_TYPE_DEVICE) {
-                       goto unlock_and_exit;
-               }
+       device_node = ACPI_CAST_PTR(struct acpi_namespace_node, wake_device);
+       if (device_node->type != ACPI_TYPE_DEVICE) {
+               goto unlock_and_exit;
+       }
+
+       if (gpe_dispatch_mask == ACPI_GPE_DISPATCH_NONE) {
                gpe_event_info->flags = (ACPI_GPE_DISPATCH_NOTIFY |
                                         ACPI_GPE_LEVEL_TRIGGERED);
-               gpe_event_info->dispatch.device_node = device_node;
+               gpe_event_info->dispatch.device.node = device_node;
+               gpe_event_info->dispatch.device.next = NULL;
+       } else {
+               /* There are multiple devices to notify implicitly. */
+
+               notify_object = ACPI_ALLOCATE_ZEROED(sizeof(*notify_object));
+               if (!notify_object) {
+                       status = AE_NO_MEMORY;
+                       goto unlock_and_exit;
+               }
+
+               notify_object->node = device_node;
+               notify_object->next = gpe_event_info->dispatch.device.next;
+               gpe_event_info->dispatch.device.next = notify_object;
        }
 
+ out:
        gpe_event_info->flags |= ACPI_GPE_CAN_WAKE;
        status = AE_OK;
 
index 5df67f1d6c612537f10ac2fd84b6720911d16723..384f7abcff77984fb67c21c34b462761bc8f611e 100644 (file)
@@ -26,7 +26,9 @@ static ssize_t cm_write(struct file *file, const char __user * user_buf,
                        size_t count, loff_t *ppos)
 {
        static char *buf;
-       static int uncopied_bytes;
+       static u32 max_size;
+       static u32 uncopied_bytes;
+
        struct acpi_table_header table;
        acpi_status status;
 
@@ -37,19 +39,24 @@ static ssize_t cm_write(struct file *file, const char __user * user_buf,
                if (copy_from_user(&table, user_buf,
                                   sizeof(struct acpi_table_header)))
                        return -EFAULT;
-               uncopied_bytes = table.length;
-               buf = kzalloc(uncopied_bytes, GFP_KERNEL);
+               uncopied_bytes = max_size = table.length;
+               buf = kzalloc(max_size, GFP_KERNEL);
                if (!buf)
                        return -ENOMEM;
        }
 
-       if (uncopied_bytes < count) {
-               kfree(buf);
+       if (buf == NULL)
+               return -EINVAL;
+
+       if ((*ppos > max_size) ||
+           (*ppos + count > max_size) ||
+           (*ppos + count < count) ||
+           (count > uncopied_bytes))
                return -EINVAL;
-       }
 
        if (copy_from_user(buf + (*ppos), user_buf, count)) {
                kfree(buf);
+               buf = NULL;
                return -EFAULT;
        }
 
@@ -59,6 +66,7 @@ static ssize_t cm_write(struct file *file, const char __user * user_buf,
        if (!uncopied_bytes) {
                status = acpi_install_method(buf);
                kfree(buf);
+               buf = NULL;
                if (ACPI_FAILURE(status))
                        return -EINVAL;
                add_taint(TAINT_OVERRIDDEN_ACPI_TABLE);
index fa5a1df42b79a40a817d27f036789d7bcd3a3c38..096787b43c960a71afd971ed8f312ba39e7787a9 100644 (file)
@@ -26,6 +26,7 @@ struct nvs_page {
        unsigned int size;
        void *kaddr;
        void *data;
+       bool unmap;
        struct list_head node;
 };
 
@@ -44,6 +45,9 @@ int suspend_nvs_register(unsigned long start, unsigned long size)
 {
        struct nvs_page *entry, *next;
 
+       pr_info("PM: Registering ACPI NVS region at %lx (%ld bytes)\n",
+               start, size);
+
        while (size > 0) {
                unsigned int nr_bytes;
 
@@ -81,7 +85,13 @@ void suspend_nvs_free(void)
                        free_page((unsigned long)entry->data);
                        entry->data = NULL;
                        if (entry->kaddr) {
-                               iounmap(entry->kaddr);
+                               if (entry->unmap) {
+                                       iounmap(entry->kaddr);
+                                       entry->unmap = false;
+                               } else {
+                                       acpi_os_unmap_memory(entry->kaddr,
+                                                            entry->size);
+                               }
                                entry->kaddr = NULL;
                        }
                }
@@ -115,8 +125,14 @@ int suspend_nvs_save(void)
 
        list_for_each_entry(entry, &nvs_list, node)
                if (entry->data) {
-                       entry->kaddr = acpi_os_ioremap(entry->phys_start,
-                                                   entry->size);
+                       unsigned long phys = entry->phys_start;
+                       unsigned int size = entry->size;
+
+                       entry->kaddr = acpi_os_get_iomem(phys, size);
+                       if (!entry->kaddr) {
+                               entry->kaddr = acpi_os_ioremap(phys, size);
+                               entry->unmap = !!entry->kaddr;
+                       }
                        if (!entry->kaddr) {
                                suspend_nvs_free();
                                return -ENOMEM;
index c90c76aa7f8b79152ab4f449dbddbf9ba4983b93..45c6ac8790d7b77c5b17ad1ef1b6a0166c4ae864 100644 (file)
@@ -76,7 +76,6 @@ EXPORT_SYMBOL(acpi_in_debugger);
 extern char line_buf[80];
 #endif                         /*ENABLE_DEBUGGER */
 
-static unsigned int acpi_irq_irq;
 static acpi_osd_handler acpi_irq_handler;
 static void *acpi_irq_context;
 static struct workqueue_struct *kacpid_wq;
@@ -105,11 +104,11 @@ struct acpi_ioremap {
        void __iomem *virt;
        acpi_physical_address phys;
        acpi_size size;
-       struct kref ref;
+       unsigned long refcount;
 };
 
 static LIST_HEAD(acpi_ioremaps);
-static DEFINE_SPINLOCK(acpi_ioremap_lock);
+static DEFINE_MUTEX(acpi_ioremap_lock);
 
 static void __init acpi_osi_setup_late(void);
 
@@ -285,6 +284,22 @@ acpi_map_vaddr_lookup(acpi_physical_address phys, unsigned int size)
        return NULL;
 }
 
+void __iomem *acpi_os_get_iomem(acpi_physical_address phys, unsigned int size)
+{
+       struct acpi_ioremap *map;
+       void __iomem *virt = NULL;
+
+       mutex_lock(&acpi_ioremap_lock);
+       map = acpi_map_lookup(phys, size);
+       if (map) {
+               virt = map->virt + (phys - map->phys);
+               map->refcount++;
+       }
+       mutex_unlock(&acpi_ioremap_lock);
+       return virt;
+}
+EXPORT_SYMBOL_GPL(acpi_os_get_iomem);
+
 /* Must be called with 'acpi_ioremap_lock' or RCU read lock held. */
 static struct acpi_ioremap *
 acpi_map_lookup_virt(void __iomem *virt, acpi_size size)
@@ -302,8 +317,7 @@ acpi_map_lookup_virt(void __iomem *virt, acpi_size size)
 void __iomem *__init_refok
 acpi_os_map_memory(acpi_physical_address phys, acpi_size size)
 {
-       struct acpi_ioremap *map, *tmp_map;
-       unsigned long flags;
+       struct acpi_ioremap *map;
        void __iomem *virt;
        acpi_physical_address pg_off;
        acpi_size pg_sz;
@@ -316,14 +330,25 @@ acpi_os_map_memory(acpi_physical_address phys, acpi_size size)
        if (!acpi_gbl_permanent_mmap)
                return __acpi_map_table((unsigned long)phys, size);
 
+       mutex_lock(&acpi_ioremap_lock);
+       /* Check if there's a suitable mapping already. */
+       map = acpi_map_lookup(phys, size);
+       if (map) {
+               map->refcount++;
+               goto out;
+       }
+
        map = kzalloc(sizeof(*map), GFP_KERNEL);
-       if (!map)
+       if (!map) {
+               mutex_unlock(&acpi_ioremap_lock);
                return NULL;
+       }
 
        pg_off = round_down(phys, PAGE_SIZE);
        pg_sz = round_up(phys + size, PAGE_SIZE) - pg_off;
        virt = acpi_os_ioremap(pg_off, pg_sz);
        if (!virt) {
+               mutex_unlock(&acpi_ioremap_lock);
                kfree(map);
                return NULL;
        }
@@ -332,62 +357,51 @@ acpi_os_map_memory(acpi_physical_address phys, acpi_size size)
        map->virt = virt;
        map->phys = pg_off;
        map->size = pg_sz;
-       kref_init(&map->ref);
-
-       spin_lock_irqsave(&acpi_ioremap_lock, flags);
-       /* Check if page has already been mapped. */
-       tmp_map = acpi_map_lookup(phys, size);
-       if (tmp_map) {
-               kref_get(&tmp_map->ref);
-               spin_unlock_irqrestore(&acpi_ioremap_lock, flags);
-               iounmap(map->virt);
-               kfree(map);
-               return tmp_map->virt + (phys - tmp_map->phys);
-       }
+       map->refcount = 1;
+
        list_add_tail_rcu(&map->list, &acpi_ioremaps);
-       spin_unlock_irqrestore(&acpi_ioremap_lock, flags);
 
+ out:
+       mutex_unlock(&acpi_ioremap_lock);
        return map->virt + (phys - map->phys);
 }
 EXPORT_SYMBOL_GPL(acpi_os_map_memory);
 
-static void acpi_kref_del_iomap(struct kref *ref)
+static void acpi_os_drop_map_ref(struct acpi_ioremap *map)
 {
-       struct acpi_ioremap *map;
+       if (!--map->refcount)
+               list_del_rcu(&map->list);
+}
 
-       map = container_of(ref, struct acpi_ioremap, ref);
-       list_del_rcu(&map->list);
+static void acpi_os_map_cleanup(struct acpi_ioremap *map)
+{
+       if (!map->refcount) {
+               synchronize_rcu();
+               iounmap(map->virt);
+               kfree(map);
+       }
 }
 
 void __ref acpi_os_unmap_memory(void __iomem *virt, acpi_size size)
 {
        struct acpi_ioremap *map;
-       unsigned long flags;
-       int del;
 
        if (!acpi_gbl_permanent_mmap) {
                __acpi_unmap_table(virt, size);
                return;
        }
 
-       spin_lock_irqsave(&acpi_ioremap_lock, flags);
+       mutex_lock(&acpi_ioremap_lock);
        map = acpi_map_lookup_virt(virt, size);
        if (!map) {
-               spin_unlock_irqrestore(&acpi_ioremap_lock, flags);
-               printk(KERN_ERR PREFIX "%s: bad address %p\n", __func__, virt);
-               dump_stack();
+               mutex_unlock(&acpi_ioremap_lock);
+               WARN(true, PREFIX "%s: bad address %p\n", __func__, virt);
                return;
        }
+       acpi_os_drop_map_ref(map);
+       mutex_unlock(&acpi_ioremap_lock);
 
-       del = kref_put(&map->ref, acpi_kref_del_iomap);
-       spin_unlock_irqrestore(&acpi_ioremap_lock, flags);
-
-       if (!del)
-               return;
-
-       synchronize_rcu();
-       iounmap(map->virt);
-       kfree(map);
+       acpi_os_map_cleanup(map);
 }
 EXPORT_SYMBOL_GPL(acpi_os_unmap_memory);
 
@@ -397,7 +411,7 @@ void __init early_acpi_os_unmap_memory(void __iomem *virt, acpi_size size)
                __acpi_unmap_table(virt, size);
 }
 
-int acpi_os_map_generic_address(struct acpi_generic_address *addr)
+static int acpi_os_map_generic_address(struct acpi_generic_address *addr)
 {
        void __iomem *virt;
 
@@ -413,13 +427,10 @@ int acpi_os_map_generic_address(struct acpi_generic_address *addr)
 
        return 0;
 }
-EXPORT_SYMBOL_GPL(acpi_os_map_generic_address);
 
-void acpi_os_unmap_generic_address(struct acpi_generic_address *addr)
+static void acpi_os_unmap_generic_address(struct acpi_generic_address *addr)
 {
-       void __iomem *virt;
-       unsigned long flags;
-       acpi_size size = addr->bit_width / 8;
+       struct acpi_ioremap *map;
 
        if (addr->space_id != ACPI_ADR_SPACE_SYSTEM_MEMORY)
                return;
@@ -427,13 +438,17 @@ void acpi_os_unmap_generic_address(struct acpi_generic_address *addr)
        if (!addr->address || !addr->bit_width)
                return;
 
-       spin_lock_irqsave(&acpi_ioremap_lock, flags);
-       virt = acpi_map_vaddr_lookup(addr->address, size);
-       spin_unlock_irqrestore(&acpi_ioremap_lock, flags);
+       mutex_lock(&acpi_ioremap_lock);
+       map = acpi_map_lookup(addr->address, addr->bit_width / 8);
+       if (!map) {
+               mutex_unlock(&acpi_ioremap_lock);
+               return;
+       }
+       acpi_os_drop_map_ref(map);
+       mutex_unlock(&acpi_ioremap_lock);
 
-       acpi_os_unmap_memory(virt, size);
+       acpi_os_map_cleanup(map);
 }
-EXPORT_SYMBOL_GPL(acpi_os_unmap_generic_address);
 
 #ifdef ACPI_FUTURE_USAGE
 acpi_status
@@ -516,11 +531,15 @@ acpi_os_install_interrupt_handler(u32 gsi, acpi_osd_handler handler,
        acpi_irq_stats_init();
 
        /*
-        * Ignore the GSI from the core, and use the value in our copy of the
-        * FADT. It may not be the same if an interrupt source override exists
-        * for the SCI.
+        * ACPI interrupts different from the SCI in our copy of the FADT are
+        * not supported.
         */
-       gsi = acpi_gbl_FADT.sci_interrupt;
+       if (gsi != acpi_gbl_FADT.sci_interrupt)
+               return AE_BAD_PARAMETER;
+
+       if (acpi_irq_handler)
+               return AE_ALREADY_ACQUIRED;
+
        if (acpi_gsi_to_irq(gsi, &irq) < 0) {
                printk(KERN_ERR PREFIX "SCI (ACPI GSI %d) not registered\n",
                       gsi);
@@ -531,20 +550,20 @@ acpi_os_install_interrupt_handler(u32 gsi, acpi_osd_handler handler,
        acpi_irq_context = context;
        if (request_irq(irq, acpi_irq, IRQF_SHARED, "acpi", acpi_irq)) {
                printk(KERN_ERR PREFIX "SCI (IRQ%d) allocation failed\n", irq);
+               acpi_irq_handler = NULL;
                return AE_NOT_ACQUIRED;
        }
-       acpi_irq_irq = irq;
 
        return AE_OK;
 }
 
 acpi_status acpi_os_remove_interrupt_handler(u32 irq, acpi_osd_handler handler)
 {
-       if (irq) {
-               free_irq(irq, acpi_irq);
-               acpi_irq_handler = NULL;
-               acpi_irq_irq = 0;
-       }
+       if (irq != acpi_gbl_FADT.sci_interrupt)
+               return AE_BAD_PARAMETER;
+
+       free_irq(irq, acpi_irq);
+       acpi_irq_handler = NULL;
 
        return AE_OK;
 }
@@ -1603,7 +1622,7 @@ acpi_status __init acpi_os_initialize1(void)
 acpi_status acpi_os_terminate(void)
 {
        if (acpi_irq_handler) {
-               acpi_os_remove_interrupt_handler(acpi_irq_irq,
+               acpi_os_remove_interrupt_handler(acpi_gbl_FADT.sci_interrupt,
                                                 acpi_irq_handler);
        }
 
index 7180013a4a3aecd5d455802fce0ee4d74f479989..4afd7102459d7fbdef49be451141e16b0591f5e7 100644 (file)
@@ -10,7 +10,6 @@ static inline void __iomem *acpi_os_ioremap(acpi_physical_address phys,
        return ioremap_cache(phys, size);
 }
 
-int acpi_os_map_generic_address(struct acpi_generic_address *addr);
-void acpi_os_unmap_generic_address(struct acpi_generic_address *addr);
+void __iomem *acpi_os_get_iomem(acpi_physical_address phys, unsigned int size);
 
 #endif