summaryrefslogtreecommitdiffstats
path: root/target/linux/sunxi/patches-4.1/123-mtd-nand-sunxi-add-hw-randomizer-support.patch
blob: 5d37a38b5230542735ab851538b95ee7a8d1ecaa (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
pre { line-height: 125%; margin: 0; }
td.linenos pre { color: #000000; background-color: #f0f0f0; padding: 0 5px 0 5px; }
span.linenos { color: #000000; background-color: #f0f0f0; padding: 0 5px 0 5px; }
td.linenos pre.special { color: #000000; background-color: #ffffc0; padding: 0 5px 0 5px; }
span.linenos.special { color: #000000; background-color: #ffffc0; padding: 0 5px 0 5px; }
.highlight .hll { background-color: #ffffcc }
.highlight { background: #ffffff; }
.highlight .c { color: #888888 } /* Comment */
.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */
.highlight .k { color: #008800; font-weight: bold } /* Keyword */
.highlight .ch { color: #888888 } /* Comment.Hashbang */
.highlight .cm { color: #888888 } /* Comment.Multiline */
.highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */
.highlight .cpf { color: #888888 } /* Comment.PreprocFile */
.highlight .c1 { color: #888888 } /* Comment.Single */
.highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */
.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .gr { color: #aa0000 } /* Generic.Error */
.highlight .gh { color: #333333 } /* Generic.Heading */
.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
.highlight .go { color: #888888 } /* Generic.Output */
.highlight .gp { color: #555555 } /* Generic.Prompt */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #666666 } /* Generic.Subheading */
.highlight .gt { color: #aa0000 } /* Generic.Traceback */
.highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #008800 } /* Keyword.Pseudo */
.highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */
.highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */
.highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */
.highlight .na { color: #336699 } /* Name.Attribute */
.highlight .nb { color: #003388 } /* Name.Builtin */
.highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */
.highlight .no { color: #003366; font-weight: bold } /* Name.Constant */
.highlight .nd { color: #555555 } /* Name.Decorator */
.highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */
.highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */
.highlight .nl { color: #336699; font-style: italic } /* Name.Label */
.highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */
.highlight .py { color: #336699; font-weight: bold } /* Name.Property */
.highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */
.highlight .nv { color: #336699 } /* Name.Variable */
.highlight .ow { color: #008800 } /* Operator.Word */
.highlight .w { color: #bbbbbb } /* Text.Whitespace */
.highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */
.highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */
.highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */
.highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */
.highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */
.highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */
.highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */
.highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */
.highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */
.highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */
.highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */
.highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */
.highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */
.highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */
.highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */
.highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */
.highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */
.highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */
.highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */
.highlight .vc { color: #336699 } /* Name.Variable.Class */
.highlight .vg { color: #dd7700 } /* Name.Variable.Global */
.highlight .vi { color: #3333bb } /* Name.Variable.Instance */
.highlight .vm { color: #336699 } /* Name.Variable.Magic */
.highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */
#define __USER_CS	(0x33)
#define __USER_DS	(0x2B)

struct target_pt_regs {
	abi_ulong r15;
	abi_ulong r14;
	abi_ulong r13;
	abi_ulong r12;
	abi_ulong rbp;
	abi_ulong rbx;
/* arguments: non interrupts/non tracing syscalls only save up to here */
 	abi_ulong r11;
	abi_ulong r10;
	abi_ulong r9;
	abi_ulong r8;
	abi_ulong rax;
	abi_ulong rcx;
	abi_ulong rdx;
	abi_ulong rsi;
	abi_ulong rdi;
	abi_ulong orig_rax;
/* end of arguments */
/* cpu exception frame or undefined */
	abi_ulong rip;
	abi_ulong cs;
	abi_ulong eflags;
	abi_ulong rsp;
	abi_ulong ss;
/* top of stack page */
};

/* Maximum number of LDT entries supported. */
#define TARGET_LDT_ENTRIES	8192
/* The size of each LDT entry. */
#define TARGET_LDT_ENTRY_SIZE	8

#define TARGET_GDT_ENTRIES 16
#define TARGET_GDT_ENTRY_TLS_ENTRIES 3
#define TARGET_GDT_ENTRY_TLS_MIN 12
#define TARGET_GDT_ENTRY_TLS_MAX 14

#if 0 // Redefine this
struct target_modify_ldt_ldt_s {
	unsigned int  entry_number;
        abi_ulong     base_addr;
	unsigned int  limit;
	unsigned int  seg_32bit:1;
	unsigned int  contents:2;
	unsigned int  read_exec_only:1;
	unsigned int  limit_in_pages:1;
	unsigned int  seg_not_present:1;
	unsigned int  useable:1;
	unsigned int  lm:1;
};
#else
struct target_modify_ldt_ldt_s {
	unsigned int  entry_number;
        abi_ulong     base_addr;
	unsigned int  limit;
        unsigned int flags;
};
#endif

struct target_ipc64_perm
{
	int		key;
	uint32_t	uid;
	uint32_t	gid;
	uint32_t	cuid;
	uint32_t	cgid;
	unsigned short		mode;
	unsigned short		__pad1;
	unsigned short		seq;
	unsigned short		__pad2;
	abi_ulong		__unused1;
	abi_ulong		__unused2;
};

struct target_msqid64_ds {
	struct target_ipc64_perm msg_perm;
	unsigned int msg_stime;	/* last msgsnd time */
	unsigned int msg_rtime;	/* last msgrcv time */
	unsigned int msg_ctime;	/* last change time */
	abi_ulong  msg_cbytes;	/* current number of bytes on queue */
	abi_ulong  msg_qnum;	/* number of messages in queue */
	abi_ulong  msg_qbytes;	/* max number of bytes on queue */
	unsigned int msg_lspid;	/* pid of last msgsnd */
	unsigned int msg_lrpid;	/* last receive pid */
	abi_ulong  __unused4;
	abi_ulong  __unused5;
};

/* FreeBSD sysarch(2) */
#define TARGET_FREEBSD_I386_GET_LDT	0
#define TARGET_FREEBSD_I386_SET_LDT	1
				/* I386_IOPL */
#define TARGET_FREEBSD_I386_GET_IOPERM	3
#define TARGET_FREEBSD_I386_SET_IOPERM	4
				/* xxxxx */
#define TARGET_FREEBSD_I386_GET_FSBASE	7
#define TARGET_FREEBSD_I386_SET_FSBASE	8
#define TARGET_FREEBSD_I386_GET_GSBASE	9
#define TARGET_FREEBSD_I386_SET_GSBASE	10

#define TARGET_FREEBSD_AMD64_GET_FSBASE	128
#define TARGET_FREEBSD_AMD64_SET_FSBASE	129
#define TARGET_FREEBSD_AMD64_GET_GSBASE	130
#define TARGET_FREEBSD_AMD64_SET_GSBASE	131


#define UNAME_MACHINE "x86_64"

#define TARGET_ARCH_SET_GS 0x1001
#define TARGET_ARCH_SET_FS 0x1002
#define TARGET_ARCH_GET_FS 0x1003
#define TARGET_ARCH_GET_GS 0x1004
440'>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 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893
From ef4bc8ab68979e5c1c30f061c5af1a7d6ec8eb52 Mon Sep 17 00:00:00 2001
From: Boris Brezillon <boris.brezillon@free-electrons.com>
Date: Tue, 21 Oct 2014 14:40:42 +0200
Subject: [PATCH] mtd: nand: sunxi: Add HW randomizer support

Add support for the HW randomizer available on the sunxi nand controller.

Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com>
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
---
 drivers/mtd/nand/sunxi_nand.c | 603 ++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 585 insertions(+), 18 deletions(-)

diff --git a/drivers/mtd/nand/sunxi_nand.c b/drivers/mtd/nand/sunxi_nand.c
index c3e0473..2f6ab39 100644
--- a/drivers/mtd/nand/sunxi_nand.c
+++ b/drivers/mtd/nand/sunxi_nand.c
@@ -206,10 +206,12 @@ struct sunxi_nand_hw_ecc {
  *
  * @part: base paritition structure
  * @ecc: per-partition ECC info
+ * @rnd: per-partition randomizer info
  */
 struct sunxi_nand_part {
 	struct nand_part part;
 	struct nand_ecc_ctrl ecc;
+	struct nand_rnd_ctrl rnd;
 };
 
 static inline struct sunxi_nand_part *
@@ -219,6 +221,29 @@ to_sunxi_nand_part(struct nand_part *part)
 }
 
 /*
+ * sunxi NAND randomizer structure: stores NAND randomizer information
+ *
+ * @page: current page
+ * @column: current column
+ * @nseeds: seed table size
+ * @seeds: seed table
+ * @subseeds: pre computed sub seeds
+ * @step: step function
+ * @left: number of remaining bytes in the page
+ * @state: current randomizer state
+ */
+struct sunxi_nand_hw_rnd {
+	int page;
+	int column;
+	int nseeds;
+	u16 *seeds;
+	u16 *subseeds;
+	u16 (*step)(struct mtd_info *mtd, u16 state, int column, int *left);
+	int left;
+	u16 state;
+};
+
+/*
  * NAND chip structure: stores NAND chip device related information
  *
  * @node:		used to store NAND chips into a list
@@ -233,6 +258,7 @@ struct sunxi_nand_chip {
 	struct list_head node;
 	struct nand_chip nand;
 	struct mtd_info mtd;
+	void *buffer;
 	unsigned long clk_rate;
 	int selected;
 	int nsels;
@@ -489,6 +515,185 @@ static void sunxi_nfc_write_buf(struct mtd_info *mtd, const uint8_t *buf,
 	}
 }
 
+static u16 sunxi_nfc_hwrnd_step(struct sunxi_nand_hw_rnd *rnd, u16 state, int count)
+{
+	state &= 0x7fff;
+	count *= 8;
+	while (count--)
+		state = ((state >> 1) |
+			 ((((state >> 0) ^ (state >> 1)) & 1) << 14)) & 0x7fff;
+
+	return state;
+}
+
+static u16 sunxi_nfc_hwrnd_single_step(u16 state, int count)
+{
+	state &= 0x7fff;
+	while (count--)
+		state = ((state >> 1) |
+			 ((((state >> 0) ^ (state >> 1)) & 1) << 14)) & 0x7fff;
+
+	return state;
+}
+
+static int sunxi_nfc_hwrnd_config(struct mtd_info *mtd, int page, int column,
+				  enum nand_rnd_action action)
+{
+	struct nand_chip *nand = mtd->priv;
+	struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);
+	struct sunxi_nand_hw_rnd *rnd = nand->cur_rnd->priv;
+	u16 state;
+
+	if (page < 0 && column < 0) {
+		rnd->page = -1;
+		rnd->column = -1;
+		return 0;
+	}
+
+	if (column < 0)
+		column = 0;
+	if (page < 0)
+		page = rnd->page;
+
+	if (page < 0)
+		return -EINVAL;
+
+	if (page != rnd->page && action == NAND_RND_READ) {
+		int status;
+
+		status = nand_page_get_status(mtd, page);
+		if (status == NAND_PAGE_STATUS_UNKNOWN) {
+			nand->cmdfunc(mtd, NAND_CMD_RNDOUT, 0, -1);
+			sunxi_nfc_read_buf(mtd, sunxi_nand->buffer,
+					   mtd->writesize + mtd->oobsize);
+
+			if (nand_page_is_empty(mtd, sunxi_nand->buffer,
+					       sunxi_nand->buffer +
+					       mtd->writesize))
+				status = NAND_PAGE_EMPTY;
+			else
+				status = NAND_PAGE_FILLED;
+
+			nand_page_set_status(mtd, page, status);
+			nand->cmdfunc(mtd, NAND_CMD_RNDOUT, column, -1);
+		}
+	}
+
+	state = rnd->seeds[page % rnd->nseeds];
+	rnd->page = page;
+	rnd->column = column;
+
+	if (rnd->step) {
+		rnd->state = rnd->step(mtd, state, column, &rnd->left);
+	} else {
+		rnd->state = sunxi_nfc_hwrnd_step(rnd, state, column % 4096);
+		rnd->left = mtd->oobsize + mtd->writesize - column;
+	}
+
+	return 0;
+}
+
+static void sunxi_nfc_hwrnd_write_buf(struct mtd_info *mtd, const uint8_t *buf,
+				      int len)
+{
+	struct nand_chip *nand = mtd->priv;
+	struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
+	struct sunxi_nand_hw_rnd *rnd = nand->cur_rnd->priv;
+	u32 tmp = readl(nfc->regs + NFC_REG_ECC_CTL);
+	int cnt;
+	int offs = 0;
+	int rndactiv;
+
+	tmp &= ~(NFC_RANDOM_DIRECTION | NFC_RANDOM_SEED | NFC_RANDOM_EN);
+	writel(tmp, nfc->regs + NFC_REG_ECC_CTL);
+
+	if (rnd->page < 0) {
+		sunxi_nfc_write_buf(mtd, buf, len);
+		return;
+	}
+
+	while (len > offs) {
+		cnt = len - offs;
+		if (cnt > 1024)
+			cnt = 1024;
+
+		rndactiv = nand_rnd_is_activ(mtd, rnd->page, rnd->column,
+					     &cnt);
+		if (rndactiv > 0) {
+			writel(tmp | NFC_RANDOM_EN | (rnd->state << 16),
+			       nfc->regs + NFC_REG_ECC_CTL);
+			if (rnd->left < cnt)
+				cnt = rnd->left;
+		}
+
+		sunxi_nfc_write_buf(mtd, buf + offs, cnt);
+
+		if (rndactiv > 0)
+			writel(tmp & ~NFC_RANDOM_EN,
+			       nfc->regs + NFC_REG_ECC_CTL);
+
+		offs += cnt;
+		if (len <= offs)
+			break;
+
+		sunxi_nfc_hwrnd_config(mtd, -1, rnd->column + cnt, NAND_RND_WRITE);
+	}
+}
+
+static void sunxi_nfc_hwrnd_read_buf(struct mtd_info *mtd, uint8_t *buf,
+				     int len)
+{
+	struct nand_chip *nand = mtd->priv;
+	struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
+	struct sunxi_nand_hw_rnd *rnd = nand->cur_rnd->priv;
+	u32 tmp = readl(nfc->regs + NFC_REG_ECC_CTL);
+	int cnt;
+	int offs = 0;
+	int rndactiv;
+
+	tmp &= ~(NFC_RANDOM_DIRECTION | NFC_RANDOM_SEED | NFC_RANDOM_EN);
+	writel(tmp, nfc->regs + NFC_REG_ECC_CTL);
+
+	if (rnd->page < 0) {
+		sunxi_nfc_read_buf(mtd, buf, len);
+		return;
+	}
+
+	while (len > offs) {
+		cnt = len - offs;
+		if (cnt > 1024)
+			cnt = 1024;
+
+		if (nand_page_get_status(mtd, rnd->page) != NAND_PAGE_EMPTY &&
+		    nand_rnd_is_activ(mtd, rnd->page, rnd->column, &cnt) > 0)
+			rndactiv = 1;
+		else
+			rndactiv = 0;
+
+		if (rndactiv > 0) {
+			writel(tmp | NFC_RANDOM_EN | (rnd->state << 16),
+			       nfc->regs + NFC_REG_ECC_CTL);
+			if (rnd->left < cnt)
+				cnt = rnd->left;
+		}
+
+		if (buf)
+			sunxi_nfc_read_buf(mtd, buf + offs, cnt);
+		else
+			sunxi_nfc_read_buf(mtd, NULL, cnt);
+
+		if (rndactiv > 0)
+			writel(tmp & ~NFC_RANDOM_EN,
+			       nfc->regs + NFC_REG_ECC_CTL);
+
+		offs += cnt;
+		if (len <= offs)
+			break;
+
+		sunxi_nfc_hwrnd_config(mtd, -1, rnd->column + cnt, NAND_RND_READ);
+	}
+}
+
 static uint8_t sunxi_nfc_read_byte(struct mtd_info *mtd)
 {
 	uint8_t ret;
@@ -538,16 +743,43 @@ static int sunxi_nfc_hw_ecc_read_page(struct mtd_info *mtd,
 				      int oob_required, int page)
 {
 	struct sunxi_nfc *nfc = to_sunxi_nfc(chip->controller);
+	struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(chip);
 	struct nand_ecc_ctrl *ecc = chip->cur_ecc;
 	struct nand_ecclayout *layout = ecc->layout;
 	struct sunxi_nand_hw_ecc *data = ecc->priv;
 	unsigned int max_bitflips = 0;
+	int status;
 	int offset;
 	int ret;
 	u32 tmp;
 	int i;
 	int cnt;
 
+	status = nand_page_get_status(mtd, page);
+	if (status == NAND_PAGE_STATUS_UNKNOWN) {
+		chip->cmdfunc(mtd, NAND_CMD_RNDOUT, 0, -1);
+		sunxi_nfc_read_buf(mtd, sunxi_nand->buffer,
+				   mtd->writesize + mtd->oobsize);
+
+		if (nand_page_is_empty(mtd, sunxi_nand->buffer,
+				       sunxi_nand->buffer +
+				       mtd->writesize)) {
+			status = NAND_PAGE_EMPTY;
+		} else {
+			status = NAND_PAGE_FILLED;
+			chip->cmdfunc(mtd, NAND_CMD_RNDOUT, 0, -1);
+		}
+
+		nand_page_set_status(mtd, page, status);
+	}
+
+	if (status == NAND_PAGE_EMPTY) {
+		memset(buf, 0xff, mtd->writesize);
+		if (oob_required)
+			memset(chip->oob_poi, 0xff, mtd->oobsize);
+		return 0;
+	}
+
 	tmp = readl(nfc->regs + NFC_REG_ECC_CTL);
 	tmp &= ~(NFC_ECC_MODE | NFC_ECC_PIPELINE | NFC_ECC_BLOCK_SIZE);
 	tmp |= NFC_ECC_EN | (data->mode << NFC_ECC_MODE_SHIFT) |
@@ -556,12 +788,15 @@ static int sunxi_nfc_hw_ecc_read_page(struct mtd_info *mtd,
 	writel(tmp, nfc->regs + NFC_REG_ECC_CTL);
 
 	for (i = 0; i < ecc->steps; i++) {
+		bool rndactiv = false;
+
 		if (i)
 			chip->cmdfunc(mtd, NAND_CMD_RNDOUT, i * ecc->size, -1);
 
 		offset = mtd->writesize + layout->eccpos[i * ecc->bytes] - 4;
 
-		chip->read_buf(mtd, NULL, ecc->size);
+		nand_rnd_config(mtd, page, i * ecc->size, NAND_RND_READ);
+		nand_rnd_read_buf(mtd, NULL, ecc->size);
 
 		chip->cmdfunc(mtd, NAND_CMD_RNDOUT, offset, -1);
 
@@ -569,6 +804,25 @@ static int sunxi_nfc_hw_ecc_read_page(struct mtd_info *mtd,
 		if (ret)
 			return ret;
 
+		if (i) {
+			cnt = ecc->bytes + 4;
+			if (nand_rnd_is_activ(mtd, page, offset, &cnt) > 0 &&
+			    cnt == ecc->bytes + 4)
+				rndactiv = true;
+		} else {
+			cnt = ecc->bytes + 2;
+			if (nand_rnd_is_activ(mtd, page, offset + 2, &cnt) > 0 &&
+			    cnt == ecc->bytes + 2)
+				rndactiv = true;
+		}
+
+		if (rndactiv) {
+			tmp = readl(nfc->regs + NFC_REG_ECC_CTL);
+			tmp &= ~(NFC_RANDOM_DIRECTION | NFC_ECC_EXCEPTION);
+			tmp |= NFC_RANDOM_EN;
+			writel(tmp, nfc->regs + NFC_REG_ECC_CTL);
+		}
+
 		tmp = NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD | (1 << 30);
 		writel(tmp, nfc->regs + NFC_REG_CMD);
 
@@ -579,6 +833,9 @@ static int sunxi_nfc_hw_ecc_read_page(struct mtd_info *mtd,
 		memcpy_fromio(buf + (i * ecc->size),
 			      nfc->regs + NFC_RAM0_BASE, ecc->size);
 
+		writel(readl(nfc->regs + NFC_REG_ECC_CTL) & ~NFC_RANDOM_EN,
+		       nfc->regs + NFC_REG_ECC_CTL);
+
 		if (readl(nfc->regs + NFC_REG_ECC_ST) & 0x1) {
 			mtd->ecc_stats.failed++;
 		} else {
@@ -594,9 +851,10 @@ static int sunxi_nfc_hw_ecc_read_page(struct mtd_info *mtd,
 			if (ret)
 				return ret;
 
+			nand_rnd_config(mtd, -1, offset, NAND_RND_READ);
 			offset -= mtd->writesize;
-			chip->read_buf(mtd, chip->oob_poi + offset,
-				      ecc->bytes + 4);
+			nand_rnd_read_buf(mtd, chip->oob_poi + offset,
+					  ecc->bytes + 4);
 		}
 	}
 
@@ -606,11 +864,14 @@ static int sunxi_nfc_hw_ecc_read_page(struct mtd_info *mtd,
 			offset = mtd->writesize +
 				 ecc->layout->oobfree[ecc->steps].offset;
 			chip->cmdfunc(mtd, NAND_CMD_RNDOUT, offset, -1);
+			nand_rnd_config(mtd, -1, offset, NAND_RND_READ);
 			offset -= mtd->writesize;
-			chip->read_buf(mtd, chip->oob_poi + offset, cnt);
+			nand_rnd_read_buf(mtd, chip->oob_poi + offset, cnt);
 		}
 	}
 
+	nand_rnd_config(mtd, -1, -1, NAND_RND_READ);
+
 	tmp = readl(nfc->regs + NFC_REG_ECC_CTL);
 	tmp &= ~NFC_ECC_EN;
 
@@ -627,6 +888,7 @@ static int sunxi_nfc_hw_ecc_write_page(struct mtd_info *mtd,
 	struct nand_ecc_ctrl *ecc = chip->cur_ecc;
 	struct nand_ecclayout *layout = ecc->layout;
 	struct sunxi_nand_hw_ecc *data = ecc->priv;
+	struct sunxi_nand_hw_rnd *rnd = chip->cur_rnd->priv;
 	int offset;
 	int ret;
 	u32 tmp;
@@ -641,22 +903,56 @@ static int sunxi_nfc_hw_ecc_write_page(struct mtd_info *mtd,
 	writel(tmp, nfc->regs + NFC_REG_ECC_CTL);
 
 	for (i = 0; i < ecc->steps; i++) {
+		bool rndactiv = false;
+		u8 oob_buf[4];
+
 		if (i)
 			chip->cmdfunc(mtd, NAND_CMD_RNDIN, i * ecc->size, -1);
 
-		chip->write_buf(mtd, buf + (i * ecc->size), ecc->size);
+		nand_rnd_config(mtd, -1, i * ecc->size, NAND_RND_WRITE);
+		nand_rnd_write_buf(mtd, buf + (i * ecc->size), ecc->size);
 
 		offset = layout->eccpos[i * ecc->bytes] - 4 + mtd->writesize;
 
 		/* Fill OOB data in */
-		if (oob_required) {
-			tmp = 0xffffffff;
-			memcpy_toio(nfc->regs + NFC_REG_USER_DATA_BASE, &tmp,
-				    4);
+		if (!oob_required)
+			memset(oob_buf, 0xff, 4);
+		else
+			memcpy(oob_buf,
+			       chip->oob_poi + layout->oobfree[i].offset,
+			       4);
+
+
+		memcpy_toio(nfc->regs + NFC_REG_USER_DATA_BASE, oob_buf, 4);
+
+		if (i) {
+			cnt = ecc->bytes + 4;
+			if (rnd &&
+			    nand_rnd_is_activ(mtd, -1, offset, &cnt) > 0 &&
+			    cnt == ecc->bytes + 4)
+				rndactiv = true;
 		} else {
-			memcpy_toio(nfc->regs + NFC_REG_USER_DATA_BASE,
-				    chip->oob_poi + offset - mtd->writesize,
-				    4);
+			cnt = ecc->bytes + 2;
+			if (rnd &&
+			    nand_rnd_is_activ(mtd, -1, offset + 2, &cnt) > 0 &&
+			    cnt == ecc->bytes + 2)
+				rndactiv = true;
+		}
+
+		if (rndactiv) {
+			/* pre randomize to generate FF patterns on the NAND */
+			if (!i) {
+				u16 state = rnd->subseeds[rnd->page % rnd->nseeds];
+				state = sunxi_nfc_hwrnd_single_step(state, 15);
+				oob_buf[0] ^= state;
+				state = sunxi_nfc_hwrnd_step(rnd, state, 1);
+				oob_buf[1] ^= state;
+				memcpy_toio(nfc->regs + NFC_REG_USER_DATA_BASE, oob_buf, 4);
+			}
+			tmp = readl(nfc->regs + NFC_REG_ECC_CTL);
+			tmp &= ~(NFC_RANDOM_DIRECTION | NFC_ECC_EXCEPTION);
+			tmp |= NFC_RANDOM_EN;
+			writel(tmp, nfc->regs + NFC_REG_ECC_CTL);
 		}
 
 		chip->cmdfunc(mtd, NAND_CMD_RNDIN, offset, -1);
@@ -671,6 +967,9 @@ static int sunxi_nfc_hw_ecc_write_page(struct mtd_info *mtd,
 		ret = sunxi_nfc_wait_int(nfc, NFC_CMD_INT_FLAG, 0);
 		if (ret)
 			return ret;
+
+		writel(readl(nfc->regs + NFC_REG_ECC_CTL) & ~NFC_RANDOM_EN,
+		       nfc->regs + NFC_REG_ECC_CTL);
 	}
 
 	if (oob_required) {
@@ -679,11 +978,14 @@ static int sunxi_nfc_hw_ecc_write_page(struct mtd_info *mtd,
 			offset = mtd->writesize +
 				 ecc->layout->oobfree[i].offset;
 			chip->cmdfunc(mtd, NAND_CMD_RNDIN, offset, -1);
+			nand_rnd_config(mtd, -1, offset, NAND_RND_WRITE);
 			offset -= mtd->writesize;
-			chip->write_buf(mtd, chip->oob_poi + offset, cnt);
+			nand_rnd_write_buf(mtd, chip->oob_poi + offset, cnt);
 		}
 	}
 
+	nand_rnd_config(mtd, -1, -1, NAND_RND_WRITE);
+
 	tmp = readl(nfc->regs + NFC_REG_ECC_CTL);
 	tmp &= ~NFC_ECC_EN;
 
@@ -692,22 +994,76 @@ static int sunxi_nfc_hw_ecc_write_page(struct mtd_info *mtd,
 	return 0;
 }
 
+static u16 sunxi_nfc_hw_ecc_rnd_steps(struct mtd_info *mtd, u16 state,
+				      int column, int *left)
+{
+	struct nand_chip *chip = mtd->priv;
+	struct nand_ecc_ctrl *ecc = chip->cur_ecc;
+	struct sunxi_nand_hw_rnd *rnd = chip->cur_rnd->priv;
+	int nblks = mtd->writesize / ecc->size;
+	int modsize = ecc->size;
+	int steps;
+
+	if (column < mtd->writesize) {
+		steps = column % modsize;
+		*left = modsize - steps;
+	} else if (column < mtd->writesize +
+			    (nblks * (ecc->bytes + 4))) {
+		column -= mtd->writesize;
+		steps = column % (ecc->bytes + 4);
+		*left = ecc->bytes + 4 - steps;
+		state = rnd->subseeds[rnd->page % rnd->nseeds];
+	} else {
+		steps = column % 4096;
+		*left = mtd->writesize + mtd->oobsize - column;
+	}
+
+	return sunxi_nfc_hwrnd_step(rnd, state, steps);
+}
+
 static int sunxi_nfc_hw_syndrome_ecc_read_page(struct mtd_info *mtd,
 					       struct nand_chip *chip,
 					       uint8_t *buf, int oob_required,
 					       int page)
 {
 	struct sunxi_nfc *nfc = to_sunxi_nfc(chip->controller);
+	struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(chip);
 	struct nand_ecc_ctrl *ecc = chip->cur_ecc;
 	struct sunxi_nand_hw_ecc *data = ecc->priv;
 	unsigned int max_bitflips = 0;
 	uint8_t *oob = chip->oob_poi;
 	int offset = 0;
 	int ret;
+	int status;
 	int cnt;
 	u32 tmp;
 	int i;
 
+	status = nand_page_get_status(mtd, page);
+	if (status == NAND_PAGE_STATUS_UNKNOWN) {
+		chip->cmdfunc(mtd, NAND_CMD_RNDOUT, 0, -1);
+		sunxi_nfc_read_buf(mtd, sunxi_nand->buffer,
+				   mtd->writesize + mtd->oobsize);
+
+		if (nand_page_is_empty(mtd, sunxi_nand->buffer,
+				       sunxi_nand->buffer +
+				       mtd->writesize)) {
+			status = NAND_PAGE_EMPTY;
+		} else {
+			status = NAND_PAGE_FILLED;
+			chip->cmdfunc(mtd, NAND_CMD_RNDOUT, 0, -1);
+		}
+
+		nand_page_set_status(mtd, page, status);
+	}
+
+	if (status == NAND_PAGE_EMPTY) {
+		memset(buf, 0xff, mtd->writesize);
+		if (oob_required)
+			memset(chip->oob_poi, 0xff, mtd->oobsize);
+		return 0;
+	}
+
 	tmp = readl(nfc->regs + NFC_REG_ECC_CTL);
 	tmp &= ~(NFC_ECC_MODE | NFC_ECC_PIPELINE | NFC_ECC_BLOCK_SIZE);
 	tmp |= NFC_ECC_EN | (data->mode << NFC_ECC_MODE_SHIFT) |
@@ -716,7 +1072,17 @@ static int sunxi_nfc_hw_syndrome_ecc_read_page(struct mtd_info *mtd,
 	writel(tmp, nfc->regs + NFC_REG_ECC_CTL);
 
 	for (i = 0; i < ecc->steps; i++) {
-		chip->read_buf(mtd, NULL, ecc->size);
+		nand_rnd_config(mtd, page, offset, NAND_RND_READ);
+		nand_rnd_read_buf(mtd, NULL, ecc->size);
+
+		cnt = ecc->bytes + 4;
+		if (nand_rnd_is_activ(mtd, page, offset, &cnt) > 0 &&
+		    cnt == ecc->bytes + 4) {
+			tmp = readl(nfc->regs + NFC_REG_ECC_CTL);
+			tmp &= ~(NFC_RANDOM_DIRECTION | NFC_ECC_EXCEPTION);
+			tmp |= NFC_RANDOM_EN;
+			writel(tmp, nfc->regs + NFC_REG_ECC_CTL);
+		}
 
 		tmp = NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD | (1 << 30);
 		writel(tmp, nfc->regs + NFC_REG_CMD);
@@ -729,6 +1095,9 @@ static int sunxi_nfc_hw_syndrome_ecc_read_page(struct mtd_info *mtd,
 		buf += ecc->size;
 		offset += ecc->size;
 
+		writel(readl(nfc->regs + NFC_REG_ECC_CTL) & ~NFC_RANDOM_EN,
+		       nfc->regs + NFC_REG_ECC_CTL);
+
 		if (readl(nfc->regs + NFC_REG_ECC_ST) & 0x1) {
 			mtd->ecc_stats.failed++;
 		} else {
@@ -739,7 +1108,8 @@ static int sunxi_nfc_hw_syndrome_ecc_read_page(struct mtd_info *mtd,
 
 		if (oob_required) {
 			chip->cmdfunc(mtd, NAND_CMD_RNDOUT, offset, -1);
-			chip->read_buf(mtd, oob, ecc->bytes + ecc->prepad);
+			nand_rnd_config(mtd, -1, offset, NAND_RND_READ);
+			nand_rnd_read_buf(mtd, oob, ecc->bytes + ecc->prepad);
 			oob += ecc->bytes + ecc->prepad;
 		}
 
@@ -750,10 +1120,13 @@ static int sunxi_nfc_hw_syndrome_ecc_read_page(struct mtd_info *mtd,
 		cnt = mtd->oobsize - (oob - chip->oob_poi);
 		if (cnt > 0) {
 			chip->cmdfunc(mtd, NAND_CMD_RNDOUT, offset, -1);
-			chip->read_buf(mtd, oob, cnt);
+			nand_rnd_config(mtd, page, offset, NAND_RND_READ);
+			nand_rnd_read_buf(mtd, oob, cnt);
 		}
 	}
 
+	nand_rnd_config(mtd, -1, -1, NAND_RND_READ);
+
 	writel(readl(nfc->regs + NFC_REG_ECC_CTL) & ~NFC_ECC_EN,
 	       nfc->regs + NFC_REG_ECC_CTL);
 
@@ -768,6 +1141,7 @@ static int sunxi_nfc_hw_syndrome_ecc_write_page(struct mtd_info *mtd,
 	struct sunxi_nfc *nfc = to_sunxi_nfc(chip->controller);
 	struct nand_ecc_ctrl *ecc = chip->cur_ecc;
 	struct sunxi_nand_hw_ecc *data = ecc->priv;
+	struct sunxi_nand_hw_rnd *rnd = chip->cur_rnd->priv;
 	uint8_t *oob = chip->oob_poi;
 	int offset = 0;
 	int ret;
@@ -783,7 +1157,8 @@ static int sunxi_nfc_hw_syndrome_ecc_write_page(struct mtd_info *mtd,
 	writel(tmp, nfc->regs + NFC_REG_ECC_CTL);
 
 	for (i = 0; i < ecc->steps; i++) {
-		chip->write_buf(mtd, buf + (i * ecc->size), ecc->size);
+		nand_rnd_config(mtd, -1, offset, NAND_RND_WRITE);
+		nand_rnd_write_buf(mtd, buf + (i * ecc->size), ecc->size);
 		offset += ecc->size;
 
 		/* Fill OOB data in */
@@ -796,6 +1171,16 @@ static int sunxi_nfc_hw_syndrome_ecc_write_page(struct mtd_info *mtd,
 				    4);
 		}
 
+		cnt = ecc->bytes + 4;
+		if (rnd &&
+		    nand_rnd_is_activ(mtd, rnd->page, offset, &cnt) > 0 &&
+		    cnt == ecc->bytes + 4) {
+			tmp = readl(nfc->regs + NFC_REG_ECC_CTL);
+			tmp &= ~(NFC_RANDOM_DIRECTION | NFC_ECC_EXCEPTION);
+			tmp |= NFC_RANDOM_EN;
+			writel(tmp, nfc->regs + NFC_REG_ECC_CTL);
+		}
+
 		tmp = NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD | NFC_ACCESS_DIR |
 		      (1 << 30);
 		writel(tmp, nfc->regs + NFC_REG_CMD);
@@ -804,6 +1189,9 @@ static int sunxi_nfc_hw_syndrome_ecc_write_page(struct mtd_info *mtd,
 		if (ret)
 			return ret;
 
+		writel(readl(nfc->regs + NFC_REG_ECC_CTL) & ~NFC_RANDOM_EN,
+		       nfc->regs + NFC_REG_ECC_CTL);
+
 		offset += ecc->bytes + ecc->prepad;
 		oob += ecc->bytes + ecc->prepad;
 	}
@@ -812,9 +1200,11 @@ static int sunxi_nfc_hw_syndrome_ecc_write_page(struct mtd_info *mtd,
 		cnt = mtd->oobsize - (oob - chip->oob_poi);
 		if (cnt > 0) {
 			chip->cmdfunc(mtd, NAND_CMD_RNDIN, offset, -1);
-			chip->write_buf(mtd, oob, cnt);
+			nand_rnd_config(mtd, -1, offset, NAND_RND_WRITE);
+			nand_rnd_write_buf(mtd, oob, cnt);
 		}
 	}
+	nand_rnd_config(mtd, -1, -1, NAND_RND_WRITE);
 
 	tmp = readl(nfc->regs + NFC_REG_ECC_CTL);
 	tmp &= ~NFC_ECC_EN;
@@ -824,6 +1214,128 @@ static int sunxi_nfc_hw_syndrome_ecc_write_page(struct mtd_info *mtd,
 	return 0;
 }
 
+static u16 sunxi_nfc_hw_syndrome_ecc_rnd_steps(struct mtd_info *mtd, u16 state,
+					       int column, int *left)
+{
+	struct nand_chip *chip = mtd->priv;
+	struct nand_ecc_ctrl *ecc = chip->cur_ecc;
+	struct sunxi_nand_hw_rnd *rnd = chip->cur_rnd->priv;
+	int eccsteps = mtd->writesize / ecc->size;
+	int modsize = ecc->size + ecc->prepad + ecc->bytes;
+	int steps;
+
+	if (column < (eccsteps * modsize)) {
+		steps = column % modsize;
+		*left = modsize - steps;
+		if (steps >= ecc->size) {
+			steps -= ecc->size;
+			state = rnd->subseeds[rnd->page % rnd->nseeds];
+		}
+	} else {
+		steps = column % 4096;
+		*left = mtd->writesize + mtd->oobsize - column;
+	}
+
+	return sunxi_nfc_hwrnd_step(rnd, state, steps);
+}
+
+static u16 default_seeds[] = {0x4a80};
+
+static void sunxi_nand_rnd_ctrl_cleanup(struct nand_rnd_ctrl *rnd)
+{
+	struct sunxi_nand_hw_rnd *hwrnd = rnd->priv;
+
+	if (hwrnd->seeds != default_seeds)
+		kfree(hwrnd->seeds);
+	kfree(hwrnd->subseeds);
+	kfree(rnd->layout);
+	kfree(hwrnd);
+}
+
+static int sunxi_nand_rnd_ctrl_init(struct mtd_info *mtd,
+				    struct nand_rnd_ctrl *rnd,
+				    struct nand_ecc_ctrl *ecc,
+				    struct device_node *np)
+{
+	struct sunxi_nand_hw_rnd *hwrnd;
+	struct nand_rnd_layout *layout = NULL;
+	int ret;
+
+	hwrnd = kzalloc(sizeof(*hwrnd), GFP_KERNEL);
+	if (!hwrnd)
+		return -ENOMEM;
+
+	hwrnd->seeds = default_seeds;
+	hwrnd->nseeds = ARRAY_SIZE(default_seeds);
+
+	if (of_get_property(np, "nand-randomizer-seeds", &ret)) {
+		hwrnd->nseeds = ret / sizeof(*hwrnd->seeds);
+		hwrnd->seeds = kzalloc(hwrnd->nseeds * sizeof(*hwrnd->seeds),
+				       GFP_KERNEL);
+		if (!hwrnd->seeds) {
+			ret = -ENOMEM;
+			goto err;
+		}
+
+		ret = of_property_read_u16_array(np, "nand-randomizer-seeds",
+						 hwrnd->seeds, hwrnd->nseeds);
+		if (ret)
+			goto err;
+	}
+
+	switch (ecc->mode) {
+	case NAND_ECC_HW_SYNDROME:
+		hwrnd->step = sunxi_nfc_hw_syndrome_ecc_rnd_steps;
+		break;
+
+	case NAND_ECC_HW:
+		hwrnd->step = sunxi_nfc_hw_ecc_rnd_steps;
+
+	default:
+		layout = kzalloc(sizeof(*layout) + sizeof(struct nand_rndfree),
+				 GFP_KERNEL);
+		if (!layout) {
+			ret = -ENOMEM;
+			goto err;
+		}
+		layout->nranges = 1;
+		layout->ranges[0].offset = mtd->writesize;
+		layout->ranges[0].length = 2;
+		rnd->layout = layout;
+		break;
+	}
+
+	if (ecc->mode == NAND_ECC_HW_SYNDROME || ecc->mode == NAND_ECC_HW) {
+		int i;
+
+		hwrnd->subseeds = kzalloc(hwrnd->nseeds *
+					  sizeof(*hwrnd->subseeds),
+					  GFP_KERNEL);
+		if (!hwrnd->subseeds) {
+			ret = -ENOMEM;
+			goto err;
+		}
+
+		for (i = 0; i < hwrnd->nseeds; i++)
+			hwrnd->subseeds[i] = sunxi_nfc_hwrnd_step(hwrnd,
+							hwrnd->seeds[i],
+							ecc->size);
+	}
+
+	rnd->config = sunxi_nfc_hwrnd_config;
+	rnd->read_buf = sunxi_nfc_hwrnd_read_buf;
+	rnd->write_buf = sunxi_nfc_hwrnd_write_buf;
+	rnd->priv = hwrnd;
+
+	return 0;
+
+err:
+	kfree(hwrnd);
+	kfree(layout);
+
+	return ret;
+}
+
 static int sunxi_nand_chip_set_timings(struct sunxi_nand_chip *chip,
 				       const struct nand_sdr_timings *timings)
 {
@@ -1084,6 +1596,40 @@ static int sunxi_nand_hw_syndrome_ecc_ctrl_init(struct mtd_info *mtd,
 	return 0;
 }
 
+static void sunxi_nand_rnd_cleanup(struct nand_rnd_ctrl *rnd)
+{
+	switch (rnd->mode) {
+	case NAND_RND_HW:
+		sunxi_nand_rnd_ctrl_cleanup(rnd);
+		break;
+	default:
+		break;
+	}
+}
+
+static int sunxi_nand_rnd_init(struct mtd_info *mtd,
+			       struct nand_rnd_ctrl *rnd,
+			       struct nand_ecc_ctrl *ecc,
+			       struct device_node *np)
+{
+	int ret;
+
+	rnd->mode = NAND_RND_NONE;
+
+	ret = of_get_nand_rnd_mode(np);
+	if (ret >= 0)
+		rnd->mode = ret;
+
+	switch (rnd->mode) {
+	case NAND_RND_HW:
+		return sunxi_nand_rnd_ctrl_init(mtd, rnd, ecc, np);
+	default:
+		break;
+	}
+
+	return 0;
+}
+
 static void sunxi_nand_ecc_cleanup(struct nand_ecc_ctrl *ecc)
 {
 	switch (ecc->mode) {
@@ -1175,7 +1721,14 @@ struct nand_part *sunxi_ofnandpart_parse(void *priv, struct mtd_info *master,
 	if (ret)
 		goto err;
 
+	ret = sunxi_nand_rnd_init(master, &part->rnd, &part->ecc, pp);
+	if (ret) {
+		sunxi_nand_ecc_cleanup(&part->ecc);
+		goto err;
+	}
+
 	part->part.ecc = &part->ecc;
+	part->part.rnd = &part->rnd;
 
 	return &part->part;
 
@@ -1300,18 +1853,30 @@ static int sunxi_nand_chip_init(struct device *dev, struct sunxi_nfc *nfc,
 	if (ret)
 		return ret;
 
+	chip->buffer = kzalloc(mtd->writesize + mtd->oobsize, GFP_KERNEL);
+	if (!chip->buffer)
+		return -ENOMEM;
+
 	ret = sunxi_nand_chip_init_timings(chip, np);
 	if (ret) {
 		dev_err(dev, "could not configure chip timings: %d\n", ret);
 		return ret;
 	}
 
+	ret = nand_pst_create(mtd);
+	if (ret)
+		return ret;
+
 	ret = sunxi_nand_ecc_init(mtd, &nand->ecc, np);
 	if (ret) {
 		dev_err(dev, "ECC init failed: %d\n", ret);
 		return ret;
 	}
 
+	ret = sunxi_nand_rnd_init(mtd, &nand->rnd, &nand->ecc, np);
+	if (ret)
+		return ret;
+
 	ret = nand_scan_tail(mtd);
 	if (ret) {
 		dev_err(dev, "nand_scan_tail failed: %d\n", ret);
@@ -1367,6 +1932,8 @@ static void sunxi_nand_chips_cleanup(struct sunxi_nfc *nfc)
 					node);
 		nand_release(&chip->mtd);
 		sunxi_nand_ecc_cleanup(&chip->nand.ecc);
+		sunxi_nand_rnd_cleanup(&chip->nand.rnd);
+		kfree(chip->buffer);
 	}
 }