brintos

brintos / linux-shallow public Read only

0
0
Text · 8.9 KiB · 1bf192f Raw
441 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (c) ????		Jochen Schäuble <psionic@psionic.de>4 * Copyright (c) 2003-2004	Joern Engel <joern@wh.fh-wedel.de>5 *6 * Usage:7 *8 * one commend line parameter per device, each in the form:9 *   phram=<name>,<start>,<len>[,<erasesize>]10 * <name> may be up to 63 characters.11 * <start>, <len>, and <erasesize> can be octal, decimal or hexadecimal.  If followed12 * by "ki", "Mi" or "Gi", the numbers will be interpreted as kilo, mega or13 * gigabytes. <erasesize> is optional and defaults to PAGE_SIZE.14 *15 * Example:16 *	phram=swap,64Mi,128Mi phram=test,900Mi,1Mi,64Ki17 */18 19#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt20 21#include <linux/io.h>22#include <linux/init.h>23#include <linux/kernel.h>24#include <linux/list.h>25#include <linux/module.h>26#include <linux/moduleparam.h>27#include <linux/slab.h>28#include <linux/mtd/mtd.h>29#include <asm/div64.h>30#include <linux/platform_device.h>31#include <linux/of_address.h>32#include <linux/of.h>33 34struct phram_mtd_list {35	struct mtd_info mtd;36	struct list_head list;37	bool cached;38};39 40static LIST_HEAD(phram_list);41 42static int phram_erase(struct mtd_info *mtd, struct erase_info *instr)43{44	u_char *start = mtd->priv;45 46	memset(start + instr->addr, 0xff, instr->len);47 48	return 0;49}50 51static int phram_point(struct mtd_info *mtd, loff_t from, size_t len,52		size_t *retlen, void **virt, resource_size_t *phys)53{54	*virt = mtd->priv + from;55	*retlen = len;56	return 0;57}58 59static int phram_unpoint(struct mtd_info *mtd, loff_t from, size_t len)60{61	return 0;62}63 64static int phram_read(struct mtd_info *mtd, loff_t from, size_t len,65		size_t *retlen, u_char *buf)66{67	u_char *start = mtd->priv;68 69	memcpy(buf, start + from, len);70	*retlen = len;71	return 0;72}73 74static int phram_write(struct mtd_info *mtd, loff_t to, size_t len,75		size_t *retlen, const u_char *buf)76{77	u_char *start = mtd->priv;78 79	memcpy(start + to, buf, len);80	*retlen = len;81	return 0;82}83 84static int phram_map(struct phram_mtd_list *phram, phys_addr_t start, size_t len)85{86	void *addr = NULL;87 88	if (phram->cached)89		addr = memremap(start, len, MEMREMAP_WB);90	else91		addr = (void __force *)ioremap(start, len);92	if (!addr)93		return -EIO;94 95	phram->mtd.priv = addr;96 97	return 0;98}99 100static void phram_unmap(struct phram_mtd_list *phram)101{102	void *addr = phram->mtd.priv;103 104	if (phram->cached) {105		memunmap(addr);106		return;107	}108 109	iounmap((void __iomem *)addr);110}111 112static void unregister_devices(void)113{114	struct phram_mtd_list *this, *safe;115 116	list_for_each_entry_safe(this, safe, &phram_list, list) {117		mtd_device_unregister(&this->mtd);118		phram_unmap(this);119		kfree(this->mtd.name);120		kfree(this);121	}122}123 124static int register_device(struct platform_device *pdev, const char *name,125			   phys_addr_t start, size_t len, uint32_t erasesize)126{127	struct device_node *np = pdev ? pdev->dev.of_node : NULL;128	bool cached = np ? !of_property_read_bool(np, "no-map") : false;129	struct phram_mtd_list *new;130	int ret = -ENOMEM;131 132	new = kzalloc(sizeof(*new), GFP_KERNEL);133	if (!new)134		goto out0;135 136	new->cached = cached;137 138	ret = phram_map(new, start, len);139	if (ret) {140		pr_err("ioremap failed\n");141		goto out1;142	}143 144 145	new->mtd.name = name;146	new->mtd.size = len;147	new->mtd.flags = MTD_CAP_RAM;148	new->mtd._erase = phram_erase;149	new->mtd._point = phram_point;150	new->mtd._unpoint = phram_unpoint;151	new->mtd._read = phram_read;152	new->mtd._write = phram_write;153	new->mtd.owner = THIS_MODULE;154	new->mtd.type = MTD_RAM;155	new->mtd.erasesize = erasesize;156	new->mtd.writesize = 1;157 158	mtd_set_of_node(&new->mtd, np);159 160	ret = -EAGAIN;161	if (mtd_device_register(&new->mtd, NULL, 0)) {162		pr_err("Failed to register new device\n");163		goto out2;164	}165 166	if (pdev)167		platform_set_drvdata(pdev, new);168	else169		list_add_tail(&new->list, &phram_list);170 171	return 0;172 173out2:174	phram_unmap(new);175out1:176	kfree(new);177out0:178	return ret;179}180 181static int parse_num64(uint64_t *num64, char *token)182{183	size_t len;184	int shift = 0;185	int ret;186 187	len = strlen(token);188	/* By dwmw2 editorial decree, "ki", "Mi" or "Gi" are to be used. */189	if (len > 2) {190		if (token[len - 1] == 'i') {191			switch (token[len - 2]) {192			case 'G':193				shift += 10;194				fallthrough;195			case 'M':196				shift += 10;197				fallthrough;198			case 'k':199				shift += 10;200				token[len - 2] = 0;201				break;202			default:203				return -EINVAL;204			}205		}206	}207 208	ret = kstrtou64(token, 0, num64);209	*num64 <<= shift;210 211	return ret;212}213 214static int parse_name(char **pname, const char *token)215{216	size_t len;217	char *name;218 219	len = strlen(token) + 1;220	if (len > 64)221		return -ENOSPC;222 223	name = kstrdup(token, GFP_KERNEL);224	if (!name)225		return -ENOMEM;226 227	*pname = name;228	return 0;229}230 231 232static inline void kill_final_newline(char *str)233{234	char *newline = strrchr(str, '\n');235 236	if (newline && !newline[1])237		*newline = 0;238}239 240 241#define parse_err(fmt, args...) do {	\242	pr_err(fmt , ## args);	\243	return 1;		\244} while (0)245 246#ifndef MODULE247static int phram_init_called;248/*249 * This shall contain the module parameter if any. It is of the form:250 * - phram=<device>,<address>,<size>[,<erasesize>] for module case251 * - phram.phram=<device>,<address>,<size>[,<erasesize>] for built-in case252 * We leave 64 bytes for the device name, 20 for the address , 20 for the253 * size and 20 for the erasesize.254 * Example: phram.phram=rootfs,0xa0000000,512Mi,65536255 */256static char phram_paramline[64 + 20 + 20 + 20];257#endif258 259static int phram_setup(const char *val)260{261	char buf[64 + 20 + 20 + 20], *str = buf;262	char *token[4];263	char *name;264	uint64_t start;265	uint64_t len;266	uint64_t erasesize = PAGE_SIZE;267	uint32_t rem;268	int i, ret;269 270	if (strnlen(val, sizeof(buf)) >= sizeof(buf))271		parse_err("parameter too long\n");272 273	strcpy(str, val);274	kill_final_newline(str);275 276	for (i = 0; i < 4; i++)277		token[i] = strsep(&str, ",");278 279	if (str)280		parse_err("too many arguments\n");281 282	if (!token[2])283		parse_err("not enough arguments\n");284 285	ret = parse_name(&name, token[0]);286	if (ret)287		return ret;288 289	ret = parse_num64(&start, token[1]);290	if (ret) {291		parse_err("illegal start address\n");292		goto error;293	}294 295	ret = parse_num64(&len, token[2]);296	if (ret) {297		parse_err("illegal device length\n");298		goto error;299	}300 301	if (token[3]) {302		ret = parse_num64(&erasesize, token[3]);303		if (ret) {304			parse_err("illegal erasesize\n");305			goto error;306		}307	}308 309	if (len == 0 || erasesize == 0 || erasesize > len310	    || erasesize > UINT_MAX) {311		parse_err("illegal erasesize or len\n");312		ret = -EINVAL;313		goto error;314	}315 316	div_u64_rem(len, (uint32_t)erasesize, &rem);317	if (rem) {318		parse_err("len is not multiple of erasesize\n");319		ret = -EINVAL;320		goto error;321	}322 323	ret = register_device(NULL, name, start, len, (uint32_t)erasesize);324	if (ret)325		goto error;326 327	pr_info("%s device: %#llx at %#llx for erasesize %#llx\n", name, len, start, erasesize);328	return 0;329 330error:331	kfree(name);332	return ret;333}334 335static int phram_param_call(const char *val, const struct kernel_param *kp)336{337#ifdef MODULE338	return phram_setup(val);339#else340	/*341	 * If more parameters are later passed in via342	 * /sys/module/phram/parameters/phram343	 * and init_phram() has already been called,344	 * we can parse the argument now.345	 */346 347	if (phram_init_called)348		return phram_setup(val);349 350	/*351	 * During early boot stage, we only save the parameters352	 * here. We must parse them later: if the param passed353	 * from kernel boot command line, phram_param_call() is354	 * called so early that it is not possible to resolve355	 * the device (even kmalloc() fails). Defer that work to356	 * phram_setup().357	 */358 359	if (strlen(val) >= sizeof(phram_paramline))360		return -ENOSPC;361	strcpy(phram_paramline, val);362 363	return 0;364#endif365}366 367module_param_call(phram, phram_param_call, NULL, NULL, 0200);368MODULE_PARM_DESC(phram, "Memory region to map. \"phram=<name>,<start>,<length>[,<erasesize>]\"");369 370#ifdef CONFIG_OF371static const struct of_device_id phram_of_match[] = {372	{ .compatible = "phram" },373	{}374};375MODULE_DEVICE_TABLE(of, phram_of_match);376#endif377 378static int phram_probe(struct platform_device *pdev)379{380	struct resource *res;381 382	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);383	if (!res)384		return -ENOMEM;385 386	/* mtd_set_of_node() reads name from "label" */387	return register_device(pdev, NULL, res->start, resource_size(res),388			       PAGE_SIZE);389}390 391static void phram_remove(struct platform_device *pdev)392{393	struct phram_mtd_list *phram = platform_get_drvdata(pdev);394 395	mtd_device_unregister(&phram->mtd);396	phram_unmap(phram);397	kfree(phram);398}399 400static struct platform_driver phram_driver = {401	.probe		= phram_probe,402	.remove_new	= phram_remove,403	.driver		= {404		.name		= "phram",405		.of_match_table	= of_match_ptr(phram_of_match),406	},407};408 409static int __init init_phram(void)410{411	int ret;412 413	ret = platform_driver_register(&phram_driver);414	if (ret)415		return ret;416 417#ifndef MODULE418	if (phram_paramline[0])419		ret = phram_setup(phram_paramline);420	phram_init_called = 1;421#endif422 423	if (ret)424		platform_driver_unregister(&phram_driver);425 426	return ret;427}428 429static void __exit cleanup_phram(void)430{431	unregister_devices();432	platform_driver_unregister(&phram_driver);433}434 435module_init(init_phram);436module_exit(cleanup_phram);437 438MODULE_LICENSE("GPL");439MODULE_AUTHOR("Joern Engel <joern@wh.fh-wedel.de>");440MODULE_DESCRIPTION("MTD driver for physical RAM");441