Merge branches 'acpi-battery', 'acpi-video' and 'acpi-misc'
[sfrench/cifs-2.6.git] / drivers / mtd / devices / phram.c
index d503821a3e60613911f409010be56eff1eacf0ae..208bd4d871f42cc3e6adf0beaf4769d50b1736ac 100644 (file)
 #include <linux/slab.h>
 #include <linux/mtd/mtd.h>
 #include <asm/div64.h>
+#include <linux/platform_device.h>
+#include <linux/of_address.h>
+#include <linux/of.h>
 
 struct phram_mtd_list {
        struct mtd_info mtd;
        struct list_head list;
+       bool cached;
 };
 
 static LIST_HEAD(phram_list);
@@ -77,20 +81,51 @@ static int phram_write(struct mtd_info *mtd, loff_t to, size_t len,
        return 0;
 }
 
+static int phram_map(struct phram_mtd_list *phram, phys_addr_t start, size_t len)
+{
+       void *addr = NULL;
+
+       if (phram->cached)
+               addr = memremap(start, len, MEMREMAP_WB);
+       else
+               addr = (void __force *)ioremap(start, len);
+       if (!addr)
+               return -EIO;
+
+       phram->mtd.priv = addr;
+
+       return 0;
+}
+
+static void phram_unmap(struct phram_mtd_list *phram)
+{
+       void *addr = phram->mtd.priv;
+
+       if (phram->cached) {
+               memunmap(addr);
+               return;
+       }
+
+       iounmap((void __iomem *)addr);
+}
+
 static void unregister_devices(void)
 {
        struct phram_mtd_list *this, *safe;
 
        list_for_each_entry_safe(this, safe, &phram_list, list) {
                mtd_device_unregister(&this->mtd);
-               iounmap(this->mtd.priv);
+               phram_unmap(this);
                kfree(this->mtd.name);
                kfree(this);
        }
 }
 
-static int register_device(char *name, phys_addr_t start, size_t len, uint32_t erasesize)
+static int register_device(struct platform_device *pdev, const char *name,
+                          phys_addr_t start, size_t len, uint32_t erasesize)
 {
+       struct device_node *np = pdev ? pdev->dev.of_node : NULL;
+       bool cached = np ? !of_property_read_bool(np, "no-map") : false;
        struct phram_mtd_list *new;
        int ret = -ENOMEM;
 
@@ -98,9 +133,10 @@ static int register_device(char *name, phys_addr_t start, size_t len, uint32_t e
        if (!new)
                goto out0;
 
-       ret = -EIO;
-       new->mtd.priv = ioremap(start, len);
-       if (!new->mtd.priv) {
+       new->cached = cached;
+
+       ret = phram_map(new, start, len);
+       if (ret) {
                pr_err("ioremap failed\n");
                goto out1;
        }
@@ -119,17 +155,23 @@ static int register_device(char *name, phys_addr_t start, size_t len, uint32_t e
        new->mtd.erasesize = erasesize;
        new->mtd.writesize = 1;
 
+       mtd_set_of_node(&new->mtd, np);
+
        ret = -EAGAIN;
        if (mtd_device_register(&new->mtd, NULL, 0)) {
                pr_err("Failed to register new device\n");
                goto out2;
        }
 
-       list_add_tail(&new->list, &phram_list);
+       if (pdev)
+               platform_set_drvdata(pdev, new);
+       else
+               list_add_tail(&new->list, &phram_list);
+
        return 0;
 
 out2:
-       iounmap(new->mtd.priv);
+       phram_unmap(new);
 out1:
        kfree(new);
 out0:
@@ -278,7 +320,7 @@ static int phram_setup(const char *val)
                goto error;
        }
 
-       ret = register_device(name, start, len, (uint32_t)erasesize);
+       ret = register_device(NULL, name, start, len, (uint32_t)erasesize);
        if (ret)
                goto error;
 
@@ -325,10 +367,54 @@ static int phram_param_call(const char *val, const struct kernel_param *kp)
 module_param_call(phram, phram_param_call, NULL, NULL, 0200);
 MODULE_PARM_DESC(phram, "Memory region to map. \"phram=<name>,<start>,<length>[,<erasesize>]\"");
 
+#ifdef CONFIG_OF
+static const struct of_device_id phram_of_match[] = {
+       { .compatible = "phram" },
+       {}
+};
+MODULE_DEVICE_TABLE(of, phram_of_match);
+#endif
+
+static int phram_probe(struct platform_device *pdev)
+{
+       struct resource *res;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!res)
+               return -ENOMEM;
+
+       /* mtd_set_of_node() reads name from "label" */
+       return register_device(pdev, NULL, res->start, resource_size(res),
+                              PAGE_SIZE);
+}
+
+static int phram_remove(struct platform_device *pdev)
+{
+       struct phram_mtd_list *phram = platform_get_drvdata(pdev);
+
+       mtd_device_unregister(&phram->mtd);
+       phram_unmap(phram);
+       kfree(phram);
+
+       return 0;
+}
+
+static struct platform_driver phram_driver = {
+       .probe          = phram_probe,
+       .remove         = phram_remove,
+       .driver         = {
+               .name           = "phram",
+               .of_match_table = of_match_ptr(phram_of_match),
+       },
+};
 
 static int __init init_phram(void)
 {
-       int ret = 0;
+       int ret;
+
+       ret = platform_driver_register(&phram_driver);
+       if (ret)
+               return ret;
 
 #ifndef MODULE
        if (phram_paramline[0])
@@ -336,12 +422,16 @@ static int __init init_phram(void)
        phram_init_called = 1;
 #endif
 
+       if (ret)
+               platform_driver_unregister(&phram_driver);
+
        return ret;
 }
 
 static void __exit cleanup_phram(void)
 {
        unregister_devices();
+       platform_driver_unregister(&phram_driver);
 }
 
 module_init(init_phram);