Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Copyright (c) ????       Jochen Schäuble <psionic@psionic.de>
0004  * Copyright (c) 2003-2004  Joern Engel <joern@wh.fh-wedel.de>
0005  *
0006  * Usage:
0007  *
0008  * one commend line parameter per device, each in the form:
0009  *   phram=<name>,<start>,<len>[,<erasesize>]
0010  * <name> may be up to 63 characters.
0011  * <start>, <len>, and <erasesize> can be octal, decimal or hexadecimal.  If followed
0012  * by "ki", "Mi" or "Gi", the numbers will be interpreted as kilo, mega or
0013  * gigabytes. <erasesize> is optional and defaults to PAGE_SIZE.
0014  *
0015  * Example:
0016  *  phram=swap,64Mi,128Mi phram=test,900Mi,1Mi,64Ki
0017  */
0018 
0019 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
0020 
0021 #include <linux/io.h>
0022 #include <linux/init.h>
0023 #include <linux/kernel.h>
0024 #include <linux/list.h>
0025 #include <linux/module.h>
0026 #include <linux/moduleparam.h>
0027 #include <linux/slab.h>
0028 #include <linux/mtd/mtd.h>
0029 #include <asm/div64.h>
0030 #include <linux/platform_device.h>
0031 #include <linux/of_address.h>
0032 #include <linux/of.h>
0033 
0034 struct phram_mtd_list {
0035     struct mtd_info mtd;
0036     struct list_head list;
0037     bool cached;
0038 };
0039 
0040 static LIST_HEAD(phram_list);
0041 
0042 static int phram_erase(struct mtd_info *mtd, struct erase_info *instr)
0043 {
0044     u_char *start = mtd->priv;
0045 
0046     memset(start + instr->addr, 0xff, instr->len);
0047 
0048     return 0;
0049 }
0050 
0051 static int phram_point(struct mtd_info *mtd, loff_t from, size_t len,
0052         size_t *retlen, void **virt, resource_size_t *phys)
0053 {
0054     *virt = mtd->priv + from;
0055     *retlen = len;
0056     return 0;
0057 }
0058 
0059 static int phram_unpoint(struct mtd_info *mtd, loff_t from, size_t len)
0060 {
0061     return 0;
0062 }
0063 
0064 static int phram_read(struct mtd_info *mtd, loff_t from, size_t len,
0065         size_t *retlen, u_char *buf)
0066 {
0067     u_char *start = mtd->priv;
0068 
0069     memcpy(buf, start + from, len);
0070     *retlen = len;
0071     return 0;
0072 }
0073 
0074 static int phram_write(struct mtd_info *mtd, loff_t to, size_t len,
0075         size_t *retlen, const u_char *buf)
0076 {
0077     u_char *start = mtd->priv;
0078 
0079     memcpy(start + to, buf, len);
0080     *retlen = len;
0081     return 0;
0082 }
0083 
0084 static int phram_map(struct phram_mtd_list *phram, phys_addr_t start, size_t len)
0085 {
0086     void *addr = NULL;
0087 
0088     if (phram->cached)
0089         addr = memremap(start, len, MEMREMAP_WB);
0090     else
0091         addr = (void __force *)ioremap(start, len);
0092     if (!addr)
0093         return -EIO;
0094 
0095     phram->mtd.priv = addr;
0096 
0097     return 0;
0098 }
0099 
0100 static void phram_unmap(struct phram_mtd_list *phram)
0101 {
0102     void *addr = phram->mtd.priv;
0103 
0104     if (phram->cached) {
0105         memunmap(addr);
0106         return;
0107     }
0108 
0109     iounmap((void __iomem *)addr);
0110 }
0111 
0112 static void unregister_devices(void)
0113 {
0114     struct phram_mtd_list *this, *safe;
0115 
0116     list_for_each_entry_safe(this, safe, &phram_list, list) {
0117         mtd_device_unregister(&this->mtd);
0118         phram_unmap(this);
0119         kfree(this->mtd.name);
0120         kfree(this);
0121     }
0122 }
0123 
0124 static int register_device(struct platform_device *pdev, const char *name,
0125                phys_addr_t start, size_t len, uint32_t erasesize)
0126 {
0127     struct device_node *np = pdev ? pdev->dev.of_node : NULL;
0128     bool cached = np ? !of_property_read_bool(np, "no-map") : false;
0129     struct phram_mtd_list *new;
0130     int ret = -ENOMEM;
0131 
0132     new = kzalloc(sizeof(*new), GFP_KERNEL);
0133     if (!new)
0134         goto out0;
0135 
0136     new->cached = cached;
0137 
0138     ret = phram_map(new, start, len);
0139     if (ret) {
0140         pr_err("ioremap failed\n");
0141         goto out1;
0142     }
0143 
0144 
0145     new->mtd.name = name;
0146     new->mtd.size = len;
0147     new->mtd.flags = MTD_CAP_RAM;
0148     new->mtd._erase = phram_erase;
0149     new->mtd._point = phram_point;
0150     new->mtd._unpoint = phram_unpoint;
0151     new->mtd._read = phram_read;
0152     new->mtd._write = phram_write;
0153     new->mtd.owner = THIS_MODULE;
0154     new->mtd.type = MTD_RAM;
0155     new->mtd.erasesize = erasesize;
0156     new->mtd.writesize = 1;
0157 
0158     mtd_set_of_node(&new->mtd, np);
0159 
0160     ret = -EAGAIN;
0161     if (mtd_device_register(&new->mtd, NULL, 0)) {
0162         pr_err("Failed to register new device\n");
0163         goto out2;
0164     }
0165 
0166     if (pdev)
0167         platform_set_drvdata(pdev, new);
0168     else
0169         list_add_tail(&new->list, &phram_list);
0170 
0171     return 0;
0172 
0173 out2:
0174     phram_unmap(new);
0175 out1:
0176     kfree(new);
0177 out0:
0178     return ret;
0179 }
0180 
0181 static int parse_num64(uint64_t *num64, char *token)
0182 {
0183     size_t len;
0184     int shift = 0;
0185     int ret;
0186 
0187     len = strlen(token);
0188     /* By dwmw2 editorial decree, "ki", "Mi" or "Gi" are to be used. */
0189     if (len > 2) {
0190         if (token[len - 1] == 'i') {
0191             switch (token[len - 2]) {
0192             case 'G':
0193                 shift += 10;
0194                 fallthrough;
0195             case 'M':
0196                 shift += 10;
0197                 fallthrough;
0198             case 'k':
0199                 shift += 10;
0200                 token[len - 2] = 0;
0201                 break;
0202             default:
0203                 return -EINVAL;
0204             }
0205         }
0206     }
0207 
0208     ret = kstrtou64(token, 0, num64);
0209     *num64 <<= shift;
0210 
0211     return ret;
0212 }
0213 
0214 static int parse_name(char **pname, const char *token)
0215 {
0216     size_t len;
0217     char *name;
0218 
0219     len = strlen(token) + 1;
0220     if (len > 64)
0221         return -ENOSPC;
0222 
0223     name = kstrdup(token, GFP_KERNEL);
0224     if (!name)
0225         return -ENOMEM;
0226 
0227     *pname = name;
0228     return 0;
0229 }
0230 
0231 
0232 static inline void kill_final_newline(char *str)
0233 {
0234     char *newline = strrchr(str, '\n');
0235 
0236     if (newline && !newline[1])
0237         *newline = 0;
0238 }
0239 
0240 
0241 #define parse_err(fmt, args...) do {    \
0242     pr_err(fmt , ## args);  \
0243     return 1;       \
0244 } while (0)
0245 
0246 #ifndef MODULE
0247 static int phram_init_called;
0248 /*
0249  * This shall contain the module parameter if any. It is of the form:
0250  * - phram=<device>,<address>,<size>[,<erasesize>] for module case
0251  * - phram.phram=<device>,<address>,<size>[,<erasesize>] for built-in case
0252  * We leave 64 bytes for the device name, 20 for the address , 20 for the
0253  * size and 20 for the erasesize.
0254  * Example: phram.phram=rootfs,0xa0000000,512Mi,65536
0255  */
0256 static char phram_paramline[64 + 20 + 20 + 20];
0257 #endif
0258 
0259 static int phram_setup(const char *val)
0260 {
0261     char buf[64 + 20 + 20 + 20], *str = buf;
0262     char *token[4];
0263     char *name;
0264     uint64_t start;
0265     uint64_t len;
0266     uint64_t erasesize = PAGE_SIZE;
0267     uint32_t rem;
0268     int i, ret;
0269 
0270     if (strnlen(val, sizeof(buf)) >= sizeof(buf))
0271         parse_err("parameter too long\n");
0272 
0273     strcpy(str, val);
0274     kill_final_newline(str);
0275 
0276     for (i = 0; i < 4; i++)
0277         token[i] = strsep(&str, ",");
0278 
0279     if (str)
0280         parse_err("too many arguments\n");
0281 
0282     if (!token[2])
0283         parse_err("not enough arguments\n");
0284 
0285     ret = parse_name(&name, token[0]);
0286     if (ret)
0287         return ret;
0288 
0289     ret = parse_num64(&start, token[1]);
0290     if (ret) {
0291         parse_err("illegal start address\n");
0292         goto error;
0293     }
0294 
0295     ret = parse_num64(&len, token[2]);
0296     if (ret) {
0297         parse_err("illegal device length\n");
0298         goto error;
0299     }
0300 
0301     if (token[3]) {
0302         ret = parse_num64(&erasesize, token[3]);
0303         if (ret) {
0304             parse_err("illegal erasesize\n");
0305             goto error;
0306         }
0307     }
0308 
0309     if (len == 0 || erasesize == 0 || erasesize > len
0310         || erasesize > UINT_MAX) {
0311         parse_err("illegal erasesize or len\n");
0312         ret = -EINVAL;
0313         goto error;
0314     }
0315 
0316     div_u64_rem(len, (uint32_t)erasesize, &rem);
0317     if (rem) {
0318         parse_err("len is not multiple of erasesize\n");
0319         ret = -EINVAL;
0320         goto error;
0321     }
0322 
0323     ret = register_device(NULL, name, start, len, (uint32_t)erasesize);
0324     if (ret)
0325         goto error;
0326 
0327     pr_info("%s device: %#llx at %#llx for erasesize %#llx\n", name, len, start, erasesize);
0328     return 0;
0329 
0330 error:
0331     kfree(name);
0332     return ret;
0333 }
0334 
0335 static int phram_param_call(const char *val, const struct kernel_param *kp)
0336 {
0337 #ifdef MODULE
0338     return phram_setup(val);
0339 #else
0340     /*
0341      * If more parameters are later passed in via
0342      * /sys/module/phram/parameters/phram
0343      * and init_phram() has already been called,
0344      * we can parse the argument now.
0345      */
0346 
0347     if (phram_init_called)
0348         return phram_setup(val);
0349 
0350     /*
0351      * During early boot stage, we only save the parameters
0352      * here. We must parse them later: if the param passed
0353      * from kernel boot command line, phram_param_call() is
0354      * called so early that it is not possible to resolve
0355      * the device (even kmalloc() fails). Defer that work to
0356      * phram_setup().
0357      */
0358 
0359     if (strlen(val) >= sizeof(phram_paramline))
0360         return -ENOSPC;
0361     strcpy(phram_paramline, val);
0362 
0363     return 0;
0364 #endif
0365 }
0366 
0367 module_param_call(phram, phram_param_call, NULL, NULL, 0200);
0368 MODULE_PARM_DESC(phram, "Memory region to map. \"phram=<name>,<start>,<length>[,<erasesize>]\"");
0369 
0370 #ifdef CONFIG_OF
0371 static const struct of_device_id phram_of_match[] = {
0372     { .compatible = "phram" },
0373     {}
0374 };
0375 MODULE_DEVICE_TABLE(of, phram_of_match);
0376 #endif
0377 
0378 static int phram_probe(struct platform_device *pdev)
0379 {
0380     struct resource *res;
0381 
0382     res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
0383     if (!res)
0384         return -ENOMEM;
0385 
0386     /* mtd_set_of_node() reads name from "label" */
0387     return register_device(pdev, NULL, res->start, resource_size(res),
0388                    PAGE_SIZE);
0389 }
0390 
0391 static int phram_remove(struct platform_device *pdev)
0392 {
0393     struct phram_mtd_list *phram = platform_get_drvdata(pdev);
0394 
0395     mtd_device_unregister(&phram->mtd);
0396     phram_unmap(phram);
0397     kfree(phram);
0398 
0399     return 0;
0400 }
0401 
0402 static struct platform_driver phram_driver = {
0403     .probe      = phram_probe,
0404     .remove     = phram_remove,
0405     .driver     = {
0406         .name       = "phram",
0407         .of_match_table = of_match_ptr(phram_of_match),
0408     },
0409 };
0410 
0411 static int __init init_phram(void)
0412 {
0413     int ret;
0414 
0415     ret = platform_driver_register(&phram_driver);
0416     if (ret)
0417         return ret;
0418 
0419 #ifndef MODULE
0420     if (phram_paramline[0])
0421         ret = phram_setup(phram_paramline);
0422     phram_init_called = 1;
0423 #endif
0424 
0425     if (ret)
0426         platform_driver_unregister(&phram_driver);
0427 
0428     return ret;
0429 }
0430 
0431 static void __exit cleanup_phram(void)
0432 {
0433     unregister_devices();
0434     platform_driver_unregister(&phram_driver);
0435 }
0436 
0437 module_init(init_phram);
0438 module_exit(cleanup_phram);
0439 
0440 MODULE_LICENSE("GPL");
0441 MODULE_AUTHOR("Joern Engel <joern@wh.fh-wedel.de>");
0442 MODULE_DESCRIPTION("MTD driver for physical RAM");