U-Boot
Threads by month
- ----- 2025 -----
- May
- April
- March
- February
- January
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2010 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2009 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2008 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2007 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2006 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2005 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2004 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2003 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2002 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2001 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2000 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
October 2008
- 175 participants
- 597 discussions

03 Nov '08
The NMDK8815 board is distributed by ST Microelectornics.
This is the initial port, with basic infrastructure and
a working serial port.
Signed-off-by: Alessandro Rubini <rubini(a)unipv.it>
---
MAINTAINERS | 5 +
MAKEALL | 1 +
Makefile | 11 ++
board/nmdk8815/Makefile | 53 ++++++
board/nmdk8815/config.mk | 26 +++
board/nmdk8815/nmdk8815.c | 79 +++++++++
board/nmdk8815/platform.S | 349 ++++++++++++++++++++++++++++++++++++++++
board/nmdk8815/u-boot.lds | 51 ++++++
cpu/arm926ejs/nomadik/Makefile | 46 ++++++
cpu/arm926ejs/nomadik/reset.S | 27 +++
cpu/arm926ejs/nomadik/timer.c | 172 ++++++++++++++++++++
include/configs/nmdk8815.h | 187 +++++++++++++++++++++
12 files changed, 1007 insertions(+), 0 deletions(-)
create mode 100644 board/nmdk8815/Makefile
create mode 100644 board/nmdk8815/config.mk
create mode 100644 board/nmdk8815/nmdk8815.c
create mode 100644 board/nmdk8815/platform.S
create mode 100644 board/nmdk8815/u-boot.lds
create mode 100644 cpu/arm926ejs/nomadik/Makefile
create mode 100644 cpu/arm926ejs/nomadik/reset.S
create mode 100644 cpu/arm926ejs/nomadik/timer.c
create mode 100644 include/configs/nmdk8815.h
diff --git a/MAINTAINERS b/MAINTAINERS
index 260c3e6..76438cc 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -581,6 +581,11 @@ Stefan Roese <sr(a)denx.de>
pdnb3 xscale
scpu xscale
+Alessandro Rubini <rubini(a)unipv.it>
+Nomadik Linux Team <STN_WMM_nomadik_linux(a)list.st.com>
+
+ nmdk8815 ARM926EJS (Nomadik 8815 Soc)
+
Robert Schwebel <r.schwebel(a)pengutronix.de>
csb226 xscale
diff --git a/MAKEALL b/MAKEALL
index 1f56ac5..347fe73 100755
--- a/MAKEALL
+++ b/MAKEALL
@@ -487,6 +487,7 @@ LIST_ARM9=" \
mx1ads \
mx1fs2 \
netstar \
+ nmdk8815 \
omap1510inn \
omap1610h2 \
omap1610inn \
diff --git a/Makefile b/Makefile
index d6abb4d..c671602 100644
--- a/Makefile
+++ b/Makefile
@@ -2612,6 +2612,17 @@ mx1fs2_config : unconfig
netstar_config: unconfig
@$(MKCONFIG) $(@:_config=) arm arm925t netstar
+nmdk8815_config \
+nmdk8815_onenand_config: unconfig
+ @ > $(obj)include/config.h
+ @if [ "$(findstring _onenand, $@)" ] ; then \
+ echo "#define CONFIG_BOOT_ONENAND" >> $(obj)include/config.h; \
+ $(XECHO) "... configured for OneNand Flash"; \
+ else \
+ $(XECHO) "... configured for Nand Flash"; \
+ fi
+ @./mkconfig -a nmdk8815 arm arm926ejs nmdk8815 NULL nomadik
+
omap1510inn_config : unconfig
@$(MKCONFIG) $(@:_config=) arm arm925t omap1510inn
diff --git a/board/nmdk8815/Makefile b/board/nmdk8815/Makefile
new file mode 100644
index 0000000..50ff6dd
--- /dev/null
+++ b/board/nmdk8815/Makefile
@@ -0,0 +1,53 @@
+#
+# (C) Copyright 2000-2004
+# Wolfgang Denk, DENX Software Engineering, wd(a)denx.de.
+#
+# (C) Copyright 2004
+# ARM Ltd.
+# Philippe Robin, <philippe.robin(a)arm.com>
+#
+# See file CREDITS for list of people who contributed to this
+# project.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2 of
+# the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+# MA 02111-1307 USA
+#
+
+include $(TOPDIR)/config.mk
+
+CFLAGS += -D__I2C_ENHANCED -D__RELEASE -D__NOMADIK_I2C -D__STN_8815=10
+CFLAGS += -DSTN8815 -D__MMCI_HCL_ENHANCED -D__MMCI_HCL_ELEMENTARY
+LIB = lib$(BOARD).a
+
+OBJS := nmdk8815.o
+SOBJS := platform.o
+
+$(LIB): $(OBJS) $(SOBJS)
+ $(AR) crv $@ $^
+
+clean:
+ rm -f $(SOBJS) $(OBJS)
+
+distclean: clean
+ rm -f $(LIB) core *.bak .depend
+
+#########################################################################
+
+.depend: Makefile $(SOBJS:.o=.S) $(OBJS:.o=.c)
+ $(CC) -M -g3 $(CPPFLAGS) $(SOBJS:.o=.S) $(OBJS:.o=.c) > $@
+
+-include .depend
+
+#########################################################################
diff --git a/board/nmdk8815/config.mk b/board/nmdk8815/config.mk
new file mode 100644
index 0000000..590393b
--- /dev/null
+++ b/board/nmdk8815/config.mk
@@ -0,0 +1,26 @@
+# (C) Copyright 2007
+# STMicroelectronics, <www.st.com>
+#
+# See file CREDITS for list of people who contributed to this
+# project.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2 of
+# the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+# MA 02111-1307 USA
+#
+#
+# image should be loaded at 0x01000000
+#
+
+TEXT_BASE = 0x03F80000
diff --git a/board/nmdk8815/nmdk8815.c b/board/nmdk8815/nmdk8815.c
new file mode 100644
index 0000000..15fa0a3
--- /dev/null
+++ b/board/nmdk8815/nmdk8815.c
@@ -0,0 +1,79 @@
+/*
+ * (C) Copyright 2005
+ * STMicrolelctronics, <www.st.com>
+ *
+ * (C) Copyright 2004
+ * ARM Ltd.
+ * Philippe Robin, <philippe.robin(a)arm.com>
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include <common.h>
+#include <linux/mtd/nand.h>
+
+
+
+#ifdef CONFIG_SHOW_BOOT_PROGRESS
+void show_boot_progress(int progress)
+{
+ printf("%i\n", progress);
+}
+#endif
+
+
+/*
+ * Miscellaneous platform dependent initialisations
+ */
+__u16 cpld_uart0 = 0x0100, uart0_value = 0;
+__u16 cpld_eth_reset = 0x6888, eth_reset_value = 0;
+
+int board_init(void)
+{
+ DECLARE_GLOBAL_DATA_PTR;
+ gd->bd->bi_arch_number = MACH_TYPE_NOMADIK;
+ gd->bd->bi_boot_params = 0x00000100;
+ *((volatile unsigned int *)(NOMADIK_GPIO1_BASE + 0x20)) = 0xC37800F0;
+ *((volatile unsigned int *)(NOMADIK_GPIO1_BASE + 0x24)) &= 0;
+ *((volatile unsigned int *)(NOMADIK_GPIO1_BASE + 0x28)) &= 0;
+ *((volatile unsigned int *)(NOMADIK_SRC_BASE)) |= 0x8000;
+
+ icache_enable();
+
+ return 0;
+}
+int misc_init_r(void)
+{
+ setenv("verify", "n");
+ return (0);
+}
+
+
+int dram_init(void)
+{
+ DECLARE_GLOBAL_DATA_PTR;
+
+ /* set dram bank start addr and size */
+ gd->bd->bi_dram[0].start = PHYS_SDRAM_1;
+ gd->bd->bi_dram[0].size = PHYS_SDRAM_1_SIZE;
+
+ gd->bd->bi_dram[1].start = PHYS_SDRAM_2;
+ gd->bd->bi_dram[1].size = PHYS_SDRAM_2_SIZE;
+ return 0;
+}
diff --git a/board/nmdk8815/platform.S b/board/nmdk8815/platform.S
new file mode 100644
index 0000000..6153da2
--- /dev/null
+++ b/board/nmdk8815/platform.S
@@ -0,0 +1,349 @@
+/*
+ * Board specific setup info
+ *
+ * (C) Copyright 2004, ARM Ltd.
+ * Philippe Robin, <philippe.robin(a)arm.com>
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include <config.h>
+#include <version.h>
+
+.globl lowlevel_init
+lowlevel_init:
+ /* Jump to the flash address */
+ ldr r0, =CFG_ONENAND_BASE
+
+ /* orr-ing makes it independent whether we boot from 0x0
+ or 0x30000000 */
+ /* This code is non-portable, relies on the knowledge that ip
+ has to be updated */
+ orr ip, ip, r0 /* adjust return address of cpu_init_crit */
+ orr lr, lr, r0 /* adjust return address */
+ orr pc, pc, r0 /* jump to the normal address */
+ nop
+
+ /* Initialize PLL, Remap clear, FSMC, MPMC here! */
+ /* What about GPIO, CLCD and UART */
+
+ /* PLL Initialization */
+ /*Prog the PLL1 @ 266 MHz ==> SDRAM Clock = 100.8 MHz*/
+ ldr r0, =NOMADIK_SRC_BASE
+
+ ldr r1, =0x2B013502
+
+ str r1, [r0, #0x14]
+
+
+ /*Used to set all the timers clock to 2.4MHZ*/
+ ldr r1, =0x2AAAA004
+ str r1, [r0]
+
+ ldr r1, =0x10000000
+ str r1, [r0, #0x10]
+
+ /* FSMC setup ---- */
+ ldr r0, =NOMADIK_FSMC_BASE
+
+ ldr r1, =0x10DB /* For 16-bit NOR flash */
+ str r1, [r0,#0x08]
+
+ ldr r1, =0x03333333 /* For 16-bit NOR flash */
+ str r1, [r0, #0xc]
+
+ /* oneNAND setting */
+ ldr r1, =0x0000105B /*BCR0 Prog control register*/
+ str r1, [r0]
+
+ ldr r1, =0x0A200551 /*BTR0 Prog timing register*/
+ str r1, [r0, #0x04]
+
+ /* preload the instructions into icache */
+ add r0, pc, #0x1F
+ bic r0, r0, #0x1F
+ mcr p15, 0, r0, c7, c13, 1
+ add r0, r0, #0x20
+ mcr p15, 0, r0, c7, c13, 1
+
+ /* Put the SDRAM under MPMC control */
+/* ldr r0, =NOMADIK_SRC_BASE
+
+ ldr r1, =0x00000008
+ str r1, [r0,#0x9000]
+
+*/
+
+ /* Now Clear Remap */
+ ldr r0, =NOMADIK_SRC_BASE
+
+ ldr r1, =0x2004
+ str r1, [r0]
+
+ ldr r1, =0x10000000
+ str r1, [r0,#0x10]
+
+ ldr r0,=0x101E9000
+ ldr r1, =0x2004
+ str r1, [r0]
+
+ ldr r0, =NOMADIK_SRC_BASE
+ ldr r1, =0x2104
+ str r1, [r0]
+
+ /* FSMC setup -- */
+ mov r0, #(NOMADIK_FSMC_BASE & 0x10000000)
+ orr r0, r0, #(NOMADIK_FSMC_BASE & 0x0FFFFFFF)
+
+ ldr r1, =0x10DB /* For 16-bit NOR flash */
+ str r1, [r0, #0x8]
+
+ ldr r1, =0x03333333 /* For 16-bit NOR flash */
+ str r1, [r0, #0xc]
+
+
+
+ /* MPMC Setup */
+ ldr r0, =NOMADIK_MPMC_BASE
+
+ ldr r1, =0xF00003
+ str r1, [r0] /* Enable the MPMC and the DLL*/
+
+ ldr r1, =0x183
+ str r1, [r0, #0x20]
+
+ ldr r2, =NOMADIK_PMU_BASE
+
+ ldr r1, =0x1111
+ str r1, [r2]
+
+ ldr r1, =0x1111 /* Prog the, mand delay strategy*/
+ str r1, [r0, #0x28]
+
+ ldr r1, =0x103 /* NOP ,mand */
+ str r1, [r0, #0x20]
+
+ /* FIXME -- Wait required here */
+
+ ldr r1, =0x103 /* PALL ,mand*/
+ str r1, [r0, #0x20]
+
+ ldr r1, =0x1
+ str r1, [r0, #0x24] /* To do at least two auto-refresh*/
+
+ /* FIXME -- Wait required here */
+
+ ldr r1, =0x31
+ str r1, [r0, #0x24] /* Prog the Auto-refresh period = 7.8µs @ SDRAM Clock = 100.8 MHz*/
+
+ ldr r1, =0x0
+ str r1, [r0, #0x8] /*Prog Little Endian, Not defined in 8800 board*/
+
+
+ ldr r1, =0x2
+ str r1, [r0, #0x30] /*Prog tRP timing*/
+
+ ldr r1, =0x4 /* Change for 8815 */
+ str r1, [r0, #0x34] /*Prog tRAS timing*/
+
+ ldr r1, =0xB
+ str r1, [r0, #0x38] /*Prog tSREX timing*/
+
+
+ ldr r1, =0x1
+ str r1, [r0, #0x44] /*Prog tWR timing */
+
+ ldr r1, =0x8
+ str r1, [r0, #0x48] /* Prog tRC timing*/
+
+ ldr r1, =0xA
+ str r1, [r0, #0x4C] /*Prog tRFC timing*/
+
+ ldr r1, =0xB
+ str r1, [r0, #0x50] /*Prog tXSR timing*/
+
+ ldr r1, =0x1
+ str r1, [r0, #0x54] /*Prog tRRD timing*/
+
+ ldr r1, =0x1
+ str r1, [r0, #0x58] /*Prog tMRD timing*/
+
+ ldr r1, =0x1
+ str r1, [r0, #0x5C] /*Prog tCDLR timing*/
+
+ /*DDR-SDRAM MEMORY IS ON BANK0* 8815/
+ ldr r1, =0x304 /*Prog RAS and CAS for Chip Select 0*/
+ str r1, [r0, #0x104]
+
+ /*SDR-SDRAM MEMORY IS ON BANK1 8815*/
+ ldr r1, =0x304 /*Prog RAS and CAS for Chip Select 1*/
+ str r1, [r0, #0x124]
+ /*THE DATA BUS WIDE IS PROGRAM FOR 16-BITS*/
+ /* DDR-SDRAM MEMORY IS ON BANK0*/
+
+ ldr r1, =0x884 /*8815 : Prog config register in BRC for Chip Select 0*/
+ str r1, [r0, #0x100]
+
+ /*SDR-SDRAM MEMORY IS ON BANK1*/
+
+ ldr r1, =0x884 /* 8815 : Prog config register in BRC for Chip Select 1*/
+ str r1, [r0, #0x120]
+
+ ldr r1, =0x83 /*MODE Mand*/
+ str r1, [r0, #0x20]
+
+ /*HERE, PROGRAM LOAD MODE REGISTER FOR 2 bursts of 16 bits, with DDR memory ON BANK0 */
+
+ ldr r1, =0x62000 /*Data in*/
+ ldr r1, [r1]
+
+ /*HERE, PROGRAM LOAD MODE REGISTER FOR 2 bursts of 16 bits, with DDR memory ON BANK1 */
+
+ ldr r1, =0x8062000
+ ldr r1, [r1]
+
+ ldr r1, =0x003 /* NORMAL ,mand and prog CE always high*/
+ str r1, [r0, #0x20]
+
+ /* ENABLE ALL THE BUFFER FOR EACH AHB PORT*/
+
+ ldr r1, =0x01 /*Enable buffer 0*/
+ str r1, [r0, #0x400]
+
+ ldr r1, =0x01 /*Enable buffer 1*/
+ str r1, [r0, #0x420]
+
+ ldr r1, =0x01 /*Enable buffer 2*/
+ str r1, [r0, #0x440]
+
+ ldr r1, =0x01 /*Enable buffer 3*/
+ str r1, [r0, #0x460]
+
+ ldr r1, =0x01 /*Enable buffer 4*/
+ str r1, [r0, #0x480]
+
+ ldr r1, =0x01 /*Enable buffer 5*/
+ str r1, [r0, #0x4A0]
+
+ /* GPIO settings */
+
+ ldr r0, =NOMADIK_GPIO1_BASE
+
+ ldr r1, =0xC0600000
+ str r1, [r0, #0x20]
+
+ ldr r1, =0x3F9FFFFF //ABHI change this for uart1
+ str r1, [r0, #0x24]
+
+ ldr r1, =0x3F9FFFFF //ABHI change this for uart1
+ str r1, [r0, #0x28]
+
+ ldr r0, =NOMADIK_GPIO0_BASE
+
+ ldr r1, =0xFFFFFFFF
+ str r1, [r0, #0x20]
+
+ ldr r1, =0x00
+ str r1, [r0, #0x24]
+
+ ldr r1, =0x00
+ str r1, [r0, #0x28]
+
+/*Configure CPLD_CTRL register for enabling MUX logic for UART0/UART2*/
+
+ ldr r0, =NOMADIK_FSMC_BASE
+
+ ldr r1,=0x10DB /*INIT FSMC bank 0*/
+ str r1, [r0, #0x00]
+
+ ldr r1,=0x0FFFFFFF
+ str r1, [r0, #0x04]
+
+ ldr r1,=0x010DB /*INIT FSMC bank 1*/
+ str r1, [r0, #0x08]
+
+ ldr r1,=0x00FFFFFFF
+ str r1, [r0, #0x0C]
+
+ ldr r0,=NOMADIK_UART0_BASE
+
+ ldr r1,=0x00000000
+ str r1,[r0,#0x30]
+
+ ldr r1,=0x0000004e
+ str r1,[r0,#0x24]
+
+ ldr r1,=0x00000008
+ str r1,[r0,#0x28]
+
+ ldr r1,=0x00000060
+ str r1,[r0,#0x2C]
+
+ ldr r1,=0x00000301
+ str r1,[r0,#0x30]
+
+ ldr r1,=0x00000066
+ str r1,[r0]
+
+
+ ldr r0,=NOMADIK_UART1_BASE
+
+ ldr r1,=0x00000000
+ str r1,[r0,#0x30]
+
+ ldr r1,=0x0000004e
+ str r1,[r0,#0x24]
+
+ ldr r1,=0x00000008
+ str r1,[r0,#0x28]
+
+ ldr r1,=0x00000060
+ str r1,[r0,#0x2C]
+
+ ldr r1,=0x00000301
+ str r1,[r0,#0x30]
+
+ ldr r1,=0x00000066
+ str r1,[r0]
+
+ ldr r0,=NOMADIK_UART2_BASE
+
+ ldr r1,=0x00000000
+ str r1,[r0,#0x30]
+
+ ldr r1,=0x0000004e
+ str r1,[r0,#0x24]
+
+ ldr r1,=0x00000008
+ str r1,[r0,#0x28]
+
+ ldr r1,=0x00000060
+ str r1,[r0,#0x2C]
+
+ ldr r1,=0x00000301
+ str r1,[r0,#0x30]
+
+ ldr r1,=0x00000066
+ str r1,[r0]
+
+ /*Configure CPLD to enable UART0*/
+
+
+ mov pc, lr
+
diff --git a/board/nmdk8815/u-boot.lds b/board/nmdk8815/u-boot.lds
new file mode 100644
index 0000000..eee4813
--- /dev/null
+++ b/board/nmdk8815/u-boot.lds
@@ -0,0 +1,51 @@
+/*
+ * (C) Copyright 2002
+ * Gary Jennejohn, DENX Software Engineering, <gj(a)denx.de>
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
+OUTPUT_ARCH(arm)
+ENTRY(_start)
+SECTIONS
+{
+ . = 0x00000000;
+ . = ALIGN(4);
+ .text :
+ {
+ cpu/arm926ejs/start.o (.text)
+ *(.text)
+ }
+ . = ALIGN(4);
+ .rodata : { *(.rodata) }
+ . = ALIGN(4);
+ .data : { *(.data) }
+ . = ALIGN(4);
+ .got : { *(.got) }
+
+ __u_boot_cmd_start = .;
+ .u_boot_cmd : { *(.u_boot_cmd) }
+ __u_boot_cmd_end = .;
+
+ . = ALIGN(4);
+ __bss_start = .;
+ .bss : { *(.bss) }
+ _end = .;
+}
diff --git a/cpu/arm926ejs/nomadik/Makefile b/cpu/arm926ejs/nomadik/Makefile
new file mode 100644
index 0000000..e3bd2ee
--- /dev/null
+++ b/cpu/arm926ejs/nomadik/Makefile
@@ -0,0 +1,46 @@
+#
+# (C) Copyright 2000-2006
+# Wolfgang Denk, DENX Software Engineering, wd(a)denx.de.
+#
+# See file CREDITS for list of people who contributed to this
+# project.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2 of
+# the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+# MA 02111-1307 USA
+#
+
+include $(TOPDIR)/config.mk
+
+LIB = $(obj)lib$(SOC).a
+
+COBJS = timer.o
+SOBJS = reset.o
+
+SRCS := $(SOBJS:.o=.S) $(COBJS:.o=.c)
+OBJS := $(addprefix $(obj),$(COBJS)) $(addprefix $(obj),$(SOBJS))
+
+all: $(obj).depend $(LIB)
+
+$(LIB): $(OBJS)
+ $(AR) $(ARFLAGS) $@ $(OBJS)
+
+#########################################################################
+
+# defines $(obj).depend target
+include $(SRCTREE)/rules.mk
+
+sinclude $(obj).depend
+
+#########################################################################
diff --git a/cpu/arm926ejs/nomadik/reset.S b/cpu/arm926ejs/nomadik/reset.S
new file mode 100644
index 0000000..522a817
--- /dev/null
+++ b/cpu/arm926ejs/nomadik/reset.S
@@ -0,0 +1,27 @@
+#include <config.h>
+/*
+ * Processor reset for Nomadik
+ */
+
+ .align 5
+.globl reset_cpu
+reset_cpu:
+#if defined CONFIG_NOMADIK_8815
+ ldr r0, =NOMADIK_SRC_BASE
+ ldr r1, =0x1
+ str r1, [r0, #0x18]
+#else
+ ldr r1, rstctl1 /* get clkm1 reset ctl */
+ mov r3, #0x0
+ strh r3, [r1] /* clear it */
+ mov r3, #0x8
+ strh r3, [r1] /* force dsp+arm reset */
+#endif
+_loop_forever:
+ b _loop_forever
+
+
+rstctl1:
+ .word 0xfffece10
+
+
diff --git a/cpu/arm926ejs/nomadik/timer.c b/cpu/arm926ejs/nomadik/timer.c
new file mode 100644
index 0000000..68b9217
--- /dev/null
+++ b/cpu/arm926ejs/nomadik/timer.c
@@ -0,0 +1,172 @@
+/*
+ * (C) Copyright 2003
+ * Texas Instruments <www.ti.com>
+ *
+ * (C) Copyright 2002
+ * Sysgo Real-Time Solutions, GmbH <www.elinos.com>
+ * Marius Groeger <mgroeger(a)sysgo.de>
+ *
+ * (C) Copyright 2002
+ * Sysgo Real-Time Solutions, GmbH <www.elinos.com>
+ * Alex Zuepke <azu(a)sysgo.de>
+ *
+ * (C) Copyright 2002-2004
+ * Gary Jennejohn, DENX Software Engineering, <gj(a)denx.de>
+ *
+ * (C) Copyright 2004
+ * Philippe Robin, ARM Ltd. <philippe.robin(a)arm.com>
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include <common.h>
+#include <arm926ejs.h>
+
+
+extern void reset_cpu(ulong addr);
+#define TIMER_LOAD_VAL 0xffffffff
+
+/* macro to read the 32 bit timer */
+#define READ_TIMER (*(volatile ulong *)(CONFIG_SYS_TIMERBASE+20))
+
+static ulong timestamp;
+static ulong lastdec;
+
+/* nothing really to do with interrupts, just starts up a counter. */
+int timer_init (void)
+{
+ /* Load timer with initial value */
+ *(volatile ulong *)(CONFIG_SYS_TIMERBASE + 16) = TIMER_LOAD_VAL;
+ /* Set timer to be enabled, free-running, no interrupts, 256 divider,
+ 32-bit, wrap-mode */
+ *(volatile ulong *)(CONFIG_SYS_TIMERBASE + 24) = 0x8A;
+ /* init the timestamp and lastdec value */
+ reset_timer_masked();
+ return 0;
+}
+
+/*
+ * timer without interrupts
+ */
+void reset_timer (void)
+{
+ reset_timer_masked ();
+}
+
+ulong get_timer (ulong base)
+{
+ return get_timer_masked () - base;
+}
+
+void set_timer (ulong t)
+{
+ timestamp = t;
+}
+
+/* delay x useconds AND perserve advance timstamp value */
+void udelay (unsigned long usec)
+{
+ ulong tmo, tmp;
+
+ if(usec >= 1000){ /* if "big" number, spread normalization to seconds */
+ tmo = usec / 1000; /* start to normalize for usec to ticks per sec */
+ tmo *= CONFIG_SYS_HZ; /* find number of "ticks" to wait to achieve target */
+ tmo /= 1000; /* finish normalize. */
+ }else{ /* else small number, don't kill it prior to HZ multiply */
+ tmo = usec * CONFIG_SYS_HZ;
+ tmo /= (1000*1000);
+ }
+
+ tmp = get_timer (0); /* get current timestamp */
+ if( (tmo + tmp + 1) < tmp ) /* if setting this fordward will roll time stamp */
+ reset_timer_masked (); /* reset "advancing" timestamp to 0, set lastdec value */
+ else
+ tmo += tmp; /* else, set advancing stamp wake up time */
+
+ while (get_timer_masked () < tmo)/* loop till event */
+ /*NOP*/;
+}
+
+void reset_timer_masked (void)
+{
+ /* reset time */
+ lastdec = READ_TIMER; /* capure current decrementer value time */
+ timestamp = 0; /* start "advancing" time stamp from 0 */
+}
+
+ulong get_timer_masked (void)
+{
+ ulong now = READ_TIMER; /* current tick value */
+
+ if (lastdec >= now) { /* normal mode (non roll) */
+ /* normal mode */
+ timestamp += lastdec - now; /* move stamp fordward with absoulte diff ticks */
+ } else { /* we have overflow of the count down timer */
+ /* nts = ts + ld + (TLV - now)
+ * ts=old stamp, ld=time that passed before passing through -1
+ * (TLV-now) amount of time after passing though -1
+ * nts = new "advancing time stamp"...it could also roll and cause problems.
+ */
+ timestamp += lastdec + TIMER_LOAD_VAL - now;
+ }
+ lastdec = now;
+
+ return timestamp;
+}
+
+/* waits specified delay value and resets timestamp */
+void udelay_masked (unsigned long usec)
+{
+ ulong tmo;
+
+ if(usec >= 1000){ /* if "big" number, spread normalization to seconds */
+ tmo = usec / 1000; /* start to normalize for usec to ticks per sec */
+ tmo *= CONFIG_SYS_HZ; /* find number of "ticks" to wait to achieve target */
+ tmo /= 1000; /* finish normalize. */
+ }else{ /* else small number, don't kill it prior to HZ multiply */
+ tmo = usec * CONFIG_SYS_HZ;
+ tmo /= (1000*1000);
+ }
+
+ reset_timer_masked (); /* set "advancing" timestamp to 0, set lastdec vaule */
+
+ while (get_timer_masked () < tmo) /* wait for time stamp to overtake tick number.*/
+ /*NOP*/;
+}
+
+/*
+ * This function is derived from PowerPC code (read timebase as long long).
+ * On ARM it just returns the timer value.
+ */
+unsigned long long get_ticks(void)
+{
+ return get_timer(0);
+}
+
+/*
+ * This function is derived from PowerPC code (timebase clock frequency).
+ * On ARM it returns the number of timer ticks per second.
+ */
+ulong get_tbclk (void)
+{
+ ulong tbclk;
+
+ tbclk = CONFIG_SYS_HZ;
+ return tbclk;
+}
diff --git a/include/configs/nmdk8815.h b/include/configs/nmdk8815.h
new file mode 100644
index 0000000..7d38c9d
--- /dev/null
+++ b/include/configs/nmdk8815.h
@@ -0,0 +1,187 @@
+/*
+ * (C) Copyright 2005
+ * STMicroelectronics.
+ * Configuration settings for the STn8815 nomadik board.
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#ifndef __CONFIG_H
+#define __CONFIG_H
+
+#define CONFIG_ARM926EJS
+#define CONFIG_NOMADIK
+#define CONFIG_NOMADIK_8815
+#define CONFIG_NOMADIK_NDK15
+#define CONFIG_NOMADIK_NHK15
+
+#define CONFIG_SKIP_LOWLEVEL_INIT /* we have already been loaded to RAM */
+
+/* commands */
+#include <config_cmd_default.h>
+#define CONFIG_CMD_PING
+#define CONFIG_CMD_DHCP
+/* At this point there is no flash driver, so remove some commands */
+#undef CONFIG_CMD_ENV
+#undef CONFIG_CMD_FLASH
+#undef CONFIG_CMD_IMLS
+
+/* user interface */
+#define CONFIG_SYS_LONGHELP
+#define CONFIG_SYS__HUSH_PARSER
+#define CONFIG_SYS_PROMPT "Nomadik> "
+#define CONFIG_SYS_PROMPT_HUSH_PS2 "> "
+#define CONFIG_SYS_CBSIZE 256 /* Console I/O Buffer Size */
+#define CONFIG_SYS_PBSIZE (CONFIG_SYS_CBSIZE+sizeof(CONFIG_SYS_PROMPT)+16)
+#define CONFIG_SYS_BARGSIZE CONFIG_SYS_CBSIZE /* Boot Argument Buffer Size */
+#define CONFIG_SYS_MAXARGS 16
+#define CONFIG_SYS_LOAD_ADDR 0x800000 /* default load address */
+#define CONFIG_SYS_LOADS_BAUD_CHANGE
+
+/* boot config */
+#define CONFIG_SETUP_MEMORY_TAGS
+#define CONFIG_INITRD_TAG
+#define CONFIG_CMDLINE_TAG
+#define CONFIG_BOOTDELAY 1
+#define CONFIG_BOOTARGS "root=/dev/ram0 console=ttyAMA1,115200n8 init=linuxrc"
+#define CONFIG_BOOTCOMMAND "fsload 0x100000 kernel.uimg;" \
+ " fsload 0x800000 initrd.gz.uimg;" \
+ " bootm 0x100000 0x800000"
+
+/* memory-related information */
+#define CONFIG_NR_DRAM_BANKS 2
+#define PHYS_SDRAM_1 0x00000000 /* DDR-SDRAM Bank #1 */
+#define PHYS_SDRAM_1_SIZE 0x04000000 /* 64 MB */
+#define PHYS_SDRAM_2 0x08000000 /* SDR-SDRAM BANK #2*/
+#define PHYS_SDRAM_2_SIZE 0x04000000 /* 64 MB */
+
+#define CONFIG_STACKSIZE (128*1024) /* regular stack */
+#ifdef CONFIG_USE_IRQ
+# define CONFIG_STACKSIZE_IRQ (4*1024) /* IRQ stack */
+# define CONFIG_STACKSIZE_FIQ (4*1024) /* FIQ stack */
+#endif
+
+#define CONFIG_SYS_MEMTEST_START 0x00000000
+#define CONFIG_SYS_MEMTEST_END 0x0FFFFFFF
+#define CONFIG_SYS_MALLOC_LEN (CONFIG_ENV_SIZE + 256*1024)
+#define CONFIG_SYS_GBL_DATA_SIZE 128 /* for initial data */
+
+#define CONFIG_MISC_INIT_R /* call misc_init_r during start up */
+
+/* timing informazion */
+#define CONFIG_SYS_HZ (2400000 / 256) /* Timer0: 2.4Mhz + divider */
+#define CONFIG_SYS_TIMERBASE 0x101E2000
+#undef CONFIG_SYS_CLKS_IN_HZ
+
+/* serial port (PL011) configuration */
+#define CONFIG_PL011_SERIAL
+#define CONFIG_CONS_INDEX 1
+#define CONFIG_BAUDRATE 115200
+#define CONFIG_SYS_BAUDRATE_TABLE { 9600, 19200, 38400, 57600, 115200 }
+#define CFG_SERIAL0 0x101FD000
+#define CFG_SERIAL1 0x101FB000
+
+#define CONFIG_PL01x_PORTS { (void *)CFG_SERIAL0, (void *)CFG_SERIAL1 }
+#define CONFIG_PL011_CLOCK 48000000
+
+/* Ethernet */
+#define PCI_MEMORY_VADDR 0xe8000000
+#define PCI_IO_VADDR 0xee000000
+#define __io(a) ((void __iomem *)(PCI_IO_VADDR + (a)))
+#define __mem_isa(a) ((a) + PCI_MEMORY_VADDR)
+
+#define CONFIG_DRIVER_SMC91111 /* Using SMC91c111*/
+#define CONFIG_SMC91111_BASE 0x34000300
+#undef CONFIG_SMC91111_EXT_PHY /* Internal PHY */
+#define CONFIG_SMC_USE_32_BIT
+#define CONFIG_BOOTFILE "uImage"
+
+/* flash memory and filesystem information */
+#define CONFIG_DOS_PARTITION
+#define CONFIG_MTD_ONENAND_VERIFY_WRITE
+#define CONFIG_SYS_ONENAND_BASE 0x30000000
+#define CONFIG_SYS_MAX_NAND_DEVICE 1
+#define CONFIG_SYS_NAND_BASE 0x40000000
+
+#define CONFIG_SYS_NO_FLASH
+
+#ifdef CONFIG_BOOT_ONENAND
+
+# undef CONFIG_CMD_NAND /* Temporary: nand and onenand can't coexist */
+ /* Partition Size Start
+ * XloaderTOC + X-Loader 256KB 0x00000000
+ * Memory init function 256KB 0x00040000
+ * U-Boot 2MB 0x00080000
+ * Sysimage (kernel + ramdisk) 4MB 0x00280000
+ * JFFS2 Root filesystem 22MB 0x00680000
+ * JFFS2 User Data 227.5MB 0x01C80000
+ */
+# define CONFIG_JFFS2_PART_SIZE 0x400000
+# define CONFIG_JFFS2_PART_OFFSET 0x280000
+
+# define CONFIG_ENV_IS_IN_ONENAND
+# define CONFIG_ENV_SIZE (256*1024)
+# define CONFIG_ENV_ADDR 0x30300000
+
+#else /* ! CONFIG_BOOT_ONENAND */
+
+# undef CONFIG_CMD_ONENAND /* Temporary: nand and onenand can't coexist */
+
+# define CONFIG_JFFS2_DEV "nand0"
+# define CONFIG_JFFS2_NAND 1 /* For the jffs2 support*/
+# define CONFIG_JFFS2_PART_SIZE 0x00300000
+# define CONFIG_JFFS2_PART_OFFSET 0x00280000
+
+# define CONFIG_ENV_IS_IN_NAND
+# define CONFIG_ENV_SIZE 0x20000 /*128 Kb*/
+# define CONFIG_ENV_OFFSET (0x8000000 - 0x20000)
+
+#endif /* CONFIG_BOOT_ONENAND */
+
+/* Temporarily, until we have no driver, env is not in nand */
+#undef CONFIG_ENV_IS_IN_NAND
+#define CONFIG_ENV_IS_NOWHERE
+
+/* this is needed to make hello_world.c and other stuff happy */
+#define CONFIG_SYS_MAX_FLASH_SECT 512
+#define CONFIG_SYS_MAX_FLASH_BANKS 1
+
+/* base addresses of our peripherals */
+#define NOMADIK_SRC_BASE 0x101E0000 /* System and Reset Cnt */
+#define NOMADIK_PMU_BASE 0x101E9000 /* Power Management Unit */
+#define NOMADIK_MPMC_BASE 0x10110000 /* SDRAM Controller */
+#define NOMADIK_FSMC_BASE 0x10100000 /* FSMC Controller */
+#define NOMADIK_1NAND_BASE 0x30000000
+#define NOMADIK_GPIO0_BASE 0x101E4000
+#define NOMADIK_GPIO1_BASE 0x101E5000
+#define NOMADIK_GPIO2_BASE 0x101E6000
+#define NOMADIK_GPIO3_BASE 0x101E7000
+#define NOMADIK_CPLD_BASE 0x36000000
+#define NOMADIK_UART0_BASE 0x101FD000
+#define NOMADIK_UART1_BASE 0x101FB000
+#define NOMADIK_UART2_BASE 0x101F2000
+
+#define NOMADIK_I2C1_BASE 0x101F7000 /* I2C1 interface */
+#define NOMADIK_I2C0_BASE 0x101F8000 /* I2C0 interface */
+
+#define NOMADIK_RTC_BASE 0x101E8000
+#define NOMADIK_ETH0_BASE 0x36800300
+#define NOMADIK_CPLD_UART_BASE 0x36480000
+
+#endif /* __CONFIG_H */
--
1.6.0.2
2
2

[U-Boot] [PATCH 1/3] Changed PPC4xx EMAC driver to require CONFIG_PPC4xx_EMAC
by Ben Warren 03 Nov '08
by Ben Warren 03 Nov '08
03 Nov '08
All in-tree IBM/AMCC PPC4xx boards using the EMAC get this new CONFIG
Signed-off-by: Ben Warren <biggerbadderben(a)gmail.com>
---
cpu/ppc4xx/4xx_enet.c | 9 ---------
cpu/ppc4xx/Makefile | 1 +
include/configs/AR405.h | 1 +
include/configs/ASH405.h | 1 +
include/configs/CMS700.h | 1 +
include/configs/CPCI405.h | 1 +
include/configs/CPCI4052.h | 1 +
include/configs/CPCI405AB.h | 1 +
include/configs/CPCI405DT.h | 1 +
include/configs/CPCIISER4.h | 1 +
include/configs/CRAYL1.h | 2 ++
include/configs/DP405.h | 1 +
include/configs/DU405.h | 1 +
include/configs/DU440.h | 1 +
include/configs/ERIC.h | 1 +
include/configs/EXBITGEN.h | 1 +
include/configs/G2000.h | 1 +
include/configs/HH405.h | 1 +
include/configs/HUB405.h | 1 +
include/configs/JSE.h | 1 +
include/configs/KAREF.h | 1 +
include/configs/METROBOX.h | 1 +
include/configs/MIP405.h | 1 +
include/configs/OCRTC.h | 1 +
include/configs/ORSG.h | 1 +
include/configs/PCI405.h | 1 +
include/configs/PIP405.h | 1 +
include/configs/PLU405.h | 1 +
include/configs/PMC405.h | 1 +
include/configs/PMC440.h | 1 +
include/configs/PPChameleonEVB.h | 1 +
include/configs/VOH405.h | 1 +
include/configs/VOM405.h | 1 +
include/configs/W7OLMC.h | 1 +
include/configs/W7OLMG.h | 1 +
include/configs/WUH405.h | 1 +
include/configs/XPEDITE1K.h | 1 +
include/configs/alpr.h | 1 +
include/configs/amcc-common.h | 1 +
include/configs/csb272.h | 1 +
include/configs/csb472.h | 1 +
include/configs/korat.h | 1 +
include/configs/lwmon5.h | 1 +
include/configs/netstal-common.h | 1 +
include/configs/p3p440.h | 1 +
include/configs/pcs440ep.h | 1 +
include/configs/quad100hd.h | 1 +
include/configs/sbc405.h | 1 +
include/configs/sc3.h | 2 ++
include/configs/zeus.h | 1 +
net/eth.c | 5 ++---
51 files changed, 53 insertions(+), 12 deletions(-)
diff --git a/cpu/ppc4xx/4xx_enet.c b/cpu/ppc4xx/4xx_enet.c
index d7b16da..1978269 100644
--- a/cpu/ppc4xx/4xx_enet.c
+++ b/cpu/ppc4xx/4xx_enet.c
@@ -91,13 +91,6 @@
#include <miiphy.h>
#include <malloc.h>
-/*
- * Only compile for platform with AMCC EMAC ethernet controller and
- * network support enabled.
- * Remark: CONFIG_405 describes Xilinx PPC405 FPGA without EMAC controller!
- */
-#if defined(CONFIG_CMD_NET) && !defined(CONFIG_405) && !defined(CONFIG_IOP480)
-
#if !(defined(CONFIG_MII) || defined(CONFIG_CMD_MII))
#error "CONFIG_MII has to be defined!"
#endif
@@ -2131,5 +2124,3 @@ int emac4xx_miiphy_initialize (bd_t * bis)
return 0;
}
#endif /* !defined(CONFIG_NET_MULTI) */
-
-#endif
diff --git a/cpu/ppc4xx/Makefile b/cpu/ppc4xx/Makefile
index 463b575..adfe13c 100644
--- a/cpu/ppc4xx/Makefile
+++ b/cpu/ppc4xx/Makefile
@@ -38,6 +38,7 @@ COBJS += 44x_spd_ddr2.o
ifdef CONFIG_PPC4xx_DDR_AUTOCALIBRATION
COBJS += 4xx_ibm_ddr2_autocalib.o
endif
+COBJS-$(CONFIG_PPC4xx_EMAC) += 4xx_enet.o
COBJS += 4xx_pci.o
COBJS += 4xx_pcie.o
COBJS += bedbug_405.o
diff --git a/include/configs/AR405.h b/include/configs/AR405.h
index 864774c..9f19269 100644
--- a/include/configs/AR405.h
+++ b/include/configs/AR405.h
@@ -67,6 +67,7 @@
#define CONFIG_LOADS_ECHO 1 /* echo on for serial download */
#define CONFIG_SYS_LOADS_BAUD_CHANGE 1 /* allow baudrate change */
+#define CONFIG_PPC4xx_EMAC
#define CONFIG_MII 1 /* MII PHY management */
#define CONFIG_PHY_ADDR 0 /* PHY address */
#define CONFIG_LXT971_NO_SLEEP 1 /* disable sleep mode in LXT971 */
diff --git a/include/configs/ASH405.h b/include/configs/ASH405.h
index bcc85ee..a694083 100644
--- a/include/configs/ASH405.h
+++ b/include/configs/ASH405.h
@@ -56,6 +56,7 @@
#define CONFIG_NET_MULTI 1
#undef CONFIG_HAS_ETH1
+#define CONFIG_PPC4xx_EMAC
#define CONFIG_MII 1 /* MII PHY management */
#define CONFIG_PHY_ADDR 0 /* PHY address */
#define CONFIG_LXT971_NO_SLEEP 1 /* disable sleep mode in LXT971 */
diff --git a/include/configs/CMS700.h b/include/configs/CMS700.h
index d58f508..d0e2464 100644
--- a/include/configs/CMS700.h
+++ b/include/configs/CMS700.h
@@ -52,6 +52,7 @@
#define CONFIG_SYS_LOADS_BAUD_CHANGE 1 /* allow baudrate change */
+#define CONFIG_PPC4xx_EMAC
#define CONFIG_NET_MULTI 1
#undef CONFIG_HAS_ETH1
diff --git a/include/configs/CPCI405.h b/include/configs/CPCI405.h
index 3493d75..1a2bc1c 100644
--- a/include/configs/CPCI405.h
+++ b/include/configs/CPCI405.h
@@ -52,6 +52,7 @@
#define CONFIG_LOADS_ECHO 1 /* echo on for serial download */
#define CONFIG_SYS_LOADS_BAUD_CHANGE 1 /* allow baudrate change */
+#define CONFIG_PPC4xx_EMAC
#define CONFIG_MII 1 /* MII PHY management */
#define CONFIG_PHY_ADDR 0 /* PHY address */
#define CONFIG_LXT971_NO_SLEEP 1 /* disable sleep mode in LXT971 */
diff --git a/include/configs/CPCI4052.h b/include/configs/CPCI4052.h
index 734ab95..e231fa7 100644
--- a/include/configs/CPCI4052.h
+++ b/include/configs/CPCI4052.h
@@ -54,6 +54,7 @@
#define CONFIG_LOADS_ECHO 1 /* echo on for serial download */
#define CONFIG_SYS_LOADS_BAUD_CHANGE 1 /* allow baudrate change */
+#define CONFIG_PPC4xx_EMAC
#define CONFIG_MII 1 /* MII PHY management */
#define CONFIG_PHY_ADDR 0 /* PHY address */
#define CONFIG_LXT971_NO_SLEEP 1 /* disable sleep mode in LXT971 */
diff --git a/include/configs/CPCI405AB.h b/include/configs/CPCI405AB.h
index 47ad89d..2319c58 100644
--- a/include/configs/CPCI405AB.h
+++ b/include/configs/CPCI405AB.h
@@ -54,6 +54,7 @@
#undef CONFIG_LOADS_ECHO /* echo on for serial download */
#define CONFIG_SYS_LOADS_BAUD_CHANGE 1 /* allow baudrate change */
+#define CONFIG_PPC4xx_EMAC
#define CONFIG_MII 1 /* MII PHY management */
#define CONFIG_PHY_ADDR 0 /* PHY address */
#define CONFIG_LXT971_NO_SLEEP 1 /* disable sleep mode in LXT971 */
diff --git a/include/configs/CPCI405DT.h b/include/configs/CPCI405DT.h
index 4e94dfc..be8c238 100644
--- a/include/configs/CPCI405DT.h
+++ b/include/configs/CPCI405DT.h
@@ -53,6 +53,7 @@
#define CONFIG_LOADS_ECHO 1 /* echo on for serial download */
#define CONFIG_SYS_LOADS_BAUD_CHANGE 1 /* allow baudrate change */
+#define CONFIG_PPC4xx_EMAC
#define CONFIG_MII 1 /* MII PHY management */
#define CONFIG_PHY_ADDR 0 /* PHY address */
#define CONFIG_LXT971_NO_SLEEP 1 /* disable sleep mode in LXT971 */
diff --git a/include/configs/CPCIISER4.h b/include/configs/CPCIISER4.h
index 2d60ebf..3263712 100644
--- a/include/configs/CPCIISER4.h
+++ b/include/configs/CPCIISER4.h
@@ -50,6 +50,7 @@
#define CONFIG_LOADS_ECHO 1 /* echo on for serial download */
#define CONFIG_SYS_LOADS_BAUD_CHANGE 1 /* allow baudrate change */
+#define CONFIG_PPC4xx_EMAC
#define CONFIG_MII 1 /* MII PHY management */
#define CONFIG_PHY_ADDR 0 /* PHY address */
#define CONFIG_LXT971_NO_SLEEP 1 /* disable sleep mode in LXT971 */
diff --git a/include/configs/CRAYL1.h b/include/configs/CRAYL1.h
index 1122d02..f1608e1 100644
--- a/include/configs/CRAYL1.h
+++ b/include/configs/CRAYL1.h
@@ -39,6 +39,8 @@
#define CONFIG_SYS_CLK_FREQ 25000000
#define CONFIG_BAUDRATE 9600
#define CONFIG_BOOTDELAY 5 /* autoboot after 5 seconds */
+
+#define CONFIG_PPC4xx_EMAC
#define CONFIG_MII 1 /* MII PHY management */
#define CONFIG_PHY_ADDR 1 /* PHY address; handling of ENET */
#define CONFIG_BOARD_EARLY_INIT_F 1 /* early setup for 405gp */
diff --git a/include/configs/DP405.h b/include/configs/DP405.h
index 0ff4f7d..884f3fe 100644
--- a/include/configs/DP405.h
+++ b/include/configs/DP405.h
@@ -52,6 +52,7 @@
#define CONFIG_SYS_LOADS_BAUD_CHANGE 1 /* allow baudrate change */
+#define CONFIG_PPC4xx_EMAC
#define CONFIG_MII 1 /* MII PHY management */
#define CONFIG_PHY_ADDR 0 /* PHY address */
diff --git a/include/configs/DU405.h b/include/configs/DU405.h
index 939e216..1d20efe 100644
--- a/include/configs/DU405.h
+++ b/include/configs/DU405.h
@@ -54,6 +54,7 @@
#define CONFIG_LOADS_ECHO 1 /* echo on for serial download */
#define CONFIG_SYS_LOADS_BAUD_CHANGE 1 /* allow baudrate change */
+#define CONFIG_PPC4xx_EMAC
#define CONFIG_MII 1 /* MII PHY management */
#define CONFIG_PHY_ADDR 0 /* PHY address */
#define CONFIG_LXT971_NO_SLEEP 1 /* disable sleep mode in LXT971 */
diff --git a/include/configs/DU440.h b/include/configs/DU440.h
index 508a0ca..729153c 100644
--- a/include/configs/DU440.h
+++ b/include/configs/DU440.h
@@ -253,6 +253,7 @@
int du440_phy_addr(int devnum);
#endif
+#define CONFIG_PPC4xx_EMAC
#define CONFIG_IBM_EMAC4_V4 1
#define CONFIG_MII 1 /* MII PHY management */
#define CONFIG_PHY_ADDR du440_phy_addr(0) /* PHY address */
diff --git a/include/configs/ERIC.h b/include/configs/ERIC.h
index c05945a..1b766a7 100644
--- a/include/configs/ERIC.h
+++ b/include/configs/ERIC.h
@@ -93,6 +93,7 @@
#define CONFIG_LOADS_ECHO 1 /* echo on for serial download */
#define CONFIG_SYS_LOADS_BAUD_CHANGE 1 /* allow baudrate change */
+#define CONFIG_PPC4xx_EMAC
#define CONFIG_MII 1 /* MII PHY management */
#define CONFIG_PHY_ADDR 1 /* PHY address */
diff --git a/include/configs/EXBITGEN.h b/include/configs/EXBITGEN.h
index 9f5d3ca..1dd6e57 100644
--- a/include/configs/EXBITGEN.h
+++ b/include/configs/EXBITGEN.h
@@ -88,6 +88,7 @@
*/
#define CONFIG_BOOTP_BOOTFILESIZE
#define CONFIG_BOOTP_BOOTPATH
+#define CONFIG_PPC4xx_EMAC
#define CONFIG_BOOTP_GATEWAY
#define CONFIG_BOOTP_HOSTNAME
diff --git a/include/configs/G2000.h b/include/configs/G2000.h
index 4341f02..d299044 100644
--- a/include/configs/G2000.h
+++ b/include/configs/G2000.h
@@ -80,6 +80,7 @@
#define CONFIG_NET_MULTI 1
+#define CONFIG_PPC4xx_EMAC
#define CONFIG_MII 1 /* MII PHY management */
#define CONFIG_PHY_ADDR 0 /* PHY address */
#define CONFIG_PHY1_ADDR 1 /* PHY address */
diff --git a/include/configs/HH405.h b/include/configs/HH405.h
index 1e7cc12..80e59bb 100644
--- a/include/configs/HH405.h
+++ b/include/configs/HH405.h
@@ -64,6 +64,7 @@
#define CONFIG_SYS_LOADS_BAUD_CHANGE 1 /* allow baudrate change */
+#define CONFIG_PPC4xx_EMAC
#define CONFIG_NET_MULTI 1
#undef CONFIG_HAS_ETH1
diff --git a/include/configs/HUB405.h b/include/configs/HUB405.h
index 3e58424..b3c7046 100644
--- a/include/configs/HUB405.h
+++ b/include/configs/HUB405.h
@@ -54,6 +54,7 @@
#define CONFIG_SYS_LOADS_BAUD_CHANGE 1 /* allow baudrate change */
+#define CONFIG_PPC4xx_EMAC
#define CONFIG_MII 1 /* MII PHY management */
#define CONFIG_PHY_ADDR 0 /* PHY address */
#define CONFIG_LXT971_NO_SLEEP 1 /* disable sleep mode in LXT971 */
diff --git a/include/configs/JSE.h b/include/configs/JSE.h
index 508b5c8..8aca1f9 100644
--- a/include/configs/JSE.h
+++ b/include/configs/JSE.h
@@ -132,6 +132,7 @@
#define CONFIG_LOADS_ECHO 1 /* echo on for serial download */
#define CONFIG_SYS_LOADS_BAUD_CHANGE 1 /* allow baudrate change */
+#define CONFIG_PPC4xx_EMAC
#define CONFIG_MII 1 /* MII PHY management */
#define CONFIG_PHY_ADDR 1 /* PHY address */
diff --git a/include/configs/KAREF.h b/include/configs/KAREF.h
index 403081d..4b67c94 100644
--- a/include/configs/KAREF.h
+++ b/include/configs/KAREF.h
@@ -157,6 +157,7 @@
/*-----------------------------------------------------------------------
* Networking
*----------------------------------------------------------------------*/
+#define CONFIG_PPC4xx_EMAC
#define CONFIG_MII 1 /* MII PHY management */
#define CONFIG_NET_MULTI 1
#define CONFIG_PHY_ADDR 0xff /* no phy on EMAC0 */
diff --git a/include/configs/METROBOX.h b/include/configs/METROBOX.h
index c0ddd45..518173a 100644
--- a/include/configs/METROBOX.h
+++ b/include/configs/METROBOX.h
@@ -221,6 +221,7 @@
/*-----------------------------------------------------------------------
* Networking
*----------------------------------------------------------------------*/
+#define CONFIG_PPC4xx_EMAC
#define CONFIG_MII 1 /* MII PHY management */
#define CONFIG_NET_MULTI 1
#define CONFIG_PHY_ADDR 0xff /* no phy on EMAC0 */
diff --git a/include/configs/MIP405.h b/include/configs/MIP405.h
index 7dcf185..c58ce05 100644
--- a/include/configs/MIP405.h
+++ b/include/configs/MIP405.h
@@ -336,6 +336,7 @@
/************************************************************
* Ethernet Stuff
***********************************************************/
+#define CONFIG_PPC4xx_EMAC
#define CONFIG_MII 1 /* MII PHY management */
#define CONFIG_PHY_ADDR 1 /* PHY address */
#define CONFIG_PHY_RESET_DELAY 300 /* Intel LXT971A needs this */
diff --git a/include/configs/OCRTC.h b/include/configs/OCRTC.h
index ff11df9..32814d4 100644
--- a/include/configs/OCRTC.h
+++ b/include/configs/OCRTC.h
@@ -50,6 +50,7 @@
#define CONFIG_LOADS_ECHO 1 /* echo on for serial download */
#define CONFIG_SYS_LOADS_BAUD_CHANGE 1 /* allow baudrate change */
+#define CONFIG_PPC4xx_EMAC
#define CONFIG_MII 1 /* MII PHY management */
#define CONFIG_PHY_ADDR 0 /* PHY address */
#define CONFIG_LXT971_NO_SLEEP 1 /* disable sleep mode in LXT971 */
diff --git a/include/configs/ORSG.h b/include/configs/ORSG.h
index a635fca..58e9328 100644
--- a/include/configs/ORSG.h
+++ b/include/configs/ORSG.h
@@ -50,6 +50,7 @@
#define CONFIG_LOADS_ECHO 1 /* echo on for serial download */
#define CONFIG_SYS_LOADS_BAUD_CHANGE 1 /* allow baudrate change */
+#define CONFIG_PPC4xx_EMAC
#define CONFIG_MII 1 /* MII PHY management */
#define CONFIG_PHY_ADDR 0 /* PHY address */
#define CONFIG_LXT971_NO_SLEEP 1 /* disable sleep mode in LXT971 */
diff --git a/include/configs/PCI405.h b/include/configs/PCI405.h
index b55e383..0393366 100644
--- a/include/configs/PCI405.h
+++ b/include/configs/PCI405.h
@@ -63,6 +63,7 @@
#define CONFIG_LOADS_ECHO 1 /* echo on for serial download */
#define CONFIG_SYS_LOADS_BAUD_CHANGE 1 /* allow baudrate change */
+#define CONFIG_PPC4xx_EMAC
#define CONFIG_MII 1 /* MII PHY management */
#define CONFIG_PHY_ADDR 0 /* PHY address */
diff --git a/include/configs/PIP405.h b/include/configs/PIP405.h
index 2966979..5c4d69b 100644
--- a/include/configs/PIP405.h
+++ b/include/configs/PIP405.h
@@ -278,6 +278,7 @@
/************************************************************
* Ethernet Stuff
***********************************************************/
+#define CONFIG_PPC4xx_EMAC
#define CONFIG_MII 1 /* MII PHY management */
#define CONFIG_PHY_ADDR 1 /* PHY address */
/************************************************************
diff --git a/include/configs/PLU405.h b/include/configs/PLU405.h
index 675dbe6..11ce008 100644
--- a/include/configs/PLU405.h
+++ b/include/configs/PLU405.h
@@ -54,6 +54,7 @@
#define CONFIG_NET_MULTI 1
#undef CONFIG_HAS_ETH1
+#define CONFIG_PPC4xx_EMAC
#define CONFIG_MII 1 /* MII PHY management */
#define CONFIG_PHY_ADDR 0 /* PHY address */
#define CONFIG_LXT971_NO_SLEEP 1 /* disable sleep mode in LXT971 */
diff --git a/include/configs/PMC405.h b/include/configs/PMC405.h
index 12e63b7..8d07d77 100644
--- a/include/configs/PMC405.h
+++ b/include/configs/PMC405.h
@@ -56,6 +56,7 @@
#define CONFIG_NET_MULTI 1
#undef CONFIG_HAS_ETH1
+#define CONFIG_PPC4xx_EMAC
#define CONFIG_MII 1 /* MII PHY management */
#define CONFIG_PHY_ADDR 0 /* PHY address */
#define CONFIG_LXT971_NO_SLEEP 1 /* disable sleep mode in LXT971 */
diff --git a/include/configs/PMC440.h b/include/configs/PMC440.h
index 85342a6..841985c 100644
--- a/include/configs/PMC440.h
+++ b/include/configs/PMC440.h
@@ -299,6 +299,7 @@
#define CONFIG_LOADS_ECHO 1 /* echo on for serial download */
#define CONFIG_SYS_LOADS_BAUD_CHANGE 1 /* allow baudrate change */
+#define CONFIG_PPC4xx_EMAC
#define CONFIG_IBM_EMAC4_V4 1
#define CONFIG_MII 1 /* MII PHY management */
#define CONFIG_PHY_ADDR 0 /* PHY address, See schematics */
diff --git a/include/configs/PPChameleonEVB.h b/include/configs/PPChameleonEVB.h
index e66f8ef..09a9641 100644
--- a/include/configs/PPChameleonEVB.h
+++ b/include/configs/PPChameleonEVB.h
@@ -104,6 +104,7 @@
#undef CONFIG_EXT_PHY
#define CONFIG_NET_MULTI 1
+#define CONFIG_PPC4xx_EMAC
#define CONFIG_MII 1 /* MII PHY management */
#ifndef CONFIG_EXT_PHY
#define CONFIG_PHY_ADDR 1 /* EMAC0 PHY address */
diff --git a/include/configs/VOH405.h b/include/configs/VOH405.h
index fb1febc..10ef620 100644
--- a/include/configs/VOH405.h
+++ b/include/configs/VOH405.h
@@ -55,6 +55,7 @@
#define CONFIG_NET_MULTI 1
#undef CONFIG_HAS_ETH1
+#define CONFIG_PPC4xx_EMAC
#define CONFIG_MII 1 /* MII PHY management */
#define CONFIG_PHY_ADDR 0 /* PHY address */
#define CONFIG_LXT971_NO_SLEEP 1 /* disable sleep mode in LXT971 */
diff --git a/include/configs/VOM405.h b/include/configs/VOM405.h
index b6e3588..90efc6d 100644
--- a/include/configs/VOM405.h
+++ b/include/configs/VOM405.h
@@ -53,6 +53,7 @@
#define CONFIG_NET_MULTI 1
#undef CONFIG_HAS_ETH1
+#define CONFIG_PPC4xx_EMAC
#define CONFIG_MII 1 /* MII PHY management */
#define CONFIG_PHY_ADDR 0 /* PHY address */
#define CONFIG_LXT971_NO_SLEEP 1 /* disable sleep mode in LXT971 */
diff --git a/include/configs/W7OLMC.h b/include/configs/W7OLMC.h
index e546369..51d0a0a 100644
--- a/include/configs/W7OLMC.h
+++ b/include/configs/W7OLMC.h
@@ -65,6 +65,7 @@
#define CONFIG_LOADS_ECHO 1 /* echo on for serial download */
#undef CONFIG_SYS_LOADS_BAUD_CHANGE /* disallow baudrate change */
+#define CONFIG_PPC4xx_EMAC
#define CONFIG_MII 1 /* MII PHY management */
#define CONFIG_PHY_ADDR 0 /* PHY address */
diff --git a/include/configs/W7OLMG.h b/include/configs/W7OLMG.h
index 2260338..ca1a9d4 100644
--- a/include/configs/W7OLMG.h
+++ b/include/configs/W7OLMG.h
@@ -65,6 +65,7 @@
#define CONFIG_LOADS_ECHO 1 /* echo on for serial download */
#undef CONFIG_SYS_LOADS_BAUD_CHANGE /* disallow baudrate change */
+#define CONFIG_PPC4xx_EMAC
#define CONFIG_MII 1 /* MII PHY management */
#define CONFIG_PHY_ADDR 0 /* PHY address */
diff --git a/include/configs/WUH405.h b/include/configs/WUH405.h
index ec81564..01cdf3a 100644
--- a/include/configs/WUH405.h
+++ b/include/configs/WUH405.h
@@ -54,6 +54,7 @@
#define CONFIG_LOADS_ECHO 1 /* echo on for serial download */
#define CONFIG_SYS_LOADS_BAUD_CHANGE 1 /* allow baudrate change */
+#define CONFIG_PPC4xx_EMAC
#define CONFIG_MII 1 /* MII PHY management */
#define CONFIG_PHY_ADDR 0 /* PHY address */
#define CONFIG_LXT971_NO_SLEEP 1 /* disable sleep mode in LXT971 */
diff --git a/include/configs/XPEDITE1K.h b/include/configs/XPEDITE1K.h
index 569bb90..8d44ec6 100644
--- a/include/configs/XPEDITE1K.h
+++ b/include/configs/XPEDITE1K.h
@@ -167,6 +167,7 @@ extern void out32(unsigned int, unsigned long);
#define CONFIG_LOADS_ECHO 1 /* echo on for serial download */
#define CONFIG_SYS_LOADS_BAUD_CHANGE 1 /* allow baudrate change */
+#define CONFIG_PPC4xx_EMAC
#define CONFIG_MII 1 /* MII PHY management */
#define CONFIG_PHY_ADDR 0 /* PHY address phy0 not populated */
#define CONFIG_PHY1_ADDR 1 /* PHY address phy1 not populated */
diff --git a/include/configs/alpr.h b/include/configs/alpr.h
index 3158412..6e9f5e5 100644
--- a/include/configs/alpr.h
+++ b/include/configs/alpr.h
@@ -198,6 +198,7 @@
#define CONFIG_LOADS_ECHO 1 /* echo on for serial download */
#define CONFIG_SYS_LOADS_BAUD_CHANGE 1 /* allow baudrate change */
+#define CONFIG_PPC4xx_EMAC
#define CONFIG_MII 1 /* MII PHY management */
#define CONFIG_NET_MULTI 1
#define CONFIG_PHY_ADDR 0x02 /* dummy setting, no EMAC0 used */
diff --git a/include/configs/amcc-common.h b/include/configs/amcc-common.h
index 8516571..d3dc3e5 100644
--- a/include/configs/amcc-common.h
+++ b/include/configs/amcc-common.h
@@ -45,6 +45,7 @@
/*
* Ethernet/EMAC/PHY
*/
+#define CONFIG_PPC4xx_EMAC
#define CONFIG_MII /* MII PHY management */
#define CONFIG_NET_MULTI
#define CONFIG_NETCONSOLE /* include NetConsole support */
diff --git a/include/configs/csb272.h b/include/configs/csb272.h
index 393e992..874f2c0 100644
--- a/include/configs/csb272.h
+++ b/include/configs/csb272.h
@@ -180,6 +180,7 @@
* MII PHY configuration
*
*/
+#define CONFIG_PPC4xx_EMAC
#define CONFIG_MII 1 /* MII PHY management */
#define CONFIG_PHY_ADDR 0 /* PHY address */
#define CONFIG_PHY_CMD_DELAY 40 /* PHY COMMAND delay */
diff --git a/include/configs/csb472.h b/include/configs/csb472.h
index af38252..2e30e69 100644
--- a/include/configs/csb472.h
+++ b/include/configs/csb472.h
@@ -179,6 +179,7 @@
* MII PHY configuration
*
*/
+#define CONFIG_PPC4xx_EMAC
#define CONFIG_MII 1 /* MII PHY management */
#define CONFIG_PHY_ADDR 0 /* PHY address */
#define CONFIG_PHY_CMD_DELAY 40 /* PHY COMMAND delay */
diff --git a/include/configs/korat.h b/include/configs/korat.h
index ca3e8a9..d56da14 100644
--- a/include/configs/korat.h
+++ b/include/configs/korat.h
@@ -215,6 +215,7 @@
#define CONFIG_LOADS_ECHO 1 /* echo on for serial download */
#define CONFIG_SYS_LOADS_BAUD_CHANGE 1 /* allow baudrate change */
+#define CONFIG_PPC4xx_EMAC
#define CONFIG_IBM_EMAC4_V4 1
#define CONFIG_MII 1 /* MII PHY management */
#define CONFIG_PHY_ADDR 2 /* PHY address, See schematics */
diff --git a/include/configs/lwmon5.h b/include/configs/lwmon5.h
index e0dbd61..05055c8 100644
--- a/include/configs/lwmon5.h
+++ b/include/configs/lwmon5.h
@@ -326,6 +326,7 @@
#define CONFIG_LOADS_ECHO 1 /* echo on for serial download */
#define CONFIG_SYS_LOADS_BAUD_CHANGE 1 /* allow baudrate change */
+#define CONFIG_PPC4xx_EMAC
#define CONFIG_IBM_EMAC4_V4 1
#define CONFIG_MII 1 /* MII PHY management */
#define CONFIG_PHY_ADDR 3 /* PHY address, See schematics */
diff --git a/include/configs/netstal-common.h b/include/configs/netstal-common.h
index 1fa4b00..0a75794 100644
--- a/include/configs/netstal-common.h
+++ b/include/configs/netstal-common.h
@@ -58,6 +58,7 @@
/*
* Ethernet/EMAC/PHY
*/
+#define CONFIG_PPC4xx_EMAC
#define CONFIG_MII /* MII PHY management */
#define CONFIG_PHY_ADDR 1 /* PHY address */
#if defined(CONFIG_440)
diff --git a/include/configs/p3p440.h b/include/configs/p3p440.h
index 1dc8656..729ca6a 100644
--- a/include/configs/p3p440.h
+++ b/include/configs/p3p440.h
@@ -158,6 +158,7 @@
#define CONFIG_LOADS_ECHO 1 /* echo on for serial download */
#define CONFIG_SYS_LOADS_BAUD_CHANGE 1 /* allow baudrate change */
+#define CONFIG_PPC4xx_EMAC
#define CONFIG_MII 1 /* MII PHY management */
#define CONFIG_PHY_ADDR 0x1c /* PHY address */
#define CONFIG_HAS_ETH1
diff --git a/include/configs/pcs440ep.h b/include/configs/pcs440ep.h
index 6e2d906..000ae5c 100644
--- a/include/configs/pcs440ep.h
+++ b/include/configs/pcs440ep.h
@@ -218,6 +218,7 @@
#define CONFIG_LOADS_ECHO 1 /* echo on for serial download */
#define CONFIG_SYS_LOADS_BAUD_CHANGE 1 /* allow baudrate change */
+#define CONFIG_PPC4xx_EMAC
#define CONFIG_MII 1 /* MII PHY management */
#define CONFIG_NET_MULTI 1 /* required for netconsole */
#define CONFIG_HAS_ETH1 1 /* add support for "eth1addr" */
diff --git a/include/configs/quad100hd.h b/include/configs/quad100hd.h
index 1a76301..0f7fca3 100644
--- a/include/configs/quad100hd.h
+++ b/include/configs/quad100hd.h
@@ -45,6 +45,7 @@
#define CONFIG_ENV_IS_IN_EEPROM
#undef CONFIG_ENV_IS_IN_FLASH
+#define CONFIG_PPC4xx_EMAC
#define CONFIG_NET_MULTI 1
#define CONFIG_HAS_ETH1 1
#define CONFIG_MII 1 /* MII PHY management */
diff --git a/include/configs/sbc405.h b/include/configs/sbc405.h
index d93ca2d..c156820 100644
--- a/include/configs/sbc405.h
+++ b/include/configs/sbc405.h
@@ -58,6 +58,7 @@
#define CONFIG_BOOTCOMMAND "version;echo;tftpboot ${loadaddr} ${loadfile};bootvx" /* autoboot command */
+#define CONFIG_PPC4xx_EMAC
#define CONFIG_MII 1 /* MII PHY management */
#define CONFIG_PHY_ADDR 0 /* PHY address */
#define CONFIG_PHY_RESET_DELAY 300 /* Intel LXT971A needs this */
diff --git a/include/configs/sc3.h b/include/configs/sc3.h
index 44135df..d152a96 100644
--- a/include/configs/sc3.h
+++ b/include/configs/sc3.h
@@ -166,6 +166,8 @@
/* #define CONFIG_EEPRO100_SROM_WRITE */
/* #define CONFIG_SHOW_MAC */
#define CONFIG_EEPRO100
+
+#define CONFIG_PPC4xx_EMAC
#define CONFIG_MII 1 /* add 405GP MII PHY management */
#define CONFIG_PHY_ADDR 1 /* the connected Phy defaults to address 1 */
diff --git a/include/configs/zeus.h b/include/configs/zeus.h
index b75e8a1..1a77c71 100644
--- a/include/configs/zeus.h
+++ b/include/configs/zeus.h
@@ -46,6 +46,7 @@
#define CONFIG_OVERWRITE_ETHADDR_ONCE 1
+#define CONFIG_PPC4xx_EMAC
#define CONFIG_MII 1 /* MII PHY management */
#define CONFIG_PHY_ADDR 0x01 /* PHY address */
#define CONFIG_HAS_ETH1 1
diff --git a/net/eth.c b/net/eth.c
index 0b954ed..eab5104 100644
--- a/net/eth.c
+++ b/net/eth.c
@@ -153,7 +153,7 @@ int eth_initialize(bd_t *bis)
#if defined(CONFIG_DB64460) || defined(CONFIG_P3Mx)
mv6446x_eth_initialize(bis);
#endif
-#if defined(CONFIG_4xx) && !defined(CONFIG_IOP480) && !defined(CONFIG_AP1000)
+#if defined(CONFIG_PPC4xx_EMAC)
ppc_4xx_eth_initialize(bis);
#endif
if (!eth_devices) {
@@ -485,8 +485,7 @@ int eth_initialize(bd_t *bis)
#if defined(CONFIG_AT91RM9200)
at91rm9200_miiphy_initialize(bis);
#endif
-#if defined(CONFIG_4xx) && !defined(CONFIG_IOP480) \
- && !defined(CONFIG_AP1000) && !defined(CONFIG_405)
+#if defined(CONFIG_PPC4xx_EMAC)
emac4xx_miiphy_initialize(bis);
#endif
#if defined(CONFIG_MCF52x2)
--
1.5.4.3
2
5

03 Nov '08
UBI (Latin: "where?") stands for "Unsorted Block Images". It is a volume management system for flash devices which manages multiple logical volumes on a single physical flash device and spreads the I/O load (i.e, wear-leveling) across the whole flash chip.
In a sense, UBI may be compared to the Logical Volume Manager (LVM). Whereas LVM maps logical sectors to physical sectors, UBI maps logical eraseblocks to physical eraseblocks. But besides the mapping, UBI implements global wear-leveling and I/O errors handling.
For more details, Please visit the following URL.
http://www.linux-mtd.infradead.org/doc/ubi.html
Signed-off-by: Kyungmin Park <kyungmin.park(a)samsung.com>
---
diff --git a/drivers/mtd/ubi/Makefile b/drivers/mtd/ubi/Makefile
new file mode 100644
index 0000000..30eed96
--- /dev/null
+++ b/drivers/mtd/ubi/Makefile
@@ -0,0 +1,48 @@
+#
+# (C) Copyright 2006
+# Wolfgang Denk, DENX Software Engineering, wd(a)denx.de.
+#
+# See file CREDITS for list of people who contributed to this
+# project.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2 of
+# the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+# MA 02111-1307 USA
+#
+
+include $(TOPDIR)/config.mk
+
+LIB := $(obj)libubi.a
+COBJS-y += build.o vtbl.o vmt.o upd.o kapi.o eba.o io.o wl.o scan.o crc32.o
+
+COBJS-y += misc.o
+COBJS-y += debug.o
+
+COBJS := $(COBJS-y)
+SRCS := $(COBJS:.o=.c)
+OBJS := $(addprefix $(obj),$(COBJS))
+
+all: $(LIB)
+
+$(LIB): $(obj).depend $(OBJS)
+ $(AR) $(ARFLAGS) $@ $(OBJS)
+
+#########################################################################
+
+# defines $(obj).depend target
+include $(SRCTREE)/rules.mk
+
+sinclude $(obj).depend
+
+#########################################################################
diff --git a/drivers/mtd/ubi/build.c b/drivers/mtd/ubi/build.c
new file mode 100644
index 0000000..b6f8699
--- /dev/null
+++ b/drivers/mtd/ubi/build.c
@@ -0,0 +1,1185 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ * Copyright (c) Nokia Corporation, 2007
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Author: Artem Bityutskiy (Битюцкий Артём),
+ * Frank Haverkamp
+ */
+
+/*
+ * This file includes UBI initialization and building of UBI devices.
+ *
+ * When UBI is initialized, it attaches all the MTD devices specified as the
+ * module load parameters or the kernel boot parameters. If MTD devices were
+ * specified, UBI does not attach any MTD device, but it is possible to do
+ * later using the "UBI control device".
+ *
+ * At the moment we only attach UBI devices by scanning, which will become a
+ * bottleneck when flashes reach certain large size. Then one may improve UBI
+ * and add other methods, although it does not seem to be easy to do.
+ */
+
+#ifdef UBI_LINUX
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/stringify.h>
+#include <linux/stat.h>
+#include <linux/miscdevice.h>
+#include <linux/log2.h>
+#include <linux/kthread.h>
+#endif
+#include <ubi_uboot.h>
+#include "ubi.h"
+
+/* Maximum length of the 'mtd=' parameter */
+#define MTD_PARAM_LEN_MAX 64
+
+/**
+ * struct mtd_dev_param - MTD device parameter description data structure.
+ * @name: MTD device name or number string
+ * @vid_hdr_offs: VID header offset
+ */
+struct mtd_dev_param {
+ char name[MTD_PARAM_LEN_MAX];
+ int vid_hdr_offs;
+};
+
+/* Numbers of elements set in the @mtd_dev_param array */
+static int mtd_devs;
+
+/* MTD devices specification parameters */
+static struct mtd_dev_param mtd_dev_param[UBI_MAX_DEVICES];
+
+/* Root UBI "class" object (corresponds to '/<sysfs>/class/ubi/') */
+struct class *ubi_class;
+
+#ifdef UBI_LINUX
+/* Slab cache for wear-leveling entries */
+struct kmem_cache *ubi_wl_entry_slab;
+
+/* UBI control character device */
+static struct miscdevice ubi_ctrl_cdev = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "ubi_ctrl",
+ .fops = &ubi_ctrl_cdev_operations,
+};
+#endif
+
+/* All UBI devices in system */
+struct ubi_device *ubi_devices[UBI_MAX_DEVICES];
+
+#ifdef UBI_LINUX
+/* Serializes UBI devices creations and removals */
+DEFINE_MUTEX(ubi_devices_mutex);
+
+/* Protects @ubi_devices and @ubi->ref_count */
+static DEFINE_SPINLOCK(ubi_devices_lock);
+
+/* "Show" method for files in '/<sysfs>/class/ubi/' */
+static ssize_t ubi_version_show(struct class *class, char *buf)
+{
+ return sprintf(buf, "%d\n", UBI_VERSION);
+}
+
+/* UBI version attribute ('/<sysfs>/class/ubi/version') */
+static struct class_attribute ubi_version =
+ __ATTR(version, S_IRUGO, ubi_version_show, NULL);
+
+static ssize_t dev_attribute_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+/* UBI device attributes (correspond to files in '/<sysfs>/class/ubi/ubiX') */
+static struct device_attribute dev_eraseblock_size =
+ __ATTR(eraseblock_size, S_IRUGO, dev_attribute_show, NULL);
+static struct device_attribute dev_avail_eraseblocks =
+ __ATTR(avail_eraseblocks, S_IRUGO, dev_attribute_show, NULL);
+static struct device_attribute dev_total_eraseblocks =
+ __ATTR(total_eraseblocks, S_IRUGO, dev_attribute_show, NULL);
+static struct device_attribute dev_volumes_count =
+ __ATTR(volumes_count, S_IRUGO, dev_attribute_show, NULL);
+static struct device_attribute dev_max_ec =
+ __ATTR(max_ec, S_IRUGO, dev_attribute_show, NULL);
+static struct device_attribute dev_reserved_for_bad =
+ __ATTR(reserved_for_bad, S_IRUGO, dev_attribute_show, NULL);
+static struct device_attribute dev_bad_peb_count =
+ __ATTR(bad_peb_count, S_IRUGO, dev_attribute_show, NULL);
+static struct device_attribute dev_max_vol_count =
+ __ATTR(max_vol_count, S_IRUGO, dev_attribute_show, NULL);
+static struct device_attribute dev_min_io_size =
+ __ATTR(min_io_size, S_IRUGO, dev_attribute_show, NULL);
+static struct device_attribute dev_bgt_enabled =
+ __ATTR(bgt_enabled, S_IRUGO, dev_attribute_show, NULL);
+static struct device_attribute dev_mtd_num =
+ __ATTR(mtd_num, S_IRUGO, dev_attribute_show, NULL);
+#endif
+
+/**
+ * ubi_get_device - get UBI device.
+ * @ubi_num: UBI device number
+ *
+ * This function returns UBI device description object for UBI device number
+ * @ubi_num, or %NULL if the device does not exist. This function increases the
+ * device reference count to prevent removal of the device. In other words, the
+ * device cannot be removed if its reference count is not zero.
+ */
+struct ubi_device *ubi_get_device(int ubi_num)
+{
+ struct ubi_device *ubi;
+
+ spin_lock(&ubi_devices_lock);
+ ubi = ubi_devices[ubi_num];
+ if (ubi) {
+ ubi_assert(ubi->ref_count >= 0);
+ ubi->ref_count += 1;
+ get_device(&ubi->dev);
+ }
+ spin_unlock(&ubi_devices_lock);
+
+ return ubi;
+}
+
+/**
+ * ubi_put_device - drop an UBI device reference.
+ * @ubi: UBI device description object
+ */
+void ubi_put_device(struct ubi_device *ubi)
+{
+ spin_lock(&ubi_devices_lock);
+ ubi->ref_count -= 1;
+ put_device(&ubi->dev);
+ spin_unlock(&ubi_devices_lock);
+}
+
+/**
+ * ubi_get_by_major - get UBI device description object by character device
+ * major number.
+ * @major: major number
+ *
+ * This function is similar to 'ubi_get_device()', but it searches the device
+ * by its major number.
+ */
+struct ubi_device *ubi_get_by_major(int major)
+{
+ int i;
+ struct ubi_device *ubi;
+
+ spin_lock(&ubi_devices_lock);
+ for (i = 0; i < UBI_MAX_DEVICES; i++) {
+ ubi = ubi_devices[i];
+ if (ubi && MAJOR(ubi->cdev.dev) == major) {
+ ubi_assert(ubi->ref_count >= 0);
+ ubi->ref_count += 1;
+ get_device(&ubi->dev);
+ spin_unlock(&ubi_devices_lock);
+ return ubi;
+ }
+ }
+ spin_unlock(&ubi_devices_lock);
+
+ return NULL;
+}
+
+/**
+ * ubi_major2num - get UBI device number by character device major number.
+ * @major: major number
+ *
+ * This function searches UBI device number object by its major number. If UBI
+ * device was not found, this function returns -ENODEV, otherwise the UBI device
+ * number is returned.
+ */
+int ubi_major2num(int major)
+{
+ int i, ubi_num = -ENODEV;
+
+ spin_lock(&ubi_devices_lock);
+ for (i = 0; i < UBI_MAX_DEVICES; i++) {
+ struct ubi_device *ubi = ubi_devices[i];
+
+ if (ubi && MAJOR(ubi->cdev.dev) == major) {
+ ubi_num = ubi->ubi_num;
+ break;
+ }
+ }
+ spin_unlock(&ubi_devices_lock);
+
+ return ubi_num;
+}
+
+#ifdef UBI_LINUX
+/* "Show" method for files in '/<sysfs>/class/ubi/ubiX/' */
+static ssize_t dev_attribute_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ ssize_t ret;
+ struct ubi_device *ubi;
+
+ /*
+ * The below code looks weird, but it actually makes sense. We get the
+ * UBI device reference from the contained 'struct ubi_device'. But it
+ * is unclear if the device was removed or not yet. Indeed, if the
+ * device was removed before we increased its reference count,
+ * 'ubi_get_device()' will return -ENODEV and we fail.
+ *
+ * Remember, 'struct ubi_device' is freed in the release function, so
+ * we still can use 'ubi->ubi_num'.
+ */
+ ubi = container_of(dev, struct ubi_device, dev);
+ ubi = ubi_get_device(ubi->ubi_num);
+ if (!ubi)
+ return -ENODEV;
+
+ if (attr == &dev_eraseblock_size)
+ ret = sprintf(buf, "%d\n", ubi->leb_size);
+ else if (attr == &dev_avail_eraseblocks)
+ ret = sprintf(buf, "%d\n", ubi->avail_pebs);
+ else if (attr == &dev_total_eraseblocks)
+ ret = sprintf(buf, "%d\n", ubi->good_peb_count);
+ else if (attr == &dev_volumes_count)
+ ret = sprintf(buf, "%d\n", ubi->vol_count - UBI_INT_VOL_COUNT);
+ else if (attr == &dev_max_ec)
+ ret = sprintf(buf, "%d\n", ubi->max_ec);
+ else if (attr == &dev_reserved_for_bad)
+ ret = sprintf(buf, "%d\n", ubi->beb_rsvd_pebs);
+ else if (attr == &dev_bad_peb_count)
+ ret = sprintf(buf, "%d\n", ubi->bad_peb_count);
+ else if (attr == &dev_max_vol_count)
+ ret = sprintf(buf, "%d\n", ubi->vtbl_slots);
+ else if (attr == &dev_min_io_size)
+ ret = sprintf(buf, "%d\n", ubi->min_io_size);
+ else if (attr == &dev_bgt_enabled)
+ ret = sprintf(buf, "%d\n", ubi->thread_enabled);
+ else if (attr == &dev_mtd_num)
+ ret = sprintf(buf, "%d\n", ubi->mtd->index);
+ else
+ ret = -EINVAL;
+
+ ubi_put_device(ubi);
+ return ret;
+}
+
+/* Fake "release" method for UBI devices */
+static void dev_release(struct device *dev) { }
+
+/**
+ * ubi_sysfs_init - initialize sysfs for an UBI device.
+ * @ubi: UBI device description object
+ *
+ * This function returns zero in case of success and a negative error code in
+ * case of failure.
+ */
+static int ubi_sysfs_init(struct ubi_device *ubi)
+{
+ int err;
+
+ ubi->dev.release = dev_release;
+ ubi->dev.devt = ubi->cdev.dev;
+ ubi->dev.class = ubi_class;
+ sprintf(&ubi->dev.bus_id[0], UBI_NAME_STR"%d", ubi->ubi_num);
+ err = device_register(&ubi->dev);
+ if (err)
+ return err;
+
+ err = device_create_file(&ubi->dev, &dev_eraseblock_size);
+ if (err)
+ return err;
+ err = device_create_file(&ubi->dev, &dev_avail_eraseblocks);
+ if (err)
+ return err;
+ err = device_create_file(&ubi->dev, &dev_total_eraseblocks);
+ if (err)
+ return err;
+ err = device_create_file(&ubi->dev, &dev_volumes_count);
+ if (err)
+ return err;
+ err = device_create_file(&ubi->dev, &dev_max_ec);
+ if (err)
+ return err;
+ err = device_create_file(&ubi->dev, &dev_reserved_for_bad);
+ if (err)
+ return err;
+ err = device_create_file(&ubi->dev, &dev_bad_peb_count);
+ if (err)
+ return err;
+ err = device_create_file(&ubi->dev, &dev_max_vol_count);
+ if (err)
+ return err;
+ err = device_create_file(&ubi->dev, &dev_min_io_size);
+ if (err)
+ return err;
+ err = device_create_file(&ubi->dev, &dev_bgt_enabled);
+ if (err)
+ return err;
+ err = device_create_file(&ubi->dev, &dev_mtd_num);
+ return err;
+}
+
+/**
+ * ubi_sysfs_close - close sysfs for an UBI device.
+ * @ubi: UBI device description object
+ */
+static void ubi_sysfs_close(struct ubi_device *ubi)
+{
+ device_remove_file(&ubi->dev, &dev_mtd_num);
+ device_remove_file(&ubi->dev, &dev_bgt_enabled);
+ device_remove_file(&ubi->dev, &dev_min_io_size);
+ device_remove_file(&ubi->dev, &dev_max_vol_count);
+ device_remove_file(&ubi->dev, &dev_bad_peb_count);
+ device_remove_file(&ubi->dev, &dev_reserved_for_bad);
+ device_remove_file(&ubi->dev, &dev_max_ec);
+ device_remove_file(&ubi->dev, &dev_volumes_count);
+ device_remove_file(&ubi->dev, &dev_total_eraseblocks);
+ device_remove_file(&ubi->dev, &dev_avail_eraseblocks);
+ device_remove_file(&ubi->dev, &dev_eraseblock_size);
+ device_unregister(&ubi->dev);
+}
+#endif
+
+/**
+ * kill_volumes - destroy all volumes.
+ * @ubi: UBI device description object
+ */
+static void kill_volumes(struct ubi_device *ubi)
+{
+ int i;
+
+ for (i = 0; i < ubi->vtbl_slots; i++)
+ if (ubi->volumes[i])
+ ubi_free_volume(ubi, ubi->volumes[i]);
+}
+
+/**
+ * uif_init - initialize user interfaces for an UBI device.
+ * @ubi: UBI device description object
+ *
+ * This function returns zero in case of success and a negative error code in
+ * case of failure.
+ */
+static int uif_init(struct ubi_device *ubi)
+{
+ int i, err;
+#ifdef UBI_LINUX
+ dev_t dev;
+#endif
+
+ sprintf(ubi->ubi_name, UBI_NAME_STR "%d", ubi->ubi_num);
+
+ /*
+ * Major numbers for the UBI character devices are allocated
+ * dynamically. Major numbers of volume character devices are
+ * equivalent to ones of the corresponding UBI character device. Minor
+ * numbers of UBI character devices are 0, while minor numbers of
+ * volume character devices start from 1. Thus, we allocate one major
+ * number and ubi->vtbl_slots + 1 minor numbers.
+ */
+ err = alloc_chrdev_region(&dev, 0, ubi->vtbl_slots + 1, ubi->ubi_name);
+ if (err) {
+ ubi_err("cannot register UBI character devices");
+ return err;
+ }
+
+ ubi_assert(MINOR(dev) == 0);
+ cdev_init(&ubi->cdev, &ubi_cdev_operations);
+ dbg_msg("%s major is %u", ubi->ubi_name, MAJOR(dev));
+ ubi->cdev.owner = THIS_MODULE;
+
+ err = cdev_add(&ubi->cdev, dev, 1);
+ if (err) {
+ ubi_err("cannot add character device");
+ goto out_unreg;
+ }
+
+ err = ubi_sysfs_init(ubi);
+ if (err)
+ goto out_sysfs;
+
+ for (i = 0; i < ubi->vtbl_slots; i++)
+ if (ubi->volumes[i]) {
+ err = ubi_add_volume(ubi, ubi->volumes[i]);
+ if (err) {
+ ubi_err("cannot add volume %d", i);
+ goto out_volumes;
+ }
+ }
+
+ return 0;
+
+out_volumes:
+ kill_volumes(ubi);
+out_sysfs:
+ ubi_sysfs_close(ubi);
+ cdev_del(&ubi->cdev);
+out_unreg:
+ unregister_chrdev_region(ubi->cdev.dev, ubi->vtbl_slots + 1);
+ ubi_err("cannot initialize UBI %s, error %d", ubi->ubi_name, err);
+ return err;
+}
+
+/**
+ * uif_close - close user interfaces for an UBI device.
+ * @ubi: UBI device description object
+ */
+static void uif_close(struct ubi_device *ubi)
+{
+ kill_volumes(ubi);
+ ubi_sysfs_close(ubi);
+ cdev_del(&ubi->cdev);
+ unregister_chrdev_region(ubi->cdev.dev, ubi->vtbl_slots + 1);
+}
+
+/**
+ * attach_by_scanning - attach an MTD device using scanning method.
+ * @ubi: UBI device descriptor
+ *
+ * This function returns zero in case of success and a negative error code in
+ * case of failure.
+ *
+ * Note, currently this is the only method to attach UBI devices. Hopefully in
+ * the future we'll have more scalable attaching methods and avoid full media
+ * scanning. But even in this case scanning will be needed as a fall-back
+ * attaching method if there are some on-flash table corruptions.
+ */
+static int attach_by_scanning(struct ubi_device *ubi)
+{
+ int err;
+ struct ubi_scan_info *si;
+
+ si = ubi_scan(ubi);
+ if (IS_ERR(si))
+ return PTR_ERR(si);
+
+ ubi->bad_peb_count = si->bad_peb_count;
+ ubi->good_peb_count = ubi->peb_count - ubi->bad_peb_count;
+ ubi->max_ec = si->max_ec;
+ ubi->mean_ec = si->mean_ec;
+
+ err = ubi_read_volume_table(ubi, si);
+ if (err)
+ goto out_si;
+
+ err = ubi_wl_init_scan(ubi, si);
+ if (err)
+ goto out_vtbl;
+
+ err = ubi_eba_init_scan(ubi, si);
+ if (err)
+ goto out_wl;
+
+ ubi_scan_destroy_si(si);
+ return 0;
+
+out_wl:
+ ubi_wl_close(ubi);
+out_vtbl:
+ vfree(ubi->vtbl);
+out_si:
+ ubi_scan_destroy_si(si);
+ return err;
+}
+
+/**
+ * io_init - initialize I/O unit for a given UBI device.
+ * @ubi: UBI device description object
+ *
+ * If @ubi->vid_hdr_offset or @ubi->leb_start is zero, default offsets are
+ * assumed:
+ * o EC header is always at offset zero - this cannot be changed;
+ * o VID header starts just after the EC header at the closest address
+ * aligned to @io->hdrs_min_io_size;
+ * o data starts just after the VID header at the closest address aligned to
+ * @io->min_io_size
+ *
+ * This function returns zero in case of success and a negative error code in
+ * case of failure.
+ */
+static int io_init(struct ubi_device *ubi)
+{
+ if (ubi->mtd->numeraseregions != 0) {
+ /*
+ * Some flashes have several erase regions. Different regions
+ * may have different eraseblock size and other
+ * characteristics. It looks like mostly multi-region flashes
+ * have one "main" region and one or more small regions to
+ * store boot loader code or boot parameters or whatever. I
+ * guess we should just pick the largest region. But this is
+ * not implemented.
+ */
+ ubi_err("multiple regions, not implemented");
+ return -EINVAL;
+ }
+
+ if (ubi->vid_hdr_offset < 0)
+ return -EINVAL;
+
+ /*
+ * Note, in this implementation we support MTD devices with 0x7FFFFFFF
+ * physical eraseblocks maximum.
+ */
+
+ ubi->peb_size = ubi->mtd->erasesize;
+ ubi->peb_count = ubi->mtd->size / ubi->mtd->erasesize;
+ ubi->flash_size = ubi->mtd->size;
+
+ if (ubi->mtd->block_isbad && ubi->mtd->block_markbad)
+ ubi->bad_allowed = 1;
+
+ ubi->min_io_size = ubi->mtd->writesize;
+ ubi->hdrs_min_io_size = ubi->mtd->writesize >> ubi->mtd->subpage_sft;
+
+ /*
+ * Make sure minimal I/O unit is power of 2. Note, there is no
+ * fundamental reason for this assumption. It is just an optimization
+ * which allows us to avoid costly division operations.
+ */
+ if (!is_power_of_2(ubi->min_io_size)) {
+ ubi_err("min. I/O unit (%d) is not power of 2",
+ ubi->min_io_size);
+ return -EINVAL;
+ }
+
+ ubi_assert(ubi->hdrs_min_io_size > 0);
+ ubi_assert(ubi->hdrs_min_io_size <= ubi->min_io_size);
+ ubi_assert(ubi->min_io_size % ubi->hdrs_min_io_size == 0);
+
+ /* Calculate default aligned sizes of EC and VID headers */
+ ubi->ec_hdr_alsize = ALIGN(UBI_EC_HDR_SIZE, ubi->hdrs_min_io_size);
+ ubi->vid_hdr_alsize = ALIGN(UBI_VID_HDR_SIZE, ubi->hdrs_min_io_size);
+
+ dbg_msg("min_io_size %d", ubi->min_io_size);
+ dbg_msg("hdrs_min_io_size %d", ubi->hdrs_min_io_size);
+ dbg_msg("ec_hdr_alsize %d", ubi->ec_hdr_alsize);
+ dbg_msg("vid_hdr_alsize %d", ubi->vid_hdr_alsize);
+
+ if (ubi->vid_hdr_offset == 0)
+ /* Default offset */
+ ubi->vid_hdr_offset = ubi->vid_hdr_aloffset =
+ ubi->ec_hdr_alsize;
+ else {
+ ubi->vid_hdr_aloffset = ubi->vid_hdr_offset &
+ ~(ubi->hdrs_min_io_size - 1);
+ ubi->vid_hdr_shift = ubi->vid_hdr_offset -
+ ubi->vid_hdr_aloffset;
+ }
+
+ /* Similar for the data offset */
+ ubi->leb_start = ubi->vid_hdr_offset + UBI_EC_HDR_SIZE;
+ ubi->leb_start = ALIGN(ubi->leb_start, ubi->min_io_size);
+
+ dbg_msg("vid_hdr_offset %d", ubi->vid_hdr_offset);
+ dbg_msg("vid_hdr_aloffset %d", ubi->vid_hdr_aloffset);
+ dbg_msg("vid_hdr_shift %d", ubi->vid_hdr_shift);
+ dbg_msg("leb_start %d", ubi->leb_start);
+
+ /* The shift must be aligned to 32-bit boundary */
+ if (ubi->vid_hdr_shift % 4) {
+ ubi_err("unaligned VID header shift %d",
+ ubi->vid_hdr_shift);
+ return -EINVAL;
+ }
+
+ /* Check sanity */
+ if (ubi->vid_hdr_offset < UBI_EC_HDR_SIZE ||
+ ubi->leb_start < ubi->vid_hdr_offset + UBI_VID_HDR_SIZE ||
+ ubi->leb_start > ubi->peb_size - UBI_VID_HDR_SIZE ||
+ ubi->leb_start & (ubi->min_io_size - 1)) {
+ ubi_err("bad VID header (%d) or data offsets (%d)",
+ ubi->vid_hdr_offset, ubi->leb_start);
+ return -EINVAL;
+ }
+
+ /*
+ * It may happen that EC and VID headers are situated in one minimal
+ * I/O unit. In this case we can only accept this UBI image in
+ * read-only mode.
+ */
+ if (ubi->vid_hdr_offset + UBI_VID_HDR_SIZE <= ubi->hdrs_min_io_size) {
+ ubi_warn("EC and VID headers are in the same minimal I/O unit, "
+ "switch to read-only mode");
+ ubi->ro_mode = 1;
+ }
+
+ ubi->leb_size = ubi->peb_size - ubi->leb_start;
+
+ if (!(ubi->mtd->flags & MTD_WRITEABLE)) {
+ ubi_msg("MTD device %d is write-protected, attach in "
+ "read-only mode", ubi->mtd->index);
+ ubi->ro_mode = 1;
+ }
+
+ ubi_msg("physical eraseblock size: %d bytes (%d KiB)",
+ ubi->peb_size, ubi->peb_size >> 10);
+ ubi_msg("logical eraseblock size: %d bytes", ubi->leb_size);
+ ubi_msg("smallest flash I/O unit: %d", ubi->min_io_size);
+ if (ubi->hdrs_min_io_size != ubi->min_io_size)
+ ubi_msg("sub-page size: %d",
+ ubi->hdrs_min_io_size);
+ ubi_msg("VID header offset: %d (aligned %d)",
+ ubi->vid_hdr_offset, ubi->vid_hdr_aloffset);
+ ubi_msg("data offset: %d", ubi->leb_start);
+
+ /*
+ * Note, ideally, we have to initialize ubi->bad_peb_count here. But
+ * unfortunately, MTD does not provide this information. We should loop
+ * over all physical eraseblocks and invoke mtd->block_is_bad() for
+ * each physical eraseblock. So, we skip ubi->bad_peb_count
+ * uninitialized and initialize it after scanning.
+ */
+
+ return 0;
+}
+
+/**
+ * autoresize - re-size the volume which has the "auto-resize" flag set.
+ * @ubi: UBI device description object
+ * @vol_id: ID of the volume to re-size
+ *
+ * This function re-sizes the volume marked by the @UBI_VTBL_AUTORESIZE_FLG in
+ * the volume table to the largest possible size. See comments in ubi-header.h
+ * for more description of the flag. Returns zero in case of success and a
+ * negative error code in case of failure.
+ */
+static int autoresize(struct ubi_device *ubi, int vol_id)
+{
+ struct ubi_volume_desc desc;
+ struct ubi_volume *vol = ubi->volumes[vol_id];
+ int err, old_reserved_pebs = vol->reserved_pebs;
+
+ /*
+ * Clear the auto-resize flag in the volume in-memory copy of the
+ * volume table, and 'ubi_resize_volume()' will propogate this change
+ * to the flash.
+ */
+ ubi->vtbl[vol_id].flags &= ~UBI_VTBL_AUTORESIZE_FLG;
+
+ if (ubi->avail_pebs == 0) {
+ struct ubi_vtbl_record vtbl_rec;
+
+ /*
+ * No avalilable PEBs to re-size the volume, clear the flag on
+ * flash and exit.
+ */
+ memcpy(&vtbl_rec, &ubi->vtbl[vol_id],
+ sizeof(struct ubi_vtbl_record));
+ err = ubi_change_vtbl_record(ubi, vol_id, &vtbl_rec);
+ if (err)
+ ubi_err("cannot clean auto-resize flag for volume %d",
+ vol_id);
+ } else {
+ desc.vol = vol;
+ err = ubi_resize_volume(&desc,
+ old_reserved_pebs + ubi->avail_pebs);
+ if (err)
+ ubi_err("cannot auto-resize volume %d", vol_id);
+ }
+
+ if (err)
+ return err;
+
+ ubi_msg("volume %d (\"%s\") re-sized from %d to %d LEBs", vol_id,
+ vol->name, old_reserved_pebs, vol->reserved_pebs);
+ return 0;
+}
+
+/**
+ * ubi_attach_mtd_dev - attach an MTD device.
+ * @mtd_dev: MTD device description object
+ * @ubi_num: number to assign to the new UBI device
+ * @vid_hdr_offset: VID header offset
+ *
+ * This function attaches MTD device @mtd_dev to UBI and assign @ubi_num number
+ * to the newly created UBI device, unless @ubi_num is %UBI_DEV_NUM_AUTO, in
+ * which case this function finds a vacant device nubert and assings it
+ * automatically. Returns the new UBI device number in case of success and a
+ * negative error code in case of failure.
+ *
+ * Note, the invocations of this function has to be serialized by the
+ * @ubi_devices_mutex.
+ */
+int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num, int vid_hdr_offset)
+{
+ struct ubi_device *ubi;
+ int i, err;
+
+ /*
+ * Check if we already have the same MTD device attached.
+ *
+ * Note, this function assumes that UBI devices creations and deletions
+ * are serialized, so it does not take the &ubi_devices_lock.
+ */
+ for (i = 0; i < UBI_MAX_DEVICES; i++) {
+ ubi = ubi_devices[i];
+ if (ubi && mtd->index == ubi->mtd->index) {
+ dbg_err("mtd%d is already attached to ubi%d",
+ mtd->index, i);
+ return -EEXIST;
+ }
+ }
+
+ /*
+ * Make sure this MTD device is not emulated on top of an UBI volume
+ * already. Well, generally this recursion works fine, but there are
+ * different problems like the UBI module takes a reference to itself
+ * by attaching (and thus, opening) the emulated MTD device. This
+ * results in inability to unload the module. And in general it makes
+ * no sense to attach emulated MTD devices, so we prohibit this.
+ */
+ if (mtd->type == MTD_UBIVOLUME) {
+ ubi_err("refuse attaching mtd%d - it is already emulated on "
+ "top of UBI", mtd->index);
+ return -EINVAL;
+ }
+
+ if (ubi_num == UBI_DEV_NUM_AUTO) {
+ /* Search for an empty slot in the @ubi_devices array */
+ for (ubi_num = 0; ubi_num < UBI_MAX_DEVICES; ubi_num++)
+ if (!ubi_devices[ubi_num])
+ break;
+ if (ubi_num == UBI_MAX_DEVICES) {
+ dbg_err("only %d UBI devices may be created", UBI_MAX_DEVICES);
+ return -ENFILE;
+ }
+ } else {
+ if (ubi_num >= UBI_MAX_DEVICES)
+ return -EINVAL;
+
+ /* Make sure ubi_num is not busy */
+ if (ubi_devices[ubi_num]) {
+ dbg_err("ubi%d already exists", ubi_num);
+ return -EEXIST;
+ }
+ }
+
+ ubi = kzalloc(sizeof(struct ubi_device), GFP_KERNEL);
+ if (!ubi)
+ return -ENOMEM;
+
+ ubi->mtd = mtd;
+ ubi->ubi_num = ubi_num;
+ ubi->vid_hdr_offset = vid_hdr_offset;
+ ubi->autoresize_vol_id = -1;
+
+ mutex_init(&ubi->buf_mutex);
+ mutex_init(&ubi->ckvol_mutex);
+ mutex_init(&ubi->volumes_mutex);
+ spin_lock_init(&ubi->volumes_lock);
+
+ ubi_msg("attaching mtd%d to ubi%d", mtd->index, ubi_num);
+
+ err = io_init(ubi);
+ if (err)
+ goto out_free;
+
+ ubi->peb_buf1 = vmalloc(ubi->peb_size);
+ if (!ubi->peb_buf1)
+ goto out_free;
+
+ ubi->peb_buf2 = vmalloc(ubi->peb_size);
+ if (!ubi->peb_buf2)
+ goto out_free;
+
+#ifdef CONFIG_MTD_UBI_DEBUG
+ mutex_init(&ubi->dbg_buf_mutex);
+ ubi->dbg_peb_buf = vmalloc(ubi->peb_size);
+ if (!ubi->dbg_peb_buf)
+ goto out_free;
+#endif
+
+ err = attach_by_scanning(ubi);
+ if (err) {
+ dbg_err("failed to attach by scanning, error %d", err);
+ goto out_free;
+ }
+
+ if (ubi->autoresize_vol_id != -1) {
+ err = autoresize(ubi, ubi->autoresize_vol_id);
+ if (err)
+ goto out_detach;
+ }
+
+ err = uif_init(ubi);
+ if (err)
+ goto out_detach;
+
+ ubi->bgt_thread = kthread_create(ubi_thread, ubi, ubi->bgt_name);
+ if (IS_ERR(ubi->bgt_thread)) {
+ err = PTR_ERR(ubi->bgt_thread);
+ ubi_err("cannot spawn \"%s\", error %d", ubi->bgt_name,
+ err);
+ goto out_uif;
+ }
+
+ ubi_msg("attached mtd%d to ubi%d", mtd->index, ubi_num);
+ ubi_msg("MTD device name: \"%s\"", mtd->name);
+ ubi_msg("MTD device size: %llu MiB", ubi->flash_size >> 20);
+ ubi_msg("number of good PEBs: %d", ubi->good_peb_count);
+ ubi_msg("number of bad PEBs: %d", ubi->bad_peb_count);
+ ubi_msg("max. allowed volumes: %d", ubi->vtbl_slots);
+ ubi_msg("wear-leveling threshold: %d", CONFIG_MTD_UBI_WL_THRESHOLD);
+ ubi_msg("number of internal volumes: %d", UBI_INT_VOL_COUNT);
+ ubi_msg("number of user volumes: %d",
+ ubi->vol_count - UBI_INT_VOL_COUNT);
+ ubi_msg("available PEBs: %d", ubi->avail_pebs);
+ ubi_msg("total number of reserved PEBs: %d", ubi->rsvd_pebs);
+ ubi_msg("number of PEBs reserved for bad PEB handling: %d",
+ ubi->beb_rsvd_pebs);
+ ubi_msg("max/mean erase counter: %d/%d", ubi->max_ec, ubi->mean_ec);
+
+ /* Enable the background thread */
+ if (!DBG_DISABLE_BGT) {
+ ubi->thread_enabled = 1;
+ wake_up_process(ubi->bgt_thread);
+ }
+
+ ubi_devices[ubi_num] = ubi;
+ return ubi_num;
+
+out_uif:
+ uif_close(ubi);
+out_detach:
+ ubi_eba_close(ubi);
+ ubi_wl_close(ubi);
+ vfree(ubi->vtbl);
+out_free:
+ vfree(ubi->peb_buf1);
+ vfree(ubi->peb_buf2);
+#ifdef CONFIG_MTD_UBI_DEBUG
+ vfree(ubi->dbg_peb_buf);
+#endif
+ kfree(ubi);
+ return err;
+}
+
+/**
+ * ubi_detach_mtd_dev - detach an MTD device.
+ * @ubi_num: UBI device number to detach from
+ * @anyway: detach MTD even if device reference count is not zero
+ *
+ * This function destroys an UBI device number @ubi_num and detaches the
+ * underlying MTD device. Returns zero in case of success and %-EBUSY if the
+ * UBI device is busy and cannot be destroyed, and %-EINVAL if it does not
+ * exist.
+ *
+ * Note, the invocations of this function has to be serialized by the
+ * @ubi_devices_mutex.
+ */
+int ubi_detach_mtd_dev(int ubi_num, int anyway)
+{
+ struct ubi_device *ubi;
+
+ if (ubi_num < 0 || ubi_num >= UBI_MAX_DEVICES)
+ return -EINVAL;
+
+ spin_lock(&ubi_devices_lock);
+ ubi = ubi_devices[ubi_num];
+ if (!ubi) {
+ spin_unlock(&ubi_devices_lock);
+ return -EINVAL;
+ }
+
+ if (ubi->ref_count) {
+ if (!anyway) {
+ spin_unlock(&ubi_devices_lock);
+ return -EBUSY;
+ }
+ /* This may only happen if there is a bug */
+ ubi_err("%s reference count %d, destroy anyway",
+ ubi->ubi_name, ubi->ref_count);
+ }
+ ubi_devices[ubi_num] = NULL;
+ spin_unlock(&ubi_devices_lock);
+
+ ubi_assert(ubi_num == ubi->ubi_num);
+ dbg_msg("detaching mtd%d from ubi%d", ubi->mtd->index, ubi_num);
+
+ /*
+ * Before freeing anything, we have to stop the background thread to
+ * prevent it from doing anything on this device while we are freeing.
+ */
+ if (ubi->bgt_thread)
+ kthread_stop(ubi->bgt_thread);
+
+ uif_close(ubi);
+ ubi_eba_close(ubi);
+ ubi_wl_close(ubi);
+ vfree(ubi->vtbl);
+ put_mtd_device(ubi->mtd);
+ vfree(ubi->peb_buf1);
+ vfree(ubi->peb_buf2);
+#ifdef CONFIG_MTD_UBI_DEBUG
+ vfree(ubi->dbg_peb_buf);
+#endif
+ ubi_msg("mtd%d is detached from ubi%d", ubi->mtd->index, ubi->ubi_num);
+ kfree(ubi);
+ return 0;
+}
+
+/**
+ * find_mtd_device - open an MTD device by its name or number.
+ * @mtd_dev: name or number of the device
+ *
+ * This function tries to open and MTD device described by @mtd_dev string,
+ * which is first treated as an ASCII number, and if it is not true, it is
+ * treated as MTD device name. Returns MTD device description object in case of
+ * success and a negative error code in case of failure.
+ */
+static struct mtd_info * __init open_mtd_device(const char *mtd_dev)
+{
+ struct mtd_info *mtd;
+ int mtd_num;
+ char *endp;
+
+ mtd_num = simple_strtoul(mtd_dev, &endp, 0);
+ if (*endp != '\0' || mtd_dev == endp) {
+ /*
+ * This does not look like an ASCII integer, probably this is
+ * MTD device name.
+ */
+ mtd = get_mtd_device_nm(mtd_dev);
+ } else
+ mtd = get_mtd_device(NULL, mtd_num);
+
+ return mtd;
+}
+
+int __init ubi_init(void)
+{
+ int err, i, k;
+
+ /* Ensure that EC and VID headers have correct size */
+ BUILD_BUG_ON(sizeof(struct ubi_ec_hdr) != 64);
+ BUILD_BUG_ON(sizeof(struct ubi_vid_hdr) != 64);
+
+ if (mtd_devs > UBI_MAX_DEVICES) {
+ ubi_err("too many MTD devices, maximum is %d", UBI_MAX_DEVICES);
+ return -EINVAL;
+ }
+
+ /* Create base sysfs directory and sysfs files */
+ ubi_class = class_create(THIS_MODULE, UBI_NAME_STR);
+ if (IS_ERR(ubi_class)) {
+ err = PTR_ERR(ubi_class);
+ ubi_err("cannot create UBI class");
+ goto out;
+ }
+
+ err = class_create_file(ubi_class, &ubi_version);
+ if (err) {
+ ubi_err("cannot create sysfs file");
+ goto out_class;
+ }
+
+ err = misc_register(&ubi_ctrl_cdev);
+ if (err) {
+ ubi_err("cannot register device");
+ goto out_version;
+ }
+
+#ifdef UBI_LINUX
+ ubi_wl_entry_slab = kmem_cache_create("ubi_wl_entry_slab",
+ sizeof(struct ubi_wl_entry),
+ 0, 0, NULL);
+ if (!ubi_wl_entry_slab)
+ goto out_dev_unreg;
+#endif
+
+ /* Attach MTD devices */
+ for (i = 0; i < mtd_devs; i++) {
+ struct mtd_dev_param *p = &mtd_dev_param[i];
+ struct mtd_info *mtd;
+
+ cond_resched();
+
+ mtd = open_mtd_device(p->name);
+ if (IS_ERR(mtd)) {
+ err = PTR_ERR(mtd);
+ goto out_detach;
+ }
+
+ mutex_lock(&ubi_devices_mutex);
+ err = ubi_attach_mtd_dev(mtd, UBI_DEV_NUM_AUTO,
+ p->vid_hdr_offs);
+ mutex_unlock(&ubi_devices_mutex);
+ if (err < 0) {
+ put_mtd_device(mtd);
+ ubi_err("cannot attach mtd%d", mtd->index);
+ goto out_detach;
+ }
+ }
+
+ return 0;
+
+out_detach:
+ for (k = 0; k < i; k++)
+ if (ubi_devices[k]) {
+ mutex_lock(&ubi_devices_mutex);
+ ubi_detach_mtd_dev(ubi_devices[k]->ubi_num, 1);
+ mutex_unlock(&ubi_devices_mutex);
+ }
+#ifdef UBI_LINUX
+ kmem_cache_destroy(ubi_wl_entry_slab);
+out_dev_unreg:
+#endif
+ misc_deregister(&ubi_ctrl_cdev);
+out_version:
+ class_remove_file(ubi_class, &ubi_version);
+out_class:
+ class_destroy(ubi_class);
+out:
+ ubi_err("UBI error: cannot initialize UBI, error %d", err);
+ return err;
+}
+module_init(ubi_init);
+
+void __exit ubi_exit(void)
+{
+ int i;
+
+ for (i = 0; i < UBI_MAX_DEVICES; i++)
+ if (ubi_devices[i]) {
+ mutex_lock(&ubi_devices_mutex);
+ ubi_detach_mtd_dev(ubi_devices[i]->ubi_num, 1);
+ mutex_unlock(&ubi_devices_mutex);
+ }
+ kmem_cache_destroy(ubi_wl_entry_slab);
+ misc_deregister(&ubi_ctrl_cdev);
+ class_remove_file(ubi_class, &ubi_version);
+ class_destroy(ubi_class);
+}
+module_exit(ubi_exit);
+
+/**
+ * bytes_str_to_int - convert a string representing number of bytes to an
+ * integer.
+ * @str: the string to convert
+ *
+ * This function returns positive resulting integer in case of success and a
+ * negative error code in case of failure.
+ */
+static int __init bytes_str_to_int(const char *str)
+{
+ char *endp;
+ unsigned long result;
+
+ result = simple_strtoul(str, &endp, 0);
+ if (str == endp || result < 0) {
+ printk(KERN_ERR "UBI error: incorrect bytes count: \"%s\"\n",
+ str);
+ return -EINVAL;
+ }
+
+ switch (*endp) {
+ case 'G':
+ result *= 1024;
+ case 'M':
+ result *= 1024;
+ case 'K':
+ result *= 1024;
+ if (endp[1] == 'i' && endp[2] == 'B')
+ endp += 2;
+ case '\0':
+ break;
+ default:
+ printk(KERN_ERR "UBI error: incorrect bytes count: \"%s\"\n",
+ str);
+ return -EINVAL;
+ }
+
+ return result;
+}
+
+/**
+ * ubi_mtd_param_parse - parse the 'mtd=' UBI parameter.
+ * @val: the parameter value to parse
+ * @kp: not used
+ *
+ * This function returns zero in case of success and a negative error code in
+ * case of error.
+ */
+int __init ubi_mtd_param_parse(const char *val, struct kernel_param *kp)
+{
+ int i, len;
+ struct mtd_dev_param *p;
+ char buf[MTD_PARAM_LEN_MAX];
+ char *pbuf = &buf[0];
+ char *tokens[2] = {NULL, NULL};
+
+ if (!val)
+ return -EINVAL;
+
+ if (mtd_devs == UBI_MAX_DEVICES) {
+ printk(KERN_ERR "UBI error: too many parameters, max. is %d\n",
+ UBI_MAX_DEVICES);
+ return -EINVAL;
+ }
+
+ len = strnlen(val, MTD_PARAM_LEN_MAX);
+ if (len == MTD_PARAM_LEN_MAX) {
+ printk(KERN_ERR "UBI error: parameter \"%s\" is too long, "
+ "max. is %d\n", val, MTD_PARAM_LEN_MAX);
+ return -EINVAL;
+ }
+
+ if (len == 0) {
+ printk(KERN_WARNING "UBI warning: empty 'mtd=' parameter - "
+ "ignored\n");
+ return 0;
+ }
+
+ strcpy(buf, val);
+
+ /* Get rid of the final newline */
+ if (buf[len - 1] == '\n')
+ buf[len - 1] = '\0';
+
+ for (i = 0; i < 2; i++)
+ tokens[i] = strsep(&pbuf, ",");
+
+ if (pbuf) {
+ printk(KERN_ERR "UBI error: too many arguments at \"%s\"\n",
+ val);
+ return -EINVAL;
+ }
+
+ p = &mtd_dev_param[mtd_devs];
+ strcpy(&p->name[0], tokens[0]);
+
+ if (tokens[1])
+ p->vid_hdr_offs = bytes_str_to_int(tokens[1]);
+
+ if (p->vid_hdr_offs < 0)
+ return p->vid_hdr_offs;
+
+ mtd_devs += 1;
+ return 0;
+}
+
+module_param_call(mtd, ubi_mtd_param_parse, NULL, NULL, 000);
+MODULE_PARM_DESC(mtd, "MTD devices to attach. Parameter format: "
+ "mtd=<name|num>[,<vid_hdr_offs>].\n"
+ "Multiple \"mtd\" parameters may be specified.\n"
+ "MTD devices may be specified by their number or name.\n"
+ "Optional \"vid_hdr_offs\" parameter specifies UBI VID "
+ "header position and data starting position to be used "
+ "by UBI.\n"
+ "Example: mtd=content,1984 mtd=4 - attach MTD device"
+ "with name \"content\" using VID header offset 1984, and "
+ "MTD device number 4 with default VID header offset.");
+
+MODULE_VERSION(__stringify(UBI_VERSION));
+MODULE_DESCRIPTION("UBI - Unsorted Block Images");
+MODULE_AUTHOR("Artem Bityutskiy");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mtd/ubi/crc32.c b/drivers/mtd/ubi/crc32.c
new file mode 100644
index 0000000..b57a7db
--- /dev/null
+++ b/drivers/mtd/ubi/crc32.c
@@ -0,0 +1,512 @@
+/*
+ * Oct 15, 2000 Matt Domsch <Matt_Domsch(a)dell.com>
+ * Nicer crc32 functions/docs submitted by linux(a)horizon.com. Thanks!
+ * Code was from the public domain, copyright abandoned. Code was
+ * subsequently included in the kernel, thus was re-licensed under the
+ * GNU GPL v2.
+ *
+ * Oct 12, 2000 Matt Domsch <Matt_Domsch(a)dell.com>
+ * Same crc32 function was used in 5 other places in the kernel.
+ * I made one version, and deleted the others.
+ * There are various incantations of crc32(). Some use a seed of 0 or ~0.
+ * Some xor at the end with ~0. The generic crc32() function takes
+ * seed as an argument, and doesn't xor at the end. Then individual
+ * users can do whatever they need.
+ * drivers/net/smc9194.c uses seed ~0, doesn't xor with ~0.
+ * fs/jffs2 uses seed 0, doesn't xor with ~0.
+ * fs/partitions/efi.c uses seed ~0, xor's with ~0.
+ *
+ * This source code is licensed under the GNU General Public License,
+ * Version 2. See the file COPYING for more details.
+ */
+
+#ifdef UBI_LINUX
+#include <linux/crc32.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/compiler.h>
+#endif
+#include <linux/types.h>
+
+#include <asm/byteorder.h>
+
+#ifdef UBI_LINUX
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <asm/atomic.h>
+#endif
+#include "crc32defs.h"
+#define CRC_LE_BITS 8
+
+# define __force
+#ifndef __constant_cpu_to_le32
+#define __constant_cpu_to_le32(x) ((__force __le32)(__u32)(x))
+#endif
+#ifndef __constant_le32_to_cpu
+#define __constant_le32_to_cpu(x) ((__force __u32)(__le32)(x))
+#endif
+
+#if CRC_LE_BITS == 8
+#define tole(x) __constant_cpu_to_le32(x)
+#define tobe(x) __constant_cpu_to_be32(x)
+#else
+#define tole(x) (x)
+#define tobe(x) (x)
+#endif
+#include "crc32table.h"
+#ifdef UBI_LINUX
+MODULE_AUTHOR("Matt Domsch <Matt_Domsch(a)dell.com>");
+MODULE_DESCRIPTION("Ethernet CRC32 calculations");
+MODULE_LICENSE("GPL");
+#endif
+/**
+ * crc32_le() - Calculate bitwise little-endian Ethernet AUTODIN II CRC32
+ * @crc: seed value for computation. ~0 for Ethernet, sometimes 0 for
+ * other uses, or the previous crc32 value if computing incrementally.
+ * @p: pointer to buffer over which CRC is run
+ * @len: length of buffer @p
+ */
+u32 crc32_le(u32 crc, unsigned char const *p, size_t len);
+
+#if CRC_LE_BITS == 1
+/*
+ * In fact, the table-based code will work in this case, but it can be
+ * simplified by inlining the table in ?: form.
+ */
+
+u32 crc32_le(u32 crc, unsigned char const *p, size_t len)
+{
+ int i;
+ while (len--) {
+ crc ^= *p++;
+ for (i = 0; i < 8; i++)
+ crc = (crc >> 1) ^ ((crc & 1) ? CRCPOLY_LE : 0);
+ }
+ return crc;
+}
+#else /* Table-based approach */
+
+u32 crc32_le(u32 crc, unsigned char const *p, size_t len)
+{
+# if CRC_LE_BITS == 8
+ const u32 *b = (u32 *) p;
+ const u32 *tab = crc32table_le;
+
+# ifdef __LITTLE_ENDIAN
+# define DO_CRC(x) (crc = tab[(crc ^ (x)) & 255] ^ (crc>>8))
+# else
+# define DO_CRC(x) (crc = tab[((crc >> 24) ^ (x)) & 255] ^ (crc<<8))
+# endif
+ crc = __cpu_to_le32(crc);
+ /* Align it */
+ if ((((long)b) & 3 && len)) {
+ do {
+ u8 *p = (u8 *) b;
+ DO_CRC(*p++);
+ b = (void *)p;
+ } while ((--len) && ((long)b) & 3);
+ }
+ if ((len >= 4)) {
+ /* load data 32 bits wide, xor data 32 bits wide. */
+ size_t save_len = len & 3;
+ len = len >> 2;
+ --b; /* use pre increment below(*++b) for speed */
+ do {
+ crc ^= *++b;
+ DO_CRC(0);
+ DO_CRC(0);
+ DO_CRC(0);
+ DO_CRC(0);
+ } while (--len);
+ b++; /* point to next byte(s) */
+ len = save_len;
+ }
+ /* And the last few bytes */
+ if (len) {
+ do {
+ u8 *p = (u8 *) b;
+ DO_CRC(*p++);
+ b = (void *)p;
+ } while (--len);
+ }
+
+ return __le32_to_cpu(crc);
+#undef ENDIAN_SHIFT
+#undef DO_CRC
+
+# elif CRC_LE_BITS == 4
+ while (len--) {
+ crc ^= *p++;
+ crc = (crc >> 4) ^ crc32table_le[crc & 15];
+ crc = (crc >> 4) ^ crc32table_le[crc & 15];
+ }
+ return crc;
+# elif CRC_LE_BITS == 2
+ while (len--) {
+ crc ^= *p++;
+ crc = (crc >> 2) ^ crc32table_le[crc & 3];
+ crc = (crc >> 2) ^ crc32table_le[crc & 3];
+ crc = (crc >> 2) ^ crc32table_le[crc & 3];
+ crc = (crc >> 2) ^ crc32table_le[crc & 3];
+ }
+ return crc;
+# endif
+}
+#endif
+#ifdef UBI_LINUX
+/**
+ * crc32_be() - Calculate bitwise big-endian Ethernet AUTODIN II CRC32
+ * @crc: seed value for computation. ~0 for Ethernet, sometimes 0 for
+ * other uses, or the previous crc32 value if computing incrementally.
+ * @p: pointer to buffer over which CRC is run
+ * @len: length of buffer @p
+ */
+u32 __attribute_pure__ crc32_be(u32 crc, unsigned char const *p, size_t len);
+
+#if CRC_BE_BITS == 1
+/*
+ * In fact, the table-based code will work in this case, but it can be
+ * simplified by inlining the table in ?: form.
+ */
+
+u32 __attribute_pure__ crc32_be(u32 crc, unsigned char const *p, size_t len)
+{
+ int i;
+ while (len--) {
+ crc ^= *p++ << 24;
+ for (i = 0; i < 8; i++)
+ crc =
+ (crc << 1) ^ ((crc & 0x80000000) ? CRCPOLY_BE : 0);
+ }
+ return crc;
+}
+
+#else /* Table-based approach */
+u32 __attribute_pure__ crc32_be(u32 crc, unsigned char const *p, size_t len)
+{
+# if CRC_BE_BITS == 8
+ const u32 *b = (u32 *) p;
+ const u32 *tab = crc32table_be;
+
+# ifdef __LITTLE_ENDIAN
+# define DO_CRC(x) (crc = tab[(crc ^ (x)) & 255] ^ (crc>>8))
+# else
+# define DO_CRC(x) (crc = tab[((crc >> 24) ^ (x)) & 255] ^ (crc<<8))
+# endif
+
+ crc = __cpu_to_be32(crc);
+ /* Align it */
+ if (unlikely(((long)b) & 3 && len)) {
+ do {
+ u8 *p = (u8 *) b;
+ DO_CRC(*p++);
+ b = (u32 *) p;
+ } while ((--len) && ((long)b) & 3);
+ }
+ if (likely(len >= 4)) {
+ /* load data 32 bits wide, xor data 32 bits wide. */
+ size_t save_len = len & 3;
+ len = len >> 2;
+ --b; /* use pre increment below(*++b) for speed */
+ do {
+ crc ^= *++b;
+ DO_CRC(0);
+ DO_CRC(0);
+ DO_CRC(0);
+ DO_CRC(0);
+ } while (--len);
+ b++; /* point to next byte(s) */
+ len = save_len;
+ }
+ /* And the last few bytes */
+ if (len) {
+ do {
+ u8 *p = (u8 *) b;
+ DO_CRC(*p++);
+ b = (void *)p;
+ } while (--len);
+ }
+ return __be32_to_cpu(crc);
+#undef ENDIAN_SHIFT
+#undef DO_CRC
+
+# elif CRC_BE_BITS == 4
+ while (len--) {
+ crc ^= *p++ << 24;
+ crc = (crc << 4) ^ crc32table_be[crc >> 28];
+ crc = (crc << 4) ^ crc32table_be[crc >> 28];
+ }
+ return crc;
+# elif CRC_BE_BITS == 2
+ while (len--) {
+ crc ^= *p++ << 24;
+ crc = (crc << 2) ^ crc32table_be[crc >> 30];
+ crc = (crc << 2) ^ crc32table_be[crc >> 30];
+ crc = (crc << 2) ^ crc32table_be[crc >> 30];
+ crc = (crc << 2) ^ crc32table_be[crc >> 30];
+ }
+ return crc;
+# endif
+}
+#endif
+
+EXPORT_SYMBOL(crc32_le);
+EXPORT_SYMBOL(crc32_be);
+#endif
+/*
+ * A brief CRC tutorial.
+ *
+ * A CRC is a long-division remainder. You add the CRC to the message,
+ * and the whole thing (message+CRC) is a multiple of the given
+ * CRC polynomial. To check the CRC, you can either check that the
+ * CRC matches the recomputed value, *or* you can check that the
+ * remainder computed on the message+CRC is 0. This latter approach
+ * is used by a lot of hardware implementations, and is why so many
+ * protocols put the end-of-frame flag after the CRC.
+ *
+ * It's actually the same long division you learned in school, except that
+ * - We're working in binary, so the digits are only 0 and 1, and
+ * - When dividing polynomials, there are no carries. Rather than add and
+ * subtract, we just xor. Thus, we tend to get a bit sloppy about
+ * the difference between adding and subtracting.
+ *
+ * A 32-bit CRC polynomial is actually 33 bits long. But since it's
+ * 33 bits long, bit 32 is always going to be set, so usually the CRC
+ * is written in hex with the most significant bit omitted. (If you're
+ * familiar with the IEEE 754 floating-point format, it's the same idea.)
+ *
+ * Note that a CRC is computed over a string of *bits*, so you have
+ * to decide on the endianness of the bits within each byte. To get
+ * the best error-detecting properties, this should correspond to the
+ * order they're actually sent. For example, standard RS-232 serial is
+ * little-endian; the most significant bit (sometimes used for parity)
+ * is sent last. And when appending a CRC word to a message, you should
+ * do it in the right order, matching the endianness.
+ *
+ * Just like with ordinary division, the remainder is always smaller than
+ * the divisor (the CRC polynomial) you're dividing by. Each step of the
+ * division, you take one more digit (bit) of the dividend and append it
+ * to the current remainder. Then you figure out the appropriate multiple
+ * of the divisor to subtract to being the remainder back into range.
+ * In binary, it's easy - it has to be either 0 or 1, and to make the
+ * XOR cancel, it's just a copy of bit 32 of the remainder.
+ *
+ * When computing a CRC, we don't care about the quotient, so we can
+ * throw the quotient bit away, but subtract the appropriate multiple of
+ * the polynomial from the remainder and we're back to where we started,
+ * ready to process the next bit.
+ *
+ * A big-endian CRC written this way would be coded like:
+ * for (i = 0; i < input_bits; i++) {
+ * multiple = remainder & 0x80000000 ? CRCPOLY : 0;
+ * remainder = (remainder << 1 | next_input_bit()) ^ multiple;
+ * }
+ * Notice how, to get at bit 32 of the shifted remainder, we look
+ * at bit 31 of the remainder *before* shifting it.
+ *
+ * But also notice how the next_input_bit() bits we're shifting into
+ * the remainder don't actually affect any decision-making until
+ * 32 bits later. Thus, the first 32 cycles of this are pretty boring.
+ * Also, to add the CRC to a message, we need a 32-bit-long hole for it at
+ * the end, so we have to add 32 extra cycles shifting in zeros at the
+ * end of every message,
+ *
+ * So the standard trick is to rearrage merging in the next_input_bit()
+ * until the moment it's needed. Then the first 32 cycles can be precomputed,
+ * and merging in the final 32 zero bits to make room for the CRC can be
+ * skipped entirely.
+ * This changes the code to:
+ * for (i = 0; i < input_bits; i++) {
+ * remainder ^= next_input_bit() << 31;
+ * multiple = (remainder & 0x80000000) ? CRCPOLY : 0;
+ * remainder = (remainder << 1) ^ multiple;
+ * }
+ * With this optimization, the little-endian code is simpler:
+ * for (i = 0; i < input_bits; i++) {
+ * remainder ^= next_input_bit();
+ * multiple = (remainder & 1) ? CRCPOLY : 0;
+ * remainder = (remainder >> 1) ^ multiple;
+ * }
+ *
+ * Note that the other details of endianness have been hidden in CRCPOLY
+ * (which must be bit-reversed) and next_input_bit().
+ *
+ * However, as long as next_input_bit is returning the bits in a sensible
+ * order, we can actually do the merging 8 or more bits at a time rather
+ * than one bit at a time:
+ * for (i = 0; i < input_bytes; i++) {
+ * remainder ^= next_input_byte() << 24;
+ * for (j = 0; j < 8; j++) {
+ * multiple = (remainder & 0x80000000) ? CRCPOLY : 0;
+ * remainder = (remainder << 1) ^ multiple;
+ * }
+ * }
+ * Or in little-endian:
+ * for (i = 0; i < input_bytes; i++) {
+ * remainder ^= next_input_byte();
+ * for (j = 0; j < 8; j++) {
+ * multiple = (remainder & 1) ? CRCPOLY : 0;
+ * remainder = (remainder << 1) ^ multiple;
+ * }
+ * }
+ * If the input is a multiple of 32 bits, you can even XOR in a 32-bit
+ * word at a time and increase the inner loop count to 32.
+ *
+ * You can also mix and match the two loop styles, for example doing the
+ * bulk of a message byte-at-a-time and adding bit-at-a-time processing
+ * for any fractional bytes at the end.
+ *
+ * The only remaining optimization is to the byte-at-a-time table method.
+ * Here, rather than just shifting one bit of the remainder to decide
+ * in the correct multiple to subtract, we can shift a byte at a time.
+ * This produces a 40-bit (rather than a 33-bit) intermediate remainder,
+ * but again the multiple of the polynomial to subtract depends only on
+ * the high bits, the high 8 bits in this case.
+ *
+ * The multile we need in that case is the low 32 bits of a 40-bit
+ * value whose high 8 bits are given, and which is a multiple of the
+ * generator polynomial. This is simply the CRC-32 of the given
+ * one-byte message.
+ *
+ * Two more details: normally, appending zero bits to a message which
+ * is already a multiple of a polynomial produces a larger multiple of that
+ * polynomial. To enable a CRC to detect this condition, it's common to
+ * invert the CRC before appending it. This makes the remainder of the
+ * message+crc come out not as zero, but some fixed non-zero value.
+ *
+ * The same problem applies to zero bits prepended to the message, and
+ * a similar solution is used. Instead of starting with a remainder of
+ * 0, an initial remainder of all ones is used. As long as you start
+ * the same way on decoding, it doesn't make a difference.
+ */
+
+#ifdef UNITTEST
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#ifdef UBI_LINUX /*Not used at present */
+static void buf_dump(char const *prefix, unsigned char const *buf, size_t len)
+{
+ fputs(prefix, stdout);
+ while (len--)
+ printf(" %02x", *buf++);
+ putchar('\n');
+
+}
+#endif
+
+static void bytereverse(unsigned char *buf, size_t len)
+{
+ while (len--) {
+ unsigned char x = bitrev8(*buf);
+ *buf++ = x;
+ }
+}
+
+static void random_garbage(unsigned char *buf, size_t len)
+{
+ while (len--)
+ *buf++ = (unsigned char)random();
+}
+
+#ifdef UBI_LINUX /* Not used at present */
+static void store_le(u32 x, unsigned char *buf)
+{
+ buf[0] = (unsigned char)x;
+ buf[1] = (unsigned char)(x >> 8);
+ buf[2] = (unsigned char)(x >> 16);
+ buf[3] = (unsigned char)(x >> 24);
+}
+#endif
+
+static void store_be(u32 x, unsigned char *buf)
+{
+ buf[0] = (unsigned char)(x >> 24);
+ buf[1] = (unsigned char)(x >> 16);
+ buf[2] = (unsigned char)(x >> 8);
+ buf[3] = (unsigned char)x;
+}
+
+/*
+ * This checks that CRC(buf + CRC(buf)) = 0, and that
+ * CRC commutes with bit-reversal. This has the side effect
+ * of bytewise bit-reversing the input buffer, and returns
+ * the CRC of the reversed buffer.
+ */
+static u32 test_step(u32 init, unsigned char *buf, size_t len)
+{
+ u32 crc1, crc2;
+ size_t i;
+
+ crc1 = crc32_be(init, buf, len);
+ store_be(crc1, buf + len);
+ crc2 = crc32_be(init, buf, len + 4);
+ if (crc2)
+ printf("\nCRC cancellation fail: 0x%08x should be 0\n", crc2);
+
+ for (i = 0; i <= len + 4; i++) {
+ crc2 = crc32_be(init, buf, i);
+ crc2 = crc32_be(crc2, buf + i, len + 4 - i);
+ if (crc2)
+ printf("\nCRC split fail: 0x%08x\n", crc2);
+ }
+
+ /* Now swap it around for the other test */
+
+ bytereverse(buf, len + 4);
+ init = bitrev32(init);
+ crc2 = bitrev32(crc1);
+ if (crc1 != bitrev32(crc2))
+ printf("\nBit reversal fail: 0x%08x -> 0x%08x -> 0x%08x\n",
+ crc1, crc2, bitrev32(crc2));
+ crc1 = crc32_le(init, buf, len);
+ if (crc1 != crc2)
+ printf("\nCRC endianness fail: 0x%08x != 0x%08x\n", crc1, crc2);
+ crc2 = crc32_le(init, buf, len + 4);
+ if (crc2)
+ printf("\nCRC cancellation fail: 0x%08x should be 0\n", crc2);
+
+ for (i = 0; i <= len + 4; i++) {
+ crc2 = crc32_le(init, buf, i);
+ crc2 = crc32_le(crc2, buf + i, len + 4 - i);
+ if (crc2)
+ printf("\nCRC split fail: 0x%08x\n", crc2);
+ }
+
+ return crc1;
+}
+
+#define SIZE 64
+#define INIT1 0
+#define INIT2 0
+
+int main(void)
+{
+ unsigned char buf1[SIZE + 4];
+ unsigned char buf2[SIZE + 4];
+ unsigned char buf3[SIZE + 4];
+ int i, j;
+ u32 crc1, crc2, crc3;
+
+ for (i = 0; i <= SIZE; i++) {
+ printf("\rTesting length %d...", i);
+ fflush(stdout);
+ random_garbage(buf1, i);
+ random_garbage(buf2, i);
+ for (j = 0; j < i; j++)
+ buf3[j] = buf1[j] ^ buf2[j];
+
+ crc1 = test_step(INIT1, buf1, i);
+ crc2 = test_step(INIT2, buf2, i);
+ /* Now check that CRC(buf1 ^ buf2) = CRC(buf1) ^ CRC(buf2) */
+ crc3 = test_step(INIT1 ^ INIT2, buf3, i);
+ if (crc3 != (crc1 ^ crc2))
+ printf("CRC XOR fail: 0x%08x != 0x%08x ^ 0x%08x\n",
+ crc3, crc1, crc2);
+ }
+ printf("\nAll test complete. No failures expected.\n");
+ return 0;
+}
+
+#endif /* UNITTEST */
diff --git a/drivers/mtd/ubi/crc32defs.h b/drivers/mtd/ubi/crc32defs.h
new file mode 100644
index 0000000..f5a5401
--- /dev/null
+++ b/drivers/mtd/ubi/crc32defs.h
@@ -0,0 +1,32 @@
+/*
+ * There are multiple 16-bit CRC polynomials in common use, but this is
+ * *the* standard CRC-32 polynomial, first popularized by Ethernet.
+ * x^32+x^26+x^23+x^22+x^16+x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x^1+x^0
+ */
+#define CRCPOLY_LE 0xedb88320
+#define CRCPOLY_BE 0x04c11db7
+
+/* How many bits at a time to use. Requires a table of 4<<CRC_xx_BITS bytes. */
+/* For less performance-sensitive, use 4 */
+#ifndef CRC_LE_BITS
+# define CRC_LE_BITS 8
+#endif
+#ifndef CRC_BE_BITS
+# define CRC_BE_BITS 8
+#endif
+
+/*
+ * Little-endian CRC computation. Used with serial bit streams sent
+ * lsbit-first. Be sure to use cpu_to_le32() to append the computed CRC.
+ */
+#if CRC_LE_BITS > 8 || CRC_LE_BITS < 1 || CRC_LE_BITS & CRC_LE_BITS-1
+# error CRC_LE_BITS must be a power of 2 between 1 and 8
+#endif
+
+/*
+ * Big-endian CRC computation. Used with serial bit streams sent
+ * msbit-first. Be sure to use cpu_to_be32() to append the computed CRC.
+ */
+#if CRC_BE_BITS > 8 || CRC_BE_BITS < 1 || CRC_BE_BITS & CRC_BE_BITS-1
+# error CRC_BE_BITS must be a power of 2 between 1 and 8
+#endif
diff --git a/drivers/mtd/ubi/crc32table.h b/drivers/mtd/ubi/crc32table.h
new file mode 100644
index 0000000..0438af4
--- /dev/null
+++ b/drivers/mtd/ubi/crc32table.h
@@ -0,0 +1,136 @@
+/* this file is generated - do not edit */
+
+static const u32 crc32table_le[] = {
+tole(0x00000000L), tole(0x77073096L), tole(0xee0e612cL), tole(0x990951baL),
+tole(0x076dc419L), tole(0x706af48fL), tole(0xe963a535L), tole(0x9e6495a3L),
+tole(0x0edb8832L), tole(0x79dcb8a4L), tole(0xe0d5e91eL), tole(0x97d2d988L),
+tole(0x09b64c2bL), tole(0x7eb17cbdL), tole(0xe7b82d07L), tole(0x90bf1d91L),
+tole(0x1db71064L), tole(0x6ab020f2L), tole(0xf3b97148L), tole(0x84be41deL),
+tole(0x1adad47dL), tole(0x6ddde4ebL), tole(0xf4d4b551L), tole(0x83d385c7L),
+tole(0x136c9856L), tole(0x646ba8c0L), tole(0xfd62f97aL), tole(0x8a65c9ecL),
+tole(0x14015c4fL), tole(0x63066cd9L), tole(0xfa0f3d63L), tole(0x8d080df5L),
+tole(0x3b6e20c8L), tole(0x4c69105eL), tole(0xd56041e4L), tole(0xa2677172L),
+tole(0x3c03e4d1L), tole(0x4b04d447L), tole(0xd20d85fdL), tole(0xa50ab56bL),
+tole(0x35b5a8faL), tole(0x42b2986cL), tole(0xdbbbc9d6L), tole(0xacbcf940L),
+tole(0x32d86ce3L), tole(0x45df5c75L), tole(0xdcd60dcfL), tole(0xabd13d59L),
+tole(0x26d930acL), tole(0x51de003aL), tole(0xc8d75180L), tole(0xbfd06116L),
+tole(0x21b4f4b5L), tole(0x56b3c423L), tole(0xcfba9599L), tole(0xb8bda50fL),
+tole(0x2802b89eL), tole(0x5f058808L), tole(0xc60cd9b2L), tole(0xb10be924L),
+tole(0x2f6f7c87L), tole(0x58684c11L), tole(0xc1611dabL), tole(0xb6662d3dL),
+tole(0x76dc4190L), tole(0x01db7106L), tole(0x98d220bcL), tole(0xefd5102aL),
+tole(0x71b18589L), tole(0x06b6b51fL), tole(0x9fbfe4a5L), tole(0xe8b8d433L),
+tole(0x7807c9a2L), tole(0x0f00f934L), tole(0x9609a88eL), tole(0xe10e9818L),
+tole(0x7f6a0dbbL), tole(0x086d3d2dL), tole(0x91646c97L), tole(0xe6635c01L),
+tole(0x6b6b51f4L), tole(0x1c6c6162L), tole(0x856530d8L), tole(0xf262004eL),
+tole(0x6c0695edL), tole(0x1b01a57bL), tole(0x8208f4c1L), tole(0xf50fc457L),
+tole(0x65b0d9c6L), tole(0x12b7e950L), tole(0x8bbeb8eaL), tole(0xfcb9887cL),
+tole(0x62dd1ddfL), tole(0x15da2d49L), tole(0x8cd37cf3L), tole(0xfbd44c65L),
+tole(0x4db26158L), tole(0x3ab551ceL), tole(0xa3bc0074L), tole(0xd4bb30e2L),
+tole(0x4adfa541L), tole(0x3dd895d7L), tole(0xa4d1c46dL), tole(0xd3d6f4fbL),
+tole(0x4369e96aL), tole(0x346ed9fcL), tole(0xad678846L), tole(0xda60b8d0L),
+tole(0x44042d73L), tole(0x33031de5L), tole(0xaa0a4c5fL), tole(0xdd0d7cc9L),
+tole(0x5005713cL), tole(0x270241aaL), tole(0xbe0b1010L), tole(0xc90c2086L),
+tole(0x5768b525L), tole(0x206f85b3L), tole(0xb966d409L), tole(0xce61e49fL),
+tole(0x5edef90eL), tole(0x29d9c998L), tole(0xb0d09822L), tole(0xc7d7a8b4L),
+tole(0x59b33d17L), tole(0x2eb40d81L), tole(0xb7bd5c3bL), tole(0xc0ba6cadL),
+tole(0xedb88320L), tole(0x9abfb3b6L), tole(0x03b6e20cL), tole(0x74b1d29aL),
+tole(0xead54739L), tole(0x9dd277afL), tole(0x04db2615L), tole(0x73dc1683L),
+tole(0xe3630b12L), tole(0x94643b84L), tole(0x0d6d6a3eL), tole(0x7a6a5aa8L),
+tole(0xe40ecf0bL), tole(0x9309ff9dL), tole(0x0a00ae27L), tole(0x7d079eb1L),
+tole(0xf00f9344L), tole(0x8708a3d2L), tole(0x1e01f268L), tole(0x6906c2feL),
+tole(0xf762575dL), tole(0x806567cbL), tole(0x196c3671L), tole(0x6e6b06e7L),
+tole(0xfed41b76L), tole(0x89d32be0L), tole(0x10da7a5aL), tole(0x67dd4accL),
+tole(0xf9b9df6fL), tole(0x8ebeeff9L), tole(0x17b7be43L), tole(0x60b08ed5L),
+tole(0xd6d6a3e8L), tole(0xa1d1937eL), tole(0x38d8c2c4L), tole(0x4fdff252L),
+tole(0xd1bb67f1L), tole(0xa6bc5767L), tole(0x3fb506ddL), tole(0x48b2364bL),
+tole(0xd80d2bdaL), tole(0xaf0a1b4cL), tole(0x36034af6L), tole(0x41047a60L),
+tole(0xdf60efc3L), tole(0xa867df55L), tole(0x316e8eefL), tole(0x4669be79L),
+tole(0xcb61b38cL), tole(0xbc66831aL), tole(0x256fd2a0L), tole(0x5268e236L),
+tole(0xcc0c7795L), tole(0xbb0b4703L), tole(0x220216b9L), tole(0x5505262fL),
+tole(0xc5ba3bbeL), tole(0xb2bd0b28L), tole(0x2bb45a92L), tole(0x5cb36a04L),
+tole(0xc2d7ffa7L), tole(0xb5d0cf31L), tole(0x2cd99e8bL), tole(0x5bdeae1dL),
+tole(0x9b64c2b0L), tole(0xec63f226L), tole(0x756aa39cL), tole(0x026d930aL),
+tole(0x9c0906a9L), tole(0xeb0e363fL), tole(0x72076785L), tole(0x05005713L),
+tole(0x95bf4a82L), tole(0xe2b87a14L), tole(0x7bb12baeL), tole(0x0cb61b38L),
+tole(0x92d28e9bL), tole(0xe5d5be0dL), tole(0x7cdcefb7L), tole(0x0bdbdf21L),
+tole(0x86d3d2d4L), tole(0xf1d4e242L), tole(0x68ddb3f8L), tole(0x1fda836eL),
+tole(0x81be16cdL), tole(0xf6b9265bL), tole(0x6fb077e1L), tole(0x18b74777L),
+tole(0x88085ae6L), tole(0xff0f6a70L), tole(0x66063bcaL), tole(0x11010b5cL),
+tole(0x8f659effL), tole(0xf862ae69L), tole(0x616bffd3L), tole(0x166ccf45L),
+tole(0xa00ae278L), tole(0xd70dd2eeL), tole(0x4e048354L), tole(0x3903b3c2L),
+tole(0xa7672661L), tole(0xd06016f7L), tole(0x4969474dL), tole(0x3e6e77dbL),
+tole(0xaed16a4aL), tole(0xd9d65adcL), tole(0x40df0b66L), tole(0x37d83bf0L),
+tole(0xa9bcae53L), tole(0xdebb9ec5L), tole(0x47b2cf7fL), tole(0x30b5ffe9L),
+tole(0xbdbdf21cL), tole(0xcabac28aL), tole(0x53b39330L), tole(0x24b4a3a6L),
+tole(0xbad03605L), tole(0xcdd70693L), tole(0x54de5729L), tole(0x23d967bfL),
+tole(0xb3667a2eL), tole(0xc4614ab8L), tole(0x5d681b02L), tole(0x2a6f2b94L),
+tole(0xb40bbe37L), tole(0xc30c8ea1L), tole(0x5a05df1bL), tole(0x2d02ef8dL)
+};
+#ifdef UBI_LINUX
+static const u32 crc32table_be[] = {
+tobe(0x00000000L), tobe(0x04c11db7L), tobe(0x09823b6eL), tobe(0x0d4326d9L),
+tobe(0x130476dcL), tobe(0x17c56b6bL), tobe(0x1a864db2L), tobe(0x1e475005L),
+tobe(0x2608edb8L), tobe(0x22c9f00fL), tobe(0x2f8ad6d6L), tobe(0x2b4bcb61L),
+tobe(0x350c9b64L), tobe(0x31cd86d3L), tobe(0x3c8ea00aL), tobe(0x384fbdbdL),
+tobe(0x4c11db70L), tobe(0x48d0c6c7L), tobe(0x4593e01eL), tobe(0x4152fda9L),
+tobe(0x5f15adacL), tobe(0x5bd4b01bL), tobe(0x569796c2L), tobe(0x52568b75L),
+tobe(0x6a1936c8L), tobe(0x6ed82b7fL), tobe(0x639b0da6L), tobe(0x675a1011L),
+tobe(0x791d4014L), tobe(0x7ddc5da3L), tobe(0x709f7b7aL), tobe(0x745e66cdL),
+tobe(0x9823b6e0L), tobe(0x9ce2ab57L), tobe(0x91a18d8eL), tobe(0x95609039L),
+tobe(0x8b27c03cL), tobe(0x8fe6dd8bL), tobe(0x82a5fb52L), tobe(0x8664e6e5L),
+tobe(0xbe2b5b58L), tobe(0xbaea46efL), tobe(0xb7a96036L), tobe(0xb3687d81L),
+tobe(0xad2f2d84L), tobe(0xa9ee3033L), tobe(0xa4ad16eaL), tobe(0xa06c0b5dL),
+tobe(0xd4326d90L), tobe(0xd0f37027L), tobe(0xddb056feL), tobe(0xd9714b49L),
+tobe(0xc7361b4cL), tobe(0xc3f706fbL), tobe(0xceb42022L), tobe(0xca753d95L),
+tobe(0xf23a8028L), tobe(0xf6fb9d9fL), tobe(0xfbb8bb46L), tobe(0xff79a6f1L),
+tobe(0xe13ef6f4L), tobe(0xe5ffeb43L), tobe(0xe8bccd9aL), tobe(0xec7dd02dL),
+tobe(0x34867077L), tobe(0x30476dc0L), tobe(0x3d044b19L), tobe(0x39c556aeL),
+tobe(0x278206abL), tobe(0x23431b1cL), tobe(0x2e003dc5L), tobe(0x2ac12072L),
+tobe(0x128e9dcfL), tobe(0x164f8078L), tobe(0x1b0ca6a1L), tobe(0x1fcdbb16L),
+tobe(0x018aeb13L), tobe(0x054bf6a4L), tobe(0x0808d07dL), tobe(0x0cc9cdcaL),
+tobe(0x7897ab07L), tobe(0x7c56b6b0L), tobe(0x71159069L), tobe(0x75d48ddeL),
+tobe(0x6b93dddbL), tobe(0x6f52c06cL), tobe(0x6211e6b5L), tobe(0x66d0fb02L),
+tobe(0x5e9f46bfL), tobe(0x5a5e5b08L), tobe(0x571d7dd1L), tobe(0x53dc6066L),
+tobe(0x4d9b3063L), tobe(0x495a2dd4L), tobe(0x44190b0dL), tobe(0x40d816baL),
+tobe(0xaca5c697L), tobe(0xa864db20L), tobe(0xa527fdf9L), tobe(0xa1e6e04eL),
+tobe(0xbfa1b04bL), tobe(0xbb60adfcL), tobe(0xb6238b25L), tobe(0xb2e29692L),
+tobe(0x8aad2b2fL), tobe(0x8e6c3698L), tobe(0x832f1041L), tobe(0x87ee0df6L),
+tobe(0x99a95df3L), tobe(0x9d684044L), tobe(0x902b669dL), tobe(0x94ea7b2aL),
+tobe(0xe0b41de7L), tobe(0xe4750050L), tobe(0xe9362689L), tobe(0xedf73b3eL),
+tobe(0xf3b06b3bL), tobe(0xf771768cL), tobe(0xfa325055L), tobe(0xfef34de2L),
+tobe(0xc6bcf05fL), tobe(0xc27dede8L), tobe(0xcf3ecb31L), tobe(0xcbffd686L),
+tobe(0xd5b88683L), tobe(0xd1799b34L), tobe(0xdc3abdedL), tobe(0xd8fba05aL),
+tobe(0x690ce0eeL), tobe(0x6dcdfd59L), tobe(0x608edb80L), tobe(0x644fc637L),
+tobe(0x7a089632L), tobe(0x7ec98b85L), tobe(0x738aad5cL), tobe(0x774bb0ebL),
+tobe(0x4f040d56L), tobe(0x4bc510e1L), tobe(0x46863638L), tobe(0x42472b8fL),
+tobe(0x5c007b8aL), tobe(0x58c1663dL), tobe(0x558240e4L), tobe(0x51435d53L),
+tobe(0x251d3b9eL), tobe(0x21dc2629L), tobe(0x2c9f00f0L), tobe(0x285e1d47L),
+tobe(0x36194d42L), tobe(0x32d850f5L), tobe(0x3f9b762cL), tobe(0x3b5a6b9bL),
+tobe(0x0315d626L), tobe(0x07d4cb91L), tobe(0x0a97ed48L), tobe(0x0e56f0ffL),
+tobe(0x1011a0faL), tobe(0x14d0bd4dL), tobe(0x19939b94L), tobe(0x1d528623L),
+tobe(0xf12f560eL), tobe(0xf5ee4bb9L), tobe(0xf8ad6d60L), tobe(0xfc6c70d7L),
+tobe(0xe22b20d2L), tobe(0xe6ea3d65L), tobe(0xeba91bbcL), tobe(0xef68060bL),
+tobe(0xd727bbb6L), tobe(0xd3e6a601L), tobe(0xdea580d8L), tobe(0xda649d6fL),
+tobe(0xc423cd6aL), tobe(0xc0e2d0ddL), tobe(0xcda1f604L), tobe(0xc960ebb3L),
+tobe(0xbd3e8d7eL), tobe(0xb9ff90c9L), tobe(0xb4bcb610L), tobe(0xb07daba7L),
+tobe(0xae3afba2L), tobe(0xaafbe615L), tobe(0xa7b8c0ccL), tobe(0xa379dd7bL),
+tobe(0x9b3660c6L), tobe(0x9ff77d71L), tobe(0x92b45ba8L), tobe(0x9675461fL),
+tobe(0x8832161aL), tobe(0x8cf30badL), tobe(0x81b02d74L), tobe(0x857130c3L),
+tobe(0x5d8a9099L), tobe(0x594b8d2eL), tobe(0x5408abf7L), tobe(0x50c9b640L),
+tobe(0x4e8ee645L), tobe(0x4a4ffbf2L), tobe(0x470cdd2bL), tobe(0x43cdc09cL),
+tobe(0x7b827d21L), tobe(0x7f436096L), tobe(0x7200464fL), tobe(0x76c15bf8L),
+tobe(0x68860bfdL), tobe(0x6c47164aL), tobe(0x61043093L), tobe(0x65c52d24L),
+tobe(0x119b4be9L), tobe(0x155a565eL), tobe(0x18197087L), tobe(0x1cd86d30L),
+tobe(0x029f3d35L), tobe(0x065e2082L), tobe(0x0b1d065bL), tobe(0x0fdc1becL),
+tobe(0x3793a651L), tobe(0x3352bbe6L), tobe(0x3e119d3fL), tobe(0x3ad08088L),
+tobe(0x2497d08dL), tobe(0x2056cd3aL), tobe(0x2d15ebe3L), tobe(0x29d4f654L),
+tobe(0xc5a92679L), tobe(0xc1683bceL), tobe(0xcc2b1d17L), tobe(0xc8ea00a0L),
+tobe(0xd6ad50a5L), tobe(0xd26c4d12L), tobe(0xdf2f6bcbL), tobe(0xdbee767cL),
+tobe(0xe3a1cbc1L), tobe(0xe760d676L), tobe(0xea23f0afL), tobe(0xeee2ed18L),
+tobe(0xf0a5bd1dL), tobe(0xf464a0aaL), tobe(0xf9278673L), tobe(0xfde69bc4L),
+tobe(0x89b8fd09L), tobe(0x8d79e0beL), tobe(0x803ac667L), tobe(0x84fbdbd0L),
+tobe(0x9abc8bd5L), tobe(0x9e7d9662L), tobe(0x933eb0bbL), tobe(0x97ffad0cL),
+tobe(0xafb010b1L), tobe(0xab710d06L), tobe(0xa6322bdfL), tobe(0xa2f33668L),
+tobe(0xbcb4666dL), tobe(0xb8757bdaL), tobe(0xb5365d03L), tobe(0xb1f740b4L)
+};
+#endif
diff --git a/drivers/mtd/ubi/debug.c b/drivers/mtd/ubi/debug.c
new file mode 100644
index 0000000..492ab5c
--- /dev/null
+++ b/drivers/mtd/ubi/debug.c
@@ -0,0 +1,192 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Author: Artem Bityutskiy (Битюцкий Артём)
+ */
+
+/*
+ * Here we keep all the UBI debugging stuff which should normally be disabled
+ * and compiled-out, but it is extremely helpful when hunting bugs or doing big
+ * changes.
+ */
+#include <ubi_uboot.h>
+
+#ifdef CONFIG_MTD_UBI_DEBUG_MSG
+
+#include "ubi.h"
+
+/**
+ * ubi_dbg_dump_ec_hdr - dump an erase counter header.
+ * @ec_hdr: the erase counter header to dump
+ */
+void ubi_dbg_dump_ec_hdr(const struct ubi_ec_hdr *ec_hdr)
+{
+ dbg_msg("erase counter header dump:");
+ dbg_msg("magic %#08x", be32_to_cpu(ec_hdr->magic));
+ dbg_msg("version %d", (int)ec_hdr->version);
+ dbg_msg("ec %llu", (long long)be64_to_cpu(ec_hdr->ec));
+ dbg_msg("vid_hdr_offset %d", be32_to_cpu(ec_hdr->vid_hdr_offset));
+ dbg_msg("data_offset %d", be32_to_cpu(ec_hdr->data_offset));
+ dbg_msg("hdr_crc %#08x", be32_to_cpu(ec_hdr->hdr_crc));
+ dbg_msg("erase counter header hexdump:");
+ print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET, 32, 1,
+ ec_hdr, UBI_EC_HDR_SIZE, 1);
+}
+
+/**
+ * ubi_dbg_dump_vid_hdr - dump a volume identifier header.
+ * @vid_hdr: the volume identifier header to dump
+ */
+void ubi_dbg_dump_vid_hdr(const struct ubi_vid_hdr *vid_hdr)
+{
+ dbg_msg("volume identifier header dump:");
+ dbg_msg("magic %08x", be32_to_cpu(vid_hdr->magic));
+ dbg_msg("version %d", (int)vid_hdr->version);
+ dbg_msg("vol_type %d", (int)vid_hdr->vol_type);
+ dbg_msg("copy_flag %d", (int)vid_hdr->copy_flag);
+ dbg_msg("compat %d", (int)vid_hdr->compat);
+ dbg_msg("vol_id %d", be32_to_cpu(vid_hdr->vol_id));
+ dbg_msg("lnum %d", be32_to_cpu(vid_hdr->lnum));
+ dbg_msg("leb_ver %u", be32_to_cpu(vid_hdr->leb_ver));
+ dbg_msg("data_size %d", be32_to_cpu(vid_hdr->data_size));
+ dbg_msg("used_ebs %d", be32_to_cpu(vid_hdr->used_ebs));
+ dbg_msg("data_pad %d", be32_to_cpu(vid_hdr->data_pad));
+ dbg_msg("sqnum %llu",
+ (unsigned long long)be64_to_cpu(vid_hdr->sqnum));
+ dbg_msg("hdr_crc %08x", be32_to_cpu(vid_hdr->hdr_crc));
+ dbg_msg("volume identifier header hexdump:");
+}
+
+/**
+ * ubi_dbg_dump_vol_info- dump volume information.
+ * @vol: UBI volume description object
+ */
+void ubi_dbg_dump_vol_info(const struct ubi_volume *vol)
+{
+ dbg_msg("volume information dump:");
+ dbg_msg("vol_id %d", vol->vol_id);
+ dbg_msg("reserved_pebs %d", vol->reserved_pebs);
+ dbg_msg("alignment %d", vol->alignment);
+ dbg_msg("data_pad %d", vol->data_pad);
+ dbg_msg("vol_type %d", vol->vol_type);
+ dbg_msg("name_len %d", vol->name_len);
+ dbg_msg("usable_leb_size %d", vol->usable_leb_size);
+ dbg_msg("used_ebs %d", vol->used_ebs);
+ dbg_msg("used_bytes %lld", vol->used_bytes);
+ dbg_msg("last_eb_bytes %d", vol->last_eb_bytes);
+ dbg_msg("corrupted %d", vol->corrupted);
+ dbg_msg("upd_marker %d", vol->upd_marker);
+
+ if (vol->name_len <= UBI_VOL_NAME_MAX &&
+ strnlen(vol->name, vol->name_len + 1) == vol->name_len) {
+ dbg_msg("name %s", vol->name);
+ } else {
+ dbg_msg("the 1st 5 characters of the name: %c%c%c%c%c",
+ vol->name[0], vol->name[1], vol->name[2],
+ vol->name[3], vol->name[4]);
+ }
+}
+
+/**
+ * ubi_dbg_dump_vtbl_record - dump a &struct ubi_vtbl_record object.
+ * @r: the object to dump
+ * @idx: volume table index
+ */
+void ubi_dbg_dump_vtbl_record(const struct ubi_vtbl_record *r, int idx)
+{
+ int name_len = be16_to_cpu(r->name_len);
+
+ dbg_msg("volume table record %d dump:", idx);
+ dbg_msg("reserved_pebs %d", be32_to_cpu(r->reserved_pebs));
+ dbg_msg("alignment %d", be32_to_cpu(r->alignment));
+ dbg_msg("data_pad %d", be32_to_cpu(r->data_pad));
+ dbg_msg("vol_type %d", (int)r->vol_type);
+ dbg_msg("upd_marker %d", (int)r->upd_marker);
+ dbg_msg("name_len %d", name_len);
+
+ if (r->name[0] == '\0') {
+ dbg_msg("name NULL");
+ return;
+ }
+
+ if (name_len <= UBI_VOL_NAME_MAX &&
+ strnlen(&r->name[0], name_len + 1) == name_len) {
+ dbg_msg("name %s", &r->name[0]);
+ } else {
+ dbg_msg("1st 5 characters of the name: %c%c%c%c%c",
+ r->name[0], r->name[1], r->name[2], r->name[3],
+ r->name[4]);
+ }
+ dbg_msg("crc %#08x", be32_to_cpu(r->crc));
+}
+
+/**
+ * ubi_dbg_dump_sv - dump a &struct ubi_scan_volume object.
+ * @sv: the object to dump
+ */
+void ubi_dbg_dump_sv(const struct ubi_scan_volume *sv)
+{
+ dbg_msg("volume scanning information dump:");
+ dbg_msg("vol_id %d", sv->vol_id);
+ dbg_msg("highest_lnum %d", sv->highest_lnum);
+ dbg_msg("leb_count %d", sv->leb_count);
+ dbg_msg("compat %d", sv->compat);
+ dbg_msg("vol_type %d", sv->vol_type);
+ dbg_msg("used_ebs %d", sv->used_ebs);
+ dbg_msg("last_data_size %d", sv->last_data_size);
+ dbg_msg("data_pad %d", sv->data_pad);
+}
+
+/**
+ * ubi_dbg_dump_seb - dump a &struct ubi_scan_leb object.
+ * @seb: the object to dump
+ * @type: object type: 0 - not corrupted, 1 - corrupted
+ */
+void ubi_dbg_dump_seb(const struct ubi_scan_leb *seb, int type)
+{
+ dbg_msg("eraseblock scanning information dump:");
+ dbg_msg("ec %d", seb->ec);
+ dbg_msg("pnum %d", seb->pnum);
+ if (type == 0) {
+ dbg_msg("lnum %d", seb->lnum);
+ dbg_msg("scrub %d", seb->scrub);
+ dbg_msg("sqnum %llu", seb->sqnum);
+ dbg_msg("leb_ver %u", seb->leb_ver);
+ }
+}
+
+/**
+ * ubi_dbg_dump_mkvol_req - dump a &struct ubi_mkvol_req object.
+ * @req: the object to dump
+ */
+void ubi_dbg_dump_mkvol_req(const struct ubi_mkvol_req *req)
+{
+ char nm[17];
+
+ dbg_msg("volume creation request dump:");
+ dbg_msg("vol_id %d", req->vol_id);
+ dbg_msg("alignment %d", req->alignment);
+ dbg_msg("bytes %lld", (long long)req->bytes);
+ dbg_msg("vol_type %d", req->vol_type);
+ dbg_msg("name_len %d", req->name_len);
+
+ memcpy(nm, req->name, 16);
+ nm[16] = 0;
+ dbg_msg("the 1st 16 characters of the name: %s", nm);
+}
+
+#endif /* CONFIG_MTD_UBI_DEBUG_MSG */
diff --git a/drivers/mtd/ubi/debug.h b/drivers/mtd/ubi/debug.h
new file mode 100644
index 0000000..06294ac
--- /dev/null
+++ b/drivers/mtd/ubi/debug.h
@@ -0,0 +1,152 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Author: Artem Bityutskiy (Битюцкий Артём)
+ */
+
+#ifndef __UBI_DEBUG_H__
+#define __UBI_DEBUG_H__
+
+#ifdef CONFIG_MTD_UBI_DEBUG
+#ifdef UBI_LINUX
+#include <linux/random.h>
+#endif
+
+#define ubi_assert(expr) BUG_ON(!(expr))
+#define dbg_err(fmt, ...) ubi_err(fmt, ##__VA_ARGS__)
+#else
+#define ubi_assert(expr) ({})
+#define dbg_err(fmt, ...) ({})
+#endif
+
+#ifdef CONFIG_MTD_UBI_DEBUG_DISABLE_BGT
+#define DBG_DISABLE_BGT 1
+#else
+#define DBG_DISABLE_BGT 0
+#endif
+
+#ifdef CONFIG_MTD_UBI_DEBUG_MSG
+/* Generic debugging message */
+#define dbg_msg(fmt, ...) \
+ printk(KERN_DEBUG "UBI DBG: %s: " fmt "\n", \
+ __func__, ##__VA_ARGS__)
+
+#define ubi_dbg_dump_stack() dump_stack()
+
+struct ubi_ec_hdr;
+struct ubi_vid_hdr;
+struct ubi_volume;
+struct ubi_vtbl_record;
+struct ubi_scan_volume;
+struct ubi_scan_leb;
+struct ubi_mkvol_req;
+
+void ubi_dbg_dump_ec_hdr(const struct ubi_ec_hdr *ec_hdr);
+void ubi_dbg_dump_vid_hdr(const struct ubi_vid_hdr *vid_hdr);
+void ubi_dbg_dump_vol_info(const struct ubi_volume *vol);
+void ubi_dbg_dump_vtbl_record(const struct ubi_vtbl_record *r, int idx);
+void ubi_dbg_dump_sv(const struct ubi_scan_volume *sv);
+void ubi_dbg_dump_seb(const struct ubi_scan_leb *seb, int type);
+void ubi_dbg_dump_mkvol_req(const struct ubi_mkvol_req *req);
+
+#else
+
+#define dbg_msg(fmt, ...) ({})
+#define ubi_dbg_dump_stack() ({})
+#define ubi_dbg_dump_ec_hdr(ec_hdr) ({})
+#define ubi_dbg_dump_vid_hdr(vid_hdr) ({})
+#define ubi_dbg_dump_vol_info(vol) ({})
+#define ubi_dbg_dump_vtbl_record(r, idx) ({})
+#define ubi_dbg_dump_sv(sv) ({})
+#define ubi_dbg_dump_seb(seb, type) ({})
+#define ubi_dbg_dump_mkvol_req(req) ({})
+
+#endif /* CONFIG_MTD_UBI_DEBUG_MSG */
+
+#ifdef CONFIG_MTD_UBI_DEBUG_MSG_EBA
+/* Messages from the eraseblock association unit */
+#define dbg_eba(fmt, ...) dbg_msg(fmt, ##__VA_ARGS__)
+#else
+#define dbg_eba(fmt, ...) ({})
+#endif
+
+#ifdef CONFIG_MTD_UBI_DEBUG_MSG_WL
+/* Messages from the wear-leveling unit */
+#define dbg_wl(fmt, ...) dbg_msg(fmt, ##__VA_ARGS__)
+#else
+#define dbg_wl(fmt, ...) ({})
+#endif
+
+#ifdef CONFIG_MTD_UBI_DEBUG_MSG_IO
+/* Messages from the input/output unit */
+#define dbg_io(fmt, ...) dbg_msg(fmt, ##__VA_ARGS__)
+#else
+#define dbg_io(fmt, ...) ({})
+#endif
+
+#ifdef CONFIG_MTD_UBI_DEBUG_MSG_BLD
+/* Initialization and build messages */
+#define dbg_bld(fmt, ...) dbg_msg(fmt, ##__VA_ARGS__)
+#else
+#define dbg_bld(fmt, ...) ({})
+#endif
+
+#ifdef CONFIG_MTD_UBI_DEBUG_EMULATE_BITFLIPS
+/**
+ * ubi_dbg_is_bitflip - if it is time to emulate a bit-flip.
+ *
+ * Returns non-zero if a bit-flip should be emulated, otherwise returns zero.
+ */
+static inline int ubi_dbg_is_bitflip(void)
+{
+ return !(random32() % 200);
+}
+#else
+#define ubi_dbg_is_bitflip() 0
+#endif
+
+#ifdef CONFIG_MTD_UBI_DEBUG_EMULATE_WRITE_FAILURES
+/**
+ * ubi_dbg_is_write_failure - if it is time to emulate a write failure.
+ *
+ * Returns non-zero if a write failure should be emulated, otherwise returns
+ * zero.
+ */
+static inline int ubi_dbg_is_write_failure(void)
+{
+ return !(random32() % 500);
+}
+#else
+#define ubi_dbg_is_write_failure() 0
+#endif
+
+#ifdef CONFIG_MTD_UBI_DEBUG_EMULATE_ERASE_FAILURES
+/**
+ * ubi_dbg_is_erase_failure - if its time to emulate an erase failure.
+ *
+ * Returns non-zero if an erase failure should be emulated, otherwise returns
+ * zero.
+ */
+static inline int ubi_dbg_is_erase_failure(void)
+{
+ return !(random32() % 400);
+}
+#else
+#define ubi_dbg_is_erase_failure() 0
+#endif
+
+#endif /* !__UBI_DEBUG_H__ */
2
3
It supports basic operation such as create, remove, read, and write.
Signed-off-by: Kyungmin Park <kyungmin.park(a)samsung.com>
---
diff --git a/common/Makefile b/common/Makefile
index f00cbd9..b02a541 100644
--- a/common/Makefile
+++ b/common/Makefile
@@ -139,6 +139,7 @@ COBJS-$(CONFIG_CMD_SETEXPR) += cmd_setexpr.o
COBJS-$(CONFIG_CMD_SPI) += cmd_spi.o
COBJS-$(CONFIG_CMD_STRINGS) += cmd_strings.o
COBJS-$(CONFIG_CMD_TERMINAL) += cmd_terminal.o
+COBJS-$(CONFIG_CMD_UBI) += cmd_ubi.o
COBJS-$(CONFIG_CMD_UNIVERSE) += cmd_universe.o
ifdef CONFIG_CMD_USB
COBJS-y += cmd_usb.o
diff --git a/common/cmd_ubi.c b/common/cmd_ubi.c
new file mode 100644
index 0000000..8c46cca
--- /dev/null
+++ b/common/cmd_ubi.c
@@ -0,0 +1,515 @@
+/*
+ * Unsorted Block Image commands
+ *
+ * Copyright (C) 2008 Samsung Electronics
+ * Kyungmin Park <kyungmin.park(a)samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <common.h>
+#include <command.h>
+#include <exports.h>
+
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+#include <ubi_uboot.h>
+#include <asm/errno.h>
+
+/* Private own data */
+static struct ubi_device *ubi;
+static int ubi_initialized;
+
+static void ubi_dump_vol_info(const struct ubi_volume *vol)
+{
+ ubi_msg("volume information dump:");
+ ubi_msg("vol_id %d", vol->vol_id);
+ ubi_msg("reserved_pebs %d", vol->reserved_pebs);
+ ubi_msg("alignment %d", vol->alignment);
+ ubi_msg("data_pad %d", vol->data_pad);
+ ubi_msg("vol_type %d", vol->vol_type);
+ ubi_msg("name_len %d", vol->name_len);
+ ubi_msg("usable_leb_size %d", vol->usable_leb_size);
+ ubi_msg("used_ebs %d", vol->used_ebs);
+ ubi_msg("used_bytes %lld", vol->used_bytes);
+ ubi_msg("last_eb_bytes %d", vol->last_eb_bytes);
+ ubi_msg("corrupted %d", vol->corrupted);
+ ubi_msg("upd_marker %d", vol->upd_marker);
+
+ if (vol->name_len <= UBI_VOL_NAME_MAX &&
+ strnlen(vol->name, vol->name_len + 1) == vol->name_len) {
+ ubi_msg("name %s", vol->name);
+ } else {
+ ubi_msg("the 1st 5 characters of the name: %c%c%c%c%c",
+ vol->name[0], vol->name[1], vol->name[2],
+ vol->name[3], vol->name[4]);
+ }
+ printf("\n");
+}
+
+static void display_volume_info(struct ubi_device *ubi)
+{
+ int i;
+
+ for (i = 0; i < (ubi->vtbl_slots + 1); i++) {
+ if (!ubi->volumes[i])
+ continue; /* Empty record */
+ ubi_dump_vol_info(ubi->volumes[i]);
+ }
+}
+
+static void display_ubi_info(struct ubi_device *ubi)
+{
+ ubi_msg("MTD device name: \"%s\"", ubi->mtd->name);
+ ubi_msg("MTD device size: %llu MiB", ubi->flash_size >> 20);
+ ubi_msg("physical eraseblock size: %d bytes (%d KiB)",
+ ubi->peb_size, ubi->peb_size >> 10);
+ ubi_msg("logical eraseblock size: %d bytes", ubi->leb_size);
+ ubi_msg("number of good PEBs: %d", ubi->good_peb_count);
+ ubi_msg("number of bad PEBs: %d", ubi->bad_peb_count);
+ ubi_msg("smallest flash I/O unit: %d", ubi->min_io_size);
+ ubi_msg("VID header offset: %d (aligned %d)",
+ ubi->vid_hdr_offset, ubi->vid_hdr_aloffset);
+ ubi_msg("data offset: %d", ubi->leb_start);
+ ubi_msg("max. allowed volumes: %d", ubi->vtbl_slots);
+ ubi_msg("wear-leveling threshold: %d", CONFIG_MTD_UBI_WL_THRESHOLD);
+ ubi_msg("number of internal volumes: %d", UBI_INT_VOL_COUNT);
+ ubi_msg("number of user volumes: %d",
+ ubi->vol_count - UBI_INT_VOL_COUNT);
+ ubi_msg("available PEBs: %d", ubi->avail_pebs);
+ ubi_msg("total number of reserved PEBs: %d", ubi->rsvd_pebs);
+ ubi_msg("number of PEBs reserved for bad PEB handling: %d",
+ ubi->beb_rsvd_pebs);
+ ubi_msg("max/mean erase counter: %d/%d", ubi->max_ec, ubi->mean_ec);
+}
+
+static int ubi_info(int layout)
+{
+ if (layout)
+ display_volume_info(ubi);
+ else
+ display_ubi_info(ubi);
+
+ return 0;
+}
+
+static int parse_num(size_t *num, const char *token)
+{
+ char *endp;
+ size_t n;
+
+ n = (size_t) ustrtoul(token, &endp, 0);
+ if (*endp)
+ return -EINVAL;
+
+ *num = n;
+ return 0;
+}
+
+static int verify_mkvol_req(const struct ubi_device *ubi,
+ const struct ubi_mkvol_req *req)
+{
+ int n, err = -EINVAL;
+
+ if (req->bytes < 0 || req->alignment < 0 || req->vol_type < 0 ||
+ req->name_len < 0)
+ goto bad;
+
+ if ((req->vol_id < 0 || req->vol_id >= ubi->vtbl_slots) &&
+ req->vol_id != UBI_VOL_NUM_AUTO)
+ goto bad;
+
+ if (req->alignment == 0)
+ goto bad;
+
+ if (req->bytes == 0)
+ goto bad;
+
+ if (req->vol_type != UBI_DYNAMIC_VOLUME &&
+ req->vol_type != UBI_STATIC_VOLUME)
+ goto bad;
+
+ if (req->alignment > ubi->leb_size)
+ goto bad;
+
+ n = req->alignment % ubi->min_io_size;
+ if (req->alignment != 1 && n)
+ goto bad;
+
+ if (req->name_len > UBI_VOL_NAME_MAX) {
+ err = -ENAMETOOLONG;
+ goto bad;
+ }
+
+ return 0;
+bad:
+ printf("bad volume creation request");
+ return err;
+}
+
+static int ubi_create_vol(char *volume, int size, int dynamic)
+{
+ struct ubi_mkvol_req req;
+ int err;
+
+ if (dynamic)
+ req.vol_type = UBI_DYNAMIC_VOLUME;
+ else
+ req.vol_type = UBI_STATIC_VOLUME;
+
+ req.vol_id = UBI_VOL_NUM_AUTO;
+ req.alignment = 1;
+ req.bytes = size;
+
+ strcpy(req.name, volume);
+ req.name_len = strlen(volume);
+ req.name[req.name_len] = '\0';
+ req.padding1 = 0;
+ /* It's duplicated at drivers/mtd/ubi/cdev.c */
+ err = verify_mkvol_req(ubi, &req);
+ if (err) {
+ printf("verify_mkvol_req failed %d\n", err);
+ return err;
+ }
+ printf("Creating %s volume %s of size %d\n",
+ dynamic ? "dynamic" : "static", volume, size);
+ /* Call real ubi create volume */
+ return ubi_create_volume(ubi, &req);
+}
+
+static int ubi_remove_vol(char *volume)
+{
+ int i, err, reserved_pebs;
+ int found = 0, vol_id = 0;
+ struct ubi_volume *vol;
+
+ for (i = 0; i < ubi->vtbl_slots; i++) {
+ vol = ubi->volumes[i];
+ if (vol && !strcmp(vol->name, volume)) {
+ printf("Volume %s found at valid %d\n", volume, i);
+ vol_id = i;
+ found = 1;
+ break;
+ }
+ }
+ if (!found) {
+ printf("%s volume not found\n", volume);
+ return -ENODEV;
+ }
+ printf("remove UBI volume %s (id %d)\n", vol->name, vol->vol_id);
+
+ if (ubi->ro_mode) {
+ printf("It's read-only mode\n");
+ err = -EROFS;
+ goto out_err;
+ }
+
+ err = ubi_change_vtbl_record(ubi, vol_id, NULL);
+ if (err) {
+ printf("Error changing Vol tabel record err=%x\n", err);
+ goto out_err;
+ }
+ reserved_pebs = vol->reserved_pebs;
+ for (i = 0; i < vol->reserved_pebs; i++) {
+ err = ubi_eba_unmap_leb(ubi, vol, i);
+ if (err)
+ goto out_err;
+ }
+
+ kfree(vol->eba_tbl);
+ ubi->volumes[vol_id]->eba_tbl = NULL;
+ ubi->volumes[vol_id] = NULL;
+
+ ubi->rsvd_pebs -= reserved_pebs;
+ ubi->avail_pebs += reserved_pebs;
+ i = ubi->beb_rsvd_level - ubi->beb_rsvd_pebs;
+ if (i > 0) {
+ i = ubi->avail_pebs >= i ? i : ubi->avail_pebs;
+ ubi->avail_pebs -= i;
+ ubi->rsvd_pebs += i;
+ ubi->beb_rsvd_pebs += i;
+ if (i > 0)
+ ubi_msg("reserve more %d PEBs", i);
+ }
+ ubi->vol_count -= 1;
+
+ return 0;
+out_err:
+ ubi_err("cannot remove volume %d, error %d", vol_id, err);
+ return err;
+}
+
+static int ubi_volume_write(char *volume, void *buf, size_t size)
+{
+ int i = 0, err = -1;
+ int rsvd_bytes = 0;
+ int found = 0;
+ struct ubi_volume *vol;
+
+ for (i = 0; i < ubi->vtbl_slots; i++) {
+ vol = ubi->volumes[i];
+ if (vol && !strcmp(vol->name, volume)) {
+ printf("Volume \"%s\" found with id %d\n", volume, i);
+ found = 1;
+ break;
+ }
+ }
+ if (!found) {
+ printf("%s voume not found\n", volume);
+ return 1;
+ }
+ rsvd_bytes = vol->reserved_pebs * (ubi->leb_size - vol->data_pad);
+ if (size < 0 || size > rsvd_bytes) {
+ printf("rsvd_bytes=%d vol->reserved_pebs=%d ubi->leb_size=%d\n",
+ rsvd_bytes, vol->reserved_pebs, ubi->leb_size);
+ printf("vol->data_pad=%d\n", vol->data_pad);
+ printf("Size > volume size !!\n");
+ return 1;
+ }
+
+ err = ubi_start_update(ubi, vol, size);
+ if (err < 0) {
+ printf("Cannot start volume update\n");
+ return err;
+ }
+
+ err = ubi_more_update_data(ubi, vol, buf, size);
+ if (err < 0) {
+ printf("Couldnt or partially wrote data \n");
+ return err;
+ }
+
+ if (err) {
+ size = err;
+
+ err = ubi_check_volume(ubi, vol->vol_id);
+ if (err < 0)
+ return err;
+
+ if (err) {
+ ubi_warn("volume %d on UBI device %d is corrupted",
+ vol->vol_id, ubi->ubi_num);
+ vol->corrupted = 1;
+ }
+
+ vol->checked = 1;
+ ubi_gluebi_updated(vol);
+ }
+
+ return 0;
+}
+
+static int ubi_volume_read(char *volume, char *buf, size_t size)
+{
+ int err, lnum, off, len, tbuf_size, i = 0;
+ size_t count_save = size;
+ void *tbuf;
+ unsigned long long tmp;
+ struct ubi_volume *vol = NULL;
+ loff_t offp = 0;
+
+ for (i = 0; i < ubi->vtbl_slots; i++) {
+ vol = ubi->volumes[i];
+ if (vol && !strcmp(vol->name, volume)) {
+ printf("Volume %s found at volume id %d\n",
+ volume, vol->vol_id);
+ break;
+ }
+ }
+ if (i == ubi->vtbl_slots) {
+ printf("%s voume not found\n", volume);
+ return 0;
+ }
+
+ printf("read %i bytes from volume %d to %x(buf address)\n",
+ (int) size, vol->vol_id, (unsigned)buf);
+
+ if (vol->updating) {
+ printf("updating");
+ return -EBUSY;
+ }
+ if (vol->upd_marker) {
+ printf("damaged volume, update marker is set");
+ return -EBADF;
+ }
+ if (offp == vol->used_bytes)
+ return 0;
+
+ if (size == 0) {
+ printf("Read [%lu] bytes\n", (unsigned long) vol->used_bytes);
+ size = vol->used_bytes;
+ }
+
+ if (vol->corrupted)
+ printf("read from corrupted volume %d", vol->vol_id);
+ if (offp + size > vol->used_bytes)
+ count_save = size = vol->used_bytes - offp;
+
+ tbuf_size = vol->usable_leb_size;
+ if (size < tbuf_size)
+ tbuf_size = ALIGN(size, ubi->min_io_size);
+ tbuf = malloc(tbuf_size);
+ if (!tbuf) {
+ printf("NO MEM\n");
+ return -ENOMEM;
+ }
+ len = size > tbuf_size ? tbuf_size : size;
+
+ tmp = offp;
+ off = do_div(tmp, vol->usable_leb_size);
+ lnum = tmp;
+ printf("off=%d lnum=%d\n", off, lnum);
+ do {
+ if (off + len >= vol->usable_leb_size)
+ len = vol->usable_leb_size - off;
+
+ err = ubi_eba_read_leb(ubi, vol, lnum, tbuf, off, len, 0);
+ if (err) {
+ printf("read err %x\n", err);
+ break;
+ }
+ off += len;
+ if (off == vol->usable_leb_size) {
+ lnum += 1;
+ off -= vol->usable_leb_size;
+ }
+
+ size -= len;
+ offp += len;
+
+ printf("buf = %x\n", (unsigned)buf);
+ memcpy(buf, tbuf, len);
+ printf("buf[0] = %x\n", buf[0]);
+
+ buf += len;
+ len = size > tbuf_size ? tbuf_size : size;
+ } while (size);
+
+ free(tbuf);
+ return err ? err : count_save - size;
+}
+
+static int do_ubi(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
+{
+ size_t size = 0;
+ ulong addr = 0;
+ int err = 0;
+
+ if (!ubi_initialized) {
+ err = ubi_board_scan();
+ if (err) {
+ printf("UBI init error %d\n", err);
+ return err;
+ }
+ ubi = ubi_devices[0];
+ ubi_initialized = 1;
+ }
+
+ if (argc < 2) {
+ printf("Usage:\n%s\n", cmdtp->usage);
+ return 1;
+ }
+ if (strcmp(argv[1], "info") == 0) {
+ int layout = 0;
+ if (argc > 2 && !strncmp(argv[2], "l", 1))
+ layout = 1;
+ return ubi_info(layout);
+ }
+ if (strncmp(argv[1], "create", 6) == 0) {
+ int dynamic = 1; /* default: dynamic volume */
+
+ /* Use maximum available size */
+ size = 0;
+
+ /* E.g., create volume size type */
+ if (argc == 5) {
+ if (strncmp(argv[4], "s", 1) == 0)
+ dynamic = 0;
+ else if (strncmp(argv[4], "d", 1) != 0) {
+ printf("Incorrect type\n");
+ return 1;
+ }
+ argc--;
+ }
+ /* E.g., create volume size */
+ if (argc == 4) {
+ err = parse_num(&size, argv[3]);
+ if (err) {
+ printf("Incorrect type\n");
+ return err;
+ }
+ argc--;
+ }
+ /* Use maximum available size */
+ if (!size)
+ size = ubi->avail_pebs * ubi->leb_size;
+ /* E.g., create volume */
+ if (argc == 3)
+ return ubi_create_vol(argv[2], size, dynamic);
+ }
+ if (strncmp(argv[1], "remove", 6) == 0) {
+ /* E.g., remove volume */
+ if (argc == 3)
+ return ubi_remove_vol(argv[2]);
+ }
+ if (strncmp(argv[1], "write", 5) == 0) {
+ if (argc < 5) {
+ printf("Please see usage\n");
+ return 1;
+ }
+
+ addr = simple_strtoul(argv[2], NULL, 16);
+ err = parse_num(&size, argv[4]);
+ if (err) {
+ printf("Please see usage\n");
+ return err;
+ }
+
+ return ubi_volume_write(argv[3], (void *)addr, size);
+ }
+ if (strncmp(argv[1], "read", 4) == 0) {
+ size = 0;
+
+ /* E.g., read volume size */
+ if (argc == 5) {
+ err = parse_num(&size, argv[4]);
+ if (err) {
+ printf("Please see usage\n");
+ return err;
+ }
+ argc--;
+ }
+
+ /* E.g., read volume */
+ if (argc == 4) {
+ addr = simple_strtoul(argv[2], NULL, 16);
+ argc--;
+ }
+
+ if (argc == 3)
+ return ubi_volume_read(argv[3], (char *)addr, size);
+ }
+
+ printf("Please see usage\n");
+ return -1;
+}
+
+U_BOOT_CMD(ubi, 6, 1, do_ubi,
+ "ubi - ubi commands\n",
+ "info [l[ayout]]"
+ " - Display volume and ubi layout information\n"
+ "ubi create[vol] volume [size] [type]"
+ " - create volume name with size\n"
+ "ubi write[vol] address volume size"
+ " - Write volume from address with size\n"
+ "ubi read[vol] address volume [size]"
+ " - Read volume to address with size\n"
+ "ubi remove[vol] volume"
+ " - Remove volume\n"
+ "[Legends]\n"
+ " volume: charater name\n"
+ " size: KiB, MiB, GiB, and bytes\n"
+ " type: s[tatic] or d[ynamic] (default=dynamic)\n"
+);
diff --git a/include/exports.h b/include/exports.h
index 6377875..0620e9e 100644
--- a/include/exports.h
+++ b/include/exports.h
@@ -25,6 +25,7 @@ char *getenv (char *name);
int setenv (char *varname, char *varvalue);
long simple_strtol(const char *cp,char **endp,unsigned int base);
int strcmp(const char * cs,const char * ct);
+int ustrtoul(const char *cp, char **endp, unsigned int base);
#ifdef CONFIG_HAS_UID
void forceenv (char *varname, char *varvalue);
#endif
diff --git a/include/jffs2/load_kernel.h b/include/jffs2/load_kernel.h
index 551fd0c..e9b7d6e 100644
--- a/include/jffs2/load_kernel.h
+++ b/include/jffs2/load_kernel.h
@@ -73,4 +73,9 @@ struct mtdids {
#define putLabeledWord(x, y) printf("%s %08x\n", x, (unsigned int)y)
#define led_blink(x, y, z, a)
+/* common/cmd_jffs2.c */
+extern int mtdparts_init(void);
+extern int find_dev_and_part(const char *id, struct mtd_device **dev,
+ u8 *part_num, struct part_info **part);
+
#endif /* load_kernel_h */
diff --git a/lib_generic/vsprintf.c b/lib_generic/vsprintf.c
index 6e903db..767dde1 100644
--- a/lib_generic/vsprintf.c
+++ b/lib_generic/vsprintf.c
@@ -55,6 +55,29 @@ long simple_strtol(const char *cp,char **endp,unsigned int base)
return simple_strtoul(cp,endp,base);
}
+int ustrtoul(const char *cp, char **endp, unsigned int base)
+{
+ unsigned long result = simple_strtoul(cp, endp, base);
+ switch (**endp) {
+ case 'G' :
+ result *= 1024;
+ /* fall through */
+ case 'M':
+ result *= 1024;
+ /* fall through */
+ case 'K':
+ case 'k':
+ result *= 1024;
+ if ((*endp)[1] == 'i') {
+ if ((*endp)[2] == 'B')
+ (*endp) += 3;
+ else
+ (*endp) += 2;
+ }
+ }
+ return result;
+}
+
#ifdef CONFIG_SYS_64BIT_STRTOUL
unsigned long long simple_strtoull (const char *cp, char **endp, unsigned int base)
{
2
4
Now you can use the UBI at apollon board
Signed-off-by: Kyungmin Park <kyungmin.park(a)samsung.com>
---
diff --git a/board/apollon/Makefile b/board/apollon/Makefile
index 9bac9a6..4c3e57f 100644
--- a/board/apollon/Makefile
+++ b/board/apollon/Makefile
@@ -25,9 +25,11 @@ include $(TOPDIR)/config.mk
LIB = $(obj)lib$(BOARD).a
-COBJS := apollon.o mem.o sys_info.o
+COBJS-y := apollon.o mem.o sys_info.o
+COBJS-$(CONFIG_CMD_UBI) += ubi.o
SOBJS := lowlevel_init.o
+COBJS := $(COBJS-y)
SRCS := $(SOBJS:.o=.S) $(COBJS:.o=.c)
OBJS := $(addprefix $(obj),$(COBJS))
SOBJS := $(addprefix $(obj),$(SOBJS))
diff --git a/board/apollon/ubi.c b/board/apollon/ubi.c
new file mode 100644
index 0000000..10dd6e7
--- /dev/null
+++ b/board/apollon/ubi.c
@@ -0,0 +1,48 @@
+/*
+ * board/apollon/ubi.c
+ *
+ * Copyright (C) 2008 Samsung Electronics
+ * Kyungmin Park <kyungmin.park(a)samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <common.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+#include <jffs2/load_kernel.h>
+#include <ubi_uboot.h>
+
+int ubi_board_scan(void)
+{
+ struct mtd_device *dev;
+ struct part_info *part;
+ struct mtd_partition mtd_part;
+ char buffer[32];
+ u8 pnum;
+ int err;
+
+ if (mtdparts_init() != 0)
+ return 1;
+
+ if (find_dev_and_part("onenand0,4", &dev, &pnum, &part) != 0)
+ return 1;
+
+ sprintf(buffer, "mtd=%d", pnum);
+ mtd_part.name = buffer;
+ mtd_part.size = part->size;
+ mtd_part.offset = part->offset;
+ add_mtd_partitions(&onenand_mtd, &mtd_part, 1);
+
+ err = ubi_mtd_param_parse(buffer, NULL);
+ if (err)
+ return err;
+
+ err = ubi_init();
+ if (err)
+ return err;
+
+ return 0;
+}
diff --git a/include/configs/apollon.h b/include/configs/apollon.h
index d71ed44..c694989 100644
--- a/include/configs/apollon.h
+++ b/include/configs/apollon.h
@@ -53,6 +53,9 @@
#define CONFIG_SYS_USE_NOR 1
#endif
+/* uncommnet if you want to use UBI */
+#define CONFIG_SYS_USE_UBI
+
#include <asm/arch/omap2420.h> /* get chip and board defs */
#define V_SCLK 12000000
@@ -73,8 +76,9 @@
* Size of malloc() pool
*/
#define CONFIG_ENV_SIZE SZ_128K /* Total Size of Environment Sector */
-#define CONFIG_SYS_MALLOC_LEN (CONFIG_ENV_SIZE + SZ_128K)
-#define CONFIG_SYS_GBL_DATA_SIZE 128 /* bytes reserved for initial data */
+#define CONFIG_SYS_MALLOC_LEN (CONFIG_ENV_SIZE + SZ_1M)
+/* bytes reserved for initial data */
+#define CONFIG_SYS_GBL_DATA_SIZE 128
/*
* Hardware drivers
@@ -116,6 +120,12 @@
#define CONFIG_CMD_DIAG
#define CONFIG_CMD_ONENAND
+#ifdef CONFIG_SYS_USE_UBI
+#define CONFIG_CMD_JFFS2
+#define CONFIG_CMD_UBI
+#define CONFIG_RBTREE
+#endif
+
#undef CONFIG_CMD_AUTOSCRIPT
#ifndef CONFIG_SYS_USE_NOR
@@ -133,24 +143,39 @@
#define CONFIG_BOOTFILE "uImage"
#define CONFIG_ETHADDR 00:0E:99:00:24:20
-#ifdef CONFIG_APOLLON_PLUS
-# define CONFIG_BOOTARGS "root=/dev/nfs rw mem=64M console=ttyS0,115200n8 ip=192.168.116.25:192.168.116.1:192.168.116.1:255.255.255.0:apollon:eth0:off nfsroot=/tftpboot/nfsroot profile=2"
+#ifdef CONFIG_APOLLON_PLUS
+#define CONFIG_SYS_MEM "mem=64M"
+#else
+#define CONFIG_SYS_MEM "mem=128"
+#endif
+
+#ifdef CONFIG_SYS_USE_UBI
+#define CONFIG_SYS_UBI "ubi.mtd=4"
#else
-# define CONFIG_BOOTARGS "root=/dev/nfs rw mem=128M console=ttyS0,115200n8 ip=192.168.116.25:192.168.116.1:192.168.116.1:255.255.255.0:apollon:eth0:off nfsroot=/tftpboot/nfsroot profile=2"
+#define CONFIG_SYS_UBI ""
#endif
+#define CONFIG_BOOTARGS "root=/dev/nfs rw " CONFIG_SYS_MEM \
+ " console=ttyS0,115200n8" \
+ " ip=192.168.116.25:192.168.116.1:192.168.116.1:255.255.255.0:" \
+ "apollon:eth0:off nfsroot=/tftpboot/nfsroot profile=2 " \
+ CONFIG_SYS_UBI
+
#define CONFIG_EXTRA_ENV_SETTINGS \
"Image=tftp 0x80008000 Image; go 0x80008000\0" \
"zImage=tftp 0x80180000 zImage; go 0x80180000\0" \
"uImage=tftp 0x80180000 uImage; bootm 0x80180000\0" \
"uboot=tftp 0x80008000 u-boot.bin; go 0x80008000\0" \
- "xloader=tftp 0x80180000 x-load.bin; cp.w 0x80180000 0x00000400 0x1000; go 0x00000400\0" \
+ "xloader=tftp 0x80180000 x-load.bin; " \
+ " cp.w 0x80180000 0x00000400 0x1000; go 0x00000400\0" \
"syncmode50=mw.w 0x1e442 0xc0c4; mw 0x6800a060 0xe30d1201\0" \
"syncmode=mw.w 0x1e442 0xe0f4; mw 0x6800a060 0xe30d1201\0" \
"norboot=cp32 0x18040000 0x80008000 0x200000; go 0x80008000\0" \
- "oneboot=onenand read 0x80008000 0x40000 0x200000; go 0x80008000\0"\
+ "oneboot=onenand read 0x80008000 0x40000 0x200000; go 0x80008000\0" \
"onesyncboot=run syncmode oneboot\0" \
- "updateb=tftp 0x80180000 u-boot-onenand.bin; onenand erase 0x0 0x20000; onenand write 0x80180000 0x0 0x20000\0" \
+ "updateb=tftp 0x80180000 u-boot-onenand.bin; " \
+ " onenand erase 0x0 0x20000; onenand write 0x80180000 0x0 0x20000\0" \
+ "ubi=setenv bootargs ${bootargs} ubi.mtd=4 ${mtdparts}; run uImage\0" \
"bootcmd=run uboot\0"
/*
@@ -164,14 +189,15 @@
/* Print Buffer Size */
#define CONFIG_SYS_PBSIZE (CONFIG_SYS_CBSIZE+sizeof(CONFIG_SYS_PROMPT)+16)
#define CONFIG_SYS_MAXARGS 16 /* max number of command args */
-#define CONFIG_SYS_BARGSIZE CONFIG_SYS_CBSIZE /* Boot Argument Buffer Size */
-
-#define CONFIG_SYS_MEMTEST_START (OMAP2420_SDRC_CS0) /* memtest works on */
+/* Boot Argument Buffer Size */
+#define CONFIG_SYS_BARGSIZE CONFIG_SYS_CBSIZE
+/* memtest works on */
+#define CONFIG_SYS_MEMTEST_START (OMAP2420_SDRC_CS0)
#define CONFIG_SYS_MEMTEST_END (OMAP2420_SDRC_CS0+SZ_31M)
#undef CONFIG_SYS_CLKS_IN_HZ /* everything, incl board info, in Hz */
-
-#define CONFIG_SYS_LOAD_ADDR (OMAP2420_SDRC_CS0) /* default load address */
+/* default load address */
+#define CONFIG_SYS_LOAD_ADDR (OMAP2420_SDRC_CS0)
/* The 2420 has 12 GP timers, they can be driven by the SysClk (12/13/19.2)
* or by 32KHz clk, or from external sig. This rate is divided by a local
@@ -211,13 +237,15 @@
# define CONFIG_SYS_MAX_FLASH_BANKS 1
# define CONFIG_SYS_MAX_FLASH_SECT 1024
/*-----------------------------------------------------------------------
-
* CFI FLASH driver setup
*/
-# define CONFIG_SYS_FLASH_CFI 1 /* Flash memory is CFI compliant */
+/* Flash memory is CFI compliant */
+# define CONFIG_SYS_FLASH_CFI 1
# define CONFIG_FLASH_CFI_DRIVER 1 /* Use drivers/cfi_flash.c */
-/* #define CONFIG_SYS_FLASH_USE_BUFFER_WRITE 1 */ /* Use buffered writes (~10x faster) */
-# define CONFIG_SYS_FLASH_PROTECTION 1 /* Use h/w sector protection*/
+/* Use buffered writes (~10x faster) */
+/* #define CONFIG_SYS_FLASH_USE_BUFFER_WRITE 1 */
+/* Use h/w sector protection*/
+# define CONFIG_SYS_FLASH_PROTECTION 1
#else /* !CONFIG_SYS_USE_NOR */
# define CONFIG_SYS_NO_FLASH 1
@@ -228,4 +256,15 @@
#define CONFIG_ENV_IS_IN_ONENAND 1
#define CONFIG_ENV_ADDR 0x00020000
+#ifdef CONFIG_SYS_USE_UBI
+#define CONFIG_JFFS2_CMDLINE
+#define MTDIDS_DEFAULT "onenand0=onenand"
+#define MTDPARTS_DEFAULT "mtdparts=onenand:128k(bootloader)," \
+ "128k(params)," \
+ "2m(kernel)," \
+ "16m(rootfs)," \
+ "32m(fs)," \
+ "-(ubifs)"
+#endif
+
#endif /* __CONFIG_H */
2
3

02 Nov '08
Related to the discussion we had earlier today about linking to libgcc I
have here a experimental patch that removes the linking to libgcc.
I called it experimental because I am not able to test if all architectures
still compile properly, and as such it needs more (build)testing before
integrating. I tested several ARM boards, and they all compile and link fine.
I am convinced that it is the way to go to remove libgcc of the link list,
but if we integrate this patch I cannot guarantee it does not break some builds,
but it makes clear which boards are depending on interfaces from a library it
should not depend on in the first place. Those boards might need additional
patches.
I hope you can find some time to do some more compile testing across different
architectures.
Kind Regards,
Remy Böhmer
4
8

02 Nov '08
UBI (Latin: "where?") stands for "Unsorted Block Images". It is a volume management system for flash devices which manages multiple logical volumes on a single physical flash device and spreads the I/O load (i.e, wear-leveling) across the whole flash chip.
In a sense, UBI may be compared to the Logical Volume Manager (LVM). Whereas LVM maps logical sectors to physical sectors, UBI maps logical eraseblocks to physical eraseblocks. But besides the mapping, UBI implements global wear-leveling and I/O errors handling.
For more details, Please visit the following URL.
http://www.linux-mtd.infradead.org/doc/ubi.html
Signed-off-by: Kyungmin Park <kyungmin.park(a)samsung.com>
---
diff --git a/Makefile b/Makefile
index 9a132f7..93759e4 100644
--- a/Makefile
+++ b/Makefile
@@ -230,6 +230,7 @@ LIBS += drivers/mtd/libmtd.a
LIBS += drivers/mtd/nand/libnand.a
LIBS += drivers/mtd/nand_legacy/libnand_legacy.a
LIBS += drivers/mtd/onenand/libonenand.a
+LIBS += drivers/mtd/ubi/libubi.a
LIBS += drivers/mtd/spi/libspi_flash.a
LIBS += drivers/net/libnet.a
LIBS += drivers/net/phy/libphy.a
diff --git a/drivers/mtd/ubi/wl.c b/drivers/mtd/ubi/wl.c
new file mode 100644
index 0000000..f5224bd
--- /dev/null
+++ b/drivers/mtd/ubi/wl.c
@@ -0,0 +1,1670 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Authors: Artem Bityutskiy (Битюцкий Артём), Thomas Gleixner
+ */
+
+/*
+ * UBI wear-leveling unit.
+ *
+ * This unit is responsible for wear-leveling. It works in terms of physical
+ * eraseblocks and erase counters and knows nothing about logical eraseblocks,
+ * volumes, etc. From this unit's perspective all physical eraseblocks are of
+ * two types - used and free. Used physical eraseblocks are those that were
+ * "get" by the 'ubi_wl_get_peb()' function, and free physical eraseblocks are
+ * those that were put by the 'ubi_wl_put_peb()' function.
+ *
+ * Physical eraseblocks returned by 'ubi_wl_get_peb()' have only erase counter
+ * header. The rest of the physical eraseblock contains only 0xFF bytes.
+ *
+ * When physical eraseblocks are returned to the WL unit by means of the
+ * 'ubi_wl_put_peb()' function, they are scheduled for erasure. The erasure is
+ * done asynchronously in context of the per-UBI device background thread,
+ * which is also managed by the WL unit.
+ *
+ * The wear-leveling is ensured by means of moving the contents of used
+ * physical eraseblocks with low erase counter to free physical eraseblocks
+ * with high erase counter.
+ *
+ * The 'ubi_wl_get_peb()' function accepts data type hints which help to pick
+ * an "optimal" physical eraseblock. For example, when it is known that the
+ * physical eraseblock will be "put" soon because it contains short-term data,
+ * the WL unit may pick a free physical eraseblock with low erase counter, and
+ * so forth.
+ *
+ * If the WL unit fails to erase a physical eraseblock, it marks it as bad.
+ *
+ * This unit is also responsible for scrubbing. If a bit-flip is detected in a
+ * physical eraseblock, it has to be moved. Technically this is the same as
+ * moving it for wear-leveling reasons.
+ *
+ * As it was said, for the UBI unit all physical eraseblocks are either "free"
+ * or "used". Free eraseblock are kept in the @wl->free RB-tree, while used
+ * eraseblocks are kept in a set of different RB-trees: @wl->used,
+ * @wl->prot.pnum, @wl->prot.aec, and @wl->scrub.
+ *
+ * Note, in this implementation, we keep a small in-RAM object for each physical
+ * eraseblock. This is surely not a scalable solution. But it appears to be good
+ * enough for moderately large flashes and it is simple. In future, one may
+ * re-work this unit and make it more scalable.
+ *
+ * At the moment this unit does not utilize the sequence number, which was
+ * introduced relatively recently. But it would be wise to do this because the
+ * sequence number of a logical eraseblock characterizes how old is it. For
+ * example, when we move a PEB with low erase counter, and we need to pick the
+ * target PEB, we pick a PEB with the highest EC if our PEB is "old" and we
+ * pick target PEB with an average EC if our PEB is not very "old". This is a
+ * room for future re-works of the WL unit.
+ *
+ * FIXME: looks too complex, should be simplified (later).
+ */
+
+#ifdef UBI_LINUX
+#include <linux/slab.h>
+#include <linux/crc32.h>
+#include <linux/freezer.h>
+#include <linux/kthread.h>
+#endif
+
+#include <ubi_uboot.h>
+#include "ubi.h"
+
+/* Number of physical eraseblocks reserved for wear-leveling purposes */
+#define WL_RESERVED_PEBS 1
+
+/*
+ * How many erase cycles are short term, unknown, and long term physical
+ * eraseblocks protected.
+ */
+#define ST_PROTECTION 16
+#define U_PROTECTION 10
+#define LT_PROTECTION 4
+
+/*
+ * Maximum difference between two erase counters. If this threshold is
+ * exceeded, the WL unit starts moving data from used physical eraseblocks with
+ * low erase counter to free physical eraseblocks with high erase counter.
+ */
+#define UBI_WL_THRESHOLD CONFIG_MTD_UBI_WL_THRESHOLD
+
+/*
+ * When a physical eraseblock is moved, the WL unit has to pick the target
+ * physical eraseblock to move to. The simplest way would be just to pick the
+ * one with the highest erase counter. But in certain workloads this could lead
+ * to an unlimited wear of one or few physical eraseblock. Indeed, imagine a
+ * situation when the picked physical eraseblock is constantly erased after the
+ * data is written to it. So, we have a constant which limits the highest erase
+ * counter of the free physical eraseblock to pick. Namely, the WL unit does
+ * not pick eraseblocks with erase counter greater then the lowest erase
+ * counter plus %WL_FREE_MAX_DIFF.
+ */
+#define WL_FREE_MAX_DIFF (2*UBI_WL_THRESHOLD)
+
+/*
+ * Maximum number of consecutive background thread failures which is enough to
+ * switch to read-only mode.
+ */
+#define WL_MAX_FAILURES 32
+
+/**
+ * struct ubi_wl_prot_entry - PEB protection entry.
+ * @rb_pnum: link in the @wl->prot.pnum RB-tree
+ * @rb_aec: link in the @wl->prot.aec RB-tree
+ * @abs_ec: the absolute erase counter value when the protection ends
+ * @e: the wear-leveling entry of the physical eraseblock under protection
+ *
+ * When the WL unit returns a physical eraseblock, the physical eraseblock is
+ * protected from being moved for some "time". For this reason, the physical
+ * eraseblock is not directly moved from the @wl->free tree to the @wl->used
+ * tree. There is one more tree in between where this physical eraseblock is
+ * temporarily stored (@wl->prot).
+ *
+ * All this protection stuff is needed because:
+ * o we don't want to move physical eraseblocks just after we have given them
+ * to the user; instead, we first want to let users fill them up with data;
+ *
+ * o there is a chance that the user will put the physical eraseblock very
+ * soon, so it makes sense not to move it for some time, but wait; this is
+ * especially important in case of "short term" physical eraseblocks.
+ *
+ * Physical eraseblocks stay protected only for limited time. But the "time" is
+ * measured in erase cycles in this case. This is implemented with help of the
+ * absolute erase counter (@wl->abs_ec). When it reaches certain value, the
+ * physical eraseblocks are moved from the protection trees (@wl->prot.*) to
+ * the @wl->used tree.
+ *
+ * Protected physical eraseblocks are searched by physical eraseblock number
+ * (when they are put) and by the absolute erase counter (to check if it is
+ * time to move them to the @wl->used tree). So there are actually 2 RB-trees
+ * storing the protected physical eraseblocks: @wl->prot.pnum and
+ * @wl->prot.aec. They are referred to as the "protection" trees. The
+ * first one is indexed by the physical eraseblock number. The second one is
+ * indexed by the absolute erase counter. Both trees store
+ * &struct ubi_wl_prot_entry objects.
+ *
+ * Each physical eraseblock has 2 main states: free and used. The former state
+ * corresponds to the @wl->free tree. The latter state is split up on several
+ * sub-states:
+ * o the WL movement is allowed (@wl->used tree);
+ * o the WL movement is temporarily prohibited (@wl->prot.pnum and
+ * @wl->prot.aec trees);
+ * o scrubbing is needed (@wl->scrub tree).
+ *
+ * Depending on the sub-state, wear-leveling entries of the used physical
+ * eraseblocks may be kept in one of those trees.
+ */
+struct ubi_wl_prot_entry {
+ struct rb_node rb_pnum;
+ struct rb_node rb_aec;
+ unsigned long long abs_ec;
+ struct ubi_wl_entry *e;
+};
+
+/**
+ * struct ubi_work - UBI work description data structure.
+ * @list: a link in the list of pending works
+ * @func: worker function
+ * @priv: private data of the worker function
+ *
+ * @e: physical eraseblock to erase
+ * @torture: if the physical eraseblock has to be tortured
+ *
+ * The @func pointer points to the worker function. If the @cancel argument is
+ * not zero, the worker has to free the resources and exit immediately. The
+ * worker has to return zero in case of success and a negative error code in
+ * case of failure.
+ */
+struct ubi_work {
+ struct list_head list;
+ int (*func)(struct ubi_device *ubi, struct ubi_work *wrk, int cancel);
+ /* The below fields are only relevant to erasure works */
+ struct ubi_wl_entry *e;
+ int torture;
+};
+
+#ifdef CONFIG_MTD_UBI_DEBUG_PARANOID
+static int paranoid_check_ec(struct ubi_device *ubi, int pnum, int ec);
+static int paranoid_check_in_wl_tree(struct ubi_wl_entry *e,
+ struct rb_root *root);
+#else
+#define paranoid_check_ec(ubi, pnum, ec) 0
+#define paranoid_check_in_wl_tree(e, root)
+#endif
+
+/**
+ * wl_tree_add - add a wear-leveling entry to a WL RB-tree.
+ * @e: the wear-leveling entry to add
+ * @root: the root of the tree
+ *
+ * Note, we use (erase counter, physical eraseblock number) pairs as keys in
+ * the @ubi->used and @ubi->free RB-trees.
+ */
+static void wl_tree_add(struct ubi_wl_entry *e, struct rb_root *root)
+{
+ struct rb_node **p, *parent = NULL;
+
+ p = &root->rb_node;
+ while (*p) {
+ struct ubi_wl_entry *e1;
+
+ parent = *p;
+ e1 = rb_entry(parent, struct ubi_wl_entry, rb);
+
+ if (e->ec < e1->ec)
+ p = &(*p)->rb_left;
+ else if (e->ec > e1->ec)
+ p = &(*p)->rb_right;
+ else {
+ ubi_assert(e->pnum != e1->pnum);
+ if (e->pnum < e1->pnum)
+ p = &(*p)->rb_left;
+ else
+ p = &(*p)->rb_right;
+ }
+ }
+
+ rb_link_node(&e->rb, parent, p);
+ rb_insert_color(&e->rb, root);
+}
+
+/**
+ * do_work - do one pending work.
+ * @ubi: UBI device description object
+ *
+ * This function returns zero in case of success and a negative error code in
+ * case of failure.
+ */
+static int do_work(struct ubi_device *ubi)
+{
+ int err;
+ struct ubi_work *wrk;
+
+ cond_resched();
+
+ /*
+ * @ubi->work_sem is used to synchronize with the workers. Workers take
+ * it in read mode, so many of them may be doing works at a time. But
+ * the queue flush code has to be sure the whole queue of works is
+ * done, and it takes the mutex in write mode.
+ */
+ down_read(&ubi->work_sem);
+ spin_lock(&ubi->wl_lock);
+ if (list_empty(&ubi->works)) {
+ spin_unlock(&ubi->wl_lock);
+ up_read(&ubi->work_sem);
+ return 0;
+ }
+
+ wrk = list_entry(ubi->works.next, struct ubi_work, list);
+ list_del(&wrk->list);
+ ubi->works_count -= 1;
+ ubi_assert(ubi->works_count >= 0);
+ spin_unlock(&ubi->wl_lock);
+
+ /*
+ * Call the worker function. Do not touch the work structure
+ * after this call as it will have been freed or reused by that
+ * time by the worker function.
+ */
+ err = wrk->func(ubi, wrk, 0);
+ if (err)
+ ubi_err("work failed with error code %d", err);
+ up_read(&ubi->work_sem);
+
+ return err;
+}
+
+/**
+ * produce_free_peb - produce a free physical eraseblock.
+ * @ubi: UBI device description object
+ *
+ * This function tries to make a free PEB by means of synchronous execution of
+ * pending works. This may be needed if, for example the background thread is
+ * disabled. Returns zero in case of success and a negative error code in case
+ * of failure.
+ */
+static int produce_free_peb(struct ubi_device *ubi)
+{
+ int err;
+
+ spin_lock(&ubi->wl_lock);
+ while (!ubi->free.rb_node) {
+ spin_unlock(&ubi->wl_lock);
+
+ dbg_wl("do one work synchronously");
+ err = do_work(ubi);
+ if (err)
+ return err;
+
+ spin_lock(&ubi->wl_lock);
+ }
+ spin_unlock(&ubi->wl_lock);
+
+ return 0;
+}
+
+/**
+ * in_wl_tree - check if wear-leveling entry is present in a WL RB-tree.
+ * @e: the wear-leveling entry to check
+ * @root: the root of the tree
+ *
+ * This function returns non-zero if @e is in the @root RB-tree and zero if it
+ * is not.
+ */
+static int in_wl_tree(struct ubi_wl_entry *e, struct rb_root *root)
+{
+ struct rb_node *p;
+
+ p = root->rb_node;
+ while (p) {
+ struct ubi_wl_entry *e1;
+
+ e1 = rb_entry(p, struct ubi_wl_entry, rb);
+
+ if (e->pnum == e1->pnum) {
+ ubi_assert(e == e1);
+ return 1;
+ }
+
+ if (e->ec < e1->ec)
+ p = p->rb_left;
+ else if (e->ec > e1->ec)
+ p = p->rb_right;
+ else {
+ ubi_assert(e->pnum != e1->pnum);
+ if (e->pnum < e1->pnum)
+ p = p->rb_left;
+ else
+ p = p->rb_right;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * prot_tree_add - add physical eraseblock to protection trees.
+ * @ubi: UBI device description object
+ * @e: the physical eraseblock to add
+ * @pe: protection entry object to use
+ * @abs_ec: absolute erase counter value when this physical eraseblock has
+ * to be removed from the protection trees.
+ *
+ * @wl->lock has to be locked.
+ */
+static void prot_tree_add(struct ubi_device *ubi, struct ubi_wl_entry *e,
+ struct ubi_wl_prot_entry *pe, int abs_ec)
+{
+ struct rb_node **p, *parent = NULL;
+ struct ubi_wl_prot_entry *pe1;
+
+ pe->e = e;
+ pe->abs_ec = ubi->abs_ec + abs_ec;
+
+ p = &ubi->prot.pnum.rb_node;
+ while (*p) {
+ parent = *p;
+ pe1 = rb_entry(parent, struct ubi_wl_prot_entry, rb_pnum);
+
+ if (e->pnum < pe1->e->pnum)
+ p = &(*p)->rb_left;
+ else
+ p = &(*p)->rb_right;
+ }
+ rb_link_node(&pe->rb_pnum, parent, p);
+ rb_insert_color(&pe->rb_pnum, &ubi->prot.pnum);
+
+ p = &ubi->prot.aec.rb_node;
+ parent = NULL;
+ while (*p) {
+ parent = *p;
+ pe1 = rb_entry(parent, struct ubi_wl_prot_entry, rb_aec);
+
+ if (pe->abs_ec < pe1->abs_ec)
+ p = &(*p)->rb_left;
+ else
+ p = &(*p)->rb_right;
+ }
+ rb_link_node(&pe->rb_aec, parent, p);
+ rb_insert_color(&pe->rb_aec, &ubi->prot.aec);
+}
+
+/**
+ * find_wl_entry - find wear-leveling entry closest to certain erase counter.
+ * @root: the RB-tree where to look for
+ * @max: highest possible erase counter
+ *
+ * This function looks for a wear leveling entry with erase counter closest to
+ * @max and less then @max.
+ */
+static struct ubi_wl_entry *find_wl_entry(struct rb_root *root, int max)
+{
+ struct rb_node *p;
+ struct ubi_wl_entry *e;
+
+ e = rb_entry(rb_first(root), struct ubi_wl_entry, rb);
+ max += e->ec;
+
+ p = root->rb_node;
+ while (p) {
+ struct ubi_wl_entry *e1;
+
+ e1 = rb_entry(p, struct ubi_wl_entry, rb);
+ if (e1->ec >= max)
+ p = p->rb_left;
+ else {
+ p = p->rb_right;
+ e = e1;
+ }
+ }
+
+ return e;
+}
+
+/**
+ * ubi_wl_get_peb - get a physical eraseblock.
+ * @ubi: UBI device description object
+ * @dtype: type of data which will be stored in this physical eraseblock
+ *
+ * This function returns a physical eraseblock in case of success and a
+ * negative error code in case of failure. Might sleep.
+ */
+int ubi_wl_get_peb(struct ubi_device *ubi, int dtype)
+{
+ int err, protect, medium_ec;
+ struct ubi_wl_entry *e, *first, *last;
+ struct ubi_wl_prot_entry *pe;
+
+ ubi_assert(dtype == UBI_LONGTERM || dtype == UBI_SHORTTERM ||
+ dtype == UBI_UNKNOWN);
+
+ pe = kmalloc(sizeof(struct ubi_wl_prot_entry), GFP_NOFS);
+ if (!pe)
+ return -ENOMEM;
+
+retry:
+ spin_lock(&ubi->wl_lock);
+ if (!ubi->free.rb_node) {
+ if (ubi->works_count == 0) {
+ ubi_assert(list_empty(&ubi->works));
+ ubi_err("no free eraseblocks");
+ spin_unlock(&ubi->wl_lock);
+ kfree(pe);
+ return -ENOSPC;
+ }
+ spin_unlock(&ubi->wl_lock);
+
+ err = produce_free_peb(ubi);
+ if (err < 0) {
+ kfree(pe);
+ return err;
+ }
+ goto retry;
+ }
+
+ switch (dtype) {
+ case UBI_LONGTERM:
+ /*
+ * For long term data we pick a physical eraseblock
+ * with high erase counter. But the highest erase
+ * counter we can pick is bounded by the the lowest
+ * erase counter plus %WL_FREE_MAX_DIFF.
+ */
+ e = find_wl_entry(&ubi->free, WL_FREE_MAX_DIFF);
+ protect = LT_PROTECTION;
+ break;
+ case UBI_UNKNOWN:
+ /*
+ * For unknown data we pick a physical eraseblock with
+ * medium erase counter. But we by no means can pick a
+ * physical eraseblock with erase counter greater or
+ * equivalent than the lowest erase counter plus
+ * %WL_FREE_MAX_DIFF.
+ */
+ first = rb_entry(rb_first(&ubi->free),
+ struct ubi_wl_entry, rb);
+ last = rb_entry(rb_last(&ubi->free),
+ struct ubi_wl_entry, rb);
+
+ if (last->ec - first->ec < WL_FREE_MAX_DIFF)
+ e = rb_entry(ubi->free.rb_node,
+ struct ubi_wl_entry, rb);
+ else {
+ medium_ec = (first->ec + WL_FREE_MAX_DIFF)/2;
+ e = find_wl_entry(&ubi->free, medium_ec);
+ }
+ protect = U_PROTECTION;
+ break;
+ case UBI_SHORTTERM:
+ /*
+ * For short term data we pick a physical eraseblock
+ * with the lowest erase counter as we expect it will
+ * be erased soon.
+ */
+ e = rb_entry(rb_first(&ubi->free),
+ struct ubi_wl_entry, rb);
+ protect = ST_PROTECTION;
+ break;
+ default:
+ protect = 0;
+ e = NULL;
+ BUG();
+ }
+
+ /*
+ * Move the physical eraseblock to the protection trees where it will
+ * be protected from being moved for some time.
+ */
+ paranoid_check_in_wl_tree(e, &ubi->free);
+ rb_erase(&e->rb, &ubi->free);
+ prot_tree_add(ubi, e, pe, protect);
+
+ dbg_wl("PEB %d EC %d, protection %d", e->pnum, e->ec, protect);
+ spin_unlock(&ubi->wl_lock);
+
+ return e->pnum;
+}
+
+/**
+ * prot_tree_del - remove a physical eraseblock from the protection trees
+ * @ubi: UBI device description object
+ * @pnum: the physical eraseblock to remove
+ *
+ * This function returns PEB @pnum from the protection trees and returns zero
+ * in case of success and %-ENODEV if the PEB was not found in the protection
+ * trees.
+ */
+static int prot_tree_del(struct ubi_device *ubi, int pnum)
+{
+ struct rb_node *p;
+ struct ubi_wl_prot_entry *pe = NULL;
+
+ p = ubi->prot.pnum.rb_node;
+ while (p) {
+
+ pe = rb_entry(p, struct ubi_wl_prot_entry, rb_pnum);
+
+ if (pnum == pe->e->pnum)
+ goto found;
+
+ if (pnum < pe->e->pnum)
+ p = p->rb_left;
+ else
+ p = p->rb_right;
+ }
+
+ return -ENODEV;
+
+found:
+ ubi_assert(pe->e->pnum == pnum);
+ rb_erase(&pe->rb_aec, &ubi->prot.aec);
+ rb_erase(&pe->rb_pnum, &ubi->prot.pnum);
+ kfree(pe);
+ return 0;
+}
+
+/**
+ * sync_erase - synchronously erase a physical eraseblock.
+ * @ubi: UBI device description object
+ * @e: the the physical eraseblock to erase
+ * @torture: if the physical eraseblock has to be tortured
+ *
+ * This function returns zero in case of success and a negative error code in
+ * case of failure.
+ */
+static int sync_erase(struct ubi_device *ubi, struct ubi_wl_entry *e, int torture)
+{
+ int err;
+ struct ubi_ec_hdr *ec_hdr;
+ unsigned long long ec = e->ec;
+
+ dbg_wl("erase PEB %d, old EC %llu", e->pnum, ec);
+
+ err = paranoid_check_ec(ubi, e->pnum, e->ec);
+ if (err > 0)
+ return -EINVAL;
+
+ ec_hdr = kzalloc(ubi->ec_hdr_alsize, GFP_NOFS);
+ if (!ec_hdr)
+ return -ENOMEM;
+
+ err = ubi_io_sync_erase(ubi, e->pnum, torture);
+ if (err < 0)
+ goto out_free;
+
+ ec += err;
+ if (ec > UBI_MAX_ERASECOUNTER) {
+ /*
+ * Erase counter overflow. Upgrade UBI and use 64-bit
+ * erase counters internally.
+ */
+ ubi_err("erase counter overflow at PEB %d, EC %llu",
+ e->pnum, ec);
+ err = -EINVAL;
+ goto out_free;
+ }
+
+ dbg_wl("erased PEB %d, new EC %llu", e->pnum, ec);
+
+ ec_hdr->ec = cpu_to_be64(ec);
+
+ err = ubi_io_write_ec_hdr(ubi, e->pnum, ec_hdr);
+ if (err)
+ goto out_free;
+
+ e->ec = ec;
+ spin_lock(&ubi->wl_lock);
+ if (e->ec > ubi->max_ec)
+ ubi->max_ec = e->ec;
+ spin_unlock(&ubi->wl_lock);
+
+out_free:
+ kfree(ec_hdr);
+ return err;
+}
+
+/**
+ * check_protection_over - check if it is time to stop protecting some
+ * physical eraseblocks.
+ * @ubi: UBI device description object
+ *
+ * This function is called after each erase operation, when the absolute erase
+ * counter is incremented, to check if some physical eraseblock have not to be
+ * protected any longer. These physical eraseblocks are moved from the
+ * protection trees to the used tree.
+ */
+static void check_protection_over(struct ubi_device *ubi)
+{
+ struct ubi_wl_prot_entry *pe;
+
+ /*
+ * There may be several protected physical eraseblock to remove,
+ * process them all.
+ */
+ while (1) {
+ spin_lock(&ubi->wl_lock);
+ if (!ubi->prot.aec.rb_node) {
+ spin_unlock(&ubi->wl_lock);
+ break;
+ }
+
+ pe = rb_entry(rb_first(&ubi->prot.aec),
+ struct ubi_wl_prot_entry, rb_aec);
+
+ if (pe->abs_ec > ubi->abs_ec) {
+ spin_unlock(&ubi->wl_lock);
+ break;
+ }
+
+ dbg_wl("PEB %d protection over, abs_ec %llu, PEB abs_ec %llu",
+ pe->e->pnum, ubi->abs_ec, pe->abs_ec);
+ rb_erase(&pe->rb_aec, &ubi->prot.aec);
+ rb_erase(&pe->rb_pnum, &ubi->prot.pnum);
+ wl_tree_add(pe->e, &ubi->used);
+ spin_unlock(&ubi->wl_lock);
+
+ kfree(pe);
+ cond_resched();
+ }
+}
+
+/**
+ * schedule_ubi_work - schedule a work.
+ * @ubi: UBI device description object
+ * @wrk: the work to schedule
+ *
+ * This function enqueues a work defined by @wrk to the tail of the pending
+ * works list.
+ */
+static void schedule_ubi_work(struct ubi_device *ubi, struct ubi_work *wrk)
+{
+ spin_lock(&ubi->wl_lock);
+ list_add_tail(&wrk->list, &ubi->works);
+ ubi_assert(ubi->works_count >= 0);
+ ubi->works_count += 1;
+ if (ubi->thread_enabled)
+ wake_up_process(ubi->bgt_thread);
+ spin_unlock(&ubi->wl_lock);
+}
+
+static int erase_worker(struct ubi_device *ubi, struct ubi_work *wl_wrk,
+ int cancel);
+
+/**
+ * schedule_erase - schedule an erase work.
+ * @ubi: UBI device description object
+ * @e: the WL entry of the physical eraseblock to erase
+ * @torture: if the physical eraseblock has to be tortured
+ *
+ * This function returns zero in case of success and a %-ENOMEM in case of
+ * failure.
+ */
+static int schedule_erase(struct ubi_device *ubi, struct ubi_wl_entry *e,
+ int torture)
+{
+ struct ubi_work *wl_wrk;
+
+ dbg_wl("schedule erasure of PEB %d, EC %d, torture %d",
+ e->pnum, e->ec, torture);
+
+ wl_wrk = kmalloc(sizeof(struct ubi_work), GFP_NOFS);
+ if (!wl_wrk)
+ return -ENOMEM;
+
+ wl_wrk->func = &erase_worker;
+ wl_wrk->e = e;
+ wl_wrk->torture = torture;
+
+ schedule_ubi_work(ubi, wl_wrk);
+ return 0;
+}
+
+/**
+ * wear_leveling_worker - wear-leveling worker function.
+ * @ubi: UBI device description object
+ * @wrk: the work object
+ * @cancel: non-zero if the worker has to free memory and exit
+ *
+ * This function copies a more worn out physical eraseblock to a less worn out
+ * one. Returns zero in case of success and a negative error code in case of
+ * failure.
+ */
+static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk,
+ int cancel)
+{
+ int err, put = 0, scrubbing = 0, protect = 0;
+ struct ubi_wl_prot_entry *uninitialized_var(pe);
+ struct ubi_wl_entry *e1, *e2;
+ struct ubi_vid_hdr *vid_hdr;
+
+ kfree(wrk);
+
+ if (cancel)
+ return 0;
+
+ vid_hdr = ubi_zalloc_vid_hdr(ubi, GFP_NOFS);
+ if (!vid_hdr)
+ return -ENOMEM;
+
+ mutex_lock(&ubi->move_mutex);
+ spin_lock(&ubi->wl_lock);
+ ubi_assert(!ubi->move_from && !ubi->move_to);
+ ubi_assert(!ubi->move_to_put);
+
+ if (!ubi->free.rb_node ||
+ (!ubi->used.rb_node && !ubi->scrub.rb_node)) {
+ /*
+ * No free physical eraseblocks? Well, they must be waiting in
+ * the queue to be erased. Cancel movement - it will be
+ * triggered again when a free physical eraseblock appears.
+ *
+ * No used physical eraseblocks? They must be temporarily
+ * protected from being moved. They will be moved to the
+ * @ubi->used tree later and the wear-leveling will be
+ * triggered again.
+ */
+ dbg_wl("cancel WL, a list is empty: free %d, used %d",
+ !ubi->free.rb_node, !ubi->used.rb_node);
+ goto out_cancel;
+ }
+
+ if (!ubi->scrub.rb_node) {
+ /*
+ * Now pick the least worn-out used physical eraseblock and a
+ * highly worn-out free physical eraseblock. If the erase
+ * counters differ much enough, start wear-leveling.
+ */
+ e1 = rb_entry(rb_first(&ubi->used), struct ubi_wl_entry, rb);
+ e2 = find_wl_entry(&ubi->free, WL_FREE_MAX_DIFF);
+
+ if (!(e2->ec - e1->ec >= UBI_WL_THRESHOLD)) {
+ dbg_wl("no WL needed: min used EC %d, max free EC %d",
+ e1->ec, e2->ec);
+ goto out_cancel;
+ }
+ paranoid_check_in_wl_tree(e1, &ubi->used);
+ rb_erase(&e1->rb, &ubi->used);
+ dbg_wl("move PEB %d EC %d to PEB %d EC %d",
+ e1->pnum, e1->ec, e2->pnum, e2->ec);
+ } else {
+ /* Perform scrubbing */
+ scrubbing = 1;
+ e1 = rb_entry(rb_first(&ubi->scrub), struct ubi_wl_entry, rb);
+ e2 = find_wl_entry(&ubi->free, WL_FREE_MAX_DIFF);
+ paranoid_check_in_wl_tree(e1, &ubi->scrub);
+ rb_erase(&e1->rb, &ubi->scrub);
+ dbg_wl("scrub PEB %d to PEB %d", e1->pnum, e2->pnum);
+ }
+
+ paranoid_check_in_wl_tree(e2, &ubi->free);
+ rb_erase(&e2->rb, &ubi->free);
+ ubi->move_from = e1;
+ ubi->move_to = e2;
+ spin_unlock(&ubi->wl_lock);
+
+ /*
+ * Now we are going to copy physical eraseblock @e1->pnum to @e2->pnum.
+ * We so far do not know which logical eraseblock our physical
+ * eraseblock (@e1) belongs to. We have to read the volume identifier
+ * header first.
+ *
+ * Note, we are protected from this PEB being unmapped and erased. The
+ * 'ubi_wl_put_peb()' would wait for moving to be finished if the PEB
+ * which is being moved was unmapped.
+ */
+
+ err = ubi_io_read_vid_hdr(ubi, e1->pnum, vid_hdr, 0);
+ if (err && err != UBI_IO_BITFLIPS) {
+ if (err == UBI_IO_PEB_FREE) {
+ /*
+ * We are trying to move PEB without a VID header. UBI
+ * always write VID headers shortly after the PEB was
+ * given, so we have a situation when it did not have
+ * chance to write it down because it was preempted.
+ * Just re-schedule the work, so that next time it will
+ * likely have the VID header in place.
+ */
+ dbg_wl("PEB %d has no VID header", e1->pnum);
+ goto out_not_moved;
+ }
+
+ ubi_err("error %d while reading VID header from PEB %d",
+ err, e1->pnum);
+ if (err > 0)
+ err = -EIO;
+ goto out_error;
+ }
+
+ err = ubi_eba_copy_leb(ubi, e1->pnum, e2->pnum, vid_hdr);
+ if (err) {
+
+ if (err < 0)
+ goto out_error;
+ if (err == 1)
+ goto out_not_moved;
+
+ /*
+ * For some reason the LEB was not moved - it might be because
+ * the volume is being deleted. We should prevent this PEB from
+ * being selected for wear-levelling movement for some "time",
+ * so put it to the protection tree.
+ */
+
+ dbg_wl("cancelled moving PEB %d", e1->pnum);
+ pe = kmalloc(sizeof(struct ubi_wl_prot_entry), GFP_NOFS);
+ if (!pe) {
+ err = -ENOMEM;
+ goto out_error;
+ }
+
+ protect = 1;
+ }
+
+ ubi_free_vid_hdr(ubi, vid_hdr);
+ spin_lock(&ubi->wl_lock);
+ if (protect)
+ prot_tree_add(ubi, e1, pe, protect);
+ if (!ubi->move_to_put)
+ wl_tree_add(e2, &ubi->used);
+ else
+ put = 1;
+ ubi->move_from = ubi->move_to = NULL;
+ ubi->move_to_put = ubi->wl_scheduled = 0;
+ spin_unlock(&ubi->wl_lock);
+
+ if (put) {
+ /*
+ * Well, the target PEB was put meanwhile, schedule it for
+ * erasure.
+ */
+ dbg_wl("PEB %d was put meanwhile, erase", e2->pnum);
+ err = schedule_erase(ubi, e2, 0);
+ if (err)
+ goto out_error;
+ }
+
+ if (!protect) {
+ err = schedule_erase(ubi, e1, 0);
+ if (err)
+ goto out_error;
+ }
+
+
+ dbg_wl("done");
+ mutex_unlock(&ubi->move_mutex);
+ return 0;
+
+ /*
+ * For some reasons the LEB was not moved, might be an error, might be
+ * something else. @e1 was not changed, so return it back. @e2 might
+ * be changed, schedule it for erasure.
+ */
+out_not_moved:
+ ubi_free_vid_hdr(ubi, vid_hdr);
+ spin_lock(&ubi->wl_lock);
+ if (scrubbing)
+ wl_tree_add(e1, &ubi->scrub);
+ else
+ wl_tree_add(e1, &ubi->used);
+ ubi->move_from = ubi->move_to = NULL;
+ ubi->move_to_put = ubi->wl_scheduled = 0;
+ spin_unlock(&ubi->wl_lock);
+
+ err = schedule_erase(ubi, e2, 0);
+ if (err)
+ goto out_error;
+
+ mutex_unlock(&ubi->move_mutex);
+ return 0;
+
+out_error:
+ ubi_err("error %d while moving PEB %d to PEB %d",
+ err, e1->pnum, e2->pnum);
+
+ ubi_free_vid_hdr(ubi, vid_hdr);
+ spin_lock(&ubi->wl_lock);
+ ubi->move_from = ubi->move_to = NULL;
+ ubi->move_to_put = ubi->wl_scheduled = 0;
+ spin_unlock(&ubi->wl_lock);
+
+ kmem_cache_free(ubi_wl_entry_slab, e1);
+ kmem_cache_free(ubi_wl_entry_slab, e2);
+ ubi_ro_mode(ubi);
+
+ mutex_unlock(&ubi->move_mutex);
+ return err;
+
+out_cancel:
+ ubi->wl_scheduled = 0;
+ spin_unlock(&ubi->wl_lock);
+ mutex_unlock(&ubi->move_mutex);
+ ubi_free_vid_hdr(ubi, vid_hdr);
+ return 0;
+}
+
+/**
+ * ensure_wear_leveling - schedule wear-leveling if it is needed.
+ * @ubi: UBI device description object
+ *
+ * This function checks if it is time to start wear-leveling and schedules it
+ * if yes. This function returns zero in case of success and a negative error
+ * code in case of failure.
+ */
+static int ensure_wear_leveling(struct ubi_device *ubi)
+{
+ int err = 0;
+ struct ubi_wl_entry *e1;
+ struct ubi_wl_entry *e2;
+ struct ubi_work *wrk;
+
+ spin_lock(&ubi->wl_lock);
+ if (ubi->wl_scheduled)
+ /* Wear-leveling is already in the work queue */
+ goto out_unlock;
+
+ /*
+ * If the ubi->scrub tree is not empty, scrubbing is needed, and the
+ * the WL worker has to be scheduled anyway.
+ */
+ if (!ubi->scrub.rb_node) {
+ if (!ubi->used.rb_node || !ubi->free.rb_node)
+ /* No physical eraseblocks - no deal */
+ goto out_unlock;
+
+ /*
+ * We schedule wear-leveling only if the difference between the
+ * lowest erase counter of used physical eraseblocks and a high
+ * erase counter of free physical eraseblocks is greater then
+ * %UBI_WL_THRESHOLD.
+ */
+ e1 = rb_entry(rb_first(&ubi->used), struct ubi_wl_entry, rb);
+ e2 = find_wl_entry(&ubi->free, WL_FREE_MAX_DIFF);
+
+ if (!(e2->ec - e1->ec >= UBI_WL_THRESHOLD))
+ goto out_unlock;
+ dbg_wl("schedule wear-leveling");
+ } else
+ dbg_wl("schedule scrubbing");
+
+ ubi->wl_scheduled = 1;
+ spin_unlock(&ubi->wl_lock);
+
+ wrk = kmalloc(sizeof(struct ubi_work), GFP_NOFS);
+ if (!wrk) {
+ err = -ENOMEM;
+ goto out_cancel;
+ }
+
+ wrk->func = &wear_leveling_worker;
+ schedule_ubi_work(ubi, wrk);
+ return err;
+
+out_cancel:
+ spin_lock(&ubi->wl_lock);
+ ubi->wl_scheduled = 0;
+out_unlock:
+ spin_unlock(&ubi->wl_lock);
+ return err;
+}
+
+/**
+ * erase_worker - physical eraseblock erase worker function.
+ * @ubi: UBI device description object
+ * @wl_wrk: the work object
+ * @cancel: non-zero if the worker has to free memory and exit
+ *
+ * This function erases a physical eraseblock and perform torture testing if
+ * needed. It also takes care about marking the physical eraseblock bad if
+ * needed. Returns zero in case of success and a negative error code in case of
+ * failure.
+ */
+static int erase_worker(struct ubi_device *ubi, struct ubi_work *wl_wrk,
+ int cancel)
+{
+ struct ubi_wl_entry *e = wl_wrk->e;
+ int pnum = e->pnum, err, need;
+
+ if (cancel) {
+ dbg_wl("cancel erasure of PEB %d EC %d", pnum, e->ec);
+ kfree(wl_wrk);
+ kmem_cache_free(ubi_wl_entry_slab, e);
+ return 0;
+ }
+
+ dbg_wl("erase PEB %d EC %d", pnum, e->ec);
+
+ err = sync_erase(ubi, e, wl_wrk->torture);
+ if (!err) {
+ /* Fine, we've erased it successfully */
+ kfree(wl_wrk);
+
+ spin_lock(&ubi->wl_lock);
+ ubi->abs_ec += 1;
+ wl_tree_add(e, &ubi->free);
+ spin_unlock(&ubi->wl_lock);
+
+ /*
+ * One more erase operation has happened, take care about protected
+ * physical eraseblocks.
+ */
+ check_protection_over(ubi);
+
+ /* And take care about wear-leveling */
+ err = ensure_wear_leveling(ubi);
+ return err;
+ }
+
+ ubi_err("failed to erase PEB %d, error %d", pnum, err);
+ kfree(wl_wrk);
+ kmem_cache_free(ubi_wl_entry_slab, e);
+
+ if (err == -EINTR || err == -ENOMEM || err == -EAGAIN ||
+ err == -EBUSY) {
+ int err1;
+
+ /* Re-schedule the LEB for erasure */
+ err1 = schedule_erase(ubi, e, 0);
+ if (err1) {
+ err = err1;
+ goto out_ro;
+ }
+ return err;
+ } else if (err != -EIO) {
+ /*
+ * If this is not %-EIO, we have no idea what to do. Scheduling
+ * this physical eraseblock for erasure again would cause
+ * errors again and again. Well, lets switch to RO mode.
+ */
+ goto out_ro;
+ }
+
+ /* It is %-EIO, the PEB went bad */
+
+ if (!ubi->bad_allowed) {
+ ubi_err("bad physical eraseblock %d detected", pnum);
+ goto out_ro;
+ }
+
+ spin_lock(&ubi->volumes_lock);
+ need = ubi->beb_rsvd_level - ubi->beb_rsvd_pebs + 1;
+ if (need > 0) {
+ need = ubi->avail_pebs >= need ? need : ubi->avail_pebs;
+ ubi->avail_pebs -= need;
+ ubi->rsvd_pebs += need;
+ ubi->beb_rsvd_pebs += need;
+ if (need > 0)
+ ubi_msg("reserve more %d PEBs", need);
+ }
+
+ if (ubi->beb_rsvd_pebs == 0) {
+ spin_unlock(&ubi->volumes_lock);
+ ubi_err("no reserved physical eraseblocks");
+ goto out_ro;
+ }
+
+ spin_unlock(&ubi->volumes_lock);
+ ubi_msg("mark PEB %d as bad", pnum);
+
+ err = ubi_io_mark_bad(ubi, pnum);
+ if (err)
+ goto out_ro;
+
+ spin_lock(&ubi->volumes_lock);
+ ubi->beb_rsvd_pebs -= 1;
+ ubi->bad_peb_count += 1;
+ ubi->good_peb_count -= 1;
+ ubi_calculate_reserved(ubi);
+ if (ubi->beb_rsvd_pebs == 0)
+ ubi_warn("last PEB from the reserved pool was used");
+ spin_unlock(&ubi->volumes_lock);
+
+ return err;
+
+out_ro:
+ ubi_ro_mode(ubi);
+ return err;
+}
+
+/**
+ * ubi_wl_put_peb - return a physical eraseblock to the wear-leveling unit.
+ * @ubi: UBI device description object
+ * @pnum: physical eraseblock to return
+ * @torture: if this physical eraseblock has to be tortured
+ *
+ * This function is called to return physical eraseblock @pnum to the pool of
+ * free physical eraseblocks. The @torture flag has to be set if an I/O error
+ * occurred to this @pnum and it has to be tested. This function returns zero
+ * in case of success, and a negative error code in case of failure.
+ */
+int ubi_wl_put_peb(struct ubi_device *ubi, int pnum, int torture)
+{
+ int err;
+ struct ubi_wl_entry *e;
+
+ dbg_wl("PEB %d", pnum);
+ ubi_assert(pnum >= 0);
+ ubi_assert(pnum < ubi->peb_count);
+
+retry:
+ spin_lock(&ubi->wl_lock);
+ e = ubi->lookuptbl[pnum];
+ if (e == ubi->move_from) {
+ /*
+ * User is putting the physical eraseblock which was selected to
+ * be moved. It will be scheduled for erasure in the
+ * wear-leveling worker.
+ */
+ dbg_wl("PEB %d is being moved, wait", pnum);
+ spin_unlock(&ubi->wl_lock);
+
+ /* Wait for the WL worker by taking the @ubi->move_mutex */
+ mutex_lock(&ubi->move_mutex);
+ mutex_unlock(&ubi->move_mutex);
+ goto retry;
+ } else if (e == ubi->move_to) {
+ /*
+ * User is putting the physical eraseblock which was selected
+ * as the target the data is moved to. It may happen if the EBA
+ * unit already re-mapped the LEB in 'ubi_eba_copy_leb()' but
+ * the WL unit has not put the PEB to the "used" tree yet, but
+ * it is about to do this. So we just set a flag which will
+ * tell the WL worker that the PEB is not needed anymore and
+ * should be scheduled for erasure.
+ */
+ dbg_wl("PEB %d is the target of data moving", pnum);
+ ubi_assert(!ubi->move_to_put);
+ ubi->move_to_put = 1;
+ spin_unlock(&ubi->wl_lock);
+ return 0;
+ } else {
+ if (in_wl_tree(e, &ubi->used)) {
+ paranoid_check_in_wl_tree(e, &ubi->used);
+ rb_erase(&e->rb, &ubi->used);
+ } else if (in_wl_tree(e, &ubi->scrub)) {
+ paranoid_check_in_wl_tree(e, &ubi->scrub);
+ rb_erase(&e->rb, &ubi->scrub);
+ } else {
+ err = prot_tree_del(ubi, e->pnum);
+ if (err) {
+ ubi_err("PEB %d not found", pnum);
+ ubi_ro_mode(ubi);
+ spin_unlock(&ubi->wl_lock);
+ return err;
+ }
+ }
+ }
+ spin_unlock(&ubi->wl_lock);
+
+ err = schedule_erase(ubi, e, torture);
+ if (err) {
+ spin_lock(&ubi->wl_lock);
+ wl_tree_add(e, &ubi->used);
+ spin_unlock(&ubi->wl_lock);
+ }
+
+ return err;
+}
+
+/**
+ * ubi_wl_scrub_peb - schedule a physical eraseblock for scrubbing.
+ * @ubi: UBI device description object
+ * @pnum: the physical eraseblock to schedule
+ *
+ * If a bit-flip in a physical eraseblock is detected, this physical eraseblock
+ * needs scrubbing. This function schedules a physical eraseblock for
+ * scrubbing which is done in background. This function returns zero in case of
+ * success and a negative error code in case of failure.
+ */
+int ubi_wl_scrub_peb(struct ubi_device *ubi, int pnum)
+{
+ struct ubi_wl_entry *e;
+
+ ubi_msg("schedule PEB %d for scrubbing", pnum);
+
+retry:
+ spin_lock(&ubi->wl_lock);
+ e = ubi->lookuptbl[pnum];
+ if (e == ubi->move_from || in_wl_tree(e, &ubi->scrub)) {
+ spin_unlock(&ubi->wl_lock);
+ return 0;
+ }
+
+ if (e == ubi->move_to) {
+ /*
+ * This physical eraseblock was used to move data to. The data
+ * was moved but the PEB was not yet inserted to the proper
+ * tree. We should just wait a little and let the WL worker
+ * proceed.
+ */
+ spin_unlock(&ubi->wl_lock);
+ dbg_wl("the PEB %d is not in proper tree, retry", pnum);
+ yield();
+ goto retry;
+ }
+
+ if (in_wl_tree(e, &ubi->used)) {
+ paranoid_check_in_wl_tree(e, &ubi->used);
+ rb_erase(&e->rb, &ubi->used);
+ } else {
+ int err;
+
+ err = prot_tree_del(ubi, e->pnum);
+ if (err) {
+ ubi_err("PEB %d not found", pnum);
+ ubi_ro_mode(ubi);
+ spin_unlock(&ubi->wl_lock);
+ return err;
+ }
+ }
+
+ wl_tree_add(e, &ubi->scrub);
+ spin_unlock(&ubi->wl_lock);
+
+ /*
+ * Technically scrubbing is the same as wear-leveling, so it is done
+ * by the WL worker.
+ */
+ return ensure_wear_leveling(ubi);
+}
+
+/**
+ * ubi_wl_flush - flush all pending works.
+ * @ubi: UBI device description object
+ *
+ * This function returns zero in case of success and a negative error code in
+ * case of failure.
+ */
+int ubi_wl_flush(struct ubi_device *ubi)
+{
+ int err;
+
+ /*
+ * Erase while the pending works queue is not empty, but not more then
+ * the number of currently pending works.
+ */
+ dbg_wl("flush (%d pending works)", ubi->works_count);
+ while (ubi->works_count) {
+ err = do_work(ubi);
+ if (err)
+ return err;
+ }
+
+ /*
+ * Make sure all the works which have been done in parallel are
+ * finished.
+ */
+ down_write(&ubi->work_sem);
+ up_write(&ubi->work_sem);
+
+ /*
+ * And in case last was the WL worker and it cancelled the LEB
+ * movement, flush again.
+ */
+ while (ubi->works_count) {
+ dbg_wl("flush more (%d pending works)", ubi->works_count);
+ err = do_work(ubi);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
+/**
+ * tree_destroy - destroy an RB-tree.
+ * @root: the root of the tree to destroy
+ */
+static void tree_destroy(struct rb_root *root)
+{
+ struct rb_node *rb;
+ struct ubi_wl_entry *e;
+
+ rb = root->rb_node;
+ while (rb) {
+ if (rb->rb_left)
+ rb = rb->rb_left;
+ else if (rb->rb_right)
+ rb = rb->rb_right;
+ else {
+ e = rb_entry(rb, struct ubi_wl_entry, rb);
+
+ rb = rb_parent(rb);
+ if (rb) {
+ if (rb->rb_left == &e->rb)
+ rb->rb_left = NULL;
+ else
+ rb->rb_right = NULL;
+ }
+
+ kmem_cache_free(ubi_wl_entry_slab, e);
+ }
+ }
+}
+
+/**
+ * ubi_thread - UBI background thread.
+ * @u: the UBI device description object pointer
+ */
+int ubi_thread(void *u)
+{
+ int failures = 0;
+ struct ubi_device *ubi = u;
+
+ ubi_msg("background thread \"%s\" started, PID %d",
+ ubi->bgt_name, task_pid_nr(current));
+
+ set_freezable();
+ for (;;) {
+ int err;
+
+ if (kthread_should_stop())
+ break;
+
+ if (try_to_freeze())
+ continue;
+
+ spin_lock(&ubi->wl_lock);
+ if (list_empty(&ubi->works) || ubi->ro_mode ||
+ !ubi->thread_enabled) {
+ set_current_state(TASK_INTERRUPTIBLE);
+ spin_unlock(&ubi->wl_lock);
+ schedule();
+ continue;
+ }
+ spin_unlock(&ubi->wl_lock);
+
+ err = do_work(ubi);
+ if (err) {
+ ubi_err("%s: work failed with error code %d",
+ ubi->bgt_name, err);
+ if (failures++ > WL_MAX_FAILURES) {
+ /*
+ * Too many failures, disable the thread and
+ * switch to read-only mode.
+ */
+ ubi_msg("%s: %d consecutive failures",
+ ubi->bgt_name, WL_MAX_FAILURES);
+ ubi_ro_mode(ubi);
+ break;
+ }
+ } else
+ failures = 0;
+
+ cond_resched();
+ }
+
+ dbg_wl("background thread \"%s\" is killed", ubi->bgt_name);
+ return 0;
+}
+
+/**
+ * cancel_pending - cancel all pending works.
+ * @ubi: UBI device description object
+ */
+static void cancel_pending(struct ubi_device *ubi)
+{
+ while (!list_empty(&ubi->works)) {
+ struct ubi_work *wrk;
+
+ wrk = list_entry(ubi->works.next, struct ubi_work, list);
+ list_del(&wrk->list);
+ wrk->func(ubi, wrk, 1);
+ ubi->works_count -= 1;
+ ubi_assert(ubi->works_count >= 0);
+ }
+}
+
+/**
+ * ubi_wl_init_scan - initialize the wear-leveling unit using scanning
+ * information.
+ * @ubi: UBI device description object
+ * @si: scanning information
+ *
+ * This function returns zero in case of success, and a negative error code in
+ * case of failure.
+ */
+int ubi_wl_init_scan(struct ubi_device *ubi, struct ubi_scan_info *si)
+{
+ int err;
+ struct rb_node *rb1, *rb2;
+ struct ubi_scan_volume *sv;
+ struct ubi_scan_leb *seb, *tmp;
+ struct ubi_wl_entry *e;
+
+
+ ubi->used = ubi->free = ubi->scrub = RB_ROOT;
+ ubi->prot.pnum = ubi->prot.aec = RB_ROOT;
+ spin_lock_init(&ubi->wl_lock);
+ mutex_init(&ubi->move_mutex);
+ init_rwsem(&ubi->work_sem);
+ ubi->max_ec = si->max_ec;
+ INIT_LIST_HEAD(&ubi->works);
+
+ sprintf(ubi->bgt_name, UBI_BGT_NAME_PATTERN, ubi->ubi_num);
+
+ err = -ENOMEM;
+ ubi->lookuptbl = kzalloc(ubi->peb_count * sizeof(void *), GFP_KERNEL);
+ if (!ubi->lookuptbl)
+ return err;
+
+ list_for_each_entry_safe(seb, tmp, &si->erase, u.list) {
+ cond_resched();
+
+ e = kmem_cache_alloc(ubi_wl_entry_slab, GFP_KERNEL);
+ if (!e)
+ goto out_free;
+
+ e->pnum = seb->pnum;
+ e->ec = seb->ec;
+ ubi->lookuptbl[e->pnum] = e;
+ if (schedule_erase(ubi, e, 0)) {
+ kmem_cache_free(ubi_wl_entry_slab, e);
+ goto out_free;
+ }
+ }
+
+ list_for_each_entry(seb, &si->free, u.list) {
+ cond_resched();
+
+ e = kmem_cache_alloc(ubi_wl_entry_slab, GFP_KERNEL);
+ if (!e)
+ goto out_free;
+
+ e->pnum = seb->pnum;
+ e->ec = seb->ec;
+ ubi_assert(e->ec >= 0);
+ wl_tree_add(e, &ubi->free);
+ ubi->lookuptbl[e->pnum] = e;
+ }
+
+ list_for_each_entry(seb, &si->corr, u.list) {
+ cond_resched();
+
+ e = kmem_cache_alloc(ubi_wl_entry_slab, GFP_KERNEL);
+ if (!e)
+ goto out_free;
+
+ e->pnum = seb->pnum;
+ e->ec = seb->ec;
+ ubi->lookuptbl[e->pnum] = e;
+ if (schedule_erase(ubi, e, 0)) {
+ kmem_cache_free(ubi_wl_entry_slab, e);
+ goto out_free;
+ }
+ }
+
+ ubi_rb_for_each_entry(rb1, sv, &si->volumes, rb) {
+ ubi_rb_for_each_entry(rb2, seb, &sv->root, u.rb) {
+ cond_resched();
+
+ e = kmem_cache_alloc(ubi_wl_entry_slab, GFP_KERNEL);
+ if (!e)
+ goto out_free;
+
+ e->pnum = seb->pnum;
+ e->ec = seb->ec;
+ ubi->lookuptbl[e->pnum] = e;
+ if (!seb->scrub) {
+ dbg_wl("add PEB %d EC %d to the used tree",
+ e->pnum, e->ec);
+ wl_tree_add(e, &ubi->used);
+ } else {
+ dbg_wl("add PEB %d EC %d to the scrub tree",
+ e->pnum, e->ec);
+ wl_tree_add(e, &ubi->scrub);
+ }
+ }
+ }
+
+ if (ubi->avail_pebs < WL_RESERVED_PEBS) {
+ ubi_err("no enough physical eraseblocks (%d, need %d)",
+ ubi->avail_pebs, WL_RESERVED_PEBS);
+ goto out_free;
+ }
+ ubi->avail_pebs -= WL_RESERVED_PEBS;
+ ubi->rsvd_pebs += WL_RESERVED_PEBS;
+
+ /* Schedule wear-leveling if needed */
+ err = ensure_wear_leveling(ubi);
+ if (err)
+ goto out_free;
+
+ return 0;
+
+out_free:
+ cancel_pending(ubi);
+ tree_destroy(&ubi->used);
+ tree_destroy(&ubi->free);
+ tree_destroy(&ubi->scrub);
+ kfree(ubi->lookuptbl);
+ return err;
+}
+
+/**
+ * protection_trees_destroy - destroy the protection RB-trees.
+ * @ubi: UBI device description object
+ */
+static void protection_trees_destroy(struct ubi_device *ubi)
+{
+ struct rb_node *rb;
+ struct ubi_wl_prot_entry *pe;
+
+ rb = ubi->prot.aec.rb_node;
+ while (rb) {
+ if (rb->rb_left)
+ rb = rb->rb_left;
+ else if (rb->rb_right)
+ rb = rb->rb_right;
+ else {
+ pe = rb_entry(rb, struct ubi_wl_prot_entry, rb_aec);
+
+ rb = rb_parent(rb);
+ if (rb) {
+ if (rb->rb_left == &pe->rb_aec)
+ rb->rb_left = NULL;
+ else
+ rb->rb_right = NULL;
+ }
+
+ kmem_cache_free(ubi_wl_entry_slab, pe->e);
+ kfree(pe);
+ }
+ }
+}
+
+/**
+ * ubi_wl_close - close the wear-leveling unit.
+ * @ubi: UBI device description object
+ */
+void ubi_wl_close(struct ubi_device *ubi)
+{
+ dbg_wl("close the UBI wear-leveling unit");
+
+ cancel_pending(ubi);
+ protection_trees_destroy(ubi);
+ tree_destroy(&ubi->used);
+ tree_destroy(&ubi->free);
+ tree_destroy(&ubi->scrub);
+ kfree(ubi->lookuptbl);
+}
+
+#ifdef CONFIG_MTD_UBI_DEBUG_PARANOID
+
+/**
+ * paranoid_check_ec - make sure that the erase counter of a physical eraseblock
+ * is correct.
+ * @ubi: UBI device description object
+ * @pnum: the physical eraseblock number to check
+ * @ec: the erase counter to check
+ *
+ * This function returns zero if the erase counter of physical eraseblock @pnum
+ * is equivalent to @ec, %1 if not, and a negative error code if an error
+ * occurred.
+ */
+static int paranoid_check_ec(struct ubi_device *ubi, int pnum, int ec)
+{
+ int err;
+ long long read_ec;
+ struct ubi_ec_hdr *ec_hdr;
+
+ ec_hdr = kzalloc(ubi->ec_hdr_alsize, GFP_NOFS);
+ if (!ec_hdr)
+ return -ENOMEM;
+
+ err = ubi_io_read_ec_hdr(ubi, pnum, ec_hdr, 0);
+ if (err && err != UBI_IO_BITFLIPS) {
+ /* The header does not have to exist */
+ err = 0;
+ goto out_free;
+ }
+
+ read_ec = be64_to_cpu(ec_hdr->ec);
+ if (ec != read_ec) {
+ ubi_err("paranoid check failed for PEB %d", pnum);
+ ubi_err("read EC is %lld, should be %d", read_ec, ec);
+ ubi_dbg_dump_stack();
+ err = 1;
+ } else
+ err = 0;
+
+out_free:
+ kfree(ec_hdr);
+ return err;
+}
+
+/**
+ * paranoid_check_in_wl_tree - make sure that a wear-leveling entry is present
+ * in a WL RB-tree.
+ * @e: the wear-leveling entry to check
+ * @root: the root of the tree
+ *
+ * This function returns zero if @e is in the @root RB-tree and %1 if it
+ * is not.
+ */
+static int paranoid_check_in_wl_tree(struct ubi_wl_entry *e,
+ struct rb_root *root)
+{
+ if (in_wl_tree(e, root))
+ return 0;
+
+ ubi_err("paranoid check failed for PEB %d, EC %d, RB-tree %p ",
+ e->pnum, e->ec, root);
+ ubi_dbg_dump_stack();
+ return 1;
+}
+
+#endif /* CONFIG_MTD_UBI_DEBUG_PARANOID */
diff --git a/include/linux/crc32.h b/include/linux/crc32.h
new file mode 100644
index 0000000..ac4aed1
--- /dev/null
+++ b/include/linux/crc32.h
@@ -0,0 +1,27 @@
+/*
+ * crc32.h
+ * See linux/lib/crc32.c for license and changes
+ */
+#ifndef _LINUX_CRC32_H
+#define _LINUX_CRC32_H
+
+#include <linux/types.h>
+/* #include <linux/bitrev.h> */
+
+extern u32 crc32_le(u32 crc, unsigned char const *p, size_t len);
+/* extern u32 crc32_be(u32 crc, unsigned char const *p, size_t len); */
+
+#define crc32(seed, data, length) crc32_le(seed, (unsigned char const *)data, length)
+
+/*
+ * Helpers for hash table generation of ethernet nics:
+ *
+ * Ethernet sends the least significant bit of a byte first, thus crc32_le
+ * is used. The output of crc32_le is bit reversed [most significant bit
+ * is in bit nr 0], thus it must be reversed before use. Except for
+ * nics that bit swap the result internally...
+ */
+/* #define ether_crc(length, data) bitrev32(crc32_le(~0, data, length)) */
+/* #define ether_crc_le(length, data) crc32_le(~0, data, length) */
+
+#endif /* _LINUX_CRC32_H */
diff --git a/include/linux/mtd/ubi.h b/include/linux/mtd/ubi.h
new file mode 100644
index 0000000..43d9b38
--- /dev/null
+++ b/include/linux/mtd/ubi.h
@@ -0,0 +1,186 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Author: Artem Bityutskiy (Битюцкий Артём)
+ */
+
+#ifndef __LINUX_UBI_H__
+#define __LINUX_UBI_H__
+
+/* #include <asm/ioctl.h> */
+#include <linux/types.h>
+#include <mtd/ubi-user.h>
+
+/*
+ * enum ubi_open_mode - UBI volume open mode constants.
+ *
+ * UBI_READONLY: read-only mode
+ * UBI_READWRITE: read-write mode
+ * UBI_EXCLUSIVE: exclusive mode
+ */
+enum {
+ UBI_READONLY = 1,
+ UBI_READWRITE,
+ UBI_EXCLUSIVE
+};
+
+/**
+ * struct ubi_volume_info - UBI volume description data structure.
+ * @vol_id: volume ID
+ * @ubi_num: UBI device number this volume belongs to
+ * @size: how many physical eraseblocks are reserved for this volume
+ * @used_bytes: how many bytes of data this volume contains
+ * @used_ebs: how many physical eraseblocks of this volume actually contain any
+ * data
+ * @vol_type: volume type (%UBI_DYNAMIC_VOLUME or %UBI_STATIC_VOLUME)
+ * @corrupted: non-zero if the volume is corrupted (static volumes only)
+ * @upd_marker: non-zero if the volume has update marker set
+ * @alignment: volume alignment
+ * @usable_leb_size: how many bytes are available in logical eraseblocks of
+ * this volume
+ * @name_len: volume name length
+ * @name: volume name
+ * @cdev: UBI volume character device major and minor numbers
+ *
+ * The @corrupted flag is only relevant to static volumes and is always zero
+ * for dynamic ones. This is because UBI does not care about dynamic volume
+ * data protection and only cares about protecting static volume data.
+ *
+ * The @upd_marker flag is set if the volume update operation was interrupted.
+ * Before touching the volume data during the update operation, UBI first sets
+ * the update marker flag for this volume. If the volume update operation was
+ * further interrupted, the update marker indicates this. If the update marker
+ * is set, the contents of the volume is certainly damaged and a new volume
+ * update operation has to be started.
+ *
+ * To put it differently, @corrupted and @upd_marker fields have different
+ * semantics:
+ * o the @corrupted flag means that this static volume is corrupted for some
+ * reasons, but not because an interrupted volume update
+ * o the @upd_marker field means that the volume is damaged because of an
+ * interrupted update operation.
+ *
+ * I.e., the @corrupted flag is never set if the @upd_marker flag is set.
+ *
+ * The @used_bytes and @used_ebs fields are only really needed for static
+ * volumes and contain the number of bytes stored in this static volume and how
+ * many eraseblock this data occupies. In case of dynamic volumes, the
+ * @used_bytes field is equivalent to @size*@usable_leb_size, and the @used_ebs
+ * field is equivalent to @size.
+ *
+ * In general, logical eraseblock size is a property of the UBI device, not
+ * of the UBI volume. Indeed, the logical eraseblock size depends on the
+ * physical eraseblock size and on how much bytes UBI headers consume. But
+ * because of the volume alignment (@alignment), the usable size of logical
+ * eraseblocks if a volume may be less. The following equation is true:
+ * @usable_leb_size = LEB size - (LEB size mod @alignment),
+ * where LEB size is the logical eraseblock size defined by the UBI device.
+ *
+ * The alignment is multiple to the minimal flash input/output unit size or %1
+ * if all the available space is used.
+ *
+ * To put this differently, alignment may be considered is a way to change
+ * volume logical eraseblock sizes.
+ */
+struct ubi_volume_info {
+ int ubi_num;
+ int vol_id;
+ int size;
+ long long used_bytes;
+ int used_ebs;
+ int vol_type;
+ int corrupted;
+ int upd_marker;
+ int alignment;
+ int usable_leb_size;
+ int name_len;
+ const char *name;
+ dev_t cdev;
+};
+
+/**
+ * struct ubi_device_info - UBI device description data structure.
+ * @ubi_num: ubi device number
+ * @leb_size: logical eraseblock size on this UBI device
+ * @min_io_size: minimal I/O unit size
+ * @ro_mode: if this device is in read-only mode
+ * @cdev: UBI character device major and minor numbers
+ *
+ * Note, @leb_size is the logical eraseblock size offered by the UBI device.
+ * Volumes of this UBI device may have smaller logical eraseblock size if their
+ * alignment is not equivalent to %1.
+ */
+struct ubi_device_info {
+ int ubi_num;
+ int leb_size;
+ int min_io_size;
+ int ro_mode;
+ dev_t cdev;
+};
+
+/* UBI descriptor given to users when they open UBI volumes */
+struct ubi_volume_desc;
+
+int ubi_get_device_info(int ubi_num, struct ubi_device_info *di);
+void ubi_get_volume_info(struct ubi_volume_desc *desc,
+ struct ubi_volume_info *vi);
+struct ubi_volume_desc *ubi_open_volume(int ubi_num, int vol_id, int mode);
+struct ubi_volume_desc *ubi_open_volume_nm(int ubi_num, const char *name,
+ int mode);
+void ubi_close_volume(struct ubi_volume_desc *desc);
+int ubi_leb_read(struct ubi_volume_desc *desc, int lnum, char *buf, int offset,
+ int len, int check);
+int ubi_leb_write(struct ubi_volume_desc *desc, int lnum, const void *buf,
+ int offset, int len, int dtype);
+int ubi_leb_change(struct ubi_volume_desc *desc, int lnum, const void *buf,
+ int len, int dtype);
+int ubi_leb_erase(struct ubi_volume_desc *desc, int lnum);
+int ubi_leb_unmap(struct ubi_volume_desc *desc, int lnum);
+int ubi_leb_map(struct ubi_volume_desc *desc, int lnum, int dtype);
+int ubi_is_mapped(struct ubi_volume_desc *desc, int lnum);
+
+/*
+ * This function is the same as the 'ubi_leb_read()' function, but it does not
+ * provide the checking capability.
+ */
+static inline int ubi_read(struct ubi_volume_desc *desc, int lnum, char *buf,
+ int offset, int len)
+{
+ return ubi_leb_read(desc, lnum, buf, offset, len, 0);
+}
+
+/*
+ * This function is the same as the 'ubi_leb_write()' functions, but it does
+ * not have the data type argument.
+ */
+static inline int ubi_write(struct ubi_volume_desc *desc, int lnum,
+ const void *buf, int offset, int len)
+{
+ return ubi_leb_write(desc, lnum, buf, offset, len, UBI_UNKNOWN);
+}
+
+/*
+ * This function is the same as the 'ubi_leb_change()' functions, but it does
+ * not have the data type argument.
+ */
+static inline int ubi_change(struct ubi_volume_desc *desc, int lnum,
+ const void *buf, int len)
+{
+ return ubi_leb_change(desc, lnum, buf, len, UBI_UNKNOWN);
+}
+
+#endif /* !__LINUX_UBI_H__ */
diff --git a/include/linux/types.h b/include/linux/types.h
index df4808f..1b0b4a4 100644
--- a/include/linux/types.h
+++ b/include/linux/types.h
@@ -119,6 +119,30 @@ typedef __s64 int64_t;
* Below are truly Linux-specific types that should never collide with
* any application/library that wants linux/types.h.
*/
+#ifdef __CHECKER__
+#define __bitwise__ __attribute__((bitwise))
+#else
+#define __bitwise__
+#endif
+#ifdef __CHECK_ENDIAN__
+#define __bitwise __bitwise__
+#else
+#define __bitwise
+#endif
+
+typedef __u16 __bitwise __le16;
+typedef __u16 __bitwise __be16;
+typedef __u32 __bitwise __le32;
+typedef __u32 __bitwise __be32;
+#if defined(__GNUC__)
+typedef __u64 __bitwise __le64;
+typedef __u64 __bitwise __be64;
+#endif
+typedef __u16 __bitwise __sum16;
+typedef __u32 __bitwise __wsum;
+
+
+typedef unsigned __bitwise__ gfp_t;
struct ustat {
__kernel_daddr_t f_tfree;
diff --git a/include/ubi_uboot.h b/include/ubi_uboot.h
new file mode 100644
index 0000000..b4b2fa2
--- /dev/null
+++ b/include/ubi_uboot.h
@@ -0,0 +1,218 @@
+/*
+ * Header file for UBI support for U-Boot
+ *
+ * Adaptation from kernel to U-Boot
+ *
+ * Copyright (C) 2005-2007 Samsung Electronics
+ * Kyungmin Park <kyungmin.park(a)samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __UBOOT_UBI_H
+#define __UBOOT_UBI_H
+
+#include <common.h>
+#include <malloc.h>
+#include <div64.h>
+#include <linux/crc32.h>
+#include <linux/mtd/mtd.h>
+
+#ifdef CONFIG_CMD_ONENAND
+#include <onenand_uboot.h>
+#endif
+
+#include <asm/errno.h>
+
+#define DPRINTK(format, args...) \
+do { \
+ printf("%s[%d]: " format "\n", __func__, __LINE__, ##args); \
+} while (0)
+
+/* configurable */
+#define CONFIG_MTD_UBI_WL_THRESHOLD 4096
+#define CONFIG_MTD_UBI_BEB_RESERVE 1
+#define UBI_IO_DEBUG 0
+
+/* #define CONFIG_MTD_UBI_DEBUG */
+/* #define CONFIG_MTD_UBI_DEBUG_PARANOID */
+
+#define CONFIG_MTD_UBI_DEBUG_DISABLE_BGT
+/* #define CONFIG_MTD_UBI_DEBUG_MSG */
+/* #define CONFIG_MTD_UBI_DEBUG_MSG_EBA */
+/* #define CONFIG_MTD_UBI_DEBUG_MSG_WL */
+/* #define CONFIG_MTD_UBI_DEBUG_MSG_IO */
+/* #define CONFIG_MTD_UBI_DEBUG_MSG_BLD */
+
+/* compiler options */
+#define uninitialized_var(x) x = x
+
+/* build.c */
+#define get_device(...)
+#define put_device(...)
+#define ubi_sysfs_init(...) 0
+#define ubi_sysfs_close(...) do { } while (0)
+static inline int is_power_of_2(unsigned long n)
+{
+ return n != 0 && ((n & (n - 1)) == 0);
+}
+
+/* FIXME */
+#define MKDEV(...) 0
+#define MAJOR(dev) 0
+#define MINOR(dev) 0
+
+#define alloc_chrdev_region(...) 0
+#define unregister_chrdev_region(...)
+
+#define class_create(...) __builtin_return_address(0)
+#define class_create_file(...) 0
+#define class_remove_file(...)
+#define class_destroy(...)
+#define misc_register(...) 0
+#define misc_deregister(...)
+
+
+/* vmt.c */
+#define device_register(...) 0
+#define volume_sysfs_init(...) 0
+#define volume_sysfs_close(...) do { } while (0)
+
+/* kapi.c */
+
+/* eba.c */
+
+/* io.c */
+#define init_waitqueue_head(...) do { } while (0)
+#define wait_event_interruptible(...) 0
+#define wake_up_interruptible(...) do { } while (0)
+#define print_hex_dump(...) do { } while (0)
+#define dump_stack(...) do { } while (0)
+
+/* wl.c */
+#define task_pid_nr(x) 0
+#define set_freezable(...) do { } while (0)
+#define try_to_freeze(...) 0
+#define set_current_state(...) do { } while (0)
+#define kthread_should_stop(...) 0
+#define schedule() do { } while (0)
+
+/* upd.c */
+#define copy_from_user(dest, src, count) ({ \
+ int __ret = 0; \
+ memcpy(dest, src, count); \
+ __ret; \
+})
+
+/* common */
+typedef int spinlock_t;
+typedef int wait_queue_head_t;
+#define spin_lock_init(...)
+#define spin_lock(...)
+#define spin_unlock(...)
+
+#define mutex_init(...)
+#define mutex_lock(...)
+#define mutex_unlock(...)
+
+#define init_rwsem(...) do { } while (0)
+#define down_read(...) do { } while (0)
+#define down_write(...) do { } while (0)
+#define down_write_trylock(...) 0
+#define up_read(...) do { } while (0)
+#define up_write(...) do { } while (0)
+
+struct kmem_cache { int i; };
+#define kmem_cache_create(...) 1
+#define kmem_cache_alloc(obj, gfp) malloc(sizeof(struct ubi_wl_entry))
+#define kmem_cache_free(obj, size) free(size)
+#define kmem_cache_destroy(...)
+
+#define cond_resched() do { } while (0)
+#define yield() do { } while (0)
+
+#define KERN_WARNING
+#define KERN_ERR
+#define KERN_NOTICE
+#define KERN_DEBUG
+
+#define GFP_KERNEL 0
+#define GFP_NOFS 1
+
+#define __user
+#define __init
+#define __exit
+
+#define kthread_create(...) __builtin_return_address(0)
+#define kthread_stop(...) do { } while (0)
+#define wake_up_process(...) do { } while (0)
+
+struct rw_semaphore { int i; };
+struct device {
+ struct device *parent;
+ struct class *class;
+ dev_t devt;
+ int bus_id[20];
+ void (*release)(struct device *dev);
+};
+struct mutex { int i; };
+struct kernel_param { int i; };
+
+struct cdev {
+ int owner;
+ dev_t dev;
+};
+#define cdev_init(...) do { } while (0)
+#define cdev_add(...) 0
+#define cdev_del(...) do { } while (0)
+
+#define MAX_ERRNO 4095
+#define IS_ERR_VALUE(x) ((x) >= (unsigned long)-MAX_ERRNO)
+
+static inline void *ERR_PTR(long error)
+{
+ return (void *) error;
+}
+
+static inline long PTR_ERR(const void *ptr)
+{
+ return (long) ptr;
+}
+
+static inline long IS_ERR(const void *ptr)
+{
+ return IS_ERR_VALUE((unsigned long)ptr);
+}
+
+/* Force a compilation error if condition is true */
+#define BUILD_BUG_ON(condition) ((void)sizeof(char[1 - 2*!!(condition)]))
+
+/* module */
+#define THIS_MODULE 0
+#define try_module_get(...) 0
+#define module_put(...) do { } while (0)
+#define module_init(...)
+#define module_exit(...)
+#define EXPORT_SYMBOL(...)
+#define EXPORT_SYMBOL_GPL(...)
+#define module_param_call(...)
+#define MODULE_PARM_DESC(...)
+#define MODULE_VERSION(...)
+#define MODULE_DESCRIPTION(...)
+#define MODULE_AUTHOR(...)
+#define MODULE_LICENSE(...)
+
+#include "../drivers/mtd/ubi/ubi.h"
+
+/* functions */
+extern int ubi_mtd_param_parse(const char *val, struct kernel_param *kp);
+extern int ubi_init(void);
+
+extern struct ubi_device *ubi_devices[];
+
+/* Board specific functions */
+extern int ubi_board_scan(void);
+
+#endif
2
1

02 Nov '08
Signed-off-by: Peter Tyser <ptyser(a)xes-inc.com>
---
lib_ppc/bootm.c | 5 +++--
1 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/lib_ppc/bootm.c b/lib_ppc/bootm.c
index fce4eff..18eb54f 100644
--- a/lib_ppc/bootm.c
+++ b/lib_ppc/bootm.c
@@ -224,12 +224,13 @@ static int boot_bd_t_linux(bootm_headers_t *images)
static int boot_body_linux(bootm_headers_t *images)
{
- ulong rd_len, bootmap_base = getenv_bootm_low();
- ulong of_size = images->ft_len;
+ ulong rd_len;
struct lmb *lmb = &images->lmb;
ulong *initrd_start = &images->initrd_start;
ulong *initrd_end = &images->initrd_end;
#if defined(CONFIG_OF_LIBFDT)
+ ulong bootmap_base = getenv_bootm_low();
+ ulong of_size = images->ft_len;
char **of_flat_tree = &images->ft_addr;
#endif
--
1.6.0.2.GIT
2
1

[U-Boot] [PATCH 1/1] common/Makefile: create others group for non core, environment and command files
by Jean-Christophe PLAGNIOL-VILLARD 02 Nov '08
by Jean-Christophe PLAGNIOL-VILLARD 02 Nov '08
02 Nov '08
Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD <plagnioj(a)jcrosoft.com>
---
common/Makefile | 7 +++++--
1 files changed, 5 insertions(+), 2 deletions(-)
diff --git a/common/Makefile b/common/Makefile
index 14042e6..8b7eac3 100644
--- a/common/Makefile
+++ b/common/Makefile
@@ -42,7 +42,7 @@ COBJS-y += s_record.o
COBJS-y += serial.o
COBJS-y += xyzModem.o
-#core command
+# core command
COBJS-y += cmd_boot.o
COBJS-y += cmd_bootm.o
COBJS-y += cmd_nvedit.o
@@ -137,6 +137,9 @@ endif
COBJS-$(CONFIG_CMD_XIMG) += cmd_ximg.o
COBJS-$(CONFIG_YAFFS2) += cmd_yaffs2.o
COBJS-$(CONFIG_VFD) += cmd_vfd.o
+
+# others
+COBJS-$(CONFIG_DDR_SPD) += ddr_spd.o
COBJS-$(CONFIG_CMD_DOC) += docecc.o
COBJS-y += flash.o
COBJS-y += kgdb.o
@@ -144,7 +147,7 @@ COBJS-$(CONFIG_LCD) += lcd.o
COBJS-$(CONFIG_LYNXKDI) += lynxkdi.o
COBJS-$(CONFIG_UPDATE_TFTP) += update.o
COBJS-$(CONFIG_USB_KEYBOARD) += usb_kbd.o
-COBJS-$(CONFIG_DDR_SPD) += ddr_spd.o
+
COBJS := $(sort $(COBJS-y))
SRCS := $(AOBJS:.o=.S) $(COBJS:.o=.c)
--
1.5.6.5
2
1

[U-Boot] [PATCH] TQM8260: use CFI flash driver instead of custom driver.
by Wolfgang Denk 02 Nov '08
by Wolfgang Denk 02 Nov '08
02 Nov '08
Signed-off-by: Wolfgang Denk <wd(a)denx.de>
---
board/tqc/tqm8260/Makefile | 2 +-
board/tqc/tqm8260/flash.c | 497 --------------------------------------------
include/configs/TQM8260.h | 7 +
3 files changed, 8 insertions(+), 498 deletions(-)
delete mode 100644 board/tqc/tqm8260/flash.c
diff --git a/board/tqc/tqm8260/Makefile b/board/tqc/tqm8260/Makefile
index 61221fd..94ba1e9 100644
--- a/board/tqc/tqm8260/Makefile
+++ b/board/tqc/tqm8260/Makefile
@@ -28,7 +28,7 @@ endif
LIB = $(obj)lib$(BOARD).a
-COBJS = $(BOARD).o flash.o ../tqm8xx/load_sernum_ethaddr.o
+COBJS = $(BOARD).o ../tqm8xx/load_sernum_ethaddr.o
SRCS := $(SOBJS:.o=.S) $(COBJS:.o=.c)
OBJS := $(addprefix $(obj),$(COBJS))
diff --git a/board/tqc/tqm8260/flash.c b/board/tqc/tqm8260/flash.c
deleted file mode 100644
index cabc818..0000000
--- a/board/tqc/tqm8260/flash.c
+++ /dev/null
@@ -1,497 +0,0 @@
-/*
- * (C) Copyright 2001, 2002
- * Wolfgang Denk, DENX Software Engineering, wd(a)denx.de.
- *
- * Flash Routines for AMD devices on the TQM8260 board
- *
- *--------------------------------------------------------------------
- * See file CREDITS for list of people who contributed to this
- * project.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
- * MA 02111-1307 USA
- */
-
-#include <common.h>
-#include <mpc8xx.h>
-
-#define V_ULONG(a) (*(volatile unsigned long *)( a ))
-#define V_BYTE(a) (*(volatile unsigned char *)( a ))
-
-
-flash_info_t flash_info[CONFIG_SYS_MAX_FLASH_BANKS];
-
-
-/*-----------------------------------------------------------------------
- */
-void flash_reset (void)
-{
- if (flash_info[0].flash_id != FLASH_UNKNOWN) {
- V_ULONG (flash_info[0].start[0]) = 0x00F000F0;
- V_ULONG (flash_info[0].start[0] + 4) = 0x00F000F0;
- }
-}
-
-/*-----------------------------------------------------------------------
- */
-ulong flash_get_size (ulong baseaddr, flash_info_t * info)
-{
- short i;
- unsigned long flashtest_h, flashtest_l;
-
- /* Write auto select command sequence and test FLASH answer */
- V_ULONG (baseaddr + ((ulong) 0x0555 << 3)) = 0x00AA00AA;
- V_ULONG (baseaddr + ((ulong) 0x02AA << 3)) = 0x00550055;
- V_ULONG (baseaddr + ((ulong) 0x0555 << 3)) = 0x00900090;
- V_ULONG (baseaddr + 4 + ((ulong) 0x0555 << 3)) = 0x00AA00AA;
- V_ULONG (baseaddr + 4 + ((ulong) 0x02AA << 3)) = 0x00550055;
- V_ULONG (baseaddr + 4 + ((ulong) 0x0555 << 3)) = 0x00900090;
-
- flashtest_h = V_ULONG (baseaddr); /* manufacturer ID */
- flashtest_l = V_ULONG (baseaddr + 4);
-
- switch ((int) flashtest_h) {
- case AMD_MANUFACT:
- info->flash_id = FLASH_MAN_AMD;
- break;
- case FUJ_MANUFACT:
- info->flash_id = FLASH_MAN_FUJ;
- break;
- default:
- info->flash_id = FLASH_UNKNOWN;
- info->sector_count = 0;
- info->size = 0;
- return (0); /* no or unknown flash */
- }
-
- flashtest_h = V_ULONG (baseaddr + 8); /* device ID */
- flashtest_l = V_ULONG (baseaddr + 12);
- if (flashtest_h != flashtest_l) {
- info->flash_id = FLASH_UNKNOWN;
- } else {
- switch (flashtest_h) {
- case AMD_ID_LV800T:
- info->flash_id += FLASH_AM800T;
- info->sector_count = 19;
- info->size = 0x00400000;
- break; /* 4 * 1 MB = 4 MB */
- case AMD_ID_LV800B:
- info->flash_id += FLASH_AM800B;
- info->sector_count = 19;
- info->size = 0x00400000;
- break; /* 4 * 1 MB = 4 MB */
- case AMD_ID_LV160T:
- info->flash_id += FLASH_AM160T;
- info->sector_count = 35;
- info->size = 0x00800000;
- break; /* 4 * 2 MB = 8 MB */
- case AMD_ID_LV160B:
- info->flash_id += FLASH_AM160B;
- info->sector_count = 35;
- info->size = 0x00800000;
- break; /* 4 * 2 MB = 8 MB */
- case AMD_ID_DL322T:
- info->flash_id += FLASH_AMDL322T;
- info->sector_count = 71;
- info->size = 0x01000000;
- break; /* 4 * 4 MB = 16 MB */
- case AMD_ID_DL322B:
- info->flash_id += FLASH_AMDL322B;
- info->sector_count = 71;
- info->size = 0x01000000;
- break; /* 4 * 4 MB = 16 MB */
- case AMD_ID_DL323T:
- info->flash_id += FLASH_AMDL323T;
- info->sector_count = 71;
- info->size = 0x01000000;
- break; /* 4 * 4 MB = 16 MB */
- case AMD_ID_DL323B:
- info->flash_id += FLASH_AMDL323B;
- info->sector_count = 71;
- info->size = 0x01000000;
- break; /* 4 * 4 MB = 16 MB */
- case AMD_ID_LV640U:
- info->flash_id += FLASH_AM640U;
- info->sector_count = 128;
- info->size = 0x02000000;
- break; /* 4 * 8 MB = 32 MB */
- default:
- info->flash_id = FLASH_UNKNOWN;
- return (0); /* no or unknown flash */
- }
- }
-
- if (flashtest_h == AMD_ID_LV640U) {
-
- /* set up sector start adress table (uniform sector type) */
- for (i = 0; i < info->sector_count; i++)
- info->start[i] = baseaddr + (i * 0x00040000);
-
- } else if (info->flash_id & FLASH_BTYPE) {
-
- /* set up sector start adress table (bottom sector type) */
- info->start[0] = baseaddr + 0x00000000;
- info->start[1] = baseaddr + 0x00010000;
- info->start[2] = baseaddr + 0x00018000;
- info->start[3] = baseaddr + 0x00020000;
- for (i = 4; i < info->sector_count; i++) {
- info->start[i] = baseaddr + (i * 0x00040000) - 0x000C0000;
- }
-
- } else {
-
- /* set up sector start adress table (top sector type) */
- i = info->sector_count - 1;
- info->start[i--] = baseaddr + info->size - 0x00010000;
- info->start[i--] = baseaddr + info->size - 0x00018000;
- info->start[i--] = baseaddr + info->size - 0x00020000;
- for (; i >= 0; i--) {
- info->start[i] = baseaddr + i * 0x00040000;
- }
- }
-
- /* check for protected sectors */
- for (i = 0; i < info->sector_count; i++) {
- /* read sector protection at sector address, (A7 .. A0) = 0x02 */
- if ((V_ULONG (info->start[i] + 16) & 0x00010001) ||
- (V_ULONG (info->start[i] + 20) & 0x00010001)) {
- info->protect[i] = 1; /* D0 = 1 if protected */
- } else {
- info->protect[i] = 0;
- }
- }
-
- flash_reset ();
- return (info->size);
-}
-
-/*-----------------------------------------------------------------------
- */
-unsigned long flash_init (void)
-{
- unsigned long size_b0 = 0;
- int i;
-
- /* Init: no FLASHes known */
- for (i = 0; i < CONFIG_SYS_MAX_FLASH_BANKS; ++i) {
- flash_info[i].flash_id = FLASH_UNKNOWN;
- }
-
- /* Static FLASH Bank configuration here (only one bank) */
-
- size_b0 = flash_get_size (CONFIG_SYS_FLASH0_BASE, &flash_info[0]);
- if (flash_info[0].flash_id == FLASH_UNKNOWN || size_b0 == 0) {
- printf ("## Unknown FLASH on Bank 0 - Size = 0x%08lx = %ld MB\n",
- size_b0, size_b0 >> 20);
- }
-
- /*
- * protect monitor and environment sectors
- */
-
-#if CONFIG_SYS_MONITOR_BASE >= CONFIG_SYS_FLASH0_BASE
- flash_protect (FLAG_PROTECT_SET,
- CONFIG_SYS_MONITOR_BASE,
- CONFIG_SYS_MONITOR_BASE + monitor_flash_len - 1,
- &flash_info[0]);
-#endif
-
-#if defined(CONFIG_ENV_IS_IN_FLASH) && defined(CONFIG_ENV_ADDR)
-# ifndef CONFIG_ENV_SIZE
-# define CONFIG_ENV_SIZE CONFIG_ENV_SECT_SIZE
-# endif
- flash_protect (FLAG_PROTECT_SET,
- CONFIG_ENV_ADDR,
- CONFIG_ENV_ADDR + CONFIG_ENV_SIZE - 1,
- &flash_info[0]);
-#endif
-
-#if defined(CONFIG_ENV_IS_IN_FLASH) && defined(CONFIG_ENV_ADDR_REDUND)
- flash_protect (FLAG_PROTECT_SET,
- CONFIG_ENV_ADDR_REDUND,
- CONFIG_ENV_ADDR_REDUND + CONFIG_ENV_SIZE_REDUND - 1,
- &flash_info[0]);
-#endif
-
- return (size_b0);
-}
-
-/*-----------------------------------------------------------------------
- */
-void flash_print_info (flash_info_t * info)
-{
- int i;
-
- if (info->flash_id == FLASH_UNKNOWN) {
- printf ("missing or unknown FLASH type\n");
- return;
- }
-
- switch (info->flash_id & FLASH_VENDMASK) {
- case FLASH_MAN_AMD:
- printf ("AMD ");
- break;
- case FLASH_MAN_FUJ:
- printf ("FUJITSU ");
- break;
- default:
- printf ("Unknown Vendor ");
- break;
- }
-
- switch (info->flash_id & FLASH_TYPEMASK) {
- case FLASH_AM800T:
- printf ("29LV800T (8 M, top sector)\n");
- break;
- case FLASH_AM800B:
- printf ("29LV800T (8 M, bottom sector)\n");
- break;
- case FLASH_AM160T:
- printf ("29LV160T (16 M, top sector)\n");
- break;
- case FLASH_AM160B:
- printf ("29LV160B (16 M, bottom sector)\n");
- break;
- case FLASH_AMDL322T:
- printf ("29DL322T (32 M, top sector)\n");
- break;
- case FLASH_AMDL322B:
- printf ("29DL322B (32 M, bottom sector)\n");
- break;
- case FLASH_AMDL323T:
- printf ("29DL323T (32 M, top sector)\n");
- break;
- case FLASH_AMDL323B:
- printf ("29DL323B (32 M, bottom sector)\n");
- break;
- case FLASH_AM640U:
- printf ("29LV640D (64 M, uniform sector)\n");
- break;
- default:
- printf ("Unknown Chip Type\n");
- break;
- }
-
- printf (" Size: %ld MB in %d Sectors\n",
- info->size >> 20, info->sector_count);
-
- printf (" Sector Start Addresses:");
- for (i = 0; i < info->sector_count; ++i) {
- if ((i % 5) == 0)
- printf ("\n ");
- printf (" %08lX%s",
- info->start[i],
- info->protect[i] ? " (RO)" : " "
- );
- }
- printf ("\n");
- return;
-}
-
-/*-----------------------------------------------------------------------
- */
-int flash_erase (flash_info_t * info, int s_first, int s_last)
-{
- int flag, prot, sect, l_sect;
- ulong start, now, last;
-
- if ((s_first < 0) || (s_first > s_last)) {
- if (info->flash_id == FLASH_UNKNOWN) {
- printf ("- missing\n");
- } else {
- printf ("- no sectors to erase\n");
- }
- return 1;
- }
-
- prot = 0;
- for (sect = s_first; sect <= s_last; sect++) {
- if (info->protect[sect])
- prot++;
- }
-
- if (prot) {
- printf ("- Warning: %d protected sectors will not be erased!\n",
- prot);
- } else {
- printf ("\n");
- }
-
- l_sect = -1;
-
- /* Disable interrupts which might cause a timeout here */
- flag = disable_interrupts ();
-
- V_ULONG (info->start[0] + (0x0555 << 3)) = 0x00AA00AA;
- V_ULONG (info->start[0] + (0x02AA << 3)) = 0x00550055;
- V_ULONG (info->start[0] + (0x0555 << 3)) = 0x00800080;
- V_ULONG (info->start[0] + (0x0555 << 3)) = 0x00AA00AA;
- V_ULONG (info->start[0] + (0x02AA << 3)) = 0x00550055;
- V_ULONG (info->start[0] + 4 + (0x0555 << 3)) = 0x00AA00AA;
- V_ULONG (info->start[0] + 4 + (0x02AA << 3)) = 0x00550055;
- V_ULONG (info->start[0] + 4 + (0x0555 << 3)) = 0x00800080;
- V_ULONG (info->start[0] + 4 + (0x0555 << 3)) = 0x00AA00AA;
- V_ULONG (info->start[0] + 4 + (0x02AA << 3)) = 0x00550055;
- udelay (1000);
-
- /* Start erase on unprotected sectors */
- for (sect = s_first; sect <= s_last; sect++) {
- if (info->protect[sect] == 0) { /* not protected */
- V_ULONG (info->start[sect]) = 0x00300030;
- V_ULONG (info->start[sect] + 4) = 0x00300030;
- l_sect = sect;
- }
- }
-
- /* re-enable interrupts if necessary */
- if (flag)
- enable_interrupts ();
-
- /* wait at least 80us - let's wait 1 ms */
- udelay (1000);
-
- /*
- * We wait for the last triggered sector
- */
- if (l_sect < 0)
- goto DONE;
-
- start = get_timer (0);
- last = start;
- while ((V_ULONG (info->start[l_sect]) & 0x00800080) != 0x00800080 ||
- (V_ULONG (info->start[l_sect] + 4) & 0x00800080) != 0x00800080)
- {
- if ((now = get_timer (start)) > CONFIG_SYS_FLASH_ERASE_TOUT) {
- printf ("Timeout\n");
- return 1;
- }
- /* show that we're waiting */
- if ((now - last) > 1000) { /* every second */
- serial_putc ('.');
- last = now;
- }
- }
-
- DONE:
- /* reset to read mode */
- flash_reset ();
-
- printf (" done\n");
- return 0;
-}
-
-static int write_dword (flash_info_t *, ulong, unsigned char *);
-
-/*-----------------------------------------------------------------------
- * Copy memory to flash, returns:
- * 0 - OK
- * 1 - write timeout
- * 2 - Flash not erased
- */
-
-int write_buff (flash_info_t * info, uchar * src, ulong addr, ulong cnt)
-{
- ulong dp;
- static unsigned char bb[8];
- int i, l, rc, cc = cnt;
-
- dp = (addr & ~7); /* get lower dword aligned address */
-
- /*
- * handle unaligned start bytes
- */
- if ((l = addr - dp) != 0) {
- for (i = 0; i < 8; i++)
- bb[i] = (i < l || (i - l) >= cc) ? V_BYTE (dp + i) : *src++;
- if ((rc = write_dword (info, dp, bb)) != 0) {
- return (rc);
- }
- dp += 8;
- cc -= 8 - l;
- }
-
- /*
- * handle word aligned part
- */
- while (cc >= 8) {
- if ((rc = write_dword (info, dp, src)) != 0) {
- return (rc);
- }
- dp += 8;
- src += 8;
- cc -= 8;
- }
-
- if (cc <= 0) {
- return (0);
- }
-
- /*
- * handle unaligned tail bytes
- */
- for (i = 0; i < 8; i++) {
- bb[i] = (i < cc) ? *src++ : V_BYTE (dp + i);
- }
- return (write_dword (info, dp, bb));
-}
-
-/*-----------------------------------------------------------------------
- * Write a dword to Flash, returns:
- * 0 - OK
- * 1 - write timeout
- * 2 - Flash not erased
- */
-static int write_dword (flash_info_t * info, ulong dest, unsigned char *pdata)
-{
- ulong start, cl, ch;
- int flag, i;
-
- for (ch = 0, i = 0; i < 4; i++)
- ch = (ch << 8) + *pdata++; /* high word */
- for (cl = 0, i = 0; i < 4; i++)
- cl = (cl << 8) + *pdata++; /* low word */
-
- /* Check if Flash is (sufficiently) erased */
- if ((*((vu_long *) dest) & ch) != ch
- || (*((vu_long *) (dest + 4)) & cl) != cl) {
- return (2);
- }
-
- /* Disable interrupts which might cause a timeout here */
- flag = disable_interrupts ();
-
- V_ULONG (info->start[0] + (0x0555 << 3)) = 0x00AA00AA;
- V_ULONG (info->start[0] + (0x02AA << 3)) = 0x00550055;
- V_ULONG (info->start[0] + (0x0555 << 3)) = 0x00A000A0;
- V_ULONG (dest) = ch;
- V_ULONG (info->start[0] + 4 + (0x0555 << 3)) = 0x00AA00AA;
- V_ULONG (info->start[0] + 4 + (0x02AA << 3)) = 0x00550055;
- V_ULONG (info->start[0] + 4 + (0x0555 << 3)) = 0x00A000A0;
- V_ULONG (dest + 4) = cl;
-
- /* re-enable interrupts if necessary */
- if (flag)
- enable_interrupts ();
-
- /* data polling for D7 */
- start = get_timer (0);
- while (((V_ULONG (dest) & 0x00800080) != (ch & 0x00800080)) ||
- ((V_ULONG (dest + 4) & 0x00800080) != (cl & 0x00800080))) {
- if (get_timer (start) > CONFIG_SYS_FLASH_WRITE_TOUT) {
- return (1);
- }
- }
- return (0);
-}
diff --git a/include/configs/TQM8260.h b/include/configs/TQM8260.h
index 533c7ad..ac9c94e 100644
--- a/include/configs/TQM8260.h
+++ b/include/configs/TQM8260.h
@@ -300,6 +300,13 @@
#define CONFIG_SYS_FLASH_ERASE_TOUT 240000 /* Flash Erase Timeout (in ms) */
#define CONFIG_SYS_FLASH_WRITE_TOUT 500 /* Flash Write Timeout (in ms) */
+/* use CFI flash driver */
+#define CONFIG_SYS_FLASH_CFI 1 /* Flash is CFI conformant */
+#define CONFIG_FLASH_CFI_DRIVER 1 /* Use the common driver */
+#define CONFIG_SYS_FLASH_BANKS_LIST { CONFIG_SYS_FLASH_BASE }
+#define CONFIG_SYS_FLASH_EMPTY_INFO 1
+#define CONFIG_SYS_FLASH_USE_BUFFER_WRITE 1
+
#define CONFIG_ENV_IS_IN_FLASH 1
#define CONFIG_ENV_ADDR (CONFIG_SYS_FLASH_BASE + 0x40000)
#define CONFIG_ENV_SIZE 0x08000
--
1.5.4.rc1.23.g3a969
1
1