diff options
Diffstat (limited to 'drivers/mtd/devices/phram.c')
| -rw-r--r-- | drivers/mtd/devices/phram.c | 190 |
1 files changed, 157 insertions, 33 deletions
diff --git a/drivers/mtd/devices/phram.c b/drivers/mtd/devices/phram.c index 9ee04b5f9311..fd9ec165e61a 100644 --- a/drivers/mtd/devices/phram.c +++ b/drivers/mtd/devices/phram.c @@ -1,18 +1,19 @@ -/** +// SPDX-License-Identifier: GPL-2.0-only +/* * Copyright (c) ???? Jochen Schäuble <psionic@psionic.de> * Copyright (c) 2003-2004 Joern Engel <joern@wh.fh-wedel.de> * * Usage: * * one commend line parameter per device, each in the form: - * phram=<name>,<start>,<len> + * phram=<name>,<start>,<len>[,<erasesize>] * <name> may be up to 63 characters. - * <start> and <len> can be octal, decimal or hexadecimal. If followed + * <start>, <len>, and <erasesize> can be octal, decimal or hexadecimal. If followed * by "ki", "Mi" or "Gi", the numbers will be interpreted as kilo, mega or - * gigabytes. + * gigabytes. <erasesize> is optional and defaults to PAGE_SIZE. * * Example: - * phram=swap,64Mi,128Mi phram=test,900Mi,1Mi + * phram=swap,64Mi,128Mi phram=test,900Mi,1Mi,64Ki */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt @@ -25,10 +26,16 @@ #include <linux/moduleparam.h> #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> +#include <linux/security.h> struct phram_mtd_list { struct mtd_info mtd; struct list_head list; + bool cached; }; static LIST_HEAD(phram_list); @@ -75,20 +82,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) +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; @@ -96,9 +134,10 @@ static int register_device(char *name, phys_addr_t start, size_t len) 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; } @@ -114,20 +153,26 @@ static int register_device(char *name, phys_addr_t start, size_t len) new->mtd._write = phram_write; new->mtd.owner = THIS_MODULE; new->mtd.type = MTD_RAM; - new->mtd.erasesize = PAGE_SIZE; + 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: @@ -147,8 +192,10 @@ static int parse_num64(uint64_t *num64, char *token) switch (token[len - 2]) { case 'G': shift += 10; + fallthrough; case 'M': shift += 10; + fallthrough; case 'k': shift += 10; token[len - 2] = 0; @@ -201,22 +248,24 @@ static inline void kill_final_newline(char *str) static int phram_init_called; /* * This shall contain the module parameter if any. It is of the form: - * - phram=<device>,<address>,<size> for module case - * - phram.phram=<device>,<address>,<size> for built-in case - * We leave 64 bytes for the device name, 20 for the address and 20 for the - * size. - * Example: phram.phram=rootfs,0xa0000000,512Mi + * - phram=<device>,<address>,<size>[,<erasesize>] for module case + * - phram.phram=<device>,<address>,<size>[,<erasesize>] for built-in case + * We leave 64 bytes for the device name, 20 for the address , 20 for the + * size and 20 for the erasesize. + * Example: phram.phram=rootfs,0xa0000000,512Mi,65536 */ -static char phram_paramline[64 + 20 + 20]; +static char phram_paramline[64 + 20 + 20 + 20]; #endif static int phram_setup(const char *val) { - char buf[64 + 20 + 20], *str = buf; - char *token[3]; + char buf[64 + 20 + 20 + 20], *str = buf; + char *token[4]; char *name; uint64_t start; uint64_t len; + uint64_t erasesize = PAGE_SIZE; + uint32_t rem; int i, ret; if (strnlen(val, sizeof(buf)) >= sizeof(buf)) @@ -225,7 +274,7 @@ static int phram_setup(const char *val) strcpy(str, val); kill_final_newline(str); - for (i = 0; i < 3; i++) + for (i = 0; i < 4; i++) token[i] = strsep(&str, ","); if (str) @@ -240,22 +289,47 @@ static int phram_setup(const char *val) ret = parse_num64(&start, token[1]); if (ret) { - kfree(name); parse_err("illegal start address\n"); + goto error; } ret = parse_num64(&len, token[2]); if (ret) { - kfree(name); parse_err("illegal device length\n"); + goto error; } - ret = register_device(name, start, len); - if (!ret) - pr_info("%s device: %#llx at %#llx\n", name, len, start); - else - kfree(name); + if (token[3]) { + ret = parse_num64(&erasesize, token[3]); + if (ret) { + parse_err("illegal erasesize\n"); + goto error; + } + } + + if (len == 0 || erasesize == 0 || erasesize > len + || erasesize > UINT_MAX) { + parse_err("illegal erasesize or len\n"); + ret = -EINVAL; + goto error; + } + + div_u64_rem(len, (uint32_t)erasesize, &rem); + if (rem) { + parse_err("len is not multiple of erasesize\n"); + ret = -EINVAL; + goto error; + } + ret = register_device(NULL, name, start, len, (uint32_t)erasesize); + if (ret) + goto error; + + pr_info("%s device: %#llx at %#llx for erasesize %#llx\n", name, len, start, erasesize); + return 0; + +error: + kfree(name); return ret; } @@ -291,17 +365,66 @@ static int phram_param_call(const char *val, const struct kernel_param *kp) #endif } -module_param_call(phram, phram_param_call, NULL, NULL, 000); -MODULE_PARM_DESC(phram, "Memory region to map. \"phram=<name>,<start>,<length>\""); +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 void 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); +} + +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 = security_locked_down(LOCKDOWN_DEV_MEM); + if (ret) + return ret; + + ret = platform_driver_register(&phram_driver); + if (ret) + return ret; #ifndef MODULE - if (phram_paramline[0]) + if (phram_paramline[0]) { ret = phram_setup(phram_paramline); + if (ret) + platform_driver_unregister(&phram_driver); + } phram_init_called = 1; #endif @@ -311,6 +434,7 @@ static int __init init_phram(void) static void __exit cleanup_phram(void) { unregister_devices(); + platform_driver_unregister(&phram_driver); } module_init(init_phram); |
