]> Git Repo - linux.git/blobdiff - drivers/acpi/osl.c
ACPI: Introduce acpi_os_get_iomem()
[linux.git] / drivers / acpi / osl.c
index b0931818cf98f133bf000f49e7200c8bbd9611fb..52ca8721bc9c95be7a1251edeb977ff463195883 100644 (file)
@@ -105,11 +105,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 +285,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 +318,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 +331,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 +358,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 +412,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 +428,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 +439,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
@@ -636,17 +652,21 @@ EXPORT_SYMBOL(acpi_os_write_port);
 acpi_status
 acpi_os_read_memory(acpi_physical_address phys_addr, u32 * value, u32 width)
 {
-       u32 dummy;
        void __iomem *virt_addr;
-       int size = width / 8, unmap = 0;
+       unsigned int size = width / 8;
+       bool unmap = false;
+       u32 dummy;
 
        rcu_read_lock();
        virt_addr = acpi_map_vaddr_lookup(phys_addr, size);
-       rcu_read_unlock();
        if (!virt_addr) {
+               rcu_read_unlock();
                virt_addr = acpi_os_ioremap(phys_addr, size);
-               unmap = 1;
+               if (!virt_addr)
+                       return AE_BAD_ADDRESS;
+               unmap = true;
        }
+
        if (!value)
                value = &dummy;
 
@@ -666,6 +686,8 @@ acpi_os_read_memory(acpi_physical_address phys_addr, u32 * value, u32 width)
 
        if (unmap)
                iounmap(virt_addr);
+       else
+               rcu_read_unlock();
 
        return AE_OK;
 }
@@ -674,14 +696,17 @@ acpi_status
 acpi_os_write_memory(acpi_physical_address phys_addr, u32 value, u32 width)
 {
        void __iomem *virt_addr;
-       int size = width / 8, unmap = 0;
+       unsigned int size = width / 8;
+       bool unmap = false;
 
        rcu_read_lock();
        virt_addr = acpi_map_vaddr_lookup(phys_addr, size);
-       rcu_read_unlock();
        if (!virt_addr) {
+               rcu_read_unlock();
                virt_addr = acpi_os_ioremap(phys_addr, size);
-               unmap = 1;
+               if (!virt_addr)
+                       return AE_BAD_ADDRESS;
+               unmap = true;
        }
 
        switch (width) {
@@ -700,6 +725,8 @@ acpi_os_write_memory(acpi_physical_address phys_addr, u32 value, u32 width)
 
        if (unmap)
                iounmap(virt_addr);
+       else
+               rcu_read_unlock();
 
        return AE_OK;
 }
This page took 0.040116 seconds and 4 git commands to generate.