aboutsummaryrefslogtreecommitdiffstats
path: root/target/linux/generic/patches-3.9/031-PCI-Remove-__weak-annotation-from-pcibios_get_phb_of.patch
blob: 20785dda14af5cb6a3914fb78fcde5a43649e2a9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
From 10629d711ed780470937ecda50d9ffa0e925a4ee Mon Sep 17 00:00:00 2001
From: Bjorn Helgaas <bhelgaas@google.com>
Date: Wed, 10 Apr 2013 09:56:54 -0600
Subject: [PATCH 1/2] PCI: Remove __weak annotation from
 pcibios_get_phb_of_node decl

The __weak annotation on the pcibios_get_phb_of_node() declaration
causes *every* definition to be marked "weak."  The linker then
selects one based on link order, which may be the wrong one.

Gabor found that on MIPS, the linker selected the generic implementation
from drivers/pci even though arch/mips supplied a definition without the
__weak annotation:

$ mipsel-openwrt-linux-readelf -s arch/mips/pci/built-in.o \
    drivers/pci/built-in.o vmlinux.o | grep pcibios_get_phb_of_node
      86: 0000046c    12 FUNC    WEAK   DEFAULT    2 pcibios_get_phb_of_node
    1430: 00012e2c   104 FUNC    WEAK   DEFAULT    2 pcibios_get_phb_of_node
   31898: 0017e4ec   104 FUNC    WEAK   DEFAULT    2 pcibios_get_phb_of_node

This removes the __weak annotation from the pcibios_get_phb_of_node()
declaration so arch-specific non-weak implementations work reliably.

Suggested-by: Gabor Juhos <juhosg@openwrt.org>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
---
 include/linux/pci.h |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -1824,7 +1824,7 @@ extern void pci_set_bus_of_node(struct p
 extern void pci_release_bus_of_node(struct pci_bus *bus);
 
 /* Arch may override this (weak) */
-extern struct device_node * __weak pcibios_get_phb_of_node(struct pci_bus *bus);
+extern struct device_node *pcibios_get_phb_of_node(struct pci_bus *bus);
 
 static inline struct device_node *
 pci_device_to_OF_node(const struct pci_dev *pdev)
277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627
--- a/drivers/mtd/Kconfig
+++ b/drivers/mtd/Kconfig
@@ -53,6 +53,16 @@ config MTD_PARTITIONS
 	  devices. Partitioning on NFTL 'devices' is a different - that's the
 	  'normal' form of partitioning used on a block device.
 
+config MTD_ROOTFS_ROOT_DEV
+	bool "Automatically set 'rootfs' partition to be root filesystem"
+	depends on MTD_PARTITIONS
+	default y
+
+config MTD_ROOTFS_SPLIT
+	bool "Automatically split 'rootfs' partition for squashfs"
+	depends on MTD_PARTITIONS
+	default y
+
 config MTD_REDBOOT_PARTS
 	tristate "RedBoot partition table parsing"
 	depends on MTD_PARTITIONS
--- a/drivers/mtd/mtdpart.c
+++ b/drivers/mtd/mtdpart.c
@@ -29,6 +29,8 @@
 #include <linux/kmod.h>
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/partitions.h>
+#include <linux/root_dev.h>
+#include <linux/magic.h>
 
 /* Our partition linked list */
 static LIST_HEAD(mtd_partitions);
@@ -46,7 +48,7 @@ struct mtd_part {
  * the pointer to that structure with this macro.
  */
 #define PART(x)  ((struct mtd_part *)(x))
-
+#define IS_PART(mtd) (mtd->read == part_read)
 
 /*
  * MTD methods which simply translate the effective address and pass through
@@ -524,6 +526,152 @@ out_register:
 	return slave;
 }
 
+#ifdef CONFIG_MTD_ROOTFS_SPLIT
+#define ROOTFS_SPLIT_NAME "rootfs_data"
+#define ROOTFS_REMOVED_NAME "<removed>"
+
+struct squashfs_super_block {
+	__le32 s_magic;
+	__le32 pad0[9];
+	__le64 bytes_used;
+};
+
+
+static int split_squashfs(struct mtd_info *master, int offset, int *split_offset)
+{
+	struct squashfs_super_block sb;
+	int len, ret;
+
+	ret = master->read(master, offset, sizeof(sb), &len, (void *) &sb);
+	if (ret || (len != sizeof(sb))) {
+		printk(KERN_ALERT "split_squashfs: error occured while reading "
+			"from \"%s\"\n", master->name);
+		return -EINVAL;
+	}
+
+	if (SQUASHFS_MAGIC != le32_to_cpu(sb.s_magic) ) {
+		printk(KERN_ALERT "split_squashfs: no squashfs found in \"%s\"\n",
+			master->name);
+		*split_offset = 0;
+		return 0;
+	}
+
+	if (le64_to_cpu((sb.bytes_used)) <= 0) {
+		printk(KERN_ALERT "split_squashfs: squashfs is empty in \"%s\"\n",
+			master->name);
+		*split_offset = 0;
+		return 0;
+	}
+
+	len = (u32) le64_to_cpu(sb.bytes_used);
+	len += (offset & 0x000fffff);
+	len +=  (master->erasesize - 1);
+	len &= ~(master->erasesize - 1);
+	len -= (offset & 0x000fffff);
+	*split_offset = offset + len;
+
+	return 0;
+}
+
+static int split_rootfs_data(struct mtd_info *master, struct mtd_info *rpart, const struct mtd_partition *part)
+{
+	struct mtd_partition *dpart;
+	struct mtd_part *slave = NULL;
+	struct mtd_part *spart;
+	int split_offset = 0;
+	int ret;
+
+	spart = PART(rpart);
+	ret = split_squashfs(master, spart->offset, &split_offset);
+	if (ret)
+		return ret;
+
+	if (split_offset <= 0)
+		return 0;
+
+	dpart = kmalloc(sizeof(*part)+sizeof(ROOTFS_SPLIT_NAME)+1, GFP_KERNEL);
+	if (dpart == NULL) {
+		printk(KERN_INFO "split_squashfs: no memory for partition \"%s\"\n",
+			ROOTFS_SPLIT_NAME);
+		return -ENOMEM;
+	}
+
+	memcpy(dpart, part, sizeof(*part));
+	dpart->name = (unsigned char *)&dpart[1];
+	strcpy(dpart->name, ROOTFS_SPLIT_NAME);
+
+	dpart->size = rpart->size - (split_offset - spart->offset);
+	dpart->offset = split_offset;
+
+	if (dpart == NULL)
+		return 1;
+
+	printk(KERN_INFO "mtd: partition \"%s\" created automatically, ofs=%llX, len=%llX \n",
+		ROOTFS_SPLIT_NAME, dpart->offset, dpart->size);
+
+	slave = add_one_partition(master, dpart, 0, split_offset);
+	if (!slave) {
+		kfree(dpart);
+		return -ENOMEM;
+	}
+	rpart->split = &slave->mtd;
+
+	return 0;
+}
+
+static int refresh_rootfs_split(struct mtd_info *mtd)
+{
+	struct mtd_partition tpart;
+	struct mtd_part *part;
+	char *name;
+	//int index = 0;
+	int offset, size;
+	int ret;
+
+	part = PART(mtd);
+
+	/* check for the new squashfs offset first */
+	ret = split_squashfs(part->master, part->offset, &offset);
+	if (ret)
+		return ret;
+
+	if ((offset > 0) && !mtd->split) {
+		printk(KERN_INFO "%s: creating new split partition for \"%s\"\n", __func__, mtd->name);
+		/* if we don't have a rootfs split partition, create a new one */
+		tpart.name = (char *) mtd->name;
+		tpart.size = mtd->size;
+		tpart.offset = part->offset;
+
+		return split_rootfs_data(part->master, &part->mtd, &tpart);
+	} else if ((offset > 0) && mtd->split) {
+		/* update the offsets of the existing partition */
+		size = mtd->size + part->offset - offset;
+
+		part = PART(mtd->split);
+		part->offset = offset;
+		part->mtd.size = size;
+		printk(KERN_INFO "%s: %s partition \"" ROOTFS_SPLIT_NAME "\", offset: 0x%06x (0x%06x)\n",
+			__func__, (!strcmp(part->mtd.name, ROOTFS_SPLIT_NAME) ? "updating" : "creating"),
+			(u32) part->offset, (u32) part->mtd.size);
+		name = kmalloc(sizeof(ROOTFS_SPLIT_NAME) + 1, GFP_KERNEL);
+		strcpy(name, ROOTFS_SPLIT_NAME);
+		part->mtd.name = name;
+	} else if ((offset <= 0) && mtd->split) {
+		printk(KERN_INFO "%s: removing partition \"%s\"\n", __func__, mtd->split->name);
+
+		/* mark existing partition as removed */
+		part = PART(mtd->split);
+		name = kmalloc(sizeof(ROOTFS_SPLIT_NAME) + 1, GFP_KERNEL);
+		strcpy(name, ROOTFS_REMOVED_NAME);
+		part->mtd.name = name;
+		part->offset = 0;
+		part->mtd.size = 0;
+	}
+
+	return 0;
+}
+#endif /* CONFIG_MTD_ROOTFS_SPLIT */
+
 /*
  * This function, given a master MTD object and a partition table, creates
  * and registers slave MTD objects which are bound to the master according to
@@ -539,7 +685,7 @@ int add_mtd_partitions(struct mtd_info *
 {
 	struct mtd_part *slave;
 	uint64_t cur_offset = 0;
-	int i;
+	int i, ret;
 
 	printk(KERN_NOTICE "Creating %d MTD partitions on \"%s\":\n", nbparts, master->name);
 
@@ -547,6 +693,21 @@ int add_mtd_partitions(struct mtd_info *
 		slave = add_one_partition(master, parts + i, i, cur_offset);
 		if (!slave)
 			return -ENOMEM;
+
+		if (!strcmp(parts[i].name, "rootfs")) {
+#ifdef CONFIG_MTD_ROOTFS_ROOT_DEV
+			if (ROOT_DEV == 0) {
+				printk(KERN_NOTICE "mtd: partition \"rootfs\" "
+					"set to be root filesystem\n");
+				ROOT_DEV = MKDEV(MTD_BLOCK_MAJOR, slave->mtd.index);
+			}
+#endif
+#ifdef CONFIG_MTD_ROOTFS_SPLIT
+			ret = split_rootfs_data(master, &slave->mtd, &parts[i]);
+			/* if (ret == 0)
+				j++; */
+#endif
+		}
 		cur_offset = slave->offset + slave->mtd.size;
 	}
 
@@ -554,6 +715,32 @@ int add_mtd_partitions(struct mtd_info *
 }
 EXPORT_SYMBOL(add_mtd_partitions);
 
+int refresh_mtd_partitions(struct mtd_info *mtd)
+{
+	int ret = 0;
+
+	if (IS_PART(mtd)) {
+		struct mtd_part *part;
+		struct mtd_info *master;
+
+		part = PART(mtd);
+		master = part->master;
+		if (master->refresh_device)
+			ret = master->refresh_device(master);
+	}
+
+	if (!ret && mtd->refresh_device)
+		ret = mtd->refresh_device(mtd);
+
+#ifdef CONFIG_MTD_ROOTFS_SPLIT
+	if (!ret && IS_PART(mtd) && !strcmp(mtd->name, "rootfs"))
+		refresh_rootfs_split(mtd);
+#endif
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(refresh_mtd_partitions);
+
 static DEFINE_SPINLOCK(part_parser_lock);
 static LIST_HEAD(part_parsers);
 
--- a/drivers/mtd/devices/block2mtd.c
+++ b/drivers/mtd/devices/block2mtd.c
@@ -30,6 +30,8 @@ struct block2mtd_dev {
 	struct block_device *blkdev;
 	struct mtd_info mtd;
 	struct mutex write_mutex;
+	rwlock_t bdev_mutex;
+	char devname[0];
 };
 
 
@@ -82,6 +84,12 @@ static int block2mtd_erase(struct mtd_in
 	size_t len = instr->len;
 	int err;
 
+	read_lock(&dev->bdev_mutex);
+	if (!dev->blkdev) {
+		err = -EINVAL;
+		goto done;
+	}
+
 	instr->state = MTD_ERASING;
 	mutex_lock(&dev->write_mutex);
 	err = _block2mtd_erase(dev, from, len);
@@ -94,6 +102,10 @@ static int block2mtd_erase(struct mtd_in
 
 	instr->state = MTD_ERASE_DONE;
 	mtd_erase_callback(instr);
+
+done:
+	read_unlock(&dev->bdev_mutex);
+
 	return err;
 }
 
@@ -105,10 +117,14 @@ static int block2mtd_read(struct mtd_inf
 	struct page *page;
 	int index = from >> PAGE_SHIFT;
 	int offset = from & (PAGE_SIZE-1);
-	int cpylen;
+	int cpylen, err = 0;
+
+	read_lock(&dev->bdev_mutex);
+	if (!dev->blkdev || (from > mtd->size)) {
+		err = -EINVAL;
+		goto done;
+	}
 
-	if (from > mtd->size)
-		return -EINVAL;
 	if (from + len > mtd->size)
 		len = mtd->size - from;
 
@@ -123,10 +139,14 @@ static int block2mtd_read(struct mtd_inf
 		len = len - cpylen;
 
 		page = page_read(dev->blkdev->bd_inode->i_mapping, index);
-		if (!page)
-			return -ENOMEM;
-		if (IS_ERR(page))
-			return PTR_ERR(page);
+		if (!page) {
+			err = -ENOMEM;
+			goto done;
+		}
+		if (IS_ERR(page)) {
+			err = PTR_ERR(page);
+			goto done;
+		}
 
 		memcpy(buf, page_address(page) + offset, cpylen);
 		page_cache_release(page);
@@ -137,7 +157,10 @@ static int block2mtd_read(struct mtd_inf
 		offset = 0;
 		index++;
 	}
-	return 0;
+
+done:
+	read_unlock(&dev->bdev_mutex);
+	return err;
 }
 
 
@@ -189,12 +212,22 @@ static int block2mtd_write(struct mtd_in
 		size_t *retlen, const u_char *buf)
 {
 	struct block2mtd_dev *dev = mtd->priv;
-	int err;
+	int err = 0;
+
+	read_lock(&dev->bdev_mutex);
+	if (!dev->blkdev) {
+		err = -EINVAL;
+		goto done;
+	}
 
 	if (!len)
-		return 0;
-	if (to >= mtd->size)
-		return -ENOSPC;
+		goto done;
+
+	if (to >= mtd->size) {
+		err = -ENOSPC;
+		goto done;
+	}
+
 	if (to + len > mtd->size)
 		len = mtd->size - to;
 
@@ -203,6 +236,9 @@ static int block2mtd_write(struct mtd_in
 	mutex_unlock(&dev->write_mutex);
 	if (err > 0)
 		err = 0;
+
+done:
+	read_unlock(&dev->bdev_mutex);
 	return err;
 }
 
@@ -211,52 +247,29 @@ static int block2mtd_write(struct mtd_in
 static void block2mtd_sync(struct mtd_info *mtd)
 {
 	struct block2mtd_dev *dev = mtd->priv;
-	sync_blockdev(dev->blkdev);
-	return;
-}
-
-
-static void block2mtd_free_device(struct block2mtd_dev *dev)
-{
-	if (!dev)
-		return;
-
-	kfree(dev->mtd.name);
 
-	if (dev->blkdev) {
-		invalidate_mapping_pages(dev->blkdev->bd_inode->i_mapping,
-					0, -1);
-		close_bdev_exclusive(dev->blkdev, FMODE_READ|FMODE_WRITE);
-	}
+	read_lock(&dev->bdev_mutex);
+	if (dev->blkdev)
+		sync_blockdev(dev->blkdev);
+	read_unlock(&dev->bdev_mutex);
 
-	kfree(dev);
+	return;
 }
 
 
-/* FIXME: ensure that mtd->size % erase_size == 0 */
-static struct block2mtd_dev *add_device(char *devname, int erase_size, const char *mtdname)
+static int _open_bdev(struct block2mtd_dev *dev)
 {
 	struct block_device *bdev;
-	struct block2mtd_dev *dev;
-	struct mtd_partition *part;
-	char *name;
-
-	if (!devname)
-		return NULL;
-
-	dev = kzalloc(sizeof(struct block2mtd_dev), GFP_KERNEL);
-	if (!dev)
-		return NULL;
 
 	/* Get a handle on the device */
-	bdev = open_bdev_exclusive(devname, FMODE_READ|FMODE_WRITE, NULL);
+	bdev = open_bdev_exclusive(dev->devname, FMODE_READ|FMODE_WRITE, NULL);
 #ifndef MODULE
 	if (IS_ERR(bdev)) {
 
 		/* We might not have rootfs mounted at this point. Try
 		   to resolve the device name by other means. */
 
-		dev_t devt = name_to_dev_t(devname);
+		dev_t devt = name_to_dev_t(dev->devname);
 		if (devt) {
 			bdev = open_by_devnum(devt, FMODE_WRITE | FMODE_READ);
 		}
@@ -264,17 +277,98 @@ static struct block2mtd_dev *add_device(
 #endif
 
 	if (IS_ERR(bdev)) {
-		ERROR("error: cannot open device %s", devname);
-		goto devinit_err;
+		ERROR("error: cannot open device %s", dev->devname);
+		return 1;
 	}
 	dev->blkdev = bdev;
 
 	if (MAJOR(bdev->bd_dev) == MTD_BLOCK_MAJOR) {
 		ERROR("attempting to use an MTD device as a block device");
-		goto devinit_err;
+		return 1;
 	}
 
+	return 0;
+}
+
+static void _close_bdev(struct block2mtd_dev *dev)
+{
+	struct block_device *bdev;
+
+	if (!dev->blkdev)
+		return;
+
+	bdev = dev->blkdev;
+	invalidate_mapping_pages(dev->blkdev->bd_inode->i_mapping, 0, -1);
+	close_bdev_exclusive(dev->blkdev, FMODE_READ|FMODE_WRITE);
+	dev->blkdev = NULL;
+}
+
+static void block2mtd_free_device(struct block2mtd_dev *dev)
+{
+	if (!dev)
+		return;
+
+	kfree(dev->mtd.name);
+	_close_bdev(dev);
+	kfree(dev);
+}
+
+
+static int block2mtd_refresh(struct mtd_info *mtd)
+{
+	struct block2mtd_dev *dev = mtd->priv;
+	struct block_device *bdev;
+	dev_t devt;
+	int err = 0;
+
+	/* no other mtd function can run at this point */
+	write_lock(&dev->bdev_mutex);
+
+	/* get the device number for the whole disk */
+	devt = MKDEV(MAJOR(dev->blkdev->bd_dev), 0);
+
+	/* close the old block device */
+	_close_bdev(dev);
+
+	/* open the whole disk, issue a partition rescan, then */
+	bdev = open_by_devnum(devt, FMODE_WRITE | FMODE_READ);
+	if (!bdev || !bdev->bd_disk)
+		err = -EINVAL;
+#ifndef CONFIG_MTD_BLOCK2MTD_MODULE
+	else
+		err = rescan_partitions(bdev->bd_disk, bdev);
+#endif
+	if (bdev)
+		close_bdev_exclusive(bdev, FMODE_READ|FMODE_WRITE);
+
+	/* try to open the partition block device again */
+	_open_bdev(dev);
+	write_unlock(&dev->bdev_mutex);
+
+	return err;
+}
+
+/* FIXME: ensure that mtd->size % erase_size == 0 */
+static struct block2mtd_dev *add_device(char *devname, int erase_size, char *mtdname)
+{
+	struct block2mtd_dev *dev;
+	struct mtd_partition *part;
+	char *name;
+
+	if (!devname)
+		return NULL;
+
+	dev = kzalloc(sizeof(struct block2mtd_dev) + strlen(devname) + 1, GFP_KERNEL);
+	if (!dev)
+		return NULL;
+
+	strcpy(dev->devname, devname);
+
+	if (_open_bdev(dev))
+		goto devinit_err;
+
 	mutex_init(&dev->write_mutex);
+	rwlock_init(&dev->bdev_mutex);
 
 	/* Setup the MTD structure */
 	/* make the name contain the block device in */
@@ -299,6 +393,7 @@ static struct block2mtd_dev *add_device(
 	dev->mtd.read = block2mtd_read;
 	dev->mtd.priv = dev;
 	dev->mtd.owner = THIS_MODULE;
+	dev->mtd.refresh_device = block2mtd_refresh;
 
 	part = kzalloc(sizeof(struct mtd_partition), GFP_KERNEL);
 	part->name = dev->mtd.name;
--- a/drivers/mtd/mtdchar.c
+++ b/drivers/mtd/mtdchar.c
@@ -30,6 +30,7 @@
 #include <linux/backing-dev.h>
 #include <linux/compat.h>
 #include <linux/mount.h>
+#include <linux/mtd/partitions.h>
 
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/map.h>
@@ -854,6 +855,13 @@ static int mtd_ioctl(struct file *file, 
 		file->f_pos = 0;
 		break;
 	}
+#ifdef CONFIG_MTD_PARTITIONS
+	case MTDREFRESH:
+	{
+		ret = refresh_mtd_partitions(mtd);
+		break;
+	}
+#endif
 
 	default:
 		ret = -ENOTTY;
--- a/include/linux/mtd/mtd.h
+++ b/include/linux/mtd/mtd.h
@@ -110,6 +110,7 @@ struct mtd_oob_ops {
 	uint8_t		*oobbuf;
 };
 
+struct mtd_info;
 struct mtd_info {
 	u_char type;
 	uint32_t flags;
@@ -251,6 +252,9 @@ struct mtd_info {
 	struct device dev;
 	int usecount;
 
+	int (*refresh_device)(struct mtd_info *mtd);
+	struct mtd_info *split;
+
 	/* If the driver is something smart, like UBI, it may need to maintain
 	 * its own reference counting. The below functions are only for driver.
 	 * The driver may register its callbacks. These callbacks are not
--- a/include/linux/mtd/partitions.h
+++ b/include/linux/mtd/partitions.h
@@ -34,12 +34,14 @@
  * erasesize aligned (e.g. use MTDPART_OFS_NEXTBLK).
  */
 
+struct mtd_partition;
 struct mtd_partition {
 	char *name;			/* identifier string */
 	uint64_t size;			/* partition size */
 	uint64_t offset;		/* offset within the master MTD space */
 	uint32_t mask_flags;		/* master MTD flags to mask out for this partition */
 	struct nand_ecclayout *ecclayout;	/* out of band layout for this partition (NAND only)*/
+	int (*refresh_partition)(struct mtd_info *);
 };
 
 #define MTDPART_OFS_NXTBLK	(-2)
@@ -51,6 +53,7 @@ struct mtd_info;
 
 int add_mtd_partitions(struct mtd_info *, const struct mtd_partition *, int);
 int del_mtd_partitions(struct mtd_info *);
+int refresh_mtd_partitions(struct mtd_info *);
 
 /*
  * Functions dealing with the various ways of partitioning the space
--- a/include/mtd/mtd-abi.h
+++ b/include/mtd/mtd-abi.h
@@ -126,6 +126,7 @@ struct otp_info {
 #define MEMWRITEOOB64		_IOWR('M', 21, struct mtd_oob_buf64)
 #define MEMREADOOB64		_IOWR('M', 22, struct mtd_oob_buf64)
 #define MEMISLOCKED		_IOR('M', 23, struct erase_info_user)
+#define MTDREFRESH		_IO('M', 24)
 
 /*
  * Obsolete legacy interface. Keep it in order not to break userspace