xref: /linux/drivers/mtd/devices/block2mtd.c (revision 36ec807b627b4c0a0a382f0ae48eac7187d14b2b)
11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds  * block2mtd.c - create an mtd from a block device
31da177e4SLinus Torvalds  *
41da177e4SLinus Torvalds  * Copyright (C) 2001,2002	Simon Evans <spse@secret.org.uk>
52b54aaefSJoern Engel  * Copyright (C) 2004-2006	Joern Engel <joern@wh.fh-wedel.de>
61da177e4SLinus Torvalds  *
71da177e4SLinus Torvalds  * Licence: GPL
81da177e4SLinus Torvalds  */
9a1c06609SJoe Perches 
10a1c06609SJoe Perches #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
11a1c06609SJoe Perches 
12d6a3f017SFelix Fietkau /*
13d6a3f017SFelix Fietkau  * When the first attempt at device initialization fails, we may need to
14d6a3f017SFelix Fietkau  * wait a little bit and retry. This timeout, by default 3 seconds, gives
15d6a3f017SFelix Fietkau  * device time to start up. Required on BCM2708 and a few other chipsets.
16d6a3f017SFelix Fietkau  */
17d6a3f017SFelix Fietkau #define MTD_DEFAULT_TIMEOUT	3
18d6a3f017SFelix Fietkau 
191da177e4SLinus Torvalds #include <linux/module.h>
20d6a3f017SFelix Fietkau #include <linux/delay.h>
211da177e4SLinus Torvalds #include <linux/fs.h>
221da177e4SLinus Torvalds #include <linux/blkdev.h>
2366114cadSTejun Heo #include <linux/backing-dev.h>
241da177e4SLinus Torvalds #include <linux/bio.h>
251da177e4SLinus Torvalds #include <linux/pagemap.h>
261da177e4SLinus Torvalds #include <linux/list.h>
271da177e4SLinus Torvalds #include <linux/init.h>
281da177e4SLinus Torvalds #include <linux/mtd/mtd.h>
2948b19268SIngo Molnar #include <linux/mutex.h>
30c4e7fb31SVille Herva #include <linux/mount.h>
315a0e3ad6STejun Heo #include <linux/slab.h>
32f83c3838SEzequiel Garcia #include <linux/major.h>
331da177e4SLinus Torvalds 
34a04e9653SJoachim Wiberg /* Maximum number of comma-separated items in the 'block2mtd=' parameter */
357b09acdcSJoachim Wiberg #define BLOCK2MTD_PARAM_MAX_COUNT 3
36a04e9653SJoachim Wiberg 
371da177e4SLinus Torvalds /* Info for the block device */
381da177e4SLinus Torvalds struct block2mtd_dev {
391da177e4SLinus Torvalds 	struct list_head list;
40f9d8c3c4SChristian Brauner 	struct file *bdev_file;
411da177e4SLinus Torvalds 	struct mtd_info mtd;
4248b19268SIngo Molnar 	struct mutex write_mutex;
431da177e4SLinus Torvalds };
441da177e4SLinus Torvalds 
451da177e4SLinus Torvalds 
461da177e4SLinus Torvalds /* Static info about the MTD, used in cleanup_module */
471da177e4SLinus Torvalds static LIST_HEAD(blkmtd_device_list);
481da177e4SLinus Torvalds 
491da177e4SLinus Torvalds 
50c3917a04SLiu Song static struct page *page_read(struct address_space *mapping, pgoff_t index)
511da177e4SLinus Torvalds {
526fe6900eSNick Piggin 	return read_mapping_page(mapping, index, NULL);
531da177e4SLinus Torvalds }
541da177e4SLinus Torvalds 
551da177e4SLinus Torvalds /* erase a specified part of the device */
561da177e4SLinus Torvalds static int _block2mtd_erase(struct block2mtd_dev *dev, loff_t to, size_t len)
571da177e4SLinus Torvalds {
58f9d8c3c4SChristian Brauner 	struct address_space *mapping = dev->bdev_file->f_mapping;
591da177e4SLinus Torvalds 	struct page *page;
60c3917a04SLiu Song 	pgoff_t index = to >> PAGE_SHIFT;	// page index
611da177e4SLinus Torvalds 	int pages = len >> PAGE_SHIFT;
621da177e4SLinus Torvalds 	u_long *p;
631da177e4SLinus Torvalds 	u_long *max;
641da177e4SLinus Torvalds 
651da177e4SLinus Torvalds 	while (pages) {
6621d31f1fSJoern Engel 		page = page_read(mapping, index);
671da177e4SLinus Torvalds 		if (IS_ERR(page))
681da177e4SLinus Torvalds 			return PTR_ERR(page);
691da177e4SLinus Torvalds 
700ffb74ccSJoern Engel 		max = page_address(page) + PAGE_SIZE;
710ffb74ccSJoern Engel 		for (p=page_address(page); p<max; p++)
721da177e4SLinus Torvalds 			if (*p != -1UL) {
731da177e4SLinus Torvalds 				lock_page(page);
741da177e4SLinus Torvalds 				memset(page_address(page), 0xff, PAGE_SIZE);
751da177e4SLinus Torvalds 				set_page_dirty(page);
761da177e4SLinus Torvalds 				unlock_page(page);
77031da73fSNeilBrown 				balance_dirty_pages_ratelimited(mapping);
781da177e4SLinus Torvalds 				break;
791da177e4SLinus Torvalds 			}
801da177e4SLinus Torvalds 
8109cbfeafSKirill A. Shutemov 		put_page(page);
821da177e4SLinus Torvalds 		pages--;
831da177e4SLinus Torvalds 		index++;
841da177e4SLinus Torvalds 	}
851da177e4SLinus Torvalds 	return 0;
861da177e4SLinus Torvalds }
871da177e4SLinus Torvalds static int block2mtd_erase(struct mtd_info *mtd, struct erase_info *instr)
881da177e4SLinus Torvalds {
891da177e4SLinus Torvalds 	struct block2mtd_dev *dev = mtd->priv;
901da177e4SLinus Torvalds 	size_t from = instr->addr;
911da177e4SLinus Torvalds 	size_t len = instr->len;
921da177e4SLinus Torvalds 	int err;
931da177e4SLinus Torvalds 
9448b19268SIngo Molnar 	mutex_lock(&dev->write_mutex);
951da177e4SLinus Torvalds 	err = _block2mtd_erase(dev, from, len);
9648b19268SIngo Molnar 	mutex_unlock(&dev->write_mutex);
97e7bfb3fdSBoris Brezillon 	if (err)
98a1c06609SJoe Perches 		pr_err("erase failed err = %d\n", err);
991da177e4SLinus Torvalds 
1001da177e4SLinus Torvalds 	return err;
1011da177e4SLinus Torvalds }
1021da177e4SLinus Torvalds 
1031da177e4SLinus Torvalds 
1041da177e4SLinus Torvalds static int block2mtd_read(struct mtd_info *mtd, loff_t from, size_t len,
1051da177e4SLinus Torvalds 		size_t *retlen, u_char *buf)
1061da177e4SLinus Torvalds {
1071da177e4SLinus Torvalds 	struct block2mtd_dev *dev = mtd->priv;
108f9d8c3c4SChristian Brauner 	struct address_space *mapping = dev->bdev_file->f_mapping;
1091da177e4SLinus Torvalds 	struct page *page;
110c3917a04SLiu Song 	pgoff_t index = from >> PAGE_SHIFT;
111711c11b7SJoern Engel 	int offset = from & (PAGE_SIZE-1);
1121da177e4SLinus Torvalds 	int cpylen;
1131da177e4SLinus Torvalds 
1141da177e4SLinus Torvalds 	while (len) {
1151da177e4SLinus Torvalds 		if ((offset + len) > PAGE_SIZE)
1161da177e4SLinus Torvalds 			cpylen = PAGE_SIZE - offset;	// multiple pages
1171da177e4SLinus Torvalds 		else
1181da177e4SLinus Torvalds 			cpylen = len;	// this page
1191da177e4SLinus Torvalds 		len = len - cpylen;
1201da177e4SLinus Torvalds 
1213817d4b1SJan Kara 		page = page_read(mapping, index);
1221da177e4SLinus Torvalds 		if (IS_ERR(page))
1231da177e4SLinus Torvalds 			return PTR_ERR(page);
1241da177e4SLinus Torvalds 
1251da177e4SLinus Torvalds 		memcpy(buf, page_address(page) + offset, cpylen);
12609cbfeafSKirill A. Shutemov 		put_page(page);
1271da177e4SLinus Torvalds 
1281da177e4SLinus Torvalds 		if (retlen)
1291da177e4SLinus Torvalds 			*retlen += cpylen;
1301da177e4SLinus Torvalds 		buf += cpylen;
1311da177e4SLinus Torvalds 		offset = 0;
1321da177e4SLinus Torvalds 		index++;
1331da177e4SLinus Torvalds 	}
1341da177e4SLinus Torvalds 	return 0;
1351da177e4SLinus Torvalds }
1361da177e4SLinus Torvalds 
1371da177e4SLinus Torvalds 
1381da177e4SLinus Torvalds /* write data to the underlying device */
1391da177e4SLinus Torvalds static int _block2mtd_write(struct block2mtd_dev *dev, const u_char *buf,
1401da177e4SLinus Torvalds 		loff_t to, size_t len, size_t *retlen)
1411da177e4SLinus Torvalds {
1421da177e4SLinus Torvalds 	struct page *page;
143f9d8c3c4SChristian Brauner 	struct address_space *mapping = dev->bdev_file->f_mapping;
144c3917a04SLiu Song 	pgoff_t index = to >> PAGE_SHIFT;	// page index
1451da177e4SLinus Torvalds 	int offset = to & ~PAGE_MASK;	// page offset
1461da177e4SLinus Torvalds 	int cpylen;
1471da177e4SLinus Torvalds 
1481da177e4SLinus Torvalds 	while (len) {
1491da177e4SLinus Torvalds 		if ((offset+len) > PAGE_SIZE)
1501da177e4SLinus Torvalds 			cpylen = PAGE_SIZE - offset;	// multiple pages
1511da177e4SLinus Torvalds 		else
1521da177e4SLinus Torvalds 			cpylen = len;			// this page
1531da177e4SLinus Torvalds 		len = len - cpylen;
1541da177e4SLinus Torvalds 
15521d31f1fSJoern Engel 		page = page_read(mapping, index);
1561da177e4SLinus Torvalds 		if (IS_ERR(page))
1571da177e4SLinus Torvalds 			return PTR_ERR(page);
1581da177e4SLinus Torvalds 
1591da177e4SLinus Torvalds 		if (memcmp(page_address(page)+offset, buf, cpylen)) {
1601da177e4SLinus Torvalds 			lock_page(page);
1611da177e4SLinus Torvalds 			memcpy(page_address(page) + offset, buf, cpylen);
1621da177e4SLinus Torvalds 			set_page_dirty(page);
1631da177e4SLinus Torvalds 			unlock_page(page);
164031da73fSNeilBrown 			balance_dirty_pages_ratelimited(mapping);
1651da177e4SLinus Torvalds 		}
16609cbfeafSKirill A. Shutemov 		put_page(page);
1671da177e4SLinus Torvalds 
1681da177e4SLinus Torvalds 		if (retlen)
1691da177e4SLinus Torvalds 			*retlen += cpylen;
1701da177e4SLinus Torvalds 
1711da177e4SLinus Torvalds 		buf += cpylen;
1721da177e4SLinus Torvalds 		offset = 0;
1731da177e4SLinus Torvalds 		index++;
1741da177e4SLinus Torvalds 	}
1751da177e4SLinus Torvalds 	return 0;
1761da177e4SLinus Torvalds }
177c4e7fb31SVille Herva 
178c4e7fb31SVille Herva 
1791da177e4SLinus Torvalds static int block2mtd_write(struct mtd_info *mtd, loff_t to, size_t len,
1801da177e4SLinus Torvalds 		size_t *retlen, const u_char *buf)
1811da177e4SLinus Torvalds {
1821da177e4SLinus Torvalds 	struct block2mtd_dev *dev = mtd->priv;
1831da177e4SLinus Torvalds 	int err;
1841da177e4SLinus Torvalds 
18548b19268SIngo Molnar 	mutex_lock(&dev->write_mutex);
1861da177e4SLinus Torvalds 	err = _block2mtd_write(dev, buf, to, len, retlen);
18748b19268SIngo Molnar 	mutex_unlock(&dev->write_mutex);
1881da177e4SLinus Torvalds 	if (err > 0)
1891da177e4SLinus Torvalds 		err = 0;
1901da177e4SLinus Torvalds 	return err;
1911da177e4SLinus Torvalds }
1921da177e4SLinus Torvalds 
1931da177e4SLinus Torvalds 
1941da177e4SLinus Torvalds /* sync the device - wait until the write queue is empty */
1951da177e4SLinus Torvalds static void block2mtd_sync(struct mtd_info *mtd)
1961da177e4SLinus Torvalds {
1971da177e4SLinus Torvalds 	struct block2mtd_dev *dev = mtd->priv;
198f9d8c3c4SChristian Brauner 	sync_blockdev(file_bdev(dev->bdev_file));
1991da177e4SLinus Torvalds 	return;
2001da177e4SLinus Torvalds }
2011da177e4SLinus Torvalds 
2021da177e4SLinus Torvalds 
2031da177e4SLinus Torvalds static void block2mtd_free_device(struct block2mtd_dev *dev)
2041da177e4SLinus Torvalds {
2051da177e4SLinus Torvalds 	if (!dev)
2061da177e4SLinus Torvalds 		return;
2071da177e4SLinus Torvalds 
2081da177e4SLinus Torvalds 	kfree(dev->mtd.name);
2091da177e4SLinus Torvalds 
210f9d8c3c4SChristian Brauner 	if (dev->bdev_file) {
211f9d8c3c4SChristian Brauner 		invalidate_mapping_pages(dev->bdev_file->f_mapping, 0, -1);
21222650a99SChristian Brauner 		bdev_fput(dev->bdev_file);
2131da177e4SLinus Torvalds 	}
2141da177e4SLinus Torvalds 
2151da177e4SLinus Torvalds 	kfree(dev);
2161da177e4SLinus Torvalds }
2171da177e4SLinus Torvalds 
2188d03187eSChristoph Hellwig /*
2198d03187eSChristoph Hellwig  * This function is marked __ref because it calls the __init marked
2208d03187eSChristoph Hellwig  * early_lookup_bdev when called from the early boot code.
2218d03187eSChristoph Hellwig  */
222f9d8c3c4SChristian Brauner static struct file __ref *mdtblock_early_get_bdev(const char *devname,
22305bdb996SChristoph Hellwig 		blk_mode_t mode, int timeout, struct block2mtd_dev *dev)
224b2baa574SChristoph Hellwig {
225f9d8c3c4SChristian Brauner 	struct file *bdev_file = ERR_PTR(-ENODEV);
226b2baa574SChristoph Hellwig #ifndef MODULE
227b2baa574SChristoph Hellwig 	int i;
228b2baa574SChristoph Hellwig 
229b2baa574SChristoph Hellwig 	/*
2308d03187eSChristoph Hellwig 	 * We can't use early_lookup_bdev from a running system.
2318d03187eSChristoph Hellwig 	 */
2328d03187eSChristoph Hellwig 	if (system_state >= SYSTEM_RUNNING)
233f9d8c3c4SChristian Brauner 		return bdev_file;
2348d03187eSChristoph Hellwig 
2358d03187eSChristoph Hellwig 	/*
236b2baa574SChristoph Hellwig 	 * We might not have the root device mounted at this point.
237b2baa574SChristoph Hellwig 	 * Try to resolve the device name by other means.
238b2baa574SChristoph Hellwig 	 */
239b2baa574SChristoph Hellwig 	for (i = 0; i <= timeout; i++) {
240b2baa574SChristoph Hellwig 		dev_t devt;
241b2baa574SChristoph Hellwig 
242b2baa574SChristoph Hellwig 		if (i)
243b2baa574SChristoph Hellwig 			/*
244b2baa574SChristoph Hellwig 			 * Calling wait_for_device_probe in the first loop
245b2baa574SChristoph Hellwig 			 * was not enough, sleep for a bit in subsequent
246b2baa574SChristoph Hellwig 			 * go-arounds.
247b2baa574SChristoph Hellwig 			 */
248b2baa574SChristoph Hellwig 			msleep(1000);
249b2baa574SChristoph Hellwig 		wait_for_device_probe();
250b2baa574SChristoph Hellwig 
251b2baa574SChristoph Hellwig 		if (!early_lookup_bdev(devname, &devt)) {
252f9d8c3c4SChristian Brauner 			bdev_file = bdev_file_open_by_dev(devt, mode, dev, NULL);
253f9d8c3c4SChristian Brauner 			if (!IS_ERR(bdev_file))
254b2baa574SChristoph Hellwig 				break;
255b2baa574SChristoph Hellwig 		}
256b2baa574SChristoph Hellwig 	}
257b2baa574SChristoph Hellwig #endif
258f9d8c3c4SChristian Brauner 	return bdev_file;
259b2baa574SChristoph Hellwig }
2601da177e4SLinus Torvalds 
261d6a3f017SFelix Fietkau static struct block2mtd_dev *add_device(char *devname, int erase_size,
2627b09acdcSJoachim Wiberg 		char *label, int timeout)
2631da177e4SLinus Torvalds {
26405bdb996SChristoph Hellwig 	const blk_mode_t mode = BLK_OPEN_READ | BLK_OPEN_WRITE;
265f9d8c3c4SChristian Brauner 	struct file *bdev_file;
266d40a18feSColin Ian King 	struct block_device *bdev;
2671da177e4SLinus Torvalds 	struct block2mtd_dev *dev;
268*c9600c63SYu Kuai 	loff_t size;
269eadcf0d7SGreg Kroah-Hartman 	char *name;
2701da177e4SLinus Torvalds 
2711da177e4SLinus Torvalds 	if (!devname)
2721da177e4SLinus Torvalds 		return NULL;
2731da177e4SLinus Torvalds 
27495b93a0cSBurman Yan 	dev = kzalloc(sizeof(struct block2mtd_dev), GFP_KERNEL);
2751da177e4SLinus Torvalds 	if (!dev)
2761da177e4SLinus Torvalds 		return NULL;
2771da177e4SLinus Torvalds 
2781da177e4SLinus Torvalds 	/* Get a handle on the device */
279f9d8c3c4SChristian Brauner 	bdev_file = bdev_file_open_by_path(devname, mode, dev, NULL);
280f9d8c3c4SChristian Brauner 	if (IS_ERR(bdev_file))
281f9d8c3c4SChristian Brauner 		bdev_file = mdtblock_early_get_bdev(devname, mode, timeout,
2823817d4b1SJan Kara 						      dev);
283f9d8c3c4SChristian Brauner 	if (IS_ERR(bdev_file)) {
284a1c06609SJoe Perches 		pr_err("error: cannot open device %s\n", devname);
2854bed207cSFabian Frederick 		goto err_free_block2mtd;
2861da177e4SLinus Torvalds 	}
287f9d8c3c4SChristian Brauner 	dev->bdev_file = bdev_file;
288f9d8c3c4SChristian Brauner 	bdev = file_bdev(bdev_file);
2891da177e4SLinus Torvalds 
2901da177e4SLinus Torvalds 	if (MAJOR(bdev->bd_dev) == MTD_BLOCK_MAJOR) {
291a1c06609SJoe Perches 		pr_err("attempting to use an MTD device as a block device\n");
2924bed207cSFabian Frederick 		goto err_free_block2mtd;
2931da177e4SLinus Torvalds 	}
2941da177e4SLinus Torvalds 
295*c9600c63SYu Kuai 	size = bdev_nr_bytes(bdev);
296*c9600c63SYu Kuai 	if ((long)size % erase_size) {
297ea6d833aSFabian Frederick 		pr_err("erasesize must be a divisor of device size\n");
298ea6d833aSFabian Frederick 		goto err_free_block2mtd;
299ea6d833aSFabian Frederick 	}
300ea6d833aSFabian Frederick 
30148b19268SIngo Molnar 	mutex_init(&dev->write_mutex);
3021da177e4SLinus Torvalds 
3031da177e4SLinus Torvalds 	/* Setup the MTD structure */
3041da177e4SLinus Torvalds 	/* make the name contain the block device in */
3057b09acdcSJoachim Wiberg 	if (!label)
3064d682420SJulia Lawall 		name = kasprintf(GFP_KERNEL, "block2mtd: %s", devname);
3077b09acdcSJoachim Wiberg 	else
3087b09acdcSJoachim Wiberg 		name = kstrdup(label, GFP_KERNEL);
309eadcf0d7SGreg Kroah-Hartman 	if (!name)
3104bed207cSFabian Frederick 		goto err_destroy_mutex;
3111da177e4SLinus Torvalds 
312eadcf0d7SGreg Kroah-Hartman 	dev->mtd.name = name;
3131da177e4SLinus Torvalds 
314*c9600c63SYu Kuai 	dev->mtd.size = size & PAGE_MASK;
3151da177e4SLinus Torvalds 	dev->mtd.erasesize = erase_size;
31617ffc7baSArtem B. Bityutskiy 	dev->mtd.writesize = 1;
317b6043874SArtem Bityutskiy 	dev->mtd.writebufsize = PAGE_SIZE;
31821c8db9eSDavid Woodhouse 	dev->mtd.type = MTD_RAM;
3191da177e4SLinus Torvalds 	dev->mtd.flags = MTD_CAP_RAM;
3203c3c10bbSArtem Bityutskiy 	dev->mtd._erase = block2mtd_erase;
3213c3c10bbSArtem Bityutskiy 	dev->mtd._write = block2mtd_write;
3223c3c10bbSArtem Bityutskiy 	dev->mtd._sync = block2mtd_sync;
3233c3c10bbSArtem Bityutskiy 	dev->mtd._read = block2mtd_read;
3241da177e4SLinus Torvalds 	dev->mtd.priv = dev;
3251da177e4SLinus Torvalds 	dev->mtd.owner = THIS_MODULE;
3261da177e4SLinus Torvalds 
327ee0e87b1SJamie Iles 	if (mtd_device_register(&dev->mtd, NULL, 0)) {
32825985edcSLucas De Marchi 		/* Device didn't get added, so free the entry */
3294bed207cSFabian Frederick 		goto err_destroy_mutex;
3301da177e4SLinus Torvalds 	}
331d6a3f017SFelix Fietkau 
3321da177e4SLinus Torvalds 	list_add(&dev->list, &blkmtd_device_list);
333a1c06609SJoe Perches 	pr_info("mtd%d: [%s] erase_size = %dKiB [%d]\n",
334a1c06609SJoe Perches 		dev->mtd.index,
3357b09acdcSJoachim Wiberg 		label ? label : dev->mtd.name + strlen("block2mtd: "),
3361da177e4SLinus Torvalds 		dev->mtd.erasesize >> 10, dev->mtd.erasesize);
3371da177e4SLinus Torvalds 	return dev;
3381da177e4SLinus Torvalds 
3394bed207cSFabian Frederick err_destroy_mutex:
3404bed207cSFabian Frederick 	mutex_destroy(&dev->write_mutex);
3414bed207cSFabian Frederick err_free_block2mtd:
3421da177e4SLinus Torvalds 	block2mtd_free_device(dev);
3431da177e4SLinus Torvalds 	return NULL;
3441da177e4SLinus Torvalds }
3451da177e4SLinus Torvalds 
3461da177e4SLinus Torvalds 
347954c2422SJoern Engel /* This function works similar to reguler strtoul.  In addition, it
348954c2422SJoern Engel  * allows some suffixes for a more human-readable number format:
349954c2422SJoern Engel  * ki, Ki, kiB, KiB	- multiply result with 1024
350954c2422SJoern Engel  * Mi, MiB		- multiply result with 1024^2
351954c2422SJoern Engel  * Gi, GiB		- multiply result with 1024^3
352954c2422SJoern Engel  */
3531da177e4SLinus Torvalds static int ustrtoul(const char *cp, char **endp, unsigned int base)
3541da177e4SLinus Torvalds {
3551da177e4SLinus Torvalds 	unsigned long result = simple_strtoul(cp, endp, base);
3561da177e4SLinus Torvalds 	switch (**endp) {
3571da177e4SLinus Torvalds 	case 'G' :
3581da177e4SLinus Torvalds 		result *= 1024;
359025a06c1SMiquel Raynal 		fallthrough;
3601da177e4SLinus Torvalds 	case 'M':
3611da177e4SLinus Torvalds 		result *= 1024;
362025a06c1SMiquel Raynal 		fallthrough;
363954c2422SJoern Engel 	case 'K':
3641da177e4SLinus Torvalds 	case 'k':
3651da177e4SLinus Torvalds 		result *= 1024;
3661da177e4SLinus Torvalds 	/* By dwmw2 editorial decree, "ki", "Mi" or "Gi" are to be used. */
367954c2422SJoern Engel 		if ((*endp)[1] == 'i') {
368954c2422SJoern Engel 			if ((*endp)[2] == 'B')
369954c2422SJoern Engel 				(*endp) += 3;
370954c2422SJoern Engel 			else
3711da177e4SLinus Torvalds 				(*endp) += 2;
3721da177e4SLinus Torvalds 		}
373954c2422SJoern Engel 	}
3741da177e4SLinus Torvalds 	return result;
3751da177e4SLinus Torvalds }
3761da177e4SLinus Torvalds 
3771da177e4SLinus Torvalds 
378cc71229fSThomas Gleixner static int parse_num(size_t *num, const char *token)
3791da177e4SLinus Torvalds {
3801da177e4SLinus Torvalds 	char *endp;
381cc71229fSThomas Gleixner 	size_t n;
3821da177e4SLinus Torvalds 
383cc71229fSThomas Gleixner 	n = (size_t) ustrtoul(token, &endp, 0);
3841da177e4SLinus Torvalds 	if (*endp)
3851da177e4SLinus Torvalds 		return -EINVAL;
3861da177e4SLinus Torvalds 
387cc71229fSThomas Gleixner 	*num = n;
3881da177e4SLinus Torvalds 	return 0;
3891da177e4SLinus Torvalds }
3901da177e4SLinus Torvalds 
3911da177e4SLinus Torvalds 
3921da177e4SLinus Torvalds static inline void kill_final_newline(char *str)
3931da177e4SLinus Torvalds {
3941da177e4SLinus Torvalds 	char *newline = strrchr(str, '\n');
3951da177e4SLinus Torvalds 	if (newline && !newline[1])
3961da177e4SLinus Torvalds 		*newline = 0;
3971da177e4SLinus Torvalds }
3981da177e4SLinus Torvalds 
3991da177e4SLinus Torvalds 
400c4e7fb31SVille Herva #ifndef MODULE
401c4e7fb31SVille Herva static int block2mtd_init_called = 0;
402d6a3f017SFelix Fietkau /* 80 for device, 12 for erase size */
403d6a3f017SFelix Fietkau static char block2mtd_paramline[80 + 12];
404c4e7fb31SVille Herva #endif
405c4e7fb31SVille Herva 
406c4e7fb31SVille Herva static int block2mtd_setup2(const char *val)
4071da177e4SLinus Torvalds {
408d6a3f017SFelix Fietkau 	/* 80 for device, 12 for erase size, 80 for name, 8 for timeout */
409d6a3f017SFelix Fietkau 	char buf[80 + 12 + 80 + 8];
410a6550e57SJesper Juhl 	char *str = buf;
411a04e9653SJoachim Wiberg 	char *token[BLOCK2MTD_PARAM_MAX_COUNT];
4121da177e4SLinus Torvalds 	char *name;
4137b09acdcSJoachim Wiberg 	char *label = NULL;
414cc71229fSThomas Gleixner 	size_t erase_size = PAGE_SIZE;
415d6a3f017SFelix Fietkau 	unsigned long timeout = MTD_DEFAULT_TIMEOUT;
4161da177e4SLinus Torvalds 	int i, ret;
4171da177e4SLinus Torvalds 
418a1c06609SJoe Perches 	if (strnlen(val, sizeof(buf)) >= sizeof(buf)) {
419a1c06609SJoe Perches 		pr_err("parameter too long\n");
420a1c06609SJoe Perches 		return 0;
421a1c06609SJoe Perches 	}
4221da177e4SLinus Torvalds 
4231da177e4SLinus Torvalds 	strcpy(str, val);
4241da177e4SLinus Torvalds 	kill_final_newline(str);
4251da177e4SLinus Torvalds 
426a04e9653SJoachim Wiberg 	for (i = 0; i < BLOCK2MTD_PARAM_MAX_COUNT; i++)
4271da177e4SLinus Torvalds 		token[i] = strsep(&str, ",");
4281da177e4SLinus Torvalds 
429a1c06609SJoe Perches 	if (str) {
430a1c06609SJoe Perches 		pr_err("too many arguments\n");
431a1c06609SJoe Perches 		return 0;
432a1c06609SJoe Perches 	}
4331da177e4SLinus Torvalds 
434a1c06609SJoe Perches 	if (!token[0]) {
435a1c06609SJoe Perches 		pr_err("no argument\n");
436a1c06609SJoe Perches 		return 0;
437a1c06609SJoe Perches 	}
4381da177e4SLinus Torvalds 
439c4e7fb31SVille Herva 	name = token[0];
440a1c06609SJoe Perches 	if (strlen(name) + 1 > 80) {
441a1c06609SJoe Perches 		pr_err("device name too long\n");
442a1c06609SJoe Perches 		return 0;
443a1c06609SJoe Perches 	}
4441da177e4SLinus Torvalds 
4457b09acdcSJoachim Wiberg 	/* Optional argument when custom label is used */
4467b09acdcSJoachim Wiberg 	if (token[1] && strlen(token[1])) {
447cc71229fSThomas Gleixner 		ret = parse_num(&erase_size, token[1]);
448a6550e57SJesper Juhl 		if (ret) {
449a1c06609SJoe Perches 			pr_err("illegal erase size\n");
450a1c06609SJoe Perches 			return 0;
4511da177e4SLinus Torvalds 		}
452a6550e57SJesper Juhl 	}
4531da177e4SLinus Torvalds 
4547b09acdcSJoachim Wiberg 	if (token[2]) {
4557b09acdcSJoachim Wiberg 		label = token[2];
4567b09acdcSJoachim Wiberg 		pr_info("Using custom MTD label '%s' for dev %s\n", label, name);
4577b09acdcSJoachim Wiberg 	}
4587b09acdcSJoachim Wiberg 
4597b09acdcSJoachim Wiberg 	add_device(name, erase_size, label, timeout);
4601da177e4SLinus Torvalds 
4611da177e4SLinus Torvalds 	return 0;
4621da177e4SLinus Torvalds }
4631da177e4SLinus Torvalds 
4641da177e4SLinus Torvalds 
465e4dca7b7SKees Cook static int block2mtd_setup(const char *val, const struct kernel_param *kp)
466c4e7fb31SVille Herva {
467c4e7fb31SVille Herva #ifdef MODULE
468c4e7fb31SVille Herva 	return block2mtd_setup2(val);
469c4e7fb31SVille Herva #else
470c4e7fb31SVille Herva 	/* If more parameters are later passed in via
471c4e7fb31SVille Herva 	   /sys/module/block2mtd/parameters/block2mtd
472c4e7fb31SVille Herva 	   and block2mtd_init() has already been called,
473c4e7fb31SVille Herva 	   we can parse the argument now. */
474c4e7fb31SVille Herva 
475c4e7fb31SVille Herva 	if (block2mtd_init_called)
476c4e7fb31SVille Herva 		return block2mtd_setup2(val);
477c4e7fb31SVille Herva 
478c4e7fb31SVille Herva 	/* During early boot stage, we only save the parameters
479c4e7fb31SVille Herva 	   here. We must parse them later: if the param passed
480c4e7fb31SVille Herva 	   from kernel boot command line, block2mtd_setup() is
481c4e7fb31SVille Herva 	   called so early that it is not possible to resolve
482c4e7fb31SVille Herva 	   the device (even kmalloc() fails). Deter that work to
483c4e7fb31SVille Herva 	   block2mtd_setup2(). */
484c4e7fb31SVille Herva 
48580b7e928SWolfram Sang 	strscpy(block2mtd_paramline, val, sizeof(block2mtd_paramline));
486c4e7fb31SVille Herva 
487c4e7fb31SVille Herva 	return 0;
488c4e7fb31SVille Herva #endif
489c4e7fb31SVille Herva }
490c4e7fb31SVille Herva 
491c4e7fb31SVille Herva 
4921da177e4SLinus Torvalds module_param_call(block2mtd, block2mtd_setup, NULL, NULL, 0200);
4937b09acdcSJoachim Wiberg MODULE_PARM_DESC(block2mtd, "Device to use. \"block2mtd=<dev>[,[<erasesize>][,<label>]]\"");
4941da177e4SLinus Torvalds 
4951da177e4SLinus Torvalds static int __init block2mtd_init(void)
4961da177e4SLinus Torvalds {
497c4e7fb31SVille Herva 	int ret = 0;
498c4e7fb31SVille Herva 
499c4e7fb31SVille Herva #ifndef MODULE
500c4e7fb31SVille Herva 	if (strlen(block2mtd_paramline))
501c4e7fb31SVille Herva 		ret = block2mtd_setup2(block2mtd_paramline);
502c4e7fb31SVille Herva 	block2mtd_init_called = 1;
503c4e7fb31SVille Herva #endif
504c4e7fb31SVille Herva 
505c4e7fb31SVille Herva 	return ret;
5061da177e4SLinus Torvalds }
5071da177e4SLinus Torvalds 
5081da177e4SLinus Torvalds 
509810b7e06SBill Pemberton static void block2mtd_exit(void)
5101da177e4SLinus Torvalds {
5111da177e4SLinus Torvalds 	struct list_head *pos, *next;
5121da177e4SLinus Torvalds 
5131da177e4SLinus Torvalds 	/* Remove the MTD devices */
5141da177e4SLinus Torvalds 	list_for_each_safe(pos, next, &blkmtd_device_list) {
5151da177e4SLinus Torvalds 		struct block2mtd_dev *dev = list_entry(pos, typeof(*dev), list);
5161da177e4SLinus Torvalds 		block2mtd_sync(&dev->mtd);
517ee0e87b1SJamie Iles 		mtd_device_unregister(&dev->mtd);
5184bed207cSFabian Frederick 		mutex_destroy(&dev->write_mutex);
519a1c06609SJoe Perches 		pr_info("mtd%d: [%s] removed\n",
520a1c06609SJoe Perches 			dev->mtd.index,
5210bc88c59SStephane Chazelas 			dev->mtd.name + strlen("block2mtd: "));
5221da177e4SLinus Torvalds 		list_del(&dev->list);
5231da177e4SLinus Torvalds 		block2mtd_free_device(dev);
5241da177e4SLinus Torvalds 	}
5251da177e4SLinus Torvalds }
5261da177e4SLinus Torvalds 
527d6a3f017SFelix Fietkau late_initcall(block2mtd_init);
5281da177e4SLinus Torvalds module_exit(block2mtd_exit);
5291da177e4SLinus Torvalds 
5301da177e4SLinus Torvalds MODULE_LICENSE("GPL");
5312b54aaefSJoern Engel MODULE_AUTHOR("Joern Engel <joern@lazybastard.org>");
5321da177e4SLinus Torvalds MODULE_DESCRIPTION("Emulate an MTD using a block device");
533