diff options
464 files changed, 42571 insertions, 27005 deletions
@@ -126,7 +126,7 @@ D: Palmtreo680 board, docg4 nand flash driver N: Dave Ellis E: DGE@sixnetio.com -D: EEPROM Speedup, SXNI855T port +D: EEPROM Speedup N: Daniel Engstr?m E: daniel@omicron.se @@ -12,13 +12,53 @@ config KCONFIG_OBJDIR string option env="KCONFIG_OBJDIR" -config DEFCONFIG_LIST - string +menu "General setup" + +config LOCALVERSION + string "Local version - append to U-Boot release" depends on !SPL_BUILD - option defconfig_list - default "configs/sandbox_defconfig" + help + Append an extra string to the end of your U-Boot version. + This will show up on your boot log, for example. + The string you set here will be appended after the contents of + any files with a filename matching localversion* in your + object and source tree, in that order. Your total string can + be a maximum of 64 characters. -menu "General setup" +config LOCALVERSION_AUTO + bool "Automatically append version information to the version string" + depends on !SPL_BUILD + default y + help + This will try to automatically determine if the current tree is a + release tree by looking for git tags that belong to the current + top of tree revision. + + A string of the format -gxxxxxxxx will be added to the localversion + if a git-based tree is found. The string generated by this will be + appended after any matching localversion* files, and after the value + set in CONFIG_LOCALVERSION. + + (The actual string used here is the first eight characters produced + by running the command: + + $ git rev-parse --verify HEAD + + which is done within the script "scripts/setlocalversion".) + +config CC_OPTIMIZE_FOR_SIZE + bool "Optimize for size" + depends on !SPL_BUILD + default y + help + Enabling this option will pass "-Os" instead of "-O2" to gcc + resulting in a smaller U-Boot image. + + This option is enabled by default for U-Boot. + +endmenu # General setup + +menu "Boot images" config SPL_BUILD bool @@ -60,6 +100,6 @@ config SYS_EXTRA_OPTIONS configuration to Kconfig. Since this option will be removed sometime, new boards should not use this option. -endmenu # General setup +endmenu # Boot images source "arch/Kconfig" diff --git a/MAINTAINERS b/MAINTAINERS new file mode 100644 index 0000000..af194ca --- /dev/null +++ b/MAINTAINERS @@ -0,0 +1,405 @@ +Descriptions of section entries: + + P: Person (obsolete) + M: Mail patches to: FullName <address@domain> + L: Mailing list that is relevant to this area + W: Web-page with status/info + Q: Patchwork web based patch tracking system site + T: SCM tree type and location. + Type is one of: git, hg, quilt, stgit, topgit + S: Status, one of the following: + Supported: Someone is actually paid to look after this. + Maintained: Someone actually looks after it. + Odd Fixes: It has a maintainer but they don't have time to do + much other than throw the odd patch in. See below.. + Orphan: No current maintainer [but maybe you could take the + role as you write your new code]. + Obsolete: Old code. Something tagged obsolete generally means + it has been replaced by a better system and you + should be using that. + F: Files and directories with wildcard patterns. + A trailing slash includes all files and subdirectory files. + F: drivers/net/ all files in and below drivers/net + F: drivers/net/* all files in drivers/net, but not below + F: */net/* all files in "any top level directory"/net + One pattern per line. Multiple F: lines acceptable. + N: Files and directories with regex patterns. + N: [^a-z]tegra all files whose path contains the word tegra + One pattern per line. Multiple N: lines acceptable. + scripts/get_maintainer.pl has different behavior for files that + match F: pattern and matches of N: patterns. By default, + get_maintainer will not look at git log history when an F: pattern + match occurs. When an N: match occurs, git log history is used + to also notify the people that have git commit signatures. + X: Files and directories that are NOT maintained, same rules as F: + Files exclusions are tested before file matches. + Can be useful for excluding a specific subdirectory, for instance: + F: net/ + X: net/ipv6/ + matches all files in and below net excluding net/ipv6/ + K: Keyword perl extended regex pattern to match content in a + patch or file. For instance: + K: of_get_profile + matches patches or files that contain "of_get_profile" + K: \b(printk|pr_(info|err))\b + matches patches or files that contain one or more of the words + printk, pr_info or pr_err + One regex pattern per line. Multiple K: lines acceptable. + +Note: For the hard of thinking, this list is meant to remain in alphabetical +order. If you could add yourselves to it in alphabetical order that would be +so much easier [Ed] + +Maintainers List (try to look for most precise areas first) + + ----------------------------------- +ARC +M: Alexey Brodkin <alexey.brodkin@synopsys.com> +S: Maintained +T: git git://git.denx.de/u-boot-arc.git +F: arch/arc/ + +ARM +M: Albert Aribaud <albert.u.boot@aribaud.net> +S: Maintained +T: git git://git.denx.de/u-boot-arm.git +F: arch/arm/ + +ARM ATMEL AT91 +M: Andreas Bießmann <andreas.devel@googlemail.com> +S: Maintained +T: git git://git.denx.de/u-boot-atmel.git +F: arch/arm/cpu/armv7/at91/ +F: arch/arm/cpu/at91-common/ +F: arch/arm/include/asm/arch-at91/ + +ARM FREESCALE IMX +M: Stefano Babic <sbabic@denx.de> +S: Maintained +T: git git://git.denx.de/u-boot-imx.git +F: arch/arm/cpu/arm1136/mx*/ +F: arch/arm/cpu/arm926ejs/mx*/ +F: arch/arm/cpu/arm926ejs/imx/ +F: arch/arm/cpu/armv7/mx*/ +F: arch/arm/cpu/armv7/vf610/ +F: arch/arm/cpu/imx-common/ +F: arch/arm/include/asm/arch-imx/ +F: arch/arm/include/asm/arch-mx*/ +F: arch/arm/include/asm/arch-vf610/ +F: arch/arm/include/asm/imx-common/ + +ARM MARVELL KIRKWOOD +M: Prafulla Wadaskar <prafulla@marvell.com> +S: Maintained +T: git git://git.denx.de/u-boot-marvell.git +F: arch/arm/cpu/arm926ejs/kirkwood/ +F: arch/arm/include/asm/arch-kirkwood/ + +ARM MARVELL PXA +M: Marek Vasut <marex@denx.de> +S: Maintained +T: git git://git.denx.de/u-boot-pxa.git +F: arch/arm/cpu/pxa/ +F: arch/arm/include/asm/arch-pxa/ + +ARM SAMSUNG +M: Minkyu Kang <mk7.kang@samsung.com> +S: Maintained +T: git git://git.denx.de/u-boot-samsung.git +F: arch/arm/cpu/arm920t/s3c24x0/ +F: arch/arm/cpu/armv7/exynos/ +F: arch/arm/cpu/armv7/s5pc1xx/ +F: arch/arm/cpu/armv7/s5p-common/ +F: arch/arm/include/asm/arch-exynos/ +F: arch/arm/include/asm/arch-s3c24x0/ +F: arch/arm/include/asm/arch-s5pc1xx/ + +ARM STM SPEAR +M: Vipin Kumar <vipin.kumar@st.com> +S: Maintained +T: git git://git.denx.de/u-boot-stm.git +F: arch/arm/cpu/arm926ejs/spear/ +F: arch/arm/include/asm/arch-spear/ + +ARM SUNXI +M: Ian Campbell <ijc@hellion.org.uk> +M: Hans De Goede <hdegoede@redhat.com> +S: Maintained +T: git git://git.denx.de/u-boot-sunxi.git +F: arch/arm/cpu/armv7/sunxi/ +F: arch/arm/include/asm/arch-sunxi/ + +ARM TEGRA +M: Tom Warren <twarren@nvidia.com> +S: Maintained +T: git git://git.denx.de/u-boot-tegra.git +F: arch/arm/cpu/arm720t/tegra*/ +F: arch/arm/cpu/armv7/tegra*/ +F: arch/arm/cpu/tegra*/ +F: arch/arm/include/asm/arch-tegra*/ + +ARM TI +M: Tom Rini <trini@ti.com> +S: Maintained +T: git git://git.denx.de/u-boot-ti.git +F: arch/arm/cpu/arm926ejs/davinci/ +F: arch/arm/cpu/arm926ejs/omap/ +F: arch/arm/cpu/armv7/omap*/ +F: arch/arm/include/asm/arch-davinci/ +F: arch/arm/include/asm/arch-omap*/ +F: arch/arm/include/asm/ti-common/ + +ARM ZYNQ +M: Michal Simek <monstr@monstr.eu> +S: Maintained +F: arch/arm/cpu/armv7/zynq/ +F: arch/arm/include/asm/arch-zynq/ + +AVR32 +M: Andreas Bießmann <andreas.devel@googlemail.com> +S: Maintained +T: git git://git.denx.de/u-boot-avr32.git +F: arch/avr32/ + +BLACKFIN +M: Sonic Zhang <sonic.adi@gmail.com> +S: Maintained +T: git git://git.denx.de/u-boot-blackfin.git +F: arch/blackfin/ + +BUILDMAN +M: Simon Glass <sjg@chromium.org> +S: Maintained +F: tools/buildman/ + +CFI FLASH +M: Stefan Roese <sr@denx.de> +S: Maintained +T: git git://git.denx.de/u-boot-cfi-flash.git +F: drivers/mtd/* + +COLDFIRE +M: Jason Jin <jason.jin@freescale.com> +S: Maintained +T: git git://git.denx.de/u-boot-coldfire.git +F: arch/m68k/ + +DFU +M: Lukasz Majewski <l.majewski@samsung.com> +S: Maintained +T: git git://git.denx.de/u-boot-dfu.git +F: drivers/dfu/ + +DRIVER MODEL +M: Simon Glass <sjg@chromium.org> +S: Maintained +F: drivers/core/ +F: include/dm/ +F: test/dm/ + +FLATTENED DEVICE TREE +M: Simon Glass <sjg@chromium.org> +S: Maintained +T: git git://git.denx.de/u-boot-fdt.git +F: lib/fdtdec* +F: lib/libfdt/ +F: include/fdt* +F: include/libfdt* +F. common/cmd_fdt.c +F: common/fdt_support.c + +FREEBSD +M: Rafal Jaworowski <raj@semihalf.com> +S: Maintained +T: git git://git.denx.de/u-boot-freebsd.git + +FREESCALE QORIQ +M: York Sun <yorksun@freescale.com> +S: Maintained +T: git git://git.denx.de/u-boot-fsl-qoriq.git + +I2C +M: Heiko Schocher <hs@denx.de> +S: Maintained +T: git git://git.denx.de/u-boot-i2c.git +F: drivers/i2c/ + +MICROBLAZE +M: Michal Simek <monstr@monstr.eu> +S: Maintained +T: git git://git.denx.de/u-boot-microblaze.git +F: arch/microblaze/ + +MIPS +M: Daniel Schwierzeck <daniel.schwierzeck@gmail.com> +S: Maintained +T: git git://git.denx.de/u-boot-mips.git +F: arch/mips/ + +MMC +M: Pantelis Antoniou <panto.antoniou-consulting.com> +S: Maintained +T: git git://git.denx.de/u-boot-mmc.git +F: drivers/mmc/ + +OPENRISC +M: Stefan Kristiansson <stefan.kristiansson@saunalahti.fi> +S: Maintained +F: arch/openrisc/ + +PATMAN +M: Simon Glass <sjg@chromium.org> +S: Maintained +F: tools/patman/ + +POWERPC +M: Wolfgang Denk <wd@denx.de> +S: Maintained +F: arch/powerpc/ + +POWERPC MPC5XXX +M: Wolfgang Denk <wd@denx.de> +S: Maintained +T: git git://git.denx.de/u-boot-mpc5xxx.git +F: arch/powerpc/cpu/mpc5*/ + +POWERPC MPC8XX +M: Wolfgang Denk <wd@denx.de> +S: Maintained +T: git git://git.denx.de/u-boot-mpc8xx.git +F: arch/powerpc/cpu/mpc8xx/ + +POWERPC MPC82XX +M: Wolfgang Denk <wd@denx.de> +S: Maintained +T: git git://git.denx.de/u-boot-mpc82xx.git +F: arch/powerpc/cpu/mpc82*/ + +POWERPC MPC83XX +M: Kim Phillips <kim.phillips@freescale.com> +S: Maintained +T: git git://git.denx.de/u-boot-mpc83xx.git +F: arch/powerpc/cpu/mpc83xx/ +F: arch/powerpc/include/asm/arch-mpc83xx/ + +POWERPC MPC85XX +M: York Sun <yorksun@freescale.com> +S: Maintained +T: git git://git.denx.de/u-boot-mpc85xx.git +F: arch/powerpc/cpu/mpc85xx/ + +POWERPC MPC86XX +M: York Sun <yorksun@freescale.com> +S: Maintained +T: git git://git.denx.de/u-boot-mpc86xx.git +F: arch/powerpc/cpu/mpc86xx/ + +POWERPC PPC74XX PPC7XX +M: Wolfgang Denk <wd@denx.de> +S: Maintained +T: git git://git.denx.de/u-boot-74xx-7xx.git +F: arch/powerpc/cpu/74xx_7xx/ + +POWERPC PPC4XX +M: Stefan Roese <sr@denx.de> +S: Maintained +T: git git://git.denx.de/u-boot-ppc4xx.git +F: arch/powerpc/cpu/ppc4xx/ + +NETWORK +M: Joe Hershberger <joe.hershberger@gmail.com> +S: Maintained +T: git git://git.denx.de/u-boot-net.git +F: drivers/net/ + +NAND FLASH +M: Scott Wood <scottwood@freescale.com> +S: Maintained +T: git git://git.denx.de/u-boot-nand-flash.git +F: drivers/mtd/nand/ + +NDS32 +M: Macpaul Lin <macpaul@andestech.com> +S: Maintained +T: git git://git.denx.de/u-boot-nds32.git +F: arch/nds32/ + +NIOS +M: Thomas Chou <thomas@wytron.com.tw> +S: Maintained +T: git git://git.denx.de/u-boot-nios.git +F: arch/nios2/ + +ONENAND +M: Lukasz Majewski <l.majewski@samsung.com> +S: Maintained +T: git git://git.denx.de/u-boot-onenand.git +F: drivers/mtd/onenand/ + +SANDBOX +M: Simon Glass <sjg@chromium.org> +S: Maintained +F: arch/sandbox/ + +SH +M: Nobuhiro Iwamatsu <iwamatsu@nigauri.org> +S: Maintained +T: git git://git.denx.de/u-boot-sh.git +F: arch/sh/ + +SPARC +M: Daniel Hellstrom <daniel@gaisler.com> +S: Maintained +T: git git://git.denx.de/u-boot-sparc.git +F: arch/sparc/ + +SPI +M: Jagannadha Sutradharudu Teki <jagannadh.teki@gmail.com> +S: Maintained +T: git git://git.denx.de/u-boot-spi.git +F: drivers/mtd/spi/ +F: drivers/spi/ +F: include/spi* + +TESTING +M: Detlev Zundel <dzu@denx.de> +S: Maintained +T: git git://git.denx.de/u-boot-testing.git + +TQ GROUP +M: Martin Krause <martin.krause@tq-systems.de> +S: Maintained +T: git git://git.denx.de/u-boot-tq-group.git + +UBI +M: Kyungmin Park <kmpark@infradead.org> +S: Maintained +T: git git://git.denx.de/u-boot-ubi.git +F: drivers/mtd/ubi/ + +USB +M: Marek Vasut <marex@denx.de> +S: Maintained +T: git git://git.denx.de/u-boot-usb.git +F: drivers/usb/ + +VIDEO +M: Anatolij Gustschin <agust@denx.de> +S: Maintained +T: git git://git.denx.de/u-boot-video.git +F: drivers/video/ + +X86 +M: Simon Glass <sjg@chromium.org> +S: Maintained +T: git git://git.denx.de/u-boot-x86.git +F: arch/x86/ + +THE REST +M: Tom Rini <trini@ti.com> +L: u-boot@lists.denx.de +Q: http://patchwork.ozlabs.org/project/uboot/list/ +S: Maintained +T: git git://git.denx.de/u-boot.git +F: * +F: */ @@ -171,13 +171,10 @@ GNU_MAKE=$(scripts/show-gnu-make) || { # echo "Remaining arguments:" # for arg do echo '--> '"\`$arg'" ; done -if [ ! -r boards.cfg ]; then - echo "Could not find boards.cfg" - tools/genboardscfg.py || { - echo "Failed to generate boards.cfg" >&2 - exit 1 - } -fi +tools/genboardscfg.py || { + echo "Failed to generate boards.cfg" >&2 + exit 1 +} FILTER="\$1 !~ /^#/" [ "$opt_a" ] && FILTER="${FILTER} && $opt_a" @@ -658,7 +655,7 @@ build_target() { MAKE="${MAKE} O=${output_dir}" fi - ${MAKE} distclean >/dev/null + ${MAKE} mrproper >/dev/null echo "Building ${target} board..." ${MAKE} -s ${target}_defconfig >/dev/null @@ -109,10 +109,6 @@ ifeq ("$(origin O)", "command line") KBUILD_OUTPUT := $(O) endif -ifeq ("$(origin W)", "command line") - export KBUILD_ENABLE_EXTRA_GCC_CHECKS := $(W) -endif - # That's our default target when none is given on the command line PHONY := _all _all: @@ -441,12 +437,12 @@ ifeq ($(mixed-targets),1) # We're called with mixed targets (*config and build targets). # Handle them one by one. -PHONY += $(MAKECMDGOALS) build-one-by-one +PHONY += $(MAKECMDGOALS) __build_one_by_one -$(MAKECMDGOALS): build-one-by-one +$(filter-out __build_one_by_one, $(MAKECMDGOALS)): __build_one_by_one @: -build-one-by-one: +__build_one_by_one: $(Q)set -e; \ for i in $(MAKECMDGOALS); do \ $(MAKE) -f $(srctree)/Makefile $$i; \ @@ -462,10 +458,10 @@ KBUILD_DEFCONFIG := sandbox_defconfig export KBUILD_DEFCONFIG KBUILD_KCONFIG config: scripts_basic outputmakefile FORCE - +$(Q)$(PYTHON) $(srctree)/scripts/multiconfig.py $@ + (Q)$(MAKE) $(build)=scripts/kconfig $@ %config: scripts_basic outputmakefile FORCE - +$(Q)$(PYTHON) $(srctree)/scripts/multiconfig.py $@ + +$(Q)$(CONFIG_SHELL) $(srctree)/scripts/multiconfig.sh $@ else # =========================================================================== @@ -533,7 +529,11 @@ else include/config/auto.conf: ; endif # $(dot-config) -KBUILD_CFLAGS += -Os #-fomit-frame-pointer +ifdef CONFIG_CC_OPTIMIZE_FOR_SIZE +KBUILD_CFLAGS += -Os +else +KBUILD_CFLAGS += -O2 +endif ifdef BUILD_TAG KBUILD_CFLAGS += -DBUILD_TAG='"$(BUILD_TAG)"' @@ -569,6 +569,8 @@ endif export CONFIG_SYS_TEXT_BASE +include $(srctree)/scripts/Makefile.extrawarn + # Add user supplied CPPFLAGS, AFLAGS and CFLAGS as the last assignments KBUILD_CPPFLAGS += $(KCPPFLAGS) KBUILD_AFLAGS += $(KAFLAGS) @@ -802,14 +804,15 @@ u-boot.hex u-boot.srec: u-boot FORCE OBJCOPYFLAGS_u-boot.bin := -O binary -binary_size_check: u-boot.bin System.map FORCE - @file_size=`stat -c %s u-boot.bin` ; \ - map_size=$(shell cat System.map | \ +binary_size_check: u-boot.bin FORCE + @file_size=$(shell wc -c u-boot.bin | awk '{print $$1}') ; \ + map_size=$(shell cat u-boot.map | \ awk '/_image_copy_start/ {start = $$1} /_image_binary_end/ {end = $$1} END {if (start != "" && end != "") print "ibase=16; " toupper(end) " - " toupper(start)}' \ + | sed 's/0X//g' \ | bc); \ if [ "" != "$$map_size" ]; then \ if test $$map_size -ne $$file_size; then \ - echo "System.map shows a binary size of $$map_size" >&2 ; \ + echo "u-boot.map shows a binary size of $$map_size" >&2 ; \ echo " but u-boot.bin shows $$file_size" >&2 ; \ exit 1; \ fi \ @@ -1005,13 +1008,17 @@ quiet_cmd_u-boot__ ?= LD $@ --start-group $(u-boot-main) --end-group \ $(PLATFORM_LIBS) -Map u-boot.map -u-boot: $(u-boot-init) $(u-boot-main) u-boot.lds - $(call if_changed,u-boot__) -ifeq ($(CONFIG_KALLSYMS),y) +quiet_cmd_smap = GEN common/system_map.o +cmd_smap = \ smap=`$(call SYSTEM_MAP,u-boot) | \ awk '$$2 ~ /[tTwW]/ {printf $$1 $$3 "\\\\000"}'` ; \ $(CC) $(c_flags) -DSYSTEM_MAP="\"$${smap}\"" \ -c $(srctree)/common/system_map.c -o common/system_map.o + +u-boot: $(u-boot-init) $(u-boot-main) u-boot.lds + $(call if_changed,u-boot__) +ifeq ($(CONFIG_KALLSYMS),y) + $(call cmd,smap) $(call cmd,u-boot__) common/system_map.o endif @@ -1286,6 +1293,7 @@ distclean: mrproper -o -name '.*.rej' -o -name '*%' -o -name 'core' \ -o -name '*.pyc' \) \ -type f -print | xargs rm -f + @rm -f boards.cfg backup: F=`basename $(srctree)` ; cd .. ; \ @@ -1152,6 +1152,7 @@ The following options need to be configured: CONFIG_RTC_DS1307 - use Maxim, Inc. DS1307 RTC CONFIG_RTC_DS1337 - use Maxim, Inc. DS1337 RTC CONFIG_RTC_DS1338 - use Maxim, Inc. DS1338 RTC + CONFIG_RTC_DS1339 - use Maxim, Inc. DS1339 RTC CONFIG_RTC_DS164x - use Dallas DS164x RTC CONFIG_RTC_ISL1208 - use Intersil ISL1208 RTC CONFIG_RTC_MAX6900 - use Maxim, Inc. MAX6900 RTC @@ -1377,6 +1378,10 @@ The following options need to be configured: CONFIG_SH_ETHER_CACHE_WRITEBACK If this option is set, the driver enables cache flush. +- PWM Support: + CONFIG_PWM_IMX + Support for PWM modul on the imx6. + - TPM Support: CONFIG_TPM Support TPM devices. @@ -2036,6 +2041,24 @@ CBFS (Coreboot Filesystem) support 4th and following BOOTP requests: delay 0 ... 8 sec + CONFIG_BOOTP_ID_CACHE_SIZE + + BOOTP packets are uniquely identified using a 32-bit ID. The + server will copy the ID from client requests to responses and + U-Boot will use this to determine if it is the destination of + an incoming response. Some servers will check that addresses + aren't in use before handing them out (usually using an ARP + ping) and therefore take up to a few hundred milliseconds to + respond. Network congestion may also influence the time it + takes for a response to make it back to the client. If that + time is too long, U-Boot will retransmit requests. In order + to allow earlier responses to still be accepted after these + retransmissions, U-Boot's BOOTP client keeps a small cache of + IDs. The CONFIG_BOOTP_ID_CACHE_SIZE controls the size of this + cache. The default is to keep IDs for up to four outstanding + requests. Increasing this will allow U-Boot to accept offers + from a BOOTP client in networks with unusually high latency. + - DHCP Advanced Options: You can fine tune the DHCP functionality by defining CONFIG_BOOTP_* symbols: @@ -2930,6 +2953,17 @@ CBFS (Coreboot Filesystem) support memories can be connected with a given cs line. currently Xilinx Zynq qspi support these type of connections. + CONFIG_SYS_SPI_ST_ENABLE_WP_PIN + enable the W#/Vpp signal to disable writing to the status + register on ST MICRON flashes like the N25Q128. + The status register write enable/disable bit, combined with + the W#/VPP signal provides hardware data protection for the + device as follows: When the enable/disable bit is set to 1, + and the W#/VPP signal is driven LOW, the status register + nonvolatile bits become read-only and the WRITE STATUS REGISTER + operation will not execute. The only way to exit this + hardware-protected mode is to drive W#/VPP HIGH. + - SystemACE Support: CONFIG_SYSTEMACE @@ -3319,6 +3353,9 @@ FIT uImage format: Adds the MTD partitioning infrastructure from the Linux kernel. Needed for UBI support. + CONFIG_MTD_NAND_VERIFY_WRITE + verify if the written data is correct reread. + - UBI support CONFIG_CMD_UBI @@ -3332,6 +3369,64 @@ FIT uImage format: Make the verbose messages from UBI stop printing. This leaves warnings and errors enabled. + + CONFIG_MTD_UBI_WL_THRESHOLD + This parameter defines the maximum difference between the highest + erase counter value and the lowest erase counter value of eraseblocks + of UBI devices. When this threshold is exceeded, UBI starts performing + wear leveling by means of moving data from eraseblock with low erase + counter to eraseblocks with high erase counter. + + The default value should be OK for SLC NAND flashes, NOR flashes and + other flashes which have eraseblock life-cycle 100000 or more. + However, in case of MLC NAND flashes which typically have eraseblock + life-cycle less than 10000, the threshold should be lessened (e.g., + to 128 or 256, although it does not have to be power of 2). + + default: 4096 + + CONFIG_MTD_UBI_BEB_LIMIT + This option specifies the maximum bad physical eraseblocks UBI + expects on the MTD device (per 1024 eraseblocks). If the + underlying flash does not admit of bad eraseblocks (e.g. NOR + flash), this value is ignored. + + NAND datasheets often specify the minimum and maximum NVM + (Number of Valid Blocks) for the flashes' endurance lifetime. + The maximum expected bad eraseblocks per 1024 eraseblocks + then can be calculated as "1024 * (1 - MinNVB / MaxNVB)", + which gives 20 for most NANDs (MaxNVB is basically the total + count of eraseblocks on the chip). + + To put it differently, if this value is 20, UBI will try to + reserve about 1.9% of physical eraseblocks for bad blocks + handling. And that will be 1.9% of eraseblocks on the entire + NAND chip, not just the MTD partition UBI attaches. This means + that if you have, say, a NAND flash chip admits maximum 40 bad + eraseblocks, and it is split on two MTD partitions of the same + size, UBI will reserve 40 eraseblocks when attaching a + partition. + + default: 20 + + CONFIG_MTD_UBI_FASTMAP + Fastmap is a mechanism which allows attaching an UBI device + in nearly constant time. Instead of scanning the whole MTD device it + only has to locate a checkpoint (called fastmap) on the device. + The on-flash fastmap contains all information needed to attach + the device. Using fastmap makes only sense on large devices where + attaching by scanning takes long. UBI will not automatically install + a fastmap on old images, but you can set the UBI parameter + CONFIG_MTD_UBI_FASTMAP_AUTOCONVERT to 1 if you want so. Please note + that fastmap-enabled images are still usable with UBI implementations + without fastmap support. On typical flash devices the whole fastmap + fits into one PEB. UBI will reserve PEBs to hold two fastmaps. + + CONFIG_MTD_UBI_FASTMAP_AUTOCONVERT + Set this parameter to enable fastmap automatically on images + without a fastmap. + default: 0 + - UBIFS support CONFIG_CMD_UBIFS @@ -4370,6 +4465,11 @@ use the "saveenv" command to store a valid environment. later, once stdio is running and output goes to the LCD, if present. +- CONFIG_BOARD_SIZE_LIMIT: + Maximum size of the U-Boot image. When defined, the + build system checks that the actual size does not + exceed it. + Low Level (hardware related) configuration options: --------------------------------------------------- diff --git a/api/api_net.c b/api/api_net.c index 3f52d71..7b3805e 100644 --- a/api/api_net.c +++ b/api/api_net.c @@ -25,6 +25,7 @@ DECLARE_GLOBAL_DATA_PTR; #define errf(fmt, args...) do { printf("ERROR @ %s(): ", __func__); printf(fmt, ##args); } while (0) +#ifdef CONFIG_CMD_NET static int dev_valid_net(void *cookie) { @@ -85,3 +86,32 @@ int dev_read_net(void *cookie, void *buf, int len) return eth_receive(buf, len); } + +#else + +int dev_open_net(void *cookie) +{ + return API_ENODEV; +} + +int dev_close_net(void *cookie) +{ + return API_ENODEV; +} + +int dev_enum_net(struct device_info *di) +{ + return 0; +} + +int dev_write_net(void *cookie, void *buf, int len) +{ + return API_ENODEV; +} + +int dev_read_net(void *cookie, void *buf, int len) +{ + return API_ENODEV; +} + +#endif diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index dd987cc..4f26d28 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -287,9 +287,6 @@ config TARGET_SC_SPS_1 config TARGET_NHK8815 bool "Support nhk8815" -config TARGET_OMAP5912OSK - bool "Support omap5912osk" - config TARGET_EDMINIV2 bool "Support edminiv2" @@ -512,6 +509,9 @@ config TARGET_CGTQMX6EVAL config TARGET_EMBESTMX6BOARDS bool "Support embestmx6boards" +config TARGET_ARISTAINETOS + bool "Support aristainetos" + config TARGET_MX6QARM2 bool "Support mx6qarm2" @@ -524,6 +524,9 @@ config TARGET_MX6SABRESD config TARGET_MX6SLEVK bool "Support mx6slevk" +config TARGET_MX6SXSABRESD + bool "Support mx6sxsabresd" + config TARGET_GW_VENTANA bool "Support gw_ventana" @@ -776,6 +779,7 @@ config TARGET_JORNADA endchoice source "board/8dtech/eco5pk/Kconfig" +source "board/aristainetos/Kconfig" source "board/Barix/ipam390/Kconfig" source "board/BuR/kwb/Kconfig" source "board/BuR/tseries/Kconfig" @@ -883,6 +887,7 @@ source "board/freescale/mx6qarm2/Kconfig" source "board/freescale/mx6qsabreauto/Kconfig" source "board/freescale/mx6sabresd/Kconfig" source "board/freescale/mx6slevk/Kconfig" +source "board/freescale/mx6sxsabresd/Kconfig" source "board/freescale/vf610twr/Kconfig" source "board/gateworks/gw_ventana/Kconfig" source "board/genesi/mx51_efikamx/Kconfig" @@ -980,7 +985,6 @@ source "board/ti/beagle/Kconfig" source "board/ti/dra7xx/Kconfig" source "board/ti/evm/Kconfig" source "board/ti/ks2_evm/Kconfig" -source "board/ti/omap5912osk/Kconfig" source "board/ti/omap5_uevm/Kconfig" source "board/ti/panda/Kconfig" source "board/ti/sdp3430/Kconfig" diff --git a/arch/arm/cpu/armv7/mx6/Makefile b/arch/arm/cpu/armv7/mx6/Makefile index 6dc9f8e..bf6effc 100644 --- a/arch/arm/cpu/armv7/mx6/Makefile +++ b/arch/arm/cpu/armv7/mx6/Makefile @@ -10,3 +10,4 @@ obj-y := soc.o clock.o obj-$(CONFIG_SPL_BUILD) += ddr.o obj-$(CONFIG_SECURE_BOOT) += hab.o +obj-$(CONFIG_MP) += mp.o diff --git a/arch/arm/cpu/armv7/mx6/clock.c b/arch/arm/cpu/armv7/mx6/clock.c index 7dd83ec..820b8d5 100644 --- a/arch/arm/cpu/armv7/mx6/clock.c +++ b/arch/arm/cpu/armv7/mx6/clock.c @@ -71,6 +71,24 @@ int enable_i2c_clk(unsigned char enable, unsigned i2c_num) } #endif +/* spi_num can be from 0 - SPI_MAX_NUM */ +int enable_spi_clk(unsigned char enable, unsigned spi_num) +{ + u32 reg; + u32 mask; + + if (spi_num > SPI_MAX_NUM) + return -EINVAL; + + mask = MXC_CCM_CCGR_CG_MASK << (spi_num << 1); + reg = __raw_readl(&imx_ccm->CCGR1); + if (enable) + reg |= mask; + else + reg &= ~mask; + __raw_writel(reg, &imx_ccm->CCGR1); + return 0; +} static u32 decode_pll(enum pll_clocks pll, u32 infreq) { u32 div; @@ -214,7 +232,7 @@ static u32 get_uart_clk(void) u32 reg, uart_podf; u32 freq = decode_pll(PLL_USBOTG, MXC_HCLK) / 6; /* static divider */ reg = __raw_readl(&imx_ccm->cscdr1); -#ifdef CONFIG_MX6SL +#if (defined(CONFIG_MX6SL) || defined(CONFIG_MX6SX)) if (reg & MXC_CCM_CSCDR1_UART_CLK_SEL) freq = MXC_HCLK; #endif @@ -282,7 +300,7 @@ static u32 get_emi_slow_clk(void) return root_freq / (emi_slow_podf + 1); } -#ifdef CONFIG_MX6SL +#if (defined(CONFIG_MX6SL) || defined(CONFIG_MX6SX)) static u32 get_mmdc_ch0_clk(void) { u32 cbcmr = __raw_readl(&imx_ccm->cbcmr); @@ -355,6 +373,27 @@ int enable_fec_anatop_clock(enum enet_freq freq) reg &= ~BM_ANADIG_PLL_ENET_BYPASS; writel(reg, &anatop->pll_enet); +#ifdef CONFIG_MX6SX + /* + * Set enet ahb clock to 200MHz + * pll2_pfd2_396m-> ENET_PODF-> ENET_AHB + */ + reg = readl(&imx_ccm->chsccdr); + reg &= ~(MXC_CCM_CHSCCDR_ENET_PRE_CLK_SEL_MASK + | MXC_CCM_CHSCCDR_ENET_PODF_MASK + | MXC_CCM_CHSCCDR_ENET_CLK_SEL_MASK); + /* PLL2 PFD2 */ + reg |= (4 << MXC_CCM_CHSCCDR_ENET_PRE_CLK_SEL_OFFSET); + /* Div = 2*/ + reg |= (1 << MXC_CCM_CHSCCDR_ENET_PODF_OFFSET); + reg |= (0 << MXC_CCM_CHSCCDR_ENET_CLK_SEL_OFFSET); + writel(reg, &imx_ccm->chsccdr); + + /* Enable enet system clock */ + reg = readl(&imx_ccm->CCGR3); + reg |= MXC_CCM_CCGR3_ENET_MASK; + writel(reg, &imx_ccm->CCGR3); +#endif return 0; } #endif @@ -437,6 +476,7 @@ static int enable_enet_pll(uint32_t en) return 0; } +#ifndef CONFIG_MX6SX static void ungate_sata_clock(void) { struct mxc_ccm_reg *const imx_ccm = @@ -445,6 +485,7 @@ static void ungate_sata_clock(void) /* Enable SATA clock. */ setbits_le32(&imx_ccm->CCGR5, MXC_CCM_CCGR5_SATA_MASK); } +#endif static void ungate_pcie_clock(void) { @@ -455,11 +496,13 @@ static void ungate_pcie_clock(void) setbits_le32(&imx_ccm->CCGR4, MXC_CCM_CCGR4_PCIE_MASK); } +#ifndef CONFIG_MX6SX int enable_sata_clock(void) { ungate_sata_clock(); return enable_enet_pll(BM_ANADIG_PLL_ENET_ENABLE_SATA); } +#endif int enable_pcie_clock(void) { @@ -491,7 +534,9 @@ int enable_pcie_clock(void) clrbits_le32(&ccm_regs->cbcmr, MXC_CCM_CBCMR_PCIE_AXI_CLK_SEL); /* Party time! Ungate the clock to the PCIe. */ +#ifndef CONFIG_MX6SX ungate_sata_clock(); +#endif ungate_pcie_clock(); return enable_enet_pll(BM_ANADIG_PLL_ENET_ENABLE_SATA | @@ -573,6 +618,7 @@ int do_mx6_showclocks(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) return 0; } +#ifndef CONFIG_MX6SX void enable_ipu_clock(void) { struct mxc_ccm_reg *mxc_ccm = (struct mxc_ccm_reg *)CCM_BASE_ADDR; @@ -581,6 +627,7 @@ void enable_ipu_clock(void) reg |= MXC_CCM_CCGR3_IPU1_IPU_MASK; writel(reg, &mxc_ccm->CCGR3); } +#endif /***************************************************/ U_BOOT_CMD( diff --git a/arch/arm/cpu/armv7/mx6/ddr.c b/arch/arm/cpu/armv7/mx6/ddr.c index 0434211..1ab69f6 100644 --- a/arch/arm/cpu/armv7/mx6/ddr.c +++ b/arch/arm/cpu/armv7/mx6/ddr.c @@ -197,6 +197,7 @@ void mx6_dram_cfg(const struct mx6_ddr_sysinfo *i, u16 trcd, trc, tras, twr, tmrd, trtp, trp, twtr, trfc, txs, txpr; u16 CS0_END; u16 tdllk = 0x1ff; /* DLL locking time: 512 cycles (JEDEC DDR3) */ + u8 coladdr; int clkper; /* clock period in picoseconds */ int clock; /* clock freq in mHz */ int cs; @@ -422,8 +423,13 @@ void mx6_dram_cfg(const struct mx6_ddr_sysinfo *i, mmdc0->mdor = reg; /* Step 5: Configure DDR physical parameters (density and burst len) */ + coladdr = m->coladdr; + if (m->coladdr == 8) /* 8-bit COL is 0x3 */ + coladdr += 4; + else if (m->coladdr == 12) /* 12-bit COL is 0x4 */ + coladdr += 1; reg = (m->rowaddr - 11) << 24 | /* ROW */ - (m->coladdr - 9) << 20 | /* COL */ + (coladdr - 9) << 20 | /* COL */ (1 << 19) | /* Burst Length = 8 for DDR3 */ (i->dsize << 16); /* DDR data bus size */ mmdc0->mdctl = reg; diff --git a/arch/arm/cpu/armv7/mx6/mp.c b/arch/arm/cpu/armv7/mx6/mp.c new file mode 100644 index 0000000..9f034d6 --- /dev/null +++ b/arch/arm/cpu/armv7/mx6/mp.c @@ -0,0 +1,87 @@ +/* + * (C) Copyright 2014 + * Gabriel Huau <contact@huau-gabriel.fr> + * + * (C) Copyright 2009 Freescale Semiconductor, Inc. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <asm/io.h> +#include <asm/errno.h> +#include <asm/arch/sys_proto.h> +#include <asm/arch/imx-regs.h> + +#define MAX_CPUS 4 +static struct src *src = (struct src *)SRC_BASE_ADDR; + +static uint32_t cpu_reset_mask[MAX_CPUS] = { + 0, /* We don't really want to modify the cpu0 */ + SRC_SCR_CORE_1_RESET_MASK, + SRC_SCR_CORE_2_RESET_MASK, + SRC_SCR_CORE_3_RESET_MASK +}; + +static uint32_t cpu_ctrl_mask[MAX_CPUS] = { + 0, /* We don't really want to modify the cpu0 */ + SRC_SCR_CORE_1_ENABLE_MASK, + SRC_SCR_CORE_2_ENABLE_MASK, + SRC_SCR_CORE_3_ENABLE_MASK +}; + +int cpu_reset(int nr) +{ + /* Software reset of the CPU N */ + src->scr |= cpu_reset_mask[nr]; + return 0; +} + +int cpu_status(int nr) +{ + printf("core %d => %d\n", nr, !!(src->scr & cpu_ctrl_mask[nr])); + return 0; +} + +int cpu_release(int nr, int argc, char *const argv[]) +{ + uint32_t boot_addr; + + boot_addr = simple_strtoul(argv[0], NULL, 16); + + switch (nr) { + case 1: + src->gpr3 = boot_addr; + break; + case 2: + src->gpr5 = boot_addr; + break; + case 3: + src->gpr7 = boot_addr; + break; + default: + return 1; + } + + /* CPU N is ready to start */ + src->scr |= cpu_ctrl_mask[nr]; + + return 0; +} + +int is_core_valid(unsigned int core) +{ + uint32_t nr_cores = get_nr_cpus(); + + if (core > nr_cores) + return 0; + + return 1; +} + +int cpu_disable(int nr) +{ + /* Disable the CPU N */ + src->scr &= ~cpu_ctrl_mask[nr]; + return 0; +} diff --git a/arch/arm/cpu/armv7/mx6/soc.c b/arch/arm/cpu/armv7/mx6/soc.c index f20bdeb..ac84a1f 100644 --- a/arch/arm/cpu/armv7/mx6/soc.c +++ b/arch/arm/cpu/armv7/mx6/soc.c @@ -35,6 +35,12 @@ struct scu_regs { u32 fpga_rev; }; +u32 get_nr_cpus(void) +{ + struct scu_regs *scu = (struct scu_regs *)SCU_BASE_ADDR; + return readl(&scu->config) & 3; +} + u32 get_cpu_rev(void) { struct anatop_regs *anatop = (struct anatop_regs *)ANATOP_BASE_ADDR; @@ -79,9 +85,15 @@ u32 __weak get_board_rev(void) void init_aips(void) { struct aipstz_regs *aips1, *aips2; +#ifdef CONFIG_MX6SX + struct aipstz_regs *aips3; +#endif aips1 = (struct aipstz_regs *)AIPS1_BASE_ADDR; aips2 = (struct aipstz_regs *)AIPS2_BASE_ADDR; +#ifdef CONFIG_MX6SX + aips3 = (struct aipstz_regs *)AIPS3_BASE_ADDR; +#endif /* * Set all MPROTx to be non-bufferable, trusted for R/W, @@ -107,6 +119,26 @@ void init_aips(void) writel(0x00000000, &aips2->opacr2); writel(0x00000000, &aips2->opacr3); writel(0x00000000, &aips2->opacr4); + +#ifdef CONFIG_MX6SX + /* + * Set all MPROTx to be non-bufferable, trusted for R/W, + * not forced to user-mode. + */ + writel(0x77777777, &aips3->mprot0); + writel(0x77777777, &aips3->mprot1); + + /* + * Set all OPACRx to be non-bufferable, not require + * supervisor privilege level for access,allow for + * write access and untrusted master access. + */ + writel(0x00000000, &aips3->opacr0); + writel(0x00000000, &aips3->opacr1); + writel(0x00000000, &aips3->opacr2); + writel(0x00000000, &aips3->opacr3); + writel(0x00000000, &aips3->opacr4); +#endif } static void clear_ldo_ramp(void) @@ -311,6 +343,10 @@ void s_init(void) u32 mask480; u32 mask528; + + if (is_cpu_type(MXC_CPU_MX6SX)) + return; + /* Due to hardware limitation, on MX6Q we need to gate/ungate all PFDs * to make sure PFD is working right, otherwise, PFDs may * not output clock after reset, MX6DL and MX6SL have added 396M pfd diff --git a/arch/arm/cpu/armv7/sunxi/dram.c b/arch/arm/cpu/armv7/sunxi/dram.c index 0f1ceec..584f742 100644 --- a/arch/arm/cpu/armv7/sunxi/dram.c +++ b/arch/arm/cpu/armv7/sunxi/dram.c @@ -36,18 +36,39 @@ #define CPU_CFG_CHIP_REV_B 0x3 /* - * Wait up to 1s for mask to be clear in given reg. + * Wait up to 1s for value to be set in given part of reg. */ -static void await_completion(u32 *reg, u32 mask) +static void await_completion(u32 *reg, u32 mask, u32 val) { unsigned long tmo = timer_get_us() + 1000000; - while (readl(reg) & mask) { + while ((readl(reg) & mask) != val) { if (timer_get_us() > tmo) panic("Timeout initialising DRAM\n"); } } +/* + * Wait up to 1s for mask to be clear in given reg. + */ +static inline void await_bits_clear(u32 *reg, u32 mask) +{ + await_completion(reg, mask, 0); +} + +/* + * Wait up to 1s for mask to be set in given reg. + */ +static inline void await_bits_set(u32 *reg, u32 mask) +{ + await_completion(reg, mask, mask); +} + +/* + * This performs the external DRAM reset by driving the RESET pin low and + * then high again. According to the DDR3 spec, the RESET pin needs to be + * kept low for at least 200 us. + */ static void mctl_ddr3_reset(void) { struct sunxi_dram_reg *dram = @@ -64,15 +85,28 @@ static void mctl_ddr3_reset(void) if ((reg_val & CPU_CFG_CHIP_VER_MASK) != CPU_CFG_CHIP_VER(CPU_CFG_CHIP_REV_A)) { setbits_le32(&dram->mcr, DRAM_MCR_RESET); - udelay(2); + udelay(200); clrbits_le32(&dram->mcr, DRAM_MCR_RESET); } else #endif { clrbits_le32(&dram->mcr, DRAM_MCR_RESET); - udelay(2); + udelay(200); setbits_le32(&dram->mcr, DRAM_MCR_RESET); } + /* After the RESET pin is de-asserted, the DDR3 spec requires to wait + * for additional 500 us before driving the CKE pin (Clock Enable) + * high. The duration of this delay can be configured in the SDR_IDCR + * (Initialization Delay Configuration Register) and applied + * automatically by the DRAM controller during the DDR3 initialization + * step. But SDR_IDCR has limited range on sun4i/sun5i hardware and + * can't provide sufficient delay at DRAM clock frequencies higher than + * 524 MHz (while Allwinner A13 supports DRAM clock frequency up to + * 533 MHz according to the datasheet). Additionally, there is no + * official documentation for the SDR_IDCR register anywhere, and + * there is always a chance that we are interpreting it wrong. + * Better be safe than sorry, so add an explicit delay here. */ + udelay(500); } static void mctl_set_drive(void) @@ -102,6 +136,14 @@ static void mctl_itm_enable(void) clrbits_le32(&dram->ccr, DRAM_CCR_ITM_OFF); } +static void mctl_itm_reset(void) +{ + mctl_itm_disable(); + udelay(1); /* ITM reset needs a bit of delay */ + mctl_itm_enable(); + udelay(1); +} + static void mctl_enable_dll0(u32 phase) { struct sunxi_dram_reg *dram = (struct sunxi_dram_reg *)SUNXI_DRAMC_BASE; @@ -118,23 +160,28 @@ static void mctl_enable_dll0(u32 phase) udelay(22); } +/* Get the number of DDR byte lanes */ +static u32 mctl_get_number_of_lanes(void) +{ + struct sunxi_dram_reg *dram = (struct sunxi_dram_reg *)SUNXI_DRAMC_BASE; + if ((readl(&dram->dcr) & DRAM_DCR_BUS_WIDTH_MASK) == + DRAM_DCR_BUS_WIDTH(DRAM_DCR_BUS_WIDTH_32BIT)) + return 4; + else + return 2; +} + /* * Note: This differs from pm/standby in that it checks the bus width */ static void mctl_enable_dllx(u32 phase) { struct sunxi_dram_reg *dram = (struct sunxi_dram_reg *)SUNXI_DRAMC_BASE; - u32 i, n, bus_width; + u32 i, number_of_lanes; - bus_width = readl(&dram->dcr); + number_of_lanes = mctl_get_number_of_lanes(); - if ((bus_width & DRAM_DCR_BUS_WIDTH_MASK) == - DRAM_DCR_BUS_WIDTH(DRAM_DCR_BUS_WIDTH_32BIT)) - n = DRAM_DCR_NR_DLLCR_32BIT; - else - n = DRAM_DCR_NR_DLLCR_16BIT; - - for (i = 1; i < n; i++) { + for (i = 1; i <= number_of_lanes; i++) { clrsetbits_le32(&dram->dllcr[i], 0xf << 14, (phase & 0xf) << 14); clrsetbits_le32(&dram->dllcr[i], DRAM_DLLCR_NRESET, @@ -143,12 +190,12 @@ static void mctl_enable_dllx(u32 phase) } udelay(2); - for (i = 1; i < n; i++) + for (i = 1; i <= number_of_lanes; i++) clrbits_le32(&dram->dllcr[i], DRAM_DLLCR_NRESET | DRAM_DLLCR_DISABLE); udelay(22); - for (i = 1; i < n; i++) + for (i = 1; i <= number_of_lanes; i++) clrsetbits_le32(&dram->dllcr[i], DRAM_DLLCR_DISABLE, DRAM_DLLCR_NRESET); udelay(22); @@ -201,11 +248,20 @@ static void mctl_configure_hostport(void) writel(hpcr_value[i], &dram->hpcr[i]); } -static void mctl_setup_dram_clock(u32 clk) +static void mctl_setup_dram_clock(u32 clk, u32 mbus_clk) { u32 reg_val; struct sunxi_ccm_reg *ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; + /* PLL5P and PLL6 are the potential clock sources for MBUS */ + u32 pll6x_div, pll5p_div; + u32 pll6x_clk = clock_get_pll6() / 1000000; + u32 pll5p_clk = clk / 24 * 48; + u32 pll5p_rate, pll6x_rate; +#ifdef CONFIG_SUN7I + pll6x_clk *= 2; /* sun7i uses PLL6*2, sun5i uses just PLL6 */ +#endif + /* setup DRAM PLL */ reg_val = readl(&ccm->pll5_cfg); reg_val &= ~CCM_PLL5_CTRL_M_MASK; /* set M to 0 (x1) */ @@ -213,41 +269,40 @@ static void mctl_setup_dram_clock(u32 clk) reg_val &= ~CCM_PLL5_CTRL_N_MASK; /* set N to 0 (x0) */ reg_val &= ~CCM_PLL5_CTRL_P_MASK; /* set P to 0 (x1) */ if (clk >= 540 && clk < 552) { - /* dram = 540MHz, pll5p = 540MHz */ + /* dram = 540MHz, pll5p = 1080MHz */ + pll5p_clk = 1080; reg_val |= CCM_PLL5_CTRL_M(CCM_PLL5_CTRL_M_X(2)); reg_val |= CCM_PLL5_CTRL_K(CCM_PLL5_CTRL_K_X(3)); reg_val |= CCM_PLL5_CTRL_N(CCM_PLL5_CTRL_N_X(15)); - reg_val |= CCM_PLL5_CTRL_P(1); } else if (clk >= 512 && clk < 528) { - /* dram = 512MHz, pll5p = 384MHz */ + /* dram = 512MHz, pll5p = 1536MHz */ + pll5p_clk = 1536; reg_val |= CCM_PLL5_CTRL_M(CCM_PLL5_CTRL_M_X(3)); reg_val |= CCM_PLL5_CTRL_K(CCM_PLL5_CTRL_K_X(4)); reg_val |= CCM_PLL5_CTRL_N(CCM_PLL5_CTRL_N_X(16)); - reg_val |= CCM_PLL5_CTRL_P(2); } else if (clk >= 496 && clk < 504) { - /* dram = 496MHz, pll5p = 372MHz */ + /* dram = 496MHz, pll5p = 1488MHz */ + pll5p_clk = 1488; reg_val |= CCM_PLL5_CTRL_M(CCM_PLL5_CTRL_M_X(3)); reg_val |= CCM_PLL5_CTRL_K(CCM_PLL5_CTRL_K_X(2)); reg_val |= CCM_PLL5_CTRL_N(CCM_PLL5_CTRL_N_X(31)); - reg_val |= CCM_PLL5_CTRL_P(2); } else if (clk >= 468 && clk < 480) { - /* dram = 468MHz, pll5p = 468MHz */ + /* dram = 468MHz, pll5p = 936MHz */ + pll5p_clk = 936; reg_val |= CCM_PLL5_CTRL_M(CCM_PLL5_CTRL_M_X(2)); reg_val |= CCM_PLL5_CTRL_K(CCM_PLL5_CTRL_K_X(3)); reg_val |= CCM_PLL5_CTRL_N(CCM_PLL5_CTRL_N_X(13)); - reg_val |= CCM_PLL5_CTRL_P(1); } else if (clk >= 396 && clk < 408) { - /* dram = 396MHz, pll5p = 396MHz */ + /* dram = 396MHz, pll5p = 792MHz */ + pll5p_clk = 792; reg_val |= CCM_PLL5_CTRL_M(CCM_PLL5_CTRL_M_X(2)); reg_val |= CCM_PLL5_CTRL_K(CCM_PLL5_CTRL_K_X(3)); reg_val |= CCM_PLL5_CTRL_N(CCM_PLL5_CTRL_N_X(11)); - reg_val |= CCM_PLL5_CTRL_P(1); } else { /* any other frequency that is a multiple of 24 */ reg_val |= CCM_PLL5_CTRL_M(CCM_PLL5_CTRL_M_X(2)); reg_val |= CCM_PLL5_CTRL_K(CCM_PLL5_CTRL_K_X(2)); reg_val |= CCM_PLL5_CTRL_N(CCM_PLL5_CTRL_N_X(clk / 24)); - reg_val |= CCM_PLL5_CTRL_P(CCM_PLL5_CTRL_P_X(2)); } reg_val &= ~CCM_PLL5_CTRL_VCO_GAIN; /* PLL VCO Gain off */ reg_val |= CCM_PLL5_CTRL_EN; /* PLL On */ @@ -264,20 +319,30 @@ static void mctl_setup_dram_clock(u32 clk) clrbits_le32(&ccm->ahb_gate0, CCM_AHB_GATE_GPS); #endif -#if defined(CONFIG_SUN5I) || defined(CONFIG_SUN7I) /* setup MBUS clock */ - reg_val = CCM_MBUS_CTRL_GATE | -#ifdef CONFIG_SUN7I - CCM_MBUS_CTRL_CLK_SRC(CCM_MBUS_CTRL_CLK_SRC_PLL6) | - CCM_MBUS_CTRL_N(CCM_MBUS_CTRL_N_X(2)) | - CCM_MBUS_CTRL_M(CCM_MBUS_CTRL_M_X(2)); -#else /* defined(CONFIG_SUN5I) */ - CCM_MBUS_CTRL_CLK_SRC(CCM_MBUS_CTRL_CLK_SRC_PLL5) | - CCM_MBUS_CTRL_N(CCM_MBUS_CTRL_N_X(1)) | - CCM_MBUS_CTRL_M(CCM_MBUS_CTRL_M_X(2)); -#endif + if (!mbus_clk) + mbus_clk = 300; + pll6x_div = DIV_ROUND_UP(pll6x_clk, mbus_clk); + pll5p_div = DIV_ROUND_UP(pll5p_clk, mbus_clk); + pll6x_rate = pll6x_clk / pll6x_div; + pll5p_rate = pll5p_clk / pll5p_div; + + if (pll6x_div <= 16 && pll6x_rate > pll5p_rate) { + /* use PLL6 as the MBUS clock source */ + reg_val = CCM_MBUS_CTRL_GATE | + CCM_MBUS_CTRL_CLK_SRC(CCM_MBUS_CTRL_CLK_SRC_PLL6) | + CCM_MBUS_CTRL_N(CCM_MBUS_CTRL_N_X(1)) | + CCM_MBUS_CTRL_M(CCM_MBUS_CTRL_M_X(pll6x_div)); + } else if (pll5p_div <= 16) { + /* use PLL5P as the MBUS clock source */ + reg_val = CCM_MBUS_CTRL_GATE | + CCM_MBUS_CTRL_CLK_SRC(CCM_MBUS_CTRL_CLK_SRC_PLL5) | + CCM_MBUS_CTRL_N(CCM_MBUS_CTRL_N_X(1)) | + CCM_MBUS_CTRL_M(CCM_MBUS_CTRL_M_X(pll5p_div)); + } else { + panic("Bad mbus_clk\n"); + } writel(reg_val, &ccm->mbus_clk_cfg); -#endif /* * open DRAMC AHB & DLL register clock @@ -299,19 +364,48 @@ static void mctl_setup_dram_clock(u32 clk) udelay(22); } +/* + * The data from rslrX and rdgrX registers (X=rank) is stored + * in a single 32-bit value using the following format: + * bits [31:26] - DQS gating system latency for byte lane 3 + * bits [25:24] - DQS gating phase select for byte lane 3 + * bits [23:18] - DQS gating system latency for byte lane 2 + * bits [17:16] - DQS gating phase select for byte lane 2 + * bits [15:10] - DQS gating system latency for byte lane 1 + * bits [ 9:8 ] - DQS gating phase select for byte lane 1 + * bits [ 7:2 ] - DQS gating system latency for byte lane 0 + * bits [ 1:0 ] - DQS gating phase select for byte lane 0 + */ +static void mctl_set_dqs_gating_delay(int rank, u32 dqs_gating_delay) +{ + struct sunxi_dram_reg *dram = (struct sunxi_dram_reg *)SUNXI_DRAMC_BASE; + u32 lane, number_of_lanes = mctl_get_number_of_lanes(); + /* rank0 gating system latency (3 bits per lane: cycles) */ + u32 slr = readl(rank == 0 ? &dram->rslr0 : &dram->rslr1); + /* rank0 gating phase select (2 bits per lane: 90, 180, 270, 360) */ + u32 dgr = readl(rank == 0 ? &dram->rdgr0 : &dram->rdgr1); + for (lane = 0; lane < number_of_lanes; lane++) { + u32 tmp = dqs_gating_delay >> (lane * 8); + slr &= ~(7 << (lane * 3)); + slr |= ((tmp >> 2) & 7) << (lane * 3); + dgr &= ~(3 << (lane * 2)); + dgr |= (tmp & 3) << (lane * 2); + } + writel(slr, rank == 0 ? &dram->rslr0 : &dram->rslr1); + writel(dgr, rank == 0 ? &dram->rdgr0 : &dram->rdgr1); +} + static int dramc_scan_readpipe(void) { struct sunxi_dram_reg *dram = (struct sunxi_dram_reg *)SUNXI_DRAMC_BASE; u32 reg_val; /* data training trigger */ -#ifdef CONFIG_SUN7I clrbits_le32(&dram->csr, DRAM_CSR_FAILED); -#endif setbits_le32(&dram->ccr, DRAM_CCR_DATA_TRAINING); /* check whether data training process has completed */ - await_completion(&dram->ccr, DRAM_CCR_DATA_TRAINING); + await_bits_clear(&dram->ccr, DRAM_CCR_DATA_TRAINING); /* check data training result */ reg_val = readl(&dram->csr); @@ -321,117 +415,6 @@ static int dramc_scan_readpipe(void) return 0; } -static int dramc_scan_dll_para(void) -{ - struct sunxi_dram_reg *dram = (struct sunxi_dram_reg *)SUNXI_DRAMC_BASE; - const u32 dqs_dly[7] = {0x3, 0x2, 0x1, 0x0, 0xe, 0xd, 0xc}; - const u32 clk_dly[15] = {0x07, 0x06, 0x05, 0x04, 0x03, - 0x02, 0x01, 0x00, 0x08, 0x10, - 0x18, 0x20, 0x28, 0x30, 0x38}; - u32 clk_dqs_count[15]; - u32 dqs_i, clk_i, cr_i; - u32 max_val, min_val; - u32 dqs_index, clk_index; - - /* Find DQS_DLY Pass Count for every CLK_DLY */ - for (clk_i = 0; clk_i < 15; clk_i++) { - clk_dqs_count[clk_i] = 0; - clrsetbits_le32(&dram->dllcr[0], 0x3f << 6, - (clk_dly[clk_i] & 0x3f) << 6); - for (dqs_i = 0; dqs_i < 7; dqs_i++) { - for (cr_i = 1; cr_i < 5; cr_i++) { - clrsetbits_le32(&dram->dllcr[cr_i], - 0x4f << 14, - (dqs_dly[dqs_i] & 0x4f) << 14); - } - udelay(2); - if (dramc_scan_readpipe() == 0) - clk_dqs_count[clk_i]++; - } - } - /* Test DQS_DLY Pass Count for every CLK_DLY from up to down */ - for (dqs_i = 15; dqs_i > 0; dqs_i--) { - max_val = 15; - min_val = 15; - for (clk_i = 0; clk_i < 15; clk_i++) { - if (clk_dqs_count[clk_i] == dqs_i) { - max_val = clk_i; - if (min_val == 15) - min_val = clk_i; - } - } - if (max_val < 15) - break; - } - - /* Check if Find a CLK_DLY failed */ - if (!dqs_i) - goto fail; - - /* Find the middle index of CLK_DLY */ - clk_index = (max_val + min_val) >> 1; - if ((max_val == (15 - 1)) && (min_val > 0)) - /* if CLK_DLY[MCTL_CLK_DLY_COUNT] is very good, then the middle - * value can be more close to the max_val - */ - clk_index = (15 + clk_index) >> 1; - else if ((max_val < (15 - 1)) && (min_val == 0)) - /* if CLK_DLY[0] is very good, then the middle value can be more - * close to the min_val - */ - clk_index >>= 1; - if (clk_dqs_count[clk_index] < dqs_i) - clk_index = min_val; - - /* Find the middle index of DQS_DLY for the CLK_DLY got above, and Scan - * read pipe again - */ - clrsetbits_le32(&dram->dllcr[0], 0x3f << 6, - (clk_dly[clk_index] & 0x3f) << 6); - max_val = 7; - min_val = 7; - for (dqs_i = 0; dqs_i < 7; dqs_i++) { - clk_dqs_count[dqs_i] = 0; - for (cr_i = 1; cr_i < 5; cr_i++) { - clrsetbits_le32(&dram->dllcr[cr_i], - 0x4f << 14, - (dqs_dly[dqs_i] & 0x4f) << 14); - } - udelay(2); - if (dramc_scan_readpipe() == 0) { - clk_dqs_count[dqs_i] = 1; - max_val = dqs_i; - if (min_val == 7) - min_val = dqs_i; - } - } - - if (max_val < 7) { - dqs_index = (max_val + min_val) >> 1; - if ((max_val == (7-1)) && (min_val > 0)) - dqs_index = (7 + dqs_index) >> 1; - else if ((max_val < (7-1)) && (min_val == 0)) - dqs_index >>= 1; - if (!clk_dqs_count[dqs_index]) - dqs_index = min_val; - for (cr_i = 1; cr_i < 5; cr_i++) { - clrsetbits_le32(&dram->dllcr[cr_i], - 0x4f << 14, - (dqs_dly[dqs_index] & 0x4f) << 14); - } - udelay(2); - return dramc_scan_readpipe(); - } - -fail: - clrbits_le32(&dram->dllcr[0], 0x3f << 6); - for (cr_i = 1; cr_i < 5; cr_i++) - clrbits_le32(&dram->dllcr[cr_i], 0x4f << 14); - udelay(2); - - return dramc_scan_readpipe(); -} - static void dramc_clock_output_en(u32 on) { #if defined(CONFIG_SUN5I) || defined(CONFIG_SUN7I) @@ -451,48 +434,164 @@ static void dramc_clock_output_en(u32 on) #endif } -static const u16 tRFC_table[2][6] = { - /* 256Mb 512Mb 1Gb 2Gb 4Gb 8Gb */ - /* DDR2 75ns 105ns 127.5ns 195ns 327.5ns invalid */ - { 77, 108, 131, 200, 336, 336 }, - /* DDR3 invalid 90ns 110ns 160ns 300ns 350ns */ - { 93, 93, 113, 164, 308, 359 } +/* tRFC in nanoseconds for different densities (from the DDR3 spec) */ +static const u16 tRFC_DDR3_table[6] = { + /* 256Mb 512Mb 1Gb 2Gb 4Gb 8Gb */ + 90, 90, 110, 160, 300, 350 }; -static void dramc_set_autorefresh_cycle(u32 clk, u32 type, u32 density) +static void dramc_set_autorefresh_cycle(u32 clk, u32 density) { struct sunxi_dram_reg *dram = (struct sunxi_dram_reg *)SUNXI_DRAMC_BASE; u32 tRFC, tREFI; - tRFC = (tRFC_table[type][density] * clk + 1023) >> 10; + tRFC = (tRFC_DDR3_table[density] * clk + 999) / 1000; tREFI = (7987 * clk) >> 10; /* <= 7.8us */ writel(DRAM_DRR_TREFI(tREFI) | DRAM_DRR_TRFC(tRFC), &dram->drr); } -unsigned long dramc_init(struct dram_para *para) +/* Calculate the value for A11, A10, A9 bits in MR0 (write recovery) */ +static u32 ddr3_write_recovery(u32 clk) +{ + u32 twr_ns = 15; /* DDR3 spec says that it is 15ns for all speed bins */ + u32 twr_ck = (twr_ns * clk + 999) / 1000; + if (twr_ck < 5) + return 1; + else if (twr_ck <= 8) + return twr_ck - 4; + else if (twr_ck <= 10) + return 5; + else + return 6; +} + +/* + * If the dram->ppwrsctl (SDR_DPCR) register has the lowest bit set to 1, this + * means that DRAM is currently in self-refresh mode and retaining the old + * data. Since we have no idea what to do in this situation yet, just set this + * register to 0 and initialize DRAM in the same way as on any normal reboot + * (discarding whatever was stored there). + * + * Note: on sun7i hardware, the highest 16 bits need to be set to 0x1651 magic + * value for this write operation to have any effect. On sun5i hadware this + * magic value is not necessary. And on sun4i hardware the writes to this + * register seem to have no effect at all. + */ +static void mctl_disable_power_save(void) +{ + struct sunxi_dram_reg *dram = (struct sunxi_dram_reg *)SUNXI_DRAMC_BASE; + writel(0x16510000, &dram->ppwrsctl); +} + +/* + * After the DRAM is powered up or reset, the DDR3 spec requires to wait at + * least 500 us before driving the CKE pin (Clock Enable) high. The dram->idct + * (SDR_IDCR) register appears to configure this delay, which gets applied + * right at the time when the DRAM initialization is activated in the + * 'mctl_ddr3_initialize' function. + */ +static void mctl_set_cke_delay(void) +{ + struct sunxi_dram_reg *dram = (struct sunxi_dram_reg *)SUNXI_DRAMC_BASE; + + /* The CKE delay is represented in DRAM clock cycles, multiplied by N + * (where N=2 for sun4i/sun5i and N=3 for sun7i). Here it is set to + * the maximum possible value 0x1ffff, just like in the Allwinner's + * boot0 bootloader. The resulting delay value is somewhere between + * ~0.4 ms (sun5i with 648 MHz DRAM clock speed) and ~1.1 ms (sun7i + * with 360 MHz DRAM clock speed). */ + setbits_le32(&dram->idcr, 0x1ffff); +} + +/* + * This triggers the DRAM initialization. It performs sending the mode registers + * to the DRAM among other things. Very likely the ZQCL command is also getting + * executed (to do the initial impedance calibration on the DRAM side of the + * wire). The memory controller and the PHY must be already configured before + * calling this function. + */ +static void mctl_ddr3_initialize(void) +{ + struct sunxi_dram_reg *dram = (struct sunxi_dram_reg *)SUNXI_DRAMC_BASE; + setbits_le32(&dram->ccr, DRAM_CCR_INIT); + await_bits_clear(&dram->ccr, DRAM_CCR_INIT); +} + +/* + * Perform impedance calibration on the DRAM controller side of the wire. + */ +static void mctl_set_impedance(u32 zq, u32 odt_en) +{ + struct sunxi_dram_reg *dram = (struct sunxi_dram_reg *)SUNXI_DRAMC_BASE; + u32 reg_val; + u32 zprog = zq & 0xFF, zdata = (zq >> 8) & 0xFFFFF; + +#ifndef CONFIG_SUN7I + /* Appears that some kind of automatically initiated default + * ZQ calibration is already in progress at this point on sun4i/sun5i + * hardware, but not on sun7i. So it is reasonable to wait for its + * completion before doing anything else. */ + await_bits_set(&dram->zqsr, DRAM_ZQSR_ZDONE); +#endif + + /* ZQ calibration is not really useful unless ODT is enabled */ + if (!odt_en) + return; + +#ifdef CONFIG_SUN7I + /* Enabling ODT in SDR_IOCR on sun7i hardware results in a deadlock + * unless bit 24 is set in SDR_ZQCR1. Not much is known about the + * SDR_ZQCR1 register, but there are hints indicating that it might + * be related to periodic impedance re-calibration. This particular + * magic value is borrowed from the Allwinner boot0 bootloader, and + * using it helps to avoid troubles */ + writel((1 << 24) | (1 << 1), &dram->zqcr1); +#endif + + /* Needed at least for sun5i, because it does not self clear there */ + clrbits_le32(&dram->zqcr0, DRAM_ZQCR0_ZCAL); + + if (zdata) { + /* Set the user supplied impedance data */ + reg_val = DRAM_ZQCR0_ZDEN | zdata; + writel(reg_val, &dram->zqcr0); + /* no need to wait, this takes effect immediately */ + } else { + /* Do the calibration using the external resistor */ + reg_val = DRAM_ZQCR0_ZCAL | DRAM_ZQCR0_IMP_DIV(zprog); + writel(reg_val, &dram->zqcr0); + /* Wait for the new impedance configuration to settle */ + await_bits_set(&dram->zqsr, DRAM_ZQSR_ZDONE); + } + + /* Needed at least for sun5i, because it does not self clear there */ + clrbits_le32(&dram->zqcr0, DRAM_ZQCR0_ZCAL); + + /* Set I/O configure register */ + writel(DRAM_IOCR_ODT_EN(odt_en), &dram->iocr); +} + +static unsigned long dramc_init_helper(struct dram_para *para) { struct sunxi_dram_reg *dram = (struct sunxi_dram_reg *)SUNXI_DRAMC_BASE; u32 reg_val; u32 density; int ret_val; - /* check input dram parameter structure */ - if (!para) + /* + * only single rank DDR3 is supported by this code even though the + * hardware can theoretically support DDR2 and up to two ranks + */ + if (para->type != DRAM_MEMORY_TYPE_DDR3 || para->rank_num != 1) return 0; /* setup DRAM relative clock */ - mctl_setup_dram_clock(para->clock); + mctl_setup_dram_clock(para->clock, para->mbus_clock); -#ifdef CONFIG_SUN5I /* Disable any pad power save control */ - writel(0, &dram->ppwrsctl); -#endif + mctl_disable_power_save(); - /* reset external DRAM */ -#ifndef CONFIG_SUN7I - mctl_ddr3_reset(); -#endif mctl_set_drive(); /* dram clock off */ @@ -507,9 +606,7 @@ unsigned long dramc_init(struct dram_para *para) mctl_enable_dll0(para->tpr3); /* configure external DRAM */ - reg_val = 0x0; - if (para->type == DRAM_MEMORY_TYPE_DDR3) - reg_val |= DRAM_DCR_TYPE_DDR3; + reg_val = DRAM_DCR_TYPE_DDR3; reg_val |= DRAM_DCR_IO_WIDTH(para->io_width >> 3); if (para->density == 256) @@ -534,85 +631,41 @@ unsigned long dramc_init(struct dram_para *para) reg_val |= DRAM_DCR_MODE(DRAM_DCR_MODE_INTERLEAVE); writel(reg_val, &dram->dcr); -#ifdef CONFIG_SUN7I - setbits_le32(&dram->zqcr1, (0x1 << 24) | (0x1 << 1)); - if (para->tpr4 & 0x2) - clrsetbits_le32(&dram->zqcr1, (0x1 << 24), (0x1 << 1)); dramc_clock_output_en(1); -#endif -#if (defined(CONFIG_SUN5I) || defined(CONFIG_SUN7I)) - /* set odt impendance divide ratio */ - reg_val = ((para->zq) >> 8) & 0xfffff; - reg_val |= ((para->zq) & 0xff) << 20; - reg_val |= (para->zq) & 0xf0000000; - writel(reg_val, &dram->zqcr0); -#endif + mctl_set_impedance(para->zq, para->odt_en); -#ifdef CONFIG_SUN7I - /* Set CKE Delay to about 1ms */ - setbits_le32(&dram->idcr, 0x1ffff); -#endif + mctl_set_cke_delay(); -#ifdef CONFIG_SUN7I - if ((readl(&dram->ppwrsctl) & 0x1) != 0x1) - mctl_ddr3_reset(); - else - setbits_le32(&dram->mcr, DRAM_MCR_RESET); -#else - /* dram clock on */ - dramc_clock_output_en(1); -#endif + mctl_ddr3_reset(); udelay(1); - await_completion(&dram->ccr, DRAM_CCR_INIT); + await_bits_clear(&dram->ccr, DRAM_CCR_INIT); mctl_enable_dllx(para->tpr3); -#ifdef CONFIG_SUN4I - /* set odt impedance divide ratio */ - reg_val = ((para->zq) >> 8) & 0xfffff; - reg_val |= ((para->zq) & 0xff) << 20; - reg_val |= (para->zq) & 0xf0000000; - writel(reg_val, &dram->zqcr0); -#endif - -#ifdef CONFIG_SUN4I - /* set I/O configure register */ - reg_val = 0x00cc0000; - reg_val |= (para->odt_en) & 0x3; - reg_val |= ((para->odt_en) & 0x3) << 30; - writel(reg_val, &dram->iocr); -#endif - /* set refresh period */ - dramc_set_autorefresh_cycle(para->clock, para->type - 2, density); + dramc_set_autorefresh_cycle(para->clock, density); /* set timing parameters */ writel(para->tpr0, &dram->tpr0); writel(para->tpr1, &dram->tpr1); writel(para->tpr2, &dram->tpr2); - if (para->type == DRAM_MEMORY_TYPE_DDR3) { - reg_val = DRAM_MR_BURST_LENGTH(0x0); + reg_val = DRAM_MR_BURST_LENGTH(0x0); #if (defined(CONFIG_SUN5I) || defined(CONFIG_SUN7I)) - reg_val |= DRAM_MR_POWER_DOWN; + reg_val |= DRAM_MR_POWER_DOWN; #endif - reg_val |= DRAM_MR_CAS_LAT(para->cas - 4); - reg_val |= DRAM_MR_WRITE_RECOVERY(0x5); - } else if (para->type == DRAM_MEMORY_TYPE_DDR2) { - reg_val = DRAM_MR_BURST_LENGTH(0x2); - reg_val |= DRAM_MR_CAS_LAT(para->cas); - reg_val |= DRAM_MR_WRITE_RECOVERY(0x5); - } + reg_val |= DRAM_MR_CAS_LAT(para->cas - 4); + reg_val |= DRAM_MR_WRITE_RECOVERY(ddr3_write_recovery(para->clock)); writel(reg_val, &dram->mr); writel(para->emr1, &dram->emr); writel(para->emr2, &dram->emr2); writel(para->emr3, &dram->emr3); - /* set DQS window mode */ + /* disable drift compensation and set passive DQS window mode */ clrsetbits_le32(&dram->ccr, DRAM_CCR_DQS_DRIFT_COMP, DRAM_CCR_DQS_GATE); #ifdef CONFIG_SUN7I @@ -620,70 +673,78 @@ unsigned long dramc_init(struct dram_para *para) if (para->tpr4 & 0x1) setbits_le32(&dram->ccr, DRAM_CCR_COMMAND_RATE_1T); #endif - /* reset external DRAM */ - setbits_le32(&dram->ccr, DRAM_CCR_INIT); - await_completion(&dram->ccr, DRAM_CCR_INIT); + /* initialize external DRAM */ + mctl_ddr3_initialize(); -#ifdef CONFIG_SUN7I - /* setup zq calibration manual */ - reg_val = readl(&dram->ppwrsctl); - if ((reg_val & 0x1) == 1) { - /* super_standby_flag = 1 */ - - reg_val = readl(0x01c20c00 + 0x120); /* rtc */ - reg_val &= 0x000fffff; - reg_val |= 0x17b00000; - writel(reg_val, &dram->zqcr0); + /* scan read pipe value */ + mctl_itm_enable(); - /* exit self-refresh state */ - clrsetbits_le32(&dram->dcr, 0x1f << 27, 0x12 << 27); - /* check whether command has been executed */ - await_completion(&dram->dcr, 0x1 << 31); + /* Hardware DQS gate training */ + ret_val = dramc_scan_readpipe(); - udelay(2); + if (ret_val < 0) + return 0; - /* dram pad hold off */ - setbits_le32(&dram->ppwrsctl, 0x16510000); + /* allow to override the DQS training results with a custom delay */ + if (para->dqs_gating_delay) + mctl_set_dqs_gating_delay(0, para->dqs_gating_delay); - await_completion(&dram->ppwrsctl, 0x1); + /* set the DQS gating window type */ + if (para->active_windowing) + clrbits_le32(&dram->ccr, DRAM_CCR_DQS_GATE); + else + setbits_le32(&dram->ccr, DRAM_CCR_DQS_GATE); - /* exit self-refresh state */ - clrsetbits_le32(&dram->dcr, 0x1f << 27, 0x12 << 27); + mctl_itm_reset(); - /* check whether command has been executed */ - await_completion(&dram->dcr, 0x1 << 31); + /* configure all host port */ + mctl_configure_hostport(); - udelay(2); + return get_ram_size((long *)PHYS_SDRAM_0, PHYS_SDRAM_0_SIZE); +} - /* issue a refresh command */ - clrsetbits_le32(&dram->dcr, 0x1f << 27, 0x13 << 27); - await_completion(&dram->dcr, 0x1 << 31); +unsigned long dramc_init(struct dram_para *para) +{ + unsigned long dram_size, actual_density; - udelay(2); - } + /* If the dram configuration is not provided, use a default */ + if (!para) + return 0; + + /* if everything is known, then autodetection is not necessary */ + if (para->io_width && para->bus_width && para->density) + return dramc_init_helper(para); + + /* try to autodetect the DRAM bus width and density */ + para->io_width = 16; + para->bus_width = 32; +#if defined(CONFIG_SUN4I) || defined(CONFIG_SUN5I) + /* only A0-A14 address lines on A10/A13, limiting max density to 4096 */ + para->density = 4096; +#else + /* all A0-A15 address lines on A20, which allow density 8192 */ + para->density = 8192; #endif - /* scan read pipe value */ - mctl_itm_enable(); - if (para->tpr3 & (0x1 << 31)) { - ret_val = dramc_scan_dll_para(); - if (ret_val == 0) - para->tpr3 = - (((readl(&dram->dllcr[0]) >> 6) & 0x3f) << 16) | - (((readl(&dram->dllcr[1]) >> 14) & 0xf) << 0) | - (((readl(&dram->dllcr[2]) >> 14) & 0xf) << 4) | - (((readl(&dram->dllcr[3]) >> 14) & 0xf) << 8) | - (((readl(&dram->dllcr[4]) >> 14) & 0xf) << 12 - ); - } else { - ret_val = dramc_scan_readpipe(); + dram_size = dramc_init_helper(para); + if (!dram_size) { + /* if 32-bit bus width failed, try 16-bit bus width instead */ + para->bus_width = 16; + dram_size = dramc_init_helper(para); + if (!dram_size) { + /* if 16-bit bus width also failed, then bail out */ + return dram_size; + } } - if (ret_val < 0) - return 0; + /* check if we need to adjust the density */ + actual_density = (dram_size >> 17) * para->io_width / para->bus_width; - /* configure all host port */ - mctl_configure_hostport(); + if (actual_density != para->density) { + /* update the density and re-initialize DRAM again */ + para->density = actual_density; + dram_size = dramc_init_helper(para); + } - return get_ram_size((long *)PHYS_SDRAM_0, PHYS_SDRAM_0_SIZE); + return dram_size; } diff --git a/arch/arm/imx-common/cpu.c b/arch/arm/imx-common/cpu.c index 5a09107..ed826a0 100644 --- a/arch/arm/imx-common/cpu.c +++ b/arch/arm/imx-common/cpu.c @@ -93,6 +93,11 @@ unsigned imx_ddr_size(void) bits += bank_lookup[ESD_MMDC_MISC_GET_BANK(misc)]; bits += ESD_MMDC_CTL_GET_WIDTH(ctl); bits += ESD_MMDC_CTL_GET_CS1(ctl); + + /* The MX6 can do only 3840 MiB of DRAM */ + if (bits == 32) + return 0xf0000000; + return 1 << bits; } #endif @@ -112,6 +117,8 @@ const char *get_imx_type(u32 imxtype) return "6SOLO"; /* Solo version of the mx6 */ case MXC_CPU_MX6SL: return "6SL"; /* Solo-Lite version of the mx6 */ + case MXC_CPU_MX6SX: + return "6SX"; /* SoloX version of the mx6 */ case MXC_CPU_MX51: return "51"; case MXC_CPU_MX53: diff --git a/arch/arm/imx-common/sata.c b/arch/arm/imx-common/sata.c index c10dd28..d174a46 100644 --- a/arch/arm/imx-common/sata.c +++ b/arch/arm/imx-common/sata.c @@ -12,8 +12,7 @@ int setup_sata(void) { - struct iomuxc_base_regs *const iomuxc_regs - = (struct iomuxc_base_regs *)IOMUXC_BASE_ADDR; + struct iomuxc *const iomuxc_regs = (struct iomuxc *)IOMUXC_BASE_ADDR; int ret; if (!is_cpu_type(MXC_CPU_MX6Q) && !is_cpu_type(MXC_CPU_MX6D)) diff --git a/arch/arm/include/asm/arch-imx/cpu.h b/arch/arm/include/asm/arch-imx/cpu.h index a35940e..a3cc96f 100644 --- a/arch/arm/include/asm/arch-imx/cpu.h +++ b/arch/arm/include/asm/arch-imx/cpu.h @@ -8,6 +8,7 @@ #define MXC_CPU_MX53 0x53 #define MXC_CPU_MX6SL 0x60 #define MXC_CPU_MX6DL 0x61 -#define MXC_CPU_MX6SOLO 0x62 +#define MXC_CPU_MX6SX 0x62 #define MXC_CPU_MX6Q 0x63 #define MXC_CPU_MX6D 0x64 +#define MXC_CPU_MX6SOLO 0x65 /* dummy ID */ diff --git a/arch/arm/include/asm/arch-mx5/crm_regs.h b/arch/arm/include/asm/arch-mx5/crm_regs.h index efe57e0..b61c7b9 100644 --- a/arch/arm/include/asm/arch-mx5/crm_regs.h +++ b/arch/arm/include/asm/arch-mx5/crm_regs.h @@ -40,7 +40,7 @@ struct mxc_ccm_reg { u32 cs1cdr; u32 cs2cdr; u32 cdcdr; /* 0x0030 */ - u32 chscdr; + u32 chsccdr; u32 cscdr2; u32 cscdr3; u32 cscdr4; /* 0x0040 */ diff --git a/arch/arm/include/asm/arch-mx6/clock.h b/arch/arm/include/asm/arch-mx6/clock.h index 1b4ded7..339c789 100644 --- a/arch/arm/include/asm/arch-mx6/clock.h +++ b/arch/arm/include/asm/arch-mx6/clock.h @@ -57,6 +57,7 @@ void enable_usboh3_clk(unsigned char enable); int enable_sata_clock(void); int enable_pcie_clock(void); int enable_i2c_clk(unsigned char enable, unsigned i2c_num); +int enable_spi_clk(unsigned char enable, unsigned spi_num); void enable_ipu_clock(void); int enable_fec_anatop_clock(enum enet_freq freq); #endif /* __ASM_ARCH_CLOCK_H */ diff --git a/arch/arm/include/asm/arch-mx6/crm_regs.h b/arch/arm/include/asm/arch-mx6/crm_regs.h index 7202073..e67b5b9 100644 --- a/arch/arm/include/asm/arch-mx6/crm_regs.h +++ b/arch/arm/include/asm/arch-mx6/crm_regs.h @@ -113,7 +113,11 @@ struct mxc_ccm_reg { #define MXC_CCM_CCR_WB_COUNT_MASK 0x7 #define MXC_CCM_CCR_WB_COUNT_OFFSET (1 << 16) #define MXC_CCM_CCR_COSC_EN (1 << 12) +#ifdef CONFIG_MX6SX +#define MXC_CCM_CCR_OSCNT_MASK 0x7F +#else #define MXC_CCM_CCR_OSCNT_MASK 0xFF +#endif #define MXC_CCM_CCR_OSCNT_OFFSET 0 /* Define the bits in register CCDR */ @@ -146,8 +150,10 @@ struct mxc_ccm_reg { #define MXC_CCM_CBCDR_PERIPH_CLK2_PODF_OFFSET 27 #define MXC_CCM_CBCDR_PERIPH2_CLK2_SEL (1 << 26) #define MXC_CCM_CBCDR_PERIPH_CLK_SEL (1 << 25) +#ifndef CONFIG_MX6SX #define MXC_CCM_CBCDR_MMDC_CH0_PODF_MASK (0x7 << 19) #define MXC_CCM_CBCDR_MMDC_CH0_PODF_OFFSET 19 +#endif #define MXC_CCM_CBCDR_AXI_PODF_MASK (0x7 << 16) #define MXC_CCM_CBCDR_AXI_PODF_OFFSET 16 #define MXC_CCM_CBCDR_AHB_PODF_MASK (0x7 << 10) @@ -173,28 +179,40 @@ struct mxc_ccm_reg { #define MXC_CCM_CBCMR_PRE_PERIPH2_CLK2_SEL (1 << 20) #define MXC_CCM_CBCMR_PRE_PERIPH_CLK_SEL_MASK (0x3 << 18) #define MXC_CCM_CBCMR_PRE_PERIPH_CLK_SEL_OFFSET 18 +#ifndef CONFIG_MX6SX #define MXC_CCM_CBCMR_GPU2D_CLK_SEL_MASK (0x3 << 16) #define MXC_CCM_CBCMR_GPU2D_CLK_SEL_OFFSET 16 #define MXC_CCM_CBCMR_VPU_AXI_CLK_SEL_MASK (0x3 << 14) #define MXC_CCM_CBCMR_VPU_AXI_CLK_SEL_OFFSET 14 +#endif #define MXC_CCM_CBCMR_PERIPH_CLK2_SEL_MASK (0x3 << 12) #define MXC_CCM_CBCMR_PERIPH_CLK2_SEL_OFFSET 12 +#ifndef CONFIG_MX6SX #define MXC_CCM_CBCMR_VDOAXI_CLK_SEL (1 << 11) +#endif #define MXC_CCM_CBCMR_PCIE_AXI_CLK_SEL (1 << 10) #define MXC_CCM_CBCMR_GPU3D_SHADER_CLK_SEL_MASK (0x3 << 8) #define MXC_CCM_CBCMR_GPU3D_SHADER_CLK_SEL_OFFSET 8 #define MXC_CCM_CBCMR_GPU3D_CORE_CLK_SEL_MASK (0x3 << 4) #define MXC_CCM_CBCMR_GPU3D_CORE_CLK_SEL_OFFSET 4 +#ifndef CONFIG_MX6SX #define MXC_CCM_CBCMR_GPU3D_AXI_CLK_SEL (1 << 1) #define MXC_CCM_CBCMR_GPU2D_AXI_CLK_SEL (1 << 0) +#endif /* Define the bits in register CSCMR1 */ #define MXC_CCM_CSCMR1_ACLK_EMI_SLOW_MASK (0x3 << 29) #define MXC_CCM_CSCMR1_ACLK_EMI_SLOW_OFFSET 29 +#ifdef CONFIG_MX6SX +#define MXC_CCM_CSCMR1_QSPI1_PODF_MASK (0x7 << 26) +#define MXC_CCM_CSCMR1_QSPI1_PODF_OFFSET 26 +#else #define MXC_CCM_CSCMR1_ACLK_EMI_MASK (0x3 << 27) #define MXC_CCM_CSCMR1_ACLK_EMI_OFFSET 27 +#endif #define MXC_CCM_CSCMR1_ACLK_EMI_SLOW_PODF_MASK (0x7 << 23) #define MXC_CCM_CSCMR1_ACLK_EMI_SLOW_PODF_OFFSET 23 +/* ACLK_EMI_PODF is LCFIF2_PODF on MX6SX */ #define MXC_CCM_CSCMR1_ACLK_EMI_PODF_MASK (0x7 << 20) #define MXC_CCM_CSCMR1_ACLK_EMI_PODF_OFFSET 20 #define MXC_CCM_CSCMR1_USDHC4_CLK_SEL (1 << 19) @@ -207,19 +225,38 @@ struct mxc_ccm_reg { #define MXC_CCM_CSCMR1_SSI2_CLK_SEL_OFFSET 12 #define MXC_CCM_CSCMR1_SSI1_CLK_SEL_MASK (0x3 << 10) #define MXC_CCM_CSCMR1_SSI1_CLK_SEL_OFFSET 10 +#ifdef CONFIG_MX6SX +#define MXC_CCM_CSCMR1_QSPI1_CLK_SEL_MASK (0x7 << 7) +#define MXC_CCM_CSCMR1_QSPI1_CLK_SEL_OFFSET 7 +#define MXC_CCM_CSCMR1_PER_CLK_SEL_MASK (1 << 6) +#define MXC_CCM_CSCMR1_PER_CLK_SEL_OFFSET 6 +#endif #define MXC_CCM_CSCMR1_PERCLK_PODF_MASK 0x3F /* Define the bits in register CSCMR2 */ +#ifdef CONFIG_MX6SX +#define MXC_CCM_CSCMR2_VID_CLK_SEL_MASK (0x7 << 21) +#define MXC_CCM_CSCMR2_VID_CLK_SEL_OFFSET 21 +#endif #define MXC_CCM_CSCMR2_ESAI_PRE_SEL_MASK (0x3 << 19) #define MXC_CCM_CSCMR2_ESAI_PRE_SEL_OFFSET 19 #define MXC_CCM_CSCMR2_LDB_DI1_IPU_DIV (1 << 11) #define MXC_CCM_CSCMR2_LDB_DI0_IPU_DIV (1 << 10) +#ifdef CONFIG_MX6SX +#define MXC_CCM_CSCMR2_CAN_CLK_SEL_MASK (0x3 << 8) +#define MXC_CCM_CSCMR2_CAN_CLK_SEL_OFFSET 8 +#define MXC_CCM_CSCMR2_CAN_CLK_PODF_MASK (0x3F << 2) +#define MXC_CCM_CSCMR2_CAN_CLK_PODF_OFFSET 2 +#else #define MXC_CCM_CSCMR2_CAN_CLK_SEL_MASK (0x3F << 2) #define MXC_CCM_CSCMR2_CAN_CLK_SEL_OFFSET 2 +#endif /* Define the bits in register CSCDR1 */ +#ifndef CONFIG_MX6SX #define MXC_CCM_CSCDR1_VPU_AXI_PODF_MASK (0x7 << 25) #define MXC_CCM_CSCDR1_VPU_AXI_PODF_OFFSET 25 +#endif #define MXC_CCM_CSCDR1_USDHC4_PODF_MASK (0x7 << 22) #define MXC_CCM_CSCDR1_USDHC4_PODF_OFFSET 22 #define MXC_CCM_CSCDR1_USDHC3_PODF_MASK (0x7 << 19) @@ -228,21 +265,28 @@ struct mxc_ccm_reg { #define MXC_CCM_CSCDR1_USDHC2_PODF_OFFSET 16 #define MXC_CCM_CSCDR1_USDHC1_PODF_MASK (0x7 << 11) #define MXC_CCM_CSCDR1_USDHC1_PODF_OFFSET 11 +#ifndef CONFIG_MX6SX #define MXC_CCM_CSCDR1_USBOH3_CLK_PRED_OFFSET 8 #define MXC_CCM_CSCDR1_USBOH3_CLK_PRED_MASK (0x7 << 8) #define MXC_CCM_CSCDR1_USBOH3_CLK_PODF_OFFSET 6 #define MXC_CCM_CSCDR1_USBOH3_CLK_PODF_MASK (0x3 << 6) +#endif #ifdef CONFIG_MX6SL #define MXC_CCM_CSCDR1_UART_CLK_PODF_MASK 0x1F #define MXC_CCM_CSCDR1_UART_CLK_SEL (1 << 6) #else #define MXC_CCM_CSCDR1_UART_CLK_PODF_MASK 0x3F +#ifdef CONFIG_MX6SX +#define MXC_CCM_CSCDR1_UART_CLK_SEL (1 << 6) +#endif #endif #define MXC_CCM_CSCDR1_UART_CLK_PODF_OFFSET 0 /* Define the bits in register CS1CDR */ #define MXC_CCM_CS1CDR_ESAI_CLK_PODF_MASK (0x3F << 25) #define MXC_CCM_CS1CDR_ESAI_CLK_PODF_OFFSET 25 +#define MXC_CCM_CS1CDR_SSI3_CLK_PRED_MASK (0x7 << 22) +#define MXC_CCM_CS1CDR_SSI3_CLK_PRED_OFFSET 22 #define MXC_CCM_CS1CDR_SSI3_CLK_PODF_MASK (0x3F << 16) #define MXC_CCM_CS1CDR_SSI3_CLK_PODF_OFFSET 16 #define MXC_CCM_CS1CDR_ESAI_CLK_PRED_MASK (0x3 << 9) @@ -253,6 +297,17 @@ struct mxc_ccm_reg { #define MXC_CCM_CS1CDR_SSI1_CLK_PODF_OFFSET 0 /* Define the bits in register CS2CDR */ +#ifdef CONFIG_MX6SX +#define MXC_CCM_CS2CDR_QSPI2_CLK_PODF_MASK (0x3F << 21) +#define MXC_CCM_CS2CDR_QSPI2_CLK_PODF_OFFSET 21 +#define MXC_CCM_CS2CDR_QSPI2_CLK_PODF(v) (((v) & 0x3f) << 21) +#define MXC_CCM_CS2CDR_QSPI2_CLK_PRED_MASK (0x7 << 18) +#define MXC_CCM_CS2CDR_QSPI2_CLK_PRED_OFFSET 18 +#define MXC_CCM_CS2CDR_QSPI2_CLK_PRED(v) (((v) & 0x7) << 18) +#define MXC_CCM_CS2CDR_QSPI2_CLK_SEL_MASK (0x7 << 15) +#define MXC_CCM_CS2CDR_QSPI2_CLK_SEL_OFFSET 15 +#define MXC_CCM_CS2CDR_QSPI2_CLK_SEL(v) (((v) & 0x7) << 15) +#else #define MXC_CCM_CS2CDR_ENFC_CLK_PODF_MASK (0x3F << 21) #define MXC_CCM_CS2CDR_ENFC_CLK_PODF_OFFSET 21 #define MXC_CCM_CS2CDR_ENFC_CLK_PODF(v) (((v) & 0x3f) << 21) @@ -262,6 +317,7 @@ struct mxc_ccm_reg { #define MXC_CCM_CS2CDR_ENFC_CLK_SEL_MASK (0x3 << 16) #define MXC_CCM_CS2CDR_ENFC_CLK_SEL_OFFSET 16 #define MXC_CCM_CS2CDR_ENFC_CLK_SEL(v) (((v) & 0x3) << 16) +#endif #define MXC_CCM_CS2CDR_LDB_DI1_CLK_SEL_MASK (0x7 << 12) #define MXC_CCM_CS2CDR_LDB_DI1_CLK_SEL_OFFSET 12 #define MXC_CCM_CS2CDR_LDB_DI0_CLK_SEL_MASK (0x7 << 9) @@ -272,13 +328,15 @@ struct mxc_ccm_reg { #define MXC_CCM_CS2CDR_SSI2_CLK_PODF_OFFSET 0 /* Define the bits in register CDCDR */ +#ifndef CONFIG_MX6SX #define MXC_CCM_CDCDR_HSI_TX_PODF_MASK (0x7 << 29) #define MXC_CCM_CDCDR_HSI_TX_PODF_OFFSET 29 #define MXC_CCM_CDCDR_HSI_TX_CLK_SEL (1 << 28) +#endif #define MXC_CCM_CDCDR_SPDIF0_CLK_PRED_MASK (0x7 << 25) #define MXC_CCM_CDCDR_SPDIF0_CLK_PRED_OFFSET 25 -#define MXC_CCM_CDCDR_SPDIF0_CLK_PODF_MASK (0x7 << 19) -#define MXC_CCM_CDCDR_SPDIF0_CLK_PODF_OFFSET 19 +#define MXC_CCM_CDCDR_SPDIF0_CLK_PODF_MASK (0x7 << 22) +#define MXC_CCM_CDCDR_SPDIF0_CLK_PODF_OFFSET 22 #define MXC_CCM_CDCDR_SPDIF0_CLK_SEL_MASK (0x3 << 20) #define MXC_CCM_CDCDR_SPDIF0_CLK_SEL_OFFSET 20 #define MXC_CCM_CDCDR_SPDIF1_CLK_PRED_MASK (0x7 << 12) @@ -289,6 +347,20 @@ struct mxc_ccm_reg { #define MXC_CCM_CDCDR_SPDIF1_CLK_SEL_OFFSET 7 /* Define the bits in register CHSCCDR */ +#ifdef CONFIG_MX6SX +#define MXC_CCM_CHSCCDR_ENET_PRE_CLK_SEL_MASK (0x7 << 15) +#define MXC_CCM_CHSCCDR_ENET_PRE_CLK_SEL_OFFSET 15 +#define MXC_CCM_CHSCCDR_ENET_PODF_MASK (0x7 << 12) +#define MXC_CCM_CHSCCDR_ENET_PODF_OFFSET 12 +#define MXC_CCM_CHSCCDR_ENET_CLK_SEL_MASK (0x7 << 9) +#define MXC_CCM_CHSCCDR_ENET_CLK_SEL_OFFSET 9 +#define MXC_CCM_CHSCCDR_M4_PRE_CLK_SEL_MASK (0x7 << 6) +#define MXC_CCM_CHSCCDR_M4_PRE_CLK_SEL_OFFSET 6 +#define MXC_CCM_CHSCCDR_M4_PODF_MASK (0x7 << 3) +#define MXC_CCM_CHSCCDR_M4_PODF_OFFSET 3 +#define MXC_CCM_CHSCCDR_M4_CLK_SEL_MASK (0x7) +#define MXC_CCM_CHSCCDR_M4_CLK_SEL_OFFSET 0 +#else #define MXC_CCM_CHSCCDR_IPU1_DI1_PRE_CLK_SEL_MASK (0x7 << 15) #define MXC_CCM_CHSCCDR_IPU1_DI1_PRE_CLK_SEL_OFFSET 15 #define MXC_CCM_CHSCCDR_IPU1_DI1_PODF_MASK (0x7 << 12) @@ -301,6 +373,7 @@ struct mxc_ccm_reg { #define MXC_CCM_CHSCCDR_IPU1_DI0_PODF_OFFSET 3 #define MXC_CCM_CHSCCDR_IPU1_DI0_CLK_SEL_MASK (0x7) #define MXC_CCM_CHSCCDR_IPU1_DI0_CLK_SEL_OFFSET 0 +#endif #define CHSCCDR_CLK_SEL_LDB_DI0 3 #define CHSCCDR_PODF_DIVIDE_BY_3 2 @@ -309,12 +382,14 @@ struct mxc_ccm_reg { /* Define the bits in register CSCDR2 */ #define MXC_CCM_CSCDR2_ECSPI_CLK_PODF_MASK (0x3F << 19) #define MXC_CCM_CSCDR2_ECSPI_CLK_PODF_OFFSET 19 +/* All IPU2_DI1 are LCDIF1 on MX6SX */ #define MXC_CCM_CHSCCDR_IPU2_DI1_PRE_CLK_SEL_MASK (0x7 << 15) #define MXC_CCM_CHSCCDR_IPU2_DI1_PRE_CLK_SEL_OFFSET 15 #define MXC_CCM_CHSCCDR_IPU2_DI1_PODF_MASK (0x7 << 12) #define MXC_CCM_CHSCCDR_IPU2_DI1_PODF_OFFSET 12 #define MXC_CCM_CHSCCDR_IPU2_DI1_CLK_SEL_MASK (0x7 << 9) #define MXC_CCM_CHSCCDR_IPU2_DI1_CLK_SEL_OFFSET 9 +/* All IPU2_DI0 are LCDIF2 on MX6SX */ #define MXC_CCM_CHSCCDR_IPU2_DI0_PRE_CLK_SEL_MASK (0x7 << 6) #define MXC_CCM_CHSCCDR_IPU2_DI0_PRE_CLK_SEL_OFFSET 6 #define MXC_CCM_CHSCCDR_IPU2_DI0_PODF_MASK (0x7 << 3) @@ -335,7 +410,9 @@ struct mxc_ccm_reg { /* Define the bits in register CDHIPR */ #define MXC_CCM_CDHIPR_ARM_PODF_BUSY (1 << 16) #define MXC_CCM_CDHIPR_PERIPH_CLK_SEL_BUSY (1 << 5) +#ifndef CONFIG_MX6SX #define MXC_CCM_CDHIPR_MMDC_CH0_PODF_BUSY (1 << 4) +#endif #define MXC_CCM_CDHIPR_PERIPH2_CLK_SEL_BUSY (1 << 3) #define MXC_CCM_CDHIPR_MMDC_CH1_PODF_BUSY (1 << 2) #define MXC_CCM_CDHIPR_AHB_PODF_BUSY (1 << 1) @@ -344,14 +421,18 @@ struct mxc_ccm_reg { /* Define the bits in register CLPCR */ #define MXC_CCM_CLPCR_MASK_L2CC_IDLE (1 << 27) #define MXC_CCM_CLPCR_MASK_SCU_IDLE (1 << 26) +#ifndef CONFIG_MX6SX #define MXC_CCM_CLPCR_MASK_CORE3_WFI (1 << 25) #define MXC_CCM_CLPCR_MASK_CORE2_WFI (1 << 24) #define MXC_CCM_CLPCR_MASK_CORE1_WFI (1 << 23) +#endif #define MXC_CCM_CLPCR_MASK_CORE0_WFI (1 << 22) #define MXC_CCM_CLPCR_BYP_MMDC_CH1_LPM_HS (1 << 21) +#ifndef CONFIG_MX6SX #define MXC_CCM_CLPCR_BYP_MMDC_CH0_LPM_HS (1 << 19) #define MXC_CCM_CLPCR_WB_CORE_AT_LPM (1 << 17) -#define MXC_CCM_CLPCR_WB_PER_AT_LPM (1 << 17) +#endif +#define MXC_CCM_CLPCR_WB_PER_AT_LPM (1 << 16) #define MXC_CCM_CLPCR_COSC_PWRDOWN (1 << 11) #define MXC_CCM_CLPCR_STBY_COUNT_MASK (0x3 << 9) #define MXC_CCM_CLPCR_STBY_COUNT_OFFSET 9 @@ -359,15 +440,19 @@ struct mxc_ccm_reg { #define MXC_CCM_CLPCR_DIS_REF_OSC (1 << 7) #define MXC_CCM_CLPCR_SBYOS (1 << 6) #define MXC_CCM_CLPCR_ARM_CLK_DIS_ON_LPM (1 << 5) +#ifndef CONFIG_MX6SX #define MXC_CCM_CLPCR_LPSR_CLK_SEL_MASK (0x3 << 3) #define MXC_CCM_CLPCR_LPSR_CLK_SEL_OFFSET 3 #define MXC_CCM_CLPCR_BYPASS_PMIC_VFUNC_READY (1 << 2) +#endif #define MXC_CCM_CLPCR_LPM_MASK 0x3 #define MXC_CCM_CLPCR_LPM_OFFSET 0 /* Define the bits in register CISR */ #define MXC_CCM_CISR_ARM_PODF_LOADED (1 << 26) +#ifndef CONFIG_MX6SX #define MXC_CCM_CISR_MMDC_CH0_PODF_LOADED (1 << 23) +#endif #define MXC_CCM_CISR_PERIPH_CLK_SEL_LOADED (1 << 22) #define MXC_CCM_CISR_MMDC_CH1_PODF_LOADED (1 << 21) #define MXC_CCM_CISR_AHB_PODF_LOADED (1 << 20) @@ -378,11 +463,13 @@ struct mxc_ccm_reg { /* Define the bits in register CIMR */ #define MXC_CCM_CIMR_MASK_ARM_PODF_LOADED (1 << 26) +#ifndef CONFIG_MX6SX #define MXC_CCM_CIMR_MASK_MMDC_CH0_PODF_LOADED (1 << 23) +#endif #define MXC_CCM_CIMR_MASK_PERIPH_CLK_SEL_LOADED (1 << 22) #define MXC_CCM_CIMR_MASK_MMDC_CH1_PODF_LOADED (1 << 21) #define MXC_CCM_CIMR_MASK_AHB_PODF_LOADED (1 << 20) -#define MXC_CCM_CIMR_MASK_PERIPH2_CLK_SEL_LOADED (1 << 22) +#define MXC_CCM_CIMR_MASK_PERIPH2_CLK_SEL_LOADED (1 << 19) #define MXC_CCM_CIMR_MASK_AXI_PODF_LOADED (1 << 17) #define MXC_CCM_CIMR_MASK_COSC_READY (1 << 6) #define MXC_CCM_CIMR_MASK_LRF_PLL 1 @@ -393,6 +480,7 @@ struct mxc_ccm_reg { #define MXC_CCM_CCOSR_CKO2_DIV_OFFSET 21 #define MXC_CCM_CCOSR_CKO2_SEL_OFFSET 16 #define MXC_CCM_CCOSR_CKO2_SEL_MASK (0x1F << 16) +#define MXC_CCM_CCOSR_CLK_OUT_SEL (0x1 << 8) #define MXC_CCM_CCOSR_CKOL_EN (0x1 << 7) #define MXC_CCM_CCOSR_CKOL_DIV_MASK (0x7 << 4) #define MXC_CCM_CCOSR_CKOL_DIV_OFFSET 4 @@ -400,6 +488,7 @@ struct mxc_ccm_reg { #define MXC_CCM_CCOSR_CKOL_SEL_OFFSET 0 /* Define the bits in registers CGPR */ +#define MXC_CCM_CGPR_FAST_PLL_EN (1 << 16) #define MXC_CCM_CGPR_EFUSE_PROG_SUPPLY_GATE (1 << 4) #define MXC_CCM_CGPR_MMDC_EXT_CLK_DIS (1 << 2) #define MXC_CCM_CGPR_PMIC_DELAY_SCALER 1 @@ -435,8 +524,13 @@ struct mxc_ccm_reg { #define MXC_CCM_CCGR0_DCIC1_MASK (3 << MXC_CCM_CCGR0_DCIC1_OFFSET) #define MXC_CCM_CCGR0_DCIC2_OFFSET 26 #define MXC_CCM_CCGR0_DCIC2_MASK (3 << MXC_CCM_CCGR0_DCIC2_OFFSET) +#ifdef CONFIG_MX6SX +#define MXC_CCM_CCGR0_AIPS_TZ3_OFFSET 30 +#define MXC_CCM_CCGR0_AIPS_TZ3_MASK (3 << MXC_CCM_CCGR0_AIPS_TZ3_OFFSET) +#else #define MXC_CCM_CCGR0_DTCP_OFFSET 28 #define MXC_CCM_CCGR0_DTCP_MASK (3 << MXC_CCM_CCGR0_DTCP_OFFSET) +#endif #define MXC_CCM_CCGR1_ECSPI1S_OFFSET 0 #define MXC_CCM_CCGR1_ECSPI1S_MASK (3 << MXC_CCM_CCGR1_ECSPI1S_OFFSET) @@ -448,27 +542,48 @@ struct mxc_ccm_reg { #define MXC_CCM_CCGR1_ECSPI4S_MASK (3 << MXC_CCM_CCGR1_ECSPI4S_OFFSET) #define MXC_CCM_CCGR1_ECSPI5S_OFFSET 8 #define MXC_CCM_CCGR1_ECSPI5S_MASK (3 << MXC_CCM_CCGR1_ECSPI5S_OFFSET) +#ifndef CONFIG_MX6SX #define MXC_CCM_CCGR1_ENET_CLK_ENABLE_OFFSET 10 #define MXC_CCM_CCGR1_ENET_CLK_ENABLE_MASK (3 << MXC_CCM_CCGR1_ENET_CLK_ENABLE_OFFSET) +#endif #define MXC_CCM_CCGR1_EPIT1S_OFFSET 12 #define MXC_CCM_CCGR1_EPIT1S_MASK (3 << MXC_CCM_CCGR1_EPIT1S_OFFSET) #define MXC_CCM_CCGR1_EPIT2S_OFFSET 14 #define MXC_CCM_CCGR1_EPIT2S_MASK (3 << MXC_CCM_CCGR1_EPIT2S_OFFSET) #define MXC_CCM_CCGR1_ESAIS_OFFSET 16 #define MXC_CCM_CCGR1_ESAIS_MASK (3 << MXC_CCM_CCGR1_ESAIS_OFFSET) +#ifdef CONFIG_MX6SX +#define MXC_CCM_CCGR1_WAKEUP_OFFSET 18 +#define MXC_CCM_CCGR1_WAKEUP_MASK (3 << MXC_CCM_CCGR1_WAKEUP_OFFSET) +#endif #define MXC_CCM_CCGR1_GPT_BUS_OFFSET 20 #define MXC_CCM_CCGR1_GPT_BUS_MASK (3 << MXC_CCM_CCGR1_GPT_BUS_OFFSET) #define MXC_CCM_CCGR1_GPT_SERIAL_OFFSET 22 #define MXC_CCM_CCGR1_GPT_SERIAL_MASK (3 << MXC_CCM_CCGR1_GPT_SERIAL_OFFSET) +#ifndef CONFIG_MX6SX #define MXC_CCM_CCGR1_GPU2D_OFFSET 24 #define MXC_CCM_CCGR1_GPU2D_MASK (3 << MXC_CCM_CCGR1_GPU2D_OFFSET) +#endif #define MXC_CCM_CCGR1_GPU3D_OFFSET 26 #define MXC_CCM_CCGR1_GPU3D_MASK (3 << MXC_CCM_CCGR1_GPU3D_OFFSET) +#ifdef CONFIG_MX6SX +#define MXC_CCM_CCGR1_OCRAM_S_OFFSET 28 +#define MXC_CCM_CCGR1_OCRAM_S_MASK (3 << MXC_CCM_CCGR1_OCRAM_S_OFFSET) +#define MXC_CCM_CCGR1_CANFD_OFFSET 30 +#define MXC_CCM_CCGR1_CANFD_MASK (3 << MXC_CCM_CCGR1_CANFD_OFFSET) +#endif +#ifndef CONFIG_MX6SX #define MXC_CCM_CCGR2_HDMI_TX_IAHBCLK_OFFSET 0 #define MXC_CCM_CCGR2_HDMI_TX_IAHBCLK_MASK (3 << MXC_CCM_CCGR2_HDMI_TX_IAHBCLK_OFFSET) +#else +#define MXC_CCM_CCGR2_CSI_OFFSET 2 +#define MXC_CCM_CCGR2_CSI_MASK (3 << MXC_CCM_CCGR2_CSI_OFFSET) +#endif +#ifndef CONFIG_MX6SX #define MXC_CCM_CCGR2_HDMI_TX_ISFRCLK_OFFSET 4 #define MXC_CCM_CCGR2_HDMI_TX_ISFRCLK_MASK (3 << MXC_CCM_CCGR2_HDMI_TX_ISFRCLK_OFFSET) +#endif #define MXC_CCM_CCGR2_I2C1_SERIAL_OFFSET 6 #define MXC_CCM_CCGR2_I2C1_SERIAL_MASK (3 << MXC_CCM_CCGR2_I2C1_SERIAL_OFFSET) #define MXC_CCM_CCGR2_I2C2_SERIAL_OFFSET 8 @@ -487,17 +602,33 @@ struct mxc_ccm_reg { #define MXC_CCM_CCGR2_IPMUX3_MASK (3 << MXC_CCM_CCGR2_IPMUX3_OFFSET) #define MXC_CCM_CCGR2_IPSYNC_IP2APB_TZASC1_IPGS_OFFSET 22 #define MXC_CCM_CCGR2_IPSYNC_IP2APB_TZASC1_IPGS_MASK (3 << MXC_CCM_CCGR2_IPSYNC_IP2APB_TZASC1_IPGS_OFFSET) +#ifdef CONFIG_MX6SX +#define MXC_CCM_CCGR2_LCD_OFFSET 28 +#define MXC_CCM_CCGR2_LCD_MASK (3 << MXC_CCM_CCGR2_LCD_OFFSET) +#define MXC_CCM_CCGR2_PXP_OFFSET 30 +#define MXC_CCM_CCGR2_PXP_MASK (3 << MXC_CCM_CCGR2_PXP_OFFSET) +#else #define MXC_CCM_CCGR2_IPSYNC_IP2APB_TZASC2_IPG_OFFSET 24 #define MXC_CCM_CCGR2_IPSYNC_IP2APB_TZASC2_IPG_MASK (3 << MXC_CCM_CCGR2_IPSYNC_IP2APB_TZASC2_IPG_OFFSET) #define MXC_CCM_CCGR2_IPSYNC_VDOA_IPG_MASTER_CLK_OFFSET 26 #define MXC_CCM_CCGR2_IPSYNC_VDOA_IPG_MASTER_CLK_MASK (3 << MXC_CCM_CCGR2_IPSYNC_VDOA_IPG_MASTER_CLK_OFFSET) +#endif +#ifdef CONFIG_MX6SX +#define MXC_CCM_CCGR3_M4_OFFSET 2 +#define MXC_CCM_CCGR3_M4_MASK (3 << MXC_CCM_CCGR3_M4_OFFSET) +#define MXC_CCM_CCGR3_ENET_OFFSET 4 +#define MXC_CCM_CCGR3_ENET_MASK (3 << MXC_CCM_CCGR3_ENET_OFFSET) +#define MXC_CCM_CCGR3_QSPI_OFFSET 14 +#define MXC_CCM_CCGR3_QSPI_MASK (3 << MXC_CCM_CCGR3_QSPI_OFFSET) +#else #define MXC_CCM_CCGR3_IPU1_IPU_OFFSET 0 #define MXC_CCM_CCGR3_IPU1_IPU_MASK (3 << MXC_CCM_CCGR3_IPU1_IPU_OFFSET) #define MXC_CCM_CCGR3_IPU1_IPU_DI0_OFFSET 2 #define MXC_CCM_CCGR3_IPU1_IPU_DI0_MASK (3 << MXC_CCM_CCGR3_IPU1_IPU_DI0_OFFSET) #define MXC_CCM_CCGR3_IPU1_IPU_DI1_OFFSET 4 #define MXC_CCM_CCGR3_IPU1_IPU_DI1_MASK (3 << MXC_CCM_CCGR3_IPU1_IPU_DI1_OFFSET) +#endif #define MXC_CCM_CCGR3_IPU2_IPU_OFFSET 6 #define MXC_CCM_CCGR3_IPU2_IPU_MASK (3 << MXC_CCM_CCGR3_IPU2_IPU_OFFSET) #define MXC_CCM_CCGR3_IPU2_IPU_DI0_OFFSET 8 @@ -506,29 +637,43 @@ struct mxc_ccm_reg { #define MXC_CCM_CCGR3_IPU2_IPU_DI1_MASK (3 << MXC_CCM_CCGR3_IPU2_IPU_DI1_OFFSET) #define MXC_CCM_CCGR3_LDB_DI0_OFFSET 12 #define MXC_CCM_CCGR3_LDB_DI0_MASK (3 << MXC_CCM_CCGR3_LDB_DI0_OFFSET) +#ifdef CONFIG_MX6SX +#define MXC_CCM_CCGR3_QSPI1_OFFSET 14 +#define MXC_CCM_CCGR3_QSPI1_MASK (3 << MXC_CCM_CCGR3_QSPI1_OFFSET) +#else #define MXC_CCM_CCGR3_LDB_DI1_OFFSET 14 #define MXC_CCM_CCGR3_LDB_DI1_MASK (3 << MXC_CCM_CCGR3_LDB_DI1_OFFSET) #define MXC_CCM_CCGR3_MIPI_CORE_CFG_OFFSET 16 #define MXC_CCM_CCGR3_MIPI_CORE_CFG_MASK (3 << MXC_CCM_CCGR3_MIPI_CORE_CFG_OFFSET) +#endif #define MXC_CCM_CCGR3_MLB_OFFSET 18 #define MXC_CCM_CCGR3_MLB_MASK (3 << MXC_CCM_CCGR3_MLB_OFFSET) #define MXC_CCM_CCGR3_MMDC_CORE_ACLK_FAST_CORE_P0_OFFSET 20 #define MXC_CCM_CCGR3_MMDC_CORE_ACLK_FAST_CORE_P0_MASK (3 << MXC_CCM_CCGR3_MMDC_CORE_ACLK_FAST_CORE_P0_OFFSET) +#ifndef CONFIG_MX6SX #define MXC_CCM_CCGR3_MMDC_CORE_ACLK_FAST_CORE_P1_OFFSET 22 #define MXC_CCM_CCGR3_MMDC_CORE_ACLK_FAST_CORE_P1_MASK (3 << MXC_CCM_CCGR3_MMDC_CORE_ACLK_FAST_CORE_P1_OFFSET) +#endif #define MXC_CCM_CCGR3_MMDC_CORE_IPG_CLK_P0_OFFSET 24 #define MXC_CCM_CCGR3_MMDC_CORE_IPG_CLK_P0_MASK (3 << MXC_CCM_CCGR3_MMDC_CORE_IPG_CLK_P0_OFFSET) #define MXC_CCM_CCGR3_MMDC_CORE_IPG_CLK_P1_OFFSET 26 #define MXC_CCM_CCGR3_MMDC_CORE_IPG_CLK_P1_MASK (3 << MXC_CCM_CCGR3_MMDC_CORE_IPG_CLK_P1_OFFSET) #define MXC_CCM_CCGR3_OCRAM_OFFSET 28 #define MXC_CCM_CCGR3_OCRAM_MASK (3 << MXC_CCM_CCGR3_OCRAM_OFFSET) +#ifndef CONFIG_MX6SX #define MXC_CCM_CCGR3_OPENVGAXICLK_OFFSET 30 #define MXC_CCM_CCGR3_OPENVGAXICLK_MASK (3 << MXC_CCM_CCGR3_OPENVGAXICLK_OFFSET) +#endif #define MXC_CCM_CCGR4_PCIE_OFFSET 0 #define MXC_CCM_CCGR4_PCIE_MASK (3 << MXC_CCM_CCGR4_PCIE_OFFSET) +#ifdef CONFIG_MX6SX +#define MXC_CCM_CCGR4_QSPI2_ENFC_OFFSET 10 +#define MXC_CCM_CCGR4_QSPI2_ENFC_MASK (3 << MXC_CCM_CCGR4_QSPI2_ENFC_OFFSET) +#else #define MXC_CCM_CCGR4_PL301_MX6QFAST1_S133_OFFSET 8 #define MXC_CCM_CCGR4_PL301_MX6QFAST1_S133_MASK (3 << MXC_CCM_CCGR4_PL301_MX6QFAST1_S133_OFFSET) +#endif #define MXC_CCM_CCGR4_PL301_MX6QPER1_BCH_OFFSET 12 #define MXC_CCM_CCGR4_PL301_MX6QPER1_BCH_MASK (3 << MXC_CCM_CCGR4_PL301_MX6QPER1_BCH_OFFSET) #define MXC_CCM_CCGR4_PL301_MX6QPER2_MAINCLK_ENABLE_OFFSET 14 @@ -552,8 +697,10 @@ struct mxc_ccm_reg { #define MXC_CCM_CCGR5_ROM_OFFSET 0 #define MXC_CCM_CCGR5_ROM_MASK (3 << MXC_CCM_CCGR5_ROM_OFFSET) +#ifndef CONFIG_MX6SX #define MXC_CCM_CCGR5_SATA_OFFSET 4 #define MXC_CCM_CCGR5_SATA_MASK (3 << MXC_CCM_CCGR5_SATA_OFFSET) +#endif #define MXC_CCM_CCGR5_SDMA_OFFSET 6 #define MXC_CCM_CCGR5_SDMA_MASK (3 << MXC_CCM_CCGR5_SDMA_OFFSET) #define MXC_CCM_CCGR5_SPBA_OFFSET 12 @@ -570,6 +717,12 @@ struct mxc_ccm_reg { #define MXC_CCM_CCGR5_UART_MASK (3 << MXC_CCM_CCGR5_UART_OFFSET) #define MXC_CCM_CCGR5_UART_SERIAL_OFFSET 26 #define MXC_CCM_CCGR5_UART_SERIAL_MASK (3 << MXC_CCM_CCGR5_UART_SERIAL_OFFSET) +#ifdef CONFIG_MX6SX +#define MXC_CCM_CCGR5_SAI1_OFFSET 20 +#define MXC_CCM_CCGR5_SAI1_MASK (3 << MXC_CCM_CCGR5_SAI1_OFFSET) +#define MXC_CCM_CCGR5_SAI2_OFFSET 30 +#define MXC_CCM_CCGR5_SAI2_MASK (3 << MXC_CCM_CCGR5_SAI2_OFFSET) +#endif #define MXC_CCM_CCGR6_USBOH3_OFFSET 0 #define MXC_CCM_CCGR6_USBOH3_MASK (3 << MXC_CCM_CCGR6_USBOH3_OFFSET) @@ -583,8 +736,25 @@ struct mxc_ccm_reg { #define MXC_CCM_CCGR6_USDHC4_MASK (3 << MXC_CCM_CCGR6_USDHC4_OFFSET) #define MXC_CCM_CCGR6_EMI_SLOW_OFFSET 10 #define MXC_CCM_CCGR6_EMI_SLOW_MASK (3 << MXC_CCM_CCGR6_EMI_SLOW_OFFSET) +#ifdef CONFIG_MX6SX +#define MXC_CCM_CCGR6_PWM8_OFFSET 16 +#define MXC_CCM_CCGR6_PWM8_MASK (3 << MXC_CCM_CCGR6_PWM8_OFFSET) +#define MXC_CCM_CCGR6_VADC_OFFSET 20 +#define MXC_CCM_CCGR6_VADC_MASK (3 << MXC_CCM_CCGR6_VADC_OFFSET) +#define MXC_CCM_CCGR6_GIS_OFFSET 22 +#define MXC_CCM_CCGR6_GIS_MASK (3 << MXC_CCM_CCGR6_GIS_OFFSET) +#define MXC_CCM_CCGR6_I2C4_OFFSET 24 +#define MXC_CCM_CCGR6_I2C4_MASK (3 << MXC_CCM_CCGR6_I2C4_OFFSET) +#define MXC_CCM_CCGR6_PWM5_OFFSET 26 +#define MXC_CCM_CCGR6_PWM5_MASK (3 << MXC_CCM_CCGR6_PWM5_OFFSET) +#define MXC_CCM_CCGR6_PWM6_OFFSET 28 +#define MXC_CCM_CCGR6_PWM6_MASK (3 << MXC_CCM_CCGR6_PWM6_OFFSET) +#define MXC_CCM_CCGR6_PWM7_OFFSET 30 +#define MXC_CCM_CCGR6_PWM7_MASK (3 << MXC_CCM_CCGR6_PWM7_OFFSET) +#else #define MXC_CCM_CCGR6_VDOAXICLK_OFFSET 12 #define MXC_CCM_CCGR6_VDOAXICLK_MASK (3 << MXC_CCM_CCGR6_VDOAXICLK_OFFSET) +#endif #define BM_ANADIG_PLL_SYS_LOCK 0x80000000 #define BP_ANADIG_PLL_SYS_RSVD0 20 @@ -811,6 +981,7 @@ struct mxc_ccm_reg { #define BM_ANADIG_PLL_ENET_RSVD1 0x7FE00000 #define BF_ANADIG_PLL_ENET_RSVD1(v) \ (((v) << 21) & BM_ANADIG_PLL_ENET_RSVD1) +#define BM_ANADIG_PLL_ENET_REF_25M_ENABLE 0x00200000 #define BM_ANADIG_PLL_ENET_ENABLE_SATA 0x00100000 #define BM_ANADIG_PLL_ENET_ENABLE_PCIE 0x00080000 #define BM_ANADIG_PLL_ENET_PFD_OFFSET_EN 0x00040000 diff --git a/arch/arm/include/asm/arch-mx6/imx-regs.h b/arch/arm/include/asm/arch-mx6/imx-regs.h index a69a753..2631beb 100644 --- a/arch/arm/include/asm/arch-mx6/imx-regs.h +++ b/arch/arm/include/asm/arch-mx6/imx-regs.h @@ -19,6 +19,19 @@ #define GPU_2D_ARB_END_ADDR 0x02203FFF #define OPENVG_ARB_BASE_ADDR 0x02204000 #define OPENVG_ARB_END_ADDR 0x02207FFF +#elif CONFIG_MX6SX +#define CAAM_ARB_BASE_ADDR 0x00100000 +#define CAAM_ARB_END_ADDR 0x00107FFF +#define GPU_ARB_BASE_ADDR 0x01800000 +#define GPU_ARB_END_ADDR 0x01803FFF +#define APBH_DMA_ARB_BASE_ADDR 0x01804000 +#define APBH_DMA_ARB_END_ADDR 0x0180BFFF +#define M4_BOOTROM_BASE_ADDR 0x007F8000 + +#define MXS_APBH_BASE APBH_DMA_ARB_BASE_ADDR +#define MXS_GPMI_BASE (APBH_DMA_ARB_BASE_ADDR + 0x02000) +#define MXS_BCH_BASE (APBH_DMA_ARB_BASE_ADDR + 0x04000) + #else #define CAAM_ARB_BASE_ADDR 0x00100000 #define CAAM_ARB_END_ADDR 0x00103FFF @@ -39,14 +52,27 @@ #define MXS_BCH_BASE (APBH_DMA_ARB_BASE_ADDR + 0x04000) /* GPV - PL301 configuration ports */ -#ifdef CONFIG_MX6SL +#if (defined(CONFIG_MX6SL) || defined(CONFIG_MX6SX)) #define GPV2_BASE_ADDR 0x00D00000 #else #define GPV2_BASE_ADDR 0x00200000 #endif +#ifdef CONFIG_MX6SX +#define GPV3_BASE_ADDR 0x00E00000 +#define GPV4_BASE_ADDR 0x00F00000 +#define GPV5_BASE_ADDR 0x01000000 +#define GPV6_BASE_ADDR 0x01100000 +#define PCIE_ARB_BASE_ADDR 0x08000000 +#define PCIE_ARB_END_ADDR 0x08FFFFFF + +#else #define GPV3_BASE_ADDR 0x00300000 #define GPV4_BASE_ADDR 0x00800000 +#define PCIE_ARB_BASE_ADDR 0x01000000 +#define PCIE_ARB_END_ADDR 0x01FFFFFF +#endif + #define IRAM_BASE_ADDR 0x00900000 #define SCU_BASE_ADDR 0x00A00000 #define IC_INTERFACES_BASE_ADDR 0x00A00100 @@ -56,13 +82,21 @@ #define L2_PL310_BASE 0x00A02000 #define GPV0_BASE_ADDR 0x00B00000 #define GPV1_BASE_ADDR 0x00C00000 -#define PCIE_ARB_BASE_ADDR 0x01000000 -#define PCIE_ARB_END_ADDR 0x01FFFFFF #define AIPS1_ARB_BASE_ADDR 0x02000000 #define AIPS1_ARB_END_ADDR 0x020FFFFF #define AIPS2_ARB_BASE_ADDR 0x02100000 #define AIPS2_ARB_END_ADDR 0x021FFFFF +#ifdef CONFIG_MX6SX +#define AIPS3_BASE_ADDR 0x02200000 +#define AIPS3_END_ADDR 0x022FFFFF +#define WEIM_ARB_BASE_ADDR 0x50000000 +#define WEIM_ARB_END_ADDR 0x57FFFFFF +#define QSPI1_ARB_BASE_ADDR 0x60000000 +#define QSPI1_ARB_END_ADDR 0x6FFFFFFF +#define QSPI2_ARB_BASE_ADDR 0x70000000 +#define QSPI2_ARB_END_ADDR 0x7FFFFFFF +#else #define SATA_ARB_BASE_ADDR 0x02200000 #define SATA_ARB_END_ADDR 0x02203FFF #define OPENVG_ARB_BASE_ADDR 0x02204000 @@ -75,8 +109,9 @@ #define IPU2_ARB_END_ADDR 0x02BFFFFF #define WEIM_ARB_BASE_ADDR 0x08000000 #define WEIM_ARB_END_ADDR 0x0FFFFFFF +#endif -#ifdef CONFIG_MX6SL +#if (defined(CONFIG_MX6SL) || defined(CONFIG_MX6SX)) #define MMDC0_ARB_BASE_ADDR 0x80000000 #define MMDC0_ARB_END_ADDR 0xFFFFFFFF #define MMDC1_ARB_BASE_ADDR 0xC0000000 @@ -88,8 +123,10 @@ #define MMDC1_ARB_END_ADDR 0xFFFFFFFF #endif +#ifndef CONFIG_MX6SX #define IPU_SOC_BASE_ADDR IPU1_ARB_BASE_ADDR #define IPU_SOC_OFFSET 0x00200000 +#endif /* Defines for Blocks connected via AIPS (SkyBlue) */ #define ATZ1_BASE_ADDR AIPS1_ARB_BASE_ADDR @@ -112,7 +149,9 @@ #define UART3_IPS_BASE_ADDR (ATZ1_BASE_ADDR + 0x34000) #define UART4_IPS_BASE_ADDR (ATZ1_BASE_ADDR + 0x38000) #else +#ifndef CONFIG_MX6SX #define ECSPI5_BASE_ADDR (ATZ1_BASE_ADDR + 0x18000) +#endif #define UART1_BASE (ATZ1_BASE_ADDR + 0x20000) #define ESAI1_BASE_ADDR (ATZ1_BASE_ADDR + 0x24000) #define SSI1_BASE_ADDR (ATZ1_BASE_ADDR + 0x28000) @@ -121,8 +160,10 @@ #define ASRC_BASE_ADDR (ATZ1_BASE_ADDR + 0x34000) #endif +#ifndef CONFIG_MX6SX #define SPBA_BASE_ADDR (ATZ1_BASE_ADDR + 0x3C000) #define VPU_BASE_ADDR (ATZ1_BASE_ADDR + 0x40000) +#endif #define AIPS1_ON_BASE_ADDR (ATZ1_BASE_ADDR + 0x7C000) #define AIPS1_OFF_BASE_ADDR (ATZ1_BASE_ADDR + 0x80000) @@ -157,6 +198,13 @@ #define CSI_BASE_ADDR (AIPS1_OFF_BASE_ADDR + 0x64000) #define SIPIX_BASE_ADDR (AIPS1_OFF_BASE_ADDR + 0x68000) #define SDMA_PORT_HOST_BASE_ADDR (AIPS1_OFF_BASE_ADDR + 0x6C000) +#elif CONFIG_MX6SX +#define CANFD1_BASE_ADDR (AIPS1_OFF_BASE_ADDR + 0x68000) +#define SDMA_BASE_ADDR (AIPS1_OFF_BASE_ADDR + 0x6C000) +#define CANFD2_BASE_ADDR (AIPS1_OFF_BASE_ADDR + 0x70000) +#define SEMAPHORE1_BASE_ADDR (AIPS1_OFF_BASE_ADDR + 0x74000) +#define SEMAPHORE2_BASE_ADDR (AIPS1_OFF_BASE_ADDR + 0x78000) +#define RDC_BASE_ADDR (AIPS1_OFF_BASE_ADDR + 0x7C000) #else #define DCIC1_BASE_ADDR (AIPS1_OFF_BASE_ADDR + 0x64000) #define DCIC2_BASE_ADDR (AIPS1_OFF_BASE_ADDR + 0x68000) @@ -193,6 +241,8 @@ #define MMDC_P0_BASE_ADDR (AIPS2_OFF_BASE_ADDR + 0x30000) #ifdef CONFIG_MX6SL #define RNGB_IPS_BASE_ADDR (AIPS2_OFF_BASE_ADDR + 0x34000) +#elif CONFIG_MX6SX +#define ENET2_BASE_ADDR (AIPS2_OFF_BASE_ADDR + 0x34000) #else #define MMDC_P1_BASE_ADDR (AIPS2_OFF_BASE_ADDR + 0x34000) #endif @@ -202,13 +252,28 @@ #define CSU_BASE_ADDR (AIPS2_OFF_BASE_ADDR + 0x40000) #define IP2APB_PERFMON1_BASE_ADDR (AIPS2_OFF_BASE_ADDR + 0x44000) #define IP2APB_PERFMON2_BASE_ADDR (AIPS2_OFF_BASE_ADDR + 0x48000) +#ifdef CONFIG_MX6SX +#define DEBUG_MONITOR_BASE_ADDR (AIPS2_OFF_BASE_ADDR + 0x4C000) +#else #define IP2APB_PERFMON3_BASE_ADDR (AIPS2_OFF_BASE_ADDR + 0x4C000) +#endif #define IP2APB_TZASC1_BASE_ADDR (AIPS2_OFF_BASE_ADDR + 0x50000) +#ifdef CONFIG_MX6SX +#define SAI1_BASE_ADDR (AIPS2_OFF_BASE_ADDR + 0x54000) +#else #define IP2APB_TZASC2_BASE_ADDR (AIPS2_OFF_BASE_ADDR + 0x54000) +#endif +#define AUDMUX_BASE_ADDR (AIPS2_OFF_BASE_ADDR + 0x58000) #define AUDMUX_BASE_ADDR (AIPS2_OFF_BASE_ADDR + 0x58000) +#ifdef CONFIG_MX6SX +#define SAI2_BASE_ADDR (AIPS2_OFF_BASE_ADDR + 0x5C000) +#define QSPI1_BASE_ADDR (AIPS2_OFF_BASE_ADDR + 0x60000) +#define QSPI2_BASE_ADDR (AIPS2_OFF_BASE_ADDR + 0x64000) +#else #define MIPI_CSI2_BASE_ADDR (AIPS2_OFF_BASE_ADDR + 0x5C000) #define MIPI_DSI_BASE_ADDR (AIPS2_OFF_BASE_ADDR + 0x60000) #define VDOA_BASE_ADDR (AIPS2_OFF_BASE_ADDR + 0x64000) +#endif #define UART2_BASE (AIPS2_OFF_BASE_ADDR + 0x68000) #define UART3_BASE (AIPS2_OFF_BASE_ADDR + 0x6C000) #define UART4_BASE (AIPS2_OFF_BASE_ADDR + 0x70000) @@ -216,10 +281,42 @@ #define IP2APB_USBPHY1_BASE_ADDR (AIPS2_OFF_BASE_ADDR + 0x78000) #define IP2APB_USBPHY2_BASE_ADDR (AIPS2_OFF_BASE_ADDR + 0x7C000) +#ifdef CONFIG_MX6SX +#define GIS_BASE_ADDR (AIPS3_ARB_BASE_ADDR + 0x04000) +#define DCIC1_BASE_ADDR (AIPS3_ARB_BASE_ADDR + 0x0C000) +#define DCIC2_BASE_ADDR (AIPS3_ARB_BASE_ADDR + 0x10000) +#define CSI1_BASE_ADDR (AIPS3_ARB_BASE_ADDR + 0x14000) +#define PXP_BASE_ADDR (AIPS3_ARB_BASE_ADDR + 0x18000) +#define CSI2_BASE_ADDR (AIPS3_ARB_BASE_ADDR + 0x1C000) +#define LCDIF1_BASE_ADDR (AIPS3_ARB_BASE_ADDR + 0x20000) +#define LCDIF2_BASE_ADDR (AIPS3_ARB_BASE_ADDR + 0x24000) +#define VADC_BASE_ADDR (AIPS3_ARB_BASE_ADDR + 0x28000) +#define VDEC_BASE_ADDR (AIPS3_ARB_BASE_ADDR + 0x2C000) +#define SPBA_BASE_ADDR (AIPS3_ARB_BASE_ADDR + 0x3C000) +#define AIPS3_CONFIG_BASE_ADDR (AIPS3_ARB_BASE_ADDR + 0x7C000) +#define ADC1_BASE_ADDR (AIPS3_ARB_BASE_ADDR + 0x80000) +#define ADC2_BASE_ADDR (AIPS3_ARB_BASE_ADDR + 0x84000) +#define WDOG3_BASE_ADDR (AIPS3_ARB_BASE_ADDR + 0x88000) +#define ECSPI5_BASE_ADDR (AIPS3_ARB_BASE_ADDR + 0x8C000) +#define HS_BASE_ADDR (AIPS3_ARB_BASE_ADDR + 0x90000) +#define MU_MCU_BASE_ADDR (AIPS3_ARB_BASE_ADDR + 0x94000) +#define CANFD_BASE_ADDR (AIPS3_ARB_BASE_ADDR + 0x98000) +#define MU_DSP_BASE_ADDR (AIPS3_ARB_BASE_ADDR + 0x9C000) +#define UART6_BASE_ADDR (AIPS3_ARB_BASE_ADDR + 0xA0000) +#define PWM5_BASE_ADDR (AIPS3_ARB_BASE_ADDR + 0xA4000) +#define PWM6_BASE_ADDR (AIPS3_ARB_BASE_ADDR + 0xA8000) +#define PWM7_BASE_ADDR (AIPS3_ARB_BASE_ADDR + 0xAC000) +#define PWM8_BASE_ADDR (AIPS3_ARB_BASE_ADDR + 0xB0000) +#endif + #define CHIP_REV_1_0 0x10 #define CHIP_REV_1_2 0x12 #define CHIP_REV_1_5 0x15 +#ifndef CONFIG_MX6SX #define IRAM_SIZE 0x00040000 +#else +#define IRAM_SIZE 0x00020000 +#endif #define FEC_QUIRK_ENET_MAC #if !(defined(__KERNEL_STRICT_NAMES) || defined(__ASSEMBLY__)) @@ -227,6 +324,19 @@ extern void imx_get_mac_from_fuse(int dev_id, unsigned char *mac); +#define SRC_SCR_CORE_1_RESET_OFFSET 14 +#define SRC_SCR_CORE_1_RESET_MASK (1<<SRC_SCR_CORE_1_RESET_OFFSET) +#define SRC_SCR_CORE_2_RESET_OFFSET 15 +#define SRC_SCR_CORE_2_RESET_MASK (1<<SRC_SCR_CORE_2_RESET_OFFSET) +#define SRC_SCR_CORE_3_RESET_OFFSET 16 +#define SRC_SCR_CORE_3_RESET_MASK (1<<SRC_SCR_CORE_3_RESET_OFFSET) +#define SRC_SCR_CORE_1_ENABLE_OFFSET 22 +#define SRC_SCR_CORE_1_ENABLE_MASK (1<<SRC_SCR_CORE_1_ENABLE_OFFSET) +#define SRC_SCR_CORE_2_ENABLE_OFFSET 23 +#define SRC_SCR_CORE_2_ENABLE_MASK (1<<SRC_SCR_CORE_2_ENABLE_OFFSET) +#define SRC_SCR_CORE_3_ENABLE_OFFSET 24 +#define SRC_SCR_CORE_3_ENABLE_MASK (1<<SRC_SCR_CORE_3_ENABLE_OFFSET) + /* System Reset Controller (SRC) */ struct src { u32 scr; @@ -251,6 +361,8 @@ struct src { /* GPR1 bitfields */ #define IOMUXC_GPR1_ENET_CLK_SEL_OFFSET 21 #define IOMUXC_GPR1_ENET_CLK_SEL_MASK (1 << IOMUXC_GPR1_ENET_CLK_SEL_OFFSET) +#define IOMUXC_GPR1_USB_OTG_ID_OFFSET 13 +#define IOMUXC_GPR1_USB_OTG_ID_SEL_MASK (1 << IOMUXC_GPR1_USB_OTG_ID_OFFSET) /* GPR3 bitfields */ #define IOMUXC_GPR3_GPU_DBG_OFFSET 29 @@ -301,9 +413,10 @@ struct src { struct iomuxc { +#ifdef CONFIG_MX6SX + u8 reserved[0x4000]; +#endif u32 gpr[14]; - u32 omux[5]; - /* mux and pad registers */ }; #define IOMUXC_GPR2_COUNTER_RESET_VAL_OFFSET 20 @@ -403,6 +516,7 @@ struct cspi_regs { #define MXC_CSPICTRL_RXOVF (1 << 6) #define MXC_CSPIPERIOD_32KHZ (1 << 15) #define MAX_SPI_BYTES 32 +#define SPI_MAX_NUM 4 /* Bit position inside CTRL register to be associated with SS */ #define MXC_CSPICTRL_CHAN 18 @@ -473,6 +587,22 @@ struct fuse_bank0_regs { u32 rsvd7[4]; }; +#ifdef CONFIG_MX6SX +struct fuse_bank4_regs { + u32 sjc_resp_low; + u32 rsvd0[3]; + u32 sjc_resp_high; + u32 rsvd1[3]; + u32 mac_addr_low; + u32 rsvd2[3]; + u32 mac_addr_high; + u32 rsvd3[3]; + u32 mac_addr2; + u32 rsvd4[7]; + u32 gp1; + u32 rsvd5[7]; +}; +#else struct fuse_bank4_regs { u32 sjc_resp_low; u32 rsvd0[3]; @@ -487,6 +617,7 @@ struct fuse_bank4_regs { u32 gp2; u32 rsvd5[3]; }; +#endif struct aipstz_regs { u32 mprot0; @@ -650,15 +781,6 @@ struct anatop_regs { #define ANATOP_PFD_CLKGATE_SHIFT(n) (7+((n)*8)) #define ANATOP_PFD_CLKGATE_MASK(n) (1<<ANATOP_PFD_CLKGATE_SHIFT(n)) -struct iomuxc_base_regs { - u32 gpr[14]; /* 0x000 */ - u32 obsrv[5]; /* 0x038 */ - u32 swmux_ctl[197]; /* 0x04c */ - u32 swpad_ctl[250]; /* 0x360 */ - u32 swgrp[26]; /* 0x748 */ - u32 daisy[104]; /* 0x7b0..94c */ -}; - struct wdog_regs { u16 wcr; /* Control */ u16 wsr; /* Service */ @@ -667,5 +789,21 @@ struct wdog_regs { u16 wmcr; /* Miscellaneous Control */ }; +#define PWMCR_PRESCALER(x) (((x - 1) & 0xFFF) << 4) +#define PWMCR_DOZEEN (1 << 24) +#define PWMCR_WAITEN (1 << 23) +#define PWMCR_DBGEN (1 << 22) +#define PWMCR_CLKSRC_IPG_HIGH (2 << 16) +#define PWMCR_CLKSRC_IPG (1 << 16) +#define PWMCR_EN (1 << 0) + +struct pwm_regs { + u32 cr; + u32 sr; + u32 ir; + u32 sar; + u32 pr; + u32 cnr; +}; #endif /* __ASSEMBLER__*/ #endif /* __ASM_ARCH_MX6_IMX_REGS_H__ */ diff --git a/arch/arm/include/asm/arch-mx6/iomux.h b/arch/arm/include/asm/arch-mx6/iomux.h index 6a4a632..f54db69 100644 --- a/arch/arm/include/asm/arch-mx6/iomux.h +++ b/arch/arm/include/asm/arch-mx6/iomux.h @@ -65,6 +65,16 @@ #define IOMUX_GPR1_FEC_MASK (IOMUX_GPR1_FEC_CLOCK_MUX1_SEL_MASK \ | IOMUX_GPR1_FEC_CLOCK_MUX2_SEL_MASK) +#define IOMUX_GPR1_FEC1_CLOCK_MUX1_SEL_MASK (0x1 << 17) +#define IOMUX_GPR1_FEC1_CLOCK_MUX2_SEL_MASK (0x1 << 13) +#define IOMUX_GPR1_FEC1_MASK (IOMUX_GPR1_FEC1_CLOCK_MUX1_SEL_MASK \ + | IOMUX_GPR1_FEC1_CLOCK_MUX2_SEL_MASK) + +#define IOMUX_GPR1_FEC2_CLOCK_MUX1_SEL_MASK (0x1 << 18) +#define IOMUX_GPR1_FEC2_CLOCK_MUX2_SEL_MASK (0x1 << 14) +#define IOMUX_GPR1_FEC2_MASK (IOMUX_GPR1_FEC2_CLOCK_MUX1_SEL_MASK \ + | IOMUX_GPR1_FEC2_CLOCK_MUX2_SEL_MASK) + #define IOMUXC_GPR13_SATA_PHY_8_RXEQ_0P5DB (0<<24) #define IOMUXC_GPR13_SATA_PHY_8_RXEQ_1P0DB (1<<24) #define IOMUXC_GPR13_SATA_PHY_8_RXEQ_1P5DB (2<<24) diff --git a/arch/arm/include/asm/arch-mx6/mx6-ddr.h b/arch/arm/include/asm/arch-mx6/mx6-ddr.h index d544d2e..5ebabfa 100644 --- a/arch/arm/include/asm/arch-mx6/mx6-ddr.h +++ b/arch/arm/include/asm/arch-mx6/mx6-ddr.h @@ -13,7 +13,11 @@ #if defined(CONFIG_MX6DL) || defined(CONFIG_MX6S) #include "mx6dl-ddr.h" #else +#ifdef CONFIG_MX6SX +#include "mx6sx-ddr.h" +#else #error "Please select cpu" +#endif /* CONFIG_MX6SX */ #endif /* CONFIG_MX6DL or CONFIG_MX6S */ #endif /* CONFIG_MX6Q */ #else diff --git a/arch/arm/include/asm/arch-mx6/mx6-pins.h b/arch/arm/include/asm/arch-mx6/mx6-pins.h index dcd7f8f..534f780 100644 --- a/arch/arm/include/asm/arch-mx6/mx6-pins.h +++ b/arch/arm/include/asm/arch-mx6/mx6-pins.h @@ -35,6 +35,8 @@ enum { }; #elif defined(CONFIG_MX6SL) #include "mx6sl_pins.h" +#elif defined(CONFIG_MX6SX) +#include "mx6sx_pins.h" #else #error "Please select cpu" #endif /* CONFIG_MX6Q */ diff --git a/arch/arm/include/asm/arch-mx6/mx6sx-ddr.h b/arch/arm/include/asm/arch-mx6/mx6sx-ddr.h new file mode 100644 index 0000000..2cc94aa --- /dev/null +++ b/arch/arm/include/asm/arch-mx6/mx6sx-ddr.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2014 Freescale Semiconductor, Inc. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __ASM_ARCH_MX6SX_DDR_H__ +#define __ASM_ARCH_MX6SX_DDR_H__ + +#ifndef CONFIG_MX6SX +#error "wrong CPU" +#endif + +#define MX6_IOM_DRAM_DQM0 0x020e02ec +#define MX6_IOM_DRAM_DQM1 0x020e02f0 +#define MX6_IOM_DRAM_DQM2 0x020e02f4 +#define MX6_IOM_DRAM_DQM3 0x020e02f8 + +#define MX6_IOM_DRAM_RAS 0x020e02fc +#define MX6_IOM_DRAM_CAS 0x020e0300 +#define MX6_IOM_DRAM_SDODT0 0x020e0310 +#define MX6_IOM_DRAM_SDODT1 0x020e0314 +#define MX6_IOM_DRAM_SDBA2 0x020e0320 +#define MX6_IOM_DRAM_SDCKE0 0x020e0324 +#define MX6_IOM_DRAM_SDCKE1 0x020e0328 +#define MX6_IOM_DRAM_SDCLK_0 0x020e032c +#define MX6_IOM_DRAM_RESET 0x020e0340 + +#define MX6_IOM_DRAM_SDQS0 0x020e0330 +#define MX6_IOM_DRAM_SDQS1 0x020e0334 +#define MX6_IOM_DRAM_SDQS2 0x020e0338 +#define MX6_IOM_DRAM_SDQS3 0x020e033c + +#define MX6_IOM_GRP_ADDDS 0x020e05f4 +#define MX6_IOM_DDRMODE_CTL 0x020e05f8 +#define MX6_IOM_GRP_DDRPKE 0x020e05fc +#define MX6_IOM_GRP_DDRMODE 0x020e0608 +#define MX6_IOM_GRP_B0DS 0x020e060c +#define MX6_IOM_GRP_B1DS 0x020e0610 +#define MX6_IOM_GRP_CTLDS 0x020e0614 +#define MX6_IOM_GRP_DDR_TYPE 0x020e0618 +#define MX6_IOM_GRP_B2DS 0x020e061c +#define MX6_IOM_GRP_B3DS 0x020e0620 + +#endif /*__ASM_ARCH_MX6SX_DDR_H__ */ diff --git a/arch/arm/include/asm/arch-mx6/mx6sx_pins.h b/arch/arm/include/asm/arch-mx6/mx6sx_pins.h new file mode 100644 index 0000000..7c6c1e8 --- /dev/null +++ b/arch/arm/include/asm/arch-mx6/mx6sx_pins.h @@ -0,0 +1,1675 @@ +/* + * Copyright (C) 2014 Freescale Semiconductor, Inc. All Rights Reserved. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __ASM_ARCH_MX6_MX6_PINS_H__ +#define __ASM_ARCH_MX6_MX6_PINS_H__ + +#include <asm/imx-common/iomux-v3.h> + +enum { + MX6_PAD_GPIO1_IO00__I2C1_SCL = IOMUX_PAD(0x035C, 0x0014, IOMUX_CONFIG_SION | 0, 0x07A8, 1, 0), + MX6_PAD_GPIO1_IO00__USDHC1_VSELECT = IOMUX_PAD(0x035C, 0x0014, 1, 0x0000, 0, 0), + MX6_PAD_GPIO1_IO00__SPDIF_LOCK = IOMUX_PAD(0x035C, 0x0014, 2, 0x0000, 0, 0), + MX6_PAD_GPIO1_IO00__CCM_WAIT = IOMUX_PAD(0x035C, 0x0014, 3, 0x0000, 0, 0), + MX6_PAD_GPIO1_IO00__WDOG1_WDOG_ANY = IOMUX_PAD(0x035C, 0x0014, 4, 0x0000, 0, 0), + MX6_PAD_GPIO1_IO00__GPIO1_IO_0 = IOMUX_PAD(0x035C, 0x0014, 5, 0x0000, 0, 0), + MX6_PAD_GPIO1_IO00__SNVS_HP_WRAPPER_VIO_5 = IOMUX_PAD(0x035C, 0x0014, 6, 0x0000, 0, 0), + MX6_PAD_GPIO1_IO00__PHY_DTB_1 = IOMUX_PAD(0x035C, 0x0014, 7, 0x0000, 0, 0), + + MX6_PAD_GPIO1_IO01__I2C1_SDA = IOMUX_PAD(0x0360, 0x0018, IOMUX_CONFIG_SION | 0, 0x07AC, 1, 0), + MX6_PAD_GPIO1_IO01__USDHC1_RESET_B = IOMUX_PAD(0x0360, 0x0018, 1, 0x0000, 0, 0), + MX6_PAD_GPIO1_IO01__SPDIF_SR_CLK = IOMUX_PAD(0x0360, 0x0018, 2, 0x0000, 0, 0), + MX6_PAD_GPIO1_IO01__CCM_STOP = IOMUX_PAD(0x0360, 0x0018, 3, 0x0000, 0, 0), + MX6_PAD_GPIO1_IO01__WDOG3_WDOG_B = IOMUX_PAD(0x0360, 0x0018, 4, 0x0000, 0, 0), + MX6_PAD_GPIO1_IO01__GPIO1_IO_1 = IOMUX_PAD(0x0360, 0x0018, 5, 0x0000, 0, 0), + MX6_PAD_GPIO1_IO01__SNVS_HP_WRAPPER_VIO_5_CTL = IOMUX_PAD(0x0360, 0x0018, 6, 0x0000, 0, 0), + MX6_PAD_GPIO1_IO01__PHY_DTB_0 = IOMUX_PAD(0x0360, 0x0018, 7, 0x0000, 0, 0), + + MX6_PAD_GPIO1_IO02__I2C2_SCL = IOMUX_PAD(0x0364, 0x001C, IOMUX_CONFIG_SION | 0, 0x07B0, 1, 0), + MX6_PAD_GPIO1_IO02__USDHC1_CD_B = IOMUX_PAD(0x0364, 0x001C, 1, 0x0864, 1, 0), + MX6_PAD_GPIO1_IO02__CSI2_MCLK = IOMUX_PAD(0x0364, 0x001C, 2, 0x0000, 0, 0), + MX6_PAD_GPIO1_IO02__CCM_DI0_EXT_CLK = IOMUX_PAD(0x0364, 0x001C, 3, 0x0000, 0, 0), + MX6_PAD_GPIO1_IO02__WDOG1_WDOG_B = IOMUX_PAD(0x0364, 0x001C, 4, 0x0000, 0, 0), + MX6_PAD_GPIO1_IO02__GPIO1_IO_2 = IOMUX_PAD(0x0364, 0x001C, 5, 0x0000, 0, 0), + MX6_PAD_GPIO1_IO02__CCM_REF_EN_B = IOMUX_PAD(0x0364, 0x001C, 6, 0x0000, 0, 0), + MX6_PAD_GPIO1_IO02__PHY_TDI = IOMUX_PAD(0x0364, 0x001C, 7, 0x0000, 0, 0), + + MX6_PAD_GPIO1_IO03__I2C2_SDA = IOMUX_PAD(0x0368, 0x0020, IOMUX_CONFIG_SION | 0, 0x07B4, 1, 0), + MX6_PAD_GPIO1_IO03__USDHC1_WP = IOMUX_PAD(0x0368, 0x0020, 1, 0x0868, 1, 0), + MX6_PAD_GPIO1_IO03__ENET1_REF_CLK_25M = IOMUX_PAD(0x0368, 0x0020, 2, 0x0000, 0, 0), + MX6_PAD_GPIO1_IO03__CCM_DI1_EXT_CLK = IOMUX_PAD(0x0368, 0x0020, 3, 0x0000, 0, 0), + MX6_PAD_GPIO1_IO03__WDOG2_WDOG_B = IOMUX_PAD(0x0368, 0x0020, 4, 0x0000, 0, 0), + MX6_PAD_GPIO1_IO03__GPIO1_IO_3 = IOMUX_PAD(0x0368, 0x0020, 5, 0x0000, 0, 0), + MX6_PAD_GPIO1_IO03__CCM_PLL3_BYP = IOMUX_PAD(0x0368, 0x0020, 6, 0x0000, 0, 0), + MX6_PAD_GPIO1_IO03__PHY_TCK = IOMUX_PAD(0x0368, 0x0020, 7, 0x0000, 0, 0), + + MX6_PAD_GPIO1_IO04__UART1_TX = IOMUX_PAD(0x036C, 0x0024, 0, 0x0830, 0, 0), + MX6_PAD_GPIO1_IO04__USDHC2_RESET_B = IOMUX_PAD(0x036C, 0x0024, 1, 0x0000, 0, 0), + MX6_PAD_GPIO1_IO04__ENET1_MDC = IOMUX_PAD(0x036C, 0x0024, 2, 0x0000, 0, 0), + MX6_PAD_GPIO1_IO04__OSC32K_32K_OUT = IOMUX_PAD(0x036C, 0x0024, 3, 0x0000, 0, 0), + MX6_PAD_GPIO1_IO04__ENET2_REF_CLK2 = IOMUX_PAD(0x036C, 0x0024, 4, 0x076C, 0, 0), + MX6_PAD_GPIO1_IO04__GPIO1_IO_4 = IOMUX_PAD(0x036C, 0x0024, 5, 0x0000, 0, 0), + MX6_PAD_GPIO1_IO04__CCM_PLL2_BYP = IOMUX_PAD(0x036C, 0x0024, 6, 0x0000, 0, 0), + MX6_PAD_GPIO1_IO04__PHY_TMS = IOMUX_PAD(0x036C, 0x0024, 7, 0x0000, 0, 0), + + MX6_PAD_GPIO1_IO05__UART1_RX = IOMUX_PAD(0x0370, 0x0028, 0, 0x0830, 1, 0), + MX6_PAD_GPIO1_IO05__USDHC2_VSELECT = IOMUX_PAD(0x0370, 0x0028, 1, 0x0000, 0, 0), + MX6_PAD_GPIO1_IO05__ENET1_MDIO = IOMUX_PAD(0x0370, 0x0028, 2, 0x0764, 0, 0), + MX6_PAD_GPIO1_IO05__ASRC_ASRC_EXT_CLK = IOMUX_PAD(0x0370, 0x0028, 3, 0x0000, 0, 0), + MX6_PAD_GPIO1_IO05__ENET1_REF_CLK1 = IOMUX_PAD(0x0370, 0x0028, 4, 0x0760, 0, 0), + MX6_PAD_GPIO1_IO05__GPIO1_IO_5 = IOMUX_PAD(0x0370, 0x0028, 5, 0x0000, 0, 0), + MX6_PAD_GPIO1_IO05__SRC_TESTER_ACK = IOMUX_PAD(0x0370, 0x0028, 6, 0x0000, 0, 0), + MX6_PAD_GPIO1_IO05__PHY_TDO = IOMUX_PAD(0x0370, 0x0028, 7, 0x0000, 0, 0), + + MX6_PAD_GPIO1_IO06__UART2_TX = IOMUX_PAD(0x0374, 0x002C, 0, 0x0838, 0, 0), + MX6_PAD_GPIO1_IO06__USDHC2_CD_B = IOMUX_PAD(0x0374, 0x002C, 1, 0x086C, 1, 0), + MX6_PAD_GPIO1_IO06__ENET2_MDC = IOMUX_PAD(0x0374, 0x002C, 2, 0x0000, 0, 0), + MX6_PAD_GPIO1_IO06__CSI1_MCLK = IOMUX_PAD(0x0374, 0x002C, 3, 0x0000, 0, 0), + MX6_PAD_GPIO1_IO06__UART1_RTS_B = IOMUX_PAD(0x0374, 0x002C, 4, 0x082C, 0, 0), + MX6_PAD_GPIO1_IO06__GPIO1_IO_6 = IOMUX_PAD(0x0374, 0x002C, 5, 0x0000, 0, 0), + MX6_PAD_GPIO1_IO06__SRC_ANY_PU_RESET = IOMUX_PAD(0x0374, 0x002C, 6, 0x0000, 0, 0), + MX6_PAD_GPIO1_IO06__OCOTP_CTRL_WRAPPER_FUSE_LATCHED = IOMUX_PAD(0x0374, 0x002C, 7, 0x0000, 0, 0), + + MX6_PAD_GPIO1_IO07__UART2_RX = IOMUX_PAD(0x0378, 0x0030, 0, 0x0838, 1, 0), + MX6_PAD_GPIO1_IO07__USDHC2_WP = IOMUX_PAD(0x0378, 0x0030, 1, 0x0870, 1, 0), + MX6_PAD_GPIO1_IO07__ENET2_MDIO = IOMUX_PAD(0x0378, 0x0030, 2, 0x0770, 0, 0), + MX6_PAD_GPIO1_IO07__AUDMUX_MCLK = IOMUX_PAD(0x0378, 0x0030, 3, 0x0000, 0, 0), + MX6_PAD_GPIO1_IO07__UART1_CTS_B = IOMUX_PAD(0x0378, 0x0030, 4, 0x082C, 1, 0), + MX6_PAD_GPIO1_IO07__GPIO1_IO_7 = IOMUX_PAD(0x0378, 0x0030, 5, 0x0000, 0, 0), + MX6_PAD_GPIO1_IO07__SRC_EARLY_RESET = IOMUX_PAD(0x0378, 0x0030, 6, 0x0000, 0, 0), + MX6_PAD_GPIO1_IO07__DCIC2_OUT = IOMUX_PAD(0x0378, 0x0030, 7, 0x0000, 0, 0), + MX6_PAD_GPIO1_IO07__VDEC_DEBUG_44 = IOMUX_PAD(0x0378, 0x0030, 8, 0x0000, 0, 0), + + MX6_PAD_GPIO1_IO08__USB_OTG1_OC = IOMUX_PAD(0x037C, 0x0034, 0, 0x0860, 0, 0), + MX6_PAD_GPIO1_IO08__WDOG1_WDOG_B = IOMUX_PAD(0x037C, 0x0034, 1, 0x0000, 0, 0), + MX6_PAD_GPIO1_IO08__SDMA_EXT_EVENT_0 = IOMUX_PAD(0x037C, 0x0034, 2, 0x081C, 0, 0), + MX6_PAD_GPIO1_IO08__CCM_PMIC_RDY = IOMUX_PAD(0x037C, 0x0034, 3, 0x069C, 1, 0), + MX6_PAD_GPIO1_IO08__UART2_RTS_B = IOMUX_PAD(0x037C, 0x0034, 4, 0x0834, 0, 0), + MX6_PAD_GPIO1_IO08__GPIO1_IO_8 = IOMUX_PAD(0x037C, 0x0034, 5, 0x0000, 0, 0), + MX6_PAD_GPIO1_IO08__SRC_SYSTEM_RESET = IOMUX_PAD(0x037C, 0x0034, 6, 0x0000, 0, 0), + MX6_PAD_GPIO1_IO08__DCIC1_OUT = IOMUX_PAD(0x037C, 0x0034, 7, 0x0000, 0, 0), + MX6_PAD_GPIO1_IO08__VDEC_DEBUG_43 = IOMUX_PAD(0x037C, 0x0034, 8, 0x0000, 0, 0), + + MX6_PAD_GPIO1_IO09__USB_OTG1_PWR = IOMUX_PAD(0x0380, 0x0038, 0, 0x0000, 0, 0), + MX6_PAD_GPIO1_IO09__WDOG2_WDOG_B = IOMUX_PAD(0x0380, 0x0038, 1, 0x0000, 0, 0), + MX6_PAD_GPIO1_IO09__SDMA_EXT_EVENT_1 = IOMUX_PAD(0x0380, 0x0038, 2, 0x0820, 0, 0), + MX6_PAD_GPIO1_IO09__CCM_OUT0 = IOMUX_PAD(0x0380, 0x0038, 3, 0x0000, 0, 0), + MX6_PAD_GPIO1_IO09__UART2_CTS_B = IOMUX_PAD(0x0380, 0x0038, 4, 0x0834, 1, 0), + MX6_PAD_GPIO1_IO09__GPIO1_IO_9 = IOMUX_PAD(0x0380, 0x0038, 5, 0x0000, 0, 0), + MX6_PAD_GPIO1_IO09__SRC_INT_BOOT = IOMUX_PAD(0x0380, 0x0038, 6, 0x0000, 0, 0), + MX6_PAD_GPIO1_IO09__OBSERVE_MUX_OUT_4 = IOMUX_PAD(0x0380, 0x0038, 7, 0x0000, 0, 0), + MX6_PAD_GPIO1_IO09__VDEC_DEBUG_42 = IOMUX_PAD(0x0380, 0x0038, 8, 0x0000, 0, 0), + + MX6_PAD_GPIO1_IO10__ANATOP_OTG1_ID = IOMUX_PAD(0x0384, 0x003C, 0, 0x0624, 0, 0), + MX6_PAD_GPIO1_IO10__SPDIF_EXT_CLK = IOMUX_PAD(0x0384, 0x003C, 1, 0x0828, 0, 0), + MX6_PAD_GPIO1_IO10__PWM1_OUT = IOMUX_PAD(0x0384, 0x003C, 2, 0x0000, 0, 0), + MX6_PAD_GPIO1_IO10__CCM_OUT1 = IOMUX_PAD(0x0384, 0x003C, 3, 0x0000, 0, 0), + MX6_PAD_GPIO1_IO10__CSI1_FIELD = IOMUX_PAD(0x0384, 0x003C, 4, 0x070C, 1, 0), + MX6_PAD_GPIO1_IO10__GPIO1_IO_10 = IOMUX_PAD(0x0384, 0x003C, 5, 0x0000, 0, 0), + MX6_PAD_GPIO1_IO10__CSU_CSU_INT_DEB = IOMUX_PAD(0x0384, 0x003C, 6, 0x0000, 0, 0), + MX6_PAD_GPIO1_IO10__OBSERVE_MUX_OUT_3 = IOMUX_PAD(0x0384, 0x003C, 7, 0x0000, 0, 0), + MX6_PAD_GPIO1_IO10__VDEC_DEBUG_41 = IOMUX_PAD(0x0384, 0x003C, 8, 0x0000, 0, 0), + + MX6_PAD_GPIO1_IO11__USB_OTG2_OC = IOMUX_PAD(0x0388, 0x0040, 0, 0x085C, 0, 0), + MX6_PAD_GPIO1_IO11__SPDIF_IN = IOMUX_PAD(0x0388, 0x0040, 1, 0x0824, 2, 0), + MX6_PAD_GPIO1_IO11__PWM2_OUT = IOMUX_PAD(0x0388, 0x0040, 2, 0x0000, 0, 0), + MX6_PAD_GPIO1_IO11__CCM_CLKO1 = IOMUX_PAD(0x0388, 0x0040, 3, 0x0000, 0, 0), + MX6_PAD_GPIO1_IO11__MLB_DATA = IOMUX_PAD(0x0388, 0x0040, 4, 0x07EC, 0, 0), + MX6_PAD_GPIO1_IO11__GPIO1_IO_11 = IOMUX_PAD(0x0388, 0x0040, 5, 0x0000, 0, 0), + MX6_PAD_GPIO1_IO11__CSU_CSU_ALARM_AUT_0 = IOMUX_PAD(0x0388, 0x0040, 6, 0x0000, 0, 0), + MX6_PAD_GPIO1_IO11__OBSERVE_MUX_OUT_2 = IOMUX_PAD(0x0388, 0x0040, 7, 0x0000, 0, 0), + MX6_PAD_GPIO1_IO11__VDEC_DEBUG_40 = IOMUX_PAD(0x0388, 0x0040, 8, 0x0000, 0, 0), + + MX6_PAD_GPIO1_IO12__USB_OTG2_PWR = IOMUX_PAD(0x038C, 0x0044, 0, 0x0000, 0, 0), + MX6_PAD_GPIO1_IO12__SPDIF_OUT = IOMUX_PAD(0x038C, 0x0044, 1, 0x0000, 0, 0), + MX6_PAD_GPIO1_IO12__PWM3_OUT = IOMUX_PAD(0x038C, 0x0044, 2, 0x0000, 0, 0), + MX6_PAD_GPIO1_IO12__CCM_CLKO2 = IOMUX_PAD(0x038C, 0x0044, 3, 0x0000, 0, 0), + MX6_PAD_GPIO1_IO12__MLB_CLK = IOMUX_PAD(0x038C, 0x0044, 4, 0x07E8, 0, 0), + MX6_PAD_GPIO1_IO12__GPIO1_IO_12 = IOMUX_PAD(0x038C, 0x0044, 5, 0x0000, 0, 0), + MX6_PAD_GPIO1_IO12__CSU_CSU_ALARM_AUT_1 = IOMUX_PAD(0x038C, 0x0044, 6, 0x0000, 0, 0), + MX6_PAD_GPIO1_IO12__OBSERVE_MUX_OUT_1 = IOMUX_PAD(0x038C, 0x0044, 7, 0x0000, 0, 0), + MX6_PAD_GPIO1_IO12__VDEC_DEBUG_39 = IOMUX_PAD(0x038C, 0x0044, 8, 0x0000, 0, 0), + + MX6_PAD_GPIO1_IO13__WDOG1_WDOG_ANY = IOMUX_PAD(0x0390, 0x0048, 0, 0x0000, 0, 0), + MX6_PAD_GPIO1_IO13__ANATOP_OTG2_ID = IOMUX_PAD(0x0390, 0x0048, 1, 0x0628, 0, 0), + MX6_PAD_GPIO1_IO13__PWM4_OUT = IOMUX_PAD(0x0390, 0x0048, 2, 0x0000, 0, 0), + MX6_PAD_GPIO1_IO13__CCM_OUT2 = IOMUX_PAD(0x0390, 0x0048, 3, 0x0000, 0, 0), + MX6_PAD_GPIO1_IO13__MLB_SIG = IOMUX_PAD(0x0390, 0x0048, 4, 0x07F0, 0, 0), + MX6_PAD_GPIO1_IO13__GPIO1_IO_13 = IOMUX_PAD(0x0390, 0x0048, 5, 0x0000, 0, 0), + MX6_PAD_GPIO1_IO13__CSU_CSU_ALARM_AUT_2 = IOMUX_PAD(0x0390, 0x0048, 6, 0x0000, 0, 0), + MX6_PAD_GPIO1_IO13__OBSERVE_MUX_OUT_0 = IOMUX_PAD(0x0390, 0x0048, 7, 0x0000, 0, 0), + MX6_PAD_GPIO1_IO13__VDEC_DEBUG_38 = IOMUX_PAD(0x0390, 0x0048, 8, 0x0000, 0, 0), + + MX6_PAD_CSI_DATA00__CSI1_DATA_2 = IOMUX_PAD(0x0394, 0x004C, 0, 0x06A8, 0, 0), + MX6_PAD_CSI_DATA00__ESAI_TX_CLK = IOMUX_PAD(0x0394, 0x004C, 1, 0x078C, 1, 0), + MX6_PAD_CSI_DATA00__AUDMUX_AUD6_TXC = IOMUX_PAD(0x0394, 0x004C, 2, 0x0684, 1, 0), + MX6_PAD_CSI_DATA00__I2C1_SCL = IOMUX_PAD(0x0394, 0x004C, 3, 0x07A8, 0, 0), + MX6_PAD_CSI_DATA00__UART6_RI_B = IOMUX_PAD(0x0394, 0x004C, 4, 0x0000, 0, 0), + MX6_PAD_CSI_DATA00__GPIO1_IO_14 = IOMUX_PAD(0x0394, 0x004C, 5, 0x0000, 0, 0), + MX6_PAD_CSI_DATA00__WEIM_DATA_23 = IOMUX_PAD(0x0394, 0x004C, 6, 0x0000, 0, 0), + MX6_PAD_CSI_DATA00__SAI1_TX_BCLK = IOMUX_PAD(0x0394, 0x004C, 7, 0x0800, 0, 0), + MX6_PAD_CSI_DATA00__VADC_DATA_4 = IOMUX_PAD(0x0394, 0x004C, 8, 0x0000, 0, 0), + MX6_PAD_CSI_DATA00__MMDC_DEBUG_37 = IOMUX_PAD(0x0394, 0x004C, 9, 0x0000, 0, 0), + + MX6_PAD_CSI_DATA01__CSI1_DATA_3 = IOMUX_PAD(0x0398, 0x0050, 0, 0x06AC, 0, 0), + MX6_PAD_CSI_DATA01__ESAI_TX_FS = IOMUX_PAD(0x0398, 0x0050, 1, 0x077C, 1, 0), + MX6_PAD_CSI_DATA01__AUDMUX_AUD6_TXFS = IOMUX_PAD(0x0398, 0x0050, 2, 0x0688, 1, 0), + MX6_PAD_CSI_DATA01__I2C1_SDA = IOMUX_PAD(0x0398, 0x0050, 3, 0x07AC, 0, 0), + MX6_PAD_CSI_DATA01__UART6_DSR_B = IOMUX_PAD(0x0398, 0x0050, 4, 0x0000, 0, 0), + MX6_PAD_CSI_DATA01__GPIO1_IO_15 = IOMUX_PAD(0x0398, 0x0050, 5, 0x0000, 0, 0), + MX6_PAD_CSI_DATA01__WEIM_DATA_22 = IOMUX_PAD(0x0398, 0x0050, 6, 0x0000, 0, 0), + MX6_PAD_CSI_DATA01__SAI1_TX_SYNC = IOMUX_PAD(0x0398, 0x0050, 7, 0x0804, 0, 0), + MX6_PAD_CSI_DATA01__VADC_DATA_5 = IOMUX_PAD(0x0398, 0x0050, 8, 0x0000, 0, 0), + MX6_PAD_CSI_DATA01__MMDC_DEBUG_38 = IOMUX_PAD(0x0398, 0x0050, 9, 0x0000, 0, 0), + + MX6_PAD_CSI_DATA02__CSI1_DATA_4 = IOMUX_PAD(0x039C, 0x0054, 0, 0x06B0, 0, 0), + MX6_PAD_CSI_DATA02__ESAI_RX_CLK = IOMUX_PAD(0x039C, 0x0054, 1, 0x0788, 1, 0), + MX6_PAD_CSI_DATA02__AUDMUX_AUD6_RXC = IOMUX_PAD(0x039C, 0x0054, 2, 0x067C, 1, 0), + MX6_PAD_CSI_DATA02__KPP_COL_5 = IOMUX_PAD(0x039C, 0x0054, 3, 0x07C8, 0, 0), + MX6_PAD_CSI_DATA02__UART6_DTR_B = IOMUX_PAD(0x039C, 0x0054, 4, 0x0000, 0, 0), + MX6_PAD_CSI_DATA02__GPIO1_IO_16 = IOMUX_PAD(0x039C, 0x0054, 5, 0x0000, 0, 0), + MX6_PAD_CSI_DATA02__WEIM_DATA_21 = IOMUX_PAD(0x039C, 0x0054, 6, 0x0000, 0, 0), + MX6_PAD_CSI_DATA02__SAI1_RX_BCLK = IOMUX_PAD(0x039C, 0x0054, 7, 0x07F4, 0, 0), + MX6_PAD_CSI_DATA02__VADC_DATA_6 = IOMUX_PAD(0x039C, 0x0054, 8, 0x0000, 0, 0), + MX6_PAD_CSI_DATA02__MMDC_DEBUG_39 = IOMUX_PAD(0x039C, 0x0054, 9, 0x0000, 0, 0), + + MX6_PAD_CSI_DATA03__CSI1_DATA_5 = IOMUX_PAD(0x03A0, 0x0058, 0, 0x06B4, 0, 0), + MX6_PAD_CSI_DATA03__ESAI_RX_FS = IOMUX_PAD(0x03A0, 0x0058, 1, 0x0778, 1, 0), + MX6_PAD_CSI_DATA03__AUDMUX_AUD6_RXFS = IOMUX_PAD(0x03A0, 0x0058, 2, 0x0680, 1, 0), + MX6_PAD_CSI_DATA03__KPP_ROW_5 = IOMUX_PAD(0x03A0, 0x0058, 3, 0x07D4, 0, 0), + MX6_PAD_CSI_DATA03__UART6_DCD_B = IOMUX_PAD(0x03A0, 0x0058, 4, 0x0000, 0, 0), + MX6_PAD_CSI_DATA03__GPIO1_IO_17 = IOMUX_PAD(0x03A0, 0x0058, 5, 0x0000, 0, 0), + MX6_PAD_CSI_DATA03__WEIM_DATA_20 = IOMUX_PAD(0x03A0, 0x0058, 6, 0x0000, 0, 0), + MX6_PAD_CSI_DATA03__SAI1_RX_SYNC = IOMUX_PAD(0x03A0, 0x0058, 7, 0x07FC, 0, 0), + MX6_PAD_CSI_DATA03__VADC_DATA_7 = IOMUX_PAD(0x03A0, 0x0058, 8, 0x0000, 0, 0), + MX6_PAD_CSI_DATA03__MMDC_DEBUG_40 = IOMUX_PAD(0x03A0, 0x0058, 9, 0x0000, 0, 0), + + MX6_PAD_CSI_DATA04__CSI1_DATA_6 = IOMUX_PAD(0x03A4, 0x005C, 0, 0x06B8, 0, 0), + MX6_PAD_CSI_DATA04__ESAI_TX1 = IOMUX_PAD(0x03A4, 0x005C, 1, 0x0794, 1, 0), + MX6_PAD_CSI_DATA04__SPDIF_OUT = IOMUX_PAD(0x03A4, 0x005C, 2, 0x0000, 0, 0), + MX6_PAD_CSI_DATA04__KPP_COL_6 = IOMUX_PAD(0x03A4, 0x005C, 3, 0x07CC, 0, 0), + MX6_PAD_CSI_DATA04__UART6_RX = IOMUX_PAD(0x03A4, 0x005C, 4, 0x0858, 0, 0), + MX6_PAD_CSI_DATA04__GPIO1_IO_18 = IOMUX_PAD(0x03A4, 0x005C, 5, 0x0000, 0, 0), + MX6_PAD_CSI_DATA04__WEIM_DATA_19 = IOMUX_PAD(0x03A4, 0x005C, 6, 0x0000, 0, 0), + MX6_PAD_CSI_DATA04__PWM5_OUT = IOMUX_PAD(0x03A4, 0x005C, 7, 0x0000, 0, 0), + MX6_PAD_CSI_DATA04__VADC_DATA_8 = IOMUX_PAD(0x03A4, 0x005C, 8, 0x0000, 0, 0), + MX6_PAD_CSI_DATA04__MMDC_DEBUG_41 = IOMUX_PAD(0x03A4, 0x005C, 9, 0x0000, 0, 0), + + MX6_PAD_CSI_DATA05__CSI1_DATA_7 = IOMUX_PAD(0x03A8, 0x0060, 0, 0x06BC, 0, 0), + MX6_PAD_CSI_DATA05__ESAI_TX4_RX1 = IOMUX_PAD(0x03A8, 0x0060, 1, 0x07A0, 1, 0), + MX6_PAD_CSI_DATA05__SPDIF_IN = IOMUX_PAD(0x03A8, 0x0060, 2, 0x0824, 1, 0), + MX6_PAD_CSI_DATA05__KPP_ROW_6 = IOMUX_PAD(0x03A8, 0x0060, 3, 0x07D8, 0, 0), + MX6_PAD_CSI_DATA05__UART6_TX = IOMUX_PAD(0x03A8, 0x0060, 4, 0x0858, 1, 0), + MX6_PAD_CSI_DATA05__GPIO1_IO_19 = IOMUX_PAD(0x03A8, 0x0060, 5, 0x0000, 0, 0), + MX6_PAD_CSI_DATA05__WEIM_DATA_18 = IOMUX_PAD(0x03A8, 0x0060, 6, 0x0000, 0, 0), + MX6_PAD_CSI_DATA05__PWM6_OUT = IOMUX_PAD(0x03A8, 0x0060, 7, 0x0000, 0, 0), + MX6_PAD_CSI_DATA05__VADC_DATA_9 = IOMUX_PAD(0x03A8, 0x0060, 8, 0x0000, 0, 0), + MX6_PAD_CSI_DATA05__MMDC_DEBUG_42 = IOMUX_PAD(0x03A8, 0x0060, 9, 0x0000, 0, 0), + + MX6_PAD_CSI_DATA06__CSI1_DATA_8 = IOMUX_PAD(0x03AC, 0x0064, 0, 0x06C0, 0, 0), + MX6_PAD_CSI_DATA06__ESAI_TX2_RX3 = IOMUX_PAD(0x03AC, 0x0064, 1, 0x0798, 1, 0), + MX6_PAD_CSI_DATA06__I2C4_SCL = IOMUX_PAD(0x03AC, 0x0064, 2, 0x07C0, 2, 0), + MX6_PAD_CSI_DATA06__KPP_COL_7 = IOMUX_PAD(0x03AC, 0x0064, 3, 0x07D0, 0, 0), + MX6_PAD_CSI_DATA06__UART6_RTS_B = IOMUX_PAD(0x03AC, 0x0064, 4, 0x0854, 0, 0), + MX6_PAD_CSI_DATA06__GPIO1_IO_20 = IOMUX_PAD(0x03AC, 0x0064, 5, 0x0000, 0, 0), + MX6_PAD_CSI_DATA06__WEIM_DATA_17 = IOMUX_PAD(0x03AC, 0x0064, 6, 0x0000, 0, 0), + MX6_PAD_CSI_DATA06__DCIC2_OUT = IOMUX_PAD(0x03AC, 0x0064, 7, 0x0000, 0, 0), + MX6_PAD_CSI_DATA06__VADC_DATA_10 = IOMUX_PAD(0x03AC, 0x0064, 8, 0x0000, 0, 0), + MX6_PAD_CSI_DATA06__MMDC_DEBUG_43 = IOMUX_PAD(0x03AC, 0x0064, 9, 0x0000, 0, 0), + + MX6_PAD_CSI_DATA07__CSI1_DATA_9 = IOMUX_PAD(0x03B0, 0x0068, 0, 0x06C4, 0, 0), + MX6_PAD_CSI_DATA07__ESAI_TX3_RX2 = IOMUX_PAD(0x03B0, 0x0068, 1, 0x079C, 1, 0), + MX6_PAD_CSI_DATA07__I2C4_SDA = IOMUX_PAD(0x03B0, 0x0068, 2, 0x07C4, 2, 0), + MX6_PAD_CSI_DATA07__KPP_ROW_7 = IOMUX_PAD(0x03B0, 0x0068, 3, 0x07DC, 0, 0), + MX6_PAD_CSI_DATA07__UART6_CTS_B = IOMUX_PAD(0x03B0, 0x0068, 4, 0x0854, 1, 0), + MX6_PAD_CSI_DATA07__GPIO1_IO_21 = IOMUX_PAD(0x03B0, 0x0068, 5, 0x0000, 0, 0), + MX6_PAD_CSI_DATA07__WEIM_DATA_16 = IOMUX_PAD(0x03B0, 0x0068, 6, 0x0000, 0, 0), + MX6_PAD_CSI_DATA07__DCIC1_OUT = IOMUX_PAD(0x03B0, 0x0068, 7, 0x0000, 0, 0), + MX6_PAD_CSI_DATA07__VADC_DATA_11 = IOMUX_PAD(0x03B0, 0x0068, 8, 0x0000, 0, 0), + MX6_PAD_CSI_DATA07__MMDC_DEBUG_44 = IOMUX_PAD(0x03B0, 0x0068, 9, 0x0000, 0, 0), + + MX6_PAD_CSI_HSYNC__CSI1_HSYNC = IOMUX_PAD(0x03B4, 0x006C, 0, 0x0700, 0, 0), + MX6_PAD_CSI_HSYNC__ESAI_TX0 = IOMUX_PAD(0x03B4, 0x006C, 1, 0x0790, 1, 0), + MX6_PAD_CSI_HSYNC__AUDMUX_AUD6_TXD = IOMUX_PAD(0x03B4, 0x006C, 2, 0x0678, 1, 0), + MX6_PAD_CSI_HSYNC__UART4_RTS_B = IOMUX_PAD(0x03B4, 0x006C, 3, 0x0844, 2, 0), + MX6_PAD_CSI_HSYNC__MQS_LEFT = IOMUX_PAD(0x03B4, 0x006C, 4, 0x0000, 0, 0), + MX6_PAD_CSI_HSYNC__GPIO1_IO_22 = IOMUX_PAD(0x03B4, 0x006C, 5, 0x0000, 0, 0), + MX6_PAD_CSI_HSYNC__WEIM_DATA_25 = IOMUX_PAD(0x03B4, 0x006C, 6, 0x0000, 0, 0), + MX6_PAD_CSI_HSYNC__SAI1_TX_DATA_0 = IOMUX_PAD(0x03B4, 0x006C, 7, 0x0000, 0, 0), + MX6_PAD_CSI_HSYNC__VADC_DATA_2 = IOMUX_PAD(0x03B4, 0x006C, 8, 0x0000, 0, 0), + MX6_PAD_CSI_HSYNC__MMDC_DEBUG_35 = IOMUX_PAD(0x03B4, 0x006C, 9, 0x0000, 0, 0), + + MX6_PAD_CSI_MCLK__CSI1_MCLK = IOMUX_PAD(0x03B8, 0x0070, 0, 0x0000, 0, 0), + MX6_PAD_CSI_MCLK__ESAI_TX_HF_CLK = IOMUX_PAD(0x03B8, 0x0070, 1, 0x0784, 1, 0), + MX6_PAD_CSI_MCLK__OSC32K_32K_OUT = IOMUX_PAD(0x03B8, 0x0070, 2, 0x0000, 0, 0), + MX6_PAD_CSI_MCLK__UART4_RX = IOMUX_PAD(0x03B8, 0x0070, 3, 0x0848, 2, 0), + MX6_PAD_CSI_MCLK__ANATOP_32K_OUT = IOMUX_PAD(0x03B8, 0x0070, 4, 0x0000, 0, 0), + MX6_PAD_CSI_MCLK__GPIO1_IO_23 = IOMUX_PAD(0x03B8, 0x0070, 5, 0x0000, 0, 0), + MX6_PAD_CSI_MCLK__WEIM_DATA_26 = IOMUX_PAD(0x03B8, 0x0070, 6, 0x0000, 0, 0), + MX6_PAD_CSI_MCLK__CSI1_FIELD = IOMUX_PAD(0x03B8, 0x0070, 7, 0x070C, 0, 0), + MX6_PAD_CSI_MCLK__VADC_DATA_1 = IOMUX_PAD(0x03B8, 0x0070, 8, 0x0000, 0, 0), + MX6_PAD_CSI_MCLK__MMDC_DEBUG_34 = IOMUX_PAD(0x03B8, 0x0070, 9, 0x0000, 0, 0), + + MX6_PAD_CSI_PIXCLK__CSI1_PIXCLK = IOMUX_PAD(0x03BC, 0x0074, 0, 0x0704, 0, 0), + MX6_PAD_CSI_PIXCLK__ESAI_RX_HF_CLK = IOMUX_PAD(0x03BC, 0x0074, 1, 0x0780, 1, 0), + MX6_PAD_CSI_PIXCLK__AUDMUX_MCLK = IOMUX_PAD(0x03BC, 0x0074, 2, 0x0000, 0, 0), + MX6_PAD_CSI_PIXCLK__UART4_TX = IOMUX_PAD(0x03BC, 0x0074, 3, 0x0848, 3, 0), + MX6_PAD_CSI_PIXCLK__ANATOP_24M_OUT = IOMUX_PAD(0x03BC, 0x0074, 4, 0x0000, 0, 0), + MX6_PAD_CSI_PIXCLK__GPIO1_IO_24 = IOMUX_PAD(0x03BC, 0x0074, 5, 0x0000, 0, 0), + MX6_PAD_CSI_PIXCLK__WEIM_DATA_27 = IOMUX_PAD(0x03BC, 0x0074, 6, 0x0000, 0, 0), + MX6_PAD_CSI_PIXCLK__ESAI_TX_HF_CLK = IOMUX_PAD(0x03BC, 0x0074, 7, 0x0784, 2, 0), + MX6_PAD_CSI_PIXCLK__VADC_CLK = IOMUX_PAD(0x03BC, 0x0074, 8, 0x0000, 0, 0), + MX6_PAD_CSI_PIXCLK__MMDC_DEBUG_33 = IOMUX_PAD(0x03BC, 0x0074, 9, 0x0000, 0, 0), + + MX6_PAD_CSI_VSYNC__CSI1_VSYNC = IOMUX_PAD(0x03C0, 0x0078, 0, 0x0708, 0, 0), + MX6_PAD_CSI_VSYNC__ESAI_TX5_RX0 = IOMUX_PAD(0x03C0, 0x0078, 1, 0x07A4, 1, 0), + MX6_PAD_CSI_VSYNC__AUDMUX_AUD6_RXD = IOMUX_PAD(0x03C0, 0x0078, 2, 0x0674, 1, 0), + MX6_PAD_CSI_VSYNC__UART4_CTS_B = IOMUX_PAD(0x03C0, 0x0078, 3, 0x0844, 3, 0), + MX6_PAD_CSI_VSYNC__MQS_RIGHT = IOMUX_PAD(0x03C0, 0x0078, 4, 0x0000, 0, 0), + MX6_PAD_CSI_VSYNC__GPIO1_IO_25 = IOMUX_PAD(0x03C0, 0x0078, 5, 0x0000, 0, 0), + MX6_PAD_CSI_VSYNC__WEIM_DATA_24 = IOMUX_PAD(0x03C0, 0x0078, 6, 0x0000, 0, 0), + MX6_PAD_CSI_VSYNC__SAI1_RX_DATA_0 = IOMUX_PAD(0x03C0, 0x0078, 7, 0x07F8, 0, 0), + MX6_PAD_CSI_VSYNC__VADC_DATA_3 = IOMUX_PAD(0x03C0, 0x0078, 8, 0x0000, 0, 0), + MX6_PAD_CSI_VSYNC__MMDC_DEBUG_36 = IOMUX_PAD(0x03C0, 0x0078, 9, 0x0000, 0, 0), + + MX6_PAD_ENET1_COL__ENET1_COL = IOMUX_PAD(0x03C4, 0x007C, 0, 0x0000, 0, 0), + MX6_PAD_ENET1_COL__ENET2_MDC = IOMUX_PAD(0x03C4, 0x007C, 1, 0x0000, 0, 0), + MX6_PAD_ENET1_COL__AUDMUX_AUD4_TXC = IOMUX_PAD(0x03C4, 0x007C, 2, 0x0654, 1, 0), + MX6_PAD_ENET1_COL__UART1_RI_B = IOMUX_PAD(0x03C4, 0x007C, 3, 0x0000, 0, 0), + MX6_PAD_ENET1_COL__SPDIF_EXT_CLK = IOMUX_PAD(0x03C4, 0x007C, 4, 0x0828, 1, 0), + MX6_PAD_ENET1_COL__GPIO2_IO_0 = IOMUX_PAD(0x03C4, 0x007C, 5, 0x0000, 0, 0), + MX6_PAD_ENET1_COL__CSI2_DATA_23 = IOMUX_PAD(0x03C4, 0x007C, 6, 0x0000, 0, 0), + MX6_PAD_ENET1_COL__LCDIF2_DATA_16 = IOMUX_PAD(0x03C4, 0x007C, 7, 0x0000, 0, 0), + MX6_PAD_ENET1_COL__VDEC_DEBUG_37 = IOMUX_PAD(0x03C4, 0x007C, 8, 0x0000, 0, 0), + MX6_PAD_ENET1_COL__PCIE_CTRL_DEBUG_31 = IOMUX_PAD(0x03C4, 0x007C, 9, 0x0000, 0, 0), + + MX6_PAD_ENET1_CRS__ENET1_CRS = IOMUX_PAD(0x03C8, 0x0080, 0, 0x0000, 0, 0), + MX6_PAD_ENET1_CRS__ENET2_MDIO = IOMUX_PAD(0x03C8, 0x0080, 1, 0x0770, 1, 0), + MX6_PAD_ENET1_CRS__AUDMUX_AUD4_TXD = IOMUX_PAD(0x03C8, 0x0080, 2, 0x0648, 1, 0), + MX6_PAD_ENET1_CRS__UART1_DCD_B = IOMUX_PAD(0x03C8, 0x0080, 3, 0x0000, 0, 0), + MX6_PAD_ENET1_CRS__SPDIF_LOCK = IOMUX_PAD(0x03C8, 0x0080, 4, 0x0000, 0, 0), + MX6_PAD_ENET1_CRS__GPIO2_IO_1 = IOMUX_PAD(0x03C8, 0x0080, 5, 0x0000, 0, 0), + MX6_PAD_ENET1_CRS__CSI2_DATA_22 = IOMUX_PAD(0x03C8, 0x0080, 6, 0x0000, 0, 0), + MX6_PAD_ENET1_CRS__LCDIF2_DATA_17 = IOMUX_PAD(0x03C8, 0x0080, 7, 0x0000, 0, 0), + MX6_PAD_ENET1_CRS__VDEC_DEBUG_36 = IOMUX_PAD(0x03C8, 0x0080, 8, 0x0000, 0, 0), + MX6_PAD_ENET1_CRS__PCIE_CTRL_DEBUG_30 = IOMUX_PAD(0x03C8, 0x0080, 9, 0x0000, 0, 0), + + MX6_PAD_ENET1_MDC__ENET1_MDC = IOMUX_PAD(0x03CC, 0x0084, 0, 0x0000, 0, 0), + MX6_PAD_ENET1_MDC__ENET2_MDC = IOMUX_PAD(0x03CC, 0x0084, 1, 0x0000, 0, 0), + MX6_PAD_ENET1_MDC__AUDMUX_AUD3_RXFS = IOMUX_PAD(0x03CC, 0x0084, 2, 0x0638, 1, 0), + MX6_PAD_ENET1_MDC__ANATOP_24M_OUT = IOMUX_PAD(0x03CC, 0x0084, 3, 0x0000, 0, 0), + MX6_PAD_ENET1_MDC__EPIT2_OUT = IOMUX_PAD(0x03CC, 0x0084, 4, 0x0000, 0, 0), + MX6_PAD_ENET1_MDC__GPIO2_IO_2 = IOMUX_PAD(0x03CC, 0x0084, 5, 0x0000, 0, 0), + MX6_PAD_ENET1_MDC__USB_OTG1_PWR = IOMUX_PAD(0x03CC, 0x0084, 6, 0x0000, 0, 0), + MX6_PAD_ENET1_MDC__PWM7_OUT = IOMUX_PAD(0x03CC, 0x0084, 7, 0x0000, 0, 0), + + MX6_PAD_ENET1_MDIO__ENET1_MDIO = IOMUX_PAD(0x03D0, 0x0088, 0, 0x0764, 1, 0), + MX6_PAD_ENET1_MDIO__ENET2_MDIO = IOMUX_PAD(0x03D0, 0x0088, 1, 0x0770, 2, 0), + MX6_PAD_ENET1_MDIO__AUDMUX_MCLK = IOMUX_PAD(0x03D0, 0x0088, 2, 0x0000, 0, 0), + MX6_PAD_ENET1_MDIO__OSC32K_32K_OUT = IOMUX_PAD(0x03D0, 0x0088, 3, 0x0000, 0, 0), + MX6_PAD_ENET1_MDIO__EPIT1_OUT = IOMUX_PAD(0x03D0, 0x0088, 4, 0x0000, 0, 0), + MX6_PAD_ENET1_MDIO__GPIO2_IO_3 = IOMUX_PAD(0x03D0, 0x0088, 5, 0x0000, 0, 0), + MX6_PAD_ENET1_MDIO__USB_OTG1_OC = IOMUX_PAD(0x03D0, 0x0088, 6, 0x0860, 1, 0), + MX6_PAD_ENET1_MDIO__PWM8_OUT = IOMUX_PAD(0x03D0, 0x0088, 7, 0x0000, 0, 0), + + MX6_PAD_ENET1_RX_CLK__ENET1_RX_CLK = IOMUX_PAD(0x03D4, 0x008C, 0, 0x0768, 0, 0), + MX6_PAD_ENET1_RX_CLK__ENET1_REF_CLK_25M = IOMUX_PAD(0x03D4, 0x008C, 1, 0x0000, 0, 0), + MX6_PAD_ENET1_RX_CLK__AUDMUX_AUD4_TXFS = IOMUX_PAD(0x03D4, 0x008C, 2, 0x0658, 1, 0), + MX6_PAD_ENET1_RX_CLK__UART1_DSR_B = IOMUX_PAD(0x03D4, 0x008C, 3, 0x0000, 0, 0), + MX6_PAD_ENET1_RX_CLK__SPDIF_OUT = IOMUX_PAD(0x03D4, 0x008C, 4, 0x0000, 0, 0), + MX6_PAD_ENET1_RX_CLK__GPIO2_IO_4 = IOMUX_PAD(0x03D4, 0x008C, 5, 0x0000, 0, 0), + MX6_PAD_ENET1_RX_CLK__CSI2_DATA_21 = IOMUX_PAD(0x03D4, 0x008C, 6, 0x0000, 0, 0), + MX6_PAD_ENET1_RX_CLK__LCDIF2_DATA_18 = IOMUX_PAD(0x03D4, 0x008C, 7, 0x0000, 0, 0), + MX6_PAD_ENET1_RX_CLK__VDEC_DEBUG_35 = IOMUX_PAD(0x03D4, 0x008C, 8, 0x0000, 0, 0), + MX6_PAD_ENET1_RX_CLK__PCIE_CTRL_DEBUG_29 = IOMUX_PAD(0x03D4, 0x008C, 9, 0x0000, 0, 0), + + MX6_PAD_ENET1_TX_CLK__ENET1_TX_CLK = IOMUX_PAD(0x03D8, 0x0090, 0, 0x0000, 0, 0), + MX6_PAD_ENET1_TX_CLK__ENET1_REF_CLK1 = IOMUX_PAD(0x03D8, 0x0090, 1, 0x0760, 1, 0), + MX6_PAD_ENET1_TX_CLK__AUDMUX_AUD4_RXD = IOMUX_PAD(0x03D8, 0x0090, 2, 0x0644, 1, 0), + MX6_PAD_ENET1_TX_CLK__UART1_DTR_B = IOMUX_PAD(0x03D8, 0x0090, 3, 0x0000, 0, 0), + MX6_PAD_ENET1_TX_CLK__SPDIF_SR_CLK = IOMUX_PAD(0x03D8, 0x0090, 4, 0x0000, 0, 0), + MX6_PAD_ENET1_TX_CLK__GPIO2_IO_5 = IOMUX_PAD(0x03D8, 0x0090, 5, 0x0000, 0, 0), + MX6_PAD_ENET1_TX_CLK__CSI2_DATA_20 = IOMUX_PAD(0x03D8, 0x0090, 6, 0x0000, 0, 0), + MX6_PAD_ENET1_TX_CLK__LCDIF2_DATA_19 = IOMUX_PAD(0x03D8, 0x0090, 7, 0x0000, 0, 0), + MX6_PAD_ENET1_TX_CLK__VDEC_DEBUG_34 = IOMUX_PAD(0x03D8, 0x0090, 8, 0x0000, 0, 0), + MX6_PAD_ENET1_TX_CLK__PCIE_CTRL_DEBUG_28 = IOMUX_PAD(0x03D8, 0x0090, 9, 0x0000, 0, 0), + + MX6_PAD_ENET2_COL__ENET2_COL = IOMUX_PAD(0x03DC, 0x0094, 0, 0x0000, 0, 0), + MX6_PAD_ENET2_COL__ENET1_MDC = IOMUX_PAD(0x03DC, 0x0094, 1, 0x0000, 0, 0), + MX6_PAD_ENET2_COL__AUDMUX_AUD4_RXC = IOMUX_PAD(0x03DC, 0x0094, 2, 0x064C, 1, 0), + MX6_PAD_ENET2_COL__UART1_RX = IOMUX_PAD(0x03DC, 0x0094, 3, 0x0830, 2, 0), + MX6_PAD_ENET2_COL__SPDIF_IN = IOMUX_PAD(0x03DC, 0x0094, 4, 0x0824, 3, 0), + MX6_PAD_ENET2_COL__GPIO2_IO_6 = IOMUX_PAD(0x03DC, 0x0094, 5, 0x0000, 0, 0), + MX6_PAD_ENET2_COL__ANATOP_OTG1_ID = IOMUX_PAD(0x03DC, 0x0094, 6, 0x0624, 1, 0), + MX6_PAD_ENET2_COL__LCDIF2_DATA_20 = IOMUX_PAD(0x03DC, 0x0094, 7, 0x0000, 0, 0), + MX6_PAD_ENET2_COL__VDEC_DEBUG_33 = IOMUX_PAD(0x03DC, 0x0094, 8, 0x0000, 0, 0), + MX6_PAD_ENET2_COL__PCIE_CTRL_DEBUG_27 = IOMUX_PAD(0x03DC, 0x0094, 9, 0x0000, 0, 0), + + MX6_PAD_ENET2_CRS__ENET2_CRS = IOMUX_PAD(0x03E0, 0x0098, 0, 0x0000, 0, 0), + MX6_PAD_ENET2_CRS__ENET1_MDIO = IOMUX_PAD(0x03E0, 0x0098, 1, 0x0764, 2, 0), + MX6_PAD_ENET2_CRS__AUDMUX_AUD4_RXFS = IOMUX_PAD(0x03E0, 0x0098, 2, 0x0650, 1, 0), + MX6_PAD_ENET2_CRS__UART1_TX = IOMUX_PAD(0x03E0, 0x0098, 3, 0x0830, 3, 0), + MX6_PAD_ENET2_CRS__MLB_SIG = IOMUX_PAD(0x03E0, 0x0098, 4, 0x07F0, 1, 0), + MX6_PAD_ENET2_CRS__GPIO2_IO_7 = IOMUX_PAD(0x03E0, 0x0098, 5, 0x0000, 0, 0), + MX6_PAD_ENET2_CRS__ANATOP_OTG2_ID = IOMUX_PAD(0x03E0, 0x0098, 6, 0x0628, 1, 0), + MX6_PAD_ENET2_CRS__LCDIF2_DATA_21 = IOMUX_PAD(0x03E0, 0x0098, 7, 0x0000, 0, 0), + MX6_PAD_ENET2_CRS__VDEC_DEBUG_32 = IOMUX_PAD(0x03E0, 0x0098, 8, 0x0000, 0, 0), + MX6_PAD_ENET2_CRS__PCIE_CTRL_DEBUG_26 = IOMUX_PAD(0x03E0, 0x0098, 9, 0x0000, 0, 0), + + MX6_PAD_ENET2_RX_CLK__ENET2_RX_CLK = IOMUX_PAD(0x03E4, 0x009C, 0, 0x0774, 0, 0), + MX6_PAD_ENET2_RX_CLK__ENET2_REF_CLK_25M = IOMUX_PAD(0x03E4, 0x009C, 1, 0x0000, 0, 0), + MX6_PAD_ENET2_RX_CLK__I2C3_SCL = IOMUX_PAD(0x03E4, 0x009C, 2, 0x07B8, 1, 0), + MX6_PAD_ENET2_RX_CLK__UART1_RTS_B = IOMUX_PAD(0x03E4, 0x009C, 3, 0x082C, 2, 0), + MX6_PAD_ENET2_RX_CLK__MLB_DATA = IOMUX_PAD(0x03E4, 0x009C, 4, 0x07EC, 1, 0), + MX6_PAD_ENET2_RX_CLK__GPIO2_IO_8 = IOMUX_PAD(0x03E4, 0x009C, 5, 0x0000, 0, 0), + MX6_PAD_ENET2_RX_CLK__USB_OTG2_OC = IOMUX_PAD(0x03E4, 0x009C, 6, 0x085C, 1, 0), + MX6_PAD_ENET2_RX_CLK__LCDIF2_DATA_22 = IOMUX_PAD(0x03E4, 0x009C, 7, 0x0000, 0, 0), + MX6_PAD_ENET2_RX_CLK__VDEC_DEBUG_31 = IOMUX_PAD(0x03E4, 0x009C, 8, 0x0000, 0, 0), + MX6_PAD_ENET2_RX_CLK__PCIE_CTRL_DEBUG_25 = IOMUX_PAD(0x03E4, 0x009C, 9, 0x0000, 0, 0), + + MX6_PAD_ENET2_TX_CLK__ENET2_TX_CLK = IOMUX_PAD(0x03E8, 0x00A0, 0, 0x0000, 0, 0), + MX6_PAD_ENET2_TX_CLK__ENET2_REF_CLK2 = IOMUX_PAD(0x03E8, 0x00A0, 1, 0x076C, 1, 0), + MX6_PAD_ENET2_TX_CLK__I2C3_SDA = IOMUX_PAD(0x03E8, 0x00A0, 2, 0x07BC, 1, 0), + MX6_PAD_ENET2_TX_CLK__UART1_CTS_B = IOMUX_PAD(0x03E8, 0x00A0, 3, 0x082C, 3, 0), + MX6_PAD_ENET2_TX_CLK__MLB_CLK = IOMUX_PAD(0x03E8, 0x00A0, 4, 0x07E8, 1, 0), + MX6_PAD_ENET2_TX_CLK__GPIO2_IO_9 = IOMUX_PAD(0x03E8, 0x00A0, 5, 0x0000, 0, 0), + MX6_PAD_ENET2_TX_CLK__USB_OTG2_PWR = IOMUX_PAD(0x03E8, 0x00A0, 6, 0x0000, 0, 0), + MX6_PAD_ENET2_TX_CLK__LCDIF2_DATA_23 = IOMUX_PAD(0x03E8, 0x00A0, 7, 0x0000, 0, 0), + MX6_PAD_ENET2_TX_CLK__VDEC_DEBUG_30 = IOMUX_PAD(0x03E8, 0x00A0, 8, 0x0000, 0, 0), + MX6_PAD_ENET2_TX_CLK__PCIE_CTRL_DEBUG_24 = IOMUX_PAD(0x03E8, 0x00A0, 9, 0x0000, 0, 0), + + MX6_PAD_KEY_COL0__KPP_COL_0 = IOMUX_PAD(0x03EC, 0x00A4, 0, 0x0000, 0, 0), + MX6_PAD_KEY_COL0__USDHC3_CD_B = IOMUX_PAD(0x03EC, 0x00A4, 1, 0x0000, 0, 0), + MX6_PAD_KEY_COL0__UART6_RTS_B = IOMUX_PAD(0x03EC, 0x00A4, 2, 0x0854, 2, 0), + MX6_PAD_KEY_COL0__ECSPI1_SCLK = IOMUX_PAD(0x03EC, 0x00A4, 3, 0x0710, 0, 0), + MX6_PAD_KEY_COL0__AUDMUX_AUD5_TXC = IOMUX_PAD(0x03EC, 0x00A4, 4, 0x066C, 0, 0), + MX6_PAD_KEY_COL0__GPIO2_IO_10 = IOMUX_PAD(0x03EC, 0x00A4, 5, 0x0000, 0, 0), + MX6_PAD_KEY_COL0__SDMA_EXT_EVENT_1 = IOMUX_PAD(0x03EC, 0x00A4, 6, 0x0820, 1, 0), + MX6_PAD_KEY_COL0__SAI2_TX_BCLK = IOMUX_PAD(0x03EC, 0x00A4, 7, 0x0814, 0, 0), + MX6_PAD_KEY_COL0__VADC_DATA_0 = IOMUX_PAD(0x03EC, 0x00A4, 8, 0x0000, 0, 0), + + MX6_PAD_KEY_COL1__KPP_COL_1 = IOMUX_PAD(0x03F0, 0x00A8, 0, 0x0000, 0, 0), + MX6_PAD_KEY_COL1__USDHC3_RESET_B = IOMUX_PAD(0x03F0, 0x00A8, 1, 0x0000, 0, 0), + MX6_PAD_KEY_COL1__UART6_TX = IOMUX_PAD(0x03F0, 0x00A8, 2, 0x0858, 2, 0), + MX6_PAD_KEY_COL1__ECSPI1_MISO = IOMUX_PAD(0x03F0, 0x00A8, 3, 0x0714, 0, 0), + MX6_PAD_KEY_COL1__AUDMUX_AUD5_TXFS = IOMUX_PAD(0x03F0, 0x00A8, 4, 0x0670, 0, 0), + MX6_PAD_KEY_COL1__GPIO2_IO_11 = IOMUX_PAD(0x03F0, 0x00A8, 5, 0x0000, 0, 0), + MX6_PAD_KEY_COL1__USDHC3_RESET = IOMUX_PAD(0x03F0, 0x00A8, 6, 0x0000, 0, 0), + MX6_PAD_KEY_COL1__SAI2_TX_SYNC = IOMUX_PAD(0x03F0, 0x00A8, 7, 0x0818, 0, 0), + + MX6_PAD_KEY_COL2__KPP_COL_2 = IOMUX_PAD(0x03F4, 0x00AC, 0, 0x0000, 0, 0), + MX6_PAD_KEY_COL2__USDHC4_CD_B = IOMUX_PAD(0x03F4, 0x00AC, 1, 0x0874, 1, 0), + MX6_PAD_KEY_COL2__UART5_RTS_B = IOMUX_PAD(0x03F4, 0x00AC, 2, 0x084C, 2, 0), + MX6_PAD_KEY_COL2__CAN1_TX = IOMUX_PAD(0x03F4, 0x00AC, 3, 0x0000, 0, 0), + MX6_PAD_KEY_COL2__CANFD_TX1 = IOMUX_PAD(0x03F4, 0x00AC, 4, 0x0000, 0, 0), + MX6_PAD_KEY_COL2__GPIO2_IO_12 = IOMUX_PAD(0x03F4, 0x00AC, 5, 0x0000, 0, 0), + MX6_PAD_KEY_COL2__WEIM_DATA_30 = IOMUX_PAD(0x03F4, 0x00AC, 6, 0x0000, 0, 0), + MX6_PAD_KEY_COL2__ECSPI1_RDY = IOMUX_PAD(0x03F4, 0x00AC, 7, 0x0000, 0, 0), + + MX6_PAD_KEY_COL3__KPP_COL_3 = IOMUX_PAD(0x03F8, 0x00B0, 0, 0x0000, 0, 0), + MX6_PAD_KEY_COL3__USDHC4_LCTL = IOMUX_PAD(0x03F8, 0x00B0, 1, 0x0000, 0, 0), + MX6_PAD_KEY_COL3__UART5_TX = IOMUX_PAD(0x03F8, 0x00B0, 2, 0x0850, 2, 0), + MX6_PAD_KEY_COL3__CAN2_TX = IOMUX_PAD(0x03F8, 0x00B0, 3, 0x0000, 0, 0), + MX6_PAD_KEY_COL3__CANFD_TX2 = IOMUX_PAD(0x03F8, 0x00B0, 4, 0x0000, 0, 0), + MX6_PAD_KEY_COL3__GPIO2_IO_13 = IOMUX_PAD(0x03F8, 0x00B0, 5, 0x0000, 0, 0), + MX6_PAD_KEY_COL3__WEIM_DATA_28 = IOMUX_PAD(0x03F8, 0x00B0, 6, 0x0000, 0, 0), + MX6_PAD_KEY_COL3__ECSPI1_SS2 = IOMUX_PAD(0x03F8, 0x00B0, 7, 0x0000, 0, 0), + + MX6_PAD_KEY_COL4__KPP_COL_4 = IOMUX_PAD(0x03FC, 0x00B4, 0, 0x0000, 0, 0), + MX6_PAD_KEY_COL4__ENET2_MDC = IOMUX_PAD(0x03FC, 0x00B4, 1, 0x0000, 0, 0), + MX6_PAD_KEY_COL4__I2C3_SCL = IOMUX_PAD(0x03FC, 0x00B4, 2, 0x07B8, 2, 0), + MX6_PAD_KEY_COL4__USDHC2_LCTL = IOMUX_PAD(0x03FC, 0x00B4, 3, 0x0000, 0, 0), + MX6_PAD_KEY_COL4__AUDMUX_AUD5_RXC = IOMUX_PAD(0x03FC, 0x00B4, 4, 0x0664, 0, 0), + MX6_PAD_KEY_COL4__GPIO2_IO_14 = IOMUX_PAD(0x03FC, 0x00B4, 5, 0x0000, 0, 0), + MX6_PAD_KEY_COL4__WEIM_CRE = IOMUX_PAD(0x03FC, 0x00B4, 6, 0x0000, 0, 0), + MX6_PAD_KEY_COL4__SAI2_RX_BCLK = IOMUX_PAD(0x03FC, 0x00B4, 7, 0x0808, 0, 0), + + MX6_PAD_KEY_ROW0__KPP_ROW_0 = IOMUX_PAD(0x0400, 0x00B8, 0, 0x0000, 0, 0), + MX6_PAD_KEY_ROW0__USDHC3_WP = IOMUX_PAD(0x0400, 0x00B8, 1, 0x0000, 0, 0), + MX6_PAD_KEY_ROW0__UART6_CTS_B = IOMUX_PAD(0x0400, 0x00B8, 2, 0x0854, 3, 0), + MX6_PAD_KEY_ROW0__ECSPI1_MOSI = IOMUX_PAD(0x0400, 0x00B8, 3, 0x0718, 0, 0), + MX6_PAD_KEY_ROW0__AUDMUX_AUD5_TXD = IOMUX_PAD(0x0400, 0x00B8, 4, 0x0660, 0, 0), + MX6_PAD_KEY_ROW0__GPIO2_IO_15 = IOMUX_PAD(0x0400, 0x00B8, 5, 0x0000, 0, 0), + MX6_PAD_KEY_ROW0__SDMA_EXT_EVENT_0 = IOMUX_PAD(0x0400, 0x00B8, 6, 0x081C, 1, 0), + MX6_PAD_KEY_ROW0__SAI2_TX_DATA_0 = IOMUX_PAD(0x0400, 0x00B8, 7, 0x0000, 0, 0), + MX6_PAD_KEY_ROW0__GPU_IDLE = IOMUX_PAD(0x0400, 0x00B8, 8, 0x0000, 0, 0), + + MX6_PAD_KEY_ROW1__KPP_ROW_1 = IOMUX_PAD(0x0404, 0x00BC, 0, 0x0000, 0, 0), + MX6_PAD_KEY_ROW1__USDHC4_VSELECT = IOMUX_PAD(0x0404, 0x00BC, 1, 0x0000, 0, 0), + MX6_PAD_KEY_ROW1__UART6_RX = IOMUX_PAD(0x0404, 0x00BC, 2, 0x0858, 3, 0), + MX6_PAD_KEY_ROW1__ECSPI1_SS0 = IOMUX_PAD(0x0404, 0x00BC, 3, 0x071C, 0, 0), + MX6_PAD_KEY_ROW1__AUDMUX_AUD5_RXD = IOMUX_PAD(0x0404, 0x00BC, 4, 0x065C, 0, 0), + MX6_PAD_KEY_ROW1__GPIO2_IO_16 = IOMUX_PAD(0x0404, 0x00BC, 5, 0x0000, 0, 0), + MX6_PAD_KEY_ROW1__WEIM_DATA_31 = IOMUX_PAD(0x0404, 0x00BC, 6, 0x0000, 0, 0), + MX6_PAD_KEY_ROW1__SAI2_RX_DATA_0 = IOMUX_PAD(0x0404, 0x00BC, 7, 0x080C, 0, 0), + MX6_PAD_KEY_ROW1__M4_NMI = IOMUX_PAD(0x0404, 0x00BC, 8, 0x0000, 0, 0), + + MX6_PAD_KEY_ROW2__KPP_ROW_2 = IOMUX_PAD(0x0408, 0x00C0, 0, 0x0000, 0, 0), + MX6_PAD_KEY_ROW2__USDHC4_WP = IOMUX_PAD(0x0408, 0x00C0, 1, 0x0878, 1, 0), + MX6_PAD_KEY_ROW2__UART5_CTS_B = IOMUX_PAD(0x0408, 0x00C0, 2, 0x084C, 3, 0), + MX6_PAD_KEY_ROW2__CAN1_RX = IOMUX_PAD(0x0408, 0x00C0, 3, 0x068C, 1, 0), + MX6_PAD_KEY_ROW2__CANFD_RX1 = IOMUX_PAD(0x0408, 0x00C0, 4, 0x0694, 1, 0), + MX6_PAD_KEY_ROW2__GPIO2_IO_17 = IOMUX_PAD(0x0408, 0x00C0, 5, 0x0000, 0, 0), + MX6_PAD_KEY_ROW2__WEIM_DATA_29 = IOMUX_PAD(0x0408, 0x00C0, 6, 0x0000, 0, 0), + MX6_PAD_KEY_ROW2__ECSPI1_SS3 = IOMUX_PAD(0x0408, 0x00C0, 7, 0x0000, 0, 0), + + MX6_PAD_KEY_ROW3__KPP_ROW_3 = IOMUX_PAD(0x040C, 0x00C4, 0, 0x0000, 0, 0), + MX6_PAD_KEY_ROW3__USDHC3_LCTL = IOMUX_PAD(0x040C, 0x00C4, 1, 0x0000, 0, 0), + MX6_PAD_KEY_ROW3__UART5_RX = IOMUX_PAD(0x040C, 0x00C4, 2, 0x0850, 3, 0), + MX6_PAD_KEY_ROW3__CAN2_RX = IOMUX_PAD(0x040C, 0x00C4, 3, 0x0690, 1, 0), + MX6_PAD_KEY_ROW3__CANFD_RX2 = IOMUX_PAD(0x040C, 0x00C4, 4, 0x0698, 1, 0), + MX6_PAD_KEY_ROW3__GPIO2_IO_18 = IOMUX_PAD(0x040C, 0x00C4, 5, 0x0000, 0, 0), + MX6_PAD_KEY_ROW3__WEIM_DTACK_B = IOMUX_PAD(0x040C, 0x00C4, 6, 0x0000, 0, 0), + MX6_PAD_KEY_ROW3__ECSPI1_SS1 = IOMUX_PAD(0x040C, 0x00C4, 7, 0x0000, 0, 0), + + MX6_PAD_KEY_ROW4__KPP_ROW_4 = IOMUX_PAD(0x0410, 0x00C8, 0, 0x0000, 0, 0), + MX6_PAD_KEY_ROW4__ENET2_MDIO = IOMUX_PAD(0x0410, 0x00C8, 1, 0x0770, 3, 0), + MX6_PAD_KEY_ROW4__I2C3_SDA = IOMUX_PAD(0x0410, 0x00C8, 2, 0x07BC, 2, 0), + MX6_PAD_KEY_ROW4__USDHC1_LCTL = IOMUX_PAD(0x0410, 0x00C8, 3, 0x0000, 0, 0), + MX6_PAD_KEY_ROW4__AUDMUX_AUD5_RXFS = IOMUX_PAD(0x0410, 0x00C8, 4, 0x0668, 0, 0), + MX6_PAD_KEY_ROW4__GPIO2_IO_19 = IOMUX_PAD(0x0410, 0x00C8, 5, 0x0000, 0, 0), + MX6_PAD_KEY_ROW4__WEIM_ACLK_FREERUN = IOMUX_PAD(0x0410, 0x00C8, 6, 0x0000, 0, 0), + MX6_PAD_KEY_ROW4__SAI2_RX_SYNC = IOMUX_PAD(0x0410, 0x00C8, 7, 0x0810, 0, 0), + + MX6_PAD_LCD1_CLK__LCDIF1_CLK = IOMUX_PAD(0x0414, 0x00CC, 0, 0x0000, 0, 0), + MX6_PAD_LCD1_CLK__LCDIF1_WR_RWN = IOMUX_PAD(0x0414, 0x00CC, 1, 0x0000, 0, 0), + MX6_PAD_LCD1_CLK__AUDMUX_AUD3_RXC = IOMUX_PAD(0x0414, 0x00CC, 2, 0x0634, 1, 0), + MX6_PAD_LCD1_CLK__ENET1_1588_EVENT2_IN = IOMUX_PAD(0x0414, 0x00CC, 3, 0x0000, 0, 0), + MX6_PAD_LCD1_CLK__CSI1_DATA_16 = IOMUX_PAD(0x0414, 0x00CC, 4, 0x06DC, 0, 0), + MX6_PAD_LCD1_CLK__GPIO3_IO_0 = IOMUX_PAD(0x0414, 0x00CC, 5, 0x0000, 0, 0), + MX6_PAD_LCD1_CLK__USDHC1_WP = IOMUX_PAD(0x0414, 0x00CC, 6, 0x0868, 0, 0), + MX6_PAD_LCD1_CLK__SIM_M_HADDR_16 = IOMUX_PAD(0x0414, 0x00CC, 7, 0x0000, 0, 0), + MX6_PAD_LCD1_CLK__VADC_TEST_0 = IOMUX_PAD(0x0414, 0x00CC, 8, 0x0000, 0, 0), + MX6_PAD_LCD1_CLK__MMDC_DEBUG_0 = IOMUX_PAD(0x0414, 0x00CC, 9, 0x0000, 0, 0), + + MX6_PAD_LCD1_DATA00__LCDIF1_DATA_0 = IOMUX_PAD(0x0418, 0x00D0, 0, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA00__WEIM_CS1_B = IOMUX_PAD(0x0418, 0x00D0, 1, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA00__M4_TRACE_0 = IOMUX_PAD(0x0418, 0x00D0, 2, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA00__KITTEN_TRACE_0 = IOMUX_PAD(0x0418, 0x00D0, 3, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA00__CSI1_DATA_20 = IOMUX_PAD(0x0418, 0x00D0, 4, 0x06EC, 0, 0), + MX6_PAD_LCD1_DATA00__GPIO3_IO_1 = IOMUX_PAD(0x0418, 0x00D0, 5, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA00__SRC_BT_CFG_0 = IOMUX_PAD(0x0418, 0x00D0, 6, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA00__SIM_M_HADDR_21 = IOMUX_PAD(0x0418, 0x00D0, 7, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA00__VADC_TEST_5 = IOMUX_PAD(0x0418, 0x00D0, 8, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA00__MMDC_DEBUG_5 = IOMUX_PAD(0x0418, 0x00D0, 9, 0x0000, 0, 0), + + MX6_PAD_LCD1_DATA01__LCDIF1_DATA_1 = IOMUX_PAD(0x041C, 0x00D4, 0, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA01__WEIM_CS2_B = IOMUX_PAD(0x041C, 0x00D4, 1, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA01__M4_TRACE_1 = IOMUX_PAD(0x041C, 0x00D4, 2, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA01__KITTEN_TRACE_1 = IOMUX_PAD(0x041C, 0x00D4, 3, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA01__CSI1_DATA_21 = IOMUX_PAD(0x041C, 0x00D4, 4, 0x06F0, 0, 0), + MX6_PAD_LCD1_DATA01__GPIO3_IO_2 = IOMUX_PAD(0x041C, 0x00D4, 5, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA01__SRC_BT_CFG_1 = IOMUX_PAD(0x041C, 0x00D4, 6, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA01__SIM_M_HADDR_22 = IOMUX_PAD(0x041C, 0x00D4, 7, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA01__VADC_TEST_6 = IOMUX_PAD(0x041C, 0x00D4, 8, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA01__MMDC_DEBUG_6 = IOMUX_PAD(0x041C, 0x00D4, 9, 0x0000, 0, 0), + + MX6_PAD_LCD1_DATA02__LCDIF1_DATA_2 = IOMUX_PAD(0x0420, 0x00D8, 0, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA02__WEIM_CS3_B = IOMUX_PAD(0x0420, 0x00D8, 1, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA02__M4_TRACE_2 = IOMUX_PAD(0x0420, 0x00D8, 2, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA02__KITTEN_TRACE_2 = IOMUX_PAD(0x0420, 0x00D8, 3, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA02__CSI1_DATA_22 = IOMUX_PAD(0x0420, 0x00D8, 4, 0x06F4, 0, 0), + MX6_PAD_LCD1_DATA02__GPIO3_IO_3 = IOMUX_PAD(0x0420, 0x00D8, 5, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA02__SRC_BT_CFG_2 = IOMUX_PAD(0x0420, 0x00D8, 6, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA02__SIM_M_HADDR_23 = IOMUX_PAD(0x0420, 0x00D8, 7, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA02__VADC_TEST_7 = IOMUX_PAD(0x0420, 0x00D8, 8, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA02__MMDC_DEBUG_7 = IOMUX_PAD(0x0420, 0x00D8, 9, 0x0000, 0, 0), + + MX6_PAD_LCD1_DATA03__LCDIF1_DATA_3 = IOMUX_PAD(0x0424, 0x00DC, 0, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA03__WEIM_ADDR_24 = IOMUX_PAD(0x0424, 0x00DC, 1, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA03__M4_TRACE_3 = IOMUX_PAD(0x0424, 0x00DC, 2, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA03__KITTEN_TRACE_3 = IOMUX_PAD(0x0424, 0x00DC, 3, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA03__CSI1_DATA_23 = IOMUX_PAD(0x0424, 0x00DC, 4, 0x06F8, 0, 0), + MX6_PAD_LCD1_DATA03__GPIO3_IO_4 = IOMUX_PAD(0x0424, 0x00DC, 5, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA03__SRC_BT_CFG_3 = IOMUX_PAD(0x0424, 0x00DC, 6, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA03__SIM_M_HADDR_24 = IOMUX_PAD(0x0424, 0x00DC, 7, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA03__VADC_TEST_8 = IOMUX_PAD(0x0424, 0x00DC, 8, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA03__MMDC_DEBUG_8 = IOMUX_PAD(0x0424, 0x00DC, 9, 0x0000, 0, 0), + + MX6_PAD_LCD1_DATA04__LCDIF1_DATA_4 = IOMUX_PAD(0x0428, 0x00E0, 0, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA04__WEIM_ADDR_25 = IOMUX_PAD(0x0428, 0x00E0, 1, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA04__KITTEN_TRACE_4 = IOMUX_PAD(0x0428, 0x00E0, 3, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA04__CSI1_VSYNC = IOMUX_PAD(0x0428, 0x00E0, 4, 0x0708, 1, 0), + MX6_PAD_LCD1_DATA04__GPIO3_IO_5 = IOMUX_PAD(0x0428, 0x00E0, 5, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA04__SRC_BT_CFG_4 = IOMUX_PAD(0x0428, 0x00E0, 6, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA04__SIM_M_HADDR_25 = IOMUX_PAD(0x0428, 0x00E0, 7, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA04__VADC_TEST_9 = IOMUX_PAD(0x0428, 0x00E0, 8, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA04__MMDC_DEBUG_9 = IOMUX_PAD(0x0428, 0x00E0, 9, 0x0000, 0, 0), + + MX6_PAD_LCD1_DATA05__LCDIF1_DATA_5 = IOMUX_PAD(0x042C, 0x00E4, 0, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA05__WEIM_ADDR_26 = IOMUX_PAD(0x042C, 0x00E4, 1, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA05__KITTEN_TRACE_5 = IOMUX_PAD(0x042C, 0x00E4, 3, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA05__CSI1_HSYNC = IOMUX_PAD(0x042C, 0x00E4, 4, 0x0700, 1, 0), + MX6_PAD_LCD1_DATA05__GPIO3_IO_6 = IOMUX_PAD(0x042C, 0x00E4, 5, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA05__SRC_BT_CFG_5 = IOMUX_PAD(0x042C, 0x00E4, 6, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA05__SIM_M_HADDR_26 = IOMUX_PAD(0x042C, 0x00E4, 7, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA05__VADC_TEST_10 = IOMUX_PAD(0x042C, 0x00E4, 8, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA05__MMDC_DEBUG_10 = IOMUX_PAD(0x042C, 0x00E4, 9, 0x0000, 0, 0), + + MX6_PAD_LCD1_DATA06__LCDIF1_DATA_6 = IOMUX_PAD(0x0430, 0x00E8, 0, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA06__WEIM_EB_B_2 = IOMUX_PAD(0x0430, 0x00E8, 1, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA06__KITTEN_TRACE_6 = IOMUX_PAD(0x0430, 0x00E8, 3, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA06__CSI1_PIXCLK = IOMUX_PAD(0x0430, 0x00E8, 4, 0x0704, 1, 0), + MX6_PAD_LCD1_DATA06__GPIO3_IO_7 = IOMUX_PAD(0x0430, 0x00E8, 5, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA06__SRC_BT_CFG_6 = IOMUX_PAD(0x0430, 0x00E8, 6, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA06__SIM_M_HADDR_27 = IOMUX_PAD(0x0430, 0x00E8, 7, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA06__VADC_TEST_11 = IOMUX_PAD(0x0430, 0x00E8, 8, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA06__MMDC_DEBUG_11 = IOMUX_PAD(0x0430, 0x00E8, 9, 0x0000, 0, 0), + + MX6_PAD_LCD1_DATA07__LCDIF1_DATA_7 = IOMUX_PAD(0x0434, 0x00EC, 0, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA07__WEIM_EB_B_3 = IOMUX_PAD(0x0434, 0x00EC, 1, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA07__KITTEN_TRACE_7 = IOMUX_PAD(0x0434, 0x00EC, 3, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA07__CSI1_MCLK = IOMUX_PAD(0x0434, 0x00EC, 4, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA07__GPIO3_IO_8 = IOMUX_PAD(0x0434, 0x00EC, 5, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA07__SRC_BT_CFG_7 = IOMUX_PAD(0x0434, 0x00EC, 6, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA07__SIM_M_HADDR_28 = IOMUX_PAD(0x0434, 0x00EC, 7, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA07__VADC_TEST_12 = IOMUX_PAD(0x0434, 0x00EC, 8, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA07__MMDC_DEBUG_12 = IOMUX_PAD(0x0434, 0x00EC, 9, 0x0000, 0, 0), + + MX6_PAD_LCD1_DATA08__LCDIF1_DATA_8 = IOMUX_PAD(0x0438, 0x00F0, 0, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA08__WEIM_AD_8 = IOMUX_PAD(0x0438, 0x00F0, 1, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA08__KITTEN_TRACE_8 = IOMUX_PAD(0x0438, 0x00F0, 3, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA08__CSI1_DATA_9 = IOMUX_PAD(0x0438, 0x00F0, 4, 0x06C4, 1, 0), + MX6_PAD_LCD1_DATA08__GPIO3_IO_9 = IOMUX_PAD(0x0438, 0x00F0, 5, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA08__SRC_BT_CFG_8 = IOMUX_PAD(0x0438, 0x00F0, 6, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA08__SIM_M_HADDR_29 = IOMUX_PAD(0x0438, 0x00F0, 7, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA08__VADC_TEST_13 = IOMUX_PAD(0x0438, 0x00F0, 8, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA08__MMDC_DEBUG_13 = IOMUX_PAD(0x0438, 0x00F0, 9, 0x0000, 0, 0), + + MX6_PAD_LCD1_DATA09__LCDIF1_DATA_9 = IOMUX_PAD(0x043C, 0x00F4, 0, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA09__WEIM_AD_9 = IOMUX_PAD(0x043C, 0x00F4, 1, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA09__KITTEN_TRACE_9 = IOMUX_PAD(0x043C, 0x00F4, 3, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA09__CSI1_DATA_8 = IOMUX_PAD(0x043C, 0x00F4, 4, 0x06C0, 1, 0), + MX6_PAD_LCD1_DATA09__GPIO3_IO_10 = IOMUX_PAD(0x043C, 0x00F4, 5, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA09__SRC_BT_CFG_9 = IOMUX_PAD(0x043C, 0x00F4, 6, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA09__SIM_M_HADDR_30 = IOMUX_PAD(0x043C, 0x00F4, 7, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA09__VADC_TEST_14 = IOMUX_PAD(0x043C, 0x00F4, 8, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA09__MMDC_DEBUG_14 = IOMUX_PAD(0x043C, 0x00F4, 9, 0x0000, 0, 0), + + MX6_PAD_LCD1_DATA10__LCDIF1_DATA_10 = IOMUX_PAD(0x0440, 0x00F8, 0, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA10__WEIM_AD_10 = IOMUX_PAD(0x0440, 0x00F8, 1, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA10__KITTEN_TRACE_10 = IOMUX_PAD(0x0440, 0x00F8, 3, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA10__CSI1_DATA_7 = IOMUX_PAD(0x0440, 0x00F8, 4, 0x06BC, 1, 0), + MX6_PAD_LCD1_DATA10__GPIO3_IO_11 = IOMUX_PAD(0x0440, 0x00F8, 5, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA10__SRC_BT_CFG_10 = IOMUX_PAD(0x0440, 0x00F8, 6, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA10__SIM_M_HADDR_31 = IOMUX_PAD(0x0440, 0x00F8, 7, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA10__VADC_TEST_15 = IOMUX_PAD(0x0440, 0x00F8, 8, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA10__MMDC_DEBUG_15 = IOMUX_PAD(0x0440, 0x00F8, 9, 0x0000, 0, 0), + + MX6_PAD_LCD1_DATA11__LCDIF1_DATA_11 = IOMUX_PAD(0x0444, 0x00FC, 0, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA11__WEIM_AD_11 = IOMUX_PAD(0x0444, 0x00FC, 1, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA11__KITTEN_TRACE_11 = IOMUX_PAD(0x0444, 0x00FC, 3, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA11__CSI1_DATA_6 = IOMUX_PAD(0x0444, 0x00FC, 4, 0x06B8, 1, 0), + MX6_PAD_LCD1_DATA11__GPIO3_IO_12 = IOMUX_PAD(0x0444, 0x00FC, 5, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA11__SRC_BT_CFG_11 = IOMUX_PAD(0x0444, 0x00FC, 6, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA11__SIM_M_HBURST_0 = IOMUX_PAD(0x0444, 0x00FC, 7, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA11__VADC_TEST_16 = IOMUX_PAD(0x0444, 0x00FC, 8, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA11__MMDC_DEBUG_16 = IOMUX_PAD(0x0444, 0x00FC, 9, 0x0000, 0, 0), + + MX6_PAD_LCD1_DATA12__LCDIF1_DATA_12 = IOMUX_PAD(0x0448, 0x0100, 0, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA12__WEIM_AD_12 = IOMUX_PAD(0x0448, 0x0100, 1, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA12__KITTEN_TRACE_12 = IOMUX_PAD(0x0448, 0x0100, 3, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA12__CSI1_DATA_5 = IOMUX_PAD(0x0448, 0x0100, 4, 0x06B4, 1, 0), + MX6_PAD_LCD1_DATA12__GPIO3_IO_13 = IOMUX_PAD(0x0448, 0x0100, 5, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA12__SRC_BT_CFG_12 = IOMUX_PAD(0x0448, 0x0100, 6, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA12__SIM_M_HBURST_1 = IOMUX_PAD(0x0448, 0x0100, 7, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA12__VADC_TEST_17 = IOMUX_PAD(0x0448, 0x0100, 8, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA12__MMDC_DEBUG_17 = IOMUX_PAD(0x0448, 0x0100, 9, 0x0000, 0, 0), + + MX6_PAD_LCD1_DATA13__LCDIF1_DATA_13 = IOMUX_PAD(0x044C, 0x0104, 0, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA13__WEIM_AD_13 = IOMUX_PAD(0x044C, 0x0104, 1, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA13__KITTEN_TRACE_13 = IOMUX_PAD(0x044C, 0x0104, 3, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA13__CSI1_DATA_4 = IOMUX_PAD(0x044C, 0x0104, 4, 0x06B0, 1, 0), + MX6_PAD_LCD1_DATA13__GPIO3_IO_14 = IOMUX_PAD(0x044C, 0x0104, 5, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA13__SRC_BT_CFG_13 = IOMUX_PAD(0x044C, 0x0104, 6, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA13__SIM_M_HBURST_2 = IOMUX_PAD(0x044C, 0x0104, 7, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA13__VADC_TEST_18 = IOMUX_PAD(0x044C, 0x0104, 8, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA13__MMDC_DEBUG_18 = IOMUX_PAD(0x044C, 0x0104, 9, 0x0000, 0, 0), + + MX6_PAD_LCD1_DATA14__LCDIF1_DATA_14 = IOMUX_PAD(0x0450, 0x0108, 0, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA14__WEIM_AD_14 = IOMUX_PAD(0x0450, 0x0108, 1, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA14__KITTEN_TRACE_14 = IOMUX_PAD(0x0450, 0x0108, 3, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA14__CSI1_DATA_3 = IOMUX_PAD(0x0450, 0x0108, 4, 0x06AC, 1, 0), + MX6_PAD_LCD1_DATA14__GPIO3_IO_15 = IOMUX_PAD(0x0450, 0x0108, 5, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA14__SRC_BT_CFG_14 = IOMUX_PAD(0x0450, 0x0108, 6, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA14__SIM_M_HMASTLOCK = IOMUX_PAD(0x0450, 0x0108, 7, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA14__VADC_TEST_19 = IOMUX_PAD(0x0450, 0x0108, 8, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA14__MMDC_DEBUG_19 = IOMUX_PAD(0x0450, 0x0108, 9, 0x0000, 0, 0), + + MX6_PAD_LCD1_DATA15__LCDIF1_DATA_15 = IOMUX_PAD(0x0454, 0x010C, 0, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA15__WEIM_AD_15 = IOMUX_PAD(0x0454, 0x010C, 1, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA15__KITTEN_TRACE_15 = IOMUX_PAD(0x0454, 0x010C, 3, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA15__CSI1_DATA_2 = IOMUX_PAD(0x0454, 0x010C, 4, 0x06A8, 1, 0), + MX6_PAD_LCD1_DATA15__GPIO3_IO_16 = IOMUX_PAD(0x0454, 0x010C, 5, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA15__SRC_BT_CFG_15 = IOMUX_PAD(0x0454, 0x010C, 6, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA15__SIM_M_HPROT_0 = IOMUX_PAD(0x0454, 0x010C, 7, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA15__VDEC_DEBUG_0 = IOMUX_PAD(0x0454, 0x010C, 8, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA15__MMDC_DEBUG_20 = IOMUX_PAD(0x0454, 0x010C, 9, 0x0000, 0, 0), + + MX6_PAD_LCD1_DATA16__LCDIF1_DATA_16 = IOMUX_PAD(0x0458, 0x0110, 0, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA16__WEIM_ADDR_16 = IOMUX_PAD(0x0458, 0x0110, 1, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA16__M4_TRACE_CLK = IOMUX_PAD(0x0458, 0x0110, 2, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA16__KITTEN_TRACE_CLK = IOMUX_PAD(0x0458, 0x0110, 3, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA16__CSI1_DATA_1 = IOMUX_PAD(0x0458, 0x0110, 4, 0x06A4, 0, 0), + MX6_PAD_LCD1_DATA16__GPIO3_IO_17 = IOMUX_PAD(0x0458, 0x0110, 5, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA16__SRC_BT_CFG_24 = IOMUX_PAD(0x0458, 0x0110, 6, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA16__SIM_M_HPROT_1 = IOMUX_PAD(0x0458, 0x0110, 7, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA16__VDEC_DEBUG_1 = IOMUX_PAD(0x0458, 0x0110, 8, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA16__MMDC_DEBUG_21 = IOMUX_PAD(0x0458, 0x0110, 9, 0x0000, 0, 0), + + MX6_PAD_LCD1_DATA17__LCDIF1_DATA_17 = IOMUX_PAD(0x045C, 0x0114, 0, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA17__WEIM_ADDR_17 = IOMUX_PAD(0x045C, 0x0114, 1, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA17__KITTEN_TRACE_CTL = IOMUX_PAD(0x045C, 0x0114, 3, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA17__CSI1_DATA_0 = IOMUX_PAD(0x045C, 0x0114, 4, 0x06A0, 0, 0), + MX6_PAD_LCD1_DATA17__GPIO3_IO_18 = IOMUX_PAD(0x045C, 0x0114, 5, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA17__SRC_BT_CFG_25 = IOMUX_PAD(0x045C, 0x0114, 6, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA17__SIM_M_HPROT_2 = IOMUX_PAD(0x045C, 0x0114, 7, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA17__VDEC_DEBUG_2 = IOMUX_PAD(0x045C, 0x0114, 8, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA17__MMDC_DEBUG_22 = IOMUX_PAD(0x045C, 0x0114, 9, 0x0000, 0, 0), + + MX6_PAD_LCD1_DATA18__LCDIF1_DATA_18 = IOMUX_PAD(0x0460, 0x0118, 0, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA18__WEIM_ADDR_18 = IOMUX_PAD(0x0460, 0x0118, 1, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA18__M4_EVENTO = IOMUX_PAD(0x0460, 0x0118, 2, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA18__KITTEN_EVENTO = IOMUX_PAD(0x0460, 0x0118, 3, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA18__CSI1_DATA_15 = IOMUX_PAD(0x0460, 0x0118, 4, 0x06D8, 0, 0), + MX6_PAD_LCD1_DATA18__GPIO3_IO_19 = IOMUX_PAD(0x0460, 0x0118, 5, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA18__SRC_BT_CFG_26 = IOMUX_PAD(0x0460, 0x0118, 6, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA18__SIM_M_HPROT_3 = IOMUX_PAD(0x0460, 0x0118, 7, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA18__VDEC_DEBUG_3 = IOMUX_PAD(0x0460, 0x0118, 8, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA18__MMDC_DEBUG_23 = IOMUX_PAD(0x0460, 0x0118, 9, 0x0000, 0, 0), + + MX6_PAD_LCD1_DATA19__LCDIF1_DATA_19 = IOMUX_PAD(0x0464, 0x011C, 0, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA19__WEIM_ADDR_19 = IOMUX_PAD(0x0464, 0x011C, 1, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA19__M4_TRACE_SWO = IOMUX_PAD(0x0464, 0x011C, 2, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA19__CSI1_DATA_14 = IOMUX_PAD(0x0464, 0x011C, 4, 0x06D4, 0, 0), + MX6_PAD_LCD1_DATA19__GPIO3_IO_20 = IOMUX_PAD(0x0464, 0x011C, 5, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA19__SRC_BT_CFG_27 = IOMUX_PAD(0x0464, 0x011C, 6, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA19__SIM_M_HREADYOUT = IOMUX_PAD(0x0464, 0x011C, 7, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA19__VDEC_DEBUG_4 = IOMUX_PAD(0x0464, 0x011C, 8, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA19__MMDC_DEBUG_24 = IOMUX_PAD(0x0464, 0x011C, 9, 0x0000, 0, 0), + + MX6_PAD_LCD1_DATA20__LCDIF1_DATA_20 = IOMUX_PAD(0x0468, 0x0120, 0, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA20__WEIM_ADDR_20 = IOMUX_PAD(0x0468, 0x0120, 1, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA20__PWM8_OUT = IOMUX_PAD(0x0468, 0x0120, 2, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA20__ENET1_1588_EVENT2_OUT = IOMUX_PAD(0x0468, 0x0120, 3, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA20__CSI1_DATA_13 = IOMUX_PAD(0x0468, 0x0120, 4, 0x06D0, 0, 0), + MX6_PAD_LCD1_DATA20__GPIO3_IO_21 = IOMUX_PAD(0x0468, 0x0120, 5, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA20__SRC_BT_CFG_28 = IOMUX_PAD(0x0468, 0x0120, 6, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA20__SIM_M_HRESP = IOMUX_PAD(0x0468, 0x0120, 7, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA20__VDEC_DEBUG_5 = IOMUX_PAD(0x0468, 0x0120, 8, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA20__MMDC_DEBUG_25 = IOMUX_PAD(0x0468, 0x0120, 9, 0x0000, 0, 0), + + MX6_PAD_LCD1_DATA21__LCDIF1_DATA_21 = IOMUX_PAD(0x046C, 0x0124, 0, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA21__WEIM_ADDR_21 = IOMUX_PAD(0x046C, 0x0124, 1, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA21__PWM7_OUT = IOMUX_PAD(0x046C, 0x0124, 2, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA21__ENET1_1588_EVENT3_OUT = IOMUX_PAD(0x046C, 0x0124, 3, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA21__CSI1_DATA_12 = IOMUX_PAD(0x046C, 0x0124, 4, 0x06CC, 0, 0), + MX6_PAD_LCD1_DATA21__GPIO3_IO_22 = IOMUX_PAD(0x046C, 0x0124, 5, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA21__SRC_BT_CFG_29 = IOMUX_PAD(0x046C, 0x0124, 6, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA21__SIM_M_HSIZE_0 = IOMUX_PAD(0x046C, 0x0124, 7, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA21__VDEC_DEBUG_6 = IOMUX_PAD(0x046C, 0x0124, 8, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA21__MMDC_DEBUG_26 = IOMUX_PAD(0x046C, 0x0124, 9, 0x0000, 0, 0), + + MX6_PAD_LCD1_DATA22__LCDIF1_DATA_22 = IOMUX_PAD(0x0470, 0x0128, 0, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA22__WEIM_ADDR_22 = IOMUX_PAD(0x0470, 0x0128, 1, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA22__PWM6_OUT = IOMUX_PAD(0x0470, 0x0128, 2, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA22__ENET2_1588_EVENT2_OUT = IOMUX_PAD(0x0470, 0x0128, 3, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA22__CSI1_DATA_11 = IOMUX_PAD(0x0470, 0x0128, 4, 0x06C8, 0, 0), + MX6_PAD_LCD1_DATA22__GPIO3_IO_23 = IOMUX_PAD(0x0470, 0x0128, 5, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA22__SRC_BT_CFG_30 = IOMUX_PAD(0x0470, 0x0128, 6, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA22__SIM_M_HSIZE_1 = IOMUX_PAD(0x0470, 0x0128, 7, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA22__VDEC_DEBUG_7 = IOMUX_PAD(0x0470, 0x0128, 8, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA22__MMDC_DEBUG_27 = IOMUX_PAD(0x0470, 0x0128, 9, 0x0000, 0, 0), + + MX6_PAD_LCD1_DATA23__LCDIF1_DATA_23 = IOMUX_PAD(0x0474, 0x012C, 0, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA23__WEIM_ADDR_23 = IOMUX_PAD(0x0474, 0x012C, 1, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA23__PWM5_OUT = IOMUX_PAD(0x0474, 0x012C, 2, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA23__ENET2_1588_EVENT3_OUT = IOMUX_PAD(0x0474, 0x012C, 3, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA23__CSI1_DATA_10 = IOMUX_PAD(0x0474, 0x012C, 4, 0x06FC, 0, 0), + MX6_PAD_LCD1_DATA23__GPIO3_IO_24 = IOMUX_PAD(0x0474, 0x012C, 5, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA23__SRC_BT_CFG_31 = IOMUX_PAD(0x0474, 0x012C, 6, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA23__SIM_M_HSIZE_2 = IOMUX_PAD(0x0474, 0x012C, 7, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA23__VDEC_DEBUG_8 = IOMUX_PAD(0x0474, 0x012C, 8, 0x0000, 0, 0), + MX6_PAD_LCD1_DATA23__MMDC_DEBUG_28 = IOMUX_PAD(0x0474, 0x012C, 9, 0x0000, 0, 0), + + MX6_PAD_LCD1_ENABLE__LCDIF1_ENABLE = IOMUX_PAD(0x0478, 0x0130, 0, 0x0000, 0, 0), + MX6_PAD_LCD1_ENABLE__LCDIF1_RD_E = IOMUX_PAD(0x0478, 0x0130, 1, 0x0000, 0, 0), + MX6_PAD_LCD1_ENABLE__AUDMUX_AUD3_TXC = IOMUX_PAD(0x0478, 0x0130, 2, 0x063C, 1, 0), + MX6_PAD_LCD1_ENABLE__ENET1_1588_EVENT3_IN = IOMUX_PAD(0x0478, 0x0130, 3, 0x0000, 0, 0), + MX6_PAD_LCD1_ENABLE__CSI1_DATA_17 = IOMUX_PAD(0x0478, 0x0130, 4, 0x06E0, 0, 0), + MX6_PAD_LCD1_ENABLE__GPIO3_IO_25 = IOMUX_PAD(0x0478, 0x0130, 5, 0x0000, 0, 0), + MX6_PAD_LCD1_ENABLE__USDHC1_CD_B = IOMUX_PAD(0x0478, 0x0130, 6, 0x0864, 0, 0), + MX6_PAD_LCD1_ENABLE__SIM_M_HADDR_17 = IOMUX_PAD(0x0478, 0x0130, 7, 0x0000, 0, 0), + MX6_PAD_LCD1_ENABLE__VADC_TEST_1 = IOMUX_PAD(0x0478, 0x0130, 8, 0x0000, 0, 0), + MX6_PAD_LCD1_ENABLE__MMDC_DEBUG_1 = IOMUX_PAD(0x0478, 0x0130, 9, 0x0000, 0, 0), + + MX6_PAD_LCD1_HSYNC__LCDIF1_HSYNC = IOMUX_PAD(0x047C, 0x0134, 0, 0x07E0, 0, 0), + MX6_PAD_LCD1_HSYNC__LCDIF1_RS = IOMUX_PAD(0x047C, 0x0134, 1, 0x0000, 0, 0), + MX6_PAD_LCD1_HSYNC__AUDMUX_AUD3_TXD = IOMUX_PAD(0x047C, 0x0134, 2, 0x0630, 1, 0), + MX6_PAD_LCD1_HSYNC__ENET2_1588_EVENT2_IN = IOMUX_PAD(0x047C, 0x0134, 3, 0x0000, 0, 0), + MX6_PAD_LCD1_HSYNC__CSI1_DATA_18 = IOMUX_PAD(0x047C, 0x0134, 4, 0x06E4, 0, 0), + MX6_PAD_LCD1_HSYNC__GPIO3_IO_26 = IOMUX_PAD(0x047C, 0x0134, 5, 0x0000, 0, 0), + MX6_PAD_LCD1_HSYNC__USDHC2_WP = IOMUX_PAD(0x047C, 0x0134, 6, 0x0870, 0, 0), + MX6_PAD_LCD1_HSYNC__SIM_M_HADDR_18 = IOMUX_PAD(0x047C, 0x0134, 7, 0x0000, 0, 0), + MX6_PAD_LCD1_HSYNC__VADC_TEST_2 = IOMUX_PAD(0x047C, 0x0134, 8, 0x0000, 0, 0), + MX6_PAD_LCD1_HSYNC__MMDC_DEBUG_2 = IOMUX_PAD(0x047C, 0x0134, 9, 0x0000, 0, 0), + + MX6_PAD_LCD1_RESET__LCDIF1_RESET = IOMUX_PAD(0x0480, 0x0138, 0, 0x0000, 0, 0), + MX6_PAD_LCD1_RESET__LCDIF1_CS = IOMUX_PAD(0x0480, 0x0138, 1, 0x0000, 0, 0), + MX6_PAD_LCD1_RESET__AUDMUX_AUD3_RXD = IOMUX_PAD(0x0480, 0x0138, 2, 0x062C, 1, 0), + MX6_PAD_LCD1_RESET__KITTEN_EVENTI = IOMUX_PAD(0x0480, 0x0138, 3, 0x0000, 0, 0), + MX6_PAD_LCD1_RESET__M4_EVENTI = IOMUX_PAD(0x0480, 0x0138, 4, 0x0000, 0, 0), + MX6_PAD_LCD1_RESET__GPIO3_IO_27 = IOMUX_PAD(0x0480, 0x0138, 5, 0x0000, 0, 0), + MX6_PAD_LCD1_RESET__CCM_PMIC_RDY = IOMUX_PAD(0x0480, 0x0138, 6, 0x069C, 0, 0), + MX6_PAD_LCD1_RESET__SIM_M_HADDR_20 = IOMUX_PAD(0x0480, 0x0138, 7, 0x0000, 0, 0), + MX6_PAD_LCD1_RESET__VADC_TEST_4 = IOMUX_PAD(0x0480, 0x0138, 8, 0x0000, 0, 0), + MX6_PAD_LCD1_RESET__MMDC_DEBUG_4 = IOMUX_PAD(0x0480, 0x0138, 9, 0x0000, 0, 0), + + MX6_PAD_LCD1_VSYNC__LCDIF1_VSYNC = IOMUX_PAD(0x0484, 0x013C, 0, 0x0000, 0, 0), + MX6_PAD_LCD1_VSYNC__LCDIF1_BUSY = IOMUX_PAD(0x0484, 0x013C, 1, 0x07E0, 1, 0), + MX6_PAD_LCD1_VSYNC__AUDMUX_AUD3_TXFS = IOMUX_PAD(0x0484, 0x013C, 2, 0x0640, 1, 0), + MX6_PAD_LCD1_VSYNC__ENET2_1588_EVENT3_IN = IOMUX_PAD(0x0484, 0x013C, 3, 0x0000, 0, 0), + MX6_PAD_LCD1_VSYNC__CSI1_DATA_19 = IOMUX_PAD(0x0484, 0x013C, 4, 0x06E8, 0, 0), + MX6_PAD_LCD1_VSYNC__GPIO3_IO_28 = IOMUX_PAD(0x0484, 0x013C, 5, 0x0000, 0, 0), + MX6_PAD_LCD1_VSYNC__USDHC2_CD_B = IOMUX_PAD(0x0484, 0x013C, 6, 0x086C, 0, 0), + MX6_PAD_LCD1_VSYNC__SIM_M_HADDR_19 = IOMUX_PAD(0x0484, 0x013C, 7, 0x0000, 0, 0), + MX6_PAD_LCD1_VSYNC__VADC_TEST_3 = IOMUX_PAD(0x0484, 0x013C, 8, 0x0000, 0, 0), + MX6_PAD_LCD1_VSYNC__MMDC_DEBUG_3 = IOMUX_PAD(0x0484, 0x013C, 9, 0x0000, 0, 0), + + MX6_PAD_NAND_ALE__RAWNAND_ALE = IOMUX_PAD(0x0488, 0x0140, 0, 0x0000, 0, 0), + MX6_PAD_NAND_ALE__I2C3_SDA = IOMUX_PAD(0x0488, 0x0140, 1, 0x07BC, 0, 0), + MX6_PAD_NAND_ALE__QSPI2_A_SS0_B = IOMUX_PAD(0x0488, 0x0140, 2, 0x0000, 0, 0), + MX6_PAD_NAND_ALE__ECSPI2_SS0 = IOMUX_PAD(0x0488, 0x0140, 3, 0x072C, 0, 0), + MX6_PAD_NAND_ALE__ESAI_TX3_RX2 = IOMUX_PAD(0x0488, 0x0140, 4, 0x079C, 0, 0), + MX6_PAD_NAND_ALE__GPIO4_IO_0 = IOMUX_PAD(0x0488, 0x0140, 5, 0x0000, 0, 0), + MX6_PAD_NAND_ALE__WEIM_CS0_B = IOMUX_PAD(0x0488, 0x0140, 6, 0x0000, 0, 0), + MX6_PAD_NAND_ALE__TPSMP_HDATA_0 = IOMUX_PAD(0x0488, 0x0140, 7, 0x0000, 0, 0), + MX6_PAD_NAND_ALE__ANATOP_USBPHY1_TSTI_TX_EN = IOMUX_PAD(0x0488, 0x0140, 8, 0x0000, 0, 0), + MX6_PAD_NAND_ALE__SDMA_DEBUG_PC_12 = IOMUX_PAD(0x0488, 0x0140, 9, 0x0000, 0, 0), + + MX6_PAD_NAND_CE0_B__RAWNAND_CE0_B = IOMUX_PAD(0x048C, 0x0144, 0, 0x0000, 0, 0), + MX6_PAD_NAND_CE0_B__USDHC2_VSELECT = IOMUX_PAD(0x048C, 0x0144, 1, 0x0000, 0, 0), + MX6_PAD_NAND_CE0_B__QSPI2_A_DATA_2 = IOMUX_PAD(0x048C, 0x0144, 2, 0x0000, 0, 0), + MX6_PAD_NAND_CE0_B__AUDMUX_AUD4_TXC = IOMUX_PAD(0x048C, 0x0144, 3, 0x0654, 0, 0), + MX6_PAD_NAND_CE0_B__ESAI_TX_CLK = IOMUX_PAD(0x048C, 0x0144, 4, 0x078C, 0, 0), + MX6_PAD_NAND_CE0_B__GPIO4_IO_1 = IOMUX_PAD(0x048C, 0x0144, 5, 0x0000, 0, 0), + MX6_PAD_NAND_CE0_B__WEIM_LBA_B = IOMUX_PAD(0x048C, 0x0144, 6, 0x0000, 0, 0), + MX6_PAD_NAND_CE0_B__TPSMP_HDATA_3 = IOMUX_PAD(0x048C, 0x0144, 7, 0x0000, 0, 0), + MX6_PAD_NAND_CE0_B__ANATOP_USBPHY1_TSTI_TX_HIZ = IOMUX_PAD(0x048C, 0x0144, 8, 0x0000, 0, 0), + MX6_PAD_NAND_CE0_B__SDMA_DEBUG_PC_9 = IOMUX_PAD(0x048C, 0x0144, 9, 0x0000, 0, 0), + + MX6_PAD_NAND_CE1_B__RAWNAND_CE1_B = IOMUX_PAD(0x0490, 0x0148, 0, 0x0000, 0, 0), + MX6_PAD_NAND_CE1_B__USDHC3_RESET_B = IOMUX_PAD(0x0490, 0x0148, 1, 0x0000, 0, 0), + MX6_PAD_NAND_CE1_B__QSPI2_A_DATA_3 = IOMUX_PAD(0x0490, 0x0148, 2, 0x0000, 0, 0), + MX6_PAD_NAND_CE1_B__AUDMUX_AUD4_TXD = IOMUX_PAD(0x0490, 0x0148, 3, 0x0648, 0, 0), + MX6_PAD_NAND_CE1_B__ESAI_TX0 = IOMUX_PAD(0x0490, 0x0148, 4, 0x0790, 0, 0), + MX6_PAD_NAND_CE1_B__GPIO4_IO_2 = IOMUX_PAD(0x0490, 0x0148, 5, 0x0000, 0, 0), + MX6_PAD_NAND_CE1_B__WEIM_OE = IOMUX_PAD(0x0490, 0x0148, 6, 0x0000, 0, 0), + MX6_PAD_NAND_CE1_B__TPSMP_HDATA_4 = IOMUX_PAD(0x0490, 0x0148, 7, 0x0000, 0, 0), + MX6_PAD_NAND_CE1_B__ANATOP_USBPHY1_TSTI_TX_LS_MODE = IOMUX_PAD(0x0490, 0x0148, 8, 0x0000, 0, 0), + MX6_PAD_NAND_CE1_B__SDMA_DEBUG_PC_8 = IOMUX_PAD(0x0490, 0x0148, 9, 0x0000, 0, 0), + + MX6_PAD_NAND_CLE__RAWNAND_CLE = IOMUX_PAD(0x0494, 0x014C, 0, 0x0000, 0, 0), + MX6_PAD_NAND_CLE__I2C3_SCL = IOMUX_PAD(0x0494, 0x014C, 1, 0x07B8, 0, 0), + MX6_PAD_NAND_CLE__QSPI2_A_SCLK = IOMUX_PAD(0x0494, 0x014C, 2, 0x0000, 0, 0), + MX6_PAD_NAND_CLE__ECSPI2_SCLK = IOMUX_PAD(0x0494, 0x014C, 3, 0x0720, 0, 0), + MX6_PAD_NAND_CLE__ESAI_TX2_RX3 = IOMUX_PAD(0x0494, 0x014C, 4, 0x0798, 0, 0), + MX6_PAD_NAND_CLE__GPIO4_IO_3 = IOMUX_PAD(0x0494, 0x014C, 5, 0x0000, 0, 0), + MX6_PAD_NAND_CLE__WEIM_BCLK = IOMUX_PAD(0x0494, 0x014C, 6, 0x0000, 0, 0), + MX6_PAD_NAND_CLE__TPSMP_CLK = IOMUX_PAD(0x0494, 0x014C, 7, 0x0000, 0, 0), + MX6_PAD_NAND_CLE__ANATOP_USBPHY1_TSTI_TX_DP = IOMUX_PAD(0x0494, 0x014C, 8, 0x0000, 0, 0), + MX6_PAD_NAND_CLE__SDMA_DEBUG_PC_13 = IOMUX_PAD(0x0494, 0x014C, 9, 0x0000, 0, 0), + + MX6_PAD_NAND_DATA00__RAWNAND_DATA00 = IOMUX_PAD(0x0498, 0x0150, 0, 0x0000, 0, 0), + MX6_PAD_NAND_DATA00__USDHC1_DATA4 = IOMUX_PAD(0x0498, 0x0150, 1, 0x0000, 0, 0), + MX6_PAD_NAND_DATA00__QSPI2_B_DATA_1 = IOMUX_PAD(0x0498, 0x0150, 2, 0x0000, 0, 0), + MX6_PAD_NAND_DATA00__ECSPI5_MISO = IOMUX_PAD(0x0498, 0x0150, 3, 0x0754, 0, 0), + MX6_PAD_NAND_DATA00__ESAI_RX_CLK = IOMUX_PAD(0x0498, 0x0150, 4, 0x0788, 0, 0), + MX6_PAD_NAND_DATA00__GPIO4_IO_4 = IOMUX_PAD(0x0498, 0x0150, 5, 0x0000, 0, 0), + MX6_PAD_NAND_DATA00__WEIM_AD_0 = IOMUX_PAD(0x0498, 0x0150, 6, 0x0000, 0, 0), + MX6_PAD_NAND_DATA00__TPSMP_HDATA_7 = IOMUX_PAD(0x0498, 0x0150, 7, 0x0000, 0, 0), + MX6_PAD_NAND_DATA00__ANATOP_USBPHY1_TSTO_RX_DISCON_DET = IOMUX_PAD(0x0498, 0x0150, 8, 0x0000, 0, 0), + MX6_PAD_NAND_DATA00__SDMA_DEBUG_EVT_CHN_LINES_5 = IOMUX_PAD(0x0498, 0x0150, 9, 0x0000, 0, 0), + + MX6_PAD_NAND_DATA01__RAWNAND_DATA01 = IOMUX_PAD(0x049C, 0x0154, 0, 0x0000, 0, 0), + MX6_PAD_NAND_DATA01__USDHC1_DATA5 = IOMUX_PAD(0x049C, 0x0154, 1, 0x0000, 0, 0), + MX6_PAD_NAND_DATA01__QSPI2_B_DATA_0 = IOMUX_PAD(0x049C, 0x0154, 2, 0x0000, 0, 0), + MX6_PAD_NAND_DATA01__ECSPI5_MOSI = IOMUX_PAD(0x049C, 0x0154, 3, 0x0758, 0, 0), + MX6_PAD_NAND_DATA01__ESAI_RX_FS = IOMUX_PAD(0x049C, 0x0154, 4, 0x0778, 0, 0), + MX6_PAD_NAND_DATA01__GPIO4_IO_5 = IOMUX_PAD(0x049C, 0x0154, 5, 0x0000, 0, 0), + MX6_PAD_NAND_DATA01__WEIM_AD_1 = IOMUX_PAD(0x049C, 0x0154, 6, 0x0000, 0, 0), + MX6_PAD_NAND_DATA01__TPSMP_HDATA_8 = IOMUX_PAD(0x049C, 0x0154, 7, 0x0000, 0, 0), + MX6_PAD_NAND_DATA01__ANATOP_USBPHY1_TSTO_RX_HS_RXD = IOMUX_PAD(0x049C, 0x0154, 8, 0x0000, 0, 0), + MX6_PAD_NAND_DATA01__SDMA_DEBUG_EVT_CHN_LINES_4 = IOMUX_PAD(0x049C, 0x0154, 9, 0x0000, 0, 0), + + MX6_PAD_NAND_DATA02__RAWNAND_DATA02 = IOMUX_PAD(0x04A0, 0x0158, 0, 0x0000, 0, 0), + MX6_PAD_NAND_DATA02__USDHC1_DATA6 = IOMUX_PAD(0x04A0, 0x0158, 1, 0x0000, 0, 0), + MX6_PAD_NAND_DATA02__QSPI2_B_SCLK = IOMUX_PAD(0x04A0, 0x0158, 2, 0x0000, 0, 0), + MX6_PAD_NAND_DATA02__ECSPI5_SCLK = IOMUX_PAD(0x04A0, 0x0158, 3, 0x0750, 0, 0), + MX6_PAD_NAND_DATA02__ESAI_TX_HF_CLK = IOMUX_PAD(0x04A0, 0x0158, 4, 0x0784, 0, 0), + MX6_PAD_NAND_DATA02__GPIO4_IO_6 = IOMUX_PAD(0x04A0, 0x0158, 5, 0x0000, 0, 0), + MX6_PAD_NAND_DATA02__WEIM_AD_2 = IOMUX_PAD(0x04A0, 0x0158, 6, 0x0000, 0, 0), + MX6_PAD_NAND_DATA02__TPSMP_HDATA_9 = IOMUX_PAD(0x04A0, 0x0158, 7, 0x0000, 0, 0), + MX6_PAD_NAND_DATA02__ANATOP_USBPHY2_TSTO_PLL_CLK20DIV = IOMUX_PAD(0x04A0, 0x0158, 8, 0x0000, 0, 0), + MX6_PAD_NAND_DATA02__SDMA_DEBUG_EVT_CHN_LINES_3 = IOMUX_PAD(0x04A0, 0x0158, 9, 0x0000, 0, 0), + + MX6_PAD_NAND_DATA03__RAWNAND_DATA03 = IOMUX_PAD(0x04A4, 0x015C, 0, 0x0000, 0, 0), + MX6_PAD_NAND_DATA03__USDHC1_DATA7 = IOMUX_PAD(0x04A4, 0x015C, 1, 0x0000, 0, 0), + MX6_PAD_NAND_DATA03__QSPI2_B_SS0_B = IOMUX_PAD(0x04A4, 0x015C, 2, 0x0000, 0, 0), + MX6_PAD_NAND_DATA03__ECSPI5_SS0 = IOMUX_PAD(0x04A4, 0x015C, 3, 0x075C, 0, 0), + MX6_PAD_NAND_DATA03__ESAI_RX_HF_CLK = IOMUX_PAD(0x04A4, 0x015C, 4, 0x0780, 0, 0), + MX6_PAD_NAND_DATA03__GPIO4_IO_7 = IOMUX_PAD(0x04A4, 0x015C, 5, 0x0000, 0, 0), + MX6_PAD_NAND_DATA03__WEIM_AD_3 = IOMUX_PAD(0x04A4, 0x015C, 6, 0x0000, 0, 0), + MX6_PAD_NAND_DATA03__TPSMP_HDATA_10 = IOMUX_PAD(0x04A4, 0x015C, 7, 0x0000, 0, 0), + MX6_PAD_NAND_DATA03__ANATOP_USBPHY1_TSTO_RX_SQUELCH = IOMUX_PAD(0x04A4, 0x015C, 8, 0x0000, 0, 0), + MX6_PAD_NAND_DATA03__SDMA_DEBUG_EVT_CHN_LINES_6 = IOMUX_PAD(0x04A4, 0x015C, 9, 0x0000, 0, 0), + + MX6_PAD_NAND_DATA04__RAWNAND_DATA04 = IOMUX_PAD(0x04A8, 0x0160, 0, 0x0000, 0, 0), + MX6_PAD_NAND_DATA04__USDHC2_DATA4 = IOMUX_PAD(0x04A8, 0x0160, 1, 0x0000, 0, 0), + MX6_PAD_NAND_DATA04__QSPI2_B_SS1_B = IOMUX_PAD(0x04A8, 0x0160, 2, 0x0000, 0, 0), + MX6_PAD_NAND_DATA04__UART3_RTS_B = IOMUX_PAD(0x04A8, 0x0160, 3, 0x083C, 0, 0), + MX6_PAD_NAND_DATA04__AUDMUX_AUD4_RXFS = IOMUX_PAD(0x04A8, 0x0160, 4, 0x0650, 0, 0), + MX6_PAD_NAND_DATA04__GPIO4_IO_8 = IOMUX_PAD(0x04A8, 0x0160, 5, 0x0000, 0, 0), + MX6_PAD_NAND_DATA04__WEIM_AD_4 = IOMUX_PAD(0x04A8, 0x0160, 6, 0x0000, 0, 0), + MX6_PAD_NAND_DATA04__TPSMP_HDATA_11 = IOMUX_PAD(0x04A8, 0x0160, 7, 0x0000, 0, 0), + MX6_PAD_NAND_DATA04__ANATOP_USBPHY2_TSTO_RX_SQUELCH = IOMUX_PAD(0x04A8, 0x0160, 8, 0x0000, 0, 0), + MX6_PAD_NAND_DATA04__SDMA_DEBUG_CORE_STATE_0 = IOMUX_PAD(0x04A8, 0x0160, 9, 0x0000, 0, 0), + + MX6_PAD_NAND_DATA05__RAWNAND_DATA05 = IOMUX_PAD(0x04AC, 0x0164, 0, 0x0000, 0, 0), + MX6_PAD_NAND_DATA05__USDHC2_DATA5 = IOMUX_PAD(0x04AC, 0x0164, 1, 0x0000, 0, 0), + MX6_PAD_NAND_DATA05__QSPI2_B_DQS = IOMUX_PAD(0x04AC, 0x0164, 2, 0x0000, 0, 0), + MX6_PAD_NAND_DATA05__UART3_CTS_B = IOMUX_PAD(0x04AC, 0x0164, 3, 0x083C, 1, 0), + MX6_PAD_NAND_DATA05__AUDMUX_AUD4_RXC = IOMUX_PAD(0x04AC, 0x0164, 4, 0x064C, 0, 0), + MX6_PAD_NAND_DATA05__GPIO4_IO_9 = IOMUX_PAD(0x04AC, 0x0164, 5, 0x0000, 0, 0), + MX6_PAD_NAND_DATA05__WEIM_AD_5 = IOMUX_PAD(0x04AC, 0x0164, 6, 0x0000, 0, 0), + MX6_PAD_NAND_DATA05__TPSMP_HDATA_12 = IOMUX_PAD(0x04AC, 0x0164, 7, 0x0000, 0, 0), + MX6_PAD_NAND_DATA05__ANATOP_USBPHY2_TSTO_RX_DISCON_DET = IOMUX_PAD(0x04AC, 0x0164, 8, 0x0000, 0, 0), + MX6_PAD_NAND_DATA05__SDMA_DEBUG_CORE_STATE_1 = IOMUX_PAD(0x04AC, 0x0164, 9, 0x0000, 0, 0), + + MX6_PAD_NAND_DATA06__RAWNAND_DATA06 = IOMUX_PAD(0x04B0, 0x0168, 0, 0x0000, 0, 0), + MX6_PAD_NAND_DATA06__USDHC2_DATA6 = IOMUX_PAD(0x04B0, 0x0168, 1, 0x0000, 0, 0), + MX6_PAD_NAND_DATA06__QSPI2_A_SS1_B = IOMUX_PAD(0x04B0, 0x0168, 2, 0x0000, 0, 0), + MX6_PAD_NAND_DATA06__UART3_RX = IOMUX_PAD(0x04B0, 0x0168, 3, 0x0840, 0, 0), + MX6_PAD_NAND_DATA06__PWM3_OUT = IOMUX_PAD(0x04B0, 0x0168, 4, 0x0000, 0, 0), + MX6_PAD_NAND_DATA06__GPIO4_IO_10 = IOMUX_PAD(0x04B0, 0x0168, 5, 0x0000, 0, 0), + MX6_PAD_NAND_DATA06__WEIM_AD_6 = IOMUX_PAD(0x04B0, 0x0168, 6, 0x0000, 0, 0), + MX6_PAD_NAND_DATA06__TPSMP_HDATA_13 = IOMUX_PAD(0x04B0, 0x0168, 7, 0x0000, 0, 0), + MX6_PAD_NAND_DATA06__ANATOP_USBPHY2_TSTO_RX_FS_RXD = IOMUX_PAD(0x04B0, 0x0168, 8, 0x0000, 0, 0), + MX6_PAD_NAND_DATA06__SDMA_DEBUG_CORE_STATE_2 = IOMUX_PAD(0x04B0, 0x0168, 9, 0x0000, 0, 0), + + MX6_PAD_NAND_DATA07__RAWNAND_DATA07 = IOMUX_PAD(0x04B4, 0x016C, 0, 0x0000, 0, 0), + MX6_PAD_NAND_DATA07__USDHC2_DATA7 = IOMUX_PAD(0x04B4, 0x016C, 1, 0x0000, 0, 0), + MX6_PAD_NAND_DATA07__QSPI2_A_DQS = IOMUX_PAD(0x04B4, 0x016C, 2, 0x0000, 0, 0), + MX6_PAD_NAND_DATA07__UART3_TX = IOMUX_PAD(0x04B4, 0x016C, 3, 0x0840, 1, 0), + MX6_PAD_NAND_DATA07__PWM4_OUT = IOMUX_PAD(0x04B4, 0x016C, 4, 0x0000, 0, 0), + MX6_PAD_NAND_DATA07__GPIO4_IO_11 = IOMUX_PAD(0x04B4, 0x016C, 5, 0x0000, 0, 0), + MX6_PAD_NAND_DATA07__WEIM_AD_7 = IOMUX_PAD(0x04B4, 0x016C, 6, 0x0000, 0, 0), + MX6_PAD_NAND_DATA07__TPSMP_HDATA_14 = IOMUX_PAD(0x04B4, 0x016C, 7, 0x0000, 0, 0), + MX6_PAD_NAND_DATA07__ANATOP_USBPHY1_TSTO_RX_FS_RXD = IOMUX_PAD(0x04B4, 0x016C, 8, 0x0000, 0, 0), + MX6_PAD_NAND_DATA07__SDMA_DEBUG_CORE_STATE_3 = IOMUX_PAD(0x04B4, 0x016C, 9, 0x0000, 0, 0), + + MX6_PAD_NAND_RE_B__RAWNAND_RE_B = IOMUX_PAD(0x04B8, 0x0170, 0, 0x0000, 0, 0), + MX6_PAD_NAND_RE_B__USDHC2_RESET_B = IOMUX_PAD(0x04B8, 0x0170, 1, 0x0000, 0, 0), + MX6_PAD_NAND_RE_B__QSPI2_B_DATA_3 = IOMUX_PAD(0x04B8, 0x0170, 2, 0x0000, 0, 0), + MX6_PAD_NAND_RE_B__AUDMUX_AUD4_TXFS = IOMUX_PAD(0x04B8, 0x0170, 3, 0x0658, 0, 0), + MX6_PAD_NAND_RE_B__ESAI_TX_FS = IOMUX_PAD(0x04B8, 0x0170, 4, 0x077C, 0, 0), + MX6_PAD_NAND_RE_B__GPIO4_IO_12 = IOMUX_PAD(0x04B8, 0x0170, 5, 0x0000, 0, 0), + MX6_PAD_NAND_RE_B__WEIM_RW = IOMUX_PAD(0x04B8, 0x0170, 6, 0x0000, 0, 0), + MX6_PAD_NAND_RE_B__TPSMP_HDATA_5 = IOMUX_PAD(0x04B8, 0x0170, 7, 0x0000, 0, 0), + MX6_PAD_NAND_RE_B__ANATOP_USBPHY2_TSTO_RX_HS_RXD = IOMUX_PAD(0x04B8, 0x0170, 8, 0x0000, 0, 0), + MX6_PAD_NAND_RE_B__SDMA_DEBUG_PC_7 = IOMUX_PAD(0x04B8, 0x0170, 9, 0x0000, 0, 0), + + MX6_PAD_NAND_READY_B__RAWNAND_READY_B = IOMUX_PAD(0x04BC, 0x0174, 0, 0x0000, 0, 0), + MX6_PAD_NAND_READY_B__USDHC1_VSELECT = IOMUX_PAD(0x04BC, 0x0174, 1, 0x0000, 0, 0), + MX6_PAD_NAND_READY_B__QSPI2_A_DATA_1 = IOMUX_PAD(0x04BC, 0x0174, 2, 0x0000, 0, 0), + MX6_PAD_NAND_READY_B__ECSPI2_MISO = IOMUX_PAD(0x04BC, 0x0174, 3, 0x0724, 0, 0), + MX6_PAD_NAND_READY_B__ESAI_TX1 = IOMUX_PAD(0x04BC, 0x0174, 4, 0x0794, 0, 0), + MX6_PAD_NAND_READY_B__GPIO4_IO_13 = IOMUX_PAD(0x04BC, 0x0174, 5, 0x0000, 0, 0), + MX6_PAD_NAND_READY_B__WEIM_EB_B_1 = IOMUX_PAD(0x04BC, 0x0174, 6, 0x0000, 0, 0), + MX6_PAD_NAND_READY_B__TPSMP_HDATA_2 = IOMUX_PAD(0x04BC, 0x0174, 7, 0x0000, 0, 0), + MX6_PAD_NAND_READY_B__ANATOP_USBPHY1_TSTI_TX_DN = IOMUX_PAD(0x04BC, 0x0174, 8, 0x0000, 0, 0), + MX6_PAD_NAND_READY_B__SDMA_DEBUG_PC_10 = IOMUX_PAD(0x04BC, 0x0174, 9, 0x0000, 0, 0), + + MX6_PAD_NAND_WE_B__RAWNAND_WE_B = IOMUX_PAD(0x04C0, 0x0178, 0, 0x0000, 0, 0), + MX6_PAD_NAND_WE_B__USDHC4_VSELECT = IOMUX_PAD(0x04C0, 0x0178, 1, 0x0000, 0, 0), + MX6_PAD_NAND_WE_B__QSPI2_B_DATA_2 = IOMUX_PAD(0x04C0, 0x0178, 2, 0x0000, 0, 0), + MX6_PAD_NAND_WE_B__AUDMUX_AUD4_RXD = IOMUX_PAD(0x04C0, 0x0178, 3, 0x0644, 0, 0), + MX6_PAD_NAND_WE_B__ESAI_TX5_RX0 = IOMUX_PAD(0x04C0, 0x0178, 4, 0x07A4, 0, 0), + MX6_PAD_NAND_WE_B__GPIO4_IO_14 = IOMUX_PAD(0x04C0, 0x0178, 5, 0x0000, 0, 0), + MX6_PAD_NAND_WE_B__WEIM_WAIT = IOMUX_PAD(0x04C0, 0x0178, 6, 0x0000, 0, 0), + MX6_PAD_NAND_WE_B__TPSMP_HDATA_6 = IOMUX_PAD(0x04C0, 0x0178, 7, 0x0000, 0, 0), + MX6_PAD_NAND_WE_B__ANATOP_USBPHY1_TSTO_PLL_CLK20DIV = IOMUX_PAD(0x04C0, 0x0178, 8, 0x0000, 0, 0), + MX6_PAD_NAND_WE_B__SDMA_DEBUG_PC_6 = IOMUX_PAD(0x04C0, 0x0178, 9, 0x0000, 0, 0), + + MX6_PAD_NAND_WP_B__RAWNAND_WP_B = IOMUX_PAD(0x04C4, 0x017C, 0, 0x0000, 0, 0), + MX6_PAD_NAND_WP_B__USDHC1_RESET_B = IOMUX_PAD(0x04C4, 0x017C, 1, 0x0000, 0, 0), + MX6_PAD_NAND_WP_B__QSPI2_A_DATA_0 = IOMUX_PAD(0x04C4, 0x017C, 2, 0x0000, 0, 0), + MX6_PAD_NAND_WP_B__ECSPI2_MOSI = IOMUX_PAD(0x04C4, 0x017C, 3, 0x0728, 0, 0), + MX6_PAD_NAND_WP_B__ESAI_TX4_RX1 = IOMUX_PAD(0x04C4, 0x017C, 4, 0x07A0, 0, 0), + MX6_PAD_NAND_WP_B__GPIO4_IO_15 = IOMUX_PAD(0x04C4, 0x017C, 5, 0x0000, 0, 0), + MX6_PAD_NAND_WP_B__WEIM_EB_B_0 = IOMUX_PAD(0x04C4, 0x017C, 6, 0x0000, 0, 0), + MX6_PAD_NAND_WP_B__TPSMP_HDATA_1 = IOMUX_PAD(0x04C4, 0x017C, 7, 0x0000, 0, 0), + MX6_PAD_NAND_WP_B__ANATOP_USBPHY1_TSTI_TX_HS_MODE = IOMUX_PAD(0x04C4, 0x017C, 8, 0x0000, 0, 0), + MX6_PAD_NAND_WP_B__SDMA_DEBUG_PC_11 = IOMUX_PAD(0x04C4, 0x017C, 9, 0x0000, 0, 0), + + MX6_PAD_QSPI1A_DATA0__QSPI1_A_DATA_0 = IOMUX_PAD(0x04C8, 0x0180, 0, 0x0000, 0, 0), + MX6_PAD_QSPI1A_DATA0__USB_OTG2_OC = IOMUX_PAD(0x04C8, 0x0180, 1, 0x085C, 2, 0), + MX6_PAD_QSPI1A_DATA0__ECSPI1_MOSI = IOMUX_PAD(0x04C8, 0x0180, 2, 0x0718, 1, 0), + MX6_PAD_QSPI1A_DATA0__ESAI_TX4_RX1 = IOMUX_PAD(0x04C8, 0x0180, 3, 0x07A0, 2, 0), + MX6_PAD_QSPI1A_DATA0__CSI1_DATA_14 = IOMUX_PAD(0x04C8, 0x0180, 4, 0x06D4, 1, 0), + MX6_PAD_QSPI1A_DATA0__GPIO4_IO_16 = IOMUX_PAD(0x04C8, 0x0180, 5, 0x0000, 0, 0), + MX6_PAD_QSPI1A_DATA0__WEIM_DATA_6 = IOMUX_PAD(0x04C8, 0x0180, 6, 0x0000, 0, 0), + MX6_PAD_QSPI1A_DATA0__SIM_M_HADDR_3 = IOMUX_PAD(0x04C8, 0x0180, 7, 0x0000, 0, 0), + MX6_PAD_QSPI1A_DATA0__SDMA_DEBUG_BUS_DEVICE_3 = IOMUX_PAD(0x04C8, 0x0180, 9, 0x0000, 0, 0), + + MX6_PAD_QSPI1A_DATA1__QSPI1_A_DATA_1 = IOMUX_PAD(0x04CC, 0x0184, 0, 0x0000, 0, 0), + MX6_PAD_QSPI1A_DATA1__ANATOP_OTG1_ID = IOMUX_PAD(0x04CC, 0x0184, 1, 0x0624, 2, 0), + MX6_PAD_QSPI1A_DATA1__ECSPI1_MISO = IOMUX_PAD(0x04CC, 0x0184, 2, 0x0714, 1, 0), + MX6_PAD_QSPI1A_DATA1__ESAI_TX1 = IOMUX_PAD(0x04CC, 0x0184, 3, 0x0794, 2, 0), + MX6_PAD_QSPI1A_DATA1__CSI1_DATA_13 = IOMUX_PAD(0x04CC, 0x0184, 4, 0x06D0, 1, 0), + MX6_PAD_QSPI1A_DATA1__GPIO4_IO_17 = IOMUX_PAD(0x04CC, 0x0184, 5, 0x0000, 0, 0), + MX6_PAD_QSPI1A_DATA1__WEIM_DATA_5 = IOMUX_PAD(0x04CC, 0x0184, 6, 0x0000, 0, 0), + MX6_PAD_QSPI1A_DATA1__SIM_M_HADDR_4 = IOMUX_PAD(0x04CC, 0x0184, 7, 0x0000, 0, 0), + MX6_PAD_QSPI1A_DATA1__SDMA_DEBUG_PC_0 = IOMUX_PAD(0x04CC, 0x0184, 9, 0x0000, 0, 0), + + MX6_PAD_QSPI1A_DATA2__QSPI1_A_DATA_2 = IOMUX_PAD(0x04D0, 0x0188, 0, 0x0000, 0, 0), + MX6_PAD_QSPI1A_DATA2__USB_OTG1_PWR = IOMUX_PAD(0x04D0, 0x0188, 1, 0x0000, 0, 0), + MX6_PAD_QSPI1A_DATA2__ECSPI5_SS1 = IOMUX_PAD(0x04D0, 0x0188, 2, 0x0000, 0, 0), + MX6_PAD_QSPI1A_DATA2__ESAI_TX_CLK = IOMUX_PAD(0x04D0, 0x0188, 3, 0x078C, 2, 0), + MX6_PAD_QSPI1A_DATA2__CSI1_DATA_12 = IOMUX_PAD(0x04D0, 0x0188, 4, 0x06CC, 1, 0), + MX6_PAD_QSPI1A_DATA2__GPIO4_IO_18 = IOMUX_PAD(0x04D0, 0x0188, 5, 0x0000, 0, 0), + MX6_PAD_QSPI1A_DATA2__WEIM_DATA_4 = IOMUX_PAD(0x04D0, 0x0188, 6, 0x0000, 0, 0), + MX6_PAD_QSPI1A_DATA2__SIM_M_HADDR_6 = IOMUX_PAD(0x04D0, 0x0188, 7, 0x0000, 0, 0), + MX6_PAD_QSPI1A_DATA2__SDMA_DEBUG_PC_1 = IOMUX_PAD(0x04D0, 0x0188, 9, 0x0000, 0, 0), + + MX6_PAD_QSPI1A_DATA3__QSPI1_A_DATA_3 = IOMUX_PAD(0x04D4, 0x018C, 0, 0x0000, 0, 0), + MX6_PAD_QSPI1A_DATA3__USB_OTG1_OC = IOMUX_PAD(0x04D4, 0x018C, 1, 0x0860, 2, 0), + MX6_PAD_QSPI1A_DATA3__ECSPI5_SS2 = IOMUX_PAD(0x04D4, 0x018C, 2, 0x0000, 0, 0), + MX6_PAD_QSPI1A_DATA3__ESAI_TX0 = IOMUX_PAD(0x04D4, 0x018C, 3, 0x0790, 2, 0), + MX6_PAD_QSPI1A_DATA3__CSI1_DATA_11 = IOMUX_PAD(0x04D4, 0x018C, 4, 0x06C8, 1, 0), + MX6_PAD_QSPI1A_DATA3__GPIO4_IO_19 = IOMUX_PAD(0x04D4, 0x018C, 5, 0x0000, 0, 0), + MX6_PAD_QSPI1A_DATA3__WEIM_DATA_3 = IOMUX_PAD(0x04D4, 0x018C, 6, 0x0000, 0, 0), + MX6_PAD_QSPI1A_DATA3__SIM_M_HADDR_7 = IOMUX_PAD(0x04D4, 0x018C, 7, 0x0000, 0, 0), + MX6_PAD_QSPI1A_DATA3__SDMA_DEBUG_PC_2 = IOMUX_PAD(0x04D4, 0x018C, 9, 0x0000, 0, 0), + + MX6_PAD_QSPI1A_DQS__QSPI1_A_DQS = IOMUX_PAD(0x04D8, 0x0190, 0, 0x0000, 0, 0), + MX6_PAD_QSPI1A_DQS__CAN2_TX = IOMUX_PAD(0x04D8, 0x0190, 1, 0x0000, 0, 0), + MX6_PAD_QSPI1A_DQS__CANFD_TX2 = IOMUX_PAD(0x04D8, 0x0190, 2, 0x0000, 0, 0), + MX6_PAD_QSPI1A_DQS__ECSPI5_MOSI = IOMUX_PAD(0x04D8, 0x0190, 3, 0x0758, 1, 0), + MX6_PAD_QSPI1A_DQS__CSI1_DATA_15 = IOMUX_PAD(0x04D8, 0x0190, 4, 0x06D8, 1, 0), + MX6_PAD_QSPI1A_DQS__GPIO4_IO_20 = IOMUX_PAD(0x04D8, 0x0190, 5, 0x0000, 0, 0), + MX6_PAD_QSPI1A_DQS__WEIM_DATA_7 = IOMUX_PAD(0x04D8, 0x0190, 6, 0x0000, 0, 0), + MX6_PAD_QSPI1A_DQS__SIM_M_HADDR_13 = IOMUX_PAD(0x04D8, 0x0190, 7, 0x0000, 0, 0), + MX6_PAD_QSPI1A_DQS__SDMA_DEBUG_BUS_DEVICE_4 = IOMUX_PAD(0x04D8, 0x0190, 9, 0x0000, 0, 0), + + MX6_PAD_QSPI1A_SCLK__QSPI1_A_SCLK = IOMUX_PAD(0x04DC, 0x0194, 0, 0x0000, 0, 0), + MX6_PAD_QSPI1A_SCLK__ANATOP_OTG2_ID = IOMUX_PAD(0x04DC, 0x0194, 1, 0x0628, 2, 0), + MX6_PAD_QSPI1A_SCLK__ECSPI1_SCLK = IOMUX_PAD(0x04DC, 0x0194, 2, 0x0710, 1, 0), + MX6_PAD_QSPI1A_SCLK__ESAI_TX2_RX3 = IOMUX_PAD(0x04DC, 0x0194, 3, 0x0798, 2, 0), + MX6_PAD_QSPI1A_SCLK__CSI1_DATA_1 = IOMUX_PAD(0x04DC, 0x0194, 4, 0x06A4, 1, 0), + MX6_PAD_QSPI1A_SCLK__GPIO4_IO_21 = IOMUX_PAD(0x04DC, 0x0194, 5, 0x0000, 0, 0), + MX6_PAD_QSPI1A_SCLK__WEIM_DATA_0 = IOMUX_PAD(0x04DC, 0x0194, 6, 0x0000, 0, 0), + MX6_PAD_QSPI1A_SCLK__SIM_M_HADDR_0 = IOMUX_PAD(0x04DC, 0x0194, 7, 0x0000, 0, 0), + MX6_PAD_QSPI1A_SCLK__SDMA_DEBUG_PC_5 = IOMUX_PAD(0x04DC, 0x0194, 9, 0x0000, 0, 0), + + MX6_PAD_QSPI1A_SS0_B__QSPI1_A_SS0_B = IOMUX_PAD(0x04E0, 0x0198, 0, 0x0000, 0, 0), + MX6_PAD_QSPI1A_SS0_B__USB_OTG2_PWR = IOMUX_PAD(0x04E0, 0x0198, 1, 0x0000, 0, 0), + MX6_PAD_QSPI1A_SS0_B__ECSPI1_SS0 = IOMUX_PAD(0x04E0, 0x0198, 2, 0x071C, 1, 0), + MX6_PAD_QSPI1A_SS0_B__ESAI_TX3_RX2 = IOMUX_PAD(0x04E0, 0x0198, 3, 0x079C, 2, 0), + MX6_PAD_QSPI1A_SS0_B__CSI1_DATA_0 = IOMUX_PAD(0x04E0, 0x0198, 4, 0x06A0, 1, 0), + MX6_PAD_QSPI1A_SS0_B__GPIO4_IO_22 = IOMUX_PAD(0x04E0, 0x0198, 5, 0x0000, 0, 0), + MX6_PAD_QSPI1A_SS0_B__WEIM_DATA_1 = IOMUX_PAD(0x04E0, 0x0198, 6, 0x0000, 0, 0), + MX6_PAD_QSPI1A_SS0_B__SIM_M_HADDR_1 = IOMUX_PAD(0x04E0, 0x0198, 7, 0x0000, 0, 0), + MX6_PAD_QSPI1A_SS0_B__SDMA_DEBUG_PC_4 = IOMUX_PAD(0x04E0, 0x0198, 9, 0x0000, 0, 0), + + MX6_PAD_QSPI1A_SS1_B__QSPI1_A_SS1_B = IOMUX_PAD(0x04E4, 0x019C, 0, 0x0000, 0, 0), + MX6_PAD_QSPI1A_SS1_B__CAN1_RX = IOMUX_PAD(0x04E4, 0x019C, 1, 0x068C, 2, 0), + MX6_PAD_QSPI1A_SS1_B__CANFD_RX1 = IOMUX_PAD(0x04E4, 0x019C, 2, 0x0694, 2, 0), + MX6_PAD_QSPI1A_SS1_B__ECSPI5_MISO = IOMUX_PAD(0x04E4, 0x019C, 3, 0x0754, 1, 0), + MX6_PAD_QSPI1A_SS1_B__CSI1_DATA_10 = IOMUX_PAD(0x04E4, 0x019C, 4, 0x06FC, 1, 0), + MX6_PAD_QSPI1A_SS1_B__GPIO4_IO_23 = IOMUX_PAD(0x04E4, 0x019C, 5, 0x0000, 0, 0), + MX6_PAD_QSPI1A_SS1_B__WEIM_DATA_2 = IOMUX_PAD(0x04E4, 0x019C, 6, 0x0000, 0, 0), + MX6_PAD_QSPI1A_SS1_B__SIM_M_HADDR_12 = IOMUX_PAD(0x04E4, 0x019C, 7, 0x0000, 0, 0), + MX6_PAD_QSPI1A_SS1_B__SDMA_DEBUG_PC_3 = IOMUX_PAD(0x04E4, 0x019C, 9, 0x0000, 0, 0), + + MX6_PAD_QSPI1B_DATA0__QSPI1_B_DATA_0 = IOMUX_PAD(0x04E8, 0x01A0, 0, 0x0000, 0, 0), + MX6_PAD_QSPI1B_DATA0__UART3_CTS_B = IOMUX_PAD(0x04E8, 0x01A0, 1, 0x083C, 4, 0), + MX6_PAD_QSPI1B_DATA0__ECSPI3_MOSI = IOMUX_PAD(0x04E8, 0x01A0, 2, 0x0738, 1, 0), + MX6_PAD_QSPI1B_DATA0__ESAI_RX_FS = IOMUX_PAD(0x04E8, 0x01A0, 3, 0x0778, 2, 0), + MX6_PAD_QSPI1B_DATA0__CSI1_DATA_22 = IOMUX_PAD(0x04E8, 0x01A0, 4, 0x06F4, 1, 0), + MX6_PAD_QSPI1B_DATA0__GPIO4_IO_24 = IOMUX_PAD(0x04E8, 0x01A0, 5, 0x0000, 0, 0), + MX6_PAD_QSPI1B_DATA0__WEIM_DATA_14 = IOMUX_PAD(0x04E8, 0x01A0, 6, 0x0000, 0, 0), + MX6_PAD_QSPI1B_DATA0__SIM_M_HADDR_9 = IOMUX_PAD(0x04E8, 0x01A0, 7, 0x0000, 0, 0), + + MX6_PAD_QSPI1B_DATA1__QSPI1_B_DATA_1 = IOMUX_PAD(0x04EC, 0x01A4, 0, 0x0000, 0, 0), + MX6_PAD_QSPI1B_DATA1__UART3_RTS_B = IOMUX_PAD(0x04EC, 0x01A4, 1, 0x083C, 5, 0), + MX6_PAD_QSPI1B_DATA1__ECSPI3_MISO = IOMUX_PAD(0x04EC, 0x01A4, 2, 0x0734, 1, 0), + MX6_PAD_QSPI1B_DATA1__ESAI_RX_CLK = IOMUX_PAD(0x04EC, 0x01A4, 3, 0x0788, 2, 0), + MX6_PAD_QSPI1B_DATA1__CSI1_DATA_21 = IOMUX_PAD(0x04EC, 0x01A4, 4, 0x06F0, 1, 0), + MX6_PAD_QSPI1B_DATA1__GPIO4_IO_25 = IOMUX_PAD(0x04EC, 0x01A4, 5, 0x0000, 0, 0), + MX6_PAD_QSPI1B_DATA1__WEIM_DATA_13 = IOMUX_PAD(0x04EC, 0x01A4, 6, 0x0000, 0, 0), + MX6_PAD_QSPI1B_DATA1__SIM_M_HADDR_8 = IOMUX_PAD(0x04EC, 0x01A4, 7, 0x0000, 0, 0), + + MX6_PAD_QSPI1B_DATA2__QSPI1_B_DATA_2 = IOMUX_PAD(0x04F0, 0x01A8, 0, 0x0000, 0, 0), + MX6_PAD_QSPI1B_DATA2__I2C2_SDA = IOMUX_PAD(0x04F0, 0x01A8, 1, 0x07B4, 2, 0), + MX6_PAD_QSPI1B_DATA2__ECSPI5_RDY = IOMUX_PAD(0x04F0, 0x01A8, 2, 0x0000, 0, 0), + MX6_PAD_QSPI1B_DATA2__ESAI_TX5_RX0 = IOMUX_PAD(0x04F0, 0x01A8, 3, 0x07A4, 2, 0), + MX6_PAD_QSPI1B_DATA2__CSI1_DATA_20 = IOMUX_PAD(0x04F0, 0x01A8, 4, 0x06EC, 1, 0), + MX6_PAD_QSPI1B_DATA2__GPIO4_IO_26 = IOMUX_PAD(0x04F0, 0x01A8, 5, 0x0000, 0, 0), + MX6_PAD_QSPI1B_DATA2__WEIM_DATA_12 = IOMUX_PAD(0x04F0, 0x01A8, 6, 0x0000, 0, 0), + MX6_PAD_QSPI1B_DATA2__SIM_M_HADDR_5 = IOMUX_PAD(0x04F0, 0x01A8, 7, 0x0000, 0, 0), + + MX6_PAD_QSPI1B_DATA3__QSPI1_B_DATA_3 = IOMUX_PAD(0x04F4, 0x01AC, 0, 0x0000, 0, 0), + MX6_PAD_QSPI1B_DATA3__I2C2_SCL = IOMUX_PAD(0x04F4, 0x01AC, 1, 0x07B0, 2, 0), + MX6_PAD_QSPI1B_DATA3__ECSPI5_SS3 = IOMUX_PAD(0x04F4, 0x01AC, 2, 0x0000, 0, 0), + MX6_PAD_QSPI1B_DATA3__ESAI_TX_FS = IOMUX_PAD(0x04F4, 0x01AC, 3, 0x077C, 2, 0), + MX6_PAD_QSPI1B_DATA3__CSI1_DATA_19 = IOMUX_PAD(0x04F4, 0x01AC, 4, 0x06E8, 1, 0), + MX6_PAD_QSPI1B_DATA3__GPIO4_IO_27 = IOMUX_PAD(0x04F4, 0x01AC, 5, 0x0000, 0, 0), + MX6_PAD_QSPI1B_DATA3__WEIM_DATA_11 = IOMUX_PAD(0x04F4, 0x01AC, 6, 0x0000, 0, 0), + MX6_PAD_QSPI1B_DATA3__SIM_M_HADDR_2 = IOMUX_PAD(0x04F4, 0x01AC, 7, 0x0000, 0, 0), + + MX6_PAD_QSPI1B_DQS__QSPI1_B_DQS = IOMUX_PAD(0x04F8, 0x01B0, 0, 0x0000, 0, 0), + MX6_PAD_QSPI1B_DQS__CAN1_TX = IOMUX_PAD(0x04F8, 0x01B0, 1, 0x0000, 0, 0), + MX6_PAD_QSPI1B_DQS__CANFD_TX1 = IOMUX_PAD(0x04F8, 0x01B0, 2, 0x0000, 0, 0), + MX6_PAD_QSPI1B_DQS__ECSPI5_SS0 = IOMUX_PAD(0x04F8, 0x01B0, 3, 0x075C, 1, 0), + MX6_PAD_QSPI1B_DQS__CSI1_DATA_23 = IOMUX_PAD(0x04F8, 0x01B0, 4, 0x06F8, 1, 0), + MX6_PAD_QSPI1B_DQS__GPIO4_IO_28 = IOMUX_PAD(0x04F8, 0x01B0, 5, 0x0000, 0, 0), + MX6_PAD_QSPI1B_DQS__WEIM_DATA_15 = IOMUX_PAD(0x04F8, 0x01B0, 6, 0x0000, 0, 0), + MX6_PAD_QSPI1B_DQS__SIM_M_HADDR_15 = IOMUX_PAD(0x04F8, 0x01B0, 7, 0x0000, 0, 0), + + MX6_PAD_QSPI1B_SCLK__QSPI1_B_SCLK = IOMUX_PAD(0x04FC, 0x01B4, 0, 0x0000, 0, 0), + MX6_PAD_QSPI1B_SCLK__UART3_RX = IOMUX_PAD(0x04FC, 0x01B4, 1, 0x0840, 4, 0), + MX6_PAD_QSPI1B_SCLK__ECSPI3_SCLK = IOMUX_PAD(0x04FC, 0x01B4, 2, 0x0730, 1, 0), + MX6_PAD_QSPI1B_SCLK__ESAI_RX_HF_CLK = IOMUX_PAD(0x04FC, 0x01B4, 3, 0x0780, 2, 0), + MX6_PAD_QSPI1B_SCLK__CSI1_DATA_16 = IOMUX_PAD(0x04FC, 0x01B4, 4, 0x06DC, 1, 0), + MX6_PAD_QSPI1B_SCLK__GPIO4_IO_29 = IOMUX_PAD(0x04FC, 0x01B4, 5, 0x0000, 0, 0), + MX6_PAD_QSPI1B_SCLK__WEIM_DATA_8 = IOMUX_PAD(0x04FC, 0x01B4, 6, 0x0000, 0, 0), + MX6_PAD_QSPI1B_SCLK__SIM_M_HADDR_11 = IOMUX_PAD(0x04FC, 0x01B4, 7, 0x0000, 0, 0), + + MX6_PAD_QSPI1B_SS0_B__QSPI1_B_SS0_B = IOMUX_PAD(0x0500, 0x01B8, 0, 0x0000, 0, 0), + MX6_PAD_QSPI1B_SS0_B__UART3_TX = IOMUX_PAD(0x0500, 0x01B8, 1, 0x0840, 5, 0), + MX6_PAD_QSPI1B_SS0_B__ECSPI3_SS0 = IOMUX_PAD(0x0500, 0x01B8, 2, 0x073C, 1, 0), + MX6_PAD_QSPI1B_SS0_B__ESAI_TX_HF_CLK = IOMUX_PAD(0x0500, 0x01B8, 3, 0x0784, 3, 0), + MX6_PAD_QSPI1B_SS0_B__CSI1_DATA_17 = IOMUX_PAD(0x0500, 0x01B8, 4, 0x06E0, 1, 0), + MX6_PAD_QSPI1B_SS0_B__GPIO4_IO_30 = IOMUX_PAD(0x0500, 0x01B8, 5, 0x0000, 0, 0), + MX6_PAD_QSPI1B_SS0_B__WEIM_DATA_9 = IOMUX_PAD(0x0500, 0x01B8, 6, 0x0000, 0, 0), + MX6_PAD_QSPI1B_SS0_B__SIM_M_HADDR_10 = IOMUX_PAD(0x0500, 0x01B8, 7, 0x0000, 0, 0), + + MX6_PAD_QSPI1B_SS1_B__QSPI1_B_SS1_B = IOMUX_PAD(0x0504, 0x01BC, 0, 0x0000, 0, 0), + MX6_PAD_QSPI1B_SS1_B__CAN2_RX = IOMUX_PAD(0x0504, 0x01BC, 1, 0x0690, 2, 0), + MX6_PAD_QSPI1B_SS1_B__CANFD_RX2 = IOMUX_PAD(0x0504, 0x01BC, 2, 0x0698, 2, 0), + MX6_PAD_QSPI1B_SS1_B__ECSPI5_SCLK = IOMUX_PAD(0x0504, 0x01BC, 3, 0x0750, 1, 0), + MX6_PAD_QSPI1B_SS1_B__CSI1_DATA_18 = IOMUX_PAD(0x0504, 0x01BC, 4, 0x06E4, 1, 0), + MX6_PAD_QSPI1B_SS1_B__GPIO4_IO_31 = IOMUX_PAD(0x0504, 0x01BC, 5, 0x0000, 0, 0), + MX6_PAD_QSPI1B_SS1_B__WEIM_DATA_10 = IOMUX_PAD(0x0504, 0x01BC, 6, 0x0000, 0, 0), + MX6_PAD_QSPI1B_SS1_B__SIM_M_HADDR_14 = IOMUX_PAD(0x0504, 0x01BC, 7, 0x0000, 0, 0), + + MX6_PAD_RGMII1_RD0__ENET1_RX_DATA_0 = IOMUX_PAD(0x0508, 0x01C0, 0, 0x0000, 0, 0), + MX6_PAD_RGMII1_RD0__GPIO5_IO_0 = IOMUX_PAD(0x0508, 0x01C0, 5, 0x0000, 0, 0), + MX6_PAD_RGMII1_RD0__CSI2_DATA_10 = IOMUX_PAD(0x0508, 0x01C0, 6, 0x0000, 0, 0), + MX6_PAD_RGMII1_RD0__ANATOP_TESTI_0 = IOMUX_PAD(0x0508, 0x01C0, 7, 0x0000, 0, 0), + MX6_PAD_RGMII1_RD0__RAWNAND_TESTER_TRIGGER = IOMUX_PAD(0x0508, 0x01C0, 8, 0x0000, 0, 0), + MX6_PAD_RGMII1_RD0__PCIE_CTRL_DEBUG_0 = IOMUX_PAD(0x0508, 0x01C0, 9, 0x0000, 0, 0), + + MX6_PAD_RGMII1_RD1__ENET1_RX_DATA_1 = IOMUX_PAD(0x050C, 0x01C4, 0, 0x0000, 0, 0), + MX6_PAD_RGMII1_RD1__GPIO5_IO_1 = IOMUX_PAD(0x050C, 0x01C4, 5, 0x0000, 0, 0), + MX6_PAD_RGMII1_RD1__CSI2_DATA_11 = IOMUX_PAD(0x050C, 0x01C4, 6, 0x0000, 0, 0), + MX6_PAD_RGMII1_RD1__ANATOP_TESTI_1 = IOMUX_PAD(0x050C, 0x01C4, 7, 0x0000, 0, 0), + MX6_PAD_RGMII1_RD1__USDHC1_TESTER_TRIGGER = IOMUX_PAD(0x050C, 0x01C4, 8, 0x0000, 0, 0), + MX6_PAD_RGMII1_RD1__PCIE_CTRL_DEBUG_1 = IOMUX_PAD(0x050C, 0x01C4, 9, 0x0000, 0, 0), + + MX6_PAD_RGMII1_RD2__ENET1_RX_DATA_2 = IOMUX_PAD(0x0510, 0x01C8, 0, 0x0000, 0, 0), + MX6_PAD_RGMII1_RD2__GPIO5_IO_2 = IOMUX_PAD(0x0510, 0x01C8, 5, 0x0000, 0, 0), + MX6_PAD_RGMII1_RD2__CSI2_DATA_12 = IOMUX_PAD(0x0510, 0x01C8, 6, 0x0000, 0, 0), + MX6_PAD_RGMII1_RD2__ANATOP_TESTI_2 = IOMUX_PAD(0x0510, 0x01C8, 7, 0x0000, 0, 0), + MX6_PAD_RGMII1_RD2__USDHC2_TESTER_TRIGGER = IOMUX_PAD(0x0510, 0x01C8, 8, 0x0000, 0, 0), + MX6_PAD_RGMII1_RD2__PCIE_CTRL_DEBUG_2 = IOMUX_PAD(0x0510, 0x01C8, 9, 0x0000, 0, 0), + + MX6_PAD_RGMII1_RD3__ENET1_RX_DATA_3 = IOMUX_PAD(0x0514, 0x01CC, 0, 0x0000, 0, 0), + MX6_PAD_RGMII1_RD3__GPIO5_IO_3 = IOMUX_PAD(0x0514, 0x01CC, 5, 0x0000, 0, 0), + MX6_PAD_RGMII1_RD3__CSI2_DATA_13 = IOMUX_PAD(0x0514, 0x01CC, 6, 0x0000, 0, 0), + MX6_PAD_RGMII1_RD3__ANATOP_TESTI_3 = IOMUX_PAD(0x0514, 0x01CC, 7, 0x0000, 0, 0), + MX6_PAD_RGMII1_RD3__USDHC3_TESTER_TRIGGER = IOMUX_PAD(0x0514, 0x01CC, 8, 0x0000, 0, 0), + MX6_PAD_RGMII1_RD3__PCIE_CTRL_DEBUG_3 = IOMUX_PAD(0x0514, 0x01CC, 9, 0x0000, 0, 0), + + MX6_PAD_RGMII1_RX_CTL__ENET1_RX_EN = IOMUX_PAD(0x0518, 0x01D0, 0, 0x0000, 0, 0), + MX6_PAD_RGMII1_RX_CTL__GPIO5_IO_4 = IOMUX_PAD(0x0518, 0x01D0, 5, 0x0000, 0, 0), + MX6_PAD_RGMII1_RX_CTL__CSI2_DATA_14 = IOMUX_PAD(0x0518, 0x01D0, 6, 0x0000, 0, 0), + MX6_PAD_RGMII1_RX_CTL__ANATOP_TESTO_0 = IOMUX_PAD(0x0518, 0x01D0, 7, 0x0000, 0, 0), + MX6_PAD_RGMII1_RX_CTL__USDHC4_TESTER_TRIGGER = IOMUX_PAD(0x0518, 0x01D0, 8, 0x0000, 0, 0), + MX6_PAD_RGMII1_RX_CTL__PCIE_CTRL_DEBUG_4 = IOMUX_PAD(0x0518, 0x01D0, 9, 0x0000, 0, 0), + + MX6_PAD_RGMII1_RXC__ENET1_RX_CLK = IOMUX_PAD(0x051C, 0x01D4, 0, 0x0768, 1, 0), + MX6_PAD_RGMII1_RXC__ENET1_RX_ER = IOMUX_PAD(0x051C, 0x01D4, 1, 0x0000, 0, 0), + MX6_PAD_RGMII1_RXC__GPIO5_IO_5 = IOMUX_PAD(0x051C, 0x01D4, 5, 0x0000, 0, 0), + MX6_PAD_RGMII1_RXC__CSI2_DATA_15 = IOMUX_PAD(0x051C, 0x01D4, 6, 0x0000, 0, 0), + MX6_PAD_RGMII1_RXC__ANATOP_TESTO_1 = IOMUX_PAD(0x051C, 0x01D4, 7, 0x0000, 0, 0), + MX6_PAD_RGMII1_RXC__ECSPI1_TESTER_TRIGGER = IOMUX_PAD(0x051C, 0x01D4, 8, 0x0000, 0, 0), + MX6_PAD_RGMII1_RXC__PCIE_CTRL_DEBUG_5 = IOMUX_PAD(0x051C, 0x01D4, 9, 0x0000, 0, 0), + + MX6_PAD_RGMII1_TD0__ENET1_TX_DATA_0 = IOMUX_PAD(0x0520, 0x01D8, 0, 0x0000, 0, 0), + MX6_PAD_RGMII1_TD0__SAI2_RX_SYNC = IOMUX_PAD(0x0520, 0x01D8, 2, 0x0810, 1, 0), + MX6_PAD_RGMII1_TD0__GPIO5_IO_6 = IOMUX_PAD(0x0520, 0x01D8, 5, 0x0000, 0, 0), + MX6_PAD_RGMII1_TD0__CSI2_DATA_16 = IOMUX_PAD(0x0520, 0x01D8, 6, 0x0000, 0, 0), + MX6_PAD_RGMII1_TD0__ANATOP_TESTO_2 = IOMUX_PAD(0x0520, 0x01D8, 7, 0x0000, 0, 0), + MX6_PAD_RGMII1_TD0__ECSPI2_TESTER_TRIGGER = IOMUX_PAD(0x0520, 0x01D8, 8, 0x0000, 0, 0), + MX6_PAD_RGMII1_TD0__PCIE_CTRL_DEBUG_6 = IOMUX_PAD(0x0520, 0x01D8, 9, 0x0000, 0, 0), + + MX6_PAD_RGMII1_TD1__ENET1_TX_DATA_1 = IOMUX_PAD(0x0524, 0x01DC, 0, 0x0000, 0, 0), + MX6_PAD_RGMII1_TD1__SAI2_RX_BCLK = IOMUX_PAD(0x0524, 0x01DC, 2, 0x0808, 1, 0), + MX6_PAD_RGMII1_TD1__GPIO5_IO_7 = IOMUX_PAD(0x0524, 0x01DC, 5, 0x0000, 0, 0), + MX6_PAD_RGMII1_TD1__CSI2_DATA_17 = IOMUX_PAD(0x0524, 0x01DC, 6, 0x0000, 0, 0), + MX6_PAD_RGMII1_TD1__ANATOP_TESTO_3 = IOMUX_PAD(0x0524, 0x01DC, 7, 0x0000, 0, 0), + MX6_PAD_RGMII1_TD1__ECSPI3_TESTER_TRIGGER = IOMUX_PAD(0x0524, 0x01DC, 8, 0x0000, 0, 0), + MX6_PAD_RGMII1_TD1__PCIE_CTRL_DEBUG_7 = IOMUX_PAD(0x0524, 0x01DC, 9, 0x0000, 0, 0), + + MX6_PAD_RGMII1_TD2__ENET1_TX_DATA_2 = IOMUX_PAD(0x0528, 0x01E0, 0, 0x0000, 0, 0), + MX6_PAD_RGMII1_TD2__SAI2_TX_SYNC = IOMUX_PAD(0x0528, 0x01E0, 2, 0x0818, 1, 0), + MX6_PAD_RGMII1_TD2__GPIO5_IO_8 = IOMUX_PAD(0x0528, 0x01E0, 5, 0x0000, 0, 0), + MX6_PAD_RGMII1_TD2__CSI2_DATA_18 = IOMUX_PAD(0x0528, 0x01E0, 6, 0x0000, 0, 0), + MX6_PAD_RGMII1_TD2__ANATOP_TESTO_4 = IOMUX_PAD(0x0528, 0x01E0, 7, 0x0000, 0, 0), + MX6_PAD_RGMII1_TD2__ECSPI4_TESTER_TRIGGER = IOMUX_PAD(0x0528, 0x01E0, 8, 0x0000, 0, 0), + MX6_PAD_RGMII1_TD2__PCIE_CTRL_DEBUG_8 = IOMUX_PAD(0x0528, 0x01E0, 9, 0x0000, 0, 0), + + MX6_PAD_RGMII1_TD3__ENET1_TX_DATA_3 = IOMUX_PAD(0x052C, 0x01E4, 0, 0x0000, 0, 0), + MX6_PAD_RGMII1_TD3__SAI2_TX_BCLK = IOMUX_PAD(0x052C, 0x01E4, 2, 0x0814, 1, 0), + MX6_PAD_RGMII1_TD3__GPIO5_IO_9 = IOMUX_PAD(0x052C, 0x01E4, 5, 0x0000, 0, 0), + MX6_PAD_RGMII1_TD3__CSI2_DATA_19 = IOMUX_PAD(0x052C, 0x01E4, 6, 0x0000, 0, 0), + MX6_PAD_RGMII1_TD3__ANATOP_TESTO_5 = IOMUX_PAD(0x052C, 0x01E4, 7, 0x0000, 0, 0), + MX6_PAD_RGMII1_TD3__ECSPI5_TESTER_TRIGGER = IOMUX_PAD(0x052C, 0x01E4, 8, 0x0000, 0, 0), + MX6_PAD_RGMII1_TD3__PCIE_CTRL_DEBUG_9 = IOMUX_PAD(0x052C, 0x01E4, 9, 0x0000, 0, 0), + + MX6_PAD_RGMII1_TX_CTL__ENET1_TX_EN = IOMUX_PAD(0x0530, 0x01E8, 0, 0x0000, 0, 0), + MX6_PAD_RGMII1_TX_CTL__SAI2_RX_DATA_0 = IOMUX_PAD(0x0530, 0x01E8, 2, 0x080C, 1, 0), + MX6_PAD_RGMII1_TX_CTL__GPIO5_IO_10 = IOMUX_PAD(0x0530, 0x01E8, 5, 0x0000, 0, 0), + MX6_PAD_RGMII1_TX_CTL__CSI2_DATA_0 = IOMUX_PAD(0x0530, 0x01E8, 6, 0x0000, 0, 0), + MX6_PAD_RGMII1_TX_CTL__ANATOP_TESTO_6 = IOMUX_PAD(0x0530, 0x01E8, 7, 0x0000, 0, 0), + MX6_PAD_RGMII1_TX_CTL__QSPI1_TESTER_TRIGGER = IOMUX_PAD(0x0530, 0x01E8, 8, 0x0000, 0, 0), + MX6_PAD_RGMII1_TX_CTL__PCIE_CTRL_DEBUG_10 = IOMUX_PAD(0x0530, 0x01E8, 9, 0x0000, 0, 0), + + MX6_PAD_RGMII1_TXC__ENET1_RGMII_TXC = IOMUX_PAD(0x0534, 0x01EC, 0, 0x0000, 0, 0), + MX6_PAD_RGMII1_TXC__ENET1_TX_ER = IOMUX_PAD(0x0534, 0x01EC, 1, 0x0000, 0, 0), + MX6_PAD_RGMII1_TXC__SAI2_TX_DATA_0 = IOMUX_PAD(0x0534, 0x01EC, 2, 0x0000, 0, 0), + MX6_PAD_RGMII1_TXC__GPIO5_IO_11 = IOMUX_PAD(0x0534, 0x01EC, 5, 0x0000, 0, 0), + MX6_PAD_RGMII1_TXC__CSI2_DATA_1 = IOMUX_PAD(0x0534, 0x01EC, 6, 0x0000, 0, 0), + MX6_PAD_RGMII1_TXC__ANATOP_TESTO_7 = IOMUX_PAD(0x0534, 0x01EC, 7, 0x0000, 0, 0), + MX6_PAD_RGMII1_TXC__QSPI2_TESTER_TRIGGER = IOMUX_PAD(0x0534, 0x01EC, 8, 0x0000, 0, 0), + MX6_PAD_RGMII1_TXC__PCIE_CTRL_DEBUG_11 = IOMUX_PAD(0x0534, 0x01EC, 9, 0x0000, 0, 0), + + MX6_PAD_RGMII2_RD0__ENET2_RX_DATA_0 = IOMUX_PAD(0x0538, 0x01F0, 0, 0x0000, 0, 0), + MX6_PAD_RGMII2_RD0__PWM4_OUT = IOMUX_PAD(0x0538, 0x01F0, 2, 0x0000, 0, 0), + MX6_PAD_RGMII2_RD0__GPIO5_IO_12 = IOMUX_PAD(0x0538, 0x01F0, 5, 0x0000, 0, 0), + MX6_PAD_RGMII2_RD0__CSI2_DATA_2 = IOMUX_PAD(0x0538, 0x01F0, 6, 0x0000, 0, 0), + MX6_PAD_RGMII2_RD0__ANATOP_TESTO_8 = IOMUX_PAD(0x0538, 0x01F0, 7, 0x0000, 0, 0), + MX6_PAD_RGMII2_RD0__VDEC_DEBUG_18 = IOMUX_PAD(0x0538, 0x01F0, 8, 0x0000, 0, 0), + MX6_PAD_RGMII2_RD0__PCIE_CTRL_DEBUG_12 = IOMUX_PAD(0x0538, 0x01F0, 9, 0x0000, 0, 0), + + MX6_PAD_RGMII2_RD1__ENET2_RX_DATA_1 = IOMUX_PAD(0x053C, 0x01F4, 0, 0x0000, 0, 0), + MX6_PAD_RGMII2_RD1__PWM3_OUT = IOMUX_PAD(0x053C, 0x01F4, 2, 0x0000, 0, 0), + MX6_PAD_RGMII2_RD1__GPIO5_IO_13 = IOMUX_PAD(0x053C, 0x01F4, 5, 0x0000, 0, 0), + MX6_PAD_RGMII2_RD1__CSI2_DATA_3 = IOMUX_PAD(0x053C, 0x01F4, 6, 0x0000, 0, 0), + MX6_PAD_RGMII2_RD1__ANATOP_TESTO_9 = IOMUX_PAD(0x053C, 0x01F4, 7, 0x0000, 0, 0), + MX6_PAD_RGMII2_RD1__VDEC_DEBUG_19 = IOMUX_PAD(0x053C, 0x01F4, 8, 0x0000, 0, 0), + MX6_PAD_RGMII2_RD1__PCIE_CTRL_DEBUG_13 = IOMUX_PAD(0x053C, 0x01F4, 9, 0x0000, 0, 0), + + MX6_PAD_RGMII2_RD2__ENET2_RX_DATA_2 = IOMUX_PAD(0x0540, 0x01F8, 0, 0x0000, 0, 0), + MX6_PAD_RGMII2_RD2__PWM2_OUT = IOMUX_PAD(0x0540, 0x01F8, 2, 0x0000, 0, 0), + MX6_PAD_RGMII2_RD2__GPIO5_IO_14 = IOMUX_PAD(0x0540, 0x01F8, 5, 0x0000, 0, 0), + MX6_PAD_RGMII2_RD2__CSI2_DATA_4 = IOMUX_PAD(0x0540, 0x01F8, 6, 0x0000, 0, 0), + MX6_PAD_RGMII2_RD2__ANATOP_TESTO_10 = IOMUX_PAD(0x0540, 0x01F8, 7, 0x0000, 0, 0), + MX6_PAD_RGMII2_RD2__VDEC_DEBUG_20 = IOMUX_PAD(0x0540, 0x01F8, 8, 0x0000, 0, 0), + MX6_PAD_RGMII2_RD2__PCIE_CTRL_DEBUG_14 = IOMUX_PAD(0x0540, 0x01F8, 9, 0x0000, 0, 0), + + MX6_PAD_RGMII2_RD3__ENET2_RX_DATA_3 = IOMUX_PAD(0x0544, 0x01FC, 0, 0x0000, 0, 0), + MX6_PAD_RGMII2_RD3__PWM1_OUT = IOMUX_PAD(0x0544, 0x01FC, 2, 0x0000, 0, 0), + MX6_PAD_RGMII2_RD3__GPIO5_IO_15 = IOMUX_PAD(0x0544, 0x01FC, 5, 0x0000, 0, 0), + MX6_PAD_RGMII2_RD3__CSI2_DATA_5 = IOMUX_PAD(0x0544, 0x01FC, 6, 0x0000, 0, 0), + MX6_PAD_RGMII2_RD3__ANATOP_TESTO_11 = IOMUX_PAD(0x0544, 0x01FC, 7, 0x0000, 0, 0), + MX6_PAD_RGMII2_RD3__VDEC_DEBUG_21 = IOMUX_PAD(0x0544, 0x01FC, 8, 0x0000, 0, 0), + MX6_PAD_RGMII2_RD3__PCIE_CTRL_DEBUG_15 = IOMUX_PAD(0x0544, 0x01FC, 9, 0x0000, 0, 0), + + MX6_PAD_RGMII2_RX_CTL__ENET2_RX_EN = IOMUX_PAD(0x0548, 0x0200, 0, 0x0000, 0, 0), + MX6_PAD_RGMII2_RX_CTL__GPIO5_IO_16 = IOMUX_PAD(0x0548, 0x0200, 5, 0x0000, 0, 0), + MX6_PAD_RGMII2_RX_CTL__CSI2_DATA_6 = IOMUX_PAD(0x0548, 0x0200, 6, 0x0000, 0, 0), + MX6_PAD_RGMII2_RX_CTL__ANATOP_TESTO_12 = IOMUX_PAD(0x0548, 0x0200, 7, 0x0000, 0, 0), + MX6_PAD_RGMII2_RX_CTL__VDEC_DEBUG_22 = IOMUX_PAD(0x0548, 0x0200, 8, 0x0000, 0, 0), + MX6_PAD_RGMII2_RX_CTL__PCIE_CTRL_DEBUG_16 = IOMUX_PAD(0x0548, 0x0200, 9, 0x0000, 0, 0), + + MX6_PAD_RGMII2_RXC__ENET2_RX_CLK = IOMUX_PAD(0x054C, 0x0204, 0, 0x0774, 1, 0), + MX6_PAD_RGMII2_RXC__ENET2_RX_ER = IOMUX_PAD(0x054C, 0x0204, 1, 0x0000, 0, 0), + MX6_PAD_RGMII2_RXC__GPIO5_IO_17 = IOMUX_PAD(0x054C, 0x0204, 5, 0x0000, 0, 0), + MX6_PAD_RGMII2_RXC__CSI2_DATA_7 = IOMUX_PAD(0x054C, 0x0204, 6, 0x0000, 0, 0), + MX6_PAD_RGMII2_RXC__ANATOP_TESTO_13 = IOMUX_PAD(0x054C, 0x0204, 7, 0x0000, 0, 0), + MX6_PAD_RGMII2_RXC__VDEC_DEBUG_23 = IOMUX_PAD(0x054C, 0x0204, 8, 0x0000, 0, 0), + MX6_PAD_RGMII2_RXC__PCIE_CTRL_DEBUG_17 = IOMUX_PAD(0x054C, 0x0204, 9, 0x0000, 0, 0), + + MX6_PAD_RGMII2_TD0__ENET2_TX_DATA_0 = IOMUX_PAD(0x0550, 0x0208, 0, 0x0000, 0, 0), + MX6_PAD_RGMII2_TD0__SAI1_RX_SYNC = IOMUX_PAD(0x0550, 0x0208, 2, 0x07FC, 1, 0), + MX6_PAD_RGMII2_TD0__PWM8_OUT = IOMUX_PAD(0x0550, 0x0208, 3, 0x0000, 0, 0), + MX6_PAD_RGMII2_TD0__GPIO5_IO_18 = IOMUX_PAD(0x0550, 0x0208, 5, 0x0000, 0, 0), + MX6_PAD_RGMII2_TD0__CSI2_DATA_8 = IOMUX_PAD(0x0550, 0x0208, 6, 0x0000, 0, 0), + MX6_PAD_RGMII2_TD0__ANATOP_TESTO_14 = IOMUX_PAD(0x0550, 0x0208, 7, 0x0000, 0, 0), + MX6_PAD_RGMII2_TD0__VDEC_DEBUG_24 = IOMUX_PAD(0x0550, 0x0208, 8, 0x0000, 0, 0), + MX6_PAD_RGMII2_TD0__PCIE_CTRL_DEBUG_18 = IOMUX_PAD(0x0550, 0x0208, 9, 0x0000, 0, 0), + + MX6_PAD_RGMII2_TD1__ENET2_TX_DATA_1 = IOMUX_PAD(0x0554, 0x020C, 0, 0x0000, 0, 0), + MX6_PAD_RGMII2_TD1__SAI1_RX_BCLK = IOMUX_PAD(0x0554, 0x020C, 2, 0x07F4, 1, 0), + MX6_PAD_RGMII2_TD1__PWM7_OUT = IOMUX_PAD(0x0554, 0x020C, 3, 0x0000, 0, 0), + MX6_PAD_RGMII2_TD1__GPIO5_IO_19 = IOMUX_PAD(0x0554, 0x020C, 5, 0x0000, 0, 0), + MX6_PAD_RGMII2_TD1__CSI2_DATA_9 = IOMUX_PAD(0x0554, 0x020C, 6, 0x0000, 0, 0), + MX6_PAD_RGMII2_TD1__ANATOP_TESTO_15 = IOMUX_PAD(0x0554, 0x020C, 7, 0x0000, 0, 0), + MX6_PAD_RGMII2_TD1__VDEC_DEBUG_25 = IOMUX_PAD(0x0554, 0x020C, 8, 0x0000, 0, 0), + MX6_PAD_RGMII2_TD1__PCIE_CTRL_DEBUG_19 = IOMUX_PAD(0x0554, 0x020C, 9, 0x0000, 0, 0), + + MX6_PAD_RGMII2_TD2__ENET2_TX_DATA_2 = IOMUX_PAD(0x0558, 0x0210, 0, 0x0000, 0, 0), + MX6_PAD_RGMII2_TD2__SAI1_TX_SYNC = IOMUX_PAD(0x0558, 0x0210, 2, 0x0804, 1, 0), + MX6_PAD_RGMII2_TD2__PWM6_OUT = IOMUX_PAD(0x0558, 0x0210, 3, 0x0000, 0, 0), + MX6_PAD_RGMII2_TD2__GPIO5_IO_20 = IOMUX_PAD(0x0558, 0x0210, 5, 0x0000, 0, 0), + MX6_PAD_RGMII2_TD2__CSI2_VSYNC = IOMUX_PAD(0x0558, 0x0210, 6, 0x0000, 0, 0), + MX6_PAD_RGMII2_TD2__SJC_FAIL = IOMUX_PAD(0x0558, 0x0210, 7, 0x0000, 0, 0), + MX6_PAD_RGMII2_TD2__VDEC_DEBUG_26 = IOMUX_PAD(0x0558, 0x0210, 8, 0x0000, 0, 0), + MX6_PAD_RGMII2_TD2__PCIE_CTRL_DEBUG_20 = IOMUX_PAD(0x0558, 0x0210, 9, 0x0000, 0, 0), + + MX6_PAD_RGMII2_TD3__ENET2_TX_DATA_3 = IOMUX_PAD(0x055C, 0x0214, 0, 0x0000, 0, 0), + MX6_PAD_RGMII2_TD3__SAI1_TX_BCLK = IOMUX_PAD(0x055C, 0x0214, 2, 0x0800, 1, 0), + MX6_PAD_RGMII2_TD3__PWM5_OUT = IOMUX_PAD(0x055C, 0x0214, 3, 0x0000, 0, 0), + MX6_PAD_RGMII2_TD3__GPIO5_IO_21 = IOMUX_PAD(0x055C, 0x0214, 5, 0x0000, 0, 0), + MX6_PAD_RGMII2_TD3__CSI2_HSYNC = IOMUX_PAD(0x055C, 0x0214, 6, 0x0000, 0, 0), + MX6_PAD_RGMII2_TD3__SJC_JTAG_ACT = IOMUX_PAD(0x055C, 0x0214, 7, 0x0000, 0, 0), + MX6_PAD_RGMII2_TD3__VDEC_DEBUG_27 = IOMUX_PAD(0x055C, 0x0214, 8, 0x0000, 0, 0), + MX6_PAD_RGMII2_TD3__PCIE_CTRL_DEBUG_21 = IOMUX_PAD(0x055C, 0x0214, 9, 0x0000, 0, 0), + + MX6_PAD_RGMII2_TX_CTL__ENET2_TX_EN = IOMUX_PAD(0x0560, 0x0218, 0, 0x0000, 0, 0), + MX6_PAD_RGMII2_TX_CTL__SAI1_RX_DATA_0 = IOMUX_PAD(0x0560, 0x0218, 2, 0x07F8, 1, 0), + MX6_PAD_RGMII2_TX_CTL__GPIO5_IO_22 = IOMUX_PAD(0x0560, 0x0218, 5, 0x0000, 0, 0), + MX6_PAD_RGMII2_TX_CTL__CSI2_FIELD = IOMUX_PAD(0x0560, 0x0218, 6, 0x0000, 0, 0), + MX6_PAD_RGMII2_TX_CTL__SJC_DE_B = IOMUX_PAD(0x0560, 0x0218, 7, 0x0000, 0, 0), + MX6_PAD_RGMII2_TX_CTL__VDEC_DEBUG_28 = IOMUX_PAD(0x0560, 0x0218, 8, 0x0000, 0, 0), + MX6_PAD_RGMII2_TX_CTL__PCIE_CTRL_DEBUG_22 = IOMUX_PAD(0x0560, 0x0218, 9, 0x0000, 0, 0), + + MX6_PAD_RGMII2_TXC__ENET2_RGMII_TXC = IOMUX_PAD(0x0564, 0x021C, 0, 0x0000, 0, 0), + MX6_PAD_RGMII2_TXC__ENET2_TX_ER = IOMUX_PAD(0x0564, 0x021C, 1, 0x0000, 0, 0), + MX6_PAD_RGMII2_TXC__SAI1_TX_DATA_0 = IOMUX_PAD(0x0564, 0x021C, 2, 0x0000, 0, 0), + MX6_PAD_RGMII2_TXC__GPIO5_IO_23 = IOMUX_PAD(0x0564, 0x021C, 5, 0x0000, 0, 0), + MX6_PAD_RGMII2_TXC__CSI2_PIXCLK = IOMUX_PAD(0x0564, 0x021C, 6, 0x0000, 0, 0), + MX6_PAD_RGMII2_TXC__SJC_DONE = IOMUX_PAD(0x0564, 0x021C, 7, 0x0000, 0, 0), + MX6_PAD_RGMII2_TXC__VDEC_DEBUG_29 = IOMUX_PAD(0x0564, 0x021C, 8, 0x0000, 0, 0), + MX6_PAD_RGMII2_TXC__PCIE_CTRL_DEBUG_23 = IOMUX_PAD(0x0564, 0x021C, 9, 0x0000, 0, 0), + + MX6_PAD_SD1_CLK__USDHC1_CLK = IOMUX_PAD(0x0568, 0x0220, 0, 0x0000, 0, 0), + MX6_PAD_SD1_CLK__AUDMUX_AUD5_RXFS = IOMUX_PAD(0x0568, 0x0220, 1, 0x0668, 1, 0), + MX6_PAD_SD1_CLK__WDOG2_WDOG_B = IOMUX_PAD(0x0568, 0x0220, 2, 0x0000, 0, 0), + MX6_PAD_SD1_CLK__GPT_CLK = IOMUX_PAD(0x0568, 0x0220, 3, 0x0000, 0, 0), + MX6_PAD_SD1_CLK__WDOG2_WDOG_RST_B_DEB = IOMUX_PAD(0x0568, 0x0220, 4, 0x0000, 0, 0), + MX6_PAD_SD1_CLK__GPIO6_IO_0 = IOMUX_PAD(0x0568, 0x0220, 5, 0x0000, 0, 0), + MX6_PAD_SD1_CLK__ENET2_1588_EVENT1_OUT = IOMUX_PAD(0x0568, 0x0220, 6, 0x0000, 0, 0), + MX6_PAD_SD1_CLK__CCM_OUT1 = IOMUX_PAD(0x0568, 0x0220, 7, 0x0000, 0, 0), + MX6_PAD_SD1_CLK__VADC_ADC_PROC_CLK = IOMUX_PAD(0x0568, 0x0220, 8, 0x0000, 0, 0), + MX6_PAD_SD1_CLK__MMDC_DEBUG_45 = IOMUX_PAD(0x0568, 0x0220, 9, 0x0000, 0, 0), + + MX6_PAD_SD1_CMD__USDHC1_CMD = IOMUX_PAD(0x056C, 0x0224, 0, 0x0000, 0, 0), + MX6_PAD_SD1_CMD__AUDMUX_AUD5_RXC = IOMUX_PAD(0x056C, 0x0224, 1, 0x0664, 1, 0), + MX6_PAD_SD1_CMD__WDOG1_WDOG_B = IOMUX_PAD(0x056C, 0x0224, 2, 0x0000, 0, 0), + MX6_PAD_SD1_CMD__GPT_COMPARE1 = IOMUX_PAD(0x056C, 0x0224, 3, 0x0000, 0, 0), + MX6_PAD_SD1_CMD__WDOG1_WDOG_RST_B_DEB = IOMUX_PAD(0x056C, 0x0224, 4, 0x0000, 0, 0), + MX6_PAD_SD1_CMD__GPIO6_IO_1 = IOMUX_PAD(0x056C, 0x0224, 5, 0x0000, 0, 0), + MX6_PAD_SD1_CMD__ENET2_1588_EVENT1_IN = IOMUX_PAD(0x056C, 0x0224, 6, 0x0000, 0, 0), + MX6_PAD_SD1_CMD__CCM_CLKO1 = IOMUX_PAD(0x056C, 0x0224, 7, 0x0000, 0, 0), + MX6_PAD_SD1_CMD__VADC_EXT_SYSCLK = IOMUX_PAD(0x056C, 0x0224, 8, 0x0000, 0, 0), + MX6_PAD_SD1_CMD__MMDC_DEBUG_46 = IOMUX_PAD(0x056C, 0x0224, 9, 0x0000, 0, 0), + + MX6_PAD_SD1_DATA0__USDHC1_DATA0 = IOMUX_PAD(0x0570, 0x0228, 0, 0x0000, 0, 0), + MX6_PAD_SD1_DATA0__AUDMUX_AUD5_RXD = IOMUX_PAD(0x0570, 0x0228, 1, 0x065C, 1, 0), + MX6_PAD_SD1_DATA0__CAAM_WRAPPER_RNG_OSC_OBS = IOMUX_PAD(0x0570, 0x0228, 2, 0x0000, 0, 0), + MX6_PAD_SD1_DATA0__GPT_CAPTURE1 = IOMUX_PAD(0x0570, 0x0228, 3, 0x0000, 0, 0), + MX6_PAD_SD1_DATA0__UART2_RX = IOMUX_PAD(0x0570, 0x0228, 4, 0x0838, 2, 0), + MX6_PAD_SD1_DATA0__GPIO6_IO_2 = IOMUX_PAD(0x0570, 0x0228, 5, 0x0000, 0, 0), + MX6_PAD_SD1_DATA0__ENET1_1588_EVENT1_IN = IOMUX_PAD(0x0570, 0x0228, 6, 0x0000, 0, 0), + MX6_PAD_SD1_DATA0__CCM_OUT2 = IOMUX_PAD(0x0570, 0x0228, 7, 0x0000, 0, 0), + MX6_PAD_SD1_DATA0__VADC_CLAMP_UP = IOMUX_PAD(0x0570, 0x0228, 8, 0x0000, 0, 0), + MX6_PAD_SD1_DATA0__MMDC_DEBUG_48 = IOMUX_PAD(0x0570, 0x0228, 9, 0x0000, 0, 0), + + MX6_PAD_SD1_DATA1__USDHC1_DATA1 = IOMUX_PAD(0x0574, 0x022C, 0, 0x0000, 0, 0), + MX6_PAD_SD1_DATA1__AUDMUX_AUD5_TXC = IOMUX_PAD(0x0574, 0x022C, 1, 0x066C, 1, 0), + MX6_PAD_SD1_DATA1__PWM4_OUT = IOMUX_PAD(0x0574, 0x022C, 2, 0x0000, 0, 0), + MX6_PAD_SD1_DATA1__GPT_CAPTURE2 = IOMUX_PAD(0x0574, 0x022C, 3, 0x0000, 0, 0), + MX6_PAD_SD1_DATA1__UART2_TX = IOMUX_PAD(0x0574, 0x022C, 4, 0x0838, 3, 0), + MX6_PAD_SD1_DATA1__GPIO6_IO_3 = IOMUX_PAD(0x0574, 0x022C, 5, 0x0000, 0, 0), + MX6_PAD_SD1_DATA1__ENET1_1588_EVENT1_OUT = IOMUX_PAD(0x0574, 0x022C, 6, 0x0000, 0, 0), + MX6_PAD_SD1_DATA1__CCM_CLKO2 = IOMUX_PAD(0x0574, 0x022C, 7, 0x0000, 0, 0), + MX6_PAD_SD1_DATA1__VADC_CLAMP_DOWN = IOMUX_PAD(0x0574, 0x022C, 8, 0x0000, 0, 0), + MX6_PAD_SD1_DATA1__MMDC_DEBUG_47 = IOMUX_PAD(0x0574, 0x022C, 9, 0x0000, 0, 0), + + MX6_PAD_SD1_DATA2__USDHC1_DATA2 = IOMUX_PAD(0x0578, 0x0230, 0, 0x0000, 0, 0), + MX6_PAD_SD1_DATA2__AUDMUX_AUD5_TXFS = IOMUX_PAD(0x0578, 0x0230, 1, 0x0670, 1, 0), + MX6_PAD_SD1_DATA2__PWM3_OUT = IOMUX_PAD(0x0578, 0x0230, 2, 0x0000, 0, 0), + MX6_PAD_SD1_DATA2__GPT_COMPARE2 = IOMUX_PAD(0x0578, 0x0230, 3, 0x0000, 0, 0), + MX6_PAD_SD1_DATA2__UART2_CTS_B = IOMUX_PAD(0x0578, 0x0230, 4, 0x0834, 2, 0), + MX6_PAD_SD1_DATA2__GPIO6_IO_4 = IOMUX_PAD(0x0578, 0x0230, 5, 0x0000, 0, 0), + MX6_PAD_SD1_DATA2__ECSPI4_RDY = IOMUX_PAD(0x0578, 0x0230, 6, 0x0000, 0, 0), + MX6_PAD_SD1_DATA2__CCM_OUT0 = IOMUX_PAD(0x0578, 0x0230, 7, 0x0000, 0, 0), + MX6_PAD_SD1_DATA2__VADC_EXT_PD_N = IOMUX_PAD(0x0578, 0x0230, 8, 0x0000, 0, 0), + + MX6_PAD_SD1_DATA3__USDHC1_DATA3 = IOMUX_PAD(0x057C, 0x0234, 0, 0x0000, 0, 0), + MX6_PAD_SD1_DATA3__AUDMUX_AUD5_TXD = IOMUX_PAD(0x057C, 0x0234, 1, 0x0660, 1, 0), + MX6_PAD_SD1_DATA3__AUDMUX_AUD5_RXD = IOMUX_PAD(0x057C, 0x0234, 2, 0x065C, 2, 0), + MX6_PAD_SD1_DATA3__GPT_COMPARE3 = IOMUX_PAD(0x057C, 0x0234, 3, 0x0000, 0, 0), + MX6_PAD_SD1_DATA3__UART2_RTS_B = IOMUX_PAD(0x057C, 0x0234, 4, 0x0834, 3, 0), + MX6_PAD_SD1_DATA3__GPIO6_IO_5 = IOMUX_PAD(0x057C, 0x0234, 5, 0x0000, 0, 0), + MX6_PAD_SD1_DATA3__ECSPI4_SS1 = IOMUX_PAD(0x057C, 0x0234, 6, 0x0000, 0, 0), + MX6_PAD_SD1_DATA3__CCM_PMIC_RDY = IOMUX_PAD(0x057C, 0x0234, 7, 0x069C, 2, 0), + MX6_PAD_SD1_DATA3__VADC_RST_N = IOMUX_PAD(0x057C, 0x0234, 8, 0x0000, 0, 0), + + MX6_PAD_SD2_CLK__USDHC2_CLK = IOMUX_PAD(0x0580, 0x0238, 0, 0x0000, 0, 0), + MX6_PAD_SD2_CLK__AUDMUX_AUD6_RXFS = IOMUX_PAD(0x0580, 0x0238, 1, 0x0680, 2, 0), + MX6_PAD_SD2_CLK__KPP_COL_5 = IOMUX_PAD(0x0580, 0x0238, 2, 0x07C8, 1, 0), + MX6_PAD_SD2_CLK__ECSPI4_SCLK = IOMUX_PAD(0x0580, 0x0238, 3, 0x0740, 1, 0), + MX6_PAD_SD2_CLK__MLB_SIG = IOMUX_PAD(0x0580, 0x0238, 4, 0x07F0, 2, 0), + MX6_PAD_SD2_CLK__GPIO6_IO_6 = IOMUX_PAD(0x0580, 0x0238, 5, 0x0000, 0, 0), + MX6_PAD_SD2_CLK__MQS_RIGHT = IOMUX_PAD(0x0580, 0x0238, 6, 0x0000, 0, 0), + MX6_PAD_SD2_CLK__WDOG1_WDOG_ANY = IOMUX_PAD(0x0580, 0x0238, 7, 0x0000, 0, 0), + MX6_PAD_SD2_CLK__VADC_CLAMP_CURRENT_5 = IOMUX_PAD(0x0580, 0x0238, 8, 0x0000, 0, 0), + MX6_PAD_SD2_CLK__MMDC_DEBUG_29 = IOMUX_PAD(0x0580, 0x0238, 9, 0x0000, 0, 0), + + MX6_PAD_SD2_CMD__USDHC2_CMD = IOMUX_PAD(0x0584, 0x023C, 0, 0x0000, 0, 0), + MX6_PAD_SD2_CMD__AUDMUX_AUD6_RXC = IOMUX_PAD(0x0584, 0x023C, 1, 0x067C, 2, 0), + MX6_PAD_SD2_CMD__KPP_ROW_5 = IOMUX_PAD(0x0584, 0x023C, 2, 0x07D4, 1, 0), + MX6_PAD_SD2_CMD__ECSPI4_MOSI = IOMUX_PAD(0x0584, 0x023C, 3, 0x0748, 1, 0), + MX6_PAD_SD2_CMD__MLB_CLK = IOMUX_PAD(0x0584, 0x023C, 4, 0x07E8, 2, 0), + MX6_PAD_SD2_CMD__GPIO6_IO_7 = IOMUX_PAD(0x0584, 0x023C, 5, 0x0000, 0, 0), + MX6_PAD_SD2_CMD__MQS_LEFT = IOMUX_PAD(0x0584, 0x023C, 6, 0x0000, 0, 0), + MX6_PAD_SD2_CMD__WDOG3_WDOG_B = IOMUX_PAD(0x0584, 0x023C, 7, 0x0000, 0, 0), + MX6_PAD_SD2_CMD__VADC_CLAMP_CURRENT_4 = IOMUX_PAD(0x0584, 0x023C, 8, 0x0000, 0, 0), + MX6_PAD_SD2_CMD__MMDC_DEBUG_30 = IOMUX_PAD(0x0584, 0x023C, 9, 0x0000, 0, 0), + + MX6_PAD_SD2_DATA0__USDHC2_DATA0 = IOMUX_PAD(0x0588, 0x0240, 0, 0x0000, 0, 0), + MX6_PAD_SD2_DATA0__AUDMUX_AUD6_RXD = IOMUX_PAD(0x0588, 0x0240, 1, 0x0674, 2, 0), + MX6_PAD_SD2_DATA0__KPP_ROW_7 = IOMUX_PAD(0x0588, 0x0240, 2, 0x07DC, 1, 0), + MX6_PAD_SD2_DATA0__PWM1_OUT = IOMUX_PAD(0x0588, 0x0240, 3, 0x0000, 0, 0), + MX6_PAD_SD2_DATA0__I2C4_SDA = IOMUX_PAD(0x0588, 0x0240, 4, 0x07C4, 3, 0), + MX6_PAD_SD2_DATA0__GPIO6_IO_8 = IOMUX_PAD(0x0588, 0x0240, 5, 0x0000, 0, 0), + MX6_PAD_SD2_DATA0__ECSPI4_SS3 = IOMUX_PAD(0x0588, 0x0240, 6, 0x0000, 0, 0), + MX6_PAD_SD2_DATA0__UART4_RX = IOMUX_PAD(0x0588, 0x0240, 7, 0x0848, 4, 0), + MX6_PAD_SD2_DATA0__VADC_CLAMP_CURRENT_0 = IOMUX_PAD(0x0588, 0x0240, 8, 0x0000, 0, 0), + MX6_PAD_SD2_DATA0__MMDC_DEBUG_50 = IOMUX_PAD(0x0588, 0x0240, 9, 0x0000, 0, 0), + + MX6_PAD_SD2_DATA1__USDHC2_DATA1 = IOMUX_PAD(0x058C, 0x0244, 0, 0x0000, 0, 0), + MX6_PAD_SD2_DATA1__AUDMUX_AUD6_TXC = IOMUX_PAD(0x058C, 0x0244, 1, 0x0684, 2, 0), + MX6_PAD_SD2_DATA1__KPP_COL_7 = IOMUX_PAD(0x058C, 0x0244, 2, 0x07D0, 1, 0), + MX6_PAD_SD2_DATA1__PWM2_OUT = IOMUX_PAD(0x058C, 0x0244, 3, 0x0000, 0, 0), + MX6_PAD_SD2_DATA1__I2C4_SCL = IOMUX_PAD(0x058C, 0x0244, 4, 0x07C0, 3, 0), + MX6_PAD_SD2_DATA1__GPIO6_IO_9 = IOMUX_PAD(0x058C, 0x0244, 5, 0x0000, 0, 0), + MX6_PAD_SD2_DATA1__ECSPI4_SS2 = IOMUX_PAD(0x058C, 0x0244, 6, 0x0000, 0, 0), + MX6_PAD_SD2_DATA1__UART4_TX = IOMUX_PAD(0x058C, 0x0244, 7, 0x0848, 5, 0), + MX6_PAD_SD2_DATA1__VADC_CLAMP_CURRENT_1 = IOMUX_PAD(0x058C, 0x0244, 8, 0x0000, 0, 0), + MX6_PAD_SD2_DATA1__MMDC_DEBUG_49 = IOMUX_PAD(0x058C, 0x0244, 9, 0x0000, 0, 0), + + MX6_PAD_SD2_DATA2__USDHC2_DATA2 = IOMUX_PAD(0x0590, 0x0248, 0, 0x0000, 0, 0), + MX6_PAD_SD2_DATA2__AUDMUX_AUD6_TXFS = IOMUX_PAD(0x0590, 0x0248, 1, 0x0688, 2, 0), + MX6_PAD_SD2_DATA2__KPP_ROW_6 = IOMUX_PAD(0x0590, 0x0248, 2, 0x07D8, 1, 0), + MX6_PAD_SD2_DATA2__ECSPI4_SS0 = IOMUX_PAD(0x0590, 0x0248, 3, 0x074C, 1, 0), + MX6_PAD_SD2_DATA2__SDMA_EXT_EVENT_0 = IOMUX_PAD(0x0590, 0x0248, 4, 0x081C, 2, 0), + MX6_PAD_SD2_DATA2__GPIO6_IO_10 = IOMUX_PAD(0x0590, 0x0248, 5, 0x0000, 0, 0), + MX6_PAD_SD2_DATA2__SPDIF_OUT = IOMUX_PAD(0x0590, 0x0248, 6, 0x0000, 0, 0), + MX6_PAD_SD2_DATA2__UART6_RX = IOMUX_PAD(0x0590, 0x0248, 7, 0x0858, 4, 0), + MX6_PAD_SD2_DATA2__VADC_CLAMP_CURRENT_2 = IOMUX_PAD(0x0590, 0x0248, 8, 0x0000, 0, 0), + MX6_PAD_SD2_DATA2__MMDC_DEBUG_32 = IOMUX_PAD(0x0590, 0x0248, 9, 0x0000, 0, 0), + + MX6_PAD_SD2_DATA3__USDHC2_DATA3 = IOMUX_PAD(0x0594, 0x024C, 0, 0x0000, 0, 0), + MX6_PAD_SD2_DATA3__AUDMUX_AUD6_TXD = IOMUX_PAD(0x0594, 0x024C, 1, 0x0678, 2, 0), + MX6_PAD_SD2_DATA3__KPP_COL_6 = IOMUX_PAD(0x0594, 0x024C, 2, 0x07CC, 1, 0), + MX6_PAD_SD2_DATA3__ECSPI4_MISO = IOMUX_PAD(0x0594, 0x024C, 3, 0x0744, 1, 0), + MX6_PAD_SD2_DATA3__MLB_DATA = IOMUX_PAD(0x0594, 0x024C, 4, 0x07EC, 2, 0), + MX6_PAD_SD2_DATA3__GPIO6_IO_11 = IOMUX_PAD(0x0594, 0x024C, 5, 0x0000, 0, 0), + MX6_PAD_SD2_DATA3__SPDIF_IN = IOMUX_PAD(0x0594, 0x024C, 6, 0x0824, 4, 0), + MX6_PAD_SD2_DATA3__UART6_TX = IOMUX_PAD(0x0594, 0x024C, 7, 0x0858, 5, 0), + MX6_PAD_SD2_DATA3__VADC_CLAMP_CURRENT_3 = IOMUX_PAD(0x0594, 0x024C, 8, 0x0000, 0, 0), + MX6_PAD_SD2_DATA3__MMDC_DEBUG_31 = IOMUX_PAD(0x0594, 0x024C, 9, 0x0000, 0, 0), + + MX6_PAD_SD3_CLK__USDHC3_CLK = IOMUX_PAD(0x0598, 0x0250, 0, 0x0000, 0, 0), + MX6_PAD_SD3_CLK__UART4_CTS_B = IOMUX_PAD(0x0598, 0x0250, 1, 0x0844, 0, 0), + MX6_PAD_SD3_CLK__ECSPI4_SCLK = IOMUX_PAD(0x0598, 0x0250, 2, 0x0740, 0, 0), + MX6_PAD_SD3_CLK__AUDMUX_AUD6_RXFS = IOMUX_PAD(0x0598, 0x0250, 3, 0x0680, 0, 0), + MX6_PAD_SD3_CLK__LCDIF2_VSYNC = IOMUX_PAD(0x0598, 0x0250, 4, 0x0000, 0, 0), + MX6_PAD_SD3_CLK__GPIO7_IO_0 = IOMUX_PAD(0x0598, 0x0250, 5, 0x0000, 0, 0), + MX6_PAD_SD3_CLK__LCDIF2_BUSY = IOMUX_PAD(0x0598, 0x0250, 6, 0x07E4, 0, 0), + MX6_PAD_SD3_CLK__TPSMP_HDATA_29 = IOMUX_PAD(0x0598, 0x0250, 7, 0x0000, 0, 0), + MX6_PAD_SD3_CLK__SDMA_DEBUG_EVENT_CHANNEL_5 = IOMUX_PAD(0x0598, 0x0250, 9, 0x0000, 0, 0), + + MX6_PAD_SD3_CMD__USDHC3_CMD = IOMUX_PAD(0x059C, 0x0254, 0, 0x0000, 0, 0), + MX6_PAD_SD3_CMD__UART4_TX = IOMUX_PAD(0x059C, 0x0254, 1, 0x0848, 0, 0), + MX6_PAD_SD3_CMD__ECSPI4_MOSI = IOMUX_PAD(0x059C, 0x0254, 2, 0x0748, 0, 0), + MX6_PAD_SD3_CMD__AUDMUX_AUD6_RXC = IOMUX_PAD(0x059C, 0x0254, 3, 0x067C, 0, 0), + MX6_PAD_SD3_CMD__LCDIF2_HSYNC = IOMUX_PAD(0x059C, 0x0254, 4, 0x07E4, 1, 0), + MX6_PAD_SD3_CMD__GPIO7_IO_1 = IOMUX_PAD(0x059C, 0x0254, 5, 0x0000, 0, 0), + MX6_PAD_SD3_CMD__LCDIF2_RS = IOMUX_PAD(0x059C, 0x0254, 6, 0x0000, 0, 0), + MX6_PAD_SD3_CMD__TPSMP_HDATA_28 = IOMUX_PAD(0x059C, 0x0254, 7, 0x0000, 0, 0), + MX6_PAD_SD3_CMD__SDMA_DEBUG_EVENT_CHANNEL_4 = IOMUX_PAD(0x059C, 0x0254, 9, 0x0000, 0, 0), + + MX6_PAD_SD3_DATA0__USDHC3_DATA0 = IOMUX_PAD(0x05A0, 0x0258, 0, 0x0000, 0, 0), + MX6_PAD_SD3_DATA0__I2C4_SCL = IOMUX_PAD(0x05A0, 0x0258, 1, 0x07C0, 0, 0), + MX6_PAD_SD3_DATA0__ECSPI2_SS1 = IOMUX_PAD(0x05A0, 0x0258, 2, 0x0000, 0, 0), + MX6_PAD_SD3_DATA0__AUDMUX_AUD6_RXD = IOMUX_PAD(0x05A0, 0x0258, 3, 0x0674, 0, 0), + MX6_PAD_SD3_DATA0__LCDIF2_DATA_1 = IOMUX_PAD(0x05A0, 0x0258, 4, 0x0000, 0, 0), + MX6_PAD_SD3_DATA0__GPIO7_IO_2 = IOMUX_PAD(0x05A0, 0x0258, 5, 0x0000, 0, 0), + MX6_PAD_SD3_DATA0__DCIC1_OUT = IOMUX_PAD(0x05A0, 0x0258, 6, 0x0000, 0, 0), + MX6_PAD_SD3_DATA0__TPSMP_HDATA_30 = IOMUX_PAD(0x05A0, 0x0258, 7, 0x0000, 0, 0), + MX6_PAD_SD3_DATA0__GPU_DEBUG_0 = IOMUX_PAD(0x05A0, 0x0258, 8, 0x0000, 0, 0), + MX6_PAD_SD3_DATA0__SDMA_DEBUG_EVT_CHN_LINES_0 = IOMUX_PAD(0x05A0, 0x0258, 9, 0x0000, 0, 0), + + MX6_PAD_SD3_DATA1__USDHC3_DATA1 = IOMUX_PAD(0x05A4, 0x025C, 0, 0x0000, 0, 0), + MX6_PAD_SD3_DATA1__I2C4_SDA = IOMUX_PAD(0x05A4, 0x025C, 1, 0x07C4, 0, 0), + MX6_PAD_SD3_DATA1__ECSPI2_SS2 = IOMUX_PAD(0x05A4, 0x025C, 2, 0x0000, 0, 0), + MX6_PAD_SD3_DATA1__AUDMUX_AUD6_TXC = IOMUX_PAD(0x05A4, 0x025C, 3, 0x0684, 0, 0), + MX6_PAD_SD3_DATA1__LCDIF2_DATA_0 = IOMUX_PAD(0x05A4, 0x025C, 4, 0x0000, 0, 0), + MX6_PAD_SD3_DATA1__GPIO7_IO_3 = IOMUX_PAD(0x05A4, 0x025C, 5, 0x0000, 0, 0), + MX6_PAD_SD3_DATA1__DCIC2_OUT = IOMUX_PAD(0x05A4, 0x025C, 6, 0x0000, 0, 0), + MX6_PAD_SD3_DATA1__TPSMP_HDATA_31 = IOMUX_PAD(0x05A4, 0x025C, 7, 0x0000, 0, 0), + MX6_PAD_SD3_DATA1__GPU_DEBUG_1 = IOMUX_PAD(0x05A4, 0x025C, 8, 0x0000, 0, 0), + MX6_PAD_SD3_DATA1__SDMA_DEBUG_EVT_CHN_LINES_1 = IOMUX_PAD(0x05A4, 0x025C, 9, 0x0000, 0, 0), + + MX6_PAD_SD3_DATA2__USDHC3_DATA2 = IOMUX_PAD(0x05A8, 0x0260, 0, 0x0000, 0, 0), + MX6_PAD_SD3_DATA2__UART4_RTS_B = IOMUX_PAD(0x05A8, 0x0260, 1, 0x0844, 1, 0), + MX6_PAD_SD3_DATA2__ECSPI4_SS0 = IOMUX_PAD(0x05A8, 0x0260, 2, 0x074C, 0, 0), + MX6_PAD_SD3_DATA2__AUDMUX_AUD6_TXFS = IOMUX_PAD(0x05A8, 0x0260, 3, 0x0688, 0, 0), + MX6_PAD_SD3_DATA2__LCDIF2_CLK = IOMUX_PAD(0x05A8, 0x0260, 4, 0x0000, 0, 0), + MX6_PAD_SD3_DATA2__GPIO7_IO_4 = IOMUX_PAD(0x05A8, 0x0260, 5, 0x0000, 0, 0), + MX6_PAD_SD3_DATA2__LCDIF2_WR_RWN = IOMUX_PAD(0x05A8, 0x0260, 6, 0x0000, 0, 0), + MX6_PAD_SD3_DATA2__TPSMP_HDATA_26 = IOMUX_PAD(0x05A8, 0x0260, 7, 0x0000, 0, 0), + MX6_PAD_SD3_DATA2__GPU_DEBUG_2 = IOMUX_PAD(0x05A8, 0x0260, 8, 0x0000, 0, 0), + MX6_PAD_SD3_DATA2__SDMA_DEBUG_EVENT_CHANNEL_2 = IOMUX_PAD(0x05A8, 0x0260, 9, 0x0000, 0, 0), + + MX6_PAD_SD3_DATA3__USDHC3_DATA3 = IOMUX_PAD(0x05AC, 0x0264, 0, 0x0000, 0, 0), + MX6_PAD_SD3_DATA3__UART4_RX = IOMUX_PAD(0x05AC, 0x0264, 1, 0x0848, 1, 0), + MX6_PAD_SD3_DATA3__ECSPI4_MISO = IOMUX_PAD(0x05AC, 0x0264, 2, 0x0744, 0, 0), + MX6_PAD_SD3_DATA3__AUDMUX_AUD6_TXD = IOMUX_PAD(0x05AC, 0x0264, 3, 0x0678, 0, 0), + MX6_PAD_SD3_DATA3__LCDIF2_ENABLE = IOMUX_PAD(0x05AC, 0x0264, 4, 0x0000, 0, 0), + MX6_PAD_SD3_DATA3__GPIO7_IO_5 = IOMUX_PAD(0x05AC, 0x0264, 5, 0x0000, 0, 0), + MX6_PAD_SD3_DATA3__LCDIF2_RD_E = IOMUX_PAD(0x05AC, 0x0264, 6, 0x0000, 0, 0), + MX6_PAD_SD3_DATA3__TPSMP_HDATA_27 = IOMUX_PAD(0x05AC, 0x0264, 7, 0x0000, 0, 0), + MX6_PAD_SD3_DATA3__GPU_DEBUG_3 = IOMUX_PAD(0x05AC, 0x0264, 8, 0x0000, 0, 0), + MX6_PAD_SD3_DATA3__SDMA_DEBUG_EVENT_CHANNEL_3 = IOMUX_PAD(0x05AC, 0x0264, 9, 0x0000, 0, 0), + + MX6_PAD_SD3_DATA4__USDHC3_DATA4 = IOMUX_PAD(0x05B0, 0x0268, 0, 0x0000, 0, 0), + MX6_PAD_SD3_DATA4__CAN2_RX = IOMUX_PAD(0x05B0, 0x0268, 1, 0x0690, 0, 0), + MX6_PAD_SD3_DATA4__CANFD_RX2 = IOMUX_PAD(0x05B0, 0x0268, 2, 0x0698, 0, 0), + MX6_PAD_SD3_DATA4__UART3_RX = IOMUX_PAD(0x05B0, 0x0268, 3, 0x0840, 2, 0), + MX6_PAD_SD3_DATA4__LCDIF2_DATA_3 = IOMUX_PAD(0x05B0, 0x0268, 4, 0x0000, 0, 0), + MX6_PAD_SD3_DATA4__GPIO7_IO_6 = IOMUX_PAD(0x05B0, 0x0268, 5, 0x0000, 0, 0), + MX6_PAD_SD3_DATA4__ENET2_1588_EVENT0_IN = IOMUX_PAD(0x05B0, 0x0268, 6, 0x0000, 0, 0), + MX6_PAD_SD3_DATA4__TPSMP_HTRANS_1 = IOMUX_PAD(0x05B0, 0x0268, 7, 0x0000, 0, 0), + MX6_PAD_SD3_DATA4__GPU_DEBUG_4 = IOMUX_PAD(0x05B0, 0x0268, 8, 0x0000, 0, 0), + MX6_PAD_SD3_DATA4__SDMA_DEBUG_BUS_DEVICE_0 = IOMUX_PAD(0x05B0, 0x0268, 9, 0x0000, 0, 0), + + MX6_PAD_SD3_DATA5__USDHC3_DATA5 = IOMUX_PAD(0x05B4, 0x026C, 0, 0x0000, 0, 0), + MX6_PAD_SD3_DATA5__CAN1_TX = IOMUX_PAD(0x05B4, 0x026C, 1, 0x0000, 0, 0), + MX6_PAD_SD3_DATA5__CANFD_TX1 = IOMUX_PAD(0x05B4, 0x026C, 2, 0x0000, 0, 0), + MX6_PAD_SD3_DATA5__UART3_TX = IOMUX_PAD(0x05B4, 0x026C, 3, 0x0840, 3, 0), + MX6_PAD_SD3_DATA5__LCDIF2_DATA_2 = IOMUX_PAD(0x05B4, 0x026C, 4, 0x0000, 0, 0), + MX6_PAD_SD3_DATA5__GPIO7_IO_7 = IOMUX_PAD(0x05B4, 0x026C, 5, 0x0000, 0, 0), + MX6_PAD_SD3_DATA5__ENET2_1588_EVENT0_OUT = IOMUX_PAD(0x05B4, 0x026C, 6, 0x0000, 0, 0), + MX6_PAD_SD3_DATA5__SIM_M_HWRITE = IOMUX_PAD(0x05B4, 0x026C, 7, 0x0000, 0, 0), + MX6_PAD_SD3_DATA5__GPU_DEBUG_5 = IOMUX_PAD(0x05B4, 0x026C, 8, 0x0000, 0, 0), + MX6_PAD_SD3_DATA5__SDMA_DEBUG_BUS_DEVICE_1 = IOMUX_PAD(0x05B4, 0x026C, 9, 0x0000, 0, 0), + + MX6_PAD_SD3_DATA6__USDHC3_DATA6 = IOMUX_PAD(0x05B8, 0x0270, 0, 0x0000, 0, 0), + MX6_PAD_SD3_DATA6__CAN2_TX = IOMUX_PAD(0x05B8, 0x0270, 1, 0x0000, 0, 0), + MX6_PAD_SD3_DATA6__CANFD_TX2 = IOMUX_PAD(0x05B8, 0x0270, 2, 0x0000, 0, 0), + MX6_PAD_SD3_DATA6__UART3_RTS_B = IOMUX_PAD(0x05B8, 0x0270, 3, 0x083C, 2, 0), + MX6_PAD_SD3_DATA6__LCDIF2_DATA_4 = IOMUX_PAD(0x05B8, 0x0270, 4, 0x0000, 0, 0), + MX6_PAD_SD3_DATA6__GPIO7_IO_8 = IOMUX_PAD(0x05B8, 0x0270, 5, 0x0000, 0, 0), + MX6_PAD_SD3_DATA6__ENET1_1588_EVENT0_OUT = IOMUX_PAD(0x05B8, 0x0270, 6, 0x0000, 0, 0), + MX6_PAD_SD3_DATA6__TPSMP_HTRANS_0 = IOMUX_PAD(0x05B8, 0x0270, 7, 0x0000, 0, 0), + MX6_PAD_SD3_DATA6__GPU_DEBUG_7 = IOMUX_PAD(0x05B8, 0x0270, 8, 0x0000, 0, 0), + MX6_PAD_SD3_DATA6__SDMA_DEBUG_EVT_CHN_LINES_7 = IOMUX_PAD(0x05B8, 0x0270, 9, 0x0000, 0, 0), + + MX6_PAD_SD3_DATA7__USDHC3_DATA7 = IOMUX_PAD(0x05BC, 0x0274, 0, 0x0000, 0, 0), + MX6_PAD_SD3_DATA7__CAN1_RX = IOMUX_PAD(0x05BC, 0x0274, 1, 0x068C, 0, 0), + MX6_PAD_SD3_DATA7__CANFD_RX1 = IOMUX_PAD(0x05BC, 0x0274, 2, 0x0694, 0, 0), + MX6_PAD_SD3_DATA7__UART3_CTS_B = IOMUX_PAD(0x05BC, 0x0274, 3, 0x083C, 3, 0), + MX6_PAD_SD3_DATA7__LCDIF2_DATA_5 = IOMUX_PAD(0x05BC, 0x0274, 4, 0x0000, 0, 0), + MX6_PAD_SD3_DATA7__GPIO7_IO_9 = IOMUX_PAD(0x05BC, 0x0274, 5, 0x0000, 0, 0), + MX6_PAD_SD3_DATA7__ENET1_1588_EVENT0_IN = IOMUX_PAD(0x05BC, 0x0274, 6, 0x0000, 0, 0), + MX6_PAD_SD3_DATA7__TPSMP_HDATA_DIR = IOMUX_PAD(0x05BC, 0x0274, 7, 0x0000, 0, 0), + MX6_PAD_SD3_DATA7__GPU_DEBUG_6 = IOMUX_PAD(0x05BC, 0x0274, 8, 0x0000, 0, 0), + MX6_PAD_SD3_DATA7__SDMA_DEBUG_EVT_CHN_LINES_2 = IOMUX_PAD(0x05BC, 0x0274, 9, 0x0000, 0, 0), + + MX6_PAD_SD4_CLK__USDHC4_CLK = IOMUX_PAD(0x05C0, 0x0278, 0, 0x0000, 0, 0), + MX6_PAD_SD4_CLK__RAWNAND_DATA15 = IOMUX_PAD(0x05C0, 0x0278, 1, 0x0000, 0, 0), + MX6_PAD_SD4_CLK__ECSPI2_MISO = IOMUX_PAD(0x05C0, 0x0278, 2, 0x0724, 1, 0), + MX6_PAD_SD4_CLK__AUDMUX_AUD3_RXFS = IOMUX_PAD(0x05C0, 0x0278, 3, 0x0638, 0, 0), + MX6_PAD_SD4_CLK__LCDIF2_DATA_13 = IOMUX_PAD(0x05C0, 0x0278, 4, 0x0000, 0, 0), + MX6_PAD_SD4_CLK__GPIO6_IO_12 = IOMUX_PAD(0x05C0, 0x0278, 5, 0x0000, 0, 0), + MX6_PAD_SD4_CLK__ECSPI3_SS2 = IOMUX_PAD(0x05C0, 0x0278, 6, 0x0000, 0, 0), + MX6_PAD_SD4_CLK__TPSMP_HDATA_20 = IOMUX_PAD(0x05C0, 0x0278, 7, 0x0000, 0, 0), + MX6_PAD_SD4_CLK__VDEC_DEBUG_12 = IOMUX_PAD(0x05C0, 0x0278, 8, 0x0000, 0, 0), + MX6_PAD_SD4_CLK__SDMA_DEBUG_EVENT_CHANNEL_SEL = IOMUX_PAD(0x05C0, 0x0278, 9, 0x0000, 0, 0), + + MX6_PAD_SD4_CMD__USDHC4_CMD = IOMUX_PAD(0x05C4, 0x027C, 0, 0x0000, 0, 0), + MX6_PAD_SD4_CMD__RAWNAND_DATA14 = IOMUX_PAD(0x05C4, 0x027C, 1, 0x0000, 0, 0), + MX6_PAD_SD4_CMD__ECSPI2_MOSI = IOMUX_PAD(0x05C4, 0x027C, 2, 0x0728, 1, 0), + MX6_PAD_SD4_CMD__AUDMUX_AUD3_RXC = IOMUX_PAD(0x05C4, 0x027C, 3, 0x0634, 0, 0), + MX6_PAD_SD4_CMD__LCDIF2_DATA_14 = IOMUX_PAD(0x05C4, 0x027C, 4, 0x0000, 0, 0), + MX6_PAD_SD4_CMD__GPIO6_IO_13 = IOMUX_PAD(0x05C4, 0x027C, 5, 0x0000, 0, 0), + MX6_PAD_SD4_CMD__ECSPI3_SS1 = IOMUX_PAD(0x05C4, 0x027C, 6, 0x0000, 0, 0), + MX6_PAD_SD4_CMD__TPSMP_HDATA_19 = IOMUX_PAD(0x05C4, 0x027C, 7, 0x0000, 0, 0), + MX6_PAD_SD4_CMD__VDEC_DEBUG_11 = IOMUX_PAD(0x05C4, 0x027C, 8, 0x0000, 0, 0), + MX6_PAD_SD4_CMD__SDMA_DEBUG_CORE_RUN = IOMUX_PAD(0x05C4, 0x027C, 9, 0x0000, 0, 0), + + MX6_PAD_SD4_DATA0__USDHC4_DATA0 = IOMUX_PAD(0x05C8, 0x0280, 0, 0x0000, 0, 0), + MX6_PAD_SD4_DATA0__RAWNAND_DATA10 = IOMUX_PAD(0x05C8, 0x0280, 1, 0x0000, 0, 0), + MX6_PAD_SD4_DATA0__ECSPI2_SS0 = IOMUX_PAD(0x05C8, 0x0280, 2, 0x072C, 1, 0), + MX6_PAD_SD4_DATA0__AUDMUX_AUD3_RXD = IOMUX_PAD(0x05C8, 0x0280, 3, 0x062C, 0, 0), + MX6_PAD_SD4_DATA0__LCDIF2_DATA_12 = IOMUX_PAD(0x05C8, 0x0280, 4, 0x0000, 0, 0), + MX6_PAD_SD4_DATA0__GPIO6_IO_14 = IOMUX_PAD(0x05C8, 0x0280, 5, 0x0000, 0, 0), + MX6_PAD_SD4_DATA0__ECSPI3_SS3 = IOMUX_PAD(0x05C8, 0x0280, 6, 0x0000, 0, 0), + MX6_PAD_SD4_DATA0__TPSMP_HDATA_21 = IOMUX_PAD(0x05C8, 0x0280, 7, 0x0000, 0, 0), + MX6_PAD_SD4_DATA0__VDEC_DEBUG_13 = IOMUX_PAD(0x05C8, 0x0280, 8, 0x0000, 0, 0), + MX6_PAD_SD4_DATA0__SDMA_DEBUG_MODE = IOMUX_PAD(0x05C8, 0x0280, 9, 0x0000, 0, 0), + + MX6_PAD_SD4_DATA1__USDHC4_DATA1 = IOMUX_PAD(0x05CC, 0x0284, 0, 0x0000, 0, 0), + MX6_PAD_SD4_DATA1__RAWNAND_DATA11 = IOMUX_PAD(0x05CC, 0x0284, 1, 0x0000, 0, 0), + MX6_PAD_SD4_DATA1__ECSPI2_SCLK = IOMUX_PAD(0x05CC, 0x0284, 2, 0x0720, 1, 0), + MX6_PAD_SD4_DATA1__AUDMUX_AUD3_TXC = IOMUX_PAD(0x05CC, 0x0284, 3, 0x063C, 0, 0), + MX6_PAD_SD4_DATA1__LCDIF2_DATA_11 = IOMUX_PAD(0x05CC, 0x0284, 4, 0x0000, 0, 0), + MX6_PAD_SD4_DATA1__GPIO6_IO_15 = IOMUX_PAD(0x05CC, 0x0284, 5, 0x0000, 0, 0), + MX6_PAD_SD4_DATA1__ECSPI3_RDY = IOMUX_PAD(0x05CC, 0x0284, 6, 0x0000, 0, 0), + MX6_PAD_SD4_DATA1__TPSMP_HDATA_22 = IOMUX_PAD(0x05CC, 0x0284, 7, 0x0000, 0, 0), + MX6_PAD_SD4_DATA1__VDEC_DEBUG_14 = IOMUX_PAD(0x05CC, 0x0284, 8, 0x0000, 0, 0), + MX6_PAD_SD4_DATA1__SDMA_DEBUG_BUS_ERROR = IOMUX_PAD(0x05CC, 0x0284, 9, 0x0000, 0, 0), + + MX6_PAD_SD4_DATA2__USDHC4_DATA2 = IOMUX_PAD(0x05D0, 0x0288, 0, 0x0000, 0, 0), + MX6_PAD_SD4_DATA2__RAWNAND_DATA12 = IOMUX_PAD(0x05D0, 0x0288, 1, 0x0000, 0, 0), + MX6_PAD_SD4_DATA2__I2C2_SDA = IOMUX_PAD(0x05D0, 0x0288, 2, 0x07B4, 0, 0), + MX6_PAD_SD4_DATA2__AUDMUX_AUD3_TXFS = IOMUX_PAD(0x05D0, 0x0288, 3, 0x0640, 0, 0), + MX6_PAD_SD4_DATA2__LCDIF2_DATA_10 = IOMUX_PAD(0x05D0, 0x0288, 4, 0x0000, 0, 0), + MX6_PAD_SD4_DATA2__GPIO6_IO_16 = IOMUX_PAD(0x05D0, 0x0288, 5, 0x0000, 0, 0), + MX6_PAD_SD4_DATA2__ECSPI2_SS3 = IOMUX_PAD(0x05D0, 0x0288, 6, 0x0000, 0, 0), + MX6_PAD_SD4_DATA2__TPSMP_HDATA_23 = IOMUX_PAD(0x05D0, 0x0288, 7, 0x0000, 0, 0), + MX6_PAD_SD4_DATA2__VDEC_DEBUG_15 = IOMUX_PAD(0x05D0, 0x0288, 8, 0x0000, 0, 0), + MX6_PAD_SD4_DATA2__SDMA_DEBUG_BUS_RWB = IOMUX_PAD(0x05D0, 0x0288, 9, 0x0000, 0, 0), + + MX6_PAD_SD4_DATA3__USDHC4_DATA3 = IOMUX_PAD(0x05D4, 0x028C, 0, 0x0000, 0, 0), + MX6_PAD_SD4_DATA3__RAWNAND_DATA13 = IOMUX_PAD(0x05D4, 0x028C, 1, 0x0000, 0, 0), + MX6_PAD_SD4_DATA3__I2C2_SCL = IOMUX_PAD(0x05D4, 0x028C, 2, 0x07B0, 0, 0), + MX6_PAD_SD4_DATA3__AUDMUX_AUD3_TXD = IOMUX_PAD(0x05D4, 0x028C, 3, 0x0630, 0, 0), + MX6_PAD_SD4_DATA3__LCDIF2_DATA_9 = IOMUX_PAD(0x05D4, 0x028C, 4, 0x0000, 0, 0), + MX6_PAD_SD4_DATA3__GPIO6_IO_17 = IOMUX_PAD(0x05D4, 0x028C, 5, 0x0000, 0, 0), + MX6_PAD_SD4_DATA3__ECSPI2_RDY = IOMUX_PAD(0x05D4, 0x028C, 6, 0x0000, 0, 0), + MX6_PAD_SD4_DATA3__TPSMP_HDATA_24 = IOMUX_PAD(0x05D4, 0x028C, 7, 0x0000, 0, 0), + MX6_PAD_SD4_DATA3__VDEC_DEBUG_16 = IOMUX_PAD(0x05D4, 0x028C, 8, 0x0000, 0, 0), + MX6_PAD_SD4_DATA3__SDMA_DEBUG_MATCHED_DMBUS = IOMUX_PAD(0x05D4, 0x028C, 9, 0x0000, 0, 0), + + MX6_PAD_SD4_DATA4__USDHC4_DATA4 = IOMUX_PAD(0x05D8, 0x0290, 0, 0x0000, 0, 0), + MX6_PAD_SD4_DATA4__RAWNAND_DATA09 = IOMUX_PAD(0x05D8, 0x0290, 1, 0x0000, 0, 0), + MX6_PAD_SD4_DATA4__UART5_RX = IOMUX_PAD(0x05D8, 0x0290, 2, 0x0850, 0, 0), + MX6_PAD_SD4_DATA4__ECSPI3_SCLK = IOMUX_PAD(0x05D8, 0x0290, 3, 0x0730, 0, 0), + MX6_PAD_SD4_DATA4__LCDIF2_DATA_8 = IOMUX_PAD(0x05D8, 0x0290, 4, 0x0000, 0, 0), + MX6_PAD_SD4_DATA4__GPIO6_IO_18 = IOMUX_PAD(0x05D8, 0x0290, 5, 0x0000, 0, 0), + MX6_PAD_SD4_DATA4__SPDIF_OUT = IOMUX_PAD(0x05D8, 0x0290, 6, 0x0000, 0, 0), + MX6_PAD_SD4_DATA4__TPSMP_HDATA_16 = IOMUX_PAD(0x05D8, 0x0290, 7, 0x0000, 0, 0), + MX6_PAD_SD4_DATA4__USB_OTG_HOST_MODE = IOMUX_PAD(0x05D8, 0x0290, 8, 0x0000, 0, 0), + MX6_PAD_SD4_DATA4__SDMA_DEBUG_RTBUFFER_WRITE = IOMUX_PAD(0x05D8, 0x0290, 9, 0x0000, 0, 0), + + MX6_PAD_SD4_DATA5__USDHC4_DATA5 = IOMUX_PAD(0x05DC, 0x0294, 0, 0x0000, 0, 0), + MX6_PAD_SD4_DATA5__RAWNAND_CE2_B = IOMUX_PAD(0x05DC, 0x0294, 1, 0x0000, 0, 0), + MX6_PAD_SD4_DATA5__UART5_TX = IOMUX_PAD(0x05DC, 0x0294, 2, 0x0850, 1, 0), + MX6_PAD_SD4_DATA5__ECSPI3_MOSI = IOMUX_PAD(0x05DC, 0x0294, 3, 0x0738, 0, 0), + MX6_PAD_SD4_DATA5__LCDIF2_DATA_7 = IOMUX_PAD(0x05DC, 0x0294, 4, 0x0000, 0, 0), + MX6_PAD_SD4_DATA5__GPIO6_IO_19 = IOMUX_PAD(0x05DC, 0x0294, 5, 0x0000, 0, 0), + MX6_PAD_SD4_DATA5__SPDIF_IN = IOMUX_PAD(0x05DC, 0x0294, 6, 0x0824, 0, 0), + MX6_PAD_SD4_DATA5__TPSMP_HDATA_17 = IOMUX_PAD(0x05DC, 0x0294, 7, 0x0000, 0, 0), + MX6_PAD_SD4_DATA5__VDEC_DEBUG_9 = IOMUX_PAD(0x05DC, 0x0294, 8, 0x0000, 0, 0), + MX6_PAD_SD4_DATA5__SDMA_DEBUG_EVENT_CHANNEL_0 = IOMUX_PAD(0x05DC, 0x0294, 9, 0x0000, 0, 0), + + MX6_PAD_SD4_DATA6__USDHC4_DATA6 = IOMUX_PAD(0x05E0, 0x0298, 0, 0x0000, 0, 0), + MX6_PAD_SD4_DATA6__RAWNAND_CE3_B = IOMUX_PAD(0x05E0, 0x0298, 1, 0x0000, 0, 0), + MX6_PAD_SD4_DATA6__UART5_RTS_B = IOMUX_PAD(0x05E0, 0x0298, 2, 0x084C, 0, 0), + MX6_PAD_SD4_DATA6__ECSPI3_MISO = IOMUX_PAD(0x05E0, 0x0298, 3, 0x0734, 0, 0), + MX6_PAD_SD4_DATA6__LCDIF2_DATA_6 = IOMUX_PAD(0x05E0, 0x0298, 4, 0x0000, 0, 0), + MX6_PAD_SD4_DATA6__GPIO6_IO_20 = IOMUX_PAD(0x05E0, 0x0298, 5, 0x0000, 0, 0), + MX6_PAD_SD4_DATA6__USDHC4_WP = IOMUX_PAD(0x05E0, 0x0298, 6, 0x0878, 0, 0), + MX6_PAD_SD4_DATA6__TPSMP_HDATA_18 = IOMUX_PAD(0x05E0, 0x0298, 7, 0x0000, 0, 0), + MX6_PAD_SD4_DATA6__VDEC_DEBUG_10 = IOMUX_PAD(0x05E0, 0x0298, 8, 0x0000, 0, 0), + MX6_PAD_SD4_DATA6__SDMA_DEBUG_EVENT_CHANNEL_1 = IOMUX_PAD(0x05E0, 0x0298, 9, 0x0000, 0, 0), + + MX6_PAD_SD4_DATA7__USDHC4_DATA7 = IOMUX_PAD(0x05E4, 0x029C, 0, 0x0000, 0, 0), + MX6_PAD_SD4_DATA7__RAWNAND_DATA08 = IOMUX_PAD(0x05E4, 0x029C, 1, 0x0000, 0, 0), + MX6_PAD_SD4_DATA7__UART5_CTS_B = IOMUX_PAD(0x05E4, 0x029C, 2, 0x084C, 1, 0), + MX6_PAD_SD4_DATA7__ECSPI3_SS0 = IOMUX_PAD(0x05E4, 0x029C, 3, 0x073C, 0, 0), + MX6_PAD_SD4_DATA7__LCDIF2_DATA_15 = IOMUX_PAD(0x05E4, 0x029C, 4, 0x0000, 0, 0), + MX6_PAD_SD4_DATA7__GPIO6_IO_21 = IOMUX_PAD(0x05E4, 0x029C, 5, 0x0000, 0, 0), + MX6_PAD_SD4_DATA7__USDHC4_CD_B = IOMUX_PAD(0x05E4, 0x029C, 6, 0x0874, 0, 0), + MX6_PAD_SD4_DATA7__TPSMP_HDATA_15 = IOMUX_PAD(0x05E4, 0x029C, 7, 0x0000, 0, 0), + MX6_PAD_SD4_DATA7__USB_OTG_PWR_WAKE = IOMUX_PAD(0x05E4, 0x029C, 8, 0x0000, 0, 0), + MX6_PAD_SD4_DATA7__SDMA_DEBUG_YIELD = IOMUX_PAD(0x05E4, 0x029C, 9, 0x0000, 0, 0), + + MX6_PAD_SD4_RESET_B__USDHC4_RESET_B = IOMUX_PAD(0x05E8, 0x02A0, 0, 0x0000, 0, 0), + MX6_PAD_SD4_RESET_B__RAWNAND_DQS = IOMUX_PAD(0x05E8, 0x02A0, 1, 0x0000, 0, 0), + MX6_PAD_SD4_RESET_B__USDHC4_RESET = IOMUX_PAD(0x05E8, 0x02A0, 2, 0x0000, 0, 0), + MX6_PAD_SD4_RESET_B__AUDMUX_MCLK = IOMUX_PAD(0x05E8, 0x02A0, 3, 0x0000, 0, 0), + MX6_PAD_SD4_RESET_B__LCDIF2_RESET = IOMUX_PAD(0x05E8, 0x02A0, 4, 0x0000, 0, 0), + MX6_PAD_SD4_RESET_B__GPIO6_IO_22 = IOMUX_PAD(0x05E8, 0x02A0, 5, 0x0000, 0, 0), + MX6_PAD_SD4_RESET_B__LCDIF2_CS = IOMUX_PAD(0x05E8, 0x02A0, 6, 0x0000, 0, 0), + MX6_PAD_SD4_RESET_B__TPSMP_HDATA_25 = IOMUX_PAD(0x05E8, 0x02A0, 7, 0x0000, 0, 0), + MX6_PAD_SD4_RESET_B__VDEC_DEBUG_17 = IOMUX_PAD(0x05E8, 0x02A0, 8, 0x0000, 0, 0), + MX6_PAD_SD4_RESET_B__SDMA_DEBUG_BUS_DEVICE_2 = IOMUX_PAD(0x05E8, 0x02A0, 9, 0x0000, 0, 0), + + MX6_PAD_USB_H_DATA__USB_H_DATA = IOMUX_PAD(0x05EC, 0x02A4, 0, 0x0000, 0, 0), + MX6_PAD_USB_H_DATA__PWM2_OUT = IOMUX_PAD(0x05EC, 0x02A4, 1, 0x0000, 0, 0), + MX6_PAD_USB_H_DATA__ANATOP_24M_OUT = IOMUX_PAD(0x05EC, 0x02A4, 2, 0x0000, 0, 0), + MX6_PAD_USB_H_DATA__I2C4_SDA = IOMUX_PAD(0x05EC, 0x02A4, 3, 0x07C4, 1, 0), + MX6_PAD_USB_H_DATA__WDOG3_WDOG_B = IOMUX_PAD(0x05EC, 0x02A4, 4, 0x0000, 0, 0), + MX6_PAD_USB_H_DATA__GPIO7_IO_10 = IOMUX_PAD(0x05EC, 0x02A4, 5, 0x0000, 0, 0), + + MX6_PAD_USB_H_STROBE__USB_H_STROBE = IOMUX_PAD(0x05F0, 0x02A8, 0, 0x0000, 0, 0), + MX6_PAD_USB_H_STROBE__PWM1_OUT = IOMUX_PAD(0x05F0, 0x02A8, 1, 0x0000, 0, 0), + MX6_PAD_USB_H_STROBE__ANATOP_32K_OUT = IOMUX_PAD(0x05F0, 0x02A8, 2, 0x0000, 0, 0), + MX6_PAD_USB_H_STROBE__I2C4_SCL = IOMUX_PAD(0x05F0, 0x02A8, 3, 0x07C0, 1, 0), + MX6_PAD_USB_H_STROBE__WDOG3_WDOG_RST_B_DEB = IOMUX_PAD(0x05F0, 0x02A8, 4, 0x0000, 0, 0), + MX6_PAD_USB_H_STROBE__GPIO7_IO_11 = IOMUX_PAD(0x05F0, 0x02A8, 5, 0x0000, 0, 0), +}; +#endif /* __ASM_ARCH_MX6_ MX6_PINS_H__ */ diff --git a/arch/arm/include/asm/arch-mx6/sys_proto.h b/arch/arm/include/asm/arch-mx6/sys_proto.h index 42d30f5..306d699 100644 --- a/arch/arm/include/asm/arch-mx6/sys_proto.h +++ b/arch/arm/include/asm/arch-mx6/sys_proto.h @@ -14,6 +14,7 @@ #define soc_rev() (get_cpu_rev() & 0xFF) #define is_soc_rev(rev) (soc_rev() - rev) +u32 get_nr_cpus(void); u32 get_cpu_rev(void); /* returns MXC_CPU_ value */ diff --git a/arch/arm/include/asm/arch-sunxi/dram.h b/arch/arm/include/asm/arch-sunxi/dram.h index 67fbfad..1945f75 100644 --- a/arch/arm/include/asm/arch-sunxi/dram.h +++ b/arch/arm/include/asm/arch-sunxi/dram.h @@ -69,6 +69,7 @@ struct sunxi_dram_reg { struct dram_para { u32 clock; + u32 mbus_clock; u32 type; u32 rank_num; u32 density; @@ -87,6 +88,8 @@ struct dram_para { u32 emr1; u32 emr2; u32 emr3; + u32 dqs_gating_delay; + u32 active_windowing; }; #define DRAM_CCR_COMMAND_RATE_1T (0x1 << 5) @@ -121,9 +124,6 @@ struct dram_para { #define DRAM_DCR_BUS_WIDTH_32BIT 0x3 #define DRAM_DCR_BUS_WIDTH_16BIT 0x1 #define DRAM_DCR_BUS_WIDTH_8BIT 0x0 -#define DRAM_DCR_NR_DLLCR_32BIT 5 -#define DRAM_DCR_NR_DLLCR_16BIT 3 -#define DRAM_DCR_NR_DLLCR_8BIT 2 #define DRAM_DCR_RANK_SEL(n) (((n) & 0x3) << 10) #define DRAM_DCR_RANK_SEL_MASK DRAM_DCR_CMD_RANK(0x3) #define DRAM_DCR_CMD_RANK_ALL (0x1 << 12) @@ -132,7 +132,9 @@ struct dram_para { #define DRAM_DCR_MODE_SEQ 0x0 #define DRAM_DCR_MODE_INTERLEAVE 0x1 -#define DRAM_CSR_FAILED (0x1 << 20) +#define DRAM_CSR_DTERR (0x1 << 20) +#define DRAM_CSR_DTIERR (0x1 << 21) +#define DRAM_CSR_FAILED (DRAM_CSR_DTERR | DRAM_CSR_DTIERR) #define DRAM_DRR_TRFC(n) ((n) & 0xff) #define DRAM_DRR_TREFI(n) (((n) & 0xffff) << 8) @@ -159,6 +161,10 @@ struct dram_para { #define DRAM_ZQCR0_IMP_DIV(n) (((n) & 0xff) << 20) #define DRAM_ZQCR0_IMP_DIV_MASK DRAM_ZQCR0_IMP_DIV(0xff) +#define DRAM_ZQCR0_ZCAL (1 << 31) /* Starts ZQ calibration when set to 1 */ +#define DRAM_ZQCR0_ZDEN (1 << 28) /* Uses ZDATA instead of doing calibration */ + +#define DRAM_ZQSR_ZDONE (1 << 31) /* ZQ calibration completion flag */ #define DRAM_IOCR_ODT_EN(n) ((((n) & 0x3) << 30) | ((n) & 0x3) << 0) #define DRAM_IOCR_ODT_EN_MASK DRAM_IOCR_ODT_EN(0x3) diff --git a/arch/arm/include/asm/emif.h b/arch/arm/include/asm/emif.h index b8d6bdc..2fe5776 100644 --- a/arch/arm/include/asm/emif.h +++ b/arch/arm/include/asm/emif.h @@ -878,7 +878,6 @@ struct dmm_lisa_map_regs { ((REG_CS_TIM << EMIF_REG_CS_TIM_SHIFT) & EMIF_REG_CS_TIM_MASK)|\ ((REG_SR_TIM << EMIF_REG_SR_TIM_SHIFT) & EMIF_REG_SR_TIM_MASK)|\ ((REG_PD_TIM << EMIF_REG_PD_TIM_SHIFT) & EMIF_REG_PD_TIM_MASK)|\ - ((REG_PD_TIM << EMIF_REG_PD_TIM_SHIFT) & EMIF_REG_PD_TIM_MASK)|\ ((LP_MODE_DISABLE << EMIF_REG_LP_MODE_SHIFT)\ & EMIF_REG_LP_MODE_MASK) |\ ((DPD_DISABLE << EMIF_REG_DPD_EN_SHIFT)\ @@ -890,8 +889,6 @@ struct dmm_lisa_map_regs { ((REG_SR_TIM << EMIF_REG_SR_TIM_SHDW_SHIFT)\ & EMIF_REG_SR_TIM_SHDW_MASK) |\ ((REG_PD_TIM << EMIF_REG_PD_TIM_SHDW_SHIFT)\ - & EMIF_REG_PD_TIM_SHDW_MASK) |\ - ((REG_PD_TIM << EMIF_REG_PD_TIM_SHDW_SHIFT)\ & EMIF_REG_PD_TIM_SHDW_MASK)) /* EMIF_L3_CONFIG register value */ diff --git a/arch/blackfin/config.mk b/arch/blackfin/config.mk index 7b17b75..584b38b 100644 --- a/arch/blackfin/config.mk +++ b/arch/blackfin/config.mk @@ -20,6 +20,9 @@ CONFIG_BFIN_CPU := $(strip $(CONFIG_BFIN_CPU:"%"=%)) endif CONFIG_BFIN_BOOT_MODE := $(strip $(CONFIG_BFIN_BOOT_MODE:"%"=%)) +# Support generic board on Blackfin +__HAVE_ARCH_GENERIC_BOARD := y + PLATFORM_RELFLAGS += -ffixed-P3 -fomit-frame-pointer -mno-fdpic LDFLAGS_FINAL += --gc-sections diff --git a/arch/blackfin/cpu/cpu.c b/arch/blackfin/cpu/cpu.c index 2409c30..b7f1188 100644 --- a/arch/blackfin/cpu/cpu.c +++ b/arch/blackfin/cpu/cpu.c @@ -11,17 +11,22 @@ #include <common.h> #include <command.h> +#include <serial.h> +#include <version.h> +#include <i2c.h> + #include <asm/blackfin.h> #include <asm/cplb.h> +#include <asm/clock.h> #include <asm/mach-common/bits/core.h> #include <asm/mach-common/bits/ebiu.h> #include <asm/mach-common/bits/trace.h> -#include <asm/serial.h> #include "cpu.h" #include "initcode.h" ulong bfin_poweron_retx; +DECLARE_GLOBAL_DATA_PTR; #if defined(CONFIG_CORE1_RUN) && defined(COREB_L1_CODE_START) void bfin_core1_start(void) @@ -48,6 +53,252 @@ void bfin_core1_start(void) } #endif +__attribute__((always_inline)) +static inline void serial_early_puts(const char *s) +{ +#ifdef CONFIG_DEBUG_EARLY_SERIAL + serial_puts("Early: "); + serial_puts(s); +#endif +} + +static int global_board_data_init(void) +{ +#ifndef CONFIG_SYS_GBL_DATA_ADDR +# define CONFIG_SYS_GBL_DATA_ADDR 0 +#endif +#ifndef CONFIG_SYS_BD_INFO_ADDR +# define CONFIG_SYS_BD_INFO_ADDR 0 +#endif + + bd_t *bd; + + if (CONFIG_SYS_GBL_DATA_ADDR) { + gd = (gd_t *)(CONFIG_SYS_GBL_DATA_ADDR); + memset((void *)gd, 0, GENERATED_GBL_DATA_SIZE); + } else { + static gd_t _bfin_gd; + gd = &_bfin_gd; + } + if (CONFIG_SYS_BD_INFO_ADDR) { + bd = (bd_t *)(CONFIG_SYS_BD_INFO_ADDR); + memset(bd, 0, GENERATED_BD_INFO_SIZE); + } else { + static bd_t _bfin_bd; + bd = &_bfin_bd; + } + + gd->bd = bd; + + bd->bi_r_version = version_string; + bd->bi_cpu = __stringify(CONFIG_BFIN_CPU); + bd->bi_board_name = CONFIG_SYS_BOARD; + bd->bi_vco = get_vco(); + bd->bi_cclk = get_cclk(); + bd->bi_sclk = get_sclk(); + bd->bi_memstart = CONFIG_SYS_SDRAM_BASE; + bd->bi_memsize = CONFIG_SYS_MAX_RAM_SIZE; + + gd->ram_size = CONFIG_SYS_MAX_RAM_SIZE; + + return 0; +} + +static void display_global_data(void) +{ + bd_t *bd; + +#ifndef CONFIG_DEBUG_EARLY_SERIAL + return; +#endif + + bd = gd->bd; + printf(" gd: %p\n", gd); + printf(" |-flags: %lx\n", gd->flags); + printf(" |-board_type: %lx\n", gd->arch.board_type); + printf(" |-baudrate: %u\n", gd->baudrate); + printf(" |-have_console: %lx\n", gd->have_console); + printf(" |-ram_size: %lx\n", gd->ram_size); + printf(" |-env_addr: %lx\n", gd->env_addr); + printf(" |-env_valid: %lx\n", gd->env_valid); + printf(" |-jt(%p): %p\n", gd->jt, *(gd->jt)); + printf(" \\-bd: %p\n", gd->bd); + printf(" |-bi_boot_params: %lx\n", bd->bi_boot_params); + printf(" |-bi_memstart: %lx\n", bd->bi_memstart); + printf(" |-bi_memsize: %lx\n", bd->bi_memsize); + printf(" |-bi_flashstart: %lx\n", bd->bi_flashstart); + printf(" |-bi_flashsize: %lx\n", bd->bi_flashsize); + printf(" \\-bi_flashoffset: %lx\n", bd->bi_flashoffset); +} + +#define CPLB_PAGE_SIZE (4 * 1024 * 1024) +#define CPLB_PAGE_MASK (~(CPLB_PAGE_SIZE - 1)) +#if defined(__ADSPBF60x__) +#define CPLB_EX_PAGE_SIZE (16 * 1024 * 1024) +#define CPLB_EX_PAGE_MASK (~(CPLB_EX_PAGE_SIZE - 1)) +#else +#define CPLB_EX_PAGE_SIZE CPLB_PAGE_SIZE +#define CPLB_EX_PAGE_MASK CPLB_PAGE_MASK +#endif +void init_cplbtables(void) +{ + uint32_t *ICPLB_ADDR, *ICPLB_DATA; + uint32_t *DCPLB_ADDR, *DCPLB_DATA; + uint32_t extern_memory; + size_t i; + + void icplb_add(uint32_t addr, uint32_t data) + { + bfin_write32(ICPLB_ADDR + i, addr); + bfin_write32(ICPLB_DATA + i, data); + } + void dcplb_add(uint32_t addr, uint32_t data) + { + bfin_write32(DCPLB_ADDR + i, addr); + bfin_write32(DCPLB_DATA + i, data); + } + + /* populate a few common entries ... we'll let + * the memory map and cplb exception handler do + * the rest of the work. + */ + i = 0; + ICPLB_ADDR = (uint32_t *)ICPLB_ADDR0; + ICPLB_DATA = (uint32_t *)ICPLB_DATA0; + DCPLB_ADDR = (uint32_t *)DCPLB_ADDR0; + DCPLB_DATA = (uint32_t *)DCPLB_DATA0; + + icplb_add(0xFFA00000, L1_IMEMORY); + dcplb_add(0xFF800000, L1_DMEMORY); + ++i; +#if defined(__ADSPBF60x__) + icplb_add(0x0, 0x0); + dcplb_add(CONFIG_SYS_FLASH_BASE, PAGE_SIZE_16MB | CPLB_DIRTY | + CPLB_SUPV_WR | CPLB_USER_WR | CPLB_USER_RD | CPLB_VALID); + ++i; +#endif + + if (CONFIG_MEM_SIZE) { + uint32_t mbase = CONFIG_SYS_MONITOR_BASE; + uint32_t mend = mbase + CONFIG_SYS_MONITOR_LEN - 1; + mbase &= CPLB_PAGE_MASK; + mend &= CPLB_PAGE_MASK; + + icplb_add(mbase, SDRAM_IKERNEL); + dcplb_add(mbase, SDRAM_DKERNEL); + ++i; + + /* + * If the monitor crosses a 4 meg boundary, we'll need + * to lock two entries for it. We assume it doesn't + * cross two 4 meg boundaries ... + */ + if (mbase != mend) { + icplb_add(mend, SDRAM_IKERNEL); + dcplb_add(mend, SDRAM_DKERNEL); + ++i; + } + } + +#ifndef __ADSPBF60x__ + icplb_add(0x20000000, SDRAM_INON_CHBL); + dcplb_add(0x20000000, SDRAM_EBIU); + ++i; +#endif + + /* Add entries for the rest of external RAM up to the bootrom */ + extern_memory = 0; + +#ifdef CONFIG_DEBUG_NULL_PTR + icplb_add(extern_memory, + (SDRAM_IKERNEL & ~PAGE_SIZE_MASK) | PAGE_SIZE_1KB); + dcplb_add(extern_memory, + (SDRAM_DKERNEL & ~PAGE_SIZE_MASK) | PAGE_SIZE_1KB); + ++i; + icplb_add(extern_memory, SDRAM_IKERNEL); + dcplb_add(extern_memory, SDRAM_DKERNEL); + extern_memory += CPLB_PAGE_SIZE; + ++i; +#endif + + while (i < 16 && extern_memory < + (CONFIG_SYS_MONITOR_BASE & CPLB_EX_PAGE_MASK)) { + icplb_add(extern_memory, SDRAM_IGENERIC); + dcplb_add(extern_memory, SDRAM_DGENERIC); + extern_memory += CPLB_EX_PAGE_SIZE; + ++i; + } + while (i < 16) { + icplb_add(0, 0); + dcplb_add(0, 0); + ++i; + } +} + +int print_cpuinfo(void) +{ + char buf[32]; + + printf("CPU: ADSP %s (Detected Rev: 0.%d) (%s boot)\n", + gd->bd->bi_cpu, + bfin_revid(), + get_bfin_boot_mode(CONFIG_BFIN_BOOT_MODE)); + + printf("Clock: VCO: %s MHz, ", strmhz(buf, get_vco())); + printf("Core: %s MHz, ", strmhz(buf, get_cclk())); +#if defined(__ADSPBF60x__) + printf("System0: %s MHz, ", strmhz(buf, get_sclk0())); + printf("System1: %s MHz, ", strmhz(buf, get_sclk1())); + printf("Dclk: %s MHz\n", strmhz(buf, get_dclk())); +#else + printf("System: %s MHz\n", strmhz(buf, get_sclk())); +#endif + + return 0; +} + +int exception_init(void) +{ + bfin_write_EVT3(trap); + return 0; +} + +int irq_init(void) +{ +#ifdef SIC_IMASK0 + bfin_write_SIC_IMASK0(0); + bfin_write_SIC_IMASK1(0); +# ifdef SIC_IMASK2 + bfin_write_SIC_IMASK2(0); +# endif +#elif defined(SICA_IMASK0) + bfin_write_SICA_IMASK0(0); + bfin_write_SICA_IMASK1(0); +#elif defined(SIC_IMASK) + bfin_write_SIC_IMASK(0); +#endif + /* Set up a dummy NMI handler if needed. */ + if (CONFIG_BFIN_BOOT_MODE == BFIN_BOOT_BYPASS || ANOMALY_05000219) + bfin_write_EVT2(evt_nmi); /* NMI */ + bfin_write_EVT5(evt_default); /* hardware error */ + bfin_write_EVT6(evt_default); /* core timer */ + bfin_write_EVT7(evt_default); + bfin_write_EVT8(evt_default); + bfin_write_EVT9(evt_default); + bfin_write_EVT10(evt_default); + bfin_write_EVT11(evt_default); + bfin_write_EVT12(evt_default); + bfin_write_EVT13(evt_default); + bfin_write_EVT14(evt_default); + bfin_write_EVT15(evt_default); + bfin_write_ILAT(0); + CSYNC(); + /* enable hardware error irq */ + irq_flags = 0x3f; + local_irq_enable(); + return 0; +} + __attribute__ ((__noreturn__)) void cpu_init_f(ulong bootflag, ulong loaded_from_ldr) { @@ -102,51 +353,62 @@ void cpu_init_f(ulong bootflag, ulong loaded_from_ldr) bfin_core1_start(); #endif - serial_early_puts("Board init flash\n"); - board_init_f(bootflag); + serial_early_puts("Init global data\n"); + global_board_data_init(); + + board_init_f(0); /* should not be reached */ while (1); } -int exception_init(void) +int arch_cpu_init(void) { - bfin_write_EVT3(trap); + serial_early_puts("Init CPLB tables\n"); + init_cplbtables(); + + serial_early_puts("Exceptions setup\n"); + exception_init(); + +#ifndef CONFIG_ICACHE_OFF + serial_early_puts("Turn on ICACHE\n"); + icache_enable(); +#endif +#ifndef CONFIG_DCACHE_OFF + serial_early_puts("Turn on DCACHE\n"); + dcache_enable(); +#endif + +#ifdef DEBUG + if (GENERATED_GBL_DATA_SIZE < sizeof(*gd)) + hang(); +#endif + + /* Initialize */ + serial_early_puts("IRQ init\n"); + irq_init(); + return 0; } -int irq_init(void) +int arch_misc_init(void) { -#ifdef SIC_IMASK0 - bfin_write_SIC_IMASK0(0); - bfin_write_SIC_IMASK1(0); -# ifdef SIC_IMASK2 - bfin_write_SIC_IMASK2(0); -# endif -#elif defined(SICA_IMASK0) - bfin_write_SICA_IMASK0(0); - bfin_write_SICA_IMASK1(0); -#elif defined(SIC_IMASK) - bfin_write_SIC_IMASK(0); +#if defined(CONFIG_SYS_I2C) + i2c_reloc_fixup(); #endif - /* Set up a dummy NMI handler if needed. */ - if (CONFIG_BFIN_BOOT_MODE == BFIN_BOOT_BYPASS || ANOMALY_05000219) - bfin_write_EVT2(evt_nmi); /* NMI */ - bfin_write_EVT5(evt_default); /* hardware error */ - bfin_write_EVT6(evt_default); /* core timer */ - bfin_write_EVT7(evt_default); - bfin_write_EVT8(evt_default); - bfin_write_EVT9(evt_default); - bfin_write_EVT10(evt_default); - bfin_write_EVT11(evt_default); - bfin_write_EVT12(evt_default); - bfin_write_EVT13(evt_default); - bfin_write_EVT14(evt_default); - bfin_write_EVT15(evt_default); - bfin_write_ILAT(0); - CSYNC(); - /* enable hardware error irq */ - irq_flags = 0x3f; - local_irq_enable(); + + display_global_data(); + + if (CONFIG_MEM_SIZE && bfin_os_log_check()) { + puts("\nLog buffer from operating system:\n"); + bfin_os_log_dump(); + puts("\n"); + } + + return 0; +} + +int interrupt_init(void) +{ return 0; } diff --git a/arch/blackfin/cpu/start.S b/arch/blackfin/cpu/start.S index 29a7c23..f31abfa 100644 --- a/arch/blackfin/cpu/start.S +++ b/arch/blackfin/cpu/start.S @@ -196,8 +196,8 @@ ENTRY(_start) * takes care of clearing things for us. */ serial_early_puts("Zero BSS"); - r0.l = __bss_vma; - r0.h = __bss_vma; + r0.l = __bss_start; + r0.h = __bss_start; r1 = 0 (x); r2.l = __bss_len; r2.h = __bss_len; @@ -251,3 +251,13 @@ LENTRY(_get_pc) #endif rts; ENDPROC(_get_pc) + +ENTRY(_relocate_code) + /* Fake relocate code. Setup the new stack only */ + sp = r0; + fp = sp; + r0 = p3; + r1.h = 0x2000; + r1.l = 0x10; + jump.l _board_init_r +ENDPROC(_relocate_code) diff --git a/arch/blackfin/cpu/u-boot.lds b/arch/blackfin/cpu/u-boot.lds index 7f0411f..ae1b813 100644 --- a/arch/blackfin/cpu/u-boot.lds +++ b/arch/blackfin/cpu/u-boot.lds @@ -135,6 +135,8 @@ SECTIONS *(COMMON) . = ALIGN(4); } >ram_data - __bss_vma = ADDR(.bss); + __bss_end = .; + __bss_start = ADDR(.bss); __bss_len = SIZEOF(.bss); + __init_end = .; } diff --git a/arch/blackfin/include/asm/clock.h b/arch/blackfin/include/asm/clock.h index 59d3faa..05ae03c 100644 --- a/arch/blackfin/include/asm/clock.h +++ b/arch/blackfin/include/asm/clock.h @@ -78,7 +78,7 @@ extern u_long get_sclk1(void); extern u_long get_dclk(void); # define get_uart_clk get_sclk0 # define get_i2c_clk get_sclk0 -# define get_spi_clk get_sclk0 +# define get_spi_clk get_sclk1 #else # define get_uart_clk get_sclk # define get_i2c_clk get_sclk diff --git a/arch/blackfin/include/asm/config.h b/arch/blackfin/include/asm/config.h index 1da386e..836658a 100644 --- a/arch/blackfin/include/asm/config.h +++ b/arch/blackfin/include/asm/config.h @@ -174,4 +174,8 @@ } #endif +#define CONFIG_SYS_GENERIC_BOARD +#define CONFIG_DISPLAY_CPUINFO +#define CONFIG_ARCH_MISC_INIT + #endif diff --git a/arch/blackfin/include/asm/mach-bf609/BF609_def.h b/arch/blackfin/include/asm/mach-bf609/BF609_def.h index 02b81d3..fd0d86d 100644 --- a/arch/blackfin/include/asm/mach-bf609/BF609_def.h +++ b/arch/blackfin/include/asm/mach-bf609/BF609_def.h @@ -125,6 +125,8 @@ #define WDOG1_CNT 0xFFC17804 /* WDOG1 Count Register */ #define WDOG1_STAT 0xFFC17808 /* WDOG1 Watchdog Timer Status Register */ +#define SDU0_MSG_SET 0xFFC1F084 /* SDU0 Message Set Register */ + #define EMAC0_MACCFG 0xFFC20000 /* EMAC0 MAC Configuration Register */ #define EMAC1_MACCFG 0xFFC22000 /* EMAC1 MAC Configuration Register */ diff --git a/arch/blackfin/include/asm/u-boot.h b/arch/blackfin/include/asm/u-boot.h index acaeee9..7b5cf6a 100644 --- a/arch/blackfin/include/asm/u-boot.h +++ b/arch/blackfin/include/asm/u-boot.h @@ -25,9 +25,12 @@ typedef struct bd_info { unsigned long bi_vco; unsigned long bi_cclk; unsigned long bi_sclk; + unsigned char bi_enetaddr[6]; } bd_t; /* For image.h:image_check_target_arch() */ #define IH_ARCH_DEFAULT IH_ARCH_BLACKFIN +int arch_misc_init(void); + #endif /* _U_BOOT_H_ */ diff --git a/arch/blackfin/lib/Makefile b/arch/blackfin/lib/Makefile index 4ba7bf6..b534a98 100644 --- a/arch/blackfin/lib/Makefile +++ b/arch/blackfin/lib/Makefile @@ -9,11 +9,6 @@ # SPDX-License-Identifier: GPL-2.0+ # -# Unnecessary. -# Use CONFIG_SYS_BOARD instead of BFIN_BOARD_NAME -# and delete this. -ccflags-y += -DBFIN_BOARD_NAME='"$(BOARD)"' - obj-y += ins.o obj-y += memcmp.o obj-y += memcpy.o @@ -21,7 +16,6 @@ obj-y += memmove.o obj-y += memset.o obj-y += outs.o obj-$(CONFIG_CMD_KGDB) += __kgdb.o -obj-y += board.o obj-y += boot.o obj-y += cache.o obj-y += clocks.o @@ -30,3 +24,4 @@ obj-$(CONFIG_CMD_KGDB) += kgdb.o obj-y += muldi3.o obj-$(CONFIG_HAS_POST) += post.o obj-y += string.o +obj-y += sections.o diff --git a/arch/blackfin/lib/board.c b/arch/blackfin/lib/board.c deleted file mode 100644 index 8784255..0000000 --- a/arch/blackfin/lib/board.c +++ /dev/null @@ -1,443 +0,0 @@ -/* - * U-boot - board.c First C file to be called contains init routines - * - * Copyright (c) 2005-2008 Analog Devices Inc. - * - * (C) Copyright 2000-2004 - * Wolfgang Denk, DENX Software Engineering, wd@denx.de. - * - * Licensed under the GPL-2 or later. - */ - -#include <common.h> -#include <command.h> -#include <stdio_dev.h> -#include <serial.h> -#include <environment.h> -#include <malloc.h> -#include <mmc.h> -#include <net.h> -#include <status_led.h> -#include <version.h> -#include <watchdog.h> - -#include <asm/cplb.h> -#include <asm/mach-common/bits/mpu.h> -#include <asm/clock.h> -#include <kgdb.h> - -#ifdef CONFIG_CMD_NAND -#include <nand.h> /* cannot even include nand.h if it isnt configured */ -#endif - -#ifdef CONFIG_BITBANGMII -#include <miiphy.h> -#endif - -#if defined(CONFIG_POST) -#include <post.h> -int post_flag; -#endif - -#if defined(CONFIG_SYS_I2C) -#include <i2c.h> -#endif - -DECLARE_GLOBAL_DATA_PTR; - -__attribute__((always_inline)) -static inline void serial_early_puts(const char *s) -{ -#ifdef CONFIG_DEBUG_EARLY_SERIAL - serial_puts("Early: "); - serial_puts(s); -#endif -} - -static int display_banner(void) -{ - display_options(); - printf("CPU: ADSP %s " - "(Detected Rev: 0.%d) " - "(%s boot)\n", - gd->bd->bi_cpu, - bfin_revid(), - get_bfin_boot_mode(CONFIG_BFIN_BOOT_MODE)); - return 0; -} - -static int init_baudrate(void) -{ - gd->baudrate = getenv_ulong("baudrate", 10, CONFIG_BAUDRATE); - return 0; -} - -static void display_global_data(void) -{ - bd_t *bd; - -#ifndef CONFIG_DEBUG_EARLY_SERIAL - return; -#endif - - bd = gd->bd; - printf(" gd: %p\n", gd); - printf(" |-flags: %lx\n", gd->flags); - printf(" |-board_type: %lx\n", gd->arch.board_type); - printf(" |-baudrate: %u\n", gd->baudrate); - printf(" |-have_console: %lx\n", gd->have_console); - printf(" |-ram_size: %lx\n", gd->ram_size); - printf(" |-env_addr: %lx\n", gd->env_addr); - printf(" |-env_valid: %lx\n", gd->env_valid); - printf(" |-jt(%p): %p\n", gd->jt, *(gd->jt)); - printf(" \\-bd: %p\n", gd->bd); - printf(" |-bi_boot_params: %lx\n", bd->bi_boot_params); - printf(" |-bi_memstart: %lx\n", bd->bi_memstart); - printf(" |-bi_memsize: %lx\n", bd->bi_memsize); - printf(" |-bi_flashstart: %lx\n", bd->bi_flashstart); - printf(" |-bi_flashsize: %lx\n", bd->bi_flashsize); - printf(" \\-bi_flashoffset: %lx\n", bd->bi_flashoffset); -} - -#define CPLB_PAGE_SIZE (4 * 1024 * 1024) -#define CPLB_PAGE_MASK (~(CPLB_PAGE_SIZE - 1)) -#if defined(__ADSPBF60x__) -#define CPLB_EX_PAGE_SIZE (16 * 1024 * 1024) -#define CPLB_EX_PAGE_MASK (~(CPLB_EX_PAGE_SIZE - 1)) -#else -#define CPLB_EX_PAGE_SIZE CPLB_PAGE_SIZE -#define CPLB_EX_PAGE_MASK CPLB_PAGE_MASK -#endif -void init_cplbtables(void) -{ - volatile uint32_t *ICPLB_ADDR, *ICPLB_DATA; - volatile uint32_t *DCPLB_ADDR, *DCPLB_DATA; - uint32_t extern_memory; - size_t i; - - void icplb_add(uint32_t addr, uint32_t data) - { - *(ICPLB_ADDR + i) = addr; - *(ICPLB_DATA + i) = data; - } - void dcplb_add(uint32_t addr, uint32_t data) - { - *(DCPLB_ADDR + i) = addr; - *(DCPLB_DATA + i) = data; - } - - /* populate a few common entries ... we'll let - * the memory map and cplb exception handler do - * the rest of the work. - */ - i = 0; - ICPLB_ADDR = (uint32_t *)ICPLB_ADDR0; - ICPLB_DATA = (uint32_t *)ICPLB_DATA0; - DCPLB_ADDR = (uint32_t *)DCPLB_ADDR0; - DCPLB_DATA = (uint32_t *)DCPLB_DATA0; - - icplb_add(0xFFA00000, L1_IMEMORY); - dcplb_add(0xFF800000, L1_DMEMORY); - ++i; -#if defined(__ADSPBF60x__) - icplb_add(0x0, 0x0); - dcplb_add(CONFIG_SYS_FLASH_BASE, PAGE_SIZE_16MB | CPLB_DIRTY | - CPLB_SUPV_WR | CPLB_USER_WR | CPLB_USER_RD | CPLB_VALID); - ++i; -#endif - - if (CONFIG_MEM_SIZE) { - uint32_t mbase = CONFIG_SYS_MONITOR_BASE; - uint32_t mend = mbase + CONFIG_SYS_MONITOR_LEN; - mbase &= CPLB_PAGE_MASK; - mend &= CPLB_PAGE_MASK; - - icplb_add(mbase, SDRAM_IKERNEL); - dcplb_add(mbase, SDRAM_DKERNEL); - ++i; - - /* - * If the monitor crosses a 4 meg boundary, we'll need - * to lock two entries for it. We assume it doesn't - * cross two 4 meg boundaries ... - */ - if (mbase != mend) { - icplb_add(mend, SDRAM_IKERNEL); - dcplb_add(mend, SDRAM_DKERNEL); - ++i; - } - } - -#ifndef __ADSPBF60x__ - icplb_add(0x20000000, SDRAM_INON_CHBL); - dcplb_add(0x20000000, SDRAM_EBIU); - ++i; -#endif - - /* Add entries for the rest of external RAM up to the bootrom */ - extern_memory = 0; - -#ifdef CONFIG_DEBUG_NULL_PTR - icplb_add(extern_memory, (SDRAM_IKERNEL & ~PAGE_SIZE_MASK) | PAGE_SIZE_1KB); - dcplb_add(extern_memory, (SDRAM_DKERNEL & ~PAGE_SIZE_MASK) | PAGE_SIZE_1KB); - ++i; - icplb_add(extern_memory, SDRAM_IKERNEL); - dcplb_add(extern_memory, SDRAM_DKERNEL); - extern_memory += CPLB_PAGE_SIZE; - ++i; -#endif - - while (i < 16 && extern_memory < - (CONFIG_SYS_MONITOR_BASE & CPLB_EX_PAGE_MASK)) { - icplb_add(extern_memory, SDRAM_IGENERIC); - dcplb_add(extern_memory, SDRAM_DGENERIC); - extern_memory += CPLB_EX_PAGE_SIZE; - ++i; - } - while (i < 16) { - icplb_add(0, 0); - dcplb_add(0, 0); - ++i; - } -} - -static int global_board_data_init(void) -{ -#ifndef CONFIG_SYS_GBL_DATA_ADDR -# define CONFIG_SYS_GBL_DATA_ADDR 0 -#endif -#ifndef CONFIG_SYS_BD_INFO_ADDR -# define CONFIG_SYS_BD_INFO_ADDR 0 -#endif - - bd_t *bd; - - if (CONFIG_SYS_GBL_DATA_ADDR) { - gd = (gd_t *) (CONFIG_SYS_GBL_DATA_ADDR); - memset((void *)gd, 0, GENERATED_GBL_DATA_SIZE); - } else { - static gd_t _bfin_gd; - gd = &_bfin_gd; - } - - if (CONFIG_SYS_BD_INFO_ADDR) { - bd = (bd_t *) (CONFIG_SYS_BD_INFO_ADDR); - memset(bd, 0, GENERATED_BD_INFO_SIZE); - } else { - static bd_t _bfin_bd; - bd = &_bfin_bd; - } - gd->bd = bd; - - bd->bi_r_version = version_string; - bd->bi_cpu = __stringify(CONFIG_BFIN_CPU); - bd->bi_board_name = BFIN_BOARD_NAME; - bd->bi_vco = get_vco(); - bd->bi_cclk = get_cclk(); - bd->bi_sclk = get_sclk(); - bd->bi_memstart = CONFIG_SYS_SDRAM_BASE; - bd->bi_memsize = CONFIG_SYS_MAX_RAM_SIZE; - - return 0; -} - -/* - * All attempts to come up with a "common" initialization sequence - * that works for all boards and architectures failed: some of the - * requirements are just _too_ different. To get rid of the resulting - * mess of board dependend #ifdef'ed code we now make the whole - * initialization sequence configurable to the user. - * - * The requirements for any new initalization function is simple: it - * receives a pointer to the "global data" structure as it's only - * argument, and returns an integer return code, where 0 means - * "continue" and != 0 means "fatal error, hang the system". - */ - -extern int watchdog_init(void); -extern int exception_init(void); -extern int irq_init(void); -extern int timer_init(void); - -void board_init_f(ulong bootflag) -{ - char buf[32]; - -#ifdef CONFIG_BOARD_EARLY_INIT_F - serial_early_puts("Board early init flash\n"); - board_early_init_f(); -#endif - - serial_early_puts("Init CPLB tables\n"); - init_cplbtables(); - - serial_early_puts("Exceptions setup\n"); - exception_init(); - -#ifndef CONFIG_ICACHE_OFF - serial_early_puts("Turn on ICACHE\n"); - icache_enable(); -#endif -#ifndef CONFIG_DCACHE_OFF - serial_early_puts("Turn on DCACHE\n"); - dcache_enable(); -#endif - -#ifdef CONFIG_HW_WATCHDOG - serial_early_puts("Setting up external watchdog\n"); - hw_watchdog_init(); -#endif - -#ifdef DEBUG - if (GENERATED_GBL_DATA_SIZE < sizeof(*gd)) - hang(); -#endif - serial_early_puts("Init global data\n"); - - global_board_data_init(); - - /* Initialize */ - serial_early_puts("IRQ init\n"); - irq_init(); - serial_early_puts("Environment init\n"); - env_init(); - serial_early_puts("Baudrate init\n"); - init_baudrate(); - serial_early_puts("Serial init\n"); - serial_init(); - serial_initialize(); - serial_early_puts("Console init flash\n"); - console_init_f(); - serial_early_puts("End of early debugging\n"); - display_banner(); - - checkboard(); - timer_init(); - - printf("Clock: VCO: %s MHz, ", strmhz(buf, get_vco())); - printf("Core: %s MHz, ", strmhz(buf, get_cclk())); -#if defined(__ADSPBF60x__) - printf("System0: %s MHz, ", strmhz(buf, get_sclk0())); - printf("System1: %s MHz, ", strmhz(buf, get_sclk1())); - printf("Dclk: %s MHz\n", strmhz(buf, get_dclk())); -#else - printf("System: %s MHz\n", strmhz(buf, get_sclk())); -#endif - - if (CONFIG_MEM_SIZE) { - printf("RAM: "); - print_size(gd->bd->bi_memsize, "\n"); - } - -#if defined(CONFIG_POST) - post_init_f(); - post_bootmode_init(); - post_run(NULL, POST_ROM | post_bootmode_get(0)); -#endif - - board_init_r((gd_t *) gd, 0x20000010); -} - -static void board_net_init_r(bd_t *bd) -{ -#ifdef CONFIG_BITBANGMII - bb_miiphy_init(); -#endif -#ifdef CONFIG_CMD_NET - printf("Net: "); - eth_initialize(bd); -#endif -} - -void board_init_r(gd_t * id, ulong dest_addr) -{ - bd_t *bd; - gd = id; - gd->flags |= GD_FLG_RELOC; /* tell others: relocation done */ - bd = gd->bd; - -#if defined(CONFIG_POST) - post_output_backlog(); -#endif - - /* initialize malloc() area */ - mem_malloc_init(CONFIG_SYS_MALLOC_BASE, CONFIG_SYS_MALLOC_LEN); - -#if !defined(CONFIG_SYS_NO_FLASH) - /* Initialize the flash and protect u-boot by default */ - extern flash_info_t flash_info[]; - puts("Flash: "); - ulong size = flash_init(); - print_size(size, "\n"); - flash_protect(FLAG_PROTECT_SET, CONFIG_SYS_FLASH_BASE, - CONFIG_SYS_FLASH_BASE + CONFIG_SYS_MONITOR_LEN - 1, - &flash_info[0]); - bd->bi_flashstart = CONFIG_SYS_FLASH_BASE; - bd->bi_flashsize = size; - bd->bi_flashoffset = 0; -#else - bd->bi_flashstart = 0; - bd->bi_flashsize = 0; - bd->bi_flashoffset = 0; -#endif - -#ifdef CONFIG_CMD_NAND - puts("NAND: "); - nand_init(); /* go init the NAND */ -#endif - -#ifdef CONFIG_GENERIC_MMC - puts("MMC: "); - mmc_initialize(bd); -#endif - -#if defined(CONFIG_SYS_I2C) - i2c_reloc_fixup(); -#endif - /* relocate environment function pointers etc. */ - env_relocate(); - - /* Initialize stdio devices */ - stdio_init(); - jumptable_init(); - - /* Initialize the console (after the relocation and devices init) */ - console_init_r(); - -#ifdef CONFIG_CMD_KGDB - puts("KGDB: "); - kgdb_init(); -#endif - -#ifdef CONFIG_STATUS_LED - status_led_set(STATUS_LED_BOOT, STATUS_LED_BLINKING); - status_led_set(STATUS_LED_CRASH, STATUS_LED_OFF); -#endif - - /* Initialize from environment */ - load_addr = getenv_ulong("loadaddr", 16, load_addr); - -#if defined(CONFIG_MISC_INIT_R) - /* miscellaneous platform dependent initialisations */ - misc_init_r(); -#endif - - board_net_init_r(bd); - - display_global_data(); - -#if defined(CONFIG_POST) - if (post_flag) - post_run(NULL, POST_RAM | post_bootmode_get(0)); -#endif - - if (CONFIG_MEM_SIZE && bfin_os_log_check()) { - puts("\nLog buffer from operating system:\n"); - bfin_os_log_dump(); - puts("\n"); - } - - /* main_loop() can return to retry autoboot, if so just run it again. */ - for (;;) - main_loop(); -} diff --git a/arch/blackfin/lib/sections.c b/arch/blackfin/lib/sections.c new file mode 100644 index 0000000..b50f30a --- /dev/null +++ b/arch/blackfin/lib/sections.c @@ -0,0 +1,11 @@ +/* + * U-boot - section.c + * + * Copyright (c) 2014 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +char __bss_start[0] __attribute__((section(".__bss_start"))); +char __bss_end[0] __attribute__((section(".__bss_end"))); +char __init_end[0] __attribute__((section(".__init_end"))); diff --git a/arch/nios2/Kconfig b/arch/nios2/Kconfig index b703646..0cba45b 100644 --- a/arch/nios2/Kconfig +++ b/arch/nios2/Kconfig @@ -11,16 +11,8 @@ choice config TARGET_NIOS2_GENERIC bool "Support nios2-generic" -config TARGET_PCI5441 - bool "Support PCI5441" - -config TARGET_PK1C20 - bool "Support PK1C20" - endchoice source "board/altera/nios2-generic/Kconfig" -source "board/psyent/pci5441/Kconfig" -source "board/psyent/pk1c20/Kconfig" endmenu diff --git a/arch/nios2/config.mk b/arch/nios2/config.mk index 82bd887..9b7c56d 100644 --- a/arch/nios2/config.mk +++ b/arch/nios2/config.mk @@ -17,3 +17,5 @@ PLATFORM_CPPFLAGS += -G0 LDFLAGS_FINAL += --gc-sections PLATFORM_RELFLAGS += -ffunction-sections -fdata-sections + +__HAVE_ARCH_GENERIC_BOARD := y diff --git a/arch/nios2/cpu/Makefile b/arch/nios2/cpu/Makefile index bdd983d..3fe7847 100644 --- a/arch/nios2/cpu/Makefile +++ b/arch/nios2/cpu/Makefile @@ -7,5 +7,5 @@ extra-y = start.o obj-y = exceptions.o -obj-y += cpu.o interrupts.o sysid.o traps.o epcs.o +obj-y += cpu.o interrupts.o sysid.o traps.o obj-y += fdt.o diff --git a/arch/nios2/cpu/cpu.c b/arch/nios2/cpu/cpu.c index e0dcbc2..86f94b7 100644 --- a/arch/nios2/cpu/cpu.c +++ b/arch/nios2/cpu/cpu.c @@ -10,11 +10,14 @@ #include <nios2-io.h> #include <asm/cache.h> +DECLARE_GLOBAL_DATA_PTR; + #if defined (CONFIG_SYS_NIOS_SYSID_BASE) extern void display_sysid (void); #endif /* CONFIG_SYS_NIOS_SYSID_BASE */ -int checkcpu (void) +#ifdef CONFIG_DISPLAY_CPUINFO +int print_cpuinfo(void) { printf ("CPU : Nios-II\n"); #if !defined(CONFIG_SYS_NIOS_SYSID_BASE) @@ -24,6 +27,7 @@ int checkcpu (void) #endif return (0); } +#endif /* CONFIG_DISPLAY_CPUINFO */ int do_reset(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) { @@ -47,3 +51,11 @@ void dcache_disable(void) { flush_dcache(CONFIG_SYS_DCACHE_SIZE, CONFIG_SYS_DCACHELINE_SIZE); } + +int arch_cpu_init(void) +{ + gd->cpu_clk = CONFIG_SYS_CLK_FREQ; + gd->ram_size = CONFIG_SYS_SDRAM_SIZE; + + return 0; +} diff --git a/arch/nios2/cpu/epcs.c b/arch/nios2/cpu/epcs.c deleted file mode 100644 index 9758552..0000000 --- a/arch/nios2/cpu/epcs.c +++ /dev/null @@ -1,717 +0,0 @@ -/* - * (C) Copyright 2004, Psyent Corporation <www.psyent.com> - * Scott McNutt <smcnutt@psyent.com> - * - * SPDX-License-Identifier: GPL-2.0+ - */ - -#include <common.h> - -#if defined(CONFIG_SYS_NIOS_EPCSBASE) -#include <command.h> -#include <asm/io.h> -#include <nios2-io.h> -#include <nios2-epcs.h> - - -/*-----------------------------------------------------------------------*/ -#define SHORT_HELP\ - "epcs - read/write Cyclone EPCS configuration device.\n" - -#define LONG_HELP\ - "\n"\ - "epcs erase start [end]\n"\ - " - erase sector start or sectors start through end.\n"\ - "epcs info\n"\ - " - display EPCS device information.\n"\ - "epcs protect on | off\n"\ - " - turn device protection on or off.\n"\ - "epcs read addr offset count\n"\ - " - read count bytes from offset to addr.\n"\ - "epcs write addr offset count\n"\ - " - write count bytes to offset from addr.\n"\ - "epcs verify addr offset count\n"\ - " - verify count bytes at offset from addr." - - -/*-----------------------------------------------------------------------*/ -/* Operation codes for serial configuration devices - */ -#define EPCS_WRITE_ENA 0x06 /* Write enable */ -#define EPCS_WRITE_DIS 0x04 /* Write disable */ -#define EPCS_READ_STAT 0x05 /* Read status */ -#define EPCS_READ_BYTES 0x03 /* Read bytes */ -#define EPCS_READ_ID 0xab /* Read silicon id */ -#define EPCS_WRITE_STAT 0x01 /* Write status */ -#define EPCS_WRITE_BYTES 0x02 /* Write bytes */ -#define EPCS_ERASE_BULK 0xc7 /* Erase entire device */ -#define EPCS_ERASE_SECT 0xd8 /* Erase sector */ - -/* Device status register bits - */ -#define EPCS_STATUS_WIP (1<<0) /* Write in progress */ -#define EPCS_STATUS_WEL (1<<1) /* Write enable latch */ - -/* Misc - */ -#define EPCS_TIMEOUT 100 /* 100 msec timeout */ - -static nios_spi_t *epcs = (nios_spi_t *)CONFIG_SYS_NIOS_EPCSBASE; - -/*********************************************************************** - * Device access - ***********************************************************************/ -static int epcs_cs (int assert) -{ - ulong start; - unsigned tmp; - - - if (assert) { - tmp = readl (&epcs->control); - writel (tmp | NIOS_SPI_SSO, &epcs->control); - } else { - /* Let all bits shift out */ - start = get_timer (0); - while ((readl (&epcs->status) & NIOS_SPI_TMT) == 0) - if (get_timer (start) > EPCS_TIMEOUT) - return (-1); - tmp = readl (&epcs->control); - writel (tmp & ~NIOS_SPI_SSO, &epcs->control); - } - return (0); -} - -static int epcs_tx (unsigned char c) -{ - ulong start; - - start = get_timer (0); - while ((readl (&epcs->status) & NIOS_SPI_TRDY) == 0) - if (get_timer (start) > EPCS_TIMEOUT) - return (-1); - writel (c, &epcs->txdata); - return (0); -} - -static int epcs_rx (void) -{ - ulong start; - - start = get_timer (0); - while ((readl (&epcs->status) & NIOS_SPI_RRDY) == 0) - if (get_timer (start) > EPCS_TIMEOUT) - return (-1); - return (readl (&epcs->rxdata)); -} - -static unsigned char bitrev[] = { - 0x00, 0x08, 0x04, 0x0c, 0x02, 0x0a, 0x06, 0x0e, - 0x01, 0x09, 0x05, 0x0d, 0x03, 0x0b, 0x07, 0x0f -}; - -static unsigned char epcs_bitrev (unsigned char c) -{ - unsigned char val; - - val = bitrev[c>>4]; - val |= bitrev[c & 0x0f]<<4; - return (val); -} - -static void epcs_rcv (unsigned char *dst, int len) -{ - while (len--) { - epcs_tx (0); - *dst++ = epcs_rx (); - } -} - -static void epcs_rrcv (unsigned char *dst, int len) -{ - while (len--) { - epcs_tx (0); - *dst++ = epcs_bitrev (epcs_rx ()); - } -} - -static void epcs_snd (unsigned char *src, int len) -{ - while (len--) { - epcs_tx (*src++); - epcs_rx (); - } -} - -static void epcs_rsnd (unsigned char *src, int len) -{ - while (len--) { - epcs_tx (epcs_bitrev (*src++)); - epcs_rx (); - } -} - -static void epcs_wr_enable (void) -{ - epcs_cs (1); - epcs_tx (EPCS_WRITE_ENA); - epcs_rx (); - epcs_cs (0); -} - -static unsigned char epcs_status_rd (void) -{ - unsigned char status; - - epcs_cs (1); - epcs_tx (EPCS_READ_STAT); - epcs_rx (); - epcs_tx (0); - status = epcs_rx (); - epcs_cs (0); - return (status); -} - -static void epcs_status_wr (unsigned char status) -{ - epcs_wr_enable (); - epcs_cs (1); - epcs_tx (EPCS_WRITE_STAT); - epcs_rx (); - epcs_tx (status); - epcs_rx (); - epcs_cs (0); - return; -} - -/*********************************************************************** - * Device information - ***********************************************************************/ - -static struct epcs_devinfo_t devinfo[] = { - { "EPCS1 ", 0x10, 17, 4, 15, 8, 0x0c }, - { "EPCS4 ", 0x12, 19, 8, 16, 8, 0x1c }, - { "EPCS16", 0x14, 21, 32, 16, 8, 0x1c }, - { "EPCS64", 0x16, 23,128, 16, 8, 0x1c }, - { 0, 0, 0, 0, 0, 0 } -}; - -int epcs_reset (void) -{ - /* When booting from an epcs controller, the epcs bootrom - * code may leave the slave select in an asserted state. - * This causes two problems: (1) The initial epcs access - * will fail -- not a big deal, and (2) a software reset - * will cause the bootrom code to hang since it does not - * ensure the select is negated prior to first access -- a - * big deal. Here we just negate chip select and everything - * gets better :-) - */ - epcs_cs (0); /* Negate chip select */ - return (0); -} - -epcs_devinfo_t *epcs_dev_find (void) -{ - unsigned char buf[4]; - unsigned char id; - int i; - struct epcs_devinfo_t *dev = NULL; - - /* Read silicon id requires 3 "dummy bytes" before it's put - * on the wire. - */ - buf[0] = EPCS_READ_ID; - buf[1] = 0; - buf[2] = 0; - buf[3] = 0; - - epcs_cs (1); - epcs_snd (buf,4); - epcs_rcv (buf,1); - if (epcs_cs (0) == -1) - return (NULL); - id = buf[0]; - - /* Find the info struct */ - i = 0; - while (devinfo[i].name) { - if (id == devinfo[i].id) { - dev = &devinfo[i]; - break; - } - i++; - } - - return (dev); -} - -/*********************************************************************** - * Misc Utilities - ***********************************************************************/ -int epcs_cfgsz (void) -{ - int sz = 0; - unsigned char buf[128]; - unsigned char *p; - struct epcs_devinfo_t *dev = epcs_dev_find (); - - if (!dev) - return (-1); - - /* Read in the first 128 bytes of the device */ - buf[0] = EPCS_READ_BYTES; - buf[1] = 0; - buf[2] = 0; - buf[3] = 0; - - epcs_cs (1); - epcs_snd (buf,4); - epcs_rrcv (buf, sizeof(buf)); - epcs_cs (0); - - /* Search for the starting 0x6a which is followed by the - * 4-byte 'register' and 4-byte bit-count. - */ - p = buf; - while (p < buf + sizeof(buf)-8) { - if ( *p == 0x6a ) { - /* Point to bit count and extract */ - p += 5; - sz = *p++; - sz |= *p++ << 8; - sz |= *p++ << 16; - sz |= *p++ << 24; - /* Convert to byte count */ - sz += 7; - sz >>= 3; - } else if (*p == 0xff) { - /* 0xff is ok ... just skip */ - p++; - continue; - } else { - /* Not 0xff or 0x6a ... something's not - * right ... report 'unknown' (sz=0). - */ - break; - } - } - return (sz); -} - -int epcs_erase (unsigned start, unsigned end) -{ - unsigned off, sectsz; - unsigned char buf[4]; - struct epcs_devinfo_t *dev = epcs_dev_find (); - - if (!dev || (start>end)) - return (-1); - - /* Erase the requested sectors. An address is required - * that lies within the requested sector -- we'll just - * use the first address in the sector. - */ - printf ("epcs erasing sector %d ", start); - if (start != end) - printf ("to %d ", end); - sectsz = (1 << dev->sz_sect); - while (start <= end) { - off = start * sectsz; - start++; - - buf[0] = EPCS_ERASE_SECT; - buf[1] = off >> 16; - buf[2] = off >> 8; - buf[3] = off; - - epcs_wr_enable (); - epcs_cs (1); - epcs_snd (buf,4); - epcs_cs (0); - - printf ("."); /* Some user feedback */ - - /* Wait for erase to complete */ - while (epcs_status_rd() & EPCS_STATUS_WIP) - ; - } - printf (" done.\n"); - return (0); -} - -int epcs_read (ulong addr, ulong off, ulong cnt) -{ - unsigned char buf[4]; - struct epcs_devinfo_t *dev = epcs_dev_find (); - - if (!dev) - return (-1); - - buf[0] = EPCS_READ_BYTES; - buf[1] = off >> 16; - buf[2] = off >> 8; - buf[3] = off; - - epcs_cs (1); - epcs_snd (buf,4); - epcs_rrcv ((unsigned char *)addr, cnt); - epcs_cs (0); - - return (0); -} - -int epcs_write (ulong addr, ulong off, ulong cnt) -{ - ulong wrcnt; - unsigned pgsz; - unsigned char buf[4]; - struct epcs_devinfo_t *dev = epcs_dev_find (); - - if (!dev) - return (-1); - - pgsz = (1<<dev->sz_page); - while (cnt) { - if (off % pgsz) - wrcnt = pgsz - (off % pgsz); - else - wrcnt = pgsz; - wrcnt = (wrcnt > cnt) ? cnt : wrcnt; - - buf[0] = EPCS_WRITE_BYTES; - buf[1] = off >> 16; - buf[2] = off >> 8; - buf[3] = off; - - epcs_wr_enable (); - epcs_cs (1); - epcs_snd (buf,4); - epcs_rsnd ((unsigned char *)addr, wrcnt); - epcs_cs (0); - - /* Wait for write to complete */ - while (epcs_status_rd() & EPCS_STATUS_WIP) - ; - - cnt -= wrcnt; - off += wrcnt; - addr += wrcnt; - } - - return (0); -} - -int epcs_verify (ulong addr, ulong off, ulong cnt, ulong *err) -{ - ulong rdcnt; - unsigned char buf[256]; - unsigned char *start,*end; - int i; - - start = end = (unsigned char *)addr; - while (cnt) { - rdcnt = (cnt>sizeof(buf)) ? sizeof(buf) : cnt; - epcs_read ((ulong)buf, off, rdcnt); - for (i=0; i<rdcnt; i++) { - if (*end != buf[i]) { - *err = end - start; - return(-1); - } - end++; - } - cnt -= rdcnt; - off += rdcnt; - } - return (0); -} - -static int epcs_sect_erased (int sect, unsigned *offset, - struct epcs_devinfo_t *dev) -{ - unsigned char buf[128]; - unsigned off, end; - unsigned sectsz; - int i; - - sectsz = (1 << dev->sz_sect); - off = sectsz * sect; - end = off + sectsz; - - while (off < end) { - epcs_read ((ulong)buf, off, sizeof(buf)); - for (i=0; i < sizeof(buf); i++) { - if (buf[i] != 0xff) { - *offset = off + i; - return (0); - } - } - off += sizeof(buf); - } - return (1); -} - - -/*********************************************************************** - * Commands - ***********************************************************************/ -static -void do_epcs_info (struct epcs_devinfo_t *dev, int argc, char * const argv[]) -{ - int i; - unsigned char stat; - unsigned tmp; - int erased; - - /* Basic device info */ - printf ("%s: %d kbytes (%d sectors x %d kbytes," - " %d bytes/page)\n", - dev->name, 1 << (dev->size-10), - dev->num_sects, 1 << (dev->sz_sect-10), - 1 << dev->sz_page ); - - /* Status -- for now protection is all-or-nothing */ - stat = epcs_status_rd(); - printf ("status: 0x%02x (WIP:%d, WEL:%d, PROT:%s)\n", - stat, - (stat & EPCS_STATUS_WIP) ? 1 : 0, - (stat & EPCS_STATUS_WEL) ? 1 : 0, - (stat & dev->prot_mask) ? "on" : "off" ); - - /* Configuration */ - tmp = epcs_cfgsz (); - if (tmp) { - printf ("config: 0x%06x (%d) bytes\n", tmp, tmp ); - } else { - printf ("config: unknown\n" ); - } - - /* Sector info */ - for (i=0; (i < dev->num_sects) && (argc > 1); i++) { - erased = epcs_sect_erased (i, &tmp, dev); - if ((i & 0x03) == 0) printf ("\n"); - printf ("%4d: %07x ", - i, i*(1<<dev->sz_sect) ); - if (erased) - printf ("E "); - else - printf (" "); - } - printf ("\n"); - - return; -} - -static -void do_epcs_erase (struct epcs_devinfo_t *dev, int argc, char * const argv[]) -{ - unsigned start,end; - - if ((argc < 3) || (argc > 4)) { - printf ("USAGE: epcs erase sect [end]\n"); - return; - } - if ((epcs_status_rd() & dev->prot_mask) != 0) { - printf ( "epcs: device protected.\n"); - return; - } - - start = simple_strtoul (argv[2], NULL, 10); - if (argc > 3) - end = simple_strtoul (argv[3], NULL, 10); - else - end = start; - if ((start >= dev->num_sects) || (start > end)) { - printf ("epcs: invalid sector range: [%d:%d]\n", - start, end ); - return; - } - - epcs_erase (start, end); - - return; -} - -static -void do_epcs_protect (struct epcs_devinfo_t *dev, int argc, char * const argv[]) -{ - unsigned char stat; - - /* For now protection is all-or-nothing to keep things - * simple. The protection bits don't map in a linear - * fashion ... and we would rather protect the bottom - * of the device since it contains the config data and - * leave the top unprotected for app use. But unfortunately - * protection works from top-to-bottom so it does - * really help very much from a software app point-of-view. - */ - if (argc < 3) { - printf ("USAGE: epcs protect on | off\n"); - return; - } - if (!dev) - return; - - /* Protection on/off is just a matter of setting/clearing - * all protection bits in the status register. - */ - stat = epcs_status_rd (); - if (strcmp ("on", argv[2]) == 0) { - stat |= dev->prot_mask; - } else if (strcmp ("off", argv[2]) == 0 ) { - stat &= ~dev->prot_mask; - } else { - printf ("epcs: unknown protection: %s\n", argv[2]); - return; - } - epcs_status_wr (stat); - return; -} - -static -void do_epcs_read (struct epcs_devinfo_t *dev, int argc, char * const argv[]) -{ - ulong addr,off,cnt; - ulong sz; - - if (argc < 5) { - printf ("USAGE: epcs read addr offset count\n"); - return; - } - - sz = 1 << dev->size; - addr = simple_strtoul (argv[2], NULL, 16); - off = simple_strtoul (argv[3], NULL, 16); - cnt = simple_strtoul (argv[4], NULL, 16); - if (off > sz) { - printf ("offset is greater than device size" - "... aborting.\n"); - return; - } - if ((off + cnt) > sz) { - printf ("request exceeds device size" - "... truncating.\n"); - cnt = sz - off; - } - printf ("epcs: read %08lx <- %06lx (0x%lx bytes)\n", - addr, off, cnt); - epcs_read (addr, off, cnt); - - return; -} - -static -void do_epcs_write (struct epcs_devinfo_t *dev, int argc, char * const argv[]) -{ - ulong addr,off,cnt; - ulong sz; - ulong err; - - if (argc < 5) { - printf ("USAGE: epcs write addr offset count\n"); - return; - } - if ((epcs_status_rd() & dev->prot_mask) != 0) { - printf ( "epcs: device protected.\n"); - return; - } - - sz = 1 << dev->size; - addr = simple_strtoul (argv[2], NULL, 16); - off = simple_strtoul (argv[3], NULL, 16); - cnt = simple_strtoul (argv[4], NULL, 16); - if (off > sz) { - printf ("offset is greater than device size" - "... aborting.\n"); - return; - } - if ((off + cnt) > sz) { - printf ("request exceeds device size" - "... truncating.\n"); - cnt = sz - off; - } - printf ("epcs: write %08lx -> %06lx (0x%lx bytes)\n", - addr, off, cnt); - epcs_write (addr, off, cnt); - if (epcs_verify (addr, off, cnt, &err) != 0) - printf ("epcs: write error at offset %06lx\n", err); - - return; -} - -static -void do_epcs_verify (struct epcs_devinfo_t *dev, int argc, char * const argv[]) -{ - ulong addr,off,cnt; - ulong sz; - ulong err; - - if (argc < 5) { - printf ("USAGE: epcs verify addr offset count\n"); - return; - } - - sz = 1 << dev->size; - addr = simple_strtoul (argv[2], NULL, 16); - off = simple_strtoul (argv[3], NULL, 16); - cnt = simple_strtoul (argv[4], NULL, 16); - if (off > sz) { - printf ("offset is greater than device size" - "... aborting.\n"); - return; - } - if ((off + cnt) > sz) { - printf ("request exceeds device size" - "... truncating.\n"); - cnt = sz - off; - } - printf ("epcs: verify %08lx -> %06lx (0x%lx bytes)\n", - addr, off, cnt); - if (epcs_verify (addr, off, cnt, &err) != 0) - printf ("epcs: verify error at offset %06lx\n", err); - - return; -} - -/*-----------------------------------------------------------------------*/ -int do_epcs (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) -{ - int len; - struct epcs_devinfo_t *dev = epcs_dev_find (); - - if (!dev) { - printf ("epcs: device not found.\n"); - return (-1); - } - - if (argc < 2) { - do_epcs_info (dev, argc, argv); - return (0); - } - - len = strlen (argv[1]); - if (strncmp ("info", argv[1], len) == 0) { - do_epcs_info (dev, argc, argv); - } else if (strncmp ("erase", argv[1], len) == 0) { - do_epcs_erase (dev, argc, argv); - } else if (strncmp ("protect", argv[1], len) == 0) { - do_epcs_protect (dev, argc, argv); - } else if (strncmp ("read", argv[1], len) == 0) { - do_epcs_read (dev, argc, argv); - } else if (strncmp ("write", argv[1], len) == 0) { - do_epcs_write (dev, argc, argv); - } else if (strncmp ("verify", argv[1], len) == 0) { - do_epcs_verify (dev, argc, argv); - } else { - printf ("epcs: unknown operation: %s\n", argv[1]); - } - - return (0); -} - -/*-----------------------------------------------------------------------*/ - - -U_BOOT_CMD( epcs, 5, 0, do_epcs, SHORT_HELP, LONG_HELP ); - -#endif /* CONFIG_NIOS_EPCS */ diff --git a/arch/nios2/cpu/start.S b/arch/nios2/cpu/start.S index 7ce0d34..6af9b4e 100644 --- a/arch/nios2/cpu/start.S +++ b/arch/nios2/cpu/start.S @@ -134,11 +134,12 @@ _reloc: mov fp, sp /* - * Call board_init -- never returns + * Call board_init_f -- never returns */ - movhi r4, %hi(board_init@h) - ori r4, r4, %lo(board_init@h) - callr r4 + mov r4, r0 + movhi r2, %hi(board_init_f@h) + ori r2, r2, %lo(board_init_f@h) + callr r2 /* NEVER RETURNS -- but branch to the _start just * in case ;-) @@ -146,6 +147,31 @@ _reloc: br _start + +/* + * relocate_code -- Nios2 handles the relocation above. But + * the generic board code monkeys with the heap, stack, etc. + * (it makes some assumptions that may not be appropriate + * for Nios). Nevertheless, we capitulate here. + * + * We'll call the board_init_r from here since this isn't + * supposed to return. + * + * void relocate_code (ulong sp, gd_t *global_data, + * ulong reloc_addr) + * __attribute__ ((noreturn)); + */ + .text + .global relocate_code + +relocate_code: + mov sp, r4 /* Set the new sp */ + mov r4, r5 + movhi r8, %hi(board_init_r@h) + ori r8, r8, %lo(board_init_r@h) + callr r8 + ret + /* * dly_clks -- Nios2 (like Nios1) doesn't have a timebase in * the core. For simple delay loops, we do our best by counting diff --git a/arch/nios2/include/asm/config.h b/arch/nios2/include/asm/config.h index cd29734..476a32b 100644 --- a/arch/nios2/include/asm/config.h +++ b/arch/nios2/include/asm/config.h @@ -7,4 +7,7 @@ #ifndef _ASM_CONFIG_H_ #define _ASM_CONFIG_H_ +#define CONFIG_SYS_GENERIC_BOARD +#define CONFIG_SYS_GENERIC_GLOBAL_DATA + #endif diff --git a/arch/nios2/include/asm/posix_types.h b/arch/nios2/include/asm/posix_types.h index 6733640..6b6c39b 100644 --- a/arch/nios2/include/asm/posix_types.h +++ b/arch/nios2/include/asm/posix_types.h @@ -16,7 +16,11 @@ typedef int __kernel_pid_t; typedef unsigned short __kernel_ipc_pid_t; typedef unsigned short __kernel_uid_t; typedef unsigned short __kernel_gid_t; +#ifdef __GNUC__ +typedef __SIZE_TYPE__ __kernel_size_t; +#else typedef unsigned long __kernel_size_t; +#endif typedef long __kernel_ssize_t; typedef int __kernel_ptrdiff_t; typedef long __kernel_time_t; diff --git a/arch/nios2/include/asm/u-boot.h b/arch/nios2/include/asm/u-boot.h index 51f6c30..cb02e98 100644 --- a/arch/nios2/include/asm/u-boot.h +++ b/arch/nios2/include/asm/u-boot.h @@ -15,15 +15,7 @@ #ifndef __ASM_NIOS2_U_BOOT_H_ #define __ASM_NIOS2_U_BOOT_H_ -typedef struct bd_info { - unsigned long bi_memstart; /* start of DRAM memory */ - phys_size_t bi_memsize; /* size of DRAM memory in bytes */ - unsigned long bi_flashstart; /* start of FLASH memory */ - unsigned long bi_flashsize; /* size of FLASH memory */ - unsigned long bi_flashoffset; /* reserved area for startup monitor */ - unsigned long bi_sramstart; /* start of SRAM memory */ - unsigned long bi_sramsize; /* size of SRAM memory */ -} bd_t; +#include <asm-generic/u-boot.h> /* For image.h:image_check_target_arch() */ #define IH_ARCH_DEFAULT IH_ARCH_NIOS2 diff --git a/arch/nios2/lib/Makefile b/arch/nios2/lib/Makefile index 7cb25c0..079378a 100644 --- a/arch/nios2/lib/Makefile +++ b/arch/nios2/lib/Makefile @@ -6,7 +6,6 @@ # obj-y += cache.o -obj-y += board.o obj-$(CONFIG_CMD_BOOTM) += bootm.o obj-y += libgcc.o obj-y += time.o diff --git a/arch/nios2/lib/board.c b/arch/nios2/lib/board.c deleted file mode 100644 index f24218f..0000000 --- a/arch/nios2/lib/board.c +++ /dev/null @@ -1,147 +0,0 @@ -/* - * (C) Copyright 2003, Psyent Corporation <www.psyent.com> - * Scott McNutt <smcnutt@psyent.com> - * - * (C) Copyright 2000-2002 - * Wolfgang Denk, DENX Software Engineering, wd@denx.de. - * - * SPDX-License-Identifier: GPL-2.0+ - */ - -#include <common.h> -#include <stdio_dev.h> -#include <watchdog.h> -#include <malloc.h> -#include <mmc.h> -#include <net.h> -#ifdef CONFIG_STATUS_LED -#include <status_led.h> -#endif -#if defined(CONFIG_SYS_NIOS_EPCSBASE) -#include <nios2-epcs.h> -#endif -#ifdef CONFIG_CMD_NAND -#include <nand.h> /* cannot even include nand.h if it isnt configured */ -#endif - -DECLARE_GLOBAL_DATA_PTR; - -/* - * All attempts to come up with a "common" initialization sequence - * that works for all boards and architectures failed: some of the - * requirements are just _too_ different. To get rid of the resulting - * mess of board dependend #ifdef'ed code we now make the whole - * initialization sequence configurable to the user. - * - * The requirements for any new initalization function is simple: it - * receives a pointer to the "global data" structure as it's only - * argument, and returns an integer return code, where 0 means - * "continue" and != 0 means "fatal error, hang the system". - */ - - -typedef int (init_fnc_t) (void); - - -/************************************************************************ - * Initialization sequence * - ***********************************************************************/ - -init_fnc_t *init_sequence[] = { -#if defined(CONFIG_BOARD_EARLY_INIT_F) - board_early_init_f, /* Call board-specific init code early.*/ -#endif -#if defined(CONFIG_SYS_NIOS_EPCSBASE) - epcs_reset, -#endif - - env_init, - serial_init, - console_init_f, - display_options, - checkcpu, - checkboard, - NULL, /* Terminate this list */ -}; - - -/***********************************************************************/ -void board_init(void) -{ - bd_t *bd; - init_fnc_t **init_fnc_ptr; - static gd_t gd_data; - static bd_t bd_data; - - /* Pointer is writable since we allocated a register for it. */ - gd = &gd_data; - /* compiler optimization barrier needed for GCC >= 3.4 */ - __asm__ __volatile__("" : : : "memory"); - - gd->bd = &bd_data; - gd->baudrate = CONFIG_BAUDRATE; - gd->cpu_clk = CONFIG_SYS_CLK_FREQ; - - bd = gd->bd; - bd->bi_memstart = CONFIG_SYS_SDRAM_BASE; - bd->bi_memsize = CONFIG_SYS_SDRAM_SIZE; -#ifndef CONFIG_SYS_NO_FLASH - bd->bi_flashstart = CONFIG_SYS_FLASH_BASE; -#endif -#if defined(CONFIG_SYS_SRAM_BASE) && defined(CONFIG_SYS_SRAM_SIZE) - bd->bi_sramstart = CONFIG_SYS_SRAM_BASE; - bd->bi_sramsize = CONFIG_SYS_SRAM_SIZE; -#endif - - for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) { - WATCHDOG_RESET(); - if ((*init_fnc_ptr) () != 0) - hang(); - } - - WATCHDOG_RESET(); - - /* The Malloc area is immediately below the monitor copy in RAM */ - mem_malloc_init(CONFIG_SYS_MALLOC_BASE, CONFIG_SYS_MALLOC_LEN); - -#ifndef CONFIG_SYS_NO_FLASH - WATCHDOG_RESET(); - bd->bi_flashsize = flash_init(); -#endif - -#ifdef CONFIG_CMD_NAND - puts("NAND: "); - nand_init(); -#endif - -#ifdef CONFIG_GENERIC_MMC - puts("MMC: "); - mmc_initialize(bd); -#endif - - WATCHDOG_RESET(); - env_relocate(); - - WATCHDOG_RESET(); - stdio_init(); - jumptable_init(); - console_init_r(); - - WATCHDOG_RESET(); - interrupt_init(); - -#if defined(CONFIG_BOARD_LATE_INIT) - board_late_init(); -#endif - -#if defined(CONFIG_CMD_NET) - puts("Net: "); - eth_initialize(bd); -#endif - - /* main_loop */ - for (;;) { - WATCHDOG_RESET(); - main_loop(); - } -} diff --git a/arch/powerpc/cpu/mpc85xx/cpu_init.c b/arch/powerpc/cpu/mpc85xx/cpu_init.c index b237505..5bfab70 100644 --- a/arch/powerpc/cpu/mpc85xx/cpu_init.c +++ b/arch/powerpc/cpu/mpc85xx/cpu_init.c @@ -254,12 +254,36 @@ static void enable_tdm_law(void) void enable_cpc(void) { int i; + int ret; u32 size = 0; - + u32 cpccfg0; + char buffer[HWCONFIG_BUFFER_SIZE]; + char cpc_subarg[16]; + bool have_hwconfig = false; + int cpc_args = 0; cpc_corenet_t *cpc = (cpc_corenet_t *)CONFIG_SYS_FSL_CPC_ADDR; + /* Extract hwconfig from environment */ + ret = getenv_f("hwconfig", buffer, sizeof(buffer)); + if (ret > 0) { + /* + * If "en_cpc" is not defined in hwconfig then by default all + * cpcs are enable. If this config is defined then individual + * cpcs which have to be enabled should also be defined. + * e.g en_cpc:cpc1,cpc2; + */ + if (hwconfig_f("en_cpc", buffer)) + have_hwconfig = true; + } + for (i = 0; i < CONFIG_SYS_NUM_CPC; i++, cpc++) { - u32 cpccfg0 = in_be32(&cpc->cpccfg0); + if (have_hwconfig) { + sprintf(cpc_subarg, "cpc%u", i + 1); + cpc_args = hwconfig_sub_f("en_cpc", cpc_subarg, buffer); + if (cpc_args == 0) + continue; + } + cpccfg0 = in_be32(&cpc->cpccfg0); size += CPC_CFG0_SZ_K(cpccfg0); #ifdef CONFIG_SYS_FSL_ERRATUM_CPC_A002 diff --git a/arch/powerpc/cpu/mpc85xx/fdt.c b/arch/powerpc/cpu/mpc85xx/fdt.c index 3665ec6..3222e26 100644 --- a/arch/powerpc/cpu/mpc85xx/fdt.c +++ b/arch/powerpc/cpu/mpc85xx/fdt.c @@ -134,6 +134,21 @@ void ft_fixup_cpu(void *blob, u64 memory_limit) printf("Failed to reserve memory for spin table: %s\n", fdt_strerror(off)); } +#ifdef CONFIG_DEEP_SLEEP +#ifdef CONFIG_SPL_MMC_BOOT + off = fdt_add_mem_rsv(blob, CONFIG_SYS_MMC_U_BOOT_START, + CONFIG_SYS_MMC_U_BOOT_SIZE); + if (off < 0) + printf("Failed to reserve memory for SD deep sleep: %s\n", + fdt_strerror(off)); +#elif defined(CONFIG_SPL_SPI_BOOT) + off = fdt_add_mem_rsv(blob, CONFIG_SYS_SPI_FLASH_U_BOOT_START, + CONFIG_SYS_SPI_FLASH_U_BOOT_SIZE); + if (off < 0) + printf("Failed to reserve memory for SPI deep sleep: %s\n", + fdt_strerror(off)); +#endif +#endif } #endif diff --git a/arch/powerpc/cpu/mpc8xx/Kconfig b/arch/powerpc/cpu/mpc8xx/Kconfig index 35608a6..2c39244 100644 --- a/arch/powerpc/cpu/mpc8xx/Kconfig +++ b/arch/powerpc/cpu/mpc8xx/Kconfig @@ -14,12 +14,6 @@ config TARGET_COGENT_MPC8XX config TARGET_ESTEEM192E bool "Support ESTEEM192E" -config TARGET_FLAGADM - bool "Support FLAGADM" - -config TARGET_GEN860T - bool "Support GEN860T" - config TARGET_HERMES bool "Support hermes" @@ -47,15 +41,9 @@ config TARGET_R360MPI config TARGET_RRVISION bool "Support RRvision" -config TARGET_SXNI855T - bool "Support SXNI855T" - config TARGET_SPD823TS bool "Support SPD823TS" -config TARGET_SVM_SC8XX - bool "Support svm_sc8xx" - config TARGET_MHPC bool "Support MHPC" @@ -74,9 +62,6 @@ config TARGET_ELPT860 config TARGET_UC100 bool "Support uc100" -config TARGET_STXXTC - bool "Support stxxtc" - config TARGET_FPS850L bool "Support FPS850L" @@ -139,8 +124,6 @@ source "board/cogent/Kconfig" source "board/eltec/mhpc/Kconfig" source "board/emk/top860/Kconfig" source "board/esteem192e/Kconfig" -source "board/flagadm/Kconfig" -source "board/gen860t/Kconfig" source "board/hermes/Kconfig" source "board/icu862/Kconfig" source "board/ip860/Kconfig" @@ -151,10 +134,7 @@ source "board/lwmon/Kconfig" source "board/manroland/uc100/Kconfig" source "board/netvia/Kconfig" source "board/r360mpi/Kconfig" -source "board/sixnet/Kconfig" source "board/spd8xx/Kconfig" -source "board/stx/stxxtc/Kconfig" -source "board/svm_sc8xx/Kconfig" source "board/tqc/tqm8xx/Kconfig" endmenu diff --git a/arch/powerpc/cpu/mpc8xx/cpu_init.c b/arch/powerpc/cpu/mpc8xx/cpu_init.c index e51fec7..90c7e61 100644 --- a/arch/powerpc/cpu/mpc8xx/cpu_init.c +++ b/arch/powerpc/cpu/mpc8xx/cpu_init.c @@ -44,11 +44,7 @@ void cpu_init_f (volatile immap_t * immr) #endif /* CONFIG_WATCHDOG */ /* SIUMCR - contains debug pin configuration (11-6) */ -#ifndef CONFIG_SVM_SC8xx immr->im_siu_conf.sc_siumcr |= CONFIG_SYS_SIUMCR; -#else - immr->im_siu_conf.sc_siumcr = CONFIG_SYS_SIUMCR; -#endif /* initialize timebase status and control register (11-26) */ /* unlock TBSCRK */ diff --git a/arch/powerpc/lib/board.c b/arch/powerpc/lib/board.c index 0296205..6eaab88 100644 --- a/arch/powerpc/lib/board.c +++ b/arch/powerpc/lib/board.c @@ -366,6 +366,8 @@ void board_init_f(ulong bootflag) memset((void *) gd, 0, sizeof(gd_t)); #endif + gd->flags = bootflag; + for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) if ((*init_fnc_ptr) () != 0) hang(); diff --git a/board/aristainetos/Kconfig b/board/aristainetos/Kconfig new file mode 100644 index 0000000..58078ea --- /dev/null +++ b/board/aristainetos/Kconfig @@ -0,0 +1,19 @@ +if TARGET_ARISTAINETOS + +config SYS_CPU + string + default "armv7" + +config SYS_BOARD + string + default "aristainetos" + +config SYS_SOC + string + default "mx6" + +config SYS_CONFIG_NAME + string + default "aristainetos" + +endif diff --git a/board/aristainetos/MAINTAINERS b/board/aristainetos/MAINTAINERS new file mode 100644 index 0000000..d45d423 --- /dev/null +++ b/board/aristainetos/MAINTAINERS @@ -0,0 +1,6 @@ +ARISTAINETOS BOARD +M: Heiko Schocher <hs@denx.de> +S: Maintained +F: board/aristainetos/ +F: include/configs/aristainetos.h +F: configs/aristainetos_defconfig diff --git a/board/aristainetos/Makefile b/board/aristainetos/Makefile new file mode 100644 index 0000000..5de48bc --- /dev/null +++ b/board/aristainetos/Makefile @@ -0,0 +1,9 @@ +# +# Copyright (C) 2007, Guennadi Liakhovetski <lg@denx.de> +# +# (C) Copyright 2011 Freescale Semiconductor, Inc. +# +# SPDX-License-Identifier: GPL-2.0+ +# + +obj-y := aristainetos.o diff --git a/board/aristainetos/aristainetos.c b/board/aristainetos/aristainetos.c new file mode 100644 index 0000000..3bfcf5b --- /dev/null +++ b/board/aristainetos/aristainetos.c @@ -0,0 +1,519 @@ +/* + * (C) Copyright 2014 + * Heiko Schocher, DENX Software Engineering, hs@denx.de. + * + * Based on: + * Copyright (C) 2012 Freescale Semiconductor, Inc. + * + * Author: Fabio Estevam <fabio.estevam@freescale.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <asm/arch/clock.h> +#include <asm/arch/imx-regs.h> +#include <asm/arch/iomux.h> +#include <asm/arch/mx6-pins.h> +#include <asm/errno.h> +#include <asm/gpio.h> +#include <asm/imx-common/iomux-v3.h> +#include <asm/imx-common/boot_mode.h> +#include <asm/imx-common/mxc_i2c.h> +#include <asm/imx-common/video.h> +#include <mmc.h> +#include <fsl_esdhc.h> +#include <miiphy.h> +#include <netdev.h> +#include <asm/arch/mxc_hdmi.h> +#include <asm/arch/crm_regs.h> +#include <linux/fb.h> +#include <ipu_pixfmt.h> +#include <asm/io.h> +#include <asm/arch/sys_proto.h> +#include <pwm.h> + +DECLARE_GLOBAL_DATA_PTR; + +#define UART_PAD_CTRL (PAD_CTL_PUS_100K_UP | \ + PAD_CTL_SPEED_MED | PAD_CTL_DSE_40ohm | \ + PAD_CTL_SRE_FAST | PAD_CTL_HYS) + +#define USDHC_PAD_CTRL (PAD_CTL_PUS_47K_UP | \ + PAD_CTL_SPEED_LOW | PAD_CTL_DSE_80ohm | \ + PAD_CTL_SRE_FAST | PAD_CTL_HYS) + +#define ENET_PAD_CTRL (PAD_CTL_PUS_100K_UP | \ + PAD_CTL_SPEED_MED | PAD_CTL_DSE_40ohm | PAD_CTL_HYS) + +#define SPI_PAD_CTRL (PAD_CTL_HYS | PAD_CTL_SPEED_MED | \ + PAD_CTL_DSE_40ohm | PAD_CTL_SRE_FAST) + +#define I2C_PAD_CTRL (PAD_CTL_PUS_100K_UP | \ + PAD_CTL_SPEED_MED | PAD_CTL_DSE_40ohm | PAD_CTL_HYS | \ + PAD_CTL_ODE | PAD_CTL_SRE_FAST) + +#define PC MUX_PAD_CTRL(I2C_PAD_CTRL) + +#define DISP_PAD_CTRL (0x10) + +#define ECSPI4_CS1 IMX_GPIO_NR(5, 2) + +struct i2c_pads_info i2c_pad_info1 = { + .scl = { + .i2c_mode = MX6_PAD_CSI0_DAT9__I2C1_SCL | PC, + .gpio_mode = MX6_PAD_CSI0_DAT9__GPIO5_IO27 | PC, + .gp = IMX_GPIO_NR(5, 27) + }, + .sda = { + .i2c_mode = MX6_PAD_CSI0_DAT8__I2C1_SDA | PC, + .gpio_mode = MX6_PAD_CSI0_DAT8__GPIO5_IO26 | PC, + .gp = IMX_GPIO_NR(5, 26) + } +}; + +struct i2c_pads_info i2c_pad_info2 = { + .scl = { + .i2c_mode = MX6_PAD_KEY_COL3__I2C2_SCL | PC, + .gpio_mode = MX6_PAD_KEY_COL3__GPIO4_IO12 | PC, + .gp = IMX_GPIO_NR(4, 12) + }, + .sda = { + .i2c_mode = MX6_PAD_KEY_ROW3__I2C2_SDA | PC, + .gpio_mode = MX6_PAD_KEY_ROW3__GPIO4_IO13 | PC, + .gp = IMX_GPIO_NR(4, 13) + } +}; + +struct i2c_pads_info i2c_pad_info3 = { + .scl = { + .i2c_mode = MX6_PAD_EIM_D17__I2C3_SCL | PC, + .gpio_mode = MX6_PAD_EIM_D17__GPIO3_IO17 | PC, + .gp = IMX_GPIO_NR(3, 17) + }, + .sda = { + .i2c_mode = MX6_PAD_EIM_D18__I2C3_SDA | PC, + .gpio_mode = MX6_PAD_EIM_D18__GPIO3_IO18 | PC, + .gp = IMX_GPIO_NR(3, 18) + } +}; + +int dram_init(void) +{ + gd->ram_size = get_ram_size((void *)PHYS_SDRAM, PHYS_SDRAM_SIZE); + + return 0; +} + +iomux_v3_cfg_t const uart1_pads[] = { + MX6_PAD_CSI0_DAT10__UART1_TX_DATA | MUX_PAD_CTRL(UART_PAD_CTRL), + MX6_PAD_CSI0_DAT11__UART1_RX_DATA | MUX_PAD_CTRL(UART_PAD_CTRL), +}; + +iomux_v3_cfg_t const uart5_pads[] = { + MX6_PAD_CSI0_DAT14__UART5_TX_DATA | MUX_PAD_CTRL(UART_PAD_CTRL), + MX6_PAD_CSI0_DAT15__UART5_RX_DATA | MUX_PAD_CTRL(UART_PAD_CTRL), +}; + +iomux_v3_cfg_t const gpio_pads[] = { + /* LED enable */ + MX6_PAD_SD4_DAT5__GPIO2_IO13 | MUX_PAD_CTRL(NO_PAD_CTRL), + /* spi flash WP protect */ + MX6_PAD_SD4_DAT7__GPIO2_IO15 | MUX_PAD_CTRL(NO_PAD_CTRL), + /* backlight enable */ + MX6_PAD_GPIO_2__GPIO1_IO02 | MUX_PAD_CTRL(NO_PAD_CTRL), + /* LED yellow */ + MX6_PAD_GPIO_3__GPIO1_IO03 | MUX_PAD_CTRL(NO_PAD_CTRL), + /* LED red */ + MX6_PAD_GPIO_4__GPIO1_IO04 | MUX_PAD_CTRL(NO_PAD_CTRL), + /* LED green */ + MX6_PAD_GPIO_5__GPIO1_IO05 | MUX_PAD_CTRL(NO_PAD_CTRL), + /* LED blue */ + MX6_PAD_GPIO_6__GPIO1_IO06 | MUX_PAD_CTRL(NO_PAD_CTRL), + /* i2c4 scl */ + MX6_PAD_GPIO_7__GPIO1_IO07 | MUX_PAD_CTRL(NO_PAD_CTRL), + /* i2c4 sda */ + MX6_PAD_GPIO_8__GPIO1_IO08 | MUX_PAD_CTRL(NO_PAD_CTRL), + /* spi CS 1 */ + MX6_PAD_EIM_A25__GPIO5_IO02 | MUX_PAD_CTRL(NO_PAD_CTRL), +}; + +static iomux_v3_cfg_t const misc_pads[] = { + MX6_PAD_GPIO_1__USB_OTG_ID | MUX_PAD_CTRL(NO_PAD_CTRL), + /* OTG Power enable */ + MX6_PAD_EIM_D31__GPIO3_IO31 | MUX_PAD_CTRL(NO_PAD_CTRL), + MX6_PAD_KEY_ROW4__GPIO4_IO15 | MUX_PAD_CTRL(NO_PAD_CTRL), +}; + +iomux_v3_cfg_t const enet_pads[] = { + MX6_PAD_GPIO_16__ENET_REF_CLK | MUX_PAD_CTRL(0x4001b0a8), + MX6_PAD_ENET_MDIO__ENET_MDIO | MUX_PAD_CTRL(ENET_PAD_CTRL), + MX6_PAD_ENET_MDC__ENET_MDC | MUX_PAD_CTRL(ENET_PAD_CTRL), + MX6_PAD_ENET_TXD0__ENET_TX_DATA0 | MUX_PAD_CTRL(ENET_PAD_CTRL), + MX6_PAD_ENET_TXD1__ENET_TX_DATA1 | MUX_PAD_CTRL(ENET_PAD_CTRL), + MX6_PAD_ENET_TX_EN__ENET_TX_EN | MUX_PAD_CTRL(ENET_PAD_CTRL), + MX6_PAD_ENET_RX_ER__ENET_RX_ER | MUX_PAD_CTRL(ENET_PAD_CTRL), + MX6_PAD_ENET_RXD0__ENET_RX_DATA0 | MUX_PAD_CTRL(ENET_PAD_CTRL), + MX6_PAD_ENET_RXD1__ENET_RX_DATA1 | MUX_PAD_CTRL(ENET_PAD_CTRL), + MX6_PAD_ENET_CRS_DV__ENET_RX_EN | MUX_PAD_CTRL(ENET_PAD_CTRL), +}; + +static void setup_iomux_enet(void) +{ + struct iomuxc *iomux = (struct iomuxc *)IOMUXC_BASE_ADDR; + + imx_iomux_v3_setup_multiple_pads(enet_pads, ARRAY_SIZE(enet_pads)); + + /* set GPIO_16 as ENET_REF_CLK_OUT */ + setbits_le32(&iomux->gpr[1], IOMUXC_GPR1_ENET_CLK_SEL_MASK); +} + +iomux_v3_cfg_t const usdhc1_pads[] = { + MX6_PAD_SD1_CLK__SD1_CLK | MUX_PAD_CTRL(USDHC_PAD_CTRL), + MX6_PAD_SD1_CMD__SD1_CMD | MUX_PAD_CTRL(USDHC_PAD_CTRL), + MX6_PAD_SD1_DAT0__SD1_DATA0 | MUX_PAD_CTRL(USDHC_PAD_CTRL), + MX6_PAD_SD1_DAT1__SD1_DATA1 | MUX_PAD_CTRL(USDHC_PAD_CTRL), + MX6_PAD_SD1_DAT2__SD1_DATA2 | MUX_PAD_CTRL(USDHC_PAD_CTRL), + MX6_PAD_SD1_DAT3__SD1_DATA3 | MUX_PAD_CTRL(USDHC_PAD_CTRL), +}; + +iomux_v3_cfg_t const usdhc2_pads[] = { + MX6_PAD_SD2_CLK__SD2_CLK | MUX_PAD_CTRL(USDHC_PAD_CTRL), + MX6_PAD_SD2_CMD__SD2_CMD | MUX_PAD_CTRL(USDHC_PAD_CTRL), + MX6_PAD_SD2_DAT0__SD2_DATA0 | MUX_PAD_CTRL(USDHC_PAD_CTRL), + MX6_PAD_SD2_DAT1__SD2_DATA1 | MUX_PAD_CTRL(USDHC_PAD_CTRL), + MX6_PAD_SD2_DAT2__SD2_DATA2 | MUX_PAD_CTRL(USDHC_PAD_CTRL), + MX6_PAD_SD2_DAT3__SD2_DATA3 | MUX_PAD_CTRL(USDHC_PAD_CTRL), +}; + +iomux_v3_cfg_t const ecspi4_pads[] = { + MX6_PAD_EIM_D21__ECSPI4_SCLK | MUX_PAD_CTRL(NO_PAD_CTRL), + MX6_PAD_EIM_D22__ECSPI4_MISO | MUX_PAD_CTRL(NO_PAD_CTRL), + MX6_PAD_EIM_D28__ECSPI4_MOSI | MUX_PAD_CTRL(NO_PAD_CTRL), + MX6_PAD_EIM_D20__GPIO3_IO20 | MUX_PAD_CTRL(NO_PAD_CTRL), +}; + +static iomux_v3_cfg_t const display_pads[] = { + MX6_PAD_DI0_DISP_CLK__IPU1_DI0_DISP_CLK | MUX_PAD_CTRL(DISP_PAD_CTRL), + MX6_PAD_DI0_PIN15__IPU1_DI0_PIN15, + MX6_PAD_DI0_PIN2__IPU1_DI0_PIN02, + MX6_PAD_DI0_PIN3__IPU1_DI0_PIN03, + MX6_PAD_DI0_PIN4__GPIO4_IO20, + MX6_PAD_DISP0_DAT0__IPU1_DISP0_DATA00, + MX6_PAD_DISP0_DAT1__IPU1_DISP0_DATA01, + MX6_PAD_DISP0_DAT2__IPU1_DISP0_DATA02, + MX6_PAD_DISP0_DAT3__IPU1_DISP0_DATA03, + MX6_PAD_DISP0_DAT4__IPU1_DISP0_DATA04, + MX6_PAD_DISP0_DAT5__IPU1_DISP0_DATA05, + MX6_PAD_DISP0_DAT6__IPU1_DISP0_DATA06, + MX6_PAD_DISP0_DAT7__IPU1_DISP0_DATA07, + MX6_PAD_DISP0_DAT8__IPU1_DISP0_DATA08, + MX6_PAD_DISP0_DAT9__IPU1_DISP0_DATA09, + MX6_PAD_DISP0_DAT10__IPU1_DISP0_DATA10, + MX6_PAD_DISP0_DAT11__IPU1_DISP0_DATA11, + MX6_PAD_DISP0_DAT12__IPU1_DISP0_DATA12, + MX6_PAD_DISP0_DAT13__IPU1_DISP0_DATA13, + MX6_PAD_DISP0_DAT14__IPU1_DISP0_DATA14, + MX6_PAD_DISP0_DAT15__IPU1_DISP0_DATA15, + MX6_PAD_DISP0_DAT16__IPU1_DISP0_DATA16, + MX6_PAD_DISP0_DAT17__IPU1_DISP0_DATA17, + MX6_PAD_DISP0_DAT18__IPU1_DISP0_DATA18, + MX6_PAD_DISP0_DAT19__IPU1_DISP0_DATA19, + MX6_PAD_DISP0_DAT20__IPU1_DISP0_DATA20, + MX6_PAD_DISP0_DAT21__IPU1_DISP0_DATA21, + MX6_PAD_DISP0_DAT22__IPU1_DISP0_DATA22, + MX6_PAD_DISP0_DAT23__IPU1_DISP0_DATA23, +}; + +static iomux_v3_cfg_t const backlight_pads[] = { + MX6_PAD_GPIO_9__PWM1_OUT | MUX_PAD_CTRL(NO_PAD_CTRL), + MX6_PAD_SD4_DAT1__PWM3_OUT | MUX_PAD_CTRL(NO_PAD_CTRL), + MX6_PAD_GPIO_2__GPIO1_IO02 | MUX_PAD_CTRL(NO_PAD_CTRL), +}; + +static void setup_spi(void) +{ + int i; + + imx_iomux_v3_setup_multiple_pads(ecspi4_pads, ARRAY_SIZE(ecspi4_pads)); + for (i = 0; i < 3; i++) + enable_spi_clk(true, i); + + /* set cs1 to high */ + gpio_direction_output(ECSPI4_CS1, 1); +} + +static void setup_iomux_gpio(void) +{ + imx_iomux_v3_setup_multiple_pads(gpio_pads, ARRAY_SIZE(gpio_pads)); +} + +static void setup_iomux_uart(void) +{ + imx_iomux_v3_setup_multiple_pads(uart5_pads, ARRAY_SIZE(uart5_pads)); +} + +#ifdef CONFIG_FSL_ESDHC +struct fsl_esdhc_cfg usdhc_cfg[2] = { + {USDHC1_BASE_ADDR}, + {USDHC2_BASE_ADDR}, +}; + +int board_mmc_getcd(struct mmc *mmc) +{ + return 1; +} + +int board_mmc_init(bd_t *bis) +{ + usdhc_cfg[0].sdhc_clk = mxc_get_clock(MXC_ESDHC_CLK); + usdhc_cfg[1].sdhc_clk = mxc_get_clock(MXC_ESDHC2_CLK); + + imx_iomux_v3_setup_multiple_pads(usdhc1_pads, ARRAY_SIZE(usdhc1_pads)); + imx_iomux_v3_setup_multiple_pads(usdhc2_pads, ARRAY_SIZE(usdhc2_pads)); + + return fsl_esdhc_initialize(bis, &usdhc_cfg[0]) | + fsl_esdhc_initialize(bis, &usdhc_cfg[1]); +} +#endif + +/* + * Do not overwrite the console + * Use always serial for U-Boot console + */ +int overwrite_console(void) +{ + return 1; +} + +int board_eth_init(bd_t *bis) +{ + struct iomuxc *iomuxc_regs = + (struct iomuxc *)IOMUXC_BASE_ADDR; + int ret; + + setup_iomux_enet(); + /* clear gpr1[14], gpr1[18:17] to select anatop clock */ + clrsetbits_le32(&iomuxc_regs->gpr[1], IOMUX_GPR1_FEC_MASK, 0); + + ret = enable_fec_anatop_clock(ENET_50MHz); + if (ret) + return ret; + + return cpu_eth_init(bis); +} +#if defined(CONFIG_VIDEO_IPUV3) + +static void enable_lvds(struct display_info_t const *dev) +{ + imx_iomux_v3_setup_multiple_pads( + display_pads, + ARRAY_SIZE(display_pads)); + imx_iomux_v3_setup_multiple_pads( + backlight_pads, + ARRAY_SIZE(backlight_pads)); + + /* enable backlight PWM 3 */ + if (pwm_init(2, 0, 0)) + goto error; + /* duty cycle 200ns, period: 3000ns */ + if (pwm_config(2, 200, 3000)) + goto error; + if (pwm_enable(2)) + goto error; + return; + +error: + puts("error init pwm for backlight\n"); + return; +} + +struct display_info_t const displays[] = { + { + .bus = -1, + .addr = 0, + .pixfmt = IPU_PIX_FMT_RGB24, + .detect = NULL, + .enable = enable_lvds, + .mode = { + .name = "lb07wv8", + .refresh = 60, + .xres = 800, + .yres = 480, + .pixclock = 33246, + .left_margin = 88, + .right_margin = 88, + .upper_margin = 10, + .lower_margin = 10, + .hsync_len = 25, + .vsync_len = 1, + .sync = 0, + .vmode = FB_VMODE_NONINTERLACED + } + } +}; +size_t display_count = ARRAY_SIZE(displays); + +static void setup_display(void) +{ + struct mxc_ccm_reg *mxc_ccm = (struct mxc_ccm_reg *)CCM_BASE_ADDR; + int reg; + + enable_ipu_clock(); + + reg = readl(&mxc_ccm->cs2cdr); + /* select pll 5 clock */ + reg &= MXC_CCM_CS2CDR_LDB_DI0_CLK_SEL_MASK; + reg &= MXC_CCM_CS2CDR_LDB_DI1_CLK_SEL_MASK; + writel(reg, &mxc_ccm->cs2cdr); + + imx_iomux_v3_setup_multiple_pads(backlight_pads, + ARRAY_SIZE(backlight_pads)); +} + +/* no console on this board */ +int board_cfb_skip(void) +{ + return 1; +} +#endif + +int board_early_init_f(void) +{ + setup_iomux_uart(); + setup_iomux_gpio(); + +#if defined(CONFIG_VIDEO_IPUV3) + setup_display(); +#endif + return 0; +} + +iomux_v3_cfg_t nfc_pads[] = { + MX6_PAD_NANDF_CLE__NAND_CLE | MUX_PAD_CTRL(NO_PAD_CTRL), + MX6_PAD_NANDF_ALE__NAND_ALE | MUX_PAD_CTRL(NO_PAD_CTRL), + MX6_PAD_NANDF_WP_B__NAND_WP_B | MUX_PAD_CTRL(NO_PAD_CTRL), + MX6_PAD_NANDF_RB0__NAND_READY_B | MUX_PAD_CTRL(NO_PAD_CTRL), + MX6_PAD_NANDF_CS0__NAND_CE0_B | MUX_PAD_CTRL(NO_PAD_CTRL), + MX6_PAD_NANDF_CS1__NAND_CE1_B | MUX_PAD_CTRL(NO_PAD_CTRL), + MX6_PAD_NANDF_CS2__NAND_CE2_B | MUX_PAD_CTRL(NO_PAD_CTRL), + MX6_PAD_NANDF_CS3__NAND_CE3_B | MUX_PAD_CTRL(NO_PAD_CTRL), + MX6_PAD_SD4_CMD__NAND_RE_B | MUX_PAD_CTRL(NO_PAD_CTRL), + MX6_PAD_SD4_CLK__NAND_WE_B | MUX_PAD_CTRL(NO_PAD_CTRL), + MX6_PAD_NANDF_D0__NAND_DATA00 | MUX_PAD_CTRL(NO_PAD_CTRL), + MX6_PAD_NANDF_D1__NAND_DATA01 | MUX_PAD_CTRL(NO_PAD_CTRL), + MX6_PAD_NANDF_D2__NAND_DATA02 | MUX_PAD_CTRL(NO_PAD_CTRL), + MX6_PAD_NANDF_D3__NAND_DATA03 | MUX_PAD_CTRL(NO_PAD_CTRL), + MX6_PAD_NANDF_D4__NAND_DATA04 | MUX_PAD_CTRL(NO_PAD_CTRL), + MX6_PAD_NANDF_D5__NAND_DATA05 | MUX_PAD_CTRL(NO_PAD_CTRL), + MX6_PAD_NANDF_D6__NAND_DATA06 | MUX_PAD_CTRL(NO_PAD_CTRL), + MX6_PAD_NANDF_D7__NAND_DATA07 | MUX_PAD_CTRL(NO_PAD_CTRL), + MX6_PAD_SD4_DAT0__NAND_DQS | MUX_PAD_CTRL(NO_PAD_CTRL), +}; + +static void setup_gpmi_nand(void) +{ + struct mxc_ccm_reg *mxc_ccm = (struct mxc_ccm_reg *)CCM_BASE_ADDR; + + /* config gpmi nand iomux */ + imx_iomux_v3_setup_multiple_pads(nfc_pads, + ARRAY_SIZE(nfc_pads)); + + /* config gpmi and bch clock to 100 MHz */ + clrsetbits_le32(&mxc_ccm->cs2cdr, + MXC_CCM_CS2CDR_ENFC_CLK_PODF_MASK | + MXC_CCM_CS2CDR_ENFC_CLK_PRED_MASK | + MXC_CCM_CS2CDR_ENFC_CLK_SEL_MASK, + MXC_CCM_CS2CDR_ENFC_CLK_PODF(0) | + MXC_CCM_CS2CDR_ENFC_CLK_PRED(3) | + MXC_CCM_CS2CDR_ENFC_CLK_SEL(3)); + + /* enable gpmi and bch clock gating */ + setbits_le32(&mxc_ccm->CCGR4, + MXC_CCM_CCGR4_RAWNAND_U_BCH_INPUT_APB_MASK | + MXC_CCM_CCGR4_RAWNAND_U_GPMI_BCH_INPUT_BCH_MASK | + MXC_CCM_CCGR4_RAWNAND_U_GPMI_BCH_INPUT_GPMI_IO_MASK | + MXC_CCM_CCGR4_RAWNAND_U_GPMI_INPUT_APB_MASK | + MXC_CCM_CCGR4_PL301_MX6QPER1_BCH_OFFSET); + + /* enable apbh clock gating */ + setbits_le32(&mxc_ccm->CCGR0, MXC_CCM_CCGR0_APBHDMA_MASK); +} + +int board_init(void) +{ + struct iomuxc *iomux = (struct iomuxc *)IOMUXC_BASE_ADDR; + + /* address of boot parameters */ + gd->bd->bi_boot_params = PHYS_SDRAM + 0x100; + + setup_spi(); + + setup_i2c(0, CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE, + &i2c_pad_info1); + setup_i2c(1, CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE, + &i2c_pad_info2); + setup_i2c(2, CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE, + &i2c_pad_info3); + + /* i2c4 not used, set it to gpio input */ + gpio_request(IMX_GPIO_NR(1, 7), "i2c4_scl"); + gpio_direction_input(IMX_GPIO_NR(1, 7)); + gpio_request(IMX_GPIO_NR(1, 8), "i2c4_sda"); + gpio_direction_input(IMX_GPIO_NR(1, 8)); + + /* SPI NOR Flash read only */ + gpio_request(CONFIG_GPIO_ENABLE_SPI_FLASH, "ena_spi_nor"); + gpio_direction_output(CONFIG_GPIO_ENABLE_SPI_FLASH, 0); + gpio_free(CONFIG_GPIO_ENABLE_SPI_FLASH); + + /* enable LED */ + gpio_request(IMX_GPIO_NR(2, 13), "LED ena"); + gpio_direction_output(IMX_GPIO_NR(2, 13), 0); + + gpio_request(IMX_GPIO_NR(1, 3), "LED yellow"); + gpio_direction_output(IMX_GPIO_NR(1, 3), 1); + gpio_request(IMX_GPIO_NR(1, 4), "LED red"); + gpio_direction_output(IMX_GPIO_NR(1, 4), 1); + gpio_request(IMX_GPIO_NR(1, 5), "LED green"); + gpio_direction_output(IMX_GPIO_NR(1, 5), 1); + gpio_request(IMX_GPIO_NR(1, 6), "LED blue"); + gpio_direction_output(IMX_GPIO_NR(1, 6), 1); + + setup_gpmi_nand(); + + /* GPIO_1 for USB_OTG_ID */ + setbits_le32(&iomux->gpr[1], IOMUXC_GPR1_USB_OTG_ID_SEL_MASK); + imx_iomux_v3_setup_multiple_pads(misc_pads, ARRAY_SIZE(misc_pads)); + + return 0; +} + +int checkboard(void) +{ + puts("Board: aristaitenos\n"); + return 0; +} + +#ifdef CONFIG_USB_EHCI_MX6 +int board_ehci_hcd_init(int port) +{ + int ret; + + ret = gpio_request(ARISTAINETOS_USB_H1_PWR, "usb-h1-pwr"); + if (!ret) + gpio_direction_output(ARISTAINETOS_USB_H1_PWR, 1); + ret = gpio_request(ARISTAINETOS_USB_OTG_PWR, "usb-OTG-pwr"); + if (!ret) + gpio_direction_output(ARISTAINETOS_USB_OTG_PWR, 1); + return 0; +} + +int board_ehci_power(int port, int on) +{ + if (port) + gpio_set_value(ARISTAINETOS_USB_OTG_PWR, on); + else + gpio_set_value(ARISTAINETOS_USB_H1_PWR, on); + return 0; +} +#endif diff --git a/board/aristainetos/aristainetos.cfg b/board/aristainetos/aristainetos.cfg new file mode 100644 index 0000000..2290180 --- /dev/null +++ b/board/aristainetos/aristainetos.cfg @@ -0,0 +1,33 @@ +/* + * (C) Copyright 2014 + * Heiko Schocher, DENX Software Engineering, hs@denx.de. + * + * Based on: + * Copyright (C) 2013 Boundary Devices + * + * SPDX-License-Identifier: GPL-2.0+ + * + * Refer doc/README.imximage for more details about how-to configure + * and create imximage boot image + * + * The syntax is taken as close as possible with the kwbimage + */ + +/* image version */ +IMAGE_VERSION 2 + +/* + * Boot Device : one of + * spi, sd + */ +BOOT_FROM spi + +#define __ASSEMBLY__ +#include <config.h> +#include "asm/arch/mx6-ddr.h" +#include "asm/arch/iomux.h" +#include "asm/arch/crm_regs.h" + +#include "ddr-setup.cfg" +#include "mt41j128M.cfg" +#include "clocks.cfg" diff --git a/board/aristainetos/clocks.cfg b/board/aristainetos/clocks.cfg new file mode 100644 index 0000000..651449e --- /dev/null +++ b/board/aristainetos/clocks.cfg @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2013 Boundary Devices + * + * SPDX-License-Identifier: GPL-2.0+ + * + * Device Configuration Data (DCD) + * + * Each entry must have the format: + * Addr-type Address Value + * + * where: + * Addr-type register length (1,2 or 4 bytes) + * Address absolute address of the register + * value value to be stored in the register + */ + +/* set the default clock gate to save power */ +DATA 4, CCM_CCGR0, 0x00c03f3f +DATA 4, CCM_CCGR1, 0x0030fcff +DATA 4, CCM_CCGR2, 0x0fffcfc0 +DATA 4, CCM_CCGR3, 0x3ff0300f +DATA 4, CCM_CCGR4, 0xfffff30c /* enable NAND/GPMI/BCH clocks */ +DATA 4, CCM_CCGR5, 0x0f0000c3 +DATA 4, CCM_CCGR6, 0x000003ff diff --git a/board/aristainetos/ddr-setup.cfg b/board/aristainetos/ddr-setup.cfg new file mode 100644 index 0000000..c72a3ef --- /dev/null +++ b/board/aristainetos/ddr-setup.cfg @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2013 Boundary Devices + * + * SPDX-License-Identifier: GPL-2.0+ + * + * Device Configuration Data (DCD) + * + * Each entry must have the format: + * Addr-type Address Value + * + * where: + * Addr-type register length (1,2 or 4 bytes) + * Address absolute address of the register + * value value to be stored in the register + */ + +/* DDR IO TYPE */ +DATA 4, MX6_IOM_GRP_DDR_TYPE, 0x000C0000 +DATA 4, MX6_IOM_GRP_DDRPKE, 0x00000000 +/* Clock */ +DATA 4, MX6_IOM_DRAM_SDCLK_0, 0x00000030 +DATA 4, MX6_IOM_DRAM_SDCLK_1, 0x00000030 +/* Address */ +DATA 4, MX6_IOM_DRAM_CAS, 0x00000030 +DATA 4, MX6_IOM_DRAM_RAS, 0x00000030 +DATA 4, MX6_IOM_GRP_ADDDS, 0x00000030 +/* Control */ +DATA 4, MX6_IOM_DRAM_RESET, 0x00000030 +DATA 4, MX6_IOM_DRAM_SDCKE0, 0x00003000 +DATA 4, MX6_IOM_DRAM_SDCKE1, 0x00003000 +DATA 4, MX6_IOM_DRAM_SDBA2, 0x00000000 +DATA 4, MX6_IOM_DRAM_SDODT0, 0x00003030 +DATA 4, MX6_IOM_DRAM_SDODT1, 0x00003030 +DATA 4, MX6_IOM_GRP_CTLDS, 0x00000030 +/* Data Strobe */ +DATA 4, MX6_IOM_DDRMODE_CTL, 0x00020000 +DATA 4, MX6_IOM_DRAM_SDQS0, 0x00000030 +DATA 4, MX6_IOM_DRAM_SDQS1, 0x00000030 +DATA 4, MX6_IOM_DRAM_SDQS2, 0x00000030 +DATA 4, MX6_IOM_DRAM_SDQS3, 0x00000030 +DATA 4, MX6_IOM_DRAM_SDQS4, 0x00000030 +DATA 4, MX6_IOM_DRAM_SDQS5, 0x00000030 +DATA 4, MX6_IOM_DRAM_SDQS6, 0x00000030 +DATA 4, MX6_IOM_DRAM_SDQS7, 0x00000030 +DATA 4, MX6_IOM_GRP_DDRMODE, 0x00020000 +DATA 4, MX6_IOM_GRP_B0DS, 0x00000030 +DATA 4, MX6_IOM_GRP_B1DS, 0x00000030 +DATA 4, MX6_IOM_GRP_B2DS, 0x00000030 +DATA 4, MX6_IOM_GRP_B3DS, 0x00000030 +DATA 4, MX6_IOM_GRP_B4DS, 0x00000030 +DATA 4, MX6_IOM_GRP_B5DS, 0x00000030 +DATA 4, MX6_IOM_GRP_B6DS, 0x00000030 +DATA 4, MX6_IOM_GRP_B7DS, 0x00000030 +DATA 4, MX6_IOM_DRAM_DQM0, 0x00000030 +DATA 4, MX6_IOM_DRAM_DQM1, 0x00000030 +DATA 4, MX6_IOM_DRAM_DQM2, 0x00000030 +DATA 4, MX6_IOM_DRAM_DQM3, 0x00000030 +DATA 4, MX6_IOM_DRAM_DQM4, 0x00000030 +DATA 4, MX6_IOM_DRAM_DQM5, 0x00000030 +DATA 4, MX6_IOM_DRAM_DQM6, 0x00000030 +DATA 4, MX6_IOM_DRAM_DQM7, 0x00000030 diff --git a/board/aristainetos/mt41j128M.cfg b/board/aristainetos/mt41j128M.cfg new file mode 100644 index 0000000..3561655 --- /dev/null +++ b/board/aristainetos/mt41j128M.cfg @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2013 Boundary Devices + * + * SPDX-License-Identifier: GPL-2.0+ + */ +/* ZQ Calibration */ +DATA 4, MX6_MMDC_P0_MPZQHWCTRL, 0xa1390003 +DATA 4, MX6_MMDC_P1_MPZQHWCTRL, 0xa1390003 +DATA 4, MX6_MMDC_P0_MPWLDECTRL0, 0x001F001F +DATA 4, MX6_MMDC_P0_MPWLDECTRL1, 0x001F001F +DATA 4, MX6_MMDC_P1_MPWLDECTRL0, 0x001F001F +DATA 4, MX6_MMDC_P1_MPWLDECTRL1, 0x001F001F +/* + * DQS gating, read delay, write delay calibration values + * based on calibration compare of 0x00ffff00 + */ +DATA 4, MX6_MMDC_P0_MPDGCTRL0, 0x420E020E +DATA 4, MX6_MMDC_P0_MPDGCTRL1, 0x02000200 +DATA 4, MX6_MMDC_P1_MPDGCTRL0, 0x42020202 +DATA 4, MX6_MMDC_P1_MPDGCTRL1, 0x01720172 +DATA 4, MX6_MMDC_P0_MPRDDLCTL, 0x494C4F4C +DATA 4, MX6_MMDC_P1_MPRDDLCTL, 0x4A4C4C49 +DATA 4, MX6_MMDC_P0_MPWRDLCTL, 0x3F3F3133 +DATA 4, MX6_MMDC_P1_MPWRDLCTL, 0x39373F2E +/* read data bit delay */ +DATA 4, MX6_MMDC_P0_MPRDDQBY0DL, 0x33333333 +DATA 4, MX6_MMDC_P0_MPRDDQBY1DL, 0x33333333 +DATA 4, MX6_MMDC_P0_MPRDDQBY2DL, 0x33333333 +DATA 4, MX6_MMDC_P0_MPRDDQBY3DL, 0x33333333 +DATA 4, MX6_MMDC_P1_MPRDDQBY0DL, 0x33333333 +DATA 4, MX6_MMDC_P1_MPRDDQBY1DL, 0x33333333 +DATA 4, MX6_MMDC_P1_MPRDDQBY2DL, 0x33333333 +DATA 4, MX6_MMDC_P1_MPRDDQBY3DL, 0x33333333 +/* Complete calibration by forced measurment */ +DATA 4, MX6_MMDC_P0_MPMUR0, 0x00000800 +DATA 4, MX6_MMDC_P1_MPMUR0, 0x00000800 +/* in DDR3, 64-bit mode, only MMDC0 is initiated */ +DATA 4, MX6_MMDC_P0_MDPDC, 0x0002002d +DATA 4, MX6_MMDC_P0_MDOTC, 0x00333030 +DATA 4, MX6_MMDC_P0_MDCFG0, 0x40445323 +DATA 4, MX6_MMDC_P0_MDCFG1, 0xb66e8c63 +DATA 4, MX6_MMDC_P0_MDCFG2, 0x01ff00db +DATA 4, MX6_MMDC_P0_MDMISC, 0x00081740 +DATA 4, MX6_MMDC_P0_MDSCR, 0x00008000 +DATA 4, MX6_MMDC_P0_MDRWD, 0x000026d2 +DATA 4, MX6_MMDC_P0_MDOR, 0x00440e21 +DATA 4, MX6_MMDC_P0_MDASP, 0x00000027 +DATA 4, MX6_MMDC_P0_MDCTL, 0x84190000 +/* MR2 */ +DATA 4, MX6_MMDC_P0_MDSCR, 0x04008032 +DATA 4, MX6_MMDC_P0_MDSCR, 0x0400803a +/* MR3 */ +DATA 4, MX6_MMDC_P0_MDSCR, 0x00008033 +DATA 4, MX6_MMDC_P0_MDSCR, 0x0000803b +/* MR1 */ +DATA 4, MX6_MMDC_P0_MDSCR, 0x00428031 +DATA 4, MX6_MMDC_P0_MDSCR, 0x00428039 +/* MR0 */ +DATA 4, MX6_MMDC_P0_MDSCR, 0x07208030 +DATA 4, MX6_MMDC_P0_MDSCR, 0x07208038 +/* ZQ calibration */ +DATA 4, MX6_MMDC_P0_MDSCR, 0x04008040 +DATA 4, MX6_MMDC_P0_MDSCR, 0x04008048 +/* final ddr setup */ +DATA 4, MX6_MMDC_P0_MDREF, 0x00005800 +DATA 4, MX6_MMDC_P0_MPODTCTRL, 0x00000007 +DATA 4, MX6_MMDC_P1_MPODTCTRL, 0x00000007 +DATA 4, MX6_MMDC_P0_MDPDC, 0x0002556d +DATA 4, MX6_MMDC_P1_MAPSR, 0x00011006 +DATA 4, MX6_MMDC_P0_MDSCR, 0x00000000 diff --git a/board/boundary/nitrogen6x/nitrogen6x.c b/board/boundary/nitrogen6x/nitrogen6x.c index 84294db..60a09f4 100644 --- a/board/boundary/nitrogen6x/nitrogen6x.c +++ b/board/boundary/nitrogen6x/nitrogen6x.c @@ -644,8 +644,7 @@ int overwrite_console(void) int board_init(void) { - struct iomuxc_base_regs *const iomuxc_regs - = (struct iomuxc_base_regs *)IOMUXC_BASE_ADDR; + struct iomuxc *const iomuxc_regs = (struct iomuxc *)IOMUXC_BASE_ADDR; clrsetbits_le32(&iomuxc_regs->gpr[1], IOMUXC_GPR1_OTG_ID_MASK, diff --git a/board/davinci/dm355evm/MAINTAINERS b/board/davinci/dm355evm/MAINTAINERS index b823785..ef586b3 100644 --- a/board/davinci/dm355evm/MAINTAINERS +++ b/board/davinci/dm355evm/MAINTAINERS @@ -1,6 +1,6 @@ DM355EVM BOARD M: Sandeep Paulraj <s-paulraj@ti.com> -S: Maintained +S: Orphan (since 2014-08) F: board/davinci/dm355evm/ F: include/configs/davinci_dm355evm.h F: configs/davinci_dm355evm_defconfig diff --git a/board/davinci/dm355leopard/MAINTAINERS b/board/davinci/dm355leopard/MAINTAINERS index f17fea2..2fc1e00 100644 --- a/board/davinci/dm355leopard/MAINTAINERS +++ b/board/davinci/dm355leopard/MAINTAINERS @@ -1,6 +1,6 @@ DM355LEOPARD BOARD M: Sandeep Paulraj <s-paulraj@ti.com> -S: Maintained +S: Orphan (since 2014-08) F: board/davinci/dm355leopard/ F: include/configs/davinci_dm355leopard.h F: configs/davinci_dm355leopard_defconfig diff --git a/board/davinci/dm365evm/MAINTAINERS b/board/davinci/dm365evm/MAINTAINERS index 5adb4e0..0bfe02d 100644 --- a/board/davinci/dm365evm/MAINTAINERS +++ b/board/davinci/dm365evm/MAINTAINERS @@ -1,6 +1,6 @@ DM365EVM BOARD M: Sandeep Paulraj <s-paulraj@ti.com> -S: Maintained +S: Orphan (since 2014-08) F: board/davinci/dm365evm/ F: include/configs/davinci_dm365evm.h F: configs/davinci_dm365evm_defconfig diff --git a/board/davinci/dm6467evm/MAINTAINERS b/board/davinci/dm6467evm/MAINTAINERS index 4030bf3..bb40536 100644 --- a/board/davinci/dm6467evm/MAINTAINERS +++ b/board/davinci/dm6467evm/MAINTAINERS @@ -1,6 +1,6 @@ DM6467EVM BOARD M: Sandeep Paulraj <s-paulraj@ti.com> -S: Maintained +S: Orphan (since 2014-08) F: board/davinci/dm6467evm/ F: include/configs/davinci_dm6467evm.h F: configs/davinci_dm6467evm_defconfig diff --git a/board/embest/mx6boards/mx6boards.c b/board/embest/mx6boards/mx6boards.c index d06b57d..530ea4f 100644 --- a/board/embest/mx6boards/mx6boards.c +++ b/board/embest/mx6boards/mx6boards.c @@ -246,6 +246,7 @@ int board_mmc_init(bd_t *bis) riotboard_usdhc3_pads, ARRAY_SIZE(riotboard_usdhc3_pads)); gpio_direction_input(USDHC3_CD_GPIO); + } else { gpio_direction_output(IMX_GPIO_NR(7, 8) , 0); udelay(250); gpio_set_value(IMX_GPIO_NR(7, 8), 1); diff --git a/board/flagadm/Kconfig b/board/flagadm/Kconfig deleted file mode 100644 index bc0657e..0000000 --- a/board/flagadm/Kconfig +++ /dev/null @@ -1,11 +0,0 @@ -if TARGET_FLAGADM - -config SYS_BOARD - string - default "flagadm" - -config SYS_CONFIG_NAME - string - default "FLAGADM" - -endif diff --git a/board/flagadm/MAINTAINERS b/board/flagadm/MAINTAINERS deleted file mode 100644 index 606989c..0000000 --- a/board/flagadm/MAINTAINERS +++ /dev/null @@ -1,6 +0,0 @@ -FLAGADM BOARD -M: Kári Davíðsson <kd@flaga.is> -S: Orphan (since 2014-06) -F: board/flagadm/ -F: include/configs/FLAGADM.h -F: configs/FLAGADM_defconfig diff --git a/board/flagadm/Makefile b/board/flagadm/Makefile deleted file mode 100644 index f2377c8..0000000 --- a/board/flagadm/Makefile +++ /dev/null @@ -1,8 +0,0 @@ -# -# (C) Copyright 2001-2006 -# Wolfgang Denk, DENX Software Engineering, wd@denx.de. -# -# SPDX-License-Identifier: GPL-2.0+ -# - -obj-y = flagadm.o flash.o diff --git a/board/flagadm/flagadm.c b/board/flagadm/flagadm.c deleted file mode 100644 index 343cb77..0000000 --- a/board/flagadm/flagadm.c +++ /dev/null @@ -1,134 +0,0 @@ -/* - * (C) Copyright 2001 - * Wolfgang Denk, DENX Software Engineering, wd@denx.de. - * - * SPDX-License-Identifier: GPL-2.0+ - */ - -#include <common.h> -#include <mpc8xx.h> - -#define _NOT_USED_ 0xFFFFFFFF - -/*Orginal table, GPL4 disabled*/ -const uint sdram_table[] = -{ - /* single read (offset 0x00 in upm ram) */ - 0x1f07cc04, 0xeeaeec04, 0x11adcc04, 0xefbbac00, - 0x1ff74c47, - /* Precharge */ - 0x1FF74C05, - _NOT_USED_, - _NOT_USED_, - /* burst read (offset 0x08 in upm ram) */ - 0x1f07cc04, 0xeeaeec04, 0x00adcc04, 0x00afcc00, - 0x00afcc00, 0x01afcc00, 0x0fbb8c00, 0x1ff74c47, - _NOT_USED_, _NOT_USED_, _NOT_USED_, _NOT_USED_, - _NOT_USED_, _NOT_USED_, _NOT_USED_, _NOT_USED_, - /* single write (offset 0x18 in upm ram) */ - 0x1f27cc04, 0xeeaeac00, 0x01b90c04, 0x1ff74c47, - /* Load moderegister */ - 0x1FF74C34, /*Precharge*/ - 0xEFEA8C34, /*NOP*/ - 0x1FB54C35, /*Load moderegister*/ - _NOT_USED_, - - /* burst write (offset 0x20 in upm ram) */ - 0x1f07cc04, 0xeeaeac00, 0x00ad4c00, 0x00afcc00, - 0x00afcc00, 0x01bb8c04, 0x1ff74c47, _NOT_USED_, - _NOT_USED_, _NOT_USED_, _NOT_USED_, _NOT_USED_, - _NOT_USED_, _NOT_USED_, _NOT_USED_, _NOT_USED_, - /* refresh (offset 0x30 in upm ram) */ - 0x1ff5cc84, 0xffffec04, 0xffffec04, 0xffffec04, - 0xffffec84, 0xffffec07, _NOT_USED_, _NOT_USED_, - _NOT_USED_, _NOT_USED_, _NOT_USED_, _NOT_USED_, - /* exception (offset 0x3C in upm ram) */ - 0x7fffec07, _NOT_USED_, _NOT_USED_, _NOT_USED_, -}; - -/* GPL5 driven every cycle */ -/* the display and the DSP */ -const uint dsp_disp_table[] = -{ - /* single read (offset 0x00 in upm ram) */ - 0xffffc80c, 0xffffc004, 0x0fffc004, 0x0fffd004, - 0x0fffc000, 0x0fffc004, 0x3fffc004, 0xffffcc05, - /* burst read (offset 0x08 in upm ram) */ - _NOT_USED_, _NOT_USED_, _NOT_USED_, _NOT_USED_, - _NOT_USED_, _NOT_USED_, _NOT_USED_, _NOT_USED_, - _NOT_USED_, _NOT_USED_, _NOT_USED_, _NOT_USED_, - _NOT_USED_, _NOT_USED_, _NOT_USED_, _NOT_USED_, - /* single write (offset 0x18 in upm ram) */ - 0xffffcc0c, 0xffffc004, 0x0fffc004, 0x0fffd004, - 0x0fffc000, 0x0fffc004, 0x7fffc004, 0xfffffc05, - /* burst write (offset 0x20 in upm ram) */ - _NOT_USED_, _NOT_USED_, _NOT_USED_, _NOT_USED_, - _NOT_USED_, _NOT_USED_, _NOT_USED_, _NOT_USED_, - _NOT_USED_, _NOT_USED_, _NOT_USED_, _NOT_USED_, - _NOT_USED_, _NOT_USED_, _NOT_USED_, _NOT_USED_, - /* refresh (offset 0x30 in upm ram) */ - _NOT_USED_, _NOT_USED_, _NOT_USED_, _NOT_USED_, - _NOT_USED_, _NOT_USED_, _NOT_USED_, _NOT_USED_, - _NOT_USED_, _NOT_USED_, _NOT_USED_, _NOT_USED_, - /* exception (offset 0x3C in upm ram) */ - _NOT_USED_, _NOT_USED_, _NOT_USED_, _NOT_USED_, -}; - -int checkboard (void) -{ - puts ("Board: FlagaDM V3.0\n"); - return 0; -} - -phys_size_t initdram (int board_type) -{ - volatile immap_t *immap = (immap_t *)CONFIG_SYS_IMMR; - volatile memctl8xx_t *memctl = &immap->im_memctl; - long int size_b0; - - memctl->memc_or2 = CONFIG_SYS_OR2; - memctl->memc_br2 = CONFIG_SYS_BR2; - - udelay(100); - upmconfig(UPMA, (uint *)sdram_table, sizeof(sdram_table)/sizeof(uint)); - - memctl->memc_mptpr = MPTPR_PTP_DIV16; - memctl->memc_mamr = CONFIG_SYS_MAMR_48_SDR | MAMR_TLFA_1X; - - /*Do the initialization of the SDRAM*/ - /*Start with the precharge cycle*/ - memctl->memc_mcr = (MCR_OP_RUN | MCR_UPM_A | MCR_MB_CS2 | \ - MCR_MLCF(1) | MCR_MAD(0x5)); - - /*Then we need two refresh cycles*/ - memctl->memc_mamr = CONFIG_SYS_MAMR_48_SDR | MAMR_TLFA_2X; - memctl->memc_mcr = (MCR_OP_RUN | MCR_UPM_A | MCR_MB_CS2 | \ - MCR_MLCF(2) | MCR_MAD(0x30)); - - /*Mode register programming*/ - memctl->memc_mar = 0x00000088; /*CAS Latency = 2 and burst length = 4*/ - memctl->memc_mcr = (MCR_OP_RUN | MCR_UPM_A | MCR_MB_CS2 | \ - MCR_MLCF(1) | MCR_MAD(0x1C)); - - /* That should do it, just enable the periodic refresh in burst of 4*/ - memctl->memc_mamr = CONFIG_SYS_MAMR_48_SDR | MAMR_TLFA_4X; - memctl->memc_mamr |= (MAMR_PTAE | MAMR_GPL_A4DIS); - - size_b0 = 16*1024*1024; - - /* - * No bank 1 or 3 - * invalidate bank - */ - memctl->memc_br1 = 0; - memctl->memc_br3 = 0; - - upmconfig(UPMB, (uint *)dsp_disp_table, sizeof(dsp_disp_table)/sizeof(uint)); - - memctl->memc_mbmr = MBMR_GPL_B4DIS; - - memctl->memc_or4 = CONFIG_SYS_OR4; - memctl->memc_br4 = CONFIG_SYS_BR4; - - return (size_b0); -} diff --git a/board/flagadm/flash.c b/board/flagadm/flash.c deleted file mode 100644 index 46a2c9a..0000000 --- a/board/flagadm/flash.c +++ /dev/null @@ -1,687 +0,0 @@ -/* - * (C) Copyright 2001 - * Wolfgang Denk, DENX Software Engineering, wd@denx.de. - * - * SPDX-License-Identifier: GPL-2.0+ - */ - -#include <common.h> -#include <mpc8xx.h> -#include <flash.h> - -flash_info_t flash_info[CONFIG_SYS_MAX_FLASH_BANKS]; /* info for FLASH chips */ - -/*----------------------------------------------------------------------- - * Functions - */ -ulong flash_recognize (vu_long *base); -int write_word (flash_info_t *info, ulong dest, ulong data); -void flash_get_geometry (vu_long *base, flash_info_t *info); -void flash_unprotect(flash_info_t *info); -int _flash_real_protect(flash_info_t *info, long idx, int on); - - -unsigned long flash_init (void) -{ - volatile immap_t *immap = (immap_t *)CONFIG_SYS_IMMR; - volatile memctl8xx_t *memctl = &immap->im_memctl; - int i; - int rec; - - for (i=0; i<CONFIG_SYS_MAX_FLASH_BANKS; ++i) { - flash_info[i].flash_id = FLASH_UNKNOWN; - } - - *((vu_short*)CONFIG_SYS_FLASH_BASE) = 0xffff; - - flash_get_geometry ((vu_long*)CONFIG_SYS_FLASH_BASE, &flash_info[0]); - - /* Remap FLASH according to real size */ - memctl->memc_or0 = CONFIG_SYS_OR_TIMING_FLASH | (-flash_info[0].size & 0xFFFF8000); - memctl->memc_br0 = (CONFIG_SYS_FLASH_BASE & BR_BA_MSK) | - (memctl->memc_br0 & ~(BR_BA_MSK)); - - rec = flash_recognize((vu_long*)CONFIG_SYS_FLASH_BASE); - - if (rec == FLASH_UNKNOWN) { - printf ("## Unknown FLASH on Bank 0 - Size = 0x%08lx = %ld MB\n", - flash_info[0].size, flash_info[0].size<<20); - } - -#if CONFIG_SYS_FLASH_PROTECTION - /*Unprotect all the flash memory*/ - flash_unprotect(&flash_info[0]); -#endif - - *((vu_short*)CONFIG_SYS_FLASH_BASE) = 0xffff; - - return (flash_info[0].size); - -#if CONFIG_SYS_MONITOR_BASE >= CONFIG_SYS_FLASH_BASE - /* monitor protection ON by default */ - flash_protect(FLAG_PROTECT_SET, - CONFIG_SYS_MONITOR_BASE, - CONFIG_SYS_MONITOR_BASE+monitor_flash_len-1, - &flash_info[0]); -#endif - -#ifdef CONFIG_ENV_IS_IN_FLASH - /* ENV protection ON by default */ - flash_protect(FLAG_PROTECT_SET, - CONFIG_ENV_OFFSET, - CONFIG_ENV_OFFSET+CONFIG_ENV_SIZE-1, - &flash_info[0]); -#endif - return (flash_info[0].size); -} - - -int flash_get_protect_status(flash_info_t * info, long idx) -{ - vu_short * base; - ushort res; - -#ifdef DEBUG - printf("\n Attempting to set protection info with %d sectors\n", info->sector_count); -#endif - - - base = (vu_short*)info->start[idx]; - - *(base) = 0xffff; - - *(base + 0x55) = 0x0098; - res = base[0x2]; - - *(base) = 0xffff; - - if(res != 0) - res = 1; - else - res = 0; - - return res; -} - -void flash_get_geometry (vu_long *base, flash_info_t *info) -{ - int i,j; - ulong ner = 0; - vu_short * sb = (vu_short*)base; - ulong offset = (ulong)base; - - /* Read Device geometry */ - - *sb = 0xffff; - - *sb = 0x0090; - - info->flash_id = ((ulong)base[0x0]); -#ifdef DEBUG - printf("Id is %x\n", (uint)(ulong)info->flash_id); -#endif - - *sb = 0xffff; - - *(sb+0x55) = 0x0098; - - info->size = 1 << (sb[0x27]); /* Read flash size */ - -#ifdef DEBUG - printf("Size is %x\n", (uint)(ulong)info->size); -#endif - - *sb = 0xffff; - - *(sb + 0x55) = 0x0098; - ner = sb[0x2c] ; /*Number of erase regions*/ - -#ifdef DEBUG - printf("Number of erase regions %x\n", (uint)ner); -#endif - - info->sector_count = 0; - - for(i = 0; i < ner; i++) - { - uint s; - uint count; - uint t1,t2,t3,t4; - - *sb = 0xffff; - - *(sb + 0x55) = 0x0098; - - t1 = sb[0x2d + i*4]; - t2 = sb[0x2e + i*4]; - t3 = sb[0x2f + i*4]; - t4 = sb[0x30 + i*4]; - - count = ((t1 & 0x00ff) | (((t2 & 0x00ff) << 8) & 0xff00) )+ 1; /*sector count*/ - s = ((t3 & 0x00ff) | (((t4 & 0x00ff) << 8) & 0xff00)) * 256;; /*Sector size*/ - -#ifdef DEBUG - printf("count and size %x, %x\n", count, s); - printf("sector count for erase region %d is %d\n", i, count); -#endif - for(j = 0; j < count; j++) - { -#ifdef DEBUG - printf("%x, ", (uint)offset); -#endif - info->start[ info->sector_count + j] = offset; - offset += s; - } - info->sector_count += count; - } - - if ((offset - (ulong)base) != info->size) - printf("WARNING reported size %x does not match to calculted size %x.\n" - , (uint)info->size, (uint)(offset - (ulong)base) ); - - /* Next check if there are any sectors protected.*/ - - for(i = 0; i < info->sector_count; i++) - info->protect[i] = flash_get_protect_status(info, i); - - *sb = 0xffff; -} - -/*----------------------------------------------------------------------- - */ -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 INTEL_MANUFACT & FLASH_VENDMASK: - printf ("Intel "); - break; - default: - printf ("Unknown Vendor "); - break; - } - - switch (info->flash_id & FLASH_TYPEMASK) { - case INTEL_ID_28F320C3B & FLASH_TYPEMASK: - printf ("28F320RC3(4 MB)\n"); - break; - case INTEL_ID_28F320J3A: - printf("28F320J3A (4 MB)\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 % 4) == 0) - printf ("\n "); - printf (" %02d %08lX%s", - i, info->start[i], - info->protect[i]!=0 ? " (RO)" : " " - ); - } - printf ("\n"); - return ; -} - -ulong flash_recognize (vu_long *base) -{ - ulong id; - ulong res = FLASH_UNKNOWN; - vu_short * sb = (vu_short*)base; - - *sb = 0xffff; - - *sb = 0x0090; - id = base[0]; - - switch (id & 0x00FF0000) - { - case (MT_MANUFACT & 0x00FF0000): /* MT or => Intel */ - case (INTEL_ALT_MANU & 0x00FF0000): - res = FLASH_MAN_INTEL; - break; - default: - res = FLASH_UNKNOWN; - } - - *sb = 0xffff; - - return res; -} - -/*-----------------------------------------------------------------------*/ -#define INTEL_FLASH_STATUS_BLS 0x02 -#define INTEL_FLASH_STATUS_PSS 0x04 -#define INTEL_FLASH_STATUS_VPPS 0x08 -#define INTEL_FLASH_STATUS_PS 0x10 -#define INTEL_FLASH_STATUS_ES 0x20 -#define INTEL_FLASH_STATUS_ESS 0x40 -#define INTEL_FLASH_STATUS_WSMS 0x80 - -int flash_decode_status_bits(char status) -{ - int err = 0; - - if(!(status & INTEL_FLASH_STATUS_WSMS)) { - printf("Busy\n"); - err = -1; - } - - if(status & INTEL_FLASH_STATUS_ESS) { - printf("Erase suspended\n"); - err = -1; - } - - if(status & INTEL_FLASH_STATUS_ES) { - printf("Error in block erase\n"); - err = -1; - } - - if(status & INTEL_FLASH_STATUS_PS) { - printf("Error in programming\n"); - err = -1; - } - - if(status & INTEL_FLASH_STATUS_VPPS) { - printf("Vpp low, operation aborted\n"); - err = -1; - } - - if(status & INTEL_FLASH_STATUS_PSS) { - printf("Program is suspended\n"); - err = -1; - } - - if(status & INTEL_FLASH_STATUS_BLS) { - printf("Attempting to program/erase a locked sector\n"); - err = -1; - } - - if((status & INTEL_FLASH_STATUS_PS) && - (status & INTEL_FLASH_STATUS_ES) && - (status & INTEL_FLASH_STATUS_ESS)) { - printf("A command sequence error\n"); - return -1; - } - - return err; -} - -/*----------------------------------------------------------------------- - */ - -int flash_erase (flash_info_t *info, int s_first, int s_last) -{ - vu_short *addr; - int flag, prot, sect; - ulong start, now; - int rcode = 0; - - 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; - } - - if ((info->flash_id & FLASH_VENDMASK) != (INTEL_MANUFACT & FLASH_VENDMASK)) { - printf ("Can't erase unknown flash type %08lx - aborted\n", - info->flash_id); - 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"); - } - - start = get_timer (0); - - /* Start erase on unprotected sectors */ - for (sect = s_first; sect<=s_last; sect++) { - char tmp; - - if (info->protect[sect] == 0) { /* not protected */ - addr = (vu_short *)(info->start[sect]); - - /* Disable interrupts which might cause a timeout here */ - flag = disable_interrupts(); - - /* Single Block Erase Command */ - *addr = 0x0020; - /* Confirm */ - *addr = 0x00D0; - /* Resume Command, as per errata update */ - *addr = 0x00D0; - - /* re-enable interrupts if necessary */ - if (flag) - enable_interrupts(); - - *addr = 0x70; /*Read status register command*/ - tmp = (short)*addr & 0x00FF; /* Read the status */ - while (!(tmp & INTEL_FLASH_STATUS_WSMS)) { - if ((now=get_timer(start)) > CONFIG_SYS_FLASH_ERASE_TOUT) { - *addr = 0x0050; /* Reset the status register */ - *addr = 0xffff; - printf ("Timeout\n"); - return 1; - } - /* show that we're waiting */ - if ((now - start) > 1000) { /* every second */ - putc ('.'); - } - udelay(100000); /* 100 ms */ - *addr = 0x0070; /*Read status register command*/ - tmp = (short)*addr & 0x00FF; /* Read status */ - start = get_timer(0); - } - if( tmp & INTEL_FLASH_STATUS_ES ) - flash_decode_status_bits(tmp); - - *addr = 0x0050; /* Reset the status register */ - *addr = 0xffff; /* Reset to read mode */ - } - } - - - printf (" done\n"); - return rcode; -} - -void flash_unprotect (flash_info_t *info) -{ - /*We can only unprotect the whole flash at once*/ - /*Therefore we must prevent the _flash_real_protect()*/ - /*from re-protecting sectors, that ware protected before */ - /*we called flash_real_protect();*/ - - int i; - - for(i = 0; i < info->sector_count; i++) - info->protect[i] = 0; - -#ifdef CONFIG_SYS_FLASH_PROTECTION - _flash_real_protect(info, 0, 0); -#endif -} - -/*----------------------------------------------------------------------- - * 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 cp, wp, data; - int i, l, rc; - - wp = (addr & ~3); /* get lower word aligned address */ - - /* - * handle unaligned start bytes - */ - if ((l = addr - wp) != 0) { - data = 0; - for (i=0, cp=wp; i<l; ++i, ++cp) { - data = (data << 8) | (*(uchar *)cp); - } - for (; i<4 && cnt>0; ++i) { - data = (data << 8) | *src++; - --cnt; - ++cp; - } - for (; cnt==0 && i<4; ++i, ++cp) { - data = (data << 8) | (*(uchar *)cp); - } - - if ((rc = write_word(info, wp, data)) != 0) { - return (rc); - } - wp += 4; - } - - /* - * handle word aligned part - */ - while (cnt >= 4) { - data = 0; - for (i=0; i<4; ++i) { - data = (data << 8) | *src++; - } - if ((rc = write_word(info, wp, data)) != 0) { - return (rc); - } - wp += 4; - cnt -= 4; - } - - if (cnt == 0) { - return (0); - } - - /* - * handle unaligned tail bytes - */ - data = 0; - for (i=0, cp=wp; i<4 && cnt>0; ++i, ++cp) { - data = (data << 8) | *src++; - --cnt; - } - for (; i<4; ++i, ++cp) { - data = (data << 8) | (*(uchar *)cp); - } - - return (write_word(info, wp, data)); -} - -/*----------------------------------------------------------------------- - * Write a word to Flash, returns: - * 0 - OK - * 1 - write timeout - * 2 - Flash not erased - */ -int write_word (flash_info_t *info, ulong dest, ulong da) -{ - vu_short *addr = (vu_short *)dest; - ulong start; - char csr; - int flag; - int i; - union { - u32 data32; - u16 data16[2]; - } data; - - data.data32 = da; - - /* Check if Flash is (sufficiently) erased */ - if (((*addr & data.data16[0]) != data.data16[0]) || - ((*(addr+1) & data.data16[1]) != data.data16[1])) { - return (2); - } - /* Disable interrupts which might cause a timeout here */ - flag = disable_interrupts(); - - for(i = 0; i < 2; i++) - { - /* Write Command */ - *addr = 0x0010; - - /* Write Data */ - *addr = data.data16[i]; - - /* re-enable interrupts if necessary */ - if (flag) - enable_interrupts(); - - /* data polling for D7 */ - start = get_timer (0); - flag = 0; - *addr = 0x0070; /*Read statusregister command */ - while (((csr = *addr) & INTEL_FLASH_STATUS_WSMS)!=INTEL_FLASH_STATUS_WSMS) { - if (get_timer(start) > CONFIG_SYS_FLASH_WRITE_TOUT) { - flag = 1; - break; - } - *addr = 0x0070; /*Read statusregister command */ - } - if (csr & INTEL_FLASH_STATUS_PSS) { - printf ("CSR indicates write error (%0x) at %08lx\n", - csr, (ulong)addr); - flag = 1; - } - - /* Clear Status Registers Command */ - *addr = 0x0050; - /* Reset to read array mode */ - *addr = 0xffff; - addr++; - } - - return (flag); -} - -int flash_real_protect(flash_info_t *info, long offset, int prot) -{ - int i, idx; - - for(idx = 0; idx < info->sector_count; idx++) - if(info->start[idx] == offset) - break; - - if(idx==info->sector_count) - return -1; - - if(prot == 0) { - /* Unprotect one sector, which means unprotect all flash - * and reprotect the other protected sectors. - */ - _flash_real_protect(info, 0, 0); /* Unprotects the whole flash*/ - info->protect[idx] = 0; - - for(i = 0; i < info->sector_count; i++) - if(info->protect[i]) - _flash_real_protect(info, i, 1); - } - else { - /* We can protect individual sectors */ - _flash_real_protect(info, idx, 1); - } - - for( i = 0; i < info->sector_count; i++) - info->protect[i] = flash_get_protect_status(info, i); - - return 0; -} - -int _flash_real_protect(flash_info_t *info, long idx, int prot) -{ - vu_short *addr; - int flag; - ushort cmd; - ushort tmp; - ulong now, start; - - if ((info->flash_id & FLASH_VENDMASK) != (INTEL_MANUFACT & FLASH_VENDMASK)) { - printf ("Can't change protection for unknown flash type %08lx - aborted\n", - info->flash_id); - return -1; - } - - if(prot == 0) { - /*Unlock the sector*/ - cmd = 0x00D0; - } - else { - /*Lock the sector*/ - cmd = 0x0001; - } - - addr = (vu_short *)(info->start[idx]); - - /* If chip is busy, wait for it */ - start = get_timer(0); - *addr = 0x0070; /*Read status register command*/ - tmp = ((ushort)(*addr))&0x00ff; /*Read the status*/ - while(!(tmp & INTEL_FLASH_STATUS_WSMS)) { - /*Write State Machine Busy*/ - /*Wait untill done or timeout.*/ - if ((now=get_timer(start)) > CONFIG_SYS_FLASH_WRITE_TOUT) { - *addr = 0x0050; /* Reset the status register */ - *addr = 0xffff; /* Reset the chip */ - printf ("TTimeout\n"); - return 1; - } - *addr = 0x0070; - tmp = ((ushort)(*addr))&0x00ff; /*Read the status*/ - start = get_timer(0); - } - - /* Disable interrupts which might cause a timeout here */ - flag = disable_interrupts(); - - /* Unlock block*/ - *addr = 0x0060; - - *addr = cmd; - - /* re-enable interrupts if necessary */ - if (flag) - enable_interrupts(); - - start = get_timer(0); - *addr = 0x0070; /*Read status register command*/ - tmp = ((ushort)(*addr)) & 0x00FF; /* Read the status */ - while (!(tmp & INTEL_FLASH_STATUS_WSMS)) { - /* Write State Machine Busy */ - if ((now=get_timer(start)) > CONFIG_SYS_FLASH_WRITE_TOUT) { - *addr = 0x0050; /* Reset the status register */ - *addr = 0xffff; - printf ("Timeout\n"); - return 1; - } - /* show that we're waiting */ - if ((now - start) > 1000) { /* every second */ - putc ('.'); - } - udelay(100000); /* 100 ms */ - *addr = 0x70; /*Read status register command*/ - tmp = (short)*addr & 0x00FF; /* Read status */ - start = get_timer(0); - } - if( tmp & INTEL_FLASH_STATUS_PS ) - flash_decode_status_bits(tmp); - - *addr =0x0050; /*Clear status register*/ - - /* reset to read mode */ - *addr = 0xffff; - - return 0; -} diff --git a/board/flagadm/u-boot.lds b/board/flagadm/u-boot.lds deleted file mode 100644 index 7ae91ff..0000000 --- a/board/flagadm/u-boot.lds +++ /dev/null @@ -1,82 +0,0 @@ -/* - * (C) Copyright 2001-2010 - * Wolfgang Denk, DENX Software Engineering, wd@denx.de. - * - * SPDX-License-Identifier: GPL-2.0+ - */ - -OUTPUT_ARCH(powerpc) - -SECTIONS -{ - /* Read-only sections, merged into text segment: */ - . = + SIZEOF_HEADERS; - .text : - { - arch/powerpc/cpu/mpc8xx/start.o (.text*) - arch/powerpc/cpu/mpc8xx/traps.o (.text*) - - *(.text*) - } - _etext = .; - PROVIDE (etext = .); - .rodata : - { - *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) - } - - /* Read-write section, merged into data segment: */ - . = (. + 0x00FF) & 0xFFFFFF00; - _erotext = .; - PROVIDE (erotext = .); - .reloc : - { - _GOT2_TABLE_ = .; - KEEP(*(.got2)) - KEEP(*(.got)) - PROVIDE(_GLOBAL_OFFSET_TABLE_ = . + 4); - _FIXUP_TABLE_ = .; - KEEP(*(.fixup)) - } - __got2_entries = ((_GLOBAL_OFFSET_TABLE_ - _GOT2_TABLE_) >> 2) - 1; - __fixup_entries = (. - _FIXUP_TABLE_)>>2; - - .data : - { - *(.data*) - *(.sdata*) - } - _edata = .; - PROVIDE (edata = .); - - . = .; - - . = ALIGN(4); - .u_boot_list : { - KEEP(*(SORT(.u_boot_list*))); - } - - - . = .; - __start___ex_table = .; - __ex_table : { *(__ex_table) } - __stop___ex_table = .; - - . = ALIGN(256); - __init_begin = .; - .text.init : { *(.text.init) } - .data.init : { *(.data.init) } - . = ALIGN(256); - __init_end = .; - - __bss_start = .; - .bss (NOLOAD) : - { - *(.bss*) - *(.sbss*) - *(COMMON) - . = ALIGN(4); - } - __bss_end = . ; - PROVIDE (end = .); -} diff --git a/board/flagadm/u-boot.lds.debug b/board/flagadm/u-boot.lds.debug deleted file mode 100644 index b0091db..0000000 --- a/board/flagadm/u-boot.lds.debug +++ /dev/null @@ -1,121 +0,0 @@ -/* - * (C) Copyright 2001 - * Wolfgang Denk, DENX Software Engineering, wd@denx.de. - * - * SPDX-License-Identifier: GPL-2.0+ - */ - -OUTPUT_ARCH(powerpc) -/* Do we need any of these for elf? - __DYNAMIC = 0; */ -SECTIONS -{ - /* Read-only sections, merged into text segment: */ - . = + SIZEOF_HEADERS; - .interp : { *(.interp) } - .hash : { *(.hash) } - .dynsym : { *(.dynsym) } - .dynstr : { *(.dynstr) } - .rel.text : { *(.rel.text) } - .rela.text : { *(.rela.text) } - .rel.data : { *(.rel.data) } - .rela.data : { *(.rela.data) } - .rel.rodata : { *(.rel.rodata) } - .rela.rodata : { *(.rela.rodata) } - .rel.got : { *(.rel.got) } - .rela.got : { *(.rela.got) } - .rel.ctors : { *(.rel.ctors) } - .rela.ctors : { *(.rela.ctors) } - .rel.dtors : { *(.rel.dtors) } - .rela.dtors : { *(.rela.dtors) } - .rel.bss : { *(.rel.bss) } - .rela.bss : { *(.rela.bss) } - .rel.plt : { *(.rel.plt) } - .rela.plt : { *(.rela.plt) } - .init : { *(.init) } - .plt : { *(.plt) } - .text : - { - /* WARNING - the following is hand-optimized to fit within */ - /* the sector layout of our flash chips! XXX FIXME XXX */ - - arch/powerpc/cpu/mpc8xx/start.o (.text) - common/dlmalloc.o (.text) - lib/vsprintf.o (.text) - lib/crc32.o (.text) - - . = env_offset; - common/env_embedded.o(.text) - - *(.text) - *(.got1) - } - _etext = .; - PROVIDE (etext = .); - .rodata : - { - *(.rodata) - *(.rodata1) - *(.rodata.str1.4) - *(.eh_frame) - } - .fini : { *(.fini) } =0 - .ctors : { *(.ctors) } - .dtors : { *(.dtors) } - - /* Read-write section, merged into data segment: */ - . = (. + 0x0FFF) & 0xFFFFF000; - _erotext = .; - PROVIDE (erotext = .); - .reloc : - { - *(.got) - _GOT2_TABLE_ = .; - *(.got2) - _FIXUP_TABLE_ = .; - *(.fixup) - } - __got2_entries = (_FIXUP_TABLE_ - _GOT2_TABLE_) >>2; - __fixup_entries = (. - _FIXUP_TABLE_)>>2; - - .data : - { - *(.data) - *(.data1) - *(.sdata) - *(.sdata2) - *(.dynamic) - CONSTRUCTORS - } - _edata = .; - PROVIDE (edata = .); - - - . = ALIGN(4); - .u_boot_list : { - KEEP(*(SORT(.u_boot_list*))); - } - - - __start___ex_table = .; - __ex_table : { *(__ex_table) } - __stop___ex_table = .; - - . = ALIGN(4096); - __init_begin = .; - .text.init : { *(.text.init) } - .data.init : { *(.data.init) } - . = ALIGN(4096); - __init_end = .; - - __bss_start = .; - .bss : - { - *(.sbss) *(.scommon) - *(.dynbss) - *(.bss) - *(COMMON) - } - __bss_end = . ; - PROVIDE (end = .); -} diff --git a/board/freescale/common/Makefile b/board/freescale/common/Makefile index 22b57cc..50d7731 100644 --- a/board/freescale/common/Makefile +++ b/board/freescale/common/Makefile @@ -34,6 +34,8 @@ ifndef CONFIG_RAMBOOT_PBL obj-$(CONFIG_FSL_FIXED_MMC_LOCATION) += sdhc_boot.o endif +obj-$(CONFIG_FSL_DIU_CH7301) += diu_ch7301.o + obj-$(CONFIG_MPC8541CDS) += cds_pci_ft.o obj-$(CONFIG_MPC8548CDS) += cds_pci_ft.o obj-$(CONFIG_MPC8555CDS) += cds_pci_ft.o diff --git a/board/freescale/common/diu_ch7301.c b/board/freescale/common/diu_ch7301.c new file mode 100644 index 0000000..82ce870 --- /dev/null +++ b/board/freescale/common/diu_ch7301.c @@ -0,0 +1,136 @@ +/* + * Copyright 2014 Freescale Semiconductor, Inc. + * Authors: Priyanka Jain <Priyanka.Jain@freescale.com> + * Wang Dongsheng <dongsheng.wang@freescale.com> + * + * This file is copied and modified from the original t1040qds/diu.c. + * Encoder can be used in T104x and LSx Platform. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <stdio_dev.h> +#include <i2c.h> + +#define I2C_DVI_INPUT_DATA_FORMAT_REG 0x1F +#define I2C_DVI_PLL_CHARGE_CNTL_REG 0x33 +#define I2C_DVI_PLL_DIVIDER_REG 0x34 +#define I2C_DVI_PLL_SUPPLY_CNTL_REG 0x35 +#define I2C_DVI_PLL_FILTER_REG 0x36 +#define I2C_DVI_TEST_PATTERN_REG 0x48 +#define I2C_DVI_POWER_MGMT_REG 0x49 +#define I2C_DVI_LOCK_STATE_REG 0x4D +#define I2C_DVI_SYNC_POLARITY_REG 0x56 + +/* + * Set VSYNC/HSYNC to active high. This is polarity of sync signals + * from DIU->DVI. The DIU default is active igh, so DVI is set to + * active high. + */ +#define I2C_DVI_INPUT_DATA_FORMAT_VAL 0x98 + +#define I2C_DVI_PLL_CHARGE_CNTL_HIGH_SPEED_VAL 0x06 +#define I2C_DVI_PLL_DIVIDER_HIGH_SPEED_VAL 0x26 +#define I2C_DVI_PLL_FILTER_HIGH_SPEED_VAL 0xA0 +#define I2C_DVI_PLL_CHARGE_CNTL_LOW_SPEED_VAL 0x08 +#define I2C_DVI_PLL_DIVIDER_LOW_SPEED_VAL 0x16 +#define I2C_DVI_PLL_FILTER_LOW_SPEED_VAL 0x60 + +/* Clear test pattern */ +#define I2C_DVI_TEST_PATTERN_VAL 0x18 +/* Exit Power-down mode */ +#define I2C_DVI_POWER_MGMT_VAL 0xC0 + +/* Monitor polarity is handled via DVI Sync Polarity Register */ +#define I2C_DVI_SYNC_POLARITY_VAL 0x00 + +/* Programming of HDMI Chrontel CH7301 connector */ +int diu_set_dvi_encoder(unsigned int pixclock) +{ + int ret; + u8 temp; + + temp = I2C_DVI_TEST_PATTERN_VAL; + ret = i2c_write(CONFIG_SYS_I2C_DVI_ADDR, I2C_DVI_TEST_PATTERN_REG, 1, + &temp, 1); + if (ret) { + puts("I2C: failed to select proper dvi test pattern\n"); + return ret; + } + temp = I2C_DVI_INPUT_DATA_FORMAT_VAL; + ret = i2c_write(CONFIG_SYS_I2C_DVI_ADDR, I2C_DVI_INPUT_DATA_FORMAT_REG, + 1, &temp, 1); + if (ret) { + puts("I2C: failed to select dvi input data format\n"); + return ret; + } + + /* Set Sync polarity register */ + temp = I2C_DVI_SYNC_POLARITY_VAL; + ret = i2c_write(CONFIG_SYS_I2C_DVI_ADDR, I2C_DVI_SYNC_POLARITY_REG, 1, + &temp, 1); + if (ret) { + puts("I2C: failed to select dvi syc polarity\n"); + return ret; + } + + /* Set PLL registers based on pixel clock rate*/ + if (pixclock > 65000000) { + temp = I2C_DVI_PLL_CHARGE_CNTL_HIGH_SPEED_VAL; + ret = i2c_write(CONFIG_SYS_I2C_DVI_ADDR, + I2C_DVI_PLL_CHARGE_CNTL_REG, 1, &temp, 1); + if (ret) { + puts("I2C: failed to select dvi pll charge_cntl\n"); + return ret; + } + temp = I2C_DVI_PLL_DIVIDER_HIGH_SPEED_VAL; + ret = i2c_write(CONFIG_SYS_I2C_DVI_ADDR, + I2C_DVI_PLL_DIVIDER_REG, 1, &temp, 1); + if (ret) { + puts("I2C: failed to select dvi pll divider\n"); + return ret; + } + temp = I2C_DVI_PLL_FILTER_HIGH_SPEED_VAL; + ret = i2c_write(CONFIG_SYS_I2C_DVI_ADDR, + I2C_DVI_PLL_FILTER_REG, 1, &temp, 1); + if (ret) { + puts("I2C: failed to select dvi pll filter\n"); + return ret; + } + } else { + temp = I2C_DVI_PLL_CHARGE_CNTL_LOW_SPEED_VAL; + ret = i2c_write(CONFIG_SYS_I2C_DVI_ADDR, + I2C_DVI_PLL_CHARGE_CNTL_REG, 1, &temp, 1); + if (ret) { + puts("I2C: failed to select dvi pll charge_cntl\n"); + return ret; + } + temp = I2C_DVI_PLL_DIVIDER_LOW_SPEED_VAL; + ret = i2c_write(CONFIG_SYS_I2C_DVI_ADDR, + I2C_DVI_PLL_DIVIDER_REG, 1, &temp, 1); + if (ret) { + puts("I2C: failed to select dvi pll divider\n"); + return ret; + } + temp = I2C_DVI_PLL_FILTER_LOW_SPEED_VAL; + ret = i2c_write(CONFIG_SYS_I2C_DVI_ADDR, + I2C_DVI_PLL_FILTER_REG, 1, &temp, 1); + if (ret) { + puts("I2C: failed to select dvi pll filter\n"); + return ret; + } + } + + temp = I2C_DVI_POWER_MGMT_VAL; + ret = i2c_write(CONFIG_SYS_I2C_DVI_ADDR, I2C_DVI_POWER_MGMT_REG, 1, + &temp, 1); + if (ret) { + puts("I2C: failed to select dvi power mgmt\n"); + return ret; + } + + udelay(500); + + return 0; +} diff --git a/board/freescale/common/diu_ch7301.h b/board/freescale/common/diu_ch7301.h new file mode 100644 index 0000000..8b6ead0 --- /dev/null +++ b/board/freescale/common/diu_ch7301.h @@ -0,0 +1,13 @@ +/* + * Copyright 2014 Freescale Semiconductor, Inc. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __DIU_HDMI_CH7301__ +#define __DIU_HDMI_CH7301__ + +/* Programming of HDMI Chrontel CH7301 connector */ +int diu_set_dvi_encoder(unsigned int pixclock); + +#endif diff --git a/board/freescale/mx31pdk/MAINTAINERS b/board/freescale/mx31pdk/MAINTAINERS index 2e057db..ec2a320 100644 --- a/board/freescale/mx31pdk/MAINTAINERS +++ b/board/freescale/mx31pdk/MAINTAINERS @@ -1,5 +1,5 @@ MX31PDK BOARD -M: Fabio Estevam <fabio.estevam@freescale.com> +M: Magnus Lilja <lilja.magnus@gmail.com> S: Maintained F: board/freescale/mx31pdk/ F: include/configs/mx31pdk.h diff --git a/board/freescale/mx6sabresd/mx6sabresd.c b/board/freescale/mx6sabresd/mx6sabresd.c index d7c4b4f..80c8ebd 100644 --- a/board/freescale/mx6sabresd/mx6sabresd.c +++ b/board/freescale/mx6sabresd/mx6sabresd.c @@ -466,7 +466,7 @@ static int pfuze_init(void) if (ret) return ret; - p = pmic_get("PFUZE100_PMIC"); + p = pmic_get("PFUZE100"); ret = pmic_probe(p); if (ret) return ret; diff --git a/board/freescale/mx6slevk/mx6slevk.c b/board/freescale/mx6slevk/mx6slevk.c index d2b64cc..a990b4c 100644 --- a/board/freescale/mx6slevk/mx6slevk.c +++ b/board/freescale/mx6slevk/mx6slevk.c @@ -130,8 +130,7 @@ int board_eth_init(bd_t *bis) static int setup_fec(void) { - struct iomuxc_base_regs *iomuxc_regs = - (struct iomuxc_base_regs *)IOMUXC_BASE_ADDR; + struct iomuxc *iomuxc_regs = (struct iomuxc *)IOMUXC_BASE_ADDR; int ret; /* clear gpr1[14], gpr1[18:17] to select anatop clock */ diff --git a/board/ti/omap5912osk/Kconfig b/board/freescale/mx6sxsabresd/Kconfig index 9f7493a..ee8f4a6 100644 --- a/board/ti/omap5912osk/Kconfig +++ b/board/freescale/mx6sxsabresd/Kconfig @@ -1,23 +1,23 @@ -if TARGET_OMAP5912OSK +if TARGET_MX6SXSABRESD config SYS_CPU string - default "arm926ejs" + default "armv7" config SYS_BOARD string - default "omap5912osk" + default "mx6sxsabresd" config SYS_VENDOR string - default "ti" + default "freescale" config SYS_SOC string - default "omap" + default "mx6" config SYS_CONFIG_NAME string - default "omap5912osk" + default "mx6sxsabresd" endif diff --git a/board/freescale/mx6sxsabresd/MAINTAINERS b/board/freescale/mx6sxsabresd/MAINTAINERS new file mode 100644 index 0000000..f52f300 --- /dev/null +++ b/board/freescale/mx6sxsabresd/MAINTAINERS @@ -0,0 +1,6 @@ +MX6SXSABRESD BOARD +M: Fabio Estevam <fabio.estevam@freescale.com> +S: Maintained +F: board/freescale/mx6sxsabresd/ +F: include/configs/mx6sxsabresd.h +F: configs/mx6sxsabresd_defconfig diff --git a/board/freescale/mx6sxsabresd/Makefile b/board/freescale/mx6sxsabresd/Makefile new file mode 100644 index 0000000..97dbfda --- /dev/null +++ b/board/freescale/mx6sxsabresd/Makefile @@ -0,0 +1,6 @@ +# (C) Copyright 2014 Freescale Semiconductor, Inc. +# +# SPDX-License-Identifier: GPL-2.0+ +# + +obj-y := mx6sxsabresd.o diff --git a/board/freescale/mx6sxsabresd/imximage.cfg b/board/freescale/mx6sxsabresd/imximage.cfg new file mode 100644 index 0000000..c862617 --- /dev/null +++ b/board/freescale/mx6sxsabresd/imximage.cfg @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2014 Freescale Semiconductor, Inc. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#define __ASSEMBLY__ +#include <config.h> + +/* image version */ + +IMAGE_VERSION 2 + +/* + * Boot Device : one of + * spi/sd/nand/onenand, qspi/nor + */ + +BOOT_FROM sd + +/* + * Device Configuration Data (DCD) + * + * Each entry must have the format: + * Addr-type Address Value + * + * where: + * Addr-type register length (1,2 or 4 bytes) + * Address absolute address of the register + * value value to be stored in the register + */ + +/* Enable all clocks */ +DATA 4 0x020c4068 0xffffffff +DATA 4 0x020c406c 0xffffffff +DATA 4 0x020c4070 0xffffffff +DATA 4 0x020c4074 0xffffffff +DATA 4 0x020c4078 0xffffffff +DATA 4 0x020c407c 0xffffffff +DATA 4 0x020c4080 0xffffffff +DATA 4 0x020c4084 0xffffffff + +/* IOMUX - DDR IO Type */ +DATA 4 0x020e0618 0x000c0000 +DATA 4 0x020e05fc 0x00000000 + +/* Clock */ +DATA 4 0x020e032c 0x00000030 + +/* Address */ +DATA 4 0x020e0300 0x00000020 +DATA 4 0x020e02fc 0x00000020 +DATA 4 0x020e05f4 0x00000020 + +/* Control */ +DATA 4 0x020e0340 0x00000020 + +DATA 4 0x020e0320 0x00000000 +DATA 4 0x020e0310 0x00000020 +DATA 4 0x020e0314 0x00000020 +DATA 4 0x020e0614 0x00000020 + +/* Data Strobe */ +DATA 4 0x020e05f8 0x00020000 +DATA 4 0x020e0330 0x00000028 +DATA 4 0x020e0334 0x00000028 +DATA 4 0x020e0338 0x00000028 +DATA 4 0x020e033c 0x00000028 + +/* Data */ +DATA 4 0x020e0608 0x00020000 +DATA 4 0x020e060c 0x00000028 +DATA 4 0x020e0610 0x00000028 +DATA 4 0x020e061c 0x00000028 +DATA 4 0x020e0620 0x00000028 +DATA 4 0x020e02ec 0x00000028 +DATA 4 0x020e02f0 0x00000028 +DATA 4 0x020e02f4 0x00000028 +DATA 4 0x020e02f8 0x00000028 + +/* Calibrations - ZQ */ +DATA 4 0x021b0800 0xa1390003 + +/* Write leveling */ +DATA 4 0x021b080c 0x00290025 +DATA 4 0x021b0810 0x00220022 + +/* DQS Read Gate */ +DATA 4 0x021b083c 0x41480144 +DATA 4 0x021b0840 0x01340130 + +/* Read/Write Delay */ +DATA 4 0x021b0848 0x3C3E4244 +DATA 4 0x021b0850 0x34363638 + +/* Read data bit delay */ +DATA 4 0x021b081c 0x33333333 +DATA 4 0x021b0820 0x33333333 +DATA 4 0x021b0824 0x33333333 +DATA 4 0x021b0828 0x33333333 + +/* Complete calibration by forced measurement */ +DATA 4 0x021b08b8 0x00000800 + +/* MMDC init - DDR3, 64-bit mode, only MMDC0 is initiated */ +DATA 4 0x021b0004 0x0002002d +DATA 4 0x021b0008 0x00333030 +DATA 4 0x021b000c 0x676b52f3 +DATA 4 0x021b0010 0xb66d8b63 +DATA 4 0x021b0014 0x01ff00db +DATA 4 0x021b0018 0x00011740 +DATA 4 0x021b001c 0x00008000 +DATA 4 0x021b002c 0x000026d2 +DATA 4 0x021b0030 0x006b1023 +DATA 4 0x021b0040 0x0000005f +DATA 4 0x021b0000 0x84190000 + +/* Initialize MT41K256M16HA-125 - MR2 */ +DATA 4 0x021b001c 0x04008032 +/* MR3 */ +DATA 4 0x021b001c 0x00008033 +/* MR1 */ +DATA 4 0x021b001c 0x00048031 +/* MR0 */ +DATA 4 0x021b001c 0x05208030 +/* DDR device ZQ calibration */ +DATA 4 0x021b001c 0x04008040 + +/* Final DDR setup, before operation start */ +DATA 4 0x021b0020 0x00000800 +DATA 4 0x021b0818 0x00011117 +DATA 4 0x021b001c 0x00000000 diff --git a/board/freescale/mx6sxsabresd/mx6sxsabresd.c b/board/freescale/mx6sxsabresd/mx6sxsabresd.c new file mode 100644 index 0000000..5eaec1b --- /dev/null +++ b/board/freescale/mx6sxsabresd/mx6sxsabresd.c @@ -0,0 +1,295 @@ +/* + * Copyright (C) 2014 Freescale Semiconductor, Inc. + * + * Author: Fabio Estevam <fabio.estevam@freescale.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <asm/arch/clock.h> +#include <asm/arch/crm_regs.h> +#include <asm/arch/iomux.h> +#include <asm/arch/imx-regs.h> +#include <asm/arch/mx6-pins.h> +#include <asm/arch/sys_proto.h> +#include <asm/gpio.h> +#include <asm/imx-common/iomux-v3.h> +#include <asm/io.h> +#include <asm/imx-common/mxc_i2c.h> +#include <linux/sizes.h> +#include <common.h> +#include <fsl_esdhc.h> +#include <mmc.h> +#include <i2c.h> +#include <miiphy.h> +#include <netdev.h> +#include <power/pmic.h> +#include <power/pfuze100_pmic.h> + +DECLARE_GLOBAL_DATA_PTR; + +#define UART_PAD_CTRL (PAD_CTL_PKE | PAD_CTL_PUE | \ + PAD_CTL_PUS_100K_UP | PAD_CTL_SPEED_MED | \ + PAD_CTL_DSE_40ohm | PAD_CTL_SRE_FAST | PAD_CTL_HYS) + +#define USDHC_PAD_CTRL (PAD_CTL_PKE | PAD_CTL_PUE | \ + PAD_CTL_PUS_22K_UP | PAD_CTL_SPEED_LOW | \ + PAD_CTL_DSE_80ohm | PAD_CTL_SRE_FAST | PAD_CTL_HYS) + +#define I2C_PAD_CTRL (PAD_CTL_PKE | PAD_CTL_PUE | \ + PAD_CTL_PUS_100K_UP | PAD_CTL_SPEED_MED | \ + PAD_CTL_DSE_40ohm | PAD_CTL_HYS | \ + PAD_CTL_ODE) + +#define ENET_PAD_CTRL (PAD_CTL_PUS_100K_UP | PAD_CTL_PUE | \ + PAD_CTL_SPEED_HIGH | \ + PAD_CTL_DSE_48ohm | PAD_CTL_SRE_FAST) + +#define ENET_CLK_PAD_CTRL (PAD_CTL_SPEED_MED | \ + PAD_CTL_DSE_120ohm | PAD_CTL_SRE_FAST) + +#define ENET_RX_PAD_CTRL (PAD_CTL_PKE | PAD_CTL_PUE | \ + PAD_CTL_SPEED_HIGH | PAD_CTL_SRE_FAST) + +#define I2C_PAD_CTRL (PAD_CTL_PKE | PAD_CTL_PUE | \ + PAD_CTL_PUS_100K_UP | PAD_CTL_SPEED_MED | \ + PAD_CTL_DSE_40ohm | PAD_CTL_HYS | \ + PAD_CTL_ODE) + +int dram_init(void) +{ + gd->ram_size = PHYS_SDRAM_SIZE; + + return 0; +} + +static iomux_v3_cfg_t const uart1_pads[] = { + MX6_PAD_GPIO1_IO04__UART1_TX | MUX_PAD_CTRL(UART_PAD_CTRL), + MX6_PAD_GPIO1_IO05__UART1_RX | MUX_PAD_CTRL(UART_PAD_CTRL), +}; + +static iomux_v3_cfg_t const usdhc4_pads[] = { + MX6_PAD_SD4_CLK__USDHC4_CLK | MUX_PAD_CTRL(USDHC_PAD_CTRL), + MX6_PAD_SD4_CMD__USDHC4_CMD | MUX_PAD_CTRL(USDHC_PAD_CTRL), + MX6_PAD_SD4_DATA0__USDHC4_DATA0 | MUX_PAD_CTRL(USDHC_PAD_CTRL), + MX6_PAD_SD4_DATA1__USDHC4_DATA1 | MUX_PAD_CTRL(USDHC_PAD_CTRL), + MX6_PAD_SD4_DATA2__USDHC4_DATA2 | MUX_PAD_CTRL(USDHC_PAD_CTRL), + MX6_PAD_SD4_DATA3__USDHC4_DATA3 | MUX_PAD_CTRL(USDHC_PAD_CTRL), + MX6_PAD_SD4_DATA7__GPIO6_IO_21 | MUX_PAD_CTRL(NO_PAD_CTRL), +}; + +static iomux_v3_cfg_t const fec1_pads[] = { + MX6_PAD_ENET1_MDC__ENET1_MDC | MUX_PAD_CTRL(ENET_PAD_CTRL), + MX6_PAD_ENET1_MDIO__ENET1_MDIO | MUX_PAD_CTRL(ENET_PAD_CTRL), + MX6_PAD_RGMII1_RX_CTL__ENET1_RX_EN | MUX_PAD_CTRL(ENET_RX_PAD_CTRL), + MX6_PAD_RGMII1_RD0__ENET1_RX_DATA_0 | MUX_PAD_CTRL(ENET_RX_PAD_CTRL), + MX6_PAD_RGMII1_RD1__ENET1_RX_DATA_1 | MUX_PAD_CTRL(ENET_RX_PAD_CTRL), + MX6_PAD_RGMII1_RD2__ENET1_RX_DATA_2 | MUX_PAD_CTRL(ENET_RX_PAD_CTRL), + MX6_PAD_RGMII1_RD3__ENET1_RX_DATA_3 | MUX_PAD_CTRL(ENET_RX_PAD_CTRL), + MX6_PAD_RGMII1_RXC__ENET1_RX_CLK | MUX_PAD_CTRL(ENET_RX_PAD_CTRL), + MX6_PAD_RGMII1_TX_CTL__ENET1_TX_EN | MUX_PAD_CTRL(ENET_PAD_CTRL), + MX6_PAD_RGMII1_TD0__ENET1_TX_DATA_0 | MUX_PAD_CTRL(ENET_PAD_CTRL), + MX6_PAD_RGMII1_TD1__ENET1_TX_DATA_1 | MUX_PAD_CTRL(ENET_PAD_CTRL), + MX6_PAD_RGMII1_TD2__ENET1_TX_DATA_2 | MUX_PAD_CTRL(ENET_PAD_CTRL), + MX6_PAD_RGMII1_TD3__ENET1_TX_DATA_3 | MUX_PAD_CTRL(ENET_PAD_CTRL), + MX6_PAD_RGMII1_TXC__ENET1_RGMII_TXC | MUX_PAD_CTRL(ENET_PAD_CTRL), +}; + +static iomux_v3_cfg_t const peri_3v3_pads[] = { + MX6_PAD_QSPI1A_DATA0__GPIO4_IO_16 | MUX_PAD_CTRL(NO_PAD_CTRL), +}; + +static iomux_v3_cfg_t const phy_control_pads[] = { + /* 25MHz Ethernet PHY Clock */ + MX6_PAD_ENET2_RX_CLK__ENET2_REF_CLK_25M | MUX_PAD_CTRL(ENET_CLK_PAD_CTRL), + + /* ENET PHY Power */ + MX6_PAD_ENET2_COL__GPIO2_IO_6 | MUX_PAD_CTRL(NO_PAD_CTRL), + + /* AR8031 PHY Reset */ + MX6_PAD_ENET2_CRS__GPIO2_IO_7 | MUX_PAD_CTRL(NO_PAD_CTRL), +}; + +static void setup_iomux_uart(void) +{ + imx_iomux_v3_setup_multiple_pads(uart1_pads, ARRAY_SIZE(uart1_pads)); +} + +static int setup_fec(void) +{ + struct iomuxc *iomuxc_regs = (struct iomuxc *)IOMUXC_BASE_ADDR; + struct anatop_regs *anatop = (struct anatop_regs *)ANATOP_BASE_ADDR; + int ret; + int reg; + + /* Use 125MHz anatop loopback REF_CLK1 for ENET1 */ + clrsetbits_le32(&iomuxc_regs->gpr[1], IOMUX_GPR1_FEC1_MASK, 0); + + imx_iomux_v3_setup_multiple_pads(phy_control_pads, + ARRAY_SIZE(phy_control_pads)); + + /* Enable the ENET power, active low */ + gpio_direction_output(IMX_GPIO_NR(2, 6) , 0); + + /* Reset AR8031 PHY */ + gpio_direction_output(IMX_GPIO_NR(2, 7) , 0); + udelay(500); + gpio_set_value(IMX_GPIO_NR(2, 7), 1); + + reg = readl(&anatop->pll_enet); + reg |= BM_ANADIG_PLL_ENET_REF_25M_ENABLE; + writel(reg, &anatop->pll_enet); + + ret = enable_fec_anatop_clock(ENET_125MHz); + if (ret) + return ret; + + return 0; +} + +int board_eth_init(bd_t *bis) +{ + imx_iomux_v3_setup_multiple_pads(fec1_pads, ARRAY_SIZE(fec1_pads)); + setup_fec(); + + return cpu_eth_init(bis); +} + +#define PC MUX_PAD_CTRL(I2C_PAD_CTRL) +/* I2C1 for PMIC */ +struct i2c_pads_info i2c_pad_info1 = { + .scl = { + .i2c_mode = MX6_PAD_GPIO1_IO00__I2C1_SCL | PC, + .gpio_mode = MX6_PAD_GPIO1_IO00__GPIO1_IO_0 | PC, + .gp = IMX_GPIO_NR(1, 0), + }, + .sda = { + .i2c_mode = MX6_PAD_GPIO1_IO01__I2C1_SDA | PC, + .gpio_mode = MX6_PAD_GPIO1_IO01__GPIO1_IO_1 | PC, + .gp = IMX_GPIO_NR(1, 1), + }, +}; + +static int pfuze_init(void) +{ + struct pmic *p; + int ret; + unsigned int reg; + + ret = power_pfuze100_init(I2C_PMIC); + if (ret) + return ret; + + p = pmic_get("PFUZE100"); + ret = pmic_probe(p); + if (ret) + return ret; + + pmic_reg_read(p, PFUZE100_DEVICEID, ®); + printf("PMIC: PFUZE100 ID=0x%02x\n", reg); + + /* Set SW1AB standby voltage to 0.975V */ + pmic_reg_read(p, PFUZE100_SW1ABSTBY, ®); + reg &= ~0x3f; + reg |= 0x1b; + pmic_reg_write(p, PFUZE100_SW1ABSTBY, reg); + + /* Set SW1AB/VDDARM step ramp up time from 16us to 4us/25mV */ + pmic_reg_read(p, PUZE_100_SW1ABCONF, ®); + reg &= ~0xc0; + reg |= 0x40; + pmic_reg_write(p, PUZE_100_SW1ABCONF, reg); + + /* Set SW1C standby voltage to 0.975V */ + pmic_reg_read(p, PFUZE100_SW1CSTBY, ®); + reg &= ~0x3f; + reg |= 0x1b; + pmic_reg_write(p, PFUZE100_SW1CSTBY, reg); + + /* Set SW1C/VDDSOC step ramp up time from 16us to 4us/25mV */ + pmic_reg_read(p, PFUZE100_SW1CCONF, ®); + reg &= ~0xc0; + reg |= 0x40; + pmic_reg_write(p, PFUZE100_SW1CCONF, reg); + + /* Enable power of VGEN5 3V3, needed for SD3 */ + pmic_reg_read(p, PFUZE100_VGEN5VOL, ®); + reg &= ~0x1F; + reg |= 0x1F; + pmic_reg_write(p, PFUZE100_VGEN5VOL, reg); + + return 0; +} + +int board_phy_config(struct phy_device *phydev) +{ + /* + * Enable 1.8V(SEL_1P5_1P8_POS_REG) on + * Phy control debug reg 0 + */ + phy_write(phydev, MDIO_DEVAD_NONE, 0x1d, 0x1f); + phy_write(phydev, MDIO_DEVAD_NONE, 0x1e, 0x8); + + /* rgmii tx clock delay enable */ + phy_write(phydev, MDIO_DEVAD_NONE, 0x1d, 0x05); + phy_write(phydev, MDIO_DEVAD_NONE, 0x1e, 0x100); + + if (phydev->drv->config) + phydev->drv->config(phydev); + + return 0; +} + +int board_early_init_f(void) +{ + setup_iomux_uart(); + setup_i2c(1, CONFIG_SYS_I2C_SPEED, 0x7f, &i2c_pad_info1); + + /* Enable PERI_3V3, which is used by SD2, ENET, LVDS, BT */ + imx_iomux_v3_setup_multiple_pads(peri_3v3_pads, + ARRAY_SIZE(peri_3v3_pads)); + + /* Active high for ncp692 */ + gpio_direction_output(IMX_GPIO_NR(4, 16) , 1); + + return 0; +} + +static struct fsl_esdhc_cfg usdhc_cfg[1] = { + {USDHC4_BASE_ADDR}, +}; + +int board_mmc_getcd(struct mmc *mmc) +{ + return 1; /* Assume boot SD always present */ +} + +int board_mmc_init(bd_t *bis) +{ + imx_iomux_v3_setup_multiple_pads(usdhc4_pads, ARRAY_SIZE(usdhc4_pads)); + + usdhc_cfg[0].sdhc_clk = mxc_get_clock(MXC_ESDHC4_CLK); + return fsl_esdhc_initialize(bis, &usdhc_cfg[0]); +} + +int board_init(void) +{ + /* Address of boot parameters */ + gd->bd->bi_boot_params = PHYS_SDRAM + 0x100; + + return 0; +} + +int board_late_init(void) +{ + pfuze_init(); + + return 0; +} + +int checkboard(void) +{ + puts("Board: MX6SX SABRE SDB\n"); + + return 0; +} diff --git a/board/freescale/t1040qds/diu.c b/board/freescale/t1040qds/diu.c index ffd074b..0214224 100644 --- a/board/freescale/t1040qds/diu.c +++ b/board/freescale/t1040qds/diu.c @@ -13,42 +13,9 @@ #include <video_fb.h> #include <fsl_diu_fb.h> #include "../common/qixis.h" +#include "../common/diu_ch7301.h" #include "t1040qds.h" #include "t1040qds_qixis.h" -#include <i2c.h> - - -#define I2C_DVI_INPUT_DATA_FORMAT_REG 0x1F -#define I2C_DVI_PLL_CHARGE_CNTL_REG 0x33 -#define I2C_DVI_PLL_DIVIDER_REG 0x34 -#define I2C_DVI_PLL_SUPPLY_CNTL_REG 0x35 -#define I2C_DVI_PLL_FILTER_REG 0x36 -#define I2C_DVI_TEST_PATTERN_REG 0x48 -#define I2C_DVI_POWER_MGMT_REG 0x49 -#define I2C_DVI_LOCK_STATE_REG 0x4D -#define I2C_DVI_SYNC_POLARITY_REG 0x56 - -/* - * Set VSYNC/HSYNC to active high. This is polarity of sync signals - * from DIU->DVI. The DIU default is active igh, so DVI is set to - * active high. - */ -#define I2C_DVI_INPUT_DATA_FORMAT_VAL 0x98 - -#define I2C_DVI_PLL_CHARGE_CNTL_HIGH_SPEED_VAL 0x06 -#define I2C_DVI_PLL_DIVIDER_HIGH_SPEED_VAL 0x26 -#define I2C_DVI_PLL_FILTER_HIGH_SPEED_VAL 0xA0 -#define I2C_DVI_PLL_CHARGE_CNTL_LOW_SPEED_VAL 0x08 -#define I2C_DVI_PLL_DIVIDER_LOW_SPEED_VAL 0x16 -#define I2C_DVI_PLL_FILTER_LOW_SPEED_VAL 0x60 - -/* Clear test pattern */ -#define I2C_DVI_TEST_PATTERN_VAL 0x18 -/* Exit Power-down mode */ -#define I2C_DVI_POWER_MGMT_VAL 0xC0 - -/* Monitor polarity is handled via DVI Sync Polarity Register */ -#define I2C_DVI_SYNC_POLARITY_VAL 0x00 /* * DIU Area Descriptor @@ -69,98 +36,6 @@ #define AD_COMP_1_SHIFT 4 #define AD_COMP_0_SHIFT 0 -/* Programming of HDMI Chrontel CH7301 connector */ -int diu_set_dvi_encoder(unsigned int pixclock) -{ - int ret; - u8 temp; - select_i2c_ch_pca9547(I2C_MUX_CH_DIU); - - temp = I2C_DVI_TEST_PATTERN_VAL; - ret = i2c_write(CONFIG_SYS_I2C_DVI_ADDR, I2C_DVI_TEST_PATTERN_REG, 1, - &temp, 1); - if (ret) { - puts("I2C: failed to select proper dvi test pattern\n"); - return ret; - } - temp = I2C_DVI_INPUT_DATA_FORMAT_VAL; - ret = i2c_write(CONFIG_SYS_I2C_DVI_ADDR, I2C_DVI_INPUT_DATA_FORMAT_REG, - 1, &temp, 1); - if (ret) { - puts("I2C: failed to select dvi input data format\n"); - return ret; - } - - /* Set Sync polarity register */ - temp = I2C_DVI_SYNC_POLARITY_VAL; - ret = i2c_write(CONFIG_SYS_I2C_DVI_ADDR, I2C_DVI_SYNC_POLARITY_REG, 1, - &temp, 1); - if (ret) { - puts("I2C: failed to select dvi syc polarity\n"); - return ret; - } - - /* Set PLL registers based on pixel clock rate*/ - if (pixclock > 65000000) { - temp = I2C_DVI_PLL_CHARGE_CNTL_HIGH_SPEED_VAL; - ret = i2c_write(CONFIG_SYS_I2C_DVI_ADDR, - I2C_DVI_PLL_CHARGE_CNTL_REG, 1, &temp, 1); - if (ret) { - puts("I2C: failed to select dvi pll charge_cntl\n"); - return ret; - } - temp = I2C_DVI_PLL_DIVIDER_HIGH_SPEED_VAL; - ret = i2c_write(CONFIG_SYS_I2C_DVI_ADDR, - I2C_DVI_PLL_DIVIDER_REG, 1, &temp, 1); - if (ret) { - puts("I2C: failed to select dvi pll divider\n"); - return ret; - } - temp = I2C_DVI_PLL_FILTER_HIGH_SPEED_VAL; - ret = i2c_write(CONFIG_SYS_I2C_DVI_ADDR, - I2C_DVI_PLL_FILTER_REG, 1, &temp, 1); - if (ret) { - puts("I2C: failed to select dvi pll filter\n"); - return ret; - } - } else { - temp = I2C_DVI_PLL_CHARGE_CNTL_LOW_SPEED_VAL; - ret = i2c_write(CONFIG_SYS_I2C_DVI_ADDR, - I2C_DVI_PLL_CHARGE_CNTL_REG, 1, &temp, 1); - if (ret) { - puts("I2C: failed to select dvi pll charge_cntl\n"); - return ret; - } - temp = I2C_DVI_PLL_DIVIDER_LOW_SPEED_VAL; - ret = i2c_write(CONFIG_SYS_I2C_DVI_ADDR, - I2C_DVI_PLL_DIVIDER_REG, 1, &temp, 1); - if (ret) { - puts("I2C: failed to select dvi pll divider\n"); - return ret; - } - temp = I2C_DVI_PLL_FILTER_LOW_SPEED_VAL; - ret = i2c_write(CONFIG_SYS_I2C_DVI_ADDR, - I2C_DVI_PLL_FILTER_REG, 1, &temp, 1); - if (ret) { - puts("I2C: failed to select dvi pll filter\n"); - return ret; - } - } - - temp = I2C_DVI_POWER_MGMT_VAL; - ret = i2c_write(CONFIG_SYS_I2C_DVI_ADDR, I2C_DVI_POWER_MGMT_REG, 1, - &temp, 1); - if (ret) { - puts("I2C: failed to select dvi power mgmt\n"); - return ret; - } - - udelay(500); - - select_i2c_ch_pca9547(I2C_MUX_CH_DEFAULT); - return 0; -} - void diu_set_pixel_clock(unsigned int pixclock) { unsigned long speed_ccb, temp; @@ -172,12 +47,19 @@ void diu_set_pixel_clock(unsigned int pixclock) pixval = speed_ccb / temp; /* Program HDMI encoder */ + /* Switch channel to DIU */ + select_i2c_ch_pca9547(I2C_MUX_CH_DIU); + + /* Set dispaly encoder */ ret = diu_set_dvi_encoder(temp); if (ret) { puts("Failed to set DVI encoder\n"); return; } + /* Switch channel to default */ + select_i2c_ch_pca9547(I2C_MUX_CH_DEFAULT); + /* Program pixel clock */ out_be32((unsigned *)CONFIG_SYS_FSL_SCFG_PIXCLK_ADDR, ((pixval << PXCK_BITS_START) & PXCK_MASK)); diff --git a/board/freescale/t104xrdb/Makefile b/board/freescale/t104xrdb/Makefile index 6cd304c..b9ef17f 100644 --- a/board/freescale/t104xrdb/Makefile +++ b/board/freescale/t104xrdb/Makefile @@ -11,6 +11,7 @@ obj-y += t104xrdb.o obj-y += cpld.o obj-y += eth.o obj-$(CONFIG_PCI) += pci.o +obj-$(CONFIG_FSL_DIU_FB)+= diu.o endif obj-y += ddr.o obj-y += law.o diff --git a/board/freescale/t104xrdb/diu.c b/board/freescale/t104xrdb/diu.c new file mode 100644 index 0000000..3285bef --- /dev/null +++ b/board/freescale/t104xrdb/diu.c @@ -0,0 +1,84 @@ +/* + * Copyright 2014 Freescale Semiconductor, Inc. + * Author: Priyanka Jain <Priyanka.Jain@freescale.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <asm/io.h> +#include <common.h> +#include <command.h> +#include <fsl_diu_fb.h> +#include <linux/ctype.h> +#include <video_fb.h> + +#include "../common/diu_ch7301.h" + +#include "cpld.h" +#include "t104xrdb.h" + +/* + * DIU Area Descriptor + * + * Note that we need to byte-swap the value before it's written to the AD + * register. So even though the registers don't look like they're in the same + * bit positions as they are on the MPC8610, the same value is written to the + * AD register on the MPC8610 and on the P1022. + */ +#define AD_BYTE_F 0x10000000 +#define AD_ALPHA_C_SHIFT 25 +#define AD_BLUE_C_SHIFT 23 +#define AD_GREEN_C_SHIFT 21 +#define AD_RED_C_SHIFT 19 +#define AD_PIXEL_S_SHIFT 16 +#define AD_COMP_3_SHIFT 12 +#define AD_COMP_2_SHIFT 8 +#define AD_COMP_1_SHIFT 4 +#define AD_COMP_0_SHIFT 0 + +void diu_set_pixel_clock(unsigned int pixclock) +{ + unsigned long speed_ccb, temp; + u32 pixval; + int ret; + + speed_ccb = get_bus_freq(0); + temp = 1000000000 / pixclock; + temp *= 1000; + pixval = speed_ccb / temp; + + /* Program HDMI encoder */ + ret = diu_set_dvi_encoder(temp); + if (ret) { + puts("Failed to set DVI encoder\n"); + return; + } + + /* Program pixel clock */ + out_be32((unsigned *)CONFIG_SYS_FSL_SCFG_PIXCLK_ADDR, + ((pixval << PXCK_BITS_START) & PXCK_MASK)); + + /* enable clock*/ + out_be32((unsigned *)CONFIG_SYS_FSL_SCFG_PIXCLK_ADDR, PXCKEN_MASK | + ((pixval << PXCK_BITS_START) & PXCK_MASK)); +} + +int platform_diu_init(unsigned int xres, unsigned int yres, const char *port) +{ + u32 pixel_format; + u8 sw; + + /*Configure Display ouput port as HDMI*/ + sw = CPLD_READ(sfp_ctl_status); + CPLD_WRITE(sfp_ctl_status , sw & ~(CPLD_DIU_SEL_DFP)); + + pixel_format = cpu_to_le32(AD_BYTE_F | (3 << AD_ALPHA_C_SHIFT) | + (0 << AD_BLUE_C_SHIFT) | (1 << AD_GREEN_C_SHIFT) | + (2 << AD_RED_C_SHIFT) | (8 << AD_COMP_3_SHIFT) | + (8 << AD_COMP_2_SHIFT) | (8 << AD_COMP_1_SHIFT) | + (8 << AD_COMP_0_SHIFT) | (3 << AD_PIXEL_S_SHIFT)); + + printf("DIU: Switching to monitor DVI @ %ux%u\n", xres, yres); + + return fsl_diu_init(xres, yres, pixel_format, 0); +} diff --git a/board/freescale/t104xrdb/spl.c b/board/freescale/t104xrdb/spl.c index c628c95..3822a37 100644 --- a/board/freescale/t104xrdb/spl.c +++ b/board/freescale/t104xrdb/spl.c @@ -11,6 +11,7 @@ #include <mmc.h> #include <fsl_esdhc.h> #include <spi_flash.h> +#include <asm/mpc85xx_gpio.h> DECLARE_GLOBAL_DATA_PTR; @@ -55,6 +56,11 @@ void board_init_f(ulong bootflag) /* Update GD pointer */ gd = (gd_t *)(CONFIG_SPL_GD_ADDR); +#ifdef CONFIG_DEEP_SLEEP + /* disable the console if boot from deep sleep */ + if (in_be32(&gur->scrtsr[0]) & (1 << 3)) + gd->flags |= GD_FLG_SILENT | GD_FLG_DISABLE_CONSOLE; +#endif /* compiler optimization barrier needed for GCC >= 3.4 */ __asm__ __volatile__("" : : : "memory"); @@ -120,3 +126,16 @@ void board_init_r(gd_t *gd, ulong dest_addr) nand_boot(); #endif } + +#ifdef CONFIG_DEEP_SLEEP +void board_mem_sleep_setup(void) +{ + void __iomem *cpld_base = (void *)CONFIG_SYS_CPLD_BASE; + + /* does not provide HW signals for power management */ + clrbits_8(cpld_base + 0x17, 0x40); + /* Disable MCKE isolation */ + gpio_set_value(2, 0); + udelay(1); +} +#endif diff --git a/doc/README.t4240qds b/board/freescale/t4qds/README index ef8c75f..3962fee 100644 --- a/doc/README.t4240qds +++ b/board/freescale/t4qds/README @@ -79,6 +79,25 @@ Board Features - High-speed serial flash Two Serial port Four I2C ports + XFI + XFI is supported on T4QDS-XFI board which removed slot3 and routed + four Lanes A/B/C/D to a SFP+ cages, which to house fiber cable or + direct attach cable(copper), the copper cable is used to emulate + 10GBASE-KR scenario. + So, for XFI usage, there are two scenarios, one will use fiber cable, + another will use copper cable. An hwconfig env "fsl_10gkr_copper" is + introduced to indicate a XFI port will use copper cable, and U-boot + will fixup the dtb accordingly. + It's used as: fsl_10gkr_copper:<10g_mac_name> + The <10g_mac_name> can be fm1_10g1, fm1_10g2, fm2_10g1, fm2_10g2, they + do not have to be coexist in hwconfig. If a MAC is listed in the env + "fsl_10gkr_copper", it will use copper cable, otherwise, fiber cable + will be used by default. + for ex. set "fsl_10gkr_copper:fm1_10g1,fm1_10g2,fm2_10g1,fm2_10g2" in + hwconfig, then both four XFI ports will use copper cable. + set "fsl_10gkr_copper:fm1_10g1,fm1_10g2" in hwconfig, then first two + XFI ports will use copper cable, the other two XFI ports will use fiber + cable. Memory map ---------- diff --git a/board/freescale/t4qds/eth.c b/board/freescale/t4qds/eth.c index 6210e46..9b416b1 100644 --- a/board/freescale/t4qds/eth.c +++ b/board/freescale/t4qds/eth.c @@ -23,6 +23,7 @@ #include <phy.h> #include <asm/fsl_dtsec.h> #include <asm/fsl_serdes.h> +#include <hwconfig.h> #include "../common/qixis.h" #include "../common/fman.h" @@ -173,6 +174,10 @@ void board_ft_fman_fixup_port(void *blob, char * prop, phys_addr_t pa, enum fm_port port, int offset) { int interface = fm_info_get_enet_if(port); + ccsr_gur_t *gur = (void *)(CONFIG_SYS_MPC85xx_GUTS_ADDR); + u32 prtcl2 = in_be32(&gur->rcwsr[4]) & FSL_CORENET2_RCWSR4_SRDS2_PRTCL; + + prtcl2 >>= FSL_CORENET2_RCWSR4_SRDS2_PRTCL_SHIFT; if (interface == PHY_INTERFACE_MODE_SGMII || interface == PHY_INTERFACE_MODE_QSGMII) { @@ -262,6 +267,76 @@ void board_ft_fman_fixup_port(void *blob, char * prop, phys_addr_t pa, default: break; } + } else if (interface == PHY_INTERFACE_MODE_XGMII && + ((prtcl2 == 55) || (prtcl2 == 57))) { + /* + * if the 10G is XFI, check hwconfig to see what is the + * media type, there are two types, fiber or copper, + * fix the dtb accordingly. + */ + int media_type = 0; + struct fixed_link f_link; + char lane_mode[20] = {"10GBASE-KR"}; + char buf[32] = "serdes-2,"; + int off; + + switch (port) { + case FM1_10GEC1: + if (hwconfig_sub("fsl_10gkr_copper", "fm1_10g1")) { + media_type = 1; + fdt_set_phy_handle(blob, prop, pa, + "phy_xfi1"); + sprintf(buf, "%s%s%s", buf, "lane-a,", + (char *)lane_mode); + } + break; + case FM1_10GEC2: + if (hwconfig_sub("fsl_10gkr_copper", "fm1_10g2")) { + media_type = 1; + fdt_set_phy_handle(blob, prop, pa, + "phy_xfi2"); + sprintf(buf, "%s%s%s", buf, "lane-b,", + (char *)lane_mode); + } + break; + case FM2_10GEC1: + if (hwconfig_sub("fsl_10gkr_copper", "fm2_10g1")) { + media_type = 1; + fdt_set_phy_handle(blob, prop, pa, + "phy_xfi3"); + sprintf(buf, "%s%s%s", buf, "lane-d,", + (char *)lane_mode); + } + break; + case FM2_10GEC2: + if (hwconfig_sub("fsl_10gkr_copper", "fm2_10g2")) { + media_type = 1; + fdt_set_phy_handle(blob, prop, pa, + "phy_xfi4"); + sprintf(buf, "%s%s%s", buf, "lane-c,", + (char *)lane_mode); + } + break; + default: + return; + } + + if (!media_type) { + /* fixed-link is used for XFI fiber cable */ + fdt_delprop(blob, offset, "phy-handle"); + f_link.phy_id = port; + f_link.duplex = 1; + f_link.link_speed = 10000; + f_link.pause = 0; + f_link.asym_pause = 0; + fdt_setprop(blob, offset, "fixed-link", &f_link, + sizeof(f_link)); + } else { + /* set property for copper cable */ + off = fdt_node_offset_by_compat_reg(blob, + "fsl,fman-memac-mdio", pa + 0x1000); + fdt_setprop_string(blob, off, "lane-instance", buf); + } } } @@ -295,8 +370,23 @@ void fdt_fixup_board_enet(void *fdt) break; case PHY_INTERFACE_MODE_XGMII: /* check if it's XFI interface for 10g */ - if ((prtcl2 == 56) || (prtcl2 == 57)) { - fdt_status_okay_by_alias(fdt, "emi2_xfislot3"); + if ((prtcl2 == 55) || (prtcl2 == 57)) { + if (i == FM1_10GEC1 && hwconfig_sub( + "fsl_10gkr_copper", "fm1_10g1")) + fdt_status_okay_by_alias( + fdt, "xfi_pcs_mdio1"); + if (i == FM1_10GEC2 && hwconfig_sub( + "fsl_10gkr_copper", "fm1_10g2")) + fdt_status_okay_by_alias( + fdt, "xfi_pcs_mdio2"); + if (i == FM2_10GEC1 && hwconfig_sub( + "fsl_10gkr_copper", "fm2_10g1")) + fdt_status_okay_by_alias( + fdt, "xfi_pcs_mdio3"); + if (i == FM2_10GEC2 && hwconfig_sub( + "fsl_10gkr_copper", "fm2_10g2")) + fdt_status_okay_by_alias( + fdt, "xfi_pcs_mdio4"); break; } switch (i) { @@ -460,7 +550,7 @@ int board_eth_init(bd_t *bis) fm_info_set_phy_address(FM1_DTSEC4, slot_qsgmii_phyaddr[2][3]); fm_info_set_phy_address(FM1_DTSEC5, slot_qsgmii_phyaddr[1][0]); fm_info_set_phy_address(FM1_DTSEC6, slot_qsgmii_phyaddr[1][1]); - if ((srds_prtcl_s2 != 56) && (srds_prtcl_s2 != 57)) { + if ((srds_prtcl_s2 != 55) && (srds_prtcl_s2 != 57)) { fm_info_set_phy_address(FM1_DTSEC9, slot_qsgmii_phyaddr[1][3]); fm_info_set_phy_address(FM1_DTSEC10, @@ -475,7 +565,7 @@ int board_eth_init(bd_t *bis) fm_info_set_phy_address(FM1_DTSEC4, slot_qsgmii_phyaddr[2][3]); fm_info_set_phy_address(FM1_DTSEC5, slot_qsgmii_phyaddr[1][0]); fm_info_set_phy_address(FM1_DTSEC6, slot_qsgmii_phyaddr[1][1]); - if ((srds_prtcl_s2 != 56) && (srds_prtcl_s2 != 57)) { + if ((srds_prtcl_s2 != 55) && (srds_prtcl_s2 != 57)) { fm_info_set_phy_address(FM1_DTSEC9, slot_qsgmii_phyaddr[1][2]); fm_info_set_phy_address(FM1_DTSEC10, @@ -490,7 +580,7 @@ int board_eth_init(bd_t *bis) case 48: fm_info_set_phy_address(FM1_DTSEC5, slot_qsgmii_phyaddr[1][0]); fm_info_set_phy_address(FM1_DTSEC6, slot_qsgmii_phyaddr[1][1]); - if ((srds_prtcl_s2 != 56) && (srds_prtcl_s2 != 57)) { + if ((srds_prtcl_s2 != 55) && (srds_prtcl_s2 != 57)) { fm_info_set_phy_address(FM1_DTSEC10, slot_qsgmii_phyaddr[1][2]); fm_info_set_phy_address(FM1_DTSEC9, @@ -567,13 +657,18 @@ int board_eth_init(bd_t *bis) idx = i - FM1_10GEC1; switch (fm_info_get_enet_if(i)) { case PHY_INTERFACE_MODE_XGMII: - lane = serdes_get_first_lane(FSL_SRDS_1, + if ((srds_prtcl_s2 == 55) || (srds_prtcl_s2 == 57)) { + /* A fake PHY address to make U-boot happy */ + fm_info_set_phy_address(i, i); + } else { + lane = serdes_get_first_lane(FSL_SRDS_1, XAUI_FM1_MAC9 + idx); - if (lane < 0) - break; - slot = lane_to_slot_fsm1[lane]; - if (QIXIS_READ(present2) & (1 << (slot - 1))) - fm_disable_port(i); + if (lane < 0) + break; + slot = lane_to_slot_fsm1[lane]; + if (QIXIS_READ(present2) & (1 << (slot - 1))) + fm_disable_port(i); + } mdio_mux[i] = EMI2; fm_info_set_mdio(i, mii_dev_for_muxval(mdio_mux[i])); break; @@ -666,7 +761,7 @@ int board_eth_init(bd_t *bis) fm_info_set_phy_address(FM2_DTSEC3, slot_qsgmii_phyaddr[4][2]); fm_info_set_phy_address(FM2_DTSEC4, slot_qsgmii_phyaddr[4][3]); break; - case 56: + case 55: case 57: /* XFI in Slot3, SGMII in Slot4 */ fm_info_set_phy_address(FM2_DTSEC1, slot_qsgmii_phyaddr[4][0]); @@ -743,13 +838,18 @@ int board_eth_init(bd_t *bis) idx = i - FM2_10GEC1; switch (fm_info_get_enet_if(i)) { case PHY_INTERFACE_MODE_XGMII: - lane = serdes_get_first_lane(FSL_SRDS_2, + if ((srds_prtcl_s2 == 55) || (srds_prtcl_s2 == 57)) { + /* A fake PHY address to make U-boot happy */ + fm_info_set_phy_address(i, i); + } else { + lane = serdes_get_first_lane(FSL_SRDS_2, XAUI_FM2_MAC9 + idx); - if (lane < 0) - break; - slot = lane_to_slot_fsm2[lane]; - if (QIXIS_READ(present2) & (1 << (slot - 1))) - fm_disable_port(i); + if (lane < 0) + break; + slot = lane_to_slot_fsm2[lane]; + if (QIXIS_READ(present2) & (1 << (slot - 1))) + fm_disable_port(i); + } mdio_mux[i] = EMI2; fm_info_set_mdio(i, mii_dev_for_muxval(mdio_mux[i])); break; diff --git a/board/gateworks/gw_ventana/eeprom.c b/board/gateworks/gw_ventana/eeprom.c index e90186e..3edc915 100644 --- a/board/gateworks/gw_ventana/eeprom.c +++ b/board/gateworks/gw_ventana/eeprom.c @@ -6,7 +6,10 @@ */ #include <common.h> +#include <errno.h> #include <i2c.h> +#include <malloc.h> +#include <asm/bitops.h> #include "gsc.h" #include "ventana_eeprom.h" @@ -38,14 +41,12 @@ read_eeprom(int bus, struct ventana_board_info *info) /* read eeprom config section */ if (gsc_i2c_read(GSC_EEPROM_ADDR, 0x00, 1, buf, sizeof(*info))) { puts("EEPROM: Failed to read EEPROM\n"); - info->model[0] = 0; return GW_UNKNOWN; } /* sanity checks */ if (info->model[0] != 'G' || info->model[1] != 'W') { puts("EEPROM: Invalid Model in EEPROM\n"); - info->model[0] = 0; return GW_UNKNOWN; } @@ -55,7 +56,6 @@ read_eeprom(int bus, struct ventana_board_info *info) if ((info->chksum[0] != chksum>>8) || (info->chksum[1] != (chksum&0xff))) { puts("EEPROM: Failed EEPROM checksum\n"); - info->model[0] = 0; return GW_UNKNOWN; } @@ -87,3 +87,165 @@ read_eeprom(int bus, struct ventana_board_info *info) } return type; } + +/* list of config bits that the bootloader will remove from dtb if not set */ +struct ventana_eeprom_config econfig[] = { + { "eth0", "ethernet0", EECONFIG_ETH0 }, + { "eth1", "ethernet1", EECONFIG_ETH1 }, + { "sata", "ahci0", EECONFIG_SATA }, + { "pcie", NULL, EECONFIG_PCIE}, + { "lvds0", NULL, EECONFIG_LVDS0 }, + { "lvds1", NULL, EECONFIG_LVDS1 }, + { "usb0", NULL, EECONFIG_USB0 }, + { "usb1", NULL, EECONFIG_USB1 }, + { "mmc0", NULL, EECONFIG_SD0 }, + { "mmc1", NULL, EECONFIG_SD1 }, + { "mmc2", NULL, EECONFIG_SD2 }, + { "mmc3", NULL, EECONFIG_SD3 }, + { "uart0", NULL, EECONFIG_UART0 }, + { "uart1", NULL, EECONFIG_UART1 }, + { "uart2", NULL, EECONFIG_UART2 }, + { "uart3", NULL, EECONFIG_UART3 }, + { "uart4", NULL, EECONFIG_UART4 }, + { "ipu0", NULL, EECONFIG_IPU0 }, + { "ipu1", NULL, EECONFIG_IPU1 }, + { "can0", NULL, EECONFIG_FLEXCAN }, + { "i2c0", NULL, EECONFIG_I2C0 }, + { "i2c1", NULL, EECONFIG_I2C1 }, + { "i2c2", NULL, EECONFIG_I2C2 }, + { "vpu", NULL, EECONFIG_VPU }, + { "csi0", NULL, EECONFIG_CSI0 }, + { "csi1", NULL, EECONFIG_CSI1 }, + { "spi0", NULL, EECONFIG_ESPCI0 }, + { "spi1", NULL, EECONFIG_ESPCI1 }, + { "spi2", NULL, EECONFIG_ESPCI2 }, + { "spi3", NULL, EECONFIG_ESPCI3 }, + { "spi4", NULL, EECONFIG_ESPCI4 }, + { "spi5", NULL, EECONFIG_ESPCI5 }, + { "gps", "pps", EECONFIG_GPS }, + { "hdmi_in", NULL, EECONFIG_HDMI_IN }, + { "hdmi_out", NULL, EECONFIG_HDMI_OUT }, + { "cvbs_in", NULL, EECONFIG_VID_IN }, + { "cvbs_out", NULL, EECONFIG_VID_OUT }, + { "nand", NULL, EECONFIG_NAND }, + { /* Sentinel */ } +}; + +#ifdef CONFIG_CMD_EECONFIG +static struct ventana_eeprom_config *get_config(const char *name) +{ + struct ventana_eeprom_config *cfg = econfig; + + while (cfg->name) { + if (0 == strcmp(name, cfg->name)) + return cfg; + cfg++; + } + return NULL; +} + +static u8 econfig_bytes[sizeof(ventana_info.config)]; +static int econfig_init = -1; + +int do_econfig(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + struct ventana_eeprom_config *cfg; + struct ventana_board_info *info = &ventana_info; + int i; + + if (argc < 2) + return CMD_RET_USAGE; + + /* initialize */ + if (econfig_init != 1) { + memcpy(econfig_bytes, info->config, sizeof(econfig_bytes)); + econfig_init = 1; + } + + /* list configs */ + if ((strncmp(argv[1], "list", 4) == 0)) { + cfg = econfig; + while (cfg->name) { + printf("%s: %d\n", cfg->name, + test_bit(cfg->bit, econfig_bytes) ? 1 : 0); + cfg++; + } + } + + /* save */ + else if ((strncmp(argv[1], "save", 4) == 0)) { + unsigned char *buf = (unsigned char *)info; + int chksum; + + /* calculate new checksum */ + memcpy(info->config, econfig_bytes, sizeof(econfig_bytes)); + for (chksum = 0, i = 0; i < sizeof(*info)-2; i++) + chksum += buf[i]; + debug("old chksum:0x%04x\n", + (info->chksum[0] << 8) | info->chksum[1]); + debug("new chksum:0x%04x\n", chksum); + info->chksum[0] = chksum >> 8; + info->chksum[1] = chksum & 0xff; + + /* write new config data */ + if (gsc_i2c_write(GSC_EEPROM_ADDR, info->config - (u8 *)info, + 1, econfig_bytes, sizeof(econfig_bytes))) { + printf("EEPROM: Failed updating config\n"); + return CMD_RET_FAILURE; + } + + /* write new config data */ + if (gsc_i2c_write(GSC_EEPROM_ADDR, info->chksum - (u8 *)info, + 1, info->chksum, 2)) { + printf("EEPROM: Failed updating checksum\n"); + return CMD_RET_FAILURE; + } + + printf("Config saved to EEPROM\n"); + } + + /* get config */ + else if (argc == 2) { + cfg = get_config(argv[1]); + if (cfg) { + printf("%s: %d\n", cfg->name, + test_bit(cfg->bit, econfig_bytes) ? 1 : 0); + } else { + printf("invalid config: %s\n", argv[1]); + return CMD_RET_FAILURE; + } + } + + /* set config */ + else if (argc == 3) { + cfg = get_config(argv[1]); + if (cfg) { + if (simple_strtol(argv[2], NULL, 10)) { + test_and_set_bit(cfg->bit, econfig_bytes); + printf("Enabled %s\n", cfg->name); + } else { + test_and_clear_bit(cfg->bit, econfig_bytes); + printf("Disabled %s\n", cfg->name); + } + } else { + printf("invalid config: %s\n", argv[1]); + return CMD_RET_FAILURE; + } + } + + else + return CMD_RET_USAGE; + + return CMD_RET_SUCCESS; +} + +U_BOOT_CMD( + econfig, 3, 0, do_econfig, + "EEPROM configuration", + "list - list config\n" + "save - save config to EEPROM\n" + "<name> - get config 'name'\n" + "<name> [0|1] - set config 'name' to value\n" +); + +#endif /* CONFIG_CMD_EECONFIG */ diff --git a/board/gateworks/gw_ventana/gsc.c b/board/gateworks/gw_ventana/gsc.c index 37966ab..1cf38d4 100644 --- a/board/gateworks/gw_ventana/gsc.c +++ b/board/gateworks/gw_ventana/gsc.c @@ -57,7 +57,7 @@ int gsc_i2c_write(uchar chip, uint addr, int alen, uchar *buf, int len) break; mdelay(10); } - mdelay(1); + mdelay(100); return ret; } diff --git a/board/gateworks/gw_ventana/gw_ventana.c b/board/gateworks/gw_ventana/gw_ventana.c index 9d2651f..a222921 100644 --- a/board/gateworks/gw_ventana/gw_ventana.c +++ b/board/gateworks/gw_ventana/gw_ventana.c @@ -50,10 +50,6 @@ DECLARE_GLOBAL_DATA_PTR; #define GP_RS232_EN IMX_GPIO_NR(2, 11) #define GP_MSATA_SEL IMX_GPIO_NR(2, 8) -/* I2C bus numbers */ -#define I2C_GSC 0 -#define I2C_PMIC 1 - #define UART_PAD_CTRL (PAD_CTL_PKE | PAD_CTL_PUE | \ PAD_CTL_PUS_100K_UP | PAD_CTL_SPEED_MED | \ PAD_CTL_DSE_40ohm | PAD_CTL_SRE_FAST | PAD_CTL_HYS) @@ -78,11 +74,18 @@ DECLARE_GLOBAL_DATA_PTR; PAD_CTL_SPEED_MED | PAD_CTL_DSE_40ohm | PAD_CTL_HYS | \ PAD_CTL_ODE | PAD_CTL_SRE_FAST) +#define IRQ_PAD_CTRL (PAD_CTL_PKE | PAD_CTL_PUE | \ + PAD_CTL_PUS_100K_UP | PAD_CTL_SPEED_MED | \ + PAD_CTL_DSE_34ohm | PAD_CTL_HYS | PAD_CTL_SRE_FAST) + +#define DIO_PAD_CFG (MUX_PAD_CTRL(DIO_PAD_CTRL) | MUX_MODE_SION) + + /* * EEPROM board info struct populated by read_eeprom so that we only have to * read it once. */ -static struct ventana_board_info ventana_info; +struct ventana_board_info ventana_info; int board_type; @@ -187,7 +190,7 @@ iomux_v3_cfg_t const usdhc3_pads[] = { IOMUX_PADS(PAD_SD3_DAT2__SD3_DATA2 | MUX_PAD_CTRL(USDHC_PAD_CTRL)), IOMUX_PADS(PAD_SD3_DAT3__SD3_DATA3 | MUX_PAD_CTRL(USDHC_PAD_CTRL)), /* CD */ - IOMUX_PADS(PAD_SD3_DAT5__GPIO7_IO00 | MUX_PAD_CTRL(NO_PAD_CTRL)), + IOMUX_PADS(PAD_SD3_DAT5__GPIO7_IO00 | MUX_PAD_CTRL(IRQ_PAD_CTRL)), }; /* ENET */ @@ -211,7 +214,7 @@ iomux_v3_cfg_t const enet_pads[] = { IOMUX_PADS(PAD_RGMII_RX_CTL__RGMII_RX_CTL | MUX_PAD_CTRL(ENET_PAD_CTRL)), /* PHY nRST */ - IOMUX_PADS(PAD_ENET_TXD0__GPIO1_IO30 | MUX_PAD_CTRL(NO_PAD_CTRL)), + IOMUX_PADS(PAD_ENET_TXD0__GPIO1_IO30 | DIO_PAD_CFG), }; /* NAND */ @@ -281,10 +284,10 @@ static void setup_iomux_uart(void) #ifdef CONFIG_USB_EHCI_MX6 iomux_v3_cfg_t const usb_pads[] = { - IOMUX_PADS(PAD_GPIO_1__USB_OTG_ID | MUX_PAD_CTRL(DIO_PAD_CTRL)), - IOMUX_PADS(PAD_KEY_COL4__USB_OTG_OC | MUX_PAD_CTRL(DIO_PAD_CTRL)), + IOMUX_PADS(PAD_GPIO_1__USB_OTG_ID | DIO_PAD_CFG), + IOMUX_PADS(PAD_KEY_COL4__USB_OTG_OC | DIO_PAD_CFG), /* OTG PWR */ - IOMUX_PADS(PAD_EIM_D22__GPIO3_IO22 | MUX_PAD_CTRL(DIO_PAD_CTRL)), + IOMUX_PADS(PAD_EIM_D22__GPIO3_IO22 | DIO_PAD_CFG), }; int board_ehci_hcd_init(int port) @@ -296,15 +299,13 @@ int board_ehci_hcd_init(int port) /* Reset USB HUB (present on GW54xx/GW53xx) */ switch (info->model[3]) { case '3': /* GW53xx */ - SETUP_IOMUX_PAD(PAD_GPIO_9__GPIO1_IO09 | - MUX_PAD_CTRL(NO_PAD_CTRL)); + SETUP_IOMUX_PAD(PAD_GPIO_9__GPIO1_IO09 | DIO_PAD_CFG); gpio_direction_output(IMX_GPIO_NR(1, 9), 0); mdelay(2); gpio_set_value(IMX_GPIO_NR(1, 9), 1); break; case '4': /* GW54xx */ - SETUP_IOMUX_PAD(PAD_SD1_DAT0__GPIO1_IO16 | - MUX_PAD_CTRL(NO_PAD_CTRL)); + SETUP_IOMUX_PAD(PAD_SD1_DAT0__GPIO1_IO16 | DIO_PAD_CFG); gpio_direction_output(IMX_GPIO_NR(1, 16), 0); mdelay(2); gpio_set_value(IMX_GPIO_NR(1, 16), 1); @@ -426,7 +427,7 @@ static void enable_lvds(struct display_info_t const *dev) writel(reg, &iomux->gpr[2]); /* Enable Backlight */ - SETUP_IOMUX_PAD(PAD_SD1_CMD__GPIO1_IO18 | MUX_PAD_CTRL(NO_PAD_CTRL)); + SETUP_IOMUX_PAD(PAD_SD1_CMD__GPIO1_IO18 | DIO_PAD_CFG); gpio_direction_output(IMX_GPIO_NR(1, 18), 1); } @@ -523,7 +524,7 @@ static void setup_display(void) writel(reg, &iomux->gpr[3]); /* Backlight CABEN on LVDS connector */ - SETUP_IOMUX_PAD(PAD_SD2_CLK__GPIO1_IO10 | MUX_PAD_CTRL(NO_PAD_CTRL)); + SETUP_IOMUX_PAD(PAD_SD2_CLK__GPIO1_IO10 | DIO_PAD_CFG); gpio_direction_output(IMX_GPIO_NR(1, 10), 0); } #endif /* CONFIG_VIDEO_IPUV3 */ @@ -535,118 +536,128 @@ static void setup_display(void) /* common to add baseboards */ static iomux_v3_cfg_t const gw_gpio_pads[] = { /* MSATA_EN */ - IOMUX_PADS(PAD_SD4_DAT0__GPIO2_IO08 | MUX_PAD_CTRL(NO_PAD_CTRL)), + IOMUX_PADS(PAD_SD4_DAT0__GPIO2_IO08 | DIO_PAD_CFG), /* RS232_EN# */ - IOMUX_PADS(PAD_SD4_DAT3__GPIO2_IO11 | MUX_PAD_CTRL(NO_PAD_CTRL)), + IOMUX_PADS(PAD_SD4_DAT3__GPIO2_IO11 | DIO_PAD_CFG), }; /* prototype */ static iomux_v3_cfg_t const gwproto_gpio_pads[] = { /* PANLEDG# */ - IOMUX_PADS(PAD_KEY_COL0__GPIO4_IO06 | MUX_PAD_CTRL(NO_PAD_CTRL)), + IOMUX_PADS(PAD_KEY_COL0__GPIO4_IO06 | DIO_PAD_CFG), /* PANLEDR# */ - IOMUX_PADS(PAD_KEY_ROW0__GPIO4_IO07 | MUX_PAD_CTRL(NO_PAD_CTRL)), + IOMUX_PADS(PAD_KEY_ROW0__GPIO4_IO07 | DIO_PAD_CFG), /* LOCLED# */ - IOMUX_PADS(PAD_KEY_ROW4__GPIO4_IO15 | MUX_PAD_CTRL(NO_PAD_CTRL)), + IOMUX_PADS(PAD_KEY_ROW4__GPIO4_IO15 | DIO_PAD_CFG), /* RS485_EN */ - IOMUX_PADS(PAD_SD3_DAT4__GPIO7_IO01 | MUX_PAD_CTRL(NO_PAD_CTRL)), + IOMUX_PADS(PAD_SD3_DAT4__GPIO7_IO01 | DIO_PAD_CFG), /* IOEXP_PWREN# */ - IOMUX_PADS(PAD_EIM_A19__GPIO2_IO19 | MUX_PAD_CTRL(NO_PAD_CTRL)), + IOMUX_PADS(PAD_EIM_A19__GPIO2_IO19 | DIO_PAD_CFG), /* IOEXP_IRQ# */ - IOMUX_PADS(PAD_EIM_A20__GPIO2_IO18 | MUX_PAD_CTRL(NO_PAD_CTRL)), + IOMUX_PADS(PAD_EIM_A20__GPIO2_IO18 | MUX_PAD_CTRL(IRQ_PAD_CTRL)), /* VID_EN */ - IOMUX_PADS(PAD_EIM_D31__GPIO3_IO31 | MUX_PAD_CTRL(NO_PAD_CTRL)), + IOMUX_PADS(PAD_EIM_D31__GPIO3_IO31 | DIO_PAD_CFG), /* DIOI2C_DIS# */ - IOMUX_PADS(PAD_GPIO_19__GPIO4_IO05 | MUX_PAD_CTRL(NO_PAD_CTRL)), + IOMUX_PADS(PAD_GPIO_19__GPIO4_IO05 | DIO_PAD_CFG), /* PCICK_SSON */ - IOMUX_PADS(PAD_SD1_CLK__GPIO1_IO20 | MUX_PAD_CTRL(NO_PAD_CTRL)), + IOMUX_PADS(PAD_SD1_CLK__GPIO1_IO20 | DIO_PAD_CFG), /* PCI_RST# */ - IOMUX_PADS(PAD_ENET_TXD1__GPIO1_IO29 | MUX_PAD_CTRL(NO_PAD_CTRL)), + IOMUX_PADS(PAD_ENET_TXD1__GPIO1_IO29 | DIO_PAD_CFG), }; static iomux_v3_cfg_t const gw51xx_gpio_pads[] = { /* PANLEDG# */ - IOMUX_PADS(PAD_KEY_COL0__GPIO4_IO06 | MUX_PAD_CTRL(NO_PAD_CTRL)), + IOMUX_PADS(PAD_KEY_COL0__GPIO4_IO06 | DIO_PAD_CFG), /* PANLEDR# */ - IOMUX_PADS(PAD_KEY_ROW0__GPIO4_IO07 | MUX_PAD_CTRL(NO_PAD_CTRL)), + IOMUX_PADS(PAD_KEY_ROW0__GPIO4_IO07 | DIO_PAD_CFG), /* IOEXP_PWREN# */ - IOMUX_PADS(PAD_EIM_A19__GPIO2_IO19 | MUX_PAD_CTRL(NO_PAD_CTRL)), + IOMUX_PADS(PAD_EIM_A19__GPIO2_IO19 | DIO_PAD_CFG), /* IOEXP_IRQ# */ - IOMUX_PADS(PAD_EIM_A20__GPIO2_IO18 | MUX_PAD_CTRL(NO_PAD_CTRL)), + IOMUX_PADS(PAD_EIM_A20__GPIO2_IO18 | MUX_PAD_CTRL(IRQ_PAD_CTRL)), /* GPS_SHDN */ - IOMUX_PADS(PAD_GPIO_2__GPIO1_IO02 | MUX_PAD_CTRL(NO_PAD_CTRL)), + IOMUX_PADS(PAD_GPIO_2__GPIO1_IO02 | DIO_PAD_CFG), /* VID_PWR */ - IOMUX_PADS(PAD_CSI0_DATA_EN__GPIO5_IO20 | MUX_PAD_CTRL(NO_PAD_CTRL)), + IOMUX_PADS(PAD_CSI0_DATA_EN__GPIO5_IO20 | DIO_PAD_CFG), /* PCI_RST# */ - IOMUX_PADS(PAD_GPIO_0__GPIO1_IO00 | MUX_PAD_CTRL(NO_PAD_CTRL)), + IOMUX_PADS(PAD_GPIO_0__GPIO1_IO00 | DIO_PAD_CFG), + /* PCIESKT_WDIS# */ + IOMUX_PADS(PAD_GPIO_17__GPIO7_IO12 | DIO_PAD_CFG), }; static iomux_v3_cfg_t const gw52xx_gpio_pads[] = { /* PANLEDG# */ - IOMUX_PADS(PAD_KEY_COL0__GPIO4_IO06 | MUX_PAD_CTRL(NO_PAD_CTRL)), + IOMUX_PADS(PAD_KEY_COL0__GPIO4_IO06 | DIO_PAD_CFG), /* PANLEDR# */ - IOMUX_PADS(PAD_KEY_ROW0__GPIO4_IO07 | MUX_PAD_CTRL(NO_PAD_CTRL)), + IOMUX_PADS(PAD_KEY_ROW0__GPIO4_IO07 | DIO_PAD_CFG), /* IOEXP_PWREN# */ - IOMUX_PADS(PAD_EIM_A19__GPIO2_IO19 | MUX_PAD_CTRL(NO_PAD_CTRL)), + IOMUX_PADS(PAD_EIM_A19__GPIO2_IO19 | DIO_PAD_CFG), /* IOEXP_IRQ# */ - IOMUX_PADS(PAD_EIM_A20__GPIO2_IO18 | MUX_PAD_CTRL(NO_PAD_CTRL)), + IOMUX_PADS(PAD_EIM_A20__GPIO2_IO18 | MUX_PAD_CTRL(IRQ_PAD_CTRL)), /* MX6_LOCLED# */ - IOMUX_PADS(PAD_KEY_ROW4__GPIO4_IO15 | MUX_PAD_CTRL(NO_PAD_CTRL)), + IOMUX_PADS(PAD_KEY_ROW4__GPIO4_IO15 | DIO_PAD_CFG), /* GPS_SHDN */ - IOMUX_PADS(PAD_ENET_RXD0__GPIO1_IO27 | MUX_PAD_CTRL(NO_PAD_CTRL)), + IOMUX_PADS(PAD_ENET_RXD0__GPIO1_IO27 | DIO_PAD_CFG), /* USBOTG_SEL */ - IOMUX_PADS(PAD_GPIO_2__GPIO1_IO02 | MUX_PAD_CTRL(NO_PAD_CTRL)), + IOMUX_PADS(PAD_GPIO_2__GPIO1_IO02 | DIO_PAD_CFG), /* VID_PWR */ - IOMUX_PADS(PAD_EIM_D31__GPIO3_IO31 | MUX_PAD_CTRL(NO_PAD_CTRL)), + IOMUX_PADS(PAD_EIM_D31__GPIO3_IO31 | DIO_PAD_CFG), /* PCI_RST# */ - IOMUX_PADS(PAD_ENET_TXD1__GPIO1_IO29 | MUX_PAD_CTRL(NO_PAD_CTRL)), + IOMUX_PADS(PAD_ENET_TXD1__GPIO1_IO29 | DIO_PAD_CFG), + /* PCIESKT_WDIS# */ + IOMUX_PADS(PAD_GPIO_17__GPIO7_IO12 | DIO_PAD_CFG), }; static iomux_v3_cfg_t const gw53xx_gpio_pads[] = { /* PANLEDG# */ - IOMUX_PADS(PAD_KEY_COL0__GPIO4_IO06 | MUX_PAD_CTRL(NO_PAD_CTRL)), + IOMUX_PADS(PAD_KEY_COL0__GPIO4_IO06 | DIO_PAD_CFG), /* PANLEDR# */ - IOMUX_PADS(PAD_KEY_ROW0__GPIO4_IO07 | MUX_PAD_CTRL(NO_PAD_CTRL)), + IOMUX_PADS(PAD_KEY_ROW0__GPIO4_IO07 | DIO_PAD_CFG), /* IOEXP_PWREN# */ - IOMUX_PADS(PAD_EIM_A19__GPIO2_IO19 | MUX_PAD_CTRL(NO_PAD_CTRL)), + IOMUX_PADS(PAD_EIM_A19__GPIO2_IO19 | DIO_PAD_CFG), /* IOEXP_IRQ# */ - IOMUX_PADS(PAD_EIM_A20__GPIO2_IO18 | MUX_PAD_CTRL(NO_PAD_CTRL)), + IOMUX_PADS(PAD_EIM_A20__GPIO2_IO18 | MUX_PAD_CTRL(IRQ_PAD_CTRL)), + /* DIOI2C_DIS# */ + IOMUX_PADS(PAD_GPIO_19__GPIO4_IO05 | DIO_PAD_CFG), /* MX6_LOCLED# */ - IOMUX_PADS(PAD_KEY_ROW4__GPIO4_IO15 | MUX_PAD_CTRL(NO_PAD_CTRL)), + IOMUX_PADS(PAD_KEY_ROW4__GPIO4_IO15 | DIO_PAD_CFG), /* GPS_SHDN */ - IOMUX_PADS(PAD_ENET_RXD0__GPIO1_IO27 | MUX_PAD_CTRL(NO_PAD_CTRL)), + IOMUX_PADS(PAD_ENET_RXD0__GPIO1_IO27 | DIO_PAD_CFG), /* VID_EN */ - IOMUX_PADS(PAD_EIM_D31__GPIO3_IO31 | MUX_PAD_CTRL(NO_PAD_CTRL)), + IOMUX_PADS(PAD_EIM_D31__GPIO3_IO31 | DIO_PAD_CFG), /* PCI_RST# */ - IOMUX_PADS(PAD_ENET_TXD1__GPIO1_IO29 | MUX_PAD_CTRL(NO_PAD_CTRL)), + IOMUX_PADS(PAD_ENET_TXD1__GPIO1_IO29 | DIO_PAD_CFG), + /* PCIESKT_WDIS# */ + IOMUX_PADS(PAD_GPIO_17__GPIO7_IO12 | DIO_PAD_CFG), }; static iomux_v3_cfg_t const gw54xx_gpio_pads[] = { /* PANLEDG# */ - IOMUX_PADS(PAD_KEY_COL0__GPIO4_IO06 | MUX_PAD_CTRL(NO_PAD_CTRL)), + IOMUX_PADS(PAD_KEY_COL0__GPIO4_IO06 | DIO_PAD_CFG), /* PANLEDR# */ - IOMUX_PADS(PAD_KEY_COL2__GPIO4_IO10 | MUX_PAD_CTRL(NO_PAD_CTRL)), + IOMUX_PADS(PAD_KEY_COL2__GPIO4_IO10 | DIO_PAD_CFG), /* MX6_LOCLED# */ - IOMUX_PADS(PAD_KEY_ROW4__GPIO4_IO15 | MUX_PAD_CTRL(NO_PAD_CTRL)), + IOMUX_PADS(PAD_KEY_ROW4__GPIO4_IO15 | DIO_PAD_CFG), /* MIPI_DIO */ - IOMUX_PADS(PAD_SD1_DAT3__GPIO1_IO21 | MUX_PAD_CTRL(NO_PAD_CTRL)), + IOMUX_PADS(PAD_SD1_DAT3__GPIO1_IO21 | DIO_PAD_CFG), /* RS485_EN */ - IOMUX_PADS(PAD_EIM_D24__GPIO3_IO24 | MUX_PAD_CTRL(NO_PAD_CTRL)), + IOMUX_PADS(PAD_EIM_D24__GPIO3_IO24 | DIO_PAD_CFG), /* IOEXP_PWREN# */ - IOMUX_PADS(PAD_KEY_ROW0__GPIO4_IO07 | MUX_PAD_CTRL(NO_PAD_CTRL)), + IOMUX_PADS(PAD_KEY_ROW0__GPIO4_IO07 | DIO_PAD_CFG), /* IOEXP_IRQ# */ - IOMUX_PADS(PAD_KEY_ROW1__GPIO4_IO09 | MUX_PAD_CTRL(NO_PAD_CTRL)), - /* DIOI2C_DIS# */ - IOMUX_PADS(PAD_GPIO_19__GPIO4_IO05 | MUX_PAD_CTRL(NO_PAD_CTRL)), + IOMUX_PADS(PAD_KEY_ROW1__GPIO4_IO09 | MUX_PAD_CTRL(IRQ_PAD_CTRL)), /* DIOI2C_DIS# */ - IOMUX_PADS(PAD_GPIO_19__GPIO4_IO05 | MUX_PAD_CTRL(NO_PAD_CTRL)), + IOMUX_PADS(PAD_GPIO_19__GPIO4_IO05 | DIO_PAD_CFG), /* PCICK_SSON */ - IOMUX_PADS(PAD_SD1_CLK__GPIO1_IO20 | MUX_PAD_CTRL(NO_PAD_CTRL)), + IOMUX_PADS(PAD_SD1_CLK__GPIO1_IO20 | DIO_PAD_CFG), /* PCI_RST# */ - IOMUX_PADS(PAD_ENET_TXD1__GPIO1_IO29 | MUX_PAD_CTRL(NO_PAD_CTRL)), + IOMUX_PADS(PAD_ENET_TXD1__GPIO1_IO29 | DIO_PAD_CFG), + /* VID_EN */ + IOMUX_PADS(PAD_EIM_D31__GPIO3_IO31 | DIO_PAD_CFG), + /* PCIESKT_WDIS# */ + IOMUX_PADS(PAD_DISP0_DAT23__GPIO5_IO17 | DIO_PAD_CFG), }; /* @@ -677,6 +688,7 @@ struct ventana { int dioi2c_en; int pcie_sson; int usb_sel; + int wdis; }; struct ventana gpio_cfg[] = { @@ -762,6 +774,7 @@ struct ventana gpio_cfg[] = { .mezz_irq = IMX_GPIO_NR(2, 18), .gps_shdn = IMX_GPIO_NR(1, 2), .vidin_en = IMX_GPIO_NR(5, 20), + .wdis = IMX_GPIO_NR(7, 12), }, /* GW52xx */ @@ -805,6 +818,7 @@ struct ventana gpio_cfg[] = { .gps_shdn = IMX_GPIO_NR(1, 27), .vidin_en = IMX_GPIO_NR(3, 31), .usb_sel = IMX_GPIO_NR(1, 2), + .wdis = IMX_GPIO_NR(7, 12), }, /* GW53xx */ @@ -847,6 +861,7 @@ struct ventana gpio_cfg[] = { .mezz_irq = IMX_GPIO_NR(2, 18), .gps_shdn = IMX_GPIO_NR(1, 27), .vidin_en = IMX_GPIO_NR(3, 31), + .wdis = IMX_GPIO_NR(7, 12), }, /* GW54xx */ @@ -891,6 +906,7 @@ struct ventana gpio_cfg[] = { .vidin_en = IMX_GPIO_NR(3, 31), .dioi2c_en = IMX_GPIO_NR(4, 5), .pcie_sson = IMX_GPIO_NR(1, 20), + .wdis = IMX_GPIO_NR(5, 17), }, }; @@ -902,8 +918,8 @@ int power_init_board(void) /* configure PFUZE100 PMIC */ if (board_type == GW54xx || board_type == GW54proto) { - power_pfuze100_init(I2C_PMIC); - p = pmic_get("PFUZE100_PMIC"); + power_pfuze100_init(CONFIG_I2C_PMIC); + p = pmic_get("PFUZE100"); if (p && !pmic_probe(p)) { pmic_reg_read(p, PFUZE100_DEVICEID, ®); printf("PMIC: PFUZE100 ID=0x%02x\n", reg); @@ -924,7 +940,7 @@ int power_init_board(void) /* configure LTC3676 PMIC */ else { - power_ltc3676_init(I2C_PMIC); + power_ltc3676_init(CONFIG_I2C_PMIC); p = pmic_get("LTC3676_PMIC"); if (p && !pmic_probe(p)) { puts("PMIC: LTC3676\n"); @@ -975,12 +991,10 @@ static void setup_board_gpio(int board) gpio_direction_output(GP_MSATA_SEL, 0); } - /* - * assert PCI_RST# (released by OS when clock is valid) - * TODO: figure out why leaving this de-asserted from PCI scan on boot - * causes linux pcie driver to hang during enumeration - */ +#if !defined(CONFIG_CMD_PCI) + /* assert PCI_RST# (released by OS when clock is valid) */ gpio_direction_output(gpio_cfg[board].pcie_rst, 0); +#endif /* turn off (active-high) user LED's */ for (i = 0; i < 4; i++) { @@ -1016,21 +1030,27 @@ static void setup_board_gpio(int board) if (gpio_cfg[board].usb_sel) gpio_direction_output(gpio_cfg[board].usb_sel, 0); + /* PCISKT_WDIS# (Wireless disable GPIO to miniPCIe sockets) */ + if (gpio_cfg[board].wdis) + gpio_direction_output(gpio_cfg[board].wdis, 1); + /* * Configure DIO pinmux/padctl registers * see IMX6DQRM/IMX6SDLRM IOMUXC_SW_PAD_CTL_PAD_* register definitions */ for (i = 0; i < 4; i++) { struct dio_cfg *cfg = &gpio_cfg[board].dio_cfg[i]; - unsigned ctrl = DIO_PAD_CTRL; + iomux_v3_cfg_t ctrl = DIO_PAD_CFG; unsigned cputype = is_cpu_type(MXC_CPU_MX6Q) ? 0 : 1; sprintf(arg, "dio%d", i); if (!hwconfig(arg)) continue; s = hwconfig_subarg(arg, "padctrl", &len); - if (s) - ctrl = simple_strtoul(s, NULL, 16) & 0x3ffff; + if (s) { + ctrl = MUX_PAD_CTRL(simple_strtoul(s, NULL, 16) + & 0x1ffff) | MUX_MODE_SION; + } if (hwconfig_subarg_cmp(arg, "mode", "gpio")) { if (!quiet) { printf("DIO%d: GPIO%d_IO%02d (gpio-%d)\n", i, @@ -1039,7 +1059,7 @@ static void setup_board_gpio(int board) cfg->gpio_param); } imx_iomux_v3_setup_pad(cfg->gpio_padmux[cputype] | - MUX_PAD_CTRL(ctrl)); + ctrl); gpio_direction_input(cfg->gpio_param); } else if (hwconfig_subarg_cmp("dio2", "mode", "pwm") && cfg->pwm_padmux) { @@ -1122,8 +1142,7 @@ int dram_init(void) int board_init(void) { - struct iomuxc_base_regs *const iomuxc_regs - = (struct iomuxc_base_regs *)IOMUXC_BASE_ADDR; + struct iomuxc *const iomuxc_regs = (struct iomuxc *)IOMUXC_BASE_ADDR; clrsetbits_le32(&iomuxc_regs->gpr[1], IOMUXC_GPR1_OTG_ID_MASK, @@ -1152,7 +1171,7 @@ int board_init(void) setup_sata(); #endif /* read Gateworks EEPROM into global struct (used later) */ - board_type = read_eeprom(I2C_GSC, &ventana_info); + board_type = read_eeprom(CONFIG_I2C_GSC, &ventana_info); /* board-specifc GPIO iomux */ SETUP_IOMUX_PADS(gw_gpio_pads); @@ -1200,7 +1219,7 @@ int checkboard(void) return 0; /* Display GSC firmware revision/CRC/status */ - i2c_set_bus_num(I2C_GSC); + i2c_set_bus_num(CONFIG_I2C_GSC); if (!gsc_i2c_read(GSC_SC_ADDR, GSC_SC_FWVER, 1, buf, 1)) { printf("GSC: v%d", buf[0]); if (!gsc_i2c_read(GSC_SC_ADDR, GSC_SC_STATUS, 1, buf, 4)) { @@ -1264,6 +1283,10 @@ int misc_init_r(void) else if (is_cpu_type(MXC_CPU_MX6DL) || is_cpu_type(MXC_CPU_MX6SOLO)) cputype = "imx6dl"; + if (8 << (ventana_info.nand_flash_size-1) >= 2048) + setenv("flash_layout", "large"); + else + setenv("flash_layout", "normal"); memset(str, 0, sizeof(str)); for (i = 0; i < (sizeof(str)-1) && info->model[i]; i++) str[i] = tolower(info->model[i]); @@ -1326,7 +1349,7 @@ int misc_init_r(void) * * Disable the boot watchdog and display/clear the timeout flag if set */ - i2c_set_bus_num(I2C_GSC); + i2c_set_bus_num(CONFIG_I2C_GSC); if (!gsc_i2c_read(GSC_SC_ADDR, GSC_SC_CTRL1, 1, ®, 1)) { reg |= (1 << GSC_SC_CTRL1_WDDIS); if (gsc_i2c_write(GSC_SC_ADDR, GSC_SC_CTRL1, 1, ®, 1)) @@ -1336,7 +1359,7 @@ int misc_init_r(void) } if (!gsc_i2c_read(GSC_SC_ADDR, GSC_SC_STATUS, 1, ®, 1)) { if (reg & (1 << GSC_SC_IRQ_WATCHDOG)) { /* watchdog timeout */ - puts("GSC boot watchdog timeout detected"); + puts("GSC boot watchdog timeout detected\n"); reg &= ~(1 << GSC_SC_IRQ_WATCHDOG); /* clear flag */ gsc_i2c_write(GSC_SC_ADDR, GSC_SC_STATUS, 1, ®, 1); } @@ -1347,74 +1370,6 @@ int misc_init_r(void) #if defined(CONFIG_OF_LIBFDT) && defined(CONFIG_OF_BOARD_SETUP) -/* FDT aliases associated with EEPROM config bits */ -const char *fdt_aliases[] = { - "ethernet0", - "ethernet1", - "hdmi_out", - "ahci0", - "pcie", - "ssi0", - "ssi1", - "lcd0", - "lvds0", - "lvds1", - "usb0", - "usb1", - "mmc0", - "mmc1", - "mmc2", - "mmc3", - "uart0", - "uart1", - "uart2", - "uart3", - "uart4", - "ipu0", - "ipu1", - "can0", - "mipi_dsi", - "mipi_csi", - "tzasc0", - "tzasc1", - "i2c0", - "i2c1", - "i2c2", - "vpu", - "csi0", - "csi1", - "caam", - NULL, - NULL, - NULL, - NULL, - NULL, - "spi0", - "spi1", - "spi2", - "spi3", - "spi4", - "spi5", - NULL, - NULL, - "pps", - NULL, - NULL, - NULL, - "hdmi_in", - "cvbs_out", - "cvbs_in", - "nand", - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, -}; - /* * called prior to booting kernel or by 'fdt boardsetup' command * @@ -1426,8 +1381,8 @@ const char *fdt_aliases[] = { */ void ft_board_setup(void *blob, bd_t *bd) { - int bit; struct ventana_board_info *info = &ventana_info; + struct ventana_eeprom_config *cfg; struct node_info nodes[] = { { "sst,w25q256", MTD_DEV_TYPE_NOR, }, /* SPI flash */ { "fsl,imx6q-gpmi-nand", MTD_DEV_TYPE_NAND, }, /* NAND flash */ @@ -1462,9 +1417,17 @@ void ft_board_setup(void *blob, bd_t *bd) * remove nodes by alias path if EEPROM config tells us the * peripheral is not loaded on the board. */ - for (bit = 0; bit < 64; bit++) { - if (!test_bit(bit, info->config)) - fdt_del_node_and_alias(blob, fdt_aliases[bit]); + if (getenv("fdt_noconfig")) { + puts(" Skiping periperhal config (fdt_noconfig defined)\n"); + return; + } + cfg = econfig; + while (cfg->name) { + if (!test_bit(cfg->bit, info->config)) { + fdt_del_node_and_alias(blob, cfg->dtalias ? + cfg->dtalias : cfg->name); + } + cfg++; } } #endif /* defined(CONFIG_OF_FLAT_TREE) && defined(CONFIG_OF_BOARD_SETUP) */ diff --git a/board/gateworks/gw_ventana/ventana_eeprom.h b/board/gateworks/gw_ventana/ventana_eeprom.h index 5b065be..d64b910 100644 --- a/board/gateworks/gw_ventana/ventana_eeprom.h +++ b/board/gateworks/gw_ventana/ventana_eeprom.h @@ -110,8 +110,19 @@ enum { GW53xx, GW54xx, GW_UNKNOWN, + GW_BADCRC, }; +/* config items */ +struct ventana_eeprom_config { + const char *name; /* name of item */ + const char *dtalias; /* name of dt node to remove if not set */ + int bit; /* bit within config */ +}; + +extern struct ventana_eeprom_config econfig[]; +extern struct ventana_board_info ventana_info; + int read_eeprom(int bus, struct ventana_board_info *); #endif diff --git a/board/gen860t/Kconfig b/board/gen860t/Kconfig deleted file mode 100644 index 438f7cc..0000000 --- a/board/gen860t/Kconfig +++ /dev/null @@ -1,11 +0,0 @@ -if TARGET_GEN860T - -config SYS_BOARD - string - default "gen860t" - -config SYS_CONFIG_NAME - string - default "GEN860T" - -endif diff --git a/board/gen860t/MAINTAINERS b/board/gen860t/MAINTAINERS deleted file mode 100644 index c5d3da3..0000000 --- a/board/gen860t/MAINTAINERS +++ /dev/null @@ -1,7 +0,0 @@ -GEN860T BOARD -M: Keith Outwater <Keith_Outwater@mvis.com> -S: Orphan (since 2014-06) -F: board/gen860t/ -F: include/configs/GEN860T.h -F: configs/GEN860T_defconfig -F: configs/GEN860T_SC_defconfig diff --git a/board/gen860t/Makefile b/board/gen860t/Makefile deleted file mode 100644 index 86ae5e8..0000000 --- a/board/gen860t/Makefile +++ /dev/null @@ -1,8 +0,0 @@ -# -# (C) Copyright 2000-2006 -# Wolfgang Denk, DENX Software Engineering, wd@denx.de. -# -# SPDX-License-Identifier: GPL-2.0+ -# - -obj-y = gen860t.o flash.o beeper.o fpga.o ioport.o diff --git a/board/gen860t/README b/board/gen860t/README deleted file mode 100644 index 3ef8ae1..0000000 --- a/board/gen860t/README +++ /dev/null @@ -1,131 +0,0 @@ -This directory contains board specific code for a generic MPC860T based -embedded computer, called 'GEN860T'. The design is generic in the sense that -common, readily available components are used and that the architecture of the -system is relatively straightforward: - - One eight bit wide boot (FLASH) memory - 32 bit main memory using SDRAM - DOC 2000+ - Ethernet PHY - Some I2C peripheral devices: Atmel AT24C256 EEPROM, Maxim DS1337 RTC. - Some other miscellaneous peripherals - -NOTE: There are references to a XIlinx FPGA and Mil-Std 1553 databus in this -port. I guess the computer is not as generic as I first said 8) However, -these extras can be safely ignored. - -Given the GEN860T files, it should be pretty easy to reverse engineer the -hardware configuration, if that's useful to you. Hopefully, this code will -be useful to someone as a basis for a port to a new system or as a head start -on a custom design. If you end up using any of this, I would appreciate -hearing from you, especially if you discover bugs or find ways to improve the -quality of this U-Boot port. - -Here are the salient features of the system: -Clock : 33.3 Mhz oscillator -Processor core frequency : 66.6 Mhz if in 1:2:1 mode; can also run 1:1 -Bus frequency : 33.3 Mhz - -Main memory: - Type : SDRAM - Width : 32 bits - Size : 64 mibibytes - Chip : Two Micron MT48LC16M16A2TG-7E - CS : MPC860T CS1*/UPMA - UPMA CONNECTIONS: - SDRAM A10 : GPLA0* - SDRAM CAS* : GPLA2* - SDRAM WE* : GPLA3* - SDRAM RAS* : GPLA4* - -Boot memory: - Type : FLASH - Width : 8 bits - Size : 16 mibibytes - Chip : One Intel 28F128J3A (StrataFlash) - CS : MPC860T CS0*/GPCM (this is the "boot" chip select) - -EEPROM memory: - Type : Serial I2C EEPROM - Width : 8 bits - Size : 32 kibibytes - Chip : One Atmel AT25C256 - CS : 0x50 (external I2C address pins on device are tied to GND) - -Filesystem memory: - Type : NAND FLASH (Toshiba) - Width : 8 bits (i.e. interface to DOC is 8 bits) - Size : 32 mibibytes - Chip : One DiskOnCHip Millenium Plus (DOC 2000+) - CS : MPC860T CS2*/GPCM - -Network support: - MAC : MPC86OT FEC (Fast Ethernet Controller) - PHY : Intel LXT971A - MII Addr: 0x0 (hardwired on the board) - MII IRQ : - -Console: - RS-232 on SMC1 (Maxim MAX3232 LVCMOS-RS232 level shifter) - -Real Time Clock: - Type : Low power, I2C interface - Chip : Maxim DS1337 - CS : Address 0x68 on I2C bus - - The MPC860T's internal RTC has a defect in Mask rev D that increases - the current drain on the KAPWR line to 10 mA. Since this is an - unreasonable amount of current draw for a RTC, and Motorola does not - plan to fix this in future mask revisions, a serial (I2C) RTC that - works has been included instead. NOTE that the DS1337 can be - configured to output a 32768 Hz clock while the main power is on. - This clock output has been routed to the MPC860T's EXTAL pin to allow - the internal RTC to be used. NOTE also that due to yet another - defect in the rev D mask, the RTC does not operate reliably when the - internal RTC divisor is set to use a 32768 Hz reference. So just use - the I2C RTC. - -Miscellaneous: - Xilinx Virtex FPGA on CS3*/GPCM. - Virtex FPGA slave SelectMap interface on cs4*/UPMB. - Mil-Std 1553 databus interface on CS5*/GPCM. - Audio sounder (beeper) with digital volume control connected to SPKROUT. - -SC variant: - A reduced-feature version of the GEN860T port is also supported: GEN860T_SC. - The 'SC' variant only provides support for the Virtex FPGA, SDRAM main - memory, EEPROM and flash memory. The system clock frequency is reduced - to 24 MHz. - -Issues: - The DOC 2000+ returns 0x40 as its device ID when probed using the method - desxribed in the DOC datasheet. Unfortunately, the U-Boot DOC driver - does not recognize this device. As of this writing, it seems that MTD - does not support the DOC 2000+ either. - -Status: - Everything appears to work except DOC support. As of this writing, - David Woodhouse has stated on the MTD mailing list that he has no - knowledge of the DOC Millineum Plus and therfore there is no support - in MTD for this device. I wish I had known this sooner :( - -The GEN860T board specific files and configuration is based on the work -of others who have contributed to U-Boot. The copyright and license notices -of these authors have been retained wherever their code has been reused. -All new code to support the GEN860T board is: - - (C) Copyright 2001-2003 - Keith Outwater (keith_outwater@mvis.com) - -and the following license applies: - -SPDX-License-Identifier: GPL-2.0+ - -Thanks to Wolfgang Denk for a great software package and to everyone -who contributed to its development. - -Keith Outwater -Sr. Staff Engineer -Microvision, Inc. -<keith_outwater@mvis.com> -<outwater@eskimo.com> diff --git a/board/gen860t/beeper.c b/board/gen860t/beeper.c deleted file mode 100644 index 0bebca9..0000000 --- a/board/gen860t/beeper.c +++ /dev/null @@ -1,183 +0,0 @@ -/* - * (C) Copyright 2002 - * Keith Outwater, keith_outwater@mvis.com - * - * SPDX-License-Identifier: GPL-2.0+ - */ - -#include <common.h> -#include <mpc8xx.h> -#include <asm/8xx_immap.h> -#include <linux/ctype.h> - -/* - * Basic beeper support for the GEN860T board. The GEN860T includes - * an audio sounder driven by a Phillips TDA8551 amplifier. The - * TDA8551 features a digital volume control which uses a "trinary" - * input (high/high-Z/low) to set volume. The 860's SPKROUT pin - * drives the amplifier input. - */ - -/* - * Initialize beeper-related hardware. Initialize timer 1 for use with - * the beeper. Use 66 MHz internal clock with prescale of 33 to get - * 1 uS period per count. - * FIXME: we should really compute the prescale based on the reported - * core clock frequency. - */ -void init_beeper (void) -{ - volatile immap_t *immap = (immap_t *) CONFIG_SYS_IMMR; - - immap->im_cpmtimer.cpmt_tgcr &= ~TGCR_RST1 | TGCR_STP1; - immap->im_cpmtimer.cpmt_tmr1 = ((33 << TMR_PS_SHIFT) & TMR_PS_MSK) - | TMR_OM | TMR_FRR | TMR_ICLK_IN_GEN; - immap->im_cpmtimer.cpmt_tcn1 = 0; - immap->im_cpmtimer.cpmt_ter1 = 0xffff; - immap->im_cpmtimer.cpmt_tgcr |= TGCR_RST1; -} - -/* - * Set beeper frequency. Max allowed frequency is 2.5 KHz. This limit - * is mostly arbitrary, but the beeper isn't really much good beyond this - * frequency. - */ -void set_beeper_frequency (uint frequency) -{ -#define FREQ_LIMIT 2500 - - volatile immap_t *immap = (immap_t *) CONFIG_SYS_IMMR; - - /* - * Compute timer ticks given desired frequency. The timer is set up - * to count 0.5 uS per tick and it takes two ticks per cycle (Hz). - */ - if (frequency > FREQ_LIMIT) - frequency = FREQ_LIMIT; - frequency = 1000000 / frequency; - immap->im_cpmtimer.cpmt_trr1 = (ushort) frequency; -} - -/* - * Turn the beeper on - */ -void beeper_on (void) -{ - volatile immap_t *immap = (immap_t *) CONFIG_SYS_IMMR; - - immap->im_cpmtimer.cpmt_tgcr &= ~TGCR_STP1; -} - -/* - * Turn the beeper off - */ -void beeper_off (void) -{ - volatile immap_t *immap = (immap_t *) CONFIG_SYS_IMMR; - - immap->im_cpmtimer.cpmt_tgcr |= TGCR_STP1; -} - -/* - * Increase or decrease the beeper volume. Volume can be set - * from off to full in 64 steps. To increase volume, the output - * pin is actively driven high, then returned to tristate. - * To decrease volume, output a low on the port pin (no need to - * change pin mode to tristate) then output a high to go back to - * tristate. - */ -void set_beeper_volume (int steps) -{ - volatile immap_t *immap = (immap_t *) CONFIG_SYS_IMMR; - int i; - - if (steps >= 0) { - for (i = 0; i < (steps >= 64 ? 64 : steps); i++) { - immap->im_cpm.cp_pbodr &= ~(0x80000000 >> 19); - udelay (1); - immap->im_cpm.cp_pbodr |= (0x80000000 >> 19); - udelay (1); - } - } else { - for (i = 0; i > (steps <= -64 ? -64 : steps); i--) { - immap->im_cpm.cp_pbdat &= ~(0x80000000 >> 19); - udelay (1); - immap->im_cpm.cp_pbdat |= (0x80000000 >> 19); - udelay (1); - } - } -} - -/* - * Check the environment to see if the beeper needs beeping. - * Controlled by a sequence of the form: - * freq/delta volume/on time/off time;... where: - * freq = frequency in Hz (0 - 2500) - * delta volume = volume steps up or down (-64 <= vol <= 64) - * on time = time in mS - * off time = time in mS - * - * Return 1 on success, 0 on failure - */ -int do_beeper (char *sequence) -{ -#define DELIMITER ';' - - int args[4]; - int i; - int val; - char *p = sequence; - char *tp; - - /* - * Parse the control sequence. This is a really simple parser - * without any real error checking. You can probably blow it - * up really easily. - */ - if (*p == '\0' || !isdigit (*p)) { - printf ("%s:%d: null or invalid string (%s)\n", - __FILE__, __LINE__, p); - return 0; - } - - i = 0; - while (*p != '\0') { - while (*p != DELIMITER) { - if (i > 3) - i = 0; - val = (int) simple_strtol (p, &tp, 0); - if (tp == p) { - printf ("%s:%d: no digits or bad format\n", - __FILE__, __LINE__); - return 0; - } else { - args[i] = val; - } - - i++; - if (*tp == DELIMITER) - p = tp; - else - p = ++tp; - } - p++; - - /* - * Well, we got something that has a chance of being correct - */ -#if 0 - for (i = 0; i < 4; i++) { - printf ("%s:%d:arg %d = %d\n", __FILE__, __LINE__, i, - args[i]); - } - printf ("\n"); -#endif - set_beeper_frequency (args[0]); - set_beeper_volume (args[1]); - beeper_on (); - udelay (1000 * args[2]); - beeper_off (); - udelay (1000 * args[3]); - } - return 1; -} diff --git a/board/gen860t/beeper.h b/board/gen860t/beeper.h deleted file mode 100644 index 0734fca..0000000 --- a/board/gen860t/beeper.h +++ /dev/null @@ -1,13 +0,0 @@ -/* - * (C) Copyright 2002 - * Keith Outwater, keith_outwater@mvis.com - * - * SPDX-License-Identifier: GPL-2.0+ - */ - -void init_beeper(void); -void set_beeper_frequency(uint frequency); -void beeper_on(void); -void beeper_off(void); -void set_beeper_volume(int steps); -int do_beeper(char *sequence); diff --git a/board/gen860t/flash.c b/board/gen860t/flash.c deleted file mode 100644 index ca1ed3d..0000000 --- a/board/gen860t/flash.c +++ /dev/null @@ -1,628 +0,0 @@ -/* - * (C) Copyright 2001 - * Wolfgang Denk, DENX Software Engineering, wd@denx.de. - * Keith Outwater, keith_outwater@mvsi.com - * - * SPDX-License-Identifier: GPL-2.0+ - */ - -#include <common.h> -#include <mpc8xx.h> - -#if defined(CONFIG_ENV_IS_IN_FLASH) -# ifndef CONFIG_ENV_ADDR -# define CONFIG_ENV_ADDR (CONFIG_SYS_FLASH_BASE + CONFIG_ENV_OFFSET) -# endif -# ifndef CONFIG_ENV_SIZE -# define CONFIG_ENV_SIZE CONFIG_ENV_SECT_SIZE -# endif -# ifndef CONFIG_ENV_SECT_SIZE -# define CONFIG_ENV_SECT_SIZE CONFIG_ENV_SIZE -# endif -#endif - -/* - * Use buffered writes to flash by default - they are about 32x faster than - * single byte writes. - */ -#ifndef CONFIG_SYS_GEN860T_FLASH_USE_WRITE_BUFFER -#define CONFIG_SYS_GEN860T_FLASH_USE_WRITE_BUFFER -#endif - -/* - * Max time to wait (in mS) for flash device to allocate a write buffer. - */ -#ifndef CONFIG_SYS_FLASH_ALLOC_BUFFER_TOUT -#define CONFIG_SYS_FLASH_ALLOC_BUFFER_TOUT 100 -#endif - -/* - * These functions support a single Intel StrataFlash device (28F128J3A) - * in byte mode only!. The flash routines are very basic and simple - * since there isn't really any remapping necessary. - */ - -/* - * Intel SCS (Scalable Command Set) command definitions - * (taken from 28F128J3A datasheet) - */ -#define SCS_READ_CMD 0xff -#define SCS_READ_ID_CMD 0x90 -#define SCS_QUERY_CMD 0x98 -#define SCS_READ_STATUS_CMD 0x70 -#define SCS_CLEAR_STATUS_CMD 0x50 -#define SCS_WRITE_BUF_CMD 0xe8 -#define SCS_PROGRAM_CMD 0x40 -#define SCS_BLOCK_ERASE_CMD 0x20 -#define SCS_BLOCK_ERASE_RESUME_CMD 0xd0 -#define SCS_PROGRAM_RESUME_CMD 0xd0 -#define SCS_BLOCK_ERASE_SUSPEND_CMD 0xb0 -#define SCS_SET_BLOCK_LOCK_CMD 0x60 -#define SCS_CLR_BLOCK_LOCK_CMD 0x60 - -/* - * SCS status/extended status register bit definitions - */ -#define SCS_SR7 0x80 -#define SCS_XSR7 0x80 - -/*---------------------------------------------------------------------*/ -#if 0 -#define DEBUG_FLASH -#endif - -#ifdef DEBUG_FLASH -#define PRINTF(fmt,args...) printf(fmt ,##args) -#else -#define PRINTF(fmt,args...) -#endif -/*---------------------------------------------------------------------*/ - -flash_info_t flash_info[CONFIG_SYS_MAX_FLASH_BANKS]; - -/*----------------------------------------------------------------------- - * Functions - */ -static ulong flash_get_size (vu_char *addr, flash_info_t *info); -static int write_data8 (flash_info_t *info, ulong dest, uchar data); -static void flash_get_offsets (ulong base, flash_info_t *info); - -/*----------------------------------------------------------------------- - * Initialize the flash memory. - */ -unsigned long -flash_init (void) -{ - volatile immap_t *immap = (immap_t *)CONFIG_SYS_IMMR; - volatile memctl8xx_t *memctl = &immap->im_memctl; - unsigned long size_b0; - int i; - - for (i= 0; i < CONFIG_SYS_MAX_FLASH_BANKS; ++i) { - flash_info[i].flash_id = FLASH_UNKNOWN; - } - - /* - * The gen860t board only has one FLASH memory device, so the - * FLASH Bank configuration is done statically. - */ - PRINTF("\n## Get flash bank 1 size @ 0x%08x\n", FLASH_BASE0_PRELIM); - size_b0 = flash_get_size((vu_char *)FLASH_BASE0_PRELIM, &flash_info[0]); - if (flash_info[0].flash_id == FLASH_UNKNOWN) { - printf ("## Unknown FLASH on Bank 0: " - "ID 0x%lx, Size = 0x%08lx = %ld MB\n", - flash_info[0].flash_id,size_b0, size_b0 << 20); - } - - PRINTF("## Before remap:\n" - " BR0: 0x%08x OR0: 0x%08x\n BR1: 0x%08x OR1: 0x%08x\n", - memctl->memc_br0, memctl->memc_or0, - memctl->memc_br1, memctl->memc_or1); - - /* - * Remap FLASH according to real size - */ - memctl->memc_or0 |= (-size_b0 & 0xFFFF8000); - memctl->memc_br0 |= (CONFIG_SYS_FLASH_BASE & BR_BA_MSK); - - PRINTF("## After remap:\n" - " BR0: 0x%08x OR0: 0x%08x\n", memctl->memc_br0, memctl->memc_or0); - - /* - * Re-do sizing to get full correct info - */ - size_b0 = flash_get_size ((vu_char *)CONFIG_SYS_FLASH_BASE, &flash_info[0]); - flash_get_offsets (CONFIG_SYS_FLASH_BASE, &flash_info[0]); - flash_info[0].size = size_b0; - -#if CONFIG_SYS_MONITOR_BASE >= CONFIG_SYS_FLASH_BASE - /* - * Monitor protection is ON by default - */ - flash_protect(FLAG_PROTECT_SET, - CONFIG_SYS_MONITOR_BASE, - CONFIG_SYS_MONITOR_BASE + monitor_flash_len - 1, - &flash_info[0]); -#endif - -#ifdef CONFIG_ENV_IS_IN_FLASH - /* - * Environment protection ON by default - */ - flash_protect(FLAG_PROTECT_SET, - CONFIG_ENV_ADDR, - CONFIG_ENV_ADDR + CONFIG_ENV_SECT_SIZE - 1, - &flash_info[0]); -#endif - - PRINTF("## Final Flash bank size: 0x%08lx\n",size_b0); - return (size_b0); -} - - -/*----------------------------------------------------------------------- - * Fill in the FLASH offset table - */ -static void -flash_get_offsets (ulong base, flash_info_t *info) -{ - int i; - - if (info->flash_id == FLASH_UNKNOWN) { - return; - } - - switch (info->flash_id & FLASH_VENDMASK) { - case FLASH_MAN_INTEL: - for (i = 0; i < info->sector_count; i++) { - info->start[i] = base; - base += 1024 * 128; - } - return; - - default: - printf ("Don't know sector offsets for FLASH" - " type 0x%lx\n", info->flash_id); - return; - } -} - - -/*----------------------------------------------------------------------- - * Display FLASH device info - */ -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_INTEL: - printf ("Intel "); - break; - default: - printf ("Unknown Vendor "); - break; - } - - switch (info->flash_id & FLASH_TYPEMASK) { - case FLASH_28F128J3A: - printf ("28F128J3A (128Mbit = 128K x 128)\n"); - break; - default: - printf ("Unknown Chip Type\n"); - break; - } - - if (info->size >= (1024 * 1024)) { - i = 20; - } else { - i = 10; - } - printf (" Size: %ld %cB in %d Sectors\n", - info->size >> i, - (i == 20) ? 'M' : 'k', - 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; -} - - -/*----------------------------------------------------------------------- - * Get size and other information for a FLASH device. - * NOTE: The following code cannot be run from FLASH! - */ -static -ulong flash_get_size (vu_char *addr, flash_info_t *info) -{ -#define NO_FLASH 0 - - vu_char value[2]; - - /* - * Try to read the manufacturer ID - */ - addr[0] = SCS_READ_CMD; - addr[0] = SCS_READ_ID_CMD; - value[0] = addr[0]; - value[1] = addr[2]; - addr[0] = SCS_READ_CMD; - - PRINTF("Manuf. ID @ 0x%08lx: 0x%02x\n", (ulong)addr, value[0]); - switch (value[0]) { - case (INTEL_MANUFACT & 0xff): - info->flash_id = FLASH_MAN_INTEL; - break; - default: - info->flash_id = FLASH_UNKNOWN; - info->sector_count = 0; - info->size = 0; - return (NO_FLASH); - } - - /* - * Read the device ID - */ - PRINTF("Device ID @ 0x%08lx: 0x%02x\n", (ulong)(&addr[2]), value[1]); - switch (value[1]) { - case (INTEL_ID_28F128J3A & 0xff): - info->flash_id += FLASH_28F128J3A; - info->sector_count = 128; - info->size = 16 * 1024 * 1024; - break; - - default: - info->flash_id = FLASH_UNKNOWN; - return (NO_FLASH); - } - - if (info->sector_count > CONFIG_SYS_MAX_FLASH_SECT) { - printf ("** ERROR: sector count %d > max (%d) **\n", - info->sector_count, CONFIG_SYS_MAX_FLASH_SECT); - info->sector_count = CONFIG_SYS_MAX_FLASH_SECT; - } - return (info->size); -} - - -/*----------------------------------------------------------------------- - * Erase the specified sectors in the specified FLASH device - */ -int -flash_erase(flash_info_t *info, int s_first, int s_last) -{ - int flag, prot, 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; - } - - if ((info->flash_id & FLASH_VENDMASK) != FLASH_MAN_INTEL) { - printf ("Can erase only Intel flash types - aborted\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"); - } - - start = get_timer (0); - last = start; - - /* - * Start erase on unprotected sectors - */ - for (sect = s_first; sect<=s_last; sect++) { - if (info->protect[sect] == 0) { /* not protected */ - vu_char *addr = (uchar *)(info->start[sect]); - vu_char status; - - /* - * Disable interrupts which might cause a timeout - */ - flag = disable_interrupts(); - - *addr = SCS_CLEAR_STATUS_CMD; - *addr = SCS_BLOCK_ERASE_CMD; - *addr = SCS_BLOCK_ERASE_RESUME_CMD; - - /* - * Re-enable interrupts if necessary - */ - if (flag) - enable_interrupts(); - - /* - * Wait at least 80us - let's wait 1 ms - */ - udelay (1000); - - while (((status = *addr) & SCS_SR7) != SCS_SR7) { - if ((now=get_timer(start)) > CONFIG_SYS_FLASH_ERASE_TOUT) { - printf ("Timeout\n"); - *addr = SCS_BLOCK_ERASE_SUSPEND_CMD; - *addr = SCS_READ_CMD; - return 1; - } - - /* - * Show that we're waiting - */ - if ((now - last) > 1000) { /* 1 second */ - putc ('.'); - last = now; - } - } - *addr = SCS_READ_CMD; - } - } - printf (" done\n"); - return 0; -} - - -#ifdef CONFIG_SYS_GEN860T_FLASH_USE_WRITE_BUFFER -/* - * Allocate a flash buffer, fill it with data and write it to the flash. - * 0 - OK - * 1 - Timeout on buffer request - * - * NOTE: After the last call to this function, WSM status needs to be checked! - */ -static int -write_flash_buffer8(flash_info_t *info_p, vu_char *src_p, vu_char *dest_p, - uint count) -{ - vu_char *block_addr_p = NULL; - vu_char *start_addr_p = NULL; - ulong blocksize = info_p->size / (ulong)info_p->sector_count; - - int i; - uint time = get_timer(0); - - PRINTF("%s:%d: src: 0x%p dest: 0x%p count: %d\n", - __FUNCTION__, __LINE__, src_p, dest_p, count); - - /* - * What block are we in? We already know that the source address is - * in the flash address range, but we also can't cross a block boundary. - * We assume that the block does not cross a boundary (we'll check before - * calling this function). - */ - for (i = 0; i < info_p->sector_count; ++i) { - if ( ((ulong)dest_p >= info_p->start[i]) && - ((ulong)dest_p < (info_p->start[i] + blocksize)) ) { - PRINTF("%s:%d: Dest addr 0x%p is in block %d @ 0x%.8lx\n", - __FUNCTION__, __LINE__, dest_p, i, info_p->start[i]); - block_addr_p = (vu_char *)info_p->start[i]; - break; - } - } - - /* - * Request a buffer - */ - *block_addr_p = SCS_WRITE_BUF_CMD; - while ((*block_addr_p & SCS_XSR7) != SCS_XSR7) { - if (get_timer(time) > CONFIG_SYS_FLASH_ALLOC_BUFFER_TOUT) { - PRINTF("%s:%d: Buffer allocation timeout @ 0x%p (waited %d mS)\n", - __FUNCTION__, __LINE__, block_addr_p, - CONFIG_SYS_FLASH_ALLOC_BUFFER_TOUT); - return 1; - } - *block_addr_p = SCS_WRITE_BUF_CMD; - } - - /* - * Fill the buffer with data - */ - start_addr_p = dest_p; - *block_addr_p = count - 1; /* flash device wants count - 1 */ - PRINTF("%s:%d: Fill buffer at block addr 0x%p\n", - __FUNCTION__, __LINE__, block_addr_p); - for (i = 0; i < count; i++) { - *start_addr_p++ = *src_p++; - } - - /* - * Flush buffer to flash - */ - *block_addr_p = SCS_PROGRAM_RESUME_CMD; -#if 1 - time = get_timer(0); - while ((*block_addr_p & SCS_SR7) != SCS_SR7) { - if (get_timer(time) > CONFIG_SYS_FLASH_WRITE_TOUT) { - PRINTF("%s:%d: Write timeout @ 0x%p (waited %d mS)\n", - __FUNCTION__, __LINE__, block_addr_p, CONFIG_SYS_FLASH_WRITE_TOUT); - return 1; - } - } - -#endif - return 0; -} -#endif - - -/*----------------------------------------------------------------------- - * Copy memory to flash, returns: - * 0 - OK - * 1 - write timeout - * 2 - Flash not erased - * 4 - Flash not identified - */ -int -write_buff(flash_info_t *info_p, uchar *src_p, ulong addr, ulong count) -{ - int rc = 0; -#ifdef CONFIG_SYS_GEN860T_FLASH_USE_WRITE_BUFFER -#define FLASH_WRITE_BUF_SIZE 0x00000020 /* 32 bytes */ - int i; - uint bufs; - ulong buf_count; - vu_char *sp; - vu_char *dp; -#else - ulong wp; -#endif - - PRINTF("\n%s:%d: src: 0x%.8lx dest: 0x%.8lx size: %d (0x%.8lx)\n", - __FUNCTION__, __LINE__, (ulong)src_p, addr, (uint)count, count); - - if (info_p->flash_id == FLASH_UNKNOWN) { - return 4; - } - -#ifdef CONFIG_SYS_GEN860T_FLASH_USE_WRITE_BUFFER - sp = src_p; - dp = (uchar *)addr; - - /* - * For maximum performance, we want to align the start address to - * the beginning of a write buffer boundary (i.e. A4-A0 of the - * start address = 0). See how many bytes are required to get to a - * write-buffer-aligned address. If that number is non-zero, do - * non buffered writes of the non-aligned data. By doing non-buffered - * writes, we avoid the problem of crossing a block (sector) boundary - * with buffered writes. - */ - buf_count = FLASH_WRITE_BUF_SIZE - (addr & (FLASH_WRITE_BUF_SIZE - 1)); - if (buf_count == FLASH_WRITE_BUF_SIZE) { /* already on a boundary */ - buf_count = 0; - } - if (buf_count > count) { /* not a full buffers worth of data to write */ - buf_count = count; - } - count -= buf_count; - - PRINTF("%s:%d: Write buffer alignment count = %ld\n", - __FUNCTION__, __LINE__, buf_count); - while (buf_count-- >= 1) { - if ((rc = write_data8(info_p, (ulong)dp++, *sp++)) != 0) { - return (rc); - } - } - - PRINTF("%s:%d: count = %ld\n", __FUNCTION__, __LINE__, count); - if (count == 0) { /* all done */ - PRINTF("%s:%d: Less than 1 buffer (%d) worth of bytes\n", - __FUNCTION__, __LINE__, FLASH_WRITE_BUF_SIZE); - return (rc); - } - - /* - * Now that we are write buffer aligned, write full or partial buffers. - * The fact that we are write buffer aligned automatically avoids - * crossing a block address during a write buffer operation. - */ - bufs = count / FLASH_WRITE_BUF_SIZE; - PRINTF("%s:%d: %d (0x%x) buffers to write\n", __FUNCTION__, __LINE__, - bufs, bufs); - while (bufs >= 1) { - rc = write_flash_buffer8(info_p, sp, dp, FLASH_WRITE_BUF_SIZE); - if (rc != 0) { - PRINTF("%s:%d: ** Error writing buf %d\n", - __FUNCTION__, __LINE__, bufs); - return (rc); - } - bufs--; - sp += FLASH_WRITE_BUF_SIZE; - dp += FLASH_WRITE_BUF_SIZE; - } - - /* - * Do the leftovers - */ - i = count % FLASH_WRITE_BUF_SIZE; - PRINTF("%s:%d: %d (0x%x) leftover bytes\n", __FUNCTION__, __LINE__, i, i); - if (i > 0) { - rc = write_flash_buffer8(info_p, sp, dp, i); - } - - sp = (vu_char*)info_p->start[0]; - *sp = SCS_READ_CMD; - return (rc); - -#else - wp = addr; - while (count-- >= 1) { - if((rc = write_data8(info_p, wp++, *src_p++)) != 0) - return (rc); - } - return 0; -#endif -} - - -/*----------------------------------------------------------------------- - * Write a byte to Flash, returns: - * 0 - OK - * 1 - write timeout - * 2 - Flash not erased - */ -static int -write_data8 (flash_info_t *info, ulong dest, uchar data) -{ - vu_char *addr = (vu_char *)dest; - vu_char status; - ulong start; - int flag; - - /* Check if Flash is (sufficiently) erased */ - if ((*addr & data) != data) { - return (2); - } - /* Disable interrupts which might cause a timeout here */ - flag = disable_interrupts(); - - *addr = SCS_PROGRAM_CMD; - *addr = data; - - /* re-enable interrupts if necessary */ - if (flag) - enable_interrupts(); - - start = get_timer (0); - - while (((status = *addr) & SCS_SR7) != SCS_SR7) { - if (get_timer(start) > CONFIG_SYS_FLASH_WRITE_TOUT) { - *addr = SCS_READ_CMD; - return (1); - } - } - *addr = SCS_READ_CMD; - return (0); -} - -/* vim: set ts=4 sw=4 tw=78: */ diff --git a/board/gen860t/fpga.c b/board/gen860t/fpga.c deleted file mode 100644 index dd0ef70..0000000 --- a/board/gen860t/fpga.c +++ /dev/null @@ -1,362 +0,0 @@ -/* - * (C) Copyright 2002 - * Rich Ireland, Enterasys Networks, rireland@enterasys.com. - * Keith Outwater, keith_outwater@mvis.com. - * - * SPDX-License-Identifier: GPL-2.0+ - */ - -/* - * Virtex2 FPGA configuration support for the GEN860T computer - */ - -#include <common.h> -#include <virtex2.h> -#include <command.h> -#include "fpga.h" - -DECLARE_GLOBAL_DATA_PTR; - -#if defined(CONFIG_FPGA) - -#if 0 -#define GEN860T_FPGA_DEBUG -#endif - -#ifdef GEN860T_FPGA_DEBUG -#define PRINTF(fmt,args...) printf (fmt ,##args) -#else -#define PRINTF(fmt,args...) -#endif - -/* - * Port bit numbers for the Selectmap controls - */ -#define FPGA_INIT_BIT_NUM 22 /* PB22 */ -#define FPGA_RESET_BIT_NUM 11 /* PC11 */ -#define FPGA_DONE_BIT_NUM 16 /* PB16 */ -#define FPGA_PROGRAM_BIT_NUM 7 /* PA7 */ - -/* Note that these are pointers to code that is in Flash. They will be - * relocated at runtime. - */ -xilinx_virtex2_slave_selectmap_fns fpga_fns = { - fpga_pre_config_fn, - fpga_pgm_fn, - fpga_init_fn, - fpga_err_fn, - fpga_done_fn, - fpga_clk_fn, - fpga_cs_fn, - fpga_wr_fn, - fpga_read_data_fn, - fpga_write_data_fn, - fpga_busy_fn, - fpga_abort_fn, - fpga_post_config_fn -}; - -xilinx_desc fpga[CONFIG_FPGA_COUNT] = { - {xilinx_virtex2, - slave_selectmap, - XILINX_XC2V3000_SIZE, - (void *) &fpga_fns, - 0} -}; - -/* - * Display FPGA revision information - */ -void print_fpga_revision (void) -{ - vu_long *rev_p = (vu_long *) 0x60000008; - - printf ("FPGA Revision 0x%.8lx" - " (Date %.2lx/%.2lx/%.2lx, Status \"%.1lx\", Version %.3lu)\n", - *rev_p, - ((*rev_p >> 28) & 0xf), - ((*rev_p >> 20) & 0xff), - ((*rev_p >> 12) & 0xff), - ((*rev_p >> 8) & 0xf), (*rev_p & 0xff)); -} - - -/* - * Perform a simple test of the FPGA to processor interface using the FPGA's - * inverting bus test register. The great thing about doing a read/write - * test on a register that inverts it's contents is that you avoid any - * problems with bus charging. - * Return 0 on failure, 1 on success. - */ -int test_fpga_ibtr (void) -{ - vu_long *ibtr_p = (vu_long *) 0x60000010; - vu_long readback; - vu_long compare; - int i; - int j; - int k; - int pass = 1; - - static const ulong bitpattern[] = { - 0xdeadbeef, /* magic ID pattern for debug */ - 0x00000001, /* single bit */ - 0x00000003, /* two adjacent bits */ - 0x00000007, /* three adjacent bits */ - 0x0000000F, /* four adjacent bits */ - 0x00000005, /* two non-adjacent bits */ - 0x00000015, /* three non-adjacent bits */ - 0x00000055, /* four non-adjacent bits */ - 0xaaaaaaaa, /* alternating 1/0 */ - }; - - for (i = 0; i < 1024; i++) { - for (j = 0; j < 31; j++) { - for (k = 0; - k < sizeof (bitpattern) / sizeof (bitpattern[0]); - k++) { - *ibtr_p = compare = (bitpattern[k] << j); - readback = *ibtr_p; - if (readback != ~compare) { - printf ("%s:%d: FPGA test fail: expected 0x%.8lx" " actual 0x%.8lx\n", __FUNCTION__, __LINE__, ~compare, readback); - pass = 0; - break; - } - } - if (!pass) - break; - } - if (!pass) - break; - } - if (pass) { - printf ("FPGA inverting bus test passed\n"); - print_fpga_revision (); - } else { - printf ("** FPGA inverting bus test failed\n"); - } - return pass; -} - - -/* - * Set the active-low FPGA reset signal. - */ -void fpga_reset (int assert) -{ - volatile immap_t *immap = (immap_t *) CONFIG_SYS_IMMR; - - PRINTF ("%s:%d: RESET ", __FUNCTION__, __LINE__); - if (assert) { - immap->im_ioport.iop_pcdat &= ~(0x8000 >> FPGA_RESET_BIT_NUM); - PRINTF ("asserted\n"); - } else { - immap->im_ioport.iop_pcdat |= (0x8000 >> FPGA_RESET_BIT_NUM); - PRINTF ("deasserted\n"); - } -} - - -/* - * Initialize the SelectMap interface. We assume that the mode and the - * initial state of all of the port pins have already been set! - */ -void fpga_selectmap_init (void) -{ - PRINTF ("%s:%d: Initialize SelectMap interface\n", __FUNCTION__, - __LINE__); - fpga_pgm_fn(false, false, 0); /* make sure program pin is inactive */ -} - - -/* - * Initialize the fpga. Return 1 on success, 0 on failure. - */ -int gen860t_init_fpga (void) -{ - int i; - - PRINTF ("%s:%d: Initialize FPGA interface\n", - __FUNCTION__, __LINE__); - fpga_init (); - fpga_selectmap_init (); - - for (i = 0; i < CONFIG_FPGA_COUNT; i++) { - PRINTF ("%s:%d: Adding fpga %d\n", __FUNCTION__, __LINE__, i); - fpga_add (fpga_xilinx, &fpga[i]); - } - return 1; -} - - -/* - * Set the FPGA's active-low SelectMap program line to the specified level - */ -int fpga_pgm_fn (int assert, int flush, int cookie) -{ - volatile immap_t *immap = (immap_t *) CONFIG_SYS_IMMR; - - PRINTF ("%s:%d: FPGA PROGRAM ", __FUNCTION__, __LINE__); - - if (assert) { - immap->im_ioport.iop_padat &= - ~(0x8000 >> FPGA_PROGRAM_BIT_NUM); - PRINTF ("asserted\n"); - } else { - immap->im_ioport.iop_padat |= - (0x8000 >> FPGA_PROGRAM_BIT_NUM); - PRINTF ("deasserted\n"); - } - return assert; -} - - -/* - * Test the state of the active-low FPGA INIT line. Return 1 on INIT - * asserted (low). - */ -int fpga_init_fn (int cookie) -{ - volatile immap_t *immap = (immap_t *) CONFIG_SYS_IMMR; - - PRINTF ("%s:%d: INIT check... ", __FUNCTION__, __LINE__); - if (immap->im_cpm.cp_pbdat & (0x80000000 >> FPGA_INIT_BIT_NUM)) { - PRINTF ("high\n"); - return 0; - } else { - PRINTF ("low\n"); - return 1; - } -} - - -/* - * Test the state of the active-high FPGA DONE pin - */ -int fpga_done_fn (int cookie) -{ - volatile immap_t *immap = (immap_t *) CONFIG_SYS_IMMR; - - PRINTF ("%s:%d: DONE check... ", __FUNCTION__, __LINE__); - if (immap->im_cpm.cp_pbdat & (0x80000000 >> FPGA_DONE_BIT_NUM)) { - PRINTF ("high\n"); - return FPGA_SUCCESS; - } else { - PRINTF ("low\n"); - return FPGA_FAIL; - } -} - - -/* - * Read FPGA SelectMap data. - */ -int fpga_read_data_fn (unsigned char *data, int cookie) -{ - vu_char *p = (vu_char *) SELECTMAP_BASE; - - *data = *p; -#if 0 - PRINTF ("%s: Read 0x%x into 0x%p\n", __FUNCTION__, (int) data, data); -#endif - return (int) data; -} - - -/* - * Write data to the FPGA SelectMap port - */ -int fpga_write_data_fn (unsigned char data, int flush, int cookie) -{ - vu_char *p = (vu_char *) SELECTMAP_BASE; - -#if 0 - PRINTF ("%s: Write Data 0x%x\n", __FUNCTION__, (int) data); -#endif - *p = data; - return (int) data; -} - - -/* - * Abort and FPGA operation - */ -int fpga_abort_fn (int cookie) -{ - PRINTF ("%s:%d: FPGA program sequence aborted\n", - __FUNCTION__, __LINE__); - return FPGA_FAIL; -} - - -/* - * FPGA pre-configuration function. Just make sure that - * FPGA reset is asserted to keep the FPGA from starting up after - * configuration. - */ -int fpga_pre_config_fn (int cookie) -{ - PRINTF ("%s:%d: FPGA pre-configuration\n", __FUNCTION__, __LINE__); - fpga_reset(true); - return 0; -} - - -/* - * FPGA post configuration function. Blip the FPGA reset line and then see if - * the FPGA appears to be running. - */ -int fpga_post_config_fn (int cookie) -{ - int rc; - - PRINTF ("%s:%d: FPGA post configuration\n", __FUNCTION__, __LINE__); - fpga_reset(true); - udelay (1000); - fpga_reset(false); - udelay (1000); - - /* - * Use the FPGA,s inverting bus test register to do a simple test of the - * processor interface. - */ - rc = test_fpga_ibtr (); - return rc; -} - - -/* - * Clock, chip select and write signal assert functions and error check - * and busy functions. These are only stubs because the GEN860T selectmap - * interface handles sequencing of control signals automatically (it uses - * a memory-mapped interface to the FPGA SelectMap port). The design of - * the interface guarantees that the SelectMap port cannot be overrun so - * no busy check is needed. A configuration error is signalled by INIT - * going low during configuration, so there is no need for a separate error - * function. - */ -int fpga_clk_fn (int assert_clk, int flush, int cookie) -{ - return assert_clk; -} - -int fpga_cs_fn (int assert_cs, int flush, int cookie) -{ - return assert_cs; -} - -int fpga_wr_fn (int assert_write, int flush, int cookie) -{ - return assert_write; -} - -int fpga_err_fn (int cookie) -{ - return 0; -} - -int fpga_busy_fn (int cookie) -{ - return 0; -} -#endif diff --git a/board/gen860t/fpga.h b/board/gen860t/fpga.h deleted file mode 100644 index 95c15c4..0000000 --- a/board/gen860t/fpga.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * (C) Copyright 2002 - * Rich Ireland, Enterasys Networks, rireland@enterasys.com. - * Keith Outwater, keith_outwater@mvis.com. - * - * SPDX-License-Identifier: GPL-2.0+ - */ - -/* - * Virtex2 FPGA configuration support for the GEN860T computer - */ - -extern int gen860t_init_fpga(void); -extern int fpga_pgm_fn(int assert_pgm, int flush, int cookie); -extern int fpga_init_fn(int cookie); -extern int fpga_err_fn(int cookie); -extern int fpga_done_fn(int cookie); -extern int fpga_clk_fn(int assert_clk, int flush, int cookie); -extern int fpga_cs_fn(int assert_cs, int flush, int cookie); -extern int fpga_wr_fn(int assert_write, int flush, int cookie); -extern int fpga_read_data_fn(unsigned char *data, int cookie); -extern int fpga_write_data_fn(unsigned char data, int flush, int cookie); -extern int fpga_busy_fn(int cookie); -extern int fpga_abort_fn(int cookie ); -extern int fpga_pre_config_fn(int cookie ); -extern int fpga_post_config_fn(int cookie ); diff --git a/board/gen860t/gen860t.c b/board/gen860t/gen860t.c deleted file mode 100644 index fe139f4..0000000 --- a/board/gen860t/gen860t.c +++ /dev/null @@ -1,278 +0,0 @@ -/* - * (C) Copyright 2000 - * Wolfgang Denk, DENX Software Engineering, wd@denx.de. - * Keith Outwater, keith_outwater@mvis.com - * - * SPDX-License-Identifier: GPL-2.0+ - */ - -#include <virtex2.h> -#include <common.h> -#include <mpc8xx.h> -#include <asm/8xx_immap.h> -#include "beeper.h" -#include "fpga.h" -#include "ioport.h" - -DECLARE_GLOBAL_DATA_PTR; - -#ifdef CONFIG_STATUS_LED -#include <status_led.h> -#endif - -#if defined(CONFIG_CMD_MII) && defined(CONFIG_MII) -#include <net.h> -#endif - -#if 0 -#define GEN860T_DEBUG -#endif - -#ifdef GEN860T_DEBUG -#define PRINTF(fmt,args...) printf (fmt ,##args) -#else -#define PRINTF(fmt,args...) -#endif - -/* - * The following UPM init tables were generated automatically by - * Motorola's MCUINIT program. See the README file for UPM to - * SDRAM pin assignments if you want to type this data into - * MCUINIT in order to reverse engineer the waveforms. - */ - -/* - * UPM initialization tables for MICRON MT48LC16M16A2TG SDRAM devices - * (UPMA) and Virtex FPGA SelectMap interface (UPMB). - * NOTE that unused areas of the table are used to hold NOP, precharge - * and mode register set sequences. - * - */ -#define UPMA_NOP_ADDR 0x5 -#define UPMA_PRECHARGE_ADDR 0x6 -#define UPMA_MRS_ADDR 0x12 - -#define UPM_SINGLE_READ_ADDR 0x00 -#define UPM_BURST_READ_ADDR 0x08 -#define UPM_SINGLE_WRITE_ADDR 0x18 -#define UPM_BURST_WRITE_ADDR 0x20 -#define UPM_REFRESH_ADDR 0x30 - -const uint sdram_upm_table[] = { - /* single read (offset 0x00 in upm ram) */ - 0x0e0fdc04, 0x01adfc04, 0x0fbffc00, 0x1fff5c05, - 0xffffffff, 0x0fffffcd, 0x0fff0fce, 0xefcfffff, - /* burst read (offset 0x08 in upm ram) */ - 0x0f0fdc04, 0x00fdfc04, 0xf0fffc00, 0xf0fffc00, - 0xf1fffc00, 0xfffffc00, 0xfffffc05, 0xffffffff, - 0xffffffff, 0xffffffff, 0x0ffffff4, 0x1f3d5ff4, - 0xfffffff4, 0xfffffff5, 0xffffffff, 0xffffffff, - /* single write (offset 0x18 in upm ram) */ - 0x0f0fdc04, 0x00ad3c00, 0x1fff5c05, 0xffffffff, - 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, - /* burst write (offset 0x20 in upm ram) */ - 0x0f0fdc00, 0x10fd7c00, 0xf0fffc00, 0xf0fffc00, - 0xf1fffc04, 0xfffffc05, 0xffffffff, 0xffffffff, - 0xffffffff, 0xffffffff, 0xffffffff, 0xfffff7ff, - 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, - /* refresh (offset 0x30 in upm ram) */ - 0x1ffddc84, 0xfffffc04, 0xfffffc04, 0xfffffc84, - 0xfffffc05, 0xffffffff, 0xffffffff, 0xffffffff, - 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, - /* exception (offset 0x3C in upm ram) */ -}; - -const uint selectmap_upm_table[] = { - /* single read (offset 0x00 in upm ram) */ - 0x88fffc06, 0x00fff404, 0x00fffc04, 0x33fffc00, - 0xfffffc05, 0xffffffff, 0xffffffff, 0xffffffff, - /* burst read (offset 0x08 in upm ram) */ - 0xfffffc04, 0xfffffc05, 0xffffffff, 0xffffffff, - 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, - 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, - 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, - /* single write (offset 0x18 in upm ram) */ - 0x88fffc04, 0x00fff400, 0x77fffc05, 0xffffffff, - 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, - /* burst write (offset 0x20 in upm ram) */ - 0xfffffc04, 0xfffffc05, 0xffffffff, 0xffffffff, - 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, - 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, - 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, - /* refresh (offset 0x30 in upm ram) */ - 0xfffffc04, 0xfffffc05, 0xffffffff, 0xffffffff, - 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, - 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, - /* exception (offset 0x3C in upm ram) */ - 0xfffffc05, 0xffffffff, 0xffffffff, 0xffffffff -}; - -/* - * Check board identity. Always successful (gives information only) - */ -int checkboard (void) -{ - char *s; - char buf[64]; - int i; - - i = getenv_f("board_id", buf, sizeof (buf)); - s = (i > 0) ? buf : NULL; - - if (s) { - printf ("%s ", s); - } else { - printf ("<unknown> "); - } - - i = getenv_f("serial#", buf, sizeof (buf)); - s = (i > 0) ? buf : NULL; - - if (s) { - printf ("S/N %s\n", s); - } else { - printf ("S/N <unknown>\n"); - } - - printf ("CPU at %s MHz, ", strmhz (buf, gd->cpu_clk)); - printf ("local bus at %s MHz\n", strmhz (buf, gd->bus_clk)); - return (0); -} - -/* - * Initialize SDRAM - */ -phys_size_t initdram (int board_type) -{ - volatile immap_t *immr = (immap_t *) CONFIG_SYS_IMMR; - volatile memctl8xx_t *memctl = &immr->im_memctl; - - upmconfig (UPMA, - (uint *) sdram_upm_table, - sizeof (sdram_upm_table) / sizeof (uint) - ); - - /* - * Setup MAMR register - */ - memctl->memc_mptpr = CONFIG_SYS_MPTPR_1BK_8K; - memctl->memc_mamr = CONFIG_SYS_MAMR_8COL & (~(MAMR_PTAE)); /* no refresh yet */ - - /* - * Map CS1* to SDRAM bank - */ - memctl->memc_or1 = CONFIG_SYS_OR1; - memctl->memc_br1 = CONFIG_SYS_BR1; - - /* - * Perform SDRAM initialization sequence: - * 1. Apply at least one NOP command - * 2. 100 uS delay (JEDEC standard says 200 uS) - * 3. Issue 4 precharge commands - * 4. Perform two refresh cycles - * 5. Program mode register - * - * Program SDRAM for standard operation, sequential burst, burst length - * of 4, CAS latency of 2. - */ - memctl->memc_mar = 0x00000000; - memctl->memc_mcr = MCR_UPM_A | MCR_OP_RUN | MCR_MB_CS1 | - MCR_MLCF (0) | UPMA_NOP_ADDR; - udelay (200); - memctl->memc_mar = 0x00000000; - memctl->memc_mcr = MCR_UPM_A | MCR_OP_RUN | MCR_MB_CS1 | - MCR_MLCF (4) | UPMA_PRECHARGE_ADDR; - - memctl->memc_mar = 0x00000000; - memctl->memc_mcr = MCR_UPM_A | MCR_OP_RUN | MCR_MB_CS1 | - MCR_MLCF (2) | UPM_REFRESH_ADDR; - - memctl->memc_mar = 0x00000088; - memctl->memc_mcr = MCR_UPM_A | MCR_OP_RUN | MCR_MB_CS1 | - MCR_MLCF (1) | UPMA_MRS_ADDR; - - memctl->memc_mar = 0x00000000; - memctl->memc_mcr = MCR_UPM_A | MCR_OP_RUN | MCR_MB_CS1 | - MCR_MLCF (0) | UPMA_NOP_ADDR; - /* - * Enable refresh - */ - memctl->memc_mamr |= MAMR_PTAE; - - return (SDRAM_SIZE); -} - -/* - * Disk On Chip (DOC) Millenium initialization. - * The DOC lives in the CS2* space - */ -#if defined(CONFIG_CMD_DOC) -void doc_init (void) -{ - printf ("Probing at 0x%.8x: ", DOC_BASE); - doc_probe (DOC_BASE); -} -#endif - -/* - * Miscellaneous intialization - */ -int misc_init_r (void) -{ - volatile immap_t *immr = (immap_t *) CONFIG_SYS_IMMR; - volatile memctl8xx_t *memctl = &immr->im_memctl; - - /* - * Set up UPMB to handle the Virtex FPGA SelectMap interface - */ - upmconfig (UPMB, (uint *) selectmap_upm_table, - sizeof (selectmap_upm_table) / sizeof (uint)); - - memctl->memc_mbmr = 0x0; - - config_mpc8xx_ioports (immr); - -#if defined(CONFIG_CMD_MII) - mii_init (); -#endif - -#if defined(CONFIG_FPGA) - gen860t_init_fpga (); -#endif - return 0; -} - -/* - * Final init hook before entering command loop. - */ -int last_stage_init (void) -{ -#if !defined(CONFIG_SC) - char buf[256]; - int i; - - /* - * Turn the beeper volume all the way down in case this is a warm boot. - */ - set_beeper_volume (-64); - init_beeper (); - - /* - * Read the environment to see what to do with the beeper - */ - i = getenv_f("beeper", buf, sizeof (buf)); - if (i > 0) { - do_beeper (buf); - } -#endif - return 0; -} - -/* - * Stub to make POST code happy. Can't self-poweroff, so just hang. - */ -void board_poweroff (void) -{ - puts ("### Please power off the board ###\n"); - while (1); -} diff --git a/board/gen860t/ioport.c b/board/gen860t/ioport.c deleted file mode 100644 index 7cd209b..0000000 --- a/board/gen860t/ioport.c +++ /dev/null @@ -1,331 +0,0 @@ -/* - * (C) Copyright 2000 - * Wolfgang Denk, DENX Software Engineering, wd@denx.de. - * - * SPDX-License-Identifier: GPL-2.0+ - */ - -#include <common.h> -#include <mpc8xx.h> -#include <asm/8xx_immap.h> -#include "ioport.h" - -#if 0 -#define IOPORT_DEBUG -#endif - -#ifdef IOPORT_DEBUG -#define PRINTF(fmt,args...) printf (fmt ,##args) -#else -#define PRINTF(fmt,args...) -#endif - -/* - * The ioport configuration table. - */ -const mpc8xx_iop_conf_t iop_conf_tab[NUM_PORTS][PORT_BITS] = { - /* - * Port A configuration - * Pin Signal Type Active Initial state - * PA7 fpgaProgramLowOut Out Low High - * PA1 fpgaCoreVoltageFailLow In Low N/A - */ - { /* conf ppar psor pdir podr pdat pint function */ - /* N/A */ { 0, 0, 0, 0, 0, 0, 0 }, /* No pin */ - /* N/A */ { 0, 0, 0, 0, 0, 0, 0 }, /* No pin */ - /* PA15 */ { 0, 0, 0, 0, 0, 0, 0 }, /* */ - /* PA14 */ { 0, 0, 0, 0, 0, 0, 0 }, /* */ - /* PA13 */ { 0, 0, 0, 0, 0, 0, 0 }, /* */ - /* PA12 */ { 0, 0, 0, 0, 0, 0, 0 }, /* */ - /* PA11 */ { 0, 0, 0, 0, 0, 0, 0 }, /* */ - /* PA10 */ { 0, 0, 0, 0, 0, 0, 0 }, /* */ - /* PA9 */ { 1, 0, 0, 1, 0, 0, 0 }, /* grn bicolor LED 1*/ - /* PA8 */ { 1, 0, 0, 1, 0, 0, 0 }, /* red bicolor LED 1*/ - /* PA7 */ { 1, 0, 0, 1, 0, 1, 0 }, /* fpgaProgramLow */ - /* PA6 */ { 0, 0, 0, 0, 0, 0, 0 }, /* */ - /* PA5 */ { 1, 0, 0, 1, 0, 0, 0 }, /* grn bicolor LED 0*/ - /* PA4 */ { 1, 0, 0, 1, 0, 0, 0 }, /* red bicolor LED 0*/ - /* PA3 */ { 0, 0, 0, 0, 0, 0, 0 }, /* */ - /* PA2 */ { 0, 0, 0, 0, 0, 0, 0 }, /* */ -#if !defined(CONFIG_SC) - /* PA1 */ { 1, 0, 0, 0, 0, 0, 0 }, /* fpgaCoreVoltageFail*/ -#else - /* PA1 */ { 0, 0, 0, 0, 0, 0, 0 }, /* */ -#endif - /* PA0 */ { 0, 0, 0, 0, 0, 0, 0 } /* */ - }, - - /* - * Port B configuration - * Pin Signal Type Active Initial state - * PB14 docBusyLowIn In Low X - * PB15 gpio1Sig Out High Low - * PB16 fpgaDoneBi In High X - * PB17 swBitOkLowOut Out Low High - * PB19 speakerVolSig Out/Hi-Z High/Low High (Hi-Z) - * PB22 fpgaInitLowBi In Low X - * PB23 batteryOkSig In High X - * PB31 pulseCatcherClr Out High 0 - */ - { /* conf ppar psor pdir podr pdat pint function */ -#if !defined(CONFIG_SC) - /* PB31 */ { 0, 0, 0, 0, 0, 0, 0 }, /* */ -#else - /* PB31 */ { 1, 0, 0, 1, 0, 0, 0 }, /* pulseCatcherClr */ -#endif - /* PB30 */ { 0, 0, 0, 0, 0, 0, 0 }, /* */ - /* PB29 */ { 0, 0, 0, 0, 0, 0, 0 }, /* */ - /* PB28 */ { 0, 0, 0, 0, 0, 0, 0 }, /* */ - /* PB27 */ { 0, 0, 0, 0, 0, 0, 0 }, /* */ - /* PB26 */ { 0, 0, 0, 0, 0, 0, 0 }, /* */ - /* PB25 */ { 0, 0, 0, 0, 0, 0, 0 }, /* */ - /* PB24 */ { 0, 0, 0, 0, 0, 0, 0 }, /* */ -#if !defined(CONFIG_SC) - /* PB23 */ { 1, 0, 0, 0, 0, 0, 0 }, /* batteryOk */ -#else - /* PB23 */ { 0, 0, 0, 0, 0, 0, 0 }, /* */ -#endif - /* PB22 */ { 1, 0, 0, 0, 0, 0, 0 }, /* fpgaInitLowBi */ - /* PB21 */ { 0, 0, 0, 0, 0, 0, 0 }, /* */ - /* PB20 */ { 0, 0, 0, 0, 0, 0, 0 }, /* */ -#if !defined(CONFIG_SC) - /* PB19 */ { 1, 0, 0, 1, 1, 1, 0 }, /* speakerVol */ -#else - /* PB19 */ { 0, 0, 0, 1, 1, 1, 0 }, /* */ -#endif - /* PB18 */ { 0, 0, 0, 0, 0, 0, 0 }, /* */ - /* PB17 */ { 1, 0, 0, 1, 0, 1, 0 }, /* swBitOkLow */ - /* PB16 */ { 1, 0, 0, 0, 0, 0, 0 }, /* fpgaDone */ - /* PB15 */ { 1, 0, 0, 1, 0, 0, 0 }, /* gpio1 */ -#if !defined(CONFIG_SC) - /* PB14 */ { 1, 0, 0, 0, 0, 0, 0 } /* docBusyLow */ -#else - /* PB14 */ { 0, 0, 0, 0, 0, 0, 0 } /* */ -#endif - }, - - /* - * Port C configuration - * Pin Signal Type Active Initial state - * PC4 i2cBus1EnSig Out High High - * PC5 i2cBus2EnSig Out High High - * PC6 gpio0Sig Out High Low - * PC8 i2cBus3EnSig Out High High - * PC10 i2cBus4EnSig Out High High - * PC11 fpgaResetLowOut Out Low High - * PC12 systemBitOkIn In High X - * PC15 selfDreqLow In Low X - */ - { /* conf ppar psor pdir podr pdat pint function */ - /* N/A */ { 0, 0, 0, 0, 0, 0, 0 }, /* */ - /* N/A */ { 0, 0, 0, 0, 0, 0, 0 }, /* */ - /* PC15 */ { 1, 0, 0, 0, 0, 0, 0 }, /* selfDreqLowIn */ - /* PC14 */ { 0, 0, 0, 0, 0, 0, 0 }, /* */ - /* PC13 */ { 0, 0, 0, 0, 0, 0, 0 }, /* */ -#if !defined(CONFIG_SC) - /* PC12 */ { 1, 0, 0, 0, 0, 0, 0 }, /* systemBitOkIn */ -#else - /* PC12 */ { 0, 0, 0, 0, 0, 0, 0 }, /* */ -#endif - /* PC11 */ { 1, 0, 0, 1, 0, 1, 0 }, /* fpgaResetLowOut */ -#if !defined(CONFIG_SC) - /* PC10 */ { 1, 0, 0, 1, 0, 1, 0 }, /* i2cBus4EnSig */ -#else - /* PC10 */ { 0, 0, 0, 1, 0, 1, 0 }, /* */ -#endif - /* PC9 */ { 0, 0, 0, 0, 0, 0, 0 }, /* */ -#if !defined(CONFIG_SC) - /* PC8 */ { 1, 0, 0, 1, 0, 1, 0 }, /* i2cBus3EnSig */ -#else - /* PC8 */ { 0, 0, 0, 1, 0, 1, 0 }, /* */ -#endif - /* PC7 */ { 0, 0, 0, 0, 0, 0, 0 }, /* */ - /* PC6 */ { 1, 0, 0, 1, 0, 1, 0 }, /* gpio0 */ -#if !defined(CONFIG_SC) - /* PC5 */ { 1, 0, 0, 1, 0, 1, 0 }, /* i2cBus2EnSig */ - /* PC4 */ { 1, 0, 0, 1, 0, 1, 0 }, /* i2cBus1EnSig */ -#else - /* PC5 */ { 0, 0, 0, 1, 0, 1, 0 }, /* */ - /* PC4 */ { 0, 0, 0, 1, 0, 1, 0 }, /* */ -#endif - /* N/A */ { 0, 0, 0, 0, 0, 0, 0 }, /* */ - /* N/A */ { 0, 0, 0, 0, 0, 0, 0 }, /* */ - /* N/A */ { 0, 0, 0, 0, 0, 0, 0 }, /* */ - /* N/A */ { 0, 0, 0, 0, 0, 0, 0 } /* */ - }, - - /* - * Port D configuration - */ - { /* conf ppar psor pdir podr pdat pint function */ - /* N/A */ { 0, 0, 0, 0, 0, 0, 0 }, /* */ - /* N/A */ { 0, 0, 0, 0, 0, 0, 0 }, /* */ - /* PD15 */ { 0, 0, 0, 0, 0, 0, 0 }, /* */ - /* PD14 */ { 0, 0, 0, 0, 0, 0, 0 }, /* */ - /* PD13 */ { 0, 0, 0, 0, 0, 0, 0 }, /* */ - /* PD12 */ { 0, 0, 0, 0, 0, 0, 0 }, /* */ - /* PD11 */ { 0, 0, 0, 0, 0, 0, 0 }, /* */ - /* PD10 */ { 0, 0, 0, 0, 0, 0, 0 }, /* */ - /* PD9 */ { 0, 0, 0, 0, 0, 0, 0 }, /* */ - /* PD8 */ { 0, 0, 0, 0, 0, 0, 0 }, /* */ - /* PD7 */ { 0, 0, 0, 0, 0, 0, 0 }, /* */ - /* PD6 */ { 0, 0, 0, 0, 0, 0, 0 }, /* */ - /* PD5 */ { 0, 0, 0, 0, 0, 0, 0 }, /* */ - /* PD4 */ { 0, 0, 0, 0, 0, 0, 0 }, /* */ - /* PD3 */ { 0, 0, 0, 0, 0, 0, 0 }, /* */ - /* N/A */ { 0, 0, 0, 0, 0, 0, 0 }, /* */ - /* N/A */ { 0, 0, 0, 0, 0, 0, 0 }, /* */ - /* N/A */ { 0, 0, 0, 0, 0, 0, 0 } /* */ - } -}; - -/* - * Configure the MPC8XX I/O ports per the ioport configuration table - * (taken from ./arch/powerpc/cpu/mpc8260/cpu_init.c) - */ -void config_mpc8xx_ioports (volatile immap_t * immr) -{ - int portnum; - - for (portnum = 0; portnum < NUM_PORTS; portnum++) { - uint pmsk = 0, ppar = 0, psor = 0, pdir = 0; - uint podr = 0, pdat = 0, pint = 0; - uint msk = 1; - mpc8xx_iop_conf_t *iopc = - (mpc8xx_iop_conf_t *) & iop_conf_tab[portnum][0]; - mpc8xx_iop_conf_t *eiopc = iopc + PORT_BITS; - - /* - * For all ports except port B, ignore the two don't care entries - * in the configuration tables. - */ - if (portnum != 1) { - iopc = (mpc8xx_iop_conf_t *) & - iop_conf_tab[portnum][2]; - } - - /* - * NOTE: index 0 refers to pin 17, index 17 refers to pin 0 - */ - while (iopc < eiopc) { - if (iopc->conf) { - pmsk |= msk; - if (iopc->ppar) - ppar |= msk; - if (iopc->psor) - psor |= msk; - if (iopc->pdir) - pdir |= msk; - if (iopc->podr) - podr |= msk; - if (iopc->pdat) - pdat |= msk; - if (iopc->pint) - pint |= msk; - } - msk <<= 1; - iopc++; - } - - PRINTF ("%s:%d:\n portnum=%d ", __FUNCTION__, __LINE__, - portnum); -#ifdef IOPORT_DEBUG - switch (portnum) { - case 0: - printf ("(A)\n"); - break; - case 1: - printf ("(B)\n"); - break; - case 2: - printf ("(C)\n"); - break; - case 3: - printf ("(D)\n"); - break; - default: - printf ("(?)\n"); - break; - } -#endif - PRINTF (" ppar=0x%.8x pdir=0x%.8x podr=0x%.8x\n" - " pdat=0x%.8x psor=0x%.8x pint=0x%.8x pmsk=0x%.8x\n", - ppar, pdir, podr, pdat, psor, pint, pmsk); - - /* - * Have to handle the ioports on a port-by-port basis since there - * are three different flavors. - */ - if (pmsk != 0) { - uint tpmsk = ~pmsk; - - if (0 == portnum) { /* port A */ - immr->im_ioport.iop_papar &= tpmsk; - immr->im_ioport.iop_padat = - (immr->im_ioport. - iop_padat & tpmsk) | pdat; - immr->im_ioport.iop_padir = - (immr->im_ioport. - iop_padir & tpmsk) | pdir; - immr->im_ioport.iop_paodr = - (immr->im_ioport. - iop_paodr & tpmsk) | podr; - immr->im_ioport.iop_papar |= ppar; - } else if (1 == portnum) { /* port B */ - immr->im_cpm.cp_pbpar &= tpmsk; - immr->im_cpm.cp_pbdat = - (immr->im_cpm. - cp_pbdat & tpmsk) | pdat; - immr->im_cpm.cp_pbdir = - (immr->im_cpm. - cp_pbdir & tpmsk) | pdir; - immr->im_cpm.cp_pbodr = - (immr->im_cpm. - cp_pbodr & tpmsk) | podr; - immr->im_cpm.cp_pbpar |= ppar; - } else if (2 == portnum) { /* port C */ - immr->im_ioport.iop_pcpar &= tpmsk; - immr->im_ioport.iop_pcdat = - (immr->im_ioport. - iop_pcdat & tpmsk) | pdat; - immr->im_ioport.iop_pcdir = - (immr->im_ioport. - iop_pcdir & tpmsk) | pdir; - immr->im_ioport.iop_pcint = - (immr->im_ioport. - iop_pcint & tpmsk) | pint; - immr->im_ioport.iop_pcso = - (immr->im_ioport. - iop_pcso & tpmsk) | psor; - immr->im_ioport.iop_pcpar |= ppar; - } else if (3 == portnum) { /* port D */ - immr->im_ioport.iop_pdpar &= tpmsk; - immr->im_ioport.iop_pddat = - (immr->im_ioport. - iop_pddat & tpmsk) | pdat; - immr->im_ioport.iop_pddir = - (immr->im_ioport. - iop_pddir & tpmsk) | pdir; - immr->im_ioport.iop_pdpar |= ppar; - } - } - } - - PRINTF ("%s:%d: Port A:\n papar=0x%.4x padir=0x%.4x" - " paodr=0x%.4x\n padat=0x%.4x\n", __FUNCTION__, __LINE__, - immr->im_ioport.iop_papar, immr->im_ioport.iop_padir, - immr->im_ioport.iop_paodr, immr->im_ioport.iop_padat); - PRINTF ("%s:%d: Port B:\n pbpar=0x%.8x pbdir=0x%.8x" - " pbodr=0x%.8x\n pbdat=0x%.8x\n", __FUNCTION__, __LINE__, - immr->im_cpm.cp_pbpar, immr->im_cpm.cp_pbdir, - immr->im_cpm.cp_pbodr, immr->im_cpm.cp_pbdat); - PRINTF ("%s:%d: Port C:\n pcpar=0x%.4x pcdir=0x%.4x" - " pcdat=0x%.4x\n pcso=0x%.4x pcint=0x%.4x\n ", - __FUNCTION__, __LINE__, immr->im_ioport.iop_pcpar, - immr->im_ioport.iop_pcdir, immr->im_ioport.iop_pcdat, - immr->im_ioport.iop_pcso, immr->im_ioport.iop_pcint); - PRINTF ("%s:%d: Port D:\n pdpar=0x%.4x pddir=0x%.4x" - " pddat=0x%.4x\n", __FUNCTION__, __LINE__, - immr->im_ioport.iop_pdpar, immr->im_ioport.iop_pddir, - immr->im_ioport.iop_pddat); -} diff --git a/board/gen860t/ioport.h b/board/gen860t/ioport.h deleted file mode 100644 index 4ac2aa4..0000000 --- a/board/gen860t/ioport.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * (C) Copyright 2000 - * Wolfgang Denk, DENX Software Engineering, wd@denx.de. - * Keith Outwater, keith_outwater@mvis.com - * - * SPDX-License-Identifier: GPL-2.0+ - */ - -#define NUM_PORTS 4 -#define PORT_BITS 18 - -/* - * This structure provides configuration information for one port pin. - * We include all fields needed to initialize any of the ioports. - */ -typedef struct { - unsigned char conf:1; /* If 1, configure this port */ - unsigned char ppar:1; /* Port Pin Assignment Register */ - unsigned char psor:1; /* Port Special Options Register */ - unsigned char pdir:1; /* Port Data Direction Register */ - unsigned char podr:1; /* Port Open Drain Register */ - unsigned char pdat:1; /* Port Data Register */ - unsigned char pint:1; /* Port Interrupt Register */ -} mpc8xx_iop_conf_t; - -extern void config_mpc8xx_ioports(volatile immap_t *immr); diff --git a/board/gen860t/u-boot-flashenv.lds b/board/gen860t/u-boot-flashenv.lds deleted file mode 100644 index 7a4a763..0000000 --- a/board/gen860t/u-boot-flashenv.lds +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Linker command file for the GEN860T board when the environment is - * stored in flash memory. - * - * (C) Copyright 2000-2010 - * Wolfgang Denk, DENX Software Engineering, wd@denx.de. - * - * SPDX-License-Identifier: GPL-2.0+ - */ - -OUTPUT_ARCH(powerpc) -SECTIONS -{ - /* - * Read-only sections, merged into text segment: - */ - . = + SIZEOF_HEADERS; - .text : - { - arch/powerpc/cpu/mpc8xx/start.o (.text*) - arch/powerpc/cpu/mpc8xx/traps.o (.text*) - - *(.text*) - } - _etext = .; - PROVIDE (etext = .); - .rodata : - { - *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) - } - - /* - * Read-write section, merged into data segment: - */ - . = (. + 0x00FF) & 0xFFFFFF00; - _erotext = .; - PROVIDE (erotext = .); - .reloc : - { - KEEP(*(.got)) - _GOT2_TABLE_ = .; - KEEP(*(.got2)) - _FIXUP_TABLE_ = .; - KEEP(*(.fixup)) - } - __got2_entries = (_FIXUP_TABLE_ - _GOT2_TABLE_) >>2; - __fixup_entries = (. - _FIXUP_TABLE_)>>2; - - .data: - { - *(.data*) - *(.sdata*) - } - _edata = .; - PROVIDE (edata = .); - - . = .; - - .u_boot_list : { - KEEP(*(SORT(.u_boot_list*))); - } - - . = .; - __start___ex_table = .; - __ex_table : { *(__ex_table) } - __stop___ex_table = .; - - . = ALIGN(256); - __init_begin = .; - .text.init : { *(.text.init) } - .data.init : { *(.data.init) } - . = ALIGN(256); - __init_end = .; - - __bss_start = .; - .bss (NOLOAD) : - { - *(.bss*) - *(.sbss*) - *(COMMON) - . = ALIGN(4); - } - - __bss_end = . ; - PROVIDE (end = .); - - .ppcenv: - { - . = env_offset; - common/env_embedded.o - } -} diff --git a/board/gen860t/u-boot.lds b/board/gen860t/u-boot.lds deleted file mode 100644 index 3371c0a..0000000 --- a/board/gen860t/u-boot.lds +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Linker command file for the GEN860T board. - * - * (C) Copyright 2000-2010 - * Wolfgang Denk, DENX Software Engineering, wd@denx.de. - * - * SPDX-License-Identifier: GPL-2.0+ - */ - -OUTPUT_ARCH(powerpc) -SECTIONS -{ - /* - * Read-only sections, merged into text segment: - */ - . = + SIZEOF_HEADERS; - .text : - { - arch/powerpc/cpu/mpc8xx/start.o (.text*) - arch/powerpc/cpu/mpc8xx/traps.o (.text*) - - *(.text*) - } - _etext = .; - PROVIDE (etext = .); - .rodata : - { - *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) - } - - /* - * Read-write section, merged into data segment: - */ - . = (. + 0x00FF) & 0xFFFFFF00; - _erotext = .; - PROVIDE (erotext = .); - .reloc : - { - _GOT2_TABLE_ = .; - KEEP(*(.got2)) - KEEP(*(.got)) - PROVIDE(_GLOBAL_OFFSET_TABLE_ = . + 4); - _FIXUP_TABLE_ = .; - KEEP(*(.fixup)) - } - __got2_entries = ((_GLOBAL_OFFSET_TABLE_ - _GOT2_TABLE_) >> 2) - 1; - __fixup_entries = (. - _FIXUP_TABLE_)>>2; - - .data : - { - *(.data*) - *(.sdata*) - } - _edata = .; - PROVIDE (edata = .); - - . = .; - - . = ALIGN(4); - .u_boot_list : { - KEEP(*(SORT(.u_boot_list*))); - } - - - . = .; - __start___ex_table = .; - __ex_table : { *(__ex_table) } - __stop___ex_table = .; - - . = ALIGN(256); - __init_begin = .; - .text.init : { *(.text.init) } - .data.init : { *(.data.init) } - . = ALIGN(256); - __init_end = .; - - __bss_start = .; - .bss (NOLOAD) : - { - *(.bss*) - *(.sbss*) - *(COMMON) - . = ALIGN(4); - } - __bss_end = . ; - PROVIDE (end = .); -} diff --git a/board/keymile/kmp204x/kmp204x.c b/board/keymile/kmp204x/kmp204x.c index cd08379..4a73613 100644 --- a/board/keymile/kmp204x/kmp204x.c +++ b/board/keymile/kmp204x/kmp204x.c @@ -108,8 +108,8 @@ int board_early_init_f(void) /* and enable WD on it */ qrio_wdmask(BFTIC4_RST, true); - /* set the ZL30138's prstcfg to reset at power-up and unit reset only */ - qrio_prstcfg(ZL30158_RST, PRSTCFG_POWUP_UNIT_RST); + /* set the ZL30138's prstcfg to reset at power-up only */ + qrio_prstcfg(ZL30158_RST, PRSTCFG_POWUP_RST); /* and take it out of reset as soon as possible (needed for Hooper) */ qrio_prst(ZL30158_RST, false, false); @@ -158,8 +158,8 @@ int misc_init_f(void) qrio_prstcfg(ETH_FRONT_PHY_RST, PRSTCFG_POWUP_UNIT_CORE_RST); qrio_prst(ETH_FRONT_PHY_RST, false, false); - /* set the ZL30343 prstcfg to reset at power-up and unit reset only */ - qrio_prstcfg(ZL30343_RST, PRSTCFG_POWUP_UNIT_RST); + /* set the ZL30343 prstcfg to reset at power-up only */ + qrio_prstcfg(ZL30343_RST, PRSTCFG_POWUP_RST); /* and enable the WD on it */ qrio_wdmask(ZL30343_RST, true); diff --git a/board/prodrive/alpr/nand.c b/board/prodrive/alpr/nand.c index 50e8d82..5427de5 100644 --- a/board/prodrive/alpr/nand.c +++ b/board/prodrive/alpr/nand.c @@ -93,6 +93,7 @@ static void alpr_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len) } } +#if defined(CONFIG_MTD_NAND_VERIFY_WRITE) static int alpr_nand_verify_buf(struct mtd_info *mtd, const u_char *buf, int len) { int i; @@ -103,6 +104,7 @@ static int alpr_nand_verify_buf(struct mtd_info *mtd, const u_char *buf, int len return 0; } +#endif static int alpr_nand_dev_ready(struct mtd_info *mtd) { @@ -128,7 +130,9 @@ int board_nand_init(struct nand_chip *nand) nand->read_byte = alpr_nand_read_byte; nand->write_buf = alpr_nand_write_buf; nand->read_buf = alpr_nand_read_buf; +#if defined(CONFIG_MTD_NAND_VERIFY_WRITE) nand->verify_buf = alpr_nand_verify_buf; +#endif nand->dev_ready = alpr_nand_dev_ready; return 0; diff --git a/board/psyent/common/AMDLV065D.c b/board/psyent/common/AMDLV065D.c deleted file mode 100644 index 64cb970..0000000 --- a/board/psyent/common/AMDLV065D.c +++ /dev/null @@ -1,170 +0,0 @@ -/* - * (C) Copyright 2000-2004 - * Wolfgang Denk, DENX Software Engineering, wd@denx.de. - * - * SPDX-License-Identifier: GPL-2.0+ - */ - - -#include <common.h> -#include <asm/io.h> - -#define SECTSZ (64 * 1024) -flash_info_t flash_info[CONFIG_SYS_MAX_FLASH_BANKS]; - -/*----------------------------------------------------------------------*/ -unsigned long flash_init (void) -{ - int i; - unsigned long addr; - flash_info_t *fli = &flash_info[0]; - - fli->size = CONFIG_SYS_FLASH_SIZE; - fli->sector_count = CONFIG_SYS_MAX_FLASH_SECT; - fli->flash_id = FLASH_MAN_AMD + FLASH_AMDLV065D; - - addr = CONFIG_SYS_FLASH_BASE; - for (i = 0; i < fli->sector_count; ++i) { - fli->start[i] = addr; - addr += SECTSZ; - fli->protect[i] = 1; - } - - return (CONFIG_SYS_FLASH_SIZE); -} -/*--------------------------------------------------------------------*/ -void flash_print_info (flash_info_t * info) -{ - int i, k; - int erased; - unsigned long *addr; - - printf (" Size: %ld KB in %d Sectors\n", - info->size >> 10, info->sector_count); - printf (" Sector Start Addresses:"); - for (i = 0; i < info->sector_count; ++i) { - - /* Check if whole sector is erased */ - erased = 1; - addr = (unsigned long *) info->start[i]; - for (k = 0; k < SECTSZ/sizeof(unsigned long); k++) { - if ( readl(addr++) != (unsigned long)-1) { - erased = 0; - break; - } - } - - /* Print the info */ - if ((i % 5) == 0) - printf ("\n "); - printf (" %08lX%s%s", - info->start[i], - erased ? " E" : " ", - info->protect[i] ? "RO " : " "); - } - printf ("\n"); -} - -/*-------------------------------------------------------------------*/ - - -int flash_erase (flash_info_t * info, int s_first, int s_last) -{ - unsigned char *addr = (unsigned char *) info->start[0]; - unsigned char *addr2; - int prot, sect; - ulong start; - - /* Some sanity checking */ - if ((s_first < 0) || (s_first > s_last)) { - 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"); - } - - /* It's ok to erase multiple sectors provided we don't delay more - * than 50 usec between cmds ... at which point the erase time-out - * occurs. So don't go and put printf() calls in the loop ... it - * won't be very helpful ;-) - */ - for (sect = s_first; sect <= s_last; sect++) { - if (info->protect[sect] == 0) { /* not protected */ - addr2 = (unsigned char *) info->start[sect]; - writeb (0xaa, addr); - writeb (0x55, addr); - writeb (0x80, addr); - writeb (0xaa, addr); - writeb (0x55, addr); - writeb (0x30, addr2); - /* Now just wait for 0xff & provide some user - * feedback while we wait. - */ - start = get_timer (0); - while ( readb (addr2) != 0xff) { - udelay (1000 * 1000); - putc ('.'); - if (get_timer (start) > CONFIG_SYS_FLASH_ERASE_TOUT) { - printf ("timeout\n"); - return 1; - } - } - } - } - printf ("\n"); - return 0; -} - -/*----------------------------------------------------------------------- - * 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) -{ - - vu_char *cmd = (vu_char *) info->start[0]; - vu_char *dst = (vu_char *) addr; - unsigned char b; - ulong start; - - while (cnt) { - /* Check for sufficient erase */ - b = *src; - if ((readb (dst) & b) != b) { - printf ("%02x : %02x\n", readb (dst), b); - return (2); - } - - writeb (0xaa, cmd); - writeb (0x55, cmd); - writeb (0xa0, cmd); - writeb (dst, b); - - /* Verify write */ - start = get_timer (0); - while (readb (dst) != b) { - if (get_timer (start) > CONFIG_SYS_FLASH_WRITE_TOUT) { - return 1; - } - } - dst++; - src++; - cnt--; - } - - return (0); -} diff --git a/board/psyent/pci5441/Kconfig b/board/psyent/pci5441/Kconfig deleted file mode 100644 index d722f31..0000000 --- a/board/psyent/pci5441/Kconfig +++ /dev/null @@ -1,15 +0,0 @@ -if TARGET_PCI5441 - -config SYS_BOARD - string - default "pci5441" - -config SYS_VENDOR - string - default "psyent" - -config SYS_CONFIG_NAME - string - default "PCI5441" - -endif diff --git a/board/psyent/pci5441/MAINTAINERS b/board/psyent/pci5441/MAINTAINERS deleted file mode 100644 index f1f10e9..0000000 --- a/board/psyent/pci5441/MAINTAINERS +++ /dev/null @@ -1,6 +0,0 @@ -PCI5441 BOARD -M: Scott McNutt <smcnutt@psyent.com> -S: Maintained -F: board/psyent/pci5441/ -F: include/configs/PCI5441.h -F: configs/PCI5441_defconfig diff --git a/board/psyent/pci5441/Makefile b/board/psyent/pci5441/Makefile deleted file mode 100644 index 364f163..0000000 --- a/board/psyent/pci5441/Makefile +++ /dev/null @@ -1,8 +0,0 @@ -# -# (C) Copyright 2001-2006 -# Wolfgang Denk, DENX Software Engineering, wd@denx.de. -# -# SPDX-License-Identifier: GPL-2.0+ -# - -obj-y := pci5441.o ../common/AMDLV065D.o diff --git a/board/psyent/pci5441/config.mk b/board/psyent/pci5441/config.mk deleted file mode 100644 index 776fa8a..0000000 --- a/board/psyent/pci5441/config.mk +++ /dev/null @@ -1,14 +0,0 @@ -# -# (C) Copyright 2004, Psyent Corporation <www.psyent.com> -# Scott McNutt <smcnutt@psyent.com> -# -# SPDX-License-Identifier: GPL-2.0+ -# - -CONFIG_SYS_TEXT_BASE = 0x018e0000 - -PLATFORM_CPPFLAGS += -mno-hw-div -mno-hw-mul - -ifeq ($(debug),1) -PLATFORM_CPPFLAGS += -DDEBUG -endif diff --git a/board/psyent/pci5441/pci5441.c b/board/psyent/pci5441/pci5441.c deleted file mode 100644 index 6d619e5..0000000 --- a/board/psyent/pci5441/pci5441.c +++ /dev/null @@ -1,24 +0,0 @@ -/* - * (C) Copyright 2004, Psyent Corporation <www.psyent.com> - * Scott McNutt <smcnutt@psyent.com> - * - * SPDX-License-Identifier: GPL-2.0+ - */ - -#include <common.h> - -int board_early_init_f (void) -{ - return 0; -} - -int checkboard (void) -{ - puts ("BOARD : Psyent PCI-5441\n"); - return 0; -} - -phys_size_t initdram (int board_type) -{ - return (0); -} diff --git a/board/psyent/pk1c20/Kconfig b/board/psyent/pk1c20/Kconfig deleted file mode 100644 index 75f6cd1..0000000 --- a/board/psyent/pk1c20/Kconfig +++ /dev/null @@ -1,15 +0,0 @@ -if TARGET_PK1C20 - -config SYS_BOARD - string - default "pk1c20" - -config SYS_VENDOR - string - default "psyent" - -config SYS_CONFIG_NAME - string - default "PK1C20" - -endif diff --git a/board/psyent/pk1c20/MAINTAINERS b/board/psyent/pk1c20/MAINTAINERS deleted file mode 100644 index 32b901a..0000000 --- a/board/psyent/pk1c20/MAINTAINERS +++ /dev/null @@ -1,6 +0,0 @@ -PK1C20 BOARD -M: Scott McNutt <smcnutt@psyent.com> -S: Maintained -F: board/psyent/pk1c20/ -F: include/configs/PK1C20.h -F: configs/PK1C20_defconfig diff --git a/board/psyent/pk1c20/Makefile b/board/psyent/pk1c20/Makefile deleted file mode 100644 index 5450f93..0000000 --- a/board/psyent/pk1c20/Makefile +++ /dev/null @@ -1,8 +0,0 @@ -# -# (C) Copyright 2001-2006 -# Wolfgang Denk, DENX Software Engineering, wd@denx.de. -# -# SPDX-License-Identifier: GPL-2.0+ -# - -obj-y := pk1c20.o led.o ../common/AMDLV065D.o diff --git a/board/psyent/pk1c20/config.mk b/board/psyent/pk1c20/config.mk deleted file mode 100644 index 83cfadc..0000000 --- a/board/psyent/pk1c20/config.mk +++ /dev/null @@ -1,14 +0,0 @@ -# -# (C) Copyright 2004, Psyent Corporation <www.psyent.com> -# Scott McNutt <smcnutt@psyent.com> -# -# SPDX-License-Identifier: GPL-2.0+ -# - -CONFIG_SYS_TEXT_BASE = 0x01fc0000 - -PLATFORM_CPPFLAGS += -mno-hw-div -mno-hw-mul - -ifeq ($(debug),1) -PLATFORM_CPPFLAGS += -DDEBUG -endif diff --git a/board/psyent/pk1c20/led.c b/board/psyent/pk1c20/led.c deleted file mode 100644 index 580d590..0000000 --- a/board/psyent/pk1c20/led.c +++ /dev/null @@ -1,46 +0,0 @@ -/* - * (C) Copyright 2004, Psyent Corporation <www.psyent.com> - * Scott McNutt <smcnutt@psyent.com> - * - * SPDX-License-Identifier: GPL-2.0+ - */ - -#include <common.h> -#include <asm/io.h> -#include <nios2-io.h> -#include <status_led.h> - -/* The LED port is configured as output only, so we - * must track the state manually. - */ -static led_id_t val = 0; - -void __led_init (led_id_t mask, int state) -{ - nios_pio_t *pio = (nios_pio_t *)CONFIG_SYS_LEDPIO_ADDR; - - if (state == STATUS_LED_ON) - val &= ~mask; - else - val |= mask; - writel (val, &pio->data); -} - -void __led_set (led_id_t mask, int state) -{ - nios_pio_t *pio = (nios_pio_t *)CONFIG_SYS_LEDPIO_ADDR; - - if (state == STATUS_LED_ON) - val &= ~mask; - else - val |= mask; - writel (val, &pio->data); -} - -void __led_toggle (led_id_t mask) -{ - nios_pio_t *pio = (nios_pio_t *)CONFIG_SYS_LEDPIO_ADDR; - - val ^= mask; - writel (val, &pio->data); -} diff --git a/board/psyent/pk1c20/pk1c20.c b/board/psyent/pk1c20/pk1c20.c deleted file mode 100644 index 0b4c9f8..0000000 --- a/board/psyent/pk1c20/pk1c20.c +++ /dev/null @@ -1,36 +0,0 @@ -/* - * (C) Copyright 2004, Psyent Corporation <www.psyent.com> - * Scott McNutt <smcnutt@psyent.com> - * - * SPDX-License-Identifier: GPL-2.0+ - */ - -#include <common.h> -#include <netdev.h> - -int board_early_init_f (void) -{ - return 0; -} - -int checkboard (void) -{ - puts ("BOARD : Psyent PK-1C20\n"); - return 0; -} - -phys_size_t initdram (int board_type) -{ - return (0); -} - -#ifdef CONFIG_CMD_NET -int board_eth_init(bd_t *bis) -{ - int rc = 0; -#ifdef CONFIG_SMC91111 - rc = smc91111_initialize(0, CONFIG_SMC91111_BASE); -#endif - return rc; -} -#endif diff --git a/board/samsung/common/Makefile b/board/samsung/common/Makefile index 41d0cc3..93347ef 100644 --- a/board/samsung/common/Makefile +++ b/board/samsung/common/Makefile @@ -6,7 +6,7 @@ # obj-$(CONFIG_SOFT_I2C_MULTI_BUS) += multi_i2c.o -obj-$(CONFIG_THOR_FUNCTION) += thor.o +obj-$(CONFIG_USBDOWNLOAD_GADGET) += gadget.o obj-$(CONFIG_MISC_COMMON) += misc.o ifndef CONFIG_SPL_BUILD diff --git a/board/samsung/common/thor.c b/board/samsung/common/gadget.c index 1c7630d..6a1e57f 100644 --- a/board/samsung/common/thor.c +++ b/board/samsung/common/gadget.c @@ -13,6 +13,9 @@ int g_dnl_bind_fixup(struct usb_device_descriptor *dev, const char *name) if (!strcmp(name, "usb_dnl_thor")) { put_unaligned(CONFIG_G_DNL_THOR_VENDOR_NUM, &dev->idVendor); put_unaligned(CONFIG_G_DNL_THOR_PRODUCT_NUM, &dev->idProduct); + } else if (!strcmp(name, "usb_dnl_ums")) { + put_unaligned(CONFIG_G_DNL_UMS_VENDOR_NUM, &dev->idVendor); + put_unaligned(CONFIG_G_DNL_UMS_PRODUCT_NUM, &dev->idProduct); } else { put_unaligned(CONFIG_G_DNL_VENDOR_NUM, &dev->idVendor); put_unaligned(CONFIG_G_DNL_PRODUCT_NUM, &dev->idProduct); diff --git a/board/sixnet/Kconfig b/board/sixnet/Kconfig deleted file mode 100644 index 2c1b995..0000000 --- a/board/sixnet/Kconfig +++ /dev/null @@ -1,11 +0,0 @@ -if TARGET_SXNI855T - -config SYS_BOARD - string - default "sixnet" - -config SYS_CONFIG_NAME - string - default "SXNI855T" - -endif diff --git a/board/sixnet/MAINTAINERS b/board/sixnet/MAINTAINERS deleted file mode 100644 index eedb409..0000000 --- a/board/sixnet/MAINTAINERS +++ /dev/null @@ -1,6 +0,0 @@ -SIXNET BOARD -M: Dave Ellis <DGE@sixnetio.com> -S: Orphan (since 2014-06) -F: board/sixnet/ -F: include/configs/SXNI855T.h -F: configs/SXNI855T_defconfig diff --git a/board/sixnet/Makefile b/board/sixnet/Makefile deleted file mode 100644 index 25a8d69..0000000 --- a/board/sixnet/Makefile +++ /dev/null @@ -1,8 +0,0 @@ -# -# (C) Copyright 2000-2006 -# Wolfgang Denk, DENX Software Engineering, wd@denx.de. -# -# SPDX-License-Identifier: GPL-2.0+ -# - -obj-y = sixnet.o flash.o diff --git a/board/sixnet/flash.c b/board/sixnet/flash.c deleted file mode 100644 index 75bc3eb..0000000 --- a/board/sixnet/flash.c +++ /dev/null @@ -1,774 +0,0 @@ -/* - * (C) Copyright 2000 - * Wolfgang Denk, DENX Software Engineering, wd@denx.de. - * - * SPDX-License-Identifier: GPL-2.0+ - */ - -#include <common.h> -#include <mpc8xx.h> -/* environment.h defines the various CONFIG_ENV_... values in terms - * of whichever ones are given in the configuration file. - */ -#include <environment.h> - -flash_info_t flash_info[CONFIG_SYS_MAX_FLASH_BANKS]; /* info for FLASH chips */ - -/* NOTE - CONFIG_FLASH_16BIT means the CPU interface is 16-bit, it - * has nothing to do with the flash chip being 8-bit or 16-bit. - */ -#ifdef CONFIG_FLASH_16BIT -typedef unsigned short FLASH_PORT_WIDTH; -typedef volatile unsigned short FLASH_PORT_WIDTHV; -#define FLASH_ID_MASK 0xFFFF -#else -typedef unsigned long FLASH_PORT_WIDTH; -typedef volatile unsigned long FLASH_PORT_WIDTHV; -#define FLASH_ID_MASK 0xFFFFFFFF -#endif - -#define FPW FLASH_PORT_WIDTH -#define FPWV FLASH_PORT_WIDTHV - -#define ORMASK(size) ((-size) & OR_AM_MSK) - -/*----------------------------------------------------------------------- - * Functions - */ -static ulong flash_get_size(FPWV *addr, flash_info_t *info); -static void flash_reset(flash_info_t *info); -static int write_word_intel(flash_info_t *info, FPWV *dest, FPW data); -static int write_word_amd(flash_info_t *info, FPWV *dest, FPW data); -static void flash_get_offsets(ulong base, flash_info_t *info); -#ifdef CONFIG_SYS_FLASH_PROTECTION -static void flash_sync_real_protect(flash_info_t *info); -#endif - -/*----------------------------------------------------------------------- - * flash_init() - * - * sets up flash_info and returns size of FLASH (bytes) - */ -unsigned long flash_init (void) -{ - volatile immap_t *immap = (immap_t *)CONFIG_SYS_IMMR; - volatile memctl8xx_t *memctl = &immap->im_memctl; - unsigned long size_b; - int i; - - /* Init: no FLASHes known */ - for (i=0; i < CONFIG_SYS_MAX_FLASH_BANKS; ++i) { - flash_info[i].flash_id = FLASH_UNKNOWN; - } - - size_b = flash_get_size((FPW *)CONFIG_SYS_FLASH_BASE, &flash_info[0]); - - flash_info[0].size = size_b; - - if (flash_info[0].flash_id == FLASH_UNKNOWN) { - printf ("## Unknown FLASH on Bank 0 - Size = 0x%08lx\n",size_b); - } - - /* Remap FLASH according to real size, so only at proper address */ - memctl->memc_or0 = (memctl->memc_or0 & ~OR_AM_MSK) | ORMASK(size_b); - - /* Do this again (was done already in flast_get_size), just - * in case we move it when remap the FLASH. - */ - flash_get_offsets (CONFIG_SYS_FLASH_BASE, &flash_info[0]); - -#ifdef CONFIG_SYS_FLASH_PROTECTION - /* read the hardware protection status (if any) into the - * protection array in flash_info. - */ - flash_sync_real_protect(&flash_info[0]); -#endif - -#if CONFIG_SYS_MONITOR_BASE >= CONFIG_SYS_FLASH_BASE - /* monitor protection ON by default */ - flash_protect(FLAG_PROTECT_SET, - CONFIG_SYS_MONITOR_BASE, - CONFIG_SYS_MONITOR_BASE+monitor_flash_len-1, - &flash_info[0]); -#endif - -#ifdef CONFIG_ENV_ADDR - flash_protect ( FLAG_PROTECT_SET, - CONFIG_ENV_ADDR, - CONFIG_ENV_ADDR + CONFIG_ENV_SECT_SIZE - 1, &flash_info[0]); -#endif - -#ifdef CONFIG_ENV_ADDR_REDUND - flash_protect ( FLAG_PROTECT_SET, - CONFIG_ENV_ADDR_REDUND, - CONFIG_ENV_ADDR_REDUND + CONFIG_ENV_SECT_SIZE - 1, - &flash_info[0]); -#endif - - return (size_b); -} - -/*----------------------------------------------------------------------- - */ -static void flash_reset(flash_info_t *info) -{ - FPWV *base = (FPWV *)(info->start[0]); - - /* Put FLASH back in read mode */ - if ((info->flash_id & FLASH_VENDMASK) == FLASH_MAN_INTEL) - *base = (FPW)0x00FF00FF; /* Intel Read Mode */ - else if ((info->flash_id & FLASH_VENDMASK) == FLASH_MAN_AMD) - *base = (FPW)0x00F000F0; /* AMD Read Mode */ -} - -/*----------------------------------------------------------------------- - */ -static void flash_get_offsets (ulong base, flash_info_t *info) -{ - int i; - - /* set up sector start address table */ - if ((info->flash_id & FLASH_VENDMASK) == FLASH_MAN_INTEL - && (info->flash_id & FLASH_BTYPE)) { - int bootsect_size; /* number of bytes/boot sector */ - int sect_size; /* number of bytes/regular sector */ - - bootsect_size = 0x00002000 * (sizeof(FPW)/2); - sect_size = 0x00010000 * (sizeof(FPW)/2); - - /* set sector offsets for bottom boot block type */ - for (i = 0; i < 8; ++i) { - info->start[i] = base + (i * bootsect_size); - } - for (i = 8; i < info->sector_count; i++) { - info->start[i] = base + ((i - 7) * sect_size); - } - } - else if ((info->flash_id & FLASH_VENDMASK) == FLASH_MAN_AMD - && (info->flash_id & FLASH_TYPEMASK) == FLASH_AM640U) { - - int sect_size; /* number of bytes/sector */ - - sect_size = 0x00010000 * (sizeof(FPW)/2); - - /* set up sector start address table (uniform sector type) */ - for( i = 0; i < info->sector_count; i++ ) - info->start[i] = base + (i * sect_size); - } - else if ((info->flash_id & FLASH_VENDMASK) == FLASH_MAN_AMD - && (info->flash_id & FLASH_TYPEMASK) == FLASH_AM800T) { - - int sect_size; /* number of bytes/sector */ - - sect_size = 0x00010000 * (sizeof(FPW)/2); - - /* set up sector start address table (top boot sector type) */ - for (i = 0; i < info->sector_count - 3; i++) - info->start[i] = base + (i * sect_size); - i = info->sector_count - 1; - info->start[i--] = base + (info->size - 0x00004000) * (sizeof(FPW)/2); - info->start[i--] = base + (info->size - 0x00006000) * (sizeof(FPW)/2); - info->start[i--] = base + (info->size - 0x00008000) * (sizeof(FPW)/2); - } -} - -/*----------------------------------------------------------------------- - */ - -void flash_print_info (flash_info_t *info) -{ - int i; - uchar *boottype; - uchar *bootletter; - char *fmt; - uchar botbootletter[] = "B"; - uchar topbootletter[] = "T"; - uchar botboottype[] = "bottom boot sector"; - uchar topboottype[] = "top boot sector"; - - 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_BM: printf ("BRIGHT MICRO "); break; - case FLASH_MAN_FUJ: printf ("FUJITSU "); break; - case FLASH_MAN_SST: printf ("SST "); break; - case FLASH_MAN_STM: printf ("STM "); break; - case FLASH_MAN_INTEL: printf ("INTEL "); break; - default: printf ("Unknown Vendor "); break; - } - - /* check for top or bottom boot, if it applies */ - if (info->flash_id & FLASH_BTYPE) { - boottype = botboottype; - bootletter = botbootletter; - } - else { - boottype = topboottype; - bootletter = topbootletter; - } - - switch (info->flash_id & FLASH_TYPEMASK) { - case FLASH_AM800T: - fmt = "29LV800B%s (8 Mbit, %s)\n"; - break; - case FLASH_AM640U: - fmt = "29LV641D (64 Mbit, uniform sectors)\n"; - break; - case FLASH_28F800C3B: - case FLASH_28F800C3T: - fmt = "28F800C3%s (8 Mbit, %s)\n"; - break; - case FLASH_INTEL800B: - case FLASH_INTEL800T: - fmt = "28F800B3%s (8 Mbit, %s)\n"; - break; - case FLASH_28F160C3B: - case FLASH_28F160C3T: - fmt = "28F160C3%s (16 Mbit, %s)\n"; - break; - case FLASH_INTEL160B: - case FLASH_INTEL160T: - fmt = "28F160B3%s (16 Mbit, %s)\n"; - break; - case FLASH_28F320C3B: - case FLASH_28F320C3T: - fmt = "28F320C3%s (32 Mbit, %s)\n"; - break; - case FLASH_INTEL320B: - case FLASH_INTEL320T: - fmt = "28F320B3%s (32 Mbit, %s)\n"; - break; - case FLASH_28F640C3B: - case FLASH_28F640C3T: - fmt = "28F640C3%s (64 Mbit, %s)\n"; - break; - case FLASH_INTEL640B: - case FLASH_INTEL640T: - fmt = "28F640B3%s (64 Mbit, %s)\n"; - break; - default: - fmt = "Unknown Chip Type\n"; - break; - } - - printf (fmt, bootletter, boottype); - - 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"); -} - -/*----------------------------------------------------------------------- - */ - -/* - * The following code cannot be run from FLASH! - */ - -ulong flash_get_size (FPWV *addr, flash_info_t *info) -{ - /* Write auto select command: read Manufacturer ID */ - - /* Write auto select command sequence and test FLASH answer */ - addr[0x0555] = (FPW)0x00AA00AA; /* for AMD, Intel ignores this */ - addr[0x02AA] = (FPW)0x00550055; /* for AMD, Intel ignores this */ - addr[0x0555] = (FPW)0x00900090; /* selects Intel or AMD */ - - /* The manufacturer codes are only 1 byte, so just use 1 byte. - * This works for any bus width and any FLASH device width. - */ - switch (addr[0] & 0xff) { - - case (uchar)AMD_MANUFACT: - info->flash_id = FLASH_MAN_AMD; - break; - - case (uchar)INTEL_MANUFACT: - info->flash_id = FLASH_MAN_INTEL; - break; - - default: - info->flash_id = FLASH_UNKNOWN; - info->sector_count = 0; - info->size = 0; - break; - } - - /* Check 16 bits or 32 bits of ID so work on 32 or 16 bit bus. */ - if (info->flash_id != FLASH_UNKNOWN) switch (addr[1]) { - - case (FPW)AMD_ID_LV800T: - info->flash_id += FLASH_AM800T; - info->sector_count = 19; - info->size = 0x00100000 * (sizeof(FPW)/2); - break; /* => 1 or 2 MiB */ - - case (FPW)AMD_ID_LV640U: /* 29LV640 and 29LV641 have same ID */ - info->flash_id += FLASH_AM640U; - info->sector_count = 128; - info->size = 0x00800000 * (sizeof(FPW)/2); - break; /* => 8 or 16 MB */ - - case (FPW)INTEL_ID_28F800C3B: - info->flash_id += FLASH_28F800C3B; - info->sector_count = 23; - info->size = 0x00100000 * (sizeof(FPW)/2); - break; /* => 1 or 2 MB */ - - case (FPW)INTEL_ID_28F800B3B: - info->flash_id += FLASH_INTEL800B; - info->sector_count = 23; - info->size = 0x00100000 * (sizeof(FPW)/2); - break; /* => 1 or 2 MB */ - - case (FPW)INTEL_ID_28F160C3B: - info->flash_id += FLASH_28F160C3B; - info->sector_count = 39; - info->size = 0x00200000 * (sizeof(FPW)/2); - break; /* => 2 or 4 MB */ - - case (FPW)INTEL_ID_28F160B3B: - info->flash_id += FLASH_INTEL160B; - info->sector_count = 39; - info->size = 0x00200000 * (sizeof(FPW)/2); - break; /* => 2 or 4 MB */ - - case (FPW)INTEL_ID_28F320C3B: - info->flash_id += FLASH_28F320C3B; - info->sector_count = 71; - info->size = 0x00400000 * (sizeof(FPW)/2); - break; /* => 4 or 8 MB */ - - case (FPW)INTEL_ID_28F320B3B: - info->flash_id += FLASH_INTEL320B; - info->sector_count = 71; - info->size = 0x00400000 * (sizeof(FPW)/2); - break; /* => 4 or 8 MB */ - - case (FPW)INTEL_ID_28F640C3B: - info->flash_id += FLASH_28F640C3B; - info->sector_count = 135; - info->size = 0x00800000 * (sizeof(FPW)/2); - break; /* => 8 or 16 MB */ - - case (FPW)INTEL_ID_28F640B3B: - info->flash_id += FLASH_INTEL640B; - info->sector_count = 135; - info->size = 0x00800000 * (sizeof(FPW)/2); - break; /* => 8 or 16 MB */ - - default: - info->flash_id = FLASH_UNKNOWN; - info->sector_count = 0; - info->size = 0; - return (0); /* => no or unknown flash */ - } - - flash_get_offsets((ulong)addr, info); - - /* Put FLASH back in read mode */ - flash_reset(info); - - return (info->size); -} - -#ifdef CONFIG_SYS_FLASH_PROTECTION -/*----------------------------------------------------------------------- - */ - -static void flash_sync_real_protect(flash_info_t *info) -{ - FPWV *addr = (FPWV *)(info->start[0]); - FPWV *sect; - int i; - - switch (info->flash_id & FLASH_TYPEMASK) { - case FLASH_28F800C3B: - case FLASH_28F800C3T: - case FLASH_28F160C3B: - case FLASH_28F160C3T: - case FLASH_28F320C3B: - case FLASH_28F320C3T: - case FLASH_28F640C3B: - case FLASH_28F640C3T: - /* check for protected sectors */ - *addr = (FPW)0x00900090; - for (i = 0; i < info->sector_count; i++) { - /* read sector protection at sector address, (A7 .. A0) = 0x02. - * D0 = 1 for each device if protected. - * If at least one device is protected the sector is marked - * protected, but mixed protected and unprotected devices - * within a sector should never happen. - */ - sect = (FPWV *)(info->start[i]); - info->protect[i] = (sect[2] & (FPW)(0x00010001)) ? 1 : 0; - } - - /* Put FLASH back in read mode */ - flash_reset(info); - break; - - case FLASH_AM640U: - case FLASH_AM800T: - default: - /* no hardware protect that we support */ - break; - } -} -#endif - -/*----------------------------------------------------------------------- - */ - -int flash_erase (flash_info_t *info, int s_first, int s_last) -{ - FPWV *addr; - int flag, prot, sect; - int intel = (info->flash_id & FLASH_VENDMASK) == FLASH_MAN_INTEL; - ulong start, now, last; - int rcode = 0; - - 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; - } - - switch (info->flash_id & FLASH_TYPEMASK) { - case FLASH_INTEL800B: - case FLASH_INTEL160B: - case FLASH_INTEL320B: - case FLASH_INTEL640B: - case FLASH_28F800C3B: - case FLASH_28F160C3B: - case FLASH_28F320C3B: - case FLASH_28F640C3B: - case FLASH_AM640U: - case FLASH_AM800T: - break; - case FLASH_UNKNOWN: - default: - printf ("Can't erase unknown flash type %08lx - aborted\n", - info->flash_id); - 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"); - } - - start = get_timer(0); - last = start; - - /* Start erase on unprotected sectors */ - for (sect = s_first; sect<=s_last && rcode == 0; sect++) { - - if (info->protect[sect] != 0) /* protected, skip it */ - continue; - - /* Disable interrupts which might cause a timeout here */ - flag = disable_interrupts(); - - addr = (FPWV *)(info->start[sect]); - if (intel) { - *addr = (FPW)0x00500050; /* clear status register */ - *addr = (FPW)0x00200020; /* erase setup */ - *addr = (FPW)0x00D000D0; /* erase confirm */ - } - else { - /* must be AMD style if not Intel */ - FPWV *base; /* first address in bank */ - - base = (FPWV *)(info->start[0]); - base[0x0555] = (FPW)0x00AA00AA; /* unlock */ - base[0x02AA] = (FPW)0x00550055; /* unlock */ - base[0x0555] = (FPW)0x00800080; /* erase mode */ - base[0x0555] = (FPW)0x00AA00AA; /* unlock */ - base[0x02AA] = (FPW)0x00550055; /* unlock */ - *addr = (FPW)0x00300030; /* erase sector */ - } - - /* re-enable interrupts if necessary */ - if (flag) - enable_interrupts(); - - /* wait at least 50us for AMD, 80us for Intel. - * Let's wait 1 ms. - */ - udelay (1000); - - while ((*addr & (FPW)0x00800080) != (FPW)0x00800080) { - if ((now = get_timer(start)) > CONFIG_SYS_FLASH_ERASE_TOUT) { - printf ("Timeout\n"); - - if (intel) { - /* suspend erase */ - *addr = (FPW)0x00B000B0; - } - - flash_reset(info); /* reset to read mode */ - rcode = 1; /* failed */ - break; - } - - /* show that we're waiting */ - if ((now - last) > 1000) { /* every second */ - putc ('.'); - last = now; - } - } - - flash_reset(info); /* reset to read mode */ - } - - printf (" done\n"); - return rcode; -} - -/*----------------------------------------------------------------------- - * 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) -{ - FPW data = 0; /* 16 or 32 bit word, matches flash bus width on MPC8XX */ - int bytes; /* number of bytes to program in current word */ - int left; /* number of bytes left to program */ - int i, res; - - for (left = cnt, res = 0; - left > 0 && res == 0; - addr += sizeof(data), left -= sizeof(data) - bytes) { - - bytes = addr & (sizeof(data) - 1); - addr &= ~(sizeof(data) - 1); - - /* combine source and destination data so can program - * an entire word of 16 or 32 bits - */ - for (i = 0; i < sizeof(data); i++) { - data <<= 8; - if (i < bytes || i - bytes >= left ) - data += *((uchar *)addr + i); - else - data += *src++; - } - - /* write one word to the flash */ - switch (info->flash_id & FLASH_VENDMASK) { - case FLASH_MAN_AMD: - res = write_word_amd(info, (FPWV *)addr, data); - break; - case FLASH_MAN_INTEL: - res = write_word_intel(info, (FPWV *)addr, data); - break; - default: - /* unknown flash type, error! */ - printf ("missing or unknown FLASH type\n"); - res = 1; /* not really a timeout, but gives error */ - break; - } - } - - return (res); -} - -/*----------------------------------------------------------------------- - * Write a word to Flash for AMD FLASH - * A word is 16 or 32 bits, whichever the bus width of the flash bank - * (not an individual chip) is. - * - * returns: - * 0 - OK - * 1 - write timeout - * 2 - Flash not erased - */ -static int write_word_amd (flash_info_t *info, FPWV *dest, FPW data) -{ - ulong start; - int flag; - int res = 0; /* result, assume success */ - FPWV *base; /* first address in flash bank */ - - /* Check if Flash is (sufficiently) erased */ - if ((*dest & data) != data) { - return (2); - } - - - base = (FPWV *)(info->start[0]); - - /* Disable interrupts which might cause a timeout here */ - flag = disable_interrupts(); - - base[0x0555] = (FPW)0x00AA00AA; /* unlock */ - base[0x02AA] = (FPW)0x00550055; /* unlock */ - base[0x0555] = (FPW)0x00A000A0; /* selects program mode */ - - *dest = data; /* start programming the data */ - - /* re-enable interrupts if necessary */ - if (flag) - enable_interrupts(); - - start = get_timer (0); - - /* data polling for D7 */ - while (res == 0 && (*dest & (FPW)0x00800080) != (data & (FPW)0x00800080)) { - if (get_timer(start) > CONFIG_SYS_FLASH_WRITE_TOUT) { - *dest = (FPW)0x00F000F0; /* reset bank */ - res = 1; - } - } - - return (res); -} - -/*----------------------------------------------------------------------- - * Write a word to Flash for Intel FLASH - * A word is 16 or 32 bits, whichever the bus width of the flash bank - * (not an individual chip) is. - * - * returns: - * 0 - OK - * 1 - write timeout - * 2 - Flash not erased - */ -static int write_word_intel (flash_info_t *info, FPWV *dest, FPW data) -{ - ulong start; - int flag; - int res = 0; /* result, assume success */ - - /* Check if Flash is (sufficiently) erased */ - if ((*dest & data) != data) { - return (2); - } - - /* Disable interrupts which might cause a timeout here */ - flag = disable_interrupts(); - - *dest = (FPW)0x00500050; /* clear status register */ - *dest = (FPW)0x00FF00FF; /* make sure in read mode */ - *dest = (FPW)0x00400040; /* program setup */ - - *dest = data; /* start programming the data */ - - /* re-enable interrupts if necessary */ - if (flag) - enable_interrupts(); - - start = get_timer (0); - - while (res == 0 && (*dest & (FPW)0x00800080) != (FPW)0x00800080) { - if (get_timer(start) > CONFIG_SYS_FLASH_WRITE_TOUT) { - *dest = (FPW)0x00B000B0; /* Suspend program */ - res = 1; - } - } - - if (res == 0 && (*dest & (FPW)0x00100010)) - res = 1; /* write failed, time out error is close enough */ - - *dest = (FPW)0x00500050; /* clear status register */ - *dest = (FPW)0x00FF00FF; /* make sure in read mode */ - - return (res); -} - -#ifdef CONFIG_SYS_FLASH_PROTECTION -/*----------------------------------------------------------------------- - */ -int flash_real_protect (flash_info_t * info, long sector, int prot) -{ - int rcode = 0; /* assume success */ - FPWV *addr; /* address of sector */ - FPW value; - - addr = (FPWV *) (info->start[sector]); - - switch (info->flash_id & FLASH_TYPEMASK) { - case FLASH_28F800C3B: - case FLASH_28F800C3T: - case FLASH_28F160C3B: - case FLASH_28F160C3T: - case FLASH_28F320C3B: - case FLASH_28F320C3T: - case FLASH_28F640C3B: - case FLASH_28F640C3T: - flash_reset (info); /* make sure in read mode */ - *addr = (FPW) 0x00600060L; /* lock command setup */ - if (prot) - *addr = (FPW) 0x00010001L; /* lock sector */ - else - *addr = (FPW) 0x00D000D0L; /* unlock sector */ - flash_reset (info); /* reset to read mode */ - - /* now see if it really is locked/unlocked as requested */ - *addr = (FPW) 0x00900090; - /* read sector protection at sector address, (A7 .. A0) = 0x02. - * D0 = 1 for each device if protected. - * If at least one device is protected the sector is marked - * protected, but return failure. Mixed protected and - * unprotected devices within a sector should never happen. - */ - value = addr[2] & (FPW) 0x00010001; - if (value == 0) - info->protect[sector] = 0; - else if (value == (FPW) 0x00010001) - info->protect[sector] = 1; - else { - /* error, mixed protected and unprotected */ - rcode = 1; - info->protect[sector] = 1; - } - if (info->protect[sector] != prot) - rcode = 1; /* failed to protect/unprotect as requested */ - - /* reload all protection bits from hardware for now */ - flash_sync_real_protect (info); - break; - - case FLASH_AM640U: - case FLASH_AM800T: - default: - /* no hardware protect that we support */ - info->protect[sector] = prot; - break; - } - - return rcode; -} -#endif diff --git a/board/sixnet/fpgadata.c b/board/sixnet/fpgadata.c deleted file mode 100644 index 2d3a7b3..0000000 --- a/board/sixnet/fpgadata.c +++ /dev/null @@ -1,1719 +0,0 @@ - 0xff, 0x87, 0xff, 0x88, 0x7f, 0xff, 0xf9, 0xff, - 0xff, 0xf5, 0xff, 0x8f, 0xff, 0xf0, 0x8f, 0xf9, - 0xff, 0xef, 0xff, 0xff, 0xff, - 0xff, 0xff, 0x8f, 0xff, 0xf0, 0xff, 0xff, 0xf0, - 0xff, 0xff, 0xff, 0xff, 0x8f, 0x7f, 0xf1, 0xcf, - 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, - 0x7f, 0x7b, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0x77, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0x86, 0xf6, 0xf0, 0xff, - 0xf0, 0xff, 0xff, 0xff, 0xff, 0x7f, 0x0f, 0x7f, - 0xc1, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xf8, 0xff, 0xff, 0xf6, 0xf0, 0xff, 0xff, - 0x7f, 0x8f, 0x7f, 0xf0, 0xff, 0x0f, 0x7f, 0xff, - 0xff, 0xff, 0xff, 0x8f, 0x7f, - 0xff, 0xf8, 0xf7, 0x8f, 0xcf, 0xf0, 0xf6, 0xff, - 0xff, 0xef, 0xff, 0xfb, 0x7f, 0x2f, 0x1f, 0x71, - 0xf5, 0xff, 0xff, 0xef, 0x7f, - 0xff, 0x7f, 0xff, 0xf7, 0xf6, 0xfe, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xf7, 0x7f, 0x77, 0xf7, 0xff, 0xfb, - 0x0f, 0xff, 0xf0, 0xff, 0xff, 0x7f, 0xff, 0xff, - 0xfe, 0xff, 0x8f, 0x7f, 0xf1, - 0xff, 0xff, 0xfa, 0xce, 0xff, 0xfd, 0xff, 0xff, - 0x9f, 0xff, 0x8e, 0xff, 0xf0, 0xbf, 0x7f, 0xf5, - 0xff, 0xef, 0x9f, 0xfd, 0x81, - 0xff, 0xf9, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, - 0xff, 0xef, 0x9f, 0xfb, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0x7f, - 0xff, 0x77, 0xfa, 0xb6, 0xff, 0x78, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xbf, 0xfd, 0x0f, 0x7f, 0xf1, - 0xff, 0xff, 0xff, 0xff, 0x8f, - 0xff, 0xf6, 0xf7, 0xf6, 0x7f, 0xbf, 0xff, 0xff, - 0xff, 0xff, 0xef, 0xbf, 0xf2, 0x7f, 0xef, 0xff, - 0xfe, 0xfb, 0xff, 0xef, 0xff, - 0xff, 0xf7, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xbf, - 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0x7f, 0xff, 0xff, - 0xff, 0xf7, 0xff, 0xf7, 0xcf, 0x8f, 0xff, 0xf0, - 0xef, 0xf9, 0xfb, 0xff, 0xff, 0xff, 0x9f, 0x0f, - 0x65, 0xe1, 0xfb, 0x7b, 0xf3, - 0xff, 0xf7, 0xf6, 0xfe, 0xff, 0x8f, 0xf6, 0xe8, - 0xf6, 0xf1, 0xff, 0xff, 0xff, 0xf9, 0xff, 0xff, - 0x6f, 0x61, 0xf1, 0xfb, 0xff, - 0xff, 0xde, 0x8f, 0x8f, 0xf0, 0xf0, 0xff, 0xff, - 0xf7, 0xbf, 0xff, 0xd4, 0x8f, 0x0f, 0x71, 0xc1, - 0x6f, 0xd1, 0xeb, 0x5f, 0xfd, - 0xff, 0x9f, 0xff, 0xfb, 0xff, 0x8f, 0x9f, 0xf7, - 0x9f, 0xff, 0xf4, 0xb7, 0xfd, 0xff, 0xfe, 0x8f, - 0xbf, 0x71, 0x1f, 0xff, 0x7f, - 0xff, 0xfd, 0x87, 0x87, 0xf0, 0x70, 0x1f, 0xf7, - 0xbf, 0xff, 0xff, 0xff, 0x8f, 0x0f, 0x71, 0x81, - 0xbf, 0x3e, 0x7f, 0x7f, 0xff, - 0xff, 0xff, 0xff, 0xff, 0x8f, 0xff, 0x7f, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xf1, 0xff, 0xff, 0xff, 0xff, - 0xbf, 0xff, 0x07, 0xff, 0xf0, 0xff, 0xff, 0xff, - 0xfe, 0xff, 0xff, 0xf7, 0x8d, 0x7f, 0xf1, 0xff, - 0xff, 0x9f, 0x6f, 0xf1, 0xff, - 0xbf, 0x71, 0x87, 0xfe, 0xf0, 0x8f, 0x8f, 0xf0, - 0xfb, 0xcb, 0xff, 0xf0, 0x8f, 0x7f, 0xf1, 0x8f, - 0x1e, 0xe1, 0x7e, 0x91, 0x7f, - 0xbf, 0x1a, 0xff, 0x71, 0xff, 0x9f, 0x8f, 0xf6, - 0xf8, 0xdf, 0xf7, 0xf4, 0xff, 0xff, 0xff, 0x8f, - 0x1f, 0xf0, 0x7f, 0x97, 0xff, - 0xbf, 0x97, 0xff, 0xfb, 0xbf, 0xdf, 0xff, 0xf7, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xaf, 0xdf, - 0xf9, 0xff, 0xff, 0xff, 0xff, - 0xff, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xf7, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xdf, 0xff, 0xf1, 0xff, - 0xff, 0x9f, 0xfc, 0xfb, 0xff, 0xf0, 0xfe, 0xff, - 0xff, 0xff, 0x9d, 0xff, 0xf4, 0xcf, 0xff, 0x7f, - 0xf7, 0xff, 0xff, 0xff, 0xcf, - 0xff, 0x97, 0xff, 0xfa, 0xff, 0x8f, 0xf8, 0xf0, - 0xff, 0xff, 0xff, 0xdf, 0xff, 0xfd, 0xff, 0x0f, - 0x7f, 0xe1, 0xff, 0xf1, 0xff, - 0xff, 0x83, 0x7f, 0xf8, 0xff, 0xff, 0xff, 0xff, - 0x7f, 0x6f, 0x7f, 0x77, 0x7d, 0xff, 0xff, 0xff, - 0xff, 0xff, 0x8f, 0x6f, 0xf1, - 0xff, 0xd7, 0xff, 0xfe, 0xff, 0xff, 0x9f, 0xfd, - 0x78, 0xef, 0xff, 0xbf, 0xff, 0xf5, 0xff, 0xff, - 0xbf, 0x0f, 0x79, 0xd1, 0xff, - 0xff, 0xd2, 0xff, 0x72, 0xff, 0xff, 0xff, 0xff, - 0x8f, 0xfe, 0x70, 0x9d, 0xff, 0xf4, 0xff, 0xfe, - 0xff, 0xff, 0xff, 0xbf, 0x7f, - 0xff, 0x07, 0xff, 0x78, 0xff, 0x9f, 0xff, 0xfe, - 0xff, 0x77, 0x7f, 0x8f, 0x7f, 0xf0, 0xff, 0x8f, - 0x7f, 0xe1, 0x0f, 0x71, 0xf1, - 0xff, 0xfe, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xff, - 0x7f, 0xfd, 0xff, 0xba, 0x7f, 0xff, 0xff, 0xff, - 0xff, 0xef, 0x7f, 0xa1, 0x7f, - 0xff, 0xbd, 0x7f, 0xf7, 0xf9, 0xfd, 0xfb, 0xff, - 0xff, 0x8f, 0xbf, 0xb7, 0x8f, 0xaf, 0xdf, 0xff, - 0xff, 0xff, 0xff, 0x5f, 0xeb, - 0xbf, 0xfd, 0xf8, 0xff, 0xff, 0xfb, 0xff, 0xfb, - 0xff, 0xf7, 0xcf, 0xfb, 0xf0, 0xff, 0xff, 0xdf, - 0xff, 0xff, 0xef, 0x7f, 0xab, - 0xff, 0xfd, 0xfa, 0xbf, 0x8f, 0xbf, 0xca, 0xfe, - 0xff, 0xff, 0xdf, 0x6f, 0xd4, 0xf6, 0x0f, 0x3f, - 0x11, 0xf9, 0xff, 0x7f, 0x8b, - 0xbf, 0xff, 0x8f, 0xff, 0xc0, 0xfb, 0xf5, 0xef, - 0xf7, 0x7f, 0xff, 0xff, 0xfb, 0x7f, 0xff, 0x7f, - 0xff, 0x6f, 0xff, 0xff, 0xff, - 0xbf, 0x87, 0xbb, 0xf8, 0xfb, 0xcf, 0xfe, 0xfe, - 0xff, 0xef, 0xff, 0xfb, 0x7f, 0xff, 0xff, 0x8f, - 0xff, 0xe1, 0x7f, 0x7b, 0xff, - 0xbf, 0x80, 0x89, 0x88, 0xb0, 0xf5, 0xf0, 0xff, - 0xf7, 0xdf, 0xfe, 0x7c, 0x8f, 0x0f, 0x71, 0xe1, - 0xff, 0xf1, 0xe5, 0x0e, 0x2b, - 0xff, 0xff, 0xff, 0xbf, 0xff, 0xcf, 0xf5, 0x9f, - 0xff, 0xff, 0xfe, 0xff, 0x8f, 0x7f, 0x71, 0x8f, - 0xff, 0x91, 0x7f, 0xfb, 0xff, - 0xff, 0x7f, 0x7f, 0xcf, 0x8a, 0xff, 0xf0, 0xff, - 0x57, 0xfe, 0xfb, 0x8f, 0xff, 0xf0, 0xff, 0x7e, - 0xff, 0xff, 0x9a, 0xff, 0xf1, - 0xff, 0xff, 0xcf, 0xb7, 0xce, 0xff, 0xf4, 0xff, - 0xff, 0x7f, 0xf7, 0xfb, 0xff, 0xfe, 0xff, 0x7f, - 0xff, 0xfd, 0xfe, 0x75, 0xfd, - 0xff, 0xef, 0xcf, 0xff, 0xf5, 0xff, 0xf5, 0xff, - 0xf7, 0xff, 0xff, 0xff, 0xff, 0x7f, 0x7f, 0xff, - 0xcf, 0x7f, 0x31, 0x7f, 0xff, - 0x3f, 0x78, 0xf8, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0x0f, 0x0f, 0xf1, 0xf1, 0xdf, 0xff, 0xff, - 0xff, 0x9f, 0xff, 0x84, 0x0e, - 0xff, 0xf8, 0x7f, 0xf7, 0x7f, 0xff, 0xff, 0x8f, - 0x8f, 0x80, 0xf1, 0xf1, 0xff, 0xff, 0xff, 0xff, - 0xfe, 0x9f, 0x8e, 0x05, 0x71, - 0xbf, 0xf8, 0xf8, 0xff, 0x7f, 0xff, 0xff, 0xff, - 0xff, 0x8f, 0x8f, 0xf1, 0xf1, 0xff, 0xff, 0xff, - 0xfe, 0xff, 0xff, 0x8f, 0x0f, - 0xff, 0xf8, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, - 0x8f, 0x8f, 0xf1, 0xf1, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0x8e, 0x0f, 0x71, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0x7f, 0xf7, 0xff, 0xff, 0x8f, 0xff, - 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0x8f, 0x7f, 0xf0, 0xff, 0xff, - 0x7f, 0xf8, 0xff, 0xf7, 0xff, 0xff, 0xff, 0xff, - 0xff, 0x8f, 0xff, 0xf0, 0xff, 0xff, 0xff, 0xff, - 0xff, 0x9f, 0xff, 0x8f, 0x7e, - 0xbf, 0xff, 0x78, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0x8f, 0xff, 0xf0, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xfe, 0xff, 0x8f, - 0xff, 0x87, 0x7f, 0xf8, 0xff, 0xff, 0xff, 0xff, - 0x8f, 0xff, 0xf0, 0x8f, 0xff, 0xf0, 0xff, 0xff, - 0xff, 0xff, 0x8e, 0x7f, 0xf1, - 0xff, 0xf8, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0x8f, 0xff, 0xf0, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0x8f, 0x7f, - 0x3f, 0xff, 0xf8, 0xff, 0x8f, 0x7f, 0xf0, 0x8f, - 0xff, 0xf0, 0x0f, 0xff, 0x70, 0xff, 0x8f, 0x7e, - 0xf1, 0xdf, 0xff, 0xfb, 0x8e, - 0xff, 0x80, 0x7f, 0xf0, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xaf, 0x7f, 0x84, 0xff, 0xf1, 0xff, 0xfe, - 0xff, 0xff, 0xfe, 0x8f, 0x7f, - 0xff, 0x80, 0xff, 0xf8, 0xff, 0x7f, 0xff, 0xff, - 0x7f, 0x8f, 0xff, 0x81, 0x7f, 0xf0, 0xff, 0xff, - 0xff, 0xff, 0xff, 0x8f, 0x7f, - 0xff, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0x8f, 0x7f, 0xf0, 0xdf, 0xdf, 0xff, 0xdf, 0xff, - 0xff, 0xff, 0x8f, 0x7f, 0xf1, - 0xff, 0xfd, 0xff, 0xff, 0xff, 0x0f, 0xff, 0x80, - 0xff, 0xf0, 0xff, 0xff, 0xdf, 0xff, 0xdf, 0x8e, - 0x0f, 0x01, 0x71, 0xf1, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xdf, 0xff, 0xdf, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xfe, - 0xbf, 0x87, 0xf8, 0xf8, 0xff, 0xff, 0xff, 0xff, - 0x7f, 0xff, 0x8f, 0x8f, 0xd0, 0xf0, 0xdf, 0xfe, - 0xff, 0xff, 0xff, 0xff, 0x8f, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd, - 0xff, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0x7f, 0xff, 0xff, - 0xff, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, - 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd, - 0xff, 0xff, 0xff, 0xff, 0x7f, 0xff, 0xff, 0xfe, - 0xff, 0xdf, 0xff, 0xfb, 0xff, - 0xff, 0xff, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xfd, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xaf, 0xfe, 0xf5, 0xff, 0xff, - 0xff, 0xff, 0x0f, 0x8f, 0xf0, 0x80, 0xff, 0xf0, - 0xff, 0xff, 0xff, 0xff, 0x1f, 0xaf, 0x71, 0xa7, - 0x6f, 0xf5, 0xfe, 0xff, 0xff, - 0xff, 0x77, 0x79, 0x8f, 0xff, 0xf0, 0x8f, 0xff, - 0x00, 0xff, 0xd0, 0x4f, 0x3d, 0xf0, 0xf7, 0xfd, - 0x8f, 0x7f, 0x81, 0x7f, 0xd1, - 0xff, 0xcd, 0xff, 0xff, 0x8f, 0x0f, 0x70, 0xf0, - 0xff, 0x7f, 0x7f, 0xff, 0xff, 0xdb, 0x8d, 0x4b, - 0x73, 0xf9, 0xff, 0xdf, 0xff, - 0x3f, 0xfc, 0xff, 0x8f, 0xff, 0xf2, 0x8f, 0x8f, - 0x70, 0x7a, 0x3f, 0xbc, 0xf7, 0xdb, 0xff, 0xf9, - 0xff, 0xff, 0xff, 0xff, 0xee, - 0xff, 0xe8, 0xf7, 0x8f, 0xfd, 0x80, 0xff, 0xf0, - 0x9f, 0xa5, 0x7a, 0xf4, 0x6f, 0x3f, 0xcf, 0x07, - 0x6a, 0xe1, 0xff, 0x8f, 0x7f, - 0xff, 0xff, 0x77, 0xf1, 0x8f, 0x8f, 0xf0, 0xf0, - 0xbf, 0xff, 0xe7, 0x7f, 0x8f, 0x24, 0x03, 0x77, - 0xf3, 0xff, 0xfe, 0xff, 0xff, - 0xbf, 0x9f, 0x77, 0x8b, 0xff, 0xf0, 0xff, 0xef, - 0x7d, 0x7f, 0xff, 0x9f, 0xeb, 0x3d, 0xff, 0xf7, - 0xff, 0xfb, 0xfe, 0xff, 0xdf, - 0xff, 0xff, 0x77, 0xff, 0x8f, 0x8f, 0xf0, 0xf0, - 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xbb, 0x5d, - 0xf5, 0xbb, 0xef, 0xff, 0xff, - 0xff, 0x7f, 0x8f, 0x8f, 0xf0, 0xf8, 0xff, 0xff, - 0xf7, 0x7f, 0xff, 0xff, 0xaf, 0xbf, 0x75, 0xb7, - 0xff, 0xf7, 0xff, 0xff, 0xff, - 0xff, 0x7f, 0x87, 0x7f, 0xf8, 0xff, 0xf7, 0xf7, - 0x8f, 0xff, 0xf0, 0x7f, 0xf7, 0xff, 0xad, 0xff, - 0xf7, 0xee, 0x9f, 0xff, 0xf5, - 0xff, 0xf8, 0x07, 0xff, 0x80, 0x8f, 0x80, 0x80, - 0xf0, 0x8f, 0x7f, 0x70, 0x4f, 0x0f, 0x79, 0xf1, - 0xfd, 0xff, 0xef, 0x8f, 0x7f, - 0xbf, 0x7f, 0xf8, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0x8f, 0xff, 0xd0, 0xbf, 0xdb, 0xe5, - 0x3b, 0xfe, 0xf7, 0xff, 0x8f, - 0xff, 0xff, 0x8f, 0x77, 0x80, 0xff, 0xf0, 0xff, - 0xff, 0x7f, 0xff, 0xff, 0xbd, 0xef, 0x07, 0x7f, - 0xf1, 0xfe, 0xff, 0xfe, 0xff, - 0x7f, 0x7f, 0xff, 0xf7, 0xf7, 0xff, 0xf7, 0x8f, - 0xbf, 0x70, 0xf5, 0x7f, 0xff, 0xef, 0x3f, 0x7d, - 0xf7, 0xff, 0xff, 0xfe, 0xfe, - 0xff, 0x97, 0xff, 0x7f, 0xff, 0xff, 0xff, 0xff, - 0x7e, 0xff, 0xff, 0x9f, 0xdf, 0xf7, 0x3b, 0xff, - 0xf7, 0xff, 0x7f, 0xfe, 0xff, - 0x3f, 0x78, 0xf8, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0x1f, 0x1f, 0xf1, 0xf1, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0x80, 0x0e, - 0xff, 0xf8, 0x7f, 0xff, 0x7f, 0xff, 0xff, 0x8f, - 0x9f, 0x80, 0xe1, 0xf1, 0xff, 0xff, 0xef, 0xff, - 0xfe, 0x9f, 0x0e, 0x01, 0x71, - 0xbf, 0xf8, 0xf8, 0xff, 0x7f, 0xff, 0xff, 0xff, - 0xff, 0x8f, 0x8f, 0xf1, 0xf1, 0xff, 0xff, 0xef, - 0xfe, 0xef, 0xff, 0x8f, 0x0f, - 0xff, 0xf8, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, - 0x9f, 0x8f, 0xf1, 0xf1, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0x8e, 0x0f, 0x71, - 0xff, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xef, 0xff, 0xff, 0xef, 0xfe, 0xef, - 0xef, 0xff, 0xff, 0xef, 0xff, - 0xff, 0xf7, 0x7f, 0xff, 0xff, 0xff, 0x8f, 0xff, - 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0x8f, 0x7f, 0xe0, 0xff, 0xff, - 0x7f, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0x8f, 0xff, 0xf0, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0x8f, 0x7e, - 0xbf, 0xff, 0x78, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0x8f, 0xff, 0xf0, 0xef, 0xff, 0xff, - 0xff, 0xff, 0xee, 0xef, 0x9f, - 0xff, 0x07, 0xff, 0xf8, 0xff, 0xff, 0xff, 0xff, - 0x8f, 0xff, 0xf0, 0x8f, 0xff, 0xe0, 0xff, 0xff, - 0xff, 0xef, 0x8e, 0x7f, 0xf1, - 0xff, 0xf8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0x8f, 0xff, 0xf0, 0xff, 0xff, 0xff, 0xff, - 0xef, 0xff, 0xfe, 0x8f, 0x7f, - 0x3f, 0xff, 0xf8, 0xff, 0x8f, 0x7f, 0xf0, 0xdf, - 0xff, 0xf0, 0x0f, 0xff, 0x70, 0xff, 0x8f, 0x7e, - 0xe1, 0xdf, 0xff, 0xf7, 0x8e, - 0xff, 0x80, 0x7f, 0xf8, 0xff, 0x7f, 0xff, 0xff, - 0xff, 0x8f, 0x7f, 0x80, 0xff, 0xf1, 0xff, 0xff, - 0xff, 0xef, 0xfe, 0x8f, 0x7f, - 0xff, 0x80, 0xff, 0xf8, 0xff, 0xff, 0xff, 0xff, - 0x7f, 0x8f, 0xff, 0x81, 0x7f, 0xf0, 0xff, 0xfe, - 0xff, 0xff, 0xff, 0x8f, 0x7f, - 0xff, 0x7f, 0xff, 0xf7, 0xff, 0xff, 0xff, 0xff, - 0x1f, 0x7f, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0x8f, 0x7e, 0xf1, - 0xff, 0xff, 0xff, 0xf7, 0xff, 0x0f, 0x8f, 0x80, - 0xf7, 0xe0, 0xff, 0xff, 0xff, 0xff, 0xff, 0x9e, - 0x6f, 0x91, 0x71, 0xf1, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, - 0xff, 0xff, 0xef, 0xff, 0xff, - 0xbf, 0x87, 0xf8, 0xf8, 0xff, 0x7f, 0xff, 0xff, - 0xff, 0xff, 0x8f, 0x8f, 0xf0, 0xf0, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xdf, 0x8f, - 0xff, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xfe, - 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xfe, - 0xff, 0xef, 0xff, 0xd7, 0xff, - 0xff, 0xff, 0xff, 0x7f, 0xff, 0xff, 0x8f, 0xff, - 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xff, - 0xff, 0xfe, 0xf9, 0xdf, 0xff, - 0xff, 0xff, 0x8f, 0xbf, 0xf7, 0x9f, 0xf8, 0xf0, - 0xff, 0xff, 0x77, 0xff, 0x0e, 0x1f, 0x61, 0x81, - 0x7f, 0xf1, 0xfe, 0xff, 0xff, - 0xff, 0x7f, 0xb9, 0xcf, 0xff, 0xff, 0x0f, 0xff, - 0x00, 0xff, 0xd0, 0x7f, 0x75, 0x8b, 0x7f, 0xf1, - 0x8f, 0x7f, 0x80, 0x7e, 0x91, - 0xff, 0xbf, 0xdf, 0xff, 0xa7, 0x47, 0x70, 0xf7, - 0xff, 0xff, 0x7f, 0xff, 0xff, 0xff, 0x8f, 0x0f, - 0x61, 0xf1, 0xef, 0xff, 0xff, - 0x7f, 0xfe, 0xef, 0x5f, 0xf7, 0xff, 0xff, 0xff, - 0xff, 0xe7, 0xb7, 0xfc, 0xeb, 0x9f, 0x7f, 0xf1, - 0x9f, 0x0f, 0x71, 0xf1, 0xee, - 0xff, 0xf0, 0xf7, 0x3f, 0xef, 0x97, 0xf8, 0xe8, - 0xff, 0x9f, 0x7f, 0xf0, 0x7f, 0x9f, 0x6f, 0x91, - 0x7e, 0xf1, 0x9f, 0x8f, 0x57, - 0xff, 0xff, 0x26, 0xb9, 0xb8, 0xff, 0xf0, 0xff, - 0xff, 0xff, 0xf7, 0x7f, 0x6f, 0xf4, 0x9f, 0x1f, - 0x71, 0xe1, 0xfe, 0x7f, 0xff, - 0xbf, 0xff, 0x71, 0xbb, 0xe8, 0xff, 0xff, 0xf8, - 0xbf, 0xff, 0xaf, 0xff, 0xf8, 0x9d, 0x6f, 0xf1, - 0xbf, 0xff, 0xb7, 0xff, 0xbd, - 0xbf, 0xff, 0xff, 0xdf, 0x97, 0xc7, 0xf7, 0xf0, - 0xff, 0xff, 0x93, 0xff, 0xff, 0xef, 0xcf, 0x5f, - 0xf1, 0xf7, 0xdf, 0xf5, 0x9f, - 0xff, 0xff, 0x87, 0xbf, 0xe0, 0xbf, 0xf7, 0xff, - 0xf7, 0x7f, 0xff, 0xff, 0x8f, 0x5f, 0x21, 0xb1, - 0xff, 0x6d, 0xff, 0xef, 0xff, - 0xff, 0xff, 0xd7, 0xff, 0xb8, 0xff, 0xff, 0xff, - 0x3f, 0xef, 0xf0, 0x7f, 0xd7, 0x7f, 0xf1, 0xff, - 0xef, 0xee, 0xbf, 0x7f, 0xf1, - 0xff, 0xf8, 0x47, 0x0f, 0xc7, 0xf0, 0x7f, 0xf0, - 0xf0, 0x90, 0x7f, 0x70, 0x8f, 0x2f, 0xc1, 0x0f, - 0x11, 0x1f, 0xef, 0xaf, 0x7f, - 0xbf, 0x7f, 0xf0, 0x9f, 0xe7, 0xf7, 0x38, 0xff, - 0xff, 0xff, 0x8f, 0x7f, 0xf0, 0xaf, 0xff, 0xff, - 0xbf, 0xfe, 0xfd, 0xdf, 0x8f, - 0xff, 0xff, 0xbf, 0xf7, 0x8f, 0xff, 0xf7, 0xff, - 0xeb, 0xff, 0xff, 0xff, 0x8d, 0x3f, 0x81, 0x7f, - 0xd1, 0xfe, 0xdf, 0xfe, 0xff, - 0x7f, 0xff, 0xff, 0xdf, 0xa8, 0xff, 0xf0, 0xff, - 0xff, 0xf0, 0xf7, 0xff, 0xff, 0xff, 0xef, 0xef, - 0xef, 0x9f, 0x7f, 0x7e, 0xfe, - 0xff, 0xff, 0xef, 0xff, 0xa7, 0x77, 0xff, 0xff, - 0xef, 0xff, 0xff, 0xdf, 0xff, 0xe7, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xfe, 0xff, - 0x3f, 0x78, 0xf8, 0xff, 0xff, 0xff, 0xff, 0xbf, - 0xff, 0x0f, 0x0f, 0xf1, 0xe1, 0xff, 0xff, 0xef, - 0xef, 0xff, 0xff, 0x8e, 0x0e, - 0xff, 0xf8, 0x7f, 0xff, 0x7f, 0xff, 0xff, 0x8f, - 0x8f, 0x80, 0xf1, 0xf1, 0xef, 0xaf, 0xaf, 0xff, - 0xee, 0xdf, 0x0e, 0x01, 0x71, - 0xbf, 0xf8, 0xf8, 0xff, 0x7f, 0xff, 0xff, 0xff, - 0xef, 0x8f, 0x9f, 0xf1, 0xe1, 0xff, 0xaf, 0xef, - 0xfe, 0xff, 0xff, 0x8f, 0x0f, - 0xff, 0xf8, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, - 0x9f, 0x9f, 0xf1, 0xf1, 0xef, 0xff, 0xaf, 0xff, - 0xff, 0xff, 0x8e, 0x0f, 0x71, - 0xff, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xef, 0xbf, 0xef, 0xff, - 0xef, 0xbf, 0xff, 0xef, 0xff, - 0xff, 0xf7, 0x7f, 0xff, 0xff, 0xff, 0x8f, 0xff, - 0xf0, 0xff, 0xff, 0xff, 0xef, 0xff, 0xef, 0xfe, - 0xcf, 0x3f, 0xf0, 0xff, 0xff, - 0x7f, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xbf, - 0xff, 0x88, 0xff, 0xf0, 0xff, 0xff, 0xef, 0xfe, - 0xff, 0xff, 0xff, 0x8f, 0x6e, - 0xbf, 0xff, 0x78, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0x8f, 0xff, 0xe0, 0xff, 0xef, 0xff, - 0xff, 0xff, 0xee, 0xef, 0x9f, - 0xff, 0x8f, 0x7f, 0xf8, 0xff, 0xff, 0xff, 0xff, - 0x8f, 0xff, 0xf0, 0x8f, 0xff, 0xa0, 0xff, 0xfe, - 0xff, 0xbf, 0x8e, 0x6f, 0xf1, - 0xff, 0xf8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0x8f, 0xff, 0xf0, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0x8f, 0x6f, - 0x3f, 0xff, 0xf8, 0xff, 0x8f, 0x7f, 0xf0, 0xcf, - 0xff, 0xb0, 0x0f, 0xaf, 0x70, 0xff, 0x8f, 0x7e, - 0xf1, 0xff, 0xff, 0xf1, 0x9e, - 0xff, 0x80, 0x7f, 0xf8, 0xff, 0x7f, 0xff, 0xff, - 0xef, 0x8f, 0x7f, 0x90, 0xff, 0xf1, 0xff, 0xff, - 0xff, 0xaf, 0xfe, 0x8f, 0x7f, - 0xff, 0x80, 0xff, 0xf8, 0xff, 0xff, 0xff, 0xff, - 0x3f, 0xdf, 0xff, 0x81, 0x7f, 0xf0, 0xff, 0xfe, - 0xff, 0xff, 0xff, 0x8f, 0x7f, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0x9f, 0x7f, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xbf, 0x7e, 0xf1, - 0xff, 0xff, 0x7f, 0xff, 0xff, 0x0f, 0xaf, 0x80, - 0xf0, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xde, - 0x0f, 0x91, 0x7f, 0xf1, 0xff, - 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0x7f, 0xff, 0xff, 0xff, 0xff, 0xf3, 0xff, 0xfe, - 0xff, 0xff, 0xbf, 0xff, 0xfb, - 0xbf, 0x87, 0xf8, 0xf8, 0xff, 0x7f, 0xff, 0xff, - 0xff, 0xdf, 0x8f, 0x8f, 0xf0, 0xf0, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0x8f, - 0xff, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0x7f, 0xff, 0xff, 0xdf, 0xbf, 0xff, 0xef, 0xff, - 0xff, 0xaf, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xbf, 0xfe, - 0xff, 0xff, 0xff, 0xff, 0xff, - 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xdf, 0xff, 0xff, 0xbf, 0xff, 0xff, 0xfe, - 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0x7f, 0xff, 0xaf, 0xff, - 0xf0, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xbf, 0xff, - 0xdf, 0xfe, 0xfe, 0xff, 0xff, - 0xff, 0xff, 0x0f, 0x8f, 0xf0, 0x8f, 0xff, 0xf0, - 0xf9, 0xff, 0xf7, 0xff, 0x0f, 0x5f, 0x29, 0x89, - 0x77, 0xf1, 0xfa, 0xff, 0xde, - 0xff, 0xc3, 0x3f, 0x4b, 0x7f, 0xe9, 0x0f, 0xff, - 0x00, 0xff, 0x90, 0x0f, 0xd7, 0xff, 0x7f, 0xf9, - 0x8f, 0x7f, 0x81, 0x7f, 0x81, - 0xff, 0xff, 0xfb, 0x7d, 0x80, 0x46, 0x76, 0xf0, - 0xff, 0xff, 0x6f, 0xff, 0xff, 0xad, 0xcf, 0x3f, - 0x71, 0xf9, 0xff, 0xff, 0xff, - 0x3f, 0xba, 0xff, 0xc7, 0xf7, 0xb9, 0xcf, 0xde, - 0x77, 0xb7, 0x77, 0xfe, 0xff, 0xbf, 0x6f, 0xf9, - 0xff, 0x7e, 0x79, 0xb9, 0xfe, - 0xff, 0xe4, 0xf7, 0x8f, 0xfe, 0x07, 0xfe, 0xf8, - 0xff, 0x89, 0x7f, 0xe8, 0x7f, 0xd7, 0x7f, 0x99, - 0x76, 0xf1, 0xff, 0x0f, 0x7b, - 0xbf, 0xff, 0xb6, 0xb9, 0x8f, 0xdf, 0xf6, 0xff, - 0xff, 0xf7, 0xff, 0xff, 0x8f, 0xdd, 0x87, 0x7f, - 0x71, 0xf1, 0xfe, 0xff, 0xff, - 0xff, 0x7f, 0xf1, 0x8a, 0xff, 0xff, 0xff, 0xff, - 0xbf, 0xff, 0xcf, 0xfb, 0xe8, 0x9d, 0x77, 0xa9, - 0xff, 0x77, 0xda, 0x7f, 0xff, - 0xbf, 0xff, 0xf7, 0xf7, 0x86, 0xe5, 0xf0, 0xe0, - 0xff, 0xff, 0xbf, 0xff, 0xff, 0xef, 0x8f, 0x7f, - 0xbd, 0xff, 0xff, 0xff, 0x8f, - 0xff, 0xef, 0x86, 0x8f, 0xf0, 0xff, 0xf6, 0x9f, - 0xff, 0x7f, 0xff, 0xff, 0xcf, 0x1f, 0x71, 0xdd, - 0x7f, 0xe1, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xc7, 0xf7, 0xb9, 0xff, 0xff, 0xfa, - 0x3f, 0xef, 0xf0, 0xff, 0xef, 0x7f, 0xd5, 0xff, - 0xfb, 0xff, 0xf7, 0x6e, 0xf1, - 0xff, 0xfc, 0xc7, 0xbf, 0xc8, 0xc0, 0x59, 0xff, - 0xdf, 0xff, 0x7b, 0xf0, 0xa7, 0x1f, 0xa9, 0x77, - 0x79, 0x71, 0x11, 0xff, 0x79, - 0xbf, 0xfb, 0x70, 0xbf, 0xff, 0xf9, 0x37, 0xbe, - 0xff, 0xff, 0x8f, 0x7f, 0xf4, 0x9f, 0xff, 0xff, - 0xd7, 0x7f, 0xff, 0xff, 0xaf, - 0xff, 0xff, 0x9e, 0xf7, 0x9f, 0xfe, 0xe4, 0xff, - 0xcf, 0xcf, 0xff, 0xff, 0xdf, 0x7f, 0x8d, 0x7f, - 0xf9, 0xfa, 0xdf, 0x9f, 0xef, - 0x7f, 0xef, 0xff, 0xff, 0xbe, 0xfd, 0xd2, 0xdf, - 0xff, 0x7e, 0xf7, 0xff, 0xff, 0xab, 0x97, 0xef, - 0xf3, 0xfe, 0x7f, 0x71, 0xfe, - 0xff, 0x9f, 0xff, 0xff, 0xb6, 0xfb, 0xf7, 0xff, - 0xff, 0xf7, 0xff, 0xbf, 0xff, 0xb7, 0xdb, 0xff, - 0xbb, 0xef, 0xff, 0xff, 0xff, - 0x3f, 0x68, 0xfe, 0xfd, 0xfb, 0xff, 0xff, 0xef, - 0xf1, 0x1e, 0x1b, 0xf1, 0xf5, 0xff, 0xff, 0xff, - 0xff, 0x9f, 0xfb, 0x9a, 0x36, - 0xff, 0xfc, 0x7d, 0xff, 0x73, 0xf7, 0xff, 0xaf, - 0x9f, 0x94, 0xfd, 0xf5, 0xff, 0xf7, 0xff, 0xfb, - 0xfe, 0xef, 0x3e, 0x07, 0x4d, - 0xbf, 0xe8, 0xf8, 0xff, 0x7f, 0xff, 0xf7, 0xf7, - 0xf1, 0x8f, 0xaf, 0xd1, 0xf7, 0xf9, 0xfd, 0xff, - 0xf8, 0xdf, 0xfb, 0x8f, 0x2f, - 0xff, 0xf8, 0x7f, 0xff, 0xf7, 0xf7, 0xff, 0xff, - 0xa7, 0xaf, 0xf7, 0xf3, 0xdf, 0xff, 0xfd, 0xff, - 0xfd, 0xff, 0xae, 0x0f, 0x71, - 0xff, 0xff, 0xff, 0xf9, 0xff, 0xff, 0xf3, 0xf3, - 0xff, 0xf3, 0xff, 0xf7, 0xfb, 0xf3, 0xff, 0xff, - 0xff, 0xeb, 0xff, 0xf3, 0xdb, - 0xff, 0xeb, 0x7b, 0xfb, 0xf7, 0xff, 0x8b, 0xf7, - 0xfc, 0xf7, 0xfb, 0xff, 0xfb, 0xf3, 0xff, 0xff, - 0x8b, 0x7f, 0xd4, 0xfb, 0xff, - 0x7f, 0xec, 0xff, 0xff, 0xff, 0xff, 0xf7, 0xf7, - 0xff, 0x8e, 0xff, 0xf8, 0xf7, 0xfb, 0xfd, 0xff, - 0xfd, 0x9f, 0xf7, 0x9f, 0x7e, - 0xbf, 0xfb, 0x7c, 0xff, 0xf7, 0xff, 0xff, 0xfb, - 0xfb, 0xf1, 0x8f, 0xf3, 0xdc, 0xf7, 0xfd, 0xff, - 0xe9, 0xeb, 0xef, 0xc3, 0xb7, - 0xff, 0x07, 0xff, 0xfc, 0xff, 0xff, 0xff, 0xf7, - 0x8f, 0xff, 0xf4, 0x8f, 0xfb, 0xfc, 0xff, 0xef, - 0xff, 0xf7, 0x8f, 0x7f, 0xd1, - 0xff, 0xfa, 0xff, 0xfb, 0xff, 0xff, 0xff, 0xff, - 0xf3, 0x89, 0xef, 0xf8, 0xff, 0xf7, 0xff, 0xef, - 0xef, 0xf7, 0xf3, 0xab, 0x7f, - 0x3f, 0xf9, 0x7e, 0xf9, 0x8f, 0x7f, 0xf0, 0xef, - 0xff, 0xfc, 0x1b, 0xff, 0x7c, 0xff, 0x8f, 0x6e, - 0xf1, 0xf7, 0x73, 0xff, 0xa6, - 0xff, 0x80, 0x7f, 0xf8, 0xff, 0x7f, 0xff, 0xff, - 0xf9, 0x8f, 0x7f, 0x84, 0xff, 0xf1, 0xff, 0xff, - 0xff, 0xff, 0xfa, 0x8f, 0x7f, - 0xff, 0x96, 0xff, 0xfc, 0xff, 0xff, 0xff, 0xff, - 0x57, 0xaf, 0xfb, 0x85, 0x7f, 0xf4, 0xff, 0xfe, - 0xef, 0xff, 0xef, 0xbf, 0x53, - 0xff, 0x7d, 0xff, 0xff, 0xe3, 0xff, 0xff, 0xff, - 0x97, 0x71, 0xf8, 0xff, 0xff, 0xff, 0xdb, 0xef, - 0xef, 0xe7, 0x97, 0x72, 0xfd, - 0xff, 0xff, 0xff, 0xff, 0xf3, 0x0f, 0xe3, 0x86, - 0xf0, 0xf4, 0xfb, 0xff, 0xdf, 0xff, 0xfb, 0x8e, - 0x0b, 0xa5, 0x72, 0xf9, 0xff, - 0xff, 0xfb, 0xff, 0xff, 0xf7, 0xff, 0xf3, 0xff, - 0xf7, 0xff, 0xf3, 0xff, 0xff, 0xff, 0xfb, 0xee, - 0xfb, 0xff, 0xef, 0xff, 0xff, - 0xbf, 0x82, 0xf8, 0xf8, 0xf7, 0x7f, 0xf7, 0xff, - 0xff, 0xef, 0x87, 0x87, 0xf0, 0xf0, 0xfb, 0xff, - 0xfb, 0xf7, 0xef, 0xef, 0x87, - 0xff, 0xf6, 0xff, 0xfa, 0xf1, 0xef, 0xf3, 0xf7, - 0x7f, 0xff, 0xff, 0xef, 0xff, 0xf7, 0xff, 0xff, - 0xfb, 0xf7, 0xff, 0xfe, 0xff, - 0xff, 0xf7, 0xfb, 0xf2, 0xf3, 0xff, 0xf1, 0xf7, - 0xff, 0xef, 0xf7, 0xef, 0xf7, 0xf7, 0xff, 0xfe, - 0xff, 0xff, 0xef, 0xff, 0xe7, - 0xff, 0xfb, 0xfb, 0xff, 0xf5, 0xef, 0xf7, 0xff, - 0xff, 0xff, 0xff, 0xf7, 0x77, 0xff, 0xff, 0xfe, - 0xff, 0xf7, 0xff, 0xef, 0xef, - 0xff, 0xff, 0xff, 0xff, 0xf7, 0xef, 0xe5, 0xff, - 0xfe, 0x61, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, - 0x9f, 0xef, 0xef, 0xf3, 0xf7, - 0xff, 0xff, 0x0f, 0x9f, 0xfa, 0x87, 0xff, 0xf6, - 0xeb, 0xff, 0xff, 0xef, 0x0f, 0x6f, 0xfd, 0x0d, - 0x53, 0xf1, 0xf3, 0xff, 0xff, - 0xbf, 0x1b, 0x7f, 0x96, 0xfe, 0xff, 0x8f, 0xfb, - 0x00, 0xff, 0xb0, 0x17, 0x7c, 0x8f, 0xff, 0xfd, - 0x8f, 0x7f, 0x81, 0x7e, 0xf1, - 0xff, 0xfd, 0xed, 0xee, 0x9e, 0x0b, 0x79, 0xff, - 0xfb, 0x77, 0x5b, 0xff, 0x9f, 0xff, 0x4f, 0x0f, - 0x71, 0xf0, 0xdb, 0xff, 0xf7, - 0x7f, 0xe7, 0xef, 0x18, 0xff, 0xff, 0x9d, 0x8e, - 0x67, 0xbf, 0x4f, 0xff, 0xff, 0xae, 0xff, 0xf1, - 0xeb, 0xef, 0xfd, 0xad, 0xf6, - 0xff, 0xfc, 0xf7, 0x1f, 0xff, 0x9f, 0xfb, 0xfc, - 0xff, 0x8f, 0x77, 0xec, 0x5f, 0x6f, 0xdf, 0x25, - 0x7e, 0xd9, 0xe6, 0x97, 0x3f, - 0xff, 0xf7, 0x67, 0xec, 0x92, 0xbe, 0xf1, 0xfb, - 0xff, 0x7f, 0xdf, 0x7b, 0x5e, 0x7d, 0xe7, 0x5f, - 0xf1, 0xf1, 0xfb, 0xff, 0xf7, - 0xbf, 0xf7, 0x71, 0x9a, 0xfd, 0xff, 0xf7, 0xfb, - 0x5f, 0x7f, 0xaf, 0xdf, 0xf9, 0xe7, 0x77, 0xdd, - 0x6f, 0xf7, 0xbb, 0xff, 0x8b, - 0xbf, 0xff, 0x77, 0xff, 0x93, 0xfe, 0xf8, 0xfe, - 0xbf, 0xfe, 0xbf, 0xff, 0xff, 0xbf, 0xab, 0x7f, - 0xfd, 0xff, 0xcf, 0x67, 0xff, - 0xff, 0x7f, 0x07, 0x9f, 0xe4, 0xdb, 0xff, 0xf1, - 0xf7, 0x7f, 0xff, 0xff, 0x8f, 0x6f, 0xd1, 0x6d, - 0x73, 0xff, 0xff, 0xfb, 0xff, - 0xff, 0x6f, 0x9f, 0x7b, 0xfd, 0xff, 0xf6, 0xfd, - 0x27, 0xff, 0xfc, 0xff, 0xaf, 0xff, 0xfd, 0xfe, - 0x7f, 0xdf, 0xff, 0x7f, 0xef, - 0xff, 0xfe, 0x81, 0xe7, 0x93, 0x91, 0x83, 0x85, - 0xef, 0x8f, 0x7f, 0x74, 0x8d, 0x1b, 0x2d, 0xe2, - 0xcd, 0xe5, 0xb5, 0x9f, 0x77, - 0xbf, 0x7f, 0xe4, 0xef, 0xff, 0xf7, 0xdb, 0xfd, - 0x7f, 0xfe, 0xab, 0x7f, 0xfc, 0xbf, 0xff, 0xde, - 0x77, 0xfb, 0xdf, 0xef, 0xbf, - 0xff, 0xff, 0x1e, 0x7f, 0x8f, 0xff, 0x92, 0xf3, - 0xdf, 0x7b, 0xff, 0x7b, 0xff, 0xdb, 0x3d, 0x5f, - 0xf9, 0xf6, 0xff, 0xf2, 0xf7, - 0x7f, 0x7f, 0xff, 0xff, 0xef, 0xd2, 0xf0, 0xb7, - 0xfb, 0x7f, 0xfc, 0x77, 0xd7, 0x3f, 0xc7, 0x7f, - 0xf3, 0xe7, 0xff, 0xfd, 0xfe, - 0xff, 0xff, 0xef, 0x7b, 0xef, 0xf5, 0xda, 0xff, - 0x7c, 0xff, 0xff, 0xff, 0xff, 0x7b, 0xeb, 0xfb, - 0xef, 0xff, 0xef, 0xff, 0xff, - 0x3f, 0x60, 0xfc, 0xfb, 0xf7, 0xff, 0xff, 0xff, - 0xfb, 0x00, 0x0f, 0xf1, 0xf5, 0xfb, 0xff, 0xff, - 0xff, 0xff, 0xf3, 0x86, 0x3e, - 0xff, 0xf8, 0x7f, 0xfb, 0x73, 0xff, 0xff, 0x9f, - 0xab, 0x8c, 0xf5, 0xd1, 0xff, 0xfb, 0xff, 0xff, - 0xfe, 0xeb, 0x36, 0x0d, 0x49, - 0xbf, 0xf0, 0xfc, 0xfb, 0x73, 0xff, 0xf3, 0xff, - 0xff, 0xab, 0xa7, 0xf1, 0xf9, 0xff, 0xf7, 0xdf, - 0xfa, 0xfb, 0xff, 0xa7, 0x3f, - 0xff, 0xf8, 0x7f, 0xff, 0xfb, 0xfb, 0xfb, 0xff, - 0xaf, 0x8f, 0xf9, 0xf9, 0xdf, 0xdf, 0xf7, 0xdb, - 0xff, 0xff, 0xba, 0x2f, 0x69, - 0xff, 0xe7, 0xfb, 0xfb, 0xff, 0xff, 0xff, 0xfb, - 0xff, 0xfb, 0xd7, 0xff, 0xdf, 0xf7, 0xd7, 0xdf, - 0xf3, 0xdb, 0xff, 0xdb, 0xff, - 0xff, 0xe3, 0x7b, 0xf9, 0xfb, 0xff, 0x8f, 0xfb, - 0xf8, 0xff, 0xff, 0xef, 0xdf, 0xf3, 0xd7, 0xdf, - 0xa3, 0x5b, 0xc4, 0xfb, 0xef, - 0x7f, 0xe0, 0xfd, 0xfb, 0xfb, 0xff, 0xfb, 0xeb, - 0xff, 0x8c, 0xeb, 0xf0, 0xd3, 0xff, 0xd7, 0xff, - 0xf7, 0xbb, 0x7f, 0x8f, 0x7e, - 0xbf, 0xfb, 0x6c, 0xfb, 0xfb, 0xff, 0xfb, 0xff, - 0xfb, 0xf3, 0x8b, 0xf3, 0xf4, 0xf7, 0xd7, 0xff, - 0xf3, 0xff, 0xfe, 0xc2, 0xbf, - 0xff, 0x87, 0x7f, 0xfa, 0xff, 0xff, 0xff, 0xff, - 0x8f, 0xff, 0xf0, 0x8f, 0xff, 0xf4, 0xff, 0xdf, - 0xff, 0xfb, 0x8f, 0x7f, 0xc5, - 0xff, 0xf8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfb, - 0xf3, 0x87, 0xef, 0xfc, 0xfd, 0xfb, 0xff, 0xff, - 0xdf, 0xff, 0xfb, 0xab, 0x7f, - 0x3f, 0xf3, 0xfa, 0xf9, 0x8f, 0x7f, 0xf0, 0xeb, - 0xfb, 0xec, 0x1f, 0xcf, 0x7e, 0xff, 0x8f, 0x5e, - 0xd1, 0xbf, 0xff, 0xfe, 0xaa, - 0xff, 0x80, 0x7d, 0xf8, 0xff, 0x7f, 0xff, 0xff, - 0xf7, 0x8f, 0x5f, 0x8c, 0xff, 0xf1, 0xff, 0xff, - 0xff, 0xff, 0xfa, 0x9f, 0x6f, - 0xff, 0x9a, 0xfd, 0xfc, 0xff, 0xff, 0xff, 0xff, - 0x6f, 0xbf, 0xd7, 0x89, 0x7f, 0xf4, 0xff, 0xfe, - 0xff, 0xff, 0xdf, 0xbf, 0x6f, - 0xff, 0xfd, 0xff, 0xff, 0xef, 0xff, 0xfb, 0xff, - 0x2b, 0x73, 0xf0, 0xf3, 0xff, 0xff, 0xc3, 0xff, - 0xff, 0xff, 0x8b, 0x62, 0xfd, - 0xff, 0xef, 0xff, 0xff, 0xfb, 0x0f, 0x8b, 0x8e, - 0xf0, 0xdc, 0xf7, 0xff, 0xff, 0xff, 0xfb, 0xae, - 0x43, 0xa9, 0x73, 0xf9, 0xfb, - 0x7f, 0xf9, 0xff, 0xff, 0xfd, 0xff, 0xf9, 0xff, - 0xfb, 0xff, 0xff, 0xff, 0xff, 0xfb, 0xf3, 0xfe, - 0xf3, 0xff, 0xff, 0xff, 0xff, - 0xbf, 0x87, 0xf8, 0xf8, 0xf9, 0x7f, 0xf9, 0xff, - 0xff, 0x7f, 0x8f, 0x8f, 0xf0, 0xf0, 0xf3, 0xff, - 0xf3, 0xfb, 0xff, 0xff, 0x8f, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf9, 0xff, - 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf3, 0xff, - 0xfb, 0xef, 0xff, 0xff, 0xff, - 0xff, 0x7f, 0xff, 0xf7, 0xf9, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfb, 0xfe, - 0xf3, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xf5, 0xff, 0xf1, 0xff, - 0xff, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xf3, 0xfe, - 0xfb, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0x7f, 0xff, 0xf1, 0xff, 0x85, 0xff, - 0xfe, 0xf1, 0xff, 0xff, 0xff, 0xff, 0xfb, 0xff, - 0xf3, 0xde, 0xff, 0xf3, 0xff, - 0xbf, 0xff, 0x0f, 0x9f, 0xfa, 0x9f, 0xeb, 0xf2, - 0xe7, 0xff, 0x7b, 0xff, 0x4f, 0x73, 0x31, 0x81, - 0x5f, 0xf1, 0xfe, 0xff, 0xbf, - 0xff, 0xaf, 0x7f, 0x94, 0xfb, 0xfe, 0x8f, 0xff, - 0x00, 0xff, 0xf0, 0xef, 0xef, 0x5f, 0xfb, 0xf5, - 0x8f, 0x7f, 0x81, 0x5e, 0xf1, - 0xff, 0xf9, 0xff, 0xef, 0x86, 0x0f, 0x71, 0xf6, - 0xff, 0x7f, 0x7f, 0x97, 0xcf, 0xfd, 0xbf, 0x5f, - 0xf9, 0xf1, 0xf3, 0xff, 0xff, - 0x3f, 0xdb, 0xed, 0x1e, 0xff, 0xf6, 0x95, 0x9a, - 0x6f, 0x3d, 0xff, 0xf8, 0xfb, 0xdf, 0xf7, 0xfd, - 0xfb, 0xf7, 0xfd, 0xed, 0xde, - 0x7f, 0xf0, 0xf7, 0x87, 0x7f, 0x9b, 0xff, 0xec, - 0x9f, 0xbf, 0x7f, 0xcd, 0x7f, 0xf7, 0x3b, 0xad, - 0x7e, 0xf8, 0xff, 0xbb, 0x79, - 0xff, 0xff, 0xe3, 0x7c, 0x01, 0x8d, 0xf5, 0xfb, - 0xe7, 0xf7, 0xff, 0xff, 0x9e, 0x7d, 0x0f, 0x7f, - 0xf1, 0xcd, 0xfe, 0xf7, 0xff, - 0x3f, 0xd7, 0xf4, 0x9a, 0xf7, 0xed, 0xff, 0xf3, - 0xb7, 0xff, 0xef, 0xff, 0xbd, 0xe7, 0x5f, 0xbd, - 0xff, 0xef, 0xfe, 0x7f, 0xf1, - 0x3f, 0xff, 0xe7, 0xff, 0xcf, 0xfa, 0xf8, 0xff, - 0xff, 0xdf, 0xbf, 0xfe, 0xdf, 0xff, 0xd3, 0x1f, - 0xfd, 0xef, 0x7f, 0xff, 0xcf, - 0x7f, 0xff, 0x93, 0xdf, 0xf0, 0xef, 0xf3, 0xd4, - 0x77, 0x6f, 0xff, 0xff, 0xbf, 0x7f, 0x7d, 0xfd, - 0x7f, 0x7d, 0xff, 0xff, 0xf7, - 0xff, 0xf7, 0xdf, 0xfb, 0xbc, 0xef, 0xff, 0xfd, - 0xff, 0xff, 0xfc, 0x7f, 0xb7, 0xff, 0xfd, 0x5f, - 0xcf, 0xff, 0xef, 0x7f, 0xfd, - 0xff, 0xee, 0x87, 0xef, 0x92, 0xf0, 0x7e, 0xe5, - 0xbf, 0x8f, 0x7f, 0x60, 0xd9, 0xdb, 0x71, 0xb3, - 0x2d, 0x49, 0x6c, 0x29, 0x7f, - 0xbf, 0xff, 0xe4, 0x6f, 0xf3, 0xfa, 0x57, 0xfd, - 0xff, 0xfe, 0xb7, 0x7f, 0xfc, 0xff, 0x73, 0xdf, - 0xf3, 0x7f, 0xfd, 0xff, 0xbf, - 0xff, 0xef, 0x8b, 0x7f, 0x8f, 0xff, 0xf2, 0xff, - 0xff, 0xf7, 0xfb, 0xff, 0xff, 0xdf, 0xed, 0xef, - 0xf1, 0xf7, 0xfd, 0xdf, 0xf7, - 0xff, 0xff, 0xff, 0xf7, 0xe7, 0xe6, 0xf1, 0xff, - 0xdf, 0xfb, 0xe9, 0xfe, 0xbf, 0xff, 0xbf, 0x5f, - 0xff, 0xbf, 0x0e, 0x75, 0xfa, - 0xff, 0xff, 0xff, 0x6f, 0xfb, 0xf9, 0xff, 0xff, - 0xf3, 0xff, 0xfb, 0xbf, 0xef, 0xff, 0xf3, 0x7f, - 0xff, 0xff, 0xff, 0xfb, 0xff, - 0xff, 0x38, 0xf8, 0xf7, 0xff, 0xff, 0xdf, 0x9f, - 0xf7, 0x0b, 0x0f, 0xf5, 0xf5, 0xff, 0xff, 0xff, - 0xbf, 0xf7, 0xf3, 0x8e, 0x0e, - 0xbf, 0xe8, 0x6f, 0xef, 0x7f, 0xff, 0xdf, 0xdf, - 0xef, 0x88, 0xf5, 0x91, 0xfb, 0xff, 0xff, 0xbf, - 0xfe, 0xbf, 0xa6, 0x81, 0x71, - 0xff, 0xf0, 0xf8, 0xff, 0x67, 0xef, 0xff, 0xb7, - 0xf7, 0x8f, 0x2f, 0xd1, 0x41, 0xff, 0xcf, 0x5f, - 0xfe, 0xff, 0x7b, 0x8f, 0x9f, - 0xff, 0xf8, 0x6f, 0xef, 0xf7, 0xe7, 0xff, 0xff, - 0xbf, 0x8f, 0xd1, 0xf1, 0xcf, 0xdf, 0xcf, 0xdf, - 0xff, 0xff, 0x9f, 0x8f, 0xe1, - 0xff, 0xe7, 0xff, 0xf7, 0xe7, 0x6f, 0xf7, 0xe7, - 0xe7, 0x77, 0xef, 0xef, 0x6f, 0xff, 0xff, 0xdf, - 0xff, 0xdf, 0xdf, 0xff, 0xff, - 0xff, 0xa7, 0x6f, 0xff, 0xf7, 0xef, 0x97, 0xe7, - 0xf0, 0xef, 0x7f, 0xaf, 0x4f, 0xff, 0xff, 0xdf, - 0xbf, 0x5f, 0xe0, 0x7f, 0xef, - 0x7f, 0xa0, 0xef, 0xff, 0xe7, 0xff, 0xf7, 0xf7, - 0xff, 0x8b, 0xbf, 0xf8, 0xdf, 0xff, 0xcf, 0x7e, - 0xff, 0xdf, 0x7f, 0x8e, 0x5f, - 0xff, 0xff, 0x38, 0xff, 0xf7, 0xff, 0xf7, 0xf7, - 0xf7, 0xf7, 0x8f, 0xf7, 0xf8, 0xf7, 0xcf, 0xff, - 0xff, 0xff, 0xfe, 0xcb, 0x3f, - 0x3f, 0x9f, 0x7f, 0xf8, 0xff, 0xef, 0xff, 0xff, - 0x8f, 0xff, 0xf0, 0xaf, 0xff, 0xf0, 0xff, 0xdf, - 0xff, 0xff, 0xae, 0x7f, 0xc1, - 0x7f, 0xf0, 0x7f, 0xff, 0xff, 0xff, 0xef, 0xff, - 0xf7, 0xbf, 0xbf, 0xd0, 0xff, 0xff, 0xff, 0xff, - 0x8f, 0xff, 0xff, 0x9b, 0xff, - 0x7f, 0xcf, 0xf8, 0xff, 0x8f, 0x6f, 0xe0, 0xd7, - 0xf7, 0xf7, 0xff, 0xfe, 0xf0, 0xfe, 0x8f, 0x5e, - 0xd1, 0xff, 0xdf, 0xdf, 0xbe, - 0xff, 0x84, 0x7f, 0xf8, 0xff, 0x7f, 0xdf, 0xff, - 0xff, 0xaf, 0x7f, 0x81, 0x7f, 0xf5, 0xff, 0xff, - 0xff, 0xff, 0xfa, 0x9f, 0x3f, - 0xff, 0xd8, 0xff, 0xf8, 0xff, 0xff, 0xff, 0xff, - 0x7f, 0x0f, 0xff, 0x85, 0x7f, 0xf0, 0xff, 0xfe, - 0xbf, 0xff, 0xdf, 0x6f, 0xbf, - 0xff, 0xff, 0xff, 0xff, 0xaf, 0xff, 0xf7, 0xdf, - 0xf7, 0x47, 0xf4, 0xff, 0xef, 0xff, 0xdf, 0x7f, - 0xff, 0xbf, 0xcf, 0x5a, 0xf1, - 0xff, 0xbf, 0xbf, 0xff, 0xff, 0x3f, 0x8f, 0xc0, - 0xf3, 0xd1, 0xff, 0xfb, 0xef, 0xff, 0xdf, 0xbe, - 0x0f, 0x25, 0xe9, 0xd1, 0xff, - 0xff, 0xff, 0xdf, 0xff, 0xff, 0xff, 0xf7, 0xf7, - 0x2f, 0xaf, 0xf3, 0xfb, 0xef, 0xff, 0xff, 0xfe, - 0xff, 0xff, 0xcf, 0xbf, 0xfb, - 0xbf, 0x87, 0xf8, 0xf8, 0xdf, 0x7f, 0xff, 0xff, - 0xff, 0xff, 0x8f, 0x8f, 0xe0, 0xf0, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0x8f, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0x7f, 0xff, 0xff, 0xff, 0xff, 0xfb, 0xeb, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xfe, - 0xfb, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xdf, 0xf7, 0xff, 0xff, 0xcf, 0xff, - 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xeb, 0xfe, - 0xcf, 0xff, 0x7b, 0xfd, 0xff, - 0xff, 0xff, 0xff, 0xf7, 0xdf, 0xff, 0xbf, 0xff, - 0xfb, 0xff, 0xff, 0xfb, 0xff, 0xff, 0xef, 0xff, - 0xfb, 0xbe, 0xff, 0xff, 0xff, - 0xff, 0xff, 0x8f, 0xff, 0xf0, 0xd4, 0xbf, 0xf0, - 0xbf, 0xff, 0xff, 0xff, 0x93, 0x2f, 0xfd, 0xad, - 0xf7, 0x75, 0xff, 0xff, 0xfe, - 0xbf, 0x7f, 0xff, 0x9a, 0xff, 0xf4, 0x0f, 0xff, - 0x00, 0xde, 0xf0, 0xf3, 0xf9, 0xbf, 0x7d, 0xff, - 0x8f, 0x7f, 0x81, 0x0f, 0xd1, - 0xff, 0xfb, 0xdf, 0xee, 0x8b, 0x0b, 0x78, 0xf0, - 0xff, 0xfa, 0x7f, 0xbf, 0xff, 0xd5, 0x8f, 0x8f, - 0xe1, 0xf7, 0xfb, 0xfb, 0xff, - 0x7f, 0xb7, 0x99, 0xef, 0xdf, 0xf4, 0xff, 0xff, - 0xe4, 0xf4, 0x5d, 0xf6, 0xef, 0x9f, 0xef, 0xf7, - 0x3b, 0x3f, 0xdf, 0xbf, 0xec, - 0xff, 0xec, 0xf7, 0xb9, 0x6b, 0xbc, 0xfb, 0xf7, - 0xef, 0xff, 0x7e, 0xfd, 0x7e, 0xbb, 0xdf, 0x85, - 0xfe, 0xf7, 0xff, 0x7b, 0x7f, - 0xff, 0xff, 0xa7, 0xee, 0xe7, 0x5f, 0xe0, 0xf0, - 0xff, 0xff, 0xff, 0x5f, 0xe6, 0x6f, 0x81, 0x8d, - 0xd5, 0xf7, 0xbf, 0xef, 0xb6, - 0xff, 0xd7, 0xf4, 0xee, 0xb7, 0x7c, 0xff, 0xd7, - 0xaf, 0x7f, 0xed, 0x9f, 0xe5, 0xbf, 0xf7, 0x7d, - 0xfb, 0xb7, 0xad, 0xd7, 0xfd, - 0xbf, 0xff, 0xff, 0xc7, 0x8b, 0xff, 0xf0, 0xf6, - 0xff, 0xfd, 0xfb, 0xff, 0xdf, 0xbe, 0x0f, 0x7f, - 0xd5, 0xf7, 0xff, 0xf2, 0xfe, - 0xff, 0xff, 0xc5, 0xff, 0xf0, 0x7c, 0xff, 0xad, - 0x7f, 0x7f, 0xef, 0xff, 0xcf, 0x4f, 0xf1, 0xf5, - 0x7b, 0xdd, 0xff, 0xdf, 0xff, - 0xff, 0x77, 0xef, 0xff, 0xd8, 0xbf, 0xf7, 0xf3, - 0x5f, 0xfb, 0xf9, 0x7f, 0xe7, 0xff, 0xd7, 0x7f, - 0xad, 0xff, 0xfb, 0xfb, 0xf3, - 0xff, 0xcc, 0x95, 0x8f, 0xd8, 0xf3, 0xfc, 0xbc, - 0xdc, 0xdf, 0xbb, 0x44, 0x8b, 0xcb, 0x87, 0xb1, - 0xb7, 0xa7, 0x97, 0xee, 0xf3, - 0xff, 0x7f, 0xb4, 0xbf, 0xff, 0xc7, 0x7f, 0xcb, - 0xfd, 0xbf, 0x7f, 0x7f, 0x74, 0xe7, 0xdf, 0xf5, - 0xbb, 0xcf, 0xed, 0xfe, 0xfd, - 0xff, 0x3f, 0xfb, 0x77, 0xcc, 0xbb, 0xf0, 0xfb, - 0xff, 0xef, 0xbe, 0xff, 0xcf, 0xff, 0x85, 0x3f, - 0xb5, 0xff, 0xf7, 0x37, 0x7f, - 0x3f, 0xf7, 0xbf, 0xcf, 0x9f, 0xd7, 0xf7, 0xef, - 0xff, 0x78, 0xe7, 0xff, 0xff, 0xff, 0x1f, 0x7f, - 0x65, 0xbf, 0xbf, 0xff, 0xe7, - 0xff, 0xff, 0xff, 0xff, 0xdb, 0xf7, 0xdf, 0xff, - 0x77, 0x7f, 0xff, 0xff, 0xbf, 0xbf, 0xde, 0x77, - 0xdd, 0xff, 0xff, 0xfe, 0xff, - 0xbf, 0x68, 0xf8, 0xff, 0xf7, 0xff, 0xcf, 0xcf, - 0xf3, 0x17, 0x3f, 0xd5, 0xdd, 0xf7, 0xff, 0xff, - 0xcf, 0xdf, 0x73, 0x95, 0x3f, - 0xff, 0xac, 0x6f, 0xef, 0x77, 0xdf, 0xff, 0xf7, - 0xbb, 0x85, 0xdd, 0xe1, 0xf7, 0xfb, 0x7b, 0xdf, - 0xfe, 0xff, 0xb7, 0x9f, 0x79, - 0xff, 0xd8, 0xac, 0xfb, 0x47, 0xaf, 0xeb, 0xf7, - 0xff, 0xaf, 0x2e, 0x70, 0xd9, 0xf7, 0xfb, 0xdf, - 0xea, 0xfb, 0xfb, 0x1b, 0x5f, - 0xff, 0xf8, 0x6f, 0xaf, 0xd7, 0xb7, 0xeb, 0xff, - 0xe7, 0xaf, 0x7c, 0x70, 0xfb, 0xdf, 0xff, 0x7b, - 0xfb, 0xff, 0xda, 0x9f, 0xf9, - 0xff, 0x95, 0xbb, 0xfd, 0xc7, 0xcf, 0xfb, 0x83, - 0xef, 0xf3, 0xbf, 0xcf, 0x47, 0xf7, 0xe7, 0x1f, - 0xd7, 0x8b, 0x6f, 0x33, 0xbe, - 0xff, 0xc7, 0x6f, 0xfd, 0x97, 0x2f, 0xeb, 0xb7, - 0xdc, 0x77, 0xd7, 0x1f, 0x67, 0xf7, 0xe7, 0x9e, - 0xe7, 0xdb, 0x34, 0xdb, 0xfb, - 0x7f, 0xa8, 0xef, 0xff, 0xe7, 0xef, 0xff, 0xf7, - 0xff, 0x8b, 0xbf, 0xd8, 0xef, 0xff, 0xe7, 0xdf, - 0xf7, 0xfb, 0xf7, 0x9f, 0x66, - 0xff, 0xfb, 0x6c, 0xff, 0xb7, 0x9f, 0xcf, 0xcb, - 0xbb, 0x93, 0xaf, 0xff, 0xa8, 0xff, 0xc7, 0x3f, - 0xa7, 0xcf, 0xfe, 0xe3, 0x3f, - 0x3f, 0xdf, 0x7b, 0xfa, 0xff, 0xff, 0xff, 0xf7, - 0x8f, 0x3f, 0xd0, 0xd3, 0x7f, 0xfc, 0xff, 0x8e, - 0xff, 0xf3, 0x8f, 0x4e, 0xe4, - 0x7f, 0xb8, 0xff, 0xff, 0xff, 0xef, 0x8f, 0xdf, - 0xf3, 0xbb, 0x3f, 0xe0, 0xf3, 0xff, 0xff, 0xef, - 0x8f, 0xd7, 0xf3, 0xab, 0xef, - 0x7f, 0x8f, 0xf8, 0xfb, 0x8f, 0x6f, 0xa0, 0xff, - 0xff, 0xdc, 0xff, 0x5f, 0xfc, 0xf3, 0x8f, 0x6e, - 0xb1, 0xf7, 0xf7, 0xf7, 0x3e, - 0xff, 0x90, 0x7b, 0xf8, 0xff, 0x7f, 0xdf, 0xff, - 0xfb, 0x9f, 0x7f, 0xa0, 0xf3, 0xf1, 0xff, 0xff, - 0xdf, 0xff, 0xdb, 0xbf, 0x3f, - 0xff, 0xdc, 0xfb, 0xf8, 0xff, 0xff, 0xff, 0xff, - 0x77, 0xaf, 0xff, 0xad, 0xf3, 0xf8, 0xff, 0xfe, - 0xef, 0xff, 0xff, 0x6f, 0xbf, - 0xff, 0xff, 0xcf, 0xff, 0xa3, 0xff, 0xaf, 0xcf, - 0x93, 0xc3, 0x74, 0xef, 0xdf, 0xff, 0xab, 0x2f, - 0xe7, 0xc7, 0xf3, 0x73, 0x79, - 0xff, 0xff, 0xcf, 0xff, 0xc3, 0x7f, 0x83, 0xe4, - 0xd3, 0xbc, 0x7b, 0xdb, 0xdf, 0x7f, 0x8b, 0x8e, - 0x83, 0x01, 0x51, 0xd5, 0x7b, - 0xff, 0xfb, 0xdf, 0xff, 0xc3, 0xef, 0xb3, 0xff, - 0xb7, 0xff, 0xfe, 0xbf, 0xdf, 0xff, 0x8b, 0x6e, - 0xf3, 0xfb, 0xb7, 0x1f, 0xfe, - 0xbf, 0x82, 0xc8, 0xf8, 0xd3, 0x7f, 0xf3, 0xfb, - 0xff, 0xef, 0x87, 0x87, 0xd0, 0x70, 0x8b, 0xff, - 0xf3, 0xff, 0xef, 0xef, 0x87, - 0xff, 0xff, 0xff, 0xff, 0x47, 0xff, 0xf3, 0xff, - 0x7f, 0xff, 0xff, 0xff, 0xdf, 0x7f, 0xcb, 0xef, - 0xf2, 0xf7, 0xff, 0xff, 0xff, - 0xff, 0xf7, 0xef, 0xf2, 0xf7, 0xff, 0xf7, 0xff, - 0xff, 0xff, 0xf7, 0xef, 0xdf, 0xf7, 0xca, 0x7f, - 0xf3, 0xf7, 0xef, 0xff, 0xf6, - 0xff, 0xfa, 0xfb, 0xff, 0xe7, 0xff, 0xf7, 0xff, - 0xff, 0xef, 0xff, 0xf7, 0x57, 0x7f, 0xca, 0xef, - 0xf3, 0xff, 0xff, 0xef, 0xef, - 0xff, 0xf6, 0xeb, 0xfa, 0xf7, 0xff, 0xf7, 0x8f, - 0xff, 0xe3, 0xf7, 0xef, 0xd7, 0xf7, 0xcb, 0x7f, - 0xf3, 0x8f, 0x6c, 0xf2, 0xe7, - 0xff, 0xff, 0x2f, 0xff, 0xf1, 0x9d, 0x9e, 0xf4, - 0xff, 0xff, 0xff, 0xef, 0x0f, 0xff, 0xf1, 0x09, - 0x3f, 0xf9, 0xbf, 0xf7, 0xfb, - 0xff, 0xef, 0x7f, 0xf6, 0xfb, 0xf5, 0x0f, 0xdf, - 0x00, 0xff, 0xd0, 0xbf, 0xc0, 0xbf, 0xf9, 0xff, - 0x8f, 0x7f, 0x81, 0x6f, 0xe1, - 0xff, 0xff, 0x9e, 0xaf, 0xf7, 0x0f, 0x18, 0xd9, - 0xbf, 0x6f, 0x37, 0xef, 0x8f, 0xff, 0x9e, 0x06, - 0x75, 0xf7, 0xf6, 0xff, 0xef, - 0x7f, 0xf7, 0xcf, 0xbb, 0xfb, 0x6d, 0xfb, 0xef, - 0x7d, 0xe9, 0xff, 0xff, 0xbf, 0xc2, 0xf7, 0x6f, - 0xff, 0xdc, 0xff, 0xf3, 0xfa, - 0x7f, 0x6f, 0xf7, 0xf9, 0xff, 0x6d, 0xfe, 0x9c, - 0xbf, 0xbf, 0x7d, 0xe2, 0x7f, 0x77, 0x9f, 0xcd, - 0xb7, 0xb5, 0xff, 0xff, 0x7f, - 0x7f, 0xff, 0x87, 0xae, 0x86, 0xdf, 0xc0, 0xfd, - 0xfb, 0xfa, 0xff, 0xff, 0x8e, 0x6d, 0xd5, 0x3d, - 0xf1, 0xff, 0xfe, 0xef, 0xff, - 0xff, 0x3f, 0xd2, 0xa4, 0xfe, 0xd9, 0xf7, 0xfe, - 0xdf, 0xff, 0xcb, 0xff, 0xdd, 0x7e, 0xbb, 0xdd, - 0x5f, 0xf7, 0xbf, 0xff, 0xed, - 0xff, 0xfe, 0xf5, 0xff, 0xb7, 0xf6, 0xb4, 0xae, - 0xfe, 0xef, 0xf7, 0xff, 0xff, 0x8f, 0x07, 0x7b, - 0xfb, 0xff, 0x7f, 0xff, 0xf7, - 0xff, 0x7b, 0xa3, 0xbf, 0xe3, 0xff, 0xff, 0xf7, - 0xf7, 0xfd, 0xdf, 0xff, 0x8f, 0x7f, 0x9b, 0xdf, - 0xfb, 0xef, 0xfe, 0xff, 0x3f, - 0xff, 0x6f, 0xff, 0x7f, 0xb3, 0x7f, 0xdf, 0xbd, - 0x78, 0xff, 0xff, 0xff, 0xff, 0x7f, 0xef, 0xff, - 0xdf, 0xee, 0x9d, 0xfd, 0xef, - 0xff, 0xf8, 0x05, 0x8e, 0xb0, 0x58, 0xf7, 0xfc, - 0xa4, 0x85, 0xdd, 0xbc, 0x0b, 0x05, 0x61, 0xf8, - 0xb7, 0xff, 0xeb, 0xef, 0x7f, - 0xbf, 0xff, 0xc7, 0xbb, 0xd8, 0x6f, 0x79, 0xde, - 0xff, 0xff, 0xcf, 0xff, 0xba, 0xaf, 0xd9, 0x7b, - 0xfd, 0xff, 0xf5, 0xdf, 0xbf, - 0xff, 0xff, 0xaf, 0x7f, 0x88, 0x7f, 0xf0, 0xea, - 0xfe, 0x7f, 0xf2, 0xff, 0xdf, 0xd7, 0x4f, 0x7f, - 0xe3, 0xde, 0xff, 0xff, 0xf7, - 0xff, 0x7d, 0x6f, 0x5f, 0xab, 0xff, 0x7a, 0xb6, - 0xbf, 0x78, 0xdd, 0x7f, 0xde, 0xef, 0x4b, 0x9b, - 0xeb, 0x7f, 0x76, 0x9f, 0xac, - 0xff, 0xcf, 0xff, 0xff, 0xb7, 0x7f, 0xae, 0xef, - 0xdb, 0xef, 0xff, 0xf7, 0xff, 0xfb, 0xe7, 0xfe, - 0xbf, 0x7e, 0xfb, 0xf7, 0xfb, - 0xff, 0xff, 0xff, 0xff, 0xcf, 0xbf, 0xff, 0xff, - 0xf3, 0xff, 0xff, 0xff, 0xff, 0xef, 0xd7, 0xff, - 0xe9, 0xef, 0xf2, 0xfe, 0xff, - 0xff, 0xff, 0xff, 0xef, 0xff, 0x8d, 0xaf, 0xf2, - 0x71, 0xff, 0xfe, 0xff, 0xff, 0x7f, 0xff, 0xcf, - 0x0f, 0x75, 0xf1, 0xff, 0xff, - 0x3f, 0x78, 0xbc, 0xfb, 0xfe, 0xff, 0xff, 0xbf, - 0xf3, 0x0f, 0x3f, 0xc9, 0xe9, 0x7f, 0xf7, 0xdf, - 0xff, 0xff, 0x57, 0x82, 0x2e, - 0xff, 0xf8, 0x7f, 0xfb, 0x7a, 0xfb, 0xff, 0xff, - 0xeb, 0x87, 0x9b, 0xf1, 0xe5, 0x7f, 0x75, 0x7f, - 0xfe, 0xff, 0xbe, 0x39, 0x79, - 0xff, 0xcd, 0xbf, 0xfd, 0x7e, 0xff, 0xfb, 0xf4, - 0xf7, 0xed, 0x6f, 0xf3, 0x6b, 0xf7, 0xd7, 0xbf, - 0xfe, 0xfd, 0xf7, 0x8f, 0xef, - 0xff, 0xf8, 0x7a, 0xff, 0xfa, 0xf0, 0xff, 0xff, - 0xe6, 0x8f, 0x9b, 0xf1, 0xad, 0xbf, 0xf7, 0xfd, - 0xbf, 0xfd, 0xef, 0x8f, 0x3f, - 0xbf, 0xff, 0xad, 0xfb, 0xf4, 0xbf, 0xf3, 0x90, - 0xdd, 0xf0, 0xfa, 0xcf, 0xe7, 0xf2, 0xf7, 0x7f, - 0xff, 0xad, 0xff, 0xf5, 0xdf, - 0xff, 0xcb, 0x7b, 0xfa, 0xd2, 0x7f, 0xc7, 0xf3, - 0xa9, 0xf7, 0xe7, 0x8b, 0xe7, 0xf1, 0xf7, 0x3f, - 0x8f, 0x7f, 0xb0, 0xdf, 0xfd, - 0x7f, 0xf8, 0xff, 0xfb, 0xf2, 0xff, 0xd2, 0xe3, - 0xdf, 0x87, 0xd3, 0xf0, 0xed, 0xff, 0xf7, 0x7f, - 0xff, 0xef, 0x77, 0xaa, 0x7f, - 0xbf, 0xea, 0xfc, 0xfb, 0xb3, 0xbf, 0xb3, 0xff, - 0xd8, 0x93, 0xab, 0xf1, 0xac, 0xff, 0xf7, 0x3f, - 0xaf, 0xef, 0xed, 0xc7, 0xad, - 0xff, 0xd6, 0xff, 0xfd, 0xff, 0xdf, 0xff, 0xf4, - 0x8f, 0xbf, 0xb1, 0xed, 0xf5, 0xfb, 0xff, 0xef, - 0xff, 0xf5, 0x8f, 0xdf, 0xf1, - 0xff, 0xf8, 0xfe, 0xfb, 0xff, 0xdf, 0x9f, 0xf8, - 0xf0, 0xef, 0xff, 0xd0, 0xfd, 0xf7, 0xff, 0xef, - 0xaf, 0xf5, 0xf7, 0xce, 0x7f, - 0x7f, 0x83, 0x7d, 0xfa, 0x8f, 0x5f, 0xd0, 0xcb, - 0xe9, 0xbb, 0x76, 0x9f, 0x7b, 0xf7, 0x8f, 0x6e, - 0xb1, 0xd5, 0xf7, 0x5f, 0xc6, - 0xff, 0xa3, 0x7f, 0xfc, 0xff, 0x7f, 0xff, 0xff, - 0xdf, 0x9f, 0x7f, 0xa8, 0x77, 0xf5, 0xff, 0xfe, - 0xff, 0xff, 0xfe, 0x7f, 0x2d, - 0xff, 0x96, 0xff, 0xfc, 0xff, 0xff, 0xff, 0xff, - 0x37, 0x6f, 0xfd, 0xaf, 0x77, 0xf8, 0xff, 0xff, - 0xef, 0xff, 0xff, 0xff, 0x4b, - 0xff, 0x72, 0xaf, 0xff, 0xe5, 0xdf, 0x99, 0xfc, - 0x10, 0x63, 0xf0, 0xef, 0xef, 0x7f, 0x8b, 0xef, - 0xaf, 0xe7, 0xf5, 0xf7, 0x7d, - 0xff, 0xef, 0x8f, 0xff, 0xa1, 0x4f, 0x81, 0xe7, - 0xb0, 0xfa, 0xfe, 0xd7, 0xcf, 0xff, 0xca, 0xcf, - 0x0b, 0x85, 0x71, 0xfa, 0xff, - 0xff, 0xdd, 0xef, 0xff, 0xa4, 0xdf, 0xb1, 0xdc, - 0xd3, 0xff, 0xf8, 0xff, 0xcf, 0x7f, 0xcb, 0x7f, - 0xff, 0xf5, 0xff, 0xbf, 0xfd, - 0xbf, 0x82, 0xc8, 0xf8, 0xe1, 0x7f, 0xf0, 0xdf, - 0xff, 0xef, 0x87, 0x87, 0xc0, 0xf0, 0xca, 0x2f, - 0xfb, 0xff, 0xef, 0xee, 0x97, - 0xff, 0xff, 0x8f, 0xfa, 0xa5, 0xdf, 0xd1, 0xf7, - 0x7f, 0xff, 0xff, 0xff, 0xff, 0x77, 0xdb, 0xef, - 0xff, 0xf5, 0xfd, 0xff, 0xff, - 0xff, 0xf6, 0x8b, 0xf2, 0x94, 0xff, 0xf1, 0xf7, - 0xff, 0xff, 0xf7, 0xef, 0xd7, 0xf7, 0xdf, 0xee, - 0xfb, 0xff, 0xef, 0xff, 0xf7, - 0xff, 0xf3, 0xcb, 0xff, 0xe6, 0xff, 0xf1, 0xef, - 0xff, 0xef, 0xff, 0xe7, 0xd7, 0x7f, 0xdb, 0x7e, - 0xfd, 0xf7, 0xff, 0xef, 0xef, - 0xff, 0xfe, 0xcf, 0xff, 0xc4, 0xff, 0xf2, 0x9f, - 0xff, 0xe0, 0xf7, 0xff, 0xdf, 0xff, 0xdf, 0x7f, - 0xdb, 0xac, 0xef, 0xf1, 0xf7, - 0x7f, 0xef, 0x0f, 0x5f, 0xb4, 0x8f, 0xff, 0xf6, - 0xfd, 0xff, 0x6f, 0xff, 0x8f, 0xff, 0xe9, 0x8d, - 0x7f, 0xf1, 0xd1, 0xf7, 0xfe, - 0xff, 0xe7, 0x7f, 0x87, 0xfd, 0xe7, 0x8f, 0x9b, - 0x00, 0xff, 0xb0, 0x7d, 0xfd, 0xcf, 0xfb, 0xfd, - 0x8f, 0x7f, 0x81, 0x6d, 0xd1, - 0xff, 0xbc, 0xed, 0xff, 0x86, 0x6e, 0x10, 0xf1, - 0xf4, 0x5f, 0x7f, 0xfe, 0x9f, 0x37, 0xc3, 0x8f, - 0xf7, 0xe5, 0xfb, 0xff, 0xff, - 0x7f, 0xfe, 0xff, 0xfd, 0x97, 0xff, 0xfb, 0xff, - 0x3f, 0xff, 0xf9, 0xc3, 0x1f, 0xf8, 0xff, 0xb5, - 0x5f, 0xef, 0xdc, 0xff, 0xbe, - 0xff, 0xcb, 0xe7, 0xfd, 0x69, 0xe7, 0xfc, 0xb6, - 0xef, 0x9a, 0x77, 0xb6, 0x67, 0xdf, 0xef, 0xf7, - 0xfe, 0xdf, 0xff, 0xf5, 0x7f, - 0xff, 0xf7, 0x97, 0xbe, 0xf4, 0xff, 0xf1, 0xba, - 0xfe, 0xff, 0x97, 0x7f, 0xbf, 0x77, 0x55, 0xdf, - 0xd9, 0xf1, 0xff, 0xdf, 0xff, - 0xbf, 0xbf, 0x72, 0xf2, 0xdd, 0xff, 0xe4, 0xff, - 0x7f, 0xef, 0xff, 0xf7, 0xfe, 0xfb, 0xb9, 0xff, - 0xff, 0xf5, 0xff, 0xfb, 0x96, - 0xff, 0x7f, 0xf7, 0xef, 0x83, 0xf7, 0xf7, 0xff, - 0xdf, 0xff, 0xf7, 0xfd, 0xd5, 0x9f, 0x0f, 0x7f, - 0xf0, 0xfb, 0xae, 0xff, 0xec, - 0xff, 0x7b, 0x85, 0xf7, 0xf3, 0xef, 0xff, 0xf7, - 0xff, 0xed, 0xff, 0xe7, 0x8f, 0x7f, 0xc7, 0x97, - 0x3f, 0xfc, 0xff, 0xf7, 0xde, - 0xff, 0xfd, 0x8b, 0xff, 0xf7, 0x1f, 0xfb, 0xff, - 0x2f, 0xfb, 0xf7, 0xff, 0xbf, 0xff, 0xff, 0x6f, - 0xf7, 0xf9, 0xe7, 0xff, 0x33, - 0xff, 0x9e, 0xe7, 0x8b, 0xf0, 0x50, 0xf1, 0xde, - 0xff, 0x9f, 0x1b, 0x28, 0x1b, 0x8d, 0xb4, 0xe1, - 0xf5, 0xf3, 0xfe, 0xef, 0xfa, - 0xff, 0x7f, 0xf6, 0xff, 0xfe, 0x07, 0xec, 0xfb, - 0x7f, 0xff, 0xfd, 0xff, 0xd6, 0x8f, 0xff, 0xf9, - 0xff, 0x37, 0x7f, 0xfb, 0xdd, - 0xff, 0xff, 0xff, 0x63, 0xef, 0xdf, 0xfa, 0xf1, - 0xff, 0xfc, 0xfe, 0xdf, 0xfb, 0xff, 0x8f, 0x7f, - 0xf9, 0xeb, 0xef, 0xfd, 0xff, - 0x7f, 0x7f, 0xfb, 0xe7, 0x9f, 0x9b, 0xe5, 0xcf, - 0xfd, 0xf7, 0xcf, 0xff, 0xf3, 0xbf, 0x5f, 0x5d, - 0x67, 0x9f, 0xdf, 0x7f, 0xf9, - 0xff, 0xff, 0xff, 0x1f, 0xfe, 0xff, 0xf5, 0xdf, - 0x5f, 0xfb, 0xfb, 0xbf, 0xcf, 0xdf, 0xfb, 0x7f, - 0xb7, 0xff, 0xfb, 0xff, 0xf7, - 0x3f, 0x78, 0xfc, 0xf3, 0xff, 0xff, 0xdf, 0xbf, - 0xf3, 0x05, 0x3f, 0xc1, 0xf1, 0xff, 0xf7, 0xef, - 0xef, 0xff, 0xfb, 0x97, 0x1f, - 0xff, 0xd8, 0x7f, 0xfb, 0x7b, 0xfb, 0xf7, 0xbf, - 0xbb, 0x8e, 0xd3, 0xe1, 0xff, 0xfd, 0xf7, 0xed, - 0xfe, 0x8b, 0x5e, 0x0f, 0x69, - 0xbf, 0xf8, 0xfc, 0xfb, 0x7b, 0xff, 0xdf, 0xed, - 0xf7, 0x8f, 0xbf, 0xce, 0xfe, 0xff, 0xf3, 0xff, - 0xf6, 0xff, 0xfe, 0x8f, 0x0f, - 0xff, 0xfc, 0x7d, 0xff, 0xff, 0xfd, 0xf3, 0xff, - 0xbf, 0x8f, 0xe3, 0xf0, 0xfe, 0xff, 0xfb, 0xff, - 0xff, 0xff, 0xae, 0x0f, 0x5d, - 0xff, 0xff, 0xff, 0xf3, 0xff, 0xff, 0xff, 0xd1, - 0xfd, 0xf1, 0xd3, 0xdd, 0xfd, 0xf1, 0xfb, 0xff, - 0xfb, 0xfd, 0xff, 0xfd, 0xf6, - 0xff, 0xff, 0x7f, 0xfe, 0xfd, 0xff, 0x87, 0xff, - 0xcc, 0xf3, 0xdf, 0xcb, 0xff, 0xfb, 0xfb, 0xff, - 0x8b, 0x7f, 0xfa, 0xff, 0xff, - 0x7f, 0xf8, 0xfe, 0xff, 0xef, 0xff, 0xff, 0xff, - 0xff, 0x8f, 0xff, 0xe8, 0xff, 0xff, 0xf9, 0xff, - 0xfb, 0xaf, 0x7f, 0x8f, 0x7e, - 0xbf, 0xfb, 0x78, 0xff, 0xff, 0xff, 0xd7, 0xd3, - 0xfd, 0xc3, 0x95, 0xc5, 0xf8, 0xff, 0xfb, 0xff, - 0xfb, 0xff, 0xfc, 0xf2, 0x8f, - 0xff, 0x8b, 0x7b, 0xff, 0xff, 0xdf, 0xff, 0xfd, - 0x8f, 0xef, 0xf4, 0xb7, 0xff, 0xfe, 0xff, 0xfe, - 0xff, 0xff, 0x8f, 0x6f, 0xf4, - 0xff, 0xfd, 0xff, 0xfe, 0xff, 0xdf, 0xdf, 0xfd, - 0xf1, 0xb3, 0xff, 0xd8, 0xff, 0xfd, 0xff, 0xff, - 0xff, 0xfd, 0xf5, 0x87, 0x7f, - 0x3f, 0xef, 0xff, 0xff, 0x8f, 0x5f, 0xf0, 0xff, - 0xf1, 0xef, 0x39, 0xc7, 0x7e, 0xf3, 0x8f, 0x7e, - 0xf1, 0xbd, 0xed, 0xfa, 0x9a, - 0xff, 0x98, 0x7f, 0xfa, 0xff, 0x7f, 0xff, 0xff, - 0xf3, 0x8f, 0x5f, 0x8a, 0xfb, 0xf1, 0xff, 0xff, - 0xff, 0xff, 0xf7, 0xaf, 0x5f, - 0xff, 0x86, 0xff, 0xfc, 0xff, 0xff, 0xdf, 0xff, - 0x7f, 0xbf, 0xcb, 0xbd, 0x77, 0xf2, 0xff, 0xfe, - 0xff, 0xff, 0xff, 0xbf, 0x77, - 0xff, 0xff, 0xef, 0xff, 0xed, 0xdf, 0xff, 0xdd, - 0x11, 0x73, 0xfc, 0xfc, 0xef, 0xff, 0xfb, 0xff, - 0xfd, 0xfd, 0x9d, 0xf3, 0xff, - 0xff, 0xf9, 0xef, 0xff, 0xf3, 0x0f, 0x83, 0x9e, - 0xf0, 0xf0, 0xff, 0xed, 0xef, 0xff, 0xe9, 0x8e, - 0x7b, 0x9d, 0x70, 0xe9, 0xf7, - 0xff, 0xff, 0xef, 0xff, 0xef, 0xff, 0xf3, 0xf9, - 0xf7, 0xff, 0xfb, 0xf3, 0xef, 0xfd, 0xeb, 0xfe, - 0xff, 0xff, 0xff, 0xff, 0xf8, - 0x3f, 0x87, 0xec, 0xf8, 0xe5, 0x7f, 0xfb, 0xff, - 0xff, 0xff, 0x8f, 0x87, 0xe8, 0xf0, 0xeb, 0xff, - 0xff, 0xfd, 0xff, 0xff, 0x97, - 0xff, 0xff, 0xef, 0xff, 0xed, 0xdf, 0xd3, 0xbf, - 0x7f, 0xfe, 0xff, 0xff, 0xef, 0xff, 0xe3, 0xef, - 0xff, 0xff, 0xff, 0xfe, 0xff, - 0xff, 0x77, 0xef, 0xfa, 0xe7, 0xff, 0xfb, 0xff, - 0xff, 0xff, 0xef, 0xef, 0xef, 0xf7, 0xea, 0xfe, - 0xf3, 0xff, 0xff, 0xff, 0xfe, - 0xff, 0xf7, 0xeb, 0xf3, 0xef, 0xff, 0xf3, 0xff, - 0xf1, 0x7f, 0xff, 0xe7, 0xe7, 0xf7, 0xea, 0xee, - 0xf7, 0xff, 0xf1, 0xff, 0xe7, - 0xff, 0xfb, 0x6f, 0xf6, 0xe5, 0xff, 0xeb, 0xdf, - 0xef, 0xff, 0xff, 0xf7, 0xef, 0xff, 0xe3, 0xef, - 0xff, 0xfe, 0xff, 0xfe, 0xf7, - 0x7f, 0xff, 0x4f, 0x5f, 0xe1, 0xc7, 0xef, 0xf1, - 0xfe, 0x7f, 0x7b, 0xff, 0x6f, 0xff, 0x93, 0x0b, - 0x7f, 0xf1, 0xfa, 0xdf, 0xff, - 0xff, 0xfb, 0x7f, 0xdf, 0xf7, 0xef, 0x8f, 0xff, - 0x00, 0xef, 0xf0, 0xdf, 0x7f, 0xef, 0xff, 0xfb, - 0x8f, 0x7f, 0x81, 0x6f, 0xd1, - 0xff, 0xde, 0xff, 0xef, 0xb9, 0x49, 0x74, 0xf3, - 0xef, 0x7b, 0x7f, 0xff, 0xeb, 0xf7, 0x85, 0x67, - 0xf1, 0xf0, 0xe1, 0xff, 0xf7, - 0x3f, 0xab, 0xff, 0xc4, 0xbb, 0xff, 0x8c, 0x9d, - 0x7e, 0x3a, 0xb5, 0xbb, 0xe3, 0xfb, 0xf3, 0xcd, - 0xe3, 0xff, 0xff, 0xff, 0xfe, - 0xff, 0xdc, 0xf7, 0xf8, 0x77, 0x8f, 0xf7, 0xfe, - 0x9f, 0x97, 0x7a, 0xf2, 0x7f, 0xfb, 0x8f, 0x1f, - 0x7d, 0xfd, 0xef, 0xb1, 0x7d, - 0xff, 0xef, 0xa6, 0xef, 0x98, 0x9d, 0xf0, 0xf4, - 0xf4, 0xff, 0xff, 0x7f, 0x8f, 0x7f, 0x89, 0x7f, - 0xe7, 0xff, 0xff, 0xf7, 0xfb, - 0xbf, 0xa7, 0xb7, 0xdf, 0xba, 0xfd, 0xfe, 0xeb, - 0xff, 0xff, 0xc4, 0xef, 0x8f, 0x7c, 0xf7, 0x8f, - 0x7f, 0xf9, 0xfb, 0xff, 0xfb, - 0xff, 0xff, 0xff, 0xeb, 0x87, 0xfd, 0xf4, 0xf7, - 0x6f, 0xff, 0xbf, 0xff, 0xff, 0xef, 0xef, 0xdf, - 0xff, 0xff, 0xff, 0xf7, 0xff, - 0xff, 0xf7, 0x85, 0xdb, 0xf2, 0xbf, 0xd7, 0xff, - 0xff, 0xfd, 0xff, 0xff, 0x8f, 0x7f, 0xf1, 0xaf, - 0x7d, 0xff, 0xf7, 0xeb, 0xff, - 0xbf, 0xfd, 0x8f, 0xff, 0xfa, 0x3f, 0xff, 0xf6, - 0xb7, 0xff, 0xfe, 0xfb, 0x8f, 0x7f, 0xff, 0xff, - 0xf3, 0xee, 0xbf, 0x7f, 0xff, - 0xff, 0x67, 0xc6, 0xaf, 0xc3, 0x74, 0xf7, 0xfe, - 0xee, 0x8a, 0x37, 0x6e, 0xec, 0x87, 0x71, 0x91, - 0x13, 0x7d, 0xec, 0x87, 0xff, - 0xbf, 0x7b, 0xf0, 0xef, 0xfb, 0x3f, 0xb7, 0xfc, - 0xff, 0xff, 0x97, 0x7d, 0xe8, 0xef, 0x9d, 0x77, - 0xfd, 0xfb, 0xff, 0xfb, 0xbf, - 0xff, 0xff, 0xde, 0x77, 0xcd, 0xff, 0xf1, 0xfb, - 0xff, 0xff, 0xf9, 0xe3, 0xff, 0xff, 0xef, 0xff, - 0xff, 0xfc, 0xcf, 0xff, 0xf3, - 0x7f, 0xff, 0xfe, 0x77, 0xaf, 0xf7, 0xf8, 0xef, - 0xff, 0x76, 0xfa, 0xff, 0x99, 0x6d, 0x9f, 0x6f, - 0xf1, 0xbf, 0x7f, 0x7f, 0xfc, - 0xff, 0xff, 0xef, 0xbf, 0xeb, 0xfa, 0xdd, 0xef, - 0xbc, 0xfd, 0xfd, 0xdf, 0xff, 0xf7, 0xff, 0xff, - 0xd1, 0xfe, 0xff, 0xfb, 0xff, - 0x3f, 0x70, 0xf8, 0xff, 0xf5, 0xff, 0xff, 0x9f, - 0xf3, 0x09, 0x1f, 0xe1, 0xf3, 0xfd, 0xfd, 0xff, - 0xef, 0xff, 0xf7, 0x8e, 0x1e, - 0xff, 0xf8, 0x7f, 0xff, 0x77, 0xff, 0xff, 0x9b, - 0x8f, 0x8e, 0xfb, 0xf1, 0xef, 0xe5, 0xfd, 0xef, - 0xfe, 0x9f, 0x16, 0x03, 0x61, - 0xbf, 0xf8, 0xfa, 0xfd, 0x73, 0xff, 0xff, 0xf7, - 0xf7, 0x8d, 0x9f, 0xe1, 0xf1, 0xff, 0xef, 0xff, - 0xfc, 0xff, 0xff, 0x8f, 0x0f, - 0xff, 0xf8, 0x7f, 0xff, 0xf7, 0xf7, 0xfd, 0xff, - 0x9f, 0x8f, 0xe1, 0xf1, 0xef, 0xff, 0xff, 0xff, - 0xfd, 0xff, 0x8e, 0x0f, 0x7d, - 0xff, 0xf7, 0xf9, 0xfe, 0xf3, 0x7f, 0xff, 0xf7, - 0xf7, 0xf3, 0xfd, 0xef, 0xff, 0xf9, 0xed, 0xff, - 0xff, 0xef, 0xff, 0xf7, 0xff, - 0xff, 0xf7, 0x7e, 0xfe, 0xf7, 0xff, 0x8b, 0xf7, - 0xfc, 0xeb, 0xeb, 0xed, 0xeb, 0xf9, 0xfd, 0xff, - 0x8f, 0x7f, 0xf2, 0xfe, 0xed, - 0x7f, 0xf8, 0xff, 0xff, 0xf7, 0xff, 0xff, 0xf7, - 0xf7, 0x8d, 0xff, 0xe8, 0xff, 0xfd, 0xef, 0xfe, - 0xfd, 0xdf, 0xf6, 0x8f, 0x6f, - 0xbf, 0xff, 0x78, 0xff, 0xf7, 0xff, 0xfb, 0xff, - 0xff, 0xe3, 0x9f, 0xe5, 0xea, 0xf5, 0xfd, 0xff, - 0xed, 0xef, 0xff, 0xf7, 0x8f, - 0xff, 0x07, 0xff, 0xf9, 0xff, 0xff, 0xff, 0xf7, - 0x8f, 0xff, 0xf3, 0x93, 0xff, 0xff, 0xff, 0xef, - 0xff, 0xf7, 0x8f, 0x7f, 0xf3, - 0xff, 0xf8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xf7, 0x8b, 0xff, 0xe8, 0xfb, 0xff, 0xff, 0xef, - 0xef, 0xf7, 0xf5, 0x8e, 0x7f, - 0x3f, 0xec, 0xf9, 0xfc, 0x8f, 0x7f, 0xf0, 0xef, - 0xef, 0xff, 0x1d, 0xed, 0x7e, 0xfd, 0x8f, 0x6e, - 0xf1, 0xd7, 0xf7, 0xde, 0x8c, - 0xff, 0x80, 0x7f, 0xf8, 0xff, 0x7f, 0xff, 0xff, - 0xfb, 0x8f, 0x6f, 0x90, 0xff, 0xf3, 0xff, 0xff, - 0xff, 0xff, 0xfe, 0x8f, 0x7f, - 0xff, 0x87, 0xfb, 0xf8, 0xff, 0xff, 0xff, 0xff, - 0x7f, 0x9f, 0xff, 0x93, 0x7f, 0xf2, 0xff, 0xfe, - 0xef, 0xff, 0xff, 0x9f, 0x69, - 0xff, 0xfb, 0xef, 0xff, 0xf6, 0xff, 0xff, 0xff, - 0x1f, 0x73, 0xfe, 0xf6, 0xff, 0xff, 0xff, 0xef, - 0xef, 0xe7, 0x97, 0x77, 0xf7, - 0xff, 0xff, 0xff, 0xff, 0xe7, 0x1f, 0x86, 0x95, - 0xf0, 0xf6, 0xf7, 0xff, 0xff, 0xff, 0xfe, 0x8f, - 0x6d, 0x93, 0x71, 0xf8, 0xfd, - 0xff, 0xfe, 0xef, 0xfd, 0xf2, 0xff, 0xf6, 0xff, - 0x6f, 0xef, 0xf7, 0xfd, 0xff, 0xff, 0xfd, 0xff, - 0xfd, 0xff, 0xef, 0xff, 0xf5, - 0x3f, 0x82, 0xf8, 0xf8, 0xe6, 0x7f, 0xf2, 0xff, - 0xff, 0xff, 0x87, 0x87, 0xf0, 0xf0, 0xfc, 0xef, - 0xfd, 0xf7, 0xef, 0xee, 0x87, - 0xff, 0xff, 0xef, 0xff, 0xe7, 0xff, 0xf6, 0xf7, - 0x7f, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, - 0xff, 0xf7, 0xff, 0xff, 0xff, - 0xff, 0xf7, 0xff, 0xf2, 0xe6, 0xff, 0xf5, 0xf7, - 0xff, 0xff, 0xff, 0xe7, 0xff, 0xf7, 0xff, 0xfe, - 0xff, 0xf7, 0xef, 0xff, 0xf7, - 0xff, 0xfa, 0xeb, 0xff, 0xe3, 0xff, 0xf5, 0xff, - 0xff, 0xff, 0xf7, 0xef, 0xf7, 0xff, 0xfd, 0xee, - 0xfd, 0xff, 0xff, 0xef, 0xef, - 0xff, 0xf6, 0xfb, 0x7a, 0xe7, 0xff, 0x91, 0xef, - 0xff, 0xe2, 0xff, 0xf7, 0xf7, 0xf7, 0xfd, 0xff, - 0xfd, 0xef, 0xeb, 0xf5, 0xe7, - 0xff, 0xff, 0x8f, 0xbe, 0xf1, 0x93, 0xfd, 0xf1, - 0xff, 0xff, 0xff, 0xff, 0x2e, 0x2f, 0x59, 0x8f, - 0x6f, 0xf9, 0xf7, 0xff, 0xfa, - 0xff, 0x4f, 0xbf, 0xc4, 0xfb, 0xf5, 0x0f, 0xff, - 0x00, 0xff, 0xd0, 0x7f, 0x70, 0xaf, 0x7f, 0xff, - 0x8f, 0x7f, 0x81, 0x7f, 0xb1, - 0xff, 0xff, 0xda, 0xff, 0x2f, 0x4f, 0x7e, 0xf9, - 0xfb, 0xff, 0x5f, 0xef, 0xff, 0xff, 0x99, 0x29, - 0x71, 0xf1, 0xed, 0xff, 0xff, - 0x3f, 0xb7, 0xef, 0xdf, 0xff, 0xf9, 0xfe, 0xfd, - 0xff, 0xff, 0xb7, 0x9f, 0xff, 0xab, 0x73, 0xfd, - 0xad, 0x3c, 0x6b, 0xff, 0xfa, - 0xff, 0xe8, 0xf7, 0xbf, 0x6f, 0x8f, 0xff, 0xfe, - 0xff, 0x87, 0x7f, 0xe0, 0x7f, 0xbf, 0x5f, 0x93, - 0x7e, 0xe4, 0xf6, 0x97, 0x7b, - 0xff, 0xdf, 0x27, 0xaf, 0xa7, 0xff, 0xf4, 0xf6, - 0xff, 0xff, 0xff, 0x5f, 0xdf, 0xfb, 0x87, 0x1f, - 0x70, 0xf3, 0xfe, 0x7f, 0xfb, - 0xbf, 0xb7, 0x61, 0xb5, 0xfc, 0xff, 0xf7, 0xff, - 0xbf, 0xff, 0xa2, 0xff, 0xd8, 0xa1, 0x77, 0xdf, - 0xe6, 0xff, 0xff, 0x7e, 0xc4, - 0xff, 0xff, 0xf6, 0xd7, 0x97, 0xdb, 0xe8, 0xff, - 0xff, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xb9, 0x3b, - 0xf8, 0xff, 0xef, 0xe7, 0xbe, - 0xff, 0xff, 0x86, 0xbf, 0xe2, 0xad, 0xff, 0xff, - 0xff, 0xff, 0x9f, 0xff, 0x8f, 0x3f, 0x63, 0xcf, - 0x7b, 0xfe, 0xff, 0xfd, 0xfe, - 0xff, 0xff, 0xdf, 0xff, 0xba, 0x7f, 0xf7, 0xee, - 0x17, 0xf7, 0xee, 0xef, 0x97, 0x7f, 0xf7, 0xff, - 0xff, 0xfd, 0x9e, 0x77, 0xf3, - 0xff, 0xfb, 0xc3, 0x8f, 0xc1, 0x70, 0x71, 0xff, - 0xf7, 0x9f, 0x77, 0x7e, 0xb6, 0x4f, 0xb9, 0x01, - 0x1f, 0x1b, 0x7a, 0x9a, 0x7e, - 0xbf, 0xff, 0xf0, 0x9f, 0xef, 0x79, 0x3f, 0xf6, - 0xff, 0xfd, 0x9d, 0x7b, 0xf2, 0xff, 0xc9, 0xf3, - 0xff, 0x7d, 0xfb, 0xfd, 0xbf, - 0xff, 0xfd, 0xbf, 0x77, 0x8f, 0xff, 0xf1, 0xf7, - 0xff, 0xff, 0xff, 0xfd, 0xbb, 0x7f, 0xbf, 0x6f, - 0xf3, 0xfe, 0xff, 0xe7, 0xbf, - 0x7f, 0xff, 0xff, 0x5f, 0xaf, 0xf1, 0xfc, 0xf7, - 0xff, 0x77, 0xfe, 0xff, 0xdf, 0xff, 0xdf, 0xff, - 0xe3, 0x9e, 0x7f, 0x7a, 0xe3, - 0xff, 0xff, 0x6f, 0xff, 0xaf, 0xfc, 0xff, 0xff, - 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xf3, 0xfb, - 0xf7, 0xff, 0xf9, 0xfe, 0xff, - 0x3f, 0x70, 0xf8, 0xff, 0xf7, 0xff, 0xff, 0xff, - 0xff, 0x0f, 0x0f, 0xf1, 0xf1, 0xff, 0xf7, 0xff, - 0xef, 0xff, 0xf7, 0x8c, 0x1e, - 0xff, 0xf8, 0x7f, 0xff, 0x7f, 0xf7, 0xff, 0x8f, - 0x8f, 0x88, 0xf1, 0xf1, 0xef, 0xff, 0xf7, 0xff, - 0xee, 0x97, 0x1e, 0x01, 0x61, - 0xbf, 0xf0, 0xfa, 0xfd, 0x75, 0xff, 0xf5, 0xff, - 0xff, 0x8f, 0x9f, 0xe1, 0xf1, 0xff, 0xf7, 0xef, - 0xfe, 0xff, 0xff, 0x8f, 0x1f, - 0xff, 0xfa, 0x7f, 0xff, 0xfd, 0xff, 0xfd, 0xff, - 0x9f, 0x9f, 0xf9, 0xf9, 0xff, 0xef, 0xf7, 0xff, - 0xff, 0xff, 0x9e, 0x0f, 0x75, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf7, - 0xff, 0xf7, 0xf7, 0xff, 0xef, 0xf7, 0xe7, 0xef, - 0xef, 0xef, 0xff, 0xff, 0xef, - 0xff, 0xff, 0x7f, 0xff, 0xff, 0xff, 0x8f, 0xff, - 0xf8, 0xf7, 0xff, 0xf7, 0xff, 0xf7, 0xe7, 0xef, - 0x9f, 0x6f, 0xf0, 0xff, 0xff, - 0x7f, 0xf8, 0xff, 0xff, 0xfd, 0xff, 0xfd, 0x8f, - 0xff, 0x8f, 0xff, 0xf0, 0xff, 0xff, 0xf7, 0xef, - 0xf7, 0xff, 0xff, 0x9d, 0x7e, - 0xbf, 0xff, 0x78, 0xff, 0xfd, 0xff, 0xfd, 0xf7, - 0xff, 0xf7, 0x8f, 0xf7, 0xf0, 0xf7, 0xf7, 0xff, - 0xf7, 0xef, 0xfe, 0xe7, 0x9f, - 0xff, 0x8f, 0x7f, 0xf8, 0xff, 0xff, 0xff, 0xff, - 0x8f, 0xff, 0xf8, 0x8f, 0xff, 0xf8, 0xff, 0xef, - 0xff, 0xff, 0x8e, 0x6f, 0xe1, - 0xff, 0xf8, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xf7, 0x87, 0xff, 0xf0, 0xff, 0xff, 0xff, 0xff, - 0xef, 0xef, 0xff, 0x9f, 0x7f, - 0x3f, 0xff, 0xf8, 0xff, 0x8f, 0x7f, 0xf0, 0x9f, - 0xf7, 0xfb, 0x07, 0xe7, 0x78, 0xf7, 0x8f, 0x7e, - 0xf1, 0x9f, 0x7f, 0xff, 0x9e, - 0xff, 0x80, 0x7f, 0xf8, 0xff, 0x7f, 0xff, 0xff, - 0xf7, 0x8f, 0x6f, 0x80, 0xff, 0xf1, 0xff, 0xff, - 0xef, 0xff, 0xfe, 0x9f, 0x7f, - 0xff, 0x88, 0xff, 0xf8, 0xff, 0xff, 0xff, 0xff, - 0x7f, 0x9f, 0xef, 0x81, 0x7f, 0xf0, 0xff, 0xfe, - 0xff, 0xff, 0xef, 0x9f, 0x7f, - 0xff, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0x97, 0x77, 0xf8, 0xff, 0xef, 0xff, 0xf7, 0xff, - 0xef, 0xef, 0x8f, 0x76, 0xf1, - 0xff, 0xff, 0xff, 0xff, 0xf7, 0x0f, 0xf7, 0x88, - 0xf7, 0xf0, 0xff, 0xff, 0xef, 0xff, 0xef, 0x8e, - 0x07, 0x99, 0x61, 0xe1, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf7, 0xf7, - 0xff, 0xff, 0xf7, 0xf7, 0xef, 0xff, 0xe7, 0xee, - 0xff, 0xff, 0xff, 0xff, 0xff, - 0xbf, 0x87, 0xf8, 0xf8, 0xff, 0x7f, 0xf7, 0xff, - 0xff, 0xff, 0x8f, 0x8f, 0xe0, 0xf0, 0xef, 0xff, - 0xf7, 0xff, 0xff, 0xff, 0x8f, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf7, 0xff, - 0x7f, 0xff, 0xff, 0xff, 0xef, 0xff, 0xe7, 0xff, - 0xf6, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xf7, 0xff, 0xff, 0xf7, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xef, 0xff, 0xef, 0xff, - 0xf7, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf7, 0xff, - 0xff, 0xff, 0xff, 0xff, 0x6f, 0xff, 0xe7, 0xff, - 0xf7, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0x7f, 0xff, 0xff, 0xff, 0xf7, 0xff, - 0xff, 0xf7, 0xff, 0xff, 0xef, 0xff, 0xef, 0xff, - 0x97, 0xee, 0xf9, 0xf7, 0xff, - 0xff, 0xff, 0xff, 0xff, 0x80, 0x88, 0xff, 0xf8, - 0xff, 0xff, 0xf7, 0xff, 0x0f, 0x1f, 0x79, 0x9b, - 0x7f, 0xf0, 0xfe, 0xff, 0xff, - 0xff, 0x4f, 0x7b, 0xf7, 0xf7, 0xf8, 0x0f, 0xff, - 0x00, 0xff, 0xd0, 0x4f, 0x75, 0x8c, 0x79, 0xff, - 0x8f, 0x7f, 0x81, 0x7e, 0xb1, - 0xff, 0xf7, 0xff, 0xfe, 0x8f, 0x7f, 0x70, 0xf0, - 0xff, 0xf7, 0x7f, 0xff, 0xf7, 0xff, 0x84, 0x0e, - 0x73, 0xff, 0xf7, 0xff, 0xff, - 0x3f, 0xbd, 0xfd, 0xfb, 0xf7, 0xe8, 0xff, 0xff, - 0x78, 0xf0, 0x3f, 0xbf, 0xe3, 0x9b, 0x6f, 0xff, - 0x97, 0x1f, 0x7f, 0xcf, 0xfe, - 0xff, 0xf8, 0xf7, 0xff, 0x8f, 0x80, 0xff, 0xf8, - 0xff, 0x8f, 0x78, 0xf0, 0x7f, 0x9f, 0x7f, 0x9b, - 0x7f, 0xf1, 0xff, 0x97, 0x7f, - 0xff, 0xff, 0x07, 0xff, 0x87, 0x7f, 0xf0, 0xf8, - 0xff, 0x7f, 0xfb, 0x7f, 0xee, 0xfb, 0x99, 0x19, - 0x73, 0xef, 0xf7, 0xef, 0xff, - 0xbf, 0x87, 0x04, 0xfc, 0xff, 0x08, 0xf7, 0xff, - 0x7b, 0x7f, 0x8e, 0x8f, 0xf9, 0x98, 0x6f, 0xf3, - 0xff, 0xef, 0xff, 0xff, 0xdf, - 0xff, 0xff, 0x73, 0xff, 0xf7, 0x9f, 0xf8, 0xfb, - 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0x8f, 0x3d, - 0x61, 0xff, 0xef, 0xff, 0xff, - 0xff, 0x7f, 0x07, 0xff, 0xf8, 0x78, 0xff, 0xee, - 0xfb, 0xff, 0xff, 0xff, 0x87, 0x1f, 0x71, 0xe3, - 0xff, 0xfb, 0xff, 0xff, 0xff, - 0xff, 0x77, 0x8b, 0x7f, 0xf8, 0x7f, 0xff, 0xff, - 0x07, 0xe7, 0xfa, 0x77, 0x8f, 0x7f, 0xff, 0xef, - 0xf1, 0xef, 0x9f, 0x77, 0xf9, - 0xff, 0xf8, 0x73, 0x8f, 0xf8, 0x0f, 0x88, 0x88, - 0xf8, 0x98, 0x77, 0x78, 0x8f, 0x6f, 0x87, 0x7b, - 0xf7, 0xff, 0xef, 0x87, 0x6f, - 0xbf, 0x7f, 0xf0, 0xff, 0x8f, 0x7f, 0xff, 0xff, - 0x7e, 0xff, 0x9f, 0x7f, 0xf0, 0xff, 0xff, 0xeb, - 0xef, 0xff, 0xfb, 0xff, 0x9f, - 0xff, 0xff, 0xff, 0x77, 0xf8, 0x7f, 0xf8, 0xf7, - 0xff, 0x7f, 0xf7, 0xff, 0x9f, 0x7f, 0x9b, 0x6f, - 0xf1, 0xfe, 0xff, 0xfe, 0xff, - 0x7f, 0x7f, 0x8f, 0xff, 0x8f, 0xff, 0x7f, 0x8f, - 0xff, 0x70, 0xfb, 0x6f, 0xff, 0xff, 0xff, 0xf5, - 0xf9, 0xff, 0xff, 0xff, 0xf4, - 0xff, 0x87, 0xff, 0x74, 0xff, 0x7f, 0xff, 0xff, - 0x7e, 0xff, 0xff, 0x8f, 0xff, 0xf1, 0xff, 0xff, - 0xe9, 0xff, 0xf7, 0xff, 0xff, - 0xbf, 0x78, 0xf8, 0x7f, 0xf7, 0xff, 0xff, 0xff, - 0xf7, 0x07, 0x1f, 0xe1, 0xf1, 0xff, 0xf7, 0xff, - 0xef, 0xef, 0xff, 0x8e, 0x0f, - 0xff, 0xf8, 0x7f, 0xff, 0xff, 0xff, 0xff, 0x87, - 0x9f, 0x88, 0xf1, 0xe1, 0xff, 0xef, 0xf7, 0xef, - 0xfe, 0x9f, 0x0f, 0x09, 0x71, - 0xbf, 0xf0, 0xf8, 0xff, 0x77, 0xff, 0xff, 0xff, - 0xff, 0x8f, 0x8f, 0xf0, 0xf0, 0xff, 0xf7, 0xff, - 0xfe, 0xf7, 0xff, 0x8e, 0x1f, - 0x7f, 0xf8, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, - 0x8f, 0x8f, 0xf9, 0xf8, 0xfe, 0xff, 0xe7, 0xf7, - 0xff, 0xff, 0x96, 0x0f, 0x61, - 0x7f, 0xff, 0xff, 0xff, 0xf7, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xf7, 0xef, 0xff, 0xf7, 0xe7, 0xef, - 0xff, 0xe7, 0xff, 0xf7, 0xfe, - 0xff, 0xf7, 0x7f, 0xff, 0xff, 0xff, 0x8f, 0xff, - 0xf8, 0xef, 0xef, 0xef, 0xff, 0xf7, 0xf7, 0xff, - 0x9f, 0x67, 0xf0, 0xff, 0xff, - 0x7f, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0x8f, - 0xff, 0x8f, 0xff, 0xe0, 0xf7, 0xff, 0xf7, 0xfe, - 0xf7, 0xf7, 0xff, 0x8f, 0x6e, - 0x3f, 0xfb, 0x7c, 0xff, 0xff, 0xff, 0xf7, 0xff, - 0xff, 0xe7, 0x9f, 0xef, 0xf0, 0xff, 0xe7, 0xef, - 0xf7, 0xef, 0xf6, 0xf6, 0x87, - 0xff, 0x07, 0xff, 0xf8, 0xff, 0xff, 0xff, 0xff, - 0x8f, 0xff, 0xf4, 0x9f, 0xff, 0xfc, 0xff, 0xff, - 0xff, 0xff, 0x8f, 0x7f, 0xf0, - 0x7f, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xf7, 0x87, 0xff, 0xe8, 0xff, 0xf7, 0xff, 0xff, - 0xef, 0xe7, 0xf7, 0x8f, 0x6f, - 0x3f, 0xff, 0xfc, 0xff, 0x8f, 0x7f, 0xf0, 0xcf, - 0xef, 0xe8, 0x1b, 0xef, 0x7c, 0xff, 0x8f, 0x7e, - 0xe1, 0x97, 0x77, 0xff, 0x9e, - 0xff, 0x80, 0x7f, 0xfc, 0xff, 0x7f, 0xff, 0xff, - 0xf7, 0x8f, 0x7f, 0x80, 0xff, 0xf1, 0xff, 0xfe, - 0xef, 0xff, 0xff, 0x9f, 0x7f, - 0xff, 0x84, 0xff, 0xf8, 0xff, 0xff, 0xff, 0xff, - 0x6f, 0x9f, 0xff, 0x91, 0x7f, 0xf0, 0xff, 0xff, - 0xff, 0xff, 0xff, 0x8f, 0x7f, - 0xff, 0xfb, 0xff, 0xff, 0xfb, 0xff, 0xff, 0xff, - 0x1f, 0x77, 0xf8, 0xf7, 0xef, 0xff, 0xf7, 0xff, - 0xf7, 0xef, 0x87, 0x67, 0xf1, - 0xff, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x8b, 0x8c, - 0xf0, 0xf8, 0xf7, 0xff, 0xef, 0xff, 0xe7, 0x9e, - 0x67, 0x89, 0x77, 0xf1, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xf3, 0xff, 0xff, 0xff, - 0xff, 0xef, 0xfb, 0xff, 0xef, 0xff, 0xe7, 0xff, - 0xf7, 0xf7, 0xff, 0xff, 0xf7, - 0xbf, 0x87, 0xf8, 0xf8, 0xf7, 0x7f, 0xfb, 0xff, - 0xff, 0xff, 0x8f, 0x8f, 0xe0, 0xf0, 0xe7, 0xfe, - 0xf7, 0xff, 0xff, 0xff, 0x8e, - 0xff, 0xff, 0xff, 0xff, 0xfb, 0xff, 0xfb, 0xff, - 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xff, - 0xf7, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xf7, 0xfb, 0xff, 0xfb, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, - 0xf7, 0xff, 0xff, 0xff, 0xfe, - 0xff, 0xff, 0xff, 0xff, 0xf3, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0x7f, 0xf7, 0xff, 0x8b, 0xff, - 0xfc, 0xf3, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xee, 0xff, 0xf6, 0xff, - 0xff, 0xf7, 0x8f, 0xbf, 0xf9, 0x8f, 0xff, 0xf0, - 0xff, 0xff, 0x7f, 0xff, 0x1f, 0x1f, 0x71, 0x89, - 0x7f, 0xf1, 0xff, 0xff, 0xff, - 0xff, 0x4f, 0xb8, 0xc6, 0xfb, 0xfd, 0x0f, 0xff, - 0x00, 0xff, 0xd0, 0x7f, 0x79, 0x90, 0x7f, 0xf9, - 0x8f, 0x7f, 0x81, 0x7f, 0x81, - 0xff, 0xff, 0xcf, 0xff, 0xb0, 0x4f, 0x77, 0xf4, - 0xff, 0xff, 0x7f, 0xe7, 0xee, 0xff, 0x97, 0x07, - 0x69, 0xf1, 0xf7, 0xff, 0xff, - 0x3f, 0xba, 0xff, 0x4d, 0xfe, 0xe5, 0xff, 0xff, - 0x7f, 0xef, 0xbf, 0x9f, 0xe7, 0x9f, 0x77, 0xe9, - 0x9f, 0x1f, 0x79, 0xc1, 0xfe, - 0xff, 0xf8, 0xf7, 0x3f, 0xff, 0x9f, 0xf7, 0xf8, - 0xff, 0x87, 0x7f, 0xf0, 0x7f, 0x97, 0x7f, 0x89, - 0x7e, 0xf1, 0xff, 0x9f, 0x7f, - 0xff, 0xff, 0x37, 0xbf, 0xb3, 0x7f, 0xf2, 0xff, - 0xff, 0xf7, 0xff, 0x7f, 0x7b, 0xfe, 0x87, 0x1f, - 0x71, 0xf1, 0xff, 0x7f, 0xfb, - 0xff, 0xff, 0x70, 0xb7, 0xfe, 0x7b, 0xff, 0xf3, - 0xbf, 0xf7, 0xff, 0xff, 0xf9, 0x9f, 0x7f, 0xf1, - 0xff, 0xf7, 0xff, 0xff, 0xfb, - 0xbf, 0xff, 0xf4, 0xcf, 0x87, 0xd8, 0xf8, 0xf8, - 0xff, 0xff, 0x8f, 0xff, 0xe9, 0xff, 0x8f, 0x1f, - 0x71, 0xff, 0xef, 0xff, 0x8d, - 0xff, 0xff, 0x87, 0xbf, 0xf9, 0x3f, 0xff, 0xf7, - 0xff, 0xff, 0xff, 0xff, 0x9f, 0x1f, 0x71, 0xf9, - 0xff, 0x7b, 0xff, 0xfe, 0xff, - 0xff, 0xff, 0xcb, 0xff, 0xbd, 0x7f, 0xfb, 0xff, - 0x8f, 0xff, 0xf8, 0x7f, 0x9f, 0x7f, 0xf1, 0xef, - 0xff, 0xf7, 0x8f, 0x7f, 0xf9, - 0xff, 0xf8, 0x43, 0x8f, 0xc4, 0x75, 0x7d, 0xff, - 0xff, 0x97, 0x7f, 0x78, 0x8f, 0x6f, 0x99, 0x17, - 0x19, 0x71, 0xf9, 0x8f, 0x6f, - 0xbf, 0x7f, 0xf0, 0x8f, 0xf6, 0x7d, 0x37, 0xff, - 0xff, 0xff, 0x9f, 0x7f, 0xe0, 0xff, 0xff, 0xef, - 0xff, 0xf7, 0xfb, 0xff, 0x8f, - 0xff, 0xff, 0xbf, 0x77, 0x8e, 0xff, 0xf0, 0xff, - 0xff, 0xf7, 0xf7, 0x7f, 0x97, 0x7f, 0x99, 0x7f, - 0xf9, 0xfe, 0xff, 0xfe, 0xff, - 0x7f, 0xff, 0xff, 0xcf, 0xbf, 0xf9, 0xfa, 0xff, - 0xff, 0xff, 0xf3, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xf7, 0x8f, 0x7f, 0x71, 0xf6, - 0xff, 0xff, 0xff, 0x7f, 0xb7, 0xfe, 0xfb, 0xff, - 0xf6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf7, - 0xff, 0xff, 0xf7, 0xff, 0xff, - 0x3f, 0x78, 0xf8, 0xff, 0xff, 0xff, 0xff, 0x8f, - 0xff, 0x08, 0x0f, 0xf1, 0xf1, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0x8e, 0x0e, - 0xff, 0xf8, 0x7f, 0xff, 0x7f, 0xff, 0xff, 0x8f, - 0x8f, 0x80, 0xf1, 0xf1, 0xff, 0xff, 0xff, 0xff, - 0xfe, 0x8f, 0x0e, 0x01, 0x71, - 0xbf, 0xf8, 0xf8, 0xff, 0x7f, 0xff, 0xff, 0xff, - 0xff, 0x8f, 0x8f, 0xf1, 0xf1, 0xff, 0xff, 0xff, - 0xfe, 0xff, 0xff, 0x8f, 0x0f, - 0xff, 0xf8, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, - 0x8f, 0x8f, 0xf1, 0xf1, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0x8e, 0x0f, 0x71, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0x7f, 0xff, 0xff, 0xff, 0x8f, 0xff, - 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0x8f, 0x7f, 0xf0, 0xff, 0xff, - 0x7f, 0xf8, 0xff, 0xff, 0xff, 0xff, 0xff, 0x8f, - 0xff, 0x88, 0xff, 0xf0, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0x8f, 0x7e, - 0xbf, 0xff, 0x78, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xf7, 0x8f, 0xf7, 0xf0, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xfe, 0xff, 0x8f, - 0xff, 0x87, 0x7f, 0xf8, 0xff, 0xff, 0xff, 0xff, - 0x8f, 0xff, 0xf8, 0x8f, 0xf7, 0xf8, 0xff, 0xff, - 0xff, 0xff, 0x8e, 0x7f, 0xf1, - 0xff, 0xf8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0x87, 0xff, 0xf0, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0x8f, 0x7f, - 0x3f, 0xff, 0xf8, 0xff, 0x8f, 0x7f, 0xf0, 0xdf, - 0xff, 0xf6, 0x07, 0xff, 0x78, 0xff, 0x8f, 0x7e, - 0xf1, 0x9f, 0x7f, 0xfd, 0x8e, - 0xff, 0x80, 0x7f, 0xf8, 0xff, 0x7f, 0xff, 0xff, - 0xf7, 0x8f, 0x7f, 0x80, 0xff, 0xf1, 0xff, 0xff, - 0xff, 0xff, 0xfe, 0x8f, 0x7f, - 0xff, 0x80, 0xff, 0xf8, 0xff, 0xff, 0xff, 0xff, - 0x7f, 0x8f, 0xff, 0x81, 0x7f, 0xf0, 0xff, 0xfe, - 0xff, 0xff, 0xff, 0x8f, 0x7f, - 0xff, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0x8f, 0x77, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0x8f, 0x6e, 0xf1, - 0xff, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x8f, 0x88, - 0xf0, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0x8e, - 0x7f, 0x81, 0x7f, 0xf1, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0x7f, 0xff, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xfe, - 0xff, 0xff, 0xff, 0xff, 0xff, - 0xbf, 0x87, 0xf8, 0xf8, 0xff, 0x7f, 0xff, 0xff, - 0xff, 0xff, 0x8f, 0x8f, 0xf0, 0xf0, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0x8f, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xf7, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, - 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xf7, 0xff, 0xf7, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, - 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xf7, 0xff, 0x87, 0xff, - 0xf8, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xfe, 0xfe, 0xff, 0xff, 0xff, - 0xff, 0xff, 0x3f, 0x8f, 0xf0, 0x85, 0xff, 0xf0, - 0xff, 0xff, 0xff, 0x7f, 0x0d, 0x0f, 0x71, 0x81, - 0x7e, 0xf1, 0xfe, 0xff, 0xff, - 0xff, 0x5f, 0x3d, 0xf0, 0xf5, 0xfa, 0x0f, 0xff, - 0x00, 0xff, 0x80, 0x0f, 0xf1, 0x88, 0x7f, 0xf1, - 0x8f, 0x7e, 0x81, 0x7e, 0xc1, - 0xff, 0xff, 0xff, 0xff, 0xba, 0x4f, 0x75, 0xfa, - 0xff, 0xff, 0x7f, 0xff, 0xff, 0xff, 0x8f, 0x0f, - 0x71, 0xf1, 0xff, 0xff, 0xff, - 0x3f, 0xbf, 0xff, 0xcf, 0xff, 0xfa, 0xff, 0xfe, - 0x7f, 0xdf, 0x7f, 0xf4, 0xff, 0x8f, 0x7f, 0xf1, - 0x8f, 0x0f, 0x71, 0xf1, 0xbe, - 0xff, 0xf8, 0xf7, 0xbb, 0x7f, 0x85, 0xff, 0xf0, - 0xff, 0x8f, 0x7f, 0xf0, 0x7f, 0x87, 0x7f, 0x81, - 0x7e, 0xf1, 0x8f, 0x0f, 0x71, - 0xff, 0xff, 0x87, 0x8f, 0x8d, 0xfd, 0xf5, 0xff, - 0xfb, 0xff, 0xff, 0xff, 0xf7, 0xfe, 0x8f, 0x0f, - 0x71, 0xf1, 0xfe, 0x7f, 0xf7, - 0xbf, 0x07, 0xc0, 0xbf, 0xf7, 0xfd, 0xff, 0xff, - 0xbf, 0xff, 0x85, 0x8f, 0xf9, 0x8f, 0x7f, 0xf1, - 0xff, 0x7f, 0xf6, 0xff, 0x83, - 0xff, 0xff, 0xf4, 0xff, 0x8f, 0xda, 0xf0, 0xb6, - 0xdf, 0xfd, 0xf6, 0xff, 0xf9, 0xff, 0x8f, 0x1f, - 0x71, 0xfd, 0x8f, 0x7b, 0xfd, - 0xff, 0xff, 0xb7, 0x8f, 0xf0, 0xbd, 0xff, 0xfd, - 0xf7, 0xff, 0xff, 0xff, 0x8f, 0x0f, 0x71, 0xf1, - 0xff, 0x7b, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xba, 0x7f, 0xf5, 0xf7, - 0x0f, 0xf7, 0xf0, 0x7f, 0x87, 0x7f, 0xf1, 0xff, - 0xff, 0xfe, 0x8f, 0x7f, 0xf1, - 0xff, 0xf8, 0x47, 0x8f, 0xca, 0x70, 0x7a, 0xff, - 0xff, 0x8f, 0x7f, 0x70, 0x8f, 0x7f, 0x81, 0x0e, - 0x01, 0x01, 0x71, 0x81, 0x7f, - 0xbf, 0x7f, 0xf0, 0x8f, 0xff, 0x70, 0x3f, 0xff, - 0xfd, 0xfe, 0x8f, 0x7f, 0xf0, 0xff, 0xff, 0xff, - 0xff, 0xfe, 0xfb, 0xff, 0x8f, - 0xff, 0xff, 0xbf, 0x7f, 0x85, 0x7f, 0xf0, 0xff, - 0xff, 0xff, 0xff, 0xff, 0x8f, 0x7f, 0x81, 0x7f, - 0x81, 0xfe, 0xf1, 0xff, 0xff, - 0x7f, 0xff, 0xff, 0xcf, 0x87, 0xfa, 0x75, 0xff, - 0xff, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0x8f, 0x7f, 0x70, 0xfe, - 0xff, 0x87, 0xff, 0x4b, 0xbf, 0x7f, 0xfd, 0xff, - 0xff, 0xff, 0xff, 0x8f, 0xff, 0xff, 0xff, 0xfe, - 0xff, 0xfe, 0xff, 0xfe, 0xff, - 0x3f, 0x78, 0xf8, 0xf7, 0xff, 0xff, 0xff, 0xff, - 0xff, 0x8f, 0x0f, 0x71, 0xf1, 0xff, 0x7f, 0xff, - 0xff, 0xff, 0xff, 0x8e, 0x0e, - 0xff, 0xf8, 0x7f, 0xff, 0x7f, 0xff, 0x7f, 0x8f, - 0x8f, 0x00, 0x71, 0xf1, 0xff, 0xff, 0xff, 0xff, - 0xfe, 0x8f, 0x0e, 0x01, 0x71, - 0xbf, 0xf8, 0xf8, 0xff, 0x7f, 0xff, 0x7f, 0xff, - 0xff, 0x8f, 0x0f, 0x71, 0xf1, 0xff, 0xff, 0xff, - 0xfe, 0xff, 0xff, 0x8f, 0x0f, - 0xff, 0xf8, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, - 0x8f, 0x0f, 0x71, 0xf1, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0x8e, 0x0f, 0x71, - 0xff, 0xff, 0xff, 0xf7, 0xff, 0xff, 0x7f, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, - 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0x7f, 0xff, 0xff, 0x7f, 0x8f, 0xff, - 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0x8f, 0x7f, 0xf0, 0xff, 0xff, - 0x7f, 0xf8, 0xff, 0xff, 0xff, 0x7f, 0x7f, 0xff, - 0xff, 0x8f, 0xff, 0xf0, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0x8f, 0x7e, - 0xbf, 0xff, 0x78, 0xff, 0xff, 0xff, 0x7f, 0xff, - 0xff, 0xff, 0x8f, 0xff, 0xf0, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xfe, 0xff, 0x8f, - 0xff, 0x07, 0xff, 0xf8, 0xff, 0xff, 0xff, 0xff, - 0x8f, 0xff, 0xf0, 0x8f, 0xff, 0xf0, 0xff, 0xff, - 0xff, 0xff, 0x8e, 0x7f, 0xf1, - 0xff, 0xf8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0x8f, 0xff, 0xf0, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0x8f, 0x7f, - 0x3f, 0xff, 0xf8, 0xff, 0x8f, 0x7f, 0xf0, 0x8f, - 0xff, 0xfc, 0x8f, 0xff, 0xf0, 0xff, 0x8f, 0x7e, - 0xf1, 0x8f, 0x7f, 0xf3, 0x8e, - 0xff, 0x80, 0x7f, 0xf8, 0xff, 0x7f, 0xff, 0xff, - 0xff, 0x8f, 0x7f, 0x80, 0x7f, 0xf1, 0xff, 0xff, - 0xff, 0xff, 0xfe, 0x8f, 0x7f, - 0xff, 0x80, 0xff, 0xf8, 0xff, 0xff, 0xff, 0xff, - 0x7f, 0x8f, 0x7f, 0x81, 0x7f, 0xf0, 0xff, 0xfe, - 0xff, 0xff, 0xff, 0x8f, 0x7f, - 0xff, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0x8f, 0x7f, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0x8f, 0x7f, 0xf1, - 0xff, 0xff, 0xff, 0xff, 0xff, 0x0f, 0xff, 0x80, - 0xff, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x8e, - 0x0f, 0x01, 0x71, 0xf0, 0xff, - 0xff, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xef, 0xff, 0xfb, 0xff, 0xff, 0xff, 0xff, 0xfe, - 0xff, 0xff, 0xff, 0xff, 0xf7, - 0xbf, 0x87, 0xf8, 0xf8, 0xff, 0x7f, 0xff, 0xff, - 0x7f, 0x7f, 0x8f, 0x8f, 0xf0, 0xf0, 0xfe, 0xff, - 0xff, 0xff, 0xff, 0xfe, 0x8f, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, 0xff, 0x7f, - 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, 0xff, 0x7e, - 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0x7f, 0xff, 0xff, 0xff, 0x7f, 0xff, 0x7e, - 0xff, 0xff, 0x7f, 0xff, 0xff, - 0xff, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0xff, 0xff, - 0x7f, 0x7f, 0xff, 0xff, 0xff, 0x7f, 0xff, 0x7f, - 0x8f, 0xfe, 0xf0, 0xff, 0xff, - 0xbf, 0xff, 0x0e, 0x8f, 0x70, 0x80, 0xff, 0xf0, - 0xff, 0xff, 0xff, 0xff, 0x0f, 0x7f, 0xf1, 0x0f, - 0x7f, 0xf1, 0xfe, 0xff, 0x9f, - 0xbf, 0x1f, 0xfb, 0x0c, 0xff, 0xf0, 0x8f, 0xff, - 0x00, 0xff, 0xb0, 0x3f, 0x71, 0x80, 0xff, 0xf1, - 0x8f, 0x7f, 0x81, 0x7e, 0xc1, - 0xff, 0xff, 0xff, 0x7f, 0x8f, 0x0f, 0x70, 0xf0, - 0x8f, 0x7f, 0x70, 0xcf, 0x8f, 0xff, 0x71, 0x0f, - 0x7e, 0xf1, 0x8f, 0x7f, 0xf1, - 0x7f, 0xff, 0x7f, 0x0f, 0xff, 0x78, 0x8f, 0x8f, - 0x70, 0x70, 0x7f, 0xfe, 0xff, 0x8f, 0xff, 0x70, - 0xff, 0xfe, 0xff, 0xff, 0xbe, - 0xff, 0xf8, 0xf7, 0x0b, 0xf7, 0x88, 0xf7, 0xf0, - 0x8f, 0x8f, 0x70, 0xf0, 0x7e, 0x73, 0xff, 0x8f, - 0x7e, 0xf0, 0xff, 0x8f, 0x7f, - 0xff, 0x78, 0x77, 0xff, 0x8f, 0x8f, 0xf0, 0xf0, - 0xff, 0x8f, 0x77, 0x70, 0x77, 0x7f, 0xff, 0x7f, - 0xff, 0xff, 0xfe, 0x8f, 0x7f, - 0xff, 0xff, 0x77, 0x8f, 0xff, 0xf0, 0xff, 0xff, - 0x77, 0x7f, 0xfb, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xef, 0x7f, 0xff, 0xbf, - 0xbf, 0xff, 0x74, 0xff, 0xff, 0x8f, 0xff, 0xfc, - 0x0f, 0xf3, 0x8c, 0xff, 0xfd, 0xff, 0xff, 0x8f, - 0x7f, 0xf1, 0x8f, 0x7d, 0x83, - 0xff, 0x7f, 0x77, 0xff, 0xff, 0xff, 0xff, 0xfb, - 0xf7, 0x7f, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0x78, 0xff, 0x7f, 0xff, 0xff, 0xff, 0xff, - 0x0f, 0x8f, 0x70, 0x70, 0xf7, 0xff, 0xff, 0xff, - 0xff, 0xff, 0x8e, 0x0f, 0x71, - 0xff, 0xf8, 0xf7, 0x7f, 0xff, 0x8f, 0x8f, 0x80, - 0x80, 0x81, 0x70, 0x70, 0x7f, 0xff, 0xff, 0xff, - 0xff, 0x6f, 0x8f, 0x0f, 0x71, - 0xbf, 0x7f, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, - 0x7b, 0xff, 0x9f, 0xff, 0x70, 0xff, 0xff, 0xff, - 0xff, 0x7f, 0xff, 0xfe, 0x8f, - 0xff, 0x87, 0x7f, 0x78, 0xfe, 0xff, 0x9e, 0xff, - 0xf2, 0x7f, 0xff, 0x0f, 0xff, 0xf0, 0xff, 0xef, - 0xff, 0x7f, 0xff, 0xff, 0xff, - 0x7f, 0x7f, 0xff, 0x7f, 0xff, 0xff, 0xfe, 0x8f, - 0xff, 0x70, 0xff, 0x7f, 0xff, 0xff, 0xdf, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xfe, - 0xff, 0xff, 0x7f, 0xf7, 0xff, 0xfe, 0xff, 0xff, - 0xff, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xdf, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0x7f, 0x7f, 0x8f, 0x8f, 0xf0, 0xf0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xaf, 0x0f, - 0x70, 0xd1, 0xff, 0xf8, 0xfe, - 0xff, 0xff, 0xff, 0xf7, 0xaf, 0x8f, 0xfa, 0xf0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0x9f, 0x8f, 0x0f, - 0x79, 0xe1, 0xff, 0xfe, 0xff, - 0xbf, 0xff, 0xff, 0x7a, 0x8f, 0xff, 0xf0, 0xef, - 0xff, 0xfb, 0xff, 0xaf, 0xff, 0xfb, 0x8f, 0x7f, - 0xf1, 0xdf, 0xff, 0xf9, 0xff, - 0xbf, 0xff, 0xff, 0xf7, 0xff, 0xaf, 0xff, 0xba, - 0xaf, 0xff, 0xfb, 0xff, 0xff, 0xff, 0xef, 0xdf, - 0xff, 0xfd, 0xbe, 0xf7, 0xf5, - 0xff, 0xf8, 0xff, 0xff, 0x8f, 0xff, 0xf0, 0xff, - 0xff, 0x8f, 0xf7, 0xf0, 0xff, 0xff, 0x8f, 0x3f, - 0x11, 0xeb, 0xdb, 0xcf, 0x7f, - 0xbf, 0xf0, 0x8f, 0x87, 0xf0, 0xf0, 0xff, 0xbf, - 0xff, 0x8f, 0xff, 0xf1, 0x8f, 0x0f, 0x31, 0xf1, - 0x9f, 0xdf, 0xf9, 0x85, 0x7f, - 0xbf, 0xff, 0x8f, 0x7f, 0xf0, 0xff, 0xff, 0xff, - 0xee, 0xff, 0xfb, 0xf7, 0x8f, 0x7f, 0xb1, 0xff, - 0xff, 0xff, 0xff, 0xdf, 0xfd, - 0xff, 0xff, 0xff, 0x8f, 0x8f, 0xf0, 0x80, 0xff, - 0xf0, 0xff, 0xff, 0xff, 0xff, 0x8f, 0x2f, 0x71, - 0xc1, 0x7f, 0xf1, 0xff, 0xff, - 0xff, 0xff, 0xff, 0x8f, 0xff, 0xf0, 0xff, 0xbf, - 0xff, 0xff, 0xff, 0xff, 0xff, 0x8f, 0x7f, 0xd1, - 0x9f, 0xff, 0x7b, 0xb7, 0xff, - 0xbf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xfe, 0x8f, 0xff, 0xf1, 0xff, 0xff, - 0xff, 0xff, 0xbf, 0xb7, 0xff, - 0xff, 0xa4, 0xf7, 0x88, 0xff, 0xf0, 0xaf, 0xbf, - 0xfb, 0xef, 0xff, 0xf7, 0xff, 0x8f, 0x7f, 0xf1, - 0xff, 0xdd, 0xf7, 0x97, 0x7f, - 0xff, 0xff, 0xf7, 0xff, 0x8f, 0xff, 0xf0, 0xff, - 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0x8f, 0x7f, - 0xf1, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xf7, 0xaf, 0x0f, 0xa0, 0xf0, - 0xfb, 0xff, 0xff, 0xff, 0xff, 0xff, 0x8f, 0x0f, - 0x71, 0xb1, 0x37, 0xf7, 0xff, - 0xff, 0xb8, 0xff, 0xff, 0xff, 0x7f, 0xff, 0xff, - 0xff, 0x8f, 0xff, 0xf0, 0xfe, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0x8f, 0x7f, - 0xbf, 0xff, 0xf8, 0xf7, 0xff, 0xa7, 0xff, 0xf0, - 0xff, 0xff, 0x8f, 0xfe, 0xf0, 0xff, 0xff, 0x8f, - 0x7f, 0xf1, 0xff, 0xff, 0xcf, - 0xff, 0xff, 0xf7, 0x9f, 0xf7, 0xf3, 0xff, 0xff, - 0xff, 0xff, 0x7f, 0xff, 0xff, 0xbf, 0x7f, 0xf9, - 0xff, 0xfe, 0xff, 0xff, 0xdf, - 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, 0xff, 0x7f, - 0xff, 0xff, 0xff, 0x7f, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xbf, 0xbf, - 0xff, 0xff, 0xff, 0xaf, 0xff, 0xf0, 0xff, 0xff, - 0x9f, 0xfe, 0xf1, 0xff, 0xff, 0xcf, 0x7f, 0xf1, - 0xff, 0xff, 0xdf, 0xff, 0xf1, - 0xbf, 0xff, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0x8f, 0xff, 0xf0, 0xbf, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0x6f, - 0xff, 0xff, 0xf7, 0xff, 0xff, 0xff, 0xff, 0x7f, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xbf, - 0xff, 0xf8, 0xff, 0xff, 0xdf, 0xff, 0xff, 0xff, - 0xff, 0x8f, 0xff, 0xf1, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xcf, 0xff, - 0xff, 0xd8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0x8f, 0xef, 0xe0, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0x8f, 0x3f, - 0xff, 0xdf, 0xf7, 0xff, 0x0f, 0xfe, 0xf0, 0xff, - 0xff, 0xff, 0xff, 0xef, 0xff, 0xdf, 0x8e, 0x7f, - 0xf1, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xbf, 0xbf, 0xff, 0xff, 0xff, - 0xff, 0xff, 0x8f, 0xff, 0xf1, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xef, 0xff, 0xbf, 0x7f, 0xe1, 0xff, - 0xdf, 0xff, 0x7f, 0xff, 0xbf, - 0xff, 0xa7, 0xff, 0x88, 0xff, 0xf1, 0xfe, 0xff, - 0xff, 0xff, 0xff, 0x1f, 0xff, 0xf0, 0xcf, 0xb1, - 0xff, 0xef, 0xff, 0x7f, 0xff, diff --git a/board/sixnet/sixnet.c b/board/sixnet/sixnet.c deleted file mode 100644 index 06b2083..0000000 --- a/board/sixnet/sixnet.c +++ /dev/null @@ -1,578 +0,0 @@ -/* - * (C) Copyright 2001, 2002 - * Dave Ellis, SIXNET, dge@sixnetio.com. - * Based on code by: - * Wolfgang Denk, DENX Software Engineering, wd@denx.de. - * and other contributors to U-Boot. - * - * SPDX-License-Identifier: GPL-2.0+ - */ - -#include <common.h> -#include <config.h> -#include <jffs2/jffs2.h> -#include <mpc8xx.h> -#include <net.h> /* for eth_init() */ -#include <rtc.h> -#include "sixnet.h" -#ifdef CONFIG_SHOW_BOOT_PROGRESS -# include <status_led.h> -#endif - -DECLARE_GLOBAL_DATA_PTR; - -#define ORMASK(size) ((-size) & OR_AM_MSK) - -static long ram_size(ulong *, long); - -/* ------------------------------------------------------------------------- */ - -#ifdef CONFIG_SHOW_BOOT_PROGRESS -void show_boot_progress (int status) -{ -#if defined(CONFIG_STATUS_LED) -# if defined(STATUS_LED_BOOT) - if (status == BOOTSTAGE_ID_RUN_OS) { - /* ready to transfer to kernel, make sure LED is proper state */ - status_led_set(STATUS_LED_BOOT, CONFIG_BOOT_LED_STATE); - } -# endif /* STATUS_LED_BOOT */ -#endif /* CONFIG_STATUS_LED */ -} -#endif - -/* ------------------------------------------------------------------------- */ - -/* - * Check Board Identity: - * returns 0 if recognized, -1 if unknown - */ - -int checkboard (void) -{ - puts ("Board: SIXNET SXNI855T\n"); - return 0; -} - -/* ------------------------------------------------------------------------- */ - -#if defined(CONFIG_CMD_PCMCIA) -#error "SXNI855T has no PCMCIA port" -#endif - -/* ------------------------------------------------------------------------- */ - -#define _not_used_ 0xffffffff - -/* UPMB table for dual UART. */ - -/* this table is for 50MHz operation, it should work at all lower speeds */ -const uint duart_table[] = -{ - /* single read. (offset 0 in upm RAM) */ - 0xfffffc04, 0x0ffffc04, 0x0ff3fc04, 0x0ff3fc04, - 0x0ff3fc00, 0x0ff3fc04, 0xfffffc04, 0xfffffc05, - - /* burst read. (offset 8 in upm RAM) */ - _not_used_, _not_used_, _not_used_, _not_used_, - _not_used_, _not_used_, _not_used_, _not_used_, - _not_used_, _not_used_, _not_used_, _not_used_, - _not_used_, _not_used_, _not_used_, _not_used_, - - /* single write. (offset 18 in upm RAM) */ - 0xfffffc04, 0x0ffffc04, 0x00fffc04, 0x00fffc04, - 0x00fffc04, 0x00fffc00, 0xfffffc04, 0xfffffc05, - - /* burst write. (offset 20 in upm RAM) */ - _not_used_, _not_used_, _not_used_, _not_used_, - _not_used_, _not_used_, _not_used_, _not_used_, - _not_used_, _not_used_, _not_used_, _not_used_, - _not_used_, _not_used_, _not_used_, _not_used_, - - /* refresh. (offset 30 in upm RAM) */ - _not_used_, _not_used_, _not_used_, _not_used_, - _not_used_, _not_used_, _not_used_, _not_used_, - _not_used_, _not_used_, _not_used_, _not_used_, - - /* exception. (offset 3c in upm RAM) */ - _not_used_, _not_used_, _not_used_, _not_used_, -}; - -/* Load FPGA very early in boot sequence, since it must be - * loaded before the 16C2550 serial channels can be used as - * console channels. - * - * Note: Much of the configuration is not complete. The - * stack is in DPRAM since SDRAM has not been initialized, - * so the stack must be kept small. Global variables - * are still in FLASH, so they cannot be written. - * Only the FLASH, DPRAM, immap and FPGA can be addressed, - * the other chip selects may not have been initialized. - * The clocks have been initialized, so udelay() can be - * used. - */ -#define FPGA_DONE 0x0080 /* PA8, input, high when FPGA load complete */ -#define FPGA_PROGRAM_L 0x0040 /* PA9, output, low to reset, high to start */ -#define FPGA_INIT_L 0x0020 /* PA10, input, low indicates not ready */ -#define fpga (*(volatile unsigned char *)(CONFIG_SYS_FPGA_PROG)) /* FPGA port */ - -int board_postclk_init (void) -{ - - /* the data to load to the XCSxxXL FPGA */ - static const unsigned char fpgadata[] = { -# include "fpgadata.c" - }; - - volatile immap_t *immap = (immap_t *)CONFIG_SYS_IMMR; - volatile memctl8xx_t *memctl = &immap->im_memctl; -#define porta (immap->im_ioport.iop_padat) - const unsigned char* pdata; - - /* /INITFPGA and DONEFPGA signals are inputs */ - immap->im_ioport.iop_padir &= ~(FPGA_INIT_L | FPGA_DONE); - - /* Force output pin to begin at 0, /PROGRAM asserted (0) resets FPGA */ - porta &= ~FPGA_PROGRAM_L; - - /* Set FPGA as an output */ - immap->im_ioport.iop_padir |= FPGA_PROGRAM_L; - - /* delay a little to make sure FPGA sees it, really - * only need less than a microsecond. - */ - udelay(10); - - /* unassert /PROGRAM */ - porta |= FPGA_PROGRAM_L; - - /* delay while FPGA does last erase, indicated by - * /INITFPGA going high. This should happen within a - * few milliseconds. - */ - /* ### FIXME - a timeout check would be good, maybe flash - * the status LED to indicate the error? - */ - while ((porta & FPGA_INIT_L) == 0) - ; /* waiting */ - - /* write program data to FPGA at the programming address - * so extra /CS1 strobes at end of configuration don't actually - * write to any registers. - */ - fpga = 0xff; /* first write is ignored */ - fpga = 0xff; /* fill byte */ - fpga = 0xff; /* fill byte */ - fpga = 0x4f; /* preamble code */ - fpga = 0x80; fpga = 0xaf; fpga = 0x9b; /* length (ignored) */ - fpga = 0x4b; /* field check code */ - - pdata = fpgadata; - /* while no error write out each of the 28 byte frames */ - while ((porta & (FPGA_INIT_L | FPGA_DONE)) == FPGA_INIT_L - && pdata < fpgadata + sizeof(fpgadata)) { - - fpga = 0x4f; /* preamble code */ - - /* 21 bytes of data in a frame */ - fpga = *(pdata++); fpga = *(pdata++); - fpga = *(pdata++); fpga = *(pdata++); - fpga = *(pdata++); fpga = *(pdata++); - fpga = *(pdata++); fpga = *(pdata++); - fpga = *(pdata++); fpga = *(pdata++); - fpga = *(pdata++); fpga = *(pdata++); - fpga = *(pdata++); fpga = *(pdata++); - fpga = *(pdata++); fpga = *(pdata++); - fpga = *(pdata++); fpga = *(pdata++); - fpga = *(pdata++); fpga = *(pdata++); - fpga = *(pdata++); - - fpga = 0x4b; /* field check code */ - fpga = 0xff; /* extended write cycle */ - fpga = 0x4b; /* extended write cycle - * (actually 0x4b from bitgen.exe) - */ - fpga = 0xff; /* extended write cycle */ - fpga = 0xff; /* extended write cycle */ - fpga = 0xff; /* extended write cycle */ - } - - fpga = 0xff; /* startup byte */ - fpga = 0xff; /* startup byte */ - fpga = 0xff; /* startup byte */ - fpga = 0xff; /* startup byte */ - -#if 0 /* ### FIXME */ - /* If didn't load all the data or FPGA_DONE is low the load failed. - * Maybe someday stop here and flash the status LED? The console - * is not configured, so can't print an error message. Can't write - * global variables to set a flag (except gd?). - * For now it must work. - */ -#endif - - /* Now that the FPGA is loaded, set up the Dual UART chip - * selects. Must be done here since it may be used as the console. - */ - upmconfig(UPMB, (uint *)duart_table, sizeof(duart_table)/sizeof(uint)); - - memctl->memc_mbmr = DUART_MBMR; - memctl->memc_or5 = DUART_OR_VALUE; - memctl->memc_br5 = DUART_BR5_VALUE; - memctl->memc_or6 = DUART_OR_VALUE; - memctl->memc_br6 = DUART_BR6_VALUE; - - return (0); -} - -/* ------------------------------------------------------------------------- */ - -/* base address for SRAM, assume 32-bit port, valid */ -#define NVRAM_BR_VALUE (CONFIG_SYS_SRAM_BASE | BR_PS_32 | BR_V) - -/* up to 64MB - will be adjusted for actual size */ -#define NVRAM_OR_PRELIM (ORMASK(CONFIG_SYS_SRAM_SIZE) \ - | OR_CSNT_SAM | OR_ACS_DIV4 | OR_BI | OR_SCY_5_CLK | OR_EHTR) -/* - * Miscellaneous platform dependent initializations after running in RAM. - */ - -int misc_init_r (void) -{ - volatile immap_t *immap = (immap_t *)CONFIG_SYS_IMMR; - volatile memctl8xx_t *memctl = &immap->im_memctl; - bd_t *bd = gd->bd; - uchar enetaddr[6]; - - memctl->memc_or2 = NVRAM_OR_PRELIM; - memctl->memc_br2 = NVRAM_BR_VALUE; - - /* Is there any SRAM? Is it 16 or 32 bits wide? */ - - /* First look for 32-bit SRAM */ - bd->bi_sramsize = ram_size((ulong*)CONFIG_SYS_SRAM_BASE, CONFIG_SYS_SRAM_SIZE); - - if (bd->bi_sramsize == 0) { - /* no 32-bit SRAM, but there could be 16-bit SRAM since - * it would report size 0 when configured for 32-bit bus. - * Try again with a 16-bit bus. - */ - memctl->memc_br2 |= BR_PS_16; - bd->bi_sramsize = ram_size((ulong*)CONFIG_SYS_SRAM_BASE, CONFIG_SYS_SRAM_SIZE); - } - - if (bd->bi_sramsize == 0) { - memctl->memc_br2 = 0; /* disable select since nothing there */ - } - else { - /* adjust or2 for actual size of SRAM */ - memctl->memc_or2 |= ORMASK(bd->bi_sramsize); - bd->bi_sramstart = CONFIG_SYS_SRAM_BASE; - printf("SRAM: %lu KB\n", bd->bi_sramsize >> 10); - } - - - /* set standard MPC8xx clock so kernel will see the time - * even if it doesn't have a DS1306 clock driver. - * This helps with experimenting with standard kernels. - */ - { - ulong tim; - struct rtc_time tmp; - - rtc_get(&tmp); /* get time from DS1306 RTC */ - - /* convert to seconds since 1970 */ - tim = mktime(tmp.tm_year, tmp.tm_mon, tmp.tm_mday, - tmp.tm_hour, tmp.tm_min, tmp.tm_sec); - - immap->im_sitk.sitk_rtck = KAPWR_KEY; - immap->im_sit.sit_rtc = tim; - } - - /* set up ethernet address for SCC ethernet. If eth1addr - * is present it gets a unique address, otherwise it - * shares the FEC address. - */ - if (!eth_getenv_enetaddr("eth1addr", enetaddr)) { - eth_getenv_enetaddr("ethaddr", enetaddr); - eth_setenv_enetaddr("eth1addr", enetaddr); - } - - return (0); -} - -#if defined(CONFIG_CMD_NAND) -void nand_init(void) -{ - unsigned long totlen = nand_probe(CONFIG_SYS_DFLASH_BASE); - - printf ("%4lu MB\n", totlen >> 20); -} -#endif - -/* ------------------------------------------------------------------------- */ - -/* - * Check memory range for valid RAM. A simple memory test determines - * the actually available RAM size between addresses `base' and - * `base + maxsize'. - * - * The memory size MUST be a power of 2 for this to work. - * - * The only memory modified is 8 bytes at offset 0. This is important - * since for the SRAM this location is reserved for autosizing, so if - * it is modified and the board is reset before ram_size() completes - * no damage is done. Normally even the memory at 0 is preserved. The - * higher SRAM addresses may contain battery backed RAM disk data which - * must never be corrupted. - */ - -static long ram_size(ulong *base, long maxsize) -{ - volatile long *test_addr; - volatile ulong *base_addr = base; - ulong ofs; /* byte offset from base_addr */ - ulong save; /* to make test non-destructive */ - ulong save2; /* to make test non-destructive */ - long ramsize = -1; /* size not determined yet */ - - save = *base_addr; /* save value at 0 so can restore */ - save2 = *(base_addr+1); /* save value at 4 so can restore */ - - /* is any SRAM present? */ - *base_addr = 0x5555aaaa; - - /* It is important to drive the data bus with different data so - * it doesn't remember the value and look like RAM that isn't there. - */ - *(base_addr + 1) = 0xaaaa5555; /* use write to modify data bus */ - - if (*base_addr != 0x5555aaaa) - ramsize = 0; /* no RAM present, or defective */ - else { - *base_addr = 0xaaaa5555; - *(base_addr + 1) = 0x5555aaaa; /* use write to modify data bus */ - if (*base_addr != 0xaaaa5555) - ramsize = 0; /* no RAM present, or defective */ - } - - /* now size it if any is present */ - for (ofs = 4; ofs < maxsize && ramsize < 0; ofs <<= 1) { - test_addr = (long*)((long)base_addr + ofs); /* location to test */ - - *base_addr = ~*test_addr; - if (*base_addr == *test_addr) - ramsize = ofs; /* wrapped back to 0, so this is the size */ - } - - *base_addr = save; /* restore value at 0 */ - *(base_addr+1) = save2; /* restore value at 4 */ - return (ramsize); -} - -/* ------------------------------------------------------------------------- */ -/* sdram table based on the FADS manual */ -/* for chip MB811171622A-100 */ - -/* this table is for 50MHz operation, it should work at all lower speeds */ - -const uint sdram_table[] = -{ - /* single read. (offset 0 in upm RAM) */ - 0x1f07fc04, 0xeeaefc04, 0x11adfc04, 0xefbbbc00, - 0x1ff77c47, - - /* precharge and Mode Register Set initialization (offset 5). - * This is also entered at offset 6 to do Mode Register Set - * without the precharge. - */ - 0x1ff77c34, 0xefeabc34, 0x1fb57c35, - - /* burst read. (offset 8 in upm RAM) */ - 0x1f07fc04, 0xeeaefc04, 0x10adfc04, 0xf0affc00, - 0xf0affc00, 0xf1affc00, 0xefbbbc00, 0x1ff77c47, - _not_used_, _not_used_, _not_used_, _not_used_, - _not_used_, _not_used_, _not_used_, _not_used_, - - /* single write. (offset 18 in upm RAM) */ - /* FADS had 0x1f27fc04, ... - * but most other boards have 0x1f07fc04, which - * sets GPL0 from A11MPC to 0 1/4 clock earlier, - * like the single read. - * This seems better so I am going with the change. - */ - 0x1f07fc04, 0xeeaebc00, 0x01b93c04, 0x1ff77c47, - _not_used_, _not_used_, _not_used_, _not_used_, - - /* burst write. (offset 20 in upm RAM) */ - 0x1f07fc04, 0xeeaebc00, 0x10ad7c00, 0xf0affc00, - 0xf0affc00, 0xe1bbbc04, 0x1ff77c47, _not_used_, - _not_used_, _not_used_, _not_used_, _not_used_, - _not_used_, _not_used_, _not_used_, _not_used_, - - /* refresh. (offset 30 in upm RAM) */ - 0x1ff5fc84, 0xfffffc04, 0xfffffc04, 0xfffffc04, - 0xfffffc84, 0xfffffc07, _not_used_, _not_used_, - _not_used_, _not_used_, _not_used_, _not_used_, - - /* exception. (offset 3c in upm RAM) */ - 0x7ffffc07, _not_used_, _not_used_, _not_used_ }; - -/* ------------------------------------------------------------------------- */ - -#define SDRAM_MAX_SIZE 0x10000000 /* max 256 MB SDRAM */ - -/* precharge and set Mode Register */ -#define SDRAM_MCR_PRE (MCR_OP_RUN | MCR_UPM_A | /* select UPM */ \ - MCR_MB_CS3 | /* chip select */ \ - MCR_MLCF(1) | MCR_MAD(5)) /* 1 time at 0x05 */ - -/* set Mode Register, no precharge */ -#define SDRAM_MCR_MRS (MCR_OP_RUN | MCR_UPM_A | /* select UPM */ \ - MCR_MB_CS3 | /* chip select */ \ - MCR_MLCF(1) | MCR_MAD(6)) /* 1 time at 0x06 */ - -/* runs refresh loop twice so get 8 refresh cycles */ -#define SDRAM_MCR_REFR (MCR_OP_RUN | MCR_UPM_A | /* select UPM */ \ - MCR_MB_CS3 | /* chip select */ \ - MCR_MLCF(2) | MCR_MAD(0x30)) /* twice at 0x30 */ - -/* MAMR values work in either mamr or mbmr */ -#define SDRAM_MAMR_BASE /* refresh at 50MHz */ \ - ((195 << MAMR_PTA_SHIFT) | MAMR_PTAE \ - | MAMR_DSA_1_CYCL /* 1 cycle disable */ \ - | MAMR_RLFA_1X /* Read loop 1 time */ \ - | MAMR_WLFA_1X /* Write loop 1 time */ \ - | MAMR_TLFA_4X) /* Timer loop 4 times */ -/* 8 column SDRAM */ -#define SDRAM_MAMR_8COL (SDRAM_MAMR_BASE \ - | MAMR_AMA_TYPE_0 /* Address MUX 0 */ \ - | MAMR_G0CLA_A11) /* GPL0 A11[MPC] */ - -/* 9 column SDRAM */ -#define SDRAM_MAMR_9COL (SDRAM_MAMR_BASE \ - | MAMR_AMA_TYPE_1 /* Address MUX 1 */ \ - | MAMR_G0CLA_A10) /* GPL0 A10[MPC] */ - -/* base address 0, 32-bit port, SDRAM UPM, valid */ -#define SDRAM_BR_VALUE (BR_PS_32 | BR_MS_UPMA | BR_V) - -/* up to 256MB, SAM, G5LS - will be adjusted for actual size */ -#define SDRAM_OR_PRELIM (ORMASK(SDRAM_MAX_SIZE) | OR_CSNT_SAM | OR_G5LS) - -/* This is the Mode Select Register value for the SDRAM. - * Burst length: 4 - * Burst Type: sequential - * CAS Latency: 2 - * Write Burst Length: burst - */ -#define SDRAM_MODE 0x22 /* CAS latency 2, burst length 4 */ - -/* ------------------------------------------------------------------------- */ - -phys_size_t initdram(int board_type) -{ - volatile immap_t *immap = (immap_t *)CONFIG_SYS_IMMR; - volatile memctl8xx_t *memctl = &immap->im_memctl; - uint size_sdram = 0; - uint size_sdram9 = 0; - uint base = 0; /* SDRAM must start at 0 */ - int i; - - upmconfig(UPMA, (uint *)sdram_table, sizeof(sdram_table)/sizeof(uint)); - - /* Configure the refresh (mostly). This needs to be - * based upon processor clock speed and optimized to provide - * the highest level of performance. - * - * Preliminary prescaler for refresh. - * This value is selected for four cycles in 31.2 us, - * which gives 8192 cycles in 64 milliseconds. - * This may be too fast, but works for any memory. - * It is adjusted to 4096 cycles in 64 milliseconds if - * possible once we know what memory we have. - * - * We have to be careful changing UPM registers after we - * ask it to run these commands. - * - * PTA - periodic timer period for our design is - * 50 MHz x 31.2us - * --------------- = 195 - * 1 x 8 x 1 - * - * 50MHz clock - * 31.2us refresh interval - * SCCR[DFBRG] 0 - * PTP divide by 8 - * 1 chip select - */ - memctl->memc_mptpr = MPTPR_PTP_DIV8; /* 0x0800 */ - memctl->memc_mamr = SDRAM_MAMR_8COL & (~MAMR_PTAE); /* no refresh yet */ - - /* The SDRAM Mode Register value is shifted left 2 bits since - * A30 and A31 don't connect to the SDRAM for 32-bit wide memory. - */ - memctl->memc_mar = SDRAM_MODE << 2; /* MRS code */ - udelay(200); /* SDRAM needs 200uS before set it up */ - - /* Now run the precharge/nop/mrs commands. */ - memctl->memc_mcr = SDRAM_MCR_PRE; - udelay(2); - - /* Run 8 refresh cycles (2 sets of 4) */ - memctl->memc_mcr = SDRAM_MCR_REFR; /* run refresh twice */ - udelay(2); - - /* some brands want Mode Register set after the refresh - * cycles. This shouldn't hurt anything for the brands - * that were happy with the first time we set it. - */ - memctl->memc_mcr = SDRAM_MCR_MRS; - udelay(2); - - memctl->memc_mamr = SDRAM_MAMR_8COL; /* enable refresh */ - memctl->memc_or3 = SDRAM_OR_PRELIM; - memctl->memc_br3 = SDRAM_BR_VALUE + base; - - /* Some brands need at least 10 DRAM accesses to stabilize. - * It wont hurt the brands that don't. - */ - for (i=0; i<10; ++i) { - volatile ulong *addr = (volatile ulong *)base; - ulong val; - - val = *(addr + i); - *(addr + i) = val; - } - - /* Check SDRAM memory Size in 8 column mode. - * For a 9 column memory we will get half the actual size. - */ - size_sdram = ram_size((ulong *)0, SDRAM_MAX_SIZE); - - /* Check SDRAM memory Size in 9 column mode. - * For an 8 column memory we will see at most 4 megabytes. - */ - memctl->memc_mamr = SDRAM_MAMR_9COL; - size_sdram9 = ram_size((ulong *)0, SDRAM_MAX_SIZE); - - if (size_sdram < size_sdram9) /* leave configuration at 9 columns */ - size_sdram = size_sdram9; - else /* go back to 8 columns */ - memctl->memc_mamr = SDRAM_MAMR_8COL; - - /* adjust or3 for actual size of SDRAM - */ - memctl->memc_or3 |= ORMASK(size_sdram); - - /* Adjust refresh rate depending on SDRAM type. - * For types > 128 MBit (32 Mbyte for 2 x16 devices) leave - * it at the current (fast) rate. - * For 16, 64 and 128 MBit half the rate will do. - */ - if (size_sdram <= 32 * 1024 * 1024) - memctl->memc_mptpr = MPTPR_PTP_DIV16; /* 0x0400 */ - - return (size_sdram); -} diff --git a/board/sixnet/sixnet.h b/board/sixnet/sixnet.h deleted file mode 100644 index 046c9de..0000000 --- a/board/sixnet/sixnet.h +++ /dev/null @@ -1,20 +0,0 @@ -/* - * (C) Copyright 2000 - * Wolfgang Denk, DENX Software Engineering, wd@denx.de. - * - * SPDX-License-Identifier: GPL-2.0+ - */ - -/* - * Memory map: - * - * ff100000 -> ff13ffff : FPGA CS1 - * ff030000 -> ff03ffff : EXPANSION CS7 - * ff020000 -> ff02ffff : DATA FLASH CS4 - * ff018000 -> ff01ffff : UART B CS6/UPMB - * ff010000 -> ff017fff : UART A CS5/UPMB - * ff000000 -> ff00ffff : IMAP internal to the MPC855T - * f8000000 -> fbffffff : FLASH CS0 up to 64MB - * f4000000 -> f7ffffff : NVSRAM CS2 up to 64MB - * 00000000 -> 0fffffff : SDRAM CS3/UPMA up to 256MB - */ diff --git a/board/sixnet/u-boot.lds b/board/sixnet/u-boot.lds deleted file mode 100644 index 7ee2012..0000000 --- a/board/sixnet/u-boot.lds +++ /dev/null @@ -1,82 +0,0 @@ -/* - * (C) Copyright 2000-2010 - * Wolfgang Denk, DENX Software Engineering, wd@denx.de. - * - * SPDX-License-Identifier: GPL-2.0+ - */ - -OUTPUT_ARCH(powerpc) - -SECTIONS -{ - /* Read-only sections, merged into text segment: */ - . = + SIZEOF_HEADERS; - .text : - { - arch/powerpc/cpu/mpc8xx/start.o (.text*) - arch/powerpc/cpu/mpc8xx/traps.o (.text*) - - *(.text*) - } - _etext = .; - PROVIDE (etext = .); - .rodata : - { - *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) - } - - /* Read-write section, merged into data segment: */ - . = (. + 0x0FFF) & 0xFFFFF000; - _erotext = .; - PROVIDE (erotext = .); - .reloc : - { - _GOT2_TABLE_ = .; - KEEP(*(.got2)) - KEEP(*(.got)) - PROVIDE(_GLOBAL_OFFSET_TABLE_ = . + 4); - _FIXUP_TABLE_ = .; - KEEP(*(.fixup)) - } - __got2_entries = ((_GLOBAL_OFFSET_TABLE_ - _GOT2_TABLE_) >> 2) - 1; - __fixup_entries = (. - _FIXUP_TABLE_)>>2; - - .data : - { - *(.data*) - *(.sdata*) - } - _edata = .; - PROVIDE (edata = .); - - . = .; - - . = ALIGN(4); - .u_boot_list : { - KEEP(*(SORT(.u_boot_list*))); - } - - - . = .; - __start___ex_table = .; - __ex_table : { *(__ex_table) } - __stop___ex_table = .; - - . = ALIGN(4096); - __init_begin = .; - .text.init : { *(.text.init) } - .data.init : { *(.data.init) } - . = ALIGN(4096); - __init_end = .; - - __bss_start = .; - .bss (NOLOAD) : - { - *(.bss*) - *(.sbss*) - *(COMMON) - . = ALIGN(4); - } - __bss_end = . ; - PROVIDE (end = .); -} diff --git a/board/socrates/nand.c b/board/socrates/nand.c index 3802c7e..7394478 100644 --- a/board/socrates/nand.c +++ b/board/socrates/nand.c @@ -18,7 +18,9 @@ static void sc_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len); static u_char sc_nand_read_byte(struct mtd_info *mtd); static u16 sc_nand_read_word(struct mtd_info *mtd); static void sc_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len); +#if defined(CONFIG_MTD_NAND_VERIFY_WRITE) static int sc_nand_verify_buf(struct mtd_info *mtd, const u_char *buf, int len); +#endif static int sc_nand_device_ready(struct mtd_info *mtdinfo); #define FPGA_NAND_CMD_MASK (0x7 << 28) @@ -100,6 +102,7 @@ static void sc_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len) } } +#if defined(CONFIG_MTD_NAND_VERIFY_WRITE) /** * sc_nand_verify_buf - Verify chip data against buffer * @mtd: MTD device structure @@ -116,6 +119,7 @@ static int sc_nand_verify_buf(struct mtd_info *mtd, const u_char *buf, int len) } return 0; } +#endif /** * sc_nand_device_ready - Check the NAND device is ready for next command. @@ -174,7 +178,9 @@ int board_nand_init(struct nand_chip *nand) nand->read_word = sc_nand_read_word; nand->write_buf = sc_nand_write_buf; nand->read_buf = sc_nand_read_buf; +#if defined(CONFIG_MTD_NAND_VERIFY_WRITE) nand->verify_buf = sc_nand_verify_buf; +#endif return 0; } diff --git a/board/solidrun/hummingboard/hummingboard.c b/board/solidrun/hummingboard/hummingboard.c index 2e2fb2a..6d204b3 100644 --- a/board/solidrun/hummingboard/hummingboard.c +++ b/board/solidrun/hummingboard/hummingboard.c @@ -144,8 +144,7 @@ int board_phy_config(struct phy_device *phydev) int board_eth_init(bd_t *bis) { - struct iomuxc_base_regs *const iomuxc_regs = - (struct iomuxc_base_regs *)IOMUXC_BASE_ADDR; + struct iomuxc *const iomuxc_regs = (struct iomuxc *)IOMUXC_BASE_ADDR; int ret = enable_fec_anatop_clock(ENET_25MHz); if (ret) diff --git a/board/stx/stxxtc/Kconfig b/board/stx/stxxtc/Kconfig deleted file mode 100644 index c444cff..0000000 --- a/board/stx/stxxtc/Kconfig +++ /dev/null @@ -1,15 +0,0 @@ -if TARGET_STXXTC - -config SYS_BOARD - string - default "stxxtc" - -config SYS_VENDOR - string - default "stx" - -config SYS_CONFIG_NAME - string - default "stxxtc" - -endif diff --git a/board/stx/stxxtc/MAINTAINERS b/board/stx/stxxtc/MAINTAINERS deleted file mode 100644 index 5ea36b2..0000000 --- a/board/stx/stxxtc/MAINTAINERS +++ /dev/null @@ -1,6 +0,0 @@ -STXXTC BOARD -M: Dan Malek <dan@embeddedalley.com> -S: Orphan (since 2014-06) -F: board/stx/stxxtc/ -F: include/configs/stxxtc.h -F: configs/stxxtc_defconfig diff --git a/board/stx/stxxtc/Makefile b/board/stx/stxxtc/Makefile deleted file mode 100644 index 6738d4e..0000000 --- a/board/stx/stxxtc/Makefile +++ /dev/null @@ -1,8 +0,0 @@ -# -# (C) Copyright 2000-2006 -# Wolfgang Denk, DENX Software Engineering, wd@denx.de. -# -# SPDX-License-Identifier: GPL-2.0+ -# - -obj-y = stxxtc.o diff --git a/board/stx/stxxtc/README.stxxtc b/board/stx/stxxtc/README.stxxtc deleted file mode 100644 index 7d9d4d3..0000000 --- a/board/stx/stxxtc/README.stxxtc +++ /dev/null @@ -1,59 +0,0 @@ - - -First, some build notes on the Silicon Turnkey eXpress XTc. - -This board has both 87x/88x procesor options at various -frequencies. The configuration file has some macros for setting -the clock speed, not all have been tested. They all have -a 10MHz input clock. Please do not check in a configuration -file that selects a high speed not available on all processors. -We chose the 66MHz core and bus speed, which should be OK on -all boards. If you have a processor, lucky you! :-) -Just build a new configuration with that speed, check -the macro configuration to ensure it's correct. If the -macro is updated, please check that in, but keep default -processor speed. - -The board is likely to have more than 1Mbyte of NOR boot flash. -It was also configured with a high boot vector (Dan's fault) -so the standard 8xx mapping doesn't work well. We had to move -the addresses around a little bit so one copy would work. The -flash got fragmented, and we are working on a better solution. -There is an "xtc.cfg" floating around for the BDI2000, use -that for programming a new version of U-Boot. You can probably -find it on the Silicon Turnkey eXpress (www.silicontkx.com), -Embedded Alley Solutions (embeddedalley.com), or Denx (denx.de) -servers. - -The board will also have various SDRAM sizes, but the code -should automatically determine the amount of memory. - -There are a couple of different board versions, visually -they use different BGA or surface mount memory parts. However, -they are logically the same board. - -Now, some operational notes. - -The board has the option of sporting two FEC Ethernet ports. -The second port isn't configured to be automatically available -because it would cause U-Boot to generate a board data structure -(the bd_t) with multiple MAC addresses and be incompatible with -standard 8xx kernel builds. You can use/test the second FEC -in U-Boot by assigning an 'eth1addr' and selecting the second -FEC as the port to use. - -Since this is just a development board and not a product, STx -does not assign unique MAC addresses. We just pilfer the -"default" ones used by Wolfgang on some other boards. Please -ensure you assign unique MAC addresses when using these boards. - -The serial port baud rate is 38400, because that's the way -I like it :-) - -Thanks to Pantelis for lots of the work on this board port. - -Have Fun! - - -- Dan - -15 August 2005 diff --git a/board/stx/stxxtc/stxxtc.c b/board/stx/stxxtc/stxxtc.c deleted file mode 100644 index 1996efb..0000000 --- a/board/stx/stxxtc/stxxtc.c +++ /dev/null @@ -1,592 +0,0 @@ -/* - * (C) Copyright 2000-2004 - * Pantelis Antoniou, Intracom S.A., panto@intracom.gr - * Wolfgang Denk, DENX Software Engineering, wd@denx.de. - * (C) Copyright 2005 - * Dan Malek, Embedded Edge, LLC, dan@embeddededge.com - * - * SPDX-License-Identifier: GPL-2.0+ - */ - -/* - * U-Boot port on STx XTc board - * Mostly copied from Netta - */ - -#include <common.h> -#include <miiphy.h> - -#include "mpc8xx.h" - -#ifdef CONFIG_HW_WATCHDOG -#include <watchdog.h> -#endif - -/****************************************************************/ - -/* some sane bit macros */ -#define _BD(_b) (1U << (31-(_b))) -#define _BDR(_l, _h) (((((1U << (31-(_l))) - 1) << 1) | 1) & ~((1U << (31-(_h))) - 1)) - -#define _BW(_b) (1U << (15-(_b))) -#define _BWR(_l, _h) (((((1U << (15-(_l))) - 1) << 1) | 1) & ~((1U << (15-(_h))) - 1)) - -#define _BB(_b) (1U << (7-(_b))) -#define _BBR(_l, _h) (((((1U << (7-(_l))) - 1) << 1) | 1) & ~((1U << (7-(_h))) - 1)) - -#define _B(_b) _BD(_b) -#define _BR(_l, _h) _BDR(_l, _h) - -/****************************************************************/ - -/* - * Check Board Identity: - * - * Return 1 always. - */ - -int checkboard(void) -{ - printf ("Silicon Turnkey eXpress XTc\n"); - return (0); -} - -/****************************************************************/ - -#define _NOT_USED_ 0xFFFFFFFF - -/****************************************************************/ - -#define CS_0000 0x00000000 -#define CS_0001 0x10000000 -#define CS_0010 0x20000000 -#define CS_0011 0x30000000 -#define CS_0100 0x40000000 -#define CS_0101 0x50000000 -#define CS_0110 0x60000000 -#define CS_0111 0x70000000 -#define CS_1000 0x80000000 -#define CS_1001 0x90000000 -#define CS_1010 0xA0000000 -#define CS_1011 0xB0000000 -#define CS_1100 0xC0000000 -#define CS_1101 0xD0000000 -#define CS_1110 0xE0000000 -#define CS_1111 0xF0000000 - -#define BS_0000 0x00000000 -#define BS_0001 0x01000000 -#define BS_0010 0x02000000 -#define BS_0011 0x03000000 -#define BS_0100 0x04000000 -#define BS_0101 0x05000000 -#define BS_0110 0x06000000 -#define BS_0111 0x07000000 -#define BS_1000 0x08000000 -#define BS_1001 0x09000000 -#define BS_1010 0x0A000000 -#define BS_1011 0x0B000000 -#define BS_1100 0x0C000000 -#define BS_1101 0x0D000000 -#define BS_1110 0x0E000000 -#define BS_1111 0x0F000000 - -#define GPL0_AAAA 0x00000000 -#define GPL0_AAA0 0x00200000 -#define GPL0_AAA1 0x00300000 -#define GPL0_000A 0x00800000 -#define GPL0_0000 0x00A00000 -#define GPL0_0001 0x00B00000 -#define GPL0_111A 0x00C00000 -#define GPL0_1110 0x00E00000 -#define GPL0_1111 0x00F00000 - -#define GPL1_0000 0x00000000 -#define GPL1_0001 0x00040000 -#define GPL1_1110 0x00080000 -#define GPL1_1111 0x000C0000 - -#define GPL2_0000 0x00000000 -#define GPL2_0001 0x00010000 -#define GPL2_1110 0x00020000 -#define GPL2_1111 0x00030000 - -#define GPL3_0000 0x00000000 -#define GPL3_0001 0x00004000 -#define GPL3_1110 0x00008000 -#define GPL3_1111 0x0000C000 - -#define GPL4_0000 0x00000000 -#define GPL4_0001 0x00001000 -#define GPL4_1110 0x00002000 -#define GPL4_1111 0x00003000 - -#define GPL5_0000 0x00000000 -#define GPL5_0001 0x00000400 -#define GPL5_1110 0x00000800 -#define GPL5_1111 0x00000C00 -#define LOOP 0x00000080 - -#define EXEN 0x00000040 - -#define AMX_COL 0x00000000 -#define AMX_ROW 0x00000020 -#define AMX_MAR 0x00000030 - -#define NA 0x00000008 - -#define UTA 0x00000004 - -#define TODT 0x00000002 - -#define LAST 0x00000001 - -#define A10_AAAA GPL0_AAAA -#define A10_AAA0 GPL0_AAA0 -#define A10_AAA1 GPL0_AAA1 -#define A10_000A GPL0_000A -#define A10_0000 GPL0_0000 -#define A10_0001 GPL0_0001 -#define A10_111A GPL0_111A -#define A10_1110 GPL0_1110 -#define A10_1111 GPL0_1111 - -#define RAS_0000 GPL1_0000 -#define RAS_0001 GPL1_0001 -#define RAS_1110 GPL1_1110 -#define RAS_1111 GPL1_1111 - -#define CAS_0000 GPL2_0000 -#define CAS_0001 GPL2_0001 -#define CAS_1110 GPL2_1110 -#define CAS_1111 GPL2_1111 - -#define WE_0000 GPL3_0000 -#define WE_0001 GPL3_0001 -#define WE_1110 GPL3_1110 -#define WE_1111 GPL3_1111 - -/* #define CAS_LATENCY 3 */ -#define CAS_LATENCY 2 - -const uint sdram_table[0x40] = { - -#if CAS_LATENCY == 3 - /* RSS */ - CS_0001 | BS_1111 | A10_AAAA | RAS_0001 | CAS_1111 | WE_1111 | AMX_COL | UTA, /* ACT */ - CS_1111 | BS_1111 | A10_0000 | RAS_1111 | CAS_1111 | WE_1111 | AMX_COL | UTA, /* NOP */ - CS_0000 | BS_1111 | A10_0001 | RAS_1111 | CAS_0001 | WE_1111 | AMX_COL | UTA, /* READ */ - CS_0001 | BS_0001 | A10_1111 | RAS_0001 | CAS_1111 | WE_0001 | AMX_COL | UTA, /* PALL */ - CS_1111 | BS_1111 | A10_1111 | RAS_1111 | CAS_1111 | WE_1111 | AMX_COL, /* NOP */ - CS_1111 | BS_1111 | A10_1111 | RAS_1111 | CAS_1111 | WE_1111 | AMX_COL | UTA | TODT | LAST, /* NOP */ - _NOT_USED_, _NOT_USED_, - - /* RBS */ - CS_0001 | BS_1111 | A10_AAAA | RAS_0001 | CAS_1111 | WE_1111 | AMX_COL | UTA, /* ACT */ - CS_1111 | BS_1111 | A10_0000 | RAS_1111 | CAS_1111 | WE_1111 | AMX_COL | UTA, /* NOP */ - CS_0001 | BS_1111 | A10_0001 | RAS_1111 | CAS_0001 | WE_1111 | AMX_COL | UTA, /* READ */ - CS_1111 | BS_0000 | A10_1111 | RAS_1111 | CAS_1111 | WE_1111 | AMX_COL | UTA, /* NOP */ - CS_1111 | BS_0000 | A10_1111 | RAS_1111 | CAS_1111 | WE_1111 | AMX_COL, /* NOP */ - CS_1111 | BS_0000 | A10_1111 | RAS_1111 | CAS_1111 | WE_1111 | AMX_COL, /* NOP */ - CS_0001 | BS_0001 | A10_1111 | RAS_0001 | CAS_1111 | WE_0001 | AMX_COL, /* PALL */ - CS_1111 | BS_1111 | A10_1111 | RAS_1111 | CAS_1111 | WE_1111 | AMX_COL | TODT | LAST, /* NOP */ - _NOT_USED_, _NOT_USED_, _NOT_USED_, _NOT_USED_, - _NOT_USED_, _NOT_USED_, _NOT_USED_, _NOT_USED_, - - /* WSS */ - CS_0001 | BS_1111 | A10_AAAA | RAS_0001 | CAS_1111 | WE_1111 | AMX_COL | UTA, /* ACT */ - CS_1111 | BS_1111 | A10_0000 | RAS_1111 | CAS_1111 | WE_1111 | AMX_COL, /* NOP */ - CS_0000 | BS_0001 | A10_0000 | RAS_1111 | CAS_0001 | WE_0000 | AMX_COL | UTA, /* WRITE */ - CS_0001 | BS_1111 | A10_1111 | RAS_0001 | CAS_1111 | WE_0001 | AMX_COL | UTA, /* PALL */ - CS_1111 | BS_1111 | A10_1111 | RAS_1111 | CAS_1111 | WE_1111 | AMX_COL | UTA | TODT | LAST, /* NOP */ - _NOT_USED_, _NOT_USED_, _NOT_USED_, - - /* WBS */ - CS_0001 | BS_1111 | A10_AAAA | RAS_0001 | CAS_1111 | WE_1111 | AMX_COL | UTA, /* ACT */ - CS_1111 | BS_1111 | A10_0000 | RAS_1111 | CAS_1111 | WE_1111 | AMX_COL, /* NOP */ - CS_0001 | BS_0000 | A10_0000 | RAS_1111 | CAS_0001 | WE_0000 | AMX_COL, /* WRITE */ - CS_1111 | BS_0000 | A10_1111 | RAS_1111 | CAS_1111 | WE_1111 | AMX_COL, /* NOP */ - CS_1111 | BS_0000 | A10_1111 | RAS_1111 | CAS_1111 | WE_1111 | AMX_COL, /* NOP */ - CS_1111 | BS_0001 | A10_1111 | RAS_1111 | CAS_1111 | WE_1111 | AMX_COL | UTA, /* NOP */ - CS_1111 | BS_1111 | A10_1111 | RAS_1111 | CAS_1111 | WE_1111 | AMX_COL | UTA, /* NOP */ - CS_0001 | BS_1111 | A10_1111 | RAS_0001 | CAS_1111 | WE_0001 | AMX_COL | UTA, /* PALL */ - CS_1111 | BS_1111 | A10_1111 | RAS_1111 | CAS_1111 | WE_1111 | AMX_COL | UTA | TODT | LAST, /* NOP */ - _NOT_USED_, _NOT_USED_, _NOT_USED_, _NOT_USED_, - _NOT_USED_, _NOT_USED_, _NOT_USED_, -#endif - -#if CAS_LATENCY == 2 - /* RSS */ - CS_0001 | BS_1111 | A10_AAAA | RAS_0001 | CAS_1111 | WE_1111 | AMX_COL | UTA, /* ACT */ - CS_1110 | BS_1110 | A10_0000 | RAS_1111 | CAS_1110 | WE_1111 | AMX_COL | UTA, /* NOP */ - CS_0001 | BS_0001 | A10_0000 | RAS_1111 | CAS_0001 | WE_1111 | AMX_COL | UTA, /* READ */ - CS_1110 | BS_1111 | A10_0001 | RAS_1110 | CAS_1111 | WE_1110 | AMX_COL, /* NOP */ - CS_0001 | BS_1111 | A10_1111 | RAS_0001 | CAS_1111 | WE_0001 | AMX_COL | UTA | TODT | LAST, /* PALL */ - _NOT_USED_, - _NOT_USED_, _NOT_USED_, - - /* RBS */ - CS_0001 | BS_1111 | A10_AAAA | RAS_0001 | CAS_1111 | WE_1111 | AMX_COL | UTA, /* ACT */ - CS_1110 | BS_1110 | A10_0000 | RAS_1111 | CAS_1110 | WE_1111 | AMX_COL | UTA, /* NOP */ - CS_0001 | BS_0000 | A10_0000 | RAS_1111 | CAS_0001 | WE_1111 | AMX_COL | UTA, /* READ */ - CS_1111 | BS_0000 | A10_0000 | RAS_1111 | CAS_1111 | WE_1111 | AMX_COL, /* NOP */ - CS_1111 | BS_0000 | A10_0000 | RAS_1111 | CAS_1111 | WE_1111 | AMX_COL, /* NOP */ - CS_1111 | BS_0001 | A10_0000 | RAS_1111 | CAS_1111 | WE_1111 | AMX_COL, /* NOP */ - CS_1110 | BS_1111 | A10_0001 | RAS_1110 | CAS_1111 | WE_1110 | AMX_COL, /* NOP */ - CS_0001 | BS_1111 | A10_1111 | RAS_0001 | CAS_1111 | WE_0001 | AMX_COL | UTA | TODT | LAST, /* PALL */ - _NOT_USED_, - _NOT_USED_, _NOT_USED_, _NOT_USED_, - _NOT_USED_, _NOT_USED_, _NOT_USED_, _NOT_USED_, - - /* WSS */ - CS_0001 | BS_1111 | A10_AAA0 | RAS_0001 | CAS_1111 | WE_1111 | AMX_COL | UTA, /* ACT */ - CS_1110 | BS_1110 | A10_0000 | RAS_1111 | CAS_1110 | WE_1110 | AMX_COL, /* NOP */ - CS_0000 | BS_0001 | A10_0001 | RAS_1110 | CAS_0001 | WE_0000 | AMX_COL | UTA, /* WRITE */ - CS_0001 | BS_1111 | A10_1111 | RAS_0001 | CAS_1111 | WE_0001 | AMX_COL | UTA | TODT | LAST, /* PALL */ - _NOT_USED_, - _NOT_USED_, _NOT_USED_, - _NOT_USED_, - - /* WBS */ - CS_0001 | BS_1111 | A10_AAAA | RAS_0001 | CAS_1111 | WE_1111 | AMX_COL | UTA, /* ACT */ - CS_1110 | BS_1110 | A10_0000 | RAS_1111 | CAS_1110 | WE_1110 | AMX_COL, /* NOP */ - CS_0001 | BS_0000 | A10_0000 | RAS_1111 | CAS_0001 | WE_0001 | AMX_COL, /* WRITE */ - CS_1111 | BS_0000 | A10_0000 | RAS_1111 | CAS_1111 | WE_1111 | AMX_COL, /* NOP */ - CS_1111 | BS_0000 | A10_0000 | RAS_1111 | CAS_1111 | WE_1111 | AMX_COL, /* NOP */ - CS_1110 | BS_0001 | A10_0001 | RAS_1110 | CAS_1111 | WE_1110 | AMX_COL | UTA, /* NOP */ - CS_0001 | BS_1111 | A10_1111 | RAS_0001 | CAS_1111 | WE_0001 | AMX_COL | UTA | TODT | LAST, /* PALL */ - _NOT_USED_, - _NOT_USED_, _NOT_USED_, _NOT_USED_, - _NOT_USED_, _NOT_USED_, _NOT_USED_, - _NOT_USED_, _NOT_USED_, - -#endif - - /* UPT */ - CS_0001 | BS_1111 | A10_1111 | RAS_0001 | CAS_0001 | WE_1111 | AMX_COL | UTA | LOOP, /* ATRFR */ - CS_1111 | BS_1111 | A10_1111 | RAS_1111 | CAS_1111 | WE_1111 | AMX_COL | UTA, /* NOP */ - CS_1111 | BS_1111 | A10_1111 | RAS_1111 | CAS_1111 | WE_1111 | AMX_COL | UTA, /* NOP */ - CS_1111 | BS_1111 | A10_1111 | RAS_1111 | CAS_1111 | WE_1111 | AMX_COL | UTA, /* NOP */ - CS_1111 | BS_1111 | A10_1111 | RAS_1111 | CAS_1111 | WE_1111 | AMX_COL | UTA | LOOP, /* NOP */ - CS_1111 | BS_1111 | A10_1111 | RAS_1111 | CAS_1111 | WE_1111 | AMX_COL | UTA | TODT | LAST, /* NOP */ - _NOT_USED_, _NOT_USED_, _NOT_USED_, _NOT_USED_, - _NOT_USED_, _NOT_USED_, - - /* EXC */ - CS_0001 | BS_1111 | A10_1111 | RAS_0001 | CAS_1111 | WE_0001 | AMX_COL | UTA | LAST, - _NOT_USED_, - - /* REG */ - CS_1110 | BS_1111 | A10_1110 | RAS_1110 | CAS_1110 | WE_1110 | AMX_MAR | UTA, - CS_0001 | BS_1111 | A10_0001 | RAS_0001 | CAS_0001 | WE_0001 | AMX_MAR | UTA | LAST, -}; - -static const uint nandcs_table[0x40] = { - /* RSS */ - CS_1000 | GPL4_1111 | GPL5_1111 | UTA, - CS_0000 | GPL4_1110 | GPL5_1111 | UTA, - CS_0000 | GPL4_0000 | GPL5_1111 | UTA, - CS_0000 | GPL4_0000 | GPL5_1111 | UTA, - CS_0000 | GPL4_0000 | GPL5_1111, - CS_0000 | GPL4_0001 | GPL5_1111 | UTA, - CS_0000 | GPL4_1111 | GPL5_1111 | UTA, - CS_0011 | GPL4_1111 | GPL5_1111 | UTA | LAST, /* NOP */ - - /* RBS */ - _NOT_USED_, _NOT_USED_, _NOT_USED_, _NOT_USED_, - _NOT_USED_, _NOT_USED_, _NOT_USED_, _NOT_USED_, - _NOT_USED_, _NOT_USED_, _NOT_USED_, _NOT_USED_, - _NOT_USED_, _NOT_USED_, _NOT_USED_, _NOT_USED_, - - /* WSS */ - CS_1000 | GPL4_1111 | GPL5_1110 | UTA, - CS_0000 | GPL4_1111 | GPL5_0000 | UTA, - CS_0000 | GPL4_1111 | GPL5_0000 | UTA, - CS_0000 | GPL4_1111 | GPL5_0000 | UTA, - CS_0000 | GPL4_1111 | GPL5_0001 | UTA, - CS_0000 | GPL4_1111 | GPL5_1111 | UTA, - CS_0000 | GPL4_1111 | GPL5_1111, - CS_0011 | GPL4_1111 | GPL5_1111 | UTA | LAST, - - /* WBS */ - _NOT_USED_, _NOT_USED_, _NOT_USED_, _NOT_USED_, - _NOT_USED_, _NOT_USED_, _NOT_USED_, _NOT_USED_, - _NOT_USED_, _NOT_USED_, _NOT_USED_, _NOT_USED_, - _NOT_USED_, _NOT_USED_, _NOT_USED_, _NOT_USED_, - - /* UPT */ - _NOT_USED_, _NOT_USED_, _NOT_USED_, _NOT_USED_, - _NOT_USED_, _NOT_USED_, _NOT_USED_, _NOT_USED_, - _NOT_USED_, _NOT_USED_, _NOT_USED_, _NOT_USED_, - - /* EXC */ - CS_0001 | LAST, - _NOT_USED_, - - /* REG */ - CS_1110 , - CS_0001 | LAST, -}; - -/* 0xC8 = 0b11001000 , CAS3, >> 2 = 0b00 11 0 010 */ -/* 0x88 = 0b10001000 , CAS2, >> 2 = 0b00 10 0 010 */ -#define MAR_SDRAM_INIT ((CAS_LATENCY << 6) | 0x00000008LU) - -/* 9 */ -#define CONFIG_SYS_MAMR ((CONFIG_SYS_MAMR_PTA << MAMR_PTA_SHIFT) | MAMR_PTAE | \ - MAMR_AMA_TYPE_1 | MAMR_DSA_1_CYCL | MAMR_G0CLA_A10 | \ - MAMR_RLFA_1X | MAMR_WLFA_1X | MAMR_TLFA_4X) - -void check_ram(unsigned int addr, unsigned int size) -{ - unsigned int i, j, v, vv; - volatile unsigned int *p; - unsigned int pv; - - p = (unsigned int *)addr; - pv = (unsigned int)p; - for (i = 0; i < size / sizeof(unsigned int); i++, pv += sizeof(unsigned int)) - *p++ = pv; - - p = (unsigned int *)addr; - for (i = 0; i < size / sizeof(unsigned int); i++) { - v = (unsigned int)p; - vv = *p; - if (vv != v) { - printf("%p: read %08x instead of %08x\n", p, vv, v); - hang(); - } - p++; - } - - for (j = 0; j < 5; j++) { - switch (j) { - case 0: v = 0x00000000; break; - case 1: v = 0xffffffff; break; - case 2: v = 0x55555555; break; - case 3: v = 0xaaaaaaaa; break; - default:v = 0xdeadbeef; break; - } - p = (unsigned int *)addr; - for (i = 0; i < size / sizeof(unsigned int); i++) { - *p = v; - vv = *p; - if (vv != v) { - printf("%p: read %08x instead of %08x\n", p, vv, v); - hang(); - } - *p = ~v; - p++; - } - } -} - -#define DO_LOOP do { for (;;) asm volatile ("nop" : : : "memory"); } while(0) - -phys_size_t initdram(int board_type) -{ - volatile immap_t *immap = (immap_t *) CONFIG_SYS_IMMR; - volatile memctl8xx_t *memctl = &immap->im_memctl; - long int size; - u32 d1, d2; - - upmconfig(UPMA, (uint *) sdram_table, sizeof(sdram_table) / sizeof(sdram_table[0])); - - /* - * Preliminary prescaler for refresh - */ - memctl->memc_mptpr = MPTPR_PTP_DIV8; - - memctl->memc_mar = MAR_SDRAM_INIT; /* 32-bit address to be output on the address bus if AMX = 0b11 */ - - /* - * Map controller bank 3 to the SDRAM bank at preliminary address. - */ - memctl->memc_or4 = CONFIG_SYS_OR4_PRELIM; - memctl->memc_br4 = CONFIG_SYS_BR4_PRELIM; - - memctl->memc_mamr = CONFIG_SYS_MAMR & ~MAMR_PTAE; /* no refresh yet */ - - udelay(200); - - /* perform SDRAM initialisation sequence */ - memctl->memc_mcr = MCR_OP_RUN | MCR_UPM_A | MCR_MB_CS4 | MCR_MLCF(1) | MCR_MAD(0x3C); /* precharge all */ - udelay(1); - - memctl->memc_mcr = MCR_OP_RUN | MCR_UPM_A | MCR_MB_CS4 | MCR_MLCF(2) | MCR_MAD(0x30); /* refresh 2 times(0) */ - udelay(1); - - memctl->memc_mcr = MCR_OP_RUN | MCR_UPM_A | MCR_MB_CS4 | MCR_MLCF(1) | MCR_MAD(0x3E); /* exception program (write mar)*/ - udelay(1); - - memctl->memc_mamr |= MAMR_PTAE; /* enable refresh */ - - udelay(10000); - - - d1 = 0xAA55AA55; - *(volatile u32 *)0 = d1; - d2 = *(volatile u32 *)0; - if (d1 != d2) { - printf("DRAM fails: wrote 0x%08x read 0x%08x\n", d1, d2); - DO_LOOP; - } - - d1 = 0x55AA55AA; - *(volatile u32 *)0 = d1; - d2 = *(volatile u32 *)0; - if (d1 != d2) { - printf("DRAM fails: wrote 0x%08x read 0x%08x\n", d1, d2); - DO_LOOP; - } - - d1 = 0x12345678; - *(volatile u32 *)0 = d1; - d2 = *(volatile u32 *)0; - if (d1 != d2) { - printf("DRAM fails: wrote 0x%08x read 0x%08x\n", d1, d2); - DO_LOOP; - } - - size = get_ram_size((long *)0, SDRAM_MAX_SIZE); - - return size; -} - -/* ------------------------------------------------------------------------- */ - -void reset_phys(void) -{ - int phyno; - unsigned short v; - - udelay(10000); - /* reset the damn phys */ - mii_init(); - - for (phyno = 0; phyno < 32; ++phyno) { - miiphy_read("FEC", phyno, MII_PHYSID1, &v); - if (v == 0xFFFF) - continue; - miiphy_write("FEC", phyno, MII_BMCR, BMCR_PDOWN); - udelay(10000); - miiphy_write("FEC", phyno, MII_BMCR, BMCR_RESET | BMCR_ANENABLE); - udelay(10000); - } -} - -/* ------------------------------------------------------------------------- */ - -/* GP = general purpose, SP = special purpose (on chip peripheral) */ - -/* bits that can have a special purpose or can be configured as inputs/outputs */ -#define PA_GP_INMASK _BW(6) -#define PA_GP_OUTMASK (_BW(7)) -#define PA_SP_MASK 0 -#define PA_ODR_VAL 0 -#define PA_GP_OUTVAL (_BW(7)) -#define PA_SP_DIRVAL 0 - -#define PB_GP_INMASK 0 -#define PB_GP_OUTMASK (_B(23)) -#define PB_SP_MASK 0 -#define PB_ODR_VAL 0 -#define PB_GP_OUTVAL (_B(23)) -#define PB_SP_DIRVAL 0 - -#define PC_GP_INMASK 0 -#define PC_GP_OUTMASK (_BW(15)) - -#define PC_SP_MASK 0 -#define PC_SOVAL 0 -#define PC_INTVAL 0 -#define PC_GP_OUTVAL 0 -#define PC_SP_DIRVAL 0 - -#define PE_GP_INMASK 0 -#define PE_GP_OUTMASK 0 -#define PE_GP_OUTVAL 0 - -#define PE_SP_MASK 0 -#define PE_ODR_VAL 0 -#define PE_SP_DIRVAL 0 - -int board_early_init_f(void) -{ - volatile immap_t *immap = (immap_t *) CONFIG_SYS_IMMR; - volatile iop8xx_t *ioport = &immap->im_ioport; - volatile cpm8xx_t *cpm = &immap->im_cpm; - volatile memctl8xx_t *memctl = &immap->im_memctl; - - (void)ioport; - (void)cpm; -#if 1 - /* NAND chip select */ - upmconfig(UPMB, (uint *) nandcs_table, sizeof(nandcs_table) / sizeof(nandcs_table[0])); - memctl->memc_or2 = ((0xFFFFFFFFLU & ~(NAND_SIZE - 1)) | OR_BI | OR_G5LS); - memctl->memc_br2 = ((NAND_BASE & BR_BA_MSK) | BR_PS_8 | BR_V | BR_MS_UPMB); - memctl->memc_mbmr = 0; /* all clear */ -#endif - - memctl->memc_br5 &= ~BR_V; - memctl->memc_br6 &= ~BR_V; - memctl->memc_br7 &= ~BR_V; - -#if 1 - ioport->iop_padat = PA_GP_OUTVAL; - ioport->iop_paodr = PA_ODR_VAL; - ioport->iop_padir = PA_GP_OUTMASK | PA_SP_DIRVAL; - ioport->iop_papar = PA_SP_MASK; - - cpm->cp_pbdat = PB_GP_OUTVAL; - cpm->cp_pbodr = PB_ODR_VAL; - cpm->cp_pbdir = PB_GP_OUTMASK | PB_SP_DIRVAL; - cpm->cp_pbpar = PB_SP_MASK; - - ioport->iop_pcdat = PC_GP_OUTVAL; - ioport->iop_pcdir = PC_GP_OUTMASK | PC_SP_DIRVAL; - ioport->iop_pcso = PC_SOVAL; - ioport->iop_pcint = PC_INTVAL; - ioport->iop_pcpar = PC_SP_MASK; - - cpm->cp_pedat = PE_GP_OUTVAL; - cpm->cp_peodr = PE_ODR_VAL; - cpm->cp_pedir = PE_GP_OUTMASK | PE_SP_DIRVAL; - cpm->cp_pepar = PE_SP_MASK; -#endif - - return 0; -} - -#ifdef CONFIG_HW_WATCHDOG - -void hw_watchdog_reset(void) -{ - /* XXX add here the really funky stuff */ -} - -#endif - -#if defined(CONFIG_SYS_CONSOLE_IS_IN_ENV) && defined(CONFIG_SYS_CONSOLE_OVERWRITE_ROUTINE) -int overwrite_console(void) -{ - /* printf("overwrite_console called\n"); */ - return 0; -} -#endif - -extern int drv_phone_init(void); -extern int drv_phone_use_me(void); -extern int drv_phone_is_idle(void); - -int misc_init_r(void) -{ - return 0; -} - -int last_stage_init(void) -{ - reset_phys(); - - return 0; -} diff --git a/board/stx/stxxtc/u-boot.lds b/board/stx/stxxtc/u-boot.lds deleted file mode 100644 index 0dff5a4..0000000 --- a/board/stx/stxxtc/u-boot.lds +++ /dev/null @@ -1,82 +0,0 @@ -/* - * (C) Copyright 2000-2010 - * Wolfgang Denk, DENX Software Engineering, wd@denx.de. - * - * SPDX-License-Identifier: GPL-2.0+ - */ - -OUTPUT_ARCH(powerpc) - -SECTIONS -{ - /* Read-only sections, merged into text segment: */ - . = + SIZEOF_HEADERS; - .text : - { - arch/powerpc/cpu/mpc8xx/start.o (.text*) - arch/powerpc/cpu/mpc8xx/traps.o (.text*) - - *(.text*) - } - _etext = .; - PROVIDE (etext = .); - .rodata : - { - *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) - } - - /* Read-write section, merged into data segment: */ - . = (. + 0x00FF) & 0xFFFFFF00; - _erotext = .; - PROVIDE (erotext = .); - .reloc : - { - _GOT2_TABLE_ = .; - KEEP(*(.got2)) - KEEP(*(.got)) - PROVIDE(_GLOBAL_OFFSET_TABLE_ = . + 4); - _FIXUP_TABLE_ = .; - KEEP(*(.fixup)) - } - __got2_entries = ((_GLOBAL_OFFSET_TABLE_ - _GOT2_TABLE_) >> 2) - 1; - __fixup_entries = (. - _FIXUP_TABLE_)>>2; - - .data : - { - *(.data*) - *(.sdata*) - } - _edata = .; - PROVIDE (edata = .); - - . = .; - - . = ALIGN(4); - .u_boot_list : { - KEEP(*(SORT(.u_boot_list*))); - } - - - . = .; - __start___ex_table = .; - __ex_table : { *(__ex_table) } - __stop___ex_table = .; - - . = ALIGN(256); - __init_begin = .; - .text.init : { *(.text.init) } - .data.init : { *(.data.init) } - . = ALIGN(256); - __init_end = .; - - __bss_start = .; - .bss (NOLOAD) : - { - *(.bss*) - *(.sbss*) - *(COMMON) - . = ALIGN(4); - } - __bss_end = . ; - PROVIDE (end = .); -} diff --git a/board/stx/stxxtc/u-boot.lds.debug b/board/stx/stxxtc/u-boot.lds.debug deleted file mode 100644 index a198cf9..0000000 --- a/board/stx/stxxtc/u-boot.lds.debug +++ /dev/null @@ -1,121 +0,0 @@ -/* - * (C) Copyright 2000-2004 - * Wolfgang Denk, DENX Software Engineering, wd@denx.de. - * - * SPDX-License-Identifier: GPL-2.0+ - */ - -OUTPUT_ARCH(powerpc) -/* Do we need any of these for elf? - __DYNAMIC = 0; */ -SECTIONS -{ - /* Read-only sections, merged into text segment: */ - . = + SIZEOF_HEADERS; - .interp : { *(.interp) } - .hash : { *(.hash) } - .dynsym : { *(.dynsym) } - .dynstr : { *(.dynstr) } - .rel.text : { *(.rel.text) } - .rela.text : { *(.rela.text) } - .rel.data : { *(.rel.data) } - .rela.data : { *(.rela.data) } - .rel.rodata : { *(.rel.rodata) } - .rela.rodata : { *(.rela.rodata) } - .rel.got : { *(.rel.got) } - .rela.got : { *(.rela.got) } - .rel.ctors : { *(.rel.ctors) } - .rela.ctors : { *(.rela.ctors) } - .rel.dtors : { *(.rel.dtors) } - .rela.dtors : { *(.rela.dtors) } - .rel.bss : { *(.rel.bss) } - .rela.bss : { *(.rela.bss) } - .rel.plt : { *(.rel.plt) } - .rela.plt : { *(.rela.plt) } - .init : { *(.init) } - .plt : { *(.plt) } - .text : - { - /* WARNING - the following is hand-optimized to fit within */ - /* the sector layout of our flash chips! XXX FIXME XXX */ - - arch/powerpc/cpu/mpc8xx/start.o (.text) - common/dlmalloc.o (.text) - lib/vsprintf.o (.text) - lib/crc32.o (.text) - - . = env_offset; - common/env_embedded.o(.text) - - *(.text) - *(.got1) - } - _etext = .; - PROVIDE (etext = .); - .rodata : - { - *(.rodata) - *(.rodata1) - *(.rodata.str1.4) - *(.eh_frame) - } - .fini : { *(.fini) } =0 - .ctors : { *(.ctors) } - .dtors : { *(.dtors) } - - /* Read-write section, merged into data segment: */ - . = (. + 0x0FFF) & 0xFFFFF000; - _erotext = .; - PROVIDE (erotext = .); - .reloc : - { - *(.got) - _GOT2_TABLE_ = .; - *(.got2) - _FIXUP_TABLE_ = .; - *(.fixup) - } - __got2_entries = (_FIXUP_TABLE_ - _GOT2_TABLE_) >>2; - __fixup_entries = (. - _FIXUP_TABLE_)>>2; - - .data : - { - *(.data) - *(.data1) - *(.sdata) - *(.sdata2) - *(.dynamic) - CONSTRUCTORS - } - _edata = .; - PROVIDE (edata = .); - - - . = ALIGN(4); - .u_boot_list : { - KEEP(*(SORT(.u_boot_list*))); - } - - - __start___ex_table = .; - __ex_table : { *(__ex_table) } - __stop___ex_table = .; - - . = ALIGN(4096); - __init_begin = .; - .text.init : { *(.text.init) } - .data.init : { *(.data.init) } - . = ALIGN(4096); - __init_end = .; - - __bss_start = .; - .bss : - { - *(.sbss) *(.scommon) - *(.dynbss) - *(.bss) - *(COMMON) - } - __bss_end = . ; - PROVIDE (end = .); -} diff --git a/board/sunxi/Kconfig b/board/sunxi/Kconfig index b06b5e0..c61c650 100644 --- a/board/sunxi/Kconfig +++ b/board/sunxi/Kconfig @@ -1,17 +1,5 @@ if TARGET_SUN4I -config SYS_CPU - string - default "armv7" - -config SYS_BOARD - string - default "sunxi" - -config SYS_SOC - string - default "sunxi" - config SYS_CONFIG_NAME string default "sun4i" @@ -20,25 +8,21 @@ endif if TARGET_SUN5I -config SYS_CPU +config SYS_CONFIG_NAME string - default "armv7" + default "sun5i" -config SYS_BOARD - string - default "sunxi" +endif -config SYS_SOC - string - default "sunxi" +if TARGET_SUN7I config SYS_CONFIG_NAME string - default "sun5i" + default "sun7i" endif -if TARGET_SUN7I +if TARGET_SUN4I || TARGET_SUN5I || TARGET_SUN7I config SYS_CPU string @@ -52,8 +36,7 @@ config SYS_SOC string default "sunxi" -config SYS_CONFIG_NAME - string - default "sun7i" +config FTDFILE + string "Default ftdfile env setting for this board" endif diff --git a/board/svm_sc8xx/Kconfig b/board/svm_sc8xx/Kconfig deleted file mode 100644 index 522b1a8..0000000 --- a/board/svm_sc8xx/Kconfig +++ /dev/null @@ -1,11 +0,0 @@ -if TARGET_SVM_SC8XX - -config SYS_BOARD - string - default "svm_sc8xx" - -config SYS_CONFIG_NAME - string - default "svm_sc8xx" - -endif diff --git a/board/svm_sc8xx/MAINTAINERS b/board/svm_sc8xx/MAINTAINERS deleted file mode 100644 index c19bcae..0000000 --- a/board/svm_sc8xx/MAINTAINERS +++ /dev/null @@ -1,6 +0,0 @@ -SVM_SC8XX BOARD -M: John Zhan <zhanz@sinovee.com> -S: Orphan (since 2014-06) -F: board/svm_sc8xx/ -F: include/configs/svm_sc8xx.h -F: configs/svm_sc8xx_defconfig diff --git a/board/svm_sc8xx/Makefile b/board/svm_sc8xx/Makefile deleted file mode 100644 index 4c0b4a3..0000000 --- a/board/svm_sc8xx/Makefile +++ /dev/null @@ -1,8 +0,0 @@ -# -# (C) Copyright 2000-2006 -# Wolfgang Denk, DENX Software Engineering, wd@denx.de. -# -# SPDX-License-Identifier: GPL-2.0+ -# - -obj-y = svm_sc8xx.o flash.o diff --git a/board/svm_sc8xx/flash.c b/board/svm_sc8xx/flash.c deleted file mode 100644 index 8a04de8..0000000 --- a/board/svm_sc8xx/flash.c +++ /dev/null @@ -1,666 +0,0 @@ -/* - * Wolfgang Denk, DENX Software Engineering, wd@denx.de. - * - * SPDX-License-Identifier: GPL-2.0+ - */ - -#include <common.h> -#include <mpc8xx.h> - -#ifndef CONFIG_ENV_ADDR -#define CONFIG_ENV_ADDR (CONFIG_SYS_FLASH_BASE + CONFIG_ENV_OFFSET) -#endif - -flash_info_t flash_info[CONFIG_SYS_MAX_FLASH_BANKS]; - -/*----------------------------------------------------------------------- - * Functions - */ -static int write_word(flash_info_t *info, ulong dest, ulong data); - -#ifdef CONFIG_BOOT_8B -static int my_in_8(unsigned char *addr); -static void my_out_8(unsigned char *addr, int val); -#endif -#ifdef CONFIG_BOOT_16B -static int my_in_be16(unsigned short *addr); -static void my_out_be16(unsigned short *addr, int val); -#endif -#ifdef CONFIG_BOOT_32B -static unsigned my_in_be32(unsigned *addr); -static void my_out_be32(unsigned *addr, int val); -#endif -/*----------------------------------------------------------------------- - */ - -unsigned long flash_init(void) -{ - volatile immap_t *immap = (immap_t *) CONFIG_SYS_IMMR; - volatile memctl8xx_t *memctl = &immap->im_memctl; - unsigned long size_b0, size_b1; - int i; - - size_b0 = 0; - size_b1 = 0; - /* Init: no FLASHes known */ - for (i = 0; i < CONFIG_SYS_MAX_FLASH_BANKS; ++i) - flash_info[i].flash_id = FLASH_UNKNOWN; - -#ifdef CONFIG_SYS_DOC_BASE -#ifndef CONFIG_FEL8xx_AT - /* 32k bytes */ - memctl->memc_or5 = (0xffff8000 | CONFIG_SYS_OR_TIMING_DOC); - memctl->memc_br5 = CONFIG_SYS_DOC_BASE | 0x401; -#else - /* 32k bytes */ - memctl->memc_or3 = (0xffff8000 | CONFIG_SYS_OR_TIMING_DOC); - memctl->memc_br3 = CONFIG_SYS_DOC_BASE | 0x401; -#endif -#endif -#if defined(CONFIG_BOOT_8B) - size_b0 = 0x80000; /* 512 K */ - - flash_info[0].flash_id = FLASH_MAN_AMD | FLASH_AM040; - flash_info[0].sector_count = 8; - flash_info[0].size = 0x00080000; - - /* set up sector start address table */ - for (i = 0; i < flash_info[0].sector_count; i++) - flash_info[0].start[i] = 0x40000000 + (i * 0x10000); - - /* protect all sectors */ - for (i = 0; i < flash_info[0].sector_count; i++) - flash_info[0].protect[i] = 0x1; - -#elif defined(CONFIG_BOOT_16B) - size_b0 = 0x400000; /* 4MB , assume AMD29LV320B */ - - flash_info[0].flash_id = FLASH_MAN_AMD | FLASH_AM320B; - flash_info[0].sector_count = 67; - flash_info[0].size = 0x00400000; - - /* set up sector start address table */ - flash_info[0].start[0] = 0x40000000; - flash_info[0].start[1] = 0x40000000 + 0x4000; - flash_info[0].start[2] = 0x40000000 + 0x6000; - flash_info[0].start[3] = 0x40000000 + 0x8000; - - for (i = 4; i < flash_info[0].sector_count; i++) { - flash_info[0].start[i] = - 0x40000000 + 0x10000 + ((i - 4) * 0x10000); - } - - /* protect all sectors */ - for (i = 0; i < flash_info[0].sector_count; i++) - flash_info[0].protect[i] = 0x1; -#endif - -#ifdef CONFIG_BOOT_32B - - /* Static FLASH Bank configuration here - FIXME XXX */ - size_b0 = flash_get_size((vu_long *) FLASH_BASE0_PRELIM, - &flash_info[0]); - - if (flash_info[0].flash_id == FLASH_UNKNOWN) { - printf("## Unknown FLASH on Bank 0 - Size = 0x%08lx = %ld MB\n", - size_b0, size_b0 << 20); - } - - size_b1 = flash_get_size((vu_long *) FLASH_BASE1_PRELIM, - &flash_info[1]); - - if (size_b1 > size_b0) { - printf("## ERROR: " - "Bank 1 (0x%08lx = %ld MB) > Bank 0 (0x%08lx = %ld MB)\n", - size_b1, size_b1 << 20, size_b0, size_b0 << 20); - flash_info[0].flash_id = FLASH_UNKNOWN; - flash_info[1].flash_id = FLASH_UNKNOWN; - flash_info[0].sector_count = -1; - flash_info[1].sector_count = -1; - flash_info[0].size = 0; - flash_info[1].size = 0; - - return 0; - } - - /* Remap FLASH according to real size */ - memctl->memc_or0 = CONFIG_SYS_OR_TIMING_FLASH | - (-size_b0 & OR_AM_MSK); - memctl->memc_br0 = (CONFIG_SYS_FLASH_BASE & BR_BA_MSK) | - BR_MS_GPCM | BR_V; - - /* Re-do sizing to get full correct info */ - size_b0 = flash_get_size((vu_long *) CONFIG_SYS_FLASH_BASE, - &flash_info[0]); - - flash_get_offsets(CONFIG_SYS_FLASH_BASE, &flash_info[0]); - -#if CONFIG_SYS_MONITOR_BASE >= CONFIG_SYS_FLASH_BASE - /* monitor protection ON by default */ - flash_protect(FLAG_PROTECT_SET, - CONFIG_SYS_MONITOR_BASE, - CONFIG_SYS_MONITOR_BASE + monitor_flash_len - 1, - &flash_info[0]); -#endif - -#ifdef CONFIG_ENV_IS_IN_FLASH - /* ENV protection ON by default */ - flash_protect(FLAG_PROTECT_SET, - CONFIG_ENV_ADDR, - CONFIG_ENV_ADDR + CONFIG_ENV_SIZE - 1, &flash_info[0]); -#endif - - if (size_b1) { - memctl->memc_or1 = CONFIG_SYS_OR_TIMING_FLASH | - (-size_b1 & 0xFFFF8000); - memctl->memc_br1 = ((CONFIG_SYS_FLASH_BASE + - size_b0) & BR_BA_MSK) | BR_MS_GPCM | BR_V; - - /* Re-do sizing to get full correct info */ - size_b1 = flash_get_size((vu_long *)(CONFIG_SYS_FLASH_BASE + - size_b0), &flash_info[1]); - - flash_get_offsets(CONFIG_SYS_FLASH_BASE + size_b0, - &flash_info[1]); - -#if CONFIG_SYS_MONITOR_BASE >= CONFIG_SYS_FLASH_BASE - /* monitor protection ON by default */ - flash_protect(FLAG_PROTECT_SET, - CONFIG_SYS_MONITOR_BASE, - CONFIG_SYS_MONITOR_BASE + monitor_flash_len - 1, - &flash_info[1]); -#endif - -#ifdef CONFIG_ENV_IS_IN_FLASH - /* ENV protection ON by default */ - flash_protect(FLAG_PROTECT_SET, - CONFIG_ENV_ADDR, - CONFIG_ENV_ADDR + CONFIG_ENV_SIZE - 1, - &flash_info[1]); -#endif - } else { - memctl->memc_br1 = 0; /* invalidate bank */ - - flash_info[1].flash_id = FLASH_UNKNOWN; - flash_info[1].sector_count = -1; - } - - flash_info[0].size = size_b0; - flash_info[1].size = size_b1; - - -#endif /* CONFIG_BOOT_32B */ - - return size_b0 + size_b1; -} - - -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_AM400B: - printf("AM29LV400B (4 Mbit, bottom boot sect)\n"); - break; - case FLASH_AM400T: - printf("AM29LV400T (4 Mbit, top boot sector)\n"); - break; - case FLASH_AM800B: - printf("AM29LV800B (8 Mbit, bottom boot sect)\n"); - break; - case FLASH_AM800T: - printf("AM29LV800T (8 Mbit, top boot sector)\n"); - break; - case FLASH_AM160B: - printf("AM29LV160B (16 Mbit, bottom boot sect)\n"); - break; - case FLASH_AM160T: - printf("AM29LV160T (16 Mbit, top boot sector)\n"); - break; - case FLASH_AM320B: - printf("AM29LV320B (32 Mbit, bottom boot sect)\n"); - break; - case FLASH_AM320T: - printf("AM29LV320T (32 Mbit, top boot 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; -} - -/* - * The following code cannot be run from FLASH! - */ - -int flash_erase(flash_info_t *info, int s_first, int s_last) -{ - vu_long *addr = (vu_long *) (info->start[0]); - int flag, prot, sect, l_sect, in_mid, in_did; - 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; - } - - if ((info->flash_id == FLASH_UNKNOWN) || - (info->flash_id > FLASH_AMD_COMP)) { - printf("Can't erase unknown flash type %08lx - aborted\n", - info->flash_id); - 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(); - -#if defined(CONFIG_BOOT_8B) - my_out_8((unsigned char *)((ulong)addr + 0x555), 0xaa); - my_out_8((unsigned char *)((ulong)addr + 0x2aa), 0x55); - my_out_8((unsigned char *)((ulong)addr + 0x555), 0x90); - - in_mid = my_in_8((unsigned char *)addr); - in_did = my_in_8((unsigned char *)((ulong)addr + 1)); - - printf(" man ID=0x%x, dev ID=0x%x.\n", in_mid, in_did); - - my_out_8((unsigned char *)addr, 0xf0); - udelay(1); - - my_out_8((unsigned char *)((ulong)addr + 0x555), 0xaa); - my_out_8((unsigned char *)((ulong)addr + 0x2aa), 0x55); - my_out_8((unsigned char *)((ulong)addr + 0x555), 0x80); - my_out_8((unsigned char *)((ulong)addr + 0x555), 0xaa); - my_out_8((unsigned char *)((ulong)addr + 0x2aa), 0x55); - - /* Start erase on unprotected sectors */ - for (sect = s_first; sect <= s_last; sect++) { - if (info->protect[sect] == 0) { /* not protected */ - addr = (vu_long *) (info->start[sect]); - /*addr[0] = 0x00300030; */ - my_out_8((unsigned char *)((ulong)addr), 0x30); - l_sect = sect; - } - } -#elif defined(CONFIG_BOOT_16B) - my_out_be16((unsigned short *)((ulong)addr + (0xaaa)), 0xaa); - my_out_be16((unsigned short *)((ulong)addr + (0x554)), 0x55); - my_out_be16((unsigned short *)((ulong)addr + (0xaaa)), 0x90); - in_mid = my_in_be16((unsigned short *)addr); - in_did = my_in_be16((unsigned short *)((ulong)addr + 2)); - printf(" man ID=0x%x, dev ID=0x%x.\n", in_mid, in_did); - my_out_be16((unsigned short *)addr, 0xf0); - udelay(1); - my_out_be16((unsigned short *)((ulong)addr + 0xaaa), 0xaa); - my_out_be16((unsigned short *)((ulong)addr + 0x554), 0x55); - my_out_be16((unsigned short *)((ulong)addr + 0xaaa), 0x80); - my_out_be16((unsigned short *)((ulong)addr + 0xaaa), 0xaa); - my_out_be16((unsigned short *)((ulong)addr + 0x554), 0x55); - /* Start erase on unprotected sectors */ - for (sect = s_first; sect <= s_last; sect++) { - if (info->protect[sect] == 0) { /* not protected */ - addr = (vu_long *) (info->start[sect]); - my_out_be16((unsigned short *)((ulong)addr), 0x30); - l_sect = sect; - } - } - -#elif defined(CONFIG_BOOT_32B) - my_out_be32((unsigned *)((ulong)addr + 0x1554), 0xaa); - my_out_be32((unsigned *)((ulong)addr + 0xaa8), 0x55); - my_out_be32((unsigned *)((ulong)addr + 0x1554), 0x90); - - in_mid = my_in_be32((unsigned *)addr); - in_did = my_in_be32((unsigned *)((ulong)addr + 4)); - - printf(" man ID=0x%x, dev ID=0x%x.\n", in_mid, in_did); - - my_out_be32((unsigned *) addr, 0xf0); - udelay(1); - - my_out_be32((unsigned *)((ulong)addr + 0x1554), 0xaa); - my_out_be32((unsigned *)((ulong)addr + 0xaa8), 0x55); - my_out_be32((unsigned *)((ulong)addr + 0x1554), 0x80); - my_out_be32((unsigned *)((ulong)addr + 0x1554), 0xaa); - my_out_be32((unsigned *)((ulong)addr + 0xaa8), 0x55); - - /* Start erase on unprotected sectors */ - for (sect = s_first; sect <= s_last; sect++) { - if (info->protect[sect] == 0) { /* not protected */ - addr = (vu_long *) (info->start[sect]); - my_out_be32((unsigned *)((ulong)addr), 0x00300030); - l_sect = sect; - } - } - -#else -#error CONFIG_BOOT_(size)B missing. -#endif - /* 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; - addr = (vu_long *) (info->start[l_sect]); -#if defined(CONFIG_BOOT_8B) - while ((my_in_8((unsigned char *) addr) & 0x80) != 0x80) -#elif defined(CONFIG_BOOT_16B) - while ((my_in_be16((unsigned short *) addr) & 0x0080) != 0x0080) -#elif defined(CONFIG_BOOT_32B) - while ((my_in_be32((unsigned *) addr) & 0x00800080) != 0x00800080) -#else -#error CONFIG_BOOT_(size)B missing. -#endif - { - now = get_timer(start); - if (now > CONFIG_SYS_FLASH_ERASE_TOUT) { - printf("Timeout\n"); - return 1; - } - /* show that we're waiting */ - if ((now - last) > 1000) { /* every second */ - putc('.'); - last = now; - } - } -DONE: - /* reset to read mode */ - addr = (volatile unsigned long *) info->start[0]; - -#if defined(CONFIG_BOOT_8B) - my_out_8((unsigned char *) addr, 0xf0); -#elif defined(CONFIG_BOOT_16B) - my_out_be16((unsigned short *) addr, 0x00f0); -#elif defined(CONFIG_BOOT_32B) - my_out_be32((unsigned *) addr, 0x00F000F0); /* reset bank */ -#else -#error CONFIG_BOOT_(size)B missing. -#endif - printf(" done\n"); - return 0; -} - -/* - * 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 cp, wp, data; - int i, l, rc; - - wp = (addr & ~3); /* get lower word aligned address */ - - /* - * handle unaligned start bytes - */ - l = addr - wp; - - if (l != 0) { - data = 0; - for (i = 0, cp = wp; i < l; ++i, ++cp) - data = (data << 8) | (*(uchar *) cp); - - for (; i < 4 && cnt > 0; ++i) { - data = (data << 8) | *src++; - --cnt; - ++cp; - } - for (; cnt == 0 && i < 4; ++i, ++cp) - data = (data << 8) | (*(uchar *) cp); - - rc = write_word(info, wp, data); - - if (rc != 0) - return rc; - - wp += 4; - } - - /* - * handle word aligned part - */ - while (cnt >= 4) { - data = 0; - for (i = 0; i < 4; ++i) - data = (data << 8) | *src++; - - rc = write_word(info, wp, data); - - if (rc != 0) - return rc; - - wp += 4; - cnt -= 4; - } - - if (cnt == 0) - return 0; - - /* - * handle unaligned tail bytes - */ - data = 0; - for (i = 0, cp = wp; i < 4 && cnt > 0; ++i, ++cp) { - data = (data << 8) | *src++; - --cnt; - } - for (; i < 4; ++i, ++cp) - data = (data << 8) | (*(uchar *) cp); - - return write_word(info, wp, data); -} - -/* - * Write a word to Flash, returns: - * 0 - OK - * 1 - write timeout - * 2 - Flash not erased - */ -static int write_word(flash_info_t *info, ulong dest, ulong data) -{ - ulong addr = (ulong) (info->start[0]); - ulong start; - int flag; - ulong i; - int data_short[2]; - - /* Check if Flash is (sufficiently) erased */ - if (((ulong)*(ulong *)dest & data) != data) - return 2; - - /* Disable interrupts which might cause a timeout here */ - flag = disable_interrupts(); -#if defined(CONFIG_BOOT_8B) -#ifdef DEBUG - { - int in_mid, in_did; - - my_out_8((unsigned char *) (addr + 0x555), 0xaa); - my_out_8((unsigned char *) (addr + 0x2aa), 0x55); - my_out_8((unsigned char *) (addr + 0x555), 0x90); - - in_mid = my_in_8((unsigned char *) addr); - in_did = my_in_8((unsigned char *) (addr + 1)); - - printf(" man ID=0x%x, dev ID=0x%x.\n", in_mid, in_did); - - my_out_8((unsigned char *) addr, 0xf0); - udelay(1); - } -#endif - { - int data_ch[4]; - - data_ch[0] = (int) ((data >> 24) & 0xff); - data_ch[1] = (int) ((data >> 16) & 0xff); - data_ch[2] = (int) ((data >> 8) & 0xff); - data_ch[3] = (int) (data & 0xff); - - for (i = 0; i < 4; i++) { - my_out_8((unsigned char *) (addr + 0x555), 0xaa); - my_out_8((unsigned char *) (addr + 0x2aa), 0x55); - my_out_8((unsigned char *) (addr + 0x555), 0xa0); - my_out_8((unsigned char *) (dest + i), data_ch[i]); - - /* re-enable interrupts if necessary */ - if (flag) - enable_interrupts(); - - start = get_timer(0); - while ((my_in_8((unsigned char *)(dest + i))) != - (data_ch[i])) { - if (get_timer(start) > - CONFIG_SYS_FLASH_WRITE_TOUT) { - return 1; - } - } - } /* for */ - } -#elif defined(CONFIG_BOOT_16B) - data_short[0] = (int) (data >> 16) & 0xffff; - data_short[1] = (int) data & 0xffff; - for (i = 0; i < 2; i++) { - my_out_be16((unsigned short *)((ulong)addr + 0xaaa), 0xaa); - my_out_be16((unsigned short *)((ulong)addr + 0x554), 0x55); - my_out_be16((unsigned short *)((ulong)addr + 0xaaa), 0xa0); - my_out_be16((unsigned short *)(dest + (i * 2)), - data_short[i]); - - /* re-enable interrupts if necessary */ - if (flag) - enable_interrupts(); - - start = get_timer(0); - while ((my_in_be16((unsigned short *)(dest + (i * 2)))) != - (data_short[i])) { - if (get_timer(start) > CONFIG_SYS_FLASH_WRITE_TOUT) - return 1; - } - } -#elif defined(CONFIG_BOOT_32B) - addr[0x0555] = 0x00AA00AA; - addr[0x02AA] = 0x00550055; - addr[0x0555] = 0x00A000A0; - - *((vu_long *)dest) = data; - - /* re-enable interrupts if necessary */ - if (flag) - enable_interrupts(); - - /* data polling for D7 */ - start = get_timer(0); - while ((*((vu_long *)dest) & 0x00800080) != (data & 0x00800080)) { - if (get_timer(start) > CONFIG_SYS_FLASH_WRITE_TOUT) - return 1; - } -#endif - return 0; -} - -#ifdef CONFIG_BOOT_8B -static int my_in_8(unsigned char *addr) -{ - int ret; - __asm__ __volatile__("lbz%U1%X1 %0,%1; eieio":"=r"(ret):"m"(*addr)); - - return ret; -} - -static void my_out_8(unsigned char *addr, int val) -{ - __asm__ __volatile__("stb%U0%X0 %1,%0; eieio":"=m"(*addr):"r"(val)); -} -#endif -#ifdef CONFIG_BOOT_16B -static int my_in_be16(unsigned short *addr) -{ - int ret; - __asm__ __volatile__("lhz%U1%X1 %0,%1; eieio":"=r"(ret):"m"(*addr)); - - return ret; -} - -static void my_out_be16(unsigned short *addr, int val) -{ - __asm__ __volatile__("sth%U0%X0 %1,%0; eieio":"=m"(*addr):"r"(val)); -} -#endif -#ifdef CONFIG_BOOT_32B -static unsigned my_in_be32(unsigned *addr) -{ - unsigned ret; - __asm__ __volatile__("lwz%U1%X1 %0,%1; eieio":"=r"(ret):"m"(*addr)); - - return ret; -} - -static void my_out_be32(unsigned *addr, int val) -{ - __asm__ __volatile__("stw%U0%X0 %1,%0; eieio":"=m"(*addr):"r"(val)); -} -#endif diff --git a/board/svm_sc8xx/svm_sc8xx.c b/board/svm_sc8xx/svm_sc8xx.c deleted file mode 100644 index 5db4850..0000000 --- a/board/svm_sc8xx/svm_sc8xx.c +++ /dev/null @@ -1,144 +0,0 @@ -/* - * (C) Copyright 2000, 2001, 2002 - * Wolfgang Denk, DENX Software Engineering, wd@denx.de. - * - * SPDX-License-Identifier: GPL-2.0+ - */ - -#include <common.h> -#include <mpc8xx.h> - -/* ------------------------------------------------------------------------- */ -const uint sdram_table[] = -{ -/*----------------- - UPM A contents: ------------------ */ -/*--------------------------------------------------- - Read Single Beat Cycle. Offset 0 in the RAM array. ----------------------------------------------------- */ -0x1f07fc04, 0xeeaefc04, 0x11adfc04, 0xefbbbc00 , -0x1ff77c47, 0x1ff77c35, 0xefeabc34, 0x1fb57c35 , -/*------------------------------------------------ - Read Burst Cycle. Offset 0x8 in the RAM array. ------------------------------------------------- */ -0x1f07fc04, 0xeeaefc04, 0x10adfc04, 0xf0affc00, -0xf0affc00, 0xf1affc00, 0xefbbbc00, 0x1ff77c47, -0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, -0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, -/*------------------------------------------------------- - Write Single Beat Cycle. Offset 0x18 in the RAM array -------------------------------------------------------- */ -0x1f27fc04, 0xeeaebc00, 0x01b93c04, 0x1ff77c47 , -0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff , -/*------------------------------------------------- - Write Burst Cycle. Offset 0x20 in the RAM array -------------------------------------------------- */ -0x1f07fc04, 0xeeaebc00, 0x10ad7c00, 0xf0affc00, -0xf0affc00, 0xe1bbbc04, 0x1ff77c47, 0xffffffff, -0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff , -0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff , -/*------------------------------------------------------------------------ - Periodic Timer Expired. For DRAM refresh. Offset 0x30 in the RAM array ------------------------------------------------------------------------- */ -0x1ff5fc84, 0xfffffc04, 0xfffffc04, 0xfffffc04, -0xfffffc84, 0xfffffc07, 0xffffffff, 0xffffffff, -0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff , -/*----------- -* Exception: -* ----------- */ -0x7ffefc07, 0xffffffff, 0xffffffff, 0xffffffff , -}; - -/* ------------------------------------------------------------------------- */ -/* - * Check Board Identity: - * - * Test ID string (SVM8...) - * - * Return 1 for "SC8xx" type, 0 else. - */ - -int checkboard(void) -{ - char buf[64]; - int i; - int l = getenv_f("serial#", buf, sizeof(buf)); - - if (l < 0 || strncmp(buf, "SVM8", 4)) { - printf("### No HW ID - assuming SVM SC8xx\n"); - return (0); - } - - for (i = 0; i < l; ++i) { - if (buf[i] == ' ') - break; - putc(buf[i]); - } - - putc('\n'); - - return 0; -} - -/* ------------------------------------------------------------------------- */ - -phys_size_t initdram (int board_type) -{ - volatile immap_t *immap = (immap_t *)CONFIG_SYS_IMMR; - volatile memctl8xx_t *memctl = &immap->im_memctl; - long int size_b0 = 0; - - upmconfig(UPMA, (uint *)sdram_table, sizeof(sdram_table)/sizeof(uint)); - - memctl->memc_mptpr = CONFIG_SYS_MPTPR; -#if defined (CONFIG_SDRAM_16M) - memctl->memc_mamr = 0x00802114 | CONFIG_SYS_MxMR_PTx; - memctl->memc_mcr = 0x80002105; /* SDRAM bank 0 */ - udelay(1); - memctl->memc_mcr = 0x80002830; - udelay(1); - memctl->memc_mar = 0x00000088; - udelay(1); - memctl->memc_mcr = 0x80002106; - udelay(1); - memctl->memc_or1 = 0xff000a00; - size_b0 = 0x01000000; -#elif defined (CONFIG_SDRAM_32M) - memctl->memc_mamr = 0x00904114 | CONFIG_SYS_MxMR_PTx; - memctl->memc_mcr = 0x80002105; /* SDRAM bank 0 */ - udelay(1); - memctl->memc_mcr = 0x80002830; - udelay(1); - memctl->memc_mar = 0x00000088; - udelay(1); - memctl->memc_mcr = 0x80002106; - udelay(1); - memctl->memc_or1 = 0xfe000a00; - size_b0 = 0x02000000; -#elif defined (CONFIG_SDRAM_64M) - memctl->memc_mamr = 0x00a04114 | CONFIG_SYS_MxMR_PTx; - memctl->memc_mcr = 0x80002105; /* SDRAM bank 0 */ - udelay(1); - memctl->memc_mcr = 0x80002830; - udelay(1); - memctl->memc_mar = 0x00000088; - udelay(1); - memctl->memc_mcr = 0x80002106; - udelay(1); - memctl->memc_or1 = 0xfc000a00; - size_b0 = 0x04000000; -#else -#error SDRAM size configuration missing. -#endif - memctl->memc_br1 = 0x00000081; - udelay(200); - return (size_b0 ); -} - -#if defined(CONFIG_CMD_DOC) -void doc_init (void) -{ - doc_probe (CONFIG_SYS_DOC_BASE); -} -#endif diff --git a/board/svm_sc8xx/u-boot.lds b/board/svm_sc8xx/u-boot.lds deleted file mode 100644 index df564e9..0000000 --- a/board/svm_sc8xx/u-boot.lds +++ /dev/null @@ -1,99 +0,0 @@ -/* - * (C) Copyright 2000-2010 - * Wolfgang Denk, DENX Software Engineering, wd@denx.de. - * - * SPDX-License-Identifier: GPL-2.0+ - */ - -OUTPUT_ARCH(powerpc) - -SECTIONS -{ - /* Read-only sections, merged into text segment: */ - . = + SIZEOF_HEADERS; - .text : - { - /* WARNING - the following is hand-optimized to fit within */ - /* the sector layout of our flash chips! XXX FIXME XXX */ - arch/powerpc/cpu/mpc8xx/start.o (.text*) - arch/powerpc/cpu/mpc8xx/traps.o (.text*) - lib/built-in.o (.text*) - net/built-in.o (.text*) - arch/powerpc/cpu/mpc8xx/built-in.o (.text*) - arch/powerpc/lib/built-in.o (.text*) - board/svm_sc8xx/built-in.o (.text*) - *(.text.*printf) - *(.text.do_mem_*) - *(.text.flash*) - *(.text.run_command) - *(.text.main_loop) - *(.text.srec_decode) - - . = env_offset; - common/env_embedded.o (.ppcenv*) - - *(.text*) - } - _etext = .; - PROVIDE (etext = .); - .rodata : - { - *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) - } - - /* Read-write section, merged into data segment: */ - . = (. + 0x00FF) & 0xFFFFFF00; - _erotext = .; - PROVIDE (erotext = .); - .reloc : - { - _GOT2_TABLE_ = .; - KEEP(*(.got2)) - KEEP(*(.got)) - PROVIDE(_GLOBAL_OFFSET_TABLE_ = . + 4); - _FIXUP_TABLE_ = .; - KEEP(*(.fixup)) - } - __got2_entries = ((_GLOBAL_OFFSET_TABLE_ - _GOT2_TABLE_) >> 2) - 1; - __fixup_entries = (. - _FIXUP_TABLE_)>>2; - - .data : - { - *(.data*) - *(.sdata*) - } - _edata = .; - PROVIDE (edata = .); - - - . = .; - - . = ALIGN(4); - .u_boot_list : { - KEEP(*(SORT(.u_boot_list*))); - } - - - . = .; - __start___ex_table = .; - __ex_table : { *(__ex_table) } - __stop___ex_table = .; - - . = ALIGN(256); - __init_begin = .; - .text.init : { *(.text.init) } - .data.init : { *(.data.init) } - . = ALIGN(256); - __init_end = .; - - __bss_start = .; - .bss (NOLOAD) : - { - *(.bss*) - *(.sbss*) - *(COMMON) - . = ALIGN(4); - } - __bss_end = . ; - PROVIDE (end = .); -} diff --git a/board/svm_sc8xx/u-boot.lds.debug b/board/svm_sc8xx/u-boot.lds.debug deleted file mode 100644 index b2c562c..0000000 --- a/board/svm_sc8xx/u-boot.lds.debug +++ /dev/null @@ -1,114 +0,0 @@ -/* - * (C) Copyright 2000 - * Wolfgang Denk, DENX Software Engineering, wd@denx.de. - * - * SPDX-License-Identifier: GPL-2.0+ - */ - -OUTPUT_ARCH(powerpc) -/* Do we need any of these for elf? - __DYNAMIC = 0; */ -SECTIONS -{ - /* Read-only sections, merged into text segment: */ - . = + SIZEOF_HEADERS; - .interp : { *(.interp) } - .hash : { *(.hash) } - .dynsym : { *(.dynsym) } - .dynstr : { *(.dynstr) } - .rel.text : { *(.rel.text) } - .rela.text : { *(.rela.text) } - .rel.data : { *(.rel.data) } - .rela.data : { *(.rela.data) } - .rel.rodata : { *(.rel.rodata) } - .rela.rodata : { *(.rela.rodata) } - .rel.got : { *(.rel.got) } - .rela.got : { *(.rela.got) } - .rel.ctors : { *(.rel.ctors) } - .rela.ctors : { *(.rela.ctors) } - .rel.dtors : { *(.rel.dtors) } - .rela.dtors : { *(.rela.dtors) } - .rel.bss : { *(.rel.bss) } - .rela.bss : { *(.rela.bss) } - .rel.plt : { *(.rel.plt) } - .rela.plt : { *(.rela.plt) } - .init : { *(.init) } - .plt : { *(.plt) } - .text : - { - /* WARNING - the following is hand-optimized to fit within */ - /* the sector layout of our flash chips! XXX FIXME XXX */ - - arch/powerpc/cpu/mpc8xx/start.o (.text) - common/dlmalloc.o (.text) - lib/vsprintf.o (.text) - lib/crc32.o (.text) - - . = env_offset; - common/env_embedded.o(.text) - - *(.text) - *(.got1) - } - _etext = .; - PROVIDE (etext = .); - .rodata : - { - *(.rodata) - *(.rodata1) - *(.rodata.str1.4) - *(.eh_frame) - } - .fini : { *(.fini) } =0 - .ctors : { *(.ctors) } - .dtors : { *(.dtors) } - - /* Read-write section, merged into data segment: */ - . = (. + 0x0FFF) & 0xFFFFF000; - _erotext = .; - PROVIDE (erotext = .); - .reloc : - { - *(.got) - _GOT2_TABLE_ = .; - *(.got2) - _FIXUP_TABLE_ = .; - *(.fixup) - } - __got2_entries = (_FIXUP_TABLE_ - _GOT2_TABLE_) >>2; - __fixup_entries = (. - _FIXUP_TABLE_)>>2; - - .data : - { - *(.data) - *(.data1) - *(.sdata) - *(.sdata2) - *(.dynamic) - CONSTRUCTORS - } - _edata = .; - PROVIDE (edata = .); - - __start___ex_table = .; - __ex_table : { *(__ex_table) } - __stop___ex_table = .; - - . = ALIGN(4096); - __init_begin = .; - .text.init : { *(.text.init) } - .data.init : { *(.data.init) } - . = ALIGN(4096); - __init_end = .; - - __bss_start = .; - .bss : - { - *(.sbss) *(.scommon) - *(.dynbss) - *(.bss) - *(COMMON) - } - __bss_end = . ; - PROVIDE (end = .); -} diff --git a/board/ti/omap5912osk/MAINTAINERS b/board/ti/omap5912osk/MAINTAINERS deleted file mode 100644 index 43ffb9b..0000000 --- a/board/ti/omap5912osk/MAINTAINERS +++ /dev/null @@ -1,6 +0,0 @@ -OMAP5912OSK BOARD -M: Rishi Bhattacharya <rishi@ti.com> -S: Maintained -F: board/ti/omap5912osk/ -F: include/configs/omap5912osk.h -F: configs/omap5912osk_defconfig diff --git a/board/ti/omap5912osk/Makefile b/board/ti/omap5912osk/Makefile deleted file mode 100644 index d7c0ebd..0000000 --- a/board/ti/omap5912osk/Makefile +++ /dev/null @@ -1,9 +0,0 @@ -# -# (C) Copyright 2000-2006 -# Wolfgang Denk, DENX Software Engineering, wd@denx.de. -# -# SPDX-License-Identifier: GPL-2.0+ -# - -obj-y := omap5912osk.o -obj-y += lowlevel_init.o diff --git a/board/ti/omap5912osk/config.mk b/board/ti/omap5912osk/config.mk deleted file mode 100644 index 5b8d952..0000000 --- a/board/ti/omap5912osk/config.mk +++ /dev/null @@ -1,30 +0,0 @@ -# -# (C) Copyright 2002-2004 -# Gary Jennejohn, DENX Software Engineering, <garyj@denx.de> -# David Mueller, ELSOFT AG, <d.mueller@elsoft.ch> -# -# (C) Copyright 2003 -# Texas Instruments, <www.ti.com> -# Kshitij Gupta <Kshitij@ti.com> -# -# (C) Copyright 2004 -# Texas Instruments, <www.ti.com> -# Rishi Bhattacharya <rishi@ti.com> -# -# TI OSK board with OMAP5912 (ARM925EJS) cpu -# see http://www.ti.com/ for more information on Texas Instruments -# -# OSK has 1 bank of 32 MB SDRAM -# Physical Address: -# 1000'0000 to 1200'0000 -# -# -# Linux-Kernel is expected to be at 1000'8000, entry 1000'8000 -# (mem base + reserved) -# -# When running from RAM use address 1108'0000, otherwise when -# booting from NOR flash link to address 0000'0000. -# - -CONFIG_SYS_TEXT_BASE = 0x00000000 -#CONFIG_SYS_TEXT_BASE = 0x11080000 diff --git a/board/ti/omap5912osk/lowlevel_init.S b/board/ti/omap5912osk/lowlevel_init.S deleted file mode 100644 index e05a1c7..0000000 --- a/board/ti/omap5912osk/lowlevel_init.S +++ /dev/null @@ -1,477 +0,0 @@ -/* - * Board specific setup info - * - * (C) Copyright 2003 - * Texas Instruments, <www.ti.com> - * Kshitij Gupta <Kshitij@ti.com> - * - * Modified for OMAP 1610 H2 board by Nishant Kamat, Jan 2004 - * - * Modified for OMAP 5912 OSK board by Rishi Bhattacharya, Apr 2004 - * SPDX-License-Identifier: GPL-2.0+ - */ - -#include <config.h> -#include <version.h> - -#if defined(CONFIG_OMAP1610) -#include <./configs/omap1510.h> -#endif - -.globl lowlevel_init -lowlevel_init: - - /*------------------------------------------------------* - * Ensure i-cache is enabled * - * To configure TC regs without fetching instruction * - *------------------------------------------------------*/ - mrc p15, 0, r0, c1, c0 - orr r0, r0, #0x1000 - mcr p15, 0, r0, c1, c0 - - /*------------------------------------------------------* - *mask all IRQs by setting all bits in the INTMR default* - *------------------------------------------------------*/ - mov r1, #0xffffffff - ldr r0, =REG_IHL1_MIR - str r1, [r0] - ldr r0, =REG_IHL2_MIR - str r1, [r0] - - /*------------------------------------------------------* - * Set up ARM CLM registers (IDLECT1) * - *------------------------------------------------------*/ - ldr r0, REG_ARM_IDLECT1 - ldr r1, VAL_ARM_IDLECT1 - str r1, [r0] - - /*------------------------------------------------------* - * Set up ARM CLM registers (IDLECT2) * - *------------------------------------------------------*/ - ldr r0, REG_ARM_IDLECT2 - ldr r1, VAL_ARM_IDLECT2 - str r1, [r0] - - /*------------------------------------------------------* - * Set up ARM CLM registers (IDLECT3) * - *------------------------------------------------------*/ - ldr r0, REG_ARM_IDLECT3 - ldr r1, VAL_ARM_IDLECT3 - str r1, [r0] - - mov r1, #0x01 /* PER_EN bit */ - ldr r0, REG_ARM_RSTCT2 - strh r1, [r0] /* CLKM; Peripheral reset. */ - - /* Set CLKM to Sync-Scalable */ - mov r1, #0x1000 - ldr r0, REG_ARM_SYSST - - mov r2, #0 -1: cmp r2, #1 - streqh r1, [r0] - add r2, r2, #1 - cmp r2, #0x100 /* wait for any bubbles to finish */ - bne 1b - - ldr r1, VAL_ARM_CKCTL - ldr r0, REG_ARM_CKCTL - strh r1, [r0] - - /* a few nops to let settle */ - nop - nop - nop - nop - nop - nop - nop - nop - nop - nop - - /* setup DPLL 1 */ - /* Ramp up the clock to 96Mhz */ - ldr r1, VAL_DPLL1_CTL - ldr r0, REG_DPLL1_CTL - strh r1, [r0] - ands r1, r1, #0x10 /* Check if PLL is enabled. */ - beq lock_end /* Do not look for lock if BYPASS selected */ -2: - ldrh r1, [r0] - ands r1, r1, #0x01 /* Check the LOCK bit.*/ - beq 2b /* loop until bit goes hi. */ -lock_end: - - /*------------------------------------------------------* - * Turn off the watchdog during init... * - *------------------------------------------------------*/ - ldr r0, REG_WATCHDOG - ldr r1, WATCHDOG_VAL1 - str r1, [r0] - ldr r1, WATCHDOG_VAL2 - str r1, [r0] - ldr r0, REG_WSPRDOG - ldr r1, WSPRDOG_VAL1 - str r1, [r0] - ldr r0, REG_WWPSDOG - -watch1Wait: - ldr r1, [r0] - tst r1, #0x10 - bne watch1Wait - - ldr r0, REG_WSPRDOG - ldr r1, WSPRDOG_VAL2 - str r1, [r0] - ldr r0, REG_WWPSDOG -watch2Wait: - ldr r1, [r0] - tst r1, #0x10 - bne watch2Wait - - /* Set memory timings corresponding to the new clock speed */ - ldr r3, VAL_SDRAM_CONFIG_SDF0 - - /* Check execution location to determine current execution location - * and branch to appropriate initialization code. - */ - mov r0, #0x10000000 /* Load physical SDRAM base. */ - mov r1, pc /* Get current execution location. */ - cmp r1, r0 /* Compare. */ - bge skip_sdram /* Skip over EMIF-fast initialization if running from SDRAM. */ - - /* identify the device revision, -- TMX or TMP(TMS) */ - ldr r0, REG_DEVICE_ID - ldr r1, [r0] - - ldr r0, VAL_DEVICE_ID_TMP - mov r1, r1, lsl #15 - mov r1, r1, lsr #16 - cmp r0, r1 - bne skip_TMP_Patch - - /* Enable TMP/TMS device new features */ - mov r0, #1 - ldr r1, REG_TC_EMIFF_DOUBLER - str r0, [r1] - - /* Enable new ac parameters */ - mov r0, #0x0b - ldr r1, REG_SDRAM_CONFIG2 - str r0, [r1] - - ldr r3, VAL_SDRAM_CONFIG_SDF1 - -skip_TMP_Patch: - - /* - * Delay for SDRAM initialization. - */ - mov r0, #0x1800 /* value should be checked */ -3: - subs r0, r0, #0x1 /* Decrement count */ - bne 3b - - /* - * Set SDRAM control values. Disable refresh before MRS command. - */ - - /* mobile ddr operation */ - ldr r0, REG_SDRAM_OPERATION - mov r2, #07 - str r2, [r0] - - /* config register */ - ldr r0, REG_SDRAM_CONFIG - str r3, [r0] - - /* manual command register */ - ldr r0, REG_SDRAM_MANUAL_CMD - - /* issue set cke high */ - mov r1, #CMD_SDRAM_CKE_SET_HIGH - str r1, [r0] - - /* issue nop */ - mov r1, #CMD_SDRAM_NOP - str r1, [r0] - - mov r2, #0x0100 -waitMDDR1: - subs r2, r2, #1 - bne waitMDDR1 /* delay loop */ - - /* issue precharge */ - mov r1, #CMD_SDRAM_PRECHARGE - str r1, [r0] - - /* issue autorefresh x 2 */ - mov r1, #CMD_SDRAM_AUTOREFRESH - str r1, [r0] - str r1, [r0] - - /* mrs register ddr mobile */ - ldr r0, REG_SDRAM_MRS - mov r1, #0x33 - str r1, [r0] - - /* emrs1 low-power register */ - ldr r0, REG_SDRAM_EMRS1 - /* self refresh on all banks */ - mov r1, #0 - str r1, [r0] - - ldr r0, REG_DLL_URD_CONTROL - ldr r1, DLL_URD_CONTROL_VAL - str r1, [r0] - - ldr r0, REG_DLL_LRD_CONTROL - ldr r1, DLL_LRD_CONTROL_VAL - str r1, [r0] - - ldr r0, REG_DLL_WRT_CONTROL - ldr r1, DLL_WRT_CONTROL_VAL - str r1, [r0] - - /* delay loop */ - mov r0, #0x0100 -waitMDDR2: - subs r0, r0, #1 - bne waitMDDR2 - - /* - * Delay for SDRAM initialization. - */ - mov r0, #0x1800 -4: - subs r0, r0, #1 /* Decrement count. */ - bne 4b - b common_tc - -skip_sdram: - ldr r0, REG_SDRAM_CONFIG - str r3, [r0] - -common_tc: - /* slow interface */ - ldr r1, VAL_TC_EMIFS_CS0_CONFIG - ldr r0, REG_TC_EMIFS_CS0_CONFIG - str r1, [r0] /* Chip Select 0 */ - - ldr r1, VAL_TC_EMIFS_CS1_CONFIG - ldr r0, REG_TC_EMIFS_CS1_CONFIG - str r1, [r0] /* Chip Select 1 */ - - ldr r1, VAL_TC_EMIFS_CS3_CONFIG - ldr r0, REG_TC_EMIFS_CS3_CONFIG - str r1, [r0] /* Chip Select 3 */ - - ldr r1, VAL_TC_EMIFS_DWS - ldr r0, REG_TC_EMIFS_DWS - str r1, [r0] /* Enable EMIFS.RDY for CS1 (ether) */ - -#ifdef CONFIG_H2_OMAP1610 - /* inserting additional 2 clock cycle hold time for LAN */ - ldr r0, REG_TC_EMIFS_CS1_ADVANCED - ldr r1, VAL_TC_EMIFS_CS1_ADVANCED - str r1, [r0] -#endif - /* Start MPU Timer 1 */ - ldr r0, REG_MPU_LOAD_TIMER - ldr r1, VAL_MPU_LOAD_TIMER - str r1, [r0] - - ldr r0, REG_MPU_CNTL_TIMER - ldr r1, VAL_MPU_CNTL_TIMER - str r1, [r0] - - /* - * Setup a temporary stack - */ - ldr sp, SRAM_STACK - bic sp, sp, #7 /* 8-byte alignment for ABI compliance */ - - /* - * Save the old lr(passed in ip) and the current lr to stack - */ - push {ip, lr} - - /* - * go setup pll, mux, memory - */ - bl s_init - pop {ip, pc} - - /* back to arch calling code */ - mov pc, lr - - /* the literal pools origin */ - .ltorg - -REG_DEVICE_ID: /* 32 bits */ - .word 0xfffe2004 -REG_TC_EMIFS_CONFIG: - .word 0xfffecc0c -REG_TC_EMIFS_CS0_CONFIG: /* 32 bits */ - .word 0xfffecc10 -REG_TC_EMIFS_CS1_CONFIG: /* 32 bits */ - .word 0xfffecc14 -REG_TC_EMIFS_CS2_CONFIG: /* 32 bits */ - .word 0xfffecc18 -REG_TC_EMIFS_CS3_CONFIG: /* 32 bits */ - .word 0xfffecc1c -REG_TC_EMIFS_DWS: /* 32 bits */ - .word 0xfffecc40 -#ifdef CONFIG_H2_OMAP1610 -REG_TC_EMIFS_CS1_ADVANCED: /* 32 bits */ - .word 0xfffecc54 -#endif - -/* MPU clock/reset/power mode control registers */ -REG_ARM_CKCTL: /* 16 bits */ - .word 0xfffece00 -REG_ARM_IDLECT3: /* 16 bits */ - .word 0xfffece24 -REG_ARM_IDLECT2: /* 16 bits */ - .word 0xfffece08 -REG_ARM_IDLECT1: /* 16 bits */ - .word 0xfffece04 -REG_ARM_RSTCT2: /* 16 bits */ - .word 0xfffece14 -REG_ARM_SYSST: /* 16 bits */ - .word 0xfffece18 - -/* DPLL control registers */ -REG_DPLL1_CTL: /* 16 bits */ - .word 0xfffecf00 - -/* Watch Dog register */ -/* secure watchdog stop */ -REG_WSPRDOG: - .word 0xfffeb048 -/* watchdog write pending */ -REG_WWPSDOG: - .word 0xfffeb034 - -WSPRDOG_VAL1: - .word 0x0000aaaa -WSPRDOG_VAL2: - .word 0x00005555 - -/* SDRAM config is: auto refresh enabled, 16 bit 4 bank, - counter @8192 rows, 10 ns, 8 burst */ -REG_SDRAM_CONFIG: - .word 0xfffecc20 -REG_SDRAM_CONFIG2: - .word 0xfffecc3c -REG_TC_EMIFF_DOUBLER: /* 32 bits */ - .word 0xfffecc60 - -/* Operation register */ -REG_SDRAM_OPERATION: - .word 0xfffecc80 - -/* Manual command register */ -REG_SDRAM_MANUAL_CMD: - .word 0xfffecc84 - -/* SDRAM MRS (New) config is: CAS latency is 2, burst length 8 */ -REG_SDRAM_MRS: - .word 0xfffecc70 - -/* SDRAM MRS (New) config is: CAS latency is 2, burst length 8 */ -REG_SDRAM_EMRS1: - .word 0xfffecc78 - -/* WRT DLL register */ -REG_DLL_WRT_CONTROL: - .word 0xfffecc68 -DLL_WRT_CONTROL_VAL: - .word 0x03f00002 /* Phase of 72deg, write offset +31 */ - -/* URD DLL register */ -REG_DLL_URD_CONTROL: - .word 0xfffeccc0 -DLL_URD_CONTROL_VAL: - .word 0x00800002 /* Phase of 72deg, read offset +31 */ - -/* LRD DLL register */ -REG_DLL_LRD_CONTROL: - .word 0xfffecccc -DLL_LRD_CONTROL_VAL: - .word 0x00800002 /* read offset +31 */ - -REG_WATCHDOG: - .word 0xfffec808 -WATCHDOG_VAL1: - .word 0x000000f5 -WATCHDOG_VAL2: - .word 0x000000a0 - -REG_MPU_LOAD_TIMER: - .word 0xfffec504 -REG_MPU_CNTL_TIMER: - .word 0xfffec500 -VAL_MPU_LOAD_TIMER: - .word 0xffffffff -VAL_MPU_CNTL_TIMER: - .word 0xffffffa1 - -/* 96 MHz Samsung Mobile DDR */ -/* Original setting for TMX device */ -VAL_SDRAM_CONFIG_SDF0: - .word 0x0014e6fe - -/* NEW_SYS_FREQ mode (valid only TMP/TMS devices) */ -VAL_SDRAM_CONFIG_SDF1: - .word 0x0114e6fe - -VAL_ARM_CKCTL: - .word 0x2000 /* was: 0x3000, now use CLK_REF for timer input */ -VAL_DPLL1_CTL: - .word 0x2830 - -#ifdef CONFIG_OSK_OMAP5912 -VAL_TC_EMIFS_CS0_CONFIG: - .word 0x002130b0 -VAL_TC_EMIFS_CS1_CONFIG: - .word 0x00001133 -VAL_TC_EMIFS_CS2_CONFIG: - .word 0x000055f0 -VAL_TC_EMIFS_CS3_CONFIG: - .word 0x88013141 -VAL_TC_EMIFS_DWS: /* Enable EMIFS.RDY for CS1 access (ether) */ - .word 0x000000c0 -VAL_DEVICE_ID_TMP: /* TMP/TMS=0xb65f, TMX=0xb58c */ - .word 0xb65f -#endif - -#ifdef CONFIG_H2_OMAP1610 -VAL_TC_EMIFS_CS0_CONFIG: - .word 0x00203331 -VAL_TC_EMIFS_CS1_CONFIG: - .word 0x8180fff3 -VAL_TC_EMIFS_CS2_CONFIG: - .word 0xf800f22a -VAL_TC_EMIFS_CS3_CONFIG: - .word 0x88013141 -VAL_TC_EMIFS_CS1_ADVANCED: - .word 0x00000022 -#endif - -VAL_ARM_IDLECT1: - .word 0x00000400 -VAL_ARM_IDLECT2: - .word 0x00000886 -VAL_ARM_IDLECT3: - .word 0x00000015 - -SRAM_STACK: - .word CONFIG_SYS_INIT_SP_ADDR - -/* command values */ -.equ CMD_SDRAM_NOP, 0x00000000 -.equ CMD_SDRAM_PRECHARGE, 0x00000001 -.equ CMD_SDRAM_AUTOREFRESH, 0x00000002 -.equ CMD_SDRAM_CKE_SET_HIGH, 0x00000007 diff --git a/board/ti/omap5912osk/omap5912osk.c b/board/ti/omap5912osk/omap5912osk.c deleted file mode 100644 index 88a7310..0000000 --- a/board/ti/omap5912osk/omap5912osk.c +++ /dev/null @@ -1,307 +0,0 @@ -/* - * (C) Copyright 2002 - * Sysgo Real-Time Solutions, GmbH <www.elinos.com> - * Marius Groeger <mgroeger@sysgo.de> - * - * (C) Copyright 2002 - * David Mueller, ELSOFT AG, <d.mueller@elsoft.ch> - * - * (C) Copyright 2003 - * Texas Instruments, <www.ti.com> - * Kshitij Gupta <Kshitij@ti.com> - * - * (C) Copyright 2004 - * Texas Instruments, <www.ti.com> - * Rishi Bhattacharya <rishi@ti.com> - * - * SPDX-License-Identifier: GPL-2.0+ - */ - -#include <common.h> -#include <netdev.h> -#if defined(CONFIG_OMAP1610) -#include <./configs/omap1510.h> -#endif - -DECLARE_GLOBAL_DATA_PTR; - -void flash__init (void); -void ether__init (void); -void set_muxconf_regs (void); -void peripheral_power_enable (void); - -#define COMP_MODE_ENABLE ((unsigned int)0x0000EAEF) - -static inline void delay (unsigned long loops) -{ - __asm__ volatile ("1:\n" - "subs %0, %1, #1\n" - "bne 1b":"=r" (loops):"0" (loops)); -} - -/* - * Miscellaneous platform dependent initialisations - */ - -int board_init (void) -{ - gd->bd->bi_arch_number = MACH_TYPE_OMAP_OSK; - - /* adress of boot parameters */ - gd->bd->bi_boot_params = 0x10000100; - - flash__init(); - ether__init(); - - return 0; -} - -void s_init(void) -{ - /* Configure MUX settings */ - set_muxconf_regs (); - peripheral_power_enable (); - -/* this speeds up your boot a quite a bit. However to make it - * work, you need make sure your kernel startup flush bug is fixed. - * ... rkw ... - */ - icache_enable (); -} - -/****************************** - Routine: - Description: -******************************/ -void flash__init (void) -{ -#define EMIFS_GlB_Config_REG 0xfffecc0c - unsigned int regval; - regval = *((volatile unsigned int *) EMIFS_GlB_Config_REG); - /* Turn off write protection for flash devices. */ - regval = regval | 0x0001; - *((volatile unsigned int *) EMIFS_GlB_Config_REG) = regval; -} -/************************************************************* - Routine:ether__init - Description: take the Ethernet controller out of reset and wait - for the EEPROM load to complete. -*************************************************************/ -void ether__init (void) -{ -#define ETH_CONTROL_REG 0x0480000b - int i; - - *((volatile unsigned short *) 0xfffece08) = 0x03FF; - *((volatile unsigned short *) 0xfffb3824) = 0x8000; - *((volatile unsigned short *) 0xfffb3830) = 0x0000; - *((volatile unsigned short *) 0xfffb3834) = 0x0009; - *((volatile unsigned short *) 0xfffb3838) = 0x0009; - *((volatile unsigned short *) 0xfffb3818) = 0x0002; - *((volatile unsigned short *) 0xfffb382C) = 0x0048; - *((volatile unsigned short *) 0xfffb3824) = 0x8603; - udelay (3); - for (i=0;i<2000;i++); - *((volatile unsigned short *) 0xfffb381C) = 0x6610; - udelay (30); - for (i=0;i<10000;i++); - - *((volatile unsigned char *) ETH_CONTROL_REG) &= ~0x01; - udelay (3); - - -} - -/****************************** - Routine: - Description: -******************************/ -int dram_init(void) -{ - gd->ram_size = get_ram_size((long *)PHYS_SDRAM_1, PHYS_SDRAM_1_SIZE); - - return 0; -} - -void dram_init_banksize(void) -{ - gd->bd->bi_dram[0].start = PHYS_SDRAM_1; - gd->bd->bi_dram[0].size = PHYS_SDRAM_1_SIZE; -} - -/****************************************************** - Routine: set_muxconf_regs - Description: Setting up the configuration Mux registers - specific to the hardware -*******************************************************/ -void set_muxconf_regs (void) -{ - volatile unsigned int *MuxConfReg; - /* set each registers to its reset value; */ - MuxConfReg = - (volatile unsigned int *) ((unsigned int) FUNC_MUX_CTRL_0); - /* setup for UART1 */ - *MuxConfReg &= ~(0x02000000); /* bit 25 */ - /* setup for UART2 */ - *MuxConfReg &= ~(0x01000000); /* bit 24 */ - /* Disable Uwire CS Hi-Z */ - *MuxConfReg |= 0x08000000; - MuxConfReg = - (volatile unsigned int *) ((unsigned int) FUNC_MUX_CTRL_3); - *MuxConfReg = 0x00000000; - MuxConfReg = - (volatile unsigned int *) ((unsigned int) FUNC_MUX_CTRL_4); - *MuxConfReg = 0x00000000; - MuxConfReg = - (volatile unsigned int *) ((unsigned int) FUNC_MUX_CTRL_5); - *MuxConfReg = 0x00000000; - MuxConfReg = - (volatile unsigned int *) ((unsigned int) FUNC_MUX_CTRL_6); - /*setup mux for UART3 */ - *MuxConfReg |= 0x00000001; /* bit3, 1, 0 (mux0 5,5,26) */ - *MuxConfReg &= ~0x0000003e; - MuxConfReg = - (volatile unsigned int *) ((unsigned int) FUNC_MUX_CTRL_7); - *MuxConfReg = 0x00000000; - MuxConfReg = - (volatile unsigned int *) ((unsigned int) FUNC_MUX_CTRL_8); - /* Disable Uwire CS Hi-Z */ - *MuxConfReg |= 0x00001200; /*bit 9 for CS0 12 for CS3 */ - MuxConfReg = - (volatile unsigned int *) ((unsigned int) FUNC_MUX_CTRL_9); - /* Need to turn on bits 21 and 12 in FUNC_MUX_CTRL_9 so the */ - /* hardware will actually use TX and RTS based on bit 25 in */ - /* FUNC_MUX_CTRL_0. I told you this thing was screwy! */ - *MuxConfReg |= 0x00201000; - MuxConfReg = - (volatile unsigned int *) ((unsigned int) FUNC_MUX_CTRL_A); - *MuxConfReg = 0x00000000; - MuxConfReg = - (volatile unsigned int *) ((unsigned int) FUNC_MUX_CTRL_B); - *MuxConfReg = 0x00000000; - MuxConfReg = - (volatile unsigned int *) ((unsigned int) FUNC_MUX_CTRL_C); - /* setup for UART2 */ - /* Need to turn on bits 27 and 24 in FUNC_MUX_CTRL_C so the */ - /* hardware will actually use TX and RTS based on bit 24 in */ - /* FUNC_MUX_CTRL_0. */ - *MuxConfReg |= 0x09000000; - MuxConfReg = - (volatile unsigned int *) ((unsigned int) FUNC_MUX_CTRL_D); - *MuxConfReg |= 0x00000020; - MuxConfReg = - (volatile unsigned int *) ((unsigned int) PULL_DWN_CTRL_0); - *MuxConfReg = 0x00000000; - MuxConfReg = - (volatile unsigned int *) ((unsigned int) PULL_DWN_CTRL_1); - *MuxConfReg = 0x00000000; - /* mux setup for SD/MMC driver */ - MuxConfReg = - (volatile unsigned int *) ((unsigned int) PULL_DWN_CTRL_2); - *MuxConfReg &= 0xFFFE0FFF; - MuxConfReg = - (volatile unsigned int *) ((unsigned int) PULL_DWN_CTRL_3); - *MuxConfReg = 0x00000000; - MuxConfReg = - (volatile unsigned int *) ((unsigned int) MOD_CONF_CTRL_0); - /* bit 13 for MMC2 XOR_CLK */ - *MuxConfReg &= ~(0x00002000); - /* bit 29 for UART 1 */ - *MuxConfReg &= ~(0x00002000); - MuxConfReg = - (volatile unsigned int *) ((unsigned int) FUNC_MUX_CTRL_0); - /* Configure for USB. Turn on VBUS_CTRL and VBUS_MODE. */ - *MuxConfReg |= 0x000C0000; - MuxConfReg = - (volatile unsigned int *) ((unsigned int)USB_TRANSCEIVER_CTRL); - *MuxConfReg &= ~(0x00000070); - *MuxConfReg &= ~(0x00000008); - *MuxConfReg |= 0x00000003; - *MuxConfReg |= 0x00000180; - MuxConfReg = - (volatile unsigned int *) ((unsigned int) MOD_CONF_CTRL_0); - /* bit 17, software controls VBUS */ - *MuxConfReg &= ~(0x00020000); - /* Enable USB 48 and 12M clocks */ - *MuxConfReg |= 0x00000200; - *MuxConfReg &= ~(0x00000180); - /*2.75V for MMCSDIO1 */ - MuxConfReg = - (volatile unsigned int *) ((unsigned int) VOLTAGE_CTRL_0); - *MuxConfReg = 0x00001FE7; - MuxConfReg = - (volatile unsigned int *) ((unsigned int) PU_PD_SEL_0); - *MuxConfReg = 0x00000000; - MuxConfReg = - (volatile unsigned int *) ((unsigned int) PU_PD_SEL_1); - *MuxConfReg = 0x00000000; - MuxConfReg = - (volatile unsigned int *) ((unsigned int) PU_PD_SEL_2); - *MuxConfReg = 0x00000000; - MuxConfReg = - (volatile unsigned int *) ((unsigned int) PU_PD_SEL_3); - *MuxConfReg = 0x00000000; - MuxConfReg = - (volatile unsigned int *) ((unsigned int) PU_PD_SEL_4); - *MuxConfReg = 0x00000000; - MuxConfReg = - (volatile unsigned int *) ((unsigned int) PULL_DWN_CTRL_4); - *MuxConfReg = 0x00000000; - /* Turn on UART2 48 MHZ clock */ - MuxConfReg = - (volatile unsigned int *) ((unsigned int) MOD_CONF_CTRL_0); - *MuxConfReg |= 0x40000000; - MuxConfReg = - (volatile unsigned int *) ((unsigned int) USB_OTG_CTRL); - /* setup for USB VBus detection OMAP161x */ - *MuxConfReg |= 0x00040000; /* bit 18 */ - MuxConfReg = - (volatile unsigned int *) ((unsigned int) PU_PD_SEL_2); - /* PullUps for SD/MMC driver */ - *MuxConfReg |= ~(0xFFFE0FFF); - MuxConfReg = - (volatile unsigned int *) ((unsigned int)COMP_MODE_CTRL_0); - *MuxConfReg = COMP_MODE_ENABLE; -} - -/****************************************************** - Routine: peripheral_power_enable - Description: Enable the power for UART1 -*******************************************************/ -void peripheral_power_enable (void) -{ -#define UART1_48MHZ_ENABLE ((unsigned short)0x0200) -#define SW_CLOCK_REQUEST ((volatile unsigned short *)0xFFFE0834) - - *SW_CLOCK_REQUEST |= UART1_48MHZ_ENABLE; -} - -/* - * Check Board Identity - */ -int checkboard(void) -{ - char buf[64]; - int i = getenv_f("serial#", buf, sizeof(buf)); - - puts("Board: OSK5912"); - - if (i > 0) { - puts(", serial# "); - puts(buf); - } - putc('\n'); - - return (0); -} - -#ifdef CONFIG_CMD_NET -int board_eth_init(bd_t *bis) -{ - int rc = 0; -#ifdef CONFIG_LAN91C96 - rc = lan91c96_initialize(0, CONFIG_LAN91C96_BASE); -#endif - return rc; -} -#endif diff --git a/board/tqc/tqm8272/nand.c b/board/tqc/tqm8272/nand.c index 4925b8d..7fb2dfa 100644 --- a/board/tqc/tqm8272/nand.c +++ b/board/tqc/tqm8272/nand.c @@ -188,6 +188,7 @@ static void tqm8272_write_buf(struct mtd_info *mtdinfo, const uint8_t *buf, int *base = buf[i]; } +#if defined(CONFIG_MTD_NAND_VERIFY_WRITE) static int tqm8272_verify_buf(struct mtd_info *mtdinfo, const uint8_t *buf, int len) { struct nand_chip *this = mtdinfo->priv; @@ -199,6 +200,7 @@ static int tqm8272_verify_buf(struct mtd_info *mtdinfo, const uint8_t *buf, int return -1; return 0; } +#endif #endif /* #ifndef CONFIG_NAND_SPL */ void board_nand_select_device(struct nand_chip *nand, int chip) @@ -247,8 +249,10 @@ int board_nand_init(struct nand_chip *nand) #ifndef CONFIG_NAND_SPL nand->write_buf = tqm8272_write_buf; nand->read_buf = tqm8272_read_buf; +#if defined(CONFIG_MTD_NAND_VERIFY_WRITE) nand->verify_buf = tqm8272_verify_buf; #endif +#endif /* * Select required NAND chip diff --git a/board/tqc/tqma6/Makefile b/board/tqc/tqma6/Makefile new file mode 100644 index 0000000..9ee6920 --- /dev/null +++ b/board/tqc/tqma6/Makefile @@ -0,0 +1,9 @@ +# +# Copyright (C) 2014, Markus Niebel <Markus.Niebel@tq-group.com> +# +# SPDX-License-Identifier: GPL-2.0+ +# + +obj-y := tqma6.o + +obj-$(CONFIG_MBA6) += tqma6_mba6.o diff --git a/board/tqc/tqma6/README b/board/tqc/tqma6/README new file mode 100644 index 0000000..2c012e7 --- /dev/null +++ b/board/tqc/tqma6/README @@ -0,0 +1,35 @@ +U-Boot for the TQ Systems TQMa6 modules + +This file contains information for the port of +U-Boot to the TQ Systems TQMa6 modules. + +1. Boot source +-------------- + +The following boot source is supported: + +- SD/eMMC +- SPI NOR + +2. Building +------------ + +To build U-Boot for the TQ Systems TQMa6 modules: + + make tqma6<x>_<baseboard>_<boot>_config + make + +x is a placeholder for the CPU variant +q - means i.MX6Q/D: TQMa6Q (i.MX6Q) and TQMa6D (i.MX6D) +s - means i.MX6S: TQMa6S (i.MX6S) + +baseboard is a placeholder for the boot device +mmc - means eMMC +spi - mean SPI NOR + +This gives the following configurations: + +tqma6q_mba6_mmc_config +tqma6q_mba6_spi_config +tqma6s_mba6_mmc_config +tqma6s_mba6_spi_config diff --git a/board/tqc/tqma6/clocks.cfg b/board/tqc/tqma6/clocks.cfg new file mode 100644 index 0000000..d9dd273 --- /dev/null +++ b/board/tqc/tqma6/clocks.cfg @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2013 Boundary Devices + * Copyright (C) 2013, 2014 Markus Niebel <Markus.Niebel@tq-group.com> + * + * SPDX-License-Identifier: GPL-2.0+ + * + * Refer doc/README.imximage for more details about how-to configure + * and create imximage boot image + */ + +/* set the default clock gate to save power */ +DATA 4, CCM_CCGR0, 0x00C03F3F +DATA 4, CCM_CCGR1, 0x0030FC03 +DATA 4, CCM_CCGR2, 0x0FFFC000 +DATA 4, CCM_CCGR3, 0x3FF00000 +DATA 4, CCM_CCGR4, 0x00FFF300 +DATA 4, CCM_CCGR5, 0x0F0000C3 +DATA 4, CCM_CCGR6, 0x000003FF + +/* enable AXI cache for VDOA/VPU/IPU */ +DATA 4, MX6_IOMUXC_GPR4, 0xF00000CF +/* set IPU AXI-id0 Qos=0xf(bypass) AXI-id1 Qos=0x7 */ +DATA 4, MX6_IOMUXC_GPR6, 0x007F007F +DATA 4, MX6_IOMUXC_GPR7, 0x007F007F diff --git a/board/tqc/tqma6/tqma6.c b/board/tqc/tqma6/tqma6.c new file mode 100644 index 0000000..b552bb8 --- /dev/null +++ b/board/tqc/tqma6/tqma6.c @@ -0,0 +1,262 @@ +/* + * Copyright (C) 2012 Freescale Semiconductor, Inc. + * Author: Fabio Estevam <fabio.estevam@freescale.com> + * + * Copyright (C) 2013, 2014 TQ Systems (ported SabreSD to TQMa6x) + * Author: Markus Niebel <markus.niebel@tq-group.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <asm/arch/clock.h> +#include <asm/arch/mx6-pins.h> +#include <asm/arch/imx-regs.h> +#include <asm/arch/iomux.h> +#include <asm/arch/sys_proto.h> +#include <asm/errno.h> +#include <asm/gpio.h> +#include <asm/io.h> +#include <asm/imx-common/mxc_i2c.h> +#include <common.h> +#include <fsl_esdhc.h> +#include <libfdt.h> +#include <i2c.h> +#include <mmc.h> +#include <power/pfuze100_pmic.h> +#include <power/pmic.h> + +#include "tqma6_bb.h" + +DECLARE_GLOBAL_DATA_PTR; + +#define USDHC_CLK_PAD_CTRL (PAD_CTL_PUS_47K_UP | PAD_CTL_SPEED_LOW | \ + PAD_CTL_DSE_40ohm | PAD_CTL_SRE_FAST | PAD_CTL_HYS) + +#define USDHC_PAD_CTRL (PAD_CTL_PUS_47K_UP | PAD_CTL_SPEED_LOW | \ + PAD_CTL_DSE_80ohm | PAD_CTL_SRE_FAST | PAD_CTL_HYS) + +#define GPIO_OUT_PAD_CTRL (PAD_CTL_PUS_100K_UP | PAD_CTL_SPEED_LOW | \ + PAD_CTL_DSE_40ohm | PAD_CTL_HYS) + +#define GPIO_IN_PAD_CTRL (PAD_CTL_PUS_100K_UP | PAD_CTL_SPEED_LOW | \ + PAD_CTL_DSE_40ohm | PAD_CTL_HYS) + +#define SPI_PAD_CTRL (PAD_CTL_PUS_100K_UP | PAD_CTL_SPEED_MED | \ + PAD_CTL_DSE_80ohm | PAD_CTL_SRE_FAST | PAD_CTL_HYS) + +#define I2C_PAD_CTRL (PAD_CTL_PUS_100K_UP | PAD_CTL_SPEED_MED | \ + PAD_CTL_DSE_40ohm | PAD_CTL_HYS | \ + PAD_CTL_ODE | PAD_CTL_SRE_FAST) + +int dram_init(void) +{ + gd->ram_size = get_ram_size((void *)PHYS_SDRAM, PHYS_SDRAM_SIZE); + + return 0; +} + +static const uint16_t tqma6_emmc_dsr = 0x0100; + +/* eMMC on USDHCI3 always present */ +static iomux_v3_cfg_t const tqma6_usdhc3_pads[] = { + NEW_PAD_CTRL(MX6_PAD_SD3_CLK__SD3_CLK, USDHC_PAD_CTRL), + NEW_PAD_CTRL(MX6_PAD_SD3_CMD__SD3_CMD, USDHC_PAD_CTRL), + NEW_PAD_CTRL(MX6_PAD_SD3_DAT0__SD3_DATA0, USDHC_PAD_CTRL), + NEW_PAD_CTRL(MX6_PAD_SD3_DAT1__SD3_DATA1, USDHC_PAD_CTRL), + NEW_PAD_CTRL(MX6_PAD_SD3_DAT2__SD3_DATA2, USDHC_PAD_CTRL), + NEW_PAD_CTRL(MX6_PAD_SD3_DAT3__SD3_DATA3, USDHC_PAD_CTRL), + NEW_PAD_CTRL(MX6_PAD_SD3_DAT4__SD3_DATA4, USDHC_PAD_CTRL), + NEW_PAD_CTRL(MX6_PAD_SD3_DAT5__SD3_DATA5, USDHC_PAD_CTRL), + NEW_PAD_CTRL(MX6_PAD_SD3_DAT6__SD3_DATA6, USDHC_PAD_CTRL), + NEW_PAD_CTRL(MX6_PAD_SD3_DAT7__SD3_DATA7, USDHC_PAD_CTRL), + /* eMMC reset */ + NEW_PAD_CTRL(MX6_PAD_SD3_RST__SD3_RESET, GPIO_OUT_PAD_CTRL), +}; + +/* + * According to board_mmc_init() the following map is done: + * (U-boot device node) (Physical Port) + * mmc0 eMMC (SD3) on TQMa6 + * mmc1 .. n optional slots used on baseboard + */ +struct fsl_esdhc_cfg tqma6_usdhc_cfg = { + .esdhc_base = USDHC3_BASE_ADDR, + .max_bus_width = 8, +}; + +int board_mmc_getcd(struct mmc *mmc) +{ + struct fsl_esdhc_cfg *cfg = (struct fsl_esdhc_cfg *)mmc->priv; + int ret = 0; + + if (cfg->esdhc_base == USDHC3_BASE_ADDR) + /* eMMC/uSDHC3 is always present */ + ret = 1; + else + ret = tqma6_bb_board_mmc_getcd(mmc); + + return ret; +} + +int board_mmc_getwp(struct mmc *mmc) +{ + struct fsl_esdhc_cfg *cfg = (struct fsl_esdhc_cfg *)mmc->priv; + int ret = 0; + + if (cfg->esdhc_base == USDHC3_BASE_ADDR) + /* eMMC/uSDHC3 is always present */ + ret = 0; + else + ret = tqma6_bb_board_mmc_getwp(mmc); + + return ret; +} + +int board_mmc_init(bd_t *bis) +{ + imx_iomux_v3_setup_multiple_pads(tqma6_usdhc3_pads, + ARRAY_SIZE(tqma6_usdhc3_pads)); + tqma6_usdhc_cfg.sdhc_clk = mxc_get_clock(MXC_ESDHC3_CLK); + if (fsl_esdhc_initialize(bis, &tqma6_usdhc_cfg)) { + puts("Warning: failed to initialize eMMC dev\n"); + } else { + struct mmc *mmc = find_mmc_device(0); + if (mmc) + mmc_set_dsr(mmc, tqma6_emmc_dsr); + } + + tqma6_bb_board_mmc_init(bis); + + return 0; +} + +static iomux_v3_cfg_t const tqma6_ecspi1_pads[] = { + /* SS1 */ + NEW_PAD_CTRL(MX6_PAD_EIM_D19__GPIO3_IO19, SPI_PAD_CTRL), + NEW_PAD_CTRL(MX6_PAD_EIM_D16__ECSPI1_SCLK, SPI_PAD_CTRL), + NEW_PAD_CTRL(MX6_PAD_EIM_D17__ECSPI1_MISO, SPI_PAD_CTRL), + NEW_PAD_CTRL(MX6_PAD_EIM_D18__ECSPI1_MOSI, SPI_PAD_CTRL), +}; + +static unsigned const tqma6_ecspi1_cs[] = { + IMX_GPIO_NR(3, 19), +}; + +static void tqma6_iomuxc_spi(void) +{ + unsigned i; + + for (i = 0; i < ARRAY_SIZE(tqma6_ecspi1_cs); ++i) + gpio_direction_output(tqma6_ecspi1_cs[i], 1); + imx_iomux_v3_setup_multiple_pads(tqma6_ecspi1_pads, + ARRAY_SIZE(tqma6_ecspi1_pads)); +} + +static struct i2c_pads_info tqma6_i2c3_pads = { + /* I2C3: on board LM75, M24C64, */ + .scl = { + .i2c_mode = NEW_PAD_CTRL(MX6_PAD_GPIO_5__I2C3_SCL, + I2C_PAD_CTRL), + .gpio_mode = NEW_PAD_CTRL(MX6_PAD_GPIO_5__GPIO1_IO05, + I2C_PAD_CTRL), + .gp = IMX_GPIO_NR(1, 5) + }, + .sda = { + .i2c_mode = NEW_PAD_CTRL(MX6_PAD_GPIO_6__I2C3_SDA, + I2C_PAD_CTRL), + .gpio_mode = NEW_PAD_CTRL(MX6_PAD_GPIO_6__GPIO1_IO06, + I2C_PAD_CTRL), + .gp = IMX_GPIO_NR(1, 6) + } +}; + +static void tqma6_setup_i2c(void) +{ + /* use logical index for bus, e.g. I2C1 -> 0 */ + setup_i2c(2, CONFIG_SYS_I2C_SPEED, 0x7f, &tqma6_i2c3_pads); +} + +int board_early_init_f(void) +{ + return tqma6_bb_board_early_init_f(); +} + +int board_init(void) +{ + /* address of boot parameters */ + gd->bd->bi_boot_params = PHYS_SDRAM + 0x100; + + tqma6_iomuxc_spi(); + tqma6_setup_i2c(); + + tqma6_bb_board_init(); + + return 0; +} + +static const char *tqma6_get_boardname(void) +{ + u32 cpurev = get_cpu_rev(); + + switch ((cpurev & 0xFF000) >> 12) { + case MXC_CPU_MX6SOLO: + return "TQMa6S"; + break; + case MXC_CPU_MX6DL: + return "TQMa6DL"; + break; + case MXC_CPU_MX6D: + return "TQMa6D"; + break; + case MXC_CPU_MX6Q: + return "TQMa6Q"; + break; + default: + return "??"; + }; +} + +int board_late_init(void) +{ + struct pmic *p; + u32 reg; + + setenv("board_name", tqma6_get_boardname()); + + /* + * configure PFUZE100 PMIC: + * TODO: should go to power_init_board if bus switching is + * fixed in generic power code + */ + power_pfuze100_init(TQMA6_PFUZE100_I2C_BUS); + p = pmic_get("PFUZE100"); + if (p && !pmic_probe(p)) { + pmic_reg_read(p, PFUZE100_DEVICEID, ®); + printf("PMIC: PFUZE100 ID=0x%02x\n", reg); + } + + tqma6_bb_board_late_init(); + + return 0; +} + +int checkboard(void) +{ + printf("Board: %s on a %s\n", tqma6_get_boardname(), + tqma6_bb_get_boardname()); + return 0; +} + +/* + * Device Tree Support + */ +#if defined(CONFIG_OF_BOARD_SETUP) && defined(CONFIG_OF_LIBFDT) +void ft_board_setup(void *blob, bd_t *bd) +{ + /* bring in eMMC dsr settings */ + do_fixup_by_path_u32(blob, + "/soc/aips-bus@02100000/usdhc@02198000", + "dsr", tqma6_emmc_dsr, 2); + tqma6_bb_ft_board_setup(blob, bd); +} +#endif /* defined(CONFIG_OF_BOARD_SETUP) && defined(CONFIG_OF_LIBFDT) */ diff --git a/board/tqc/tqma6/tqma6_bb.h b/board/tqc/tqma6/tqma6_bb.h new file mode 100644 index 0000000..9d072d2 --- /dev/null +++ b/board/tqc/tqma6/tqma6_bb.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2013, 2014 TQ Systems + * Author: Markus Niebel <markus.niebel@tq-group.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __TQMA6_BB__ +#define __TQMA6_BB + +#include <common.h> + +int tqma6_bb_board_mmc_getwp(struct mmc *mmc); +int tqma6_bb_board_mmc_getcd(struct mmc *mmc); +int tqma6_bb_board_mmc_init(bd_t *bis); + +int tqma6_bb_board_early_init_f(void); +int tqma6_bb_board_init(void); +int tqma6_bb_board_late_init(void); +int tqma6_bb_checkboard(void); + +const char *tqma6_bb_get_boardname(void); +/* + * Device Tree Support + */ +#if defined(CONFIG_OF_BOARD_SETUP) && defined(CONFIG_OF_LIBFDT) +void tqma6_bb_ft_board_setup(void *blob, bd_t *bd); +#endif /* defined(CONFIG_OF_BOARD_SETUP) && defined(CONFIG_OF_LIBFDT) */ + +#endif diff --git a/board/tqc/tqma6/tqma6_mba6.c b/board/tqc/tqma6/tqma6_mba6.c new file mode 100644 index 0000000..fd59287 --- /dev/null +++ b/board/tqc/tqma6/tqma6_mba6.c @@ -0,0 +1,361 @@ +/* + * Copyright (C) 2012 Freescale Semiconductor, Inc. + * Author: Fabio Estevam <fabio.estevam@freescale.com> + * + * Copyright (C) 2013, 2014 TQ Systems (ported SabreSD to TQMa6x) + * Author: Markus Niebel <markus.niebel@tq-group.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <asm/io.h> +#include <asm/arch/clock.h> +#include <asm/arch/mx6-pins.h> +#include <asm/arch/imx-regs.h> +#include <asm/arch/iomux.h> +#include <asm/arch/sys_proto.h> +#include <asm/errno.h> +#include <asm/gpio.h> +#include <asm/imx-common/mxc_i2c.h> + +#include <common.h> +#include <fsl_esdhc.h> +#include <libfdt.h> +#include <malloc.h> +#include <i2c.h> +#include <micrel.h> +#include <miiphy.h> +#include <mmc.h> +#include <netdev.h> + +#include "tqma6_bb.h" + +DECLARE_GLOBAL_DATA_PTR; + +#define UART_PAD_CTRL (PAD_CTL_PUS_100K_UP | PAD_CTL_SPEED_MED | \ + PAD_CTL_DSE_80ohm | PAD_CTL_SRE_FAST | PAD_CTL_HYS) + +#define USDHC_CLK_PAD_CTRL (PAD_CTL_PUS_47K_UP | PAD_CTL_SPEED_LOW | \ + PAD_CTL_DSE_40ohm | PAD_CTL_SRE_FAST | PAD_CTL_HYS) + +#define USDHC_PAD_CTRL (PAD_CTL_PUS_47K_UP | PAD_CTL_SPEED_LOW | \ + PAD_CTL_DSE_80ohm | PAD_CTL_SRE_FAST | PAD_CTL_HYS) + +#define GPIO_OUT_PAD_CTRL (PAD_CTL_PUS_100K_UP | PAD_CTL_SPEED_LOW | \ + PAD_CTL_DSE_40ohm | PAD_CTL_HYS) + +#define GPIO_IN_PAD_CTRL (PAD_CTL_PUS_100K_UP | PAD_CTL_SPEED_LOW | \ + PAD_CTL_DSE_40ohm | PAD_CTL_HYS) + +#define SPI_PAD_CTRL (PAD_CTL_PUS_100K_UP | PAD_CTL_SPEED_MED | \ + PAD_CTL_DSE_80ohm | PAD_CTL_SRE_FAST | PAD_CTL_HYS) + +#define I2C_PAD_CTRL (PAD_CTL_PUS_100K_UP | PAD_CTL_SPEED_MED | \ + PAD_CTL_DSE_40ohm | PAD_CTL_HYS | \ + PAD_CTL_ODE | PAD_CTL_SRE_FAST) + +#if defined(CONFIG_MX6Q) + +#define IOMUX_SW_PAD_CTRL_GRP_DDR_TYPE_RGMII 0x02e0790 +#define IOMUX_SW_PAD_CTRL_GRP_RGMII_TERM 0x02e07ac + +#elif defined(CONFIG_MX6S) + +#define IOMUX_SW_PAD_CTRL_GRP_DDR_TYPE_RGMII 0x02e0768 +#define IOMUX_SW_PAD_CTRL_GRP_RGMII_TERM 0x02e0788 + +#else + +#error "need to define target CPU" + +#endif + +#define ENET_RX_PAD_CTRL (PAD_CTL_DSE_34ohm) +#define ENET_TX_PAD_CTRL (PAD_CTL_PUS_100K_UP | PAD_CTL_DSE_34ohm) +#define ENET_CLK_PAD_CTRL (PAD_CTL_PUS_100K_UP | PAD_CTL_SPEED_HIGH | \ + PAD_CTL_DSE_34ohm) +#define ENET_MDIO_PAD_CTRL (PAD_CTL_PUS_100K_UP | PAD_CTL_SPEED_MED | \ + PAD_CTL_DSE_60ohm) + +/* disable on die termination for RGMII */ +#define IOMUX_SW_PAD_CTRL_GRP_RGMII_TERM_DISABLE 0x00000000 +/* optimised drive strength for 1.0 .. 1.3 V signal on RGMII */ +#define IOMUX_SW_PAD_CTRL_GRP_DDR_TYPE_RGMII_1P2V 0x00080000 +/* optimised drive strength for 1.3 .. 2.5 V signal on RGMII */ +#define IOMUX_SW_PAD_CTRL_GRP_DDR_TYPE_RGMII_1P5V 0x000C0000 + +#define ENET_PHY_RESET_GPIO IMX_GPIO_NR(1, 25) + +static iomux_v3_cfg_t const mba6_enet_pads[] = { + NEW_PAD_CTRL(MX6_PAD_ENET_MDIO__ENET_MDIO, ENET_MDIO_PAD_CTRL), + NEW_PAD_CTRL(MX6_PAD_ENET_MDC__ENET_MDC, ENET_MDIO_PAD_CTRL), + + NEW_PAD_CTRL(MX6_PAD_RGMII_TXC__RGMII_TXC, ENET_TX_PAD_CTRL), + NEW_PAD_CTRL(MX6_PAD_RGMII_TD0__RGMII_TD0, ENET_TX_PAD_CTRL), + NEW_PAD_CTRL(MX6_PAD_RGMII_TD1__RGMII_TD1, ENET_TX_PAD_CTRL), + NEW_PAD_CTRL(MX6_PAD_RGMII_TD2__RGMII_TD2, ENET_TX_PAD_CTRL), + NEW_PAD_CTRL(MX6_PAD_RGMII_TD3__RGMII_TD3, ENET_TX_PAD_CTRL), + NEW_PAD_CTRL(MX6_PAD_RGMII_TX_CTL__RGMII_TX_CTL, + ENET_TX_PAD_CTRL), + NEW_PAD_CTRL(MX6_PAD_ENET_REF_CLK__ENET_TX_CLK, ENET_CLK_PAD_CTRL), + /* + * these pins are also used for config strapping by phy + */ + NEW_PAD_CTRL(MX6_PAD_RGMII_RD0__RGMII_RD0, ENET_RX_PAD_CTRL), + NEW_PAD_CTRL(MX6_PAD_RGMII_RD1__RGMII_RD1, ENET_RX_PAD_CTRL), + NEW_PAD_CTRL(MX6_PAD_RGMII_RD2__RGMII_RD2, ENET_RX_PAD_CTRL), + NEW_PAD_CTRL(MX6_PAD_RGMII_RD3__RGMII_RD3, ENET_RX_PAD_CTRL), + NEW_PAD_CTRL(MX6_PAD_RGMII_RXC__RGMII_RXC, ENET_RX_PAD_CTRL), + NEW_PAD_CTRL(MX6_PAD_RGMII_RX_CTL__RGMII_RX_CTL, + ENET_RX_PAD_CTRL), + /* KSZ9031 PHY Reset */ + NEW_PAD_CTRL(MX6_PAD_ENET_CRS_DV__GPIO1_IO25, GPIO_OUT_PAD_CTRL), +}; + +static void mba6_setup_iomuxc_enet(void) +{ + __raw_writel(IOMUX_SW_PAD_CTRL_GRP_RGMII_TERM_DISABLE, + (void *)IOMUX_SW_PAD_CTRL_GRP_RGMII_TERM); + __raw_writel(IOMUX_SW_PAD_CTRL_GRP_DDR_TYPE_RGMII_1P5V, + (void *)IOMUX_SW_PAD_CTRL_GRP_DDR_TYPE_RGMII); + + imx_iomux_v3_setup_multiple_pads(mba6_enet_pads, + ARRAY_SIZE(mba6_enet_pads)); + + /* Reset PHY */ + gpio_direction_output(ENET_PHY_RESET_GPIO , 0); + /* Need delay 10ms after power on according to KSZ9031 spec */ + udelay(1000 * 10); + gpio_set_value(ENET_PHY_RESET_GPIO, 1); + /* + * KSZ9031 manual: 100 usec wait time after reset before communication + * over MDIO + * BUGBUG: hardware has an RC const that needs > 10 msec from 0->1 on + * reset before the phy sees a high level + */ + udelay(200); +} + +static iomux_v3_cfg_t const mba6_uart2_pads[] = { + NEW_PAD_CTRL(MX6_PAD_SD4_DAT4__UART2_RX_DATA, UART_PAD_CTRL), + NEW_PAD_CTRL(MX6_PAD_SD4_DAT7__UART2_TX_DATA, UART_PAD_CTRL), +}; + +static void mba6_setup_iomuxc_uart(void) +{ + imx_iomux_v3_setup_multiple_pads(mba6_uart2_pads, + ARRAY_SIZE(mba6_uart2_pads)); +} + +#define USDHC2_CD_GPIO IMX_GPIO_NR(1, 4) +#define USDHC2_WP_GPIO IMX_GPIO_NR(1, 2) + +int tqma6_bb_board_mmc_getcd(struct mmc *mmc) +{ + struct fsl_esdhc_cfg *cfg = (struct fsl_esdhc_cfg *)mmc->priv; + int ret = 0; + + if (cfg->esdhc_base == USDHC2_BASE_ADDR) + ret = !gpio_get_value(USDHC2_CD_GPIO); + + return ret; +} + +int tqma6_bb_board_mmc_getwp(struct mmc *mmc) +{ + struct fsl_esdhc_cfg *cfg = (struct fsl_esdhc_cfg *)mmc->priv; + int ret = 0; + + if (cfg->esdhc_base == USDHC2_BASE_ADDR) + ret = gpio_get_value(USDHC2_WP_GPIO); + + return ret; +} + +static struct fsl_esdhc_cfg mba6_usdhc_cfg = { + .esdhc_base = USDHC2_BASE_ADDR, + .max_bus_width = 4, +}; + +static iomux_v3_cfg_t const mba6_usdhc2_pads[] = { + NEW_PAD_CTRL(MX6_PAD_SD2_CLK__SD2_CLK, USDHC_CLK_PAD_CTRL), + NEW_PAD_CTRL(MX6_PAD_SD2_CMD__SD2_CMD, USDHC_PAD_CTRL), + NEW_PAD_CTRL(MX6_PAD_SD2_DAT0__SD2_DATA0, USDHC_PAD_CTRL), + NEW_PAD_CTRL(MX6_PAD_SD2_DAT1__SD2_DATA1, USDHC_PAD_CTRL), + NEW_PAD_CTRL(MX6_PAD_SD2_DAT2__SD2_DATA2, USDHC_PAD_CTRL), + NEW_PAD_CTRL(MX6_PAD_SD2_DAT3__SD2_DATA3, USDHC_PAD_CTRL), + /* CD */ + NEW_PAD_CTRL(MX6_PAD_GPIO_4__GPIO1_IO04, GPIO_IN_PAD_CTRL), + /* WP */ + NEW_PAD_CTRL(MX6_PAD_GPIO_2__GPIO1_IO02, GPIO_IN_PAD_CTRL), +}; + +int tqma6_bb_board_mmc_init(bd_t *bis) +{ + imx_iomux_v3_setup_multiple_pads(mba6_usdhc2_pads, + ARRAY_SIZE(mba6_usdhc2_pads)); + gpio_direction_input(USDHC2_CD_GPIO); + gpio_direction_input(USDHC2_WP_GPIO); + + mba6_usdhc_cfg.sdhc_clk = mxc_get_clock(MXC_ESDHC2_CLK); + if (fsl_esdhc_initialize(bis, &mba6_usdhc_cfg)) + puts("Warning: failed to initialize SD\n"); + + return 0; +} + +static struct i2c_pads_info mba6_i2c1_pads = { +/* I2C1: MBa6x */ + .scl = { + .i2c_mode = NEW_PAD_CTRL(MX6_PAD_CSI0_DAT9__I2C1_SCL, + I2C_PAD_CTRL), + .gpio_mode = NEW_PAD_CTRL(MX6_PAD_CSI0_DAT9__GPIO5_IO27, + I2C_PAD_CTRL), + .gp = IMX_GPIO_NR(5, 27) + }, + .sda = { + .i2c_mode = NEW_PAD_CTRL(MX6_PAD_CSI0_DAT8__I2C1_SDA, + I2C_PAD_CTRL), + .gpio_mode = NEW_PAD_CTRL(MX6_PAD_CSI0_DAT8__GPIO5_IO26, + I2C_PAD_CTRL), + .gp = IMX_GPIO_NR(5, 26) + } +}; + +static void mba6_setup_i2c(void) +{ + /* use logical index for bus, e.g. I2C1 -> 0 */ + setup_i2c(0, CONFIG_SYS_I2C_SPEED, 0x7f, &mba6_i2c1_pads); +} + + +static iomux_v3_cfg_t const mba6_ecspi1_pads[] = { + NEW_PAD_CTRL(MX6_PAD_EIM_D24__GPIO3_IO24, SPI_PAD_CTRL), + NEW_PAD_CTRL(MX6_PAD_EIM_D25__GPIO3_IO25, SPI_PAD_CTRL), +}; + +static unsigned const mba6_ecspi1_cs[] = { + IMX_GPIO_NR(3, 24), + IMX_GPIO_NR(3, 25), +}; + +static void mba6_setup_iomuxc_spi(void) +{ + unsigned i; + + for (i = 0; i < ARRAY_SIZE(mba6_ecspi1_cs); ++i) + gpio_direction_output(mba6_ecspi1_cs[i], 1); + imx_iomux_v3_setup_multiple_pads(mba6_ecspi1_pads, + ARRAY_SIZE(mba6_ecspi1_pads)); +} + +int board_phy_config(struct phy_device *phydev) +{ +/* + * optimized pad skew values depends on CPU variant on the TQMa6x module: + * i.MX6Q/D or i.MX6DL/S + */ +#if defined(CONFIG_MX6Q) || defined(CONFIG_MX6Q) +#define MBA6X_KSZ9031_CTRL_SKEW 0x0032 +#define MBA6X_KSZ9031_CLK_SKEW 0x03ff +#define MBA6X_KSZ9031_RX_SKEW 0x3333 +#define MBA6X_KSZ9031_TX_SKEW 0x2036 +#elif defined(CONFIG_MX6DL) || defined(CONFIG_MX6S) +#define MBA6X_KSZ9031_CTRL_SKEW 0x0030 +#define MBA6X_KSZ9031_CLK_SKEW 0x03ff +#define MBA6X_KSZ9031_RX_SKEW 0x3333 +#define MBA6X_KSZ9031_TX_SKEW 0x2052 +#else +#error +#endif + /* min rx/tx ctrl delay */ + ksz9031_phy_extended_write(phydev, 2, + MII_KSZ9031_EXT_RGMII_CTRL_SIG_SKEW, + MII_KSZ9031_MOD_DATA_NO_POST_INC, + MBA6X_KSZ9031_CTRL_SKEW); + /* min rx delay */ + ksz9031_phy_extended_write(phydev, 2, + MII_KSZ9031_EXT_RGMII_RX_DATA_SKEW, + MII_KSZ9031_MOD_DATA_NO_POST_INC, + MBA6X_KSZ9031_RX_SKEW); + /* max tx delay */ + ksz9031_phy_extended_write(phydev, 2, + MII_KSZ9031_EXT_RGMII_TX_DATA_SKEW, + MII_KSZ9031_MOD_DATA_NO_POST_INC, + MBA6X_KSZ9031_TX_SKEW); + /* rx/tx clk skew */ + ksz9031_phy_extended_write(phydev, 2, + MII_KSZ9031_EXT_RGMII_CLOCK_SKEW, + MII_KSZ9031_MOD_DATA_NO_POST_INC, + MBA6X_KSZ9031_CLK_SKEW); + + phydev->drv->config(phydev); + + return 0; +} + +int board_eth_init(bd_t *bis) +{ + uint32_t base = IMX_FEC_BASE; + struct mii_dev *bus = NULL; + struct phy_device *phydev = NULL; + int ret; + + bus = fec_get_miibus(base, -1); + if (!bus) + return 0; + /* scan phy */ + phydev = phy_find_by_mask(bus, (0xf << CONFIG_FEC_MXC_PHYADDR), + PHY_INTERFACE_MODE_RGMII); + + if (!phydev) { + free(bus); + puts("No phy found\n"); + return 0; + } + ret = fec_probe(bis, -1, base, bus, phydev); + if (ret) { + puts("FEC MXC: probe failed\n"); + free(phydev); + free(bus); + } + + return 0; +} + +int tqma6_bb_board_early_init_f(void) +{ + mba6_setup_iomuxc_uart(); + + return 0; +} + +int tqma6_bb_board_init(void) +{ + mba6_setup_i2c(); + mba6_setup_iomuxc_spi(); + /* do it here - to have reset completed */ + mba6_setup_iomuxc_enet(); + + return 0; +} + +int tqma6_bb_board_late_init(void) +{ + return 0; +} + +const char *tqma6_bb_get_boardname(void) +{ + return "MBa6x"; +} + +/* + * Device Tree Support + */ +#if defined(CONFIG_OF_BOARD_SETUP) && defined(CONFIG_OF_LIBFDT) +void tqma6_bb_ft_board_setup(void *blob, bd_t *bd) +{ + /* TBD */ +} +#endif /* defined(CONFIG_OF_BOARD_SETUP) && defined(CONFIG_OF_LIBFDT) */ diff --git a/board/tqc/tqma6/tqma6q.cfg b/board/tqc/tqma6/tqma6q.cfg new file mode 100644 index 0000000..f54dff7 --- /dev/null +++ b/board/tqc/tqma6/tqma6q.cfg @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2013, 2014 Markus Niebel <Markus.Niebel@tq-group.com> + * + * SPDX-License-Identifier: GPL-2.0+ + * + * Refer doc/README.imximage for more details about how-to configure + * and create imximage boot image + * + * The syntax is taken as close as possible with the kwbimage + */ + +/* image version */ +IMAGE_VERSION 2 + +#define __ASSEMBLY__ +#include <config.h> + +/* + * Boot Device : one of + * spi, sd (the board has no nand neither onenand) + */ +#if defined(CONFIG_TQMA6X_MMC_BOOT) +BOOT_FROM sd +#elif defined(CONFIG_TQMA6X_SPI_BOOT) +BOOT_FROM spi +#endif + +#include "asm/arch/mx6-ddr.h" +#include "asm/arch/iomux.h" +#include "asm/arch/crm_regs.h" + +/* TQMa6Q/D DDR config Rev. 0100B */ +/* IOMUX configuration */ +DATA 4, MX6_IOM_GRP_DDR_TYPE, 0x000C0000 +DATA 4, MX6_IOM_GRP_DDRPKE, 0x00000000 +DATA 4, MX6_IOM_DRAM_SDCLK_0, 0x00008030 +DATA 4, MX6_IOM_DRAM_SDCLK_1, 0x00008030 +DATA 4, MX6_IOM_DRAM_CAS, 0x00008030 +DATA 4, MX6_IOM_DRAM_RAS, 0x00008030 +DATA 4, MX6_IOM_GRP_ADDDS, 0x00000030 +DATA 4, MX6_IOM_DRAM_RESET, 0x000C3030 +DATA 4, MX6_IOM_DRAM_SDCKE0, 0x00003000 +DATA 4, MX6_IOM_DRAM_SDCKE1, 0x00000000 +DATA 4, MX6_IOM_DRAM_SDBA2, 0x00000000 +DATA 4, MX6_IOM_DRAM_SDODT0, 0x00003030 +DATA 4, MX6_IOM_DRAM_SDODT1, 0x00003030 +DATA 4, MX6_IOM_GRP_CTLDS, 0x00000030 +DATA 4, MX6_IOM_DDRMODE_CTL, 0x00020000 +DATA 4, MX6_IOM_DRAM_SDQS0, 0x00000030 +DATA 4, MX6_IOM_DRAM_SDQS1, 0x00000030 +DATA 4, MX6_IOM_DRAM_SDQS2, 0x00000030 +DATA 4, MX6_IOM_DRAM_SDQS3, 0x00000030 +DATA 4, MX6_IOM_DRAM_SDQS4, 0x00000030 +DATA 4, MX6_IOM_DRAM_SDQS5, 0x00000030 +DATA 4, MX6_IOM_DRAM_SDQS6, 0x00000030 +DATA 4, MX6_IOM_DRAM_SDQS7, 0x00000030 +DATA 4, MX6_IOM_GRP_DDRMODE, 0x00020000 +DATA 4, MX6_IOM_GRP_B0DS, 0x00000030 +DATA 4, MX6_IOM_GRP_B1DS, 0x00000030 +DATA 4, MX6_IOM_GRP_B2DS, 0x00000030 +DATA 4, MX6_IOM_GRP_B3DS, 0x00000030 +DATA 4, MX6_IOM_GRP_B4DS, 0x00000030 +DATA 4, MX6_IOM_GRP_B5DS, 0x00000030 +DATA 4, MX6_IOM_GRP_B6DS, 0x00000030 +DATA 4, MX6_IOM_GRP_B7DS, 0x00000030 +DATA 4, MX6_IOM_DRAM_DQM0, 0x00000030 +DATA 4, MX6_IOM_DRAM_DQM1, 0x00000030 +DATA 4, MX6_IOM_DRAM_DQM2, 0x00000030 +DATA 4, MX6_IOM_DRAM_DQM3, 0x00000030 +DATA 4, MX6_IOM_DRAM_DQM4, 0x00000030 +DATA 4, MX6_IOM_DRAM_DQM5, 0x00000030 +DATA 4, MX6_IOM_DRAM_DQM6, 0x00000030 +DATA 4, MX6_IOM_DRAM_DQM7, 0x00000030 + +/* memory interface calibration values */ +DATA 4, MX6_MMDC_P0_MPZQHWCTRL, 0xA1390003 +DATA 4, MX6_MMDC_P1_MPZQHWCTRL, 0xA1390003 +DATA 4, MX6_MMDC_P0_MPWLDECTRL0, 0x001B0013 +DATA 4, MX6_MMDC_P0_MPWLDECTRL1, 0x0018001B +DATA 4, MX6_MMDC_P1_MPWLDECTRL0, 0x001B0016 +DATA 4, MX6_MMDC_P1_MPWLDECTRL1, 0x0012001C +DATA 4, MX6_MMDC_P0_MPDGCTRL0, 0x43400350 +DATA 4, MX6_MMDC_P0_MPDGCTRL1, 0x023E032C +DATA 4, MX6_MMDC_P1_MPDGCTRL0, 0x43400348 +DATA 4, MX6_MMDC_P1_MPDGCTRL1, 0x03300304 +DATA 4, MX6_MMDC_P0_MPRDDLCTL, 0x3C323436 +DATA 4, MX6_MMDC_P1_MPRDDLCTL, 0x38383242 +DATA 4, MX6_MMDC_P0_MPWRDLCTL, 0x3E3C4440 +DATA 4, MX6_MMDC_P1_MPWRDLCTL, 0x4236483E +DATA 4, MX6_MMDC_P0_MPRDDQBY0DL, 0x33333333 +DATA 4, MX6_MMDC_P0_MPRDDQBY1DL, 0x33333333 +DATA 4, MX6_MMDC_P0_MPRDDQBY2DL, 0x33333333 +DATA 4, MX6_MMDC_P0_MPRDDQBY3DL, 0x33333333 +DATA 4, MX6_MMDC_P1_MPRDDQBY0DL, 0x33333333 +DATA 4, MX6_MMDC_P1_MPRDDQBY1DL, 0x33333333 +DATA 4, MX6_MMDC_P1_MPRDDQBY2DL, 0x33333333 +DATA 4, MX6_MMDC_P1_MPRDDQBY3DL, 0x33333333 +DATA 4, MX6_MMDC_P0_MPMUR0, 0x00000800 +DATA 4, MX6_MMDC_P1_MPMUR0, 0x00000800 + +/* configure memory interface */ +DATA 4, MX6_MMDC_P0_MDPDC, 0x00020036 +DATA 4, MX6_MMDC_P0_MDOTC, 0x09444040 +DATA 4, MX6_MMDC_P0_MDCFG0, 0x545A79B4 +DATA 4, MX6_MMDC_P0_MDCFG1, 0xDB538F64 +DATA 4, MX6_MMDC_P0_MDCFG2, 0x01FF00DB +DATA 4, MX6_MMDC_P0_MDMISC, 0x00001740 +DATA 4, MX6_MMDC_P0_MDSCR, 0x00008000 +DATA 4, MX6_MMDC_P0_MDRWD, 0x000026D2 +DATA 4, MX6_MMDC_P0_MDOR, 0x005A1023 +DATA 4, MX6_MMDC_P0_MDASP, 0x00000027 +DATA 4, MX6_MMDC_P0_MDCTL, 0x831A0000 +DATA 4, MX6_MMDC_P0_MDSCR, 0x00088032 +DATA 4, MX6_MMDC_P0_MDSCR, 0x00008033 +DATA 4, MX6_MMDC_P0_MDSCR, 0x00048031 +DATA 4, MX6_MMDC_P0_MDSCR, 0x09308030 +DATA 4, MX6_MMDC_P0_MDSCR, 0x04008040 +DATA 4, MX6_MMDC_P0_MDREF, 0x00005800 +DATA 4, MX6_MMDC_P0_MPODTCTRL, 0x00022222 +DATA 4, MX6_MMDC_P1_MPODTCTRL, 0x00022222 +DATA 4, MX6_MMDC_P0_MDPDC, 0x00025536 +DATA 4, MX6_MMDC_P0_MAPSR, 0x00001006 +DATA 4, MX6_MMDC_P0_MDSCR, 0x00000000 + +#include "clocks.cfg" diff --git a/board/tqc/tqma6/tqma6s.cfg b/board/tqc/tqma6/tqma6s.cfg new file mode 100644 index 0000000..24d4e2f --- /dev/null +++ b/board/tqc/tqma6/tqma6s.cfg @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2013, 2014 Markus Niebel <Markus.Niebel@tq-group.com> + * + * SPDX-License-Identifier: GPL-2.0+ + * + * Refer doc/README.imximage for more details about how-to configure + * and create imximage boot image + * + * The syntax is taken as close as possible with the kwbimage + */ + +/* image version */ +IMAGE_VERSION 2 + +#define __ASSEMBLY__ +#include <config.h> + +/* + * Boot Device : one of + * spi, sd (the board has no nand neither onenand) + */ +#if defined(CONFIG_TQMA6X_MMC_BOOT) +BOOT_FROM sd +#elif defined(CONFIG_TQMA6X_SPI_BOOT) +BOOT_FROM spi +#endif + +#include "asm/arch/mx6-ddr.h" +#include "asm/arch/iomux.h" +#include "asm/arch/crm_regs.h" + +/* TQMa6S DDR config Rev. 0100B */ +/* IOMUX configuration */ +DATA 4, MX6_IOM_GRP_DDR_TYPE, 0x000C0000 +DATA 4, MX6_IOM_GRP_DDRPKE, 0x00000000 +DATA 4, MX6_IOM_DRAM_SDCLK_0, 0x00008000 +DATA 4, MX6_IOM_DRAM_SDCLK_1, 0x00008030 +DATA 4, MX6_IOM_DRAM_CAS, 0x00008030 +DATA 4, MX6_IOM_DRAM_RAS, 0x00008030 +DATA 4, MX6_IOM_GRP_ADDDS, 0x00000030 +DATA 4, MX6_IOM_DRAM_RESET, 0x000C3030 +DATA 4, MX6_IOM_DRAM_SDCKE0, 0x00003000 +DATA 4, MX6_IOM_DRAM_SDCKE1, 0x00000000 +DATA 4, MX6_IOM_DRAM_SDBA2, 0x00000000 +DATA 4, MX6_IOM_DRAM_SDODT0, 0x00003030 +DATA 4, MX6_IOM_DRAM_SDODT1, 0x00003030 +DATA 4, MX6_IOM_GRP_CTLDS, 0x00000030 +DATA 4, MX6_IOM_DDRMODE_CTL, 0x00020000 +DATA 4, MX6_IOM_DRAM_SDQS0, 0x00000030 +DATA 4, MX6_IOM_DRAM_SDQS1, 0x00000030 +DATA 4, MX6_IOM_DRAM_SDQS2, 0x00000030 +DATA 4, MX6_IOM_DRAM_SDQS3, 0x00000030 +DATA 4, MX6_IOM_DRAM_SDQS4, 0x00000000 +DATA 4, MX6_IOM_DRAM_SDQS5, 0x00000000 +DATA 4, MX6_IOM_DRAM_SDQS6, 0x00000000 +DATA 4, MX6_IOM_DRAM_SDQS7, 0x00000000 +DATA 4, MX6_IOM_GRP_DDRMODE, 0x00020000 +DATA 4, MX6_IOM_GRP_B0DS, 0x00000030 +DATA 4, MX6_IOM_GRP_B1DS, 0x00000030 +DATA 4, MX6_IOM_GRP_B2DS, 0x00000030 +DATA 4, MX6_IOM_GRP_B3DS, 0x00000030 +DATA 4, MX6_IOM_GRP_B4DS, 0x00000000 +DATA 4, MX6_IOM_GRP_B5DS, 0x00000000 +DATA 4, MX6_IOM_GRP_B6DS, 0x00000000 +DATA 4, MX6_IOM_GRP_B7DS, 0x00000000 +DATA 4, MX6_IOM_DRAM_DQM0, 0x00000030 +DATA 4, MX6_IOM_DRAM_DQM1, 0x00000030 +DATA 4, MX6_IOM_DRAM_DQM2, 0x00000030 +DATA 4, MX6_IOM_DRAM_DQM3, 0x00000030 +DATA 4, MX6_IOM_DRAM_DQM4, 0x00000000 +DATA 4, MX6_IOM_DRAM_DQM5, 0x00000000 +DATA 4, MX6_IOM_DRAM_DQM6, 0x00000000 +DATA 4, MX6_IOM_DRAM_DQM7, 0x00000000 + +/* memory interface calibration values */ +DATA 4, MX6_MMDC_P0_MPZQHWCTRL, 0xA1390003 +DATA 4, MX6_MMDC_P1_MPZQHWCTRL, 0xA1380000 +DATA 4, MX6_MMDC_P0_MPWLDECTRL0, 0x0014000E +DATA 4, MX6_MMDC_P0_MPWLDECTRL1, 0x00120014 +DATA 4, MX6_MMDC_P1_MPWLDECTRL0, 0x00000000 +DATA 4, MX6_MMDC_P1_MPWLDECTRL1, 0x00000000 +DATA 4, MX6_MMDC_P0_MPDGCTRL0, 0x0240023C +DATA 4, MX6_MMDC_P0_MPDGCTRL1, 0x0228022C +DATA 4, MX6_MMDC_P1_MPDGCTRL0, 0x00000000 +DATA 4, MX6_MMDC_P1_MPDGCTRL1, 0x00000000 +DATA 4, MX6_MMDC_P0_MPRDDLCTL, 0x4A4A4E4A +DATA 4, MX6_MMDC_P1_MPRDDLCTL, 0x00000000 +DATA 4, MX6_MMDC_P0_MPWRDLCTL, 0x36362A32 +DATA 4, MX6_MMDC_P1_MPWRDLCTL, 0x00000000 +DATA 4, MX6_MMDC_P0_MPRDDQBY0DL, 0x33333333 +DATA 4, MX6_MMDC_P0_MPRDDQBY1DL, 0x33333333 +DATA 4, MX6_MMDC_P0_MPRDDQBY2DL, 0x33333333 +DATA 4, MX6_MMDC_P0_MPRDDQBY3DL, 0x33333333 +DATA 4, MX6_MMDC_P1_MPRDDQBY0DL, 0x00000000 +DATA 4, MX6_MMDC_P1_MPRDDQBY1DL, 0x00000000 +DATA 4, MX6_MMDC_P1_MPRDDQBY2DL, 0x00000000 +DATA 4, MX6_MMDC_P1_MPRDDQBY3DL, 0x00000000 +DATA 4, MX6_MMDC_P0_MPMUR0, 0x00000800 +DATA 4, MX6_MMDC_P1_MPMUR0, 0x00000000 + +/* configure memory interface */ +DATA 4, MX6_MMDC_P0_MDPDC, 0x0002002D +DATA 4, MX6_MMDC_P0_MDOTC, 0x00333030 +DATA 4, MX6_MMDC_P0_MDCFG0, 0x3F435333 +DATA 4, MX6_MMDC_P0_MDCFG1, 0xB68E8B63 +DATA 4, MX6_MMDC_P0_MDCFG2, 0x01FF00DB +DATA 4, MX6_MMDC_P0_MDMISC, 0x00001740 +DATA 4, MX6_MMDC_P0_MDSCR, 0x00008000 +DATA 4, MX6_MMDC_P0_MDRWD, 0x000026D2 +DATA 4, MX6_MMDC_P0_MDOR, 0x00431023 +DATA 4, MX6_MMDC_P0_MDASP, 0x00000017 +DATA 4, MX6_MMDC_P0_MDCTL, 0x83190000 +DATA 4, MX6_MMDC_P0_MDSCR, 0x00008032 +DATA 4, MX6_MMDC_P0_MDSCR, 0x00008033 +DATA 4, MX6_MMDC_P0_MDSCR, 0x00048031 +DATA 4, MX6_MMDC_P0_MDSCR, 0x05208030 +DATA 4, MX6_MMDC_P0_MDSCR, 0x04008040 +DATA 4, MX6_MMDC_P0_MDREF, 0x00005800 +DATA 4, MX6_MMDC_P0_MPODTCTRL, 0x00022222 +DATA 4, MX6_MMDC_P1_MPODTCTRL, 0x00000000 +DATA 4, MX6_MMDC_P0_MDPDC, 0x0002552D +DATA 4, MX6_MMDC_P0_MAPSR, 0x00001006 +DATA 4, MX6_MMDC_P0_MDSCR, 0x00000000 + +#include "clocks.cfg" diff --git a/common/board_f.c b/common/board_f.c index 6203d85..4ece2b6 100644 --- a/common/board_f.c +++ b/common/board_f.c @@ -34,6 +34,9 @@ #ifdef CONFIG_MPC5xxx #include <mpc5xxx.h> #endif +#if (defined(CONFIG_MPC86xx) || defined(CONFIG_E500)) +#include <asm/mp.h> +#endif #include <os.h> #include <post.h> @@ -43,9 +46,6 @@ #include <watchdog.h> #include <asm/errno.h> #include <asm/io.h> -#ifdef CONFIG_MP -#include <asm/mp.h> -#endif #include <asm/sections.h> #ifdef CONFIG_X86 #include <asm/init_helpers.h> @@ -106,9 +106,14 @@ __weak void blue_led_off(void) {} * Could the CONFIG_SPL_BUILD infection become a flag in gd? */ -#if defined(CONFIG_WATCHDOG) +#if defined(CONFIG_WATCHDOG) || defined(CONFIG_HW_WATCHDOG) static int init_func_watchdog_init(void) { +# if defined(CONFIG_HW_WATCHDOG) && (defined(CONFIG_BLACKFIN) || \ + defined(CONFIG_M68K) || defined(CONFIG_MICROBLAZE) || \ + defined(CONFIG_SH)) + hw_watchdog_init(); +# endif puts(" Watchdog enabled\n"); WATCHDOG_RESET(); @@ -146,7 +151,11 @@ static int display_text_info(void) bss_end = (ulong)&__bss_end; debug("U-Boot code: %08X -> %08lX BSS: -> %08lX\n", +#ifdef CONFIG_SYS_TEXT_BASE CONFIG_SYS_TEXT_BASE, bss_start, bss_end); +#else + CONFIG_SYS_MONITOR_BASE, bss_start, bss_end); +#endif #endif #ifdef CONFIG_MODEM_SUPPORT @@ -261,6 +270,8 @@ static int setup_mon_len(void) gd->mon_len = (ulong)&__bss_end - (ulong)_start; #elif defined(CONFIG_SANDBOX) gd->mon_len = (ulong)&_end - (ulong)_init; +#elif defined(CONFIG_BLACKFIN) || defined(CONFIG_NIOS2) + gd->mon_len = CONFIG_SYS_MONITOR_LEN; #else /* TODO: use (ulong)&__bss_end - (ulong)&__text_start; ? */ gd->mon_len = (ulong)&__bss_end - CONFIG_SYS_MONITOR_BASE; @@ -381,7 +392,7 @@ static int setup_dest_addr(void) gd->ram_top = board_get_usable_ram_top(gd->mon_len); gd->relocaddr = gd->ram_top; debug("Ram top: %08lX\n", (ulong)gd->ram_top); -#if defined(CONFIG_MP) && (defined(CONFIG_MPC86xx) || defined(CONFIG_E500)) +#if (defined(CONFIG_MPC86xx) || defined(CONFIG_E500)) /* * We need to make sure the location we intend to put secondary core * boot code is reserved and not used by any part of u-boot @@ -470,8 +481,9 @@ static int reserve_trace(void) return 0; } -#if defined(CONFIG_VIDEO) && (!defined(CONFIG_PPC) || defined(CONFIG_8xx)) \ - && !defined(CONFIG_ARM) && !defined(CONFIG_X86) +#if defined(CONFIG_VIDEO) && (!defined(CONFIG_PPC) || defined(CONFIG_8xx)) && \ + !defined(CONFIG_ARM) && !defined(CONFIG_X86) && \ + !defined(CONFIG_BLACKFIN) static int reserve_video(void) { /* reserve memory for video display (always full pages) */ @@ -516,11 +528,13 @@ static int reserve_malloc(void) /* (permanently) allocate a Board Info struct */ static int reserve_board(void) { - gd->start_addr_sp -= sizeof(bd_t); - gd->bd = (bd_t *)map_sysmem(gd->start_addr_sp, sizeof(bd_t)); - memset(gd->bd, '\0', sizeof(bd_t)); - debug("Reserving %zu Bytes for Board Info at: %08lx\n", - sizeof(bd_t), gd->start_addr_sp); + if (!gd->bd) { + gd->start_addr_sp -= sizeof(bd_t); + gd->bd = (bd_t *)map_sysmem(gd->start_addr_sp, sizeof(bd_t)); + memset(gd->bd, '\0', sizeof(bd_t)); + debug("Reserving %zu Bytes for Board Info at: %08lx\n", + sizeof(bd_t), gd->start_addr_sp); + } return 0; } #endif @@ -721,7 +735,9 @@ static int reloc_fdt(void) static int setup_reloc(void) { +#ifdef CONFIG_SYS_TEXT_BASE gd->reloc_off = gd->relocaddr - CONFIG_SYS_TEXT_BASE; +#endif memcpy(gd->new_gd, (char *)gd, sizeof(gd_t)); debug("Relocation Offset is: %08lx\n", gd->reloc_off); @@ -828,7 +844,7 @@ static init_fnc_t init_sequence_f[] = { /* TODO: can we rename this to timer_init()? */ init_timebase, #endif -#if defined(CONFIG_ARM) || defined(CONFIG_MIPS) +#if defined(CONFIG_ARM) || defined(CONFIG_MIPS) || defined(CONFIG_BLACKFIN) timer_init, /* initialize timer */ #endif #ifdef CONFIG_SYS_ALLOC_DPRAM @@ -929,6 +945,10 @@ static init_fnc_t init_sequence_f[] = { * - board info struct */ setup_dest_addr, +#if defined(CONFIG_BLACKFIN) || defined(CONFIG_NIOS2) + /* Blackfin u-boot monitor should be on top of the ram */ + reserve_uboot, +#endif #if defined(CONFIG_LOGBUFFER) && !defined(CONFIG_ALT_LB_ADDR) reserve_logbuffer, #endif @@ -945,11 +965,14 @@ static init_fnc_t init_sequence_f[] = { #endif reserve_trace, /* TODO: Why the dependency on CONFIG_8xx? */ -#if defined(CONFIG_VIDEO) && (!defined(CONFIG_PPC) || defined(CONFIG_8xx)) \ - && !defined(CONFIG_ARM) && !defined(CONFIG_X86) +#if defined(CONFIG_VIDEO) && (!defined(CONFIG_PPC) || defined(CONFIG_8xx)) && \ + !defined(CONFIG_ARM) && !defined(CONFIG_X86) && \ + !defined(CONFIG_BLACKFIN) reserve_video, #endif +#if !defined(CONFIG_BLACKFIN) && !defined(CONFIG_NIOS2) reserve_uboot, +#endif #ifndef CONFIG_SPL_BUILD reserve_malloc, reserve_board, diff --git a/common/board_r.c b/common/board_r.c index 8e7a3ac..f9647e1 100644 --- a/common/board_r.c +++ b/common/board_r.c @@ -133,7 +133,7 @@ static int initr_reloc_global_data(void) { #ifdef __ARM__ monitor_flash_len = _end - __image_copy_start; -#elif !defined(CONFIG_SANDBOX) +#elif !defined(CONFIG_SANDBOX) && !defined(CONFIG_NIOS2) monitor_flash_len = (ulong)&__init_end - gd->relocaddr; #endif #if defined(CONFIG_MPC85xx) || defined(CONFIG_MPC86xx) @@ -587,6 +587,7 @@ static int initr_doc(void) { puts("DOC: "); doc_init(); + return 0; } #endif diff --git a/common/bootm.c b/common/bootm.c index 7ec2ed8..ff81a27 100644 --- a/common/bootm.c +++ b/common/bootm.c @@ -725,32 +725,15 @@ static const void *boot_get_kernel(cmd_tbl_t *cmdtp, int flag, int argc, #endif ulong img_addr; const void *buf; -#if defined(CONFIG_FIT) const char *fit_uname_config = NULL; const char *fit_uname_kernel = NULL; +#if defined(CONFIG_FIT) int os_noffset; #endif - /* find out kernel image address */ - if (argc < 1) { - img_addr = load_addr; - debug("* kernel: default image load address = 0x%08lx\n", - load_addr); -#if defined(CONFIG_FIT) - } else if (fit_parse_conf(argv[0], load_addr, &img_addr, - &fit_uname_config)) { - debug("* kernel: config '%s' from image at 0x%08lx\n", - fit_uname_config, img_addr); - } else if (fit_parse_subimage(argv[0], load_addr, &img_addr, - &fit_uname_kernel)) { - debug("* kernel: subimage '%s' from image at 0x%08lx\n", - fit_uname_kernel, img_addr); -#endif - } else { - img_addr = simple_strtoul(argv[0], NULL, 16); - debug("* kernel: cmdline image address = 0x%08lx\n", - img_addr); - } + img_addr = genimg_get_kernel_addr_fit(argc < 1 ? NULL : argv[0], + &fit_uname_config, + &fit_uname_kernel); bootstage_mark(BOOTSTAGE_ID_CHECK_MAGIC); diff --git a/common/cli_simple.c b/common/cli_simple.c index 353ceeb..6c65cc6 100644 --- a/common/cli_simple.c +++ b/common/cli_simple.c @@ -57,7 +57,7 @@ int cli_simple_parse_line(char *line, char *argv[]) return nargs; } -static void process_macros(const char *input, char *output) +void cli_simple_process_macros(const char *input, char *output) { char c, prev; const char *varname_start = NULL; @@ -236,7 +236,7 @@ int cli_simple_run_command(const char *cmd, int flag) debug_parser("token: \"%s\"\n", token); /* find macros in this token and replace them */ - process_macros(token, finaltoken); + cli_simple_process_macros(token, finaltoken); /* Extract arguments */ argc = cli_simple_parse_line(finaltoken, argv); diff --git a/common/cmd_dfu.c b/common/cmd_dfu.c index 433bddd..2633b30 100644 --- a/common/cmd_dfu.c +++ b/common/cmd_dfu.c @@ -24,8 +24,7 @@ static int do_dfu(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) int ret, i = 0; - ret = dfu_init_env_entities(interface, simple_strtoul(devstring, - NULL, 10)); + ret = dfu_init_env_entities(interface, devstring); if (ret) goto done; diff --git a/common/cmd_ext4.c b/common/cmd_ext4.c index 68b047b..ecfc6d3 100644 --- a/common/cmd_ext4.c +++ b/common/cmd_ext4.c @@ -42,6 +42,12 @@ #include <usb.h> #endif +int do_ext4_size(cmd_tbl_t *cmdtp, int flag, int argc, + char *const argv[]) +{ + return do_size(cmdtp, flag, argc, argv, FS_TYPE_EXT); +} + int do_ext4_load(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) { @@ -113,6 +119,14 @@ U_BOOT_CMD(ext4write, 6, 1, do_ext4_write, #endif +U_BOOT_CMD( + ext4size, 4, 0, do_ext4_size, + "determine a file's size", + "<interface> <dev[:part]> <filename>\n" + " - Find file 'filename' from 'dev' on 'interface'\n" + " and determine its size." +); + U_BOOT_CMD(ext4ls, 4, 1, do_ext4_ls, "list files in a directory (default /)", "<interface> <dev[:part]> [directory]\n" @@ -120,6 +134,6 @@ U_BOOT_CMD(ext4ls, 4, 1, do_ext4_ls, U_BOOT_CMD(ext4load, 6, 0, do_ext4_load, "load binary file from a Ext4 filesystem", - "<interface> <dev[:part]> [addr] [filename] [bytes]\n" + "<interface> [<dev[:part]> [addr [filename [bytes [pos]]]]]\n" " - load binary file 'filename' from 'dev' on 'interface'\n" " to address 'addr' from ext4 filesystem"); diff --git a/common/cmd_fat.c b/common/cmd_fat.c index a478017..633fbf1 100644 --- a/common/cmd_fat.c +++ b/common/cmd_fat.c @@ -18,6 +18,19 @@ #include <fat.h> #include <fs.h> +int do_fat_size(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + return do_size(cmdtp, flag, argc, argv, FS_TYPE_FAT); +} + +U_BOOT_CMD( + fatsize, 4, 0, do_fat_size, + "determine a file's size", + "<interface> <dev[:part]> <filename>\n" + " - Find file 'filename' from 'dev' on 'interface'\n" + " and determine its size." +); + int do_fat_fsload (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) { return do_load(cmdtp, flag, argc, argv, FS_TYPE_FAT); diff --git a/common/cmd_fdt.c b/common/cmd_fdt.c index e86d992..5640ded 100644 --- a/common/cmd_fdt.c +++ b/common/cmd_fdt.c @@ -621,7 +621,7 @@ static int do_fdt(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) } /* resize the fdt */ else if (strncmp(argv[1], "re", 2) == 0) { - fdt_resize(working_fdt); + fdt_shrink_to_minimum(working_fdt); } else { /* Unrecognized command */ diff --git a/common/cmd_fs.c b/common/cmd_fs.c index 78590d2..6754340 100644 --- a/common/cmd_fs.c +++ b/common/cmd_fs.c @@ -20,6 +20,19 @@ #include <command.h> #include <fs.h> +static int do_size_wrapper(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + return do_size(cmdtp, flag, argc, argv, FS_TYPE_ANY); +} + +U_BOOT_CMD( + size, 4, 0, do_size_wrapper, + "determine a file's size", + "<interface> <dev[:part]> <filename>\n" + " - Find file 'filename' from 'dev' on 'interface'\n" + " and determine its size." +); + static int do_load_wrapper(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) { diff --git a/common/cmd_mtdparts.c b/common/cmd_mtdparts.c index 40b6333..3cb0571 100644 --- a/common/cmd_mtdparts.c +++ b/common/cmd_mtdparts.c @@ -292,6 +292,7 @@ static int get_mtd_info(u8 type, u8 num, struct mtd_info **mtd) printf("Device %s not found!\n", mtd_dev); return 1; } + put_mtd_device(*mtd); return 0; } diff --git a/common/cmd_pxe.c b/common/cmd_pxe.c index ba48692..0ab1e0a 100644 --- a/common/cmd_pxe.c +++ b/common/cmd_pxe.c @@ -1,5 +1,6 @@ /* * Copyright 2010-2011 Calxeda, Inc. + * Copyright (c) 2014, NVIDIA CORPORATION. All rights reserved. * * SPDX-License-Identifier: GPL-2.0+ */ @@ -14,6 +15,7 @@ #include <fs.h> #include "menu.h" +#include "cli.h" #define MAX_TFTP_PATH_LEN 127 @@ -577,8 +579,12 @@ static int label_localboot(struct pxe_label *label) if (!localcmd) return -ENOENT; - if (label->append) - setenv("bootargs", label->append); + if (label->append) { + char bootargs[CONFIG_SYS_CBSIZE]; + + cli_simple_process_macros(label->append, bootargs); + setenv("bootargs", bootargs); + } debug("running: %s\n", localcmd); @@ -606,9 +612,10 @@ static int label_boot(cmd_tbl_t *cmdtp, struct pxe_label *label) char initrd_str[22]; char mac_str[29] = ""; char ip_str[68] = ""; - char *bootargs; int bootm_argc = 3; int len = 0; + ulong kernel_addr; + void *buf; label_print(label); @@ -651,7 +658,6 @@ static int label_boot(cmd_tbl_t *cmdtp, struct pxe_label *label) sprintf(ip_str, " ip=%s:%s:%s:%s", getenv("ipaddr"), getenv("serverip"), getenv("gatewayip"), getenv("netmask")); - len += strlen(ip_str); } #ifdef CONFIG_CMD_NET @@ -661,27 +667,21 @@ static int label_boot(cmd_tbl_t *cmdtp, struct pxe_label *label) err = format_mac_pxe(mac_str + 8, sizeof(mac_str) - 8); if (err < 0) mac_str[0] = '\0'; - len += strlen(mac_str); } #endif - if (label->append) - len += strlen(label->append); + if ((label->ipappend & 0x3) || label->append) { + char bootargs[CONFIG_SYS_CBSIZE] = ""; + char finalbootargs[CONFIG_SYS_CBSIZE]; - if (len) { - bootargs = malloc(len + 1); - if (!bootargs) - return 1; - bootargs[0] = '\0'; if (label->append) strcpy(bootargs, label->append); strcat(bootargs, ip_str); strcat(bootargs, mac_str); - setenv("bootargs", bootargs); - printf("append: %s\n", bootargs); - - free(bootargs); + cli_simple_process_macros(bootargs, finalbootargs); + setenv("bootargs", finalbootargs); + printf("append: %s\n", finalbootargs); } bootm_argv[1] = getenv("kernel_addr_r"); @@ -771,11 +771,15 @@ static int label_boot(cmd_tbl_t *cmdtp, struct pxe_label *label) if (bootm_argv[3]) bootm_argc = 4; - do_bootm(cmdtp, 0, bootm_argc, bootm_argv); - + kernel_addr = genimg_get_kernel_addr(bootm_argv[1]); + buf = map_sysmem(kernel_addr, 0); + /* Try bootm for legacy and FIT format image */ + if (genimg_get_format(buf) != IMAGE_FORMAT_INVALID) + do_bootm(cmdtp, 0, bootm_argc, bootm_argv); #ifdef CONFIG_CMD_BOOTZ - /* Try booting a zImage if do_bootm returns */ - do_bootz(cmdtp, 0, bootm_argc, bootm_argv); + /* Try booting a zImage */ + else + do_bootz(cmdtp, 0, bootm_argc, bootm_argv); #endif return 1; } @@ -1554,6 +1558,8 @@ do_pxe_boot(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) destroy_pxe_menu(cfg); + copy_filename(BootFile, "", sizeof(BootFile)); + return 0; } diff --git a/common/cmd_thordown.c b/common/cmd_thordown.c index 2dd7509..8ed1dc6 100644 --- a/common/cmd_thordown.c +++ b/common/cmd_thordown.c @@ -26,10 +26,9 @@ int do_thor_down(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) puts("TIZEN \"THOR\" Downloader\n"); - ret = dfu_init_env_entities(interface, simple_strtoul(devstring, - NULL, 10)); + ret = dfu_init_env_entities(interface, devstring); if (ret) - return ret; + goto done; int controller_index = simple_strtoul(usb_controller, NULL, 0); ret = board_usb_init(controller_index, USB_INIT_DEVICE); @@ -57,6 +56,7 @@ int do_thor_down(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) exit: g_dnl_unregister(); +done: dfu_free_entities(); return ret; diff --git a/common/cmd_ubi.c b/common/cmd_ubi.c index 3c37c93..6c85703 100644 --- a/common/cmd_ubi.c +++ b/common/cmd_ubi.c @@ -19,6 +19,7 @@ #include <onenand_uboot.h> #include <linux/mtd/mtd.h> #include <linux/mtd/partitions.h> +#include <linux/err.h> #include <ubi_uboot.h> #include <asm/errno.h> #include <jffs2/load_kernel.h> @@ -50,33 +51,6 @@ int ubifs_is_mounted(void); void cmd_ubifs_umount(void); #endif -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; diff --git a/common/cmd_ubifs.c b/common/cmd_ubifs.c index 19c8a43..8e9a4e5 100644 --- a/common/cmd_ubifs.c +++ b/common/cmd_ubifs.c @@ -38,7 +38,7 @@ static int do_ubifs_mount(cmd_tbl_t *cmdtp, int flag, int argc, ubifs_initialized = 1; } - ret = ubifs_mount(vol_name); + ret = uboot_ubifs_mount(vol_name); if (ret) return -1; diff --git a/common/env_fat.c b/common/env_fat.c index 328c09d..8db0160 100644 --- a/common/env_fat.c +++ b/common/env_fat.c @@ -73,7 +73,7 @@ int saveenv(void) void env_relocate_spec(void) { - char buf[CONFIG_ENV_SIZE]; + ALLOC_CACHE_ALIGN_BUFFER(char, buf, CONFIG_ENV_SIZE); block_dev_desc_t *dev_desc = NULL; disk_partition_t info; int dev, part; @@ -92,7 +92,7 @@ void env_relocate_spec(void) goto err_env_relocate; } - err = file_fat_read(FAT_ENV_FILE, (uchar *)&buf, CONFIG_ENV_SIZE); + err = file_fat_read(FAT_ENV_FILE, buf, CONFIG_ENV_SIZE); if (err == -1) { printf("\n** Unable to read \"%s\" from %s%d:%d **\n", FAT_ENV_FILE, FAT_ENV_INTERFACE, dev, part); diff --git a/common/fdt_support.c b/common/fdt_support.c index 7927a83..784a570 100644 --- a/common/fdt_support.c +++ b/common/fdt_support.c @@ -508,7 +508,7 @@ void fdt_fixup_ethernet(void *fdt) } /* Resize the fdt to its actual size + a bit of padding */ -int fdt_resize(void *blob) +int fdt_shrink_to_minimum(void *blob) { int i; uint64_t addr, size; diff --git a/common/image-fdt.c b/common/image-fdt.c index db6e395..a2342fa 100644 --- a/common/image-fdt.c +++ b/common/image-fdt.c @@ -479,7 +479,7 @@ int image_setup_libfdt(bootm_headers_t *images, void *blob, lmb_free(lmb, (phys_addr_t)(u32)(uintptr_t)blob, (phys_size_t)fdt_totalsize(blob)); - ret = fdt_resize(blob); + ret = fdt_shrink_to_minimum(blob); if (ret < 0) return ret; of_size = ret; diff --git a/common/image-fit.c b/common/image-fit.c index c61be65..255c4ca 100644 --- a/common/image-fit.c +++ b/common/image-fit.c @@ -1656,7 +1656,7 @@ int fit_image_load(bootm_headers_t *images, ulong addr, bootstage_error(bootstage_id + BOOTSTAGE_SUB_LOAD); return -EBADF; } - } else { + } else if (load_op != FIT_LOAD_OPTIONAL_NON_ZERO || load) { ulong image_start, image_end; ulong load_end; void *dst; diff --git a/common/image.c b/common/image.c index 11b3cf5..38b56e3 100644 --- a/common/image.c +++ b/common/image.c @@ -643,6 +643,64 @@ int genimg_get_comp_id(const char *name) #ifndef USE_HOSTCC /** + * genimg_get_kernel_addr_fit - get the real kernel address and return 2 + * FIT strings + * @img_addr: a string might contain real image address + * @fit_uname_config: double pointer to a char, will hold pointer to a + * configuration unit name + * @fit_uname_kernel: double pointer to a char, will hold pointer to a subimage + * name + * + * genimg_get_kernel_addr_fit get the real kernel start address from a string + * which is normally the first argv of bootm/bootz + * + * returns: + * kernel start address + */ +ulong genimg_get_kernel_addr_fit(char * const img_addr, + const char **fit_uname_config, + const char **fit_uname_kernel) +{ + ulong kernel_addr; + + /* find out kernel image address */ + if (!img_addr) { + kernel_addr = load_addr; + debug("* kernel: default image load address = 0x%08lx\n", + load_addr); +#if defined(CONFIG_FIT) + } else if (fit_parse_conf(img_addr, load_addr, &kernel_addr, + fit_uname_config)) { + debug("* kernel: config '%s' from image at 0x%08lx\n", + *fit_uname_config, kernel_addr); + } else if (fit_parse_subimage(img_addr, load_addr, &kernel_addr, + fit_uname_kernel)) { + debug("* kernel: subimage '%s' from image at 0x%08lx\n", + *fit_uname_kernel, kernel_addr); +#endif + } else { + kernel_addr = simple_strtoul(img_addr, NULL, 16); + debug("* kernel: cmdline image address = 0x%08lx\n", + kernel_addr); + } + + return kernel_addr; +} + +/** + * genimg_get_kernel_addr() is the simple version of + * genimg_get_kernel_addr_fit(). It ignores those return FIT strings + */ +ulong genimg_get_kernel_addr(char * const img_addr) +{ + const char *fit_uname_config = NULL; + const char *fit_uname_kernel = NULL; + + return genimg_get_kernel_addr_fit(img_addr, &fit_uname_config, + &fit_uname_kernel); +} + +/** * genimg_get_format - get image format type * @img_addr: image start address * @@ -908,7 +966,8 @@ int boot_get_ramdisk(int argc, char * const argv[], bootm_headers_t *images, &fit_uname_config, arch, IH_TYPE_RAMDISK, BOOTSTAGE_ID_FIT_RD_START, - FIT_LOAD_IGNORED, &rd_data, &rd_len); + FIT_LOAD_OPTIONAL_NON_ZERO, + &rd_data, &rd_len); if (rd_noffset < 0) return 1; diff --git a/common/lcd.c b/common/lcd.c index feb913a..217ec9d 100644 --- a/common/lcd.c +++ b/common/lcd.c @@ -100,7 +100,8 @@ #if LCD_BPP == LCD_MONOCHROME # define COLOR_MASK(c) ((c) | (c) << 1 | (c) << 2 | (c) << 3 | \ (c) << 4 | (c) << 5 | (c) << 6 | (c) << 7) -#elif (LCD_BPP == LCD_COLOR8) || (LCD_BPP == LCD_COLOR16) +#elif (LCD_BPP == LCD_COLOR8) || (LCD_BPP == LCD_COLOR16) || \ + (LCD_BPP == LCD_COLOR32) # define COLOR_MASK(c) (c) #else # error Unsupported LCD BPP. @@ -109,14 +110,12 @@ DECLARE_GLOBAL_DATA_PTR; static void lcd_drawchars(ushort x, ushort y, uchar *str, int count); -static inline void lcd_puts_xy(ushort x, ushort y, uchar *s); static inline void lcd_putc_xy(ushort x, ushort y, uchar c); static int lcd_init(void *lcdbase); static void *lcd_logo(void); -static int lcd_getbgcolor(void); static void lcd_setfgcolor(int color); static void lcd_setbgcolor(int color); @@ -177,10 +176,20 @@ static void console_scrollup(void) CONSOLE_SIZE - CONSOLE_ROW_SIZE * rows); /* Clear the last rows */ +#if (LCD_BPP != LCD_COLOR32) memset(lcd_console_address + CONSOLE_SIZE - CONSOLE_ROW_SIZE * rows, COLOR_MASK(lcd_color_bg), CONSOLE_ROW_SIZE * rows); - +#else + u32 *ppix = lcd_console_address + + CONSOLE_SIZE - CONSOLE_ROW_SIZE * rows; + u32 i; + for (i = 0; + i < (CONSOLE_ROW_SIZE * rows) / NBYTES(panel_info.vl_bpix); + i++) { + *ppix++ = COLOR_MASK(lcd_color_bg); + } +#endif lcd_sync(); console_row -= rows; } @@ -308,13 +317,15 @@ static void lcd_drawchars(ushort x, ushort y, uchar *str, int count) ushort off = x * (1 << LCD_BPP) % 8; #endif - dest = (uchar *)(lcd_base + y * lcd_line_length + x * (1 << LCD_BPP) / 8); + dest = (uchar *)(lcd_base + y * lcd_line_length + x * NBITS(LCD_BPP)/8); for (row = 0; row < VIDEO_FONT_HEIGHT; ++row, dest += lcd_line_length) { uchar *s = str; int i; #if LCD_BPP == LCD_COLOR16 ushort *d = (ushort *)dest; +#elif LCD_BPP == LCD_COLOR32 + u32 *d = (u32 *)dest; #else uchar *d = dest; #endif @@ -347,6 +358,12 @@ static void lcd_drawchars(ushort x, ushort y, uchar *str, int count) lcd_color_fg : lcd_color_bg; bits <<= 1; } +#elif LCD_BPP == LCD_COLOR32 + for (c = 0; c < 8; ++c) { + *d++ = (bits & 0x80) ? + lcd_color_fg : lcd_color_bg; + bits <<= 1; + } #endif } #if LCD_BPP == LCD_MONOCHROME @@ -355,15 +372,6 @@ static void lcd_drawchars(ushort x, ushort y, uchar *str, int count) } } -/*----------------------------------------------------------------------*/ - -static inline void lcd_puts_xy(ushort x, ushort y, uchar *s) -{ - lcd_drawchars(x, y, s, strlen((char *)s)); -} - -/*----------------------------------------------------------------------*/ - static inline void lcd_putc_xy(ushort x, ushort y, uchar c) { lcd_drawchars(x, y, &c, 1); @@ -476,9 +484,19 @@ void lcd_clear(void) test_pattern(); #else /* set framebuffer to background color */ +#if (LCD_BPP != LCD_COLOR32) memset((char *)lcd_base, - COLOR_MASK(lcd_getbgcolor()), + COLOR_MASK(lcd_color_bg), lcd_line_length * panel_info.vl_row); +#else + u32 *ppix = lcd_base; + u32 i; + for (i = 0; + i < (lcd_line_length * panel_info.vl_row)/NBYTES(panel_info.vl_bpix); + i++) { + *ppix++ = COLOR_MASK(lcd_color_bg); + } +#endif #endif /* Paint the logo and retrieve LCD base address */ debug("[LCD] Drawing the logo...\n"); @@ -586,20 +604,6 @@ static void lcd_setbgcolor(int color) lcd_color_bg = color; } -/*----------------------------------------------------------------------*/ - -int lcd_getfgcolor(void) -{ - return lcd_color_fg; -} - -/*----------------------------------------------------------------------*/ - -static int lcd_getbgcolor(void) -{ - return lcd_color_bg; -} - /************************************************************************/ /* ** Chipset depending Bitmap / Logo stuff... */ /************************************************************************/ @@ -938,8 +942,13 @@ int lcd_display_bitmap(ulong bmp_image, int x, int y) return 1; } - /* We support displaying 8bpp BMPs on 16bpp LCDs */ - if (bpix != bmp_bpix && !(bmp_bpix == 8 && bpix == 16)) { + /* + * We support displaying 8bpp BMPs on 16bpp LCDs + * and displaying 24bpp BMPs on 32bpp LCDs + * */ + if (bpix != bmp_bpix && + !(bmp_bpix == 8 && bpix == 16) && + !(bmp_bpix == 24 && bpix == 32)) { printf ("Error: %d bit/pixel mode, but BMP has %d bit/pixel\n", bpix, get_unaligned_le16(&bmp->header.bit_count)); return 1; @@ -1060,7 +1069,19 @@ int lcd_display_bitmap(ulong bmp_image, int x, int y) } break; #endif /* CONFIG_BMP_16BPP */ - +#if defined(CONFIG_BMP_24BMP) + case 24: + for (i = 0; i < height; ++i) { + for (j = 0; j < width; j++) { + *(fb++) = *(bmap++); + *(fb++) = *(bmap++); + *(fb++) = *(bmap++); + *(fb++) = 0; + } + fb -= lcd_line_length + width * (bpix / 8); + } + break; +#endif /* CONFIG_BMP_24BMP */ #if defined(CONFIG_BMP_32BPP) case 32: for (i = 0; i < height; ++i) { diff --git a/configs/A10-OLinuXino-Lime_defconfig b/configs/A10-OLinuXino-Lime_defconfig index a414953..b93ae7d 100644 --- a/configs/A10-OLinuXino-Lime_defconfig +++ b/configs/A10-OLinuXino-Lime_defconfig @@ -1,4 +1,5 @@ CONFIG_SPL=y CONFIG_SYS_EXTRA_OPTIONS="A10_OLINUXINO_L,SPL,AXP209_POWER,SUNXI_EMAC,AHCI,SATAPWR=SUNXI_GPC(3),USB_EHCI" +CONFIG_FTDFILE="sun4i-a10-olinuxino-lime.dtb" +S:CONFIG_ARM=y +S:CONFIG_TARGET_SUN4I=y diff --git a/configs/A10s-OLinuXino-M_defconfig b/configs/A10s-OLinuXino-M_defconfig index 1560ab1..f206970 100644 --- a/configs/A10s-OLinuXino-M_defconfig +++ b/configs/A10s-OLinuXino-M_defconfig @@ -1,4 +1,5 @@ CONFIG_SPL=y CONFIG_SYS_EXTRA_OPTIONS="A10S_OLINUXINO_M,SPL,AXP152_POWER,SUNXI_EMAC,USB_EHCI,SUNXI_USB_VBUS0_GPIO=SUNXI_GPB(10)" +CONFIG_FTDFILE="sun5i-a10s-olinuxino-micro.dtb" +S:CONFIG_ARM=y +S:CONFIG_TARGET_SUN5I=y diff --git a/configs/A13-OLinuXinoM_defconfig b/configs/A13-OLinuXinoM_defconfig index fb510d5..8529dbe 100644 --- a/configs/A13-OLinuXinoM_defconfig +++ b/configs/A13-OLinuXinoM_defconfig @@ -1,4 +1,5 @@ CONFIG_SPL=y CONFIG_SYS_EXTRA_OPTIONS="A13_OLINUXINOM,SPL,CONS_INDEX=2,USB_EHCI,SUNXI_USB_VBUS0_GPIO=SUNXI_GPG(11)" +CONFIG_FTDFILE="sun5i-a13-olinuxino-micro.dtb" +S:CONFIG_ARM=y +S:CONFIG_TARGET_SUN5I=y diff --git a/configs/A13-OLinuXino_defconfig b/configs/A13-OLinuXino_defconfig index ba21136..c3a12cc 100644 --- a/configs/A13-OLinuXino_defconfig +++ b/configs/A13-OLinuXino_defconfig @@ -1,4 +1,5 @@ CONFIG_SPL=y CONFIG_SYS_EXTRA_OPTIONS="A13_OLINUXINO,SPL,CONS_INDEX=2,AXP209_POWER,USB_EHCI,SUNXI_USB_VBUS0_GPIO=SUNXI_GPG(11)" +CONFIG_FTDFILE="sun5i-a13-olinuxino.dtb" +S:CONFIG_ARM=y +S:CONFIG_TARGET_SUN5I=y diff --git a/configs/A20-OLinuXino_MICRO_defconfig b/configs/A20-OLinuXino_MICRO_defconfig index 52a857a..c91319d 100644 --- a/configs/A20-OLinuXino_MICRO_defconfig +++ b/configs/A20-OLinuXino_MICRO_defconfig @@ -1,4 +1,5 @@ CONFIG_SPL=y CONFIG_SYS_EXTRA_OPTIONS="A20_OLINUXINO_M,SPL,AXP209_POWER,SUNXI_GMAC,AHCI,SATAPWR=SUNXI_GPB(8),USB_EHCI" +CONFIG_FTDFILE="sun7i-a20-olinuxino-micro.dtb" +S:CONFIG_ARM=y +S:CONFIG_TARGET_SUN7I=y diff --git a/configs/Auxtek-T004_defconfig b/configs/Auxtek-T004_defconfig index 643e628..193019c 100644 --- a/configs/Auxtek-T004_defconfig +++ b/configs/Auxtek-T004_defconfig @@ -1,4 +1,5 @@ CONFIG_SPL=y CONFIG_SYS_EXTRA_OPTIONS="AUXTEK_T004,SPL,AXP152_POWER,USB_EHCI,SUNXI_USB_VBUS0_GPIO=SUNXI_GPG(13)" +CONFIG_FTDFILE="sun5i-a10s-auxtek-t004.dtb" +S:CONFIG_ARM=y +S:CONFIG_TARGET_SUN5I=y diff --git a/configs/Bananapi_defconfig b/configs/Bananapi_defconfig index be3d815..dc68469 100644 --- a/configs/Bananapi_defconfig +++ b/configs/Bananapi_defconfig @@ -1,4 +1,5 @@ CONFIG_SPL=y CONFIG_SYS_EXTRA_OPTIONS="BANANAPI,SPL,AXP209_POWER,SUNXI_GMAC,RGMII,MACPWR=SUNXI_GPH(23),AHCI,USB_EHCI" +CONFIG_FTDFILE="sun7i-a20-bananapi.dtb" +S:CONFIG_ARM=y +S:CONFIG_TARGET_SUN7I=y diff --git a/configs/Cubieboard2_FEL_defconfig b/configs/Cubieboard2_FEL_defconfig index 63636b0..ae5e25a 100644 --- a/configs/Cubieboard2_FEL_defconfig +++ b/configs/Cubieboard2_FEL_defconfig @@ -1,4 +1,5 @@ CONFIG_SPL=y CONFIG_SYS_EXTRA_OPTIONS="CUBIEBOARD2,SPL_FEL,AXP209_POWER,SUNXI_GMAC,AHCI,SATAPWR=SUNXI_GPB(8),USB_EHCI" +CONFIG_FTDFILE="sun7i-a20-cubieboard2.dtb" +S:CONFIG_ARM=y +S:CONFIG_TARGET_SUN7I=y diff --git a/configs/Cubieboard2_defconfig b/configs/Cubieboard2_defconfig index fa34c51..df87edc 100644 --- a/configs/Cubieboard2_defconfig +++ b/configs/Cubieboard2_defconfig @@ -1,4 +1,5 @@ CONFIG_SPL=y CONFIG_SYS_EXTRA_OPTIONS="CUBIEBOARD2,SPL,AXP209_POWER,SUNXI_GMAC,AHCI,SATAPWR=SUNXI_GPB(8),USB_EHCI" +CONFIG_FTDFILE="sun7i-a20-cubieboard2.dtb" +S:CONFIG_ARM=y +S:CONFIG_TARGET_SUN7I=y diff --git a/configs/Cubieboard_defconfig b/configs/Cubieboard_defconfig index 774029c..57bf045 100644 --- a/configs/Cubieboard_defconfig +++ b/configs/Cubieboard_defconfig @@ -1,4 +1,5 @@ CONFIG_SPL=y CONFIG_SYS_EXTRA_OPTIONS="CUBIEBOARD,SPL,AXP209_POWER,SUNXI_EMAC,AHCI,SATAPWR=SUNXI_GPB(8),USB_EHCI" +CONFIG_FTDFILE="sun4i-a10-cubieboard.dtb" +S:CONFIG_ARM=y +S:CONFIG_TARGET_SUN4I=y diff --git a/configs/Cubietruck_FEL_defconfig b/configs/Cubietruck_FEL_defconfig index 7ebf635..790125a 100644 --- a/configs/Cubietruck_FEL_defconfig +++ b/configs/Cubietruck_FEL_defconfig @@ -1,4 +1,5 @@ CONFIG_SPL=y CONFIG_SYS_EXTRA_OPTIONS="CUBIETRUCK,SPL_FEL,AXP209_POWER,SUNXI_GMAC,RGMII,AHCI,SATAPWR=SUNXI_GPH(12),USB_EHCI" +CONFIG_FTDFILE="sun7i-a20-cubietruck.dtb" +S:CONFIG_ARM=y +S:CONFIG_TARGET_SUN7I=y diff --git a/configs/Cubietruck_defconfig b/configs/Cubietruck_defconfig index fbc9127..4ad82e3 100644 --- a/configs/Cubietruck_defconfig +++ b/configs/Cubietruck_defconfig @@ -1,4 +1,5 @@ CONFIG_SPL=y CONFIG_SYS_EXTRA_OPTIONS="CUBIETRUCK,SPL,AXP209_POWER,SUNXI_GMAC,RGMII,AHCI,SATAPWR=SUNXI_GPH(12),USB_EHCI" +CONFIG_FTDFILE="sun7i-a20-cubietruck.dtb" +S:CONFIG_ARM=y +S:CONFIG_TARGET_SUN7I=y diff --git a/configs/FLAGADM_defconfig b/configs/FLAGADM_defconfig deleted file mode 100644 index 6113797..0000000 --- a/configs/FLAGADM_defconfig +++ /dev/null @@ -1,3 +0,0 @@ -CONFIG_PPC=y -CONFIG_8xx=y -CONFIG_TARGET_FLAGADM=y diff --git a/configs/GEN860T_SC_defconfig b/configs/GEN860T_SC_defconfig deleted file mode 100644 index ff624db..0000000 --- a/configs/GEN860T_SC_defconfig +++ /dev/null @@ -1,4 +0,0 @@ -CONFIG_SYS_EXTRA_OPTIONS="SC" -CONFIG_PPC=y -CONFIG_8xx=y -CONFIG_TARGET_GEN860T=y diff --git a/configs/GEN860T_defconfig b/configs/GEN860T_defconfig deleted file mode 100644 index e672623..0000000 --- a/configs/GEN860T_defconfig +++ /dev/null @@ -1,3 +0,0 @@ -CONFIG_PPC=y -CONFIG_8xx=y -CONFIG_TARGET_GEN860T=y diff --git a/configs/Linksprite_pcDuino3_defconfig b/configs/Linksprite_pcDuino3_defconfig index 10ebcef..22d0446 100644 --- a/configs/Linksprite_pcDuino3_defconfig +++ b/configs/Linksprite_pcDuino3_defconfig @@ -1,4 +1,5 @@ CONFIG_SPL=y CONFIG_SYS_EXTRA_OPTIONS="PCDUINO3,SPL,AXP209_POWER,SUNXI_GMAC,AHCI,SATAPWR=SUNXI_GPH(2),USB_EHCI" +CONFIG_FTDFILE="sun7i-a20-pcduino3.dtb" +S:CONFIG_ARM=y +S:CONFIG_TARGET_SUN7I=y diff --git a/configs/Mele_A1000G_defconfig b/configs/Mele_A1000G_defconfig index 2079279..fa47fae 100644 --- a/configs/Mele_A1000G_defconfig +++ b/configs/Mele_A1000G_defconfig @@ -1,4 +1,5 @@ CONFIG_SPL=y CONFIG_SYS_EXTRA_OPTIONS="MELE_A1000G,SPL,AXP209_POWER,SUNXI_EMAC,MACPWR=SUNXI_GPH(15),AHCI,USB_EHCI" +CONFIG_FTDFILE="sun4i-a10-a1000.dtb" +S:CONFIG_ARM=y +S:CONFIG_TARGET_SUN4I=y diff --git a/configs/Mele_A1000_defconfig b/configs/Mele_A1000_defconfig index 13528ab..d7c156d 100644 --- a/configs/Mele_A1000_defconfig +++ b/configs/Mele_A1000_defconfig @@ -1,4 +1,5 @@ CONFIG_SPL=y CONFIG_SYS_EXTRA_OPTIONS="MELE_A1000,SPL,AXP209_POWER,SUNXI_EMAC,MACPWR=SUNXI_GPH(15),AHCI,USB_EHCI" +CONFIG_FTDFILE="sun4i-a10-a1000.dtb" +S:CONFIG_ARM=y +S:CONFIG_TARGET_SUN4I=y diff --git a/configs/Mini-X-1Gb_defconfig b/configs/Mini-X-1Gb_defconfig index 61fa1de..af0b800 100644 --- a/configs/Mini-X-1Gb_defconfig +++ b/configs/Mini-X-1Gb_defconfig @@ -1,4 +1,5 @@ CONFIG_SPL=y CONFIG_SYS_EXTRA_OPTIONS="MINI_X_1GB,SPL,AXP209_POWER,USB_EHCI" +CONFIG_FTDFILE="sun4i-a10-mini-xplus.dtb" +S:CONFIG_ARM=y +S:CONFIG_TARGET_SUN4I=y diff --git a/configs/Mini-X_defconfig b/configs/Mini-X_defconfig index ceb02c5..ea0c786 100644 --- a/configs/Mini-X_defconfig +++ b/configs/Mini-X_defconfig @@ -1,4 +1,5 @@ CONFIG_SPL=y CONFIG_SYS_EXTRA_OPTIONS="MINI_X,SPL,AXP209_POWER,USB_EHCI" +CONFIG_FTDFILE="sun4i-a10-mini-xplus.dtb" +S:CONFIG_ARM=y +S:CONFIG_TARGET_SUN4I=y diff --git a/configs/PCI5441_defconfig b/configs/PCI5441_defconfig deleted file mode 100644 index a4bfdb4..0000000 --- a/configs/PCI5441_defconfig +++ /dev/null @@ -1,2 +0,0 @@ -CONFIG_NIOS2=y -CONFIG_TARGET_PCI5441=y diff --git a/configs/PK1C20_defconfig b/configs/PK1C20_defconfig deleted file mode 100644 index bb2513a..0000000 --- a/configs/PK1C20_defconfig +++ /dev/null @@ -1,2 +0,0 @@ -CONFIG_NIOS2=y -CONFIG_TARGET_PK1C20=y diff --git a/configs/SXNI855T_defconfig b/configs/SXNI855T_defconfig deleted file mode 100644 index a4c900a..0000000 --- a/configs/SXNI855T_defconfig +++ /dev/null @@ -1,3 +0,0 @@ -CONFIG_PPC=y -CONFIG_8xx=y -CONFIG_TARGET_SXNI855T=y diff --git a/configs/aristainetos_defconfig b/configs/aristainetos_defconfig new file mode 100644 index 0000000..6541865 --- /dev/null +++ b/configs/aristainetos_defconfig @@ -0,0 +1,3 @@ +CONFIG_SYS_EXTRA_OPTIONS="IMX_CONFIG=board/aristainetos/aristainetos.cfg,MX6DL" +CONFIG_ARM=y +CONFIG_TARGET_ARISTAINETOS=y diff --git a/configs/ba10_tv_box_defconfig b/configs/ba10_tv_box_defconfig index 02a2538..c9961bd 100644 --- a/configs/ba10_tv_box_defconfig +++ b/configs/ba10_tv_box_defconfig @@ -1,4 +1,5 @@ CONFIG_SPL=y CONFIG_SYS_EXTRA_OPTIONS="BA10_TV_BOX,SPL,AXP209_POWER,SUNXI_EMAC,USB_EHCI,SUNXI_USB_VBUS1_GPIO=SUNXI_GPH(12)" +CONFIG_FTDFILE="sun4i-a10-ba10-tvbox.dtb" +S:CONFIG_ARM=y +S:CONFIG_TARGET_SUN4I=y diff --git a/configs/i12-tvbox_defconfig b/configs/i12-tvbox_defconfig index ff86841..b267312 100644 --- a/configs/i12-tvbox_defconfig +++ b/configs/i12-tvbox_defconfig @@ -1,4 +1,5 @@ CONFIG_SPL=y CONFIG_SYS_EXTRA_OPTIONS="I12_TVBOX,SPL,AXP209_POWER,SUNXI_GMAC,MACPWR=SUNXI_GPH(21),USB_EHCI" +CONFIG_FTDFILE="sun7i-a20-i12-tvbox.dtb" +S:CONFIG_ARM=y +S:CONFIG_TARGET_SUN7I=y diff --git a/configs/mx6sxsabresd_defconfig b/configs/mx6sxsabresd_defconfig new file mode 100644 index 0000000..f23d48f --- /dev/null +++ b/configs/mx6sxsabresd_defconfig @@ -0,0 +1,3 @@ +CONFIG_SYS_EXTRA_OPTIONS="IMX_CONFIG=board/freescale/mx6sxsabresd/imximage.cfg,MX6SX" +CONFIG_ARM=y +CONFIG_TARGET_MX6SXSABRESD=y diff --git a/configs/omap5912osk_defconfig b/configs/omap5912osk_defconfig deleted file mode 100644 index 5aeb097..0000000 --- a/configs/omap5912osk_defconfig +++ /dev/null @@ -1,2 +0,0 @@ -CONFIG_ARM=y -CONFIG_TARGET_OMAP5912OSK=y diff --git a/configs/qt840a_defconfig b/configs/qt840a_defconfig index acb100c..7360212 100644 --- a/configs/qt840a_defconfig +++ b/configs/qt840a_defconfig @@ -1,4 +1,5 @@ CONFIG_SPL=y CONFIG_SYS_EXTRA_OPTIONS="QT840A,SPL,AXP209_POWER,SUNXI_GMAC,MACPWR=SUNXI_GPH(21),USB_EHCI" +CONFIG_FTDFILE="sun7i-a20-i12-tvbox.dtb" +S:CONFIG_ARM=y +S:CONFIG_TARGET_SUN7I=y diff --git a/configs/r7-tv-dongle_defconfig b/configs/r7-tv-dongle_defconfig index f0f97b0..35b66f0 100644 --- a/configs/r7-tv-dongle_defconfig +++ b/configs/r7-tv-dongle_defconfig @@ -1,4 +1,5 @@ CONFIG_SPL=y CONFIG_SYS_EXTRA_OPTIONS="R7DONGLE,SPL,AXP152_POWER,USB_EHCI,SUNXI_USB_VBUS0_GPIO=SUNXI_GPG(13)" +CONFIG_FTDFILE="sun5i-a10s-r7-tv-dongle.dtb" +S:CONFIG_ARM=y +S:CONFIG_TARGET_SUN5I=y diff --git a/configs/stxxtc_defconfig b/configs/stxxtc_defconfig deleted file mode 100644 index d0642e4..0000000 --- a/configs/stxxtc_defconfig +++ /dev/null @@ -1,3 +0,0 @@ -CONFIG_PPC=y -CONFIG_8xx=y -CONFIG_TARGET_STXXTC=y diff --git a/configs/svm_sc8xx_defconfig b/configs/svm_sc8xx_defconfig deleted file mode 100644 index 9f0d343..0000000 --- a/configs/svm_sc8xx_defconfig +++ /dev/null @@ -1,3 +0,0 @@ -CONFIG_PPC=y -CONFIG_8xx=y -CONFIG_TARGET_SVM_SC8XX=y diff --git a/doc/README.android-fastboot b/doc/README.android-fastboot index f1d128c..4045727 100644 --- a/doc/README.android-fastboot +++ b/doc/README.android-fastboot @@ -38,6 +38,10 @@ CONFIG_G_DNL_VENDOR_NUM CONFIG_G_DNL_PRODUCT_NUM CONFIG_G_DNL_MANUFACTURER +NOTE: The CONFIG_G_DNL_VENDOR_NUM must be one of the numbers supported by +the fastboot client. The list of vendor IDs supported can be found in the +fastboot client source code (fastboot.c) mentioned above. + The fastboot function is enabled by defining CONFIG_CMD_FASTBOOT and CONFIG_ANDROID_BOOT_IMAGE. diff --git a/doc/README.kconfig b/doc/README.kconfig new file mode 100644 index 0000000..3aad5b4 --- /dev/null +++ b/doc/README.kconfig @@ -0,0 +1,226 @@ +Kconfig in U-Boot +================= + +This document describes the configuration infrastructure of U-Boot. + +The conventional configuration was replaced by Kconfig at v2014.10-rc1 release. + + +Language Specification +---------------------- + +Kconfig originates in Linux Kernel. +See the file "Documentation/kbuild/kconfig*.txt" in your Linux Kernel +source directory for a basic specification of Kconfig. + + +Difference from Linux's Kconfig +------------------------------- + +The biggest difference between Linux Kernel and U-Boot in terms of the +configuration is that U-Boot has to configure multiple boot images per board: +Normal, SPL, TPL. +Kconfig functions need to be expanded for U-Boot to handle multiple images. +The files scripts/kconfig/* were imported from Linux Kernel and adjusted +for that purpose. + +See below for how each configuration target works in U-Boot: + +- config, nconfig, menuconfig, xconfig, gconfig + + These targets are used to configure Normal and create (or modify) the + .config file. For SPL configuration, the configutation targets are prefixed + with "spl/", for example "make spl/config", "make spl/menuconfig", etc. + Those targets create or modify the spl/.config file. Likewise, run + "make tpl/config", "make tpl/menuconfig", etc. for TPL. + +- silentoldconfig + + This target updates .config, include/generated/autoconf.h and + include/configs/*. In U-Boot, the same thing is done for SPL, TPL, + if supported by the target board. Depending on whether CONFIG_SPL and + CONFIG_TPL are defined, "make silentoldconfig" iterates three times at most + changing the work directory. + + To sum up, "make silentoldconfig" possibly updates: + - .config, include/generated/autoconf.h, include/config/* + - spl/.config, spl/include/generated/autoconf.h, spl/include/config/* + (in case CONFIG_SPL=y) + - tpl/.config, tpl/include/generated/autoconf.h, tpl/include/config/* + (in case CONFIG_TPL=y) + +- defconfig, <board>_defconfig + + The target "<board>_defconfig" is used to create the .config based on the + file configs/<board>_defconfig. The "defconfig" target is the same + except it checks for a file specified with KBUILD_DEFCONFIG environment. + + Note: + The defconfig files are placed under the "configs" directory, + not "arch/$(ARCH)/configs". This is because "ARCH" is not necessarily + given from the command line for the U-Boot configuration and build. + + The defconfig file format in U-Boot has the special syntax; each line has + "<condition>:" prefix to show which image(s) the line is valid for. + For example, + + CONFIG_FOO=100 + S:CONFIG_FOO=200 + T:CONFIG_FOO=300 + ST:CONFIG_BAR=y + +S:CONFIG_BAZ=y + +T:CONFIG_QUX=y + +ST:CONFIG_QUUX=y + + Here, the "<condition>:" prefix is one of: + None - the line is valid only for Normal image + S: - the line is valid only for SPL image + T: - the line is valid only for TPL image + ST: - the line is valid for SPL and TPL images + +S: - the line is valid for Normal and SPL images + +T: - the line is valid for Normal and TPL images + +ST: - the line is valid for Normal, SPL and SPL images + + So, if neither CONFIG_SPL nor CONFIG_TPL is defined, the defconfig file + has no "<condition>:" part and therefore has the same form as in Linux. + From the example defconfig shown above, three separete configuration sets + are generated and used for creating .config, spl/.config and tpl/.config. + + - Input for the default configuration of Normal + CONFIG_FOO=100 + CONFIG_BAZ=y + CONFIG_QUX=y + CONFIG_QUUX=y + + - Input for the default configuration of SPL + CONFIG_FOO=200 + CONFIG_BAR=y + CONFIG_BAZ=y + CONFIG_QUUX=y + + - Input for the default configuration of TPL + CONFIG_FOO=300 + CONFIG_BAR=y + CONFIG_QUX=y + CONFIG_QUUX=y + +- savedefconfig + + This is the reverse operation of "make defconfig". If neither CONFIG_SPL + nor CONFIG_TPL is defined in the .config file, it works like "savedefconfig" + in Linux Kernel: creates the minimal set of config based on the .config + and saves it into the "defconfig" file. If CONFIG_SPL (and CONFIG_TPL) is + defined, the common lines among .config, spl/.config (and tpl/.config) are + coalesced together with "<condition:>" prefix for each line as shown above. + This file can be used as an input of "defconfig" target. + +- <board>_config + + This does not exist in Linux's Kconfig. + Prior to Kconfig, in U-Boot, "make <board>_config" was used for the + configuration. It is still supported for backward compatibility and + its behavior is the same as "make <board>_defconfig". + + +Migration steps to Kconfig +-------------------------- + +Prior to Kconfig, the C preprocessor based board configuration had been used +in U-Boot. + +Although Kconfig was introduced and some configs have been moved to Kconfig, +many of configs are still defined in C header files. It will take a very +long term to move all of them to Kconfig. In the interim, the two different +configuration infrastructures should coexist. +The configuration files are generated by both Kconfig and the old preprocessor +based configuration as follows: + +Configuration files for use in C sources + - include/generated/autoconf.h (generated by Kconfig for Normal) + - spl/include/generated/autoconf.h (generated by Kconfig for SPL) + - tpl/include/generated/autoconf.h (generated by Kconfig for TPL) + - include/configs/<board>.h (exists for all boards) + +Configuration file for use in makefiles + - include/config/auto.conf (generated by Kconfig for Normal) + - spl/include/config/auto.conf (generated by Kconfig for SPL) + - tpl/include/config/auto.conf (generated by Kconfig for TPL) + - include/autoconf.mk (generated by the old config for Normal) + - spl/include/autoconfig.mk (generated by the old config for SPL) + - tpl/include/autoconfig.mk (generated by the old config for TPL) + +When adding a new CONFIG macro, it is highly recommended to add it to Kconfig +rather than to a header file. + + +Conversion from boards.cfg to Kconfig +------------------------------------- + +Prior to Kconfig, boards.cfg was a primary database that contained Arch, CPU, +SoC, etc. of all the supported boards. It was deleted when switching to +Kconfig. Each field of boards.cfg was converted as follows: + + Status -> "S:" entry of MAINTAINERS + Arch -> CONFIG_SYS_ARCH defined by Kconfig + CPU -> CONFIG_SYS_CPU defined by Kconfig + SoC -> CONFIG_SYS_SOC defined by Kconfig + Vendor -> CONFIG_SYS_VENDOR defined by Kconfig + Board -> CONFIG_SYS_BOARD defined by Kconfig + Target -> File name of defconfig (configs/<target>_defconfig) + Options -> CONFIG_SYS_EXTRA_OPTIONS defined by Kconfig + Maintainers -> "M:" entry of MAINTAINERS + + +Tips to add/remove boards +------------------------- + +When adding a new board, the following steps are generally needed: + + [1] Add a header file include/configs/<target>.h + [2] Make sure to define necessary CONFIG_SYS_* in Kconfig: + Define CONFIG_SYS_CPU="cpu" to compile arch/<arch>/cpu/<cpu> + Define CONFIG_SYS_SOC="soc" to compile arch/<arch>/cpu/<cpu>/<soc> + Define CONFIG_SYS_VENDOR="vendor" to compile board/<vendor>/common/* + and board/<vendor>/<board>/* + Define CONFIG_SYS_BOARD="board" to compile board/<board>/* + (or board/<vendor>/<board>/* if CONFIG_SYS_VENDOR is defined) + Define CONFIG_SYS_CONFIG_NAME="target" to include + include/configs/<target>.h + [3] Add a new entry to the board select menu in Kconfig. + The board select menu is located in arch/<arch>/Kconfig or + arch/<arch>/*/Kconfig. + [4] Add a MAINTAINERS file + It is generally placed at board/<board>/MAINTAINERS or + board/<vendor>/<board>/MAINTAINERS + [5] Add configs/<target>_defconfig + +When removing an obsolete board, the following steps are generally needed: + + [1] Remove configs/<target>_defconfig + [2] Remove include/configs/<target>.h if it is not used by any other boards + [3] Remove board/<vendor>/<board>/* or board/<board>/* if it is not used + by any other boards + [4] Update MAINTAINERS if necessary + [5] Remove the unused entry from the board select menu in Kconfig + [6] Add an entry to doc/README.scrapyard + + +TODO +---- + +- The option field of boards.cfg, which was used for the pre-Kconfig + configuration, moved to CONFIG_SYS_EXTRA_OPTIONS verbatim now. + Board maintainers are expected to implement proper Kconfig options + and switch over to them. Eventually CONFIG_SYS_EXTRA_OPTIONS will go away. + CONFIG_SYS_EXTRA_OPTIONS should not be used for new boards. + +- In the pre-Kconfig, a single board had multiple entries in the boards.cfg + file with differences in the option fields. The correspoing defconfig files + were auto-generated when switching to Kconfig. Now we have too many + defconfig files compared with the number of the supported boards. It is + recommended to have only one defconfig per board and allow users to select + the config options. + +- Move the config macros in header files to Kconfig. When we move at least + macros used in makefiles, we can drop include/autoconfig.mk, which makes + the build scripts much simpler. diff --git a/doc/README.scrapyard b/doc/README.scrapyard index 6a1d2a5..7ef5a22 100644 --- a/doc/README.scrapyard +++ b/doc/README.scrapyard @@ -5,23 +5,31 @@ by unnoticed, but often build errors will result. If nobody cares any more to resolve such problems, then the code is really dead and will be removed from the U-Boot source tree. The remainders rest in piece in the imperishable depths of the git history. This document tries to -maintain a list of such former fellows, so archeologists can check -easily if here is something they might want to dig for... +maintain a list of such former fellows, so archaeologists can check +easily if there is something they might want to dig for... +The list should be sorted in reverse chronological order. Board Arch CPU Commit Removed Last known maintainer/contact ================================================================================================= -spc1920 powerpc mpc8xx - - -v37 powerpc mpc8xx - - -fads powerpc mpc8xx - - -netphone powerpc mpc8xx - - -netta2 powerpc mpc8xx - - -netta powerpc mpc8xx - - -rbc823 powerpc mpc8xx - - -quantum powerpc mpc8xx - - -RPXlite_dw powerpc mpc8xx - - -qs850 powerpc mpc8xx - - -qs860t powerpc mpc8xx - - +flagadm powerpc mpc8xx - - Kári Davíðsson <kd@flaga.is> +gen860t powerpc mpc8xx - - Keith Outwater <Keith_Outwater@mvis.com> +sixnet powerpc mpc8xx - - Dave Ellis <DGE@sixnetio.com> +svm_sc8xx powerpc mpc8xx - - John Zhan <zhanz@sinovee.com> +stxxtc powerpc mpc8xx - - Dan Malek <dan@embeddedalley.com> +omap5912osk arm arm926ejs - - Rishi Bhattacharya <rishi@ti.com> +p1023rds powerpc mpc85xx d0bc5140 2014-07-22 Roy Zang <tie-fei.zang@freescale.com> +spc1920 powerpc mpc8xx 98ad54be 2014-07-07 +v37 powerpc mpc8xx b8c1438a 2014-07-07 +fads powerpc mpc8xx 03f9d7d1 2014-07-07 +netphone powerpc mpc8xx c51c1c9a 2014-07-07 +netta2 powerpc mpc8xx c51c1c9a 2014-07-07 +netta powerpc mpc8xx c51c1c9a 2014-07-07 +rbc823 powerpc mpc8xx c750b9c0 2014-07-07 +quantum powerpc mpc8xx 0657e46e 2014-07-07 +RPXlite_dw powerpc mpc8xx 0657e46e 2014-07-07 +qs850 powerpc mpc8xx dab0f762 2014-07-07 +qs860t powerpc mpc8xx dab0f762 2014-07-07 simpc8313 powerpc mpc83xx 7445207f 2014-06-05 Ron Madrid <info@sheldoninst.com> hidden_dragon powerpc mpc824x 3fe1a854 2014-05-30 Yusdi Santoso <yusdi_santoso@adaptec.com> debris powerpc mpc824x 7edb1f7b 2014-05-30 Sangmoon Kim <dogoil@etinsys.com> @@ -33,6 +41,7 @@ zpc1900 powerpc mpc8260 6f80bb48 2014-05-30 Yuli Barcohe mpc8260ads powerpc mpc8260 facb6725 2014-05-30 Yuli Barcohen <yuli@arabellasw.com> adder powerpc mpc8xx 373a9788 2014-05-30 Yuli Barcohen <yuli@arabellasw.com> quad100hd powerpc ppc405ep 3569571d 2014-05-30 Gary Jennejohn <gljennjohn@googlemail.com> +incaip mips mips32 538cf92c 2014-04-20 Wolfgang Denk <wd@denx.de> lubbock arm pxa 36bf57b 2014-04-18 Kyle Harris <kharris@nexus-tech.net> EVB64260 powerpc mpc824x bb3aef9 2014-04-18 MOUSSE powerpc mpc824x 03f2ecc 2014-04-18 @@ -149,4 +158,3 @@ MVS1 powerpc MPC823 306620b 2008-08-26 Andre Schwar adsvix ARM PXA27x 7610db1 2008-07-30 Adrian Filipi <adrian.filipi@eurotech.com> R5200 ColdFire - 48ead7a 2008-03-31 Zachary P. Landau <zachary.landau@labxtechnologies.com> CPCI440 powerpc 440GP b568fd2 2007-12-27 Matthias Fuchs <matthias.fuchs@esd-electronics.com> -incaip mips mips32 - 2014-04-17 Wolfgang Denk <wd@denx.de> diff --git a/doc/git-mailrc b/doc/git-mailrc index 0985791..0fba100 100644 --- a/doc/git-mailrc +++ b/doc/git-mailrc @@ -106,10 +106,11 @@ alias sh superh alias x86 uboot, sjg, gruss # Subsystem aliases +alias dm uboot, sjg alias cfi uboot, stroese alias dfu uboot, lukma alias kerneldoc uboot, marex -alias fdt uboot, Jerry Van Baren <vanbaren@cideas.com> +alias fdt uboot, sjg alias i2c uboot, hs alias kconfig uboot, masahiro alias mmc uboot, panto @@ -118,3 +119,5 @@ alias net uboot, jhersh alias spi uboot, jagan alias usb uboot, marex alias video uboot, ag +alias patman uboot, sjg +alias buildman uboot, sjg diff --git a/doc/uImage.FIT/signature.txt b/doc/uImage.FIT/signature.txt index a6ab543..b2f89fc 100644 --- a/doc/uImage.FIT/signature.txt +++ b/doc/uImage.FIT/signature.txt @@ -66,7 +66,8 @@ Creating an RSA key and certificate ----------------------------------- To create a new public key, size 2048 bits: -$ openssl genrsa -F4 -out keys/dev.key 2048 +$ openssl genpkey -algorithm RSA -out keys/dev.key \ + -pkeyopt rsa_keygen_bits:2048 -pkeyopt rsa_keygen_pubexp:65537 To create a certificate for this: @@ -159,6 +160,7 @@ For RSA the following are mandatory: - rsa,num-bits: Number of key bits (e.g. 2048) - rsa,modulus: Modulus (N) as a big-endian multi-word integer +- rsa,exponent: Public exponent (E) as a 64 bit unsigned integer - rsa,r-squared: (2^num-bits)^2 as a big-endian multi-word integer - rsa,n0-inverse: -1 / modulus[0] mod 2^32 diff --git a/drivers/Makefile b/drivers/Makefile index b23076f..b22b109 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -15,3 +15,4 @@ obj-y += video/ obj-y += watchdog/ obj-$(CONFIG_QE) += qe/ obj-y += memory/ +obj-y += pwm/ diff --git a/drivers/dfu/Makefile b/drivers/dfu/Makefile index def628d..5cc535e 100644 --- a/drivers/dfu/Makefile +++ b/drivers/dfu/Makefile @@ -9,3 +9,4 @@ obj-$(CONFIG_DFU_FUNCTION) += dfu.o obj-$(CONFIG_DFU_MMC) += dfu_mmc.o obj-$(CONFIG_DFU_NAND) += dfu_nand.o obj-$(CONFIG_DFU_RAM) += dfu_ram.o +obj-$(CONFIG_DFU_SF) += dfu_sf.o diff --git a/drivers/dfu/dfu.c b/drivers/dfu/dfu.c index dc09ff6..3512b14 100644 --- a/drivers/dfu/dfu.c +++ b/drivers/dfu/dfu.c @@ -44,7 +44,7 @@ static int dfu_find_alt_num(const char *s) return ++i; } -int dfu_init_env_entities(char *interface, int dev) +int dfu_init_env_entities(char *interface, char *devstr) { const char *str_env; char *env_bkp; @@ -57,7 +57,7 @@ int dfu_init_env_entities(char *interface, int dev) } env_bkp = strdup(str_env); - ret = dfu_config_entities(env_bkp, interface, dev); + ret = dfu_config_entities(env_bkp, interface, devstr); if (ret) { error("DFU entities configuration failed!\n"); return ret; @@ -82,7 +82,7 @@ unsigned long dfu_get_buf_size(void) return dfu_buf_size; } -unsigned char *dfu_get_buf(void) +unsigned char *dfu_get_buf(struct dfu_entity *dfu) { char *s; @@ -92,6 +92,8 @@ unsigned char *dfu_get_buf(void) s = getenv("dfu_bufsiz"); dfu_buf_size = s ? (unsigned long)simple_strtol(s, NULL, 16) : CONFIG_SYS_DFU_DATA_BUF_SIZE; + if (dfu->max_buf_size && dfu_buf_size > dfu->max_buf_size) + dfu_buf_size = dfu->max_buf_size; dfu_buf = memalign(CONFIG_SYS_CACHELINE_SIZE, dfu_buf_size); if (dfu_buf == NULL) @@ -147,6 +149,19 @@ static int dfu_write_buffer_drain(struct dfu_entity *dfu) return ret; } +void dfu_write_transaction_cleanup(struct dfu_entity *dfu) +{ + /* clear everything */ + dfu_free_buf(); + dfu->crc = 0; + dfu->offset = 0; + dfu->i_blk_seq_num = 0; + dfu->i_buf_start = dfu_buf; + dfu->i_buf_end = dfu_buf; + dfu->i_buf = dfu->i_buf_start; + dfu->inited = 0; +} + int dfu_flush(struct dfu_entity *dfu, void *buf, int size, int blk_seq_num) { int ret = 0; @@ -162,23 +177,14 @@ int dfu_flush(struct dfu_entity *dfu, void *buf, int size, int blk_seq_num) printf("\nDFU complete %s: 0x%08x\n", dfu_hash_algo->name, dfu->crc); - /* clear everything */ - dfu_free_buf(); - dfu->crc = 0; - dfu->offset = 0; - dfu->i_blk_seq_num = 0; - dfu->i_buf_start = dfu_buf; - dfu->i_buf_end = dfu_buf; - dfu->i_buf = dfu->i_buf_start; - dfu->inited = 0; + dfu_write_transaction_cleanup(dfu); return ret; } int dfu_write(struct dfu_entity *dfu, void *buf, int size, int blk_seq_num) { - int ret = 0; - int tret; + int ret; debug("%s: name: %s buf: 0x%p size: 0x%x p_num: 0x%x offset: 0x%llx bufoffset: 0x%x\n", __func__, dfu->name, buf, size, blk_seq_num, dfu->offset, @@ -190,10 +196,10 @@ int dfu_write(struct dfu_entity *dfu, void *buf, int size, int blk_seq_num) dfu->offset = 0; dfu->bad_skip = 0; dfu->i_blk_seq_num = 0; - dfu->i_buf_start = dfu_get_buf(); + dfu->i_buf_start = dfu_get_buf(dfu); if (dfu->i_buf_start == NULL) return -ENOMEM; - dfu->i_buf_end = dfu_get_buf() + dfu_buf_size; + dfu->i_buf_end = dfu_get_buf(dfu) + dfu_buf_size; dfu->i_buf = dfu->i_buf_start; dfu->inited = 1; @@ -202,6 +208,7 @@ int dfu_write(struct dfu_entity *dfu, void *buf, int size, int blk_seq_num) if (dfu->i_blk_seq_num != blk_seq_num) { printf("%s: Wrong sequence number! [%d] [%d]\n", __func__, dfu->i_blk_seq_num, blk_seq_num); + dfu_write_transaction_cleanup(dfu); return -1; } @@ -223,15 +230,18 @@ int dfu_write(struct dfu_entity *dfu, void *buf, int size, int blk_seq_num) /* flush buffer if overflow */ if ((dfu->i_buf + size) > dfu->i_buf_end) { - tret = dfu_write_buffer_drain(dfu); - if (ret == 0) - ret = tret; + ret = dfu_write_buffer_drain(dfu); + if (ret) { + dfu_write_transaction_cleanup(dfu); + return ret; + } } /* we should be in buffer now (if not then size too large) */ if ((dfu->i_buf + size) > dfu->i_buf_end) { error("Buffer overflow! (0x%p + 0x%x > 0x%p)\n", dfu->i_buf, size, dfu->i_buf_end); + dfu_write_transaction_cleanup(dfu); return -1; } @@ -240,12 +250,14 @@ int dfu_write(struct dfu_entity *dfu, void *buf, int size, int blk_seq_num) /* if end or if buffer full flush */ if (size == 0 || (dfu->i_buf + size) > dfu->i_buf_end) { - tret = dfu_write_buffer_drain(dfu); - if (ret == 0) - ret = tret; + ret = dfu_write_buffer_drain(dfu); + if (ret) { + dfu_write_transaction_cleanup(dfu); + return ret; + } } - return ret; + return 0; } static int dfu_read_buffer_fill(struct dfu_entity *dfu, void *buf, int size) @@ -267,7 +279,6 @@ static int dfu_read_buffer_fill(struct dfu_entity *dfu, void *buf, int size) dfu->i_buf += chunk; dfu->b_left -= chunk; - dfu->r_left -= chunk; size -= chunk; buf += chunk; readn += chunk; @@ -309,14 +320,23 @@ int dfu_read(struct dfu_entity *dfu, void *buf, int size, int blk_seq_num) __func__, dfu->name, buf, size, blk_seq_num, dfu->i_buf); if (!dfu->inited) { - dfu->i_buf_start = dfu_get_buf(); + dfu->i_buf_start = dfu_get_buf(dfu); if (dfu->i_buf_start == NULL) return -ENOMEM; - ret = dfu->read_medium(dfu, 0, dfu->i_buf_start, &dfu->r_left); - if (ret != 0) { - debug("%s: failed to get r_left\n", __func__); - return ret; + dfu->r_left = dfu->get_medium_size(dfu); + if (dfu->r_left < 0) + return dfu->r_left; + switch (dfu->layout) { + case DFU_RAW_ADDR: + case DFU_RAM_ADDR: + break; + default: + if (dfu->r_left > dfu_buf_size) { + printf("%s: File too big for buffer\n", + __func__); + return -EOVERFLOW; + } } debug("%s: %s %ld [B]\n", __func__, dfu->name, dfu->r_left); @@ -324,9 +344,9 @@ int dfu_read(struct dfu_entity *dfu, void *buf, int size, int blk_seq_num) dfu->i_blk_seq_num = 0; dfu->crc = 0; dfu->offset = 0; - dfu->i_buf_end = dfu_get_buf() + dfu_buf_size; + dfu->i_buf_end = dfu_get_buf(dfu) + dfu_buf_size; dfu->i_buf = dfu->i_buf_start; - dfu->b_left = min(dfu_buf_size, dfu->r_left); + dfu->b_left = 0; dfu->bad_skip = 0; @@ -371,26 +391,30 @@ int dfu_read(struct dfu_entity *dfu, void *buf, int size, int blk_seq_num) } static int dfu_fill_entity(struct dfu_entity *dfu, char *s, int alt, - char *interface, int num) + char *interface, char *devstr) { char *st; - debug("%s: %s interface: %s num: %d\n", __func__, s, interface, num); + debug("%s: %s interface: %s dev: %s\n", __func__, s, interface, devstr); st = strsep(&s, " "); strcpy(dfu->name, st); - dfu->dev_num = num; dfu->alt = alt; + dfu->max_buf_size = 0; + dfu->free_entity = NULL; /* Specific for mmc device */ if (strcmp(interface, "mmc") == 0) { - if (dfu_fill_entity_mmc(dfu, s)) + if (dfu_fill_entity_mmc(dfu, devstr, s)) return -1; } else if (strcmp(interface, "nand") == 0) { - if (dfu_fill_entity_nand(dfu, s)) + if (dfu_fill_entity_nand(dfu, devstr, s)) return -1; } else if (strcmp(interface, "ram") == 0) { - if (dfu_fill_entity_ram(dfu, s)) + if (dfu_fill_entity_ram(dfu, devstr, s)) + return -1; + } else if (strcmp(interface, "sf") == 0) { + if (dfu_fill_entity_sf(dfu, devstr, s)) return -1; } else { printf("%s: Device %s not (yet) supported!\n", @@ -407,6 +431,8 @@ void dfu_free_entities(void) list_for_each_entry_safe_reverse(dfu, p, &dfu_list, list) { list_del(&dfu->list); + if (dfu->free_entity) + dfu->free_entity(dfu); t = dfu; } if (t) @@ -416,7 +442,7 @@ void dfu_free_entities(void) alt_num_cnt = 0; } -int dfu_config_entities(char *env, char *interface, int num) +int dfu_config_entities(char *env, char *interface, char *devstr) { struct dfu_entity *dfu; int i, ret; @@ -439,7 +465,8 @@ int dfu_config_entities(char *env, char *interface, int num) for (i = 0; i < dfu_alt_num; i++) { s = strsep(&env, ";"); - ret = dfu_fill_entity(&dfu[i], s, alt_num_cnt, interface, num); + ret = dfu_fill_entity(&dfu[i], s, alt_num_cnt, interface, + devstr); if (ret) return -1; diff --git a/drivers/dfu/dfu_mmc.c b/drivers/dfu/dfu_mmc.c index 63cc876..72fa03e 100644 --- a/drivers/dfu/dfu_mmc.c +++ b/drivers/dfu/dfu_mmc.c @@ -12,6 +12,8 @@ #include <errno.h> #include <div64.h> #include <dfu.h> +#include <ext4fs.h> +#include <fat.h> #include <mmc.h> static unsigned char __aligned(CONFIG_SYS_CACHELINE_SIZE) @@ -25,7 +27,7 @@ static int mmc_access_part(struct dfu_entity *dfu, struct mmc *mmc, int part) if (part == mmc->part_num) return 0; - ret = mmc_switch_part(dfu->dev_num, part); + ret = mmc_switch_part(dfu->data.mmc.dev_num, part); if (ret) { error("Cannot switch to partition %d\n", part); return ret; @@ -38,7 +40,7 @@ static int mmc_access_part(struct dfu_entity *dfu, struct mmc *mmc, int part) static int mmc_block_op(enum dfu_op op, struct dfu_entity *dfu, u64 offset, void *buf, long *len) { - struct mmc *mmc = find_mmc_device(dfu->dev_num); + struct mmc *mmc = find_mmc_device(dfu->data.mmc.dev_num); u32 blk_start, blk_count, n = 0; int ret, part_num_bkp = 0; @@ -65,15 +67,15 @@ static int mmc_block_op(enum dfu_op op, struct dfu_entity *dfu, } debug("%s: %s dev: %d start: %d cnt: %d buf: 0x%p\n", __func__, - op == DFU_OP_READ ? "MMC READ" : "MMC WRITE", dfu->dev_num, - blk_start, blk_count, buf); + op == DFU_OP_READ ? "MMC READ" : "MMC WRITE", + dfu->data.mmc.dev_num, blk_start, blk_count, buf); switch (op) { case DFU_OP_READ: - n = mmc->block_dev.block_read(dfu->dev_num, blk_start, + n = mmc->block_dev.block_read(dfu->data.mmc.dev_num, blk_start, blk_count, buf); break; case DFU_OP_WRITE: - n = mmc->block_dev.block_write(dfu->dev_num, blk_start, + n = mmc->block_dev.block_write(dfu->data.mmc.dev_num, blk_start, blk_count, buf); break; default: @@ -113,22 +115,17 @@ static int mmc_file_buffer(struct dfu_entity *dfu, void *buf, long *len) static int mmc_file_op(enum dfu_op op, struct dfu_entity *dfu, void *buf, long *len) { + const char *fsname, *opname; char cmd_buf[DFU_CMD_BUF_SIZE]; char *str_env; int ret; switch (dfu->layout) { case DFU_FS_FAT: - sprintf(cmd_buf, "fat%s mmc %d:%d 0x%x %s", - op == DFU_OP_READ ? "load" : "write", - dfu->data.mmc.dev, dfu->data.mmc.part, - (unsigned int) buf, dfu->name); + fsname = "fat"; break; case DFU_FS_EXT4: - sprintf(cmd_buf, "ext4%s mmc %d:%d 0x%x /%s", - op == DFU_OP_READ ? "load" : "write", - dfu->data.mmc.dev, dfu->data.mmc.part, - (unsigned int) buf, dfu->name); + fsname = "ext4"; break; default: printf("%s: Layout (%s) not (yet) supported!\n", __func__, @@ -136,6 +133,28 @@ static int mmc_file_op(enum dfu_op op, struct dfu_entity *dfu, return -1; } + switch (op) { + case DFU_OP_READ: + opname = "load"; + break; + case DFU_OP_WRITE: + opname = "write"; + break; + case DFU_OP_SIZE: + opname = "size"; + break; + default: + return -1; + } + + sprintf(cmd_buf, "%s%s mmc %d:%d", fsname, opname, + dfu->data.mmc.dev, dfu->data.mmc.part); + + if (op != DFU_OP_SIZE) + sprintf(cmd_buf + strlen(cmd_buf), " 0x%x", (unsigned int)buf); + + sprintf(cmd_buf + strlen(cmd_buf), " %s", dfu->name); + if (op == DFU_OP_WRITE) sprintf(cmd_buf + strlen(cmd_buf), " %lx", *len); @@ -147,7 +166,7 @@ static int mmc_file_op(enum dfu_op op, struct dfu_entity *dfu, return ret; } - if (dfu->layout != DFU_RAW_ADDR && op == DFU_OP_READ) { + if (op != DFU_OP_WRITE) { str_env = getenv("filesize"); if (str_env == NULL) { puts("dfu: Wrong file size!\n"); @@ -196,6 +215,27 @@ int dfu_flush_medium_mmc(struct dfu_entity *dfu) return ret; } +long dfu_get_medium_size_mmc(struct dfu_entity *dfu) +{ + int ret; + long len; + + switch (dfu->layout) { + case DFU_RAW_ADDR: + return dfu->data.mmc.lba_size * dfu->data.mmc.lba_blk_size; + case DFU_FS_FAT: + case DFU_FS_EXT4: + ret = mmc_file_op(DFU_OP_SIZE, dfu, NULL, &len); + if (ret < 0) + return ret; + return len; + default: + printf("%s: Layout (%s) not (yet) supported!\n", __func__, + dfu_get_layout(dfu->layout)); + return -1; + } +} + int dfu_read_medium_mmc(struct dfu_entity *dfu, u64 offset, void *buf, long *len) { @@ -230,7 +270,7 @@ int dfu_read_medium_mmc(struct dfu_entity *dfu, u64 offset, void *buf, * 4th (optional): * mmcpart <num> (access to HW eMMC partitions) */ -int dfu_fill_entity_mmc(struct dfu_entity *dfu, char *s) +int dfu_fill_entity_mmc(struct dfu_entity *dfu, char *devstr, char *s) { const char *entity_type; size_t second_arg; @@ -241,6 +281,8 @@ int dfu_fill_entity_mmc(struct dfu_entity *dfu, char *s) const char *argv[3]; const char **parg = argv; + dfu->data.mmc.dev_num = simple_strtoul(devstr, NULL, 10); + for (; parg < argv + sizeof(argv) / sizeof(*argv); ++parg) { *parg = strsep(&s, " "); if (*parg == NULL) { @@ -257,9 +299,10 @@ int dfu_fill_entity_mmc(struct dfu_entity *dfu, char *s) second_arg = simple_strtoul(argv[1], NULL, 0); third_arg = simple_strtoul(argv[2], NULL, 0); - mmc = find_mmc_device(dfu->dev_num); + mmc = find_mmc_device(dfu->data.mmc.dev_num); if (mmc == NULL) { - error("Couldn't find MMC device no. %d.\n", dfu->dev_num); + error("Couldn't find MMC device no. %d.\n", + dfu->data.mmc.dev_num); return -ENODEV; } @@ -316,6 +359,7 @@ int dfu_fill_entity_mmc(struct dfu_entity *dfu, char *s) } dfu->dev_type = DFU_DEV_MMC; + dfu->get_medium_size = dfu_get_medium_size_mmc; dfu->read_medium = dfu_read_medium_mmc; dfu->write_medium = dfu_write_medium_mmc; dfu->flush_medium = dfu_flush_medium_mmc; diff --git a/drivers/dfu/dfu_nand.c b/drivers/dfu/dfu_nand.c index ccdbef6..f9ee189 100644 --- a/drivers/dfu/dfu_nand.c +++ b/drivers/dfu/dfu_nand.c @@ -114,6 +114,11 @@ static int dfu_write_medium_nand(struct dfu_entity *dfu, return ret; } +long dfu_get_medium_size_nand(struct dfu_entity *dfu) +{ + return dfu->data.nand.size; +} + static int dfu_read_medium_nand(struct dfu_entity *dfu, u64 offset, void *buf, long *len) { @@ -121,7 +126,6 @@ static int dfu_read_medium_nand(struct dfu_entity *dfu, u64 offset, void *buf, switch (dfu->layout) { case DFU_RAW_ADDR: - *len = dfu->data.nand.size; ret = nand_block_read(dfu, offset, buf, len); break; default: @@ -175,7 +179,7 @@ unsigned int dfu_polltimeout_nand(struct dfu_entity *dfu) return DFU_DEFAULT_POLL_TIMEOUT; } -int dfu_fill_entity_nand(struct dfu_entity *dfu, char *s) +int dfu_fill_entity_nand(struct dfu_entity *dfu, char *devstr, char *s) { char *st; int ret, dev, part; @@ -220,6 +224,7 @@ int dfu_fill_entity_nand(struct dfu_entity *dfu, char *s) return -1; } + dfu->get_medium_size = dfu_get_medium_size_nand; dfu->read_medium = dfu_read_medium_nand; dfu->write_medium = dfu_write_medium_nand; dfu->flush_medium = dfu_flush_medium_nand; diff --git a/drivers/dfu/dfu_ram.c b/drivers/dfu/dfu_ram.c index 335a8e1..e094a94 100644 --- a/drivers/dfu/dfu_ram.c +++ b/drivers/dfu/dfu_ram.c @@ -41,18 +41,18 @@ static int dfu_write_medium_ram(struct dfu_entity *dfu, u64 offset, return dfu_transfer_medium_ram(DFU_OP_WRITE, dfu, offset, buf, len); } +long dfu_get_medium_size_ram(struct dfu_entity *dfu) +{ + return dfu->data.ram.size; +} + static int dfu_read_medium_ram(struct dfu_entity *dfu, u64 offset, void *buf, long *len) { - if (!*len) { - *len = dfu->data.ram.size; - return 0; - } - return dfu_transfer_medium_ram(DFU_OP_READ, dfu, offset, buf, len); } -int dfu_fill_entity_ram(struct dfu_entity *dfu, char *s) +int dfu_fill_entity_ram(struct dfu_entity *dfu, char *devstr, char *s) { char *st; @@ -69,6 +69,7 @@ int dfu_fill_entity_ram(struct dfu_entity *dfu, char *s) dfu->data.ram.size = simple_strtoul(s, &s, 16); dfu->write_medium = dfu_write_medium_ram; + dfu->get_medium_size = dfu_get_medium_size_ram; dfu->read_medium = dfu_read_medium_ram; dfu->inited = 0; diff --git a/drivers/dfu/dfu_sf.c b/drivers/dfu/dfu_sf.c new file mode 100644 index 0000000..91f6df2 --- /dev/null +++ b/drivers/dfu/dfu_sf.c @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2014, NVIDIA CORPORATION. All rights reserved. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <malloc.h> +#include <errno.h> +#include <div64.h> +#include <dfu.h> +#include <spi_flash.h> + +static long dfu_get_medium_size_sf(struct dfu_entity *dfu) +{ + return dfu->data.sf.size; +} + +static int dfu_read_medium_sf(struct dfu_entity *dfu, u64 offset, void *buf, + long *len) +{ + return spi_flash_read(dfu->data.sf.dev, offset, *len, buf); +} + +static int dfu_write_medium_sf(struct dfu_entity *dfu, + u64 offset, void *buf, long *len) +{ + int ret; + + ret = spi_flash_erase(dfu->data.sf.dev, offset, *len); + if (ret) + return ret; + + ret = spi_flash_write(dfu->data.sf.dev, offset, *len, buf); + if (ret) + return ret; + + return 0; +} + +static int dfu_flush_medium_sf(struct dfu_entity *dfu) +{ + return 0; +} + +static unsigned int dfu_polltimeout_sf(struct dfu_entity *dfu) +{ + return DFU_DEFAULT_POLL_TIMEOUT; +} + +static void dfu_free_entity_sf(struct dfu_entity *dfu) +{ + spi_flash_free(dfu->data.sf.dev); +} + +static struct spi_flash *parse_dev(char *devstr) +{ + unsigned int bus; + unsigned int cs; + unsigned int speed = CONFIG_SF_DEFAULT_SPEED; + unsigned int mode = CONFIG_SF_DEFAULT_MODE; + char *s, *endp; + struct spi_flash *dev; + + s = strsep(&devstr, ":"); + if (!s || !*s || (bus = simple_strtoul(s, &endp, 0), *endp)) { + printf("Invalid SPI bus %s\n", s); + return NULL; + } + + s = strsep(&devstr, ":"); + if (!s || !*s || (cs = simple_strtoul(s, &endp, 0), *endp)) { + printf("Invalid SPI chip-select %s\n", s); + return NULL; + } + + s = strsep(&devstr, ":"); + if (s && *s) { + speed = simple_strtoul(s, &endp, 0); + if (*endp || !speed) { + printf("Invalid SPI speed %s\n", s); + return NULL; + } + } + + s = strsep(&devstr, ":"); + if (s && *s) { + mode = simple_strtoul(s, &endp, 0); + if (*endp || mode > 3) { + printf("Invalid SPI mode %s\n", s); + return NULL; + } + } + + dev = spi_flash_probe(bus, cs, speed, mode); + if (!dev) { + printf("Failed to create SPI flash at %d:%d:%d:%d\n", + bus, cs, speed, mode); + return NULL; + } + + return dev; +} + +int dfu_fill_entity_sf(struct dfu_entity *dfu, char *devstr, char *s) +{ + char *st; + + dfu->data.sf.dev = parse_dev(devstr); + if (!dfu->data.sf.dev) + return -ENODEV; + + dfu->dev_type = DFU_DEV_SF; + dfu->max_buf_size = dfu->data.sf.dev->sector_size; + + st = strsep(&s, " "); + if (!strcmp(st, "raw")) { + dfu->layout = DFU_RAW_ADDR; + dfu->data.sf.start = simple_strtoul(s, &s, 16); + s++; + dfu->data.sf.size = simple_strtoul(s, &s, 16); + } else { + printf("%s: Memory layout (%s) not supported!\n", __func__, st); + spi_flash_free(dfu->data.sf.dev); + return -1; + } + + dfu->get_medium_size = dfu_get_medium_size_sf; + dfu->read_medium = dfu_read_medium_sf; + dfu->write_medium = dfu_write_medium_sf; + dfu->flush_medium = dfu_flush_medium_sf; + dfu->poll_timeout = dfu_polltimeout_sf; + dfu->free_entity = dfu_free_entity_sf; + + /* initial state */ + dfu->inited = 0; + + return 0; +} diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile index 8b2821b..a79c391 100644 --- a/drivers/dma/Makefile +++ b/drivers/dma/Makefile @@ -8,4 +8,3 @@ obj-$(CONFIG_FSLDMAFEC) += MCD_tasksInit.o MCD_dmaApi.o MCD_tasks.o obj-$(CONFIG_APBH_DMA) += apbh_dma.o obj-$(CONFIG_FSL_DMA) += fsl_dma.o -obj-$(CONFIG_OMAP3_DMA) += omap3_dma.o diff --git a/drivers/dma/omap3_dma.c b/drivers/dma/omap3_dma.c deleted file mode 100644 index 3320b3d..0000000 --- a/drivers/dma/omap3_dma.c +++ /dev/null @@ -1,167 +0,0 @@ -/* Copyright (C) 2011 - * Corscience GmbH & Co. KG - Simon Schwarz <schwarz@corscience.de> - * - * SPDX-License-Identifier: GPL-2.0+ - */ - -/* This is a basic implementation of the SDMA/DMA4 controller of OMAP3 - * Tested on Silicon Revision major:0x4 minor:0x0 - */ - -#include <common.h> -#include <asm/arch/cpu.h> -#include <asm/arch/omap3.h> -#include <asm/arch/dma.h> -#include <asm/io.h> -#include <asm/errno.h> - -static struct dma4 *dma4_cfg = (struct dma4 *)OMAP34XX_DMA4_BASE; -uint32_t dma_active; /* if a transfer is started the respective - bit is set for the logical channel */ - -/* Check if we have the given channel - * PARAMETERS: - * chan: Channel number - * - * RETURN of non-zero means error */ -static inline int check_channel(uint32_t chan) -{ - if (chan < CHAN_NR_MIN || chan > CHAN_NR_MAX) - return -EINVAL; - return 0; -} - -static inline void reset_irq(uint32_t chan) -{ - /* reset IRQ reason */ - writel(0x1DFE, &dma4_cfg->chan[chan].csr); - /* reset IRQ */ - writel((1 << chan), &dma4_cfg->irqstatus_l[0]); - dma_active &= ~(1 << chan); -} - -/* Set Source, Destination and Size of DMA transfer for the - * specified channel. - * PARAMETERS: - * chan: channel to use - * src: source of the transfer - * dst: destination of the transfer - * sze: Size of the transfer - * - * RETURN of non-zero means error */ -int omap3_dma_conf_transfer(uint32_t chan, uint32_t *src, uint32_t *dst, - uint32_t sze) -{ - if (check_channel(chan)) - return -EINVAL; - /* CDSA0 */ - writel((uint32_t)src, &dma4_cfg->chan[chan].cssa); - writel((uint32_t)dst, &dma4_cfg->chan[chan].cdsa); - writel(sze, &dma4_cfg->chan[chan].cen); -return 0; -} - -/* Start the DMA transfer */ -int omap3_dma_start_transfer(uint32_t chan) -{ - uint32_t val; - - if (check_channel(chan)) - return -EINVAL; - - val = readl(&dma4_cfg->chan[chan].ccr); - /* Test for channel already in use */ - if (val & CCR_ENABLE_ENABLE) - return -EBUSY; - - writel((val | CCR_ENABLE_ENABLE), &dma4_cfg->chan[chan].ccr); - dma_active |= (1 << chan); - debug("started transfer...\n"); - return 0; -} - -/* Busy-waiting for a DMA transfer - * This has to be called before another transfer is started - * PARAMETER - * chan: Channel to wait for - * - * RETURN of non-zero means error*/ -int omap3_dma_wait_for_transfer(uint32_t chan) -{ - uint32_t val; - - if (!(dma_active & (1 << chan))) { - val = readl(&dma4_cfg->irqstatus_l[0]); - if (!(val & chan)) { - debug("dma: The channel you are trying to wait for " - "was never activated - ERROR\n"); - return -1; /* channel was never active */ - } - } - - /* all irqs on line 0 */ - while (!(readl(&dma4_cfg->irqstatus_l[0]) & (1 << chan))) - asm("nop"); - - val = readl(&dma4_cfg->chan[chan].csr); - if ((val & CSR_TRANS_ERR) | (val & CSR_SUPERVISOR_ERR) | - (val & CSR_MISALIGNED_ADRS_ERR)) { - debug("err code: %X\n", val); - debug("dma: transfer error detected\n"); - reset_irq(chan); - return -1; - } - reset_irq(chan); - return 0; -} - -/* Get the revision of the DMA module - * PARAMETER - * minor: Address of minor revision to write - * major: Address of major revision to write - * - * RETURN of non-zero means error - */ -int omap3_dma_get_revision(uint32_t *minor, uint32_t *major) -{ - uint32_t val; - - /* debug information */ - val = readl(&dma4_cfg->revision); - *major = (val & 0x000000F0) >> 4; - *minor = (val & 0x0000000F); - debug("DMA Silicon revision (maj/min): 0x%X/0x%X\n", *major, *minor); - return 0; -} - -/* Initial config of omap dma - */ -void omap3_dma_init(void) -{ - dma_active = 0; - /* All interrupts on channel 0 */ - writel(0xFFFFFFFF, &dma4_cfg->irqenable_l[0]); -} - -/* set channel config to config - * - * RETURN of non-zero means error */ -int omap3_dma_conf_chan(uint32_t chan, struct dma4_chan *config) -{ - if (check_channel(chan)) - return -EINVAL; - - dma4_cfg->chan[chan] = *config; - return 0; -} - -/* get channel config to config - * - * RETURN of non-zero means error */ -int omap3_dma_get_conf_chan(uint32_t chan, struct dma4_chan *config) -{ - if (check_channel(chan)) - return -EINVAL; - *config = dma4_cfg->chan[chan]; - return 0; -} diff --git a/drivers/mtd/mtdconcat.c b/drivers/mtd/mtdconcat.c index 31e4289..39daeab 100644 --- a/drivers/mtd/mtdconcat.c +++ b/drivers/mtd/mtdconcat.c @@ -1,16 +1,32 @@ /* * MTD device concatenation layer * - * (C) 2002 Robert Kaiser <rkaiser@sysgo.de> + * Copyright © 2002 Robert Kaiser <rkaiser@sysgo.de> + * Copyright © 2002-2010 David Woodhouse <dwmw2@infradead.org> * * NAND support by Christian Gan <cgan@iders.ca> * - * This code is GPL + * SPDX-License-Identifier: GPL-2.0+ + * */ -#include <linux/mtd/mtd.h> +#define __UBOOT__ +#ifndef __UBOOT__ +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/sched.h> +#include <linux/types.h> +#include <linux/backing-dev.h> +#include <asm/div64.h> +#else +#include <div64.h> #include <linux/compat.h> +#endif + +#include <linux/mtd/mtd.h> #include <linux/mtd/concat.h> + #include <ubi_uboot.h> /* @@ -51,7 +67,9 @@ concat_read(struct mtd_info *mtd, loff_t from, size_t len, int ret = 0, err; int i; +#ifdef __UBOOT__ *retlen = 0; +#endif for (i = 0; i < concat->num_subdev; i++) { struct mtd_info *subdev = concat->subdev[i]; @@ -105,7 +123,9 @@ concat_write(struct mtd_info *mtd, loff_t to, size_t len, int err = -EINVAL; int i; +#ifdef __UBOOT__ *retlen = 0; +#endif for (i = 0; i < concat->num_subdev; i++) { struct mtd_info *subdev = concat->subdev[i]; @@ -137,6 +157,83 @@ concat_write(struct mtd_info *mtd, loff_t to, size_t len, return err; } +#ifndef __UBOOT__ +static int +concat_writev(struct mtd_info *mtd, const struct kvec *vecs, + unsigned long count, loff_t to, size_t * retlen) +{ + struct mtd_concat *concat = CONCAT(mtd); + struct kvec *vecs_copy; + unsigned long entry_low, entry_high; + size_t total_len = 0; + int i; + int err = -EINVAL; + + /* Calculate total length of data */ + for (i = 0; i < count; i++) + total_len += vecs[i].iov_len; + + /* Check alignment */ + if (mtd->writesize > 1) { + uint64_t __to = to; + if (do_div(__to, mtd->writesize) || (total_len % mtd->writesize)) + return -EINVAL; + } + + /* make a copy of vecs */ + vecs_copy = kmemdup(vecs, sizeof(struct kvec) * count, GFP_KERNEL); + if (!vecs_copy) + return -ENOMEM; + + entry_low = 0; + for (i = 0; i < concat->num_subdev; i++) { + struct mtd_info *subdev = concat->subdev[i]; + size_t size, wsize, retsize, old_iov_len; + + if (to >= subdev->size) { + to -= subdev->size; + continue; + } + + size = min_t(uint64_t, total_len, subdev->size - to); + wsize = size; /* store for future use */ + + entry_high = entry_low; + while (entry_high < count) { + if (size <= vecs_copy[entry_high].iov_len) + break; + size -= vecs_copy[entry_high++].iov_len; + } + + old_iov_len = vecs_copy[entry_high].iov_len; + vecs_copy[entry_high].iov_len = size; + + err = mtd_writev(subdev, &vecs_copy[entry_low], + entry_high - entry_low + 1, to, &retsize); + + vecs_copy[entry_high].iov_len = old_iov_len - size; + vecs_copy[entry_high].iov_base += size; + + entry_low = entry_high; + + if (err) + break; + + *retlen += retsize; + total_len -= wsize; + + if (total_len == 0) + break; + + err = -EINVAL; + to = 0; + } + + kfree(vecs_copy); + return err; +} +#endif + static int concat_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops) { @@ -204,7 +301,7 @@ concat_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops) if (!(mtd->flags & MTD_WRITEABLE)) return -EROFS; - ops->retlen = 0; + ops->retlen = ops->oobretlen = 0; for (i = 0; i < concat->num_subdev; i++) { struct mtd_info *subdev = concat->subdev[i]; @@ -219,7 +316,7 @@ concat_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops) devops.len = subdev->size - to; err = mtd_write_oob(subdev, to, &devops); - ops->retlen += devops.retlen; + ops->retlen += devops.oobretlen; if (err) return err; @@ -243,6 +340,9 @@ concat_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops) static void concat_erase_callback(struct erase_info *instr) { /* Nothing to do here in U-Boot */ +#ifndef __UBOOT__ + wake_up((wait_queue_head_t *) instr->priv); +#endif } static int concat_dev_erase(struct mtd_info *mtd, struct erase_info *erase) @@ -316,7 +416,7 @@ static int concat_erase(struct mtd_info *mtd, struct erase_info *instr) * to-be-erased area begins. Verify that the starting * offset is aligned to this region's erase size: */ - if (instr->addr & (erase_regions[i].erasesize - 1)) + if (i < 0 || instr->addr & (erase_regions[i].erasesize - 1)) return -EINVAL; /* @@ -329,8 +429,8 @@ static int concat_erase(struct mtd_info *mtd, struct erase_info *instr) /* * check if the ending offset is aligned to this region's erase size */ - if ((instr->addr + instr->len) & (erase_regions[i].erasesize - - 1)) + if (i < 0 || ((instr->addr + instr->len) & + (erase_regions[i].erasesize - 1))) return -EINVAL; } @@ -422,7 +522,6 @@ static int concat_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) size = len; err = mtd_lock(subdev, ofs, size); - if (err) break; @@ -457,7 +556,6 @@ static int concat_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len) size = len; err = mtd_unlock(subdev, ofs, size); - if (err) break; @@ -483,6 +581,32 @@ static void concat_sync(struct mtd_info *mtd) } } +#ifndef __UBOOT__ +static int concat_suspend(struct mtd_info *mtd) +{ + struct mtd_concat *concat = CONCAT(mtd); + int i, rc = 0; + + for (i = 0; i < concat->num_subdev; i++) { + struct mtd_info *subdev = concat->subdev[i]; + if ((rc = mtd_suspend(subdev)) < 0) + return rc; + } + return rc; +} + +static void concat_resume(struct mtd_info *mtd) +{ + struct mtd_concat *concat = CONCAT(mtd); + int i; + + for (i = 0; i < concat->num_subdev; i++) { + struct mtd_info *subdev = concat->subdev[i]; + mtd_resume(subdev); + } +} +#endif + static int concat_block_isbad(struct mtd_info *mtd, loff_t ofs) { struct mtd_concat *concat = CONCAT(mtd); @@ -511,9 +635,6 @@ static int concat_block_markbad(struct mtd_info *mtd, loff_t ofs) struct mtd_concat *concat = CONCAT(mtd); int i, err = -EINVAL; - if (!mtd_can_have_bb(concat->subdev[0])) - return 0; - for (i = 0; i < concat->num_subdev; i++) { struct mtd_info *subdev = concat->subdev[i]; @@ -532,6 +653,32 @@ static int concat_block_markbad(struct mtd_info *mtd, loff_t ofs) } /* + * try to support NOMMU mmaps on concatenated devices + * - we don't support subdev spanning as we can't guarantee it'll work + */ +static unsigned long concat_get_unmapped_area(struct mtd_info *mtd, + unsigned long len, + unsigned long offset, + unsigned long flags) +{ + struct mtd_concat *concat = CONCAT(mtd); + int i; + + for (i = 0; i < concat->num_subdev; i++) { + struct mtd_info *subdev = concat->subdev[i]; + + if (offset >= subdev->size) { + offset -= subdev->size; + continue; + } + + return mtd_get_unmapped_area(subdev, len, offset, flags); + } + + return (unsigned long) -ENOSYS; +} + +/* * This function constructs a virtual MTD device by concatenating * num_devs MTD devices. A pointer to the new device object is * stored to *new_dev upon success. This function does _not_ @@ -539,17 +686,22 @@ static int concat_block_markbad(struct mtd_info *mtd, loff_t ofs) */ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to concatenate */ int num_devs, /* number of subdevices */ +#ifndef __UBOOT__ const char *name) +#else + char *name) +#endif { /* name for the new device */ int i; size_t size; struct mtd_concat *concat; uint32_t max_erasesize, curr_erasesize; int num_erase_region; + int max_writebufsize = 0; debug("Concatenating MTD devices:\n"); for (i = 0; i < num_devs; i++) - debug("(%d): \"%s\"\n", i, subdev[i]->name); + printk(KERN_NOTICE "(%d): \"%s\"\n", i, subdev[i]->name); debug("into device \"%s\"\n", name); /* allocate the device structure */ @@ -565,16 +717,26 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to c /* * Set up the new "super" device's MTD object structure, check for - * incompatibilites between the subdevices. + * incompatibilities between the subdevices. */ concat->mtd.type = subdev[0]->type; concat->mtd.flags = subdev[0]->flags; concat->mtd.size = subdev[0]->size; concat->mtd.erasesize = subdev[0]->erasesize; concat->mtd.writesize = subdev[0]->writesize; + + for (i = 0; i < num_devs; i++) + if (max_writebufsize < subdev[i]->writebufsize) + max_writebufsize = subdev[i]->writebufsize; + concat->mtd.writebufsize = max_writebufsize; + concat->mtd.subpage_sft = subdev[0]->subpage_sft; concat->mtd.oobsize = subdev[0]->oobsize; concat->mtd.oobavail = subdev[0]->oobavail; +#ifndef __UBOOT__ + if (subdev[0]->_writev) + concat->mtd._writev = concat_writev; +#endif if (subdev[0]->_read_oob) concat->mtd._read_oob = concat_read_oob; if (subdev[0]->_write_oob) @@ -586,6 +748,10 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to c concat->mtd.ecc_stats.badblocks = subdev[0]->ecc_stats.badblocks; +#ifndef __UBOOT__ + concat->mtd.backing_dev_info = subdev[0]->backing_dev_info; +#endif + concat->subdev[0] = subdev[0]; for (i = 1; i < num_devs; i++) { @@ -613,6 +779,16 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to c subdev[i]->flags & MTD_WRITEABLE; } +#ifndef __UBOOT__ + /* only permit direct mapping if the BDIs are all the same + * - copy-mapping is still permitted + */ + if (concat->mtd.backing_dev_info != + subdev[i]->backing_dev_info) + concat->mtd.backing_dev_info = + &default_backing_dev_info; +#endif + concat->mtd.size += subdev[i]->size; concat->mtd.ecc_stats.badblocks += subdev[i]->ecc_stats.badblocks; @@ -641,6 +817,11 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to c concat->mtd._sync = concat_sync; concat->mtd._lock = concat_lock; concat->mtd._unlock = concat_unlock; +#ifndef __UBOOT__ + concat->mtd._suspend = concat_suspend; + concat->mtd._resume = concat_resume; +#endif + concat->mtd._get_unmapped_area = concat_get_unmapped_area; /* * Combine the erase block size info of the subdevices: @@ -771,3 +952,22 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to c return &concat->mtd; } + +/* + * This function destroys an MTD object obtained from concat_mtd_devs() + */ + +void mtd_concat_destroy(struct mtd_info *mtd) +{ + struct mtd_concat *concat = CONCAT(mtd); + if (concat->mtd.numeraseregions) + kfree(concat->mtd.eraseregions); + kfree(concat); +} + +EXPORT_SYMBOL(mtd_concat_create); +EXPORT_SYMBOL(mtd_concat_destroy); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Robert Kaiser <rkaiser@sysgo.de>"); +MODULE_DESCRIPTION("Generic support for concatenating of MTD devices"); diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index 0a38fbe..6ad0357 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -2,130 +2,769 @@ * Core registration and callback routines for MTD * drivers and users. * - * 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. + * Copyright © 1999-2010 David Woodhouse <dwmw2@infradead.org> + * Copyright © 2006 Red Hat UK Limited + * + * SPDX-License-Identifier: GPL-2.0+ + * */ -#include <linux/mtd/mtd.h> +#define __UBOOT__ +#ifndef __UBOOT__ +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/ptrace.h> +#include <linux/seq_file.h> +#include <linux/string.h> +#include <linux/timer.h> +#include <linux/major.h> +#include <linux/fs.h> +#include <linux/err.h> +#include <linux/ioctl.h> +#include <linux/init.h> +#include <linux/proc_fs.h> +#include <linux/idr.h> +#include <linux/backing-dev.h> +#include <linux/gfp.h> +#include <linux/slab.h> +#else #include <linux/compat.h> +#include <linux/err.h> #include <ubi_uboot.h> +#endif + +#include <linux/mtd/mtd.h> +#include <linux/mtd/partitions.h> + +#include "mtdcore.h" + +#ifndef __UBOOT__ +/* + * backing device capabilities for non-mappable devices (such as NAND flash) + * - permits private mappings, copies are taken of the data + */ +static struct backing_dev_info mtd_bdi_unmappable = { + .capabilities = BDI_CAP_MAP_COPY, +}; + +/* + * backing device capabilities for R/O mappable devices (such as ROM) + * - permits private mappings, copies are taken of the data + * - permits non-writable shared mappings + */ +static struct backing_dev_info mtd_bdi_ro_mappable = { + .capabilities = (BDI_CAP_MAP_COPY | BDI_CAP_MAP_DIRECT | + BDI_CAP_EXEC_MAP | BDI_CAP_READ_MAP), +}; +/* + * backing device capabilities for writable mappable devices (such as RAM) + * - permits private mappings, copies are taken of the data + * - permits non-writable shared mappings + */ +static struct backing_dev_info mtd_bdi_rw_mappable = { + .capabilities = (BDI_CAP_MAP_COPY | BDI_CAP_MAP_DIRECT | + BDI_CAP_EXEC_MAP | BDI_CAP_READ_MAP | + BDI_CAP_WRITE_MAP), +}; + +static int mtd_cls_suspend(struct device *dev, pm_message_t state); +static int mtd_cls_resume(struct device *dev); + +static struct class mtd_class = { + .name = "mtd", + .owner = THIS_MODULE, + .suspend = mtd_cls_suspend, + .resume = mtd_cls_resume, +}; +#else struct mtd_info *mtd_table[MAX_MTD_DEVICES]; +#define MAX_IDR_ID 64 + +struct idr_layer { + int used; + void *ptr; +}; + +struct idr { + struct idr_layer id[MAX_IDR_ID]; +}; + +#define DEFINE_IDR(name) struct idr name; + +void idr_remove(struct idr *idp, int id) +{ + if (idp->id[id].used) + idp->id[id].used = 0; + + return; +} +void *idr_find(struct idr *idp, int id) +{ + if (idp->id[id].used) + return idp->id[id].ptr; + + return NULL; +} + +void *idr_get_next(struct idr *idp, int *next) +{ + void *ret; + int id = *next; + + ret = idr_find(idp, id); + if (ret) { + id ++; + if (!idp->id[id].used) + id = 0; + *next = id; + } else { + *next = 0; + } + + return ret; +} + +int idr_alloc(struct idr *idp, void *ptr, int start, int end, gfp_t gfp_mask) +{ + struct idr_layer *idl; + int i = 0; + + while (i < MAX_IDR_ID) { + idl = &idp->id[i]; + if (idl->used == 0) { + idl->used = 1; + idl->ptr = ptr; + return i; + } + i++; + } + return -ENOSPC; +} +#endif + +static DEFINE_IDR(mtd_idr); + +/* These are exported solely for the purpose of mtd_blkdevs.c. You + should not use them for _anything_ else */ +DEFINE_MUTEX(mtd_table_mutex); +EXPORT_SYMBOL_GPL(mtd_table_mutex); + +struct mtd_info *__mtd_next_device(int i) +{ + return idr_get_next(&mtd_idr, &i); +} +EXPORT_SYMBOL_GPL(__mtd_next_device); + +#ifndef __UBOOT__ +static LIST_HEAD(mtd_notifiers); + + +#define MTD_DEVT(index) MKDEV(MTD_CHAR_MAJOR, (index)*2) + +/* REVISIT once MTD uses the driver model better, whoever allocates + * the mtd_info will probably want to use the release() hook... + */ +static void mtd_release(struct device *dev) +{ + struct mtd_info __maybe_unused *mtd = dev_get_drvdata(dev); + dev_t index = MTD_DEVT(mtd->index); + + /* remove /dev/mtdXro node if needed */ + if (index) + device_destroy(&mtd_class, index + 1); +} + +static int mtd_cls_suspend(struct device *dev, pm_message_t state) +{ + struct mtd_info *mtd = dev_get_drvdata(dev); + + return mtd ? mtd_suspend(mtd) : 0; +} + +static int mtd_cls_resume(struct device *dev) +{ + struct mtd_info *mtd = dev_get_drvdata(dev); + + if (mtd) + mtd_resume(mtd); + return 0; +} + +static ssize_t mtd_type_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct mtd_info *mtd = dev_get_drvdata(dev); + char *type; + + switch (mtd->type) { + case MTD_ABSENT: + type = "absent"; + break; + case MTD_RAM: + type = "ram"; + break; + case MTD_ROM: + type = "rom"; + break; + case MTD_NORFLASH: + type = "nor"; + break; + case MTD_NANDFLASH: + type = "nand"; + break; + case MTD_DATAFLASH: + type = "dataflash"; + break; + case MTD_UBIVOLUME: + type = "ubi"; + break; + case MTD_MLCNANDFLASH: + type = "mlc-nand"; + break; + default: + type = "unknown"; + } + + return snprintf(buf, PAGE_SIZE, "%s\n", type); +} +static DEVICE_ATTR(type, S_IRUGO, mtd_type_show, NULL); + +static ssize_t mtd_flags_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct mtd_info *mtd = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE, "0x%lx\n", (unsigned long)mtd->flags); + +} +static DEVICE_ATTR(flags, S_IRUGO, mtd_flags_show, NULL); + +static ssize_t mtd_size_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct mtd_info *mtd = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE, "%llu\n", + (unsigned long long)mtd->size); + +} +static DEVICE_ATTR(size, S_IRUGO, mtd_size_show, NULL); + +static ssize_t mtd_erasesize_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct mtd_info *mtd = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE, "%lu\n", (unsigned long)mtd->erasesize); + +} +static DEVICE_ATTR(erasesize, S_IRUGO, mtd_erasesize_show, NULL); + +static ssize_t mtd_writesize_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct mtd_info *mtd = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE, "%lu\n", (unsigned long)mtd->writesize); + +} +static DEVICE_ATTR(writesize, S_IRUGO, mtd_writesize_show, NULL); + +static ssize_t mtd_subpagesize_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct mtd_info *mtd = dev_get_drvdata(dev); + unsigned int subpagesize = mtd->writesize >> mtd->subpage_sft; + + return snprintf(buf, PAGE_SIZE, "%u\n", subpagesize); + +} +static DEVICE_ATTR(subpagesize, S_IRUGO, mtd_subpagesize_show, NULL); + +static ssize_t mtd_oobsize_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct mtd_info *mtd = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE, "%lu\n", (unsigned long)mtd->oobsize); + +} +static DEVICE_ATTR(oobsize, S_IRUGO, mtd_oobsize_show, NULL); + +static ssize_t mtd_numeraseregions_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct mtd_info *mtd = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE, "%u\n", mtd->numeraseregions); + +} +static DEVICE_ATTR(numeraseregions, S_IRUGO, mtd_numeraseregions_show, + NULL); + +static ssize_t mtd_name_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct mtd_info *mtd = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE, "%s\n", mtd->name); + +} +static DEVICE_ATTR(name, S_IRUGO, mtd_name_show, NULL); + +static ssize_t mtd_ecc_strength_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct mtd_info *mtd = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE, "%u\n", mtd->ecc_strength); +} +static DEVICE_ATTR(ecc_strength, S_IRUGO, mtd_ecc_strength_show, NULL); + +static ssize_t mtd_bitflip_threshold_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct mtd_info *mtd = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE, "%u\n", mtd->bitflip_threshold); +} + +static ssize_t mtd_bitflip_threshold_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct mtd_info *mtd = dev_get_drvdata(dev); + unsigned int bitflip_threshold; + int retval; + + retval = kstrtouint(buf, 0, &bitflip_threshold); + if (retval) + return retval; + + mtd->bitflip_threshold = bitflip_threshold; + return count; +} +static DEVICE_ATTR(bitflip_threshold, S_IRUGO | S_IWUSR, + mtd_bitflip_threshold_show, + mtd_bitflip_threshold_store); + +static ssize_t mtd_ecc_step_size_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct mtd_info *mtd = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE, "%u\n", mtd->ecc_step_size); + +} +static DEVICE_ATTR(ecc_step_size, S_IRUGO, mtd_ecc_step_size_show, NULL); + +static struct attribute *mtd_attrs[] = { + &dev_attr_type.attr, + &dev_attr_flags.attr, + &dev_attr_size.attr, + &dev_attr_erasesize.attr, + &dev_attr_writesize.attr, + &dev_attr_subpagesize.attr, + &dev_attr_oobsize.attr, + &dev_attr_numeraseregions.attr, + &dev_attr_name.attr, + &dev_attr_ecc_strength.attr, + &dev_attr_ecc_step_size.attr, + &dev_attr_bitflip_threshold.attr, + NULL, +}; +ATTRIBUTE_GROUPS(mtd); + +static struct device_type mtd_devtype = { + .name = "mtd", + .groups = mtd_groups, + .release = mtd_release, +}; +#endif + +/** + * add_mtd_device - register an MTD device + * @mtd: pointer to new MTD device info structure + * + * Add a device to the list of MTD devices present in the system, and + * notify each currently active MTD 'user' of its arrival. Returns + * zero on success or 1 on failure, which currently will only happen + * if there is insufficient memory or a sysfs error. + */ + int add_mtd_device(struct mtd_info *mtd) { - int i; +#ifndef __UBOOT__ + struct mtd_notifier *not; +#endif + int i, error; + +#ifndef __UBOOT__ + if (!mtd->backing_dev_info) { + switch (mtd->type) { + case MTD_RAM: + mtd->backing_dev_info = &mtd_bdi_rw_mappable; + break; + case MTD_ROM: + mtd->backing_dev_info = &mtd_bdi_ro_mappable; + break; + default: + mtd->backing_dev_info = &mtd_bdi_unmappable; + break; + } + } +#endif BUG_ON(mtd->writesize == 0); + mutex_lock(&mtd_table_mutex); - for (i = 0; i < MAX_MTD_DEVICES; i++) - if (!mtd_table[i]) { - mtd_table[i] = mtd; - mtd->index = i; - mtd->usecount = 0; + i = idr_alloc(&mtd_idr, mtd, 0, 0, GFP_KERNEL); + if (i < 0) + goto fail_locked; - /* default value if not set by driver */ - if (mtd->bitflip_threshold == 0) - mtd->bitflip_threshold = mtd->ecc_strength; + mtd->index = i; + mtd->usecount = 0; + /* default value if not set by driver */ + if (mtd->bitflip_threshold == 0) + mtd->bitflip_threshold = mtd->ecc_strength; - /* No need to get a refcount on the module containing - the notifier, since we hold the mtd_table_mutex */ + if (is_power_of_2(mtd->erasesize)) + mtd->erasesize_shift = ffs(mtd->erasesize) - 1; + else + mtd->erasesize_shift = 0; - /* We _know_ we aren't being removed, because - our caller is still holding us here. So none - of this try_ nonsense, and no bitching about it - either. :) */ - return 0; - } + if (is_power_of_2(mtd->writesize)) + mtd->writesize_shift = ffs(mtd->writesize) - 1; + else + mtd->writesize_shift = 0; + + mtd->erasesize_mask = (1 << mtd->erasesize_shift) - 1; + mtd->writesize_mask = (1 << mtd->writesize_shift) - 1; + + /* Some chips always power up locked. Unlock them now */ + if ((mtd->flags & MTD_WRITEABLE) && (mtd->flags & MTD_POWERUP_LOCK)) { + error = mtd_unlock(mtd, 0, mtd->size); + if (error && error != -EOPNOTSUPP) + printk(KERN_WARNING + "%s: unlock failed, writes may not work\n", + mtd->name); + } + +#ifndef __UBOOT__ + /* Caller should have set dev.parent to match the + * physical device. + */ + mtd->dev.type = &mtd_devtype; + mtd->dev.class = &mtd_class; + mtd->dev.devt = MTD_DEVT(i); + dev_set_name(&mtd->dev, "mtd%d", i); + dev_set_drvdata(&mtd->dev, mtd); + if (device_register(&mtd->dev) != 0) + goto fail_added; + + if (MTD_DEVT(i)) + device_create(&mtd_class, mtd->dev.parent, + MTD_DEVT(i) + 1, + NULL, "mtd%dro", i); + + pr_debug("mtd: Giving out device %d to %s\n", i, mtd->name); + /* No need to get a refcount on the module containing + the notifier, since we hold the mtd_table_mutex */ + list_for_each_entry(not, &mtd_notifiers, list) + not->add(mtd); +#else + pr_debug("mtd: Giving out device %d to %s\n", i, mtd->name); +#endif + mutex_unlock(&mtd_table_mutex); + /* We _know_ we aren't being removed, because + our caller is still holding us here. So none + of this try_ nonsense, and no bitching about it + either. :) */ + __module_get(THIS_MODULE); + return 0; + +#ifndef __UBOOT__ +fail_added: + idr_remove(&mtd_idr, i); +#endif +fail_locked: + mutex_unlock(&mtd_table_mutex); return 1; } /** - * del_mtd_device - unregister an MTD device - * @mtd: pointer to MTD device info structure + * del_mtd_device - unregister an MTD device + * @mtd: pointer to MTD device info structure * - * Remove a device from the list of MTD devices present in the system, - * and notify each currently active MTD 'user' of its departure. - * Returns zero on success or 1 on failure, which currently will happen - * if the requested device does not appear to be present in the list. + * Remove a device from the list of MTD devices present in the system, + * and notify each currently active MTD 'user' of its departure. + * Returns zero on success or 1 on failure, which currently will happen + * if the requested device does not appear to be present in the list. */ + int del_mtd_device(struct mtd_info *mtd) { int ret; +#ifndef __UBOOT__ + struct mtd_notifier *not; +#endif + + mutex_lock(&mtd_table_mutex); - if (mtd_table[mtd->index] != mtd) { + if (idr_find(&mtd_idr, mtd->index) != mtd) { ret = -ENODEV; - } else if (mtd->usecount) { - printk(KERN_NOTICE "Removing MTD device #%d (%s)" - " with use count %d\n", - mtd->index, mtd->name, mtd->usecount); + goto out_error; + } + +#ifndef __UBOOT__ + /* No need to get a refcount on the module containing + the notifier, since we hold the mtd_table_mutex */ + list_for_each_entry(not, &mtd_notifiers, list) + not->remove(mtd); +#endif + + if (mtd->usecount) { + printk(KERN_NOTICE "Removing MTD device #%d (%s) with use count %d\n", + mtd->index, mtd->name, mtd->usecount); ret = -EBUSY; } else { - /* No need to get a refcount on the module containing - * the notifier, since we hold the mtd_table_mutex */ - mtd_table[mtd->index] = NULL; +#ifndef __UBOOT__ + device_unregister(&mtd->dev); +#endif + idr_remove(&mtd_idr, mtd->index); + + module_put(THIS_MODULE); ret = 0; } +out_error: + mutex_unlock(&mtd_table_mutex); return ret; } +#ifndef __UBOOT__ +/** + * mtd_device_parse_register - parse partitions and register an MTD device. + * + * @mtd: the MTD device to register + * @types: the list of MTD partition probes to try, see + * 'parse_mtd_partitions()' for more information + * @parser_data: MTD partition parser-specific data + * @parts: fallback partition information to register, if parsing fails; + * only valid if %nr_parts > %0 + * @nr_parts: the number of partitions in parts, if zero then the full + * MTD device is registered if no partition info is found + * + * This function aggregates MTD partitions parsing (done by + * 'parse_mtd_partitions()') and MTD device and partitions registering. It + * basically follows the most common pattern found in many MTD drivers: + * + * * It first tries to probe partitions on MTD device @mtd using parsers + * specified in @types (if @types is %NULL, then the default list of parsers + * is used, see 'parse_mtd_partitions()' for more information). If none are + * found this functions tries to fallback to information specified in + * @parts/@nr_parts. + * * If any partitioning info was found, this function registers the found + * partitions. + * * If no partitions were found this function just registers the MTD device + * @mtd and exits. + * + * Returns zero in case of success and a negative error code in case of failure. + */ +int mtd_device_parse_register(struct mtd_info *mtd, const char * const *types, + struct mtd_part_parser_data *parser_data, + const struct mtd_partition *parts, + int nr_parts) +{ + int err; + struct mtd_partition *real_parts; + + err = parse_mtd_partitions(mtd, types, &real_parts, parser_data); + if (err <= 0 && nr_parts && parts) { + real_parts = kmemdup(parts, sizeof(*parts) * nr_parts, + GFP_KERNEL); + if (!real_parts) + err = -ENOMEM; + else + err = nr_parts; + } + + if (err > 0) { + err = add_mtd_partitions(mtd, real_parts, err); + kfree(real_parts); + } else if (err == 0) { + err = add_mtd_device(mtd); + if (err == 1) + err = -ENODEV; + } + + return err; +} +EXPORT_SYMBOL_GPL(mtd_device_parse_register); + +/** + * mtd_device_unregister - unregister an existing MTD device. + * + * @master: the MTD device to unregister. This will unregister both the master + * and any partitions if registered. + */ +int mtd_device_unregister(struct mtd_info *master) +{ + int err; + + err = del_mtd_partitions(master); + if (err) + return err; + + if (!device_is_registered(&master->dev)) + return 0; + + return del_mtd_device(master); +} +EXPORT_SYMBOL_GPL(mtd_device_unregister); + +/** + * register_mtd_user - register a 'user' of MTD devices. + * @new: pointer to notifier info structure + * + * Registers a pair of callbacks function to be called upon addition + * or removal of MTD devices. Causes the 'add' callback to be immediately + * invoked for each MTD device currently present in the system. + */ +void register_mtd_user (struct mtd_notifier *new) +{ + struct mtd_info *mtd; + + mutex_lock(&mtd_table_mutex); + + list_add(&new->list, &mtd_notifiers); + + __module_get(THIS_MODULE); + + mtd_for_each_device(mtd) + new->add(mtd); + + mutex_unlock(&mtd_table_mutex); +} +EXPORT_SYMBOL_GPL(register_mtd_user); + +/** + * unregister_mtd_user - unregister a 'user' of MTD devices. + * @old: pointer to notifier info structure + * + * Removes a callback function pair from the list of 'users' to be + * notified upon addition or removal of MTD devices. Causes the + * 'remove' callback to be immediately invoked for each MTD device + * currently present in the system. + */ +int unregister_mtd_user (struct mtd_notifier *old) +{ + struct mtd_info *mtd; + + mutex_lock(&mtd_table_mutex); + + module_put(THIS_MODULE); + + mtd_for_each_device(mtd) + old->remove(mtd); + + list_del(&old->list); + mutex_unlock(&mtd_table_mutex); + return 0; +} +EXPORT_SYMBOL_GPL(unregister_mtd_user); +#endif + /** * get_mtd_device - obtain a validated handle for an MTD device * @mtd: last known address of the required MTD device * @num: internal device number of the required MTD device * * Given a number and NULL address, return the num'th entry in the device - * table, if any. Given an address and num == -1, search the device table - * for a device with that address and return if it's still present. Given - * both, return the num'th driver only if its address matches. Return - * error code if not. + * table, if any. Given an address and num == -1, search the device table + * for a device with that address and return if it's still present. Given + * both, return the num'th driver only if its address matches. Return + * error code if not. */ struct mtd_info *get_mtd_device(struct mtd_info *mtd, int num) { - struct mtd_info *ret = NULL; - int i, err = -ENODEV; + struct mtd_info *ret = NULL, *other; + int err = -ENODEV; + + mutex_lock(&mtd_table_mutex); if (num == -1) { - for (i = 0; i < MAX_MTD_DEVICES; i++) - if (mtd_table[i] == mtd) - ret = mtd_table[i]; - } else if (num < MAX_MTD_DEVICES) { - ret = mtd_table[num]; + mtd_for_each_device(other) { + if (other == mtd) { + ret = mtd; + break; + } + } + } else if (num >= 0) { + ret = idr_find(&mtd_idr, num); if (mtd && mtd != ret) ret = NULL; } - if (!ret) - goto out_unlock; + if (!ret) { + ret = ERR_PTR(err); + goto out; + } - ret->usecount++; + err = __get_mtd_device(ret); + if (err) + ret = ERR_PTR(err); +out: + mutex_unlock(&mtd_table_mutex); return ret; +} +EXPORT_SYMBOL_GPL(get_mtd_device); -out_unlock: - return ERR_PTR(err); + +int __get_mtd_device(struct mtd_info *mtd) +{ + int err; + + if (!try_module_get(mtd->owner)) + return -ENODEV; + + if (mtd->_get_device) { + err = mtd->_get_device(mtd); + + if (err) { + module_put(mtd->owner); + return err; + } + } + mtd->usecount++; + return 0; } +EXPORT_SYMBOL_GPL(__get_mtd_device); /** - * get_mtd_device_nm - obtain a validated handle for an MTD device by - * device name - * @name: MTD device name to open + * get_mtd_device_nm - obtain a validated handle for an MTD device by + * device name + * @name: MTD device name to open * - * This function returns MTD device description structure in case of - * success and an error code in case of failure. + * This function returns MTD device description structure in case of + * success and an error code in case of failure. */ struct mtd_info *get_mtd_device_nm(const char *name) { - int i, err = -ENODEV; - struct mtd_info *mtd = NULL; + int err = -ENODEV; + struct mtd_info *mtd = NULL, *other; + + mutex_lock(&mtd_table_mutex); - for (i = 0; i < MAX_MTD_DEVICES; i++) { - if (mtd_table[i] && !strcmp(name, mtd_table[i]->name)) { - mtd = mtd_table[i]; + mtd_for_each_device(other) { + if (!strcmp(name, other->name)) { + mtd = other; break; } } @@ -133,20 +772,18 @@ struct mtd_info *get_mtd_device_nm(const char *name) if (!mtd) goto out_unlock; - mtd->usecount++; + err = __get_mtd_device(mtd); + if (err) + goto out_unlock; + + mutex_unlock(&mtd_table_mutex); return mtd; out_unlock: + mutex_unlock(&mtd_table_mutex); return ERR_PTR(err); } - -void put_mtd_device(struct mtd_info *mtd) -{ - int c; - - c = --mtd->usecount; - BUG_ON(c < 0); -} +EXPORT_SYMBOL_GPL(get_mtd_device_nm); #if defined(CONFIG_CMD_MTDPARTS_SPREAD) /** @@ -192,7 +829,28 @@ void mtd_get_len_incl_bad(struct mtd_info *mtd, uint64_t offset, } #endif /* defined(CONFIG_CMD_MTDPARTS_SPREAD) */ - /* +void put_mtd_device(struct mtd_info *mtd) +{ + mutex_lock(&mtd_table_mutex); + __put_mtd_device(mtd); + mutex_unlock(&mtd_table_mutex); + +} +EXPORT_SYMBOL_GPL(put_mtd_device); + +void __put_mtd_device(struct mtd_info *mtd) +{ + --mtd->usecount; + BUG_ON(mtd->usecount < 0); + + if (mtd->_put_device) + mtd->_put_device(mtd); + + module_put(mtd->owner); +} +EXPORT_SYMBOL_GPL(__put_mtd_device); + +/* * Erase is an asynchronous operation. Device drivers are supposed * to call instr->callback() whenever the operation completes, even * if it completes with a failure. @@ -213,11 +871,64 @@ int mtd_erase(struct mtd_info *mtd, struct erase_info *instr) } return mtd->_erase(mtd, instr); } +EXPORT_SYMBOL_GPL(mtd_erase); + +#ifndef __UBOOT__ +/* + * This stuff for eXecute-In-Place. phys is optional and may be set to NULL. + */ +int mtd_point(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, + void **virt, resource_size_t *phys) +{ + *retlen = 0; + *virt = NULL; + if (phys) + *phys = 0; + if (!mtd->_point) + return -EOPNOTSUPP; + if (from < 0 || from > mtd->size || len > mtd->size - from) + return -EINVAL; + if (!len) + return 0; + return mtd->_point(mtd, from, len, retlen, virt, phys); +} +EXPORT_SYMBOL_GPL(mtd_point); + +/* We probably shouldn't allow XIP if the unpoint isn't a NULL */ +int mtd_unpoint(struct mtd_info *mtd, loff_t from, size_t len) +{ + if (!mtd->_point) + return -EOPNOTSUPP; + if (from < 0 || from > mtd->size || len > mtd->size - from) + return -EINVAL; + if (!len) + return 0; + return mtd->_unpoint(mtd, from, len); +} +EXPORT_SYMBOL_GPL(mtd_unpoint); +#endif + +/* + * Allow NOMMU mmap() to directly map the device (if not NULL) + * - return the address to which the offset maps + * - return -ENOSYS to indicate refusal to do the mapping + */ +unsigned long mtd_get_unmapped_area(struct mtd_info *mtd, unsigned long len, + unsigned long offset, unsigned long flags) +{ + if (!mtd->_get_unmapped_area) + return -EOPNOTSUPP; + if (offset > mtd->size || len > mtd->size - offset) + return -EINVAL; + return mtd->_get_unmapped_area(mtd, len, offset, flags); +} +EXPORT_SYMBOL_GPL(mtd_get_unmapped_area); int mtd_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) { int ret_code; + *retlen = 0; if (from < 0 || from > mtd->size || len > mtd->size - from) return -EINVAL; if (!len) @@ -235,6 +946,7 @@ int mtd_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, return 0; /* device lacks ecc */ return ret_code >= mtd->bitflip_threshold ? -EUCLEAN : 0; } +EXPORT_SYMBOL_GPL(mtd_read); int mtd_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf) @@ -248,6 +960,7 @@ int mtd_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, return 0; return mtd->_write(mtd, to, len, retlen, buf); } +EXPORT_SYMBOL_GPL(mtd_write); /* * In blackbox flight recorder like scenarios we want to make successful writes @@ -270,29 +983,44 @@ int mtd_panic_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, return 0; return mtd->_panic_write(mtd, to, len, retlen, buf); } +EXPORT_SYMBOL_GPL(mtd_panic_write); int mtd_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops) { + int ret_code; ops->retlen = ops->oobretlen = 0; if (!mtd->_read_oob) return -EOPNOTSUPP; - return mtd->_read_oob(mtd, from, ops); + /* + * In cases where ops->datbuf != NULL, mtd->_read_oob() has semantics + * similar to mtd->_read(), returning a non-negative integer + * representing max bitflips. In other cases, mtd->_read_oob() may + * return -EUCLEAN. In all cases, perform similar logic to mtd_read(). + */ + ret_code = mtd->_read_oob(mtd, from, ops); + if (unlikely(ret_code < 0)) + return ret_code; + if (mtd->ecc_strength == 0) + return 0; /* device lacks ecc */ + return ret_code >= mtd->bitflip_threshold ? -EUCLEAN : 0; } +EXPORT_SYMBOL_GPL(mtd_read_oob); /* * Method to access the protection register area, present in some flash * devices. The user data is one time programmable but the factory data is read * only. */ -int mtd_get_fact_prot_info(struct mtd_info *mtd, struct otp_info *buf, - size_t len) +int mtd_get_fact_prot_info(struct mtd_info *mtd, size_t len, size_t *retlen, + struct otp_info *buf) { if (!mtd->_get_fact_prot_info) return -EOPNOTSUPP; if (!len) return 0; - return mtd->_get_fact_prot_info(mtd, buf, len); + return mtd->_get_fact_prot_info(mtd, len, retlen, buf); } +EXPORT_SYMBOL_GPL(mtd_get_fact_prot_info); int mtd_read_fact_prot_reg(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) @@ -304,16 +1032,18 @@ int mtd_read_fact_prot_reg(struct mtd_info *mtd, loff_t from, size_t len, return 0; return mtd->_read_fact_prot_reg(mtd, from, len, retlen, buf); } +EXPORT_SYMBOL_GPL(mtd_read_fact_prot_reg); -int mtd_get_user_prot_info(struct mtd_info *mtd, struct otp_info *buf, - size_t len) +int mtd_get_user_prot_info(struct mtd_info *mtd, size_t len, size_t *retlen, + struct otp_info *buf) { if (!mtd->_get_user_prot_info) return -EOPNOTSUPP; if (!len) return 0; - return mtd->_get_user_prot_info(mtd, buf, len); + return mtd->_get_user_prot_info(mtd, len, retlen, buf); } +EXPORT_SYMBOL_GPL(mtd_get_user_prot_info); int mtd_read_user_prot_reg(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) @@ -325,17 +1055,29 @@ int mtd_read_user_prot_reg(struct mtd_info *mtd, loff_t from, size_t len, return 0; return mtd->_read_user_prot_reg(mtd, from, len, retlen, buf); } +EXPORT_SYMBOL_GPL(mtd_read_user_prot_reg); int mtd_write_user_prot_reg(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, u_char *buf) { + int ret; + *retlen = 0; if (!mtd->_write_user_prot_reg) return -EOPNOTSUPP; if (!len) return 0; - return mtd->_write_user_prot_reg(mtd, to, len, retlen, buf); + ret = mtd->_write_user_prot_reg(mtd, to, len, retlen, buf); + if (ret) + return ret; + + /* + * If no data could be written at all, we are out of memory and + * must return -ENOSPC. + */ + return (*retlen) ? 0 : -ENOSPC; } +EXPORT_SYMBOL_GPL(mtd_write_user_prot_reg); int mtd_lock_user_prot_reg(struct mtd_info *mtd, loff_t from, size_t len) { @@ -345,6 +1087,7 @@ int mtd_lock_user_prot_reg(struct mtd_info *mtd, loff_t from, size_t len) return 0; return mtd->_lock_user_prot_reg(mtd, from, len); } +EXPORT_SYMBOL_GPL(mtd_lock_user_prot_reg); /* Chip-supported device locking */ int mtd_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) @@ -357,6 +1100,7 @@ int mtd_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) return 0; return mtd->_lock(mtd, ofs, len); } +EXPORT_SYMBOL_GPL(mtd_lock); int mtd_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len) { @@ -368,6 +1112,19 @@ int mtd_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len) return 0; return mtd->_unlock(mtd, ofs, len); } +EXPORT_SYMBOL_GPL(mtd_unlock); + +int mtd_is_locked(struct mtd_info *mtd, loff_t ofs, uint64_t len) +{ + if (!mtd->_is_locked) + return -EOPNOTSUPP; + if (ofs < 0 || ofs > mtd->size || len > mtd->size - ofs) + return -EINVAL; + if (!len) + return 0; + return mtd->_is_locked(mtd, ofs, len); +} +EXPORT_SYMBOL_GPL(mtd_is_locked); int mtd_block_isbad(struct mtd_info *mtd, loff_t ofs) { @@ -377,6 +1134,7 @@ int mtd_block_isbad(struct mtd_info *mtd, loff_t ofs) return -EINVAL; return mtd->_block_isbad(mtd, ofs); } +EXPORT_SYMBOL_GPL(mtd_block_isbad); int mtd_block_markbad(struct mtd_info *mtd, loff_t ofs) { @@ -388,3 +1146,225 @@ int mtd_block_markbad(struct mtd_info *mtd, loff_t ofs) return -EROFS; return mtd->_block_markbad(mtd, ofs); } +EXPORT_SYMBOL_GPL(mtd_block_markbad); + +#ifndef __UBOOT__ +/* + * default_mtd_writev - the default writev method + * @mtd: mtd device description object pointer + * @vecs: the vectors to write + * @count: count of vectors in @vecs + * @to: the MTD device offset to write to + * @retlen: on exit contains the count of bytes written to the MTD device. + * + * This function returns zero in case of success and a negative error code in + * case of failure. + */ +static int default_mtd_writev(struct mtd_info *mtd, const struct kvec *vecs, + unsigned long count, loff_t to, size_t *retlen) +{ + unsigned long i; + size_t totlen = 0, thislen; + int ret = 0; + + for (i = 0; i < count; i++) { + if (!vecs[i].iov_len) + continue; + ret = mtd_write(mtd, to, vecs[i].iov_len, &thislen, + vecs[i].iov_base); + totlen += thislen; + if (ret || thislen != vecs[i].iov_len) + break; + to += vecs[i].iov_len; + } + *retlen = totlen; + return ret; +} + +/* + * mtd_writev - the vector-based MTD write method + * @mtd: mtd device description object pointer + * @vecs: the vectors to write + * @count: count of vectors in @vecs + * @to: the MTD device offset to write to + * @retlen: on exit contains the count of bytes written to the MTD device. + * + * This function returns zero in case of success and a negative error code in + * case of failure. + */ +int mtd_writev(struct mtd_info *mtd, const struct kvec *vecs, + unsigned long count, loff_t to, size_t *retlen) +{ + *retlen = 0; + if (!(mtd->flags & MTD_WRITEABLE)) + return -EROFS; + if (!mtd->_writev) + return default_mtd_writev(mtd, vecs, count, to, retlen); + return mtd->_writev(mtd, vecs, count, to, retlen); +} +EXPORT_SYMBOL_GPL(mtd_writev); + +/** + * mtd_kmalloc_up_to - allocate a contiguous buffer up to the specified size + * @mtd: mtd device description object pointer + * @size: a pointer to the ideal or maximum size of the allocation, points + * to the actual allocation size on success. + * + * This routine attempts to allocate a contiguous kernel buffer up to + * the specified size, backing off the size of the request exponentially + * until the request succeeds or until the allocation size falls below + * the system page size. This attempts to make sure it does not adversely + * impact system performance, so when allocating more than one page, we + * ask the memory allocator to avoid re-trying, swapping, writing back + * or performing I/O. + * + * Note, this function also makes sure that the allocated buffer is aligned to + * the MTD device's min. I/O unit, i.e. the "mtd->writesize" value. + * + * This is called, for example by mtd_{read,write} and jffs2_scan_medium, + * to handle smaller (i.e. degraded) buffer allocations under low- or + * fragmented-memory situations where such reduced allocations, from a + * requested ideal, are allowed. + * + * Returns a pointer to the allocated buffer on success; otherwise, NULL. + */ +void *mtd_kmalloc_up_to(const struct mtd_info *mtd, size_t *size) +{ + gfp_t flags = __GFP_NOWARN | __GFP_WAIT | + __GFP_NORETRY | __GFP_NO_KSWAPD; + size_t min_alloc = max_t(size_t, mtd->writesize, PAGE_SIZE); + void *kbuf; + + *size = min_t(size_t, *size, KMALLOC_MAX_SIZE); + + while (*size > min_alloc) { + kbuf = kmalloc(*size, flags); + if (kbuf) + return kbuf; + + *size >>= 1; + *size = ALIGN(*size, mtd->writesize); + } + + /* + * For the last resort allocation allow 'kmalloc()' to do all sorts of + * things (write-back, dropping caches, etc) by using GFP_KERNEL. + */ + return kmalloc(*size, GFP_KERNEL); +} +EXPORT_SYMBOL_GPL(mtd_kmalloc_up_to); +#endif + +#ifdef CONFIG_PROC_FS + +/*====================================================================*/ +/* Support for /proc/mtd */ + +static int mtd_proc_show(struct seq_file *m, void *v) +{ + struct mtd_info *mtd; + + seq_puts(m, "dev: size erasesize name\n"); + mutex_lock(&mtd_table_mutex); + mtd_for_each_device(mtd) { + seq_printf(m, "mtd%d: %8.8llx %8.8x \"%s\"\n", + mtd->index, (unsigned long long)mtd->size, + mtd->erasesize, mtd->name); + } + mutex_unlock(&mtd_table_mutex); + return 0; +} + +static int mtd_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, mtd_proc_show, NULL); +} + +static const struct file_operations mtd_proc_ops = { + .open = mtd_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; +#endif /* CONFIG_PROC_FS */ + +/*====================================================================*/ +/* Init code */ + +#ifndef __UBOOT__ +static int __init mtd_bdi_init(struct backing_dev_info *bdi, const char *name) +{ + int ret; + + ret = bdi_init(bdi); + if (!ret) + ret = bdi_register(bdi, NULL, "%s", name); + + if (ret) + bdi_destroy(bdi); + + return ret; +} + +static struct proc_dir_entry *proc_mtd; + +static int __init init_mtd(void) +{ + int ret; + + ret = class_register(&mtd_class); + if (ret) + goto err_reg; + + ret = mtd_bdi_init(&mtd_bdi_unmappable, "mtd-unmap"); + if (ret) + goto err_bdi1; + + ret = mtd_bdi_init(&mtd_bdi_ro_mappable, "mtd-romap"); + if (ret) + goto err_bdi2; + + ret = mtd_bdi_init(&mtd_bdi_rw_mappable, "mtd-rwmap"); + if (ret) + goto err_bdi3; + + proc_mtd = proc_create("mtd", 0, NULL, &mtd_proc_ops); + + ret = init_mtdchar(); + if (ret) + goto out_procfs; + + return 0; + +out_procfs: + if (proc_mtd) + remove_proc_entry("mtd", NULL); +err_bdi3: + bdi_destroy(&mtd_bdi_ro_mappable); +err_bdi2: + bdi_destroy(&mtd_bdi_unmappable); +err_bdi1: + class_unregister(&mtd_class); +err_reg: + pr_err("Error registering mtd class or bdi: %d\n", ret); + return ret; +} + +static void __exit cleanup_mtd(void) +{ + cleanup_mtdchar(); + if (proc_mtd) + remove_proc_entry("mtd", NULL); + class_unregister(&mtd_class); + bdi_destroy(&mtd_bdi_unmappable); + bdi_destroy(&mtd_bdi_ro_mappable); + bdi_destroy(&mtd_bdi_rw_mappable); +} + +module_init(init_mtd); +module_exit(cleanup_mtd); +#endif + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>"); +MODULE_DESCRIPTION("Core MTD registration and access routines"); diff --git a/drivers/mtd/mtdcore.h b/drivers/mtd/mtdcore.h new file mode 100644 index 0000000..7b03533 --- /dev/null +++ b/drivers/mtd/mtdcore.h @@ -0,0 +1,23 @@ +/* + * These are exported solely for the purpose of mtd_blkdevs.c and mtdchar.c. + * You should not use them for _anything_ else. + */ + +extern struct mutex mtd_table_mutex; + +struct mtd_info *__mtd_next_device(int i); +int add_mtd_device(struct mtd_info *mtd); +int del_mtd_device(struct mtd_info *mtd); +int add_mtd_partitions(struct mtd_info *, const struct mtd_partition *, int); +int del_mtd_partitions(struct mtd_info *); +int parse_mtd_partitions(struct mtd_info *master, const char * const *types, + struct mtd_partition **pparts, + struct mtd_part_parser_data *data); + +int __init init_mtdchar(void); +void __exit cleanup_mtdchar(void); + +#define mtd_for_each_device(mtd) \ + for ((mtd) = __mtd_next_device(0); \ + (mtd) != NULL; \ + (mtd) = __mtd_next_device(mtd->index + 1)) diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c index 146ce11..2f20b92 100644 --- a/drivers/mtd/mtdpart.c +++ b/drivers/mtd/mtdpart.c @@ -1,35 +1,50 @@ /* * Simple MTD partitioning layer * - * (C) 2000 Nicolas Pitre <nico@cam.org> + * Copyright © 2000 Nicolas Pitre <nico@fluxnic.net> + * Copyright © 2002 Thomas Gleixner <gleixner@linutronix.de> + * Copyright © 2000-2010 David Woodhouse <dwmw2@infradead.org> * - * This code is GPL + * SPDX-License-Identifier: GPL-2.0+ * - * 02-21-2002 Thomas Gleixner <gleixner@autronix.de> - * added support for read_oob, write_oob */ +#define __UBOOT__ +#ifndef __UBOOT__ +#include <linux/module.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/list.h> +#include <linux/kmod.h> +#endif + #include <common.h> #include <malloc.h> #include <asm/errno.h> +#include <linux/compat.h> +#include <ubi_uboot.h> -#include <linux/types.h> -#include <linux/list.h> #include <linux/mtd/mtd.h> #include <linux/mtd/partitions.h> -#include <linux/compat.h> +#include <linux/err.h> + +#include "mtdcore.h" /* Our partition linked list */ -struct list_head mtd_partitions; +static LIST_HEAD(mtd_partitions); +#ifndef __UBOOT__ +static DEFINE_MUTEX(mtd_partitions_mutex); +#else +DEFINE_MUTEX(mtd_partitions_mutex); +#endif /* Our partition node structure */ struct mtd_part { struct mtd_info mtd; struct mtd_info *master; uint64_t offset; - int index; struct list_head list; - int registered; }; /* @@ -39,6 +54,30 @@ struct mtd_part { #define PART(x) ((struct mtd_part *)(x)) +#ifdef __UBOOT__ +/* from mm/util.c */ + +/** + * kstrdup - allocate space for and copy an existing string + * @s: the string to duplicate + * @gfp: the GFP mask used in the kmalloc() call when allocating memory + */ +char *kstrdup(const char *s, gfp_t gfp) +{ + size_t len; + char *buf; + + if (!s) + return NULL; + + len = strlen(s) + 1; + buf = kmalloc(len, gfp); + if (buf) + memcpy(buf, s, len); + return buf; +} +#endif + /* * MTD methods which simply translate the effective address and pass through * to the _real_ device. @@ -52,7 +91,8 @@ static int part_read(struct mtd_info *mtd, loff_t from, size_t len, int res; stats = part->master->ecc_stats; - res = mtd_read(part->master, from + part->offset, len, retlen, buf); + res = part->master->_read(part->master, from + part->offset, len, + retlen, buf); if (unlikely(mtd_is_eccerr(res))) mtd->ecc_stats.failed += part->master->ecc_stats.failed - stats.failed; @@ -62,6 +102,36 @@ static int part_read(struct mtd_info *mtd, loff_t from, size_t len, return res; } +#ifndef __UBOOT__ +static int part_point(struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, void **virt, resource_size_t *phys) +{ + struct mtd_part *part = PART(mtd); + + return part->master->_point(part->master, from + part->offset, len, + retlen, virt, phys); +} + +static int part_unpoint(struct mtd_info *mtd, loff_t from, size_t len) +{ + struct mtd_part *part = PART(mtd); + + return part->master->_unpoint(part->master, from + part->offset, len); +} +#endif + +static unsigned long part_get_unmapped_area(struct mtd_info *mtd, + unsigned long len, + unsigned long offset, + unsigned long flags) +{ + struct mtd_part *part = PART(mtd); + + offset += part->offset; + return part->master->_get_unmapped_area(part->master, len, offset, + flags); +} + static int part_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops) { @@ -72,8 +142,25 @@ static int part_read_oob(struct mtd_info *mtd, loff_t from, return -EINVAL; if (ops->datbuf && from + ops->len > mtd->size) return -EINVAL; - res = mtd_read_oob(part->master, from + part->offset, ops); + /* + * If OOB is also requested, make sure that we do not read past the end + * of this partition. + */ + if (ops->oobbuf) { + size_t len, pages; + + if (ops->mode == MTD_OPS_AUTO_OOB) + len = mtd->oobavail; + else + len = mtd->oobsize; + pages = mtd_div_by_ws(mtd->size, mtd); + pages -= mtd_div_by_ws(from, mtd); + if (ops->ooboffs + ops->ooblen > pages * len) + return -EINVAL; + } + + res = part->master->_read_oob(part->master, from + part->offset, ops); if (unlikely(res)) { if (mtd_is_bitflip(res)) mtd->ecc_stats.corrected++; @@ -87,35 +174,48 @@ static int part_read_user_prot_reg(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) { struct mtd_part *part = PART(mtd); - return mtd_read_user_prot_reg(part->master, from, len, retlen, buf); + return part->master->_read_user_prot_reg(part->master, from, len, + retlen, buf); } -static int part_get_user_prot_info(struct mtd_info *mtd, - struct otp_info *buf, size_t len) +static int part_get_user_prot_info(struct mtd_info *mtd, size_t len, + size_t *retlen, struct otp_info *buf) { struct mtd_part *part = PART(mtd); - return mtd_get_user_prot_info(part->master, buf, len); + return part->master->_get_user_prot_info(part->master, len, retlen, + buf); } static int part_read_fact_prot_reg(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) { struct mtd_part *part = PART(mtd); - return mtd_read_fact_prot_reg(part->master, from, len, retlen, buf); + return part->master->_read_fact_prot_reg(part->master, from, len, + retlen, buf); } -static int part_get_fact_prot_info(struct mtd_info *mtd, struct otp_info *buf, - size_t len) +static int part_get_fact_prot_info(struct mtd_info *mtd, size_t len, + size_t *retlen, struct otp_info *buf) { struct mtd_part *part = PART(mtd); - return mtd_get_fact_prot_info(part->master, buf, len); + return part->master->_get_fact_prot_info(part->master, len, retlen, + buf); } static int part_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf) { struct mtd_part *part = PART(mtd); - return mtd_write(part->master, to + part->offset, len, retlen, buf); + return part->master->_write(part->master, to + part->offset, len, + retlen, buf); +} + +static int part_panic_write(struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, const u_char *buf) +{ + struct mtd_part *part = PART(mtd); + return part->master->_panic_write(part->master, to + part->offset, len, + retlen, buf); } static int part_write_oob(struct mtd_info *mtd, loff_t to, @@ -127,22 +227,33 @@ static int part_write_oob(struct mtd_info *mtd, loff_t to, return -EINVAL; if (ops->datbuf && to + ops->len > mtd->size) return -EINVAL; - return mtd_write_oob(part->master, to + part->offset, ops); + return part->master->_write_oob(part->master, to + part->offset, ops); } static int part_write_user_prot_reg(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) { struct mtd_part *part = PART(mtd); - return mtd_write_user_prot_reg(part->master, from, len, retlen, buf); + return part->master->_write_user_prot_reg(part->master, from, len, + retlen, buf); } static int part_lock_user_prot_reg(struct mtd_info *mtd, loff_t from, size_t len) { struct mtd_part *part = PART(mtd); - return mtd_lock_user_prot_reg(part->master, from, len); + return part->master->_lock_user_prot_reg(part->master, from, len); +} + +#ifndef __UBOOT__ +static int part_writev(struct mtd_info *mtd, const struct kvec *vecs, + unsigned long count, loff_t to, size_t *retlen) +{ + struct mtd_part *part = PART(mtd); + return part->master->_writev(part->master, vecs, count, + to + part->offset, retlen); } +#endif static int part_erase(struct mtd_info *mtd, struct erase_info *instr) { @@ -150,7 +261,7 @@ static int part_erase(struct mtd_info *mtd, struct erase_info *instr) int ret; instr->addr += part->offset; - ret = mtd_erase(part->master, instr); + ret = part->master->_erase(part->master, instr); if (ret) { if (instr->fail_addr != MTD_FAIL_ADDR_UNKNOWN) instr->fail_addr -= part->offset; @@ -171,30 +282,51 @@ void mtd_erase_callback(struct erase_info *instr) if (instr->callback) instr->callback(instr); } +EXPORT_SYMBOL_GPL(mtd_erase_callback); static int part_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) { struct mtd_part *part = PART(mtd); - return mtd_lock(part->master, ofs + part->offset, len); + return part->master->_lock(part->master, ofs + part->offset, len); } static int part_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len) { struct mtd_part *part = PART(mtd); - return mtd_unlock(part->master, ofs + part->offset, len); + return part->master->_unlock(part->master, ofs + part->offset, len); +} + +static int part_is_locked(struct mtd_info *mtd, loff_t ofs, uint64_t len) +{ + struct mtd_part *part = PART(mtd); + return part->master->_is_locked(part->master, ofs + part->offset, len); } static void part_sync(struct mtd_info *mtd) { struct mtd_part *part = PART(mtd); - mtd_sync(part->master); + part->master->_sync(part->master); +} + +#ifndef __UBOOT__ +static int part_suspend(struct mtd_info *mtd) +{ + struct mtd_part *part = PART(mtd); + return part->master->_suspend(part->master); +} + +static void part_resume(struct mtd_info *mtd) +{ + struct mtd_part *part = PART(mtd); + part->master->_resume(part->master); } +#endif static int part_block_isbad(struct mtd_info *mtd, loff_t ofs) { struct mtd_part *part = PART(mtd); ofs += part->offset; - return mtd_block_isbad(part->master, ofs); + return part->master->_block_isbad(part->master, ofs); } static int part_block_markbad(struct mtd_info *mtd, loff_t ofs) @@ -203,12 +335,18 @@ static int part_block_markbad(struct mtd_info *mtd, loff_t ofs) int res; ofs += part->offset; - res = mtd_block_markbad(part->master, ofs); + res = part->master->_block_markbad(part->master, ofs); if (!res) mtd->ecc_stats.badblocks++; return res; } +static inline void free_partition(struct mtd_part *p) +{ + kfree(p->mtd.name); + kfree(p); +} + /* * This function unregisters and destroy all slave MTD objects which are * attached to the given master MTD object. @@ -217,49 +355,78 @@ static int part_block_markbad(struct mtd_info *mtd, loff_t ofs) int del_mtd_partitions(struct mtd_info *master) { struct mtd_part *slave, *next; + int ret, err = 0; + mutex_lock(&mtd_partitions_mutex); list_for_each_entry_safe(slave, next, &mtd_partitions, list) if (slave->master == master) { + ret = del_mtd_device(&slave->mtd); + if (ret < 0) { + err = ret; + continue; + } list_del(&slave->list); - if (slave->registered) - del_mtd_device(&slave->mtd); - kfree(slave); + free_partition(slave); } + mutex_unlock(&mtd_partitions_mutex); - return 0; + return err; } -static struct mtd_part *add_one_partition(struct mtd_info *master, - const struct mtd_partition *part, int partno, - uint64_t cur_offset) +static struct mtd_part *allocate_partition(struct mtd_info *master, + const struct mtd_partition *part, int partno, + uint64_t cur_offset) { struct mtd_part *slave; + char *name; /* allocate the partition structure */ slave = kzalloc(sizeof(*slave), GFP_KERNEL); - if (!slave) { + name = kstrdup(part->name, GFP_KERNEL); + if (!name || !slave) { printk(KERN_ERR"memory allocation error while creating partitions for \"%s\"\n", - master->name); - del_mtd_partitions(master); - return NULL; + master->name); + kfree(name); + kfree(slave); + return ERR_PTR(-ENOMEM); } - list_add(&slave->list, &mtd_partitions); /* set up the MTD object for this partition */ slave->mtd.type = master->type; slave->mtd.flags = master->flags & ~part->mask_flags; slave->mtd.size = part->size; slave->mtd.writesize = master->writesize; + slave->mtd.writebufsize = master->writebufsize; slave->mtd.oobsize = master->oobsize; slave->mtd.oobavail = master->oobavail; slave->mtd.subpage_sft = master->subpage_sft; - slave->mtd.name = part->name; + slave->mtd.name = name; slave->mtd.owner = master->owner; +#ifndef __UBOOT__ + slave->mtd.backing_dev_info = master->backing_dev_info; + + /* NOTE: we don't arrange MTDs as a tree; it'd be error-prone + * to have the same data be in two different partitions. + */ + slave->mtd.dev.parent = master->dev.parent; +#endif slave->mtd._read = part_read; slave->mtd._write = part_write; + if (master->_panic_write) + slave->mtd._panic_write = part_panic_write; + +#ifndef __UBOOT__ + if (master->_point && master->_unpoint) { + slave->mtd._point = part_point; + slave->mtd._unpoint = part_unpoint; + } +#endif + + if (master->_get_unmapped_area) + slave->mtd._get_unmapped_area = part_get_unmapped_area; if (master->_read_oob) slave->mtd._read_oob = part_read_oob; if (master->_write_oob) @@ -278,10 +445,21 @@ static struct mtd_part *add_one_partition(struct mtd_info *master, slave->mtd._get_fact_prot_info = part_get_fact_prot_info; if (master->_sync) slave->mtd._sync = part_sync; +#ifndef __UBOOT__ + if (!partno && !master->dev.class && master->_suspend && + master->_resume) { + slave->mtd._suspend = part_suspend; + slave->mtd._resume = part_resume; + } + if (master->_writev) + slave->mtd._writev = part_writev; +#endif if (master->_lock) slave->mtd._lock = part_lock; if (master->_unlock) slave->mtd._unlock = part_unlock; + if (master->_is_locked) + slave->mtd._is_locked = part_is_locked; if (master->_block_isbad) slave->mtd._block_isbad = part_block_isbad; if (master->_block_markbad) @@ -289,7 +467,6 @@ static struct mtd_part *add_one_partition(struct mtd_info *master, slave->mtd._erase = part_erase; slave->master = master; slave->offset = part->offset; - slave->index = partno; if (slave->offset == MTDPART_OFS_APPEND) slave->offset = cur_offset; @@ -298,18 +475,29 @@ static struct mtd_part *add_one_partition(struct mtd_info *master, if (mtd_mod_by_eb(cur_offset, master) != 0) { /* Round up to next erasesize */ slave->offset = (mtd_div_by_eb(cur_offset, master) + 1) * master->erasesize; - debug("Moving partition %d: 0x%012llx -> 0x%012llx\n", - partno, (unsigned long long)cur_offset, - (unsigned long long)slave->offset); + debug("Moving partition %d: " + "0x%012llx -> 0x%012llx\n", partno, + (unsigned long long)cur_offset, (unsigned long long)slave->offset); + } + } + if (slave->offset == MTDPART_OFS_RETAIN) { + slave->offset = cur_offset; + if (master->size - slave->offset >= slave->mtd.size) { + slave->mtd.size = master->size - slave->offset + - slave->mtd.size; + } else { + debug("mtd partition \"%s\" doesn't have enough space: %#llx < %#llx, disabled\n", + part->name, master->size - slave->offset, + slave->mtd.size); + /* register to preserve ordering */ + goto out_register; } } if (slave->mtd.size == MTDPART_SIZ_FULL) slave->mtd.size = master->size - slave->offset; - debug("0x%012llx-0x%012llx : \"%s\"\n", - (unsigned long long)slave->offset, - (unsigned long long)(slave->offset + slave->mtd.size), - slave->mtd.name); + debug("0x%012llx-0x%012llx : \"%s\"\n", (unsigned long long)slave->offset, + (unsigned long long)(slave->offset + slave->mtd.size), slave->mtd.name); /* let's do some sanity checks */ if (slave->offset >= master->size) { @@ -336,7 +524,8 @@ static struct mtd_part *add_one_partition(struct mtd_info *master, for (i = 0; i < max && regions[i].offset <= slave->offset; i++) ; /* The loop searched for the region _behind_ the first one */ - i--; + if (i > 0) + i--; /* Pick biggest erasesize */ for (; i < max && regions[i].offset < end; i++) { @@ -367,6 +556,10 @@ static struct mtd_part *add_one_partition(struct mtd_info *master, } slave->mtd.ecclayout = master->ecclayout; + slave->mtd.ecc_step_size = master->ecc_step_size; + slave->mtd.ecc_strength = master->ecc_strength; + slave->mtd.bitflip_threshold = master->bitflip_threshold; + if (master->_block_isbad) { uint64_t offs = 0; @@ -378,18 +571,91 @@ static struct mtd_part *add_one_partition(struct mtd_info *master, } out_register: - if (part->mtdp) { - /* store the object pointer (caller may or may not register it*/ - *part->mtdp = &slave->mtd; - slave->registered = 0; - } else { - /* register our partition */ - add_mtd_device(&slave->mtd); - slave->registered = 1; - } return slave; } +#ifndef __UBOOT__ +int mtd_add_partition(struct mtd_info *master, const char *name, + long long offset, long long length) +{ + struct mtd_partition part; + struct mtd_part *p, *new; + uint64_t start, end; + int ret = 0; + + /* the direct offset is expected */ + if (offset == MTDPART_OFS_APPEND || + offset == MTDPART_OFS_NXTBLK) + return -EINVAL; + + if (length == MTDPART_SIZ_FULL) + length = master->size - offset; + + if (length <= 0) + return -EINVAL; + + part.name = name; + part.size = length; + part.offset = offset; + part.mask_flags = 0; + part.ecclayout = NULL; + + new = allocate_partition(master, &part, -1, offset); + if (IS_ERR(new)) + return PTR_ERR(new); + + start = offset; + end = offset + length; + + mutex_lock(&mtd_partitions_mutex); + list_for_each_entry(p, &mtd_partitions, list) + if (p->master == master) { + if ((start >= p->offset) && + (start < (p->offset + p->mtd.size))) + goto err_inv; + + if ((end >= p->offset) && + (end < (p->offset + p->mtd.size))) + goto err_inv; + } + + list_add(&new->list, &mtd_partitions); + mutex_unlock(&mtd_partitions_mutex); + + add_mtd_device(&new->mtd); + + return ret; +err_inv: + mutex_unlock(&mtd_partitions_mutex); + free_partition(new); + return -EINVAL; +} +EXPORT_SYMBOL_GPL(mtd_add_partition); + +int mtd_del_partition(struct mtd_info *master, int partno) +{ + struct mtd_part *slave, *next; + int ret = -EINVAL; + + mutex_lock(&mtd_partitions_mutex); + list_for_each_entry_safe(slave, next, &mtd_partitions, list) + if ((slave->master == master) && + (slave->mtd.index == partno)) { + ret = del_mtd_device(&slave->mtd); + if (ret < 0) + break; + + list_del(&slave->list); + free_partition(slave); + break; + } + mutex_unlock(&mtd_partitions_mutex); + + return ret; +} +EXPORT_SYMBOL_GPL(mtd_del_partition); +#endif + /* * This function, given a master MTD object and a partition table, creates * and registers slave MTD objects which are bound to the master according to @@ -407,6 +673,7 @@ int add_mtd_partitions(struct mtd_info *master, uint64_t cur_offset = 0; int i; +#ifdef __UBOOT__ /* * Need to init the list here, since LIST_INIT() does not * work on platforms where relocation has problems (like MIPS @@ -414,15 +681,147 @@ int add_mtd_partitions(struct mtd_info *master, */ if (mtd_partitions.next == NULL) INIT_LIST_HEAD(&mtd_partitions); +#endif debug("Creating %d MTD partitions on \"%s\":\n", nbparts, master->name); for (i = 0; i < nbparts; i++) { - slave = add_one_partition(master, parts + i, i, cur_offset); - if (!slave) - return -ENOMEM; + slave = allocate_partition(master, parts + i, i, cur_offset); + if (IS_ERR(slave)) + return PTR_ERR(slave); + + mutex_lock(&mtd_partitions_mutex); + list_add(&slave->list, &mtd_partitions); + mutex_unlock(&mtd_partitions_mutex); + + add_mtd_device(&slave->mtd); + cur_offset = slave->offset + slave->mtd.size; } return 0; } + +#ifndef __UBOOT__ +static DEFINE_SPINLOCK(part_parser_lock); +static LIST_HEAD(part_parsers); + +static struct mtd_part_parser *get_partition_parser(const char *name) +{ + struct mtd_part_parser *p, *ret = NULL; + + spin_lock(&part_parser_lock); + + list_for_each_entry(p, &part_parsers, list) + if (!strcmp(p->name, name) && try_module_get(p->owner)) { + ret = p; + break; + } + + spin_unlock(&part_parser_lock); + + return ret; +} + +#define put_partition_parser(p) do { module_put((p)->owner); } while (0) + +void register_mtd_parser(struct mtd_part_parser *p) +{ + spin_lock(&part_parser_lock); + list_add(&p->list, &part_parsers); + spin_unlock(&part_parser_lock); +} +EXPORT_SYMBOL_GPL(register_mtd_parser); + +void deregister_mtd_parser(struct mtd_part_parser *p) +{ + spin_lock(&part_parser_lock); + list_del(&p->list); + spin_unlock(&part_parser_lock); +} +EXPORT_SYMBOL_GPL(deregister_mtd_parser); + +/* + * Do not forget to update 'parse_mtd_partitions()' kerneldoc comment if you + * are changing this array! + */ +static const char * const default_mtd_part_types[] = { + "cmdlinepart", + "ofpart", + NULL +}; + +/** + * parse_mtd_partitions - parse MTD partitions + * @master: the master partition (describes whole MTD device) + * @types: names of partition parsers to try or %NULL + * @pparts: array of partitions found is returned here + * @data: MTD partition parser-specific data + * + * This function tries to find partition on MTD device @master. It uses MTD + * partition parsers, specified in @types. However, if @types is %NULL, then + * the default list of parsers is used. The default list contains only the + * "cmdlinepart" and "ofpart" parsers ATM. + * Note: If there are more then one parser in @types, the kernel only takes the + * partitions parsed out by the first parser. + * + * This function may return: + * o a negative error code in case of failure + * o zero if no partitions were found + * o a positive number of found partitions, in which case on exit @pparts will + * point to an array containing this number of &struct mtd_info objects. + */ +int parse_mtd_partitions(struct mtd_info *master, const char *const *types, + struct mtd_partition **pparts, + struct mtd_part_parser_data *data) +{ + struct mtd_part_parser *parser; + int ret = 0; + + if (!types) + types = default_mtd_part_types; + + for ( ; ret <= 0 && *types; types++) { + parser = get_partition_parser(*types); + if (!parser && !request_module("%s", *types)) + parser = get_partition_parser(*types); + if (!parser) + continue; + ret = (*parser->parse_fn)(master, pparts, data); + put_partition_parser(parser); + if (ret > 0) { + printk(KERN_NOTICE "%d %s partitions found on MTD device %s\n", + ret, parser->name, master->name); + break; + } + } + return ret; +} +#endif + +int mtd_is_partition(const struct mtd_info *mtd) +{ + struct mtd_part *part; + int ispart = 0; + + mutex_lock(&mtd_partitions_mutex); + list_for_each_entry(part, &mtd_partitions, list) + if (&part->mtd == mtd) { + ispart = 1; + break; + } + mutex_unlock(&mtd_partitions_mutex); + + return ispart; +} +EXPORT_SYMBOL_GPL(mtd_is_partition); + +/* Returns the size of the entire flash chip */ +uint64_t mtd_get_device_size(const struct mtd_info *mtd) +{ + if (!mtd_is_partition(mtd)) + return mtd->size; + + return PART(mtd)->master->size; +} +EXPORT_SYMBOL_GPL(mtd_get_device_size); diff --git a/drivers/mtd/nand/fsl_elbc_nand.c b/drivers/mtd/nand/fsl_elbc_nand.c index 2f31fc9..7e1e6ec 100644 --- a/drivers/mtd/nand/fsl_elbc_nand.c +++ b/drivers/mtd/nand/fsl_elbc_nand.c @@ -561,6 +561,7 @@ static void fsl_elbc_read_buf(struct mtd_info *mtd, u8 *buf, int len) len, avail); } +#if defined(CONFIG_MTD_NAND_VERIFY_WRITE) /* * Verify buffer against the FCM Controller Data Buffer */ @@ -593,6 +594,7 @@ static int fsl_elbc_verify_buf(struct mtd_info *mtd, ctrl->index += len; return i == len && ctrl->status == LTESR_CC ? 0 : -EIO; } +#endif /* This function is called after Program and Erase Operations to * check for success or failure. @@ -725,7 +727,9 @@ static int fsl_elbc_chip_init(int devnum, u8 *addr) nand->read_byte = fsl_elbc_read_byte; nand->write_buf = fsl_elbc_write_buf; nand->read_buf = fsl_elbc_read_buf; +#if defined(CONFIG_MTD_NAND_VERIFY_WRITE) nand->verify_buf = fsl_elbc_verify_buf; +#endif nand->select_chip = fsl_elbc_select_chip; nand->cmdfunc = fsl_elbc_cmdfunc; nand->waitfunc = fsl_elbc_wait; diff --git a/drivers/mtd/nand/fsl_ifc_nand.c b/drivers/mtd/nand/fsl_ifc_nand.c index 8b453cb..2f04c69 100644 --- a/drivers/mtd/nand/fsl_ifc_nand.c +++ b/drivers/mtd/nand/fsl_ifc_nand.c @@ -684,6 +684,7 @@ static void fsl_ifc_read_buf(struct mtd_info *mtd, u8 *buf, int len) __func__, len, avail); } +#if defined(CONFIG_MTD_NAND_VERIFY_WRITE) /* * Verify buffer against the IFC Controller Data Buffer */ @@ -716,6 +717,7 @@ static int fsl_ifc_verify_buf(struct mtd_info *mtd, ctrl->index += len; return i == len && ctrl->status == IFC_NAND_EVTER_STAT_OPC ? 0 : -EIO; } +#endif /* This function is called after Program and Erase Operations to * check for success or failure. @@ -939,7 +941,9 @@ static int fsl_ifc_chip_init(int devnum, u8 *addr) nand->write_buf = fsl_ifc_write_buf; nand->read_buf = fsl_ifc_read_buf; +#if defined(CONFIG_MTD_NAND_VERIFY_WRITE) nand->verify_buf = fsl_ifc_verify_buf; +#endif nand->select_chip = fsl_ifc_select_chip; nand->cmdfunc = fsl_ifc_cmdfunc; nand->waitfunc = fsl_ifc_wait; diff --git a/drivers/mtd/nand/fsl_upm.c b/drivers/mtd/nand/fsl_upm.c index 3ae0044..65ce98a 100644 --- a/drivers/mtd/nand/fsl_upm.c +++ b/drivers/mtd/nand/fsl_upm.c @@ -153,6 +153,7 @@ static void upm_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len) buf[i] = in_8(chip->IO_ADDR_R); } +#if defined(CONFIG_MTD_NAND_VERIFY_WRITE) static int upm_nand_verify_buf(struct mtd_info *mtd, const u_char *buf, int len) { int i; @@ -165,6 +166,7 @@ static int upm_nand_verify_buf(struct mtd_info *mtd, const u_char *buf, int len) return 0; } +#endif static int nand_dev_ready(struct mtd_info *mtd) { @@ -191,7 +193,9 @@ int fsl_upm_nand_init(struct nand_chip *chip, struct fsl_upm_nand *fun) chip->read_byte = upm_nand_read_byte; chip->read_buf = upm_nand_read_buf; chip->write_buf = upm_nand_write_buf; +#if defined(CONFIG_MTD_NAND_VERIFY_WRITE) chip->verify_buf = upm_nand_verify_buf; +#endif if (fun->dev_ready) chip->dev_ready = nand_dev_ready; diff --git a/drivers/mtd/nand/mpc5121_nfc.c b/drivers/mtd/nand/mpc5121_nfc.c index d0f3a35..7233bfc 100644 --- a/drivers/mtd/nand/mpc5121_nfc.c +++ b/drivers/mtd/nand/mpc5121_nfc.c @@ -459,6 +459,7 @@ static void mpc5121_nfc_write_buf(struct mtd_info *mtd, mpc5121_nfc_buf_copy(mtd, (u_char *) buf, len, 1); } +#if defined(CONFIG_MTD_NAND_VERIFY_WRITE) /* Compare buffer with NAND flash */ static int mpc5121_nfc_verify_buf(struct mtd_info *mtd, const u_char * buf, int len) @@ -479,6 +480,7 @@ static int mpc5121_nfc_verify_buf(struct mtd_info *mtd, return 0; } +#endif /* Read byte from NFC buffers */ static u8 mpc5121_nfc_read_byte(struct mtd_info *mtd) @@ -607,7 +609,9 @@ int board_nand_init(struct nand_chip *chip) chip->read_word = mpc5121_nfc_read_word; chip->read_buf = mpc5121_nfc_read_buf; chip->write_buf = mpc5121_nfc_write_buf; +#if defined(CONFIG_MTD_NAND_VERIFY_WRITE) chip->verify_buf = mpc5121_nfc_verify_buf; +#endif chip->select_chip = mpc5121_nfc_select_chip; chip->bbt_options = NAND_BBT_USE_FLASH; chip->ecc.mode = NAND_ECC_SOFT; diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index ed0ca3a..2e5b5b9 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c @@ -949,6 +949,8 @@ static void mxc_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len) host->col_addr = col; } +#ifdef __UBOOT__ +#if defined(CONFIG_MTD_NAND_VERIFY_WRITE) /* * Used by the upper layer to verify the data in NAND Flash * with the data in the buf. @@ -972,6 +974,8 @@ static int mxc_nand_verify_buf(struct mtd_info *mtd, return 0; } +#endif +#endif /* * This function is used by upper layer for select and @@ -1203,7 +1207,11 @@ int board_nand_init(struct nand_chip *this) this->read_word = mxc_nand_read_word; this->write_buf = mxc_nand_write_buf; this->read_buf = mxc_nand_read_buf; +#ifdef __UBOOT__ +#if defined(CONFIG_MTD_NAND_VERIFY_WRITE) this->verify_buf = mxc_nand_verify_buf; +#endif +#endif host->regs = (struct mxc_nand_regs __iomem *)CONFIG_MXC_NAND_REGS_BASE; #ifdef MXC_NFC_V3_2 diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 376976d..085b154 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -4,7 +4,6 @@ * Overview: * This is the generic MTD driver for NAND flash devices. It should be * capable of working with almost all NAND chips currently available. - * Basic support for AG-AND chips is provided. * * Additional technical information is available on * http://www.linux-mtd.infradead.org/doc/nand.html @@ -22,8 +21,6 @@ * Enable cached programming for 2k page size chips * Check, if mtd->ecctype should be set to MTD_ECC_HW * if we have HW ECC support. - * The AG-AND chips have nice features for speed improvement, - * which are not supported yet. Read / program 4 pages in one go. * BBT table is not serialized, has to be fixed * * This program is free software; you can redistribute it and/or modify @@ -32,10 +29,29 @@ * */ -#include <common.h> - -#define ENOTSUPP 524 /* Operation is not supported */ +#define __UBOOT__ +#ifndef __UBOOT__ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/errno.h> +#include <linux/err.h> +#include <linux/sched.h> +#include <linux/slab.h> +#include <linux/types.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/nand.h> +#include <linux/mtd/nand_ecc.h> +#include <linux/mtd/nand_bch.h> +#include <linux/interrupt.h> +#include <linux/bitops.h> +#include <linux/leds.h> +#include <linux/io.h> +#include <linux/mtd/partitions.h> +#else +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include <common.h> #include <malloc.h> #include <watchdog.h> #include <linux/err.h> @@ -44,11 +60,9 @@ #include <linux/mtd/nand.h> #include <linux/mtd/nand_ecc.h> #include <linux/mtd/nand_bch.h> - #ifdef CONFIG_MTD_PARTITIONS #include <linux/mtd/partitions.h> #endif - #include <asm/io.h> #include <asm/errno.h> @@ -63,6 +77,9 @@ #define CONFIG_SYS_NAND_RESET_CNT 200000 #endif +static bool is_module_text_address(unsigned long addr) {return 0;} +#endif + /* Define default oob placement schemes for large and small page devices */ static struct nand_ecclayout nand_oob_8 = { .eccbytes = 3, @@ -107,13 +124,16 @@ static struct nand_ecclayout nand_oob_128 = { .length = 78} } }; -static int nand_get_device(struct nand_chip *chip, struct mtd_info *mtd, - int new_state); +static int nand_get_device(struct mtd_info *mtd, int new_state); static int nand_do_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops); -static int nand_wait(struct mtd_info *mtd, struct nand_chip *this); +/* + * For devices which display every fart in the system on a separate LED. Is + * compiled away when LED support is disabled. + */ +DEFINE_LED_TRIGGER(nand_led_trigger); static int check_offs_len(struct mtd_info *mtd, loff_t ofs, uint64_t len) @@ -122,15 +142,14 @@ static int check_offs_len(struct mtd_info *mtd, int ret = 0; /* Start address must align on block boundary */ - if (ofs & ((1 << chip->phys_erase_shift) - 1)) { - MTDDEBUG(MTD_DEBUG_LEVEL0, "%s: Unaligned address\n", __func__); + if (ofs & ((1ULL << chip->phys_erase_shift) - 1)) { + pr_debug("%s: unaligned address\n", __func__); ret = -EINVAL; } /* Length must align on block boundary */ - if (len & ((1 << chip->phys_erase_shift) - 1)) { - MTDDEBUG(MTD_DEBUG_LEVEL0, "%s: Length not block aligned\n", - __func__); + if (len & ((1ULL << chip->phys_erase_shift) - 1)) { + pr_debug("%s: length not block aligned\n", __func__); ret = -EINVAL; } @@ -141,30 +160,43 @@ static int check_offs_len(struct mtd_info *mtd, * nand_release_device - [GENERIC] release chip * @mtd: MTD device structure * - * Deselect, release chip lock and wake up anyone waiting on the device. + * Release chip lock and wake up anyone waiting on the device. */ static void nand_release_device(struct mtd_info *mtd) { struct nand_chip *chip = mtd->priv; +#ifndef __UBOOT__ + /* Release the controller and the chip */ + spin_lock(&chip->controller->lock); + chip->controller->active = NULL; + chip->state = FL_READY; + wake_up(&chip->controller->wq); + spin_unlock(&chip->controller->lock); +#else /* De-select the NAND device */ chip->select_chip(mtd, -1); +#endif } /** * nand_read_byte - [DEFAULT] read one byte from the chip * @mtd: MTD device structure * - * Default read function for 8bit buswidth. + * Default read function for 8bit buswidth */ +#ifndef __UBOOT__ +static uint8_t nand_read_byte(struct mtd_info *mtd) +#else uint8_t nand_read_byte(struct mtd_info *mtd) +#endif { struct nand_chip *chip = mtd->priv; return readb(chip->IO_ADDR_R); } /** - * nand_read_byte16 - [DEFAULT] read one byte endianess aware from the chip + * nand_read_byte16 - [DEFAULT] read one byte endianness aware from the chip * nand_read_byte16 - [DEFAULT] read one byte endianness aware from the chip * @mtd: MTD device structure * @@ -213,6 +245,88 @@ static void nand_select_chip(struct mtd_info *mtd, int chipnr) } /** + * nand_write_byte - [DEFAULT] write single byte to chip + * @mtd: MTD device structure + * @byte: value to write + * + * Default function to write a byte to I/O[7:0] + */ +static void nand_write_byte(struct mtd_info *mtd, uint8_t byte) +{ + struct nand_chip *chip = mtd->priv; + + chip->write_buf(mtd, &byte, 1); +} + +/** + * nand_write_byte16 - [DEFAULT] write single byte to a chip with width 16 + * @mtd: MTD device structure + * @byte: value to write + * + * Default function to write a byte to I/O[7:0] on a 16-bit wide chip. + */ +static void nand_write_byte16(struct mtd_info *mtd, uint8_t byte) +{ + struct nand_chip *chip = mtd->priv; + uint16_t word = byte; + + /* + * It's not entirely clear what should happen to I/O[15:8] when writing + * a byte. The ONFi spec (Revision 3.1; 2012-09-19, Section 2.16) reads: + * + * When the host supports a 16-bit bus width, only data is + * transferred at the 16-bit width. All address and command line + * transfers shall use only the lower 8-bits of the data bus. During + * command transfers, the host may place any value on the upper + * 8-bits of the data bus. During address transfers, the host shall + * set the upper 8-bits of the data bus to 00h. + * + * One user of the write_byte callback is nand_onfi_set_features. The + * four parameters are specified to be written to I/O[7:0], but this is + * neither an address nor a command transfer. Let's assume a 0 on the + * upper I/O lines is OK. + */ + chip->write_buf(mtd, (uint8_t *)&word, 2); +} + +#if defined(__UBOOT__) && !defined(CONFIG_BLACKFIN) +static void iowrite8_rep(void *addr, const uint8_t *buf, int len) +{ + int i; + + for (i = 0; i < len; i++) + writeb(buf[i], addr); +} +static void ioread8_rep(void *addr, uint8_t *buf, int len) +{ + int i; + + for (i = 0; i < len; i++) + buf[i] = readb(addr); +} + +static void ioread16_rep(void *addr, void *buf, int len) +{ + int i; + u16 *p = (u16 *) buf; + len >>= 1; + + for (i = 0; i < len; i++) + p[i] = readw(addr); +} + +static void iowrite16_rep(void *addr, void *buf, int len) +{ + int i; + u16 *p = (u16 *) buf; + len >>= 1; + + for (i = 0; i < len; i++) + writew(p[i], addr); +} +#endif + +/** * nand_write_buf - [DEFAULT] write buffer to chip * @mtd: MTD device structure * @buf: data buffer @@ -220,13 +334,15 @@ static void nand_select_chip(struct mtd_info *mtd, int chipnr) * * Default write function for 8bit buswidth. */ +#ifndef __UBOOT__ +static void nand_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) +#else void nand_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) +#endif { - int i; struct nand_chip *chip = mtd->priv; - for (i = 0; i < len; i++) - writeb(buf[i], chip->IO_ADDR_W); + iowrite8_rep(chip->IO_ADDR_W, buf, len); } /** @@ -237,15 +353,19 @@ void nand_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) * * Default read function for 8bit buswidth. */ +#ifndef __UBOOT__ +static void nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) +#else void nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) +#endif { - int i; struct nand_chip *chip = mtd->priv; - for (i = 0; i < len; i++) - buf[i] = readb(chip->IO_ADDR_R); + ioread8_rep(chip->IO_ADDR_R, buf, len); } +#ifdef __UBOOT__ +#if defined(CONFIG_MTD_NAND_VERIFY_WRITE) /** * nand_verify_buf - [DEFAULT] Verify chip data against buffer * @mtd: MTD device structure @@ -266,14 +386,14 @@ static int nand_verify_buf(struct mtd_info *mtd, const uint8_t *buf, int len) } /** - * nand_write_buf16 - [DEFAULT] write buffer to chip + * nand_verify_buf16 - [DEFAULT] Verify chip data against buffer * @mtd: MTD device structure - * @buf: data buffer - * @len: number of bytes to write + * @buf: buffer containing the data to compare + * @len: number of bytes to compare * - * Default write function for 16bit buswidth. + * Default verify function for 16bit buswidth. */ -void nand_write_buf16(struct mtd_info *mtd, const uint8_t *buf, int len) +static int nand_verify_buf16(struct mtd_info *mtd, const uint8_t *buf, int len) { int i; struct nand_chip *chip = mtd->priv; @@ -281,49 +401,52 @@ void nand_write_buf16(struct mtd_info *mtd, const uint8_t *buf, int len) len >>= 1; for (i = 0; i < len; i++) - writew(p[i], chip->IO_ADDR_W); + if (p[i] != readw(chip->IO_ADDR_R)) + return -EFAULT; + return 0; } +#endif +#endif /** - * nand_read_buf16 - [DEFAULT] read chip data into buffer + * nand_write_buf16 - [DEFAULT] write buffer to chip * @mtd: MTD device structure - * @buf: buffer to store date - * @len: number of bytes to read + * @buf: data buffer + * @len: number of bytes to write * - * Default read function for 16bit buswidth. + * Default write function for 16bit buswidth. */ -void nand_read_buf16(struct mtd_info *mtd, uint8_t *buf, int len) +#ifndef __UBOOT__ +static void nand_write_buf16(struct mtd_info *mtd, const uint8_t *buf, int len) +#else +void nand_write_buf16(struct mtd_info *mtd, const uint8_t *buf, int len) +#endif { - int i; struct nand_chip *chip = mtd->priv; u16 *p = (u16 *) buf; - len >>= 1; - for (i = 0; i < len; i++) - p[i] = readw(chip->IO_ADDR_R); + iowrite16_rep(chip->IO_ADDR_W, p, len >> 1); } /** - * nand_verify_buf16 - [DEFAULT] Verify chip data against buffer + * nand_read_buf16 - [DEFAULT] read chip data into buffer * @mtd: MTD device structure - * @buf: buffer containing the data to compare - * @len: number of bytes to compare + * @buf: buffer to store date + * @len: number of bytes to read * - * Default verify function for 16bit buswidth. + * Default read function for 16bit buswidth. */ -static int nand_verify_buf16(struct mtd_info *mtd, const uint8_t *buf, int len) +#ifndef __UBOOT__ +static void nand_read_buf16(struct mtd_info *mtd, uint8_t *buf, int len) +#else +void nand_read_buf16(struct mtd_info *mtd, uint8_t *buf, int len) +#endif { - int i; struct nand_chip *chip = mtd->priv; u16 *p = (u16 *) buf; - len >>= 1; - for (i = 0; i < len; i++) - if (p[i] != readw(chip->IO_ADDR_R)) - return -EFAULT; - - return 0; + ioread16_rep(chip->IO_ADDR_R, p, len >> 1); } /** @@ -348,7 +471,7 @@ static int nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip) if (getchip) { chipnr = (int)(ofs >> chip->chip_shift); - nand_get_device(chip, mtd, FL_READING); + nand_get_device(mtd, FL_READING); /* Select the NAND device */ chip->select_chip(mtd, chipnr); @@ -378,87 +501,97 @@ static int nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip) i++; } while (!res && i < 2 && (chip->bbt_options & NAND_BBT_SCAN2NDPAGE)); - if (getchip) + if (getchip) { + chip->select_chip(mtd, -1); nand_release_device(mtd); + } return res; } /** - * nand_default_block_markbad - [DEFAULT] mark a block bad + * nand_default_block_markbad - [DEFAULT] mark a block bad via bad block marker * @mtd: MTD device structure * @ofs: offset from device start * * This is the default implementation, which can be overridden by a hardware - * specific driver. We try operations in the following order, according to our - * bbt_options (NAND_BBT_NO_OOB_BBM and NAND_BBT_USE_FLASH): + * specific driver. It provides the details for writing a bad block marker to a + * block. + */ +static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs) +{ + struct nand_chip *chip = mtd->priv; + struct mtd_oob_ops ops; + uint8_t buf[2] = { 0, 0 }; + int ret = 0, res, i = 0; + + ops.datbuf = NULL; + ops.oobbuf = buf; + ops.ooboffs = chip->badblockpos; + if (chip->options & NAND_BUSWIDTH_16) { + ops.ooboffs &= ~0x01; + ops.len = ops.ooblen = 2; + } else { + ops.len = ops.ooblen = 1; + } + ops.mode = MTD_OPS_PLACE_OOB; + + /* Write to first/last page(s) if necessary */ + if (chip->bbt_options & NAND_BBT_SCANLASTPAGE) + ofs += mtd->erasesize - mtd->writesize; + do { + res = nand_do_write_oob(mtd, ofs, &ops); + if (!ret) + ret = res; + + i++; + ofs += mtd->writesize; + } while ((chip->bbt_options & NAND_BBT_SCAN2NDPAGE) && i < 2); + + return ret; +} + +/** + * nand_block_markbad_lowlevel - mark a block bad + * @mtd: MTD device structure + * @ofs: offset from device start + * + * This function performs the generic NAND bad block marking steps (i.e., bad + * block table(s) and/or marker(s)). We only allow the hardware driver to + * specify how to write bad block markers to OOB (chip->block_markbad). + * + * We try operations in the following order: * (1) erase the affected block, to allow OOB marker to be written cleanly - * (2) update in-memory BBT - * (3) write bad block marker to OOB area of affected block - * (4) update flash-based BBT - * Note that we retain the first error encountered in (3) or (4), finish the + * (2) write bad block marker to OOB area of affected block (unless flag + * NAND_BBT_NO_OOB_BBM is present) + * (3) update the BBT + * Note that we retain the first error encountered in (2) or (3), finish the * procedures, and dump the error in the end. */ -static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs) +static int nand_block_markbad_lowlevel(struct mtd_info *mtd, loff_t ofs) { struct nand_chip *chip = mtd->priv; - uint8_t buf[2] = { 0, 0 }; - int block, res, ret = 0, i = 0; - int write_oob = !(chip->bbt_options & NAND_BBT_NO_OOB_BBM); + int res, ret = 0; - if (write_oob) { + if (!(chip->bbt_options & NAND_BBT_NO_OOB_BBM)) { struct erase_info einfo; /* Attempt erase before marking OOB */ memset(&einfo, 0, sizeof(einfo)); einfo.mtd = mtd; einfo.addr = ofs; - einfo.len = 1 << chip->phys_erase_shift; + einfo.len = 1ULL << chip->phys_erase_shift; nand_erase_nand(mtd, &einfo, 0); - } - - /* Get block number */ - block = (int)(ofs >> chip->bbt_erase_shift); - /* Mark block bad in memory-based BBT */ - if (chip->bbt) - chip->bbt[block >> 2] |= 0x01 << ((block & 0x03) << 1); - - /* Write bad block marker to OOB */ - if (write_oob) { - struct mtd_oob_ops ops; - loff_t wr_ofs = ofs; - - nand_get_device(chip, mtd, FL_WRITING); - - ops.datbuf = NULL; - ops.oobbuf = buf; - ops.ooboffs = chip->badblockpos; - if (chip->options & NAND_BUSWIDTH_16) { - ops.ooboffs &= ~0x01; - ops.len = ops.ooblen = 2; - } else { - ops.len = ops.ooblen = 1; - } - ops.mode = MTD_OPS_PLACE_OOB; - - /* Write to first/last page(s) if necessary */ - if (chip->bbt_options & NAND_BBT_SCANLASTPAGE) - wr_ofs += mtd->erasesize - mtd->writesize; - do { - res = nand_do_write_oob(mtd, wr_ofs, &ops); - if (!ret) - ret = res; - - i++; - wr_ofs += mtd->writesize; - } while ((chip->bbt_options & NAND_BBT_SCAN2NDPAGE) && i < 2); + /* Write bad block marker to OOB */ + nand_get_device(mtd, FL_WRITING); + ret = chip->block_markbad(mtd, ofs); nand_release_device(mtd); } - /* Update flash-based bad block table */ - if (chip->bbt_options & NAND_BBT_USE_FLASH) { - res = nand_update_bbt(mtd, ofs); + /* Mark block bad in BBT */ + if (chip->bbt) { + res = nand_markbad_bbt(mtd, ofs); if (!ret) ret = res; } @@ -504,11 +637,6 @@ static int nand_block_checkbad(struct mtd_info *mtd, loff_t ofs, int getchip, { struct nand_chip *chip = mtd->priv; - if (!(chip->options & NAND_BBT_SCANNED)) { - chip->options |= NAND_BBT_SCANNED; - chip->scan_bbt(mtd); - } - if (!chip->bbt) return chip->block_bad(mtd, ofs, getchip); @@ -516,22 +644,63 @@ static int nand_block_checkbad(struct mtd_info *mtd, loff_t ofs, int getchip, return nand_isbad_bbt(mtd, ofs, allowbbt); } +#ifndef __UBOOT__ +/** + * panic_nand_wait_ready - [GENERIC] Wait for the ready pin after commands. + * @mtd: MTD device structure + * @timeo: Timeout + * + * Helper function for nand_wait_ready used when needing to wait in interrupt + * context. + */ +static void panic_nand_wait_ready(struct mtd_info *mtd, unsigned long timeo) +{ + struct nand_chip *chip = mtd->priv; + int i; + + /* Wait for the device to get ready */ + for (i = 0; i < timeo; i++) { + if (chip->dev_ready(mtd)) + break; + touch_softlockup_watchdog(); + mdelay(1); + } +} +#endif + /* Wait for the ready pin, after a command. The timeout is caught later. */ void nand_wait_ready(struct mtd_info *mtd) { struct nand_chip *chip = mtd->priv; +#ifndef __UBOOT__ + unsigned long timeo = jiffies + msecs_to_jiffies(20); + + /* 400ms timeout */ + if (in_interrupt() || oops_in_progress) + return panic_nand_wait_ready(mtd, 400); + + led_trigger_event(nand_led_trigger, LED_FULL); + /* Wait until command is processed or timeout occurs */ + do { + if (chip->dev_ready(mtd)) + break; + touch_softlockup_watchdog(); + } while (time_before(jiffies, timeo)); + led_trigger_event(nand_led_trigger, LED_OFF); +#else u32 timeo = (CONFIG_SYS_HZ * 20) / 1000; u32 time_start; time_start = get_timer(0); - /* Wait until command is processed or timeout occurs */ while (get_timer(time_start) < timeo) { if (chip->dev_ready) if (chip->dev_ready(mtd)) break; } +#endif } +EXPORT_SYMBOL_GPL(nand_wait_ready); /** * nand_command - [DEFAULT] Send command to NAND device @@ -541,7 +710,7 @@ void nand_wait_ready(struct mtd_info *mtd) * @page_addr: the page address for this command, -1 if none * * Send command to NAND device. This function is used for small page devices - * (256/512 Bytes per page). + * (512 Bytes per page). */ static void nand_command(struct mtd_info *mtd, unsigned int command, int column, int page_addr) @@ -575,7 +744,7 @@ static void nand_command(struct mtd_info *mtd, unsigned int command, /* Serially input address */ if (column != -1) { /* Adjust columns for 16 bit buswidth */ - if ((chip->options & NAND_BUSWIDTH_16) && + if (chip->options & NAND_BUSWIDTH_16 && !nand_opcode_8bits(command)) column >>= 1; chip->cmd_ctrl(mtd, column, ctrl); @@ -660,8 +829,7 @@ static void nand_command_lp(struct mtd_info *mtd, unsigned int command, } /* Command latch cycle */ - chip->cmd_ctrl(mtd, command & 0xff, - NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE); + chip->cmd_ctrl(mtd, command, NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE); if (column != -1 || page_addr != -1) { int ctrl = NAND_CTRL_CHANGE | NAND_NCE | NAND_ALE; @@ -669,7 +837,7 @@ static void nand_command_lp(struct mtd_info *mtd, unsigned int command, /* Serially input address */ if (column != -1) { /* Adjust columns for 16 bit buswidth */ - if ((chip->options & NAND_BUSWIDTH_16) && + if (chip->options & NAND_BUSWIDTH_16 && !nand_opcode_8bits(command)) column >>= 1; chip->cmd_ctrl(mtd, column, ctrl); @@ -701,16 +869,6 @@ static void nand_command_lp(struct mtd_info *mtd, unsigned int command, case NAND_CMD_SEQIN: case NAND_CMD_RNDIN: case NAND_CMD_STATUS: - case NAND_CMD_DEPLETE1: - return; - - case NAND_CMD_STATUS_ERROR: - case NAND_CMD_STATUS_ERROR0: - case NAND_CMD_STATUS_ERROR1: - case NAND_CMD_STATUS_ERROR2: - case NAND_CMD_STATUS_ERROR3: - /* Read error status commands require only a short delay */ - udelay(chip->chip_delay); return; case NAND_CMD_RESET: @@ -761,18 +919,91 @@ static void nand_command_lp(struct mtd_info *mtd, unsigned int command, } /** - * nand_get_device - [GENERIC] Get chip for selected access + * panic_nand_get_device - [GENERIC] Get chip for selected access * @chip: the nand chip descriptor * @mtd: MTD device structure * @new_state: the state which is requested * + * Used when in panic, no locks are taken. + */ +static void panic_nand_get_device(struct nand_chip *chip, + struct mtd_info *mtd, int new_state) +{ + /* Hardware controller shared among independent devices */ + chip->controller->active = chip; + chip->state = new_state; +} + +/** + * nand_get_device - [GENERIC] Get chip for selected access + * @mtd: MTD device structure + * @new_state: the state which is requested + * * Get the device and lock it for exclusive access */ static int -nand_get_device(struct nand_chip *chip, struct mtd_info *mtd, int new_state) +nand_get_device(struct mtd_info *mtd, int new_state) { + struct nand_chip *chip = mtd->priv; +#ifndef __UBOOT__ + spinlock_t *lock = &chip->controller->lock; + wait_queue_head_t *wq = &chip->controller->wq; + DECLARE_WAITQUEUE(wait, current); +retry: + spin_lock(lock); + + /* Hardware controller shared among independent devices */ + if (!chip->controller->active) + chip->controller->active = chip; + + if (chip->controller->active == chip && chip->state == FL_READY) { + chip->state = new_state; + spin_unlock(lock); + return 0; + } + if (new_state == FL_PM_SUSPENDED) { + if (chip->controller->active->state == FL_PM_SUSPENDED) { + chip->state = FL_PM_SUSPENDED; + spin_unlock(lock); + return 0; + } + } + set_current_state(TASK_UNINTERRUPTIBLE); + add_wait_queue(wq, &wait); + spin_unlock(lock); + schedule(); + remove_wait_queue(wq, &wait); + goto retry; +#else chip->state = new_state; return 0; +#endif +} + +/** + * panic_nand_wait - [GENERIC] wait until the command is done + * @mtd: MTD device structure + * @chip: NAND chip structure + * @timeo: timeout + * + * Wait for command done. This is a helper function for nand_wait used when + * we are in interrupt context. May happen when in panic and trying to write + * an oops through mtdoops. + */ +static void panic_nand_wait(struct mtd_info *mtd, struct nand_chip *chip, + unsigned long timeo) +{ + int i; + for (i = 0; i < timeo; i++) { + if (chip->dev_ready) { + if (chip->dev_ready(mtd)) + break; + } else { + if (chip->read_byte(mtd) & NAND_STATUS_READY) + break; + } + mdelay(1); + } } /** @@ -786,28 +1017,42 @@ nand_get_device(struct nand_chip *chip, struct mtd_info *mtd, int new_state) */ static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip) { - unsigned long timeo; - int state = chip->state; - u32 time_start; - if (state == FL_ERASING) - timeo = (CONFIG_SYS_HZ * 400) / 1000; - else - timeo = (CONFIG_SYS_HZ * 20) / 1000; + int status, state = chip->state; + unsigned long timeo = (state == FL_ERASING ? 400 : 20); - if ((state == FL_ERASING) && (chip->options & NAND_IS_AND)) - chip->cmdfunc(mtd, NAND_CMD_STATUS_MULTI, -1, -1); - else - chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1); + led_trigger_event(nand_led_trigger, LED_FULL); - time_start = get_timer(0); + /* + * Apply this short delay always to ensure that we do wait tWB in any + * case on any machine. + */ + ndelay(100); - while (1) { - if (get_timer(time_start) > timeo) { - printf("Timeout!"); - return 0x01; - } + chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1); +#ifndef __UBOOT__ + if (in_interrupt() || oops_in_progress) + panic_nand_wait(mtd, chip, timeo); + else { + timeo = jiffies + msecs_to_jiffies(timeo); + while (time_before(jiffies, timeo)) { + if (chip->dev_ready) { + if (chip->dev_ready(mtd)) + break; + } else { + if (chip->read_byte(mtd) & NAND_STATUS_READY) + break; + } + cond_resched(); + } + } +#else + u32 timer = (CONFIG_SYS_HZ * timeo) / 1000; + u32 time_start; + + time_start = get_timer(0); + while (get_timer(time_start) < timer) { if (chip->dev_ready) { if (chip->dev_ready(mtd)) break; @@ -816,16 +1061,177 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip) break; } } +#endif #ifdef PPCHAMELON_NAND_TIMER_HACK time_start = get_timer(0); while (get_timer(time_start) < 10) ; #endif /* PPCHAMELON_NAND_TIMER_HACK */ + led_trigger_event(nand_led_trigger, LED_OFF); + + status = (int)chip->read_byte(mtd); + /* This can happen if in case of timeout or buggy dev_ready */ + WARN_ON(!(status & NAND_STATUS_READY)); + return status; +} + +#ifndef __UBOOT__ +/** + * __nand_unlock - [REPLACEABLE] unlocks specified locked blocks + * @mtd: mtd info + * @ofs: offset to start unlock from + * @len: length to unlock + * @invert: when = 0, unlock the range of blocks within the lower and + * upper boundary address + * when = 1, unlock the range of blocks outside the boundaries + * of the lower and upper boundary address + * + * Returs unlock status. + */ +static int __nand_unlock(struct mtd_info *mtd, loff_t ofs, + uint64_t len, int invert) +{ + int ret = 0; + int status, page; + struct nand_chip *chip = mtd->priv; + + /* Submit address of first page to unlock */ + page = ofs >> chip->page_shift; + chip->cmdfunc(mtd, NAND_CMD_UNLOCK1, -1, page & chip->pagemask); + + /* Submit address of last page to unlock */ + page = (ofs + len) >> chip->page_shift; + chip->cmdfunc(mtd, NAND_CMD_UNLOCK2, -1, + (page | invert) & chip->pagemask); + + /* Call wait ready function */ + status = chip->waitfunc(mtd, chip); + /* See if device thinks it succeeded */ + if (status & NAND_STATUS_FAIL) { + pr_debug("%s: error status = 0x%08x\n", + __func__, status); + ret = -EIO; + } - return (int)chip->read_byte(mtd); + return ret; } /** + * nand_unlock - [REPLACEABLE] unlocks specified locked blocks + * @mtd: mtd info + * @ofs: offset to start unlock from + * @len: length to unlock + * + * Returns unlock status. + */ +int nand_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len) +{ + int ret = 0; + int chipnr; + struct nand_chip *chip = mtd->priv; + + pr_debug("%s: start = 0x%012llx, len = %llu\n", + __func__, (unsigned long long)ofs, len); + + if (check_offs_len(mtd, ofs, len)) + ret = -EINVAL; + + /* Align to last block address if size addresses end of the device */ + if (ofs + len == mtd->size) + len -= mtd->erasesize; + + nand_get_device(mtd, FL_UNLOCKING); + + /* Shift to get chip number */ + chipnr = ofs >> chip->chip_shift; + + chip->select_chip(mtd, chipnr); + + /* Check, if it is write protected */ + if (nand_check_wp(mtd)) { + pr_debug("%s: device is write protected!\n", + __func__); + ret = -EIO; + goto out; + } + + ret = __nand_unlock(mtd, ofs, len, 0); + +out: + chip->select_chip(mtd, -1); + nand_release_device(mtd); + + return ret; +} +EXPORT_SYMBOL(nand_unlock); + +/** + * nand_lock - [REPLACEABLE] locks all blocks present in the device + * @mtd: mtd info + * @ofs: offset to start unlock from + * @len: length to unlock + * + * This feature is not supported in many NAND parts. 'Micron' NAND parts do + * have this feature, but it allows only to lock all blocks, not for specified + * range for block. Implementing 'lock' feature by making use of 'unlock', for + * now. + * + * Returns lock status. + */ +int nand_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) +{ + int ret = 0; + int chipnr, status, page; + struct nand_chip *chip = mtd->priv; + + pr_debug("%s: start = 0x%012llx, len = %llu\n", + __func__, (unsigned long long)ofs, len); + + if (check_offs_len(mtd, ofs, len)) + ret = -EINVAL; + + nand_get_device(mtd, FL_LOCKING); + + /* Shift to get chip number */ + chipnr = ofs >> chip->chip_shift; + + chip->select_chip(mtd, chipnr); + + /* Check, if it is write protected */ + if (nand_check_wp(mtd)) { + pr_debug("%s: device is write protected!\n", + __func__); + status = MTD_ERASE_FAILED; + ret = -EIO; + goto out; + } + + /* Submit address of first page to lock */ + page = ofs >> chip->page_shift; + chip->cmdfunc(mtd, NAND_CMD_LOCK, -1, page & chip->pagemask); + + /* Call wait ready function */ + status = chip->waitfunc(mtd, chip); + /* See if device thinks it succeeded */ + if (status & NAND_STATUS_FAIL) { + pr_debug("%s: error status = 0x%08x\n", + __func__, status); + ret = -EIO; + goto out; + } + + ret = __nand_unlock(mtd, ofs, len, 0x1); + +out: + chip->select_chip(mtd, -1); + nand_release_device(mtd); + + return ret; +} +EXPORT_SYMBOL(nand_lock); +#endif + +/** * nand_read_page_raw - [INTERN] read raw page data without ecc * @mtd: mtd info structure * @chip: nand chip info structure @@ -906,6 +1312,7 @@ static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *ecc_calc = chip->buffers->ecccalc; uint8_t *ecc_code = chip->buffers->ecccode; uint32_t *eccpos = chip->ecc.layout->eccpos; + unsigned int max_bitflips = 0; chip->ecc.read_page_raw(mtd, chip, buf, 1, page); @@ -922,24 +1329,28 @@ static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, int stat; stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]); - if (stat < 0) + if (stat < 0) { mtd->ecc_stats.failed++; - else + } else { mtd->ecc_stats.corrected += stat; + max_bitflips = max_t(unsigned int, max_bitflips, stat); + } } - return 0; + return max_bitflips; } /** - * nand_read_subpage - [REPLACEABLE] software ECC based sub-page read function + * nand_read_subpage - [REPLACEABLE] ECC based sub-page read function * @mtd: mtd info structure * @chip: nand chip info structure * @data_offs: offset of requested data within the page * @readlen: data length * @bufpoi: buffer to store read data + * @page: page number to read */ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, - uint32_t data_offs, uint32_t readlen, uint8_t *bufpoi) + uint32_t data_offs, uint32_t readlen, uint8_t *bufpoi, + int page) { int start_step, end_step, num_steps; uint32_t *eccpos = chip->ecc.layout->eccpos; @@ -947,12 +1358,14 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, int data_col_addr, i, gaps = 0; int datafrag_len, eccfrag_len, aligned_len, aligned_pos; int busw = (chip->options & NAND_BUSWIDTH_16) ? 2 : 1; - int index = 0; + int index; + unsigned int max_bitflips = 0; /* Column address within the page aligned to ECC size (256bytes) */ start_step = data_offs / chip->ecc.size; end_step = (data_offs + readlen - 1) / chip->ecc.size; num_steps = end_step - start_step + 1; + index = start_step * chip->ecc.bytes; /* Data size aligned to ECC ecc.size */ datafrag_len = num_steps * chip->ecc.size; @@ -989,8 +1402,6 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, * Send the command to read the particular ECC bytes take care * about buswidth alignment in read_buf. */ - index = start_step * chip->ecc.bytes; - aligned_pos = eccpos[index] & ~(busw - 1); aligned_len = eccfrag_len; if (eccpos[index] & (busw - 1)) @@ -1012,12 +1423,14 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, stat = chip->ecc.correct(mtd, p, &chip->buffers->ecccode[i], &chip->buffers->ecccalc[i]); - if (stat < 0) + if (stat < 0) { mtd->ecc_stats.failed++; - else + } else { mtd->ecc_stats.corrected += stat; + max_bitflips = max_t(unsigned int, max_bitflips, stat); + } } - return 0; + return max_bitflips; } /** @@ -1040,6 +1453,7 @@ static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *ecc_calc = chip->buffers->ecccalc; uint8_t *ecc_code = chip->buffers->ecccode; uint32_t *eccpos = chip->ecc.layout->eccpos; + unsigned int max_bitflips = 0; for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { chip->ecc.hwctl(mtd, NAND_ECC_READ); @@ -1058,12 +1472,14 @@ static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, int stat; stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]); - if (stat < 0) + if (stat < 0) { mtd->ecc_stats.failed++; - else + } else { mtd->ecc_stats.corrected += stat; + max_bitflips = max_t(unsigned int, max_bitflips, stat); + } } - return 0; + return max_bitflips; } /** @@ -1090,6 +1506,7 @@ static int nand_read_page_hwecc_oob_first(struct mtd_info *mtd, uint8_t *ecc_code = chip->buffers->ecccode; uint32_t *eccpos = chip->ecc.layout->eccpos; uint8_t *ecc_calc = chip->buffers->ecccalc; + unsigned int max_bitflips = 0; /* Read the OOB area first */ chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page); @@ -1107,12 +1524,14 @@ static int nand_read_page_hwecc_oob_first(struct mtd_info *mtd, chip->ecc.calculate(mtd, p, &ecc_calc[i]); stat = chip->ecc.correct(mtd, p, &ecc_code[i], NULL); - if (stat < 0) + if (stat < 0) { mtd->ecc_stats.failed++; - else + } else { mtd->ecc_stats.corrected += stat; + max_bitflips = max_t(unsigned int, max_bitflips, stat); + } } - return 0; + return max_bitflips; } /** @@ -1134,6 +1553,7 @@ static int nand_read_page_syndrome(struct mtd_info *mtd, struct nand_chip *chip, int eccsteps = chip->ecc.steps; uint8_t *p = buf; uint8_t *oob = chip->oob_poi; + unsigned int max_bitflips = 0; for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { int stat; @@ -1150,10 +1570,12 @@ static int nand_read_page_syndrome(struct mtd_info *mtd, struct nand_chip *chip, chip->read_buf(mtd, oob, eccbytes); stat = chip->ecc.correct(mtd, p, oob, NULL); - if (stat < 0) + if (stat < 0) { mtd->ecc_stats.failed++; - else + } else { mtd->ecc_stats.corrected += stat; + max_bitflips = max_t(unsigned int, max_bitflips, stat); + } oob += eccbytes; @@ -1168,7 +1590,7 @@ static int nand_read_page_syndrome(struct mtd_info *mtd, struct nand_chip *chip, if (i) chip->read_buf(mtd, oob, i); - return 0; + return max_bitflips; } /** @@ -1220,6 +1642,30 @@ static uint8_t *nand_transfer_oob(struct nand_chip *chip, uint8_t *oob, } /** + * nand_setup_read_retry - [INTERN] Set the READ RETRY mode + * @mtd: MTD device structure + * @retry_mode: the retry mode to use + * + * Some vendors supply a special command to shift the Vt threshold, to be used + * when there are too many bitflips in a page (i.e., ECC error). After setting + * a new threshold, the host should retry reading the page. + */ +static int nand_setup_read_retry(struct mtd_info *mtd, int retry_mode) +{ + struct nand_chip *chip = mtd->priv; + + pr_debug("setting READ RETRY mode %d\n", retry_mode); + + if (retry_mode >= chip->read_retries) + return -EINVAL; + + if (!chip->setup_read_retry) + return -EOPNOTSUPP; + + return chip->setup_read_retry(mtd, retry_mode); +} + +/** * nand_do_read_ops - [INTERN] Read data with ECC * @mtd: MTD device structure * @from: offset to read from @@ -1232,7 +1678,6 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, { int chipnr, page, realpage, col, bytes, aligned, oob_required; struct nand_chip *chip = mtd->priv; - struct mtd_ecc_stats stats; int ret = 0; uint32_t readlen = ops->len; uint32_t oobreadlen = ops->ooblen; @@ -1241,8 +1686,8 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, uint8_t *bufpoi, *oob, *buf; unsigned int max_bitflips = 0; - - stats = mtd->ecc_stats; + int retry_mode = 0; + bool ecc_fail = false; chipnr = (int)(from >> chip->chip_shift); chip->select_chip(mtd, chipnr); @@ -1257,8 +1702,9 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, oob_required = oob ? 1 : 0; while (1) { - WATCHDOG_RESET(); + unsigned int ecc_failures = mtd->ecc_stats.failed; + WATCHDOG_RESET(); bytes = min(mtd->writesize - col, readlen); aligned = (bytes == mtd->writesize); @@ -1266,6 +1712,7 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, if (realpage != chip->pagebuf || oob) { bufpoi = aligned ? buf : chip->buffers->databuf; +read_retry: chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page); /* @@ -1277,9 +1724,10 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, oob_required, page); else if (!aligned && NAND_HAS_SUBPAGE_READ(chip) && - !oob) + !oob) ret = chip->ecc.read_subpage(mtd, chip, - col, bytes, bufpoi); + col, bytes, bufpoi, + page); else ret = chip->ecc.read_page(mtd, chip, bufpoi, oob_required, page); @@ -1295,7 +1743,7 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, /* Transfer not aligned data */ if (!aligned) { if (!NAND_HAS_SUBPAGE_READ(chip) && !oob && - !(mtd->ecc_stats.failed - stats.failed) && + !(mtd->ecc_stats.failed - ecc_failures) && (ops->mode != MTD_OPS_RAW)) { chip->pagebuf = realpage; chip->pagebuf_bitflips = ret; @@ -1306,8 +1754,6 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, memcpy(buf, chip->buffers->databuf + col, bytes); } - buf += bytes; - if (unlikely(oob)) { int toread = min(oobreadlen, max_oobsize); @@ -1317,6 +1763,33 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, oobreadlen -= toread; } } + + if (chip->options & NAND_NEED_READRDY) { + /* Apply delay or wait for ready/busy pin */ + if (!chip->dev_ready) + udelay(chip->chip_delay); + else + nand_wait_ready(mtd); + } + + if (mtd->ecc_stats.failed - ecc_failures) { + if (retry_mode + 1 < chip->read_retries) { + retry_mode++; + ret = nand_setup_read_retry(mtd, + retry_mode); + if (ret < 0) + break; + + /* Reset failures; retry */ + mtd->ecc_stats.failed = ecc_failures; + goto read_retry; + } else { + /* No more retry modes; real failure */ + ecc_fail = true; + } + } + + buf += bytes; } else { memcpy(buf, chip->buffers->databuf + col, bytes); buf += bytes; @@ -1326,6 +1799,14 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, readlen -= bytes; + /* Reset to retry mode 0 */ + if (retry_mode) { + ret = nand_setup_read_retry(mtd, 0); + if (ret < 0) + break; + retry_mode = 0; + } + if (!readlen) break; @@ -1342,15 +1823,16 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, chip->select_chip(mtd, chipnr); } } + chip->select_chip(mtd, -1); ops->retlen = ops->len - (size_t) readlen; if (oob) ops->oobretlen = ops->ooblen - oobreadlen; - if (ret) + if (ret < 0) return ret; - if (mtd->ecc_stats.failed - stats.failed) + if (ecc_fail) return -EBADMSG; return max_bitflips; @@ -1369,11 +1851,10 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, static int nand_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, uint8_t *buf) { - struct nand_chip *chip = mtd->priv; struct mtd_oob_ops ops; int ret; - nand_get_device(chip, mtd, FL_READING); + nand_get_device(mtd, FL_READING); ops.len = len; ops.datbuf = buf; ops.oobbuf = NULL; @@ -1537,7 +2018,7 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from, uint8_t *buf = ops->oobbuf; int ret = 0; - MTDDEBUG(MTD_DEBUG_LEVEL3, "%s: from = 0x%08Lx, len = %i\n", + pr_debug("%s: from = 0x%08Lx, len = %i\n", __func__, (unsigned long long)from, readlen); stats = mtd->ecc_stats; @@ -1548,8 +2029,8 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from, len = mtd->oobsize; if (unlikely(ops->ooboffs >= len)) { - MTDDEBUG(MTD_DEBUG_LEVEL0, "%s: Attempt to start read " - "outside oob\n", __func__); + pr_debug("%s: attempt to start read outside oob\n", + __func__); return -EINVAL; } @@ -1557,8 +2038,8 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from, if (unlikely(from >= mtd->size || ops->ooboffs + readlen > ((mtd->size >> chip->page_shift) - (from >> chip->page_shift)) * len)) { - MTDDEBUG(MTD_DEBUG_LEVEL0, "%s: Attempt read beyond end " - "of device\n", __func__); + pr_debug("%s: attempt to read beyond end of device\n", + __func__); return -EINVAL; } @@ -1571,6 +2052,7 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from, while (1) { WATCHDOG_RESET(); + if (ops->mode == MTD_OPS_RAW) ret = chip->ecc.read_oob_raw(mtd, chip, page); else @@ -1582,6 +2064,14 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from, len = min(len, readlen); buf = nand_transfer_oob(chip, buf, ops, len); + if (chip->options & NAND_NEED_READRDY) { + /* Apply delay or wait for ready/busy pin */ + if (!chip->dev_ready) + udelay(chip->chip_delay); + else + nand_wait_ready(mtd); + } + readlen -= len; if (!readlen) break; @@ -1597,6 +2087,7 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from, chip->select_chip(mtd, chipnr); } } + chip->select_chip(mtd, -1); ops->oobretlen = ops->ooblen - readlen; @@ -1620,19 +2111,18 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from, static int nand_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops) { - struct nand_chip *chip = mtd->priv; int ret = -ENOTSUPP; ops->retlen = 0; /* Do not allow reads past end of device */ if (ops->datbuf && (from + ops->len) > mtd->size) { - MTDDEBUG(MTD_DEBUG_LEVEL0, "%s: Attempt read " - "beyond end of device\n", __func__); + pr_debug("%s: attempt to read beyond end of device\n", + __func__); return -EINVAL; } - nand_get_device(chip, mtd, FL_READING); + nand_get_device(mtd, FL_READING); switch (ops->mode) { case MTD_OPS_PLACE_OOB: @@ -1701,7 +2191,7 @@ static int nand_write_page_raw_syndrome(struct mtd_info *mtd, oob += chip->ecc.prepad; } - chip->read_buf(mtd, oob, eccbytes); + chip->write_buf(mtd, oob, eccbytes); oob += eccbytes; if (chip->ecc.postpad) { @@ -1774,6 +2264,68 @@ static int nand_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, return 0; } + +/** + * nand_write_subpage_hwecc - [REPLACABLE] hardware ECC based subpage write + * @mtd: mtd info structure + * @chip: nand chip info structure + * @offset: column address of subpage within the page + * @data_len: data length + * @buf: data buffer + * @oob_required: must write chip->oob_poi to OOB + */ +static int nand_write_subpage_hwecc(struct mtd_info *mtd, + struct nand_chip *chip, uint32_t offset, + uint32_t data_len, const uint8_t *buf, + int oob_required) +{ + uint8_t *oob_buf = chip->oob_poi; + uint8_t *ecc_calc = chip->buffers->ecccalc; + int ecc_size = chip->ecc.size; + int ecc_bytes = chip->ecc.bytes; + int ecc_steps = chip->ecc.steps; + uint32_t *eccpos = chip->ecc.layout->eccpos; + uint32_t start_step = offset / ecc_size; + uint32_t end_step = (offset + data_len - 1) / ecc_size; + int oob_bytes = mtd->oobsize / ecc_steps; + int step, i; + + for (step = 0; step < ecc_steps; step++) { + /* configure controller for WRITE access */ + chip->ecc.hwctl(mtd, NAND_ECC_WRITE); + + /* write data (untouched subpages already masked by 0xFF) */ + chip->write_buf(mtd, buf, ecc_size); + + /* mask ECC of un-touched subpages by padding 0xFF */ + if ((step < start_step) || (step > end_step)) + memset(ecc_calc, 0xff, ecc_bytes); + else + chip->ecc.calculate(mtd, buf, ecc_calc); + + /* mask OOB of un-touched subpages by padding 0xFF */ + /* if oob_required, preserve OOB metadata of written subpage */ + if (!oob_required || (step < start_step) || (step > end_step)) + memset(oob_buf, 0xff, oob_bytes); + + buf += ecc_size; + ecc_calc += ecc_bytes; + oob_buf += oob_bytes; + } + + /* copy calculated ECC for whole page to chip->buffer->oob */ + /* this include masked-value(0xFF) for unwritten subpages */ + ecc_calc = chip->buffers->ecccalc; + for (i = 0; i < chip->ecc.total; i++) + chip->oob_poi[eccpos[i]] = ecc_calc[i]; + + /* write OOB buffer to NAND device */ + chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); + + return 0; +} + + /** * nand_write_page_syndrome - [REPLACEABLE] hardware ECC syndrome based page write * @mtd: mtd info structure @@ -1826,6 +2378,8 @@ static int nand_write_page_syndrome(struct mtd_info *mtd, * nand_write_page - [REPLACEABLE] write one page * @mtd: MTD device structure * @chip: NAND chip descriptor + * @offset: address offset within the page + * @data_len: length of actual data to be written * @buf: the data to write * @oob_required: must write chip->oob_poi to OOB * @page: page number to write @@ -1833,15 +2387,25 @@ static int nand_write_page_syndrome(struct mtd_info *mtd, * @raw: use _raw version of write_page */ static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip, - const uint8_t *buf, int oob_required, int page, - int cached, int raw) + uint32_t offset, int data_len, const uint8_t *buf, + int oob_required, int page, int cached, int raw) { - int status; + int status, subpage; + + if (!(chip->options & NAND_NO_SUBPAGE_WRITE) && + chip->ecc.write_subpage) + subpage = offset || (data_len < mtd->writesize); + else + subpage = 0; chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page); if (unlikely(raw)) - status = chip->ecc.write_page_raw(mtd, chip, buf, oob_required); + status = chip->ecc.write_page_raw(mtd, chip, buf, + oob_required); + else if (subpage) + status = chip->ecc.write_subpage(mtd, chip, offset, data_len, + buf, oob_required); else status = chip->ecc.write_page(mtd, chip, buf, oob_required); @@ -1854,7 +2418,7 @@ static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip, */ cached = 0; - if (!cached || !(chip->options & NAND_CACHEPRG)) { + if (!cached || !NAND_HAS_CACHEPROG(chip)) { chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); status = chip->waitfunc(mtd, chip); @@ -1873,7 +2437,9 @@ static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip, status = chip->waitfunc(mtd, chip); } -#ifdef CONFIG_MTD_NAND_VERIFY_WRITE + +#ifdef __UBOOT__ +#if defined(CONFIG_MTD_NAND_VERIFY_WRITE) /* Send command to read back the data */ chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page); @@ -1883,6 +2449,8 @@ static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip, /* Make sure the next page prog is preceded by a status read */ chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1); #endif +#endif + return 0; } @@ -1965,26 +2533,34 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to, uint8_t *oob = ops->oobbuf; uint8_t *buf = ops->datbuf; - int ret, subpage; + int ret; int oob_required = oob ? 1 : 0; ops->retlen = 0; if (!writelen) return 0; - column = to & (mtd->writesize - 1); - subpage = column || (writelen & (mtd->writesize - 1)); - - if (subpage && oob) +#ifndef __UBOOT__ + /* Reject writes, which are not page aligned */ + if (NOTALIGNED(to) || NOTALIGNED(ops->len)) { +#else + /* Reject writes, which are not page aligned */ + if (NOTALIGNED(to)) { +#endif + pr_notice("%s: attempt to write non page aligned data\n", + __func__); return -EINVAL; + } + + column = to & (mtd->writesize - 1); chipnr = (int)(to >> chip->chip_shift); chip->select_chip(mtd, chipnr); /* Check, if it is write protected */ if (nand_check_wp(mtd)) { - printk (KERN_NOTICE "nand_do_write_ops: Device is write protected\n"); - return -EIO; + ret = -EIO; + goto err_out; } realpage = (int)(to >> chip->page_shift); @@ -1997,18 +2573,19 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to, chip->pagebuf = -1; /* Don't allow multipage oob writes with offset */ - if (oob && ops->ooboffs && (ops->ooboffs + ops->ooblen > oobmaxlen)) - return -EINVAL; + if (oob && ops->ooboffs && (ops->ooboffs + ops->ooblen > oobmaxlen)) { + ret = -EINVAL; + goto err_out; + } while (1) { - WATCHDOG_RESET(); - int bytes = mtd->writesize; int cached = writelen > bytes && page != blockmask; uint8_t *wbuf = buf; + WATCHDOG_RESET(); /* Partial page write? */ - if (unlikely(column || writelen < mtd->writesize)) { + if (unlikely(column || writelen < (mtd->writesize - 1))) { cached = 0; bytes = min_t(int, bytes - column, (int) writelen); chip->pagebuf = -1; @@ -2025,9 +2602,9 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to, /* We still need to erase leftover OOB data */ memset(chip->oob_poi, 0xff, mtd->oobsize); } - - ret = chip->write_page(mtd, chip, wbuf, oob_required, page, - cached, (ops->mode == MTD_OPS_RAW)); + ret = chip->write_page(mtd, chip, column, bytes, wbuf, + oob_required, page, cached, + (ops->mode == MTD_OPS_RAW)); if (ret) break; @@ -2051,6 +2628,44 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to, ops->retlen = ops->len - writelen; if (unlikely(oob)) ops->oobretlen = ops->ooblen; + +err_out: + chip->select_chip(mtd, -1); + return ret; +} + +/** + * panic_nand_write - [MTD Interface] NAND write with ECC + * @mtd: MTD device structure + * @to: offset to write to + * @len: number of bytes to write + * @retlen: pointer to variable to store the number of written bytes + * @buf: the data to write + * + * NAND write with ECC. Used when performing writes in interrupt context, this + * may for example be called by mtdoops when writing an oops while in panic. + */ +static int panic_nand_write(struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, const uint8_t *buf) +{ + struct nand_chip *chip = mtd->priv; + struct mtd_oob_ops ops; + int ret; + + /* Wait for the device to get ready */ + panic_nand_wait(mtd, chip, 400); + + /* Grab the device */ + panic_nand_get_device(chip, mtd, FL_WRITING); + + ops.len = len; + ops.datbuf = (uint8_t *)buf; + ops.oobbuf = NULL; + ops.mode = MTD_OPS_PLACE_OOB; + + ret = nand_do_write_ops(mtd, to, &ops); + + *retlen = ops.retlen; return ret; } @@ -2067,11 +2682,10 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to, static int nand_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const uint8_t *buf) { - struct nand_chip *chip = mtd->priv; struct mtd_oob_ops ops; int ret; - nand_get_device(chip, mtd, FL_WRITING); + nand_get_device(mtd, FL_WRITING); ops.len = len; ops.datbuf = (uint8_t *)buf; ops.oobbuf = NULL; @@ -2096,7 +2710,7 @@ static int nand_do_write_oob(struct mtd_info *mtd, loff_t to, int chipnr, page, status, len; struct nand_chip *chip = mtd->priv; - MTDDEBUG(MTD_DEBUG_LEVEL3, "%s: to = 0x%08x, len = %i\n", + pr_debug("%s: to = 0x%08x, len = %i\n", __func__, (unsigned int)to, (int)ops->ooblen); if (ops->mode == MTD_OPS_AUTO_OOB) @@ -2106,14 +2720,14 @@ static int nand_do_write_oob(struct mtd_info *mtd, loff_t to, /* Do not allow write past end of page */ if ((ops->ooboffs + ops->ooblen) > len) { - MTDDEBUG(MTD_DEBUG_LEVEL0, "%s: Attempt to write " - "past end of page\n", __func__); + pr_debug("%s: attempt to write past end of page\n", + __func__); return -EINVAL; } if (unlikely(ops->ooboffs >= len)) { - MTDDEBUG(MTD_DEBUG_LEVEL0, "%s: Attempt to start " - "write outside oob\n", __func__); + pr_debug("%s: attempt to start write outside oob\n", + __func__); return -EINVAL; } @@ -2122,8 +2736,8 @@ static int nand_do_write_oob(struct mtd_info *mtd, loff_t to, ops->ooboffs + ops->ooblen > ((mtd->size >> chip->page_shift) - (to >> chip->page_shift)) * len)) { - MTDDEBUG(MTD_DEBUG_LEVEL0, "%s: Attempt write beyond " - "end of device\n", __func__); + pr_debug("%s: attempt to write beyond end of device\n", + __func__); return -EINVAL; } @@ -2142,8 +2756,10 @@ static int nand_do_write_oob(struct mtd_info *mtd, loff_t to, chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); /* Check, if it is write protected */ - if (nand_check_wp(mtd)) + if (nand_check_wp(mtd)) { + chip->select_chip(mtd, -1); return -EROFS; + } /* Invalidate the page cache, if we write to the cached page */ if (page == chip->pagebuf) @@ -2156,6 +2772,8 @@ static int nand_do_write_oob(struct mtd_info *mtd, loff_t to, else status = chip->ecc.write_oob(mtd, chip, page & chip->pagemask); + chip->select_chip(mtd, -1); + if (status) return status; @@ -2173,19 +2791,18 @@ static int nand_do_write_oob(struct mtd_info *mtd, loff_t to, static int nand_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops) { - struct nand_chip *chip = mtd->priv; int ret = -ENOTSUPP; ops->retlen = 0; /* Do not allow writes past end of device */ if (ops->datbuf && (to + ops->len) > mtd->size) { - MTDDEBUG(MTD_DEBUG_LEVEL0, "%s: Attempt write beyond " - "end of device\n", __func__); + pr_debug("%s: attempt to write beyond end of device\n", + __func__); return -EINVAL; } - nand_get_device(chip, mtd, FL_WRITING); + nand_get_device(mtd, FL_WRITING); switch (ops->mode) { case MTD_OPS_PLACE_OOB: @@ -2223,24 +2840,6 @@ static void single_erase_cmd(struct mtd_info *mtd, int page) } /** - * multi_erase_cmd - [GENERIC] AND specific block erase command function - * @mtd: MTD device structure - * @page: the page address of the block which will be erased - * - * AND multi block erase command function. Erase 4 consecutive blocks. - */ -static void multi_erase_cmd(struct mtd_info *mtd, int page) -{ - struct nand_chip *chip = mtd->priv; - /* Send commands to erase a block */ - chip->cmdfunc(mtd, NAND_CMD_ERASE1, -1, page++); - chip->cmdfunc(mtd, NAND_CMD_ERASE1, -1, page++); - chip->cmdfunc(mtd, NAND_CMD_ERASE1, -1, page++); - chip->cmdfunc(mtd, NAND_CMD_ERASE1, -1, page); - chip->cmdfunc(mtd, NAND_CMD_ERASE2, -1, -1); -} - -/** * nand_erase - [MTD Interface] erase block(s) * @mtd: MTD device structure * @instr: erase instruction @@ -2252,7 +2851,6 @@ static int nand_erase(struct mtd_info *mtd, struct erase_info *instr) return nand_erase_nand(mtd, instr, 0); } -#define BBT_PAGE_MASK 0xffffff3f /** * nand_erase_nand - [INTERN] erase block(s) * @mtd: MTD device structure @@ -2266,19 +2864,17 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr, { int page, status, pages_per_block, ret, chipnr; struct nand_chip *chip = mtd->priv; - loff_t rewrite_bbt[CONFIG_SYS_NAND_MAX_CHIPS] = {0}; - unsigned int bbt_masked_page = 0xffffffff; loff_t len; - MTDDEBUG(MTD_DEBUG_LEVEL3, "%s: start = 0x%012llx, len = %llu\n", - __func__, (unsigned long long)instr->addr, - (unsigned long long)instr->len); + pr_debug("%s: start = 0x%012llx, len = %llu\n", + __func__, (unsigned long long)instr->addr, + (unsigned long long)instr->len); if (check_offs_len(mtd, instr->addr, instr->len)) return -EINVAL; /* Grab the lock and see if the device is available */ - nand_get_device(chip, mtd, FL_ERASING); + nand_get_device(mtd, FL_ERASING); /* Shift to get first page */ page = (int)(instr->addr >> chip->page_shift); @@ -2292,21 +2888,12 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr, /* Check, if it is write protected */ if (nand_check_wp(mtd)) { - MTDDEBUG(MTD_DEBUG_LEVEL0, "%s: Device is write protected!!!\n", - __func__); + pr_debug("%s: device is write protected!\n", + __func__); instr->state = MTD_ERASE_FAILED; goto erase_exit; } - /* - * If BBT requires refresh, set the BBT page mask to see if the BBT - * should be rewritten. Otherwise the mask is set to 0xffffffff which - * can not be matched. This is also done when the bbt is actually - * erased to avoid recursive updates. - */ - if (chip->options & BBT_AUTO_REFRESH && !allowbbt) - bbt_masked_page = chip->bbt_td->pages[chipnr] & BBT_PAGE_MASK; - /* Loop through the pages */ len = instr->len; @@ -2314,11 +2901,12 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr, while (len) { WATCHDOG_RESET(); + /* Check if we have a bad block, we do not erase bad blocks! */ - if (!instr->scrub && nand_block_checkbad(mtd, ((loff_t) page) << + if (nand_block_checkbad(mtd, ((loff_t) page) << chip->page_shift, 0, allowbbt)) { pr_warn("%s: attempt to erase a bad block at page 0x%08x\n", - __func__, page); + __func__, page); instr->state = MTD_ERASE_FAILED; goto erase_exit; } @@ -2345,25 +2933,16 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr, /* See if block erase succeeded */ if (status & NAND_STATUS_FAIL) { - MTDDEBUG(MTD_DEBUG_LEVEL0, "%s: Failed erase, " - "page 0x%08x\n", __func__, page); + pr_debug("%s: failed erase, page 0x%08x\n", + __func__, page); instr->state = MTD_ERASE_FAILED; instr->fail_addr = ((loff_t)page << chip->page_shift); goto erase_exit; } - /* - * If BBT requires refresh, set the BBT rewrite flag to the - * page being erased. - */ - if (bbt_masked_page != 0xffffffff && - (page & BBT_PAGE_MASK) == bbt_masked_page) - rewrite_bbt[chipnr] = - ((loff_t)page << chip->page_shift); - /* Increment page address and decrement length */ - len -= (1 << chip->phys_erase_shift); + len -= (1ULL << chip->phys_erase_shift); page += pages_per_block; /* Check, if we cross a chip boundary */ @@ -2371,15 +2950,6 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr, chipnr++; chip->select_chip(mtd, -1); chip->select_chip(mtd, chipnr); - - /* - * If BBT requires refresh and BBT-PERCHIP, set the BBT - * page mask to see if this BBT should be rewritten. - */ - if (bbt_masked_page != 0xffffffff && - (chip->bbt_td->options & NAND_BBT_PERCHIP)) - bbt_masked_page = chip->bbt_td->pages[chipnr] & - BBT_PAGE_MASK; } } instr->state = MTD_ERASE_DONE; @@ -2389,29 +2959,13 @@ erase_exit: ret = instr->state == MTD_ERASE_DONE ? 0 : -EIO; /* Deselect and wake up anyone waiting on the device */ + chip->select_chip(mtd, -1); nand_release_device(mtd); /* Do call back function */ if (!ret) mtd_erase_callback(instr); - /* - * If BBT requires refresh and erase was successful, rewrite any - * selected bad block tables. - */ - if (bbt_masked_page == 0xffffffff || ret) - return ret; - - for (chipnr = 0; chipnr < chip->numchips; chipnr++) { - if (!rewrite_bbt[chipnr]) - continue; - /* Update the BBT for chip */ - MTDDEBUG(MTD_DEBUG_LEVEL0, "%s: nand_update_bbt " - "(%d:0x%0llx 0x%0x)\n", __func__, chipnr, - rewrite_bbt[chipnr], chip->bbt_td->pages[chipnr]); - nand_update_bbt(mtd, rewrite_bbt[chipnr]); - } - /* Return more or less happy */ return ret; } @@ -2424,12 +2978,10 @@ erase_exit: */ static void nand_sync(struct mtd_info *mtd) { - struct nand_chip *chip = mtd->priv; - - MTDDEBUG(MTD_DEBUG_LEVEL3, "%s: called\n", __func__); + pr_debug("%s: called\n", __func__); /* Grab the lock and see if the device is available */ - nand_get_device(chip, mtd, FL_SYNCING); + nand_get_device(mtd, FL_SYNCING); /* Release it and go back */ nand_release_device(mtd); } @@ -2451,7 +3003,6 @@ static int nand_block_isbad(struct mtd_info *mtd, loff_t offs) */ static int nand_block_markbad(struct mtd_info *mtd, loff_t ofs) { - struct nand_chip *chip = mtd->priv; int ret; ret = nand_block_isbad(mtd, ofs); @@ -2462,10 +3013,10 @@ static int nand_block_markbad(struct mtd_info *mtd, loff_t ofs) return ret; } - return chip->block_markbad(mtd, ofs); + return nand_block_markbad_lowlevel(mtd, ofs); } - /** +/** * nand_onfi_set_features- [REPLACEABLE] set features for ONFI nand * @mtd: MTD device structure * @chip: nand chip info structure @@ -2476,12 +3027,19 @@ static int nand_onfi_set_features(struct mtd_info *mtd, struct nand_chip *chip, int addr, uint8_t *subfeature_param) { int status; + int i; - if (!chip->onfi_version) +#ifdef CONFIG_SYS_NAND_ONFI_DETECTION + if (!chip->onfi_version || + !(le16_to_cpu(chip->onfi_params.opt_cmd) + & ONFI_OPT_CMD_SET_GET_FEATURES)) return -EINVAL; +#endif chip->cmdfunc(mtd, NAND_CMD_SET_FEATURES, addr, -1); - chip->write_buf(mtd, subfeature_param, ONFI_SUBFEATURE_PARAM_LEN); + for (i = 0; i < ONFI_SUBFEATURE_PARAM_LEN; ++i) + chip->write_byte(mtd, subfeature_param[i]); + status = chip->waitfunc(mtd, chip); if (status & NAND_STATUS_FAIL) return -EIO; @@ -2498,17 +3056,50 @@ static int nand_onfi_set_features(struct mtd_info *mtd, struct nand_chip *chip, static int nand_onfi_get_features(struct mtd_info *mtd, struct nand_chip *chip, int addr, uint8_t *subfeature_param) { - if (!chip->onfi_version) + int i; + +#ifdef CONFIG_SYS_NAND_ONFI_DETECTION + if (!chip->onfi_version || + !(le16_to_cpu(chip->onfi_params.opt_cmd) + & ONFI_OPT_CMD_SET_GET_FEATURES)) return -EINVAL; +#endif /* clear the sub feature parameters */ memset(subfeature_param, 0, ONFI_SUBFEATURE_PARAM_LEN); chip->cmdfunc(mtd, NAND_CMD_GET_FEATURES, addr, -1); - chip->read_buf(mtd, subfeature_param, ONFI_SUBFEATURE_PARAM_LEN); + for (i = 0; i < ONFI_SUBFEATURE_PARAM_LEN; ++i) + *subfeature_param++ = chip->read_byte(mtd); return 0; } +#ifndef __UBOOT__ +/** + * nand_suspend - [MTD Interface] Suspend the NAND flash + * @mtd: MTD device structure + */ +static int nand_suspend(struct mtd_info *mtd) +{ + return nand_get_device(mtd, FL_PM_SUSPENDED); +} + +/** + * nand_resume - [MTD Interface] Resume the NAND flash + * @mtd: MTD device structure + */ +static void nand_resume(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd->priv; + + if (chip->state == FL_PM_SUSPENDED) + nand_release_device(mtd); + else + pr_err("%s called for a chip which is not in suspended state\n", + __func__); +} +#endif + /* Set default functions */ static void nand_set_defaults(struct nand_chip *chip, int busw) { @@ -2526,7 +3117,15 @@ static void nand_set_defaults(struct nand_chip *chip, int busw) if (!chip->select_chip) chip->select_chip = nand_select_chip; - if (!chip->read_byte) + + /* set for ONFI nand */ + if (!chip->onfi_set_features) + chip->onfi_set_features = nand_onfi_set_features; + if (!chip->onfi_get_features) + chip->onfi_get_features = nand_onfi_get_features; + + /* If called twice, pointers that depend on busw may need to be reset */ + if (!chip->read_byte || chip->read_byte == nand_read_byte) chip->read_byte = busw ? nand_read_byte16 : nand_read_byte; if (!chip->read_word) chip->read_word = nand_read_word; @@ -2534,21 +3133,35 @@ static void nand_set_defaults(struct nand_chip *chip, int busw) chip->block_bad = nand_block_bad; if (!chip->block_markbad) chip->block_markbad = nand_default_block_markbad; - if (!chip->write_buf) + if (!chip->write_buf || chip->write_buf == nand_write_buf) chip->write_buf = busw ? nand_write_buf16 : nand_write_buf; - if (!chip->read_buf) + if (!chip->write_byte || chip->write_byte == nand_write_byte) + chip->write_byte = busw ? nand_write_byte16 : nand_write_byte; + if (!chip->read_buf || chip->read_buf == nand_read_buf) chip->read_buf = busw ? nand_read_buf16 : nand_read_buf; - if (!chip->verify_buf) - chip->verify_buf = busw ? nand_verify_buf16 : nand_verify_buf; if (!chip->scan_bbt) chip->scan_bbt = nand_default_bbt; - if (!chip->controller) +#ifdef __UBOOT__ +#if defined(CONFIG_MTD_NAND_VERIFY_WRITE) + if (!chip->verify_buf) + chip->verify_buf = busw ? nand_verify_buf16 : nand_verify_buf; +#endif +#endif + + if (!chip->controller) { chip->controller = &chip->hwcontrol; + spin_lock_init(&chip->controller->lock); + init_waitqueue_head(&chip->controller->wq); + } + } -#ifdef CONFIG_SYS_NAND_ONFI_DETECTION /* Sanitize ONFI strings so we can safely print them */ +#ifndef __UBOOT__ +static void sanitize_string(uint8_t *s, size_t len) +#else static void sanitize_string(char *s, size_t len) +#endif { ssize_t i; @@ -2577,6 +3190,106 @@ static u16 onfi_crc16(u16 crc, u8 const *p, size_t len) return crc; } +#ifdef CONFIG_SYS_NAND_ONFI_DETECTION +/* Parse the Extended Parameter Page. */ +static int nand_flash_detect_ext_param_page(struct mtd_info *mtd, + struct nand_chip *chip, struct nand_onfi_params *p) +{ + struct onfi_ext_param_page *ep; + struct onfi_ext_section *s; + struct onfi_ext_ecc_info *ecc; + uint8_t *cursor; + int ret = -EINVAL; + int len; + int i; + + len = le16_to_cpu(p->ext_param_page_length) * 16; + ep = kmalloc(len, GFP_KERNEL); + if (!ep) + return -ENOMEM; + + /* Send our own NAND_CMD_PARAM. */ + chip->cmdfunc(mtd, NAND_CMD_PARAM, 0, -1); + + /* Use the Change Read Column command to skip the ONFI param pages. */ + chip->cmdfunc(mtd, NAND_CMD_RNDOUT, + sizeof(*p) * p->num_of_param_pages , -1); + + /* Read out the Extended Parameter Page. */ + chip->read_buf(mtd, (uint8_t *)ep, len); + if ((onfi_crc16(ONFI_CRC_BASE, ((uint8_t *)ep) + 2, len - 2) + != le16_to_cpu(ep->crc))) { + pr_debug("fail in the CRC.\n"); + goto ext_out; + } + + /* + * Check the signature. + * Do not strictly follow the ONFI spec, maybe changed in future. + */ +#ifndef __UBOOT__ + if (strncmp(ep->sig, "EPPS", 4)) { +#else + if (strncmp((char *)ep->sig, "EPPS", 4)) { +#endif + pr_debug("The signature is invalid.\n"); + goto ext_out; + } + + /* find the ECC section. */ + cursor = (uint8_t *)(ep + 1); + for (i = 0; i < ONFI_EXT_SECTION_MAX; i++) { + s = ep->sections + i; + if (s->type == ONFI_SECTION_TYPE_2) + break; + cursor += s->length * 16; + } + if (i == ONFI_EXT_SECTION_MAX) { + pr_debug("We can not find the ECC section.\n"); + goto ext_out; + } + + /* get the info we want. */ + ecc = (struct onfi_ext_ecc_info *)cursor; + + if (!ecc->codeword_size) { + pr_debug("Invalid codeword size\n"); + goto ext_out; + } + + chip->ecc_strength_ds = ecc->ecc_bits; + chip->ecc_step_ds = 1 << ecc->codeword_size; + ret = 0; + +ext_out: + kfree(ep); + return ret; +} + +static int nand_setup_read_retry_micron(struct mtd_info *mtd, int retry_mode) +{ + struct nand_chip *chip = mtd->priv; + uint8_t feature[ONFI_SUBFEATURE_PARAM_LEN] = {retry_mode}; + + return chip->onfi_set_features(mtd, chip, ONFI_FEATURE_ADDR_READ_RETRY, + feature); +} + +/* + * Configure chip properties from Micron vendor-specific ONFI table + */ +static void nand_onfi_detect_micron(struct nand_chip *chip, + struct nand_onfi_params *p) +{ + struct nand_onfi_vendor_micron *micron = (void *)p->vendor; + + if (le16_to_cpu(p->vendor_revision) < 1) + return; + + chip->read_retries = micron->read_retry_options; + chip->setup_read_retry = nand_setup_read_retry_micron; +} + /* * Check if the NAND chip is ONFI compliant, returns 1 if it is, 0 otherwise. */ @@ -2599,13 +3312,14 @@ static int nand_flash_detect_onfi(struct mtd_info *mtd, struct nand_chip *chip, ((uint8_t *)p)[j] = chip->read_byte(mtd); if (onfi_crc16(ONFI_CRC_BASE, (uint8_t *)p, 254) == le16_to_cpu(p->crc)) { - pr_info("ONFI param page %d valid\n", i); break; } } - if (i == 3) + if (i == 3) { + pr_err("Could not find valid ONFI parameter page; aborting\n"); return 0; + } /* Check version */ val = le16_to_cpu(p->revision); @@ -2619,11 +3333,9 @@ static int nand_flash_detect_onfi(struct mtd_info *mtd, struct nand_chip *chip, chip->onfi_version = 20; else if (val & (1 << 1)) chip->onfi_version = 10; - else - chip->onfi_version = 0; if (!chip->onfi_version) { - pr_info("%s: unsupported ONFI version: %d\n", __func__, val); + pr_info("unsupported ONFI version: %d\n", val); return 0; } @@ -2631,21 +3343,58 @@ static int nand_flash_detect_onfi(struct mtd_info *mtd, struct nand_chip *chip, sanitize_string(p->model, sizeof(p->model)); if (!mtd->name) mtd->name = p->model; + mtd->writesize = le32_to_cpu(p->byte_per_page); - mtd->erasesize = le32_to_cpu(p->pages_per_block) * mtd->writesize; + + /* + * pages_per_block and blocks_per_lun may not be a power-of-2 size + * (don't ask me who thought of this...). MTD assumes that these + * dimensions will be power-of-2, so just truncate the remaining area. + */ + mtd->erasesize = 1 << (fls(le32_to_cpu(p->pages_per_block)) - 1); + mtd->erasesize *= mtd->writesize; + mtd->oobsize = le16_to_cpu(p->spare_bytes_per_page); - chip->chipsize = le32_to_cpu(p->blocks_per_lun); + + /* See erasesize comment */ + chip->chipsize = 1 << (fls(le32_to_cpu(p->blocks_per_lun)) - 1); chip->chipsize *= (uint64_t)mtd->erasesize * p->lun_count; - *busw = 0; - if (le16_to_cpu(p->features) & 1) + chip->bits_per_cell = p->bits_per_cell; + + if (onfi_feature(chip) & ONFI_FEATURE_16_BIT_BUS) *busw = NAND_BUSWIDTH_16; + else + *busw = 0; + + if (p->ecc_bits != 0xff) { + chip->ecc_strength_ds = p->ecc_bits; + chip->ecc_step_ds = 512; + } else if (chip->onfi_version >= 21 && + (onfi_feature(chip) & ONFI_FEATURE_EXT_PARAM_PAGE)) { + + /* + * The nand_flash_detect_ext_param_page() uses the + * Change Read Column command which maybe not supported + * by the chip->cmdfunc. So try to update the chip->cmdfunc + * now. We do not replace user supplied command function. + */ + if (mtd->writesize > 512 && chip->cmdfunc == nand_command) + chip->cmdfunc = nand_command_lp; + + /* The Extended Parameter Page is supported since ONFI 2.1. */ + if (nand_flash_detect_ext_param_page(mtd, chip, p)) + pr_warn("Failed to detect ONFI extended param page\n"); + } else { + pr_warn("Could not retrieve ONFI ECC requirements\n"); + } + + if (p->jedec_id == NAND_MFR_MICRON) + nand_onfi_detect_micron(chip, p); - pr_info("ONFI flash detected\n"); return 1; } #else -static inline int nand_flash_detect_onfi(struct mtd_info *mtd, - struct nand_chip *chip, +static int nand_flash_detect_onfi(struct mtd_info *mtd, struct nand_chip *chip, int *busw) { return 0; @@ -2653,6 +3402,87 @@ static inline int nand_flash_detect_onfi(struct mtd_info *mtd, #endif /* + * Check if the NAND chip is JEDEC compliant, returns 1 if it is, 0 otherwise. + */ +static int nand_flash_detect_jedec(struct mtd_info *mtd, struct nand_chip *chip, + int *busw) +{ + struct nand_jedec_params *p = &chip->jedec_params; + struct jedec_ecc_info *ecc; + int val; + int i, j; + + /* Try JEDEC for unknown chip or LP */ + chip->cmdfunc(mtd, NAND_CMD_READID, 0x40, -1); + if (chip->read_byte(mtd) != 'J' || chip->read_byte(mtd) != 'E' || + chip->read_byte(mtd) != 'D' || chip->read_byte(mtd) != 'E' || + chip->read_byte(mtd) != 'C') + return 0; + + chip->cmdfunc(mtd, NAND_CMD_PARAM, 0x40, -1); + for (i = 0; i < 3; i++) { + for (j = 0; j < sizeof(*p); j++) + ((uint8_t *)p)[j] = chip->read_byte(mtd); + + if (onfi_crc16(ONFI_CRC_BASE, (uint8_t *)p, 510) == + le16_to_cpu(p->crc)) + break; + } + + if (i == 3) { + pr_err("Could not find valid JEDEC parameter page; aborting\n"); + return 0; + } + + /* Check version */ + val = le16_to_cpu(p->revision); + if (val & (1 << 2)) + chip->jedec_version = 10; + else if (val & (1 << 1)) + chip->jedec_version = 1; /* vendor specific version */ + + if (!chip->jedec_version) { + pr_info("unsupported JEDEC version: %d\n", val); + return 0; + } + + sanitize_string(p->manufacturer, sizeof(p->manufacturer)); + sanitize_string(p->model, sizeof(p->model)); + if (!mtd->name) + mtd->name = p->model; + + mtd->writesize = le32_to_cpu(p->byte_per_page); + + /* Please reference to the comment for nand_flash_detect_onfi. */ + mtd->erasesize = 1 << (fls(le32_to_cpu(p->pages_per_block)) - 1); + mtd->erasesize *= mtd->writesize; + + mtd->oobsize = le16_to_cpu(p->spare_bytes_per_page); + + /* Please reference to the comment for nand_flash_detect_onfi. */ + chip->chipsize = 1 << (fls(le32_to_cpu(p->blocks_per_lun)) - 1); + chip->chipsize *= (uint64_t)mtd->erasesize * p->lun_count; + chip->bits_per_cell = p->bits_per_cell; + + if (jedec_feature(chip) & JEDEC_FEATURE_16_BIT_BUS) + *busw = NAND_BUSWIDTH_16; + else + *busw = 0; + + /* ECC info */ + ecc = &p->ecc_info[0]; + + if (ecc->codeword_size >= 9) { + chip->ecc_strength_ds = ecc->ecc_bits; + chip->ecc_step_ds = 1 << ecc->codeword_size; + } else { + pr_warn("Invalid codeword size\n"); + } + + return 1; +} + +/* * nand_id_has_period - Check if an ID string has a given wraparound period * @id_data: the ID string * @arrlen: the length of the @id_data array @@ -2660,7 +3490,7 @@ static inline int nand_flash_detect_onfi(struct mtd_info *mtd, * * Check if an ID string is repeated within a given sequence of bytes at * specific repetition interval period (e.g., {0x20,0x01,0x7F,0x20} has a - * period of 2). This is a helper function for nand_id_len(). Returns non-zero + * period of 3). This is a helper function for nand_id_len(). Returns non-zero * if the repetition has a period of @period; otherwise, returns zero. */ static int nand_id_has_period(u8 *id_data, int arrlen, int period) @@ -2711,6 +3541,16 @@ static int nand_id_len(u8 *id_data, int arrlen) return arrlen; } +/* Extract the bits of per cell from the 3rd byte of the extended ID */ +static int nand_get_bits_per_cell(u8 cellinfo) +{ + int bits; + + bits = cellinfo & NAND_CI_CELLTYPE_MSK; + bits >>= NAND_CI_CELLTYPE_SHIFT; + return bits + 1; +} + /* * Many new NAND share similar device ID codes, which represent the size of the * chip. The rest of the parameters must be decoded according to generic or @@ -2721,7 +3561,7 @@ static void nand_decode_ext_id(struct mtd_info *mtd, struct nand_chip *chip, { int extid, id_len; /* The 3rd id byte holds MLC / multichip data */ - chip->cellinfo = id_data[2]; + chip->bits_per_cell = nand_get_bits_per_cell(id_data[2]); /* The 4th id byte is the important one */ extid = id_data[3]; @@ -2737,8 +3577,7 @@ static void nand_decode_ext_id(struct mtd_info *mtd, struct nand_chip *chip, * ID to decide what to do. */ if (id_len == 6 && id_data[0] == NAND_MFR_SAMSUNG && - (chip->cellinfo & NAND_CI_CELLTYPE_MSK) && - id_data[5] != 0x00) { + !nand_is_slc(chip) && id_data[5] != 0x00) { /* Calc pagesize */ mtd->writesize = 2048 << (extid & 0x03); extid >>= 2; @@ -2760,9 +3599,12 @@ static void nand_decode_ext_id(struct mtd_info *mtd, struct nand_chip *chip, mtd->oobsize = 512; break; case 6: - default: /* Other cases are "reserved" (unknown) */ mtd->oobsize = 640; break; + case 7: + default: /* Other cases are "reserved" (unknown) */ + mtd->oobsize = 1024; + break; } extid >>= 2; /* Calc blocksize */ @@ -2770,7 +3612,7 @@ static void nand_decode_ext_id(struct mtd_info *mtd, struct nand_chip *chip, (((extid >> 1) & 0x04) | (extid & 0x03)); *busw = 0; } else if (id_len == 6 && id_data[0] == NAND_MFR_HYNIX && - (chip->cellinfo & NAND_CI_CELLTYPE_MSK)) { + !nand_is_slc(chip)) { unsigned int tmp; /* Calc pagesize */ @@ -2823,16 +3665,32 @@ static void nand_decode_ext_id(struct mtd_info *mtd, struct nand_chip *chip, extid >>= 2; /* Get buswidth information */ *busw = (extid & 0x01) ? NAND_BUSWIDTH_16 : 0; + + /* + * Toshiba 24nm raw SLC (i.e., not BENAND) have 32B OOB per + * 512B page. For Toshiba SLC, we decode the 5th/6th byte as + * follows: + * - ID byte 6, bits[2:0]: 100b -> 43nm, 101b -> 32nm, + * 110b -> 24nm + * - ID byte 5, bit[7]: 1 -> BENAND, 0 -> raw SLC + */ + if (id_len >= 6 && id_data[0] == NAND_MFR_TOSHIBA && + nand_is_slc(chip) && + (id_data[5] & 0x7) == 0x6 /* 24nm */ && + !(id_data[4] & 0x80) /* !BENAND */) { + mtd->oobsize = 32 * mtd->writesize >> 9; + } + } } - /* +/* * Old devices have chip data hardcoded in the device ID table. nand_decode_id * decodes a matching ID table entry and assigns the MTD size parameters for * the chip. */ static void nand_decode_id(struct mtd_info *mtd, struct nand_chip *chip, - const struct nand_flash_dev *type, u8 id_data[8], + struct nand_flash_dev *type, u8 id_data[8], int *busw) { int maf_id = id_data[0]; @@ -2842,6 +3700,9 @@ static void nand_decode_id(struct mtd_info *mtd, struct nand_chip *chip, mtd->oobsize = mtd->writesize / 32; *busw = type->options & NAND_BUSWIDTH_16; + /* All legacy ID NAND are small-page, SLC */ + chip->bits_per_cell = 1; + /* * Check for Spansion/AMD ID + repeating 5th, 6th byte since * some Spansion chips have erasesize that conflicts with size @@ -2856,7 +3717,7 @@ static void nand_decode_id(struct mtd_info *mtd, struct nand_chip *chip, } } - /* +/* * Set the bad block marker/indicator (BBM/BBI) patterns according to some * heuristic patterns using various detected parameters (e.g., manufacturer, * page size, cell-type information). @@ -2878,11 +3739,11 @@ static void nand_decode_bbm_options(struct mtd_info *mtd, * Micron devices with 2KiB pages and on SLC Samsung, Hynix, Toshiba, * AMD/Spansion, and Macronix. All others scan only the first page. */ - if ((chip->cellinfo & NAND_CI_CELLTYPE_MSK) && + if (!nand_is_slc(chip) && (maf_id == NAND_MFR_SAMSUNG || maf_id == NAND_MFR_HYNIX)) chip->bbt_options |= NAND_BBT_SCANLASTPAGE; - else if ((!(chip->cellinfo & NAND_CI_CELLTYPE_MSK) && + else if ((nand_is_slc(chip) && (maf_id == NAND_MFR_SAMSUNG || maf_id == NAND_MFR_HYNIX || maf_id == NAND_MFR_TOSHIBA || @@ -2893,16 +3754,48 @@ static void nand_decode_bbm_options(struct mtd_info *mtd, chip->bbt_options |= NAND_BBT_SCAN2NDPAGE; } +static inline bool is_full_id_nand(struct nand_flash_dev *type) +{ + return type->id_len; +} + +static bool find_full_id_nand(struct mtd_info *mtd, struct nand_chip *chip, + struct nand_flash_dev *type, u8 *id_data, int *busw) +{ +#ifndef __UBOOT__ + if (!strncmp(type->id, id_data, type->id_len)) { +#else + if (!strncmp((char *)type->id, (char *)id_data, type->id_len)) { +#endif + mtd->writesize = type->pagesize; + mtd->erasesize = type->erasesize; + mtd->oobsize = type->oobsize; + + chip->bits_per_cell = nand_get_bits_per_cell(id_data[2]); + chip->chipsize = (uint64_t)type->chipsize << 20; + chip->options |= type->options; + chip->ecc_strength_ds = NAND_ECC_STRENGTH(type); + chip->ecc_step_ds = NAND_ECC_STEP(type); + + *busw = type->options & NAND_BUSWIDTH_16; + + if (!mtd->name) + mtd->name = type->name; + + return true; + } + return false; +} + /* * Get the flash and manufacturer id and lookup if the type is supported. */ -static const struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd, +static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd, struct nand_chip *chip, - int busw, int *maf_id, int *dev_id, - const struct nand_flash_dev *type) + struct nand_flash_dev *type) { - const char *name; + int busw; int i, maf_idx; u8 id_data[8]; @@ -2936,8 +3829,7 @@ static const struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd, id_data[i] = chip->read_byte(mtd); if (id_data[0] != *maf_id || id_data[1] != *dev_id) { - pr_info("%s: second ID read did not match " - "%02x,%02x against %02x,%02x\n", __func__, + pr_info("second ID read did not match %02x,%02x against %02x,%02x\n", *maf_id, *dev_id, id_data[0], id_data[1]); return ERR_PTR(-ENODEV); } @@ -2945,15 +3837,24 @@ static const struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd, if (!type) type = nand_flash_ids; - for (; type->name != NULL; type++) - if (*dev_id == type->id) - break; + for (; type->name != NULL; type++) { + if (is_full_id_nand(type)) { + if (find_full_id_nand(mtd, chip, type, id_data, &busw)) + goto ident_done; + } else if (*dev_id == type->dev_id) { + break; + } + } chip->onfi_version = 0; if (!type->name || !type->pagesize) { /* Check is chip is ONFI compliant */ if (nand_flash_detect_onfi(mtd, chip, &busw)) goto ident_done; + + /* Check if the chip is JEDEC compliant */ + if (nand_flash_detect_jedec(mtd, chip, &busw)) + goto ident_done; } if (!type->name) @@ -2973,7 +3874,7 @@ static const struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd, } else { nand_decode_id(mtd, chip, type, id_data, &busw); } - /* Get chip options, preserve non chip based options */ + /* Get chip options */ chip->options |= type->options; /* @@ -2990,15 +3891,19 @@ ident_done: break; } - /* - * Check, if buswidth is correct. Hardware drivers should set - * chip correct! - */ - if (busw != (chip->options & NAND_BUSWIDTH_16)) { - pr_info("NAND device: Manufacturer ID:" - " 0x%02x, Chip ID: 0x%02x (%s %s)\n", *maf_id, - *dev_id, nand_manuf_ids[maf_idx].name, mtd->name); - pr_warn("NAND bus width %d instead %d bit\n", + if (chip->options & NAND_BUSWIDTH_AUTO) { + WARN_ON(chip->options & NAND_BUSWIDTH_16); + chip->options |= busw; + nand_set_defaults(chip, busw); + } else if (busw != (chip->options & NAND_BUSWIDTH_16)) { + /* + * Check, if buswidth is correct. Hardware drivers should set + * chip correct! + */ + pr_info("device found, Manufacturer ID: 0x%02x, Chip ID: 0x%02x\n", + *maf_id, *dev_id); + pr_info("%s %s\n", nand_manuf_ids[maf_idx].name, mtd->name); + pr_warn("bus width %d instead %d bit\n", (chip->options & NAND_BUSWIDTH_16) ? 16 : 8, busw ? 16 : 8); return ERR_PTR(-EINVAL); @@ -3021,28 +3926,40 @@ ident_done: } chip->badblockbits = 8; - - /* Check for AND chips with 4 page planes */ - if (chip->options & NAND_4PAGE_ARRAY) - chip->erase_cmd = multi_erase_cmd; - else - chip->erase_cmd = single_erase_cmd; + chip->erase_cmd = single_erase_cmd; /* Do not replace user supplied command function! */ if (mtd->writesize > 512 && chip->cmdfunc == nand_command) chip->cmdfunc = nand_command_lp; - name = type->name; + pr_info("device found, Manufacturer ID: 0x%02x, Chip ID: 0x%02x\n", + *maf_id, *dev_id); + #ifdef CONFIG_SYS_NAND_ONFI_DETECTION if (chip->onfi_version) - name = chip->onfi_params.model; + pr_info("%s %s\n", nand_manuf_ids[maf_idx].name, + chip->onfi_params.model); + else if (chip->jedec_version) + pr_info("%s %s\n", nand_manuf_ids[maf_idx].name, + chip->jedec_params.model); + else + pr_info("%s %s\n", nand_manuf_ids[maf_idx].name, + type->name); +#else + if (chip->jedec_version) + pr_info("%s %s\n", nand_manuf_ids[maf_idx].name, + chip->jedec_params.model); + else + pr_info("%s %s\n", nand_manuf_ids[maf_idx].name, + type->name); + + pr_info("%s %s\n", nand_manuf_ids[maf_idx].name, + type->name); #endif - pr_info("NAND device: Manufacturer ID: 0x%02x, Chip ID: 0x%02x (%s %s)," - " page size: %d, OOB size: %d\n", - *maf_id, *dev_id, nand_manuf_ids[maf_idx].name, - name, - mtd->writesize, mtd->oobsize); + pr_info("%dMiB, %s, page size: %d, OOB size: %d\n", + (int)(chip->chipsize >> 20), nand_is_slc(chip) ? "SLC" : "MLC", + mtd->writesize, mtd->oobsize); return type; } @@ -3058,29 +3975,28 @@ ident_done: * The mtd->owner field must be set to the module of the caller. */ int nand_scan_ident(struct mtd_info *mtd, int maxchips, - const struct nand_flash_dev *table) + struct nand_flash_dev *table) { - int i, busw, nand_maf_id, nand_dev_id; + int i, nand_maf_id, nand_dev_id; struct nand_chip *chip = mtd->priv; - const struct nand_flash_dev *type; + struct nand_flash_dev *type; - /* Get buswidth to select the correct functions */ - busw = chip->options & NAND_BUSWIDTH_16; /* Set the default functions */ - nand_set_defaults(chip, busw); + nand_set_defaults(chip, chip->options & NAND_BUSWIDTH_16); /* Read the flash type */ - type = nand_get_flash_type(mtd, chip, busw, - &nand_maf_id, &nand_dev_id, table); + type = nand_get_flash_type(mtd, chip, &nand_maf_id, + &nand_dev_id, table); if (IS_ERR(type)) { -#ifndef CONFIG_SYS_NAND_QUIET_TEST - pr_warn("No NAND device found\n"); -#endif + if (!(chip->options & NAND_SCAN_SILENT_NODEV)) + pr_warn("No NAND device found\n"); chip->select_chip(mtd, -1); return PTR_ERR(type); } + chip->select_chip(mtd, -1); + /* Check for a chip array */ for (i = 1; i < maxchips; i++) { chip->select_chip(mtd, i); @@ -3090,12 +4006,16 @@ int nand_scan_ident(struct mtd_info *mtd, int maxchips, chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1); /* Read manufacturer and device IDs */ if (nand_maf_id != chip->read_byte(mtd) || - nand_dev_id != chip->read_byte(mtd)) + nand_dev_id != chip->read_byte(mtd)) { + chip->select_chip(mtd, -1); break; + } + chip->select_chip(mtd, -1); } + #ifdef DEBUG if (i > 1) - pr_info("%d NAND chips detected\n", i); + pr_info("%d chips detected\n", i); #endif /* Store the number of chips and calc total size for mtd */ @@ -3104,6 +4024,7 @@ int nand_scan_ident(struct mtd_info *mtd, int maxchips, return 0; } +EXPORT_SYMBOL(nand_scan_ident); /** @@ -3118,16 +4039,31 @@ int nand_scan_tail(struct mtd_info *mtd) { int i; struct nand_chip *chip = mtd->priv; + struct nand_ecc_ctrl *ecc = &chip->ecc; + struct nand_buffers *nbuf; /* New bad blocks should be marked in OOB, flash-based BBT, or both */ BUG_ON((chip->bbt_options & NAND_BBT_NO_OOB_BBM) && !(chip->bbt_options & NAND_BBT_USE_FLASH)); - if (!(chip->options & NAND_OWN_BUFFERS)) - chip->buffers = memalign(ARCH_DMA_MINALIGN, - sizeof(*chip->buffers)); - if (!chip->buffers) - return -ENOMEM; + if (!(chip->options & NAND_OWN_BUFFERS)) { +#ifndef __UBOOT__ + nbuf = kzalloc(sizeof(*nbuf) + mtd->writesize + + mtd->oobsize * 3, GFP_KERNEL); + if (!nbuf) + return -ENOMEM; + nbuf->ecccalc = (uint8_t *)(nbuf + 1); + nbuf->ecccode = nbuf->ecccalc + mtd->oobsize; + nbuf->databuf = nbuf->ecccode + mtd->oobsize; +#else + nbuf = kzalloc(sizeof(struct nand_buffers), GFP_KERNEL); +#endif + + chip->buffers = nbuf; + } else { + if (!chip->buffers) + return -ENOMEM; + } /* Set the internal oob buffer location, just after the page data */ chip->oob_poi = chip->buffers->databuf + mtd->writesize; @@ -3135,94 +4071,91 @@ int nand_scan_tail(struct mtd_info *mtd) /* * If no default placement scheme is given, select an appropriate one. */ - if (!chip->ecc.layout && (chip->ecc.mode != NAND_ECC_SOFT_BCH)) { + if (!ecc->layout && (ecc->mode != NAND_ECC_SOFT_BCH)) { switch (mtd->oobsize) { case 8: - chip->ecc.layout = &nand_oob_8; + ecc->layout = &nand_oob_8; break; case 16: - chip->ecc.layout = &nand_oob_16; + ecc->layout = &nand_oob_16; break; case 64: - chip->ecc.layout = &nand_oob_64; + ecc->layout = &nand_oob_64; break; case 128: - chip->ecc.layout = &nand_oob_128; + ecc->layout = &nand_oob_128; break; default: pr_warn("No oob scheme defined for oobsize %d\n", mtd->oobsize); + BUG(); } } if (!chip->write_page) chip->write_page = nand_write_page; - /* set for ONFI nand */ - if (!chip->onfi_set_features) - chip->onfi_set_features = nand_onfi_set_features; - if (!chip->onfi_get_features) - chip->onfi_get_features = nand_onfi_get_features; - /* * Check ECC mode, default to software if 3byte/512byte hardware ECC is * selected and we have 256 byte pagesize fallback to software ECC */ - switch (chip->ecc.mode) { + switch (ecc->mode) { case NAND_ECC_HW_OOB_FIRST: /* Similar to NAND_ECC_HW, but a separate read_page handle */ - if (!chip->ecc.calculate || !chip->ecc.correct || - !chip->ecc.hwctl) { + if (!ecc->calculate || !ecc->correct || !ecc->hwctl) { pr_warn("No ECC functions supplied; " "hardware ECC not possible\n"); BUG(); } - if (!chip->ecc.read_page) - chip->ecc.read_page = nand_read_page_hwecc_oob_first; + if (!ecc->read_page) + ecc->read_page = nand_read_page_hwecc_oob_first; case NAND_ECC_HW: /* Use standard hwecc read page function? */ - if (!chip->ecc.read_page) - chip->ecc.read_page = nand_read_page_hwecc; - if (!chip->ecc.write_page) - chip->ecc.write_page = nand_write_page_hwecc; - if (!chip->ecc.read_page_raw) - chip->ecc.read_page_raw = nand_read_page_raw; - if (!chip->ecc.write_page_raw) - chip->ecc.write_page_raw = nand_write_page_raw; - if (!chip->ecc.read_oob) - chip->ecc.read_oob = nand_read_oob_std; - if (!chip->ecc.write_oob) - chip->ecc.write_oob = nand_write_oob_std; + if (!ecc->read_page) + ecc->read_page = nand_read_page_hwecc; + if (!ecc->write_page) + ecc->write_page = nand_write_page_hwecc; + if (!ecc->read_page_raw) + ecc->read_page_raw = nand_read_page_raw; + if (!ecc->write_page_raw) + ecc->write_page_raw = nand_write_page_raw; + if (!ecc->read_oob) + ecc->read_oob = nand_read_oob_std; + if (!ecc->write_oob) + ecc->write_oob = nand_write_oob_std; + if (!ecc->read_subpage) + ecc->read_subpage = nand_read_subpage; + if (!ecc->write_subpage) + ecc->write_subpage = nand_write_subpage_hwecc; case NAND_ECC_HW_SYNDROME: - if ((!chip->ecc.calculate || !chip->ecc.correct || - !chip->ecc.hwctl) && - (!chip->ecc.read_page || - chip->ecc.read_page == nand_read_page_hwecc || - !chip->ecc.write_page || - chip->ecc.write_page == nand_write_page_hwecc)) { + if ((!ecc->calculate || !ecc->correct || !ecc->hwctl) && + (!ecc->read_page || + ecc->read_page == nand_read_page_hwecc || + !ecc->write_page || + ecc->write_page == nand_write_page_hwecc)) { pr_warn("No ECC functions supplied; " "hardware ECC not possible\n"); BUG(); } /* Use standard syndrome read/write page function? */ - if (!chip->ecc.read_page) - chip->ecc.read_page = nand_read_page_syndrome; - if (!chip->ecc.write_page) - chip->ecc.write_page = nand_write_page_syndrome; - if (!chip->ecc.read_page_raw) - chip->ecc.read_page_raw = nand_read_page_raw_syndrome; - if (!chip->ecc.write_page_raw) - chip->ecc.write_page_raw = nand_write_page_raw_syndrome; - if (!chip->ecc.read_oob) - chip->ecc.read_oob = nand_read_oob_syndrome; - if (!chip->ecc.write_oob) - chip->ecc.write_oob = nand_write_oob_syndrome; - - if (mtd->writesize >= chip->ecc.size) { - if (!chip->ecc.strength) { + if (!ecc->read_page) + ecc->read_page = nand_read_page_syndrome; + if (!ecc->write_page) + ecc->write_page = nand_write_page_syndrome; + if (!ecc->read_page_raw) + ecc->read_page_raw = nand_read_page_raw_syndrome; + if (!ecc->write_page_raw) + ecc->write_page_raw = nand_write_page_raw_syndrome; + if (!ecc->read_oob) + ecc->read_oob = nand_read_oob_syndrome; + if (!ecc->write_oob) + ecc->write_oob = nand_write_oob_syndrome; + + if (mtd->writesize >= ecc->size) { + if (!ecc->strength) { pr_warn("Driver must set ecc.strength when using hardware ECC\n"); BUG(); } @@ -3230,109 +4163,107 @@ int nand_scan_tail(struct mtd_info *mtd) } pr_warn("%d byte HW ECC not possible on " "%d byte page size, fallback to SW ECC\n", - chip->ecc.size, mtd->writesize); - chip->ecc.mode = NAND_ECC_SOFT; + ecc->size, mtd->writesize); + ecc->mode = NAND_ECC_SOFT; case NAND_ECC_SOFT: - chip->ecc.calculate = nand_calculate_ecc; - chip->ecc.correct = nand_correct_data; - chip->ecc.read_page = nand_read_page_swecc; - chip->ecc.read_subpage = nand_read_subpage; - chip->ecc.write_page = nand_write_page_swecc; - chip->ecc.read_page_raw = nand_read_page_raw; - chip->ecc.write_page_raw = nand_write_page_raw; - chip->ecc.read_oob = nand_read_oob_std; - chip->ecc.write_oob = nand_write_oob_std; - if (!chip->ecc.size) - chip->ecc.size = 256; - chip->ecc.bytes = 3; - chip->ecc.strength = 1; + ecc->calculate = nand_calculate_ecc; + ecc->correct = nand_correct_data; + ecc->read_page = nand_read_page_swecc; + ecc->read_subpage = nand_read_subpage; + ecc->write_page = nand_write_page_swecc; + ecc->read_page_raw = nand_read_page_raw; + ecc->write_page_raw = nand_write_page_raw; + ecc->read_oob = nand_read_oob_std; + ecc->write_oob = nand_write_oob_std; + if (!ecc->size) + ecc->size = 256; + ecc->bytes = 3; + ecc->strength = 1; break; case NAND_ECC_SOFT_BCH: if (!mtd_nand_has_bch()) { - pr_warn("CONFIG_MTD_ECC_BCH not enabled\n"); - return -EINVAL; + pr_warn("CONFIG_MTD_NAND_ECC_BCH not enabled\n"); + BUG(); } - chip->ecc.calculate = nand_bch_calculate_ecc; - chip->ecc.correct = nand_bch_correct_data; - chip->ecc.read_page = nand_read_page_swecc; - chip->ecc.read_subpage = nand_read_subpage; - chip->ecc.write_page = nand_write_page_swecc; - chip->ecc.read_page_raw = nand_read_page_raw; - chip->ecc.write_page_raw = nand_write_page_raw; - chip->ecc.read_oob = nand_read_oob_std; - chip->ecc.write_oob = nand_write_oob_std; + ecc->calculate = nand_bch_calculate_ecc; + ecc->correct = nand_bch_correct_data; + ecc->read_page = nand_read_page_swecc; + ecc->read_subpage = nand_read_subpage; + ecc->write_page = nand_write_page_swecc; + ecc->read_page_raw = nand_read_page_raw; + ecc->write_page_raw = nand_write_page_raw; + ecc->read_oob = nand_read_oob_std; + ecc->write_oob = nand_write_oob_std; /* * Board driver should supply ecc.size and ecc.bytes values to * select how many bits are correctable; see nand_bch_init() * for details. Otherwise, default to 4 bits for large page * devices. */ - if (!chip->ecc.size && (mtd->oobsize >= 64)) { - chip->ecc.size = 512; - chip->ecc.bytes = 7; + if (!ecc->size && (mtd->oobsize >= 64)) { + ecc->size = 512; + ecc->bytes = 7; } - chip->ecc.priv = nand_bch_init(mtd, - chip->ecc.size, - chip->ecc.bytes, - &chip->ecc.layout); - if (!chip->ecc.priv) + ecc->priv = nand_bch_init(mtd, ecc->size, ecc->bytes, + &ecc->layout); + if (!ecc->priv) { pr_warn("BCH ECC initialization failed!\n"); - chip->ecc.strength = - chip->ecc.bytes * 8 / fls(8 * chip->ecc.size); + BUG(); + } + ecc->strength = ecc->bytes * 8 / fls(8 * ecc->size); break; case NAND_ECC_NONE: pr_warn("NAND_ECC_NONE selected by board driver. " - "This is not recommended !!\n"); - chip->ecc.read_page = nand_read_page_raw; - chip->ecc.write_page = nand_write_page_raw; - chip->ecc.read_oob = nand_read_oob_std; - chip->ecc.read_page_raw = nand_read_page_raw; - chip->ecc.write_page_raw = nand_write_page_raw; - chip->ecc.write_oob = nand_write_oob_std; - chip->ecc.size = mtd->writesize; - chip->ecc.bytes = 0; + "This is not recommended!\n"); + ecc->read_page = nand_read_page_raw; + ecc->write_page = nand_write_page_raw; + ecc->read_oob = nand_read_oob_std; + ecc->read_page_raw = nand_read_page_raw; + ecc->write_page_raw = nand_write_page_raw; + ecc->write_oob = nand_write_oob_std; + ecc->size = mtd->writesize; + ecc->bytes = 0; + ecc->strength = 0; break; default: - pr_warn("Invalid NAND_ECC_MODE %d\n", chip->ecc.mode); + pr_warn("Invalid NAND_ECC_MODE %d\n", ecc->mode); BUG(); } /* For many systems, the standard OOB write also works for raw */ - if (!chip->ecc.read_oob_raw) - chip->ecc.read_oob_raw = chip->ecc.read_oob; - if (!chip->ecc.write_oob_raw) - chip->ecc.write_oob_raw = chip->ecc.write_oob; + if (!ecc->read_oob_raw) + ecc->read_oob_raw = ecc->read_oob; + if (!ecc->write_oob_raw) + ecc->write_oob_raw = ecc->write_oob; /* * The number of bytes available for a client to place data into * the out of band area. */ - chip->ecc.layout->oobavail = 0; - for (i = 0; chip->ecc.layout->oobfree[i].length - && i < ARRAY_SIZE(chip->ecc.layout->oobfree); i++) - chip->ecc.layout->oobavail += - chip->ecc.layout->oobfree[i].length; - mtd->oobavail = chip->ecc.layout->oobavail; + ecc->layout->oobavail = 0; + for (i = 0; ecc->layout->oobfree[i].length + && i < ARRAY_SIZE(ecc->layout->oobfree); i++) + ecc->layout->oobavail += ecc->layout->oobfree[i].length; + mtd->oobavail = ecc->layout->oobavail; /* * Set the number of read / write steps for one page depending on ECC * mode. */ - chip->ecc.steps = mtd->writesize / chip->ecc.size; - if (chip->ecc.steps * chip->ecc.size != mtd->writesize) { + ecc->steps = mtd->writesize / ecc->size; + if (ecc->steps * ecc->size != mtd->writesize) { pr_warn("Invalid ECC parameters\n"); BUG(); } - chip->ecc.total = chip->ecc.steps * chip->ecc.bytes; + ecc->total = ecc->steps * ecc->bytes; /* Allow subpage writes up to ecc.steps. Not possible for MLC flash */ - if (!(chip->options & NAND_NO_SUBPAGE_WRITE) && - !(chip->cellinfo & NAND_CI_CELLTYPE_MSK)) { - switch (chip->ecc.steps) { + if (!(chip->options & NAND_NO_SUBPAGE_WRITE) && nand_is_slc(chip)) { + switch (ecc->steps) { case 2: mtd->subpage_sft = 1; break; @@ -3348,36 +4279,42 @@ int nand_scan_tail(struct mtd_info *mtd) /* Initialize state */ chip->state = FL_READY; - /* De-select the device */ - chip->select_chip(mtd, -1); - /* Invalidate the pagebuffer reference */ chip->pagebuf = -1; /* Large page NAND with SOFT_ECC should support subpage reads */ - if ((chip->ecc.mode == NAND_ECC_SOFT) && (chip->page_shift > 9)) + if ((ecc->mode == NAND_ECC_SOFT) && (chip->page_shift > 9)) chip->options |= NAND_SUBPAGE_READ; /* Fill in remaining MTD driver data */ - mtd->type = MTD_NANDFLASH; + mtd->type = nand_is_slc(chip) ? MTD_NANDFLASH : MTD_MLCNANDFLASH; mtd->flags = (chip->options & NAND_ROM) ? MTD_CAP_ROM : MTD_CAP_NANDFLASH; mtd->_erase = nand_erase; +#ifndef __UBOOT__ mtd->_point = NULL; mtd->_unpoint = NULL; +#endif mtd->_read = nand_read; mtd->_write = nand_write; + mtd->_panic_write = panic_nand_write; mtd->_read_oob = nand_read_oob; mtd->_write_oob = nand_write_oob; mtd->_sync = nand_sync; mtd->_lock = NULL; mtd->_unlock = NULL; +#ifndef __UBOOT__ + mtd->_suspend = nand_suspend; + mtd->_resume = nand_resume; +#endif mtd->_block_isbad = nand_block_isbad; mtd->_block_markbad = nand_block_markbad; + mtd->writebufsize = mtd->writesize; /* propagate ecc info to mtd_info */ - mtd->ecclayout = chip->ecc.layout; - mtd->ecc_strength = chip->ecc.strength; + mtd->ecclayout = ecc->layout; + mtd->ecc_strength = ecc->strength; + mtd->ecc_step_size = ecc->size; /* * Initialize bitflip_threshold to its default prior scan_bbt() call. * scan_bbt() might invoke mtd_read(), thus bitflip_threshold must be @@ -3388,10 +4325,24 @@ int nand_scan_tail(struct mtd_info *mtd) /* Check, if we should skip the bad block table scan */ if (chip->options & NAND_SKIP_BBTSCAN) - chip->options |= NAND_BBT_SCANNED; + return 0; - return 0; + /* Build bad block table */ + return chip->scan_bbt(mtd); } +EXPORT_SYMBOL(nand_scan_tail); + +/* + * is_module_text_address() isn't exported, and it's mostly a pointless + * test if this is a module _anyway_ -- they'd have to try _really_ hard + * to call us from in-kernel code if the core NAND support is modular. + */ +#ifdef MODULE +#define caller_is_module() (1) +#else +#define caller_is_module() \ + is_module_text_address((unsigned long)__builtin_return_address(0)) +#endif /** * nand_scan - [NAND Interface] Scan for the NAND device @@ -3407,12 +4358,20 @@ int nand_scan(struct mtd_info *mtd, int maxchips) { int ret; + /* Many callers got this wrong, so check for it for a while... */ + if (!mtd->owner && caller_is_module()) { + pr_crit("%s called with NULL mtd->owner!\n", __func__); + BUG(); + } + ret = nand_scan_ident(mtd, maxchips, NULL); if (!ret) ret = nand_scan_tail(mtd); return ret; } +EXPORT_SYMBOL(nand_scan); +#ifndef __UBOOT__ /** * nand_release - [NAND Interface] Free resources held by the NAND device * @mtd: MTD device structure @@ -3424,10 +4383,7 @@ void nand_release(struct mtd_info *mtd) if (chip->ecc.mode == NAND_ECC_SOFT_BCH) nand_bch_free((struct nand_bch_control *)chip->ecc.priv); -#ifdef CONFIG_MTD_PARTITIONS - /* Deregister partitions */ - del_mtd_partitions(mtd); -#endif + mtd_device_unregister(mtd); /* Free bad block table memory */ kfree(chip->bbt); @@ -3439,3 +4395,24 @@ void nand_release(struct mtd_info *mtd) & NAND_BBT_DYNAMICSTRUCT) kfree(chip->badblock_pattern); } +EXPORT_SYMBOL_GPL(nand_release); + +static int __init nand_base_init(void) +{ + led_trigger_register_simple("nand-disk", &nand_led_trigger); + return 0; +} + +static void __exit nand_base_exit(void) +{ + led_trigger_unregister_simple(nand_led_trigger); +} +#endif + +module_init(nand_base_init); +module_exit(nand_base_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Steven J. Hill <sjhill@realitydiluted.com>"); +MODULE_AUTHOR("Thomas Gleixner <tglx@linutronix.de>"); +MODULE_DESCRIPTION("Generic NAND flash driver code"); diff --git a/drivers/mtd/nand/nand_bbt.c b/drivers/mtd/nand/nand_bbt.c index 8ef5845..c8f28c7 100644 --- a/drivers/mtd/nand/nand_bbt.c +++ b/drivers/mtd/nand/nand_bbt.c @@ -59,17 +59,55 @@ * */ -#include <common.h> -#include <malloc.h> -#include <linux/compat.h> +#define __UBOOT__ +#ifndef __UBOOT__ +#include <linux/slab.h> +#include <linux/types.h> #include <linux/mtd/mtd.h> #include <linux/mtd/bbm.h> #include <linux/mtd/nand.h> #include <linux/mtd/nand_ecc.h> #include <linux/bitops.h> +#include <linux/delay.h> +#include <linux/vmalloc.h> +#include <linux/export.h> #include <linux/string.h> +#else +#include <common.h> +#include <malloc.h> +#include <linux/compat.h> + + #include <linux/mtd/mtd.h> + #include <linux/mtd/bbm.h> + #include <linux/mtd/nand.h> + #include <linux/mtd/nand_ecc.h> + #include <linux/bitops.h> + #include <linux/string.h> +#endif + +#define BBT_BLOCK_GOOD 0x00 +#define BBT_BLOCK_WORN 0x01 +#define BBT_BLOCK_RESERVED 0x02 +#define BBT_BLOCK_FACTORY_BAD 0x03 -#include <asm/errno.h> +#define BBT_ENTRY_MASK 0x03 +#define BBT_ENTRY_SHIFT 2 + +static int nand_update_bbt(struct mtd_info *mtd, loff_t offs); + +static inline uint8_t bbt_get_entry(struct nand_chip *chip, int block) +{ + uint8_t entry = chip->bbt[block >> BBT_ENTRY_SHIFT]; + entry >>= (block & BBT_ENTRY_MASK) * 2; + return entry & BBT_ENTRY_MASK; +} + +static inline void bbt_mark_entry(struct nand_chip *chip, int block, + uint8_t mark) +{ + uint8_t msk = (mark & BBT_ENTRY_MASK) << ((block & BBT_ENTRY_MASK) * 2); + chip->bbt[block >> BBT_ENTRY_SHIFT] |= msk; +} static int check_pattern_no_oob(uint8_t *buf, struct nand_bbt_descr *td) { @@ -86,33 +124,17 @@ static int check_pattern_no_oob(uint8_t *buf, struct nand_bbt_descr *td) * @td: search pattern descriptor * * Check for a pattern at the given place. Used to search bad block tables and - * good / bad block identifiers. If the SCAN_EMPTY option is set then check, if - * all bytes except the pattern area contain 0xff. + * good / bad block identifiers. */ static int check_pattern(uint8_t *buf, int len, int paglen, struct nand_bbt_descr *td) { - int end = 0; - uint8_t *p = buf; - if (td->options & NAND_BBT_NO_OOB) return check_pattern_no_oob(buf, td); - end = paglen + td->offs; - if (td->options & NAND_BBT_SCANEMPTY) - if (memchr_inv(p, 0xff, end)) - return -1; - p += end; - /* Compare the pattern */ - if (memcmp(p, td->pattern, td->len)) + if (memcmp(buf + paglen + td->offs, td->pattern, td->len)) return -1; - if (td->options & NAND_BBT_SCANEMPTY) { - p += td->len; - end += td->len; - if (memchr_inv(p, 0xff, len - end)) - return -1; - } return 0; } @@ -159,7 +181,7 @@ static u32 add_marker_len(struct nand_bbt_descr *td) * @page: the starting page * @num: the number of bbt descriptors to read * @td: the bbt describtion table - * @offs: offset in the memory table + * @offs: block number offset in the table * * Read the bad block table starting from page. */ @@ -209,25 +231,33 @@ static int read_bbt(struct mtd_info *mtd, uint8_t *buf, int page, int num, /* Analyse data */ for (i = 0; i < len; i++) { uint8_t dat = buf[i]; - for (j = 0; j < 8; j += bits, act += 2) { + for (j = 0; j < 8; j += bits, act++) { uint8_t tmp = (dat >> j) & msk; if (tmp == msk) continue; if (reserved_block_code && (tmp == reserved_block_code)) { pr_info("nand_read_bbt: reserved block at 0x%012llx\n", - (loff_t)((offs << 2) + (act >> 1)) << this->bbt_erase_shift); - this->bbt[offs + (act >> 3)] |= 0x2 << (act & 0x06); + (loff_t)(offs + act) << + this->bbt_erase_shift); + bbt_mark_entry(this, offs + act, + BBT_BLOCK_RESERVED); mtd->ecc_stats.bbtblocks++; continue; } - pr_info("nand_read_bbt: Bad block at 0x%012llx\n", - (loff_t)((offs << 2) + (act >> 1)) - << this->bbt_erase_shift); + /* + * Leave it for now, if it's matured we can + * move this message to pr_debug. + */ + pr_info("nand_read_bbt: bad block at 0x%012llx\n", + (loff_t)(offs + act) << + this->bbt_erase_shift); /* Factory marked bad or worn out? */ if (tmp == 0) - this->bbt[offs + (act >> 3)] |= 0x3 << (act & 0x06); + bbt_mark_entry(this, offs + act, + BBT_BLOCK_FACTORY_BAD); else - this->bbt[offs + (act >> 3)] |= 0x1 << (act & 0x06); + bbt_mark_entry(this, offs + act, + BBT_BLOCK_WORN); mtd->ecc_stats.badblocks++; } } @@ -262,7 +292,7 @@ static int read_abs_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_desc td, offs); if (res) return res; - offs += this->chipsize >> (this->bbt_erase_shift + 2); + offs += this->chipsize >> this->bbt_erase_shift; } } else { res = read_bbt(mtd, buf, td->pages[0], @@ -396,25 +426,6 @@ static void read_abs_bbts(struct mtd_info *mtd, uint8_t *buf, } } -/* Scan a given block full */ -static int scan_block_full(struct mtd_info *mtd, struct nand_bbt_descr *bd, - loff_t offs, uint8_t *buf, size_t readlen, - int scanlen, int numpages) -{ - int ret, j; - - ret = scan_read_oob(mtd, buf, offs, readlen); - /* Ignore ECC errors when checking for BBM */ - if (ret && !mtd_is_bitflip_or_eccerr(ret)) - return ret; - - for (j = 0; j < numpages; j++, buf += scanlen) { - if (check_pattern(buf, scanlen, mtd->writesize, bd)) - return 1; - } - return 0; -} - /* Scan a given block partially */ static int scan_block_fast(struct mtd_info *mtd, struct nand_bbt_descr *bd, loff_t offs, uint8_t *buf, int numpages) @@ -461,36 +472,19 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *bd, int chip) { struct nand_chip *this = mtd->priv; - int i, numblocks, numpages, scanlen; + int i, numblocks, numpages; int startblock; loff_t from; - size_t readlen; pr_info("Scanning device for bad blocks\n"); - if (bd->options & NAND_BBT_SCANALLPAGES) - numpages = 1 << (this->bbt_erase_shift - this->page_shift); - else if (bd->options & NAND_BBT_SCAN2NDPAGE) + if (bd->options & NAND_BBT_SCAN2NDPAGE) numpages = 2; else numpages = 1; - if (!(bd->options & NAND_BBT_SCANEMPTY)) { - /* We need only read few bytes from the OOB area */ - scanlen = 0; - readlen = bd->len; - } else { - /* Full page content should be read */ - scanlen = mtd->writesize + mtd->oobsize; - readlen = numpages * mtd->writesize; - } - if (chip == -1) { - /* - * Note that numblocks is 2 * (real numblocks) here, see i+=2 - * below as it makes shifting and masking less painful - */ - numblocks = mtd->size >> (this->bbt_erase_shift - 1); + numblocks = mtd->size >> this->bbt_erase_shift; startblock = 0; from = 0; } else { @@ -499,37 +493,31 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf, chip + 1, this->numchips); return -EINVAL; } - numblocks = this->chipsize >> (this->bbt_erase_shift - 1); + numblocks = this->chipsize >> this->bbt_erase_shift; startblock = chip * numblocks; numblocks += startblock; - from = (loff_t)startblock << (this->bbt_erase_shift - 1); + from = (loff_t)startblock << this->bbt_erase_shift; } if (this->bbt_options & NAND_BBT_SCANLASTPAGE) from += mtd->erasesize - (mtd->writesize * numpages); - for (i = startblock; i < numblocks;) { + for (i = startblock; i < numblocks; i++) { int ret; BUG_ON(bd->options & NAND_BBT_NO_OOB); - if (bd->options & NAND_BBT_SCANALLPAGES) - ret = scan_block_full(mtd, bd, from, buf, readlen, - scanlen, numpages); - else - ret = scan_block_fast(mtd, bd, from, buf, numpages); - + ret = scan_block_fast(mtd, bd, from, buf, numpages); if (ret < 0) return ret; if (ret) { - this->bbt[i >> 3] |= 0x03 << (i & 0x6); + bbt_mark_entry(this, i, BBT_BLOCK_FACTORY_BAD); pr_warn("Bad eraseblock %d at 0x%012llx\n", - i >> 1, (unsigned long long)from); + i, (unsigned long long)from); mtd->ecc_stats.badblocks++; } - i += 2; from += (1 << this->bbt_erase_shift); } return 0; @@ -554,7 +542,11 @@ static int search_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr { struct nand_chip *this = mtd->priv; int i, chips; +#ifndef __UBOOT__ + int bits, startblock, block, dir; +#else int startblock, block, dir; +#endif int scanlen = mtd->writesize + mtd->oobsize; int bbtblocks; int blocktopage = this->bbt_erase_shift - this->page_shift; @@ -578,6 +570,11 @@ static int search_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr bbtblocks = mtd->size >> this->bbt_erase_shift; } +#ifndef __UBOOT__ + /* Number of bits for each erase block in the bbt */ + bits = td->options & NAND_BBT_NRBITS_MSK; +#endif + for (i = 0; i < chips; i++) { /* Reset version information */ td->version[i] = 0; @@ -606,8 +603,8 @@ static int search_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr if (td->pages[i] == -1) pr_warn("Bad block table not found for chip %d\n", i); else - pr_info("Bad block table found at page %d, version 0x%02X\n", td->pages[i], - td->version[i]); + pr_info("Bad block table found at page %d, version " + "0x%02X\n", td->pages[i], td->version[i]); } return 0; } @@ -649,9 +646,9 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf, { struct nand_chip *this = mtd->priv; struct erase_info einfo; - int i, j, res, chip = 0; + int i, res, chip = 0; int bits, startblock, dir, page, offs, numblocks, sft, sftmsk; - int nrchips, bbtoffs, pageoffs, ooboffs; + int nrchips, pageoffs, ooboffs; uint8_t msk[4]; uint8_t rcode = td->reserved_block_code; size_t retlen, len = 0; @@ -707,10 +704,9 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf, for (i = 0; i < td->maxblocks; i++) { int block = startblock + dir * i; /* Check, if the block is bad */ - switch ((this->bbt[block >> 2] >> - (2 * (block & 0x03))) & 0x03) { - case 0x01: - case 0x03: + switch (bbt_get_entry(this, block)) { + case BBT_BLOCK_WORN: + case BBT_BLOCK_FACTORY_BAD: continue; } page = block << @@ -742,8 +738,6 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf, default: return -EINVAL; } - bbtoffs = chip * (numblocks >> 2); - to = ((loff_t)page) << this->page_shift; /* Must we save the block contents? */ @@ -808,16 +802,12 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf, buf[ooboffs + td->veroffs] = td->version[chip]; /* Walk through the memory table */ - for (i = 0; i < numblocks;) { + for (i = 0; i < numblocks; i++) { uint8_t dat; - dat = this->bbt[bbtoffs + (i >> 2)]; - for (j = 0; j < 4; j++, i++) { - int sftcnt = (i << (3 - sft)) & sftmsk; - /* Do not store the reserved bbt blocks! */ - buf[offs + (i >> sft)] &= - ~(msk[dat & 0x03] << sftcnt); - dat >>= 2; - } + int sftcnt = (i << (3 - sft)) & sftmsk; + dat = bbt_get_entry(this, chip * numblocks + i); + /* Do not store the reserved bbt blocks! */ + buf[offs + (i >> sft)] &= ~(msk[dat] << sftcnt); } memset(&einfo, 0, sizeof(einfo)); @@ -859,7 +849,6 @@ static inline int nand_memory_bbt(struct mtd_info *mtd, struct nand_bbt_descr *b { struct nand_chip *this = mtd->priv; - bd->options &= ~NAND_BBT_SCANEMPTY; return create_bbt(mtd, this->buffers->databuf, bd, -1); } @@ -1003,7 +992,7 @@ static void mark_bbt_region(struct mtd_info *mtd, struct nand_bbt_descr *td) { struct nand_chip *this = mtd->priv; int i, j, chips, block, nrblocks, update; - uint8_t oldval, newval; + uint8_t oldval; /* Do we have a bbt per chip? */ if (td->options & NAND_BBT_PERCHIP) { @@ -1020,12 +1009,12 @@ static void mark_bbt_region(struct mtd_info *mtd, struct nand_bbt_descr *td) if (td->pages[i] == -1) continue; block = td->pages[i] >> (this->bbt_erase_shift - this->page_shift); - block <<= 1; - oldval = this->bbt[(block >> 3)]; - newval = oldval | (0x2 << (block & 0x06)); - this->bbt[(block >> 3)] = newval; - if ((oldval != newval) && td->reserved_block_code) - nand_update_bbt(mtd, (loff_t)block << (this->bbt_erase_shift - 1)); + oldval = bbt_get_entry(this, block); + bbt_mark_entry(this, block, BBT_BLOCK_RESERVED); + if ((oldval != BBT_BLOCK_RESERVED) && + td->reserved_block_code) + nand_update_bbt(mtd, (loff_t)block << + this->bbt_erase_shift); continue; } update = 0; @@ -1033,14 +1022,12 @@ static void mark_bbt_region(struct mtd_info *mtd, struct nand_bbt_descr *td) block = ((i + 1) * nrblocks) - td->maxblocks; else block = i * nrblocks; - block <<= 1; for (j = 0; j < td->maxblocks; j++) { - oldval = this->bbt[(block >> 3)]; - newval = oldval | (0x2 << (block & 0x06)); - this->bbt[(block >> 3)] = newval; - if (oldval != newval) + oldval = bbt_get_entry(this, block); + bbt_mark_entry(this, block, BBT_BLOCK_RESERVED); + if (oldval != BBT_BLOCK_RESERVED) update = 1; - block += 2; + block++; } /* * If we want reserved blocks to be recorded to flash, and some @@ -1048,7 +1035,8 @@ static void mark_bbt_region(struct mtd_info *mtd, struct nand_bbt_descr *td) * bbts. This should only happen once. */ if (update && td->reserved_block_code) - nand_update_bbt(mtd, (loff_t)(block - 2) << (this->bbt_erase_shift - 1)); + nand_update_bbt(mtd, (loff_t)(block - 1) << + this->bbt_erase_shift); } } @@ -1174,13 +1162,13 @@ int nand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd) } /** - * nand_update_bbt - [NAND Interface] update bad block table(s) + * nand_update_bbt - update bad block table(s) * @mtd: MTD device structure * @offs: the offset of the newly marked block * * The function updates the bad block table(s). */ -int nand_update_bbt(struct mtd_info *mtd, loff_t offs) +static int nand_update_bbt(struct mtd_info *mtd, loff_t offs) { struct nand_chip *this = mtd->priv; int len, res = 0; @@ -1234,15 +1222,6 @@ int nand_update_bbt(struct mtd_info *mtd, loff_t offs) */ static uint8_t scan_ff_pattern[] = { 0xff, 0xff }; -static uint8_t scan_agand_pattern[] = { 0x1C, 0x71, 0xC7, 0x1C, 0x71, 0xC7 }; - -static struct nand_bbt_descr agand_flashbased = { - .options = NAND_BBT_SCANEMPTY | NAND_BBT_SCANALLPAGES, - .offs = 0x20, - .len = 6, - .pattern = scan_agand_pattern -}; - /* Generic flash bbt descriptors */ static uint8_t bbt_pattern[] = {'B', 'b', 't', '0' }; static uint8_t mirror_pattern[] = {'1', 't', 'b', 'B' }; @@ -1327,22 +1306,6 @@ int nand_default_bbt(struct mtd_info *mtd) { struct nand_chip *this = mtd->priv; - /* - * Default for AG-AND. We must use a flash based bad block table as the - * devices have factory marked _good_ blocks. Erasing those blocks - * leads to loss of the good / bad information, so we _must_ store this - * information in a good / bad table during startup. - */ - if (this->options & NAND_IS_AND) { - /* Use the default pattern descriptors */ - if (!this->bbt_td) { - this->bbt_td = &bbt_main_descr; - this->bbt_md = &bbt_mirror_descr; - } - this->bbt_options |= NAND_BBT_USE_FLASH; - return nand_scan_bbt(mtd, &agand_flashbased); - } - /* Is a flash based bad block table requested? */ if (this->bbt_options & NAND_BBT_USE_FLASH) { /* Use the default pattern descriptors */ @@ -1375,23 +1338,46 @@ int nand_default_bbt(struct mtd_info *mtd) int nand_isbad_bbt(struct mtd_info *mtd, loff_t offs, int allowbbt) { struct nand_chip *this = mtd->priv; - int block; - uint8_t res; + int block, res; - /* Get block number * 2 */ - block = (int)(offs >> (this->bbt_erase_shift - 1)); - res = (this->bbt[block >> 3] >> (block & 0x06)) & 0x03; + block = (int)(offs >> this->bbt_erase_shift); + res = bbt_get_entry(this, block); - MTDDEBUG(MTD_DEBUG_LEVEL2, "nand_isbad_bbt(): bbt info for offs 0x%08x: (block %d) 0x%02x\n", - (unsigned int)offs, block >> 1, res); + pr_debug("nand_isbad_bbt(): bbt info for offs 0x%08x: " + "(block %d) 0x%02x\n", + (unsigned int)offs, block, res); - switch ((int)res) { - case 0x00: + switch (res) { + case BBT_BLOCK_GOOD: return 0; - case 0x01: + case BBT_BLOCK_WORN: return 1; - case 0x02: + case BBT_BLOCK_RESERVED: return allowbbt ? 0 : 1; } return 1; } + +/** + * nand_markbad_bbt - [NAND Interface] Mark a block bad in the BBT + * @mtd: MTD device structure + * @offs: offset of the bad block + */ +int nand_markbad_bbt(struct mtd_info *mtd, loff_t offs) +{ + struct nand_chip *this = mtd->priv; + int block, ret = 0; + + block = (int)(offs >> this->bbt_erase_shift); + + /* Mark bad block in memory */ + bbt_mark_entry(this, block, BBT_BLOCK_WORN); + + /* Update flash-based bad block table */ + if (this->bbt_options & NAND_BBT_USE_FLASH) + ret = nand_update_bbt(mtd, offs); + + return ret; +} + +EXPORT_SYMBOL(nand_scan_bbt); diff --git a/drivers/mtd/nand/nand_ids.c b/drivers/mtd/nand/nand_ids.c index f3f0cb6..54f9f13 100644 --- a/drivers/mtd/nand/nand_ids.c +++ b/drivers/mtd/nand/nand_ids.c @@ -8,165 +8,175 @@ * published by the Free Software Foundation. * */ - +#define __UBOOT__ +#ifndef __UBOOT__ +#include <linux/module.h> +#include <linux/mtd/nand.h> +#else #include <common.h> #include <linux/mtd/nand.h> -/* -* Chip ID list -* -* Name. ID code, pagesize, chipsize in MegaByte, eraseblock size, -* options -* -* Pagesize; 0, 256, 512 -* 0 get this information from the extended chip ID -+ 256 256 Byte page size -* 512 512 Byte page size -*/ -const struct nand_flash_dev nand_flash_ids[] = { - -#ifdef CONFIG_MTD_NAND_MUSEUM_IDS - {"NAND 1MiB 5V 8-bit", 0x6e, 256, 1, 0x1000, 0}, - {"NAND 2MiB 5V 8-bit", 0x64, 256, 2, 0x1000, 0}, - {"NAND 4MiB 5V 8-bit", 0x6b, 512, 4, 0x2000, 0}, - {"NAND 1MiB 3,3V 8-bit", 0xe8, 256, 1, 0x1000, 0}, - {"NAND 1MiB 3,3V 8-bit", 0xec, 256, 1, 0x1000, 0}, - {"NAND 2MiB 3,3V 8-bit", 0xea, 256, 2, 0x1000, 0}, - {"NAND 4MiB 3,3V 8-bit", 0xd5, 512, 4, 0x2000, 0}, - {"NAND 4MiB 3,3V 8-bit", 0xe3, 512, 4, 0x2000, 0}, - {"NAND 4MiB 3,3V 8-bit", 0xe5, 512, 4, 0x2000, 0}, - {"NAND 8MiB 3,3V 8-bit", 0xd6, 512, 8, 0x2000, 0}, - - {"NAND 8MiB 1,8V 8-bit", 0x39, 512, 8, 0x2000, 0}, - {"NAND 8MiB 3,3V 8-bit", 0xe6, 512, 8, 0x2000, 0}, - {"NAND 8MiB 1,8V 16-bit", 0x49, 512, 8, 0x2000, NAND_BUSWIDTH_16}, - {"NAND 8MiB 3,3V 16-bit", 0x59, 512, 8, 0x2000, NAND_BUSWIDTH_16}, #endif +#include <linux/sizes.h> - {"NAND 16MiB 1,8V 8-bit", 0x33, 512, 16, 0x4000, 0}, - {"NAND 16MiB 3,3V 8-bit", 0x73, 512, 16, 0x4000, 0}, - {"NAND 16MiB 1,8V 16-bit", 0x43, 512, 16, 0x4000, NAND_BUSWIDTH_16}, - {"NAND 16MiB 3,3V 16-bit", 0x53, 512, 16, 0x4000, NAND_BUSWIDTH_16}, - - {"NAND 32MiB 1,8V 8-bit", 0x35, 512, 32, 0x4000, 0}, - {"NAND 32MiB 3,3V 8-bit", 0x75, 512, 32, 0x4000, 0}, - {"NAND 32MiB 1,8V 16-bit", 0x45, 512, 32, 0x4000, NAND_BUSWIDTH_16}, - {"NAND 32MiB 3,3V 16-bit", 0x55, 512, 32, 0x4000, NAND_BUSWIDTH_16}, - - {"NAND 64MiB 1,8V 8-bit", 0x36, 512, 64, 0x4000, 0}, - {"NAND 64MiB 3,3V 8-bit", 0x76, 512, 64, 0x4000, 0}, - {"NAND 64MiB 1,8V 16-bit", 0x46, 512, 64, 0x4000, NAND_BUSWIDTH_16}, - {"NAND 64MiB 3,3V 16-bit", 0x56, 512, 64, 0x4000, NAND_BUSWIDTH_16}, +#define LP_OPTIONS NAND_SAMSUNG_LP_OPTIONS +#define LP_OPTIONS16 (LP_OPTIONS | NAND_BUSWIDTH_16) - {"NAND 128MiB 1,8V 8-bit", 0x78, 512, 128, 0x4000, 0}, - {"NAND 128MiB 1,8V 8-bit", 0x39, 512, 128, 0x4000, 0}, - {"NAND 128MiB 3,3V 8-bit", 0x79, 512, 128, 0x4000, 0}, - {"NAND 128MiB 1,8V 16-bit", 0x72, 512, 128, 0x4000, NAND_BUSWIDTH_16}, - {"NAND 128MiB 1,8V 16-bit", 0x49, 512, 128, 0x4000, NAND_BUSWIDTH_16}, - {"NAND 128MiB 3,3V 16-bit", 0x74, 512, 128, 0x4000, NAND_BUSWIDTH_16}, - {"NAND 128MiB 3,3V 16-bit", 0x59, 512, 128, 0x4000, NAND_BUSWIDTH_16}, +#define SP_OPTIONS NAND_NEED_READRDY +#define SP_OPTIONS16 (SP_OPTIONS | NAND_BUSWIDTH_16) - {"NAND 256MiB 3,3V 8-bit", 0x71, 512, 256, 0x4000, 0}, +/* + * The chip ID list: + * name, device ID, page size, chip size in MiB, eraseblock size, options + * + * If page size and eraseblock size are 0, the sizes are taken from the + * extended chip ID. + */ +struct nand_flash_dev nand_flash_ids[] = { +#ifdef CONFIG_MTD_NAND_MUSEUM_IDS + LEGACY_ID_NAND("NAND 1MiB 5V 8-bit", 0x6e, 1, SZ_4K, SP_OPTIONS), + LEGACY_ID_NAND("NAND 2MiB 5V 8-bit", 0x64, 2, SZ_4K, SP_OPTIONS), + LEGACY_ID_NAND("NAND 1MiB 3,3V 8-bit", 0xe8, 1, SZ_4K, SP_OPTIONS), + LEGACY_ID_NAND("NAND 1MiB 3,3V 8-bit", 0xec, 1, SZ_4K, SP_OPTIONS), + LEGACY_ID_NAND("NAND 2MiB 3,3V 8-bit", 0xea, 2, SZ_4K, SP_OPTIONS), + LEGACY_ID_NAND("NAND 4MiB 3,3V 8-bit", 0xd5, 4, SZ_8K, SP_OPTIONS), + + LEGACY_ID_NAND("NAND 8MiB 3,3V 8-bit", 0xe6, 8, SZ_8K, SP_OPTIONS), +#endif + /* + * Some incompatible NAND chips share device ID's and so must be + * listed by full ID. We list them first so that we can easily identify + * the most specific match. + */ + {"TC58NVG2S0F 4G 3.3V 8-bit", + { .id = {0x98, 0xdc, 0x90, 0x26, 0x76, 0x15, 0x01, 0x08} }, + SZ_4K, SZ_512, SZ_256K, 0, 8, 224, NAND_ECC_INFO(4, SZ_512) }, + {"TC58NVG3S0F 8G 3.3V 8-bit", + { .id = {0x98, 0xd3, 0x90, 0x26, 0x76, 0x15, 0x02, 0x08} }, + SZ_4K, SZ_1K, SZ_256K, 0, 8, 232, NAND_ECC_INFO(4, SZ_512) }, + {"TC58NVG5D2 32G 3.3V 8-bit", + { .id = {0x98, 0xd7, 0x94, 0x32, 0x76, 0x56, 0x09, 0x00} }, + SZ_8K, SZ_4K, SZ_1M, 0, 8, 640, NAND_ECC_INFO(40, SZ_1K) }, + {"TC58NVG6D2 64G 3.3V 8-bit", + { .id = {0x98, 0xde, 0x94, 0x82, 0x76, 0x56, 0x04, 0x20} }, + SZ_8K, SZ_8K, SZ_2M, 0, 8, 640, NAND_ECC_INFO(40, SZ_1K) }, + {"SDTNRGAMA 64G 3.3V 8-bit", + { .id = {0x45, 0xde, 0x94, 0x93, 0x76, 0x50} }, + SZ_16K, SZ_8K, SZ_4M, 0, 6, 1280, NAND_ECC_INFO(40, SZ_1K) }, + + LEGACY_ID_NAND("NAND 4MiB 5V 8-bit", 0x6B, 4, SZ_8K, SP_OPTIONS), + LEGACY_ID_NAND("NAND 4MiB 3,3V 8-bit", 0xE3, 4, SZ_8K, SP_OPTIONS), + LEGACY_ID_NAND("NAND 4MiB 3,3V 8-bit", 0xE5, 4, SZ_8K, SP_OPTIONS), + LEGACY_ID_NAND("NAND 8MiB 3,3V 8-bit", 0xD6, 8, SZ_8K, SP_OPTIONS), + LEGACY_ID_NAND("NAND 8MiB 3,3V 8-bit", 0xE6, 8, SZ_8K, SP_OPTIONS), + + LEGACY_ID_NAND("NAND 16MiB 1,8V 8-bit", 0x33, 16, SZ_16K, SP_OPTIONS), + LEGACY_ID_NAND("NAND 16MiB 3,3V 8-bit", 0x73, 16, SZ_16K, SP_OPTIONS), + LEGACY_ID_NAND("NAND 16MiB 1,8V 16-bit", 0x43, 16, SZ_16K, SP_OPTIONS16), + LEGACY_ID_NAND("NAND 16MiB 3,3V 16-bit", 0x53, 16, SZ_16K, SP_OPTIONS16), + + LEGACY_ID_NAND("NAND 32MiB 1,8V 8-bit", 0x35, 32, SZ_16K, SP_OPTIONS), + LEGACY_ID_NAND("NAND 32MiB 3,3V 8-bit", 0x75, 32, SZ_16K, SP_OPTIONS), + LEGACY_ID_NAND("NAND 32MiB 1,8V 16-bit", 0x45, 32, SZ_16K, SP_OPTIONS16), + LEGACY_ID_NAND("NAND 32MiB 3,3V 16-bit", 0x55, 32, SZ_16K, SP_OPTIONS16), + + LEGACY_ID_NAND("NAND 64MiB 1,8V 8-bit", 0x36, 64, SZ_16K, SP_OPTIONS), + LEGACY_ID_NAND("NAND 64MiB 3,3V 8-bit", 0x76, 64, SZ_16K, SP_OPTIONS), + LEGACY_ID_NAND("NAND 64MiB 1,8V 16-bit", 0x46, 64, SZ_16K, SP_OPTIONS16), + LEGACY_ID_NAND("NAND 64MiB 3,3V 16-bit", 0x56, 64, SZ_16K, SP_OPTIONS16), + + LEGACY_ID_NAND("NAND 128MiB 1,8V 8-bit", 0x78, 128, SZ_16K, SP_OPTIONS), + LEGACY_ID_NAND("NAND 128MiB 1,8V 8-bit", 0x39, 128, SZ_16K, SP_OPTIONS), + LEGACY_ID_NAND("NAND 128MiB 3,3V 8-bit", 0x79, 128, SZ_16K, SP_OPTIONS), + LEGACY_ID_NAND("NAND 128MiB 1,8V 16-bit", 0x72, 128, SZ_16K, SP_OPTIONS16), + LEGACY_ID_NAND("NAND 128MiB 1,8V 16-bit", 0x49, 128, SZ_16K, SP_OPTIONS16), + LEGACY_ID_NAND("NAND 128MiB 3,3V 16-bit", 0x74, 128, SZ_16K, SP_OPTIONS16), + LEGACY_ID_NAND("NAND 128MiB 3,3V 16-bit", 0x59, 128, SZ_16K, SP_OPTIONS16), + + LEGACY_ID_NAND("NAND 256MiB 3,3V 8-bit", 0x71, 256, SZ_16K, SP_OPTIONS), /* - * These are the new chips with large page size. The pagesize and the - * erasesize is determined from the extended id bytes + * These are the new chips with large page size. Their page size and + * eraseblock size are determined from the extended ID bytes. */ -#define LP_OPTIONS NAND_SAMSUNG_LP_OPTIONS -#define LP_OPTIONS16 (LP_OPTIONS | NAND_BUSWIDTH_16) /* 512 Megabit */ - {"NAND 64MiB 1,8V 8-bit", 0xA2, 0, 64, 0, LP_OPTIONS}, - {"NAND 64MiB 1,8V 8-bit", 0xA0, 0, 64, 0, LP_OPTIONS}, - {"NAND 64MiB 3,3V 8-bit", 0xF2, 0, 64, 0, LP_OPTIONS}, - {"NAND 64MiB 3,3V 8-bit", 0xD0, 0, 64, 0, LP_OPTIONS}, - {"NAND 64MiB 3,3V 8-bit", 0xF0, 0, 64, 0, LP_OPTIONS}, - {"NAND 64MiB 1,8V 16-bit", 0xB2, 0, 64, 0, LP_OPTIONS16}, - {"NAND 64MiB 1,8V 16-bit", 0xB0, 0, 64, 0, LP_OPTIONS16}, - {"NAND 64MiB 3,3V 16-bit", 0xC2, 0, 64, 0, LP_OPTIONS16}, - {"NAND 64MiB 3,3V 16-bit", 0xC0, 0, 64, 0, LP_OPTIONS16}, + EXTENDED_ID_NAND("NAND 64MiB 1,8V 8-bit", 0xA2, 64, LP_OPTIONS), + EXTENDED_ID_NAND("NAND 64MiB 1,8V 8-bit", 0xA0, 64, LP_OPTIONS), + EXTENDED_ID_NAND("NAND 64MiB 3,3V 8-bit", 0xF2, 64, LP_OPTIONS), + EXTENDED_ID_NAND("NAND 64MiB 3,3V 8-bit", 0xD0, 64, LP_OPTIONS), + EXTENDED_ID_NAND("NAND 64MiB 3,3V 8-bit", 0xF0, 64, LP_OPTIONS), + EXTENDED_ID_NAND("NAND 64MiB 1,8V 16-bit", 0xB2, 64, LP_OPTIONS16), + EXTENDED_ID_NAND("NAND 64MiB 1,8V 16-bit", 0xB0, 64, LP_OPTIONS16), + EXTENDED_ID_NAND("NAND 64MiB 3,3V 16-bit", 0xC2, 64, LP_OPTIONS16), + EXTENDED_ID_NAND("NAND 64MiB 3,3V 16-bit", 0xC0, 64, LP_OPTIONS16), /* 1 Gigabit */ - {"NAND 128MiB 1,8V 8-bit", 0xA1, 0, 128, 0, LP_OPTIONS}, - {"NAND 128MiB 3,3V 8-bit", 0xF1, 0, 128, 0, LP_OPTIONS}, - {"NAND 128MiB 3,3V 8-bit", 0xD1, 0, 128, 0, LP_OPTIONS}, - {"NAND 128MiB 1,8V 16-bit", 0xB1, 0, 128, 0, LP_OPTIONS16}, - {"NAND 128MiB 3,3V 16-bit", 0xC1, 0, 128, 0, LP_OPTIONS16}, - {"NAND 128MiB 1,8V 16-bit", 0xAD, 0, 128, 0, LP_OPTIONS16}, + EXTENDED_ID_NAND("NAND 128MiB 1,8V 8-bit", 0xA1, 128, LP_OPTIONS), + EXTENDED_ID_NAND("NAND 128MiB 3,3V 8-bit", 0xF1, 128, LP_OPTIONS), + EXTENDED_ID_NAND("NAND 128MiB 3,3V 8-bit", 0xD1, 128, LP_OPTIONS), + EXTENDED_ID_NAND("NAND 128MiB 1,8V 16-bit", 0xB1, 128, LP_OPTIONS16), + EXTENDED_ID_NAND("NAND 128MiB 3,3V 16-bit", 0xC1, 128, LP_OPTIONS16), + EXTENDED_ID_NAND("NAND 128MiB 1,8V 16-bit", 0xAD, 128, LP_OPTIONS16), /* 2 Gigabit */ - {"NAND 256MiB 1,8V 8-bit", 0xAA, 0, 256, 0, LP_OPTIONS}, - {"NAND 256MiB 3,3V 8-bit", 0xDA, 0, 256, 0, LP_OPTIONS}, - {"NAND 256MiB 1,8V 16-bit", 0xBA, 0, 256, 0, LP_OPTIONS16}, - {"NAND 256MiB 3,3V 16-bit", 0xCA, 0, 256, 0, LP_OPTIONS16}, + EXTENDED_ID_NAND("NAND 256MiB 1,8V 8-bit", 0xAA, 256, LP_OPTIONS), + EXTENDED_ID_NAND("NAND 256MiB 3,3V 8-bit", 0xDA, 256, LP_OPTIONS), + EXTENDED_ID_NAND("NAND 256MiB 1,8V 16-bit", 0xBA, 256, LP_OPTIONS16), + EXTENDED_ID_NAND("NAND 256MiB 3,3V 16-bit", 0xCA, 256, LP_OPTIONS16), /* 4 Gigabit */ - {"NAND 512MiB 1,8V 8-bit", 0xAC, 0, 512, 0, LP_OPTIONS}, - {"NAND 512MiB 3,3V 8-bit", 0xDC, 0, 512, 0, LP_OPTIONS}, - {"NAND 512MiB 1,8V 16-bit", 0xBC, 0, 512, 0, LP_OPTIONS16}, - {"NAND 512MiB 3,3V 16-bit", 0xCC, 0, 512, 0, LP_OPTIONS16}, + EXTENDED_ID_NAND("NAND 512MiB 1,8V 8-bit", 0xAC, 512, LP_OPTIONS), + EXTENDED_ID_NAND("NAND 512MiB 3,3V 8-bit", 0xDC, 512, LP_OPTIONS), + EXTENDED_ID_NAND("NAND 512MiB 1,8V 16-bit", 0xBC, 512, LP_OPTIONS16), + EXTENDED_ID_NAND("NAND 512MiB 3,3V 16-bit", 0xCC, 512, LP_OPTIONS16), /* 8 Gigabit */ - {"NAND 1GiB 1,8V 8-bit", 0xA3, 0, 1024, 0, LP_OPTIONS}, - {"NAND 1GiB 3,3V 8-bit", 0xD3, 0, 1024, 0, LP_OPTIONS}, - {"NAND 1GiB 1,8V 16-bit", 0xB3, 0, 1024, 0, LP_OPTIONS16}, - {"NAND 1GiB 3,3V 16-bit", 0xC3, 0, 1024, 0, LP_OPTIONS16}, + EXTENDED_ID_NAND("NAND 1GiB 1,8V 8-bit", 0xA3, 1024, LP_OPTIONS), + EXTENDED_ID_NAND("NAND 1GiB 3,3V 8-bit", 0xD3, 1024, LP_OPTIONS), + EXTENDED_ID_NAND("NAND 1GiB 1,8V 16-bit", 0xB3, 1024, LP_OPTIONS16), + EXTENDED_ID_NAND("NAND 1GiB 3,3V 16-bit", 0xC3, 1024, LP_OPTIONS16), /* 16 Gigabit */ - {"NAND 2GiB 1,8V 8-bit", 0xA5, 0, 2048, 0, LP_OPTIONS}, - {"NAND 2GiB 3,3V 8-bit", 0xD5, 0, 2048, 0, LP_OPTIONS}, - {"NAND 2GiB 1,8V 16-bit", 0xB5, 0, 2048, 0, LP_OPTIONS16}, - {"NAND 2GiB 3,3V 16-bit", 0xC5, 0, 2048, 0, LP_OPTIONS16}, + EXTENDED_ID_NAND("NAND 2GiB 1,8V 8-bit", 0xA5, 2048, LP_OPTIONS), + EXTENDED_ID_NAND("NAND 2GiB 3,3V 8-bit", 0xD5, 2048, LP_OPTIONS), + EXTENDED_ID_NAND("NAND 2GiB 1,8V 16-bit", 0xB5, 2048, LP_OPTIONS16), + EXTENDED_ID_NAND("NAND 2GiB 3,3V 16-bit", 0xC5, 2048, LP_OPTIONS16), /* 32 Gigabit */ - {"NAND 4GiB 1,8V 8-bit", 0xA7, 0, 4096, 0, LP_OPTIONS}, - {"NAND 4GiB 3,3V 8-bit", 0xD7, 0, 4096, 0, LP_OPTIONS}, - {"NAND 4GiB 1,8V 16-bit", 0xB7, 0, 4096, 0, LP_OPTIONS16}, - {"NAND 4GiB 3,3V 16-bit", 0xC7, 0, 4096, 0, LP_OPTIONS16}, + EXTENDED_ID_NAND("NAND 4GiB 1,8V 8-bit", 0xA7, 4096, LP_OPTIONS), + EXTENDED_ID_NAND("NAND 4GiB 3,3V 8-bit", 0xD7, 4096, LP_OPTIONS), + EXTENDED_ID_NAND("NAND 4GiB 1,8V 16-bit", 0xB7, 4096, LP_OPTIONS16), + EXTENDED_ID_NAND("NAND 4GiB 3,3V 16-bit", 0xC7, 4096, LP_OPTIONS16), /* 64 Gigabit */ - {"NAND 8GiB 1,8V 8-bit", 0xAE, 0, 8192, 0, LP_OPTIONS}, - {"NAND 8GiB 3,3V 8-bit", 0xDE, 0, 8192, 0, LP_OPTIONS}, - {"NAND 8GiB 1,8V 16-bit", 0xBE, 0, 8192, 0, LP_OPTIONS16}, - {"NAND 8GiB 3,3V 16-bit", 0xCE, 0, 8192, 0, LP_OPTIONS16}, + EXTENDED_ID_NAND("NAND 8GiB 1,8V 8-bit", 0xAE, 8192, LP_OPTIONS), + EXTENDED_ID_NAND("NAND 8GiB 3,3V 8-bit", 0xDE, 8192, LP_OPTIONS), + EXTENDED_ID_NAND("NAND 8GiB 1,8V 16-bit", 0xBE, 8192, LP_OPTIONS16), + EXTENDED_ID_NAND("NAND 8GiB 3,3V 16-bit", 0xCE, 8192, LP_OPTIONS16), /* 128 Gigabit */ - {"NAND 16GiB 1,8V 8-bit", 0x1A, 0, 16384, 0, LP_OPTIONS}, - {"NAND 16GiB 3,3V 8-bit", 0x3A, 0, 16384, 0, LP_OPTIONS}, - {"NAND 16GiB 1,8V 16-bit", 0x2A, 0, 16384, 0, LP_OPTIONS16}, - {"NAND 16GiB 3,3V 16-bit", 0x4A, 0, 16384, 0, LP_OPTIONS16}, + EXTENDED_ID_NAND("NAND 16GiB 1,8V 8-bit", 0x1A, 16384, LP_OPTIONS), + EXTENDED_ID_NAND("NAND 16GiB 3,3V 8-bit", 0x3A, 16384, LP_OPTIONS), + EXTENDED_ID_NAND("NAND 16GiB 1,8V 16-bit", 0x2A, 16384, LP_OPTIONS16), + EXTENDED_ID_NAND("NAND 16GiB 3,3V 16-bit", 0x4A, 16384, LP_OPTIONS16), /* 256 Gigabit */ - {"NAND 32GiB 1,8V 8-bit", 0x1C, 0, 32768, 0, LP_OPTIONS}, - {"NAND 32GiB 3,3V 8-bit", 0x3C, 0, 32768, 0, LP_OPTIONS}, - {"NAND 32GiB 1,8V 16-bit", 0x2C, 0, 32768, 0, LP_OPTIONS16}, - {"NAND 32GiB 3,3V 16-bit", 0x4C, 0, 32768, 0, LP_OPTIONS16}, + EXTENDED_ID_NAND("NAND 32GiB 1,8V 8-bit", 0x1C, 32768, LP_OPTIONS), + EXTENDED_ID_NAND("NAND 32GiB 3,3V 8-bit", 0x3C, 32768, LP_OPTIONS), + EXTENDED_ID_NAND("NAND 32GiB 1,8V 16-bit", 0x2C, 32768, LP_OPTIONS16), + EXTENDED_ID_NAND("NAND 32GiB 3,3V 16-bit", 0x4C, 32768, LP_OPTIONS16), /* 512 Gigabit */ - {"NAND 64GiB 1,8V 8-bit", 0x1E, 0, 65536, 0, LP_OPTIONS}, - {"NAND 64GiB 3,3V 8-bit", 0x3E, 0, 65536, 0, LP_OPTIONS}, - {"NAND 64GiB 1,8V 16-bit", 0x2E, 0, 65536, 0, LP_OPTIONS16}, - {"NAND 64GiB 3,3V 16-bit", 0x4E, 0, 65536, 0, LP_OPTIONS16}, + EXTENDED_ID_NAND("NAND 64GiB 1,8V 8-bit", 0x1E, 65536, LP_OPTIONS), + EXTENDED_ID_NAND("NAND 64GiB 3,3V 8-bit", 0x3E, 65536, LP_OPTIONS), + EXTENDED_ID_NAND("NAND 64GiB 1,8V 16-bit", 0x2E, 65536, LP_OPTIONS16), + EXTENDED_ID_NAND("NAND 64GiB 3,3V 16-bit", 0x4E, 65536, LP_OPTIONS16), - /* - * Renesas AND 1 Gigabit. Those chips do not support extended id and - * have a strange page/block layout ! The chosen minimum erasesize is - * 4 * 2 * 2048 = 16384 Byte, as those chips have an array of 4 page - * planes 1 block = 2 pages, but due to plane arrangement the blocks - * 0-3 consists of page 0 + 4,1 + 5, 2 + 6, 3 + 7 Anyway JFFS2 would - * increase the eraseblock size so we chose a combined one which can be - * erased in one go There are more speed improvements for reads and - * writes possible, but not implemented now - */ - {"AND 128MiB 3,3V 8-bit", 0x01, 2048, 128, 0x4000, - NAND_IS_AND | NAND_4PAGE_ARRAY | BBT_AUTO_REFRESH}, - - {NULL,} + {NULL} }; -/* -* Manufacturer ID list -*/ -const struct nand_manufacturers nand_manuf_ids[] = { +/* Manufacturer IDs */ +struct nand_manufacturers nand_manuf_ids[] = { {NAND_MFR_TOSHIBA, "Toshiba"}, {NAND_MFR_SAMSUNG, "Samsung"}, {NAND_MFR_FUJITSU, "Fujitsu"}, @@ -178,5 +188,14 @@ const struct nand_manufacturers nand_manuf_ids[] = { {NAND_MFR_AMD, "AMD/Spansion"}, {NAND_MFR_MACRONIX, "Macronix"}, {NAND_MFR_EON, "Eon"}, + {NAND_MFR_SANDISK, "SanDisk"}, + {NAND_MFR_INTEL, "Intel"}, {0x0, "Unknown"} }; + +EXPORT_SYMBOL(nand_manuf_ids); +EXPORT_SYMBOL(nand_flash_ids); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Thomas Gleixner <tglx@linutronix.de>"); +MODULE_DESCRIPTION("Nand device & manufacturer IDs"); diff --git a/drivers/mtd/nand/nand_util.c b/drivers/mtd/nand/nand_util.c index b292826..024f6fb 100644 --- a/drivers/mtd/nand/nand_util.c +++ b/drivers/mtd/nand/nand_util.c @@ -187,6 +187,9 @@ int nand_erase_opts(nand_info_t *meminfo, const nand_erase_options_t *opts) #ifdef CONFIG_CMD_NAND_LOCK_UNLOCK +#define NAND_CMD_LOCK_TIGHT 0x2c +#define NAND_CMD_LOCK_STATUS 0x7a + /****************************************************************************** * Support for locking / unlocking operations of some NAND devices *****************************************************************************/ diff --git a/drivers/mtd/nand/ndfc.c b/drivers/mtd/nand/ndfc.c index 5510b13..2659595 100644 --- a/drivers/mtd/nand/ndfc.c +++ b/drivers/mtd/nand/ndfc.c @@ -118,6 +118,7 @@ static void ndfc_write_buf(struct mtd_info *mtdinfo, const uint8_t *buf, int len out_be32((u32 *)(base + NDFC_DATA), *p++); } +#if defined(CONFIG_MTD_NAND_VERIFY_WRITE) static int ndfc_verify_buf(struct mtd_info *mtdinfo, const uint8_t *buf, int len) { struct nand_chip *this = mtdinfo->priv; @@ -130,6 +131,7 @@ static int ndfc_verify_buf(struct mtd_info *mtdinfo, const uint8_t *buf, int len return 0; } +#endif /* * Read a byte from the NDFC. @@ -205,7 +207,9 @@ int board_nand_init(struct nand_chip *nand) #endif nand->write_buf = ndfc_write_buf; +#if defined(CONFIG_MTD_NAND_VERIFY_WRITE) nand->verify_buf = ndfc_verify_buf; +#endif nand->read_byte = ndfc_read_byte; chip++; diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index e33e8d3..03deabc 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c @@ -22,6 +22,7 @@ #include <common.h> #include <linux/compat.h> #include <linux/mtd/mtd.h> +#include "linux/mtd/flashchip.h" #include <linux/mtd/onenand.h> #include <asm/io.h> diff --git a/drivers/mtd/onenand/onenand_bbt.c b/drivers/mtd/onenand/onenand_bbt.c index 0267c2c..52509f1 100644 --- a/drivers/mtd/onenand/onenand_bbt.c +++ b/drivers/mtd/onenand/onenand_bbt.c @@ -140,7 +140,6 @@ static inline int onenand_memory_bbt(struct mtd_info *mtd, { unsigned char data_buf[MAX_ONENAND_PAGESIZE]; - bd->options &= ~NAND_BBT_SCANEMPTY; return create_bbt(mtd, data_buf, bd, -1); } diff --git a/drivers/mtd/onenand/samsung.c b/drivers/mtd/onenand/samsung.c index df04c2b..5e56a29 100644 --- a/drivers/mtd/onenand/samsung.c +++ b/drivers/mtd/onenand/samsung.c @@ -15,20 +15,12 @@ #include <linux/compat.h> #include <linux/mtd/mtd.h> #include <linux/mtd/onenand.h> +#include <linux/mtd/flashchip.h> #include <linux/mtd/samsung_onenand.h> #include <asm/io.h> #include <asm/errno.h> -#ifdef ONENAND_DEBUG -#define DPRINTK(format, args...) \ -do { \ - printf("%s[%d]: " format "\n", __func__, __LINE__, ##args); \ -} while (0) -#else -#define DPRINTK(...) do { } while (0) -#endif - #define ONENAND_ERASE_STATUS 0x00 #define ONENAND_MULTI_ERASE_SET 0x01 #define ONENAND_ERASE_START 0x03 diff --git a/drivers/mtd/spi/sf_internal.h b/drivers/mtd/spi/sf_internal.h index 6bcd522..19d4914 100644 --- a/drivers/mtd/spi/sf_internal.h +++ b/drivers/mtd/spi/sf_internal.h @@ -60,6 +60,10 @@ #define STATUS_QEB_MXIC (1 << 6) #define STATUS_PEC (1 << 7) +#ifdef CONFIG_SYS_SPI_ST_ENABLE_WP_PIN +#define STATUS_SRWD (1 << 7) /* SR write protect */ +#endif + /* Flash timeout values */ #define SPI_FLASH_PROG_TIMEOUT (2 * CONFIG_SYS_HZ) #define SPI_FLASH_PAGE_ERASE_TIMEOUT (5 * CONFIG_SYS_HZ) diff --git a/drivers/mtd/spi/sf_probe.c b/drivers/mtd/spi/sf_probe.c index 36ae5e0..4d148d1 100644 --- a/drivers/mtd/spi/sf_probe.c +++ b/drivers/mtd/spi/sf_probe.c @@ -281,6 +281,34 @@ int spi_flash_decode_fdt(const void *blob, struct spi_flash *flash) } #endif /* CONFIG_OF_CONTROL */ +#ifdef CONFIG_SYS_SPI_ST_ENABLE_WP_PIN +/* enable the W#/Vpp signal to disable writing to the status register */ +static int spi_enable_wp_pin(struct spi_flash *flash) +{ + u8 status; + int ret; + + ret = spi_flash_cmd_read_status(flash, &status); + if (ret < 0) + return ret; + + ret = spi_flash_cmd_write_status(flash, STATUS_SRWD); + if (ret < 0) + return ret; + + ret = spi_flash_cmd_write_disable(flash); + if (ret < 0) + return ret; + + return 0; +} +#else +static int spi_enable_wp_pin(struct spi_flash *flash) +{ + return 0; +} +#endif + static struct spi_flash *spi_flash_probe_slave(struct spi_slave *spi) { struct spi_flash *flash = NULL; @@ -351,6 +379,8 @@ static struct spi_flash *spi_flash_probe_slave(struct spi_slave *spi) puts(" Full access #define CONFIG_SPI_FLASH_BAR\n"); } #endif + if (spi_enable_wp_pin(flash)) + puts("Enable WP pin failed\n"); /* Release spi bus */ spi_release_bus(spi); diff --git a/drivers/mtd/ubi/Makefile b/drivers/mtd/ubi/Makefile index 56c2823..4807f94 100644 --- a/drivers/mtd/ubi/Makefile +++ b/drivers/mtd/ubi/Makefile @@ -5,6 +5,7 @@ # SPDX-License-Identifier: GPL-2.0+ # -obj-y += build.o vtbl.o vmt.o upd.o kapi.o eba.o io.o wl.o scan.o crc32.o +obj-y += attach.o build.o vtbl.o vmt.o upd.o kapi.o eba.o io.o wl.o crc32.o +obj-$(CONFIG_MTD_UBI_FASTMAP) += fastmap.o obj-y += misc.o obj-y += debug.o diff --git a/drivers/mtd/ubi/attach.c b/drivers/mtd/ubi/attach.c new file mode 100644 index 0000000..9fce02e --- /dev/null +++ b/drivers/mtd/ubi/attach.c @@ -0,0 +1,1754 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * SPDX-License-Identifier: GPL-2.0+ + * + * Author: Artem Bityutskiy (Битюцкий Артём) + */ + +/* + * UBI attaching sub-system. + * + * This sub-system is responsible for attaching MTD devices and it also + * implements flash media scanning. + * + * The attaching information is represented by a &struct ubi_attach_info' + * object. Information about volumes is represented by &struct ubi_ainf_volume + * objects which are kept in volume RB-tree with root at the @volumes field. + * The RB-tree is indexed by the volume ID. + * + * Logical eraseblocks are represented by &struct ubi_ainf_peb objects. These + * objects are kept in per-volume RB-trees with the root at the corresponding + * &struct ubi_ainf_volume object. To put it differently, we keep an RB-tree of + * per-volume objects and each of these objects is the root of RB-tree of + * per-LEB objects. + * + * Corrupted physical eraseblocks are put to the @corr list, free physical + * eraseblocks are put to the @free list and the physical eraseblock to be + * erased are put to the @erase list. + * + * About corruptions + * ~~~~~~~~~~~~~~~~~ + * + * UBI protects EC and VID headers with CRC-32 checksums, so it can detect + * whether the headers are corrupted or not. Sometimes UBI also protects the + * data with CRC-32, e.g., when it executes the atomic LEB change operation, or + * when it moves the contents of a PEB for wear-leveling purposes. + * + * UBI tries to distinguish between 2 types of corruptions. + * + * 1. Corruptions caused by power cuts. These are expected corruptions and UBI + * tries to handle them gracefully, without printing too many warnings and + * error messages. The idea is that we do not lose important data in these + * cases - we may lose only the data which were being written to the media just + * before the power cut happened, and the upper layers (e.g., UBIFS) are + * supposed to handle such data losses (e.g., by using the FS journal). + * + * When UBI detects a corruption (CRC-32 mismatch) in a PEB, and it looks like + * the reason is a power cut, UBI puts this PEB to the @erase list, and all + * PEBs in the @erase list are scheduled for erasure later. + * + * 2. Unexpected corruptions which are not caused by power cuts. During + * attaching, such PEBs are put to the @corr list and UBI preserves them. + * Obviously, this lessens the amount of available PEBs, and if at some point + * UBI runs out of free PEBs, it switches to R/O mode. UBI also loudly informs + * about such PEBs every time the MTD device is attached. + * + * However, it is difficult to reliably distinguish between these types of + * corruptions and UBI's strategy is as follows (in case of attaching by + * scanning). UBI assumes corruption type 2 if the VID header is corrupted and + * the data area does not contain all 0xFFs, and there were no bit-flips or + * integrity errors (e.g., ECC errors in case of NAND) while reading the data + * area. Otherwise UBI assumes corruption type 1. So the decision criteria + * are as follows. + * o If the data area contains only 0xFFs, there are no data, and it is safe + * to just erase this PEB - this is corruption type 1. + * o If the data area has bit-flips or data integrity errors (ECC errors on + * NAND), it is probably a PEB which was being erased when power cut + * happened, so this is corruption type 1. However, this is just a guess, + * which might be wrong. + * o Otherwise this is corruption type 2. + */ + +#define __UBOOT__ +#ifndef __UBOOT__ +#include <linux/err.h> +#include <linux/slab.h> +#include <linux/crc32.h> +#include <linux/random.h> +#else +#include <div64.h> +#include <linux/err.h> +#endif + +#include <linux/math64.h> + +#include <ubi_uboot.h> +#include "ubi.h" + +static int self_check_ai(struct ubi_device *ubi, struct ubi_attach_info *ai); + +/* Temporary variables used during scanning */ +static struct ubi_ec_hdr *ech; +static struct ubi_vid_hdr *vidh; + +/** + * add_to_list - add physical eraseblock to a list. + * @ai: attaching information + * @pnum: physical eraseblock number to add + * @vol_id: the last used volume id for the PEB + * @lnum: the last used LEB number for the PEB + * @ec: erase counter of the physical eraseblock + * @to_head: if not zero, add to the head of the list + * @list: the list to add to + * + * This function allocates a 'struct ubi_ainf_peb' object for physical + * eraseblock @pnum and adds it to the "free", "erase", or "alien" lists. + * It stores the @lnum and @vol_id alongside, which can both be + * %UBI_UNKNOWN if they are not available, not readable, or not assigned. + * If @to_head is not zero, PEB will be added to the head of the list, which + * basically means it will be processed first later. E.g., we add corrupted + * PEBs (corrupted due to power cuts) to the head of the erase list to make + * sure we erase them first and get rid of corruptions ASAP. This function + * returns zero in case of success and a negative error code in case of + * failure. + */ +static int add_to_list(struct ubi_attach_info *ai, int pnum, int vol_id, + int lnum, int ec, int to_head, struct list_head *list) +{ + struct ubi_ainf_peb *aeb; + + if (list == &ai->free) { + dbg_bld("add to free: PEB %d, EC %d", pnum, ec); + } else if (list == &ai->erase) { + dbg_bld("add to erase: PEB %d, EC %d", pnum, ec); + } else if (list == &ai->alien) { + dbg_bld("add to alien: PEB %d, EC %d", pnum, ec); + ai->alien_peb_count += 1; + } else + BUG(); + + aeb = kmem_cache_alloc(ai->aeb_slab_cache, GFP_KERNEL); + if (!aeb) + return -ENOMEM; + + aeb->pnum = pnum; + aeb->vol_id = vol_id; + aeb->lnum = lnum; + aeb->ec = ec; + if (to_head) + list_add(&aeb->u.list, list); + else + list_add_tail(&aeb->u.list, list); + return 0; +} + +/** + * add_corrupted - add a corrupted physical eraseblock. + * @ai: attaching information + * @pnum: physical eraseblock number to add + * @ec: erase counter of the physical eraseblock + * + * This function allocates a 'struct ubi_ainf_peb' object for a corrupted + * physical eraseblock @pnum and adds it to the 'corr' list. The corruption + * was presumably not caused by a power cut. Returns zero in case of success + * and a negative error code in case of failure. + */ +static int add_corrupted(struct ubi_attach_info *ai, int pnum, int ec) +{ + struct ubi_ainf_peb *aeb; + + dbg_bld("add to corrupted: PEB %d, EC %d", pnum, ec); + + aeb = kmem_cache_alloc(ai->aeb_slab_cache, GFP_KERNEL); + if (!aeb) + return -ENOMEM; + + ai->corr_peb_count += 1; + aeb->pnum = pnum; + aeb->ec = ec; + list_add(&aeb->u.list, &ai->corr); + return 0; +} + +/** + * validate_vid_hdr - check volume identifier header. + * @vid_hdr: the volume identifier header to check + * @av: information about the volume this logical eraseblock belongs to + * @pnum: physical eraseblock number the VID header came from + * + * This function checks that data stored in @vid_hdr is consistent. Returns + * non-zero if an inconsistency was found and zero if not. + * + * Note, UBI does sanity check of everything it reads from the flash media. + * Most of the checks are done in the I/O sub-system. Here we check that the + * information in the VID header is consistent to the information in other VID + * headers of the same volume. + */ +static int validate_vid_hdr(const struct ubi_vid_hdr *vid_hdr, + const struct ubi_ainf_volume *av, int pnum) +{ + int vol_type = vid_hdr->vol_type; + int vol_id = be32_to_cpu(vid_hdr->vol_id); + int used_ebs = be32_to_cpu(vid_hdr->used_ebs); + int data_pad = be32_to_cpu(vid_hdr->data_pad); + + if (av->leb_count != 0) { + int av_vol_type; + + /* + * This is not the first logical eraseblock belonging to this + * volume. Ensure that the data in its VID header is consistent + * to the data in previous logical eraseblock headers. + */ + + if (vol_id != av->vol_id) { + ubi_err("inconsistent vol_id"); + goto bad; + } + + if (av->vol_type == UBI_STATIC_VOLUME) + av_vol_type = UBI_VID_STATIC; + else + av_vol_type = UBI_VID_DYNAMIC; + + if (vol_type != av_vol_type) { + ubi_err("inconsistent vol_type"); + goto bad; + } + + if (used_ebs != av->used_ebs) { + ubi_err("inconsistent used_ebs"); + goto bad; + } + + if (data_pad != av->data_pad) { + ubi_err("inconsistent data_pad"); + goto bad; + } + } + + return 0; + +bad: + ubi_err("inconsistent VID header at PEB %d", pnum); + ubi_dump_vid_hdr(vid_hdr); + ubi_dump_av(av); + return -EINVAL; +} + +/** + * add_volume - add volume to the attaching information. + * @ai: attaching information + * @vol_id: ID of the volume to add + * @pnum: physical eraseblock number + * @vid_hdr: volume identifier header + * + * If the volume corresponding to the @vid_hdr logical eraseblock is already + * present in the attaching information, this function does nothing. Otherwise + * it adds corresponding volume to the attaching information. Returns a pointer + * to the allocated "av" object in case of success and a negative error code in + * case of failure. + */ +static struct ubi_ainf_volume *add_volume(struct ubi_attach_info *ai, + int vol_id, int pnum, + const struct ubi_vid_hdr *vid_hdr) +{ + struct ubi_ainf_volume *av; + struct rb_node **p = &ai->volumes.rb_node, *parent = NULL; + + ubi_assert(vol_id == be32_to_cpu(vid_hdr->vol_id)); + + /* Walk the volume RB-tree to look if this volume is already present */ + while (*p) { + parent = *p; + av = rb_entry(parent, struct ubi_ainf_volume, rb); + + if (vol_id == av->vol_id) + return av; + + if (vol_id > av->vol_id) + p = &(*p)->rb_left; + else + p = &(*p)->rb_right; + } + + /* The volume is absent - add it */ + av = kmalloc(sizeof(struct ubi_ainf_volume), GFP_KERNEL); + if (!av) + return ERR_PTR(-ENOMEM); + + av->highest_lnum = av->leb_count = 0; + av->vol_id = vol_id; + av->root = RB_ROOT; + av->used_ebs = be32_to_cpu(vid_hdr->used_ebs); + av->data_pad = be32_to_cpu(vid_hdr->data_pad); + av->compat = vid_hdr->compat; + av->vol_type = vid_hdr->vol_type == UBI_VID_DYNAMIC ? UBI_DYNAMIC_VOLUME + : UBI_STATIC_VOLUME; + if (vol_id > ai->highest_vol_id) + ai->highest_vol_id = vol_id; + + rb_link_node(&av->rb, parent, p); + rb_insert_color(&av->rb, &ai->volumes); + ai->vols_found += 1; + dbg_bld("added volume %d", vol_id); + return av; +} + +/** + * ubi_compare_lebs - find out which logical eraseblock is newer. + * @ubi: UBI device description object + * @aeb: first logical eraseblock to compare + * @pnum: physical eraseblock number of the second logical eraseblock to + * compare + * @vid_hdr: volume identifier header of the second logical eraseblock + * + * This function compares 2 copies of a LEB and informs which one is newer. In + * case of success this function returns a positive value, in case of failure, a + * negative error code is returned. The success return codes use the following + * bits: + * o bit 0 is cleared: the first PEB (described by @aeb) is newer than the + * second PEB (described by @pnum and @vid_hdr); + * o bit 0 is set: the second PEB is newer; + * o bit 1 is cleared: no bit-flips were detected in the newer LEB; + * o bit 1 is set: bit-flips were detected in the newer LEB; + * o bit 2 is cleared: the older LEB is not corrupted; + * o bit 2 is set: the older LEB is corrupted. + */ +int ubi_compare_lebs(struct ubi_device *ubi, const struct ubi_ainf_peb *aeb, + int pnum, const struct ubi_vid_hdr *vid_hdr) +{ + int len, err, second_is_newer, bitflips = 0, corrupted = 0; + uint32_t data_crc, crc; + struct ubi_vid_hdr *vh = NULL; + unsigned long long sqnum2 = be64_to_cpu(vid_hdr->sqnum); + + if (sqnum2 == aeb->sqnum) { + /* + * This must be a really ancient UBI image which has been + * created before sequence numbers support has been added. At + * that times we used 32-bit LEB versions stored in logical + * eraseblocks. That was before UBI got into mainline. We do not + * support these images anymore. Well, those images still work, + * but only if no unclean reboots happened. + */ + ubi_err("unsupported on-flash UBI format"); + return -EINVAL; + } + + /* Obviously the LEB with lower sequence counter is older */ + second_is_newer = (sqnum2 > aeb->sqnum); + + /* + * Now we know which copy is newer. If the copy flag of the PEB with + * newer version is not set, then we just return, otherwise we have to + * check data CRC. For the second PEB we already have the VID header, + * for the first one - we'll need to re-read it from flash. + * + * Note: this may be optimized so that we wouldn't read twice. + */ + + if (second_is_newer) { + if (!vid_hdr->copy_flag) { + /* It is not a copy, so it is newer */ + dbg_bld("second PEB %d is newer, copy_flag is unset", + pnum); + return 1; + } + } else { + if (!aeb->copy_flag) { + /* It is not a copy, so it is newer */ + dbg_bld("first PEB %d is newer, copy_flag is unset", + pnum); + return bitflips << 1; + } + + vh = ubi_zalloc_vid_hdr(ubi, GFP_KERNEL); + if (!vh) + return -ENOMEM; + + pnum = aeb->pnum; + err = ubi_io_read_vid_hdr(ubi, pnum, vh, 0); + if (err) { + if (err == UBI_IO_BITFLIPS) + bitflips = 1; + else { + ubi_err("VID of PEB %d header is bad, but it was OK earlier, err %d", + pnum, err); + if (err > 0) + err = -EIO; + + goto out_free_vidh; + } + } + + vid_hdr = vh; + } + + /* Read the data of the copy and check the CRC */ + + len = be32_to_cpu(vid_hdr->data_size); + + mutex_lock(&ubi->buf_mutex); + err = ubi_io_read_data(ubi, ubi->peb_buf, pnum, 0, len); + if (err && err != UBI_IO_BITFLIPS && !mtd_is_eccerr(err)) + goto out_unlock; + + data_crc = be32_to_cpu(vid_hdr->data_crc); + crc = crc32(UBI_CRC32_INIT, ubi->peb_buf, len); + if (crc != data_crc) { + dbg_bld("PEB %d CRC error: calculated %#08x, must be %#08x", + pnum, crc, data_crc); + corrupted = 1; + bitflips = 0; + second_is_newer = !second_is_newer; + } else { + dbg_bld("PEB %d CRC is OK", pnum); + bitflips = !!err; + } + mutex_unlock(&ubi->buf_mutex); + + ubi_free_vid_hdr(ubi, vh); + + if (second_is_newer) + dbg_bld("second PEB %d is newer, copy_flag is set", pnum); + else + dbg_bld("first PEB %d is newer, copy_flag is set", pnum); + + return second_is_newer | (bitflips << 1) | (corrupted << 2); + +out_unlock: + mutex_unlock(&ubi->buf_mutex); +out_free_vidh: + ubi_free_vid_hdr(ubi, vh); + return err; +} + +/** + * ubi_add_to_av - add used physical eraseblock to the attaching information. + * @ubi: UBI device description object + * @ai: attaching information + * @pnum: the physical eraseblock number + * @ec: erase counter + * @vid_hdr: the volume identifier header + * @bitflips: if bit-flips were detected when this physical eraseblock was read + * + * This function adds information about a used physical eraseblock to the + * 'used' tree of the corresponding volume. The function is rather complex + * because it has to handle cases when this is not the first physical + * eraseblock belonging to the same logical eraseblock, and the newer one has + * to be picked, while the older one has to be dropped. This function returns + * zero in case of success and a negative error code in case of failure. + */ +int ubi_add_to_av(struct ubi_device *ubi, struct ubi_attach_info *ai, int pnum, + int ec, const struct ubi_vid_hdr *vid_hdr, int bitflips) +{ + int err, vol_id, lnum; + unsigned long long sqnum; + struct ubi_ainf_volume *av; + struct ubi_ainf_peb *aeb; + struct rb_node **p, *parent = NULL; + + vol_id = be32_to_cpu(vid_hdr->vol_id); + lnum = be32_to_cpu(vid_hdr->lnum); + sqnum = be64_to_cpu(vid_hdr->sqnum); + + dbg_bld("PEB %d, LEB %d:%d, EC %d, sqnum %llu, bitflips %d", + pnum, vol_id, lnum, ec, sqnum, bitflips); + + av = add_volume(ai, vol_id, pnum, vid_hdr); + if (IS_ERR(av)) + return PTR_ERR(av); + + if (ai->max_sqnum < sqnum) + ai->max_sqnum = sqnum; + + /* + * Walk the RB-tree of logical eraseblocks of volume @vol_id to look + * if this is the first instance of this logical eraseblock or not. + */ + p = &av->root.rb_node; + while (*p) { + int cmp_res; + + parent = *p; + aeb = rb_entry(parent, struct ubi_ainf_peb, u.rb); + if (lnum != aeb->lnum) { + if (lnum < aeb->lnum) + p = &(*p)->rb_left; + else + p = &(*p)->rb_right; + continue; + } + + /* + * There is already a physical eraseblock describing the same + * logical eraseblock present. + */ + + dbg_bld("this LEB already exists: PEB %d, sqnum %llu, EC %d", + aeb->pnum, aeb->sqnum, aeb->ec); + + /* + * Make sure that the logical eraseblocks have different + * sequence numbers. Otherwise the image is bad. + * + * However, if the sequence number is zero, we assume it must + * be an ancient UBI image from the era when UBI did not have + * sequence numbers. We still can attach these images, unless + * there is a need to distinguish between old and new + * eraseblocks, in which case we'll refuse the image in + * 'ubi_compare_lebs()'. In other words, we attach old clean + * images, but refuse attaching old images with duplicated + * logical eraseblocks because there was an unclean reboot. + */ + if (aeb->sqnum == sqnum && sqnum != 0) { + ubi_err("two LEBs with same sequence number %llu", + sqnum); + ubi_dump_aeb(aeb, 0); + ubi_dump_vid_hdr(vid_hdr); + return -EINVAL; + } + + /* + * Now we have to drop the older one and preserve the newer + * one. + */ + cmp_res = ubi_compare_lebs(ubi, aeb, pnum, vid_hdr); + if (cmp_res < 0) + return cmp_res; + + if (cmp_res & 1) { + /* + * This logical eraseblock is newer than the one + * found earlier. + */ + err = validate_vid_hdr(vid_hdr, av, pnum); + if (err) + return err; + + err = add_to_list(ai, aeb->pnum, aeb->vol_id, + aeb->lnum, aeb->ec, cmp_res & 4, + &ai->erase); + if (err) + return err; + + aeb->ec = ec; + aeb->pnum = pnum; + aeb->vol_id = vol_id; + aeb->lnum = lnum; + aeb->scrub = ((cmp_res & 2) || bitflips); + aeb->copy_flag = vid_hdr->copy_flag; + aeb->sqnum = sqnum; + + if (av->highest_lnum == lnum) + av->last_data_size = + be32_to_cpu(vid_hdr->data_size); + + return 0; + } else { + /* + * This logical eraseblock is older than the one found + * previously. + */ + return add_to_list(ai, pnum, vol_id, lnum, ec, + cmp_res & 4, &ai->erase); + } + } + + /* + * We've met this logical eraseblock for the first time, add it to the + * attaching information. + */ + + err = validate_vid_hdr(vid_hdr, av, pnum); + if (err) + return err; + + aeb = kmem_cache_alloc(ai->aeb_slab_cache, GFP_KERNEL); + if (!aeb) + return -ENOMEM; + + aeb->ec = ec; + aeb->pnum = pnum; + aeb->vol_id = vol_id; + aeb->lnum = lnum; + aeb->scrub = bitflips; + aeb->copy_flag = vid_hdr->copy_flag; + aeb->sqnum = sqnum; + + if (av->highest_lnum <= lnum) { + av->highest_lnum = lnum; + av->last_data_size = be32_to_cpu(vid_hdr->data_size); + } + + av->leb_count += 1; + rb_link_node(&aeb->u.rb, parent, p); + rb_insert_color(&aeb->u.rb, &av->root); + return 0; +} + +/** + * ubi_find_av - find volume in the attaching information. + * @ai: attaching information + * @vol_id: the requested volume ID + * + * This function returns a pointer to the volume description or %NULL if there + * are no data about this volume in the attaching information. + */ +struct ubi_ainf_volume *ubi_find_av(const struct ubi_attach_info *ai, + int vol_id) +{ + struct ubi_ainf_volume *av; + struct rb_node *p = ai->volumes.rb_node; + + while (p) { + av = rb_entry(p, struct ubi_ainf_volume, rb); + + if (vol_id == av->vol_id) + return av; + + if (vol_id > av->vol_id) + p = p->rb_left; + else + p = p->rb_right; + } + + return NULL; +} + +/** + * ubi_remove_av - delete attaching information about a volume. + * @ai: attaching information + * @av: the volume attaching information to delete + */ +void ubi_remove_av(struct ubi_attach_info *ai, struct ubi_ainf_volume *av) +{ + struct rb_node *rb; + struct ubi_ainf_peb *aeb; + + dbg_bld("remove attaching information about volume %d", av->vol_id); + + while ((rb = rb_first(&av->root))) { + aeb = rb_entry(rb, struct ubi_ainf_peb, u.rb); + rb_erase(&aeb->u.rb, &av->root); + list_add_tail(&aeb->u.list, &ai->erase); + } + + rb_erase(&av->rb, &ai->volumes); + kfree(av); + ai->vols_found -= 1; +} + +/** + * early_erase_peb - erase a physical eraseblock. + * @ubi: UBI device description object + * @ai: attaching information + * @pnum: physical eraseblock number to erase; + * @ec: erase counter value to write (%UBI_UNKNOWN if it is unknown) + * + * This function erases physical eraseblock 'pnum', and writes the erase + * counter header to it. This function should only be used on UBI device + * initialization stages, when the EBA sub-system had not been yet initialized. + * This function returns zero in case of success and a negative error code in + * case of failure. + */ +static int early_erase_peb(struct ubi_device *ubi, + const struct ubi_attach_info *ai, int pnum, int ec) +{ + int err; + struct ubi_ec_hdr *ec_hdr; + + if ((long long)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 %d", pnum, ec); + return -EINVAL; + } + + ec_hdr = kzalloc(ubi->ec_hdr_alsize, GFP_KERNEL); + if (!ec_hdr) + return -ENOMEM; + + ec_hdr->ec = cpu_to_be64(ec); + + err = ubi_io_sync_erase(ubi, pnum, 0); + if (err < 0) + goto out_free; + + err = ubi_io_write_ec_hdr(ubi, pnum, ec_hdr); + +out_free: + kfree(ec_hdr); + return err; +} + +/** + * ubi_early_get_peb - get a free physical eraseblock. + * @ubi: UBI device description object + * @ai: attaching information + * + * This function returns a free physical eraseblock. It is supposed to be + * called on the UBI initialization stages when the wear-leveling sub-system is + * not initialized yet. This function picks a physical eraseblocks from one of + * the lists, writes the EC header if it is needed, and removes it from the + * list. + * + * This function returns a pointer to the "aeb" of the found free PEB in case + * of success and an error code in case of failure. + */ +struct ubi_ainf_peb *ubi_early_get_peb(struct ubi_device *ubi, + struct ubi_attach_info *ai) +{ + int err = 0; + struct ubi_ainf_peb *aeb, *tmp_aeb; + + if (!list_empty(&ai->free)) { + aeb = list_entry(ai->free.next, struct ubi_ainf_peb, u.list); + list_del(&aeb->u.list); + dbg_bld("return free PEB %d, EC %d", aeb->pnum, aeb->ec); + return aeb; + } + + /* + * We try to erase the first physical eraseblock from the erase list + * and pick it if we succeed, or try to erase the next one if not. And + * so forth. We don't want to take care about bad eraseblocks here - + * they'll be handled later. + */ + list_for_each_entry_safe(aeb, tmp_aeb, &ai->erase, u.list) { + if (aeb->ec == UBI_UNKNOWN) + aeb->ec = ai->mean_ec; + + err = early_erase_peb(ubi, ai, aeb->pnum, aeb->ec+1); + if (err) + continue; + + aeb->ec += 1; + list_del(&aeb->u.list); + dbg_bld("return PEB %d, EC %d", aeb->pnum, aeb->ec); + return aeb; + } + + ubi_err("no free eraseblocks"); + return ERR_PTR(-ENOSPC); +} + +/** + * check_corruption - check the data area of PEB. + * @ubi: UBI device description object + * @vid_hdr: the (corrupted) VID header of this PEB + * @pnum: the physical eraseblock number to check + * + * This is a helper function which is used to distinguish between VID header + * corruptions caused by power cuts and other reasons. If the PEB contains only + * 0xFF bytes in the data area, the VID header is most probably corrupted + * because of a power cut (%0 is returned in this case). Otherwise, it was + * probably corrupted for some other reasons (%1 is returned in this case). A + * negative error code is returned if a read error occurred. + * + * If the corruption reason was a power cut, UBI can safely erase this PEB. + * Otherwise, it should preserve it to avoid possibly destroying important + * information. + */ +static int check_corruption(struct ubi_device *ubi, struct ubi_vid_hdr *vid_hdr, + int pnum) +{ + int err; + + mutex_lock(&ubi->buf_mutex); + memset(ubi->peb_buf, 0x00, ubi->leb_size); + + err = ubi_io_read(ubi, ubi->peb_buf, pnum, ubi->leb_start, + ubi->leb_size); + if (err == UBI_IO_BITFLIPS || mtd_is_eccerr(err)) { + /* + * Bit-flips or integrity errors while reading the data area. + * It is difficult to say for sure what type of corruption is + * this, but presumably a power cut happened while this PEB was + * erased, so it became unstable and corrupted, and should be + * erased. + */ + err = 0; + goto out_unlock; + } + + if (err) + goto out_unlock; + + if (ubi_check_pattern(ubi->peb_buf, 0xFF, ubi->leb_size)) + goto out_unlock; + + ubi_err("PEB %d contains corrupted VID header, and the data does not contain all 0xFF", + pnum); + ubi_err("this may be a non-UBI PEB or a severe VID header corruption which requires manual inspection"); + ubi_dump_vid_hdr(vid_hdr); + pr_err("hexdump of PEB %d offset %d, length %d", + pnum, ubi->leb_start, ubi->leb_size); + ubi_dbg_print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET, 32, 1, + ubi->peb_buf, ubi->leb_size, 1); + err = 1; + +out_unlock: + mutex_unlock(&ubi->buf_mutex); + return err; +} + +/** + * scan_peb - scan and process UBI headers of a PEB. + * @ubi: UBI device description object + * @ai: attaching information + * @pnum: the physical eraseblock number + * @vid: The volume ID of the found volume will be stored in this pointer + * @sqnum: The sqnum of the found volume will be stored in this pointer + * + * This function reads UBI headers of PEB @pnum, checks them, and adds + * information about this PEB to the corresponding list or RB-tree in the + * "attaching info" structure. Returns zero if the physical eraseblock was + * successfully handled and a negative error code in case of failure. + */ +static int scan_peb(struct ubi_device *ubi, struct ubi_attach_info *ai, + int pnum, int *vid, unsigned long long *sqnum) +{ + long long uninitialized_var(ec); + int err, bitflips = 0, vol_id = -1, ec_err = 0; + + dbg_bld("scan PEB %d", pnum); + + /* Skip bad physical eraseblocks */ + err = ubi_io_is_bad(ubi, pnum); + if (err < 0) + return err; + else if (err) { + ai->bad_peb_count += 1; + return 0; + } + + err = ubi_io_read_ec_hdr(ubi, pnum, ech, 0); + if (err < 0) + return err; + switch (err) { + case 0: + break; + case UBI_IO_BITFLIPS: + bitflips = 1; + break; + case UBI_IO_FF: + ai->empty_peb_count += 1; + return add_to_list(ai, pnum, UBI_UNKNOWN, UBI_UNKNOWN, + UBI_UNKNOWN, 0, &ai->erase); + case UBI_IO_FF_BITFLIPS: + ai->empty_peb_count += 1; + return add_to_list(ai, pnum, UBI_UNKNOWN, UBI_UNKNOWN, + UBI_UNKNOWN, 1, &ai->erase); + case UBI_IO_BAD_HDR_EBADMSG: + case UBI_IO_BAD_HDR: + /* + * We have to also look at the VID header, possibly it is not + * corrupted. Set %bitflips flag in order to make this PEB be + * moved and EC be re-created. + */ + ec_err = err; + ec = UBI_UNKNOWN; + bitflips = 1; + break; + default: + ubi_err("'ubi_io_read_ec_hdr()' returned unknown code %d", err); + return -EINVAL; + } + + if (!ec_err) { + int image_seq; + + /* Make sure UBI version is OK */ + if (ech->version != UBI_VERSION) { + ubi_err("this UBI version is %d, image version is %d", + UBI_VERSION, (int)ech->version); + return -EINVAL; + } + + ec = be64_to_cpu(ech->ec); + if (ec > UBI_MAX_ERASECOUNTER) { + /* + * Erase counter overflow. The EC headers have 64 bits + * reserved, but we anyway make use of only 31 bit + * values, as this seems to be enough for any existing + * flash. Upgrade UBI and use 64-bit erase counters + * internally. + */ + ubi_err("erase counter overflow, max is %d", + UBI_MAX_ERASECOUNTER); + ubi_dump_ec_hdr(ech); + return -EINVAL; + } + + /* + * Make sure that all PEBs have the same image sequence number. + * This allows us to detect situations when users flash UBI + * images incorrectly, so that the flash has the new UBI image + * and leftovers from the old one. This feature was added + * relatively recently, and the sequence number was always + * zero, because old UBI implementations always set it to zero. + * For this reasons, we do not panic if some PEBs have zero + * sequence number, while other PEBs have non-zero sequence + * number. + */ + image_seq = be32_to_cpu(ech->image_seq); + if (!ubi->image_seq) + ubi->image_seq = image_seq; + if (image_seq && ubi->image_seq != image_seq) { + ubi_err("bad image sequence number %d in PEB %d, expected %d", + image_seq, pnum, ubi->image_seq); + ubi_dump_ec_hdr(ech); + return -EINVAL; + } + } + + /* OK, we've done with the EC header, let's look at the VID header */ + + err = ubi_io_read_vid_hdr(ubi, pnum, vidh, 0); + if (err < 0) + return err; + switch (err) { + case 0: + break; + case UBI_IO_BITFLIPS: + bitflips = 1; + break; + case UBI_IO_BAD_HDR_EBADMSG: + if (ec_err == UBI_IO_BAD_HDR_EBADMSG) + /* + * Both EC and VID headers are corrupted and were read + * with data integrity error, probably this is a bad + * PEB, bit it is not marked as bad yet. This may also + * be a result of power cut during erasure. + */ + ai->maybe_bad_peb_count += 1; + case UBI_IO_BAD_HDR: + if (ec_err) + /* + * Both headers are corrupted. There is a possibility + * that this a valid UBI PEB which has corresponding + * LEB, but the headers are corrupted. However, it is + * impossible to distinguish it from a PEB which just + * contains garbage because of a power cut during erase + * operation. So we just schedule this PEB for erasure. + * + * Besides, in case of NOR flash, we deliberately + * corrupt both headers because NOR flash erasure is + * slow and can start from the end. + */ + err = 0; + else + /* + * The EC was OK, but the VID header is corrupted. We + * have to check what is in the data area. + */ + err = check_corruption(ubi, vidh, pnum); + + if (err < 0) + return err; + else if (!err) + /* This corruption is caused by a power cut */ + err = add_to_list(ai, pnum, UBI_UNKNOWN, + UBI_UNKNOWN, ec, 1, &ai->erase); + else + /* This is an unexpected corruption */ + err = add_corrupted(ai, pnum, ec); + if (err) + return err; + goto adjust_mean_ec; + case UBI_IO_FF_BITFLIPS: + err = add_to_list(ai, pnum, UBI_UNKNOWN, UBI_UNKNOWN, + ec, 1, &ai->erase); + if (err) + return err; + goto adjust_mean_ec; + case UBI_IO_FF: + if (ec_err || bitflips) + err = add_to_list(ai, pnum, UBI_UNKNOWN, + UBI_UNKNOWN, ec, 1, &ai->erase); + else + err = add_to_list(ai, pnum, UBI_UNKNOWN, + UBI_UNKNOWN, ec, 0, &ai->free); + if (err) + return err; + goto adjust_mean_ec; + default: + ubi_err("'ubi_io_read_vid_hdr()' returned unknown code %d", + err); + return -EINVAL; + } + + vol_id = be32_to_cpu(vidh->vol_id); + if (vid) + *vid = vol_id; + if (sqnum) + *sqnum = be64_to_cpu(vidh->sqnum); + if (vol_id > UBI_MAX_VOLUMES && vol_id != UBI_LAYOUT_VOLUME_ID) { + int lnum = be32_to_cpu(vidh->lnum); + + /* Unsupported internal volume */ + switch (vidh->compat) { + case UBI_COMPAT_DELETE: + if (vol_id != UBI_FM_SB_VOLUME_ID + && vol_id != UBI_FM_DATA_VOLUME_ID) { + ubi_msg("\"delete\" compatible internal volume %d:%d found, will remove it", + vol_id, lnum); + } + err = add_to_list(ai, pnum, vol_id, lnum, + ec, 1, &ai->erase); + if (err) + return err; + return 0; + + case UBI_COMPAT_RO: + ubi_msg("read-only compatible internal volume %d:%d found, switch to read-only mode", + vol_id, lnum); + ubi->ro_mode = 1; + break; + + case UBI_COMPAT_PRESERVE: + ubi_msg("\"preserve\" compatible internal volume %d:%d found", + vol_id, lnum); + err = add_to_list(ai, pnum, vol_id, lnum, + ec, 0, &ai->alien); + if (err) + return err; + return 0; + + case UBI_COMPAT_REJECT: + ubi_err("incompatible internal volume %d:%d found", + vol_id, lnum); + return -EINVAL; + } + } + + if (ec_err) + ubi_warn("valid VID header but corrupted EC header at PEB %d", + pnum); + err = ubi_add_to_av(ubi, ai, pnum, ec, vidh, bitflips); + if (err) + return err; + +adjust_mean_ec: + if (!ec_err) { + ai->ec_sum += ec; + ai->ec_count += 1; + if (ec > ai->max_ec) + ai->max_ec = ec; + if (ec < ai->min_ec) + ai->min_ec = ec; + } + + return 0; +} + +/** + * late_analysis - analyze the overall situation with PEB. + * @ubi: UBI device description object + * @ai: attaching information + * + * This is a helper function which takes a look what PEBs we have after we + * gather information about all of them ("ai" is compete). It decides whether + * the flash is empty and should be formatted of whether there are too many + * corrupted PEBs and we should not attach this MTD device. Returns zero if we + * should proceed with attaching the MTD device, and %-EINVAL if we should not. + */ +static int late_analysis(struct ubi_device *ubi, struct ubi_attach_info *ai) +{ + struct ubi_ainf_peb *aeb; + int max_corr, peb_count; + + peb_count = ubi->peb_count - ai->bad_peb_count - ai->alien_peb_count; + max_corr = peb_count / 20 ?: 8; + + /* + * Few corrupted PEBs is not a problem and may be just a result of + * unclean reboots. However, many of them may indicate some problems + * with the flash HW or driver. + */ + if (ai->corr_peb_count) { + ubi_err("%d PEBs are corrupted and preserved", + ai->corr_peb_count); + pr_err("Corrupted PEBs are:"); + list_for_each_entry(aeb, &ai->corr, u.list) + pr_cont(" %d", aeb->pnum); + pr_cont("\n"); + + /* + * If too many PEBs are corrupted, we refuse attaching, + * otherwise, only print a warning. + */ + if (ai->corr_peb_count >= max_corr) { + ubi_err("too many corrupted PEBs, refusing"); + return -EINVAL; + } + } + + if (ai->empty_peb_count + ai->maybe_bad_peb_count == peb_count) { + /* + * All PEBs are empty, or almost all - a couple PEBs look like + * they may be bad PEBs which were not marked as bad yet. + * + * This piece of code basically tries to distinguish between + * the following situations: + * + * 1. Flash is empty, but there are few bad PEBs, which are not + * marked as bad so far, and which were read with error. We + * want to go ahead and format this flash. While formatting, + * the faulty PEBs will probably be marked as bad. + * + * 2. Flash contains non-UBI data and we do not want to format + * it and destroy possibly important information. + */ + if (ai->maybe_bad_peb_count <= 2) { + ai->is_empty = 1; + ubi_msg("empty MTD device detected"); + get_random_bytes(&ubi->image_seq, + sizeof(ubi->image_seq)); + } else { + ubi_err("MTD device is not UBI-formatted and possibly contains non-UBI data - refusing it"); + return -EINVAL; + } + + } + + return 0; +} + +/** + * destroy_av - free volume attaching information. + * @av: volume attaching information + * @ai: attaching information + * + * This function destroys the volume attaching information. + */ +static void destroy_av(struct ubi_attach_info *ai, struct ubi_ainf_volume *av) +{ + struct ubi_ainf_peb *aeb; + struct rb_node *this = av->root.rb_node; + + while (this) { + if (this->rb_left) + this = this->rb_left; + else if (this->rb_right) + this = this->rb_right; + else { + aeb = rb_entry(this, struct ubi_ainf_peb, u.rb); + this = rb_parent(this); + if (this) { + if (this->rb_left == &aeb->u.rb) + this->rb_left = NULL; + else + this->rb_right = NULL; + } + + kmem_cache_free(ai->aeb_slab_cache, aeb); + } + } + kfree(av); +} + +/** + * destroy_ai - destroy attaching information. + * @ai: attaching information + */ +static void destroy_ai(struct ubi_attach_info *ai) +{ + struct ubi_ainf_peb *aeb, *aeb_tmp; + struct ubi_ainf_volume *av; + struct rb_node *rb; + + list_for_each_entry_safe(aeb, aeb_tmp, &ai->alien, u.list) { + list_del(&aeb->u.list); + kmem_cache_free(ai->aeb_slab_cache, aeb); + } + list_for_each_entry_safe(aeb, aeb_tmp, &ai->erase, u.list) { + list_del(&aeb->u.list); + kmem_cache_free(ai->aeb_slab_cache, aeb); + } + list_for_each_entry_safe(aeb, aeb_tmp, &ai->corr, u.list) { + list_del(&aeb->u.list); + kmem_cache_free(ai->aeb_slab_cache, aeb); + } + list_for_each_entry_safe(aeb, aeb_tmp, &ai->free, u.list) { + list_del(&aeb->u.list); + kmem_cache_free(ai->aeb_slab_cache, aeb); + } + + /* Destroy the volume RB-tree */ + rb = ai->volumes.rb_node; + while (rb) { + if (rb->rb_left) + rb = rb->rb_left; + else if (rb->rb_right) + rb = rb->rb_right; + else { + av = rb_entry(rb, struct ubi_ainf_volume, rb); + + rb = rb_parent(rb); + if (rb) { + if (rb->rb_left == &av->rb) + rb->rb_left = NULL; + else + rb->rb_right = NULL; + } + + destroy_av(ai, av); + } + } + + if (ai->aeb_slab_cache) + kmem_cache_destroy(ai->aeb_slab_cache); + + kfree(ai); +} + +/** + * scan_all - scan entire MTD device. + * @ubi: UBI device description object + * @ai: attach info object + * @start: start scanning at this PEB + * + * This function does full scanning of an MTD device and returns complete + * information about it in form of a "struct ubi_attach_info" object. In case + * of failure, an error code is returned. + */ +static int scan_all(struct ubi_device *ubi, struct ubi_attach_info *ai, + int start) +{ + int err, pnum; + struct rb_node *rb1, *rb2; + struct ubi_ainf_volume *av; + struct ubi_ainf_peb *aeb; + + err = -ENOMEM; + + ech = kzalloc(ubi->ec_hdr_alsize, GFP_KERNEL); + if (!ech) + return err; + + vidh = ubi_zalloc_vid_hdr(ubi, GFP_KERNEL); + if (!vidh) + goto out_ech; + + for (pnum = start; pnum < ubi->peb_count; pnum++) { + cond_resched(); + + dbg_gen("process PEB %d", pnum); + err = scan_peb(ubi, ai, pnum, NULL, NULL); + if (err < 0) + goto out_vidh; + } + + ubi_msg("scanning is finished"); + + /* Calculate mean erase counter */ + if (ai->ec_count) + ai->mean_ec = div_u64(ai->ec_sum, ai->ec_count); + + err = late_analysis(ubi, ai); + if (err) + goto out_vidh; + + /* + * In case of unknown erase counter we use the mean erase counter + * value. + */ + ubi_rb_for_each_entry(rb1, av, &ai->volumes, rb) { + ubi_rb_for_each_entry(rb2, aeb, &av->root, u.rb) + if (aeb->ec == UBI_UNKNOWN) + aeb->ec = ai->mean_ec; + } + + list_for_each_entry(aeb, &ai->free, u.list) { + if (aeb->ec == UBI_UNKNOWN) + aeb->ec = ai->mean_ec; + } + + list_for_each_entry(aeb, &ai->corr, u.list) + if (aeb->ec == UBI_UNKNOWN) + aeb->ec = ai->mean_ec; + + list_for_each_entry(aeb, &ai->erase, u.list) + if (aeb->ec == UBI_UNKNOWN) + aeb->ec = ai->mean_ec; + + err = self_check_ai(ubi, ai); + if (err) + goto out_vidh; + + ubi_free_vid_hdr(ubi, vidh); + kfree(ech); + + return 0; + +out_vidh: + ubi_free_vid_hdr(ubi, vidh); +out_ech: + kfree(ech); + return err; +} + +#ifdef CONFIG_MTD_UBI_FASTMAP + +/** + * scan_fastmap - try to find a fastmap and attach from it. + * @ubi: UBI device description object + * @ai: attach info object + * + * Returns 0 on success, negative return values indicate an internal + * error. + * UBI_NO_FASTMAP denotes that no fastmap was found. + * UBI_BAD_FASTMAP denotes that the found fastmap was invalid. + */ +static int scan_fast(struct ubi_device *ubi, struct ubi_attach_info *ai) +{ + int err, pnum, fm_anchor = -1; + unsigned long long max_sqnum = 0; + + err = -ENOMEM; + + ech = kzalloc(ubi->ec_hdr_alsize, GFP_KERNEL); + if (!ech) + goto out; + + vidh = ubi_zalloc_vid_hdr(ubi, GFP_KERNEL); + if (!vidh) + goto out_ech; + + for (pnum = 0; pnum < UBI_FM_MAX_START; pnum++) { + int vol_id = -1; + unsigned long long sqnum = -1; + cond_resched(); + + dbg_gen("process PEB %d", pnum); + err = scan_peb(ubi, ai, pnum, &vol_id, &sqnum); + if (err < 0) + goto out_vidh; + + if (vol_id == UBI_FM_SB_VOLUME_ID && sqnum > max_sqnum) { + max_sqnum = sqnum; + fm_anchor = pnum; + } + } + + ubi_free_vid_hdr(ubi, vidh); + kfree(ech); + + if (fm_anchor < 0) + return UBI_NO_FASTMAP; + + return ubi_scan_fastmap(ubi, ai, fm_anchor); + +out_vidh: + ubi_free_vid_hdr(ubi, vidh); +out_ech: + kfree(ech); +out: + return err; +} + +#endif + +static struct ubi_attach_info *alloc_ai(const char *slab_name) +{ + struct ubi_attach_info *ai; + + ai = kzalloc(sizeof(struct ubi_attach_info), GFP_KERNEL); + if (!ai) + return ai; + + INIT_LIST_HEAD(&ai->corr); + INIT_LIST_HEAD(&ai->free); + INIT_LIST_HEAD(&ai->erase); + INIT_LIST_HEAD(&ai->alien); + ai->volumes = RB_ROOT; + ai->aeb_slab_cache = kmem_cache_create(slab_name, + sizeof(struct ubi_ainf_peb), + 0, 0, NULL); + if (!ai->aeb_slab_cache) { + kfree(ai); + ai = NULL; + } + + return ai; +} + +/** + * ubi_attach - attach an MTD device. + * @ubi: UBI device descriptor + * @force_scan: if set to non-zero attach by scanning + * + * This function returns zero in case of success and a negative error code in + * case of failure. + */ +int ubi_attach(struct ubi_device *ubi, int force_scan) +{ + int err; + struct ubi_attach_info *ai; + + ai = alloc_ai("ubi_aeb_slab_cache"); + if (!ai) + return -ENOMEM; + +#ifdef CONFIG_MTD_UBI_FASTMAP + /* On small flash devices we disable fastmap in any case. */ + if ((int)mtd_div_by_eb(ubi->mtd->size, ubi->mtd) <= UBI_FM_MAX_START) { + ubi->fm_disabled = 1; + force_scan = 1; + } + + if (force_scan) + err = scan_all(ubi, ai, 0); + else { + err = scan_fast(ubi, ai); + if (err > 0) { + if (err != UBI_NO_FASTMAP) { + destroy_ai(ai); + ai = alloc_ai("ubi_aeb_slab_cache2"); + if (!ai) + return -ENOMEM; + + err = scan_all(ubi, ai, 0); + } else { + err = scan_all(ubi, ai, UBI_FM_MAX_START); + } + } + } +#else + err = scan_all(ubi, ai, 0); +#endif + if (err) + goto out_ai; + + ubi->bad_peb_count = ai->bad_peb_count; + ubi->good_peb_count = ubi->peb_count - ubi->bad_peb_count; + ubi->corr_peb_count = ai->corr_peb_count; + ubi->max_ec = ai->max_ec; + ubi->mean_ec = ai->mean_ec; + dbg_gen("max. sequence number: %llu", ai->max_sqnum); + + err = ubi_read_volume_table(ubi, ai); + if (err) + goto out_ai; + + err = ubi_wl_init(ubi, ai); + if (err) + goto out_vtbl; + + err = ubi_eba_init(ubi, ai); + if (err) + goto out_wl; + +#ifdef CONFIG_MTD_UBI_FASTMAP + if (ubi->fm && ubi_dbg_chk_gen(ubi)) { + struct ubi_attach_info *scan_ai; + + scan_ai = alloc_ai("ubi_ckh_aeb_slab_cache"); + if (!scan_ai) { + err = -ENOMEM; + goto out_wl; + } + + err = scan_all(ubi, scan_ai, 0); + if (err) { + destroy_ai(scan_ai); + goto out_wl; + } + + err = self_check_eba(ubi, ai, scan_ai); + destroy_ai(scan_ai); + + if (err) + goto out_wl; + } +#endif + + destroy_ai(ai); + return 0; + +out_wl: + ubi_wl_close(ubi); +out_vtbl: + ubi_free_internal_volumes(ubi); + vfree(ubi->vtbl); +out_ai: + destroy_ai(ai); + return err; +} + +/** + * self_check_ai - check the attaching information. + * @ubi: UBI device description object + * @ai: attaching information + * + * This function returns zero if the attaching information is all right, and a + * negative error code if not or if an error occurred. + */ +static int self_check_ai(struct ubi_device *ubi, struct ubi_attach_info *ai) +{ + int pnum, err, vols_found = 0; + struct rb_node *rb1, *rb2; + struct ubi_ainf_volume *av; + struct ubi_ainf_peb *aeb, *last_aeb; + uint8_t *buf; + + if (!ubi_dbg_chk_gen(ubi)) + return 0; + + /* + * At first, check that attaching information is OK. + */ + ubi_rb_for_each_entry(rb1, av, &ai->volumes, rb) { + int leb_count = 0; + + cond_resched(); + + vols_found += 1; + + if (ai->is_empty) { + ubi_err("bad is_empty flag"); + goto bad_av; + } + + if (av->vol_id < 0 || av->highest_lnum < 0 || + av->leb_count < 0 || av->vol_type < 0 || av->used_ebs < 0 || + av->data_pad < 0 || av->last_data_size < 0) { + ubi_err("negative values"); + goto bad_av; + } + + if (av->vol_id >= UBI_MAX_VOLUMES && + av->vol_id < UBI_INTERNAL_VOL_START) { + ubi_err("bad vol_id"); + goto bad_av; + } + + if (av->vol_id > ai->highest_vol_id) { + ubi_err("highest_vol_id is %d, but vol_id %d is there", + ai->highest_vol_id, av->vol_id); + goto out; + } + + if (av->vol_type != UBI_DYNAMIC_VOLUME && + av->vol_type != UBI_STATIC_VOLUME) { + ubi_err("bad vol_type"); + goto bad_av; + } + + if (av->data_pad > ubi->leb_size / 2) { + ubi_err("bad data_pad"); + goto bad_av; + } + + last_aeb = NULL; + ubi_rb_for_each_entry(rb2, aeb, &av->root, u.rb) { + cond_resched(); + + last_aeb = aeb; + leb_count += 1; + + if (aeb->pnum < 0 || aeb->ec < 0) { + ubi_err("negative values"); + goto bad_aeb; + } + + if (aeb->ec < ai->min_ec) { + ubi_err("bad ai->min_ec (%d), %d found", + ai->min_ec, aeb->ec); + goto bad_aeb; + } + + if (aeb->ec > ai->max_ec) { + ubi_err("bad ai->max_ec (%d), %d found", + ai->max_ec, aeb->ec); + goto bad_aeb; + } + + if (aeb->pnum >= ubi->peb_count) { + ubi_err("too high PEB number %d, total PEBs %d", + aeb->pnum, ubi->peb_count); + goto bad_aeb; + } + + if (av->vol_type == UBI_STATIC_VOLUME) { + if (aeb->lnum >= av->used_ebs) { + ubi_err("bad lnum or used_ebs"); + goto bad_aeb; + } + } else { + if (av->used_ebs != 0) { + ubi_err("non-zero used_ebs"); + goto bad_aeb; + } + } + + if (aeb->lnum > av->highest_lnum) { + ubi_err("incorrect highest_lnum or lnum"); + goto bad_aeb; + } + } + + if (av->leb_count != leb_count) { + ubi_err("bad leb_count, %d objects in the tree", + leb_count); + goto bad_av; + } + + if (!last_aeb) + continue; + + aeb = last_aeb; + + if (aeb->lnum != av->highest_lnum) { + ubi_err("bad highest_lnum"); + goto bad_aeb; + } + } + + if (vols_found != ai->vols_found) { + ubi_err("bad ai->vols_found %d, should be %d", + ai->vols_found, vols_found); + goto out; + } + + /* Check that attaching information is correct */ + ubi_rb_for_each_entry(rb1, av, &ai->volumes, rb) { + last_aeb = NULL; + ubi_rb_for_each_entry(rb2, aeb, &av->root, u.rb) { + int vol_type; + + cond_resched(); + + last_aeb = aeb; + + err = ubi_io_read_vid_hdr(ubi, aeb->pnum, vidh, 1); + if (err && err != UBI_IO_BITFLIPS) { + ubi_err("VID header is not OK (%d)", err); + if (err > 0) + err = -EIO; + return err; + } + + vol_type = vidh->vol_type == UBI_VID_DYNAMIC ? + UBI_DYNAMIC_VOLUME : UBI_STATIC_VOLUME; + if (av->vol_type != vol_type) { + ubi_err("bad vol_type"); + goto bad_vid_hdr; + } + + if (aeb->sqnum != be64_to_cpu(vidh->sqnum)) { + ubi_err("bad sqnum %llu", aeb->sqnum); + goto bad_vid_hdr; + } + + if (av->vol_id != be32_to_cpu(vidh->vol_id)) { + ubi_err("bad vol_id %d", av->vol_id); + goto bad_vid_hdr; + } + + if (av->compat != vidh->compat) { + ubi_err("bad compat %d", vidh->compat); + goto bad_vid_hdr; + } + + if (aeb->lnum != be32_to_cpu(vidh->lnum)) { + ubi_err("bad lnum %d", aeb->lnum); + goto bad_vid_hdr; + } + + if (av->used_ebs != be32_to_cpu(vidh->used_ebs)) { + ubi_err("bad used_ebs %d", av->used_ebs); + goto bad_vid_hdr; + } + + if (av->data_pad != be32_to_cpu(vidh->data_pad)) { + ubi_err("bad data_pad %d", av->data_pad); + goto bad_vid_hdr; + } + } + + if (!last_aeb) + continue; + + if (av->highest_lnum != be32_to_cpu(vidh->lnum)) { + ubi_err("bad highest_lnum %d", av->highest_lnum); + goto bad_vid_hdr; + } + + if (av->last_data_size != be32_to_cpu(vidh->data_size)) { + ubi_err("bad last_data_size %d", av->last_data_size); + goto bad_vid_hdr; + } + } + + /* + * Make sure that all the physical eraseblocks are in one of the lists + * or trees. + */ + buf = kzalloc(ubi->peb_count, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + for (pnum = 0; pnum < ubi->peb_count; pnum++) { + err = ubi_io_is_bad(ubi, pnum); + if (err < 0) { + kfree(buf); + return err; + } else if (err) + buf[pnum] = 1; + } + + ubi_rb_for_each_entry(rb1, av, &ai->volumes, rb) + ubi_rb_for_each_entry(rb2, aeb, &av->root, u.rb) + buf[aeb->pnum] = 1; + + list_for_each_entry(aeb, &ai->free, u.list) + buf[aeb->pnum] = 1; + + list_for_each_entry(aeb, &ai->corr, u.list) + buf[aeb->pnum] = 1; + + list_for_each_entry(aeb, &ai->erase, u.list) + buf[aeb->pnum] = 1; + + list_for_each_entry(aeb, &ai->alien, u.list) + buf[aeb->pnum] = 1; + + err = 0; + for (pnum = 0; pnum < ubi->peb_count; pnum++) + if (!buf[pnum]) { + ubi_err("PEB %d is not referred", pnum); + err = 1; + } + + kfree(buf); + if (err) + goto out; + return 0; + +bad_aeb: + ubi_err("bad attaching information about LEB %d", aeb->lnum); + ubi_dump_aeb(aeb, 0); + ubi_dump_av(av); + goto out; + +bad_av: + ubi_err("bad attaching information about volume %d", av->vol_id); + ubi_dump_av(av); + goto out; + +bad_vid_hdr: + ubi_err("bad attaching information about volume %d", av->vol_id); + ubi_dump_av(av); + ubi_dump_vid_hdr(vidh); + +out: + dump_stack(); + return -EINVAL; +} diff --git a/drivers/mtd/ubi/build.c b/drivers/mtd/ubi/build.c index 6d86c0b..ff8bf0c 100644 --- a/drivers/mtd/ubi/build.c +++ b/drivers/mtd/ubi/build.c @@ -15,56 +15,88 @@ * 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> +#define __UBOOT__ +#ifndef __UBOOT__ #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/stringify.h> +#include <linux/namei.h> #include <linux/stat.h> #include <linux/miscdevice.h> #include <linux/log2.h> #include <linux/kthread.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/major.h> +#else +#include <linux/compat.h> #endif +#include <linux/err.h> #include <ubi_uboot.h> +#include <linux/mtd/partitions.h> + #include "ubi.h" +/* Maximum length of the 'mtd=' parameter */ +#define MTD_PARAM_LEN_MAX 64 + +/* Maximum number of comma-separated items in the 'mtd=' parameter */ +#define MTD_PARAM_MAX_COUNT 4 + +/* Maximum value for the number of bad PEBs per 1024 PEBs */ +#define MAX_MTD_UBI_BEB_LIMIT 768 + +#ifdef CONFIG_MTD_UBI_MODULE +#define ubi_is_module() 1 +#else +#define ubi_is_module() 0 +#endif + #if (CONFIG_SYS_MALLOC_LEN < (512 << 10)) #error Malloc area too small for UBI, increase CONFIG_SYS_MALLOC_LEN to >= 512k #endif -/* 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 + * @name: MTD character device node path, MTD device name, or MTD device number + * string * @vid_hdr_offs: VID header offset + * @max_beb_per1024: maximum expected number of bad PEBs per 1024 PEBs */ -struct mtd_dev_param -{ +struct mtd_dev_param { char name[MTD_PARAM_LEN_MAX]; + int ubi_num; int vid_hdr_offs; + int max_beb_per1024; }; /* Numbers of elements set in the @mtd_dev_param array */ -static int mtd_devs = 0; +static int __initdata mtd_devs; /* MTD devices specification parameters */ -static struct mtd_dev_param mtd_dev_param[UBI_MAX_DEVICES]; - +static struct mtd_dev_param __initdata mtd_dev_param[UBI_MAX_DEVICES]; +#ifndef __UBOOT__ +#ifdef CONFIG_MTD_UBI_FASTMAP +/* UBI module parameter to enable fastmap automatically on non-fastmap images */ +static bool fm_autoconvert; +#endif +#else +#ifdef CONFIG_MTD_UBI_FASTMAP +#if !defined(CONFIG_MTD_UBI_FASTMAP_AUTOCONVERT) +#define CONFIG_MTD_UBI_FASTMAP_AUTOCONVERT 0 +#endif +static bool fm_autoconvert = CONFIG_MTD_UBI_FASTMAP_AUTOCONVERT; +#endif +#endif /* 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; +#ifndef __UBOOT__ /* UBI control character device */ static struct miscdevice ubi_ctrl_cdev = { .minor = MISC_DYNAMIC_MINOR, @@ -74,9 +106,13 @@ static struct miscdevice ubi_ctrl_cdev = { #endif /* All UBI devices in system */ +#ifndef __UBOOT__ +static struct ubi_device *ubi_devices[UBI_MAX_DEVICES]; +#else struct ubi_device *ubi_devices[UBI_MAX_DEVICES]; +#endif -#ifdef UBI_LINUX +#ifndef __UBOOT__ /* Serializes UBI devices creations and removals */ DEFINE_MUTEX(ubi_devices_mutex); @@ -84,7 +120,8 @@ DEFINE_MUTEX(ubi_devices_mutex); 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) +static ssize_t ubi_version_show(struct class *class, + struct class_attribute *attr, char *buf) { return sprintf(buf, "%d\n", UBI_VERSION); } @@ -122,6 +159,112 @@ static struct device_attribute dev_mtd_num = #endif /** + * ubi_volume_notify - send a volume change notification. + * @ubi: UBI device description object + * @vol: volume description object of the changed volume + * @ntype: notification type to send (%UBI_VOLUME_ADDED, etc) + * + * This is a helper function which notifies all subscribers about a volume + * change event (creation, removal, re-sizing, re-naming, updating). Returns + * zero in case of success and a negative error code in case of failure. + */ +int ubi_volume_notify(struct ubi_device *ubi, struct ubi_volume *vol, int ntype) +{ + struct ubi_notification nt; + + ubi_do_get_device_info(ubi, &nt.di); + ubi_do_get_volume_info(ubi, vol, &nt.vi); + +#ifdef CONFIG_MTD_UBI_FASTMAP + switch (ntype) { + case UBI_VOLUME_ADDED: + case UBI_VOLUME_REMOVED: + case UBI_VOLUME_RESIZED: + case UBI_VOLUME_RENAMED: + if (ubi_update_fastmap(ubi)) { + ubi_err("Unable to update fastmap!"); + ubi_ro_mode(ubi); + } + } +#endif + return blocking_notifier_call_chain(&ubi_notifiers, ntype, &nt); +} + +/** + * ubi_notify_all - send a notification to all volumes. + * @ubi: UBI device description object + * @ntype: notification type to send (%UBI_VOLUME_ADDED, etc) + * @nb: the notifier to call + * + * This function walks all volumes of UBI device @ubi and sends the @ntype + * notification for each volume. If @nb is %NULL, then all registered notifiers + * are called, otherwise only the @nb notifier is called. Returns the number of + * sent notifications. + */ +int ubi_notify_all(struct ubi_device *ubi, int ntype, struct notifier_block *nb) +{ + struct ubi_notification nt; + int i, count = 0; +#ifndef __UBOOT__ + int ret; +#endif + + ubi_do_get_device_info(ubi, &nt.di); + + mutex_lock(&ubi->device_mutex); + for (i = 0; i < ubi->vtbl_slots; i++) { + /* + * Since the @ubi->device is locked, and we are not going to + * change @ubi->volumes, we do not have to lock + * @ubi->volumes_lock. + */ + if (!ubi->volumes[i]) + continue; + + ubi_do_get_volume_info(ubi, ubi->volumes[i], &nt.vi); +#ifndef __UBOOT__ + if (nb) + nb->notifier_call(nb, ntype, &nt); + else + ret = blocking_notifier_call_chain(&ubi_notifiers, ntype, + &nt); +#endif + count += 1; + } + mutex_unlock(&ubi->device_mutex); + + return count; +} + +/** + * ubi_enumerate_volumes - send "add" notification for all existing volumes. + * @nb: the notifier to call + * + * This function walks all UBI devices and volumes and sends the + * %UBI_VOLUME_ADDED notification for each volume. If @nb is %NULL, then all + * registered notifiers are called, otherwise only the @nb notifier is called. + * Returns the number of sent notifications. + */ +int ubi_enumerate_volumes(struct notifier_block *nb) +{ + int i, count = 0; + + /* + * Since the @ubi_devices_mutex is locked, and we are not going to + * change @ubi_devices, we do not have to lock @ubi_devices_lock. + */ + for (i = 0; i < UBI_MAX_DEVICES; i++) { + struct ubi_device *ubi = ubi_devices[i]; + + if (!ubi) + continue; + count += ubi_notify_all(ubi, UBI_VOLUME_ADDED, nb); + } + + return count; +} + +/** * ubi_get_device - get UBI device. * @ubi_num: UBI device number * @@ -159,8 +302,7 @@ void ubi_put_device(struct ubi_device *ubi) } /** - * ubi_get_by_major - get UBI device description object by character device - * major number. + * ubi_get_by_major - get UBI device by character device major number. * @major: major number * * This function is similar to 'ubi_get_device()', but it searches the device @@ -213,7 +355,7 @@ int ubi_major2num(int major) return ubi_num; } -#ifdef UBI_LINUX +#ifndef __UBOOT__ /* "Show" method for files in '/<sysfs>/class/ubi/ubiX/' */ static ssize_t dev_attribute_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -265,28 +407,35 @@ static ssize_t dev_attribute_show(struct device *dev, return ret; } -/* Fake "release" method for UBI devices */ -static void dev_release(struct device *dev) { } +static void dev_release(struct device *dev) +{ + struct ubi_device *ubi = container_of(dev, struct ubi_device, dev); + + kfree(ubi); +} /** * ubi_sysfs_init - initialize sysfs for an UBI device. * @ubi: UBI device description object + * @ref: set to %1 on exit in case of failure if a reference to @ubi->dev was + * taken * * 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) +static int ubi_sysfs_init(struct ubi_device *ubi, int *ref) { 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); + dev_set_name(&ubi->dev, UBI_NAME_STR"%d", ubi->ubi_num); err = device_register(&ubi->dev); if (err) return err; + *ref = 1; err = device_create_file(&ubi->dev, &dev_eraseblock_size); if (err) return err; @@ -343,7 +492,7 @@ static void ubi_sysfs_close(struct ubi_device *ubi) #endif /** - * kill_volumes - destroy all volumes. + * kill_volumes - destroy all user volumes. * @ubi: UBI device description object */ static void kill_volumes(struct ubi_device *ubi) @@ -358,17 +507,29 @@ static void kill_volumes(struct ubi_device *ubi) /** * uif_init - initialize user interfaces for an UBI device. * @ubi: UBI device description object + * @ref: set to %1 on exit in case of failure if a reference to @ubi->dev was + * taken, otherwise set to %0 + * + * This function initializes various user interfaces for an UBI device. If the + * initialization fails at an early stage, this function frees all the + * resources it allocated, returns an error, and @ref is set to %0. However, + * if the initialization fails after the UBI device was registered in the + * driver core subsystem, this function takes a reference to @ubi->dev, because + * otherwise the release function ('dev_release()') would free whole @ubi + * object. The @ref argument is set to %1 in this case. The caller has to put + * this reference. * * 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) +static int uif_init(struct ubi_device *ubi, int *ref) { int i, err; -#ifdef UBI_LINUX +#ifndef __UBOOT__ dev_t dev; #endif + *ref = 0; sprintf(ubi->ubi_name, UBI_NAME_STR "%d", ubi->ubi_num); /* @@ -387,7 +548,7 @@ static int uif_init(struct ubi_device *ubi) ubi_assert(MINOR(dev) == 0); cdev_init(&ubi->cdev, &ubi_cdev_operations); - dbg_msg("%s major is %u", ubi->ubi_name, MAJOR(dev)); + dbg_gen("%s major is %u", ubi->ubi_name, MAJOR(dev)); ubi->cdev.owner = THIS_MODULE; err = cdev_add(&ubi->cdev, dev, 1); @@ -396,7 +557,7 @@ static int uif_init(struct ubi_device *ubi) goto out_unreg; } - err = ubi_sysfs_init(ubi); + err = ubi_sysfs_init(ubi, ref); if (err) goto out_sysfs; @@ -414,6 +575,8 @@ static int uif_init(struct ubi_device *ubi) out_volumes: kill_volumes(ubi); out_sysfs: + if (*ref) + get_device(&ubi->dev); ubi_sysfs_close(ubi); cdev_del(&ubi->cdev); out_unreg: @@ -425,6 +588,10 @@ out_unreg: /** * uif_close - close user interfaces for an UBI device. * @ubi: UBI device description object + * + * Note, since this function un-registers UBI volume device objects (@vol->dev), + * the memory allocated voe the volumes is freed as well (in the release + * function). */ static void uif_close(struct ubi_device *ubi) { @@ -435,58 +602,52 @@ static void uif_close(struct ubi_device *ubi) } /** - * 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. + * ubi_free_internal_volumes - free internal volumes. + * @ubi: UBI device description object */ -static int attach_by_scanning(struct ubi_device *ubi) +void ubi_free_internal_volumes(struct ubi_device *ubi) { - int err; - struct ubi_scan_info *si; - - si = ubi_scan(ubi); - if (IS_ERR(si)) - return PTR_ERR(si); + int i; - 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; + for (i = ubi->vtbl_slots; + i < ubi->vtbl_slots + UBI_INT_VOL_COUNT; i++) { + kfree(ubi->volumes[i]->eba_tbl); + kfree(ubi->volumes[i]); + } +} - err = ubi_read_volume_table(ubi, si); - if (err) - goto out_si; +static int get_bad_peb_limit(const struct ubi_device *ubi, int max_beb_per1024) +{ + int limit, device_pebs; + uint64_t device_size; - err = ubi_eba_init_scan(ubi, si); - if (err) - goto out_vtbl; + if (!max_beb_per1024) + return 0; - err = ubi_wl_init_scan(ubi, si); - if (err) - goto out_eba; + /* + * Here we are using size of the entire flash chip and + * not just the MTD partition size because the maximum + * number of bad eraseblocks is a percentage of the + * whole device and bad eraseblocks are not fairly + * distributed over the flash chip. So the worst case + * is that all the bad eraseblocks of the chip are in + * the MTD partition we are attaching (ubi->mtd). + */ + device_size = mtd_get_device_size(ubi->mtd); + device_pebs = mtd_div_by_eb(device_size, ubi->mtd); + limit = mult_frac(device_pebs, max_beb_per1024, 1024); - ubi_scan_destroy_si(si); - return 0; + /* Round it up */ + if (mult_frac(limit, 1024, max_beb_per1024) < device_pebs) + limit += 1; -out_eba: - ubi_eba_close(ubi); -out_vtbl: - vfree(ubi->vtbl); -out_si: - ubi_scan_destroy_si(si); - return err; + return limit; } /** - * io_init - initialize I/O unit for a given UBI device. + * io_init - initialize I/O sub-system for a given UBI device. * @ubi: UBI device description object + * @max_beb_per1024: maximum expected number of bad PEB per 1024 PEBs * * If @ubi->vid_hdr_offset or @ubi->leb_start is zero, default offsets are * assumed: @@ -499,8 +660,11 @@ out_si: * 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) +static int io_init(struct ubi_device *ubi, int max_beb_per1024) { + dbg_gen("sizeof(struct ubi_ainf_peb) %zu", sizeof(struct ubi_ainf_peb)); + dbg_gen("sizeof(struct ubi_wl_entry) %zu", sizeof(struct ubi_wl_entry)); + if (ubi->mtd->numeraseregions != 0) { /* * Some flashes have several erase regions. Different regions @@ -527,8 +691,15 @@ static int io_init(struct ubi_device *ubi) ubi->peb_count = mtd_div_by_eb(ubi->mtd->size, ubi->mtd); ubi->flash_size = ubi->mtd->size; - if (mtd_can_have_bb(ubi->mtd)) + if (mtd_can_have_bb(ubi->mtd)) { ubi->bad_allowed = 1; + ubi->bad_peb_limit = get_bad_peb_limit(ubi, max_beb_per1024); + } + + if (ubi->mtd->type == MTD_NORFLASH) { + ubi_assert(ubi->mtd->writesize == 1); + ubi->nor_flash = 1; + } ubi->min_io_size = ubi->mtd->writesize; ubi->hdrs_min_io_size = ubi->mtd->writesize >> ubi->mtd->subpage_sft; @@ -548,14 +719,28 @@ static int io_init(struct ubi_device *ubi) ubi_assert(ubi->hdrs_min_io_size <= ubi->min_io_size); ubi_assert(ubi->min_io_size % ubi->hdrs_min_io_size == 0); + ubi->max_write_size = ubi->mtd->writebufsize; + /* + * Maximum write size has to be greater or equivalent to min. I/O + * size, and be multiple of min. I/O size. + */ + if (ubi->max_write_size < ubi->min_io_size || + ubi->max_write_size % ubi->min_io_size || + !is_power_of_2(ubi->max_write_size)) { + ubi_err("bad write buffer size %d for %d min. I/O unit", + ubi->max_write_size, ubi->min_io_size); + return -EINVAL; + } + /* 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); + dbg_gen("min_io_size %d", ubi->min_io_size); + dbg_gen("max_write_size %d", ubi->max_write_size); + dbg_gen("hdrs_min_io_size %d", ubi->hdrs_min_io_size); + dbg_gen("ec_hdr_alsize %d", ubi->ec_hdr_alsize); + dbg_gen("vid_hdr_alsize %d", ubi->vid_hdr_alsize); if (ubi->vid_hdr_offset == 0) /* Default offset */ @@ -569,13 +754,13 @@ static int io_init(struct ubi_device *ubi) } /* Similar for the data offset */ - ubi->leb_start = ubi->vid_hdr_offset + UBI_EC_HDR_SIZE; + ubi->leb_start = ubi->vid_hdr_offset + UBI_VID_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); + dbg_gen("vid_hdr_offset %d", ubi->vid_hdr_offset); + dbg_gen("vid_hdr_aloffset %d", ubi->vid_hdr_aloffset); + dbg_gen("vid_hdr_shift %d", ubi->vid_hdr_shift); + dbg_gen("leb_start %d", ubi->leb_start); /* The shift must be aligned to 32-bit boundary */ if (ubi->vid_hdr_shift % 4) { @@ -595,41 +780,38 @@ static int io_init(struct ubi_device *ubi) } /* + * Set maximum amount of physical erroneous eraseblocks to be 10%. + * Erroneous PEB are those which have read errors. + */ + ubi->max_erroneous = ubi->peb_count / 10; + if (ubi->max_erroneous < 16) + ubi->max_erroneous = 16; + dbg_gen("max_erroneous %d", ubi->max_erroneous); + + /* * 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_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_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 + * 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. + * each physical eraseblock. So, we leave @ubi->bad_peb_count + * uninitialized so far. */ return 0; @@ -640,7 +822,7 @@ static int io_init(struct ubi_device *ubi) * @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 + * 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. @@ -651,9 +833,14 @@ static int autoresize(struct ubi_device *ubi, int vol_id) struct ubi_volume *vol = ubi->volumes[vol_id]; int err, old_reserved_pebs = vol->reserved_pebs; + if (ubi->ro_mode) { + ubi_warn("skip auto-resize because of R/O mode"); + return 0; + } + /* * Clear the auto-resize flag in the volume in-memory copy of the - * volume table, and 'ubi_resize_volume()' will propogate this change + * volume table, and 'ubi_resize_volume()' will propagate this change * to the flash. */ ubi->vtbl[vol_id].flags &= ~UBI_VTBL_AUTORESIZE_FLG; @@ -662,11 +849,10 @@ static int autoresize(struct ubi_device *ubi, int vol_id) struct ubi_vtbl_record vtbl_rec; /* - * No avalilable PEBs to re-size the volume, clear the flag on + * No available 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)); + vtbl_rec = ubi->vtbl[vol_id]; err = ubi_change_vtbl_record(ubi, vol_id, &vtbl_rec); if (err) ubi_err("cannot clean auto-resize flag for volume %d", @@ -689,23 +875,31 @@ static int autoresize(struct ubi_device *ubi, int vol_id) /** * ubi_attach_mtd_dev - attach an MTD device. - * @mtd_dev: MTD device description object + * @mtd: MTD device description object * @ubi_num: number to assign to the new UBI device * @vid_hdr_offset: VID header offset + * @max_beb_per1024: maximum expected number of bad PEB per 1024 PEBs * * 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 + * which case this function finds a vacant device number and assigns 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) +int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num, + int vid_hdr_offset, int max_beb_per1024) { struct ubi_device *ubi; - int i, err; + int i, err, ref = 0; + + if (max_beb_per1024 < 0 || max_beb_per1024 > MAX_MTD_UBI_BEB_LIMIT) + return -EINVAL; + + if (!max_beb_per1024) + max_beb_per1024 = CONFIG_MTD_UBI_BEB_LIMIT; /* * Check if we already have the same MTD device attached. @@ -716,7 +910,7 @@ int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num, int vid_hdr_offset) 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", + ubi_err("mtd%d is already attached to ubi%d", mtd->index, i); return -EEXIST; } @@ -731,8 +925,8 @@ int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num, int vid_hdr_offset) * 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); + ubi_err("refuse attaching mtd%d - it is already emulated on top of UBI", + mtd->index); return -EINVAL; } @@ -742,7 +936,8 @@ int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num, int vid_hdr_offset) if (!ubi_devices[ubi_num]) break; if (ubi_num == UBI_MAX_DEVICES) { - dbg_err("only %d UBI devices may be created", UBI_MAX_DEVICES); + ubi_err("only %d UBI devices may be created", + UBI_MAX_DEVICES); return -ENFILE; } } else { @@ -751,7 +946,7 @@ int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num, int vid_hdr_offset) /* Make sure ubi_num is not busy */ if (ubi_devices[ubi_num]) { - dbg_err("ubi%d already exists", ubi_num); + ubi_err("ubi%d already exists", ubi_num); return -EEXIST; } } @@ -765,36 +960,61 @@ int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num, int vid_hdr_offset) ubi->vid_hdr_offset = vid_hdr_offset; ubi->autoresize_vol_id = -1; +#ifdef CONFIG_MTD_UBI_FASTMAP + ubi->fm_pool.used = ubi->fm_pool.size = 0; + ubi->fm_wl_pool.used = ubi->fm_wl_pool.size = 0; + + /* + * fm_pool.max_size is 5% of the total number of PEBs but it's also + * between UBI_FM_MAX_POOL_SIZE and UBI_FM_MIN_POOL_SIZE. + */ + ubi->fm_pool.max_size = min(((int)mtd_div_by_eb(ubi->mtd->size, + ubi->mtd) / 100) * 5, UBI_FM_MAX_POOL_SIZE); + if (ubi->fm_pool.max_size < UBI_FM_MIN_POOL_SIZE) + ubi->fm_pool.max_size = UBI_FM_MIN_POOL_SIZE; + + ubi->fm_wl_pool.max_size = UBI_FM_WL_POOL_SIZE; + ubi->fm_disabled = !fm_autoconvert; + + if (!ubi->fm_disabled && (int)mtd_div_by_eb(ubi->mtd->size, ubi->mtd) + <= UBI_FM_MAX_START) { + ubi_err("More than %i PEBs are needed for fastmap, sorry.", + UBI_FM_MAX_START); + ubi->fm_disabled = 1; + } + + ubi_msg("default fastmap pool size: %d", ubi->fm_pool.max_size); + ubi_msg("default fastmap WL pool size: %d", ubi->fm_wl_pool.max_size); +#else + ubi->fm_disabled = 1; +#endif mutex_init(&ubi->buf_mutex); mutex_init(&ubi->ckvol_mutex); - mutex_init(&ubi->volumes_mutex); + mutex_init(&ubi->device_mutex); spin_lock_init(&ubi->volumes_lock); + mutex_init(&ubi->fm_mutex); + init_rwsem(&ubi->fm_sem); ubi_msg("attaching mtd%d to ubi%d", mtd->index, ubi_num); - err = io_init(ubi); + err = io_init(ubi, max_beb_per1024); if (err) goto out_free; err = -ENOMEM; - 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) + ubi->peb_buf = vmalloc(ubi->peb_size); + if (!ubi->peb_buf) 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) +#ifdef CONFIG_MTD_UBI_FASTMAP + ubi->fm_size = ubi_calc_fm_size(ubi); + ubi->fm_buf = vzalloc(ubi->fm_size); + if (!ubi->fm_buf) goto out_free; #endif - - err = attach_by_scanning(ubi); + err = ubi_attach(ubi, 0); if (err) { - dbg_err("failed to attach by scanning, error %d", err); + ubi_err("failed to attach mtd%d, error %d", mtd->index, err); goto out_free; } @@ -804,56 +1024,71 @@ int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num, int vid_hdr_offset) goto out_detach; } - err = uif_init(ubi); + err = uif_init(ubi, &ref); if (err) goto out_detach; - ubi->bgt_thread = kthread_create(ubi_thread, ubi, ubi->bgt_name); + err = ubi_debugfs_init_dev(ubi); + if (err) + goto out_uif; + + ubi->bgt_thread = kthread_create(ubi_thread, ubi, "%s", 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; + goto out_debugfs; } - 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_msg("attached mtd%d (name \"%s\", size %llu MiB) to ubi%d", + mtd->index, mtd->name, ubi->flash_size >> 20, ubi_num); + ubi_msg("PEB size: %d bytes (%d KiB), LEB size: %d bytes", + ubi->peb_size, ubi->peb_size >> 10, ubi->leb_size); + ubi_msg("min./max. I/O unit sizes: %d/%d, sub-page size %d", + ubi->min_io_size, ubi->max_write_size, ubi->hdrs_min_io_size); + ubi_msg("VID header offset: %d (aligned %d), data offset: %d", + ubi->vid_hdr_offset, ubi->vid_hdr_aloffset, ubi->leb_start); + ubi_msg("good PEBs: %d, bad PEBs: %d, corrupted PEBs: %d", + ubi->good_peb_count, ubi->bad_peb_count, ubi->corr_peb_count); + ubi_msg("user volume: %d, internal volumes: %d, max. volumes count: %d", + ubi->vol_count - UBI_INT_VOL_COUNT, UBI_INT_VOL_COUNT, + ubi->vtbl_slots); + ubi_msg("max/mean erase counter: %d/%d, WL threshold: %d, image sequence number: %u", + ubi->max_ec, ubi->mean_ec, CONFIG_MTD_UBI_WL_THRESHOLD, + ubi->image_seq); + ubi_msg("available PEBs: %d, total reserved PEBs: %d, PEBs reserved for bad PEB handling: %d", + ubi->avail_pebs, ubi->rsvd_pebs, ubi->beb_rsvd_pebs); + + /* + * The below lock makes sure we do not race with 'ubi_thread()' which + * checks @ubi->thread_enabled. Otherwise we may fail to wake it up. + */ + spin_lock(&ubi->wl_lock); + ubi->thread_enabled = 1; + wake_up_process(ubi->bgt_thread); + spin_unlock(&ubi->wl_lock); ubi_devices[ubi_num] = ubi; + ubi_notify_all(ubi, UBI_VOLUME_ADDED, NULL); return ubi_num; +out_debugfs: + ubi_debugfs_exit_dev(ubi); out_uif: + get_device(&ubi->dev); + ubi_assert(ref); uif_close(ubi); out_detach: - ubi_eba_close(ubi); ubi_wl_close(ubi); + ubi_free_internal_volumes(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); + vfree(ubi->peb_buf); + vfree(ubi->fm_buf); + if (ref) + put_device(&ubi->dev); + else + kfree(ubi); return err; } @@ -877,13 +1112,13 @@ int ubi_detach_mtd_dev(int ubi_num, int anyway) 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); + ubi = ubi_get_device(ubi_num); + if (!ubi) return -EINVAL; - } + spin_lock(&ubi_devices_lock); + put_device(&ubi->dev); + ubi->ref_count -= 1; if (ubi->ref_count) { if (!anyway) { spin_unlock(&ubi_devices_lock); @@ -897,8 +1132,13 @@ int ubi_detach_mtd_dev(int ubi_num, int anyway) 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); - + ubi_notify_all(ubi, UBI_VOLUME_REMOVED, NULL); + ubi_msg("detaching mtd%d from ubi%d", ubi->mtd->index, ubi_num); +#ifdef CONFIG_MTD_UBI_FASTMAP + /* If we don't write a new fastmap at detach time we lose all + * EC updates that have been made since the last written fastmap. */ + ubi_update_fastmap(ubi); +#endif /* * Before freeing anything, we have to stop the background thread to * prevent it from doing anything on this device while we are freeing. @@ -906,29 +1146,73 @@ int ubi_detach_mtd_dev(int ubi_num, int anyway) if (ubi->bgt_thread) kthread_stop(ubi->bgt_thread); + /* + * Get a reference to the device in order to prevent 'dev_release()' + * from freeing the @ubi object. + */ + get_device(&ubi->dev); + + ubi_debugfs_exit_dev(ubi); uif_close(ubi); - ubi_eba_close(ubi); + ubi_wl_close(ubi); + ubi_free_internal_volumes(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 + vfree(ubi->peb_buf); + vfree(ubi->fm_buf); ubi_msg("mtd%d is detached from ubi%d", ubi->mtd->index, ubi->ubi_num); - kfree(ubi); + put_device(&ubi->dev); return 0; } +#ifndef __UBOOT__ +/** + * open_mtd_by_chdev - open an MTD device by its character device node path. + * @mtd_dev: MTD character device node path + * + * This helper function opens an MTD device by its character node device path. + * 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_by_chdev(const char *mtd_dev) +{ + int err, major, minor, mode; + struct path path; + + /* Probably this is an MTD character device node path */ + err = kern_path(mtd_dev, LOOKUP_FOLLOW, &path); + if (err) + return ERR_PTR(err); + + /* MTD device number is defined by the major / minor numbers */ + major = imajor(path.dentry->d_inode); + minor = iminor(path.dentry->d_inode); + mode = path.dentry->d_inode->i_mode; + path_put(&path); + if (major != MTD_CHAR_MAJOR || !S_ISCHR(mode)) + return ERR_PTR(-EINVAL); + + if (minor & 1) + /* + * Just do not think the "/dev/mtdrX" devices support is need, + * so do not support them to avoid doing extra work. + */ + return ERR_PTR(-EINVAL); + + return get_mtd_device(NULL, minor / 2); +} +#endif + /** - * find_mtd_device - open an MTD device by its name or number. - * @mtd_dev: name or number of the device + * open_mtd_device - open MTD device by name, character device path, or number. + * @mtd_dev: name, character device node path, or MTD device device number * * 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. + * which is first treated as ASCII MTD device number, and if it is not true, it + * is treated as MTD device name, and if that is also not true, it is treated + * as MTD character device node path. 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) { @@ -943,13 +1227,22 @@ static struct mtd_info * __init open_mtd_device(const char *mtd_dev) * MTD device name. */ mtd = get_mtd_device_nm(mtd_dev); +#ifndef __UBOOT__ + if (IS_ERR(mtd) && PTR_ERR(mtd) == -ENODEV) + /* Probably this is an MTD character device node path */ + mtd = open_mtd_by_chdev(mtd_dev); +#endif } else mtd = get_mtd_device(NULL, mtd_num); return mtd; } -int __init ubi_init(void) +#ifndef __UBOOT__ +static int __init ubi_init(void) +#else +int ubi_init(void) +#endif { int err, i, k; @@ -982,13 +1275,18 @@ int __init ubi_init(void) 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) + if (!ubi_wl_entry_slab) { + err = -ENOMEM; goto out_dev_unreg; -#endif + } + + err = ubi_debugfs_init(); + if (err) + goto out_slab; + /* Attach MTD devices */ for (i = 0; i < mtd_devs; i++) { @@ -1000,20 +1298,48 @@ int __init ubi_init(void) mtd = open_mtd_device(p->name); if (IS_ERR(mtd)) { err = PTR_ERR(mtd); - goto out_detach; + ubi_err("cannot open mtd %s, error %d", p->name, err); + /* See comment below re-ubi_is_module(). */ + if (ubi_is_module()) + goto out_detach; + continue; } mutex_lock(&ubi_devices_mutex); - err = ubi_attach_mtd_dev(mtd, UBI_DEV_NUM_AUTO, - p->vid_hdr_offs); + err = ubi_attach_mtd_dev(mtd, p->ubi_num, + p->vid_hdr_offs, p->max_beb_per1024); mutex_unlock(&ubi_devices_mutex); if (err < 0) { - put_mtd_device(mtd); ubi_err("cannot attach mtd%d", mtd->index); - goto out_detach; + put_mtd_device(mtd); + + /* + * Originally UBI stopped initializing on any error. + * However, later on it was found out that this + * behavior is not very good when UBI is compiled into + * the kernel and the MTD devices to attach are passed + * through the command line. Indeed, UBI failure + * stopped whole boot sequence. + * + * To fix this, we changed the behavior for the + * non-module case, but preserved the old behavior for + * the module case, just for compatibility. This is a + * little inconsistent, though. + */ + if (ubi_is_module()) + goto out_detach; } } + err = ubiblock_init(); + if (err) { + ubi_err("block: cannot initialize, error %d", err); + + /* See comment above re-ubi_is_module(). */ + if (ubi_is_module()) + goto out_detach; + } + return 0; out_detach: @@ -1023,43 +1349,47 @@ out_detach: ubi_detach_mtd_dev(ubi_devices[k]->ubi_num, 1); mutex_unlock(&ubi_devices_mutex); } -#ifdef UBI_LINUX + ubi_debugfs_exit(); +out_slab: 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: - mtd_devs = 0; - ubi_err("UBI error: cannot initialize UBI, error %d", err); + ubi_err("cannot initialize UBI, error %d", err); return err; } -module_init(ubi_init); +late_initcall(ubi_init); -void __exit ubi_exit(void) +#ifndef __UBOOT__ +static void __exit ubi_exit(void) +#else +void ubi_exit(void) +#endif { int i; + ubiblock_exit(); + 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); } + ubi_debugfs_exit(); kmem_cache_destroy(ubi_wl_entry_slab); misc_deregister(&ubi_ctrl_cdev); class_remove_file(ubi_class, &ubi_version); class_destroy(ubi_class); - mtd_devs = 0; } module_exit(ubi_exit); /** - * bytes_str_to_int - convert a string representing number of bytes to an - * integer. + * bytes_str_to_int - convert a number of bytes string into an integer. * @str: the string to convert * * This function returns positive resulting integer in case of success and a @@ -1071,9 +1401,8 @@ static int __init bytes_str_to_int(const char *str) 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); + if (str == endp || result >= INT_MAX) { + ubi_err("incorrect bytes count: \"%s\"\n", str); return -EINVAL; } @@ -1089,14 +1418,24 @@ static int __init bytes_str_to_int(const char *str) case '\0': break; default: - printk(KERN_ERR "UBI error: incorrect bytes count: \"%s\"\n", - str); + ubi_err("incorrect bytes count: \"%s\"\n", str); return -EINVAL; } return result; } +int kstrtoint(const char *s, unsigned int base, int *res) +{ + unsigned long long tmp; + + tmp = simple_strtoull(s, NULL, base); + if (tmp != (unsigned long long)(int)tmp) + return -ERANGE; + + return (int)tmp; +} + /** * ubi_mtd_param_parse - parse the 'mtd=' UBI parameter. * @val: the parameter value to parse @@ -1105,33 +1444,36 @@ static int __init bytes_str_to_int(const char *str) * 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) +#ifndef __UBOOT__ +static int __init ubi_mtd_param_parse(const char *val, struct kernel_param *kp) +#else +int ubi_mtd_param_parse(const char *val, struct kernel_param *kp) +#endif { int i, len; struct mtd_dev_param *p; char buf[MTD_PARAM_LEN_MAX]; char *pbuf = &buf[0]; - char *tokens[2] = {NULL, NULL}; + char *tokens[MTD_PARAM_MAX_COUNT], *token; 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); + ubi_err("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); + ubi_err("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"); + pr_warn("UBI warning: empty 'mtd=' parameter - ignored\n"); return 0; } @@ -1141,40 +1483,69 @@ int __init ubi_mtd_param_parse(const char *val, struct kernel_param *kp) if (buf[len - 1] == '\n') buf[len - 1] = '\0'; - for (i = 0; i < 2; i++) + for (i = 0; i < MTD_PARAM_MAX_COUNT; i++) tokens[i] = strsep(&pbuf, ","); if (pbuf) { - printk(KERN_ERR "UBI error: too many arguments at \"%s\"\n", - val); + ubi_err("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]); + token = tokens[1]; + if (token) { + p->vid_hdr_offs = bytes_str_to_int(token); + + if (p->vid_hdr_offs < 0) + return p->vid_hdr_offs; + } + + token = tokens[2]; + if (token) { + int err = kstrtoint(token, 10, &p->max_beb_per1024); + + if (err) { + ubi_err("bad value for max_beb_per1024 parameter: %s", + token); + return -EINVAL; + } + } - if (p->vid_hdr_offs < 0) - return p->vid_hdr_offs; + token = tokens[3]; + if (token) { + int err = kstrtoint(token, 10, &p->ubi_num); + + if (err) { + ubi_err("bad value for ubi_num parameter: %s", token); + return -EINVAL; + } + } else + p->ubi_num = UBI_DEV_NUM_AUTO; 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" +MODULE_PARM_DESC(mtd, "MTD devices to attach. Parameter format: mtd=<name|num|path>[,<vid_hdr_offs>[,max_beb_per1024[,ubi_num]]].\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."); - + "MTD devices may be specified by their number, name, or path to the MTD character device node.\n" + "Optional \"vid_hdr_offs\" parameter specifies UBI VID header position to be used by UBI. (default value if 0)\n" + "Optional \"max_beb_per1024\" parameter specifies the maximum expected bad eraseblock per 1024 eraseblocks. (default value (" + __stringify(CONFIG_MTD_UBI_BEB_LIMIT) ") if 0)\n" + "Optional \"ubi_num\" parameter specifies UBI device number which have to be assigned to the newly created UBI device (assigned automatically by default)\n" + "\n" + "Example 1: mtd=/dev/mtd0 - attach MTD device /dev/mtd0.\n" + "Example 2: 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.\n" + "Example 3: mtd=/dev/mtd1,0,25 - attach MTD device /dev/mtd1 using default VID header offset and reserve 25*nand_size_in_blocks/1024 erase blocks for bad block handling.\n" + "Example 4: mtd=/dev/mtd1,0,0,5 - attach MTD device /dev/mtd1 to UBI 5 and using default values for the other fields.\n" + "\t(e.g. if the NAND *chipset* has 4096 PEB, 100 will be reserved for this UBI device)."); +#ifdef CONFIG_MTD_UBI_FASTMAP +module_param(fm_autoconvert, bool, 0644); +MODULE_PARM_DESC(fm_autoconvert, "Set this parameter to enable fastmap automatically on images without a fastmap."); +#endif MODULE_VERSION(__stringify(UBI_VERSION)); MODULE_DESCRIPTION("UBI - Unsorted Block Images"); MODULE_AUTHOR("Artem Bityutskiy"); diff --git a/drivers/mtd/ubi/crc32.c b/drivers/mtd/ubi/crc32.c index f1bebf5..0d65bf4 100644 --- a/drivers/mtd/ubi/crc32.c +++ b/drivers/mtd/ubi/crc32.c @@ -20,7 +20,8 @@ * Version 2. See the file COPYING for more details. */ -#ifdef UBI_LINUX +#define __UBOOT__ +#ifndef __UBOOT__ #include <linux/crc32.h> #include <linux/kernel.h> #include <linux/module.h> @@ -30,7 +31,7 @@ #include <asm/byteorder.h> -#ifdef UBI_LINUX +#ifndef __UBOOT__ #include <linux/slab.h> #include <linux/init.h> #include <asm/atomic.h> @@ -46,7 +47,7 @@ #define tobe(x) (x) #endif #include "crc32table.h" -#ifdef UBI_LINUX +#ifndef __UBOOT__ MODULE_AUTHOR("Matt Domsch <Matt_Domsch@dell.com>"); MODULE_DESCRIPTION("Ethernet CRC32 calculations"); MODULE_LICENSE("GPL"); @@ -146,7 +147,7 @@ u32 crc32_le(u32 crc, unsigned char const *p, size_t len) # endif } #endif -#ifdef UBI_LINUX +#ifndef __UBOOT__ /** * crc32_be() - Calculate bitwise big-endian Ethernet AUTODIN II CRC32 * @crc: seed value for computation. ~0 for Ethernet, sometimes 0 for @@ -379,7 +380,7 @@ EXPORT_SYMBOL(crc32_be); #include <stdlib.h> #include <stdio.h> -#ifdef UBI_LINUX /*Not used at present */ +#ifndef __UBOOT__ static void buf_dump(char const *prefix, unsigned char const *buf, size_t len) { @@ -405,7 +406,7 @@ static void random_garbage(unsigned char *buf, size_t len) *buf++ = (unsigned char) random(); } -#ifdef UBI_LINUX /* Not used at present */ +#ifndef __UBOOT__ static void store_le(u32 x, unsigned char *buf) { buf[0] = (unsigned char) x; diff --git a/drivers/mtd/ubi/crc32table.h b/drivers/mtd/ubi/crc32table.h index 0438af4..02ce6fd 100644 --- a/drivers/mtd/ubi/crc32table.h +++ b/drivers/mtd/ubi/crc32table.h @@ -66,7 +66,7 @@ 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 +#ifndef __UBOOT__ static const u32 crc32table_be[] = { tobe(0x00000000L), tobe(0x04c11db7L), tobe(0x09823b6eL), tobe(0x0d4326d9L), tobe(0x130476dcL), tobe(0x17c56b6bL), tobe(0x1a864db2L), tobe(0x1e475005L), diff --git a/drivers/mtd/ubi/debug.c b/drivers/mtd/ubi/debug.c index 6c22301..af254da 100644 --- a/drivers/mtd/ubi/debug.c +++ b/drivers/mtd/ubi/debug.c @@ -6,175 +6,455 @@ * 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> +#include "ubi.h" +#define __UBOOT__ +#ifndef __UBOOT__ +#include <linux/debugfs.h> +#include <linux/uaccess.h> +#include <linux/module.h> +#endif -#ifdef CONFIG_MTD_UBI_DEBUG_MSG +/** + * ubi_dump_flash - dump a region of flash. + * @ubi: UBI device description object + * @pnum: the physical eraseblock number to dump + * @offset: the starting offset within the physical eraseblock to dump + * @len: the length of the region to dump + */ +void ubi_dump_flash(struct ubi_device *ubi, int pnum, int offset, int len) +{ + int err; + size_t read; + void *buf; + loff_t addr = (loff_t)pnum * ubi->peb_size + offset; -#include "ubi.h" + buf = vmalloc(len); + if (!buf) + return; + err = mtd_read(ubi->mtd, addr, len, &read, buf); + if (err && err != -EUCLEAN) { + ubi_err("error %d while reading %d bytes from PEB %d:%d, read %zd bytes", + err, len, pnum, offset, read); + goto out; + } + + ubi_msg("dumping %d bytes of data from PEB %d, offset %d", + len, pnum, offset); + print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET, 32, 1, buf, len, 1); +out: + vfree(buf); + return; +} /** - * ubi_dbg_dump_ec_hdr - dump an erase counter header. + * ubi_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:"); +void ubi_dump_ec_hdr(const struct ubi_ec_hdr *ec_hdr) +{ + pr_err("Erase counter header dump:\n"); + pr_err("\tmagic %#08x\n", be32_to_cpu(ec_hdr->magic)); + pr_err("\tversion %d\n", (int)ec_hdr->version); + pr_err("\tec %llu\n", (long long)be64_to_cpu(ec_hdr->ec)); + pr_err("\tvid_hdr_offset %d\n", be32_to_cpu(ec_hdr->vid_hdr_offset)); + pr_err("\tdata_offset %d\n", be32_to_cpu(ec_hdr->data_offset)); + pr_err("\timage_seq %d\n", be32_to_cpu(ec_hdr->image_seq)); + pr_err("\thdr_crc %#08x\n", be32_to_cpu(ec_hdr->hdr_crc)); + pr_err("erase counter header hexdump:\n"); 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. + * ubi_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", +void ubi_dump_vid_hdr(const struct ubi_vid_hdr *vid_hdr) +{ + pr_err("Volume identifier header dump:\n"); + pr_err("\tmagic %08x\n", be32_to_cpu(vid_hdr->magic)); + pr_err("\tversion %d\n", (int)vid_hdr->version); + pr_err("\tvol_type %d\n", (int)vid_hdr->vol_type); + pr_err("\tcopy_flag %d\n", (int)vid_hdr->copy_flag); + pr_err("\tcompat %d\n", (int)vid_hdr->compat); + pr_err("\tvol_id %d\n", be32_to_cpu(vid_hdr->vol_id)); + pr_err("\tlnum %d\n", be32_to_cpu(vid_hdr->lnum)); + pr_err("\tdata_size %d\n", be32_to_cpu(vid_hdr->data_size)); + pr_err("\tused_ebs %d\n", be32_to_cpu(vid_hdr->used_ebs)); + pr_err("\tdata_pad %d\n", be32_to_cpu(vid_hdr->data_pad)); + pr_err("\tsqnum %llu\n", (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:"); + pr_err("\thdr_crc %08x\n", be32_to_cpu(vid_hdr->hdr_crc)); + pr_err("Volume identifier header hexdump:\n"); + print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET, 32, 1, + vid_hdr, UBI_VID_HDR_SIZE, 1); } /** - * ubi_dbg_dump_vol_info- dump volume information. + * ubi_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); +void ubi_dump_vol_info(const struct ubi_volume *vol) +{ + printf("Volume information dump:\n"); + printf("\tvol_id %d\n", vol->vol_id); + printf("\treserved_pebs %d\n", vol->reserved_pebs); + printf("\talignment %d\n", vol->alignment); + printf("\tdata_pad %d\n", vol->data_pad); + printf("\tvol_type %d\n", vol->vol_type); + printf("\tname_len %d\n", vol->name_len); + printf("\tusable_leb_size %d\n", vol->usable_leb_size); + printf("\tused_ebs %d\n", vol->used_ebs); + printf("\tused_bytes %lld\n", vol->used_bytes); + printf("\tlast_eb_bytes %d\n", vol->last_eb_bytes); + printf("\tcorrupted %d\n", vol->corrupted); + printf("\tupd_marker %d\n", 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); + printf("\tname %s\n", 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]); + printf("\t1st 5 characters of name: %c%c%c%c%c\n", + 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. + * ubi_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) +void ubi_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); + pr_err("Volume table record %d dump:\n", idx); + pr_err("\treserved_pebs %d\n", be32_to_cpu(r->reserved_pebs)); + pr_err("\talignment %d\n", be32_to_cpu(r->alignment)); + pr_err("\tdata_pad %d\n", be32_to_cpu(r->data_pad)); + pr_err("\tvol_type %d\n", (int)r->vol_type); + pr_err("\tupd_marker %d\n", (int)r->upd_marker); + pr_err("\tname_len %d\n", name_len); if (r->name[0] == '\0') { - dbg_msg("name NULL"); + pr_err("\tname NULL\n"); return; } if (name_len <= UBI_VOL_NAME_MAX && strnlen(&r->name[0], name_len + 1) == name_len) { - dbg_msg("name %s", &r->name[0]); + pr_err("\tname %s\n", &r->name[0]); } else { - dbg_msg("1st 5 characters of the name: %c%c%c%c%c", + pr_err("\t1st 5 characters of name: %c%c%c%c%c\n", r->name[0], r->name[1], r->name[2], r->name[3], r->name[4]); } - dbg_msg("crc %#08x", be32_to_cpu(r->crc)); + pr_err("\tcrc %#08x\n", be32_to_cpu(r->crc)); } /** - * ubi_dbg_dump_sv - dump a &struct ubi_scan_volume object. - * @sv: the object to dump + * ubi_dump_av - dump a &struct ubi_ainf_volume object. + * @av: the object to dump */ -void ubi_dbg_dump_sv(const struct ubi_scan_volume *sv) +void ubi_dump_av(const struct ubi_ainf_volume *av) { - 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); + pr_err("Volume attaching information dump:\n"); + pr_err("\tvol_id %d\n", av->vol_id); + pr_err("\thighest_lnum %d\n", av->highest_lnum); + pr_err("\tleb_count %d\n", av->leb_count); + pr_err("\tcompat %d\n", av->compat); + pr_err("\tvol_type %d\n", av->vol_type); + pr_err("\tused_ebs %d\n", av->used_ebs); + pr_err("\tlast_data_size %d\n", av->last_data_size); + pr_err("\tdata_pad %d\n", av->data_pad); } /** - * ubi_dbg_dump_seb - dump a &struct ubi_scan_leb object. - * @seb: the object to dump + * ubi_dump_aeb - dump a &struct ubi_ainf_peb object. + * @aeb: 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) +void ubi_dump_aeb(const struct ubi_ainf_peb *aeb, int type) { - dbg_msg("eraseblock scanning information dump:"); - dbg_msg("ec %d", seb->ec); - dbg_msg("pnum %d", seb->pnum); + pr_err("eraseblock attaching information dump:\n"); + pr_err("\tec %d\n", aeb->ec); + pr_err("\tpnum %d\n", aeb->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); + pr_err("\tlnum %d\n", aeb->lnum); + pr_err("\tscrub %d\n", aeb->scrub); + pr_err("\tsqnum %llu\n", aeb->sqnum); } } /** - * ubi_dbg_dump_mkvol_req - dump a &struct ubi_mkvol_req object. + * ubi_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) +void ubi_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); + pr_err("Volume creation request dump:\n"); + pr_err("\tvol_id %d\n", req->vol_id); + pr_err("\talignment %d\n", req->alignment); + pr_err("\tbytes %lld\n", (long long)req->bytes); + pr_err("\tvol_type %d\n", req->vol_type); + pr_err("\tname_len %d\n", req->name_len); memcpy(nm, req->name, 16); nm[16] = 0; - dbg_msg("the 1st 16 characters of the name: %s", nm); + pr_err("\t1st 16 characters of name: %s\n", nm); } -#endif /* CONFIG_MTD_UBI_DEBUG_MSG */ +#ifndef __UBOOT__ +/* + * Root directory for UBI stuff in debugfs. Contains sub-directories which + * contain the stuff specific to particular UBI devices. + */ +static struct dentry *dfs_rootdir; + +/** + * ubi_debugfs_init - create UBI debugfs directory. + * + * Create UBI debugfs directory. Returns zero in case of success and a negative + * error code in case of failure. + */ +int ubi_debugfs_init(void) +{ + if (!IS_ENABLED(CONFIG_DEBUG_FS)) + return 0; + + dfs_rootdir = debugfs_create_dir("ubi", NULL); + if (IS_ERR_OR_NULL(dfs_rootdir)) { + int err = dfs_rootdir ? -ENODEV : PTR_ERR(dfs_rootdir); + + ubi_err("cannot create \"ubi\" debugfs directory, error %d\n", + err); + return err; + } + + return 0; +} + +/** + * ubi_debugfs_exit - remove UBI debugfs directory. + */ +void ubi_debugfs_exit(void) +{ + if (IS_ENABLED(CONFIG_DEBUG_FS)) + debugfs_remove(dfs_rootdir); +} + +/* Read an UBI debugfs file */ +static ssize_t dfs_file_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + unsigned long ubi_num = (unsigned long)file->private_data; + struct dentry *dent = file->f_path.dentry; + struct ubi_device *ubi; + struct ubi_debug_info *d; + char buf[3]; + int val; + + ubi = ubi_get_device(ubi_num); + if (!ubi) + return -ENODEV; + d = &ubi->dbg; + + if (dent == d->dfs_chk_gen) + val = d->chk_gen; + else if (dent == d->dfs_chk_io) + val = d->chk_io; + else if (dent == d->dfs_disable_bgt) + val = d->disable_bgt; + else if (dent == d->dfs_emulate_bitflips) + val = d->emulate_bitflips; + else if (dent == d->dfs_emulate_io_failures) + val = d->emulate_io_failures; + else { + count = -EINVAL; + goto out; + } + + if (val) + buf[0] = '1'; + else + buf[0] = '0'; + buf[1] = '\n'; + buf[2] = 0x00; + + count = simple_read_from_buffer(user_buf, count, ppos, buf, 2); + +out: + ubi_put_device(ubi); + return count; +} + +/* Write an UBI debugfs file */ +static ssize_t dfs_file_write(struct file *file, const char __user *user_buf, + size_t count, loff_t *ppos) +{ + unsigned long ubi_num = (unsigned long)file->private_data; + struct dentry *dent = file->f_path.dentry; + struct ubi_device *ubi; + struct ubi_debug_info *d; + size_t buf_size; + char buf[8]; + int val; + + ubi = ubi_get_device(ubi_num); + if (!ubi) + return -ENODEV; + d = &ubi->dbg; + + buf_size = min_t(size_t, count, (sizeof(buf) - 1)); + if (copy_from_user(buf, user_buf, buf_size)) { + count = -EFAULT; + goto out; + } + + if (buf[0] == '1') + val = 1; + else if (buf[0] == '0') + val = 0; + else { + count = -EINVAL; + goto out; + } + + if (dent == d->dfs_chk_gen) + d->chk_gen = val; + else if (dent == d->dfs_chk_io) + d->chk_io = val; + else if (dent == d->dfs_disable_bgt) + d->disable_bgt = val; + else if (dent == d->dfs_emulate_bitflips) + d->emulate_bitflips = val; + else if (dent == d->dfs_emulate_io_failures) + d->emulate_io_failures = val; + else + count = -EINVAL; + +out: + ubi_put_device(ubi); + return count; +} + +/* File operations for all UBI debugfs files */ +static const struct file_operations dfs_fops = { + .read = dfs_file_read, + .write = dfs_file_write, + .open = simple_open, + .llseek = no_llseek, + .owner = THIS_MODULE, +}; + +/** + * ubi_debugfs_init_dev - initialize debugfs for an UBI device. + * @ubi: UBI device description object + * + * This function creates all debugfs files for UBI device @ubi. Returns zero in + * case of success and a negative error code in case of failure. + */ +int ubi_debugfs_init_dev(struct ubi_device *ubi) +{ + int err, n; + unsigned long ubi_num = ubi->ubi_num; + const char *fname; + struct dentry *dent; + struct ubi_debug_info *d = &ubi->dbg; + + if (!IS_ENABLED(CONFIG_DEBUG_FS)) + return 0; + + n = snprintf(d->dfs_dir_name, UBI_DFS_DIR_LEN + 1, UBI_DFS_DIR_NAME, + ubi->ubi_num); + if (n == UBI_DFS_DIR_LEN) { + /* The array size is too small */ + fname = UBI_DFS_DIR_NAME; + dent = ERR_PTR(-EINVAL); + goto out; + } + + fname = d->dfs_dir_name; + dent = debugfs_create_dir(fname, dfs_rootdir); + if (IS_ERR_OR_NULL(dent)) + goto out; + d->dfs_dir = dent; + + fname = "chk_gen"; + dent = debugfs_create_file(fname, S_IWUSR, d->dfs_dir, (void *)ubi_num, + &dfs_fops); + if (IS_ERR_OR_NULL(dent)) + goto out_remove; + d->dfs_chk_gen = dent; + + fname = "chk_io"; + dent = debugfs_create_file(fname, S_IWUSR, d->dfs_dir, (void *)ubi_num, + &dfs_fops); + if (IS_ERR_OR_NULL(dent)) + goto out_remove; + d->dfs_chk_io = dent; + + fname = "tst_disable_bgt"; + dent = debugfs_create_file(fname, S_IWUSR, d->dfs_dir, (void *)ubi_num, + &dfs_fops); + if (IS_ERR_OR_NULL(dent)) + goto out_remove; + d->dfs_disable_bgt = dent; + + fname = "tst_emulate_bitflips"; + dent = debugfs_create_file(fname, S_IWUSR, d->dfs_dir, (void *)ubi_num, + &dfs_fops); + if (IS_ERR_OR_NULL(dent)) + goto out_remove; + d->dfs_emulate_bitflips = dent; + + fname = "tst_emulate_io_failures"; + dent = debugfs_create_file(fname, S_IWUSR, d->dfs_dir, (void *)ubi_num, + &dfs_fops); + if (IS_ERR_OR_NULL(dent)) + goto out_remove; + d->dfs_emulate_io_failures = dent; + + return 0; + +out_remove: + debugfs_remove_recursive(d->dfs_dir); +out: + err = dent ? PTR_ERR(dent) : -ENODEV; + ubi_err("cannot create \"%s\" debugfs file or directory, error %d\n", + fname, err); + return err; +} + +/** + * dbg_debug_exit_dev - free all debugfs files corresponding to device @ubi + * @ubi: UBI device description object + */ +void ubi_debugfs_exit_dev(struct ubi_device *ubi) +{ + if (IS_ENABLED(CONFIG_DEBUG_FS)) + debugfs_remove_recursive(ubi->dbg.dfs_dir); +} +#else +int ubi_debugfs_init(void) +{ + return 0; +} + +void ubi_debugfs_exit(void) +{ +} + +int ubi_debugfs_init_dev(struct ubi_device *ubi) +{ + return 0; +} + +void ubi_debugfs_exit_dev(struct ubi_device *ubi) +{ +} +#endif diff --git a/drivers/mtd/ubi/debug.h b/drivers/mtd/ubi/debug.h index 222b2b8..980eb11 100644 --- a/drivers/mtd/ubi/debug.h +++ b/drivers/mtd/ubi/debug.h @@ -9,132 +9,113 @@ #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", \ - __FUNCTION__, ##__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 +void ubi_dump_flash(struct ubi_device *ubi, int pnum, int offset, int len); +void ubi_dump_ec_hdr(const struct ubi_ec_hdr *ec_hdr); +void ubi_dump_vid_hdr(const struct ubi_vid_hdr *vid_hdr); -#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, ...) ({}) +#define __UBOOT__ +#ifndef __UBOOT__ +#include <linux/random.h> #endif -#ifdef CONFIG_MTD_UBI_DEBUG_MSG_BLD +#define ubi_assert(expr) do { \ + if (unlikely(!(expr))) { \ + pr_crit("UBI assert failed in %s at %u (pid %d)\n", \ + __func__, __LINE__, current->pid); \ + dump_stack(); \ + } \ +} while (0) + +#define ubi_dbg_print_hex_dump(l, ps, pt, r, g, b, len, a) \ + print_hex_dump(l, ps, pt, r, g, b, len, a) + +#define ubi_dbg_msg(type, fmt, ...) \ + pr_debug("UBI DBG " type " (pid %d): " fmt "\n", current->pid, \ + ##__VA_ARGS__) + +/* General debugging messages */ +#define dbg_gen(fmt, ...) ubi_dbg_msg("gen", fmt, ##__VA_ARGS__) +/* Messages from the eraseblock association sub-system */ +#define dbg_eba(fmt, ...) ubi_dbg_msg("eba", fmt, ##__VA_ARGS__) +/* Messages from the wear-leveling sub-system */ +#define dbg_wl(fmt, ...) ubi_dbg_msg("wl", fmt, ##__VA_ARGS__) +/* Messages from the input/output sub-system */ +#define dbg_io(fmt, ...) ubi_dbg_msg("io", fmt, ##__VA_ARGS__) /* Initialization and build messages */ -#define dbg_bld(fmt, ...) dbg_msg(fmt, ##__VA_ARGS__) -#else -#define dbg_bld(fmt, ...) ({}) -#endif +#define dbg_bld(fmt, ...) ubi_dbg_msg("bld", fmt, ##__VA_ARGS__) + +void ubi_dump_vol_info(const struct ubi_volume *vol); +void ubi_dump_vtbl_record(const struct ubi_vtbl_record *r, int idx); +void ubi_dump_av(const struct ubi_ainf_volume *av); +void ubi_dump_aeb(const struct ubi_ainf_peb *aeb, int type); +void ubi_dump_mkvol_req(const struct ubi_mkvol_req *req); +int ubi_self_check_all_ff(struct ubi_device *ubi, int pnum, int offset, + int len); +int ubi_debugfs_init(void); +void ubi_debugfs_exit(void); +int ubi_debugfs_init_dev(struct ubi_device *ubi); +void ubi_debugfs_exit_dev(struct ubi_device *ubi); + +/** + * ubi_dbg_is_bgt_disabled - if the background thread is disabled. + * @ubi: UBI device description object + * + * Returns non-zero if the UBI background thread is disabled for testing + * purposes. + */ +static inline int ubi_dbg_is_bgt_disabled(const struct ubi_device *ubi) +{ + return ubi->dbg.disable_bgt; +} -#ifdef CONFIG_MTD_UBI_DEBUG_EMULATE_BITFLIPS /** * ubi_dbg_is_bitflip - if it is time to emulate a bit-flip. + * @ubi: UBI device description object * * Returns non-zero if a bit-flip should be emulated, otherwise returns zero. */ -static inline int ubi_dbg_is_bitflip(void) +static inline int ubi_dbg_is_bitflip(const struct ubi_device *ubi) { - return !(random32() % 200); + if (ubi->dbg.emulate_bitflips) + return !(prandom_u32() % 200); + return 0; } -#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. + * @ubi: UBI device description object * * Returns non-zero if a write failure should be emulated, otherwise returns * zero. */ -static inline int ubi_dbg_is_write_failure(void) +static inline int ubi_dbg_is_write_failure(const struct ubi_device *ubi) { - return !(random32() % 500); + if (ubi->dbg.emulate_io_failures) + return !(prandom_u32() % 500); + return 0; } -#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. + * @ubi: UBI device description object * * Returns non-zero if an erase failure should be emulated, otherwise returns * zero. */ -static inline int ubi_dbg_is_erase_failure(void) +static inline int ubi_dbg_is_erase_failure(const struct ubi_device *ubi) { - return !(random32() % 400); + if (ubi->dbg.emulate_io_failures) + return !(prandom_u32() % 400); + return 0; +} + +static inline int ubi_dbg_chk_io(const struct ubi_device *ubi) +{ + return ubi->dbg.chk_io; } -#else -#define ubi_dbg_is_erase_failure() 0 -#endif +static inline int ubi_dbg_chk_gen(const struct ubi_device *ubi) +{ + return ubi->dbg.chk_gen; +} #endif /* !__UBI_DEBUG_H__ */ diff --git a/drivers/mtd/ubi/eba.c b/drivers/mtd/ubi/eba.c index 7d27eda..3c2a7e6 100644 --- a/drivers/mtd/ubi/eba.c +++ b/drivers/mtd/ubi/eba.c @@ -7,20 +7,20 @@ */ /* - * The UBI Eraseblock Association (EBA) unit. + * The UBI Eraseblock Association (EBA) sub-system. * - * This unit is responsible for I/O to/from logical eraseblock. + * This sub-system is responsible for I/O to/from logical eraseblock. * * Although in this implementation the EBA table is fully kept and managed in * RAM, which assumes poor scalability, it might be (partially) maintained on * flash in future implementations. * - * The EBA unit implements per-logical eraseblock locking. Before accessing a - * logical eraseblock it is locked for reading or writing. The per-logical - * eraseblock locking is implemented by means of the lock tree. The lock tree - * is an RB-tree which refers all the currently locked logical eraseblocks. The - * lock tree elements are &struct ubi_ltree_entry objects. They are indexed by - * (@vol_id, @lnum) pairs. + * The EBA sub-system implements per-logical eraseblock locking. Before + * accessing a logical eraseblock it is locked for reading or writing. The + * per-logical eraseblock locking is implemented by means of the lock tree. The + * lock tree is an RB-tree which refers all the currently locked logical + * eraseblocks. The lock tree elements are &struct ubi_ltree_entry objects. + * They are indexed by (@vol_id, @lnum) pairs. * * EBA also maintains the global sequence counter which is incremented each * time a logical eraseblock is mapped to a physical eraseblock and it is @@ -29,13 +29,15 @@ * 64 bits is enough to never overflow. */ -#ifdef UBI_LINUX +#define __UBOOT__ +#ifndef __UBOOT__ #include <linux/slab.h> #include <linux/crc32.h> -#include <linux/err.h> +#else +#include <ubi_uboot.h> #endif -#include <ubi_uboot.h> +#include <linux/err.h> #include "ubi.h" /* Number of physical eraseblocks reserved for atomic LEB change operation */ @@ -49,7 +51,7 @@ * global sequence counter value. It also increases the global sequence * counter. */ -static unsigned long long next_sqnum(struct ubi_device *ubi) +unsigned long long ubi_next_sqnum(struct ubi_device *ubi) { unsigned long long sqnum; @@ -181,9 +183,7 @@ static struct ubi_ltree_entry *ltree_add_entry(struct ubi_device *ubi, le->users += 1; spin_unlock(&ubi->ltree_lock); - if (le_free) - kfree(le_free); - + kfree(le_free); return le; } @@ -215,22 +215,18 @@ static int leb_read_lock(struct ubi_device *ubi, int vol_id, int lnum) */ static void leb_read_unlock(struct ubi_device *ubi, int vol_id, int lnum) { - int _free = 0; struct ubi_ltree_entry *le; spin_lock(&ubi->ltree_lock); le = ltree_lookup(ubi, vol_id, lnum); le->users -= 1; ubi_assert(le->users >= 0); + up_read(&le->mutex); if (le->users == 0) { rb_erase(&le->rb, &ubi->ltree); - _free = 1; + kfree(le); } spin_unlock(&ubi->ltree_lock); - - up_read(&le->mutex); - if (_free) - kfree(le); } /** @@ -266,7 +262,6 @@ static int leb_write_lock(struct ubi_device *ubi, int vol_id, int lnum) */ static int leb_write_trylock(struct ubi_device *ubi, int vol_id, int lnum) { - int _free; struct ubi_ltree_entry *le; le = ltree_add_entry(ubi, vol_id, lnum); @@ -281,12 +276,9 @@ static int leb_write_trylock(struct ubi_device *ubi, int vol_id, int lnum) ubi_assert(le->users >= 0); if (le->users == 0) { rb_erase(&le->rb, &ubi->ltree); - _free = 1; - } else - _free = 0; - spin_unlock(&ubi->ltree_lock); - if (_free) kfree(le); + } + spin_unlock(&ubi->ltree_lock); return 1; } @@ -299,23 +291,18 @@ static int leb_write_trylock(struct ubi_device *ubi, int vol_id, int lnum) */ static void leb_write_unlock(struct ubi_device *ubi, int vol_id, int lnum) { - int _free; struct ubi_ltree_entry *le; spin_lock(&ubi->ltree_lock); le = ltree_lookup(ubi, vol_id, lnum); le->users -= 1; ubi_assert(le->users >= 0); + up_write(&le->mutex); if (le->users == 0) { rb_erase(&le->rb, &ubi->ltree); - _free = 1; - } else - _free = 0; - spin_unlock(&ubi->ltree_lock); - - up_write(&le->mutex); - if (_free) kfree(le); + } + spin_unlock(&ubi->ltree_lock); } /** @@ -347,8 +334,10 @@ int ubi_eba_unmap_leb(struct ubi_device *ubi, struct ubi_volume *vol, dbg_eba("erase LEB %d:%d, PEB %d", vol_id, lnum, pnum); + down_read(&ubi->fm_sem); vol->eba_tbl[lnum] = UBI_LEB_UNMAPPED; - err = ubi_wl_put_peb(ubi, pnum, 0); + up_read(&ubi->fm_sem); + err = ubi_wl_put_peb(ubi, vol_id, lnum, pnum, 0); out_unlock: leb_write_unlock(ubi, vol_id, lnum); @@ -425,9 +414,10 @@ retry: * may try to recover data. FIXME: but this is * not implemented. */ - if (err == UBI_IO_BAD_VID_HDR) { - ubi_warn("bad VID header at PEB %d, LEB" - "%d:%d", pnum, vol_id, lnum); + if (err == UBI_IO_BAD_HDR_EBADMSG || + err == UBI_IO_BAD_HDR) { + ubi_warn("corrupted VID header at PEB %d, LEB %d:%d", + pnum, vol_id, lnum); err = -EBADMSG; } else ubi_ro_mode(ubi); @@ -508,16 +498,12 @@ static int recover_peb(struct ubi_device *ubi, int pnum, int vol_id, int lnum, struct ubi_vid_hdr *vid_hdr; vid_hdr = ubi_zalloc_vid_hdr(ubi, GFP_NOFS); - if (!vid_hdr) { + if (!vid_hdr) return -ENOMEM; - } - - mutex_lock(&ubi->buf_mutex); retry: - new_pnum = ubi_wl_get_peb(ubi, UBI_UNKNOWN); + new_pnum = ubi_wl_get_peb(ubi); if (new_pnum < 0) { - mutex_unlock(&ubi->buf_mutex); ubi_free_vid_hdr(ubi, vid_hdr); return new_pnum; } @@ -531,39 +517,45 @@ retry: goto out_put; } - vid_hdr->sqnum = cpu_to_be64(next_sqnum(ubi)); + vid_hdr->sqnum = cpu_to_be64(ubi_next_sqnum(ubi)); err = ubi_io_write_vid_hdr(ubi, new_pnum, vid_hdr); if (err) goto write_error; data_size = offset + len; - memset(ubi->peb_buf1 + offset, 0xFF, len); + mutex_lock(&ubi->buf_mutex); + memset(ubi->peb_buf + offset, 0xFF, len); /* Read everything before the area where the write failure happened */ if (offset > 0) { - err = ubi_io_read_data(ubi, ubi->peb_buf1, pnum, 0, offset); + err = ubi_io_read_data(ubi, ubi->peb_buf, pnum, 0, offset); if (err && err != UBI_IO_BITFLIPS) - goto out_put; + goto out_unlock; } - memcpy(ubi->peb_buf1 + offset, buf, len); + memcpy(ubi->peb_buf + offset, buf, len); - err = ubi_io_write_data(ubi, ubi->peb_buf1, new_pnum, 0, data_size); - if (err) + err = ubi_io_write_data(ubi, ubi->peb_buf, new_pnum, 0, data_size); + if (err) { + mutex_unlock(&ubi->buf_mutex); goto write_error; + } mutex_unlock(&ubi->buf_mutex); ubi_free_vid_hdr(ubi, vid_hdr); + down_read(&ubi->fm_sem); vol->eba_tbl[lnum] = new_pnum; - ubi_wl_put_peb(ubi, pnum, 1); + up_read(&ubi->fm_sem); + ubi_wl_put_peb(ubi, vol_id, lnum, pnum, 1); ubi_msg("data was successfully recovered"); return 0; -out_put: +out_unlock: mutex_unlock(&ubi->buf_mutex); - ubi_wl_put_peb(ubi, new_pnum, 1); +out_put: + ubi_wl_put_peb(ubi, vol_id, lnum, new_pnum, 1); ubi_free_vid_hdr(ubi, vid_hdr); return err; @@ -573,9 +565,8 @@ write_error: * get another one. */ ubi_warn("failed to write to PEB %d", new_pnum); - ubi_wl_put_peb(ubi, new_pnum, 1); + ubi_wl_put_peb(ubi, vol_id, lnum, new_pnum, 1); if (++tries > UBI_IO_RETRIES) { - mutex_unlock(&ubi->buf_mutex); ubi_free_vid_hdr(ubi, vid_hdr); return err; } @@ -591,7 +582,6 @@ write_error: * @buf: the data to write * @offset: offset within the logical eraseblock where to write * @len: how many bytes to write - * @dtype: data type * * This function writes data to logical eraseblock @lnum of a dynamic volume * @vol. Returns zero in case of success and a negative error code in case @@ -599,7 +589,7 @@ write_error: * written to the flash media, but may be some garbage. */ int ubi_eba_write_leb(struct ubi_device *ubi, struct ubi_volume *vol, int lnum, - const void *buf, int offset, int len, int dtype) + const void *buf, int offset, int len) { int err, pnum, tries = 0, vol_id = vol->vol_id; struct ubi_vid_hdr *vid_hdr; @@ -640,14 +630,14 @@ int ubi_eba_write_leb(struct ubi_device *ubi, struct ubi_volume *vol, int lnum, } vid_hdr->vol_type = UBI_VID_DYNAMIC; - vid_hdr->sqnum = cpu_to_be64(next_sqnum(ubi)); + vid_hdr->sqnum = cpu_to_be64(ubi_next_sqnum(ubi)); vid_hdr->vol_id = cpu_to_be32(vol_id); vid_hdr->lnum = cpu_to_be32(lnum); vid_hdr->compat = ubi_get_compat(ubi, vol_id); vid_hdr->data_pad = cpu_to_be32(vol->data_pad); retry: - pnum = ubi_wl_get_peb(ubi, dtype); + pnum = ubi_wl_get_peb(ubi); if (pnum < 0) { ubi_free_vid_hdr(ubi, vid_hdr); leb_write_unlock(ubi, vol_id, lnum); @@ -667,14 +657,15 @@ retry: if (len) { err = ubi_io_write_data(ubi, buf, pnum, offset, len); if (err) { - ubi_warn("failed to write %d bytes at offset %d of " - "LEB %d:%d, PEB %d", len, offset, vol_id, - lnum, pnum); + ubi_warn("failed to write %d bytes at offset %d of LEB %d:%d, PEB %d", + len, offset, vol_id, lnum, pnum); goto write_error; } } + down_read(&ubi->fm_sem); vol->eba_tbl[lnum] = pnum; + up_read(&ubi->fm_sem); leb_write_unlock(ubi, vol_id, lnum); ubi_free_vid_hdr(ubi, vid_hdr); @@ -693,7 +684,7 @@ write_error: * eraseblock, so just put it and request a new one. We assume that if * this physical eraseblock went bad, the erase code will handle that. */ - err = ubi_wl_put_peb(ubi, pnum, 1); + err = ubi_wl_put_peb(ubi, vol_id, lnum, pnum, 1); if (err || ++tries > UBI_IO_RETRIES) { ubi_ro_mode(ubi); leb_write_unlock(ubi, vol_id, lnum); @@ -701,7 +692,7 @@ write_error: return err; } - vid_hdr->sqnum = cpu_to_be64(next_sqnum(ubi)); + vid_hdr->sqnum = cpu_to_be64(ubi_next_sqnum(ubi)); ubi_msg("try another PEB"); goto retry; } @@ -713,7 +704,6 @@ write_error: * @lnum: logical eraseblock number * @buf: data to write * @len: how many bytes to write - * @dtype: data type * @used_ebs: how many logical eraseblocks will this volume contain * * This function writes data to logical eraseblock @lnum of static volume @@ -725,13 +715,12 @@ write_error: * to the real data size, although the @buf buffer has to contain the * alignment. In all other cases, @len has to be aligned. * - * It is prohibited to write more then once to logical eraseblocks of static + * It is prohibited to write more than once to logical eraseblocks of static * volumes. This function returns zero in case of success and a negative error * code in case of failure. */ int ubi_eba_write_leb_st(struct ubi_device *ubi, struct ubi_volume *vol, - int lnum, const void *buf, int len, int dtype, - int used_ebs) + int lnum, const void *buf, int len, int used_ebs) { int err, pnum, tries = 0, data_size = len, vol_id = vol->vol_id; struct ubi_vid_hdr *vid_hdr; @@ -756,7 +745,7 @@ int ubi_eba_write_leb_st(struct ubi_device *ubi, struct ubi_volume *vol, return err; } - vid_hdr->sqnum = cpu_to_be64(next_sqnum(ubi)); + vid_hdr->sqnum = cpu_to_be64(ubi_next_sqnum(ubi)); vid_hdr->vol_id = cpu_to_be32(vol_id); vid_hdr->lnum = cpu_to_be32(lnum); vid_hdr->compat = ubi_get_compat(ubi, vol_id); @@ -769,7 +758,7 @@ int ubi_eba_write_leb_st(struct ubi_device *ubi, struct ubi_volume *vol, vid_hdr->data_crc = cpu_to_be32(crc); retry: - pnum = ubi_wl_get_peb(ubi, dtype); + pnum = ubi_wl_get_peb(ubi); if (pnum < 0) { ubi_free_vid_hdr(ubi, vid_hdr); leb_write_unlock(ubi, vol_id, lnum); @@ -794,7 +783,9 @@ retry: } ubi_assert(vol->eba_tbl[lnum] < 0); + down_read(&ubi->fm_sem); vol->eba_tbl[lnum] = pnum; + up_read(&ubi->fm_sem); leb_write_unlock(ubi, vol_id, lnum); ubi_free_vid_hdr(ubi, vid_hdr); @@ -813,7 +804,7 @@ write_error: return err; } - err = ubi_wl_put_peb(ubi, pnum, 1); + err = ubi_wl_put_peb(ubi, vol_id, lnum, pnum, 1); if (err || ++tries > UBI_IO_RETRIES) { ubi_ro_mode(ubi); leb_write_unlock(ubi, vol_id, lnum); @@ -821,7 +812,7 @@ write_error: return err; } - vid_hdr->sqnum = cpu_to_be64(next_sqnum(ubi)); + vid_hdr->sqnum = cpu_to_be64(ubi_next_sqnum(ubi)); ubi_msg("try another PEB"); goto retry; } @@ -833,7 +824,6 @@ write_error: * @lnum: logical eraseblock number * @buf: data to write * @len: how many bytes to write - * @dtype: data type * * This function changes the contents of a logical eraseblock atomically. @buf * has to contain new logical eraseblock data, and @len - the length of the @@ -845,7 +835,7 @@ write_error: * LEB change may be done at a time. This is ensured by @ubi->alc_mutex. */ int ubi_eba_atomic_leb_change(struct ubi_device *ubi, struct ubi_volume *vol, - int lnum, const void *buf, int len, int dtype) + int lnum, const void *buf, int len) { int err, pnum, tries = 0, vol_id = vol->vol_id; struct ubi_vid_hdr *vid_hdr; @@ -862,7 +852,7 @@ int ubi_eba_atomic_leb_change(struct ubi_device *ubi, struct ubi_volume *vol, err = ubi_eba_unmap_leb(ubi, vol, lnum); if (err) return err; - return ubi_eba_write_leb(ubi, vol, lnum, NULL, 0, 0, dtype); + return ubi_eba_write_leb(ubi, vol, lnum, NULL, 0, 0); } vid_hdr = ubi_zalloc_vid_hdr(ubi, GFP_NOFS); @@ -874,7 +864,7 @@ int ubi_eba_atomic_leb_change(struct ubi_device *ubi, struct ubi_volume *vol, if (err) goto out_mutex; - vid_hdr->sqnum = cpu_to_be64(next_sqnum(ubi)); + vid_hdr->sqnum = cpu_to_be64(ubi_next_sqnum(ubi)); vid_hdr->vol_id = cpu_to_be32(vol_id); vid_hdr->lnum = cpu_to_be32(lnum); vid_hdr->compat = ubi_get_compat(ubi, vol_id); @@ -887,7 +877,7 @@ int ubi_eba_atomic_leb_change(struct ubi_device *ubi, struct ubi_volume *vol, vid_hdr->data_crc = cpu_to_be32(crc); retry: - pnum = ubi_wl_get_peb(ubi, dtype); + pnum = ubi_wl_get_peb(ubi); if (pnum < 0) { err = pnum; goto out_leb_unlock; @@ -911,12 +901,14 @@ retry: } if (vol->eba_tbl[lnum] >= 0) { - err = ubi_wl_put_peb(ubi, vol->eba_tbl[lnum], 1); + err = ubi_wl_put_peb(ubi, vol_id, lnum, vol->eba_tbl[lnum], 0); if (err) goto out_leb_unlock; } + down_read(&ubi->fm_sem); vol->eba_tbl[lnum] = pnum; + up_read(&ubi->fm_sem); out_leb_unlock: leb_write_unlock(ubi, vol_id, lnum); @@ -936,18 +928,45 @@ write_error: goto out_leb_unlock; } - err = ubi_wl_put_peb(ubi, pnum, 1); + err = ubi_wl_put_peb(ubi, vol_id, lnum, pnum, 1); if (err || ++tries > UBI_IO_RETRIES) { ubi_ro_mode(ubi); goto out_leb_unlock; } - vid_hdr->sqnum = cpu_to_be64(next_sqnum(ubi)); + vid_hdr->sqnum = cpu_to_be64(ubi_next_sqnum(ubi)); ubi_msg("try another PEB"); goto retry; } /** + * is_error_sane - check whether a read error is sane. + * @err: code of the error happened during reading + * + * This is a helper function for 'ubi_eba_copy_leb()' which is called when we + * cannot read data from the target PEB (an error @err happened). If the error + * code is sane, then we treat this error as non-fatal. Otherwise the error is + * fatal and UBI will be switched to R/O mode later. + * + * The idea is that we try not to switch to R/O mode if the read error is + * something which suggests there was a real read problem. E.g., %-EIO. Or a + * memory allocation failed (-%ENOMEM). Otherwise, it is safer to switch to R/O + * mode, simply because we do not know what happened at the MTD level, and we + * cannot handle this. E.g., the underlying driver may have become crazy, and + * it is safer to switch to R/O mode to preserve the data. + * + * And bear in mind, this is about reading from the target PEB, i.e. the PEB + * which we have just written. + */ +static int is_error_sane(int err) +{ + if (err == -EIO || err == -ENOMEM || err == UBI_IO_BAD_HDR || + err == UBI_IO_BAD_HDR_EBADMSG || err == -ETIMEDOUT) + return 0; + return 1; +} + +/** * ubi_eba_copy_leb - copy logical eraseblock. * @ubi: UBI device description object * @from: physical eraseblock number from where to copy @@ -957,10 +976,9 @@ write_error: * This function copies logical eraseblock from physical eraseblock @from to * physical eraseblock @to. The @vid_hdr buffer may be changed by this * function. Returns: - * o %0 in case of success; - * o %1 if the operation was canceled and should be tried later (e.g., - * because a bit-flip was detected at the target PEB); - * o %2 if the volume is being deleted and this LEB should not be moved. + * o %0 in case of success; + * o %MOVE_CANCEL_RACE, %MOVE_TARGET_WR_ERR, %MOVE_TARGET_BITFLIPS, etc; + * o a negative error code in case of failure. */ int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to, struct ubi_vid_hdr *vid_hdr) @@ -972,7 +990,7 @@ int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to, vol_id = be32_to_cpu(vid_hdr->vol_id); lnum = be32_to_cpu(vid_hdr->lnum); - dbg_eba("copy LEB %d:%d, PEB %d to PEB %d", vol_id, lnum, from, to); + dbg_wl("copy LEB %d:%d, PEB %d to PEB %d", vol_id, lnum, from, to); if (vid_hdr->vol_type == UBI_VID_STATIC) { data_size = be32_to_cpu(vid_hdr->data_size); @@ -986,17 +1004,16 @@ int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to, /* * Note, we may race with volume deletion, which means that the volume * this logical eraseblock belongs to might be being deleted. Since the - * volume deletion unmaps all the volume's logical eraseblocks, it will + * volume deletion un-maps all the volume's logical eraseblocks, it will * be locked in 'ubi_wl_put_peb()' and wait for the WL worker to finish. */ vol = ubi->volumes[idx]; + spin_unlock(&ubi->volumes_lock); if (!vol) { /* No need to do further work, cancel */ - dbg_eba("volume %d is being removed, cancel", vol_id); - spin_unlock(&ubi->volumes_lock); - return 2; + dbg_wl("volume %d is being removed, cancel", vol_id); + return MOVE_CANCEL_RACE; } - spin_unlock(&ubi->volumes_lock); /* * We do not want anybody to write to this logical eraseblock while we @@ -1008,12 +1025,15 @@ int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to, * (@from). This task locks the LEB and goes sleep in the * 'ubi_wl_put_peb()' function on the @ubi->move_mutex. In turn, we are * holding @ubi->move_mutex and go sleep on the LEB lock. So, if the - * LEB is already locked, we just do not move it and return %1. + * LEB is already locked, we just do not move it and return + * %MOVE_RETRY. Note, we do not return %MOVE_CANCEL_RACE here because + * we do not know the reasons of the contention - it may be just a + * normal I/O on this LEB, so we want to re-try. */ err = leb_write_trylock(ubi, vol_id, lnum); if (err) { - dbg_eba("contention on LEB %d:%d, cancel", vol_id, lnum); - return err; + dbg_wl("contention on LEB %d:%d, cancel", vol_id, lnum); + return MOVE_RETRY; } /* @@ -1022,30 +1042,30 @@ int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to, * cancel it. */ if (vol->eba_tbl[lnum] != from) { - dbg_eba("LEB %d:%d is no longer mapped to PEB %d, mapped to " - "PEB %d, cancel", vol_id, lnum, from, - vol->eba_tbl[lnum]); - err = 1; + dbg_wl("LEB %d:%d is no longer mapped to PEB %d, mapped to PEB %d, cancel", + vol_id, lnum, from, vol->eba_tbl[lnum]); + err = MOVE_CANCEL_RACE; goto out_unlock_leb; } /* - * OK, now the LEB is locked and we can safely start moving iy. Since - * this function utilizes thie @ubi->peb1_buf buffer which is shared - * with some other functions, so lock the buffer by taking the + * OK, now the LEB is locked and we can safely start moving it. Since + * this function utilizes the @ubi->peb_buf buffer which is shared + * with some other functions - we lock the buffer by taking the * @ubi->buf_mutex. */ mutex_lock(&ubi->buf_mutex); - dbg_eba("read %d bytes of data", aldata_size); - err = ubi_io_read_data(ubi, ubi->peb_buf1, from, 0, aldata_size); + dbg_wl("read %d bytes of data", aldata_size); + err = ubi_io_read_data(ubi, ubi->peb_buf, from, 0, aldata_size); if (err && err != UBI_IO_BITFLIPS) { ubi_warn("error %d while reading data from PEB %d", err, from); + err = MOVE_SOURCE_RD_ERR; goto out_unlock_buf; } /* - * Now we have got to calculate how much data we have to to copy. In + * Now we have got to calculate how much data we have to copy. In * case of a static volume it is fairly easy - the VID header contains * the data size. In case of a dynamic volume it is more difficult - we * have to read the contents, cut 0xFF bytes from the end and copy only @@ -1056,14 +1076,14 @@ int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to, */ if (vid_hdr->vol_type == UBI_VID_DYNAMIC) aldata_size = data_size = - ubi_calc_data_len(ubi, ubi->peb_buf1, data_size); + ubi_calc_data_len(ubi, ubi->peb_buf, data_size); cond_resched(); - crc = crc32(UBI_CRC32_INIT, ubi->peb_buf1, data_size); + crc = crc32(UBI_CRC32_INIT, ubi->peb_buf, data_size); cond_resched(); /* - * It may turn out to me that the whole @from physical eraseblock + * It may turn out to be that the whole @from physical eraseblock * contains only 0xFF bytes. Then we have to only write the VID header * and do not write any data. This also means we should not set * @vid_hdr->copy_flag, @vid_hdr->data_size, and @vid_hdr->data_crc. @@ -1073,28 +1093,37 @@ int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to, vid_hdr->data_size = cpu_to_be32(data_size); vid_hdr->data_crc = cpu_to_be32(crc); } - vid_hdr->sqnum = cpu_to_be64(next_sqnum(ubi)); + vid_hdr->sqnum = cpu_to_be64(ubi_next_sqnum(ubi)); err = ubi_io_write_vid_hdr(ubi, to, vid_hdr); - if (err) + if (err) { + if (err == -EIO) + err = MOVE_TARGET_WR_ERR; goto out_unlock_buf; + } cond_resched(); /* Read the VID header back and check if it was written correctly */ err = ubi_io_read_vid_hdr(ubi, to, vid_hdr, 1); if (err) { - if (err != UBI_IO_BITFLIPS) - ubi_warn("cannot read VID header back from PEB %d", to); - else - err = 1; + if (err != UBI_IO_BITFLIPS) { + ubi_warn("error %d while reading VID header back from PEB %d", + err, to); + if (is_error_sane(err)) + err = MOVE_TARGET_RD_ERR; + } else + err = MOVE_TARGET_BITFLIPS; goto out_unlock_buf; } if (data_size > 0) { - err = ubi_io_write_data(ubi, ubi->peb_buf1, to, 0, aldata_size); - if (err) + err = ubi_io_write_data(ubi, ubi->peb_buf, to, 0, aldata_size); + if (err) { + if (err == -EIO) + err = MOVE_TARGET_WR_ERR; goto out_unlock_buf; + } cond_resched(); @@ -1102,28 +1131,33 @@ int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to, * We've written the data and are going to read it back to make * sure it was written correctly. */ - - err = ubi_io_read_data(ubi, ubi->peb_buf2, to, 0, aldata_size); + memset(ubi->peb_buf, 0xFF, aldata_size); + err = ubi_io_read_data(ubi, ubi->peb_buf, to, 0, aldata_size); if (err) { - if (err != UBI_IO_BITFLIPS) - ubi_warn("cannot read data back from PEB %d", - to); - else - err = 1; + if (err != UBI_IO_BITFLIPS) { + ubi_warn("error %d while reading data back from PEB %d", + err, to); + if (is_error_sane(err)) + err = MOVE_TARGET_RD_ERR; + } else + err = MOVE_TARGET_BITFLIPS; goto out_unlock_buf; } cond_resched(); - if (memcmp(ubi->peb_buf1, ubi->peb_buf2, aldata_size)) { - ubi_warn("read data back from PEB %d - it is different", + if (crc != crc32(UBI_CRC32_INIT, ubi->peb_buf, data_size)) { + ubi_warn("read data back from PEB %d and it is different", to); + err = -EINVAL; goto out_unlock_buf; } } ubi_assert(vol->eba_tbl[lnum] == from); + down_read(&ubi->fm_sem); vol->eba_tbl[lnum] = to; + up_read(&ubi->fm_sem); out_unlock_buf: mutex_unlock(&ubi->buf_mutex); @@ -1133,28 +1167,165 @@ out_unlock_leb: } /** - * ubi_eba_init_scan - initialize the EBA unit using scanning information. + * print_rsvd_warning - warn about not having enough reserved PEBs. * @ubi: UBI device description object - * @si: scanning information + * + * This is a helper function for 'ubi_eba_init()' which is called when UBI + * cannot reserve enough PEBs for bad block handling. This function makes a + * decision whether we have to print a warning or not. The algorithm is as + * follows: + * o if this is a new UBI image, then just print the warning + * o if this is an UBI image which has already been used for some time, print + * a warning only if we can reserve less than 10% of the expected amount of + * the reserved PEB. + * + * The idea is that when UBI is used, PEBs become bad, and the reserved pool + * of PEBs becomes smaller, which is normal and we do not want to scare users + * with a warning every time they attach the MTD device. This was an issue + * reported by real users. + */ +static void print_rsvd_warning(struct ubi_device *ubi, + struct ubi_attach_info *ai) +{ + /* + * The 1 << 18 (256KiB) number is picked randomly, just a reasonably + * large number to distinguish between newly flashed and used images. + */ + if (ai->max_sqnum > (1 << 18)) { + int min = ubi->beb_rsvd_level / 10; + + if (!min) + min = 1; + if (ubi->beb_rsvd_pebs > min) + return; + } + + ubi_warn("cannot reserve enough PEBs for bad PEB handling, reserved %d, need %d", + ubi->beb_rsvd_pebs, ubi->beb_rsvd_level); + if (ubi->corr_peb_count) + ubi_warn("%d PEBs are corrupted and not used", + ubi->corr_peb_count); +} + +/** + * self_check_eba - run a self check on the EBA table constructed by fastmap. + * @ubi: UBI device description object + * @ai_fastmap: UBI attach info object created by fastmap + * @ai_scan: UBI attach info object created by scanning + * + * Returns < 0 in case of an internal error, 0 otherwise. + * If a bad EBA table entry was found it will be printed out and + * ubi_assert() triggers. + */ +int self_check_eba(struct ubi_device *ubi, struct ubi_attach_info *ai_fastmap, + struct ubi_attach_info *ai_scan) +{ + int i, j, num_volumes, ret = 0; + int **scan_eba, **fm_eba; + struct ubi_ainf_volume *av; + struct ubi_volume *vol; + struct ubi_ainf_peb *aeb; + struct rb_node *rb; + + num_volumes = ubi->vtbl_slots + UBI_INT_VOL_COUNT; + + scan_eba = kmalloc(sizeof(*scan_eba) * num_volumes, GFP_KERNEL); + if (!scan_eba) + return -ENOMEM; + + fm_eba = kmalloc(sizeof(*fm_eba) * num_volumes, GFP_KERNEL); + if (!fm_eba) { + kfree(scan_eba); + return -ENOMEM; + } + + for (i = 0; i < num_volumes; i++) { + vol = ubi->volumes[i]; + if (!vol) + continue; + + scan_eba[i] = kmalloc(vol->reserved_pebs * sizeof(**scan_eba), + GFP_KERNEL); + if (!scan_eba[i]) { + ret = -ENOMEM; + goto out_free; + } + + fm_eba[i] = kmalloc(vol->reserved_pebs * sizeof(**fm_eba), + GFP_KERNEL); + if (!fm_eba[i]) { + ret = -ENOMEM; + goto out_free; + } + + for (j = 0; j < vol->reserved_pebs; j++) + scan_eba[i][j] = fm_eba[i][j] = UBI_LEB_UNMAPPED; + + av = ubi_find_av(ai_scan, idx2vol_id(ubi, i)); + if (!av) + continue; + + ubi_rb_for_each_entry(rb, aeb, &av->root, u.rb) + scan_eba[i][aeb->lnum] = aeb->pnum; + + av = ubi_find_av(ai_fastmap, idx2vol_id(ubi, i)); + if (!av) + continue; + + ubi_rb_for_each_entry(rb, aeb, &av->root, u.rb) + fm_eba[i][aeb->lnum] = aeb->pnum; + + for (j = 0; j < vol->reserved_pebs; j++) { + if (scan_eba[i][j] != fm_eba[i][j]) { + if (scan_eba[i][j] == UBI_LEB_UNMAPPED || + fm_eba[i][j] == UBI_LEB_UNMAPPED) + continue; + + ubi_err("LEB:%i:%i is PEB:%i instead of %i!", + vol->vol_id, i, fm_eba[i][j], + scan_eba[i][j]); + ubi_assert(0); + } + } + } + +out_free: + for (i = 0; i < num_volumes; i++) { + if (!ubi->volumes[i]) + continue; + + kfree(scan_eba[i]); + kfree(fm_eba[i]); + } + + kfree(scan_eba); + kfree(fm_eba); + return ret; +} + +/** + * ubi_eba_init - initialize the EBA sub-system using attaching information. + * @ubi: UBI device description object + * @ai: attaching information * * This function returns zero in case of success and a negative error code in * case of failure. */ -int ubi_eba_init_scan(struct ubi_device *ubi, struct ubi_scan_info *si) +int ubi_eba_init(struct ubi_device *ubi, struct ubi_attach_info *ai) { int i, j, err, num_volumes; - struct ubi_scan_volume *sv; + struct ubi_ainf_volume *av; struct ubi_volume *vol; - struct ubi_scan_leb *seb; + struct ubi_ainf_peb *aeb; struct rb_node *rb; - dbg_eba("initialize EBA unit"); + dbg_eba("initialize EBA sub-system"); spin_lock_init(&ubi->ltree_lock); mutex_init(&ubi->alc_mutex); ubi->ltree = RB_ROOT; - ubi->global_sqnum = si->max_sqnum + 1; + ubi->global_sqnum = ai->max_sqnum + 1; num_volumes = ubi->vtbl_slots + UBI_INT_VOL_COUNT; for (i = 0; i < num_volumes; i++) { @@ -1174,24 +1345,27 @@ int ubi_eba_init_scan(struct ubi_device *ubi, struct ubi_scan_info *si) for (j = 0; j < vol->reserved_pebs; j++) vol->eba_tbl[j] = UBI_LEB_UNMAPPED; - sv = ubi_scan_find_sv(si, idx2vol_id(ubi, i)); - if (!sv) + av = ubi_find_av(ai, idx2vol_id(ubi, i)); + if (!av) continue; - ubi_rb_for_each_entry(rb, seb, &sv->root, u.rb) { - if (seb->lnum >= vol->reserved_pebs) + ubi_rb_for_each_entry(rb, aeb, &av->root, u.rb) { + if (aeb->lnum >= vol->reserved_pebs) /* * This may happen in case of an unclean reboot * during re-size. */ - ubi_scan_move_to_list(sv, seb, &si->erase); - vol->eba_tbl[seb->lnum] = seb->pnum; + ubi_move_aeb_to_list(av, aeb, &ai->erase); + vol->eba_tbl[aeb->lnum] = aeb->pnum; } } if (ubi->avail_pebs < EBA_RESERVED_PEBS) { ubi_err("no enough physical eraseblocks (%d, need %d)", ubi->avail_pebs, EBA_RESERVED_PEBS); + if (ubi->corr_peb_count) + ubi_err("%d PEBs are corrupted and not used", + ubi->corr_peb_count); err = -ENOSPC; goto out_free; } @@ -1204,9 +1378,7 @@ int ubi_eba_init_scan(struct ubi_device *ubi, struct ubi_scan_info *si) if (ubi->avail_pebs < ubi->beb_rsvd_level) { /* No enough free physical eraseblocks */ ubi->beb_rsvd_pebs = ubi->avail_pebs; - ubi_warn("cannot reserve enough PEBs for bad PEB " - "handling, reserved %d, need %d", - ubi->beb_rsvd_pebs, ubi->beb_rsvd_level); + print_rsvd_warning(ubi, ai); } else ubi->beb_rsvd_pebs = ubi->beb_rsvd_level; @@ -1214,7 +1386,7 @@ int ubi_eba_init_scan(struct ubi_device *ubi, struct ubi_scan_info *si) ubi->rsvd_pebs += ubi->beb_rsvd_pebs; } - dbg_eba("EBA unit is initialized"); + dbg_eba("EBA sub-system is initialized"); return 0; out_free: @@ -1222,23 +1394,7 @@ out_free: if (!ubi->volumes[i]) continue; kfree(ubi->volumes[i]->eba_tbl); + ubi->volumes[i]->eba_tbl = NULL; } return err; } - -/** - * ubi_eba_close - close EBA unit. - * @ubi: UBI device description object - */ -void ubi_eba_close(const struct ubi_device *ubi) -{ - int i, num_volumes = ubi->vtbl_slots + UBI_INT_VOL_COUNT; - - dbg_eba("close EBA unit"); - - for (i = 0; i < num_volumes; i++) { - if (!ubi->volumes[i]) - continue; - kfree(ubi->volumes[i]->eba_tbl); - } -} diff --git a/drivers/mtd/ubi/fastmap.c b/drivers/mtd/ubi/fastmap.c new file mode 100644 index 0000000..787522f --- /dev/null +++ b/drivers/mtd/ubi/fastmap.c @@ -0,0 +1,1584 @@ +/* + * Copyright (c) 2012 Linutronix GmbH + * Author: Richard Weinberger <richard@nod.at> + * + * SPDX-License-Identifier: GPL-2.0+ + * + */ + +#define __UBOOT__ +#ifndef __UBOOT__ +#include <linux/crc32.h> +#else +#include <div64.h> +#include <malloc.h> +#include <ubi_uboot.h> +#endif + +#include <linux/compat.h> +#include <linux/math64.h> +#include "ubi.h" + +/** + * ubi_calc_fm_size - calculates the fastmap size in bytes for an UBI device. + * @ubi: UBI device description object + */ +size_t ubi_calc_fm_size(struct ubi_device *ubi) +{ + size_t size; + + size = sizeof(struct ubi_fm_hdr) + \ + sizeof(struct ubi_fm_scan_pool) + \ + sizeof(struct ubi_fm_scan_pool) + \ + (ubi->peb_count * sizeof(struct ubi_fm_ec)) + \ + (sizeof(struct ubi_fm_eba) + \ + (ubi->peb_count * sizeof(__be32))) + \ + sizeof(struct ubi_fm_volhdr) * UBI_MAX_VOLUMES; + return roundup(size, ubi->leb_size); +} + + +/** + * new_fm_vhdr - allocate a new volume header for fastmap usage. + * @ubi: UBI device description object + * @vol_id: the VID of the new header + * + * Returns a new struct ubi_vid_hdr on success. + * NULL indicates out of memory. + */ +static struct ubi_vid_hdr *new_fm_vhdr(struct ubi_device *ubi, int vol_id) +{ + struct ubi_vid_hdr *new; + + new = ubi_zalloc_vid_hdr(ubi, GFP_KERNEL); + if (!new) + goto out; + + new->vol_type = UBI_VID_DYNAMIC; + new->vol_id = cpu_to_be32(vol_id); + + /* UBI implementations without fastmap support have to delete the + * fastmap. + */ + new->compat = UBI_COMPAT_DELETE; + +out: + return new; +} + +/** + * add_aeb - create and add a attach erase block to a given list. + * @ai: UBI attach info object + * @list: the target list + * @pnum: PEB number of the new attach erase block + * @ec: erease counter of the new LEB + * @scrub: scrub this PEB after attaching + * + * Returns 0 on success, < 0 indicates an internal error. + */ +static int add_aeb(struct ubi_attach_info *ai, struct list_head *list, + int pnum, int ec, int scrub) +{ + struct ubi_ainf_peb *aeb; + + aeb = kmem_cache_alloc(ai->aeb_slab_cache, GFP_KERNEL); + if (!aeb) + return -ENOMEM; + + aeb->pnum = pnum; + aeb->ec = ec; + aeb->lnum = -1; + aeb->scrub = scrub; + aeb->copy_flag = aeb->sqnum = 0; + + ai->ec_sum += aeb->ec; + ai->ec_count++; + + if (ai->max_ec < aeb->ec) + ai->max_ec = aeb->ec; + + if (ai->min_ec > aeb->ec) + ai->min_ec = aeb->ec; + + list_add_tail(&aeb->u.list, list); + + return 0; +} + +/** + * add_vol - create and add a new volume to ubi_attach_info. + * @ai: ubi_attach_info object + * @vol_id: VID of the new volume + * @used_ebs: number of used EBS + * @data_pad: data padding value of the new volume + * @vol_type: volume type + * @last_eb_bytes: number of bytes in the last LEB + * + * Returns the new struct ubi_ainf_volume on success. + * NULL indicates an error. + */ +static struct ubi_ainf_volume *add_vol(struct ubi_attach_info *ai, int vol_id, + int used_ebs, int data_pad, u8 vol_type, + int last_eb_bytes) +{ + struct ubi_ainf_volume *av; + struct rb_node **p = &ai->volumes.rb_node, *parent = NULL; + + while (*p) { + parent = *p; + av = rb_entry(parent, struct ubi_ainf_volume, rb); + + if (vol_id > av->vol_id) + p = &(*p)->rb_left; + else if (vol_id > av->vol_id) + p = &(*p)->rb_right; + } + + av = kmalloc(sizeof(struct ubi_ainf_volume), GFP_KERNEL); + if (!av) + goto out; + + av->highest_lnum = av->leb_count = 0; + av->vol_id = vol_id; + av->used_ebs = used_ebs; + av->data_pad = data_pad; + av->last_data_size = last_eb_bytes; + av->compat = 0; + av->vol_type = vol_type; + av->root = RB_ROOT; + + dbg_bld("found volume (ID %i)", vol_id); + + rb_link_node(&av->rb, parent, p); + rb_insert_color(&av->rb, &ai->volumes); + +out: + return av; +} + +/** + * assign_aeb_to_av - assigns a SEB to a given ainf_volume and removes it + * from it's original list. + * @ai: ubi_attach_info object + * @aeb: the to be assigned SEB + * @av: target scan volume + */ +static void assign_aeb_to_av(struct ubi_attach_info *ai, + struct ubi_ainf_peb *aeb, + struct ubi_ainf_volume *av) +{ + struct ubi_ainf_peb *tmp_aeb; + struct rb_node **p = &ai->volumes.rb_node, *parent = NULL; + + p = &av->root.rb_node; + while (*p) { + parent = *p; + + tmp_aeb = rb_entry(parent, struct ubi_ainf_peb, u.rb); + if (aeb->lnum != tmp_aeb->lnum) { + if (aeb->lnum < tmp_aeb->lnum) + p = &(*p)->rb_left; + else + p = &(*p)->rb_right; + + continue; + } else + break; + } + + list_del(&aeb->u.list); + av->leb_count++; + + rb_link_node(&aeb->u.rb, parent, p); + rb_insert_color(&aeb->u.rb, &av->root); +} + +/** + * update_vol - inserts or updates a LEB which was found a pool. + * @ubi: the UBI device object + * @ai: attach info object + * @av: the volume this LEB belongs to + * @new_vh: the volume header derived from new_aeb + * @new_aeb: the AEB to be examined + * + * Returns 0 on success, < 0 indicates an internal error. + */ +static int update_vol(struct ubi_device *ubi, struct ubi_attach_info *ai, + struct ubi_ainf_volume *av, struct ubi_vid_hdr *new_vh, + struct ubi_ainf_peb *new_aeb) +{ + struct rb_node **p = &av->root.rb_node, *parent = NULL; + struct ubi_ainf_peb *aeb, *victim; + int cmp_res; + + while (*p) { + parent = *p; + aeb = rb_entry(parent, struct ubi_ainf_peb, u.rb); + + if (be32_to_cpu(new_vh->lnum) != aeb->lnum) { + if (be32_to_cpu(new_vh->lnum) < aeb->lnum) + p = &(*p)->rb_left; + else + p = &(*p)->rb_right; + + continue; + } + + /* This case can happen if the fastmap gets written + * because of a volume change (creation, deletion, ..). + * Then a PEB can be within the persistent EBA and the pool. + */ + if (aeb->pnum == new_aeb->pnum) { + ubi_assert(aeb->lnum == new_aeb->lnum); + kmem_cache_free(ai->aeb_slab_cache, new_aeb); + + return 0; + } + + cmp_res = ubi_compare_lebs(ubi, aeb, new_aeb->pnum, new_vh); + if (cmp_res < 0) + return cmp_res; + + /* new_aeb is newer */ + if (cmp_res & 1) { + victim = kmem_cache_alloc(ai->aeb_slab_cache, + GFP_KERNEL); + if (!victim) + return -ENOMEM; + + victim->ec = aeb->ec; + victim->pnum = aeb->pnum; + list_add_tail(&victim->u.list, &ai->erase); + + if (av->highest_lnum == be32_to_cpu(new_vh->lnum)) + av->last_data_size = \ + be32_to_cpu(new_vh->data_size); + + dbg_bld("vol %i: AEB %i's PEB %i is the newer", + av->vol_id, aeb->lnum, new_aeb->pnum); + + aeb->ec = new_aeb->ec; + aeb->pnum = new_aeb->pnum; + aeb->copy_flag = new_vh->copy_flag; + aeb->scrub = new_aeb->scrub; + kmem_cache_free(ai->aeb_slab_cache, new_aeb); + + /* new_aeb is older */ + } else { + dbg_bld("vol %i: AEB %i's PEB %i is old, dropping it", + av->vol_id, aeb->lnum, new_aeb->pnum); + list_add_tail(&new_aeb->u.list, &ai->erase); + } + + return 0; + } + /* This LEB is new, let's add it to the volume */ + + if (av->highest_lnum <= be32_to_cpu(new_vh->lnum)) { + av->highest_lnum = be32_to_cpu(new_vh->lnum); + av->last_data_size = be32_to_cpu(new_vh->data_size); + } + + if (av->vol_type == UBI_STATIC_VOLUME) + av->used_ebs = be32_to_cpu(new_vh->used_ebs); + + av->leb_count++; + + rb_link_node(&new_aeb->u.rb, parent, p); + rb_insert_color(&new_aeb->u.rb, &av->root); + + return 0; +} + +/** + * process_pool_aeb - we found a non-empty PEB in a pool. + * @ubi: UBI device object + * @ai: attach info object + * @new_vh: the volume header derived from new_aeb + * @new_aeb: the AEB to be examined + * + * Returns 0 on success, < 0 indicates an internal error. + */ +static int process_pool_aeb(struct ubi_device *ubi, struct ubi_attach_info *ai, + struct ubi_vid_hdr *new_vh, + struct ubi_ainf_peb *new_aeb) +{ + struct ubi_ainf_volume *av, *tmp_av = NULL; + struct rb_node **p = &ai->volumes.rb_node, *parent = NULL; + int found = 0; + + if (be32_to_cpu(new_vh->vol_id) == UBI_FM_SB_VOLUME_ID || + be32_to_cpu(new_vh->vol_id) == UBI_FM_DATA_VOLUME_ID) { + kmem_cache_free(ai->aeb_slab_cache, new_aeb); + + return 0; + } + + /* Find the volume this SEB belongs to */ + while (*p) { + parent = *p; + tmp_av = rb_entry(parent, struct ubi_ainf_volume, rb); + + if (be32_to_cpu(new_vh->vol_id) > tmp_av->vol_id) + p = &(*p)->rb_left; + else if (be32_to_cpu(new_vh->vol_id) < tmp_av->vol_id) + p = &(*p)->rb_right; + else { + found = 1; + break; + } + } + + if (found) + av = tmp_av; + else { + ubi_err("orphaned volume in fastmap pool!"); + return UBI_BAD_FASTMAP; + } + + ubi_assert(be32_to_cpu(new_vh->vol_id) == av->vol_id); + + return update_vol(ubi, ai, av, new_vh, new_aeb); +} + +/** + * unmap_peb - unmap a PEB. + * If fastmap detects a free PEB in the pool it has to check whether + * this PEB has been unmapped after writing the fastmap. + * + * @ai: UBI attach info object + * @pnum: The PEB to be unmapped + */ +static void unmap_peb(struct ubi_attach_info *ai, int pnum) +{ + struct ubi_ainf_volume *av; + struct rb_node *node, *node2; + struct ubi_ainf_peb *aeb; + + for (node = rb_first(&ai->volumes); node; node = rb_next(node)) { + av = rb_entry(node, struct ubi_ainf_volume, rb); + + for (node2 = rb_first(&av->root); node2; + node2 = rb_next(node2)) { + aeb = rb_entry(node2, struct ubi_ainf_peb, u.rb); + if (aeb->pnum == pnum) { + rb_erase(&aeb->u.rb, &av->root); + kmem_cache_free(ai->aeb_slab_cache, aeb); + return; + } + } + } +} + +/** + * scan_pool - scans a pool for changed (no longer empty PEBs). + * @ubi: UBI device object + * @ai: attach info object + * @pebs: an array of all PEB numbers in the to be scanned pool + * @pool_size: size of the pool (number of entries in @pebs) + * @max_sqnum: pointer to the maximal sequence number + * @eba_orphans: list of PEBs which need to be scanned + * @free: list of PEBs which are most likely free (and go into @ai->free) + * + * Returns 0 on success, if the pool is unusable UBI_BAD_FASTMAP is returned. + * < 0 indicates an internal error. + */ +#ifndef __UBOOT__ +static int scan_pool(struct ubi_device *ubi, struct ubi_attach_info *ai, + int *pebs, int pool_size, unsigned long long *max_sqnum, + struct list_head *eba_orphans, struct list_head *freef) +#else +static int scan_pool(struct ubi_device *ubi, struct ubi_attach_info *ai, + __be32 *pebs, int pool_size, unsigned long long *max_sqnum, + struct list_head *eba_orphans, struct list_head *freef) +#endif +{ + struct ubi_vid_hdr *vh; + struct ubi_ec_hdr *ech; + struct ubi_ainf_peb *new_aeb, *tmp_aeb; + int i, pnum, err, found_orphan, ret = 0; + + ech = kzalloc(ubi->ec_hdr_alsize, GFP_KERNEL); + if (!ech) + return -ENOMEM; + + vh = ubi_zalloc_vid_hdr(ubi, GFP_KERNEL); + if (!vh) { + kfree(ech); + return -ENOMEM; + } + + dbg_bld("scanning fastmap pool: size = %i", pool_size); + + /* + * Now scan all PEBs in the pool to find changes which have been made + * after the creation of the fastmap + */ + for (i = 0; i < pool_size; i++) { + int scrub = 0; + int image_seq; + + pnum = be32_to_cpu(pebs[i]); + + if (ubi_io_is_bad(ubi, pnum)) { + ubi_err("bad PEB in fastmap pool!"); + ret = UBI_BAD_FASTMAP; + goto out; + } + + err = ubi_io_read_ec_hdr(ubi, pnum, ech, 0); + if (err && err != UBI_IO_BITFLIPS) { + ubi_err("unable to read EC header! PEB:%i err:%i", + pnum, err); + ret = err > 0 ? UBI_BAD_FASTMAP : err; + goto out; + } else if (ret == UBI_IO_BITFLIPS) + scrub = 1; + + /* + * Older UBI implementations have image_seq set to zero, so + * we shouldn't fail if image_seq == 0. + */ + image_seq = be32_to_cpu(ech->image_seq); + + if (image_seq && (image_seq != ubi->image_seq)) { + ubi_err("bad image seq: 0x%x, expected: 0x%x", + be32_to_cpu(ech->image_seq), ubi->image_seq); + ret = UBI_BAD_FASTMAP; + goto out; + } + + err = ubi_io_read_vid_hdr(ubi, pnum, vh, 0); + if (err == UBI_IO_FF || err == UBI_IO_FF_BITFLIPS) { + unsigned long long ec = be64_to_cpu(ech->ec); + unmap_peb(ai, pnum); + dbg_bld("Adding PEB to free: %i", pnum); + if (err == UBI_IO_FF_BITFLIPS) + add_aeb(ai, freef, pnum, ec, 1); + else + add_aeb(ai, freef, pnum, ec, 0); + continue; + } else if (err == 0 || err == UBI_IO_BITFLIPS) { + dbg_bld("Found non empty PEB:%i in pool", pnum); + + if (err == UBI_IO_BITFLIPS) + scrub = 1; + + found_orphan = 0; + list_for_each_entry(tmp_aeb, eba_orphans, u.list) { + if (tmp_aeb->pnum == pnum) { + found_orphan = 1; + break; + } + } + if (found_orphan) { + list_del(&tmp_aeb->u.list); + kmem_cache_free(ai->aeb_slab_cache, tmp_aeb); + } + + new_aeb = kmem_cache_alloc(ai->aeb_slab_cache, + GFP_KERNEL); + if (!new_aeb) { + ret = -ENOMEM; + goto out; + } + + new_aeb->ec = be64_to_cpu(ech->ec); + new_aeb->pnum = pnum; + new_aeb->lnum = be32_to_cpu(vh->lnum); + new_aeb->sqnum = be64_to_cpu(vh->sqnum); + new_aeb->copy_flag = vh->copy_flag; + new_aeb->scrub = scrub; + + if (*max_sqnum < new_aeb->sqnum) + *max_sqnum = new_aeb->sqnum; + + err = process_pool_aeb(ubi, ai, vh, new_aeb); + if (err) { + ret = err > 0 ? UBI_BAD_FASTMAP : err; + goto out; + } + } else { + /* We are paranoid and fall back to scanning mode */ + ubi_err("fastmap pool PEBs contains damaged PEBs!"); + ret = err > 0 ? UBI_BAD_FASTMAP : err; + goto out; + } + + } + +out: + ubi_free_vid_hdr(ubi, vh); + kfree(ech); + return ret; +} + +/** + * count_fastmap_pebs - Counts the PEBs found by fastmap. + * @ai: The UBI attach info object + */ +static int count_fastmap_pebs(struct ubi_attach_info *ai) +{ + struct ubi_ainf_peb *aeb; + struct ubi_ainf_volume *av; + struct rb_node *rb1, *rb2; + int n = 0; + + list_for_each_entry(aeb, &ai->erase, u.list) + n++; + + list_for_each_entry(aeb, &ai->free, u.list) + n++; + + ubi_rb_for_each_entry(rb1, av, &ai->volumes, rb) + ubi_rb_for_each_entry(rb2, aeb, &av->root, u.rb) + n++; + + return n; +} + +/** + * ubi_attach_fastmap - creates ubi_attach_info from a fastmap. + * @ubi: UBI device object + * @ai: UBI attach info object + * @fm: the fastmap to be attached + * + * Returns 0 on success, UBI_BAD_FASTMAP if the found fastmap was unusable. + * < 0 indicates an internal error. + */ +static int ubi_attach_fastmap(struct ubi_device *ubi, + struct ubi_attach_info *ai, + struct ubi_fastmap_layout *fm) +{ + struct list_head used, eba_orphans, freef; + struct ubi_ainf_volume *av; + struct ubi_ainf_peb *aeb, *tmp_aeb, *_tmp_aeb; + struct ubi_ec_hdr *ech; + struct ubi_fm_sb *fmsb; + struct ubi_fm_hdr *fmhdr; + struct ubi_fm_scan_pool *fmpl1, *fmpl2; + struct ubi_fm_ec *fmec; + struct ubi_fm_volhdr *fmvhdr; + struct ubi_fm_eba *fm_eba; + int ret, i, j, pool_size, wl_pool_size; + size_t fm_pos = 0, fm_size = ubi->fm_size; + unsigned long long max_sqnum = 0; + void *fm_raw = ubi->fm_buf; + + INIT_LIST_HEAD(&used); + INIT_LIST_HEAD(&freef); + INIT_LIST_HEAD(&eba_orphans); + INIT_LIST_HEAD(&ai->corr); + INIT_LIST_HEAD(&ai->free); + INIT_LIST_HEAD(&ai->erase); + INIT_LIST_HEAD(&ai->alien); + ai->volumes = RB_ROOT; + ai->min_ec = UBI_MAX_ERASECOUNTER; + + ai->aeb_slab_cache = kmem_cache_create("ubi_ainf_peb_slab", + sizeof(struct ubi_ainf_peb), + 0, 0, NULL); + if (!ai->aeb_slab_cache) { + ret = -ENOMEM; + goto fail; + } + + fmsb = (struct ubi_fm_sb *)(fm_raw); + ai->max_sqnum = fmsb->sqnum; + fm_pos += sizeof(struct ubi_fm_sb); + if (fm_pos >= fm_size) + goto fail_bad; + + fmhdr = (struct ubi_fm_hdr *)(fm_raw + fm_pos); + fm_pos += sizeof(*fmhdr); + if (fm_pos >= fm_size) + goto fail_bad; + + if (be32_to_cpu(fmhdr->magic) != UBI_FM_HDR_MAGIC) { + ubi_err("bad fastmap header magic: 0x%x, expected: 0x%x", + be32_to_cpu(fmhdr->magic), UBI_FM_HDR_MAGIC); + goto fail_bad; + } + + fmpl1 = (struct ubi_fm_scan_pool *)(fm_raw + fm_pos); + fm_pos += sizeof(*fmpl1); + if (fm_pos >= fm_size) + goto fail_bad; + if (be32_to_cpu(fmpl1->magic) != UBI_FM_POOL_MAGIC) { + ubi_err("bad fastmap pool magic: 0x%x, expected: 0x%x", + be32_to_cpu(fmpl1->magic), UBI_FM_POOL_MAGIC); + goto fail_bad; + } + + fmpl2 = (struct ubi_fm_scan_pool *)(fm_raw + fm_pos); + fm_pos += sizeof(*fmpl2); + if (fm_pos >= fm_size) + goto fail_bad; + if (be32_to_cpu(fmpl2->magic) != UBI_FM_POOL_MAGIC) { + ubi_err("bad fastmap pool magic: 0x%x, expected: 0x%x", + be32_to_cpu(fmpl2->magic), UBI_FM_POOL_MAGIC); + goto fail_bad; + } + + pool_size = be16_to_cpu(fmpl1->size); + wl_pool_size = be16_to_cpu(fmpl2->size); + fm->max_pool_size = be16_to_cpu(fmpl1->max_size); + fm->max_wl_pool_size = be16_to_cpu(fmpl2->max_size); + + if (pool_size > UBI_FM_MAX_POOL_SIZE || pool_size < 0) { + ubi_err("bad pool size: %i", pool_size); + goto fail_bad; + } + + if (wl_pool_size > UBI_FM_MAX_POOL_SIZE || wl_pool_size < 0) { + ubi_err("bad WL pool size: %i", wl_pool_size); + goto fail_bad; + } + + + if (fm->max_pool_size > UBI_FM_MAX_POOL_SIZE || + fm->max_pool_size < 0) { + ubi_err("bad maximal pool size: %i", fm->max_pool_size); + goto fail_bad; + } + + if (fm->max_wl_pool_size > UBI_FM_MAX_POOL_SIZE || + fm->max_wl_pool_size < 0) { + ubi_err("bad maximal WL pool size: %i", fm->max_wl_pool_size); + goto fail_bad; + } + + /* read EC values from free list */ + for (i = 0; i < be32_to_cpu(fmhdr->free_peb_count); i++) { + fmec = (struct ubi_fm_ec *)(fm_raw + fm_pos); + fm_pos += sizeof(*fmec); + if (fm_pos >= fm_size) + goto fail_bad; + + add_aeb(ai, &ai->free, be32_to_cpu(fmec->pnum), + be32_to_cpu(fmec->ec), 0); + } + + /* read EC values from used list */ + for (i = 0; i < be32_to_cpu(fmhdr->used_peb_count); i++) { + fmec = (struct ubi_fm_ec *)(fm_raw + fm_pos); + fm_pos += sizeof(*fmec); + if (fm_pos >= fm_size) + goto fail_bad; + + add_aeb(ai, &used, be32_to_cpu(fmec->pnum), + be32_to_cpu(fmec->ec), 0); + } + + /* read EC values from scrub list */ + for (i = 0; i < be32_to_cpu(fmhdr->scrub_peb_count); i++) { + fmec = (struct ubi_fm_ec *)(fm_raw + fm_pos); + fm_pos += sizeof(*fmec); + if (fm_pos >= fm_size) + goto fail_bad; + + add_aeb(ai, &used, be32_to_cpu(fmec->pnum), + be32_to_cpu(fmec->ec), 1); + } + + /* read EC values from erase list */ + for (i = 0; i < be32_to_cpu(fmhdr->erase_peb_count); i++) { + fmec = (struct ubi_fm_ec *)(fm_raw + fm_pos); + fm_pos += sizeof(*fmec); + if (fm_pos >= fm_size) + goto fail_bad; + + add_aeb(ai, &ai->erase, be32_to_cpu(fmec->pnum), + be32_to_cpu(fmec->ec), 1); + } + + ai->mean_ec = div_u64(ai->ec_sum, ai->ec_count); + ai->bad_peb_count = be32_to_cpu(fmhdr->bad_peb_count); + + /* Iterate over all volumes and read their EBA table */ + for (i = 0; i < be32_to_cpu(fmhdr->vol_count); i++) { + fmvhdr = (struct ubi_fm_volhdr *)(fm_raw + fm_pos); + fm_pos += sizeof(*fmvhdr); + if (fm_pos >= fm_size) + goto fail_bad; + + if (be32_to_cpu(fmvhdr->magic) != UBI_FM_VHDR_MAGIC) { + ubi_err("bad fastmap vol header magic: 0x%x, " \ + "expected: 0x%x", + be32_to_cpu(fmvhdr->magic), UBI_FM_VHDR_MAGIC); + goto fail_bad; + } + + av = add_vol(ai, be32_to_cpu(fmvhdr->vol_id), + be32_to_cpu(fmvhdr->used_ebs), + be32_to_cpu(fmvhdr->data_pad), + fmvhdr->vol_type, + be32_to_cpu(fmvhdr->last_eb_bytes)); + + if (!av) + goto fail_bad; + + ai->vols_found++; + if (ai->highest_vol_id < be32_to_cpu(fmvhdr->vol_id)) + ai->highest_vol_id = be32_to_cpu(fmvhdr->vol_id); + + fm_eba = (struct ubi_fm_eba *)(fm_raw + fm_pos); + fm_pos += sizeof(*fm_eba); + fm_pos += (sizeof(__be32) * be32_to_cpu(fm_eba->reserved_pebs)); + if (fm_pos >= fm_size) + goto fail_bad; + + if (be32_to_cpu(fm_eba->magic) != UBI_FM_EBA_MAGIC) { + ubi_err("bad fastmap EBA header magic: 0x%x, " \ + "expected: 0x%x", + be32_to_cpu(fm_eba->magic), UBI_FM_EBA_MAGIC); + goto fail_bad; + } + + for (j = 0; j < be32_to_cpu(fm_eba->reserved_pebs); j++) { + int pnum = be32_to_cpu(fm_eba->pnum[j]); + + if ((int)be32_to_cpu(fm_eba->pnum[j]) < 0) + continue; + + aeb = NULL; + list_for_each_entry(tmp_aeb, &used, u.list) { + if (tmp_aeb->pnum == pnum) { + aeb = tmp_aeb; + break; + } + } + + /* This can happen if a PEB is already in an EBA known + * by this fastmap but the PEB itself is not in the used + * list. + * In this case the PEB can be within the fastmap pool + * or while writing the fastmap it was in the protection + * queue. + */ + if (!aeb) { + aeb = kmem_cache_alloc(ai->aeb_slab_cache, + GFP_KERNEL); + if (!aeb) { + ret = -ENOMEM; + + goto fail; + } + + aeb->lnum = j; + aeb->pnum = be32_to_cpu(fm_eba->pnum[j]); + aeb->ec = -1; + aeb->scrub = aeb->copy_flag = aeb->sqnum = 0; + list_add_tail(&aeb->u.list, &eba_orphans); + continue; + } + + aeb->lnum = j; + + if (av->highest_lnum <= aeb->lnum) + av->highest_lnum = aeb->lnum; + + assign_aeb_to_av(ai, aeb, av); + + dbg_bld("inserting PEB:%i (LEB %i) to vol %i", + aeb->pnum, aeb->lnum, av->vol_id); + } + + ech = kzalloc(ubi->ec_hdr_alsize, GFP_KERNEL); + if (!ech) { + ret = -ENOMEM; + goto fail; + } + + list_for_each_entry_safe(tmp_aeb, _tmp_aeb, &eba_orphans, + u.list) { + int err; + + if (ubi_io_is_bad(ubi, tmp_aeb->pnum)) { + ubi_err("bad PEB in fastmap EBA orphan list"); + ret = UBI_BAD_FASTMAP; + kfree(ech); + goto fail; + } + + err = ubi_io_read_ec_hdr(ubi, tmp_aeb->pnum, ech, 0); + if (err && err != UBI_IO_BITFLIPS) { + ubi_err("unable to read EC header! PEB:%i " \ + "err:%i", tmp_aeb->pnum, err); + ret = err > 0 ? UBI_BAD_FASTMAP : err; + kfree(ech); + + goto fail; + } else if (err == UBI_IO_BITFLIPS) + tmp_aeb->scrub = 1; + + tmp_aeb->ec = be64_to_cpu(ech->ec); + assign_aeb_to_av(ai, tmp_aeb, av); + } + + kfree(ech); + } + + ret = scan_pool(ubi, ai, fmpl1->pebs, pool_size, &max_sqnum, + &eba_orphans, &freef); + if (ret) + goto fail; + + ret = scan_pool(ubi, ai, fmpl2->pebs, wl_pool_size, &max_sqnum, + &eba_orphans, &freef); + if (ret) + goto fail; + + if (max_sqnum > ai->max_sqnum) + ai->max_sqnum = max_sqnum; + + list_for_each_entry_safe(tmp_aeb, _tmp_aeb, &freef, u.list) + list_move_tail(&tmp_aeb->u.list, &ai->free); + + ubi_assert(list_empty(&used)); + ubi_assert(list_empty(&eba_orphans)); + ubi_assert(list_empty(&freef)); + + /* + * If fastmap is leaking PEBs (must not happen), raise a + * fat warning and fall back to scanning mode. + * We do this here because in ubi_wl_init() it's too late + * and we cannot fall back to scanning. + */ +#ifndef __UBOOT__ + if (WARN_ON(count_fastmap_pebs(ai) != ubi->peb_count - + ai->bad_peb_count - fm->used_blocks)) + goto fail_bad; +#else + if (count_fastmap_pebs(ai) != ubi->peb_count - + ai->bad_peb_count - fm->used_blocks) { + WARN_ON(1); + goto fail_bad; + } +#endif + + return 0; + +fail_bad: + ret = UBI_BAD_FASTMAP; +fail: + list_for_each_entry_safe(tmp_aeb, _tmp_aeb, &used, u.list) { + list_del(&tmp_aeb->u.list); + kmem_cache_free(ai->aeb_slab_cache, tmp_aeb); + } + list_for_each_entry_safe(tmp_aeb, _tmp_aeb, &eba_orphans, u.list) { + list_del(&tmp_aeb->u.list); + kmem_cache_free(ai->aeb_slab_cache, tmp_aeb); + } + list_for_each_entry_safe(tmp_aeb, _tmp_aeb, &freef, u.list) { + list_del(&tmp_aeb->u.list); + kmem_cache_free(ai->aeb_slab_cache, tmp_aeb); + } + + return ret; +} + +/** + * ubi_scan_fastmap - scan the fastmap. + * @ubi: UBI device object + * @ai: UBI attach info to be filled + * @fm_anchor: The fastmap starts at this PEB + * + * Returns 0 on success, UBI_NO_FASTMAP if no fastmap was found, + * UBI_BAD_FASTMAP if one was found but is not usable. + * < 0 indicates an internal error. + */ +int ubi_scan_fastmap(struct ubi_device *ubi, struct ubi_attach_info *ai, + int fm_anchor) +{ + struct ubi_fm_sb *fmsb, *fmsb2; + struct ubi_vid_hdr *vh; + struct ubi_ec_hdr *ech; + struct ubi_fastmap_layout *fm; + int i, used_blocks, pnum, ret = 0; + size_t fm_size; + __be32 crc, tmp_crc; + unsigned long long sqnum = 0; + + mutex_lock(&ubi->fm_mutex); + memset(ubi->fm_buf, 0, ubi->fm_size); + + fmsb = kmalloc(sizeof(*fmsb), GFP_KERNEL); + if (!fmsb) { + ret = -ENOMEM; + goto out; + } + + fm = kzalloc(sizeof(*fm), GFP_KERNEL); + if (!fm) { + ret = -ENOMEM; + kfree(fmsb); + goto out; + } + + ret = ubi_io_read(ubi, fmsb, fm_anchor, ubi->leb_start, sizeof(*fmsb)); + if (ret && ret != UBI_IO_BITFLIPS) + goto free_fm_sb; + else if (ret == UBI_IO_BITFLIPS) + fm->to_be_tortured[0] = 1; + + if (be32_to_cpu(fmsb->magic) != UBI_FM_SB_MAGIC) { + ubi_err("bad super block magic: 0x%x, expected: 0x%x", + be32_to_cpu(fmsb->magic), UBI_FM_SB_MAGIC); + ret = UBI_BAD_FASTMAP; + goto free_fm_sb; + } + + if (fmsb->version != UBI_FM_FMT_VERSION) { + ubi_err("bad fastmap version: %i, expected: %i", + fmsb->version, UBI_FM_FMT_VERSION); + ret = UBI_BAD_FASTMAP; + goto free_fm_sb; + } + + used_blocks = be32_to_cpu(fmsb->used_blocks); + if (used_blocks > UBI_FM_MAX_BLOCKS || used_blocks < 1) { + ubi_err("number of fastmap blocks is invalid: %i", used_blocks); + ret = UBI_BAD_FASTMAP; + goto free_fm_sb; + } + + fm_size = ubi->leb_size * used_blocks; + if (fm_size != ubi->fm_size) { + ubi_err("bad fastmap size: %zi, expected: %zi", fm_size, + ubi->fm_size); + ret = UBI_BAD_FASTMAP; + goto free_fm_sb; + } + + ech = kzalloc(ubi->ec_hdr_alsize, GFP_KERNEL); + if (!ech) { + ret = -ENOMEM; + goto free_fm_sb; + } + + vh = ubi_zalloc_vid_hdr(ubi, GFP_KERNEL); + if (!vh) { + ret = -ENOMEM; + goto free_hdr; + } + + for (i = 0; i < used_blocks; i++) { + int image_seq; + + pnum = be32_to_cpu(fmsb->block_loc[i]); + + if (ubi_io_is_bad(ubi, pnum)) { + ret = UBI_BAD_FASTMAP; + goto free_hdr; + } + + ret = ubi_io_read_ec_hdr(ubi, pnum, ech, 0); + if (ret && ret != UBI_IO_BITFLIPS) { + ubi_err("unable to read fastmap block# %i EC (PEB: %i)", + i, pnum); + if (ret > 0) + ret = UBI_BAD_FASTMAP; + goto free_hdr; + } else if (ret == UBI_IO_BITFLIPS) + fm->to_be_tortured[i] = 1; + + image_seq = be32_to_cpu(ech->image_seq); + if (!ubi->image_seq) + ubi->image_seq = image_seq; + + /* + * Older UBI implementations have image_seq set to zero, so + * we shouldn't fail if image_seq == 0. + */ + if (image_seq && (image_seq != ubi->image_seq)) { + ubi_err("wrong image seq:%d instead of %d", + be32_to_cpu(ech->image_seq), ubi->image_seq); + ret = UBI_BAD_FASTMAP; + goto free_hdr; + } + + ret = ubi_io_read_vid_hdr(ubi, pnum, vh, 0); + if (ret && ret != UBI_IO_BITFLIPS) { + ubi_err("unable to read fastmap block# %i (PEB: %i)", + i, pnum); + goto free_hdr; + } + + if (i == 0) { + if (be32_to_cpu(vh->vol_id) != UBI_FM_SB_VOLUME_ID) { + ubi_err("bad fastmap anchor vol_id: 0x%x," \ + " expected: 0x%x", + be32_to_cpu(vh->vol_id), + UBI_FM_SB_VOLUME_ID); + ret = UBI_BAD_FASTMAP; + goto free_hdr; + } + } else { + if (be32_to_cpu(vh->vol_id) != UBI_FM_DATA_VOLUME_ID) { + ubi_err("bad fastmap data vol_id: 0x%x," \ + " expected: 0x%x", + be32_to_cpu(vh->vol_id), + UBI_FM_DATA_VOLUME_ID); + ret = UBI_BAD_FASTMAP; + goto free_hdr; + } + } + + if (sqnum < be64_to_cpu(vh->sqnum)) + sqnum = be64_to_cpu(vh->sqnum); + + ret = ubi_io_read(ubi, ubi->fm_buf + (ubi->leb_size * i), pnum, + ubi->leb_start, ubi->leb_size); + if (ret && ret != UBI_IO_BITFLIPS) { + ubi_err("unable to read fastmap block# %i (PEB: %i, " \ + "err: %i)", i, pnum, ret); + goto free_hdr; + } + } + + kfree(fmsb); + fmsb = NULL; + + fmsb2 = (struct ubi_fm_sb *)(ubi->fm_buf); + tmp_crc = be32_to_cpu(fmsb2->data_crc); + fmsb2->data_crc = 0; + crc = crc32(UBI_CRC32_INIT, ubi->fm_buf, fm_size); + if (crc != tmp_crc) { + ubi_err("fastmap data CRC is invalid"); + ubi_err("CRC should be: 0x%x, calc: 0x%x", tmp_crc, crc); + ret = UBI_BAD_FASTMAP; + goto free_hdr; + } + + fmsb2->sqnum = sqnum; + + fm->used_blocks = used_blocks; + + ret = ubi_attach_fastmap(ubi, ai, fm); + if (ret) { + if (ret > 0) + ret = UBI_BAD_FASTMAP; + goto free_hdr; + } + + for (i = 0; i < used_blocks; i++) { + struct ubi_wl_entry *e; + + e = kmem_cache_alloc(ubi_wl_entry_slab, GFP_KERNEL); + if (!e) { + while (i--) + kfree(fm->e[i]); + + ret = -ENOMEM; + goto free_hdr; + } + + e->pnum = be32_to_cpu(fmsb2->block_loc[i]); + e->ec = be32_to_cpu(fmsb2->block_ec[i]); + fm->e[i] = e; + } + + ubi->fm = fm; + ubi->fm_pool.max_size = ubi->fm->max_pool_size; + ubi->fm_wl_pool.max_size = ubi->fm->max_wl_pool_size; + ubi_msg("attached by fastmap"); + ubi_msg("fastmap pool size: %d", ubi->fm_pool.max_size); + ubi_msg("fastmap WL pool size: %d", ubi->fm_wl_pool.max_size); + ubi->fm_disabled = 0; + + ubi_free_vid_hdr(ubi, vh); + kfree(ech); +out: + mutex_unlock(&ubi->fm_mutex); + if (ret == UBI_BAD_FASTMAP) + ubi_err("Attach by fastmap failed, doing a full scan!"); + return ret; + +free_hdr: + ubi_free_vid_hdr(ubi, vh); + kfree(ech); +free_fm_sb: + kfree(fmsb); + kfree(fm); + goto out; +} + +/** + * ubi_write_fastmap - writes a fastmap. + * @ubi: UBI device object + * @new_fm: the to be written fastmap + * + * Returns 0 on success, < 0 indicates an internal error. + */ +static int ubi_write_fastmap(struct ubi_device *ubi, + struct ubi_fastmap_layout *new_fm) +{ + size_t fm_pos = 0; + void *fm_raw; + struct ubi_fm_sb *fmsb; + struct ubi_fm_hdr *fmh; + struct ubi_fm_scan_pool *fmpl1, *fmpl2; + struct ubi_fm_ec *fec; + struct ubi_fm_volhdr *fvh; + struct ubi_fm_eba *feba; + struct rb_node *node; + struct ubi_wl_entry *wl_e; + struct ubi_volume *vol; + struct ubi_vid_hdr *avhdr, *dvhdr; + struct ubi_work *ubi_wrk; + int ret, i, j, free_peb_count, used_peb_count, vol_count; + int scrub_peb_count, erase_peb_count; + + fm_raw = ubi->fm_buf; + memset(ubi->fm_buf, 0, ubi->fm_size); + + avhdr = new_fm_vhdr(ubi, UBI_FM_SB_VOLUME_ID); + if (!avhdr) { + ret = -ENOMEM; + goto out; + } + + dvhdr = new_fm_vhdr(ubi, UBI_FM_DATA_VOLUME_ID); + if (!dvhdr) { + ret = -ENOMEM; + goto out_kfree; + } + + spin_lock(&ubi->volumes_lock); + spin_lock(&ubi->wl_lock); + + fmsb = (struct ubi_fm_sb *)fm_raw; + fm_pos += sizeof(*fmsb); + ubi_assert(fm_pos <= ubi->fm_size); + + fmh = (struct ubi_fm_hdr *)(fm_raw + fm_pos); + fm_pos += sizeof(*fmh); + ubi_assert(fm_pos <= ubi->fm_size); + + fmsb->magic = cpu_to_be32(UBI_FM_SB_MAGIC); + fmsb->version = UBI_FM_FMT_VERSION; + fmsb->used_blocks = cpu_to_be32(new_fm->used_blocks); + /* the max sqnum will be filled in while *reading* the fastmap */ + fmsb->sqnum = 0; + + fmh->magic = cpu_to_be32(UBI_FM_HDR_MAGIC); + free_peb_count = 0; + used_peb_count = 0; + scrub_peb_count = 0; + erase_peb_count = 0; + vol_count = 0; + + fmpl1 = (struct ubi_fm_scan_pool *)(fm_raw + fm_pos); + fm_pos += sizeof(*fmpl1); + fmpl1->magic = cpu_to_be32(UBI_FM_POOL_MAGIC); + fmpl1->size = cpu_to_be16(ubi->fm_pool.size); + fmpl1->max_size = cpu_to_be16(ubi->fm_pool.max_size); + + for (i = 0; i < ubi->fm_pool.size; i++) + fmpl1->pebs[i] = cpu_to_be32(ubi->fm_pool.pebs[i]); + + fmpl2 = (struct ubi_fm_scan_pool *)(fm_raw + fm_pos); + fm_pos += sizeof(*fmpl2); + fmpl2->magic = cpu_to_be32(UBI_FM_POOL_MAGIC); + fmpl2->size = cpu_to_be16(ubi->fm_wl_pool.size); + fmpl2->max_size = cpu_to_be16(ubi->fm_wl_pool.max_size); + + for (i = 0; i < ubi->fm_wl_pool.size; i++) + fmpl2->pebs[i] = cpu_to_be32(ubi->fm_wl_pool.pebs[i]); + + for (node = rb_first(&ubi->free); node; node = rb_next(node)) { + wl_e = rb_entry(node, struct ubi_wl_entry, u.rb); + fec = (struct ubi_fm_ec *)(fm_raw + fm_pos); + + fec->pnum = cpu_to_be32(wl_e->pnum); + fec->ec = cpu_to_be32(wl_e->ec); + + free_peb_count++; + fm_pos += sizeof(*fec); + ubi_assert(fm_pos <= ubi->fm_size); + } + fmh->free_peb_count = cpu_to_be32(free_peb_count); + + for (node = rb_first(&ubi->used); node; node = rb_next(node)) { + wl_e = rb_entry(node, struct ubi_wl_entry, u.rb); + fec = (struct ubi_fm_ec *)(fm_raw + fm_pos); + + fec->pnum = cpu_to_be32(wl_e->pnum); + fec->ec = cpu_to_be32(wl_e->ec); + + used_peb_count++; + fm_pos += sizeof(*fec); + ubi_assert(fm_pos <= ubi->fm_size); + } + fmh->used_peb_count = cpu_to_be32(used_peb_count); + + for (node = rb_first(&ubi->scrub); node; node = rb_next(node)) { + wl_e = rb_entry(node, struct ubi_wl_entry, u.rb); + fec = (struct ubi_fm_ec *)(fm_raw + fm_pos); + + fec->pnum = cpu_to_be32(wl_e->pnum); + fec->ec = cpu_to_be32(wl_e->ec); + + scrub_peb_count++; + fm_pos += sizeof(*fec); + ubi_assert(fm_pos <= ubi->fm_size); + } + fmh->scrub_peb_count = cpu_to_be32(scrub_peb_count); + + + list_for_each_entry(ubi_wrk, &ubi->works, list) { + if (ubi_is_erase_work(ubi_wrk)) { + wl_e = ubi_wrk->e; + ubi_assert(wl_e); + + fec = (struct ubi_fm_ec *)(fm_raw + fm_pos); + + fec->pnum = cpu_to_be32(wl_e->pnum); + fec->ec = cpu_to_be32(wl_e->ec); + + erase_peb_count++; + fm_pos += sizeof(*fec); + ubi_assert(fm_pos <= ubi->fm_size); + } + } + fmh->erase_peb_count = cpu_to_be32(erase_peb_count); + + for (i = 0; i < UBI_MAX_VOLUMES + UBI_INT_VOL_COUNT; i++) { + vol = ubi->volumes[i]; + + if (!vol) + continue; + + vol_count++; + + fvh = (struct ubi_fm_volhdr *)(fm_raw + fm_pos); + fm_pos += sizeof(*fvh); + ubi_assert(fm_pos <= ubi->fm_size); + + fvh->magic = cpu_to_be32(UBI_FM_VHDR_MAGIC); + fvh->vol_id = cpu_to_be32(vol->vol_id); + fvh->vol_type = vol->vol_type; + fvh->used_ebs = cpu_to_be32(vol->used_ebs); + fvh->data_pad = cpu_to_be32(vol->data_pad); + fvh->last_eb_bytes = cpu_to_be32(vol->last_eb_bytes); + + ubi_assert(vol->vol_type == UBI_DYNAMIC_VOLUME || + vol->vol_type == UBI_STATIC_VOLUME); + + feba = (struct ubi_fm_eba *)(fm_raw + fm_pos); + fm_pos += sizeof(*feba) + (sizeof(__be32) * vol->reserved_pebs); + ubi_assert(fm_pos <= ubi->fm_size); + + for (j = 0; j < vol->reserved_pebs; j++) + feba->pnum[j] = cpu_to_be32(vol->eba_tbl[j]); + + feba->reserved_pebs = cpu_to_be32(j); + feba->magic = cpu_to_be32(UBI_FM_EBA_MAGIC); + } + fmh->vol_count = cpu_to_be32(vol_count); + fmh->bad_peb_count = cpu_to_be32(ubi->bad_peb_count); + + avhdr->sqnum = cpu_to_be64(ubi_next_sqnum(ubi)); + avhdr->lnum = 0; + + spin_unlock(&ubi->wl_lock); + spin_unlock(&ubi->volumes_lock); + + dbg_bld("writing fastmap SB to PEB %i", new_fm->e[0]->pnum); + ret = ubi_io_write_vid_hdr(ubi, new_fm->e[0]->pnum, avhdr); + if (ret) { + ubi_err("unable to write vid_hdr to fastmap SB!"); + goto out_kfree; + } + + for (i = 0; i < new_fm->used_blocks; i++) { + fmsb->block_loc[i] = cpu_to_be32(new_fm->e[i]->pnum); + fmsb->block_ec[i] = cpu_to_be32(new_fm->e[i]->ec); + } + + fmsb->data_crc = 0; + fmsb->data_crc = cpu_to_be32(crc32(UBI_CRC32_INIT, fm_raw, + ubi->fm_size)); + + for (i = 1; i < new_fm->used_blocks; i++) { + dvhdr->sqnum = cpu_to_be64(ubi_next_sqnum(ubi)); + dvhdr->lnum = cpu_to_be32(i); + dbg_bld("writing fastmap data to PEB %i sqnum %llu", + new_fm->e[i]->pnum, be64_to_cpu(dvhdr->sqnum)); + ret = ubi_io_write_vid_hdr(ubi, new_fm->e[i]->pnum, dvhdr); + if (ret) { + ubi_err("unable to write vid_hdr to PEB %i!", + new_fm->e[i]->pnum); + goto out_kfree; + } + } + + for (i = 0; i < new_fm->used_blocks; i++) { + ret = ubi_io_write(ubi, fm_raw + (i * ubi->leb_size), + new_fm->e[i]->pnum, ubi->leb_start, ubi->leb_size); + if (ret) { + ubi_err("unable to write fastmap to PEB %i!", + new_fm->e[i]->pnum); + goto out_kfree; + } + } + + ubi_assert(new_fm); + ubi->fm = new_fm; + + dbg_bld("fastmap written!"); + +out_kfree: + ubi_free_vid_hdr(ubi, avhdr); + ubi_free_vid_hdr(ubi, dvhdr); +out: + return ret; +} + +/** + * erase_block - Manually erase a PEB. + * @ubi: UBI device object + * @pnum: PEB to be erased + * + * Returns the new EC value on success, < 0 indicates an internal error. + */ +static int erase_block(struct ubi_device *ubi, int pnum) +{ + int ret; + struct ubi_ec_hdr *ec_hdr; + long long ec; + + ec_hdr = kzalloc(ubi->ec_hdr_alsize, GFP_KERNEL); + if (!ec_hdr) + return -ENOMEM; + + ret = ubi_io_read_ec_hdr(ubi, pnum, ec_hdr, 0); + if (ret < 0) + goto out; + else if (ret && ret != UBI_IO_BITFLIPS) { + ret = -EINVAL; + goto out; + } + + ret = ubi_io_sync_erase(ubi, pnum, 0); + if (ret < 0) + goto out; + + ec = be64_to_cpu(ec_hdr->ec); + ec += ret; + if (ec > UBI_MAX_ERASECOUNTER) { + ret = -EINVAL; + goto out; + } + + ec_hdr->ec = cpu_to_be64(ec); + ret = ubi_io_write_ec_hdr(ubi, pnum, ec_hdr); + if (ret < 0) + goto out; + + ret = ec; +out: + kfree(ec_hdr); + return ret; +} + +/** + * invalidate_fastmap - destroys a fastmap. + * @ubi: UBI device object + * @fm: the fastmap to be destroyed + * + * Returns 0 on success, < 0 indicates an internal error. + */ +static int invalidate_fastmap(struct ubi_device *ubi, + struct ubi_fastmap_layout *fm) +{ + int ret; + struct ubi_vid_hdr *vh; + + ret = erase_block(ubi, fm->e[0]->pnum); + if (ret < 0) + return ret; + + vh = new_fm_vhdr(ubi, UBI_FM_SB_VOLUME_ID); + if (!vh) + return -ENOMEM; + + /* deleting the current fastmap SB is not enough, an old SB may exist, + * so create a (corrupted) SB such that fastmap will find it and fall + * back to scanning mode in any case */ + vh->sqnum = cpu_to_be64(ubi_next_sqnum(ubi)); + ret = ubi_io_write_vid_hdr(ubi, fm->e[0]->pnum, vh); + + return ret; +} + +/** + * ubi_update_fastmap - will be called by UBI if a volume changes or + * a fastmap pool becomes full. + * @ubi: UBI device object + * + * Returns 0 on success, < 0 indicates an internal error. + */ +int ubi_update_fastmap(struct ubi_device *ubi) +{ + int ret, i; + struct ubi_fastmap_layout *new_fm, *old_fm; + struct ubi_wl_entry *tmp_e; + + mutex_lock(&ubi->fm_mutex); + + ubi_refill_pools(ubi); + + if (ubi->ro_mode || ubi->fm_disabled) { + mutex_unlock(&ubi->fm_mutex); + return 0; + } + + ret = ubi_ensure_anchor_pebs(ubi); + if (ret) { + mutex_unlock(&ubi->fm_mutex); + return ret; + } + + new_fm = kzalloc(sizeof(*new_fm), GFP_KERNEL); + if (!new_fm) { + mutex_unlock(&ubi->fm_mutex); + return -ENOMEM; + } + + new_fm->used_blocks = ubi->fm_size / ubi->leb_size; + + for (i = 0; i < new_fm->used_blocks; i++) { + new_fm->e[i] = kmem_cache_alloc(ubi_wl_entry_slab, GFP_KERNEL); + if (!new_fm->e[i]) { + while (i--) + kfree(new_fm->e[i]); + + kfree(new_fm); + mutex_unlock(&ubi->fm_mutex); + return -ENOMEM; + } + } + + old_fm = ubi->fm; + ubi->fm = NULL; + + if (new_fm->used_blocks > UBI_FM_MAX_BLOCKS) { + ubi_err("fastmap too large"); + ret = -ENOSPC; + goto err; + } + + for (i = 1; i < new_fm->used_blocks; i++) { + spin_lock(&ubi->wl_lock); + tmp_e = ubi_wl_get_fm_peb(ubi, 0); + spin_unlock(&ubi->wl_lock); + + if (!tmp_e && !old_fm) { + int j; + ubi_err("could not get any free erase block"); + + for (j = 1; j < i; j++) + ubi_wl_put_fm_peb(ubi, new_fm->e[j], j, 0); + + ret = -ENOSPC; + goto err; + } else if (!tmp_e && old_fm) { + ret = erase_block(ubi, old_fm->e[i]->pnum); + if (ret < 0) { + int j; + + for (j = 1; j < i; j++) + ubi_wl_put_fm_peb(ubi, new_fm->e[j], + j, 0); + + ubi_err("could not erase old fastmap PEB"); + goto err; + } + + new_fm->e[i]->pnum = old_fm->e[i]->pnum; + new_fm->e[i]->ec = old_fm->e[i]->ec; + } else { + new_fm->e[i]->pnum = tmp_e->pnum; + new_fm->e[i]->ec = tmp_e->ec; + + if (old_fm) + ubi_wl_put_fm_peb(ubi, old_fm->e[i], i, + old_fm->to_be_tortured[i]); + } + } + + spin_lock(&ubi->wl_lock); + tmp_e = ubi_wl_get_fm_peb(ubi, 1); + spin_unlock(&ubi->wl_lock); + + if (old_fm) { + /* no fresh anchor PEB was found, reuse the old one */ + if (!tmp_e) { + ret = erase_block(ubi, old_fm->e[0]->pnum); + if (ret < 0) { + int i; + ubi_err("could not erase old anchor PEB"); + + for (i = 1; i < new_fm->used_blocks; i++) + ubi_wl_put_fm_peb(ubi, new_fm->e[i], + i, 0); + goto err; + } + + new_fm->e[0]->pnum = old_fm->e[0]->pnum; + new_fm->e[0]->ec = ret; + } else { + /* we've got a new anchor PEB, return the old one */ + ubi_wl_put_fm_peb(ubi, old_fm->e[0], 0, + old_fm->to_be_tortured[0]); + + new_fm->e[0]->pnum = tmp_e->pnum; + new_fm->e[0]->ec = tmp_e->ec; + } + } else { + if (!tmp_e) { + int i; + ubi_err("could not find any anchor PEB"); + + for (i = 1; i < new_fm->used_blocks; i++) + ubi_wl_put_fm_peb(ubi, new_fm->e[i], i, 0); + + ret = -ENOSPC; + goto err; + } + + new_fm->e[0]->pnum = tmp_e->pnum; + new_fm->e[0]->ec = tmp_e->ec; + } + + down_write(&ubi->work_sem); + down_write(&ubi->fm_sem); + ret = ubi_write_fastmap(ubi, new_fm); + up_write(&ubi->fm_sem); + up_write(&ubi->work_sem); + + if (ret) + goto err; + +out_unlock: + mutex_unlock(&ubi->fm_mutex); + kfree(old_fm); + return ret; + +err: + kfree(new_fm); + + ubi_warn("Unable to write new fastmap, err=%i", ret); + + ret = 0; + if (old_fm) { + ret = invalidate_fastmap(ubi, old_fm); + if (ret < 0) + ubi_err("Unable to invalidiate current fastmap!"); + else if (ret) + ret = 0; + } + goto out_unlock; +} diff --git a/drivers/mtd/ubi/io.c b/drivers/mtd/ubi/io.c index 960befc..41d7eb7 100644 --- a/drivers/mtd/ubi/io.c +++ b/drivers/mtd/ubi/io.c @@ -1,22 +1,21 @@ /* * Copyright (c) International Business Machines Corp., 2006 * Copyright (c) Nokia Corporation, 2006, 2007 - * * SPDX-License-Identifier: GPL-2.0+ * * Author: Artem Bityutskiy (Битюцкий Артём) */ /* - * UBI input/output unit. + * UBI input/output sub-system. * - * This unit provides a uniform way to work with all kinds of the underlying - * MTD devices. It also implements handy functions for reading and writing UBI - * headers. + * This sub-system provides a uniform way to work with all kinds of the + * underlying MTD devices. It also implements handy functions for reading and + * writing UBI headers. * * We are trying to have a paranoid mindset and not to trust to what we read - * from the flash media in order to be more secure and robust. So this unit - * validates every single header it reads from the flash media. + * from the flash media in order to be more secure and robust. So this + * sub-system validates every single header it reads from the flash media. * * Some words about how the eraseblock headers are stored. * @@ -52,9 +51,9 @@ * device, e.g., make @ubi->min_io_size = 512 in the example above? * * A: because when writing a sub-page, MTD still writes a full 2K page but the - * bytes which are no relevant to the sub-page are 0xFF. So, basically, writing - * 4x512 sub-pages is 4 times slower then writing one 2KiB NAND page. Thus, we - * prefer to use sub-pages only for EV and VID headers. + * bytes which are not relevant to the sub-page are 0xFF. So, basically, + * writing 4x512 sub-pages is 4 times slower than writing one 2KiB NAND page. + * Thus, we prefer to use sub-pages only for EC and VID headers. * * As it was noted above, the VID header may start at a non-aligned offset. * For example, in case of a 2KiB page NAND flash with a 512 bytes sub-page, @@ -67,39 +66,33 @@ * 512-byte chunks, we have to allocate one more buffer and copy our VID header * to offset 448 of this buffer. * - * The I/O unit does the following trick in order to avoid this extra copy. - * It always allocates a @ubi->vid_hdr_alsize bytes buffer for the VID header - * and returns a pointer to offset @ubi->vid_hdr_shift of this buffer. When the - * VID header is being written out, it shifts the VID header pointer back and - * writes the whole sub-page. + * The I/O sub-system does the following trick in order to avoid this extra + * copy. It always allocates a @ubi->vid_hdr_alsize bytes buffer for the VID + * header and returns a pointer to offset @ubi->vid_hdr_shift of this buffer. + * When the VID header is being written out, it shifts the VID header pointer + * back and writes the whole sub-page. */ -#ifdef UBI_LINUX +#define __UBOOT__ +#ifndef __UBOOT__ #include <linux/crc32.h> #include <linux/err.h> +#include <linux/slab.h> +#else +#include <ubi_uboot.h> #endif -#include <ubi_uboot.h> #include "ubi.h" -#ifdef CONFIG_MTD_UBI_DEBUG_PARANOID -static int paranoid_check_not_bad(const struct ubi_device *ubi, int pnum); -static int paranoid_check_peb_ec_hdr(const struct ubi_device *ubi, int pnum); -static int paranoid_check_ec_hdr(const struct ubi_device *ubi, int pnum, - const struct ubi_ec_hdr *ec_hdr); -static int paranoid_check_peb_vid_hdr(const struct ubi_device *ubi, int pnum); -static int paranoid_check_vid_hdr(const struct ubi_device *ubi, int pnum, - const struct ubi_vid_hdr *vid_hdr); -static int paranoid_check_all_ff(struct ubi_device *ubi, int pnum, int offset, - int len); -#else -#define paranoid_check_not_bad(ubi, pnum) 0 -#define paranoid_check_peb_ec_hdr(ubi, pnum) 0 -#define paranoid_check_ec_hdr(ubi, pnum, ec_hdr) 0 -#define paranoid_check_peb_vid_hdr(ubi, pnum) 0 -#define paranoid_check_vid_hdr(ubi, pnum, vid_hdr) 0 -#define paranoid_check_all_ff(ubi, pnum, offset, len) 0 -#endif +static int self_check_not_bad(const struct ubi_device *ubi, int pnum); +static int self_check_peb_ec_hdr(const struct ubi_device *ubi, int pnum); +static int self_check_ec_hdr(const struct ubi_device *ubi, int pnum, + const struct ubi_ec_hdr *ec_hdr); +static int self_check_peb_vid_hdr(const struct ubi_device *ubi, int pnum); +static int self_check_vid_hdr(const struct ubi_device *ubi, int pnum, + const struct ubi_vid_hdr *vid_hdr); +static int self_check_write(struct ubi_device *ubi, const void *buf, int pnum, + int offset, int len); /** * ubi_io_read - read data from a physical eraseblock. @@ -136,51 +129,77 @@ int ubi_io_read(const struct ubi_device *ubi, void *buf, int pnum, int offset, ubi_assert(offset >= 0 && offset + len <= ubi->peb_size); ubi_assert(len > 0); - err = paranoid_check_not_bad(ubi, pnum); + err = self_check_not_bad(ubi, pnum); if (err) - return err > 0 ? -EINVAL : err; + return err; + + /* + * Deliberately corrupt the buffer to improve robustness. Indeed, if we + * do not do this, the following may happen: + * 1. The buffer contains data from previous operation, e.g., read from + * another PEB previously. The data looks like expected, e.g., if we + * just do not read anything and return - the caller would not + * notice this. E.g., if we are reading a VID header, the buffer may + * contain a valid VID header from another PEB. + * 2. The driver is buggy and returns us success or -EBADMSG or + * -EUCLEAN, but it does not actually put any data to the buffer. + * + * This may confuse UBI or upper layers - they may think the buffer + * contains valid data while in fact it is just old data. This is + * especially possible because UBI (and UBIFS) relies on CRC, and + * treats data as correct even in case of ECC errors if the CRC is + * correct. + * + * Try to prevent this situation by changing the first byte of the + * buffer. + */ + *((uint8_t *)buf) ^= 0xFF; addr = (loff_t)pnum * ubi->peb_size + offset; retry: err = mtd_read(ubi->mtd, addr, len, &read, buf); if (err) { - if (err == -EUCLEAN) { + const char *errstr = mtd_is_eccerr(err) ? " (ECC error)" : ""; + + if (mtd_is_bitflip(err)) { /* * -EUCLEAN is reported if there was a bit-flip which * was corrected, so this is harmless. + * + * We do not report about it here unless debugging is + * enabled. A corresponding message will be printed + * later, when it is has been scrubbed. */ ubi_msg("fixable bit-flip detected at PEB %d", pnum); ubi_assert(len == read); return UBI_IO_BITFLIPS; } - if (read != len && retries++ < UBI_IO_RETRIES) { - dbg_io("error %d while reading %d bytes from PEB %d:%d, " - "read only %zd bytes, retry", - err, len, pnum, offset, read); + if (retries++ < UBI_IO_RETRIES) { + ubi_warn("error %d%s while reading %d bytes from PEB %d:%d, read only %zd bytes, retry", + err, errstr, len, pnum, offset, read); yield(); goto retry; } - ubi_err("error %d while reading %d bytes from PEB %d:%d, " - "read %zd bytes", err, len, pnum, offset, read); - ubi_dbg_dump_stack(); + ubi_err("error %d%s while reading %d bytes from PEB %d:%d, read %zd bytes", + err, errstr, len, pnum, offset, read); + dump_stack(); /* * The driver should never return -EBADMSG if it failed to read * all the requested data. But some buggy drivers might do * this, so we change it to -EIO. */ - if (read != len && err == -EBADMSG) { + if (read != len && mtd_is_eccerr(err)) { ubi_assert(0); - printk("%s[%d] not here\n", __func__, __LINE__); -/* err = -EIO; */ + err = -EIO; } } else { ubi_assert(len == read); - if (ubi_dbg_is_bitflip()) { - dbg_msg("bit-flip (emulated)"); + if (ubi_dbg_is_bitflip(ubi)) { + dbg_gen("bit-flip (emulated)"); err = UBI_IO_BITFLIPS; } } @@ -224,46 +243,60 @@ int ubi_io_write(struct ubi_device *ubi, const void *buf, int pnum, int offset, return -EROFS; } - /* The below has to be compiled out if paranoid checks are disabled */ - - err = paranoid_check_not_bad(ubi, pnum); + err = self_check_not_bad(ubi, pnum); if (err) - return err > 0 ? -EINVAL : err; + return err; /* The area we are writing to has to contain all 0xFF bytes */ - err = paranoid_check_all_ff(ubi, pnum, offset, len); + err = ubi_self_check_all_ff(ubi, pnum, offset, len); if (err) - return err > 0 ? -EINVAL : err; + return err; if (offset >= ubi->leb_start) { /* * We write to the data area of the physical eraseblock. Make * sure it has valid EC and VID headers. */ - err = paranoid_check_peb_ec_hdr(ubi, pnum); + err = self_check_peb_ec_hdr(ubi, pnum); if (err) - return err > 0 ? -EINVAL : err; - err = paranoid_check_peb_vid_hdr(ubi, pnum); + return err; + err = self_check_peb_vid_hdr(ubi, pnum); if (err) - return err > 0 ? -EINVAL : err; + return err; } - if (ubi_dbg_is_write_failure()) { - dbg_err("cannot write %d bytes to PEB %d:%d " - "(emulated)", len, pnum, offset); - ubi_dbg_dump_stack(); + if (ubi_dbg_is_write_failure(ubi)) { + ubi_err("cannot write %d bytes to PEB %d:%d (emulated)", + len, pnum, offset); + dump_stack(); return -EIO; } addr = (loff_t)pnum * ubi->peb_size + offset; err = mtd_write(ubi->mtd, addr, len, &written, buf); if (err) { - ubi_err("error %d while writing %d bytes to PEB %d:%d, written" - " %zd bytes", err, len, pnum, offset, written); - ubi_dbg_dump_stack(); + ubi_err("error %d while writing %d bytes to PEB %d:%d, written %zd bytes", + err, len, pnum, offset, written); + dump_stack(); + ubi_dump_flash(ubi, pnum, offset, len); } else ubi_assert(written == len); + if (!err) { + err = self_check_write(ubi, buf, pnum, offset, len); + if (err) + return err; + + /* + * Since we always write sequentially, the rest of the PEB has + * to contain only 0xFF bytes. + */ + offset += len; + len = ubi->peb_size - offset; + if (len) + err = ubi_self_check_all_ff(ubi, pnum, offset, len); + } + return err; } @@ -295,6 +328,12 @@ static int do_sync_erase(struct ubi_device *ubi, int pnum) wait_queue_head_t wq; dbg_io("erase PEB %d", pnum); + ubi_assert(pnum >= 0 && pnum < ubi->peb_count); + + if (ubi->ro_mode) { + ubi_err("read-only mode"); + return -EROFS; + } retry: init_waitqueue_head(&wq); @@ -309,13 +348,13 @@ retry: err = mtd_erase(ubi->mtd, &ei); if (err) { if (retries++ < UBI_IO_RETRIES) { - dbg_io("error %d while erasing PEB %d, retry", - err, pnum); + ubi_warn("error %d while erasing PEB %d, retry", + err, pnum); yield(); goto retry; } ubi_err("cannot erase PEB %d, error %d", pnum, err); - ubi_dbg_dump_stack(); + dump_stack(); return err; } @@ -328,46 +367,27 @@ retry: if (ei.state == MTD_ERASE_FAILED) { if (retries++ < UBI_IO_RETRIES) { - dbg_io("error while erasing PEB %d, retry", pnum); + ubi_warn("error while erasing PEB %d, retry", pnum); yield(); goto retry; } ubi_err("cannot erase PEB %d", pnum); - ubi_dbg_dump_stack(); + dump_stack(); return -EIO; } - err = paranoid_check_all_ff(ubi, pnum, 0, ubi->peb_size); + err = ubi_self_check_all_ff(ubi, pnum, 0, ubi->peb_size); if (err) - return err > 0 ? -EINVAL : err; + return err; - if (ubi_dbg_is_erase_failure() && !err) { - dbg_err("cannot erase PEB %d (emulated)", pnum); + if (ubi_dbg_is_erase_failure(ubi)) { + ubi_err("cannot erase PEB %d (emulated)", pnum); return -EIO; } return 0; } -/** - * check_pattern - check if buffer contains only a certain byte pattern. - * @buf: buffer to check - * @patt: the pattern to check - * @size: buffer size in bytes - * - * This function returns %1 in there are only @patt bytes in @buf, and %0 if - * something else was also found. - */ -static int check_pattern(const void *buf, uint8_t patt, int size) -{ - int i; - - for (i = 0; i < size; i++) - if (((const uint8_t *)buf)[i] != patt) - return 0; - return 1; -} - /* Patterns to write to a physical eraseblock when torturing it */ static uint8_t patterns[] = {0xa5, 0x5a, 0x0}; @@ -384,6 +404,7 @@ static int torture_peb(struct ubi_device *ubi, int pnum) { int err, i, patt_count; + ubi_msg("run torture test for PEB %d", pnum); patt_count = ARRAY_SIZE(patterns); ubi_assert(patt_count > 0); @@ -394,11 +415,11 @@ static int torture_peb(struct ubi_device *ubi, int pnum) goto out; /* Make sure the PEB contains only 0xFF bytes */ - err = ubi_io_read(ubi, ubi->peb_buf1, pnum, 0, ubi->peb_size); + err = ubi_io_read(ubi, ubi->peb_buf, pnum, 0, ubi->peb_size); if (err) goto out; - err = check_pattern(ubi->peb_buf1, 0xFF, ubi->peb_size); + err = ubi_check_pattern(ubi->peb_buf, 0xFF, ubi->peb_size); if (err == 0) { ubi_err("erased PEB %d, but a non-0xFF byte found", pnum); @@ -407,17 +428,18 @@ static int torture_peb(struct ubi_device *ubi, int pnum) } /* Write a pattern and check it */ - memset(ubi->peb_buf1, patterns[i], ubi->peb_size); - err = ubi_io_write(ubi, ubi->peb_buf1, pnum, 0, ubi->peb_size); + memset(ubi->peb_buf, patterns[i], ubi->peb_size); + err = ubi_io_write(ubi, ubi->peb_buf, pnum, 0, ubi->peb_size); if (err) goto out; - memset(ubi->peb_buf1, ~patterns[i], ubi->peb_size); - err = ubi_io_read(ubi, ubi->peb_buf1, pnum, 0, ubi->peb_size); + memset(ubi->peb_buf, ~patterns[i], ubi->peb_size); + err = ubi_io_read(ubi, ubi->peb_buf, pnum, 0, ubi->peb_size); if (err) goto out; - err = check_pattern(ubi->peb_buf1, patterns[i], ubi->peb_size); + err = ubi_check_pattern(ubi->peb_buf, patterns[i], + ubi->peb_size); if (err == 0) { ubi_err("pattern %x checking failed for PEB %d", patterns[i], pnum); @@ -427,10 +449,11 @@ static int torture_peb(struct ubi_device *ubi, int pnum) } err = patt_count; + ubi_msg("PEB %d passed torture test, do not mark it as bad", pnum); out: mutex_unlock(&ubi->buf_mutex); - if (err == UBI_IO_BITFLIPS || err == -EBADMSG) { + if (err == UBI_IO_BITFLIPS || mtd_is_eccerr(err)) { /* * If a bit-flip or data integrity error was detected, the test * has not passed because it happened on a freshly erased @@ -444,6 +467,80 @@ out: } /** + * nor_erase_prepare - prepare a NOR flash PEB for erasure. + * @ubi: UBI device description object + * @pnum: physical eraseblock number to prepare + * + * NOR flash, or at least some of them, have peculiar embedded PEB erasure + * algorithm: the PEB is first filled with zeroes, then it is erased. And + * filling with zeroes starts from the end of the PEB. This was observed with + * Spansion S29GL512N NOR flash. + * + * This means that in case of a power cut we may end up with intact data at the + * beginning of the PEB, and all zeroes at the end of PEB. In other words, the + * EC and VID headers are OK, but a large chunk of data at the end of PEB is + * zeroed. This makes UBI mistakenly treat this PEB as used and associate it + * with an LEB, which leads to subsequent failures (e.g., UBIFS fails). + * + * This function is called before erasing NOR PEBs and it zeroes out EC and VID + * magic numbers in order to invalidate them and prevent the failures. Returns + * zero in case of success and a negative error code in case of failure. + */ +static int nor_erase_prepare(struct ubi_device *ubi, int pnum) +{ + int err; + size_t written; + loff_t addr; + uint32_t data = 0; + struct ubi_ec_hdr ec_hdr; + + /* + * Note, we cannot generally define VID header buffers on stack, + * because of the way we deal with these buffers (see the header + * comment in this file). But we know this is a NOR-specific piece of + * code, so we can do this. But yes, this is error-prone and we should + * (pre-)allocate VID header buffer instead. + */ + struct ubi_vid_hdr vid_hdr; + + /* + * If VID or EC is valid, we have to corrupt them before erasing. + * It is important to first invalidate the EC header, and then the VID + * header. Otherwise a power cut may lead to valid EC header and + * invalid VID header, in which case UBI will treat this PEB as + * corrupted and will try to preserve it, and print scary warnings. + */ + addr = (loff_t)pnum * ubi->peb_size; + err = ubi_io_read_ec_hdr(ubi, pnum, &ec_hdr, 0); + if (err != UBI_IO_BAD_HDR_EBADMSG && err != UBI_IO_BAD_HDR && + err != UBI_IO_FF){ + err = mtd_write(ubi->mtd, addr, 4, &written, (void *)&data); + if(err) + goto error; + } + + err = ubi_io_read_vid_hdr(ubi, pnum, &vid_hdr, 0); + if (err != UBI_IO_BAD_HDR_EBADMSG && err != UBI_IO_BAD_HDR && + err != UBI_IO_FF){ + addr += ubi->vid_hdr_aloffset; + err = mtd_write(ubi->mtd, addr, 4, &written, (void *)&data); + if (err) + goto error; + } + return 0; + +error: + /* + * The PEB contains a valid VID or EC header, but we cannot invalidate + * it. Supposedly the flash media or the driver is screwed up, so + * return an error. + */ + ubi_err("cannot invalidate PEB %d, write returned %d", pnum, err); + ubi_dump_flash(ubi, pnum, 0, ubi->peb_size); + return -EIO; +} + +/** * ubi_io_sync_erase - synchronously erase a physical eraseblock. * @ubi: UBI device description object * @pnum: physical eraseblock number to erase @@ -452,7 +549,7 @@ out: * This function synchronously erases physical eraseblock @pnum. If @torture * flag is not zero, the physical eraseblock is checked by means of writing * different patterns to it and reading them back. If the torturing is enabled, - * the physical eraseblock is erased more then once. + * the physical eraseblock is erased more than once. * * This function returns the number of erasures made in case of success, %-EIO * if the erasure failed or the torturing test failed, and other negative error @@ -465,15 +562,21 @@ int ubi_io_sync_erase(struct ubi_device *ubi, int pnum, int torture) ubi_assert(pnum >= 0 && pnum < ubi->peb_count); - err = paranoid_check_not_bad(ubi, pnum); + err = self_check_not_bad(ubi, pnum); if (err != 0) - return err > 0 ? -EINVAL : err; + return err; if (ubi->ro_mode) { ubi_err("read-only mode"); return -EROFS; } + if (ubi->nor_flash) { + err = nor_erase_prepare(ubi, pnum); + if (err) + return err; + } + if (torture) { ret = torture_peb(ubi, pnum); if (ret < 0) @@ -564,8 +667,7 @@ static int validate_ec_hdr(const struct ubi_device *ubi, leb_start = be32_to_cpu(ec_hdr->data_offset); if (ec_hdr->version != UBI_VERSION) { - ubi_err("node with incompatible UBI version found: " - "this UBI version is %d, image version is %d", + ubi_err("node with incompatible UBI version found: this UBI version is %d, image version is %d", UBI_VERSION, (int)ec_hdr->version); goto bad; } @@ -591,8 +693,8 @@ static int validate_ec_hdr(const struct ubi_device *ubi, bad: ubi_err("bad EC header"); - ubi_dbg_dump_ec_hdr(ec_hdr); - ubi_dbg_dump_stack(); + ubi_dump_ec_hdr(ec_hdr); + dump_stack(); return 1; } @@ -612,67 +714,58 @@ bad: * o %UBI_IO_BITFLIPS if the CRC is correct, but bit-flips were detected * and corrected by the flash driver; this is harmless but may indicate that * this eraseblock may become bad soon (but may be not); - * o %UBI_IO_BAD_EC_HDR if the erase counter header is corrupted (a CRC error); - * o %UBI_IO_PEB_EMPTY if the physical eraseblock is empty; + * o %UBI_IO_BAD_HDR if the erase counter header is corrupted (a CRC error); + * o %UBI_IO_BAD_HDR_EBADMSG is the same as %UBI_IO_BAD_HDR, but there also was + * a data integrity error (uncorrectable ECC error in case of NAND); + * o %UBI_IO_FF if only 0xFF bytes were read (the PEB is supposedly empty) * o a negative error code in case of failure. */ int ubi_io_read_ec_hdr(struct ubi_device *ubi, int pnum, struct ubi_ec_hdr *ec_hdr, int verbose) { - int err, read_err = 0; + int err, read_err; uint32_t crc, magic, hdr_crc; dbg_io("read EC header from PEB %d", pnum); ubi_assert(pnum >= 0 && pnum < ubi->peb_count); - if (UBI_IO_DEBUG) - verbose = 1; - err = ubi_io_read(ubi, ec_hdr, pnum, 0, UBI_EC_HDR_SIZE); - if (err) { - if (err != UBI_IO_BITFLIPS && err != -EBADMSG) - return err; + read_err = ubi_io_read(ubi, ec_hdr, pnum, 0, UBI_EC_HDR_SIZE); + if (read_err) { + if (read_err != UBI_IO_BITFLIPS && !mtd_is_eccerr(read_err)) + return read_err; /* * We read all the data, but either a correctable bit-flip - * occurred, or MTD reported about some data integrity error, - * like an ECC error in case of NAND. The former is harmless, - * the later may mean that the read data is corrupted. But we - * have a CRC check-sum and we will detect this. If the EC - * header is still OK, we just report this as there was a - * bit-flip. + * occurred, or MTD reported a data integrity error + * (uncorrectable ECC error in case of NAND). The former is + * harmless, the later may mean that the read data is + * corrupted. But we have a CRC check-sum and we will detect + * this. If the EC header is still OK, we just report this as + * there was a bit-flip, to force scrubbing. */ - read_err = err; } magic = be32_to_cpu(ec_hdr->magic); if (magic != UBI_EC_HDR_MAGIC) { + if (mtd_is_eccerr(read_err)) + return UBI_IO_BAD_HDR_EBADMSG; + /* * The magic field is wrong. Let's check if we have read all * 0xFF. If yes, this physical eraseblock is assumed to be * empty. - * - * But if there was a read error, we do not test it for all - * 0xFFs. Even if it does contain all 0xFFs, this error - * indicates that something is still wrong with this physical - * eraseblock and we anyway cannot treat it as empty. */ - if (read_err != -EBADMSG && - check_pattern(ec_hdr, 0xFF, UBI_EC_HDR_SIZE)) { + if (ubi_check_pattern(ec_hdr, 0xFF, UBI_EC_HDR_SIZE)) { /* The physical eraseblock is supposedly empty */ - - /* - * The below is just a paranoid check, it has to be - * compiled out if paranoid checks are disabled. - */ - err = paranoid_check_all_ff(ubi, pnum, 0, - ubi->peb_size); - if (err) - return err > 0 ? UBI_IO_BAD_EC_HDR : err; - if (verbose) - ubi_warn("no EC header found at PEB %d, " - "only 0xFF bytes", pnum); - return UBI_IO_PEB_EMPTY; + ubi_warn("no EC header found at PEB %d, only 0xFF bytes", + pnum); + dbg_bld("no EC header found at PEB %d, only 0xFF bytes", + pnum); + if (!read_err) + return UBI_IO_FF; + else + return UBI_IO_FF_BITFLIPS; } /* @@ -680,11 +773,13 @@ int ubi_io_read_ec_hdr(struct ubi_device *ubi, int pnum, * 0xFF bytes. Report that the header is corrupted. */ if (verbose) { - ubi_warn("bad magic number at PEB %d: %08x instead of " - "%08x", pnum, magic, UBI_EC_HDR_MAGIC); - ubi_dbg_dump_ec_hdr(ec_hdr); + ubi_warn("bad magic number at PEB %d: %08x instead of %08x", + pnum, magic, UBI_EC_HDR_MAGIC); + ubi_dump_ec_hdr(ec_hdr); } - return UBI_IO_BAD_EC_HDR; + dbg_bld("bad magic number at PEB %d: %08x instead of %08x", + pnum, magic, UBI_EC_HDR_MAGIC); + return UBI_IO_BAD_HDR; } crc = crc32(UBI_CRC32_INIT, ec_hdr, UBI_EC_HDR_SIZE_CRC); @@ -692,11 +787,17 @@ int ubi_io_read_ec_hdr(struct ubi_device *ubi, int pnum, if (hdr_crc != crc) { if (verbose) { - ubi_warn("bad EC header CRC at PEB %d, calculated %#08x," - " read %#08x", pnum, crc, hdr_crc); - ubi_dbg_dump_ec_hdr(ec_hdr); + ubi_warn("bad EC header CRC at PEB %d, calculated %#08x, read %#08x", + pnum, crc, hdr_crc); + ubi_dump_ec_hdr(ec_hdr); } - return UBI_IO_BAD_EC_HDR; + dbg_bld("bad EC header CRC at PEB %d, calculated %#08x, read %#08x", + pnum, crc, hdr_crc); + + if (!read_err) + return UBI_IO_BAD_HDR; + else + return UBI_IO_BAD_HDR_EBADMSG; } /* And of course validate what has just been read from the media */ @@ -706,6 +807,10 @@ int ubi_io_read_ec_hdr(struct ubi_device *ubi, int pnum, return -EINVAL; } + /* + * If there was %-EBADMSG, but the header CRC is still OK, report about + * a bit-flip to force scrubbing on this PEB. + */ return read_err ? UBI_IO_BITFLIPS : 0; } @@ -737,12 +842,13 @@ int ubi_io_write_ec_hdr(struct ubi_device *ubi, int pnum, ec_hdr->version = UBI_VERSION; ec_hdr->vid_hdr_offset = cpu_to_be32(ubi->vid_hdr_offset); ec_hdr->data_offset = cpu_to_be32(ubi->leb_start); + ec_hdr->image_seq = cpu_to_be32(ubi->image_seq); crc = crc32(UBI_CRC32_INIT, ec_hdr, UBI_EC_HDR_SIZE_CRC); ec_hdr->hdr_crc = cpu_to_be32(crc); - err = paranoid_check_ec_hdr(ubi, pnum, ec_hdr); + err = self_check_ec_hdr(ubi, pnum, ec_hdr); if (err) - return -EINVAL; + return err; err = ubi_io_write(ubi, ec_hdr, pnum, 0, ubi->ec_hdr_alsize); return err; @@ -771,40 +877,40 @@ static int validate_vid_hdr(const struct ubi_device *ubi, int usable_leb_size = ubi->leb_size - data_pad; if (copy_flag != 0 && copy_flag != 1) { - dbg_err("bad copy_flag"); + ubi_err("bad copy_flag"); goto bad; } if (vol_id < 0 || lnum < 0 || data_size < 0 || used_ebs < 0 || data_pad < 0) { - dbg_err("negative values"); + ubi_err("negative values"); goto bad; } if (vol_id >= UBI_MAX_VOLUMES && vol_id < UBI_INTERNAL_VOL_START) { - dbg_err("bad vol_id"); + ubi_err("bad vol_id"); goto bad; } if (vol_id < UBI_INTERNAL_VOL_START && compat != 0) { - dbg_err("bad compat"); + ubi_err("bad compat"); goto bad; } if (vol_id >= UBI_INTERNAL_VOL_START && compat != UBI_COMPAT_DELETE && compat != UBI_COMPAT_RO && compat != UBI_COMPAT_PRESERVE && compat != UBI_COMPAT_REJECT) { - dbg_err("bad compat"); + ubi_err("bad compat"); goto bad; } if (vol_type != UBI_VID_DYNAMIC && vol_type != UBI_VID_STATIC) { - dbg_err("bad vol_type"); + ubi_err("bad vol_type"); goto bad; } if (data_pad >= ubi->leb_size / 2) { - dbg_err("bad data_pad"); + ubi_err("bad data_pad"); goto bad; } @@ -816,45 +922,45 @@ static int validate_vid_hdr(const struct ubi_device *ubi, * mapped logical eraseblocks. */ if (used_ebs == 0) { - dbg_err("zero used_ebs"); + ubi_err("zero used_ebs"); goto bad; } if (data_size == 0) { - dbg_err("zero data_size"); + ubi_err("zero data_size"); goto bad; } if (lnum < used_ebs - 1) { if (data_size != usable_leb_size) { - dbg_err("bad data_size"); + ubi_err("bad data_size"); goto bad; } } else if (lnum == used_ebs - 1) { if (data_size == 0) { - dbg_err("bad data_size at last LEB"); + ubi_err("bad data_size at last LEB"); goto bad; } } else { - dbg_err("too high lnum"); + ubi_err("too high lnum"); goto bad; } } else { if (copy_flag == 0) { if (data_crc != 0) { - dbg_err("non-zero data CRC"); + ubi_err("non-zero data CRC"); goto bad; } if (data_size != 0) { - dbg_err("non-zero data_size"); + ubi_err("non-zero data_size"); goto bad; } } else { if (data_size == 0) { - dbg_err("zero data_size of copy"); + ubi_err("zero data_size of copy"); goto bad; } } if (used_ebs != 0) { - dbg_err("bad used_ebs"); + ubi_err("bad used_ebs"); goto bad; } } @@ -863,8 +969,8 @@ static int validate_vid_hdr(const struct ubi_device *ubi, bad: ubi_err("bad VID header"); - ubi_dbg_dump_vid_hdr(vid_hdr); - ubi_dbg_dump_stack(); + ubi_dump_vid_hdr(vid_hdr); + dump_stack(); return 1; } @@ -878,88 +984,53 @@ bad: * * This function reads the volume identifier header from physical eraseblock * @pnum and stores it in @vid_hdr. It also checks CRC checksum of the read - * volume identifier header. The following codes may be returned: + * volume identifier header. The error codes are the same as in + * 'ubi_io_read_ec_hdr()'. * - * o %0 if the CRC checksum is correct and the header was successfully read; - * o %UBI_IO_BITFLIPS if the CRC is correct, but bit-flips were detected - * and corrected by the flash driver; this is harmless but may indicate that - * this eraseblock may become bad soon; - * o %UBI_IO_BAD_VID_HRD if the volume identifier header is corrupted (a CRC - * error detected); - * o %UBI_IO_PEB_FREE if the physical eraseblock is free (i.e., there is no VID - * header there); - * o a negative error code in case of failure. + * Note, the implementation of this function is also very similar to + * 'ubi_io_read_ec_hdr()', so refer commentaries in 'ubi_io_read_ec_hdr()'. */ int ubi_io_read_vid_hdr(struct ubi_device *ubi, int pnum, struct ubi_vid_hdr *vid_hdr, int verbose) { - int err, read_err = 0; + int err, read_err; uint32_t crc, magic, hdr_crc; void *p; dbg_io("read VID header from PEB %d", pnum); ubi_assert(pnum >= 0 && pnum < ubi->peb_count); - if (UBI_IO_DEBUG) - verbose = 1; p = (char *)vid_hdr - ubi->vid_hdr_shift; - err = ubi_io_read(ubi, p, pnum, ubi->vid_hdr_aloffset, + read_err = ubi_io_read(ubi, p, pnum, ubi->vid_hdr_aloffset, ubi->vid_hdr_alsize); - if (err) { - if (err != UBI_IO_BITFLIPS && err != -EBADMSG) - return err; - - /* - * We read all the data, but either a correctable bit-flip - * occurred, or MTD reported about some data integrity error, - * like an ECC error in case of NAND. The former is harmless, - * the later may mean the read data is corrupted. But we have a - * CRC check-sum and we will identify this. If the VID header is - * still OK, we just report this as there was a bit-flip. - */ - read_err = err; - } + if (read_err && read_err != UBI_IO_BITFLIPS && !mtd_is_eccerr(read_err)) + return read_err; magic = be32_to_cpu(vid_hdr->magic); if (magic != UBI_VID_HDR_MAGIC) { - /* - * If we have read all 0xFF bytes, the VID header probably does - * not exist and the physical eraseblock is assumed to be free. - * - * But if there was a read error, we do not test the data for - * 0xFFs. Even if it does contain all 0xFFs, this error - * indicates that something is still wrong with this physical - * eraseblock and it cannot be regarded as free. - */ - if (read_err != -EBADMSG && - check_pattern(vid_hdr, 0xFF, UBI_VID_HDR_SIZE)) { - /* The physical eraseblock is supposedly free */ - - /* - * The below is just a paranoid check, it has to be - * compiled out if paranoid checks are disabled. - */ - err = paranoid_check_all_ff(ubi, pnum, ubi->leb_start, - ubi->leb_size); - if (err) - return err > 0 ? UBI_IO_BAD_VID_HDR : err; + if (mtd_is_eccerr(read_err)) + return UBI_IO_BAD_HDR_EBADMSG; + if (ubi_check_pattern(vid_hdr, 0xFF, UBI_VID_HDR_SIZE)) { if (verbose) - ubi_warn("no VID header found at PEB %d, " - "only 0xFF bytes", pnum); - return UBI_IO_PEB_FREE; + ubi_warn("no VID header found at PEB %d, only 0xFF bytes", + pnum); + dbg_bld("no VID header found at PEB %d, only 0xFF bytes", + pnum); + if (!read_err) + return UBI_IO_FF; + else + return UBI_IO_FF_BITFLIPS; } - /* - * This is not a valid VID header, and these are not 0xFF - * bytes. Report that the header is corrupted. - */ if (verbose) { - ubi_warn("bad magic number at PEB %d: %08x instead of " - "%08x", pnum, magic, UBI_VID_HDR_MAGIC); - ubi_dbg_dump_vid_hdr(vid_hdr); + ubi_warn("bad magic number at PEB %d: %08x instead of %08x", + pnum, magic, UBI_VID_HDR_MAGIC); + ubi_dump_vid_hdr(vid_hdr); } - return UBI_IO_BAD_VID_HDR; + dbg_bld("bad magic number at PEB %d: %08x instead of %08x", + pnum, magic, UBI_VID_HDR_MAGIC); + return UBI_IO_BAD_HDR; } crc = crc32(UBI_CRC32_INIT, vid_hdr, UBI_VID_HDR_SIZE_CRC); @@ -967,14 +1038,18 @@ int ubi_io_read_vid_hdr(struct ubi_device *ubi, int pnum, if (hdr_crc != crc) { if (verbose) { - ubi_warn("bad CRC at PEB %d, calculated %#08x, " - "read %#08x", pnum, crc, hdr_crc); - ubi_dbg_dump_vid_hdr(vid_hdr); + ubi_warn("bad CRC at PEB %d, calculated %#08x, read %#08x", + pnum, crc, hdr_crc); + ubi_dump_vid_hdr(vid_hdr); } - return UBI_IO_BAD_VID_HDR; + dbg_bld("bad CRC at PEB %d, calculated %#08x, read %#08x", + pnum, crc, hdr_crc); + if (!read_err) + return UBI_IO_BAD_HDR; + else + return UBI_IO_BAD_HDR_EBADMSG; } - /* Validate the VID header that we have just read */ err = validate_vid_hdr(ubi, vid_hdr); if (err) { ubi_err("validation failed for PEB %d", pnum); @@ -1009,18 +1084,18 @@ int ubi_io_write_vid_hdr(struct ubi_device *ubi, int pnum, dbg_io("write VID header to PEB %d", pnum); ubi_assert(pnum >= 0 && pnum < ubi->peb_count); - err = paranoid_check_peb_ec_hdr(ubi, pnum); + err = self_check_peb_ec_hdr(ubi, pnum); if (err) - return err > 0 ? -EINVAL: err; + return err; vid_hdr->magic = cpu_to_be32(UBI_VID_HDR_MAGIC); vid_hdr->version = UBI_VERSION; crc = crc32(UBI_CRC32_INIT, vid_hdr, UBI_VID_HDR_SIZE_CRC); vid_hdr->hdr_crc = cpu_to_be32(crc); - err = paranoid_check_vid_hdr(ubi, pnum, vid_hdr); + err = self_check_vid_hdr(ubi, pnum, vid_hdr); if (err) - return -EINVAL; + return err; p = (char *)vid_hdr - ubi->vid_hdr_shift; err = ubi_io_write(ubi, p, pnum, ubi->vid_hdr_aloffset, @@ -1028,44 +1103,48 @@ int ubi_io_write_vid_hdr(struct ubi_device *ubi, int pnum, return err; } -#ifdef CONFIG_MTD_UBI_DEBUG_PARANOID - /** - * paranoid_check_not_bad - ensure that a physical eraseblock is not bad. + * self_check_not_bad - ensure that a physical eraseblock is not bad. * @ubi: UBI device description object * @pnum: physical eraseblock number to check * - * This function returns zero if the physical eraseblock is good, a positive - * number if it is bad and a negative error code if an error occurred. + * This function returns zero if the physical eraseblock is good, %-EINVAL if + * it is bad and a negative error code if an error occurred. */ -static int paranoid_check_not_bad(const struct ubi_device *ubi, int pnum) +static int self_check_not_bad(const struct ubi_device *ubi, int pnum) { int err; + if (!ubi_dbg_chk_io(ubi)) + return 0; + err = ubi_io_is_bad(ubi, pnum); if (!err) return err; - ubi_err("paranoid check failed for PEB %d", pnum); - ubi_dbg_dump_stack(); - return err; + ubi_err("self-check failed for PEB %d", pnum); + dump_stack(); + return err > 0 ? -EINVAL : err; } /** - * paranoid_check_ec_hdr - check if an erase counter header is all right. + * self_check_ec_hdr - check if an erase counter header is all right. * @ubi: UBI device description object * @pnum: physical eraseblock number the erase counter header belongs to * @ec_hdr: the erase counter header to check * * This function returns zero if the erase counter header contains valid - * values, and %1 if not. + * values, and %-EINVAL if not. */ -static int paranoid_check_ec_hdr(const struct ubi_device *ubi, int pnum, - const struct ubi_ec_hdr *ec_hdr) +static int self_check_ec_hdr(const struct ubi_device *ubi, int pnum, + const struct ubi_ec_hdr *ec_hdr) { int err; uint32_t magic; + if (!ubi_dbg_chk_io(ubi)) + return 0; + magic = be32_to_cpu(ec_hdr->magic); if (magic != UBI_EC_HDR_MAGIC) { ubi_err("bad magic %#08x, must be %#08x", @@ -1075,53 +1154,55 @@ static int paranoid_check_ec_hdr(const struct ubi_device *ubi, int pnum, err = validate_ec_hdr(ubi, ec_hdr); if (err) { - ubi_err("paranoid check failed for PEB %d", pnum); + ubi_err("self-check failed for PEB %d", pnum); goto fail; } return 0; fail: - ubi_dbg_dump_ec_hdr(ec_hdr); - ubi_dbg_dump_stack(); - return 1; + ubi_dump_ec_hdr(ec_hdr); + dump_stack(); + return -EINVAL; } /** - * paranoid_check_peb_ec_hdr - check that the erase counter header of a - * physical eraseblock is in-place and is all right. + * self_check_peb_ec_hdr - check erase counter header. * @ubi: UBI device description object * @pnum: the physical eraseblock number to check * - * This function returns zero if the erase counter header is all right, %1 if - * not, and a negative error code if an error occurred. + * This function returns zero if the erase counter header is all right and and + * a negative error code if not or if an error occurred. */ -static int paranoid_check_peb_ec_hdr(const struct ubi_device *ubi, int pnum) +static int self_check_peb_ec_hdr(const struct ubi_device *ubi, int pnum) { int err; uint32_t crc, hdr_crc; struct ubi_ec_hdr *ec_hdr; + if (!ubi_dbg_chk_io(ubi)) + return 0; + ec_hdr = kzalloc(ubi->ec_hdr_alsize, GFP_NOFS); if (!ec_hdr) return -ENOMEM; err = ubi_io_read(ubi, ec_hdr, pnum, 0, UBI_EC_HDR_SIZE); - if (err && err != UBI_IO_BITFLIPS && err != -EBADMSG) + if (err && err != UBI_IO_BITFLIPS && !mtd_is_eccerr(err)) goto exit; crc = crc32(UBI_CRC32_INIT, ec_hdr, UBI_EC_HDR_SIZE_CRC); hdr_crc = be32_to_cpu(ec_hdr->hdr_crc); if (hdr_crc != crc) { ubi_err("bad CRC, calculated %#08x, read %#08x", crc, hdr_crc); - ubi_err("paranoid check failed for PEB %d", pnum); - ubi_dbg_dump_ec_hdr(ec_hdr); - ubi_dbg_dump_stack(); - err = 1; + ubi_err("self-check failed for PEB %d", pnum); + ubi_dump_ec_hdr(ec_hdr); + dump_stack(); + err = -EINVAL; goto exit; } - err = paranoid_check_ec_hdr(ubi, pnum, ec_hdr); + err = self_check_ec_hdr(ubi, pnum, ec_hdr); exit: kfree(ec_hdr); @@ -1129,20 +1210,23 @@ exit: } /** - * paranoid_check_vid_hdr - check that a volume identifier header is all right. + * self_check_vid_hdr - check that a volume identifier header is all right. * @ubi: UBI device description object * @pnum: physical eraseblock number the volume identifier header belongs to * @vid_hdr: the volume identifier header to check * * This function returns zero if the volume identifier header is all right, and - * %1 if not. + * %-EINVAL if not. */ -static int paranoid_check_vid_hdr(const struct ubi_device *ubi, int pnum, - const struct ubi_vid_hdr *vid_hdr) +static int self_check_vid_hdr(const struct ubi_device *ubi, int pnum, + const struct ubi_vid_hdr *vid_hdr) { int err; uint32_t magic; + if (!ubi_dbg_chk_io(ubi)) + return 0; + magic = be32_to_cpu(vid_hdr->magic); if (magic != UBI_VID_HDR_MAGIC) { ubi_err("bad VID header magic %#08x at PEB %d, must be %#08x", @@ -1152,36 +1236,38 @@ static int paranoid_check_vid_hdr(const struct ubi_device *ubi, int pnum, err = validate_vid_hdr(ubi, vid_hdr); if (err) { - ubi_err("paranoid check failed for PEB %d", pnum); + ubi_err("self-check failed for PEB %d", pnum); goto fail; } return err; fail: - ubi_err("paranoid check failed for PEB %d", pnum); - ubi_dbg_dump_vid_hdr(vid_hdr); - ubi_dbg_dump_stack(); - return 1; + ubi_err("self-check failed for PEB %d", pnum); + ubi_dump_vid_hdr(vid_hdr); + dump_stack(); + return -EINVAL; } /** - * paranoid_check_peb_vid_hdr - check that the volume identifier header of a - * physical eraseblock is in-place and is all right. + * self_check_peb_vid_hdr - check volume identifier header. * @ubi: UBI device description object * @pnum: the physical eraseblock number to check * * This function returns zero if the volume identifier header is all right, - * %1 if not, and a negative error code if an error occurred. + * and a negative error code if not or if an error occurred. */ -static int paranoid_check_peb_vid_hdr(const struct ubi_device *ubi, int pnum) +static int self_check_peb_vid_hdr(const struct ubi_device *ubi, int pnum) { int err; uint32_t crc, hdr_crc; struct ubi_vid_hdr *vid_hdr; void *p; + if (!ubi_dbg_chk_io(ubi)) + return 0; + vid_hdr = ubi_zalloc_vid_hdr(ubi, GFP_NOFS); if (!vid_hdr) return -ENOMEM; @@ -1189,22 +1275,22 @@ static int paranoid_check_peb_vid_hdr(const struct ubi_device *ubi, int pnum) p = (char *)vid_hdr - ubi->vid_hdr_shift; err = ubi_io_read(ubi, p, pnum, ubi->vid_hdr_aloffset, ubi->vid_hdr_alsize); - if (err && err != UBI_IO_BITFLIPS && err != -EBADMSG) + if (err && err != UBI_IO_BITFLIPS && !mtd_is_eccerr(err)) goto exit; crc = crc32(UBI_CRC32_INIT, vid_hdr, UBI_EC_HDR_SIZE_CRC); hdr_crc = be32_to_cpu(vid_hdr->hdr_crc); if (hdr_crc != crc) { - ubi_err("bad VID header CRC at PEB %d, calculated %#08x, " - "read %#08x", pnum, crc, hdr_crc); - ubi_err("paranoid check failed for PEB %d", pnum); - ubi_dbg_dump_vid_hdr(vid_hdr); - ubi_dbg_dump_stack(); - err = 1; + ubi_err("bad VID header CRC at PEB %d, calculated %#08x, read %#08x", + pnum, crc, hdr_crc); + ubi_err("self-check failed for PEB %d", pnum); + ubi_dump_vid_hdr(vid_hdr); + dump_stack(); + err = -EINVAL; goto exit; } - err = paranoid_check_vid_hdr(ubi, pnum, vid_hdr); + err = self_check_vid_hdr(ubi, pnum, vid_hdr); exit: ubi_free_vid_hdr(ubi, vid_hdr); @@ -1212,51 +1298,123 @@ exit: } /** - * paranoid_check_all_ff - check that a region of flash is empty. + * self_check_write - make sure write succeeded. + * @ubi: UBI device description object + * @buf: buffer with data which were written + * @pnum: physical eraseblock number the data were written to + * @offset: offset within the physical eraseblock the data were written to + * @len: how many bytes were written + * + * This functions reads data which were recently written and compares it with + * the original data buffer - the data have to match. Returns zero if the data + * match and a negative error code if not or in case of failure. + */ +static int self_check_write(struct ubi_device *ubi, const void *buf, int pnum, + int offset, int len) +{ + int err, i; + size_t read; + void *buf1; + loff_t addr = (loff_t)pnum * ubi->peb_size + offset; + + if (!ubi_dbg_chk_io(ubi)) + return 0; + + buf1 = __vmalloc(len, GFP_NOFS, PAGE_KERNEL); + if (!buf1) { + ubi_err("cannot allocate memory to check writes"); + return 0; + } + + err = mtd_read(ubi->mtd, addr, len, &read, buf1); + if (err && !mtd_is_bitflip(err)) + goto out_free; + + for (i = 0; i < len; i++) { + uint8_t c = ((uint8_t *)buf)[i]; + uint8_t c1 = ((uint8_t *)buf1)[i]; +#if !defined(CONFIG_UBI_SILENCE_MSG) + int dump_len = max_t(int, 128, len - i); +#endif + + if (c == c1) + continue; + + ubi_err("self-check failed for PEB %d:%d, len %d", + pnum, offset, len); + ubi_msg("data differ at position %d", i); + ubi_msg("hex dump of the original buffer from %d to %d", + i, i + dump_len); + print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET, 32, 1, + buf + i, dump_len, 1); + ubi_msg("hex dump of the read buffer from %d to %d", + i, i + dump_len); + print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET, 32, 1, + buf1 + i, dump_len, 1); + dump_stack(); + err = -EINVAL; + goto out_free; + } + + vfree(buf1); + return 0; + +out_free: + vfree(buf1); + return err; +} + +/** + * ubi_self_check_all_ff - check that a region of flash is empty. * @ubi: UBI device description object * @pnum: the physical eraseblock number to check * @offset: the starting offset within the physical eraseblock to check * @len: the length of the region to check * * This function returns zero if only 0xFF bytes are present at offset - * @offset of the physical eraseblock @pnum, %1 if not, and a negative error - * code if an error occurred. + * @offset of the physical eraseblock @pnum, and a negative error code if not + * or if an error occurred. */ -static int paranoid_check_all_ff(struct ubi_device *ubi, int pnum, int offset, - int len) +int ubi_self_check_all_ff(struct ubi_device *ubi, int pnum, int offset, int len) { size_t read; int err; + void *buf; loff_t addr = (loff_t)pnum * ubi->peb_size + offset; - mutex_lock(&ubi->dbg_buf_mutex); - err = mtd_read(ubi->mtd, addr, len, &read, ubi->dbg_peb_buf); - if (err && err != -EUCLEAN) { - ubi_err("error %d while reading %d bytes from PEB %d:%d, " - "read %zd bytes", err, len, pnum, offset, read); + if (!ubi_dbg_chk_io(ubi)) + return 0; + + buf = __vmalloc(len, GFP_NOFS, PAGE_KERNEL); + if (!buf) { + ubi_err("cannot allocate memory to check for 0xFFs"); + return 0; + } + + err = mtd_read(ubi->mtd, addr, len, &read, buf); + if (err && !mtd_is_bitflip(err)) { + ubi_err("error %d while reading %d bytes from PEB %d:%d, read %zd bytes", + err, len, pnum, offset, read); goto error; } - err = check_pattern(ubi->dbg_peb_buf, 0xFF, len); + err = ubi_check_pattern(buf, 0xFF, len); if (err == 0) { - ubi_err("flash region at PEB %d:%d, length %d does not " - "contain all 0xFF bytes", pnum, offset, len); + ubi_err("flash region at PEB %d:%d, length %d does not contain all 0xFF bytes", + pnum, offset, len); goto fail; } - mutex_unlock(&ubi->dbg_buf_mutex); + vfree(buf); return 0; fail: - ubi_err("paranoid check failed for PEB %d", pnum); - dbg_msg("hex dump of the %d-%d region", offset, offset + len); - print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET, 32, 1, - ubi->dbg_peb_buf, len, 1); - err = 1; + ubi_err("self-check failed for PEB %d", pnum); + ubi_msg("hex dump of the %d-%d region", offset, offset + len); + print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET, 32, 1, buf, len, 1); + err = -EINVAL; error: - ubi_dbg_dump_stack(); - mutex_unlock(&ubi->dbg_buf_mutex); + dump_stack(); + vfree(buf); return err; } - -#endif /* CONFIG_MTD_UBI_DEBUG_PARANOID */ diff --git a/drivers/mtd/ubi/kapi.c b/drivers/mtd/ubi/kapi.c index 63c56c9..0183c93 100644 --- a/drivers/mtd/ubi/kapi.c +++ b/drivers/mtd/ubi/kapi.c @@ -8,16 +8,43 @@ /* This file mostly implements UBI kernel API functions */ -#ifdef UBI_LINUX +#define __UBOOT__ +#ifndef __UBOOT__ #include <linux/module.h> -#include <linux/err.h> +#include <linux/slab.h> +#include <linux/namei.h> +#include <linux/fs.h> #include <asm/div64.h> +#else +#include <ubi_uboot.h> #endif +#include <linux/err.h> -#include <ubi_uboot.h> #include "ubi.h" /** + * ubi_do_get_device_info - get information about UBI device. + * @ubi: UBI device description object + * @di: the information is stored here + * + * This function is the same as 'ubi_get_device_info()', but it assumes the UBI + * device is locked and cannot disappear. + */ +void ubi_do_get_device_info(struct ubi_device *ubi, struct ubi_device_info *di) +{ + di->ubi_num = ubi->ubi_num; + di->leb_size = ubi->leb_size; + di->leb_start = ubi->leb_start; + di->min_io_size = ubi->min_io_size; + di->max_write_size = ubi->max_write_size; + di->ro_mode = ubi->ro_mode; +#ifndef __UBOOT__ + di->cdev = ubi->cdev.dev; +#endif +} +EXPORT_SYMBOL_GPL(ubi_do_get_device_info); + +/** * ubi_get_device_info - get information about UBI device. * @ubi_num: UBI device number * @di: the information is stored here @@ -31,33 +58,24 @@ int ubi_get_device_info(int ubi_num, struct ubi_device_info *di) if (ubi_num < 0 || ubi_num >= UBI_MAX_DEVICES) return -EINVAL; - ubi = ubi_get_device(ubi_num); if (!ubi) return -ENODEV; - - di->ubi_num = ubi->ubi_num; - di->leb_size = ubi->leb_size; - di->min_io_size = ubi->min_io_size; - di->ro_mode = ubi->ro_mode; - di->cdev = ubi->cdev.dev; - + ubi_do_get_device_info(ubi, di); ubi_put_device(ubi); return 0; } EXPORT_SYMBOL_GPL(ubi_get_device_info); /** - * ubi_get_volume_info - get information about UBI volume. - * @desc: volume descriptor + * ubi_do_get_volume_info - get information about UBI volume. + * @ubi: UBI device description object + * @vol: volume description object * @vi: the information is stored here */ -void ubi_get_volume_info(struct ubi_volume_desc *desc, - struct ubi_volume_info *vi) +void ubi_do_get_volume_info(struct ubi_device *ubi, struct ubi_volume *vol, + struct ubi_volume_info *vi) { - const struct ubi_volume *vol = desc->vol; - const struct ubi_device *ubi = vol->ubi; - vi->vol_id = vol->vol_id; vi->ubi_num = ubi->ubi_num; vi->size = vol->reserved_pebs; @@ -71,6 +89,17 @@ void ubi_get_volume_info(struct ubi_volume_desc *desc, vi->name = vol->name; vi->cdev = vol->cdev.dev; } + +/** + * ubi_get_volume_info - get information about UBI volume. + * @desc: volume descriptor + * @vi: the information is stored here + */ +void ubi_get_volume_info(struct ubi_volume_desc *desc, + struct ubi_volume_info *vi) +{ + ubi_do_get_volume_info(desc->vol->ubi, desc->vol, vi); +} EXPORT_SYMBOL_GPL(ubi_get_volume_info); /** @@ -98,7 +127,7 @@ struct ubi_volume_desc *ubi_open_volume(int ubi_num, int vol_id, int mode) struct ubi_device *ubi; struct ubi_volume *vol; - dbg_msg("open device %d volume %d, mode %d", ubi_num, vol_id, mode); + dbg_gen("open device %d, volume %d, mode %d", ubi_num, vol_id, mode); if (ubi_num < 0 || ubi_num >= UBI_MAX_DEVICES) return ERR_PTR(-EINVAL); @@ -188,6 +217,8 @@ out_free: kfree(desc); out_put_ubi: ubi_put_device(ubi); + ubi_err("cannot open device %d, volume %d, error %d", + ubi_num, vol_id, err); return ERR_PTR(err); } EXPORT_SYMBOL_GPL(ubi_open_volume); @@ -207,7 +238,7 @@ struct ubi_volume_desc *ubi_open_volume_nm(int ubi_num, const char *name, struct ubi_device *ubi; struct ubi_volume_desc *ret; - dbg_msg("open volume %s, mode %d", name, mode); + dbg_gen("open device %d, volume %s, mode %d", ubi_num, name, mode); if (!name) return ERR_PTR(-EINVAL); @@ -249,6 +280,45 @@ struct ubi_volume_desc *ubi_open_volume_nm(int ubi_num, const char *name, } EXPORT_SYMBOL_GPL(ubi_open_volume_nm); +#ifndef __UBOOT__ +/** + * ubi_open_volume_path - open UBI volume by its character device node path. + * @pathname: volume character device node path + * @mode: open mode + * + * This function is similar to 'ubi_open_volume()', but opens a volume the path + * to its character device node. + */ +struct ubi_volume_desc *ubi_open_volume_path(const char *pathname, int mode) +{ + int error, ubi_num, vol_id, mod; + struct inode *inode; + struct path path; + + dbg_gen("open volume %s, mode %d", pathname, mode); + + if (!pathname || !*pathname) + return ERR_PTR(-EINVAL); + + error = kern_path(pathname, LOOKUP_FOLLOW, &path); + if (error) + return ERR_PTR(error); + + inode = path.dentry->d_inode; + mod = inode->i_mode; + ubi_num = ubi_major2num(imajor(inode)); + vol_id = iminor(inode) - 1; + path_put(&path); + + if (!S_ISCHR(mod)) + return ERR_PTR(-EINVAL); + if (vol_id >= 0 && ubi_num >= 0) + return ubi_open_volume(ubi_num, vol_id, mode); + return ERR_PTR(-ENODEV); +} +EXPORT_SYMBOL_GPL(ubi_open_volume_path); +#endif + /** * ubi_close_volume - close UBI volume. * @desc: volume descriptor @@ -258,7 +328,8 @@ void ubi_close_volume(struct ubi_volume_desc *desc) struct ubi_volume *vol = desc->vol; struct ubi_device *ubi = vol->ubi; - dbg_msg("close volume %d, mode %d", vol->vol_id, desc->mode); + dbg_gen("close device %d, volume %d, mode %d", + ubi->ubi_num, vol->vol_id, desc->mode); spin_lock(&ubi->volumes_lock); switch (desc->mode) { @@ -315,7 +386,7 @@ int ubi_leb_read(struct ubi_volume_desc *desc, int lnum, char *buf, int offset, struct ubi_device *ubi = vol->ubi; int err, vol_id = vol->vol_id; - dbg_msg("read %d bytes from LEB %d:%d:%d", len, vol_id, lnum, offset); + dbg_gen("read %d bytes from LEB %d:%d:%d", len, vol_id, lnum, offset); if (vol_id < 0 || vol_id >= ubi->vtbl_slots || lnum < 0 || lnum >= vol->used_ebs || offset < 0 || len < 0 || @@ -353,11 +424,9 @@ EXPORT_SYMBOL_GPL(ubi_leb_read); * @buf: data to write * @offset: offset within the logical eraseblock where to write * @len: how many bytes to write - * @dtype: expected data type * * This function writes @len bytes of data from @buf to offset @offset of - * logical eraseblock @lnum. The @dtype argument describes expected lifetime of - * the data. + * logical eraseblock @lnum. * * This function takes care of physical eraseblock write failures. If write to * the physical eraseblock write operation fails, the logical eraseblock is @@ -374,13 +443,13 @@ EXPORT_SYMBOL_GPL(ubi_leb_read); * returns immediately with %-EBADF code. */ int ubi_leb_write(struct ubi_volume_desc *desc, int lnum, const void *buf, - int offset, int len, int dtype) + int offset, int len) { struct ubi_volume *vol = desc->vol; struct ubi_device *ubi = vol->ubi; int vol_id = vol->vol_id; - dbg_msg("write %d bytes to LEB %d:%d:%d", len, vol_id, lnum, offset); + dbg_gen("write %d bytes to LEB %d:%d:%d", len, vol_id, lnum, offset); if (vol_id < 0 || vol_id >= ubi->vtbl_slots) return -EINVAL; @@ -393,17 +462,13 @@ int ubi_leb_write(struct ubi_volume_desc *desc, int lnum, const void *buf, offset & (ubi->min_io_size - 1) || len & (ubi->min_io_size - 1)) return -EINVAL; - if (dtype != UBI_LONGTERM && dtype != UBI_SHORTTERM && - dtype != UBI_UNKNOWN) - return -EINVAL; - if (vol->upd_marker) return -EBADF; if (len == 0) return 0; - return ubi_eba_write_leb(ubi, vol, lnum, buf, offset, len, dtype); + return ubi_eba_write_leb(ubi, vol, lnum, buf, offset, len); } EXPORT_SYMBOL_GPL(ubi_leb_write); @@ -413,24 +478,23 @@ EXPORT_SYMBOL_GPL(ubi_leb_write); * @lnum: logical eraseblock number to change * @buf: data to write * @len: how many bytes to write - * @dtype: expected data type * * This function changes the contents of a logical eraseblock atomically. @buf * has to contain new logical eraseblock data, and @len - the length of the - * data, which has to be aligned. The length may be shorter then the logical + * data, which has to be aligned. The length may be shorter than the logical * eraseblock size, ant the logical eraseblock may be appended to more times * later on. This function guarantees that in case of an unclean reboot the old * contents is preserved. Returns zero in case of success and a negative error * code in case of failure. */ int ubi_leb_change(struct ubi_volume_desc *desc, int lnum, const void *buf, - int len, int dtype) + int len) { struct ubi_volume *vol = desc->vol; struct ubi_device *ubi = vol->ubi; int vol_id = vol->vol_id; - dbg_msg("atomically write %d bytes to LEB %d:%d", len, vol_id, lnum); + dbg_gen("atomically write %d bytes to LEB %d:%d", len, vol_id, lnum); if (vol_id < 0 || vol_id >= ubi->vtbl_slots) return -EINVAL; @@ -442,17 +506,13 @@ int ubi_leb_change(struct ubi_volume_desc *desc, int lnum, const void *buf, len > vol->usable_leb_size || len & (ubi->min_io_size - 1)) return -EINVAL; - if (dtype != UBI_LONGTERM && dtype != UBI_SHORTTERM && - dtype != UBI_UNKNOWN) - return -EINVAL; - if (vol->upd_marker) return -EBADF; if (len == 0) return 0; - return ubi_eba_atomic_leb_change(ubi, vol, lnum, buf, len, dtype); + return ubi_eba_atomic_leb_change(ubi, vol, lnum, buf, len); } EXPORT_SYMBOL_GPL(ubi_leb_change); @@ -474,7 +534,7 @@ int ubi_leb_erase(struct ubi_volume_desc *desc, int lnum) struct ubi_device *ubi = vol->ubi; int err; - dbg_msg("erase LEB %d:%d", vol->vol_id, lnum); + dbg_gen("erase LEB %d:%d", vol->vol_id, lnum); if (desc->mode == UBI_READONLY || vol->vol_type == UBI_STATIC_VOLUME) return -EROFS; @@ -489,7 +549,7 @@ int ubi_leb_erase(struct ubi_volume_desc *desc, int lnum) if (err) return err; - return ubi_wl_flush(ubi); + return ubi_wl_flush(ubi, vol->vol_id, lnum); } EXPORT_SYMBOL_GPL(ubi_leb_erase); @@ -500,7 +560,7 @@ EXPORT_SYMBOL_GPL(ubi_leb_erase); * * This function un-maps logical eraseblock @lnum and schedules the * corresponding physical eraseblock for erasure, so that it will eventually be - * physically erased in background. This operation is much faster then the + * physically erased in background. This operation is much faster than the * erase operation. * * Unlike erase, the un-map operation does not guarantee that the logical @@ -519,7 +579,7 @@ EXPORT_SYMBOL_GPL(ubi_leb_erase); * * The main and obvious use-case of this function is when the contents of a * logical eraseblock has to be re-written. Then it is much more efficient to - * first un-map it, then write new data, rather then first erase it, then write + * first un-map it, then write new data, rather than first erase it, then write * new data. Note, once new data has been written to the logical eraseblock, * UBI guarantees that the old contents has gone forever. In other words, if an * unclean reboot happens after the logical eraseblock has been un-mapped and @@ -534,7 +594,7 @@ int ubi_leb_unmap(struct ubi_volume_desc *desc, int lnum) struct ubi_volume *vol = desc->vol; struct ubi_device *ubi = vol->ubi; - dbg_msg("unmap LEB %d:%d", vol->vol_id, lnum); + dbg_gen("unmap LEB %d:%d", vol->vol_id, lnum); if (desc->mode == UBI_READONLY || vol->vol_type == UBI_STATIC_VOLUME) return -EROFS; @@ -550,13 +610,12 @@ int ubi_leb_unmap(struct ubi_volume_desc *desc, int lnum) EXPORT_SYMBOL_GPL(ubi_leb_unmap); /** - * ubi_leb_map - map logical erasblock to a physical eraseblock. + * ubi_leb_map - map logical eraseblock to a physical eraseblock. * @desc: volume descriptor * @lnum: logical eraseblock number - * @dtype: expected data type * * This function maps an un-mapped logical eraseblock @lnum to a physical - * eraseblock. This means, that after a successfull invocation of this + * eraseblock. This means, that after a successful invocation of this * function the logical eraseblock @lnum will be empty (contain only %0xFF * bytes) and be mapped to a physical eraseblock, even if an unclean reboot * happens. @@ -566,12 +625,12 @@ EXPORT_SYMBOL_GPL(ubi_leb_unmap); * eraseblock is already mapped, and other negative error codes in case of * other failures. */ -int ubi_leb_map(struct ubi_volume_desc *desc, int lnum, int dtype) +int ubi_leb_map(struct ubi_volume_desc *desc, int lnum) { struct ubi_volume *vol = desc->vol; struct ubi_device *ubi = vol->ubi; - dbg_msg("unmap LEB %d:%d", vol->vol_id, lnum); + dbg_gen("unmap LEB %d:%d", vol->vol_id, lnum); if (desc->mode == UBI_READONLY || vol->vol_type == UBI_STATIC_VOLUME) return -EROFS; @@ -579,17 +638,13 @@ int ubi_leb_map(struct ubi_volume_desc *desc, int lnum, int dtype) if (lnum < 0 || lnum >= vol->reserved_pebs) return -EINVAL; - if (dtype != UBI_LONGTERM && dtype != UBI_SHORTTERM && - dtype != UBI_UNKNOWN) - return -EINVAL; - if (vol->upd_marker) return -EBADF; if (vol->eba_tbl[lnum] >= 0) return -EBADMSG; - return ubi_eba_write_leb(ubi, vol, lnum, NULL, 0, 0, dtype); + return ubi_eba_write_leb(ubi, vol, lnum, NULL, 0, 0); } EXPORT_SYMBOL_GPL(ubi_leb_map); @@ -613,7 +668,7 @@ int ubi_is_mapped(struct ubi_volume_desc *desc, int lnum) { struct ubi_volume *vol = desc->vol; - dbg_msg("test LEB %d:%d", vol->vol_id, lnum); + dbg_gen("test LEB %d:%d", vol->vol_id, lnum); if (lnum < 0 || lnum >= vol->reserved_pebs) return -EINVAL; @@ -624,3 +679,110 @@ int ubi_is_mapped(struct ubi_volume_desc *desc, int lnum) return vol->eba_tbl[lnum] >= 0; } EXPORT_SYMBOL_GPL(ubi_is_mapped); + +/** + * ubi_sync - synchronize UBI device buffers. + * @ubi_num: UBI device to synchronize + * + * The underlying MTD device may cache data in hardware or in software. This + * function ensures the caches are flushed. Returns zero in case of success and + * a negative error code in case of failure. + */ +int ubi_sync(int ubi_num) +{ + struct ubi_device *ubi; + + ubi = ubi_get_device(ubi_num); + if (!ubi) + return -ENODEV; + + mtd_sync(ubi->mtd); + ubi_put_device(ubi); + return 0; +} +EXPORT_SYMBOL_GPL(ubi_sync); + +/** + * ubi_flush - flush UBI work queue. + * @ubi_num: UBI device to flush work queue + * @vol_id: volume id to flush for + * @lnum: logical eraseblock number to flush for + * + * This function executes all pending works for a particular volume id / logical + * eraseblock number pair. If either value is set to %UBI_ALL, then it acts as + * a wildcard for all of the corresponding volume numbers or logical + * eraseblock numbers. It returns zero in case of success and a negative error + * code in case of failure. + */ +int ubi_flush(int ubi_num, int vol_id, int lnum) +{ + struct ubi_device *ubi; + int err = 0; + + ubi = ubi_get_device(ubi_num); + if (!ubi) + return -ENODEV; + + err = ubi_wl_flush(ubi, vol_id, lnum); + ubi_put_device(ubi); + return err; +} +EXPORT_SYMBOL_GPL(ubi_flush); + +#ifndef __UBOOT__ +BLOCKING_NOTIFIER_HEAD(ubi_notifiers); + +/** + * ubi_register_volume_notifier - register a volume notifier. + * @nb: the notifier description object + * @ignore_existing: if non-zero, do not send "added" notification for all + * already existing volumes + * + * This function registers a volume notifier, which means that + * 'nb->notifier_call()' will be invoked when an UBI volume is created, + * removed, re-sized, re-named, or updated. The first argument of the function + * is the notification type. The second argument is pointer to a + * &struct ubi_notification object which describes the notification event. + * Using UBI API from the volume notifier is prohibited. + * + * This function returns zero in case of success and a negative error code + * in case of failure. + */ +int ubi_register_volume_notifier(struct notifier_block *nb, + int ignore_existing) +{ + int err; + + err = blocking_notifier_chain_register(&ubi_notifiers, nb); + if (err != 0) + return err; + if (ignore_existing) + return 0; + + /* + * We are going to walk all UBI devices and all volumes, and + * notify the user about existing volumes by the %UBI_VOLUME_ADDED + * event. We have to lock the @ubi_devices_mutex to make sure UBI + * devices do not disappear. + */ + mutex_lock(&ubi_devices_mutex); + ubi_enumerate_volumes(nb); + mutex_unlock(&ubi_devices_mutex); + + return err; +} +EXPORT_SYMBOL_GPL(ubi_register_volume_notifier); + +/** + * ubi_unregister_volume_notifier - unregister the volume notifier. + * @nb: the notifier description object + * + * This function unregisters volume notifier @nm and returns zero in case of + * success and a negative error code in case of failure. + */ +int ubi_unregister_volume_notifier(struct notifier_block *nb) +{ + return blocking_notifier_chain_unregister(&ubi_notifiers, nb); +} +EXPORT_SYMBOL_GPL(ubi_unregister_volume_notifier); +#endif diff --git a/drivers/mtd/ubi/misc.c b/drivers/mtd/ubi/misc.c index 5ff55b4..49530b7 100644 --- a/drivers/mtd/ubi/misc.c +++ b/drivers/mtd/ubi/misc.c @@ -81,14 +81,62 @@ int ubi_check_volume(struct ubi_device *ubi, int vol_id) } /** - * ubi_calculate_rsvd_pool - calculate how many PEBs must be reserved for bad + * ubi_update_reserved - update bad eraseblock handling accounting data. + * @ubi: UBI device description object + * + * This function calculates the gap between current number of PEBs reserved for + * bad eraseblock handling and the required level of PEBs that must be + * reserved, and if necessary, reserves more PEBs to fill that gap, according + * to availability. Should be called with ubi->volumes_lock held. + */ +void ubi_update_reserved(struct ubi_device *ubi) +{ + int need = ubi->beb_rsvd_level - ubi->beb_rsvd_pebs; + + if (need <= 0 || ubi->avail_pebs == 0) + return; + + need = min_t(int, need, ubi->avail_pebs); + ubi->avail_pebs -= need; + ubi->rsvd_pebs += need; + ubi->beb_rsvd_pebs += need; + ubi_msg("reserved more %d PEBs for bad PEB handling", need); +} + +/** + * ubi_calculate_reserved - calculate how many PEBs must be reserved for bad * eraseblock handling. * @ubi: UBI device description object */ void ubi_calculate_reserved(struct ubi_device *ubi) { - ubi->beb_rsvd_level = ubi->good_peb_count/100; - ubi->beb_rsvd_level *= CONFIG_MTD_UBI_BEB_RESERVE; - if (ubi->beb_rsvd_level < MIN_RESEVED_PEBS) - ubi->beb_rsvd_level = MIN_RESEVED_PEBS; + /* + * Calculate the actual number of PEBs currently needed to be reserved + * for future bad eraseblock handling. + */ + ubi->beb_rsvd_level = ubi->bad_peb_limit - ubi->bad_peb_count; + if (ubi->beb_rsvd_level < 0) { + ubi->beb_rsvd_level = 0; + ubi_warn("number of bad PEBs (%d) is above the expected limit (%d), not reserving any PEBs for bad PEB handling, will use available PEBs (if any)", + ubi->bad_peb_count, ubi->bad_peb_limit); + } +} + +/** + * ubi_check_pattern - check if buffer contains only a certain byte pattern. + * @buf: buffer to check + * @patt: the pattern to check + * @size: buffer size in bytes + * + * This function returns %1 in there are only @patt bytes in @buf, and %0 if + * something else was also found. + */ +int ubi_check_pattern(const void *buf, uint8_t patt, int size) +{ + int i; + + for (i = 0; i < size; i++) + if (((const uint8_t *)buf)[i] != patt) + return 0; + return 1; } diff --git a/drivers/mtd/ubi/scan.c b/drivers/mtd/ubi/scan.c deleted file mode 100644 index a6d0fbc..0000000 --- a/drivers/mtd/ubi/scan.c +++ /dev/null @@ -1,1348 +0,0 @@ -/* - * Copyright (c) International Business Machines Corp., 2006 - * - * SPDX-License-Identifier: GPL-2.0+ - * - * Author: Artem Bityutskiy (Битюцкий Артём) - */ - -/* - * UBI scanning unit. - * - * This unit is responsible for scanning the flash media, checking UBI - * headers and providing complete information about the UBI flash image. - * - * The scanning information is represented by a &struct ubi_scan_info' object. - * Information about found volumes is represented by &struct ubi_scan_volume - * objects which are kept in volume RB-tree with root at the @volumes field. - * The RB-tree is indexed by the volume ID. - * - * Found logical eraseblocks are represented by &struct ubi_scan_leb objects. - * These objects are kept in per-volume RB-trees with the root at the - * corresponding &struct ubi_scan_volume object. To put it differently, we keep - * an RB-tree of per-volume objects and each of these objects is the root of - * RB-tree of per-eraseblock objects. - * - * Corrupted physical eraseblocks are put to the @corr list, free physical - * eraseblocks are put to the @free list and the physical eraseblock to be - * erased are put to the @erase list. - */ - -#ifdef UBI_LINUX -#include <linux/err.h> -#include <linux/crc32.h> -#include <asm/div64.h> -#endif - -#include <ubi_uboot.h> -#include "ubi.h" - -#ifdef CONFIG_MTD_UBI_DEBUG_PARANOID -static int paranoid_check_si(struct ubi_device *ubi, struct ubi_scan_info *si); -#else -#define paranoid_check_si(ubi, si) 0 -#endif - -/* Temporary variables used during scanning */ -static struct ubi_ec_hdr *ech; -static struct ubi_vid_hdr *vidh; - -/** - * add_to_list - add physical eraseblock to a list. - * @si: scanning information - * @pnum: physical eraseblock number to add - * @ec: erase counter of the physical eraseblock - * @list: the list to add to - * - * This function adds physical eraseblock @pnum to free, erase, corrupted or - * alien lists. Returns zero in case of success and a negative error code in - * case of failure. - */ -static int add_to_list(struct ubi_scan_info *si, int pnum, int ec, - struct list_head *list) -{ - struct ubi_scan_leb *seb; - - if (list == &si->free) - dbg_bld("add to free: PEB %d, EC %d", pnum, ec); - else if (list == &si->erase) - dbg_bld("add to erase: PEB %d, EC %d", pnum, ec); - else if (list == &si->corr) - dbg_bld("add to corrupted: PEB %d, EC %d", pnum, ec); - else if (list == &si->alien) - dbg_bld("add to alien: PEB %d, EC %d", pnum, ec); - else - BUG(); - - seb = kmalloc(sizeof(struct ubi_scan_leb), GFP_KERNEL); - if (!seb) - return -ENOMEM; - - seb->pnum = pnum; - seb->ec = ec; - list_add_tail(&seb->u.list, list); - return 0; -} - -/** - * validate_vid_hdr - check that volume identifier header is correct and - * consistent. - * @vid_hdr: the volume identifier header to check - * @sv: information about the volume this logical eraseblock belongs to - * @pnum: physical eraseblock number the VID header came from - * - * This function checks that data stored in @vid_hdr is consistent. Returns - * non-zero if an inconsistency was found and zero if not. - * - * Note, UBI does sanity check of everything it reads from the flash media. - * Most of the checks are done in the I/O unit. Here we check that the - * information in the VID header is consistent to the information in other VID - * headers of the same volume. - */ -static int validate_vid_hdr(const struct ubi_vid_hdr *vid_hdr, - const struct ubi_scan_volume *sv, int pnum) -{ - int vol_type = vid_hdr->vol_type; - int vol_id = be32_to_cpu(vid_hdr->vol_id); - int used_ebs = be32_to_cpu(vid_hdr->used_ebs); - int data_pad = be32_to_cpu(vid_hdr->data_pad); - - if (sv->leb_count != 0) { - int sv_vol_type; - - /* - * This is not the first logical eraseblock belonging to this - * volume. Ensure that the data in its VID header is consistent - * to the data in previous logical eraseblock headers. - */ - - if (vol_id != sv->vol_id) { - dbg_err("inconsistent vol_id"); - goto bad; - } - - if (sv->vol_type == UBI_STATIC_VOLUME) - sv_vol_type = UBI_VID_STATIC; - else - sv_vol_type = UBI_VID_DYNAMIC; - - if (vol_type != sv_vol_type) { - dbg_err("inconsistent vol_type"); - goto bad; - } - - if (used_ebs != sv->used_ebs) { - dbg_err("inconsistent used_ebs"); - goto bad; - } - - if (data_pad != sv->data_pad) { - dbg_err("inconsistent data_pad"); - goto bad; - } - } - - return 0; - -bad: - ubi_err("inconsistent VID header at PEB %d", pnum); - ubi_dbg_dump_vid_hdr(vid_hdr); - ubi_dbg_dump_sv(sv); - return -EINVAL; -} - -/** - * add_volume - add volume to the scanning information. - * @si: scanning information - * @vol_id: ID of the volume to add - * @pnum: physical eraseblock number - * @vid_hdr: volume identifier header - * - * If the volume corresponding to the @vid_hdr logical eraseblock is already - * present in the scanning information, this function does nothing. Otherwise - * it adds corresponding volume to the scanning information. Returns a pointer - * to the scanning volume object in case of success and a negative error code - * in case of failure. - */ -static struct ubi_scan_volume *add_volume(struct ubi_scan_info *si, int vol_id, - int pnum, - const struct ubi_vid_hdr *vid_hdr) -{ - struct ubi_scan_volume *sv; - struct rb_node **p = &si->volumes.rb_node, *parent = NULL; - - ubi_assert(vol_id == be32_to_cpu(vid_hdr->vol_id)); - - /* Walk the volume RB-tree to look if this volume is already present */ - while (*p) { - parent = *p; - sv = rb_entry(parent, struct ubi_scan_volume, rb); - - if (vol_id == sv->vol_id) - return sv; - - if (vol_id > sv->vol_id) - p = &(*p)->rb_left; - else - p = &(*p)->rb_right; - } - - /* The volume is absent - add it */ - sv = kmalloc(sizeof(struct ubi_scan_volume), GFP_KERNEL); - if (!sv) - return ERR_PTR(-ENOMEM); - - sv->highest_lnum = sv->leb_count = 0; - sv->vol_id = vol_id; - sv->root = RB_ROOT; - sv->used_ebs = be32_to_cpu(vid_hdr->used_ebs); - sv->data_pad = be32_to_cpu(vid_hdr->data_pad); - sv->compat = vid_hdr->compat; - sv->vol_type = vid_hdr->vol_type == UBI_VID_DYNAMIC ? UBI_DYNAMIC_VOLUME - : UBI_STATIC_VOLUME; - if (vol_id > si->highest_vol_id) - si->highest_vol_id = vol_id; - - rb_link_node(&sv->rb, parent, p); - rb_insert_color(&sv->rb, &si->volumes); - si->vols_found += 1; - dbg_bld("added volume %d", vol_id); - return sv; -} - -/** - * compare_lebs - find out which logical eraseblock is newer. - * @ubi: UBI device description object - * @seb: first logical eraseblock to compare - * @pnum: physical eraseblock number of the second logical eraseblock to - * compare - * @vid_hdr: volume identifier header of the second logical eraseblock - * - * This function compares 2 copies of a LEB and informs which one is newer. In - * case of success this function returns a positive value, in case of failure, a - * negative error code is returned. The success return codes use the following - * bits: - * o bit 0 is cleared: the first PEB (described by @seb) is newer then the - * second PEB (described by @pnum and @vid_hdr); - * o bit 0 is set: the second PEB is newer; - * o bit 1 is cleared: no bit-flips were detected in the newer LEB; - * o bit 1 is set: bit-flips were detected in the newer LEB; - * o bit 2 is cleared: the older LEB is not corrupted; - * o bit 2 is set: the older LEB is corrupted. - */ -static int compare_lebs(struct ubi_device *ubi, const struct ubi_scan_leb *seb, - int pnum, const struct ubi_vid_hdr *vid_hdr) -{ - void *buf; - int len, err, second_is_newer, bitflips = 0, corrupted = 0; - uint32_t data_crc, crc; - struct ubi_vid_hdr *vh = NULL; - unsigned long long sqnum2 = be64_to_cpu(vid_hdr->sqnum); - - if (seb->sqnum == 0 && sqnum2 == 0) { - long long abs, v1 = seb->leb_ver, v2 = be32_to_cpu(vid_hdr->leb_ver); - - /* - * UBI constantly increases the logical eraseblock version - * number and it can overflow. Thus, we have to bear in mind - * that versions that are close to %0xFFFFFFFF are less then - * versions that are close to %0. - * - * The UBI WL unit guarantees that the number of pending tasks - * is not greater then %0x7FFFFFFF. So, if the difference - * between any two versions is greater or equivalent to - * %0x7FFFFFFF, there was an overflow and the logical - * eraseblock with lower version is actually newer then the one - * with higher version. - * - * FIXME: but this is anyway obsolete and will be removed at - * some point. - */ - dbg_bld("using old crappy leb_ver stuff"); - - if (v1 == v2) { - ubi_err("PEB %d and PEB %d have the same version %lld", - seb->pnum, pnum, v1); - return -EINVAL; - } - - abs = v1 - v2; - if (abs < 0) - abs = -abs; - - if (abs < 0x7FFFFFFF) - /* Non-overflow situation */ - second_is_newer = (v2 > v1); - else - second_is_newer = (v2 < v1); - } else - /* Obviously the LEB with lower sequence counter is older */ - second_is_newer = sqnum2 > seb->sqnum; - - /* - * Now we know which copy is newer. If the copy flag of the PEB with - * newer version is not set, then we just return, otherwise we have to - * check data CRC. For the second PEB we already have the VID header, - * for the first one - we'll need to re-read it from flash. - * - * FIXME: this may be optimized so that we wouldn't read twice. - */ - - if (second_is_newer) { - if (!vid_hdr->copy_flag) { - /* It is not a copy, so it is newer */ - dbg_bld("second PEB %d is newer, copy_flag is unset", - pnum); - return 1; - } - } else { - pnum = seb->pnum; - - vh = ubi_zalloc_vid_hdr(ubi, GFP_KERNEL); - if (!vh) - return -ENOMEM; - - err = ubi_io_read_vid_hdr(ubi, pnum, vh, 0); - if (err) { - if (err == UBI_IO_BITFLIPS) - bitflips = 1; - else { - dbg_err("VID of PEB %d header is bad, but it " - "was OK earlier", pnum); - if (err > 0) - err = -EIO; - - goto out_free_vidh; - } - } - - if (!vh->copy_flag) { - /* It is not a copy, so it is newer */ - dbg_bld("first PEB %d is newer, copy_flag is unset", - pnum); - err = bitflips << 1; - goto out_free_vidh; - } - - vid_hdr = vh; - } - - /* Read the data of the copy and check the CRC */ - - len = be32_to_cpu(vid_hdr->data_size); - buf = vmalloc(len); - if (!buf) { - err = -ENOMEM; - goto out_free_vidh; - } - - err = ubi_io_read_data(ubi, buf, pnum, 0, len); - if (err && err != UBI_IO_BITFLIPS) - goto out_free_buf; - - data_crc = be32_to_cpu(vid_hdr->data_crc); - crc = crc32(UBI_CRC32_INIT, buf, len); - if (crc != data_crc) { - dbg_bld("PEB %d CRC error: calculated %#08x, must be %#08x", - pnum, crc, data_crc); - corrupted = 1; - bitflips = 0; - second_is_newer = !second_is_newer; - } else { - dbg_bld("PEB %d CRC is OK", pnum); - bitflips = !!err; - } - - vfree(buf); - ubi_free_vid_hdr(ubi, vh); - - if (second_is_newer) - dbg_bld("second PEB %d is newer, copy_flag is set", pnum); - else - dbg_bld("first PEB %d is newer, copy_flag is set", pnum); - - return second_is_newer | (bitflips << 1) | (corrupted << 2); - -out_free_buf: - vfree(buf); -out_free_vidh: - ubi_free_vid_hdr(ubi, vh); - return err; -} - -/** - * ubi_scan_add_used - add information about a physical eraseblock to the - * scanning information. - * @ubi: UBI device description object - * @si: scanning information - * @pnum: the physical eraseblock number - * @ec: erase counter - * @vid_hdr: the volume identifier header - * @bitflips: if bit-flips were detected when this physical eraseblock was read - * - * This function adds information about a used physical eraseblock to the - * 'used' tree of the corresponding volume. The function is rather complex - * because it has to handle cases when this is not the first physical - * eraseblock belonging to the same logical eraseblock, and the newer one has - * to be picked, while the older one has to be dropped. This function returns - * zero in case of success and a negative error code in case of failure. - */ -int ubi_scan_add_used(struct ubi_device *ubi, struct ubi_scan_info *si, - int pnum, int ec, const struct ubi_vid_hdr *vid_hdr, - int bitflips) -{ - int err, vol_id, lnum; - uint32_t leb_ver; - unsigned long long sqnum; - struct ubi_scan_volume *sv; - struct ubi_scan_leb *seb; - struct rb_node **p, *parent = NULL; - - vol_id = be32_to_cpu(vid_hdr->vol_id); - lnum = be32_to_cpu(vid_hdr->lnum); - sqnum = be64_to_cpu(vid_hdr->sqnum); - leb_ver = be32_to_cpu(vid_hdr->leb_ver); - - dbg_bld("PEB %d, LEB %d:%d, EC %d, sqnum %llu, ver %u, bitflips %d", - pnum, vol_id, lnum, ec, sqnum, leb_ver, bitflips); - - sv = add_volume(si, vol_id, pnum, vid_hdr); - if (IS_ERR(sv) < 0) - return PTR_ERR(sv); - - if (si->max_sqnum < sqnum) - si->max_sqnum = sqnum; - - /* - * Walk the RB-tree of logical eraseblocks of volume @vol_id to look - * if this is the first instance of this logical eraseblock or not. - */ - p = &sv->root.rb_node; - while (*p) { - int cmp_res; - - parent = *p; - seb = rb_entry(parent, struct ubi_scan_leb, u.rb); - if (lnum != seb->lnum) { - if (lnum < seb->lnum) - p = &(*p)->rb_left; - else - p = &(*p)->rb_right; - continue; - } - - /* - * There is already a physical eraseblock describing the same - * logical eraseblock present. - */ - - dbg_bld("this LEB already exists: PEB %d, sqnum %llu, " - "LEB ver %u, EC %d", seb->pnum, seb->sqnum, - seb->leb_ver, seb->ec); - - /* - * Make sure that the logical eraseblocks have different - * versions. Otherwise the image is bad. - */ - if (seb->leb_ver == leb_ver && leb_ver != 0) { - ubi_err("two LEBs with same version %u", leb_ver); - ubi_dbg_dump_seb(seb, 0); - ubi_dbg_dump_vid_hdr(vid_hdr); - return -EINVAL; - } - - /* - * Make sure that the logical eraseblocks have different - * sequence numbers. Otherwise the image is bad. - * - * FIXME: remove 'sqnum != 0' check when leb_ver is removed. - */ - if (seb->sqnum == sqnum && sqnum != 0) { - ubi_err("two LEBs with same sequence number %llu", - sqnum); - ubi_dbg_dump_seb(seb, 0); - ubi_dbg_dump_vid_hdr(vid_hdr); - return -EINVAL; - } - - /* - * Now we have to drop the older one and preserve the newer - * one. - */ - cmp_res = compare_lebs(ubi, seb, pnum, vid_hdr); - if (cmp_res < 0) - return cmp_res; - - if (cmp_res & 1) { - /* - * This logical eraseblock is newer then the one - * found earlier. - */ - err = validate_vid_hdr(vid_hdr, sv, pnum); - if (err) - return err; - - if (cmp_res & 4) - err = add_to_list(si, seb->pnum, seb->ec, - &si->corr); - else - err = add_to_list(si, seb->pnum, seb->ec, - &si->erase); - if (err) - return err; - - seb->ec = ec; - seb->pnum = pnum; - seb->scrub = ((cmp_res & 2) || bitflips); - seb->sqnum = sqnum; - seb->leb_ver = leb_ver; - - if (sv->highest_lnum == lnum) - sv->last_data_size = - be32_to_cpu(vid_hdr->data_size); - - return 0; - } else { - /* - * This logical eraseblock is older then the one found - * previously. - */ - if (cmp_res & 4) - return add_to_list(si, pnum, ec, &si->corr); - else - return add_to_list(si, pnum, ec, &si->erase); - } - } - - /* - * We've met this logical eraseblock for the first time, add it to the - * scanning information. - */ - - err = validate_vid_hdr(vid_hdr, sv, pnum); - if (err) - return err; - - seb = kmalloc(sizeof(struct ubi_scan_leb), GFP_KERNEL); - if (!seb) - return -ENOMEM; - - seb->ec = ec; - seb->pnum = pnum; - seb->lnum = lnum; - seb->sqnum = sqnum; - seb->scrub = bitflips; - seb->leb_ver = leb_ver; - - if (sv->highest_lnum <= lnum) { - sv->highest_lnum = lnum; - sv->last_data_size = be32_to_cpu(vid_hdr->data_size); - } - - sv->leb_count += 1; - rb_link_node(&seb->u.rb, parent, p); - rb_insert_color(&seb->u.rb, &sv->root); - return 0; -} - -/** - * ubi_scan_find_sv - find information about a particular volume in the - * scanning information. - * @si: scanning information - * @vol_id: the requested volume ID - * - * This function returns a pointer to the volume description or %NULL if there - * are no data about this volume in the scanning information. - */ -struct ubi_scan_volume *ubi_scan_find_sv(const struct ubi_scan_info *si, - int vol_id) -{ - struct ubi_scan_volume *sv; - struct rb_node *p = si->volumes.rb_node; - - while (p) { - sv = rb_entry(p, struct ubi_scan_volume, rb); - - if (vol_id == sv->vol_id) - return sv; - - if (vol_id > sv->vol_id) - p = p->rb_left; - else - p = p->rb_right; - } - - return NULL; -} - -/** - * ubi_scan_find_seb - find information about a particular logical - * eraseblock in the volume scanning information. - * @sv: a pointer to the volume scanning information - * @lnum: the requested logical eraseblock - * - * This function returns a pointer to the scanning logical eraseblock or %NULL - * if there are no data about it in the scanning volume information. - */ -struct ubi_scan_leb *ubi_scan_find_seb(const struct ubi_scan_volume *sv, - int lnum) -{ - struct ubi_scan_leb *seb; - struct rb_node *p = sv->root.rb_node; - - while (p) { - seb = rb_entry(p, struct ubi_scan_leb, u.rb); - - if (lnum == seb->lnum) - return seb; - - if (lnum > seb->lnum) - p = p->rb_left; - else - p = p->rb_right; - } - - return NULL; -} - -/** - * ubi_scan_rm_volume - delete scanning information about a volume. - * @si: scanning information - * @sv: the volume scanning information to delete - */ -void ubi_scan_rm_volume(struct ubi_scan_info *si, struct ubi_scan_volume *sv) -{ - struct rb_node *rb; - struct ubi_scan_leb *seb; - - dbg_bld("remove scanning information about volume %d", sv->vol_id); - - while ((rb = rb_first(&sv->root))) { - seb = rb_entry(rb, struct ubi_scan_leb, u.rb); - rb_erase(&seb->u.rb, &sv->root); - list_add_tail(&seb->u.list, &si->erase); - } - - rb_erase(&sv->rb, &si->volumes); - kfree(sv); - si->vols_found -= 1; -} - -/** - * ubi_scan_erase_peb - erase a physical eraseblock. - * @ubi: UBI device description object - * @si: scanning information - * @pnum: physical eraseblock number to erase; - * @ec: erase counter value to write (%UBI_SCAN_UNKNOWN_EC if it is unknown) - * - * This function erases physical eraseblock 'pnum', and writes the erase - * counter header to it. This function should only be used on UBI device - * initialization stages, when the EBA unit had not been yet initialized. This - * function returns zero in case of success and a negative error code in case - * of failure. - */ -int ubi_scan_erase_peb(struct ubi_device *ubi, const struct ubi_scan_info *si, - int pnum, int ec) -{ - int err; - struct ubi_ec_hdr *ec_hdr; - - if ((long long)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 %d", pnum, ec); - return -EINVAL; - } - - ec_hdr = kzalloc(ubi->ec_hdr_alsize, GFP_KERNEL); - if (!ec_hdr) - return -ENOMEM; - - ec_hdr->ec = cpu_to_be64(ec); - - err = ubi_io_sync_erase(ubi, pnum, 0); - if (err < 0) - goto out_free; - - err = ubi_io_write_ec_hdr(ubi, pnum, ec_hdr); - -out_free: - kfree(ec_hdr); - return err; -} - -/** - * ubi_scan_get_free_peb - get a free physical eraseblock. - * @ubi: UBI device description object - * @si: scanning information - * - * This function returns a free physical eraseblock. It is supposed to be - * called on the UBI initialization stages when the wear-leveling unit is not - * initialized yet. This function picks a physical eraseblocks from one of the - * lists, writes the EC header if it is needed, and removes it from the list. - * - * This function returns scanning physical eraseblock information in case of - * success and an error code in case of failure. - */ -struct ubi_scan_leb *ubi_scan_get_free_peb(struct ubi_device *ubi, - struct ubi_scan_info *si) -{ - int err = 0, i; - struct ubi_scan_leb *seb; - - if (!list_empty(&si->free)) { - seb = list_entry(si->free.next, struct ubi_scan_leb, u.list); - list_del(&seb->u.list); - dbg_bld("return free PEB %d, EC %d", seb->pnum, seb->ec); - return seb; - } - - for (i = 0; i < 2; i++) { - struct list_head *head; - struct ubi_scan_leb *tmp_seb; - - if (i == 0) - head = &si->erase; - else - head = &si->corr; - - /* - * We try to erase the first physical eraseblock from the @head - * list and pick it if we succeed, or try to erase the - * next one if not. And so forth. We don't want to take care - * about bad eraseblocks here - they'll be handled later. - */ - list_for_each_entry_safe(seb, tmp_seb, head, u.list) { - if (seb->ec == UBI_SCAN_UNKNOWN_EC) - seb->ec = si->mean_ec; - - err = ubi_scan_erase_peb(ubi, si, seb->pnum, seb->ec+1); - if (err) - continue; - - seb->ec += 1; - list_del(&seb->u.list); - dbg_bld("return PEB %d, EC %d", seb->pnum, seb->ec); - return seb; - } - } - - ubi_err("no eraseblocks found"); - return ERR_PTR(-ENOSPC); -} - -/** - * process_eb - read UBI headers, check them and add corresponding data - * to the scanning information. - * @ubi: UBI device description object - * @si: scanning information - * @pnum: the physical eraseblock number - * - * This function returns a zero if the physical eraseblock was successfully - * handled and a negative error code in case of failure. - */ -static int process_eb(struct ubi_device *ubi, struct ubi_scan_info *si, int pnum) -{ - long long uninitialized_var(ec); - int err, bitflips = 0, vol_id, ec_corr = 0; - - dbg_bld("scan PEB %d", pnum); - - /* Skip bad physical eraseblocks */ - err = ubi_io_is_bad(ubi, pnum); - if (err < 0) - return err; - else if (err) { - /* - * FIXME: this is actually duty of the I/O unit to initialize - * this, but MTD does not provide enough information. - */ - si->bad_peb_count += 1; - return 0; - } - - err = ubi_io_read_ec_hdr(ubi, pnum, ech, 0); - if (err < 0) - return err; - else if (err == UBI_IO_BITFLIPS) - bitflips = 1; - else if (err == UBI_IO_PEB_EMPTY) - return add_to_list(si, pnum, UBI_SCAN_UNKNOWN_EC, &si->erase); - else if (err == UBI_IO_BAD_EC_HDR) { - /* - * We have to also look at the VID header, possibly it is not - * corrupted. Set %bitflips flag in order to make this PEB be - * moved and EC be re-created. - */ - ec_corr = 1; - ec = UBI_SCAN_UNKNOWN_EC; - bitflips = 1; - } - - si->is_empty = 0; - - if (!ec_corr) { - /* Make sure UBI version is OK */ - if (ech->version != UBI_VERSION) { - ubi_err("this UBI version is %d, image version is %d", - UBI_VERSION, (int)ech->version); - return -EINVAL; - } - - ec = be64_to_cpu(ech->ec); - if (ec > UBI_MAX_ERASECOUNTER) { - /* - * Erase counter overflow. The EC headers have 64 bits - * reserved, but we anyway make use of only 31 bit - * values, as this seems to be enough for any existing - * flash. Upgrade UBI and use 64-bit erase counters - * internally. - */ - ubi_err("erase counter overflow, max is %d", - UBI_MAX_ERASECOUNTER); - ubi_dbg_dump_ec_hdr(ech); - return -EINVAL; - } - } - - /* OK, we've done with the EC header, let's look at the VID header */ - - err = ubi_io_read_vid_hdr(ubi, pnum, vidh, 0); - if (err < 0) - return err; - else if (err == UBI_IO_BITFLIPS) - bitflips = 1; - else if (err == UBI_IO_BAD_VID_HDR || - (err == UBI_IO_PEB_FREE && ec_corr)) { - /* VID header is corrupted */ - err = add_to_list(si, pnum, ec, &si->corr); - if (err) - return err; - goto adjust_mean_ec; - } else if (err == UBI_IO_PEB_FREE) { - /* No VID header - the physical eraseblock is free */ - err = add_to_list(si, pnum, ec, &si->free); - if (err) - return err; - goto adjust_mean_ec; - } - - vol_id = be32_to_cpu(vidh->vol_id); - if (vol_id > UBI_MAX_VOLUMES && vol_id != UBI_LAYOUT_VOLUME_ID) { - int lnum = be32_to_cpu(vidh->lnum); - - /* Unsupported internal volume */ - switch (vidh->compat) { - case UBI_COMPAT_DELETE: - ubi_msg("\"delete\" compatible internal volume %d:%d" - " found, remove it", vol_id, lnum); - err = add_to_list(si, pnum, ec, &si->corr); - if (err) - return err; - break; - - case UBI_COMPAT_RO: - ubi_msg("read-only compatible internal volume %d:%d" - " found, switch to read-only mode", - vol_id, lnum); - ubi->ro_mode = 1; - break; - - case UBI_COMPAT_PRESERVE: - ubi_msg("\"preserve\" compatible internal volume %d:%d" - " found", vol_id, lnum); - err = add_to_list(si, pnum, ec, &si->alien); - if (err) - return err; - si->alien_peb_count += 1; - return 0; - - case UBI_COMPAT_REJECT: - ubi_err("incompatible internal volume %d:%d found", - vol_id, lnum); - return -EINVAL; - } - } - - /* Both UBI headers seem to be fine */ - err = ubi_scan_add_used(ubi, si, pnum, ec, vidh, bitflips); - if (err) - return err; - -adjust_mean_ec: - if (!ec_corr) { - si->ec_sum += ec; - si->ec_count += 1; - if (ec > si->max_ec) - si->max_ec = ec; - if (ec < si->min_ec) - si->min_ec = ec; - } - - return 0; -} - -/** - * ubi_scan - scan an MTD device. - * @ubi: UBI device description object - * - * This function does full scanning of an MTD device and returns complete - * information about it. In case of failure, an error code is returned. - */ -struct ubi_scan_info *ubi_scan(struct ubi_device *ubi) -{ - int err, pnum; - struct rb_node *rb1, *rb2; - struct ubi_scan_volume *sv; - struct ubi_scan_leb *seb; - struct ubi_scan_info *si; - - si = kzalloc(sizeof(struct ubi_scan_info), GFP_KERNEL); - if (!si) - return ERR_PTR(-ENOMEM); - - INIT_LIST_HEAD(&si->corr); - INIT_LIST_HEAD(&si->free); - INIT_LIST_HEAD(&si->erase); - INIT_LIST_HEAD(&si->alien); - si->volumes = RB_ROOT; - si->is_empty = 1; - - err = -ENOMEM; - ech = kzalloc(ubi->ec_hdr_alsize, GFP_KERNEL); - if (!ech) - goto out_si; - - vidh = ubi_zalloc_vid_hdr(ubi, GFP_KERNEL); - if (!vidh) - goto out_ech; - - for (pnum = 0; pnum < ubi->peb_count; pnum++) { - cond_resched(); - - dbg_msg("process PEB %d", pnum); - err = process_eb(ubi, si, pnum); - if (err < 0) - goto out_vidh; - } - - dbg_msg("scanning is finished"); - - /* Calculate mean erase counter */ - if (si->ec_count) { - do_div(si->ec_sum, si->ec_count); - si->mean_ec = si->ec_sum; - } - - if (si->is_empty) - ubi_msg("empty MTD device detected"); - - /* - * In case of unknown erase counter we use the mean erase counter - * value. - */ - ubi_rb_for_each_entry(rb1, sv, &si->volumes, rb) { - ubi_rb_for_each_entry(rb2, seb, &sv->root, u.rb) - if (seb->ec == UBI_SCAN_UNKNOWN_EC) - seb->ec = si->mean_ec; - } - - list_for_each_entry(seb, &si->free, u.list) { - if (seb->ec == UBI_SCAN_UNKNOWN_EC) - seb->ec = si->mean_ec; - } - - list_for_each_entry(seb, &si->corr, u.list) - if (seb->ec == UBI_SCAN_UNKNOWN_EC) - seb->ec = si->mean_ec; - - list_for_each_entry(seb, &si->erase, u.list) - if (seb->ec == UBI_SCAN_UNKNOWN_EC) - seb->ec = si->mean_ec; - - err = paranoid_check_si(ubi, si); - if (err) { - if (err > 0) - err = -EINVAL; - goto out_vidh; - } - - ubi_free_vid_hdr(ubi, vidh); - kfree(ech); - - return si; - -out_vidh: - ubi_free_vid_hdr(ubi, vidh); -out_ech: - kfree(ech); -out_si: - ubi_scan_destroy_si(si); - return ERR_PTR(err); -} - -/** - * destroy_sv - free the scanning volume information - * @sv: scanning volume information - * - * This function destroys the volume RB-tree (@sv->root) and the scanning - * volume information. - */ -static void destroy_sv(struct ubi_scan_volume *sv) -{ - struct ubi_scan_leb *seb; - struct rb_node *this = sv->root.rb_node; - - while (this) { - if (this->rb_left) - this = this->rb_left; - else if (this->rb_right) - this = this->rb_right; - else { - seb = rb_entry(this, struct ubi_scan_leb, u.rb); - this = rb_parent(this); - if (this) { - if (this->rb_left == &seb->u.rb) - this->rb_left = NULL; - else - this->rb_right = NULL; - } - - kfree(seb); - } - } - kfree(sv); -} - -/** - * ubi_scan_destroy_si - destroy scanning information. - * @si: scanning information - */ -void ubi_scan_destroy_si(struct ubi_scan_info *si) -{ - struct ubi_scan_leb *seb, *seb_tmp; - struct ubi_scan_volume *sv; - struct rb_node *rb; - - list_for_each_entry_safe(seb, seb_tmp, &si->alien, u.list) { - list_del(&seb->u.list); - kfree(seb); - } - list_for_each_entry_safe(seb, seb_tmp, &si->erase, u.list) { - list_del(&seb->u.list); - kfree(seb); - } - list_for_each_entry_safe(seb, seb_tmp, &si->corr, u.list) { - list_del(&seb->u.list); - kfree(seb); - } - list_for_each_entry_safe(seb, seb_tmp, &si->free, u.list) { - list_del(&seb->u.list); - kfree(seb); - } - - /* Destroy the volume RB-tree */ - rb = si->volumes.rb_node; - while (rb) { - if (rb->rb_left) - rb = rb->rb_left; - else if (rb->rb_right) - rb = rb->rb_right; - else { - sv = rb_entry(rb, struct ubi_scan_volume, rb); - - rb = rb_parent(rb); - if (rb) { - if (rb->rb_left == &sv->rb) - rb->rb_left = NULL; - else - rb->rb_right = NULL; - } - - destroy_sv(sv); - } - } - - kfree(si); -} - -#ifdef CONFIG_MTD_UBI_DEBUG_PARANOID - -/** - * paranoid_check_si - check if the scanning information is correct and - * consistent. - * @ubi: UBI device description object - * @si: scanning information - * - * This function returns zero if the scanning information is all right, %1 if - * not and a negative error code if an error occurred. - */ -static int paranoid_check_si(struct ubi_device *ubi, struct ubi_scan_info *si) -{ - int pnum, err, vols_found = 0; - struct rb_node *rb1, *rb2; - struct ubi_scan_volume *sv; - struct ubi_scan_leb *seb, *last_seb; - uint8_t *buf; - - /* - * At first, check that scanning information is OK. - */ - ubi_rb_for_each_entry(rb1, sv, &si->volumes, rb) { - int leb_count = 0; - - cond_resched(); - - vols_found += 1; - - if (si->is_empty) { - ubi_err("bad is_empty flag"); - goto bad_sv; - } - - if (sv->vol_id < 0 || sv->highest_lnum < 0 || - sv->leb_count < 0 || sv->vol_type < 0 || sv->used_ebs < 0 || - sv->data_pad < 0 || sv->last_data_size < 0) { - ubi_err("negative values"); - goto bad_sv; - } - - if (sv->vol_id >= UBI_MAX_VOLUMES && - sv->vol_id < UBI_INTERNAL_VOL_START) { - ubi_err("bad vol_id"); - goto bad_sv; - } - - if (sv->vol_id > si->highest_vol_id) { - ubi_err("highest_vol_id is %d, but vol_id %d is there", - si->highest_vol_id, sv->vol_id); - goto out; - } - - if (sv->vol_type != UBI_DYNAMIC_VOLUME && - sv->vol_type != UBI_STATIC_VOLUME) { - ubi_err("bad vol_type"); - goto bad_sv; - } - - if (sv->data_pad > ubi->leb_size / 2) { - ubi_err("bad data_pad"); - goto bad_sv; - } - - last_seb = NULL; - ubi_rb_for_each_entry(rb2, seb, &sv->root, u.rb) { - cond_resched(); - - last_seb = seb; - leb_count += 1; - - if (seb->pnum < 0 || seb->ec < 0) { - ubi_err("negative values"); - goto bad_seb; - } - - if (seb->ec < si->min_ec) { - ubi_err("bad si->min_ec (%d), %d found", - si->min_ec, seb->ec); - goto bad_seb; - } - - if (seb->ec > si->max_ec) { - ubi_err("bad si->max_ec (%d), %d found", - si->max_ec, seb->ec); - goto bad_seb; - } - - if (seb->pnum >= ubi->peb_count) { - ubi_err("too high PEB number %d, total PEBs %d", - seb->pnum, ubi->peb_count); - goto bad_seb; - } - - if (sv->vol_type == UBI_STATIC_VOLUME) { - if (seb->lnum >= sv->used_ebs) { - ubi_err("bad lnum or used_ebs"); - goto bad_seb; - } - } else { - if (sv->used_ebs != 0) { - ubi_err("non-zero used_ebs"); - goto bad_seb; - } - } - - if (seb->lnum > sv->highest_lnum) { - ubi_err("incorrect highest_lnum or lnum"); - goto bad_seb; - } - } - - if (sv->leb_count != leb_count) { - ubi_err("bad leb_count, %d objects in the tree", - leb_count); - goto bad_sv; - } - - if (!last_seb) - continue; - - seb = last_seb; - - if (seb->lnum != sv->highest_lnum) { - ubi_err("bad highest_lnum"); - goto bad_seb; - } - } - - if (vols_found != si->vols_found) { - ubi_err("bad si->vols_found %d, should be %d", - si->vols_found, vols_found); - goto out; - } - - /* Check that scanning information is correct */ - ubi_rb_for_each_entry(rb1, sv, &si->volumes, rb) { - last_seb = NULL; - ubi_rb_for_each_entry(rb2, seb, &sv->root, u.rb) { - int vol_type; - - cond_resched(); - - last_seb = seb; - - err = ubi_io_read_vid_hdr(ubi, seb->pnum, vidh, 1); - if (err && err != UBI_IO_BITFLIPS) { - ubi_err("VID header is not OK (%d)", err); - if (err > 0) - err = -EIO; - return err; - } - - vol_type = vidh->vol_type == UBI_VID_DYNAMIC ? - UBI_DYNAMIC_VOLUME : UBI_STATIC_VOLUME; - if (sv->vol_type != vol_type) { - ubi_err("bad vol_type"); - goto bad_vid_hdr; - } - - if (seb->sqnum != be64_to_cpu(vidh->sqnum)) { - ubi_err("bad sqnum %llu", seb->sqnum); - goto bad_vid_hdr; - } - - if (sv->vol_id != be32_to_cpu(vidh->vol_id)) { - ubi_err("bad vol_id %d", sv->vol_id); - goto bad_vid_hdr; - } - - if (sv->compat != vidh->compat) { - ubi_err("bad compat %d", vidh->compat); - goto bad_vid_hdr; - } - - if (seb->lnum != be32_to_cpu(vidh->lnum)) { - ubi_err("bad lnum %d", seb->lnum); - goto bad_vid_hdr; - } - - if (sv->used_ebs != be32_to_cpu(vidh->used_ebs)) { - ubi_err("bad used_ebs %d", sv->used_ebs); - goto bad_vid_hdr; - } - - if (sv->data_pad != be32_to_cpu(vidh->data_pad)) { - ubi_err("bad data_pad %d", sv->data_pad); - goto bad_vid_hdr; - } - - if (seb->leb_ver != be32_to_cpu(vidh->leb_ver)) { - ubi_err("bad leb_ver %u", seb->leb_ver); - goto bad_vid_hdr; - } - } - - if (!last_seb) - continue; - - if (sv->highest_lnum != be32_to_cpu(vidh->lnum)) { - ubi_err("bad highest_lnum %d", sv->highest_lnum); - goto bad_vid_hdr; - } - - if (sv->last_data_size != be32_to_cpu(vidh->data_size)) { - ubi_err("bad last_data_size %d", sv->last_data_size); - goto bad_vid_hdr; - } - } - - /* - * Make sure that all the physical eraseblocks are in one of the lists - * or trees. - */ - buf = kzalloc(ubi->peb_count, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - for (pnum = 0; pnum < ubi->peb_count; pnum++) { - err = ubi_io_is_bad(ubi, pnum); - if (err < 0) { - kfree(buf); - return err; - } - else if (err) - buf[pnum] = 1; - } - - ubi_rb_for_each_entry(rb1, sv, &si->volumes, rb) - ubi_rb_for_each_entry(rb2, seb, &sv->root, u.rb) - buf[seb->pnum] = 1; - - list_for_each_entry(seb, &si->free, u.list) - buf[seb->pnum] = 1; - - list_for_each_entry(seb, &si->corr, u.list) - buf[seb->pnum] = 1; - - list_for_each_entry(seb, &si->erase, u.list) - buf[seb->pnum] = 1; - - list_for_each_entry(seb, &si->alien, u.list) - buf[seb->pnum] = 1; - - err = 0; - for (pnum = 0; pnum < ubi->peb_count; pnum++) - if (!buf[pnum]) { - ubi_err("PEB %d is not referred", pnum); - err = 1; - } - - kfree(buf); - if (err) - goto out; - return 0; - -bad_seb: - ubi_err("bad scanning information about LEB %d", seb->lnum); - ubi_dbg_dump_seb(seb, 0); - ubi_dbg_dump_sv(sv); - goto out; - -bad_sv: - ubi_err("bad scanning information about volume %d", sv->vol_id); - ubi_dbg_dump_sv(sv); - goto out; - -bad_vid_hdr: - ubi_err("bad scanning information about volume %d", sv->vol_id); - ubi_dbg_dump_sv(sv); - ubi_dbg_dump_vid_hdr(vidh); - -out: - ubi_dbg_dump_stack(); - return 1; -} - -#endif /* CONFIG_MTD_UBI_DEBUG_PARANOID */ diff --git a/drivers/mtd/ubi/scan.h b/drivers/mtd/ubi/scan.h deleted file mode 100644 index 252b1f1..0000000 --- a/drivers/mtd/ubi/scan.h +++ /dev/null @@ -1,153 +0,0 @@ -/* - * Copyright (c) International Business Machines Corp., 2006 - * - * SPDX-License-Identifier: GPL-2.0+ - * - * Author: Artem Bityutskiy (Битюцкий Артём) - */ - -#ifndef __UBI_SCAN_H__ -#define __UBI_SCAN_H__ - -/* The erase counter value for this physical eraseblock is unknown */ -#define UBI_SCAN_UNKNOWN_EC (-1) - -/** - * struct ubi_scan_leb - scanning information about a physical eraseblock. - * @ec: erase counter (%UBI_SCAN_UNKNOWN_EC if it is unknown) - * @pnum: physical eraseblock number - * @lnum: logical eraseblock number - * @scrub: if this physical eraseblock needs scrubbing - * @sqnum: sequence number - * @u: unions RB-tree or @list links - * @u.rb: link in the per-volume RB-tree of &struct ubi_scan_leb objects - * @u.list: link in one of the eraseblock lists - * @leb_ver: logical eraseblock version (obsolete) - * - * One object of this type is allocated for each physical eraseblock during - * scanning. - */ -struct ubi_scan_leb { - int ec; - int pnum; - int lnum; - int scrub; - unsigned long long sqnum; - union { - struct rb_node rb; - struct list_head list; - } u; - uint32_t leb_ver; -}; - -/** - * struct ubi_scan_volume - scanning information about a volume. - * @vol_id: volume ID - * @highest_lnum: highest logical eraseblock number in this volume - * @leb_count: number of logical eraseblocks in this volume - * @vol_type: volume type - * @used_ebs: number of used logical eraseblocks in this volume (only for - * static volumes) - * @last_data_size: amount of data in the last logical eraseblock of this - * volume (always equivalent to the usable logical eraseblock size in case of - * dynamic volumes) - * @data_pad: how many bytes at the end of logical eraseblocks of this volume - * are not used (due to volume alignment) - * @compat: compatibility flags of this volume - * @rb: link in the volume RB-tree - * @root: root of the RB-tree containing all the eraseblock belonging to this - * volume (&struct ubi_scan_leb objects) - * - * One object of this type is allocated for each volume during scanning. - */ -struct ubi_scan_volume { - int vol_id; - int highest_lnum; - int leb_count; - int vol_type; - int used_ebs; - int last_data_size; - int data_pad; - int compat; - struct rb_node rb; - struct rb_root root; -}; - -/** - * struct ubi_scan_info - UBI scanning information. - * @volumes: root of the volume RB-tree - * @corr: list of corrupted physical eraseblocks - * @free: list of free physical eraseblocks - * @erase: list of physical eraseblocks which have to be erased - * @alien: list of physical eraseblocks which should not be used by UBI (e.g., - * @bad_peb_count: count of bad physical eraseblocks - * those belonging to "preserve"-compatible internal volumes) - * @vols_found: number of volumes found during scanning - * @highest_vol_id: highest volume ID - * @alien_peb_count: count of physical eraseblocks in the @alien list - * @is_empty: flag indicating whether the MTD device is empty or not - * @min_ec: lowest erase counter value - * @max_ec: highest erase counter value - * @max_sqnum: highest sequence number value - * @mean_ec: mean erase counter value - * @ec_sum: a temporary variable used when calculating @mean_ec - * @ec_count: a temporary variable used when calculating @mean_ec - * - * This data structure contains the result of scanning and may be used by other - * UBI units to build final UBI data structures, further error-recovery and so - * on. - */ -struct ubi_scan_info { - struct rb_root volumes; - struct list_head corr; - struct list_head free; - struct list_head erase; - struct list_head alien; - int bad_peb_count; - int vols_found; - int highest_vol_id; - int alien_peb_count; - int is_empty; - int min_ec; - int max_ec; - unsigned long long max_sqnum; - int mean_ec; - uint64_t ec_sum; - int ec_count; -}; - -struct ubi_device; -struct ubi_vid_hdr; - -/* - * ubi_scan_move_to_list - move a physical eraseblock from the volume tree to a - * list. - * - * @sv: volume scanning information - * @seb: scanning eraseblock infprmation - * @list: the list to move to - */ -static inline void ubi_scan_move_to_list(struct ubi_scan_volume *sv, - struct ubi_scan_leb *seb, - struct list_head *list) -{ - rb_erase(&seb->u.rb, &sv->root); - list_add_tail(&seb->u.list, list); -} - -int ubi_scan_add_used(struct ubi_device *ubi, struct ubi_scan_info *si, - int pnum, int ec, const struct ubi_vid_hdr *vid_hdr, - int bitflips); -struct ubi_scan_volume *ubi_scan_find_sv(const struct ubi_scan_info *si, - int vol_id); -struct ubi_scan_leb *ubi_scan_find_seb(const struct ubi_scan_volume *sv, - int lnum); -void ubi_scan_rm_volume(struct ubi_scan_info *si, struct ubi_scan_volume *sv); -struct ubi_scan_leb *ubi_scan_get_free_peb(struct ubi_device *ubi, - struct ubi_scan_info *si); -int ubi_scan_erase_peb(struct ubi_device *ubi, const struct ubi_scan_info *si, - int pnum, int ec); -struct ubi_scan_info *ubi_scan(struct ubi_device *ubi); -void ubi_scan_destroy_si(struct ubi_scan_info *si); - -#endif /* !__UBI_SCAN_H__ */ diff --git a/drivers/mtd/ubi/ubi-media.h b/drivers/mtd/ubi/ubi-media.h index 9012326..2809805 100644 --- a/drivers/mtd/ubi/ubi-media.h +++ b/drivers/mtd/ubi/ubi-media.h @@ -86,10 +86,11 @@ enum { * Compatibility constants used by internal volumes. * * @UBI_COMPAT_DELETE: delete this internal volume before anything is written - * to the flash + * to the flash * @UBI_COMPAT_RO: attach this device in read-only mode * @UBI_COMPAT_PRESERVE: preserve this internal volume - do not touch its - * physical eraseblocks, don't allow the wear-leveling unit to move them + * physical eraseblocks, don't allow the wear-leveling + * sub-system to move them * @UBI_COMPAT_REJECT: reject this UBI image */ enum { @@ -111,18 +112,19 @@ enum { * struct ubi_ec_hdr - UBI erase counter header. * @magic: erase counter header magic number (%UBI_EC_HDR_MAGIC) * @version: version of UBI implementation which is supposed to accept this - * UBI image + * UBI image * @padding1: reserved for future, zeroes * @ec: the erase counter * @vid_hdr_offset: where the VID header starts * @data_offset: where the user data start + * @image_seq: image sequence number * @padding2: reserved for future, zeroes * @hdr_crc: erase counter header CRC checksum * * The erase counter header takes 64 bytes and has a plenty of unused space for * future usage. The unused fields are zeroed. The @version field is used to * indicate the version of UBI implementation which is supposed to be able to - * work with this UBI image. If @version is greater then the current UBI + * work with this UBI image. If @version is greater than the current UBI * version, the image is rejected. This may be useful in future if something * is changed radically. This field is duplicated in the volume identifier * header. @@ -131,6 +133,14 @@ enum { * volume identifier header and user data, relative to the beginning of the * physical eraseblock. These values have to be the same for all physical * eraseblocks. + * + * The @image_seq field is used to validate a UBI image that has been prepared + * for a UBI device. The @image_seq value can be any value, but it must be the + * same on all eraseblocks. UBI will ensure that all new erase counter headers + * also contain this value, and will check the value when attaching the flash. + * One way to make use of @image_seq is to increase its value by one every time + * an image is flashed over an existing image, then, if the flashing does not + * complete, UBI will detect the error when attaching the media. */ struct ubi_ec_hdr { __be32 magic; @@ -139,32 +149,32 @@ struct ubi_ec_hdr { __be64 ec; /* Warning: the current limit is 31-bit anyway! */ __be32 vid_hdr_offset; __be32 data_offset; - __u8 padding2[36]; + __be32 image_seq; + __u8 padding2[32]; __be32 hdr_crc; -} __attribute__ ((packed)); +} __packed; /** * struct ubi_vid_hdr - on-flash UBI volume identifier header. * @magic: volume identifier header magic number (%UBI_VID_HDR_MAGIC) * @version: UBI implementation version which is supposed to accept this UBI - * image (%UBI_VERSION) + * image (%UBI_VERSION) * @vol_type: volume type (%UBI_VID_DYNAMIC or %UBI_VID_STATIC) * @copy_flag: if this logical eraseblock was copied from another physical - * eraseblock (for wear-leveling reasons) + * eraseblock (for wear-leveling reasons) * @compat: compatibility of this volume (%0, %UBI_COMPAT_DELETE, - * %UBI_COMPAT_IGNORE, %UBI_COMPAT_PRESERVE, or %UBI_COMPAT_REJECT) + * %UBI_COMPAT_IGNORE, %UBI_COMPAT_PRESERVE, or %UBI_COMPAT_REJECT) * @vol_id: ID of this volume * @lnum: logical eraseblock number - * @leb_ver: version of this logical eraseblock (IMPORTANT: obsolete, to be - * removed, kept only for not breaking older UBI users) + * @padding1: reserved for future, zeroes * @data_size: how many bytes of data this logical eraseblock contains * @used_ebs: total number of used logical eraseblocks in this volume * @data_pad: how many bytes at the end of this physical eraseblock are not - * used + * used * @data_crc: CRC checksum of the data stored in this logical eraseblock - * @padding1: reserved for future, zeroes - * @sqnum: sequence number * @padding2: reserved for future, zeroes + * @sqnum: sequence number + * @padding3: reserved for future, zeroes * @hdr_crc: volume identifier header CRC checksum * * The @sqnum is the value of the global sequence counter at the time when this @@ -175,7 +185,7 @@ struct ubi_ec_hdr { * (sequence number) is used to distinguish between older and newer versions of * logical eraseblocks. * - * There are 2 situations when there may be more then one physical eraseblock + * There are 2 situations when there may be more than one physical eraseblock * corresponding to the same logical eraseblock, i.e., having the same @vol_id * and @lnum values in the volume identifier header. Suppose we have a logical * eraseblock L and it is mapped to the physical eraseblock P. @@ -212,10 +222,6 @@ struct ubi_ec_hdr { * checksum is correct, this physical eraseblock is selected (P1). Otherwise * the older one (P) is selected. * - * Note, there is an obsolete @leb_ver field which was used instead of @sqnum - * in the past. But it is not used anymore and we keep it in order to be able - * to deal with old UBI images. It will be removed at some point. - * * There are 2 sorts of volumes in UBI: user volumes and internal volumes. * Internal volumes are not seen from outside and are used for various internal * UBI purposes. In this implementation there is only one internal volume - the @@ -236,9 +242,9 @@ struct ubi_ec_hdr { * The @data_crc field contains the CRC checksum of the contents of the logical * eraseblock if this is a static volume. In case of dynamic volumes, it does * not contain the CRC checksum as a rule. The only exception is when the - * data of the physical eraseblock was moved by the wear-leveling unit, then - * the wear-leveling unit calculates the data CRC and stores it in the - * @data_crc field. And of course, the @copy_flag is %in this case. + * data of the physical eraseblock was moved by the wear-leveling sub-system, + * then the wear-leveling sub-system calculates the data CRC and stores it in + * the @data_crc field. And of course, the @copy_flag is %in this case. * * The @data_size field is used only for static volumes because UBI has to know * how many bytes of data are stored in this eraseblock. For dynamic volumes, @@ -265,23 +271,23 @@ struct ubi_vid_hdr { __u8 compat; __be32 vol_id; __be32 lnum; - __be32 leb_ver; /* obsolete, to be removed, don't use */ + __u8 padding1[4]; __be32 data_size; __be32 used_ebs; __be32 data_pad; __be32 data_crc; - __u8 padding1[4]; + __u8 padding2[4]; __be64 sqnum; - __u8 padding2[12]; + __u8 padding3[12]; __be32 hdr_crc; -} __attribute__ ((packed)); +} __packed; /* Internal UBI volumes count */ #define UBI_INT_VOL_COUNT 1 /* - * Starting ID of internal volumes. There is reserved room for 4096 internal - * volumes. + * Starting ID of internal volumes: 0x7fffefff. + * There is reserved room for 4096 internal volumes. */ #define UBI_INTERNAL_VOL_START (0x7FFFFFFF - 4096) @@ -351,10 +357,151 @@ struct ubi_vtbl_record { __u8 vol_type; __u8 upd_marker; __be16 name_len; +#ifndef __UBOOT__ __u8 name[UBI_VOL_NAME_MAX+1]; +#else + char name[UBI_VOL_NAME_MAX+1]; +#endif __u8 flags; __u8 padding[23]; __be32 crc; -} __attribute__ ((packed)); +} __packed; + +/* UBI fastmap on-flash data structures */ + +#define UBI_FM_SB_VOLUME_ID (UBI_LAYOUT_VOLUME_ID + 1) +#define UBI_FM_DATA_VOLUME_ID (UBI_LAYOUT_VOLUME_ID + 2) +/* fastmap on-flash data structure format version */ +#define UBI_FM_FMT_VERSION 1 + +#define UBI_FM_SB_MAGIC 0x7B11D69F +#define UBI_FM_HDR_MAGIC 0xD4B82EF7 +#define UBI_FM_VHDR_MAGIC 0xFA370ED1 +#define UBI_FM_POOL_MAGIC 0x67AF4D08 +#define UBI_FM_EBA_MAGIC 0xf0c040a8 + +/* A fastmap supber block can be located between PEB 0 and + * UBI_FM_MAX_START */ +#define UBI_FM_MAX_START 64 + +/* A fastmap can use up to UBI_FM_MAX_BLOCKS PEBs */ +#define UBI_FM_MAX_BLOCKS 32 + +/* 5% of the total number of PEBs have to be scanned while attaching + * from a fastmap. + * But the size of this pool is limited to be between UBI_FM_MIN_POOL_SIZE and + * UBI_FM_MAX_POOL_SIZE */ +#define UBI_FM_MIN_POOL_SIZE 8 +#define UBI_FM_MAX_POOL_SIZE 256 + +#define UBI_FM_WL_POOL_SIZE 25 + +/** + * struct ubi_fm_sb - UBI fastmap super block + * @magic: fastmap super block magic number (%UBI_FM_SB_MAGIC) + * @version: format version of this fastmap + * @data_crc: CRC over the fastmap data + * @used_blocks: number of PEBs used by this fastmap + * @block_loc: an array containing the location of all PEBs of the fastmap + * @block_ec: the erase counter of each used PEB + * @sqnum: highest sequence number value at the time while taking the fastmap + * + */ +struct ubi_fm_sb { + __be32 magic; + __u8 version; + __u8 padding1[3]; + __be32 data_crc; + __be32 used_blocks; + __be32 block_loc[UBI_FM_MAX_BLOCKS]; + __be32 block_ec[UBI_FM_MAX_BLOCKS]; + __be64 sqnum; + __u8 padding2[32]; +} __packed; + +/** + * struct ubi_fm_hdr - header of the fastmap data set + * @magic: fastmap header magic number (%UBI_FM_HDR_MAGIC) + * @free_peb_count: number of free PEBs known by this fastmap + * @used_peb_count: number of used PEBs known by this fastmap + * @scrub_peb_count: number of to be scrubbed PEBs known by this fastmap + * @bad_peb_count: number of bad PEBs known by this fastmap + * @erase_peb_count: number of bad PEBs which have to be erased + * @vol_count: number of UBI volumes known by this fastmap + */ +struct ubi_fm_hdr { + __be32 magic; + __be32 free_peb_count; + __be32 used_peb_count; + __be32 scrub_peb_count; + __be32 bad_peb_count; + __be32 erase_peb_count; + __be32 vol_count; + __u8 padding[4]; +} __packed; + +/* struct ubi_fm_hdr is followed by two struct ubi_fm_scan_pool */ + +/** + * struct ubi_fm_scan_pool - Fastmap pool PEBs to be scanned while attaching + * @magic: pool magic numer (%UBI_FM_POOL_MAGIC) + * @size: current pool size + * @max_size: maximal pool size + * @pebs: an array containing the location of all PEBs in this pool + */ +struct ubi_fm_scan_pool { + __be32 magic; + __be16 size; + __be16 max_size; + __be32 pebs[UBI_FM_MAX_POOL_SIZE]; + __be32 padding[4]; +} __packed; + +/* ubi_fm_scan_pool is followed by nfree+nused struct ubi_fm_ec records */ + +/** + * struct ubi_fm_ec - stores the erase counter of a PEB + * @pnum: PEB number + * @ec: ec of this PEB + */ +struct ubi_fm_ec { + __be32 pnum; + __be32 ec; +} __packed; + +/** + * struct ubi_fm_volhdr - Fastmap volume header + * it identifies the start of an eba table + * @magic: Fastmap volume header magic number (%UBI_FM_VHDR_MAGIC) + * @vol_id: volume id of the fastmapped volume + * @vol_type: type of the fastmapped volume + * @data_pad: data_pad value of the fastmapped volume + * @used_ebs: number of used LEBs within this volume + * @last_eb_bytes: number of bytes used in the last LEB + */ +struct ubi_fm_volhdr { + __be32 magic; + __be32 vol_id; + __u8 vol_type; + __u8 padding1[3]; + __be32 data_pad; + __be32 used_ebs; + __be32 last_eb_bytes; + __u8 padding2[8]; +} __packed; + +/* struct ubi_fm_volhdr is followed by one struct ubi_fm_eba records */ + +/** + * struct ubi_fm_eba - denotes an association beween a PEB and LEB + * @magic: EBA table magic number + * @reserved_pebs: number of table entries + * @pnum: PEB number of LEB (LEB is the index) + */ +struct ubi_fm_eba { + __be32 magic; + __be32 reserved_pebs; + __be32 pnum[0]; +} __packed; #endif /* !__UBI_MEDIA_H__ */ diff --git a/drivers/mtd/ubi/ubi.h b/drivers/mtd/ubi/ubi.h index f4f7165..20fd704 100644 --- a/drivers/mtd/ubi/ubi.h +++ b/drivers/mtd/ubi/ubi.h @@ -10,8 +10,8 @@ #ifndef __UBI_UBI_H__ #define __UBI_UBI_H__ -#ifdef UBI_LINUX -#include <linux/init.h> +#define __UBOOT__ +#ifndef __UBOOT__ #include <linux/types.h> #include <linux/list.h> #include <linux/rbtree.h> @@ -23,22 +23,18 @@ #include <linux/fs.h> #include <linux/cdev.h> #include <linux/device.h> +#include <linux/slab.h> #include <linux/string.h> #include <linux/vmalloc.h> -#include <linux/mtd/mtd.h> -#include <linux/mtd/ubi.h> +#include <linux/notifier.h> +#include <asm/pgtable.h> +#else +#include <ubi_uboot.h> #endif - -#include <linux/types.h> -#include <linux/list.h> -#include <linux/rbtree.h> -#include <linux/string.h> #include <linux/mtd/mtd.h> #include <linux/mtd/ubi.h> - #include "ubi-media.h" -#include "scan.h" -#include "debug.h" +#include <mtd/ubi-user.h> /* Maximum number of supported UBI devices */ #define UBI_MAX_DEVICES 32 @@ -52,20 +48,21 @@ #else #define ubi_msg(fmt, ...) printk(KERN_NOTICE "UBI: " fmt "\n", ##__VA_ARGS__) #endif + /* UBI warning messages */ -#define ubi_warn(fmt, ...) printk(KERN_WARNING "UBI warning: %s: " fmt "\n", \ - __func__, ##__VA_ARGS__) +#define ubi_warn(fmt, ...) pr_warn("UBI warning: %s: " fmt "\n", \ + __func__, ##__VA_ARGS__) /* UBI error messages */ -#define ubi_err(fmt, ...) printk(KERN_ERR "UBI error: %s: " fmt "\n", \ +#define ubi_err(fmt, ...) pr_err("UBI error: %s: " fmt "\n", \ __func__, ##__VA_ARGS__) -/* Lowest number PEBs reserved for bad PEB handling */ -#define MIN_RESEVED_PEBS 2 - /* Background thread name pattern */ #define UBI_BGT_NAME_PATTERN "ubi_bgt%dd" -/* This marker in the EBA table means that the LEB is um-mapped */ +/* + * This marker in the EBA table means that the LEB is um-mapped. + * NOTE! It has to have the same value as %UBI_ALL. + */ #define UBI_LEB_UNMAPPED -1 /* @@ -75,37 +72,98 @@ #define UBI_IO_RETRIES 3 /* - * Error codes returned by the I/O unit. - * - * UBI_IO_PEB_EMPTY: the physical eraseblock is empty, i.e. it contains only - * 0xFF bytes - * UBI_IO_PEB_FREE: the physical eraseblock is free, i.e. it contains only a - * valid erase counter header, and the rest are %0xFF bytes - * UBI_IO_BAD_EC_HDR: the erase counter header is corrupted (bad magic or CRC) - * UBI_IO_BAD_VID_HDR: the volume identifier header is corrupted (bad magic or - * CRC) + * Length of the protection queue. The length is effectively equivalent to the + * number of (global) erase cycles PEBs are protected from the wear-leveling + * worker. + */ +#define UBI_PROT_QUEUE_LEN 10 + +/* The volume ID/LEB number/erase counter is unknown */ +#define UBI_UNKNOWN -1 + +/* + * The UBI debugfs directory name pattern and maximum name length (3 for "ubi" + * + 2 for the number plus 1 for the trailing zero byte. + */ +#define UBI_DFS_DIR_NAME "ubi%d" +#define UBI_DFS_DIR_LEN (3 + 2 + 1) + +/* + * Error codes returned by the I/O sub-system. + * + * UBI_IO_FF: the read region of flash contains only 0xFFs + * UBI_IO_FF_BITFLIPS: the same as %UBI_IO_FF, but also also there was a data + * integrity error reported by the MTD driver + * (uncorrectable ECC error in case of NAND) + * UBI_IO_BAD_HDR: the EC or VID header is corrupted (bad magic or CRC) + * UBI_IO_BAD_HDR_EBADMSG: the same as %UBI_IO_BAD_HDR, but also there was a + * data integrity error reported by the MTD driver + * (uncorrectable ECC error in case of NAND) * UBI_IO_BITFLIPS: bit-flips were detected and corrected + * + * Note, it is probably better to have bit-flip and ebadmsg as flags which can + * be or'ed with other error code. But this is a big change because there are + * may callers, so it does not worth the risk of introducing a bug + */ +enum { + UBI_IO_FF = 1, + UBI_IO_FF_BITFLIPS, + UBI_IO_BAD_HDR, + UBI_IO_BAD_HDR_EBADMSG, + UBI_IO_BITFLIPS, +}; + +/* + * Return codes of the 'ubi_eba_copy_leb()' function. + * + * MOVE_CANCEL_RACE: canceled because the volume is being deleted, the source + * PEB was put meanwhile, or there is I/O on the source PEB + * MOVE_SOURCE_RD_ERR: canceled because there was a read error from the source + * PEB + * MOVE_TARGET_RD_ERR: canceled because there was a read error from the target + * PEB + * MOVE_TARGET_WR_ERR: canceled because there was a write error to the target + * PEB + * MOVE_TARGET_BITFLIPS: canceled because a bit-flip was detected in the + * target PEB + * MOVE_RETRY: retry scrubbing the PEB */ enum { - UBI_IO_PEB_EMPTY = 1, - UBI_IO_PEB_FREE, - UBI_IO_BAD_EC_HDR, - UBI_IO_BAD_VID_HDR, - UBI_IO_BITFLIPS + MOVE_CANCEL_RACE = 1, + MOVE_SOURCE_RD_ERR, + MOVE_TARGET_RD_ERR, + MOVE_TARGET_WR_ERR, + MOVE_TARGET_BITFLIPS, + MOVE_RETRY, +}; + +/* + * Return codes of the fastmap sub-system + * + * UBI_NO_FASTMAP: No fastmap super block was found + * UBI_BAD_FASTMAP: A fastmap was found but it's unusable + */ +enum { + UBI_NO_FASTMAP = 1, + UBI_BAD_FASTMAP, }; /** * struct ubi_wl_entry - wear-leveling entry. - * @rb: link in the corresponding RB-tree + * @u.rb: link in the corresponding (free/used) RB-tree + * @u.list: link in the protection queue * @ec: erase counter * @pnum: physical eraseblock number * - * This data structure is used in the WL unit. Each physical eraseblock has a - * corresponding &struct wl_entry object which may be kept in different - * RB-trees. See WL unit for details. + * This data structure is used in the WL sub-system. Each physical eraseblock + * has a corresponding &struct wl_entry object which may be kept in different + * RB-trees. See WL sub-system for details. */ struct ubi_wl_entry { - struct rb_node rb; + union { + struct rb_node rb; + struct list_head list; + } u; int ec; int pnum; }; @@ -119,10 +177,10 @@ struct ubi_wl_entry { * @mutex: read/write mutex to implement read/write access serialization to * the (@vol_id, @lnum) logical eraseblock * - * This data structure is used in the EBA unit to implement per-LEB locking. - * When a logical eraseblock is being locked - corresponding + * This data structure is used in the EBA sub-system to implement per-LEB + * locking. When a logical eraseblock is being locked - corresponding * &struct ubi_ltree_entry object is inserted to the lock tree (@ubi->ltree). - * See EBA unit for details. + * See EBA sub-system for details. */ struct ubi_ltree_entry { struct rb_node rb; @@ -132,9 +190,65 @@ struct ubi_ltree_entry { struct rw_semaphore mutex; }; +/** + * struct ubi_rename_entry - volume re-name description data structure. + * @new_name_len: new volume name length + * @new_name: new volume name + * @remove: if not zero, this volume should be removed, not re-named + * @desc: descriptor of the volume + * @list: links re-name entries into a list + * + * This data structure is utilized in the multiple volume re-name code. Namely, + * UBI first creates a list of &struct ubi_rename_entry objects from the + * &struct ubi_rnvol_req request object, and then utilizes this list to do all + * the job. + */ +struct ubi_rename_entry { + int new_name_len; + char new_name[UBI_VOL_NAME_MAX + 1]; + int remove; + struct ubi_volume_desc *desc; + struct list_head list; +}; + struct ubi_volume_desc; /** + * struct ubi_fastmap_layout - in-memory fastmap data structure. + * @e: PEBs used by the current fastmap + * @to_be_tortured: if non-zero tortured this PEB + * @used_blocks: number of used PEBs + * @max_pool_size: maximal size of the user pool + * @max_wl_pool_size: maximal size of the pool used by the WL sub-system + */ +struct ubi_fastmap_layout { + struct ubi_wl_entry *e[UBI_FM_MAX_BLOCKS]; + int to_be_tortured[UBI_FM_MAX_BLOCKS]; + int used_blocks; + int max_pool_size; + int max_wl_pool_size; +}; + +/** + * struct ubi_fm_pool - in-memory fastmap pool + * @pebs: PEBs in this pool + * @used: number of used PEBs + * @size: total number of PEBs in this pool + * @max_size: maximal size of the pool + * + * A pool gets filled with up to max_size. + * If all PEBs within the pool are used a new fastmap will be written + * to the flash and the pool gets refilled with empty PEBs. + * + */ +struct ubi_fm_pool { + int pebs[UBI_FM_MAX_POOL_SIZE]; + int used; + int size; + int max_size; +}; + +/** * struct ubi_volume - UBI volume description data structure. * @dev: device object to make use of the the Linux device model * @cdev: character device object to create character device @@ -160,8 +274,6 @@ struct ubi_volume_desc; * @upd_ebs: how many eraseblocks are expected to be updated * @ch_lnum: LEB number which is being changing by the atomic LEB change * operation - * @ch_dtype: data persistency type which is being changing by the atomic LEB - * change operation * @upd_bytes: how many bytes are expected to be received for volume update or * atomic LEB change * @upd_received: how many bytes were already received for volume update or @@ -175,10 +287,7 @@ struct ubi_volume_desc; * @upd_marker: %1 if the update marker is set for this volume * @updating: %1 if the volume is being updated * @changing_leb: %1 if the atomic LEB change ioctl command is in progress - * - * @gluebi_desc: gluebi UBI volume descriptor - * @gluebi_refcount: reference count of the gluebi MTD device - * @gluebi_mtd: MTD device description object of the gluebi MTD device + * @direct_writes: %1 if direct writes are enabled for this volume * * The @corrupted field indicates that the volume's contents is corrupted. * Since UBI protects only static volumes, this field is not relevant to @@ -202,16 +311,19 @@ struct ubi_volume { int vol_type; int usable_leb_size; int used_ebs; +#ifndef __UBOOT__ int last_eb_bytes; +#else + u32 last_eb_bytes; +#endif long long used_bytes; int alignment; int data_pad; int name_len; - char name[UBI_VOL_NAME_MAX+1]; + char name[UBI_VOL_NAME_MAX + 1]; int upd_ebs; int ch_lnum; - int ch_dtype; long long upd_bytes; long long upd_received; void *upd_buf; @@ -222,22 +334,11 @@ struct ubi_volume { unsigned int upd_marker:1; unsigned int updating:1; unsigned int changing_leb:1; - -#ifdef CONFIG_MTD_UBI_GLUEBI - /* - * Gluebi-related stuff may be compiled out. - * TODO: this should not be built into UBI but should be a separate - * ubimtd driver which works on top of UBI and emulates MTD devices. - */ - struct ubi_volume_desc *gluebi_desc; - int gluebi_refcount; - struct mtd_info gluebi_mtd; -#endif + unsigned int direct_writes:1; }; /** - * struct ubi_volume_desc - descriptor of the UBI volume returned when it is - * opened. + * struct ubi_volume_desc - UBI volume descriptor returned when it is opened. * @vol: reference to the corresponding volume description object * @mode: open mode (%UBI_READONLY, %UBI_READWRITE, or %UBI_EXCLUSIVE) */ @@ -249,6 +350,37 @@ struct ubi_volume_desc { struct ubi_wl_entry; /** + * struct ubi_debug_info - debugging information for an UBI device. + * + * @chk_gen: if UBI general extra checks are enabled + * @chk_io: if UBI I/O extra checks are enabled + * @disable_bgt: disable the background task for testing purposes + * @emulate_bitflips: emulate bit-flips for testing purposes + * @emulate_io_failures: emulate write/erase failures for testing purposes + * @dfs_dir_name: name of debugfs directory containing files of this UBI device + * @dfs_dir: direntry object of the UBI device debugfs directory + * @dfs_chk_gen: debugfs knob to enable UBI general extra checks + * @dfs_chk_io: debugfs knob to enable UBI I/O extra checks + * @dfs_disable_bgt: debugfs knob to disable the background task + * @dfs_emulate_bitflips: debugfs knob to emulate bit-flips + * @dfs_emulate_io_failures: debugfs knob to emulate write/erase failures + */ +struct ubi_debug_info { + unsigned int chk_gen:1; + unsigned int chk_io:1; + unsigned int disable_bgt:1; + unsigned int emulate_bitflips:1; + unsigned int emulate_io_failures:1; + char dfs_dir_name[UBI_DFS_DIR_LEN + 1]; + struct dentry *dfs_dir; + struct dentry *dfs_chk_gen; + struct dentry *dfs_chk_io; + struct dentry *dfs_disable_bgt; + struct dentry *dfs_emulate_bitflips; + struct dentry *dfs_emulate_io_failures; +}; + +/** * struct ubi_device - UBI device description structure * @dev: UBI device object to use the the Linux device model * @cdev: character device object to create character device @@ -261,6 +393,7 @@ struct ubi_wl_entry; * @vol->readers, @vol->writers, @vol->exclusive, * @vol->ref_count, @vol->mapping and @vol->eba_tbl. * @ref_count: count of references on the UBI device + * @image_seq: image sequence number recorded on EC headers * * @rsvd_pebs: count of reserved physical eraseblocks * @avail_pebs: count of available physical eraseblocks @@ -269,12 +402,13 @@ struct ubi_wl_entry; * @beb_rsvd_level: normal level of PEBs reserved for bad PEB handling * * @autoresize_vol_id: ID of the volume which has to be auto-resized at the end - * of UBI ititializetion + * of UBI initialization * @vtbl_slots: how many slots are available in the volume table * @vtbl_size: size of the volume table in bytes * @vtbl: in-RAM volume table copy - * @volumes_mutex: protects on-flash volume table and serializes volume - * changes, like creation, deletion, update, resize + * @device_mutex: protects on-flash volume table and serializes volume + * creation, deletion, update, re-size, re-name and set + * property * * @max_ec: current highest erase counter value * @mean_ec: current mean erase counter value @@ -284,20 +418,33 @@ struct ubi_wl_entry; * @ltree: the lock tree * @alc_mutex: serializes "atomic LEB change" operations * + * @fm_disabled: non-zero if fastmap is disabled (default) + * @fm: in-memory data structure of the currently used fastmap + * @fm_pool: in-memory data structure of the fastmap pool + * @fm_wl_pool: in-memory data structure of the fastmap pool used by the WL + * sub-system + * @fm_mutex: serializes ubi_update_fastmap() and protects @fm_buf + * @fm_buf: vmalloc()'d buffer which holds the raw fastmap + * @fm_size: fastmap size in bytes + * @fm_sem: allows ubi_update_fastmap() to block EBA table changes + * @fm_work: fastmap work queue + * * @used: RB-tree of used physical eraseblocks + * @erroneous: RB-tree of erroneous used physical eraseblocks * @free: RB-tree of free physical eraseblocks + * @free_count: Contains the number of elements in @free * @scrub: RB-tree of physical eraseblocks which need scrubbing - * @prot: protection trees - * @prot.pnum: protection tree indexed by physical eraseblock numbers - * @prot.aec: protection tree indexed by absolute erase counter value - * @wl_lock: protects the @used, @free, @prot, @lookuptbl, @abs_ec, @move_from, - * @move_to, @move_to_put @erase_pending, @wl_scheduled, and @works - * fields + * @pq: protection queue (contain physical eraseblocks which are temporarily + * protected from the wear-leveling worker) + * @pq_head: protection queue head + * @wl_lock: protects the @used, @free, @pq, @pq_head, @lookuptbl, @move_from, + * @move_to, @move_to_put @erase_pending, @wl_scheduled, @works, + * @erroneous, and @erroneous_peb_count fields * @move_mutex: serializes eraseblock moves + * @work_sem: synchronizes the WL worker with use tasks * @wl_scheduled: non-zero if the wear-leveling was scheduled * @lookuptbl: a table to quickly find a &struct ubi_wl_entry object for any * physical eraseblock - * @abs_ec: absolute erase counter * @move_from: physical eraseblock from where the data is being moved * @move_to: physical eraseblock where the data is being moved to * @move_to_put: if the "to" PEB was put @@ -310,30 +457,38 @@ struct ubi_wl_entry; * @flash_size: underlying MTD device size (in bytes) * @peb_count: count of physical eraseblocks on the MTD device * @peb_size: physical eraseblock size + * @bad_peb_limit: top limit of expected bad physical eraseblocks * @bad_peb_count: count of bad physical eraseblocks * @good_peb_count: count of good physical eraseblocks + * @corr_peb_count: count of corrupted physical eraseblocks (preserved and not + * used by UBI) + * @erroneous_peb_count: count of erroneous physical eraseblocks in @erroneous + * @max_erroneous: maximum allowed amount of erroneous physical eraseblocks * @min_io_size: minimal input/output unit size of the underlying MTD device * @hdrs_min_io_size: minimal I/O unit size used for VID and EC headers * @ro_mode: if the UBI device is in read-only mode * @leb_size: logical eraseblock size * @leb_start: starting offset of logical eraseblocks within physical - * eraseblocks + * eraseblocks * @ec_hdr_alsize: size of the EC header aligned to @hdrs_min_io_size * @vid_hdr_alsize: size of the VID header aligned to @hdrs_min_io_size * @vid_hdr_offset: starting offset of the volume identifier header (might be - * unaligned) + * unaligned) * @vid_hdr_aloffset: starting offset of the VID header aligned to * @hdrs_min_io_size * @vid_hdr_shift: contains @vid_hdr_offset - @vid_hdr_aloffset * @bad_allowed: whether the MTD device admits of bad physical eraseblocks or * not + * @nor_flash: non-zero if working on top of NOR flash + * @max_write_size: maximum amount of bytes the underlying flash can write at a + * time (MTD write buffer size) * @mtd: MTD device descriptor * - * @peb_buf1: a buffer of PEB size used for different purposes - * @peb_buf2: another buffer of PEB size used for different purposes - * @buf_mutex: proptects @peb_buf1 and @peb_buf2 - * @dbg_peb_buf: buffer of PEB size used for debugging - * @dbg_buf_mutex: proptects @dbg_peb_buf + * @peb_buf: a buffer of PEB size used for different purposes + * @buf_mutex: protects @peb_buf + * @ckvol_mutex: serializes static volume checking when opening + * + * @dbg: debugging information for this UBI device */ struct ubi_device { struct cdev cdev; @@ -344,42 +499,56 @@ struct ubi_device { struct ubi_volume *volumes[UBI_MAX_VOLUMES+UBI_INT_VOL_COUNT]; spinlock_t volumes_lock; int ref_count; + int image_seq; int rsvd_pebs; int avail_pebs; int beb_rsvd_pebs; int beb_rsvd_level; + int bad_peb_limit; int autoresize_vol_id; int vtbl_slots; int vtbl_size; struct ubi_vtbl_record *vtbl; - struct mutex volumes_mutex; + struct mutex device_mutex; int max_ec; - /* TODO: mean_ec is not updated run-time, fix */ + /* Note, mean_ec is not updated run-time - should be fixed */ int mean_ec; - /* EBA unit's stuff */ + /* EBA sub-system's stuff */ unsigned long long global_sqnum; spinlock_t ltree_lock; struct rb_root ltree; struct mutex alc_mutex; - /* Wear-leveling unit's stuff */ + /* Fastmap stuff */ + int fm_disabled; + struct ubi_fastmap_layout *fm; + struct ubi_fm_pool fm_pool; + struct ubi_fm_pool fm_wl_pool; + struct rw_semaphore fm_sem; + struct mutex fm_mutex; + void *fm_buf; + size_t fm_size; +#ifndef __UBOOT__ + struct work_struct fm_work; +#endif + + /* Wear-leveling sub-system's stuff */ struct rb_root used; + struct rb_root erroneous; struct rb_root free; + int free_count; struct rb_root scrub; - struct { - struct rb_root pnum; - struct rb_root aec; - } prot; + struct list_head pq[UBI_PROT_QUEUE_LEN]; + int pq_head; spinlock_t wl_lock; struct mutex move_mutex; struct rw_semaphore work_sem; int wl_scheduled; struct ubi_wl_entry **lookuptbl; - unsigned long long abs_ec; struct ubi_wl_entry *move_from; struct ubi_wl_entry *move_to; int move_to_put; @@ -389,12 +558,15 @@ struct ubi_device { int thread_enabled; char bgt_name[sizeof(UBI_BGT_NAME_PATTERN)+2]; - /* I/O unit's stuff */ + /* I/O sub-system's stuff */ long long flash_size; int peb_count; int peb_size; int bad_peb_count; int good_peb_count; + int corr_peb_count; + int erroneous_peb_count; + int max_erroneous; int min_io_size; int hdrs_min_io_size; int ro_mode; @@ -405,35 +577,195 @@ struct ubi_device { int vid_hdr_offset; int vid_hdr_aloffset; int vid_hdr_shift; - int bad_allowed; + unsigned int bad_allowed:1; + unsigned int nor_flash:1; + int max_write_size; struct mtd_info *mtd; - void *peb_buf1; - void *peb_buf2; + void *peb_buf; struct mutex buf_mutex; struct mutex ckvol_mutex; -#ifdef CONFIG_MTD_UBI_DEBUG - void *dbg_peb_buf; - struct mutex dbg_buf_mutex; -#endif + + struct ubi_debug_info dbg; +}; + +/** + * struct ubi_ainf_peb - attach information about a physical eraseblock. + * @ec: erase counter (%UBI_UNKNOWN if it is unknown) + * @pnum: physical eraseblock number + * @vol_id: ID of the volume this LEB belongs to + * @lnum: logical eraseblock number + * @scrub: if this physical eraseblock needs scrubbing + * @copy_flag: this LEB is a copy (@copy_flag is set in VID header of this LEB) + * @sqnum: sequence number + * @u: unions RB-tree or @list links + * @u.rb: link in the per-volume RB-tree of &struct ubi_ainf_peb objects + * @u.list: link in one of the eraseblock lists + * + * One object of this type is allocated for each physical eraseblock when + * attaching an MTD device. Note, if this PEB does not belong to any LEB / + * volume, the @vol_id and @lnum fields are initialized to %UBI_UNKNOWN. + */ +struct ubi_ainf_peb { + int ec; + int pnum; + int vol_id; + int lnum; + unsigned int scrub:1; + unsigned int copy_flag:1; + unsigned long long sqnum; + union { + struct rb_node rb; + struct list_head list; + } u; +}; + +/** + * struct ubi_ainf_volume - attaching information about a volume. + * @vol_id: volume ID + * @highest_lnum: highest logical eraseblock number in this volume + * @leb_count: number of logical eraseblocks in this volume + * @vol_type: volume type + * @used_ebs: number of used logical eraseblocks in this volume (only for + * static volumes) + * @last_data_size: amount of data in the last logical eraseblock of this + * volume (always equivalent to the usable logical eraseblock + * size in case of dynamic volumes) + * @data_pad: how many bytes at the end of logical eraseblocks of this volume + * are not used (due to volume alignment) + * @compat: compatibility flags of this volume + * @rb: link in the volume RB-tree + * @root: root of the RB-tree containing all the eraseblock belonging to this + * volume (&struct ubi_ainf_peb objects) + * + * One object of this type is allocated for each volume when attaching an MTD + * device. + */ +struct ubi_ainf_volume { + int vol_id; + int highest_lnum; + int leb_count; + int vol_type; + int used_ebs; + int last_data_size; + int data_pad; + int compat; + struct rb_node rb; + struct rb_root root; }; +/** + * struct ubi_attach_info - MTD device attaching information. + * @volumes: root of the volume RB-tree + * @corr: list of corrupted physical eraseblocks + * @free: list of free physical eraseblocks + * @erase: list of physical eraseblocks which have to be erased + * @alien: list of physical eraseblocks which should not be used by UBI (e.g., + * those belonging to "preserve"-compatible internal volumes) + * @corr_peb_count: count of PEBs in the @corr list + * @empty_peb_count: count of PEBs which are presumably empty (contain only + * 0xFF bytes) + * @alien_peb_count: count of PEBs in the @alien list + * @bad_peb_count: count of bad physical eraseblocks + * @maybe_bad_peb_count: count of bad physical eraseblocks which are not marked + * as bad yet, but which look like bad + * @vols_found: number of volumes found + * @highest_vol_id: highest volume ID + * @is_empty: flag indicating whether the MTD device is empty or not + * @min_ec: lowest erase counter value + * @max_ec: highest erase counter value + * @max_sqnum: highest sequence number value + * @mean_ec: mean erase counter value + * @ec_sum: a temporary variable used when calculating @mean_ec + * @ec_count: a temporary variable used when calculating @mean_ec + * @aeb_slab_cache: slab cache for &struct ubi_ainf_peb objects + * + * This data structure contains the result of attaching an MTD device and may + * be used by other UBI sub-systems to build final UBI data structures, further + * error-recovery and so on. + */ +struct ubi_attach_info { + struct rb_root volumes; + struct list_head corr; + struct list_head free; + struct list_head erase; + struct list_head alien; + int corr_peb_count; + int empty_peb_count; + int alien_peb_count; + int bad_peb_count; + int maybe_bad_peb_count; + int vols_found; + int highest_vol_id; + int is_empty; + int min_ec; + int max_ec; + unsigned long long max_sqnum; + int mean_ec; + uint64_t ec_sum; + int ec_count; + struct kmem_cache *aeb_slab_cache; +}; + +/** + * struct ubi_work - UBI work description data structure. + * @list: a link in the list of pending works + * @func: worker function + * @e: physical eraseblock to erase + * @vol_id: the volume ID on which this erasure is being performed + * @lnum: the logical eraseblock number + * @torture: if the physical eraseblock has to be tortured + * @anchor: produce a anchor PEB to by used by fastmap + * + * 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 vol_id; + int lnum; + int torture; + int anchor; +}; + +#include "debug.h" + extern struct kmem_cache *ubi_wl_entry_slab; -extern struct file_operations ubi_ctrl_cdev_operations; -extern struct file_operations ubi_cdev_operations; -extern struct file_operations ubi_vol_cdev_operations; +extern const struct file_operations ubi_ctrl_cdev_operations; +extern const struct file_operations ubi_cdev_operations; +extern const struct file_operations ubi_vol_cdev_operations; extern struct class *ubi_class; extern struct mutex ubi_devices_mutex; +extern struct blocking_notifier_head ubi_notifiers; + +/* attach.c */ +int ubi_add_to_av(struct ubi_device *ubi, struct ubi_attach_info *ai, int pnum, + int ec, const struct ubi_vid_hdr *vid_hdr, int bitflips); +struct ubi_ainf_volume *ubi_find_av(const struct ubi_attach_info *ai, + int vol_id); +void ubi_remove_av(struct ubi_attach_info *ai, struct ubi_ainf_volume *av); +struct ubi_ainf_peb *ubi_early_get_peb(struct ubi_device *ubi, + struct ubi_attach_info *ai); +int ubi_attach(struct ubi_device *ubi, int force_scan); +void ubi_destroy_ai(struct ubi_attach_info *ai); /* vtbl.c */ int ubi_change_vtbl_record(struct ubi_device *ubi, int idx, struct ubi_vtbl_record *vtbl_rec); -int ubi_read_volume_table(struct ubi_device *ubi, struct ubi_scan_info *si); +int ubi_vtbl_rename_volumes(struct ubi_device *ubi, + struct list_head *rename_list); +int ubi_read_volume_table(struct ubi_device *ubi, struct ubi_attach_info *ai); /* vmt.c */ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req); -int ubi_remove_volume(struct ubi_volume_desc *desc); +int ubi_remove_volume(struct ubi_volume_desc *desc, int no_vtbl); int ubi_resize_volume(struct ubi_volume_desc *desc, int reserved_pebs); +int ubi_rename_volumes(struct ubi_device *ubi, struct list_head *rename_list); int ubi_add_volume(struct ubi_device *ubi, struct ubi_volume *vol); void ubi_free_volume(struct ubi_device *ubi, struct ubi_volume *vol); @@ -448,9 +780,12 @@ int ubi_more_leb_change_data(struct ubi_device *ubi, struct ubi_volume *vol, const void __user *buf, int count); /* misc.c */ -int ubi_calc_data_len(const struct ubi_device *ubi, const void *buf, int length); +int ubi_calc_data_len(const struct ubi_device *ubi, const void *buf, + int length); int ubi_check_volume(struct ubi_device *ubi, int vol_id); +void ubi_update_reserved(struct ubi_device *ubi); void ubi_calculate_reserved(struct ubi_device *ubi); +int ubi_check_pattern(const void *buf, uint8_t patt, int size); /* gluebi.c */ #ifdef CONFIG_MTD_UBI_GLUEBI @@ -474,25 +809,33 @@ int ubi_eba_unmap_leb(struct ubi_device *ubi, struct ubi_volume *vol, int ubi_eba_read_leb(struct ubi_device *ubi, struct ubi_volume *vol, int lnum, void *buf, int offset, int len, int check); int ubi_eba_write_leb(struct ubi_device *ubi, struct ubi_volume *vol, int lnum, - const void *buf, int offset, int len, int dtype); + const void *buf, int offset, int len); int ubi_eba_write_leb_st(struct ubi_device *ubi, struct ubi_volume *vol, - int lnum, const void *buf, int len, int dtype, - int used_ebs); + int lnum, const void *buf, int len, int used_ebs); int ubi_eba_atomic_leb_change(struct ubi_device *ubi, struct ubi_volume *vol, - int lnum, const void *buf, int len, int dtype); + int lnum, const void *buf, int len); int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to, struct ubi_vid_hdr *vid_hdr); -int ubi_eba_init_scan(struct ubi_device *ubi, struct ubi_scan_info *si); -void ubi_eba_close(const struct ubi_device *ubi); +int ubi_eba_init(struct ubi_device *ubi, struct ubi_attach_info *ai); +unsigned long long ubi_next_sqnum(struct ubi_device *ubi); +int self_check_eba(struct ubi_device *ubi, struct ubi_attach_info *ai_fastmap, + struct ubi_attach_info *ai_scan); /* wl.c */ -int ubi_wl_get_peb(struct ubi_device *ubi, int dtype); -int ubi_wl_put_peb(struct ubi_device *ubi, int pnum, int torture); -int ubi_wl_flush(struct ubi_device *ubi); +int ubi_wl_get_peb(struct ubi_device *ubi); +int ubi_wl_put_peb(struct ubi_device *ubi, int vol_id, int lnum, + int pnum, int torture); +int ubi_wl_flush(struct ubi_device *ubi, int vol_id, int lnum); int ubi_wl_scrub_peb(struct ubi_device *ubi, int pnum); -int ubi_wl_init_scan(struct ubi_device *ubi, struct ubi_scan_info *si); +int ubi_wl_init(struct ubi_device *ubi, struct ubi_attach_info *ai); void ubi_wl_close(struct ubi_device *ubi); int ubi_thread(void *u); +struct ubi_wl_entry *ubi_wl_get_fm_peb(struct ubi_device *ubi, int anchor); +int ubi_wl_put_fm_peb(struct ubi_device *ubi, struct ubi_wl_entry *used_e, + int lnum, int torture); +int ubi_is_erase_work(struct ubi_work *wrk); +void ubi_refill_pools(struct ubi_device *ubi); +int ubi_ensure_anchor_pebs(struct ubi_device *ubi); /* io.c */ int ubi_io_read(const struct ubi_device *ubi, void *buf, int pnum, int offset, @@ -512,16 +855,57 @@ int ubi_io_write_vid_hdr(struct ubi_device *ubi, int pnum, struct ubi_vid_hdr *vid_hdr); /* build.c */ -int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num, int vid_hdr_offset); +int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num, + int vid_hdr_offset, int max_beb_per1024); int ubi_detach_mtd_dev(int ubi_num, int anyway); struct ubi_device *ubi_get_device(int ubi_num); void ubi_put_device(struct ubi_device *ubi); struct ubi_device *ubi_get_by_major(int major); int ubi_major2num(int major); +int ubi_volume_notify(struct ubi_device *ubi, struct ubi_volume *vol, + int ntype); +int ubi_notify_all(struct ubi_device *ubi, int ntype, + struct notifier_block *nb); +int ubi_enumerate_volumes(struct notifier_block *nb); +void ubi_free_internal_volumes(struct ubi_device *ubi); + +/* kapi.c */ +void ubi_do_get_device_info(struct ubi_device *ubi, struct ubi_device_info *di); +void ubi_do_get_volume_info(struct ubi_device *ubi, struct ubi_volume *vol, + struct ubi_volume_info *vi); +/* scan.c */ +int ubi_compare_lebs(struct ubi_device *ubi, const struct ubi_ainf_peb *aeb, + int pnum, const struct ubi_vid_hdr *vid_hdr); + +/* fastmap.c */ +size_t ubi_calc_fm_size(struct ubi_device *ubi); +int ubi_update_fastmap(struct ubi_device *ubi); +int ubi_scan_fastmap(struct ubi_device *ubi, struct ubi_attach_info *ai, + int fm_anchor); + +/* block.c */ +#ifdef CONFIG_MTD_UBI_BLOCK +int ubiblock_init(void); +void ubiblock_exit(void); +int ubiblock_create(struct ubi_volume_info *vi); +int ubiblock_remove(struct ubi_volume_info *vi); +#else +static inline int ubiblock_init(void) { return 0; } +static inline void ubiblock_exit(void) {} +static inline int ubiblock_create(struct ubi_volume_info *vi) +{ + return -ENOSYS; +} +static inline int ubiblock_remove(struct ubi_volume_info *vi) +{ + return -ENOSYS; +} +#endif + /* * ubi_rb_for_each_entry - walk an RB-tree. - * @rb: a pointer to type 'struct rb_node' to to use as a loop counter + * @rb: a pointer to type 'struct rb_node' to use as a loop counter * @pos: a pointer to RB-tree entry type to use as a loop counter * @root: RB-tree's root * @member: the name of the 'struct rb_node' within the RB-tree entry @@ -530,7 +914,23 @@ int ubi_major2num(int major); for (rb = rb_first(root), \ pos = (rb ? container_of(rb, typeof(*pos), member) : NULL); \ rb; \ - rb = rb_next(rb), pos = container_of(rb, typeof(*pos), member)) + rb = rb_next(rb), \ + pos = (rb ? container_of(rb, typeof(*pos), member) : NULL)) + +/* + * ubi_move_aeb_to_list - move a PEB from the volume tree to a list. + * + * @av: volume attaching information + * @aeb: attaching eraseblock information + * @list: the list to move to + */ +static inline void ubi_move_aeb_to_list(struct ubi_ainf_volume *av, + struct ubi_ainf_peb *aeb, + struct list_head *list) +{ + rb_erase(&aeb->u.rb, &av->root); + list_add_tail(&aeb->u.list, list); +} /** * ubi_zalloc_vid_hdr - allocate a volume identifier header object. @@ -606,6 +1006,7 @@ static inline void ubi_ro_mode(struct ubi_device *ubi) if (!ubi->ro_mode) { ubi->ro_mode = 1; ubi_warn("switch to read-only mode"); + dump_stack(); } } diff --git a/drivers/mtd/ubi/upd.c b/drivers/mtd/ubi/upd.c index e597f82..220c120 100644 --- a/drivers/mtd/ubi/upd.c +++ b/drivers/mtd/ubi/upd.c @@ -26,13 +26,16 @@ * transaction with a roll-back capability. */ -#ifdef UBI_LINUX -#include <linux/err.h> -#include <asm/uaccess.h> -#include <asm/div64.h> +#define __UBOOT__ +#ifndef __UBOOT__ +#include <linux/uaccess.h> +#else +#include <div64.h> +#include <ubi_uboot.h> #endif +#include <linux/err.h> +#include <linux/math64.h> -#include <ubi_uboot.h> #include "ubi.h" /** @@ -48,22 +51,21 @@ static int set_update_marker(struct ubi_device *ubi, struct ubi_volume *vol) int err; struct ubi_vtbl_record vtbl_rec; - dbg_msg("set update marker for volume %d", vol->vol_id); + dbg_gen("set update marker for volume %d", vol->vol_id); if (vol->upd_marker) { ubi_assert(ubi->vtbl[vol->vol_id].upd_marker); - dbg_msg("already set"); + dbg_gen("already set"); return 0; } - memcpy(&vtbl_rec, &ubi->vtbl[vol->vol_id], - sizeof(struct ubi_vtbl_record)); + vtbl_rec = ubi->vtbl[vol->vol_id]; vtbl_rec.upd_marker = 1; - mutex_lock(&ubi->volumes_mutex); + mutex_lock(&ubi->device_mutex); err = ubi_change_vtbl_record(ubi, vol->vol_id, &vtbl_rec); - mutex_unlock(&ubi->volumes_mutex); vol->upd_marker = 1; + mutex_unlock(&ubi->device_mutex); return err; } @@ -81,31 +83,29 @@ static int clear_update_marker(struct ubi_device *ubi, struct ubi_volume *vol, long long bytes) { int err; - uint64_t tmp; struct ubi_vtbl_record vtbl_rec; - dbg_msg("clear update marker for volume %d", vol->vol_id); + dbg_gen("clear update marker for volume %d", vol->vol_id); - memcpy(&vtbl_rec, &ubi->vtbl[vol->vol_id], - sizeof(struct ubi_vtbl_record)); + vtbl_rec = ubi->vtbl[vol->vol_id]; ubi_assert(vol->upd_marker && vtbl_rec.upd_marker); vtbl_rec.upd_marker = 0; if (vol->vol_type == UBI_STATIC_VOLUME) { vol->corrupted = 0; - vol->used_bytes = tmp = bytes; - vol->last_eb_bytes = do_div(tmp, vol->usable_leb_size); - vol->used_ebs = tmp; + vol->used_bytes = bytes; + vol->used_ebs = div_u64_rem(bytes, vol->usable_leb_size, + &vol->last_eb_bytes); if (vol->last_eb_bytes) vol->used_ebs += 1; else vol->last_eb_bytes = vol->usable_leb_size; } - mutex_lock(&ubi->volumes_mutex); + mutex_lock(&ubi->device_mutex); err = ubi_change_vtbl_record(ubi, vol->vol_id, &vtbl_rec); - mutex_unlock(&ubi->volumes_mutex); vol->upd_marker = 0; + mutex_unlock(&ubi->device_mutex); return err; } @@ -123,9 +123,8 @@ int ubi_start_update(struct ubi_device *ubi, struct ubi_volume *vol, long long bytes) { int i, err; - uint64_t tmp; - dbg_msg("start update of volume %d, %llu bytes", vol->vol_id, bytes); + dbg_gen("start update of volume %d, %llu bytes", vol->vol_id, bytes); ubi_assert(!vol->updating && !vol->changing_leb); vol->updating = 1; @@ -141,21 +140,23 @@ int ubi_start_update(struct ubi_device *ubi, struct ubi_volume *vol, } if (bytes == 0) { + err = ubi_wl_flush(ubi, UBI_ALL, UBI_ALL); + if (err) + return err; + err = clear_update_marker(ubi, vol, 0); if (err) return err; - err = ubi_wl_flush(ubi); - if (!err) - vol->updating = 0; + vol->updating = 0; + return 0; } vol->upd_buf = vmalloc(ubi->leb_size); if (!vol->upd_buf) return -ENOMEM; - tmp = bytes; - vol->upd_ebs = !!do_div(tmp, vol->usable_leb_size); - vol->upd_ebs += tmp; + vol->upd_ebs = div_u64(bytes + vol->usable_leb_size - 1, + vol->usable_leb_size); vol->upd_bytes = bytes; vol->upd_received = 0; return 0; @@ -175,17 +176,15 @@ int ubi_start_leb_change(struct ubi_device *ubi, struct ubi_volume *vol, { ubi_assert(!vol->updating && !vol->changing_leb); - dbg_msg("start changing LEB %d:%d, %u bytes", + dbg_gen("start changing LEB %d:%d, %u bytes", vol->vol_id, req->lnum, req->bytes); if (req->bytes == 0) - return ubi_eba_atomic_leb_change(ubi, vol, req->lnum, NULL, 0, - req->dtype); + return ubi_eba_atomic_leb_change(ubi, vol, req->lnum, NULL, 0); vol->upd_bytes = req->bytes; vol->upd_received = 0; vol->changing_leb = 1; vol->ch_lnum = req->lnum; - vol->ch_dtype = req->dtype; vol->upd_buf = vmalloc(req->bytes); if (!vol->upd_buf) @@ -234,11 +233,11 @@ static int write_leb(struct ubi_device *ubi, struct ubi_volume *vol, int lnum, memset(buf + len, 0xFF, l - len); len = ubi_calc_data_len(ubi, buf, l); if (len == 0) { - dbg_msg("all %d bytes contain 0xFF - skip", len); + dbg_gen("all %d bytes contain 0xFF - skip", len); return 0; } - err = ubi_eba_write_leb(ubi, vol, lnum, buf, 0, len, UBI_UNKNOWN); + err = ubi_eba_write_leb(ubi, vol, lnum, buf, 0, len); } else { /* * When writing static volume, and this is the last logical @@ -250,8 +249,7 @@ static int write_leb(struct ubi_device *ubi, struct ubi_volume *vol, int lnum, * contain zeros, not random trash. */ memset(buf + len, 0, vol->usable_leb_size - len); - err = ubi_eba_write_leb_st(ubi, vol, lnum, buf, len, - UBI_UNKNOWN, used_ebs); + err = ubi_eba_write_leb_st(ubi, vol, lnum, buf, len, used_ebs); } return err; @@ -259,6 +257,7 @@ static int write_leb(struct ubi_device *ubi, struct ubi_volume *vol, int lnum, /** * ubi_more_update_data - write more update data. + * @ubi: UBI device description object * @vol: volume description object * @buf: write data (user-space memory buffer) * @count: how much bytes to write @@ -272,19 +271,20 @@ static int write_leb(struct ubi_device *ubi, struct ubi_volume *vol, int lnum, int ubi_more_update_data(struct ubi_device *ubi, struct ubi_volume *vol, const void __user *buf, int count) { - uint64_t tmp; +#ifndef __UBOOT__ int lnum, offs, err = 0, len, to_write = count; +#else + int lnum, err = 0, len, to_write = count; + u32 offs; +#endif - dbg_msg("write %d of %lld bytes, %lld already passed", + dbg_gen("write %d of %lld bytes, %lld already passed", count, vol->upd_bytes, vol->upd_received); if (ubi->ro_mode) return -EROFS; - tmp = vol->upd_received; - offs = do_div(tmp, vol->usable_leb_size); - lnum = tmp; - + lnum = div_u64_rem(vol->upd_received, vol->usable_leb_size, &offs); if (vol->upd_received + count > vol->upd_bytes) to_write = count = vol->upd_bytes - vol->upd_received; @@ -359,16 +359,16 @@ int ubi_more_update_data(struct ubi_device *ubi, struct ubi_volume *vol, ubi_assert(vol->upd_received <= vol->upd_bytes); if (vol->upd_received == vol->upd_bytes) { + err = ubi_wl_flush(ubi, UBI_ALL, UBI_ALL); + if (err) + return err; /* The update is finished, clear the update marker */ err = clear_update_marker(ubi, vol, vol->upd_bytes); if (err) return err; - err = ubi_wl_flush(ubi); - if (err == 0) { - vol->updating = 0; - err = to_write; - vfree(vol->upd_buf); - } + vol->updating = 0; + err = to_write; + vfree(vol->upd_buf); } return err; @@ -376,6 +376,7 @@ int ubi_more_update_data(struct ubi_device *ubi, struct ubi_volume *vol, /** * ubi_more_leb_change_data - accept more data for atomic LEB change. + * @ubi: UBI device description object * @vol: volume description object * @buf: write data (user-space memory buffer) * @count: how much bytes to write @@ -392,7 +393,7 @@ int ubi_more_leb_change_data(struct ubi_device *ubi, struct ubi_volume *vol, { int err; - dbg_msg("write %d of %lld bytes, %lld already passed", + dbg_gen("write %d of %lld bytes, %lld already passed", count, vol->upd_bytes, vol->upd_received); if (ubi->ro_mode) @@ -410,10 +411,11 @@ int ubi_more_leb_change_data(struct ubi_device *ubi, struct ubi_volume *vol, if (vol->upd_received == vol->upd_bytes) { int len = ALIGN((int)vol->upd_bytes, ubi->min_io_size); - memset(vol->upd_buf + vol->upd_bytes, 0xFF, len - vol->upd_bytes); + memset(vol->upd_buf + vol->upd_bytes, 0xFF, + len - vol->upd_bytes); len = ubi_calc_data_len(ubi, vol->upd_buf, len); err = ubi_eba_atomic_leb_change(ubi, vol, vol->ch_lnum, - vol->upd_buf, len, UBI_UNKNOWN); + vol->upd_buf, len); if (err) return err; } diff --git a/drivers/mtd/ubi/vmt.c b/drivers/mtd/ubi/vmt.c index c4e894b..d9665a4 100644 --- a/drivers/mtd/ubi/vmt.c +++ b/drivers/mtd/ubi/vmt.c @@ -11,21 +11,22 @@ * resizing. */ -#ifdef UBI_LINUX +#define __UBOOT__ +#ifndef __UBOOT__ #include <linux/err.h> -#include <asm/div64.h> +#include <linux/slab.h> +#include <linux/export.h> +#else +#include <div64.h> +#include <ubi_uboot.h> #endif +#include <linux/math64.h> -#include <ubi_uboot.h> #include "ubi.h" -#ifdef CONFIG_MTD_UBI_DEBUG_PARANOID -static void paranoid_check_volumes(struct ubi_device *ubi); -#else -#define paranoid_check_volumes(ubi) -#endif +static int self_check_volumes(struct ubi_device *ubi); -#ifdef UBI_LINUX +#ifndef __UBOOT__ static ssize_t vol_attribute_show(struct device *dev, struct device_attribute *attr, char *buf); @@ -121,10 +122,11 @@ static void vol_release(struct device *dev) { struct ubi_volume *vol = container_of(dev, struct ubi_volume, dev); + kfree(vol->eba_tbl); kfree(vol); } -#ifdef UBI_LINUX +#ifndef __UBOOT__ /** * volume_sysfs_init - initialize sysfs for new volume. * @ubi: UBI device description object @@ -193,14 +195,13 @@ static void volume_sysfs_close(struct ubi_volume *vol) * %UBI_VOL_NUM_AUTO, this function automatically assign ID to the new volume * and saves it in @req->vol_id. Returns zero in case of success and a negative * error code in case of failure. Note, the caller has to have the - * @ubi->volumes_mutex locked. + * @ubi->device_mutex locked. */ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req) { - int i, err, vol_id = req->vol_id, dont_free = 0; + int i, err, vol_id = req->vol_id, do_free = 1; struct ubi_volume *vol; struct ubi_vtbl_record vtbl_rec; - uint64_t bytes; dev_t dev; if (ubi->ro_mode) @@ -213,7 +214,7 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req) spin_lock(&ubi->volumes_lock); if (vol_id == UBI_VOL_NUM_AUTO) { /* Find unused volume ID */ - dbg_msg("search for vacant volume ID"); + dbg_gen("search for vacant volume ID"); for (i = 0; i < ubi->vtbl_slots; i++) if (!ubi->volumes[i]) { vol_id = i; @@ -221,21 +222,21 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req) } if (vol_id == UBI_VOL_NUM_AUTO) { - dbg_err("out of volume IDs"); + ubi_err("out of volume IDs"); err = -ENFILE; goto out_unlock; } req->vol_id = vol_id; } - dbg_msg("volume ID %d, %llu bytes, type %d, name %s", - vol_id, (unsigned long long)req->bytes, + dbg_gen("create device %d, volume %d, %llu bytes, type %d, name %s", + ubi->ubi_num, vol_id, (unsigned long long)req->bytes, (int)req->vol_type, req->name); /* Ensure that this volume does not exist */ err = -EEXIST; if (ubi->volumes[vol_id]) { - dbg_err("volume %d already exists", vol_id); + ubi_err("volume %d already exists", vol_id); goto out_unlock; } @@ -244,20 +245,21 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req) if (ubi->volumes[i] && ubi->volumes[i]->name_len == req->name_len && !strcmp(ubi->volumes[i]->name, req->name)) { - dbg_err("volume \"%s\" exists (ID %d)", req->name, i); + ubi_err("volume \"%s\" exists (ID %d)", req->name, i); goto out_unlock; } /* Calculate how many eraseblocks are requested */ vol->usable_leb_size = ubi->leb_size - ubi->leb_size % req->alignment; - bytes = req->bytes; - if (do_div(bytes, vol->usable_leb_size)) - vol->reserved_pebs = 1; - vol->reserved_pebs += bytes; + vol->reserved_pebs += div_u64(req->bytes + vol->usable_leb_size - 1, + vol->usable_leb_size); /* Reserve physical eraseblocks */ if (vol->reserved_pebs > ubi->avail_pebs) { - dbg_err("not enough PEBs, only %d available", ubi->avail_pebs); + ubi_err("not enough PEBs, only %d available", ubi->avail_pebs); + if (ubi->corr_peb_count) + ubi_err("%d PEBs are corrupted and not used", + ubi->corr_peb_count); err = -ENOSPC; goto out_unlock; } @@ -270,14 +272,14 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req) vol->data_pad = ubi->leb_size % vol->alignment; vol->vol_type = req->vol_type; vol->name_len = req->name_len; - memcpy(vol->name, req->name, vol->name_len + 1); + memcpy(vol->name, req->name, vol->name_len); vol->ubi = ubi; /* * Finish all pending erases because there may be some LEBs belonging * to the same volume ID. */ - err = ubi_wl_flush(ubi); + err = ubi_wl_flush(ubi, vol_id, UBI_ALL); if (err) goto out_acc; @@ -296,10 +298,10 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req) vol->used_bytes = (long long)vol->used_ebs * vol->usable_leb_size; } else { - bytes = vol->used_bytes; - vol->last_eb_bytes = do_div(bytes, vol->usable_leb_size); - vol->used_ebs = bytes; - if (vol->last_eb_bytes) + vol->used_ebs = div_u64_rem(vol->used_bytes, + vol->usable_leb_size, + &vol->last_eb_bytes); + if (vol->last_eb_bytes != 0) vol->used_ebs += 1; else vol->last_eb_bytes = vol->usable_leb_size; @@ -315,20 +317,16 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req) goto out_mapping; } - err = ubi_create_gluebi(ubi, vol); - if (err) - goto out_cdev; - vol->dev.release = vol_release; vol->dev.parent = &ubi->dev; vol->dev.devt = dev; vol->dev.class = ubi_class; - sprintf(&vol->dev.bus_id[0], "%s_%d", ubi->ubi_name, vol->vol_id); + dev_set_name(&vol->dev, "%s_%d", ubi->ubi_name, vol->vol_id); err = device_register(&vol->dev); if (err) { ubi_err("cannot register device"); - goto out_gluebi; + goto out_cdev; } err = volume_sysfs_init(ubi, vol); @@ -345,7 +343,7 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req) vtbl_rec.vol_type = UBI_VID_DYNAMIC; else vtbl_rec.vol_type = UBI_VID_STATIC; - memcpy(vtbl_rec.name, vol->name, vol->name_len + 1); + memcpy(vtbl_rec.name, vol->name, vol->name_len); err = ubi_change_vtbl_record(ubi, vol_id, &vtbl_rec); if (err) @@ -356,39 +354,37 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req) ubi->vol_count += 1; spin_unlock(&ubi->volumes_lock); - paranoid_check_volumes(ubi); - return 0; + ubi_volume_notify(ubi, vol, UBI_VOLUME_ADDED); + self_check_volumes(ubi); + return err; out_sysfs: /* - * We have registered our device, we should not free the volume* + * We have registered our device, we should not free the volume * description object in this function in case of an error - it is * freed by the release function. * * Get device reference to prevent the release function from being * called just after sysfs has been closed. */ - dont_free = 1; + do_free = 0; get_device(&vol->dev); volume_sysfs_close(vol); -out_gluebi: - if (ubi_destroy_gluebi(vol)) - dbg_err("cannot destroy gluebi for volume %d:%d", - ubi->ubi_num, vol_id); out_cdev: cdev_del(&vol->cdev); out_mapping: - kfree(vol->eba_tbl); + if (do_free) + kfree(vol->eba_tbl); out_acc: spin_lock(&ubi->volumes_lock); ubi->rsvd_pebs -= vol->reserved_pebs; ubi->avail_pebs += vol->reserved_pebs; out_unlock: spin_unlock(&ubi->volumes_lock); - if (dont_free) - put_device(&vol->dev); - else + if (do_free) kfree(vol); + else + put_device(&vol->dev); ubi_err("cannot create volume %d, error %d", vol_id, err); return err; } @@ -396,19 +392,20 @@ out_unlock: /** * ubi_remove_volume - remove volume. * @desc: volume descriptor + * @no_vtbl: do not change volume table if not zero * * This function removes volume described by @desc. The volume has to be opened * in "exclusive" mode. Returns zero in case of success and a negative error - * code in case of failure. The caller has to have the @ubi->volumes_mutex + * code in case of failure. The caller has to have the @ubi->device_mutex * locked. */ -int ubi_remove_volume(struct ubi_volume_desc *desc) +int ubi_remove_volume(struct ubi_volume_desc *desc, int no_vtbl) { struct ubi_volume *vol = desc->vol; struct ubi_device *ubi = vol->ubi; int i, err, vol_id = vol->vol_id, reserved_pebs = vol->reserved_pebs; - dbg_msg("remove UBI volume %d", vol_id); + dbg_gen("remove device %d, volume %d", ubi->ubi_num, vol_id); ubi_assert(desc->mode == UBI_EXCLUSIVE); ubi_assert(vol == ubi->volumes[vol_id]); @@ -427,13 +424,11 @@ int ubi_remove_volume(struct ubi_volume_desc *desc) ubi->volumes[vol_id] = NULL; spin_unlock(&ubi->volumes_lock); - err = ubi_destroy_gluebi(vol); - if (err) - goto out_err; - - err = ubi_change_vtbl_record(ubi, vol_id, NULL); - if (err) - goto out_err; + if (!no_vtbl) { + err = ubi_change_vtbl_record(ubi, vol_id, NULL); + if (err) + goto out_err; + } for (i = 0; i < vol->reserved_pebs; i++) { err = ubi_eba_unmap_leb(ubi, vol, i); @@ -441,28 +436,21 @@ int ubi_remove_volume(struct ubi_volume_desc *desc) goto out_err; } - kfree(vol->eba_tbl); - vol->eba_tbl = NULL; cdev_del(&vol->cdev); volume_sysfs_close(vol); spin_lock(&ubi->volumes_lock); 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_update_reserved(ubi); ubi->vol_count -= 1; spin_unlock(&ubi->volumes_lock); - paranoid_check_volumes(ubi); - return 0; + ubi_volume_notify(ubi, vol, UBI_VOLUME_REMOVED); + if (!no_vtbl) + self_check_volumes(ubi); + + return err; out_err: ubi_err("cannot remove volume %d, error %d", vol_id, err); @@ -480,7 +468,7 @@ out_unlock: * * This function re-sizes the volume and returns zero in case of success, and a * negative error code in case of failure. The caller has to have the - * @ubi->volumes_mutex locked. + * @ubi->device_mutex locked. */ int ubi_resize_volume(struct ubi_volume_desc *desc, int reserved_pebs) { @@ -493,12 +481,12 @@ int ubi_resize_volume(struct ubi_volume_desc *desc, int reserved_pebs) if (ubi->ro_mode) return -EROFS; - dbg_msg("re-size volume %d to from %d to %d PEBs", - vol_id, vol->reserved_pebs, reserved_pebs); + dbg_gen("re-size device %d, volume %d to from %d to %d PEBs", + ubi->ubi_num, vol_id, vol->reserved_pebs, reserved_pebs); if (vol->vol_type == UBI_STATIC_VOLUME && reserved_pebs < vol->used_ebs) { - dbg_err("too small size %d, %d LEBs contain data", + ubi_err("too small size %d, %d LEBs contain data", reserved_pebs, vol->used_ebs); return -EINVAL; } @@ -527,8 +515,11 @@ int ubi_resize_volume(struct ubi_volume_desc *desc, int reserved_pebs) if (pebs > 0) { spin_lock(&ubi->volumes_lock); if (pebs > ubi->avail_pebs) { - dbg_err("not enough PEBs: requested %d, available %d", + ubi_err("not enough PEBs: requested %d, available %d", pebs, ubi->avail_pebs); + if (ubi->corr_peb_count) + ubi_err("%d PEBs are corrupted and not used", + ubi->corr_peb_count); spin_unlock(&ubi->volumes_lock); err = -ENOSPC; goto out_free; @@ -543,7 +534,7 @@ int ubi_resize_volume(struct ubi_volume_desc *desc, int reserved_pebs) } /* Change volume table record */ - memcpy(&vtbl_rec, &ubi->vtbl[vol_id], sizeof(struct ubi_vtbl_record)); + vtbl_rec = ubi->vtbl[vol_id]; vtbl_rec.reserved_pebs = cpu_to_be32(reserved_pebs); err = ubi_change_vtbl_record(ubi, vol_id, &vtbl_rec); if (err) @@ -558,15 +549,7 @@ int ubi_resize_volume(struct ubi_volume_desc *desc, int reserved_pebs) spin_lock(&ubi->volumes_lock); ubi->rsvd_pebs += pebs; ubi->avail_pebs -= pebs; - pebs = ubi->beb_rsvd_level - ubi->beb_rsvd_pebs; - if (pebs > 0) { - pebs = ubi->avail_pebs >= pebs ? pebs : ubi->avail_pebs; - ubi->avail_pebs -= pebs; - ubi->rsvd_pebs += pebs; - ubi->beb_rsvd_pebs += pebs; - if (pebs > 0) - ubi_msg("reserve more %d PEBs", pebs); - } + ubi_update_reserved(ubi); for (i = 0; i < reserved_pebs; i++) new_mapping[i] = vol->eba_tbl[i]; kfree(vol->eba_tbl); @@ -582,8 +565,9 @@ int ubi_resize_volume(struct ubi_volume_desc *desc, int reserved_pebs) (long long)vol->used_ebs * vol->usable_leb_size; } - paranoid_check_volumes(ubi); - return 0; + ubi_volume_notify(ubi, vol, UBI_VOLUME_RESIZED); + self_check_volumes(ubi); + return err; out_acc: if (pebs > 0) { @@ -598,6 +582,45 @@ out_free: } /** + * ubi_rename_volumes - re-name UBI volumes. + * @ubi: UBI device description object + * @rename_list: list of &struct ubi_rename_entry objects + * + * This function re-names or removes volumes specified in the re-name list. + * Returns zero in case of success and a negative error code in case of + * failure. + */ +int ubi_rename_volumes(struct ubi_device *ubi, struct list_head *rename_list) +{ + int err; + struct ubi_rename_entry *re; + + err = ubi_vtbl_rename_volumes(ubi, rename_list); + if (err) + return err; + + list_for_each_entry(re, rename_list, list) { + if (re->remove) { + err = ubi_remove_volume(re->desc, 1); + if (err) + break; + } else { + struct ubi_volume *vol = re->desc->vol; + + spin_lock(&ubi->volumes_lock); + vol->name_len = re->new_name_len; + memcpy(vol->name, re->new_name, re->new_name_len + 1); + spin_unlock(&ubi->volumes_lock); + ubi_volume_notify(ubi, vol, UBI_VOLUME_RENAMED); + } + } + + if (!err) + self_check_volumes(ubi); + return err; +} + +/** * ubi_add_volume - add volume. * @ubi: UBI device description object * @vol: volume description object @@ -611,8 +634,7 @@ int ubi_add_volume(struct ubi_device *ubi, struct ubi_volume *vol) int err, vol_id = vol->vol_id; dev_t dev; - dbg_msg("add volume %d", vol_id); - ubi_dbg_dump_vol_info(vol); + dbg_gen("add volume %d", vol_id); /* Register character device for the volume */ cdev_init(&vol->cdev, &ubi_vol_cdev_operations); @@ -625,32 +647,25 @@ int ubi_add_volume(struct ubi_device *ubi, struct ubi_volume *vol) return err; } - err = ubi_create_gluebi(ubi, vol); - if (err) - goto out_cdev; - vol->dev.release = vol_release; vol->dev.parent = &ubi->dev; vol->dev.devt = dev; vol->dev.class = ubi_class; - sprintf(&vol->dev.bus_id[0], "%s_%d", ubi->ubi_name, vol->vol_id); + dev_set_name(&vol->dev, "%s_%d", ubi->ubi_name, vol->vol_id); err = device_register(&vol->dev); if (err) - goto out_gluebi; + goto out_cdev; err = volume_sysfs_init(ubi, vol); if (err) { cdev_del(&vol->cdev); - err = ubi_destroy_gluebi(vol); volume_sysfs_close(vol); return err; } - paranoid_check_volumes(ubi); - return 0; + self_check_volumes(ubi); + return err; -out_gluebi: - err = ubi_destroy_gluebi(vol); out_cdev: cdev_del(&vol->cdev); return err; @@ -666,22 +681,21 @@ out_cdev: */ void ubi_free_volume(struct ubi_device *ubi, struct ubi_volume *vol) { - dbg_msg("free volume %d", vol->vol_id); + dbg_gen("free volume %d", vol->vol_id); ubi->volumes[vol->vol_id] = NULL; - ubi_destroy_gluebi(vol); cdev_del(&vol->cdev); volume_sysfs_close(vol); } -#ifdef CONFIG_MTD_UBI_DEBUG_PARANOID - /** - * paranoid_check_volume - check volume information. + * self_check_volume - check volume information. * @ubi: UBI device description object * @vol_id: volume ID + * + * Returns zero if volume is all right and a a negative error code if not. */ -static void paranoid_check_volume(struct ubi_device *ubi, int vol_id) +static int self_check_volume(struct ubi_device *ubi, int vol_id) { int idx = vol_id2idx(ubi, vol_id); int reserved_pebs, alignment, data_pad, vol_type, name_len, upd_marker; @@ -699,16 +713,7 @@ static void paranoid_check_volume(struct ubi_device *ubi, int vol_id) goto fail; } spin_unlock(&ubi->volumes_lock); - return; - } - - if (vol->exclusive) { - /* - * The volume may be being created at the moment, do not check - * it (e.g., it may be in the middle of ubi_create_volume(). - */ - spin_unlock(&ubi->volumes_lock); - return; + return 0; } if (vol->reserved_pebs < 0 || vol->alignment < 0 || vol->data_pad < 0 || @@ -740,7 +745,7 @@ static void paranoid_check_volume(struct ubi_device *ubi, int vol_id) } if (vol->upd_marker && vol->corrupted) { - dbg_err("update marker and corrupted simultaneously"); + ubi_err("update marker and corrupted simultaneously"); goto fail; } @@ -760,11 +765,6 @@ static void paranoid_check_volume(struct ubi_device *ubi, int vol_id) goto fail; } - if (!vol->name) { - ubi_err("NULL volume name"); - goto fail; - } - n = strnlen(vol->name, vol->name_len + 1); if (n != vol->name_len) { ubi_err("bad name_len %lld", n); @@ -818,31 +818,42 @@ static void paranoid_check_volume(struct ubi_device *ubi, int vol_id) if (alignment != vol->alignment || data_pad != vol->data_pad || upd_marker != vol->upd_marker || vol_type != vol->vol_type || - name_len!= vol->name_len || strncmp(name, vol->name, name_len)) { + name_len != vol->name_len || strncmp(name, vol->name, name_len)) { ubi_err("volume info is different"); goto fail; } spin_unlock(&ubi->volumes_lock); - return; + return 0; fail: - ubi_err("paranoid check failed for volume %d", vol_id); - ubi_dbg_dump_vol_info(vol); - ubi_dbg_dump_vtbl_record(&ubi->vtbl[vol_id], vol_id); + ubi_err("self-check failed for volume %d", vol_id); + if (vol) + ubi_dump_vol_info(vol); + ubi_dump_vtbl_record(&ubi->vtbl[vol_id], vol_id); + dump_stack(); spin_unlock(&ubi->volumes_lock); - BUG(); + return -EINVAL; } /** - * paranoid_check_volumes - check information about all volumes. + * self_check_volumes - check information about all volumes. * @ubi: UBI device description object + * + * Returns zero if volumes are all right and a a negative error code if not. */ -static void paranoid_check_volumes(struct ubi_device *ubi) +static int self_check_volumes(struct ubi_device *ubi) { - int i; + int i, err = 0; - for (i = 0; i < ubi->vtbl_slots; i++) - paranoid_check_volume(ubi, i); + if (!ubi_dbg_chk_gen(ubi)) + return 0; + + for (i = 0; i < ubi->vtbl_slots; i++) { + err = self_check_volume(ubi, i); + if (err) + break; + } + + return err; } -#endif diff --git a/drivers/mtd/ubi/vtbl.c b/drivers/mtd/ubi/vtbl.c index 3fbb4a0..e6c8f5b 100644 --- a/drivers/mtd/ubi/vtbl.c +++ b/drivers/mtd/ubi/vtbl.c @@ -25,16 +25,15 @@ * LEB 1. This scheme guarantees recoverability from unclean reboots. * * In this UBI implementation the on-flash volume table does not contain any - * information about how many data static volumes contain. This information may - * be found from the scanning data. + * information about how much data static volumes contain. * * But it would still be beneficial to store this information in the volume * table. For example, suppose we have a static volume X, and all its physical * eraseblocks became bad for some reasons. Suppose we are attaching the - * corresponding MTD device, the scanning has found no logical eraseblocks + * corresponding MTD device, for some reason we find no logical eraseblocks * corresponding to the volume X. According to the volume table volume X does * exist. So we don't know whether it is just empty or all its physical - * eraseblocks went bad. So we cannot alarm the user about this corruption. + * eraseblocks went bad. So we cannot alarm the user properly. * * The volume table also stores so-called "update marker", which is used for * volume updates. Before updating the volume, the update marker is set, and @@ -44,20 +43,20 @@ * damaged. */ -#ifdef UBI_LINUX +#define __UBOOT__ +#ifndef __UBOOT__ #include <linux/crc32.h> #include <linux/err.h> +#include <linux/slab.h> #include <asm/div64.h> +#else +#include <ubi_uboot.h> #endif -#include <ubi_uboot.h> +#include <linux/err.h> #include "ubi.h" -#ifdef CONFIG_MTD_UBI_DEBUG_PARANOID -static void paranoid_vtbl_check(const struct ubi_device *ubi); -#else -#define paranoid_vtbl_check(ubi) -#endif +static void self_vtbl_check(const struct ubi_device *ubi); /* Empty volume table record */ static struct ubi_vtbl_record empty_vtbl_record; @@ -97,18 +96,68 @@ int ubi_change_vtbl_record(struct ubi_device *ubi, int idx, return err; err = ubi_eba_write_leb(ubi, layout_vol, i, ubi->vtbl, 0, - ubi->vtbl_size, UBI_LONGTERM); + ubi->vtbl_size); + if (err) + return err; + } + + self_vtbl_check(ubi); + return 0; +} + +/** + * ubi_vtbl_rename_volumes - rename UBI volumes in the volume table. + * @ubi: UBI device description object + * @rename_list: list of &struct ubi_rename_entry objects + * + * This function re-names multiple volumes specified in @req in the volume + * table. Returns zero in case of success and a negative error code in case of + * failure. + */ +int ubi_vtbl_rename_volumes(struct ubi_device *ubi, + struct list_head *rename_list) +{ + int i, err; + struct ubi_rename_entry *re; + struct ubi_volume *layout_vol; + + list_for_each_entry(re, rename_list, list) { + uint32_t crc; + struct ubi_volume *vol = re->desc->vol; + struct ubi_vtbl_record *vtbl_rec = &ubi->vtbl[vol->vol_id]; + + if (re->remove) { + memcpy(vtbl_rec, &empty_vtbl_record, + sizeof(struct ubi_vtbl_record)); + continue; + } + + vtbl_rec->name_len = cpu_to_be16(re->new_name_len); + memcpy(vtbl_rec->name, re->new_name, re->new_name_len); + memset(vtbl_rec->name + re->new_name_len, 0, + UBI_VOL_NAME_MAX + 1 - re->new_name_len); + crc = crc32(UBI_CRC32_INIT, vtbl_rec, + UBI_VTBL_RECORD_SIZE_CRC); + vtbl_rec->crc = cpu_to_be32(crc); + } + + layout_vol = ubi->volumes[vol_id2idx(ubi, UBI_LAYOUT_VOLUME_ID)]; + for (i = 0; i < UBI_LAYOUT_VOLUME_EBS; i++) { + err = ubi_eba_unmap_leb(ubi, layout_vol, i); + if (err) + return err; + + err = ubi_eba_write_leb(ubi, layout_vol, i, ubi->vtbl, 0, + ubi->vtbl_size); if (err) return err; } - paranoid_vtbl_check(ubi); return 0; } /** - * vtbl_check - check if volume table is not corrupted and contains sensible - * data. + * vtbl_check - check if volume table is not corrupted and sensible. * @ubi: UBI device description object * @vtbl: volume table * @@ -132,13 +181,13 @@ static int vtbl_check(const struct ubi_device *ubi, upd_marker = vtbl[i].upd_marker; vol_type = vtbl[i].vol_type; name_len = be16_to_cpu(vtbl[i].name_len); - name = (const char *) &vtbl[i].name[0]; + name = &vtbl[i].name[0]; crc = crc32(UBI_CRC32_INIT, &vtbl[i], UBI_VTBL_RECORD_SIZE_CRC); if (be32_to_cpu(vtbl[i].crc) != crc) { ubi_err("bad CRC at record %u: %#08x, not %#08x", i, crc, be32_to_cpu(vtbl[i].crc)); - ubi_dbg_dump_vtbl_record(&vtbl[i], i); + ubi_dump_vtbl_record(&vtbl[i], i); return 1; } @@ -170,7 +219,7 @@ static int vtbl_check(const struct ubi_device *ubi, n = ubi->leb_size % alignment; if (data_pad != n) { - dbg_err("bad data_pad, has to be %d", n); + ubi_err("bad data_pad, has to be %d", n); err = 6; goto bad; } @@ -186,8 +235,8 @@ static int vtbl_check(const struct ubi_device *ubi, } if (reserved_pebs > ubi->good_peb_count) { - dbg_err("too large reserved_pebs, good PEBs %d", - ubi->good_peb_count); + ubi_err("too large reserved_pebs %d, good PEBs %d", + reserved_pebs, ubi->good_peb_count); err = 9; goto bad; } @@ -215,11 +264,15 @@ static int vtbl_check(const struct ubi_device *ubi, int len2 = be16_to_cpu(vtbl[n].name_len); if (len1 > 0 && len1 == len2 && - !strncmp((char *)vtbl[i].name, (char *)vtbl[n].name, len1)) { - ubi_err("volumes %d and %d have the same name" - " \"%s\"", i, n, vtbl[i].name); - ubi_dbg_dump_vtbl_record(&vtbl[i], i); - ubi_dbg_dump_vtbl_record(&vtbl[n], n); +#ifndef __UBOOT__ + !strncmp(vtbl[i].name, vtbl[n].name, len1)) { +#else + !strncmp((char *)vtbl[i].name, vtbl[n].name, len1)) { +#endif + ubi_err("volumes %d and %d have the same name \"%s\"", + i, n, vtbl[i].name); + ubi_dump_vtbl_record(&vtbl[i], i); + ubi_dump_vtbl_record(&vtbl[n], n); return -EINVAL; } } @@ -229,76 +282,64 @@ static int vtbl_check(const struct ubi_device *ubi, bad: ubi_err("volume table check failed: record %d, error %d", i, err); - ubi_dbg_dump_vtbl_record(&vtbl[i], i); + ubi_dump_vtbl_record(&vtbl[i], i); return -EINVAL; } /** * create_vtbl - create a copy of volume table. * @ubi: UBI device description object - * @si: scanning information + * @ai: attaching information * @copy: number of the volume table copy * @vtbl: contents of the volume table * * This function returns zero in case of success and a negative error code in * case of failure. */ -static int create_vtbl(struct ubi_device *ubi, struct ubi_scan_info *si, +static int create_vtbl(struct ubi_device *ubi, struct ubi_attach_info *ai, int copy, void *vtbl) { int err, tries = 0; - static struct ubi_vid_hdr *vid_hdr; - struct ubi_scan_volume *sv; - struct ubi_scan_leb *new_seb, *old_seb = NULL; + struct ubi_vid_hdr *vid_hdr; + struct ubi_ainf_peb *new_aeb; - ubi_msg("create volume table (copy #%d)", copy + 1); + dbg_gen("create volume table (copy #%d)", copy + 1); vid_hdr = ubi_zalloc_vid_hdr(ubi, GFP_KERNEL); if (!vid_hdr) return -ENOMEM; - /* - * Check if there is a logical eraseblock which would have to contain - * this volume table copy was found during scanning. It has to be wiped - * out. - */ - sv = ubi_scan_find_sv(si, UBI_LAYOUT_VOLUME_ID); - if (sv) - old_seb = ubi_scan_find_seb(sv, copy); - retry: - new_seb = ubi_scan_get_free_peb(ubi, si); - if (IS_ERR(new_seb)) { - err = PTR_ERR(new_seb); + new_aeb = ubi_early_get_peb(ubi, ai); + if (IS_ERR(new_aeb)) { + err = PTR_ERR(new_aeb); goto out_free; } - vid_hdr->vol_type = UBI_VID_DYNAMIC; + vid_hdr->vol_type = UBI_LAYOUT_VOLUME_TYPE; vid_hdr->vol_id = cpu_to_be32(UBI_LAYOUT_VOLUME_ID); vid_hdr->compat = UBI_LAYOUT_VOLUME_COMPAT; vid_hdr->data_size = vid_hdr->used_ebs = vid_hdr->data_pad = cpu_to_be32(0); vid_hdr->lnum = cpu_to_be32(copy); - vid_hdr->sqnum = cpu_to_be64(++si->max_sqnum); - vid_hdr->leb_ver = cpu_to_be32(old_seb ? old_seb->leb_ver + 1: 0); + vid_hdr->sqnum = cpu_to_be64(++ai->max_sqnum); /* The EC header is already there, write the VID header */ - err = ubi_io_write_vid_hdr(ubi, new_seb->pnum, vid_hdr); + err = ubi_io_write_vid_hdr(ubi, new_aeb->pnum, vid_hdr); if (err) goto write_error; /* Write the layout volume contents */ - err = ubi_io_write_data(ubi, vtbl, new_seb->pnum, 0, ubi->vtbl_size); + err = ubi_io_write_data(ubi, vtbl, new_aeb->pnum, 0, ubi->vtbl_size); if (err) goto write_error; /* - * And add it to the scanning information. Don't delete the old - * @old_seb as it will be deleted and freed in 'ubi_scan_add_used()'. + * And add it to the attaching information. Don't delete the old version + * of this LEB as it will be deleted and freed in 'ubi_add_to_av()'. */ - err = ubi_scan_add_used(ubi, si, new_seb->pnum, new_seb->ec, - vid_hdr, 0); - kfree(new_seb); + err = ubi_add_to_av(ubi, ai, new_aeb->pnum, new_aeb->ec, vid_hdr, 0); + kmem_cache_free(ai->aeb_slab_cache, new_aeb); ubi_free_vid_hdr(ubi, vid_hdr); return err; @@ -308,10 +349,10 @@ write_error: * Probably this physical eraseblock went bad, try to pick * another one. */ - list_add_tail(&new_seb->u.list, &si->corr); + list_add(&new_aeb->u.list, &ai->erase); goto retry; } - kfree(new_seb); + kmem_cache_free(ai->aeb_slab_cache, new_aeb); out_free: ubi_free_vid_hdr(ubi, vid_hdr); return err; @@ -321,20 +362,20 @@ out_free: /** * process_lvol - process the layout volume. * @ubi: UBI device description object - * @si: scanning information - * @sv: layout volume scanning information + * @ai: attaching information + * @av: layout volume attaching information * * This function is responsible for reading the layout volume, ensuring it is * not corrupted, and recovering from corruptions if needed. Returns volume * table in case of success and a negative error code in case of failure. */ static struct ubi_vtbl_record *process_lvol(struct ubi_device *ubi, - struct ubi_scan_info *si, - struct ubi_scan_volume *sv) + struct ubi_attach_info *ai, + struct ubi_ainf_volume *av) { int err; struct rb_node *rb; - struct ubi_scan_leb *seb; + struct ubi_ainf_peb *aeb; struct ubi_vtbl_record *leb[UBI_LAYOUT_VOLUME_EBS] = { NULL, NULL }; int leb_corrupted[UBI_LAYOUT_VOLUME_EBS] = {1, 1}; @@ -356,25 +397,24 @@ static struct ubi_vtbl_record *process_lvol(struct ubi_device *ubi, * 0 contains more recent information. * * So the plan is to first check LEB 0. Then - * a. if LEB 0 is OK, it must be containing the most resent data; then + * a. if LEB 0 is OK, it must be containing the most recent data; then * we compare it with LEB 1, and if they are different, we copy LEB * 0 to LEB 1; * b. if LEB 0 is corrupted, but LEB 1 has to be OK, and we copy LEB 1 * to LEB 0. */ - dbg_msg("check layout volume"); + dbg_gen("check layout volume"); /* Read both LEB 0 and LEB 1 into memory */ - ubi_rb_for_each_entry(rb, seb, &sv->root, u.rb) { - leb[seb->lnum] = vmalloc(ubi->vtbl_size); - if (!leb[seb->lnum]) { + ubi_rb_for_each_entry(rb, aeb, &av->root, u.rb) { + leb[aeb->lnum] = vzalloc(ubi->vtbl_size); + if (!leb[aeb->lnum]) { err = -ENOMEM; goto out_free; } - memset(leb[seb->lnum], 0, ubi->vtbl_size); - err = ubi_io_read_data(ubi, leb[seb->lnum], seb->pnum, 0, + err = ubi_io_read_data(ubi, leb[aeb->lnum], aeb->pnum, 0, ubi->vtbl_size); if (err == UBI_IO_BITFLIPS || mtd_is_eccerr(err)) /* @@ -382,12 +422,12 @@ static struct ubi_vtbl_record *process_lvol(struct ubi_device *ubi, * uncorrectable ECC error, but we have our own CRC and * the data will be checked later. If the data is OK, * the PEB will be scrubbed (because we set - * seb->scrub). If the data is not OK, the contents of + * aeb->scrub). If the data is not OK, the contents of * the PEB will be recovered from the second copy, and - * seb->scrub will be cleared in - * 'ubi_scan_add_used()'. + * aeb->scrub will be cleared in + * 'ubi_add_to_av()'. */ - seb->scrub = 1; + aeb->scrub = 1; else if (err) goto out_free; } @@ -402,10 +442,11 @@ static struct ubi_vtbl_record *process_lvol(struct ubi_device *ubi, if (!leb_corrupted[0]) { /* LEB 0 is OK */ if (leb[1]) - leb_corrupted[1] = memcmp(leb[0], leb[1], ubi->vtbl_size); + leb_corrupted[1] = memcmp(leb[0], leb[1], + ubi->vtbl_size); if (leb_corrupted[1]) { ubi_warn("volume table copy #2 is corrupted"); - err = create_vtbl(ubi, si, 1, leb[0]); + err = create_vtbl(ubi, ai, 1, leb[0]); if (err) goto out_free; ubi_msg("volume table was restored"); @@ -428,7 +469,7 @@ static struct ubi_vtbl_record *process_lvol(struct ubi_device *ubi, } ubi_warn("volume table copy #1 is corrupted"); - err = create_vtbl(ubi, si, 0, leb[1]); + err = create_vtbl(ubi, ai, 0, leb[1]); if (err) goto out_free; ubi_msg("volume table was restored"); @@ -446,21 +487,20 @@ out_free: /** * create_empty_lvol - create empty layout volume. * @ubi: UBI device description object - * @si: scanning information + * @ai: attaching information * * This function returns volume table contents in case of success and a * negative error code in case of failure. */ static struct ubi_vtbl_record *create_empty_lvol(struct ubi_device *ubi, - struct ubi_scan_info *si) + struct ubi_attach_info *ai) { int i; struct ubi_vtbl_record *vtbl; - vtbl = vmalloc(ubi->vtbl_size); + vtbl = vzalloc(ubi->vtbl_size); if (!vtbl) return ERR_PTR(-ENOMEM); - memset(vtbl, 0, ubi->vtbl_size); for (i = 0; i < ubi->vtbl_slots; i++) memcpy(&vtbl[i], &empty_vtbl_record, UBI_VTBL_RECORD_SIZE); @@ -468,7 +508,7 @@ static struct ubi_vtbl_record *create_empty_lvol(struct ubi_device *ubi, for (i = 0; i < UBI_LAYOUT_VOLUME_EBS; i++) { int err; - err = create_vtbl(ubi, si, i, vtbl); + err = create_vtbl(ubi, ai, i, vtbl); if (err) { vfree(vtbl); return ERR_PTR(err); @@ -481,18 +521,19 @@ static struct ubi_vtbl_record *create_empty_lvol(struct ubi_device *ubi, /** * init_volumes - initialize volume information for existing volumes. * @ubi: UBI device description object - * @si: scanning information + * @ai: scanning information * @vtbl: volume table * * This function allocates volume description objects for existing volumes. * Returns zero in case of success and a negative error code in case of * failure. */ -static int init_volumes(struct ubi_device *ubi, const struct ubi_scan_info *si, +static int init_volumes(struct ubi_device *ubi, + const struct ubi_attach_info *ai, const struct ubi_vtbl_record *vtbl) { int i, reserved_pebs = 0; - struct ubi_scan_volume *sv; + struct ubi_ainf_volume *av; struct ubi_volume *vol; for (i = 0; i < ubi->vtbl_slots; i++) { @@ -520,8 +561,8 @@ static int init_volumes(struct ubi_device *ubi, const struct ubi_scan_info *si, if (vtbl[i].flags & UBI_VTBL_AUTORESIZE_FLG) { /* Auto re-size flag may be set only for one volume */ if (ubi->autoresize_vol_id != -1) { - ubi_err("more then one auto-resize volume (%d " - "and %d)", ubi->autoresize_vol_id, i); + ubi_err("more than one auto-resize volume (%d and %d)", + ubi->autoresize_vol_id, i); kfree(vol); return -EINVAL; } @@ -548,8 +589,8 @@ static int init_volumes(struct ubi_device *ubi, const struct ubi_scan_info *si, } /* Static volumes only */ - sv = ubi_scan_find_sv(si, i); - if (!sv) { + av = ubi_find_av(ai, i); + if (!av) { /* * No eraseblocks belonging to this volume found. We * don't actually know whether this static volume is @@ -561,22 +602,22 @@ static int init_volumes(struct ubi_device *ubi, const struct ubi_scan_info *si, continue; } - if (sv->leb_count != sv->used_ebs) { + if (av->leb_count != av->used_ebs) { /* * We found a static volume which misses several * eraseblocks. Treat it as corrupted. */ ubi_warn("static volume %d misses %d LEBs - corrupted", - sv->vol_id, sv->used_ebs - sv->leb_count); + av->vol_id, av->used_ebs - av->leb_count); vol->corrupted = 1; continue; } - vol->used_ebs = sv->used_ebs; + vol->used_ebs = av->used_ebs; vol->used_bytes = (long long)(vol->used_ebs - 1) * vol->usable_leb_size; - vol->used_bytes += sv->last_data_size; - vol->last_eb_bytes = sv->last_data_size; + vol->used_bytes += av->last_data_size; + vol->last_eb_bytes = av->last_data_size; } /* And add the layout volume */ @@ -585,7 +626,7 @@ static int init_volumes(struct ubi_device *ubi, const struct ubi_scan_info *si, return -ENOMEM; vol->reserved_pebs = UBI_LAYOUT_VOLUME_EBS; - vol->alignment = 1; + vol->alignment = UBI_LAYOUT_VOLUME_ALIGN; vol->vol_type = UBI_DYNAMIC_VOLUME; vol->name_len = sizeof(UBI_LAYOUT_VOLUME_NAME) - 1; memcpy(vol->name, UBI_LAYOUT_VOLUME_NAME, vol->name_len + 1); @@ -603,9 +644,13 @@ static int init_volumes(struct ubi_device *ubi, const struct ubi_scan_info *si, ubi->vol_count += 1; vol->ubi = ubi; - if (reserved_pebs > ubi->avail_pebs) + if (reserved_pebs > ubi->avail_pebs) { ubi_err("not enough PEBs, required %d, available %d", reserved_pebs, ubi->avail_pebs); + if (ubi->corr_peb_count) + ubi_err("%d PEBs are corrupted and not used", + ubi->corr_peb_count); + } ubi->rsvd_pebs += reserved_pebs; ubi->avail_pebs -= reserved_pebs; @@ -613,105 +658,104 @@ static int init_volumes(struct ubi_device *ubi, const struct ubi_scan_info *si, } /** - * check_sv - check volume scanning information. + * check_av - check volume attaching information. * @vol: UBI volume description object - * @sv: volume scanning information + * @av: volume attaching information * - * This function returns zero if the volume scanning information is consistent + * This function returns zero if the volume attaching information is consistent * to the data read from the volume tabla, and %-EINVAL if not. */ -static int check_sv(const struct ubi_volume *vol, - const struct ubi_scan_volume *sv) +static int check_av(const struct ubi_volume *vol, + const struct ubi_ainf_volume *av) { int err; - if (sv->highest_lnum >= vol->reserved_pebs) { + if (av->highest_lnum >= vol->reserved_pebs) { err = 1; goto bad; } - if (sv->leb_count > vol->reserved_pebs) { + if (av->leb_count > vol->reserved_pebs) { err = 2; goto bad; } - if (sv->vol_type != vol->vol_type) { + if (av->vol_type != vol->vol_type) { err = 3; goto bad; } - if (sv->used_ebs > vol->reserved_pebs) { + if (av->used_ebs > vol->reserved_pebs) { err = 4; goto bad; } - if (sv->data_pad != vol->data_pad) { + if (av->data_pad != vol->data_pad) { err = 5; goto bad; } return 0; bad: - ubi_err("bad scanning information, error %d", err); - ubi_dbg_dump_sv(sv); - ubi_dbg_dump_vol_info(vol); + ubi_err("bad attaching information, error %d", err); + ubi_dump_av(av); + ubi_dump_vol_info(vol); return -EINVAL; } /** - * check_scanning_info - check that scanning information. + * check_attaching_info - check that attaching information. * @ubi: UBI device description object - * @si: scanning information + * @ai: attaching information * * Even though we protect on-flash data by CRC checksums, we still don't trust - * the media. This function ensures that scanning information is consistent to - * the information read from the volume table. Returns zero if the scanning + * the media. This function ensures that attaching information is consistent to + * the information read from the volume table. Returns zero if the attaching * information is OK and %-EINVAL if it is not. */ -static int check_scanning_info(const struct ubi_device *ubi, - struct ubi_scan_info *si) +static int check_attaching_info(const struct ubi_device *ubi, + struct ubi_attach_info *ai) { int err, i; - struct ubi_scan_volume *sv; + struct ubi_ainf_volume *av; struct ubi_volume *vol; - if (si->vols_found > UBI_INT_VOL_COUNT + ubi->vtbl_slots) { - ubi_err("scanning found %d volumes, maximum is %d + %d", - si->vols_found, UBI_INT_VOL_COUNT, ubi->vtbl_slots); + if (ai->vols_found > UBI_INT_VOL_COUNT + ubi->vtbl_slots) { + ubi_err("found %d volumes while attaching, maximum is %d + %d", + ai->vols_found, UBI_INT_VOL_COUNT, ubi->vtbl_slots); return -EINVAL; } - if (si->highest_vol_id >= ubi->vtbl_slots + UBI_INT_VOL_COUNT && - si->highest_vol_id < UBI_INTERNAL_VOL_START) { - ubi_err("too large volume ID %d found by scanning", - si->highest_vol_id); + if (ai->highest_vol_id >= ubi->vtbl_slots + UBI_INT_VOL_COUNT && + ai->highest_vol_id < UBI_INTERNAL_VOL_START) { + ubi_err("too large volume ID %d found", ai->highest_vol_id); return -EINVAL; } for (i = 0; i < ubi->vtbl_slots + UBI_INT_VOL_COUNT; i++) { cond_resched(); - sv = ubi_scan_find_sv(si, i); + av = ubi_find_av(ai, i); vol = ubi->volumes[i]; if (!vol) { - if (sv) - ubi_scan_rm_volume(si, sv); + if (av) + ubi_remove_av(ai, av); continue; } if (vol->reserved_pebs == 0) { ubi_assert(i < ubi->vtbl_slots); - if (!sv) + if (!av) continue; /* - * During scanning we found a volume which does not + * During attaching we found a volume which does not * exist according to the information in the volume * table. This must have happened due to an unclean * reboot while the volume was being removed. Discard * these eraseblocks. */ - ubi_msg("finish volume %d removal", sv->vol_id); - ubi_scan_rm_volume(si, sv); - } else if (sv) { - err = check_sv(vol, sv); + ubi_msg("finish volume %d removal", av->vol_id); + ubi_remove_av(ai, av); + } else if (av) { + err = check_av(vol, av); if (err) return err; } @@ -721,19 +765,18 @@ static int check_scanning_info(const struct ubi_device *ubi, } /** - * ubi_read_volume_table - read volume table. - * information. + * ubi_read_volume_table - read the volume table. * @ubi: UBI device description object - * @si: scanning information + * @ai: attaching information * * This function reads volume table, checks it, recover from errors if needed, * or creates it if needed. Returns zero in case of success and a negative * error code in case of failure. */ -int ubi_read_volume_table(struct ubi_device *ubi, struct ubi_scan_info *si) +int ubi_read_volume_table(struct ubi_device *ubi, struct ubi_attach_info *ai) { int i, err; - struct ubi_scan_volume *sv; + struct ubi_ainf_volume *av; empty_vtbl_record.crc = cpu_to_be32(0xf116c36b); @@ -748,8 +791,8 @@ int ubi_read_volume_table(struct ubi_device *ubi, struct ubi_scan_info *si) ubi->vtbl_size = ubi->vtbl_slots * UBI_VTBL_RECORD_SIZE; ubi->vtbl_size = ALIGN(ubi->vtbl_size, ubi->min_io_size); - sv = ubi_scan_find_sv(si, UBI_LAYOUT_VOLUME_ID); - if (!sv) { + av = ubi_find_av(ai, UBI_LAYOUT_VOLUME_ID); + if (!av) { /* * No logical eraseblocks belonging to the layout volume were * found. This could mean that the flash is just empty. In @@ -758,8 +801,8 @@ int ubi_read_volume_table(struct ubi_device *ubi, struct ubi_scan_info *si) * But if flash is not empty this must be a corruption or the * MTD device just contains garbage. */ - if (si->is_empty) { - ubi->vtbl = create_empty_lvol(ubi, si); + if (ai->is_empty) { + ubi->vtbl = create_empty_lvol(ubi, ai); if (IS_ERR(ubi->vtbl)) return PTR_ERR(ubi->vtbl); } else { @@ -767,33 +810,33 @@ int ubi_read_volume_table(struct ubi_device *ubi, struct ubi_scan_info *si) return -EINVAL; } } else { - if (sv->leb_count > UBI_LAYOUT_VOLUME_EBS) { + if (av->leb_count > UBI_LAYOUT_VOLUME_EBS) { /* This must not happen with proper UBI images */ - dbg_err("too many LEBs (%d) in layout volume", - sv->leb_count); + ubi_err("too many LEBs (%d) in layout volume", + av->leb_count); return -EINVAL; } - ubi->vtbl = process_lvol(ubi, si, sv); + ubi->vtbl = process_lvol(ubi, ai, av); if (IS_ERR(ubi->vtbl)) return PTR_ERR(ubi->vtbl); } - ubi->avail_pebs = ubi->good_peb_count; + ubi->avail_pebs = ubi->good_peb_count - ubi->corr_peb_count; /* * The layout volume is OK, initialize the corresponding in-RAM data * structures. */ - err = init_volumes(ubi, si, ubi->vtbl); + err = init_volumes(ubi, ai, ubi->vtbl); if (err) goto out_free; /* - * Get sure that the scanning information is consistent to the + * Make sure that the attaching information is consistent to the * information stored in the volume table. */ - err = check_scanning_info(ubi, si); + err = check_attaching_info(ubi, ai); if (err) goto out_free; @@ -801,26 +844,24 @@ int ubi_read_volume_table(struct ubi_device *ubi, struct ubi_scan_info *si) out_free: vfree(ubi->vtbl); - for (i = 0; i < ubi->vtbl_slots + UBI_INT_VOL_COUNT; i++) - if (ubi->volumes[i]) { - kfree(ubi->volumes[i]); - ubi->volumes[i] = NULL; - } + for (i = 0; i < ubi->vtbl_slots + UBI_INT_VOL_COUNT; i++) { + kfree(ubi->volumes[i]); + ubi->volumes[i] = NULL; + } return err; } -#ifdef CONFIG_MTD_UBI_DEBUG_PARANOID - /** - * paranoid_vtbl_check - check volume table. + * self_vtbl_check - check volume table. * @ubi: UBI device description object */ -static void paranoid_vtbl_check(const struct ubi_device *ubi) +static void self_vtbl_check(const struct ubi_device *ubi) { + if (!ubi_dbg_chk_gen(ubi)) + return; + if (vtbl_check(ubi, ubi->vtbl)) { - ubi_err("paranoid check failed"); + ubi_err("self-check failed"); BUG(); } } - -#endif /* CONFIG_MTD_UBI_DEBUG_PARANOID */ diff --git a/drivers/mtd/ubi/wl.c b/drivers/mtd/ubi/wl.c index 1eaa88b..1023090 100644 --- a/drivers/mtd/ubi/wl.c +++ b/drivers/mtd/ubi/wl.c @@ -7,97 +7,117 @@ */ /* - * UBI wear-leveling unit. + * UBI wear-leveling sub-system. * - * 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. + * This sub-system 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 sub-system'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. + * header. The rest of the physical eraseblock contains only %0xFF bytes. * - * When physical eraseblocks are returned to the WL unit by means of the + * When physical eraseblocks are returned to the WL sub-system 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. + * which is also managed by the WL sub-system. * * 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 sub-system fails to erase a physical eraseblock, it marks it as + * bad. * - * If the WL unit fails to erase a physical eraseblock, it marks it as bad. + * This sub-system 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. * - * 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 sub-system all physical eraseblocks are either + * "free" or "used". Free eraseblock are kept in the @wl->free RB-tree, while + * used eraseblocks are kept in @wl->used, @wl->erroneous, or @wl->scrub + * RB-trees, as well as (temporarily) in the @wl->pq queue. * - * 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. + * When the WL sub-system 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 a protection queue in between where this + * physical eraseblock is temporarily stored (@wl->pq). + * + * 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. + * + * 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 + * protection queue. Eraseblocks are put to the tail of this queue when they + * are returned by the 'ubi_wl_get_peb()', and eraseblocks are removed from the + * head of the queue on each erase operation (for any eraseblock). So the + * length of the queue defines how may (global) erase cycles PEBs are protected. + * + * To put it differently, 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 disallowed (@wl->erroneous) because the PEB is + * erroneous - e.g., there was a read error; + * o the WL movement is temporarily prohibited (@wl->pq queue); + * 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 structures. * * 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. + * re-work this sub-system 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 + * At the moment this sub-system 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). + * room for future re-works of the WL sub-system. */ -#ifdef UBI_LINUX +#define __UBOOT__ +#ifndef __UBOOT__ #include <linux/slab.h> #include <linux/crc32.h> #include <linux/freezer.h> #include <linux/kthread.h> +#else +#include <ubi_uboot.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. + * exceeded, the WL sub-system 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 + * When a physical eraseblock is moved, the WL sub-system 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 of the free physical eraseblock to pick. Namely, the WL sub-system + * does not pick eraseblocks with erase counter greater than the lowest erase * counter plus %WL_FREE_MAX_DIFF. */ #define WL_FREE_MAX_DIFF (2*UBI_WL_THRESHOLD) @@ -108,89 +128,48 @@ */ #define WL_MAX_FAILURES 32 +static int self_check_ec(struct ubi_device *ubi, int pnum, int ec); +static int self_check_in_wl_tree(const struct ubi_device *ubi, + struct ubi_wl_entry *e, struct rb_root *root); +static int self_check_in_pq(const struct ubi_device *ubi, + struct ubi_wl_entry *e); + +#ifdef CONFIG_MTD_UBI_FASTMAP +#ifndef __UBOOT__ /** - * 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. + * update_fastmap_work_fn - calls ubi_update_fastmap from a work queue + * @wrk: the work description object */ -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; -}; +static void update_fastmap_work_fn(struct work_struct *wrk) +{ + struct ubi_device *ubi = container_of(wrk, struct ubi_device, fm_work); + ubi_update_fastmap(ubi); +} +#endif /** - * 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. + * ubi_ubi_is_fm_block - returns 1 if a PEB is currently used in a fastmap. + * @ubi: UBI device description object + * @pnum: the to be checked PEB */ -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; -}; +static int ubi_is_fm_block(struct ubi_device *ubi, int pnum) +{ + int i; + + if (!ubi->fm) + return 0; + + for (i = 0; i < ubi->fm->used_blocks; i++) + if (ubi->fm->e[i]->pnum == pnum) + return 1; -#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); + return 0; +} #else -#define paranoid_check_ec(ubi, pnum, ec) 0 -#define paranoid_check_in_wl_tree(e, root) +static int ubi_is_fm_block(struct ubi_device *ubi, int pnum) +{ + return 0; +} #endif /** @@ -210,7 +189,7 @@ static void wl_tree_add(struct ubi_wl_entry *e, struct rb_root *root) struct ubi_wl_entry *e1; parent = *p; - e1 = rb_entry(parent, struct ubi_wl_entry, rb); + e1 = rb_entry(parent, struct ubi_wl_entry, u.rb); if (e->ec < e1->ec) p = &(*p)->rb_left; @@ -225,8 +204,8 @@ static void wl_tree_add(struct ubi_wl_entry *e, struct rb_root *root) } } - rb_link_node(&e->rb, parent, p); - rb_insert_color(&e->rb, root); + rb_link_node(&e->u.rb, parent, p); + rb_insert_color(&e->u.rb, root); } /** @@ -289,18 +268,16 @@ 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); + if (err) + return err; } - spin_unlock(&ubi->wl_lock); return 0; } @@ -321,7 +298,7 @@ static int in_wl_tree(struct ubi_wl_entry *e, struct rb_root *root) while (p) { struct ubi_wl_entry *e1; - e1 = rb_entry(p, struct ubi_wl_entry, rb); + e1 = rb_entry(p, struct ubi_wl_entry, u.rb); if (e->pnum == e1->pnum) { ubi_assert(e == e1); @@ -345,223 +322,406 @@ static int in_wl_tree(struct ubi_wl_entry *e, struct rb_root *root) } /** - * prot_tree_add - add physical eraseblock to protection trees. + * prot_queue_add - add physical eraseblock to the protection queue. * @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. + * This function adds @e to the tail of the protection queue @ubi->pq, where + * @e will stay for %UBI_PROT_QUEUE_LEN erase operations and will be + * temporarily protected from the wear-leveling worker. Note, @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) +static void prot_queue_add(struct ubi_device *ubi, struct ubi_wl_entry *e) { - 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); + int pq_tail = ubi->pq_head - 1; - 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); + if (pq_tail < 0) + pq_tail = UBI_PROT_QUEUE_LEN - 1; + ubi_assert(pq_tail >= 0 && pq_tail < UBI_PROT_QUEUE_LEN); + list_add_tail(&e->u.list, &ubi->pq[pq_tail]); + dbg_wl("added PEB %d EC %d to the protection queue", e->pnum, e->ec); } /** * find_wl_entry - find wear-leveling entry closest to certain erase counter. + * @ubi: UBI device description object * @root: the RB-tree where to look for - * @max: highest possible erase counter + * @diff: maximum possible difference from the smallest erase counter * * This function looks for a wear leveling entry with erase counter closest to - * @max and less then @max. + * min + @diff, where min is the smallest erase counter. */ -static struct ubi_wl_entry *find_wl_entry(struct rb_root *root, int max) +static struct ubi_wl_entry *find_wl_entry(struct ubi_device *ubi, + struct rb_root *root, int diff) { struct rb_node *p; - struct ubi_wl_entry *e; + struct ubi_wl_entry *e, *prev_e = NULL; + int max; - e = rb_entry(rb_first(root), struct ubi_wl_entry, rb); - max += e->ec; + e = rb_entry(rb_first(root), struct ubi_wl_entry, u.rb); + max = e->ec + diff; p = root->rb_node; while (p) { struct ubi_wl_entry *e1; - e1 = rb_entry(p, struct ubi_wl_entry, rb); + e1 = rb_entry(p, struct ubi_wl_entry, u.rb); if (e1->ec >= max) p = p->rb_left; else { p = p->rb_right; + prev_e = e; e = e1; } } + /* If no fastmap has been written and this WL entry can be used + * as anchor PEB, hold it back and return the second best WL entry + * such that fastmap can use the anchor PEB later. */ + if (prev_e && !ubi->fm_disabled && + !ubi->fm && e->pnum < UBI_FM_MAX_START) + return prev_e; + return e; } /** - * ubi_wl_get_peb - get a physical eraseblock. + * find_mean_wl_entry - find wear-leveling entry with medium erase counter. * @ubi: UBI device description object - * @dtype: type of data which will be stored in this physical eraseblock + * @root: the RB-tree where to look for * - * This function returns a physical eraseblock in case of success and a - * negative error code in case of failure. Might sleep. + * This function looks for a wear leveling entry with medium erase counter, + * but not greater or equivalent than the lowest erase counter plus + * %WL_FREE_MAX_DIFF/2. */ -int ubi_wl_get_peb(struct ubi_device *ubi, int dtype) +static struct ubi_wl_entry *find_mean_wl_entry(struct ubi_device *ubi, + struct rb_root *root) { - 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); + first = rb_entry(rb_first(root), struct ubi_wl_entry, u.rb); + last = rb_entry(rb_last(root), struct ubi_wl_entry, u.rb); - pe = kmalloc(sizeof(struct ubi_wl_prot_entry), GFP_NOFS); - if (!pe) - return -ENOMEM; + if (last->ec - first->ec < WL_FREE_MAX_DIFF) { + e = rb_entry(root->rb_node, struct ubi_wl_entry, u.rb); + +#ifdef CONFIG_MTD_UBI_FASTMAP + /* If no fastmap has been written and this WL entry can be used + * as anchor PEB, hold it back and return the second best + * WL entry such that fastmap can use the anchor PEB later. */ + if (e && !ubi->fm_disabled && !ubi->fm && + e->pnum < UBI_FM_MAX_START) + e = rb_entry(rb_next(root->rb_node), + struct ubi_wl_entry, u.rb); +#endif + } else + e = find_wl_entry(ubi, root, WL_FREE_MAX_DIFF/2); + + return e; +} + +#ifdef CONFIG_MTD_UBI_FASTMAP +/** + * find_anchor_wl_entry - find wear-leveling entry to used as anchor PEB. + * @root: the RB-tree where to look for + */ +static struct ubi_wl_entry *find_anchor_wl_entry(struct rb_root *root) +{ + struct rb_node *p; + struct ubi_wl_entry *e, *victim = NULL; + int max_ec = UBI_MAX_ERASECOUNTER; + + ubi_rb_for_each_entry(p, e, root, u.rb) { + if (e->pnum < UBI_FM_MAX_START && e->ec < max_ec) { + victim = e; + max_ec = e->ec; + } + } + + return victim; +} + +static int anchor_pebs_avalible(struct rb_root *root) +{ + struct rb_node *p; + struct ubi_wl_entry *e; + + ubi_rb_for_each_entry(p, e, root, u.rb) + if (e->pnum < UBI_FM_MAX_START) + return 1; + + return 0; +} + +/** + * ubi_wl_get_fm_peb - find a physical erase block with a given maximal number. + * @ubi: UBI device description object + * @anchor: This PEB will be used as anchor PEB by fastmap + * + * The function returns a physical erase block with a given maximal number + * and removes it from the wl subsystem. + * Must be called with wl_lock held! + */ +struct ubi_wl_entry *ubi_wl_get_fm_peb(struct ubi_device *ubi, int anchor) +{ + struct ubi_wl_entry *e = NULL; + + if (!ubi->free.rb_node || (ubi->free_count - ubi->beb_rsvd_pebs < 1)) + goto out; + + if (anchor) + e = find_anchor_wl_entry(&ubi->free); + else + e = find_mean_wl_entry(ubi, &ubi->free); + + if (!e) + goto out; + + self_check_in_wl_tree(ubi, e, &ubi->free); + + /* remove it from the free list, + * the wl subsystem does no longer know this erase block */ + rb_erase(&e->u.rb, &ubi->free); + ubi->free_count--; +out: + return e; +} +#endif + +/** + * __wl_get_peb - get a physical eraseblock. + * @ubi: UBI device description object + * + * This function returns a physical eraseblock in case of success and a + * negative error code in case of failure. + */ +static int __wl_get_peb(struct ubi_device *ubi) +{ + int err; + struct ubi_wl_entry *e; 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); + ubi_assert(list_empty(&ubi->works)); return -ENOSPC; } - spin_unlock(&ubi->wl_lock); err = produce_free_peb(ubi); - if (err < 0) { - kfree(pe); + if (err < 0) 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(); + e = find_mean_wl_entry(ubi, &ubi->free); + if (!e) { + ubi_err("no free eraseblocks"); + return -ENOSPC; } + self_check_in_wl_tree(ubi, e, &ubi->free); + /* - * Move the physical eraseblock to the protection trees where it will + * Move the physical eraseblock to the protection queue 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); + rb_erase(&e->u.rb, &ubi->free); + ubi->free_count--; + dbg_wl("PEB %d EC %d", e->pnum, e->ec); +#ifndef CONFIG_MTD_UBI_FASTMAP + /* We have to enqueue e only if fastmap is disabled, + * is fastmap enabled prot_queue_add() will be called by + * ubi_wl_get_peb() after removing e from the pool. */ + prot_queue_add(ubi, e); +#endif + return e->pnum; +} - dbg_wl("PEB %d EC %d, protection %d", e->pnum, e->ec, protect); - spin_unlock(&ubi->wl_lock); +#ifdef CONFIG_MTD_UBI_FASTMAP +/** + * return_unused_pool_pebs - returns unused PEB to the free tree. + * @ubi: UBI device description object + * @pool: fastmap pool description object + */ +static void return_unused_pool_pebs(struct ubi_device *ubi, + struct ubi_fm_pool *pool) +{ + int i; + struct ubi_wl_entry *e; - return e->pnum; + for (i = pool->used; i < pool->size; i++) { + e = ubi->lookuptbl[pool->pebs[i]]; + wl_tree_add(e, &ubi->free); + ubi->free_count++; + } } /** - * prot_tree_del - remove a physical eraseblock from the protection trees + * refill_wl_pool - refills all the fastmap pool used by the + * WL sub-system. * @ubi: UBI device description object - * @pnum: the physical eraseblock to remove + */ +static void refill_wl_pool(struct ubi_device *ubi) +{ + struct ubi_wl_entry *e; + struct ubi_fm_pool *pool = &ubi->fm_wl_pool; + + return_unused_pool_pebs(ubi, pool); + + for (pool->size = 0; pool->size < pool->max_size; pool->size++) { + if (!ubi->free.rb_node || + (ubi->free_count - ubi->beb_rsvd_pebs < 5)) + break; + + e = find_wl_entry(ubi, &ubi->free, WL_FREE_MAX_DIFF); + self_check_in_wl_tree(ubi, e, &ubi->free); + rb_erase(&e->u.rb, &ubi->free); + ubi->free_count--; + + pool->pebs[pool->size] = e->pnum; + } + pool->used = 0; +} + +/** + * refill_wl_user_pool - refills all the fastmap pool used by ubi_wl_get_peb. + * @ubi: UBI device description object + */ +static void refill_wl_user_pool(struct ubi_device *ubi) +{ + struct ubi_fm_pool *pool = &ubi->fm_pool; + + return_unused_pool_pebs(ubi, pool); + + for (pool->size = 0; pool->size < pool->max_size; pool->size++) { + pool->pebs[pool->size] = __wl_get_peb(ubi); + if (pool->pebs[pool->size] < 0) + break; + } + pool->used = 0; +} + +/** + * ubi_refill_pools - refills all fastmap PEB pools. + * @ubi: UBI device description object + */ +void ubi_refill_pools(struct ubi_device *ubi) +{ + spin_lock(&ubi->wl_lock); + refill_wl_pool(ubi); + refill_wl_user_pool(ubi); + spin_unlock(&ubi->wl_lock); +} + +/* ubi_wl_get_peb - works exaclty like __wl_get_peb but keeps track of + * the fastmap pool. + */ +int ubi_wl_get_peb(struct ubi_device *ubi) +{ + int ret; + struct ubi_fm_pool *pool = &ubi->fm_pool; + struct ubi_fm_pool *wl_pool = &ubi->fm_wl_pool; + + if (!pool->size || !wl_pool->size || pool->used == pool->size || + wl_pool->used == wl_pool->size) + ubi_update_fastmap(ubi); + + /* we got not a single free PEB */ + if (!pool->size) + ret = -ENOSPC; + else { + spin_lock(&ubi->wl_lock); + ret = pool->pebs[pool->used++]; + prot_queue_add(ubi, ubi->lookuptbl[ret]); + spin_unlock(&ubi->wl_lock); + } + + return ret; +} + +/* get_peb_for_wl - returns a PEB to be used internally by the WL sub-system. * - * 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. + * @ubi: UBI device description object */ -static int prot_tree_del(struct ubi_device *ubi, int pnum) +static struct ubi_wl_entry *get_peb_for_wl(struct ubi_device *ubi) { - struct rb_node *p; - struct ubi_wl_prot_entry *pe = NULL; + struct ubi_fm_pool *pool = &ubi->fm_wl_pool; + int pnum; + + if (pool->used == pool->size || !pool->size) { + /* We cannot update the fastmap here because this + * function is called in atomic context. + * Let's fail here and refill/update it as soon as possible. */ +#ifndef __UBOOT__ + schedule_work(&ubi->fm_work); +#else + /* In U-Boot we must call this directly */ + ubi_update_fastmap(ubi); +#endif + return NULL; + } else { + pnum = pool->pebs[pool->used++]; + return ubi->lookuptbl[pnum]; + } +} +#else +static struct ubi_wl_entry *get_peb_for_wl(struct ubi_device *ubi) +{ + struct ubi_wl_entry *e; - p = ubi->prot.pnum.rb_node; - while (p) { + e = find_wl_entry(ubi, &ubi->free, WL_FREE_MAX_DIFF); + self_check_in_wl_tree(ubi, e, &ubi->free); + ubi->free_count--; + ubi_assert(ubi->free_count >= 0); + rb_erase(&e->u.rb, &ubi->free); - pe = rb_entry(p, struct ubi_wl_prot_entry, rb_pnum); + return e; +} - if (pnum == pe->e->pnum) - goto found; +int ubi_wl_get_peb(struct ubi_device *ubi) +{ + int peb, err; - if (pnum < pe->e->pnum) - p = p->rb_left; - else - p = p->rb_right; + spin_lock(&ubi->wl_lock); + peb = __wl_get_peb(ubi); + spin_unlock(&ubi->wl_lock); + + if (peb < 0) + return peb; + + err = ubi_self_check_all_ff(ubi, peb, ubi->vid_hdr_aloffset, + ubi->peb_size - ubi->vid_hdr_aloffset); + if (err) { + ubi_err("new PEB %d does not contain all 0xFF bytes", peb); + return err; } - return -ENODEV; + return peb; +} +#endif -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); +/** + * prot_queue_del - remove a physical eraseblock from the protection queue. + * @ubi: UBI device description object + * @pnum: the physical eraseblock to remove + * + * This function deletes PEB @pnum from the protection queue and returns zero + * in case of success and %-ENODEV if the PEB was not found. + */ +static int prot_queue_del(struct ubi_device *ubi, int pnum) +{ + struct ubi_wl_entry *e; + + e = ubi->lookuptbl[pnum]; + if (!e) + return -ENODEV; + + if (self_check_in_pq(ubi, e)) + return -ENODEV; + + list_del(&e->u.list); + dbg_wl("deleted PEB %d from the protection queue", e->pnum); return 0; } @@ -574,7 +734,8 @@ found: * 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) +static int sync_erase(struct ubi_device *ubi, struct ubi_wl_entry *e, + int torture) { int err; struct ubi_ec_hdr *ec_hdr; @@ -582,8 +743,8 @@ static int sync_erase(struct ubi_device *ubi, struct ubi_wl_entry *e, int tortur dbg_wl("erase PEB %d, old EC %llu", e->pnum, ec); - err = paranoid_check_ec(ubi, e->pnum, e->ec); - if (err > 0) + err = self_check_ec(ubi, e->pnum, e->ec); + if (err) return -EINVAL; ec_hdr = kzalloc(ubi->ec_hdr_alsize, GFP_NOFS); @@ -626,91 +787,124 @@ out_free: } /** - * check_protection_over - check if it is time to stop protecting some - * physical eraseblocks. + * serve_prot_queue - check if it is time to stop protecting PEBs. * @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. + * This function is called after each erase operation and removes PEBs from the + * tail of the protection queue. These PEBs have been protected for long enough + * and should be moved to the used tree. */ -static void check_protection_over(struct ubi_device *ubi) +static void serve_prot_queue(struct ubi_device *ubi) { - struct ubi_wl_prot_entry *pe; + struct ubi_wl_entry *e, *tmp; + int count; /* * 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); +repeat: + count = 0; + spin_lock(&ubi->wl_lock); + list_for_each_entry_safe(e, tmp, &ubi->pq[ubi->pq_head], u.list) { + dbg_wl("PEB %d EC %d protection over, move to used tree", + e->pnum, e->ec); - if (pe->abs_ec > ubi->abs_ec) { + list_del(&e->u.list); + wl_tree_add(e, &ubi->used); + if (count++ > 32) { + /* + * Let's be nice and avoid holding the spinlock for + * too long. + */ spin_unlock(&ubi->wl_lock); - break; + cond_resched(); + goto repeat; } - - 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(); } + + ubi->pq_head += 1; + if (ubi->pq_head == UBI_PROT_QUEUE_LEN) + ubi->pq_head = 0; + ubi_assert(ubi->pq_head >= 0 && ubi->pq_head < UBI_PROT_QUEUE_LEN); + spin_unlock(&ubi->wl_lock); } /** - * schedule_ubi_work - schedule a work. + * __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. + * This function adds a work defined by @wrk to the tail of the pending works + * list. Can only be used of ubi->work_sem is already held in read mode! */ -static void schedule_ubi_work(struct ubi_device *ubi, struct ubi_work *wrk) +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; - +#ifndef __UBOOT__ + if (ubi->thread_enabled && !ubi_dbg_is_bgt_disabled(ubi)) + wake_up_process(ubi->bgt_thread); +#else /* * U-Boot special: We have no bgt_thread in U-Boot! * So just call do_work() here directly. */ do_work(ubi); - +#endif spin_unlock(&ubi->wl_lock); } +/** + * schedule_ubi_work - schedule a work. + * @ubi: UBI device description object + * @wrk: the work to schedule + * + * This function adds 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) +{ + down_read(&ubi->work_sem); + __schedule_ubi_work(ubi, wrk); + up_read(&ubi->work_sem); +} + static int erase_worker(struct ubi_device *ubi, struct ubi_work *wl_wrk, int cancel); +#ifdef CONFIG_MTD_UBI_FASTMAP +/** + * ubi_is_erase_work - checks whether a work is erase work. + * @wrk: The work object to be checked + */ +int ubi_is_erase_work(struct ubi_work *wrk) +{ + return wrk->func == erase_worker; +} +#endif + /** * schedule_erase - schedule an erase work. * @ubi: UBI device description object * @e: the WL entry of the physical eraseblock to erase + * @vol_id: the volume ID that last used this PEB + * @lnum: the last used logical eraseblock number for the PEB * @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) + int vol_id, int lnum, int torture) { struct ubi_work *wl_wrk; + ubi_assert(e); + ubi_assert(!ubi_is_fm_block(ubi, e->pnum)); + dbg_wl("schedule erasure of PEB %d, EC %d, torture %d", e->pnum, e->ec, torture); @@ -720,6 +914,8 @@ static int schedule_erase(struct ubi_device *ubi, struct ubi_wl_entry *e, wl_wrk->func = &erase_worker; wl_wrk->e = e; + wl_wrk->vol_id = vol_id; + wl_wrk->lnum = lnum; wl_wrk->torture = torture; schedule_ubi_work(ubi, wl_wrk); @@ -727,6 +923,79 @@ static int schedule_erase(struct ubi_device *ubi, struct ubi_wl_entry *e, } /** + * do_sync_erase - run the erase worker synchronously. + * @ubi: UBI device description object + * @e: the WL entry of the physical eraseblock to erase + * @vol_id: the volume ID that last used this PEB + * @lnum: the last used logical eraseblock number for the PEB + * @torture: if the physical eraseblock has to be tortured + * + */ +static int do_sync_erase(struct ubi_device *ubi, struct ubi_wl_entry *e, + int vol_id, int lnum, int torture) +{ + struct ubi_work *wl_wrk; + + dbg_wl("sync erase of PEB %i", e->pnum); + + wl_wrk = kmalloc(sizeof(struct ubi_work), GFP_NOFS); + if (!wl_wrk) + return -ENOMEM; + + wl_wrk->e = e; + wl_wrk->vol_id = vol_id; + wl_wrk->lnum = lnum; + wl_wrk->torture = torture; + + return erase_worker(ubi, wl_wrk, 0); +} + +#ifdef CONFIG_MTD_UBI_FASTMAP +/** + * ubi_wl_put_fm_peb - returns a PEB used in a fastmap to the wear-leveling + * sub-system. + * see: ubi_wl_put_peb() + * + * @ubi: UBI device description object + * @fm_e: physical eraseblock to return + * @lnum: the last used logical eraseblock number for the PEB + * @torture: if this physical eraseblock has to be tortured + */ +int ubi_wl_put_fm_peb(struct ubi_device *ubi, struct ubi_wl_entry *fm_e, + int lnum, int torture) +{ + struct ubi_wl_entry *e; + int vol_id, pnum = fm_e->pnum; + + dbg_wl("PEB %d", pnum); + + ubi_assert(pnum >= 0); + ubi_assert(pnum < ubi->peb_count); + + spin_lock(&ubi->wl_lock); + e = ubi->lookuptbl[pnum]; + + /* This can happen if we recovered from a fastmap the very + * first time and writing now a new one. In this case the wl system + * has never seen any PEB used by the original fastmap. + */ + if (!e) { + e = fm_e; + ubi_assert(e->ec >= 0); + ubi->lookuptbl[pnum] = e; + } else { + e->ec = fm_e->ec; + kfree(fm_e); + } + + spin_unlock(&ubi->wl_lock); + + vol_id = lnum ? UBI_FM_DATA_VOLUME_ID : UBI_FM_SB_VOLUME_ID; + return schedule_erase(ubi, e, vol_id, lnum, torture); +} +#endif + +/** * wear_leveling_worker - wear-leveling worker function. * @ubi: UBI device description object * @wrk: the work object @@ -739,13 +1008,15 @@ static int schedule_erase(struct ubi_device *ubi, struct ubi_wl_entry *e, 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); + int err, scrubbing = 0, torture = 0, protect = 0, erroneous = 0; + int vol_id = -1, uninitialized_var(lnum); +#ifdef CONFIG_MTD_UBI_FASTMAP + int anchor = wrk->anchor; +#endif struct ubi_wl_entry *e1, *e2; struct ubi_vid_hdr *vid_hdr; kfree(wrk); - if (cancel) return 0; @@ -775,36 +1046,62 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk, goto out_cancel; } +#ifdef CONFIG_MTD_UBI_FASTMAP + /* Check whether we need to produce an anchor PEB */ + if (!anchor) + anchor = !anchor_pebs_avalible(&ubi->free); + + if (anchor) { + e1 = find_anchor_wl_entry(&ubi->used); + if (!e1) + goto out_cancel; + e2 = get_peb_for_wl(ubi); + if (!e2) + goto out_cancel; + + self_check_in_wl_tree(ubi, e1, &ubi->used); + rb_erase(&e1->u.rb, &ubi->used); + dbg_wl("anchor-move PEB %d to PEB %d", e1->pnum, e2->pnum); + } else if (!ubi->scrub.rb_node) { +#else if (!ubi->scrub.rb_node) { +#endif /* * 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); + e1 = rb_entry(rb_first(&ubi->used), struct ubi_wl_entry, u.rb); + e2 = get_peb_for_wl(ubi); + if (!e2) + goto out_cancel; 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); + + /* Give the unused PEB back */ + wl_tree_add(e2, &ubi->free); + ubi->free_count++; goto out_cancel; } - paranoid_check_in_wl_tree(e1, &ubi->used); - rb_erase(&e1->rb, &ubi->used); + self_check_in_wl_tree(ubi, e1, &ubi->used); + rb_erase(&e1->u.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); + e1 = rb_entry(rb_first(&ubi->scrub), struct ubi_wl_entry, u.rb); + e2 = get_peb_for_wl(ubi); + if (!e2) + goto out_cancel; + + self_check_in_wl_tree(ubi, e1, &ubi->scrub); + rb_erase(&e1->u.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); @@ -822,81 +1119,127 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk, err = ubi_io_read_vid_hdr(ubi, e1->pnum, vid_hdr, 0); if (err && err != UBI_IO_BITFLIPS) { - if (err == UBI_IO_PEB_FREE) { + if (err == UBI_IO_FF) { /* * 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. + * given, so we have a situation when it has not yet + * had a chance to write it, because it was preempted. + * So add this PEB to the protection queue so far, + * because presumably more data will be written there + * (including the missing VID header), and then we'll + * move it. */ dbg_wl("PEB %d has no VID header", e1->pnum); + protect = 1; + goto out_not_moved; + } else if (err == UBI_IO_FF_BITFLIPS) { + /* + * The same situation as %UBI_IO_FF, but bit-flips were + * detected. It is better to schedule this PEB for + * scrubbing. + */ + dbg_wl("PEB %d has no VID header but has bit-flips", + e1->pnum); + scrubbing = 1; 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; } + vol_id = be32_to_cpu(vid_hdr->vol_id); + lnum = be32_to_cpu(vid_hdr->lnum); + err = ubi_eba_copy_leb(ubi, e1->pnum, e2->pnum, vid_hdr); if (err) { - - if (err < 0) - goto out_error; - if (err == 1) + if (err == MOVE_CANCEL_RACE) { + /* + * The LEB has not been moved because the volume is + * being deleted or the PEB has been put meanwhile. We + * should prevent this PEB from being selected for + * wear-leveling movement again, so put it to the + * protection queue. + */ + protect = 1; + goto out_not_moved; + } + if (err == MOVE_RETRY) { + scrubbing = 1; goto out_not_moved; + } + if (err == MOVE_TARGET_BITFLIPS || err == MOVE_TARGET_WR_ERR || + err == MOVE_TARGET_RD_ERR) { + /* + * Target PEB had bit-flips or write error - torture it. + */ + torture = 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. - */ + if (err == MOVE_SOURCE_RD_ERR) { + /* + * An error happened while reading the source PEB. Do + * not switch to R/O mode in this case, and give the + * upper layers a possibility to recover from this, + * e.g. by unmapping corresponding LEB. Instead, just + * put this PEB to the @ubi->erroneous list to prevent + * UBI from trying to move it over and over again. + */ + if (ubi->erroneous_peb_count > ubi->max_erroneous) { + ubi_err("too many erroneous eraseblocks (%d)", + ubi->erroneous_peb_count); + goto out_error; + } + erroneous = 1; + goto out_not_moved; + } - dbg_wl("cancelled moving PEB %d", e1->pnum); - pe = kmalloc(sizeof(struct ubi_wl_prot_entry), GFP_NOFS); - if (!pe) { - err = -ENOMEM; + if (err < 0) goto out_error; - } - protect = 1; + ubi_assert(0); } + /* The PEB has been successfully moved */ + if (scrubbing) + ubi_msg("scrubbed PEB %d (LEB %d:%d), data moved to PEB %d", + e1->pnum, vol_id, lnum, e2->pnum); 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) + if (!ubi->move_to_put) { wl_tree_add(e2, &ubi->used); - else - put = 1; + e2 = NULL; + } ubi->move_from = ubi->move_to = NULL; ubi->move_to_put = ubi->wl_scheduled = 0; spin_unlock(&ubi->wl_lock); - if (put) { + err = do_sync_erase(ubi, e1, vol_id, lnum, 0); + if (err) { + kmem_cache_free(ubi_wl_entry_slab, e1); + if (e2) + kmem_cache_free(ubi_wl_entry_slab, e2); + goto out_ro; + } + + if (e2) { /* * 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("PEB %d (LEB %d:%d) was put meanwhile, erase", + e2->pnum, vol_id, lnum); + err = do_sync_erase(ubi, e2, vol_id, lnum, 0); + if (err) { + kmem_cache_free(ubi_wl_entry_slab, e2); + goto out_ro; + } } - dbg_wl("done"); mutex_unlock(&ubi->move_mutex); return 0; @@ -904,42 +1247,60 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk, /* * 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. + * have been changed, schedule it for erasure. */ out_not_moved: - ubi_free_vid_hdr(ubi, vid_hdr); + if (vol_id != -1) + dbg_wl("cancel moving PEB %d (LEB %d:%d) to PEB %d (%d)", + e1->pnum, vol_id, lnum, e2->pnum, err); + else + dbg_wl("cancel moving PEB %d to PEB %d (%d)", + e1->pnum, e2->pnum, err); spin_lock(&ubi->wl_lock); - if (scrubbing) + if (protect) + prot_queue_add(ubi, e1); + else if (erroneous) { + wl_tree_add(e1, &ubi->erroneous); + ubi->erroneous_peb_count += 1; + } else if (scrubbing) wl_tree_add(e1, &ubi->scrub); else wl_tree_add(e1, &ubi->used); + ubi_assert(!ubi->move_to_put); ubi->move_from = ubi->move_to = NULL; - ubi->move_to_put = ubi->wl_scheduled = 0; + ubi->wl_scheduled = 0; spin_unlock(&ubi->wl_lock); - err = schedule_erase(ubi, e2, 0); - if (err) - goto out_error; - + ubi_free_vid_hdr(ubi, vid_hdr); + err = do_sync_erase(ubi, e2, vol_id, lnum, torture); + if (err) { + kmem_cache_free(ubi_wl_entry_slab, e2); + goto out_ro; + } 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); + if (vol_id != -1) + ubi_err("error %d while moving PEB %d to PEB %d", + err, e1->pnum, e2->pnum); + else + ubi_err("error %d while moving PEB %d (LEB %d:%d) to PEB %d", + err, e1->pnum, vol_id, lnum, e2->pnum); 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); + ubi_free_vid_hdr(ubi, vid_hdr); kmem_cache_free(ubi_wl_entry_slab, e1); kmem_cache_free(ubi_wl_entry_slab, e2); - ubi_ro_mode(ubi); +out_ro: + ubi_ro_mode(ubi); mutex_unlock(&ubi->move_mutex); - return err; + ubi_assert(err != 0); + return err < 0 ? err : -EIO; out_cancel: ubi->wl_scheduled = 0; @@ -952,12 +1313,13 @@ out_cancel: /** * ensure_wear_leveling - schedule wear-leveling if it is needed. * @ubi: UBI device description object + * @nested: set to non-zero if this function is called from UBI worker * * 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) +static int ensure_wear_leveling(struct ubi_device *ubi, int nested) { int err = 0; struct ubi_wl_entry *e1; @@ -981,11 +1343,11 @@ static int ensure_wear_leveling(struct ubi_device *ubi) /* * 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 + * erase counter of free physical eraseblocks is greater than * %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); + e1 = rb_entry(rb_first(&ubi->used), struct ubi_wl_entry, u.rb); + e2 = find_wl_entry(ubi, &ubi->free, WL_FREE_MAX_DIFF); if (!(e2->ec - e1->ec >= UBI_WL_THRESHOLD)) goto out_unlock; @@ -1002,8 +1364,12 @@ static int ensure_wear_leveling(struct ubi_device *ubi) goto out_cancel; } + wrk->anchor = 0; wrk->func = &wear_leveling_worker; - schedule_ubi_work(ubi, wrk); + if (nested) + __schedule_ubi_work(ubi, wrk); + else + schedule_ubi_work(ubi, wrk); return err; out_cancel: @@ -1014,6 +1380,38 @@ out_unlock: return err; } +#ifdef CONFIG_MTD_UBI_FASTMAP +/** + * ubi_ensure_anchor_pebs - schedule wear-leveling to produce an anchor PEB. + * @ubi: UBI device description object + */ +int ubi_ensure_anchor_pebs(struct ubi_device *ubi) +{ + struct ubi_work *wrk; + + spin_lock(&ubi->wl_lock); + if (ubi->wl_scheduled) { + spin_unlock(&ubi->wl_lock); + return 0; + } + ubi->wl_scheduled = 1; + spin_unlock(&ubi->wl_lock); + + wrk = kmalloc(sizeof(struct ubi_work), GFP_NOFS); + if (!wrk) { + spin_lock(&ubi->wl_lock); + ubi->wl_scheduled = 0; + spin_unlock(&ubi->wl_lock); + return -ENOMEM; + } + + wrk->anchor = 1; + wrk->func = &wear_leveling_worker; + schedule_ubi_work(ubi, wrk); + return 0; +} +#endif + /** * erase_worker - physical eraseblock erase worker function. * @ubi: UBI device description object @@ -1029,7 +1427,10 @@ 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; + int pnum = e->pnum; + int vol_id = wl_wrk->vol_id; + int lnum = wl_wrk->lnum; + int err, available_consumed = 0; if (cancel) { dbg_wl("cancel erasure of PEB %d EC %d", pnum, e->ec); @@ -1038,7 +1439,10 @@ static int erase_worker(struct ubi_device *ubi, struct ubi_work *wl_wrk, return 0; } - dbg_wl("erase PEB %d EC %d", pnum, e->ec); + dbg_wl("erase PEB %d EC %d LEB %d:%d", + pnum, e->ec, wl_wrk->vol_id, wl_wrk->lnum); + + ubi_assert(!ubi_is_fm_block(ubi, e->pnum)); err = sync_erase(ubi, e, wl_wrk->torture); if (!err) { @@ -1046,44 +1450,45 @@ static int erase_worker(struct ubi_device *ubi, struct ubi_work *wl_wrk, kfree(wl_wrk); spin_lock(&ubi->wl_lock); - ubi->abs_ec += 1; wl_tree_add(e, &ubi->free); + ubi->free_count++; spin_unlock(&ubi->wl_lock); /* - * One more erase operation has happened, take care about protected - * physical eraseblocks. + * One more erase operation has happened, take care about + * protected physical eraseblocks. */ - check_protection_over(ubi); + serve_prot_queue(ubi); /* And take care about wear-leveling */ - err = ensure_wear_leveling(ubi); + err = ensure_wear_leveling(ubi, 1); 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); + err1 = schedule_erase(ubi, e, vol_id, lnum, 0); if (err1) { err = err1; goto out_ro; } return err; - } else if (err != -EIO) { + } + + kmem_cache_free(ubi_wl_entry_slab, e); + 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. + * errors again and again. Well, lets switch to R/O mode. */ goto out_ro; - } /* It is %-EIO, the PEB went bad */ @@ -1093,48 +1498,62 @@ static int erase_worker(struct ubi_device *ubi, struct ubi_work *wl_wrk, } 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; + if (ubi->avail_pebs == 0) { + spin_unlock(&ubi->volumes_lock); + ubi_err("no reserved/available physical eraseblocks"); + goto out_ro; + } + ubi->avail_pebs -= 1; + available_consumed = 1; } - spin_unlock(&ubi->volumes_lock); - ubi_msg("mark PEB %d as bad", pnum); + 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; + if (ubi->beb_rsvd_pebs > 0) { + if (available_consumed) { + /* + * The amount of reserved PEBs increased since we last + * checked. + */ + ubi->avail_pebs += 1; + available_consumed = 0; + } + 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"); + if (available_consumed) + ubi_warn("no PEBs in the reserved pool, used an available PEB"); + else if (ubi->beb_rsvd_pebs) + ubi_msg("%d PEBs left in the reserve", ubi->beb_rsvd_pebs); + else + ubi_warn("last PEB from the reserve was used"); spin_unlock(&ubi->volumes_lock); return err; out_ro: + if (available_consumed) { + spin_lock(&ubi->volumes_lock); + ubi->avail_pebs += 1; + spin_unlock(&ubi->volumes_lock); + } ubi_ro_mode(ubi); return err; } /** - * ubi_wl_put_peb - return a physical eraseblock to the wear-leveling unit. + * ubi_wl_put_peb - return a PEB to the wear-leveling sub-system. * @ubi: UBI device description object + * @vol_id: the volume ID that last used this PEB + * @lnum: the last used logical eraseblock number for the PEB * @pnum: physical eraseblock to return * @torture: if this physical eraseblock has to be tortured * @@ -1143,7 +1562,8 @@ out_ro: * 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 ubi_wl_put_peb(struct ubi_device *ubi, int vol_id, int lnum, + int pnum, int torture) { int err; struct ubi_wl_entry *e; @@ -1172,11 +1592,11 @@ retry: /* * 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. + * sub-system already re-mapped the LEB in 'ubi_eba_copy_leb()' + * but the WL sub-system 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); @@ -1185,13 +1605,20 @@ retry: return 0; } else { if (in_wl_tree(e, &ubi->used)) { - paranoid_check_in_wl_tree(e, &ubi->used); - rb_erase(&e->rb, &ubi->used); + self_check_in_wl_tree(ubi, e, &ubi->used); + rb_erase(&e->u.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); + self_check_in_wl_tree(ubi, e, &ubi->scrub); + rb_erase(&e->u.rb, &ubi->scrub); + } else if (in_wl_tree(e, &ubi->erroneous)) { + self_check_in_wl_tree(ubi, e, &ubi->erroneous); + rb_erase(&e->u.rb, &ubi->erroneous); + ubi->erroneous_peb_count -= 1; + ubi_assert(ubi->erroneous_peb_count >= 0); + /* Erroneous PEBs should be tortured */ + torture = 1; } else { - err = prot_tree_del(ubi, e->pnum); + err = prot_queue_del(ubi, e->pnum); if (err) { ubi_err("PEB %d not found", pnum); ubi_ro_mode(ubi); @@ -1202,7 +1629,7 @@ retry: } spin_unlock(&ubi->wl_lock); - err = schedule_erase(ubi, e, torture); + err = schedule_erase(ubi, e, vol_id, lnum, torture); if (err) { spin_lock(&ubi->wl_lock); wl_tree_add(e, &ubi->used); @@ -1231,7 +1658,8 @@ int ubi_wl_scrub_peb(struct ubi_device *ubi, int pnum) retry: spin_lock(&ubi->wl_lock); e = ubi->lookuptbl[pnum]; - if (e == ubi->move_from || in_wl_tree(e, &ubi->scrub)) { + if (e == ubi->move_from || in_wl_tree(e, &ubi->scrub) || + in_wl_tree(e, &ubi->erroneous)) { spin_unlock(&ubi->wl_lock); return 0; } @@ -1250,12 +1678,12 @@ retry: } if (in_wl_tree(e, &ubi->used)) { - paranoid_check_in_wl_tree(e, &ubi->used); - rb_erase(&e->rb, &ubi->used); + self_check_in_wl_tree(ubi, e, &ubi->used); + rb_erase(&e->u.rb, &ubi->used); } else { int err; - err = prot_tree_del(ubi, e->pnum); + err = prot_queue_del(ubi, e->pnum); if (err) { ubi_err("PEB %d not found", pnum); ubi_ro_mode(ubi); @@ -1271,29 +1699,60 @@ retry: * Technically scrubbing is the same as wear-leveling, so it is done * by the WL worker. */ - return ensure_wear_leveling(ubi); + return ensure_wear_leveling(ubi, 0); } /** * ubi_wl_flush - flush all pending works. * @ubi: UBI device description object + * @vol_id: the volume id to flush for + * @lnum: the logical eraseblock number to flush for * - * This function returns zero in case of success and a negative error code in - * case of failure. + * This function executes all pending works for a particular volume id / + * logical eraseblock number pair. If either value is set to %UBI_ALL, then it + * acts as a wildcard for all of the corresponding volume numbers or logical + * eraseblock numbers. It returns zero in case of success and a negative error + * code in case of failure. */ -int ubi_wl_flush(struct ubi_device *ubi) +int ubi_wl_flush(struct ubi_device *ubi, int vol_id, int lnum) { - int err; + int err = 0; + int found = 1; /* - * Erase while the pending works queue is not empty, but not more then + * Erase while the pending works queue is not empty, but not more than * 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; + dbg_wl("flush pending work for LEB %d:%d (%d pending works)", + vol_id, lnum, ubi->works_count); + + while (found) { + struct ubi_work *wrk; + found = 0; + + down_read(&ubi->work_sem); + spin_lock(&ubi->wl_lock); + list_for_each_entry(wrk, &ubi->works, list) { + if ((vol_id == UBI_ALL || wrk->vol_id == vol_id) && + (lnum == UBI_ALL || wrk->lnum == lnum)) { + list_del(&wrk->list); + ubi->works_count -= 1; + ubi_assert(ubi->works_count >= 0); + spin_unlock(&ubi->wl_lock); + + err = wrk->func(ubi, wrk, 0); + if (err) { + up_read(&ubi->work_sem); + return err; + } + + spin_lock(&ubi->wl_lock); + found = 1; + break; + } + } + spin_unlock(&ubi->wl_lock); + up_read(&ubi->work_sem); } /* @@ -1303,18 +1762,7 @@ int ubi_wl_flush(struct ubi_device *ubi) 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; + return err; } /** @@ -1333,11 +1781,11 @@ static void tree_destroy(struct rb_root *root) else if (rb->rb_right) rb = rb->rb_right; else { - e = rb_entry(rb, struct ubi_wl_entry, rb); + e = rb_entry(rb, struct ubi_wl_entry, u.rb); rb = rb_parent(rb); if (rb) { - if (rb->rb_left == &e->rb) + if (rb->rb_left == &e->u.rb) rb->rb_left = NULL; else rb->rb_right = NULL; @@ -1372,7 +1820,7 @@ int ubi_thread(void *u) spin_lock(&ubi->wl_lock); if (list_empty(&ubi->works) || ubi->ro_mode || - !ubi->thread_enabled) { + !ubi->thread_enabled || ubi_dbg_is_bgt_disabled(ubi)) { set_current_state(TASK_INTERRUPTIBLE); spin_unlock(&ubi->wl_lock); schedule(); @@ -1392,7 +1840,8 @@ int ubi_thread(void *u) ubi_msg("%s: %d consecutive failures", ubi->bgt_name, WL_MAX_FAILURES); ubi_ro_mode(ubi); - break; + ubi->thread_enabled = 0; + continue; } } else failures = 0; @@ -1422,30 +1871,32 @@ static void cancel_pending(struct ubi_device *ubi) } /** - * ubi_wl_init_scan - initialize the wear-leveling unit using scanning - * information. + * ubi_wl_init - initialize the WL sub-system using attaching information. * @ubi: UBI device description object - * @si: scanning information + * @ai: attaching 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 ubi_wl_init(struct ubi_device *ubi, struct ubi_attach_info *ai) { - int err; + int err, i, reserved_pebs, found_pebs = 0; struct rb_node *rb1, *rb2; - struct ubi_scan_volume *sv; - struct ubi_scan_leb *seb, *tmp; + struct ubi_ainf_volume *av; + struct ubi_ainf_peb *aeb, *tmp; struct ubi_wl_entry *e; - - ubi->used = ubi->free = ubi->scrub = RB_ROOT; - ubi->prot.pnum = ubi->prot.aec = RB_ROOT; + ubi->used = ubi->erroneous = ubi->free = ubi->scrub = RB_ROOT; spin_lock_init(&ubi->wl_lock); mutex_init(&ubi->move_mutex); init_rwsem(&ubi->work_sem); - ubi->max_ec = si->max_ec; + ubi->max_ec = ai->max_ec; INIT_LIST_HEAD(&ubi->works); +#ifndef __UBOOT__ +#ifdef CONFIG_MTD_UBI_FASTMAP + INIT_WORK(&ubi->fm_work, update_fastmap_work_fn); +#endif +#endif sprintf(ubi->bgt_name, UBI_BGT_NAME_PATTERN, ubi->ubi_num); @@ -1454,64 +1905,63 @@ int ubi_wl_init_scan(struct ubi_device *ubi, struct ubi_scan_info *si) if (!ubi->lookuptbl) return err; - list_for_each_entry_safe(seb, tmp, &si->erase, u.list) { + for (i = 0; i < UBI_PROT_QUEUE_LEN; i++) + INIT_LIST_HEAD(&ubi->pq[i]); + ubi->pq_head = 0; + + list_for_each_entry_safe(aeb, tmp, &ai->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; + e->pnum = aeb->pnum; + e->ec = aeb->ec; + ubi_assert(!ubi_is_fm_block(ubi, e->pnum)); ubi->lookuptbl[e->pnum] = e; - if (schedule_erase(ubi, e, 0)) { + if (schedule_erase(ubi, e, aeb->vol_id, aeb->lnum, 0)) { kmem_cache_free(ubi_wl_entry_slab, e); goto out_free; } + + found_pebs++; } - list_for_each_entry(seb, &si->free, u.list) { + ubi->free_count = 0; + list_for_each_entry(aeb, &ai->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; + e->pnum = aeb->pnum; + e->ec = aeb->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(); + ubi_assert(!ubi_is_fm_block(ubi, e->pnum)); - e = kmem_cache_alloc(ubi_wl_entry_slab, GFP_KERNEL); - if (!e) - goto out_free; + wl_tree_add(e, &ubi->free); + ubi->free_count++; - 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; - } + + found_pebs++; } - ubi_rb_for_each_entry(rb1, sv, &si->volumes, rb) { - ubi_rb_for_each_entry(rb2, seb, &sv->root, u.rb) { + ubi_rb_for_each_entry(rb1, av, &ai->volumes, rb) { + ubi_rb_for_each_entry(rb2, aeb, &av->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; + e->pnum = aeb->pnum; + e->ec = aeb->ec; ubi->lookuptbl[e->pnum] = e; - if (!seb->scrub) { + + if (!aeb->scrub) { dbg_wl("add PEB %d EC %d to the used tree", e->pnum, e->ec); wl_tree_add(e, &ubi->used); @@ -1520,20 +1970,38 @@ int ubi_wl_init_scan(struct ubi_device *ubi, struct ubi_scan_info *si) e->pnum, e->ec); wl_tree_add(e, &ubi->scrub); } + + found_pebs++; } } - if (ubi->avail_pebs < WL_RESERVED_PEBS) { + dbg_wl("found %i PEBs", found_pebs); + + if (ubi->fm) + ubi_assert(ubi->good_peb_count == \ + found_pebs + ubi->fm->used_blocks); + else + ubi_assert(ubi->good_peb_count == found_pebs); + + reserved_pebs = WL_RESERVED_PEBS; +#ifdef CONFIG_MTD_UBI_FASTMAP + /* Reserve enough LEBs to store two fastmaps. */ + reserved_pebs += (ubi->fm_size / ubi->leb_size) * 2; +#endif + + if (ubi->avail_pebs < reserved_pebs) { ubi_err("no enough physical eraseblocks (%d, need %d)", - ubi->avail_pebs, WL_RESERVED_PEBS); - err = -ENOSPC; + ubi->avail_pebs, reserved_pebs); + if (ubi->corr_peb_count) + ubi_err("%d PEBs are corrupted and not used", + ubi->corr_peb_count); goto out_free; } - ubi->avail_pebs -= WL_RESERVED_PEBS; - ubi->rsvd_pebs += WL_RESERVED_PEBS; + ubi->avail_pebs -= reserved_pebs; + ubi->rsvd_pebs += reserved_pebs; /* Schedule wear-leveling if needed */ - err = ensure_wear_leveling(ubi); + err = ensure_wear_leveling(ubi, 0); if (err) goto out_free; @@ -1549,72 +2017,57 @@ out_free: } /** - * protection_trees_destroy - destroy the protection RB-trees. + * protection_queue_destroy - destroy the protection queue. * @ubi: UBI device description object */ -static void protection_trees_destroy(struct ubi_device *ubi) +static void protection_queue_destroy(struct ubi_device *ubi) { - struct rb_node *rb; - struct ubi_wl_prot_entry *pe; + int i; + struct ubi_wl_entry *e, *tmp; - 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); + for (i = 0; i < UBI_PROT_QUEUE_LEN; ++i) { + list_for_each_entry_safe(e, tmp, &ubi->pq[i], u.list) { + list_del(&e->u.list); + kmem_cache_free(ubi_wl_entry_slab, e); } } } /** - * ubi_wl_close - close the wear-leveling unit. + * ubi_wl_close - close the wear-leveling sub-system. * @ubi: UBI device description object */ void ubi_wl_close(struct ubi_device *ubi) { - dbg_wl("close the UBI wear-leveling unit"); - + dbg_wl("close the WL sub-system"); cancel_pending(ubi); - protection_trees_destroy(ubi); + protection_queue_destroy(ubi); tree_destroy(&ubi->used); + tree_destroy(&ubi->erroneous); 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. + * self_check_ec - make sure that the erase counter of a PEB 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 + * is equivalent to @ec, and a negative error code if not or if an error * occurred. */ -static int paranoid_check_ec(struct ubi_device *ubi, int pnum, int ec) +static int self_check_ec(struct ubi_device *ubi, int pnum, int ec) { int err; long long read_ec; struct ubi_ec_hdr *ec_hdr; + if (!ubi_dbg_chk_gen(ubi)) + return 0; + ec_hdr = kzalloc(ubi->ec_hdr_alsize, GFP_NOFS); if (!ec_hdr) return -ENOMEM; @@ -1627,10 +2080,10 @@ static int paranoid_check_ec(struct ubi_device *ubi, int pnum, int ec) } read_ec = be64_to_cpu(ec_hdr->ec); - if (ec != read_ec) { - ubi_err("paranoid check failed for PEB %d", pnum); + if (ec != read_ec && read_ec - ec > 1) { + ubi_err("self-check failed for PEB %d", pnum); ubi_err("read EC is %lld, should be %d", read_ec, ec); - ubi_dbg_dump_stack(); + dump_stack(); err = 1; } else err = 0; @@ -1641,24 +2094,53 @@ out_free: } /** - * paranoid_check_in_wl_tree - make sure that a wear-leveling entry is present - * in a WL RB-tree. + * self_check_in_wl_tree - check that wear-leveling entry is in WL RB-tree. + * @ubi: UBI device description object * @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 + * This function returns zero if @e is in the @root RB-tree and %-EINVAL if it * is not. */ -static int paranoid_check_in_wl_tree(struct ubi_wl_entry *e, - struct rb_root *root) +static int self_check_in_wl_tree(const struct ubi_device *ubi, + struct ubi_wl_entry *e, struct rb_root *root) { + if (!ubi_dbg_chk_gen(ubi)) + return 0; + if (in_wl_tree(e, root)) return 0; - ubi_err("paranoid check failed for PEB %d, EC %d, RB-tree %p ", + ubi_err("self-check failed for PEB %d, EC %d, RB-tree %p ", e->pnum, e->ec, root); - ubi_dbg_dump_stack(); - return 1; + dump_stack(); + return -EINVAL; } -#endif /* CONFIG_MTD_UBI_DEBUG_PARANOID */ +/** + * self_check_in_pq - check if wear-leveling entry is in the protection + * queue. + * @ubi: UBI device description object + * @e: the wear-leveling entry to check + * + * This function returns zero if @e is in @ubi->pq and %-EINVAL if it is not. + */ +static int self_check_in_pq(const struct ubi_device *ubi, + struct ubi_wl_entry *e) +{ + struct ubi_wl_entry *p; + int i; + + if (!ubi_dbg_chk_gen(ubi)) + return 0; + + for (i = 0; i < UBI_PROT_QUEUE_LEN; ++i) + list_for_each_entry(p, &ubi->pq[i], u.list) + if (p == e) + return 0; + + ubi_err("self-check failed for PEB %d, EC %d, Protect queue", + e->pnum, e->ec); + dump_stack(); + return -EINVAL; +} diff --git a/drivers/net/Makefile b/drivers/net/Makefile index 7cc6b6f..84b8ec4 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -30,7 +30,6 @@ obj-$(CONFIG_FTGMAC100) += ftgmac100.o obj-$(CONFIG_FTMAC110) += ftmac110.o obj-$(CONFIG_FTMAC100) += ftmac100.o obj-$(CONFIG_GRETH) += greth.o -obj-$(CONFIG_INCA_IP_SWITCH) += inca-ip_sw.o obj-$(CONFIG_DRIVER_TI_KEYSTONE_NET) += keystone_net.o obj-$(CONFIG_DRIVER_KS8695ETH) += ks8695eth.o obj-$(CONFIG_KS8851_MLL) += ks8851_mll.o @@ -46,7 +45,6 @@ obj-$(CONFIG_DRIVER_AX88796L) += ax88796.o ne2000_base.o obj-$(CONFIG_NETCONSOLE) += netconsole.o obj-$(CONFIG_NS8382X) += ns8382x.o obj-$(CONFIG_PCNET) += pcnet.o -obj-$(CONFIG_PLB2800_ETHER) += plb2800_eth.o obj-$(CONFIG_RTL8139) += rtl8139.o obj-$(CONFIG_RTL8169) += rtl8169.o obj-$(CONFIG_SH_ETHER) += sh_eth.o diff --git a/drivers/net/cpsw.c b/drivers/net/cpsw.c index 8ec5161..52f8da6 100644 --- a/drivers/net/cpsw.c +++ b/drivers/net/cpsw.c @@ -235,7 +235,6 @@ struct cpsw_priv { struct phy_device *phydev; struct mii_dev *bus; - u32 mdio_link; u32 phy_mask; }; @@ -613,19 +612,8 @@ static int cpsw_update_link(struct cpsw_priv *priv) for_active_slave(slave, priv) cpsw_slave_update_link(slave, priv, &link); - priv->mdio_link = readl(&mdio_regs->link); - return link; -} - -static int cpsw_check_link(struct cpsw_priv *priv) -{ - u32 link = 0; - link = __raw_readl(&mdio_regs->link) & priv->phy_mask; - if ((link) && (link == priv->mdio_link)) - return 1; - - return cpsw_update_link(priv); + return link; } static inline u32 cpsw_get_slave_port(struct cpsw_priv *priv, u32 slave_num) @@ -891,9 +879,6 @@ static int cpsw_send(struct eth_device *dev, void *packet, int length) int len; int timeout = CPDMA_TIMEOUT; - if (!cpsw_check_link(priv)) - return -EIO; - flush_dcache_range((unsigned long)packet, (unsigned long)packet + length); @@ -916,8 +901,6 @@ static int cpsw_recv(struct eth_device *dev) void *buffer; int len; - cpsw_check_link(priv); - while (cpdma_process(priv, &priv->rx_chan, &buffer, &len) >= 0) { invalidate_dcache_range((unsigned long)buffer, (unsigned long)buffer + PKTSIZE_ALIGN); diff --git a/drivers/net/e1000.c b/drivers/net/e1000.c index 9d9b259..0eba57c 100644 --- a/drivers/net/e1000.c +++ b/drivers/net/e1000.c @@ -41,12 +41,12 @@ tested on both gig copper and gig fiber boards /* NIC specific static variables go here */ -static char tx_pool[128 + 16]; -static char rx_pool[128 + 16]; -static char packet[2096]; +/* Intel i210 needs the DMA descriptor rings aligned to 128b */ +#define E1000_BUFFER_ALIGN 128 -static struct e1000_tx_desc *tx_base; -static struct e1000_rx_desc *rx_base; +DEFINE_ALIGN_BUFFER(struct e1000_tx_desc, tx_base, 16, E1000_BUFFER_ALIGN); +DEFINE_ALIGN_BUFFER(struct e1000_rx_desc, rx_base, 16, E1000_BUFFER_ALIGN); +DEFINE_ALIGN_BUFFER(unsigned char, packet, 4096, E1000_BUFFER_ALIGN); static int tx_tail; static int rx_tail, rx_last; @@ -92,6 +92,12 @@ static struct pci_device_id e1000_supported[] = { {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_80003ES2LAN_SERDES_DPT}, {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_80003ES2LAN_COPPER_SPT}, {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_80003ES2LAN_SERDES_SPT}, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_I210_COPPER}, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_I210_COPPER_FLASHLESS}, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_I210_SERDES}, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_I210_SERDES_FLASHLESS}, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_I210_1000BASEKX}, + {} }; @@ -340,7 +346,7 @@ int32_t e1000_acquire_eeprom(struct e1000_hw *hw) return -E1000_ERR_SWFW_SYNC; eecd = E1000_READ_REG(hw, EECD); - if (hw->mac_type != e1000_82573 || hw->mac_type != e1000_82574) { + if (hw->mac_type != e1000_82573 && hw->mac_type != e1000_82574) { /* Request EEPROM Access */ if (hw->mac_type > e1000_82544) { eecd |= E1000_EECD_REQ; @@ -391,10 +397,15 @@ int32_t e1000_acquire_eeprom(struct e1000_hw *hw) static int32_t e1000_init_eeprom_params(struct e1000_hw *hw) { struct e1000_eeprom_info *eeprom = &hw->eeprom; - uint32_t eecd = E1000_READ_REG(hw, EECD); + uint32_t eecd; int32_t ret_val = E1000_SUCCESS; uint16_t eeprom_size; + if (hw->mac_type == e1000_igb) + eecd = E1000_READ_REG(hw, I210_EECD); + else + eecd = E1000_READ_REG(hw, EECD); + DEBUGFUNC(); switch (hw->mac_type) { @@ -485,9 +496,10 @@ static int32_t e1000_init_eeprom_params(struct e1000_hw *hw) eeprom->page_size = 8; eeprom->address_bits = 8; } - eeprom->use_eerd = true; - eeprom->use_eewr = true; if (e1000_is_onboard_nvm_eeprom(hw) == false) { + eeprom->use_eerd = true; + eeprom->use_eewr = true; + eeprom->type = e1000_eeprom_flash; eeprom->word_size = 2048; @@ -511,6 +523,16 @@ static int32_t e1000_init_eeprom_params(struct e1000_hw *hw) eeprom->use_eerd = true; eeprom->use_eewr = false; break; + case e1000_igb: + /* i210 has 4k of iNVM mapped as EEPROM */ + eeprom->type = e1000_eeprom_invm; + eeprom->opcode_bits = 8; + eeprom->delay_usec = 1; + eeprom->page_size = 32; + eeprom->address_bits = 16; + eeprom->use_eerd = true; + eeprom->use_eewr = false; + break; /* ich8lan does not support currently. if needed, please * add corresponding code and functions. @@ -552,7 +574,8 @@ static int32_t e1000_init_eeprom_params(struct e1000_hw *hw) break; } - if (eeprom->type == e1000_eeprom_spi) { + if (eeprom->type == e1000_eeprom_spi || + eeprom->type == e1000_eeprom_invm) { /* eeprom_size will be an enum [0..8] that maps * to eeprom sizes 128B to * 32KB (incremented by powers of 2). @@ -596,10 +619,17 @@ e1000_poll_eerd_eewr_done(struct e1000_hw *hw, int eerd) int32_t done = E1000_ERR_EEPROM; for (i = 0; i < attempts; i++) { - if (eerd == E1000_EEPROM_POLL_READ) - reg = E1000_READ_REG(hw, EERD); - else - reg = E1000_READ_REG(hw, EEWR); + if (eerd == E1000_EEPROM_POLL_READ) { + if (hw->mac_type == e1000_igb) + reg = E1000_READ_REG(hw, I210_EERD); + else + reg = E1000_READ_REG(hw, EERD); + } else { + if (hw->mac_type == e1000_igb) + reg = E1000_READ_REG(hw, I210_EEWR); + else + reg = E1000_READ_REG(hw, EEWR); + } if (reg & E1000_EEPROM_RW_REG_DONE) { done = E1000_SUCCESS; @@ -632,13 +662,23 @@ e1000_read_eeprom_eerd(struct e1000_hw *hw, eerd = ((offset+i) << E1000_EEPROM_RW_ADDR_SHIFT) + E1000_EEPROM_RW_REG_START; - E1000_WRITE_REG(hw, EERD, eerd); + if (hw->mac_type == e1000_igb) + E1000_WRITE_REG(hw, I210_EERD, eerd); + else + E1000_WRITE_REG(hw, EERD, eerd); + error = e1000_poll_eerd_eewr_done(hw, E1000_EEPROM_POLL_READ); if (error) break; - data[i] = (E1000_READ_REG(hw, EERD) >> + + if (hw->mac_type == e1000_igb) { + data[i] = (E1000_READ_REG(hw, I210_EERD) >> + E1000_EEPROM_RW_REG_DATA); + } else { + data[i] = (E1000_READ_REG(hw, EERD) >> E1000_EEPROM_RW_REG_DATA); + } } @@ -949,6 +989,10 @@ e1000_get_software_semaphore(struct e1000_hw *hw) DEBUGFUNC(); + swsm = E1000_READ_REG(hw, SWSM); + swsm &= ~E1000_SWSM_SMBI; + E1000_WRITE_REG(hw, SWSM, swsm); + if (hw->mac_type != e1000_80003es2lan) return E1000_SUCCESS; @@ -1069,7 +1113,7 @@ e1000_swfw_sync_acquire(struct e1000_hw *hw, uint16_t mask) return -E1000_ERR_SWFW_SYNC; swfw_sync = E1000_READ_REG(hw, SW_FW_SYNC); - if (!(swfw_sync & (fwmask | swmask))) + if ((swfw_sync & swmask) && !(swfw_sync & fwmask)) break; /* firmware currently using resource (fwmask) */ @@ -1118,13 +1162,23 @@ e1000_read_mac_addr(struct eth_device *nic) struct e1000_hw *hw = nic->priv; uint16_t offset; uint16_t eeprom_data; + uint32_t reg_data = 0; int i; DEBUGFUNC(); for (i = 0; i < NODE_ADDRESS_SIZE; i += 2) { offset = i >> 1; - if (e1000_read_eeprom(hw, offset, 1, &eeprom_data) < 0) { + if (hw->mac_type == e1000_igb) { + /* i210 preloads MAC address into RAL/RAH registers */ + if (offset == 0) + reg_data = E1000_READ_REG_ARRAY(hw, RA, 0); + else if (offset == 1) + reg_data >>= 16; + else if (offset == 2) + reg_data = E1000_READ_REG_ARRAY(hw, RA, 1); + eeprom_data = reg_data & 0xffff; + } else if (e1000_read_eeprom(hw, offset, 1, &eeprom_data) < 0) { DEBUGOUT("EEPROM Read Error\n"); return -E1000_ERR_EEPROM; } @@ -1320,6 +1374,13 @@ e1000_set_mac_type(struct e1000_hw *hw) case E1000_DEV_ID_ICH8_IGP_M: hw->mac_type = e1000_ich8lan; break; + case PCI_DEVICE_ID_INTEL_I210_COPPER: + case PCI_DEVICE_ID_INTEL_I210_COPPER_FLASHLESS: + case PCI_DEVICE_ID_INTEL_I210_SERDES: + case PCI_DEVICE_ID_INTEL_I210_SERDES_FLASHLESS: + case PCI_DEVICE_ID_INTEL_I210_1000BASEKX: + hw->mac_type = e1000_igb; + break; default: /* Should never have loaded on this device */ return -E1000_ERR_MAC_TYPE; @@ -1339,6 +1400,7 @@ e1000_reset_hw(struct e1000_hw *hw) uint32_t ctrl_ext; uint32_t manc; uint32_t pba = 0; + uint32_t reg; DEBUGFUNC(); @@ -1357,6 +1419,8 @@ e1000_reset_hw(struct e1000_hw *hw) /* Clear interrupt mask to stop board from generating interrupts */ DEBUGOUT("Masking off all interrupts\n"); + if (hw->mac_type == e1000_igb) + E1000_WRITE_REG(hw, I210_IAM, 0); E1000_WRITE_REG(hw, IMC, 0xffffffff); /* Disable the Transmit and Receive units. Then delay to allow @@ -1386,7 +1450,15 @@ e1000_reset_hw(struct e1000_hw *hw) E1000_WRITE_REG(hw, CTRL, (ctrl | E1000_CTRL_RST)); /* Force a reload from the EEPROM if necessary */ - if (hw->mac_type < e1000_82540) { + if (hw->mac_type == e1000_igb) { + mdelay(20); + reg = E1000_READ_REG(hw, STATUS); + if (reg & E1000_STATUS_PF_RST_DONE) + DEBUGOUT("PF OK\n"); + reg = E1000_READ_REG(hw, I210_EECD); + if (reg & E1000_EECD_AUTO_RD) + DEBUGOUT("EEC OK\n"); + } else if (hw->mac_type < e1000_82540) { /* Wait for reset to complete */ udelay(10); ctrl_ext = E1000_READ_REG(hw, CTRL_EXT); @@ -1406,6 +1478,8 @@ e1000_reset_hw(struct e1000_hw *hw) /* Clear interrupt mask to stop board from generating interrupts */ DEBUGOUT("Masking off all interrupts\n"); + if (hw->mac_type == e1000_igb) + E1000_WRITE_REG(hw, I210_IAM, 0); E1000_WRITE_REG(hw, IMC, 0xffffffff); /* Clear any pending interrupt events. */ @@ -1415,7 +1489,8 @@ e1000_reset_hw(struct e1000_hw *hw) if (hw->mac_type == e1000_82542_rev2_0) { pci_write_config_word(hw->pdev, PCI_COMMAND, hw->pci_cmd_word); } - E1000_WRITE_REG(hw, PBA, pba); + if (hw->mac_type != e1000_igb) + E1000_WRITE_REG(hw, PBA, pba); } /****************************************************************************** @@ -1451,6 +1526,10 @@ e1000_initialize_hardware_bits(struct e1000_hw *hw) reg_txdctl1 |= E1000_TXDCTL_COUNT_DESC; E1000_WRITE_REG(hw, TXDCTL1, reg_txdctl1); + /* IGB is cool */ + if (hw->mac_type == e1000_igb) + return; + switch (hw->mac_type) { case e1000_82571: case e1000_82572: @@ -1641,6 +1720,7 @@ e1000_init_hw(struct eth_device *nic) switch (hw->mac_type) { case e1000_82545_rev_3: case e1000_82546_rev_3: + case e1000_igb: break; default: /* Workaround for PCI-X problem when BIOS sets MMRBC incorrectly. */ @@ -1670,6 +1750,8 @@ e1000_init_hw(struct eth_device *nic) /* More time needed for PHY to initialize */ if (hw->mac_type == e1000_ich8lan) mdelay(15); + if (hw->mac_type == e1000_igb) + mdelay(15); /* Call a subroutine to configure the link and setup flow control. */ ret_val = e1000_setup_link(nic); @@ -1684,7 +1766,6 @@ e1000_init_hw(struct eth_device *nic) } /* Set the receive descriptor write back policy */ - if (hw->mac_type >= e1000_82571) { ctrl = E1000_READ_REG(hw, RXDCTL); ctrl = @@ -1731,6 +1812,8 @@ e1000_init_hw(struct eth_device *nic) reg_data = E1000_READ_REG(hw, GCR); reg_data |= E1000_GCR_L1_ACT_WITHOUT_L0S_RX; E1000_WRITE_REG(hw, GCR, reg_data); + case e1000_igb: + break; } #if 0 @@ -1807,6 +1890,7 @@ e1000_setup_link(struct eth_device *nic) case e1000_ich8lan: case e1000_82573: case e1000_82574: + case e1000_igb: hw->fc = e1000_fc_full; break; default: @@ -2267,6 +2351,8 @@ e1000_set_d0_lplu_state(struct e1000_hw *hw, bool active) if (hw->mac_type == e1000_ich8lan) { phy_ctrl = E1000_READ_REG(hw, PHY_CTRL); + } else if (hw->mac_type == e1000_igb) { + phy_ctrl = E1000_READ_REG(hw, I210_PHY_CTRL); } else { ret_val = e1000_read_phy_reg(hw, IGP02E1000_PHY_POWER_MGMT, &phy_data); @@ -2278,6 +2364,9 @@ e1000_set_d0_lplu_state(struct e1000_hw *hw, bool active) if (hw->mac_type == e1000_ich8lan) { phy_ctrl &= ~E1000_PHY_CTRL_D0A_LPLU; E1000_WRITE_REG(hw, PHY_CTRL, phy_ctrl); + } else if (hw->mac_type == e1000_igb) { + phy_ctrl &= ~E1000_PHY_CTRL_D0A_LPLU; + E1000_WRITE_REG(hw, I210_PHY_CTRL, phy_ctrl); } else { phy_data &= ~IGP02E1000_PM_D0_LPLU; ret_val = e1000_write_phy_reg(hw, @@ -2286,6 +2375,9 @@ e1000_set_d0_lplu_state(struct e1000_hw *hw, bool active) return ret_val; } + if (hw->mac_type == e1000_igb) + return E1000_SUCCESS; + /* LPLU and SmartSpeed are mutually exclusive. LPLU is used during * Dx states where the power conservation is most important. During * driver activity we should enable SmartSpeed, so performance is @@ -2320,6 +2412,9 @@ e1000_set_d0_lplu_state(struct e1000_hw *hw, bool active) if (hw->mac_type == e1000_ich8lan) { phy_ctrl |= E1000_PHY_CTRL_D0A_LPLU; E1000_WRITE_REG(hw, PHY_CTRL, phy_ctrl); + } else if (hw->mac_type == e1000_igb) { + phy_ctrl |= E1000_PHY_CTRL_D0A_LPLU; + E1000_WRITE_REG(hw, I210_PHY_CTRL, phy_ctrl); } else { phy_data |= IGP02E1000_PM_D0_LPLU; ret_val = e1000_write_phy_reg(hw, @@ -2328,6 +2423,9 @@ e1000_set_d0_lplu_state(struct e1000_hw *hw, bool active) return ret_val; } + if (hw->mac_type == e1000_igb) + return E1000_SUCCESS; + /* When LPLU is enabled we should disable SmartSpeed */ ret_val = e1000_read_phy_reg(hw, IGP01E1000_PHY_PORT_CONFIG, &phy_data); @@ -2549,8 +2647,10 @@ e1000_read_kmrn_reg(struct e1000_hw *hw, uint32_t reg_addr, uint16_t *data) if (e1000_is_second_port(hw)) swfw = E1000_SWFW_PHY1_SM; - if (e1000_swfw_sync_acquire(hw, swfw)) + if (e1000_swfw_sync_acquire(hw, swfw)) { + debug("%s[%i]\n", __func__, __LINE__); return -E1000_ERR_SWFW_SYNC; + } /* Write register address */ reg_val = ((reg_addr << E1000_KUMCTRLSTA_OFFSET_SHIFT) & @@ -2985,7 +3085,8 @@ e1000_setup_copper_link(struct eth_device *nic) ret_val = e1000_copper_link_igp_setup(hw); if (ret_val) return ret_val; - } else if (hw->phy_type == e1000_phy_m88) { + } else if (hw->phy_type == e1000_phy_m88 || + hw->phy_type == e1000_phy_igb) { ret_val = e1000_copper_link_mgp_setup(hw); if (ret_val) return ret_val; @@ -3229,7 +3330,8 @@ e1000_config_mac_to_phy(struct e1000_hw *hw) */ ctrl = E1000_READ_REG(hw, CTRL); ctrl |= (E1000_CTRL_FRCSPD | E1000_CTRL_FRCDPX); - ctrl &= ~(E1000_CTRL_SPD_SEL | E1000_CTRL_ILOS); + ctrl &= ~(E1000_CTRL_ILOS); + ctrl |= (E1000_CTRL_SPD_SEL); /* Set up duplex in the Device Control and Transmit Control * registers depending on negotiated values. @@ -4255,11 +4357,16 @@ e1000_get_phy_cfg_done(struct e1000_hw *hw) case e1000_82571: case e1000_82572: + case e1000_igb: while (timeout) { - if (E1000_READ_REG(hw, EEMNGCTL) & cfg_mask) - break; - else - mdelay(1); + if (hw->mac_type == e1000_igb) { + if (E1000_READ_REG(hw, I210_EEMNGCTL) & cfg_mask) + break; + } else { + if (E1000_READ_REG(hw, EEMNGCTL) & cfg_mask) + break; + } + mdelay(1); timeout--; } if (!timeout) { @@ -4488,6 +4595,7 @@ e1000_phy_reset(struct e1000_hw *hw) case e1000_phy_igp_2: case e1000_phy_igp_3: case e1000_phy_ife: + case e1000_phy_igb: ret_val = e1000_phy_hw_reset(hw); if (ret_val) return ret_val; @@ -4550,6 +4658,9 @@ static int e1000_set_phy_type (struct e1000_hw *hw) case BME1000_E_PHY_ID: hw->phy_type = e1000_phy_bm; break; + case I210_I_PHY_ID: + hw->phy_type = e1000_phy_igb; + break; /* Fall Through */ default: /* Should never have loaded on this device */ @@ -4654,6 +4765,10 @@ e1000_detect_gig_phy(struct e1000_hw *hw) if (hw->phy_id == IFE_C_E_PHY_ID) match = true; break; + case e1000_igb: + if (hw->phy_id == I210_I_PHY_ID) + match = true; + break; default: DEBUGOUT("Invalid MAC type %d\n", hw->mac_type); return -E1000_ERR_CONFIG; @@ -4705,6 +4820,7 @@ e1000_set_media_type(struct e1000_hw *hw) case e1000_ich8lan: case e1000_82573: case e1000_82574: + case e1000_igb: /* The STATUS_TBIMODE bit is reserved or reused * for the this device. */ @@ -4773,6 +4889,7 @@ e1000_sw_init(struct eth_device *nic) hw->fc_send_xon = 1; /* Media type - copper or fiber */ + hw->tbi_compatibility_en = true; e1000_set_media_type(hw); if (hw->mac_type >= e1000_82543) { @@ -4789,7 +4906,6 @@ e1000_sw_init(struct eth_device *nic) hw->media_type = e1000_media_type_fiber; } - hw->tbi_compatibility_en = true; hw->wait_autoneg_complete = true; if (hw->mac_type < e1000_82543) hw->report_tx_early = 0; @@ -4803,12 +4919,25 @@ void fill_rx(struct e1000_hw *hw) { struct e1000_rx_desc *rd; + uint32_t flush_start, flush_end; rx_last = rx_tail; rd = rx_base + rx_tail; rx_tail = (rx_tail + 1) % 8; memset(rd, 0, 16); - rd->buffer_addr = cpu_to_le64((u32) & packet); + rd->buffer_addr = cpu_to_le64((u32)packet); + + /* + * Make sure there are no stale data in WB over this area, which + * might get written into the memory while the e1000 also writes + * into the same memory area. + */ + invalidate_dcache_range((u32)packet, (u32)packet + 4096); + /* Dump the DMA descriptor into RAM. */ + flush_start = ((u32)rd) & ~(ARCH_DMA_MINALIGN - 1); + flush_end = flush_start + roundup(sizeof(*rd), ARCH_DMA_MINALIGN); + flush_dcache_range(flush_start, flush_end); + E1000_WRITE_REG(hw, RDT, rx_tail); } @@ -4822,17 +4951,10 @@ fill_rx(struct e1000_hw *hw) static void e1000_configure_tx(struct e1000_hw *hw) { - unsigned long ptr; unsigned long tctl; unsigned long tipg, tarc; uint32_t ipgr1, ipgr2; - ptr = (u32) tx_pool; - if (ptr & 0xf) - ptr = (ptr + 0x10) & (~0xf); - - tx_base = (typeof(tx_base)) ptr; - E1000_WRITE_REG(hw, TDBAL, (u32) tx_base); E1000_WRITE_REG(hw, TDBAH, 0); @@ -4901,7 +5023,22 @@ e1000_configure_tx(struct e1000_hw *hw) hw->txd_cmd |= E1000_TXD_CMD_RPS; else hw->txd_cmd |= E1000_TXD_CMD_RS; + + + if (hw->mac_type == e1000_igb) { + E1000_WRITE_REG(hw, TCTL_EXT, 0x42 << 10); + + uint32_t reg_txdctl = E1000_READ_REG(hw, TXDCTL); + reg_txdctl |= 1 << 25; + E1000_WRITE_REG(hw, TXDCTL, reg_txdctl); + mdelay(20); + } + + + E1000_WRITE_REG(hw, TCTL, tctl); + + } /** @@ -4941,7 +5078,6 @@ e1000_setup_rctl(struct e1000_hw *hw) static void e1000_configure_rx(struct e1000_hw *hw) { - unsigned long ptr; unsigned long rctl, ctrl_ext; rx_tail = 0; /* make sure receives are disabled while setting up the descriptors */ @@ -4963,10 +5099,6 @@ e1000_configure_rx(struct e1000_hw *hw) E1000_WRITE_FLUSH(hw); } /* Setup the Base and Length of the Rx Descriptor Ring */ - ptr = (u32) rx_pool; - if (ptr & 0xf) - ptr = (ptr + 0x10) & (~0xf); - rx_base = (typeof(rx_base)) ptr; E1000_WRITE_REG(hw, RDBAL, (u32) rx_base); E1000_WRITE_REG(hw, RDBAH, 0); @@ -4977,7 +5109,16 @@ e1000_configure_rx(struct e1000_hw *hw) E1000_WRITE_REG(hw, RDT, 0); /* Enable Receives */ + if (hw->mac_type == e1000_igb) { + + uint32_t reg_rxdctl = E1000_READ_REG(hw, RXDCTL); + reg_rxdctl |= 1 << 25; + E1000_WRITE_REG(hw, RXDCTL, reg_rxdctl); + mdelay(20); + } + E1000_WRITE_REG(hw, RCTL, rctl); + fill_rx(hw); } @@ -4989,12 +5130,25 @@ e1000_poll(struct eth_device *nic) { struct e1000_hw *hw = nic->priv; struct e1000_rx_desc *rd; + uint32_t inval_start, inval_end; + uint32_t len; + /* return true if there's an ethernet packet ready to read */ rd = rx_base + rx_last; + + /* Re-load the descriptor from RAM. */ + inval_start = ((u32)rd) & ~(ARCH_DMA_MINALIGN - 1); + inval_end = inval_start + roundup(sizeof(*rd), ARCH_DMA_MINALIGN); + invalidate_dcache_range(inval_start, inval_end); + if (!(le32_to_cpu(rd->status)) & E1000_RXD_STAT_DD) return 0; /*DEBUGOUT("recv: packet len=%d \n", rd->length); */ - NetReceive((uchar *)packet, le32_to_cpu(rd->length)); + /* Packet received, make sure the data are re-loaded from RAM. */ + len = le32_to_cpu(rd->length); + invalidate_dcache_range((u32)packet, + (u32)packet + roundup(len, ARCH_DMA_MINALIGN)); + NetReceive((uchar *)packet, len); fill_rx(hw); return 1; } @@ -5002,12 +5156,13 @@ e1000_poll(struct eth_device *nic) /************************************************************************** TRANSMIT - Transmit a frame ***************************************************************************/ -static int e1000_transmit(struct eth_device *nic, void *packet, int length) +static int e1000_transmit(struct eth_device *nic, void *txpacket, int length) { - void *nv_packet = (void *)packet; + void *nv_packet = (void *)txpacket; struct e1000_hw *hw = nic->priv; struct e1000_tx_desc *txp; int i = 0; + uint32_t flush_start, flush_end; txp = tx_base + tx_tail; tx_tail = (tx_tail + 1) % 8; @@ -5015,10 +5170,22 @@ static int e1000_transmit(struct eth_device *nic, void *packet, int length) txp->buffer_addr = cpu_to_le64(virt_to_bus(hw->pdev, nv_packet)); txp->lower.data = cpu_to_le32(hw->txd_cmd | length); txp->upper.data = 0; + + /* Dump the packet into RAM so e1000 can pick them. */ + flush_dcache_range((u32)nv_packet, + (u32)nv_packet + roundup(length, ARCH_DMA_MINALIGN)); + /* Dump the descriptor into RAM as well. */ + flush_start = ((u32)txp) & ~(ARCH_DMA_MINALIGN - 1); + flush_end = flush_start + roundup(sizeof(*txp), ARCH_DMA_MINALIGN); + flush_dcache_range(flush_start, flush_end); + E1000_WRITE_REG(hw, TDT, tx_tail); E1000_WRITE_FLUSH(hw); - while (!(le32_to_cpu(txp->upper.data) & E1000_TXD_STAT_DD)) { + while (1) { + invalidate_dcache_range(flush_start, flush_end); + if (le32_to_cpu(txp->upper.data) & E1000_TXD_STAT_DD) + break; if (i++ > TOUT_LOOP) { DEBUGOUT("e1000: tx timeout\n"); return 0; @@ -5113,9 +5280,8 @@ void e1000_get_bus_type(struct e1000_hw *hw) case e1000_82573: case e1000_82574: case e1000_80003es2lan: - hw->bus_type = e1000_bus_type_pci_express; - break; case e1000_ich8lan: + case e1000_igb: hw->bus_type = e1000_bus_type_pci_express; break; default: @@ -5196,6 +5362,7 @@ e1000_initialize(bd_t * bis) hw->autoneg_failed = 0; hw->autoneg = 1; hw->get_link_status = true; + hw->eeprom_semaphore_present = true; hw->hw_addr = pci_map_bar(devno, PCI_BASE_ADDRESS_0, PCI_REGION_MEM); hw->mac_type = e1000_undefined; @@ -5219,7 +5386,8 @@ e1000_initialize(bd_t * bis) E1000_ERR(nic, "EEPROM is invalid!\n"); continue; } - if (e1000_validate_eeprom_checksum(hw)) + if ((E1000_READ_REG(hw, I210_EECD) & E1000_EECD_FLUPD) && + e1000_validate_eeprom_checksum(hw)) continue; #endif e1000_read_mac_addr(nic); diff --git a/drivers/net/e1000.h b/drivers/net/e1000.h index ff87af2..b025ecc 100644 --- a/drivers/net/e1000.h +++ b/drivers/net/e1000.h @@ -100,6 +100,7 @@ typedef enum { e1000_82574, e1000_80003es2lan, e1000_ich8lan, + e1000_igb, e1000_num_macs } e1000_mac_type; @@ -118,6 +119,7 @@ typedef enum { e1000_eeprom_flash, e1000_eeprom_ich8, e1000_eeprom_none, /* No NVM support */ + e1000_eeprom_invm, e1000_num_eeprom_types } e1000_eeprom_type; @@ -212,6 +214,7 @@ typedef enum { e1000_phy_gg82563, e1000_phy_igp_3, e1000_phy_ife, + e1000_phy_igb, e1000_phy_bm, e1000_phy_undefined = 0xFF } e1000_phy_type; @@ -686,7 +689,9 @@ struct e1000_ffvt_entry { #define E1000_CTRL 0x00000 /* Device Control - RW */ #define E1000_STATUS 0x00008 /* Device Status - RO */ #define E1000_EECD 0x00010 /* EEPROM/Flash Control - RW */ +#define E1000_I210_EECD 0x12010 /* EEPROM/Flash Control - RW */ #define E1000_EERD 0x00014 /* EEPROM Read - RW */ +#define E1000_I210_EERD 0x12014 /* EEPROM Read - RW */ #define E1000_CTRL_EXT 0x00018 /* Extended Device Control - RW */ #define E1000_MDIC 0x00020 /* MDI Control - RW */ #define E1000_FCAL 0x00028 /* Flow Control Address Low - RW */ @@ -698,6 +703,7 @@ struct e1000_ffvt_entry { #define E1000_ICS 0x000C8 /* Interrupt Cause Set - WO */ #define E1000_IMS 0x000D0 /* Interrupt Mask Set - RW */ #define E1000_IMC 0x000D8 /* Interrupt Mask Clear - WO */ +#define E1000_I210_IAM 0x000E0 /* Interrupt Ack Auto Mask - RW */ #define E1000_RCTL 0x00100 /* RX Control - RW */ #define E1000_FCTTV 0x00170 /* Flow Control Transmit Timer Value - RW */ #define E1000_TXCW 0x00178 /* TX Configuration Word - RW */ @@ -711,14 +717,17 @@ struct e1000_ffvt_entry { #define E1000_EXTCNF_CTRL 0x00F00 /* Extended Configuration Control */ #define E1000_EXTCNF_SIZE 0x00F08 /* Extended Configuration Size */ #define E1000_PHY_CTRL 0x00F10 /* PHY Control Register in CSR */ +#define E1000_I210_PHY_CTRL 0x00E14 /* PHY Control Register in CSR */ #define FEXTNVM_SW_CONFIG 0x0001 #define E1000_PBA 0x01000 /* Packet Buffer Allocation - RW */ #define E1000_PBS 0x01008 /* Packet Buffer Size */ #define E1000_EEMNGCTL 0x01010 /* MNG EEprom Control */ +#define E1000_I210_EEMNGCTL 0x12030 /* MNG EEprom Control */ #define E1000_FLASH_UPDATES 1000 #define E1000_EEARBC 0x01024 /* EEPROM Auto Read Bus Control */ #define E1000_FLASHT 0x01028 /* FLASH Timer Register */ #define E1000_EEWR 0x0102C /* EEPROM Write Register - RW */ +#define E1000_I210_EEWR 0x12018 /* EEPROM Write Register - RW */ #define E1000_FLSWCTL 0x01030 /* FLASH control register */ #define E1000_FLSWDATA 0x01034 /* FLASH data register */ #define E1000_FLSWCNT 0x01038 /* FLASH Access Counter */ @@ -1222,6 +1231,7 @@ struct e1000_hw { #define E1000_STATUS_BUS64 0x00001000 /* In 64 bit slot */ #define E1000_STATUS_PCIX_MODE 0x00002000 /* PCI-X mode */ #define E1000_STATUS_PCIX_SPEED 0x0000C000 /* PCI-X bus speed */ +#define E1000_STATUS_PF_RST_DONE 0x00200000 /* PCI-X bus speed */ /* Constants used to intrepret the masked PCI-X bus speed. */ #define E1000_STATUS_PCIX_SPEED_66 0x00000000 /* PCI-X bus speed 50-66 MHz */ @@ -2438,6 +2448,8 @@ struct e1000_hw { #define BME1000_E_PHY_ID 0x01410CB0 +#define I210_I_PHY_ID 0x01410C00 + /* Miscellaneous PHY bit definitions. */ #define PHY_PREAMBLE 0xFFFFFFFF #define PHY_SOF 0x01 diff --git a/drivers/net/fm/t4240.c b/drivers/net/fm/t4240.c index 1eacb22..ae5aca4 100644 --- a/drivers/net/fm/t4240.c +++ b/drivers/net/fm/t4240.c @@ -71,6 +71,11 @@ phy_interface_t fman_port_enet_if(enum fm_port port) (is_serdes_configured(XFI_FM1_MAC10)))) return PHY_INTERFACE_MODE_XGMII; + if ((port == FM1_DTSEC9 || port == FM1_DTSEC10) && + ((is_serdes_configured(XFI_FM1_MAC9)) || + (is_serdes_configured(XFI_FM1_MAC10)))) + return PHY_INTERFACE_MODE_XGMII; + if ((port == FM2_10GEC1 || port == FM2_10GEC2) && ((is_serdes_configured(XAUI_FM2_MAC9)) || (is_serdes_configured(XAUI_FM2_MAC10)) || diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile index dbf7bf7..9556536 100644 --- a/drivers/net/phy/Makefile +++ b/drivers/net/phy/Makefile @@ -15,7 +15,6 @@ obj-$(CONFIG_PHY_ATHEROS) += atheros.o obj-$(CONFIG_PHY_BROADCOM) += broadcom.o obj-$(CONFIG_PHY_DAVICOM) += davicom.o obj-$(CONFIG_PHY_ET1011C) += et1011c.o -obj-$(CONFIG_PHY_ICPLUS) += icplus.o obj-$(CONFIG_PHY_LXT) += lxt.o obj-$(CONFIG_PHY_MARVELL) += marvell.o obj-$(CONFIG_PHY_MICREL) += micrel.o diff --git a/drivers/net/phy/icplus.c b/drivers/net/phy/icplus.c deleted file mode 100644 index 5971955..0000000 --- a/drivers/net/phy/icplus.c +++ /dev/null @@ -1,80 +0,0 @@ -/* - * ICPlus PHY drivers - * - * SPDX-License-Identifier: GPL-2.0+ - * - * Copyright (c) 2007 Freescale Semiconductor, Inc. - */ -#include <phy.h> - -/* IP101A/G - IP1001 */ -#define IP10XX_SPEC_CTRL_STATUS 16 /* Spec. Control Register */ -#define IP1001_SPEC_CTRL_STATUS_2 20 /* IP1001 Spec. Control Reg 2 */ -#define IP1001_PHASE_SEL_MASK 3 /* IP1001 RX/TXPHASE_SEL */ -#define IP1001_APS_ON 11 /* IP1001 APS Mode bit */ -#define IP101A_G_APS_ON 2 /* IP101A/G APS Mode bit */ -#define IP101A_G_IRQ_CONF_STATUS 0x11 /* Conf Info IRQ & Status Reg */ -#define IP101A_G_IRQ_PIN_USED (1<<15) /* INTR pin used */ -#define IP101A_G_IRQ_DEFAULT IP101A_G_IRQ_PIN_USED - -static int ip1001_config(struct phy_device *phydev) -{ - int c; - - /* Enable Auto Power Saving mode */ - c = phy_read(phydev, MDIO_DEVAD_NONE, IP1001_SPEC_CTRL_STATUS_2); - if (c < 0) - return c; - c |= IP1001_APS_ON; - c = phy_write(phydev, MDIO_DEVAD_NONE, IP1001_SPEC_CTRL_STATUS_2, c); - if (c < 0) - return c; - - /* INTR pin used: speed/link/duplex will cause an interrupt */ - c = phy_write(phydev, MDIO_DEVAD_NONE, IP101A_G_IRQ_CONF_STATUS, - IP101A_G_IRQ_DEFAULT); - if (c < 0) - return c; - - if (phydev->interface == PHY_INTERFACE_MODE_RGMII) { - /* - * Additional delay (2ns) used to adjust RX clock phase - * at RGMII interface - */ - c = phy_read(phydev, MDIO_DEVAD_NONE, IP10XX_SPEC_CTRL_STATUS); - if (c < 0) - return c; - - c |= IP1001_PHASE_SEL_MASK; - c = phy_write(phydev, MDIO_DEVAD_NONE, IP10XX_SPEC_CTRL_STATUS, - c); - if (c < 0) - return c; - } - - return 0; -} - -static int ip1001_startup(struct phy_device *phydev) -{ - genphy_update_link(phydev); - genphy_parse_link(phydev); - - return 0; -} -static struct phy_driver IP1001_driver = { - .name = "ICPlus IP1001", - .uid = 0x02430d90, - .mask = 0x0ffffff0, - .features = PHY_GBIT_FEATURES, - .config = &ip1001_config, - .startup = &ip1001_startup, - .shutdown = &genphy_shutdown, -}; - -int phy_icplus_init(void) -{ - phy_register(&IP1001_driver); - - return 0; -} diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index aac85c4..1d6c14f 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -454,9 +454,6 @@ int phy_init(void) #ifdef CONFIG_PHY_ET1011C phy_et1011c_init(); #endif -#ifdef CONFIG_PHY_ICPLUS - phy_icplus_init(); -#endif #ifdef CONFIG_PHY_LXT phy_lxt_init(); #endif diff --git a/drivers/net/phy/vitesse.c b/drivers/net/phy/vitesse.c index c58fe50..2b29cd8 100644 --- a/drivers/net/phy/vitesse.c +++ b/drivers/net/phy/vitesse.c @@ -126,7 +126,6 @@ static int cis8204_config(struct phy_device *phydev) genphy_config_aneg(phydev); if ((phydev->interface == PHY_INTERFACE_MODE_RGMII) || - (phydev->interface == PHY_INTERFACE_MODE_RGMII) || (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) || (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID)) phy_write(phydev, MDIO_DEVAD_NONE, MIIM_CIS8204_EPHY_CON, diff --git a/drivers/net/plb2800_eth.c b/drivers/net/plb2800_eth.c deleted file mode 100644 index f869514..0000000 --- a/drivers/net/plb2800_eth.c +++ /dev/null @@ -1,373 +0,0 @@ -/* - * PLB2800 internal switch ethernet driver. - * - * (C) Copyright 2003 - * Wolfgang Denk, DENX Software Engineering, wd@denx.de. - * - * SPDX-License-Identifier: GPL-2.0+ - */ - -#include <common.h> -#include <malloc.h> -#include <net.h> -#include <netdev.h> -#include <asm/addrspace.h> - - -#define NUM_RX_DESC PKTBUFSRX -#define TOUT_LOOP 1000000 - -#define LONG_REF(addr) (*((volatile unsigned long*)addr)) - -#define CMAC_CRX_CTRL LONG_REF(0xb800c870) -#define CMAC_CTX_CTRL LONG_REF(0xb800c874) -#define SYS_MAC_ADDR_0 LONG_REF(0xb800c878) -#define SYS_MAC_ADDR_1 LONG_REF(0xb800c87c) -#define MIPS_H_MASK LONG_REF(0xB800C810) - -#define MA_LEARN LONG_REF(0xb8008004) -#define DA_LOOKUP LONG_REF(0xb8008008) - -#define CMAC_CRX_CTRL_PD 0x00000001 -#define CMAC_CRX_CTRL_CG 0x00000002 -#define CMAC_CRX_CTRL_PL_SHIFT 2 -#define CMAC_CRIT 0x0 -#define CMAC_NON_CRIT 0x1 -#define MBOX_STAT_ID_SHF 28 -#define MBOX_STAT_CP 0x80000000 -#define MBOX_STAT_MB 0x00000001 -#define EN_MA_LEARN 0x02000000 -#define EN_DA_LKUP 0x01000000 -#define MA_DEST_SHF 11 -#define DA_DEST_SHF 11 -#define DA_STATE_SHF 19 -#define TSTAMP_MS 0x00000000 -#define SW_H_MBOX4_MASK 0x08000000 -#define SW_H_MBOX3_MASK 0x04000000 -#define SW_H_MBOX2_MASK 0x02000000 -#define SW_H_MBOX1_MASK 0x01000000 - -typedef volatile struct { - unsigned int stat; - unsigned int cmd; - unsigned int cnt; - unsigned int adr; -} mailbox_t; - -#define MBOX_REG(mb) ((mailbox_t*)(0xb800c830+(mb<<4))) - -typedef volatile struct { - unsigned int word0; - unsigned int word1; - unsigned int word2; -} mbhdr_t; - -#define MBOX_MEM(mb) ((void*)(0xb800a000+((3-mb)<<11))) - - -static int plb2800_eth_init(struct eth_device *dev, bd_t * bis); -static int plb2800_eth_send(struct eth_device *dev, void *packet, int length); -static int plb2800_eth_recv(struct eth_device *dev); -static void plb2800_eth_halt(struct eth_device *dev); - -static void plb2800_set_mac_addr(struct eth_device *dev, unsigned char * addr); -static unsigned char * plb2800_get_mac_addr(void); - -static int rx_new; -static int mac_addr_set = 0; - - -int plb2800_eth_initialize(bd_t * bis) -{ - struct eth_device *dev; - ulong temp; - -#ifdef DEBUG - printf("Entered plb2800_eth_initialize()\n"); -#endif - - if (!(dev = (struct eth_device *) malloc (sizeof *dev))) - { - printf("Failed to allocate memory\n"); - return -1; - } - memset(dev, 0, sizeof(*dev)); - - sprintf(dev->name, "PLB2800 Switch"); - dev->init = plb2800_eth_init; - dev->halt = plb2800_eth_halt; - dev->send = plb2800_eth_send; - dev->recv = plb2800_eth_recv; - - eth_register(dev); - - /* bug fix */ - *(ulong *)0xb800e800 = 0x838; - - /* Set MBOX ownership */ - temp = CMAC_CRIT << MBOX_STAT_ID_SHF; - MBOX_REG(0)->stat = temp; - MBOX_REG(1)->stat = temp; - - temp = CMAC_NON_CRIT << MBOX_STAT_ID_SHF; - MBOX_REG(2)->stat = temp; - MBOX_REG(3)->stat = temp; - - plb2800_set_mac_addr(dev, plb2800_get_mac_addr()); - - /* Disable all Mbox interrupt */ - temp = MIPS_H_MASK; - temp &= ~ (SW_H_MBOX1_MASK | SW_H_MBOX2_MASK | SW_H_MBOX3_MASK | SW_H_MBOX4_MASK) ; - MIPS_H_MASK = temp; - -#ifdef DEBUG - printf("Leaving plb2800_eth_initialize()\n"); -#endif - - return 0; -} - -static int plb2800_eth_init(struct eth_device *dev, bd_t * bis) -{ -#ifdef DEBUG - printf("Entering plb2800_eth_init()\n"); -#endif - - plb2800_set_mac_addr(dev, dev->enetaddr); - - rx_new = 0; - -#ifdef DEBUG - printf("Leaving plb2800_eth_init()\n"); -#endif - - return 0; -} - - -static int plb2800_eth_send(struct eth_device *dev, void *packet, int length) -{ - int i; - int res = -1; - u32 temp; - mailbox_t * mb = MBOX_REG(0); - char * mem = MBOX_MEM(0); - -#ifdef DEBUG - printf("Entered plb2800_eth_send()\n"); -#endif - - if (length <= 0) - { - printf ("%s: bad packet size: %d\n", dev->name, length); - goto Done; - } - - if (length < 64) - { - length = 64; - } - - temp = CMAC_CRX_CTRL_CG | ((length + 4) << CMAC_CRX_CTRL_PL_SHIFT); - -#ifdef DEBUG - printf("0 mb->stat = 0x%x\n", mb->stat); -#endif - - for(i = 0; mb->stat & (MBOX_STAT_CP | MBOX_STAT_MB); i++) - { - if (i >= TOUT_LOOP) - { - printf("%s: tx buffer not ready\n", dev->name); - printf("1 mb->stat = 0x%x\n", mb->stat); - goto Done; - } - } - - /* For some strange reason, memcpy doesn't work, here! - */ - do - { - int words = (length >> 2) + 1; - unsigned int* dst = (unsigned int*)(mem); - unsigned int* src = (unsigned int*)(packet); - for (i = 0; i < words; i++) - { - *dst = *src; - dst++; - src++; - }; - } while(0); - - CMAC_CRX_CTRL = temp; - mb->cmd = MBOX_STAT_CP; - -#ifdef DEBUG - printf("2 mb->stat = 0x%x\n", mb->stat); -#endif - - res = length; -Done: - -#ifdef DEBUG - printf("Leaving plb2800_eth_send()\n"); -#endif - - return res; -} - - -static int plb2800_eth_recv(struct eth_device *dev) -{ - int length = 0; - mailbox_t * mbox = MBOX_REG(3); - unsigned char * hdr = MBOX_MEM(3); - unsigned int stat; - -#ifdef DEBUG - printf("Entered plb2800_eth_recv()\n"); -#endif - - for (;;) - { - stat = mbox->stat; - - if (!(stat & MBOX_STAT_CP)) - { - break; - } - - length = ((*(hdr + 6) & 0x3f) << 8) + *(hdr + 7); - memcpy((void *)NetRxPackets[rx_new], hdr + 12, length); - - stat &= ~MBOX_STAT_CP; - mbox->stat = stat; -#ifdef DEBUG - { - int i; - for (i=0;i<length - 4;i++) - { - if (i % 16 == 0) printf("\n%04x: ", i); - printf("%02X ", NetRxPackets[rx_new][i]); - } - printf("\n"); - } -#endif - - if (length) - { -#ifdef DEBUG - printf("Received %d bytes\n", length); -#endif - NetReceive((void*)(NetRxPackets[rx_new]), - length - 4); - } - else - { -#if 1 - printf("Zero length!!!\n"); -#endif - } - - rx_new = (rx_new + 1) % NUM_RX_DESC; - } - -#ifdef DEBUG - printf("Leaving plb2800_eth_recv()\n"); -#endif - - return length; -} - - -static void plb2800_eth_halt(struct eth_device *dev) -{ -#ifdef DEBUG - printf("Entered plb2800_eth_halt()\n"); -#endif - -#ifdef DEBUG - printf("Leaving plb2800_eth_halt()\n"); -#endif -} - -static void plb2800_set_mac_addr(struct eth_device *dev, unsigned char * addr) -{ - char packet[60]; - ulong temp; - int ix; - - if (mac_addr_set || - NULL == addr || memcmp(addr, "\0\0\0\0\0\0", 6) == 0) - { - return; - } - - /* send one packet through CPU port - * in order to learn system MAC address - */ - - /* Set DA_LOOKUP register */ - temp = EN_MA_LEARN | (0 << DA_STATE_SHF) | (63 << DA_DEST_SHF); - DA_LOOKUP = temp; - - /* Set MA_LEARN register */ - temp = 50 << MA_DEST_SHF; /* static entry */ - MA_LEARN = temp; - - /* set destination address */ - for (ix=0;ix<6;ix++) - packet[ix] = 0xff; - - /* set source address = system MAC address */ - for (ix=0;ix<6;ix++) - packet[6+ix] = addr[ix]; - - /* set type field */ - packet[12]=0xaa; - packet[13]=0x55; - - /* set data field */ - for(ix=14;ix<60;ix++) - packet[ix] = 0x00; - -#ifdef DEBUG - for (ix=0;ix<6;ix++) - printf("mac_addr[%d]=%02X\n", ix, (unsigned char)packet[6+ix]); -#endif - - /* set one packet */ - plb2800_eth_send(dev, packet, sizeof(packet)); - - /* delay for a while */ - for(ix=0;ix<65535;ix++) - temp = ~temp; - - /* Set CMAC_CTX_CTRL register */ - temp = TSTAMP_MS; /* no autocast */ - CMAC_CTX_CTRL = temp; - - /* Set DA_LOOKUP register */ - temp = EN_DA_LKUP; - DA_LOOKUP = temp; - - mac_addr_set = 1; -} - -static unsigned char * plb2800_get_mac_addr(void) -{ - static unsigned char addr[6]; - char *tmp, *end; - int i; - - tmp = getenv ("ethaddr"); - if (NULL == tmp) return NULL; - - for (i=0; i<6; i++) { - addr[i] = tmp ? simple_strtoul(tmp, &end, 16) : 0; - if (tmp) - tmp = (*end) ? end+1 : end; - } - - return addr; -} diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index ed113bf..4fd9c53 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -323,7 +323,7 @@ int __pci_hose_bus_to_phys(struct pci_controller *hose, continue; if (bus_addr >= res->bus_start && - bus_addr < res->bus_start + res->size) { + (bus_addr - res->bus_start) < res->size) { *pa = (bus_addr - res->bus_start + res->phys_start); return 0; } diff --git a/drivers/pci/pcie_imx.c b/drivers/pci/pcie_imx.c index c48737e..a3982c4 100644 --- a/drivers/pci/pcie_imx.c +++ b/drivers/pci/pcie_imx.c @@ -509,10 +509,6 @@ static int imx6_pcie_deassert_core_reset(void) imx6_pcie_toggle_power(); - /* Enable PCIe */ - clrbits_le32(&iomuxc_regs->gpr[1], IOMUXC_GPR1_TEST_POWERDOWN); - setbits_le32(&iomuxc_regs->gpr[1], IOMUXC_GPR1_REF_SSP_EN); - enable_pcie_clock(); /* @@ -521,6 +517,10 @@ static int imx6_pcie_deassert_core_reset(void) */ mdelay(50); + /* Enable PCIe */ + clrbits_le32(&iomuxc_regs->gpr[1], IOMUXC_GPR1_TEST_POWERDOWN); + setbits_le32(&iomuxc_regs->gpr[1], IOMUXC_GPR1_REF_SSP_EN); + imx6_pcie_toggle_reset(); return 0; diff --git a/drivers/pcmcia/tqm8xx_pcmcia.c b/drivers/pcmcia/tqm8xx_pcmcia.c index dda7d37..8b74478 100644 --- a/drivers/pcmcia/tqm8xx_pcmcia.c +++ b/drivers/pcmcia/tqm8xx_pcmcia.c @@ -20,14 +20,12 @@ #endif #if defined(CONFIG_PCMCIA) \ - && (defined(CONFIG_TQM8xxL) || defined(CONFIG_SVM_SC8xx)) + && defined(CONFIG_TQM8xxL) #if defined(CONFIG_VIRTLAB2) #define PCMCIA_BOARD_MSG "Virtlab2" #elif defined(CONFIG_TQM8xxL) #define PCMCIA_BOARD_MSG "TQM8xxL" -#elif defined(CONFIG_SVM_SC8xx) -#define PCMCIA_BOARD_MSG "SC8xx" #endif #if defined(CONFIG_NSCU) @@ -302,4 +300,4 @@ done: return 0; } -#endif /* CONFIG_PCMCIA && (CONFIG_TQM8xxL || CONFIG_SVM_SC8xx) */ +#endif /* CONFIG_PCMCIA && CONFIG_TQM8xxL */ diff --git a/drivers/power/pmic/pmic_pfuze100.c b/drivers/power/pmic/pmic_pfuze100.c index 21f12d2..22a04c0 100644 --- a/drivers/power/pmic/pmic_pfuze100.c +++ b/drivers/power/pmic/pmic_pfuze100.c @@ -13,7 +13,7 @@ int power_pfuze100_init(unsigned char bus) { - static const char name[] = "PFUZE100_PMIC"; + static const char name[] = "PFUZE100"; struct pmic *p = pmic_alloc(); if (!p) { diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile new file mode 100644 index 0000000..c0c4883 --- /dev/null +++ b/drivers/pwm/Makefile @@ -0,0 +1,13 @@ +# +# (C) Copyright 2006 +# Wolfgang Denk, DENX Software Engineering, wd@denx.de. +# +# (C) Copyright 2001 +# Erik Theisen, Wave 7 Optics, etheisen@mindspring.com. +# +# SPDX-License-Identifier: GPL-2.0+ +# + +#ccflags-y += -DDEBUG + +obj-$(CONFIG_PWM_IMX) += pwm-imx.o pwm-imx-util.o diff --git a/drivers/pwm/pwm-imx-util.c b/drivers/pwm/pwm-imx-util.c new file mode 100644 index 0000000..f1d0b35 --- /dev/null +++ b/drivers/pwm/pwm-imx-util.c @@ -0,0 +1,73 @@ +/* + * (C) Copyright 2014 + * Heiko Schocher, DENX Software Engineering, hs@denx.de. + * + * Basic support for the pwm modul on imx6. + * + * Based on linux:drivers/pwm/pwm-imx.c + * from + * Sascha Hauer <s.hauer@pengutronix.de> + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include <common.h> +#include <div64.h> +#include <asm/arch/imx-regs.h> + +/* pwm_id from 0..3 */ +struct pwm_regs *pwm_id_to_reg(int pwm_id) +{ + switch (pwm_id) { + case 0: + return (struct pwm_regs *)PWM1_BASE_ADDR; + break; + case 1: + return (struct pwm_regs *)PWM2_BASE_ADDR; + break; + case 2: + return (struct pwm_regs *)PWM3_BASE_ADDR; + break; + case 3: + return (struct pwm_regs *)PWM4_BASE_ADDR; + break; + default: + printf("unknown pwm_id: %d\n", pwm_id); + break; + } + return NULL; +} + +int pwm_imx_get_parms(int period_ns, int duty_ns, unsigned long *period_c, + unsigned long *duty_c, unsigned long *prescale) +{ + unsigned long long c; + + /* + * we have not yet a clock framework for imx6, so add the clock + * value here as a define. Replace it when we have the clock + * framework. + */ + c = CONFIG_IMX6_PWM_PER_CLK; + c = c * period_ns; + do_div(c, 1000000000); + *period_c = c; + + *prescale = *period_c / 0x10000 + 1; + + *period_c /= *prescale; + c = (unsigned long long)(*period_c * duty_ns); + do_div(c, period_ns); + *duty_c = c; + + /* + * according to imx pwm RM, the real period value should be + * PERIOD value in PWMPR plus 2. + */ + if (*period_c > 2) + *period_c -= 2; + else + *period_c = 0; + + return 0; +} diff --git a/drivers/pwm/pwm-imx-util.h b/drivers/pwm/pwm-imx-util.h new file mode 100644 index 0000000..45465c4 --- /dev/null +++ b/drivers/pwm/pwm-imx-util.h @@ -0,0 +1,16 @@ +/* + * (C) Copyright 2014 + * Heiko Schocher, DENX Software Engineering, hs@denx.de. + * + * Basic support for the pwm modul on imx6. + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#ifndef _pwm_imx_util_h_ +#define _pwm_imx_util_h_ + +struct pwm_regs *pwm_id_to_reg(int pwm_id); +int pwm_imx_get_parms(int period_ns, int duty_ns, unsigned long *period_c, + unsigned long *duty_c, unsigned long *prescale); +#endif diff --git a/drivers/pwm/pwm-imx.c b/drivers/pwm/pwm-imx.c new file mode 100644 index 0000000..40bf027 --- /dev/null +++ b/drivers/pwm/pwm-imx.c @@ -0,0 +1,59 @@ +/* + * (C) Copyright 2014 + * Heiko Schocher, DENX Software Engineering, hs@denx.de. + * + * Basic support for the pwm modul on imx6. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <div64.h> +#include <pwm.h> +#include <asm/arch/imx-regs.h> +#include <asm/io.h> +#include "pwm-imx-util.h" + +int pwm_init(int pwm_id, int div, int invert) +{ + struct pwm_regs *pwm = (struct pwm_regs *)pwm_id_to_reg(pwm_id); + + writel(0, &pwm->ir); + return 0; +} + +int pwm_config(int pwm_id, int duty_ns, int period_ns) +{ + struct pwm_regs *pwm = (struct pwm_regs *)pwm_id_to_reg(pwm_id); + unsigned long period_cycles, duty_cycles, prescale; + u32 cr; + + pwm_imx_get_parms(period_ns, duty_ns, &period_cycles, &duty_cycles, + &prescale); + + cr = PWMCR_PRESCALER(prescale) | + PWMCR_DOZEEN | PWMCR_WAITEN | + PWMCR_DBGEN | PWMCR_CLKSRC_IPG_HIGH; + + writel(cr, &pwm->cr); + /* set duty cycles */ + writel(duty_cycles, &pwm->sar); + /* set period cycles */ + writel(period_cycles, &pwm->pr); + return 0; +} + +int pwm_enable(int pwm_id) +{ + struct pwm_regs *pwm = (struct pwm_regs *)pwm_id_to_reg(pwm_id); + + setbits_le32(&pwm->cr, PWMCR_EN); + return 0; +} + +void pwm_disable(int pwm_id) +{ + struct pwm_regs *pwm = (struct pwm_regs *)pwm_id_to_reg(pwm_id); + + clrbits_le32(&pwm->cr, PWMCR_EN); +} diff --git a/drivers/qe/qe.c b/drivers/qe/qe.c index be09a17..4358a91 100644 --- a/drivers/qe/qe.c +++ b/drivers/qe/qe.c @@ -333,7 +333,7 @@ int qe_upload_firmware(const struct qe_firmware *firmware) /* Check the magic */ if ((hdr->magic[0] != 'Q') || (hdr->magic[1] != 'E') || (hdr->magic[2] != 'F')) { - printf("Not a microcode\n"); + printf("QE microcode not found\n"); #ifdef CONFIG_DEEP_SLEEP setbits_be32(&gur->devdisr, MPC85xx_DEVDISR_QE_DISABLE); #endif diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index 003d322..43f8546 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -16,6 +16,7 @@ obj-$(CONFIG_RTC_DS1302) += ds1302.o obj-$(CONFIG_RTC_DS1306) += ds1306.o obj-$(CONFIG_RTC_DS1307) += ds1307.o obj-$(CONFIG_RTC_DS1338) += ds1307.o +obj-$(CONFIG_RTC_DS1339) += ds1307.o obj-$(CONFIG_RTC_DS1337) += ds1337.o obj-$(CONFIG_RTC_DS1374) += ds1374.o obj-$(CONFIG_RTC_DS1388) += ds1337.o diff --git a/drivers/rtc/ds1307.c b/drivers/rtc/ds1307.c index 1a2bad3..03ab1a8 100644 --- a/drivers/rtc/ds1307.c +++ b/drivers/rtc/ds1307.c @@ -9,7 +9,7 @@ /* * Date & Time support (no alarms) for Dallas Semiconductor (now Maxim) - * DS1307 and DS1338 Real Time Clock (RTC). + * DS1307 and DS1338/9 Real Time Clock (RTC). * * based on ds1337.c */ diff --git a/drivers/serial/ns16550.c b/drivers/serial/ns16550.c index 8e7052d..079f67d 100644 --- a/drivers/serial/ns16550.c +++ b/drivers/serial/ns16550.c @@ -81,7 +81,7 @@ void NS16550_init(NS16550_t com_port, int baud_divisor) serial_out(baud_divisor & 0xff, &com_port->dll); serial_out((baud_divisor >> 8) & 0xff, &com_port->dlm); serial_out(UART_LCRVAL, &com_port->lcr); -#if (defined(CONFIG_OMAP) && !defined(CONFIG_OMAP3_ZOOM2)) || \ +#if defined(CONFIG_OMAP) || \ defined(CONFIG_AM33XX) || defined(CONFIG_SOC_DA8XX) || \ defined(CONFIG_TI81XX) || defined(CONFIG_AM43XX) diff --git a/drivers/serial/serial_ns16550.c b/drivers/serial/serial_ns16550.c index 4413e69..dafeed7 100644 --- a/drivers/serial/serial_ns16550.c +++ b/drivers/serial/serial_ns16550.c @@ -122,15 +122,6 @@ static int calc_divisor (NS16550_t port) { const unsigned int mode_x_div = 16; -#ifdef CONFIG_OMAP1510 - /* If can't cleanly clock 115200 set div to 1 */ - if ((CONFIG_SYS_NS16550_CLK == 12000000) && (gd->baudrate == 115200)) { - port->osc_12m_sel = OSC_12M_SEL; /* enable 6.5 * divisor */ - return (1); /* return 1 for base divisor */ - } - port->osc_12m_sel = 0; /* clear if previsouly set */ -#endif - return DIV_ROUND_CLOSEST(CONFIG_SYS_NS16550_CLK, mode_x_div * gd->baudrate); } diff --git a/drivers/serial/serial_sh.c b/drivers/serial/serial_sh.c index 0826d59..144a925 100644 --- a/drivers/serial/serial_sh.c +++ b/drivers/serial/serial_sh.c @@ -49,9 +49,15 @@ static struct uart_port sh_sci = { static void sh_serial_setbrg(void) { DECLARE_GLOBAL_DATA_PTR; - +#ifdef CONFIG_SCIF_USE_EXT_CLK + unsigned short dl = DL_VALUE(gd->baudrate, CONFIG_SH_SCIF_CLK_FREQ); + sci_out(&sh_sci, DL, dl); + /* Need wait: Clock * 1/dl $B!_(B 1/16 */ + udelay((1000000 * dl * 16 / CONFIG_SYS_CLK_FREQ) * 1000 + 1); +#else sci_out(&sh_sci, SCBRR, SCBRR_VALUE(gd->baudrate, CONFIG_SH_SCIF_CLK_FREQ)); +#endif } static int sh_serial_init(void) diff --git a/drivers/serial/serial_sh.h b/drivers/serial/serial_sh.h index 341997c..fe8cde4 100644 --- a/drivers/serial/serial_sh.h +++ b/drivers/serial/serial_sh.h @@ -588,7 +588,8 @@ SCIF_FNS(SCSPTR, 0, 0, 0, 0) #else SCIF_FNS(SCSPTR, 0, 0, 0x20, 16) #endif -#if defined(CONFIG_R8A7790) || defined(CONFIG_R8A7791) +#if defined(CONFIG_R8A7790) || defined(CONFIG_R8A7791) || \ + defined(CONFIG_R8A7794) SCIF_FNS(DL, 0, 0, 0x30, 16) SCIF_FNS(CKS, 0, 0, 0x34, 16) #endif @@ -734,8 +735,8 @@ static inline int scbrr_calc(struct uart_port port, int bps, int clk) #elif defined(__H8300H__) || defined(__H8300S__) #define SCBRR_VALUE(bps, clk) (((clk*1000/32)/bps)-1) #elif defined(CONFIG_R8A7790) || defined(CONFIG_R8A7791) -#define SCBRR DL -#define SCBRR_VALUE(bps, clk) (clk / bps / 16) +#define DL_VALUE(bps, clk) (clk / bps / 16) /* External Clock */ +#define SCBRR_VALUE(bps, clk) ((clk+16*bps)/(32*bps)-1) /* Internal Clock */ #else /* Generic SH */ #define SCBRR_VALUE(bps, clk) ((clk+16*bps)/(32*bps)-1) #endif diff --git a/drivers/serial/usbtty.h b/drivers/serial/usbtty.h index 21a3ef4..538b6d7 100644 --- a/drivers/serial/usbtty.h +++ b/drivers/serial/usbtty.h @@ -14,8 +14,6 @@ #include <usbdevice.h> #if defined(CONFIG_PPC) #include <usb/mpc8xx_udc.h> -#elif defined(CONFIG_OMAP1510) -#include <usb/omap1510_udc.h> #elif defined(CONFIG_CPU_PXA27X) #include <usb/pxa27x_udc.h> #elif defined(CONFIG_DW_UDC) diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 66becdc..2efd5a4 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -31,8 +31,6 @@ ifdef CONFIG_USB_DEVICE obj-y += core.o obj-y += ep0.o obj-$(CONFIG_DW_UDC) += designware_udc.o -obj-$(CONFIG_OMAP1510) += omap1510_udc.o -obj-$(CONFIG_OMAP1610) += omap1510_udc.o obj-$(CONFIG_MPC885_FAMILY) += mpc8xx_udc.o obj-$(CONFIG_CPU_PXA27X) += pxa27x_udc.o endif diff --git a/drivers/usb/gadget/ether.c b/drivers/usb/gadget/ether.c index cc6cc1f..d0dd29f 100644 --- a/drivers/usb/gadget/ether.c +++ b/drivers/usb/gadget/ether.c @@ -25,15 +25,10 @@ #define atomic_read extern struct platform_data brd; -#define spin_lock(x) -#define spin_unlock(x) unsigned packet_received, packet_sent; -#define GFP_ATOMIC ((gfp_t) 0) -#define GFP_KERNEL ((gfp_t) 0) - /* * Ethernet gadget driver -- with CDC and non-CDC options * Builds on hardware support for a full duplex link. diff --git a/drivers/usb/gadget/f_dfu.c b/drivers/usb/gadget/f_dfu.c index 859fe82..9863dec 100644 --- a/drivers/usb/gadget/f_dfu.c +++ b/drivers/usb/gadget/f_dfu.c @@ -162,17 +162,27 @@ static void dfu_set_poll_timeout(struct dfu_status *dstat, unsigned int ms) static void dnload_request_complete(struct usb_ep *ep, struct usb_request *req) { struct f_dfu *f_dfu = req->context; + int ret; - dfu_write(dfu_get_entity(f_dfu->altsetting), req->buf, - req->length, f_dfu->blk_seq_num); + ret = dfu_write(dfu_get_entity(f_dfu->altsetting), req->buf, + req->length, f_dfu->blk_seq_num); + if (ret) { + f_dfu->dfu_status = DFU_STATUS_errUNKNOWN; + f_dfu->dfu_state = DFU_STATE_dfuERROR; + } } static void dnload_request_flush(struct usb_ep *ep, struct usb_request *req) { struct f_dfu *f_dfu = req->context; + int ret; - dfu_flush(dfu_get_entity(f_dfu->altsetting), req->buf, - req->length, f_dfu->blk_seq_num); + ret = dfu_flush(dfu_get_entity(f_dfu->altsetting), req->buf, + req->length, f_dfu->blk_seq_num); + if (ret) { + f_dfu->dfu_status = DFU_STATUS_errUNKNOWN; + f_dfu->dfu_state = DFU_STATE_dfuERROR; + } } static inline int dfu_get_manifest_timeout(struct dfu_entity *dfu) @@ -770,6 +780,8 @@ static int dfu_set_alt(struct usb_function *f, unsigned intf, unsigned alt) debug("%s: intf:%d alt:%d\n", __func__, intf, alt); f_dfu->altsetting = alt; + f_dfu->dfu_state = DFU_STATE_dfuIDLE; + f_dfu->dfu_status = DFU_STATUS_OK; return 0; } diff --git a/drivers/usb/gadget/f_thor.c b/drivers/usb/gadget/f_thor.c index 4e06273..c85b0fb 100644 --- a/drivers/usb/gadget/f_thor.c +++ b/drivers/usb/gadget/f_thor.c @@ -142,7 +142,8 @@ static long long int download_head(unsigned long long total, int *cnt) { long long int rcv_cnt = 0, left_to_rcv, ret_rcv; - void *transfer_buffer = dfu_get_buf(); + struct dfu_entity *dfu_entity = dfu_get_entity(alt_setting_num); + void *transfer_buffer = dfu_get_buf(dfu_entity); void *buf = transfer_buffer; int usb_pkt_cnt = 0, ret; @@ -205,7 +206,7 @@ static long long int download_head(unsigned long long total, static int download_tail(long long int left, int cnt) { struct dfu_entity *dfu_entity = dfu_get_entity(alt_setting_num); - void *transfer_buffer = dfu_get_buf(); + void *transfer_buffer = dfu_get_buf(dfu_entity); int ret; debug("%s: left: %llu cnt: %d\n", __func__, left, cnt); diff --git a/drivers/usb/gadget/omap1510_udc.c b/drivers/usb/gadget/omap1510_udc.c deleted file mode 100644 index bdc1b88..0000000 --- a/drivers/usb/gadget/omap1510_udc.c +++ /dev/null @@ -1,1555 +0,0 @@ -/* - * (C) Copyright 2003 - * Gerry Hamel, geh@ti.com, Texas Instruments - * - * Based on - * linux/drivers/usb/device/bi/omap.c - * TI OMAP1510 USB bus interface driver - * - * Author: MontaVista Software, Inc. - * source@mvista.com - * (C) Copyright 2002 - * - * SPDX-License-Identifier: GPL-2.0+ - */ - -#include <common.h> -#include <asm/io.h> -#ifdef CONFIG_OMAP_SX1 -#include <i2c.h> -#endif -#include <usbdevice.h> -#include <usb/omap1510_udc.h> -#include <usb/udc.h> - -#include "ep0.h" - - -#define UDC_INIT_MDELAY 80 /* Device settle delay */ -#define UDC_MAX_ENDPOINTS 31 /* Number of endpoints on this UDC */ - -/* Some kind of debugging output... */ -#if 1 -#define UDCDBG(str) -#define UDCDBGA(fmt,args...) -#else /* The bugs still exists... */ -#define UDCDBG(str) serial_printf("[%s] %s:%d: " str "\n", __FILE__,__FUNCTION__,__LINE__) -#define UDCDBGA(fmt,args...) serial_printf("[%s] %s:%d: " fmt "\n", __FILE__,__FUNCTION__,__LINE__, ##args) -#endif - -#if 1 -#define UDCREG(name) -#define UDCREGL(name) -#else /* The bugs still exists... */ -#define UDCREG(name) serial_printf("%s():%d: %s[%08x]=%.4x\n",__FUNCTION__,__LINE__, (#name), name, inw(name)) /* For 16-bit regs */ -#define UDCREGL(name) serial_printf("%s():%d: %s[%08x]=%.8x\n",__FUNCTION__,__LINE__, (#name), name, inl(name)) /* For 32-bit regs */ -#endif - - -static struct urb *ep0_urb = NULL; - -static struct usb_device_instance *udc_device; /* Used in interrupt handler */ -static u16 udc_devstat = 0; /* UDC status (DEVSTAT) */ -static u32 udc_interrupts = 0; - -static void udc_stall_ep (unsigned int ep_addr); - - -static struct usb_endpoint_instance *omap1510_find_ep (int ep) -{ - int i; - - for (i = 0; i < udc_device->bus->max_endpoints; i++) { - if (udc_device->bus->endpoint_array[i].endpoint_address == ep) - return &udc_device->bus->endpoint_array[i]; - } - return NULL; -} - -/* ************************************************************************** */ -/* IO - */ - -/* - * omap1510_prepare_endpoint_for_rx - * - * This function implements TRM Figure 14-11. - * - * The endpoint to prepare for transfer is specified as a physical endpoint - * number. For OUT (rx) endpoints 1 through 15, the corresponding endpoint - * configuration register is checked to see if the endpoint is ISO or not. - * If the OUT endpoint is valid and is non-ISO then its FIFO is enabled. - * No action is taken for endpoint 0 or for IN (tx) endpoints 16 through 30. - */ -static void omap1510_prepare_endpoint_for_rx (int ep_addr) -{ - int ep_num = ep_addr & USB_ENDPOINT_NUMBER_MASK; - - UDCDBGA ("omap1510_prepare_endpoint %x", ep_addr); - if (((ep_addr & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT)) { - if ((inw (UDC_EP_RX (ep_num)) & - (UDC_EPn_RX_Valid | UDC_EPn_RX_Iso)) == - UDC_EPn_RX_Valid) { - /* rx endpoint is valid, non-ISO, so enable its FIFO */ - outw (UDC_EP_Sel | ep_num, UDC_EP_NUM); - outw (UDC_Set_FIFO_En, UDC_CTRL); - outw (0, UDC_EP_NUM); - } - } -} - -/* omap1510_configure_endpoints - * - * This function implements TRM Figure 14-10. - */ -static void omap1510_configure_endpoints (struct usb_device_instance *device) -{ - int ep; - struct usb_bus_instance *bus; - struct usb_endpoint_instance *endpoint; - unsigned short ep_ptr; - unsigned short ep_size; - unsigned short ep_isoc; - unsigned short ep_doublebuffer; - int ep_addr; - int packet_size; - int buffer_size; - int attributes; - - bus = device->bus; - - /* There is a dedicated 2048 byte buffer for USB packets that may be - * arbitrarily partitioned among the endpoints on 8-byte boundaries. - * The first 8 bytes are reserved for receiving setup packets on - * endpoint 0. - */ - ep_ptr = 8; /* reserve the first 8 bytes for the setup fifo */ - - for (ep = 0; ep < bus->max_endpoints; ep++) { - endpoint = bus->endpoint_array + ep; - ep_addr = endpoint->endpoint_address; - if ((ep_addr & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN) { - /* IN endpoint */ - packet_size = endpoint->tx_packetSize; - attributes = endpoint->tx_attributes; - } else { - /* OUT endpoint */ - packet_size = endpoint->rcv_packetSize; - attributes = endpoint->rcv_attributes; - } - - switch (packet_size) { - case 0: - ep_size = 0; - break; - case 8: - ep_size = 0; - break; - case 16: - ep_size = 1; - break; - case 32: - ep_size = 2; - break; - case 64: - ep_size = 3; - break; - case 128: - ep_size = 4; - break; - case 256: - ep_size = 5; - break; - case 512: - ep_size = 6; - break; - default: - UDCDBGA ("ep 0x%02x has bad packet size %d", - ep_addr, packet_size); - packet_size = 0; - ep_size = 0; - break; - } - - switch (attributes & USB_ENDPOINT_XFERTYPE_MASK) { - case USB_ENDPOINT_XFER_CONTROL: - case USB_ENDPOINT_XFER_BULK: - case USB_ENDPOINT_XFER_INT: - default: - /* A non-isochronous endpoint may optionally be - * double-buffered. For now we disable - * double-buffering. - */ - ep_doublebuffer = 0; - ep_isoc = 0; - if (packet_size > 64) - packet_size = 0; - if (!ep || !ep_doublebuffer) - buffer_size = packet_size; - else - buffer_size = packet_size * 2; - break; - case USB_ENDPOINT_XFER_ISOC: - /* Isochronous endpoints are always double- - * buffered, but the double-buffering bit - * in the endpoint configuration register - * becomes the msb of the endpoint size so we - * set the double-buffering flag to zero. - */ - ep_doublebuffer = 0; - ep_isoc = 1; - buffer_size = packet_size * 2; - break; - } - - /* check to see if our packet buffer RAM is exhausted */ - if ((ep_ptr + buffer_size) > 2048) { - UDCDBGA ("out of packet RAM for ep 0x%02x buf size %d", ep_addr, buffer_size); - buffer_size = packet_size = 0; - } - - /* force a default configuration for endpoint 0 since it is - * always enabled - */ - if (!ep && ((packet_size < 8) || (packet_size > 64))) { - buffer_size = packet_size = 64; - ep_size = 3; - } - - if (!ep) { - /* configure endpoint 0 */ - outw ((ep_size << 12) | (ep_ptr >> 3), UDC_EP0); - /*UDCDBGA("ep 0 buffer offset 0x%03x packet size 0x%03x", */ - /* ep_ptr, packet_size); */ - } else if ((ep_addr & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN) { - /* IN endpoint */ - if (packet_size) { - outw ((1 << 15) | (ep_doublebuffer << 14) | - (ep_size << 12) | (ep_isoc << 11) | - (ep_ptr >> 3), - UDC_EP_TX (ep_addr & - USB_ENDPOINT_NUMBER_MASK)); - UDCDBGA ("IN ep %d buffer offset 0x%03x" - " packet size 0x%03x", - ep_addr & USB_ENDPOINT_NUMBER_MASK, - ep_ptr, packet_size); - } else { - outw (0, - UDC_EP_TX (ep_addr & - USB_ENDPOINT_NUMBER_MASK)); - } - } else { - /* OUT endpoint */ - if (packet_size) { - outw ((1 << 15) | (ep_doublebuffer << 14) | - (ep_size << 12) | (ep_isoc << 11) | - (ep_ptr >> 3), - UDC_EP_RX (ep_addr & - USB_ENDPOINT_NUMBER_MASK)); - UDCDBGA ("OUT ep %d buffer offset 0x%03x" - " packet size 0x%03x", - ep_addr & USB_ENDPOINT_NUMBER_MASK, - ep_ptr, packet_size); - } else { - outw (0, - UDC_EP_RX (ep_addr & - USB_ENDPOINT_NUMBER_MASK)); - } - } - ep_ptr += buffer_size; - } -} - -/* omap1510_deconfigure_device - * - * This function balances omap1510_configure_device. - */ -static void omap1510_deconfigure_device (void) -{ - int epnum; - - UDCDBG ("clear Cfg_Lock"); - outw (inw (UDC_SYSCON1) & ~UDC_Cfg_Lock, UDC_SYSCON1); - UDCREG (UDC_SYSCON1); - - /* deconfigure all endpoints */ - for (epnum = 1; epnum <= 15; epnum++) { - outw (0, UDC_EP_RX (epnum)); - outw (0, UDC_EP_TX (epnum)); - } -} - -/* omap1510_configure_device - * - * This function implements TRM Figure 14-9. - */ -static void omap1510_configure_device (struct usb_device_instance *device) -{ - omap1510_configure_endpoints (device); - - - /* Figure 14-9 indicates we should enable interrupts here, but we have - * other routines (udc_all_interrupts, udc_suspended_interrupts) to - * do that. - */ - - UDCDBG ("set Cfg_Lock"); - outw (inw (UDC_SYSCON1) | UDC_Cfg_Lock, UDC_SYSCON1); - UDCREG (UDC_SYSCON1); -} - -/* omap1510_write_noniso_tx_fifo - * - * This function implements TRM Figure 14-30. - * - * If the endpoint has an active tx_urb, then the next packet of data from the - * URB is written to the tx FIFO. The total amount of data in the urb is given - * by urb->actual_length. The maximum amount of data that can be sent in any - * one packet is given by endpoint->tx_packetSize. The number of data bytes - * from this URB that have already been transmitted is given by endpoint->sent. - * endpoint->last is updated by this routine with the number of data bytes - * transmitted in this packet. - * - * In accordance with Figure 14-30, the EP_NUM register must already have been - * written with the value to select the appropriate tx FIFO before this routine - * is called. - */ -static void omap1510_write_noniso_tx_fifo (struct usb_endpoint_instance - *endpoint) -{ - struct urb *urb = endpoint->tx_urb; - - if (urb) { - unsigned int last, i; - - UDCDBGA ("urb->buffer %p, buffer_length %d, actual_length %d", - urb->buffer, urb->buffer_length, urb->actual_length); - if ((last = - MIN (urb->actual_length - endpoint->sent, - endpoint->tx_packetSize))) { - u8 *cp = urb->buffer + endpoint->sent; - - UDCDBGA ("endpoint->sent %d, tx_packetSize %d, last %d", endpoint->sent, endpoint->tx_packetSize, last); - - if (((u32) cp & 1) == 0) { /* word aligned? */ - outsw (UDC_DATA, cp, last >> 1); - } else { /* byte aligned. */ - for (i = 0; i < (last >> 1); i++) { - u16 w = ((u16) cp[2 * i + 1] << 8) | - (u16) cp[2 * i]; - outw (w, UDC_DATA); - } - } - if (last & 1) { - outb (*(cp + last - 1), UDC_DATA); - } - } - endpoint->last = last; - } -} - -/* omap1510_read_noniso_rx_fifo - * - * This function implements TRM Figure 14-28. - * - * If the endpoint has an active rcv_urb, then the next packet of data is read - * from the rcv FIFO and written to rcv_urb->buffer at offset - * rcv_urb->actual_length to append the packet data to the data from any - * previous packets for this transfer. We assume that there is sufficient room - * left in the buffer to hold an entire packet of data. - * - * The return value is the number of bytes read from the FIFO for this packet. - * - * In accordance with Figure 14-28, the EP_NUM register must already have been - * written with the value to select the appropriate rcv FIFO before this routine - * is called. - */ -static int omap1510_read_noniso_rx_fifo (struct usb_endpoint_instance - *endpoint) -{ - struct urb *urb = endpoint->rcv_urb; - int len = 0; - - if (urb) { - len = inw (UDC_RXFSTAT); - - if (len) { - unsigned char *cp = urb->buffer + urb->actual_length; - - insw (UDC_DATA, cp, len >> 1); - if (len & 1) - *(cp + len - 1) = inb (UDC_DATA); - } - } - return len; -} - -/* omap1510_prepare_for_control_write_status - * - * This function implements TRM Figure 14-17. - * - * We have to deal here with non-autodecoded control writes that haven't already - * been dealt with by ep0_recv_setup. The non-autodecoded standard control - * write requests are: set/clear endpoint feature, set configuration, set - * interface, and set descriptor. ep0_recv_setup handles set/clear requests for - * ENDPOINT_HALT by halting the endpoint for a set request and resetting the - * endpoint for a clear request. ep0_recv_setup returns an error for - * SET_DESCRIPTOR requests which causes them to be terminated with a stall by - * the setup handler. A SET_INTERFACE request is handled by ep0_recv_setup by - * generating a DEVICE_SET_INTERFACE event. This leaves only the - * SET_CONFIGURATION event for us to deal with here. - * - */ -static void omap1510_prepare_for_control_write_status (struct urb *urb) -{ - struct usb_device_request *request = &urb->device_request;; - - /* check for a SET_CONFIGURATION request */ - if (request->bRequest == USB_REQ_SET_CONFIGURATION) { - int configuration = le16_to_cpu (request->wValue) & 0xff; - unsigned short devstat = inw (UDC_DEVSTAT); - - if ((devstat & (UDC_ADD | UDC_CFG)) == UDC_ADD) { - /* device is currently in ADDRESSED state */ - if (configuration) { - /* Assume the specified non-zero configuration - * value is valid and switch to the CONFIGURED - * state. - */ - outw (UDC_Dev_Cfg, UDC_SYSCON2); - } - } else if ((devstat & UDC_CFG) == UDC_CFG) { - /* device is currently in CONFIGURED state */ - if (!configuration) { - /* Switch to ADDRESSED state. */ - outw (UDC_Clr_Cfg, UDC_SYSCON2); - } - } - } - - /* select EP0 tx FIFO */ - outw (UDC_EP_Dir | UDC_EP_Sel, UDC_EP_NUM); - /* clear endpoint (no data bytes in status stage) */ - outw (UDC_Clr_EP, UDC_CTRL); - /* enable the EP0 tx FIFO */ - outw (UDC_Set_FIFO_En, UDC_CTRL); - /* deselect the endpoint */ - outw (UDC_EP_Dir, UDC_EP_NUM); -} - -/* udc_state_transition_up - * udc_state_transition_down - * - * Helper functions to implement device state changes. The device states and - * the events that transition between them are: - * - * STATE_ATTACHED - * || /\ - * \/ || - * DEVICE_HUB_CONFIGURED DEVICE_HUB_RESET - * || /\ - * \/ || - * STATE_POWERED - * || /\ - * \/ || - * DEVICE_RESET DEVICE_POWER_INTERRUPTION - * || /\ - * \/ || - * STATE_DEFAULT - * || /\ - * \/ || - * DEVICE_ADDRESS_ASSIGNED DEVICE_RESET - * || /\ - * \/ || - * STATE_ADDRESSED - * || /\ - * \/ || - * DEVICE_CONFIGURED DEVICE_DE_CONFIGURED - * || /\ - * \/ || - * STATE_CONFIGURED - * - * udc_state_transition_up transitions up (in the direction from STATE_ATTACHED - * to STATE_CONFIGURED) from the specified initial state to the specified final - * state, passing through each intermediate state on the way. If the initial - * state is at or above (i.e. nearer to STATE_CONFIGURED) the final state, then - * no state transitions will take place. - * - * udc_state_transition_down transitions down (in the direction from - * STATE_CONFIGURED to STATE_ATTACHED) from the specified initial state to the - * specified final state, passing through each intermediate state on the way. - * If the initial state is at or below (i.e. nearer to STATE_ATTACHED) the final - * state, then no state transitions will take place. - * - * These functions must only be called with interrupts disabled. - */ -static void udc_state_transition_up (usb_device_state_t initial, - usb_device_state_t final) -{ - if (initial < final) { - switch (initial) { - case STATE_ATTACHED: - usbd_device_event_irq (udc_device, - DEVICE_HUB_CONFIGURED, 0); - if (final == STATE_POWERED) - break; - case STATE_POWERED: - usbd_device_event_irq (udc_device, DEVICE_RESET, 0); - if (final == STATE_DEFAULT) - break; - case STATE_DEFAULT: - usbd_device_event_irq (udc_device, - DEVICE_ADDRESS_ASSIGNED, 0); - if (final == STATE_ADDRESSED) - break; - case STATE_ADDRESSED: - usbd_device_event_irq (udc_device, DEVICE_CONFIGURED, - 0); - case STATE_CONFIGURED: - break; - default: - break; - } - } -} - -static void udc_state_transition_down (usb_device_state_t initial, - usb_device_state_t final) -{ - if (initial > final) { - switch (initial) { - case STATE_CONFIGURED: - usbd_device_event_irq (udc_device, DEVICE_DE_CONFIGURED, 0); - if (final == STATE_ADDRESSED) - break; - case STATE_ADDRESSED: - usbd_device_event_irq (udc_device, DEVICE_RESET, 0); - if (final == STATE_DEFAULT) - break; - case STATE_DEFAULT: - usbd_device_event_irq (udc_device, DEVICE_POWER_INTERRUPTION, 0); - if (final == STATE_POWERED) - break; - case STATE_POWERED: - usbd_device_event_irq (udc_device, DEVICE_HUB_RESET, 0); - case STATE_ATTACHED: - break; - default: - break; - } - } -} - -/* Handle all device state changes. - * This function implements TRM Figure 14-21. - */ -static void omap1510_udc_state_changed (void) -{ - u16 bits; - u16 devstat = inw (UDC_DEVSTAT); - - UDCDBGA ("state changed, devstat %x, old %x", devstat, udc_devstat); - - bits = devstat ^ udc_devstat; - if (bits) { - if (bits & UDC_ATT) { - if (devstat & UDC_ATT) { - UDCDBG ("device attached and powered"); - udc_state_transition_up (udc_device->device_state, STATE_POWERED); - } else { - UDCDBG ("device detached or unpowered"); - udc_state_transition_down (udc_device->device_state, STATE_ATTACHED); - } - } - if (bits & UDC_USB_Reset) { - if (devstat & UDC_USB_Reset) { - UDCDBG ("device reset in progess"); - udc_state_transition_down (udc_device->device_state, STATE_POWERED); - } else { - UDCDBG ("device reset completed"); - } - } - if (bits & UDC_DEF) { - if (devstat & UDC_DEF) { - UDCDBG ("device entering default state"); - udc_state_transition_up (udc_device->device_state, STATE_DEFAULT); - } else { - UDCDBG ("device leaving default state"); - udc_state_transition_down (udc_device->device_state, STATE_POWERED); - } - } - if (bits & UDC_SUS) { - if (devstat & UDC_SUS) { - UDCDBG ("entering suspended state"); - usbd_device_event_irq (udc_device, DEVICE_BUS_INACTIVE, 0); - } else { - UDCDBG ("leaving suspended state"); - usbd_device_event_irq (udc_device, DEVICE_BUS_ACTIVITY, 0); - } - } - if (bits & UDC_R_WK_OK) { - UDCDBGA ("remote wakeup %s", (devstat & UDC_R_WK_OK) - ? "enabled" : "disabled"); - } - if (bits & UDC_ADD) { - if (devstat & UDC_ADD) { - UDCDBG ("default -> addressed"); - udc_state_transition_up (udc_device->device_state, STATE_ADDRESSED); - } else { - UDCDBG ("addressed -> default"); - udc_state_transition_down (udc_device->device_state, STATE_DEFAULT); - } - } - if (bits & UDC_CFG) { - if (devstat & UDC_CFG) { - UDCDBG ("device configured"); - /* The ep0_recv_setup function generates the - * DEVICE_CONFIGURED event when a - * USB_REQ_SET_CONFIGURATION setup packet is - * received, so we should already be in the - * state STATE_CONFIGURED. - */ - udc_state_transition_up (udc_device->device_state, STATE_CONFIGURED); - } else { - UDCDBG ("device deconfigured"); - udc_state_transition_down (udc_device->device_state, STATE_ADDRESSED); - } - } - } - - /* Clear interrupt source */ - outw (UDC_DS_Chg, UDC_IRQ_SRC); - - /* Save current DEVSTAT */ - udc_devstat = devstat; -} - -/* Handle SETUP USB interrupt. - * This function implements TRM Figure 14-14. - */ -static void omap1510_udc_setup (struct usb_endpoint_instance *endpoint) -{ - UDCDBG ("-> Entering device setup"); - - do { - const int setup_pktsize = 8; - unsigned char *datap = - (unsigned char *) &ep0_urb->device_request; - - /* Gain access to EP 0 setup FIFO */ - outw (UDC_Setup_Sel, UDC_EP_NUM); - - /* Read control request data */ - insb (UDC_DATA, datap, setup_pktsize); - - UDCDBGA ("EP0 setup read [%x %x %x %x %x %x %x %x]", - *(datap + 0), *(datap + 1), *(datap + 2), - *(datap + 3), *(datap + 4), *(datap + 5), - *(datap + 6), *(datap + 7)); - - /* Reset EP0 setup FIFO */ - outw (0, UDC_EP_NUM); - } while (inw (UDC_IRQ_SRC) & UDC_Setup); - - /* Try to process setup packet */ - if (ep0_recv_setup (ep0_urb)) { - /* Not a setup packet, stall next EP0 transaction */ - udc_stall_ep (0); - UDCDBG ("can't parse setup packet, still waiting for setup"); - return; - } - - /* Check direction */ - if ((ep0_urb->device_request.bmRequestType & USB_REQ_DIRECTION_MASK) - == USB_REQ_HOST2DEVICE) { - UDCDBG ("control write on EP0"); - if (le16_to_cpu (ep0_urb->device_request.wLength)) { - /* We don't support control write data stages. - * The only standard control write request with a data - * stage is SET_DESCRIPTOR, and ep0_recv_setup doesn't - * support that so we just stall those requests. A - * function driver might support a non-standard - * write request with a data stage, but it isn't - * obvious what we would do with the data if we read it - * so we'll just stall it. It seems like the API isn't - * quite right here. - */ -#if 0 - /* Here is what we would do if we did support control - * write data stages. - */ - ep0_urb->actual_length = 0; - outw (0, UDC_EP_NUM); - /* enable the EP0 rx FIFO */ - outw (UDC_Set_FIFO_En, UDC_CTRL); -#else - /* Stall this request */ - UDCDBG ("Stalling unsupported EP0 control write data " - "stage."); - udc_stall_ep (0); -#endif - } else { - omap1510_prepare_for_control_write_status (ep0_urb); - } - } else { - UDCDBG ("control read on EP0"); - /* The ep0_recv_setup function has already placed our response - * packet data in ep0_urb->buffer and the packet length in - * ep0_urb->actual_length. - */ - endpoint->tx_urb = ep0_urb; - endpoint->sent = 0; - /* select the EP0 tx FIFO */ - outw (UDC_EP_Dir | UDC_EP_Sel, UDC_EP_NUM); - /* Write packet data to the FIFO. omap1510_write_noniso_tx_fifo - * will update endpoint->last with the number of bytes written - * to the FIFO. - */ - omap1510_write_noniso_tx_fifo (endpoint); - /* enable the FIFO to start the packet transmission */ - outw (UDC_Set_FIFO_En, UDC_CTRL); - /* deselect the EP0 tx FIFO */ - outw (UDC_EP_Dir, UDC_EP_NUM); - } - - UDCDBG ("<- Leaving device setup"); -} - -/* Handle endpoint 0 RX interrupt - * This routine implements TRM Figure 14-16. - */ -static void omap1510_udc_ep0_rx (struct usb_endpoint_instance *endpoint) -{ - unsigned short status; - - UDCDBG ("RX on EP0"); - /* select EP0 rx FIFO */ - outw (UDC_EP_Sel, UDC_EP_NUM); - - status = inw (UDC_STAT_FLG); - - if (status & UDC_ACK) { - /* Check direction */ - if ((ep0_urb->device_request.bmRequestType - & USB_REQ_DIRECTION_MASK) == USB_REQ_HOST2DEVICE) { - /* This rx interrupt must be for a control write data - * stage packet. - * - * We don't support control write data stages. - * We should never end up here. - */ - - /* clear the EP0 rx FIFO */ - outw (UDC_Clr_EP, UDC_CTRL); - - /* deselect the EP0 rx FIFO */ - outw (0, UDC_EP_NUM); - - UDCDBG ("Stalling unexpected EP0 control write " - "data stage packet"); - udc_stall_ep (0); - } else { - /* This rx interrupt must be for a control read status - * stage packet. - */ - UDCDBG ("ACK on EP0 control read status stage packet"); - /* deselect EP0 rx FIFO */ - outw (0, UDC_EP_NUM); - } - } else if (status & UDC_STALL) { - UDCDBG ("EP0 stall during RX"); - /* deselect EP0 rx FIFO */ - outw (0, UDC_EP_NUM); - } else { - /* deselect EP0 rx FIFO */ - outw (0, UDC_EP_NUM); - } -} - -/* Handle endpoint 0 TX interrupt - * This routine implements TRM Figure 14-18. - */ -static void omap1510_udc_ep0_tx (struct usb_endpoint_instance *endpoint) -{ - unsigned short status; - struct usb_device_request *request = &ep0_urb->device_request; - - UDCDBG ("TX on EP0"); - /* select EP0 TX FIFO */ - outw (UDC_EP_Dir | UDC_EP_Sel, UDC_EP_NUM); - - status = inw (UDC_STAT_FLG); - if (status & UDC_ACK) { - /* Check direction */ - if ((request->bmRequestType & USB_REQ_DIRECTION_MASK) == - USB_REQ_HOST2DEVICE) { - /* This tx interrupt must be for a control write status - * stage packet. - */ - UDCDBG ("ACK on EP0 control write status stage packet"); - /* deselect EP0 TX FIFO */ - outw (UDC_EP_Dir, UDC_EP_NUM); - } else { - /* This tx interrupt must be for a control read data - * stage packet. - */ - int wLength = le16_to_cpu (request->wLength); - - /* Update our count of bytes sent so far in this - * transfer. - */ - endpoint->sent += endpoint->last; - - /* We are finished with this transfer if we have sent - * all of the bytes in our tx urb (urb->actual_length) - * unless we need a zero-length terminating packet. We - * need a zero-length terminating packet if we returned - * fewer bytes than were requested (wLength) by the host, - * and the number of bytes we returned is an exact - * multiple of the packet size endpoint->tx_packetSize. - */ - if ((endpoint->sent == ep0_urb->actual_length) - && ((ep0_urb->actual_length == wLength) - || (endpoint->last != - endpoint->tx_packetSize))) { - /* Done with control read data stage. */ - UDCDBG ("control read data stage complete"); - /* deselect EP0 TX FIFO */ - outw (UDC_EP_Dir, UDC_EP_NUM); - /* select EP0 RX FIFO to prepare for control - * read status stage. - */ - outw (UDC_EP_Sel, UDC_EP_NUM); - /* clear the EP0 RX FIFO */ - outw (UDC_Clr_EP, UDC_CTRL); - /* enable the EP0 RX FIFO */ - outw (UDC_Set_FIFO_En, UDC_CTRL); - /* deselect the EP0 RX FIFO */ - outw (0, UDC_EP_NUM); - } else { - /* We still have another packet of data to send - * in this control read data stage or else we - * need a zero-length terminating packet. - */ - UDCDBG ("ACK control read data stage packet"); - omap1510_write_noniso_tx_fifo (endpoint); - /* enable the EP0 tx FIFO to start transmission */ - outw (UDC_Set_FIFO_En, UDC_CTRL); - /* deselect EP0 TX FIFO */ - outw (UDC_EP_Dir, UDC_EP_NUM); - } - } - } else if (status & UDC_STALL) { - UDCDBG ("EP0 stall during TX"); - /* deselect EP0 TX FIFO */ - outw (UDC_EP_Dir, UDC_EP_NUM); - } else { - /* deselect EP0 TX FIFO */ - outw (UDC_EP_Dir, UDC_EP_NUM); - } -} - -/* Handle RX transaction on non-ISO endpoint. - * This function implements TRM Figure 14-27. - * The ep argument is a physical endpoint number for a non-ISO OUT endpoint - * in the range 1 to 15. - */ -static void omap1510_udc_epn_rx (int ep) -{ - unsigned short status; - - /* Check endpoint status */ - status = inw (UDC_STAT_FLG); - - if (status & UDC_ACK) { - int nbytes; - struct usb_endpoint_instance *endpoint = - omap1510_find_ep (ep); - - nbytes = omap1510_read_noniso_rx_fifo (endpoint); - usbd_rcv_complete (endpoint, nbytes, 0); - - /* enable rx FIFO to prepare for next packet */ - outw (UDC_Set_FIFO_En, UDC_CTRL); - } else if (status & UDC_STALL) { - UDCDBGA ("STALL on RX endpoint %d", ep); - } else if (status & UDC_NAK) { - UDCDBGA ("NAK on RX ep %d", ep); - } else { - serial_printf ("omap-bi: RX on ep %d with status %x", ep, - status); - } -} - -/* Handle TX transaction on non-ISO endpoint. - * This function implements TRM Figure 14-29. - * The ep argument is a physical endpoint number for a non-ISO IN endpoint - * in the range 16 to 30. - */ -static void omap1510_udc_epn_tx (int ep) -{ - unsigned short status; - - /*serial_printf("omap1510_udc_epn_tx( %x )\n",ep); */ - - /* Check endpoint status */ - status = inw (UDC_STAT_FLG); - - if (status & UDC_ACK) { - struct usb_endpoint_instance *endpoint = - omap1510_find_ep (ep); - - /* We need to transmit a terminating zero-length packet now if - * we have sent all of the data in this URB and the transfer - * size was an exact multiple of the packet size. - */ - if (endpoint->tx_urb - && (endpoint->last == endpoint->tx_packetSize) - && (endpoint->tx_urb->actual_length - endpoint->sent - - endpoint->last == 0)) { - /* Prepare to transmit a zero-length packet. */ - endpoint->sent += endpoint->last; - /* write 0 bytes of data to FIFO */ - omap1510_write_noniso_tx_fifo (endpoint); - /* enable tx FIFO to start transmission */ - outw (UDC_Set_FIFO_En, UDC_CTRL); - } else if (endpoint->tx_urb - && endpoint->tx_urb->actual_length) { - /* retire the data that was just sent */ - usbd_tx_complete (endpoint); - /* Check to see if we have more data ready to transmit - * now. - */ - if (endpoint->tx_urb - && endpoint->tx_urb->actual_length) { - /* write data to FIFO */ - omap1510_write_noniso_tx_fifo (endpoint); - /* enable tx FIFO to start transmission */ - outw (UDC_Set_FIFO_En, UDC_CTRL); - } - } - } else if (status & UDC_STALL) { - UDCDBGA ("STALL on TX endpoint %d", ep); - } else if (status & UDC_NAK) { - UDCDBGA ("NAK on TX endpoint %d", ep); - } else { - /*serial_printf("omap-bi: TX on ep %d with status %x\n", ep, status); */ - } -} - - -/* -------------------------------------------------------------------------------- -*/ - -/* Handle general USB interrupts and dispatch according to type. - * This function implements TRM Figure 14-13. - */ -void omap1510_udc_irq (void) -{ - u16 irq_src = inw (UDC_IRQ_SRC); - int valid_irq = 0; - - if (!(irq_src & ~UDC_SOF_Flg)) /* ignore SOF interrupts ) */ - return; - - UDCDBGA ("< IRQ #%d start >- %x", udc_interrupts, irq_src); - /*serial_printf("< IRQ #%d start >- %x\n", udc_interrupts, irq_src); */ - - if (irq_src & UDC_DS_Chg) { - /* Device status changed */ - omap1510_udc_state_changed (); - valid_irq++; - } - if (irq_src & UDC_EP0_RX) { - /* Endpoint 0 receive */ - outw (UDC_EP0_RX, UDC_IRQ_SRC); /* ack interrupt */ - omap1510_udc_ep0_rx (udc_device->bus->endpoint_array + 0); - valid_irq++; - } - if (irq_src & UDC_EP0_TX) { - /* Endpoint 0 transmit */ - outw (UDC_EP0_TX, UDC_IRQ_SRC); /* ack interrupt */ - omap1510_udc_ep0_tx (udc_device->bus->endpoint_array + 0); - valid_irq++; - } - if (irq_src & UDC_Setup) { - /* Device setup */ - omap1510_udc_setup (udc_device->bus->endpoint_array + 0); - valid_irq++; - } - /*if (!valid_irq) */ - /* serial_printf("unknown interrupt, IRQ_SRC %.4x\n", irq_src); */ - UDCDBGA ("< IRQ #%d end >", udc_interrupts); - udc_interrupts++; -} - -/* This function implements TRM Figure 14-26. */ -void omap1510_udc_noniso_irq (void) -{ - unsigned short epnum; - unsigned short irq_src = inw (UDC_IRQ_SRC); - int valid_irq = 0; - - if (!(irq_src & (UDC_EPn_RX | UDC_EPn_TX))) - return; - - UDCDBGA ("non-ISO IRQ, IRQ_SRC %x", inw (UDC_IRQ_SRC)); - - if (irq_src & UDC_EPn_RX) { /* Endpoint N OUT transaction */ - /* Determine the endpoint number for this interrupt */ - epnum = (inw (UDC_EPN_STAT) & 0x0f00) >> 8; - UDCDBGA ("RX on ep %x", epnum); - - /* acknowledge interrupt */ - outw (UDC_EPn_RX, UDC_IRQ_SRC); - - if (epnum) { - /* select the endpoint FIFO */ - outw (UDC_EP_Sel | epnum, UDC_EP_NUM); - - omap1510_udc_epn_rx (epnum); - - /* deselect the endpoint FIFO */ - outw (epnum, UDC_EP_NUM); - } - valid_irq++; - } - if (irq_src & UDC_EPn_TX) { /* Endpoint N IN transaction */ - /* Determine the endpoint number for this interrupt */ - epnum = (inw (UDC_EPN_STAT) & 0x000f) | USB_DIR_IN; - UDCDBGA ("TX on ep %x", epnum); - - /* acknowledge interrupt */ - outw (UDC_EPn_TX, UDC_IRQ_SRC); - - if (epnum) { - /* select the endpoint FIFO */ - outw (UDC_EP_Sel | UDC_EP_Dir | epnum, UDC_EP_NUM); - - omap1510_udc_epn_tx (epnum); - - /* deselect the endpoint FIFO */ - outw (UDC_EP_Dir | epnum, UDC_EP_NUM); - } - valid_irq++; - } - if (!valid_irq) - serial_printf (": unknown non-ISO interrupt, IRQ_SRC %.4x\n", - irq_src); -} - -/* -------------------------------------------------------------------------------- -*/ - - -/* - * Start of public functions. - */ - -/* Called to start packet transmission. */ -int udc_endpoint_write (struct usb_endpoint_instance *endpoint) -{ - unsigned short epnum = - endpoint->endpoint_address & USB_ENDPOINT_NUMBER_MASK; - - UDCDBGA ("Starting transmit on ep %x", epnum); - - if (endpoint->tx_urb) { - /* select the endpoint FIFO */ - outw (UDC_EP_Sel | UDC_EP_Dir | epnum, UDC_EP_NUM); - /* write data to FIFO */ - omap1510_write_noniso_tx_fifo (endpoint); - /* enable tx FIFO to start transmission */ - outw (UDC_Set_FIFO_En, UDC_CTRL); - /* deselect the endpoint FIFO */ - outw (UDC_EP_Dir | epnum, UDC_EP_NUM); - } - - return 0; -} - -/* Start to initialize h/w stuff */ -int udc_init (void) -{ - u16 udc_rev; - uchar value; - ulong gpio; - int i; - - /* Let the device settle down before we start */ - for (i = 0; i < UDC_INIT_MDELAY; i++) udelay(1000); - - udc_device = NULL; - - UDCDBG ("starting"); - - /* Check peripheral reset. Must be 1 to make sure - MPU TIPB peripheral reset is inactive */ - UDCREG (ARM_RSTCT2); - - /* Set and check clock control. - * We might ought to be using the clock control API to do - * this instead of fiddling with the clock registers directly - * here. - */ - outw ((1 << 4) | (1 << 5), CLOCK_CTRL); - UDCREG (CLOCK_CTRL); - -#ifdef CONFIG_OMAP1510 - /* This code was originally implemented for OMAP1510 and - * therefore is only applicable for OMAP1510 boards. For - * OMAP5912 or OMAP16xx the register APLL_CTRL does not - * exist and DPLL_CTRL is already configured. - */ - - /* Set and check APLL */ - outw (0x0008, APLL_CTRL); - UDCREG (APLL_CTRL); - /* Set and check DPLL */ - outw (0x2210, DPLL_CTRL); - UDCREG (DPLL_CTRL); -#endif - /* Set and check SOFT - * The below line of code has been changed to perform a - * read-modify-write instead of a simple write for - * configuring the SOFT_REQ register. This allows the code - * to be compatible with OMAP5912 and OMAP16xx devices - */ - outw ((1 << 4) | (1 << 3) | 1 | (inw(SOFT_REQ)), SOFT_REQ); - - /* Short delay to wait for DPLL */ - udelay (1000); - - /* Print banner with device revision */ - udc_rev = inw (UDC_REV) & 0xff; -#ifdef CONFIG_OMAP1510 - printf ("USB: TI OMAP1510 USB function module rev %d.%d\n", - udc_rev >> 4, udc_rev & 0xf); -#endif - -#ifdef CONFIG_OMAP1610 - printf ("USB: TI OMAP5912 USB function module rev %d.%d\n", - udc_rev >> 4, udc_rev & 0xf); -#endif - -#ifdef CONFIG_OMAP_SX1 - i2c_read (0x32, 0x04, 1, &value, 1); - value |= 0x04; - i2c_write (0x32, 0x04, 1, &value, 1); - - i2c_read (0x32, 0x03, 1, &value, 1); - value |= 0x01; - i2c_write (0x32, 0x03, 1, &value, 1); - - gpio = inl(GPIO_PIN_CONTROL_REG); - gpio |= 0x0002; /* A_IRDA_OFF */ - gpio |= 0x0800; /* A_SWITCH */ - gpio |= 0x8000; /* A_USB_ON */ - outl (gpio, GPIO_PIN_CONTROL_REG); - - gpio = inl(GPIO_DIR_CONTROL_REG); - gpio &= ~0x0002; /* A_IRDA_OFF */ - gpio &= ~0x0800; /* A_SWITCH */ - gpio &= ~0x8000; /* A_USB_ON */ - outl (gpio, GPIO_DIR_CONTROL_REG); - - gpio = inl(GPIO_DATA_OUTPUT_REG); - gpio |= 0x0002; /* A_IRDA_OFF */ - gpio &= ~0x0800; /* A_SWITCH */ - gpio &= ~0x8000; /* A_USB_ON */ - outl (gpio, GPIO_DATA_OUTPUT_REG); -#endif - - /* The VBUS_MODE bit selects whether VBUS detection is done via - * software (1) or hardware (0). When software detection is - * selected, VBUS_CTRL selects whether USB is not connected (0) - * or connected (1). - */ - outl (inl (FUNC_MUX_CTRL_0) | UDC_VBUS_MODE, FUNC_MUX_CTRL_0); - outl (inl (FUNC_MUX_CTRL_0) & ~UDC_VBUS_CTRL, FUNC_MUX_CTRL_0); - UDCREGL (FUNC_MUX_CTRL_0); - - /* - * At this point, device is ready for configuration... - */ - - UDCDBG ("disable USB interrupts"); - outw (0, UDC_IRQ_EN); - UDCREG (UDC_IRQ_EN); - - UDCDBG ("disable USB DMA"); - outw (0, UDC_DMA_IRQ_EN); - UDCREG (UDC_DMA_IRQ_EN); - - UDCDBG ("initialize SYSCON1"); - outw (UDC_Self_Pwr | UDC_Pullup_En, UDC_SYSCON1); - UDCREG (UDC_SYSCON1); - - return 0; -} - -/* Stall endpoint */ -static void udc_stall_ep (unsigned int ep_addr) -{ - /*int ep_addr = PHYS_EP_TO_EP_ADDR(ep); */ - int ep_num = ep_addr & USB_ENDPOINT_NUMBER_MASK; - - UDCDBGA ("stall ep_addr %d", ep_addr); - - /* REVISIT? - * The OMAP TRM section 14.2.4.2 says we must check that the FIFO - * is empty before halting the endpoint. The current implementation - * doesn't check that the FIFO is empty. - */ - - if (!ep_num) { - outw (UDC_Stall_Cmd, UDC_SYSCON2); - } else if ((ep_addr & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT) { - if (inw (UDC_EP_RX (ep_num)) & UDC_EPn_RX_Valid) { - /* we have a valid rx endpoint, so halt it */ - outw (UDC_EP_Sel | ep_num, UDC_EP_NUM); - outw (UDC_Set_Halt, UDC_CTRL); - outw (ep_num, UDC_EP_NUM); - } - } else { - if (inw (UDC_EP_TX (ep_num)) & UDC_EPn_TX_Valid) { - /* we have a valid tx endpoint, so halt it */ - outw (UDC_EP_Sel | UDC_EP_Dir | ep_num, UDC_EP_NUM); - outw (UDC_Set_Halt, UDC_CTRL); - outw (ep_num, UDC_EP_NUM); - } - } -} - -/* Reset endpoint */ -#if 0 -static void udc_reset_ep (unsigned int ep_addr) -{ - /*int ep_addr = PHYS_EP_TO_EP_ADDR(ep); */ - int ep_num = ep_addr & USB_ENDPOINT_NUMBER_MASK; - - UDCDBGA ("reset ep_addr %d", ep_addr); - - if (!ep_num) { - /* control endpoint 0 can't be reset */ - } else if ((ep_addr & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT) { - UDCDBGA ("UDC_EP_RX(%d) = 0x%04x", ep_num, - inw (UDC_EP_RX (ep_num))); - if (inw (UDC_EP_RX (ep_num)) & UDC_EPn_RX_Valid) { - /* we have a valid rx endpoint, so reset it */ - outw (ep_num | UDC_EP_Sel, UDC_EP_NUM); - outw (UDC_Reset_EP, UDC_CTRL); - outw (ep_num, UDC_EP_NUM); - UDCDBGA ("OUT endpoint %d reset", ep_num); - } - } else { - UDCDBGA ("UDC_EP_TX(%d) = 0x%04x", ep_num, - inw (UDC_EP_TX (ep_num))); - /* Resetting of tx endpoints seems to be causing the USB function - * module to fail, which causes problems when the driver is - * uninstalled. We'll skip resetting tx endpoints for now until - * we figure out what the problem is. - */ -#if 0 - if (inw (UDC_EP_TX (ep_num)) & UDC_EPn_TX_Valid) { - /* we have a valid tx endpoint, so reset it */ - outw (ep_num | UDC_EP_Dir | UDC_EP_Sel, UDC_EP_NUM); - outw (UDC_Reset_EP, UDC_CTRL); - outw (ep_num | UDC_EP_Dir, UDC_EP_NUM); - UDCDBGA ("IN endpoint %d reset", ep_num); - } -#endif - } -} -#endif - -/* ************************************************************************** */ - -/** - * udc_check_ep - check logical endpoint - * - * Return physical endpoint number to use for this logical endpoint or zero if not valid. - */ -#if 0 -int udc_check_ep (int logical_endpoint, int packetsize) -{ - if ((logical_endpoint == 0x80) || - ((logical_endpoint & 0x8f) != logical_endpoint)) { - return 0; - } - - switch (packetsize) { - case 8: - case 16: - case 32: - case 64: - case 128: - case 256: - case 512: - break; - default: - return 0; - } - - return EP_ADDR_TO_PHYS_EP (logical_endpoint); -} -#endif - -/* - * udc_setup_ep - setup endpoint - * - * Associate a physical endpoint with endpoint_instance - */ -void udc_setup_ep (struct usb_device_instance *device, - unsigned int ep, struct usb_endpoint_instance *endpoint) -{ - UDCDBGA ("setting up endpoint addr %x", endpoint->endpoint_address); - - /* This routine gets called by bi_modinit for endpoint 0 and from - * bi_config for all of the other endpoints. bi_config gets called - * during the DEVICE_CREATE, DEVICE_CONFIGURED, and - * DEVICE_SET_INTERFACE events. We need to reconfigure the OMAP packet - * RAM after bi_config scans the selected device configuration and - * initializes the endpoint structures, but before this routine enables - * the OUT endpoint FIFOs. Since bi_config calls this routine in a - * loop for endpoints 1 through UDC_MAX_ENDPOINTS, we reconfigure our - * packet RAM here when ep==1. - * I really hate to do this here, but it seems like the API exported - * by the USB bus interface controller driver to the usbd-bi module - * isn't quite right so there is no good place to do this. - */ - if (ep == 1) { - omap1510_deconfigure_device (); - omap1510_configure_device (device); - } - - if (endpoint && (ep < UDC_MAX_ENDPOINTS)) { - int ep_addr = endpoint->endpoint_address; - - if (!ep_addr) { - /* nothing to do for endpoint 0 */ - } else if ((ep_addr & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN) { - /* nothing to do for IN (tx) endpoints */ - } else { /* OUT (rx) endpoint */ - if (endpoint->rcv_packetSize) { - /*struct urb* urb = &(urb_out_array[ep&0xFF]); */ - /*urb->endpoint = endpoint; */ - /*urb->device = device; */ - /*urb->buffer_length = sizeof(urb->buffer); */ - - /*endpoint->rcv_urb = urb; */ - omap1510_prepare_endpoint_for_rx (ep_addr); - } - } - } -} - -/** - * udc_disable_ep - disable endpoint - * @ep: - * - * Disable specified endpoint - */ -#if 0 -void udc_disable_ep (unsigned int ep_addr) -{ - /*int ep_addr = PHYS_EP_TO_EP_ADDR(ep); */ - int ep_num = ep_addr & USB_ENDPOINT_NUMBER_MASK; - struct usb_endpoint_instance *endpoint = omap1510_find_ep (ep_addr); /*udc_device->bus->endpoint_array + ep; */ - - UDCDBGA ("disable ep_addr %d", ep_addr); - - if (!ep_num) { - /* nothing to do for endpoint 0 */ ; - } else if ((ep_addr & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN) { - if (endpoint->tx_packetSize) { - /* we have a valid tx endpoint */ - /*usbd_flush_tx(endpoint); */ - endpoint->tx_urb = NULL; - } - } else { - if (endpoint->rcv_packetSize) { - /* we have a valid rx endpoint */ - /*usbd_flush_rcv(endpoint); */ - endpoint->rcv_urb = NULL; - } - } -} -#endif - -/* ************************************************************************** */ - -/** - * udc_connected - is the USB cable connected - * - * Return non-zero if cable is connected. - */ -#if 0 -int udc_connected (void) -{ - return ((inw (UDC_DEVSTAT) & UDC_ATT) == UDC_ATT); -} -#endif - -/* Turn on the USB connection by enabling the pullup resistor */ -void udc_connect (void) -{ - UDCDBG ("connect, enable Pullup"); - outl (0x00000018, FUNC_MUX_CTRL_D); -} - -/* Turn off the USB connection by disabling the pullup resistor */ -void udc_disconnect (void) -{ - UDCDBG ("disconnect, disable Pullup"); - outl (0x00000000, FUNC_MUX_CTRL_D); -} - -/* ************************************************************************** */ - - -/* - * udc_disable_interrupts - disable interrupts - * switch off interrupts - */ -#if 0 -void udc_disable_interrupts (struct usb_device_instance *device) -{ - UDCDBG ("disabling all interrupts"); - outw (0, UDC_IRQ_EN); -} -#endif - -/* ************************************************************************** */ - -/** - * udc_ep0_packetsize - return ep0 packetsize - */ -#if 0 -int udc_ep0_packetsize (void) -{ - return EP0_PACKETSIZE; -} -#endif - -/* Switch on the UDC */ -void udc_enable (struct usb_device_instance *device) -{ - UDCDBGA ("enable device %p, status %d", device, device->status); - - /* initialize driver state variables */ - udc_devstat = 0; - - /* Save the device structure pointer */ - udc_device = device; - - /* Setup ep0 urb */ - if (!ep0_urb) { - ep0_urb = - usbd_alloc_urb (udc_device, - udc_device->bus->endpoint_array); - } else { - serial_printf ("udc_enable: ep0_urb already allocated %p\n", - ep0_urb); - } - - UDCDBG ("Check clock status"); - UDCREG (STATUS_REQ); - - /* The VBUS_MODE bit selects whether VBUS detection is done via - * software (1) or hardware (0). When software detection is - * selected, VBUS_CTRL selects whether USB is not connected (0) - * or connected (1). - */ - outl (inl (FUNC_MUX_CTRL_0) | UDC_VBUS_CTRL | UDC_VBUS_MODE, - FUNC_MUX_CTRL_0); - UDCREGL (FUNC_MUX_CTRL_0); - - omap1510_configure_device (device); -} - -/* Switch off the UDC */ -void udc_disable (void) -{ - UDCDBG ("disable UDC"); - - omap1510_deconfigure_device (); - - /* The VBUS_MODE bit selects whether VBUS detection is done via - * software (1) or hardware (0). When software detection is - * selected, VBUS_CTRL selects whether USB is not connected (0) - * or connected (1). - */ - outl (inl (FUNC_MUX_CTRL_0) | UDC_VBUS_MODE, FUNC_MUX_CTRL_0); - outl (inl (FUNC_MUX_CTRL_0) & ~UDC_VBUS_CTRL, FUNC_MUX_CTRL_0); - UDCREGL (FUNC_MUX_CTRL_0); - - /* Free ep0 URB */ - if (ep0_urb) { - /*usbd_dealloc_urb(ep0_urb); */ - ep0_urb = NULL; - } - - /* Reset device pointer. - * We ought to do this here to balance the initialization of udc_device - * in udc_enable, but some of our other exported functions get called - * by the bus interface driver after udc_disable, so we have to hang on - * to the device pointer to avoid a null pointer dereference. */ - /* udc_device = NULL; */ -} - -/** - * udc_startup - allow udc code to do any additional startup - */ -void udc_startup_events (struct usb_device_instance *device) -{ - /* The DEVICE_INIT event puts the USB device in the state STATE_INIT. */ - usbd_device_event_irq (device, DEVICE_INIT, 0); - - /* The DEVICE_CREATE event puts the USB device in the state - * STATE_ATTACHED. - */ - usbd_device_event_irq (device, DEVICE_CREATE, 0); - - /* Some USB controller driver implementations signal - * DEVICE_HUB_CONFIGURED and DEVICE_RESET events here. - * DEVICE_HUB_CONFIGURED causes a transition to the state STATE_POWERED, - * and DEVICE_RESET causes a transition to the state STATE_DEFAULT. - * The OMAP USB client controller has the capability to detect when the - * USB cable is connected to a powered USB bus via the ATT bit in the - * DEVSTAT register, so we will defer the DEVICE_HUB_CONFIGURED and - * DEVICE_RESET events until later. - */ - - udc_enable (device); -} - -/** - * udc_irq - do pseudo interrupts - */ -void udc_irq(void) -{ - /* Loop while we have interrupts. - * If we don't do this, the input chain - * polling delay is likely to miss - * host requests. - */ - while (inw (UDC_IRQ_SRC) & ~UDC_SOF_Flg) { - /* Handle any new IRQs */ - omap1510_udc_irq (); - omap1510_udc_noniso_irq (); - } -} - -/* Flow control */ -void udc_set_nak(int epid) -{ - /* TODO: implement this functionality in omap1510 */ -} - -void udc_unset_nak (int epid) -{ - /* TODO: implement this functionality in omap1510 */ -} diff --git a/drivers/usb/gadget/storage_common.c b/drivers/usb/gadget/storage_common.c index 02803df..b55e40b 100644 --- a/drivers/usb/gadget/storage_common.c +++ b/drivers/usb/gadget/storage_common.c @@ -267,11 +267,6 @@ struct interrupt_data { #define ASCQ(x) ((u8) (x)) struct device_attribute { int i; }; -struct rw_semaphore { int i; }; -#define down_write(...) do { } while (0) -#define up_write(...) do { } while (0) -#define down_read(...) do { } while (0) -#define up_read(...) do { } while (0) #define ETOOSMALL 525 #include <usb_mass_storage.h> diff --git a/drivers/usb/host/ehci-rmobile.c b/drivers/usb/host/ehci-rmobile.c index 049e4c4..0d1a726 100644 --- a/drivers/usb/host/ehci-rmobile.c +++ b/drivers/usb/host/ehci-rmobile.c @@ -21,13 +21,16 @@ static u32 usb_base_address[CONFIG_USB_MAX_CONTROLLER_COUNT] = { 0xEE080000, /* USB0 (EHCI) */ 0xEE0A0000, /* USB1 */ 0xEE0C0000, /* USB2 */ - 0xEE000000 /* USB3 (USB3.0 Host)*/ }; #elif defined(CONFIG_R8A7791) static u32 usb_base_address[CONFIG_USB_MAX_CONTROLLER_COUNT] = { 0xEE080000, /* USB0 (EHCI) */ 0xEE0C0000, /* USB1 */ - 0xEE000000 /* USB3 (USB3.0 Host)*/ +}; +#elif defined(CONFIG_R8A7794) +static u32 usb_base_address[CONFIG_USB_MAX_CONTROLLER_COUNT] = { + 0xEE080000, /* USB0 (EHCI) */ + 0xEE0C0000, /* USB1 */ }; #else #error rmobile EHCI USB driver not supported on this platform diff --git a/drivers/usb/musb-new/linux-compat.h b/drivers/usb/musb-new/linux-compat.h index d7a5663..46f83d9 100644 --- a/drivers/usb/musb-new/linux-compat.h +++ b/drivers/usb/musb-new/linux-compat.h @@ -5,39 +5,6 @@ #include <linux/list.h> #include <linux/compat.h> -#define __init -#define __devinit -#define __devinitdata -#define __devinitconst -#define __iomem -#define __deprecated - -struct unused {}; -typedef struct unused unused_t; - -typedef int irqreturn_t; -typedef unused_t spinlock_t; - -struct work_struct {}; - -struct timer_list {}; -struct notifier_block {}; - -typedef unsigned long dmaaddr_t; - -#define spin_lock_init(lock) do {} while (0) -#define spin_lock(lock) do {} while (0) -#define spin_unlock(lock) do {} while (0) -#define spin_lock_irqsave(lock, flags) do {} while (0) -#define spin_unlock_irqrestore(lock, flags) do {} while (0) - -#define setup_timer(timer, func, data) do {} while (0) -#define del_timer_sync(timer) do {} while (0) -#define schedule_work(work) do {} while (0) -#define INIT_WORK(work, fun) do {} while (0) - -#define cpu_relax() do {} while (0) - #define pr_debug(fmt, args...) debug(fmt, ##args) #define WARN(condition, fmt, args...) ({ \ @@ -46,21 +13,6 @@ typedef unsigned long dmaaddr_t; printf(fmt, ##args); \ ret_warn; }) -#define pm_runtime_get_sync(dev) do {} while (0) -#define pm_runtime_put(dev) do {} while (0) -#define pm_runtime_put_sync(dev) do {} while (0) -#define pm_runtime_use_autosuspend(dev) do {} while (0) -#define pm_runtime_set_autosuspend_delay(dev, delay) do {} while (0) -#define pm_runtime_enable(dev) do {} while (0) - -#define MODULE_DESCRIPTION(desc) -#define MODULE_AUTHOR(author) -#define MODULE_LICENSE(license) -#define MODULE_ALIAS(alias) -#define module_param(name, type, perm) -#define MODULE_PARM_DESC(name, desc) -#define EXPORT_SYMBOL_GPL(name) - #define writesl(a, d, s) __raw_writesl((unsigned long)a, d, s) #define readsl(a, d, s) __raw_readsl((unsigned long)a, d, s) #define writesw(a, d, s) __raw_writesw((unsigned long)a, d, s) @@ -68,16 +20,6 @@ typedef unsigned long dmaaddr_t; #define writesb(a, d, s) __raw_writesb((unsigned long)a, d, s) #define readsb(a, d, s) __raw_readsb((unsigned long)a, d, s) -#define IRQ_NONE 0 -#define IRQ_HANDLED 0 - -#define dev_set_drvdata(dev, data) do {} while (0) - -#define disable_irq_wake(irq) do {} while (0) -#define enable_irq_wake(irq) -EINVAL -#define free_irq(irq, data) do {} while (0) -#define request_irq(nr, f, flags, nm, data) 0 - #define device_init_wakeup(dev, a) do {} while (0) #define platform_data device_data diff --git a/drivers/video/Makefile b/drivers/video/Makefile index 945f35d..93a91c3 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -41,3 +41,4 @@ obj-$(CONFIG_VIDEO_SMI_LYNXEM) += smiLynxEM.o videomodes.o obj-$(CONFIG_VIDEO_TEGRA) += tegra.o obj-$(CONFIG_VIDEO_VCXK) += bus_vcxk.o obj-$(CONFIG_FORMIKE) += formike.o +obj-$(CONFIG_AM335X_LCD) += am335x-fb.o diff --git a/drivers/video/am335x-fb.c b/drivers/video/am335x-fb.c new file mode 100644 index 0000000..ab98941 --- /dev/null +++ b/drivers/video/am335x-fb.c @@ -0,0 +1,169 @@ +/* + * Copyright (C) 2013 Hannes Petermaier <oe5hpm@oevsv.at> + * Bernecker & Rainer Industrieelektronik GmbH - http://www.br-automation.com + * + * minimal framebuffer driver for TI's AM335x SoC to be compatible with + * Wolfgang Denk's LCD-Framework (CONFIG_LCD, common/lcd.c) + * + * - supporting only 24bit RGB/TFT raster Mode (not using palette) + * - sets up LCD controller as in 'am335x_lcdpanel' struct given + * - starts output DMA from gd->fb_base buffer + * + * SPDX-License-Identifier: GPL-2.0+ + */ +#include <common.h> +#include <asm/arch/hardware.h> +#include <lcd.h> +#include "am335x-fb.h" + +#if !defined(LCD_CNTL_BASE) +#error "hw-base address of LCD-Controller (LCD_CNTL_BASE) not defined!" +#endif + + +/* LCD Control Register */ +#define LCD_CLK_DIVISOR(x) ((x) << 8) +#define LCD_RASTER_MODE 0x01 +/* LCD Clock Enable Register */ +#define LCD_CORECLKEN (0x01 << 0) +#define LCD_LIDDCLKEN (0x01 << 1) +#define LCD_DMACLKEN (0x01 << 2) +/* LCD DMA Control Register */ +#define LCD_DMA_BURST_SIZE(x) ((x) << 4) +#define LCD_DMA_BURST_1 0x0 +#define LCD_DMA_BURST_2 0x1 +#define LCD_DMA_BURST_4 0x2 +#define LCD_DMA_BURST_8 0x3 +#define LCD_DMA_BURST_16 0x4 +/* LCD Timing_0 Register */ +#define LCD_HBPLSB(x) ((((x)-1) & 0xFF) << 24) +#define LCD_HFPLSB(x) ((((x)-1) & 0xFF) << 16) +#define LCD_HSWLSB(x) ((((x)-1) & 0x3F) << 10) +#define LCD_HORLSB(x) (((((x) >> 4)-1) & 0x3F) << 4) +#define LCD_HORMSB(x) (((((x) >> 4)-1) & 0x40) >> 4) +/* LCD Timing_1 Register */ +#define LCD_VBP(x) ((x) << 24) +#define LCD_VFP(x) ((x) << 16) +#define LCD_VSW(x) (((x)-1) << 10) +#define LCD_VERLSB(x) (((x)-1) & 0x3FF) +/* LCD Timing_2 Register */ +#define LCD_HSWMSB(x) ((((x)-1) & 0x3C0) << 21) +#define LCD_VERMSB(x) ((((x)-1) & 0x400) << 16) +#define LCD_HBPMSB(x) ((((x)-1) & 0x300) >> 4) +#define LCD_HFPMSB(x) ((((x)-1) & 0x300) >> 8) +#define LCD_INVMASK(x) ((x) & 0x3F00000) +/* LCD Raster Ctrl Register */ +#define LCD_TFT_24BPP_MODE (1 << 25) +#define LCD_TFT_24BPP_UNPACK (1 << 26) +#define LCD_PALMODE_RAWDATA (0x10 << 20) +#define LCD_TFT_MODE (0x01 << 7) +#define LCD_RASTER_ENABLE (0x01 << 0) + + +/* Macro definitions */ +#define FBSIZE(x) ((x->hactive * x->vactive * x->bpp) >> 3) + +struct am335x_lcdhw { + unsigned int pid; /* 0x00 */ + unsigned int ctrl; /* 0x04 */ + unsigned int gap0; /* 0x08 */ + unsigned int lidd_ctrl; /* 0x0C */ + unsigned int lidd_cs0_conf; /* 0x10 */ + unsigned int lidd_cs0_addr; /* 0x14 */ + unsigned int lidd_cs0_data; /* 0x18 */ + unsigned int lidd_cs1_conf; /* 0x1C */ + unsigned int lidd_cs1_addr; /* 0x20 */ + unsigned int lidd_cs1_data; /* 0x24 */ + unsigned int raster_ctrl; /* 0x28 */ + unsigned int raster_timing0; /* 0x2C */ + unsigned int raster_timing1; /* 0x30 */ + unsigned int raster_timing2; /* 0x34 */ + unsigned int raster_subpanel; /* 0x38 */ + unsigned int raster_subpanel2; /* 0x3C */ + unsigned int lcddma_ctrl; /* 0x40 */ + unsigned int lcddma_fb0_base; /* 0x44 */ + unsigned int lcddma_fb0_ceiling; /* 0x48 */ + unsigned int lcddma_fb1_base; /* 0x4C */ + unsigned int lcddma_fb1_ceiling; /* 0x50 */ + unsigned int sysconfig; /* 0x54 */ + unsigned int irqstatus_raw; /* 0x58 */ + unsigned int irqstatus; /* 0x5C */ + unsigned int irqenable_set; /* 0x60 */ + unsigned int irqenable_clear; /* 0x64 */ + unsigned int gap1; /* 0x68 */ + unsigned int clkc_enable; /* 0x6C */ + unsigned int clkc_reset; /* 0x70 */ +}; + +static struct am335x_lcdhw *lcdhw = (void *)LCD_CNTL_BASE; +DECLARE_GLOBAL_DATA_PTR; + +int lcd_get_size(int *line_length) +{ + *line_length = (panel_info.vl_col * NBITS(panel_info.vl_bpix)) / 8; + return *line_length * panel_info.vl_row + 0x20; +} + +int am335xfb_init(struct am335x_lcdpanel *panel) +{ + if (0 == gd->fb_base) { + printf("ERROR: no valid fb_base stored in GLOBAL_DATA_PTR!\n"); + return -1; + } + if (0 == panel) { + printf("ERROR: missing ptr to am335x_lcdpanel!\n"); + return -1; + } + + debug("setting up LCD-Controller for %dx%dx%d (hfp=%d,hbp=%d,hsw=%d / ", + panel->hactive, panel->vactive, panel->bpp, + panel->hfp, panel->hbp, panel->hsw); + debug("vfp=%d,vbp=%d,vsw=%d / clk-div=%d)\n", + panel->vfp, panel->vfp, panel->vsw, panel->pxl_clk_div); + debug("using frambuffer at 0x%08x with size %d.\n", + (unsigned int)gd->fb_base, FBSIZE(panel)); + + /* palette default entry */ + memset((void *)gd->fb_base, 0, 0x20); + *(unsigned int *)gd->fb_base = 0x4000; + + lcdhw->clkc_enable = LCD_CORECLKEN | LCD_LIDDCLKEN | LCD_DMACLKEN; + lcdhw->raster_ctrl = 0; + lcdhw->ctrl = LCD_CLK_DIVISOR(panel->pxl_clk_div) | LCD_RASTER_MODE; + lcdhw->lcddma_fb0_base = gd->fb_base; + lcdhw->lcddma_fb0_ceiling = gd->fb_base + FBSIZE(panel) + 0x20; + lcdhw->lcddma_fb1_base = gd->fb_base; + lcdhw->lcddma_fb1_ceiling = gd->fb_base + FBSIZE(panel) + 0x20; + lcdhw->lcddma_ctrl = LCD_DMA_BURST_SIZE(LCD_DMA_BURST_16); + + lcdhw->raster_timing0 = LCD_HORLSB(panel->hactive) | + LCD_HORMSB(panel->hactive) | + LCD_HFPLSB(panel->hfp) | + LCD_HBPLSB(panel->hbp) | + LCD_HSWLSB(panel->hsw); + lcdhw->raster_timing1 = LCD_VBP(panel->vbp) | + LCD_VFP(panel->vfp) | + LCD_VSW(panel->vsw) | + LCD_VERLSB(panel->vactive); + lcdhw->raster_timing2 = LCD_HSWMSB(panel->hsw) | + LCD_VERMSB(panel->vactive) | + LCD_INVMASK(panel->pol) | + LCD_HBPMSB(panel->hbp) | + LCD_HFPMSB(panel->hfp) | + 0x0000FF00; /* clk cycles for ac-bias */ + lcdhw->raster_ctrl = LCD_TFT_24BPP_MODE | + LCD_TFT_24BPP_UNPACK | + LCD_PALMODE_RAWDATA | + LCD_TFT_MODE | + LCD_RASTER_ENABLE; + + gd->fb_base += 0x20; /* point fb behind palette */ + + /* turn ON display through powercontrol function if accessible */ + if (0 != panel->panel_power_ctrl) { + mdelay(panel->pon_delay); + panel->panel_power_ctrl(1); + } + + return 0; +} diff --git a/drivers/video/am335x-fb.h b/drivers/video/am335x-fb.h new file mode 100644 index 0000000..8a0b131 --- /dev/null +++ b/drivers/video/am335x-fb.h @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2013 Hannes Petermaier <oe5hpm@oevsv.at> - + * Bernecker & Rainer Industrieelektronik GmbH - http://www.br-automation.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef AM335X_FB_H +#define AM335X_FB_H + +#define HSVS_CONTROL (0x01 << 25) /* + * 0 = lcd_lp and lcd_fp are driven on + * opposite edges of pixel clock than + * the lcd_pixel_o + * 1 = lcd_lp and lcd_fp are driven + * according to bit 24 Note that this + * bit MUST be set to '0' for Passive + * Matrix displays the edge timing is + * fixed + */ +#define HSVS_RISEFALL (0x01 << 24) /* + * 0 = lcd_lp and lcd_fp are driven on + * the rising edge of pixel clock (bit + * 25 must be set to 1) + * 1 = lcd_lp and lcd_fp are driven on + * the falling edge of pixel clock (bit + * 25 must be set to 1) + */ +#define DE_INVERT (0x01 << 23) /* + * 0 = DE is low-active + * 1 = DE is high-active + */ +#define PXCLK_INVERT (0x01 << 22) /* + * 0 = pix-clk is high-active + * 1 = pic-clk is low-active + */ +#define HSYNC_INVERT (0x01 << 21) /* + * 0 = HSYNC is active high + * 1 = HSYNC is avtive low + */ +#define VSYNC_INVERT (0x01 << 20) /* + * 0 = VSYNC is active high + * 1 = VSYNC is active low + */ + +struct am335x_lcdpanel { + unsigned int hactive; /* Horizontal active area */ + unsigned int vactive; /* Vertical active area */ + unsigned int bpp; /* bits per pixel */ + unsigned int hfp; /* Horizontal front porch */ + unsigned int hbp; /* Horizontal back porch */ + unsigned int hsw; /* Horizontal Sync Pulse Width */ + unsigned int vfp; /* Vertical front porch */ + unsigned int vbp; /* Vertical back porch */ + unsigned int vsw; /* Vertical Sync Pulse Width */ + unsigned int pxl_clk_div; /* Pixel clock divider*/ + unsigned int pol; /* polarity of sync, clock signals */ + unsigned int pon_delay; /* + * time in ms for turning on lcd after + * initializing lcd-controller + */ + void (*panel_power_ctrl)(int); /* fp for power on/off display */ +}; + +int am335xfb_init(struct am335x_lcdpanel *panel); + +#endif /* AM335X_FB_H */ diff --git a/drivers/video/exynos_dp.c b/drivers/video/exynos_dp.c index 682483f..f60b060 100644 --- a/drivers/video/exynos_dp.c +++ b/drivers/video/exynos_dp.c @@ -9,6 +9,7 @@ #include <config.h> #include <common.h> #include <malloc.h> +#include <linux/compat.h> #include <linux/err.h> #include <asm/arch/clk.h> #include <asm/arch/cpu.h> diff --git a/drivers/video/exynos_mipi_dsi.c b/drivers/video/exynos_mipi_dsi.c index 7dd4652..c68ebd6 100644 --- a/drivers/video/exynos_mipi_dsi.c +++ b/drivers/video/exynos_mipi_dsi.c @@ -11,6 +11,7 @@ #include <malloc.h> #include <fdtdec.h> #include <libfdt.h> +#include <linux/compat.h> #include <linux/err.h> #include <asm/arch/dsim.h> #include <asm/arch/mipi_dsim.h> diff --git a/drivers/video/ipu.h b/drivers/video/ipu.h index 3d2741f..091b58f 100644 --- a/drivers/video/ipu.h +++ b/drivers/video/ipu.h @@ -176,6 +176,14 @@ typedef union { } ipu_channel_params_t; /* + * Enumeration of IPU interrupts. + */ +enum ipu_irq_line { + IPU_IRQ_DP_SF_END = 448 + 3, + IPU_IRQ_DC_FC_1 = 448 + 9, +}; + +/* * Bitfield of Display Interface signal polarities. */ typedef struct { diff --git a/drivers/video/ipu_disp.c b/drivers/video/ipu_disp.c index cefd2dc..948e1fc 100644 --- a/drivers/video/ipu_disp.c +++ b/drivers/video/ipu_disp.c @@ -33,7 +33,7 @@ enum csc_type_t { struct dp_csc_param_t { int mode; - void *coeff; + const int (*coeff)[5][3]; }; #define SYNC_WAVE 0 @@ -666,13 +666,16 @@ void ipu_dp_dc_disable(ipu_channel_t channel, unsigned char swap) uint32_t csc; uint32_t dc_chan = 0; int timeout = 50; + int irq = 0; dc_swap = swap; if (channel == MEM_DC_SYNC) { dc_chan = 1; + irq = IPU_IRQ_DC_FC_1; } else if (channel == MEM_BG_SYNC) { dc_chan = 5; + irq = IPU_IRQ_DP_SF_END; } else if (channel == MEM_FG_SYNC) { /* Disable FG channel */ dc_chan = 5; @@ -723,25 +726,11 @@ void ipu_dp_dc_disable(ipu_channel_t channel, unsigned char swap) reg ^= DC_WR_CH_CONF_PROG_DI_ID; __raw_writel(reg, DC_WR_CH_CONF(dc_chan)); } else { - timeout = 50; - - /* Wait for DC triple buffer to empty */ - if (g_dc_di_assignment[dc_chan] == 0) - while ((__raw_readl(DC_STAT) & 0x00000002) - != 0x00000002) { - udelay(2000); - timeout -= 2; - if (timeout <= 0) - break; - } - else if (g_dc_di_assignment[dc_chan] == 1) - while ((__raw_readl(DC_STAT) & 0x00000020) - != 0x00000020) { - udelay(2000); - timeout -= 2; - if (timeout <= 0) - break; - } + /* Make sure that we leave at the irq starting edge */ + __raw_writel(IPUIRQ_2_MASK(irq), IPUIRQ_2_STATREG(irq)); + do { + reg = __raw_readl(IPUIRQ_2_STATREG(irq)); + } while (!(reg & IPUIRQ_2_MASK(irq))); reg = __raw_readl(DC_WR_CH_CONF(dc_chan)); reg &= ~DC_WR_CH_CONF_PROG_TYPE_MASK; diff --git a/drivers/video/ipu_regs.h b/drivers/video/ipu_regs.h index 21e9c99..c2c134a 100644 --- a/drivers/video/ipu_regs.h +++ b/drivers/video/ipu_regs.h @@ -313,9 +313,12 @@ struct ipu_dmfc { #define IPU_STAT ((struct ipu_stat *)(IPU_CTRL_BASE_ADDR + \ IPU_STAT_REG_BASE)) +#define IPU_INT_STAT(n) (&IPU_STAT->int_stat[(n) - 1]) #define IPU_CHA_CUR_BUF(ch) (&IPU_STAT->cur_buf[ch / 32]) #define IPU_CHA_BUF0_RDY(ch) (&IPU_STAT->ch_buf0_rdy[ch / 32]) #define IPU_CHA_BUF1_RDY(ch) (&IPU_STAT->ch_buf1_rdy[ch / 32]) +#define IPUIRQ_2_STATREG(irq) (IPU_INT_STAT(1) + ((irq) / 32)) +#define IPUIRQ_2_MASK(irq) (1UL << ((irq) & 0x1F)) #define IPU_INT_CTRL(n) (&IPU_CM_REG->int_ctrl[(n) - 1]) diff --git a/fs/ext4/ext4fs.c b/fs/ext4/ext4fs.c index 417ce7b..cbdc220 100644 --- a/fs/ext4/ext4fs.c +++ b/fs/ext4/ext4fs.c @@ -182,6 +182,11 @@ int ext4fs_exists(const char *filename) return file_len >= 0; } +int ext4fs_size(const char *filename) +{ + return ext4fs_open(filename); +} + int ext4fs_read(char *buf, unsigned len) { if (ext4fs_root == NULL || ext4fs_file == NULL) diff --git a/fs/fat/fat.c b/fs/fat/fat.c index 54f42ea..561921f 100644 --- a/fs/fat/fat.c +++ b/fs/fat/fat.c @@ -1243,6 +1243,11 @@ int fat_exists(const char *filename) return sz >= 0; } +int fat_size(const char *filename) +{ + return do_fat_read_at(filename, 0, NULL, 0, LS_NO, 1); +} + long file_fat_read_at(const char *filename, unsigned long pos, void *buffer, unsigned long maxsize) { @@ -46,6 +46,11 @@ static inline int fs_exists_unsupported(const char *filename) return 0; } +static inline int fs_size_unsupported(const char *filename) +{ + return -1; +} + static inline int fs_read_unsupported(const char *filename, void *buf, int offset, int len) { @@ -77,6 +82,7 @@ struct fstype_info { disk_partition_t *fs_partition); int (*ls)(const char *dirname); int (*exists)(const char *filename); + int (*size)(const char *filename); int (*read)(const char *filename, void *buf, int offset, int len); int (*write)(const char *filename, void *buf, int offset, int len); void (*close)(void); @@ -91,6 +97,7 @@ static struct fstype_info fstypes[] = { .close = fat_close, .ls = file_fat_ls, .exists = fat_exists, + .size = fat_size, .read = fat_read_file, .write = fs_write_unsupported, }, @@ -103,6 +110,7 @@ static struct fstype_info fstypes[] = { .close = ext4fs_close, .ls = ext4fs_ls, .exists = ext4fs_exists, + .size = ext4fs_size, .read = ext4_read_file, .write = fs_write_unsupported, }, @@ -115,6 +123,7 @@ static struct fstype_info fstypes[] = { .close = sandbox_fs_close, .ls = sandbox_fs_ls, .exists = sandbox_fs_exists, + .size = sandbox_fs_size, .read = fs_read_sandbox, .write = fs_write_sandbox, }, @@ -126,6 +135,7 @@ static struct fstype_info fstypes[] = { .close = fs_close_unsupported, .ls = fs_ls_unsupported, .exists = fs_exists_unsupported, + .size = fs_size_unsupported, .read = fs_read_unsupported, .write = fs_write_unsupported, }, @@ -223,6 +233,19 @@ int fs_exists(const char *filename) return ret; } +int fs_size(const char *filename) +{ + int ret; + + struct fstype_info *info = fs_get_info(fs_type); + + ret = info->size(filename); + + fs_close(); + + return ret; +} + int fs_read(const char *filename, ulong addr, int offset, int len) { struct fstype_info *info = fs_get_info(fs_type); @@ -266,6 +289,26 @@ int fs_write(const char *filename, ulong addr, int offset, int len) return ret; } +int do_size(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[], + int fstype) +{ + int size; + + if (argc != 4) + return CMD_RET_USAGE; + + if (fs_set_blk_dev(argv[1], argv[2], fstype)) + return 1; + + size = fs_size(argv[3]); + if (size < 0) + return CMD_RET_FAILURE; + + setenv_hex("filesize", size); + + return 0; +} + int do_load(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[], int fstype) { diff --git a/fs/sandbox/sandboxfs.c b/fs/sandbox/sandboxfs.c index 8507978..ba6402c 100644 --- a/fs/sandbox/sandboxfs.c +++ b/fs/sandbox/sandboxfs.c @@ -80,6 +80,11 @@ int sandbox_fs_exists(const char *filename) return sz >= 0; } +int sandbox_fs_size(const char *filename) +{ + return os_get_filesize(filename); +} + void sandbox_fs_close(void) { } diff --git a/fs/ubifs/budget.c b/fs/ubifs/budget.c index 85377ea..9ed4017 100644 --- a/fs/ubifs/budget.c +++ b/fs/ubifs/budget.c @@ -3,18 +3,7 @@ * * Copyright (C) 2006-2008 Nokia Corporation. * - * 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. - * - * 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., 51 - * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * SPDX-License-Identifier: GPL-2.0+ * * Authors: Adrian Hunter * Artem Bityutskiy (Битюцкий Артём) @@ -31,32 +20,171 @@ */ #include "ubifs.h" +#define __UBOOT__ +#ifndef __UBOOT__ +#include <linux/writeback.h> +#else +#include <linux/err.h> +#endif #include <linux/math64.h> +/* + * When pessimistic budget calculations say that there is no enough space, + * UBIFS starts writing back dirty inodes and pages, doing garbage collection, + * or committing. The below constant defines maximum number of times UBIFS + * repeats the operations. + */ +#define MAX_MKSPC_RETRIES 3 + +/* + * The below constant defines amount of dirty pages which should be written + * back at when trying to shrink the liability. + */ +#define NR_TO_WRITE 16 + +#ifndef __UBOOT__ +/** + * shrink_liability - write-back some dirty pages/inodes. + * @c: UBIFS file-system description object + * @nr_to_write: how many dirty pages to write-back + * + * This function shrinks UBIFS liability by means of writing back some amount + * of dirty inodes and their pages. + * + * Note, this function synchronizes even VFS inodes which are locked + * (@i_mutex) by the caller of the budgeting function, because write-back does + * not touch @i_mutex. + */ +static void shrink_liability(struct ubifs_info *c, int nr_to_write) +{ + down_read(&c->vfs_sb->s_umount); + writeback_inodes_sb(c->vfs_sb, WB_REASON_FS_FREE_SPACE); + up_read(&c->vfs_sb->s_umount); +} + +/** + * run_gc - run garbage collector. + * @c: UBIFS file-system description object + * + * This function runs garbage collector to make some more free space. Returns + * zero if a free LEB has been produced, %-EAGAIN if commit is required, and a + * negative error code in case of failure. + */ +static int run_gc(struct ubifs_info *c) +{ + int err, lnum; + + /* Make some free space by garbage-collecting dirty space */ + down_read(&c->commit_sem); + lnum = ubifs_garbage_collect(c, 1); + up_read(&c->commit_sem); + if (lnum < 0) + return lnum; + + /* GC freed one LEB, return it to lprops */ + dbg_budg("GC freed LEB %d", lnum); + err = ubifs_return_leb(c, lnum); + if (err) + return err; + return 0; +} + /** - * ubifs_calc_min_idx_lebs - calculate amount of eraseblocks for the index. + * get_liability - calculate current liability. * @c: UBIFS file-system description object * - * This function calculates and returns the number of eraseblocks which should - * be kept for index usage. + * This function calculates and returns current UBIFS liability, i.e. the + * amount of bytes UBIFS has "promised" to write to the media. + */ +static long long get_liability(struct ubifs_info *c) +{ + long long liab; + + spin_lock(&c->space_lock); + liab = c->bi.idx_growth + c->bi.data_growth + c->bi.dd_growth; + spin_unlock(&c->space_lock); + return liab; +} + +/** + * make_free_space - make more free space on the file-system. + * @c: UBIFS file-system description object + * + * This function is called when an operation cannot be budgeted because there + * is supposedly no free space. But in most cases there is some free space: + * o budgeting is pessimistic, so it always budgets more than it is actually + * needed, so shrinking the liability is one way to make free space - the + * cached data will take less space then it was budgeted for; + * o GC may turn some dark space into free space (budgeting treats dark space + * as not available); + * o commit may free some LEB, i.e., turn freeable LEBs into free LEBs. + * + * So this function tries to do the above. Returns %-EAGAIN if some free space + * was presumably made and the caller has to re-try budgeting the operation. + * Returns %-ENOSPC if it couldn't do more free space, and other negative error + * codes on failures. + */ +static int make_free_space(struct ubifs_info *c) +{ + int err, retries = 0; + long long liab1, liab2; + + do { + liab1 = get_liability(c); + /* + * We probably have some dirty pages or inodes (liability), try + * to write them back. + */ + dbg_budg("liability %lld, run write-back", liab1); + shrink_liability(c, NR_TO_WRITE); + + liab2 = get_liability(c); + if (liab2 < liab1) + return -EAGAIN; + + dbg_budg("new liability %lld (not shrunk)", liab2); + + /* Liability did not shrink again, try GC */ + dbg_budg("Run GC"); + err = run_gc(c); + if (!err) + return -EAGAIN; + + if (err != -EAGAIN && err != -ENOSPC) + /* Some real error happened */ + return err; + + dbg_budg("Run commit (retries %d)", retries); + err = ubifs_run_commit(c); + if (err) + return err; + } while (retries++ < MAX_MKSPC_RETRIES); + + return -ENOSPC; +} +#endif + +/** + * ubifs_calc_min_idx_lebs - calculate amount of LEBs for the index. + * @c: UBIFS file-system description object + * + * This function calculates and returns the number of LEBs which should be kept + * for index usage. */ int ubifs_calc_min_idx_lebs(struct ubifs_info *c) { - int idx_lebs, eff_leb_size = c->leb_size - c->max_idx_node_sz; + int idx_lebs; long long idx_size; - idx_size = c->old_idx_sz + c->budg_idx_growth + c->budg_uncommitted_idx; - + idx_size = c->bi.old_idx_sz + c->bi.idx_growth + c->bi.uncommitted_idx; /* And make sure we have thrice the index size of space reserved */ - idx_size = idx_size + (idx_size << 1); - + idx_size += idx_size << 1; /* * We do not maintain 'old_idx_size' as 'old_idx_lebs'/'old_idx_bytes' * pair, nor similarly the two variables for the new index size, so we * have to do this costly 64-bit division on fast-path. */ - idx_size += eff_leb_size - 1; - idx_lebs = div_u64(idx_size, eff_leb_size); + idx_lebs = div_u64(idx_size + c->idx_leb_size - 1, c->idx_leb_size); /* * The index head is not available for the in-the-gaps method, so add an * extra LEB to compensate. @@ -67,6 +195,424 @@ int ubifs_calc_min_idx_lebs(struct ubifs_info *c) return idx_lebs; } +#ifndef __UBOOT__ +/** + * ubifs_calc_available - calculate available FS space. + * @c: UBIFS file-system description object + * @min_idx_lebs: minimum number of LEBs reserved for the index + * + * This function calculates and returns amount of FS space available for use. + */ +long long ubifs_calc_available(const struct ubifs_info *c, int min_idx_lebs) +{ + int subtract_lebs; + long long available; + + available = c->main_bytes - c->lst.total_used; + + /* + * Now 'available' contains theoretically available flash space + * assuming there is no index, so we have to subtract the space which + * is reserved for the index. + */ + subtract_lebs = min_idx_lebs; + + /* Take into account that GC reserves one LEB for its own needs */ + subtract_lebs += 1; + + /* + * The GC journal head LEB is not really accessible. And since + * different write types go to different heads, we may count only on + * one head's space. + */ + subtract_lebs += c->jhead_cnt - 1; + + /* We also reserve one LEB for deletions, which bypass budgeting */ + subtract_lebs += 1; + + available -= (long long)subtract_lebs * c->leb_size; + + /* Subtract the dead space which is not available for use */ + available -= c->lst.total_dead; + + /* + * Subtract dark space, which might or might not be usable - it depends + * on the data which we have on the media and which will be written. If + * this is a lot of uncompressed or not-compressible data, the dark + * space cannot be used. + */ + available -= c->lst.total_dark; + + /* + * However, there is more dark space. The index may be bigger than + * @min_idx_lebs. Those extra LEBs are assumed to be available, but + * their dark space is not included in total_dark, so it is subtracted + * here. + */ + if (c->lst.idx_lebs > min_idx_lebs) { + subtract_lebs = c->lst.idx_lebs - min_idx_lebs; + available -= subtract_lebs * c->dark_wm; + } + + /* The calculations are rough and may end up with a negative number */ + return available > 0 ? available : 0; +} + +/** + * can_use_rp - check whether the user is allowed to use reserved pool. + * @c: UBIFS file-system description object + * + * UBIFS has so-called "reserved pool" which is flash space reserved + * for the superuser and for uses whose UID/GID is recorded in UBIFS superblock. + * This function checks whether current user is allowed to use reserved pool. + * Returns %1 current user is allowed to use reserved pool and %0 otherwise. + */ +static int can_use_rp(struct ubifs_info *c) +{ + if (uid_eq(current_fsuid(), c->rp_uid) || capable(CAP_SYS_RESOURCE) || + (!gid_eq(c->rp_gid, GLOBAL_ROOT_GID) && in_group_p(c->rp_gid))) + return 1; + return 0; +} + +/** + * do_budget_space - reserve flash space for index and data growth. + * @c: UBIFS file-system description object + * + * This function makes sure UBIFS has enough free LEBs for index growth and + * data. + * + * When budgeting index space, UBIFS reserves thrice as many LEBs as the index + * would take if it was consolidated and written to the flash. This guarantees + * that the "in-the-gaps" commit method always succeeds and UBIFS will always + * be able to commit dirty index. So this function basically adds amount of + * budgeted index space to the size of the current index, multiplies this by 3, + * and makes sure this does not exceed the amount of free LEBs. + * + * Notes about @c->bi.min_idx_lebs and @c->lst.idx_lebs variables: + * o @c->lst.idx_lebs is the number of LEBs the index currently uses. It might + * be large, because UBIFS does not do any index consolidation as long as + * there is free space. IOW, the index may take a lot of LEBs, but the LEBs + * will contain a lot of dirt. + * o @c->bi.min_idx_lebs is the number of LEBS the index presumably takes. IOW, + * the index may be consolidated to take up to @c->bi.min_idx_lebs LEBs. + * + * This function returns zero in case of success, and %-ENOSPC in case of + * failure. + */ +static int do_budget_space(struct ubifs_info *c) +{ + long long outstanding, available; + int lebs, rsvd_idx_lebs, min_idx_lebs; + + /* First budget index space */ + min_idx_lebs = ubifs_calc_min_idx_lebs(c); + + /* Now 'min_idx_lebs' contains number of LEBs to reserve */ + if (min_idx_lebs > c->lst.idx_lebs) + rsvd_idx_lebs = min_idx_lebs - c->lst.idx_lebs; + else + rsvd_idx_lebs = 0; + + /* + * The number of LEBs that are available to be used by the index is: + * + * @c->lst.empty_lebs + @c->freeable_cnt + @c->idx_gc_cnt - + * @c->lst.taken_empty_lebs + * + * @c->lst.empty_lebs are available because they are empty. + * @c->freeable_cnt are available because they contain only free and + * dirty space, @c->idx_gc_cnt are available because they are index + * LEBs that have been garbage collected and are awaiting the commit + * before they can be used. And the in-the-gaps method will grab these + * if it needs them. @c->lst.taken_empty_lebs are empty LEBs that have + * already been allocated for some purpose. + * + * Note, @c->idx_gc_cnt is included to both @c->lst.empty_lebs (because + * these LEBs are empty) and to @c->lst.taken_empty_lebs (because they + * are taken until after the commit). + * + * Note, @c->lst.taken_empty_lebs may temporarily be higher by one + * because of the way we serialize LEB allocations and budgeting. See a + * comment in 'ubifs_find_free_space()'. + */ + lebs = c->lst.empty_lebs + c->freeable_cnt + c->idx_gc_cnt - + c->lst.taken_empty_lebs; + if (unlikely(rsvd_idx_lebs > lebs)) { + dbg_budg("out of indexing space: min_idx_lebs %d (old %d), rsvd_idx_lebs %d", + min_idx_lebs, c->bi.min_idx_lebs, rsvd_idx_lebs); + return -ENOSPC; + } + + available = ubifs_calc_available(c, min_idx_lebs); + outstanding = c->bi.data_growth + c->bi.dd_growth; + + if (unlikely(available < outstanding)) { + dbg_budg("out of data space: available %lld, outstanding %lld", + available, outstanding); + return -ENOSPC; + } + + if (available - outstanding <= c->rp_size && !can_use_rp(c)) + return -ENOSPC; + + c->bi.min_idx_lebs = min_idx_lebs; + return 0; +} + +/** + * calc_idx_growth - calculate approximate index growth from budgeting request. + * @c: UBIFS file-system description object + * @req: budgeting request + * + * For now we assume each new node adds one znode. But this is rather poor + * approximation, though. + */ +static int calc_idx_growth(const struct ubifs_info *c, + const struct ubifs_budget_req *req) +{ + int znodes; + + znodes = req->new_ino + (req->new_page << UBIFS_BLOCKS_PER_PAGE_SHIFT) + + req->new_dent; + return znodes * c->max_idx_node_sz; +} + +/** + * calc_data_growth - calculate approximate amount of new data from budgeting + * request. + * @c: UBIFS file-system description object + * @req: budgeting request + */ +static int calc_data_growth(const struct ubifs_info *c, + const struct ubifs_budget_req *req) +{ + int data_growth; + + data_growth = req->new_ino ? c->bi.inode_budget : 0; + if (req->new_page) + data_growth += c->bi.page_budget; + if (req->new_dent) + data_growth += c->bi.dent_budget; + data_growth += req->new_ino_d; + return data_growth; +} + +/** + * calc_dd_growth - calculate approximate amount of data which makes other data + * dirty from budgeting request. + * @c: UBIFS file-system description object + * @req: budgeting request + */ +static int calc_dd_growth(const struct ubifs_info *c, + const struct ubifs_budget_req *req) +{ + int dd_growth; + + dd_growth = req->dirtied_page ? c->bi.page_budget : 0; + + if (req->dirtied_ino) + dd_growth += c->bi.inode_budget << (req->dirtied_ino - 1); + if (req->mod_dent) + dd_growth += c->bi.dent_budget; + dd_growth += req->dirtied_ino_d; + return dd_growth; +} + +/** + * ubifs_budget_space - ensure there is enough space to complete an operation. + * @c: UBIFS file-system description object + * @req: budget request + * + * This function allocates budget for an operation. It uses pessimistic + * approximation of how much flash space the operation needs. The goal of this + * function is to make sure UBIFS always has flash space to flush all dirty + * pages, dirty inodes, and dirty znodes (liability). This function may force + * commit, garbage-collection or write-back. Returns zero in case of success, + * %-ENOSPC if there is no free space and other negative error codes in case of + * failures. + */ +int ubifs_budget_space(struct ubifs_info *c, struct ubifs_budget_req *req) +{ + int uninitialized_var(cmt_retries), uninitialized_var(wb_retries); + int err, idx_growth, data_growth, dd_growth, retried = 0; + + ubifs_assert(req->new_page <= 1); + ubifs_assert(req->dirtied_page <= 1); + ubifs_assert(req->new_dent <= 1); + ubifs_assert(req->mod_dent <= 1); + ubifs_assert(req->new_ino <= 1); + ubifs_assert(req->new_ino_d <= UBIFS_MAX_INO_DATA); + ubifs_assert(req->dirtied_ino <= 4); + ubifs_assert(req->dirtied_ino_d <= UBIFS_MAX_INO_DATA * 4); + ubifs_assert(!(req->new_ino_d & 7)); + ubifs_assert(!(req->dirtied_ino_d & 7)); + + data_growth = calc_data_growth(c, req); + dd_growth = calc_dd_growth(c, req); + if (!data_growth && !dd_growth) + return 0; + idx_growth = calc_idx_growth(c, req); + +again: + spin_lock(&c->space_lock); + ubifs_assert(c->bi.idx_growth >= 0); + ubifs_assert(c->bi.data_growth >= 0); + ubifs_assert(c->bi.dd_growth >= 0); + + if (unlikely(c->bi.nospace) && (c->bi.nospace_rp || !can_use_rp(c))) { + dbg_budg("no space"); + spin_unlock(&c->space_lock); + return -ENOSPC; + } + + c->bi.idx_growth += idx_growth; + c->bi.data_growth += data_growth; + c->bi.dd_growth += dd_growth; + + err = do_budget_space(c); + if (likely(!err)) { + req->idx_growth = idx_growth; + req->data_growth = data_growth; + req->dd_growth = dd_growth; + spin_unlock(&c->space_lock); + return 0; + } + + /* Restore the old values */ + c->bi.idx_growth -= idx_growth; + c->bi.data_growth -= data_growth; + c->bi.dd_growth -= dd_growth; + spin_unlock(&c->space_lock); + + if (req->fast) { + dbg_budg("no space for fast budgeting"); + return err; + } + + err = make_free_space(c); + cond_resched(); + if (err == -EAGAIN) { + dbg_budg("try again"); + goto again; + } else if (err == -ENOSPC) { + if (!retried) { + retried = 1; + dbg_budg("-ENOSPC, but anyway try once again"); + goto again; + } + dbg_budg("FS is full, -ENOSPC"); + c->bi.nospace = 1; + if (can_use_rp(c) || c->rp_size == 0) + c->bi.nospace_rp = 1; + smp_wmb(); + } else + ubifs_err("cannot budget space, error %d", err); + return err; +} + +/** + * ubifs_release_budget - release budgeted free space. + * @c: UBIFS file-system description object + * @req: budget request + * + * This function releases the space budgeted by 'ubifs_budget_space()'. Note, + * since the index changes (which were budgeted for in @req->idx_growth) will + * only be written to the media on commit, this function moves the index budget + * from @c->bi.idx_growth to @c->bi.uncommitted_idx. The latter will be zeroed + * by the commit operation. + */ +void ubifs_release_budget(struct ubifs_info *c, struct ubifs_budget_req *req) +{ + ubifs_assert(req->new_page <= 1); + ubifs_assert(req->dirtied_page <= 1); + ubifs_assert(req->new_dent <= 1); + ubifs_assert(req->mod_dent <= 1); + ubifs_assert(req->new_ino <= 1); + ubifs_assert(req->new_ino_d <= UBIFS_MAX_INO_DATA); + ubifs_assert(req->dirtied_ino <= 4); + ubifs_assert(req->dirtied_ino_d <= UBIFS_MAX_INO_DATA * 4); + ubifs_assert(!(req->new_ino_d & 7)); + ubifs_assert(!(req->dirtied_ino_d & 7)); + if (!req->recalculate) { + ubifs_assert(req->idx_growth >= 0); + ubifs_assert(req->data_growth >= 0); + ubifs_assert(req->dd_growth >= 0); + } + + if (req->recalculate) { + req->data_growth = calc_data_growth(c, req); + req->dd_growth = calc_dd_growth(c, req); + req->idx_growth = calc_idx_growth(c, req); + } + + if (!req->data_growth && !req->dd_growth) + return; + + c->bi.nospace = c->bi.nospace_rp = 0; + smp_wmb(); + + spin_lock(&c->space_lock); + c->bi.idx_growth -= req->idx_growth; + c->bi.uncommitted_idx += req->idx_growth; + c->bi.data_growth -= req->data_growth; + c->bi.dd_growth -= req->dd_growth; + c->bi.min_idx_lebs = ubifs_calc_min_idx_lebs(c); + + ubifs_assert(c->bi.idx_growth >= 0); + ubifs_assert(c->bi.data_growth >= 0); + ubifs_assert(c->bi.dd_growth >= 0); + ubifs_assert(c->bi.min_idx_lebs < c->main_lebs); + ubifs_assert(!(c->bi.idx_growth & 7)); + ubifs_assert(!(c->bi.data_growth & 7)); + ubifs_assert(!(c->bi.dd_growth & 7)); + spin_unlock(&c->space_lock); +} + +/** + * ubifs_convert_page_budget - convert budget of a new page. + * @c: UBIFS file-system description object + * + * This function converts budget which was allocated for a new page of data to + * the budget of changing an existing page of data. The latter is smaller than + * the former, so this function only does simple re-calculation and does not + * involve any write-back. + */ +void ubifs_convert_page_budget(struct ubifs_info *c) +{ + spin_lock(&c->space_lock); + /* Release the index growth reservation */ + c->bi.idx_growth -= c->max_idx_node_sz << UBIFS_BLOCKS_PER_PAGE_SHIFT; + /* Release the data growth reservation */ + c->bi.data_growth -= c->bi.page_budget; + /* Increase the dirty data growth reservation instead */ + c->bi.dd_growth += c->bi.page_budget; + /* And re-calculate the indexing space reservation */ + c->bi.min_idx_lebs = ubifs_calc_min_idx_lebs(c); + spin_unlock(&c->space_lock); +} + +/** + * ubifs_release_dirty_inode_budget - release dirty inode budget. + * @c: UBIFS file-system description object + * @ui: UBIFS inode to release the budget for + * + * This function releases budget corresponding to a dirty inode. It is usually + * called when after the inode has been written to the media and marked as + * clean. It also causes the "no space" flags to be cleared. + */ +void ubifs_release_dirty_inode_budget(struct ubifs_info *c, + struct ubifs_inode *ui) +{ + struct ubifs_budget_req req; + + memset(&req, 0, sizeof(struct ubifs_budget_req)); + /* The "no space" flags will be cleared because dd_growth is > 0 */ + req.dd_growth = c->bi.inode_budget + ALIGN(ui->data_len, 8); + ubifs_release_budget(c, &req); +} +#endif + /** * ubifs_reported_space - calculate reported free space. * @c: the UBIFS file-system description object @@ -111,3 +657,75 @@ long long ubifs_reported_space(const struct ubifs_info *c, long long free) free *= factor; return div_u64(free, divisor); } + +#ifndef __UBOOT__ +/** + * ubifs_get_free_space_nolock - return amount of free space. + * @c: UBIFS file-system description object + * + * This function calculates amount of free space to report to user-space. + * + * Because UBIFS may introduce substantial overhead (the index, node headers, + * alignment, wastage at the end of LEBs, etc), it cannot report real amount of + * free flash space it has (well, because not all dirty space is reclaimable, + * UBIFS does not actually know the real amount). If UBIFS did so, it would + * bread user expectations about what free space is. Users seem to accustomed + * to assume that if the file-system reports N bytes of free space, they would + * be able to fit a file of N bytes to the FS. This almost works for + * traditional file-systems, because they have way less overhead than UBIFS. + * So, to keep users happy, UBIFS tries to take the overhead into account. + */ +long long ubifs_get_free_space_nolock(struct ubifs_info *c) +{ + int rsvd_idx_lebs, lebs; + long long available, outstanding, free; + + ubifs_assert(c->bi.min_idx_lebs == ubifs_calc_min_idx_lebs(c)); + outstanding = c->bi.data_growth + c->bi.dd_growth; + available = ubifs_calc_available(c, c->bi.min_idx_lebs); + + /* + * When reporting free space to user-space, UBIFS guarantees that it is + * possible to write a file of free space size. This means that for + * empty LEBs we may use more precise calculations than + * 'ubifs_calc_available()' is using. Namely, we know that in empty + * LEBs we would waste only @c->leb_overhead bytes, not @c->dark_wm. + * Thus, amend the available space. + * + * Note, the calculations below are similar to what we have in + * 'do_budget_space()', so refer there for comments. + */ + if (c->bi.min_idx_lebs > c->lst.idx_lebs) + rsvd_idx_lebs = c->bi.min_idx_lebs - c->lst.idx_lebs; + else + rsvd_idx_lebs = 0; + lebs = c->lst.empty_lebs + c->freeable_cnt + c->idx_gc_cnt - + c->lst.taken_empty_lebs; + lebs -= rsvd_idx_lebs; + available += lebs * (c->dark_wm - c->leb_overhead); + + if (available > outstanding) + free = ubifs_reported_space(c, available - outstanding); + else + free = 0; + return free; +} + +/** + * ubifs_get_free_space - return amount of free space. + * @c: UBIFS file-system description object + * + * This function calculates and returns amount of free space to report to + * user-space. + */ +long long ubifs_get_free_space(struct ubifs_info *c) +{ + long long free; + + spin_lock(&c->space_lock); + free = ubifs_get_free_space_nolock(c); + spin_unlock(&c->space_lock); + + return free; +} +#endif diff --git a/fs/ubifs/debug.c b/fs/ubifs/debug.c index 6afb883..2f50a55 100644 --- a/fs/ubifs/debug.c +++ b/fs/ubifs/debug.c @@ -3,18 +3,7 @@ * * Copyright (C) 2006-2008 Nokia Corporation * - * 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. - * - * 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., 51 - * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * SPDX-License-Identifier: GPL-2.0+ * * Authors: Artem Bityutskiy (Битюцкий Артём) * Adrian Hunter @@ -27,28 +16,44 @@ * various local functions of those subsystems. */ -#define UBIFS_DBG_PRESERVE_UBI - +#define __UBOOT__ +#ifndef __UBOOT__ +#include <linux/module.h> +#include <linux/debugfs.h> +#include <linux/math64.h> +#include <linux/uaccess.h> +#include <linux/random.h> +#else +#include <linux/compat.h> +#include <linux/err.h> +#endif #include "ubifs.h" -#ifdef CONFIG_UBIFS_FS_DEBUG - -DEFINE_SPINLOCK(dbg_lock); +#ifndef __UBOOT__ +static DEFINE_SPINLOCK(dbg_lock); +#endif -static char dbg_key_buf0[128]; -static char dbg_key_buf1[128]; - -unsigned int ubifs_msg_flags = UBIFS_MSG_FLAGS_DEFAULT; -unsigned int ubifs_chk_flags = UBIFS_CHK_FLAGS_DEFAULT; -unsigned int ubifs_tst_flags; - -module_param_named(debug_msgs, ubifs_msg_flags, uint, S_IRUGO | S_IWUSR); -module_param_named(debug_chks, ubifs_chk_flags, uint, S_IRUGO | S_IWUSR); -module_param_named(debug_tsts, ubifs_tst_flags, uint, S_IRUGO | S_IWUSR); +static const char *get_key_fmt(int fmt) +{ + switch (fmt) { + case UBIFS_SIMPLE_KEY_FMT: + return "simple"; + default: + return "unknown/invalid format"; + } +} -MODULE_PARM_DESC(debug_msgs, "Debug message type flags"); -MODULE_PARM_DESC(debug_chks, "Debug check flags"); -MODULE_PARM_DESC(debug_tsts, "Debug special test flags"); +static const char *get_key_hash(int hash) +{ + switch (hash) { + case UBIFS_KEY_HASH_R5: + return "R5"; + case UBIFS_KEY_HASH_TEST: + return "test"; + default: + return "unknown/invalid name hash"; + } +} static const char *get_key_type(int type) { @@ -68,8 +73,32 @@ static const char *get_key_type(int type) } } -static void sprintf_key(const struct ubifs_info *c, const union ubifs_key *key, - char *buffer) +#ifndef __UBOOT__ +static const char *get_dent_type(int type) +{ + switch (type) { + case UBIFS_ITYPE_REG: + return "file"; + case UBIFS_ITYPE_DIR: + return "dir"; + case UBIFS_ITYPE_LNK: + return "symlink"; + case UBIFS_ITYPE_BLK: + return "blkdev"; + case UBIFS_ITYPE_CHR: + return "char dev"; + case UBIFS_ITYPE_FIFO: + return "fifo"; + case UBIFS_ITYPE_SOCK: + return "socket"; + default: + return "unknown/invalid type"; + } +} +#endif + +const char *dbg_snprintf_key(const struct ubifs_info *c, + const union ubifs_key *key, char *buffer, int len) { char *p = buffer; int type = key_type(c, key); @@ -77,70 +106,3030 @@ static void sprintf_key(const struct ubifs_info *c, const union ubifs_key *key, if (c->key_fmt == UBIFS_SIMPLE_KEY_FMT) { switch (type) { case UBIFS_INO_KEY: - sprintf(p, "(%lu, %s)", (unsigned long)key_inum(c, key), - get_key_type(type)); + len -= snprintf(p, len, "(%lu, %s)", + (unsigned long)key_inum(c, key), + get_key_type(type)); break; case UBIFS_DENT_KEY: case UBIFS_XENT_KEY: - sprintf(p, "(%lu, %s, %#08x)", - (unsigned long)key_inum(c, key), - get_key_type(type), key_hash(c, key)); + len -= snprintf(p, len, "(%lu, %s, %#08x)", + (unsigned long)key_inum(c, key), + get_key_type(type), key_hash(c, key)); break; case UBIFS_DATA_KEY: - sprintf(p, "(%lu, %s, %u)", - (unsigned long)key_inum(c, key), - get_key_type(type), key_block(c, key)); + len -= snprintf(p, len, "(%lu, %s, %u)", + (unsigned long)key_inum(c, key), + get_key_type(type), key_block(c, key)); break; case UBIFS_TRUN_KEY: - sprintf(p, "(%lu, %s)", - (unsigned long)key_inum(c, key), - get_key_type(type)); + len -= snprintf(p, len, "(%lu, %s)", + (unsigned long)key_inum(c, key), + get_key_type(type)); break; default: - sprintf(p, "(bad key type: %#08x, %#08x)", - key->u32[0], key->u32[1]); + len -= snprintf(p, len, "(bad key type: %#08x, %#08x)", + key->u32[0], key->u32[1]); } } else - sprintf(p, "bad key format %d", c->key_fmt); + len -= snprintf(p, len, "bad key format %d", c->key_fmt); + ubifs_assert(len > 0); + return p; } -const char *dbg_key_str0(const struct ubifs_info *c, const union ubifs_key *key) +const char *dbg_ntype(int type) { - /* dbg_lock must be held */ - sprintf_key(c, key, dbg_key_buf0); - return dbg_key_buf0; + switch (type) { + case UBIFS_PAD_NODE: + return "padding node"; + case UBIFS_SB_NODE: + return "superblock node"; + case UBIFS_MST_NODE: + return "master node"; + case UBIFS_REF_NODE: + return "reference node"; + case UBIFS_INO_NODE: + return "inode node"; + case UBIFS_DENT_NODE: + return "direntry node"; + case UBIFS_XENT_NODE: + return "xentry node"; + case UBIFS_DATA_NODE: + return "data node"; + case UBIFS_TRUN_NODE: + return "truncate node"; + case UBIFS_IDX_NODE: + return "indexing node"; + case UBIFS_CS_NODE: + return "commit start node"; + case UBIFS_ORPH_NODE: + return "orphan node"; + default: + return "unknown node"; + } } -const char *dbg_key_str1(const struct ubifs_info *c, const union ubifs_key *key) +static const char *dbg_gtype(int type) { - /* dbg_lock must be held */ - sprintf_key(c, key, dbg_key_buf1); - return dbg_key_buf1; + switch (type) { + case UBIFS_NO_NODE_GROUP: + return "no node group"; + case UBIFS_IN_NODE_GROUP: + return "in node group"; + case UBIFS_LAST_OF_NODE_GROUP: + return "last of node group"; + default: + return "unknown"; + } +} + +const char *dbg_cstate(int cmt_state) +{ + switch (cmt_state) { + case COMMIT_RESTING: + return "commit resting"; + case COMMIT_BACKGROUND: + return "background commit requested"; + case COMMIT_REQUIRED: + return "commit required"; + case COMMIT_RUNNING_BACKGROUND: + return "BACKGROUND commit running"; + case COMMIT_RUNNING_REQUIRED: + return "commit running and required"; + case COMMIT_BROKEN: + return "broken commit"; + default: + return "unknown commit state"; + } +} + +const char *dbg_jhead(int jhead) +{ + switch (jhead) { + case GCHD: + return "0 (GC)"; + case BASEHD: + return "1 (base)"; + case DATAHD: + return "2 (data)"; + default: + return "unknown journal head"; + } +} + +static void dump_ch(const struct ubifs_ch *ch) +{ + pr_err("\tmagic %#x\n", le32_to_cpu(ch->magic)); + pr_err("\tcrc %#x\n", le32_to_cpu(ch->crc)); + pr_err("\tnode_type %d (%s)\n", ch->node_type, + dbg_ntype(ch->node_type)); + pr_err("\tgroup_type %d (%s)\n", ch->group_type, + dbg_gtype(ch->group_type)); + pr_err("\tsqnum %llu\n", + (unsigned long long)le64_to_cpu(ch->sqnum)); + pr_err("\tlen %u\n", le32_to_cpu(ch->len)); +} + +void ubifs_dump_inode(struct ubifs_info *c, const struct inode *inode) +{ +#ifndef __UBOOT__ + const struct ubifs_inode *ui = ubifs_inode(inode); + struct qstr nm = { .name = NULL }; + union ubifs_key key; + struct ubifs_dent_node *dent, *pdent = NULL; + int count = 2; + + pr_err("Dump in-memory inode:"); + pr_err("\tinode %lu\n", inode->i_ino); + pr_err("\tsize %llu\n", + (unsigned long long)i_size_read(inode)); + pr_err("\tnlink %u\n", inode->i_nlink); + pr_err("\tuid %u\n", (unsigned int)i_uid_read(inode)); + pr_err("\tgid %u\n", (unsigned int)i_gid_read(inode)); + pr_err("\tatime %u.%u\n", + (unsigned int)inode->i_atime.tv_sec, + (unsigned int)inode->i_atime.tv_nsec); + pr_err("\tmtime %u.%u\n", + (unsigned int)inode->i_mtime.tv_sec, + (unsigned int)inode->i_mtime.tv_nsec); + pr_err("\tctime %u.%u\n", + (unsigned int)inode->i_ctime.tv_sec, + (unsigned int)inode->i_ctime.tv_nsec); + pr_err("\tcreat_sqnum %llu\n", ui->creat_sqnum); + pr_err("\txattr_size %u\n", ui->xattr_size); + pr_err("\txattr_cnt %u\n", ui->xattr_cnt); + pr_err("\txattr_names %u\n", ui->xattr_names); + pr_err("\tdirty %u\n", ui->dirty); + pr_err("\txattr %u\n", ui->xattr); + pr_err("\tbulk_read %u\n", ui->xattr); + pr_err("\tsynced_i_size %llu\n", + (unsigned long long)ui->synced_i_size); + pr_err("\tui_size %llu\n", + (unsigned long long)ui->ui_size); + pr_err("\tflags %d\n", ui->flags); + pr_err("\tcompr_type %d\n", ui->compr_type); + pr_err("\tlast_page_read %lu\n", ui->last_page_read); + pr_err("\tread_in_a_row %lu\n", ui->read_in_a_row); + pr_err("\tdata_len %d\n", ui->data_len); + + if (!S_ISDIR(inode->i_mode)) + return; + + pr_err("List of directory entries:\n"); + ubifs_assert(!mutex_is_locked(&c->tnc_mutex)); + + lowest_dent_key(c, &key, inode->i_ino); + while (1) { + dent = ubifs_tnc_next_ent(c, &key, &nm); + if (IS_ERR(dent)) { + if (PTR_ERR(dent) != -ENOENT) + pr_err("error %ld\n", PTR_ERR(dent)); + break; + } + + pr_err("\t%d: %s (%s)\n", + count++, dent->name, get_dent_type(dent->type)); + + nm.name = dent->name; + nm.len = le16_to_cpu(dent->nlen); + kfree(pdent); + pdent = dent; + key_read(c, &dent->key, &key); + } + kfree(pdent); +#endif +} + +void ubifs_dump_node(const struct ubifs_info *c, const void *node) +{ + int i, n; + union ubifs_key key; + const struct ubifs_ch *ch = node; + char key_buf[DBG_KEY_BUF_LEN]; + + /* If the magic is incorrect, just hexdump the first bytes */ + if (le32_to_cpu(ch->magic) != UBIFS_NODE_MAGIC) { + pr_err("Not a node, first %zu bytes:", UBIFS_CH_SZ); + print_hex_dump(KERN_ERR, "", DUMP_PREFIX_OFFSET, 32, 1, + (void *)node, UBIFS_CH_SZ, 1); + return; + } + + spin_lock(&dbg_lock); + dump_ch(node); + + switch (ch->node_type) { + case UBIFS_PAD_NODE: + { + const struct ubifs_pad_node *pad = node; + + pr_err("\tpad_len %u\n", le32_to_cpu(pad->pad_len)); + break; + } + case UBIFS_SB_NODE: + { + const struct ubifs_sb_node *sup = node; + unsigned int sup_flags = le32_to_cpu(sup->flags); + + pr_err("\tkey_hash %d (%s)\n", + (int)sup->key_hash, get_key_hash(sup->key_hash)); + pr_err("\tkey_fmt %d (%s)\n", + (int)sup->key_fmt, get_key_fmt(sup->key_fmt)); + pr_err("\tflags %#x\n", sup_flags); + pr_err("\t big_lpt %u\n", + !!(sup_flags & UBIFS_FLG_BIGLPT)); + pr_err("\t space_fixup %u\n", + !!(sup_flags & UBIFS_FLG_SPACE_FIXUP)); + pr_err("\tmin_io_size %u\n", le32_to_cpu(sup->min_io_size)); + pr_err("\tleb_size %u\n", le32_to_cpu(sup->leb_size)); + pr_err("\tleb_cnt %u\n", le32_to_cpu(sup->leb_cnt)); + pr_err("\tmax_leb_cnt %u\n", le32_to_cpu(sup->max_leb_cnt)); + pr_err("\tmax_bud_bytes %llu\n", + (unsigned long long)le64_to_cpu(sup->max_bud_bytes)); + pr_err("\tlog_lebs %u\n", le32_to_cpu(sup->log_lebs)); + pr_err("\tlpt_lebs %u\n", le32_to_cpu(sup->lpt_lebs)); + pr_err("\torph_lebs %u\n", le32_to_cpu(sup->orph_lebs)); + pr_err("\tjhead_cnt %u\n", le32_to_cpu(sup->jhead_cnt)); + pr_err("\tfanout %u\n", le32_to_cpu(sup->fanout)); + pr_err("\tlsave_cnt %u\n", le32_to_cpu(sup->lsave_cnt)); + pr_err("\tdefault_compr %u\n", + (int)le16_to_cpu(sup->default_compr)); + pr_err("\trp_size %llu\n", + (unsigned long long)le64_to_cpu(sup->rp_size)); + pr_err("\trp_uid %u\n", le32_to_cpu(sup->rp_uid)); + pr_err("\trp_gid %u\n", le32_to_cpu(sup->rp_gid)); + pr_err("\tfmt_version %u\n", le32_to_cpu(sup->fmt_version)); + pr_err("\ttime_gran %u\n", le32_to_cpu(sup->time_gran)); + pr_err("\tUUID %pUB\n", sup->uuid); + break; + } + case UBIFS_MST_NODE: + { + const struct ubifs_mst_node *mst = node; + + pr_err("\thighest_inum %llu\n", + (unsigned long long)le64_to_cpu(mst->highest_inum)); + pr_err("\tcommit number %llu\n", + (unsigned long long)le64_to_cpu(mst->cmt_no)); + pr_err("\tflags %#x\n", le32_to_cpu(mst->flags)); + pr_err("\tlog_lnum %u\n", le32_to_cpu(mst->log_lnum)); + pr_err("\troot_lnum %u\n", le32_to_cpu(mst->root_lnum)); + pr_err("\troot_offs %u\n", le32_to_cpu(mst->root_offs)); + pr_err("\troot_len %u\n", le32_to_cpu(mst->root_len)); + pr_err("\tgc_lnum %u\n", le32_to_cpu(mst->gc_lnum)); + pr_err("\tihead_lnum %u\n", le32_to_cpu(mst->ihead_lnum)); + pr_err("\tihead_offs %u\n", le32_to_cpu(mst->ihead_offs)); + pr_err("\tindex_size %llu\n", + (unsigned long long)le64_to_cpu(mst->index_size)); + pr_err("\tlpt_lnum %u\n", le32_to_cpu(mst->lpt_lnum)); + pr_err("\tlpt_offs %u\n", le32_to_cpu(mst->lpt_offs)); + pr_err("\tnhead_lnum %u\n", le32_to_cpu(mst->nhead_lnum)); + pr_err("\tnhead_offs %u\n", le32_to_cpu(mst->nhead_offs)); + pr_err("\tltab_lnum %u\n", le32_to_cpu(mst->ltab_lnum)); + pr_err("\tltab_offs %u\n", le32_to_cpu(mst->ltab_offs)); + pr_err("\tlsave_lnum %u\n", le32_to_cpu(mst->lsave_lnum)); + pr_err("\tlsave_offs %u\n", le32_to_cpu(mst->lsave_offs)); + pr_err("\tlscan_lnum %u\n", le32_to_cpu(mst->lscan_lnum)); + pr_err("\tleb_cnt %u\n", le32_to_cpu(mst->leb_cnt)); + pr_err("\tempty_lebs %u\n", le32_to_cpu(mst->empty_lebs)); + pr_err("\tidx_lebs %u\n", le32_to_cpu(mst->idx_lebs)); + pr_err("\ttotal_free %llu\n", + (unsigned long long)le64_to_cpu(mst->total_free)); + pr_err("\ttotal_dirty %llu\n", + (unsigned long long)le64_to_cpu(mst->total_dirty)); + pr_err("\ttotal_used %llu\n", + (unsigned long long)le64_to_cpu(mst->total_used)); + pr_err("\ttotal_dead %llu\n", + (unsigned long long)le64_to_cpu(mst->total_dead)); + pr_err("\ttotal_dark %llu\n", + (unsigned long long)le64_to_cpu(mst->total_dark)); + break; + } + case UBIFS_REF_NODE: + { + const struct ubifs_ref_node *ref = node; + + pr_err("\tlnum %u\n", le32_to_cpu(ref->lnum)); + pr_err("\toffs %u\n", le32_to_cpu(ref->offs)); + pr_err("\tjhead %u\n", le32_to_cpu(ref->jhead)); + break; + } + case UBIFS_INO_NODE: + { + const struct ubifs_ino_node *ino = node; + + key_read(c, &ino->key, &key); + pr_err("\tkey %s\n", + dbg_snprintf_key(c, &key, key_buf, DBG_KEY_BUF_LEN)); + pr_err("\tcreat_sqnum %llu\n", + (unsigned long long)le64_to_cpu(ino->creat_sqnum)); + pr_err("\tsize %llu\n", + (unsigned long long)le64_to_cpu(ino->size)); + pr_err("\tnlink %u\n", le32_to_cpu(ino->nlink)); + pr_err("\tatime %lld.%u\n", + (long long)le64_to_cpu(ino->atime_sec), + le32_to_cpu(ino->atime_nsec)); + pr_err("\tmtime %lld.%u\n", + (long long)le64_to_cpu(ino->mtime_sec), + le32_to_cpu(ino->mtime_nsec)); + pr_err("\tctime %lld.%u\n", + (long long)le64_to_cpu(ino->ctime_sec), + le32_to_cpu(ino->ctime_nsec)); + pr_err("\tuid %u\n", le32_to_cpu(ino->uid)); + pr_err("\tgid %u\n", le32_to_cpu(ino->gid)); + pr_err("\tmode %u\n", le32_to_cpu(ino->mode)); + pr_err("\tflags %#x\n", le32_to_cpu(ino->flags)); + pr_err("\txattr_cnt %u\n", le32_to_cpu(ino->xattr_cnt)); + pr_err("\txattr_size %u\n", le32_to_cpu(ino->xattr_size)); + pr_err("\txattr_names %u\n", le32_to_cpu(ino->xattr_names)); + pr_err("\tcompr_type %#x\n", + (int)le16_to_cpu(ino->compr_type)); + pr_err("\tdata len %u\n", le32_to_cpu(ino->data_len)); + break; + } + case UBIFS_DENT_NODE: + case UBIFS_XENT_NODE: + { + const struct ubifs_dent_node *dent = node; + int nlen = le16_to_cpu(dent->nlen); + + key_read(c, &dent->key, &key); + pr_err("\tkey %s\n", + dbg_snprintf_key(c, &key, key_buf, DBG_KEY_BUF_LEN)); + pr_err("\tinum %llu\n", + (unsigned long long)le64_to_cpu(dent->inum)); + pr_err("\ttype %d\n", (int)dent->type); + pr_err("\tnlen %d\n", nlen); + pr_err("\tname "); + + if (nlen > UBIFS_MAX_NLEN) + pr_err("(bad name length, not printing, bad or corrupted node)"); + else { + for (i = 0; i < nlen && dent->name[i]; i++) + pr_cont("%c", dent->name[i]); + } + pr_cont("\n"); + + break; + } + case UBIFS_DATA_NODE: + { + const struct ubifs_data_node *dn = node; + int dlen = le32_to_cpu(ch->len) - UBIFS_DATA_NODE_SZ; + + key_read(c, &dn->key, &key); + pr_err("\tkey %s\n", + dbg_snprintf_key(c, &key, key_buf, DBG_KEY_BUF_LEN)); + pr_err("\tsize %u\n", le32_to_cpu(dn->size)); + pr_err("\tcompr_typ %d\n", + (int)le16_to_cpu(dn->compr_type)); + pr_err("\tdata size %d\n", dlen); + pr_err("\tdata:\n"); + print_hex_dump(KERN_ERR, "\t", DUMP_PREFIX_OFFSET, 32, 1, + (void *)&dn->data, dlen, 0); + break; + } + case UBIFS_TRUN_NODE: + { + const struct ubifs_trun_node *trun = node; + + pr_err("\tinum %u\n", le32_to_cpu(trun->inum)); + pr_err("\told_size %llu\n", + (unsigned long long)le64_to_cpu(trun->old_size)); + pr_err("\tnew_size %llu\n", + (unsigned long long)le64_to_cpu(trun->new_size)); + break; + } + case UBIFS_IDX_NODE: + { + const struct ubifs_idx_node *idx = node; + + n = le16_to_cpu(idx->child_cnt); + pr_err("\tchild_cnt %d\n", n); + pr_err("\tlevel %d\n", (int)le16_to_cpu(idx->level)); + pr_err("\tBranches:\n"); + + for (i = 0; i < n && i < c->fanout - 1; i++) { + const struct ubifs_branch *br; + + br = ubifs_idx_branch(c, idx, i); + key_read(c, &br->key, &key); + pr_err("\t%d: LEB %d:%d len %d key %s\n", + i, le32_to_cpu(br->lnum), le32_to_cpu(br->offs), + le32_to_cpu(br->len), + dbg_snprintf_key(c, &key, key_buf, + DBG_KEY_BUF_LEN)); + } + break; + } + case UBIFS_CS_NODE: + break; + case UBIFS_ORPH_NODE: + { + const struct ubifs_orph_node *orph = node; + + pr_err("\tcommit number %llu\n", + (unsigned long long) + le64_to_cpu(orph->cmt_no) & LLONG_MAX); + pr_err("\tlast node flag %llu\n", + (unsigned long long)(le64_to_cpu(orph->cmt_no)) >> 63); + n = (le32_to_cpu(ch->len) - UBIFS_ORPH_NODE_SZ) >> 3; + pr_err("\t%d orphan inode numbers:\n", n); + for (i = 0; i < n; i++) + pr_err("\t ino %llu\n", + (unsigned long long)le64_to_cpu(orph->inos[i])); + break; + } + default: + pr_err("node type %d was not recognized\n", + (int)ch->node_type); + } + spin_unlock(&dbg_lock); +} + +void ubifs_dump_budget_req(const struct ubifs_budget_req *req) +{ + spin_lock(&dbg_lock); + pr_err("Budgeting request: new_ino %d, dirtied_ino %d\n", + req->new_ino, req->dirtied_ino); + pr_err("\tnew_ino_d %d, dirtied_ino_d %d\n", + req->new_ino_d, req->dirtied_ino_d); + pr_err("\tnew_page %d, dirtied_page %d\n", + req->new_page, req->dirtied_page); + pr_err("\tnew_dent %d, mod_dent %d\n", + req->new_dent, req->mod_dent); + pr_err("\tidx_growth %d\n", req->idx_growth); + pr_err("\tdata_growth %d dd_growth %d\n", + req->data_growth, req->dd_growth); + spin_unlock(&dbg_lock); +} + +void ubifs_dump_lstats(const struct ubifs_lp_stats *lst) +{ + spin_lock(&dbg_lock); + pr_err("(pid %d) Lprops statistics: empty_lebs %d, idx_lebs %d\n", + current->pid, lst->empty_lebs, lst->idx_lebs); + pr_err("\ttaken_empty_lebs %d, total_free %lld, total_dirty %lld\n", + lst->taken_empty_lebs, lst->total_free, lst->total_dirty); + pr_err("\ttotal_used %lld, total_dark %lld, total_dead %lld\n", + lst->total_used, lst->total_dark, lst->total_dead); + spin_unlock(&dbg_lock); +} + +#ifndef __UBOOT__ +void ubifs_dump_budg(struct ubifs_info *c, const struct ubifs_budg_info *bi) +{ + int i; + struct rb_node *rb; + struct ubifs_bud *bud; + struct ubifs_gced_idx_leb *idx_gc; + long long available, outstanding, free; + + spin_lock(&c->space_lock); + spin_lock(&dbg_lock); + pr_err("(pid %d) Budgeting info: data budget sum %lld, total budget sum %lld\n", + current->pid, bi->data_growth + bi->dd_growth, + bi->data_growth + bi->dd_growth + bi->idx_growth); + pr_err("\tbudg_data_growth %lld, budg_dd_growth %lld, budg_idx_growth %lld\n", + bi->data_growth, bi->dd_growth, bi->idx_growth); + pr_err("\tmin_idx_lebs %d, old_idx_sz %llu, uncommitted_idx %lld\n", + bi->min_idx_lebs, bi->old_idx_sz, bi->uncommitted_idx); + pr_err("\tpage_budget %d, inode_budget %d, dent_budget %d\n", + bi->page_budget, bi->inode_budget, bi->dent_budget); + pr_err("\tnospace %u, nospace_rp %u\n", bi->nospace, bi->nospace_rp); + pr_err("\tdark_wm %d, dead_wm %d, max_idx_node_sz %d\n", + c->dark_wm, c->dead_wm, c->max_idx_node_sz); + + if (bi != &c->bi) + /* + * If we are dumping saved budgeting data, do not print + * additional information which is about the current state, not + * the old one which corresponded to the saved budgeting data. + */ + goto out_unlock; + + pr_err("\tfreeable_cnt %d, calc_idx_sz %lld, idx_gc_cnt %d\n", + c->freeable_cnt, c->calc_idx_sz, c->idx_gc_cnt); + pr_err("\tdirty_pg_cnt %ld, dirty_zn_cnt %ld, clean_zn_cnt %ld\n", + atomic_long_read(&c->dirty_pg_cnt), + atomic_long_read(&c->dirty_zn_cnt), + atomic_long_read(&c->clean_zn_cnt)); + pr_err("\tgc_lnum %d, ihead_lnum %d\n", c->gc_lnum, c->ihead_lnum); + + /* If we are in R/O mode, journal heads do not exist */ + if (c->jheads) + for (i = 0; i < c->jhead_cnt; i++) + pr_err("\tjhead %s\t LEB %d\n", + dbg_jhead(c->jheads[i].wbuf.jhead), + c->jheads[i].wbuf.lnum); + for (rb = rb_first(&c->buds); rb; rb = rb_next(rb)) { + bud = rb_entry(rb, struct ubifs_bud, rb); + pr_err("\tbud LEB %d\n", bud->lnum); + } + list_for_each_entry(bud, &c->old_buds, list) + pr_err("\told bud LEB %d\n", bud->lnum); + list_for_each_entry(idx_gc, &c->idx_gc, list) + pr_err("\tGC'ed idx LEB %d unmap %d\n", + idx_gc->lnum, idx_gc->unmap); + pr_err("\tcommit state %d\n", c->cmt_state); + + /* Print budgeting predictions */ + available = ubifs_calc_available(c, c->bi.min_idx_lebs); + outstanding = c->bi.data_growth + c->bi.dd_growth; + free = ubifs_get_free_space_nolock(c); + pr_err("Budgeting predictions:\n"); + pr_err("\tavailable: %lld, outstanding %lld, free %lld\n", + available, outstanding, free); +out_unlock: + spin_unlock(&dbg_lock); + spin_unlock(&c->space_lock); +} +#else +void ubifs_dump_budg(struct ubifs_info *c, const struct ubifs_budg_info *bi) +{ +} +#endif + +void ubifs_dump_lprop(const struct ubifs_info *c, const struct ubifs_lprops *lp) +{ + int i, spc, dark = 0, dead = 0; + struct rb_node *rb; + struct ubifs_bud *bud; + + spc = lp->free + lp->dirty; + if (spc < c->dead_wm) + dead = spc; + else + dark = ubifs_calc_dark(c, spc); + + if (lp->flags & LPROPS_INDEX) + pr_err("LEB %-7d free %-8d dirty %-8d used %-8d free + dirty %-8d flags %#x (", + lp->lnum, lp->free, lp->dirty, c->leb_size - spc, spc, + lp->flags); + else + pr_err("LEB %-7d free %-8d dirty %-8d used %-8d free + dirty %-8d dark %-4d dead %-4d nodes fit %-3d flags %#-4x (", + lp->lnum, lp->free, lp->dirty, c->leb_size - spc, spc, + dark, dead, (int)(spc / UBIFS_MAX_NODE_SZ), lp->flags); + + if (lp->flags & LPROPS_TAKEN) { + if (lp->flags & LPROPS_INDEX) + pr_cont("index, taken"); + else + pr_cont("taken"); + } else { + const char *s; + + if (lp->flags & LPROPS_INDEX) { + switch (lp->flags & LPROPS_CAT_MASK) { + case LPROPS_DIRTY_IDX: + s = "dirty index"; + break; + case LPROPS_FRDI_IDX: + s = "freeable index"; + break; + default: + s = "index"; + } + } else { + switch (lp->flags & LPROPS_CAT_MASK) { + case LPROPS_UNCAT: + s = "not categorized"; + break; + case LPROPS_DIRTY: + s = "dirty"; + break; + case LPROPS_FREE: + s = "free"; + break; + case LPROPS_EMPTY: + s = "empty"; + break; + case LPROPS_FREEABLE: + s = "freeable"; + break; + default: + s = NULL; + break; + } + } + pr_cont("%s", s); + } + + for (rb = rb_first((struct rb_root *)&c->buds); rb; rb = rb_next(rb)) { + bud = rb_entry(rb, struct ubifs_bud, rb); + if (bud->lnum == lp->lnum) { + int head = 0; + for (i = 0; i < c->jhead_cnt; i++) { + /* + * Note, if we are in R/O mode or in the middle + * of mounting/re-mounting, the write-buffers do + * not exist. + */ + if (c->jheads && + lp->lnum == c->jheads[i].wbuf.lnum) { + pr_cont(", jhead %s", dbg_jhead(i)); + head = 1; + } + } + if (!head) + pr_cont(", bud of jhead %s", + dbg_jhead(bud->jhead)); + } + } + if (lp->lnum == c->gc_lnum) + pr_cont(", GC LEB"); + pr_cont(")\n"); +} + +void ubifs_dump_lprops(struct ubifs_info *c) +{ + int lnum, err; + struct ubifs_lprops lp; + struct ubifs_lp_stats lst; + + pr_err("(pid %d) start dumping LEB properties\n", current->pid); + ubifs_get_lp_stats(c, &lst); + ubifs_dump_lstats(&lst); + + for (lnum = c->main_first; lnum < c->leb_cnt; lnum++) { + err = ubifs_read_one_lp(c, lnum, &lp); + if (err) + ubifs_err("cannot read lprops for LEB %d", lnum); + + ubifs_dump_lprop(c, &lp); + } + pr_err("(pid %d) finish dumping LEB properties\n", current->pid); +} + +void ubifs_dump_lpt_info(struct ubifs_info *c) +{ + int i; + + spin_lock(&dbg_lock); + pr_err("(pid %d) dumping LPT information\n", current->pid); + pr_err("\tlpt_sz: %lld\n", c->lpt_sz); + pr_err("\tpnode_sz: %d\n", c->pnode_sz); + pr_err("\tnnode_sz: %d\n", c->nnode_sz); + pr_err("\tltab_sz: %d\n", c->ltab_sz); + pr_err("\tlsave_sz: %d\n", c->lsave_sz); + pr_err("\tbig_lpt: %d\n", c->big_lpt); + pr_err("\tlpt_hght: %d\n", c->lpt_hght); + pr_err("\tpnode_cnt: %d\n", c->pnode_cnt); + pr_err("\tnnode_cnt: %d\n", c->nnode_cnt); + pr_err("\tdirty_pn_cnt: %d\n", c->dirty_pn_cnt); + pr_err("\tdirty_nn_cnt: %d\n", c->dirty_nn_cnt); + pr_err("\tlsave_cnt: %d\n", c->lsave_cnt); + pr_err("\tspace_bits: %d\n", c->space_bits); + pr_err("\tlpt_lnum_bits: %d\n", c->lpt_lnum_bits); + pr_err("\tlpt_offs_bits: %d\n", c->lpt_offs_bits); + pr_err("\tlpt_spc_bits: %d\n", c->lpt_spc_bits); + pr_err("\tpcnt_bits: %d\n", c->pcnt_bits); + pr_err("\tlnum_bits: %d\n", c->lnum_bits); + pr_err("\tLPT root is at %d:%d\n", c->lpt_lnum, c->lpt_offs); + pr_err("\tLPT head is at %d:%d\n", + c->nhead_lnum, c->nhead_offs); + pr_err("\tLPT ltab is at %d:%d\n", c->ltab_lnum, c->ltab_offs); + if (c->big_lpt) + pr_err("\tLPT lsave is at %d:%d\n", + c->lsave_lnum, c->lsave_offs); + for (i = 0; i < c->lpt_lebs; i++) + pr_err("\tLPT LEB %d free %d dirty %d tgc %d cmt %d\n", + i + c->lpt_first, c->ltab[i].free, c->ltab[i].dirty, + c->ltab[i].tgc, c->ltab[i].cmt); + spin_unlock(&dbg_lock); +} + +void ubifs_dump_sleb(const struct ubifs_info *c, + const struct ubifs_scan_leb *sleb, int offs) +{ + struct ubifs_scan_node *snod; + + pr_err("(pid %d) start dumping scanned data from LEB %d:%d\n", + current->pid, sleb->lnum, offs); + + list_for_each_entry(snod, &sleb->nodes, list) { + cond_resched(); + pr_err("Dumping node at LEB %d:%d len %d\n", + sleb->lnum, snod->offs, snod->len); + ubifs_dump_node(c, snod->node); + } +} + +void ubifs_dump_leb(const struct ubifs_info *c, int lnum) +{ + struct ubifs_scan_leb *sleb; + struct ubifs_scan_node *snod; + void *buf; + + pr_err("(pid %d) start dumping LEB %d\n", current->pid, lnum); + + buf = __vmalloc(c->leb_size, GFP_NOFS, PAGE_KERNEL); + if (!buf) { + ubifs_err("cannot allocate memory for dumping LEB %d", lnum); + return; + } + + sleb = ubifs_scan(c, lnum, 0, buf, 0); + if (IS_ERR(sleb)) { + ubifs_err("scan error %d", (int)PTR_ERR(sleb)); + goto out; + } + + pr_err("LEB %d has %d nodes ending at %d\n", lnum, + sleb->nodes_cnt, sleb->endpt); + + list_for_each_entry(snod, &sleb->nodes, list) { + cond_resched(); + pr_err("Dumping node at LEB %d:%d len %d\n", lnum, + snod->offs, snod->len); + ubifs_dump_node(c, snod->node); + } + + pr_err("(pid %d) finish dumping LEB %d\n", current->pid, lnum); + ubifs_scan_destroy(sleb); + +out: + vfree(buf); + return; +} + +void ubifs_dump_znode(const struct ubifs_info *c, + const struct ubifs_znode *znode) +{ + int n; + const struct ubifs_zbranch *zbr; + char key_buf[DBG_KEY_BUF_LEN]; + + spin_lock(&dbg_lock); + if (znode->parent) + zbr = &znode->parent->zbranch[znode->iip]; + else + zbr = &c->zroot; + + pr_err("znode %p, LEB %d:%d len %d parent %p iip %d level %d child_cnt %d flags %lx\n", + znode, zbr->lnum, zbr->offs, zbr->len, znode->parent, znode->iip, + znode->level, znode->child_cnt, znode->flags); + + if (znode->child_cnt <= 0 || znode->child_cnt > c->fanout) { + spin_unlock(&dbg_lock); + return; + } + + pr_err("zbranches:\n"); + for (n = 0; n < znode->child_cnt; n++) { + zbr = &znode->zbranch[n]; + if (znode->level > 0) + pr_err("\t%d: znode %p LEB %d:%d len %d key %s\n", + n, zbr->znode, zbr->lnum, zbr->offs, zbr->len, + dbg_snprintf_key(c, &zbr->key, key_buf, + DBG_KEY_BUF_LEN)); + else + pr_err("\t%d: LNC %p LEB %d:%d len %d key %s\n", + n, zbr->znode, zbr->lnum, zbr->offs, zbr->len, + dbg_snprintf_key(c, &zbr->key, key_buf, + DBG_KEY_BUF_LEN)); + } + spin_unlock(&dbg_lock); +} + +void ubifs_dump_heap(struct ubifs_info *c, struct ubifs_lpt_heap *heap, int cat) +{ + int i; + + pr_err("(pid %d) start dumping heap cat %d (%d elements)\n", + current->pid, cat, heap->cnt); + for (i = 0; i < heap->cnt; i++) { + struct ubifs_lprops *lprops = heap->arr[i]; + + pr_err("\t%d. LEB %d hpos %d free %d dirty %d flags %d\n", + i, lprops->lnum, lprops->hpos, lprops->free, + lprops->dirty, lprops->flags); + } + pr_err("(pid %d) finish dumping heap\n", current->pid); +} + +void ubifs_dump_pnode(struct ubifs_info *c, struct ubifs_pnode *pnode, + struct ubifs_nnode *parent, int iip) +{ + int i; + + pr_err("(pid %d) dumping pnode:\n", current->pid); + pr_err("\taddress %zx parent %zx cnext %zx\n", + (size_t)pnode, (size_t)parent, (size_t)pnode->cnext); + pr_err("\tflags %lu iip %d level %d num %d\n", + pnode->flags, iip, pnode->level, pnode->num); + for (i = 0; i < UBIFS_LPT_FANOUT; i++) { + struct ubifs_lprops *lp = &pnode->lprops[i]; + + pr_err("\t%d: free %d dirty %d flags %d lnum %d\n", + i, lp->free, lp->dirty, lp->flags, lp->lnum); + } +} + +void ubifs_dump_tnc(struct ubifs_info *c) +{ + struct ubifs_znode *znode; + int level; + + pr_err("\n"); + pr_err("(pid %d) start dumping TNC tree\n", current->pid); + znode = ubifs_tnc_levelorder_next(c->zroot.znode, NULL); + level = znode->level; + pr_err("== Level %d ==\n", level); + while (znode) { + if (level != znode->level) { + level = znode->level; + pr_err("== Level %d ==\n", level); + } + ubifs_dump_znode(c, znode); + znode = ubifs_tnc_levelorder_next(c->zroot.znode, znode); + } + pr_err("(pid %d) finish dumping TNC tree\n", current->pid); +} + +static int dump_znode(struct ubifs_info *c, struct ubifs_znode *znode, + void *priv) +{ + ubifs_dump_znode(c, znode); + return 0; } /** - * ubifs_debugging_init - initialize UBIFS debugging. + * ubifs_dump_index - dump the on-flash index. * @c: UBIFS file-system description object * - * This function initializes debugging-related data for the file system. + * This function dumps whole UBIFS indexing B-tree, unlike 'ubifs_dump_tnc()' + * which dumps only in-memory znodes and does not read znodes which from flash. + */ +void ubifs_dump_index(struct ubifs_info *c) +{ + dbg_walk_index(c, NULL, dump_znode, NULL); +} + +#ifndef __UBOOT__ +/** + * dbg_save_space_info - save information about flash space. + * @c: UBIFS file-system description object + * + * This function saves information about UBIFS free space, dirty space, etc, in + * order to check it later. + */ +void dbg_save_space_info(struct ubifs_info *c) +{ + struct ubifs_debug_info *d = c->dbg; + int freeable_cnt; + + spin_lock(&c->space_lock); + memcpy(&d->saved_lst, &c->lst, sizeof(struct ubifs_lp_stats)); + memcpy(&d->saved_bi, &c->bi, sizeof(struct ubifs_budg_info)); + d->saved_idx_gc_cnt = c->idx_gc_cnt; + + /* + * We use a dirty hack here and zero out @c->freeable_cnt, because it + * affects the free space calculations, and UBIFS might not know about + * all freeable eraseblocks. Indeed, we know about freeable eraseblocks + * only when we read their lprops, and we do this only lazily, upon the + * need. So at any given point of time @c->freeable_cnt might be not + * exactly accurate. + * + * Just one example about the issue we hit when we did not zero + * @c->freeable_cnt. + * 1. The file-system is mounted R/O, c->freeable_cnt is %0. We save the + * amount of free space in @d->saved_free + * 2. We re-mount R/W, which makes UBIFS to read the "lsave" + * information from flash, where we cache LEBs from various + * categories ('ubifs_remount_fs()' -> 'ubifs_lpt_init()' + * -> 'lpt_init_wr()' -> 'read_lsave()' -> 'ubifs_lpt_lookup()' + * -> 'ubifs_get_pnode()' -> 'update_cats()' + * -> 'ubifs_add_to_cat()'). + * 3. Lsave contains a freeable eraseblock, and @c->freeable_cnt + * becomes %1. + * 4. We calculate the amount of free space when the re-mount is + * finished in 'dbg_check_space_info()' and it does not match + * @d->saved_free. + */ + freeable_cnt = c->freeable_cnt; + c->freeable_cnt = 0; + d->saved_free = ubifs_get_free_space_nolock(c); + c->freeable_cnt = freeable_cnt; + spin_unlock(&c->space_lock); +} + +/** + * dbg_check_space_info - check flash space information. + * @c: UBIFS file-system description object + * + * This function compares current flash space information with the information + * which was saved when the 'dbg_save_space_info()' function was called. + * Returns zero if the information has not changed, and %-EINVAL it it has + * changed. + */ +int dbg_check_space_info(struct ubifs_info *c) +{ + struct ubifs_debug_info *d = c->dbg; + struct ubifs_lp_stats lst; + long long free; + int freeable_cnt; + + spin_lock(&c->space_lock); + freeable_cnt = c->freeable_cnt; + c->freeable_cnt = 0; + free = ubifs_get_free_space_nolock(c); + c->freeable_cnt = freeable_cnt; + spin_unlock(&c->space_lock); + + if (free != d->saved_free) { + ubifs_err("free space changed from %lld to %lld", + d->saved_free, free); + goto out; + } + + return 0; + +out: + ubifs_msg("saved lprops statistics dump"); + ubifs_dump_lstats(&d->saved_lst); + ubifs_msg("saved budgeting info dump"); + ubifs_dump_budg(c, &d->saved_bi); + ubifs_msg("saved idx_gc_cnt %d", d->saved_idx_gc_cnt); + ubifs_msg("current lprops statistics dump"); + ubifs_get_lp_stats(c, &lst); + ubifs_dump_lstats(&lst); + ubifs_msg("current budgeting info dump"); + ubifs_dump_budg(c, &c->bi); + dump_stack(); + return -EINVAL; +} + +/** + * dbg_check_synced_i_size - check synchronized inode size. + * @c: UBIFS file-system description object + * @inode: inode to check + * + * If inode is clean, synchronized inode size has to be equivalent to current + * inode size. This function has to be called only for locked inodes (@i_mutex + * has to be locked). Returns %0 if synchronized inode size if correct, and + * %-EINVAL if not. + */ +int dbg_check_synced_i_size(const struct ubifs_info *c, struct inode *inode) +{ + int err = 0; + struct ubifs_inode *ui = ubifs_inode(inode); + + if (!dbg_is_chk_gen(c)) + return 0; + if (!S_ISREG(inode->i_mode)) + return 0; + + mutex_lock(&ui->ui_mutex); + spin_lock(&ui->ui_lock); + if (ui->ui_size != ui->synced_i_size && !ui->dirty) { + ubifs_err("ui_size is %lld, synced_i_size is %lld, but inode is clean", + ui->ui_size, ui->synced_i_size); + ubifs_err("i_ino %lu, i_mode %#x, i_size %lld", inode->i_ino, + inode->i_mode, i_size_read(inode)); + dump_stack(); + err = -EINVAL; + } + spin_unlock(&ui->ui_lock); + mutex_unlock(&ui->ui_mutex); + return err; +} + +/* + * dbg_check_dir - check directory inode size and link count. + * @c: UBIFS file-system description object + * @dir: the directory to calculate size for + * @size: the result is returned here + * + * This function makes sure that directory size and link count are correct. * Returns zero in case of success and a negative error code in case of * failure. + * + * Note, it is good idea to make sure the @dir->i_mutex is locked before + * calling this function. + */ +int dbg_check_dir(struct ubifs_info *c, const struct inode *dir) +{ + unsigned int nlink = 2; + union ubifs_key key; + struct ubifs_dent_node *dent, *pdent = NULL; + struct qstr nm = { .name = NULL }; + loff_t size = UBIFS_INO_NODE_SZ; + + if (!dbg_is_chk_gen(c)) + return 0; + + if (!S_ISDIR(dir->i_mode)) + return 0; + + lowest_dent_key(c, &key, dir->i_ino); + while (1) { + int err; + + dent = ubifs_tnc_next_ent(c, &key, &nm); + if (IS_ERR(dent)) { + err = PTR_ERR(dent); + if (err == -ENOENT) + break; + return err; + } + + nm.name = dent->name; + nm.len = le16_to_cpu(dent->nlen); + size += CALC_DENT_SIZE(nm.len); + if (dent->type == UBIFS_ITYPE_DIR) + nlink += 1; + kfree(pdent); + pdent = dent; + key_read(c, &dent->key, &key); + } + kfree(pdent); + + if (i_size_read(dir) != size) { + ubifs_err("directory inode %lu has size %llu, but calculated size is %llu", + dir->i_ino, (unsigned long long)i_size_read(dir), + (unsigned long long)size); + ubifs_dump_inode(c, dir); + dump_stack(); + return -EINVAL; + } + if (dir->i_nlink != nlink) { + ubifs_err("directory inode %lu has nlink %u, but calculated nlink is %u", + dir->i_ino, dir->i_nlink, nlink); + ubifs_dump_inode(c, dir); + dump_stack(); + return -EINVAL; + } + + return 0; +} + +/** + * dbg_check_key_order - make sure that colliding keys are properly ordered. + * @c: UBIFS file-system description object + * @zbr1: first zbranch + * @zbr2: following zbranch + * + * In UBIFS indexing B-tree colliding keys has to be sorted in binary order of + * names of the direntries/xentries which are referred by the keys. This + * function reads direntries/xentries referred by @zbr1 and @zbr2 and makes + * sure the name of direntry/xentry referred by @zbr1 is less than + * direntry/xentry referred by @zbr2. Returns zero if this is true, %1 if not, + * and a negative error code in case of failure. */ +static int dbg_check_key_order(struct ubifs_info *c, struct ubifs_zbranch *zbr1, + struct ubifs_zbranch *zbr2) +{ + int err, nlen1, nlen2, cmp; + struct ubifs_dent_node *dent1, *dent2; + union ubifs_key key; + char key_buf[DBG_KEY_BUF_LEN]; + + ubifs_assert(!keys_cmp(c, &zbr1->key, &zbr2->key)); + dent1 = kmalloc(UBIFS_MAX_DENT_NODE_SZ, GFP_NOFS); + if (!dent1) + return -ENOMEM; + dent2 = kmalloc(UBIFS_MAX_DENT_NODE_SZ, GFP_NOFS); + if (!dent2) { + err = -ENOMEM; + goto out_free; + } + + err = ubifs_tnc_read_node(c, zbr1, dent1); + if (err) + goto out_free; + err = ubifs_validate_entry(c, dent1); + if (err) + goto out_free; + + err = ubifs_tnc_read_node(c, zbr2, dent2); + if (err) + goto out_free; + err = ubifs_validate_entry(c, dent2); + if (err) + goto out_free; + + /* Make sure node keys are the same as in zbranch */ + err = 1; + key_read(c, &dent1->key, &key); + if (keys_cmp(c, &zbr1->key, &key)) { + ubifs_err("1st entry at %d:%d has key %s", zbr1->lnum, + zbr1->offs, dbg_snprintf_key(c, &key, key_buf, + DBG_KEY_BUF_LEN)); + ubifs_err("but it should have key %s according to tnc", + dbg_snprintf_key(c, &zbr1->key, key_buf, + DBG_KEY_BUF_LEN)); + ubifs_dump_node(c, dent1); + goto out_free; + } + + key_read(c, &dent2->key, &key); + if (keys_cmp(c, &zbr2->key, &key)) { + ubifs_err("2nd entry at %d:%d has key %s", zbr1->lnum, + zbr1->offs, dbg_snprintf_key(c, &key, key_buf, + DBG_KEY_BUF_LEN)); + ubifs_err("but it should have key %s according to tnc", + dbg_snprintf_key(c, &zbr2->key, key_buf, + DBG_KEY_BUF_LEN)); + ubifs_dump_node(c, dent2); + goto out_free; + } + + nlen1 = le16_to_cpu(dent1->nlen); + nlen2 = le16_to_cpu(dent2->nlen); + + cmp = memcmp(dent1->name, dent2->name, min_t(int, nlen1, nlen2)); + if (cmp < 0 || (cmp == 0 && nlen1 < nlen2)) { + err = 0; + goto out_free; + } + if (cmp == 0 && nlen1 == nlen2) + ubifs_err("2 xent/dent nodes with the same name"); + else + ubifs_err("bad order of colliding key %s", + dbg_snprintf_key(c, &key, key_buf, DBG_KEY_BUF_LEN)); + + ubifs_msg("first node at %d:%d\n", zbr1->lnum, zbr1->offs); + ubifs_dump_node(c, dent1); + ubifs_msg("second node at %d:%d\n", zbr2->lnum, zbr2->offs); + ubifs_dump_node(c, dent2); + +out_free: + kfree(dent2); + kfree(dent1); + return err; +} + +/** + * dbg_check_znode - check if znode is all right. + * @c: UBIFS file-system description object + * @zbr: zbranch which points to this znode + * + * This function makes sure that znode referred to by @zbr is all right. + * Returns zero if it is, and %-EINVAL if it is not. + */ +static int dbg_check_znode(struct ubifs_info *c, struct ubifs_zbranch *zbr) +{ + struct ubifs_znode *znode = zbr->znode; + struct ubifs_znode *zp = znode->parent; + int n, err, cmp; + + if (znode->child_cnt <= 0 || znode->child_cnt > c->fanout) { + err = 1; + goto out; + } + if (znode->level < 0) { + err = 2; + goto out; + } + if (znode->iip < 0 || znode->iip >= c->fanout) { + err = 3; + goto out; + } + + if (zbr->len == 0) + /* Only dirty zbranch may have no on-flash nodes */ + if (!ubifs_zn_dirty(znode)) { + err = 4; + goto out; + } + + if (ubifs_zn_dirty(znode)) { + /* + * If znode is dirty, its parent has to be dirty as well. The + * order of the operation is important, so we have to have + * memory barriers. + */ + smp_mb(); + if (zp && !ubifs_zn_dirty(zp)) { + /* + * The dirty flag is atomic and is cleared outside the + * TNC mutex, so znode's dirty flag may now have + * been cleared. The child is always cleared before the + * parent, so we just need to check again. + */ + smp_mb(); + if (ubifs_zn_dirty(znode)) { + err = 5; + goto out; + } + } + } + + if (zp) { + const union ubifs_key *min, *max; + + if (znode->level != zp->level - 1) { + err = 6; + goto out; + } + + /* Make sure the 'parent' pointer in our znode is correct */ + err = ubifs_search_zbranch(c, zp, &zbr->key, &n); + if (!err) { + /* This zbranch does not exist in the parent */ + err = 7; + goto out; + } + + if (znode->iip >= zp->child_cnt) { + err = 8; + goto out; + } + + if (znode->iip != n) { + /* This may happen only in case of collisions */ + if (keys_cmp(c, &zp->zbranch[n].key, + &zp->zbranch[znode->iip].key)) { + err = 9; + goto out; + } + n = znode->iip; + } + + /* + * Make sure that the first key in our znode is greater than or + * equal to the key in the pointing zbranch. + */ + min = &zbr->key; + cmp = keys_cmp(c, min, &znode->zbranch[0].key); + if (cmp == 1) { + err = 10; + goto out; + } + + if (n + 1 < zp->child_cnt) { + max = &zp->zbranch[n + 1].key; + + /* + * Make sure the last key in our znode is less or + * equivalent than the key in the zbranch which goes + * after our pointing zbranch. + */ + cmp = keys_cmp(c, max, + &znode->zbranch[znode->child_cnt - 1].key); + if (cmp == -1) { + err = 11; + goto out; + } + } + } else { + /* This may only be root znode */ + if (zbr != &c->zroot) { + err = 12; + goto out; + } + } + + /* + * Make sure that next key is greater or equivalent then the previous + * one. + */ + for (n = 1; n < znode->child_cnt; n++) { + cmp = keys_cmp(c, &znode->zbranch[n - 1].key, + &znode->zbranch[n].key); + if (cmp > 0) { + err = 13; + goto out; + } + if (cmp == 0) { + /* This can only be keys with colliding hash */ + if (!is_hash_key(c, &znode->zbranch[n].key)) { + err = 14; + goto out; + } + + if (znode->level != 0 || c->replaying) + continue; + + /* + * Colliding keys should follow binary order of + * corresponding xentry/dentry names. + */ + err = dbg_check_key_order(c, &znode->zbranch[n - 1], + &znode->zbranch[n]); + if (err < 0) + return err; + if (err) { + err = 15; + goto out; + } + } + } + + for (n = 0; n < znode->child_cnt; n++) { + if (!znode->zbranch[n].znode && + (znode->zbranch[n].lnum == 0 || + znode->zbranch[n].len == 0)) { + err = 16; + goto out; + } + + if (znode->zbranch[n].lnum != 0 && + znode->zbranch[n].len == 0) { + err = 17; + goto out; + } + + if (znode->zbranch[n].lnum == 0 && + znode->zbranch[n].len != 0) { + err = 18; + goto out; + } + + if (znode->zbranch[n].lnum == 0 && + znode->zbranch[n].offs != 0) { + err = 19; + goto out; + } + + if (znode->level != 0 && znode->zbranch[n].znode) + if (znode->zbranch[n].znode->parent != znode) { + err = 20; + goto out; + } + } + + return 0; + +out: + ubifs_err("failed, error %d", err); + ubifs_msg("dump of the znode"); + ubifs_dump_znode(c, znode); + if (zp) { + ubifs_msg("dump of the parent znode"); + ubifs_dump_znode(c, zp); + } + dump_stack(); + return -EINVAL; +} +#else + +int dbg_check_dir(struct ubifs_info *c, const struct inode *dir) +{ + return 0; +} + +void dbg_debugfs_exit_fs(struct ubifs_info *c) +{ + return; +} + int ubifs_debugging_init(struct ubifs_info *c) { - c->dbg = kzalloc(sizeof(struct ubifs_debug_info), GFP_KERNEL); - if (!c->dbg) + return 0; +} +void ubifs_debugging_exit(struct ubifs_info *c) +{ +} +int dbg_check_filesystem(struct ubifs_info *c) +{ + return 0; +} +int dbg_debugfs_init_fs(struct ubifs_info *c) +{ + return 0; +} +#endif + +#ifndef __UBOOT__ +/** + * dbg_check_tnc - check TNC tree. + * @c: UBIFS file-system description object + * @extra: do extra checks that are possible at start commit + * + * This function traverses whole TNC tree and checks every znode. Returns zero + * if everything is all right and %-EINVAL if something is wrong with TNC. + */ +int dbg_check_tnc(struct ubifs_info *c, int extra) +{ + struct ubifs_znode *znode; + long clean_cnt = 0, dirty_cnt = 0; + int err, last; + + if (!dbg_is_chk_index(c)) + return 0; + + ubifs_assert(mutex_is_locked(&c->tnc_mutex)); + if (!c->zroot.znode) + return 0; + + znode = ubifs_tnc_postorder_first(c->zroot.znode); + while (1) { + struct ubifs_znode *prev; + struct ubifs_zbranch *zbr; + + if (!znode->parent) + zbr = &c->zroot; + else + zbr = &znode->parent->zbranch[znode->iip]; + + err = dbg_check_znode(c, zbr); + if (err) + return err; + + if (extra) { + if (ubifs_zn_dirty(znode)) + dirty_cnt += 1; + else + clean_cnt += 1; + } + + prev = znode; + znode = ubifs_tnc_postorder_next(znode); + if (!znode) + break; + + /* + * If the last key of this znode is equivalent to the first key + * of the next znode (collision), then check order of the keys. + */ + last = prev->child_cnt - 1; + if (prev->level == 0 && znode->level == 0 && !c->replaying && + !keys_cmp(c, &prev->zbranch[last].key, + &znode->zbranch[0].key)) { + err = dbg_check_key_order(c, &prev->zbranch[last], + &znode->zbranch[0]); + if (err < 0) + return err; + if (err) { + ubifs_msg("first znode"); + ubifs_dump_znode(c, prev); + ubifs_msg("second znode"); + ubifs_dump_znode(c, znode); + return -EINVAL; + } + } + } + + if (extra) { + if (clean_cnt != atomic_long_read(&c->clean_zn_cnt)) { + ubifs_err("incorrect clean_zn_cnt %ld, calculated %ld", + atomic_long_read(&c->clean_zn_cnt), + clean_cnt); + return -EINVAL; + } + if (dirty_cnt != atomic_long_read(&c->dirty_zn_cnt)) { + ubifs_err("incorrect dirty_zn_cnt %ld, calculated %ld", + atomic_long_read(&c->dirty_zn_cnt), + dirty_cnt); + return -EINVAL; + } + } + + return 0; +} +#else +int dbg_check_tnc(struct ubifs_info *c, int extra) +{ + return 0; +} +#endif + +/** + * dbg_walk_index - walk the on-flash index. + * @c: UBIFS file-system description object + * @leaf_cb: called for each leaf node + * @znode_cb: called for each indexing node + * @priv: private data which is passed to callbacks + * + * This function walks the UBIFS index and calls the @leaf_cb for each leaf + * node and @znode_cb for each indexing node. Returns zero in case of success + * and a negative error code in case of failure. + * + * It would be better if this function removed every znode it pulled to into + * the TNC, so that the behavior more closely matched the non-debugging + * behavior. + */ +int dbg_walk_index(struct ubifs_info *c, dbg_leaf_callback leaf_cb, + dbg_znode_callback znode_cb, void *priv) +{ + int err; + struct ubifs_zbranch *zbr; + struct ubifs_znode *znode, *child; + + mutex_lock(&c->tnc_mutex); + /* If the root indexing node is not in TNC - pull it */ + if (!c->zroot.znode) { + c->zroot.znode = ubifs_load_znode(c, &c->zroot, NULL, 0); + if (IS_ERR(c->zroot.znode)) { + err = PTR_ERR(c->zroot.znode); + c->zroot.znode = NULL; + goto out_unlock; + } + } + + /* + * We are going to traverse the indexing tree in the postorder manner. + * Go down and find the leftmost indexing node where we are going to + * start from. + */ + znode = c->zroot.znode; + while (znode->level > 0) { + zbr = &znode->zbranch[0]; + child = zbr->znode; + if (!child) { + child = ubifs_load_znode(c, zbr, znode, 0); + if (IS_ERR(child)) { + err = PTR_ERR(child); + goto out_unlock; + } + zbr->znode = child; + } + + znode = child; + } + + /* Iterate over all indexing nodes */ + while (1) { + int idx; + + cond_resched(); + + if (znode_cb) { + err = znode_cb(c, znode, priv); + if (err) { + ubifs_err("znode checking function returned error %d", + err); + ubifs_dump_znode(c, znode); + goto out_dump; + } + } + if (leaf_cb && znode->level == 0) { + for (idx = 0; idx < znode->child_cnt; idx++) { + zbr = &znode->zbranch[idx]; + err = leaf_cb(c, zbr, priv); + if (err) { + ubifs_err("leaf checking function returned error %d, for leaf at LEB %d:%d", + err, zbr->lnum, zbr->offs); + goto out_dump; + } + } + } + + if (!znode->parent) + break; + + idx = znode->iip + 1; + znode = znode->parent; + if (idx < znode->child_cnt) { + /* Switch to the next index in the parent */ + zbr = &znode->zbranch[idx]; + child = zbr->znode; + if (!child) { + child = ubifs_load_znode(c, zbr, znode, idx); + if (IS_ERR(child)) { + err = PTR_ERR(child); + goto out_unlock; + } + zbr->znode = child; + } + znode = child; + } else + /* + * This is the last child, switch to the parent and + * continue. + */ + continue; + + /* Go to the lowest leftmost znode in the new sub-tree */ + while (znode->level > 0) { + zbr = &znode->zbranch[0]; + child = zbr->znode; + if (!child) { + child = ubifs_load_znode(c, zbr, znode, 0); + if (IS_ERR(child)) { + err = PTR_ERR(child); + goto out_unlock; + } + zbr->znode = child; + } + znode = child; + } + } + + mutex_unlock(&c->tnc_mutex); + return 0; + +out_dump: + if (znode->parent) + zbr = &znode->parent->zbranch[znode->iip]; + else + zbr = &c->zroot; + ubifs_msg("dump of znode at LEB %d:%d", zbr->lnum, zbr->offs); + ubifs_dump_znode(c, znode); +out_unlock: + mutex_unlock(&c->tnc_mutex); + return err; +} + +/** + * add_size - add znode size to partially calculated index size. + * @c: UBIFS file-system description object + * @znode: znode to add size for + * @priv: partially calculated index size + * + * This is a helper function for 'dbg_check_idx_size()' which is called for + * every indexing node and adds its size to the 'long long' variable pointed to + * by @priv. + */ +static int add_size(struct ubifs_info *c, struct ubifs_znode *znode, void *priv) +{ + long long *idx_size = priv; + int add; + + add = ubifs_idx_node_sz(c, znode->child_cnt); + add = ALIGN(add, 8); + *idx_size += add; + return 0; +} + +/** + * dbg_check_idx_size - check index size. + * @c: UBIFS file-system description object + * @idx_size: size to check + * + * This function walks the UBIFS index, calculates its size and checks that the + * size is equivalent to @idx_size. Returns zero in case of success and a + * negative error code in case of failure. + */ +int dbg_check_idx_size(struct ubifs_info *c, long long idx_size) +{ + int err; + long long calc = 0; + + if (!dbg_is_chk_index(c)) + return 0; + + err = dbg_walk_index(c, NULL, add_size, &calc); + if (err) { + ubifs_err("error %d while walking the index", err); + return err; + } + + if (calc != idx_size) { + ubifs_err("index size check failed: calculated size is %lld, should be %lld", + calc, idx_size); + dump_stack(); + return -EINVAL; + } + + return 0; +} + +#ifndef __UBOOT__ +/** + * struct fsck_inode - information about an inode used when checking the file-system. + * @rb: link in the RB-tree of inodes + * @inum: inode number + * @mode: inode type, permissions, etc + * @nlink: inode link count + * @xattr_cnt: count of extended attributes + * @references: how many directory/xattr entries refer this inode (calculated + * while walking the index) + * @calc_cnt: for directory inode count of child directories + * @size: inode size (read from on-flash inode) + * @xattr_sz: summary size of all extended attributes (read from on-flash + * inode) + * @calc_sz: for directories calculated directory size + * @calc_xcnt: count of extended attributes + * @calc_xsz: calculated summary size of all extended attributes + * @xattr_nms: sum of lengths of all extended attribute names belonging to this + * inode (read from on-flash inode) + * @calc_xnms: calculated sum of lengths of all extended attribute names + */ +struct fsck_inode { + struct rb_node rb; + ino_t inum; + umode_t mode; + unsigned int nlink; + unsigned int xattr_cnt; + int references; + int calc_cnt; + long long size; + unsigned int xattr_sz; + long long calc_sz; + long long calc_xcnt; + long long calc_xsz; + unsigned int xattr_nms; + long long calc_xnms; +}; + +/** + * struct fsck_data - private FS checking information. + * @inodes: RB-tree of all inodes (contains @struct fsck_inode objects) + */ +struct fsck_data { + struct rb_root inodes; +}; + +/** + * add_inode - add inode information to RB-tree of inodes. + * @c: UBIFS file-system description object + * @fsckd: FS checking information + * @ino: raw UBIFS inode to add + * + * This is a helper function for 'check_leaf()' which adds information about + * inode @ino to the RB-tree of inodes. Returns inode information pointer in + * case of success and a negative error code in case of failure. + */ +static struct fsck_inode *add_inode(struct ubifs_info *c, + struct fsck_data *fsckd, + struct ubifs_ino_node *ino) +{ + struct rb_node **p, *parent = NULL; + struct fsck_inode *fscki; + ino_t inum = key_inum_flash(c, &ino->key); + struct inode *inode; + struct ubifs_inode *ui; + + p = &fsckd->inodes.rb_node; + while (*p) { + parent = *p; + fscki = rb_entry(parent, struct fsck_inode, rb); + if (inum < fscki->inum) + p = &(*p)->rb_left; + else if (inum > fscki->inum) + p = &(*p)->rb_right; + else + return fscki; + } + + if (inum > c->highest_inum) { + ubifs_err("too high inode number, max. is %lu", + (unsigned long)c->highest_inum); + return ERR_PTR(-EINVAL); + } + + fscki = kzalloc(sizeof(struct fsck_inode), GFP_NOFS); + if (!fscki) + return ERR_PTR(-ENOMEM); + + inode = ilookup(c->vfs_sb, inum); + + fscki->inum = inum; + /* + * If the inode is present in the VFS inode cache, use it instead of + * the on-flash inode which might be out-of-date. E.g., the size might + * be out-of-date. If we do not do this, the following may happen, for + * example: + * 1. A power cut happens + * 2. We mount the file-system R/O, the replay process fixes up the + * inode size in the VFS cache, but on on-flash. + * 3. 'check_leaf()' fails because it hits a data node beyond inode + * size. + */ + if (!inode) { + fscki->nlink = le32_to_cpu(ino->nlink); + fscki->size = le64_to_cpu(ino->size); + fscki->xattr_cnt = le32_to_cpu(ino->xattr_cnt); + fscki->xattr_sz = le32_to_cpu(ino->xattr_size); + fscki->xattr_nms = le32_to_cpu(ino->xattr_names); + fscki->mode = le32_to_cpu(ino->mode); + } else { + ui = ubifs_inode(inode); + fscki->nlink = inode->i_nlink; + fscki->size = inode->i_size; + fscki->xattr_cnt = ui->xattr_cnt; + fscki->xattr_sz = ui->xattr_size; + fscki->xattr_nms = ui->xattr_names; + fscki->mode = inode->i_mode; + iput(inode); + } + + if (S_ISDIR(fscki->mode)) { + fscki->calc_sz = UBIFS_INO_NODE_SZ; + fscki->calc_cnt = 2; + } + + rb_link_node(&fscki->rb, parent, p); + rb_insert_color(&fscki->rb, &fsckd->inodes); + + return fscki; +} + +/** + * search_inode - search inode in the RB-tree of inodes. + * @fsckd: FS checking information + * @inum: inode number to search + * + * This is a helper function for 'check_leaf()' which searches inode @inum in + * the RB-tree of inodes and returns an inode information pointer or %NULL if + * the inode was not found. + */ +static struct fsck_inode *search_inode(struct fsck_data *fsckd, ino_t inum) +{ + struct rb_node *p; + struct fsck_inode *fscki; + + p = fsckd->inodes.rb_node; + while (p) { + fscki = rb_entry(p, struct fsck_inode, rb); + if (inum < fscki->inum) + p = p->rb_left; + else if (inum > fscki->inum) + p = p->rb_right; + else + return fscki; + } + return NULL; +} + +/** + * read_add_inode - read inode node and add it to RB-tree of inodes. + * @c: UBIFS file-system description object + * @fsckd: FS checking information + * @inum: inode number to read + * + * This is a helper function for 'check_leaf()' which finds inode node @inum in + * the index, reads it, and adds it to the RB-tree of inodes. Returns inode + * information pointer in case of success and a negative error code in case of + * failure. + */ +static struct fsck_inode *read_add_inode(struct ubifs_info *c, + struct fsck_data *fsckd, ino_t inum) +{ + int n, err; + union ubifs_key key; + struct ubifs_znode *znode; + struct ubifs_zbranch *zbr; + struct ubifs_ino_node *ino; + struct fsck_inode *fscki; + + fscki = search_inode(fsckd, inum); + if (fscki) + return fscki; + + ino_key_init(c, &key, inum); + err = ubifs_lookup_level0(c, &key, &znode, &n); + if (!err) { + ubifs_err("inode %lu not found in index", (unsigned long)inum); + return ERR_PTR(-ENOENT); + } else if (err < 0) { + ubifs_err("error %d while looking up inode %lu", + err, (unsigned long)inum); + return ERR_PTR(err); + } + + zbr = &znode->zbranch[n]; + if (zbr->len < UBIFS_INO_NODE_SZ) { + ubifs_err("bad node %lu node length %d", + (unsigned long)inum, zbr->len); + return ERR_PTR(-EINVAL); + } + + ino = kmalloc(zbr->len, GFP_NOFS); + if (!ino) + return ERR_PTR(-ENOMEM); + + err = ubifs_tnc_read_node(c, zbr, ino); + if (err) { + ubifs_err("cannot read inode node at LEB %d:%d, error %d", + zbr->lnum, zbr->offs, err); + kfree(ino); + return ERR_PTR(err); + } + + fscki = add_inode(c, fsckd, ino); + kfree(ino); + if (IS_ERR(fscki)) { + ubifs_err("error %ld while adding inode %lu node", + PTR_ERR(fscki), (unsigned long)inum); + return fscki; + } + + return fscki; +} + +/** + * check_leaf - check leaf node. + * @c: UBIFS file-system description object + * @zbr: zbranch of the leaf node to check + * @priv: FS checking information + * + * This is a helper function for 'dbg_check_filesystem()' which is called for + * every single leaf node while walking the indexing tree. It checks that the + * leaf node referred from the indexing tree exists, has correct CRC, and does + * some other basic validation. This function is also responsible for building + * an RB-tree of inodes - it adds all inodes into the RB-tree. It also + * calculates reference count, size, etc for each inode in order to later + * compare them to the information stored inside the inodes and detect possible + * inconsistencies. Returns zero in case of success and a negative error code + * in case of failure. + */ +static int check_leaf(struct ubifs_info *c, struct ubifs_zbranch *zbr, + void *priv) +{ + ino_t inum; + void *node; + struct ubifs_ch *ch; + int err, type = key_type(c, &zbr->key); + struct fsck_inode *fscki; + + if (zbr->len < UBIFS_CH_SZ) { + ubifs_err("bad leaf length %d (LEB %d:%d)", + zbr->len, zbr->lnum, zbr->offs); + return -EINVAL; + } + + node = kmalloc(zbr->len, GFP_NOFS); + if (!node) return -ENOMEM; - c->dbg->buf = vmalloc(c->leb_size); - if (!c->dbg->buf) + err = ubifs_tnc_read_node(c, zbr, node); + if (err) { + ubifs_err("cannot read leaf node at LEB %d:%d, error %d", + zbr->lnum, zbr->offs, err); + goto out_free; + } + + /* If this is an inode node, add it to RB-tree of inodes */ + if (type == UBIFS_INO_KEY) { + fscki = add_inode(c, priv, node); + if (IS_ERR(fscki)) { + err = PTR_ERR(fscki); + ubifs_err("error %d while adding inode node", err); + goto out_dump; + } goto out; + } + + if (type != UBIFS_DENT_KEY && type != UBIFS_XENT_KEY && + type != UBIFS_DATA_KEY) { + ubifs_err("unexpected node type %d at LEB %d:%d", + type, zbr->lnum, zbr->offs); + err = -EINVAL; + goto out_free; + } + + ch = node; + if (le64_to_cpu(ch->sqnum) > c->max_sqnum) { + ubifs_err("too high sequence number, max. is %llu", + c->max_sqnum); + err = -EINVAL; + goto out_dump; + } + + if (type == UBIFS_DATA_KEY) { + long long blk_offs; + struct ubifs_data_node *dn = node; + + /* + * Search the inode node this data node belongs to and insert + * it to the RB-tree of inodes. + */ + inum = key_inum_flash(c, &dn->key); + fscki = read_add_inode(c, priv, inum); + if (IS_ERR(fscki)) { + err = PTR_ERR(fscki); + ubifs_err("error %d while processing data node and trying to find inode node %lu", + err, (unsigned long)inum); + goto out_dump; + } + + /* Make sure the data node is within inode size */ + blk_offs = key_block_flash(c, &dn->key); + blk_offs <<= UBIFS_BLOCK_SHIFT; + blk_offs += le32_to_cpu(dn->size); + if (blk_offs > fscki->size) { + ubifs_err("data node at LEB %d:%d is not within inode size %lld", + zbr->lnum, zbr->offs, fscki->size); + err = -EINVAL; + goto out_dump; + } + } else { + int nlen; + struct ubifs_dent_node *dent = node; + struct fsck_inode *fscki1; + + err = ubifs_validate_entry(c, dent); + if (err) + goto out_dump; + + /* + * Search the inode node this entry refers to and the parent + * inode node and insert them to the RB-tree of inodes. + */ + inum = le64_to_cpu(dent->inum); + fscki = read_add_inode(c, priv, inum); + if (IS_ERR(fscki)) { + err = PTR_ERR(fscki); + ubifs_err("error %d while processing entry node and trying to find inode node %lu", + err, (unsigned long)inum); + goto out_dump; + } + + /* Count how many direntries or xentries refers this inode */ + fscki->references += 1; + + inum = key_inum_flash(c, &dent->key); + fscki1 = read_add_inode(c, priv, inum); + if (IS_ERR(fscki1)) { + err = PTR_ERR(fscki1); + ubifs_err("error %d while processing entry node and trying to find parent inode node %lu", + err, (unsigned long)inum); + goto out_dump; + } + + nlen = le16_to_cpu(dent->nlen); + if (type == UBIFS_XENT_KEY) { + fscki1->calc_xcnt += 1; + fscki1->calc_xsz += CALC_DENT_SIZE(nlen); + fscki1->calc_xsz += CALC_XATTR_BYTES(fscki->size); + fscki1->calc_xnms += nlen; + } else { + fscki1->calc_sz += CALC_DENT_SIZE(nlen); + if (dent->type == UBIFS_ITYPE_DIR) + fscki1->calc_cnt += 1; + } + } + +out: + kfree(node); + return 0; + +out_dump: + ubifs_msg("dump of node at LEB %d:%d", zbr->lnum, zbr->offs); + ubifs_dump_node(c, node); +out_free: + kfree(node); + return err; +} + +/** + * free_inodes - free RB-tree of inodes. + * @fsckd: FS checking information + */ +static void free_inodes(struct fsck_data *fsckd) +{ + struct fsck_inode *fscki, *n; + + rbtree_postorder_for_each_entry_safe(fscki, n, &fsckd->inodes, rb) + kfree(fscki); +} + +/** + * check_inodes - checks all inodes. + * @c: UBIFS file-system description object + * @fsckd: FS checking information + * + * This is a helper function for 'dbg_check_filesystem()' which walks the + * RB-tree of inodes after the index scan has been finished, and checks that + * inode nlink, size, etc are correct. Returns zero if inodes are fine, + * %-EINVAL if not, and a negative error code in case of failure. + */ +static int check_inodes(struct ubifs_info *c, struct fsck_data *fsckd) +{ + int n, err; + union ubifs_key key; + struct ubifs_znode *znode; + struct ubifs_zbranch *zbr; + struct ubifs_ino_node *ino; + struct fsck_inode *fscki; + struct rb_node *this = rb_first(&fsckd->inodes); + + while (this) { + fscki = rb_entry(this, struct fsck_inode, rb); + this = rb_next(this); + + if (S_ISDIR(fscki->mode)) { + /* + * Directories have to have exactly one reference (they + * cannot have hardlinks), although root inode is an + * exception. + */ + if (fscki->inum != UBIFS_ROOT_INO && + fscki->references != 1) { + ubifs_err("directory inode %lu has %d direntries which refer it, but should be 1", + (unsigned long)fscki->inum, + fscki->references); + goto out_dump; + } + if (fscki->inum == UBIFS_ROOT_INO && + fscki->references != 0) { + ubifs_err("root inode %lu has non-zero (%d) direntries which refer it", + (unsigned long)fscki->inum, + fscki->references); + goto out_dump; + } + if (fscki->calc_sz != fscki->size) { + ubifs_err("directory inode %lu size is %lld, but calculated size is %lld", + (unsigned long)fscki->inum, + fscki->size, fscki->calc_sz); + goto out_dump; + } + if (fscki->calc_cnt != fscki->nlink) { + ubifs_err("directory inode %lu nlink is %d, but calculated nlink is %d", + (unsigned long)fscki->inum, + fscki->nlink, fscki->calc_cnt); + goto out_dump; + } + } else { + if (fscki->references != fscki->nlink) { + ubifs_err("inode %lu nlink is %d, but calculated nlink is %d", + (unsigned long)fscki->inum, + fscki->nlink, fscki->references); + goto out_dump; + } + } + if (fscki->xattr_sz != fscki->calc_xsz) { + ubifs_err("inode %lu has xattr size %u, but calculated size is %lld", + (unsigned long)fscki->inum, fscki->xattr_sz, + fscki->calc_xsz); + goto out_dump; + } + if (fscki->xattr_cnt != fscki->calc_xcnt) { + ubifs_err("inode %lu has %u xattrs, but calculated count is %lld", + (unsigned long)fscki->inum, + fscki->xattr_cnt, fscki->calc_xcnt); + goto out_dump; + } + if (fscki->xattr_nms != fscki->calc_xnms) { + ubifs_err("inode %lu has xattr names' size %u, but calculated names' size is %lld", + (unsigned long)fscki->inum, fscki->xattr_nms, + fscki->calc_xnms); + goto out_dump; + } + } + + return 0; + +out_dump: + /* Read the bad inode and dump it */ + ino_key_init(c, &key, fscki->inum); + err = ubifs_lookup_level0(c, &key, &znode, &n); + if (!err) { + ubifs_err("inode %lu not found in index", + (unsigned long)fscki->inum); + return -ENOENT; + } else if (err < 0) { + ubifs_err("error %d while looking up inode %lu", + err, (unsigned long)fscki->inum); + return err; + } + + zbr = &znode->zbranch[n]; + ino = kmalloc(zbr->len, GFP_NOFS); + if (!ino) + return -ENOMEM; + + err = ubifs_tnc_read_node(c, zbr, ino); + if (err) { + ubifs_err("cannot read inode node at LEB %d:%d, error %d", + zbr->lnum, zbr->offs, err); + kfree(ino); + return err; + } + + ubifs_msg("dump of the inode %lu sitting in LEB %d:%d", + (unsigned long)fscki->inum, zbr->lnum, zbr->offs); + ubifs_dump_node(c, ino); + kfree(ino); + return -EINVAL; +} + +/** + * dbg_check_filesystem - check the file-system. + * @c: UBIFS file-system description object + * + * This function checks the file system, namely: + * o makes sure that all leaf nodes exist and their CRCs are correct; + * o makes sure inode nlink, size, xattr size/count are correct (for all + * inodes). + * + * The function reads whole indexing tree and all nodes, so it is pretty + * heavy-weight. Returns zero if the file-system is consistent, %-EINVAL if + * not, and a negative error code in case of failure. + */ +int dbg_check_filesystem(struct ubifs_info *c) +{ + int err; + struct fsck_data fsckd; + + if (!dbg_is_chk_fs(c)) + return 0; + + fsckd.inodes = RB_ROOT; + err = dbg_walk_index(c, check_leaf, NULL, &fsckd); + if (err) + goto out_free; + + err = check_inodes(c, &fsckd); + if (err) + goto out_free; + + free_inodes(&fsckd); + return 0; + +out_free: + ubifs_err("file-system check failed with error %d", err); + dump_stack(); + free_inodes(&fsckd); + return err; +} + +/** + * dbg_check_data_nodes_order - check that list of data nodes is sorted. + * @c: UBIFS file-system description object + * @head: the list of nodes ('struct ubifs_scan_node' objects) + * + * This function returns zero if the list of data nodes is sorted correctly, + * and %-EINVAL if not. + */ +int dbg_check_data_nodes_order(struct ubifs_info *c, struct list_head *head) +{ + struct list_head *cur; + struct ubifs_scan_node *sa, *sb; + + if (!dbg_is_chk_gen(c)) + return 0; + + for (cur = head->next; cur->next != head; cur = cur->next) { + ino_t inuma, inumb; + uint32_t blka, blkb; + + cond_resched(); + sa = container_of(cur, struct ubifs_scan_node, list); + sb = container_of(cur->next, struct ubifs_scan_node, list); + + if (sa->type != UBIFS_DATA_NODE) { + ubifs_err("bad node type %d", sa->type); + ubifs_dump_node(c, sa->node); + return -EINVAL; + } + if (sb->type != UBIFS_DATA_NODE) { + ubifs_err("bad node type %d", sb->type); + ubifs_dump_node(c, sb->node); + return -EINVAL; + } + + inuma = key_inum(c, &sa->key); + inumb = key_inum(c, &sb->key); + + if (inuma < inumb) + continue; + if (inuma > inumb) { + ubifs_err("larger inum %lu goes before inum %lu", + (unsigned long)inuma, (unsigned long)inumb); + goto error_dump; + } + + blka = key_block(c, &sa->key); + blkb = key_block(c, &sb->key); + + if (blka > blkb) { + ubifs_err("larger block %u goes before %u", blka, blkb); + goto error_dump; + } + if (blka == blkb) { + ubifs_err("two data nodes for the same block"); + goto error_dump; + } + } return 0; +error_dump: + ubifs_dump_node(c, sa->node); + ubifs_dump_node(c, sb->node); + return -EINVAL; +} + +/** + * dbg_check_nondata_nodes_order - check that list of data nodes is sorted. + * @c: UBIFS file-system description object + * @head: the list of nodes ('struct ubifs_scan_node' objects) + * + * This function returns zero if the list of non-data nodes is sorted correctly, + * and %-EINVAL if not. + */ +int dbg_check_nondata_nodes_order(struct ubifs_info *c, struct list_head *head) +{ + struct list_head *cur; + struct ubifs_scan_node *sa, *sb; + + if (!dbg_is_chk_gen(c)) + return 0; + + for (cur = head->next; cur->next != head; cur = cur->next) { + ino_t inuma, inumb; + uint32_t hasha, hashb; + + cond_resched(); + sa = container_of(cur, struct ubifs_scan_node, list); + sb = container_of(cur->next, struct ubifs_scan_node, list); + + if (sa->type != UBIFS_INO_NODE && sa->type != UBIFS_DENT_NODE && + sa->type != UBIFS_XENT_NODE) { + ubifs_err("bad node type %d", sa->type); + ubifs_dump_node(c, sa->node); + return -EINVAL; + } + if (sa->type != UBIFS_INO_NODE && sa->type != UBIFS_DENT_NODE && + sa->type != UBIFS_XENT_NODE) { + ubifs_err("bad node type %d", sb->type); + ubifs_dump_node(c, sb->node); + return -EINVAL; + } + + if (sa->type != UBIFS_INO_NODE && sb->type == UBIFS_INO_NODE) { + ubifs_err("non-inode node goes before inode node"); + goto error_dump; + } + + if (sa->type == UBIFS_INO_NODE && sb->type != UBIFS_INO_NODE) + continue; + + if (sa->type == UBIFS_INO_NODE && sb->type == UBIFS_INO_NODE) { + /* Inode nodes are sorted in descending size order */ + if (sa->len < sb->len) { + ubifs_err("smaller inode node goes first"); + goto error_dump; + } + continue; + } + + /* + * This is either a dentry or xentry, which should be sorted in + * ascending (parent ino, hash) order. + */ + inuma = key_inum(c, &sa->key); + inumb = key_inum(c, &sb->key); + + if (inuma < inumb) + continue; + if (inuma > inumb) { + ubifs_err("larger inum %lu goes before inum %lu", + (unsigned long)inuma, (unsigned long)inumb); + goto error_dump; + } + + hasha = key_block(c, &sa->key); + hashb = key_block(c, &sb->key); + + if (hasha > hashb) { + ubifs_err("larger hash %u goes before %u", + hasha, hashb); + goto error_dump; + } + } + + return 0; + +error_dump: + ubifs_msg("dumping first node"); + ubifs_dump_node(c, sa->node); + ubifs_msg("dumping second node"); + ubifs_dump_node(c, sb->node); + return -EINVAL; + return 0; +} + +static inline int chance(unsigned int n, unsigned int out_of) +{ + return !!((prandom_u32() % out_of) + 1 <= n); + +} + +static int power_cut_emulated(struct ubifs_info *c, int lnum, int write) +{ + struct ubifs_debug_info *d = c->dbg; + + ubifs_assert(dbg_is_tst_rcvry(c)); + + if (!d->pc_cnt) { + /* First call - decide delay to the power cut */ + if (chance(1, 2)) { + unsigned long delay; + + if (chance(1, 2)) { + d->pc_delay = 1; + /* Fail withing 1 minute */ + delay = prandom_u32() % 60000; + d->pc_timeout = jiffies; + d->pc_timeout += msecs_to_jiffies(delay); + ubifs_warn("failing after %lums", delay); + } else { + d->pc_delay = 2; + delay = prandom_u32() % 10000; + /* Fail within 10000 operations */ + d->pc_cnt_max = delay; + ubifs_warn("failing after %lu calls", delay); + } + } + + d->pc_cnt += 1; + } + + /* Determine if failure delay has expired */ + if (d->pc_delay == 1 && time_before(jiffies, d->pc_timeout)) + return 0; + if (d->pc_delay == 2 && d->pc_cnt++ < d->pc_cnt_max) + return 0; + + if (lnum == UBIFS_SB_LNUM) { + if (write && chance(1, 2)) + return 0; + if (chance(19, 20)) + return 0; + ubifs_warn("failing in super block LEB %d", lnum); + } else if (lnum == UBIFS_MST_LNUM || lnum == UBIFS_MST_LNUM + 1) { + if (chance(19, 20)) + return 0; + ubifs_warn("failing in master LEB %d", lnum); + } else if (lnum >= UBIFS_LOG_LNUM && lnum <= c->log_last) { + if (write && chance(99, 100)) + return 0; + if (chance(399, 400)) + return 0; + ubifs_warn("failing in log LEB %d", lnum); + } else if (lnum >= c->lpt_first && lnum <= c->lpt_last) { + if (write && chance(7, 8)) + return 0; + if (chance(19, 20)) + return 0; + ubifs_warn("failing in LPT LEB %d", lnum); + } else if (lnum >= c->orph_first && lnum <= c->orph_last) { + if (write && chance(1, 2)) + return 0; + if (chance(9, 10)) + return 0; + ubifs_warn("failing in orphan LEB %d", lnum); + } else if (lnum == c->ihead_lnum) { + if (chance(99, 100)) + return 0; + ubifs_warn("failing in index head LEB %d", lnum); + } else if (c->jheads && lnum == c->jheads[GCHD].wbuf.lnum) { + if (chance(9, 10)) + return 0; + ubifs_warn("failing in GC head LEB %d", lnum); + } else if (write && !RB_EMPTY_ROOT(&c->buds) && + !ubifs_search_bud(c, lnum)) { + if (chance(19, 20)) + return 0; + ubifs_warn("failing in non-bud LEB %d", lnum); + } else if (c->cmt_state == COMMIT_RUNNING_BACKGROUND || + c->cmt_state == COMMIT_RUNNING_REQUIRED) { + if (chance(999, 1000)) + return 0; + ubifs_warn("failing in bud LEB %d commit running", lnum); + } else { + if (chance(9999, 10000)) + return 0; + ubifs_warn("failing in bud LEB %d commit not running", lnum); + } + + d->pc_happened = 1; + ubifs_warn("========== Power cut emulated =========="); + dump_stack(); + return 1; +} + +static int corrupt_data(const struct ubifs_info *c, const void *buf, + unsigned int len) +{ + unsigned int from, to, ffs = chance(1, 2); + unsigned char *p = (void *)buf; + + from = prandom_u32() % len; + /* Corruption span max to end of write unit */ + to = min(len, ALIGN(from + 1, c->max_write_size)); + + ubifs_warn("filled bytes %u-%u with %s", from, to - 1, + ffs ? "0xFFs" : "random data"); + + if (ffs) + memset(p + from, 0xFF, to - from); + else + prandom_bytes(p + from, to - from); + + return to; +} + +int dbg_leb_write(struct ubifs_info *c, int lnum, const void *buf, + int offs, int len) +{ + int err, failing; + + if (c->dbg->pc_happened) + return -EROFS; + + failing = power_cut_emulated(c, lnum, 1); + if (failing) { + len = corrupt_data(c, buf, len); + ubifs_warn("actually write %d bytes to LEB %d:%d (the buffer was corrupted)", + len, lnum, offs); + } + err = ubi_leb_write(c->ubi, lnum, buf, offs, len); + if (err) + return err; + if (failing) + return -EROFS; + return 0; +} + +int dbg_leb_change(struct ubifs_info *c, int lnum, const void *buf, + int len) +{ + int err; + + if (c->dbg->pc_happened) + return -EROFS; + if (power_cut_emulated(c, lnum, 1)) + return -EROFS; + err = ubi_leb_change(c->ubi, lnum, buf, len); + if (err) + return err; + if (power_cut_emulated(c, lnum, 1)) + return -EROFS; + return 0; +} + +int dbg_leb_unmap(struct ubifs_info *c, int lnum) +{ + int err; + + if (c->dbg->pc_happened) + return -EROFS; + if (power_cut_emulated(c, lnum, 0)) + return -EROFS; + err = ubi_leb_unmap(c->ubi, lnum); + if (err) + return err; + if (power_cut_emulated(c, lnum, 0)) + return -EROFS; + return 0; +} + +int dbg_leb_map(struct ubifs_info *c, int lnum) +{ + int err; + + if (c->dbg->pc_happened) + return -EROFS; + if (power_cut_emulated(c, lnum, 0)) + return -EROFS; + err = ubi_leb_map(c->ubi, lnum); + if (err) + return err; + if (power_cut_emulated(c, lnum, 0)) + return -EROFS; + return 0; +} + +/* + * Root directory for UBIFS stuff in debugfs. Contains sub-directories which + * contain the stuff specific to particular file-system mounts. + */ +static struct dentry *dfs_rootdir; + +static int dfs_file_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return nonseekable_open(inode, file); +} + +/** + * provide_user_output - provide output to the user reading a debugfs file. + * @val: boolean value for the answer + * @u: the buffer to store the answer at + * @count: size of the buffer + * @ppos: position in the @u output buffer + * + * This is a simple helper function which stores @val boolean value in the user + * buffer when the user reads one of UBIFS debugfs files. Returns amount of + * bytes written to @u in case of success and a negative error code in case of + * failure. + */ +static int provide_user_output(int val, char __user *u, size_t count, + loff_t *ppos) +{ + char buf[3]; + + if (val) + buf[0] = '1'; + else + buf[0] = '0'; + buf[1] = '\n'; + buf[2] = 0x00; + + return simple_read_from_buffer(u, count, ppos, buf, 2); +} + +static ssize_t dfs_file_read(struct file *file, char __user *u, size_t count, + loff_t *ppos) +{ + struct dentry *dent = file->f_path.dentry; + struct ubifs_info *c = file->private_data; + struct ubifs_debug_info *d = c->dbg; + int val; + + if (dent == d->dfs_chk_gen) + val = d->chk_gen; + else if (dent == d->dfs_chk_index) + val = d->chk_index; + else if (dent == d->dfs_chk_orph) + val = d->chk_orph; + else if (dent == d->dfs_chk_lprops) + val = d->chk_lprops; + else if (dent == d->dfs_chk_fs) + val = d->chk_fs; + else if (dent == d->dfs_tst_rcvry) + val = d->tst_rcvry; + else if (dent == d->dfs_ro_error) + val = c->ro_error; + else + return -EINVAL; + + return provide_user_output(val, u, count, ppos); +} + +/** + * interpret_user_input - interpret user debugfs file input. + * @u: user-provided buffer with the input + * @count: buffer size + * + * This is a helper function which interpret user input to a boolean UBIFS + * debugfs file. Returns %0 or %1 in case of success and a negative error code + * in case of failure. + */ +static int interpret_user_input(const char __user *u, size_t count) +{ + size_t buf_size; + char buf[8]; + + buf_size = min_t(size_t, count, (sizeof(buf) - 1)); + if (copy_from_user(buf, u, buf_size)) + return -EFAULT; + + if (buf[0] == '1') + return 1; + else if (buf[0] == '0') + return 0; + + return -EINVAL; +} + +static ssize_t dfs_file_write(struct file *file, const char __user *u, + size_t count, loff_t *ppos) +{ + struct ubifs_info *c = file->private_data; + struct ubifs_debug_info *d = c->dbg; + struct dentry *dent = file->f_path.dentry; + int val; + + /* + * TODO: this is racy - the file-system might have already been + * unmounted and we'd oops in this case. The plan is to fix it with + * help of 'iterate_supers_type()' which we should have in v3.0: when + * a debugfs opened, we rember FS's UUID in file->private_data. Then + * whenever we access the FS via a debugfs file, we iterate all UBIFS + * superblocks and fine the one with the same UUID, and take the + * locking right. + * + * The other way to go suggested by Al Viro is to create a separate + * 'ubifs-debug' file-system instead. + */ + if (file->f_path.dentry == d->dfs_dump_lprops) { + ubifs_dump_lprops(c); + return count; + } + if (file->f_path.dentry == d->dfs_dump_budg) { + ubifs_dump_budg(c, &c->bi); + return count; + } + if (file->f_path.dentry == d->dfs_dump_tnc) { + mutex_lock(&c->tnc_mutex); + ubifs_dump_tnc(c); + mutex_unlock(&c->tnc_mutex); + return count; + } + + val = interpret_user_input(u, count); + if (val < 0) + return val; + + if (dent == d->dfs_chk_gen) + d->chk_gen = val; + else if (dent == d->dfs_chk_index) + d->chk_index = val; + else if (dent == d->dfs_chk_orph) + d->chk_orph = val; + else if (dent == d->dfs_chk_lprops) + d->chk_lprops = val; + else if (dent == d->dfs_chk_fs) + d->chk_fs = val; + else if (dent == d->dfs_tst_rcvry) + d->tst_rcvry = val; + else if (dent == d->dfs_ro_error) + c->ro_error = !!val; + else + return -EINVAL; + + return count; +} + +static const struct file_operations dfs_fops = { + .open = dfs_file_open, + .read = dfs_file_read, + .write = dfs_file_write, + .owner = THIS_MODULE, + .llseek = no_llseek, +}; + +/** + * dbg_debugfs_init_fs - initialize debugfs for UBIFS instance. + * @c: UBIFS file-system description object + * + * This function creates all debugfs files for this instance of UBIFS. Returns + * zero in case of success and a negative error code in case of failure. + * + * Note, the only reason we have not merged this function with the + * 'ubifs_debugging_init()' function is because it is better to initialize + * debugfs interfaces at the very end of the mount process, and remove them at + * the very beginning of the mount process. + */ +int dbg_debugfs_init_fs(struct ubifs_info *c) +{ + int err, n; + const char *fname; + struct dentry *dent; + struct ubifs_debug_info *d = c->dbg; + + if (!IS_ENABLED(CONFIG_DEBUG_FS)) + return 0; + + n = snprintf(d->dfs_dir_name, UBIFS_DFS_DIR_LEN + 1, UBIFS_DFS_DIR_NAME, + c->vi.ubi_num, c->vi.vol_id); + if (n == UBIFS_DFS_DIR_LEN) { + /* The array size is too small */ + fname = UBIFS_DFS_DIR_NAME; + dent = ERR_PTR(-EINVAL); + goto out; + } + + fname = d->dfs_dir_name; + dent = debugfs_create_dir(fname, dfs_rootdir); + if (IS_ERR_OR_NULL(dent)) + goto out; + d->dfs_dir = dent; + + fname = "dump_lprops"; + dent = debugfs_create_file(fname, S_IWUSR, d->dfs_dir, c, &dfs_fops); + if (IS_ERR_OR_NULL(dent)) + goto out_remove; + d->dfs_dump_lprops = dent; + + fname = "dump_budg"; + dent = debugfs_create_file(fname, S_IWUSR, d->dfs_dir, c, &dfs_fops); + if (IS_ERR_OR_NULL(dent)) + goto out_remove; + d->dfs_dump_budg = dent; + + fname = "dump_tnc"; + dent = debugfs_create_file(fname, S_IWUSR, d->dfs_dir, c, &dfs_fops); + if (IS_ERR_OR_NULL(dent)) + goto out_remove; + d->dfs_dump_tnc = dent; + + fname = "chk_general"; + dent = debugfs_create_file(fname, S_IRUSR | S_IWUSR, d->dfs_dir, c, + &dfs_fops); + if (IS_ERR_OR_NULL(dent)) + goto out_remove; + d->dfs_chk_gen = dent; + + fname = "chk_index"; + dent = debugfs_create_file(fname, S_IRUSR | S_IWUSR, d->dfs_dir, c, + &dfs_fops); + if (IS_ERR_OR_NULL(dent)) + goto out_remove; + d->dfs_chk_index = dent; + + fname = "chk_orphans"; + dent = debugfs_create_file(fname, S_IRUSR | S_IWUSR, d->dfs_dir, c, + &dfs_fops); + if (IS_ERR_OR_NULL(dent)) + goto out_remove; + d->dfs_chk_orph = dent; + + fname = "chk_lprops"; + dent = debugfs_create_file(fname, S_IRUSR | S_IWUSR, d->dfs_dir, c, + &dfs_fops); + if (IS_ERR_OR_NULL(dent)) + goto out_remove; + d->dfs_chk_lprops = dent; + + fname = "chk_fs"; + dent = debugfs_create_file(fname, S_IRUSR | S_IWUSR, d->dfs_dir, c, + &dfs_fops); + if (IS_ERR_OR_NULL(dent)) + goto out_remove; + d->dfs_chk_fs = dent; + + fname = "tst_recovery"; + dent = debugfs_create_file(fname, S_IRUSR | S_IWUSR, d->dfs_dir, c, + &dfs_fops); + if (IS_ERR_OR_NULL(dent)) + goto out_remove; + d->dfs_tst_rcvry = dent; + + fname = "ro_error"; + dent = debugfs_create_file(fname, S_IRUSR | S_IWUSR, d->dfs_dir, c, + &dfs_fops); + if (IS_ERR_OR_NULL(dent)) + goto out_remove; + d->dfs_ro_error = dent; + + return 0; + +out_remove: + debugfs_remove_recursive(d->dfs_dir); out: - kfree(c->dbg); - return -ENOMEM; + err = dent ? PTR_ERR(dent) : -ENODEV; + ubifs_err("cannot create \"%s\" debugfs file or directory, error %d\n", + fname, err); + return err; +} + +/** + * dbg_debugfs_exit_fs - remove all debugfs files. + * @c: UBIFS file-system description object + */ +void dbg_debugfs_exit_fs(struct ubifs_info *c) +{ + if (IS_ENABLED(CONFIG_DEBUG_FS)) + debugfs_remove_recursive(c->dbg->dfs_dir); +} + +struct ubifs_global_debug_info ubifs_dbg; + +static struct dentry *dfs_chk_gen; +static struct dentry *dfs_chk_index; +static struct dentry *dfs_chk_orph; +static struct dentry *dfs_chk_lprops; +static struct dentry *dfs_chk_fs; +static struct dentry *dfs_tst_rcvry; + +static ssize_t dfs_global_file_read(struct file *file, char __user *u, + size_t count, loff_t *ppos) +{ + struct dentry *dent = file->f_path.dentry; + int val; + + if (dent == dfs_chk_gen) + val = ubifs_dbg.chk_gen; + else if (dent == dfs_chk_index) + val = ubifs_dbg.chk_index; + else if (dent == dfs_chk_orph) + val = ubifs_dbg.chk_orph; + else if (dent == dfs_chk_lprops) + val = ubifs_dbg.chk_lprops; + else if (dent == dfs_chk_fs) + val = ubifs_dbg.chk_fs; + else if (dent == dfs_tst_rcvry) + val = ubifs_dbg.tst_rcvry; + else + return -EINVAL; + + return provide_user_output(val, u, count, ppos); +} + +static ssize_t dfs_global_file_write(struct file *file, const char __user *u, + size_t count, loff_t *ppos) +{ + struct dentry *dent = file->f_path.dentry; + int val; + + val = interpret_user_input(u, count); + if (val < 0) + return val; + + if (dent == dfs_chk_gen) + ubifs_dbg.chk_gen = val; + else if (dent == dfs_chk_index) + ubifs_dbg.chk_index = val; + else if (dent == dfs_chk_orph) + ubifs_dbg.chk_orph = val; + else if (dent == dfs_chk_lprops) + ubifs_dbg.chk_lprops = val; + else if (dent == dfs_chk_fs) + ubifs_dbg.chk_fs = val; + else if (dent == dfs_tst_rcvry) + ubifs_dbg.tst_rcvry = val; + else + return -EINVAL; + + return count; +} + +static const struct file_operations dfs_global_fops = { + .read = dfs_global_file_read, + .write = dfs_global_file_write, + .owner = THIS_MODULE, + .llseek = no_llseek, +}; + +/** + * dbg_debugfs_init - initialize debugfs file-system. + * + * UBIFS uses debugfs file-system to expose various debugging knobs to + * user-space. This function creates "ubifs" directory in the debugfs + * file-system. Returns zero in case of success and a negative error code in + * case of failure. + */ +int dbg_debugfs_init(void) +{ + int err; + const char *fname; + struct dentry *dent; + + if (!IS_ENABLED(CONFIG_DEBUG_FS)) + return 0; + + fname = "ubifs"; + dent = debugfs_create_dir(fname, NULL); + if (IS_ERR_OR_NULL(dent)) + goto out; + dfs_rootdir = dent; + + fname = "chk_general"; + dent = debugfs_create_file(fname, S_IRUSR | S_IWUSR, dfs_rootdir, NULL, + &dfs_global_fops); + if (IS_ERR_OR_NULL(dent)) + goto out_remove; + dfs_chk_gen = dent; + + fname = "chk_index"; + dent = debugfs_create_file(fname, S_IRUSR | S_IWUSR, dfs_rootdir, NULL, + &dfs_global_fops); + if (IS_ERR_OR_NULL(dent)) + goto out_remove; + dfs_chk_index = dent; + + fname = "chk_orphans"; + dent = debugfs_create_file(fname, S_IRUSR | S_IWUSR, dfs_rootdir, NULL, + &dfs_global_fops); + if (IS_ERR_OR_NULL(dent)) + goto out_remove; + dfs_chk_orph = dent; + + fname = "chk_lprops"; + dent = debugfs_create_file(fname, S_IRUSR | S_IWUSR, dfs_rootdir, NULL, + &dfs_global_fops); + if (IS_ERR_OR_NULL(dent)) + goto out_remove; + dfs_chk_lprops = dent; + + fname = "chk_fs"; + dent = debugfs_create_file(fname, S_IRUSR | S_IWUSR, dfs_rootdir, NULL, + &dfs_global_fops); + if (IS_ERR_OR_NULL(dent)) + goto out_remove; + dfs_chk_fs = dent; + + fname = "tst_recovery"; + dent = debugfs_create_file(fname, S_IRUSR | S_IWUSR, dfs_rootdir, NULL, + &dfs_global_fops); + if (IS_ERR_OR_NULL(dent)) + goto out_remove; + dfs_tst_rcvry = dent; + + return 0; + +out_remove: + debugfs_remove_recursive(dfs_rootdir); +out: + err = dent ? PTR_ERR(dent) : -ENODEV; + ubifs_err("cannot create \"%s\" debugfs file or directory, error %d\n", + fname, err); + return err; +} + +/** + * dbg_debugfs_exit - remove the "ubifs" directory from debugfs file-system. + */ +void dbg_debugfs_exit(void) +{ + if (IS_ENABLED(CONFIG_DEBUG_FS)) + debugfs_remove_recursive(dfs_rootdir); +} + +/** + * ubifs_debugging_init - initialize UBIFS debugging. + * @c: UBIFS file-system description object + * + * This function initializes debugging-related data for the file system. + * Returns zero in case of success and a negative error code in case of + * failure. + */ +int ubifs_debugging_init(struct ubifs_info *c) +{ + c->dbg = kzalloc(sizeof(struct ubifs_debug_info), GFP_KERNEL); + if (!c->dbg) + return -ENOMEM; + + return 0; } /** @@ -149,8 +3138,6 @@ out: */ void ubifs_debugging_exit(struct ubifs_info *c) { - vfree(c->dbg->buf); kfree(c->dbg); } - -#endif /* CONFIG_UBIFS_FS_DEBUG */ +#endif diff --git a/fs/ubifs/debug.h b/fs/ubifs/debug.h index 62617b6..6d325af 100644 --- a/fs/ubifs/debug.h +++ b/fs/ubifs/debug.h @@ -3,18 +3,7 @@ * * Copyright (C) 2006-2008 Nokia Corporation. * - * 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. - * - * 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., 51 - * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * SPDX-License-Identifier: GPL-2.0+ * * Authors: Artem Bityutskiy (Битюцкий Артём) * Adrian Hunter @@ -23,19 +12,32 @@ #ifndef __UBIFS_DEBUG_H__ #define __UBIFS_DEBUG_H__ -#ifdef CONFIG_UBIFS_FS_DEBUG +#define __UBOOT__ +/* Checking helper functions */ +typedef int (*dbg_leaf_callback)(struct ubifs_info *c, + struct ubifs_zbranch *zbr, void *priv); +typedef int (*dbg_znode_callback)(struct ubifs_info *c, + struct ubifs_znode *znode, void *priv); + +/* + * The UBIFS debugfs directory name pattern and maximum name length (3 for "ubi" + * + 1 for "_" and plus 2x2 for 2 UBI numbers and 1 for the trailing zero byte. + */ +#define UBIFS_DFS_DIR_NAME "ubi%d_%d" +#define UBIFS_DFS_DIR_LEN (3 + 1 + 2*2 + 1) /** * ubifs_debug_info - per-FS debugging information. - * @buf: a buffer of LEB size, used for various purposes * @old_zroot: old index root - used by 'dbg_check_old_index()' * @old_zroot_level: old index root level - used by 'dbg_check_old_index()' * @old_zroot_sqnum: old index root sqnum - used by 'dbg_check_old_index()' - * @failure_mode: failure mode for recovery testing - * @fail_delay: 0=>don't delay, 1=>delay a time, 2=>delay a number of calls - * @fail_timeout: time in jiffies when delay of failure mode expires - * @fail_cnt: current number of calls to failure mode I/O functions - * @fail_cnt_max: number of calls by which to delay failure mode + * + * @pc_happened: non-zero if an emulated power cut happened + * @pc_delay: 0=>don't delay, 1=>delay a time, 2=>delay a number of calls + * @pc_timeout: time in jiffies when delay of failure mode expires + * @pc_cnt: current number of calls to failure mode I/O functions + * @pc_cnt_max: number of calls by which to delay failure mode + * * @chk_lpt_sz: used by LPT tree size checker * @chk_lpt_sz2: used by LPT tree size checker * @chk_lpt_wastage: used by LPT tree size checker @@ -45,24 +47,44 @@ * @new_ihead_offs: used by debugging to check @c->ihead_offs * * @saved_lst: saved lprops statistics (used by 'dbg_save_space_info()') - * @saved_free: saved free space (used by 'dbg_save_space_info()') + * @saved_bi: saved budgeting information + * @saved_free: saved amount of free space + * @saved_idx_gc_cnt: saved value of @c->idx_gc_cnt + * + * @chk_gen: if general extra checks are enabled + * @chk_index: if index xtra checks are enabled + * @chk_orph: if orphans extra checks are enabled + * @chk_lprops: if lprops extra checks are enabled + * @chk_fs: if UBIFS contents extra checks are enabled + * @tst_rcvry: if UBIFS recovery testing mode enabled * - * dfs_dir_name: name of debugfs directory containing this file-system's files - * dfs_dir: direntry object of the file-system debugfs directory - * dfs_dump_lprops: "dump lprops" debugfs knob - * dfs_dump_budg: "dump budgeting information" debugfs knob - * dfs_dump_tnc: "dump TNC" debugfs knob + * @dfs_dir_name: name of debugfs directory containing this file-system's files + * @dfs_dir: direntry object of the file-system debugfs directory + * @dfs_dump_lprops: "dump lprops" debugfs knob + * @dfs_dump_budg: "dump budgeting information" debugfs knob + * @dfs_dump_tnc: "dump TNC" debugfs knob + * @dfs_chk_gen: debugfs knob to enable UBIFS general extra checks + * @dfs_chk_index: debugfs knob to enable UBIFS index extra checks + * @dfs_chk_orph: debugfs knob to enable UBIFS orphans extra checks + * @dfs_chk_lprops: debugfs knob to enable UBIFS LEP properties extra checks + * @dfs_chk_fs: debugfs knob to enable UBIFS contents extra checks + * @dfs_tst_rcvry: debugfs knob to enable UBIFS recovery testing + * @dfs_ro_error: debugfs knob to switch UBIFS to R/O mode (different to + * re-mounting to R/O mode because it does not flush any buffers + * and UBIFS just starts returning -EROFS on all write + * operations) */ struct ubifs_debug_info { - void *buf; struct ubifs_zbranch old_zroot; int old_zroot_level; unsigned long long old_zroot_sqnum; - int failure_mode; - int fail_delay; - unsigned long fail_timeout; - unsigned int fail_cnt; - unsigned int fail_cnt_max; + + int pc_happened; + int pc_delay; + unsigned long pc_timeout; + unsigned int pc_cnt; + unsigned int pc_cnt_max; + long long chk_lpt_sz; long long chk_lpt_sz2; long long chk_lpt_wastage; @@ -72,321 +94,285 @@ struct ubifs_debug_info { int new_ihead_offs; struct ubifs_lp_stats saved_lst; + struct ubifs_budg_info saved_bi; long long saved_free; + int saved_idx_gc_cnt; + + unsigned int chk_gen:1; + unsigned int chk_index:1; + unsigned int chk_orph:1; + unsigned int chk_lprops:1; + unsigned int chk_fs:1; + unsigned int tst_rcvry:1; - char dfs_dir_name[100]; + char dfs_dir_name[UBIFS_DFS_DIR_LEN + 1]; struct dentry *dfs_dir; struct dentry *dfs_dump_lprops; struct dentry *dfs_dump_budg; struct dentry *dfs_dump_tnc; + struct dentry *dfs_chk_gen; + struct dentry *dfs_chk_index; + struct dentry *dfs_chk_orph; + struct dentry *dfs_chk_lprops; + struct dentry *dfs_chk_fs; + struct dentry *dfs_tst_rcvry; + struct dentry *dfs_ro_error; }; -#define UBIFS_DBG(op) op +/** + * ubifs_global_debug_info - global (not per-FS) UBIFS debugging information. + * + * @chk_gen: if general extra checks are enabled + * @chk_index: if index xtra checks are enabled + * @chk_orph: if orphans extra checks are enabled + * @chk_lprops: if lprops extra checks are enabled + * @chk_fs: if UBIFS contents extra checks are enabled + * @tst_rcvry: if UBIFS recovery testing mode enabled + */ +struct ubifs_global_debug_info { + unsigned int chk_gen:1; + unsigned int chk_index:1; + unsigned int chk_orph:1; + unsigned int chk_lprops:1; + unsigned int chk_fs:1; + unsigned int tst_rcvry:1; +}; +#ifndef __UBOOT__ #define ubifs_assert(expr) do { \ if (unlikely(!(expr))) { \ - printk(KERN_CRIT "UBIFS assert failed in %s at %u (pid %d)\n", \ - __func__, __LINE__, 0); \ - dbg_dump_stack(); \ + pr_crit("UBIFS assert failed in %s at %u (pid %d)\n", \ + __func__, __LINE__, current->pid); \ + dump_stack(); \ } \ } while (0) #define ubifs_assert_cmt_locked(c) do { \ if (unlikely(down_write_trylock(&(c)->commit_sem))) { \ up_write(&(c)->commit_sem); \ - printk(KERN_CRIT "commit lock is not locked!\n"); \ + pr_crit("commit lock is not locked!\n"); \ ubifs_assert(0); \ } \ } while (0) -#define dbg_dump_stack() do { \ - if (!dbg_failure_mode) \ +#define ubifs_dbg_msg(type, fmt, ...) \ + pr_debug("UBIFS DBG " type " (pid %d): " fmt "\n", current->pid, \ + ##__VA_ARGS__) + +#define DBG_KEY_BUF_LEN 48 +#define ubifs_dbg_msg_key(type, key, fmt, ...) do { \ + char __tmp_key_buf[DBG_KEY_BUF_LEN]; \ + pr_debug("UBIFS DBG " type " (pid %d): " fmt "%s\n", current->pid, \ + ##__VA_ARGS__, \ + dbg_snprintf_key(c, key, __tmp_key_buf, DBG_KEY_BUF_LEN)); \ +} while (0) +#else +#define ubifs_assert(expr) do { \ + if (unlikely(!(expr))) { \ + pr_crit("UBIFS assert failed in %s at %u\n", \ + __func__, __LINE__); \ dump_stack(); \ + } \ } while (0) -/* Generic debugging messages */ -#define dbg_msg(fmt, ...) do { \ - spin_lock(&dbg_lock); \ - printk(KERN_DEBUG "UBIFS DBG (pid %d): %s: " fmt "\n", 0, \ - __func__, ##__VA_ARGS__); \ - spin_unlock(&dbg_lock); \ +#define ubifs_assert_cmt_locked(c) do { \ + if (unlikely(down_write_trylock(&(c)->commit_sem))) { \ + up_write(&(c)->commit_sem); \ + pr_crit("commit lock is not locked!\n"); \ + ubifs_assert(0); \ + } \ } while (0) -#define dbg_do_msg(typ, fmt, ...) do { \ - if (ubifs_msg_flags & typ) \ - dbg_msg(fmt, ##__VA_ARGS__); \ +#define ubifs_dbg_msg(type, fmt, ...) \ + pr_debug("UBIFS DBG " type ": " fmt "\n", \ + ##__VA_ARGS__) + +#define DBG_KEY_BUF_LEN 48 +#if defined CONFIG_MTD_DEBUG +#define ubifs_dbg_msg_key(type, key, fmt, ...) do { \ + char __tmp_key_buf[DBG_KEY_BUF_LEN]; \ + pr_debug("UBIFS DBG " type ": " fmt "%s\n", \ + ##__VA_ARGS__, \ + dbg_snprintf_key(c, key, __tmp_key_buf, DBG_KEY_BUF_LEN)); \ } while (0) - -#define dbg_err(fmt, ...) do { \ - spin_lock(&dbg_lock); \ - ubifs_err(fmt, ##__VA_ARGS__); \ - spin_unlock(&dbg_lock); \ +#else +#define ubifs_dbg_msg_key(type, key, fmt, ...) do { \ + pr_debug("UBIFS DBG\n"); \ } while (0) -const char *dbg_key_str0(const struct ubifs_info *c, - const union ubifs_key *key); -const char *dbg_key_str1(const struct ubifs_info *c, - const union ubifs_key *key); +#endif -/* - * DBGKEY macros require @dbg_lock to be held, which it is in the dbg message - * macros. - */ -#define DBGKEY(key) dbg_key_str0(c, (key)) -#define DBGKEY1(key) dbg_key_str1(c, (key)) +#endif /* General messages */ -#define dbg_gen(fmt, ...) dbg_do_msg(UBIFS_MSG_GEN, fmt, ##__VA_ARGS__) - +#define dbg_gen(fmt, ...) ubifs_dbg_msg("gen", fmt, ##__VA_ARGS__) /* Additional journal messages */ -#define dbg_jnl(fmt, ...) dbg_do_msg(UBIFS_MSG_JNL, fmt, ##__VA_ARGS__) - +#define dbg_jnl(fmt, ...) ubifs_dbg_msg("jnl", fmt, ##__VA_ARGS__) +#define dbg_jnlk(key, fmt, ...) \ + ubifs_dbg_msg_key("jnl", key, fmt, ##__VA_ARGS__) /* Additional TNC messages */ -#define dbg_tnc(fmt, ...) dbg_do_msg(UBIFS_MSG_TNC, fmt, ##__VA_ARGS__) - +#define dbg_tnc(fmt, ...) ubifs_dbg_msg("tnc", fmt, ##__VA_ARGS__) +#define dbg_tnck(key, fmt, ...) \ + ubifs_dbg_msg_key("tnc", key, fmt, ##__VA_ARGS__) /* Additional lprops messages */ -#define dbg_lp(fmt, ...) dbg_do_msg(UBIFS_MSG_LP, fmt, ##__VA_ARGS__) - +#define dbg_lp(fmt, ...) ubifs_dbg_msg("lp", fmt, ##__VA_ARGS__) /* Additional LEB find messages */ -#define dbg_find(fmt, ...) dbg_do_msg(UBIFS_MSG_FIND, fmt, ##__VA_ARGS__) - +#define dbg_find(fmt, ...) ubifs_dbg_msg("find", fmt, ##__VA_ARGS__) /* Additional mount messages */ -#define dbg_mnt(fmt, ...) dbg_do_msg(UBIFS_MSG_MNT, fmt, ##__VA_ARGS__) - +#define dbg_mnt(fmt, ...) ubifs_dbg_msg("mnt", fmt, ##__VA_ARGS__) +#define dbg_mntk(key, fmt, ...) \ + ubifs_dbg_msg_key("mnt", key, fmt, ##__VA_ARGS__) /* Additional I/O messages */ -#define dbg_io(fmt, ...) dbg_do_msg(UBIFS_MSG_IO, fmt, ##__VA_ARGS__) - +#define dbg_io(fmt, ...) ubifs_dbg_msg("io", fmt, ##__VA_ARGS__) /* Additional commit messages */ -#define dbg_cmt(fmt, ...) dbg_do_msg(UBIFS_MSG_CMT, fmt, ##__VA_ARGS__) - +#define dbg_cmt(fmt, ...) ubifs_dbg_msg("cmt", fmt, ##__VA_ARGS__) /* Additional budgeting messages */ -#define dbg_budg(fmt, ...) dbg_do_msg(UBIFS_MSG_BUDG, fmt, ##__VA_ARGS__) - +#define dbg_budg(fmt, ...) ubifs_dbg_msg("budg", fmt, ##__VA_ARGS__) /* Additional log messages */ -#define dbg_log(fmt, ...) dbg_do_msg(UBIFS_MSG_LOG, fmt, ##__VA_ARGS__) - +#define dbg_log(fmt, ...) ubifs_dbg_msg("log", fmt, ##__VA_ARGS__) /* Additional gc messages */ -#define dbg_gc(fmt, ...) dbg_do_msg(UBIFS_MSG_GC, fmt, ##__VA_ARGS__) - +#define dbg_gc(fmt, ...) ubifs_dbg_msg("gc", fmt, ##__VA_ARGS__) /* Additional scan messages */ -#define dbg_scan(fmt, ...) dbg_do_msg(UBIFS_MSG_SCAN, fmt, ##__VA_ARGS__) - +#define dbg_scan(fmt, ...) ubifs_dbg_msg("scan", fmt, ##__VA_ARGS__) /* Additional recovery messages */ -#define dbg_rcvry(fmt, ...) dbg_do_msg(UBIFS_MSG_RCVRY, fmt, ##__VA_ARGS__) +#define dbg_rcvry(fmt, ...) ubifs_dbg_msg("rcvry", fmt, ##__VA_ARGS__) + +#ifndef __UBOOT__ +extern struct ubifs_global_debug_info ubifs_dbg; + +static inline int dbg_is_chk_gen(const struct ubifs_info *c) +{ + return !!(ubifs_dbg.chk_gen || c->dbg->chk_gen); +} +static inline int dbg_is_chk_index(const struct ubifs_info *c) +{ + return !!(ubifs_dbg.chk_index || c->dbg->chk_index); +} +static inline int dbg_is_chk_orph(const struct ubifs_info *c) +{ + return !!(ubifs_dbg.chk_orph || c->dbg->chk_orph); +} +static inline int dbg_is_chk_lprops(const struct ubifs_info *c) +{ + return !!(ubifs_dbg.chk_lprops || c->dbg->chk_lprops); +} +static inline int dbg_is_chk_fs(const struct ubifs_info *c) +{ + return !!(ubifs_dbg.chk_fs || c->dbg->chk_fs); +} +static inline int dbg_is_tst_rcvry(const struct ubifs_info *c) +{ + return !!(ubifs_dbg.tst_rcvry || c->dbg->tst_rcvry); +} +static inline int dbg_is_power_cut(const struct ubifs_info *c) +{ + return !!c->dbg->pc_happened; +} -/* - * Debugging message type flags (must match msg_type_names in debug.c). - * - * UBIFS_MSG_GEN: general messages - * UBIFS_MSG_JNL: journal messages - * UBIFS_MSG_MNT: mount messages - * UBIFS_MSG_CMT: commit messages - * UBIFS_MSG_FIND: LEB find messages - * UBIFS_MSG_BUDG: budgeting messages - * UBIFS_MSG_GC: garbage collection messages - * UBIFS_MSG_TNC: TNC messages - * UBIFS_MSG_LP: lprops messages - * UBIFS_MSG_IO: I/O messages - * UBIFS_MSG_LOG: log messages - * UBIFS_MSG_SCAN: scan messages - * UBIFS_MSG_RCVRY: recovery messages - */ -enum { - UBIFS_MSG_GEN = 0x1, - UBIFS_MSG_JNL = 0x2, - UBIFS_MSG_MNT = 0x4, - UBIFS_MSG_CMT = 0x8, - UBIFS_MSG_FIND = 0x10, - UBIFS_MSG_BUDG = 0x20, - UBIFS_MSG_GC = 0x40, - UBIFS_MSG_TNC = 0x80, - UBIFS_MSG_LP = 0x100, - UBIFS_MSG_IO = 0x200, - UBIFS_MSG_LOG = 0x400, - UBIFS_MSG_SCAN = 0x800, - UBIFS_MSG_RCVRY = 0x1000, -}; - -/* Debugging message type flags for each default debug message level */ -#define UBIFS_MSG_LVL_0 0 -#define UBIFS_MSG_LVL_1 0x1 -#define UBIFS_MSG_LVL_2 0x7f -#define UBIFS_MSG_LVL_3 0xffff - -/* - * Debugging check flags (must match chk_names in debug.c). - * - * UBIFS_CHK_GEN: general checks - * UBIFS_CHK_TNC: check TNC - * UBIFS_CHK_IDX_SZ: check index size - * UBIFS_CHK_ORPH: check orphans - * UBIFS_CHK_OLD_IDX: check the old index - * UBIFS_CHK_LPROPS: check lprops - * UBIFS_CHK_FS: check the file-system - */ -enum { - UBIFS_CHK_GEN = 0x1, - UBIFS_CHK_TNC = 0x2, - UBIFS_CHK_IDX_SZ = 0x4, - UBIFS_CHK_ORPH = 0x8, - UBIFS_CHK_OLD_IDX = 0x10, - UBIFS_CHK_LPROPS = 0x20, - UBIFS_CHK_FS = 0x40, -}; - -/* - * Special testing flags (must match tst_names in debug.c). - * - * UBIFS_TST_FORCE_IN_THE_GAPS: force the use of in-the-gaps method - * UBIFS_TST_RCVRY: failure mode for recovery testing - */ -enum { - UBIFS_TST_FORCE_IN_THE_GAPS = 0x2, - UBIFS_TST_RCVRY = 0x4, -}; - -#if CONFIG_UBIFS_FS_DEBUG_MSG_LVL == 1 -#define UBIFS_MSG_FLAGS_DEFAULT UBIFS_MSG_LVL_1 -#elif CONFIG_UBIFS_FS_DEBUG_MSG_LVL == 2 -#define UBIFS_MSG_FLAGS_DEFAULT UBIFS_MSG_LVL_2 -#elif CONFIG_UBIFS_FS_DEBUG_MSG_LVL == 3 -#define UBIFS_MSG_FLAGS_DEFAULT UBIFS_MSG_LVL_3 -#else -#define UBIFS_MSG_FLAGS_DEFAULT UBIFS_MSG_LVL_0 -#endif - -#ifdef CONFIG_UBIFS_FS_DEBUG_CHKS -#define UBIFS_CHK_FLAGS_DEFAULT 0xffffffff +int ubifs_debugging_init(struct ubifs_info *c); +void ubifs_debugging_exit(struct ubifs_info *c); #else -#define UBIFS_CHK_FLAGS_DEFAULT 0 -#endif - -#define dbg_ntype(type) "" -#define dbg_cstate(cmt_state) "" -#define dbg_get_key_dump(c, key) ({}) -#define dbg_dump_inode(c, inode) ({}) -#define dbg_dump_node(c, node) ({}) -#define dbg_dump_budget_req(req) ({}) -#define dbg_dump_lstats(lst) ({}) -#define dbg_dump_budg(c) ({}) -#define dbg_dump_lprop(c, lp) ({}) -#define dbg_dump_lprops(c) ({}) -#define dbg_dump_lpt_info(c) ({}) -#define dbg_dump_leb(c, lnum) ({}) -#define dbg_dump_znode(c, znode) ({}) -#define dbg_dump_heap(c, heap, cat) ({}) -#define dbg_dump_pnode(c, pnode, parent, iip) ({}) -#define dbg_dump_tnc(c) ({}) -#define dbg_dump_index(c) ({}) - -#define dbg_walk_index(c, leaf_cb, znode_cb, priv) 0 -#define dbg_old_index_check_init(c, zroot) 0 -#define dbg_check_old_index(c, zroot) 0 -#define dbg_check_cats(c) 0 -#define dbg_check_ltab(c) 0 -#define dbg_chk_lpt_free_spc(c) 0 -#define dbg_chk_lpt_sz(c, action, len) 0 -#define dbg_check_synced_i_size(inode) 0 -#define dbg_check_dir_size(c, dir) 0 -#define dbg_check_tnc(c, x) 0 -#define dbg_check_idx_size(c, idx_size) 0 -#define dbg_check_filesystem(c) 0 -#define dbg_check_heap(c, heap, cat, add_pos) ({}) -#define dbg_check_lprops(c) 0 -#define dbg_check_lpt_nodes(c, cnode, row, col) 0 -#define dbg_force_in_the_gaps_enabled 0 -#define dbg_force_in_the_gaps() 0 -#define dbg_failure_mode 0 -#define dbg_failure_mode_registration(c) ({}) -#define dbg_failure_mode_deregistration(c) ({}) +static inline int dbg_is_chk_gen(const struct ubifs_info *c) +{ + return 0; +} +static inline int dbg_is_chk_index(const struct ubifs_info *c) +{ + return 0; +} +static inline int dbg_is_chk_orph(const struct ubifs_info *c) +{ + return 0; +} +static inline int dbg_is_chk_lprops(const struct ubifs_info *c) +{ + return 0; +} +static inline int dbg_is_chk_fs(const struct ubifs_info *c) +{ + return 0; +} +static inline int dbg_is_tst_rcvry(const struct ubifs_info *c) +{ + return 0; +} +static inline int dbg_is_power_cut(const struct ubifs_info *c) +{ + return 0; +} int ubifs_debugging_init(struct ubifs_info *c); void ubifs_debugging_exit(struct ubifs_info *c); -#else /* !CONFIG_UBIFS_FS_DEBUG */ - -#define UBIFS_DBG(op) - -/* Use "if (0)" to make compiler check arguments even if debugging is off */ -#define ubifs_assert(expr) do { \ - if (0 && (expr)) \ - printk(KERN_CRIT "UBIFS assert failed in %s at %u (pid %d)\n", \ - __func__, __LINE__, 0); \ -} while (0) - -#define dbg_err(fmt, ...) do { \ - if (0) \ - ubifs_err(fmt, ##__VA_ARGS__); \ -} while (0) - -#define dbg_msg(fmt, ...) do { \ - if (0) \ - printk(KERN_DEBUG "UBIFS DBG (pid %d): %s: " fmt "\n", \ - 0, __func__, ##__VA_ARGS__); \ -} while (0) - -#define dbg_dump_stack() -#define ubifs_assert_cmt_locked(c) - -#define dbg_gen(fmt, ...) dbg_msg(fmt, ##__VA_ARGS__) -#define dbg_jnl(fmt, ...) dbg_msg(fmt, ##__VA_ARGS__) -#define dbg_tnc(fmt, ...) dbg_msg(fmt, ##__VA_ARGS__) -#define dbg_lp(fmt, ...) dbg_msg(fmt, ##__VA_ARGS__) -#define dbg_find(fmt, ...) dbg_msg(fmt, ##__VA_ARGS__) -#define dbg_mnt(fmt, ...) dbg_msg(fmt, ##__VA_ARGS__) -#define dbg_io(fmt, ...) dbg_msg(fmt, ##__VA_ARGS__) -#define dbg_cmt(fmt, ...) dbg_msg(fmt, ##__VA_ARGS__) -#define dbg_budg(fmt, ...) dbg_msg(fmt, ##__VA_ARGS__) -#define dbg_log(fmt, ...) dbg_msg(fmt, ##__VA_ARGS__) -#define dbg_gc(fmt, ...) dbg_msg(fmt, ##__VA_ARGS__) -#define dbg_scan(fmt, ...) dbg_msg(fmt, ##__VA_ARGS__) -#define dbg_rcvry(fmt, ...) dbg_msg(fmt, ##__VA_ARGS__) - -#define DBGKEY(key) ((char *)(key)) -#define DBGKEY1(key) ((char *)(key)) - -#define ubifs_debugging_init(c) 0 -#define ubifs_debugging_exit(c) ({}) - -#define dbg_ntype(type) "" -#define dbg_cstate(cmt_state) "" -#define dbg_get_key_dump(c, key) ({}) -#define dbg_dump_inode(c, inode) ({}) -#define dbg_dump_node(c, node) ({}) -#define dbg_dump_budget_req(req) ({}) -#define dbg_dump_lstats(lst) ({}) -#define dbg_dump_budg(c) ({}) -#define dbg_dump_lprop(c, lp) ({}) -#define dbg_dump_lprops(c) ({}) -#define dbg_dump_lpt_info(c) ({}) -#define dbg_dump_leb(c, lnum) ({}) -#define dbg_dump_znode(c, znode) ({}) -#define dbg_dump_heap(c, heap, cat) ({}) -#define dbg_dump_pnode(c, pnode, parent, iip) ({}) -#define dbg_dump_tnc(c) ({}) -#define dbg_dump_index(c) ({}) - -#define dbg_walk_index(c, leaf_cb, znode_cb, priv) 0 -#define dbg_old_index_check_init(c, zroot) 0 -#define dbg_check_old_index(c, zroot) 0 -#define dbg_check_cats(c) 0 -#define dbg_check_ltab(c) 0 -#define dbg_chk_lpt_free_spc(c) 0 -#define dbg_chk_lpt_sz(c, action, len) 0 -#define dbg_check_synced_i_size(inode) 0 -#define dbg_check_dir_size(c, dir) 0 -#define dbg_check_tnc(c, x) 0 -#define dbg_check_idx_size(c, idx_size) 0 -#define dbg_check_filesystem(c) 0 -#define dbg_check_heap(c, heap, cat, add_pos) ({}) -#define dbg_check_lprops(c) 0 -#define dbg_check_lpt_nodes(c, cnode, row, col) 0 -#define dbg_force_in_the_gaps_enabled 0 -#define dbg_force_in_the_gaps() 0 -#define dbg_failure_mode 0 -#define dbg_failure_mode_registration(c) ({}) -#define dbg_failure_mode_deregistration(c) ({}) +#endif -#endif /* !CONFIG_UBIFS_FS_DEBUG */ +/* Dump functions */ +const char *dbg_ntype(int type); +const char *dbg_cstate(int cmt_state); +const char *dbg_jhead(int jhead); +const char *dbg_get_key_dump(const struct ubifs_info *c, + const union ubifs_key *key); +const char *dbg_snprintf_key(const struct ubifs_info *c, + const union ubifs_key *key, char *buffer, int len); +void ubifs_dump_inode(struct ubifs_info *c, const struct inode *inode); +void ubifs_dump_node(const struct ubifs_info *c, const void *node); +void ubifs_dump_budget_req(const struct ubifs_budget_req *req); +void ubifs_dump_lstats(const struct ubifs_lp_stats *lst); +void ubifs_dump_budg(struct ubifs_info *c, const struct ubifs_budg_info *bi); +void ubifs_dump_lprop(const struct ubifs_info *c, + const struct ubifs_lprops *lp); +void ubifs_dump_lprops(struct ubifs_info *c); +void ubifs_dump_lpt_info(struct ubifs_info *c); +void ubifs_dump_leb(const struct ubifs_info *c, int lnum); +void ubifs_dump_sleb(const struct ubifs_info *c, + const struct ubifs_scan_leb *sleb, int offs); +void ubifs_dump_znode(const struct ubifs_info *c, + const struct ubifs_znode *znode); +void ubifs_dump_heap(struct ubifs_info *c, struct ubifs_lpt_heap *heap, + int cat); +void ubifs_dump_pnode(struct ubifs_info *c, struct ubifs_pnode *pnode, + struct ubifs_nnode *parent, int iip); +void ubifs_dump_tnc(struct ubifs_info *c); +void ubifs_dump_index(struct ubifs_info *c); +void ubifs_dump_lpt_lebs(const struct ubifs_info *c); + +int dbg_walk_index(struct ubifs_info *c, dbg_leaf_callback leaf_cb, + dbg_znode_callback znode_cb, void *priv); + +/* Checking functions */ +void dbg_save_space_info(struct ubifs_info *c); +int dbg_check_space_info(struct ubifs_info *c); +int dbg_check_lprops(struct ubifs_info *c); +int dbg_old_index_check_init(struct ubifs_info *c, struct ubifs_zbranch *zroot); +int dbg_check_old_index(struct ubifs_info *c, struct ubifs_zbranch *zroot); +int dbg_check_cats(struct ubifs_info *c); +int dbg_check_ltab(struct ubifs_info *c); +int dbg_chk_lpt_free_spc(struct ubifs_info *c); +int dbg_chk_lpt_sz(struct ubifs_info *c, int action, int len); +int dbg_check_synced_i_size(const struct ubifs_info *c, struct inode *inode); +int dbg_check_dir(struct ubifs_info *c, const struct inode *dir); +int dbg_check_tnc(struct ubifs_info *c, int extra); +int dbg_check_idx_size(struct ubifs_info *c, long long idx_size); +int dbg_check_filesystem(struct ubifs_info *c); +void dbg_check_heap(struct ubifs_info *c, struct ubifs_lpt_heap *heap, int cat, + int add_pos); +int dbg_check_lpt_nodes(struct ubifs_info *c, struct ubifs_cnode *cnode, + int row, int col); +int dbg_check_inode_size(struct ubifs_info *c, const struct inode *inode, + loff_t size); +int dbg_check_data_nodes_order(struct ubifs_info *c, struct list_head *head); +int dbg_check_nondata_nodes_order(struct ubifs_info *c, struct list_head *head); + +int dbg_leb_write(struct ubifs_info *c, int lnum, const void *buf, int offs, + int len); +int dbg_leb_change(struct ubifs_info *c, int lnum, const void *buf, int len); +int dbg_leb_unmap(struct ubifs_info *c, int lnum); +int dbg_leb_map(struct ubifs_info *c, int lnum); + +/* Debugfs-related stuff */ +int dbg_debugfs_init(void); +void dbg_debugfs_exit(void); +int dbg_debugfs_init_fs(struct ubifs_info *c); +void dbg_debugfs_exit_fs(struct ubifs_info *c); #endif /* !__UBIFS_DEBUG_H__ */ diff --git a/fs/ubifs/io.c b/fs/ubifs/io.c index aae5c65..f87341e 100644 --- a/fs/ubifs/io.c +++ b/fs/ubifs/io.c @@ -4,18 +4,7 @@ * Copyright (C) 2006-2008 Nokia Corporation. * Copyright (C) 2006, 2007 University of Szeged, Hungary * - * 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. - * - * 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., 51 - * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * SPDX-License-Identifier: GPL-2.0+ * * Authors: Artem Bityutskiy (Битюцкий Артём) * Adrian Hunter @@ -31,6 +20,26 @@ * buffer is full or when it is not used for some time (by timer). This is * similar to the mechanism is used by JFFS2. * + * UBIFS distinguishes between minimum write size (@c->min_io_size) and maximum + * write size (@c->max_write_size). The latter is the maximum amount of bytes + * the underlying flash is able to program at a time, and writing in + * @c->max_write_size units should presumably be faster. Obviously, + * @c->min_io_size <= @c->max_write_size. Write-buffers are of + * @c->max_write_size bytes in size for maximum performance. However, when a + * write-buffer is flushed, only the portion of it (aligned to @c->min_io_size + * boundary) which contains data is written, not the whole write-buffer, + * because this is more space-efficient. + * + * This optimization adds few complications to the code. Indeed, on the one + * hand, we want to write in optimal @c->max_write_size bytes chunks, which + * also means aligning writes at the @c->max_write_size bytes offsets. On the + * other hand, we do not want to waste space when synchronizing the write + * buffer, so during synchronization we writes in smaller chunks. And this makes + * the next write offset to be not aligned to @c->max_write_size bytes. So the + * have to make sure that the write-buffer offset (@wbuf->offs) becomes aligned + * to @c->max_write_size bytes again. We do this by temporarily shrinking + * write-buffer size (@wbuf->size). + * * Write-buffers are defined by 'struct ubifs_wbuf' objects and protected by * mutexes defined inside these objects. Since sometimes upper-level code * has to lock the write-buffer (e.g. journal space reservation code), many @@ -46,10 +55,18 @@ * UBIFS uses padding when it pads to the next min. I/O unit. In this case it * uses padding nodes or padding bytes, if the padding node does not fit. * - * All UBIFS nodes are protected by CRC checksums and UBIFS checks all nodes - * every time they are read from the flash media. + * All UBIFS nodes are protected by CRC checksums and UBIFS checks CRC when + * they are read from the flash media. */ +#define __UBOOT__ +#ifndef __UBOOT__ +#include <linux/crc32.h> +#include <linux/slab.h> +#else +#include <linux/compat.h> +#include <linux/err.h> +#endif #include "ubifs.h" /** @@ -59,12 +76,129 @@ */ void ubifs_ro_mode(struct ubifs_info *c, int err) { - if (!c->ro_media) { - c->ro_media = 1; + if (!c->ro_error) { + c->ro_error = 1; c->no_chk_data_crc = 0; + c->vfs_sb->s_flags |= MS_RDONLY; ubifs_warn("switched to read-only mode, error %d", err); - dbg_dump_stack(); + dump_stack(); + } +} + +/* + * Below are simple wrappers over UBI I/O functions which include some + * additional checks and UBIFS debugging stuff. See corresponding UBI function + * for more information. + */ + +int ubifs_leb_read(const struct ubifs_info *c, int lnum, void *buf, int offs, + int len, int even_ebadmsg) +{ + int err; + + err = ubi_read(c->ubi, lnum, buf, offs, len); + /* + * In case of %-EBADMSG print the error message only if the + * @even_ebadmsg is true. + */ + if (err && (err != -EBADMSG || even_ebadmsg)) { + ubifs_err("reading %d bytes from LEB %d:%d failed, error %d", + len, lnum, offs, err); + dump_stack(); + } + return err; +} + +int ubifs_leb_write(struct ubifs_info *c, int lnum, const void *buf, int offs, + int len) +{ + int err; + + ubifs_assert(!c->ro_media && !c->ro_mount); + if (c->ro_error) + return -EROFS; + if (!dbg_is_tst_rcvry(c)) + err = ubi_leb_write(c->ubi, lnum, buf, offs, len); + else + err = dbg_leb_write(c, lnum, buf, offs, len); + if (err) { + ubifs_err("writing %d bytes to LEB %d:%d failed, error %d", + len, lnum, offs, err); + ubifs_ro_mode(c, err); + dump_stack(); + } + return err; +} + +int ubifs_leb_change(struct ubifs_info *c, int lnum, const void *buf, int len) +{ + int err; + + ubifs_assert(!c->ro_media && !c->ro_mount); + if (c->ro_error) + return -EROFS; + if (!dbg_is_tst_rcvry(c)) + err = ubi_leb_change(c->ubi, lnum, buf, len); + else + err = dbg_leb_change(c, lnum, buf, len); + if (err) { + ubifs_err("changing %d bytes in LEB %d failed, error %d", + len, lnum, err); + ubifs_ro_mode(c, err); + dump_stack(); } + return err; +} + +int ubifs_leb_unmap(struct ubifs_info *c, int lnum) +{ + int err; + + ubifs_assert(!c->ro_media && !c->ro_mount); + if (c->ro_error) + return -EROFS; + if (!dbg_is_tst_rcvry(c)) + err = ubi_leb_unmap(c->ubi, lnum); + else + err = dbg_leb_unmap(c, lnum); + if (err) { + ubifs_err("unmap LEB %d failed, error %d", lnum, err); + ubifs_ro_mode(c, err); + dump_stack(); + } + return err; +} + +int ubifs_leb_map(struct ubifs_info *c, int lnum) +{ + int err; + + ubifs_assert(!c->ro_media && !c->ro_mount); + if (c->ro_error) + return -EROFS; + if (!dbg_is_tst_rcvry(c)) + err = ubi_leb_map(c->ubi, lnum); + else + err = dbg_leb_map(c, lnum); + if (err) { + ubifs_err("mapping LEB %d failed, error %d", lnum, err); + ubifs_ro_mode(c, err); + dump_stack(); + } + return err; +} + +int ubifs_is_mapped(const struct ubifs_info *c, int lnum) +{ + int err; + + err = ubi_is_mapped(c->ubi, lnum); + if (err < 0) { + ubifs_err("ubi_is_mapped failed for LEB %d, error %d", + lnum, err); + dump_stack(); + } + return err; } /** @@ -85,8 +219,12 @@ void ubifs_ro_mode(struct ubifs_info *c, int err) * This function may skip data nodes CRC checking if @c->no_chk_data_crc is * true, which is controlled by corresponding UBIFS mount option. However, if * @must_chk_crc is true, then @c->no_chk_data_crc is ignored and CRC is - * checked. Similarly, if @c->always_chk_crc is true, @c->no_chk_data_crc is - * ignored and CRC is checked. + * checked. Similarly, if @c->mounting or @c->remounting_rw is true (we are + * mounting or re-mounting to R/W mode), @c->no_chk_data_crc is ignored and CRC + * is checked. This is because during mounting or re-mounting from R/O mode to + * R/W mode we may read journal nodes (when replying the journal or doing the + * recovery) and the journal nodes may potentially be corrupted, so checking is + * required. * * This function returns zero in case of success and %-EUCLEAN in case of bad * CRC or magic. @@ -128,8 +266,8 @@ int ubifs_check_node(const struct ubifs_info *c, const void *buf, int lnum, node_len > c->ranges[type].max_len) goto out_len; - if (!must_chk_crc && type == UBIFS_DATA_NODE && !c->always_chk_crc && - c->no_chk_data_crc) + if (!must_chk_crc && type == UBIFS_DATA_NODE && !c->mounting && + !c->remounting_rw && c->no_chk_data_crc) return 0; crc = crc32(UBIFS_CRC32_INIT, buf + 8, node_len - 8); @@ -150,8 +288,8 @@ out_len: out: if (!quiet) { ubifs_err("bad node at LEB %d:%d", lnum, offs); - dbg_dump_node(c, buf); - dbg_dump_stack(); + ubifs_dump_node(c, buf); + dump_stack(); } return err; } @@ -257,6 +395,571 @@ void ubifs_prepare_node(struct ubifs_info *c, void *node, int len, int pad) } /** + * ubifs_prep_grp_node - prepare node of a group to be written to flash. + * @c: UBIFS file-system description object + * @node: the node to pad + * @len: node length + * @last: indicates the last node of the group + * + * This function prepares node at @node to be written to the media - it + * calculates node CRC and fills the common header. + */ +void ubifs_prep_grp_node(struct ubifs_info *c, void *node, int len, int last) +{ + uint32_t crc; + struct ubifs_ch *ch = node; + unsigned long long sqnum = next_sqnum(c); + + ubifs_assert(len >= UBIFS_CH_SZ); + + ch->magic = cpu_to_le32(UBIFS_NODE_MAGIC); + ch->len = cpu_to_le32(len); + if (last) + ch->group_type = UBIFS_LAST_OF_NODE_GROUP; + else + ch->group_type = UBIFS_IN_NODE_GROUP; + ch->sqnum = cpu_to_le64(sqnum); + ch->padding[0] = ch->padding[1] = 0; + crc = crc32(UBIFS_CRC32_INIT, node + 8, len - 8); + ch->crc = cpu_to_le32(crc); +} + +#ifndef __UBOOT__ +/** + * wbuf_timer_callback - write-buffer timer callback function. + * @data: timer data (write-buffer descriptor) + * + * This function is called when the write-buffer timer expires. + */ +static enum hrtimer_restart wbuf_timer_callback_nolock(struct hrtimer *timer) +{ + struct ubifs_wbuf *wbuf = container_of(timer, struct ubifs_wbuf, timer); + + dbg_io("jhead %s", dbg_jhead(wbuf->jhead)); + wbuf->need_sync = 1; + wbuf->c->need_wbuf_sync = 1; + ubifs_wake_up_bgt(wbuf->c); + return HRTIMER_NORESTART; +} + +/** + * new_wbuf_timer - start new write-buffer timer. + * @wbuf: write-buffer descriptor + */ +static void new_wbuf_timer_nolock(struct ubifs_wbuf *wbuf) +{ + ubifs_assert(!hrtimer_active(&wbuf->timer)); + + if (wbuf->no_timer) + return; + dbg_io("set timer for jhead %s, %llu-%llu millisecs", + dbg_jhead(wbuf->jhead), + div_u64(ktime_to_ns(wbuf->softlimit), USEC_PER_SEC), + div_u64(ktime_to_ns(wbuf->softlimit) + wbuf->delta, + USEC_PER_SEC)); + hrtimer_start_range_ns(&wbuf->timer, wbuf->softlimit, wbuf->delta, + HRTIMER_MODE_REL); +} +#endif + +/** + * cancel_wbuf_timer - cancel write-buffer timer. + * @wbuf: write-buffer descriptor + */ +static void cancel_wbuf_timer_nolock(struct ubifs_wbuf *wbuf) +{ + if (wbuf->no_timer) + return; + wbuf->need_sync = 0; +#ifndef __UBOOT__ + hrtimer_cancel(&wbuf->timer); +#endif +} + +/** + * ubifs_wbuf_sync_nolock - synchronize write-buffer. + * @wbuf: write-buffer to synchronize + * + * This function synchronizes write-buffer @buf and returns zero in case of + * success or a negative error code in case of failure. + * + * Note, although write-buffers are of @c->max_write_size, this function does + * not necessarily writes all @c->max_write_size bytes to the flash. Instead, + * if the write-buffer is only partially filled with data, only the used part + * of the write-buffer (aligned on @c->min_io_size boundary) is synchronized. + * This way we waste less space. + */ +int ubifs_wbuf_sync_nolock(struct ubifs_wbuf *wbuf) +{ + struct ubifs_info *c = wbuf->c; + int err, dirt, sync_len; + + cancel_wbuf_timer_nolock(wbuf); + if (!wbuf->used || wbuf->lnum == -1) + /* Write-buffer is empty or not seeked */ + return 0; + + dbg_io("LEB %d:%d, %d bytes, jhead %s", + wbuf->lnum, wbuf->offs, wbuf->used, dbg_jhead(wbuf->jhead)); + ubifs_assert(!(wbuf->avail & 7)); + ubifs_assert(wbuf->offs + wbuf->size <= c->leb_size); + ubifs_assert(wbuf->size >= c->min_io_size); + ubifs_assert(wbuf->size <= c->max_write_size); + ubifs_assert(wbuf->size % c->min_io_size == 0); + ubifs_assert(!c->ro_media && !c->ro_mount); + if (c->leb_size - wbuf->offs >= c->max_write_size) + ubifs_assert(!((wbuf->offs + wbuf->size) % c->max_write_size)); + + if (c->ro_error) + return -EROFS; + + /* + * Do not write whole write buffer but write only the minimum necessary + * amount of min. I/O units. + */ + sync_len = ALIGN(wbuf->used, c->min_io_size); + dirt = sync_len - wbuf->used; + if (dirt) + ubifs_pad(c, wbuf->buf + wbuf->used, dirt); + err = ubifs_leb_write(c, wbuf->lnum, wbuf->buf, wbuf->offs, sync_len); + if (err) + return err; + + spin_lock(&wbuf->lock); + wbuf->offs += sync_len; + /* + * Now @wbuf->offs is not necessarily aligned to @c->max_write_size. + * But our goal is to optimize writes and make sure we write in + * @c->max_write_size chunks and to @c->max_write_size-aligned offset. + * Thus, if @wbuf->offs is not aligned to @c->max_write_size now, make + * sure that @wbuf->offs + @wbuf->size is aligned to + * @c->max_write_size. This way we make sure that after next + * write-buffer flush we are again at the optimal offset (aligned to + * @c->max_write_size). + */ + if (c->leb_size - wbuf->offs < c->max_write_size) + wbuf->size = c->leb_size - wbuf->offs; + else if (wbuf->offs & (c->max_write_size - 1)) + wbuf->size = ALIGN(wbuf->offs, c->max_write_size) - wbuf->offs; + else + wbuf->size = c->max_write_size; + wbuf->avail = wbuf->size; + wbuf->used = 0; + wbuf->next_ino = 0; + spin_unlock(&wbuf->lock); + + if (wbuf->sync_callback) + err = wbuf->sync_callback(c, wbuf->lnum, + c->leb_size - wbuf->offs, dirt); + return err; +} + +/** + * ubifs_wbuf_seek_nolock - seek write-buffer. + * @wbuf: write-buffer + * @lnum: logical eraseblock number to seek to + * @offs: logical eraseblock offset to seek to + * + * This function targets the write-buffer to logical eraseblock @lnum:@offs. + * The write-buffer has to be empty. Returns zero in case of success and a + * negative error code in case of failure. + */ +int ubifs_wbuf_seek_nolock(struct ubifs_wbuf *wbuf, int lnum, int offs) +{ + const struct ubifs_info *c = wbuf->c; + + dbg_io("LEB %d:%d, jhead %s", lnum, offs, dbg_jhead(wbuf->jhead)); + ubifs_assert(lnum >= 0 && lnum < c->leb_cnt); + ubifs_assert(offs >= 0 && offs <= c->leb_size); + ubifs_assert(offs % c->min_io_size == 0 && !(offs & 7)); + ubifs_assert(lnum != wbuf->lnum); + ubifs_assert(wbuf->used == 0); + + spin_lock(&wbuf->lock); + wbuf->lnum = lnum; + wbuf->offs = offs; + if (c->leb_size - wbuf->offs < c->max_write_size) + wbuf->size = c->leb_size - wbuf->offs; + else if (wbuf->offs & (c->max_write_size - 1)) + wbuf->size = ALIGN(wbuf->offs, c->max_write_size) - wbuf->offs; + else + wbuf->size = c->max_write_size; + wbuf->avail = wbuf->size; + wbuf->used = 0; + spin_unlock(&wbuf->lock); + + return 0; +} + +#ifndef __UBOOT__ +/** + * ubifs_bg_wbufs_sync - synchronize write-buffers. + * @c: UBIFS file-system description object + * + * This function is called by background thread to synchronize write-buffers. + * Returns zero in case of success and a negative error code in case of + * failure. + */ +int ubifs_bg_wbufs_sync(struct ubifs_info *c) +{ + int err, i; + + ubifs_assert(!c->ro_media && !c->ro_mount); + if (!c->need_wbuf_sync) + return 0; + c->need_wbuf_sync = 0; + + if (c->ro_error) { + err = -EROFS; + goto out_timers; + } + + dbg_io("synchronize"); + for (i = 0; i < c->jhead_cnt; i++) { + struct ubifs_wbuf *wbuf = &c->jheads[i].wbuf; + + cond_resched(); + + /* + * If the mutex is locked then wbuf is being changed, so + * synchronization is not necessary. + */ + if (mutex_is_locked(&wbuf->io_mutex)) + continue; + + mutex_lock_nested(&wbuf->io_mutex, wbuf->jhead); + if (!wbuf->need_sync) { + mutex_unlock(&wbuf->io_mutex); + continue; + } + + err = ubifs_wbuf_sync_nolock(wbuf); + mutex_unlock(&wbuf->io_mutex); + if (err) { + ubifs_err("cannot sync write-buffer, error %d", err); + ubifs_ro_mode(c, err); + goto out_timers; + } + } + + return 0; + +out_timers: + /* Cancel all timers to prevent repeated errors */ + for (i = 0; i < c->jhead_cnt; i++) { + struct ubifs_wbuf *wbuf = &c->jheads[i].wbuf; + + mutex_lock_nested(&wbuf->io_mutex, wbuf->jhead); + cancel_wbuf_timer_nolock(wbuf); + mutex_unlock(&wbuf->io_mutex); + } + return err; +} + +/** + * ubifs_wbuf_write_nolock - write data to flash via write-buffer. + * @wbuf: write-buffer + * @buf: node to write + * @len: node length + * + * This function writes data to flash via write-buffer @wbuf. This means that + * the last piece of the node won't reach the flash media immediately if it + * does not take whole max. write unit (@c->max_write_size). Instead, the node + * will sit in RAM until the write-buffer is synchronized (e.g., by timer, or + * because more data are appended to the write-buffer). + * + * This function returns zero in case of success and a negative error code in + * case of failure. If the node cannot be written because there is no more + * space in this logical eraseblock, %-ENOSPC is returned. + */ +int ubifs_wbuf_write_nolock(struct ubifs_wbuf *wbuf, void *buf, int len) +{ + struct ubifs_info *c = wbuf->c; + int err, written, n, aligned_len = ALIGN(len, 8); + + dbg_io("%d bytes (%s) to jhead %s wbuf at LEB %d:%d", len, + dbg_ntype(((struct ubifs_ch *)buf)->node_type), + dbg_jhead(wbuf->jhead), wbuf->lnum, wbuf->offs + wbuf->used); + ubifs_assert(len > 0 && wbuf->lnum >= 0 && wbuf->lnum < c->leb_cnt); + ubifs_assert(wbuf->offs >= 0 && wbuf->offs % c->min_io_size == 0); + ubifs_assert(!(wbuf->offs & 7) && wbuf->offs <= c->leb_size); + ubifs_assert(wbuf->avail > 0 && wbuf->avail <= wbuf->size); + ubifs_assert(wbuf->size >= c->min_io_size); + ubifs_assert(wbuf->size <= c->max_write_size); + ubifs_assert(wbuf->size % c->min_io_size == 0); + ubifs_assert(mutex_is_locked(&wbuf->io_mutex)); + ubifs_assert(!c->ro_media && !c->ro_mount); + ubifs_assert(!c->space_fixup); + if (c->leb_size - wbuf->offs >= c->max_write_size) + ubifs_assert(!((wbuf->offs + wbuf->size) % c->max_write_size)); + + if (c->leb_size - wbuf->offs - wbuf->used < aligned_len) { + err = -ENOSPC; + goto out; + } + + cancel_wbuf_timer_nolock(wbuf); + + if (c->ro_error) + return -EROFS; + + if (aligned_len <= wbuf->avail) { + /* + * The node is not very large and fits entirely within + * write-buffer. + */ + memcpy(wbuf->buf + wbuf->used, buf, len); + + if (aligned_len == wbuf->avail) { + dbg_io("flush jhead %s wbuf to LEB %d:%d", + dbg_jhead(wbuf->jhead), wbuf->lnum, wbuf->offs); + err = ubifs_leb_write(c, wbuf->lnum, wbuf->buf, + wbuf->offs, wbuf->size); + if (err) + goto out; + + spin_lock(&wbuf->lock); + wbuf->offs += wbuf->size; + if (c->leb_size - wbuf->offs >= c->max_write_size) + wbuf->size = c->max_write_size; + else + wbuf->size = c->leb_size - wbuf->offs; + wbuf->avail = wbuf->size; + wbuf->used = 0; + wbuf->next_ino = 0; + spin_unlock(&wbuf->lock); + } else { + spin_lock(&wbuf->lock); + wbuf->avail -= aligned_len; + wbuf->used += aligned_len; + spin_unlock(&wbuf->lock); + } + + goto exit; + } + + written = 0; + + if (wbuf->used) { + /* + * The node is large enough and does not fit entirely within + * current available space. We have to fill and flush + * write-buffer and switch to the next max. write unit. + */ + dbg_io("flush jhead %s wbuf to LEB %d:%d", + dbg_jhead(wbuf->jhead), wbuf->lnum, wbuf->offs); + memcpy(wbuf->buf + wbuf->used, buf, wbuf->avail); + err = ubifs_leb_write(c, wbuf->lnum, wbuf->buf, wbuf->offs, + wbuf->size); + if (err) + goto out; + + wbuf->offs += wbuf->size; + len -= wbuf->avail; + aligned_len -= wbuf->avail; + written += wbuf->avail; + } else if (wbuf->offs & (c->max_write_size - 1)) { + /* + * The write-buffer offset is not aligned to + * @c->max_write_size and @wbuf->size is less than + * @c->max_write_size. Write @wbuf->size bytes to make sure the + * following writes are done in optimal @c->max_write_size + * chunks. + */ + dbg_io("write %d bytes to LEB %d:%d", + wbuf->size, wbuf->lnum, wbuf->offs); + err = ubifs_leb_write(c, wbuf->lnum, buf, wbuf->offs, + wbuf->size); + if (err) + goto out; + + wbuf->offs += wbuf->size; + len -= wbuf->size; + aligned_len -= wbuf->size; + written += wbuf->size; + } + + /* + * The remaining data may take more whole max. write units, so write the + * remains multiple to max. write unit size directly to the flash media. + * We align node length to 8-byte boundary because we anyway flash wbuf + * if the remaining space is less than 8 bytes. + */ + n = aligned_len >> c->max_write_shift; + if (n) { + n <<= c->max_write_shift; + dbg_io("write %d bytes to LEB %d:%d", n, wbuf->lnum, + wbuf->offs); + err = ubifs_leb_write(c, wbuf->lnum, buf + written, + wbuf->offs, n); + if (err) + goto out; + wbuf->offs += n; + aligned_len -= n; + len -= n; + written += n; + } + + spin_lock(&wbuf->lock); + if (aligned_len) + /* + * And now we have what's left and what does not take whole + * max. write unit, so write it to the write-buffer and we are + * done. + */ + memcpy(wbuf->buf, buf + written, len); + + if (c->leb_size - wbuf->offs >= c->max_write_size) + wbuf->size = c->max_write_size; + else + wbuf->size = c->leb_size - wbuf->offs; + wbuf->avail = wbuf->size - aligned_len; + wbuf->used = aligned_len; + wbuf->next_ino = 0; + spin_unlock(&wbuf->lock); + +exit: + if (wbuf->sync_callback) { + int free = c->leb_size - wbuf->offs - wbuf->used; + + err = wbuf->sync_callback(c, wbuf->lnum, free, 0); + if (err) + goto out; + } + + if (wbuf->used) + new_wbuf_timer_nolock(wbuf); + + return 0; + +out: + ubifs_err("cannot write %d bytes to LEB %d:%d, error %d", + len, wbuf->lnum, wbuf->offs, err); + ubifs_dump_node(c, buf); + dump_stack(); + ubifs_dump_leb(c, wbuf->lnum); + return err; +} + +/** + * ubifs_write_node - write node to the media. + * @c: UBIFS file-system description object + * @buf: the node to write + * @len: node length + * @lnum: logical eraseblock number + * @offs: offset within the logical eraseblock + * + * This function automatically fills node magic number, assigns sequence + * number, and calculates node CRC checksum. The length of the @buf buffer has + * to be aligned to the minimal I/O unit size. This function automatically + * appends padding node and padding bytes if needed. Returns zero in case of + * success and a negative error code in case of failure. + */ +int ubifs_write_node(struct ubifs_info *c, void *buf, int len, int lnum, + int offs) +{ + int err, buf_len = ALIGN(len, c->min_io_size); + + dbg_io("LEB %d:%d, %s, length %d (aligned %d)", + lnum, offs, dbg_ntype(((struct ubifs_ch *)buf)->node_type), len, + buf_len); + ubifs_assert(lnum >= 0 && lnum < c->leb_cnt && offs >= 0); + ubifs_assert(offs % c->min_io_size == 0 && offs < c->leb_size); + ubifs_assert(!c->ro_media && !c->ro_mount); + ubifs_assert(!c->space_fixup); + + if (c->ro_error) + return -EROFS; + + ubifs_prepare_node(c, buf, len, 1); + err = ubifs_leb_write(c, lnum, buf, offs, buf_len); + if (err) + ubifs_dump_node(c, buf); + + return err; +} +#endif + +/** + * ubifs_read_node_wbuf - read node from the media or write-buffer. + * @wbuf: wbuf to check for un-written data + * @buf: buffer to read to + * @type: node type + * @len: node length + * @lnum: logical eraseblock number + * @offs: offset within the logical eraseblock + * + * This function reads a node of known type and length, checks it and stores + * in @buf. If the node partially or fully sits in the write-buffer, this + * function takes data from the buffer, otherwise it reads the flash media. + * Returns zero in case of success, %-EUCLEAN if CRC mismatched and a negative + * error code in case of failure. + */ +int ubifs_read_node_wbuf(struct ubifs_wbuf *wbuf, void *buf, int type, int len, + int lnum, int offs) +{ + const struct ubifs_info *c = wbuf->c; + int err, rlen, overlap; + struct ubifs_ch *ch = buf; + + dbg_io("LEB %d:%d, %s, length %d, jhead %s", lnum, offs, + dbg_ntype(type), len, dbg_jhead(wbuf->jhead)); + ubifs_assert(wbuf && lnum >= 0 && lnum < c->leb_cnt && offs >= 0); + ubifs_assert(!(offs & 7) && offs < c->leb_size); + ubifs_assert(type >= 0 && type < UBIFS_NODE_TYPES_CNT); + + spin_lock(&wbuf->lock); + overlap = (lnum == wbuf->lnum && offs + len > wbuf->offs); + if (!overlap) { + /* We may safely unlock the write-buffer and read the data */ + spin_unlock(&wbuf->lock); + return ubifs_read_node(c, buf, type, len, lnum, offs); + } + + /* Don't read under wbuf */ + rlen = wbuf->offs - offs; + if (rlen < 0) + rlen = 0; + + /* Copy the rest from the write-buffer */ + memcpy(buf + rlen, wbuf->buf + offs + rlen - wbuf->offs, len - rlen); + spin_unlock(&wbuf->lock); + + if (rlen > 0) { + /* Read everything that goes before write-buffer */ + err = ubifs_leb_read(c, lnum, buf, offs, rlen, 0); + if (err && err != -EBADMSG) + return err; + } + + if (type != ch->node_type) { + ubifs_err("bad node type (%d but expected %d)", + ch->node_type, type); + goto out; + } + + err = ubifs_check_node(c, buf, lnum, offs, 0, 0); + if (err) { + ubifs_err("expected node type %d", type); + return err; + } + + rlen = le32_to_cpu(ch->len); + if (rlen != len) { + ubifs_err("bad node length %d, expected %d", rlen, len); + goto out; + } + + return 0; + +out: + ubifs_err("bad node at LEB %d:%d", lnum, offs); + ubifs_dump_node(c, buf); + dump_stack(); + return -EINVAL; +} + +/** * ubifs_read_node - read node. * @c: UBIFS file-system description object * @buf: buffer to read to @@ -281,12 +984,9 @@ int ubifs_read_node(const struct ubifs_info *c, void *buf, int type, int len, ubifs_assert(!(offs & 7) && offs < c->leb_size); ubifs_assert(type >= 0 && type < UBIFS_NODE_TYPES_CNT); - err = ubi_read(c->ubi, lnum, buf, offs, len); - if (err && err != -EBADMSG) { - ubifs_err("cannot read node %d from LEB %d:%d, error %d", - type, lnum, offs, err); + err = ubifs_leb_read(c, lnum, buf, offs, len, 0); + if (err && err != -EBADMSG) return err; - } if (type != ch->node_type) { ubifs_err("bad node type (%d but expected %d)", @@ -309,8 +1009,143 @@ int ubifs_read_node(const struct ubifs_info *c, void *buf, int type, int len, return 0; out: - ubifs_err("bad node at LEB %d:%d", lnum, offs); - dbg_dump_node(c, buf); - dbg_dump_stack(); + ubifs_err("bad node at LEB %d:%d, LEB mapping status %d", lnum, offs, + ubi_is_mapped(c->ubi, lnum)); + ubifs_dump_node(c, buf); + dump_stack(); return -EINVAL; } + +/** + * ubifs_wbuf_init - initialize write-buffer. + * @c: UBIFS file-system description object + * @wbuf: write-buffer to initialize + * + * This function initializes write-buffer. Returns zero in case of success + * %-ENOMEM in case of failure. + */ +int ubifs_wbuf_init(struct ubifs_info *c, struct ubifs_wbuf *wbuf) +{ + size_t size; + + wbuf->buf = kmalloc(c->max_write_size, GFP_KERNEL); + if (!wbuf->buf) + return -ENOMEM; + + size = (c->max_write_size / UBIFS_CH_SZ + 1) * sizeof(ino_t); + wbuf->inodes = kmalloc(size, GFP_KERNEL); + if (!wbuf->inodes) { + kfree(wbuf->buf); + wbuf->buf = NULL; + return -ENOMEM; + } + + wbuf->used = 0; + wbuf->lnum = wbuf->offs = -1; + /* + * If the LEB starts at the max. write size aligned address, then + * write-buffer size has to be set to @c->max_write_size. Otherwise, + * set it to something smaller so that it ends at the closest max. + * write size boundary. + */ + size = c->max_write_size - (c->leb_start % c->max_write_size); + wbuf->avail = wbuf->size = size; + wbuf->sync_callback = NULL; + mutex_init(&wbuf->io_mutex); + spin_lock_init(&wbuf->lock); + wbuf->c = c; + wbuf->next_ino = 0; + +#ifndef __UBOOT__ + hrtimer_init(&wbuf->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + wbuf->timer.function = wbuf_timer_callback_nolock; + wbuf->softlimit = ktime_set(WBUF_TIMEOUT_SOFTLIMIT, 0); + wbuf->delta = WBUF_TIMEOUT_HARDLIMIT - WBUF_TIMEOUT_SOFTLIMIT; + wbuf->delta *= 1000000000ULL; + ubifs_assert(wbuf->delta <= ULONG_MAX); +#endif + return 0; +} + +/** + * ubifs_wbuf_add_ino_nolock - add an inode number into the wbuf inode array. + * @wbuf: the write-buffer where to add + * @inum: the inode number + * + * This function adds an inode number to the inode array of the write-buffer. + */ +void ubifs_wbuf_add_ino_nolock(struct ubifs_wbuf *wbuf, ino_t inum) +{ + if (!wbuf->buf) + /* NOR flash or something similar */ + return; + + spin_lock(&wbuf->lock); + if (wbuf->used) + wbuf->inodes[wbuf->next_ino++] = inum; + spin_unlock(&wbuf->lock); +} + +/** + * wbuf_has_ino - returns if the wbuf contains data from the inode. + * @wbuf: the write-buffer + * @inum: the inode number + * + * This function returns with %1 if the write-buffer contains some data from the + * given inode otherwise it returns with %0. + */ +static int wbuf_has_ino(struct ubifs_wbuf *wbuf, ino_t inum) +{ + int i, ret = 0; + + spin_lock(&wbuf->lock); + for (i = 0; i < wbuf->next_ino; i++) + if (inum == wbuf->inodes[i]) { + ret = 1; + break; + } + spin_unlock(&wbuf->lock); + + return ret; +} + +/** + * ubifs_sync_wbufs_by_inode - synchronize write-buffers for an inode. + * @c: UBIFS file-system description object + * @inode: inode to synchronize + * + * This function synchronizes write-buffers which contain nodes belonging to + * @inode. Returns zero in case of success and a negative error code in case of + * failure. + */ +int ubifs_sync_wbufs_by_inode(struct ubifs_info *c, struct inode *inode) +{ + int i, err = 0; + + for (i = 0; i < c->jhead_cnt; i++) { + struct ubifs_wbuf *wbuf = &c->jheads[i].wbuf; + + if (i == GCHD) + /* + * GC head is special, do not look at it. Even if the + * head contains something related to this inode, it is + * a _copy_ of corresponding on-flash node which sits + * somewhere else. + */ + continue; + + if (!wbuf_has_ino(wbuf, inode->i_ino)) + continue; + + mutex_lock_nested(&wbuf->io_mutex, wbuf->jhead); + if (wbuf_has_ino(wbuf, inode->i_ino)) + err = ubifs_wbuf_sync_nolock(wbuf); + mutex_unlock(&wbuf->io_mutex); + + if (err) { + ubifs_ro_mode(c, err); + return err; + } + } + return 0; +} diff --git a/fs/ubifs/key.h b/fs/ubifs/key.h index efb3430..b5c4884 100644 --- a/fs/ubifs/key.h +++ b/fs/ubifs/key.h @@ -3,18 +3,7 @@ * * Copyright (C) 2006-2008 Nokia Corporation. * - * 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. - * - * 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., 51 - * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * SPDX-License-Identifier: GPL-2.0+ * * Authors: Artem Bityutskiy (Битюцкий Артём) * Adrian Hunter @@ -229,23 +218,6 @@ static inline void xent_key_init(const struct ubifs_info *c, } /** - * xent_key_init_hash - initialize extended attribute entry key without - * re-calculating hash function. - * @c: UBIFS file-system description object - * @key: key to initialize - * @inum: host inode number - * @hash: extended attribute entry name hash - */ -static inline void xent_key_init_hash(const struct ubifs_info *c, - union ubifs_key *key, ino_t inum, - uint32_t hash) -{ - ubifs_assert(!(hash & ~UBIFS_S_KEY_HASH_MASK)); - key->u32[0] = inum; - key->u32[1] = hash | (UBIFS_XENT_KEY << UBIFS_S_KEY_HASH_BITS); -} - -/** * xent_key_init_flash - initialize on-flash extended attribute entry key. * @c: UBIFS file-system description object * @k: key to initialize @@ -295,22 +267,15 @@ static inline void data_key_init(const struct ubifs_info *c, } /** - * data_key_init_flash - initialize on-flash data key. + * highest_data_key - get the highest possible data key for an inode. * @c: UBIFS file-system description object - * @k: key to initialize + * @key: key to initialize * @inum: inode number - * @block: block number */ -static inline void data_key_init_flash(const struct ubifs_info *c, void *k, - ino_t inum, unsigned int block) +static inline void highest_data_key(const struct ubifs_info *c, + union ubifs_key *key, ino_t inum) { - union ubifs_key *key = k; - - ubifs_assert(!(block & ~UBIFS_S_KEY_BLOCK_MASK)); - key->j32[0] = cpu_to_le32(inum); - key->j32[1] = cpu_to_le32(block | - (UBIFS_DATA_KEY << UBIFS_S_KEY_BLOCK_BITS)); - memset(k + 8, 0, UBIFS_MAX_KEY_LEN - 8); + data_key_init(c, key, inum, UBIFS_S_KEY_BLOCK_MASK); } /** @@ -330,6 +295,20 @@ static inline void trun_key_init(const struct ubifs_info *c, } /** + * invalid_key_init - initialize invalid node key. + * @c: UBIFS file-system description object + * @key: key to initialize + * + * This is a helper function which marks a @key object as invalid. + */ +static inline void invalid_key_init(const struct ubifs_info *c, + union ubifs_key *key) +{ + key->u32[0] = 0xDEADBEAF; + key->u32[1] = UBIFS_INVALID_KEY; +} + +/** * key_type - get key type. * @c: UBIFS file-system description object * @key: key to get type of @@ -381,8 +360,8 @@ static inline ino_t key_inum_flash(const struct ubifs_info *c, const void *k) * @c: UBIFS file-system description object * @key: the key to get hash from */ -static inline int key_hash(const struct ubifs_info *c, - const union ubifs_key *key) +static inline uint32_t key_hash(const struct ubifs_info *c, + const union ubifs_key *key) { return key->u32[1] & UBIFS_S_KEY_HASH_MASK; } @@ -392,7 +371,7 @@ static inline int key_hash(const struct ubifs_info *c, * @c: UBIFS file-system description object * @k: the key to get hash from */ -static inline int key_hash_flash(const struct ubifs_info *c, const void *k) +static inline uint32_t key_hash_flash(const struct ubifs_info *c, const void *k) { const union ubifs_key *key = k; @@ -554,4 +533,5 @@ static inline unsigned long long key_max_inode_size(const struct ubifs_info *c) return 0; } } + #endif /* !__UBIFS_KEY_H__ */ diff --git a/fs/ubifs/log.c b/fs/ubifs/log.c index 68a9bd9..ced0424 100644 --- a/fs/ubifs/log.c +++ b/fs/ubifs/log.c @@ -3,18 +3,7 @@ * * Copyright (C) 2006-2008 Nokia Corporation. * - * 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. - * - * 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., 51 - * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * SPDX-License-Identifier: GPL-2.0+ * * Authors: Artem Bityutskiy (Битюцкий Артём) * Adrian Hunter @@ -27,8 +16,14 @@ * journal. */ +#define __UBOOT__ +#ifdef __UBOOT__ +#include <linux/err.h> +#endif #include "ubifs.h" +static int dbg_check_bud_bytes(struct ubifs_info *c); + /** * ubifs_search_bud - search bud LEB. * @c: UBIFS file-system description object @@ -60,6 +55,57 @@ struct ubifs_bud *ubifs_search_bud(struct ubifs_info *c, int lnum) } /** + * ubifs_get_wbuf - get the wbuf associated with a LEB, if there is one. + * @c: UBIFS file-system description object + * @lnum: logical eraseblock number to search + * + * This functions returns the wbuf for @lnum or %NULL if there is not one. + */ +struct ubifs_wbuf *ubifs_get_wbuf(struct ubifs_info *c, int lnum) +{ + struct rb_node *p; + struct ubifs_bud *bud; + int jhead; + + if (!c->jheads) + return NULL; + + spin_lock(&c->buds_lock); + p = c->buds.rb_node; + while (p) { + bud = rb_entry(p, struct ubifs_bud, rb); + if (lnum < bud->lnum) + p = p->rb_left; + else if (lnum > bud->lnum) + p = p->rb_right; + else { + jhead = bud->jhead; + spin_unlock(&c->buds_lock); + return &c->jheads[jhead].wbuf; + } + } + spin_unlock(&c->buds_lock); + return NULL; +} + +/** + * empty_log_bytes - calculate amount of empty space in the log. + * @c: UBIFS file-system description object + */ +static inline long long empty_log_bytes(const struct ubifs_info *c) +{ + long long h, t; + + h = (long long)c->lhead_lnum * c->leb_size + c->lhead_offs; + t = (long long)c->ltail_lnum * c->leb_size; + + if (h >= t) + return c->log_bytes - h + t; + else + return t - h; +} + +/** * ubifs_add_bud - add bud LEB to the tree of buds and its journal head list. * @c: UBIFS file-system description object * @bud: the bud to add @@ -88,7 +134,7 @@ void ubifs_add_bud(struct ubifs_info *c, struct ubifs_bud *bud) jhead = &c->jheads[bud->jhead]; list_add_tail(&bud->list, &jhead->buds_list); } else - ubifs_assert(c->replaying && (c->vfs_sb->s_flags & MS_RDONLY)); + ubifs_assert(c->replaying && c->ro_mount); /* * Note, although this is a new bud, we anyway account this space now, @@ -98,7 +144,594 @@ void ubifs_add_bud(struct ubifs_info *c, struct ubifs_bud *bud) */ c->bud_bytes += c->leb_size - bud->start; - dbg_log("LEB %d:%d, jhead %d, bud_bytes %lld", bud->lnum, - bud->start, bud->jhead, c->bud_bytes); + dbg_log("LEB %d:%d, jhead %s, bud_bytes %lld", bud->lnum, + bud->start, dbg_jhead(bud->jhead), c->bud_bytes); + spin_unlock(&c->buds_lock); +} + +/** + * ubifs_add_bud_to_log - add a new bud to the log. + * @c: UBIFS file-system description object + * @jhead: journal head the bud belongs to + * @lnum: LEB number of the bud + * @offs: starting offset of the bud + * + * This function writes reference node for the new bud LEB @lnum it to the log, + * and adds it to the buds tress. It also makes sure that log size does not + * exceed the 'c->max_bud_bytes' limit. Returns zero in case of success, + * %-EAGAIN if commit is required, and a negative error codes in case of + * failure. + */ +int ubifs_add_bud_to_log(struct ubifs_info *c, int jhead, int lnum, int offs) +{ + int err; + struct ubifs_bud *bud; + struct ubifs_ref_node *ref; + + bud = kmalloc(sizeof(struct ubifs_bud), GFP_NOFS); + if (!bud) + return -ENOMEM; + ref = kzalloc(c->ref_node_alsz, GFP_NOFS); + if (!ref) { + kfree(bud); + return -ENOMEM; + } + + mutex_lock(&c->log_mutex); + ubifs_assert(!c->ro_media && !c->ro_mount); + if (c->ro_error) { + err = -EROFS; + goto out_unlock; + } + + /* Make sure we have enough space in the log */ + if (empty_log_bytes(c) - c->ref_node_alsz < c->min_log_bytes) { + dbg_log("not enough log space - %lld, required %d", + empty_log_bytes(c), c->min_log_bytes); + ubifs_commit_required(c); + err = -EAGAIN; + goto out_unlock; + } + + /* + * Make sure the amount of space in buds will not exceed the + * 'c->max_bud_bytes' limit, because we want to guarantee mount time + * limits. + * + * It is not necessary to hold @c->buds_lock when reading @c->bud_bytes + * because we are holding @c->log_mutex. All @c->bud_bytes take place + * when both @c->log_mutex and @c->bud_bytes are locked. + */ + if (c->bud_bytes + c->leb_size - offs > c->max_bud_bytes) { + dbg_log("bud bytes %lld (%lld max), require commit", + c->bud_bytes, c->max_bud_bytes); + ubifs_commit_required(c); + err = -EAGAIN; + goto out_unlock; + } + + /* + * If the journal is full enough - start background commit. Note, it is + * OK to read 'c->cmt_state' without spinlock because integer reads + * are atomic in the kernel. + */ + if (c->bud_bytes >= c->bg_bud_bytes && + c->cmt_state == COMMIT_RESTING) { + dbg_log("bud bytes %lld (%lld max), initiate BG commit", + c->bud_bytes, c->max_bud_bytes); + ubifs_request_bg_commit(c); + } + + bud->lnum = lnum; + bud->start = offs; + bud->jhead = jhead; + + ref->ch.node_type = UBIFS_REF_NODE; + ref->lnum = cpu_to_le32(bud->lnum); + ref->offs = cpu_to_le32(bud->start); + ref->jhead = cpu_to_le32(jhead); + + if (c->lhead_offs > c->leb_size - c->ref_node_alsz) { + c->lhead_lnum = ubifs_next_log_lnum(c, c->lhead_lnum); + c->lhead_offs = 0; + } + + if (c->lhead_offs == 0) { + /* Must ensure next log LEB has been unmapped */ + err = ubifs_leb_unmap(c, c->lhead_lnum); + if (err) + goto out_unlock; + } + + if (bud->start == 0) { + /* + * Before writing the LEB reference which refers an empty LEB + * to the log, we have to make sure it is mapped, because + * otherwise we'd risk to refer an LEB with garbage in case of + * an unclean reboot, because the target LEB might have been + * unmapped, but not yet physically erased. + */ + err = ubifs_leb_map(c, bud->lnum); + if (err) + goto out_unlock; + } + + dbg_log("write ref LEB %d:%d", + c->lhead_lnum, c->lhead_offs); + err = ubifs_write_node(c, ref, UBIFS_REF_NODE_SZ, c->lhead_lnum, + c->lhead_offs); + if (err) + goto out_unlock; + + c->lhead_offs += c->ref_node_alsz; + + ubifs_add_bud(c, bud); + + mutex_unlock(&c->log_mutex); + kfree(ref); + return 0; + +out_unlock: + mutex_unlock(&c->log_mutex); + kfree(ref); + kfree(bud); + return err; +} + +/** + * remove_buds - remove used buds. + * @c: UBIFS file-system description object + * + * This function removes use buds from the buds tree. It does not remove the + * buds which are pointed to by journal heads. + */ +static void remove_buds(struct ubifs_info *c) +{ + struct rb_node *p; + + ubifs_assert(list_empty(&c->old_buds)); + c->cmt_bud_bytes = 0; + spin_lock(&c->buds_lock); + p = rb_first(&c->buds); + while (p) { + struct rb_node *p1 = p; + struct ubifs_bud *bud; + struct ubifs_wbuf *wbuf; + + p = rb_next(p); + bud = rb_entry(p1, struct ubifs_bud, rb); + wbuf = &c->jheads[bud->jhead].wbuf; + + if (wbuf->lnum == bud->lnum) { + /* + * Do not remove buds which are pointed to by journal + * heads (non-closed buds). + */ + c->cmt_bud_bytes += wbuf->offs - bud->start; + dbg_log("preserve %d:%d, jhead %s, bud bytes %d, cmt_bud_bytes %lld", + bud->lnum, bud->start, dbg_jhead(bud->jhead), + wbuf->offs - bud->start, c->cmt_bud_bytes); + bud->start = wbuf->offs; + } else { + c->cmt_bud_bytes += c->leb_size - bud->start; + dbg_log("remove %d:%d, jhead %s, bud bytes %d, cmt_bud_bytes %lld", + bud->lnum, bud->start, dbg_jhead(bud->jhead), + c->leb_size - bud->start, c->cmt_bud_bytes); + rb_erase(p1, &c->buds); + /* + * If the commit does not finish, the recovery will need + * to replay the journal, in which case the old buds + * must be unchanged. Do not release them until post + * commit i.e. do not allow them to be garbage + * collected. + */ + list_move(&bud->list, &c->old_buds); + } + } + spin_unlock(&c->buds_lock); +} + +/** + * ubifs_log_start_commit - start commit. + * @c: UBIFS file-system description object + * @ltail_lnum: return new log tail LEB number + * + * The commit operation starts with writing "commit start" node to the log and + * reference nodes for all journal heads which will define new journal after + * the commit has been finished. The commit start and reference nodes are + * written in one go to the nearest empty log LEB (hence, when commit is + * finished UBIFS may safely unmap all the previous log LEBs). This function + * returns zero in case of success and a negative error code in case of + * failure. + */ +int ubifs_log_start_commit(struct ubifs_info *c, int *ltail_lnum) +{ + void *buf; + struct ubifs_cs_node *cs; + struct ubifs_ref_node *ref; + int err, i, max_len, len; + + err = dbg_check_bud_bytes(c); + if (err) + return err; + + max_len = UBIFS_CS_NODE_SZ + c->jhead_cnt * UBIFS_REF_NODE_SZ; + max_len = ALIGN(max_len, c->min_io_size); + buf = cs = kmalloc(max_len, GFP_NOFS); + if (!buf) + return -ENOMEM; + + cs->ch.node_type = UBIFS_CS_NODE; + cs->cmt_no = cpu_to_le64(c->cmt_no); + ubifs_prepare_node(c, cs, UBIFS_CS_NODE_SZ, 0); + + /* + * Note, we do not lock 'c->log_mutex' because this is the commit start + * phase and we are exclusively using the log. And we do not lock + * write-buffer because nobody can write to the file-system at this + * phase. + */ + + len = UBIFS_CS_NODE_SZ; + for (i = 0; i < c->jhead_cnt; i++) { + int lnum = c->jheads[i].wbuf.lnum; + int offs = c->jheads[i].wbuf.offs; + + if (lnum == -1 || offs == c->leb_size) + continue; + + dbg_log("add ref to LEB %d:%d for jhead %s", + lnum, offs, dbg_jhead(i)); + ref = buf + len; + ref->ch.node_type = UBIFS_REF_NODE; + ref->lnum = cpu_to_le32(lnum); + ref->offs = cpu_to_le32(offs); + ref->jhead = cpu_to_le32(i); + + ubifs_prepare_node(c, ref, UBIFS_REF_NODE_SZ, 0); + len += UBIFS_REF_NODE_SZ; + } + + ubifs_pad(c, buf + len, ALIGN(len, c->min_io_size) - len); + + /* Switch to the next log LEB */ + if (c->lhead_offs) { + c->lhead_lnum = ubifs_next_log_lnum(c, c->lhead_lnum); + c->lhead_offs = 0; + } + + if (c->lhead_offs == 0) { + /* Must ensure next LEB has been unmapped */ + err = ubifs_leb_unmap(c, c->lhead_lnum); + if (err) + goto out; + } + + len = ALIGN(len, c->min_io_size); + dbg_log("writing commit start at LEB %d:0, len %d", c->lhead_lnum, len); + err = ubifs_leb_write(c, c->lhead_lnum, cs, 0, len); + if (err) + goto out; + + *ltail_lnum = c->lhead_lnum; + + c->lhead_offs += len; + if (c->lhead_offs == c->leb_size) { + c->lhead_lnum = ubifs_next_log_lnum(c, c->lhead_lnum); + c->lhead_offs = 0; + } + + remove_buds(c); + + /* + * We have started the commit and now users may use the rest of the log + * for new writes. + */ + c->min_log_bytes = 0; + +out: + kfree(buf); + return err; +} + +/** + * ubifs_log_end_commit - end commit. + * @c: UBIFS file-system description object + * @ltail_lnum: new log tail LEB number + * + * This function is called on when the commit operation was finished. It + * moves log tail to new position and unmaps LEBs which contain obsolete data. + * Returns zero in case of success and a negative error code in case of + * failure. + */ +int ubifs_log_end_commit(struct ubifs_info *c, int ltail_lnum) +{ + int err; + + /* + * At this phase we have to lock 'c->log_mutex' because UBIFS allows FS + * writes during commit. Its only short "commit" start phase when + * writers are blocked. + */ + mutex_lock(&c->log_mutex); + + dbg_log("old tail was LEB %d:0, new tail is LEB %d:0", + c->ltail_lnum, ltail_lnum); + + c->ltail_lnum = ltail_lnum; + /* + * The commit is finished and from now on it must be guaranteed that + * there is always enough space for the next commit. + */ + c->min_log_bytes = c->leb_size; + + spin_lock(&c->buds_lock); + c->bud_bytes -= c->cmt_bud_bytes; + spin_unlock(&c->buds_lock); + + err = dbg_check_bud_bytes(c); + + mutex_unlock(&c->log_mutex); + return err; +} + +/** + * ubifs_log_post_commit - things to do after commit is completed. + * @c: UBIFS file-system description object + * @old_ltail_lnum: old log tail LEB number + * + * Release buds only after commit is completed, because they must be unchanged + * if recovery is needed. + * + * Unmap log LEBs only after commit is completed, because they may be needed for + * recovery. + * + * This function returns %0 on success and a negative error code on failure. + */ +int ubifs_log_post_commit(struct ubifs_info *c, int old_ltail_lnum) +{ + int lnum, err = 0; + + while (!list_empty(&c->old_buds)) { + struct ubifs_bud *bud; + + bud = list_entry(c->old_buds.next, struct ubifs_bud, list); + err = ubifs_return_leb(c, bud->lnum); + if (err) + return err; + list_del(&bud->list); + kfree(bud); + } + mutex_lock(&c->log_mutex); + for (lnum = old_ltail_lnum; lnum != c->ltail_lnum; + lnum = ubifs_next_log_lnum(c, lnum)) { + dbg_log("unmap log LEB %d", lnum); + err = ubifs_leb_unmap(c, lnum); + if (err) + goto out; + } +out: + mutex_unlock(&c->log_mutex); + return err; +} + +/** + * struct done_ref - references that have been done. + * @rb: rb-tree node + * @lnum: LEB number + */ +struct done_ref { + struct rb_node rb; + int lnum; +}; + +/** + * done_already - determine if a reference has been done already. + * @done_tree: rb-tree to store references that have been done + * @lnum: LEB number of reference + * + * This function returns %1 if the reference has been done, %0 if not, otherwise + * a negative error code is returned. + */ +static int done_already(struct rb_root *done_tree, int lnum) +{ + struct rb_node **p = &done_tree->rb_node, *parent = NULL; + struct done_ref *dr; + + while (*p) { + parent = *p; + dr = rb_entry(parent, struct done_ref, rb); + if (lnum < dr->lnum) + p = &(*p)->rb_left; + else if (lnum > dr->lnum) + p = &(*p)->rb_right; + else + return 1; + } + + dr = kzalloc(sizeof(struct done_ref), GFP_NOFS); + if (!dr) + return -ENOMEM; + + dr->lnum = lnum; + + rb_link_node(&dr->rb, parent, p); + rb_insert_color(&dr->rb, done_tree); + + return 0; +} + +/** + * destroy_done_tree - destroy the done tree. + * @done_tree: done tree to destroy + */ +static void destroy_done_tree(struct rb_root *done_tree) +{ + struct done_ref *dr, *n; + + rbtree_postorder_for_each_entry_safe(dr, n, done_tree, rb) + kfree(dr); +} + +/** + * add_node - add a node to the consolidated log. + * @c: UBIFS file-system description object + * @buf: buffer to which to add + * @lnum: LEB number to which to write is passed and returned here + * @offs: offset to where to write is passed and returned here + * @node: node to add + * + * This function returns %0 on success and a negative error code on failure. + */ +static int add_node(struct ubifs_info *c, void *buf, int *lnum, int *offs, + void *node) +{ + struct ubifs_ch *ch = node; + int len = le32_to_cpu(ch->len), remains = c->leb_size - *offs; + + if (len > remains) { + int sz = ALIGN(*offs, c->min_io_size), err; + + ubifs_pad(c, buf + *offs, sz - *offs); + err = ubifs_leb_change(c, *lnum, buf, sz); + if (err) + return err; + *lnum = ubifs_next_log_lnum(c, *lnum); + *offs = 0; + } + memcpy(buf + *offs, node, len); + *offs += ALIGN(len, 8); + return 0; +} + +/** + * ubifs_consolidate_log - consolidate the log. + * @c: UBIFS file-system description object + * + * Repeated failed commits could cause the log to be full, but at least 1 LEB is + * needed for commit. This function rewrites the reference nodes in the log + * omitting duplicates, and failed CS nodes, and leaving no gaps. + * + * This function returns %0 on success and a negative error code on failure. + */ +int ubifs_consolidate_log(struct ubifs_info *c) +{ + struct ubifs_scan_leb *sleb; + struct ubifs_scan_node *snod; + struct rb_root done_tree = RB_ROOT; + int lnum, err, first = 1, write_lnum, offs = 0; + void *buf; + + dbg_rcvry("log tail LEB %d, log head LEB %d", c->ltail_lnum, + c->lhead_lnum); + buf = vmalloc(c->leb_size); + if (!buf) + return -ENOMEM; + lnum = c->ltail_lnum; + write_lnum = lnum; + while (1) { + sleb = ubifs_scan(c, lnum, 0, c->sbuf, 0); + if (IS_ERR(sleb)) { + err = PTR_ERR(sleb); + goto out_free; + } + list_for_each_entry(snod, &sleb->nodes, list) { + switch (snod->type) { + case UBIFS_REF_NODE: { + struct ubifs_ref_node *ref = snod->node; + int ref_lnum = le32_to_cpu(ref->lnum); + + err = done_already(&done_tree, ref_lnum); + if (err < 0) + goto out_scan; + if (err != 1) { + err = add_node(c, buf, &write_lnum, + &offs, snod->node); + if (err) + goto out_scan; + } + break; + } + case UBIFS_CS_NODE: + if (!first) + break; + err = add_node(c, buf, &write_lnum, &offs, + snod->node); + if (err) + goto out_scan; + first = 0; + break; + } + } + ubifs_scan_destroy(sleb); + if (lnum == c->lhead_lnum) + break; + lnum = ubifs_next_log_lnum(c, lnum); + } + if (offs) { + int sz = ALIGN(offs, c->min_io_size); + + ubifs_pad(c, buf + offs, sz - offs); + err = ubifs_leb_change(c, write_lnum, buf, sz); + if (err) + goto out_free; + offs = ALIGN(offs, c->min_io_size); + } + destroy_done_tree(&done_tree); + vfree(buf); + if (write_lnum == c->lhead_lnum) { + ubifs_err("log is too full"); + return -EINVAL; + } + /* Unmap remaining LEBs */ + lnum = write_lnum; + do { + lnum = ubifs_next_log_lnum(c, lnum); + err = ubifs_leb_unmap(c, lnum); + if (err) + return err; + } while (lnum != c->lhead_lnum); + c->lhead_lnum = write_lnum; + c->lhead_offs = offs; + dbg_rcvry("new log head at %d:%d", c->lhead_lnum, c->lhead_offs); + return 0; + +out_scan: + ubifs_scan_destroy(sleb); +out_free: + destroy_done_tree(&done_tree); + vfree(buf); + return err; +} + +/** + * dbg_check_bud_bytes - make sure bud bytes calculation are all right. + * @c: UBIFS file-system description object + * + * This function makes sure the amount of flash space used by closed buds + * ('c->bud_bytes' is correct). Returns zero in case of success and %-EINVAL in + * case of failure. + */ +static int dbg_check_bud_bytes(struct ubifs_info *c) +{ + int i, err = 0; + struct ubifs_bud *bud; + long long bud_bytes = 0; + + if (!dbg_is_chk_gen(c)) + return 0; + + spin_lock(&c->buds_lock); + for (i = 0; i < c->jhead_cnt; i++) + list_for_each_entry(bud, &c->jheads[i].buds_list, list) + bud_bytes += c->leb_size - bud->start; + + if (c->bud_bytes != bud_bytes) { + ubifs_err("bad bud_bytes %lld, calculated %lld", + c->bud_bytes, bud_bytes); + err = -EINVAL; + } spin_unlock(&c->buds_lock); + + return err; } diff --git a/fs/ubifs/lprops.c b/fs/ubifs/lprops.c index 8ce4949..fc6686b 100644 --- a/fs/ubifs/lprops.c +++ b/fs/ubifs/lprops.c @@ -3,18 +3,7 @@ * * Copyright (C) 2006-2008 Nokia Corporation. * - * 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. - * - * 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., 51 - * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * SPDX-License-Identifier: GPL-2.0+ * * Authors: Adrian Hunter * Artem Bityutskiy (Битюцкий Артём) @@ -28,6 +17,10 @@ * an empty LEB for the journal, or a very dirty LEB for garbage collection. */ +#define __UBOOT__ +#ifdef __UBOOT__ +#include <linux/err.h> +#endif #include "ubifs.h" /** @@ -281,7 +274,7 @@ void ubifs_add_to_cat(struct ubifs_info *c, struct ubifs_lprops *lprops, case LPROPS_FREE: if (add_to_lpt_heap(c, lprops, cat)) break; - /* No more room on heap so make it uncategorized */ + /* No more room on heap so make it un-categorized */ cat = LPROPS_UNCAT; /* Fall through */ case LPROPS_UNCAT: @@ -300,8 +293,11 @@ void ubifs_add_to_cat(struct ubifs_info *c, struct ubifs_lprops *lprops, default: ubifs_assert(0); } + lprops->flags &= ~LPROPS_CAT_MASK; lprops->flags |= cat; + c->in_a_category_cnt += 1; + ubifs_assert(c->in_a_category_cnt <= c->main_lebs); } /** @@ -334,6 +330,9 @@ static void ubifs_remove_from_cat(struct ubifs_info *c, default: ubifs_assert(0); } + + c->in_a_category_cnt -= 1; + ubifs_assert(c->in_a_category_cnt >= 0); } /** @@ -375,8 +374,8 @@ void ubifs_replace_cat(struct ubifs_info *c, struct ubifs_lprops *old_lprops, * @lprops: LEB properties * * A LEB may have fallen off of the bottom of a heap, and ended up as - * uncategorized even though it has enough space for us now. If that is the case - * this function will put the LEB back onto a heap. + * un-categorized even though it has enough space for us now. If that is the + * case this function will put the LEB back onto a heap. */ void ubifs_ensure_cat(struct ubifs_info *c, struct ubifs_lprops *lprops) { @@ -436,10 +435,10 @@ int ubifs_categorize_lprops(const struct ubifs_info *c, /** * change_category - change LEB properties category. * @c: UBIFS file-system description object - * @lprops: LEB properties to recategorize + * @lprops: LEB properties to re-categorize * * LEB properties are categorized to enable fast find operations. When the LEB - * properties change they must be recategorized. + * properties change they must be re-categorized. */ static void change_category(struct ubifs_info *c, struct ubifs_lprops *lprops) { @@ -447,7 +446,7 @@ static void change_category(struct ubifs_info *c, struct ubifs_lprops *lprops) int new_cat = ubifs_categorize_lprops(c, lprops); if (old_cat == new_cat) { - struct ubifs_lpt_heap *heap = &c->lpt_heap[new_cat - 1]; + struct ubifs_lpt_heap *heap; /* lprops on a heap now must be moved up or down */ if (new_cat < 1 || new_cat > LPROPS_HEAP_CNT) @@ -461,21 +460,18 @@ static void change_category(struct ubifs_info *c, struct ubifs_lprops *lprops) } /** - * calc_dark - calculate LEB dark space size. + * ubifs_calc_dark - calculate LEB dark space size. * @c: the UBIFS file-system description object * @spc: amount of free and dirty space in the LEB * - * This function calculates amount of dark space in an LEB which has @spc bytes - * of free and dirty space. Returns the calculations result. + * This function calculates and returns amount of dark space in an LEB which + * has @spc bytes of free and dirty space. * - * Dark space is the space which is not always usable - it depends on which - * nodes are written in which order. E.g., if an LEB has only 512 free bytes, - * it is dark space, because it cannot fit a large data node. So UBIFS cannot - * count on this LEB and treat these 512 bytes as usable because it is not true - * if, for example, only big chunks of uncompressible data will be written to - * the FS. + * UBIFS is trying to account the space which might not be usable, and this + * space is called "dark space". For example, if an LEB has only %512 free + * bytes, it is dark space, because it cannot fit a large data node. */ -static int calc_dark(struct ubifs_info *c, int spc) +int ubifs_calc_dark(const struct ubifs_info *c, int spc) { ubifs_assert(!(spc & 7)); @@ -507,7 +503,7 @@ static int is_lprops_dirty(struct ubifs_info *c, struct ubifs_lprops *lprops) pnode = (struct ubifs_pnode *)container_of(lprops - pos, struct ubifs_pnode, lprops[0]); - return !test_bit(COW_ZNODE, &pnode->flags) && + return !test_bit(COW_CNODE, &pnode->flags) && test_bit(DIRTY_CNODE, &pnode->flags); } @@ -518,7 +514,7 @@ static int is_lprops_dirty(struct ubifs_info *c, struct ubifs_lprops *lprops) * @free: new free space amount * @dirty: new dirty space amount * @flags: new flags - * @idx_gc_cnt: change to the count of idx_gc list + * @idx_gc_cnt: change to the count of @idx_gc list * * This function changes LEB properties (@free, @dirty or @flag). However, the * property which has the %LPROPS_NC value is not changed. Returns a pointer to @@ -535,7 +531,7 @@ const struct ubifs_lprops *ubifs_change_lp(struct ubifs_info *c, { /* * This is the only function that is allowed to change lprops, so we - * discard the const qualifier. + * discard the "const" qualifier. */ struct ubifs_lprops *lprops = (struct ubifs_lprops *)lp; @@ -575,7 +571,7 @@ const struct ubifs_lprops *ubifs_change_lp(struct ubifs_info *c, if (old_spc < c->dead_wm) c->lst.total_dead -= old_spc; else - c->lst.total_dark -= calc_dark(c, old_spc); + c->lst.total_dark -= ubifs_calc_dark(c, old_spc); c->lst.total_used -= c->leb_size - old_spc; } @@ -616,7 +612,7 @@ const struct ubifs_lprops *ubifs_change_lp(struct ubifs_info *c, if (new_spc < c->dead_wm) c->lst.total_dead += new_spc; else - c->lst.total_dark += calc_dark(c, new_spc); + c->lst.total_dark += ubifs_calc_dark(c, new_spc); c->lst.total_used += c->leb_size - new_spc; } @@ -678,6 +674,9 @@ int ubifs_change_one_lp(struct ubifs_info *c, int lnum, int free, int dirty, out: ubifs_release_lprops(c); + if (err) + ubifs_err("cannot change properties of LEB %d, error %d", + lnum, err); return err; } @@ -714,6 +713,9 @@ int ubifs_update_one_lp(struct ubifs_info *c, int lnum, int free, int dirty, out: ubifs_release_lprops(c); + if (err) + ubifs_err("cannot update properties of LEB %d, error %d", + lnum, err); return err; } @@ -737,6 +739,8 @@ int ubifs_read_one_lp(struct ubifs_info *c, int lnum, struct ubifs_lprops *lp) lpp = ubifs_lpt_lookup(c, lnum); if (IS_ERR(lpp)) { err = PTR_ERR(lpp); + ubifs_err("cannot read properties of LEB %d, error %d", + lnum, err); goto out; } @@ -840,3 +844,471 @@ const struct ubifs_lprops *ubifs_fast_find_frdi_idx(struct ubifs_info *c) ubifs_assert(lprops->free + lprops->dirty == c->leb_size); return lprops; } + +/* + * Everything below is related to debugging. + */ + +/** + * dbg_check_cats - check category heaps and lists. + * @c: UBIFS file-system description object + * + * This function returns %0 on success and a negative error code on failure. + */ +int dbg_check_cats(struct ubifs_info *c) +{ + struct ubifs_lprops *lprops; + struct list_head *pos; + int i, cat; + + if (!dbg_is_chk_gen(c) && !dbg_is_chk_lprops(c)) + return 0; + + list_for_each_entry(lprops, &c->empty_list, list) { + if (lprops->free != c->leb_size) { + ubifs_err("non-empty LEB %d on empty list (free %d dirty %d flags %d)", + lprops->lnum, lprops->free, lprops->dirty, + lprops->flags); + return -EINVAL; + } + if (lprops->flags & LPROPS_TAKEN) { + ubifs_err("taken LEB %d on empty list (free %d dirty %d flags %d)", + lprops->lnum, lprops->free, lprops->dirty, + lprops->flags); + return -EINVAL; + } + } + + i = 0; + list_for_each_entry(lprops, &c->freeable_list, list) { + if (lprops->free + lprops->dirty != c->leb_size) { + ubifs_err("non-freeable LEB %d on freeable list (free %d dirty %d flags %d)", + lprops->lnum, lprops->free, lprops->dirty, + lprops->flags); + return -EINVAL; + } + if (lprops->flags & LPROPS_TAKEN) { + ubifs_err("taken LEB %d on freeable list (free %d dirty %d flags %d)", + lprops->lnum, lprops->free, lprops->dirty, + lprops->flags); + return -EINVAL; + } + i += 1; + } + if (i != c->freeable_cnt) { + ubifs_err("freeable list count %d expected %d", i, + c->freeable_cnt); + return -EINVAL; + } + + i = 0; + list_for_each(pos, &c->idx_gc) + i += 1; + if (i != c->idx_gc_cnt) { + ubifs_err("idx_gc list count %d expected %d", i, + c->idx_gc_cnt); + return -EINVAL; + } + + list_for_each_entry(lprops, &c->frdi_idx_list, list) { + if (lprops->free + lprops->dirty != c->leb_size) { + ubifs_err("non-freeable LEB %d on frdi_idx list (free %d dirty %d flags %d)", + lprops->lnum, lprops->free, lprops->dirty, + lprops->flags); + return -EINVAL; + } + if (lprops->flags & LPROPS_TAKEN) { + ubifs_err("taken LEB %d on frdi_idx list (free %d dirty %d flags %d)", + lprops->lnum, lprops->free, lprops->dirty, + lprops->flags); + return -EINVAL; + } + if (!(lprops->flags & LPROPS_INDEX)) { + ubifs_err("non-index LEB %d on frdi_idx list (free %d dirty %d flags %d)", + lprops->lnum, lprops->free, lprops->dirty, + lprops->flags); + return -EINVAL; + } + } + + for (cat = 1; cat <= LPROPS_HEAP_CNT; cat++) { + struct ubifs_lpt_heap *heap = &c->lpt_heap[cat - 1]; + + for (i = 0; i < heap->cnt; i++) { + lprops = heap->arr[i]; + if (!lprops) { + ubifs_err("null ptr in LPT heap cat %d", cat); + return -EINVAL; + } + if (lprops->hpos != i) { + ubifs_err("bad ptr in LPT heap cat %d", cat); + return -EINVAL; + } + if (lprops->flags & LPROPS_TAKEN) { + ubifs_err("taken LEB in LPT heap cat %d", cat); + return -EINVAL; + } + } + } + + return 0; +} + +void dbg_check_heap(struct ubifs_info *c, struct ubifs_lpt_heap *heap, int cat, + int add_pos) +{ + int i = 0, j, err = 0; + + if (!dbg_is_chk_gen(c) && !dbg_is_chk_lprops(c)) + return; + + for (i = 0; i < heap->cnt; i++) { + struct ubifs_lprops *lprops = heap->arr[i]; + struct ubifs_lprops *lp; + + if (i != add_pos) + if ((lprops->flags & LPROPS_CAT_MASK) != cat) { + err = 1; + goto out; + } + if (lprops->hpos != i) { + err = 2; + goto out; + } + lp = ubifs_lpt_lookup(c, lprops->lnum); + if (IS_ERR(lp)) { + err = 3; + goto out; + } + if (lprops != lp) { + ubifs_err("lprops %zx lp %zx lprops->lnum %d lp->lnum %d", + (size_t)lprops, (size_t)lp, lprops->lnum, + lp->lnum); + err = 4; + goto out; + } + for (j = 0; j < i; j++) { + lp = heap->arr[j]; + if (lp == lprops) { + err = 5; + goto out; + } + if (lp->lnum == lprops->lnum) { + err = 6; + goto out; + } + } + } +out: + if (err) { + ubifs_err("failed cat %d hpos %d err %d", cat, i, err); + dump_stack(); + ubifs_dump_heap(c, heap, cat); + } +} + +/** + * scan_check_cb - scan callback. + * @c: the UBIFS file-system description object + * @lp: LEB properties to scan + * @in_tree: whether the LEB properties are in main memory + * @lst: lprops statistics to update + * + * This function returns a code that indicates whether the scan should continue + * (%LPT_SCAN_CONTINUE), whether the LEB properties should be added to the tree + * in main memory (%LPT_SCAN_ADD), or whether the scan should stop + * (%LPT_SCAN_STOP). + */ +static int scan_check_cb(struct ubifs_info *c, + const struct ubifs_lprops *lp, int in_tree, + struct ubifs_lp_stats *lst) +{ + struct ubifs_scan_leb *sleb; + struct ubifs_scan_node *snod; + int cat, lnum = lp->lnum, is_idx = 0, used = 0, freef, dirty, ret; + void *buf = NULL; + + cat = lp->flags & LPROPS_CAT_MASK; + if (cat != LPROPS_UNCAT) { + cat = ubifs_categorize_lprops(c, lp); + if (cat != (lp->flags & LPROPS_CAT_MASK)) { + ubifs_err("bad LEB category %d expected %d", + (lp->flags & LPROPS_CAT_MASK), cat); + return -EINVAL; + } + } + + /* Check lp is on its category list (if it has one) */ + if (in_tree) { + struct list_head *list = NULL; + + switch (cat) { + case LPROPS_EMPTY: + list = &c->empty_list; + break; + case LPROPS_FREEABLE: + list = &c->freeable_list; + break; + case LPROPS_FRDI_IDX: + list = &c->frdi_idx_list; + break; + case LPROPS_UNCAT: + list = &c->uncat_list; + break; + } + if (list) { + struct ubifs_lprops *lprops; + int found = 0; + + list_for_each_entry(lprops, list, list) { + if (lprops == lp) { + found = 1; + break; + } + } + if (!found) { + ubifs_err("bad LPT list (category %d)", cat); + return -EINVAL; + } + } + } + + /* Check lp is on its category heap (if it has one) */ + if (in_tree && cat > 0 && cat <= LPROPS_HEAP_CNT) { + struct ubifs_lpt_heap *heap = &c->lpt_heap[cat - 1]; + + if ((lp->hpos != -1 && heap->arr[lp->hpos]->lnum != lnum) || + lp != heap->arr[lp->hpos]) { + ubifs_err("bad LPT heap (category %d)", cat); + return -EINVAL; + } + } + + buf = __vmalloc(c->leb_size, GFP_NOFS, PAGE_KERNEL); + if (!buf) + return -ENOMEM; + + /* + * After an unclean unmount, empty and freeable LEBs + * may contain garbage - do not scan them. + */ + if (lp->free == c->leb_size) { + lst->empty_lebs += 1; + lst->total_free += c->leb_size; + lst->total_dark += ubifs_calc_dark(c, c->leb_size); + return LPT_SCAN_CONTINUE; + } + if (lp->free + lp->dirty == c->leb_size && + !(lp->flags & LPROPS_INDEX)) { + lst->total_free += lp->free; + lst->total_dirty += lp->dirty; + lst->total_dark += ubifs_calc_dark(c, c->leb_size); + return LPT_SCAN_CONTINUE; + } + + sleb = ubifs_scan(c, lnum, 0, buf, 0); + if (IS_ERR(sleb)) { + ret = PTR_ERR(sleb); + if (ret == -EUCLEAN) { + ubifs_dump_lprops(c); + ubifs_dump_budg(c, &c->bi); + } + goto out; + } + + is_idx = -1; + list_for_each_entry(snod, &sleb->nodes, list) { + int found, level = 0; + + cond_resched(); + + if (is_idx == -1) + is_idx = (snod->type == UBIFS_IDX_NODE) ? 1 : 0; + + if (is_idx && snod->type != UBIFS_IDX_NODE) { + ubifs_err("indexing node in data LEB %d:%d", + lnum, snod->offs); + goto out_destroy; + } + + if (snod->type == UBIFS_IDX_NODE) { + struct ubifs_idx_node *idx = snod->node; + + key_read(c, ubifs_idx_key(c, idx), &snod->key); + level = le16_to_cpu(idx->level); + } + + found = ubifs_tnc_has_node(c, &snod->key, level, lnum, + snod->offs, is_idx); + if (found) { + if (found < 0) + goto out_destroy; + used += ALIGN(snod->len, 8); + } + } + + freef = c->leb_size - sleb->endpt; + dirty = sleb->endpt - used; + + if (freef > c->leb_size || freef < 0 || dirty > c->leb_size || + dirty < 0) { + ubifs_err("bad calculated accounting for LEB %d: free %d, dirty %d", + lnum, freef, dirty); + goto out_destroy; + } + + if (lp->free + lp->dirty == c->leb_size && + freef + dirty == c->leb_size) + if ((is_idx && !(lp->flags & LPROPS_INDEX)) || + (!is_idx && freef == c->leb_size) || + lp->free == c->leb_size) { + /* + * Empty or freeable LEBs could contain index + * nodes from an uncompleted commit due to an + * unclean unmount. Or they could be empty for + * the same reason. Or it may simply not have been + * unmapped. + */ + freef = lp->free; + dirty = lp->dirty; + is_idx = 0; + } + + if (is_idx && lp->free + lp->dirty == freef + dirty && + lnum != c->ihead_lnum) { + /* + * After an unclean unmount, an index LEB could have a different + * amount of free space than the value recorded by lprops. That + * is because the in-the-gaps method may use free space or + * create free space (as a side-effect of using ubi_leb_change + * and not writing the whole LEB). The incorrect free space + * value is not a problem because the index is only ever + * allocated empty LEBs, so there will never be an attempt to + * write to the free space at the end of an index LEB - except + * by the in-the-gaps method for which it is not a problem. + */ + freef = lp->free; + dirty = lp->dirty; + } + + if (lp->free != freef || lp->dirty != dirty) + goto out_print; + + if (is_idx && !(lp->flags & LPROPS_INDEX)) { + if (freef == c->leb_size) + /* Free but not unmapped LEB, it's fine */ + is_idx = 0; + else { + ubifs_err("indexing node without indexing flag"); + goto out_print; + } + } + + if (!is_idx && (lp->flags & LPROPS_INDEX)) { + ubifs_err("data node with indexing flag"); + goto out_print; + } + + if (freef == c->leb_size) + lst->empty_lebs += 1; + + if (is_idx) + lst->idx_lebs += 1; + + if (!(lp->flags & LPROPS_INDEX)) + lst->total_used += c->leb_size - freef - dirty; + lst->total_free += freef; + lst->total_dirty += dirty; + + if (!(lp->flags & LPROPS_INDEX)) { + int spc = freef + dirty; + + if (spc < c->dead_wm) + lst->total_dead += spc; + else + lst->total_dark += ubifs_calc_dark(c, spc); + } + + ubifs_scan_destroy(sleb); + vfree(buf); + return LPT_SCAN_CONTINUE; + +out_print: + ubifs_err("bad accounting of LEB %d: free %d, dirty %d flags %#x, should be free %d, dirty %d", + lnum, lp->free, lp->dirty, lp->flags, freef, dirty); + ubifs_dump_leb(c, lnum); +out_destroy: + ubifs_scan_destroy(sleb); + ret = -EINVAL; +out: + vfree(buf); + return ret; +} + +/** + * dbg_check_lprops - check all LEB properties. + * @c: UBIFS file-system description object + * + * This function checks all LEB properties and makes sure they are all correct. + * It returns zero if everything is fine, %-EINVAL if there is an inconsistency + * and other negative error codes in case of other errors. This function is + * called while the file system is locked (because of commit start), so no + * additional locking is required. Note that locking the LPT mutex would cause + * a circular lock dependency with the TNC mutex. + */ +int dbg_check_lprops(struct ubifs_info *c) +{ + int i, err; + struct ubifs_lp_stats lst; + + if (!dbg_is_chk_lprops(c)) + return 0; + + /* + * As we are going to scan the media, the write buffers have to be + * synchronized. + */ + for (i = 0; i < c->jhead_cnt; i++) { + err = ubifs_wbuf_sync(&c->jheads[i].wbuf); + if (err) + return err; + } + + memset(&lst, 0, sizeof(struct ubifs_lp_stats)); + err = ubifs_lpt_scan_nolock(c, c->main_first, c->leb_cnt - 1, + (ubifs_lpt_scan_callback)scan_check_cb, + &lst); + if (err && err != -ENOSPC) + goto out; + + if (lst.empty_lebs != c->lst.empty_lebs || + lst.idx_lebs != c->lst.idx_lebs || + lst.total_free != c->lst.total_free || + lst.total_dirty != c->lst.total_dirty || + lst.total_used != c->lst.total_used) { + ubifs_err("bad overall accounting"); + ubifs_err("calculated: empty_lebs %d, idx_lebs %d, total_free %lld, total_dirty %lld, total_used %lld", + lst.empty_lebs, lst.idx_lebs, lst.total_free, + lst.total_dirty, lst.total_used); + ubifs_err("read from lprops: empty_lebs %d, idx_lebs %d, total_free %lld, total_dirty %lld, total_used %lld", + c->lst.empty_lebs, c->lst.idx_lebs, c->lst.total_free, + c->lst.total_dirty, c->lst.total_used); + err = -EINVAL; + goto out; + } + + if (lst.total_dead != c->lst.total_dead || + lst.total_dark != c->lst.total_dark) { + ubifs_err("bad dead/dark space accounting"); + ubifs_err("calculated: total_dead %lld, total_dark %lld", + lst.total_dead, lst.total_dark); + ubifs_err("read from lprops: total_dead %lld, total_dark %lld", + c->lst.total_dead, c->lst.total_dark); + err = -EINVAL; + goto out; + } + + err = dbg_check_cats(c); +out: + return err; +} diff --git a/fs/ubifs/lpt.c b/fs/ubifs/lpt.c index 1a50d4c..c49d3b0 100644 --- a/fs/ubifs/lpt.c +++ b/fs/ubifs/lpt.c @@ -3,18 +3,7 @@ * * Copyright (C) 2006-2008 Nokia Corporation. * - * 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. - * - * 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., 51 - * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * SPDX-License-Identifier: GPL-2.0+ * * Authors: Adrian Hunter * Artem Bityutskiy (Битюцкий Артём) @@ -44,8 +33,17 @@ */ #include "ubifs.h" -#include "crc16.h" +#define __UBOOT__ +#ifndef __UBOOT__ +#include <linux/crc16.h> #include <linux/math64.h> +#include <linux/slab.h> +#else +#include <linux/compat.h> +#include <linux/err.h> +#include <ubi_uboot.h> +#include "crc16.h" +#endif /** * do_calc_lpt_geom - calculate sizes for the LPT area. @@ -159,6 +157,119 @@ int ubifs_calc_lpt_geom(struct ubifs_info *c) } /** + * calc_dflt_lpt_geom - calculate default LPT geometry. + * @c: the UBIFS file-system description object + * @main_lebs: number of main area LEBs is passed and returned here + * @big_lpt: whether the LPT area is "big" is returned here + * + * The size of the LPT area depends on parameters that themselves are dependent + * on the size of the LPT area. This function, successively recalculates the LPT + * area geometry until the parameters and resultant geometry are consistent. + * + * This function returns %0 on success and a negative error code on failure. + */ +static int calc_dflt_lpt_geom(struct ubifs_info *c, int *main_lebs, + int *big_lpt) +{ + int i, lebs_needed; + long long sz; + + /* Start by assuming the minimum number of LPT LEBs */ + c->lpt_lebs = UBIFS_MIN_LPT_LEBS; + c->main_lebs = *main_lebs - c->lpt_lebs; + if (c->main_lebs <= 0) + return -EINVAL; + + /* And assume we will use the small LPT model */ + c->big_lpt = 0; + + /* + * Calculate the geometry based on assumptions above and then see if it + * makes sense + */ + do_calc_lpt_geom(c); + + /* Small LPT model must have lpt_sz < leb_size */ + if (c->lpt_sz > c->leb_size) { + /* Nope, so try again using big LPT model */ + c->big_lpt = 1; + do_calc_lpt_geom(c); + } + + /* Now check there are enough LPT LEBs */ + for (i = 0; i < 64 ; i++) { + sz = c->lpt_sz * 4; /* Allow 4 times the size */ + lebs_needed = div_u64(sz + c->leb_size - 1, c->leb_size); + if (lebs_needed > c->lpt_lebs) { + /* Not enough LPT LEBs so try again with more */ + c->lpt_lebs = lebs_needed; + c->main_lebs = *main_lebs - c->lpt_lebs; + if (c->main_lebs <= 0) + return -EINVAL; + do_calc_lpt_geom(c); + continue; + } + if (c->ltab_sz > c->leb_size) { + ubifs_err("LPT ltab too big"); + return -EINVAL; + } + *main_lebs = c->main_lebs; + *big_lpt = c->big_lpt; + return 0; + } + return -EINVAL; +} + +/** + * pack_bits - pack bit fields end-to-end. + * @addr: address at which to pack (passed and next address returned) + * @pos: bit position at which to pack (passed and next position returned) + * @val: value to pack + * @nrbits: number of bits of value to pack (1-32) + */ +static void pack_bits(uint8_t **addr, int *pos, uint32_t val, int nrbits) +{ + uint8_t *p = *addr; + int b = *pos; + + ubifs_assert(nrbits > 0); + ubifs_assert(nrbits <= 32); + ubifs_assert(*pos >= 0); + ubifs_assert(*pos < 8); + ubifs_assert((val >> nrbits) == 0 || nrbits == 32); + if (b) { + *p |= ((uint8_t)val) << b; + nrbits += b; + if (nrbits > 8) { + *++p = (uint8_t)(val >>= (8 - b)); + if (nrbits > 16) { + *++p = (uint8_t)(val >>= 8); + if (nrbits > 24) { + *++p = (uint8_t)(val >>= 8); + if (nrbits > 32) + *++p = (uint8_t)(val >>= 8); + } + } + } + } else { + *p = (uint8_t)val; + if (nrbits > 8) { + *++p = (uint8_t)(val >>= 8); + if (nrbits > 16) { + *++p = (uint8_t)(val >>= 8); + if (nrbits > 24) + *++p = (uint8_t)(val >>= 8); + } + } + } + b = nrbits & 7; + if (b == 0) + p++; + *addr = p; + *pos = b; +} + +/** * ubifs_unpack_bits - unpack bit fields. * @addr: address at which to unpack (passed and next address returned) * @pos: bit position at which to unpack (passed and next position returned) @@ -228,6 +339,118 @@ uint32_t ubifs_unpack_bits(uint8_t **addr, int *pos, int nrbits) } /** + * ubifs_pack_pnode - pack all the bit fields of a pnode. + * @c: UBIFS file-system description object + * @buf: buffer into which to pack + * @pnode: pnode to pack + */ +void ubifs_pack_pnode(struct ubifs_info *c, void *buf, + struct ubifs_pnode *pnode) +{ + uint8_t *addr = buf + UBIFS_LPT_CRC_BYTES; + int i, pos = 0; + uint16_t crc; + + pack_bits(&addr, &pos, UBIFS_LPT_PNODE, UBIFS_LPT_TYPE_BITS); + if (c->big_lpt) + pack_bits(&addr, &pos, pnode->num, c->pcnt_bits); + for (i = 0; i < UBIFS_LPT_FANOUT; i++) { + pack_bits(&addr, &pos, pnode->lprops[i].free >> 3, + c->space_bits); + pack_bits(&addr, &pos, pnode->lprops[i].dirty >> 3, + c->space_bits); + if (pnode->lprops[i].flags & LPROPS_INDEX) + pack_bits(&addr, &pos, 1, 1); + else + pack_bits(&addr, &pos, 0, 1); + } + crc = crc16(-1, buf + UBIFS_LPT_CRC_BYTES, + c->pnode_sz - UBIFS_LPT_CRC_BYTES); + addr = buf; + pos = 0; + pack_bits(&addr, &pos, crc, UBIFS_LPT_CRC_BITS); +} + +/** + * ubifs_pack_nnode - pack all the bit fields of a nnode. + * @c: UBIFS file-system description object + * @buf: buffer into which to pack + * @nnode: nnode to pack + */ +void ubifs_pack_nnode(struct ubifs_info *c, void *buf, + struct ubifs_nnode *nnode) +{ + uint8_t *addr = buf + UBIFS_LPT_CRC_BYTES; + int i, pos = 0; + uint16_t crc; + + pack_bits(&addr, &pos, UBIFS_LPT_NNODE, UBIFS_LPT_TYPE_BITS); + if (c->big_lpt) + pack_bits(&addr, &pos, nnode->num, c->pcnt_bits); + for (i = 0; i < UBIFS_LPT_FANOUT; i++) { + int lnum = nnode->nbranch[i].lnum; + + if (lnum == 0) + lnum = c->lpt_last + 1; + pack_bits(&addr, &pos, lnum - c->lpt_first, c->lpt_lnum_bits); + pack_bits(&addr, &pos, nnode->nbranch[i].offs, + c->lpt_offs_bits); + } + crc = crc16(-1, buf + UBIFS_LPT_CRC_BYTES, + c->nnode_sz - UBIFS_LPT_CRC_BYTES); + addr = buf; + pos = 0; + pack_bits(&addr, &pos, crc, UBIFS_LPT_CRC_BITS); +} + +/** + * ubifs_pack_ltab - pack the LPT's own lprops table. + * @c: UBIFS file-system description object + * @buf: buffer into which to pack + * @ltab: LPT's own lprops table to pack + */ +void ubifs_pack_ltab(struct ubifs_info *c, void *buf, + struct ubifs_lpt_lprops *ltab) +{ + uint8_t *addr = buf + UBIFS_LPT_CRC_BYTES; + int i, pos = 0; + uint16_t crc; + + pack_bits(&addr, &pos, UBIFS_LPT_LTAB, UBIFS_LPT_TYPE_BITS); + for (i = 0; i < c->lpt_lebs; i++) { + pack_bits(&addr, &pos, ltab[i].free, c->lpt_spc_bits); + pack_bits(&addr, &pos, ltab[i].dirty, c->lpt_spc_bits); + } + crc = crc16(-1, buf + UBIFS_LPT_CRC_BYTES, + c->ltab_sz - UBIFS_LPT_CRC_BYTES); + addr = buf; + pos = 0; + pack_bits(&addr, &pos, crc, UBIFS_LPT_CRC_BITS); +} + +/** + * ubifs_pack_lsave - pack the LPT's save table. + * @c: UBIFS file-system description object + * @buf: buffer into which to pack + * @lsave: LPT's save table to pack + */ +void ubifs_pack_lsave(struct ubifs_info *c, void *buf, int *lsave) +{ + uint8_t *addr = buf + UBIFS_LPT_CRC_BYTES; + int i, pos = 0; + uint16_t crc; + + pack_bits(&addr, &pos, UBIFS_LPT_LSAVE, UBIFS_LPT_TYPE_BITS); + for (i = 0; i < c->lsave_cnt; i++) + pack_bits(&addr, &pos, lsave[i], c->lnum_bits); + crc = crc16(-1, buf + UBIFS_LPT_CRC_BYTES, + c->lsave_sz - UBIFS_LPT_CRC_BYTES); + addr = buf; + pos = 0; + pack_bits(&addr, &pos, crc, UBIFS_LPT_CRC_BITS); +} + +/** * ubifs_add_lpt_dirt - add dirty space to LPT LEB properties. * @c: UBIFS file-system description object * @lnum: LEB number to which to add dirty space @@ -244,6 +467,23 @@ void ubifs_add_lpt_dirt(struct ubifs_info *c, int lnum, int dirty) } /** + * set_ltab - set LPT LEB properties. + * @c: UBIFS file-system description object + * @lnum: LEB number + * @free: amount of free space + * @dirty: amount of dirty space + */ +static void set_ltab(struct ubifs_info *c, int lnum, int free, int dirty) +{ + dbg_lp("LEB %d free %d dirty %d to %d %d", + lnum, c->ltab[lnum - c->lpt_first].free, + c->ltab[lnum - c->lpt_first].dirty, free, dirty); + ubifs_assert(lnum >= c->lpt_first && lnum <= c->lpt_last); + c->ltab[lnum - c->lpt_first].free = free; + c->ltab[lnum - c->lpt_first].dirty = dirty; +} + +/** * ubifs_add_nnode_dirt - add dirty space to LPT LEB properties. * @c: UBIFS file-system description object * @nnode: nnode for which to add dirt @@ -276,6 +516,31 @@ static void add_pnode_dirt(struct ubifs_info *c, struct ubifs_pnode *pnode) } /** + * calc_nnode_num - calculate nnode number. + * @row: the row in the tree (root is zero) + * @col: the column in the row (leftmost is zero) + * + * The nnode number is a number that uniquely identifies a nnode and can be used + * easily to traverse the tree from the root to that nnode. + * + * This function calculates and returns the nnode number for the nnode at @row + * and @col. + */ +static int calc_nnode_num(int row, int col) +{ + int num, bits; + + num = 1; + while (row--) { + bits = (col & (UBIFS_LPT_FANOUT - 1)); + col >>= UBIFS_LPT_FANOUT_SHIFT; + num <<= UBIFS_LPT_FANOUT_SHIFT; + num |= bits; + } + return num; +} + +/** * calc_nnode_num_from_parent - calculate nnode number. * @c: UBIFS file-system description object * @parent: parent nnode @@ -328,6 +593,269 @@ static int calc_pnode_num_from_parent(const struct ubifs_info *c, } /** + * ubifs_create_dflt_lpt - create default LPT. + * @c: UBIFS file-system description object + * @main_lebs: number of main area LEBs is passed and returned here + * @lpt_first: LEB number of first LPT LEB + * @lpt_lebs: number of LEBs for LPT is passed and returned here + * @big_lpt: use big LPT model is passed and returned here + * + * This function returns %0 on success and a negative error code on failure. + */ +int ubifs_create_dflt_lpt(struct ubifs_info *c, int *main_lebs, int lpt_first, + int *lpt_lebs, int *big_lpt) +{ + int lnum, err = 0, node_sz, iopos, i, j, cnt, len, alen, row; + int blnum, boffs, bsz, bcnt; + struct ubifs_pnode *pnode = NULL; + struct ubifs_nnode *nnode = NULL; + void *buf = NULL, *p; + struct ubifs_lpt_lprops *ltab = NULL; + int *lsave = NULL; + + err = calc_dflt_lpt_geom(c, main_lebs, big_lpt); + if (err) + return err; + *lpt_lebs = c->lpt_lebs; + + /* Needed by 'ubifs_pack_nnode()' and 'set_ltab()' */ + c->lpt_first = lpt_first; + /* Needed by 'set_ltab()' */ + c->lpt_last = lpt_first + c->lpt_lebs - 1; + /* Needed by 'ubifs_pack_lsave()' */ + c->main_first = c->leb_cnt - *main_lebs; + + lsave = kmalloc(sizeof(int) * c->lsave_cnt, GFP_KERNEL); + pnode = kzalloc(sizeof(struct ubifs_pnode), GFP_KERNEL); + nnode = kzalloc(sizeof(struct ubifs_nnode), GFP_KERNEL); + buf = vmalloc(c->leb_size); + ltab = vmalloc(sizeof(struct ubifs_lpt_lprops) * c->lpt_lebs); + if (!pnode || !nnode || !buf || !ltab || !lsave) { + err = -ENOMEM; + goto out; + } + + ubifs_assert(!c->ltab); + c->ltab = ltab; /* Needed by set_ltab */ + + /* Initialize LPT's own lprops */ + for (i = 0; i < c->lpt_lebs; i++) { + ltab[i].free = c->leb_size; + ltab[i].dirty = 0; + ltab[i].tgc = 0; + ltab[i].cmt = 0; + } + + lnum = lpt_first; + p = buf; + /* Number of leaf nodes (pnodes) */ + cnt = c->pnode_cnt; + + /* + * The first pnode contains the LEB properties for the LEBs that contain + * the root inode node and the root index node of the index tree. + */ + node_sz = ALIGN(ubifs_idx_node_sz(c, 1), 8); + iopos = ALIGN(node_sz, c->min_io_size); + pnode->lprops[0].free = c->leb_size - iopos; + pnode->lprops[0].dirty = iopos - node_sz; + pnode->lprops[0].flags = LPROPS_INDEX; + + node_sz = UBIFS_INO_NODE_SZ; + iopos = ALIGN(node_sz, c->min_io_size); + pnode->lprops[1].free = c->leb_size - iopos; + pnode->lprops[1].dirty = iopos - node_sz; + + for (i = 2; i < UBIFS_LPT_FANOUT; i++) + pnode->lprops[i].free = c->leb_size; + + /* Add first pnode */ + ubifs_pack_pnode(c, p, pnode); + p += c->pnode_sz; + len = c->pnode_sz; + pnode->num += 1; + + /* Reset pnode values for remaining pnodes */ + pnode->lprops[0].free = c->leb_size; + pnode->lprops[0].dirty = 0; + pnode->lprops[0].flags = 0; + + pnode->lprops[1].free = c->leb_size; + pnode->lprops[1].dirty = 0; + + /* + * To calculate the internal node branches, we keep information about + * the level below. + */ + blnum = lnum; /* LEB number of level below */ + boffs = 0; /* Offset of level below */ + bcnt = cnt; /* Number of nodes in level below */ + bsz = c->pnode_sz; /* Size of nodes in level below */ + + /* Add all remaining pnodes */ + for (i = 1; i < cnt; i++) { + if (len + c->pnode_sz > c->leb_size) { + alen = ALIGN(len, c->min_io_size); + set_ltab(c, lnum, c->leb_size - alen, alen - len); + memset(p, 0xff, alen - len); + err = ubifs_leb_change(c, lnum++, buf, alen); + if (err) + goto out; + p = buf; + len = 0; + } + ubifs_pack_pnode(c, p, pnode); + p += c->pnode_sz; + len += c->pnode_sz; + /* + * pnodes are simply numbered left to right starting at zero, + * which means the pnode number can be used easily to traverse + * down the tree to the corresponding pnode. + */ + pnode->num += 1; + } + + row = 0; + for (i = UBIFS_LPT_FANOUT; cnt > i; i <<= UBIFS_LPT_FANOUT_SHIFT) + row += 1; + /* Add all nnodes, one level at a time */ + while (1) { + /* Number of internal nodes (nnodes) at next level */ + cnt = DIV_ROUND_UP(cnt, UBIFS_LPT_FANOUT); + for (i = 0; i < cnt; i++) { + if (len + c->nnode_sz > c->leb_size) { + alen = ALIGN(len, c->min_io_size); + set_ltab(c, lnum, c->leb_size - alen, + alen - len); + memset(p, 0xff, alen - len); + err = ubifs_leb_change(c, lnum++, buf, alen); + if (err) + goto out; + p = buf; + len = 0; + } + /* Only 1 nnode at this level, so it is the root */ + if (cnt == 1) { + c->lpt_lnum = lnum; + c->lpt_offs = len; + } + /* Set branches to the level below */ + for (j = 0; j < UBIFS_LPT_FANOUT; j++) { + if (bcnt) { + if (boffs + bsz > c->leb_size) { + blnum += 1; + boffs = 0; + } + nnode->nbranch[j].lnum = blnum; + nnode->nbranch[j].offs = boffs; + boffs += bsz; + bcnt--; + } else { + nnode->nbranch[j].lnum = 0; + nnode->nbranch[j].offs = 0; + } + } + nnode->num = calc_nnode_num(row, i); + ubifs_pack_nnode(c, p, nnode); + p += c->nnode_sz; + len += c->nnode_sz; + } + /* Only 1 nnode at this level, so it is the root */ + if (cnt == 1) + break; + /* Update the information about the level below */ + bcnt = cnt; + bsz = c->nnode_sz; + row -= 1; + } + + if (*big_lpt) { + /* Need to add LPT's save table */ + if (len + c->lsave_sz > c->leb_size) { + alen = ALIGN(len, c->min_io_size); + set_ltab(c, lnum, c->leb_size - alen, alen - len); + memset(p, 0xff, alen - len); + err = ubifs_leb_change(c, lnum++, buf, alen); + if (err) + goto out; + p = buf; + len = 0; + } + + c->lsave_lnum = lnum; + c->lsave_offs = len; + + for (i = 0; i < c->lsave_cnt && i < *main_lebs; i++) + lsave[i] = c->main_first + i; + for (; i < c->lsave_cnt; i++) + lsave[i] = c->main_first; + + ubifs_pack_lsave(c, p, lsave); + p += c->lsave_sz; + len += c->lsave_sz; + } + + /* Need to add LPT's own LEB properties table */ + if (len + c->ltab_sz > c->leb_size) { + alen = ALIGN(len, c->min_io_size); + set_ltab(c, lnum, c->leb_size - alen, alen - len); + memset(p, 0xff, alen - len); + err = ubifs_leb_change(c, lnum++, buf, alen); + if (err) + goto out; + p = buf; + len = 0; + } + + c->ltab_lnum = lnum; + c->ltab_offs = len; + + /* Update ltab before packing it */ + len += c->ltab_sz; + alen = ALIGN(len, c->min_io_size); + set_ltab(c, lnum, c->leb_size - alen, alen - len); + + ubifs_pack_ltab(c, p, ltab); + p += c->ltab_sz; + + /* Write remaining buffer */ + memset(p, 0xff, alen - len); + err = ubifs_leb_change(c, lnum, buf, alen); + if (err) + goto out; + + c->nhead_lnum = lnum; + c->nhead_offs = ALIGN(len, c->min_io_size); + + dbg_lp("space_bits %d", c->space_bits); + dbg_lp("lpt_lnum_bits %d", c->lpt_lnum_bits); + dbg_lp("lpt_offs_bits %d", c->lpt_offs_bits); + dbg_lp("lpt_spc_bits %d", c->lpt_spc_bits); + dbg_lp("pcnt_bits %d", c->pcnt_bits); + dbg_lp("lnum_bits %d", c->lnum_bits); + dbg_lp("pnode_sz %d", c->pnode_sz); + dbg_lp("nnode_sz %d", c->nnode_sz); + dbg_lp("ltab_sz %d", c->ltab_sz); + dbg_lp("lsave_sz %d", c->lsave_sz); + dbg_lp("lsave_cnt %d", c->lsave_cnt); + dbg_lp("lpt_hght %d", c->lpt_hght); + dbg_lp("big_lpt %d", c->big_lpt); + dbg_lp("LPT root is at %d:%d", c->lpt_lnum, c->lpt_offs); + dbg_lp("LPT head is at %d:%d", c->nhead_lnum, c->nhead_offs); + dbg_lp("LPT ltab is at %d:%d", c->ltab_lnum, c->ltab_offs); + if (c->big_lpt) + dbg_lp("LPT lsave is at %d:%d", c->lsave_lnum, c->lsave_offs); +out: + c->ltab = NULL; + kfree(lsave); + vfree(ltab); + vfree(buf); + kfree(nnode); + kfree(pnode); + return err; +} + +/** * update_cats - add LEB properties of a pnode to LEB category lists and heaps. * @c: UBIFS file-system description object * @pnode: pnode @@ -392,7 +920,7 @@ static int check_lpt_crc(void *buf, int len) if (crc != calc_crc) { ubifs_err("invalid crc in LPT node: crc %hx calc %hx", crc, calc_crc); - dbg_dump_stack(); + dump_stack(); return -EINVAL; } return 0; @@ -415,7 +943,7 @@ static int check_lpt_type(uint8_t **addr, int *pos, int type) if (node_type != type) { ubifs_err("invalid type (%d) in LPT node type %d", node_type, type); - dbg_dump_stack(); + dump_stack(); return -EINVAL; } return 0; @@ -524,6 +1052,34 @@ static int unpack_ltab(const struct ubifs_info *c, void *buf) return err; } +#ifndef __UBOOT__ +/** + * unpack_lsave - unpack the LPT's save table. + * @c: UBIFS file-system description object + * @buf: buffer from which to unpack + * + * This function returns %0 on success and a negative error code on failure. + */ +static int unpack_lsave(const struct ubifs_info *c, void *buf) +{ + uint8_t *addr = buf + UBIFS_LPT_CRC_BYTES; + int i, pos = 0, err; + + err = check_lpt_type(&addr, &pos, UBIFS_LPT_LSAVE); + if (err) + return err; + for (i = 0; i < c->lsave_cnt; i++) { + int lnum = ubifs_unpack_bits(&addr, &pos, c->lnum_bits); + + if (lnum < c->main_first || lnum >= c->leb_cnt) + return -EINVAL; + c->lsave[i] = lnum; + } + err = check_lpt_crc(buf, c->lsave_sz); + return err; +} +#endif + /** * validate_nnode - validate a nnode. * @c: UBIFS file-system description object @@ -662,7 +1218,7 @@ int ubifs_read_nnode(struct ubifs_info *c, struct ubifs_nnode *parent, int iip) if (c->big_lpt) nnode->num = calc_nnode_num_from_parent(c, parent, iip); } else { - err = ubi_read(c->ubi, lnum, buf, offs, c->nnode_sz); + err = ubifs_leb_read(c, lnum, buf, offs, c->nnode_sz, 1); if (err) goto out; err = ubifs_unpack_nnode(c, buf, nnode); @@ -687,6 +1243,7 @@ int ubifs_read_nnode(struct ubifs_info *c, struct ubifs_nnode *parent, int iip) out: ubifs_err("error %d reading nnode at %d:%d", err, lnum, offs); + dump_stack(); kfree(nnode); return err; } @@ -710,10 +1267,9 @@ static int read_pnode(struct ubifs_info *c, struct ubifs_nnode *parent, int iip) lnum = branch->lnum; offs = branch->offs; pnode = kzalloc(sizeof(struct ubifs_pnode), GFP_NOFS); - if (!pnode) { - err = -ENOMEM; - goto out; - } + if (!pnode) + return -ENOMEM; + if (lnum == 0) { /* * This pnode was not written which just means that the LEB @@ -731,7 +1287,7 @@ static int read_pnode(struct ubifs_info *c, struct ubifs_nnode *parent, int iip) lprops->flags = ubifs_categorize_lprops(c, lprops); } } else { - err = ubi_read(c->ubi, lnum, buf, offs, c->pnode_sz); + err = ubifs_leb_read(c, lnum, buf, offs, c->pnode_sz, 1); if (err) goto out; err = unpack_pnode(c, buf, pnode); @@ -752,8 +1308,9 @@ static int read_pnode(struct ubifs_info *c, struct ubifs_nnode *parent, int iip) out: ubifs_err("error %d reading pnode at %d:%d", err, lnum, offs); - dbg_dump_pnode(c, pnode, parent, iip); - dbg_msg("calc num: %d", calc_pnode_num_from_parent(c, parent, iip)); + ubifs_dump_pnode(c, pnode, parent, iip); + dump_stack(); + ubifs_err("calc num: %d", calc_pnode_num_from_parent(c, parent, iip)); kfree(pnode); return err; } @@ -772,7 +1329,7 @@ static int read_ltab(struct ubifs_info *c) buf = vmalloc(c->ltab_sz); if (!buf) return -ENOMEM; - err = ubi_read(c->ubi, c->ltab_lnum, buf, c->ltab_offs, c->ltab_sz); + err = ubifs_leb_read(c, c->ltab_lnum, buf, c->ltab_offs, c->ltab_sz, 1); if (err) goto out; err = unpack_ltab(c, buf); @@ -781,6 +1338,50 @@ out: return err; } +#ifndef __UBOOT__ +/** + * read_lsave - read LPT's save table. + * @c: UBIFS file-system description object + * + * This function returns %0 on success and a negative error code on failure. + */ +static int read_lsave(struct ubifs_info *c) +{ + int err, i; + void *buf; + + buf = vmalloc(c->lsave_sz); + if (!buf) + return -ENOMEM; + err = ubifs_leb_read(c, c->lsave_lnum, buf, c->lsave_offs, + c->lsave_sz, 1); + if (err) + goto out; + err = unpack_lsave(c, buf); + if (err) + goto out; + for (i = 0; i < c->lsave_cnt; i++) { + int lnum = c->lsave[i]; + struct ubifs_lprops *lprops; + + /* + * Due to automatic resizing, the values in the lsave table + * could be beyond the volume size - just ignore them. + */ + if (lnum >= c->leb_cnt) + continue; + lprops = ubifs_lpt_lookup(c, lnum); + if (IS_ERR(lprops)) { + err = PTR_ERR(lprops); + goto out; + } + } +out: + vfree(buf); + return err; +} +#endif + /** * ubifs_get_nnode - get a nnode. * @c: UBIFS file-system description object @@ -861,13 +1462,13 @@ struct ubifs_lprops *ubifs_lpt_lookup(struct ubifs_info *c, int lnum) shft -= UBIFS_LPT_FANOUT_SHIFT; nnode = ubifs_get_nnode(c, nnode, iip); if (IS_ERR(nnode)) - return ERR_PTR(PTR_ERR(nnode)); + return ERR_CAST(nnode); } iip = ((i >> shft) & (UBIFS_LPT_FANOUT - 1)); shft -= UBIFS_LPT_FANOUT_SHIFT; pnode = ubifs_get_pnode(c, nnode, iip); if (IS_ERR(pnode)) - return ERR_PTR(PTR_ERR(pnode)); + return ERR_CAST(pnode); iip = (i & (UBIFS_LPT_FANOUT - 1)); dbg_lp("LEB %d, free %d, dirty %d, flags %d", lnum, pnode->lprops[iip].free, pnode->lprops[iip].dirty, @@ -990,7 +1591,7 @@ struct ubifs_lprops *ubifs_lpt_lookup_dirty(struct ubifs_info *c, int lnum) nnode = c->nroot; nnode = dirty_cow_nnode(c, nnode); if (IS_ERR(nnode)) - return ERR_PTR(PTR_ERR(nnode)); + return ERR_CAST(nnode); i = lnum - c->main_first; shft = c->lpt_hght * UBIFS_LPT_FANOUT_SHIFT; for (h = 1; h < c->lpt_hght; h++) { @@ -998,19 +1599,19 @@ struct ubifs_lprops *ubifs_lpt_lookup_dirty(struct ubifs_info *c, int lnum) shft -= UBIFS_LPT_FANOUT_SHIFT; nnode = ubifs_get_nnode(c, nnode, iip); if (IS_ERR(nnode)) - return ERR_PTR(PTR_ERR(nnode)); + return ERR_CAST(nnode); nnode = dirty_cow_nnode(c, nnode); if (IS_ERR(nnode)) - return ERR_PTR(PTR_ERR(nnode)); + return ERR_CAST(nnode); } iip = ((i >> shft) & (UBIFS_LPT_FANOUT - 1)); shft -= UBIFS_LPT_FANOUT_SHIFT; pnode = ubifs_get_pnode(c, nnode, iip); if (IS_ERR(pnode)) - return ERR_PTR(PTR_ERR(pnode)); + return ERR_CAST(pnode); pnode = dirty_cow_pnode(c, pnode); if (IS_ERR(pnode)) - return ERR_PTR(PTR_ERR(pnode)); + return ERR_CAST(pnode); iip = (i & (UBIFS_LPT_FANOUT - 1)); dbg_lp("LEB %d, free %d, dirty %d, flags %d", lnum, pnode->lprops[iip].free, pnode->lprops[iip].dirty, @@ -1079,6 +1680,47 @@ static int lpt_init_rd(struct ubifs_info *c) return 0; } +#ifndef __UBOOT__ +/** + * lpt_init_wr - initialize the LPT for writing. + * @c: UBIFS file-system description object + * + * 'lpt_init_rd()' must have been called already. + * + * This function returns %0 on success and a negative error code on failure. + */ +static int lpt_init_wr(struct ubifs_info *c) +{ + int err, i; + + c->ltab_cmt = vmalloc(sizeof(struct ubifs_lpt_lprops) * c->lpt_lebs); + if (!c->ltab_cmt) + return -ENOMEM; + + c->lpt_buf = vmalloc(c->leb_size); + if (!c->lpt_buf) + return -ENOMEM; + + if (c->big_lpt) { + c->lsave = kmalloc(sizeof(int) * c->lsave_cnt, GFP_NOFS); + if (!c->lsave) + return -ENOMEM; + err = read_lsave(c); + if (err) + return err; + } + + for (i = 0; i < c->lpt_lebs; i++) + if (c->ltab[i].free == c->leb_size) { + err = ubifs_leb_unmap(c, i + c->lpt_first); + if (err) + return err; + } + + return 0; +} +#endif + /** * ubifs_lpt_init - initialize the LPT. * @c: UBIFS file-system description object @@ -1098,8 +1740,546 @@ int ubifs_lpt_init(struct ubifs_info *c, int rd, int wr) if (rd) { err = lpt_init_rd(c); if (err) + goto out_err; + } + +#ifndef __UBOOT__ + if (wr) { + err = lpt_init_wr(c); + if (err) + goto out_err; + } +#endif + + return 0; + +out_err: +#ifndef __UBOOT__ + if (wr) + ubifs_lpt_free(c, 1); +#endif + if (rd) + ubifs_lpt_free(c, 0); + return err; +} + +/** + * struct lpt_scan_node - somewhere to put nodes while we scan LPT. + * @nnode: where to keep a nnode + * @pnode: where to keep a pnode + * @cnode: where to keep a cnode + * @in_tree: is the node in the tree in memory + * @ptr.nnode: pointer to the nnode (if it is an nnode) which may be here or in + * the tree + * @ptr.pnode: ditto for pnode + * @ptr.cnode: ditto for cnode + */ +struct lpt_scan_node { + union { + struct ubifs_nnode nnode; + struct ubifs_pnode pnode; + struct ubifs_cnode cnode; + }; + int in_tree; + union { + struct ubifs_nnode *nnode; + struct ubifs_pnode *pnode; + struct ubifs_cnode *cnode; + } ptr; +}; + +/** + * scan_get_nnode - for the scan, get a nnode from either the tree or flash. + * @c: the UBIFS file-system description object + * @path: where to put the nnode + * @parent: parent of the nnode + * @iip: index in parent of the nnode + * + * This function returns a pointer to the nnode on success or a negative error + * code on failure. + */ +static struct ubifs_nnode *scan_get_nnode(struct ubifs_info *c, + struct lpt_scan_node *path, + struct ubifs_nnode *parent, int iip) +{ + struct ubifs_nbranch *branch; + struct ubifs_nnode *nnode; + void *buf = c->lpt_nod_buf; + int err; + + branch = &parent->nbranch[iip]; + nnode = branch->nnode; + if (nnode) { + path->in_tree = 1; + path->ptr.nnode = nnode; + return nnode; + } + nnode = &path->nnode; + path->in_tree = 0; + path->ptr.nnode = nnode; + memset(nnode, 0, sizeof(struct ubifs_nnode)); + if (branch->lnum == 0) { + /* + * This nnode was not written which just means that the LEB + * properties in the subtree below it describe empty LEBs. We + * make the nnode as though we had read it, which in fact means + * doing almost nothing. + */ + if (c->big_lpt) + nnode->num = calc_nnode_num_from_parent(c, parent, iip); + } else { + err = ubifs_leb_read(c, branch->lnum, buf, branch->offs, + c->nnode_sz, 1); + if (err) + return ERR_PTR(err); + err = ubifs_unpack_nnode(c, buf, nnode); + if (err) + return ERR_PTR(err); + } + err = validate_nnode(c, nnode, parent, iip); + if (err) + return ERR_PTR(err); + if (!c->big_lpt) + nnode->num = calc_nnode_num_from_parent(c, parent, iip); + nnode->level = parent->level - 1; + nnode->parent = parent; + nnode->iip = iip; + return nnode; +} + +/** + * scan_get_pnode - for the scan, get a pnode from either the tree or flash. + * @c: the UBIFS file-system description object + * @path: where to put the pnode + * @parent: parent of the pnode + * @iip: index in parent of the pnode + * + * This function returns a pointer to the pnode on success or a negative error + * code on failure. + */ +static struct ubifs_pnode *scan_get_pnode(struct ubifs_info *c, + struct lpt_scan_node *path, + struct ubifs_nnode *parent, int iip) +{ + struct ubifs_nbranch *branch; + struct ubifs_pnode *pnode; + void *buf = c->lpt_nod_buf; + int err; + + branch = &parent->nbranch[iip]; + pnode = branch->pnode; + if (pnode) { + path->in_tree = 1; + path->ptr.pnode = pnode; + return pnode; + } + pnode = &path->pnode; + path->in_tree = 0; + path->ptr.pnode = pnode; + memset(pnode, 0, sizeof(struct ubifs_pnode)); + if (branch->lnum == 0) { + /* + * This pnode was not written which just means that the LEB + * properties in it describe empty LEBs. We make the pnode as + * though we had read it. + */ + int i; + + if (c->big_lpt) + pnode->num = calc_pnode_num_from_parent(c, parent, iip); + for (i = 0; i < UBIFS_LPT_FANOUT; i++) { + struct ubifs_lprops * const lprops = &pnode->lprops[i]; + + lprops->free = c->leb_size; + lprops->flags = ubifs_categorize_lprops(c, lprops); + } + } else { + ubifs_assert(branch->lnum >= c->lpt_first && + branch->lnum <= c->lpt_last); + ubifs_assert(branch->offs >= 0 && branch->offs < c->leb_size); + err = ubifs_leb_read(c, branch->lnum, buf, branch->offs, + c->pnode_sz, 1); + if (err) + return ERR_PTR(err); + err = unpack_pnode(c, buf, pnode); + if (err) + return ERR_PTR(err); + } + err = validate_pnode(c, pnode, parent, iip); + if (err) + return ERR_PTR(err); + if (!c->big_lpt) + pnode->num = calc_pnode_num_from_parent(c, parent, iip); + pnode->parent = parent; + pnode->iip = iip; + set_pnode_lnum(c, pnode); + return pnode; +} + +/** + * ubifs_lpt_scan_nolock - scan the LPT. + * @c: the UBIFS file-system description object + * @start_lnum: LEB number from which to start scanning + * @end_lnum: LEB number at which to stop scanning + * @scan_cb: callback function called for each lprops + * @data: data to be passed to the callback function + * + * This function returns %0 on success and a negative error code on failure. + */ +int ubifs_lpt_scan_nolock(struct ubifs_info *c, int start_lnum, int end_lnum, + ubifs_lpt_scan_callback scan_cb, void *data) +{ + int err = 0, i, h, iip, shft; + struct ubifs_nnode *nnode; + struct ubifs_pnode *pnode; + struct lpt_scan_node *path; + + if (start_lnum == -1) { + start_lnum = end_lnum + 1; + if (start_lnum >= c->leb_cnt) + start_lnum = c->main_first; + } + + ubifs_assert(start_lnum >= c->main_first && start_lnum < c->leb_cnt); + ubifs_assert(end_lnum >= c->main_first && end_lnum < c->leb_cnt); + + if (!c->nroot) { + err = ubifs_read_nnode(c, NULL, 0); + if (err) return err; } + path = kmalloc(sizeof(struct lpt_scan_node) * (c->lpt_hght + 1), + GFP_NOFS); + if (!path) + return -ENOMEM; + + path[0].ptr.nnode = c->nroot; + path[0].in_tree = 1; +again: + /* Descend to the pnode containing start_lnum */ + nnode = c->nroot; + i = start_lnum - c->main_first; + shft = c->lpt_hght * UBIFS_LPT_FANOUT_SHIFT; + for (h = 1; h < c->lpt_hght; h++) { + iip = ((i >> shft) & (UBIFS_LPT_FANOUT - 1)); + shft -= UBIFS_LPT_FANOUT_SHIFT; + nnode = scan_get_nnode(c, path + h, nnode, iip); + if (IS_ERR(nnode)) { + err = PTR_ERR(nnode); + goto out; + } + } + iip = ((i >> shft) & (UBIFS_LPT_FANOUT - 1)); + shft -= UBIFS_LPT_FANOUT_SHIFT; + pnode = scan_get_pnode(c, path + h, nnode, iip); + if (IS_ERR(pnode)) { + err = PTR_ERR(pnode); + goto out; + } + iip = (i & (UBIFS_LPT_FANOUT - 1)); + + /* Loop for each lprops */ + while (1) { + struct ubifs_lprops *lprops = &pnode->lprops[iip]; + int ret, lnum = lprops->lnum; + + ret = scan_cb(c, lprops, path[h].in_tree, data); + if (ret < 0) { + err = ret; + goto out; + } + if (ret & LPT_SCAN_ADD) { + /* Add all the nodes in path to the tree in memory */ + for (h = 1; h < c->lpt_hght; h++) { + const size_t sz = sizeof(struct ubifs_nnode); + struct ubifs_nnode *parent; + + if (path[h].in_tree) + continue; + nnode = kmemdup(&path[h].nnode, sz, GFP_NOFS); + if (!nnode) { + err = -ENOMEM; + goto out; + } + parent = nnode->parent; + parent->nbranch[nnode->iip].nnode = nnode; + path[h].ptr.nnode = nnode; + path[h].in_tree = 1; + path[h + 1].cnode.parent = nnode; + } + if (path[h].in_tree) + ubifs_ensure_cat(c, lprops); + else { + const size_t sz = sizeof(struct ubifs_pnode); + struct ubifs_nnode *parent; + + pnode = kmemdup(&path[h].pnode, sz, GFP_NOFS); + if (!pnode) { + err = -ENOMEM; + goto out; + } + parent = pnode->parent; + parent->nbranch[pnode->iip].pnode = pnode; + path[h].ptr.pnode = pnode; + path[h].in_tree = 1; + update_cats(c, pnode); + c->pnodes_have += 1; + } + err = dbg_check_lpt_nodes(c, (struct ubifs_cnode *) + c->nroot, 0, 0); + if (err) + goto out; + err = dbg_check_cats(c); + if (err) + goto out; + } + if (ret & LPT_SCAN_STOP) { + err = 0; + break; + } + /* Get the next lprops */ + if (lnum == end_lnum) { + /* + * We got to the end without finding what we were + * looking for + */ + err = -ENOSPC; + goto out; + } + if (lnum + 1 >= c->leb_cnt) { + /* Wrap-around to the beginning */ + start_lnum = c->main_first; + goto again; + } + if (iip + 1 < UBIFS_LPT_FANOUT) { + /* Next lprops is in the same pnode */ + iip += 1; + continue; + } + /* We need to get the next pnode. Go up until we can go right */ + iip = pnode->iip; + while (1) { + h -= 1; + ubifs_assert(h >= 0); + nnode = path[h].ptr.nnode; + if (iip + 1 < UBIFS_LPT_FANOUT) + break; + iip = nnode->iip; + } + /* Go right */ + iip += 1; + /* Descend to the pnode */ + h += 1; + for (; h < c->lpt_hght; h++) { + nnode = scan_get_nnode(c, path + h, nnode, iip); + if (IS_ERR(nnode)) { + err = PTR_ERR(nnode); + goto out; + } + iip = 0; + } + pnode = scan_get_pnode(c, path + h, nnode, iip); + if (IS_ERR(pnode)) { + err = PTR_ERR(pnode); + goto out; + } + iip = 0; + } +out: + kfree(path); + return err; +} + +/** + * dbg_chk_pnode - check a pnode. + * @c: the UBIFS file-system description object + * @pnode: pnode to check + * @col: pnode column + * + * This function returns %0 on success and a negative error code on failure. + */ +static int dbg_chk_pnode(struct ubifs_info *c, struct ubifs_pnode *pnode, + int col) +{ + int i; + + if (pnode->num != col) { + ubifs_err("pnode num %d expected %d parent num %d iip %d", + pnode->num, col, pnode->parent->num, pnode->iip); + return -EINVAL; + } + for (i = 0; i < UBIFS_LPT_FANOUT; i++) { + struct ubifs_lprops *lp, *lprops = &pnode->lprops[i]; + int lnum = (pnode->num << UBIFS_LPT_FANOUT_SHIFT) + i + + c->main_first; + int found, cat = lprops->flags & LPROPS_CAT_MASK; + struct ubifs_lpt_heap *heap; + struct list_head *list = NULL; + + if (lnum >= c->leb_cnt) + continue; + if (lprops->lnum != lnum) { + ubifs_err("bad LEB number %d expected %d", + lprops->lnum, lnum); + return -EINVAL; + } + if (lprops->flags & LPROPS_TAKEN) { + if (cat != LPROPS_UNCAT) { + ubifs_err("LEB %d taken but not uncat %d", + lprops->lnum, cat); + return -EINVAL; + } + continue; + } + if (lprops->flags & LPROPS_INDEX) { + switch (cat) { + case LPROPS_UNCAT: + case LPROPS_DIRTY_IDX: + case LPROPS_FRDI_IDX: + break; + default: + ubifs_err("LEB %d index but cat %d", + lprops->lnum, cat); + return -EINVAL; + } + } else { + switch (cat) { + case LPROPS_UNCAT: + case LPROPS_DIRTY: + case LPROPS_FREE: + case LPROPS_EMPTY: + case LPROPS_FREEABLE: + break; + default: + ubifs_err("LEB %d not index but cat %d", + lprops->lnum, cat); + return -EINVAL; + } + } + switch (cat) { + case LPROPS_UNCAT: + list = &c->uncat_list; + break; + case LPROPS_EMPTY: + list = &c->empty_list; + break; + case LPROPS_FREEABLE: + list = &c->freeable_list; + break; + case LPROPS_FRDI_IDX: + list = &c->frdi_idx_list; + break; + } + found = 0; + switch (cat) { + case LPROPS_DIRTY: + case LPROPS_DIRTY_IDX: + case LPROPS_FREE: + heap = &c->lpt_heap[cat - 1]; + if (lprops->hpos < heap->cnt && + heap->arr[lprops->hpos] == lprops) + found = 1; + break; + case LPROPS_UNCAT: + case LPROPS_EMPTY: + case LPROPS_FREEABLE: + case LPROPS_FRDI_IDX: + list_for_each_entry(lp, list, list) + if (lprops == lp) { + found = 1; + break; + } + break; + } + if (!found) { + ubifs_err("LEB %d cat %d not found in cat heap/list", + lprops->lnum, cat); + return -EINVAL; + } + switch (cat) { + case LPROPS_EMPTY: + if (lprops->free != c->leb_size) { + ubifs_err("LEB %d cat %d free %d dirty %d", + lprops->lnum, cat, lprops->free, + lprops->dirty); + return -EINVAL; + } + case LPROPS_FREEABLE: + case LPROPS_FRDI_IDX: + if (lprops->free + lprops->dirty != c->leb_size) { + ubifs_err("LEB %d cat %d free %d dirty %d", + lprops->lnum, cat, lprops->free, + lprops->dirty); + return -EINVAL; + } + } + } + return 0; +} + +/** + * dbg_check_lpt_nodes - check nnodes and pnodes. + * @c: the UBIFS file-system description object + * @cnode: next cnode (nnode or pnode) to check + * @row: row of cnode (root is zero) + * @col: column of cnode (leftmost is zero) + * + * This function returns %0 on success and a negative error code on failure. + */ +int dbg_check_lpt_nodes(struct ubifs_info *c, struct ubifs_cnode *cnode, + int row, int col) +{ + struct ubifs_nnode *nnode, *nn; + struct ubifs_cnode *cn; + int num, iip = 0, err; + + if (!dbg_is_chk_lprops(c)) + return 0; + + while (cnode) { + ubifs_assert(row >= 0); + nnode = cnode->parent; + if (cnode->level) { + /* cnode is a nnode */ + num = calc_nnode_num(row, col); + if (cnode->num != num) { + ubifs_err("nnode num %d expected %d parent num %d iip %d", + cnode->num, num, + (nnode ? nnode->num : 0), cnode->iip); + return -EINVAL; + } + nn = (struct ubifs_nnode *)cnode; + while (iip < UBIFS_LPT_FANOUT) { + cn = nn->nbranch[iip].cnode; + if (cn) { + /* Go down */ + row += 1; + col <<= UBIFS_LPT_FANOUT_SHIFT; + col += iip; + iip = 0; + cnode = cn; + break; + } + /* Go right */ + iip += 1; + } + if (iip < UBIFS_LPT_FANOUT) + continue; + } else { + struct ubifs_pnode *pnode; + + /* cnode is a pnode */ + pnode = (struct ubifs_pnode *)cnode; + err = dbg_chk_pnode(c, pnode, col); + if (err) + return err; + } + /* Go up and to the right */ + row -= 1; + col >>= UBIFS_LPT_FANOUT_SHIFT; + iip = cnode->iip + 1; + cnode = (struct ubifs_cnode *)nnode; + } return 0; } diff --git a/fs/ubifs/lpt_commit.c b/fs/ubifs/lpt_commit.c index c0af818..cad422e 100644 --- a/fs/ubifs/lpt_commit.c +++ b/fs/ubifs/lpt_commit.c @@ -3,18 +3,7 @@ * * Copyright (C) 2006-2008 Nokia Corporation. * - * 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. - * - * 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., 51 - * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * SPDX-License-Identifier: GPL-2.0+ * * Authors: Adrian Hunter * Artem Bityutskiy (Битюцкий Артём) @@ -25,9 +14,1284 @@ * subsystem. */ +#define __UBOOT__ +#ifndef __UBOOT__ +#include <linux/crc16.h> +#include <linux/slab.h> +#include <linux/random.h> +#else +#include <linux/compat.h> +#include <linux/err.h> #include "crc16.h" +#endif #include "ubifs.h" +#ifndef __UBOOT__ +static int dbg_populate_lsave(struct ubifs_info *c); +#endif + +/** + * first_dirty_cnode - find first dirty cnode. + * @c: UBIFS file-system description object + * @nnode: nnode at which to start + * + * This function returns the first dirty cnode or %NULL if there is not one. + */ +static struct ubifs_cnode *first_dirty_cnode(struct ubifs_nnode *nnode) +{ + ubifs_assert(nnode); + while (1) { + int i, cont = 0; + + for (i = 0; i < UBIFS_LPT_FANOUT; i++) { + struct ubifs_cnode *cnode; + + cnode = nnode->nbranch[i].cnode; + if (cnode && + test_bit(DIRTY_CNODE, &cnode->flags)) { + if (cnode->level == 0) + return cnode; + nnode = (struct ubifs_nnode *)cnode; + cont = 1; + break; + } + } + if (!cont) + return (struct ubifs_cnode *)nnode; + } +} + +/** + * next_dirty_cnode - find next dirty cnode. + * @cnode: cnode from which to begin searching + * + * This function returns the next dirty cnode or %NULL if there is not one. + */ +static struct ubifs_cnode *next_dirty_cnode(struct ubifs_cnode *cnode) +{ + struct ubifs_nnode *nnode; + int i; + + ubifs_assert(cnode); + nnode = cnode->parent; + if (!nnode) + return NULL; + for (i = cnode->iip + 1; i < UBIFS_LPT_FANOUT; i++) { + cnode = nnode->nbranch[i].cnode; + if (cnode && test_bit(DIRTY_CNODE, &cnode->flags)) { + if (cnode->level == 0) + return cnode; /* cnode is a pnode */ + /* cnode is a nnode */ + return first_dirty_cnode((struct ubifs_nnode *)cnode); + } + } + return (struct ubifs_cnode *)nnode; +} + +/** + * get_cnodes_to_commit - create list of dirty cnodes to commit. + * @c: UBIFS file-system description object + * + * This function returns the number of cnodes to commit. + */ +static int get_cnodes_to_commit(struct ubifs_info *c) +{ + struct ubifs_cnode *cnode, *cnext; + int cnt = 0; + + if (!c->nroot) + return 0; + + if (!test_bit(DIRTY_CNODE, &c->nroot->flags)) + return 0; + + c->lpt_cnext = first_dirty_cnode(c->nroot); + cnode = c->lpt_cnext; + if (!cnode) + return 0; + cnt += 1; + while (1) { + ubifs_assert(!test_bit(COW_CNODE, &cnode->flags)); + __set_bit(COW_CNODE, &cnode->flags); + cnext = next_dirty_cnode(cnode); + if (!cnext) { + cnode->cnext = c->lpt_cnext; + break; + } + cnode->cnext = cnext; + cnode = cnext; + cnt += 1; + } + dbg_cmt("committing %d cnodes", cnt); + dbg_lp("committing %d cnodes", cnt); + ubifs_assert(cnt == c->dirty_nn_cnt + c->dirty_pn_cnt); + return cnt; +} + +/** + * upd_ltab - update LPT LEB properties. + * @c: UBIFS file-system description object + * @lnum: LEB number + * @free: amount of free space + * @dirty: amount of dirty space to add + */ +static void upd_ltab(struct ubifs_info *c, int lnum, int free, int dirty) +{ + dbg_lp("LEB %d free %d dirty %d to %d +%d", + lnum, c->ltab[lnum - c->lpt_first].free, + c->ltab[lnum - c->lpt_first].dirty, free, dirty); + ubifs_assert(lnum >= c->lpt_first && lnum <= c->lpt_last); + c->ltab[lnum - c->lpt_first].free = free; + c->ltab[lnum - c->lpt_first].dirty += dirty; +} + +/** + * alloc_lpt_leb - allocate an LPT LEB that is empty. + * @c: UBIFS file-system description object + * @lnum: LEB number is passed and returned here + * + * This function finds the next empty LEB in the ltab starting from @lnum. If a + * an empty LEB is found it is returned in @lnum and the function returns %0. + * Otherwise the function returns -ENOSPC. Note however, that LPT is designed + * never to run out of space. + */ +static int alloc_lpt_leb(struct ubifs_info *c, int *lnum) +{ + int i, n; + + n = *lnum - c->lpt_first + 1; + for (i = n; i < c->lpt_lebs; i++) { + if (c->ltab[i].tgc || c->ltab[i].cmt) + continue; + if (c->ltab[i].free == c->leb_size) { + c->ltab[i].cmt = 1; + *lnum = i + c->lpt_first; + return 0; + } + } + + for (i = 0; i < n; i++) { + if (c->ltab[i].tgc || c->ltab[i].cmt) + continue; + if (c->ltab[i].free == c->leb_size) { + c->ltab[i].cmt = 1; + *lnum = i + c->lpt_first; + return 0; + } + } + return -ENOSPC; +} + +/** + * layout_cnodes - layout cnodes for commit. + * @c: UBIFS file-system description object + * + * This function returns %0 on success and a negative error code on failure. + */ +static int layout_cnodes(struct ubifs_info *c) +{ + int lnum, offs, len, alen, done_lsave, done_ltab, err; + struct ubifs_cnode *cnode; + + err = dbg_chk_lpt_sz(c, 0, 0); + if (err) + return err; + cnode = c->lpt_cnext; + if (!cnode) + return 0; + lnum = c->nhead_lnum; + offs = c->nhead_offs; + /* Try to place lsave and ltab nicely */ + done_lsave = !c->big_lpt; + done_ltab = 0; + if (!done_lsave && offs + c->lsave_sz <= c->leb_size) { + done_lsave = 1; + c->lsave_lnum = lnum; + c->lsave_offs = offs; + offs += c->lsave_sz; + dbg_chk_lpt_sz(c, 1, c->lsave_sz); + } + + if (offs + c->ltab_sz <= c->leb_size) { + done_ltab = 1; + c->ltab_lnum = lnum; + c->ltab_offs = offs; + offs += c->ltab_sz; + dbg_chk_lpt_sz(c, 1, c->ltab_sz); + } + + do { + if (cnode->level) { + len = c->nnode_sz; + c->dirty_nn_cnt -= 1; + } else { + len = c->pnode_sz; + c->dirty_pn_cnt -= 1; + } + while (offs + len > c->leb_size) { + alen = ALIGN(offs, c->min_io_size); + upd_ltab(c, lnum, c->leb_size - alen, alen - offs); + dbg_chk_lpt_sz(c, 2, c->leb_size - offs); + err = alloc_lpt_leb(c, &lnum); + if (err) + goto no_space; + offs = 0; + ubifs_assert(lnum >= c->lpt_first && + lnum <= c->lpt_last); + /* Try to place lsave and ltab nicely */ + if (!done_lsave) { + done_lsave = 1; + c->lsave_lnum = lnum; + c->lsave_offs = offs; + offs += c->lsave_sz; + dbg_chk_lpt_sz(c, 1, c->lsave_sz); + continue; + } + if (!done_ltab) { + done_ltab = 1; + c->ltab_lnum = lnum; + c->ltab_offs = offs; + offs += c->ltab_sz; + dbg_chk_lpt_sz(c, 1, c->ltab_sz); + continue; + } + break; + } + if (cnode->parent) { + cnode->parent->nbranch[cnode->iip].lnum = lnum; + cnode->parent->nbranch[cnode->iip].offs = offs; + } else { + c->lpt_lnum = lnum; + c->lpt_offs = offs; + } + offs += len; + dbg_chk_lpt_sz(c, 1, len); + cnode = cnode->cnext; + } while (cnode && cnode != c->lpt_cnext); + + /* Make sure to place LPT's save table */ + if (!done_lsave) { + if (offs + c->lsave_sz > c->leb_size) { + alen = ALIGN(offs, c->min_io_size); + upd_ltab(c, lnum, c->leb_size - alen, alen - offs); + dbg_chk_lpt_sz(c, 2, c->leb_size - offs); + err = alloc_lpt_leb(c, &lnum); + if (err) + goto no_space; + offs = 0; + ubifs_assert(lnum >= c->lpt_first && + lnum <= c->lpt_last); + } + done_lsave = 1; + c->lsave_lnum = lnum; + c->lsave_offs = offs; + offs += c->lsave_sz; + dbg_chk_lpt_sz(c, 1, c->lsave_sz); + } + + /* Make sure to place LPT's own lprops table */ + if (!done_ltab) { + if (offs + c->ltab_sz > c->leb_size) { + alen = ALIGN(offs, c->min_io_size); + upd_ltab(c, lnum, c->leb_size - alen, alen - offs); + dbg_chk_lpt_sz(c, 2, c->leb_size - offs); + err = alloc_lpt_leb(c, &lnum); + if (err) + goto no_space; + offs = 0; + ubifs_assert(lnum >= c->lpt_first && + lnum <= c->lpt_last); + } + done_ltab = 1; + c->ltab_lnum = lnum; + c->ltab_offs = offs; + offs += c->ltab_sz; + dbg_chk_lpt_sz(c, 1, c->ltab_sz); + } + + alen = ALIGN(offs, c->min_io_size); + upd_ltab(c, lnum, c->leb_size - alen, alen - offs); + dbg_chk_lpt_sz(c, 4, alen - offs); + err = dbg_chk_lpt_sz(c, 3, alen); + if (err) + return err; + return 0; + +no_space: + ubifs_err("LPT out of space at LEB %d:%d needing %d, done_ltab %d, done_lsave %d", + lnum, offs, len, done_ltab, done_lsave); + ubifs_dump_lpt_info(c); + ubifs_dump_lpt_lebs(c); + dump_stack(); + return err; +} + +#ifndef __UBOOT__ +/** + * realloc_lpt_leb - allocate an LPT LEB that is empty. + * @c: UBIFS file-system description object + * @lnum: LEB number is passed and returned here + * + * This function duplicates exactly the results of the function alloc_lpt_leb. + * It is used during end commit to reallocate the same LEB numbers that were + * allocated by alloc_lpt_leb during start commit. + * + * This function finds the next LEB that was allocated by the alloc_lpt_leb + * function starting from @lnum. If a LEB is found it is returned in @lnum and + * the function returns %0. Otherwise the function returns -ENOSPC. + * Note however, that LPT is designed never to run out of space. + */ +static int realloc_lpt_leb(struct ubifs_info *c, int *lnum) +{ + int i, n; + + n = *lnum - c->lpt_first + 1; + for (i = n; i < c->lpt_lebs; i++) + if (c->ltab[i].cmt) { + c->ltab[i].cmt = 0; + *lnum = i + c->lpt_first; + return 0; + } + + for (i = 0; i < n; i++) + if (c->ltab[i].cmt) { + c->ltab[i].cmt = 0; + *lnum = i + c->lpt_first; + return 0; + } + return -ENOSPC; +} + +/** + * write_cnodes - write cnodes for commit. + * @c: UBIFS file-system description object + * + * This function returns %0 on success and a negative error code on failure. + */ +static int write_cnodes(struct ubifs_info *c) +{ + int lnum, offs, len, from, err, wlen, alen, done_ltab, done_lsave; + struct ubifs_cnode *cnode; + void *buf = c->lpt_buf; + + cnode = c->lpt_cnext; + if (!cnode) + return 0; + lnum = c->nhead_lnum; + offs = c->nhead_offs; + from = offs; + /* Ensure empty LEB is unmapped */ + if (offs == 0) { + err = ubifs_leb_unmap(c, lnum); + if (err) + return err; + } + /* Try to place lsave and ltab nicely */ + done_lsave = !c->big_lpt; + done_ltab = 0; + if (!done_lsave && offs + c->lsave_sz <= c->leb_size) { + done_lsave = 1; + ubifs_pack_lsave(c, buf + offs, c->lsave); + offs += c->lsave_sz; + dbg_chk_lpt_sz(c, 1, c->lsave_sz); + } + + if (offs + c->ltab_sz <= c->leb_size) { + done_ltab = 1; + ubifs_pack_ltab(c, buf + offs, c->ltab_cmt); + offs += c->ltab_sz; + dbg_chk_lpt_sz(c, 1, c->ltab_sz); + } + + /* Loop for each cnode */ + do { + if (cnode->level) + len = c->nnode_sz; + else + len = c->pnode_sz; + while (offs + len > c->leb_size) { + wlen = offs - from; + if (wlen) { + alen = ALIGN(wlen, c->min_io_size); + memset(buf + offs, 0xff, alen - wlen); + err = ubifs_leb_write(c, lnum, buf + from, from, + alen); + if (err) + return err; + } + dbg_chk_lpt_sz(c, 2, c->leb_size - offs); + err = realloc_lpt_leb(c, &lnum); + if (err) + goto no_space; + offs = from = 0; + ubifs_assert(lnum >= c->lpt_first && + lnum <= c->lpt_last); + err = ubifs_leb_unmap(c, lnum); + if (err) + return err; + /* Try to place lsave and ltab nicely */ + if (!done_lsave) { + done_lsave = 1; + ubifs_pack_lsave(c, buf + offs, c->lsave); + offs += c->lsave_sz; + dbg_chk_lpt_sz(c, 1, c->lsave_sz); + continue; + } + if (!done_ltab) { + done_ltab = 1; + ubifs_pack_ltab(c, buf + offs, c->ltab_cmt); + offs += c->ltab_sz; + dbg_chk_lpt_sz(c, 1, c->ltab_sz); + continue; + } + break; + } + if (cnode->level) + ubifs_pack_nnode(c, buf + offs, + (struct ubifs_nnode *)cnode); + else + ubifs_pack_pnode(c, buf + offs, + (struct ubifs_pnode *)cnode); + /* + * The reason for the barriers is the same as in case of TNC. + * See comment in 'write_index()'. 'dirty_cow_nnode()' and + * 'dirty_cow_pnode()' are the functions for which this is + * important. + */ + clear_bit(DIRTY_CNODE, &cnode->flags); + smp_mb__before_clear_bit(); + clear_bit(COW_CNODE, &cnode->flags); + smp_mb__after_clear_bit(); + offs += len; + dbg_chk_lpt_sz(c, 1, len); + cnode = cnode->cnext; + } while (cnode && cnode != c->lpt_cnext); + + /* Make sure to place LPT's save table */ + if (!done_lsave) { + if (offs + c->lsave_sz > c->leb_size) { + wlen = offs - from; + alen = ALIGN(wlen, c->min_io_size); + memset(buf + offs, 0xff, alen - wlen); + err = ubifs_leb_write(c, lnum, buf + from, from, alen); + if (err) + return err; + dbg_chk_lpt_sz(c, 2, c->leb_size - offs); + err = realloc_lpt_leb(c, &lnum); + if (err) + goto no_space; + offs = from = 0; + ubifs_assert(lnum >= c->lpt_first && + lnum <= c->lpt_last); + err = ubifs_leb_unmap(c, lnum); + if (err) + return err; + } + done_lsave = 1; + ubifs_pack_lsave(c, buf + offs, c->lsave); + offs += c->lsave_sz; + dbg_chk_lpt_sz(c, 1, c->lsave_sz); + } + + /* Make sure to place LPT's own lprops table */ + if (!done_ltab) { + if (offs + c->ltab_sz > c->leb_size) { + wlen = offs - from; + alen = ALIGN(wlen, c->min_io_size); + memset(buf + offs, 0xff, alen - wlen); + err = ubifs_leb_write(c, lnum, buf + from, from, alen); + if (err) + return err; + dbg_chk_lpt_sz(c, 2, c->leb_size - offs); + err = realloc_lpt_leb(c, &lnum); + if (err) + goto no_space; + offs = from = 0; + ubifs_assert(lnum >= c->lpt_first && + lnum <= c->lpt_last); + err = ubifs_leb_unmap(c, lnum); + if (err) + return err; + } + done_ltab = 1; + ubifs_pack_ltab(c, buf + offs, c->ltab_cmt); + offs += c->ltab_sz; + dbg_chk_lpt_sz(c, 1, c->ltab_sz); + } + + /* Write remaining data in buffer */ + wlen = offs - from; + alen = ALIGN(wlen, c->min_io_size); + memset(buf + offs, 0xff, alen - wlen); + err = ubifs_leb_write(c, lnum, buf + from, from, alen); + if (err) + return err; + + dbg_chk_lpt_sz(c, 4, alen - wlen); + err = dbg_chk_lpt_sz(c, 3, ALIGN(offs, c->min_io_size)); + if (err) + return err; + + c->nhead_lnum = lnum; + c->nhead_offs = ALIGN(offs, c->min_io_size); + + dbg_lp("LPT root is at %d:%d", c->lpt_lnum, c->lpt_offs); + dbg_lp("LPT head is at %d:%d", c->nhead_lnum, c->nhead_offs); + dbg_lp("LPT ltab is at %d:%d", c->ltab_lnum, c->ltab_offs); + if (c->big_lpt) + dbg_lp("LPT lsave is at %d:%d", c->lsave_lnum, c->lsave_offs); + + return 0; + +no_space: + ubifs_err("LPT out of space mismatch at LEB %d:%d needing %d, done_ltab %d, done_lsave %d", + lnum, offs, len, done_ltab, done_lsave); + ubifs_dump_lpt_info(c); + ubifs_dump_lpt_lebs(c); + dump_stack(); + return err; +} +#endif + +/** + * next_pnode_to_dirty - find next pnode to dirty. + * @c: UBIFS file-system description object + * @pnode: pnode + * + * This function returns the next pnode to dirty or %NULL if there are no more + * pnodes. Note that pnodes that have never been written (lnum == 0) are + * skipped. + */ +static struct ubifs_pnode *next_pnode_to_dirty(struct ubifs_info *c, + struct ubifs_pnode *pnode) +{ + struct ubifs_nnode *nnode; + int iip; + + /* Try to go right */ + nnode = pnode->parent; + for (iip = pnode->iip + 1; iip < UBIFS_LPT_FANOUT; iip++) { + if (nnode->nbranch[iip].lnum) + return ubifs_get_pnode(c, nnode, iip); + } + + /* Go up while can't go right */ + do { + iip = nnode->iip + 1; + nnode = nnode->parent; + if (!nnode) + return NULL; + for (; iip < UBIFS_LPT_FANOUT; iip++) { + if (nnode->nbranch[iip].lnum) + break; + } + } while (iip >= UBIFS_LPT_FANOUT); + + /* Go right */ + nnode = ubifs_get_nnode(c, nnode, iip); + if (IS_ERR(nnode)) + return (void *)nnode; + + /* Go down to level 1 */ + while (nnode->level > 1) { + for (iip = 0; iip < UBIFS_LPT_FANOUT; iip++) { + if (nnode->nbranch[iip].lnum) + break; + } + if (iip >= UBIFS_LPT_FANOUT) { + /* + * Should not happen, but we need to keep going + * if it does. + */ + iip = 0; + } + nnode = ubifs_get_nnode(c, nnode, iip); + if (IS_ERR(nnode)) + return (void *)nnode; + } + + for (iip = 0; iip < UBIFS_LPT_FANOUT; iip++) + if (nnode->nbranch[iip].lnum) + break; + if (iip >= UBIFS_LPT_FANOUT) + /* Should not happen, but we need to keep going if it does */ + iip = 0; + return ubifs_get_pnode(c, nnode, iip); +} + +/** + * pnode_lookup - lookup a pnode in the LPT. + * @c: UBIFS file-system description object + * @i: pnode number (0 to main_lebs - 1) + * + * This function returns a pointer to the pnode on success or a negative + * error code on failure. + */ +static struct ubifs_pnode *pnode_lookup(struct ubifs_info *c, int i) +{ + int err, h, iip, shft; + struct ubifs_nnode *nnode; + + if (!c->nroot) { + err = ubifs_read_nnode(c, NULL, 0); + if (err) + return ERR_PTR(err); + } + i <<= UBIFS_LPT_FANOUT_SHIFT; + nnode = c->nroot; + shft = c->lpt_hght * UBIFS_LPT_FANOUT_SHIFT; + for (h = 1; h < c->lpt_hght; h++) { + iip = ((i >> shft) & (UBIFS_LPT_FANOUT - 1)); + shft -= UBIFS_LPT_FANOUT_SHIFT; + nnode = ubifs_get_nnode(c, nnode, iip); + if (IS_ERR(nnode)) + return ERR_CAST(nnode); + } + iip = ((i >> shft) & (UBIFS_LPT_FANOUT - 1)); + return ubifs_get_pnode(c, nnode, iip); +} + +/** + * add_pnode_dirt - add dirty space to LPT LEB properties. + * @c: UBIFS file-system description object + * @pnode: pnode for which to add dirt + */ +static void add_pnode_dirt(struct ubifs_info *c, struct ubifs_pnode *pnode) +{ + ubifs_add_lpt_dirt(c, pnode->parent->nbranch[pnode->iip].lnum, + c->pnode_sz); +} + +/** + * do_make_pnode_dirty - mark a pnode dirty. + * @c: UBIFS file-system description object + * @pnode: pnode to mark dirty + */ +static void do_make_pnode_dirty(struct ubifs_info *c, struct ubifs_pnode *pnode) +{ + /* Assumes cnext list is empty i.e. not called during commit */ + if (!test_and_set_bit(DIRTY_CNODE, &pnode->flags)) { + struct ubifs_nnode *nnode; + + c->dirty_pn_cnt += 1; + add_pnode_dirt(c, pnode); + /* Mark parent and ancestors dirty too */ + nnode = pnode->parent; + while (nnode) { + if (!test_and_set_bit(DIRTY_CNODE, &nnode->flags)) { + c->dirty_nn_cnt += 1; + ubifs_add_nnode_dirt(c, nnode); + nnode = nnode->parent; + } else + break; + } + } +} + +/** + * make_tree_dirty - mark the entire LEB properties tree dirty. + * @c: UBIFS file-system description object + * + * This function is used by the "small" LPT model to cause the entire LEB + * properties tree to be written. The "small" LPT model does not use LPT + * garbage collection because it is more efficient to write the entire tree + * (because it is small). + * + * This function returns %0 on success and a negative error code on failure. + */ +static int make_tree_dirty(struct ubifs_info *c) +{ + struct ubifs_pnode *pnode; + + pnode = pnode_lookup(c, 0); + if (IS_ERR(pnode)) + return PTR_ERR(pnode); + + while (pnode) { + do_make_pnode_dirty(c, pnode); + pnode = next_pnode_to_dirty(c, pnode); + if (IS_ERR(pnode)) + return PTR_ERR(pnode); + } + return 0; +} + +/** + * need_write_all - determine if the LPT area is running out of free space. + * @c: UBIFS file-system description object + * + * This function returns %1 if the LPT area is running out of free space and %0 + * if it is not. + */ +static int need_write_all(struct ubifs_info *c) +{ + long long free = 0; + int i; + + for (i = 0; i < c->lpt_lebs; i++) { + if (i + c->lpt_first == c->nhead_lnum) + free += c->leb_size - c->nhead_offs; + else if (c->ltab[i].free == c->leb_size) + free += c->leb_size; + else if (c->ltab[i].free + c->ltab[i].dirty == c->leb_size) + free += c->leb_size; + } + /* Less than twice the size left */ + if (free <= c->lpt_sz * 2) + return 1; + return 0; +} + +/** + * lpt_tgc_start - start trivial garbage collection of LPT LEBs. + * @c: UBIFS file-system description object + * + * LPT trivial garbage collection is where a LPT LEB contains only dirty and + * free space and so may be reused as soon as the next commit is completed. + * This function is called during start commit to mark LPT LEBs for trivial GC. + */ +static void lpt_tgc_start(struct ubifs_info *c) +{ + int i; + + for (i = 0; i < c->lpt_lebs; i++) { + if (i + c->lpt_first == c->nhead_lnum) + continue; + if (c->ltab[i].dirty > 0 && + c->ltab[i].free + c->ltab[i].dirty == c->leb_size) { + c->ltab[i].tgc = 1; + c->ltab[i].free = c->leb_size; + c->ltab[i].dirty = 0; + dbg_lp("LEB %d", i + c->lpt_first); + } + } +} + +/** + * lpt_tgc_end - end trivial garbage collection of LPT LEBs. + * @c: UBIFS file-system description object + * + * LPT trivial garbage collection is where a LPT LEB contains only dirty and + * free space and so may be reused as soon as the next commit is completed. + * This function is called after the commit is completed (master node has been + * written) and un-maps LPT LEBs that were marked for trivial GC. + */ +static int lpt_tgc_end(struct ubifs_info *c) +{ + int i, err; + + for (i = 0; i < c->lpt_lebs; i++) + if (c->ltab[i].tgc) { + err = ubifs_leb_unmap(c, i + c->lpt_first); + if (err) + return err; + c->ltab[i].tgc = 0; + dbg_lp("LEB %d", i + c->lpt_first); + } + return 0; +} + +/** + * populate_lsave - fill the lsave array with important LEB numbers. + * @c: the UBIFS file-system description object + * + * This function is only called for the "big" model. It records a small number + * of LEB numbers of important LEBs. Important LEBs are ones that are (from + * most important to least important): empty, freeable, freeable index, dirty + * index, dirty or free. Upon mount, we read this list of LEB numbers and bring + * their pnodes into memory. That will stop us from having to scan the LPT + * straight away. For the "small" model we assume that scanning the LPT is no + * big deal. + */ +static void populate_lsave(struct ubifs_info *c) +{ + struct ubifs_lprops *lprops; + struct ubifs_lpt_heap *heap; + int i, cnt = 0; + + ubifs_assert(c->big_lpt); + if (!(c->lpt_drty_flgs & LSAVE_DIRTY)) { + c->lpt_drty_flgs |= LSAVE_DIRTY; + ubifs_add_lpt_dirt(c, c->lsave_lnum, c->lsave_sz); + } + +#ifndef __UBOOT__ + if (dbg_populate_lsave(c)) + return; +#endif + + list_for_each_entry(lprops, &c->empty_list, list) { + c->lsave[cnt++] = lprops->lnum; + if (cnt >= c->lsave_cnt) + return; + } + list_for_each_entry(lprops, &c->freeable_list, list) { + c->lsave[cnt++] = lprops->lnum; + if (cnt >= c->lsave_cnt) + return; + } + list_for_each_entry(lprops, &c->frdi_idx_list, list) { + c->lsave[cnt++] = lprops->lnum; + if (cnt >= c->lsave_cnt) + return; + } + heap = &c->lpt_heap[LPROPS_DIRTY_IDX - 1]; + for (i = 0; i < heap->cnt; i++) { + c->lsave[cnt++] = heap->arr[i]->lnum; + if (cnt >= c->lsave_cnt) + return; + } + heap = &c->lpt_heap[LPROPS_DIRTY - 1]; + for (i = 0; i < heap->cnt; i++) { + c->lsave[cnt++] = heap->arr[i]->lnum; + if (cnt >= c->lsave_cnt) + return; + } + heap = &c->lpt_heap[LPROPS_FREE - 1]; + for (i = 0; i < heap->cnt; i++) { + c->lsave[cnt++] = heap->arr[i]->lnum; + if (cnt >= c->lsave_cnt) + return; + } + /* Fill it up completely */ + while (cnt < c->lsave_cnt) + c->lsave[cnt++] = c->main_first; +} + +/** + * nnode_lookup - lookup a nnode in the LPT. + * @c: UBIFS file-system description object + * @i: nnode number + * + * This function returns a pointer to the nnode on success or a negative + * error code on failure. + */ +static struct ubifs_nnode *nnode_lookup(struct ubifs_info *c, int i) +{ + int err, iip; + struct ubifs_nnode *nnode; + + if (!c->nroot) { + err = ubifs_read_nnode(c, NULL, 0); + if (err) + return ERR_PTR(err); + } + nnode = c->nroot; + while (1) { + iip = i & (UBIFS_LPT_FANOUT - 1); + i >>= UBIFS_LPT_FANOUT_SHIFT; + if (!i) + break; + nnode = ubifs_get_nnode(c, nnode, iip); + if (IS_ERR(nnode)) + return nnode; + } + return nnode; +} + +/** + * make_nnode_dirty - find a nnode and, if found, make it dirty. + * @c: UBIFS file-system description object + * @node_num: nnode number of nnode to make dirty + * @lnum: LEB number where nnode was written + * @offs: offset where nnode was written + * + * This function is used by LPT garbage collection. LPT garbage collection is + * used only for the "big" LPT model (c->big_lpt == 1). Garbage collection + * simply involves marking all the nodes in the LEB being garbage-collected as + * dirty. The dirty nodes are written next commit, after which the LEB is free + * to be reused. + * + * This function returns %0 on success and a negative error code on failure. + */ +static int make_nnode_dirty(struct ubifs_info *c, int node_num, int lnum, + int offs) +{ + struct ubifs_nnode *nnode; + + nnode = nnode_lookup(c, node_num); + if (IS_ERR(nnode)) + return PTR_ERR(nnode); + if (nnode->parent) { + struct ubifs_nbranch *branch; + + branch = &nnode->parent->nbranch[nnode->iip]; + if (branch->lnum != lnum || branch->offs != offs) + return 0; /* nnode is obsolete */ + } else if (c->lpt_lnum != lnum || c->lpt_offs != offs) + return 0; /* nnode is obsolete */ + /* Assumes cnext list is empty i.e. not called during commit */ + if (!test_and_set_bit(DIRTY_CNODE, &nnode->flags)) { + c->dirty_nn_cnt += 1; + ubifs_add_nnode_dirt(c, nnode); + /* Mark parent and ancestors dirty too */ + nnode = nnode->parent; + while (nnode) { + if (!test_and_set_bit(DIRTY_CNODE, &nnode->flags)) { + c->dirty_nn_cnt += 1; + ubifs_add_nnode_dirt(c, nnode); + nnode = nnode->parent; + } else + break; + } + } + return 0; +} + +/** + * make_pnode_dirty - find a pnode and, if found, make it dirty. + * @c: UBIFS file-system description object + * @node_num: pnode number of pnode to make dirty + * @lnum: LEB number where pnode was written + * @offs: offset where pnode was written + * + * This function is used by LPT garbage collection. LPT garbage collection is + * used only for the "big" LPT model (c->big_lpt == 1). Garbage collection + * simply involves marking all the nodes in the LEB being garbage-collected as + * dirty. The dirty nodes are written next commit, after which the LEB is free + * to be reused. + * + * This function returns %0 on success and a negative error code on failure. + */ +static int make_pnode_dirty(struct ubifs_info *c, int node_num, int lnum, + int offs) +{ + struct ubifs_pnode *pnode; + struct ubifs_nbranch *branch; + + pnode = pnode_lookup(c, node_num); + if (IS_ERR(pnode)) + return PTR_ERR(pnode); + branch = &pnode->parent->nbranch[pnode->iip]; + if (branch->lnum != lnum || branch->offs != offs) + return 0; + do_make_pnode_dirty(c, pnode); + return 0; +} + +/** + * make_ltab_dirty - make ltab node dirty. + * @c: UBIFS file-system description object + * @lnum: LEB number where ltab was written + * @offs: offset where ltab was written + * + * This function is used by LPT garbage collection. LPT garbage collection is + * used only for the "big" LPT model (c->big_lpt == 1). Garbage collection + * simply involves marking all the nodes in the LEB being garbage-collected as + * dirty. The dirty nodes are written next commit, after which the LEB is free + * to be reused. + * + * This function returns %0 on success and a negative error code on failure. + */ +static int make_ltab_dirty(struct ubifs_info *c, int lnum, int offs) +{ + if (lnum != c->ltab_lnum || offs != c->ltab_offs) + return 0; /* This ltab node is obsolete */ + if (!(c->lpt_drty_flgs & LTAB_DIRTY)) { + c->lpt_drty_flgs |= LTAB_DIRTY; + ubifs_add_lpt_dirt(c, c->ltab_lnum, c->ltab_sz); + } + return 0; +} + +/** + * make_lsave_dirty - make lsave node dirty. + * @c: UBIFS file-system description object + * @lnum: LEB number where lsave was written + * @offs: offset where lsave was written + * + * This function is used by LPT garbage collection. LPT garbage collection is + * used only for the "big" LPT model (c->big_lpt == 1). Garbage collection + * simply involves marking all the nodes in the LEB being garbage-collected as + * dirty. The dirty nodes are written next commit, after which the LEB is free + * to be reused. + * + * This function returns %0 on success and a negative error code on failure. + */ +static int make_lsave_dirty(struct ubifs_info *c, int lnum, int offs) +{ + if (lnum != c->lsave_lnum || offs != c->lsave_offs) + return 0; /* This lsave node is obsolete */ + if (!(c->lpt_drty_flgs & LSAVE_DIRTY)) { + c->lpt_drty_flgs |= LSAVE_DIRTY; + ubifs_add_lpt_dirt(c, c->lsave_lnum, c->lsave_sz); + } + return 0; +} + +/** + * make_node_dirty - make node dirty. + * @c: UBIFS file-system description object + * @node_type: LPT node type + * @node_num: node number + * @lnum: LEB number where node was written + * @offs: offset where node was written + * + * This function is used by LPT garbage collection. LPT garbage collection is + * used only for the "big" LPT model (c->big_lpt == 1). Garbage collection + * simply involves marking all the nodes in the LEB being garbage-collected as + * dirty. The dirty nodes are written next commit, after which the LEB is free + * to be reused. + * + * This function returns %0 on success and a negative error code on failure. + */ +static int make_node_dirty(struct ubifs_info *c, int node_type, int node_num, + int lnum, int offs) +{ + switch (node_type) { + case UBIFS_LPT_NNODE: + return make_nnode_dirty(c, node_num, lnum, offs); + case UBIFS_LPT_PNODE: + return make_pnode_dirty(c, node_num, lnum, offs); + case UBIFS_LPT_LTAB: + return make_ltab_dirty(c, lnum, offs); + case UBIFS_LPT_LSAVE: + return make_lsave_dirty(c, lnum, offs); + } + return -EINVAL; +} + +/** + * get_lpt_node_len - return the length of a node based on its type. + * @c: UBIFS file-system description object + * @node_type: LPT node type + */ +static int get_lpt_node_len(const struct ubifs_info *c, int node_type) +{ + switch (node_type) { + case UBIFS_LPT_NNODE: + return c->nnode_sz; + case UBIFS_LPT_PNODE: + return c->pnode_sz; + case UBIFS_LPT_LTAB: + return c->ltab_sz; + case UBIFS_LPT_LSAVE: + return c->lsave_sz; + } + return 0; +} + +/** + * get_pad_len - return the length of padding in a buffer. + * @c: UBIFS file-system description object + * @buf: buffer + * @len: length of buffer + */ +static int get_pad_len(const struct ubifs_info *c, uint8_t *buf, int len) +{ + int offs, pad_len; + + if (c->min_io_size == 1) + return 0; + offs = c->leb_size - len; + pad_len = ALIGN(offs, c->min_io_size) - offs; + return pad_len; +} + +/** + * get_lpt_node_type - return type (and node number) of a node in a buffer. + * @c: UBIFS file-system description object + * @buf: buffer + * @node_num: node number is returned here + */ +static int get_lpt_node_type(const struct ubifs_info *c, uint8_t *buf, + int *node_num) +{ + uint8_t *addr = buf + UBIFS_LPT_CRC_BYTES; + int pos = 0, node_type; + + node_type = ubifs_unpack_bits(&addr, &pos, UBIFS_LPT_TYPE_BITS); + *node_num = ubifs_unpack_bits(&addr, &pos, c->pcnt_bits); + return node_type; +} + +/** + * is_a_node - determine if a buffer contains a node. + * @c: UBIFS file-system description object + * @buf: buffer + * @len: length of buffer + * + * This function returns %1 if the buffer contains a node or %0 if it does not. + */ +static int is_a_node(const struct ubifs_info *c, uint8_t *buf, int len) +{ + uint8_t *addr = buf + UBIFS_LPT_CRC_BYTES; + int pos = 0, node_type, node_len; + uint16_t crc, calc_crc; + + if (len < UBIFS_LPT_CRC_BYTES + (UBIFS_LPT_TYPE_BITS + 7) / 8) + return 0; + node_type = ubifs_unpack_bits(&addr, &pos, UBIFS_LPT_TYPE_BITS); + if (node_type == UBIFS_LPT_NOT_A_NODE) + return 0; + node_len = get_lpt_node_len(c, node_type); + if (!node_len || node_len > len) + return 0; + pos = 0; + addr = buf; + crc = ubifs_unpack_bits(&addr, &pos, UBIFS_LPT_CRC_BITS); + calc_crc = crc16(-1, buf + UBIFS_LPT_CRC_BYTES, + node_len - UBIFS_LPT_CRC_BYTES); + if (crc != calc_crc) + return 0; + return 1; +} + +/** + * lpt_gc_lnum - garbage collect a LPT LEB. + * @c: UBIFS file-system description object + * @lnum: LEB number to garbage collect + * + * LPT garbage collection is used only for the "big" LPT model + * (c->big_lpt == 1). Garbage collection simply involves marking all the nodes + * in the LEB being garbage-collected as dirty. The dirty nodes are written + * next commit, after which the LEB is free to be reused. + * + * This function returns %0 on success and a negative error code on failure. + */ +static int lpt_gc_lnum(struct ubifs_info *c, int lnum) +{ + int err, len = c->leb_size, node_type, node_num, node_len, offs; + void *buf = c->lpt_buf; + + dbg_lp("LEB %d", lnum); + + err = ubifs_leb_read(c, lnum, buf, 0, c->leb_size, 1); + if (err) + return err; + + while (1) { + if (!is_a_node(c, buf, len)) { + int pad_len; + + pad_len = get_pad_len(c, buf, len); + if (pad_len) { + buf += pad_len; + len -= pad_len; + continue; + } + return 0; + } + node_type = get_lpt_node_type(c, buf, &node_num); + node_len = get_lpt_node_len(c, node_type); + offs = c->leb_size - len; + ubifs_assert(node_len != 0); + mutex_lock(&c->lp_mutex); + err = make_node_dirty(c, node_type, node_num, lnum, offs); + mutex_unlock(&c->lp_mutex); + if (err) + return err; + buf += node_len; + len -= node_len; + } + return 0; +} + +/** + * lpt_gc - LPT garbage collection. + * @c: UBIFS file-system description object + * + * Select a LPT LEB for LPT garbage collection and call 'lpt_gc_lnum()'. + * Returns %0 on success and a negative error code on failure. + */ +static int lpt_gc(struct ubifs_info *c) +{ + int i, lnum = -1, dirty = 0; + + mutex_lock(&c->lp_mutex); + for (i = 0; i < c->lpt_lebs; i++) { + ubifs_assert(!c->ltab[i].tgc); + if (i + c->lpt_first == c->nhead_lnum || + c->ltab[i].free + c->ltab[i].dirty == c->leb_size) + continue; + if (c->ltab[i].dirty > dirty) { + dirty = c->ltab[i].dirty; + lnum = i + c->lpt_first; + } + } + mutex_unlock(&c->lp_mutex); + if (lnum == -1) + return -ENOSPC; + return lpt_gc_lnum(c, lnum); +} + +/** + * ubifs_lpt_start_commit - UBIFS commit starts. + * @c: the UBIFS file-system description object + * + * This function has to be called when UBIFS starts the commit operation. + * This function "freezes" all currently dirty LEB properties and does not + * change them anymore. Further changes are saved and tracked separately + * because they are not part of this commit. This function returns zero in case + * of success and a negative error code in case of failure. + */ +int ubifs_lpt_start_commit(struct ubifs_info *c) +{ + int err, cnt; + + dbg_lp(""); + + mutex_lock(&c->lp_mutex); + err = dbg_chk_lpt_free_spc(c); + if (err) + goto out; + err = dbg_check_ltab(c); + if (err) + goto out; + + if (c->check_lpt_free) { + /* + * We ensure there is enough free space in + * ubifs_lpt_post_commit() by marking nodes dirty. That + * information is lost when we unmount, so we also need + * to check free space once after mounting also. + */ + c->check_lpt_free = 0; + while (need_write_all(c)) { + mutex_unlock(&c->lp_mutex); + err = lpt_gc(c); + if (err) + return err; + mutex_lock(&c->lp_mutex); + } + } + + lpt_tgc_start(c); + + if (!c->dirty_pn_cnt) { + dbg_cmt("no cnodes to commit"); + err = 0; + goto out; + } + + if (!c->big_lpt && need_write_all(c)) { + /* If needed, write everything */ + err = make_tree_dirty(c); + if (err) + goto out; + lpt_tgc_start(c); + } + + if (c->big_lpt) + populate_lsave(c); + + cnt = get_cnodes_to_commit(c); + ubifs_assert(cnt != 0); + + err = layout_cnodes(c); + if (err) + goto out; + + /* Copy the LPT's own lprops for end commit to write */ + memcpy(c->ltab_cmt, c->ltab, + sizeof(struct ubifs_lpt_lprops) * c->lpt_lebs); + c->lpt_drty_flgs &= ~(LTAB_DIRTY | LSAVE_DIRTY); + +out: + mutex_unlock(&c->lp_mutex); + return err; +} + /** * free_obsolete_cnodes - free obsolete cnodes for commit end. * @c: UBIFS file-system description object @@ -50,6 +1314,65 @@ static void free_obsolete_cnodes(struct ubifs_info *c) c->lpt_cnext = NULL; } +#ifndef __UBOOT__ +/** + * ubifs_lpt_end_commit - finish the commit operation. + * @c: the UBIFS file-system description object + * + * This function has to be called when the commit operation finishes. It + * flushes the changes which were "frozen" by 'ubifs_lprops_start_commit()' to + * the media. Returns zero in case of success and a negative error code in case + * of failure. + */ +int ubifs_lpt_end_commit(struct ubifs_info *c) +{ + int err; + + dbg_lp(""); + + if (!c->lpt_cnext) + return 0; + + err = write_cnodes(c); + if (err) + return err; + + mutex_lock(&c->lp_mutex); + free_obsolete_cnodes(c); + mutex_unlock(&c->lp_mutex); + + return 0; +} +#endif + +/** + * ubifs_lpt_post_commit - post commit LPT trivial GC and LPT GC. + * @c: UBIFS file-system description object + * + * LPT trivial GC is completed after a commit. Also LPT GC is done after a + * commit for the "big" LPT model. + */ +int ubifs_lpt_post_commit(struct ubifs_info *c) +{ + int err; + + mutex_lock(&c->lp_mutex); + err = lpt_tgc_end(c); + if (err) + goto out; + if (c->big_lpt) + while (need_write_all(c)) { + mutex_unlock(&c->lp_mutex); + err = lpt_gc(c); + if (err) + return err; + mutex_lock(&c->lp_mutex); + } +out: + mutex_unlock(&c->lp_mutex); + return err; +} + /** * first_nnode - find the first nnode in memory. * @c: UBIFS file-system description object @@ -169,3 +1492,549 @@ void ubifs_lpt_free(struct ubifs_info *c, int wr_only) vfree(c->ltab); kfree(c->lpt_nod_buf); } + +#ifndef __UBOOT__ +/* + * Everything below is related to debugging. + */ + +/** + * dbg_is_all_ff - determine if a buffer contains only 0xFF bytes. + * @buf: buffer + * @len: buffer length + */ +static int dbg_is_all_ff(uint8_t *buf, int len) +{ + int i; + + for (i = 0; i < len; i++) + if (buf[i] != 0xff) + return 0; + return 1; +} + +/** + * dbg_is_nnode_dirty - determine if a nnode is dirty. + * @c: the UBIFS file-system description object + * @lnum: LEB number where nnode was written + * @offs: offset where nnode was written + */ +static int dbg_is_nnode_dirty(struct ubifs_info *c, int lnum, int offs) +{ + struct ubifs_nnode *nnode; + int hght; + + /* Entire tree is in memory so first_nnode / next_nnode are OK */ + nnode = first_nnode(c, &hght); + for (; nnode; nnode = next_nnode(c, nnode, &hght)) { + struct ubifs_nbranch *branch; + + cond_resched(); + if (nnode->parent) { + branch = &nnode->parent->nbranch[nnode->iip]; + if (branch->lnum != lnum || branch->offs != offs) + continue; + if (test_bit(DIRTY_CNODE, &nnode->flags)) + return 1; + return 0; + } else { + if (c->lpt_lnum != lnum || c->lpt_offs != offs) + continue; + if (test_bit(DIRTY_CNODE, &nnode->flags)) + return 1; + return 0; + } + } + return 1; +} + +/** + * dbg_is_pnode_dirty - determine if a pnode is dirty. + * @c: the UBIFS file-system description object + * @lnum: LEB number where pnode was written + * @offs: offset where pnode was written + */ +static int dbg_is_pnode_dirty(struct ubifs_info *c, int lnum, int offs) +{ + int i, cnt; + + cnt = DIV_ROUND_UP(c->main_lebs, UBIFS_LPT_FANOUT); + for (i = 0; i < cnt; i++) { + struct ubifs_pnode *pnode; + struct ubifs_nbranch *branch; + + cond_resched(); + pnode = pnode_lookup(c, i); + if (IS_ERR(pnode)) + return PTR_ERR(pnode); + branch = &pnode->parent->nbranch[pnode->iip]; + if (branch->lnum != lnum || branch->offs != offs) + continue; + if (test_bit(DIRTY_CNODE, &pnode->flags)) + return 1; + return 0; + } + return 1; +} + +/** + * dbg_is_ltab_dirty - determine if a ltab node is dirty. + * @c: the UBIFS file-system description object + * @lnum: LEB number where ltab node was written + * @offs: offset where ltab node was written + */ +static int dbg_is_ltab_dirty(struct ubifs_info *c, int lnum, int offs) +{ + if (lnum != c->ltab_lnum || offs != c->ltab_offs) + return 1; + return (c->lpt_drty_flgs & LTAB_DIRTY) != 0; +} + +/** + * dbg_is_lsave_dirty - determine if a lsave node is dirty. + * @c: the UBIFS file-system description object + * @lnum: LEB number where lsave node was written + * @offs: offset where lsave node was written + */ +static int dbg_is_lsave_dirty(struct ubifs_info *c, int lnum, int offs) +{ + if (lnum != c->lsave_lnum || offs != c->lsave_offs) + return 1; + return (c->lpt_drty_flgs & LSAVE_DIRTY) != 0; +} + +/** + * dbg_is_node_dirty - determine if a node is dirty. + * @c: the UBIFS file-system description object + * @node_type: node type + * @lnum: LEB number where node was written + * @offs: offset where node was written + */ +static int dbg_is_node_dirty(struct ubifs_info *c, int node_type, int lnum, + int offs) +{ + switch (node_type) { + case UBIFS_LPT_NNODE: + return dbg_is_nnode_dirty(c, lnum, offs); + case UBIFS_LPT_PNODE: + return dbg_is_pnode_dirty(c, lnum, offs); + case UBIFS_LPT_LTAB: + return dbg_is_ltab_dirty(c, lnum, offs); + case UBIFS_LPT_LSAVE: + return dbg_is_lsave_dirty(c, lnum, offs); + } + return 1; +} + +/** + * dbg_check_ltab_lnum - check the ltab for a LPT LEB number. + * @c: the UBIFS file-system description object + * @lnum: LEB number where node was written + * @offs: offset where node was written + * + * This function returns %0 on success and a negative error code on failure. + */ +static int dbg_check_ltab_lnum(struct ubifs_info *c, int lnum) +{ + int err, len = c->leb_size, dirty = 0, node_type, node_num, node_len; + int ret; + void *buf, *p; + + if (!dbg_is_chk_lprops(c)) + return 0; + + buf = p = __vmalloc(c->leb_size, GFP_NOFS, PAGE_KERNEL); + if (!buf) { + ubifs_err("cannot allocate memory for ltab checking"); + return 0; + } + + dbg_lp("LEB %d", lnum); + + err = ubifs_leb_read(c, lnum, buf, 0, c->leb_size, 1); + if (err) + goto out; + + while (1) { + if (!is_a_node(c, p, len)) { + int i, pad_len; + + pad_len = get_pad_len(c, p, len); + if (pad_len) { + p += pad_len; + len -= pad_len; + dirty += pad_len; + continue; + } + if (!dbg_is_all_ff(p, len)) { + ubifs_err("invalid empty space in LEB %d at %d", + lnum, c->leb_size - len); + err = -EINVAL; + } + i = lnum - c->lpt_first; + if (len != c->ltab[i].free) { + ubifs_err("invalid free space in LEB %d (free %d, expected %d)", + lnum, len, c->ltab[i].free); + err = -EINVAL; + } + if (dirty != c->ltab[i].dirty) { + ubifs_err("invalid dirty space in LEB %d (dirty %d, expected %d)", + lnum, dirty, c->ltab[i].dirty); + err = -EINVAL; + } + goto out; + } + node_type = get_lpt_node_type(c, p, &node_num); + node_len = get_lpt_node_len(c, node_type); + ret = dbg_is_node_dirty(c, node_type, lnum, c->leb_size - len); + if (ret == 1) + dirty += node_len; + p += node_len; + len -= node_len; + } + + err = 0; +out: + vfree(buf); + return err; +} + +/** + * dbg_check_ltab - check the free and dirty space in the ltab. + * @c: the UBIFS file-system description object + * + * This function returns %0 on success and a negative error code on failure. + */ +int dbg_check_ltab(struct ubifs_info *c) +{ + int lnum, err, i, cnt; + + if (!dbg_is_chk_lprops(c)) + return 0; + + /* Bring the entire tree into memory */ + cnt = DIV_ROUND_UP(c->main_lebs, UBIFS_LPT_FANOUT); + for (i = 0; i < cnt; i++) { + struct ubifs_pnode *pnode; + + pnode = pnode_lookup(c, i); + if (IS_ERR(pnode)) + return PTR_ERR(pnode); + cond_resched(); + } + + /* Check nodes */ + err = dbg_check_lpt_nodes(c, (struct ubifs_cnode *)c->nroot, 0, 0); + if (err) + return err; + + /* Check each LEB */ + for (lnum = c->lpt_first; lnum <= c->lpt_last; lnum++) { + err = dbg_check_ltab_lnum(c, lnum); + if (err) { + ubifs_err("failed at LEB %d", lnum); + return err; + } + } + + dbg_lp("succeeded"); + return 0; +} + +/** + * dbg_chk_lpt_free_spc - check LPT free space is enough to write entire LPT. + * @c: the UBIFS file-system description object + * + * This function returns %0 on success and a negative error code on failure. + */ +int dbg_chk_lpt_free_spc(struct ubifs_info *c) +{ + long long free = 0; + int i; + + if (!dbg_is_chk_lprops(c)) + return 0; + + for (i = 0; i < c->lpt_lebs; i++) { + if (c->ltab[i].tgc || c->ltab[i].cmt) + continue; + if (i + c->lpt_first == c->nhead_lnum) + free += c->leb_size - c->nhead_offs; + else if (c->ltab[i].free == c->leb_size) + free += c->leb_size; + } + if (free < c->lpt_sz) { + ubifs_err("LPT space error: free %lld lpt_sz %lld", + free, c->lpt_sz); + ubifs_dump_lpt_info(c); + ubifs_dump_lpt_lebs(c); + dump_stack(); + return -EINVAL; + } + return 0; +} + +/** + * dbg_chk_lpt_sz - check LPT does not write more than LPT size. + * @c: the UBIFS file-system description object + * @action: what to do + * @len: length written + * + * This function returns %0 on success and a negative error code on failure. + * The @action argument may be one of: + * o %0 - LPT debugging checking starts, initialize debugging variables; + * o %1 - wrote an LPT node, increase LPT size by @len bytes; + * o %2 - switched to a different LEB and wasted @len bytes; + * o %3 - check that we've written the right number of bytes. + * o %4 - wasted @len bytes; + */ +int dbg_chk_lpt_sz(struct ubifs_info *c, int action, int len) +{ + struct ubifs_debug_info *d = c->dbg; + long long chk_lpt_sz, lpt_sz; + int err = 0; + + if (!dbg_is_chk_lprops(c)) + return 0; + + switch (action) { + case 0: + d->chk_lpt_sz = 0; + d->chk_lpt_sz2 = 0; + d->chk_lpt_lebs = 0; + d->chk_lpt_wastage = 0; + if (c->dirty_pn_cnt > c->pnode_cnt) { + ubifs_err("dirty pnodes %d exceed max %d", + c->dirty_pn_cnt, c->pnode_cnt); + err = -EINVAL; + } + if (c->dirty_nn_cnt > c->nnode_cnt) { + ubifs_err("dirty nnodes %d exceed max %d", + c->dirty_nn_cnt, c->nnode_cnt); + err = -EINVAL; + } + return err; + case 1: + d->chk_lpt_sz += len; + return 0; + case 2: + d->chk_lpt_sz += len; + d->chk_lpt_wastage += len; + d->chk_lpt_lebs += 1; + return 0; + case 3: + chk_lpt_sz = c->leb_size; + chk_lpt_sz *= d->chk_lpt_lebs; + chk_lpt_sz += len - c->nhead_offs; + if (d->chk_lpt_sz != chk_lpt_sz) { + ubifs_err("LPT wrote %lld but space used was %lld", + d->chk_lpt_sz, chk_lpt_sz); + err = -EINVAL; + } + if (d->chk_lpt_sz > c->lpt_sz) { + ubifs_err("LPT wrote %lld but lpt_sz is %lld", + d->chk_lpt_sz, c->lpt_sz); + err = -EINVAL; + } + if (d->chk_lpt_sz2 && d->chk_lpt_sz != d->chk_lpt_sz2) { + ubifs_err("LPT layout size %lld but wrote %lld", + d->chk_lpt_sz, d->chk_lpt_sz2); + err = -EINVAL; + } + if (d->chk_lpt_sz2 && d->new_nhead_offs != len) { + ubifs_err("LPT new nhead offs: expected %d was %d", + d->new_nhead_offs, len); + err = -EINVAL; + } + lpt_sz = (long long)c->pnode_cnt * c->pnode_sz; + lpt_sz += (long long)c->nnode_cnt * c->nnode_sz; + lpt_sz += c->ltab_sz; + if (c->big_lpt) + lpt_sz += c->lsave_sz; + if (d->chk_lpt_sz - d->chk_lpt_wastage > lpt_sz) { + ubifs_err("LPT chk_lpt_sz %lld + waste %lld exceeds %lld", + d->chk_lpt_sz, d->chk_lpt_wastage, lpt_sz); + err = -EINVAL; + } + if (err) { + ubifs_dump_lpt_info(c); + ubifs_dump_lpt_lebs(c); + dump_stack(); + } + d->chk_lpt_sz2 = d->chk_lpt_sz; + d->chk_lpt_sz = 0; + d->chk_lpt_wastage = 0; + d->chk_lpt_lebs = 0; + d->new_nhead_offs = len; + return err; + case 4: + d->chk_lpt_sz += len; + d->chk_lpt_wastage += len; + return 0; + default: + return -EINVAL; + } +} + +/** + * ubifs_dump_lpt_leb - dump an LPT LEB. + * @c: UBIFS file-system description object + * @lnum: LEB number to dump + * + * This function dumps an LEB from LPT area. Nodes in this area are very + * different to nodes in the main area (e.g., they do not have common headers, + * they do not have 8-byte alignments, etc), so we have a separate function to + * dump LPT area LEBs. Note, LPT has to be locked by the caller. + */ +static void dump_lpt_leb(const struct ubifs_info *c, int lnum) +{ + int err, len = c->leb_size, node_type, node_num, node_len, offs; + void *buf, *p; + + pr_err("(pid %d) start dumping LEB %d\n", current->pid, lnum); + buf = p = __vmalloc(c->leb_size, GFP_NOFS, PAGE_KERNEL); + if (!buf) { + ubifs_err("cannot allocate memory to dump LPT"); + return; + } + + err = ubifs_leb_read(c, lnum, buf, 0, c->leb_size, 1); + if (err) + goto out; + + while (1) { + offs = c->leb_size - len; + if (!is_a_node(c, p, len)) { + int pad_len; + + pad_len = get_pad_len(c, p, len); + if (pad_len) { + pr_err("LEB %d:%d, pad %d bytes\n", + lnum, offs, pad_len); + p += pad_len; + len -= pad_len; + continue; + } + if (len) + pr_err("LEB %d:%d, free %d bytes\n", + lnum, offs, len); + break; + } + + node_type = get_lpt_node_type(c, p, &node_num); + switch (node_type) { + case UBIFS_LPT_PNODE: + { + node_len = c->pnode_sz; + if (c->big_lpt) + pr_err("LEB %d:%d, pnode num %d\n", + lnum, offs, node_num); + else + pr_err("LEB %d:%d, pnode\n", lnum, offs); + break; + } + case UBIFS_LPT_NNODE: + { + int i; + struct ubifs_nnode nnode; + + node_len = c->nnode_sz; + if (c->big_lpt) + pr_err("LEB %d:%d, nnode num %d, ", + lnum, offs, node_num); + else + pr_err("LEB %d:%d, nnode, ", + lnum, offs); + err = ubifs_unpack_nnode(c, p, &nnode); + for (i = 0; i < UBIFS_LPT_FANOUT; i++) { + pr_cont("%d:%d", nnode.nbranch[i].lnum, + nnode.nbranch[i].offs); + if (i != UBIFS_LPT_FANOUT - 1) + pr_cont(", "); + } + pr_cont("\n"); + break; + } + case UBIFS_LPT_LTAB: + node_len = c->ltab_sz; + pr_err("LEB %d:%d, ltab\n", lnum, offs); + break; + case UBIFS_LPT_LSAVE: + node_len = c->lsave_sz; + pr_err("LEB %d:%d, lsave len\n", lnum, offs); + break; + default: + ubifs_err("LPT node type %d not recognized", node_type); + goto out; + } + + p += node_len; + len -= node_len; + } + + pr_err("(pid %d) finish dumping LEB %d\n", current->pid, lnum); +out: + vfree(buf); + return; +} + +/** + * ubifs_dump_lpt_lebs - dump LPT lebs. + * @c: UBIFS file-system description object + * + * This function dumps all LPT LEBs. The caller has to make sure the LPT is + * locked. + */ +void ubifs_dump_lpt_lebs(const struct ubifs_info *c) +{ + int i; + + pr_err("(pid %d) start dumping all LPT LEBs\n", current->pid); + for (i = 0; i < c->lpt_lebs; i++) + dump_lpt_leb(c, i + c->lpt_first); + pr_err("(pid %d) finish dumping all LPT LEBs\n", current->pid); +} + +/** + * dbg_populate_lsave - debugging version of 'populate_lsave()' + * @c: UBIFS file-system description object + * + * This is a debugging version for 'populate_lsave()' which populates lsave + * with random LEBs instead of useful LEBs, which is good for test coverage. + * Returns zero if lsave has not been populated (this debugging feature is + * disabled) an non-zero if lsave has been populated. + */ +static int dbg_populate_lsave(struct ubifs_info *c) +{ + struct ubifs_lprops *lprops; + struct ubifs_lpt_heap *heap; + int i; + + if (!dbg_is_chk_gen(c)) + return 0; + if (prandom_u32() & 3) + return 0; + + for (i = 0; i < c->lsave_cnt; i++) + c->lsave[i] = c->main_first; + + list_for_each_entry(lprops, &c->empty_list, list) + c->lsave[prandom_u32() % c->lsave_cnt] = lprops->lnum; + list_for_each_entry(lprops, &c->freeable_list, list) + c->lsave[prandom_u32() % c->lsave_cnt] = lprops->lnum; + list_for_each_entry(lprops, &c->frdi_idx_list, list) + c->lsave[prandom_u32() % c->lsave_cnt] = lprops->lnum; + + heap = &c->lpt_heap[LPROPS_DIRTY_IDX - 1]; + for (i = 0; i < heap->cnt; i++) + c->lsave[prandom_u32() % c->lsave_cnt] = heap->arr[i]->lnum; + heap = &c->lpt_heap[LPROPS_DIRTY - 1]; + for (i = 0; i < heap->cnt; i++) + c->lsave[prandom_u32() % c->lsave_cnt] = heap->arr[i]->lnum; + heap = &c->lpt_heap[LPROPS_FREE - 1]; + for (i = 0; i < heap->cnt; i++) + c->lsave[prandom_u32() % c->lsave_cnt] = heap->arr[i]->lnum; + + return 1; +} +#endif diff --git a/fs/ubifs/master.c b/fs/ubifs/master.c index 3f2926e..00ca855 100644 --- a/fs/ubifs/master.c +++ b/fs/ubifs/master.c @@ -3,18 +3,7 @@ * * Copyright (C) 2006-2008 Nokia Corporation. * - * 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. - * - * 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., 51 - * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * SPDX-License-Identifier: GPL-2.0+ * * Authors: Artem Bityutskiy (Битюцкий Артём) * Adrian Hunter @@ -22,14 +11,21 @@ /* This file implements reading and writing the master node */ +#define __UBOOT__ #include "ubifs.h" +#ifdef __UBOOT__ +#include <linux/compat.h> +#include <linux/err.h> +#include <ubi_uboot.h> +#endif /** * scan_for_master - search the valid master node. * @c: UBIFS file-system description object * * This function scans the master node LEBs and search for the latest master - * node. Returns zero in case of success and a negative error code in case of + * node. Returns zero in case of success, %-EUCLEAN if there master area is + * corrupted and requires recovery, and a negative error code in case of * failure. */ static int scan_for_master(struct ubifs_info *c) @@ -40,7 +36,7 @@ static int scan_for_master(struct ubifs_info *c) lnum = UBIFS_MST_LNUM; - sleb = ubifs_scan(c, lnum, 0, c->sbuf); + sleb = ubifs_scan(c, lnum, 0, c->sbuf, 1); if (IS_ERR(sleb)) return PTR_ERR(sleb); nodes_cnt = sleb->nodes_cnt; @@ -48,7 +44,7 @@ static int scan_for_master(struct ubifs_info *c) snod = list_entry(sleb->nodes.prev, struct ubifs_scan_node, list); if (snod->type != UBIFS_MST_NODE) - goto out; + goto out_dump; memcpy(c->mst_node, snod->node, snod->len); offs = snod->offs; } @@ -56,7 +52,7 @@ static int scan_for_master(struct ubifs_info *c) lnum += 1; - sleb = ubifs_scan(c, lnum, 0, c->sbuf); + sleb = ubifs_scan(c, lnum, 0, c->sbuf, 1); if (IS_ERR(sleb)) return PTR_ERR(sleb); if (sleb->nodes_cnt != nodes_cnt) @@ -65,7 +61,7 @@ static int scan_for_master(struct ubifs_info *c) goto out; snod = list_entry(sleb->nodes.prev, struct ubifs_scan_node, list); if (snod->type != UBIFS_MST_NODE) - goto out; + goto out_dump; if (snod->offs != offs) goto out; if (memcmp((void *)c->mst_node + UBIFS_CH_SZ, @@ -78,6 +74,12 @@ static int scan_for_master(struct ubifs_info *c) out: ubifs_scan_destroy(sleb); + return -EUCLEAN; + +out_dump: + ubifs_err("unexpected node type %d master LEB %d:%d", + snod->type, lnum, snod->offs); + ubifs_scan_destroy(sleb); return -EINVAL; } @@ -141,7 +143,7 @@ static int validate_master(const struct ubifs_info *c) } main_sz = (long long)c->main_lebs * c->leb_size; - if (c->old_idx_sz & 7 || c->old_idx_sz >= main_sz) { + if (c->bi.old_idx_sz & 7 || c->bi.old_idx_sz >= main_sz) { err = 9; goto out; } @@ -211,7 +213,7 @@ static int validate_master(const struct ubifs_info *c) } if (c->lst.total_dead + c->lst.total_dark + - c->lst.total_used + c->old_idx_sz > main_sz) { + c->lst.total_used + c->bi.old_idx_sz > main_sz) { err = 21; goto out; } @@ -234,7 +236,7 @@ static int validate_master(const struct ubifs_info *c) out: ubifs_err("bad master node at offset %d error %d", c->mst_offs, err); - dbg_dump_node(c, c->mst_node); + ubifs_dump_node(c, c->mst_node); return -EINVAL; } @@ -256,7 +258,8 @@ int ubifs_read_master(struct ubifs_info *c) err = scan_for_master(c); if (err) { - err = ubifs_recover_master_node(c); + if (err == -EUCLEAN) + err = ubifs_recover_master_node(c); if (err) /* * Note, we do not free 'c->mst_node' here because the @@ -278,7 +281,7 @@ int ubifs_read_master(struct ubifs_info *c) c->gc_lnum = le32_to_cpu(c->mst_node->gc_lnum); c->ihead_lnum = le32_to_cpu(c->mst_node->ihead_lnum); c->ihead_offs = le32_to_cpu(c->mst_node->ihead_offs); - c->old_idx_sz = le64_to_cpu(c->mst_node->index_size); + c->bi.old_idx_sz = le64_to_cpu(c->mst_node->index_size); c->lpt_lnum = le32_to_cpu(c->mst_node->lpt_lnum); c->lpt_offs = le32_to_cpu(c->mst_node->lpt_offs); c->nhead_lnum = le32_to_cpu(c->mst_node->nhead_lnum); @@ -297,7 +300,7 @@ int ubifs_read_master(struct ubifs_info *c) c->lst.total_dead = le64_to_cpu(c->mst_node->total_dead); c->lst.total_dark = le64_to_cpu(c->mst_node->total_dark); - c->calc_idx_sz = c->old_idx_sz; + c->calc_idx_sz = c->bi.old_idx_sz; if (c->mst_node->flags & cpu_to_le32(UBIFS_MST_NO_ORPHS)) c->no_orphs = 1; @@ -309,7 +312,7 @@ int ubifs_read_master(struct ubifs_info *c) if (c->leb_cnt < old_leb_cnt || c->leb_cnt < UBIFS_MIN_LEB_CNT) { ubifs_err("bad leb_cnt on master node"); - dbg_dump_node(c, c->mst_node); + ubifs_dump_node(c, c->mst_node); return -EINVAL; } @@ -335,7 +338,58 @@ int ubifs_read_master(struct ubifs_info *c) if (err) return err; +#ifndef __UBOOT__ err = dbg_old_index_check_init(c, &c->zroot); +#endif + + return err; +} + +#ifndef __UBOOT__ +/** + * ubifs_write_master - write master node. + * @c: UBIFS file-system description object + * + * This function writes the master node. The caller has to take the + * @c->mst_mutex lock before calling this function. Returns zero in case of + * success and a negative error code in case of failure. The master node is + * written twice to enable recovery. + */ +int ubifs_write_master(struct ubifs_info *c) +{ + int err, lnum, offs, len; + + ubifs_assert(!c->ro_media && !c->ro_mount); + if (c->ro_error) + return -EROFS; + + lnum = UBIFS_MST_LNUM; + offs = c->mst_offs + c->mst_node_alsz; + len = UBIFS_MST_NODE_SZ; + + if (offs + UBIFS_MST_NODE_SZ > c->leb_size) { + err = ubifs_leb_unmap(c, lnum); + if (err) + return err; + offs = 0; + } + + c->mst_offs = offs; + c->mst_node->highest_inum = cpu_to_le64(c->highest_inum); + + err = ubifs_write_node(c, c->mst_node, len, lnum, offs); + if (err) + return err; + + lnum += 1; + + if (offs == 0) { + err = ubifs_leb_unmap(c, lnum); + if (err) + return err; + } + err = ubifs_write_node(c, c->mst_node, len, lnum, offs); return err; } +#endif diff --git a/fs/ubifs/misc.h b/fs/ubifs/misc.h index 609232e..4316d3c 100644 --- a/fs/ubifs/misc.h +++ b/fs/ubifs/misc.h @@ -3,18 +3,7 @@ * * Copyright (C) 2006-2008 Nokia Corporation * - * 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. - * - * 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., 51 - * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * SPDX-License-Identifier: GPL-2.0+ * * Authors: Artem Bityutskiy (Битюцкий Артём) * Adrian Hunter @@ -27,6 +16,7 @@ #ifndef __UBIFS_MISC_H__ #define __UBIFS_MISC_H__ +#define __UBOOT__ /** * ubifs_zn_dirty - check if znode is dirty. * @znode: znode to check @@ -39,6 +29,29 @@ static inline int ubifs_zn_dirty(const struct ubifs_znode *znode) } /** + * ubifs_zn_obsolete - check if znode is obsolete. + * @znode: znode to check + * + * This helper function returns %1 if @znode is obsolete and %0 otherwise. + */ +static inline int ubifs_zn_obsolete(const struct ubifs_znode *znode) +{ + return !!test_bit(OBSOLETE_ZNODE, &znode->flags); +} + +/** + * ubifs_zn_cow - check if znode has to be copied on write. + * @znode: znode to check + * + * This helper function returns %1 if @znode is has COW flag set and %0 + * otherwise. + */ +static inline int ubifs_zn_cow(const struct ubifs_znode *znode) +{ + return !!test_bit(COW_ZNODE, &znode->flags); +} + +/** * ubifs_wake_up_bgt - wake up background thread. * @c: UBIFS file-system description object */ @@ -121,82 +134,27 @@ static inline int ubifs_wbuf_sync(struct ubifs_wbuf *wbuf) return err; } +#ifndef __UBOOT__ /** - * ubifs_leb_unmap - unmap an LEB. - * @c: UBIFS file-system description object - * @lnum: LEB number to unmap - * - * This function returns %0 on success and a negative error code on failure. - */ -static inline int ubifs_leb_unmap(const struct ubifs_info *c, int lnum) -{ - int err; - - if (c->ro_media) - return -EROFS; - err = ubi_leb_unmap(c->ubi, lnum); - if (err) { - ubifs_err("unmap LEB %d failed, error %d", lnum, err); - return err; - } - - return 0; -} - -/** - * ubifs_leb_write - write to a LEB. - * @c: UBIFS file-system description object - * @lnum: LEB number to write - * @buf: buffer to write from - * @offs: offset within LEB to write to - * @len: length to write - * @dtype: data type - * - * This function returns %0 on success and a negative error code on failure. - */ -static inline int ubifs_leb_write(const struct ubifs_info *c, int lnum, - const void *buf, int offs, int len, int dtype) -{ - int err; - - if (c->ro_media) - return -EROFS; - err = ubi_leb_write(c->ubi, lnum, buf, offs, len, dtype); - if (err) { - ubifs_err("writing %d bytes at %d:%d, error %d", - len, lnum, offs, err); - return err; - } - - return 0; -} - -/** - * ubifs_leb_change - atomic LEB change. - * @c: UBIFS file-system description object - * @lnum: LEB number to write - * @buf: buffer to write from - * @len: length to write - * @dtype: data type + * ubifs_encode_dev - encode device node IDs. + * @dev: UBIFS device node information + * @rdev: device IDs to encode * - * This function returns %0 on success and a negative error code on failure. + * This is a helper function which encodes major/minor numbers of a device node + * into UBIFS device node description. We use standard Linux "new" and "huge" + * encodings. */ -static inline int ubifs_leb_change(const struct ubifs_info *c, int lnum, - const void *buf, int len, int dtype) +static inline int ubifs_encode_dev(union ubifs_dev_desc *dev, dev_t rdev) { - int err; - - if (c->ro_media) - return -EROFS; - err = ubi_leb_change(c->ubi, lnum, buf, len, dtype); - if (err) { - ubifs_err("changing %d bytes in LEB %d, error %d", - len, lnum, err); - return err; + if (new_valid_dev(rdev)) { + dev->new = cpu_to_le32(new_encode_dev(rdev)); + return sizeof(dev->new); + } else { + dev->huge = cpu_to_le64(huge_encode_dev(rdev)); + return sizeof(dev->huge); } - - return 0; } +#endif /** * ubifs_add_dirt - add dirty space to LEB properties. @@ -260,8 +218,24 @@ struct ubifs_branch *ubifs_idx_branch(const struct ubifs_info *c, static inline void *ubifs_idx_key(const struct ubifs_info *c, const struct ubifs_idx_node *idx) { - const __u8 *branch = idx->branches; - return (void *)((struct ubifs_branch *)branch)->key; +#ifndef __UBOOT__ + return (void *)((struct ubifs_branch *)idx->branches)->key; +#else + struct ubifs_branch *tmp; + + tmp = (struct ubifs_branch *)idx->branches; + return (void *)tmp->key; +#endif +} + +/** + * ubifs_current_time - round current time to time granularity. + * @inode: inode + */ +static inline struct timespec ubifs_current_time(struct inode *inode) +{ + return (inode->i_sb->s_time_gran < NSEC_PER_SEC) ? + current_fs_time(inode->i_sb) : CURRENT_TIME_SEC; } /** @@ -308,4 +282,21 @@ static inline void ubifs_release_lprops(struct ubifs_info *c) mutex_unlock(&c->lp_mutex); } +/** + * ubifs_next_log_lnum - switch to the next log LEB. + * @c: UBIFS file-system description object + * @lnum: current log LEB + * + * This helper function returns the log LEB number which goes next after LEB + * 'lnum'. + */ +static inline int ubifs_next_log_lnum(const struct ubifs_info *c, int lnum) +{ + lnum += 1; + if (lnum > c->log_last) + lnum = UBIFS_LOG_LNUM; + + return lnum; +} + #endif /* __UBIFS_MISC_H__ */ diff --git a/fs/ubifs/orphan.c b/fs/ubifs/orphan.c index d091031..4e42879 100644 --- a/fs/ubifs/orphan.c +++ b/fs/ubifs/orphan.c @@ -3,22 +3,12 @@ * * Copyright (C) 2006-2008 Nokia Corporation. * - * 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. - * - * 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., 51 - * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * SPDX-License-Identifier: GPL-2.0+ * * Author: Adrian Hunter */ +#include <linux/err.h> #include "ubifs.h" /* @@ -52,6 +42,166 @@ * than the maximum number of orphans allowed. */ +static int dbg_check_orphans(struct ubifs_info *c); + +/** + * ubifs_add_orphan - add an orphan. + * @c: UBIFS file-system description object + * @inum: orphan inode number + * + * Add an orphan. This function is called when an inodes link count drops to + * zero. + */ +int ubifs_add_orphan(struct ubifs_info *c, ino_t inum) +{ + struct ubifs_orphan *orphan, *o; + struct rb_node **p, *parent = NULL; + + orphan = kzalloc(sizeof(struct ubifs_orphan), GFP_NOFS); + if (!orphan) + return -ENOMEM; + orphan->inum = inum; + orphan->new = 1; + + spin_lock(&c->orphan_lock); + if (c->tot_orphans >= c->max_orphans) { + spin_unlock(&c->orphan_lock); + kfree(orphan); + return -ENFILE; + } + p = &c->orph_tree.rb_node; + while (*p) { + parent = *p; + o = rb_entry(parent, struct ubifs_orphan, rb); + if (inum < o->inum) + p = &(*p)->rb_left; + else if (inum > o->inum) + p = &(*p)->rb_right; + else { + ubifs_err("orphaned twice"); + spin_unlock(&c->orphan_lock); + kfree(orphan); + return 0; + } + } + c->tot_orphans += 1; + c->new_orphans += 1; + rb_link_node(&orphan->rb, parent, p); + rb_insert_color(&orphan->rb, &c->orph_tree); + list_add_tail(&orphan->list, &c->orph_list); + list_add_tail(&orphan->new_list, &c->orph_new); + spin_unlock(&c->orphan_lock); + dbg_gen("ino %lu", (unsigned long)inum); + return 0; +} + +/** + * ubifs_delete_orphan - delete an orphan. + * @c: UBIFS file-system description object + * @inum: orphan inode number + * + * Delete an orphan. This function is called when an inode is deleted. + */ +void ubifs_delete_orphan(struct ubifs_info *c, ino_t inum) +{ + struct ubifs_orphan *o; + struct rb_node *p; + + spin_lock(&c->orphan_lock); + p = c->orph_tree.rb_node; + while (p) { + o = rb_entry(p, struct ubifs_orphan, rb); + if (inum < o->inum) + p = p->rb_left; + else if (inum > o->inum) + p = p->rb_right; + else { + if (o->del) { + spin_unlock(&c->orphan_lock); + dbg_gen("deleted twice ino %lu", + (unsigned long)inum); + return; + } + if (o->cmt) { + o->del = 1; + o->dnext = c->orph_dnext; + c->orph_dnext = o; + spin_unlock(&c->orphan_lock); + dbg_gen("delete later ino %lu", + (unsigned long)inum); + return; + } + rb_erase(p, &c->orph_tree); + list_del(&o->list); + c->tot_orphans -= 1; + if (o->new) { + list_del(&o->new_list); + c->new_orphans -= 1; + } + spin_unlock(&c->orphan_lock); + kfree(o); + dbg_gen("inum %lu", (unsigned long)inum); + return; + } + } + spin_unlock(&c->orphan_lock); + ubifs_err("missing orphan ino %lu", (unsigned long)inum); + dump_stack(); +} + +/** + * ubifs_orphan_start_commit - start commit of orphans. + * @c: UBIFS file-system description object + * + * Start commit of orphans. + */ +int ubifs_orphan_start_commit(struct ubifs_info *c) +{ + struct ubifs_orphan *orphan, **last; + + spin_lock(&c->orphan_lock); + last = &c->orph_cnext; + list_for_each_entry(orphan, &c->orph_new, new_list) { + ubifs_assert(orphan->new); + ubifs_assert(!orphan->cmt); + orphan->new = 0; + orphan->cmt = 1; + *last = orphan; + last = &orphan->cnext; + } + *last = NULL; + c->cmt_orphans = c->new_orphans; + c->new_orphans = 0; + dbg_cmt("%d orphans to commit", c->cmt_orphans); + INIT_LIST_HEAD(&c->orph_new); + if (c->tot_orphans == 0) + c->no_orphs = 1; + else + c->no_orphs = 0; + spin_unlock(&c->orphan_lock); + return 0; +} + +/** + * avail_orphs - calculate available space. + * @c: UBIFS file-system description object + * + * This function returns the number of orphans that can be written in the + * available space. + */ +static int avail_orphs(struct ubifs_info *c) +{ + int avail_lebs, avail, gap; + + avail_lebs = c->orph_lebs - (c->ohead_lnum - c->orph_first) - 1; + avail = avail_lebs * + ((c->leb_size - UBIFS_ORPH_NODE_SZ) / sizeof(__le64)); + gap = c->leb_size - c->ohead_offs; + if (gap >= UBIFS_ORPH_NODE_SZ + sizeof(__le64)) + avail += (gap - UBIFS_ORPH_NODE_SZ) / sizeof(__le64); + return avail; +} + /** * tot_avail_orphs - calculate total space. * @c: UBIFS file-system description object @@ -70,6 +220,256 @@ static int tot_avail_orphs(struct ubifs_info *c) } /** + * do_write_orph_node - write a node to the orphan head. + * @c: UBIFS file-system description object + * @len: length of node + * @atomic: write atomically + * + * This function writes a node to the orphan head from the orphan buffer. If + * %atomic is not zero, then the write is done atomically. On success, %0 is + * returned, otherwise a negative error code is returned. + */ +static int do_write_orph_node(struct ubifs_info *c, int len, int atomic) +{ + int err = 0; + + if (atomic) { + ubifs_assert(c->ohead_offs == 0); + ubifs_prepare_node(c, c->orph_buf, len, 1); + len = ALIGN(len, c->min_io_size); + err = ubifs_leb_change(c, c->ohead_lnum, c->orph_buf, len); + } else { + if (c->ohead_offs == 0) { + /* Ensure LEB has been unmapped */ + err = ubifs_leb_unmap(c, c->ohead_lnum); + if (err) + return err; + } + err = ubifs_write_node(c, c->orph_buf, len, c->ohead_lnum, + c->ohead_offs); + } + return err; +} + +/** + * write_orph_node - write an orphan node. + * @c: UBIFS file-system description object + * @atomic: write atomically + * + * This function builds an orphan node from the cnext list and writes it to the + * orphan head. On success, %0 is returned, otherwise a negative error code + * is returned. + */ +static int write_orph_node(struct ubifs_info *c, int atomic) +{ + struct ubifs_orphan *orphan, *cnext; + struct ubifs_orph_node *orph; + int gap, err, len, cnt, i; + + ubifs_assert(c->cmt_orphans > 0); + gap = c->leb_size - c->ohead_offs; + if (gap < UBIFS_ORPH_NODE_SZ + sizeof(__le64)) { + c->ohead_lnum += 1; + c->ohead_offs = 0; + gap = c->leb_size; + if (c->ohead_lnum > c->orph_last) { + /* + * We limit the number of orphans so that this should + * never happen. + */ + ubifs_err("out of space in orphan area"); + return -EINVAL; + } + } + cnt = (gap - UBIFS_ORPH_NODE_SZ) / sizeof(__le64); + if (cnt > c->cmt_orphans) + cnt = c->cmt_orphans; + len = UBIFS_ORPH_NODE_SZ + cnt * sizeof(__le64); + ubifs_assert(c->orph_buf); + orph = c->orph_buf; + orph->ch.node_type = UBIFS_ORPH_NODE; + spin_lock(&c->orphan_lock); + cnext = c->orph_cnext; + for (i = 0; i < cnt; i++) { + orphan = cnext; + ubifs_assert(orphan->cmt); + orph->inos[i] = cpu_to_le64(orphan->inum); + orphan->cmt = 0; + cnext = orphan->cnext; + orphan->cnext = NULL; + } + c->orph_cnext = cnext; + c->cmt_orphans -= cnt; + spin_unlock(&c->orphan_lock); + if (c->cmt_orphans) + orph->cmt_no = cpu_to_le64(c->cmt_no); + else + /* Mark the last node of the commit */ + orph->cmt_no = cpu_to_le64((c->cmt_no) | (1ULL << 63)); + ubifs_assert(c->ohead_offs + len <= c->leb_size); + ubifs_assert(c->ohead_lnum >= c->orph_first); + ubifs_assert(c->ohead_lnum <= c->orph_last); + err = do_write_orph_node(c, len, atomic); + c->ohead_offs += ALIGN(len, c->min_io_size); + c->ohead_offs = ALIGN(c->ohead_offs, 8); + return err; +} + +/** + * write_orph_nodes - write orphan nodes until there are no more to commit. + * @c: UBIFS file-system description object + * @atomic: write atomically + * + * This function writes orphan nodes for all the orphans to commit. On success, + * %0 is returned, otherwise a negative error code is returned. + */ +static int write_orph_nodes(struct ubifs_info *c, int atomic) +{ + int err; + + while (c->cmt_orphans > 0) { + err = write_orph_node(c, atomic); + if (err) + return err; + } + if (atomic) { + int lnum; + + /* Unmap any unused LEBs after consolidation */ + lnum = c->ohead_lnum + 1; + for (lnum = c->ohead_lnum + 1; lnum <= c->orph_last; lnum++) { + err = ubifs_leb_unmap(c, lnum); + if (err) + return err; + } + } + return 0; +} + +/** + * consolidate - consolidate the orphan area. + * @c: UBIFS file-system description object + * + * This function enables consolidation by putting all the orphans into the list + * to commit. The list is in the order that the orphans were added, and the + * LEBs are written atomically in order, so at no time can orphans be lost by + * an unclean unmount. + * + * This function returns %0 on success and a negative error code on failure. + */ +static int consolidate(struct ubifs_info *c) +{ + int tot_avail = tot_avail_orphs(c), err = 0; + + spin_lock(&c->orphan_lock); + dbg_cmt("there is space for %d orphans and there are %d", + tot_avail, c->tot_orphans); + if (c->tot_orphans - c->new_orphans <= tot_avail) { + struct ubifs_orphan *orphan, **last; + int cnt = 0; + + /* Change the cnext list to include all non-new orphans */ + last = &c->orph_cnext; + list_for_each_entry(orphan, &c->orph_list, list) { + if (orphan->new) + continue; + orphan->cmt = 1; + *last = orphan; + last = &orphan->cnext; + cnt += 1; + } + *last = NULL; + ubifs_assert(cnt == c->tot_orphans - c->new_orphans); + c->cmt_orphans = cnt; + c->ohead_lnum = c->orph_first; + c->ohead_offs = 0; + } else { + /* + * We limit the number of orphans so that this should + * never happen. + */ + ubifs_err("out of space in orphan area"); + err = -EINVAL; + } + spin_unlock(&c->orphan_lock); + return err; +} + +/** + * commit_orphans - commit orphans. + * @c: UBIFS file-system description object + * + * This function commits orphans to flash. On success, %0 is returned, + * otherwise a negative error code is returned. + */ +static int commit_orphans(struct ubifs_info *c) +{ + int avail, atomic = 0, err; + + ubifs_assert(c->cmt_orphans > 0); + avail = avail_orphs(c); + if (avail < c->cmt_orphans) { + /* Not enough space to write new orphans, so consolidate */ + err = consolidate(c); + if (err) + return err; + atomic = 1; + } + err = write_orph_nodes(c, atomic); + return err; +} + +/** + * erase_deleted - erase the orphans marked for deletion. + * @c: UBIFS file-system description object + * + * During commit, the orphans being committed cannot be deleted, so they are + * marked for deletion and deleted by this function. Also, the recovery + * adds killed orphans to the deletion list, and therefore they are deleted + * here too. + */ +static void erase_deleted(struct ubifs_info *c) +{ + struct ubifs_orphan *orphan, *dnext; + + spin_lock(&c->orphan_lock); + dnext = c->orph_dnext; + while (dnext) { + orphan = dnext; + dnext = orphan->dnext; + ubifs_assert(!orphan->new); + ubifs_assert(orphan->del); + rb_erase(&orphan->rb, &c->orph_tree); + list_del(&orphan->list); + c->tot_orphans -= 1; + dbg_gen("deleting orphan ino %lu", (unsigned long)orphan->inum); + kfree(orphan); + } + c->orph_dnext = NULL; + spin_unlock(&c->orphan_lock); +} + +/** + * ubifs_orphan_end_commit - end commit of orphans. + * @c: UBIFS file-system description object + * + * End commit of orphans. + */ +int ubifs_orphan_end_commit(struct ubifs_info *c) +{ + int err; + + if (c->cmt_orphans != 0) { + err = commit_orphans(c); + if (err) + return err; + } + erase_deleted(c); + err = dbg_check_orphans(c); + return err; +} + +/** * ubifs_clear_orphans - erase all LEBs used for orphans. * @c: UBIFS file-system description object * @@ -128,6 +528,7 @@ static int insert_dead_orphan(struct ubifs_info *c, ino_t inum) rb_link_node(&orphan->rb, parent, p); rb_insert_color(&orphan->rb, &c->orph_tree); list_add_tail(&orphan->list, &c->orph_list); + orphan->del = 1; orphan->dnext = c->orph_dnext; c->orph_dnext = orphan; dbg_mnt("ino %lu, new %d, tot %d", (unsigned long)inum, @@ -159,9 +560,9 @@ static int do_kill_orphans(struct ubifs_info *c, struct ubifs_scan_leb *sleb, list_for_each_entry(snod, &sleb->nodes, list) { if (snod->type != UBIFS_ORPH_NODE) { - ubifs_err("invalid node type %d in orphan area at " - "%d:%d", snod->type, sleb->lnum, snod->offs); - dbg_dump_node(c, snod->node); + ubifs_err("invalid node type %d in orphan area at %d:%d", + snod->type, sleb->lnum, snod->offs); + ubifs_dump_node(c, snod->node); return -EINVAL; } @@ -186,10 +587,9 @@ static int do_kill_orphans(struct ubifs_info *c, struct ubifs_scan_leb *sleb, * number. That makes this orphan node, out of date. */ if (!first) { - ubifs_err("out of order commit number %llu in " - "orphan node at %d:%d", + ubifs_err("out of order commit number %llu in orphan node at %d:%d", cmt_no, sleb->lnum, snod->offs); - dbg_dump_node(c, snod->node); + ubifs_dump_node(c, snod->node); return -EINVAL; } dbg_rcvry("out of date LEB %d", sleb->lnum); @@ -262,9 +662,11 @@ static int kill_orphans(struct ubifs_info *c) struct ubifs_scan_leb *sleb; dbg_rcvry("LEB %d", lnum); - sleb = ubifs_scan(c, lnum, 0, c->sbuf); + sleb = ubifs_scan(c, lnum, 0, c->sbuf, 1); if (IS_ERR(sleb)) { - sleb = ubifs_recover_leb(c, lnum, 0, c->sbuf, 0); + if (PTR_ERR(sleb) == -EUCLEAN) + sleb = ubifs_recover_leb(c, lnum, 0, + c->sbuf, -1); if (IS_ERR(sleb)) { err = PTR_ERR(sleb); break; @@ -314,3 +716,232 @@ int ubifs_mount_orphans(struct ubifs_info *c, int unclean, int read_only) return err; } + +/* + * Everything below is related to debugging. + */ + +struct check_orphan { + struct rb_node rb; + ino_t inum; +}; + +struct check_info { + unsigned long last_ino; + unsigned long tot_inos; + unsigned long missing; + unsigned long long leaf_cnt; + struct ubifs_ino_node *node; + struct rb_root root; +}; + +static int dbg_find_orphan(struct ubifs_info *c, ino_t inum) +{ + struct ubifs_orphan *o; + struct rb_node *p; + + spin_lock(&c->orphan_lock); + p = c->orph_tree.rb_node; + while (p) { + o = rb_entry(p, struct ubifs_orphan, rb); + if (inum < o->inum) + p = p->rb_left; + else if (inum > o->inum) + p = p->rb_right; + else { + spin_unlock(&c->orphan_lock); + return 1; + } + } + spin_unlock(&c->orphan_lock); + return 0; +} + +static int dbg_ins_check_orphan(struct rb_root *root, ino_t inum) +{ + struct check_orphan *orphan, *o; + struct rb_node **p, *parent = NULL; + + orphan = kzalloc(sizeof(struct check_orphan), GFP_NOFS); + if (!orphan) + return -ENOMEM; + orphan->inum = inum; + + p = &root->rb_node; + while (*p) { + parent = *p; + o = rb_entry(parent, struct check_orphan, rb); + if (inum < o->inum) + p = &(*p)->rb_left; + else if (inum > o->inum) + p = &(*p)->rb_right; + else { + kfree(orphan); + return 0; + } + } + rb_link_node(&orphan->rb, parent, p); + rb_insert_color(&orphan->rb, root); + return 0; +} + +static int dbg_find_check_orphan(struct rb_root *root, ino_t inum) +{ + struct check_orphan *o; + struct rb_node *p; + + p = root->rb_node; + while (p) { + o = rb_entry(p, struct check_orphan, rb); + if (inum < o->inum) + p = p->rb_left; + else if (inum > o->inum) + p = p->rb_right; + else + return 1; + } + return 0; +} + +static void dbg_free_check_tree(struct rb_root *root) +{ + struct check_orphan *o, *n; + + rbtree_postorder_for_each_entry_safe(o, n, root, rb) + kfree(o); +} + +static int dbg_orphan_check(struct ubifs_info *c, struct ubifs_zbranch *zbr, + void *priv) +{ + struct check_info *ci = priv; + ino_t inum; + int err; + + inum = key_inum(c, &zbr->key); + if (inum != ci->last_ino) { + /* Lowest node type is the inode node, so it comes first */ + if (key_type(c, &zbr->key) != UBIFS_INO_KEY) + ubifs_err("found orphan node ino %lu, type %d", + (unsigned long)inum, key_type(c, &zbr->key)); + ci->last_ino = inum; + ci->tot_inos += 1; + err = ubifs_tnc_read_node(c, zbr, ci->node); + if (err) { + ubifs_err("node read failed, error %d", err); + return err; + } + if (ci->node->nlink == 0) + /* Must be recorded as an orphan */ + if (!dbg_find_check_orphan(&ci->root, inum) && + !dbg_find_orphan(c, inum)) { + ubifs_err("missing orphan, ino %lu", + (unsigned long)inum); + ci->missing += 1; + } + } + ci->leaf_cnt += 1; + return 0; +} + +static int dbg_read_orphans(struct check_info *ci, struct ubifs_scan_leb *sleb) +{ + struct ubifs_scan_node *snod; + struct ubifs_orph_node *orph; + ino_t inum; + int i, n, err; + + list_for_each_entry(snod, &sleb->nodes, list) { + cond_resched(); + if (snod->type != UBIFS_ORPH_NODE) + continue; + orph = snod->node; + n = (le32_to_cpu(orph->ch.len) - UBIFS_ORPH_NODE_SZ) >> 3; + for (i = 0; i < n; i++) { + inum = le64_to_cpu(orph->inos[i]); + err = dbg_ins_check_orphan(&ci->root, inum); + if (err) + return err; + } + } + return 0; +} + +static int dbg_scan_orphans(struct ubifs_info *c, struct check_info *ci) +{ + int lnum, err = 0; + void *buf; + + /* Check no-orphans flag and skip this if no orphans */ + if (c->no_orphs) + return 0; + + buf = __vmalloc(c->leb_size, GFP_NOFS, PAGE_KERNEL); + if (!buf) { + ubifs_err("cannot allocate memory to check orphans"); + return 0; + } + + for (lnum = c->orph_first; lnum <= c->orph_last; lnum++) { + struct ubifs_scan_leb *sleb; + + sleb = ubifs_scan(c, lnum, 0, buf, 0); + if (IS_ERR(sleb)) { + err = PTR_ERR(sleb); + break; + } + + err = dbg_read_orphans(ci, sleb); + ubifs_scan_destroy(sleb); + if (err) + break; + } + + vfree(buf); + return err; +} + +static int dbg_check_orphans(struct ubifs_info *c) +{ + struct check_info ci; + int err; + + if (!dbg_is_chk_orph(c)) + return 0; + + ci.last_ino = 0; + ci.tot_inos = 0; + ci.missing = 0; + ci.leaf_cnt = 0; + ci.root = RB_ROOT; + ci.node = kmalloc(UBIFS_MAX_INO_NODE_SZ, GFP_NOFS); + if (!ci.node) { + ubifs_err("out of memory"); + return -ENOMEM; + } + + err = dbg_scan_orphans(c, &ci); + if (err) + goto out; + + err = dbg_walk_index(c, &dbg_orphan_check, NULL, &ci); + if (err) { + ubifs_err("cannot scan TNC, error %d", err); + goto out; + } + + if (ci.missing) { + ubifs_err("%lu missing orphan(s)", ci.missing); + err = -EINVAL; + goto out; + } + + dbg_cmt("last inode number is %lu", ci.last_ino); + dbg_cmt("total number of inodes is %lu", ci.tot_inos); + dbg_cmt("total number of leaf nodes is %llu", ci.leaf_cnt); + +out: + dbg_free_check_tree(&ci.root); + kfree(ci.node); + return err; +} diff --git a/fs/ubifs/recovery.c b/fs/ubifs/recovery.c index 7444650..f54a440 100644 --- a/fs/ubifs/recovery.c +++ b/fs/ubifs/recovery.c @@ -3,18 +3,7 @@ * * Copyright (C) 2006-2008 Nokia Corporation * - * 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. - * - * 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., 51 - * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * SPDX-License-Identifier: GPL-2.0+ * * Authors: Adrian Hunter * Artem Bityutskiy (Битюцкий Артём) @@ -23,13 +12,37 @@ /* * This file implements functions needed to recover from unclean un-mounts. * When UBIFS is mounted, it checks a flag on the master node to determine if - * an un-mount was completed sucessfully. If not, the process of mounting - * incorparates additional checking and fixing of on-flash data structures. + * an un-mount was completed successfully. If not, the process of mounting + * incorporates additional checking and fixing of on-flash data structures. * UBIFS always cleans away all remnants of an unclean un-mount, so that * errors do not accumulate. However UBIFS defers recovery if it is mounted * read-only, and the flash is not modified in that case. + * + * The general UBIFS approach to the recovery is that it recovers from + * corruptions which could be caused by power cuts, but it refuses to recover + * from corruption caused by other reasons. And UBIFS tries to distinguish + * between these 2 reasons of corruptions and silently recover in the former + * case and loudly complain in the latter case. + * + * UBIFS writes only to erased LEBs, so it writes only to the flash space + * containing only 0xFFs. UBIFS also always writes strictly from the beginning + * of the LEB to the end. And UBIFS assumes that the underlying flash media + * writes in @c->max_write_size bytes at a time. + * + * Hence, if UBIFS finds a corrupted node at offset X, it expects only the min. + * I/O unit corresponding to offset X to contain corrupted data, all the + * following min. I/O units have to contain empty space (all 0xFFs). If this is + * not true, the corruption cannot be the result of a power cut, and UBIFS + * refuses to mount. */ +#define __UBOOT__ +#ifndef __UBOOT__ +#include <linux/crc32.h> +#include <linux/slab.h> +#else +#include <linux/err.h> +#endif #include "ubifs.h" /** @@ -52,6 +65,25 @@ static int is_empty(void *buf, int len) } /** + * first_non_ff - find offset of the first non-0xff byte. + * @buf: buffer to search in + * @len: length of buffer + * + * This function returns offset of the first non-0xff byte in @buf or %-1 if + * the buffer contains only 0xff bytes. + */ +static int first_non_ff(void *buf, int len) +{ + uint8_t *p = buf; + int i; + + for (i = 0; i < len; i++) + if (*p++ != 0xff) + return i; + return -1; +} + +/** * get_master_node - get the last valid master node allowing for corruption. * @c: UBIFS file-system description object * @lnum: LEB number @@ -79,7 +111,7 @@ static int get_master_node(const struct ubifs_info *c, int lnum, void **pbuf, if (!sbuf) return -ENOMEM; - err = ubi_read(c->ubi, lnum, sbuf, 0, c->leb_size); + err = ubifs_leb_read(c, lnum, sbuf, 0, c->leb_size, 0); if (err && err != -EBADMSG) goto out_free; @@ -175,10 +207,10 @@ static int write_rcvrd_mst_node(struct ubifs_info *c, mst->flags |= cpu_to_le32(UBIFS_MST_RCVRY); ubifs_prepare_node(c, mst, UBIFS_MST_NODE_SZ, 1); - err = ubi_leb_change(c->ubi, lnum, mst, sz, UBI_SHORTTERM); + err = ubifs_leb_change(c, lnum, mst, sz); if (err) goto out; - err = ubi_leb_change(c->ubi, lnum + 1, mst, sz, UBI_SHORTTERM); + err = ubifs_leb_change(c, lnum + 1, mst, sz); if (err) goto out; out: @@ -236,7 +268,8 @@ int ubifs_recover_master_node(struct ubifs_info *c) if (cor1) goto out_err; mst = mst1; - } else if (offs1 == 0 && offs2 + sz >= c->leb_size) { + } else if (offs1 == 0 && + c->leb_size - offs2 - sz < sz) { /* 1st LEB was unmapped and written, 2nd not */ if (cor1) goto out_err; @@ -266,12 +299,12 @@ int ubifs_recover_master_node(struct ubifs_info *c) mst = mst2; } - dbg_rcvry("recovered master node from LEB %d", + ubifs_msg("recovered master node from LEB %d", (mst == mst1 ? UBIFS_MST_LNUM : UBIFS_MST_LNUM + 1)); memcpy(c->mst_node, mst, UBIFS_MST_NODE_SZ); - if ((c->vfs_sb->s_flags & MS_RDONLY)) { + if (c->ro_mount) { /* Read-only mode. Keep a copy for switching to rw mode */ c->rcvrd_mst_node = kmalloc(sz, GFP_KERNEL); if (!c->rcvrd_mst_node) { @@ -279,6 +312,40 @@ int ubifs_recover_master_node(struct ubifs_info *c) goto out_free; } memcpy(c->rcvrd_mst_node, c->mst_node, UBIFS_MST_NODE_SZ); + + /* + * We had to recover the master node, which means there was an + * unclean reboot. However, it is possible that the master node + * is clean at this point, i.e., %UBIFS_MST_DIRTY is not set. + * E.g., consider the following chain of events: + * + * 1. UBIFS was cleanly unmounted, so the master node is clean + * 2. UBIFS is being mounted R/W and starts changing the master + * node in the first (%UBIFS_MST_LNUM). A power cut happens, + * so this LEB ends up with some amount of garbage at the + * end. + * 3. UBIFS is being mounted R/O. We reach this place and + * recover the master node from the second LEB + * (%UBIFS_MST_LNUM + 1). But we cannot update the media + * because we are being mounted R/O. We have to defer the + * operation. + * 4. However, this master node (@c->mst_node) is marked as + * clean (since the step 1). And if we just return, the + * mount code will be confused and won't recover the master + * node when it is re-mounter R/W later. + * + * Thus, to force the recovery by marking the master node as + * dirty. + */ + c->mst_node->flags |= cpu_to_le32(UBIFS_MST_DIRTY); +#ifndef __UBOOT__ + } else { + /* Write the recovered master node */ + c->max_sqnum = le64_to_cpu(mst->ch.sqnum) - 1; + err = write_rcvrd_mst_node(c, c->mst_node); + if (err) + goto out_free; +#endif } vfree(buf2); @@ -291,12 +358,12 @@ out_err: out_free: ubifs_err("failed to recover master node"); if (mst1) { - dbg_err("dumping first master node"); - dbg_dump_node(c, mst1); + ubifs_err("dumping first master node"); + ubifs_dump_node(c, mst1); } if (mst2) { - dbg_err("dumping second master node"); - dbg_dump_node(c, mst2); + ubifs_err("dumping second master node"); + ubifs_dump_node(c, mst2); } vfree(buf2); vfree(buf1); @@ -335,44 +402,23 @@ int ubifs_write_rcvrd_mst_node(struct ubifs_info *c) * @offs: offset to check * * This function returns %1 if @offs was in the last write to the LEB whose data - * is in @buf, otherwise %0 is returned. The determination is made by checking - * for subsequent empty space starting from the next min_io_size boundary (or a - * bit less than the common header size if min_io_size is one). + * is in @buf, otherwise %0 is returned. The determination is made by checking + * for subsequent empty space starting from the next @c->max_write_size + * boundary. */ static int is_last_write(const struct ubifs_info *c, void *buf, int offs) { - int empty_offs; - int check_len; + int empty_offs, check_len; uint8_t *p; - if (c->min_io_size == 1) { - check_len = c->leb_size - offs; - p = buf + check_len; - for (; check_len > 0; check_len--) - if (*--p != 0xff) - break; - /* - * 'check_len' is the size of the corruption which cannot be - * more than the size of 1 node if it was caused by an unclean - * unmount. - */ - if (check_len > UBIFS_MAX_NODE_SZ) - return 0; - return 1; - } - /* - * Round up to the next c->min_io_size boundary i.e. 'offs' is in the - * last wbuf written. After that should be empty space. + * Round up to the next @c->max_write_size boundary i.e. @offs is in + * the last wbuf written. After that should be empty space. */ - empty_offs = ALIGN(offs + 1, c->min_io_size); + empty_offs = ALIGN(offs + 1, c->max_write_size); check_len = c->leb_size - empty_offs; p = buf + empty_offs - offs; - - for (; check_len > 0; check_len--) - if (*p++ != 0xff) - return 0; - return 1; + return is_empty(p, check_len); } /** @@ -385,7 +431,7 @@ static int is_last_write(const struct ubifs_info *c, void *buf, int offs) * * This function pads up to the next min_io_size boundary (if there is one) and * sets empty space to all 0xff. @buf, @offs and @len are updated to the next - * min_io_size boundary (if there is one). + * @c->min_io_size boundary. */ static void clean_buf(const struct ubifs_info *c, void **buf, int lnum, int *offs, int *len) @@ -395,11 +441,6 @@ static void clean_buf(const struct ubifs_info *c, void **buf, int lnum, lnum = lnum; dbg_rcvry("cleaning corruption at %d:%d", lnum, *offs); - if (c->min_io_size == 1) { - memset(*buf, 0xff, c->leb_size - *offs); - return; - } - ubifs_assert(!(*offs & 7)); empty_offs = ALIGN(*offs, c->min_io_size); pad_len = empty_offs - *offs; @@ -429,7 +470,7 @@ static int no_more_nodes(const struct ubifs_info *c, void *buf, int len, int skip, dlen = le32_to_cpu(ch->len); /* Check for empty space after the corrupt node's common header */ - skip = ALIGN(offs + UBIFS_CH_SZ, c->min_io_size) - offs; + skip = ALIGN(offs + UBIFS_CH_SZ, c->max_write_size) - offs; if (is_empty(buf + skip, len - skip)) return 1; /* @@ -441,7 +482,7 @@ static int no_more_nodes(const struct ubifs_info *c, void *buf, int len, return 0; } /* Now we know the corrupt node's length we can skip over it */ - skip = ALIGN(offs + dlen, c->min_io_size) - offs; + skip = ALIGN(offs + dlen, c->max_write_size) - offs; /* After which there should be empty space */ if (is_empty(buf + skip, len - skip)) return 1; @@ -469,7 +510,7 @@ static int fix_unclean_leb(struct ubifs_info *c, struct ubifs_scan_leb *sleb, endpt = snod->offs + snod->len; } - if ((c->vfs_sb->s_flags & MS_RDONLY) && !c->remounting_rw) { + if (c->ro_mount && !c->remounting_rw) { /* Add to recovery list */ struct ubifs_unclean_leb *ucleb; @@ -481,21 +522,55 @@ static int fix_unclean_leb(struct ubifs_info *c, struct ubifs_scan_leb *sleb, ucleb->lnum = lnum; ucleb->endpt = endpt; list_add_tail(&ucleb->list, &c->unclean_leb_list); +#ifndef __UBOOT__ + } else { + /* Write the fixed LEB back to flash */ + int err; + + dbg_rcvry("fixing LEB %d start %d endpt %d", + lnum, start, sleb->endpt); + if (endpt == 0) { + err = ubifs_leb_unmap(c, lnum); + if (err) + return err; + } else { + int len = ALIGN(endpt, c->min_io_size); + + if (start) { + err = ubifs_leb_read(c, lnum, sleb->buf, 0, + start, 1); + if (err) + return err; + } + /* Pad to min_io_size */ + if (len > endpt) { + int pad_len = len - ALIGN(endpt, 8); + + if (pad_len > 0) { + void *buf = sleb->buf + len - pad_len; + + ubifs_pad(c, buf, pad_len); + } + } + err = ubifs_leb_change(c, lnum, sleb->buf, len); + if (err) + return err; + } +#endif } return 0; } /** - * drop_incomplete_group - drop nodes from an incomplete group. + * drop_last_group - drop the last group of nodes. * @sleb: scanned LEB information * @offs: offset of dropped nodes is returned here * - * This function returns %1 if nodes are dropped and %0 otherwise. + * This is a helper function for 'ubifs_recover_leb()' which drops the last + * group of nodes of the scanned LEB. */ -static int drop_incomplete_group(struct ubifs_scan_leb *sleb, int *offs) +static void drop_last_group(struct ubifs_scan_leb *sleb, int *offs) { - int dropped = 0; - while (!list_empty(&sleb->nodes)) { struct ubifs_scan_node *snod; struct ubifs_ch *ch; @@ -504,15 +579,41 @@ static int drop_incomplete_group(struct ubifs_scan_leb *sleb, int *offs) list); ch = snod->node; if (ch->group_type != UBIFS_IN_NODE_GROUP) - return dropped; - dbg_rcvry("dropping node at %d:%d", sleb->lnum, snod->offs); + break; + + dbg_rcvry("dropping grouped node at %d:%d", + sleb->lnum, snod->offs); + *offs = snod->offs; + list_del(&snod->list); + kfree(snod); + sleb->nodes_cnt -= 1; + } +} + +/** + * drop_last_node - drop the last node. + * @sleb: scanned LEB information + * @offs: offset of dropped nodes is returned here + * @grouped: non-zero if whole group of nodes have to be dropped + * + * This is a helper function for 'ubifs_recover_leb()' which drops the last + * node of the scanned LEB. + */ +static void drop_last_node(struct ubifs_scan_leb *sleb, int *offs) +{ + struct ubifs_scan_node *snod; + + if (!list_empty(&sleb->nodes)) { + snod = list_entry(sleb->nodes.prev, struct ubifs_scan_node, + list); + + dbg_rcvry("dropping last node at %d:%d", + sleb->lnum, snod->offs); *offs = snod->offs; list_del(&snod->list); kfree(snod); sleb->nodes_cnt -= 1; - dropped = 1; } - return dropped; } /** @@ -521,33 +622,30 @@ static int drop_incomplete_group(struct ubifs_scan_leb *sleb, int *offs) * @lnum: LEB number * @offs: offset * @sbuf: LEB-sized buffer to use - * @grouped: nodes may be grouped for recovery + * @jhead: journal head number this LEB belongs to (%-1 if the LEB does not + * belong to any journal head) * * This function does a scan of a LEB, but caters for errors that might have * been caused by the unclean unmount from which we are attempting to recover. - * - * This function returns %0 on success and a negative error code on failure. + * Returns %0 in case of success, %-EUCLEAN if an unrecoverable corruption is + * found, and a negative error code in case of failure. */ struct ubifs_scan_leb *ubifs_recover_leb(struct ubifs_info *c, int lnum, - int offs, void *sbuf, int grouped) + int offs, void *sbuf, int jhead) { - int err, len = c->leb_size - offs, need_clean = 0, quiet = 1; - int empty_chkd = 0, start = offs; + int ret = 0, err, len = c->leb_size - offs, start = offs, min_io_unit; + int grouped = jhead == -1 ? 0 : c->jheads[jhead].grouped; struct ubifs_scan_leb *sleb; void *buf = sbuf + offs; - dbg_rcvry("%d:%d", lnum, offs); + dbg_rcvry("%d:%d, jhead %d, grouped %d", lnum, offs, jhead, grouped); sleb = ubifs_start_scan(c, lnum, offs, sbuf); if (IS_ERR(sleb)) return sleb; - if (sleb->ecc) - need_clean = 1; - + ubifs_assert(len >= 8); while (len >= 8) { - int ret; - dbg_scan("look at LEB %d:%d (%d bytes left)", lnum, offs, len); @@ -557,8 +655,7 @@ struct ubifs_scan_leb *ubifs_recover_leb(struct ubifs_info *c, int lnum, * Scan quietly until there is an error from which we cannot * recover */ - ret = ubifs_scan_a_node(c, buf, len, lnum, offs, quiet); - + ret = ubifs_scan_a_node(c, buf, len, lnum, offs, 1); if (ret == SCANNED_A_NODE) { /* A valid node, and not a padding node */ struct ubifs_ch *ch = buf; @@ -571,98 +668,127 @@ struct ubifs_scan_leb *ubifs_recover_leb(struct ubifs_info *c, int lnum, offs += node_len; buf += node_len; len -= node_len; - continue; - } - - if (ret > 0) { + } else if (ret > 0) { /* Padding bytes or a valid padding node */ offs += ret; buf += ret; len -= ret; - continue; - } - - if (ret == SCANNED_EMPTY_SPACE) { - if (!is_empty(buf, len)) { - if (!is_last_write(c, buf, offs)) - break; - clean_buf(c, &buf, lnum, &offs, &len); - need_clean = 1; - } - empty_chkd = 1; + } else if (ret == SCANNED_EMPTY_SPACE || + ret == SCANNED_GARBAGE || + ret == SCANNED_A_BAD_PAD_NODE || + ret == SCANNED_A_CORRUPT_NODE) { + dbg_rcvry("found corruption (%d) at %d:%d", + ret, lnum, offs); break; + } else { + ubifs_err("unexpected return value %d", ret); + err = -EINVAL; + goto error; } + } - if (ret == SCANNED_GARBAGE || ret == SCANNED_A_BAD_PAD_NODE) - if (is_last_write(c, buf, offs)) { - clean_buf(c, &buf, lnum, &offs, &len); - need_clean = 1; - empty_chkd = 1; - break; - } - - if (ret == SCANNED_A_CORRUPT_NODE) - if (no_more_nodes(c, buf, len, lnum, offs)) { - clean_buf(c, &buf, lnum, &offs, &len); - need_clean = 1; - empty_chkd = 1; - break; - } - - if (quiet) { - /* Redo the last scan but noisily */ - quiet = 0; - continue; - } + if (ret == SCANNED_GARBAGE || ret == SCANNED_A_BAD_PAD_NODE) { + if (!is_last_write(c, buf, offs)) + goto corrupted_rescan; + } else if (ret == SCANNED_A_CORRUPT_NODE) { + if (!no_more_nodes(c, buf, len, lnum, offs)) + goto corrupted_rescan; + } else if (!is_empty(buf, len)) { + if (!is_last_write(c, buf, offs)) { + int corruption = first_non_ff(buf, len); - switch (ret) { - case SCANNED_GARBAGE: - dbg_err("garbage"); - goto corrupted; - case SCANNED_A_CORRUPT_NODE: - case SCANNED_A_BAD_PAD_NODE: - dbg_err("bad node"); - goto corrupted; - default: - dbg_err("unknown"); + /* + * See header comment for this file for more + * explanations about the reasons we have this check. + */ + ubifs_err("corrupt empty space LEB %d:%d, corruption starts at %d", + lnum, offs, corruption); + /* Make sure we dump interesting non-0xFF data */ + offs += corruption; + buf += corruption; goto corrupted; } } - if (!empty_chkd && !is_empty(buf, len)) { - if (is_last_write(c, buf, offs)) { - clean_buf(c, &buf, lnum, &offs, &len); - need_clean = 1; - } else { - ubifs_err("corrupt empty space at LEB %d:%d", - lnum, offs); - goto corrupted; - } - } + min_io_unit = round_down(offs, c->min_io_size); + if (grouped) + /* + * If nodes are grouped, always drop the incomplete group at + * the end. + */ + drop_last_group(sleb, &offs); - /* Drop nodes from incomplete group */ - if (grouped && drop_incomplete_group(sleb, &offs)) { - buf = sbuf + offs; - len = c->leb_size - offs; - clean_buf(c, &buf, lnum, &offs, &len); - need_clean = 1; + if (jhead == GCHD) { + /* + * If this LEB belongs to the GC head then while we are in the + * middle of the same min. I/O unit keep dropping nodes. So + * basically, what we want is to make sure that the last min. + * I/O unit where we saw the corruption is dropped completely + * with all the uncorrupted nodes which may possibly sit there. + * + * In other words, let's name the min. I/O unit where the + * corruption starts B, and the previous min. I/O unit A. The + * below code tries to deal with a situation when half of B + * contains valid nodes or the end of a valid node, and the + * second half of B contains corrupted data or garbage. This + * means that UBIFS had been writing to B just before the power + * cut happened. I do not know how realistic is this scenario + * that half of the min. I/O unit had been written successfully + * and the other half not, but this is possible in our 'failure + * mode emulation' infrastructure at least. + * + * So what is the problem, why we need to drop those nodes? Why + * can't we just clean-up the second half of B by putting a + * padding node there? We can, and this works fine with one + * exception which was reproduced with power cut emulation + * testing and happens extremely rarely. + * + * Imagine the file-system is full, we run GC which starts + * moving valid nodes from LEB X to LEB Y (obviously, LEB Y is + * the current GC head LEB). The @c->gc_lnum is -1, which means + * that GC will retain LEB X and will try to continue. Imagine + * that LEB X is currently the dirtiest LEB, and the amount of + * used space in LEB Y is exactly the same as amount of free + * space in LEB X. + * + * And a power cut happens when nodes are moved from LEB X to + * LEB Y. We are here trying to recover LEB Y which is the GC + * head LEB. We find the min. I/O unit B as described above. + * Then we clean-up LEB Y by padding min. I/O unit. And later + * 'ubifs_rcvry_gc_commit()' function fails, because it cannot + * find a dirty LEB which could be GC'd into LEB Y! Even LEB X + * does not match because the amount of valid nodes there does + * not fit the free space in LEB Y any more! And this is + * because of the padding node which we added to LEB Y. The + * user-visible effect of this which I once observed and + * analysed is that we cannot mount the file-system with + * -ENOSPC error. + * + * So obviously, to make sure that situation does not happen we + * should free min. I/O unit B in LEB Y completely and the last + * used min. I/O unit in LEB Y should be A. This is basically + * what the below code tries to do. + */ + while (offs > min_io_unit) + drop_last_node(sleb, &offs); } - if (offs % c->min_io_size) { - clean_buf(c, &buf, lnum, &offs, &len); - need_clean = 1; - } + buf = sbuf + offs; + len = c->leb_size - offs; + clean_buf(c, &buf, lnum, &offs, &len); ubifs_end_scan(c, sleb, lnum, offs); - if (need_clean) { - err = fix_unclean_leb(c, sleb, start); - if (err) - goto error; - } + err = fix_unclean_leb(c, sleb, start); + if (err) + goto error; return sleb; +corrupted_rescan: + /* Re-scan the corrupted data with verbose messages */ + ubifs_err("corruption %d", ret); + ubifs_scan_a_node(c, buf, len, lnum, offs, 1); corrupted: ubifs_scanned_corruption(c, lnum, offs, buf); err = -EUCLEAN; @@ -693,22 +819,23 @@ static int get_cs_sqnum(struct ubifs_info *c, int lnum, int offs, return -ENOMEM; if (c->leb_size - offs < UBIFS_CS_NODE_SZ) goto out_err; - err = ubi_read(c->ubi, lnum, (void *)cs_node, offs, UBIFS_CS_NODE_SZ); + err = ubifs_leb_read(c, lnum, (void *)cs_node, offs, + UBIFS_CS_NODE_SZ, 0); if (err && err != -EBADMSG) goto out_free; ret = ubifs_scan_a_node(c, cs_node, UBIFS_CS_NODE_SZ, lnum, offs, 0); if (ret != SCANNED_A_NODE) { - dbg_err("Not a valid node"); + ubifs_err("Not a valid node"); goto out_err; } if (cs_node->ch.node_type != UBIFS_CS_NODE) { - dbg_err("Node a CS node, type is %d", cs_node->ch.node_type); + ubifs_err("Node a CS node, type is %d", cs_node->ch.node_type); goto out_err; } if (le64_to_cpu(cs_node->cmt_no) != c->cmt_no) { - dbg_err("CS node cmt_no %llu != current cmt_no %llu", - (unsigned long long)le64_to_cpu(cs_node->cmt_no), - c->cmt_no); + ubifs_err("CS node cmt_no %llu != current cmt_no %llu", + (unsigned long long)le64_to_cpu(cs_node->cmt_no), + c->cmt_no); goto out_err; } *cs_sqnum = le64_to_cpu(cs_node->ch.sqnum); @@ -732,7 +859,8 @@ out_free: * @sbuf: LEB-sized buffer to use * * This function does a scan of a LEB, but caters for errors that might have - * been caused by the unclean unmount from which we are attempting to recover. + * been caused by unclean reboots from which we are attempting to recover + * (assume that only the last log LEB can be corrupted by an unclean reboot). * * This function returns %0 on success and a negative error code on failure. */ @@ -751,7 +879,7 @@ struct ubifs_scan_leb *ubifs_recover_log_leb(struct ubifs_info *c, int lnum, * We can only recover at the end of the log, so check that the * next log LEB is empty or out of date. */ - sleb = ubifs_scan(c, next_lnum, 0, sbuf); + sleb = ubifs_scan(c, next_lnum, 0, sbuf, 0); if (IS_ERR(sleb)) return sleb; if (sleb->nodes_cnt) { @@ -770,15 +898,15 @@ struct ubifs_scan_leb *ubifs_recover_log_leb(struct ubifs_info *c, int lnum, } } if (snod->sqnum > cs_sqnum) { - ubifs_err("unrecoverable log corruption " - "in LEB %d", lnum); + ubifs_err("unrecoverable log corruption in LEB %d", + lnum); ubifs_scan_destroy(sleb); return ERR_PTR(-EUCLEAN); } } ubifs_scan_destroy(sleb); } - return ubifs_recover_leb(c, lnum, offs, sbuf, 0); + return ubifs_recover_leb(c, lnum, offs, sbuf, -1); } /** @@ -792,15 +920,10 @@ struct ubifs_scan_leb *ubifs_recover_log_leb(struct ubifs_info *c, int lnum, * * This function returns %0 on success and a negative error code on failure. */ -static int recover_head(const struct ubifs_info *c, int lnum, int offs, - void *sbuf) +static int recover_head(struct ubifs_info *c, int lnum, int offs, void *sbuf) { - int len, err, need_clean = 0; + int len = c->max_write_size, err; - if (c->min_io_size > 1) - len = c->min_io_size; - else - len = 512; if (offs + len > c->leb_size) len = c->leb_size - offs; @@ -808,27 +931,15 @@ static int recover_head(const struct ubifs_info *c, int lnum, int offs, return 0; /* Read at the head location and check it is empty flash */ - err = ubi_read(c->ubi, lnum, sbuf, offs, len); - if (err) - need_clean = 1; - else { - uint8_t *p = sbuf; - - while (len--) - if (*p++ != 0xff) { - need_clean = 1; - break; - } - } - - if (need_clean) { + err = ubifs_leb_read(c, lnum, sbuf, offs, len, 1); + if (err || !is_empty(sbuf, len)) { dbg_rcvry("cleaning head at %d:%d", lnum, offs); if (offs == 0) return ubifs_leb_unmap(c, lnum); - err = ubi_read(c->ubi, lnum, sbuf, 0, offs); + err = ubifs_leb_read(c, lnum, sbuf, 0, offs, 1); if (err) return err; - return ubi_leb_change(c->ubi, lnum, sbuf, offs, UBI_UNKNOWN); + return ubifs_leb_change(c, lnum, sbuf, offs); } return 0; @@ -851,11 +962,11 @@ static int recover_head(const struct ubifs_info *c, int lnum, int offs, * * This function returns %0 on success and a negative error code on failure. */ -int ubifs_recover_inl_heads(const struct ubifs_info *c, void *sbuf) +int ubifs_recover_inl_heads(struct ubifs_info *c, void *sbuf) { int err; - ubifs_assert(!(c->vfs_sb->s_flags & MS_RDONLY) || c->remounting_rw); + ubifs_assert(!c->ro_mount || c->remounting_rw); dbg_rcvry("checking index head at %d:%d", c->ihead_lnum, c->ihead_offs); err = recover_head(c, c->ihead_lnum, c->ihead_offs, sbuf); @@ -871,7 +982,7 @@ int ubifs_recover_inl_heads(const struct ubifs_info *c, void *sbuf) } /** - * clean_an_unclean_leb - read and write a LEB to remove corruption. + * clean_an_unclean_leb - read and write a LEB to remove corruption. * @c: UBIFS file-system description object * @ucleb: unclean LEB information * @sbuf: LEB-sized buffer to use @@ -882,7 +993,7 @@ int ubifs_recover_inl_heads(const struct ubifs_info *c, void *sbuf) * * This function returns %0 on success and a negative error code on failure. */ -static int clean_an_unclean_leb(const struct ubifs_info *c, +static int clean_an_unclean_leb(struct ubifs_info *c, struct ubifs_unclean_leb *ucleb, void *sbuf) { int err, lnum = ucleb->lnum, offs = 0, len = ucleb->endpt, quiet = 1; @@ -898,7 +1009,7 @@ static int clean_an_unclean_leb(const struct ubifs_info *c, return 0; } - err = ubi_read(c->ubi, lnum, buf, offs, len); + err = ubifs_leb_read(c, lnum, buf, offs, len, 0); if (err && err != -EBADMSG) return err; @@ -958,7 +1069,7 @@ static int clean_an_unclean_leb(const struct ubifs_info *c, } /* Write back the LEB atomically */ - err = ubi_leb_change(c->ubi, lnum, sbuf, len, UBI_UNKNOWN); + err = ubifs_leb_change(c, lnum, sbuf, len); if (err) return err; @@ -978,7 +1089,7 @@ static int clean_an_unclean_leb(const struct ubifs_info *c, * * This function returns %0 on success and a negative error code on failure. */ -int ubifs_clean_lebs(const struct ubifs_info *c, void *sbuf) +int ubifs_clean_lebs(struct ubifs_info *c, void *sbuf) { dbg_rcvry("recovery"); while (!list_empty(&c->unclean_leb_list)) { @@ -996,6 +1107,140 @@ int ubifs_clean_lebs(const struct ubifs_info *c, void *sbuf) return 0; } +#ifndef __UBOOT__ +/** + * grab_empty_leb - grab an empty LEB to use as GC LEB and run commit. + * @c: UBIFS file-system description object + * + * This is a helper function for 'ubifs_rcvry_gc_commit()' which grabs an empty + * LEB to be used as GC LEB (@c->gc_lnum), and then runs the commit. Returns + * zero in case of success and a negative error code in case of failure. + */ +static int grab_empty_leb(struct ubifs_info *c) +{ + int lnum, err; + + /* + * Note, it is very important to first search for an empty LEB and then + * run the commit, not vice-versa. The reason is that there might be + * only one empty LEB at the moment, the one which has been the + * @c->gc_lnum just before the power cut happened. During the regular + * UBIFS operation (not now) @c->gc_lnum is marked as "taken", so no + * one but GC can grab it. But at this moment this single empty LEB is + * not marked as taken, so if we run commit - what happens? Right, the + * commit will grab it and write the index there. Remember that the + * index always expands as long as there is free space, and it only + * starts consolidating when we run out of space. + * + * IOW, if we run commit now, we might not be able to find a free LEB + * after this. + */ + lnum = ubifs_find_free_leb_for_idx(c); + if (lnum < 0) { + ubifs_err("could not find an empty LEB"); + ubifs_dump_lprops(c); + ubifs_dump_budg(c, &c->bi); + return lnum; + } + + /* Reset the index flag */ + err = ubifs_change_one_lp(c, lnum, LPROPS_NC, LPROPS_NC, 0, + LPROPS_INDEX, 0); + if (err) + return err; + + c->gc_lnum = lnum; + dbg_rcvry("found empty LEB %d, run commit", lnum); + + return ubifs_run_commit(c); +} + +/** + * ubifs_rcvry_gc_commit - recover the GC LEB number and run the commit. + * @c: UBIFS file-system description object + * + * Out-of-place garbage collection requires always one empty LEB with which to + * start garbage collection. The LEB number is recorded in c->gc_lnum and is + * written to the master node on unmounting. In the case of an unclean unmount + * the value of gc_lnum recorded in the master node is out of date and cannot + * be used. Instead, recovery must allocate an empty LEB for this purpose. + * However, there may not be enough empty space, in which case it must be + * possible to GC the dirtiest LEB into the GC head LEB. + * + * This function also runs the commit which causes the TNC updates from + * size-recovery and orphans to be written to the flash. That is important to + * ensure correct replay order for subsequent mounts. + * + * This function returns %0 on success and a negative error code on failure. + */ +int ubifs_rcvry_gc_commit(struct ubifs_info *c) +{ + struct ubifs_wbuf *wbuf = &c->jheads[GCHD].wbuf; + struct ubifs_lprops lp; + int err; + + dbg_rcvry("GC head LEB %d, offs %d", wbuf->lnum, wbuf->offs); + + c->gc_lnum = -1; + if (wbuf->lnum == -1 || wbuf->offs == c->leb_size) + return grab_empty_leb(c); + + err = ubifs_find_dirty_leb(c, &lp, wbuf->offs, 2); + if (err) { + if (err != -ENOSPC) + return err; + + dbg_rcvry("could not find a dirty LEB"); + return grab_empty_leb(c); + } + + ubifs_assert(!(lp.flags & LPROPS_INDEX)); + ubifs_assert(lp.free + lp.dirty >= wbuf->offs); + + /* + * We run the commit before garbage collection otherwise subsequent + * mounts will see the GC and orphan deletion in a different order. + */ + dbg_rcvry("committing"); + err = ubifs_run_commit(c); + if (err) + return err; + + dbg_rcvry("GC'ing LEB %d", lp.lnum); + mutex_lock_nested(&wbuf->io_mutex, wbuf->jhead); + err = ubifs_garbage_collect_leb(c, &lp); + if (err >= 0) { + int err2 = ubifs_wbuf_sync_nolock(wbuf); + + if (err2) + err = err2; + } + mutex_unlock(&wbuf->io_mutex); + if (err < 0) { + ubifs_err("GC failed, error %d", err); + if (err == -EAGAIN) + err = -EINVAL; + return err; + } + + ubifs_assert(err == LEB_RETAINED); + if (err != LEB_RETAINED) + return -EINVAL; + + err = ubifs_leb_unmap(c, c->gc_lnum); + if (err) + return err; + + dbg_rcvry("allocated LEB %d for GC", lp.lnum); + return 0; +} +#else +int ubifs_rcvry_gc_commit(struct ubifs_info *c) +{ + return 0; +} +#endif + /** * struct size_entry - inode size information for recovery. * @rb: link in the RB-tree of sizes @@ -1090,6 +1335,23 @@ static void remove_ino(struct ubifs_info *c, ino_t inum) } /** + * ubifs_destroy_size_tree - free resources related to the size tree. + * @c: UBIFS file-system description object + */ +void ubifs_destroy_size_tree(struct ubifs_info *c) +{ + struct size_entry *e, *n; + + rbtree_postorder_for_each_entry_safe(e, n, &c->size_tree, rb) { + if (e->inode) + iput(e->inode); + kfree(e); + } + + c->size_tree = RB_ROOT; +} + +/** * ubifs_recover_size_accum - accumulate inode sizes for recovery. * @c: UBIFS file-system description object * @key: node key @@ -1157,6 +1419,64 @@ int ubifs_recover_size_accum(struct ubifs_info *c, union ubifs_key *key, return 0; } +#ifndef __UBOOT__ +/** + * fix_size_in_place - fix inode size in place on flash. + * @c: UBIFS file-system description object + * @e: inode size information for recovery + */ +static int fix_size_in_place(struct ubifs_info *c, struct size_entry *e) +{ + struct ubifs_ino_node *ino = c->sbuf; + unsigned char *p; + union ubifs_key key; + int err, lnum, offs, len; + loff_t i_size; + uint32_t crc; + + /* Locate the inode node LEB number and offset */ + ino_key_init(c, &key, e->inum); + err = ubifs_tnc_locate(c, &key, ino, &lnum, &offs); + if (err) + goto out; + /* + * If the size recorded on the inode node is greater than the size that + * was calculated from nodes in the journal then don't change the inode. + */ + i_size = le64_to_cpu(ino->size); + if (i_size >= e->d_size) + return 0; + /* Read the LEB */ + err = ubifs_leb_read(c, lnum, c->sbuf, 0, c->leb_size, 1); + if (err) + goto out; + /* Change the size field and recalculate the CRC */ + ino = c->sbuf + offs; + ino->size = cpu_to_le64(e->d_size); + len = le32_to_cpu(ino->ch.len); + crc = crc32(UBIFS_CRC32_INIT, (void *)ino + 8, len - 8); + ino->ch.crc = cpu_to_le32(crc); + /* Work out where data in the LEB ends and free space begins */ + p = c->sbuf; + len = c->leb_size - 1; + while (p[len] == 0xff) + len -= 1; + len = ALIGN(len + 1, c->min_io_size); + /* Atomically write the fixed LEB back again */ + err = ubifs_leb_change(c, lnum, c->sbuf, len); + if (err) + goto out; + dbg_rcvry("inode %lu at %d:%d size %lld -> %lld", + (unsigned long)e->inum, lnum, offs, i_size, e->d_size); + return 0; + +out: + ubifs_warn("inode %lu failed to fix size %lld -> %lld error %d", + (unsigned long)e->inum, e->i_size, e->d_size, err); + return err; +} +#endif + /** * ubifs_recover_size - recover inode size. * @c: UBIFS file-system description object @@ -1196,30 +1516,48 @@ int ubifs_recover_size(struct ubifs_info *c) e->i_size = le64_to_cpu(ino->size); } } + if (e->exists && e->i_size < e->d_size) { - if (!e->inode && (c->vfs_sb->s_flags & MS_RDONLY)) { + if (c->ro_mount) { /* Fix the inode size and pin it in memory */ struct inode *inode; + struct ubifs_inode *ui; + + ubifs_assert(!e->inode); inode = ubifs_iget(c->vfs_sb, e->inum); if (IS_ERR(inode)) return PTR_ERR(inode); + + ui = ubifs_inode(inode); if (inode->i_size < e->d_size) { dbg_rcvry("ino %lu size %lld -> %lld", (unsigned long)e->inum, - e->d_size, inode->i_size); + inode->i_size, e->d_size); inode->i_size = e->d_size; - ubifs_inode(inode)->ui_size = e->d_size; + ui->ui_size = e->d_size; + ui->synced_i_size = e->d_size; e->inode = inode; this = rb_next(this); continue; } iput(inode); +#ifndef __UBOOT__ + } else { + /* Fix the size in place */ + err = fix_size_in_place(c, e); + if (err) + return err; + if (e->inode) + iput(e->inode); +#endif } } + this = rb_next(this); rb_erase(&e->rb, &c->size_tree); kfree(e); } + return 0; } diff --git a/fs/ubifs/replay.c b/fs/ubifs/replay.c index da33a14..6393b15 100644 --- a/fs/ubifs/replay.c +++ b/fs/ubifs/replay.c @@ -3,18 +3,7 @@ * * Copyright (C) 2006-2008 Nokia Corporation. * - * 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. - * - * 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., 51 - * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * SPDX-License-Identifier: GPL-2.0+ * * Authors: Adrian Hunter * Artem Bityutskiy (Битюцкий Артём) @@ -32,44 +21,38 @@ * larger is the journal, the more memory its index may consume. */ +#define __UBOOT__ +#ifdef __UBOOT__ +#include <linux/compat.h> +#include <linux/err.h> +#endif #include "ubifs.h" - -/* - * Replay flags. - * - * REPLAY_DELETION: node was deleted - * REPLAY_REF: node is a reference node - */ -enum { - REPLAY_DELETION = 1, - REPLAY_REF = 2, -}; +#include <linux/list_sort.h> /** - * struct replay_entry - replay tree entry. + * struct replay_entry - replay list entry. * @lnum: logical eraseblock number of the node * @offs: node offset * @len: node length + * @deletion: non-zero if this entry corresponds to a node deletion * @sqnum: node sequence number - * @flags: replay flags - * @rb: links the replay tree + * @list: links the replay list * @key: node key * @nm: directory entry name * @old_size: truncation old size * @new_size: truncation new size - * @free: amount of free space in a bud - * @dirty: amount of dirty space in a bud from padding and deletion nodes * - * UBIFS journal replay must compare node sequence numbers, which means it must - * build a tree of node information to insert into the TNC. + * The replay process first scans all buds and builds the replay list, then + * sorts the replay list in nodes sequence number order, and then inserts all + * the replay entries to the TNC. */ struct replay_entry { int lnum; int offs; int len; + unsigned int deletion:1; unsigned long long sqnum; - int flags; - struct rb_node rb; + struct list_head list; union ubifs_key key; union { struct qstr nm; @@ -77,10 +60,6 @@ struct replay_entry { loff_t old_size; loff_t new_size; }; - struct { - int free; - int dirty; - }; }; }; @@ -88,83 +67,117 @@ struct replay_entry { * struct bud_entry - entry in the list of buds to replay. * @list: next bud in the list * @bud: bud description object - * @free: free bytes in the bud * @sqnum: reference node sequence number + * @free: free bytes in the bud + * @dirty: dirty bytes in the bud */ struct bud_entry { struct list_head list; struct ubifs_bud *bud; - int free; unsigned long long sqnum; + int free; + int dirty; }; +#ifndef __UBOOT__ /** * set_bud_lprops - set free and dirty space used by a bud. * @c: UBIFS file-system description object - * @r: replay entry of bud + * @b: bud entry which describes the bud + * + * This function makes sure the LEB properties of bud @b are set correctly + * after the replay. Returns zero in case of success and a negative error code + * in case of failure. */ -static int set_bud_lprops(struct ubifs_info *c, struct replay_entry *r) +static int set_bud_lprops(struct ubifs_info *c, struct bud_entry *b) { const struct ubifs_lprops *lp; int err = 0, dirty; ubifs_get_lprops(c); - lp = ubifs_lpt_lookup_dirty(c, r->lnum); + lp = ubifs_lpt_lookup_dirty(c, b->bud->lnum); if (IS_ERR(lp)) { err = PTR_ERR(lp); goto out; } dirty = lp->dirty; - if (r->offs == 0 && (lp->free != c->leb_size || lp->dirty != 0)) { + if (b->bud->start == 0 && (lp->free != c->leb_size || lp->dirty != 0)) { /* * The LEB was added to the journal with a starting offset of * zero which means the LEB must have been empty. The LEB - * property values should be lp->free == c->leb_size and - * lp->dirty == 0, but that is not the case. The reason is that - * the LEB was garbage collected. The garbage collector resets - * the free and dirty space without recording it anywhere except - * lprops, so if there is not a commit then lprops does not have - * that information next time the file system is mounted. + * property values should be @lp->free == @c->leb_size and + * @lp->dirty == 0, but that is not the case. The reason is that + * the LEB had been garbage collected before it became the bud, + * and there was not commit inbetween. The garbage collector + * resets the free and dirty space without recording it + * anywhere except lprops, so if there was no commit then + * lprops does not have that information. * * We do not need to adjust free space because the scan has told * us the exact value which is recorded in the replay entry as - * r->free. + * @b->free. * * However we do need to subtract from the dirty space the * amount of space that the garbage collector reclaimed, which * is the whole LEB minus the amount of space that was free. */ - dbg_mnt("bud LEB %d was GC'd (%d free, %d dirty)", r->lnum, + dbg_mnt("bud LEB %d was GC'd (%d free, %d dirty)", b->bud->lnum, lp->free, lp->dirty); - dbg_gc("bud LEB %d was GC'd (%d free, %d dirty)", r->lnum, + dbg_gc("bud LEB %d was GC'd (%d free, %d dirty)", b->bud->lnum, lp->free, lp->dirty); dirty -= c->leb_size - lp->free; /* * If the replay order was perfect the dirty space would now be - * zero. The order is not perfect because the the journal heads + * zero. The order is not perfect because the journal heads * race with each other. This is not a problem but is does mean * that the dirty space may temporarily exceed c->leb_size * during the replay. */ if (dirty != 0) - dbg_msg("LEB %d lp: %d free %d dirty " - "replay: %d free %d dirty", r->lnum, lp->free, - lp->dirty, r->free, r->dirty); + dbg_mnt("LEB %d lp: %d free %d dirty replay: %d free %d dirty", + b->bud->lnum, lp->free, lp->dirty, b->free, + b->dirty); } - lp = ubifs_change_lp(c, lp, r->free, dirty + r->dirty, + lp = ubifs_change_lp(c, lp, b->free, dirty + b->dirty, lp->flags | LPROPS_TAKEN, 0); if (IS_ERR(lp)) { err = PTR_ERR(lp); goto out; } + + /* Make sure the journal head points to the latest bud */ + err = ubifs_wbuf_seek_nolock(&c->jheads[b->bud->jhead].wbuf, + b->bud->lnum, c->leb_size - b->free); + out: ubifs_release_lprops(c); return err; } /** + * set_buds_lprops - set free and dirty space for all replayed buds. + * @c: UBIFS file-system description object + * + * This function sets LEB properties for all replayed buds. Returns zero in + * case of success and a negative error code in case of failure. + */ +static int set_buds_lprops(struct ubifs_info *c) +{ + struct bud_entry *b; + int err; + + list_for_each_entry(b, &c->replay_buds, list) { + err = set_bud_lprops(c, b); + if (err) + return err; + } + + return 0; +} + +/** * trun_remove_range - apply a replay entry for a truncation to the TNC. * @c: UBIFS file-system description object * @r: replay entry of truncation @@ -200,24 +213,22 @@ static int trun_remove_range(struct ubifs_info *c, struct replay_entry *r) */ static int apply_replay_entry(struct ubifs_info *c, struct replay_entry *r) { - int err, deletion = ((r->flags & REPLAY_DELETION) != 0); + int err; - dbg_mnt("LEB %d:%d len %d flgs %d sqnum %llu %s", r->lnum, - r->offs, r->len, r->flags, r->sqnum, DBGKEY(&r->key)); + dbg_mntk(&r->key, "LEB %d:%d len %d deletion %d sqnum %llu key ", + r->lnum, r->offs, r->len, r->deletion, r->sqnum); /* Set c->replay_sqnum to help deal with dangling branches. */ c->replay_sqnum = r->sqnum; - if (r->flags & REPLAY_REF) - err = set_bud_lprops(c, r); - else if (is_hash_key(c, &r->key)) { - if (deletion) + if (is_hash_key(c, &r->key)) { + if (r->deletion) err = ubifs_tnc_remove_nm(c, &r->key, &r->nm); else err = ubifs_tnc_add_nm(c, &r->key, r->lnum, r->offs, r->len, &r->nm); } else { - if (deletion) + if (r->deletion) switch (key_type(c, &r->key)) { case UBIFS_INO_KEY: { @@ -240,7 +251,7 @@ static int apply_replay_entry(struct ubifs_info *c, struct replay_entry *r) return err; if (c->need_recovery) - err = ubifs_recover_size_accum(c, &r->key, deletion, + err = ubifs_recover_size_accum(c, &r->key, r->deletion, r->new_size); } @@ -248,68 +259,77 @@ static int apply_replay_entry(struct ubifs_info *c, struct replay_entry *r) } /** - * destroy_replay_tree - destroy the replay. - * @c: UBIFS file-system description object + * replay_entries_cmp - compare 2 replay entries. + * @priv: UBIFS file-system description object + * @a: first replay entry + * @a: second replay entry * - * Destroy the replay tree. + * This is a comparios function for 'list_sort()' which compares 2 replay + * entries @a and @b by comparing their sequence numer. Returns %1 if @a has + * greater sequence number and %-1 otherwise. */ -static void destroy_replay_tree(struct ubifs_info *c) +static int replay_entries_cmp(void *priv, struct list_head *a, + struct list_head *b) { - struct rb_node *this = c->replay_tree.rb_node; - struct replay_entry *r; - - while (this) { - if (this->rb_left) { - this = this->rb_left; - continue; - } else if (this->rb_right) { - this = this->rb_right; - continue; - } - r = rb_entry(this, struct replay_entry, rb); - this = rb_parent(this); - if (this) { - if (this->rb_left == &r->rb) - this->rb_left = NULL; - else - this->rb_right = NULL; - } - if (is_hash_key(c, &r->key)) - kfree((void *)r->nm.name); - kfree(r); - } - c->replay_tree = RB_ROOT; + struct replay_entry *ra, *rb; + + cond_resched(); + if (a == b) + return 0; + + ra = list_entry(a, struct replay_entry, list); + rb = list_entry(b, struct replay_entry, list); + ubifs_assert(ra->sqnum != rb->sqnum); + if (ra->sqnum > rb->sqnum) + return 1; + return -1; } /** - * apply_replay_tree - apply the replay tree to the TNC. + * apply_replay_list - apply the replay list to the TNC. * @c: UBIFS file-system description object * - * Apply the replay tree. - * Returns zero in case of success and a negative error code in case of - * failure. + * Apply all entries in the replay list to the TNC. Returns zero in case of + * success and a negative error code in case of failure. */ -static int apply_replay_tree(struct ubifs_info *c) +static int apply_replay_list(struct ubifs_info *c) { - struct rb_node *this = rb_first(&c->replay_tree); + struct replay_entry *r; + int err; - while (this) { - struct replay_entry *r; - int err; + list_sort(c, &c->replay_list, &replay_entries_cmp); + list_for_each_entry(r, &c->replay_list, list) { cond_resched(); - r = rb_entry(this, struct replay_entry, rb); err = apply_replay_entry(c, r); if (err) return err; - this = rb_next(this); } + return 0; } /** - * insert_node - insert a node to the replay tree. + * destroy_replay_list - destroy the replay. + * @c: UBIFS file-system description object + * + * Destroy the replay list. + */ +static void destroy_replay_list(struct ubifs_info *c) +{ + struct replay_entry *r, *tmp; + + list_for_each_entry_safe(r, tmp, &c->replay_list, list) { + if (is_hash_key(c, &r->key)) + kfree(r->nm.name); + list_del(&r->list); + kfree(r); + } +} + +/** + * insert_node - insert a node to the replay list * @c: UBIFS file-system description object * @lnum: node logical eraseblock number * @offs: node offset @@ -321,39 +341,25 @@ static int apply_replay_tree(struct ubifs_info *c) * @old_size: truncation old size * @new_size: truncation new size * - * This function inserts a scanned non-direntry node to the replay tree. The - * replay tree is an RB-tree containing @struct replay_entry elements which are - * indexed by the sequence number. The replay tree is applied at the very end - * of the replay process. Since the tree is sorted in sequence number order, - * the older modifications are applied first. This function returns zero in - * case of success and a negative error code in case of failure. + * This function inserts a scanned non-direntry node to the replay list. The + * replay list contains @struct replay_entry elements, and we sort this list in + * sequence number order before applying it. The replay list is applied at the + * very end of the replay process. Since the list is sorted in sequence number + * order, the older modifications are applied first. This function returns zero + * in case of success and a negative error code in case of failure. */ static int insert_node(struct ubifs_info *c, int lnum, int offs, int len, union ubifs_key *key, unsigned long long sqnum, int deletion, int *used, loff_t old_size, loff_t new_size) { - struct rb_node **p = &c->replay_tree.rb_node, *parent = NULL; struct replay_entry *r; + dbg_mntk(key, "add LEB %d:%d, key ", lnum, offs); + if (key_inum(c, key) >= c->highest_inum) c->highest_inum = key_inum(c, key); - dbg_mnt("add LEB %d:%d, key %s", lnum, offs, DBGKEY(key)); - while (*p) { - parent = *p; - r = rb_entry(parent, struct replay_entry, rb); - if (sqnum < r->sqnum) { - p = &(*p)->rb_left; - continue; - } else if (sqnum > r->sqnum) { - p = &(*p)->rb_right; - continue; - } - ubifs_err("duplicate sqnum in replay"); - return -EINVAL; - } - r = kzalloc(sizeof(struct replay_entry), GFP_KERNEL); if (!r) return -ENOMEM; @@ -363,19 +369,18 @@ static int insert_node(struct ubifs_info *c, int lnum, int offs, int len, r->lnum = lnum; r->offs = offs; r->len = len; + r->deletion = !!deletion; r->sqnum = sqnum; - r->flags = (deletion ? REPLAY_DELETION : 0); + key_copy(c, key, &r->key); r->old_size = old_size; r->new_size = new_size; - key_copy(c, key, &r->key); - rb_link_node(&r->rb, parent, p); - rb_insert_color(&r->rb, &c->replay_tree); + list_add_tail(&r->list, &c->replay_list); return 0; } /** - * insert_dent - insert a directory entry node into the replay tree. + * insert_dent - insert a directory entry node into the replay list. * @c: UBIFS file-system description object * @lnum: node logical eraseblock number * @offs: node offset @@ -387,43 +392,25 @@ static int insert_node(struct ubifs_info *c, int lnum, int offs, int len, * @deletion: non-zero if this is a deletion * @used: number of bytes in use in a LEB * - * This function inserts a scanned directory entry node to the replay tree. - * Returns zero in case of success and a negative error code in case of - * failure. - * - * This function is also used for extended attribute entries because they are - * implemented as directory entry nodes. + * This function inserts a scanned directory entry node or an extended + * attribute entry to the replay list. Returns zero in case of success and a + * negative error code in case of failure. */ static int insert_dent(struct ubifs_info *c, int lnum, int offs, int len, union ubifs_key *key, const char *name, int nlen, unsigned long long sqnum, int deletion, int *used) { - struct rb_node **p = &c->replay_tree.rb_node, *parent = NULL; struct replay_entry *r; char *nbuf; + dbg_mntk(key, "add LEB %d:%d, key ", lnum, offs); if (key_inum(c, key) >= c->highest_inum) c->highest_inum = key_inum(c, key); - dbg_mnt("add LEB %d:%d, key %s", lnum, offs, DBGKEY(key)); - while (*p) { - parent = *p; - r = rb_entry(parent, struct replay_entry, rb); - if (sqnum < r->sqnum) { - p = &(*p)->rb_left; - continue; - } - if (sqnum > r->sqnum) { - p = &(*p)->rb_right; - continue; - } - ubifs_err("duplicate sqnum in replay"); - return -EINVAL; - } - r = kzalloc(sizeof(struct replay_entry), GFP_KERNEL); if (!r) return -ENOMEM; + nbuf = kmalloc(nlen + 1, GFP_KERNEL); if (!nbuf) { kfree(r); @@ -435,19 +422,18 @@ static int insert_dent(struct ubifs_info *c, int lnum, int offs, int len, r->lnum = lnum; r->offs = offs; r->len = len; + r->deletion = !!deletion; r->sqnum = sqnum; + key_copy(c, key, &r->key); r->nm.len = nlen; memcpy(nbuf, name, nlen); nbuf[nlen] = '\0'; r->nm.name = nbuf; - r->flags = (deletion ? REPLAY_DELETION : 0); - key_copy(c, key, &r->key); - ubifs_assert(!*p); - rb_link_node(&r->rb, parent, p); - rb_insert_color(&r->rb, &c->replay_tree); + list_add_tail(&r->list, &c->replay_list); return 0; } +#endif /** * ubifs_validate_entry - validate directory or extended attribute entry node. @@ -466,7 +452,7 @@ int ubifs_validate_entry(struct ubifs_info *c, if (le32_to_cpu(dent->ch.len) != nlen + UBIFS_DENT_NODE_SZ + 1 || dent->type >= UBIFS_ITYPES_CNT || nlen > UBIFS_MAX_NLEN || dent->name[nlen] != 0 || - strnlen((char *)dent->name, nlen) != nlen || + strnlen(dent->name, nlen) != nlen || le64_to_cpu(dent->inum) > MAX_INUM) { ubifs_err("bad %s node", key_type == UBIFS_DENT_KEY ? "directory entry" : "extended attribute entry"); @@ -481,32 +467,94 @@ int ubifs_validate_entry(struct ubifs_info *c, return 0; } +#ifndef __UBOOT__ +/** + * is_last_bud - check if the bud is the last in the journal head. + * @c: UBIFS file-system description object + * @bud: bud description object + * + * This function checks if bud @bud is the last bud in its journal head. This + * information is then used by 'replay_bud()' to decide whether the bud can + * have corruptions or not. Indeed, only last buds can be corrupted by power + * cuts. Returns %1 if this is the last bud, and %0 if not. + */ +static int is_last_bud(struct ubifs_info *c, struct ubifs_bud *bud) +{ + struct ubifs_jhead *jh = &c->jheads[bud->jhead]; + struct ubifs_bud *next; + uint32_t data; + int err; + + if (list_is_last(&bud->list, &jh->buds_list)) + return 1; + + /* + * The following is a quirk to make sure we work correctly with UBIFS + * images used with older UBIFS. + * + * Normally, the last bud will be the last in the journal head's list + * of bud. However, there is one exception if the UBIFS image belongs + * to older UBIFS. This is fairly unlikely: one would need to use old + * UBIFS, then have a power cut exactly at the right point, and then + * try to mount this image with new UBIFS. + * + * The exception is: it is possible to have 2 buds A and B, A goes + * before B, and B is the last, bud B is contains no data, and bud A is + * corrupted at the end. The reason is that in older versions when the + * journal code switched the next bud (from A to B), it first added a + * log reference node for the new bud (B), and only after this it + * synchronized the write-buffer of current bud (A). But later this was + * changed and UBIFS started to always synchronize the write-buffer of + * the bud (A) before writing the log reference for the new bud (B). + * + * But because older UBIFS always synchronized A's write-buffer before + * writing to B, we can recognize this exceptional situation but + * checking the contents of bud B - if it is empty, then A can be + * treated as the last and we can recover it. + * + * TODO: remove this piece of code in a couple of years (today it is + * 16.05.2011). + */ + next = list_entry(bud->list.next, struct ubifs_bud, list); + if (!list_is_last(&next->list, &jh->buds_list)) + return 0; + + err = ubifs_leb_read(c, next->lnum, (char *)&data, next->start, 4, 1); + if (err) + return 0; + + return data == 0xFFFFFFFF; +} + /** * replay_bud - replay a bud logical eraseblock. * @c: UBIFS file-system description object - * @lnum: bud logical eraseblock number to replay - * @offs: bud start offset - * @jhead: journal head to which this bud belongs - * @free: amount of free space in the bud is returned here - * @dirty: amount of dirty space from padding and deletion nodes is returned - * here + * @b: bud entry which describes the bud * - * This function returns zero in case of success and a negative error code in - * case of failure. + * This function replays bud @bud, recovers it if needed, and adds all nodes + * from this bud to the replay list. Returns zero in case of success and a + * negative error code in case of failure. */ -static int replay_bud(struct ubifs_info *c, int lnum, int offs, int jhead, - int *free, int *dirty) +static int replay_bud(struct ubifs_info *c, struct bud_entry *b) { - int err = 0, used = 0; + int is_last = is_last_bud(c, b->bud); + int err = 0, used = 0, lnum = b->bud->lnum, offs = b->bud->start; struct ubifs_scan_leb *sleb; struct ubifs_scan_node *snod; - struct ubifs_bud *bud; - dbg_mnt("replay bud LEB %d, head %d", lnum, jhead); - if (c->need_recovery) - sleb = ubifs_recover_leb(c, lnum, offs, c->sbuf, jhead != GCHD); + dbg_mnt("replay bud LEB %d, head %d, offs %d, is_last %d", + lnum, b->bud->jhead, offs, is_last); + + if (c->need_recovery && is_last) + /* + * Recover only last LEBs in the journal heads, because power + * cuts may cause corruptions only in these LEBs, because only + * these LEBs could possibly be written to at the power cut + * time. + */ + sleb = ubifs_recover_leb(c, lnum, offs, c->sbuf, b->bud->jhead); else - sleb = ubifs_scan(c, lnum, offs, c->sbuf); + sleb = ubifs_scan(c, lnum, offs, c->sbuf, 0); if (IS_ERR(sleb)) return PTR_ERR(sleb); @@ -580,7 +628,7 @@ static int replay_bud(struct ubifs_info *c, int lnum, int offs, int jhead, goto out_dump; err = insert_dent(c, lnum, snod->offs, snod->len, - &snod->key, (char *)dent->name, + &snod->key, dent->name, le16_to_cpu(dent->nlen), snod->sqnum, !le64_to_cpu(dent->inum), &used); break; @@ -620,15 +668,14 @@ static int replay_bud(struct ubifs_info *c, int lnum, int offs, int jhead, goto out; } - bud = ubifs_search_bud(c, lnum); - if (!bud) - BUG(); - + ubifs_assert(ubifs_search_bud(c, lnum)); ubifs_assert(sleb->endpt - offs >= used); ubifs_assert(sleb->endpt % c->min_io_size == 0); - *dirty = sleb->endpt - offs - used; - *free = c->leb_size - sleb->endpt; + b->dirty = sleb->endpt - offs - used; + b->free = c->leb_size - sleb->endpt; + dbg_mnt("bud LEB %d replied: dirty %d, free %d", + lnum, b->dirty, b->free); out: ubifs_scan_destroy(sleb); @@ -636,61 +683,12 @@ out: out_dump: ubifs_err("bad node is at LEB %d:%d", lnum, snod->offs); - dbg_dump_node(c, snod->node); + ubifs_dump_node(c, snod->node); ubifs_scan_destroy(sleb); return -EINVAL; } /** - * insert_ref_node - insert a reference node to the replay tree. - * @c: UBIFS file-system description object - * @lnum: node logical eraseblock number - * @offs: node offset - * @sqnum: sequence number - * @free: amount of free space in bud - * @dirty: amount of dirty space from padding and deletion nodes - * - * This function inserts a reference node to the replay tree and returns zero - * in case of success or a negative error code in case of failure. - */ -static int insert_ref_node(struct ubifs_info *c, int lnum, int offs, - unsigned long long sqnum, int free, int dirty) -{ - struct rb_node **p = &c->replay_tree.rb_node, *parent = NULL; - struct replay_entry *r; - - dbg_mnt("add ref LEB %d:%d", lnum, offs); - while (*p) { - parent = *p; - r = rb_entry(parent, struct replay_entry, rb); - if (sqnum < r->sqnum) { - p = &(*p)->rb_left; - continue; - } else if (sqnum > r->sqnum) { - p = &(*p)->rb_right; - continue; - } - ubifs_err("duplicate sqnum in replay tree"); - return -EINVAL; - } - - r = kzalloc(sizeof(struct replay_entry), GFP_KERNEL); - if (!r) - return -ENOMEM; - - r->lnum = lnum; - r->offs = offs; - r->sqnum = sqnum; - r->flags = REPLAY_REF; - r->free = free; - r->dirty = dirty; - - rb_link_node(&r->rb, parent, p); - rb_insert_color(&r->rb, &c->replay_tree); - return 0; -} - -/** * replay_buds - replay all buds. * @c: UBIFS file-system description object * @@ -700,17 +698,16 @@ static int insert_ref_node(struct ubifs_info *c, int lnum, int offs, static int replay_buds(struct ubifs_info *c) { struct bud_entry *b; - int err, uninitialized_var(free), uninitialized_var(dirty); + int err; + unsigned long long prev_sqnum = 0; list_for_each_entry(b, &c->replay_buds, list) { - err = replay_bud(c, b->bud->lnum, b->bud->start, b->bud->jhead, - &free, &dirty); - if (err) - return err; - err = insert_ref_node(c, b->bud->lnum, b->bud->start, b->sqnum, - free, dirty); + err = replay_bud(c, b); if (err) return err; + + ubifs_assert(b->sqnum > prev_sqnum); + prev_sqnum = b->sqnum; } return 0; @@ -831,10 +828,16 @@ static int replay_log_leb(struct ubifs_info *c, int lnum, int offs, void *sbuf) const struct ubifs_cs_node *node; dbg_mnt("replay log LEB %d:%d", lnum, offs); - sleb = ubifs_scan(c, lnum, offs, sbuf); + sleb = ubifs_scan(c, lnum, offs, sbuf, c->need_recovery); if (IS_ERR(sleb)) { - if (c->need_recovery) - sleb = ubifs_recover_log_leb(c, lnum, offs, sbuf); + if (PTR_ERR(sleb) != -EUCLEAN || !c->need_recovery) + return PTR_ERR(sleb); + /* + * Note, the below function will recover this log LEB only if + * it is the last, because unclean reboots can possibly corrupt + * only the tail of the log. + */ + sleb = ubifs_recover_log_leb(c, lnum, offs, sbuf); if (IS_ERR(sleb)) return PTR_ERR(sleb); } @@ -845,7 +848,6 @@ static int replay_log_leb(struct ubifs_info *c, int lnum, int offs, void *sbuf) } node = sleb->buf; - snod = list_entry(sleb->nodes.next, struct ubifs_scan_node, list); if (c->cs_sqnum == 0) { /* @@ -856,16 +858,15 @@ static int replay_log_leb(struct ubifs_info *c, int lnum, int offs, void *sbuf) * numbers. */ if (snod->type != UBIFS_CS_NODE) { - dbg_err("first log node at LEB %d:%d is not CS node", - lnum, offs); + ubifs_err("first log node at LEB %d:%d is not CS node", + lnum, offs); goto out_dump; } if (le64_to_cpu(node->cmt_no) != c->cmt_no) { - dbg_err("first CS node at LEB %d:%d has wrong " - "commit number %llu expected %llu", - lnum, offs, - (unsigned long long)le64_to_cpu(node->cmt_no), - c->cmt_no); + ubifs_err("first CS node at LEB %d:%d has wrong commit number %llu expected %llu", + lnum, offs, + (unsigned long long)le64_to_cpu(node->cmt_no), + c->cmt_no); goto out_dump; } @@ -887,12 +888,11 @@ static int replay_log_leb(struct ubifs_info *c, int lnum, int offs, void *sbuf) /* Make sure the first node sits at offset zero of the LEB */ if (snod->offs != 0) { - dbg_err("first node is not at zero offset"); + ubifs_err("first node is not at zero offset"); goto out_dump; } list_for_each_entry(snod, &sleb->nodes, list) { - cond_resched(); if (snod->sqnum >= SQNUM_WATERMARK) { @@ -901,8 +901,8 @@ static int replay_log_leb(struct ubifs_info *c, int lnum, int offs, void *sbuf) } if (snod->sqnum < c->cs_sqnum) { - dbg_err("bad sqnum %llu, commit sqnum %llu", - snod->sqnum, c->cs_sqnum); + ubifs_err("bad sqnum %llu, commit sqnum %llu", + snod->sqnum, c->cs_sqnum); goto out_dump; } @@ -952,9 +952,9 @@ out: return err; out_dump: - ubifs_err("log error detected while replying the log at LEB %d:%d", + ubifs_err("log error detected while replaying the log at LEB %d:%d", lnum, offs + snod->offs); - dbg_dump_node(c, snod->node); + ubifs_dump_node(c, snod->node); ubifs_scan_destroy(sleb); return -EINVAL; } @@ -1004,67 +1004,64 @@ out: */ int ubifs_replay_journal(struct ubifs_info *c) { - int err, i, lnum, offs, _free; - void *sbuf = NULL; + int err, lnum, free; BUILD_BUG_ON(UBIFS_TRUN_KEY > 5); /* Update the status of the index head in lprops to 'taken' */ - _free = take_ihead(c); - if (_free < 0) - return _free; /* Error code */ + free = take_ihead(c); + if (free < 0) + return free; /* Error code */ - if (c->ihead_offs != c->leb_size - _free) { + if (c->ihead_offs != c->leb_size - free) { ubifs_err("bad index head LEB %d:%d", c->ihead_lnum, c->ihead_offs); return -EINVAL; } - sbuf = vmalloc(c->leb_size); - if (!sbuf) - return -ENOMEM; - dbg_mnt("start replaying the journal"); - c->replaying = 1; - lnum = c->ltail_lnum = c->lhead_lnum; - offs = c->lhead_offs; - for (i = 0; i < c->log_lebs; i++, lnum++) { - if (lnum >= UBIFS_LOG_LNUM + c->log_lebs) { - /* - * The log is logically circular, we reached the last - * LEB, switch to the first one. - */ - lnum = UBIFS_LOG_LNUM; - offs = 0; - } - err = replay_log_leb(c, lnum, offs, sbuf); + do { + err = replay_log_leb(c, lnum, 0, c->sbuf); if (err == 1) /* We hit the end of the log */ break; if (err) goto out; - offs = 0; - } + lnum = ubifs_next_log_lnum(c, lnum); + } while (lnum != c->ltail_lnum); err = replay_buds(c); if (err) goto out; - err = apply_replay_tree(c); + err = apply_replay_list(c); if (err) goto out; + err = set_buds_lprops(c); + if (err) + goto out; + + /* + * UBIFS budgeting calculations use @c->bi.uncommitted_idx variable + * to roughly estimate index growth. Things like @c->bi.min_idx_lebs + * depend on it. This means we have to initialize it to make sure + * budgeting works properly. + */ + c->bi.uncommitted_idx = atomic_long_read(&c->dirty_zn_cnt); + c->bi.uncommitted_idx *= c->max_idx_node_sz; + ubifs_assert(c->bud_bytes <= c->max_bud_bytes || c->need_recovery); - dbg_mnt("finished, log head LEB %d:%d, max_sqnum %llu, " - "highest_inum %lu", c->lhead_lnum, c->lhead_offs, c->max_sqnum, + dbg_mnt("finished, log head LEB %d:%d, max_sqnum %llu, highest_inum %lu", + c->lhead_lnum, c->lhead_offs, c->max_sqnum, (unsigned long)c->highest_inum); out: - destroy_replay_tree(c); + destroy_replay_list(c); destroy_bud_list(c); - vfree(sbuf); c->replaying = 0; return err; } +#endif diff --git a/fs/ubifs/sb.c b/fs/ubifs/sb.c index 00c9cd3..fc0194a 100644 --- a/fs/ubifs/sb.c +++ b/fs/ubifs/sb.c @@ -3,18 +3,7 @@ * * Copyright (C) 2006-2008 Nokia Corporation. * - * 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. - * - * 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., 51 - * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * SPDX-License-Identifier: GPL-2.0+ * * Authors: Artem Bityutskiy (Битюцкий Артём) * Adrian Hunter @@ -27,6 +16,18 @@ */ #include "ubifs.h" +#define __UBOOT__ +#ifndef __UBOOT__ +#include <linux/slab.h> +#include <linux/random.h> +#include <linux/math64.h> +#else + +#include <linux/compat.h> +#include <linux/err.h> +#include <ubi_uboot.h> +#include <linux/stat.h> +#endif /* * Default journal size in logical eraseblocks as a percent of total @@ -60,6 +61,282 @@ /* Default time granularity in nanoseconds */ #define DEFAULT_TIME_GRAN 1000000000 +#ifndef __UBOOT__ +/** + * create_default_filesystem - format empty UBI volume. + * @c: UBIFS file-system description object + * + * This function creates default empty file-system. Returns zero in case of + * success and a negative error code in case of failure. + */ +static int create_default_filesystem(struct ubifs_info *c) +{ + struct ubifs_sb_node *sup; + struct ubifs_mst_node *mst; + struct ubifs_idx_node *idx; + struct ubifs_branch *br; + struct ubifs_ino_node *ino; + struct ubifs_cs_node *cs; + union ubifs_key key; + int err, tmp, jnl_lebs, log_lebs, max_buds, main_lebs, main_first; + int lpt_lebs, lpt_first, orph_lebs, big_lpt, ino_waste, sup_flags = 0; + int min_leb_cnt = UBIFS_MIN_LEB_CNT; + long long tmp64, main_bytes; + __le64 tmp_le64; + + /* Some functions called from here depend on the @c->key_len filed */ + c->key_len = UBIFS_SK_LEN; + + /* + * First of all, we have to calculate default file-system geometry - + * log size, journal size, etc. + */ + if (c->leb_cnt < 0x7FFFFFFF / DEFAULT_JNL_PERCENT) + /* We can first multiply then divide and have no overflow */ + jnl_lebs = c->leb_cnt * DEFAULT_JNL_PERCENT / 100; + else + jnl_lebs = (c->leb_cnt / 100) * DEFAULT_JNL_PERCENT; + + if (jnl_lebs < UBIFS_MIN_JNL_LEBS) + jnl_lebs = UBIFS_MIN_JNL_LEBS; + if (jnl_lebs * c->leb_size > DEFAULT_MAX_JNL) + jnl_lebs = DEFAULT_MAX_JNL / c->leb_size; + + /* + * The log should be large enough to fit reference nodes for all bud + * LEBs. Because buds do not have to start from the beginning of LEBs + * (half of the LEB may contain committed data), the log should + * generally be larger, make it twice as large. + */ + tmp = 2 * (c->ref_node_alsz * jnl_lebs) + c->leb_size - 1; + log_lebs = tmp / c->leb_size; + /* Plus one LEB reserved for commit */ + log_lebs += 1; + if (c->leb_cnt - min_leb_cnt > 8) { + /* And some extra space to allow writes while committing */ + log_lebs += 1; + min_leb_cnt += 1; + } + + max_buds = jnl_lebs - log_lebs; + if (max_buds < UBIFS_MIN_BUD_LEBS) + max_buds = UBIFS_MIN_BUD_LEBS; + + /* + * Orphan nodes are stored in a separate area. One node can store a lot + * of orphan inode numbers, but when new orphan comes we just add a new + * orphan node. At some point the nodes are consolidated into one + * orphan node. + */ + orph_lebs = UBIFS_MIN_ORPH_LEBS; + if (c->leb_cnt - min_leb_cnt > 1) + /* + * For debugging purposes it is better to have at least 2 + * orphan LEBs, because the orphan subsystem would need to do + * consolidations and would be stressed more. + */ + orph_lebs += 1; + + main_lebs = c->leb_cnt - UBIFS_SB_LEBS - UBIFS_MST_LEBS - log_lebs; + main_lebs -= orph_lebs; + + lpt_first = UBIFS_LOG_LNUM + log_lebs; + c->lsave_cnt = DEFAULT_LSAVE_CNT; + c->max_leb_cnt = c->leb_cnt; + err = ubifs_create_dflt_lpt(c, &main_lebs, lpt_first, &lpt_lebs, + &big_lpt); + if (err) + return err; + + dbg_gen("LEB Properties Tree created (LEBs %d-%d)", lpt_first, + lpt_first + lpt_lebs - 1); + + main_first = c->leb_cnt - main_lebs; + + /* Create default superblock */ + tmp = ALIGN(UBIFS_SB_NODE_SZ, c->min_io_size); + sup = kzalloc(tmp, GFP_KERNEL); + if (!sup) + return -ENOMEM; + + tmp64 = (long long)max_buds * c->leb_size; + if (big_lpt) + sup_flags |= UBIFS_FLG_BIGLPT; + + sup->ch.node_type = UBIFS_SB_NODE; + sup->key_hash = UBIFS_KEY_HASH_R5; + sup->flags = cpu_to_le32(sup_flags); + sup->min_io_size = cpu_to_le32(c->min_io_size); + sup->leb_size = cpu_to_le32(c->leb_size); + sup->leb_cnt = cpu_to_le32(c->leb_cnt); + sup->max_leb_cnt = cpu_to_le32(c->max_leb_cnt); + sup->max_bud_bytes = cpu_to_le64(tmp64); + sup->log_lebs = cpu_to_le32(log_lebs); + sup->lpt_lebs = cpu_to_le32(lpt_lebs); + sup->orph_lebs = cpu_to_le32(orph_lebs); + sup->jhead_cnt = cpu_to_le32(DEFAULT_JHEADS_CNT); + sup->fanout = cpu_to_le32(DEFAULT_FANOUT); + sup->lsave_cnt = cpu_to_le32(c->lsave_cnt); + sup->fmt_version = cpu_to_le32(UBIFS_FORMAT_VERSION); + sup->time_gran = cpu_to_le32(DEFAULT_TIME_GRAN); + if (c->mount_opts.override_compr) + sup->default_compr = cpu_to_le16(c->mount_opts.compr_type); + else + sup->default_compr = cpu_to_le16(UBIFS_COMPR_LZO); + + generate_random_uuid(sup->uuid); + + main_bytes = (long long)main_lebs * c->leb_size; + tmp64 = div_u64(main_bytes * DEFAULT_RP_PERCENT, 100); + if (tmp64 > DEFAULT_MAX_RP_SIZE) + tmp64 = DEFAULT_MAX_RP_SIZE; + sup->rp_size = cpu_to_le64(tmp64); + sup->ro_compat_version = cpu_to_le32(UBIFS_RO_COMPAT_VERSION); + + err = ubifs_write_node(c, sup, UBIFS_SB_NODE_SZ, 0, 0); + kfree(sup); + if (err) + return err; + + dbg_gen("default superblock created at LEB 0:0"); + + /* Create default master node */ + mst = kzalloc(c->mst_node_alsz, GFP_KERNEL); + if (!mst) + return -ENOMEM; + + mst->ch.node_type = UBIFS_MST_NODE; + mst->log_lnum = cpu_to_le32(UBIFS_LOG_LNUM); + mst->highest_inum = cpu_to_le64(UBIFS_FIRST_INO); + mst->cmt_no = 0; + mst->root_lnum = cpu_to_le32(main_first + DEFAULT_IDX_LEB); + mst->root_offs = 0; + tmp = ubifs_idx_node_sz(c, 1); + mst->root_len = cpu_to_le32(tmp); + mst->gc_lnum = cpu_to_le32(main_first + DEFAULT_GC_LEB); + mst->ihead_lnum = cpu_to_le32(main_first + DEFAULT_IDX_LEB); + mst->ihead_offs = cpu_to_le32(ALIGN(tmp, c->min_io_size)); + mst->index_size = cpu_to_le64(ALIGN(tmp, 8)); + mst->lpt_lnum = cpu_to_le32(c->lpt_lnum); + mst->lpt_offs = cpu_to_le32(c->lpt_offs); + mst->nhead_lnum = cpu_to_le32(c->nhead_lnum); + mst->nhead_offs = cpu_to_le32(c->nhead_offs); + mst->ltab_lnum = cpu_to_le32(c->ltab_lnum); + mst->ltab_offs = cpu_to_le32(c->ltab_offs); + mst->lsave_lnum = cpu_to_le32(c->lsave_lnum); + mst->lsave_offs = cpu_to_le32(c->lsave_offs); + mst->lscan_lnum = cpu_to_le32(main_first); + mst->empty_lebs = cpu_to_le32(main_lebs - 2); + mst->idx_lebs = cpu_to_le32(1); + mst->leb_cnt = cpu_to_le32(c->leb_cnt); + + /* Calculate lprops statistics */ + tmp64 = main_bytes; + tmp64 -= ALIGN(ubifs_idx_node_sz(c, 1), c->min_io_size); + tmp64 -= ALIGN(UBIFS_INO_NODE_SZ, c->min_io_size); + mst->total_free = cpu_to_le64(tmp64); + + tmp64 = ALIGN(ubifs_idx_node_sz(c, 1), c->min_io_size); + ino_waste = ALIGN(UBIFS_INO_NODE_SZ, c->min_io_size) - + UBIFS_INO_NODE_SZ; + tmp64 += ino_waste; + tmp64 -= ALIGN(ubifs_idx_node_sz(c, 1), 8); + mst->total_dirty = cpu_to_le64(tmp64); + + /* The indexing LEB does not contribute to dark space */ + tmp64 = ((long long)(c->main_lebs - 1) * c->dark_wm); + mst->total_dark = cpu_to_le64(tmp64); + + mst->total_used = cpu_to_le64(UBIFS_INO_NODE_SZ); + + err = ubifs_write_node(c, mst, UBIFS_MST_NODE_SZ, UBIFS_MST_LNUM, 0); + if (err) { + kfree(mst); + return err; + } + err = ubifs_write_node(c, mst, UBIFS_MST_NODE_SZ, UBIFS_MST_LNUM + 1, + 0); + kfree(mst); + if (err) + return err; + + dbg_gen("default master node created at LEB %d:0", UBIFS_MST_LNUM); + + /* Create the root indexing node */ + tmp = ubifs_idx_node_sz(c, 1); + idx = kzalloc(ALIGN(tmp, c->min_io_size), GFP_KERNEL); + if (!idx) + return -ENOMEM; + + c->key_fmt = UBIFS_SIMPLE_KEY_FMT; + c->key_hash = key_r5_hash; + + idx->ch.node_type = UBIFS_IDX_NODE; + idx->child_cnt = cpu_to_le16(1); + ino_key_init(c, &key, UBIFS_ROOT_INO); + br = ubifs_idx_branch(c, idx, 0); + key_write_idx(c, &key, &br->key); + br->lnum = cpu_to_le32(main_first + DEFAULT_DATA_LEB); + br->len = cpu_to_le32(UBIFS_INO_NODE_SZ); + err = ubifs_write_node(c, idx, tmp, main_first + DEFAULT_IDX_LEB, 0); + kfree(idx); + if (err) + return err; + + dbg_gen("default root indexing node created LEB %d:0", + main_first + DEFAULT_IDX_LEB); + + /* Create default root inode */ + tmp = ALIGN(UBIFS_INO_NODE_SZ, c->min_io_size); + ino = kzalloc(tmp, GFP_KERNEL); + if (!ino) + return -ENOMEM; + + ino_key_init_flash(c, &ino->key, UBIFS_ROOT_INO); + ino->ch.node_type = UBIFS_INO_NODE; + ino->creat_sqnum = cpu_to_le64(++c->max_sqnum); + ino->nlink = cpu_to_le32(2); + tmp_le64 = cpu_to_le64(CURRENT_TIME_SEC.tv_sec); + ino->atime_sec = tmp_le64; + ino->ctime_sec = tmp_le64; + ino->mtime_sec = tmp_le64; + ino->atime_nsec = 0; + ino->ctime_nsec = 0; + ino->mtime_nsec = 0; + ino->mode = cpu_to_le32(S_IFDIR | S_IRUGO | S_IWUSR | S_IXUGO); + ino->size = cpu_to_le64(UBIFS_INO_NODE_SZ); + + /* Set compression enabled by default */ + ino->flags = cpu_to_le32(UBIFS_COMPR_FL); + + err = ubifs_write_node(c, ino, UBIFS_INO_NODE_SZ, + main_first + DEFAULT_DATA_LEB, 0); + kfree(ino); + if (err) + return err; + + dbg_gen("root inode created at LEB %d:0", + main_first + DEFAULT_DATA_LEB); + + /* + * The first node in the log has to be the commit start node. This is + * always the case during normal file-system operation. Write a fake + * commit start node to the log. + */ + tmp = ALIGN(UBIFS_CS_NODE_SZ, c->min_io_size); + cs = kzalloc(tmp, GFP_KERNEL); + if (!cs) + return -ENOMEM; + + cs->ch.node_type = UBIFS_CS_NODE; + err = ubifs_write_node(c, cs, UBIFS_CS_NODE_SZ, UBIFS_LOG_LNUM, 0); + kfree(cs); + + ubifs_msg("default file-system created"); + return 0; +} +#endif + /** * validate_sb - validate superblock node. * @c: UBIFS file-system description object @@ -114,9 +391,8 @@ static int validate_sb(struct ubifs_info *c, struct ubifs_sb_node *sup) min_leb_cnt += c->lpt_lebs + c->orph_lebs + c->jhead_cnt + 6; if (c->leb_cnt < min_leb_cnt || c->leb_cnt > c->vi.size) { - ubifs_err("bad LEB count: %d in superblock, %d on UBI volume, " - "%d minimum required", c->leb_cnt, c->vi.size, - min_leb_cnt); + ubifs_err("bad LEB count: %d in superblock, %d on UBI volume, %d minimum required", + c->leb_cnt, c->vi.size, min_leb_cnt); goto failed; } @@ -127,13 +403,22 @@ static int validate_sb(struct ubifs_info *c, struct ubifs_sb_node *sup) } if (c->main_lebs < UBIFS_MIN_MAIN_LEBS) { - err = 7; + ubifs_err("too few main LEBs count %d, must be at least %d", + c->main_lebs, UBIFS_MIN_MAIN_LEBS); + goto failed; + } + + max_bytes = (long long)c->leb_size * UBIFS_MIN_BUD_LEBS; + if (c->max_bud_bytes < max_bytes) { + ubifs_err("too small journal (%lld bytes), must be at least %lld bytes", + c->max_bud_bytes, max_bytes); goto failed; } - if (c->max_bud_bytes < (long long)c->leb_size * UBIFS_MIN_BUD_LEBS || - c->max_bud_bytes > (long long)c->leb_size * c->main_lebs) { - err = 8; + max_bytes = (long long)c->leb_size * c->main_lebs; + if (c->max_bud_bytes > max_bytes) { + ubifs_err("too large journal size (%lld bytes), only %lld bytes available in the main area", + c->max_bud_bytes, max_bytes); goto failed; } @@ -167,7 +452,6 @@ static int validate_sb(struct ubifs_info *c, struct ubifs_sb_node *sup) goto failed; } - max_bytes = c->main_lebs * (long long)c->leb_size; if (c->rp_size < 0 || max_bytes < c->rp_size) { err = 14; goto failed; @@ -183,7 +467,7 @@ static int validate_sb(struct ubifs_info *c, struct ubifs_sb_node *sup) failed: ubifs_err("bad superblock, error %d", err); - dbg_dump_node(c, sup); + ubifs_dump_node(c, sup); return -EINVAL; } @@ -192,7 +476,8 @@ failed: * @c: UBIFS file-system description object * * This function returns a pointer to the superblock node or a negative error - * code. + * code. Note, the user of this function is responsible of kfree()'ing the + * returned superblock buffer. */ struct ubifs_sb_node *ubifs_read_sb_node(struct ubifs_info *c) { @@ -214,6 +499,21 @@ struct ubifs_sb_node *ubifs_read_sb_node(struct ubifs_info *c) } /** + * ubifs_write_sb_node - write superblock node. + * @c: UBIFS file-system description object + * @sup: superblock node read with 'ubifs_read_sb_node()' + * + * This function returns %0 on success and a negative error code on failure. + */ +int ubifs_write_sb_node(struct ubifs_info *c, struct ubifs_sb_node *sup) +{ + int len = ALIGN(UBIFS_SB_NODE_SZ, c->min_io_size); + + ubifs_prepare_node(c, sup, UBIFS_SB_NODE_SZ, 1); + return ubifs_leb_change(c, UBIFS_SB_LNUM, sup, len); +} + +/** * ubifs_read_superblock - read superblock. * @c: UBIFS file-system description object * @@ -227,8 +527,14 @@ int ubifs_read_superblock(struct ubifs_info *c) struct ubifs_sb_node *sup; if (c->empty) { +#ifndef __UBOOT__ + err = create_default_filesystem(c); + if (err) + return err; +#else printf("No UBIFS filesystem found!\n"); return -1; +#endif } sup = ubifs_read_sb_node(c); @@ -243,16 +549,12 @@ int ubifs_read_superblock(struct ubifs_info *c) * due to the unavailability of time-travelling equipment. */ if (c->fmt_version > UBIFS_FORMAT_VERSION) { - struct super_block *sb = c->vfs_sb; - int mounting_ro = sb->s_flags & MS_RDONLY; - - ubifs_assert(!c->ro_media || mounting_ro); - if (!mounting_ro || + ubifs_assert(!c->ro_media || c->ro_mount); + if (!c->ro_mount || c->ro_compat_version > UBIFS_RO_COMPAT_VERSION) { - ubifs_err("on-flash format version is w%d/r%d, but " - "software only supports up to version " - "w%d/r%d", c->fmt_version, - c->ro_compat_version, UBIFS_FORMAT_VERSION, + ubifs_err("on-flash format version is w%d/r%d, but software only supports up to version w%d/r%d", + c->fmt_version, c->ro_compat_version, + UBIFS_FORMAT_VERSION, UBIFS_RO_COMPAT_VERSION); if (c->ro_compat_version <= UBIFS_RO_COMPAT_VERSION) { ubifs_msg("only R/O mounting is possible"); @@ -310,22 +612,41 @@ int ubifs_read_superblock(struct ubifs_info *c) c->jhead_cnt = le32_to_cpu(sup->jhead_cnt) + NONDATA_JHEADS_CNT; c->fanout = le32_to_cpu(sup->fanout); c->lsave_cnt = le32_to_cpu(sup->lsave_cnt); - c->default_compr = le16_to_cpu(sup->default_compr); c->rp_size = le64_to_cpu(sup->rp_size); - c->rp_uid = le32_to_cpu(sup->rp_uid); - c->rp_gid = le32_to_cpu(sup->rp_gid); +#ifndef __UBOOT__ + c->rp_uid = make_kuid(&init_user_ns, le32_to_cpu(sup->rp_uid)); + c->rp_gid = make_kgid(&init_user_ns, le32_to_cpu(sup->rp_gid)); +#else + c->rp_uid.val = le32_to_cpu(sup->rp_uid); + c->rp_gid.val = le32_to_cpu(sup->rp_gid); +#endif sup_flags = le32_to_cpu(sup->flags); + if (!c->mount_opts.override_compr) + c->default_compr = le16_to_cpu(sup->default_compr); c->vfs_sb->s_time_gran = le32_to_cpu(sup->time_gran); memcpy(&c->uuid, &sup->uuid, 16); c->big_lpt = !!(sup_flags & UBIFS_FLG_BIGLPT); + c->space_fixup = !!(sup_flags & UBIFS_FLG_SPACE_FIXUP); /* Automatically increase file system size to the maximum size */ c->old_leb_cnt = c->leb_cnt; if (c->leb_cnt < c->vi.size && c->leb_cnt < c->max_leb_cnt) { c->leb_cnt = min_t(int, c->max_leb_cnt, c->vi.size); - dbg_mnt("Auto resizing (ro) from %d LEBs to %d LEBs", - c->old_leb_cnt, c->leb_cnt); + if (c->ro_mount) + dbg_mnt("Auto resizing (ro) from %d LEBs to %d LEBs", + c->old_leb_cnt, c->leb_cnt); +#ifndef __UBOOT__ + else { + dbg_mnt("Auto resizing (sb) from %d LEBs to %d LEBs", + c->old_leb_cnt, c->leb_cnt); + sup->leb_cnt = cpu_to_le32(c->leb_cnt); + err = ubifs_write_sb_node(c, sup); + if (err) + goto out; + c->old_leb_cnt = c->leb_cnt; + } +#endif } c->log_bytes = (long long)c->log_lebs * c->leb_size; @@ -337,10 +658,162 @@ int ubifs_read_superblock(struct ubifs_info *c) c->main_lebs = c->leb_cnt - UBIFS_SB_LEBS - UBIFS_MST_LEBS; c->main_lebs -= c->log_lebs + c->lpt_lebs + c->orph_lebs; c->main_first = c->leb_cnt - c->main_lebs; - c->report_rp_size = ubifs_reported_space(c, c->rp_size); err = validate_sb(c, sup); out: kfree(sup); return err; } + +/** + * fixup_leb - fixup/unmap an LEB containing free space. + * @c: UBIFS file-system description object + * @lnum: the LEB number to fix up + * @len: number of used bytes in LEB (starting at offset 0) + * + * This function reads the contents of the given LEB number @lnum, then fixes + * it up, so that empty min. I/O units in the end of LEB are actually erased on + * flash (rather than being just all-0xff real data). If the LEB is completely + * empty, it is simply unmapped. + */ +static int fixup_leb(struct ubifs_info *c, int lnum, int len) +{ + int err; + + ubifs_assert(len >= 0); + ubifs_assert(len % c->min_io_size == 0); + ubifs_assert(len < c->leb_size); + + if (len == 0) { + dbg_mnt("unmap empty LEB %d", lnum); + return ubifs_leb_unmap(c, lnum); + } + + dbg_mnt("fixup LEB %d, data len %d", lnum, len); + err = ubifs_leb_read(c, lnum, c->sbuf, 0, len, 1); + if (err) + return err; + + return ubifs_leb_change(c, lnum, c->sbuf, len); +} + +/** + * fixup_free_space - find & remap all LEBs containing free space. + * @c: UBIFS file-system description object + * + * This function walks through all LEBs in the filesystem and fiexes up those + * containing free/empty space. + */ +static int fixup_free_space(struct ubifs_info *c) +{ + int lnum, err = 0; + struct ubifs_lprops *lprops; + + ubifs_get_lprops(c); + + /* Fixup LEBs in the master area */ + for (lnum = UBIFS_MST_LNUM; lnum < UBIFS_LOG_LNUM; lnum++) { + err = fixup_leb(c, lnum, c->mst_offs + c->mst_node_alsz); + if (err) + goto out; + } + + /* Unmap unused log LEBs */ + lnum = ubifs_next_log_lnum(c, c->lhead_lnum); + while (lnum != c->ltail_lnum) { + err = fixup_leb(c, lnum, 0); + if (err) + goto out; + lnum = ubifs_next_log_lnum(c, lnum); + } + + /* + * Fixup the log head which contains the only a CS node at the + * beginning. + */ + err = fixup_leb(c, c->lhead_lnum, + ALIGN(UBIFS_CS_NODE_SZ, c->min_io_size)); + if (err) + goto out; + + /* Fixup LEBs in the LPT area */ + for (lnum = c->lpt_first; lnum <= c->lpt_last; lnum++) { + int free = c->ltab[lnum - c->lpt_first].free; + + if (free > 0) { + err = fixup_leb(c, lnum, c->leb_size - free); + if (err) + goto out; + } + } + + /* Unmap LEBs in the orphans area */ + for (lnum = c->orph_first; lnum <= c->orph_last; lnum++) { + err = fixup_leb(c, lnum, 0); + if (err) + goto out; + } + + /* Fixup LEBs in the main area */ + for (lnum = c->main_first; lnum < c->leb_cnt; lnum++) { + lprops = ubifs_lpt_lookup(c, lnum); + if (IS_ERR(lprops)) { + err = PTR_ERR(lprops); + goto out; + } + + if (lprops->free > 0) { + err = fixup_leb(c, lnum, c->leb_size - lprops->free); + if (err) + goto out; + } + } + +out: + ubifs_release_lprops(c); + return err; +} + +/** + * ubifs_fixup_free_space - find & fix all LEBs with free space. + * @c: UBIFS file-system description object + * + * This function fixes up LEBs containing free space on first mount, if the + * appropriate flag was set when the FS was created. Each LEB with one or more + * empty min. I/O unit (i.e. free-space-count > 0) is re-written, to make sure + * the free space is actually erased. E.g., this is necessary for some NAND + * chips, since the free space may have been programmed like real "0xff" data + * (generating a non-0xff ECC), causing future writes to the not-really-erased + * NAND pages to behave badly. After the space is fixed up, the superblock flag + * is cleared, so that this is skipped for all future mounts. + */ +int ubifs_fixup_free_space(struct ubifs_info *c) +{ + int err; + struct ubifs_sb_node *sup; + + ubifs_assert(c->space_fixup); + ubifs_assert(!c->ro_mount); + + ubifs_msg("start fixing up free space"); + + err = fixup_free_space(c); + if (err) + return err; + + sup = ubifs_read_sb_node(c); + if (IS_ERR(sup)) + return PTR_ERR(sup); + + /* Free-space fixup is no longer required */ + c->space_fixup = 0; + sup->flags &= cpu_to_le32(~UBIFS_FLG_SPACE_FIXUP); + + err = ubifs_write_sb_node(c, sup); + kfree(sup); + if (err) + return err; + + ubifs_msg("free space fixup complete"); + return err; +} diff --git a/fs/ubifs/scan.c b/fs/ubifs/scan.c index 0ed8247..5523d4e 100644 --- a/fs/ubifs/scan.c +++ b/fs/ubifs/scan.c @@ -3,18 +3,7 @@ * * Copyright (C) 2006-2008 Nokia Corporation * - * 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. - * - * 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., 51 - * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * SPDX-License-Identifier: GPL-2.0+ * * Authors: Adrian Hunter * Artem Bityutskiy (Битюцкий Артём) @@ -27,6 +16,10 @@ * debugging functions. */ +#define __UBOOT__ +#ifdef __UBOOT__ +#include <linux/err.h> +#endif #include "ubifs.h" /** @@ -75,7 +68,7 @@ int ubifs_scan_a_node(const struct ubifs_info *c, void *buf, int len, int lnum, magic = le32_to_cpu(ch->magic); if (magic == 0xFFFFFFFF) { - dbg_scan("hit empty space"); + dbg_scan("hit empty space at LEB %d:%d", lnum, offs); return SCANNED_EMPTY_SPACE; } @@ -85,7 +78,8 @@ int ubifs_scan_a_node(const struct ubifs_info *c, void *buf, int len, int lnum, if (len < UBIFS_CH_SZ) return SCANNED_GARBAGE; - dbg_scan("scanning %s", dbg_ntype(ch->node_type)); + dbg_scan("scanning %s at LEB %d:%d", + dbg_ntype(ch->node_type), lnum, offs); if (ubifs_check_node(c, buf, lnum, offs, quiet, 1)) return SCANNED_A_CORRUPT_NODE; @@ -101,22 +95,21 @@ int ubifs_scan_a_node(const struct ubifs_info *c, void *buf, int len, int lnum, if (!quiet) { ubifs_err("bad pad node at LEB %d:%d", lnum, offs); - dbg_dump_node(c, pad); + ubifs_dump_node(c, pad); } return SCANNED_A_BAD_PAD_NODE; } /* Make the node pads to 8-byte boundary */ if ((node_len + pad_len) & 7) { - if (!quiet) { - dbg_err("bad padding length %d - %d", - offs, offs + node_len + pad_len); - } + if (!quiet) + ubifs_err("bad padding length %d - %d", + offs, offs + node_len + pad_len); return SCANNED_A_BAD_PAD_NODE; } - dbg_scan("%d bytes padded, offset now %d", - pad_len, ALIGN(offs + node_len + pad_len, 8)); + dbg_scan("%d bytes padded at LEB %d:%d, offset now %d", pad_len, + lnum, offs, ALIGN(offs + node_len + pad_len, 8)); return node_len + pad_len; } @@ -149,10 +142,10 @@ struct ubifs_scan_leb *ubifs_start_scan(const struct ubifs_info *c, int lnum, INIT_LIST_HEAD(&sleb->nodes); sleb->buf = sbuf; - err = ubi_read(c->ubi, lnum, sbuf + offs, offs, c->leb_size - offs); + err = ubifs_leb_read(c, lnum, sbuf + offs, offs, c->leb_size - offs, 0); if (err && err != -EBADMSG) { - ubifs_err("cannot read %d bytes from LEB %d:%d," - " error %d", c->leb_size - offs, lnum, offs, err); + ubifs_err("cannot read %d bytes from LEB %d:%d, error %d", + c->leb_size - offs, lnum, offs, err); kfree(sleb); return ERR_PTR(err); } @@ -198,7 +191,7 @@ int ubifs_add_snod(const struct ubifs_info *c, struct ubifs_scan_leb *sleb, struct ubifs_ino_node *ino = buf; struct ubifs_scan_node *snod; - snod = kzalloc(sizeof(struct ubifs_scan_node), GFP_NOFS); + snod = kmalloc(sizeof(struct ubifs_scan_node), GFP_NOFS); if (!snod) return -ENOMEM; @@ -213,13 +206,15 @@ int ubifs_add_snod(const struct ubifs_info *c, struct ubifs_scan_leb *sleb, case UBIFS_DENT_NODE: case UBIFS_XENT_NODE: case UBIFS_DATA_NODE: - case UBIFS_TRUN_NODE: /* * The key is in the same place in all keyed * nodes. */ key_read(c, &ino->key, &snod->key); break; + default: + invalid_key_init(c, &snod->key); + break; } list_add_tail(&snod->list, &sleb->nodes); sleb->nodes_cnt += 1; @@ -238,13 +233,11 @@ void ubifs_scanned_corruption(const struct ubifs_info *c, int lnum, int offs, { int len; - ubifs_err("corrupted data at LEB %d:%d", lnum, offs); - if (dbg_failure_mode) - return; + ubifs_err("corruption at LEB %d:%d", lnum, offs); len = c->leb_size - offs; - if (len > 4096) - len = 4096; - dbg_err("first %d bytes from LEB %d:%d", len, lnum, offs); + if (len > 8192) + len = 8192; + ubifs_err("first %d bytes from LEB %d:%d", len, lnum, offs); print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET, 32, 4, buf, len, 1); } @@ -253,13 +246,19 @@ void ubifs_scanned_corruption(const struct ubifs_info *c, int lnum, int offs, * @c: UBIFS file-system description object * @lnum: logical eraseblock number * @offs: offset to start at (usually zero) - * @sbuf: scan buffer (must be c->leb_size) + * @sbuf: scan buffer (must be of @c->leb_size bytes in size) + * @quiet: print no messages * * This function scans LEB number @lnum and returns complete information about - * its contents. Returns an error code in case of failure. + * its contents. Returns the scaned information in case of success and, + * %-EUCLEAN if the LEB neads recovery, and other negative error codes in case + * of failure. + * + * If @quiet is non-zero, this function does not print large and scary + * error messages and flash dumps in case of errors. */ struct ubifs_scan_leb *ubifs_scan(const struct ubifs_info *c, int lnum, - int offs, void *sbuf) + int offs, void *sbuf, int quiet) { void *buf = sbuf + offs; int err, len = c->leb_size - offs; @@ -278,8 +277,7 @@ struct ubifs_scan_leb *ubifs_scan(const struct ubifs_info *c, int lnum, cond_resched(); - ret = ubifs_scan_a_node(c, buf, len, lnum, offs, 0); - + ret = ubifs_scan_a_node(c, buf, len, lnum, offs, quiet); if (ret > 0) { /* Padding bytes or a valid padding node */ offs += ret; @@ -294,17 +292,18 @@ struct ubifs_scan_leb *ubifs_scan(const struct ubifs_info *c, int lnum, switch (ret) { case SCANNED_GARBAGE: - dbg_err("garbage"); + ubifs_err("garbage"); goto corrupted; case SCANNED_A_NODE: break; case SCANNED_A_CORRUPT_NODE: case SCANNED_A_BAD_PAD_NODE: - dbg_err("bad node"); + ubifs_err("bad node"); goto corrupted; default: - dbg_err("unknown"); - goto corrupted; + ubifs_err("unknown"); + err = -EINVAL; + goto error; } err = ubifs_add_snod(c, sleb, buf, offs); @@ -317,8 +316,12 @@ struct ubifs_scan_leb *ubifs_scan(const struct ubifs_info *c, int lnum, len -= node_len; } - if (offs % c->min_io_size) + if (offs % c->min_io_size) { + if (!quiet) + ubifs_err("empty space starts at non-aligned offset %d", + offs); goto corrupted; + } ubifs_end_scan(c, sleb, lnum, offs); @@ -327,18 +330,25 @@ struct ubifs_scan_leb *ubifs_scan(const struct ubifs_info *c, int lnum, break; for (; len; offs++, buf++, len--) if (*(uint8_t *)buf != 0xff) { - ubifs_err("corrupt empty space at LEB %d:%d", - lnum, offs); + if (!quiet) + ubifs_err("corrupt empty space at LEB %d:%d", + lnum, offs); goto corrupted; } return sleb; corrupted: - ubifs_scanned_corruption(c, lnum, offs, buf); + if (!quiet) { + ubifs_scanned_corruption(c, lnum, offs, buf); + ubifs_err("LEB %d scanning failed", lnum); + } err = -EUCLEAN; + ubifs_scan_destroy(sleb); + return ERR_PTR(err); + error: - ubifs_err("LEB %d scanning failed", lnum); + ubifs_err("LEB %d scanning failed, error %d", lnum, err); ubifs_scan_destroy(sleb); return ERR_PTR(err); } diff --git a/fs/ubifs/super.c b/fs/ubifs/super.c index 748ab67..dd9b668 100644 --- a/fs/ubifs/super.c +++ b/fs/ubifs/super.c @@ -3,18 +3,7 @@ * * Copyright (C) 2006-2008 Nokia Corporation. * - * 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. - * - * 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., 51 - * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * SPDX-License-Identifier: GPL-2.0+ * * Authors: Artem Bityutskiy (Битюцкий Артём) * Adrian Hunter @@ -26,103 +15,45 @@ * corresponding subsystems, but most of it is here. */ -#include "ubifs.h" +#define __UBOOT__ +#ifndef __UBOOT__ +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/ctype.h> +#include <linux/kthread.h> +#include <linux/parser.h> +#include <linux/seq_file.h> +#include <linux/mount.h> #include <linux/math64.h> +#include <linux/writeback.h> +#else -#define INODE_LOCKED_MAX 64 +#include <linux/compat.h> +#include <linux/stat.h> +#include <linux/err.h> +#include "ubifs.h" +#include <ubi_uboot.h> +#include <mtd/ubi-user.h> -struct super_block *ubifs_sb; -static struct inode *inodes_locked_down[INODE_LOCKED_MAX]; +struct dentry; +struct file; +struct iattr; +struct kstat; +struct vfsmount; -/* shrinker.c */ +#define INODE_LOCKED_MAX 64 -/* List of all UBIFS file-system instances */ -struct list_head ubifs_infos; +struct super_block *ubifs_sb; +LIST_HEAD(super_blocks); -/* linux/fs/super.c */ +static struct inode *inodes_locked_down[INODE_LOCKED_MAX]; -static int sb_set(struct super_block *sb, void *data) +int set_anon_super(struct super_block *s, void *data) { - dev_t *dev = data; - - sb->s_dev = *dev; return 0; } -/** - * sget - find or create a superblock - * @type: filesystem type superblock should belong to - * @test: comparison callback - * @set: setup callback - * @data: argument to each of them - */ -struct super_block *sget(struct file_system_type *type, - int (*test)(struct super_block *,void *), - int (*set)(struct super_block *,void *), - void *data) -{ - struct super_block *s = NULL; - int err; - - s = kzalloc(sizeof(struct super_block), GFP_USER); - if (!s) { - err = -ENOMEM; - return ERR_PTR(err); - } - - INIT_LIST_HEAD(&s->s_instances); - INIT_LIST_HEAD(&s->s_inodes); - s->s_time_gran = 1000000000; - - err = set(s, data); - if (err) { - return ERR_PTR(err); - } - s->s_type = type; - strncpy(s->s_id, type->name, sizeof(s->s_id)); - list_add(&s->s_instances, &type->fs_supers); - return s; -} - -/** - * validate_inode - validate inode. - * @c: UBIFS file-system description object - * @inode: the inode to validate - * - * This is a helper function for 'ubifs_iget()' which validates various fields - * of a newly built inode to make sure they contain sane values and prevent - * possible vulnerabilities. Returns zero if the inode is all right and - * a non-zero error code if not. - */ -static int validate_inode(struct ubifs_info *c, const struct inode *inode) -{ - int err; - const struct ubifs_inode *ui = ubifs_inode(inode); - - if (inode->i_size > c->max_inode_sz) { - ubifs_err("inode is too large (%lld)", - (long long)inode->i_size); - return 1; - } - - if (ui->compr_type < 0 || ui->compr_type >= UBIFS_COMPR_TYPES_CNT) { - ubifs_err("unknown compression type %d", ui->compr_type); - return 2; - } - - if (ui->data_len < 0 || ui->data_len > UBIFS_MAX_INO_DATA) - return 4; - - if (!ubifs_compr_present(ui->compr_type)) { - ubifs_warn("inode %lu uses '%s' compression, but it was not " - "compiled in", inode->i_ino, - ubifs_compr_name(ui->compr_type)); - } - - err = dbg_check_dir_size(c, inode); - return err; -} - struct inode *iget_locked(struct super_block *sb, unsigned long ino) { struct inode *inode; @@ -138,6 +69,10 @@ struct inode *iget_locked(struct super_block *sb, unsigned long ino) return inode; } +void iget_failed(struct inode *inode) +{ +} + int ubifs_iput(struct inode *inode) { list_del_init(&inode->i_sb_list); @@ -179,6 +114,125 @@ void iput(struct inode *inode) inodes_locked_down[i] = ino; } +/* from fs/inode.c */ +/** + * clear_nlink - directly zero an inode's link count + * @inode: inode + * + * This is a low-level filesystem helper to replace any + * direct filesystem manipulation of i_nlink. See + * drop_nlink() for why we care about i_nlink hitting zero. + */ +void clear_nlink(struct inode *inode) +{ + if (inode->i_nlink) { + inode->__i_nlink = 0; + atomic_long_inc(&inode->i_sb->s_remove_count); + } +} +EXPORT_SYMBOL(clear_nlink); + +/** + * set_nlink - directly set an inode's link count + * @inode: inode + * @nlink: new nlink (should be non-zero) + * + * This is a low-level filesystem helper to replace any + * direct filesystem manipulation of i_nlink. + */ +void set_nlink(struct inode *inode, unsigned int nlink) +{ + if (!nlink) { + clear_nlink(inode); + } else { + /* Yes, some filesystems do change nlink from zero to one */ + if (inode->i_nlink == 0) + atomic_long_dec(&inode->i_sb->s_remove_count); + + inode->__i_nlink = nlink; + } +} +EXPORT_SYMBOL(set_nlink); + +/* from include/linux/fs.h */ +static inline void i_uid_write(struct inode *inode, uid_t uid) +{ + inode->i_uid.val = uid; +} + +static inline void i_gid_write(struct inode *inode, gid_t gid) +{ + inode->i_gid.val = gid; +} + +void unlock_new_inode(struct inode *inode) +{ + return; +} +#endif + +/* + * Maximum amount of memory we may 'kmalloc()' without worrying that we are + * allocating too much. + */ +#define UBIFS_KMALLOC_OK (128*1024) + +/* Slab cache for UBIFS inodes */ +struct kmem_cache *ubifs_inode_slab; + +#ifndef __UBOOT__ +/* UBIFS TNC shrinker description */ +static struct shrinker ubifs_shrinker_info = { + .scan_objects = ubifs_shrink_scan, + .count_objects = ubifs_shrink_count, + .seeks = DEFAULT_SEEKS, +}; +#endif + +/** + * validate_inode - validate inode. + * @c: UBIFS file-system description object + * @inode: the inode to validate + * + * This is a helper function for 'ubifs_iget()' which validates various fields + * of a newly built inode to make sure they contain sane values and prevent + * possible vulnerabilities. Returns zero if the inode is all right and + * a non-zero error code if not. + */ +static int validate_inode(struct ubifs_info *c, const struct inode *inode) +{ + int err; + const struct ubifs_inode *ui = ubifs_inode(inode); + + if (inode->i_size > c->max_inode_sz) { + ubifs_err("inode is too large (%lld)", + (long long)inode->i_size); + return 1; + } + + if (ui->compr_type < 0 || ui->compr_type >= UBIFS_COMPR_TYPES_CNT) { + ubifs_err("unknown compression type %d", ui->compr_type); + return 2; + } + + if (ui->xattr_names + ui->xattr_cnt > XATTR_LIST_MAX) + return 3; + + if (ui->data_len < 0 || ui->data_len > UBIFS_MAX_INO_DATA) + return 4; + + if (ui->xattr && !S_ISREG(inode->i_mode)) + return 5; + + if (!ubifs_compr_present(ui->compr_type)) { + ubifs_warn("inode %lu uses '%s' compression, but it was not compiled in", + inode->i_ino, ubifs_compr_name(ui->compr_type)); + } + + err = dbg_check_dir(c, inode); + return err; +} + struct inode *ubifs_iget(struct super_block *sb, unsigned long inum) { int err; @@ -187,10 +241,13 @@ struct inode *ubifs_iget(struct super_block *sb, unsigned long inum) struct ubifs_info *c = sb->s_fs_info; struct inode *inode; struct ubifs_inode *ui; +#ifdef __UBOOT__ int i; +#endif dbg_gen("inode %lu", inum); +#ifdef __UBOOT__ /* * U-Boot special handling of locked down inodes via recovery * e.g. ubifs_recover_size() @@ -211,6 +268,7 @@ struct inode *ubifs_iget(struct super_block *sb, unsigned long inum) return inodes_locked_down[i]; } } +#endif inode = iget_locked(sb, inum); if (!inode) @@ -232,9 +290,9 @@ struct inode *ubifs_iget(struct super_block *sb, unsigned long inum) goto out_ino; inode->i_flags |= (S_NOCMTIME | S_NOATIME); - inode->i_nlink = le32_to_cpu(ino->nlink); - inode->i_uid = le32_to_cpu(ino->uid); - inode->i_gid = le32_to_cpu(ino->gid); + set_nlink(inode, le32_to_cpu(ino->nlink)); + i_uid_write(inode, le32_to_cpu(ino->uid)); + i_gid_write(inode, le32_to_cpu(ino->gid)); inode->i_atime.tv_sec = (int64_t)le64_to_cpu(ino->atime_sec); inode->i_atime.tv_nsec = le32_to_cpu(ino->atime_nsec); inode->i_mtime.tv_sec = (int64_t)le64_to_cpu(ino->mtime_sec); @@ -248,12 +306,101 @@ struct inode *ubifs_iget(struct super_block *sb, unsigned long inum) ui->flags = le32_to_cpu(ino->flags); ui->compr_type = le16_to_cpu(ino->compr_type); ui->creat_sqnum = le64_to_cpu(ino->creat_sqnum); + ui->xattr_cnt = le32_to_cpu(ino->xattr_cnt); + ui->xattr_size = le32_to_cpu(ino->xattr_size); + ui->xattr_names = le32_to_cpu(ino->xattr_names); ui->synced_i_size = ui->ui_size = inode->i_size; + ui->xattr = (ui->flags & UBIFS_XATTR_FL) ? 1 : 0; + err = validate_inode(c, inode); if (err) goto out_invalid; +#ifndef __UBOOT__ + /* Disable read-ahead */ + inode->i_mapping->backing_dev_info = &c->bdi; + + switch (inode->i_mode & S_IFMT) { + case S_IFREG: + inode->i_mapping->a_ops = &ubifs_file_address_operations; + inode->i_op = &ubifs_file_inode_operations; + inode->i_fop = &ubifs_file_operations; + if (ui->xattr) { + ui->data = kmalloc(ui->data_len + 1, GFP_NOFS); + if (!ui->data) { + err = -ENOMEM; + goto out_ino; + } + memcpy(ui->data, ino->data, ui->data_len); + ((char *)ui->data)[ui->data_len] = '\0'; + } else if (ui->data_len != 0) { + err = 10; + goto out_invalid; + } + break; + case S_IFDIR: + inode->i_op = &ubifs_dir_inode_operations; + inode->i_fop = &ubifs_dir_operations; + if (ui->data_len != 0) { + err = 11; + goto out_invalid; + } + break; + case S_IFLNK: + inode->i_op = &ubifs_symlink_inode_operations; + if (ui->data_len <= 0 || ui->data_len > UBIFS_MAX_INO_DATA) { + err = 12; + goto out_invalid; + } + ui->data = kmalloc(ui->data_len + 1, GFP_NOFS); + if (!ui->data) { + err = -ENOMEM; + goto out_ino; + } + memcpy(ui->data, ino->data, ui->data_len); + ((char *)ui->data)[ui->data_len] = '\0'; + break; + case S_IFBLK: + case S_IFCHR: + { + dev_t rdev; + union ubifs_dev_desc *dev; + + ui->data = kmalloc(sizeof(union ubifs_dev_desc), GFP_NOFS); + if (!ui->data) { + err = -ENOMEM; + goto out_ino; + } + + dev = (union ubifs_dev_desc *)ino->data; + if (ui->data_len == sizeof(dev->new)) + rdev = new_decode_dev(le32_to_cpu(dev->new)); + else if (ui->data_len == sizeof(dev->huge)) + rdev = huge_decode_dev(le64_to_cpu(dev->huge)); + else { + err = 13; + goto out_invalid; + } + memcpy(ui->data, ino->data, ui->data_len); + inode->i_op = &ubifs_file_inode_operations; + init_special_inode(inode, inode->i_mode, rdev); + break; + } + case S_IFSOCK: + case S_IFIFO: + inode->i_op = &ubifs_file_inode_operations; + init_special_inode(inode, inode->i_mode, 0); + if (ui->data_len != 0) { + err = 14; + goto out_invalid; + } + break; + default: + err = 15; + goto out_invalid; + } +#else if ((inode->i_mode & S_IFMT) == S_IFLNK) { if (ui->data_len <= 0 || ui->data_len > UBIFS_MAX_INO_DATA) { err = 12; @@ -267,23 +414,258 @@ struct inode *ubifs_iget(struct super_block *sb, unsigned long inum) memcpy(ui->data, ino->data, ui->data_len); ((char *)ui->data)[ui->data_len] = '\0'; } +#endif kfree(ino); - inode->i_state &= ~(I_LOCK | I_NEW); +#ifndef __UBOOT__ + ubifs_set_inode_flags(inode); +#endif + unlock_new_inode(inode); return inode; out_invalid: ubifs_err("inode %lu validation failed, error %d", inode->i_ino, err); - dbg_dump_node(c, ino); - dbg_dump_inode(c, inode); + ubifs_dump_node(c, ino); + ubifs_dump_inode(c, inode); err = -EINVAL; out_ino: kfree(ino); out: ubifs_err("failed to read inode %lu, error %d", inode->i_ino, err); + iget_failed(inode); return ERR_PTR(err); } +static struct inode *ubifs_alloc_inode(struct super_block *sb) +{ + struct ubifs_inode *ui; + + ui = kmem_cache_alloc(ubifs_inode_slab, GFP_NOFS); + if (!ui) + return NULL; + + memset((void *)ui + sizeof(struct inode), 0, + sizeof(struct ubifs_inode) - sizeof(struct inode)); + mutex_init(&ui->ui_mutex); + spin_lock_init(&ui->ui_lock); + return &ui->vfs_inode; +}; + +#ifndef __UBOOT__ +static void ubifs_i_callback(struct rcu_head *head) +{ + struct inode *inode = container_of(head, struct inode, i_rcu); + struct ubifs_inode *ui = ubifs_inode(inode); + kmem_cache_free(ubifs_inode_slab, ui); +} + +static void ubifs_destroy_inode(struct inode *inode) +{ + struct ubifs_inode *ui = ubifs_inode(inode); + + kfree(ui->data); + call_rcu(&inode->i_rcu, ubifs_i_callback); +} + +/* + * Note, Linux write-back code calls this without 'i_mutex'. + */ +static int ubifs_write_inode(struct inode *inode, struct writeback_control *wbc) +{ + int err = 0; + struct ubifs_info *c = inode->i_sb->s_fs_info; + struct ubifs_inode *ui = ubifs_inode(inode); + + ubifs_assert(!ui->xattr); + if (is_bad_inode(inode)) + return 0; + + mutex_lock(&ui->ui_mutex); + /* + * Due to races between write-back forced by budgeting + * (see 'sync_some_inodes()') and background write-back, the inode may + * have already been synchronized, do not do this again. This might + * also happen if it was synchronized in an VFS operation, e.g. + * 'ubifs_link()'. + */ + if (!ui->dirty) { + mutex_unlock(&ui->ui_mutex); + return 0; + } + + /* + * As an optimization, do not write orphan inodes to the media just + * because this is not needed. + */ + dbg_gen("inode %lu, mode %#x, nlink %u", + inode->i_ino, (int)inode->i_mode, inode->i_nlink); + if (inode->i_nlink) { + err = ubifs_jnl_write_inode(c, inode); + if (err) + ubifs_err("can't write inode %lu, error %d", + inode->i_ino, err); + else + err = dbg_check_inode_size(c, inode, ui->ui_size); + } + + ui->dirty = 0; + mutex_unlock(&ui->ui_mutex); + ubifs_release_dirty_inode_budget(c, ui); + return err; +} + +static void ubifs_evict_inode(struct inode *inode) +{ + int err; + struct ubifs_info *c = inode->i_sb->s_fs_info; + struct ubifs_inode *ui = ubifs_inode(inode); + + if (ui->xattr) + /* + * Extended attribute inode deletions are fully handled in + * 'ubifs_removexattr()'. These inodes are special and have + * limited usage, so there is nothing to do here. + */ + goto out; + + dbg_gen("inode %lu, mode %#x", inode->i_ino, (int)inode->i_mode); + ubifs_assert(!atomic_read(&inode->i_count)); + + truncate_inode_pages_final(&inode->i_data); + + if (inode->i_nlink) + goto done; + + if (is_bad_inode(inode)) + goto out; + + ui->ui_size = inode->i_size = 0; + err = ubifs_jnl_delete_inode(c, inode); + if (err) + /* + * Worst case we have a lost orphan inode wasting space, so a + * simple error message is OK here. + */ + ubifs_err("can't delete inode %lu, error %d", + inode->i_ino, err); + +out: + if (ui->dirty) + ubifs_release_dirty_inode_budget(c, ui); + else { + /* We've deleted something - clean the "no space" flags */ + c->bi.nospace = c->bi.nospace_rp = 0; + smp_wmb(); + } +done: + clear_inode(inode); +} +#endif + +static void ubifs_dirty_inode(struct inode *inode, int flags) +{ + struct ubifs_inode *ui = ubifs_inode(inode); + + ubifs_assert(mutex_is_locked(&ui->ui_mutex)); + if (!ui->dirty) { + ui->dirty = 1; + dbg_gen("inode %lu", inode->i_ino); + } +} + +#ifndef __UBOOT__ +static int ubifs_statfs(struct dentry *dentry, struct kstatfs *buf) +{ + struct ubifs_info *c = dentry->d_sb->s_fs_info; + unsigned long long free; + __le32 *uuid = (__le32 *)c->uuid; + + free = ubifs_get_free_space(c); + dbg_gen("free space %lld bytes (%lld blocks)", + free, free >> UBIFS_BLOCK_SHIFT); + + buf->f_type = UBIFS_SUPER_MAGIC; + buf->f_bsize = UBIFS_BLOCK_SIZE; + buf->f_blocks = c->block_cnt; + buf->f_bfree = free >> UBIFS_BLOCK_SHIFT; + if (free > c->report_rp_size) + buf->f_bavail = (free - c->report_rp_size) >> UBIFS_BLOCK_SHIFT; + else + buf->f_bavail = 0; + buf->f_files = 0; + buf->f_ffree = 0; + buf->f_namelen = UBIFS_MAX_NLEN; + buf->f_fsid.val[0] = le32_to_cpu(uuid[0]) ^ le32_to_cpu(uuid[2]); + buf->f_fsid.val[1] = le32_to_cpu(uuid[1]) ^ le32_to_cpu(uuid[3]); + ubifs_assert(buf->f_bfree <= c->block_cnt); + return 0; +} + +static int ubifs_show_options(struct seq_file *s, struct dentry *root) +{ + struct ubifs_info *c = root->d_sb->s_fs_info; + + if (c->mount_opts.unmount_mode == 2) + seq_printf(s, ",fast_unmount"); + else if (c->mount_opts.unmount_mode == 1) + seq_printf(s, ",norm_unmount"); + + if (c->mount_opts.bulk_read == 2) + seq_printf(s, ",bulk_read"); + else if (c->mount_opts.bulk_read == 1) + seq_printf(s, ",no_bulk_read"); + + if (c->mount_opts.chk_data_crc == 2) + seq_printf(s, ",chk_data_crc"); + else if (c->mount_opts.chk_data_crc == 1) + seq_printf(s, ",no_chk_data_crc"); + + if (c->mount_opts.override_compr) { + seq_printf(s, ",compr=%s", + ubifs_compr_name(c->mount_opts.compr_type)); + } + + return 0; +} + +static int ubifs_sync_fs(struct super_block *sb, int wait) +{ + int i, err; + struct ubifs_info *c = sb->s_fs_info; + + /* + * Zero @wait is just an advisory thing to help the file system shove + * lots of data into the queues, and there will be the second + * '->sync_fs()' call, with non-zero @wait. + */ + if (!wait) + return 0; + + /* + * Synchronize write buffers, because 'ubifs_run_commit()' does not + * do this if it waits for an already running commit. + */ + for (i = 0; i < c->jhead_cnt; i++) { + err = ubifs_wbuf_sync(&c->jheads[i].wbuf); + if (err) + return err; + } + + /* + * Strictly speaking, it is not necessary to commit the journal here, + * synchronizing write-buffers would be enough. But committing makes + * UBIFS free space predictions much more accurate, so we want to let + * the user be able to get more accurate results of 'statfs()' after + * they synchronize the file system. + */ + err = ubifs_run_commit(c); + if (err) + return err; + + return ubi_sync(c->vi.ubi_num); +} +#endif + /** * init_constants_early - initialize UBIFS constants. * @c: UBIFS file-system description object @@ -312,9 +694,12 @@ static int init_constants_early(struct ubifs_info *c) c->leb_cnt = c->vi.size; c->leb_size = c->vi.usable_leb_size; + c->leb_start = c->di.leb_start; c->half_leb_size = c->leb_size / 2; c->min_io_size = c->di.min_io_size; c->min_io_shift = fls(c->min_io_size) - 1; + c->max_write_size = c->di.max_write_size; + c->max_write_shift = fls(c->max_write_size) - 1; if (c->leb_size < UBIFS_MIN_LEB_SZ) { ubifs_err("too small LEBs (%d bytes), min. is %d bytes", @@ -334,6 +719,18 @@ static int init_constants_early(struct ubifs_info *c) } /* + * Maximum write size has to be greater or equivalent to min. I/O + * size, and be multiple of min. I/O size. + */ + if (c->max_write_size < c->min_io_size || + c->max_write_size % c->min_io_size || + !is_power_of_2(c->max_write_size)) { + ubifs_err("bad write buffer size %d for %d min. I/O unit", + c->max_write_size, c->min_io_size); + return -EINVAL; + } + + /* * UBIFS aligns all node to 8-byte boundary, so to make function in * io.c simpler, assume minimum I/O unit size to be 8 bytes if it is * less than 8. @@ -341,6 +738,10 @@ static int init_constants_early(struct ubifs_info *c) if (c->min_io_size < 8) { c->min_io_size = 8; c->min_io_shift = 3; + if (c->max_write_size < c->min_io_size) { + c->max_write_size = c->min_io_size; + c->max_write_shift = c->min_io_shift; + } } c->ref_node_alsz = ALIGN(UBIFS_REF_NODE_SZ, c->min_io_size); @@ -393,9 +794,33 @@ static int init_constants_early(struct ubifs_info *c) */ c->leb_overhead = c->leb_size % UBIFS_MAX_DATA_NODE_SZ; + /* Buffer size for bulk-reads */ + c->max_bu_buf_len = UBIFS_MAX_BULK_READ * UBIFS_MAX_DATA_NODE_SZ; + if (c->max_bu_buf_len > c->leb_size) + c->max_bu_buf_len = c->leb_size; return 0; } +/** + * bud_wbuf_callback - bud LEB write-buffer synchronization call-back. + * @c: UBIFS file-system description object + * @lnum: LEB the write-buffer was synchronized to + * @free: how many free bytes left in this LEB + * @pad: how many bytes were padded + * + * This is a callback function which is called by the I/O unit when the + * write-buffer is synchronized. We need this to correctly maintain space + * accounting in bud logical eraseblocks. This function returns zero in case of + * success and a negative error code in case of failure. + * + * This function actually belongs to the journal, but we keep it here because + * we want to keep it static. + */ +static int bud_wbuf_callback(struct ubifs_info *c, int lnum, int free, int pad) +{ + return ubifs_update_one_lp(c, lnum, free, pad, 0, 0); +} + /* * init_constants_sb - initialize UBIFS constants. * @c: UBIFS file-system description object @@ -426,8 +851,8 @@ static int init_constants_sb(struct ubifs_info *c) tmp = UBIFS_CS_NODE_SZ + UBIFS_REF_NODE_SZ * c->jhead_cnt; tmp = ALIGN(tmp, c->min_io_size); if (tmp > c->leb_size) { - dbg_err("too small LEB size %d, at least %d needed", - c->leb_size, tmp); + ubifs_err("too small LEB size %d, at least %d needed", + c->leb_size, tmp); return -EINVAL; } @@ -441,8 +866,8 @@ static int init_constants_sb(struct ubifs_info *c) tmp /= c->leb_size; tmp += 1; if (c->log_lebs < tmp) { - dbg_err("too small log %d LEBs, required min. %d LEBs", - c->log_lebs, tmp); + ubifs_err("too small log %d LEBs, required min. %d LEBs", + c->log_lebs, tmp); return -EINVAL; } @@ -451,11 +876,11 @@ static int init_constants_sb(struct ubifs_info *c) * be compressed and direntries are of the maximum size. * * Note, data, which may be stored in inodes is budgeted separately, so - * it is not included into 'c->inode_budget'. + * it is not included into 'c->bi.inode_budget'. */ - c->page_budget = UBIFS_MAX_DATA_NODE_SZ * UBIFS_BLOCKS_PER_PAGE; - c->inode_budget = UBIFS_INO_NODE_SZ; - c->dent_budget = UBIFS_MAX_DENT_NODE_SZ; + c->bi.page_budget = UBIFS_MAX_DATA_NODE_SZ * UBIFS_BLOCKS_PER_PAGE; + c->bi.inode_budget = UBIFS_INO_NODE_SZ; + c->bi.dent_budget = UBIFS_MAX_DENT_NODE_SZ; /* * When the amount of flash space used by buds becomes @@ -482,6 +907,8 @@ static int init_constants_sb(struct ubifs_info *c) if (err) return err; + /* Initialize effective LEB size used in budgeting calculations */ + c->idx_leb_size = c->leb_size - c->max_idx_node_sz; return 0; } @@ -497,7 +924,8 @@ static void init_constants_master(struct ubifs_info *c) { long long tmp64; - c->min_idx_lebs = ubifs_calc_min_idx_lebs(c); + c->bi.min_idx_lebs = ubifs_calc_min_idx_lebs(c); + c->report_rp_size = ubifs_reported_space(c, c->rp_size); /* * Calculate total amount of FS blocks. This number is not used @@ -515,6 +943,88 @@ static void init_constants_master(struct ubifs_info *c) } /** + * take_gc_lnum - reserve GC LEB. + * @c: UBIFS file-system description object + * + * This function ensures that the LEB reserved for garbage collection is marked + * as "taken" in lprops. We also have to set free space to LEB size and dirty + * space to zero, because lprops may contain out-of-date information if the + * file-system was un-mounted before it has been committed. This function + * returns zero in case of success and a negative error code in case of + * failure. + */ +static int take_gc_lnum(struct ubifs_info *c) +{ + int err; + + if (c->gc_lnum == -1) { + ubifs_err("no LEB for GC"); + return -EINVAL; + } + + /* And we have to tell lprops that this LEB is taken */ + err = ubifs_change_one_lp(c, c->gc_lnum, c->leb_size, 0, + LPROPS_TAKEN, 0, 0); + return err; +} + +/** + * alloc_wbufs - allocate write-buffers. + * @c: UBIFS file-system description object + * + * This helper function allocates and initializes UBIFS write-buffers. Returns + * zero in case of success and %-ENOMEM in case of failure. + */ +static int alloc_wbufs(struct ubifs_info *c) +{ + int i, err; + + c->jheads = kzalloc(c->jhead_cnt * sizeof(struct ubifs_jhead), + GFP_KERNEL); + if (!c->jheads) + return -ENOMEM; + + /* Initialize journal heads */ + for (i = 0; i < c->jhead_cnt; i++) { + INIT_LIST_HEAD(&c->jheads[i].buds_list); + err = ubifs_wbuf_init(c, &c->jheads[i].wbuf); + if (err) + return err; + + c->jheads[i].wbuf.sync_callback = &bud_wbuf_callback; + c->jheads[i].wbuf.jhead = i; + c->jheads[i].grouped = 1; + } + + /* + * Garbage Collector head does not need to be synchronized by timer. + * Also GC head nodes are not grouped. + */ + c->jheads[GCHD].wbuf.no_timer = 1; + c->jheads[GCHD].grouped = 0; + + return 0; +} + +/** + * free_wbufs - free write-buffers. + * @c: UBIFS file-system description object + */ +static void free_wbufs(struct ubifs_info *c) +{ + int i; + + if (c->jheads) { + for (i = 0; i < c->jhead_cnt; i++) { + kfree(c->jheads[i].wbuf.buf); + kfree(c->jheads[i].wbuf.inodes); + } + kfree(c->jheads); + c->jheads = NULL; + } +} + +/** * free_orphans - free orphans. * @c: UBIFS file-system description object */ @@ -533,13 +1043,27 @@ static void free_orphans(struct ubifs_info *c) orph = list_entry(c->orph_list.next, struct ubifs_orphan, list); list_del(&orph->list); kfree(orph); - dbg_err("orphan list not empty at unmount"); + ubifs_err("orphan list not empty at unmount"); } vfree(c->orph_buf); c->orph_buf = NULL; } +#ifndef __UBOOT__ +/** + * free_buds - free per-bud objects. + * @c: UBIFS file-system description object + */ +static void free_buds(struct ubifs_info *c) +{ + struct ubifs_bud *bud, *n; + + rbtree_postorder_for_each_entry_safe(bud, n, &c->buds, rb) + kfree(bud); +} +#endif + /** * check_volume_empty - check if the UBI volume is empty. * @c: UBIFS file-system description object @@ -555,7 +1079,7 @@ static int check_volume_empty(struct ubifs_info *c) c->empty = 1; for (lnum = 0; lnum < c->leb_cnt; lnum++) { - err = ubi_is_mapped(c->ubi, lnum); + err = ubifs_is_mapped(c, lnum); if (unlikely(err < 0)) return err; if (err == 1) { @@ -569,23 +1093,258 @@ static int check_volume_empty(struct ubifs_info *c) return 0; } +/* + * UBIFS mount options. + * + * Opt_fast_unmount: do not run a journal commit before un-mounting + * Opt_norm_unmount: run a journal commit before un-mounting + * Opt_bulk_read: enable bulk-reads + * Opt_no_bulk_read: disable bulk-reads + * Opt_chk_data_crc: check CRCs when reading data nodes + * Opt_no_chk_data_crc: do not check CRCs when reading data nodes + * Opt_override_compr: override default compressor + * Opt_err: just end of array marker + */ +enum { + Opt_fast_unmount, + Opt_norm_unmount, + Opt_bulk_read, + Opt_no_bulk_read, + Opt_chk_data_crc, + Opt_no_chk_data_crc, + Opt_override_compr, + Opt_err, +}; + +#ifndef __UBOOT__ +static const match_table_t tokens = { + {Opt_fast_unmount, "fast_unmount"}, + {Opt_norm_unmount, "norm_unmount"}, + {Opt_bulk_read, "bulk_read"}, + {Opt_no_bulk_read, "no_bulk_read"}, + {Opt_chk_data_crc, "chk_data_crc"}, + {Opt_no_chk_data_crc, "no_chk_data_crc"}, + {Opt_override_compr, "compr=%s"}, + {Opt_err, NULL}, +}; + +/** + * parse_standard_option - parse a standard mount option. + * @option: the option to parse + * + * Normally, standard mount options like "sync" are passed to file-systems as + * flags. However, when a "rootflags=" kernel boot parameter is used, they may + * be present in the options string. This function tries to deal with this + * situation and parse standard options. Returns 0 if the option was not + * recognized, and the corresponding integer flag if it was. + * + * UBIFS is only interested in the "sync" option, so do not check for anything + * else. + */ +static int parse_standard_option(const char *option) +{ + ubifs_msg("parse %s", option); + if (!strcmp(option, "sync")) + return MS_SYNCHRONOUS; + return 0; +} + +/** + * ubifs_parse_options - parse mount parameters. + * @c: UBIFS file-system description object + * @options: parameters to parse + * @is_remount: non-zero if this is FS re-mount + * + * This function parses UBIFS mount options and returns zero in case success + * and a negative error code in case of failure. + */ +static int ubifs_parse_options(struct ubifs_info *c, char *options, + int is_remount) +{ + char *p; + substring_t args[MAX_OPT_ARGS]; + + if (!options) + return 0; + + while ((p = strsep(&options, ","))) { + int token; + + if (!*p) + continue; + + token = match_token(p, tokens, args); + switch (token) { + /* + * %Opt_fast_unmount and %Opt_norm_unmount options are ignored. + * We accept them in order to be backward-compatible. But this + * should be removed at some point. + */ + case Opt_fast_unmount: + c->mount_opts.unmount_mode = 2; + break; + case Opt_norm_unmount: + c->mount_opts.unmount_mode = 1; + break; + case Opt_bulk_read: + c->mount_opts.bulk_read = 2; + c->bulk_read = 1; + break; + case Opt_no_bulk_read: + c->mount_opts.bulk_read = 1; + c->bulk_read = 0; + break; + case Opt_chk_data_crc: + c->mount_opts.chk_data_crc = 2; + c->no_chk_data_crc = 0; + break; + case Opt_no_chk_data_crc: + c->mount_opts.chk_data_crc = 1; + c->no_chk_data_crc = 1; + break; + case Opt_override_compr: + { + char *name = match_strdup(&args[0]); + + if (!name) + return -ENOMEM; + if (!strcmp(name, "none")) + c->mount_opts.compr_type = UBIFS_COMPR_NONE; + else if (!strcmp(name, "lzo")) + c->mount_opts.compr_type = UBIFS_COMPR_LZO; + else if (!strcmp(name, "zlib")) + c->mount_opts.compr_type = UBIFS_COMPR_ZLIB; + else { + ubifs_err("unknown compressor \"%s\"", name); + kfree(name); + return -EINVAL; + } + kfree(name); + c->mount_opts.override_compr = 1; + c->default_compr = c->mount_opts.compr_type; + break; + } + default: + { + unsigned long flag; + struct super_block *sb = c->vfs_sb; + + flag = parse_standard_option(p); + if (!flag) { + ubifs_err("unrecognized mount option \"%s\" or missing value", + p); + return -EINVAL; + } + sb->s_flags |= flag; + break; + } + } + } + + return 0; +} + +/** + * destroy_journal - destroy journal data structures. + * @c: UBIFS file-system description object + * + * This function destroys journal data structures including those that may have + * been created by recovery functions. + */ +static void destroy_journal(struct ubifs_info *c) +{ + while (!list_empty(&c->unclean_leb_list)) { + struct ubifs_unclean_leb *ucleb; + + ucleb = list_entry(c->unclean_leb_list.next, + struct ubifs_unclean_leb, list); + list_del(&ucleb->list); + kfree(ucleb); + } + while (!list_empty(&c->old_buds)) { + struct ubifs_bud *bud; + + bud = list_entry(c->old_buds.next, struct ubifs_bud, list); + list_del(&bud->list); + kfree(bud); + } + ubifs_destroy_idx_gc(c); + ubifs_destroy_size_tree(c); + ubifs_tnc_close(c); + free_buds(c); +} +#endif + +/** + * bu_init - initialize bulk-read information. + * @c: UBIFS file-system description object + */ +static void bu_init(struct ubifs_info *c) +{ + ubifs_assert(c->bulk_read == 1); + + if (c->bu.buf) + return; /* Already initialized */ + +again: + c->bu.buf = kmalloc(c->max_bu_buf_len, GFP_KERNEL | __GFP_NOWARN); + if (!c->bu.buf) { + if (c->max_bu_buf_len > UBIFS_KMALLOC_OK) { + c->max_bu_buf_len = UBIFS_KMALLOC_OK; + goto again; + } + + /* Just disable bulk-read */ + ubifs_warn("cannot allocate %d bytes of memory for bulk-read, disabling it", + c->max_bu_buf_len); + c->mount_opts.bulk_read = 1; + c->bulk_read = 0; + return; + } +} + +#ifndef __UBOOT__ +/** + * check_free_space - check if there is enough free space to mount. + * @c: UBIFS file-system description object + * + * This function makes sure UBIFS has enough free space to be mounted in + * read/write mode. UBIFS must always have some free space to allow deletions. + */ +static int check_free_space(struct ubifs_info *c) +{ + ubifs_assert(c->dark_wm > 0); + if (c->lst.total_free + c->lst.total_dirty < c->dark_wm) { + ubifs_err("insufficient free space to mount in R/W mode"); + ubifs_dump_budg(c, &c->bi); + ubifs_dump_lprops(c); + return -ENOSPC; + } + return 0; +} +#endif + /** * mount_ubifs - mount UBIFS file-system. * @c: UBIFS file-system description object * * This function mounts UBIFS file system. Returns zero in case of success and * a negative error code in case of failure. - * - * Note, the function does not de-allocate resources it it fails half way - * through, and the caller has to do this instead. */ static int mount_ubifs(struct ubifs_info *c) { - struct super_block *sb = c->vfs_sb; - int err, mounted_read_only = (sb->s_flags & MS_RDONLY); - long long x; + int err; + long long x, y; size_t sz; + c->ro_mount = !!(c->vfs_sb->s_flags & MS_RDONLY); +#ifdef __UBOOT__ + if (!c->ro_mount) { + printf("UBIFS: only ro mode in U-Boot allowed.\n"); + return -EACCES; + } +#endif + err = init_constants_early(c); if (err) return err; @@ -598,7 +1357,7 @@ static int mount_ubifs(struct ubifs_info *c) if (err) goto out_free; - if (c->empty && (mounted_read_only || c->ro_media)) { + if (c->empty && (c->ro_mount || c->ro_media)) { /* * This UBI volume is empty, and read-only, or the file system * is mounted read-only - we cannot format it. @@ -609,7 +1368,7 @@ static int mount_ubifs(struct ubifs_info *c) goto out_free; } - if (c->ro_media && !mounted_read_only) { + if (c->ro_media && !c->ro_mount) { ubifs_err("cannot mount read-write - read-only media"); err = -EROFS; goto out_free; @@ -629,11 +1388,27 @@ static int mount_ubifs(struct ubifs_info *c) if (!c->sbuf) goto out_free; - /* - * We have to check all CRCs, even for data nodes, when we mount the FS - * (specifically, when we are replaying). - */ - c->always_chk_crc = 1; +#ifndef __UBOOT__ + if (!c->ro_mount) { + c->ileb_buf = vmalloc(c->leb_size); + if (!c->ileb_buf) + goto out_free; + } +#endif + + if (c->bulk_read == 1) + bu_init(c); + +#ifndef __UBOOT__ + if (!c->ro_mount) { + c->write_reserve_buf = kmalloc(COMPRESSED_DATA_NODE_BUF_SZ, + GFP_KERNEL); + if (!c->write_reserve_buf) + goto out_free; + } +#endif + + c->mounting = 1; err = ubifs_read_superblock(c); if (err) @@ -646,11 +1421,10 @@ static int mount_ubifs(struct ubifs_info *c) if (!ubifs_compr_present(c->default_compr)) { ubifs_err("'compressor \"%s\" is not compiled in", ubifs_compr_name(c->default_compr)); + err = -ENOTSUPP; goto out_free; } - dbg_failure_mode_registration(c); - err = init_constants_sb(c); if (err) goto out_free; @@ -663,7 +1437,25 @@ static int mount_ubifs(struct ubifs_info *c) goto out_free; } + err = alloc_wbufs(c); + if (err) + goto out_cbuf; + sprintf(c->bgt_name, BGT_NAME_PATTERN, c->vi.ubi_num, c->vi.vol_id); +#ifndef __UBOOT__ + if (!c->ro_mount) { + /* Create background thread */ + c->bgt = kthread_create(ubifs_bg_thread, c, "%s", c->bgt_name); + if (IS_ERR(c->bgt)) { + err = PTR_ERR(c->bgt); + c->bgt = NULL; + ubifs_err("cannot spawn \"%s\", error %d", + c->bgt_name, err); + goto out_wbufs; + } + wake_up_process(c->bgt); + } +#endif err = ubifs_read_master(c); if (err) @@ -676,118 +1468,208 @@ static int mount_ubifs(struct ubifs_info *c) c->need_recovery = 1; } - err = ubifs_lpt_init(c, 1, !mounted_read_only); +#ifndef __UBOOT__ + if (c->need_recovery && !c->ro_mount) { + err = ubifs_recover_inl_heads(c, c->sbuf); + if (err) + goto out_master; + } +#endif + + err = ubifs_lpt_init(c, 1, !c->ro_mount); if (err) - goto out_lpt; + goto out_master; + +#ifndef __UBOOT__ + if (!c->ro_mount && c->space_fixup) { + err = ubifs_fixup_free_space(c); + if (err) + goto out_lpt; + } + + if (!c->ro_mount) { + /* + * Set the "dirty" flag so that if we reboot uncleanly we + * will notice this immediately on the next mount. + */ + c->mst_node->flags |= cpu_to_le32(UBIFS_MST_DIRTY); + err = ubifs_write_master(c); + if (err) + goto out_lpt; + } +#endif - err = dbg_check_idx_size(c, c->old_idx_sz); + err = dbg_check_idx_size(c, c->bi.old_idx_sz); if (err) goto out_lpt; +#ifndef __UBOOT__ err = ubifs_replay_journal(c); if (err) goto out_journal; +#endif + + /* Calculate 'min_idx_lebs' after journal replay */ + c->bi.min_idx_lebs = ubifs_calc_min_idx_lebs(c); - err = ubifs_mount_orphans(c, c->need_recovery, mounted_read_only); + err = ubifs_mount_orphans(c, c->need_recovery, c->ro_mount); if (err) goto out_orphans; - if (c->need_recovery) { + if (!c->ro_mount) { +#ifndef __UBOOT__ + int lnum; + + err = check_free_space(c); + if (err) + goto out_orphans; + + /* Check for enough log space */ + lnum = c->lhead_lnum + 1; + if (lnum >= UBIFS_LOG_LNUM + c->log_lebs) + lnum = UBIFS_LOG_LNUM; + if (lnum == c->ltail_lnum) { + err = ubifs_consolidate_log(c); + if (err) + goto out_orphans; + } + + if (c->need_recovery) { + err = ubifs_recover_size(c); + if (err) + goto out_orphans; + err = ubifs_rcvry_gc_commit(c); + if (err) + goto out_orphans; + } else { + err = take_gc_lnum(c); + if (err) + goto out_orphans; + + /* + * GC LEB may contain garbage if there was an unclean + * reboot, and it should be un-mapped. + */ + err = ubifs_leb_unmap(c, c->gc_lnum); + if (err) + goto out_orphans; + } + + err = dbg_check_lprops(c); + if (err) + goto out_orphans; +#endif + } else if (c->need_recovery) { err = ubifs_recover_size(c); if (err) goto out_orphans; + } else { + /* + * Even if we mount read-only, we have to set space in GC LEB + * to proper value because this affects UBIFS free space + * reporting. We do not want to have a situation when + * re-mounting from R/O to R/W changes amount of free space. + */ + err = take_gc_lnum(c); + if (err) + goto out_orphans; } +#ifndef __UBOOT__ spin_lock(&ubifs_infos_lock); list_add_tail(&c->infos_list, &ubifs_infos); spin_unlock(&ubifs_infos_lock); +#endif if (c->need_recovery) { - if (mounted_read_only) + if (c->ro_mount) ubifs_msg("recovery deferred"); else { c->need_recovery = 0; ubifs_msg("recovery completed"); + /* + * GC LEB has to be empty and taken at this point. But + * the journal head LEBs may also be accounted as + * "empty taken" if they are empty. + */ + ubifs_assert(c->lst.taken_empty_lebs > 0); } - } + } else + ubifs_assert(c->lst.taken_empty_lebs > 0); err = dbg_check_filesystem(c); if (err) goto out_infos; - c->always_chk_crc = 0; + err = dbg_debugfs_init_fs(c); + if (err) + goto out_infos; + + c->mounting = 0; - ubifs_msg("mounted UBI device %d, volume %d, name \"%s\"", - c->vi.ubi_num, c->vi.vol_id, c->vi.name); - if (mounted_read_only) - ubifs_msg("mounted read-only"); + ubifs_msg("mounted UBI device %d, volume %d, name \"%s\"%s", + c->vi.ubi_num, c->vi.vol_id, c->vi.name, + c->ro_mount ? ", R/O mode" : ""); x = (long long)c->main_lebs * c->leb_size; - ubifs_msg("file system size: %lld bytes (%lld KiB, %lld MiB, %d " - "LEBs)", x, x >> 10, x >> 20, c->main_lebs); - x = (long long)c->log_lebs * c->leb_size + c->max_bud_bytes; - ubifs_msg("journal size: %lld bytes (%lld KiB, %lld MiB, %d " - "LEBs)", x, x >> 10, x >> 20, c->log_lebs + c->max_bud_cnt); - ubifs_msg("media format: w%d/r%d (latest is w%d/r%d)", + y = (long long)c->log_lebs * c->leb_size + c->max_bud_bytes; + ubifs_msg("LEB size: %d bytes (%d KiB), min./max. I/O unit sizes: %d bytes/%d bytes", + c->leb_size, c->leb_size >> 10, c->min_io_size, + c->max_write_size); + ubifs_msg("FS size: %lld bytes (%lld MiB, %d LEBs), journal size %lld bytes (%lld MiB, %d LEBs)", + x, x >> 20, c->main_lebs, + y, y >> 20, c->log_lebs + c->max_bud_cnt); + ubifs_msg("reserved for root: %llu bytes (%llu KiB)", + c->report_rp_size, c->report_rp_size >> 10); + ubifs_msg("media format: w%d/r%d (latest is w%d/r%d), UUID %pUB%s", c->fmt_version, c->ro_compat_version, - UBIFS_FORMAT_VERSION, UBIFS_RO_COMPAT_VERSION); - ubifs_msg("default compressor: %s", ubifs_compr_name(c->default_compr)); - ubifs_msg("reserved for root: %llu bytes (%llu KiB)", - c->report_rp_size, c->report_rp_size >> 10); - - dbg_msg("min. I/O unit size: %d bytes", c->min_io_size); - dbg_msg("LEB size: %d bytes (%d KiB)", - c->leb_size, c->leb_size >> 10); - dbg_msg("data journal heads: %d", + UBIFS_FORMAT_VERSION, UBIFS_RO_COMPAT_VERSION, c->uuid, + c->big_lpt ? ", big LPT model" : ", small LPT model"); + + dbg_gen("default compressor: %s", ubifs_compr_name(c->default_compr)); + dbg_gen("data journal heads: %d", c->jhead_cnt - NONDATA_JHEADS_CNT); - dbg_msg("UUID: %02X%02X%02X%02X-%02X%02X" - "-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X", - c->uuid[0], c->uuid[1], c->uuid[2], c->uuid[3], - c->uuid[4], c->uuid[5], c->uuid[6], c->uuid[7], - c->uuid[8], c->uuid[9], c->uuid[10], c->uuid[11], - c->uuid[12], c->uuid[13], c->uuid[14], c->uuid[15]); - dbg_msg("big_lpt %d", c->big_lpt); - dbg_msg("log LEBs: %d (%d - %d)", + dbg_gen("log LEBs: %d (%d - %d)", c->log_lebs, UBIFS_LOG_LNUM, c->log_last); - dbg_msg("LPT area LEBs: %d (%d - %d)", + dbg_gen("LPT area LEBs: %d (%d - %d)", c->lpt_lebs, c->lpt_first, c->lpt_last); - dbg_msg("orphan area LEBs: %d (%d - %d)", + dbg_gen("orphan area LEBs: %d (%d - %d)", c->orph_lebs, c->orph_first, c->orph_last); - dbg_msg("main area LEBs: %d (%d - %d)", + dbg_gen("main area LEBs: %d (%d - %d)", c->main_lebs, c->main_first, c->leb_cnt - 1); - dbg_msg("index LEBs: %d", c->lst.idx_lebs); - dbg_msg("total index bytes: %lld (%lld KiB, %lld MiB)", - c->old_idx_sz, c->old_idx_sz >> 10, c->old_idx_sz >> 20); - dbg_msg("key hash type: %d", c->key_hash_type); - dbg_msg("tree fanout: %d", c->fanout); - dbg_msg("reserved GC LEB: %d", c->gc_lnum); - dbg_msg("first main LEB: %d", c->main_first); - dbg_msg("max. znode size %d", c->max_znode_sz); - dbg_msg("max. index node size %d", c->max_idx_node_sz); - dbg_msg("node sizes: data %zu, inode %zu, dentry %zu", + dbg_gen("index LEBs: %d", c->lst.idx_lebs); + dbg_gen("total index bytes: %lld (%lld KiB, %lld MiB)", + c->bi.old_idx_sz, c->bi.old_idx_sz >> 10, + c->bi.old_idx_sz >> 20); + dbg_gen("key hash type: %d", c->key_hash_type); + dbg_gen("tree fanout: %d", c->fanout); + dbg_gen("reserved GC LEB: %d", c->gc_lnum); + dbg_gen("max. znode size %d", c->max_znode_sz); + dbg_gen("max. index node size %d", c->max_idx_node_sz); + dbg_gen("node sizes: data %zu, inode %zu, dentry %zu", UBIFS_DATA_NODE_SZ, UBIFS_INO_NODE_SZ, UBIFS_DENT_NODE_SZ); - dbg_msg("node sizes: trun %zu, sb %zu, master %zu", + dbg_gen("node sizes: trun %zu, sb %zu, master %zu", UBIFS_TRUN_NODE_SZ, UBIFS_SB_NODE_SZ, UBIFS_MST_NODE_SZ); - dbg_msg("node sizes: ref %zu, cmt. start %zu, orph %zu", + dbg_gen("node sizes: ref %zu, cmt. start %zu, orph %zu", UBIFS_REF_NODE_SZ, UBIFS_CS_NODE_SZ, UBIFS_ORPH_NODE_SZ); - dbg_msg("max. node sizes: data %zu, inode %zu dentry %zu", + dbg_gen("max. node sizes: data %zu, inode %zu dentry %zu, idx %d", UBIFS_MAX_DATA_NODE_SZ, UBIFS_MAX_INO_NODE_SZ, - UBIFS_MAX_DENT_NODE_SZ); - dbg_msg("dead watermark: %d", c->dead_wm); - dbg_msg("dark watermark: %d", c->dark_wm); - dbg_msg("LEB overhead: %d", c->leb_overhead); + UBIFS_MAX_DENT_NODE_SZ, ubifs_idx_node_sz(c, c->fanout)); + dbg_gen("dead watermark: %d", c->dead_wm); + dbg_gen("dark watermark: %d", c->dark_wm); + dbg_gen("LEB overhead: %d", c->leb_overhead); x = (long long)c->main_lebs * c->dark_wm; - dbg_msg("max. dark space: %lld (%lld KiB, %lld MiB)", + dbg_gen("max. dark space: %lld (%lld KiB, %lld MiB)", x, x >> 10, x >> 20); - dbg_msg("maximum bud bytes: %lld (%lld KiB, %lld MiB)", + dbg_gen("maximum bud bytes: %lld (%lld KiB, %lld MiB)", c->max_bud_bytes, c->max_bud_bytes >> 10, c->max_bud_bytes >> 20); - dbg_msg("BG commit bud bytes: %lld (%lld KiB, %lld MiB)", + dbg_gen("BG commit bud bytes: %lld (%lld KiB, %lld MiB)", c->bg_bud_bytes, c->bg_bud_bytes >> 10, c->bg_bud_bytes >> 20); - dbg_msg("current bud bytes %lld (%lld KiB, %lld MiB)", + dbg_gen("current bud bytes %lld (%lld KiB, %lld MiB)", c->bud_bytes, c->bud_bytes >> 10, c->bud_bytes >> 20); - dbg_msg("max. seq. number: %llu", c->max_sqnum); - dbg_msg("commit number: %llu", c->cmt_no); + dbg_gen("max. seq. number: %llu", c->max_sqnum); + dbg_gen("commit number: %llu", c->cmt_no); return 0; @@ -797,7 +1679,10 @@ out_infos: spin_unlock(&ubifs_infos_lock); out_orphans: free_orphans(c); +#ifndef __UBOOT__ out_journal: + destroy_journal(c); +#endif out_lpt: ubifs_lpt_free(c, 0); out_master: @@ -805,8 +1690,15 @@ out_master: kfree(c->rcvrd_mst_node); if (c->bgt) kthread_stop(c->bgt); +#ifndef __UBOOT__ +out_wbufs: +#endif + free_wbufs(c); +out_cbuf: kfree(c->cbuf); out_free: + kfree(c->write_reserve_buf); + kfree(c->bu.buf); vfree(c->ileb_buf); vfree(c->sbuf); kfree(c->bottom_up_buf); @@ -823,57 +1715,448 @@ out_free: * through mounting (error path cleanup function). So it has to make sure the * resource was actually allocated before freeing it. */ +#ifndef __UBOOT__ +static void ubifs_umount(struct ubifs_info *c) +#else void ubifs_umount(struct ubifs_info *c) +#endif { dbg_gen("un-mounting UBI device %d, volume %d", c->vi.ubi_num, c->vi.vol_id); + dbg_debugfs_exit_fs(c); spin_lock(&ubifs_infos_lock); list_del(&c->infos_list); spin_unlock(&ubifs_infos_lock); +#ifndef __UBOOT__ if (c->bgt) kthread_stop(c->bgt); + destroy_journal(c); +#endif + free_wbufs(c); free_orphans(c); ubifs_lpt_free(c, 0); kfree(c->cbuf); kfree(c->rcvrd_mst_node); kfree(c->mst_node); + kfree(c->write_reserve_buf); + kfree(c->bu.buf); vfree(c->ileb_buf); vfree(c->sbuf); kfree(c->bottom_up_buf); ubifs_debugging_exit(c); - +#ifdef __UBOOT__ /* Finally free U-Boot's global copy of superblock */ if (ubifs_sb != NULL) { free(ubifs_sb->s_fs_info); free(ubifs_sb); } +#endif +} + +#ifndef __UBOOT__ +/** + * ubifs_remount_rw - re-mount in read-write mode. + * @c: UBIFS file-system description object + * + * UBIFS avoids allocating many unnecessary resources when mounted in read-only + * mode. This function allocates the needed resources and re-mounts UBIFS in + * read-write mode. + */ +static int ubifs_remount_rw(struct ubifs_info *c) +{ + int err, lnum; + + if (c->rw_incompat) { + ubifs_err("the file-system is not R/W-compatible"); + ubifs_msg("on-flash format version is w%d/r%d, but software only supports up to version w%d/r%d", + c->fmt_version, c->ro_compat_version, + UBIFS_FORMAT_VERSION, UBIFS_RO_COMPAT_VERSION); + return -EROFS; + } + + mutex_lock(&c->umount_mutex); + dbg_save_space_info(c); + c->remounting_rw = 1; + c->ro_mount = 0; + + if (c->space_fixup) { + err = ubifs_fixup_free_space(c); + if (err) + goto out; + } + + err = check_free_space(c); + if (err) + goto out; + + if (c->old_leb_cnt != c->leb_cnt) { + struct ubifs_sb_node *sup; + + sup = ubifs_read_sb_node(c); + if (IS_ERR(sup)) { + err = PTR_ERR(sup); + goto out; + } + sup->leb_cnt = cpu_to_le32(c->leb_cnt); + err = ubifs_write_sb_node(c, sup); + kfree(sup); + if (err) + goto out; + } + + if (c->need_recovery) { + ubifs_msg("completing deferred recovery"); + err = ubifs_write_rcvrd_mst_node(c); + if (err) + goto out; + err = ubifs_recover_size(c); + if (err) + goto out; + err = ubifs_clean_lebs(c, c->sbuf); + if (err) + goto out; + err = ubifs_recover_inl_heads(c, c->sbuf); + if (err) + goto out; + } else { + /* A readonly mount is not allowed to have orphans */ + ubifs_assert(c->tot_orphans == 0); + err = ubifs_clear_orphans(c); + if (err) + goto out; + } + + if (!(c->mst_node->flags & cpu_to_le32(UBIFS_MST_DIRTY))) { + c->mst_node->flags |= cpu_to_le32(UBIFS_MST_DIRTY); + err = ubifs_write_master(c); + if (err) + goto out; + } + + c->ileb_buf = vmalloc(c->leb_size); + if (!c->ileb_buf) { + err = -ENOMEM; + goto out; + } + + c->write_reserve_buf = kmalloc(COMPRESSED_DATA_NODE_BUF_SZ, GFP_KERNEL); + if (!c->write_reserve_buf) { + err = -ENOMEM; + goto out; + } + + err = ubifs_lpt_init(c, 0, 1); + if (err) + goto out; + + /* Create background thread */ + c->bgt = kthread_create(ubifs_bg_thread, c, "%s", c->bgt_name); + if (IS_ERR(c->bgt)) { + err = PTR_ERR(c->bgt); + c->bgt = NULL; + ubifs_err("cannot spawn \"%s\", error %d", + c->bgt_name, err); + goto out; + } + wake_up_process(c->bgt); + + c->orph_buf = vmalloc(c->leb_size); + if (!c->orph_buf) { + err = -ENOMEM; + goto out; + } + + /* Check for enough log space */ + lnum = c->lhead_lnum + 1; + if (lnum >= UBIFS_LOG_LNUM + c->log_lebs) + lnum = UBIFS_LOG_LNUM; + if (lnum == c->ltail_lnum) { + err = ubifs_consolidate_log(c); + if (err) + goto out; + } + + if (c->need_recovery) + err = ubifs_rcvry_gc_commit(c); + else + err = ubifs_leb_unmap(c, c->gc_lnum); + if (err) + goto out; + + dbg_gen("re-mounted read-write"); + c->remounting_rw = 0; + + if (c->need_recovery) { + c->need_recovery = 0; + ubifs_msg("deferred recovery completed"); + } else { + /* + * Do not run the debugging space check if the were doing + * recovery, because when we saved the information we had the + * file-system in a state where the TNC and lprops has been + * modified in memory, but all the I/O operations (including a + * commit) were deferred. So the file-system was in + * "non-committed" state. Now the file-system is in committed + * state, and of course the amount of free space will change + * because, for example, the old index size was imprecise. + */ + err = dbg_check_space_info(c); + } + + mutex_unlock(&c->umount_mutex); + return err; + +out: + c->ro_mount = 1; + vfree(c->orph_buf); + c->orph_buf = NULL; + if (c->bgt) { + kthread_stop(c->bgt); + c->bgt = NULL; + } + free_wbufs(c); + kfree(c->write_reserve_buf); + c->write_reserve_buf = NULL; + vfree(c->ileb_buf); + c->ileb_buf = NULL; + ubifs_lpt_free(c, 1); + c->remounting_rw = 0; + mutex_unlock(&c->umount_mutex); + return err; +} + +/** + * ubifs_remount_ro - re-mount in read-only mode. + * @c: UBIFS file-system description object + * + * We assume VFS has stopped writing. Possibly the background thread could be + * running a commit, however kthread_stop will wait in that case. + */ +static void ubifs_remount_ro(struct ubifs_info *c) +{ + int i, err; + + ubifs_assert(!c->need_recovery); + ubifs_assert(!c->ro_mount); + + mutex_lock(&c->umount_mutex); + if (c->bgt) { + kthread_stop(c->bgt); + c->bgt = NULL; + } + + dbg_save_space_info(c); + + for (i = 0; i < c->jhead_cnt; i++) + ubifs_wbuf_sync(&c->jheads[i].wbuf); + + c->mst_node->flags &= ~cpu_to_le32(UBIFS_MST_DIRTY); + c->mst_node->flags |= cpu_to_le32(UBIFS_MST_NO_ORPHS); + c->mst_node->gc_lnum = cpu_to_le32(c->gc_lnum); + err = ubifs_write_master(c); + if (err) + ubifs_ro_mode(c, err); + + vfree(c->orph_buf); + c->orph_buf = NULL; + kfree(c->write_reserve_buf); + c->write_reserve_buf = NULL; + vfree(c->ileb_buf); + c->ileb_buf = NULL; + ubifs_lpt_free(c, 1); + c->ro_mount = 1; + err = dbg_check_space_info(c); + if (err) + ubifs_ro_mode(c, err); + mutex_unlock(&c->umount_mutex); +} + +static void ubifs_put_super(struct super_block *sb) +{ + int i; + struct ubifs_info *c = sb->s_fs_info; + + ubifs_msg("un-mount UBI device %d, volume %d", c->vi.ubi_num, + c->vi.vol_id); + + /* + * The following asserts are only valid if there has not been a failure + * of the media. For example, there will be dirty inodes if we failed + * to write them back because of I/O errors. + */ + if (!c->ro_error) { + ubifs_assert(c->bi.idx_growth == 0); + ubifs_assert(c->bi.dd_growth == 0); + ubifs_assert(c->bi.data_growth == 0); + } + + /* + * The 'c->umount_lock' prevents races between UBIFS memory shrinker + * and file system un-mount. Namely, it prevents the shrinker from + * picking this superblock for shrinking - it will be just skipped if + * the mutex is locked. + */ + mutex_lock(&c->umount_mutex); + if (!c->ro_mount) { + /* + * First of all kill the background thread to make sure it does + * not interfere with un-mounting and freeing resources. + */ + if (c->bgt) { + kthread_stop(c->bgt); + c->bgt = NULL; + } + + /* + * On fatal errors c->ro_error is set to 1, in which case we do + * not write the master node. + */ + if (!c->ro_error) { + int err; + + /* Synchronize write-buffers */ + for (i = 0; i < c->jhead_cnt; i++) + ubifs_wbuf_sync(&c->jheads[i].wbuf); + + /* + * We are being cleanly unmounted which means the + * orphans were killed - indicate this in the master + * node. Also save the reserved GC LEB number. + */ + c->mst_node->flags &= ~cpu_to_le32(UBIFS_MST_DIRTY); + c->mst_node->flags |= cpu_to_le32(UBIFS_MST_NO_ORPHS); + c->mst_node->gc_lnum = cpu_to_le32(c->gc_lnum); + err = ubifs_write_master(c); + if (err) + /* + * Recovery will attempt to fix the master area + * next mount, so we just print a message and + * continue to unmount normally. + */ + ubifs_err("failed to write master node, error %d", + err); + } else { +#ifndef __UBOOT__ + for (i = 0; i < c->jhead_cnt; i++) + /* Make sure write-buffer timers are canceled */ + hrtimer_cancel(&c->jheads[i].wbuf.timer); +#endif + } + } + + ubifs_umount(c); +#ifndef __UBOOT__ + bdi_destroy(&c->bdi); +#endif + ubi_close_volume(c->ubi); + mutex_unlock(&c->umount_mutex); +} +#endif + +#ifndef __UBOOT__ +static int ubifs_remount_fs(struct super_block *sb, int *flags, char *data) +{ + int err; + struct ubifs_info *c = sb->s_fs_info; + + sync_filesystem(sb); + dbg_gen("old flags %#lx, new flags %#x", sb->s_flags, *flags); + + err = ubifs_parse_options(c, data, 1); + if (err) { + ubifs_err("invalid or unknown remount parameter"); + return err; + } + + if (c->ro_mount && !(*flags & MS_RDONLY)) { + if (c->ro_error) { + ubifs_msg("cannot re-mount R/W due to prior errors"); + return -EROFS; + } + if (c->ro_media) { + ubifs_msg("cannot re-mount R/W - UBI volume is R/O"); + return -EROFS; + } + err = ubifs_remount_rw(c); + if (err) + return err; + } else if (!c->ro_mount && (*flags & MS_RDONLY)) { + if (c->ro_error) { + ubifs_msg("cannot re-mount R/O due to prior errors"); + return -EROFS; + } + ubifs_remount_ro(c); + } + + if (c->bulk_read == 1) + bu_init(c); + else { + dbg_gen("disable bulk-read"); + kfree(c->bu.buf); + c->bu.buf = NULL; + } + + ubifs_assert(c->lst.taken_empty_lebs > 0); + return 0; } +#endif + +const struct super_operations ubifs_super_operations = { + .alloc_inode = ubifs_alloc_inode, +#ifndef __UBOOT__ + .destroy_inode = ubifs_destroy_inode, + .put_super = ubifs_put_super, + .write_inode = ubifs_write_inode, + .evict_inode = ubifs_evict_inode, + .statfs = ubifs_statfs, +#endif + .dirty_inode = ubifs_dirty_inode, +#ifndef __UBOOT__ + .remount_fs = ubifs_remount_fs, + .show_options = ubifs_show_options, + .sync_fs = ubifs_sync_fs, +#endif +}; /** * open_ubi - parse UBI device name string and open the UBI device. * @name: UBI volume name * @mode: UBI volume open mode * - * There are several ways to specify UBI volumes when mounting UBIFS: - * o ubiX_Y - UBI device number X, volume Y; - * o ubiY - UBI device number 0, volume Y; + * The primary method of mounting UBIFS is by specifying the UBI volume + * character device node path. However, UBIFS may also be mounted withoug any + * character device node using one of the following methods: + * + * o ubiX_Y - mount UBI device number X, volume Y; + * o ubiY - mount UBI device number 0, volume Y; * o ubiX:NAME - mount UBI device X, volume with name NAME; * o ubi:NAME - mount UBI device 0, volume with name NAME. * * Alternative '!' separator may be used instead of ':' (because some shells * like busybox may interpret ':' as an NFS host name separator). This function - * returns ubi volume object in case of success and a negative error code in - * case of failure. + * returns UBI volume description object in case of success and a negative + * error code in case of failure. */ static struct ubi_volume_desc *open_ubi(const char *name, int mode) { +#ifndef __UBOOT__ + struct ubi_volume_desc *ubi; +#endif int dev, vol; char *endptr; +#ifndef __UBOOT__ + /* First, try to open using the device node path method */ + ubi = ubi_open_volume_path(name, mode); + if (!IS_ERR(ubi)) + return ubi; +#endif + + /* Try the "nodev" method */ if (name[0] != 'u' || name[1] != 'b' || name[2] != 'i') return ERR_PTR(-EINVAL); @@ -905,78 +2188,106 @@ static struct ubi_volume_desc *open_ubi(const char *name, int mode) return ERR_PTR(-EINVAL); } -static int ubifs_fill_super(struct super_block *sb, void *data, int silent) +static struct ubifs_info *alloc_ubifs_info(struct ubi_volume_desc *ubi) { - struct ubi_volume_desc *ubi = sb->s_fs_info; struct ubifs_info *c; - struct inode *root; - int err; c = kzalloc(sizeof(struct ubifs_info), GFP_KERNEL); - if (!c) - return -ENOMEM; + if (c) { + spin_lock_init(&c->cnt_lock); + spin_lock_init(&c->cs_lock); + spin_lock_init(&c->buds_lock); + spin_lock_init(&c->space_lock); + spin_lock_init(&c->orphan_lock); + init_rwsem(&c->commit_sem); + mutex_init(&c->lp_mutex); + mutex_init(&c->tnc_mutex); + mutex_init(&c->log_mutex); + mutex_init(&c->mst_mutex); + mutex_init(&c->umount_mutex); + mutex_init(&c->bu_mutex); + mutex_init(&c->write_reserve_mutex); + init_waitqueue_head(&c->cmt_wq); + c->buds = RB_ROOT; + c->old_idx = RB_ROOT; + c->size_tree = RB_ROOT; + c->orph_tree = RB_ROOT; + INIT_LIST_HEAD(&c->infos_list); + INIT_LIST_HEAD(&c->idx_gc); + INIT_LIST_HEAD(&c->replay_list); + INIT_LIST_HEAD(&c->replay_buds); + INIT_LIST_HEAD(&c->uncat_list); + INIT_LIST_HEAD(&c->empty_list); + INIT_LIST_HEAD(&c->freeable_list); + INIT_LIST_HEAD(&c->frdi_idx_list); + INIT_LIST_HEAD(&c->unclean_leb_list); + INIT_LIST_HEAD(&c->old_buds); + INIT_LIST_HEAD(&c->orph_list); + INIT_LIST_HEAD(&c->orph_new); + c->no_chk_data_crc = 1; + + c->highest_inum = UBIFS_FIRST_INO; + c->lhead_lnum = c->ltail_lnum = UBIFS_LOG_LNUM; + + ubi_get_volume_info(ubi, &c->vi); + ubi_get_device_info(c->vi.ubi_num, &c->di); + } + return c; +} - spin_lock_init(&c->cnt_lock); - spin_lock_init(&c->cs_lock); - spin_lock_init(&c->buds_lock); - spin_lock_init(&c->space_lock); - spin_lock_init(&c->orphan_lock); - init_rwsem(&c->commit_sem); - mutex_init(&c->lp_mutex); - mutex_init(&c->tnc_mutex); - mutex_init(&c->log_mutex); - mutex_init(&c->mst_mutex); - mutex_init(&c->umount_mutex); - init_waitqueue_head(&c->cmt_wq); - c->buds = RB_ROOT; - c->old_idx = RB_ROOT; - c->size_tree = RB_ROOT; - c->orph_tree = RB_ROOT; - INIT_LIST_HEAD(&c->infos_list); - INIT_LIST_HEAD(&c->idx_gc); - INIT_LIST_HEAD(&c->replay_list); - INIT_LIST_HEAD(&c->replay_buds); - INIT_LIST_HEAD(&c->uncat_list); - INIT_LIST_HEAD(&c->empty_list); - INIT_LIST_HEAD(&c->freeable_list); - INIT_LIST_HEAD(&c->frdi_idx_list); - INIT_LIST_HEAD(&c->unclean_leb_list); - INIT_LIST_HEAD(&c->old_buds); - INIT_LIST_HEAD(&c->orph_list); - INIT_LIST_HEAD(&c->orph_new); - - c->highest_inum = UBIFS_FIRST_INO; - c->lhead_lnum = c->ltail_lnum = UBIFS_LOG_LNUM; - - ubi_get_volume_info(ubi, &c->vi); - ubi_get_device_info(c->vi.ubi_num, &c->di); +static int ubifs_fill_super(struct super_block *sb, void *data, int silent) +{ + struct ubifs_info *c = sb->s_fs_info; + struct inode *root; + int err; + c->vfs_sb = sb; +#ifndef __UBOOT__ /* Re-open the UBI device in read-write mode */ + c->ubi = ubi_open_volume(c->vi.ubi_num, c->vi.vol_id, UBI_READWRITE); +#else + /* U-Boot read only mode */ c->ubi = ubi_open_volume(c->vi.ubi_num, c->vi.vol_id, UBI_READONLY); +#endif + if (IS_ERR(c->ubi)) { err = PTR_ERR(c->ubi); - goto out_free; + goto out; } - c->vfs_sb = sb; +#ifndef __UBOOT__ + /* + * UBIFS provides 'backing_dev_info' in order to disable read-ahead. For + * UBIFS, I/O is not deferred, it is done immediately in readpage, + * which means the user would have to wait not just for their own I/O + * but the read-ahead I/O as well i.e. completely pointless. + * + * Read-ahead will be disabled because @c->bdi.ra_pages is 0. + */ + co>bdi.name = "ubifs", + c->bdi.capabilities = BDI_CAP_MAP_COPY; + err = bdi_init(&c->bdi); + if (err) + goto out_close; + err = bdi_register(&c->bdi, NULL, "ubifs_%d_%d", + c->vi.ubi_num, c->vi.vol_id); + if (err) + goto out_bdi; + + err = ubifs_parse_options(c, data, 0); + if (err) + goto out_bdi; + sb->s_bdi = &c->bdi; +#endif sb->s_fs_info = c; sb->s_magic = UBIFS_SUPER_MAGIC; sb->s_blocksize = UBIFS_BLOCK_SIZE; sb->s_blocksize_bits = UBIFS_BLOCK_SHIFT; - sb->s_dev = c->vi.cdev; sb->s_maxbytes = c->max_inode_sz = key_max_inode_size(c); if (c->max_inode_sz > MAX_LFS_FILESIZE) sb->s_maxbytes = c->max_inode_sz = MAX_LFS_FILESIZE; - - if (c->rw_incompat) { - ubifs_err("the file-system is not R/W-compatible"); - ubifs_msg("on-flash format version is w%d/r%d, but software " - "only supports up to version w%d/r%d", c->fmt_version, - c->ro_compat_version, UBIFS_FORMAT_VERSION, - UBIFS_RO_COMPAT_VERSION); - return -EROFS; - } + sb->s_op = &ubifs_super_operations; mutex_lock(&c->umount_mutex); err = mount_ubifs(c); @@ -992,7 +2303,15 @@ static int ubifs_fill_super(struct super_block *sb, void *data, int silent) goto out_umount; } +#ifndef __UBOOT__ + sb->s_root = d_make_root(root); + if (!sb->s_root) { + err = -ENOMEM; + goto out_umount; + } +#else sb->s_root = NULL; +#endif mutex_unlock(&c->umount_mutex); return 0; @@ -1001,24 +2320,130 @@ out_umount: ubifs_umount(c); out_unlock: mutex_unlock(&c->umount_mutex); +#ifndef __UBOOT__ +out_bdi: + bdi_destroy(&c->bdi); +out_close: +#endif ubi_close_volume(c->ubi); -out_free: - kfree(c); +out: return err; } static int sb_test(struct super_block *sb, void *data) { - dev_t *dev = data; + struct ubifs_info *c1 = data; + struct ubifs_info *c = sb->s_fs_info; - return sb->s_dev == *dev; + return c->vi.cdev == c1->vi.cdev; } -static int ubifs_get_sb(struct file_system_type *fs_type, int flags, - const char *name, void *data, struct vfsmount *mnt) +static int sb_set(struct super_block *sb, void *data) +{ + sb->s_fs_info = data; + return set_anon_super(sb, NULL); +} + +static struct super_block *alloc_super(struct file_system_type *type, int flags) +{ + struct super_block *s; + int err; + + s = kzalloc(sizeof(struct super_block), GFP_USER); + if (!s) { + err = -ENOMEM; + return ERR_PTR(err); + } + + INIT_HLIST_NODE(&s->s_instances); + INIT_LIST_HEAD(&s->s_inodes); + s->s_time_gran = 1000000000; + s->s_flags = flags; + + return s; +} + +/** + * sget - find or create a superblock + * @type: filesystem type superblock should belong to + * @test: comparison callback + * @set: setup callback + * @flags: mount flags + * @data: argument to each of them + */ +struct super_block *sget(struct file_system_type *type, + int (*test)(struct super_block *,void *), + int (*set)(struct super_block *,void *), + int flags, + void *data) +{ + struct super_block *s = NULL; +#ifndef __UBOOT__ + struct super_block *old; +#endif + int err; + +#ifndef __UBOOT__ +retry: + spin_lock(&sb_lock); + if (test) { + hlist_for_each_entry(old, &type->fs_supers, s_instances) { + if (!test(old, data)) + continue; + if (!grab_super(old)) + goto retry; + if (s) { + up_write(&s->s_umount); + destroy_super(s); + s = NULL; + } + return old; + } + } +#endif + if (!s) { + spin_unlock(&sb_lock); + s = alloc_super(type, flags); + if (!s) + return ERR_PTR(-ENOMEM); +#ifndef __UBOOT__ + goto retry; +#endif + } + + err = set(s, data); + if (err) { +#ifndef __UBOOT__ + spin_unlock(&sb_lock); + up_write(&s->s_umount); + destroy_super(s); +#endif + return ERR_PTR(err); + } + s->s_type = type; +#ifndef __UBOOT__ + strlcpy(s->s_id, type->name, sizeof(s->s_id)); +#else + strncpy(s->s_id, type->name, sizeof(s->s_id)); +#endif + list_add_tail(&s->s_list, &super_blocks); + hlist_add_head(&s->s_instances, &type->fs_supers); +#ifndef __UBOOT__ + spin_unlock(&sb_lock); + get_filesystem(type); + register_shrinker(&s->s_shrink); +#endif + return s; +} + +EXPORT_SYMBOL(sget); + + +static struct dentry *ubifs_mount(struct file_system_type *fs_type, int flags, + const char *name, void *data) { struct ubi_volume_desc *ubi; - struct ubi_volume_info vi; + struct ubifs_info *c; struct super_block *sb; int err; @@ -1033,32 +2458,34 @@ static int ubifs_get_sb(struct file_system_type *fs_type, int flags, if (IS_ERR(ubi)) { ubifs_err("cannot open \"%s\", error %d", name, (int)PTR_ERR(ubi)); - return PTR_ERR(ubi); + return ERR_CAST(ubi); + } + + c = alloc_ubifs_info(ubi); + if (!c) { + err = -ENOMEM; + goto out_close; } - ubi_get_volume_info(ubi, &vi); - dbg_gen("opened ubi%d_%d", vi.ubi_num, vi.vol_id); + dbg_gen("opened ubi%d_%d", c->vi.ubi_num, c->vi.vol_id); - sb = sget(fs_type, &sb_test, &sb_set, &vi.cdev); + sb = sget(fs_type, sb_test, sb_set, flags, c); if (IS_ERR(sb)) { err = PTR_ERR(sb); + kfree(c); goto out_close; } if (sb->s_root) { + struct ubifs_info *c1 = sb->s_fs_info; + kfree(c); /* A new mount point for already mounted UBIFS */ dbg_gen("this ubi volume is already mounted"); - if ((flags ^ sb->s_flags) & MS_RDONLY) { + if (!!(flags & MS_RDONLY) != c1->ro_mount) { err = -EBUSY; goto out_deact; } } else { - sb->s_flags = flags; - /* - * Pass 'ubi' to 'fill_super()' in sb->s_fs_info where it is - * replaced by 'c'. - */ - sb->s_fs_info = ubi; err = ubifs_fill_super(sb, data, flags & MS_SILENT ? 1 : 0); if (err) goto out_deact; @@ -1069,17 +2496,53 @@ static int ubifs_get_sb(struct file_system_type *fs_type, int flags, /* 'fill_super()' opens ubi again so we must close it here */ ubi_close_volume(ubi); +#ifdef __UBOOT__ ubifs_sb = sb; return 0; +#else + return dget(sb->s_root); +#endif out_deact: - up_write(&sb->s_umount); +#ifndef __UBOOT__ + deactivate_locked_super(sb); +#endif out_close: ubi_close_volume(ubi); - return err; + return ERR_PTR(err); +} + +static void kill_ubifs_super(struct super_block *s) +{ + struct ubifs_info *c = s->s_fs_info; +#ifndef __UBOOT__ + kill_anon_super(s); +#endif + kfree(c); +} + +static struct file_system_type ubifs_fs_type = { + .name = "ubifs", + .owner = THIS_MODULE, + .mount = ubifs_mount, + .kill_sb = kill_ubifs_super, +}; +#ifndef __UBOOT__ +MODULE_ALIAS_FS("ubifs"); + +/* + * Inode slab cache constructor. + */ +static void inode_slab_ctor(void *obj) +{ + struct ubifs_inode *ui = obj; + inode_init_once(&ui->vfs_inode); } -int __init ubifs_init(void) +static int __init ubifs_init(void) +#else +int ubifs_init(void) +#endif { int err; @@ -1135,41 +2598,84 @@ int __init ubifs_init(void) * UBIFS_BLOCK_SIZE. It is assumed that both are powers of 2. */ if (PAGE_CACHE_SIZE < UBIFS_BLOCK_SIZE) { - ubifs_err("VFS page cache size is %u bytes, but UBIFS requires" - " at least 4096 bytes", + ubifs_err("VFS page cache size is %u bytes, but UBIFS requires at least 4096 bytes", (unsigned int)PAGE_CACHE_SIZE); return -EINVAL; } - err = -ENOMEM; +#ifndef __UBOOT__ + ubifs_inode_slab = kmem_cache_create("ubifs_inode_slab", + sizeof(struct ubifs_inode), 0, + SLAB_MEM_SPREAD | SLAB_RECLAIM_ACCOUNT, + &inode_slab_ctor); + if (!ubifs_inode_slab) + return -ENOMEM; + + register_shrinker(&ubifs_shrinker_info); +#endif err = ubifs_compressors_init(); if (err) goto out_shrinker; +#ifndef __UBOOT__ + err = dbg_debugfs_init(); + if (err) + goto out_compr; + + err = register_filesystem(&ubifs_fs_type); + if (err) { + ubifs_err("cannot register file system, error %d", err); + goto out_dbg; + } +#endif return 0; +#ifndef __UBOOT__ +out_dbg: + dbg_debugfs_exit(); +out_compr: + ubifs_compressors_exit(); +#endif out_shrinker: +#ifndef __UBOOT__ + unregister_shrinker(&ubifs_shrinker_info); +#endif + kmem_cache_destroy(ubifs_inode_slab); return err; } +/* late_initcall to let compressors initialize first */ +late_initcall(ubifs_init); -/* - * ubifsmount... - */ +#ifndef __UBOOT__ +static void __exit ubifs_exit(void) +{ + ubifs_assert(list_empty(&ubifs_infos)); + ubifs_assert(atomic_long_read(&ubifs_clean_zn_cnt) == 0); -static struct file_system_type ubifs_fs_type = { - .name = "ubifs", - .owner = THIS_MODULE, - .get_sb = ubifs_get_sb, -}; + dbg_debugfs_exit(); + ubifs_compressors_exit(); + unregister_shrinker(&ubifs_shrinker_info); -int ubifs_mount(char *name) + /* + * Make sure all delayed rcu free inodes are flushed before we + * destroy cache. + */ + rcu_barrier(); + kmem_cache_destroy(ubifs_inode_slab); + unregister_filesystem(&ubifs_fs_type); +} +module_exit(ubifs_exit); + +MODULE_LICENSE("GPL"); +MODULE_VERSION(__stringify(UBIFS_VERSION)); +MODULE_AUTHOR("Artem Bityutskiy, Adrian Hunter"); +MODULE_DESCRIPTION("UBIFS - UBI File System"); +#else +int uboot_ubifs_mount(char *vol_name) { + struct dentry *ret; int flags; - void *data; - struct vfsmount *mnt; - int ret; - struct ubifs_info *c; /* * First unmount if allready mounted @@ -1177,23 +2683,17 @@ int ubifs_mount(char *name) if (ubifs_sb) ubifs_umount(ubifs_sb->s_fs_info); - INIT_LIST_HEAD(&ubifs_infos); - INIT_LIST_HEAD(&ubifs_fs_type.fs_supers); - /* * Mount in read-only mode */ flags = MS_RDONLY; - data = NULL; - mnt = NULL; - ret = ubifs_get_sb(&ubifs_fs_type, flags, name, data, mnt); - if (ret) { - ubifs_err("Error reading superblock on volume '%s' errno=%d!\n", name, ret); + ret = ubifs_mount(&ubifs_fs_type, flags, vol_name, NULL); + if (IS_ERR(ret)) { + printf("Error reading superblock on volume '%s' " \ + "errno=%d!\n", vol_name, (int)PTR_ERR(ret)); return -1; } - c = ubifs_sb->s_fs_info; - ubi_close_volume(c->ubi); - return 0; } +#endif diff --git a/fs/ubifs/tnc.c b/fs/ubifs/tnc.c index ccda938..eda5070 100644 --- a/fs/ubifs/tnc.c +++ b/fs/ubifs/tnc.c @@ -3,18 +3,7 @@ * * Copyright (C) 2006-2008 Nokia Corporation. * - * 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. - * - * 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., 51 - * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * SPDX-License-Identifier: GPL-2.0+ * * Authors: Adrian Hunter * Artem Bityutskiy (Битюцкий Артём) @@ -30,6 +19,15 @@ * the mutex locked. */ +#define __UBOOT__ +#ifndef __UBOOT__ +#include <linux/crc32.h> +#include <linux/slab.h> +#else +#include <linux/compat.h> +#include <linux/err.h> +#include <linux/stat.h> +#endif #include "ubifs.h" /* @@ -176,27 +174,11 @@ static int ins_clr_old_idx_znode(struct ubifs_info *c, */ void destroy_old_idx(struct ubifs_info *c) { - struct rb_node *this = c->old_idx.rb_node; - struct ubifs_old_idx *old_idx; + struct ubifs_old_idx *old_idx, *n; - while (this) { - if (this->rb_left) { - this = this->rb_left; - continue; - } else if (this->rb_right) { - this = this->rb_right; - continue; - } - old_idx = rb_entry(this, struct ubifs_old_idx, rb); - this = rb_parent(this); - if (this) { - if (this->rb_left == &old_idx->rb) - this->rb_left = NULL; - else - this->rb_right = NULL; - } + rbtree_postorder_for_each_entry_safe(old_idx, n, &c->old_idx, rb) kfree(old_idx); - } + c->old_idx = RB_ROOT; } @@ -221,7 +203,7 @@ static struct ubifs_znode *copy_znode(struct ubifs_info *c, __set_bit(DIRTY_ZNODE, &zn->flags); __clear_bit(COW_ZNODE, &zn->flags); - ubifs_assert(!test_bit(OBSOLETE_ZNODE, &znode->flags)); + ubifs_assert(!ubifs_zn_obsolete(znode)); __set_bit(OBSOLETE_ZNODE, &znode->flags); if (znode->level != 0) { @@ -269,7 +251,7 @@ static struct ubifs_znode *dirty_cow_znode(struct ubifs_info *c, struct ubifs_znode *zn; int err; - if (!test_bit(COW_ZNODE, &znode->flags)) { + if (!ubifs_zn_cow(znode)) { /* znode is not being committed */ if (!test_and_set_bit(DIRTY_ZNODE, &znode->flags)) { atomic_long_inc(&c->dirty_zn_cnt); @@ -337,17 +319,16 @@ static int lnc_add(struct ubifs_info *c, struct ubifs_zbranch *zbr, err = ubifs_validate_entry(c, dent); if (err) { - dbg_dump_stack(); - dbg_dump_node(c, dent); + dump_stack(); + ubifs_dump_node(c, dent); return err; } - lnc_node = kmalloc(zbr->len, GFP_NOFS); + lnc_node = kmemdup(node, zbr->len, GFP_NOFS); if (!lnc_node) /* We don't have to have the cache, so no error */ return 0; - memcpy(lnc_node, node, zbr->len); zbr->leaf = lnc_node; return 0; } @@ -371,8 +352,8 @@ static int lnc_add_directly(struct ubifs_info *c, struct ubifs_zbranch *zbr, err = ubifs_validate_entry(c, node); if (err) { - dbg_dump_stack(); - dbg_dump_node(c, node); + dump_stack(); + ubifs_dump_node(c, node); return err; } @@ -445,8 +426,11 @@ static int tnc_read_node_nm(struct ubifs_info *c, struct ubifs_zbranch *zbr, * * Note, this function does not check CRC of data nodes if @c->no_chk_data_crc * is true (it is controlled by corresponding mount option). However, if - * @c->always_chk_crc is true, @c->no_chk_data_crc is ignored and CRC is always - * checked. + * @c->mounting or @c->remounting_rw is true (we are mounting or re-mounting to + * R/W mode), @c->no_chk_data_crc is ignored and CRC is checked. This is + * because during mounting or re-mounting from R/O mode to R/W mode we may read + * journal nodes (when replying the journal or doing the recovery) and the + * journal nodes may potentially be corrupted, so checking is required. */ static int try_read_node(const struct ubifs_info *c, void *buf, int type, int len, int lnum, int offs) @@ -457,7 +441,7 @@ static int try_read_node(const struct ubifs_info *c, void *buf, int type, dbg_io("LEB %d:%d, %s, length %d", lnum, offs, dbg_ntype(type), len); - err = ubi_read(c->ubi, lnum, buf, offs, len); + err = ubifs_leb_read(c, lnum, buf, offs, len, 1); if (err) { ubifs_err("cannot read node type %d from LEB %d:%d, error %d", type, lnum, offs, err); @@ -474,7 +458,8 @@ static int try_read_node(const struct ubifs_info *c, void *buf, int type, if (node_len != len) return 0; - if (type == UBIFS_DATA_NODE && !c->always_chk_crc && c->no_chk_data_crc) + if (type == UBIFS_DATA_NODE && c->no_chk_data_crc && !c->mounting && + !c->remounting_rw) return 1; crc = crc32(UBIFS_CRC32_INIT, buf + 8, node_len - 8); @@ -500,7 +485,7 @@ static int fallible_read_node(struct ubifs_info *c, const union ubifs_key *key, { int ret; - dbg_tnc("LEB %d:%d, key %s", zbr->lnum, zbr->offs, DBGKEY(key)); + dbg_tnck(key, "LEB %d:%d, key ", zbr->lnum, zbr->offs); ret = try_read_node(c, node, key_type(c, key), zbr->len, zbr->lnum, zbr->offs); @@ -514,8 +499,8 @@ static int fallible_read_node(struct ubifs_info *c, const union ubifs_key *key, ret = 0; } if (ret == 0 && c->replaying) - dbg_mnt("dangling branch LEB %d:%d len %d, key %s", - zbr->lnum, zbr->offs, zbr->len, DBGKEY(key)); + dbg_mntk(key, "dangling branch LEB %d:%d len %d, key ", + zbr->lnum, zbr->offs, zbr->len); return ret; } @@ -990,9 +975,9 @@ static int fallible_resolve_collision(struct ubifs_info *c, if (adding || !o_znode) return 0; - dbg_mnt("dangling match LEB %d:%d len %d %s", + dbg_mntk(key, "dangling match LEB %d:%d len %d key ", o_znode->zbranch[o_n].lnum, o_znode->zbranch[o_n].offs, - o_znode->zbranch[o_n].len, DBGKEY(key)); + o_znode->zbranch[o_n].len); *zn = o_znode; *n = o_n; return 1; @@ -1158,8 +1143,8 @@ static struct ubifs_znode *dirty_cow_bottom_up(struct ubifs_info *c, * o exact match, i.e. the found zero-level znode contains key @key, then %1 * is returned and slot number of the matched branch is stored in @n; * o not exact match, which means that zero-level znode does not contain - * @key, then %0 is returned and slot number of the closed branch is stored - * in @n; + * @key, then %0 is returned and slot number of the closest branch is stored + * in @n; * o @key is so small that it is even less than the lowest key of the * leftmost zero-level node, then %0 is returned and %0 is stored in @n. * @@ -1174,7 +1159,8 @@ int ubifs_lookup_level0(struct ubifs_info *c, const union ubifs_key *key, struct ubifs_znode *znode; unsigned long time = get_seconds(); - dbg_tnc("search key %s", DBGKEY(key)); + dbg_tnck(key, "search key "); + ubifs_assert(key_type(c, key) < UBIFS_INVALID_KEY); znode = c->zroot.znode; if (unlikely(!znode)) { @@ -1251,7 +1237,7 @@ int ubifs_lookup_level0(struct ubifs_info *c, const union ubifs_key *key, * splitting in the middle of the colliding sequence. Also, when * removing the leftmost key, we would have to correct the key of the * parent node, which would introduce additional complications. Namely, - * if we changed the the leftmost key of the parent znode, the garbage + * if we changed the leftmost key of the parent znode, the garbage * collector would be unable to find it (GC is doing this when GC'ing * indexing LEBs). Although we already have an additional RB-tree where * we save such changed znodes (see 'ins_clr_old_idx_znode()') until @@ -1309,7 +1295,7 @@ static int lookup_level0_dirty(struct ubifs_info *c, const union ubifs_key *key, struct ubifs_znode *znode; unsigned long time = get_seconds(); - dbg_tnc("search and dirty key %s", DBGKEY(key)); + dbg_tnck(key, "search and dirty key "); znode = c->zroot.znode; if (unlikely(!znode)) { @@ -1400,9 +1386,31 @@ static int lookup_level0_dirty(struct ubifs_info *c, const union ubifs_key *key, */ static int maybe_leb_gced(struct ubifs_info *c, int lnum, int gc_seq1) { +#ifndef __UBOOT__ + int gc_seq2, gced_lnum; + + gced_lnum = c->gced_lnum; + smp_rmb(); + gc_seq2 = c->gc_seq; + /* Same seq means no GC */ + if (gc_seq1 == gc_seq2) + return 0; + /* Different by more than 1 means we don't know */ + if (gc_seq1 + 1 != gc_seq2) + return 1; /* - * No garbage collection in the read-only U-Boot implementation + * We have seen the sequence number has increased by 1. Now we need to + * be sure we read the right LEB number, so read it again. */ + smp_rmb(); + if (gced_lnum != c->gced_lnum) + return 1; + /* Finally we can check lnum */ + if (gced_lnum == lnum) + return 1; +#else + /* No garbage collection in the read-only U-Boot implementation */ +#endif return 0; } @@ -1414,7 +1422,7 @@ static int maybe_leb_gced(struct ubifs_info *c, int lnum, int gc_seq1) * @lnum: LEB number is returned here * @offs: offset is returned here * - * This function look up and reads node with key @key. The caller has to make + * This function looks up and reads node with key @key. The caller has to make * sure the @node buffer is large enough to fit the node. Returns zero in case * of success, %-ENOENT if the node was not found, and a negative error code in * case of failure. The node location can be returned in @lnum and @offs. @@ -1458,6 +1466,12 @@ again: gc_seq1 = c->gc_seq; mutex_unlock(&c->tnc_mutex); + if (ubifs_get_wbuf(c, zbr.lnum)) { + /* We do not GC journal heads */ + err = ubifs_tnc_read_node(c, &zbr, node); + return err; + } + err = fallible_read_node(c, key, &zbr, node); if (err <= 0 || maybe_leb_gced(c, zbr.lnum, gc_seq1)) { /* @@ -1610,6 +1624,51 @@ out: } /** + * read_wbuf - bulk-read from a LEB with a wbuf. + * @wbuf: wbuf that may overlap the read + * @buf: buffer into which to read + * @len: read length + * @lnum: LEB number from which to read + * @offs: offset from which to read + * + * This functions returns %0 on success or a negative error code on failure. + */ +static int read_wbuf(struct ubifs_wbuf *wbuf, void *buf, int len, int lnum, + int offs) +{ + const struct ubifs_info *c = wbuf->c; + int rlen, overlap; + + dbg_io("LEB %d:%d, length %d", lnum, offs, len); + ubifs_assert(wbuf && lnum >= 0 && lnum < c->leb_cnt && offs >= 0); + ubifs_assert(!(offs & 7) && offs < c->leb_size); + ubifs_assert(offs + len <= c->leb_size); + + spin_lock(&wbuf->lock); + overlap = (lnum == wbuf->lnum && offs + len > wbuf->offs); + if (!overlap) { + /* We may safely unlock the write-buffer and read the data */ + spin_unlock(&wbuf->lock); + return ubifs_leb_read(c, lnum, buf, offs, len, 0); + } + + /* Don't read under wbuf */ + rlen = wbuf->offs - offs; + if (rlen < 0) + rlen = 0; + + /* Copy the rest from the write-buffer */ + memcpy(buf + rlen, wbuf->buf + offs + rlen - wbuf->offs, len - rlen); + spin_unlock(&wbuf->lock); + + if (rlen > 0) + /* Read everything that goes before write-buffer */ + return ubifs_leb_read(c, lnum, buf, offs, rlen, 0); + + return 0; +} + +/** * validate_data_node - validate data nodes for bulk-read. * @c: UBIFS file-system description object * @buf: buffer containing data node to validate @@ -1647,8 +1706,8 @@ static int validate_data_node(struct ubifs_info *c, void *buf, if (!keys_eq(c, &zbr->key, &key1)) { ubifs_err("bad key in node at LEB %d:%d", zbr->lnum, zbr->offs); - dbg_tnc("looked for key %s found node's key %s", - DBGKEY(&zbr->key), DBGKEY1(&key1)); + dbg_tnck(&zbr->key, "looked for key "); + dbg_tnck(&key1, "found node's key "); goto out_err; } @@ -1658,8 +1717,8 @@ out_err: err = -EINVAL; out: ubifs_err("bad node at LEB %d:%d", zbr->lnum, zbr->offs); - dbg_dump_node(c, buf); - dbg_dump_stack(); + ubifs_dump_node(c, buf); + dump_stack(); return err; } @@ -1676,6 +1735,7 @@ out: int ubifs_tnc_bulk_read(struct ubifs_info *c, struct bu_info *bu) { int lnum = bu->zbranch[0].lnum, offs = bu->zbranch[0].offs, len, err, i; + struct ubifs_wbuf *wbuf; void *buf; len = bu->zbranch[bu->cnt - 1].offs; @@ -1686,7 +1746,11 @@ int ubifs_tnc_bulk_read(struct ubifs_info *c, struct bu_info *bu) } /* Do the read */ - err = ubi_read(c->ubi, lnum, bu->buf, offs, len); + wbuf = ubifs_get_wbuf(c, lnum); + if (wbuf) + err = read_wbuf(wbuf, bu->buf, len, lnum, offs); + else + err = ubifs_leb_read(c, lnum, bu->buf, offs, len, 0); /* Check for a race with GC */ if (maybe_leb_gced(c, lnum, bu->gc_seq)) @@ -1695,8 +1759,8 @@ int ubifs_tnc_bulk_read(struct ubifs_info *c, struct bu_info *bu) if (err && err != -EBADMSG) { ubifs_err("failed to read from LEB %d:%d, error %d", lnum, offs, err); - dbg_dump_stack(); - dbg_tnc("key %s", DBGKEY(&bu->key)); + dump_stack(); + dbg_tnck(&bu->key, "key "); return err; } @@ -1731,7 +1795,7 @@ static int do_lookup_nm(struct ubifs_info *c, const union ubifs_key *key, int found, n, err; struct ubifs_znode *znode; - dbg_tnc("name '%.*s' key %s", nm->len, nm->name, DBGKEY(key)); + dbg_tnck(key, "name '%.*s' key ", nm->len, nm->name); mutex_lock(&c->tnc_mutex); found = ubifs_lookup_level0(c, key, &znode, &n); if (!found) { @@ -1905,8 +1969,7 @@ again: zp = znode->parent; if (znode->child_cnt < c->fanout) { ubifs_assert(n != c->fanout); - dbg_tnc("inserted at %d level %d, key %s", n, znode->level, - DBGKEY(key)); + dbg_tnck(key, "inserted at %d level %d, key ", n, znode->level); insert_zbranch(znode, zbr, n); @@ -1921,7 +1984,7 @@ again: * Unfortunately, @znode does not have more empty slots and we have to * split it. */ - dbg_tnc("splitting level %d, key %s", znode->level, DBGKEY(key)); + dbg_tnck(key, "splitting level %d, key ", znode->level); if (znode->alt) /* @@ -2015,7 +2078,7 @@ do_split: } /* Insert new key and branch */ - dbg_tnc("inserting at %d level %d, key %s", n, zn->level, DBGKEY(key)); + dbg_tnck(key, "inserting at %d level %d, key ", n, zn->level); insert_zbranch(zi, zbr, n); @@ -2091,7 +2154,7 @@ int ubifs_tnc_add(struct ubifs_info *c, const union ubifs_key *key, int lnum, struct ubifs_znode *znode; mutex_lock(&c->tnc_mutex); - dbg_tnc("%d:%d, len %d, key %s", lnum, offs, len, DBGKEY(key)); + dbg_tnck(key, "%d:%d, len %d, key ", lnum, offs, len); found = lookup_level0_dirty(c, key, &znode, &n); if (!found) { struct ubifs_zbranch zbr; @@ -2140,8 +2203,8 @@ int ubifs_tnc_replace(struct ubifs_info *c, const union ubifs_key *key, struct ubifs_znode *znode; mutex_lock(&c->tnc_mutex); - dbg_tnc("old LEB %d:%d, new LEB %d:%d, len %d, key %s", old_lnum, - old_offs, lnum, offs, len, DBGKEY(key)); + dbg_tnck(key, "old LEB %d:%d, new LEB %d:%d, len %d, key ", old_lnum, + old_offs, lnum, offs, len); found = lookup_level0_dirty(c, key, &znode, &n); if (found < 0) { err = found; @@ -2223,8 +2286,8 @@ int ubifs_tnc_add_nm(struct ubifs_info *c, const union ubifs_key *key, struct ubifs_znode *znode; mutex_lock(&c->tnc_mutex); - dbg_tnc("LEB %d:%d, name '%.*s', key %s", lnum, offs, nm->len, nm->name, - DBGKEY(key)); + dbg_tnck(key, "LEB %d:%d, name '%.*s', key ", + lnum, offs, nm->len, nm->name); found = lookup_level0_dirty(c, key, &znode, &n); if (found < 0) { err = found; @@ -2282,7 +2345,7 @@ int ubifs_tnc_add_nm(struct ubifs_info *c, const union ubifs_key *key, * by passing 'ubifs_tnc_remove_nm()' the same key but * an unmatchable name. */ - struct qstr noname = { .len = 0, .name = "" }; + struct qstr noname = { .name = "" }; err = dbg_check_tnc(c, 0); mutex_unlock(&c->tnc_mutex); @@ -2317,14 +2380,14 @@ static int tnc_delete(struct ubifs_info *c, struct ubifs_znode *znode, int n) /* Delete without merge for now */ ubifs_assert(znode->level == 0); ubifs_assert(n >= 0 && n < c->fanout); - dbg_tnc("deleting %s", DBGKEY(&znode->zbranch[n].key)); + dbg_tnck(&znode->zbranch[n].key, "deleting key "); zbr = &znode->zbranch[n]; lnc_free(zbr); err = ubifs_add_dirt(c, zbr->lnum, zbr->len); if (err) { - dbg_dump_znode(c, znode); + ubifs_dump_znode(c, znode); return err; } @@ -2342,7 +2405,7 @@ static int tnc_delete(struct ubifs_info *c, struct ubifs_znode *znode, int n) */ do { - ubifs_assert(!test_bit(OBSOLETE_ZNODE, &znode->flags)); + ubifs_assert(!ubifs_zn_obsolete(znode)); ubifs_assert(ubifs_zn_dirty(znode)); zp = znode->parent; @@ -2398,9 +2461,8 @@ static int tnc_delete(struct ubifs_info *c, struct ubifs_znode *znode, int n) c->zroot.offs = zbr->offs; c->zroot.len = zbr->len; c->zroot.znode = znode; - ubifs_assert(!test_bit(OBSOLETE_ZNODE, - &zp->flags)); - ubifs_assert(test_bit(DIRTY_ZNODE, &zp->flags)); + ubifs_assert(!ubifs_zn_obsolete(zp)); + ubifs_assert(ubifs_zn_dirty(zp)); atomic_long_dec(&c->dirty_zn_cnt); if (zp->cnext) { @@ -2428,7 +2490,7 @@ int ubifs_tnc_remove(struct ubifs_info *c, const union ubifs_key *key) struct ubifs_znode *znode; mutex_lock(&c->tnc_mutex); - dbg_tnc("key %s", DBGKEY(key)); + dbg_tnck(key, "key "); found = lookup_level0_dirty(c, key, &znode, &n); if (found < 0) { err = found; @@ -2459,7 +2521,7 @@ int ubifs_tnc_remove_nm(struct ubifs_info *c, const union ubifs_key *key, struct ubifs_znode *znode; mutex_lock(&c->tnc_mutex); - dbg_tnc("%.*s, key %s", nm->len, nm->name, DBGKEY(key)); + dbg_tnck(key, "%.*s, key ", nm->len, nm->name); err = lookup_level0_dirty(c, key, &znode, &n); if (err < 0) goto out_unlock; @@ -2476,11 +2538,11 @@ int ubifs_tnc_remove_nm(struct ubifs_info *c, const union ubifs_key *key, if (err) { /* Ensure the znode is dirtied */ if (znode->cnext || !ubifs_zn_dirty(znode)) { - znode = dirty_cow_bottom_up(c, znode); - if (IS_ERR(znode)) { - err = PTR_ERR(znode); - goto out_unlock; - } + znode = dirty_cow_bottom_up(c, znode); + if (IS_ERR(znode)) { + err = PTR_ERR(znode); + goto out_unlock; + } } err = tnc_delete(c, znode, n); } @@ -2571,10 +2633,10 @@ int ubifs_tnc_remove_range(struct ubifs_info *c, union ubifs_key *from_key, err = ubifs_add_dirt(c, znode->zbranch[i].lnum, znode->zbranch[i].len); if (err) { - dbg_dump_znode(c, znode); + ubifs_dump_znode(c, znode); goto out_unlock; } - dbg_tnc("removing %s", DBGKEY(key)); + dbg_tnck(key, "removing key "); } if (k) { for (i = n + 1 + k; i < znode->child_cnt; i++) @@ -2633,7 +2695,7 @@ int ubifs_tnc_remove_ino(struct ubifs_info *c, ino_t inum) dbg_tnc("xent '%s', ino %lu", xent->name, (unsigned long)xattr_inum); - nm.name = (char *)xent->name; + nm.name = xent->name; nm.len = le16_to_cpu(xent->nlen); err = ubifs_tnc_remove_nm(c, &key1, &nm); if (err) { @@ -2694,7 +2756,7 @@ struct ubifs_dent_node *ubifs_tnc_next_ent(struct ubifs_info *c, struct ubifs_zbranch *zbr; union ubifs_key *dkey; - dbg_tnc("%s %s", nm->name ? (char *)nm->name : "(lowest)", DBGKEY(key)); + dbg_tnck(key, "%s ", nm->name ? (char *)nm->name : "(lowest)"); ubifs_assert(is_hash_key(c, key)); mutex_lock(&c->tnc_mutex); @@ -2765,3 +2827,503 @@ out_unlock: mutex_unlock(&c->tnc_mutex); return ERR_PTR(err); } + +#ifndef __UBOOT__ +/** + * tnc_destroy_cnext - destroy left-over obsolete znodes from a failed commit. + * @c: UBIFS file-system description object + * + * Destroy left-over obsolete znodes from a failed commit. + */ +static void tnc_destroy_cnext(struct ubifs_info *c) +{ + struct ubifs_znode *cnext; + + if (!c->cnext) + return; + ubifs_assert(c->cmt_state == COMMIT_BROKEN); + cnext = c->cnext; + do { + struct ubifs_znode *znode = cnext; + + cnext = cnext->cnext; + if (ubifs_zn_obsolete(znode)) + kfree(znode); + } while (cnext && cnext != c->cnext); +} + +/** + * ubifs_tnc_close - close TNC subsystem and free all related resources. + * @c: UBIFS file-system description object + */ +void ubifs_tnc_close(struct ubifs_info *c) +{ + tnc_destroy_cnext(c); + if (c->zroot.znode) { + long n; + + ubifs_destroy_tnc_subtree(c->zroot.znode); + n = atomic_long_read(&c->clean_zn_cnt); + atomic_long_sub(n, &ubifs_clean_zn_cnt); + } + kfree(c->gap_lebs); + kfree(c->ilebs); + destroy_old_idx(c); +} +#endif + +/** + * left_znode - get the znode to the left. + * @c: UBIFS file-system description object + * @znode: znode + * + * This function returns a pointer to the znode to the left of @znode or NULL if + * there is not one. A negative error code is returned on failure. + */ +static struct ubifs_znode *left_znode(struct ubifs_info *c, + struct ubifs_znode *znode) +{ + int level = znode->level; + + while (1) { + int n = znode->iip - 1; + + /* Go up until we can go left */ + znode = znode->parent; + if (!znode) + return NULL; + if (n >= 0) { + /* Now go down the rightmost branch to 'level' */ + znode = get_znode(c, znode, n); + if (IS_ERR(znode)) + return znode; + while (znode->level != level) { + n = znode->child_cnt - 1; + znode = get_znode(c, znode, n); + if (IS_ERR(znode)) + return znode; + } + break; + } + } + return znode; +} + +/** + * right_znode - get the znode to the right. + * @c: UBIFS file-system description object + * @znode: znode + * + * This function returns a pointer to the znode to the right of @znode or NULL + * if there is not one. A negative error code is returned on failure. + */ +static struct ubifs_znode *right_znode(struct ubifs_info *c, + struct ubifs_znode *znode) +{ + int level = znode->level; + + while (1) { + int n = znode->iip + 1; + + /* Go up until we can go right */ + znode = znode->parent; + if (!znode) + return NULL; + if (n < znode->child_cnt) { + /* Now go down the leftmost branch to 'level' */ + znode = get_znode(c, znode, n); + if (IS_ERR(znode)) + return znode; + while (znode->level != level) { + znode = get_znode(c, znode, 0); + if (IS_ERR(znode)) + return znode; + } + break; + } + } + return znode; +} + +/** + * lookup_znode - find a particular indexing node from TNC. + * @c: UBIFS file-system description object + * @key: index node key to lookup + * @level: index node level + * @lnum: index node LEB number + * @offs: index node offset + * + * This function searches an indexing node by its first key @key and its + * address @lnum:@offs. It looks up the indexing tree by pulling all indexing + * nodes it traverses to TNC. This function is called for indexing nodes which + * were found on the media by scanning, for example when garbage-collecting or + * when doing in-the-gaps commit. This means that the indexing node which is + * looked for does not have to have exactly the same leftmost key @key, because + * the leftmost key may have been changed, in which case TNC will contain a + * dirty znode which still refers the same @lnum:@offs. This function is clever + * enough to recognize such indexing nodes. + * + * Note, if a znode was deleted or changed too much, then this function will + * not find it. For situations like this UBIFS has the old index RB-tree + * (indexed by @lnum:@offs). + * + * This function returns a pointer to the znode found or %NULL if it is not + * found. A negative error code is returned on failure. + */ +static struct ubifs_znode *lookup_znode(struct ubifs_info *c, + union ubifs_key *key, int level, + int lnum, int offs) +{ + struct ubifs_znode *znode, *zn; + int n, nn; + + ubifs_assert(key_type(c, key) < UBIFS_INVALID_KEY); + + /* + * The arguments have probably been read off flash, so don't assume + * they are valid. + */ + if (level < 0) + return ERR_PTR(-EINVAL); + + /* Get the root znode */ + znode = c->zroot.znode; + if (!znode) { + znode = ubifs_load_znode(c, &c->zroot, NULL, 0); + if (IS_ERR(znode)) + return znode; + } + /* Check if it is the one we are looking for */ + if (c->zroot.lnum == lnum && c->zroot.offs == offs) + return znode; + /* Descend to the parent level i.e. (level + 1) */ + if (level >= znode->level) + return NULL; + while (1) { + ubifs_search_zbranch(c, znode, key, &n); + if (n < 0) { + /* + * We reached a znode where the leftmost key is greater + * than the key we are searching for. This is the same + * situation as the one described in a huge comment at + * the end of the 'ubifs_lookup_level0()' function. And + * for exactly the same reasons we have to try to look + * left before giving up. + */ + znode = left_znode(c, znode); + if (!znode) + return NULL; + if (IS_ERR(znode)) + return znode; + ubifs_search_zbranch(c, znode, key, &n); + ubifs_assert(n >= 0); + } + if (znode->level == level + 1) + break; + znode = get_znode(c, znode, n); + if (IS_ERR(znode)) + return znode; + } + /* Check if the child is the one we are looking for */ + if (znode->zbranch[n].lnum == lnum && znode->zbranch[n].offs == offs) + return get_znode(c, znode, n); + /* If the key is unique, there is nowhere else to look */ + if (!is_hash_key(c, key)) + return NULL; + /* + * The key is not unique and so may be also in the znodes to either + * side. + */ + zn = znode; + nn = n; + /* Look left */ + while (1) { + /* Move one branch to the left */ + if (n) + n -= 1; + else { + znode = left_znode(c, znode); + if (!znode) + break; + if (IS_ERR(znode)) + return znode; + n = znode->child_cnt - 1; + } + /* Check it */ + if (znode->zbranch[n].lnum == lnum && + znode->zbranch[n].offs == offs) + return get_znode(c, znode, n); + /* Stop if the key is less than the one we are looking for */ + if (keys_cmp(c, &znode->zbranch[n].key, key) < 0) + break; + } + /* Back to the middle */ + znode = zn; + n = nn; + /* Look right */ + while (1) { + /* Move one branch to the right */ + if (++n >= znode->child_cnt) { + znode = right_znode(c, znode); + if (!znode) + break; + if (IS_ERR(znode)) + return znode; + n = 0; + } + /* Check it */ + if (znode->zbranch[n].lnum == lnum && + znode->zbranch[n].offs == offs) + return get_znode(c, znode, n); + /* Stop if the key is greater than the one we are looking for */ + if (keys_cmp(c, &znode->zbranch[n].key, key) > 0) + break; + } + return NULL; +} + +/** + * is_idx_node_in_tnc - determine if an index node is in the TNC. + * @c: UBIFS file-system description object + * @key: key of index node + * @level: index node level + * @lnum: LEB number of index node + * @offs: offset of index node + * + * This function returns %0 if the index node is not referred to in the TNC, %1 + * if the index node is referred to in the TNC and the corresponding znode is + * dirty, %2 if an index node is referred to in the TNC and the corresponding + * znode is clean, and a negative error code in case of failure. + * + * Note, the @key argument has to be the key of the first child. Also note, + * this function relies on the fact that 0:0 is never a valid LEB number and + * offset for a main-area node. + */ +int is_idx_node_in_tnc(struct ubifs_info *c, union ubifs_key *key, int level, + int lnum, int offs) +{ + struct ubifs_znode *znode; + + znode = lookup_znode(c, key, level, lnum, offs); + if (!znode) + return 0; + if (IS_ERR(znode)) + return PTR_ERR(znode); + + return ubifs_zn_dirty(znode) ? 1 : 2; +} + +/** + * is_leaf_node_in_tnc - determine if a non-indexing not is in the TNC. + * @c: UBIFS file-system description object + * @key: node key + * @lnum: node LEB number + * @offs: node offset + * + * This function returns %1 if the node is referred to in the TNC, %0 if it is + * not, and a negative error code in case of failure. + * + * Note, this function relies on the fact that 0:0 is never a valid LEB number + * and offset for a main-area node. + */ +static int is_leaf_node_in_tnc(struct ubifs_info *c, union ubifs_key *key, + int lnum, int offs) +{ + struct ubifs_zbranch *zbr; + struct ubifs_znode *znode, *zn; + int n, found, err, nn; + const int unique = !is_hash_key(c, key); + + found = ubifs_lookup_level0(c, key, &znode, &n); + if (found < 0) + return found; /* Error code */ + if (!found) + return 0; + zbr = &znode->zbranch[n]; + if (lnum == zbr->lnum && offs == zbr->offs) + return 1; /* Found it */ + if (unique) + return 0; + /* + * Because the key is not unique, we have to look left + * and right as well + */ + zn = znode; + nn = n; + /* Look left */ + while (1) { + err = tnc_prev(c, &znode, &n); + if (err == -ENOENT) + break; + if (err) + return err; + if (keys_cmp(c, key, &znode->zbranch[n].key)) + break; + zbr = &znode->zbranch[n]; + if (lnum == zbr->lnum && offs == zbr->offs) + return 1; /* Found it */ + } + /* Look right */ + znode = zn; + n = nn; + while (1) { + err = tnc_next(c, &znode, &n); + if (err) { + if (err == -ENOENT) + return 0; + return err; + } + if (keys_cmp(c, key, &znode->zbranch[n].key)) + break; + zbr = &znode->zbranch[n]; + if (lnum == zbr->lnum && offs == zbr->offs) + return 1; /* Found it */ + } + return 0; +} + +/** + * ubifs_tnc_has_node - determine whether a node is in the TNC. + * @c: UBIFS file-system description object + * @key: node key + * @level: index node level (if it is an index node) + * @lnum: node LEB number + * @offs: node offset + * @is_idx: non-zero if the node is an index node + * + * This function returns %1 if the node is in the TNC, %0 if it is not, and a + * negative error code in case of failure. For index nodes, @key has to be the + * key of the first child. An index node is considered to be in the TNC only if + * the corresponding znode is clean or has not been loaded. + */ +int ubifs_tnc_has_node(struct ubifs_info *c, union ubifs_key *key, int level, + int lnum, int offs, int is_idx) +{ + int err; + + mutex_lock(&c->tnc_mutex); + if (is_idx) { + err = is_idx_node_in_tnc(c, key, level, lnum, offs); + if (err < 0) + goto out_unlock; + if (err == 1) + /* The index node was found but it was dirty */ + err = 0; + else if (err == 2) + /* The index node was found and it was clean */ + err = 1; + else + BUG_ON(err != 0); + } else + err = is_leaf_node_in_tnc(c, key, lnum, offs); + +out_unlock: + mutex_unlock(&c->tnc_mutex); + return err; +} + +/** + * ubifs_dirty_idx_node - dirty an index node. + * @c: UBIFS file-system description object + * @key: index node key + * @level: index node level + * @lnum: index node LEB number + * @offs: index node offset + * + * This function loads and dirties an index node so that it can be garbage + * collected. The @key argument has to be the key of the first child. This + * function relies on the fact that 0:0 is never a valid LEB number and offset + * for a main-area node. Returns %0 on success and a negative error code on + * failure. + */ +int ubifs_dirty_idx_node(struct ubifs_info *c, union ubifs_key *key, int level, + int lnum, int offs) +{ + struct ubifs_znode *znode; + int err = 0; + + mutex_lock(&c->tnc_mutex); + znode = lookup_znode(c, key, level, lnum, offs); + if (!znode) + goto out_unlock; + if (IS_ERR(znode)) { + err = PTR_ERR(znode); + goto out_unlock; + } + znode = dirty_cow_bottom_up(c, znode); + if (IS_ERR(znode)) { + err = PTR_ERR(znode); + goto out_unlock; + } + +out_unlock: + mutex_unlock(&c->tnc_mutex); + return err; +} + +/** + * dbg_check_inode_size - check if inode size is correct. + * @c: UBIFS file-system description object + * @inum: inode number + * @size: inode size + * + * This function makes sure that the inode size (@size) is correct and it does + * not have any pages beyond @size. Returns zero if the inode is OK, %-EINVAL + * if it has a data page beyond @size, and other negative error code in case of + * other errors. + */ +int dbg_check_inode_size(struct ubifs_info *c, const struct inode *inode, + loff_t size) +{ + int err, n; + union ubifs_key from_key, to_key, *key; + struct ubifs_znode *znode; + unsigned int block; + + if (!S_ISREG(inode->i_mode)) + return 0; + if (!dbg_is_chk_gen(c)) + return 0; + + block = (size + UBIFS_BLOCK_SIZE - 1) >> UBIFS_BLOCK_SHIFT; + data_key_init(c, &from_key, inode->i_ino, block); + highest_data_key(c, &to_key, inode->i_ino); + + mutex_lock(&c->tnc_mutex); + err = ubifs_lookup_level0(c, &from_key, &znode, &n); + if (err < 0) + goto out_unlock; + + if (err) { + err = -EINVAL; + key = &from_key; + goto out_dump; + } + + err = tnc_next(c, &znode, &n); + if (err == -ENOENT) { + err = 0; + goto out_unlock; + } + if (err < 0) + goto out_unlock; + + ubifs_assert(err == 0); + key = &znode->zbranch[n].key; + if (!key_in_range(c, key, &from_key, &to_key)) + goto out_unlock; + +out_dump: + block = key_block(c, key); + ubifs_err("inode %lu has size %lld, but there are data at offset %lld", + (unsigned long)inode->i_ino, size, + ((loff_t)block) << UBIFS_BLOCK_SHIFT); + mutex_unlock(&c->tnc_mutex); + ubifs_dump_inode(c, inode); + dump_stack(); + return -EINVAL; + +out_unlock: + mutex_unlock(&c->tnc_mutex); + return err; +} diff --git a/fs/ubifs/tnc_misc.c b/fs/ubifs/tnc_misc.c index 955219f..81bdad9 100644 --- a/fs/ubifs/tnc_misc.c +++ b/fs/ubifs/tnc_misc.c @@ -3,18 +3,7 @@ * * Copyright (C) 2006-2008 Nokia Corporation. * - * 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. - * - * 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., 51 - * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * SPDX-License-Identifier: GPL-2.0+ * * Authors: Adrian Hunter * Artem Bityutskiy (Битюцкий Артём) @@ -27,6 +16,10 @@ * putting it all in one file would make that file too big and unreadable. */ +#define __UBOOT__ +#ifdef __UBOOT__ +#include <linux/err.h> +#endif #include "ubifs.h" /** @@ -219,6 +212,44 @@ struct ubifs_znode *ubifs_tnc_postorder_next(struct ubifs_znode *znode) } /** + * ubifs_destroy_tnc_subtree - destroy all znodes connected to a subtree. + * @znode: znode defining subtree to destroy + * + * This function destroys subtree of the TNC tree. Returns number of clean + * znodes in the subtree. + */ +long ubifs_destroy_tnc_subtree(struct ubifs_znode *znode) +{ + struct ubifs_znode *zn = ubifs_tnc_postorder_first(znode); + long clean_freed = 0; + int n; + + ubifs_assert(zn); + while (1) { + for (n = 0; n < zn->child_cnt; n++) { + if (!zn->zbranch[n].znode) + continue; + + if (zn->level > 0 && + !ubifs_zn_dirty(zn->zbranch[n].znode)) + clean_freed += 1; + + cond_resched(); + kfree(zn->zbranch[n].znode); + } + + if (zn == znode) { + if (!ubifs_zn_dirty(zn)) + clean_freed += 1; + kfree(zn); + return clean_freed; + } + + zn = ubifs_tnc_postorder_next(zn); + } +} + +/** * read_znode - read an indexing node from flash and fill znode. * @c: UBIFS file-system description object * @lnum: LEB of the indexing node to read @@ -255,10 +286,10 @@ static int read_znode(struct ubifs_info *c, int lnum, int offs, int len, lnum, offs, znode->level, znode->child_cnt); if (znode->child_cnt > c->fanout || znode->level > UBIFS_MAX_LEVELS) { - dbg_err("current fanout %d, branch count %d", - c->fanout, znode->child_cnt); - dbg_err("max levels %d, znode level %d", - UBIFS_MAX_LEVELS, znode->level); + ubifs_err("current fanout %d, branch count %d", + c->fanout, znode->child_cnt); + ubifs_err("max levels %d, znode level %d", + UBIFS_MAX_LEVELS, znode->level); err = 1; goto out_dump; } @@ -278,7 +309,7 @@ static int read_znode(struct ubifs_info *c, int lnum, int offs, int len, if (zbr->lnum < c->main_first || zbr->lnum >= c->leb_cnt || zbr->offs < 0 || zbr->offs + zbr->len > c->leb_size || zbr->offs & 7) { - dbg_err("bad branch %d", i); + ubifs_err("bad branch %d", i); err = 2; goto out_dump; } @@ -290,8 +321,8 @@ static int read_znode(struct ubifs_info *c, int lnum, int offs, int len, case UBIFS_XENT_KEY: break; default: - dbg_msg("bad key type at slot %d: %s", i, - DBGKEY(&zbr->key)); + ubifs_err("bad key type at slot %d: %d", + i, key_type(c, &zbr->key)); err = 3; goto out_dump; } @@ -302,19 +333,19 @@ static int read_znode(struct ubifs_info *c, int lnum, int offs, int len, type = key_type(c, &zbr->key); if (c->ranges[type].max_len == 0) { if (zbr->len != c->ranges[type].len) { - dbg_err("bad target node (type %d) length (%d)", - type, zbr->len); - dbg_err("have to be %d", c->ranges[type].len); + ubifs_err("bad target node (type %d) length (%d)", + type, zbr->len); + ubifs_err("have to be %d", c->ranges[type].len); err = 4; goto out_dump; } } else if (zbr->len < c->ranges[type].min_len || zbr->len > c->ranges[type].max_len) { - dbg_err("bad target node (type %d) length (%d)", - type, zbr->len); - dbg_err("have to be in range of %d-%d", - c->ranges[type].min_len, - c->ranges[type].max_len); + ubifs_err("bad target node (type %d) length (%d)", + type, zbr->len); + ubifs_err("have to be in range of %d-%d", + c->ranges[type].min_len, + c->ranges[type].max_len); err = 5; goto out_dump; } @@ -332,13 +363,13 @@ static int read_znode(struct ubifs_info *c, int lnum, int offs, int len, cmp = keys_cmp(c, key1, key2); if (cmp > 0) { - dbg_err("bad key order (keys %d and %d)", i, i + 1); + ubifs_err("bad key order (keys %d and %d)", i, i + 1); err = 6; goto out_dump; } else if (cmp == 0 && !is_hash_key(c, key1)) { /* These can only be keys with colliding hash */ - dbg_err("keys %d and %d are not hashed but equivalent", - i, i + 1); + ubifs_err("keys %d and %d are not hashed but equivalent", + i, i + 1); err = 7; goto out_dump; } @@ -349,7 +380,7 @@ static int read_znode(struct ubifs_info *c, int lnum, int offs, int len, out_dump: ubifs_err("bad indexing node at LEB %d:%d, error %d", lnum, offs, err); - dbg_dump_node(c, idx); + ubifs_dump_node(c, idx); kfree(idx); return -EINVAL; } @@ -385,6 +416,16 @@ struct ubifs_znode *ubifs_load_znode(struct ubifs_info *c, if (err) goto out; + atomic_long_inc(&c->clean_zn_cnt); + + /* + * Increment the global clean znode counter as well. It is OK that + * global and per-FS clean znode counters may be inconsistent for some + * short time (because we might be preempted at this point), the global + * one is only used in shrinker. + */ + atomic_long_inc(&ubifs_clean_zn_cnt); + zbr->znode = znode; znode->parent = parent; znode->time = get_seconds(); @@ -412,11 +453,22 @@ int ubifs_tnc_read_node(struct ubifs_info *c, struct ubifs_zbranch *zbr, { union ubifs_key key1, *key = &zbr->key; int err, type = key_type(c, key); + struct ubifs_wbuf *wbuf; - err = ubifs_read_node(c, node, type, zbr->len, zbr->lnum, zbr->offs); + /* + * 'zbr' has to point to on-flash node. The node may sit in a bud and + * may even be in a write buffer, so we have to take care about this. + */ + wbuf = ubifs_get_wbuf(c, zbr->lnum); + if (wbuf) + err = ubifs_read_node_wbuf(wbuf, node, type, zbr->len, + zbr->lnum, zbr->offs); + else + err = ubifs_read_node(c, node, type, zbr->len, zbr->lnum, + zbr->offs); if (err) { - dbg_tnc("key %s", DBGKEY(key)); + dbg_tnck(key, "key "); return err; } @@ -425,9 +477,9 @@ int ubifs_tnc_read_node(struct ubifs_info *c, struct ubifs_zbranch *zbr, if (!keys_eq(c, key, &key1)) { ubifs_err("bad key in node at LEB %d:%d", zbr->lnum, zbr->offs); - dbg_tnc("looked for key %s found node's key %s", - DBGKEY(key), DBGKEY1(&key1)); - dbg_dump_node(c, node); + dbg_tnck(key, "looked for key "); + dbg_tnck(&key1, "but found node's key "); + ubifs_dump_node(c, node); return -EINVAL; } diff --git a/fs/ubifs/ubifs-media.h b/fs/ubifs/ubifs-media.h index 3eee07e..90b8ffa 100644 --- a/fs/ubifs/ubifs-media.h +++ b/fs/ubifs/ubifs-media.h @@ -3,18 +3,7 @@ * * Copyright (C) 2006-2008 Nokia Corporation. * - * 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. - * - * 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., 51 - * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * SPDX-License-Identifier: GPL-2.0+ * * Authors: Artem Bityutskiy (Битюцкий Артём) * Adrian Hunter @@ -135,6 +124,13 @@ /* The key is always at the same position in all keyed nodes */ #define UBIFS_KEY_OFFSET offsetof(struct ubifs_ino_node, key) +/* Garbage collector journal head number */ +#define UBIFS_GC_HEAD 0 +/* Base journal head number */ +#define UBIFS_BASE_HEAD 1 +/* Data journal head number */ +#define UBIFS_DATA_HEAD 2 + /* * LEB Properties Tree node types. * @@ -401,9 +397,11 @@ enum { * Superblock flags. * * UBIFS_FLG_BIGLPT: if "big" LPT model is used if set + * UBIFS_FLG_SPACE_FIXUP: first-mount "fixup" of free space within LEBs needed */ enum { UBIFS_FLG_BIGLPT = 0x02, + UBIFS_FLG_SPACE_FIXUP = 0x04, }; /** @@ -427,7 +425,7 @@ struct ubifs_ch { __u8 node_type; __u8 group_type; __u8 padding[2]; -} __attribute__ ((packed)); +} __packed; /** * union ubifs_dev_desc - device node descriptor. @@ -441,7 +439,7 @@ struct ubifs_ch { union ubifs_dev_desc { __le32 new; __le64 huge; -} __attribute__ ((packed)); +} __packed; /** * struct ubifs_ino_node - inode node. @@ -502,7 +500,7 @@ struct ubifs_ino_node { __le16 compr_type; __u8 padding2[26]; /* Watch 'zero_ino_node_unused()' if changing! */ __u8 data[]; -} __attribute__ ((packed)); +} __packed; /** * struct ubifs_dent_node - directory entry node. @@ -526,8 +524,12 @@ struct ubifs_dent_node { __u8 type; __le16 nlen; __u8 padding2[4]; /* Watch 'zero_dent_node_unused()' if changing! */ +#ifndef __UBOOT__ __u8 name[]; -} __attribute__ ((packed)); +#else + char name[]; +#endif +} __packed; /** * struct ubifs_data_node - data node. @@ -548,7 +550,7 @@ struct ubifs_data_node { __le16 compr_type; __u8 padding[2]; /* Watch 'zero_data_node_unused()' if changing! */ __u8 data[]; -} __attribute__ ((packed)); +} __packed; /** * struct ubifs_trun_node - truncation node. @@ -568,7 +570,7 @@ struct ubifs_trun_node { __u8 padding[12]; /* Watch 'zero_trun_node_unused()' if changing! */ __le64 old_size; __le64 new_size; -} __attribute__ ((packed)); +} __packed; /** * struct ubifs_pad_node - padding node. @@ -579,7 +581,7 @@ struct ubifs_trun_node { struct ubifs_pad_node { struct ubifs_ch ch; __le32 pad_len; -} __attribute__ ((packed)); +} __packed; /** * struct ubifs_sb_node - superblock node. @@ -637,7 +639,7 @@ struct ubifs_sb_node { __u8 uuid[16]; __le32 ro_compat_version; __u8 padding2[3968]; -} __attribute__ ((packed)); +} __packed; /** * struct ubifs_mst_node - master node. @@ -704,7 +706,7 @@ struct ubifs_mst_node { __le32 idx_lebs; __le32 leb_cnt; __u8 padding[344]; -} __attribute__ ((packed)); +} __packed; /** * struct ubifs_ref_node - logical eraseblock reference node. @@ -720,7 +722,7 @@ struct ubifs_ref_node { __le32 offs; __le32 jhead; __u8 padding[28]; -} __attribute__ ((packed)); +} __packed; /** * struct ubifs_branch - key/reference/length branch @@ -733,8 +735,12 @@ struct ubifs_branch { __le32 lnum; __le32 offs; __le32 len; +#ifndef __UBOOT__ __u8 key[]; -} __attribute__ ((packed)); +#else + char key[]; +#endif +} __packed; /** * struct ubifs_idx_node - indexing node. @@ -747,8 +753,12 @@ struct ubifs_idx_node { struct ubifs_ch ch; __le16 child_cnt; __le16 level; +#ifndef __UBOOT__ __u8 branches[]; -} __attribute__ ((packed)); +#else + char branches[]; +#endif +} __packed; /** * struct ubifs_cs_node - commit start node. @@ -758,7 +768,7 @@ struct ubifs_idx_node { struct ubifs_cs_node { struct ubifs_ch ch; __le64 cmt_no; -} __attribute__ ((packed)); +} __packed; /** * struct ubifs_orph_node - orphan node. @@ -770,6 +780,6 @@ struct ubifs_orph_node { struct ubifs_ch ch; __le64 cmt_no; __le64 inos[]; -} __attribute__ ((packed)); +} __packed; #endif /* __UBIFS_MEDIA_H__ */ diff --git a/fs/ubifs/ubifs.c b/fs/ubifs/ubifs.c index 273c0a9..b91a6fd 100644 --- a/fs/ubifs/ubifs.c +++ b/fs/ubifs/ubifs.c @@ -26,6 +26,10 @@ #include "ubifs.h" #include <u-boot/zlib.h> +#define __UBOOT__ +#include <linux/err.h> +#include <linux/lzo.h> + DECLARE_GLOBAL_DATA_PTR; /* compress.c */ @@ -44,20 +48,27 @@ static int gzip_decompress(const unsigned char *in, size_t in_len, /* Fake description object for the "none" compressor */ static struct ubifs_compressor none_compr = { .compr_type = UBIFS_COMPR_NONE, - .name = "no compression", + .name = "none", .capi_name = "", .decompress = NULL, }; static struct ubifs_compressor lzo_compr = { .compr_type = UBIFS_COMPR_LZO, - .name = "LZO", +#ifndef __UBOOT__ + .comp_mutex = &lzo_mutex, +#endif + .name = "lzo", .capi_name = "lzo", .decompress = lzo1x_decompress_safe, }; static struct ubifs_compressor zlib_compr = { .compr_type = UBIFS_COMPR_ZLIB, +#ifndef __UBOOT__ + .comp_mutex = &deflate_mutex, + .decomp_mutex = &inflate_mutex, +#endif .name = "zlib", .capi_name = "deflate", .decompress = gzip_decompress, @@ -66,6 +77,82 @@ static struct ubifs_compressor zlib_compr = { /* All UBIFS compressors */ struct ubifs_compressor *ubifs_compressors[UBIFS_COMPR_TYPES_CNT]; + +#ifdef __UBOOT__ +/* from mm/util.c */ + +/** + * kmemdup - duplicate region of memory + * + * @src: memory region to duplicate + * @len: memory region length + * @gfp: GFP mask to use + */ +void *kmemdup(const void *src, size_t len, gfp_t gfp) +{ + void *p; + + p = kmalloc(len, gfp); + if (p) + memcpy(p, src, len); + return p; +} + +struct crypto_comp { + int compressor; +}; + +static inline struct crypto_comp *crypto_alloc_comp(const char *alg_name, + u32 type, u32 mask) +{ + struct ubifs_compressor *comp; + struct crypto_comp *ptr; + int i = 0; + + ptr = malloc(sizeof(struct crypto_comp)); + while (i < UBIFS_COMPR_TYPES_CNT) { + comp = ubifs_compressors[i]; + if (!comp) { + i++; + continue; + } + if (strncmp(alg_name, comp->capi_name, strlen(alg_name)) == 0) { + ptr->compressor = i; + return ptr; + } + i++; + } + if (i >= UBIFS_COMPR_TYPES_CNT) { + ubifs_err("invalid compression type %s", alg_name); + free (ptr); + return NULL; + } + return ptr; +} +static inline int crypto_comp_decompress(struct crypto_comp *tfm, + const u8 *src, unsigned int slen, + u8 *dst, unsigned int *dlen) +{ + struct ubifs_compressor *compr = ubifs_compressors[tfm->compressor]; + int err; + + if (compr->compr_type == UBIFS_COMPR_NONE) { + memcpy(dst, src, slen); + *dlen = slen; + return 0; + } + + err = compr->decompress(src, slen, dst, (size_t *)dlen); + if (err) + ubifs_err("cannot decompress %d bytes, compressor %s, " + "error %d", slen, compr->name, err); + + return err; + + return 0; +} +#endif + /** * ubifs_decompress - decompress data. * @in_buf: data to decompress @@ -102,10 +189,15 @@ int ubifs_decompress(const void *in_buf, int in_len, void *out_buf, return 0; } - err = compr->decompress(in_buf, in_len, out_buf, (size_t *)out_len); + if (compr->decomp_mutex) + mutex_lock(compr->decomp_mutex); + err = crypto_comp_decompress(compr->cc, in_buf, in_len, out_buf, + (unsigned int *)out_len); + if (compr->decomp_mutex) + mutex_unlock(compr->decomp_mutex); if (err) - ubifs_err("cannot decompress %d bytes, compressor %s, " - "error %d", in_len, compr->name, err); + ubifs_err("cannot decompress %d bytes, compressor %s, error %d", + in_len, compr->name, err); return err; } @@ -127,6 +219,15 @@ static int __init compr_init(struct ubifs_compressor *compr) ubifs_compressors[compr->compr_type]->decompress += gd->reloc_off; #endif + if (compr->capi_name) { + compr->cc = crypto_alloc_comp(compr->capi_name, 0, 0); + if (IS_ERR(compr->cc)) { + ubifs_err("cannot initialize compressor %s, error %ld", + compr->name, PTR_ERR(compr->cc)); + return PTR_ERR(compr->cc); + } + } + return 0; } @@ -188,7 +289,9 @@ static int filldir(struct ubifs_info *c, const char *name, int namlen, } ctime_r((time_t *)&inode->i_mtime, filetime); printf("%9lld %24.24s ", inode->i_size, filetime); +#ifndef __UBOOT__ ubifs_iput(inode); +#endif printf("%s\n", name); @@ -562,7 +665,7 @@ static int read_block(struct inode *inode, void *addr, unsigned int block, dump: ubifs_err("bad data node (block %u, inode %lu)", block, inode->i_ino); - dbg_dump_node(c, dn); + ubifs_dump_node(c, dn); return -EINVAL; } diff --git a/fs/ubifs/ubifs.h b/fs/ubifs/ubifs.h index 2213201..acc6a40 100644 --- a/fs/ubifs/ubifs.h +++ b/fs/ubifs/ubifs.h @@ -6,18 +6,7 @@ * (C) Copyright 2008-2009 * Stefan Roese, DENX Software Engineering, sr@denx.de. * - * 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. - * - * 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., 51 - * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * SPDX-License-Identifier: GPL-2.0+ * * Authors: Artem Bityutskiy (Битюцкий Артём) * Adrian Hunter @@ -26,12 +15,25 @@ #ifndef __UBIFS_H__ #define __UBIFS_H__ -#if 0 /* Enable for debugging output */ -#define CONFIG_UBIFS_FS_DEBUG -#define CONFIG_UBIFS_FS_DEBUG_MSG_LVL 3 -#endif - +#define __UBOOT__ +#ifndef __UBOOT__ +#include <asm/div64.h> +#include <linux/statfs.h> +#include <linux/fs.h> +#include <linux/err.h> +#include <linux/sched.h> +#include <linux/slab.h> +#include <linux/vmalloc.h> +#include <linux/spinlock.h> +#include <linux/mutex.h> +#include <linux/rwsem.h> +#include <linux/mtd/ubi.h> +#include <linux/pagemap.h> +#include <linux/backing-dev.h> +#include "ubifs-media.h" +#else #include <ubi_uboot.h> + #include <linux/ctype.h> #include <linux/time.h> #include <linux/math64.h> @@ -70,13 +72,26 @@ void iput(struct inode *inode); #define atomic_long_dec(a) #define atomic_long_sub(a, b) +typedef unsigned long atomic_long_t; + /* linux/include/time.h */ +#define NSEC_PER_SEC 1000000000L +#define get_seconds() 0 +#define CURRENT_TIME_SEC ((struct timespec) { get_seconds(), 0 }) struct timespec { time_t tv_sec; /* seconds */ long tv_nsec; /* nanoseconds */ }; +static struct timespec current_fs_time(struct super_block *sb) +{ + struct timespec now; + now.tv_sec = 0; + now.tv_nsec = 0; + return now; +}; + /* linux/include/dcache.h */ /* @@ -89,111 +104,245 @@ struct timespec { struct qstr { unsigned int hash; unsigned int len; +#ifndef __UBOOT__ const char *name; +#else + char *name; +#endif +}; + +/* include/linux/fs.h */ + +/* Possible states of 'frozen' field */ +enum { + SB_UNFROZEN = 0, /* FS is unfrozen */ + SB_FREEZE_WRITE = 1, /* Writes, dir ops, ioctls frozen */ + SB_FREEZE_PAGEFAULT = 2, /* Page faults stopped as well */ + SB_FREEZE_FS = 3, /* For internal FS use (e.g. to stop + * internal threads if needed) */ + SB_FREEZE_COMPLETE = 4, /* ->freeze_fs finished successfully */ }; +#define SB_FREEZE_LEVELS (SB_FREEZE_COMPLETE - 1) + +struct sb_writers { +#ifndef __UBOOT__ + /* Counters for counting writers at each level */ + struct percpu_counter counter[SB_FREEZE_LEVELS]; +#endif + wait_queue_head_t wait; /* queue for waiting for + writers / faults to finish */ + int frozen; /* Is sb frozen? */ + wait_queue_head_t wait_unfrozen; /* queue for waiting for + sb to be thawed */ +#ifdef CONFIG_DEBUG_LOCK_ALLOC + struct lockdep_map lock_map[SB_FREEZE_LEVELS]; +#endif +}; + +struct address_space { + struct inode *host; /* owner: inode, block_device */ +#ifndef __UBOOT__ + struct radix_tree_root page_tree; /* radix tree of all pages */ +#endif + spinlock_t tree_lock; /* and lock protecting it */ + unsigned int i_mmap_writable;/* count VM_SHARED mappings */ + struct rb_root i_mmap; /* tree of private and shared mappings */ + struct list_head i_mmap_nonlinear;/*list VM_NONLINEAR mappings */ + struct mutex i_mmap_mutex; /* protect tree, count, list */ + /* Protected by tree_lock together with the radix tree */ + unsigned long nrpages; /* number of total pages */ + pgoff_t writeback_index;/* writeback starts here */ + const struct address_space_operations *a_ops; /* methods */ + unsigned long flags; /* error bits/gfp mask */ +#ifndef __UBOOT__ + struct backing_dev_info *backing_dev_info; /* device readahead, etc */ +#endif + spinlock_t private_lock; /* for use by the address_space */ + struct list_head private_list; /* ditto */ + void *private_data; /* ditto */ +} __attribute__((aligned(sizeof(long)))); + +/* + * Keep mostly read-only and often accessed (especially for + * the RCU path lookup and 'stat' data) fields at the beginning + * of the 'struct inode' + */ struct inode { - struct hlist_node i_hash; - struct list_head i_list; - struct list_head i_sb_list; - struct list_head i_dentry; + umode_t i_mode; + unsigned short i_opflags; + kuid_t i_uid; + kgid_t i_gid; + unsigned int i_flags; + +#ifdef CONFIG_FS_POSIX_ACL + struct posix_acl *i_acl; + struct posix_acl *i_default_acl; +#endif + + const struct inode_operations *i_op; + struct super_block *i_sb; + struct address_space *i_mapping; + +#ifdef CONFIG_SECURITY + void *i_security; +#endif + + /* Stat data, not accessed from path walking */ unsigned long i_ino; - unsigned int i_nlink; - uid_t i_uid; - gid_t i_gid; + /* + * Filesystems may only read i_nlink directly. They shall use the + * following functions for modification: + * + * (set|clear|inc|drop)_nlink + * inode_(inc|dec)_link_count + */ + union { + const unsigned int i_nlink; + unsigned int __i_nlink; + }; dev_t i_rdev; - u64 i_version; loff_t i_size; -#ifdef __NEED_I_SIZE_ORDERED - seqcount_t i_size_seqcount; -#endif struct timespec i_atime; struct timespec i_mtime; struct timespec i_ctime; - unsigned int i_blkbits; - unsigned short i_bytes; - umode_t i_mode; spinlock_t i_lock; /* i_blocks, i_bytes, maybe i_size */ + unsigned short i_bytes; + unsigned int i_blkbits; + blkcnt_t i_blocks; + +#ifdef __NEED_I_SIZE_ORDERED + seqcount_t i_size_seqcount; +#endif + + /* Misc */ + unsigned long i_state; struct mutex i_mutex; - struct rw_semaphore i_alloc_sem; - const struct inode_operations *i_op; + + unsigned long dirtied_when; /* jiffies of first dirtying */ + + struct hlist_node i_hash; + struct list_head i_wb_list; /* backing dev IO list */ + struct list_head i_lru; /* inode LRU list */ + struct list_head i_sb_list; + union { + struct hlist_head i_dentry; + struct rcu_head i_rcu; + }; + u64 i_version; + atomic_t i_count; + atomic_t i_dio_count; + atomic_t i_writecount; const struct file_operations *i_fop; /* former ->i_op->default_file_ops */ - struct super_block *i_sb; struct file_lock *i_flock; + struct address_space i_data; #ifdef CONFIG_QUOTA struct dquot *i_dquot[MAXQUOTAS]; #endif struct list_head i_devices; - int i_cindex; + union { + struct pipe_inode_info *i_pipe; + struct block_device *i_bdev; + struct cdev *i_cdev; + }; __u32 i_generation; -#ifdef CONFIG_DNOTIFY - unsigned long i_dnotify_mask; /* Directory notify events */ - struct dnotify_struct *i_dnotify; /* for directory notifications */ +#ifdef CONFIG_FSNOTIFY + __u32 i_fsnotify_mask; /* all events this inode cares about */ + struct hlist_head i_fsnotify_marks; #endif -#ifdef CONFIG_INOTIFY - struct list_head inotify_watches; /* watches on this inode */ - struct mutex inotify_mutex; /* protects the watches list */ +#ifdef CONFIG_IMA + atomic_t i_readcount; /* struct files open RO */ #endif + void *i_private; /* fs or device private pointer */ +}; - unsigned long i_state; - unsigned long dirtied_when; /* jiffies of first dirtying */ - - unsigned int i_flags; - -#ifdef CONFIG_SECURITY - void *i_security; +struct super_operations { + struct inode *(*alloc_inode)(struct super_block *sb); + void (*destroy_inode)(struct inode *); + + void (*dirty_inode) (struct inode *, int flags); + int (*write_inode) (struct inode *, struct writeback_control *wbc); + int (*drop_inode) (struct inode *); + void (*evict_inode) (struct inode *); + void (*put_super) (struct super_block *); + int (*sync_fs)(struct super_block *sb, int wait); + int (*freeze_fs) (struct super_block *); + int (*unfreeze_fs) (struct super_block *); +#ifndef __UBOOT__ + int (*statfs) (struct dentry *, struct kstatfs *); #endif - void *i_private; /* fs or device private pointer */ + int (*remount_fs) (struct super_block *, int *, char *); + void (*umount_begin) (struct super_block *); + +#ifndef __UBOOT__ + int (*show_options)(struct seq_file *, struct dentry *); + int (*show_devname)(struct seq_file *, struct dentry *); + int (*show_path)(struct seq_file *, struct dentry *); + int (*show_stats)(struct seq_file *, struct dentry *); +#endif +#ifdef CONFIG_QUOTA + ssize_t (*quota_read)(struct super_block *, int, char *, size_t, loff_t); + ssize_t (*quota_write)(struct super_block *, int, const char *, size_t, loff_t); +#endif + int (*bdev_try_to_free_page)(struct super_block*, struct page*, gfp_t); + long (*nr_cached_objects)(struct super_block *, int); + long (*free_cached_objects)(struct super_block *, long, int); }; struct super_block { struct list_head s_list; /* Keep this first */ dev_t s_dev; /* search index; _not_ kdev_t */ - unsigned long s_blocksize; unsigned char s_blocksize_bits; - unsigned char s_dirt; - unsigned long long s_maxbytes; /* Max file size */ + unsigned long s_blocksize; + loff_t s_maxbytes; /* Max file size */ struct file_system_type *s_type; const struct super_operations *s_op; - struct dquot_operations *dq_op; - struct quotactl_ops *s_qcop; + const struct dquot_operations *dq_op; + const struct quotactl_ops *s_qcop; const struct export_operations *s_export_op; unsigned long s_flags; unsigned long s_magic; struct dentry *s_root; struct rw_semaphore s_umount; - struct mutex s_lock; int s_count; - int s_syncing; - int s_need_sync_fs; + atomic_t s_active; #ifdef CONFIG_SECURITY void *s_security; #endif - struct xattr_handler **s_xattr; + const struct xattr_handler **s_xattr; struct list_head s_inodes; /* all inodes */ - struct list_head s_dirty; /* dirty inodes */ - struct list_head s_io; /* parked for writeback */ - struct list_head s_more_io; /* parked for more writeback */ - struct hlist_head s_anon; /* anonymous dentries for (nfs) exporting */ - struct list_head s_files; - /* s_dentry_lru and s_nr_dentry_unused are protected by dcache_lock */ - struct list_head s_dentry_lru; /* unused dentry lru */ - int s_nr_dentry_unused; /* # of dentry on lru */ - +#ifndef __UBOOT__ + struct hlist_bl_head s_anon; /* anonymous dentries for (nfs) exporting */ +#endif + struct list_head s_mounts; /* list of mounts; _not_ for fs use */ struct block_device *s_bdev; +#ifndef __UBOOT__ + struct backing_dev_info *s_bdi; +#endif struct mtd_info *s_mtd; - struct list_head s_instances; + struct hlist_node s_instances; +#ifndef __UBOOT__ + struct quota_info s_dquot; /* Diskquota specific options */ +#endif - int s_frozen; - wait_queue_head_t s_wait_unfrozen; + struct sb_writers s_writers; char s_id[32]; /* Informational name */ + u8 s_uuid[16]; /* UUID */ void *s_fs_info; /* Filesystem private info */ + unsigned int s_max_links; +#ifndef __UBOOT__ + fmode_t s_mode; +#endif + + /* Granularity of c/m/atime in ns. + Cannot be worse than a second */ + u32 s_time_gran; /* * The next field is for VFS *only*. No filesystems have any business @@ -201,66 +350,83 @@ struct super_block { */ struct mutex s_vfs_rename_mutex; /* Kludge */ - /* Granularity of c/m/atime in ns. - Cannot be worse than a second */ - u32 s_time_gran; - /* * Filesystem subtype. If non-empty the filesystem type field * in /proc/mounts will be "type.subtype" */ char *s_subtype; +#ifndef __UBOOT__ /* * Saved mount options for lazy filesystems using * generic_show_options() */ - char *s_options; + char __rcu *s_options; +#endif + const struct dentry_operations *s_d_op; /* default d_op for dentries */ + + /* + * Saved pool identifier for cleancache (-1 means none) + */ + int cleancache_poolid; + +#ifndef __UBOOT__ + struct shrinker s_shrink; /* per-sb shrinker handle */ +#endif + + /* Number of inodes with nlink == 0 but still referenced */ + atomic_long_t s_remove_count; + + /* Being remounted read-only */ + int s_readonly_remount; + + /* AIO completions deferred from interrupt context */ + struct workqueue_struct *s_dio_done_wq; + +#ifndef __UBOOT__ + /* + * Keep the lru lists last in the structure so they always sit on their + * own individual cachelines. + */ + struct list_lru s_dentry_lru ____cacheline_aligned_in_smp; + struct list_lru s_inode_lru ____cacheline_aligned_in_smp; +#endif + struct rcu_head rcu; }; struct file_system_type { const char *name; int fs_flags; - int (*get_sb) (struct file_system_type *, int, - const char *, void *, struct vfsmount *); +#define FS_REQUIRES_DEV 1 +#define FS_BINARY_MOUNTDATA 2 +#define FS_HAS_SUBTYPE 4 +#define FS_USERNS_MOUNT 8 /* Can be mounted by userns root */ +#define FS_USERNS_DEV_MOUNT 16 /* A userns mount does not imply MNT_NODEV */ +#define FS_RENAME_DOES_D_MOVE 32768 /* FS will handle d_move() during rename() internally. */ + struct dentry *(*mount) (struct file_system_type *, int, + const char *, void *); void (*kill_sb) (struct super_block *); struct module *owner; struct file_system_type * next; - struct list_head fs_supers; + struct hlist_head fs_supers; + +#ifndef __UBOOT__ + struct lock_class_key s_lock_key; + struct lock_class_key s_umount_key; + struct lock_class_key s_vfs_rename_key; + struct lock_class_key s_writers_key[SB_FREEZE_LEVELS]; + + struct lock_class_key i_lock_key; + struct lock_class_key i_mutex_key; + struct lock_class_key i_mutex_dir_key; +#endif }; +/* include/linux/mount.h */ struct vfsmount { - struct list_head mnt_hash; - struct vfsmount *mnt_parent; /* fs we are mounted on */ - struct dentry *mnt_mountpoint; /* dentry of mountpoint */ struct dentry *mnt_root; /* root of the mounted tree */ struct super_block *mnt_sb; /* pointer to superblock */ - struct list_head mnt_mounts; /* list of children, anchored here */ - struct list_head mnt_child; /* and going through their mnt_child */ int mnt_flags; - /* 4 bytes hole on 64bits arches */ - const char *mnt_devname; /* Name of device e.g. /dev/dsk/hda1 */ - struct list_head mnt_list; - struct list_head mnt_expire; /* link in fs-specific expiry list */ - struct list_head mnt_share; /* circular list of shared mounts */ - struct list_head mnt_slave_list;/* list of slave mounts */ - struct list_head mnt_slave; /* slave list entry */ - struct vfsmount *mnt_master; /* slave is on master->mnt_slave_list */ - struct mnt_namespace *mnt_ns; /* containing namespace */ - int mnt_id; /* mount identifier */ - int mnt_group_id; /* peer group identifier */ - /* - * We put mnt_count & mnt_expiry_mark at the end of struct vfsmount - * to let these frequently modified fields in a separate cache line - * (so that reads of mnt_flags wont ping-pong on SMP machines) - */ - int mnt_expiry_mark; /* true if marked for expiry */ - int mnt_pinned; - int mnt_ghosts; - /* - * This value is not stable unless all of the mnt_writers[] spinlocks - * are held, and all mnt_writer[]s on this mount have 0 as their ->count - */ }; struct path { @@ -451,32 +617,35 @@ static inline ino_t parent_ino(struct dentry *dentry) /* debug.c */ -#define DEFINE_SPINLOCK(...) #define module_param_named(...) /* misc.h */ #define mutex_lock_nested(...) #define mutex_unlock_nested(...) #define mutex_is_locked(...) 0 +#endif /* Version of this UBIFS implementation */ #define UBIFS_VERSION 1 /* Normal UBIFS messages */ -#ifdef CONFIG_UBIFS_SILENCE_MSG -#define ubifs_msg(fmt, ...) -#else -#define ubifs_msg(fmt, ...) \ - printk(KERN_NOTICE "UBIFS: " fmt "\n", ##__VA_ARGS__) -#endif +#define ubifs_msg(fmt, ...) pr_notice("UBIFS: " fmt "\n", ##__VA_ARGS__) /* UBIFS error messages */ -#define ubifs_err(fmt, ...) \ - printk(KERN_ERR "UBIFS error (pid %d): %s: " fmt "\n", 0, \ +#ifndef __UBOOT__ +#define ubifs_err(fmt, ...) \ + pr_err("UBIFS error (pid %d): %s: " fmt "\n", current->pid, \ __func__, ##__VA_ARGS__) /* UBIFS warning messages */ -#define ubifs_warn(fmt, ...) \ - printk(KERN_WARNING "UBIFS warning (pid %d): %s: " fmt "\n", \ - 0, __func__, ##__VA_ARGS__) +#define ubifs_warn(fmt, ...) \ + pr_warn("UBIFS warning (pid %d): %s: " fmt "\n", \ + current->pid, __func__, ##__VA_ARGS__) +#else +#define ubifs_err(fmt, ...) \ + pr_err("UBIFS error: %s: " fmt "\n", __func__, ##__VA_ARGS__) +/* UBIFS warning messages */ +#define ubifs_warn(fmt, ...) \ + pr_warn("UBIFS warning: %s: " fmt "\n", __func__, ##__VA_ARGS__) +#endif /* UBIFS file system VFS magic number */ #define UBIFS_SUPER_MAGIC 0x24051905 @@ -509,9 +678,6 @@ static inline ino_t parent_ino(struct dentry *dentry) #define INUM_WARN_WATERMARK 0xFFF00000 #define INUM_WATERMARK 0xFFFFFF00 -/* Largest key size supported in this implementation */ -#define CUR_MAX_KEY_LEN UBIFS_SK_LEN - /* Maximum number of entries in each LPT (LEB category) heap */ #define LPT_HEAP_SZ 256 @@ -521,8 +687,9 @@ static inline ino_t parent_ino(struct dentry *dentry) */ #define BGT_NAME_PATTERN "ubifs_bgt%d_%d" -/* Default write-buffer synchronization timeout (5 secs) */ -#define DEFAULT_WBUF_TIMEOUT (5 * HZ) +/* Write-buffer synchronization timeout interval in seconds */ +#define WBUF_TIMEOUT_SOFTLIMIT 3 +#define WBUF_TIMEOUT_HARDLIMIT 5 /* Maximum possible inode number (only 32-bit inodes are supported now) */ #define MAX_INUM 0xFFFFFFFF @@ -530,12 +697,10 @@ static inline ino_t parent_ino(struct dentry *dentry) /* Number of non-data journal heads */ #define NONDATA_JHEADS_CNT 2 -/* Garbage collector head */ -#define GCHD 0 -/* Base journal head number */ -#define BASEHD 1 -/* First "general purpose" journal head */ -#define DATAHD 2 +/* Shorter names for journal head numbers for internal usage */ +#define GCHD UBIFS_GC_HEAD +#define BASEHD UBIFS_BASE_HEAD +#define DATAHD UBIFS_DATA_HEAD /* 'No change' value for 'ubifs_change_lp()' */ #define LPROPS_NC 0x80000001 @@ -545,8 +710,12 @@ static inline ino_t parent_ino(struct dentry *dentry) * in TNC. However, when replaying, it is handy to introduce fake "truncation" * keys for truncation nodes because the code becomes simpler. So we define * %UBIFS_TRUN_KEY type. + * + * But otherwise, out of the journal reply scope, the truncation keys are + * invalid. */ -#define UBIFS_TRUN_KEY UBIFS_KEY_TYPES_CNT +#define UBIFS_TRUN_KEY UBIFS_KEY_TYPES_CNT +#define UBIFS_INVALID_KEY UBIFS_KEY_TYPES_CNT /* * How much a directory entry/extended attribute entry adds to the parent/host @@ -573,6 +742,12 @@ static inline ino_t parent_ino(struct dentry *dentry) */ #define WORST_COMPR_FACTOR 2 +/* + * How much memory is needed for a buffer where we comress a data node. + */ +#define COMPRESSED_DATA_NODE_BUF_SZ \ + (UBIFS_DATA_NODE_SZ + UBIFS_BLOCK_SIZE * WORST_COMPR_FACTOR) + /* Maximum expected tree height for use by bottom_up_buf */ #define BOTTOM_UP_HEIGHT 64 @@ -646,14 +821,14 @@ enum { * LPT cnode flag bits. * * DIRTY_CNODE: cnode is dirty - * COW_CNODE: cnode is being committed and must be copied before writing * OBSOLETE_CNODE: cnode is being committed and has been copied (or deleted), - * so it can (and must) be freed when the commit is finished + * so it can (and must) be freed when the commit is finished + * COW_CNODE: cnode is being committed and must be copied before writing */ enum { DIRTY_CNODE = 0, - COW_CNODE = 1, - OBSOLETE_CNODE = 2, + OBSOLETE_CNODE = 1, + COW_CNODE = 2, }; /* @@ -693,10 +868,10 @@ struct ubifs_old_idx { /* The below union makes it easier to deal with keys */ union ubifs_key { - uint8_t u8[CUR_MAX_KEY_LEN]; - uint32_t u32[CUR_MAX_KEY_LEN/4]; - uint64_t u64[CUR_MAX_KEY_LEN/8]; - __le32 j32[CUR_MAX_KEY_LEN/4]; + uint8_t u8[UBIFS_SK_LEN]; + uint32_t u32[UBIFS_SK_LEN/4]; + uint64_t u64[UBIFS_SK_LEN/8]; + __le32 j32[UBIFS_SK_LEN/4]; }; /** @@ -805,9 +980,9 @@ struct ubifs_gced_idx_leb { * The @ui_size is a "shadow" variable for @inode->i_size and UBIFS uses * @ui_size instead of @inode->i_size. The reason for this is that UBIFS cannot * make sure @inode->i_size is always changed under @ui_mutex, because it - * cannot call 'vmtruncate()' with @ui_mutex locked, because it would deadlock - * with 'ubifs_writepage()' (see file.c). All the other inode fields are - * changed under @ui_mutex, so they do not need "shadow" fields. Note, one + * cannot call 'truncate_setsize()' with @ui_mutex locked, because it would + * deadlock with 'ubifs_writepage()' (see file.c). All the other inode fields + * are changed under @ui_mutex, so they do not need "shadow" fields. Note, one * could consider to rework locking and base it on "shadow" fields. */ struct ubifs_inode { @@ -1068,17 +1243,19 @@ typedef int (*ubifs_lpt_scan_callback)(struct ubifs_info *c, * @offs: write-buffer offset in this logical eraseblock * @avail: number of bytes available in the write-buffer * @used: number of used bytes in the write-buffer - * @dtype: type of data stored in this LEB (%UBI_LONGTERM, %UBI_SHORTTERM, - * %UBI_UNKNOWN) + * @size: write-buffer size (in [@c->min_io_size, @c->max_write_size] range) * @jhead: journal head the mutex belongs to (note, needed only to shut lockdep * up by 'mutex_lock_nested()). * @sync_callback: write-buffer synchronization callback * @io_mutex: serializes write-buffer I/O * @lock: serializes @buf, @lnum, @offs, @avail, @used, @next_ino and @inodes * fields + * @softlimit: soft write-buffer timeout interval + * @delta: hard and soft timeouts delta (the timer expire inteval is @softlimit + * and @softlimit + @delta) * @timer: write-buffer timer - * @timeout: timer expire interval in jiffies - * @need_sync: it is set if its timer expired and needs sync + * @no_timer: non-zero if this write-buffer does not have a timer + * @need_sync: non-zero if the timer expired and the wbuf needs sync'ing * @next_ino: points to the next position of the following inode number * @inodes: stores the inode numbers of the nodes which are in wbuf * @@ -1099,13 +1276,16 @@ struct ubifs_wbuf { int offs; int avail; int used; - int dtype; + int size; int jhead; int (*sync_callback)(struct ubifs_info *c, int lnum, int free, int pad); struct mutex io_mutex; spinlock_t lock; - int timeout; - int need_sync; +// ktime_t softlimit; +// unsigned long long delta; +// struct hrtimer timer; + unsigned int no_timer:1; + unsigned int need_sync:1; int next_ino; ino_t *inodes; }; @@ -1130,12 +1310,14 @@ struct ubifs_bud { * struct ubifs_jhead - journal head. * @wbuf: head's write-buffer * @buds_list: list of bud LEBs belonging to this journal head + * @grouped: non-zero if UBIFS groups nodes when writing to this journal head * * Note, the @buds list is protected by the @c->buds_lock. */ struct ubifs_jhead { struct ubifs_wbuf wbuf; struct list_head buds_list; + unsigned int grouped:1; }; /** @@ -1171,6 +1353,9 @@ struct ubifs_zbranch { * @offs: offset of the corresponding indexing node * @len: length of the corresponding indexing node * @zbranch: array of znode branches (@c->fanout elements) + * + * Note! The @lnum, @offs, and @len fields are not really needed - we have them + * only for internal consistency check. They could be removed to save some RAM. */ struct ubifs_znode { struct ubifs_znode *parent; @@ -1181,9 +1366,9 @@ struct ubifs_znode { int child_cnt; int iip; int alt; -#ifdef CONFIG_UBIFS_FS_DEBUG - int lnum, offs, len; -#endif + int lnum; + int offs; + int len; struct ubifs_zbranch zbranch[]; }; @@ -1236,10 +1421,15 @@ struct ubifs_node_range { */ struct ubifs_compressor { int compr_type; - char *name; - char *capi_name; + struct crypto_comp *cc; + struct mutex *comp_mutex; + struct mutex *decomp_mutex; + const char *name; + const char *capi_name; +#ifdef __UBOOT__ int (*decompress)(const unsigned char *in, size_t in_len, unsigned char *out, size_t *out_len); +#endif }; /** @@ -1313,6 +1503,8 @@ struct ubifs_budget_req { * @dnext: next orphan to delete * @inum: inode number * @new: %1 => added since the last commit, otherwise %0 + * @cmt: %1 => commit pending, otherwise %0 + * @del: %1 => delete pending, otherwise %0 */ struct ubifs_orphan { struct rb_node rb; @@ -1321,7 +1513,9 @@ struct ubifs_orphan { struct ubifs_orphan *cnext; struct ubifs_orphan *dnext; ino_t inum; - int new; + unsigned new:1; + unsigned cmt:1; + unsigned del:1; }; /** @@ -1344,6 +1538,40 @@ struct ubifs_mount_opts { unsigned int compr_type:2; }; +/** + * struct ubifs_budg_info - UBIFS budgeting information. + * @idx_growth: amount of bytes budgeted for index growth + * @data_growth: amount of bytes budgeted for cached data + * @dd_growth: amount of bytes budgeted for cached data that will make + * other data dirty + * @uncommitted_idx: amount of bytes were budgeted for growth of the index, but + * which still have to be taken into account because the index + * has not been committed so far + * @old_idx_sz: size of index on flash + * @min_idx_lebs: minimum number of LEBs required for the index + * @nospace: non-zero if the file-system does not have flash space (used as + * optimization) + * @nospace_rp: the same as @nospace, but additionally means that even reserved + * pool is full + * @page_budget: budget for a page (constant, nenver changed after mount) + * @inode_budget: budget for an inode (constant, nenver changed after mount) + * @dent_budget: budget for a directory entry (constant, nenver changed after + * mount) + */ +struct ubifs_budg_info { + long long idx_growth; + long long data_growth; + long long dd_growth; + long long uncommitted_idx; + unsigned long long old_idx_sz; + int min_idx_lebs; + unsigned int nospace:1; + unsigned int nospace_rp:1; + int page_budget; + int inode_budget; + int dent_budget; +}; + struct ubifs_debug_info; /** @@ -1387,6 +1615,7 @@ struct ubifs_debug_info; * @cmt_wq: wait queue to sleep on if the log is full and a commit is running * * @big_lpt: flag that LPT is too big to write whole during commit + * @space_fixup: flag indicating that free space in LEBs needs to be cleaned up * @no_chk_data_crc: do not check CRCs when reading data nodes (except during * recovery) * @bulk_read: enable bulk-reads @@ -1418,6 +1647,11 @@ struct ubifs_debug_info; * @bu_mutex: protects the pre-allocated bulk-read buffer and @c->bu * @bu: pre-allocated bulk-read information * + * @write_reserve_mutex: protects @write_reserve_buf + * @write_reserve_buf: on the write path we allocate memory, which might + * sometimes be unavailable, in which case we use this + * write reserve buffer + * * @log_lebs: number of logical eraseblocks in the log * @log_bytes: log size in bytes * @log_last: last LEB of the log @@ -1439,43 +1673,34 @@ struct ubifs_debug_info; * * @min_io_size: minimal input/output unit size * @min_io_shift: number of bits in @min_io_size minus one + * @max_write_size: maximum amount of bytes the underlying flash can write at a + * time (MTD write buffer size) + * @max_write_shift: number of bits in @max_write_size minus one * @leb_size: logical eraseblock size in bytes + * @leb_start: starting offset of logical eraseblocks within physical + * eraseblocks * @half_leb_size: half LEB size + * @idx_leb_size: how many bytes of an LEB are effectively available when it is + * used to store indexing nodes (@leb_size - @max_idx_node_sz) * @leb_cnt: count of logical eraseblocks * @max_leb_cnt: maximum count of logical eraseblocks * @old_leb_cnt: count of logical eraseblocks before re-size * @ro_media: the underlying UBI volume is read-only + * @ro_mount: the file-system was mounted as read-only + * @ro_error: UBIFS switched to R/O mode because an error happened * * @dirty_pg_cnt: number of dirty pages (not used) * @dirty_zn_cnt: number of dirty znodes * @clean_zn_cnt: number of clean znodes * - * @budg_idx_growth: amount of bytes budgeted for index growth - * @budg_data_growth: amount of bytes budgeted for cached data - * @budg_dd_growth: amount of bytes budgeted for cached data that will make - * other data dirty - * @budg_uncommitted_idx: amount of bytes were budgeted for growth of the index, - * but which still have to be taken into account because - * the index has not been committed so far - * @space_lock: protects @budg_idx_growth, @budg_data_growth, @budg_dd_growth, - * @budg_uncommited_idx, @min_idx_lebs, @old_idx_sz, @lst, - * @nospace, and @nospace_rp; - * @min_idx_lebs: minimum number of LEBs required for the index - * @old_idx_sz: size of index on flash + * @space_lock: protects @bi and @lst + * @lst: lprops statistics + * @bi: budgeting information * @calc_idx_sz: temporary variable which is used to calculate new index size * (contains accurate new index size at end of TNC commit start) - * @lst: lprops statistics - * @nospace: non-zero if the file-system does not have flash space (used as - * optimization) - * @nospace_rp: the same as @nospace, but additionally means that even reserved - * pool is full - * - * @page_budget: budget for a page - * @inode_budget: budget for an inode - * @dent_budget: budget for a directory entry * * @ref_node_alsz: size of the LEB reference node aligned to the min. flash - * I/O unit + * I/O unit * @mst_node_alsz: master node aligned size * @min_idx_node_sz: minimum indexing node aligned on 8-bytes boundary * @max_idx_node_sz: maximum indexing node aligned on 8-bytes boundary @@ -1558,9 +1783,11 @@ struct ubifs_debug_info; * previous commit start * @uncat_list: list of un-categorized LEBs * @empty_list: list of empty LEBs - * @freeable_list: list of freeable non-index LEBs (free + dirty == leb_size) - * @frdi_idx_list: list of freeable index LEBs (free + dirty == leb_size) + * @freeable_list: list of freeable non-index LEBs (free + dirty == @leb_size) + * @frdi_idx_list: list of freeable index LEBs (free + dirty == @leb_size) * @freeable_cnt: number of freeable LEBs in @freeable_list + * @in_a_category_cnt: count of lprops which are in a certain category, which + * basically meants that they were loaded from the flash * * @ltab_lnum: LEB number of LPT's own lprops table * @ltab_offs: offset of LPT's own lprops table @@ -1577,25 +1804,29 @@ struct ubifs_debug_info; * @rp_uid: reserved pool user ID * @rp_gid: reserved pool group ID * - * @empty: if the UBI device is empty - * @replay_tree: temporary tree used during journal replay + * @empty: %1 if the UBI device is empty + * @need_recovery: %1 if the file-system needs recovery + * @replaying: %1 during journal replay + * @mounting: %1 while mounting + * @remounting_rw: %1 while re-mounting from R/O mode to R/W mode * @replay_list: temporary list used during journal replay * @replay_buds: list of buds to replay * @cs_sqnum: sequence number of first node in the log (commit start node) * @replay_sqnum: sequence number of node currently being replayed - * @need_recovery: file-system needs recovery - * @replaying: set to %1 during journal replay - * @unclean_leb_list: LEBs to recover when mounting ro to rw - * @rcvrd_mst_node: recovered master node to write when mounting ro to rw + * @unclean_leb_list: LEBs to recover when re-mounting R/O mounted FS to R/W + * mode + * @rcvrd_mst_node: recovered master node to write when re-mounting R/O mounted + * FS to R/W mode * @size_tree: inode size information for recovery - * @remounting_rw: set while remounting from ro to rw (sb flags have MS_RDONLY) - * @always_chk_crc: always check CRCs (while mounting and remounting rw) * @mount_opts: UBIFS-specific mount options * * @dbg: debugging-related information */ struct ubifs_info { struct super_block *vfs_sb; +#ifndef __UBOOT__ + struct backing_dev_info bdi; +#endif ino_t highest_inum; unsigned long long max_sqnum; @@ -1628,6 +1859,7 @@ struct ubifs_info { wait_queue_head_t cmt_wq; unsigned int big_lpt:1; + unsigned int space_fixup:1; unsigned int no_chk_data_crc:1; unsigned int bulk_read:1; unsigned int default_compr:2; @@ -1657,6 +1889,9 @@ struct ubifs_info { struct mutex bu_mutex; struct bu_info bu; + struct mutex write_reserve_mutex; + void *write_reserve_buf; + int log_lebs; long long log_bytes; int log_last; @@ -1678,28 +1913,27 @@ struct ubifs_info { int min_io_size; int min_io_shift; + int max_write_size; + int max_write_shift; int leb_size; + int leb_start; int half_leb_size; + int idx_leb_size; int leb_cnt; int max_leb_cnt; int old_leb_cnt; - int ro_media; + unsigned int ro_media:1; + unsigned int ro_mount:1; + unsigned int ro_error:1; + + atomic_long_t dirty_pg_cnt; + atomic_long_t dirty_zn_cnt; + atomic_long_t clean_zn_cnt; - long long budg_idx_growth; - long long budg_data_growth; - long long budg_dd_growth; - long long budg_uncommitted_idx; spinlock_t space_lock; - int min_idx_lebs; - unsigned long long old_idx_sz; - unsigned long long calc_idx_sz; struct ubifs_lp_stats lst; - unsigned int nospace:1; - unsigned int nospace_rp:1; - - int page_budget; - int inode_budget; - int dent_budget; + struct ubifs_budg_info bi; + unsigned long long calc_idx_sz; int ref_node_alsz; int mst_node_alsz; @@ -1785,6 +2019,7 @@ struct ubifs_info { struct list_head freeable_list; struct list_head frdi_idx_list; int freeable_cnt; + int in_a_category_cnt; int ltab_lnum; int ltab_offs; @@ -1798,31 +2033,32 @@ struct ubifs_info { long long rp_size; long long report_rp_size; - uid_t rp_uid; - gid_t rp_gid; + kuid_t rp_uid; + kgid_t rp_gid; /* The below fields are used only during mounting and re-mounting */ - int empty; - struct rb_root replay_tree; + unsigned int empty:1; + unsigned int need_recovery:1; + unsigned int replaying:1; + unsigned int mounting:1; + unsigned int remounting_rw:1; struct list_head replay_list; struct list_head replay_buds; unsigned long long cs_sqnum; unsigned long long replay_sqnum; - int need_recovery; - int replaying; struct list_head unclean_leb_list; struct ubifs_mst_node *rcvrd_mst_node; struct rb_root size_tree; - int remounting_rw; - int always_chk_crc; struct ubifs_mount_opts mount_opts; -#ifdef CONFIG_UBIFS_FS_DEBUG +#ifndef __UBOOT__ struct ubifs_debug_info *dbg; #endif }; +extern struct list_head ubifs_infos; extern spinlock_t ubifs_infos_lock; +extern atomic_long_t ubifs_clean_zn_cnt; extern struct kmem_cache *ubifs_inode_slab; extern const struct super_operations ubifs_super_operations; extern const struct address_space_operations ubifs_file_address_operations; @@ -1836,16 +2072,23 @@ extern struct ubifs_compressor *ubifs_compressors[UBIFS_COMPR_TYPES_CNT]; /* io.c */ void ubifs_ro_mode(struct ubifs_info *c, int err); +int ubifs_leb_read(const struct ubifs_info *c, int lnum, void *buf, int offs, + int len, int even_ebadmsg); +int ubifs_leb_write(struct ubifs_info *c, int lnum, const void *buf, int offs, + int len); +int ubifs_leb_change(struct ubifs_info *c, int lnum, const void *buf, int len); +int ubifs_leb_unmap(struct ubifs_info *c, int lnum); +int ubifs_leb_map(struct ubifs_info *c, int lnum); +int ubifs_is_mapped(const struct ubifs_info *c, int lnum); int ubifs_wbuf_write_nolock(struct ubifs_wbuf *wbuf, void *buf, int len); -int ubifs_wbuf_seek_nolock(struct ubifs_wbuf *wbuf, int lnum, int offs, - int dtype); +int ubifs_wbuf_seek_nolock(struct ubifs_wbuf *wbuf, int lnum, int offs); int ubifs_wbuf_init(struct ubifs_info *c, struct ubifs_wbuf *wbuf); int ubifs_read_node(const struct ubifs_info *c, void *buf, int type, int len, int lnum, int offs); int ubifs_read_node_wbuf(struct ubifs_wbuf *wbuf, void *buf, int type, int len, int lnum, int offs); int ubifs_write_node(struct ubifs_info *c, void *node, int len, int lnum, - int offs, int dtype); + int offs); int ubifs_check_node(const struct ubifs_info *c, const void *buf, int lnum, int offs, int quiet, int must_chk_crc); void ubifs_prepare_node(struct ubifs_info *c, void *buf, int len, int pad); @@ -1859,7 +2102,7 @@ int ubifs_sync_wbufs_by_inode(struct ubifs_info *c, struct inode *inode); /* scan.c */ struct ubifs_scan_leb *ubifs_scan(const struct ubifs_info *c, int lnum, - int offs, void *sbuf); + int offs, void *sbuf, int quiet); void ubifs_scan_destroy(struct ubifs_scan_leb *sleb); int ubifs_scan_a_node(const struct ubifs_info *c, void *buf, int len, int lnum, int offs, int quiet); @@ -1921,7 +2164,7 @@ long long ubifs_reported_space(const struct ubifs_info *c, long long free); long long ubifs_calc_available(const struct ubifs_info *c, int min_idx_lebs); /* find.c */ -int ubifs_find_free_space(struct ubifs_info *c, int min_space, int *free, +int ubifs_find_free_space(struct ubifs_info *c, int min_space, int *offs, int squeeze); int ubifs_find_free_leb_for_idx(struct ubifs_info *c); int ubifs_find_dirty_leb(struct ubifs_info *c, struct ubifs_lprops *ret_lp, @@ -1983,8 +2226,13 @@ int ubifs_tnc_read_node(struct ubifs_info *c, struct ubifs_zbranch *zbr, int ubifs_tnc_start_commit(struct ubifs_info *c, struct ubifs_zbranch *zroot); int ubifs_tnc_end_commit(struct ubifs_info *c); +#ifndef __UBOOT__ /* shrinker.c */ -int ubifs_shrinker(int nr_to_scan, gfp_t gfp_mask); +unsigned long ubifs_shrink_scan(struct shrinker *shrink, + struct shrink_control *sc); +unsigned long ubifs_shrink_count(struct shrinker *shrink, + struct shrink_control *sc); +#endif /* commit.c */ int ubifs_bg_thread(void *info); @@ -2003,6 +2251,7 @@ int ubifs_write_master(struct ubifs_info *c); int ubifs_read_superblock(struct ubifs_info *c); struct ubifs_sb_node *ubifs_read_sb_node(struct ubifs_info *c); int ubifs_write_sb_node(struct ubifs_info *c, struct ubifs_sb_node *sup); +int ubifs_fixup_free_space(struct ubifs_info *c); /* replay.c */ int ubifs_validate_entry(struct ubifs_info *c, @@ -2084,14 +2333,15 @@ const struct ubifs_lprops *ubifs_fast_find_free(struct ubifs_info *c); const struct ubifs_lprops *ubifs_fast_find_empty(struct ubifs_info *c); const struct ubifs_lprops *ubifs_fast_find_freeable(struct ubifs_info *c); const struct ubifs_lprops *ubifs_fast_find_frdi_idx(struct ubifs_info *c); +int ubifs_calc_dark(const struct ubifs_info *c, int spc); /* file.c */ -int ubifs_fsync(struct file *file, struct dentry *dentry, int datasync); +int ubifs_fsync(struct file *file, loff_t start, loff_t end, int datasync); int ubifs_setattr(struct dentry *dentry, struct iattr *attr); /* dir.c */ struct inode *ubifs_new_inode(struct ubifs_info *c, const struct inode *dir, - int mode); + umode_t mode); int ubifs_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat); @@ -2111,11 +2361,11 @@ int ubifs_iput(struct inode *inode); int ubifs_recover_master_node(struct ubifs_info *c); int ubifs_write_rcvrd_mst_node(struct ubifs_info *c); struct ubifs_scan_leb *ubifs_recover_leb(struct ubifs_info *c, int lnum, - int offs, void *sbuf, int grouped); + int offs, void *sbuf, int jhead); struct ubifs_scan_leb *ubifs_recover_log_leb(struct ubifs_info *c, int lnum, int offs, void *sbuf); -int ubifs_recover_inl_heads(const struct ubifs_info *c, void *sbuf); -int ubifs_clean_lebs(const struct ubifs_info *c, void *sbuf); +int ubifs_recover_inl_heads(struct ubifs_info *c, void *sbuf); +int ubifs_clean_lebs(struct ubifs_info *c, void *sbuf); int ubifs_rcvry_gc_commit(struct ubifs_info *c); int ubifs_recover_size_accum(struct ubifs_info *c, union ubifs_key *key, int deletion, loff_t new_size); @@ -2131,24 +2381,22 @@ long ubifs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg); /* compressor.c */ int __init ubifs_compressors_init(void); -void __exit ubifs_compressors_exit(void); +void ubifs_compressors_exit(void); void ubifs_compress(const void *in_buf, int in_len, void *out_buf, int *out_len, int *compr_type); int ubifs_decompress(const void *buf, int len, void *out, int *out_len, int compr_type); +#include "debug.h" +#include "misc.h" +#include "key.h" + +#ifdef __UBOOT__ /* these are used in cmd_ubifs.c */ int ubifs_init(void); -int ubifs_mount(char *vol_name); +int uboot_ubifs_mount(char *vol_name); void ubifs_umount(struct ubifs_info *c); int ubifs_ls(char *dir_name); int ubifs_load(char *filename, u32 addr, u32 size); - -#include "debug.h" -#include "misc.h" -#include "key.h" - -/* todo: Move these to a common U-Boot header */ -int lzo1x_decompress_safe(const unsigned char *in, size_t in_len, - unsigned char *out, size_t *out_len); +#endif #endif /* !__UBIFS_H__ */ diff --git a/fs/yaffs2/ydirectenv.h b/fs/yaffs2/ydirectenv.h index c6614f1..2b3e84f 100644 --- a/fs/yaffs2/ydirectenv.h +++ b/fs/yaffs2/ydirectenv.h @@ -58,8 +58,6 @@ void yaffs_qsort(void *aa, size_t n, size_t es, #define inline #endif -#define cond_resched() do {} while (0) - #define yaffs_trace(msk, fmt, ...) do { \ if (yaffs_trace_mask & (msk)) \ printf("yaffs: " fmt "\n", ##__VA_ARGS__); \ diff --git a/include/cli.h b/include/cli.h index 6994262..6da7a4a 100644 --- a/include/cli.h +++ b/include/cli.h @@ -31,6 +31,14 @@ void cli_simple_loop(void); int cli_simple_run_command(const char *cmd, int flag); /** + * cli_simple_process_macros() - Expand $() and ${} format env. variables + * + * @param input Input string possible containing $() / ${} vars + * @param output Output string with $() / ${} vars expanded + */ +void cli_simple_process_macros(const char *input, char *output); + +/** * cli_simple_run_command_list() - Execute a list of command * * The commands should be separated by ; or \n and will be executed diff --git a/include/common.h b/include/common.h index 1d6cb48..c7e51ca 100644 --- a/include/common.h +++ b/include/common.h @@ -32,10 +32,7 @@ typedef volatile unsigned char vu_char; defined(CONFIG_MPC866) || \ defined(CONFIG_MPC866P) # define CONFIG_MPC866_FAMILY 1 -#elif defined(CONFIG_MPC870) \ - || defined(CONFIG_MPC875) \ - || defined(CONFIG_MPC880) \ - || defined(CONFIG_MPC885) +#elif defined(CONFIG_MPC885) # define CONFIG_MPC885_FAMILY 1 #endif #if defined(CONFIG_MPC860) \ diff --git a/include/commproc.h b/include/commproc.h index 52ac4ca..82a1a98 100644 --- a/include/commproc.h +++ b/include/commproc.h @@ -456,27 +456,6 @@ typedef struct scc_enet { #define SICR_ENET_CLKRT ((uint)0x00002c00) #endif /* CONFIG_BSEIP */ -/*** BSEIP **********************************************************/ - -#ifdef CONFIG_FLAGADM -/* Enet configuration for the FLAGADM */ -/* Enet on SCC2 */ - -#define PROFF_ENET PROFF_SCC2 -#define CPM_CR_ENET CPM_CR_CH_SCC2 -#define SCC_ENET 1 -#define PA_ENET_RXD ((ushort)0x0004) -#define PA_ENET_TXD ((ushort)0x0008) -#define PA_ENET_TCLK ((ushort)0x0100) -#define PA_ENET_RCLK ((ushort)0x0400) -#define PB_ENET_TENA ((uint)0x00002000) -#define PC_ENET_CLSN ((ushort)0x0040) -#define PC_ENET_RENA ((ushort)0x0080) - -#define SICR_ENET_MASK ((uint)0x0000ff00) -#define SICR_ENET_CLKRT ((uint)0x00003400) -#endif /* CONFIG_FLAGADM */ - /*** ELPT860 *********************************************************/ #ifdef CONFIG_ELPT860 @@ -556,27 +535,6 @@ typedef struct scc_enet { #define SICR_ENET_CLKRT ((uint)0x00002600) #endif /* CONFIG_FPS850L, CONFIG_FPS860L */ -/*** GEN860T **********************************************************/ -#if defined(CONFIG_GEN860T) -#undef SCC_ENET -#define FEC_ENET - -#define PD_MII_TXD1 ((ushort)0x1000) /* PD 3 */ -#define PD_MII_TXD2 ((ushort)0x0800) /* PD 4 */ -#define PD_MII_TXD3 ((ushort)0x0400) /* PD 5 */ -#define PD_MII_RX_DV ((ushort)0x0200) /* PD 6 */ -#define PD_MII_RX_ERR ((ushort)0x0100) /* PD 7 */ -#define PD_MII_RX_CLK ((ushort)0x0080) /* PD 8 */ -#define PD_MII_TXD0 ((ushort)0x0040) /* PD 9 */ -#define PD_MII_RXD0 ((ushort)0x0020) /* PD 10 */ -#define PD_MII_TX_ERR ((ushort)0x0010) /* PD 11 */ -#define PD_MII_MDC ((ushort)0x0008) /* PD 12 */ -#define PD_MII_RXD1 ((ushort)0x0004) /* PD 13 */ -#define PD_MII_RXD2 ((ushort)0x0002) /* PD 14 */ -#define PD_MII_RXD3 ((ushort)0x0001) /* PD 15 */ -#define PD_MII_MASK ((ushort)0x1FFF) /* PD 3-15 */ -#endif /* CONFIG_GEN860T */ - /*** HERMES-PRO ******************************************************/ /* The HERMES-PRO uses the FEC on a MPC860T for Ethernet */ @@ -779,57 +737,6 @@ typedef struct scc_enet { /*** NETVIA *******************************************************/ -/* SinoVee Microsystems SC8xx series FEL8xx-AT,SC823,SC850,SC855T,SC860T */ -#if ( defined CONFIG_SVM_SC8xx ) -# ifndef CONFIG_FEC_ENET - -#define PROFF_ENET PROFF_SCC2 -#define CPM_CR_ENET CPM_CR_CH_SCC2 -#define SCC_ENET 1 - - /* Bits in parallel I/O port registers that have to be set/cleared - * * * * to configure the pins for SCC2 use. - * * * */ -#define PA_ENET_RXD ((ushort)0x0004) /* PA 13 */ -#define PA_ENET_TXD ((ushort)0x0008) /* PA 12 */ -#define PA_ENET_RCLK ((ushort)0x0400) /* PA 5 */ -#define PA_ENET_TCLK ((ushort)0x0800) /* PA 4 */ - -#define PB_ENET_TENA ((uint)0x00002000) /* PB 18 */ - -#define PC_ENET_CLSN ((ushort)0x0040) /* PC 9 */ -#define PC_ENET_RENA ((ushort)0x0080) /* PC 8 */ -/* Control bits in the SICR to route TCLK (CLK3) and RCLK (CLK1) to - * * * * SCC2. Also, make sure GR2 (bit 16) and SC2 (bit 17) are zero. - * * * */ -#define SICR_ENET_MASK ((uint)0x0000ff00) -#define SICR_ENET_CLKRT ((uint)0x00003700) - -# else /* Use FEC for Fast Ethernet */ - -#undef SCC_ENET -#define FEC_ENET - -#define PD_MII_TXD1 ((ushort)0x1000) /* PD 3 */ -#define PD_MII_TXD2 ((ushort)0x0800) /* PD 4 */ -#define PD_MII_TXD3 ((ushort)0x0400) /* PD 5 */ -#define PD_MII_RX_DV ((ushort)0x0200) /* PD 6 */ -#define PD_MII_RX_ERR ((ushort)0x0100) /* PD 7 */ -#define PD_MII_RX_CLK ((ushort)0x0080) /* PD 8 */ -#define PD_MII_TXD0 ((ushort)0x0040) /* PD 9 */ -#define PD_MII_RXD0 ((ushort)0x0020) /* PD 10 */ -#define PD_MII_TX_ERR ((ushort)0x0010) /* PD 11 */ -#define PD_MII_MDC ((ushort)0x0008) /* PD 12 */ -#define PD_MII_RXD1 ((ushort)0x0004) /* PD 13 */ -#define PD_MII_RXD2 ((ushort)0x0002) /* PD 14 */ -#define PD_MII_RXD3 ((ushort)0x0001) /* PD 15 */ - -#define PD_MII_MASK ((ushort)0x1FFF) /* PD 3...15 */ - -# endif /* CONFIG_FEC_ENET */ -#endif /* CONFIG_SVM_SC8xx */ - - #if defined(CONFIG_NETVIA) /* Bits in parallel I/O port registers that have to be set/cleared * to configure the pins for SCC2 use. @@ -916,16 +823,6 @@ typedef struct scc_enet { #define SICR_ENET_CLKRT ((uint)0x00002E00) #endif /* CONFIG_SPD823TS */ -/*** SXNI855T ******************************************************/ - -#if defined(CONFIG_SXNI855T) - -#ifdef CONFIG_FEC_ENET -#define FEC_ENET /* use FEC for Ethernet */ -#endif /* CONFIG_FEC_ETHERNET */ - -#endif /* CONFIG_SXNI855T */ - /*** MVS1, TQM823L/M, TQM850L/M, TQM885D, R360MPI **********/ #if (defined(CONFIG_MVS) && CONFIG_MVS < 2) || \ diff --git a/include/config_distro_bootcmd.h b/include/config_distro_bootcmd.h new file mode 100644 index 0000000..90d9901 --- /dev/null +++ b/include/config_distro_bootcmd.h @@ -0,0 +1,197 @@ +/* + * (C) Copyright 2014 + * NVIDIA Corporation <www.nvidia.com> + * + * Copyright 2014 Red Hat, Inc. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef _CONFIG_CMD_DISTRO_BOOTCMD_H +#define _CONFIG_CMD_DISTRO_BOOTCMD_H + +#define BOOTENV_SHARED_BLKDEV_BODY(devtypel) \ + "if " #devtypel " dev ${devnum}; then " \ + "setenv devtype " #devtypel "; " \ + "run scan_dev_for_boot; " \ + "fi\0" + +#define BOOTENV_SHARED_BLKDEV(devtypel) \ + #devtypel "_boot=" \ + BOOTENV_SHARED_BLKDEV_BODY(devtypel) + +#define BOOTENV_DEV_BLKDEV(devtypeu, devtypel, instance) \ + "bootcmd_" #devtypel #instance "=" \ + "setenv devnum " #instance "; " \ + "run " #devtypel "_boot\0" + +#define BOOTENV_DEV_NAME_BLKDEV(devtypeu, devtypel, instance) \ + #devtypel #instance " " + +#ifdef CONFIG_CMD_MMC +#define BOOTENV_SHARED_MMC BOOTENV_SHARED_BLKDEV(mmc) +#define BOOTENV_DEV_MMC BOOTENV_DEV_BLKDEV +#define BOOTENV_DEV_NAME_MMC BOOTENV_DEV_NAME_BLKDEV +#else +#define BOOTENV_SHARED_MMC +#define BOOTENV_DEV_MMC \ + BOOT_TARGET_DEVICES_references_MMC_without_CONFIG_CMD_MMC +#define BOOTENV_DEV_NAME_MMC \ + BOOT_TARGET_DEVICES_references_MMC_without_CONFIG_CMD_MMC +#endif + +#ifdef CONFIG_CMD_SATA +#define BOOTENV_SHARED_SATA BOOTENV_SHARED_BLKDEV(sata) +#define BOOTENV_DEV_SATA BOOTENV_DEV_BLKDEV +#define BOOTENV_DEV_NAME_SATA BOOTENV_DEV_NAME_BLKDEV +#else +#define BOOTENV_SHARED_SATA +#define BOOTENV_DEV_SATA \ + BOOT_TARGET_DEVICES_references_SATA_without_CONFIG_CMD_SATA +#define BOOTENV_DEV_NAME_SATA \ + BOOT_TARGET_DEVICES_references_SATA_without_CONFIG_CMD_SATA +#endif + +#ifdef CONFIG_CMD_SCSI +#define BOOTENV_SHARED_SCSI BOOTENV_SHARED_BLKDEV(scsi) +#define BOOTENV_DEV_SCSI BOOTENV_DEV_BLKDEV +#define BOOTENV_DEV_NAME_SCSI BOOTENV_DEV_NAME_BLKDEV +#else +#define BOOTENV_SHARED_SCSI +#define BOOTENV_DEV_SCSI \ + BOOT_TARGET_DEVICES_references_SCSI_without_CONFIG_CMD_SCSI +#define BOOTENV_DEV_NAME_SCSI \ + BOOT_TARGET_DEVICES_references_SCSI_without_CONFIG_CMD_SCSI +#endif + +#ifdef CONFIG_CMD_IDE +#define BOOTENV_SHARED_IDE BOOTENV_SHARED_BLKDEV(ide) +#define BOOTENV_DEV_IDE BOOTENV_DEV_BLKDEV +#define BOOTENV_DEV_NAME_IDE BOOTENV_DEV_NAME_BLKDEV +#else +#define BOOTENV_SHARED_IDE +#define BOOTENV_DEV_IDE \ + BOOT_TARGET_DEVICES_references_IDE_without_CONFIG_CMD_IDE +#define BOOTENV_DEV_NAME_IDE \ + BOOT_TARGET_DEVICES_references_IDE_without_CONFIG_CMD_IDE +#endif + +#ifdef CONFIG_CMD_USB +#define BOOTENV_RUN_USB_INIT "run usb_init; " +#define BOOTENV_SET_USB_NEED_INIT "setenv usb_need_init; " +#define BOOTENV_SHARED_USB \ + "usb_init=" \ + "if ${usb_need_init}; then " \ + "setenv usb_need_init false; " \ + "usb start 0; " \ + "fi\0" \ + \ + "usb_boot=" \ + BOOTENV_RUN_USB_INIT \ + BOOTENV_SHARED_BLKDEV_BODY(usb) +#define BOOTENV_DEV_USB BOOTENV_DEV_BLKDEV +#define BOOTENV_DEV_NAME_USB BOOTENV_DEV_NAME_BLKDEV +#else +#define BOOTENV_RUN_USB_INIT +#define BOOTENV_SET_USB_NEED_INIT +#define BOOTENV_SHARED_USB +#define BOOTENV_DEV_USB \ + BOOT_TARGET_DEVICES_references_USB_without_CONFIG_CMD_USB +#define BOOTENV_DEV_NAME_USB \ + BOOT_TARGET_DEVICES_references_USB_without_CONFIG_CMD_USB +#endif + +#if defined(CONFIG_CMD_DHCP) +#define BOOTENV_DEV_DHCP(devtypeu, devtypel, instance) \ + "bootcmd_dhcp=" \ + BOOTENV_RUN_USB_INIT \ + "if dhcp ${scriptaddr} boot.scr.uimg; then " \ + "source ${scriptaddr}; " \ + "fi\0" +#define BOOTENV_DEV_NAME_DHCP(devtypeu, devtypel, instance) \ + "dhcp " +#else +#define BOOTENV_DEV_DHCP \ + BOOT_TARGET_DEVICES_references_DHCP_without_CONFIG_CMD_DHCP +#define BOOTENV_DEV_NAME_DHCP \ + BOOT_TARGET_DEVICES_references_DHCP_without_CONFIG_CMD_DHCP +#endif + +#if defined(CONFIG_CMD_DHCP) && defined(CONFIG_CMD_PXE) +#define BOOTENV_DEV_PXE(devtypeu, devtypel, instance) \ + "bootcmd_pxe=" \ + BOOTENV_RUN_USB_INIT \ + "dhcp; " \ + "if pxe get; then " \ + "pxe boot; " \ + "fi\0" +#define BOOTENV_DEV_NAME_PXE(devtypeu, devtypel, instance) \ + "pxe " +#else +#define BOOTENV_DEV_PXE \ + BOOT_TARGET_DEVICES_references_PXE_without_CONFIG_CMD_DHCP_or_PXE +#define BOOTENV_DEV_NAME_PXE \ + BOOT_TARGET_DEVICES_references_PXE_without_CONFIG_CMD_DHCP_or_PXE +#endif + +#define BOOTENV_DEV_NAME(devtypeu, devtypel, instance) \ + BOOTENV_DEV_NAME_##devtypeu(devtypeu, devtypel, instance) +#define BOOTENV_BOOT_TARGETS \ + "boot_targets=" BOOT_TARGET_DEVICES(BOOTENV_DEV_NAME) "\0" + +#define BOOTENV_DEV(devtypeu, devtypel, instance) \ + BOOTENV_DEV_##devtypeu(devtypeu, devtypel, instance) +#define BOOTENV \ + BOOTENV_SHARED_MMC \ + BOOTENV_SHARED_USB \ + BOOTENV_SHARED_SATA \ + BOOTENV_SHARED_SCSI \ + BOOTENV_SHARED_IDE \ + "boot_prefixes=/ /boot/\0" \ + "boot_scripts=boot.scr.uimg boot.scr\0" \ + BOOTENV_BOOT_TARGETS \ + "bootpart=1\0" \ + \ + "boot_extlinux=" \ + "sysboot ${devtype} ${devnum}:${bootpart} any " \ + "${scriptaddr} ${prefix}extlinux/extlinux.conf\0" \ + \ + "scan_dev_for_extlinux=" \ + "if test -e ${devtype} ${devnum}:${bootpart} " \ + "${prefix}extlinux/extlinux.conf; then " \ + "echo Found ${prefix}extlinux/extlinux.conf; " \ + "run boot_extlinux; " \ + "echo SCRIPT FAILED: continuing...; " \ + "fi\0" \ + \ + "boot_a_script=" \ + "load ${devtype} ${devnum}:${bootpart} " \ + "${scriptaddr} ${prefix}${script}; " \ + "source ${scriptaddr}\0" \ + \ + "scan_dev_for_scripts=" \ + "for script in ${boot_scripts}; do " \ + "if test -e ${devtype} ${devnum}:${bootpart} " \ + "${prefix}${script}; then " \ + "echo Found U-Boot script " \ + "${prefix}${script}; " \ + "run boot_a_script; " \ + "echo SCRIPT FAILED: continuing...; " \ + "fi; " \ + "done\0" \ + \ + "scan_dev_for_boot=" \ + "echo Scanning ${devtype} ${devnum}...; " \ + "for prefix in ${boot_prefixes}; do " \ + "run scan_dev_for_extlinux; " \ + "run scan_dev_for_scripts; " \ + "done\0" \ + \ + BOOT_TARGET_DEVICES(BOOTENV_DEV) \ + \ + "bootcmd=" BOOTENV_SET_USB_NEED_INIT \ + "for target in ${boot_targets}; do " \ + "run bootcmd_${target}; " \ + "done\0" + +#endif /* _CONFIG_CMD_DISTRO_BOOTCMD_H */ diff --git a/include/configs/FLAGADM.h b/include/configs/FLAGADM.h deleted file mode 100644 index d93223f..0000000 --- a/include/configs/FLAGADM.h +++ /dev/null @@ -1,296 +0,0 @@ -/* - * (C) Copyright 2001 - * Wolfgang Denk, DENX Software Engineering, wd@denx.de. - * - * SPDX-License-Identifier: GPL-2.0+ - */ - -/* - * board/config.h - configuration options, board specific - */ - -#ifndef __CONFIG_H -#define __CONFIG_H - -/* - * High Level Configuration Options - * (easy to change) - */ - -#define CONFIG_MPC823 1 /* This is a MPC823 CPU */ -#define CONFIG_FLAGADM 1 /* ...on a FLAGA DM */ -#define CONFIG_8xx_GCLK_FREQ 48000000 /*48MHz*/ - -#define CONFIG_SYS_TEXT_BASE 0x40000000 - -#undef CONFIG_8xx_CONS_SMC1 /* Console is on SMC1 */ -#define CONFIG_8xx_CONS_SMC2 1 -#undef CONFIG_8xx_CONS_NONE - -#define CONFIG_BAUDRATE 115200 /* console baudrate = 115kbps */ -#define CONFIG_BOOTDELAY 3 /* autoboot after 5 seconds */ - -#undef CONFIG_CLOCKS_IN_MHZ - -#if 0 -#define CONFIG_BOOTARGS "root=/dev/nfs rw ip=bootp" -#define CONFIG_BOOTCOMMAND \ - "setenv bootargs root=/dev/ram ip=off panic=1;" \ - "bootm 40040000 400e0000" -#else -#define CONFIG_BOOTARGS "root=/dev/nfs rw ip=bootp panic=1" -#define CONFIG_BOOTCOMMAND "bootp 0x400000; bootm 0x400000" -#endif /* 0|1*/ - -#define CONFIG_LOADS_ECHO 1 /* echo on for serial download */ -#undef CONFIG_SYS_LOADS_BAUD_CHANGE /* don't allow baudrate change */ - -/*#define CONFIG_WATCHDOG*/ /* watchdog enabled */ -#undef CONFIG_WATCHDOG /* watchdog disabled */ - -/* - * BOOTP options - */ -#define CONFIG_BOOTP_SUBNETMASK -#define CONFIG_BOOTP_GATEWAY -#define CONFIG_BOOTP_HOSTNAME -#define CONFIG_BOOTP_BOOTPATH -#define CONFIG_BOOTP_BOOTFILESIZE - - -/* - * Command line configuration. - */ - -#define CONFIG_CMD_BDI -#define CONFIG_CMD_IMI -#define CONFIG_CMD_CACHE -#define CONFIG_CMD_MEMORY -#define CONFIG_CMD_FLASH -#define CONFIG_CMD_LOADB -#define CONFIG_CMD_LOADS -#define CONFIG_CMD_SAVEENV -#define CONFIG_CMD_REGINFO -#define CONFIG_CMD_IMMAP -#define CONFIG_CMD_NET - - -/* - * Miscellaneous configurable options - */ -#define CONFIG_SYS_LONGHELP /* undef to save memory */ -#define CONFIG_SYS_PROMPT "EEG> " /* Monitor Command Prompt */ -#if defined(CONFIG_CMD_KGDB) -#define CONFIG_SYS_CBSIZE 1024 /* Console I/O Buffer Size */ -#else -#define CONFIG_SYS_CBSIZE 256 /* Console I/O Buffer Size */ -#endif -#define CONFIG_SYS_PBSIZE (CONFIG_SYS_CBSIZE+sizeof(CONFIG_SYS_PROMPT)+16) /* Print Buffer Size */ -#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 0x0100000 /* memtest works on */ -#define CONFIG_SYS_MEMTEST_END 0x0f00000 /* 1 ... 15 MB in DRAM */ - -#define CONFIG_SYS_LOAD_ADDR 0x40040000 /* default load address */ - -/* - * Low Level Configuration Settings - * (address mappings, register initial values, etc.) - * You should know what you are doing if you make changes here. - */ -/*----------------------------------------------------------------------- - * Internal Memory Mapped Register - */ -#define CONFIG_SYS_IMMR 0xFF000000 - -/*----------------------------------------------------------------------- - * Definitions for initial stack pointer and data area (in DPRAM) - */ -#define CONFIG_SYS_INIT_RAM_ADDR CONFIG_SYS_IMMR -#define CONFIG_SYS_INIT_RAM_SIZE 0x2F00 /* Size of used area in DPRAM */ -#define CONFIG_SYS_GBL_DATA_OFFSET (CONFIG_SYS_INIT_RAM_SIZE - GENERATED_GBL_DATA_SIZE) -#define CONFIG_SYS_INIT_SP_OFFSET CONFIG_SYS_GBL_DATA_OFFSET - -/*----------------------------------------------------------------------- - * Start addresses for the final memory configuration - * (Set up by the startup code) - * Please note that CONFIG_SYS_SDRAM_BASE _must_ start at 0 - */ -#define CONFIG_SYS_SDRAM_BASE 0x00000000 -#define CONFIG_SYS_FLASH_BASE 0x40000000 -#define CONFIG_SYS_MONITOR_LEN (128 << 10) /* Reserve 128 kB for Monitor */ -#define CONFIG_SYS_MONITOR_BASE CONFIG_SYS_FLASH_BASE -#define CONFIG_SYS_MALLOC_LEN (128 << 10) /* Reserve 128 kB for malloc() */ - -/* - * For booting Linux, the board info and command line data - * have to be in the first 8 MB of memory, since this is - * the maximum mapped by the Linux kernel during initialization. - */ -#define CONFIG_SYS_BOOTMAPSZ (8 << 20) /* Initial Memory map for Linux */ - -/*----------------------------------------------------------------------- - * FLASH organization - */ -#define CONFIG_SYS_MAX_FLASH_BANKS 1 /* max number of memory banks */ -#define CONFIG_SYS_MAX_FLASH_SECT 71 /* max number of sectors on one chip */ - -#define CONFIG_SYS_FLASH_ERASE_TOUT 8000 /* Timeout for Flash Erase (in ms) */ -#define CONFIG_SYS_FLASH_WRITE_TOUT 500 /* Timeout for Flash Write (in ms) */ - -#define CONFIG_ENV_IS_IN_FLASH 1 -/* This is a litlebit wasteful, but one sector is 128kb and we have to - * assigne a whole sector for the environment, so that we can safely - * erase and write it without disturbing the boot sector - */ -#define CONFIG_ENV_OFFSET 0x20000 /* Offset of Environment Sector */ -#define CONFIG_ENV_SIZE 0x20000 /* Total Size of Environment Sector */ - -/*----------------------------------------------------------------------- - * Cache Configuration - */ -#define CONFIG_SYS_CACHELINE_SIZE 16 /* For all MPC8xx CPUs */ -#if defined(CONFIG_CMD_KGDB) -#define CONFIG_SYS_CACHELINE_SHIFT 4 /* log base 2 of the above value */ -#endif -#define CONFIG_SYS_DELAYED_ICACHE 1 /* enable ICache not before - * running in RAM. - */ - -/*----------------------------------------------------------------------- - * SYPCR - System Protection Control 11-9 - * SYPCR can only be written once after reset! - *----------------------------------------------------------------------- - * Software & Bus Monitor Timer max, Bus Monitor enable, SW Watchdog freeze - */ -#ifdef CONFIG_WATCHDOG -#define CONFIG_SYS_SYPCR (SYPCR_SWTC | SYPCR_SWF | SYPCR_SWE | SYPCR_SWRI | SYPCR_SWP) -#else -#define CONFIG_SYS_SYPCR (SYPCR_SWTC | SYPCR_BMT | SYPCR_BME | SYPCR_SWF) -#endif - -/*----------------------------------------------------------------------- - * SIUMCR - SIU Module Configuration 11-6 - *----------------------------------------------------------------------- - * PCMCIA config., multi-function pin tri-state - */ -#define CONFIG_SYS_PRE_SIUMCR (SIUMCR_DBGC11 | SIUMCR_MPRE | \ - SIUMCR_MLRC01 | SIUMCR_GB5E) -#define CONFIG_SYS_SIUMCR (CONFIG_SYS_PRE_SIUMCR | SIUMCR_DLK) - -/*----------------------------------------------------------------------- - * TBSCR - Time Base Status and Control 11-26 - *----------------------------------------------------------------------- - * Clear Reference Interrupt Status, Timebase freezing enabled - */ -#define CONFIG_SYS_TBSCR (TBSCR_REFA | TBSCR_REFB | TBSCR_TBF) - -/*----------------------------------------------------------------------- - * RTCSC - Real-Time Clock Status and Control Register 11-27 - *----------------------------------------------------------------------- - */ -#define CONFIG_SYS_RTCSC (RTCSC_SEC | RTCSC_ALR | RTCSC_RTF| RTCSC_RTE) - -/*----------------------------------------------------------------------- - * PISCR - Periodic Interrupt Status and Control 11-31 - *----------------------------------------------------------------------- - * Clear Periodic Interrupt Status, Interrupt Timer freezing enabled - */ -#define CONFIG_SYS_PISCR (PISCR_PS | PISCR_PITF) - -/*----------------------------------------------------------------------- - * PLPRCR - PLL, Low-Power, and Reset Control Register 15-30 - *----------------------------------------------------------------------- - * Reset PLL lock status sticky bit, timer expired status bit and timer - * interrupt status bit miltiplier of 0x00b i.e. operation clock is - * 4MHz * (0x00b+1) = 4MHz * 12 = 48MHz - */ -#define CONFIG_SYS_PLPRCR (0x00b00000 | PLPRCR_SPLSS | PLPRCR_TEXPS | PLPRCR_TMIST) - -/*----------------------------------------------------------------------- - * SCCR - System Clock and reset Control Register 15-27 - *----------------------------------------------------------------------- - * Set clock output, timebase and RTC source and divider, - * power management and some other internal clocks - */ -#define SCCR_MASK SCCR_EBDF11 -#define CONFIG_SYS_SCCR ( SCCR_COM00 | SCCR_DFSYNC00 | SCCR_DFBRG00 | \ - SCCR_DFNL000 | SCCR_DFNH000 | SCCR_DFLCD000 | \ - SCCR_DFALCD00) - -#define CONFIG_SYS_DER 0 - -/* - * In the Flaga DM we have: - * Flash on BR0/OR0/CS0a at 0x40000000 - * Display on BR1/OR1/CS1 at 0x20000000 - * SDRAM on BR2/OR2/CS2 at 0x00000000 - * Free BR3/OR3/CS3 - * DSP1 on BR4/OR4/CS4 at 0x80000000 - * DSP2 on BR5/OR5/CS5 at 0xa0000000 - * - * For now we just configure the Flash and the SDRAM and leave the others - * untouched. -*/ - -#define CONFIG_SYS_FLASH_PROTECTION 0 - -#define FLASH_BASE0 0x40000000 /* FLASH bank #0 */ - -/* used to re-map FLASH both when starting from SRAM or FLASH: - * restrict access enough to keep SRAM working (if any) - * but not too much to meddle with FLASH accesses - */ -#define CONFIG_SYS_OR_AM 0xff000000 /* OR addr mask */ -#define CONFIG_SYS_OR_ATM 0x00006000 - -/* FLASH timing: ACS = 10, TRLX = 1, CSNT = 1, SCY = 3, EHTR = 1 */ -#define CONFIG_SYS_OR_TIMING_FLASH (OR_CSNT_SAM | OR_ACS_DIV4 | OR_BI | \ - OR_SCY_3_CLK | OR_TRLX | OR_EHTR ) - -#define CONFIG_SYS_OR0_PRELIM (CONFIG_SYS_OR_AM | CONFIG_SYS_OR_ATM | CONFIG_SYS_OR_TIMING_FLASH) -#define CONFIG_SYS_BR0_PRELIM ((FLASH_BASE0 & BR_BA_MSK) | BR_PS_16 | BR_V ) - -/* - * BR2 and OR2 (SDRAM) - * - */ -#define SDRAM_BASE2 0x00000000 /* SDRAM bank #0 */ -#define SDRAM_MAX_SIZE 0x04000000 /* max 64 MB per bank */ - -/* SDRAM timing: Multiplexed addresses, GPL5 output to GPL5_A (don't care) */ -#define CONFIG_SYS_OR_TIMING_SDRAM ( 0x00000800 ) - -#define CONFIG_SYS_OR2_PRELIM (CONFIG_SYS_OR_AM | CONFIG_SYS_OR_TIMING_SDRAM) -#define CONFIG_SYS_BR2_PRELIM ((SDRAM_BASE2 & BR_BA_MSK) | BR_MS_UPMA | BR_V ) - -#define CONFIG_SYS_BR2 CONFIG_SYS_BR2_PRELIM -#define CONFIG_SYS_OR2 CONFIG_SYS_OR2_PRELIM - -/* - * MAMR settings for SDRAM - */ -#define CONFIG_SYS_MAMR_48_SDR (CONFIG_SYS_MAMR_PTA | MAMR_WLFA_1X | MAMR_RLFA_1X \ - | MAMR_G0CLA_A11) - -/* - * Memory Periodic Timer Prescaler - */ - -/* periodic timer for refresh */ -#define CONFIG_SYS_MAMR_PTA 0x0F000000 - -/* - * BR4 and OR4 (DSP1) - * - * We do not wan't preliminary setup of the DSP, anyway we need the - * UPMB setup correctly before we can access the DSP. - * -*/ -#define DSP_BASE 0x80000000 - -#define CONFIG_SYS_OR4 ( OR_AM_MSK | OR_CSNT_SAM | OR_BI | OR_G5LS) -#define CONFIG_SYS_BR4 ( (DSP_BASE & BR_BA_MSK) | BR_PS_16 | BR_MS_UPMB | BR_V ) - -#endif /* __CONFIG_H */ diff --git a/include/configs/GEN860T.h b/include/configs/GEN860T.h deleted file mode 100644 index fd6c976..0000000 --- a/include/configs/GEN860T.h +++ /dev/null @@ -1,712 +0,0 @@ -/* - * (C) Copyright 2000 - * Wolfgang Denk, DENX Software Engineering, wd@denx.de. - * Keith Outwater, keith_outwater@mvis.com - * - * SPDX-License-Identifier: GPL-2.0+ - */ - -/* - * board/config_GEN860T.h - board specific configuration options - */ - -#ifndef __CONFIG_GEN860T_H -#define __CONFIG_H - -/* - * High Level Configuration Options - */ -#define CONFIG_MPC860 -#define CONFIG_GEN860T - -#define CONFIG_SYS_TEXT_BASE 0x40000000 - -/* - * Identify the board - */ -#if !defined(CONFIG_SC) -#define CONFIG_IDENT_STRING " B2" -#else -#define CONFIG_IDENT_STRING " SC" -#endif - -/* - * Don't depend on the RTC clock to determine clock frequency - - * the 860's internal rtc uses a 32.768 KHz clock which is - * generated by the DS1337 - and the DS1337 clock can be turned off. - */ -#if !defined(CONFIG_SC) -#define CONFIG_8xx_GCLK_FREQ 66600000 -#else -#define CONFIG_8xx_GCLK_FREQ 48000000 -#endif - -/* - * The RS-232 console port is on SMC1 - */ -#define CONFIG_8xx_CONS_SMC1 -#define CONFIG_BAUDRATE 38400 - -/* - * Print console information - */ -#undef CONFIG_SYS_CONSOLE_INFO_QUIET - -/* - * Set the autoboot delay in seconds. A delay of -1 disables autoboot - */ -#define CONFIG_BOOTDELAY 5 - -/* - * Pass the clock frequency to the Linux kernel in units of MHz - */ -#define CONFIG_CLOCKS_IN_MHZ - -#define CONFIG_PREBOOT \ - "echo;echo" - -#undef CONFIG_BOOTARGS -#define CONFIG_BOOTCOMMAND \ - "bootp;" \ - "setenv bootargs root=/dev/nfs rw nfsroot=${serverip}:${rootpath} " \ - "ip=${ipaddr}:${serverip}:${gatewayip}:${netmask}:${hostname}::off; " \ - "bootm" - -/* - * Turn off echo for serial download by default. Allow baud rate to be changed - * for downloads - */ -#undef CONFIG_LOADS_ECHO -#define CONFIG_SYS_LOADS_BAUD_CHANGE - -/* - * Turn off the watchdog timer - */ -#undef CONFIG_WATCHDOG - -/* - * Do not reboot if a panic occurs - */ -#define CONFIG_PANIC_HANG - -/* - * Enable the status LED - */ -#define CONFIG_STATUS_LED - -/* - * Reset address. We pick an address such that when an instruction - * is executed at that address, a machine check exception occurs - */ -#define CONFIG_SYS_RESET_ADDRESS ((ulong) -1) - -/* - * BOOTP options - */ -#define CONFIG_BOOTP_SUBNETMASK -#define CONFIG_BOOTP_GATEWAY -#define CONFIG_BOOTP_HOSTNAME -#define CONFIG_BOOTP_BOOTPATH -#define CONFIG_BOOTP_BOOTFILESIZE - - -/* - * The GEN860T network interface uses the on-chip 10/100 FEC with - * an Intel LXT971A PHY connected to the 860T's MII. The PHY's - * MII address is hardwired on the board to zero. - */ -#define CONFIG_FEC_ENET -#define CONFIG_SYS_DISCOVER_PHY -#define CONFIG_MII -#define CONFIG_MII_INIT 1 -#define CONFIG_PHY_ADDR 0 - -/* - * Set default IP stuff just to get bootstrap entries into the - * environment so that we can source the full default environment. - */ -#define CONFIG_ETHADDR 9a:52:63:15:85:25 -#define CONFIG_SERVERIP 10.0.4.201 -#define CONFIG_IPADDR 10.0.4.111 - -/* - * This board has a 32 kibibyte EEPROM (Atmel AT24C256) connected to - * the MPC860T I2C interface. - */ -#define CONFIG_SYS_I2C_EEPROM_ADDR 0x50 -#define CONFIG_SYS_EEPROM_PAGE_WRITE_BITS 6 /* 64 byte pages */ -#define CONFIG_SYS_EEPROM_PAGE_WRITE_DELAY_MS 12 /* 10 mS w/ 20% margin */ -#define CONFIG_SYS_I2C_EEPROM_ADDR_LEN 2 /* need 16 bit address */ -#define CONFIG_ENV_EEPROM_SIZE (32 * 1024) - -/* - * Enable I2C and select the hardware/software driver - */ -#define CONFIG_HARD_I2C 1 /* CPM based I2C */ -#undef CONFIG_SYS_I2C_SOFT /* Bit-banged I2C */ - -#ifdef CONFIG_HARD_I2C -#define CONFIG_SYS_I2C_SPEED 100000 /* clock speed in Hz */ -#define CONFIG_SYS_I2C_SLAVE 0xFE /* I2C slave address */ -#endif - -#ifdef CONFIG_SYS_I2C_SOFT -#define CONFIG_SYS_I2C -#define CONFIG_SYS_I2C_SOFT_SPEED 50000 -#define CONFIG_SYS_I2C_SOFT_SLAVE 0xFE -#define PB_SCL 0x00000020 /* PB 26 */ -#define PB_SDA 0x00000010 /* PB 27 */ -#define I2C_INIT (immr->im_cpm.cp_pbdir |= PB_SCL) -#define I2C_ACTIVE (immr->im_cpm.cp_pbdir |= PB_SDA) -#define I2C_TRISTATE (immr->im_cpm.cp_pbdir &= ~PB_SDA) -#define I2C_READ ((immr->im_cpm.cp_pbdat & PB_SDA) != 0) -#define I2C_SDA(bit) if (bit) \ - immr->im_cpm.cp_pbdat |= PB_SDA; \ - else \ - immr->im_cpm.cp_pbdat &= ~PB_SDA -#define I2C_SCL(bit) if (bit) \ - immr->im_cpm.cp_pbdat |= PB_SCL; \ - else \ - immr->im_cpm.cp_pbdat &= ~PB_SCL -#define I2C_DELAY udelay(5) /* 1/4 I2C clock duration */ -#endif - -/* - * Allow environment overwrites by anyone - */ -#define CONFIG_ENV_OVERWRITE - -#if !defined(CONFIG_SC) -/* - * The MPC860's internal RTC is horribly broken in rev D masks. Three - * internal MPC860T circuit nodes were inadvertently left floating; this - * causes KAPWR current in power down mode to be three orders of magnitude - * higher than specified in the datasheet (from 10 uA to 10 mA). No - * reasonable battery can keep that kind RTC running during powerdown for any - * length of time, so we use an external RTC on the I2C bus instead. - */ -#define CONFIG_RTC_DS1337 -#define CONFIG_SYS_I2C_RTC_ADDR 0x68 - -#else -/* - * No external RTC on SC variant, so we're stuck with the internal one. - */ -#define CONFIG_RTC_MPC8xx -#endif - -/* - * Power On Self Test support - */ -#define CONFIG_POST ( CONFIG_SYS_POST_CACHE | \ - CONFIG_SYS_POST_MEMORY | \ - CONFIG_SYS_POST_CPU | \ - CONFIG_SYS_POST_UART | \ - CONFIG_SYS_POST_SPR ) - - -/* - * Command line configuration. - */ -#include <config_cmd_default.h> - -#define CONFIG_CMD_ASKENV -#define CONFIG_CMD_DHCP -#define CONFIG_CMD_I2C -#define CONFIG_CMD_EEPROM -#define CONFIG_CMD_REGINFO -#define CONFIG_CMD_IMMAP -#define CONFIG_CMD_ELF -#define CONFIG_CMD_DATE -#define CONFIG_CMD_FPGA -#define CONFIG_CMD_FPGA_LOADMK -#define CONFIG_CMD_MII -#define CONFIG_CMD_BEDBUG - -#ifdef CONFIG_POST -#define CONFIG_CMD_DIAG -#endif - -/* - * There is no IDE/PCMCIA hardware support on the board. - */ -#undef CONFIG_IDE_PCMCIA -#undef CONFIG_IDE_LED -#undef CONFIG_IDE_RESET - -/* - * Enable the call to misc_init_r() for miscellaneous platform - * dependent initialization. - */ -#define CONFIG_MISC_INIT_R - -/* - * Enable call to last_stage_init() so we can twiddle some LEDS :) - */ -#define CONFIG_LAST_STAGE_INIT - -/* - * Virtex2 FPGA configuration support - */ -#define CONFIG_FPGA_COUNT 1 -#define CONFIG_FPGA -#define CONFIG_FPGA_XILINX -#define CONFIG_FPGA_VIRTEX2 -#define CONFIG_SYS_FPGA_PROG_FEEDBACK - -/* - * Verbose help from command monitor. - */ -#define CONFIG_SYS_LONGHELP -#if !defined(CONFIG_SC) -#define CONFIG_SYS_PROMPT "B2> " -#else -#define CONFIG_SYS_PROMPT "SC> " -#endif - - -/* - * Use the "hush" command parser - */ -#define CONFIG_SYS_HUSH_PARSER - -/* - * Set buffer size for console I/O - */ -#if defined(CONFIG_CMD_KGDB) -#define CONFIG_SYS_CBSIZE 1024 -#else -#define CONFIG_SYS_CBSIZE 256 -#endif - -/* - * Print buffer size - */ -#define CONFIG_SYS_PBSIZE (CONFIG_SYS_CBSIZE + sizeof(CONFIG_SYS_PROMPT) + 16) - -/* - * Maximum number of arguments that a command can accept - */ -#define CONFIG_SYS_MAXARGS 16 - -/* - * Boot argument buffer size - */ -#define CONFIG_SYS_BARGSIZE CONFIG_SYS_CBSIZE - -/* - * Default memory test range - */ -#define CONFIG_SYS_MEMTEST_START 0x0100000 -#define CONFIG_SYS_MEMTEST_END (CONFIG_SYS_MEMTEST_START + (128 * 1024)) - -/* - * Select the more full-featured memory test - */ -#define CONFIG_SYS_ALT_MEMTEST - -/* - * Default load address - */ -#define CONFIG_SYS_LOAD_ADDR 0x01000000 - -/* - * Device memory map (after SDRAM remap to 0x0): - * - * CS Device Base Addr Size - * ---------------------------------------------------- - * CS0* Flash 0x40000000 64 M - * CS1* SDRAM 0x00000000 16 M - * CS2* Disk-On-Chip 0x50000000 32 K - * CS3* FPGA 0x60000000 64 M - * CS4* SelectMap 0x70000000 32 K - * CS5* Mil-Std 1553 I/F 0x80000000 32 K - * CS6* Unused - * CS7* Unused - * IMMR 860T Registers 0xfff00000 - */ - -/* - * Base addresses and block sizes - */ -#define CONFIG_SYS_IMMR 0xFF000000 - -#define SDRAM_BASE 0x00000000 -#define SDRAM_SIZE (64 * 1024 * 1024) - -#define FLASH_BASE 0x40000000 -#define FLASH_SIZE (16 * 1024 * 1024) - -#define DOC_BASE 0x50000000 -#define DOC_SIZE (32 * 1024) - -#define FPGA_BASE 0x60000000 -#define FPGA_SIZE (64 * 1024 * 1024) - -#define SELECTMAP_BASE 0x70000000 -#define SELECTMAP_SIZE (32 * 1024) - -#define M1553_BASE 0x80000000 -#define M1553_SIZE (64 * 1024) - -/* - * Definitions for initial stack pointer and data area (in DPRAM) - */ -#define CONFIG_SYS_INIT_RAM_ADDR CONFIG_SYS_IMMR -#define CONFIG_SYS_INIT_RAM_SIZE 0x2F00 /* Size of used area in DPRAM */ -#define CONFIG_SYS_INIT_DATA_SIZE 64 /* # bytes reserved for initial data*/ -#define CONFIG_SYS_GBL_DATA_OFFSET (CONFIG_SYS_INIT_RAM_SIZE - CONFIG_SYS_INIT_DATA_SIZE) -#define CONFIG_SYS_INIT_SP_OFFSET CONFIG_SYS_GBL_DATA_OFFSET - -/* - * Start addresses for the final memory configuration - * (Set up by the startup code) - * Please note that CONFIG_SYS_SDRAM_BASE _must_ start at 0 - */ -#define CONFIG_SYS_SDRAM_BASE SDRAM_BASE - -/* - * FLASH organization - */ -#define CONFIG_SYS_FLASH_BASE FLASH_BASE -#define CONFIG_SYS_FLASH_SIZE FLASH_SIZE -#define CONFIG_SYS_FLASH_SECT_SIZE (128 * 1024) -#define CONFIG_SYS_MAX_FLASH_BANKS 1 -#define CONFIG_SYS_MAX_FLASH_SECT 128 - -/* - * The timeout values are for an entire chip and are in milliseconds. - * Yes I know that the write timeout is huge. Accroding to the - * datasheet a single byte takes 630 uS (round to 1 ms) max at worst - * case VCC and temp after 100K programming cycles. It works out - * to 280 minutes (might as well be forever). - */ -#define CONFIG_SYS_FLASH_ERASE_TOUT (CONFIG_SYS_MAX_FLASH_SECT * 5000) -#define CONFIG_SYS_FLASH_WRITE_TOUT (CONFIG_SYS_MAX_FLASH_SECT * 128 * 1024 * 1) - -/* - * Allow direct writes to FLASH from tftp transfers (** dangerous **) - */ -#define CONFIG_SYS_DIRECT_FLASH_TFTP - -/* - * Reserve memory for U-Boot. - */ -#define CONFIG_SYS_MAX_UBOOT_SECTS 4 -#define CONFIG_SYS_MONITOR_LEN (CONFIG_SYS_MAX_UBOOT_SECTS * CONFIG_SYS_FLASH_SECT_SIZE) -#define CONFIG_SYS_MONITOR_BASE CONFIG_SYS_FLASH_BASE - -/* - * Select environment placement. NOTE that u-boot.lds must - * be edited if this is changed! - */ -#undef CONFIG_ENV_IS_IN_FLASH -#define CONFIG_ENV_IS_IN_EEPROM - -#if defined(CONFIG_ENV_IS_IN_EEPROM) -#define CONFIG_ENV_SIZE (2 * 1024) -#define CONFIG_ENV_OFFSET (CONFIG_ENV_EEPROM_SIZE - (8 * 1024)) -#else -#define CONFIG_ENV_SIZE 0x1000 -#define CONFIG_ENV_SECT_SIZE CONFIG_SYS_FLASH_SECT_SIZE - -/* - * This ultimately gets passed right into the linker script, so we have to - * use a number :( - */ -#define CONFIG_ENV_OFFSET 0x060000 -#endif - -/* - * Reserve memory for malloc() - */ -#define CONFIG_SYS_MALLOC_LEN (128 * 1024) - -/* - * For booting Linux, the board info and command line data - * have to be in the first 8 MB of memory, since this is - * the maximum mapped by the Linux kernel during initialization. - */ -#define CONFIG_SYS_BOOTMAPSZ (8 * 1024 * 1024) - -/* - * Cache Configuration - */ -#define CONFIG_SYS_CACHELINE_SIZE 16 /* For all MPC8xx CPUs */ -#if defined(CONFIG_CMD_KGDB) -#define CONFIG_SYS_CACHELINE_SHIFT 4 /* log base 2 of above value */ -#endif - -/*------------------------------------------------------------------------ - * SYPCR - System Protection Control UM 11-9 - * ----------------------------------------------------------------------- - * SYPCR can only be written once after reset! - * - * Software & Bus Monitor Timer max, Bus Monitor enable, SW Watchdog freeze - */ -#if defined(CONFIG_WATCHDOG) -#define CONFIG_SYS_SYPCR ( SYPCR_SWTC | \ - SYPCR_BMT | \ - SYPCR_BME | \ - SYPCR_SWF | \ - SYPCR_SWE | \ - SYPCR_SWRI | \ - SYPCR_SWP \ - ) -#else -#define CONFIG_SYS_SYPCR ( SYPCR_SWTC | \ - SYPCR_BMT | \ - SYPCR_BME | \ - SYPCR_SWF | \ - SYPCR_SWP \ - ) -#endif - -/*----------------------------------------------------------------------- - * SIUMCR - SIU Module Configuration UM 11-6 - *----------------------------------------------------------------------- - * Set debug pin mux, enable SPKROUT and GPLB5*. - */ -#define CONFIG_SYS_SIUMCR ( SIUMCR_DBGC11 | \ - SIUMCR_DBPC11 | \ - SIUMCR_MLRC11 | \ - SIUMCR_GB5E \ - ) - -/*----------------------------------------------------------------------- - * TBSCR - Time Base Status and Control UM 11-26 - *----------------------------------------------------------------------- - * Clear Reference Interrupt Status, Timebase freeze enabled - */ -#define CONFIG_SYS_TBSCR ( TBSCR_REFA | \ - TBSCR_REFB | \ - TBSCR_TBF \ - ) - -/*----------------------------------------------------------------------- - * RTCSC - Real-Time Clock Status and Control Register UM 11-27 - *----------------------------------------------------------------------- - */ -#define CONFIG_SYS_RTCSC ( RTCSC_SEC | \ - RTCSC_ALR | \ - RTCSC_RTF | \ - RTCSC_RTE \ - ) - -/*----------------------------------------------------------------------- - * PISCR - Periodic Interrupt Status and Control UM 11-31 - *----------------------------------------------------------------------- - * Clear Periodic Interrupt Status, Interrupt Timer freezing enabled - */ -#define CONFIG_SYS_PISCR ( PISCR_PS | \ - PISCR_PITF \ - ) - -/*----------------------------------------------------------------------- - * PLPRCR - PLL, Low-Power, and Reset Control Register UM 15-30 - *----------------------------------------------------------------------- - * Reset PLL lock status sticky bit, timer expired status bit and timer - * interrupt status bit. Set MF for 1:2:1 mode. - */ -#define CONFIG_SYS_PLPRCR ( ((0x1 << PLPRCR_MF_SHIFT) & PLPRCR_MF_MSK) | \ - PLPRCR_SPLSS | \ - PLPRCR_TEXPS | \ - PLPRCR_TMIST \ - ) - -/*----------------------------------------------------------------------- - * SCCR - System Clock and reset Control Register UM 15-27 - *----------------------------------------------------------------------- - * Set clock output, timebase and RTC source and divider, - * power management and some other internal clocks - */ -#define SCCR_MASK SCCR_EBDF11 - -#if !defined(CONFIG_SC) -#define CONFIG_SYS_SCCR ( SCCR_TBS | /* timebase = GCLK/2 */ \ - SCCR_COM00 | /* full strength CLKOUT */ \ - SCCR_DFSYNC00 | /* SYNCLK / 1 (normal) */ \ - SCCR_DFBRG00 | /* BRGCLK / 1 (normal) */ \ - SCCR_DFNL000 | \ - SCCR_DFNH000 \ - ) -#else -#define CONFIG_SYS_SCCR ( SCCR_TBS | /* timebase = GCLK/2 */ \ - SCCR_COM00 | /* full strength CLKOUT */ \ - SCCR_DFSYNC00 | /* SYNCLK / 1 (normal) */ \ - SCCR_DFBRG00 | /* BRGCLK / 1 (normal) */ \ - SCCR_DFNL000 | \ - SCCR_DFNH000 | \ - SCCR_RTDIV | \ - SCCR_RTSEL \ - ) -#endif - -/*----------------------------------------------------------------------- - * DER - Debug Enable Register UM 37-46 - *----------------------------------------------------------------------- - * Mask all events that can cause entry into debug mode - */ -#define CONFIG_SYS_DER 0 - -/* - * Initialize Memory Controller: - * - * BR0 and OR0 (FLASH memory) - */ -#define FLASH_BASE0_PRELIM FLASH_BASE - -/* - * Flash address mask - */ -#define CONFIG_SYS_PRELIM_OR_AM 0xfe000000 - -/* - * FLASH timing: - * 33 Mhz bus with ACS = 11, TRLX = 1, CSNT = 1, SCY = 3, EHTR = 1 - */ -#define CONFIG_SYS_OR_TIMING_FLASH ( OR_CSNT_SAM | \ - OR_ACS_DIV2 | \ - OR_BI | \ - OR_SCY_2_CLK | \ - OR_TRLX | \ - OR_EHTR \ - ) - -#define CONFIG_SYS_OR0_PRELIM ( CONFIG_SYS_PRELIM_OR_AM | \ - CONFIG_SYS_OR_TIMING_FLASH \ - ) - -#define CONFIG_SYS_BR0_PRELIM ( (FLASH_BASE0_PRELIM & BR_BA_MSK) | \ - BR_MS_GPCM | \ - BR_PS_8 | \ - BR_V \ - ) - -/* - * SDRAM configuration - */ -#define CONFIG_SYS_OR1_AM 0xfc000000 -#define CONFIG_SYS_OR1 ( (CONFIG_SYS_OR1_AM & OR_AM_MSK) | \ - OR_CSNT_SAM \ - ) - -#define CONFIG_SYS_BR1 ( (SDRAM_BASE & BR_BA_MSK) | \ - BR_MS_UPMA | \ - BR_PS_32 | \ - BR_V \ - ) - -/* - * Refresh rate 7.8 us (= 64 ms / 8K = 31.2 uS quad bursts) for one bank - * of 256 MBit SDRAM - */ -#define CONFIG_SYS_MPTPR_1BK_8K MPTPR_PTP_DIV16 - -/* - * Periodic timer for refresh @ 33 MHz system clock - */ -#define CONFIG_SYS_MAMR_PTA 64 - -/* - * MAMR settings for SDRAM - */ -#define CONFIG_SYS_MAMR_8COL ( (CONFIG_SYS_MAMR_PTA << MAMR_PTA_SHIFT) | \ - MAMR_PTAE | \ - MAMR_AMA_TYPE_1 | \ - MAMR_DSA_1_CYCL | \ - MAMR_G0CLA_A10 | \ - MAMR_RLFA_1X | \ - MAMR_WLFA_1X | \ - MAMR_TLFA_4X \ - ) - -/* - * CS2* configuration for Disk On Chip: - * 33 MHz bus with TRLX=1, ACS=11, CSNT=1, EBDF=1, SCY=2, EHTR=1, - * no burst. - */ -#define CONFIG_SYS_OR2_PRELIM ( (0xffff0000 & OR_AM_MSK) | \ - OR_CSNT_SAM | \ - OR_ACS_DIV2 | \ - OR_BI | \ - OR_SCY_2_CLK | \ - OR_TRLX | \ - OR_EHTR \ - ) - -#define CONFIG_SYS_BR2_PRELIM ( (DOC_BASE & BR_BA_MSK) | \ - BR_PS_8 | \ - BR_MS_GPCM | \ - BR_V \ - ) - -/* - * CS3* configuration for FPGA: - * 33 MHz bus with SCY=15, no burst. - * The FPGA uses TA and TEA to terminate bus cycles, but we - * clear SETA and set the cycle length to a large number so that - * the cycle will still complete even if there is a configuration - * error that prevents TA from asserting on FPGA accesss. - */ -#define CONFIG_SYS_OR3_PRELIM ( (0xfc000000 & OR_AM_MSK) | \ - OR_SCY_15_CLK | \ - OR_BI \ - ) - -#define CONFIG_SYS_BR3_PRELIM ( (FPGA_BASE & BR_BA_MSK) | \ - BR_PS_32 | \ - BR_MS_GPCM | \ - BR_V \ - ) -/* - * CS4* configuration for FPGA SelectMap configuration interface. - * 33 MHz bus, UPMB, no burst. Do not assert GPLB5 on falling edge - * of GCLK1_50 - */ -#define CONFIG_SYS_OR4_PRELIM ( (0xffff0000 & OR_AM_MSK) | \ - OR_G5LS | \ - OR_BI \ - ) - -#define CONFIG_SYS_BR4_PRELIM ( (SELECTMAP_BASE & BR_BA_MSK) | \ - BR_PS_8 | \ - BR_MS_UPMB | \ - BR_V \ - ) - -/* - * CS5* configuration for Mil-Std 1553 databus interface. - * 33 MHz bus, GPCM, no burst. - * The 1553 interface uses TA and TEA to terminate bus cycles, - * but we clear SETA and set the cycle length to a large number so that - * the cycle will still complete even if there is a configuration - * error that prevents TA from asserting on FPGA accesss. - */ -#define CONFIG_SYS_OR5_PRELIM ( (0xffff0000 & OR_AM_MSK) | \ - OR_SCY_15_CLK | \ - OR_EHTR | \ - OR_TRLX | \ - OR_CSNT_SAM | \ - OR_BI \ - ) - -#define CONFIG_SYS_BR5_PRELIM ( (M1553_BASE & BR_BA_MSK) | \ - BR_PS_16 | \ - BR_MS_GPCM | \ - BR_V \ - ) - -/* - * FEC interrupt assignment - */ -#define FEC_INTERRUPT SIU_LEVEL1 - -/* - * Sanity checks - */ -#if defined(CONFIG_SCC1_ENET) && defined(CONFIG_FEC_ENET) -#error Both CONFIG_SCC1_ENET and CONFIG_FEC_ENET configured -#endif - -#endif /* __CONFIG_GEN860T_H */ diff --git a/include/configs/PCI5441.h b/include/configs/PCI5441.h deleted file mode 100644 index 7ae25d7..0000000 --- a/include/configs/PCI5441.h +++ /dev/null @@ -1,150 +0,0 @@ -/* - * (C) Copyright 2004, Psyent Corporation <www.psyent.com> - * Scott McNutt <smcnutt@psyent.com> - * - * SPDX-License-Identifier: GPL-2.0+ - */ - -#ifndef __CONFIG_H -#define __CONFIG_H - -/*------------------------------------------------------------------------ - * BOARD/CPU - *----------------------------------------------------------------------*/ -#define CONFIG_PCI5441 1 /* PCI-5441 board */ -#define CONFIG_SYS_CLK_FREQ 50000000 /* 50 MHz core clk */ - -#define CONFIG_SYS_RESET_ADDR 0x00000000 /* Hard-reset address */ -#define CONFIG_SYS_EXCEPTION_ADDR 0x01000020 /* Exception entry point*/ -#define CONFIG_SYS_NIOS_SYSID_BASE 0x00920828 /* System id address */ -#define CONFIG_BOARD_EARLY_INIT_F 1 /* enable early board-spec. init*/ - -/*------------------------------------------------------------------------ - * CACHE -- the following will support II/s and II/f. The II/s does not - * have dcache, so the cache instructions will behave as NOPs. - *----------------------------------------------------------------------*/ -#define CONFIG_SYS_ICACHE_SIZE 4096 /* 4 KByte total */ -#define CONFIG_SYS_ICACHELINE_SIZE 32 /* 32 bytes/line */ -#define CONFIG_SYS_DCACHE_SIZE 2048 /* 2 KByte (II/f) */ -#define CONFIG_SYS_DCACHELINE_SIZE 4 /* 4 bytes/line (II/f) */ - -/*------------------------------------------------------------------------ - * MEMORY BASE ADDRESSES - *----------------------------------------------------------------------*/ -#define CONFIG_SYS_FLASH_BASE 0x00000000 /* FLASH base addr */ -#define CONFIG_SYS_FLASH_SIZE 0x00800000 /* 8 MByte */ -#define CONFIG_SYS_SDRAM_BASE 0x01000000 /* SDRAM base addr */ -#define CONFIG_SYS_SDRAM_SIZE 0x01000000 /* 16 MByte */ - -/*------------------------------------------------------------------------ - * MEMORY ORGANIZATION - * -Monitor at top. - * -The heap is placed below the monitor. - * -Global data is placed below the heap. - * -The stack is placed below global data (&grows down). - *----------------------------------------------------------------------*/ -#define CONFIG_SYS_MONITOR_LEN (128 * 1024) /* Reserve 128k */ -#define CONFIG_SYS_MALLOC_LEN (CONFIG_ENV_SIZE + 128*1024) - -#define CONFIG_SYS_MONITOR_BASE CONFIG_SYS_TEXT_BASE -#define CONFIG_SYS_MALLOC_BASE (CONFIG_SYS_MONITOR_BASE - CONFIG_SYS_MALLOC_LEN) -#define CONFIG_SYS_GBL_DATA_OFFSET (CONFIG_SYS_MALLOC_BASE - GENERATED_GBL_DATA_SIZE) -#define CONFIG_SYS_INIT_SP CONFIG_SYS_GBL_DATA_OFFSET - -/*------------------------------------------------------------------------ - * FLASH (AM29LV065D) - *----------------------------------------------------------------------*/ -#define CONFIG_SYS_MAX_FLASH_SECT 128 /* Max # sects per bank */ -#define CONFIG_SYS_MAX_FLASH_BANKS 1 /* Max # of flash banks */ -#define CONFIG_SYS_FLASH_ERASE_TOUT 8000 /* Erase timeout (msec) */ -#define CONFIG_SYS_FLASH_WRITE_TOUT 100 /* Write timeout (msec) */ -#define CONFIG_SYS_FLASH_WORD_SIZE unsigned char /* flash word size */ - -/*------------------------------------------------------------------------ - * ENVIRONMENT -- Put environment in sector CONFIG_SYS_MONITOR_LEN above - * CONFIG_SYS_RESET_ADDR, since we assume the monitor is stored at the - * reset address, no? This will keep the environment in user region - * of flash. NOTE: the monitor length must be multiple of sector size - * (which is common practice). - *----------------------------------------------------------------------*/ -#define CONFIG_ENV_IS_IN_FLASH 1 /* Environment in flash */ -#define CONFIG_ENV_SIZE (64 * 1024) /* 64 KByte (1 sector) */ -#define CONFIG_ENV_OVERWRITE /* Serial change Ok */ -#define CONFIG_ENV_ADDR (CONFIG_SYS_RESET_ADDR + CONFIG_SYS_MONITOR_LEN) - -/*------------------------------------------------------------------------ - * CONSOLE - *----------------------------------------------------------------------*/ -#define CONFIG_ALTERA_UART 1 /* Use altera uart */ -#if defined(CONFIG_ALTERA_JTAG_UART) -#define CONFIG_SYS_NIOS_CONSOLE 0x00920820 /* JTAG UART base addr */ -#else -#define CONFIG_SYS_NIOS_CONSOLE 0x009208a0 /* UART base addr */ -#endif - -#define CONFIG_SYS_NIOS_FIXEDBAUD 1 /* Baudrate is fixed */ -#define CONFIG_BAUDRATE 115200 /* Initial baudrate */ -#define CONFIG_SYS_BAUDRATE_TABLE {115200} /* It's fixed ;-) */ - -#define CONFIG_SYS_CONSOLE_INFO_QUIET 1 /* Suppress console info*/ - -/*------------------------------------------------------------------------ - * DEBUG - *----------------------------------------------------------------------*/ -#undef CONFIG_ROM_STUBS /* Stubs not in ROM */ - -/*------------------------------------------------------------------------ - * TIMEBASE -- - * - * The high res timer defaults to 1 msec. Since it includes the period - * registers, the interrupt frequency can be reduced using TMRCNT. - * If the default period is acceptable, TMRCNT can be left undefined. - * TMRMS represents the desired mecs per tick (msecs per interrupt). - *----------------------------------------------------------------------*/ -#define CONFIG_SYS_LOW_RES_TIMER -#define CONFIG_SYS_NIOS_TMRBASE 0x00920860 /* Tick timer base addr */ -#define CONFIG_SYS_NIOS_TMRIRQ 3 /* Timer IRQ num */ -#define CONFIG_SYS_NIOS_TMRMS 10 /* Desired period (msec)*/ -#define CONFIG_SYS_NIOS_TMRCNT \ - (CONFIG_SYS_NIOS_TMRMS * (CONFIG_SYS_CLK_FREQ/1000)) - - -/* - * BOOTP options - */ -#define CONFIG_BOOTP_BOOTFILESIZE -#define CONFIG_BOOTP_BOOTPATH -#define CONFIG_BOOTP_GATEWAY -#define CONFIG_BOOTP_HOSTNAME - - -/* - * Command line configuration. - */ -#define CONFIG_CMD_BDI -#define CONFIG_CMD_ECHO -#define CONFIG_CMD_SAVEENV -#define CONFIG_CMD_FLASH -#define CONFIG_CMD_IMI -#define CONFIG_CMD_IRQ -#define CONFIG_CMD_LOADS -#define CONFIG_CMD_LOADB -#define CONFIG_CMD_MEMORY -#define CONFIG_CMD_MISC -#define CONFIG_CMD_RUN -#define CONFIG_CMD_SAVES - - -/*------------------------------------------------------------------------ - * MISC - *----------------------------------------------------------------------*/ -#define CONFIG_SYS_LONGHELP /* Provide extended help*/ -#define CONFIG_SYS_CBSIZE 256 /* Console I/O buf size */ -#define CONFIG_SYS_MAXARGS 16 /* Max command args */ -#define CONFIG_SYS_BARGSIZE CONFIG_SYS_CBSIZE /* Boot arg buf size */ -#define CONFIG_SYS_PBSIZE (CONFIG_SYS_CBSIZE+sizeof(CONFIG_SYS_PROMPT)+16) /* Print buf size */ -#define CONFIG_SYS_LOAD_ADDR CONFIG_SYS_SDRAM_BASE /* Default load address */ -#define CONFIG_SYS_MEMTEST_START CONFIG_SYS_SDRAM_BASE /* Start addr for test */ -#define CONFIG_SYS_MEMTEST_END CONFIG_SYS_INIT_SP - 0x00020000 - -#endif /* __CONFIG_H */ diff --git a/include/configs/PK1C20.h b/include/configs/PK1C20.h deleted file mode 100644 index e7d0864..0000000 --- a/include/configs/PK1C20.h +++ /dev/null @@ -1,225 +0,0 @@ -/* - * (C) Copyright 2004, Psyent Corporation <www.psyent.com> - * Scott McNutt <smcnutt@psyent.com> - * - * SPDX-License-Identifier: GPL-2.0+ - */ - -#ifndef __CONFIG_H -#define __CONFIG_H - -/*------------------------------------------------------------------------ - * BOARD/CPU - *----------------------------------------------------------------------*/ -#define CONFIG_PK1C20 1 /* PK1C20 board */ -#define CONFIG_SYS_CLK_FREQ 50000000 /* 50 MHz core clk */ - -#define CONFIG_SYS_RESET_ADDR 0x00000000 /* Hard-reset address */ -#define CONFIG_SYS_EXCEPTION_ADDR 0x01000020 /* Exception entry point*/ -#define CONFIG_SYS_NIOS_SYSID_BASE 0x021208b8 /* System id address */ -#define CONFIG_BOARD_EARLY_INIT_F 1 /* enable early board-spec. init*/ - -/*------------------------------------------------------------------------ - * CACHE -- the following will support II/s and II/f. The II/s does not - * have dcache, so the cache instructions will behave as NOPs. - *----------------------------------------------------------------------*/ -#define CONFIG_SYS_ICACHE_SIZE 4096 /* 4 KByte total */ -#define CONFIG_SYS_ICACHELINE_SIZE 32 /* 32 bytes/line */ -#define CONFIG_SYS_DCACHE_SIZE 2048 /* 2 KByte (II/f) */ -#define CONFIG_SYS_DCACHELINE_SIZE 4 /* 4 bytes/line (II/f) */ - -/*------------------------------------------------------------------------ - * MEMORY BASE ADDRESSES - *----------------------------------------------------------------------*/ -#define CONFIG_SYS_FLASH_BASE 0x00000000 /* FLASH base addr */ -#define CONFIG_SYS_FLASH_SIZE 0x00800000 /* 8 MByte */ -#define CONFIG_SYS_SDRAM_BASE 0x01000000 /* SDRAM base addr */ -#define CONFIG_SYS_SDRAM_SIZE 0x01000000 /* 16 MByte */ -#define CONFIG_SYS_SRAM_BASE 0x02000000 /* SRAM base addr */ -#define CONFIG_SYS_SRAM_SIZE 0x00100000 /* 1 MB (only 1M mapped)*/ - -/*------------------------------------------------------------------------ - * MEMORY ORGANIZATION - * -Monitor at top. - * -The heap is placed below the monitor. - * -Global data is placed below the heap. - * -The stack is placed below global data (&grows down). - *----------------------------------------------------------------------*/ -#define CONFIG_SYS_MONITOR_LEN (256 * 1024) /* Reserve 128k */ -#define CONFIG_SYS_MALLOC_LEN (CONFIG_ENV_SIZE + 128*1024) - -#define CONFIG_SYS_MONITOR_BASE CONFIG_SYS_TEXT_BASE -#define CONFIG_SYS_MALLOC_BASE (CONFIG_SYS_MONITOR_BASE - CONFIG_SYS_MALLOC_LEN) -#define CONFIG_SYS_GBL_DATA_OFFSET (CONFIG_SYS_MALLOC_BASE - GENERATED_GBL_DATA_SIZE) -#define CONFIG_SYS_INIT_SP CONFIG_SYS_GBL_DATA_OFFSET - -/*------------------------------------------------------------------------ - * FLASH (AM29LV065D) - *----------------------------------------------------------------------*/ -#define CONFIG_SYS_MAX_FLASH_SECT 128 /* Max # sects per bank */ -#define CONFIG_SYS_MAX_FLASH_BANKS 1 /* Max # of flash banks */ -#define CONFIG_SYS_FLASH_ERASE_TOUT 8000 /* Erase timeout (msec) */ -#define CONFIG_SYS_FLASH_WRITE_TOUT 100 /* Write timeout (msec) */ -#define CONFIG_SYS_FLASH_WORD_SIZE unsigned char /* flash word size */ - -/*------------------------------------------------------------------------ - * ENVIRONMENT -- Put environment in sector CONFIG_SYS_MONITOR_LEN above - * CONFIG_SYS_RESET_ADDR, since we assume the monitor is stored at the - * reset address, no? This will keep the environment in user region - * of flash. NOTE: the monitor length must be multiple of sector size - * (which is common practice). - *----------------------------------------------------------------------*/ -#define CONFIG_ENV_IS_IN_FLASH 1 /* Environment in flash */ -#define CONFIG_ENV_SIZE (64 * 1024) /* 64 KByte (1 sector) */ -#define CONFIG_ENV_OVERWRITE /* Serial change Ok */ -#define CONFIG_ENV_ADDR (CONFIG_SYS_RESET_ADDR + CONFIG_SYS_MONITOR_LEN) - -/*------------------------------------------------------------------------ - * CONSOLE - *----------------------------------------------------------------------*/ -#define CONFIG_ALTERA_UART 1 /* Use altera uart */ -#if defined(CONFIG_ALTERA_JTAG_UART) -#define CONFIG_SYS_NIOS_CONSOLE 0x021208b0 /* JTAG UART base addr */ -#else -#define CONFIG_SYS_NIOS_CONSOLE 0x02120840 /* UART base addr */ -#endif - -#define CONFIG_SYS_NIOS_FIXEDBAUD 1 /* Baudrate is fixed */ -#define CONFIG_BAUDRATE 115200 /* Initial baudrate */ -#define CONFIG_SYS_BAUDRATE_TABLE {115200} /* It's fixed ;-) */ - -#define CONFIG_SYS_CONSOLE_INFO_QUIET 1 /* Suppress console info*/ - -/*------------------------------------------------------------------------ - * EPCS Device -- wne CONFIG_SYS_NIOS_EPCSBASE is defined code/commands for - * epcs device access is enabled. The base address is the epcs - * _register_ base address, NOT THE ADDRESS OF THE MEMORY BLOCK. - * The register base is currently at offset 0x600 from the memory base. - *----------------------------------------------------------------------*/ -#define CONFIG_SYS_NIOS_EPCSBASE 0x02100200 /* EPCS register base */ - -/*------------------------------------------------------------------------ - * DEBUG - *----------------------------------------------------------------------*/ -#undef CONFIG_ROM_STUBS /* Stubs not in ROM */ - -/*------------------------------------------------------------------------ - * TIMEBASE -- - * - * The high res timer defaults to 1 msec. Since it includes the period - * registers, the interrupt frequency can be reduced using TMRCNT. - * If the default period is acceptable, TMRCNT can be left undefined. - * TMRMS represents the desired mecs per tick (msecs per interrupt). - *----------------------------------------------------------------------*/ -#define CONFIG_SYS_LOW_RES_TIMER -#define CONFIG_SYS_NIOS_TMRBASE 0x02120820 /* Tick timer base addr */ -#define CONFIG_SYS_NIOS_TMRIRQ 3 /* Timer IRQ num */ -#define CONFIG_SYS_NIOS_TMRMS 10 /* Desired period */ -#define CONFIG_SYS_NIOS_TMRCNT \ - (CONFIG_SYS_NIOS_TMRMS * (CONFIG_SYS_CLK_FREQ/1000)) - -/*------------------------------------------------------------------------ - * STATUS LED -- Provides a simple blinking led. For Nios2 each board - * must implement its own led routines -- leds are, after all, - * board-specific, no? - *----------------------------------------------------------------------*/ -#define CONFIG_SYS_LEDPIO_ADDR 0x02120870 /* LED PIO base addr */ -#define CONFIG_STATUS_LED /* Enable status driver */ -#define CONFIG_BOARD_SPECIFIC_LED - -#define STATUS_LED_BIT 1 /* Bit-0 on PIO */ -#define STATUS_LED_STATE 1 /* Blinking */ -#define STATUS_LED_PERIOD (500/CONFIG_SYS_NIOS_TMRMS) /* Every 500 msec */ - -/*------------------------------------------------------------------------ - * ETHERNET -- The header file for the SMC91111 driver hurts my eyes ... - * and really doesn't need any additional clutter. So I choose the lazy - * way out to avoid changes there -- define the base address to ensure - * cache bypass so there's no need to monkey with inx/outx macros. - *----------------------------------------------------------------------*/ -#define CONFIG_SMC91111_BASE 0x82110300 /* Base addr (bypass) */ -#define CONFIG_SMC91111 /* Using SMC91c111 */ -#undef CONFIG_SMC91111_EXT_PHY /* Internal PHY */ -#define CONFIG_SMC_USE_32_BIT /* 32-bit interface */ - -#define CONFIG_ETHADDR 08:00:3e:26:0a:5b -#define CONFIG_NETMASK 255.255.255.0 -#define CONFIG_IPADDR 192.168.2.21 -#define CONFIG_SERVERIP 192.168.2.16 - - -/* - * BOOTP options - */ -#define CONFIG_BOOTP_BOOTFILESIZE -#define CONFIG_BOOTP_BOOTPATH -#define CONFIG_BOOTP_GATEWAY -#define CONFIG_BOOTP_HOSTNAME - - -/* - * Command line configuration. - */ - -#define CONFIG_CMD_BDI -#define CONFIG_CMD_DHCP -#define CONFIG_CMD_ECHO -#define CONFIG_CMD_SAVEENV -#define CONFIG_CMD_FLASH -#define CONFIG_CMD_IMI -#define CONFIG_CMD_IRQ -#define CONFIG_CMD_LOADS -#define CONFIG_CMD_LOADB -#define CONFIG_CMD_MEMORY -#define CONFIG_CMD_MISC -#define CONFIG_CMD_NET -#define CONFIG_CMD_PING -#define CONFIG_CMD_RUN -#define CONFIG_CMD_SAVES - - -/*------------------------------------------------------------------------ - * COMPACT FLASH - *----------------------------------------------------------------------*/ -#if defined(CONFIG_CMD_IDE) -#define CONFIG_IDE_PREINIT /* Implement id_preinit */ -#define CONFIG_SYS_IDE_MAXBUS 1 /* 1 IDE bus */ -#define CONFIG_SYS_IDE_MAXDEVICE 1 /* 1 drive per IDE bus */ - -#define CONFIG_SYS_ATA_BASE_ADDR 0x00900800 /* ATA base addr */ -#define CONFIG_SYS_ATA_IDE0_OFFSET 0x0000 /* IDE0 offset */ -#define CONFIG_SYS_ATA_DATA_OFFSET 0x0040 /* Data IO offset */ -#define CONFIG_SYS_ATA_REG_OFFSET 0x0040 /* Register offset */ -#define CONFIG_SYS_ATA_ALT_OFFSET 0x0100 /* Alternate reg offset */ -#define CONFIG_SYS_ATA_STRIDE 4 /* Width betwix addrs */ -#define CONFIG_DOS_PARTITION - -/* Board-specific cf regs */ -#define CONFIG_SYS_CF_PRESENT 0x00900880 /* CF Present PIO base */ -#define CONFIG_SYS_CF_POWER 0x00900890 /* CF Power FET PIO base*/ -#define CONFIG_SYS_CF_ATASEL 0x009008a0 /* CF ATASEL PIO base */ - -#endif - -/*------------------------------------------------------------------------ - * JFFS2 - *----------------------------------------------------------------------*/ -#if defined(CONFIG_CMD_JFFS2) -#define CONFIG_SYS_JFFS_CUSTOM_PART /* board defined part */ -#endif - -/*------------------------------------------------------------------------ - * MISC - *----------------------------------------------------------------------*/ -#define CONFIG_SYS_LONGHELP /* Provide extended help*/ -#define CONFIG_SYS_CBSIZE 256 /* Console I/O buf size */ -#define CONFIG_SYS_MAXARGS 16 /* Max command args */ -#define CONFIG_SYS_BARGSIZE CONFIG_SYS_CBSIZE /* Boot arg buf size */ -#define CONFIG_SYS_PBSIZE (CONFIG_SYS_CBSIZE+sizeof(CONFIG_SYS_PROMPT)+16) /* Print buf size */ -#define CONFIG_SYS_LOAD_ADDR CONFIG_SYS_SDRAM_BASE /* Default load address */ -#define CONFIG_SYS_MEMTEST_START CONFIG_SYS_SDRAM_BASE /* Start addr for test */ -#define CONFIG_SYS_MEMTEST_END CONFIG_SYS_INIT_SP - 0x00020000 - -#define CONFIG_SYS_HUSH_PARSER - -#endif /* __CONFIG_H */ diff --git a/include/configs/SXNI855T.h b/include/configs/SXNI855T.h deleted file mode 100644 index 3894019..0000000 --- a/include/configs/SXNI855T.h +++ /dev/null @@ -1,378 +0,0 @@ -/* - * U-Boot configuration for SIXNET SXNI855T CPU board. - * This board is based (loosely) on the Motorola FADS board, so this - * file is based (loosely) on config_FADS860T.h, see it for additional - * credits. - * - * Copyright (c) 2000-2002 Dave Ellis, SIXNET, dge@sixnetio.com - * - * SPDX-License-Identifier: GPL-2.0+ - */ - -/* - * Memory map: - * - * ff100000 -> ff13ffff : FPGA CS1 - * ff030000 -> ff03ffff : EXPANSION CS7 - * ff020000 -> ff02ffff : DATA FLASH CS4 - * ff018000 -> ff01ffff : UART B CS6/UPMB - * ff010000 -> ff017fff : UART A CS5/UPMB - * ff000000 -> ff00ffff : IMAP internal to the MPC855T - * f8000000 -> fbffffff : FLASH CS0 up to 64MB - * f4000000 -> f7ffffff : NVSRAM CS2 up to 64MB - * 00000000 -> 0fffffff : SDRAM CS3/UPMA up to 256MB - */ - -/* ------------------------------------------------------------------------- */ - -/* - * board/config.h - configuration options, board specific - */ - -#ifndef __CONFIG_H -#define __CONFIG_H - -/* - * High Level Configuration Options - * (easy to change) - */ -#include <mpc8xx_irq.h> - -#define CONFIG_SXNI855T 1 /* SIXNET IPm 855T CPU module */ - -/* The 855T is just a stripped 860T and needs code for 860, so for now - * at least define 860, 860T and 855T - */ -#define CONFIG_MPC860 1 -#define CONFIG_MPC860T 1 -#define CONFIG_MPC855T 1 - -#define CONFIG_SYS_TEXT_BASE 0xF8000000 - -#define CONFIG_8xx_CONS_SMC1 1 /* Console is on SMC1 */ -#undef CONFIG_8xx_CONS_SMC2 -#undef CONFIG_8xx_CONS_SCC1 -#undef CONFIG_8xx_CONS_NONE -#define CONFIG_BAUDRATE 9600 -#define CONFIG_LOADS_ECHO 1 /* echo on for serial download */ - -#define MPC8XX_FACT 10 /* 50 MHz is 5 MHz in times 10 */ - -#define CONFIG_CLOCKS_IN_MHZ 1 /* clocks passsed to Linux in MHz */ - -#if 0 -#define CONFIG_BOOTDELAY -1 /* autoboot disabled */ -#else -#define CONFIG_BOOTDELAY 5 /* autoboot after 5 seconds */ -#endif - -#define CONFIG_HAS_ETH1 - -/*----------------------------------------------------------------------- - * Definitions for status LED - */ -#define CONFIG_STATUS_LED 1 /* Status LED enabled */ - -# define STATUS_LED_PAR im_ioport.iop_papar -# define STATUS_LED_DIR im_ioport.iop_padir -# define STATUS_LED_ODR im_ioport.iop_paodr -# define STATUS_LED_DAT im_ioport.iop_padat - -# define STATUS_LED_BIT 0x8000 /* LED 0 is on PA.0 */ -# define STATUS_LED_PERIOD ((CONFIG_SYS_HZ / 2) / 5) /* blink at 5 Hz */ -# define STATUS_LED_STATE STATUS_LED_BLINKING - -# define STATUS_LED_ACTIVE 0 /* LED on for bit == 0 */ - -# define STATUS_LED_BOOT 0 /* LED 0 used for boot status */ - -#ifdef DEV /* development (debug) settings */ -#define CONFIG_BOOT_LED_STATE STATUS_LED_OFF -#else /* production settings */ -#define CONFIG_BOOT_LED_STATE STATUS_LED_ON -#endif - -#define CONFIG_SHOW_BOOT_PROGRESS 1 - -#define CONFIG_BOOTCOMMAND "bootm f8040000 f8100000" /* autoboot command */ -#define CONFIG_BOOTARGS "root=/dev/ram ip=off" - -#define CONFIG_MISC_INIT_R /* have misc_init_r() function */ -#define CONFIG_BOARD_POSTCLK_INIT /* have board_postclk_init() function */ - -#undef CONFIG_WATCHDOG /* watchdog disabled */ - -#define CONFIG_RTC_DS1306 /* Dallas 1306 real time clock */ - -#define CONFIG_SYS_I2C -#define CONFIG_SYS_I2C_SOFT /* I2C bit-banged */ -#define CONFIG_SYS_I2C_SOFT_SPEED 50000 -#define CONFIG_SYS_I2C_SOFT_SLAVE 0xFE -/* - * Software (bit-bang) I2C driver configuration - */ -#define PB_SCL 0x00000020 /* PB 26 */ -#define PB_SDA 0x00000010 /* PB 27 */ - -#define I2C_INIT (immr->im_cpm.cp_pbdir |= PB_SCL) -#define I2C_ACTIVE (immr->im_cpm.cp_pbdir |= PB_SDA) -#define I2C_TRISTATE (immr->im_cpm.cp_pbdir &= ~PB_SDA) -#define I2C_READ ((immr->im_cpm.cp_pbdat & PB_SDA) != 0) -#define I2C_SDA(bit) if(bit) immr->im_cpm.cp_pbdat |= PB_SDA; \ - else immr->im_cpm.cp_pbdat &= ~PB_SDA -#define I2C_SCL(bit) if(bit) immr->im_cpm.cp_pbdat |= PB_SCL; \ - else immr->im_cpm.cp_pbdat &= ~PB_SCL -#define I2C_DELAY udelay(5) /* 1/4 I2C clock duration */ - -# define CONFIG_SYS_I2C_EEPROM_ADDR 0x50 /* Atmel 24C64 */ -# define CONFIG_SYS_I2C_EEPROM_ADDR_LEN 2 /* two byte address */ - -#define CONFIG_FEC_ENET 1 /* use FEC ethernet */ -#define CONFIG_MII 1 - -#define CONFIG_SYS_DISCOVER_PHY - - -/* - * BOOTP options - */ -#define CONFIG_BOOTP_BOOTFILESIZE -#define CONFIG_BOOTP_BOOTPATH -#define CONFIG_BOOTP_GATEWAY -#define CONFIG_BOOTP_HOSTNAME - - -/* - * Command line configuration. - */ -#include <config_cmd_default.h> - -#define CONFIG_CMD_EEPROM -#define CONFIG_CMD_JFFS2 -#define CONFIG_CMD_DATE - -/* - * Miscellaneous configurable options - */ -#define CONFIG_SYS_LONGHELP /* undef to save a little memory */ -#if defined(CONFIG_CMD_KGDB) -#define CONFIG_SYS_CBSIZE 1024 /* Console I/O Buffer Size */ -#else -#define CONFIG_SYS_CBSIZE 256 /* Console I/O Buffer Size */ -#endif -#define CONFIG_SYS_PBSIZE (CONFIG_SYS_CBSIZE+sizeof(CONFIG_SYS_PROMPT)+16) /* Print Buffer Size */ -#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 0x0100000 /* memtest works on */ -#define CONFIG_SYS_MEMTEST_END 0x0400000 /* 1 ... 4 MB in DRAM */ - -#define CONFIG_SYS_LOAD_ADDR 0x00100000 - -/* - * Low Level Configuration Settings - * (address mappings, register initial values, etc.) - * You should know what you are doing if you make changes here. - */ -/*----------------------------------------------------------------------- - * Internal Memory Mapped Register - */ -#define CONFIG_SYS_IMMR 0xFF000000 -#define CONFIG_SYS_IMMR_SIZE ((uint)(64 * 1024)) - -/*----------------------------------------------------------------------- - * Definitions for initial stack pointer and data area (in DPRAM) - */ -#define CONFIG_SYS_INIT_RAM_ADDR CONFIG_SYS_IMMR -#define CONFIG_SYS_INIT_RAM_SIZE 0x2F00 /* Size of used area in DPRAM */ -#define CONFIG_SYS_GBL_DATA_OFFSET (CONFIG_SYS_INIT_RAM_SIZE - GENERATED_GBL_DATA_SIZE) -#define CONFIG_SYS_INIT_SP_OFFSET CONFIG_SYS_GBL_DATA_OFFSET - -/*----------------------------------------------------------------------- - * Start addresses for the final memory configuration - * (Set up by the startup code) - * Please note that CONFIG_SYS_SDRAM_BASE _must_ start at 0 - */ -#define CONFIG_SYS_SDRAM_BASE 0x00000000 -#define CONFIG_SYS_SRAM_BASE 0xF4000000 -#define CONFIG_SYS_SRAM_SIZE 0x04000000 /* autosize up to 64Mbyte */ - -#define CONFIG_SYS_FLASH_BASE 0xF8000000 -#define CONFIG_SYS_FLASH_SIZE ((uint)(8 * 1024 * 1024)) /* max 8Mbyte */ - -#define CONFIG_SYS_DFLASH_BASE 0xff020000 /* DiskOnChip or NAND FLASH */ -#define CONFIG_SYS_DFLASH_SIZE 0x00010000 - -#define CONFIG_SYS_FPGA_BASE 0xFF100000 /* Xilinx FPGA */ -#define CONFIG_SYS_FPGA_PROG 0xFF130000 /* Programming address */ -#define CONFIG_SYS_FPGA_SIZE 0x00040000 /* 256KiB usable */ - -#define CONFIG_SYS_MONITOR_LEN (256 << 10) /* Reserve 256 kB for Monitor */ -#define CONFIG_SYS_MONITOR_BASE CONFIG_SYS_FLASH_BASE -#define CONFIG_SYS_MALLOC_LEN (128 << 10) /* Reserve 128 kB for malloc() */ - -/* - * For booting Linux, the board info and command line data - * have to be in the first 8 MB of memory, since this is - * the maximum mapped by the Linux kernel during initialization. - */ -#define CONFIG_SYS_BOOTMAPSZ (8 << 20) /* Initial Memory map for Linux */ -/*----------------------------------------------------------------------- - * FLASH organization - */ -#define CONFIG_SYS_MAX_FLASH_BANKS 1 /* max number of memory banks */ -/* Intel 28F640 has 135, 127 64K sectors in 8MB, + 8 more for 8K boot blocks. - * AMD 29LV641 has 128 64K sectors in 8MB - */ -#define CONFIG_SYS_MAX_FLASH_SECT 135 /* max number of sectors on one chip */ - -#define CONFIG_SYS_FLASH_ERASE_TOUT 120000 /* Timeout for Flash Erase (in ms) */ -#define CONFIG_SYS_FLASH_WRITE_TOUT 500 /* Timeout for Flash Write (in ms) */ - -/*----------------------------------------------------------------------- - * Cache Configuration - */ -#define CONFIG_SYS_CACHELINE_SIZE 16 /* For all MPC8xx CPUs */ -#if defined(CONFIG_CMD_KGDB) -#define CONFIG_SYS_CACHELINE_SHIFT 4 /* log base 2 of the above value */ -#endif - -/*----------------------------------------------------------------------- - * SYPCR - System Protection Control 11-9 - * SYPCR can only be written once after reset! - *----------------------------------------------------------------------- - * Software & Bus Monitor Timer max, Bus Monitor enable, SW Watchdog freeze - */ -#if defined(CONFIG_WATCHDOG) -#define CONFIG_SYS_SYPCR (SYPCR_SWTC | SYPCR_BMT | SYPCR_BME | SYPCR_SWF | \ - SYPCR_SWE | SYPCR_SWRI| SYPCR_SWP) -#else -#define CONFIG_SYS_SYPCR (SYPCR_SWTC | SYPCR_BMT | SYPCR_BME | SYPCR_SWF | SYPCR_SWP) -#endif - -/*----------------------------------------------------------------------- - * SIUMCR - SIU Module Configuration 11-6 - *----------------------------------------------------------------------- - * PCMCIA config., multi-function pin tri-state - */ -#define CONFIG_SYS_SIUMCR (SIUMCR_DBGC00 | SIUMCR_DBPC00 | SIUMCR_MLRC01) - -/*----------------------------------------------------------------------- - * TBSCR - Time Base Status and Control 11-26 - *----------------------------------------------------------------------- - * Clear Reference Interrupt Status, Timebase freezing enabled - */ -#define CONFIG_SYS_TBSCR (TBSCR_REFA | TBSCR_REFB | TBSCR_TBE) - -/*----------------------------------------------------------------------- - * PISCR - Periodic Interrupt Status and Control 11-31 - *----------------------------------------------------------------------- - * Clear Periodic Interrupt Status, Interrupt Timer freezing enabled - */ -#define CONFIG_SYS_PISCR (PISCR_PS | PISCR_PITF) - -/*----------------------------------------------------------------------- - * PLPRCR - PLL, Low-Power, and Reset Control Register 15-30 - *----------------------------------------------------------------------- - * set the PLL, the low-power modes and the reset control (15-29) - */ -#define CONFIG_SYS_PLPRCR (((MPC8XX_FACT-1) << PLPRCR_MF_SHIFT) | \ - PLPRCR_SPLSS | PLPRCR_TEXPS | PLPRCR_TMIST) - -/*----------------------------------------------------------------------- - * SCCR - System Clock and reset Control Register 15-27 - *----------------------------------------------------------------------- - * Set clock output, timebase and RTC source and divider, - * power management and some other internal clocks - */ -#define SCCR_MASK SCCR_EBDF11 -#define CONFIG_SYS_SCCR (SCCR_TBS|SCCR_COM00|SCCR_DFSYNC00|SCCR_DFBRG00|SCCR_DFNL000|SCCR_DFNH000|SCCR_DFLCD000|SCCR_DFALCD00) - - /*----------------------------------------------------------------------- - * - *----------------------------------------------------------------------- - * - */ -#define CONFIG_SYS_DER 0 - -/* Because of the way the 860 starts up and assigns CS0 the - * entire address space, we have to set the memory controller - * differently. Normally, you write the option register - * first, and then enable the chip select by writing the - * base register. For CS0, you must write the base register - * first, followed by the option register. - */ - -/* - * Init Memory Controller: - * - ********************************************************** - * BR0 and OR0 (FLASH) - */ - -#define CONFIG_SYS_PRELIM_OR0_AM 0xFC000000 /* OR addr mask */ - -/* FLASH timing: ACS = 10, TRLX = 1, CSNT = 1, SCY = 3, EHTR = 0 */ -#define CONFIG_SYS_OR_TIMING_FLASH (OR_CSNT_SAM | OR_ACS_DIV4 | OR_BI | OR_SCY_3_CLK | OR_TRLX) - -#define CONFIG_SYS_OR0_PRELIM (CONFIG_SYS_PRELIM_OR0_AM | CONFIG_SYS_OR_TIMING_FLASH) - -#define CONFIG_FLASH_16BIT -#define CONFIG_SYS_BR0_PRELIM ((CONFIG_SYS_FLASH_BASE & BR_BA_MSK) | BR_PS_16 | BR_V ) -#define CONFIG_SYS_FLASH_PROTECTION /* need to lock/unlock sectors in hardware */ - -/********************************************************** - * BR1 and OR1 (FPGA) - * These preliminary values are also the final values. - */ -#define CONFIG_SYS_OR_TIMING_FPGA \ - (OR_CSNT_SAM | OR_ACS_DIV2 | OR_BI | OR_SCY_4_CLK | OR_EHTR | OR_TRLX) -#define CONFIG_SYS_BR1_PRELIM ((CONFIG_SYS_FPGA_BASE & BR_BA_MSK) | BR_PS_8 | BR_V ) -#define CONFIG_SYS_OR1_PRELIM (((-CONFIG_SYS_FPGA_SIZE) & OR_AM_MSK) | CONFIG_SYS_OR_TIMING_FPGA) - -/********************************************************** - * BR4 and OR4 (data flash) - * These preliminary values are also the final values. - */ -#define CONFIG_SYS_OR_TIMING_DFLASH \ - (OR_CSNT_SAM | OR_ACS_DIV4 | OR_BI | OR_SCY_2_CLK | OR_EHTR | OR_TRLX) -#define CONFIG_SYS_BR4_PRELIM ((CONFIG_SYS_DFLASH_BASE & BR_BA_MSK) | BR_PS_8 | BR_V ) -#define CONFIG_SYS_OR4_PRELIM (((-CONFIG_SYS_DFLASH_SIZE) & OR_AM_MSK) | CONFIG_SYS_OR_TIMING_DFLASH) - -/********************************************************** - * BR5/6 and OR5/6 (Dual UART) - */ -#define CONFIG_SYS_DUART_SIZE 0x8000 /* 32K window, only uses 8 bytes */ -#define CONFIG_SYS_DUARTA_BASE 0xff010000 -#define CONFIG_SYS_DUARTB_BASE 0xff018000 - -#define DUART_MBMR 0 -#define DUART_OR_VALUE (ORMASK(CONFIG_SYS_DUART_SIZE) | OR_G5LS| OR_BI) -#define DUART_BR_VALUE (BR_MS_UPMB | BR_PS_8 | BR_V) -#define DUART_BR5_VALUE ((CONFIG_SYS_DUARTA_BASE & BR_BA_MSK ) | DUART_BR_VALUE) -#define DUART_BR6_VALUE ((CONFIG_SYS_DUARTB_BASE & BR_BA_MSK ) | DUART_BR_VALUE) - -#define CONFIG_RESET_ON_PANIC /* reset if system panic() */ - -#define CONFIG_ENV_IS_IN_FLASH -#ifdef CONFIG_ENV_IS_IN_FLASH - /* environment is in FLASH */ - #define CONFIG_ENV_ADDR 0xF8040000 /* AM29LV641 or AM29LV800BT */ - #define CONFIG_ENV_ADDR_REDUND 0xF8050000 /* AM29LV641 or AM29LV800BT */ - #define CONFIG_ENV_SECT_SIZE 0x00010000 - #define CONFIG_ENV_SIZE 0x00002000 -#else - /* environment is in EEPROM */ - #define CONFIG_ENV_IS_IN_EEPROM 1 - #define CONFIG_ENV_OFFSET 0 /* at beginning of EEPROM */ - #define CONFIG_ENV_SIZE 1024 /* Use only a part of it*/ -#endif - -#if 1 -#define CONFIG_AUTOBOOT_KEYED /* use key strings to stop autoboot */ -#define CONFIG_AUTOBOOT_PROMPT "autoboot in %d seconds\n", bootdelay -#define CONFIG_AUTOBOOT_DELAY_STR "delayabit" -#define CONFIG_AUTOBOOT_STOP_STR " " /* easy to stop for now */ -#endif - -#endif /* __CONFIG_H */ diff --git a/include/configs/T1040QDS.h b/include/configs/T1040QDS.h index ebee89a..a781ba3 100644 --- a/include/configs/T1040QDS.h +++ b/include/configs/T1040QDS.h @@ -416,6 +416,7 @@ unsigned long get_board_ddr_clk(void); /* Video */ #define CONFIG_FSL_DIU_FB #ifdef CONFIG_FSL_DIU_FB +#define CONFIG_FSL_DIU_CH7301 #define CONFIG_SYS_DIU_ADDR (CONFIG_SYS_CCSRBAR + 0x180000) #define CONFIG_VIDEO #define CONFIG_CMD_BMP diff --git a/include/configs/T104xRDB.h b/include/configs/T104xRDB.h index c4bf0d6..0ee0ff2 100644 --- a/include/configs/T104xRDB.h +++ b/include/configs/T104xRDB.h @@ -32,7 +32,7 @@ #define CONFIG_SPL_I2C_SUPPORT #define CONFIG_SPL_DRIVERS_MISC_SUPPORT #define CONFIG_FSL_LAW /* Use common FSL init code */ -#define CONFIG_SYS_TEXT_BASE 0x00201000 +#define CONFIG_SYS_TEXT_BASE 0x30001000 #define CONFIG_SPL_TEXT_BASE 0xFFFD8000 #define CONFIG_SPL_PAD_TO 0x40000 #define CONFIG_SPL_MAX_SIZE 0x28000 @@ -48,21 +48,21 @@ #ifdef CONFIG_NAND #define CONFIG_SPL_NAND_SUPPORT #define CONFIG_SYS_NAND_U_BOOT_SIZE (768 << 10) -#define CONFIG_SYS_NAND_U_BOOT_DST 0x00200000 -#define CONFIG_SYS_NAND_U_BOOT_START 0x00200000 +#define CONFIG_SYS_NAND_U_BOOT_DST 0x30000000 +#define CONFIG_SYS_NAND_U_BOOT_START 0x30000000 #define CONFIG_SYS_NAND_U_BOOT_OFFS (256 << 10) #define CONFIG_SYS_LDSCRIPT "arch/powerpc/cpu/mpc85xx/u-boot-nand.lds" #define CONFIG_SPL_NAND_BOOT #endif #ifdef CONFIG_SPIFLASH -#define CONFIG_RESET_VECTOR_ADDRESS 0x200FFC +#define CONFIG_RESET_VECTOR_ADDRESS 0x30000FFC #define CONFIG_SPL_SPI_SUPPORT #define CONFIG_SPL_SPI_FLASH_SUPPORT #define CONFIG_SPL_SPI_FLASH_MINIMAL #define CONFIG_SYS_SPI_FLASH_U_BOOT_SIZE (768 << 10) -#define CONFIG_SYS_SPI_FLASH_U_BOOT_DST (0x00200000) -#define CONFIG_SYS_SPI_FLASH_U_BOOT_START (0x00200000) +#define CONFIG_SYS_SPI_FLASH_U_BOOT_DST (0x30000000) +#define CONFIG_SYS_SPI_FLASH_U_BOOT_START (0x30000000) #define CONFIG_SYS_SPI_FLASH_U_BOOT_OFFS (256 << 10) #define CONFIG_SYS_LDSCRIPT "arch/powerpc/cpu/mpc85xx/u-boot.lds" #ifndef CONFIG_SPL_BUILD @@ -72,12 +72,12 @@ #endif #ifdef CONFIG_SDCARD -#define CONFIG_RESET_VECTOR_ADDRESS 0x200FFC +#define CONFIG_RESET_VECTOR_ADDRESS 0x30000FFC #define CONFIG_SPL_MMC_SUPPORT #define CONFIG_SPL_MMC_MINIMAL #define CONFIG_SYS_MMC_U_BOOT_SIZE (768 << 10) -#define CONFIG_SYS_MMC_U_BOOT_DST (0x00200000) -#define CONFIG_SYS_MMC_U_BOOT_START (0x00200000) +#define CONFIG_SYS_MMC_U_BOOT_DST (0x30000000) +#define CONFIG_SYS_MMC_U_BOOT_START (0x30000000) #define CONFIG_SYS_MMC_U_BOOT_OFFS (260 << 10) #define CONFIG_SYS_LDSCRIPT "arch/powerpc/cpu/mpc85xx/u-boot.lds" #ifndef CONFIG_SPL_BUILD @@ -268,6 +268,9 @@ #define CPLD_LBMAP_DFLTBANK 0x40 /* BANK OR | BANK0 */ #define CPLD_LBMAP_RESET 0xFF #define CPLD_LBMAP_SHIFT 0x03 +#ifdef CONFIG_T1042RDB_PI +#define CPLD_DIU_SEL_DFP 0x80 +#endif #define CONFIG_SYS_CPLD_BASE 0xffdf0000 #define CONFIG_SYS_CPLD_BASE_PHYS (0xf00000000ull | CONFIG_SYS_CPLD_BASE) @@ -429,6 +432,24 @@ #define CONFIG_SYS_HUSH_PARSER #define CONFIG_SYS_PROMPT_HUSH_PS2 "> " +#ifdef CONFIG_T1042RDB_PI +/* Video */ +#define CONFIG_FSL_DIU_FB + +#ifdef CONFIG_FSL_DIU_FB +#define CONFIG_FSL_DIU_CH7301 +#define CONFIG_SYS_DIU_ADDR (CONFIG_SYS_CCSRBAR + 0x180000) +#define CONFIG_VIDEO +#define CONFIG_CMD_BMP +#define CONFIG_CFB_CONSOLE +#define CONFIG_CFB_CONSOLE_ANSI +#define CONFIG_VIDEO_SW_CURSOR +#define CONFIG_VGA_AS_SINGLE_DEVICE +#define CONFIG_VIDEO_LOGO +#define CONFIG_VIDEO_BMP_LOGO +#endif +#endif + /* pass open firmware flat tree */ #define CONFIG_OF_LIBFDT #define CONFIG_OF_BOARD_SETUP @@ -461,6 +482,10 @@ #endif #ifdef CONFIG_T1042RDB_PI +/* LDI/DVI Encoder for display */ +#define CONFIG_SYS_I2C_LDI_ADDR 0x38 +#define CONFIG_SYS_I2C_DVI_ADDR 0x75 + /* * RTC configuration */ @@ -772,11 +797,18 @@ #define RAMDISKFILE "t1040rdb_pi/ramdisk.uboot" #endif +#ifdef CONFIG_FSL_DIU_FB +#define DIU_ENVIRONMENT "video-mode=fslfb:1024x768-32@60,monitor=dvi" +#else +#define DIU_ENVIRONMENT +#endif + #define CONFIG_EXTRA_ENV_SETTINGS \ "hwconfig=fsl_ddr:bank_intlv=cs0_cs1;" \ "usb1:dr_mode=host,phy_type=" __stringify(__USB_PHY_TYPE) ";"\ "usb2:dr_mode=host,phy_type=" __stringify(__USB_PHY_TYPE) "\0"\ "netdev=eth0\0" \ + "video-mode=" __stringify(DIU_ENVIRONMENT) "\0" \ "uboot=" __stringify(CONFIG_UBOOTPATH) "\0" \ "ubootaddr=" __stringify(CONFIG_SYS_TEXT_BASE) "\0" \ "tftpflash=tftpboot $loadaddr $uboot && " \ diff --git a/include/configs/aristainetos.h b/include/configs/aristainetos.h new file mode 100644 index 0000000..20aea85 --- /dev/null +++ b/include/configs/aristainetos.h @@ -0,0 +1,325 @@ +/* + * (C) Copyright 2014 + * Heiko Schocher, DENX Software Engineering, hs@denx.de. + * + * Based on: + * Copyright (C) 2012 Freescale Semiconductor, Inc. + * + * Configuration settings for the Freescale i.MX6Q SabreSD board. + * + * SPDX-License-Identifier: GPL-2.0+ + */ +#ifndef __ARISTAINETOS_CONFIG_H +#define __ARISTAINETOS_CONFIG_H + +#define CONFIG_MX6 + +#include "mx6_common.h" +#include <linux/sizes.h> + +#define CONFIG_DISPLAY_CPUINFO +#define CONFIG_DISPLAY_BOARDINFO + +#include <asm/arch/imx-regs.h> +#include <asm/imx-common/gpio.h> + +#define CONFIG_MACH_TYPE 4501 +#define CONFIG_MMCROOT "/dev/mmcblk0p2" +#define CONFIG_DEFAULT_FDT_FILE "aristainetos.dtb" +#define CONFIG_HOSTNAME aristainetos +#define PHYS_SDRAM_SIZE (1u * 1024 * 1024 * 1024) + +#define CONFIG_SYS_GENERIC_BOARD + +/* Size of malloc() pool */ +#define CONFIG_SYS_MALLOC_LEN (64 * SZ_1M) + +#define CONFIG_BOARD_EARLY_INIT_F +#define CONFIG_MXC_GPIO + +#define CONFIG_MXC_UART +#define CONFIG_MXC_UART_BASE UART5_BASE +#define CONFIG_CONSOLE_DEV "ttymxc4" + +#define CONFIG_CMD_FUSE +#define CONFIG_MXC_OCOTP + +/* MMC Configs */ +#define CONFIG_FSL_ESDHC +#define CONFIG_FSL_USDHC +#define CONFIG_SYS_FSL_ESDHC_ADDR 0 + +#define CONFIG_MMC +#define CONFIG_CMD_MMC +#define CONFIG_GENERIC_MMC +#define CONFIG_BOUNCE_BUFFER +#define CONFIG_CMD_EXT2 +#define CONFIG_CMD_FAT +#define CONFIG_DOS_PARTITION + +#define CONFIG_CMD_PING +#define CONFIG_CMD_DHCP +#define CONFIG_CMD_MII +#define CONFIG_CMD_NET +#define CONFIG_FEC_MXC +#define CONFIG_MII +#define IMX_FEC_BASE ENET_BASE_ADDR +#define CONFIG_FEC_XCV_TYPE RMII +#define CONFIG_ETHPRIME "FEC" +#define CONFIG_FEC_MXC_PHYADDR 0 + +#define CONFIG_PHYLIB +#define CONFIG_PHY_MICREL + +#define CONFIG_CMD_SF +#define CONFIG_SPI_FLASH +#define CONFIG_SPI_FLASH_MTD +#define CONFIG_SPI_FLASH_STMICRO +#define CONFIG_MXC_SPI +#define CONFIG_SF_DEFAULT_BUS 3 +#define CONFIG_SF_DEFAULT_CS (0|(IMX_GPIO_NR(3, 20)<<8)) +#define CONFIG_SF_DEFAULT_SPEED 20000000 +#define CONFIG_SF_DEFAULT_MODE SPI_MODE_0 +#define CONFIG_SYS_SPI_ST_ENABLE_WP_PIN + +/* allow to overwrite serial and ethaddr */ +#define CONFIG_ENV_OVERWRITE +#define CONFIG_CONS_INDEX 1 +#define CONFIG_BAUDRATE 115200 + +/* Command definition */ +#include <config_cmd_default.h> + +#define CONFIG_CMD_BMODE +#define CONFIG_CMD_BOOTZ +#define CONFIG_CMD_SETEXPR +#undef CONFIG_CMD_IMLS + +#define CONFIG_BOOTDELAY 3 + +#define CONFIG_LOADADDR 0x12000000 +#define CONFIG_SYS_TEXT_BASE 0x17800000 + +#define CONFIG_EXTRA_ENV_SETTINGS \ + "uimage=uImage\0" \ + "fdt_file=" CONFIG_DEFAULT_FDT_FILE "\0" \ + "fdt_addr_r=0x11000000\0" \ + "kernel_addr_r=0x12000000\0" \ + "kernel_file=uImage\0" \ + "boot_fdt=try\0" \ + "ip_dyn=yes\0" \ + "console=" CONFIG_CONSOLE_DEV "\0" \ + "fdt_high=0xffffffff\0" \ + "initrd_high=0xffffffff\0" \ + "mmcpart=1\0" \ + "mmcdev=" __stringify(CONFIG_SYS_MMC_ENV_DEV) "\0" \ + "mmcroot=" CONFIG_MMCROOT " rootwait rw\0" \ + "mmcargs=setenv bootargs console=${console},${baudrate} " \ + "root=${mmcroot}\0" \ + "loadimage=fatload mmc ${mmcdev}:${mmcpart} ${kernel_addr_r} " \ + "${uimage}\0" \ + "loadfdt=fatload mmc ${mmcdev}:${mmcpart} ${fdt_addr_r} " \ + "${fdt_file}\0" \ + "mmcboot=echo Booting from mmc ...; " \ + "run mmcargs;run loadimage loadfdt fdt_setup;" \ + "bootm ${kernel_addr_r} - ${fdt_addr_r};\0" \ + "rootpath=/opt/eldk-5.5/armv7a-hf/rootfs-sato-sdk\0" \ + "nfsopts=nfsvers=3 nolock rw\0" \ + "netdev=eth0\0" \ + "fdt_setup=fdt addr ${fdt_addr_r};fdt resize;fdt chosen;fdt board\0"\ + "load_fdt=tftp ${fdt_addr_r} ${fdt_file}\0" \ + "load_kernel=tftp ${kernel_addr_r} ${kernel_file}\0" \ + "addmtd=setenv bootargs ${bootargs} ${mtdparts}\0" \ + "get_env=mw ${loadaddr} 0x00000000 0x20000;" \ + "tftp ${loadaddr} /tftpboot/aristainetos/env.txt;" \ + "env import -t ${loadaddr}\0" \ + "addmisc=setenv bootargs ${bootargs} maxcpus=1 loglevel=8\0" \ + "bootargs_defaults=setenv bootargs ${console} ${mtdoops} " \ + "${optargs}\0" \ + "net_args=run bootargs_defaults;setenv bootargs ${bootargs} " \ + "root=/dev/nfs nfsroot=${serverip}:${rootpath},${nfsopts} " \ + "ip=${ipaddr}:${serverip}:${gatewayip}:${netmask}:" \ + "${hostname}:${netdev}:off\0" \ + "net_nfs=run load_kernel load_fdt;run net_args addmtd addmisc;" \ + "run fdt_setup;bootm ${kernel_addr_r} - ${fdt_addr_r}\0" \ + "uboot=/tftpboot/aristainetos/u-boot.imx\0" \ + "load_uboot=tftp ${loadaddr} ${uboot}\0" \ + "uboot_sz=c0000\0" \ + "upd_uboot=mw.b ${loadaddr} 0xff ${uboot_sz};" \ + "mw.b 10200000 0x00 ${uboot_sz};" \ + "run load_uboot;sf probe;sf erase 0 ${uboot_sz};" \ + "sf write ${loadaddr} 400 ${filesize};" \ + "sf read 10200000 400 ${uboot_sz};" \ + "cmp.b ${loadaddr} 10200000 bc000\0" \ + "ubi_prep=ubi part ubi 2048;ubifsmount ubi:kernel\0" \ + "load_kernel_ubi=ubifsload ${kernel_addr_r} uImage\0" \ + "load_fdt_ubi=ubifsload ${fdt_addr_r} aristainetos.dtb\0" \ + "ubi_nfs=run ubiprep load_kernel_ubi load_fdt_ubi;" \ + "run net_args addmtd addmisc;run fdt_setup;" \ + "bootm ${kernel_addr_r} - ${fdt_addr_r}\0" \ + "rootfsname=rootfs\0" \ + "ubi_args=run bootargs_defaults;setenv bootargs ${bootargs} " \ + "ubi.mtd=0,2048 root=ubi0:${rootfsname} rootfstype=ubifs " \ + "ip=${ipaddr}:${serverip}:${gatewayip}:${netmask}:" \ + "${hostname}:${netdev}:off\0" \ + "ubi_ubi=run ubi_prep load_kernel_ubi load_fdt_ubi;" \ + "run bootargs_defaults ubi_args addmtd addmisc;" \ + "run fdt_setup;bootm ${kernel_addr_r} - ${fdt_addr_r}\0" \ + "ubirootfs_file=/tftpboot/aristainetos/rootfs-minimal.ubifs\0" \ + "upd_ubirootfs=run ubi_prep;tftp ${loadaddr} ${ubirootfs_file};" \ + "ubi write ${loadaddr} rootfs ${filesize}\0" \ + "ksz=800000\0" \ + "rootsz=2000000\0" \ + "usersz=8000000\0" \ + "ubi_make=run ubi_prep;ubi create kernel ${ksz};" \ + "ubi create rootfs ${rootsz};ubi create userfs ${usersz}\0" + +#define CONFIG_BOOTCOMMAND \ + "mmc dev ${mmcdev};" \ + "if mmc rescan; then " \ + "run mmcboot;" \ + "else run ubi_ubi; fi" + +#define CONFIG_ARP_TIMEOUT 200UL + +/* Miscellaneous configurable options */ +#define CONFIG_SYS_LONGHELP +#define CONFIG_SYS_HUSH_PARSER +#define CONFIG_SYS_PROMPT_HUSH_PS2 "> " +#define CONFIG_AUTO_COMPLETE +#define CONFIG_SYS_CBSIZE 256 + +/* Print Buffer Size */ +#define CONFIG_SYS_PBSIZE (CONFIG_SYS_CBSIZE + sizeof(CONFIG_SYS_PROMPT) + 16) +#define CONFIG_SYS_MAXARGS 16 +#define CONFIG_SYS_BARGSIZE CONFIG_SYS_CBSIZE + +#define CONFIG_SYS_MEMTEST_START PHYS_SDRAM +#define CONFIG_SYS_MEMTEST_END (CONFIG_SYS_MEMTEST_START + 0x100000) +#define CONFIG_SYS_MEMTEST_SCRATCH 0x10800000 + +#define CONFIG_SYS_LOAD_ADDR CONFIG_LOADADDR + +#define CONFIG_CMDLINE_EDITING +#define CONFIG_STACKSIZE (128 * 1024) + +/* Physical Memory Map */ +#define CONFIG_NR_DRAM_BANKS 1 +#define PHYS_SDRAM MMDC0_ARB_BASE_ADDR + +#define CONFIG_SYS_SDRAM_BASE PHYS_SDRAM +#define CONFIG_SYS_INIT_RAM_ADDR IRAM_BASE_ADDR +#define CONFIG_SYS_INIT_RAM_SIZE IRAM_SIZE + +#define CONFIG_SYS_INIT_SP_OFFSET \ + (CONFIG_SYS_INIT_RAM_SIZE - GENERATED_GBL_DATA_SIZE) +#define CONFIG_SYS_INIT_SP_ADDR \ + (CONFIG_SYS_INIT_RAM_ADDR + CONFIG_SYS_INIT_SP_OFFSET) + +/* FLASH and environment organization */ +#define CONFIG_SYS_NO_FLASH + +#define CONFIG_ENV_SIZE (12 * 1024) +#define CONFIG_ENV_IS_IN_SPI_FLASH +#define CONFIG_SYS_REDUNDAND_ENVIRONMENT +#define CONFIG_ENV_SPI_BUS CONFIG_SF_DEFAULT_BUS +#define CONFIG_ENV_SPI_CS CONFIG_SF_DEFAULT_CS +#define CONFIG_ENV_SPI_MAX_HZ CONFIG_SF_DEFAULT_SPEED +#define CONFIG_ENV_SPI_MODE CONFIG_SF_DEFAULT_MODE +#define CONFIG_ENV_SECT_SIZE (0x010000) +#define CONFIG_ENV_OFFSET (0x0c0000) +#define CONFIG_ENV_OFFSET_REDUND (0x0d0000) + +#define CONFIG_OF_LIBFDT + +#define CONFIG_CMD_CACHE + +#define CONFIG_SYS_FSL_USDHC_NUM 2 + +#define CONFIG_CMD_I2C +#define CONFIG_SYS_I2C +#define CONFIG_SYS_I2C_MXC +#define CONFIG_SYS_I2C_SPEED 100000 +#define CONFIG_SYS_I2C_SLAVE 0x7f +#define CONFIG_SYS_I2C_NOPROBES { {0, 0x00} } + +#define CONFIG_CMD_GPIO +#define CONFIG_GPIO_ENABLE_SPI_FLASH IMX_GPIO_NR(2, 15) + +/* NAND stuff */ +#define CONFIG_CMD_NAND +#define CONFIG_CMD_NAND_TRIMFFS +#define CONFIG_NAND_MXS +#define CONFIG_SYS_MAX_NAND_DEVICE 1 +#define CONFIG_SYS_NAND_BASE 0x40000000 +#define CONFIG_SYS_NAND_5_ADDR_CYCLE +#define CONFIG_SYS_NAND_ONFI_DETECTION + +/* DMA stuff, needed for GPMI/MXS NAND support */ +#define CONFIG_APBH_DMA +#define CONFIG_APBH_DMA_BURST +#define CONFIG_APBH_DMA_BURST8 + +/* RTC */ +#define CONFIG_SYS_I2C_RTC_ADDR 0x68 +#define CONFIG_SYS_RTC_BUS_NUM 2 +#define CONFIG_RTC_M41T11 +#define CONFIG_CMD_DATE + +/* USB Configs */ +#define CONFIG_CMD_USB +#define CONFIG_CMD_FAT +#define CONFIG_USB_EHCI +#define CONFIG_USB_EHCI_MX6 +#define CONFIG_USB_STORAGE +#define CONFIG_USB_MAX_CONTROLLER_COUNT 2 +#define CONFIG_EHCI_HCD_INIT_AFTER_RESET /* For OTG port */ +#define CONFIG_MXC_USB_PORTSC (PORT_PTS_UTMI | PORT_PTS_PTW) +#define CONFIG_MXC_USB_FLAGS 0 + +#define ARISTAINETOS_USB_OTG_PWR IMX_GPIO_NR(4, 15) +#define ARISTAINETOS_USB_H1_PWR IMX_GPIO_NR(3, 31) + +/* UBI support */ +#define CONFIG_CMD_MTDPARTS +#define CONFIG_MTD_PARTITIONS +#define CONFIG_MTD_DEVICE +#define CONFIG_RBTREE +#define CONFIG_LZO +#define CONFIG_CMD_UBI +#define CONFIG_CMD_UBIFS + +#define MTDIDS_DEFAULT "nand0=gpmi-nand" +#define MTDPARTS_DEFAULT "mtdparts=gpmi-nand:-(ubi)" + +#define CONFIG_MTD_UBI_FASTMAP +#define CONFIG_MTD_UBI_FASTMAP_AUTOCONVERT 1 + +#define CONFIG_HW_WATCHDOG +#define CONFIG_IMX_WATCHDOG + +#define CONFIG_FIT + +/* Framebuffer */ +#define CONFIG_VIDEO +#define CONFIG_VIDEO_IPUV3 +/* check this console not needed, after test remove it */ +#define CONFIG_CFB_CONSOLE +#define CONFIG_VGA_AS_SINGLE_DEVICE +#define CONFIG_SYS_CONSOLE_IS_IN_ENV +#define CONFIG_SYS_CONSOLE_OVERWRITE_ROUTINE +#define CONFIG_VIDEO_BMP_RLE8 +#define CONFIG_SPLASH_SCREEN +#define CONFIG_SPLASH_SCREEN_ALIGN +#define CONFIG_BMP_16BPP +#define CONFIG_VIDEO_LOGO +#define CONFIG_VIDEO_BMP_LOGO +#define CONFIG_IPUV3_CLK 198000000 +#define CONFIG_IMX_VIDEO_SKIP + +#define CONFIG_CMD_BMP + +#define CONFIG_PWM_IMX +#define CONFIG_IMX6_PWM_PER_CLK 66000000 + +#endif /* __ARISTAINETOS_CONFIG_H */ diff --git a/include/configs/bf506f-ezkit.h b/include/configs/bf506f-ezkit.h index eed2d5b..5db1819 100644 --- a/include/configs/bf506f-ezkit.h +++ b/include/configs/bf506f-ezkit.h @@ -56,7 +56,7 @@ /* * Flash Settings */ - +/* #define CONFIG_FLASH_CFI_DRIVER #define CONFIG_SYS_FLASH_BASE 0x20000000 #define CONFIG_SYS_FLASH_CFI @@ -64,9 +64,8 @@ #define CONFIG_SYS_MAX_FLASH_SECT 71 #define CONFIG_CMD_FLASH #define CONFIG_MONITOR_IS_IN_RAM -/* -#define CONFIG_SYS_NO_FLASH */ +#define CONFIG_SYS_NO_FLASH /* * SPI Settings diff --git a/include/configs/bf533-stamp.h b/include/configs/bf533-stamp.h index d82c5b2..3d36d84 100644 --- a/include/configs/bf533-stamp.h +++ b/include/configs/bf533-stamp.h @@ -98,10 +98,11 @@ */ #define CONFIG_BFIN_SPI #define CONFIG_ENV_SPI_MAX_HZ 30000000 +/* #define CONFIG_SF_DEFAULT_SPEED 30000000 #define CONFIG_SPI_FLASH #define CONFIG_SPI_FLASH_ALL - +*/ /* * Env Storage Settings diff --git a/include/configs/bf538f-ezkit.h b/include/configs/bf538f-ezkit.h index 742c299..32df5ec 100644 --- a/include/configs/bf538f-ezkit.h +++ b/include/configs/bf538f-ezkit.h @@ -83,10 +83,11 @@ */ #define CONFIG_BFIN_SPI #define CONFIG_ENV_SPI_MAX_HZ 30000000 +/* #define CONFIG_SF_DEFAULT_SPEED 30000000 #define CONFIG_SPI_FLASH #define CONFIG_SPI_FLASH_ALL - +*/ /* * Env Storage Settings diff --git a/include/configs/embestmx6boards.h b/include/configs/embestmx6boards.h index f1000f3..a7fd43b 100644 --- a/include/configs/embestmx6boards.h +++ b/include/configs/embestmx6boards.h @@ -19,6 +19,8 @@ #include "mx6_common.h" #include <linux/sizes.h> +#define CONFIG_SYS_GENERIC_BOARD + #define CONFIG_MXC_UART_BASE UART2_BASE #define CONFIG_CONSOLE_DEV "ttymxc1" #define CONFIG_MMCROOT "/dev/mmcblk1p2" diff --git a/include/configs/exynos4-dt.h b/include/configs/exynos4-dt.h index 44e6ab4..7dac1a3 100644 --- a/include/configs/exynos4-dt.h +++ b/include/configs/exynos4-dt.h @@ -105,6 +105,8 @@ #define CONFIG_G_DNL_PRODUCT_NUM 0x6601 #define CONFIG_G_DNL_THOR_VENDOR_NUM CONFIG_G_DNL_VENDOR_NUM #define CONFIG_G_DNL_THOR_PRODUCT_NUM 0x685D +#define CONFIG_G_DNL_UMS_VENDOR_NUM 0x0525 +#define CONFIG_G_DNL_UMS_PRODUCT_NUM 0xA4A5 #define CONFIG_G_DNL_MANUFACTURER "Samsung" /* Miscellaneous configurable options */ diff --git a/include/configs/gw_ventana.h b/include/configs/gw_ventana.h index 8197a72..b991b09 100644 --- a/include/configs/gw_ventana.h +++ b/include/configs/gw_ventana.h @@ -95,7 +95,9 @@ #define CONFIG_CMD_I2C #define CONFIG_SYS_I2C #define CONFIG_SYS_I2C_MXC -#define CONFIG_SYS_I2C_SPEED 100000 +#define CONFIG_SYS_I2C_SPEED 100000 +#define CONFIG_I2C_GSC 0 +#define CONFIG_I2C_PMIC 1 /* MMC Configs */ #define CONFIG_FSL_ESDHC @@ -164,6 +166,7 @@ #define CONFIG_CMD_SETEXPR #define CONFIG_CMD_BOOTZ #define CONFIG_CMD_GSC +#define CONFIG_CMD_EECONFIG /* Gateworks EEPROM config cmd */ #define CONFIG_CMD_UBI #define CONFIG_RBTREE #define CONFIG_LZO diff --git a/include/configs/imx6_spl.h b/include/configs/imx6_spl.h index 6fdc438..970460d 100644 --- a/include/configs/imx6_spl.h +++ b/include/configs/imx6_spl.h @@ -24,6 +24,7 @@ * and some padding thus 'our' max size is really 0x00908000 - 0x00918000 * or 64KB */ +#define CONFIG_SYS_THUMB_BUILD #define CONFIG_SPL_LDSCRIPT "arch/arm/cpu/armv7/omap-common/u-boot-spl.lds" #define CONFIG_SPL_TEXT_BASE 0x00908000 #define CONFIG_SPL_MAX_SIZE (64 * 1024) diff --git a/include/configs/km/km-powerpc.h b/include/configs/km/km-powerpc.h index 763c5ba..eb85a74 100644 --- a/include/configs/km/km-powerpc.h +++ b/include/configs/km/km-powerpc.h @@ -59,8 +59,9 @@ #define CONFIG_KM_PHRAM 0x100000 /* resereved pram area at the end of memroy [hex] */ #define CONFIG_KM_RESERVED_PRAM 0x0 -/* enable protected RAM */ -#define CONFIG_PRAM 0 +/* set the default PRAM value to at least PNVRAM + PHRAM when pram env variable + * is not valid yet, which is the case for when u-boot copies itself to RAM */ +#define CONFIG_PRAM ((CONFIG_KM_PNVRAM + CONFIG_KM_PHRAM)>>10) #define CONFIG_KM_CRAMFS_ADDR 0x800000 #define CONFIG_KM_KERNEL_ADDR 0x400000 /* 3968Kbytes */ diff --git a/include/configs/koelsch.h b/include/configs/koelsch.h index 6795f28..3c2b613 100644 --- a/include/configs/koelsch.h +++ b/include/configs/koelsch.h @@ -171,7 +171,7 @@ /* USB */ #define CONFIG_USB_EHCI #define CONFIG_USB_EHCI_RMOBILE -#define CONFIG_USB_MAX_CONTROLLER_COUNT 3 +#define CONFIG_USB_MAX_CONTROLLER_COUNT 2 #define CONFIG_USB_STORAGE #endif /* __KOELSCH_H */ diff --git a/include/configs/lager.h b/include/configs/lager.h index f39a788..74c998f 100644 --- a/include/configs/lager.h +++ b/include/configs/lager.h @@ -175,7 +175,7 @@ /* USB */ #define CONFIG_USB_EHCI #define CONFIG_USB_EHCI_RMOBILE -#define CONFIG_USB_MAX_CONTROLLER_COUNT 4 +#define CONFIG_USB_MAX_CONTROLLER_COUNT 3 #define CONFIG_USB_STORAGE #endif /* __LAGER_H */ diff --git a/include/configs/m53evk.h b/include/configs/m53evk.h index 6e5200d..df6a226 100644 --- a/include/configs/m53evk.h +++ b/include/configs/m53evk.h @@ -175,7 +175,7 @@ #ifdef CONFIG_CMD_I2C #define CONFIG_SYS_I2C #define CONFIG_SYS_I2C_MXC -#define CONFIG_SYS_SPD_BUS_NUM 1 /* I2C2 */ +#define CONFIG_SYS_RTC_BUS_NUM 1 /* I2C2 */ #endif /* diff --git a/include/configs/mx31pdk.h b/include/configs/mx31pdk.h index f796a41..bc4583b 100644 --- a/include/configs/mx31pdk.h +++ b/include/configs/mx31pdk.h @@ -20,6 +20,8 @@ #define CONFIG_ARM1136 /* This is an arm1136 CPU core */ #define CONFIG_MX31 /* in a mx31 */ +#define CONFIG_SYS_GENERIC_BOARD + #define CONFIG_DISPLAY_CPUINFO #define CONFIG_DISPLAY_BOARDINFO diff --git a/include/configs/mx6_common.h b/include/configs/mx6_common.h index e4a5cc5..135a3f5 100644 --- a/include/configs/mx6_common.h +++ b/include/configs/mx6_common.h @@ -28,4 +28,6 @@ #define CONFIG_SYS_PL310_BASE L2_PL310_BASE #endif +#define CONFIG_MP + #endif diff --git a/include/configs/mx6sxsabresd.h b/include/configs/mx6sxsabresd.h new file mode 100644 index 0000000..1eda65e --- /dev/null +++ b/include/configs/mx6sxsabresd.h @@ -0,0 +1,216 @@ +/* + * Copyright 2014 Freescale Semiconductor, Inc. + * + * Configuration settings for the Freescale i.MX6SX Sabresd board. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + + +#ifndef __CONFIG_H +#define __CONFIG_H + +#include <asm/arch/imx-regs.h> +#include <linux/sizes.h> +#include "mx6_common.h" + +#define CONFIG_MX6 +#define CONFIG_DISPLAY_CPUINFO +#define CONFIG_DISPLAY_BOARDINFO + +#define CONFIG_CMDLINE_TAG +#define CONFIG_SETUP_MEMORY_TAGS +#define CONFIG_INITRD_TAG +#define CONFIG_REVISION_TAG +#define CONFIG_SYS_GENERIC_BOARD + +/* Size of malloc() pool */ +#define CONFIG_SYS_MALLOC_LEN (3 * SZ_1M) + +#define CONFIG_BOARD_EARLY_INIT_F +#define CONFIG_BOARD_LATE_INIT +#define CONFIG_MXC_GPIO + +#define CONFIG_MXC_UART +#define CONFIG_MXC_UART_BASE UART1_BASE + +/* allow to overwrite serial and ethaddr */ +#define CONFIG_ENV_OVERWRITE +#define CONFIG_CONS_INDEX 1 +#define CONFIG_BAUDRATE 115200 + +/* Command definition */ +#include <config_cmd_default.h> + +#undef CONFIG_CMD_IMLS + +#define CONFIG_BOOTDELAY 3 + +#define CONFIG_LOADADDR 0x80800000 +#define CONFIG_SYS_TEXT_BASE 0x87800000 + +#define CONFIG_EXTRA_ENV_SETTINGS \ + "script=boot.scr\0" \ + "image=zImage\0" \ + "console=ttymxc0\0" \ + "fdt_high=0xffffffff\0" \ + "initrd_high=0xffffffff\0" \ + "fdt_file=imx6sx-sdb.dtb\0" \ + "fdt_addr=0x88000000\0" \ + "boot_fdt=try\0" \ + "ip_dyn=yes\0" \ + "mmcdev=0\0" \ + "mmcpart=1\0" \ + "mmcroot=/dev/mmcblk0p2 rootwait rw\0" \ + "mmcargs=setenv bootargs console=${console},${baudrate} " \ + "root=${mmcroot}\0" \ + "loadbootscript=" \ + "fatload mmc ${mmcdev}:${mmcpart} ${loadaddr} ${script};\0" \ + "bootscript=echo Running bootscript from mmc ...; " \ + "source\0" \ + "loadimage=fatload mmc ${mmcdev}:${mmcpart} ${loadaddr} ${image}\0" \ + "loadfdt=fatload mmc ${mmcdev}:${mmcpart} ${fdt_addr} ${fdt_file}\0" \ + "mmcboot=echo Booting from mmc ...; " \ + "run mmcargs; " \ + "if test ${boot_fdt} = yes || test ${boot_fdt} = try; then " \ + "if run loadfdt; then " \ + "bootz ${loadaddr} - ${fdt_addr}; " \ + "else " \ + "if test ${boot_fdt} = try; then " \ + "bootz; " \ + "else " \ + "echo WARN: Cannot load the DT; " \ + "fi; " \ + "fi; " \ + "else " \ + "bootz; " \ + "fi;\0" \ + "netargs=setenv bootargs console=${console},${baudrate} " \ + "root=/dev/nfs " \ + "ip=dhcp nfsroot=${serverip}:${nfsroot},v3,tcp\0" \ + "netboot=echo Booting from net ...; " \ + "run netargs; " \ + "if test ${ip_dyn} = yes; then " \ + "setenv get_cmd dhcp; " \ + "else " \ + "setenv get_cmd tftp; " \ + "fi; " \ + "${get_cmd} ${image}; " \ + "if test ${boot_fdt} = yes || test ${boot_fdt} = try; then " \ + "if ${get_cmd} ${fdt_addr} ${fdt_file}; then " \ + "bootz ${loadaddr} - ${fdt_addr}; " \ + "else " \ + "if test ${boot_fdt} = try; then " \ + "bootz; " \ + "else " \ + "echo WARN: Cannot load the DT; " \ + "fi; " \ + "fi; " \ + "else " \ + "bootz; " \ + "fi;\0" + +#define CONFIG_BOOTCOMMAND \ + "mmc dev ${mmcdev};" \ + "mmc dev ${mmcdev}; if mmc rescan; then " \ + "if run loadbootscript; then " \ + "run bootscript; " \ + "else " \ + "if run loadimage; then " \ + "run mmcboot; " \ + "else run netboot; " \ + "fi; " \ + "fi; " \ + "else run netboot; fi" + +/* Miscellaneous configurable options */ +#define CONFIG_SYS_LONGHELP +#define CONFIG_SYS_HUSH_PARSER +#define CONFIG_AUTO_COMPLETE +#define CONFIG_SYS_CBSIZE 1024 + +/* Print Buffer Size */ +#define CONFIG_SYS_PBSIZE (CONFIG_SYS_CBSIZE + sizeof(CONFIG_SYS_PROMPT) + 16) +#define CONFIG_SYS_MAXARGS 256 +#define CONFIG_SYS_BARGSIZE CONFIG_SYS_CBSIZE + +#define CONFIG_SYS_MEMTEST_START 0x80000000 +#define CONFIG_SYS_MEMTEST_END (CONFIG_SYS_MEMTEST_START + 0x10000) + +#define CONFIG_SYS_LOAD_ADDR CONFIG_LOADADDR + +#define CONFIG_CMDLINE_EDITING +#define CONFIG_STACKSIZE SZ_128K + +/* Physical Memory Map */ +#define CONFIG_NR_DRAM_BANKS 1 +#define PHYS_SDRAM MMDC0_ARB_BASE_ADDR +#define PHYS_SDRAM_SIZE SZ_1G + +#define CONFIG_SYS_SDRAM_BASE PHYS_SDRAM +#define CONFIG_SYS_INIT_RAM_ADDR IRAM_BASE_ADDR +#define CONFIG_SYS_INIT_RAM_SIZE IRAM_SIZE + +#define CONFIG_SYS_INIT_SP_OFFSET \ + (CONFIG_SYS_INIT_RAM_SIZE - GENERATED_GBL_DATA_SIZE) +#define CONFIG_SYS_INIT_SP_ADDR \ + (CONFIG_SYS_INIT_RAM_ADDR + CONFIG_SYS_INIT_SP_OFFSET) + +/* MMC Configuration */ +#define CONFIG_FSL_ESDHC +#define CONFIG_FSL_USDHC +#define CONFIG_SYS_FSL_ESDHC_ADDR 0 + +#define CONFIG_MMC +#define CONFIG_CMD_MMC +#define CONFIG_GENERIC_MMC +#define CONFIG_BOUNCE_BUFFER +#define CONFIG_CMD_EXT2 +#define CONFIG_CMD_FAT +#define CONFIG_DOS_PARTITION + +/* I2C Configs */ +#define CONFIG_CMD_I2C +#define CONFIG_SYS_I2C +#define CONFIG_SYS_I2C_MXC +#define CONFIG_SYS_I2C_SPEED 100000 + +/* PMIC */ +#define CONFIG_POWER +#define CONFIG_POWER_I2C +#define CONFIG_POWER_PFUZE100 +#define CONFIG_POWER_PFUZE100_I2C_ADDR 0x08 + +/* Network */ +#define CONFIG_CMD_PING +#define CONFIG_CMD_DHCP +#define CONFIG_CMD_MII +#define CONFIG_CMD_NET +#define CONFIG_FEC_MXC +#define CONFIG_MII + +#define IMX_FEC_BASE ENET_BASE_ADDR +#define CONFIG_FEC_MXC_PHYADDR 0x1 + +#define CONFIG_FEC_XCV_TYPE RGMII +#define CONFIG_ETHPRIME "FEC" + +#define CONFIG_PHYLIB +#define CONFIG_PHY_ATHEROS + +/* FLASH and environment organization */ +#define CONFIG_SYS_NO_FLASH + +#define CONFIG_ENV_OFFSET (6 * SZ_64K) +#define CONFIG_ENV_SIZE SZ_8K +#define CONFIG_ENV_IS_IN_MMC +#define CONFIG_SYS_MMC_ENV_DEV 0 + +#define CONFIG_OF_LIBFDT +#define CONFIG_CMD_BOOTZ + +#ifndef CONFIG_SYS_DCACHE_OFF +#define CONFIG_CMD_CACHE +#endif + +#endif /* __CONFIG_H */ diff --git a/include/configs/mxs.h b/include/configs/mxs.h index 5f4e48e..eb96fc1 100644 --- a/include/configs/mxs.h +++ b/include/configs/mxs.h @@ -93,8 +93,6 @@ /* U-Boot general configuration */ #define CONFIG_SYS_LONGHELP -#ifndef CONFIG_SYS_PROMPT -#endif #define CONFIG_SYS_CBSIZE 1024 /* Console I/O buffer size */ #define CONFIG_SYS_PBSIZE \ (CONFIG_SYS_CBSIZE + sizeof(CONFIG_SYS_PROMPT) + 16) diff --git a/include/configs/nios2-generic.h b/include/configs/nios2-generic.h index 1578b01..51b1d00 100644 --- a/include/configs/nios2-generic.h +++ b/include/configs/nios2-generic.h @@ -15,6 +15,8 @@ #include "../board/altera/nios2-generic/custom_fpga.h" /* fpga parameters */ #define CONFIG_BOARD_NAME "nios2-generic" /* custom board name */ #define CONFIG_BOARD_EARLY_INIT_F /* enable early board-spec. init */ +#define CONFIG_DISPLAY_CPUINFO +#define CONFIG_DISPLAY_BOARDINFO #define CONFIG_SYS_NIOS_SYSID_BASE CONFIG_SYS_SYSID_BASE /* diff --git a/include/configs/omap1510.h b/include/configs/omap1510.h deleted file mode 100644 index 41f7973..0000000 --- a/include/configs/omap1510.h +++ /dev/null @@ -1,772 +0,0 @@ -/* - * - * BRIEF MODULE DESCRIPTION - * OMAP hardware map - * - * Copyright (C) 2001 RidgeRun, Inc. (http://www.ridgerun.com) - * Author: RidgeRun, Inc. - * Greg Lonnon (glonnon@ridgerun.com) or info@ridgerun.com - * - * SPDX-License-Identifier: GPL-2.0+ - */ - -#include <linux/sizes.h> - -/* - There are 2 sets of general I/O --> - 1. GPIO (shared between ARM & DSP, configured by ARM) - 2. MPUIO which can be used only by the ARM. - - Base address FFFB:5000 is where the ARM accesses the MPUIO control registers - (see 7.2.2 of the TRM for MPUIO reg definitions). - - Base address E101:5000 is reserved for ARM access of the same MPUIO control - regs, but via the DSP I/O map. This address is unavailable on 1510. - - Base address FFFC:E000 is where the ARM accesses the GPIO config registers - directly via its own peripheral bus. - - Base address E101:E000 is where the ARM can access the same GPIO config - registers, but the access takes place through the ARM port interface (called - API or MPUI) via the DSP's peripheral bus (DSP I/O space). - - Therefore, the ARM should setup the GPIO regs thru the FFFC:E000 addresses - instead of the E101:E000 addresses. The DSP has only read access of the pin - control register, so this may explain the inability to write to E101:E018. - Try accessing pin control reg at FFFC:E018. - */ -#define OMAP1510_GPIO_BASE 0xfffce000 -#define OMAP1510_GPIO_START OMAP1510_GPIO_BASE -#define OMAP1510_GPIO_SIZE SZ_4K - -#define OMAP1510_MCBSP1_BASE 0xE1011000 -#define OMAP1510_MCBSP1_SIZE SZ_4K -#define OMAP1510_MCBSP1_START 0xE1011000 - -#define OMAP1510_MCBSP2_BASE 0xFFFB1000 - -#define OMAP1510_MCBSP3_BASE 0xE1017000 -#define OMAP1510_MCBSP3_SIZE SZ_4K -#define OMAP1510_MCBSP3_START 0xE1017000 - -/* - * Where's the flush address (for flushing D and I cache?) - */ -#define FLUSH_BASE 0xdf000000 -#define FLUSH_BASE_PHYS 0x00000000 - -#ifndef __ASSEMBLER__ - -#define PCIO_BASE 0 - -/* - * RAM definitions - */ -#define MAPTOPHYS(a) ((unsigned long)(a) - PAGE_OFFSET) -#define KERNTOPHYS(a) ((unsigned long)(&a)) -#define KERNEL_BASE (0x10008000) -#endif - -/* macro to get at IO space when running virtually */ -#define IO_ADDRESS(x) ((x)) - -/* ---------------------------------------------------------------------------- - * OMAP1510 system registers - * ---------------------------------------------------------------------------- - */ - -#define OMAP1510_UART1_BASE 0xfffb0000 /* "BLUETOOTH-UART" */ -#define OMAP1510_UART2_BASE 0xfffb0800 /* "MODEM-UART" */ -#define OMAP1510_RTC_BASE 0xfffb4800 /* RTC */ -#define OMAP1510_UART3_BASE 0xfffb9800 /* Shared MPU/DSP UART */ -#define OMAP1510_COM_MCBSP2_BASE 0xffff1000 /* Com McBSP2 */ -#define OMAP1510_AUDIO_MCBSP_BASE 0xffff1800 /* Audio McBSP2 */ -#define OMAP1510_ARMIO_BASE 0xfffb5000 /* keyboard/gpio */ - -/* - * OMAP1510 UART3 Registers - */ - -#define OMAP_MPU_UART3_BASE 0xFFFB9800 /* UART3 through MPU bus */ - -/* UART3 Registers Maping through MPU bus */ - -#define UART3_RHR (OMAP_MPU_UART3_BASE + 0) -#define UART3_THR (OMAP_MPU_UART3_BASE + 0) -#define UART3_DLL (OMAP_MPU_UART3_BASE + 0) -#define UART3_IER (OMAP_MPU_UART3_BASE + 4) -#define UART3_DLH (OMAP_MPU_UART3_BASE + 4) -#define UART3_IIR (OMAP_MPU_UART3_BASE + 8) -#define UART3_FCR (OMAP_MPU_UART3_BASE + 8) -#define UART3_EFR (OMAP_MPU_UART3_BASE + 8) -#define UART3_LCR (OMAP_MPU_UART3_BASE + 0x0C) -#define UART3_MCR (OMAP_MPU_UART3_BASE + 0x10) -#define UART3_XON1_ADDR1 (OMAP_MPU_UART3_BASE + 0x10) -#define UART3_XON2_ADDR2 (OMAP_MPU_UART3_BASE + 0x14) -#define UART3_LSR (OMAP_MPU_UART3_BASE + 0x14) -#define UART3_TCR (OMAP_MPU_UART3_BASE + 0x18) -#define UART3_MSR (OMAP_MPU_UART3_BASE + 0x18) -#define UART3_XOFF1 (OMAP_MPU_UART3_BASE + 0x18) -#define UART3_XOFF2 (OMAP_MPU_UART3_BASE + 0x1C) -#define UART3_SPR (OMAP_MPU_UART3_BASE + 0x1C) -#define UART3_TLR (OMAP_MPU_UART3_BASE + 0x1C) -#define UART3_MDR1 (OMAP_MPU_UART3_BASE + 0x20) -#define UART3_MDR2 (OMAP_MPU_UART3_BASE + 0x24) -#define UART3_SFLSR (OMAP_MPU_UART3_BASE + 0x28) -#define UART3_TXFLL (OMAP_MPU_UART3_BASE + 0x28) -#define UART3_RESUME (OMAP_MPU_UART3_BASE + 0x2C) -#define UART3_TXFLH (OMAP_MPU_UART3_BASE + 0x2C) -#define UART3_SFREGL (OMAP_MPU_UART3_BASE + 0x30) -#define UART3_RXFLL (OMAP_MPU_UART3_BASE + 0x30) -#define UART3_SFREGH (OMAP_MPU_UART3_BASE + 0x34) -#define UART3_RXFLH (OMAP_MPU_UART3_BASE + 0x34) -#define UART3_BLR (OMAP_MPU_UART3_BASE + 0x38) -#define UART3_ACREG (OMAP_MPU_UART3_BASE + 0x3C) -#define UART3_DIV16 (OMAP_MPU_UART3_BASE + 0x3C) -#define UART3_SCR (OMAP_MPU_UART3_BASE + 0x40) -#define UART3_SSR (OMAP_MPU_UART3_BASE + 0x44) -#define UART3_EBLR (OMAP_MPU_UART3_BASE + 0x48) -#define UART3_OSC_12M_SEL (OMAP_MPU_UART3_BASE + 0x4C) -#define UART3_MVR (OMAP_MPU_UART3_BASE + 0x50) - -/* - * Configuration Registers - */ -#define FUNC_MUX_CTRL_0 0xfffe1000 -#define FUNC_MUX_CTRL_1 0xfffe1004 -#define FUNC_MUX_CTRL_2 0xfffe1008 -#define COMP_MODE_CTRL_0 0xfffe100c -#define FUNC_MUX_CTRL_3 0xfffe1010 -#define FUNC_MUX_CTRL_4 0xfffe1014 -#define FUNC_MUX_CTRL_5 0xfffe1018 -#define FUNC_MUX_CTRL_6 0xfffe101C -#define FUNC_MUX_CTRL_7 0xfffe1020 -#define FUNC_MUX_CTRL_8 0xfffe1024 -#define FUNC_MUX_CTRL_9 0xfffe1028 -#define FUNC_MUX_CTRL_A 0xfffe102C -#define FUNC_MUX_CTRL_B 0xfffe1030 -#define FUNC_MUX_CTRL_C 0xfffe1034 -#define FUNC_MUX_CTRL_D 0xfffe1038 -#define PULL_DWN_CTRL_0 0xfffe1040 -#define PULL_DWN_CTRL_1 0xfffe1044 -#define PULL_DWN_CTRL_2 0xfffe1048 -#define PULL_DWN_CTRL_3 0xfffe104c -#define GATE_INH_CTRL_0 0xfffe1050 -#define VOLTAGE_CTRL_0 0xfffe1060 -#define TEST_DBG_CTRL_0 0xfffe1070 - -#define MOD_CONF_CTRL_0 0xfffe1080 - -#ifdef CONFIG_OMAP1610 /* 1610 Configuration Register */ - -#define USB_OTG_CTRL 0xFFFB040C -#define USB_TRANSCEIVER_CTRL 0xFFFE1064 -#define PULL_DWN_CTRL_4 0xFFFE10AC -#define PU_PD_SEL_0 0xFFFE10B4 -#define PU_PD_SEL_1 0xFFFE10B8 -#define PU_PD_SEL_2 0xFFFE10BC -#define PU_PD_SEL_3 0xFFFE10C0 -#define PU_PD_SEL_4 0xFFFE10C4 - -#endif -/* - * Traffic Controller Memory Interface Registers - */ -#define TCMIF_BASE 0xfffecc00 -#define IMIF_PRIO (TCMIF_BASE + 0x00) -#define EMIFS_PRIO_REG (TCMIF_BASE + 0x04) -#define EMIFF_PRIO_REG (TCMIF_BASE + 0x08) -#define EMIFS_CONFIG_REG (TCMIF_BASE + 0x0c) -#define EMIFS_CS0_CONFIG (TCMIF_BASE + 0x10) -#define EMIFS_CS1_CONFIG (TCMIF_BASE + 0x14) -#define EMIFS_CS2_CONFIG (TCMIF_BASE + 0x18) -#define EMIFS_CS3_CONFIG (TCMIF_BASE + 0x1c) -#define EMIFF_SDRAM_CONFIG (TCMIF_BASE + 0x20) -#define EMIFF_MRS (TCMIF_BASE + 0x24) -#define TC_TIMEOUT1 (TCMIF_BASE + 0x28) -#define TC_TIMEOUT2 (TCMIF_BASE + 0x2c) -#define TC_TIMEOUT3 (TCMIF_BASE + 0x30) -#define TC_ENDIANISM (TCMIF_BASE + 0x34) -#define EMIFF_SDRAM_CONFIG_2 (TCMIF_BASE + 0x3c) -#define EMIF_CFG_DYNAMIC_WS (TCMIF_BASE + 0x40) - -/* - * LCD Panel - */ -#define TI925_LCD_BASE 0xFFFEC000 -#define TI925_LCD_CONTROL (TI925_LCD_BASE) -#define TI925_LCD_TIMING0 (TI925_LCD_BASE+0x4) -#define TI925_LCD_TIMING1 (TI925_LCD_BASE+0x8) -#define TI925_LCD_TIMING2 (TI925_LCD_BASE+0xc) -#define TI925_LCD_STATUS (TI925_LCD_BASE+0x10) -#define TI925_LCD_SUBPANEL (TI925_LCD_BASE+0x14) - -#define OMAP_LCD_CONTROL TI925_LCD_CONTROL - -/* I2C Registers */ - -#define I2C_BASE 0xfffb3800 - -#define I2C_REV (I2C_BASE + 0x00) -#define I2C_IE (I2C_BASE + 0x04) -#define I2C_STAT (I2C_BASE + 0x08) -#define I2C_IV (I2C_BASE + 0x0c) -#define I2C_BUF (I2C_BASE + 0x14) -#define I2C_CNT (I2C_BASE + 0x18) -#define I2C_DATA (I2C_BASE + 0x1c) -#define I2C_CON (I2C_BASE + 0x24) -#define I2C_OA (I2C_BASE + 0x28) -#define I2C_SA (I2C_BASE + 0x2c) -#define I2C_PSC (I2C_BASE + 0x30) -#define I2C_SCLL (I2C_BASE + 0x34) -#define I2C_SCLH (I2C_BASE + 0x38) -#define I2C_SYSTEST (I2C_BASE + 0x3c) - -/* I2C masks */ - -/* I2C Interrupt Enable Register (I2C_IE): */ - -#define I2C_IE_XRDY_IE (1 << 4) /* Transmit data ready interrupt enable */ -#define I2C_IE_RRDY_IE (1 << 3) /* Receive data ready interrupt enable */ -#define I2C_IE_ARDY_IE (1 << 2) /* Register access ready interrupt enable */ -#define I2C_IE_NACK_IE (1 << 1) /* No acknowledgment interrupt enable */ -#define I2C_IE_AL_IE (1 << 0) /* Arbitration lost interrupt enable */ - -/* I2C Status Register (I2C_STAT): */ - -#define I2C_STAT_SBD (1 << 15) /* Single byte data */ -#define I2C_STAT_BB (1 << 12) /* Bus busy */ -#define I2C_STAT_ROVR (1 << 11) /* Receive overrun */ -#define I2C_STAT_XUDF (1 << 10) /* Transmit underflow */ -#define I2C_STAT_AAS (1 << 9) /* Address as slave */ -#define I2C_STAT_AD0 (1 << 8) /* Address zero */ -#define I2C_STAT_XRDY (1 << 4) /* Transmit data ready */ -#define I2C_STAT_RRDY (1 << 3) /* Receive data ready */ -#define I2C_STAT_ARDY (1 << 2) /* Register access ready */ -#define I2C_STAT_NACK (1 << 1) /* No acknowledgment interrupt enable */ -#define I2C_STAT_AL (1 << 0) /* Arbitration lost interrupt enable */ - -/* I2C Interrupt Vector Register (I2C_IV): */ - -/* I2C Interrupt Code Register (I2C_INTCODE): */ - -#define I2C_INTCODE_MASK 7 -#define I2C_INTCODE_NONE 0 -#define I2C_INTCODE_AL 1 /* Arbitration lost */ -#define I2C_INTCODE_NAK 2 /* No acknowledgement/general call */ -#define I2C_INTCODE_ARDY 3 /* Register access ready */ -#define I2C_INTCODE_RRDY 4 /* Rcv data ready */ -#define I2C_INTCODE_XRDY 5 /* Xmit data ready */ - -/* I2C Buffer Configuration Register (I2C_BUF): */ - -#define I2C_BUF_RDMA_EN (1 << 15) /* Receive DMA channel enable */ -#define I2C_BUF_XDMA_EN (1 << 7) /* Transmit DMA channel enable */ - -/* I2C Configuration Register (I2C_CON): */ - -#define I2C_CON_EN (1 << 15) /* I2C module enable */ -#define I2C_CON_BE (1 << 14) /* Big endian mode */ -#define I2C_CON_STB (1 << 11) /* Start byte mode (master mode only) */ -#define I2C_CON_MST (1 << 10) /* Master/slave mode */ -#define I2C_CON_TRX (1 << 9) /* Transmitter/receiver mode (master mode only) */ -#define I2C_CON_XA (1 << 8) /* Expand address */ -#define I2C_CON_RM (1 << 2) /* Repeat mode (master mode only) */ -#define I2C_CON_STP (1 << 1) /* Stop condition (master mode only) */ -#define I2C_CON_STT (1 << 0) /* Start condition (master mode only) */ - -/* I2C System Test Register (I2C_SYSTEST): */ - -#define I2C_SYSTEST_ST_EN (1 << 15) /* System test enable */ -#define I2C_SYSTEST_FREE (1 << 14) /* Free running mode (on breakpoint) */ -#define I2C_SYSTEST_TMODE_MASK (3 << 12) /* Test mode select */ -#define I2C_SYSTEST_TMODE_SHIFT (12) /* Test mode select */ -#define I2C_SYSTEST_SCL_I (1 << 3) /* SCL line sense input value */ -#define I2C_SYSTEST_SCL_O (1 << 2) /* SCL line drive output value */ -#define I2C_SYSTEST_SDA_I (1 << 1) /* SDA line sense input value */ -#define I2C_SYSTEST_SDA_O (1 << 0) /* SDA line drive output value */ - -/* - * MMC/SD Host Controller Registers - */ - -#define OMAP_MMC_CMD 0xFFFB7800 /* MMC Command */ -#define OMAP_MMC_ARGL 0xFFFB7804 /* MMC argument low */ -#define OMAP_MMC_ARGH 0xFFFB7808 /* MMC argument high */ -#define OMAP_MMC_CON 0xFFFB780C /* MMC system configuration */ -#define OMAP_MMC_STAT 0xFFFB7810 /* MMC status */ -#define OMAP_MMC_IE 0xFFFB7814 /* MMC system interrupt enable */ -#define OMAP_MMC_CTO 0xFFFB7818 /* MMC command time-out */ -#define OMAP_MMC_DTO 0xFFFB781C /* MMC data time-out */ -#define OMAP_MMC_DATA 0xFFFB7820 /* MMC TX/RX FIFO data */ -#define OMAP_MMC_BLEN 0xFFFB7824 /* MMC block length */ -#define OMAP_MMC_NBLK 0xFFFB7828 /* MMC number of blocks */ -#define OMAP_MMC_BUF 0xFFFB782C /* MMC buffer configuration */ -#define OMAP_MMC_SPI 0xFFFB7830 /* MMC serial port interface */ -#define OMAP_MMC_SDIO 0xFFFB7834 /* MMC SDIO mode configuration */ -#define OMAP_MMC_SYST 0xFFFB7838 /* MMC system test */ -#define OMAP_MMC_REV 0xFFFB783C /* MMC module version */ -#define OMAP_MMC_RSP0 0xFFFB7840 /* MMC command response 0 */ -#define OMAP_MMC_RSP1 0xFFFB7844 /* MMC command response 1 */ -#define OMAP_MMC_RSP2 0xFFFB7848 /* MMC command response 2 */ -#define OMAP_MMC_RSP3 0xFFFB784C /* MMC command response 3 */ -#define OMAP_MMC_RSP4 0xFFFB7850 /* MMC command response 4 */ -#define OMAP_MMC_RSP5 0xFFFB7854 /* MMC command response 5 */ -#define OMAP_MMC_RSP6 0xFFFB7858 /* MMC command response 6 */ -#define OMAP_MMC_RSP7 0xFFFB785C /* MMC command response 4 */ - -/* MMC masks */ - -#define OMAP_MMC_END_OF_CMD (1 << 0) /* End of command phase */ -#define OMAP_MMC_CARD_BUSY (1 << 2) /* Card enter busy state */ -#define OMAP_MMC_BLOCK_RS (1 << 3) /* Block received/sent */ -#define OMAP_MMC_EOF_BUSY (1 << 4) /* Card exit busy state */ -#define OMAP_MMC_DATA_TIMEOUT (1 << 5) /* Data response time-out */ -#define OMAP_MMC_DATA_CRC (1 << 6) /* Date CRC error */ -#define OMAP_MMC_CMD_TIMEOUT (1 << 7) /* Command response time-out */ -#define OMAP_MMC_CMD_CRC (1 << 8) /* Command CRC error */ -#define OMAP_MMC_A_FULL (1 << 10) /* Buffer almost full */ -#define OMAP_MMC_A_EMPTY (1 << 11) /* Buffer almost empty */ -#define OMAP_MMC_OCR_BUSY (1 << 12) /* OCR busy */ -#define OMAP_MMC_CARD_IRQ (1 << 13) /* Card IRQ received */ -#define OMAP_MMC_CARD_ERR (1 << 14) /* Card status error in response */ - -/* 2.9.2 MPUI Interface Registers FFFE:C900 */ - -#define MPUI_CTRL_REG (volatile __u32 *)(0xfffec900) -#define MPUI_DEBUG_ADDR (volatile __u32 *)(0xfffec904) -#define MPUI_DEBUG_DATA (volatile __u32 *)(0xfffec908) -#define MPUI_DEBUG_FLAG (volatile __u16 *)(0xfffec90c) -#define MPUI_STATUS_REG (volatile __u16 *)(0xfffec910) -#define MPUI_DSP_STATUS_REG (volatile __u16 *)(0xfffec914) -#define MPUI_DSP_BOOT_CONFIG (volatile __u16 *)(0xfffec918) -#define MPUI_DSP_API_CONFIG (volatile __u16 *)(0xfffec91c) - -/* 2.9.6 Traffic Controller Memory Interface Registers: */ -#define OMAP_IMIF_PRIO_REG 0xfffecc00 -#define OMAP_EMIFS_PRIO_REG 0xfffecc04 -#define OMAP_EMIFF_PRIO_REG 0xfffecc08 -#define OMAP_EMIFS_CONFIG_REG 0xfffecc0c -#define OMAP_EMIFS_CS0_CONFIG 0xfffecc10 -#define OMAP_EMIFS_CS1_CONFIG 0xfffecc14 -#define OMAP_EMIFS_CS2_CONFIG 0xfffecc18 -#define OMAP_EMIFS_CS3_CONFIG 0xfffecc1c -#define OMAP_EMIFF_SDRAM_CONFIG 0xfffecc20 -#define OMAP_EMIFF_MRS 0xfffecc24 -#define OMAP_TIMEOUT1 0xfffecc28 -#define OMAP_TIMEOUT2 0xfffecc2c -#define OMAP_TIMEOUT3 0xfffecc30 -#define OMAP_ENDIANISM 0xfffecc34 - -/* 2.9.10 EMIF Slow Interface Configuration Register (EMIFS_CONFIG_REG): */ -#define OMAP_EMIFS_CONFIG_FR (1 << 4) -#define OMAP_EMIFS_CONFIG_PDE (1 << 3) -#define OMAP_EMIFS_CONFIG_PWD_EN (1 << 2) -#define OMAP_EMIFS_CONFIG_BM (1 << 1) -#define OMAP_EMIFS_CONFIG_WP (1 << 0) - -/* - * Memory chunk set aside for the Framebuffer in SRAM - */ -#define SRAM_FRAMEBUFFER_MEMORY OMAP1510_SRAM_BASE - - -/* - * DMA - */ - -#define OMAP1510_DMA_BASE 0xFFFED800 -#define OMAP_DMA_BASE OMAP1510_DMA_BASE - -/* Global Register selection */ -#define NO_GLOBAL_DMA_ACCESS 0 - -/* Channel select field - * NOTE: all other channels are linear, chan0 is 0, chan1 is 1, etc... - */ -#define LCD_CHANNEL 0xc - -/* Register Select Field (LCD) */ -#define DMA_LCD_CTRL 0 -#define DMA_LCD_TOP_F1_L 1 -#define DMA_LCD_TOP_F1_U 2 -#define DMA_LCD_BOT_F1_L 3 -#define DMA_LCD_BOT_F1_U 4 - -#define LCD_FRAME_MODE (1<<0) -#define LCD_FRAME_IT_IE (1<<1) -#define LCD_BUS_ERROR_IT_IE (1<<2) -#define LCD_FRAME_1_IT_COND (1<<3) -#define LCD_FRAME_2_IT_COND (1<<4) -#define LCD_BUS_ERROR_IT_COND (1<<5) -#define LCD_SOURCE_IMIF (1<<6) - -/* - * Real-Time Clock - */ - -#define RTC_SECONDS (volatile __u8 *)(OMAP1510_RTC_BASE + 0x00) -#define RTC_MINUTES (volatile __u8 *)(OMAP1510_RTC_BASE + 0x04) -#define RTC_HOURS (volatile __u8 *)(OMAP1510_RTC_BASE + 0x08) -#define RTC_DAYS (volatile __u8 *)(OMAP1510_RTC_BASE + 0x0C) -#define RTC_MONTHS (volatile __u8 *)(OMAP1510_RTC_BASE + 0x10) -#define RTC_YEARS (volatile __u8 *)(OMAP1510_RTC_BASE + 0x14) -#define RTC_CTRL (volatile __u8 *)(OMAP1510_RTC_BASE + 0x40) - - -/* --------------------------------------------------------------------------- - * OMAP1510 Interrupt Handlers - * --------------------------------------------------------------------------- - * - */ -#define OMAP_IH1_BASE 0xfffecb00 -#define OMAP_IH2_BASE 0xfffe0000 -#define OMAP1510_ITR 0x0 -#define OMAP1510_MASK 0x4 - -#define INTERRUPT_HANDLER_BASE OMAP_IH1_BASE -#define INTERRUPT_INPUT_REGISTER OMAP1510_ITR -#define INTERRUPT_MASK_REGISTER OMAP1510_MASK - - -/* --------------------------------------------------------------------------- - * OMAP1510 TIMERS - * --------------------------------------------------------------------------- - * - */ - -#define OMAP1510_32kHz_TIMER_BASE 0xfffb9000 - -/* 32k Timer Registers */ -#define TIMER32k_CR 0x08 -#define TIMER32k_TVR 0x00 -#define TIMER32k_TCR 0x04 - -/* 32k Timer Control Register definition */ -#define TIMER32k_TSS (1<<0) -#define TIMER32k_TRB (1<<1) -#define TIMER32k_INT (1<<2) -#define TIMER32k_ARL (1<<3) - -/* MPU Timer base addresses */ -#define OMAP1510_MPUTIMER_BASE 0xfffec500 -#define OMAP1510_MPUTIMER_OFF 0x00000100 - -#define OMAP1510_TIMER1_BASE 0xfffec500 -#define OMAP1510_TIMER2_BASE 0xfffec600 -#define OMAP1510_TIMER3_BASE 0xfffec700 - -/* MPU Timer Registers */ -#define CNTL_TIMER 0 -#define LOAD_TIM 4 -#define READ_TIM 8 - -/* CNTL_TIMER register bits */ -#define MPUTIM_FREE (1<<6) -#define MPUTIM_CLOCK_ENABLE (1<<5) -#define MPUTIM_PTV_MASK (0x7<<MPUTIM_PTV_BIT) -#define MPUTIM_PTV_BIT 2 -#define MPUTIM_AR (1<<1) -#define MPUTIM_ST (1<<0) - -/* --------------------------------------------------------------------------- - * OMAP1510 GPIO (SHARED) - * --------------------------------------------------------------------------- - * - */ -#define GPIO_DATA_INPUT_REG (OMAP1510_GPIO_BASE + 0x0) -#define GPIO_DATA_OUTPUT_REG (OMAP1510_GPIO_BASE + 0x4) -#define GPIO_DIR_CONTROL_REG (OMAP1510_GPIO_BASE + 0x8) -#define GPIO_INT_CONTROL_REG (OMAP1510_GPIO_BASE + 0xc) -#define GPIO_INT_MASK_REG (OMAP1510_GPIO_BASE + 0x10) -#define GPIO_INT_STATUS_REG (OMAP1510_GPIO_BASE + 0x14) -#define GPIO_PIN_CONTROL_REG (OMAP1510_GPIO_BASE + 0x18) - - -/* --------------------------- - * OMAP1510 MPUIO (ARM only) - *---------------------------- - */ -#define OMAP1510_MPUIO_BASE 0xFFFB5000 -#define MPUIO_DATA_INPUT_REG (OMAP1510_MPUIO_BASE + 0x0) -#define MPUIO_DATA_OUTPUT_REG (OMAP1510_MPUIO_BASE + 0x4) -#define MPUIO_DIR_CONTROL_REG (OMAP1510_MPUIO_BASE + 0x8) - -/* --------------------------------------------------------------------------- - * OMAP1510 TIPB (only) - * --------------------------------------------------------------------------- - * - */ -#define TIPB_PUBLIC_CNTL_BASE 0xfffed300 -#define MPU_PUBLIC_TIPB_CNTL_REG (TIPB_PUBLIC_CNTL_BASE + 0x8) -#define TIPB_PRIVATE_CNTL_BASE 0xfffeca00 -#define MPU_PRIVATE_TIPB_CNTL_REG (TIPB_PRIVATE_CNTL_BASE + 0x8) - -/* - * --------------------------------------------------------------------------- - * OMAP1510 Camera Interface - * --------------------------------------------------------------------------- - */ -#define CAMERA_BASE (IO_BASE + 0x6800) -#define CAM_CTRLCLOCK_REG (CAMERA_BASE + 0x00) -#define CAM_IT_STATUS_REG (CAMERA_BASE + 0x04) -#define CAM_MODE_REG (CAMERA_BASE + 0x08) -#define CAM_STATUS_REG (CAMERA_BASE + 0x0C) -#define CAM_CAMDATA_REG (CAMERA_BASE + 0x10) -#define CAM_GPIO_REG (CAMERA_BASE + 0x14) -#define CAM_PEAK_CTR_REG (CAMERA_BASE + 0x18) - -#if 0 -#ifndef __ASSEMBLY__ -typedef struct { - __u32 ctrlclock; - __u32 it_status; - __u32 mode; - __u32 status; - __u32 camdata; - __u32 gpio; - __u32 peak_counter; -} camera_regs_t; -#endif -#endif - -/* CTRLCLOCK bit shifts */ -#define FOSCMOD_BIT 0 -#define FOSCMOD_MASK (0x7 << FOSCMOD_BIT) -#define FOSCMOD_12MHz 0x0 -#define FOSCMOD_6MHz 0x2 -#define FOSCMOD_9_6MHz 0x4 -#define FOSCMOD_24MHz 0x5 -#define FOSCMOD_8MHz 0x6 -#define POLCLK (1<<3) -#define CAMEXCLK_EN (1<<4) -#define MCLK_EN (1<<5) -#define DPLL_EN (1<<6) -#define LCLK_EN (1<<7) - -/* IT_STATUS bit shifts */ -#define V_UP (1<<0) -#define V_DOWN (1<<1) -#define H_UP (1<<2) -#define H_DOWN (1<<3) -#define FIFO_FULL (1<<4) -#define DATA_XFER (1<<5) - -/* MODE bit shifts */ -#define CAMOSC (1<<0) -#define IMGSIZE_BIT 1 -#define IMGSIZE_MASK (0x3 << IMGSIZE_BIT) -#define IMGSIZE_CIF (0x0 << IMGSIZE_BIT) /* 352x288 */ -#define IMGSIZE_QCIF (0x1 << IMGSIZE_BIT) /* 176x144 */ -#define IMGSIZE_VGA (0x2 << IMGSIZE_BIT) /* 640x480 */ -#define IMGSIZE_QVGA (0x3 << IMGSIZE_BIT) /* 320x240 */ -#define ORDERCAMD (1<<3) -#define EN_V_UP (1<<4) -#define EN_V_DOWN (1<<5) -#define EN_H_UP (1<<6) -#define EN_H_DOWN (1<<7) -#define EN_DMA (1<<8) -#define THRESHOLD (1<<9) -#define THRESHOLD_BIT 9 -#define THRESHOLD_MASK (0x7f<<9) -#define EN_NIRQ (1<<16) -#define EN_FIFO_FULL (1<<17) -#define RAZ_FIFO (1<<18) - -/* STATUS bit shifts */ -#define VSTATUS (1<<0) -#define HSTATUS (1<<1) - -/* GPIO bit shifts */ -#define CAM_RST (1<<0) - - -/********************* - * Watchdog timer. - *********************/ -#define WDTIM_BASE 0xfffec800 -#define WDTIM_CONTROL (WDTIM_BASE+0x00) -#define WDTIM_LOAD (WDTIM_BASE+0x04) -#define WDTIM_READ (WDTIM_BASE+0x04) -#define WDTIM_MODE (WDTIM_BASE+0x08) - -/* Values to write to mode register to disable the watchdog function. */ -#define DISABLE_SEQ1 0xF5 -#define DISABLE_SEQ2 0xA0 - -/* WDTIM_CONTROL bit definitions. */ -#define WDTIM_CONTROL_ST BIT7 - - -/* --------------------------------------------------------------------------- - * Differentiating processor versions for those who care. - * --------------------------------------------------------------------------- - * - */ -#define OMAP1509 0 -#define OMAP1510 1 - -#define OMAP1510_ID_CODE_REG 0xfffed404 - -#ifndef __ASSEMBLY__ -int cpu_type(void); -#endif - -/* - * EVM Implementation Specifics. - * - * *** NOTE *** - * Any definitions in these files should be prefixed by an identifier - - * eg. OMAP1510P1_FLASH0_BASE . - * - */ -#ifdef CONFIG_OMAP_INNOVATOR -#include "innovator.h" -#endif - -#ifdef CONFIG_OMAP_1510P1 -#include "omap1510p1.h" -#endif - -/*****************************************************************************/ - -#define CLKGEN_RESET_BASE (0xfffece00) -#define ARM_CKCTL (volatile __u16 *)(CLKGEN_RESET_BASE + 0x0) -#define ARM_IDLECT1 (volatile __u16 *)(CLKGEN_RESET_BASE + 0x4) -#define ARM_IDLECT2 (volatile __u16 *)(CLKGEN_RESET_BASE + 0x8) -#define ARM_EWUPCT (volatile __u16 *)(CLKGEN_RESET_BASE + 0xC) -#define ARM_RSTCT1 (volatile __u16 *)(CLKGEN_RESET_BASE + 0x10) -#define ARM_RSTCT2 (volatile __u16 *)(CLKGEN_RESET_BASE + 0x14) -#define ARM_SYSST (volatile __u16 *)(CLKGEN_RESET_BASE + 0x18) - - -#define CK_CLKIN 12 /* MHz */ -#define CK_RATEF 1 -#define CK_IDLEF 2 -#define CK_ENABLEF 4 -#define CK_SELECTF 8 -#ifndef __ASSEMBLER__ -#define CK_DPLL1 ((volatile __u16 *)0xfffecf00) -#else -#define CK_DPLL1 (0xfffecf00) -#endif -#define SETARM_IDLE_SHIFT - -/* ARM_CKCTL bit shifts */ -#define PERDIV 0 -#define LCDDIV 2 -#define ARMDIV 4 -#define DSPDIV 6 -#define TCDIV 8 -#define DSPMMUDIV 10 -#define ARM_TIMXO 12 -#define EN_DSPCK 13 -#define ARM_INTHCK_SEL 14 /* REVISIT -- where is this used? */ - -#define ARM_CKCTL_RSRVD_BIT15 (1 << 15) -#define ARM_CKCTL_ARM_INTHCK_SEL (1 << 14) -#define ARM_CKCTL_EN_DSPCK (1 << 13) -#define ARM_CKCTL_ARM_TIMXO (1 << 12) -#define ARM_CKCTL_DSPMMU_DIV1 (1 << 11) -#define ARM_CKCTL_DSPMMU_DIV2 (1 << 10) -#define ARM_CKCTL_TCDIV1 (1 << 9) -#define ARM_CKCTL_TCDIV2 (1 << 8) -#define ARM_CKCTL_DSPDIV1 (1 << 7) -#define ARM_CKCTL_DSPDIV0 (1 << 6) -#define ARM_CKCTL_ARMDIV1 (1 << 5) -#define ARM_CKCTL_ARMDIV0 (1 << 4) -#define ARM_CKCTL_LCDDIV1 (1 << 3) -#define ARM_CKCTL_LCDDIV0 (1 << 2) -#define ARM_CKCTL_PERDIV1 (1 << 1) -#define ARM_CKCTL_PERDIV0 (1 << 0) - -/* ARM_IDLECT1 bit shifts */ -#define IDLWDT_ARM 0 -#define IDLXORP_ARM 1 -#define IDLPER_ARM 2 -#define IDLLCD_ARM 3 -#define IDLLB_ARM 4 -#define IDLHSAB_ARM 5 -#define IDLIF_ARM 6 -#define IDLDPLL_ARM 7 -#define IDLAPI_ARM 8 -#define IDLTIM_ARM 9 -#define SETARM_IDLE 11 - -/* ARM_IDLECT2 bit shifts */ -#define EN_WDTCK 0 -#define EN_XORPCK 1 -#define EN_PERCK 2 -#define EN_LCDCK 3 -#define EN_LBCK 4 -#define EN_HSABCK 5 -#define EN_APICK 6 -#define EN_TIMCK 7 -#define DMACK_REQ 8 -#define EN_GPIOCK 9 -#define EN_LBFREECK 10 - -#define ARM_RSTCT1_SW_RST (1 << 3) -#define ARM_RSTCT1_DSP_RST (1 << 2) -#define ARM_RSTCT1_DSP_EN (1 << 1) -#define ARM_RSTCT1_ARM_RST (1 << 0) - -/* ARM_RSTCT2 bit shifts */ -#define EN_PER 0 - -#define ARM_SYSST_RSRVD_BIT15 (1 << 15) -#define ARM_SYSST_RSRVD_BIT14 (1 << 14) -#define ARM_SYSST_CLOCK_SELECT2 (1 << 13) -#define ARM_SYSST_CLOCK_SELECT1 (1 << 12) -#define ARM_SYSST_CLOCK_SELECT0 (1 << 11) -#define ARM_SYSST_RSRVD_BIT10 (1 << 10) -#define ARM_SYSST_RSRVD_BIT9 (1 << 9) -#define ARM_SYSST_RSRVD_BIT8 (1 << 8) -#define ARM_SYSST_RSRVD_BIT7 (1 << 7) -#define ARM_SYSST_IDLE_DSP (1 << 6) -#define ARM_SYSST_POR (1 << 5) -#define ARM_SYSST_EXT_RST (1 << 4) -#define ARM_SYSST_ARM_MCRST (1 << 3) -#define ARM_SYSST_ARM_WDRST (1 << 2) -#define ARM_SYSST_GLOB_SWRST (1 << 1) -#define ARM_SYSST_DSP_WDRST (1 << 0) - -/* Table 15-23. DPLL Control Registers: */ -#define DPLL_CTL_REG (volatile __u16 *)(0xfffecf00) - -/* Table 15-24. Control Register (CTL_REG): */ - -#define DPLL_CTL_REG_IOB (1 << 13) -#define DPLL_CTL_REG_PLL_MULT Fld(5,0) - -/*****************************************************************************/ - -/* OMAP INTERRUPT REGISTERS */ -#define IRQ_ITR 0x00 -#define IRQ_MIR 0x04 -#define IRQ_SIR_IRQ 0x10 -#define IRQ_SIR_FIQ 0x14 -#define IRQ_CONTROL_REG 0x18 -#define IRQ_ISR 0x9c -#define IRQ_ILR0 0x1c - -#define REG_IHL1_MIR (OMAP_IH1_BASE+IRQ_MIR) -#define REG_IHL2_MIR (OMAP_IH2_BASE+IRQ_MIR) - -/* INTERRUPT LEVEL REGISTER BITS */ -#define ILR_PRIORITY_MASK (0x3c) -#define ILR_PRIORITY_SHIFT (2) -#define ILR_LEVEL_TRIGGER (1<<1) -#define ILR_FIQ (1<<0) - -#define IRQ_LEVEL_INT 1 -#define IRQ_EDGE_INT 0 - -/* Macros to access registers */ -#define outb(v,p) *(volatile u8 *) (p) = v -#define outw(v,p) *(volatile u16 *) (p) = v -#define outl(v,p) *(volatile u32 *) (p) = v - -#define inb(p) *(volatile u8 *) (p) -#define inw(p) *(volatile u16 *) (p) -#define inl(p) *(volatile u32 *) (p) diff --git a/include/configs/omap5912osk.h b/include/configs/omap5912osk.h deleted file mode 100644 index 376dfdb..0000000 --- a/include/configs/omap5912osk.h +++ /dev/null @@ -1,174 +0,0 @@ -/* - * (C) Copyright 2003 - * Texas Instruments. - * Kshitij Gupta <kshitij@ti.com> - * Configuation settings for the TI OMAP Innovator board. - * - * SPDX-License-Identifier: GPL-2.0+ - */ - -#ifndef __CONFIG_H -#define __CONFIG_H - -/* - * High Level Configuration Options - * (easy to change) - */ -#define CONFIG_ARM926EJS 1 /* This is an arm926ejs CPU core */ -#define CONFIG_OMAP 1 /* in a TI OMAP core */ -#define CONFIG_OMAP1610 1 /* 5912 is same as 1610 */ -#define CONFIG_OSK_OMAP5912 1 /* a OSK Board */ - -#define CONFIG_DISPLAY_CPUINFO 1 /* display cpu info (and speed) */ -#define CONFIG_DISPLAY_BOARDINFO 1 /* display board info */ - -/* input clock of PLL */ -/* the OMAP5912 OSK has 12MHz input clock */ -#define CONFIG_SYS_CLK_FREQ 12000000 - -#define CONFIG_CMDLINE_TAG 1 /* enable passing of ATAGs */ -#define CONFIG_SETUP_MEMORY_TAGS 1 -#define CONFIG_INITRD_TAG 1 /* Required for ramdisk support */ - -/* - * Size of malloc() pool - */ -#define CONFIG_SYS_MALLOC_LEN (CONFIG_ENV_SIZE + 128*1024) - -/* - * Hardware drivers - */ -/* -*/ -#define CONFIG_LAN91C96 -#define CONFIG_LAN91C96_BASE 0x04800300 -#define CONFIG_LAN91C96_EXT_PHY - -/* - * NS16550 Configuration - */ -#define CONFIG_SYS_NS16550 -#define CONFIG_SYS_NS16550_SERIAL -#define CONFIG_SYS_NS16550_REG_SIZE (-4) -#define CONFIG_SYS_NS16550_CLK (48000000) /* can be 12M/32Khz or 48Mhz */ -#define CONFIG_SYS_NS16550_COM1 0xfffb0000 /* uart1, bluetooth uart - on helen */ - -/* - * select serial console configuration - */ -#define CONFIG_SERIAL1 1 /* we use SERIAL 1 on OMAP5912 OSK */ - -/* allow to overwrite serial and ethaddr */ -#define CONFIG_ENV_OVERWRITE -#define CONFIG_CONS_INDEX 1 -#define CONFIG_BAUDRATE 115200 - -/* - * Command line configuration. - */ -#include <config_cmd_default.h> - -#define CONFIG_CMD_DHCP - - -/* - * BOOTP options - */ -#define CONFIG_BOOTP_SUBNETMASK -#define CONFIG_BOOTP_GATEWAY -#define CONFIG_BOOTP_HOSTNAME -#define CONFIG_BOOTP_BOOTPATH - - -#include <configs/omap1510.h> - -#define CONFIG_BOOTDELAY 3 -#define CONFIG_BOOTARGS "mem=32M console=ttyS0,115200n8 noinitrd \ - root=/dev/nfs rw nfsroot=157.87.82.48:\ - /home/mwd/myfs/target ip=dhcp" -#define CONFIG_NETMASK 255.255.254.0 /* talk on MY local net */ -#define CONFIG_IPADDR 156.117.97.156 /* static IP I currently own */ -#define CONFIG_SERVERIP 156.117.97.139 /* current IP of my dev pc */ -#define CONFIG_BOOTFILE "uImage" /* file to load */ - -#if defined(CONFIG_CMD_KGDB) -#define CONFIG_KGDB_BAUDRATE 115200 /* speed to run kgdb serial port */ -#endif - -/* - * Miscellaneous configurable options - */ -#define CONFIG_SYS_LONGHELP /* undef to save memory */ -#define CONFIG_SYS_PROMPT "OMAP5912 OSK # " /* Monitor Command Prompt */ -#define CONFIG_SYS_CBSIZE 256 /* Console I/O Buffer Size */ -/* 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 0x10000000 /* memtest works on */ -#define CONFIG_SYS_MEMTEST_END 0x12000000 /* 32 MB in DRAM */ - -#define CONFIG_SYS_LOAD_ADDR 0x10000000 /* default load address */ - -/* The 1610 has 6 timers, they can be driven by the RefClk (12Mhz) or by - * DPLL1. This time is further subdivided by a local divisor. - */ -#define CONFIG_SYS_TIMERBASE 0xFFFEC500 /* use timer 1 */ -#define CONFIG_SYS_PTV 7 /* 2^(PTV+1), divide by 256 */ - -/*----------------------------------------------------------------------- - * Physical Memory Map - */ -#define CONFIG_NR_DRAM_BANKS 1 /* we have 1 bank of DRAM */ -#define PHYS_SDRAM_1 0x10000000 /* SDRAM Bank #1 */ -#define PHYS_SDRAM_1_SIZE 0x02000000 /* 32 MB */ - -#define PHYS_FLASH_1 0x00000000 /* Flash Bank #1 */ -#define PHYS_FLASH_2 0x01000000 /* Flash Bank #2 */ - -#define CONFIG_SYS_FLASH_BASE PHYS_FLASH_1 - -#define CONFIG_SYS_MONITOR_BASE CONFIG_SYS_FLASH_BASE /* Monitor at beginning of flash */ - -#define PHYS_SRAM 0x20000000 - -/*----------------------------------------------------------------------- - * FLASH driver setup - */ -#define CONFIG_SYS_FLASH_CFI 1 /* Flash memory is CFI compliant */ -#define CONFIG_FLASH_CFI_DRIVER 1 /* Use drivers/mtd/cfi_flash.c */ - -#define CONFIG_SYS_FLASH_BANKS_LIST { PHYS_FLASH_1, PHYS_FLASH_2 } - -#define CONFIG_SYS_MAX_FLASH_BANKS 2 /* max number of memory banks */ -#define PHYS_FLASH_SIZE 0x02000000 /* 32MB */ -#define CONFIG_SYS_MAX_FLASH_SECT (259) /* max number of sectors on one chip */ - -#define CONFIG_SYS_FLASH_USE_BUFFER_WRITE 1 /* Use buffered writes (~10x faster) */ -#define CONFIG_SYS_FLASH_PROTECTION 1 /* Use hardware sector protection */ - -#define CONFIG_SYS_FLASH_EMPTY_INFO /* print 'E' for empty sector on flinfo */ - -/* timeout values are in ticks */ -#define CONFIG_SYS_FLASH_ERASE_TOUT (20*CONFIG_SYS_HZ) /* Timeout for Flash Erase */ -#define CONFIG_SYS_FLASH_WRITE_TOUT (20*CONFIG_SYS_HZ) /* Timeout for Flash Write */ - -/*----------------------------------------------------------------------- - * FLASH and environment organization - */ -#define CONFIG_ENV_IS_IN_FLASH 1 -/* addr of environment */ -#define CONFIG_ENV_ADDR (CONFIG_SYS_FLASH_BASE + CONFIG_ENV_OFFSET) - -#define CONFIG_ENV_SIZE 0x20000 /* Total Size of Environment Sector */ -#define CONFIG_ENV_OFFSET 0x40000 /* environment starts here */ - -#define CONFIG_SYS_SDRAM_BASE PHYS_SDRAM_1 -#define CONFIG_SYS_INIT_RAM_ADDR PHYS_SRAM -#define CONFIG_SYS_INIT_RAM_SIZE (250 * 1024) -#define CONFIG_SYS_INIT_SP_ADDR (CONFIG_SYS_INIT_RAM_ADDR + \ - CONFIG_SYS_INIT_RAM_SIZE) - -#endif /* __CONFIG_H */ diff --git a/include/configs/openrd.h b/include/configs/openrd.h index 3eb408f..b65bdfd 100644 --- a/include/configs/openrd.h +++ b/include/configs/openrd.h @@ -77,6 +77,11 @@ #define CONFIG_ENV_SIZE 0x20000 /* 128k */ #define CONFIG_ENV_ADDR 0x60000 #define CONFIG_ENV_OFFSET 0x60000 /* env starts here */ +/* + * Environment is right behind U-Boot in flash. Make sure U-Boot + * doesn't grow into the environment area. + */ +#define CONFIG_BOARD_SIZE_LIMIT CONFIG_ENV_OFFSET /* * Default environment variables diff --git a/include/configs/rpi_b.h b/include/configs/rpi_b.h index 60f2489..2d69809 100644 --- a/include/configs/rpi_b.h +++ b/include/configs/rpi_b.h @@ -101,6 +101,38 @@ "env import -t -r ${loadaddr} ${filesize}; " \ "fi" +/* Shell */ +#define CONFIG_SYS_MAXARGS 8 +#define CONFIG_SYS_PROMPT "U-Boot> " +#define CONFIG_COMMAND_HISTORY + +/* Commands */ +#include <config_cmd_default.h> +#define CONFIG_CMD_GPIO +#define CONFIG_CMD_MMC +#define CONFIG_PARTITION_UUIDS +#define CONFIG_CMD_PART + +/* Device tree support */ +#define CONFIG_OF_BOARD_SETUP +/* ATAGs support for bootm/bootz */ +#define CONFIG_SETUP_MEMORY_TAGS +#define CONFIG_CMDLINE_TAG +#define CONFIG_INITRD_TAG + +#include <config_distro_defaults.h> + +/* Some things don't make sense on this HW or yet */ +#undef CONFIG_CMD_FPGA +#undef CONFIG_CMD_NET +#undef CONFIG_CMD_NFS +#undef CONFIG_CMD_SAVEENV +#undef CONFIG_CMD_DHCP +#undef CONFIG_CMD_MII +#undef CONFIG_CMD_NET +#undef CONFIG_CMD_PING + +/* Environment */ #define ENV_DEVICE_SETTINGS \ "stdin=serial,lcd\0" \ "stdout=serial,lcd\0" \ @@ -138,104 +170,15 @@ "fdtfile=bcm2835-rpi-b.dtb\0" \ "ramdisk_addr_r=0x02100000\0" \ -#define BOOTCMDS_MMC \ - "mmc_boot=" \ - "setenv devtype mmc; " \ - "if mmc dev ${devnum}; then " \ - "run scan_boot; " \ - "fi\0" \ - "bootcmd_mmc0=setenv devnum 0; run mmc_boot;\0" -#define BOOT_TARGETS_MMC "mmc0" - -#define BOOTCMDS_COMMON \ - "rootpart=1\0" \ - \ - "do_script_boot=" \ - "load ${devtype} ${devnum}:${rootpart} " \ - "${scriptaddr} ${prefix}${script}; " \ - "source ${scriptaddr}\0" \ - \ - "script_boot=" \ - "for script in ${boot_scripts}; do " \ - "if test -e ${devtype} ${devnum}:${rootpart} " \ - "${prefix}${script}; then " \ - "echo Found ${prefix}${script}; " \ - "run do_script_boot; " \ - "echo SCRIPT FAILED: continuing...; " \ - "fi; " \ - "done\0" \ - \ - "do_sysboot_boot=" \ - "sysboot ${devtype} ${devnum}:${rootpart} any " \ - "${scriptaddr} ${prefix}extlinux/extlinux.conf\0" \ - \ - "sysboot_boot=" \ - "if test -e ${devtype} ${devnum}:${rootpart} " \ - "${prefix}extlinux/extlinux.conf; then " \ - "echo Found ${prefix}extlinux/extlinux.conf; " \ - "run do_sysboot_boot; " \ - "echo SCRIPT FAILED: continuing...; " \ - "fi\0" \ - \ - "scan_boot=" \ - "echo Scanning ${devtype} ${devnum}...; " \ - "for prefix in ${boot_prefixes}; do " \ - "run sysboot_boot; " \ - "run script_boot; " \ - "done\0" \ - \ - "boot_targets=" \ - BOOT_TARGETS_MMC " " \ - "\0" \ - \ - "boot_prefixes=/\0" \ - \ - "boot_scripts=boot.scr.uimg\0" \ - \ - BOOTCMDS_MMC - -#define CONFIG_BOOTCOMMAND \ - "for target in ${boot_targets}; do run bootcmd_${target}; done" - -#define CONFIG_BOOTCOMMAND \ - "for target in ${boot_targets}; do run bootcmd_${target}; done" +#define BOOT_TARGET_DEVICES(func) \ + func(MMC, mmc, 0) +#include <config_distro_bootcmd.h> #define CONFIG_EXTRA_ENV_SETTINGS \ ENV_DEVICE_SETTINGS \ ENV_MEM_LAYOUT_SETTINGS \ - BOOTCMDS_COMMON + BOOTENV #define CONFIG_BOOTDELAY 2 -/* Shell */ -#define CONFIG_SYS_MAXARGS 8 -#define CONFIG_SYS_PROMPT "U-Boot> " -#define CONFIG_COMMAND_HISTORY - -/* Commands */ -#include <config_cmd_default.h> -#define CONFIG_CMD_GPIO -#define CONFIG_CMD_MMC -#define CONFIG_PARTITION_UUIDS -#define CONFIG_CMD_PART - -/* Device tree support */ -#define CONFIG_OF_BOARD_SETUP -/* ATAGs support for bootm/bootz */ -#define CONFIG_SETUP_MEMORY_TAGS -#define CONFIG_CMDLINE_TAG -#define CONFIG_INITRD_TAG - -#include <config_distro_defaults.h> - -/* Some things don't make sense on this HW or yet */ -#undef CONFIG_CMD_FPGA -#undef CONFIG_CMD_NET -#undef CONFIG_CMD_NFS -#undef CONFIG_CMD_SAVEENV -#undef CONFIG_CMD_DHCP -#undef CONFIG_CMD_MII -#undef CONFIG_CMD_NET -#undef CONFIG_CMD_PING - #endif diff --git a/include/configs/s5p_goni.h b/include/configs/s5p_goni.h index 6e795bf..a51215d 100644 --- a/include/configs/s5p_goni.h +++ b/include/configs/s5p_goni.h @@ -91,6 +91,8 @@ #define CONFIG_G_DNL_PRODUCT_NUM 0x6601 #define CONFIG_G_DNL_THOR_VENDOR_NUM CONFIG_G_DNL_VENDOR_NUM #define CONFIG_G_DNL_THOR_PRODUCT_NUM 0x685D +#define CONFIG_G_DNL_UMS_VENDOR_NUM 0x0525 +#define CONFIG_G_DNL_UMS_PRODUCT_NUM 0xA4A5 #define CONFIG_G_DNL_MANUFACTURER "Samsung" /* Actual modem binary size is 16MiB. Add 2MiB for bad block handling */ diff --git a/include/configs/stxxtc.h b/include/configs/stxxtc.h deleted file mode 100644 index 4e3b727..0000000 --- a/include/configs/stxxtc.h +++ /dev/null @@ -1,485 +0,0 @@ -/* - * (C) Copyright 2000-2004 - * Wolfgang Denk, DENX Software Engineering, wd@denx.de. - * - * SPDX-License-Identifier: GPL-2.0+ - */ - -/* - * Dan Malek, Embedded Edge, LLC, dan@embeddededge.com - * U-Boot port on STx XTc 8xx board - * Mostly copied from Panto's NETTA2 board. - */ - -#ifndef __CONFIG_H -#define __CONFIG_H - -/* - * High Level Configuration Options - * (easy to change) - */ - -#define CONFIG_MPC875 1 /* This is a MPC875 CPU */ -#define CONFIG_STXXTC 1 /* ...on a STx XTc board */ - -#define CONFIG_SYS_TEXT_BASE 0x40F00000 - -#define CONFIG_8xx_CONS_SMC1 1 /* Console is on SMC1 */ -#undef CONFIG_8xx_CONS_SMC2 -#undef CONFIG_8xx_CONS_NONE - -#define CONFIG_BAUDRATE 115200 /* console baudrate = 115.2kbps */ - -#define CONFIG_XIN 10000000 /* 10 MHz input xtal */ - -/* Select one of few clock rates defined later in this file. -*/ -/* #define MPC8XX_HZ 50000000 */ -#define MPC8XX_HZ 66666666 - -#define CONFIG_8xx_GCLK_FREQ MPC8XX_HZ - -#if 0 -#define CONFIG_BOOTDELAY -1 /* autoboot disabled */ -#else -#define CONFIG_BOOTDELAY 5 /* autoboot after 5 seconds */ -#endif - -#undef CONFIG_CLOCKS_IN_MHZ /* clocks NOT passsed to Linux in MHz */ - -#undef CONFIG_BOOTARGS -#define CONFIG_BOOTCOMMAND \ - "tftpboot; " \ - "setenv bootargs root=/dev/nfs rw nfsroot=${serverip}:${rootpath} " \ - "ip=${ipaddr}:${serverip}:${gatewayip}:${netmask}:${hostname}::off; " \ - "bootm" - -#define CONFIG_SOURCE -#define CONFIG_LOADS_ECHO 0 /* echo off for serial download */ -#undef CONFIG_SYS_LOADS_BAUD_CHANGE /* don't allow baudrate change */ - -#undef CONFIG_WATCHDOG /* watchdog disabled */ - -#define CONFIG_STATUS_LED 1 /* Status LED enabled */ -#define CONFIG_BOARD_SPECIFIC_LED /* version has board specific leds */ - -/* - * BOOTP options - */ -#define CONFIG_BOOTP_SUBNETMASK -#define CONFIG_BOOTP_GATEWAY -#define CONFIG_BOOTP_HOSTNAME -#define CONFIG_BOOTP_BOOTPATH -#define CONFIG_BOOTP_BOOTFILESIZE -#define CONFIG_BOOTP_NISDOMAIN - - -#undef CONFIG_MAC_PARTITION -#undef CONFIG_DOS_PARTITION - -#define CONFIG_RTC_MPC8xx /* use internal RTC of MPC8xx */ - -#define FEC_ENET 1 /* eth.c needs it that way... */ -#undef CONFIG_SYS_DISCOVER_PHY -#define CONFIG_MII 1 -#define CONFIG_MII_INIT 1 -#undef CONFIG_RMII - -#define CONFIG_ETHER_ON_FEC1 1 -#define CONFIG_FEC1_PHY 1 /* phy address of FEC */ -#undef CONFIG_FEC1_PHY_NORXERR - -#define CONFIG_ETHER_ON_FEC2 1 -#define CONFIG_FEC2_PHY 3 -#undef CONFIG_FEC2_PHY_NORXERR - -#define CONFIG_ENV_OVERWRITE 1 /* allow modification of vendor params */ - - -/* - * Command line configuration. - */ -#include <config_cmd_default.h> - -#define CONFIG_CMD_DHCP -#define CONFIG_CMD_MII -#define CONFIG_CMD_NFS -#define CONFIG_CMD_PING - - -#define CONFIG_BOARD_EARLY_INIT_F 1 -#define CONFIG_MISC_INIT_R - -/* - * Miscellaneous configurable options - */ -#define CONFIG_SYS_LONGHELP /* undef to save memory */ -#define CONFIG_SYS_PROMPT "xtc> " /* Monitor Command Prompt */ - -#define CONFIG_SYS_HUSH_PARSER 1 - -#if defined(CONFIG_CMD_KGDB) -#define CONFIG_SYS_CBSIZE 1024 /* Console I/O Buffer Size */ -#else -#define CONFIG_SYS_CBSIZE 256 /* Console I/O Buffer Size */ -#endif -#define CONFIG_SYS_PBSIZE (CONFIG_SYS_CBSIZE+sizeof(CONFIG_SYS_PROMPT)+16) /* Print Buffer Size */ -#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 0x0300000 /* memtest works on */ -#define CONFIG_SYS_MEMTEST_END 0x0700000 /* 3 ... 7 MB in DRAM */ - -#define CONFIG_SYS_LOAD_ADDR 0x100000 /* default load address */ - -/* - * Low Level Configuration Settings - * (address mappings, register initial values, etc.) - * You should know what you are doing if you make changes here. - */ -/*----------------------------------------------------------------------- - * Internal Memory Mapped Register - */ -#define CONFIG_SYS_IMMR 0xFF000000 - -/*----------------------------------------------------------------------- - * Definitions for initial stack pointer and data area (in DPRAM) - */ -#define CONFIG_SYS_INIT_RAM_ADDR CONFIG_SYS_IMMR -#define CONFIG_SYS_INIT_RAM_SIZE 0x3000 /* Size of used area in DPRAM */ -#define CONFIG_SYS_GBL_DATA_OFFSET (CONFIG_SYS_INIT_RAM_SIZE - GENERATED_GBL_DATA_SIZE) -#define CONFIG_SYS_INIT_SP_OFFSET CONFIG_SYS_GBL_DATA_OFFSET - -/*----------------------------------------------------------------------- - * Start addresses for the final memory configuration - * (Set up by the startup code) - * Please note that CONFIG_SYS_SDRAM_BASE _must_ start at 0 - */ -#define CONFIG_SYS_SDRAM_BASE 0x00000000 -#define CONFIG_SYS_FLASH_BASE 0x40000000 -#if defined(DEBUG) -#define CONFIG_SYS_MONITOR_LEN (256 << 10) /* Reserve 256 kB for Monitor */ -#else -#define CONFIG_SYS_MONITOR_LEN (192 << 10) /* Reserve 192 kB for Monitor */ -#endif - -/* yes this is weird, I know :) */ -#define CONFIG_SYS_MONITOR_BASE (CONFIG_SYS_FLASH_BASE | 0x00F00000) -#define CONFIG_SYS_MALLOC_LEN (128 << 10) /* Reserve 128 kB for malloc() */ - -#define CONFIG_SYS_RESET_ADDRESS 0x80000000 - -/* - * For booting Linux, the board info and command line data - * have to be in the first 8 MB of memory, since this is - * the maximum mapped by the Linux kernel during initialization. - */ -#define CONFIG_SYS_BOOTMAPSZ (8 << 20) /* Initial Memory map for Linux */ - -/*----------------------------------------------------------------------- - * FLASH organization - */ -#define CONFIG_ENV_IS_IN_FLASH 1 -#define CONFIG_ENV_SECT_SIZE 0x10000 - -#define CONFIG_ENV_ADDR (CONFIG_SYS_FLASH_BASE + 0x00000000) -#define CONFIG_ENV_OFFSET 0 -#define CONFIG_ENV_SIZE 0x4000 - -#define CONFIG_ENV_ADDR_REDUND (CONFIG_SYS_FLASH_BASE + 0x00010000) -#define CONFIG_ENV_OFFSET_REDUND 0 -#define CONFIG_ENV_SIZE_REDUND CONFIG_ENV_SIZE - -#define CONFIG_SYS_FLASH_CFI 1 -#define CONFIG_FLASH_CFI_DRIVER 1 -#undef CONFIG_SYS_FLASH_USE_BUFFER_WRITE /* use buffered writes (20x faster) */ -#define CONFIG_SYS_MAX_FLASH_SECT 128 /* max number of sectors on one chip */ -#define CONFIG_SYS_MAX_FLASH_BANKS 2 /* max number of memory banks */ - -#define CONFIG_SYS_FLASH_BANKS_LIST { CONFIG_SYS_FLASH_BASE, CONFIG_SYS_FLASH_BASE + 0x2000000 } - -#define CONFIG_SYS_FLASH_PROTECTION - -/*----------------------------------------------------------------------- - * Cache Configuration - */ -#define CONFIG_SYS_CACHELINE_SIZE 16 /* For all MPC8xx CPUs */ -#if defined(CONFIG_CMD_KGDB) -#define CONFIG_SYS_CACHELINE_SHIFT 4 /* log base 2 of the above value */ -#endif - -/*----------------------------------------------------------------------- - * SYPCR - System Protection Control 11-9 - * SYPCR can only be written once after reset! - *----------------------------------------------------------------------- - * Software & Bus Monitor Timer max, Bus Monitor enable, SW Watchdog freeze - */ -#if defined(CONFIG_WATCHDOG) -#define CONFIG_SYS_SYPCR (SYPCR_SWTC | SYPCR_BMT | SYPCR_BME | SYPCR_SWF | \ - SYPCR_SWE | SYPCR_SWRI| SYPCR_SWP) -#else -#define CONFIG_SYS_SYPCR (SYPCR_SWTC | SYPCR_BMT | SYPCR_BME | SYPCR_SWF | SYPCR_SWP) -#endif - -/*----------------------------------------------------------------------- - * SIUMCR - SIU Module Configuration 11-6 - *----------------------------------------------------------------------- - * PCMCIA config., multi-function pin tri-state - */ -#define CONFIG_SYS_SIUMCR (SIUMCR_DBGC00 | SIUMCR_DBPC00 | SIUMCR_MLRC01 | SIUMCR_FRC | SIUMCR_GB5E) - -/*----------------------------------------------------------------------- - * TBSCR - Time Base Status and Control 11-26 - *----------------------------------------------------------------------- - * Clear Reference Interrupt Status, Timebase freezing enabled - */ -#define CONFIG_SYS_TBSCR (TBSCR_REFA | TBSCR_REFB | TBSCR_TBF) - -/*----------------------------------------------------------------------- - * RTCSC - Real-Time Clock Status and Control Register 11-27 - *----------------------------------------------------------------------- - */ -#define CONFIG_SYS_RTCSC (RTCSC_SEC | RTCSC_ALR | RTCSC_RTF| RTCSC_RTE) - -/*----------------------------------------------------------------------- - * PISCR - Periodic Interrupt Status and Control 11-31 - *----------------------------------------------------------------------- - * Clear Periodic Interrupt Status, Interrupt Timer freezing enabled - */ -#define CONFIG_SYS_PISCR (PISCR_PS | PISCR_PITF) - -/*----------------------------------------------------------------------- - * PLPRCR - PLL, Low-Power, and Reset Control Register 15-30 - *----------------------------------------------------------------------- - * Reset PLL lock status sticky bit, timer expired status bit and timer - * interrupt status bit - * - */ - -#if CONFIG_XIN == 10000000 - -#if MPC8XX_HZ == 50000000 -#define CONFIG_SYS_PLPRCR ((0 << PLPRCR_MFN_SHIFT) | (0 << PLPRCR_MFD_SHIFT) | \ - (1 << PLPRCR_S_SHIFT) | (10 << PLPRCR_MFI_SHIFT) | (0 << PLPRCR_PDF_SHIFT) | \ - PLPRCR_TEXPS) -#elif MPC8XX_HZ == 66666666 -#define CONFIG_SYS_PLPRCR ((1 << PLPRCR_MFN_SHIFT) | (2 << PLPRCR_MFD_SHIFT) | \ - (1 << PLPRCR_S_SHIFT) | (13 << PLPRCR_MFI_SHIFT) | (0 << PLPRCR_PDF_SHIFT) | \ - PLPRCR_TEXPS) -#else -#error unsupported CPU freq for XIN = 10MHz -#endif -#else -#error unsupported freq for XIN (must be 10MHz) -#endif - - -/* - *----------------------------------------------------------------------- - * SCCR - System Clock and reset Control Register 15-27 - *----------------------------------------------------------------------- - * Set clock output, timebase and RTC source and divider, - * power management and some other internal clocks - * - * Note: When TBS == 0 the timebase is independent of current cpu clock. - */ - -#define SCCR_MASK SCCR_EBDF11 -#if MPC8XX_HZ > 66666666 -#define CONFIG_SYS_SCCR (/* SCCR_TBS | */ SCCR_CRQEN | \ - SCCR_COM00 | SCCR_DFSYNC00 | SCCR_DFBRG00 | \ - SCCR_DFNL000 | SCCR_DFNH000 | SCCR_DFLCD000 | \ - SCCR_DFALCD00 | SCCR_EBDF01) -#else -#define CONFIG_SYS_SCCR (/* SCCR_TBS | */ SCCR_CRQEN | \ - SCCR_COM00 | SCCR_DFSYNC00 | SCCR_DFBRG00 | \ - SCCR_DFNL000 | SCCR_DFNH000 | SCCR_DFLCD000 | \ - SCCR_DFALCD00) -#endif - -/*----------------------------------------------------------------------- - * - *----------------------------------------------------------------------- - * - */ -/*#define CONFIG_SYS_DER 0x2002000F*/ -#define CONFIG_SYS_DER 0 - -/* - * Init Memory Controller: - * - * BR0/1 and OR0/1 (FLASH) - */ - -#define FLASH_BASE0_PRELIM 0x40000000 /* FLASH bank #0 */ -#define FLASH_BASE1_PRELIM 0x42000000 /* FLASH bank #1 */ - -/* used to re-map FLASH both when starting from SRAM or FLASH: - * restrict access enough to keep SRAM working (if any) - * but not too much to meddle with FLASH accesses - */ - -#define FLASH_BANK_MAX_SIZE 0x01000000 /* max size per chip */ - -#define CONFIG_SYS_REMAP_OR_AM 0x80000000 -#define CONFIG_SYS_PRELIM_OR_AM (0xFFFFFFFFLU & ~(FLASH_BANK_MAX_SIZE - 1)) - -/* FLASH timing: ACS = 11, TRLX = 0, CSNT = 1, SCY = 5, EHTR = 1 */ -#define CONFIG_SYS_OR_TIMING_FLASH (OR_CSNT_SAM | OR_BI | OR_SCY_5_CLK | OR_TRLX) - -#define CONFIG_SYS_OR0_REMAP (CONFIG_SYS_REMAP_OR_AM | CONFIG_SYS_OR_TIMING_FLASH) -#define CONFIG_SYS_OR0_PRELIM (CONFIG_SYS_PRELIM_OR_AM | CONFIG_SYS_OR_TIMING_FLASH) -#define CONFIG_SYS_BR0_PRELIM ((FLASH_BASE0_PRELIM & BR_BA_MSK) | BR_PS_16 | BR_V ) - -#define CONFIG_SYS_OR1_PRELIM ((0xFFFFFFFFLU & ~(FLASH_BANK_MAX_SIZE - 1)) | CONFIG_SYS_OR_TIMING_FLASH) -#define CONFIG_SYS_BR1_PRELIM ((FLASH_BASE1_PRELIM & BR_BA_MSK) | BR_PS_16 | BR_V ) - -/* - * BR4 and OR4 (SDRAM) - * - */ -#define SDRAM_BASE1_PRELIM 0x00000000 /* SDRAM bank #0 */ -#define SDRAM_MAX_SIZE (256 << 20) /* max 256MB per bank */ - -/* SDRAM timing: Multiplexed addresses, GPL5 output to GPL5_A (don't care) */ -#define CONFIG_SYS_OR_TIMING_SDRAM (OR_CSNT_SAM | OR_G5LS) - -#define CONFIG_SYS_OR4_PRELIM ((0xFFFFFFFFLU & ~(SDRAM_MAX_SIZE - 1)) | CONFIG_SYS_OR_TIMING_SDRAM) -#define CONFIG_SYS_BR4_PRELIM ((SDRAM_BASE1_PRELIM & BR_BA_MSK) | BR_MS_UPMA | BR_PS_32 | BR_V) - -/* - * Memory Periodic Timer Prescaler - */ - -/* - * Memory Periodic Timer Prescaler - * - * The Divider for PTA (refresh timer) configuration is based on an - * example SDRAM configuration (64 MBit, one bank). The adjustment to - * the number of chip selects (NCS) and the actually needed refresh - * rate is done by setting MPTPR. - * - * PTA is calculated from - * PTA = (gclk * Trefresh) / ((2 ^ (2 * DFBRG)) * PTP * NCS) - * - * gclk CPU clock (not bus clock!) - * Trefresh Refresh cycle * 4 (four word bursts used) - * - * 4096 Rows from SDRAM example configuration - * 1000 factor s -> ms - * 32 PTP (pre-divider from MPTPR) from SDRAM example configuration - * 4 Number of refresh cycles per period - * 64 Refresh cycle in ms per number of rows - * -------------------------------------------- - * Divider = 4096 * 32 * 1000 / (4 * 64) = 512000 - * - * 50 MHz => 50.000.000 / Divider = 98 - * 66 Mhz => 66.000.000 / Divider = 129 - * 80 Mhz => 80.000.000 / Divider = 156 - */ - -#define CONFIG_SYS_MAMR_PTA 234 - -/* - * For 16 MBit, refresh rates could be 31.3 us - * (= 64 ms / 2K = 125 / quad bursts). - * For a simpler initialization, 15.6 us is used instead. - * - * #define CONFIG_SYS_MPTPR_2BK_2K MPTPR_PTP_DIV32 for 2 banks - * #define CONFIG_SYS_MPTPR_1BK_2K MPTPR_PTP_DIV64 for 1 bank - */ -#define CONFIG_SYS_MPTPR_2BK_4K MPTPR_PTP_DIV16 /* setting for 2 banks */ -#define CONFIG_SYS_MPTPR_1BK_4K MPTPR_PTP_DIV32 /* setting for 1 bank */ - -/* refresh rate 7.8 us (= 64 ms / 8K = 31.2 / quad bursts) for 256 MBit */ -#define CONFIG_SYS_MPTPR_2BK_8K MPTPR_PTP_DIV8 /* setting for 2 banks */ -#define CONFIG_SYS_MPTPR_1BK_8K MPTPR_PTP_DIV16 /* setting for 1 bank */ - -/* - * MAMR settings for SDRAM - */ - -/* 8 column SDRAM */ -#define CONFIG_SYS_MAMR_8COL ((CONFIG_SYS_MAMR_PTA << MAMR_PTA_SHIFT) | MAMR_PTAE | \ - MAMR_AMA_TYPE_0 | MAMR_DSA_1_CYCL | MAMR_G0CLA_A11 | \ - MAMR_RLFA_1X | MAMR_WLFA_1X | MAMR_TLFA_4X) - -/* 9 column SDRAM */ -#define CONFIG_SYS_MAMR_9COL ((CONFIG_SYS_MAMR_PTA << MAMR_PTA_SHIFT) | MAMR_PTAE | \ - MAMR_AMA_TYPE_1 | MAMR_DSA_1_CYCL | MAMR_G0CLA_A10 | \ - MAMR_RLFA_1X | MAMR_WLFA_1X | MAMR_TLFA_4X) - -#define CONFIG_LAST_STAGE_INIT /* needed to reset the damn phys */ - -/****************************************************************/ - -#define NAND_SIZE 0x00010000 /* 64K */ -#define NAND_BASE 0xF1000000 - -/*****************************************************************************/ - -#define CONFIG_SYS_DIRECT_FLASH_TFTP - -/*****************************************************************************/ - -/* Status Leds are on the MODCK pins, which become the PCMCIA PGCRB, - * CxOE and CxRESET. We use the CxOE. - */ -#define STATUS_LED_BIT 0x00000080 /* bit 24 */ - -#define STATUS_LED_PERIOD (CONFIG_SYS_HZ / 2) -#define STATUS_LED_STATE STATUS_LED_BLINKING - -#define STATUS_LED_ACTIVE 0 /* LED on for bit == 0 */ -#define STATUS_LED_BOOT 0 /* LED 0 used for boot status */ - -#ifndef __ASSEMBLY__ - -/* LEDs */ - -/* led_id_t is unsigned int mask */ -typedef unsigned int led_id_t; - -#define __led_toggle(_msk) \ - do { \ - ((volatile immap_t *)CONFIG_SYS_IMMR)->im_pcmcia.pcmc_pgcrb ^= (_msk); \ - } while(0) - -#define __led_set(_msk, _st) \ - do { \ - if ((_st)) \ - ((volatile immap_t *)CONFIG_SYS_IMMR)->im_pcmcia.pcmc_pgcrb |= (_msk); \ - else \ - ((volatile immap_t *)CONFIG_SYS_IMMR)->im_pcmcia.pcmc_pgcrb &= ~(_msk); \ - } while(0) - -#define __led_init(msk, st) __led_set(msk, st) - -#endif - -/******************************************************************************/ - -#define CONFIG_SYS_CONSOLE_IS_IN_ENV 1 -#define CONFIG_SYS_CONSOLE_OVERWRITE_ROUTINE 1 -#define CONFIG_SYS_CONSOLE_ENV_OVERWRITE 1 - -/******************************************************************************/ - -/* use board specific hardware */ -#undef CONFIG_WATCHDOG /* watchdog disabled */ -#define CONFIG_HW_WATCHDOG - -/*****************************************************************************/ - -#define CONFIG_AUTO_COMPLETE 1 -#define CONFIG_CRC32_VERIFY 1 -#define CONFIG_HUSH_OLD_PARSER_COMPATIBLE 1 - -/*****************************************************************************/ - -/* pass open firmware flattened device tree */ -#define CONFIG_OF_LIBFDT 1 - -#define OF_TBCLK (MPC8XX_HZ / 16) - -#endif /* __CONFIG_H */ diff --git a/include/configs/sunxi-common.h b/include/configs/sunxi-common.h index 6a3044f..e768921 100644 --- a/include/configs/sunxi-common.h +++ b/include/configs/sunxi-common.h @@ -100,10 +100,10 @@ /* Boot Argument Buffer Size */ #define CONFIG_SYS_BARGSIZE CONFIG_SYS_CBSIZE -#define CONFIG_SYS_LOAD_ADDR 0x48000000 /* default load address */ +#define CONFIG_SYS_LOAD_ADDR 0x42000000 /* default load address */ /* standalone support */ -#define CONFIG_STANDALONE_LOAD_ADDR 0x48000000 +#define CONFIG_STANDALONE_LOAD_ADDR 0x42000000 #define CONFIG_SYS_HZ 1000 @@ -123,12 +123,8 @@ #define CONFIG_ENV_OFFSET (544 << 10) /* (8 + 24 + 512) KiB */ #define CONFIG_ENV_SIZE (128 << 10) /* 128 KiB */ -#define CONFIG_EXTRA_ENV_SETTINGS \ - "bootm_size=0x10000000\0" - -#define CONFIG_SYS_BOOT_GET_CMDLINE - #include <config_cmd_default.h> +#undef CONFIG_CMD_FPGA #define CONFIG_FAT_WRITE /* enable write access */ @@ -168,10 +164,6 @@ #define CONFIG_SYS_SPL_MALLOC_START 0x4ff00000 #define CONFIG_SYS_SPL_MALLOC_SIZE 0x00080000 /* 512 KiB */ -#undef CONFIG_CMD_FPGA -#undef CONFIG_CMD_NET -#undef CONFIG_CMD_NFS - /* I2C */ #define CONFIG_SPL_I2C_SUPPORT #define CONFIG_SYS_I2C @@ -207,14 +199,6 @@ #define CONFIG_PHYLIB #endif -#ifdef CONFIG_CMD_NET -#define CONFIG_CMD_NFS -#define CONFIG_CMD_DNS -#define CONFIG_NETCONSOLE -#define CONFIG_BOOTP_DNS2 -#define CONFIG_BOOTP_SEND_HOSTNAME -#endif - #ifdef CONFIG_USB_EHCI #define CONFIG_CMD_USB #define CONFIG_SYS_USB_EHCI_MAX_ROOT_PORTS 1 @@ -232,6 +216,40 @@ #ifndef CONFIG_SPL_BUILD #include <config_distro_defaults.h> + +/* 256M RAM (minimum), 32M uncompressed kernel, 16M compressed kernel, 1M fdt, + * 1M script, 1M pxe and the ramdisk at the end */ +#define MEM_LAYOUT_ENV_SETTINGS \ + "bootm_size=0x10000000\0" \ + "kernel_addr_r=0x42000000\0" \ + "fdt_addr_r=0x43000000\0" \ + "scriptaddr=0x43100000\0" \ + "pxefile_addr_r=0x43200000\0" \ + "ramdisk_addr_r=0x43300000\0" + +#ifdef CONFIG_AHCI +#define BOOT_TARGET_DEVICES_SCSI(func) func(SCSI, scsi, 0) +#else +#define BOOT_TARGET_DEVICES_SCSI(func) +#endif + +#define BOOT_TARGET_DEVICES(func) \ + func(MMC, mmc, 0) \ + BOOT_TARGET_DEVICES_SCSI(func) \ + func(USB, usb, 0) \ + func(PXE, pxe, na) \ + func(DHCP, dhcp, na) + +#include <config_distro_bootcmd.h> + +#define CONFIG_EXTRA_ENV_SETTINGS \ + MEM_LAYOUT_ENV_SETTINGS \ + "fdtfile=" CONFIG_FTDFILE "\0" \ + "console=ttyS0,115200\0" \ + BOOTENV + +#else /* ifndef CONFIG_SPL_BUILD */ +#define CONFIG_EXTRA_ENV_SETTINGS #endif #endif /* _SUNXI_COMMON_CONFIG_H */ diff --git a/include/configs/svm_sc8xx.h b/include/configs/svm_sc8xx.h deleted file mode 100644 index b4aa948..0000000 --- a/include/configs/svm_sc8xx.h +++ /dev/null @@ -1,450 +0,0 @@ -/* - * (C) Copyright 2000, 2001, 2002 - * Wolfgang Denk, DENX Software Engineering, wd@denx.de. - * - * SPDX-License-Identifier: GPL-2.0+ - */ - -/* - * board/config.h - configuration options, board specific, - * for SinoVee Microsystems SC8xx series SBC - * http://www.fel.com.cn (Chinese) - * http://www.sinovee.com (English) - */ - -#ifndef __CONFIG_H -#define __CONFIG_H - -#define CONFIG_SYS_TEXT_BASE 0x40000000 - -/* Custom configuration */ -/* SC823,SC850,SC860SAR, FEL8xx-AT(823/850/860) */ -/* SC85T,SC860T, FEL8xx-AT(855T/860T) */ -/*#define CONFIG_FEL8xx_AT */ -/*#define CONFIG_LCD */ -/*#define CONFIG_MPC8XX_LCD*/ -/* if core > 50MHz , un-comment CONFIG_BUS_DIV2 */ -/* #define CONFIG_50MHz */ -/* #define CONFIG_66MHz */ -/* #define CONFIG_75MHz */ -#define CONFIG_80MHz -/*#define CONFIG_100MHz */ -/* #define CONFIG_BUS_DIV2 1 */ -/* for BOOT device port size */ -/* #define CONFIG_BOOT_8B */ -#define CONFIG_BOOT_16B -/* #define CONFIG_BOOT_32B */ -/* #define CONFIG_CAN_DRIVER */ -/* #define DEBUG */ -#define CONFIG_FEC_ENET - -/* #define CONFIG_SDRAM_16M */ -#define CONFIG_SDRAM_32M -/* #define CONFIG_SDRAM_64M */ -#define CONFIG_SYS_RESET_ADDRESS 0xffffffff -/* - * High Level Configuration Options - * (easy to change) - */ - -/* #define CONFIG_MPC823 1 */ -/* #define CONFIG_MPC850 1 */ -#define CONFIG_MPC855 1 -/* #define CONFIG_MPC860 1 */ -/* #define CONFIG_MPC860T 1 */ - -#undef CONFIG_WATCHDOG /* watchdog */ - -#define CONFIG_SVM_SC8xx 1 /* ...on SVM SC8xx series */ - -#ifdef CONFIG_LCD /* with LCD controller ? */ -/* #define CONFIG_NEC_NL6448BC20 1 / * use NEC NL6448BC20 display */ -#endif - -#define CONFIG_8xx_CONS_SMC1 1 /* Console is on SMC1 */ -#undef CONFIG_8xx_CONS_SMC2 -#undef CONFIG_8xx_CONS_NONE -#define CONFIG_BAUDRATE 19200 /* console baudrate = 115kbps */ -#if 0 -#define CONFIG_BOOTDELAY -1 /* autoboot disabled */ -#else -#define CONFIG_BOOTDELAY 1 /* autoboot after 5 seconds */ -#endif - -#define CONFIG_CLOCKS_IN_MHZ 1 /* clocks passsed to Linux in MHz */ - -#define CONFIG_BOARD_TYPES 1 /* support board types */ - -#define CONFIG_PREBOOT "echo;echo Welcome to U-Boot SVM port;echo;echo Type \"? or help\" to get on-line help;echo" - -#undef CONFIG_BOOTARGS -#define CONFIG_EXTRA_ENV_SETTINGS \ - "nfsargs=setenv bootargs root=/dev/nfs rw " \ - "nfsroot=${serverip}:${rootpath}\0" \ - "ramargs=setenv bootargs root=/dev/ram rw\0" \ - "addip=setenv bootargs ${bootargs} " \ - "ip=${ipaddr}:${serverip}:${gatewayip}:${netmask}" \ - ":${hostname}:${netdev}:off panic=1\0" \ - "flash_nfs=run nfsargs addip;" \ - "bootm ${kernel_addr}\0" \ - "flash_self=run ramargs addip;" \ - "bootm ${kernel_addr} ${ramdisk_addr}\0" \ - "net_nfs=tftp 0x210000 ${bootfile};run nfsargs addip;bootm\0" \ - "rootpath=/opt/sinovee/ppc8xx-linux-2.0/target\0" \ - "bootfile=pImage-sc855t\0" \ - "kernel_addr=48000000\0" \ - "ramdisk_addr=48100000\0" \ - "" -#define CONFIG_BOOTCOMMAND \ - "setenv bootargs root=/dev/nfs rw nfsroot=${serverip}:${rootpath} " \ - "ip=${ipaddr}:${serverip}:${gatewayip}:${netmask}:${hostname}::off; " \ - "tftpboot 0x210000 pImage-sc855t;bootm 0x210000" - -#define CONFIG_LOADS_ECHO 1 /* echo on for serial download */ -#undef CONFIG_SYS_LOADS_BAUD_CHANGE /* don't allow baudrate change */ - - -#ifdef CONFIG_LCD -# undef CONFIG_STATUS_LED /* disturbs display */ -#else -# define CONFIG_STATUS_LED 1 /* Status LED enabled */ -#endif /* CONFIG_LCD */ - -#undef CONFIG_CAN_DRIVER /* CAN Driver support disabled */ - -/* - * BOOTP options - */ -#define CONFIG_BOOTP_SUBNETMASK -#define CONFIG_BOOTP_GATEWAY -#define CONFIG_BOOTP_HOSTNAME -#define CONFIG_BOOTP_BOOTPATH -#define CONFIG_BOOTP_BOOTFILESIZE - -#define CONFIG_MAC_PARTITION -#define CONFIG_DOS_PARTITION - -#define CONFIG_RTC_MPC8xx /* use internal RTC of MPC8xx */ - - -/* - * Command line configuration. - */ -#include <config_cmd_default.h> - -#define CONFIG_CMD_ASKENV -#define CONFIG_CMD_DHCP -#define CONFIG_CMD_DATE - -/* - * Miscellaneous configurable options - */ -#define CONFIG_SYS_LONGHELP /* undef to save memory */ - -#ifdef CONFIG_SYS_HUSH_PARSER -#endif - -#if defined(CONFIG_CMD_KGDB) -#define CONFIG_SYS_CBSIZE 1024 /* Console I/O Buffer Size */ -#else -#define CONFIG_SYS_CBSIZE 256 /* Console I/O Buffer Size */ -#endif -#define CONFIG_SYS_PBSIZE (CONFIG_SYS_CBSIZE+sizeof(CONFIG_SYS_PROMPT)+16) /* Print Buffer Size */ -#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 0x0400000 /* memtest works on */ -#define CONFIG_SYS_MEMTEST_END 0x0C00000 /* 4 ... 12 MB in DRAM */ - -#define CONFIG_SYS_LOAD_ADDR 0x100000 /* default load address */ - -/* - * Low Level Configuration Settings - * (address mappings, register initial values, etc.) - * You should know what you are doing if you make changes here. - */ -/*----------------------------------------------------------------------- - * Internal Memory Mapped Register - */ -#define CONFIG_SYS_IMMR 0xFF000000 - -/*----------------------------------------------------------------------- - * Definitions for initial stack pointer and data area (in DPRAM) - */ -#define CONFIG_SYS_INIT_RAM_ADDR CONFIG_SYS_IMMR -#define CONFIG_SYS_INIT_RAM_SIZE 0x2F00 /* Size of used area in DPRAM */ -#define CONFIG_SYS_GBL_DATA_OFFSET (CONFIG_SYS_INIT_RAM_SIZE - GENERATED_GBL_DATA_SIZE) -#define CONFIG_SYS_INIT_SP_OFFSET CONFIG_SYS_GBL_DATA_OFFSET - -/*----------------------------------------------------------------------- - * Start addresses for the final memory configuration - * (Set up by the startup code) - * Please note that CONFIG_SYS_SDRAM_BASE _must_ start at 0 - */ -#define CONFIG_SYS_SDRAM_BASE 0x00000000 -#define CONFIG_SYS_FLASH_BASE 0x40000000 -#define CONFIG_SYS_MONITOR_LEN (384 << 10) /* Reserve 192 kB for Monitor */ -#define CONFIG_SYS_MONITOR_BASE CONFIG_SYS_FLASH_BASE -#define CONFIG_SYS_MALLOC_LEN (128 << 10) /* Reserve 128 kB for malloc() */ - -/* - * For booting Linux, the board info and command line data - * have to be in the first 8 MB of memory, since this is - * the maximum mapped by the Linux kernel during initialization. - */ -#define CONFIG_SYS_BOOTMAPSZ (8 << 20) /* Initial Memory map for Linux */ - -/*----------------------------------------------------------------------- - * FLASH organization - */ -#define CONFIG_SYS_MAX_FLASH_BANKS 2 /* max number of memory banks */ -#define CONFIG_SYS_MAX_FLASH_SECT 67 /* max number of sectors on one chip */ - -#define CONFIG_SYS_FLASH_ERASE_TOUT 120000 /* Timeout for Flash Erase (in ms) */ -#define CONFIG_SYS_FLASH_WRITE_TOUT 500 /* Timeout for Flash Write (in ms) */ - -#define CONFIG_ENV_IS_IN_FLASH 1 - -#ifdef CONFIG_BOOT_8B -#define CONFIG_ENV_OFFSET 0x10000 /* Offset of Environment Sector */ -#define CONFIG_ENV_SIZE 0x10000 /* Total Size of Environment Sector */ -#elif defined (CONFIG_BOOT_16B) -#define CONFIG_ENV_OFFSET 0x10000 /* Offset of Environment Sector */ -#define CONFIG_ENV_SIZE 0x10000 /* Total Size of Environment Sector */ -#elif defined (CONFIG_BOOT_32B) -#define CONFIG_ENV_OFFSET 0x8000 /* Offset of Environment Sector */ -#define CONFIG_ENV_SIZE 0x4000 /* Total Size of Environment Sector */ -#endif - -/* Address and size of Redundant Environment Sector */ -#define CONFIG_ENV_OFFSET_REDUND (CONFIG_ENV_OFFSET+CONFIG_ENV_SIZE) -#define CONFIG_ENV_SIZE_REDUND (CONFIG_ENV_SIZE) - - -/*----------------------------------------------------------------------- - * Hardware Information Block - */ -#define CONFIG_SYS_HWINFO_OFFSET 0x0003FFC0 /* offset of HW Info block */ -#define CONFIG_SYS_HWINFO_SIZE 0x00000040 /* size of HW Info block */ -#define CONFIG_SYS_HWINFO_MAGIC 0x46454C38 /* 'SVM8' */ - -/*----------------------------------------------------------------------- - * Cache Configuration - */ -#define CONFIG_SYS_CACHELINE_SIZE 16 /* For all MPC8xx CPUs */ -#if defined(CONFIG_CMD_KGDB) -#define CONFIG_SYS_CACHELINE_SHIFT 4 /* log base 2 of the above value */ -#endif - -/*----------------------------------------------------------------------- - * SYPCR - System Protection Control 11-9 - * SYPCR can only be written once after reset! - *----------------------------------------------------------------------- - * Software & Bus Monitor Timer max, Bus Monitor enable, SW Watchdog freeze - */ -#if defined(CONFIG_WATCHDOG) -/*#define CONFIG_SYS_SYPCR (SYPCR_SWTC | SYPCR_BMT | SYPCR_BME | SYPCR_SWF | \ - SYPCR_SWE | SYPCR_SWRI| SYPCR_SWP) -*/ -#define CONFIG_SYS_SYPCR (SYPCR_SWTC | SYPCR_BMT | SYPCR_SWF | \ - SYPCR_SWE | SYPCR_SWRI| SYPCR_SWP) -#else -#define CONFIG_SYS_SYPCR 0xffffff88 -#endif - -/*----------------------------------------------------------------------- - * SIUMCR - SIU Module Configuration 11-6 - *----------------------------------------------------------------------- - * PCMCIA config., multi-function pin tri-state - */ -#ifndef CONFIG_CAN_DRIVER -/*#define CONFIG_SYS_SIUMCR 0x00610c00 */ -#define CONFIG_SYS_SIUMCR 0x00000000 -#else /* we must activate GPL5 in the SIUMCR for CAN */ -#define CONFIG_SYS_SIUMCR (SIUMCR_DBGC11 | SIUMCR_DBPC00 | SIUMCR_MLRC01) -#endif /* CONFIG_CAN_DRIVER */ - -/*----------------------------------------------------------------------- - * TBSCR - Time Base Status and Control 11-26 - *----------------------------------------------------------------------- - * Clear Reference Interrupt Status, Timebase freezing enabled - */ -#define CONFIG_SYS_TBSCR 0x0001 - -/*----------------------------------------------------------------------- - * RTCSC - Real-Time Clock Status and Control Register 11-27 - *----------------------------------------------------------------------- - */ -#define CONFIG_SYS_RTCSC 0x00c3 - -/*----------------------------------------------------------------------- - * PISCR - Periodic Interrupt Status and Control 11-31 - *----------------------------------------------------------------------- - * Clear Periodic Interrupt Status, Interrupt Timer freezing enabled - */ -#define CONFIG_SYS_PISCR 0x0000 - -/*----------------------------------------------------------------------- - * PLPRCR - PLL, Low-Power, and Reset Control Register 15-30 - *----------------------------------------------------------------------- - * Reset PLL lock status sticky bit, timer expired status bit and timer - * interrupt status bit - */ -#if defined (CONFIG_100MHz) -#define CONFIG_SYS_PLPRCR 0x06301000 -#define CONFIG_8xx_GCLK_FREQ 100000000 -#elif defined (CONFIG_80MHz) -#define CONFIG_SYS_PLPRCR 0x04f01000 -#define CONFIG_8xx_GCLK_FREQ 80000000 -#elif defined(CONFIG_75MHz) -#define CONFIG_SYS_PLPRCR 0x04a00100 -#define CONFIG_8xx_GCLK_FREQ 75000000 -#elif defined(CONFIG_66MHz) -#define CONFIG_SYS_PLPRCR 0x04101000 -#define CONFIG_8xx_GCLK_FREQ 66000000 -#elif defined(CONFIG_50MHz) -#define CONFIG_SYS_PLPRCR 0x03101000 -#define CONFIG_8xx_GCLK_FREQ 50000000 -#endif - -/*----------------------------------------------------------------------- - * SCCR - System Clock and reset Control Register 15-27 - *----------------------------------------------------------------------- - * Set clock output, timebase and RTC source and divider, - * power management and some other internal clocks - */ -#define SCCR_MASK SCCR_EBDF11 -#ifdef CONFIG_BUS_DIV2 -#define CONFIG_SYS_SCCR 0x02020000 | SCCR_RTSEL -#else /* up to 50 MHz we use a 1:1 clock */ -#define CONFIG_SYS_SCCR 0x02000000 | SCCR_RTSEL -#endif - -/*----------------------------------------------------------------------- - * PCMCIA stuff - *----------------------------------------------------------------------- - * - */ -#define CONFIG_SYS_PCMCIA_MEM_ADDR (0xE0000000) -#define CONFIG_SYS_PCMCIA_MEM_SIZE ( 64 << 20 ) -#define CONFIG_SYS_PCMCIA_DMA_ADDR (0xE4000000) -#define CONFIG_SYS_PCMCIA_DMA_SIZE ( 64 << 20 ) -#define CONFIG_SYS_PCMCIA_ATTRB_ADDR (0xE8000000) -#define CONFIG_SYS_PCMCIA_ATTRB_SIZE ( 64 << 20 ) -#define CONFIG_SYS_PCMCIA_IO_ADDR (0xEC000000) -#define CONFIG_SYS_PCMCIA_IO_SIZE ( 64 << 20 ) - -/*----------------------------------------------------------------------- - * IDE/ATA stuff (Supports IDE harddisk on PCMCIA Adapter) - *----------------------------------------------------------------------- - */ - -#undef CONFIG_IDE_8xx_PCCARD /* Use IDE with PC Card Adapter */ - -#define CONFIG_IDE_PREINIT 1 /* Use preinit IDE hook */ -#define CONFIG_IDE_INIT_POSTRESET 1 /* Use postreset IDE hook */ -#define CONFIG_IDE_8xx_DIRECT 1 /* Direct IDE not supported */ -#undef CONFIG_IDE_LED /* LED for ide not supported */ -#undef CONFIG_IDE_RESET /* reset for ide not supported */ - -#define CONFIG_SYS_IDE_MAXBUS 1 /* max. 1 IDE bus */ -#define CONFIG_SYS_IDE_MAXDEVICE 1 /* max. 1 drive per IDE bus */ - -#define CONFIG_SYS_ATA_BASE_ADDR 0xFE100010 -#define CONFIG_SYS_ATA_IDE0_OFFSET 0x0000 -/*#define CONFIG_SYS_ATA_IDE1_OFFSET 0x0C00 */ -#define CONFIG_SYS_ATA_DATA_OFFSET 0x0000 /* Offset for data I/O - */ -#define CONFIG_SYS_ATA_REG_OFFSET 0x0200 /* Offset for normal register accesses - */ -#define CONFIG_SYS_ATA_ALT_OFFSET 0x0210 /* Offset for alternate registers - */ -#define CONFIG_ATAPI -#define CONFIG_SYS_PIO_MODE 0 - -/*----------------------------------------------------------------------- - * - *----------------------------------------------------------------------- - * - */ -/*#define CONFIG_SYS_DER 0x2002000F*/ -#define CONFIG_SYS_DER 0x0 - -/* - * Init Memory Controller: - * - * BR0/1 and OR0/1 (FLASH) - */ - -#define FLASH_BASE0_PRELIM 0x40000000 /* FLASH bank #0 */ -#define FLASH_BASE1_PRELIM 0x60000000 /* FLASH bank #0 */ - -/* used to re-map FLASH both when starting from SRAM or FLASH: - * restrict access enough to keep SRAM working (if any) - * but not too much to meddle with FLASH accesses - */ -#define CONFIG_SYS_REMAP_OR_AM 0x80000000 /* OR addr mask */ -#define CONFIG_SYS_PRELIM_OR_AM 0xE0000000 /* OR addr mask */ - -/* - * FLASH timing: - */ -#if defined(CONFIG_100MHz) -#define CONFIG_SYS_OR_TIMING_FLASH 0x000002f4 -#define CONFIG_SYS_OR_TIMING_DOC 0x000002f4 -#define CONFIG_SYS_MxMR_PTx 0x61000000 -#define CONFIG_SYS_MPTPR 0x400 - -#elif defined(CONFIG_80MHz) -#define CONFIG_SYS_OR_TIMING_FLASH 0x00000ff4 -#define CONFIG_SYS_OR_TIMING_DOC 0x000001f4 -#define CONFIG_SYS_MxMR_PTx 0x4e000000 -#define CONFIG_SYS_MPTPR 0x400 - -#elif defined(CONFIG_75MHz) -#define CONFIG_SYS_OR_TIMING_FLASH 0x000008f4 -#define CONFIG_SYS_OR_TIMING_DOC 0x000002f4 -#define CONFIG_SYS_MxMR_PTx 0x49000000 -#define CONFIG_SYS_MPTPR 0x400 - -#elif defined(CONFIG_66MHz) -#define CONFIG_SYS_OR_TIMING_FLASH (OR_ACS_DIV1 | OR_TRLX | OR_CSNT_SAM | \ - OR_SCY_3_CLK | OR_EHTR | OR_BI) -/*#define CONFIG_SYS_OR_TIMING_FLASH 0x000001f4 */ -#define CONFIG_SYS_OR_TIMING_DOC 0x000003f4 -#define CONFIG_SYS_MxMR_PTx 0x40000000 -#define CONFIG_SYS_MPTPR 0x400 - -#else /* 50 MHz */ -#define CONFIG_SYS_OR_TIMING_FLASH 0x00000ff4 -#define CONFIG_SYS_OR_TIMING_DOC 0x000001f4 -#define CONFIG_SYS_MxMR_PTx 0x30000000 -#define CONFIG_SYS_MPTPR 0x400 -#endif /*CONFIG_??MHz */ - - -#if defined (CONFIG_BOOT_8B) /* 512K X 8 ,29F040 , 2MB space */ -#define CONFIG_SYS_OR0_PRELIM (0xffe00000 | CONFIG_SYS_OR_TIMING_FLASH) -#define CONFIG_SYS_BR0_PRELIM ((FLASH_BASE0_PRELIM & BR_BA_MSK) | BR_V | BR_PS_8) -#elif defined (CONFIG_BOOT_16B) /* 29lv160 X 16 , 4MB space */ -#define CONFIG_SYS_OR0_PRELIM (0xffc00000 | CONFIG_SYS_OR_TIMING_FLASH) -#define CONFIG_SYS_BR0_PRELIM ((FLASH_BASE0_PRELIM & BR_BA_MSK) | BR_V | BR_PS_16) -#elif defined( CONFIG_BOOT_32B ) /* 29lv160 X 2 X 32, 4/8/16MB , 64MB space */ -#define CONFIG_SYS_OR0_PRELIM (0xfc000000 | CONFIG_SYS_OR_TIMING_FLASH) -#define CONFIG_SYS_BR0_PRELIM ((FLASH_BASE0_PRELIM & BR_BA_MSK) | BR_V ) -#else -#error Boot device port size missing. -#endif - -/* - * Disk-On-Chip configuration - */ - -#define CONFIG_SYS_DOC_SHORT_TIMEOUT -#define CONFIG_SYS_MAX_DOC_DEVICE 1 /* Max number of DOC devices */ - -#define CONFIG_SYS_DOC_SUPPORT_2000 -#define CONFIG_SYS_DOC_SUPPORT_MILLENNIUM -#define CONFIG_SYS_DOC_BASE 0x80000000 - -#endif /* __CONFIG_H */ diff --git a/include/configs/tegra-common-post.h b/include/configs/tegra-common-post.h index 1c770c9..c337e30 100644 --- a/include/configs/tegra-common-post.h +++ b/include/configs/tegra-common-post.h @@ -8,136 +8,16 @@ #ifndef __TEGRA_COMMON_POST_H #define __TEGRA_COMMON_POST_H -#ifdef CONFIG_BOOTCOMMAND - -#define BOOTCMDS_COMMON "" - -#else - -#ifdef CONFIG_CMD_MMC -#define BOOTCMDS_MMC \ - "mmc_boot=" \ - "setenv devtype mmc; " \ - "if mmc dev ${devnum}; then " \ - "run scan_boot; " \ - "fi\0" \ - "bootcmd_mmc0=setenv devnum 0; run mmc_boot;\0" \ - "bootcmd_mmc1=setenv devnum 1; run mmc_boot;\0" -#define BOOT_TARGETS_MMC "mmc1 mmc0" +#ifndef CONFIG_SPL_BUILD +#define BOOT_TARGET_DEVICES(func) \ + func(MMC, mmc, 1) \ + func(MMC, mmc, 0) \ + func(USB, usb, 0) \ + func(PXE, pxe, na) \ + func(DHCP, dhcp, na) +#include <config_distro_bootcmd.h> #else -#define BOOTCMDS_MMC "" -#define BOOT_TARGETS_MMC "" -#endif - -#ifdef CONFIG_CMD_USB -#define BOOTCMD_INIT_USB "run usb_init; " -#define BOOTCMDS_USB \ - "usb_init=" \ - "if ${usb_need_init}; then " \ - "set usb_need_init false; " \ - "usb start 0; " \ - "fi\0" \ - \ - "usb_boot=" \ - "setenv devtype usb; " \ - BOOTCMD_INIT_USB \ - "if usb dev ${devnum}; then " \ - "run scan_boot; " \ - "fi\0" \ - \ - "bootcmd_usb0=setenv devnum 0; run usb_boot;\0" -#define BOOT_TARGETS_USB "usb0" -#else -#define BOOTCMD_INIT_USB "" -#define BOOTCMDS_USB "" -#define BOOT_TARGETS_USB "" -#endif - -#ifdef CONFIG_CMD_DHCP -#define BOOTCMDS_DHCP \ - "bootcmd_dhcp=" \ - BOOTCMD_INIT_USB \ - "if dhcp ${scriptaddr} boot.scr.uimg; then "\ - "source ${scriptaddr}; " \ - "fi\0" -#define BOOT_TARGETS_DHCP "dhcp" -#else -#define BOOTCMDS_DHCP "" -#define BOOT_TARGETS_DHCP "" -#endif - -#if defined(CONFIG_CMD_DHCP) && defined(CONFIG_CMD_PXE) -#define BOOTCMDS_PXE \ - "bootcmd_pxe=" \ - BOOTCMD_INIT_USB \ - "dhcp; " \ - "if pxe get; then " \ - "pxe boot; " \ - "fi\0" -#define BOOT_TARGETS_PXE "pxe" -#else -#define BOOTCMDS_PXE "" -#define BOOT_TARGETS_PXE "" -#endif - -#define BOOTCMDS_COMMON \ - "rootpart=1\0" \ - \ - "do_script_boot=" \ - "load ${devtype} ${devnum}:${rootpart} " \ - "${scriptaddr} ${prefix}${script}; " \ - "source ${scriptaddr}\0" \ - \ - "script_boot=" \ - "for script in ${boot_scripts}; do " \ - "if test -e ${devtype} ${devnum}:${rootpart} " \ - "${prefix}${script}; then " \ - "echo Found U-Boot script " \ - "${prefix}${script}; " \ - "run do_script_boot; " \ - "echo SCRIPT FAILED: continuing...; " \ - "fi; " \ - "done\0" \ - \ - "do_sysboot_boot=" \ - "sysboot ${devtype} ${devnum}:${rootpart} any " \ - "${scriptaddr} ${prefix}extlinux/extlinux.conf\0" \ - \ - "sysboot_boot=" \ - "if test -e ${devtype} ${devnum}:${rootpart} " \ - "${prefix}extlinux/extlinux.conf; then " \ - "echo Found ${prefix}extlinux/extlinux.conf; " \ - "run do_sysboot_boot; " \ - "echo SCRIPT FAILED: continuing...; " \ - "fi\0" \ - \ - "scan_boot=" \ - "echo Scanning ${devtype} ${devnum}...; " \ - "for prefix in ${boot_prefixes}; do " \ - "run sysboot_boot; " \ - "run script_boot; " \ - "done\0" \ - \ - "boot_targets=" \ - BOOT_TARGETS_MMC " " \ - BOOT_TARGETS_USB " " \ - BOOT_TARGETS_PXE " " \ - BOOT_TARGETS_DHCP " " \ - "\0" \ - \ - "boot_prefixes=/ /boot/\0" \ - \ - "boot_scripts=boot.scr.uimg boot.scr\0" \ - \ - BOOTCMDS_MMC \ - BOOTCMDS_USB \ - BOOTCMDS_DHCP \ - BOOTCMDS_PXE - -#define CONFIG_BOOTCOMMAND \ - "set usb_need_init; " \ - "for target in ${boot_targets}; do run bootcmd_${target}; done" - +#define BOOTENV #endif #ifdef CONFIG_TEGRA_KEYBOARD @@ -175,7 +55,7 @@ MEM_LAYOUT_ENV_SETTINGS \ "fdt_high=ffffffff\0" \ "initrd_high=ffffffff\0" \ - BOOTCMDS_COMMON \ + BOOTENV \ BOARD_EXTRA_ENV_SETTINGS #if defined(CONFIG_TEGRA20_SFLASH) || defined(CONFIG_TEGRA20_SLINK) || defined(CONFIG_TEGRA114_SPI) diff --git a/include/configs/tqma6.h b/include/configs/tqma6.h new file mode 100644 index 0000000..2705d2c --- /dev/null +++ b/include/configs/tqma6.h @@ -0,0 +1,483 @@ +/* + * Copyright (C) 2013, 2014 Markus Niebel <Markus.Niebel@tq-group.com> + * + * Configuration settings for the TQ Systems TQMa6<Q,S> module. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __CONFIG_H +#define __CONFIG_H + +#include "mx6_common.h" +#include <asm/arch/imx-regs.h> +#include <asm/imx-common/gpio.h> +#include <linux/sizes.h> + +#define CONFIG_MX6 + +#if defined(CONFIG_MX6DL) || defined(CONFIG_MX6S) +#define PHYS_SDRAM_SIZE (512u * SZ_1M) +#elif defined(CONFIG_MX6Q) || defined(CONFIG_MX6D) +#define PHYS_SDRAM_SIZE (1024u * SZ_1M) +#endif + +#if defined(CONFIG_MBA6) + +#if defined(CONFIG_MX6DL) || defined(CONFIG_MX6S) +#define CONFIG_DEFAULT_FDT_FILE "imx6dl-mba6x.dtb" +#elif defined(CONFIG_MX6Q) || defined(CONFIG_MX6Q) +#define CONFIG_DEFAULT_FDT_FILE "imx6q-mba6x.dtb" +#endif + +#endif + +#define CONFIG_DISPLAY_CPUINFO +#define CONFIG_DISPLAY_BOARDINFO +#define CONFIG_SYS_GENERIC_BOARD + +#define CONFIG_CMDLINE_TAG +#define CONFIG_SETUP_MEMORY_TAGS +#define CONFIG_INITRD_TAG +#define CONFIG_REVISION_TAG + +#define CONFIG_BOARD_EARLY_INIT_F +#define CONFIG_BOARD_LATE_INIT + +#define CONFIG_MXC_GPIO +#define CONFIG_MXC_UART + +/* SPI */ +#define CONFIG_CMD_SPI +#define CONFIG_MXC_SPI + +/* SPI Flash */ +#define CONFIG_SPI_FLASH +#define CONFIG_SPI_FLASH_STMICRO + +#define CONFIG_CMD_SF +#define CONFIG_SF_DEFAULT_BUS 0 +#define CONFIG_SF_DEFAULT_CS (0 | (IMX_GPIO_NR(3, 19) << 8)) +#define CONFIG_SF_DEFAULT_SPEED 50000000 +#define CONFIG_SF_DEFAULT_MODE (SPI_MODE_0) + +/* I2C Configs */ +#define CONFIG_CMD_I2C +#define CONFIG_SYS_I2C +#define CONFIG_SYS_I2C_MXC +#define CONFIG_I2C_MULTI_BUS +#define CONFIG_SYS_I2C_SPEED 100000 + +/* I2C SYSMON (LM75) */ +#define CONFIG_DTT_LM75 +#if defined(CONFIG_MBA6) +#define CONFIG_DTT_SENSORS { 0, 1 } +#else +#define CONFIG_DTT_SENSORS { 0 } +#endif +#define CONFIG_DTT_MAX_TEMP 70 +#define CONFIG_DTT_MIN_TEMP -30 +#define CONFIG_DTT_HYSTERESIS 3 +#define CONFIG_CMD_DTT + +/* I2C EEPROM (M24C64) */ +#define CONFIG_SYS_I2C_EEPROM_ADDR 0x50 +#define CONFIG_SYS_I2C_EEPROM_ADDR_LEN 2 +#define CONFIG_SYS_I2C_EEPROM_PAGE_WRITE_BITS 5 /* 32 Bytes */ +#define CONFIG_SYS_I2C_EEPROM_PAGE_WRITE_DELAY_MS 20 +#define CONFIG_CMD_EEPROM + +#define CONFIG_POWER +#define CONFIG_POWER_I2C +#define CONFIG_POWER_PFUZE100 +#define CONFIG_POWER_PFUZE100_I2C_ADDR 0x08 +#define TQMA6_PFUZE100_I2C_BUS 2 + +/* MMC Configs */ +#define CONFIG_FSL_ESDHC +#define CONFIG_FSL_USDHC +#define CONFIG_SYS_FSL_ESDHC_ADDR 0 + +#define CONFIG_MMC +#define CONFIG_CMD_MMC +#define CONFIG_GENERIC_MMC +#define CONFIG_BOUNCE_BUFFER + +/* USB Configs */ +#define CONFIG_CMD_USB +#define CONFIG_USB_EHCI +#define CONFIG_USB_EHCI_MX6 +#define CONFIG_USB_STORAGE +#define CONFIG_USB_HOST_ETHER +#define CONFIG_USB_ETHER_SMSC95XX +#define CONFIG_MXC_USB_PORT 1 +#define CONFIG_MXC_USB_PORTSC (PORT_PTS_UTMI | PORT_PTS_PTW) +#define CONFIG_MXC_USB_FLAGS 0 + +/* Fuses */ +#define CONFIG_MXC_OCOTP +#define CONFIG_CMD_FUSE + +#define CONFIG_CMD_EXT2 +#define CONFIG_CMD_FAT +#define CONFIG_DOS_PARTITION + +#define CONFIG_CMD_PING +#define CONFIG_CMD_DHCP +#define CONFIG_CMD_MII +#define CONFIG_CMD_NET + +#define CONFIG_FEC_MXC +#define IMX_FEC_BASE ENET_BASE_ADDR +#define CONFIG_PHYLIB +#define CONFIG_MII + +#if defined(CONFIG_MBA6) + +#define CONFIG_FEC_XCV_TYPE RGMII +#define CONFIG_ETHPRIME "FEC" + +#define CONFIG_FEC_MXC_PHYADDR 0x03 +#define CONFIG_PHY_MICREL +#define CONFIG_PHY_KSZ9031 + +#else + +#error "define PHY to use for your baseboard" + +#endif + +#define CONFIG_ARP_TIMEOUT 200UL +/* Network config - Allow larger/faster download for TFTP/NFS */ +#define CONFIG_IP_DEFRAG +#define CONFIG_TFTP_BLOCKSIZE 4096 +#define CONFIG_NFS_READ_SIZE 4096 + +#if defined(CONFIG_MBA6) + +#define CONFIG_MXC_UART_BASE UART2_BASE +#define CONFIG_CONSOLE_DEV "ttymxc1" + +#else + +#error "define baseboard specific things (uart, number of SD-card slots)" + +#endif + +/* allow to overwrite serial and ethaddr */ +#define CONFIG_ENV_OVERWRITE +#define CONFIG_CONS_INDEX 1 +#define CONFIG_BAUDRATE 115200 + +/* Command definition */ +#include <config_cmd_default.h> + +#define CONFIG_CMD_BMODE +#define CONFIG_CMD_BOOTZ +#define CONFIG_CMD_ITEST +#define CONFIG_CMD_SETEXPR +#undef CONFIG_CMD_IMLS + +#define CONFIG_BOOTDELAY 3 + +#define CONFIG_LOADADDR 0x12000000 + +/* place code in last 4 MiB of RAM */ +#if defined(CONFIG_MX6DL) || defined(CONFIG_MX6S) +#define CONFIG_SYS_TEXT_BASE 0x2fc00000 +#elif defined(CONFIG_MX6Q) || defined(CONFIG_MX6D) +#define CONFIG_SYS_TEXT_BASE 0x4fc00000 +#endif + +#define CONFIG_ENV_SIZE (SZ_8K) +/* Size of malloc() pool */ +#define CONFIG_SYS_MALLOC_LEN (CONFIG_ENV_SIZE + 2 * SZ_1M) + +#if defined(CONFIG_TQMA6X_MMC_BOOT) + +#define CONFIG_ENV_IS_IN_MMC +#define TQMA6_UBOOT_OFFSET SZ_1K +#define TQMA6_UBOOT_SECTOR_START 0x2 +#define TQMA6_UBOOT_SECTOR_COUNT 0x7fe + +#define CONFIG_ENV_OFFSET SZ_1M +#define CONFIG_SYS_MMC_ENV_DEV 0 + +#define TQMA6_FDT_OFFSET (2 * SZ_1M) +#define TQMA6_FDT_SECTOR_START 0x1000 +#define TQMA6_FDT_SECTOR_COUNT 0x800 + +#define TQMA6_KERNEL_SECTOR_START 0x2000 +#define TQMA6_KERNEL_SECTOR_COUNT 0x2000 + +#define TQMA6_EXTRA_BOOTDEV_ENV_SETTINGS \ + "uboot_start="__stringify(TQMA6_UBOOT_SECTOR_START)"\0" \ + "uboot_size="__stringify(TQMA6_UBOOT_SECTOR_COUNT)"\0" \ + "fdt_start="__stringify(TQMA6_FDT_SECTOR_START)"\0" \ + "fdt_size="__stringify(TQMA6_FDT_SECTOR_COUNT)"\0" \ + "kernel_start="__stringify(TQMA6_KERNEL_SECTOR_START)"\0" \ + "kernel_size="__stringify(TQMA6_KERNEL_SECTOR_COUNT)"\0" \ + "mmcdev="__stringify(CONFIG_SYS_MMC_ENV_DEV)"\0" \ + "loadimage=mmc dev ${mmcdev}; " \ + "mmc read ${loadaddr} ${kernel_start} ${kernel_size};\0" \ + "loadfdt=mmc dev ${mmcdev}; " \ + "mmc read ${fdt_addr} ${fdt_start} ${fdt_size};\0" \ + "update_uboot=if tftp ${uboot}; then " \ + "if itest ${filesize} > 0; then " \ + "mmc dev ${mmcdev}; mmc rescan; " \ + "setexpr blkc ${filesize} / 0x200; " \ + "setexpr blkc ${blkc} + 1; " \ + "if itest ${blkc} <= ${uboot_size}; then " \ + "mmc write ${loadaddr} ${uboot_start} " \ + "${blkc}; " \ + "fi; " \ + "fi; fi; " \ + "setenv filesize; setenv blkc \0" \ + "update_kernel=run kernel_name; " \ + "if tftp ${kernel}; then " \ + "if itest ${filesize} > 0; then " \ + "mmc dev ${mmcdev}; mmc rescan; " \ + "setexpr blkc ${filesize} / 0x200; " \ + "setexpr blkc ${blkc} + 1; " \ + "if itest ${blkc} <= ${kernel_size}; then " \ + "mmc write ${loadaddr} " \ + "${kernel_start} ${blkc}; " \ + "fi; " \ + "fi; " \ + "fi; " \ + "setenv filesize; setenv blkc \0" \ + "update_fdt=if tftp ${fdt_file}; then " \ + "if itest ${filesize} > 0; then " \ + "mmc dev ${mmcdev}; mmc rescan; " \ + "setexpr blkc ${filesize} / 0x200; " \ + "setexpr blkc ${blkc} + 1; " \ + "if itest ${blkc} <= ${fdt_size}; then " \ + "mmc write ${loadaddr} ${fdt_start} ${blkc}; " \ + "fi; " \ + "fi; fi; " \ + "setenv filesize; setenv blkc \0" \ + +#define CONFIG_BOOTCOMMAND \ + "run mmcboot; run netboot; run panicboot" + +#elif defined(CONFIG_TQMA6X_SPI_BOOT) + +#define CONFIG_FLASH_SECTOR_SIZE 0x10000 + +#define TQMA6_UBOOT_OFFSET 0x400 +#define TQMA6_UBOOT_SECTOR_START 0x0 +/* max u-boot size: 512k */ +#define TQMA6_UBOOT_SECTOR_SIZE CONFIG_FLASH_SECTOR_SIZE +#define TQMA6_UBOOT_SECTOR_COUNT 0x8 +#define TQMA6_UBOOT_SIZE (TQMA6_UBOOT_SECTOR_SIZE * \ + TQMA6_UBOOT_SECTOR_COUNT) + +#define CONFIG_ENV_IS_IN_SPI_FLASH +#define CONFIG_SYS_REDUNDAND_ENVIRONMENT +#define CONFIG_ENV_OFFSET (TQMA6_UBOOT_SIZE) +#define CONFIG_ENV_SECT_SIZE CONFIG_FLASH_SECTOR_SIZE +#define CONFIG_ENV_OFFSET_REDUND (CONFIG_ENV_OFFSET + \ + CONFIG_ENV_SECT_SIZE) + +#define CONFIG_ENV_SPI_BUS (CONFIG_SF_DEFAULT_BUS) +#define CONFIG_ENV_SPI_CS (CONFIG_SF_DEFAULT_CS) +#define CONFIG_ENV_SPI_MAX_HZ (CONFIG_SF_DEFAULT_SPEED) +#define CONFIG_ENV_SPI_MODE (CONFIG_SF_DEFAULT_MODE) + +#define TQMA6_FDT_OFFSET (CONFIG_ENV_OFFSET_REDUND + \ + CONFIG_ENV_SECT_SIZE) +#define TQMA6_FDT_SECT_SIZE (CONFIG_FLASH_SECTOR_SIZE) + +#define TQMA6_FDT_SECTOR_START 0x0a /* 8 Sector u-boot, 2 Sector env */ +#define TQMA6_FDT_SECTOR_COUNT 0x01 + +#define TQMA6_KERNEL_SECTOR_START 0x10 +#define TQMA6_KERNEL_SECTOR_COUNT 0x60 + +#define TQMA6_EXTRA_BOOTDEV_ENV_SETTINGS \ + "mmcblkdev=0\0" \ + "uboot_offset="__stringify(TQMA6_UBOOT_OFFSET)"\0" \ + "uboot_sectors="__stringify(TQMA6_UBOOT_SECTOR_COUNT)"\0" \ + "fdt_start="__stringify(TQMA6_FDT_SECTOR_START)"\0" \ + "fdt_sectors="__stringify(TQMA6_FDT_SECTOR_COUNT)"\0" \ + "kernel_start="__stringify(TQMA6_KERNEL_SECTOR_START)"\0" \ + "kernel_sectors="__stringify(TQMA6_KERNEL_SECTOR_COUNT)"\0" \ + "update_uboot=if tftp ${uboot}; then " \ + "if itest ${filesize} > 0; then " \ + "setexpr blkc ${filesize} + " \ + __stringify(TQMA6_UBOOT_OFFSET) "; " \ + "setexpr size ${uboot_sectors} * " \ + __stringify(CONFIG_FLASH_SECTOR_SIZE)"; " \ + "if itest ${blkc} <= ${size}; then " \ + "sf probe; " \ + "sf erase 0 ${size}; " \ + "sf write ${loadaddr} ${uboot_offset} " \ + "${filesize}; " \ + "fi; " \ + "fi; fi; " \ + "setenv filesize 0; setenv blkc; setenv size \0" \ + "update_kernel=run kernel_name; if tftp ${kernel}; then " \ + "if itest ${filesize} > 0; then " \ + "setexpr size ${kernel_sectors} * " \ + __stringify(CONFIG_FLASH_SECTOR_SIZE)"; " \ + "setexpr offset ${kernel_start} * " \ + __stringify(CONFIG_FLASH_SECTOR_SIZE)"; " \ + "if itest ${filesize} <= ${size}; then " \ + "sf probe; " \ + "sf erase ${offset} ${size}; " \ + "sf write ${loadaddr} ${offset} " \ + "${filesize}; " \ + "fi; " \ + "fi; fi; " \ + "setenv filesize 0; setenv size ; setenv offset\0" \ + "update_fdt=if tftp ${fdt_file}; then " \ + "if itest ${filesize} > 0; then " \ + "setexpr size ${fdt_sectors} * " \ + __stringify(CONFIG_FLASH_SECTOR_SIZE)"; " \ + "setexpr offset ${fdt_start} * " \ + __stringify(CONFIG_FLASH_SECTOR_SIZE)"; " \ + "if itest ${filesize} <= ${size}; then " \ + "sf probe; " \ + "sf erase ${offset} ${size}; " \ + "sf write ${loadaddr} ${offset} " \ + "${filesize}; " \ + "fi; " \ + "fi; fi; " \ + "setenv filesize 0; setenv size ; setenv offset\0" \ + "loadimage=sf probe; " \ + "setexpr size ${kernel_sectors} * " \ + __stringify(CONFIG_FLASH_SECTOR_SIZE)"; " \ + "setexpr offset ${kernel_start} * " \ + __stringify(CONFIG_FLASH_SECTOR_SIZE)"; " \ + "sf read ${loadaddr} ${offset} ${size}; " \ + "setenv size ; setenv offset\0" \ + "loadfdt=sf probe; " \ + "setexpr size ${fdt_sectors} * " \ + __stringify(CONFIG_FLASH_SECTOR_SIZE)"; " \ + "setexpr offset ${fdt_start} * " \ + __stringify(CONFIG_FLASH_SECTOR_SIZE)"; " \ + "sf read ${${fdt_addr}} ${offset} ${size}; " \ + "setenv size ; setenv offset\0" \ + + +#define CONFIG_BOOTCOMMAND \ + "sf probe; run mmcboot; run netboot; run panicboot" \ + +#else + +#error "need to define boot source" + +#endif + +/* 128 MiB offset as in ARM related docu for linux suggested */ +#define TQMA6_FDT_ADDRESS 0x18000000 + +#define CONFIG_EXTRA_ENV_SETTINGS \ + "board=tqma6\0" \ + "uimage=uImage\0" \ + "zimage=zImage\0" \ + "boot_type=bootz\0" \ + "kernel_name=if test \"${boot_type}\" != bootz; then " \ + "setenv kernel ${uimage}; " \ + "else setenv kernel ${zimage}; fi\0" \ + "uboot=u-boot.imx\0" \ + "fdt_file=" CONFIG_DEFAULT_FDT_FILE "\0" \ + "fdt_addr="__stringify(TQMA6_FDT_ADDRESS)"\0" \ + "console=" CONFIG_CONSOLE_DEV "\0" \ + "fdt_high=0xffffffff\0" \ + "initrd_high=0xffffffff\0" \ + "addtty=setenv bootargs ${bootargs} console=${console},${baudrate}\0" \ + "addfb=setenv bootargs ${bootargs} " \ + "imx-fbdev.legacyfb_depth=32 consoleblank=0\0" \ + "mmcpart=2\0" \ + "mmcblkdev=0\0" \ + "mmcargs=run addmmc addtty addfb\0" \ + "addmmc=setenv bootargs ${bootargs} " \ + "root=/dev/mmcblk${mmcblkdev}p${mmcpart} rw rootwait\0" \ + "mmcboot=echo Booting from mmc ...; " \ + "setenv bootargs; " \ + "run mmcargs; " \ + "run loadimage; " \ + "if run loadfdt; then " \ + "echo boot device tree kernel ...; " \ + "${boot_type} ${loadaddr} - ${fdt_addr}; " \ + "else " \ + "${boot_type}; " \ + "fi;\0" \ + "setenv bootargs \0" \ + "netdev=eth0\0" \ + "rootpath=/srv/nfs/tqma6\0" \ + "ipmode=static\0" \ + "netargs=run addnfs addip addtty addfb\0" \ + "addnfs=setenv bootargs ${bootargs} " \ + "root=/dev/nfs rw " \ + "nfsroot=${serverip}:${rootpath},v3,tcp;\0" \ + "addip_static=setenv bootargs ${bootargs} " \ + "ip=${ipaddr}:${serverip}:${gatewayip}:${netmask}:" \ + "${hostname}:${netdev}:off\0" \ + "addip_dynamic=setenv bootargs ${bootargs} ip=dhcp\0" \ + "addip=if test \"${ipmode}\" != static; then " \ + "run addip_dynamic; else run addip_static; fi\0" \ + "set_getcmd=if test \"${ipmode}\" != static; then " \ + "setenv getcmd dhcp; setenv autoload yes; " \ + "else setenv getcmd tftp; setenv autoload no; fi\0" \ + "netboot=echo Booting from net ...; " \ + "run kernel_name; " \ + "run set_getcmd; " \ + "setenv bootargs; " \ + "run netargs; " \ + "if ${getcmd} ${kernel}; then " \ + "if ${getcmd} ${fdt_addr} ${fdt_file}; then " \ + "${boot_type} ${loadaddr} - ${fdt_addr}; " \ + "fi; " \ + "fi; " \ + "echo ... failed\0" \ + "panicboot=echo No boot device !!! reset\0" \ + TQMA6_EXTRA_BOOTDEV_ENV_SETTINGS \ + +/* Miscellaneous configurable options */ +#define CONFIG_SYS_LONGHELP +#define CONFIG_SYS_HUSH_PARSER +#define CONFIG_SYS_PROMPT_HUSH_PS2 "> " + +#define CONFIG_AUTO_COMPLETE +#define CONFIG_SYS_CBSIZE 512 + +/* Print Buffer Size */ +#define CONFIG_SYS_PBSIZE (CONFIG_SYS_CBSIZE + \ + sizeof(CONFIG_SYS_PROMPT) + 16) +#define CONFIG_SYS_MAXARGS 16 +#define CONFIG_SYS_BARGSIZE CONFIG_SYS_CBSIZE + +#define CONFIG_SYS_LOAD_ADDR CONFIG_LOADADDR +#define CONFIG_SYS_HZ 1000 + +#define CONFIG_CMDLINE_EDITING +#define CONFIG_STACKSIZE (128u * SZ_1K) + +/* Physical Memory Map */ +#define CONFIG_NR_DRAM_BANKS 1 +#define PHYS_SDRAM MMDC0_ARB_BASE_ADDR + +#define CONFIG_SYS_SDRAM_BASE PHYS_SDRAM +#define CONFIG_SYS_INIT_RAM_ADDR IRAM_BASE_ADDR +#define CONFIG_SYS_INIT_RAM_SIZE IRAM_SIZE + +#define CONFIG_SYS_INIT_SP_OFFSET \ + (CONFIG_SYS_INIT_RAM_SIZE - GENERATED_GBL_DATA_SIZE) +#define CONFIG_SYS_INIT_SP_ADDR \ + (CONFIG_SYS_INIT_RAM_ADDR + CONFIG_SYS_INIT_SP_OFFSET) + +/* FLASH and environment organization */ +#define CONFIG_SYS_NO_FLASH + +#define CONFIG_OF_LIBFDT +#define CONFIG_OF_BOARD_SETUP +#define CONFIG_FIT +#define CONFIG_FIT_VERBOSE + +#ifndef CONFIG_SYS_DCACHE_OFF +#define CONFIG_CMD_CACHE +#endif + +#endif /* __CONFIG_H */ diff --git a/include/configs/trats.h b/include/configs/trats.h index 90f1962..7db1db6 100644 --- a/include/configs/trats.h +++ b/include/configs/trats.h @@ -102,9 +102,9 @@ #define CONFIG_DFU_ALT \ "u-boot raw 0x80 0x400;" \ - "uImage ext4 0 2;" \ - "modem.bin ext4 0 2;" \ - "exynos4210-trats.dtb ext4 0 2;" \ + "/uImage ext4 0 2;" \ + "/modem.bin ext4 0 2;" \ + "/exynos4210-trats.dtb ext4 0 2;" \ ""PARTS_CSA" part 0 1;" \ ""PARTS_BOOT" part 0 2;" \ ""PARTS_QBOOT" part 0 3;" \ diff --git a/include/configs/trats2.h b/include/configs/trats2.h index 206975b..f537e4f 100644 --- a/include/configs/trats2.h +++ b/include/configs/trats2.h @@ -92,9 +92,9 @@ #define CONFIG_DFU_ALT \ "u-boot raw 0x80 0x800;" \ - "uImage ext4 0 2;" \ - "modem.bin ext4 0 2;" \ - "exynos4412-trats2.dtb ext4 0 2;" \ + "/uImage ext4 0 2;" \ + "/modem.bin ext4 0 2;" \ + "/exynos4412-trats2.dtb ext4 0 2;" \ ""PARTS_CSA" part 0 1;" \ ""PARTS_BOOT" part 0 2;" \ ""PARTS_QBOOT" part 0 3;" \ diff --git a/include/dfu.h b/include/dfu.h index 26ffbc8..7e0a999 100644 --- a/include/dfu.h +++ b/include/dfu.h @@ -14,6 +14,7 @@ #include <common.h> #include <linux/list.h> #include <mmc.h> +#include <spi_flash.h> #include <linux/usb/composite.h> enum dfu_device_type { @@ -21,6 +22,7 @@ enum dfu_device_type { DFU_DEV_ONENAND, DFU_DEV_NAND, DFU_DEV_RAM, + DFU_DEV_SF, }; enum dfu_layout { @@ -35,9 +37,12 @@ enum dfu_layout { enum dfu_op { DFU_OP_READ = 1, DFU_OP_WRITE, + DFU_OP_SIZE, }; struct mmc_internal_data { + int dev_num; + /* RAW programming */ unsigned int lba_start; unsigned int lba_size; @@ -67,6 +72,14 @@ struct ram_internal_data { unsigned int size; }; +struct sf_internal_data { + struct spi_flash *dev; + + /* RAW programming */ + u64 start; + u64 size; +}; + #define DFU_NAME_SIZE 32 #define DFU_CMD_BUF_SIZE 128 #ifndef CONFIG_SYS_DFU_DATA_BUF_SIZE @@ -86,16 +99,19 @@ struct dfu_entity { char name[DFU_NAME_SIZE]; int alt; void *dev_private; - int dev_num; enum dfu_device_type dev_type; enum dfu_layout layout; + unsigned long max_buf_size; union { struct mmc_internal_data mmc; struct nand_internal_data nand; struct ram_internal_data ram; + struct sf_internal_data sf; } data; + long (*get_medium_size)(struct dfu_entity *dfu); + int (*read_medium)(struct dfu_entity *dfu, u64 offset, void *buf, long *len); @@ -105,6 +121,8 @@ struct dfu_entity { int (*flush_medium)(struct dfu_entity *dfu); unsigned int (*poll_timeout)(struct dfu_entity *dfu); + void (*free_entity)(struct dfu_entity *dfu); + struct list_head list; /* on the fly state */ @@ -122,7 +140,7 @@ struct dfu_entity { unsigned int inited:1; }; -int dfu_config_entities(char *s, char *interface, int num); +int dfu_config_entities(char *s, char *interface, char *devstr); void dfu_free_entities(void); void dfu_show_entities(void); int dfu_get_alt_number(void); @@ -133,8 +151,8 @@ char *dfu_extract_token(char** e, int *n); void dfu_trigger_reset(void); int dfu_get_alt(char *name); bool dfu_reset(void); -int dfu_init_env_entities(char *interface, int dev); -unsigned char *dfu_get_buf(void); +int dfu_init_env_entities(char *interface, char *devstr); +unsigned char *dfu_get_buf(struct dfu_entity *dfu); unsigned char *dfu_free_buf(void); unsigned long dfu_get_buf_size(void); @@ -143,9 +161,10 @@ int dfu_write(struct dfu_entity *de, void *buf, int size, int blk_seq_num); int dfu_flush(struct dfu_entity *de, void *buf, int size, int blk_seq_num); /* Device specific */ #ifdef CONFIG_DFU_MMC -extern int dfu_fill_entity_mmc(struct dfu_entity *dfu, char *s); +extern int dfu_fill_entity_mmc(struct dfu_entity *dfu, char *devstr, char *s); #else -static inline int dfu_fill_entity_mmc(struct dfu_entity *dfu, char *s) +static inline int dfu_fill_entity_mmc(struct dfu_entity *dfu, char *devstr, + char *s) { puts("MMC support not available!\n"); return -1; @@ -153,9 +172,10 @@ static inline int dfu_fill_entity_mmc(struct dfu_entity *dfu, char *s) #endif #ifdef CONFIG_DFU_NAND -extern int dfu_fill_entity_nand(struct dfu_entity *dfu, char *s); +extern int dfu_fill_entity_nand(struct dfu_entity *dfu, char *devstr, char *s); #else -static inline int dfu_fill_entity_nand(struct dfu_entity *dfu, char *s) +static inline int dfu_fill_entity_nand(struct dfu_entity *dfu, char *devstr, + char *s) { puts("NAND support not available!\n"); return -1; @@ -163,14 +183,26 @@ static inline int dfu_fill_entity_nand(struct dfu_entity *dfu, char *s) #endif #ifdef CONFIG_DFU_RAM -extern int dfu_fill_entity_ram(struct dfu_entity *dfu, char *s); +extern int dfu_fill_entity_ram(struct dfu_entity *dfu, char *devstr, char *s); #else -static inline int dfu_fill_entity_ram(struct dfu_entity *dfu, char *s) +static inline int dfu_fill_entity_ram(struct dfu_entity *dfu, char *devstr, + char *s) { puts("RAM support not available!\n"); return -1; } #endif +#ifdef CONFIG_DFU_SF +extern int dfu_fill_entity_sf(struct dfu_entity *dfu, char *devstr, char *s); +#else +static inline int dfu_fill_entity_sf(struct dfu_entity *dfu, char *devstr, + char *s) +{ + puts("SF support not available!\n"); + return -1; +} +#endif + int dfu_add(struct usb_configuration *c); #endif /* __DFU_ENTITY_H_ */ diff --git a/include/ext4fs.h b/include/ext4fs.h index fbbb002..6c419f3 100644 --- a/include/ext4fs.h +++ b/include/ext4fs.h @@ -136,6 +136,7 @@ void ext4fs_close(void); void ext4fs_reinit_global(void); int ext4fs_ls(const char *dirname); int ext4fs_exists(const char *filename); +int ext4fs_size(const char *filename); void ext4fs_free_node(struct ext2fs_node *node, struct ext2fs_node *currroot); int ext4fs_devread(lbaint_t sector, int byte_offset, int byte_len, char *buf); void ext4fs_set_blk_dev(block_dev_desc_t *rbdd, disk_partition_t *info); diff --git a/include/fat.h b/include/fat.h index 63cf787..20ca3f3 100644 --- a/include/fat.h +++ b/include/fat.h @@ -198,6 +198,7 @@ int file_cd(const char *path); int file_fat_detectfs(void); int file_fat_ls(const char *dir); int fat_exists(const char *filename); +int fat_size(const char *filename); long file_fat_read_at(const char *filename, unsigned long pos, void *buffer, unsigned long maxsize); long file_fat_read(const char *filename, void *buffer, unsigned long maxsize); diff --git a/include/fdt_support.h b/include/fdt_support.h index fd44d7e..1bda686 100644 --- a/include/fdt_support.h +++ b/include/fdt_support.h @@ -76,7 +76,7 @@ void ft_cpu_setup(void *blob, bd_t *bd); void ft_pci_setup(void *blob, bd_t *bd); void set_working_fdt_addr(void *addr); -int fdt_resize(void *blob); +int fdt_shrink_to_minimum(void *blob); int fdt_increase_size(void *fdt, int add_len); int fdt_fixup_nor_flash_size(void *blob); diff --git a/include/fs.h b/include/fs.h index 26de053..06a45f2 100644 --- a/include/fs.h +++ b/include/fs.h @@ -51,6 +51,13 @@ int fs_ls(const char *dirname); int fs_exists(const char *filename); /* + * Determine a file's size + * + * Returns the file's size in bytes, or a negative value if it doesn't exist. + */ +int fs_size(const char *filename); + +/* * Read file "filename" from the partition previously set by fs_set_blk_dev(), * to address "addr", starting at byte offset "offset", and reading "len" * bytes. "offset" may be 0 to read from the start of the file. "len" may be @@ -75,6 +82,8 @@ int fs_write(const char *filename, ulong addr, int offset, int len); * Common implementation for various filesystem commands, optionally limited * to a specific filesystem type via the fstype parameter. */ +int do_size(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[], + int fstype); int do_load(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[], int fstype); int do_ls(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[], diff --git a/include/image.h b/include/image.h index 3e8f78d..3401056 100644 --- a/include/image.h +++ b/include/image.h @@ -412,6 +412,7 @@ void genimg_print_time(time_t timestamp); enum fit_load_op { FIT_LOAD_IGNORED, /* Ignore load address */ FIT_LOAD_OPTIONAL, /* Can be provided, but optional */ + FIT_LOAD_OPTIONAL_NON_ZERO, /* Optional, a value of 0 is ignored */ FIT_LOAD_REQUIRED, /* Must be provided */ }; @@ -424,6 +425,10 @@ enum fit_load_op { #define IMAGE_FORMAT_FIT 0x02 /* new, libfdt based format */ #define IMAGE_FORMAT_ANDROID 0x03 /* Android boot image */ +ulong genimg_get_kernel_addr_fit(char * const img_addr, + const char **fit_uname_config, + const char **fit_uname_kernel); +ulong genimg_get_kernel_addr(char * const img_addr); int genimg_get_format(const void *img_addr); int genimg_has_config(bootm_headers_t *images); ulong genimg_get_image(ulong img_addr); diff --git a/include/lcd.h b/include/lcd.h index cc2ee3f..ea5860c 100644 --- a/include/lcd.h +++ b/include/lcd.h @@ -26,8 +26,6 @@ void lcd_enable(void); void lcd_setcolreg(ushort regno, ushort red, ushort green, ushort blue); void lcd_initcolregs(void); -int lcd_getfgcolor(void); - /* gunzip_bmp used if CONFIG_VIDEO_BMP_GZIP */ struct bmp_image *gunzip_bmp(unsigned long addr, unsigned long *lenp, void **alloc_addr); @@ -333,7 +331,7 @@ void lcd_sync(void); #define LCD_COLOR4 2 #define LCD_COLOR8 3 #define LCD_COLOR16 4 - +#define LCD_COLOR32 5 /*----------------------------------------------------------------------*/ #if defined(CONFIG_LCD_INFO_BELOW_LOGO) # define LCD_INFO_X 0 @@ -384,6 +382,21 @@ void lcd_sync(void); # define CONSOLE_COLOR_GREY 14 # define CONSOLE_COLOR_WHITE 15 /* Must remain last / highest */ +#elif LCD_BPP == LCD_COLOR32 +/* + * 32bpp color definitions + */ +# define CONSOLE_COLOR_RED 0x00ff0000 +# define CONSOLE_COLOR_GREEN 0x0000ff00 +# define CONSOLE_COLOR_YELLOW 0x00ffff00 +# define CONSOLE_COLOR_BLUE 0x000000ff +# define CONSOLE_COLOR_MAGENTA 0x00ff00ff +# define CONSOLE_COLOR_CYAN 0x0000ffff +# define CONSOLE_COLOR_GREY 0x00aaaaaa +# define CONSOLE_COLOR_BLACK 0x00000000 +# define CONSOLE_COLOR_WHITE 0x00ffffff /* Must remain last / highest*/ +# define NBYTES(bit_code) (NBITS(bit_code) >> 3) + #else /* diff --git a/include/libfdt.h b/include/libfdt.h index 9eefaaf..a1ef1e1 100644 --- a/include/libfdt.h +++ b/include/libfdt.h @@ -116,7 +116,12 @@ * Should never be returned, if it is, it indicates a bug in * libfdt itself. */ -#define FDT_ERR_MAX 13 +/* Errors in device tree content */ +#define FDT_ERR_BADNCELLS 14 + /* FDT_ERR_BADNCELLS: Device tree has a #address-cells, #size-cells + * or similar property with a bad format or value */ + +#define FDT_ERR_MAX 14 /**********************************************************************/ /* Low-level functions (you probably don't need these) */ @@ -596,9 +601,9 @@ const char *fdt_get_alias_namelen(const void *fdt, const char *name, int namelen); /** - * fdt_get_alias - retrieve the path referenced by a given alias + * fdt_get_alias - retreive the path referenced by a given alias * @fdt: pointer to the device tree blob - * @name: name of the alias to look up + * @name: name of the alias th look up * * fdt_get_alias() retrieves the value of a given alias. That is, the * value of the property named 'name' in the node /aliases. @@ -731,7 +736,7 @@ int fdt_parent_offset(const void *fdt, int nodeoffset); * offset = fdt_node_offset_by_prop_value(fdt, -1, propname, * propval, proplen); * while (offset != -FDT_ERR_NOTFOUND) { - * ... other code here ... + * // other code here * offset = fdt_node_offset_by_prop_value(fdt, offset, propname, * propval, proplen); * } @@ -816,7 +821,7 @@ int fdt_node_check_compatible(const void *fdt, int nodeoffset, * idiom can be used: * offset = fdt_node_offset_by_compatible(fdt, -1, compatible); * while (offset != -FDT_ERR_NOTFOUND) { - * ... other code here ... + * // other code here * offset = fdt_node_offset_by_compatible(fdt, offset, compatible); * } * @@ -853,6 +858,63 @@ int fdt_node_offset_by_compatible(const void *fdt, int startoffset, int fdt_stringlist_contains(const char *strlist, int listlen, const char *str); /**********************************************************************/ +/* Read-only functions (addressing related) */ +/**********************************************************************/ + +/** + * FDT_MAX_NCELLS - maximum value for #address-cells and #size-cells + * + * This is the maximum value for #address-cells, #size-cells and + * similar properties that will be processed by libfdt. IEE1275 + * requires that OF implementations handle values up to 4. + * Implementations may support larger values, but in practice higher + * values aren't used. + */ +#define FDT_MAX_NCELLS 4 + +/** + * fdt_address_cells - retrieve address size for a bus represented in the tree + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node to find the address size for + * + * When the node has a valid #address-cells property, returns its value. + * + * returns: + * 0 <= n < FDT_MAX_NCELLS, on success + * 2, if the node has no #address-cells property + * -FDT_ERR_BADNCELLS, if the node has a badly formatted or invalid + * #address-cells property + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings + */ +int fdt_address_cells(const void *fdt, int nodeoffset); + +/** + * fdt_size_cells - retrieve address range size for a bus represented in the + * tree + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node to find the address range size for + * + * When the node has a valid #size-cells property, returns its value. + * + * returns: + * 0 <= n < FDT_MAX_NCELLS, on success + * 2, if the node has no #address-cells property + * -FDT_ERR_BADNCELLS, if the node has a badly formatted or invalid + * #size-cells property + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings + */ +int fdt_size_cells(const void *fdt, int nodeoffset); + + +/**********************************************************************/ /* Write-in-place functions */ /**********************************************************************/ @@ -1023,6 +1085,7 @@ int fdt_nop_node(void *fdt, int nodeoffset); /**********************************************************************/ int fdt_create(void *buf, int bufsize); +int fdt_resize(void *fdt, void *buf, int bufsize); int fdt_add_reservemap_entry(void *fdt, uint64_t addr, uint64_t size); int fdt_finish_reservemap(void *fdt); int fdt_begin_node(void *fdt, const char *name); diff --git a/include/linux/compat.h b/include/linux/compat.h index 35e216e..7ff6064 100644 --- a/include/linux/compat.h +++ b/include/linux/compat.h @@ -1,6 +1,19 @@ #ifndef _LINUX_COMPAT_H_ #define _LINUX_COMPAT_H_ +#include <malloc.h> +#include <linux/types.h> +#include <linux/err.h> + +struct unused {}; +typedef struct unused unused_t; + +struct p_current{ + int pid; +}; + +extern struct p_current *current; + #define ndelay(x) udelay(1) #define dev_dbg(dev, fmt, args...) \ @@ -12,6 +25,7 @@ #define dev_err(dev, fmt, args...) \ printf(fmt, ##args) #define printk printf +#define printk_once printf #define KERN_EMERG #define KERN_ALERT @@ -22,11 +36,20 @@ #define KERN_INFO #define KERN_DEBUG -#define kmalloc(size, flags) malloc(size) -#define kzalloc(size, flags) calloc(size, 1) -#define vmalloc(size) malloc(size) -#define kfree(ptr) free(ptr) -#define vfree(ptr) free(ptr) +void *kmalloc(size_t size, int flags); +void *kzalloc(size_t size, int flags); +#define vmalloc(size) kmalloc(size, 0) +#define __vmalloc(size, flags, pgsz) kmalloc(size, flags) +#define kfree(ptr) free(ptr) +#define vfree(ptr) free(ptr) + +struct kmem_cache { int sz; }; + +struct kmem_cache *get_mem(int element_sz); +#define kmem_cache_create(a, sz, c, d, e) get_mem(sz) +void *kmem_cache_alloc(struct kmem_cache *obj, int flag); +#define kmem_cache_free(obj, size) free(size) +#define kmem_cache_destroy(obj) free(obj) #define DECLARE_WAITQUEUE(...) do { } while (0) #define add_wait_queue(...) do { } while (0) @@ -76,4 +99,300 @@ */ #define lower_32_bits(n) ((u32)(n)) +/* drivers/char/random.c */ +#define get_random_bytes(...) + +/* idr.c */ +#define GFP_ATOMIC ((gfp_t) 0) +#define GFP_KERNEL ((gfp_t) 0) +#define GFP_NOFS ((gfp_t) 0) +#define GFP_USER ((gfp_t) 0) +#define __GFP_NOWARN ((gfp_t) 0) + +/* include/linux/leds.h */ +struct led_trigger {}; + +#define DEFINE_LED_TRIGGER(x) static struct led_trigger *x; +enum led_brightness { + LED_OFF = 0, + LED_HALF = 127, + LED_FULL = 255, +}; + +static inline void led_trigger_register_simple(const char *name, + struct led_trigger **trigger) {} +static inline void led_trigger_unregister_simple(struct led_trigger *trigger) {} +static inline void led_trigger_event(struct led_trigger *trigger, + enum led_brightness event) {} + +/* include/linux/log2.h */ +static inline int is_power_of_2(unsigned long n) +{ + return (n != 0 && ((n & (n - 1)) == 0)); +} + +/* uapi/linux/limits.h */ +#define XATTR_LIST_MAX 65536 /* size of extended attribute namelist (64k) */ + +/** + * The type used for indexing onto a disc or disc partition. + * + * Linux always considers sectors to be 512 bytes long independently + * of the devices real block size. + * + * blkcnt_t is the type of the inode's block count. + */ +#ifdef CONFIG_LBDAF +typedef u64 sector_t; +typedef u64 blkcnt_t; +#else +typedef unsigned long sector_t; +typedef unsigned long blkcnt_t; +#endif + +#define ENOTSUPP 524 /* Operation is not supported */ + +/* from include/linux/kernel.h */ +/* + * This looks more complex than it should be. But we need to + * get the type for the ~ right in round_down (it needs to be + * as wide as the result!), and we want to evaluate the macro + * arguments just once each. + */ +#define __round_mask(x, y) ((__typeof__(x))((y)-1)) +#define round_up(x, y) ((((x)-1) | __round_mask(x, y))+1) +#define round_down(x, y) ((x) & ~__round_mask(x, y)) + +/* module */ +#define THIS_MODULE 0 +#define try_module_get(...) 1 +#define module_put(...) do { } while (0) +#define module_init(...) +#define module_exit(...) +#define EXPORT_SYMBOL(...) +#define EXPORT_SYMBOL_GPL(...) +#define module_param(...) +#define module_param_call(...) +#define MODULE_PARM_DESC(...) +#define MODULE_VERSION(...) +#define MODULE_DESCRIPTION(...) +#define MODULE_AUTHOR(...) +#define MODULE_LICENSE(...) +#define MODULE_ALIAS(...) +#define __module_get(...) + +/* character device */ +#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(...) + +#define blocking_notifier_call_chain(...) 0 + +/* + * Multiplies an integer by a fraction, while avoiding unnecessary + * overflow or loss of precision. + */ +#define mult_frac(x, numer, denom)( \ +{ \ + typeof(x) quot = (x) / (denom); \ + typeof(x) rem = (x) % (denom); \ + (quot * (numer)) + ((rem * (numer)) / (denom)); \ +} \ +) + +#define __initdata +#define late_initcall(...) + +#define dev_set_name(...) do { } while (0) +#define device_register(...) 0 +#define volume_sysfs_init(...) 0 +#define volume_sysfs_close(...) do { } while (0) + +#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) + +#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) + +#define setup_timer(timer, func, data) do {} while (0) +#define del_timer_sync(timer) do {} while (0) +#define schedule_work(work) do {} while (0) +#define INIT_WORK(work, fun) do {} while (0) + +struct work_struct {}; + +unsigned long copy_from_user(void *dest, const void *src, + unsigned long count); + +void *vzalloc(unsigned long size); + +typedef unused_t spinlock_t; +typedef int wait_queue_head_t; + +#define spin_lock_init(lock) do {} while (0) +#define spin_lock(lock) do {} while (0) +#define spin_unlock(lock) do {} while (0) +#define spin_lock_irqsave(lock, flags) do { debug("%lu\n", flags); } while (0) +#define spin_unlock_irqrestore(lock, flags) do { flags = 0; } while (0) + +#define DEFINE_MUTEX(...) +#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(...) 1 +#define up_read(...) do { } while (0) +#define up_write(...) do { } while (0) + +#define cond_resched() do { } while (0) +#define yield() do { } while (0) + +#define INT_MAX ((int)(~0U>>1)) + +#define __user +#define __init +#define __exit +#define __devinit +#define __devinitdata +#define __devinitconst +#define __iomem + +#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; }; +#define down_write(...) do { } while (0) +#define up_write(...) do { } while (0) +#define down_read(...) do { } while (0) +#define up_read(...) do { } while (0) +struct device { + struct device *parent; + struct class *class; + dev_t devt; /* dev_t, creates the sysfs "dev" */ + void (*release)(struct device *dev); + /* This is used from drivers/usb/musb-new subsystem only */ + void *driver_data; /* data private to the driver */ + void *device_data; /* data private to the device */ +}; +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 prandom_u32(...) 0 + +typedef struct { + uid_t val; +} kuid_t; + +typedef struct { + gid_t val; +} kgid_t; + +/* from include/linux/types.h */ + +typedef int atomic_t; +/** + * struct callback_head - callback structure for use with RCU and task_work + * @next: next update requests in a list + * @func: actual update function to call after the grace period. + */ +struct callback_head { + struct callback_head *next; + void (*func)(struct callback_head *head); +}; +#define rcu_head callback_head +enum writeback_sync_modes { + WB_SYNC_NONE, /* Don't wait on anything */ + WB_SYNC_ALL, /* Wait on every mapping */ +}; + +/* from include/linux/writeback.h */ +/* + * A control structure which tells the writeback code what to do. These are + * always on the stack, and hence need no locking. They are always initialised + * in a manner such that unspecified fields are set to zero. + */ +struct writeback_control { + long nr_to_write; /* Write this many pages, and decrement + this for each page written */ + long pages_skipped; /* Pages which were not written */ + + /* + * For a_ops->writepages(): if start or end are non-zero then this is + * a hint that the filesystem need only write out the pages inside that + * byterange. The byte at `end' is included in the writeout request. + */ + loff_t range_start; + loff_t range_end; + + enum writeback_sync_modes sync_mode; + + unsigned for_kupdate:1; /* A kupdate writeback */ + unsigned for_background:1; /* A background writeback */ + unsigned tagged_writepages:1; /* tag-and-write to avoid livelock */ + unsigned for_reclaim:1; /* Invoked from the page allocator */ + unsigned range_cyclic:1; /* range_start is cyclic */ + unsigned for_sync:1; /* sync(2) WB_SYNC_ALL writeback */ +}; + +void *kmemdup(const void *src, size_t len, gfp_t gfp); + +typedef int irqreturn_t; + +struct timer_list {}; +struct notifier_block {}; + +typedef unsigned long dmaaddr_t; + +#define cpu_relax() do {} while (0) + +#define pm_runtime_get_sync(dev) do {} while (0) +#define pm_runtime_put(dev) do {} while (0) +#define pm_runtime_put_sync(dev) do {} while (0) +#define pm_runtime_use_autosuspend(dev) do {} while (0) +#define pm_runtime_set_autosuspend_delay(dev, delay) do {} while (0) +#define pm_runtime_enable(dev) do {} while (0) + +#define IRQ_NONE 0 +#define IRQ_HANDLED 1 + +#define dev_set_drvdata(dev, data) do {} while (0) + +#define enable_irq(...) +#define disable_irq(...) +#define disable_irq_wake(irq) do {} while (0) +#define enable_irq_wake(irq) -EINVAL +#define free_irq(irq, data) do {} while (0) +#define request_irq(nr, f, flags, nm, data) 0 + #endif diff --git a/include/linux/err.h b/include/linux/err.h index 96c0c72..5b3c8bc 100644 --- a/include/linux/err.h +++ b/include/linux/err.h @@ -1,12 +1,8 @@ #ifndef _LINUX_ERR_H #define _LINUX_ERR_H -/* XXX U-BOOT XXX */ -#if 0 #include <linux/compiler.h> -#else #include <linux/compat.h> -#endif #include <asm/errno.h> @@ -40,6 +36,19 @@ static inline long IS_ERR(const void *ptr) return IS_ERR_VALUE((unsigned long)ptr); } +/** + * ERR_CAST - Explicitly cast an error-valued pointer to another pointer type + * @ptr: The pointer to cast. + * + * Explicitly cast an error-valued pointer to another pointer type in such a + * way as to make it clear that's what's going on. + */ +static inline void * __must_check ERR_CAST(__force const void *ptr) +{ + /* cast away the const */ + return (void *) ptr; +} + #endif #endif /* _LINUX_ERR_H */ diff --git a/include/linux/list_sort.h b/include/linux/list_sort.h new file mode 100644 index 0000000..1a2df2e --- /dev/null +++ b/include/linux/list_sort.h @@ -0,0 +1,11 @@ +#ifndef _LINUX_LIST_SORT_H +#define _LINUX_LIST_SORT_H + +#include <linux/types.h> + +struct list_head; + +void list_sort(void *priv, struct list_head *head, + int (*cmp)(void *priv, struct list_head *a, + struct list_head *b)); +#endif diff --git a/include/linux/mtd/bbm.h b/include/linux/mtd/bbm.h index 25a3d3a..be81d38 100644 --- a/include/linux/mtd/bbm.h +++ b/include/linux/mtd/bbm.h @@ -4,13 +4,14 @@ * NAND family Bad Block Management (BBM) header file * - Bad Block Table (BBT) implementation * - * Copyright (c) 2005-2007 Samsung Electronics + * Copyright © 2005 Samsung Electronics * Kyungmin Park <kyungmin.park@samsung.com> * - * Copyright (c) 2000-2005 + * Copyright © 2000-2005 * Thomas Gleixner <tglx@linuxtronix.de> * * SPDX-License-Identifier: GPL-2.0+ + * */ #ifndef __LINUX_MTD_BBM_H #define __LINUX_MTD_BBM_H @@ -22,22 +23,21 @@ /** * struct nand_bbt_descr - bad block table descriptor - * @param options options for this descriptor - * @param pages the page(s) where we find the bbt, used with - * option BBT_ABSPAGE when bbt is searched, - * then we store the found bbts pages here. - * Its an array and supports up to 8 chips now - * @param offs offset of the pattern in the oob area of the page - * @param veroffs offset of the bbt version counter in the oob are of the page - * @param version version read from the bbt page during scan - * @param len length of the pattern, if 0 no pattern check is performed - * @param maxblocks maximum number of blocks to search for a bbt. This number of - * blocks is reserved at the end of the device - * where the tables are written. - * @param reserved_block_code if non-0, this pattern denotes a reserved - * (rather than bad) block in the stored bbt - * @param pattern pattern to identify bad block table or factory marked - * good / bad blocks, can be NULL, if len = 0 + * @options: options for this descriptor + * @pages: the page(s) where we find the bbt, used with option BBT_ABSPAGE + * when bbt is searched, then we store the found bbts pages here. + * Its an array and supports up to 8 chips now + * @offs: offset of the pattern in the oob area of the page + * @veroffs: offset of the bbt version counter in the oob are of the page + * @version: version read from the bbt page during scan + * @len: length of the pattern, if 0 no pattern check is performed + * @maxblocks: maximum number of blocks to search for a bbt. This number of + * blocks is reserved at the end of the device where the tables are + * written. + * @reserved_block_code: if non-0, this pattern denotes a reserved (rather than + * bad) block in the stored bbt + * @pattern: pattern to identify bad block table or factory marked good / + * bad blocks, can be NULL, if len = 0 * * Descriptor for the bad block table marker and the descriptor for the * pattern which identifies good and bad blocks. The assumption is made @@ -81,10 +81,6 @@ struct nand_bbt_descr { * with NAND_BBT_CREATE. */ #define NAND_BBT_CREATE_EMPTY 0x00000400 -/* Search good / bad pattern through all pages of a block */ -#define NAND_BBT_SCANALLPAGES 0x00000800 -/* Scan block empty during good / bad block scan */ -#define NAND_BBT_SCANEMPTY 0x00001000 /* Write bbt if neccecary */ #define NAND_BBT_WRITE 0x00002000 /* Read and write back block contents when writing bbt */ @@ -122,22 +118,27 @@ struct nand_bbt_descr { /* * Constants for oob configuration */ -#define ONENAND_BADBLOCK_POS 0 +#define NAND_SMALL_BADBLOCK_POS 5 +#define NAND_LARGE_BADBLOCK_POS 0 +#define ONENAND_BADBLOCK_POS 0 /* * Bad block scanning errors */ -#define ONENAND_BBT_READ_ERROR 1 -#define ONENAND_BBT_READ_ECC_ERROR 2 -#define ONENAND_BBT_READ_FATAL_ERROR 4 +#define ONENAND_BBT_READ_ERROR 1 +#define ONENAND_BBT_READ_ECC_ERROR 2 +#define ONENAND_BBT_READ_FATAL_ERROR 4 /** - * struct bbt_info - [GENERIC] Bad Block Table data structure - * @param bbt_erase_shift [INTERN] number of address bits in a bbt entry - * @param badblockpos [INTERN] position of the bad block marker in the oob area - * @param bbt [INTERN] bad block table pointer - * @param badblock_pattern [REPLACEABLE] bad block scan pattern used for initial bad block scan - * @param priv [OPTIONAL] pointer to private bbm date + * struct bbm_info - [GENERIC] Bad Block Table data structure + * @bbt_erase_shift: [INTERN] number of address bits in a bbt entry + * @badblockpos: [INTERN] position of the bad block marker in the oob area + * @options: options for this descriptor + * @bbt: [INTERN] bad block table pointer + * @isbad_bbt: function to determine if a block is bad + * @badblock_pattern: [REPLACEABLE] bad block scan pattern used for + * initial bad block scan + * @priv: [OPTIONAL] pointer to private bbm date */ struct bbm_info { int bbt_erase_shift; @@ -146,7 +147,7 @@ struct bbm_info { uint8_t *bbt; - int (*isbad_bbt) (struct mtd_info * mtd, loff_t ofs, int allowbbt); + int (*isbad_bbt)(struct mtd_info *mtd, loff_t ofs, int allowbbt); /* TODO Add more NAND specific fileds */ struct nand_bbt_descr *badblock_pattern; @@ -155,7 +156,7 @@ struct bbm_info { }; /* OneNAND BBT interface */ -extern int onenand_scan_bbt (struct mtd_info *mtd, struct nand_bbt_descr *bd); -extern int onenand_default_bbt (struct mtd_info *mtd); +extern int onenand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd); +extern int onenand_default_bbt(struct mtd_info *mtd); -#endif /* __LINUX_MTD_BBM_H */ +#endif /* __LINUX_MTD_BBM_H */ diff --git a/include/linux/mtd/concat.h b/include/linux/mtd/concat.h index c92b4dd..195a4a5 100644 --- a/include/linux/mtd/concat.h +++ b/include/linux/mtd/concat.h @@ -12,7 +12,11 @@ struct mtd_info *mtd_concat_create( struct mtd_info *subdev[], /* subdevices to concatenate */ int num_devs, /* number of subdevices */ +#ifndef __UBOOT__ const char *name); /* name for the new device */ +#else + char *name); /* name for the new device */ +#endif void mtd_concat_destroy(struct mtd_info *mtd); diff --git a/include/linux/mtd/flashchip.h b/include/linux/mtd/flashchip.h new file mode 100644 index 0000000..7028ee1 --- /dev/null +++ b/include/linux/mtd/flashchip.h @@ -0,0 +1,105 @@ +/* + * Copyright © 2000 Red Hat UK Limited + * Copyright © 2000-2010 David Woodhouse <dwmw2@infradead.org> + * + * SPDX-License-Identifier: GPL-2.0+ + * + */ + +#ifndef __MTD_FLASHCHIP_H__ +#define __MTD_FLASHCHIP_H__ + +#define __UBOOT__ +#ifndef __UBOOT__ +/* For spinlocks. sched.h includes spinlock.h from whichever directory it + * happens to be in - so we don't have to care whether we're on 2.2, which + * has asm/spinlock.h, or 2.4, which has linux/spinlock.h + */ +#include <linux/sched.h> +#include <linux/mutex.h> +#endif + +typedef enum { + FL_READY, + FL_STATUS, + FL_CFI_QUERY, + FL_JEDEC_QUERY, + FL_ERASING, + FL_ERASE_SUSPENDING, + FL_ERASE_SUSPENDED, + FL_WRITING, + FL_WRITING_TO_BUFFER, + FL_OTP_WRITE, + FL_WRITE_SUSPENDING, + FL_WRITE_SUSPENDED, + FL_PM_SUSPENDED, + FL_SYNCING, + FL_UNLOADING, + FL_LOCKING, + FL_UNLOCKING, + FL_POINT, + FL_XIP_WHILE_ERASING, + FL_XIP_WHILE_WRITING, + FL_SHUTDOWN, + /* These 2 come from nand_state_t, which has been unified here */ + FL_READING, + FL_CACHEDPRG, + /* These 4 come from onenand_state_t, which has been unified here */ + FL_RESETING, + FL_OTPING, + FL_PREPARING_ERASE, + FL_VERIFYING_ERASE, + + FL_UNKNOWN +} flstate_t; + + + +/* NOTE: confusingly, this can be used to refer to more than one chip at a time, + if they're interleaved. This can even refer to individual partitions on + the same physical chip when present. */ + +struct flchip { + unsigned long start; /* Offset within the map */ + // unsigned long len; + /* We omit len for now, because when we group them together + we insist that they're all of the same size, and the chip size + is held in the next level up. If we get more versatile later, + it'll make it a damn sight harder to find which chip we want from + a given offset, and we'll want to add the per-chip length field + back in. + */ + int ref_point_counter; + flstate_t state; + flstate_t oldstate; + + unsigned int write_suspended:1; + unsigned int erase_suspended:1; + unsigned long in_progress_block_addr; + + struct mutex mutex; +#ifndef __UBOOT__ + wait_queue_head_t wq; /* Wait on here when we're waiting for the chip + to be ready */ +#endif + int word_write_time; + int buffer_write_time; + int erase_time; + + int word_write_time_max; + int buffer_write_time_max; + int erase_time_max; + + void *priv; +}; + +/* This is used to handle contention on write/erase operations + between partitions of the same physical chip. */ +struct flchip_shared { + struct mutex lock; + struct flchip *writing; + struct flchip *erasing; +}; + + +#endif /* __MTD_FLASHCHIP_H__ */ diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h index a65b681..1526d07 100644 --- a/include/linux/mtd/mtd.h +++ b/include/linux/mtd/mtd.h @@ -1,48 +1,45 @@ /* - * Copyright (C) 1999-2003 David Woodhouse <dwmw2@infradead.org> et al. + * Copyright © 1999-2010 David Woodhouse <dwmw2@infradead.org> et al. * * Released under GPL + * */ #ifndef __MTD_MTD_H__ #define __MTD_MTD_H__ +#define __UBOOT__ +#ifndef __UBOOT__ #include <linux/types.h> -#include <div64.h> +#include <linux/uio.h> +#include <linux/notifier.h> +#include <linux/device.h> + +#include <mtd/mtd-abi.h> + +#include <asm/div64.h> +#else +#include <linux/compat.h> #include <mtd/mtd-abi.h> #include <asm/errno.h> +#include <div64.h> -#define MTD_CHAR_MAJOR 90 -#define MTD_BLOCK_MAJOR 31 #define MAX_MTD_DEVICES 32 +#endif #define MTD_ERASE_PENDING 0x01 #define MTD_ERASING 0x02 #define MTD_ERASE_SUSPEND 0x04 -#define MTD_ERASE_DONE 0x08 -#define MTD_ERASE_FAILED 0x10 +#define MTD_ERASE_DONE 0x08 +#define MTD_ERASE_FAILED 0x10 -#define MTD_FAIL_ADDR_UNKNOWN -1LL +#define MTD_FAIL_ADDR_UNKNOWN -1LL /* - * Enumeration for NAND/OneNAND flash chip state + * If the erase fails, fail_addr might indicate exactly which block failed. If + * fail_addr = MTD_FAIL_ADDR_UNKNOWN, the failure was not at the device level + * or was not specific to any particular block. */ -enum { - FL_READY, - FL_READING, - FL_WRITING, - FL_ERASING, - FL_SYNCING, - FL_CACHEDPRG, - FL_RESETING, - FL_UNLOCKING, - FL_LOCKING, - FL_PM_SUSPENDED, -}; - -/* If the erase fails, fail_addr might indicate exactly which block failed. If - fail_addr = MTD_FAIL_ADDR_UNKNOWN, the failure was not at the device level or was not - specific to any particular block. */ struct erase_info { struct mtd_info *mtd; uint64_t addr; @@ -50,8 +47,8 @@ struct erase_info { uint64_t fail_addr; u_long time; u_long retries; - u_int dev; - u_int cell; + unsigned dev; + unsigned cell; void (*callback) (struct erase_info *self); u_long priv; u_char state; @@ -60,9 +57,9 @@ struct erase_info { }; struct mtd_erase_region_info { - uint64_t offset; /* At which this region starts, from the beginning of the MTD */ - u_int32_t erasesize; /* For this region */ - u_int32_t numblocks; /* Number of blocks of erasesize in this region */ + uint64_t offset; /* At which this region starts, from the beginning of the MTD */ + uint32_t erasesize; /* For this region */ + uint32_t numblocks; /* Number of blocks of erasesize in this region */ unsigned long *lockmap; /* If keeping bitmap of locks */ }; @@ -81,7 +78,7 @@ struct mtd_erase_region_info { * @datbuf: data buffer - if NULL only oob data are read/written * @oobbuf: oob data buffer * - * Note, it is allowed to read more then one OOB area at one go, but not write. + * Note, it is allowed to read more than one OOB area at one go, but not write. * The interface assumes that the OOB write requests program only one page's * OOB area. */ @@ -109,26 +106,30 @@ struct mtd_oob_ops { #endif /* - * ECC layout control structure. Exported to userspace for - * diagnosis and to allow creation of raw images + * Internal ECC layout control structure. For historical reasons, there is a + * similar, smaller struct nand_ecclayout_user (in mtd-abi.h) that is retained + * for export to user-space via the ECCGETLAYOUT ioctl. + * nand_ecclayout should be expandable in the future simply by the above macros. */ struct nand_ecclayout { - uint32_t eccbytes; - uint32_t eccpos[MTD_MAX_ECCPOS_ENTRIES_LARGE]; - uint32_t oobavail; + __u32 eccbytes; + __u32 eccpos[MTD_MAX_ECCPOS_ENTRIES_LARGE]; + __u32 oobavail; struct nand_oobfree oobfree[MTD_MAX_OOBFREE_ENTRIES_LARGE]; }; +struct module; /* only needed for owner field in mtd_info */ + struct mtd_info { u_char type; - u_int32_t flags; - uint64_t size; /* Total size of the MTD */ + uint32_t flags; + uint64_t size; // Total size of the MTD /* "Major" erase size for the device. Naïve users may take this * to be the only erase size available, or may use the more detailed * information below if they desire */ - u_int32_t erasesize; + uint32_t erasesize; /* Minimal writable flash unit size. In case of NOR flash it is 1 (even * though individual bits can be cleared), in case of NAND flash it is * one NAND page (or half, or one-fourths of it), in case of ECC-ed NOR @@ -136,10 +137,31 @@ struct mtd_info { * Any driver registering a struct mtd_info must ensure a writesize of * 1 or larger. */ - u_int32_t writesize; + uint32_t writesize; + + /* + * Size of the write buffer used by the MTD. MTD devices having a write + * buffer can write multiple writesize chunks at a time. E.g. while + * writing 4 * writesize bytes to a device with 2 * writesize bytes + * buffer the MTD driver can (but doesn't have to) do 2 writesize + * operations, but not 4. Currently, all NANDs have writebufsize + * equivalent to writesize (NAND page size). Some NOR flashes do have + * writebufsize greater than writesize. + */ + uint32_t writebufsize; - u_int32_t oobsize; /* Amount of OOB data per block (e.g. 16) */ - u_int32_t oobavail; /* Available OOB bytes per block */ + uint32_t oobsize; // Amount of OOB data per block (e.g. 16) + uint32_t oobavail; // Available OOB bytes per block + + /* + * If erasesize is a power of 2 then the shift is stored in + * erasesize_shift otherwise erasesize_shift is zero. Ditto writesize. + */ + unsigned int erasesize_shift; + unsigned int writesize_shift; + /* Masks based on erasesize_shift and writesize_shift */ + unsigned int erasesize_mask; + unsigned int writesize_mask; /* * read ops return -EUCLEAN if max number of bitflips corrected on any @@ -150,13 +172,20 @@ struct mtd_info { */ unsigned int bitflip_threshold; - /* Kernel-only stuff starts here. */ + // Kernel-only stuff starts here. +#ifndef __UBOOT__ const char *name; +#else + char *name; +#endif int index; /* ECC layout structure pointer - read only! */ struct nand_ecclayout *ecclayout; + /* the ecc step size. */ + unsigned int ecc_step_size; + /* max number of correctible bit errors per ecc step */ unsigned int ecc_strength; @@ -171,44 +200,51 @@ struct mtd_info { * wrappers instead. */ int (*_erase) (struct mtd_info *mtd, struct erase_info *instr); +#ifndef __UBOOT__ int (*_point) (struct mtd_info *mtd, loff_t from, size_t len, - size_t *retlen, void **virt, phys_addr_t *phys); - void (*_unpoint) (struct mtd_info *mtd, loff_t from, size_t len); + size_t *retlen, void **virt, resource_size_t *phys); + int (*_unpoint) (struct mtd_info *mtd, loff_t from, size_t len); +#endif + unsigned long (*_get_unmapped_area) (struct mtd_info *mtd, + unsigned long len, + unsigned long offset, + unsigned long flags); int (*_read) (struct mtd_info *mtd, loff_t from, size_t len, - size_t *retlen, u_char *buf); + size_t *retlen, u_char *buf); int (*_write) (struct mtd_info *mtd, loff_t to, size_t len, - size_t *retlen, const u_char *buf); - - /* In blackbox flight recorder like scenarios we want to make successful - writes in interrupt context. panic_write() is only intended to be - called when its known the kernel is about to panic and we need the - write to succeed. Since the kernel is not going to be running for much - longer, this function can break locks and delay to ensure the write - succeeds (but not sleep). */ - - int (*_panic_write) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf); - + size_t *retlen, const u_char *buf); + int (*_panic_write) (struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, const u_char *buf); int (*_read_oob) (struct mtd_info *mtd, loff_t from, - struct mtd_oob_ops *ops); + struct mtd_oob_ops *ops); int (*_write_oob) (struct mtd_info *mtd, loff_t to, - struct mtd_oob_ops *ops); - int (*_get_fact_prot_info) (struct mtd_info *mtd, struct otp_info *buf, - size_t len); + struct mtd_oob_ops *ops); + int (*_get_fact_prot_info) (struct mtd_info *mtd, size_t len, + size_t *retlen, struct otp_info *buf); int (*_read_fact_prot_reg) (struct mtd_info *mtd, loff_t from, - size_t len, size_t *retlen, u_char *buf); - int (*_get_user_prot_info) (struct mtd_info *mtd, struct otp_info *buf, - size_t len); + size_t len, size_t *retlen, u_char *buf); + int (*_get_user_prot_info) (struct mtd_info *mtd, size_t len, + size_t *retlen, struct otp_info *buf); int (*_read_user_prot_reg) (struct mtd_info *mtd, loff_t from, - size_t len, size_t *retlen, u_char *buf); - int (*_write_user_prot_reg) (struct mtd_info *mtd, loff_t to, size_t len, - size_t *retlen, u_char *buf); + size_t len, size_t *retlen, u_char *buf); + int (*_write_user_prot_reg) (struct mtd_info *mtd, loff_t to, + size_t len, size_t *retlen, u_char *buf); int (*_lock_user_prot_reg) (struct mtd_info *mtd, loff_t from, - size_t len); + size_t len); +#ifndef __UBOOT__ + int (*_writev) (struct mtd_info *mtd, const struct kvec *vecs, + unsigned long count, loff_t to, size_t *retlen); +#endif void (*_sync) (struct mtd_info *mtd); int (*_lock) (struct mtd_info *mtd, loff_t ofs, uint64_t len); int (*_unlock) (struct mtd_info *mtd, loff_t ofs, uint64_t len); + int (*_is_locked) (struct mtd_info *mtd, loff_t ofs, uint64_t len); int (*_block_isbad) (struct mtd_info *mtd, loff_t ofs); int (*_block_markbad) (struct mtd_info *mtd, loff_t ofs); +#ifndef __UBOOT__ + int (*_suspend) (struct mtd_info *mtd); + void (*_resume) (struct mtd_info *mtd); +#endif /* * If the driver is something smart, like UBI, it may need to maintain * its own reference counting. The below functions are only for driver. @@ -216,16 +252,12 @@ struct mtd_info { int (*_get_device) (struct mtd_info *mtd); void (*_put_device) (struct mtd_info *mtd); -/* XXX U-BOOT XXX */ -#if 0 - /* kvec-based read/write methods. - NB: The 'count' parameter is the number of _vectors_, each of - which contains an (ofs, len) tuple. - */ - int (*writev) (struct mtd_info *mtd, const struct kvec *vecs, unsigned long count, loff_t to, size_t *retlen); -#endif -/* XXX U-BOOT XXX */ -#if 0 +#ifndef __UBOOT__ + /* Backing device capabilities for this device + * - provides mmap capabilities + */ + struct backing_dev_info *backing_dev_info; + struct notifier_block reboot_notifier; /* default mode before reboot */ #endif @@ -237,10 +269,20 @@ struct mtd_info { void *priv; struct module *owner; +#ifndef __UBOOT__ + struct device dev; +#endif int usecount; }; int mtd_erase(struct mtd_info *mtd, struct erase_info *instr); +#ifndef __UBOOT__ +int mtd_point(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, + void **virt, resource_size_t *phys); +int mtd_unpoint(struct mtd_info *mtd, loff_t from, size_t len); +#endif +unsigned long mtd_get_unmapped_area(struct mtd_info *mtd, unsigned long len, + unsigned long offset, unsigned long flags); int mtd_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf); int mtd_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, @@ -261,20 +303,19 @@ static inline int mtd_write_oob(struct mtd_info *mtd, loff_t to, return mtd->_write_oob(mtd, to, ops); } -int mtd_get_fact_prot_info(struct mtd_info *mtd, struct otp_info *buf, - size_t len); +int mtd_get_fact_prot_info(struct mtd_info *mtd, size_t len, size_t *retlen, + struct otp_info *buf); int mtd_read_fact_prot_reg(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf); -int mtd_get_user_prot_info(struct mtd_info *mtd, struct otp_info *buf, - size_t len); +int mtd_get_user_prot_info(struct mtd_info *mtd, size_t len, size_t *retlen, + struct otp_info *buf); int mtd_read_user_prot_reg(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf); int mtd_write_user_prot_reg(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, u_char *buf); int mtd_lock_user_prot_reg(struct mtd_info *mtd, loff_t from, size_t len); -/* XXX U-BOOT XXX */ -#if 0 +#ifndef __UBOOT__ int mtd_writev(struct mtd_info *mtd, const struct kvec *vecs, unsigned long count, loff_t to, size_t *retlen); #endif @@ -291,22 +332,59 @@ int mtd_is_locked(struct mtd_info *mtd, loff_t ofs, uint64_t len); int mtd_block_isbad(struct mtd_info *mtd, loff_t ofs); int mtd_block_markbad(struct mtd_info *mtd, loff_t ofs); +#ifndef __UBOOT__ +static inline int mtd_suspend(struct mtd_info *mtd) +{ + return mtd->_suspend ? mtd->_suspend(mtd) : 0; +} + +static inline void mtd_resume(struct mtd_info *mtd) +{ + if (mtd->_resume) + mtd->_resume(mtd); +} +#endif + static inline uint32_t mtd_div_by_eb(uint64_t sz, struct mtd_info *mtd) { + if (mtd->erasesize_shift) + return sz >> mtd->erasesize_shift; do_div(sz, mtd->erasesize); return sz; } static inline uint32_t mtd_mod_by_eb(uint64_t sz, struct mtd_info *mtd) { + if (mtd->erasesize_shift) + return sz & mtd->erasesize_mask; return do_div(sz, mtd->erasesize); } +static inline uint32_t mtd_div_by_ws(uint64_t sz, struct mtd_info *mtd) +{ + if (mtd->writesize_shift) + return sz >> mtd->writesize_shift; + do_div(sz, mtd->writesize); + return sz; +} + +static inline uint32_t mtd_mod_by_ws(uint64_t sz, struct mtd_info *mtd) +{ + if (mtd->writesize_shift) + return sz & mtd->writesize_mask; + return do_div(sz, mtd->writesize); +} + static inline int mtd_has_oob(const struct mtd_info *mtd) { return mtd->_read_oob && mtd->_write_oob; } +static inline int mtd_type_is_nand(const struct mtd_info *mtd) +{ + return mtd->type == MTD_NANDFLASH || mtd->type == MTD_MLCNANDFLASH; +} + static inline int mtd_can_have_bb(const struct mtd_info *mtd) { return !!mtd->_block_isbad; @@ -314,27 +392,36 @@ static inline int mtd_can_have_bb(const struct mtd_info *mtd) /* Kernel-side ioctl definitions */ -extern int add_mtd_device(struct mtd_info *mtd); -extern int del_mtd_device (struct mtd_info *mtd); - +struct mtd_partition; +struct mtd_part_parser_data; + +extern int mtd_device_parse_register(struct mtd_info *mtd, + const char * const *part_probe_types, + struct mtd_part_parser_data *parser_data, + const struct mtd_partition *defparts, + int defnr_parts); +#define mtd_device_register(master, parts, nr_parts) \ + mtd_device_parse_register(master, NULL, NULL, parts, nr_parts) +extern int mtd_device_unregister(struct mtd_info *master); extern struct mtd_info *get_mtd_device(struct mtd_info *mtd, int num); +extern int __get_mtd_device(struct mtd_info *mtd); +extern void __put_mtd_device(struct mtd_info *mtd); extern struct mtd_info *get_mtd_device_nm(const char *name); - extern void put_mtd_device(struct mtd_info *mtd); -extern void mtd_get_len_incl_bad(struct mtd_info *mtd, uint64_t offset, - const uint64_t length, uint64_t *len_incl_bad, - int *truncated); -/* XXX U-BOOT XXX */ -#if 0 + + +#ifndef __UBOOT__ struct mtd_notifier { void (*add)(struct mtd_info *mtd); void (*remove)(struct mtd_info *mtd); struct list_head list; }; + extern void register_mtd_user (struct mtd_notifier *new); extern int unregister_mtd_user (struct mtd_notifier *old); #endif +void *mtd_kmalloc_up_to(const struct mtd_info *mtd, size_t *size); #ifdef CONFIG_MTD_PARTITIONS void mtd_erase_callback(struct erase_info *instr); @@ -346,6 +433,7 @@ static inline void mtd_erase_callback(struct erase_info *instr) } #endif +#ifdef __UBOOT__ /* * Debugging macro and defines */ @@ -372,7 +460,11 @@ static inline void mtd_erase_callback(struct erase_info *instr) #define pr_info(args...) MTDDEBUG(MTD_DEBUG_LEVEL0, args) #define pr_warn(args...) MTDDEBUG(MTD_DEBUG_LEVEL0, args) #define pr_err(args...) MTDDEBUG(MTD_DEBUG_LEVEL0, args) - +#define pr_crit(args...) MTDDEBUG(MTD_DEBUG_LEVEL0, args) +#define pr_cont(args...) MTDDEBUG(MTD_DEBUG_LEVEL0, args) +#define pr_notice(args...) MTDDEBUG(MTD_DEBUG_LEVEL0, args) +#endif + static inline int mtd_is_bitflip(int err) { return err == -EUCLEAN; } @@ -385,4 +477,11 @@ static inline int mtd_is_bitflip_or_eccerr(int err) { return mtd_is_bitflip(err) || mtd_is_eccerr(err); } +#ifdef __UBOOT__ +/* drivers/mtd/mtdcore.h */ +int add_mtd_device(struct mtd_info *mtd); +int del_mtd_device(struct mtd_info *mtd); +int add_mtd_partitions(struct mtd_info *, const struct mtd_partition *, int); +int del_mtd_partitions(struct mtd_info *); +#endif #endif /* __MTD_MTD_H__ */ diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index 991bd8e..67d2651 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -5,9 +5,7 @@ * Steven J. Hill <sjhill@realitydiluted.com> * Thomas Gleixner <tglx@linutronix.de> * - * 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. + * SPDX-License-Identifier: GPL-2.0+ * * Info: * Contains standard defines and IDs for NAND flash devices @@ -18,21 +16,32 @@ #ifndef __LINUX_MTD_NAND_H #define __LINUX_MTD_NAND_H +#define __UBOOT__ +#ifndef __UBOOT__ +#include <linux/wait.h> +#include <linux/spinlock.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/flashchip.h> +#include <linux/mtd/bbm.h> +#else #include "config.h" #include "linux/compat.h" #include "linux/mtd/mtd.h" +#include "linux/mtd/flashchip.h" #include "linux/mtd/bbm.h" - +#endif struct mtd_info; struct nand_flash_dev; /* Scan and identify a NAND device */ -extern int nand_scan (struct mtd_info *mtd, int max_chips); -/* Separate phases of nand_scan(), allowing board driver to intervene - * and override command or ECC setup according to flash type */ +extern int nand_scan(struct mtd_info *mtd, int max_chips); +/* + * Separate phases of nand_scan(), allowing board driver to intervene + * and override command or ECC setup according to flash type. + */ extern int nand_scan_ident(struct mtd_info *mtd, int max_chips, - const struct nand_flash_dev *table); + struct nand_flash_dev *table); extern int nand_scan_tail(struct mtd_info *mtd); /* Free resources held by the NAND device */ @@ -41,13 +50,24 @@ extern void nand_release(struct mtd_info *mtd); /* Internal helper for board drivers which need to override command function */ extern void nand_wait_ready(struct mtd_info *mtd); +#ifndef __UBOOT__ +/* locks all blocks present in the device */ +extern int nand_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len); + +/* unlocks specified locked blocks */ +extern int nand_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len); + +/* The maximum number of NAND chips in an array */ +#define NAND_MAX_CHIPS 8 +#else /* * This constant declares the max. oobsize / page, which * is supported now. If you add a chip with bigger oobsize/page * adjust this accordingly. */ -#define NAND_MAX_OOBSIZE 640 -#define NAND_MAX_PAGESIZE 8192 +#define NAND_MAX_OOBSIZE 744 +#define NAND_MAX_PAGESIZE 8192 +#endif /* * Constants for hardware specific CLE/ALE/NCE function @@ -76,7 +96,6 @@ extern void nand_wait_ready(struct mtd_info *mtd); #define NAND_CMD_READOOB 0x50 #define NAND_CMD_ERASE1 0x60 #define NAND_CMD_STATUS 0x70 -#define NAND_CMD_STATUS_MULTI 0x71 #define NAND_CMD_SEQIN 0x80 #define NAND_CMD_RNDIN 0x85 #define NAND_CMD_READID 0x90 @@ -87,10 +106,8 @@ extern void nand_wait_ready(struct mtd_info *mtd); #define NAND_CMD_RESET 0xff #define NAND_CMD_LOCK 0x2a -#define NAND_CMD_LOCK_TIGHT 0x2c #define NAND_CMD_UNLOCK1 0x23 #define NAND_CMD_UNLOCK2 0x24 -#define NAND_CMD_LOCK_STATUS 0x7a /* Extended commands for large page devices */ #define NAND_CMD_READSTART 0x30 @@ -164,21 +181,12 @@ typedef enum { /* Chip has copy back function */ #define NAND_COPYBACK 0x00000010 /* - * AND Chip which has 4 banks and a confusing page / block - * assignment. See Renesas datasheet for further information. + * Chip requires ready check on read (for auto-incremented sequential read). + * True only for small page devices; large page devices do not support + * autoincrement. */ -#define NAND_IS_AND 0x00000020 -/* - * Chip has a array of 4 pages which can be read without - * additional ready /busy waits. - */ -#define NAND_4PAGE_ARRAY 0x00000040 -/* - * Chip requires that BBT is periodically rewritten to prevent - * bits from adjacent blocks from 'leaking' in altering data. - * This happens with the Renesas AG-AND chips, possibly others. - */ -#define BBT_AUTO_REFRESH 0x00000080 +#define NAND_NEED_READRDY 0x00000100 + /* Chip does not allow subpage writes */ #define NAND_NO_SUBPAGE_WRITE 0x00000200 @@ -189,16 +197,13 @@ typedef enum { #define NAND_ROM 0x00000800 /* Device supports subpage reads */ -#define NAND_SUBPAGE_READ 0x00001000 +#define NAND_SUBPAGE_READ 0x00001000 /* Options valid for Samsung large page devices */ -#define NAND_SAMSUNG_LP_OPTIONS \ - (NAND_NO_PADDING | NAND_CACHEPRG | NAND_COPYBACK) +#define NAND_SAMSUNG_LP_OPTIONS NAND_CACHEPRG /* Macros to identify the above */ -#define NAND_MUST_PAD(chip) (!(chip->options & NAND_NO_PADDING)) #define NAND_HAS_CACHEPROG(chip) ((chip->options & NAND_CACHEPRG)) -#define NAND_HAS_COPYBACK(chip) ((chip->options & NAND_COPYBACK)) #define NAND_HAS_SUBPAGE_READ(chip) ((chip->options & NAND_SUBPAGE_READ)) /* Non chip related options */ @@ -211,6 +216,13 @@ typedef enum { #define NAND_OWN_BUFFERS 0x00020000 /* Chip may not exist, so silence any errors in scan */ #define NAND_SCAN_SILENT_NODEV 0x00040000 +/* + * Autodetect nand buswidth with readid/onfi. + * This suppose the driver will configure the hardware in 8 bits mode + * when calling nand_scan_ident, and update its configuration + * before calling nand_scan_tail. + */ +#define NAND_BUSWIDTH_AUTO 0x00080000 /* Options set by nand scan */ /* bbt has already been read */ @@ -221,10 +233,15 @@ typedef enum { /* Cell info constants */ #define NAND_CI_CHIPNR_MSK 0x03 #define NAND_CI_CELLTYPE_MSK 0x0C +#define NAND_CI_CELLTYPE_SHIFT 2 /* Keep gcc happy */ struct nand_chip; +/* ONFI features */ +#define ONFI_FEATURE_16_BIT_BUS (1 << 0) +#define ONFI_FEATURE_EXT_PARAM_PAGE (1 << 7) + /* ONFI timing mode, used in both asynchronous and synchronous mode */ #define ONFI_TIMING_MODE_0 (1 << 0) #define ONFI_TIMING_MODE_1 (1 << 1) @@ -237,9 +254,15 @@ struct nand_chip; /* ONFI feature address */ #define ONFI_FEATURE_ADDR_TIMING_MODE 0x1 +/* Vendor-specific feature address (Micron) */ +#define ONFI_FEATURE_ADDR_READ_RETRY 0x89 + /* ONFI subfeature parameters length */ #define ONFI_SUBFEATURE_PARAM_LEN 4 +/* ONFI optional commands SET/GET FEATURES supported? */ +#define ONFI_OPT_CMD_SET_GET_FEATURES (1 << 2) + struct nand_onfi_params { /* rev info and features block */ /* 'O' 'N' 'F' 'I' */ @@ -247,7 +270,10 @@ struct nand_onfi_params { __le16 revision; __le16 features; __le16 opt_cmd; - u8 reserved[22]; + u8 reserved0[2]; + __le16 ext_param_page_length; /* since ONFI 2.1 */ + u8 num_of_param_pages; /* since ONFI 2.1 */ + u8 reserved1[17]; /* manufacturer information block */ char manufacturer[12]; @@ -291,19 +317,152 @@ struct nand_onfi_params { __le16 io_pin_capacitance_typ; __le16 input_pin_capacitance_typ; u8 input_pin_capacitance_max; - u8 driver_strenght_support; + u8 driver_strength_support; __le16 t_int_r; __le16 t_ald; u8 reserved4[7]; /* vendor */ - u8 reserved5[90]; + __le16 vendor_revision; + u8 vendor[88]; __le16 crc; -} __attribute__((packed)); +} __packed; #define ONFI_CRC_BASE 0x4F4E +/* Extended ECC information Block Definition (since ONFI 2.1) */ +struct onfi_ext_ecc_info { + u8 ecc_bits; + u8 codeword_size; + __le16 bb_per_lun; + __le16 block_endurance; + u8 reserved[2]; +} __packed; + +#define ONFI_SECTION_TYPE_0 0 /* Unused section. */ +#define ONFI_SECTION_TYPE_1 1 /* for additional sections. */ +#define ONFI_SECTION_TYPE_2 2 /* for ECC information. */ +struct onfi_ext_section { + u8 type; + u8 length; +} __packed; + +#define ONFI_EXT_SECTION_MAX 8 + +/* Extended Parameter Page Definition (since ONFI 2.1) */ +struct onfi_ext_param_page { + __le16 crc; + u8 sig[4]; /* 'E' 'P' 'P' 'S' */ + u8 reserved0[10]; + struct onfi_ext_section sections[ONFI_EXT_SECTION_MAX]; + + /* + * The actual size of the Extended Parameter Page is in + * @ext_param_page_length of nand_onfi_params{}. + * The following are the variable length sections. + * So we do not add any fields below. Please see the ONFI spec. + */ +} __packed; + +struct nand_onfi_vendor_micron { + u8 two_plane_read; + u8 read_cache; + u8 read_unique_id; + u8 dq_imped; + u8 dq_imped_num_settings; + u8 dq_imped_feat_addr; + u8 rb_pulldown_strength; + u8 rb_pulldown_strength_feat_addr; + u8 rb_pulldown_strength_num_settings; + u8 otp_mode; + u8 otp_page_start; + u8 otp_data_prot_addr; + u8 otp_num_pages; + u8 otp_feat_addr; + u8 read_retry_options; + u8 reserved[72]; + u8 param_revision; +} __packed; + +struct jedec_ecc_info { + u8 ecc_bits; + u8 codeword_size; + __le16 bb_per_lun; + __le16 block_endurance; + u8 reserved[2]; +} __packed; + +/* JEDEC features */ +#define JEDEC_FEATURE_16_BIT_BUS (1 << 0) + +struct nand_jedec_params { + /* rev info and features block */ + /* 'J' 'E' 'S' 'D' */ + u8 sig[4]; + __le16 revision; + __le16 features; + u8 opt_cmd[3]; + __le16 sec_cmd; + u8 num_of_param_pages; + u8 reserved0[18]; + + /* manufacturer information block */ + char manufacturer[12]; + char model[20]; + u8 jedec_id[6]; + u8 reserved1[10]; + + /* memory organization block */ + __le32 byte_per_page; + __le16 spare_bytes_per_page; + u8 reserved2[6]; + __le32 pages_per_block; + __le32 blocks_per_lun; + u8 lun_count; + u8 addr_cycles; + u8 bits_per_cell; + u8 programs_per_page; + u8 multi_plane_addr; + u8 multi_plane_op_attr; + u8 reserved3[38]; + + /* electrical parameter block */ + __le16 async_sdr_speed_grade; + __le16 toggle_ddr_speed_grade; + __le16 sync_ddr_speed_grade; + u8 async_sdr_features; + u8 toggle_ddr_features; + u8 sync_ddr_features; + __le16 t_prog; + __le16 t_bers; + __le16 t_r; + __le16 t_r_multi_plane; + __le16 t_ccs; + __le16 io_pin_capacitance_typ; + __le16 input_pin_capacitance_typ; + __le16 clk_pin_capacitance_typ; + u8 driver_strength_support; + __le16 t_ald; + u8 reserved4[36]; + + /* ECC and endurance block */ + u8 guaranteed_good_blocks; + __le16 guaranteed_block_endurance; + struct jedec_ecc_info ecc_info[4]; + u8 reserved5[29]; + + /* reserved */ + u8 reserved6[148]; + + /* vendor */ + __le16 vendor_rev_num; + u8 reserved7[88]; + + /* CRC for Parameter Page */ + __le16 crc; +} __packed; + /** * struct nand_hw_control - Control structure for hardware controller (e.g ECC generator) shared among independent devices * @lock: protection lock @@ -313,12 +472,11 @@ struct nand_onfi_params { * when a hw controller is available. */ struct nand_hw_control { -/* XXX U-BOOT XXX */ -#if 0 - spinlock_t lock; + spinlock_t lock; + struct nand_chip *active; +#ifndef __UBOOT__ wait_queue_head_t wq; #endif - struct nand_chip *active; }; /** @@ -344,6 +502,7 @@ struct nand_hw_control { * any single ECC step, 0 if bitflips uncorrectable, -EIO hw error * @read_subpage: function to read parts of the page covered by ECC; * returns same as read_page() + * @write_subpage: function to write parts of the page covered by ECC. * @write_page: function to write a page according to the ECC generator * requirements. * @write_oob_raw: function to write chip OOB data without ECC @@ -374,7 +533,10 @@ struct nand_ecc_ctrl { int (*read_page)(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *buf, int oob_required, int page); int (*read_subpage)(struct mtd_info *mtd, struct nand_chip *chip, - uint32_t offs, uint32_t len, uint8_t *buf); + uint32_t offs, uint32_t len, uint8_t *buf, int page); + int (*write_subpage)(struct mtd_info *mtd, struct nand_chip *chip, + uint32_t offset, uint32_t data_len, + const uint8_t *data_buf, int oob_required); int (*write_page)(struct mtd_info *mtd, struct nand_chip *chip, const uint8_t *buf, int oob_required); int (*write_oob_raw)(struct mtd_info *mtd, struct nand_chip *chip, @@ -388,18 +550,24 @@ struct nand_ecc_ctrl { /** * struct nand_buffers - buffer structure for read/write - * @ecccalc: buffer for calculated ECC - * @ecccode: buffer for ECC read from flash - * @databuf: buffer for data - dynamically sized + * @ecccalc: buffer pointer for calculated ECC, size is oobsize. + * @ecccode: buffer pointer for ECC read from flash, size is oobsize. + * @databuf: buffer pointer for data, size is (page size + oobsize). * * Do not change the order of buffers. databuf and oobrbuf must be in * consecutive order. */ struct nand_buffers { +#ifndef __UBOOT__ + uint8_t *ecccalc; + uint8_t *ecccode; + uint8_t *databuf; +#else uint8_t ecccalc[ALIGN(NAND_MAX_OOBSIZE, ARCH_DMA_MINALIGN)]; uint8_t ecccode[ALIGN(NAND_MAX_OOBSIZE, ARCH_DMA_MINALIGN)]; uint8_t databuf[ALIGN(NAND_MAX_PAGESIZE + NAND_MAX_OOBSIZE, ARCH_DMA_MINALIGN)]; +#endif }; /** @@ -410,13 +578,13 @@ struct nand_buffers { * flash device. * @read_byte: [REPLACEABLE] read one byte from the chip * @read_word: [REPLACEABLE] read one word from the chip + * @write_byte: [REPLACEABLE] write a single byte to the chip on the + * low 8 I/O lines * @write_buf: [REPLACEABLE] write data from the buffer to the chip * @read_buf: [REPLACEABLE] read data from the chip into the buffer - * @verify_buf: [REPLACEABLE] verify buffer contents against the chip - * data. * @select_chip: [REPLACEABLE] select chip nr - * @block_bad: [REPLACEABLE] check, if the block is bad - * @block_markbad: [REPLACEABLE] mark the block bad + * @block_bad: [REPLACEABLE] check if a block is bad, using OOB markers + * @block_markbad: [REPLACEABLE] mark a block bad * @cmd_ctrl: [BOARDSPECIFIC] hardwarespecific function for controlling * ALE/CLE/nCE. Also used to write command and address * @init_size: [BOARDSPECIFIC] hardwarespecific function for setting @@ -431,6 +599,8 @@ struct nand_buffers { * commands to the chip. * @waitfunc: [REPLACEABLE] hardwarespecific function for wait on * ready. + * @setup_read_retry: [FLASHSPECIFIC] flash (vendor) specific function for + * setting the read-retry mode. Mostly needed for MLC NAND. * @ecc: [BOARDSPECIFIC] ECC control structure * @buffers: buffer structure for read/write * @hwcontrol: platform-specific hardware control structure @@ -458,7 +628,13 @@ struct nand_buffers { * @badblockbits: [INTERN] minimum number of set bits in a good block's * bad block marker position; i.e., BBM == 11110111b is * not bad when badblockbits == 7 - * @cellinfo: [INTERN] MLC/multichip data from chip ident + * @bits_per_cell: [INTERN] number of bits per cell. i.e., 1 means SLC. + * @ecc_strength_ds: [INTERN] ECC correctability from the datasheet. + * Minimum amount of bit errors per @ecc_step_ds guaranteed + * to be correctable. If unknown, set to zero. + * @ecc_step_ds: [INTERN] ECC step required by the @ecc_strength_ds, + * also from the datasheet. It is the recommended ECC step + * size, if known; if unknown, set to zero. * @numchips: [INTERN] number of physical chips * @chipsize: [INTERN] the size of one chip for multichip arrays * @pagemask: [INTERN] page number mask = number of (pages / chip) - 1 @@ -469,11 +645,15 @@ struct nand_buffers { * @subpagesize: [INTERN] holds the subpagesize * @onfi_version: [INTERN] holds the chip ONFI version (BCD encoded), * non 0 if ONFI supported. + * @jedec_version: [INTERN] holds the chip JEDEC version (BCD encoded), + * non 0 if JEDEC supported. * @onfi_params: [INTERN] holds the ONFI page parameter when ONFI is * supported, 0 otherwise. - * @onfi_set_features [REPLACEABLE] set the features for ONFI nand - * @onfi_get_features [REPLACEABLE] get the features for ONFI nand - * @ecclayout: [REPLACEABLE] the default ECC placement scheme + * @jedec_params: [INTERN] holds the JEDEC parameter page when JEDEC is + * supported, 0 otherwise. + * @read_retries: [INTERN] the number of read retry modes supported + * @onfi_set_features: [REPLACEABLE] set the features for ONFI nand + * @onfi_get_features: [REPLACEABLE] get the features for ONFI nand * @bbt: [INTERN] bad block table pointer * @bbt_td: [REPLACEABLE] bad block table descriptor for flash * lookup. @@ -496,9 +676,14 @@ struct nand_chip { uint8_t (*read_byte)(struct mtd_info *mtd); u16 (*read_word)(struct mtd_info *mtd); + void (*write_byte)(struct mtd_info *mtd, uint8_t byte); void (*write_buf)(struct mtd_info *mtd, const uint8_t *buf, int len); void (*read_buf)(struct mtd_info *mtd, uint8_t *buf, int len); - int (*verify_buf)(struct mtd_info *mtd, const uint8_t *buf, int len); +#ifdef __UBOOT__ +#if defined(CONFIG_MTD_NAND_VERIFY_WRITE) + int (*verify_buf)(struct mtd_info *mtd, const uint8_t *buf, int len); +#endif +#endif void (*select_chip)(struct mtd_info *mtd, int chip); int (*block_bad)(struct mtd_info *mtd, loff_t ofs, int getchip); int (*block_markbad)(struct mtd_info *mtd, loff_t ofs); @@ -514,12 +699,13 @@ struct nand_chip { int (*errstat)(struct mtd_info *mtd, struct nand_chip *this, int state, int status, int page); int (*write_page)(struct mtd_info *mtd, struct nand_chip *chip, - const uint8_t *buf, int oob_required, int page, - int cached, int raw); + uint32_t offset, int data_len, const uint8_t *buf, + int oob_required, int page, int cached, int raw); int (*onfi_set_features)(struct mtd_info *mtd, struct nand_chip *chip, int feature_addr, uint8_t *subfeature_para); int (*onfi_get_features)(struct mtd_info *mtd, struct nand_chip *chip, int feature_addr, uint8_t *subfeature_para); + int (*setup_read_retry)(struct mtd_info *mtd, int retry_mode); int chip_delay; unsigned int options; @@ -535,20 +721,28 @@ struct nand_chip { int pagebuf; unsigned int pagebuf_bitflips; int subpagesize; - uint8_t cellinfo; + uint8_t bits_per_cell; + uint16_t ecc_strength_ds; + uint16_t ecc_step_ds; int badblockpos; int badblockbits; int onfi_version; + int jedec_version; #ifdef CONFIG_SYS_NAND_ONFI_DETECTION - struct nand_onfi_params onfi_params; + struct nand_onfi_params onfi_params; #endif + struct nand_jedec_params jedec_params; + + int read_retries; - int state; + flstate_t state; uint8_t *oob_poi; struct nand_hw_control *controller; +#ifdef __UBOOT__ struct nand_ecclayout *ecclayout; +#endif struct nand_ecc_ctrl ecc; struct nand_buffers *buffers; @@ -577,26 +771,83 @@ struct nand_chip { #define NAND_MFR_AMD 0x01 #define NAND_MFR_MACRONIX 0xc2 #define NAND_MFR_EON 0x92 +#define NAND_MFR_SANDISK 0x45 +#define NAND_MFR_INTEL 0x89 + +/* The maximum expected count of bytes in the NAND ID sequence */ +#define NAND_MAX_ID_LEN 8 + +/* + * A helper for defining older NAND chips where the second ID byte fully + * defined the chip, including the geometry (chip size, eraseblock size, page + * size). All these chips have 512 bytes NAND page size. + */ +#define LEGACY_ID_NAND(nm, devid, chipsz, erasesz, opts) \ + { .name = (nm), {{ .dev_id = (devid) }}, .pagesize = 512, \ + .chipsize = (chipsz), .erasesize = (erasesz), .options = (opts) } + +/* + * A helper for defining newer chips which report their page size and + * eraseblock size via the extended ID bytes. + * + * The real difference between LEGACY_ID_NAND and EXTENDED_ID_NAND is that with + * EXTENDED_ID_NAND, manufacturers overloaded the same device ID so that the + * device ID now only represented a particular total chip size (and voltage, + * buswidth), and the page size, eraseblock size, and OOB size could vary while + * using the same device ID. + */ +#define EXTENDED_ID_NAND(nm, devid, chipsz, opts) \ + { .name = (nm), {{ .dev_id = (devid) }}, .chipsize = (chipsz), \ + .options = (opts) } + +#define NAND_ECC_INFO(_strength, _step) \ + { .strength_ds = (_strength), .step_ds = (_step) } +#define NAND_ECC_STRENGTH(type) ((type)->ecc.strength_ds) +#define NAND_ECC_STEP(type) ((type)->ecc.step_ds) /** * struct nand_flash_dev - NAND Flash Device ID Structure - * @name: Identify the device type - * @id: device ID code - * @pagesize: Pagesize in bytes. Either 256 or 512 or 0 - * If the pagesize is 0, then the real pagesize - * and the eraseize are determined from the - * extended id bytes in the chip - * @erasesize: Size of an erase block in the flash device. - * @chipsize: Total chipsize in Mega Bytes - * @options: Bitfield to store chip relevant options + * @name: a human-readable name of the NAND chip + * @dev_id: the device ID (the second byte of the full chip ID array) + * @mfr_id: manufecturer ID part of the full chip ID array (refers the same + * memory address as @id[0]) + * @dev_id: device ID part of the full chip ID array (refers the same memory + * address as @id[1]) + * @id: full device ID array + * @pagesize: size of the NAND page in bytes; if 0, then the real page size (as + * well as the eraseblock size) is determined from the extended NAND + * chip ID array) + * @chipsize: total chip size in MiB + * @erasesize: eraseblock size in bytes (determined from the extended ID if 0) + * @options: stores various chip bit options + * @id_len: The valid length of the @id. + * @oobsize: OOB size + * @ecc.strength_ds: The ECC correctability from the datasheet, same as the + * @ecc_strength_ds in nand_chip{}. + * @ecc.step_ds: The ECC step required by the @ecc.strength_ds, same as the + * @ecc_step_ds in nand_chip{}, also from the datasheet. + * For example, the "4bit ECC for each 512Byte" can be set with + * NAND_ECC_INFO(4, 512). */ struct nand_flash_dev { char *name; - int id; - unsigned long pagesize; - unsigned long chipsize; - unsigned long erasesize; - unsigned long options; + union { + struct { + uint8_t mfr_id; + uint8_t dev_id; + }; + uint8_t id[NAND_MAX_ID_LEN]; + }; + unsigned int pagesize; + unsigned int chipsize; + unsigned int erasesize; + unsigned int options; + uint16_t id_len; + uint16_t oobsize; + struct { + uint16_t strength_ds; + uint16_t step_ds; + } ecc; }; /** @@ -609,23 +860,25 @@ struct nand_manufacturers { char *name; }; -extern const struct nand_flash_dev nand_flash_ids[]; -extern const struct nand_manufacturers nand_manuf_ids[]; +extern struct nand_flash_dev nand_flash_ids[]; +extern struct nand_manufacturers nand_manuf_ids[]; extern int nand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd); -extern int nand_update_bbt(struct mtd_info *mtd, loff_t offs); extern int nand_default_bbt(struct mtd_info *mtd); +extern int nand_markbad_bbt(struct mtd_info *mtd, loff_t offs); extern int nand_isbad_bbt(struct mtd_info *mtd, loff_t offs, int allowbbt); extern int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr, int allowbbt); extern int nand_do_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, uint8_t *buf); +#ifdef __UBOOT__ /* * Constants for oob configuration */ #define NAND_SMALL_BADBLOCK_POS 5 #define NAND_LARGE_BADBLOCK_POS 0 +#endif /** * struct platform_nand_chip - chip level device structure @@ -656,20 +909,29 @@ struct platform_device; /** * struct platform_nand_ctrl - controller level device structure + * @probe: platform specific function to probe/setup hardware + * @remove: platform specific function to remove/teardown hardware * @hwcontrol: platform specific hardware control structure * @dev_ready: platform specific function to read ready/busy pin * @select_chip: platform specific chip select function * @cmd_ctrl: platform specific function for controlling * ALE/CLE/nCE. Also used to write command and address + * @write_buf: platform specific function for write buffer + * @read_buf: platform specific function for read buffer + * @read_byte: platform specific function to read one byte from chip * @priv: private data to transport driver specific settings * * All fields are optional and depend on the hardware driver requirements */ struct platform_nand_ctrl { + int (*probe)(struct platform_device *pdev); + void (*remove)(struct platform_device *pdev); void (*hwcontrol)(struct mtd_info *mtd, int cmd); int (*dev_ready)(struct mtd_info *mtd); void (*select_chip)(struct mtd_info *mtd, int chip); void (*cmd_ctrl)(struct mtd_info *mtd, int dat, unsigned int ctrl); + void (*write_buf)(struct mtd_info *mtd, const uint8_t *buf, int len); + void (*read_buf)(struct mtd_info *mtd, uint8_t *buf, int len); unsigned char (*read_byte)(struct mtd_info *mtd); void *priv; }; @@ -693,16 +955,14 @@ struct platform_nand_chip *get_platform_nandchip(struct mtd_info *mtd) return chip->priv; } -/* Standard NAND functions from nand_base.c */ -void nand_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len); -void nand_write_buf16(struct mtd_info *mtd, const uint8_t *buf, int len); -void nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len); -void nand_read_buf16(struct mtd_info *mtd, uint8_t *buf, int len); -uint8_t nand_read_byte(struct mtd_info *mtd); +#ifdef CONFIG_SYS_NAND_ONFI_DETECTION +/* return the supported features. */ +static inline int onfi_feature(struct nand_chip *chip) +{ + return chip->onfi_version ? le16_to_cpu(chip->onfi_params.features) : 0; +} /* return the supported asynchronous timing mode. */ - -#ifdef CONFIG_SYS_NAND_ONFI_DETECTION static inline int onfi_get_async_timing_mode(struct nand_chip *chip) { if (!chip->onfi_version) @@ -719,6 +979,16 @@ static inline int onfi_get_sync_timing_mode(struct nand_chip *chip) } #endif +/* + * Check if it is a SLC nand. + * The !nand_is_slc() can be used to check the MLC/TLC nand chips. + * We do not distinguish the MLC and TLC now. + */ +static inline bool nand_is_slc(struct nand_chip *chip) +{ + return chip->bits_per_cell == 1; +} + /** * Check if the opcode's address should be sent only on the lower 8 bits * @command: opcode to check @@ -737,5 +1007,19 @@ static inline int nand_opcode_8bits(unsigned int command) return 0; } +/* return the supported JEDEC features. */ +static inline int jedec_feature(struct nand_chip *chip) +{ + return chip->jedec_version ? le16_to_cpu(chip->jedec_params.features) + : 0; +} +#ifdef __UBOOT__ +/* Standard NAND functions from nand_base.c */ +void nand_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len); +void nand_write_buf16(struct mtd_info *mtd, const uint8_t *buf, int len); +void nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len); +void nand_read_buf16(struct mtd_info *mtd, uint8_t *buf, int len); +uint8_t nand_read_byte(struct mtd_info *mtd); +#endif #endif /* __LINUX_MTD_NAND_H */ diff --git a/include/linux/mtd/partitions.h b/include/linux/mtd/partitions.h index d1d9a96..ce0e8db 100644 --- a/include/linux/mtd/partitions.h +++ b/include/linux/mtd/partitions.h @@ -1,11 +1,9 @@ /* * MTD partitioning layer definitions * - * (C) 2000 Nicolas Pitre <nico@cam.org> + * (C) 2000 Nicolas Pitre <nico@fluxnic.net> * * This code is GPL - * - * $Id: partitions.h,v 1.17 2005/11/07 11:14:55 gleixner Exp $ */ #ifndef MTD_PARTITIONS_H @@ -18,7 +16,7 @@ * Partition definition structure: * * An array of struct partition is passed along with a MTD object to - * add_mtd_partitions() to create them. + * mtd_device_register() to create them. * * For each partition, these fields are available: * name: string that will be used to label the partition's MTD device. @@ -26,7 +24,9 @@ * will extend to the end of the master MTD device. * offset: absolute starting position within the master MTD device; if * defined as MTDPART_OFS_APPEND, the partition will start where the - * previous one ended; if MTDPART_OFS_NXTBLK, at the next erase block. + * previous one ended; if MTDPART_OFS_NXTBLK, at the next erase block; + * if MTDPART_OFS_RETAIN, consume as much as possible, leaving size + * after the end of partition. * mask_flags: contains flags that have to be masked (removed) from the * master MTD flag set for the corresponding MTD partition. * For example, to force a read-only partition, simply adding @@ -37,23 +37,34 @@ */ struct mtd_partition { - char *name; /* identifier string */ + const char *name; /* identifier string */ uint64_t size; /* partition size */ uint64_t offset; /* offset within the master MTD space */ - u_int32_t mask_flags; /* master MTD flags to mask out for this partition */ - struct nand_ecclayout *ecclayout; /* out of band layout for this partition (NAND only)*/ - struct mtd_info **mtdp; /* pointer to store the MTD object */ + uint32_t mask_flags; /* master MTD flags to mask out for this partition */ + struct nand_ecclayout *ecclayout; /* out of band layout for this partition (NAND only) */ }; +#define MTDPART_OFS_RETAIN (-3) #define MTDPART_OFS_NXTBLK (-2) #define MTDPART_OFS_APPEND (-1) #define MTDPART_SIZ_FULL (0) -int add_mtd_partitions(struct mtd_info *, const struct mtd_partition *, int); -int del_mtd_partitions(struct mtd_info *); +struct mtd_info; +struct device_node; + +#ifndef __UBOOT__ +/** + * struct mtd_part_parser_data - used to pass data to MTD partition parsers. + * @origin: for RedBoot, start address of MTD device + * @of_node: for OF parsers, device node containing partitioning information + */ +struct mtd_part_parser_data { + unsigned long origin; + struct device_node *of_node; +}; + -#if 0 /* * Functions dealing with the various ways of partitioning the space */ @@ -62,23 +73,18 @@ struct mtd_part_parser { struct list_head list; struct module *owner; const char *name; - int (*parse_fn)(struct mtd_info *, struct mtd_partition **, unsigned long); + int (*parse_fn)(struct mtd_info *, struct mtd_partition **, + struct mtd_part_parser_data *); }; -extern int register_mtd_parser(struct mtd_part_parser *parser); -extern int deregister_mtd_parser(struct mtd_part_parser *parser); -extern int parse_mtd_partitions(struct mtd_info *master, const char **types, - struct mtd_partition **pparts, unsigned long origin); - -#define put_partition_parser(p) do { module_put((p)->owner); } while(0) - -struct device; -struct device_node; - -int __devinit of_mtd_parse_partitions(struct device *dev, - struct mtd_info *mtd, - struct device_node *node, - struct mtd_partition **pparts); +extern void register_mtd_parser(struct mtd_part_parser *parser); +extern void deregister_mtd_parser(struct mtd_part_parser *parser); #endif +int mtd_is_partition(const struct mtd_info *mtd); +int mtd_add_partition(struct mtd_info *master, const char *name, + long long offset, long long length); +int mtd_del_partition(struct mtd_info *master, int partno); +uint64_t mtd_get_device_size(const struct mtd_info *mtd); + #endif diff --git a/include/linux/mtd/ubi.h b/include/linux/mtd/ubi.h index 4755770..d9e58ae 100644 --- a/include/linux/mtd/ubi.h +++ b/include/linux/mtd/ubi.h @@ -9,9 +9,15 @@ #ifndef __LINUX_UBI_H__ #define __LINUX_UBI_H__ -/* #include <asm/ioctl.h> */ #include <linux/types.h> +#define __UBOOT__ +#ifndef __UBOOT__ +#include <linux/ioctl.h> #include <mtd/ubi-user.h> +#endif + +/* All voumes/LEBs */ +#define UBI_ALL -1 /* * enum ubi_open_mode - UBI volume open mode constants. @@ -33,13 +39,13 @@ enum { * @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 + * 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 + * this volume * @name_len: volume name length * @name: volume name * @cdev: UBI volume character device major and minor numbers @@ -75,7 +81,7 @@ enum { * 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), + * @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 @@ -104,20 +110,79 @@ struct ubi_volume_info { * struct ubi_device_info - UBI device description data structure. * @ubi_num: ubi device number * @leb_size: logical eraseblock size on this UBI device + * @leb_start: starting offset of logical eraseblocks within physical + * eraseblocks * @min_io_size: minimal I/O unit size + * @max_write_size: maximum amount of bytes the underlying flash can write at a + * time (MTD write buffer 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. + * + * The @max_write_size field describes flash write maximum write unit. For + * example, NOR flash allows for changing individual bytes, so @min_io_size is + * %1. However, it does not mean than NOR flash has to write data byte-by-byte. + * Instead, CFI NOR flashes have a write-buffer of, e.g., 64 bytes, and when + * writing large chunks of data, they write 64-bytes at a time. Obviously, this + * improves write throughput. + * + * Also, the MTD device may have N interleaved (striped) flash chips + * underneath, in which case @min_io_size can be physical min. I/O size of + * single flash chip, while @max_write_size can be N * @min_io_size. + * + * The @max_write_size field is always greater or equivalent to @min_io_size. + * E.g., some NOR flashes may have (@min_io_size = 1, @max_write_size = 64). In + * contrast, NAND flashes usually have @min_io_size = @max_write_size = NAND + * page size. */ struct ubi_device_info { int ubi_num; int leb_size; + int leb_start; int min_io_size; + int max_write_size; int ro_mode; +#ifndef __UBOOT__ dev_t cdev; +#endif +}; + +/* + * Volume notification types. + * @UBI_VOLUME_ADDED: a volume has been added (an UBI device was attached or a + * volume was created) + * @UBI_VOLUME_REMOVED: a volume has been removed (an UBI device was detached + * or a volume was removed) + * @UBI_VOLUME_RESIZED: a volume has been re-sized + * @UBI_VOLUME_RENAMED: a volume has been re-named + * @UBI_VOLUME_UPDATED: data has been written to a volume + * + * These constants define which type of event has happened when a volume + * notification function is invoked. + */ +enum { + UBI_VOLUME_ADDED, + UBI_VOLUME_REMOVED, + UBI_VOLUME_RESIZED, + UBI_VOLUME_RENAMED, + UBI_VOLUME_UPDATED, +}; + +/* + * struct ubi_notification - UBI notification description structure. + * @di: UBI device description object + * @vi: UBI volume description object + * + * UBI notifiers are called with a pointer to an object of this type. The + * object describes the notification. Namely, it provides a description of the + * UBI device and UBI volume the notification informs about. + */ +struct ubi_notification { + struct ubi_device_info di; + struct ubi_volume_info vi; }; /* UBI descriptor given to users when they open UBI volumes */ @@ -129,17 +194,37 @@ void ubi_get_volume_info(struct ubi_volume_desc *desc, 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); +struct ubi_volume_desc *ubi_open_volume_path(const char *pathname, int mode); + +#ifndef __UBOOT__ +typedef int (*notifier_fn_t)(void *nb, + unsigned long action, void *data); + +struct notifier_block { + notifier_fn_t notifier_call; + struct notifier_block *next; + void *next; + int priority; +}; + +int ubi_register_volume_notifier(struct notifier_block *nb, + int ignore_existing); +int ubi_unregister_volume_notifier(struct notifier_block *nb); +#endif + 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 offset, int len); int ubi_leb_change(struct ubi_volume_desc *desc, int lnum, const void *buf, - int len, int dtype); + int len); 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_leb_map(struct ubi_volume_desc *desc, int lnum); int ubi_is_mapped(struct ubi_volume_desc *desc, int lnum); +int ubi_sync(int ubi_num); +int ubi_flush(int ubi_num, int vol_id, int lnum); /* * This function is the same as the 'ubi_leb_read()' function, but it does not @@ -150,25 +235,4 @@ static inline int ubi_read(struct ubi_volume_desc *desc, int lnum, char *buf, { 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/rbtree.h b/include/linux/rbtree.h index ad892d1..b5994e3 100644 --- a/include/linux/rbtree.h +++ b/include/linux/rbtree.h @@ -1,7 +1,7 @@ /* Red Black Trees (C) 1999 Andrea Arcangeli <andrea@suse.de> - + * SPDX-License-Identifier: GPL-2.0+ linux/include/linux/rbtree.h @@ -11,138 +11,89 @@ I know it's not the cleaner way, but in C (not in C++) to get performances and genericity... - Some example of insert and search follows here. The search is a plain - normal search over an ordered tree. The insert instead must be implemented - int two steps: as first thing the code must insert the element in - order as a red leaf in the tree, then the support library function - rb_insert_color() must be called. Such function will do the - not trivial work to rebalance the rbtree if necessary. - ------------------------------------------------------------------------ -static inline struct page * rb_search_page_cache(struct inode * inode, - unsigned long offset) -{ - struct rb_node * n = inode->i_rb_page_cache.rb_node; - struct page * page; - - while (n) - { - page = rb_entry(n, struct page, rb_page_cache); - - if (offset < page->offset) - n = n->rb_left; - else if (offset > page->offset) - n = n->rb_right; - else - return page; - } - return NULL; -} - -static inline struct page * __rb_insert_page_cache(struct inode * inode, - unsigned long offset, - struct rb_node * node) -{ - struct rb_node ** p = &inode->i_rb_page_cache.rb_node; - struct rb_node * parent = NULL; - struct page * page; - - while (*p) - { - parent = *p; - page = rb_entry(parent, struct page, rb_page_cache); - - if (offset < page->offset) - p = &(*p)->rb_left; - else if (offset > page->offset) - p = &(*p)->rb_right; - else - return page; - } - - rb_link_node(node, parent, p); - - return NULL; -} - -static inline struct page * rb_insert_page_cache(struct inode * inode, - unsigned long offset, - struct rb_node * node) -{ - struct page * ret; - if ((ret = __rb_insert_page_cache(inode, offset, node))) - goto out; - rb_insert_color(node, &inode->i_rb_page_cache); - out: - return ret; -} ------------------------------------------------------------------------ + See Documentation/rbtree.txt for documentation and samples. */ #ifndef _LINUX_RBTREE_H #define _LINUX_RBTREE_H +#define __UBOOT__ +#ifndef __UBOOT__ +#include <linux/kernel.h> +#endif #include <linux/stddef.h> -struct rb_node -{ - unsigned long rb_parent_color; -#define RB_RED 0 -#define RB_BLACK 1 +struct rb_node { + unsigned long __rb_parent_color; struct rb_node *rb_right; struct rb_node *rb_left; } __attribute__((aligned(sizeof(long)))); /* The alignment might seem pointless, but allegedly CRIS needs it */ -struct rb_root -{ +struct rb_root { struct rb_node *rb_node; }; -#define rb_parent(r) ((struct rb_node *)((r)->rb_parent_color & ~3)) -#define rb_color(r) ((r)->rb_parent_color & 1) -#define rb_is_red(r) (!rb_color(r)) -#define rb_is_black(r) rb_color(r) -#define rb_set_red(r) do { (r)->rb_parent_color &= ~1; } while (0) -#define rb_set_black(r) do { (r)->rb_parent_color |= 1; } while (0) - -static inline void rb_set_parent(struct rb_node *rb, struct rb_node *p) -{ - rb->rb_parent_color = (rb->rb_parent_color & 3) | (unsigned long)p; -} -static inline void rb_set_color(struct rb_node *rb, int color) -{ - rb->rb_parent_color = (rb->rb_parent_color & ~1) | color; -} +#define rb_parent(r) ((struct rb_node *)((r)->__rb_parent_color & ~3)) #define RB_ROOT (struct rb_root) { NULL, } #define rb_entry(ptr, type, member) container_of(ptr, type, member) -#define RB_EMPTY_ROOT(root) ((root)->rb_node == NULL) -#define RB_EMPTY_NODE(node) (rb_parent(node) == node) -#define RB_CLEAR_NODE(node) (rb_set_parent(node, node)) +#define RB_EMPTY_ROOT(root) ((root)->rb_node == NULL) + +/* 'empty' nodes are nodes that are known not to be inserted in an rbree */ +#define RB_EMPTY_NODE(node) \ + ((node)->__rb_parent_color == (unsigned long)(node)) +#define RB_CLEAR_NODE(node) \ + ((node)->__rb_parent_color = (unsigned long)(node)) + extern void rb_insert_color(struct rb_node *, struct rb_root *); extern void rb_erase(struct rb_node *, struct rb_root *); + /* Find logical next and previous nodes in a tree */ -extern struct rb_node *rb_next(struct rb_node *); -extern struct rb_node *rb_prev(struct rb_node *); -extern struct rb_node *rb_first(struct rb_root *); -extern struct rb_node *rb_last(struct rb_root *); +extern struct rb_node *rb_next(const struct rb_node *); +extern struct rb_node *rb_prev(const struct rb_node *); +extern struct rb_node *rb_first(const struct rb_root *); +extern struct rb_node *rb_last(const struct rb_root *); + +/* Postorder iteration - always visit the parent after its children */ +extern struct rb_node *rb_first_postorder(const struct rb_root *); +extern struct rb_node *rb_next_postorder(const struct rb_node *); /* Fast replacement of a single node without remove/rebalance/add/rebalance */ -extern void rb_replace_node(struct rb_node *victim, struct rb_node *new, +extern void rb_replace_node(struct rb_node *victim, struct rb_node *new, struct rb_root *root); static inline void rb_link_node(struct rb_node * node, struct rb_node * parent, struct rb_node ** rb_link) { - node->rb_parent_color = (unsigned long )parent; + node->__rb_parent_color = (unsigned long)parent; node->rb_left = node->rb_right = NULL; *rb_link = node; } +#define rb_entry_safe(ptr, type, member) \ + ({ typeof(ptr) ____ptr = (ptr); \ + ____ptr ? rb_entry(____ptr, type, member) : NULL; \ + }) + +/** + * rbtree_postorder_for_each_entry_safe - iterate over rb_root in post order of + * given type safe against removal of rb_node entry + * + * @pos: the 'type *' to use as a loop cursor. + * @n: another 'type *' to use as temporary storage + * @root: 'rb_root *' of the rbtree. + * @field: the name of the rb_node field within 'type'. + */ +#define rbtree_postorder_for_each_entry_safe(pos, n, root, field) \ + for (pos = rb_entry_safe(rb_first_postorder(root), typeof(*pos), field); \ + pos && ({ n = rb_entry_safe(rb_next_postorder(&pos->field), \ + typeof(*pos), field); 1; }); \ + pos = n) + #endif /* _LINUX_RBTREE_H */ diff --git a/include/linux/rbtree_augmented.h b/include/linux/rbtree_augmented.h new file mode 100644 index 0000000..a86797e --- /dev/null +++ b/include/linux/rbtree_augmented.h @@ -0,0 +1,220 @@ +/* + Red Black Trees + (C) 1999 Andrea Arcangeli <andrea@suse.de> + (C) 2002 David Woodhouse <dwmw2@infradead.org> + (C) 2012 Michel Lespinasse <walken@google.com> + + * SPDX-License-Identifier: GPL-2.0+ + + linux/include/linux/rbtree_augmented.h +*/ + +#ifndef _LINUX_RBTREE_AUGMENTED_H +#define _LINUX_RBTREE_AUGMENTED_H + +#include <linux/compiler.h> +#include <linux/rbtree.h> + +/* + * Please note - only struct rb_augment_callbacks and the prototypes for + * rb_insert_augmented() and rb_erase_augmented() are intended to be public. + * The rest are implementation details you are not expected to depend on. + * + * See Documentation/rbtree.txt for documentation and samples. + */ + +struct rb_augment_callbacks { + void (*propagate)(struct rb_node *node, struct rb_node *stop); + void (*copy)(struct rb_node *old, struct rb_node *new); + void (*rotate)(struct rb_node *old, struct rb_node *new); +}; + +extern void __rb_insert_augmented(struct rb_node *node, struct rb_root *root, + void (*augment_rotate)(struct rb_node *old, struct rb_node *new)); +static inline void +rb_insert_augmented(struct rb_node *node, struct rb_root *root, + const struct rb_augment_callbacks *augment) +{ + __rb_insert_augmented(node, root, augment->rotate); +} + +#define RB_DECLARE_CALLBACKS(rbstatic, rbname, rbstruct, rbfield, \ + rbtype, rbaugmented, rbcompute) \ +static inline void \ +rbname ## _propagate(struct rb_node *rb, struct rb_node *stop) \ +{ \ + while (rb != stop) { \ + rbstruct *node = rb_entry(rb, rbstruct, rbfield); \ + rbtype augmented = rbcompute(node); \ + if (node->rbaugmented == augmented) \ + break; \ + node->rbaugmented = augmented; \ + rb = rb_parent(&node->rbfield); \ + } \ +} \ +static inline void \ +rbname ## _copy(struct rb_node *rb_old, struct rb_node *rb_new) \ +{ \ + rbstruct *old = rb_entry(rb_old, rbstruct, rbfield); \ + rbstruct *new = rb_entry(rb_new, rbstruct, rbfield); \ + new->rbaugmented = old->rbaugmented; \ +} \ +static void \ +rbname ## _rotate(struct rb_node *rb_old, struct rb_node *rb_new) \ +{ \ + rbstruct *old = rb_entry(rb_old, rbstruct, rbfield); \ + rbstruct *new = rb_entry(rb_new, rbstruct, rbfield); \ + new->rbaugmented = old->rbaugmented; \ + old->rbaugmented = rbcompute(old); \ +} \ +rbstatic const struct rb_augment_callbacks rbname = { \ + rbname ## _propagate, rbname ## _copy, rbname ## _rotate \ +}; + + +#define RB_RED 0 +#define RB_BLACK 1 + +#define __rb_parent(pc) ((struct rb_node *)(pc & ~3)) + +#define __rb_color(pc) ((pc) & 1) +#define __rb_is_black(pc) __rb_color(pc) +#define __rb_is_red(pc) (!__rb_color(pc)) +#define rb_color(rb) __rb_color((rb)->__rb_parent_color) +#define rb_is_red(rb) __rb_is_red((rb)->__rb_parent_color) +#define rb_is_black(rb) __rb_is_black((rb)->__rb_parent_color) + +static inline void rb_set_parent(struct rb_node *rb, struct rb_node *p) +{ + rb->__rb_parent_color = rb_color(rb) | (unsigned long)p; +} + +static inline void rb_set_parent_color(struct rb_node *rb, + struct rb_node *p, int color) +{ + rb->__rb_parent_color = (unsigned long)p | color; +} + +static inline void +__rb_change_child(struct rb_node *old, struct rb_node *new, + struct rb_node *parent, struct rb_root *root) +{ + if (parent) { + if (parent->rb_left == old) + parent->rb_left = new; + else + parent->rb_right = new; + } else + root->rb_node = new; +} + +extern void __rb_erase_color(struct rb_node *parent, struct rb_root *root, + void (*augment_rotate)(struct rb_node *old, struct rb_node *new)); + +static __always_inline struct rb_node * +__rb_erase_augmented(struct rb_node *node, struct rb_root *root, + const struct rb_augment_callbacks *augment) +{ + struct rb_node *child = node->rb_right, *tmp = node->rb_left; + struct rb_node *parent, *rebalance; + unsigned long pc; + + if (!tmp) { + /* + * Case 1: node to erase has no more than 1 child (easy!) + * + * Note that if there is one child it must be red due to 5) + * and node must be black due to 4). We adjust colors locally + * so as to bypass __rb_erase_color() later on. + */ + pc = node->__rb_parent_color; + parent = __rb_parent(pc); + __rb_change_child(node, child, parent, root); + if (child) { + child->__rb_parent_color = pc; + rebalance = NULL; + } else + rebalance = __rb_is_black(pc) ? parent : NULL; + tmp = parent; + } else if (!child) { + /* Still case 1, but this time the child is node->rb_left */ + tmp->__rb_parent_color = pc = node->__rb_parent_color; + parent = __rb_parent(pc); + __rb_change_child(node, tmp, parent, root); + rebalance = NULL; + tmp = parent; + } else { + struct rb_node *successor = child, *child2; + tmp = child->rb_left; + if (!tmp) { + /* + * Case 2: node's successor is its right child + * + * (n) (s) + * / \ / \ + * (x) (s) -> (x) (c) + * \ + * (c) + */ + parent = successor; + child2 = successor->rb_right; + augment->copy(node, successor); + } else { + /* + * Case 3: node's successor is leftmost under + * node's right child subtree + * + * (n) (s) + * / \ / \ + * (x) (y) -> (x) (y) + * / / + * (p) (p) + * / / + * (s) (c) + * \ + * (c) + */ + do { + parent = successor; + successor = tmp; + tmp = tmp->rb_left; + } while (tmp); + parent->rb_left = child2 = successor->rb_right; + successor->rb_right = child; + rb_set_parent(child, successor); + augment->copy(node, successor); + augment->propagate(parent, successor); + } + + successor->rb_left = tmp = node->rb_left; + rb_set_parent(tmp, successor); + + pc = node->__rb_parent_color; + tmp = __rb_parent(pc); + __rb_change_child(node, successor, tmp, root); + if (child2) { + successor->__rb_parent_color = pc; + rb_set_parent_color(child2, parent, RB_BLACK); + rebalance = NULL; + } else { + unsigned long pc2 = successor->__rb_parent_color; + successor->__rb_parent_color = pc; + rebalance = __rb_is_black(pc2) ? parent : NULL; + } + tmp = successor; + } + + augment->propagate(tmp, NULL); + return rebalance; +} + +static __always_inline void +rb_erase_augmented(struct rb_node *node, struct rb_root *root, + const struct rb_augment_callbacks *augment) +{ + struct rb_node *rebalance = __rb_erase_augmented(node, root, augment); + if (rebalance) + __rb_erase_color(rebalance, root, augment->rotate); +} + +#endif /* _LINUX_RBTREE_AUGMENTED_H */ diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h index a8a5763..9bccd45 100644 --- a/include/linux/usb/gadget.h +++ b/include/linux/usb/gadget.h @@ -19,6 +19,7 @@ #define __LINUX_USB_GADGET_H #include <errno.h> +#include <linux/compat.h> #include <linux/list.h> struct usb_ep; @@ -410,11 +411,6 @@ struct usb_gadget_ops { unsigned code, unsigned long param); }; -struct device { - void *driver_data; /* data private to the driver */ - void *device_data; /* data private to the device */ -}; - /** * struct usb_gadget - represents a usb slave device * @ops: Function pointers used to access hardware-specific operations. diff --git a/include/mtd/mtd-abi.h b/include/mtd/mtd-abi.h index ac3c298..b9f4bcb 100644 --- a/include/mtd/mtd-abi.h +++ b/include/mtd/mtd-abi.h @@ -1,30 +1,44 @@ /* - * $Id: mtd-abi.h,v 1.13 2005/11/07 11:14:56 gleixner Exp $ + * Copyright © 1999-2010 David Woodhouse <dwmw2@infradead.org> et al. + * + * SPDX-License-Identifier: GPL-2.0+ * - * Portions of MTD ABI definition which are shared by kernel and user space */ #ifndef __MTD_ABI_H__ #define __MTD_ABI_H__ -#if 1 +#define __UBOOT__ +#ifdef __UBOOT__ #include <linux/compat.h> #endif #include <linux/compiler.h> struct erase_info_user { - uint32_t start; - uint32_t length; + __u32 start; + __u32 length; +}; + +struct erase_info_user64 { + __u64 start; + __u64 length; }; struct mtd_oob_buf { - uint32_t start; - uint32_t length; + __u32 start; + __u32 length; unsigned char __user *ptr; }; -/* +struct mtd_oob_buf64 { + __u64 start; + __u32 pad; + __u32 length; + __u64 usr_ptr; +}; + +/** * MTD operation modes * * @MTD_OPS_PLACE_OOB: OOB data are placed at the given offset (default) @@ -43,18 +57,45 @@ enum { MTD_OPS_RAW = 2, }; +/** + * struct mtd_write_req - data structure for requesting a write operation + * + * @start: start address + * @len: length of data buffer + * @ooblen: length of OOB buffer + * @usr_data: user-provided data buffer + * @usr_oob: user-provided OOB buffer + * @mode: MTD mode (see "MTD operation modes") + * @padding: reserved, must be set to 0 + * + * This structure supports ioctl(MEMWRITE) operations, allowing data and/or OOB + * writes in various modes. To write to OOB-only, set @usr_data == NULL, and to + * write data-only, set @usr_oob == NULL. However, setting both @usr_data and + * @usr_oob to NULL is not allowed. + */ +struct mtd_write_req { + __u64 start; + __u64 len; + __u64 ooblen; + __u64 usr_data; + __u64 usr_oob; + __u8 mode; + __u8 padding[7]; +}; + #define MTD_ABSENT 0 #define MTD_RAM 1 #define MTD_ROM 2 #define MTD_NORFLASH 3 -#define MTD_NANDFLASH 4 +#define MTD_NANDFLASH 4 /* SLC NAND */ #define MTD_DATAFLASH 6 #define MTD_UBIVOLUME 7 +#define MTD_MLCNANDFLASH 8 /* MLC NAND (including TLC) */ #define MTD_WRITEABLE 0x400 /* Device is writeable */ #define MTD_BIT_WRITEABLE 0x800 /* Single bits can be flipped */ #define MTD_NO_ERASE 0x1000 /* No erase necessary */ -#define MTD_STUPID_LOCK 0x2000 /* Always locked after reset */ +#define MTD_POWERUP_LOCK 0x2000 /* Always locked after reset */ /* Some common devices / combinations of capabilities */ #define MTD_CAP_ROM 0 @@ -62,12 +103,12 @@ enum { #define MTD_CAP_NORFLASH (MTD_WRITEABLE | MTD_BIT_WRITEABLE) #define MTD_CAP_NANDFLASH (MTD_WRITEABLE) -/* ECC byte placement */ -#define MTD_NANDECC_OFF 0 /* Switch off ECC (Not recommended) */ -#define MTD_NANDECC_PLACE 1 /* Use the given placement in the structure (YAFFS1 legacy mode) */ -#define MTD_NANDECC_AUTOPLACE 2 /* Use the default placement scheme */ -#define MTD_NANDECC_PLACEONLY 3 /* Use the given placement in the structure (Do not store ecc result on read) */ -#define MTD_NANDECC_AUTOPL_USR 4 /* Use the given autoplacement scheme rather than using the default */ +/* Obsolete ECC byte placement modes (used with obsolete MEMGETOOBSEL) */ +#define MTD_NANDECC_OFF 0 // Switch off ECC (Not recommended) +#define MTD_NANDECC_PLACE 1 // Use the given placement in the structure (YAFFS1 legacy mode) +#define MTD_NANDECC_AUTOPLACE 2 // Use the default placement scheme +#define MTD_NANDECC_PLACEONLY 3 // Use the given placement in the structure (Do not store ecc result on read) +#define MTD_NANDECC_AUTOPL_USR 4 // Use the given autoplacement scheme rather than using the default /* OTP mode selection */ #define MTD_OTP_OFF 0 @@ -75,32 +116,35 @@ enum { #define MTD_OTP_USER 2 struct mtd_info_user { - uint8_t type; - uint32_t flags; - uint32_t size; /* Total size of the MTD */ - uint32_t erasesize; - uint32_t writesize; - uint32_t oobsize; /* Amount of OOB data per block (e.g. 16) */ - /* The below two fields are obsolete and broken, do not use them - * (TODO: remove at some point) */ - uint32_t ecctype; - uint32_t eccsize; + __u8 type; + __u32 flags; + __u32 size; /* Total size of the MTD */ + __u32 erasesize; + __u32 writesize; + __u32 oobsize; /* Amount of OOB data per block (e.g. 16) */ + __u64 padding; /* Old obsolete field; do not use */ }; struct region_info_user { - uint32_t offset; /* At which this region starts, - * from the beginning of the MTD */ - uint32_t erasesize; /* For this region */ - uint32_t numblocks; /* Number of blocks in this region */ - uint32_t regionindex; + __u32 offset; /* At which this region starts, + * from the beginning of the MTD */ + __u32 erasesize; /* For this region */ + __u32 numblocks; /* Number of blocks in this region */ + __u32 regionindex; }; struct otp_info { - uint32_t start; - uint32_t length; - uint32_t locked; + __u32 start; + __u32 length; + __u32 locked; }; +/* + * Note, the following ioctl existed in the past and was removed: + * #define MEMSETOOBSEL _IOW('M', 9, struct nand_oobinfo) + * Try to avoid adding a new ioctl with the same ioctl number. + */ + /* Get basic MTD characteristics info (better to use sysfs) */ #define MEMGETINFO _IOR('M', 1, struct mtd_info_user) /* Erase segment of MTD */ @@ -118,12 +162,11 @@ struct otp_info { /* Get information about the erase region for a specific index */ #define MEMGETREGIONINFO _IOWR('M', 8, struct region_info_user) /* Get info about OOB modes (e.g., RAW, PLACE, AUTO) - legacy interface */ -#define MEMSETOOBSEL _IOW('M', 9, struct nand_oobinfo) #define MEMGETOOBSEL _IOR('M', 10, struct nand_oobinfo) /* Check if an eraseblock is bad */ -#define MEMGETBADBLOCK _IOW('M', 11, loff_t) +#define MEMGETBADBLOCK _IOW('M', 11, __kernel_loff_t) /* Mark an eraseblock as bad */ -#define MEMSETBADBLOCK _IOW('M', 12, loff_t) +#define MEMSETBADBLOCK _IOW('M', 12, __kernel_loff_t) /* Set OTP (One-Time Programmable) mode (factory vs. user) */ #define OTPSELECT _IOR('M', 13, int) /* Get number of OTP (One-Time Programmable) regions */ @@ -133,26 +176,57 @@ struct otp_info { /* Lock a given range of user data (must be in mode %MTD_FILE_MODE_OTP_USER) */ #define OTPLOCK _IOR('M', 16, struct otp_info) /* Get ECC layout (deprecated) */ -#define ECCGETLAYOUT _IOR('M', 17, struct nand_ecclayout) +#define ECCGETLAYOUT _IOR('M', 17, struct nand_ecclayout_user) /* Get statistics about corrected/uncorrected errors */ #define ECCGETSTATS _IOR('M', 18, struct mtd_ecc_stats) /* Set MTD mode on a per-file-descriptor basis (see "MTD file modes") */ #define MTDFILEMODE _IO('M', 19) +/* Erase segment of MTD (supports 64-bit address) */ +#define MEMERASE64 _IOW('M', 20, struct erase_info_user64) +/* Write data to OOB (64-bit version) */ +#define MEMWRITEOOB64 _IOWR('M', 21, struct mtd_oob_buf64) +/* Read data from OOB (64-bit version) */ +#define MEMREADOOB64 _IOWR('M', 22, struct mtd_oob_buf64) +/* Check if chip is locked (for MTD that supports it) */ +#define MEMISLOCKED _IOR('M', 23, struct erase_info_user) +/* + * Most generic write interface; can write in-band and/or out-of-band in various + * modes (see "struct mtd_write_req"). This ioctl is not supported for flashes + * without OOB, e.g., NOR flash. + */ +#define MEMWRITE _IOWR('M', 24, struct mtd_write_req) /* * Obsolete legacy interface. Keep it in order not to break userspace * interfaces */ struct nand_oobinfo { - uint32_t useecc; - uint32_t eccbytes; - uint32_t oobfree[8][2]; - uint32_t eccpos[48]; + __u32 useecc; + __u32 eccbytes; + __u32 oobfree[8][2]; + __u32 eccpos[32]; }; struct nand_oobfree { - uint32_t offset; - uint32_t length; + __u32 offset; + __u32 length; +}; + +#define MTD_MAX_OOBFREE_ENTRIES 8 +#define MTD_MAX_ECCPOS_ENTRIES 64 +/* + * OBSOLETE: ECC layout control structure. Exported to user-space via ioctl + * ECCGETLAYOUT for backwards compatbility and should not be mistaken as a + * complete set of ECC information. The ioctl truncates the larger internal + * structure to retain binary compatibility with the static declaration of the + * ioctl. Note that the "MTD_MAX_..._ENTRIES" macros represent the max size of + * the user struct, not the MAX size of the internal struct nand_ecclayout. + */ +struct nand_ecclayout_user { + __u32 eccbytes; + __u32 eccpos[MTD_MAX_ECCPOS_ENTRIES]; + __u32 oobavail; + struct nand_oobfree oobfree[MTD_MAX_OOBFREE_ENTRIES]; }; /** @@ -164,10 +238,10 @@ struct nand_oobfree { * @bbtblocks: number of blocks reserved for bad block tables */ struct mtd_ecc_stats { - uint32_t corrected; - uint32_t failed; - uint32_t badblocks; - uint32_t bbtblocks; + __u32 corrected; + __u32 failed; + __u32 badblocks; + __u32 bbtblocks; }; /* @@ -188,10 +262,15 @@ struct mtd_ecc_stats { * used out of necessity (e.g., `write()', ioctl(MEMWRITEOOB64)). */ enum mtd_file_modes { - MTD_MODE_NORMAL = MTD_OTP_OFF, - MTD_MODE_OTP_FACTORY = MTD_OTP_FACTORY, - MTD_MODE_OTP_USER = MTD_OTP_USER, - MTD_MODE_RAW, + MTD_FILE_MODE_NORMAL = MTD_OTP_OFF, + MTD_FILE_MODE_OTP_FACTORY = MTD_OTP_FACTORY, + MTD_FILE_MODE_OTP_USER = MTD_OTP_USER, + MTD_FILE_MODE_RAW, }; +static inline int mtd_type_is_nand_user(const struct mtd_info_user *mtd) +{ + return mtd->type == MTD_NANDFLASH || mtd->type == MTD_MLCNANDFLASH; +} + #endif /* __MTD_ABI_H__ */ diff --git a/include/mtd/ubi-user.h b/include/mtd/ubi-user.h index 1ccc06e..22d9004 100644 --- a/include/mtd/ubi-user.h +++ b/include/mtd/ubi-user.h @@ -1,7 +1,7 @@ /* - * Copyright (c) International Business Machines Corp., 2006 + * Copyright © International Business Machines Corp., 2006 * - * SPDX-License-Identifier: GPL-2.0+ + * SPDX-License-Identifier: GPL-2.0+ * * Author: Artem Bityutskiy (Битюцкий Артём) */ @@ -9,6 +9,8 @@ #ifndef __UBI_USER_H__ #define __UBI_USER_H__ +#include <linux/types.h> + /* * UBI device creation (the same as MTD device attachment) * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -28,30 +30,37 @@ * UBI volume creation * ~~~~~~~~~~~~~~~~~~~ * - * UBI volumes are created via the %UBI_IOCMKVOL IOCTL command of UBI character + * UBI volumes are created via the %UBI_IOCMKVOL ioctl command of UBI character * device. A &struct ubi_mkvol_req object has to be properly filled and a - * pointer to it has to be passed to the IOCTL. + * pointer to it has to be passed to the ioctl. * * UBI volume deletion * ~~~~~~~~~~~~~~~~~~~ * - * To delete a volume, the %UBI_IOCRMVOL IOCTL command of the UBI character + * To delete a volume, the %UBI_IOCRMVOL ioctl command of the UBI character * device should be used. A pointer to the 32-bit volume ID hast to be passed - * to the IOCTL. + * to the ioctl. * * UBI volume re-size * ~~~~~~~~~~~~~~~~~~ * - * To re-size a volume, the %UBI_IOCRSVOL IOCTL command of the UBI character + * To re-size a volume, the %UBI_IOCRSVOL ioctl command of the UBI character * device should be used. A &struct ubi_rsvol_req object has to be properly - * filled and a pointer to it has to be passed to the IOCTL. + * filled and a pointer to it has to be passed to the ioctl. + * + * UBI volumes re-name + * ~~~~~~~~~~~~~~~~~~~ + * + * To re-name several volumes atomically at one go, the %UBI_IOCRNVOL command + * of the UBI character device should be used. A &struct ubi_rnvol_req object + * has to be properly filled and a pointer to it has to be passed to the ioctl. * * UBI volume update * ~~~~~~~~~~~~~~~~~ * - * Volume update should be done via the %UBI_IOCVOLUP IOCTL command of the + * Volume update should be done via the %UBI_IOCVOLUP ioctl command of the * corresponding UBI volume character device. A pointer to a 64-bit update - * size should be passed to the IOCTL. After this, UBI expects user to write + * size should be passed to the ioctl. After this, UBI expects user to write * this number of bytes to the volume character device. The update is finished * when the claimed number of bytes is passed. So, the volume update sequence * is something like: @@ -61,14 +70,68 @@ * write(fd, buf, image_size); * close(fd); * - * Atomic eraseblock change + * Logical eraseblock erase + * ~~~~~~~~~~~~~~~~~~~~~~~~ + * + * To erase a logical eraseblock, the %UBI_IOCEBER ioctl command of the + * corresponding UBI volume character device should be used. This command + * unmaps the requested logical eraseblock, makes sure the corresponding + * physical eraseblock is successfully erased, and returns. + * + * Atomic logical eraseblock change + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * Atomic logical eraseblock change operation is called using the %UBI_IOCEBCH + * ioctl command of the corresponding UBI volume character device. A pointer to + * a &struct ubi_leb_change_req object has to be passed to the ioctl. Then the + * user is expected to write the requested amount of bytes (similarly to what + * should be done in case of the "volume update" ioctl). + * + * Logical eraseblock map + * ~~~~~~~~~~~~~~~~~~~~~ + * + * To map a logical eraseblock to a physical eraseblock, the %UBI_IOCEBMAP + * ioctl command should be used. A pointer to a &struct ubi_map_req object is + * expected to be passed. The ioctl maps the requested logical eraseblock to + * a physical eraseblock and returns. Only non-mapped logical eraseblocks can + * be mapped. If the logical eraseblock specified in the request is already + * mapped to a physical eraseblock, the ioctl fails and returns error. + * + * Logical eraseblock unmap * ~~~~~~~~~~~~~~~~~~~~~~~~ * - * Atomic eraseblock change operation is done via the %UBI_IOCEBCH IOCTL - * command of the corresponding UBI volume character device. A pointer to - * &struct ubi_leb_change_req has to be passed to the IOCTL. Then the user is - * expected to write the requested amount of bytes. This is similar to the - * "volume update" IOCTL. + * To unmap a logical eraseblock to a physical eraseblock, the %UBI_IOCEBUNMAP + * ioctl command should be used. The ioctl unmaps the logical eraseblocks, + * schedules corresponding physical eraseblock for erasure, and returns. Unlike + * the "LEB erase" command, it does not wait for the physical eraseblock being + * erased. Note, the side effect of this is that if an unclean reboot happens + * after the unmap ioctl returns, you may find the LEB mapped again to the same + * physical eraseblock after the UBI is run again. + * + * Check if logical eraseblock is mapped + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * To check if a logical eraseblock is mapped to a physical eraseblock, the + * %UBI_IOCEBISMAP ioctl command should be used. It returns %0 if the LEB is + * not mapped, and %1 if it is mapped. + * + * Set an UBI volume property + * ~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * To set an UBI volume property the %UBI_IOCSETPROP ioctl command should be + * used. A pointer to a &struct ubi_set_vol_prop_req object is expected to be + * passed. The object describes which property should be set, and to which value + * it should be set. + * + * Block devices on UBI volumes + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * To create a R/O block device on top of an UBI volume the %UBI_IOCVOLCRBLK + * should be used. A pointer to a &struct ubi_blkcreate_req object is expected + * to be passed, which is not used and reserved for future usage. + * + * Conversely, to remove a block device the %UBI_IOCVOLRMBLK should be used, + * which takes no arguments. */ /* @@ -82,56 +145,60 @@ /* Maximum volume name length */ #define UBI_MAX_VOLUME_NAME 127 -/* IOCTL commands of UBI character devices */ +/* ioctl commands of UBI character devices */ #define UBI_IOC_MAGIC 'o' /* Create an UBI volume */ #define UBI_IOCMKVOL _IOW(UBI_IOC_MAGIC, 0, struct ubi_mkvol_req) /* Remove an UBI volume */ -#define UBI_IOCRMVOL _IOW(UBI_IOC_MAGIC, 1, int32_t) +#define UBI_IOCRMVOL _IOW(UBI_IOC_MAGIC, 1, __s32) /* Re-size an UBI volume */ #define UBI_IOCRSVOL _IOW(UBI_IOC_MAGIC, 2, struct ubi_rsvol_req) +/* Re-name volumes */ +#define UBI_IOCRNVOL _IOW(UBI_IOC_MAGIC, 3, struct ubi_rnvol_req) -/* IOCTL commands of the UBI control character device */ +/* ioctl commands of the UBI control character device */ #define UBI_CTRL_IOC_MAGIC 'o' /* Attach an MTD device */ #define UBI_IOCATT _IOW(UBI_CTRL_IOC_MAGIC, 64, struct ubi_attach_req) /* Detach an MTD device */ -#define UBI_IOCDET _IOW(UBI_CTRL_IOC_MAGIC, 65, int32_t) +#define UBI_IOCDET _IOW(UBI_CTRL_IOC_MAGIC, 65, __s32) -/* IOCTL commands of UBI volume character devices */ +/* ioctl commands of UBI volume character devices */ #define UBI_VOL_IOC_MAGIC 'O' -/* Start UBI volume update */ -#define UBI_IOCVOLUP _IOW(UBI_VOL_IOC_MAGIC, 0, int64_t) -/* An eraseblock erasure command, used for debugging, disabled by default */ -#define UBI_IOCEBER _IOW(UBI_VOL_IOC_MAGIC, 1, int32_t) -/* An atomic eraseblock change command */ -#define UBI_IOCEBCH _IOW(UBI_VOL_IOC_MAGIC, 2, int32_t) +/* Start UBI volume update + * Note: This actually takes a pointer (__s64*), but we can't change + * that without breaking the ABI on 32bit systems + */ +#define UBI_IOCVOLUP _IOW(UBI_VOL_IOC_MAGIC, 0, __s64) +/* LEB erasure command, used for debugging, disabled by default */ +#define UBI_IOCEBER _IOW(UBI_VOL_IOC_MAGIC, 1, __s32) +/* Atomic LEB change command */ +#define UBI_IOCEBCH _IOW(UBI_VOL_IOC_MAGIC, 2, __s32) +/* Map LEB command */ +#define UBI_IOCEBMAP _IOW(UBI_VOL_IOC_MAGIC, 3, struct ubi_map_req) +/* Unmap LEB command */ +#define UBI_IOCEBUNMAP _IOW(UBI_VOL_IOC_MAGIC, 4, __s32) +/* Check if LEB is mapped command */ +#define UBI_IOCEBISMAP _IOR(UBI_VOL_IOC_MAGIC, 5, __s32) +/* Set an UBI volume property */ +#define UBI_IOCSETVOLPROP _IOW(UBI_VOL_IOC_MAGIC, 6, \ + struct ubi_set_vol_prop_req) +/* Create a R/O block device on top of an UBI volume */ +#define UBI_IOCVOLCRBLK _IOW(UBI_VOL_IOC_MAGIC, 7, struct ubi_blkcreate_req) +/* Remove the R/O block device */ +#define UBI_IOCVOLRMBLK _IO(UBI_VOL_IOC_MAGIC, 8) /* Maximum MTD device name length supported by UBI */ #define MAX_UBI_MTD_NAME_LEN 127 -/* - * UBI data type hint constants. - * - * UBI_LONGTERM: long-term data - * UBI_SHORTTERM: short-term data - * UBI_UNKNOWN: data persistence is unknown - * - * These constants are used when data is written to UBI volumes in order to - * help the UBI wear-leveling unit to find more appropriate physical - * eraseblocks. - */ -enum { - UBI_LONGTERM = 1, - UBI_SHORTTERM = 2, - UBI_UNKNOWN = 3, -}; +/* Maximum amount of UBI volumes that can be re-named at one go */ +#define UBI_MAX_RNVOL 32 /* * UBI volume type constants. @@ -144,11 +211,23 @@ enum { UBI_STATIC_VOLUME = 4, }; +/* + * UBI set volume property ioctl constants. + * + * @UBI_VOL_PROP_DIRECT_WRITE: allow (any non-zero value) or disallow (value 0) + * user to directly write and erase individual + * eraseblocks on dynamic volumes + */ +enum { + UBI_VOL_PROP_DIRECT_WRITE = 1, +}; + /** * struct ubi_attach_req - attach MTD device request. * @ubi_num: UBI device number to create * @mtd_num: MTD device number to attach * @vid_hdr_offset: VID header offset (use defaults if %0) + * @max_beb_per1024: maximum expected number of bad PEB per 1024 PEBs * @padding: reserved for future, not used, has to be zeroed * * This data structure is used to specify MTD device UBI has to attach and the @@ -164,20 +243,33 @@ enum { * it will be 512 in case of a 2KiB page NAND flash with 4 512-byte sub-pages. * * But in rare cases, if this optimizes things, the VID header may be placed to - * a different offset. For example, the boot-loader might do things faster if the - * VID header sits at the end of the first 2KiB NAND page with 4 sub-pages. As - * the boot-loader would not normally need to read EC headers (unless it needs - * UBI in RW mode), it might be faster to calculate ECC. This is weird example, - * but it real-life example. So, in this example, @vid_hdr_offer would be - * 2KiB-64 bytes = 1984. Note, that this position is not even 512-bytes - * aligned, which is OK, as UBI is clever enough to realize this is 4th sub-page - * of the first page and add needed padding. + * a different offset. For example, the boot-loader might do things faster if + * the VID header sits at the end of the first 2KiB NAND page with 4 sub-pages. + * As the boot-loader would not normally need to read EC headers (unless it + * needs UBI in RW mode), it might be faster to calculate ECC. This is weird + * example, but it real-life example. So, in this example, @vid_hdr_offer would + * be 2KiB-64 bytes = 1984. Note, that this position is not even 512-bytes + * aligned, which is OK, as UBI is clever enough to realize this is 4th + * sub-page of the first page and add needed padding. + * + * The @max_beb_per1024 is the maximum amount of bad PEBs UBI expects on the + * UBI device per 1024 eraseblocks. This value is often given in an other form + * in the NAND datasheet (min NVB i.e. minimal number of valid blocks). The + * maximum expected bad eraseblocks per 1024 is then: + * 1024 * (1 - MinNVB / MaxNVB) + * Which gives 20 for most NAND devices. This limit is used in order to derive + * amount of eraseblock UBI reserves for handling new bad blocks. If the device + * has more bad eraseblocks than this limit, UBI does not reserve any physical + * eraseblocks for new bad eraseblocks, but attempts to use available + * eraseblocks (if any). The accepted range is 0-768. If 0 is given, the + * default kernel value of %CONFIG_MTD_UBI_BEB_LIMIT will be used. */ struct ubi_attach_req { - int32_t ubi_num; - int32_t mtd_num; - int32_t vid_hdr_offset; - uint8_t padding[12]; + __s32 ubi_num; + __s32 mtd_num; + __s32 vid_hdr_offset; + __s16 max_beb_per1024; + __s8 padding[10]; }; /** @@ -212,15 +304,15 @@ struct ubi_attach_req { * BLOBs, without caring about how to properly align them. */ struct ubi_mkvol_req { - int32_t vol_id; - int32_t alignment; - int64_t bytes; - int8_t vol_type; - int8_t padding1; - int16_t name_len; - int8_t padding2[4]; + __s32 vol_id; + __s32 alignment; + __s64 bytes; + __s8 vol_type; + __s8 padding1; + __s16 name_len; + __s8 padding2[4]; char name[UBI_MAX_VOLUME_NAME + 1]; -} __attribute__ ((packed)); +} __packed; /** * struct ubi_rsvol_req - a data structure used in volume re-size requests. @@ -229,28 +321,113 @@ struct ubi_mkvol_req { * * Re-sizing is possible for both dynamic and static volumes. But while dynamic * volumes may be re-sized arbitrarily, static volumes cannot be made to be - * smaller then the number of bytes they bear. To arbitrarily shrink a static + * smaller than the number of bytes they bear. To arbitrarily shrink a static * volume, it must be wiped out first (by means of volume update operation with * zero number of bytes). */ struct ubi_rsvol_req { - int64_t bytes; - int32_t vol_id; -} __attribute__ ((packed)); + __s64 bytes; + __s32 vol_id; +} __packed; + +/** + * struct ubi_rnvol_req - volumes re-name request. + * @count: count of volumes to re-name + * @padding1: reserved for future, not used, has to be zeroed + * @vol_id: ID of the volume to re-name + * @name_len: name length + * @padding2: reserved for future, not used, has to be zeroed + * @name: new volume name + * + * UBI allows to re-name up to %32 volumes at one go. The count of volumes to + * re-name is specified in the @count field. The ID of the volumes to re-name + * and the new names are specified in the @vol_id and @name fields. + * + * The UBI volume re-name operation is atomic, which means that should power cut + * happen, the volumes will have either old name or new name. So the possible + * use-cases of this command is atomic upgrade. Indeed, to upgrade, say, volumes + * A and B one may create temporary volumes %A1 and %B1 with the new contents, + * then atomically re-name A1->A and B1->B, in which case old %A and %B will + * be removed. + * + * If it is not desirable to remove old A and B, the re-name request has to + * contain 4 entries: A1->A, A->A1, B1->B, B->B1, in which case old A1 and B1 + * become A and B, and old A and B will become A1 and B1. + * + * It is also OK to request: A1->A, A1->X, B1->B, B->Y, in which case old A1 + * and B1 become A and B, and old A and B become X and Y. + * + * In other words, in case of re-naming into an existing volume name, the + * existing volume is removed, unless it is re-named as well at the same + * re-name request. + */ +struct ubi_rnvol_req { + __s32 count; + __s8 padding1[12]; + struct { + __s32 vol_id; + __s16 name_len; + __s8 padding2[2]; + char name[UBI_MAX_VOLUME_NAME + 1]; + } ents[UBI_MAX_RNVOL]; +} __packed; /** - * struct ubi_leb_change_req - a data structure used in atomic logical - * eraseblock change requests. + * struct ubi_leb_change_req - a data structure used in atomic LEB change + * requests. * @lnum: logical eraseblock number to change * @bytes: how many bytes will be written to the logical eraseblock - * @dtype: data type (%UBI_LONGTERM, %UBI_SHORTTERM, %UBI_UNKNOWN) + * @dtype: pass "3" for better compatibility with old kernels * @padding: reserved for future, not used, has to be zeroed + * + * The @dtype field used to inform UBI about what kind of data will be written + * to the LEB: long term (value 1), short term (value 2), unknown (value 3). + * UBI tried to pick a PEB with lower erase counter for short term data and a + * PEB with higher erase counter for long term data. But this was not really + * used because users usually do not know this and could easily mislead UBI. We + * removed this feature in May 2012. UBI currently just ignores the @dtype + * field. But for better compatibility with older kernels it is recommended to + * set @dtype to 3 (unknown). */ struct ubi_leb_change_req { - int32_t lnum; - int32_t bytes; - uint8_t dtype; - uint8_t padding[7]; -} __attribute__ ((packed)); + __s32 lnum; + __s32 bytes; + __s8 dtype; /* obsolete, do not use! */ + __s8 padding[7]; +} __packed; + +/** + * struct ubi_map_req - a data structure used in map LEB requests. + * @dtype: pass "3" for better compatibility with old kernels + * @lnum: logical eraseblock number to unmap + * @padding: reserved for future, not used, has to be zeroed + */ +struct ubi_map_req { + __s32 lnum; + __s8 dtype; /* obsolete, do not use! */ + __s8 padding[3]; +} __packed; + + +/** + * struct ubi_set_vol_prop_req - a data structure used to set an UBI volume + * property. + * @property: property to set (%UBI_VOL_PROP_DIRECT_WRITE) + * @padding: reserved for future, not used, has to be zeroed + * @value: value to set + */ +struct ubi_set_vol_prop_req { + __u8 property; + __u8 padding[7]; + __u64 value; +} __packed; + +/** + * struct ubi_blkcreate_req - a data structure used in block creation requests. + * @padding: reserved for future, not used, has to be zeroed + */ +struct ubi_blkcreate_req { + __s8 padding[128]; +} __packed; #endif /* __UBI_USER_H__ */ diff --git a/include/netdev.h b/include/netdev.h index e45dd7a..260c8d0 100644 --- a/include/netdev.h +++ b/include/netdev.h @@ -69,7 +69,6 @@ int ne2k_register(void); int npe_initialize(bd_t *bis); int ns8382x_initialize(bd_t *bis); int pcnet_initialize(bd_t *bis); -int plb2800_eth_initialize(bd_t *bis); int ppc_4xx_eth_initialize (bd_t *bis); int rtl8139_initialize(bd_t *bis); int rtl8169_initialize(bd_t *bis); diff --git a/include/nios2-epcs.h b/include/nios2-epcs.h deleted file mode 100644 index 8c8f238..0000000 --- a/include/nios2-epcs.h +++ /dev/null @@ -1,60 +0,0 @@ -/* - * (C) Copyright 2004, Psyent Corporation <www.psyent.com> - * Scott McNutt <smcnutt@psyent.com> - * - * SPDX-License-Identifier: GPL-2.0+ - */ - -/************************************************************************* - * Altera Nios-II EPCS Controller Core interfaces - ************************************************************************/ - -#ifndef __NIOS2_EPCS_H__ -#define __NIOS2_EPCS_H__ - -typedef struct epcs_devinfo_t { - const char *name; /* Device name */ - unsigned char id; /* Device silicon id */ - unsigned char size; /* Total size log2(bytes)*/ - unsigned char num_sects; /* Number of sectors */ - unsigned char sz_sect; /* Sector size log2(bytes) */ - unsigned char sz_page; /* Page size log2(bytes) */ - unsigned char prot_mask; /* Protection mask */ -}epcs_devinfo_t; - -/* Resets the epcs controller -- to prevent (potential) soft-reset - * problems when booting from the epcs controller - */ -extern int epcs_reset (void); - -/* Returns the devinfo struct if EPCS device is found; - * NULL otherwise. - */ -extern epcs_devinfo_t *epcs_dev_find (void); - -/* Returns the number of bytes used by config data. - * Negative on error. - */ -extern int epcs_cfgsz (void); - -/* Erase sectors 'start' to 'end' - return zero on success - */ -extern int epcs_erase (unsigned start, unsigned end); - -/* Read 'cnt' bytes from device offset 'off' into buf at 'addr' - * Zero return on success - */ -extern int epcs_read (ulong addr, ulong off, ulong cnt); - -/* Write 'cnt' bytes to device offset 'off' from buf at 'addr'. - * Zero return on success - */ -extern int epcs_write (ulong addr, ulong off, ulong cnt); - -/* Verify 'cnt' bytes at device offset 'off' comparing with buf - * at 'addr'. On failure, write first invalid offset to *err. - * Zero return on success - */ -extern int epcs_verify (ulong addr, ulong off, ulong cnt, ulong *err); - -#endif /* __NIOS2_EPCS_H__ */ diff --git a/include/ns16550.h b/include/ns16550.h index 17f829f..d1f3a90 100644 --- a/include/ns16550.h +++ b/include/ns16550.h @@ -64,8 +64,6 @@ struct NS16550 { UART_REG(uasr); /* F */ UART_REG(scr); /* 10*/ UART_REG(ssr); /* 11*/ - UART_REG(reg12); /* 12*/ - UART_REG(osc_12m_sel); /* 13*/ #endif }; @@ -164,11 +162,6 @@ typedef struct NS16550 *NS16550_t; #define UART_IER_THRI 0x02 /* Enable Transmitter holding register int. */ #define UART_IER_RDI 0x01 /* Enable receiver data interrupt */ - -#ifdef CONFIG_OMAP1510 -#define OSC_12M_SEL 0x01 /* selects 6.5 * current clk div */ -#endif - /* useful defaults for LCR */ #define UART_LCR_8N1 0x03 diff --git a/include/pci_ids.h b/include/pci_ids.h index 6bab677..f220c3a 100644 --- a/include/pci_ids.h +++ b/include/pci_ids.h @@ -2546,6 +2546,13 @@ #define PCI_DEVICE_ID_INTEL_82441 0x1237 #define PCI_DEVICE_ID_INTEL_82380FB 0x124b #define PCI_DEVICE_ID_INTEL_82439 0x1250 +#define PCI_DEVICE_ID_INTEL_I210_UNPROGRAMMED 0x1531 +#define PCI_DEVICE_ID_INTEL_I210_COPPER 0x1533 +#define PCI_DEVICE_ID_INTEL_I210_SERDES 0x1536 +#define PCI_DEVICE_ID_INTEL_I210_1000BASEKX 0x1537 +#define PCI_DEVICE_ID_INTEL_I210_EXTPHY 0x1538 +#define PCI_DEVICE_ID_INTEL_I210_COPPER_FLASHLESS 0x157b +#define PCI_DEVICE_ID_INTEL_I210_SERDES_FLASHLESS 0x157c #define PCI_DEVICE_ID_INTEL_80960_RP 0x1960 #define PCI_DEVICE_ID_INTEL_82840_HB 0x1a21 #define PCI_DEVICE_ID_INTEL_82845_HB 0x1a30 diff --git a/include/pcmcia.h b/include/pcmcia.h index 8aec254..4b667f4 100644 --- a/include/pcmcia.h +++ b/include/pcmcia.h @@ -21,7 +21,7 @@ #if !defined(CONFIG_PCMCIA_SLOT_A) && !defined(CONFIG_PCMCIA_SLOT_B) -#if defined(CONFIG_TQM8xxL) || defined(CONFIG_SVM_SC8xx) +#if defined(CONFIG_TQM8xxL) # define CONFIG_PCMCIA_SLOT_B /* The TQM8xxL use SLOT_B */ #elif defined(CONFIG_SPD823TS) /* The SPD8xx use SLOT_B */ # define CONFIG_PCMCIA_SLOT_B diff --git a/include/sandboxfs.h b/include/sandboxfs.h index a51ad13..e7c3262 100644 --- a/include/sandboxfs.h +++ b/include/sandboxfs.h @@ -26,6 +26,7 @@ long sandbox_fs_read_at(const char *filename, unsigned long pos, void sandbox_fs_close(void); int sandbox_fs_ls(const char *dirname); int sandbox_fs_exists(const char *filename); +int sandbox_fs_size(const char *filename); int fs_read_sandbox(const char *filename, void *buf, int offset, int len); int fs_write_sandbox(const char *filename, void *buf, int offset, int len); diff --git a/include/sparse_format.h b/include/sparse_format.h new file mode 100644 index 0000000..af67581 --- /dev/null +++ b/include/sparse_format.h @@ -0,0 +1,49 @@ +/* + * This is from the Android Project, + * Repository: https://android.googlesource.com/platform/system/core + * File: libsparse/sparse_format.h + * Commit: 28fa5bc347390480fe190294c6c385b6a9f0d68b + * Copyright (C) 2010 The Android Open Source Project + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef _LIBSPARSE_SPARSE_FORMAT_H_ +#define _LIBSPARSE_SPARSE_FORMAT_H_ +#include "sparse_defs.h" + +typedef struct sparse_header { + __le32 magic; /* 0xed26ff3a */ + __le16 major_version; /* (0x1) - reject images with higher major versions */ + __le16 minor_version; /* (0x0) - allow images with higer minor versions */ + __le16 file_hdr_sz; /* 28 bytes for first revision of the file format */ + __le16 chunk_hdr_sz; /* 12 bytes for first revision of the file format */ + __le32 blk_sz; /* block size in bytes, must be a multiple of 4 (4096) */ + __le32 total_blks; /* total blocks in the non-sparse output image */ + __le32 total_chunks; /* total chunks in the sparse input image */ + __le32 image_checksum; /* CRC32 checksum of the original data, counting "don't care" */ + /* as 0. Standard 802.3 polynomial, use a Public Domain */ + /* table implementation */ +} sparse_header_t; + +#define SPARSE_HEADER_MAGIC 0xed26ff3a + +#define CHUNK_TYPE_RAW 0xCAC1 +#define CHUNK_TYPE_FILL 0xCAC2 +#define CHUNK_TYPE_DONT_CARE 0xCAC3 +#define CHUNK_TYPE_CRC32 0xCAC4 + +typedef struct chunk_header { + __le16 chunk_type; /* 0xCAC1 -> raw; 0xCAC2 -> fill; 0xCAC3 -> don't care */ + __le16 reserved1; + __le32 chunk_sz; /* in blocks in output image */ + __le32 total_sz; /* in bytes of chunk input file including chunk header and data */ +} chunk_header_t; + +/* Following a Raw or Fill or CRC32 chunk is data. + * For a Raw chunk, it's the data in chunk_sz * blk_sz. + * For a Fill chunk, it's 4 bytes of the fill data. + * For a CRC32 chunk, it's 4 bytes of CRC32 + */ + +#endif diff --git a/include/status_led.h b/include/status_led.h index b8aaaf7..c1d2242 100644 --- a/include/status_led.h +++ b/include/status_led.h @@ -56,30 +56,6 @@ void status_led_set (int led, int state); # define STATUS_LED_BOOT 0 /* LED 0 used for boot status */ -/***** GEN860T *********************************************************/ -#elif defined(CONFIG_GEN860T) - -# define STATUS_LED_PAR im_ioport.iop_papar -# define STATUS_LED_DIR im_ioport.iop_padir -# define STATUS_LED_ODR im_ioport.iop_paodr -# define STATUS_LED_DAT im_ioport.iop_padat - -# define STATUS_LED_BIT 0x0800 /* Red LED 0 is on PA.4 */ -# define STATUS_LED_PERIOD (CONFIG_SYS_HZ / 4) -# define STATUS_LED_STATE STATUS_LED_OFF -# define STATUS_LED_BIT1 0x0400 /* Grn LED 1 is on PA.5 */ -# define STATUS_LED_PERIOD1 (CONFIG_SYS_HZ / 8) -# define STATUS_LED_STATE1 STATUS_LED_BLINKING -# define STATUS_LED_BIT2 0x0080 /* Red LED 2 is on PA.8 */ -# define STATUS_LED_PERIOD2 (CONFIG_SYS_HZ / 4) -# define STATUS_LED_STATE2 STATUS_LED_OFF -# define STATUS_LED_BIT3 0x0040 /* Grn LED 3 is on PA.9 */ -# define STATUS_LED_PERIOD3 (CONFIG_SYS_HZ / 4) -# define STATUS_LED_STATE3 STATUS_LED_OFF - -# define STATUS_LED_ACTIVE 1 /* LED on for bit == 1 */ -# define STATUS_LED_BOOT 1 /* Boot status on LED 1 */ - /***** IVMS8 **********************************************************/ #elif defined(CONFIG_IVMS8) @@ -217,24 +193,6 @@ void status_led_set (int led, int state); # define STATUS_LED_BOOT 0 /* LED 0 used for boot status */ -#elif defined(CONFIG_SVM_SC8xx) -# define STATUS_LED_PAR im_cpm.cp_pbpar -# define STATUS_LED_DIR im_cpm.cp_pbdir -# define STATUS_LED_ODR im_cpm.cp_pbodr -# define STATUS_LED_DAT im_cpm.cp_pbdat - -# define STATUS_LED_BIT 0x00000001 -# define STATUS_LED_PERIOD (CONFIG_SYS_HZ / 2) -# define STATUS_LED_STATE STATUS_LED_BLINKING - -# define STATUS_LED_ACTIVE 1 /* LED on for bit == 1 */ - -# define STATUS_LED_BOOT 0 /* LED 0 used for boot status */ - -/***** STx XTc ********************************************************/ -#elif defined(CONFIG_STXXTC) -/* XXX empty just to avoid the error */ -/************************************************************************/ #elif defined(CONFIG_V38B) # define STATUS_LED_BIT 0x0010 /* Timer7 GPIO */ diff --git a/include/u-boot/rsa.h b/include/u-boot/rsa.h index 325751a..fd08a61 100644 --- a/include/u-boot/rsa.h +++ b/include/u-boot/rsa.h @@ -27,6 +27,7 @@ struct rsa_public_key { uint32_t n0inv; /* -1 / modulus[0] mod 2^32 */ uint32_t *modulus; /* modulus as little endian array */ uint32_t *rr; /* R^2 as little endian array */ + uint64_t exponent; /* public exponent */ }; #if IMAGE_ENABLE_SIGN diff --git a/include/ubi_uboot.h b/include/ubi_uboot.h index 7f72022..1fd15f4 100644 --- a/include/ubi_uboot.h +++ b/include/ubi_uboot.h @@ -16,8 +16,10 @@ #include <common.h> #include <compiler.h> +#include <linux/compat.h> #include <malloc.h> #include <div64.h> +#include <linux/math64.h> #include <linux/crc32.h> #include <linux/types.h> #include <linux/list.h> @@ -32,15 +34,11 @@ #include <asm/errno.h> -#define DPRINTK(format, args...) \ -do { \ - printf("%s[%d]: " format "\n", __func__, __LINE__, ##args); \ -} while (0) - /* configurable */ +#if !defined(CONFIG_MTD_UBI_WL_THRESHOLD) #define CONFIG_MTD_UBI_WL_THRESHOLD 4096 +#endif #define CONFIG_MTD_UBI_BEB_RESERVE 1 -#define UBI_IO_DEBUG 0 /* debug options (Linux: drivers/mtd/ubi/Kconfig.debug) */ #undef CONFIG_MTD_UBI_DEBUG @@ -50,161 +48,18 @@ do { \ #undef CONFIG_MTD_UBI_DEBUG_MSG_WL #undef CONFIG_MTD_UBI_DEBUG_MSG_IO #undef CONFIG_MTD_UBI_DEBUG_MSG_BLD -#define CONFIG_MTD_UBI_DEBUG_DISABLE_BGT + +#undef CONFIG_MTD_UBI_BLOCK + +#if !defined(CONFIG_MTD_UBI_BEB_LIMIT) +#define CONFIG_MTD_UBI_BEB_LIMIT 20 +#endif /* 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 */ -static inline unsigned long copy_from_user(void *dest, const void *src, - unsigned long count) -{ - memcpy((void *)dest, (void *)src, count); - return 0; -} - -/* 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(...) 1 -#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) - -#define BUS_ID_SIZE 20 - -struct rw_semaphore { int i; }; -struct device { - struct device *parent; - struct class *class; - char bus_id[BUS_ID_SIZE]; /* position on parent bus */ - dev_t devt; /* dev_t, creates the sysfs "dev" */ - 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); -} - -/* module */ -#define THIS_MODULE 0 -#define try_module_get(...) 1 -#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(...) #ifndef __UBIFS_H__ #include "../drivers/mtd/ubi/ubi.h" diff --git a/include/usb/lin_gadget_compat.h b/include/usb/lin_gadget_compat.h index a25e9d9..29fb166 100644 --- a/include/usb/lin_gadget_compat.h +++ b/include/usb/lin_gadget_compat.h @@ -13,22 +13,6 @@ #include <linux/compat.h> /* common */ -#define spin_lock_init(...) -#define spin_lock(...) -#define spin_lock_irqsave(lock, flags) do { debug("%lu\n", flags); } while (0) -#define spin_unlock(...) -#define spin_unlock_irqrestore(lock, flags) do {flags = 0; } while (0) -#define disable_irq(...) -#define enable_irq(...) - -#define mutex_init(...) -#define mutex_lock(...) -#define mutex_unlock(...) - -#define GFP_KERNEL 0 - -#define IRQ_HANDLED 1 - #define ENOTSUPP 524 /* Operation is not supported */ #define BITS_PER_BYTE 8 diff --git a/include/usb/udc.h b/include/usb/udc.h index 1f545ec..b2e0c6b 100644 --- a/include/usb/udc.h +++ b/include/usb/udc.h @@ -12,8 +12,8 @@ #define EP_MAX_PACKET_SIZE 64 #endif -#if !defined(CONFIG_PPC) && !defined(CONFIG_OMAP1510) -/* omap1510_udc.h and mpc8xx_udc.h will set these values */ +#if !defined(CONFIG_PPC) +/* mpc8xx_udc.h will set these values */ #define UDC_OUT_PACKET_SIZE EP_MAX_PACKET_SIZE #define UDC_IN_PACKET_SIZE EP_MAX_PACKET_SIZE #define UDC_INT_PACKET_SIZE EP_MAX_PACKET_SIZE diff --git a/include/watchdog.h b/include/watchdog.h index bd0a8d6..9273fa1 100644 --- a/include/watchdog.h +++ b/include/watchdog.h @@ -21,7 +21,8 @@ int init_func_watchdog_reset(void); #endif -#ifdef CONFIG_WATCHDOG +#if defined(CONFIG_SYS_GENERIC_BOARD) && \ + (defined(CONFIG_WATCHDOG) || defined(CONFIG_HW_WATCHDOG)) #define INIT_FUNC_WATCHDOG_INIT init_func_watchdog_init, #define INIT_FUNC_WATCHDOG_RESET init_func_watchdog_reset, #else diff --git a/lib/Makefile b/lib/Makefile index 68210a5..320197a 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -43,6 +43,7 @@ obj-y += strmhz.o obj-$(CONFIG_TPM) += tpm.o obj-$(CONFIG_RBTREE) += rbtree.o obj-$(CONFIG_BITREVERSE) += bitrev.o +obj-y += list_sort.o endif ifdef CONFIG_SPL_BUILD @@ -58,6 +59,7 @@ obj-y += crc32.o obj-y += ctype.o obj-y += div64.o obj-y += hang.o +obj-y += linux_compat.o obj-y += linux_string.o obj-$(CONFIG_REGEX) += slre.o obj-y += string.o diff --git a/lib/libfdt/Makefile b/lib/libfdt/Makefile index a02c9b0..6fe79e0 100644 --- a/lib/libfdt/Makefile +++ b/lib/libfdt/Makefile @@ -5,7 +5,8 @@ # SPDX-License-Identifier: GPL-2.0+ # -COBJS-libfdt += fdt.o fdt_ro.o fdt_rw.o fdt_strerror.o fdt_sw.o fdt_wip.o fdt_empty_tree.o +COBJS-libfdt += fdt.o fdt_ro.o fdt_rw.o fdt_strerror.o fdt_sw.o fdt_wip.o \ + fdt_empty_tree.o fdt_addresses.o obj-$(CONFIG_OF_LIBFDT) += $(COBJS-libfdt) obj-$(CONFIG_FIT) += $(COBJS-libfdt) diff --git a/lib/libfdt/fdt_addresses.c b/lib/libfdt/fdt_addresses.c new file mode 100644 index 0000000..76054d9 --- /dev/null +++ b/lib/libfdt/fdt_addresses.c @@ -0,0 +1,55 @@ +/* + * libfdt - Flat Device Tree manipulation + * Copyright (C) 2014 David Gibson <david@gibson.dropbear.id.au> + * SPDX-License-Identifier: GPL-2.0+ BSD-2-Clause + */ +#include "libfdt_env.h" + +#ifndef USE_HOSTCC +#include <fdt.h> +#include <libfdt.h> +#else +#include "fdt_host.h" +#endif + +#include "libfdt_internal.h" + +int fdt_address_cells(const void *fdt, int nodeoffset) +{ + const fdt32_t *ac; + int val; + int len; + + ac = fdt_getprop(fdt, nodeoffset, "#address-cells", &len); + if (!ac) + return 2; + + if (len != sizeof(*ac)) + return -FDT_ERR_BADNCELLS; + + val = fdt32_to_cpu(*ac); + if ((val <= 0) || (val > FDT_MAX_NCELLS)) + return -FDT_ERR_BADNCELLS; + + return val; +} + +int fdt_size_cells(const void *fdt, int nodeoffset) +{ + const fdt32_t *sc; + int val; + int len; + + sc = fdt_getprop(fdt, nodeoffset, "#size-cells", &len); + if (!sc) + return 2; + + if (len != sizeof(*sc)) + return -FDT_ERR_BADNCELLS; + + val = fdt32_to_cpu(*sc); + if ((val < 0) || (val > FDT_MAX_NCELLS)) + return -FDT_ERR_BADNCELLS; + + return val; +} diff --git a/lib/libfdt/fdt_rw.c b/lib/libfdt/fdt_rw.c index 6fa4f13..bec8b8a 100644 --- a/lib/libfdt/fdt_rw.c +++ b/lib/libfdt/fdt_rw.c @@ -43,9 +43,9 @@ static int _fdt_rw_check_header(void *fdt) #define FDT_RW_CHECK_HEADER(fdt) \ { \ - int err; \ - if ((err = _fdt_rw_check_header(fdt)) != 0) \ - return err; \ + int __err; \ + if ((__err = _fdt_rw_check_header(fdt)) != 0) \ + return __err; \ } static inline int _fdt_data_size(void *fdt) diff --git a/lib/libfdt/fdt_sw.c b/lib/libfdt/fdt_sw.c index 580b570..320a914 100644 --- a/lib/libfdt/fdt_sw.c +++ b/lib/libfdt/fdt_sw.c @@ -62,6 +62,38 @@ int fdt_create(void *buf, int bufsize) return 0; } +int fdt_resize(void *fdt, void *buf, int bufsize) +{ + size_t headsize, tailsize; + char *oldtail, *newtail; + + FDT_SW_CHECK_HEADER(fdt); + + headsize = fdt_off_dt_struct(fdt); + tailsize = fdt_size_dt_strings(fdt); + + if ((headsize + tailsize) > bufsize) + return -FDT_ERR_NOSPACE; + + oldtail = (char *)fdt + fdt_totalsize(fdt) - tailsize; + newtail = (char *)buf + bufsize - tailsize; + + /* Two cases to avoid clobbering data if the old and new + * buffers partially overlap */ + if (buf <= fdt) { + memmove(buf, fdt, headsize); + memmove(newtail, oldtail, tailsize); + } else { + memmove(newtail, oldtail, tailsize); + memmove(buf, fdt, headsize); + } + + fdt_set_off_dt_strings(buf, bufsize); + fdt_set_totalsize(buf, bufsize); + + return 0; +} + int fdt_add_reservemap_entry(void *fdt, uint64_t addr, uint64_t size) { struct fdt_reserve_entry *re; diff --git a/lib/libfdt/libfdt_internal.h b/lib/libfdt/libfdt_internal.h index 13cbc9a..9a79fe8 100644 --- a/lib/libfdt/libfdt_internal.h +++ b/lib/libfdt/libfdt_internal.h @@ -12,9 +12,9 @@ #define FDT_CHECK_HEADER(fdt) \ { \ - int err; \ - if ((err = fdt_check_header(fdt)) != 0) \ - return err; \ + int __err; \ + if ((__err = fdt_check_header(fdt)) != 0) \ + return __err; \ } int _fdt_check_node_offset(const void *fdt, int offset); diff --git a/lib/linux_compat.c b/lib/linux_compat.c new file mode 100644 index 0000000..a3d4675 --- /dev/null +++ b/lib/linux_compat.c @@ -0,0 +1,47 @@ + +#include <common.h> +#include <linux/compat.h> + +struct p_current cur = { + .pid = 1, +}; +__maybe_unused struct p_current *current = &cur; + +unsigned long copy_from_user(void *dest, const void *src, + unsigned long count) +{ + memcpy((void *)dest, (void *)src, count); + return 0; +} + +void *kmalloc(size_t size, int flags) +{ + return memalign(ARCH_DMA_MINALIGN, size); +} + +void *kzalloc(size_t size, int flags) +{ + void *ptr = kmalloc(size, flags); + memset(ptr, 0, size); + return ptr; +} + +void *vzalloc(unsigned long size) +{ + return kzalloc(size, 0); +} + +struct kmem_cache *get_mem(int element_sz) +{ + struct kmem_cache *ret; + + ret = memalign(ARCH_DMA_MINALIGN, sizeof(struct kmem_cache)); + ret->sz = element_sz; + + return ret; +} + +void *kmem_cache_alloc(struct kmem_cache *obj, int flag) +{ + return memalign(ARCH_DMA_MINALIGN, obj->sz); +} diff --git a/lib/list_sort.c b/lib/list_sort.c new file mode 100644 index 0000000..81de0a1 --- /dev/null +++ b/lib/list_sort.c @@ -0,0 +1,298 @@ +#define __UBOOT__ +#ifndef __UBOOT__ +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#else +#include <linux/compat.h> +#include <common.h> +#include <malloc.h> +#endif +#include <linux/list.h> +#include <linux/list_sort.h> + +#define MAX_LIST_LENGTH_BITS 20 + +/* + * Returns a list organized in an intermediate format suited + * to chaining of merge() calls: null-terminated, no reserved or + * sentinel head node, "prev" links not maintained. + */ +static struct list_head *merge(void *priv, + int (*cmp)(void *priv, struct list_head *a, + struct list_head *b), + struct list_head *a, struct list_head *b) +{ + struct list_head head, *tail = &head; + + while (a && b) { + /* if equal, take 'a' -- important for sort stability */ + if ((*cmp)(priv, a, b) <= 0) { + tail->next = a; + a = a->next; + } else { + tail->next = b; + b = b->next; + } + tail = tail->next; + } + tail->next = a?:b; + return head.next; +} + +/* + * Combine final list merge with restoration of standard doubly-linked + * list structure. This approach duplicates code from merge(), but + * runs faster than the tidier alternatives of either a separate final + * prev-link restoration pass, or maintaining the prev links + * throughout. + */ +static void merge_and_restore_back_links(void *priv, + int (*cmp)(void *priv, struct list_head *a, + struct list_head *b), + struct list_head *head, + struct list_head *a, struct list_head *b) +{ + struct list_head *tail = head; + + while (a && b) { + /* if equal, take 'a' -- important for sort stability */ + if ((*cmp)(priv, a, b) <= 0) { + tail->next = a; + a->prev = tail; + a = a->next; + } else { + tail->next = b; + b->prev = tail; + b = b->next; + } + tail = tail->next; + } + tail->next = a ? : b; + + do { + /* + * In worst cases this loop may run many iterations. + * Continue callbacks to the client even though no + * element comparison is needed, so the client's cmp() + * routine can invoke cond_resched() periodically. + */ + (*cmp)(priv, tail->next, tail->next); + + tail->next->prev = tail; + tail = tail->next; + } while (tail->next); + + tail->next = head; + head->prev = tail; +} + +/** + * list_sort - sort a list + * @priv: private data, opaque to list_sort(), passed to @cmp + * @head: the list to sort + * @cmp: the elements comparison function + * + * This function implements "merge sort", which has O(nlog(n)) + * complexity. + * + * The comparison function @cmp must return a negative value if @a + * should sort before @b, and a positive value if @a should sort after + * @b. If @a and @b are equivalent, and their original relative + * ordering is to be preserved, @cmp must return 0. + */ +void list_sort(void *priv, struct list_head *head, + int (*cmp)(void *priv, struct list_head *a, + struct list_head *b)) +{ + struct list_head *part[MAX_LIST_LENGTH_BITS+1]; /* sorted partial lists + -- last slot is a sentinel */ + int lev; /* index into part[] */ + int max_lev = 0; + struct list_head *list; + + if (list_empty(head)) + return; + + memset(part, 0, sizeof(part)); + + head->prev->next = NULL; + list = head->next; + + while (list) { + struct list_head *cur = list; + list = list->next; + cur->next = NULL; + + for (lev = 0; part[lev]; lev++) { + cur = merge(priv, cmp, part[lev], cur); + part[lev] = NULL; + } + if (lev > max_lev) { + if (unlikely(lev >= ARRAY_SIZE(part)-1)) { + printk_once(KERN_DEBUG "list passed to" + " list_sort() too long for" + " efficiency\n"); + lev--; + } + max_lev = lev; + } + part[lev] = cur; + } + + for (lev = 0; lev < max_lev; lev++) + if (part[lev]) + list = merge(priv, cmp, part[lev], list); + + merge_and_restore_back_links(priv, cmp, head, part[max_lev], list); +} +EXPORT_SYMBOL(list_sort); + +#ifdef CONFIG_TEST_LIST_SORT + +#include <linux/random.h> + +/* + * The pattern of set bits in the list length determines which cases + * are hit in list_sort(). + */ +#define TEST_LIST_LEN (512+128+2) /* not including head */ + +#define TEST_POISON1 0xDEADBEEF +#define TEST_POISON2 0xA324354C + +struct debug_el { + unsigned int poison1; + struct list_head list; + unsigned int poison2; + int value; + unsigned serial; +}; + +/* Array, containing pointers to all elements in the test list */ +static struct debug_el **elts __initdata; + +static int __init check(struct debug_el *ela, struct debug_el *elb) +{ + if (ela->serial >= TEST_LIST_LEN) { + printk(KERN_ERR "list_sort_test: error: incorrect serial %d\n", + ela->serial); + return -EINVAL; + } + if (elb->serial >= TEST_LIST_LEN) { + printk(KERN_ERR "list_sort_test: error: incorrect serial %d\n", + elb->serial); + return -EINVAL; + } + if (elts[ela->serial] != ela || elts[elb->serial] != elb) { + printk(KERN_ERR "list_sort_test: error: phantom element\n"); + return -EINVAL; + } + if (ela->poison1 != TEST_POISON1 || ela->poison2 != TEST_POISON2) { + printk(KERN_ERR "list_sort_test: error: bad poison: %#x/%#x\n", + ela->poison1, ela->poison2); + return -EINVAL; + } + if (elb->poison1 != TEST_POISON1 || elb->poison2 != TEST_POISON2) { + printk(KERN_ERR "list_sort_test: error: bad poison: %#x/%#x\n", + elb->poison1, elb->poison2); + return -EINVAL; + } + return 0; +} + +static int __init cmp(void *priv, struct list_head *a, struct list_head *b) +{ + struct debug_el *ela, *elb; + + ela = container_of(a, struct debug_el, list); + elb = container_of(b, struct debug_el, list); + + check(ela, elb); + return ela->value - elb->value; +} + +static int __init list_sort_test(void) +{ + int i, count = 1, err = -EINVAL; + struct debug_el *el; + struct list_head *cur, *tmp; + LIST_HEAD(head); + + printk(KERN_DEBUG "list_sort_test: start testing list_sort()\n"); + + elts = kmalloc(sizeof(void *) * TEST_LIST_LEN, GFP_KERNEL); + if (!elts) { + printk(KERN_ERR "list_sort_test: error: cannot allocate " + "memory\n"); + goto exit; + } + + for (i = 0; i < TEST_LIST_LEN; i++) { + el = kmalloc(sizeof(*el), GFP_KERNEL); + if (!el) { + printk(KERN_ERR "list_sort_test: error: cannot " + "allocate memory\n"); + goto exit; + } + /* force some equivalencies */ + el->value = prandom_u32() % (TEST_LIST_LEN / 3); + el->serial = i; + el->poison1 = TEST_POISON1; + el->poison2 = TEST_POISON2; + elts[i] = el; + list_add_tail(&el->list, &head); + } + + list_sort(NULL, &head, cmp); + + for (cur = head.next; cur->next != &head; cur = cur->next) { + struct debug_el *el1; + int cmp_result; + + if (cur->next->prev != cur) { + printk(KERN_ERR "list_sort_test: error: list is " + "corrupted\n"); + goto exit; + } + + cmp_result = cmp(NULL, cur, cur->next); + if (cmp_result > 0) { + printk(KERN_ERR "list_sort_test: error: list is not " + "sorted\n"); + goto exit; + } + + el = container_of(cur, struct debug_el, list); + el1 = container_of(cur->next, struct debug_el, list); + if (cmp_result == 0 && el->serial >= el1->serial) { + printk(KERN_ERR "list_sort_test: error: order of " + "equivalent elements not preserved\n"); + goto exit; + } + + if (check(el, el1)) { + printk(KERN_ERR "list_sort_test: error: element check " + "failed\n"); + goto exit; + } + count++; + } + + if (count != TEST_LIST_LEN) { + printk(KERN_ERR "list_sort_test: error: bad list length %d", + count); + goto exit; + } + + err = 0; +exit: + kfree(elts); + list_for_each_safe(cur, tmp, &head) { + list_del(cur); + kfree(container_of(cur, struct debug_el, list)); + } + return err; +} +module_init(list_sort_test); +#endif /* CONFIG_TEST_LIST_SORT */ @@ -295,7 +295,10 @@ phys_addr_t __lmb_alloc_base(struct lmb *lmb, phys_size_t size, ulong align, phy if (max_addr == LMB_ALLOC_ANYWHERE) base = lmb_align_down(lmbbase + lmbsize - size, align); else if (lmbbase < max_addr) { - base = min(lmbbase + lmbsize, max_addr); + base = lmbbase + lmbsize; + if (base < lmbbase) + base = -1; + base = min(base, max_addr); base = lmb_align_down(base - size, align); } else continue; diff --git a/lib/rbtree.c b/lib/rbtree.c index b05f1ab..9e52f70 100644 --- a/lib/rbtree.c +++ b/lib/rbtree.c @@ -2,283 +2,412 @@ Red Black Trees (C) 1999 Andrea Arcangeli <andrea@suse.de> (C) 2002 David Woodhouse <dwmw2@infradead.org> + (C) 2012 Michel Lespinasse <walken@google.com> * SPDX-License-Identifier: GPL-2.0+ linux/lib/rbtree.c */ +#define __UBOOT__ +#include <linux/rbtree_augmented.h> +#ifndef __UBOOT__ +#include <linux/export.h> +#else #include <ubi_uboot.h> -#include <linux/rbtree.h> +#endif +/* + * red-black trees properties: http://en.wikipedia.org/wiki/Rbtree + * + * 1) A node is either red or black + * 2) The root is black + * 3) All leaves (NULL) are black + * 4) Both children of every red node are black + * 5) Every simple path from root to leaves contains the same number + * of black nodes. + * + * 4 and 5 give the O(log n) guarantee, since 4 implies you cannot have two + * consecutive red nodes in a path and every red node is therefore followed by + * a black. So if B is the number of black nodes on every simple path (as per + * 5), then the longest possible path due to 4 is 2B. + * + * We shall indicate color with case, where black nodes are uppercase and red + * nodes will be lowercase. Unknown color nodes shall be drawn as red within + * parentheses and have some accompanying text comment. + */ -static void __rb_rotate_left(struct rb_node *node, struct rb_root *root) +static inline void rb_set_black(struct rb_node *rb) { - struct rb_node *right = node->rb_right; - struct rb_node *parent = rb_parent(node); - - if ((node->rb_right = right->rb_left)) - rb_set_parent(right->rb_left, node); - right->rb_left = node; - - rb_set_parent(right, parent); - - if (parent) - { - if (node == parent->rb_left) - parent->rb_left = right; - else - parent->rb_right = right; - } - else - root->rb_node = right; - rb_set_parent(node, right); + rb->__rb_parent_color |= RB_BLACK; } -static void __rb_rotate_right(struct rb_node *node, struct rb_root *root) +static inline struct rb_node *rb_red_parent(struct rb_node *red) { - struct rb_node *left = node->rb_left; - struct rb_node *parent = rb_parent(node); - - if ((node->rb_left = left->rb_right)) - rb_set_parent(left->rb_right, node); - left->rb_right = node; - - rb_set_parent(left, parent); + return (struct rb_node *)red->__rb_parent_color; +} - if (parent) - { - if (node == parent->rb_right) - parent->rb_right = left; - else - parent->rb_left = left; - } - else - root->rb_node = left; - rb_set_parent(node, left); +/* + * Helper function for rotations: + * - old's parent and color get assigned to new + * - old gets assigned new as a parent and 'color' as a color. + */ +static inline void +__rb_rotate_set_parents(struct rb_node *old, struct rb_node *new, + struct rb_root *root, int color) +{ + struct rb_node *parent = rb_parent(old); + new->__rb_parent_color = old->__rb_parent_color; + rb_set_parent_color(old, new, color); + __rb_change_child(old, new, parent, root); } -void rb_insert_color(struct rb_node *node, struct rb_root *root) +static __always_inline void +__rb_insert(struct rb_node *node, struct rb_root *root, + void (*augment_rotate)(struct rb_node *old, struct rb_node *new)) { - struct rb_node *parent, *gparent; - - while ((parent = rb_parent(node)) && rb_is_red(parent)) - { - gparent = rb_parent(parent); - - if (parent == gparent->rb_left) - { - { - register struct rb_node *uncle = gparent->rb_right; - if (uncle && rb_is_red(uncle)) - { - rb_set_black(uncle); - rb_set_black(parent); - rb_set_red(gparent); - node = gparent; - continue; - } + struct rb_node *parent = rb_red_parent(node), *gparent, *tmp; + + while (true) { + /* + * Loop invariant: node is red + * + * If there is a black parent, we are done. + * Otherwise, take some corrective action as we don't + * want a red root or two consecutive red nodes. + */ + if (!parent) { + rb_set_parent_color(node, NULL, RB_BLACK); + break; + } else if (rb_is_black(parent)) + break; + + gparent = rb_red_parent(parent); + + tmp = gparent->rb_right; + if (parent != tmp) { /* parent == gparent->rb_left */ + if (tmp && rb_is_red(tmp)) { + /* + * Case 1 - color flips + * + * G g + * / \ / \ + * p u --> P U + * / / + * n N + * + * However, since g's parent might be red, and + * 4) does not allow this, we need to recurse + * at g. + */ + rb_set_parent_color(tmp, gparent, RB_BLACK); + rb_set_parent_color(parent, gparent, RB_BLACK); + node = gparent; + parent = rb_parent(node); + rb_set_parent_color(node, parent, RB_RED); + continue; } - if (parent->rb_right == node) - { - register struct rb_node *tmp; - __rb_rotate_left(parent, root); - tmp = parent; + tmp = parent->rb_right; + if (node == tmp) { + /* + * Case 2 - left rotate at parent + * + * G G + * / \ / \ + * p U --> n U + * \ / + * n p + * + * This still leaves us in violation of 4), the + * continuation into Case 3 will fix that. + */ + parent->rb_right = tmp = node->rb_left; + node->rb_left = parent; + if (tmp) + rb_set_parent_color(tmp, parent, + RB_BLACK); + rb_set_parent_color(parent, node, RB_RED); + augment_rotate(parent, node); parent = node; - node = tmp; + tmp = node->rb_right; } - rb_set_black(parent); - rb_set_red(gparent); - __rb_rotate_right(gparent, root); + /* + * Case 3 - right rotate at gparent + * + * G P + * / \ / \ + * p U --> n g + * / \ + * n U + */ + gparent->rb_left = tmp; /* == parent->rb_right */ + parent->rb_right = gparent; + if (tmp) + rb_set_parent_color(tmp, gparent, RB_BLACK); + __rb_rotate_set_parents(gparent, parent, root, RB_RED); + augment_rotate(gparent, parent); + break; } else { - { - register struct rb_node *uncle = gparent->rb_left; - if (uncle && rb_is_red(uncle)) - { - rb_set_black(uncle); - rb_set_black(parent); - rb_set_red(gparent); - node = gparent; - continue; - } + tmp = gparent->rb_left; + if (tmp && rb_is_red(tmp)) { + /* Case 1 - color flips */ + rb_set_parent_color(tmp, gparent, RB_BLACK); + rb_set_parent_color(parent, gparent, RB_BLACK); + node = gparent; + parent = rb_parent(node); + rb_set_parent_color(node, parent, RB_RED); + continue; } - if (parent->rb_left == node) - { - register struct rb_node *tmp; - __rb_rotate_right(parent, root); - tmp = parent; + tmp = parent->rb_left; + if (node == tmp) { + /* Case 2 - right rotate at parent */ + parent->rb_left = tmp = node->rb_right; + node->rb_right = parent; + if (tmp) + rb_set_parent_color(tmp, parent, + RB_BLACK); + rb_set_parent_color(parent, node, RB_RED); + augment_rotate(parent, node); parent = node; - node = tmp; + tmp = node->rb_left; } - rb_set_black(parent); - rb_set_red(gparent); - __rb_rotate_left(gparent, root); + /* Case 3 - left rotate at gparent */ + gparent->rb_right = tmp; /* == parent->rb_left */ + parent->rb_left = gparent; + if (tmp) + rb_set_parent_color(tmp, gparent, RB_BLACK); + __rb_rotate_set_parents(gparent, parent, root, RB_RED); + augment_rotate(gparent, parent); + break; } } - - rb_set_black(root->rb_node); } -static void __rb_erase_color(struct rb_node *node, struct rb_node *parent, - struct rb_root *root) +/* + * Inline version for rb_erase() use - we want to be able to inline + * and eliminate the dummy_rotate callback there + */ +static __always_inline void +____rb_erase_color(struct rb_node *parent, struct rb_root *root, + void (*augment_rotate)(struct rb_node *old, struct rb_node *new)) { - struct rb_node *other; - - while ((!node || rb_is_black(node)) && node != root->rb_node) - { - if (parent->rb_left == node) - { - other = parent->rb_right; - if (rb_is_red(other)) - { - rb_set_black(other); - rb_set_red(parent); - __rb_rotate_left(parent, root); - other = parent->rb_right; - } - if ((!other->rb_left || rb_is_black(other->rb_left)) && - (!other->rb_right || rb_is_black(other->rb_right))) - { - rb_set_red(other); - node = parent; - parent = rb_parent(node); + struct rb_node *node = NULL, *sibling, *tmp1, *tmp2; + + while (true) { + /* + * Loop invariants: + * - node is black (or NULL on first iteration) + * - node is not the root (parent is not NULL) + * - All leaf paths going through parent and node have a + * black node count that is 1 lower than other leaf paths. + */ + sibling = parent->rb_right; + if (node != sibling) { /* node == parent->rb_left */ + if (rb_is_red(sibling)) { + /* + * Case 1 - left rotate at parent + * + * P S + * / \ / \ + * N s --> p Sr + * / \ / \ + * Sl Sr N Sl + */ + parent->rb_right = tmp1 = sibling->rb_left; + sibling->rb_left = parent; + rb_set_parent_color(tmp1, parent, RB_BLACK); + __rb_rotate_set_parents(parent, sibling, root, + RB_RED); + augment_rotate(parent, sibling); + sibling = tmp1; } - else - { - if (!other->rb_right || rb_is_black(other->rb_right)) - { - struct rb_node *o_left; - if ((o_left = other->rb_left)) - rb_set_black(o_left); - rb_set_red(other); - __rb_rotate_right(other, root); - other = parent->rb_right; + tmp1 = sibling->rb_right; + if (!tmp1 || rb_is_black(tmp1)) { + tmp2 = sibling->rb_left; + if (!tmp2 || rb_is_black(tmp2)) { + /* + * Case 2 - sibling color flip + * (p could be either color here) + * + * (p) (p) + * / \ / \ + * N S --> N s + * / \ / \ + * Sl Sr Sl Sr + * + * This leaves us violating 5) which + * can be fixed by flipping p to black + * if it was red, or by recursing at p. + * p is red when coming from Case 1. + */ + rb_set_parent_color(sibling, parent, + RB_RED); + if (rb_is_red(parent)) + rb_set_black(parent); + else { + node = parent; + parent = rb_parent(node); + if (parent) + continue; + } + break; } - rb_set_color(other, rb_color(parent)); - rb_set_black(parent); - if (other->rb_right) - rb_set_black(other->rb_right); - __rb_rotate_left(parent, root); - node = root->rb_node; - break; + /* + * Case 3 - right rotate at sibling + * (p could be either color here) + * + * (p) (p) + * / \ / \ + * N S --> N Sl + * / \ \ + * sl Sr s + * \ + * Sr + */ + sibling->rb_left = tmp1 = tmp2->rb_right; + tmp2->rb_right = sibling; + parent->rb_right = tmp2; + if (tmp1) + rb_set_parent_color(tmp1, sibling, + RB_BLACK); + augment_rotate(sibling, tmp2); + tmp1 = sibling; + sibling = tmp2; } - } - else - { - other = parent->rb_left; - if (rb_is_red(other)) - { - rb_set_black(other); - rb_set_red(parent); - __rb_rotate_right(parent, root); - other = parent->rb_left; - } - if ((!other->rb_left || rb_is_black(other->rb_left)) && - (!other->rb_right || rb_is_black(other->rb_right))) - { - rb_set_red(other); - node = parent; - parent = rb_parent(node); + /* + * Case 4 - left rotate at parent + color flips + * (p and sl could be either color here. + * After rotation, p becomes black, s acquires + * p's color, and sl keeps its color) + * + * (p) (s) + * / \ / \ + * N S --> P Sr + * / \ / \ + * (sl) sr N (sl) + */ + parent->rb_right = tmp2 = sibling->rb_left; + sibling->rb_left = parent; + rb_set_parent_color(tmp1, sibling, RB_BLACK); + if (tmp2) + rb_set_parent(tmp2, parent); + __rb_rotate_set_parents(parent, sibling, root, + RB_BLACK); + augment_rotate(parent, sibling); + break; + } else { + sibling = parent->rb_left; + if (rb_is_red(sibling)) { + /* Case 1 - right rotate at parent */ + parent->rb_left = tmp1 = sibling->rb_right; + sibling->rb_right = parent; + rb_set_parent_color(tmp1, parent, RB_BLACK); + __rb_rotate_set_parents(parent, sibling, root, + RB_RED); + augment_rotate(parent, sibling); + sibling = tmp1; } - else - { - if (!other->rb_left || rb_is_black(other->rb_left)) - { - register struct rb_node *o_right; - if ((o_right = other->rb_right)) - rb_set_black(o_right); - rb_set_red(other); - __rb_rotate_left(other, root); - other = parent->rb_left; + tmp1 = sibling->rb_left; + if (!tmp1 || rb_is_black(tmp1)) { + tmp2 = sibling->rb_right; + if (!tmp2 || rb_is_black(tmp2)) { + /* Case 2 - sibling color flip */ + rb_set_parent_color(sibling, parent, + RB_RED); + if (rb_is_red(parent)) + rb_set_black(parent); + else { + node = parent; + parent = rb_parent(node); + if (parent) + continue; + } + break; } - rb_set_color(other, rb_color(parent)); - rb_set_black(parent); - if (other->rb_left) - rb_set_black(other->rb_left); - __rb_rotate_right(parent, root); - node = root->rb_node; - break; + /* Case 3 - right rotate at sibling */ + sibling->rb_right = tmp1 = tmp2->rb_left; + tmp2->rb_left = sibling; + parent->rb_left = tmp2; + if (tmp1) + rb_set_parent_color(tmp1, sibling, + RB_BLACK); + augment_rotate(sibling, tmp2); + tmp1 = sibling; + sibling = tmp2; } + /* Case 4 - left rotate at parent + color flips */ + parent->rb_left = tmp2 = sibling->rb_right; + sibling->rb_right = parent; + rb_set_parent_color(tmp1, sibling, RB_BLACK); + if (tmp2) + rb_set_parent(tmp2, parent); + __rb_rotate_set_parents(parent, sibling, root, + RB_BLACK); + augment_rotate(parent, sibling); + break; } } - if (node) - rb_set_black(node); } +/* Non-inline version for rb_erase_augmented() use */ +void __rb_erase_color(struct rb_node *parent, struct rb_root *root, + void (*augment_rotate)(struct rb_node *old, struct rb_node *new)) +{ + ____rb_erase_color(parent, root, augment_rotate); +} +EXPORT_SYMBOL(__rb_erase_color); + +/* + * Non-augmented rbtree manipulation functions. + * + * We use dummy augmented callbacks here, and have the compiler optimize them + * out of the rb_insert_color() and rb_erase() function definitions. + */ + +static inline void dummy_propagate(struct rb_node *node, struct rb_node *stop) {} +static inline void dummy_copy(struct rb_node *old, struct rb_node *new) {} +static inline void dummy_rotate(struct rb_node *old, struct rb_node *new) {} + +static const struct rb_augment_callbacks dummy_callbacks = { + dummy_propagate, dummy_copy, dummy_rotate +}; + +void rb_insert_color(struct rb_node *node, struct rb_root *root) +{ + __rb_insert(node, root, dummy_rotate); +} +EXPORT_SYMBOL(rb_insert_color); + void rb_erase(struct rb_node *node, struct rb_root *root) { - struct rb_node *child, *parent; - int color; - - if (!node->rb_left) - child = node->rb_right; - else if (!node->rb_right) - child = node->rb_left; - else - { - struct rb_node *old = node, *left; - - node = node->rb_right; - while ((left = node->rb_left) != NULL) - node = left; - child = node->rb_right; - parent = rb_parent(node); - color = rb_color(node); - - if (child) - rb_set_parent(child, parent); - if (parent == old) { - parent->rb_right = child; - parent = node; - } else - parent->rb_left = child; - - node->rb_parent_color = old->rb_parent_color; - node->rb_right = old->rb_right; - node->rb_left = old->rb_left; - - if (rb_parent(old)) - { - if (rb_parent(old)->rb_left == old) - rb_parent(old)->rb_left = node; - else - rb_parent(old)->rb_right = node; - } else - root->rb_node = node; - - rb_set_parent(old->rb_left, node); - if (old->rb_right) - rb_set_parent(old->rb_right, node); - goto color; - } + struct rb_node *rebalance; + rebalance = __rb_erase_augmented(node, root, &dummy_callbacks); + if (rebalance) + ____rb_erase_color(rebalance, root, dummy_rotate); +} +EXPORT_SYMBOL(rb_erase); - parent = rb_parent(node); - color = rb_color(node); - - if (child) - rb_set_parent(child, parent); - if (parent) - { - if (parent->rb_left == node) - parent->rb_left = child; - else - parent->rb_right = child; - } - else - root->rb_node = child; +/* + * Augmented rbtree manipulation functions. + * + * This instantiates the same __always_inline functions as in the non-augmented + * case, but this time with user-defined callbacks. + */ - color: - if (color == RB_BLACK) - __rb_erase_color(child, parent, root); +void __rb_insert_augmented(struct rb_node *node, struct rb_root *root, + void (*augment_rotate)(struct rb_node *old, struct rb_node *new)) +{ + __rb_insert(node, root, augment_rotate); } +EXPORT_SYMBOL(__rb_insert_augmented); /* * This function returns the first node (in sort order) of the tree. */ -struct rb_node *rb_first(struct rb_root *root) +struct rb_node *rb_first(const struct rb_root *root) { struct rb_node *n; @@ -289,8 +418,9 @@ struct rb_node *rb_first(struct rb_root *root) n = n->rb_left; return n; } +EXPORT_SYMBOL(rb_first); -struct rb_node *rb_last(struct rb_root *root) +struct rb_node *rb_last(const struct rb_root *root) { struct rb_node *n; @@ -301,58 +431,68 @@ struct rb_node *rb_last(struct rb_root *root) n = n->rb_right; return n; } +EXPORT_SYMBOL(rb_last); -struct rb_node *rb_next(struct rb_node *node) +struct rb_node *rb_next(const struct rb_node *node) { struct rb_node *parent; - if (rb_parent(node) == node) + if (RB_EMPTY_NODE(node)) return NULL; - /* If we have a right-hand child, go down and then left as far - as we can. */ + /* + * If we have a right-hand child, go down and then left as far + * as we can. + */ if (node->rb_right) { - node = node->rb_right; + node = node->rb_right; while (node->rb_left) node=node->rb_left; - return node; + return (struct rb_node *)node; } - /* No right-hand children. Everything down and left is - smaller than us, so any 'next' node must be in the general - direction of our parent. Go up the tree; any time the - ancestor is a right-hand child of its parent, keep going - up. First time it's a left-hand child of its parent, said - parent is our 'next' node. */ + /* + * No right-hand children. Everything down and left is smaller than us, + * so any 'next' node must be in the general direction of our parent. + * Go up the tree; any time the ancestor is a right-hand child of its + * parent, keep going up. First time it's a left-hand child of its + * parent, said parent is our 'next' node. + */ while ((parent = rb_parent(node)) && node == parent->rb_right) node = parent; return parent; } +EXPORT_SYMBOL(rb_next); -struct rb_node *rb_prev(struct rb_node *node) +struct rb_node *rb_prev(const struct rb_node *node) { struct rb_node *parent; - if (rb_parent(node) == node) + if (RB_EMPTY_NODE(node)) return NULL; - /* If we have a left-hand child, go down and then right as far - as we can. */ + /* + * If we have a left-hand child, go down and then right as far + * as we can. + */ if (node->rb_left) { - node = node->rb_left; + node = node->rb_left; while (node->rb_right) node=node->rb_right; - return node; + return (struct rb_node *)node; } - /* No left-hand children. Go up till we find an ancestor which - is a right-hand child of its parent */ + /* + * No left-hand children. Go up till we find an ancestor which + * is a right-hand child of its parent. + */ while ((parent = rb_parent(node)) && node == parent->rb_left) node = parent; return parent; } +EXPORT_SYMBOL(rb_prev); void rb_replace_node(struct rb_node *victim, struct rb_node *new, struct rb_root *root) @@ -360,14 +500,7 @@ void rb_replace_node(struct rb_node *victim, struct rb_node *new, struct rb_node *parent = rb_parent(victim); /* Set the surrounding nodes to point to the replacement */ - if (parent) { - if (victim == parent->rb_left) - parent->rb_left = new; - else - parent->rb_right = new; - } else { - root->rb_node = new; - } + __rb_change_child(victim, new, parent, root); if (victim->rb_left) rb_set_parent(victim->rb_left, new); if (victim->rb_right) @@ -376,3 +509,44 @@ void rb_replace_node(struct rb_node *victim, struct rb_node *new, /* Copy the pointers/colour from the victim to the replacement */ *new = *victim; } +EXPORT_SYMBOL(rb_replace_node); + +static struct rb_node *rb_left_deepest_node(const struct rb_node *node) +{ + for (;;) { + if (node->rb_left) + node = node->rb_left; + else if (node->rb_right) + node = node->rb_right; + else + return (struct rb_node *)node; + } +} + +struct rb_node *rb_next_postorder(const struct rb_node *node) +{ + const struct rb_node *parent; + if (!node) + return NULL; + parent = rb_parent(node); + + /* If we're sitting on node, we've already seen our children */ + if (parent && node == parent->rb_left && parent->rb_right) { + /* If we are the parent's left node, go to the parent's right + * node then all the way down to the left */ + return rb_left_deepest_node(parent->rb_right); + } else + /* Otherwise we are the parent's right node, and the parent + * should be next */ + return (struct rb_node *)parent; +} +EXPORT_SYMBOL(rb_next_postorder); + +struct rb_node *rb_first_postorder(const struct rb_root *root) +{ + if (!root->rb_node) + return NULL; + + return rb_left_deepest_node(root->rb_node); +} +EXPORT_SYMBOL(rb_first_postorder); diff --git a/lib/rsa/rsa-sign.c b/lib/rsa/rsa-sign.c index 83f5e87..5d9716f 100644 --- a/lib/rsa/rsa-sign.c +++ b/lib/rsa/rsa-sign.c @@ -76,6 +76,7 @@ static int rsa_get_pub_key(const char *keydir, const char *name, RSA **rsap) rsa = EVP_PKEY_get1_RSA(key); if (!rsa) { rsa_err("Couldn't convert to a RSA style key"); + ret = -EINVAL; goto err_rsa; } fclose(f); @@ -261,10 +262,57 @@ err_priv: } /* + * rsa_get_exponent(): - Get the public exponent from an RSA key + */ +static int rsa_get_exponent(RSA *key, uint64_t *e) +{ + int ret; + BIGNUM *bn_te; + uint64_t te; + + ret = -EINVAL; + bn_te = NULL; + + if (!e) + goto cleanup; + + if (BN_num_bits(key->e) > 64) + goto cleanup; + + *e = BN_get_word(key->e); + + if (BN_num_bits(key->e) < 33) { + ret = 0; + goto cleanup; + } + + bn_te = BN_dup(key->e); + if (!bn_te) + goto cleanup; + + if (!BN_rshift(bn_te, bn_te, 32)) + goto cleanup; + + if (!BN_mask_bits(bn_te, 32)) + goto cleanup; + + te = BN_get_word(bn_te); + te <<= 32; + *e |= te; + ret = 0; + +cleanup: + if (bn_te) + BN_free(bn_te); + + return ret; +} + +/* * rsa_get_params(): - Get the important parameters of an RSA public key */ -int rsa_get_params(RSA *key, uint32_t *n0_invp, BIGNUM **modulusp, - BIGNUM **r_squaredp) +int rsa_get_params(RSA *key, uint64_t *exponent, uint32_t *n0_invp, + BIGNUM **modulusp, BIGNUM **r_squaredp) { BIGNUM *big1, *big2, *big32, *big2_32; BIGNUM *n, *r, *r_squared, *tmp; @@ -286,6 +334,9 @@ int rsa_get_params(RSA *key, uint32_t *n0_invp, BIGNUM **modulusp, return -ENOMEM; } + if (0 != rsa_get_exponent(key, exponent)) + ret = -1; + if (!BN_copy(n, key->n) || !BN_set_word(big1, 1L) || !BN_set_word(big2, 2L) || !BN_set_word(big32, 32L)) ret = -1; @@ -386,6 +437,7 @@ static int fdt_add_bignum(void *blob, int noffset, const char *prop_name, int rsa_add_verify_data(struct image_sign_info *info, void *keydest) { BIGNUM *modulus, *r_squared; + uint64_t exponent; uint32_t n0_inv; int parent, node; char name[100]; @@ -397,7 +449,7 @@ int rsa_add_verify_data(struct image_sign_info *info, void *keydest) ret = rsa_get_pub_key(info->keydir, info->keyname, &rsa); if (ret) return ret; - ret = rsa_get_params(rsa, &n0_inv, &modulus, &r_squared); + ret = rsa_get_params(rsa, &exponent, &n0_inv, &modulus, &r_squared); if (ret) return ret; bits = BN_num_bits(modulus); @@ -442,6 +494,9 @@ int rsa_add_verify_data(struct image_sign_info *info, void *keydest) if (!ret) ret = fdt_setprop_u32(keydest, node, "rsa,n0-inverse", n0_inv); if (!ret) { + ret = fdt_setprop_u64(keydest, node, "rsa,exponent", exponent); + } + if (!ret) { ret = fdt_add_bignum(keydest, node, "rsa,modulus", modulus, bits); } diff --git a/lib/rsa/rsa-verify.c b/lib/rsa/rsa-verify.c index bcb9063..4ef19b6 100644 --- a/lib/rsa/rsa-verify.c +++ b/lib/rsa/rsa-verify.c @@ -26,6 +26,9 @@ #define get_unaligned_be32(a) fdt32_to_cpu(*(uint32_t *)a) #define put_unaligned_be32(a, b) (*(uint32_t *)(b) = cpu_to_fdt32(a)) +/* Default public exponent for backward compatibility */ +#define RSA_DEFAULT_PUBEXP 65537 + /** * subtract_modulus() - subtract modulus from the given value * @@ -54,9 +57,9 @@ static void subtract_modulus(const struct rsa_public_key *key, uint32_t num[]) static int greater_equal_modulus(const struct rsa_public_key *key, uint32_t num[]) { - uint32_t i; + int i; - for (i = key->len - 1; i >= 0; i--) { + for (i = (int)key->len - 1; i >= 0; i--) { if (num[i] < key->modulus[i]) return 0; if (num[i] > key->modulus[i]) @@ -123,6 +126,48 @@ static void montgomery_mul(const struct rsa_public_key *key, } /** + * num_pub_exponent_bits() - Number of bits in the public exponent + * + * @key: RSA key + * @num_bits: Storage for the number of public exponent bits + */ +static int num_public_exponent_bits(const struct rsa_public_key *key, + int *num_bits) +{ + uint64_t exponent; + int exponent_bits; + const uint max_bits = (sizeof(exponent) * 8); + + exponent = key->exponent; + exponent_bits = 0; + + if (!exponent) { + *num_bits = exponent_bits; + return 0; + } + + for (exponent_bits = 1; exponent_bits < max_bits + 1; ++exponent_bits) + if (!(exponent >>= 1)) { + *num_bits = exponent_bits; + return 0; + } + + return -EINVAL; +} + +/** + * is_public_exponent_bit_set() - Check if a bit in the public exponent is set + * + * @key: RSA key + * @pos: The bit position to check + */ +static int is_public_exponent_bit_set(const struct rsa_public_key *key, + int pos) +{ + return key->exponent & (1ULL << pos); +} + +/** * pow_mod() - in-place public exponentiation * * @key: RSA key @@ -132,6 +177,7 @@ static int pow_mod(const struct rsa_public_key *key, uint32_t *inout) { uint32_t *result, *ptr; uint i; + int j, k; /* Sanity check for stack size - key->len is in 32-bit words */ if (key->len > RSA_MAX_KEY_BITS / 32) { @@ -141,18 +187,48 @@ static int pow_mod(const struct rsa_public_key *key, uint32_t *inout) } uint32_t val[key->len], acc[key->len], tmp[key->len]; + uint32_t a_scaled[key->len]; result = tmp; /* Re-use location. */ /* Convert from big endian byte array to little endian word array. */ for (i = 0, ptr = inout + key->len - 1; i < key->len; i++, ptr--) val[i] = get_unaligned_be32(ptr); - montgomery_mul(key, acc, val, key->rr); /* axx = a * RR / R mod M */ - for (i = 0; i < 16; i += 2) { - montgomery_mul(key, tmp, acc, acc); /* tmp = acc^2 / R mod M */ - montgomery_mul(key, acc, tmp, tmp); /* acc = tmp^2 / R mod M */ + if (0 != num_public_exponent_bits(key, &k)) + return -EINVAL; + + if (k < 2) { + debug("Public exponent is too short (%d bits, minimum 2)\n", + k); + return -EINVAL; } - montgomery_mul(key, result, acc, val); /* result = XX * a / R mod M */ + + if (!is_public_exponent_bit_set(key, 0)) { + debug("LSB of RSA public exponent must be set.\n"); + return -EINVAL; + } + + /* the bit at e[k-1] is 1 by definition, so start with: C := M */ + montgomery_mul(key, acc, val, key->rr); /* acc = a * RR / R mod n */ + /* retain scaled version for intermediate use */ + memcpy(a_scaled, acc, key->len * sizeof(a_scaled[0])); + + for (j = k - 2; j > 0; --j) { + montgomery_mul(key, tmp, acc, acc); /* tmp = acc^2 / R mod n */ + + if (is_public_exponent_bit_set(key, j)) { + /* acc = tmp * val / R mod n */ + montgomery_mul(key, acc, tmp, a_scaled); + } else { + /* e[j] == 0, copy tmp back to acc for next operation */ + memcpy(acc, tmp, key->len * sizeof(acc[0])); + } + } + + /* the bit at e[0] is always 1 */ + montgomery_mul(key, tmp, acc, acc); /* tmp = acc^2 / R mod n */ + montgomery_mul(key, acc, tmp, val); /* acc = tmp * a / R mod M */ + memcpy(result, acc, key->len * sizeof(result[0])); /* Make sure result < mod; result is at most 1x mod too large. */ if (greater_equal_modulus(key, result)) @@ -229,6 +305,8 @@ static int rsa_verify_with_keynode(struct image_sign_info *info, const void *blob = info->fdt_blob; struct rsa_public_key key; const void *modulus, *rr; + const uint64_t *public_exponent; + int length; int ret; if (node < 0) { @@ -241,6 +319,11 @@ static int rsa_verify_with_keynode(struct image_sign_info *info, } key.len = fdtdec_get_int(blob, node, "rsa,num-bits", 0); key.n0inv = fdtdec_get_int(blob, node, "rsa,n0-inverse", 0); + public_exponent = fdt_getprop(blob, node, "rsa,exponent", &length); + if (!public_exponent || length < sizeof(*public_exponent)) + key.exponent = RSA_DEFAULT_PUBEXP; + else + key.exponent = fdt64_to_cpu(*public_exponent); modulus = fdt_getprop(blob, node, "rsa,modulus", NULL); rr = fdt_getprop(blob, node, "rsa,r-squared", NULL); if (!key.len || !modulus || !rr) { diff --git a/net/bootp.c b/net/bootp.c index fdb97cb..d4c86cf 100644 --- a/net/bootp.c +++ b/net/bootp.c @@ -23,12 +23,22 @@ #define BOOTP_VENDOR_MAGIC 0x63825363 /* RFC1048 Magic Cookie */ -#define TIMEOUT 5000UL /* Milliseconds before trying BOOTP again */ +/* + * The timeout for the initial BOOTP/DHCP request used to be described by a + * counter of fixed-length timeout periods. TIMEOUT_COUNT represents + * that counter + * + * Now that the timeout periods are variable (exponential backoff and retry) + * we convert the timeout count to the absolute time it would have take to + * execute that many retries, and keep sending retry packets until that time + * is reached. + */ #ifndef CONFIG_NET_RETRY_COUNT # define TIMEOUT_COUNT 5 /* # of timeouts before giving up */ #else # define TIMEOUT_COUNT (CONFIG_NET_RETRY_COUNT) #endif +#define TIMEOUT_MS ((3 + (TIMEOUT_COUNT * 5)) * 1000) #define PORT_BOOTPS 67 /* BOOTP server UDP port */ #define PORT_BOOTPC 68 /* BOOTP client UDP port */ @@ -37,8 +47,15 @@ #define CONFIG_DHCP_MIN_EXT_LEN 64 #endif -ulong BootpID; +#ifndef CONFIG_BOOTP_ID_CACHE_SIZE +#define CONFIG_BOOTP_ID_CACHE_SIZE 4 +#endif + +ulong bootp_ids[CONFIG_BOOTP_ID_CACHE_SIZE]; +unsigned int bootp_num_ids; int BootpTry; +ulong bootp_start; +ulong bootp_timeout; #if defined(CONFIG_CMD_DHCP) static dhcp_state_t dhcp_state = INIT; @@ -65,6 +82,30 @@ static char *dhcpmsg2str(int type) #endif #endif +static void bootp_add_id(ulong id) +{ + if (bootp_num_ids >= ARRAY_SIZE(bootp_ids)) { + size_t size = sizeof(bootp_ids) - sizeof(id); + + memmove(bootp_ids, &bootp_ids[1], size); + bootp_ids[bootp_num_ids - 1] = id; + } else { + bootp_ids[bootp_num_ids] = id; + bootp_num_ids++; + } +} + +static bool bootp_match_id(ulong id) +{ + unsigned int i; + + for (i = 0; i < bootp_num_ids; i++) + if (bootp_ids[i] == id) + return true; + + return false; +} + static int BootpCheckPkt(uchar *pkt, unsigned dest, unsigned src, unsigned len) { struct Bootp_t *bp = (struct Bootp_t *) pkt; @@ -84,7 +125,7 @@ static int BootpCheckPkt(uchar *pkt, unsigned dest, unsigned src, unsigned len) retval = -4; else if (bp->bp_hlen != HWL_ETHER) retval = -5; - else if (NetReadLong((ulong *)&bp->bp_id) != BootpID) + else if (!bootp_match_id(NetReadLong((ulong *)&bp->bp_id))) retval = -6; debug("Filtering pkt = %d\n", retval); @@ -327,16 +368,21 @@ BootpHandler(uchar *pkt, unsigned dest, IPaddr_t sip, unsigned src, static void BootpTimeout(void) { - if (BootpTry >= TIMEOUT_COUNT) { + ulong time_taken = get_timer(bootp_start); + + if (time_taken >= TIMEOUT_MS) { #ifdef CONFIG_BOOTP_MAY_FAIL - puts("\nRetry count exceeded\n"); + puts("\nRetry time exceeded\n"); net_set_state(NETLOOP_FAIL); #else - puts("\nRetry count exceeded; starting again\n"); + puts("\nRetry time exceeded; starting again\n"); NetStartAgain(); #endif } else { - NetSetTimeout(TIMEOUT, BootpTimeout); + bootp_timeout *= 2; + if (bootp_timeout > 2000) + bootp_timeout = 2000; + NetSetTimeout(bootp_timeout, BootpTimeout); BootpRequest(); } } @@ -597,6 +643,14 @@ static int BootpExtended(u8 *e) } #endif +void BootpReset(void) +{ + bootp_num_ids = 0; + BootpTry = 0; + bootp_start = get_timer(0); + bootp_timeout = 250; +} + void BootpRequest(void) { @@ -607,6 +661,7 @@ BootpRequest(void) #ifdef CONFIG_BOOTP_RANDOM_DELAY ulong rand_ms; #endif + ulong BootpID; bootstage_mark_name(BOOTSTAGE_ID_BOOTP_START, "bootp_start"); #if defined(CONFIG_CMD_DHCP) @@ -675,7 +730,8 @@ BootpRequest(void) | ((ulong)NetOurEther[4] << 8) | (ulong)NetOurEther[5]; BootpID += get_timer(0); - BootpID = htonl(BootpID); + BootpID = htonl(BootpID); + bootp_add_id(BootpID); NetCopyLong(&bp->bp_id, &BootpID); /* @@ -685,7 +741,7 @@ BootpRequest(void) iplen = BOOTP_HDR_SIZE - OPT_FIELD_SIZE + extlen; pktlen = eth_hdr_size + IP_UDP_HDR_SIZE + iplen; net_set_udp_header(iphdr, 0xFFFFFFFFL, PORT_BOOTPS, PORT_BOOTPC, iplen); - NetSetTimeout(SELECT_TIMEOUT, BootpTimeout); + NetSetTimeout(bootp_timeout, BootpTimeout); #if defined(CONFIG_CMD_DHCP) dhcp_state = SELECTING; @@ -918,7 +974,7 @@ DhcpHandler(uchar *pkt, unsigned dest, IPaddr_t sip, unsigned src, htonl(BOOTP_VENDOR_MAGIC)) DhcpOptionsProcess((u8 *)&bp->bp_vend[4], bp); - NetSetTimeout(TIMEOUT, BootpTimeout); + NetSetTimeout(5000, BootpTimeout); DhcpSendRequestPkt(bp); #ifdef CONFIG_SYS_BOOTFILE_PREFIX } @@ -936,8 +992,8 @@ DhcpHandler(uchar *pkt, unsigned dest, IPaddr_t sip, unsigned src, /* Store net params from reply */ BootpCopyNetParams(bp); dhcp_state = BOUND; - printf("DHCP client bound to address %pI4\n", - &NetOurIP); + printf("DHCP client bound to address %pI4 (%lu ms)\n", + &NetOurIP, get_timer(bootp_start)); bootstage_mark_name(BOOTSTAGE_ID_BOOTP_STOP, "bootp_stop"); diff --git a/net/bootp.h b/net/bootp.h index ecbcc4d..3b95a0a 100644 --- a/net/bootp.h +++ b/net/bootp.h @@ -65,6 +65,7 @@ extern int BootpTry; /* Send a BOOTP request */ +extern void BootpReset(void); extern void BootpRequest(void); /****************** DHCP Support *********************/ @@ -88,8 +89,6 @@ typedef enum { INIT, #define DHCP_NAK 6 #define DHCP_RELEASE 7 -#define SELECT_TIMEOUT 3000UL /* Milliseconds to wait for offers */ - /**********************************************************************/ #endif /* __BOOTP_H__ */ @@ -385,14 +385,14 @@ restart: #endif #if defined(CONFIG_CMD_DHCP) case DHCP: - BootpTry = 0; + BootpReset(); NetOurIP = 0; DhcpRequest(); /* Basically same as BOOTP */ break; #endif case BOOTP: - BootpTry = 0; + BootpReset(); NetOurIP = 0; BootpRequest(); break; diff --git a/scripts/Lindent b/scripts/Lindent new file mode 100755 index 0000000..9c4b3e2 --- /dev/null +++ b/scripts/Lindent @@ -0,0 +1,18 @@ +#!/bin/sh +PARAM="-npro -kr -i8 -ts8 -sob -l80 -ss -ncs -cp1" +RES=`indent --version` +V1=`echo $RES | cut -d' ' -f3 | cut -d'.' -f1` +V2=`echo $RES | cut -d' ' -f3 | cut -d'.' -f2` +V3=`echo $RES | cut -d' ' -f3 | cut -d'.' -f3` +if [ $V1 -gt 2 ]; then + PARAM="$PARAM -il0" +elif [ $V1 -eq 2 ]; then + if [ $V2 -gt 2 ]; then + PARAM="$PARAM -il0"; + elif [ $V2 -eq 2 ]; then + if [ $V3 -ge 10 ]; then + PARAM="$PARAM -il0" + fi + fi +fi +indent $PARAM "$@" diff --git a/scripts/Makefile.build b/scripts/Makefile.build index baeaabe..6742ddd 100644 --- a/scripts/Makefile.build +++ b/scripts/Makefile.build @@ -66,67 +66,6 @@ ifeq ($(KBUILD_NOPEDANTIC),) endif endif -# -# make W=... settings -# -# W=1 - warnings that may be relevant and does not occur too often -# W=2 - warnings that occur quite often but may still be relevant -# W=3 - the more obscure warnings, can most likely be ignored -# -# $(call cc-option, -W...) handles gcc -W.. options which -# are not supported by all versions of the compiler -ifdef KBUILD_ENABLE_EXTRA_GCC_CHECKS -warning- := $(empty) - -warning-1 := -Wextra -Wunused -Wno-unused-parameter -warning-1 += -Wmissing-declarations -warning-1 += -Wmissing-format-attribute -warning-1 += $(call cc-option, -Wmissing-prototypes) -warning-1 += -Wold-style-definition -warning-1 += $(call cc-option, -Wmissing-include-dirs) -warning-1 += $(call cc-option, -Wunused-but-set-variable) -warning-1 += $(call cc-disable-warning, missing-field-initializers) - -# Clang -warning-1 += $(call cc-disable-warning, initializer-overrides) -warning-1 += $(call cc-disable-warning, unused-value) -warning-1 += $(call cc-disable-warning, format) -warning-1 += $(call cc-disable-warning, unknown-warning-option) -warning-1 += $(call cc-disable-warning, sign-compare) -warning-1 += $(call cc-disable-warning, format-zero-length) -warning-1 += $(call cc-disable-warning, uninitialized) -warning-1 += $(call cc-option, -fcatch-undefined-behavior) - -warning-2 := -Waggregate-return -warning-2 += -Wcast-align -warning-2 += -Wdisabled-optimization -warning-2 += -Wnested-externs -warning-2 += -Wshadow -warning-2 += $(call cc-option, -Wlogical-op) -warning-2 += $(call cc-option, -Wmissing-field-initializers) - -warning-3 := -Wbad-function-cast -warning-3 += -Wcast-qual -warning-3 += -Wconversion -warning-3 += -Wpacked -warning-3 += -Wpadded -warning-3 += -Wpointer-arith -warning-3 += -Wredundant-decls -warning-3 += -Wswitch-default -warning-3 += $(call cc-option, -Wpacked-bitfield-compat) -warning-3 += $(call cc-option, -Wvla) - -warning := $(warning-$(findstring 1, $(KBUILD_ENABLE_EXTRA_GCC_CHECKS))) -warning += $(warning-$(findstring 2, $(KBUILD_ENABLE_EXTRA_GCC_CHECKS))) -warning += $(warning-$(findstring 3, $(KBUILD_ENABLE_EXTRA_GCC_CHECKS))) - -ifeq ("$(strip $(warning))","") - $(error W=$(KBUILD_ENABLE_EXTRA_GCC_CHECKS) is unknown) -endif - -KBUILD_CFLAGS += $(warning) -endif - include scripts/Makefile.lib ifdef host-progs diff --git a/scripts/Makefile.extrawarn b/scripts/Makefile.extrawarn new file mode 100644 index 0000000..6564350 --- /dev/null +++ b/scripts/Makefile.extrawarn @@ -0,0 +1,67 @@ +# ========================================================================== +# +# make W=... settings +# +# W=1 - warnings that may be relevant and does not occur too often +# W=2 - warnings that occur quite often but may still be relevant +# W=3 - the more obscure warnings, can most likely be ignored +# +# $(call cc-option, -W...) handles gcc -W.. options which +# are not supported by all versions of the compiler +# ========================================================================== + +ifeq ("$(origin W)", "command line") + export KBUILD_ENABLE_EXTRA_GCC_CHECKS := $(W) +endif + +ifdef KBUILD_ENABLE_EXTRA_GCC_CHECKS +warning- := $(empty) + +warning-1 := -Wextra -Wunused -Wno-unused-parameter +warning-1 += -Wmissing-declarations +warning-1 += -Wmissing-format-attribute +warning-1 += $(call cc-option, -Wmissing-prototypes) +warning-1 += -Wold-style-definition +warning-1 += $(call cc-option, -Wmissing-include-dirs) +warning-1 += $(call cc-option, -Wunused-but-set-variable) +warning-1 += $(call cc-disable-warning, missing-field-initializers) + +# Clang +warning-1 += $(call cc-disable-warning, initializer-overrides) +warning-1 += $(call cc-disable-warning, unused-value) +warning-1 += $(call cc-disable-warning, format) +warning-1 += $(call cc-disable-warning, unknown-warning-option) +warning-1 += $(call cc-disable-warning, sign-compare) +warning-1 += $(call cc-disable-warning, format-zero-length) +warning-1 += $(call cc-disable-warning, uninitialized) +warning-1 += $(call cc-option, -fcatch-undefined-behavior) + +warning-2 := -Waggregate-return +warning-2 += -Wcast-align +warning-2 += -Wdisabled-optimization +warning-2 += -Wnested-externs +warning-2 += -Wshadow +warning-2 += $(call cc-option, -Wlogical-op) +warning-2 += $(call cc-option, -Wmissing-field-initializers) + +warning-3 := -Wbad-function-cast +warning-3 += -Wcast-qual +warning-3 += -Wconversion +warning-3 += -Wpacked +warning-3 += -Wpadded +warning-3 += -Wpointer-arith +warning-3 += -Wredundant-decls +warning-3 += -Wswitch-default +warning-3 += $(call cc-option, -Wpacked-bitfield-compat) +warning-3 += $(call cc-option, -Wvla) + +warning := $(warning-$(findstring 1, $(KBUILD_ENABLE_EXTRA_GCC_CHECKS))) +warning += $(warning-$(findstring 2, $(KBUILD_ENABLE_EXTRA_GCC_CHECKS))) +warning += $(warning-$(findstring 3, $(KBUILD_ENABLE_EXTRA_GCC_CHECKS))) + +ifeq ("$(strip $(warning))","") + $(error W=$(KBUILD_ENABLE_EXTRA_GCC_CHECKS) is unknown) +endif + +KBUILD_CFLAGS += $(warning) +endif diff --git a/scripts/get_maintainer.pl b/scripts/get_maintainer.pl new file mode 100755 index 0000000..7717d68 --- /dev/null +++ b/scripts/get_maintainer.pl @@ -0,0 +1,2270 @@ +#!/usr/bin/perl -w +# (c) 2007, Joe Perches <joe@perches.com> +# created from checkpatch.pl +# +# Print selected MAINTAINERS information for +# the files modified in a patch or for a file +# +# usage: perl scripts/get_maintainer.pl [OPTIONS] <patch> +# perl scripts/get_maintainer.pl [OPTIONS] -f <file> +# +# Licensed under the terms of the GNU GPL License version 2 + +use strict; + +my $P = $0; +my $V = '0.26'; + +use Getopt::Long qw(:config no_auto_abbrev); +use File::Find; + +my $lk_path = "./"; +my $email = 1; +my $email_usename = 1; +my $email_maintainer = 1; +my $email_list = 1; +my $email_subscriber_list = 0; +my $email_git_penguin_chiefs = 0; +my $email_git = 0; +my $email_git_all_signature_types = 0; +my $email_git_blame = 0; +my $email_git_blame_signatures = 1; +my $email_git_fallback = 1; +my $email_git_min_signatures = 1; +my $email_git_max_maintainers = 5; +my $email_git_min_percent = 5; +my $email_git_since = "1-year-ago"; +my $email_hg_since = "-365"; +my $interactive = 0; +my $email_remove_duplicates = 1; +my $email_use_mailmap = 1; +my $output_multiline = 1; +my $output_separator = ", "; +my $output_roles = 0; +my $output_rolestats = 1; +my $scm = 0; +my $web = 0; +my $subsystem = 0; +my $status = 0; +my $keywords = 1; +my $sections = 0; +my $file_emails = 0; +my $from_filename = 0; +my $pattern_depth = 0; +my $version = 0; +my $help = 0; + +my $vcs_used = 0; + +my $exit = 0; + +my %commit_author_hash; +my %commit_signer_hash; + +my @penguin_chief = (); +push(@penguin_chief, "Tom Rini:trini\@ti.com"); + +my @penguin_chief_names = (); +foreach my $chief (@penguin_chief) { + if ($chief =~ m/^(.*):(.*)/) { + my $chief_name = $1; + my $chief_addr = $2; + push(@penguin_chief_names, $chief_name); + } +} +my $penguin_chiefs = "\(" . join("|", @penguin_chief_names) . "\)"; + +# Signature types of people who are either +# a) responsible for the code in question, or +# b) familiar enough with it to give relevant feedback +my @signature_tags = (); +push(@signature_tags, "Signed-off-by:"); +push(@signature_tags, "Reviewed-by:"); +push(@signature_tags, "Acked-by:"); + +my $signature_pattern = "\(" . join("|", @signature_tags) . "\)"; + +# rfc822 email address - preloaded methods go here. +my $rfc822_lwsp = "(?:(?:\\r\\n)?[ \\t])"; +my $rfc822_char = '[\\000-\\377]'; + +# VCS command support: class-like functions and strings + +my %VCS_cmds; + +my %VCS_cmds_git = ( + "execute_cmd" => \&git_execute_cmd, + "available" => '(which("git") ne "") && (-e ".git")', + "find_signers_cmd" => + "git log --no-color --follow --since=\$email_git_since " . + '--numstat --no-merges ' . + '--format="GitCommit: %H%n' . + 'GitAuthor: %an <%ae>%n' . + 'GitDate: %aD%n' . + 'GitSubject: %s%n' . + '%b%n"' . + " -- \$file", + "find_commit_signers_cmd" => + "git log --no-color " . + '--numstat ' . + '--format="GitCommit: %H%n' . + 'GitAuthor: %an <%ae>%n' . + 'GitDate: %aD%n' . + 'GitSubject: %s%n' . + '%b%n"' . + " -1 \$commit", + "find_commit_author_cmd" => + "git log --no-color " . + '--numstat ' . + '--format="GitCommit: %H%n' . + 'GitAuthor: %an <%ae>%n' . + 'GitDate: %aD%n' . + 'GitSubject: %s%n"' . + " -1 \$commit", + "blame_range_cmd" => "git blame -l -L \$diff_start,+\$diff_length \$file", + "blame_file_cmd" => "git blame -l \$file", + "commit_pattern" => "^GitCommit: ([0-9a-f]{40,40})", + "blame_commit_pattern" => "^([0-9a-f]+) ", + "author_pattern" => "^GitAuthor: (.*)", + "subject_pattern" => "^GitSubject: (.*)", + "stat_pattern" => "^(\\d+)\\t(\\d+)\\t\$file\$", +); + +my %VCS_cmds_hg = ( + "execute_cmd" => \&hg_execute_cmd, + "available" => '(which("hg") ne "") && (-d ".hg")', + "find_signers_cmd" => + "hg log --date=\$email_hg_since " . + "--template='HgCommit: {node}\\n" . + "HgAuthor: {author}\\n" . + "HgSubject: {desc}\\n'" . + " -- \$file", + "find_commit_signers_cmd" => + "hg log " . + "--template='HgSubject: {desc}\\n'" . + " -r \$commit", + "find_commit_author_cmd" => + "hg log " . + "--template='HgCommit: {node}\\n" . + "HgAuthor: {author}\\n" . + "HgSubject: {desc|firstline}\\n'" . + " -r \$commit", + "blame_range_cmd" => "", # not supported + "blame_file_cmd" => "hg blame -n \$file", + "commit_pattern" => "^HgCommit: ([0-9a-f]{40,40})", + "blame_commit_pattern" => "^([ 0-9a-f]+):", + "author_pattern" => "^HgAuthor: (.*)", + "subject_pattern" => "^HgSubject: (.*)", + "stat_pattern" => "^(\\d+)\t(\\d+)\t\$file\$", +); + +my $conf = which_conf(".get_maintainer.conf"); +if (-f $conf) { + my @conf_args; + open(my $conffile, '<', "$conf") + or warn "$P: Can't find a readable .get_maintainer.conf file $!\n"; + + while (<$conffile>) { + my $line = $_; + + $line =~ s/\s*\n?$//g; + $line =~ s/^\s*//g; + $line =~ s/\s+/ /g; + + next if ($line =~ m/^\s*#/); + next if ($line =~ m/^\s*$/); + + my @words = split(" ", $line); + foreach my $word (@words) { + last if ($word =~ m/^#/); + push (@conf_args, $word); + } + } + close($conffile); + unshift(@ARGV, @conf_args) if @conf_args; +} + +if (!GetOptions( + 'email!' => \$email, + 'git!' => \$email_git, + 'git-all-signature-types!' => \$email_git_all_signature_types, + 'git-blame!' => \$email_git_blame, + 'git-blame-signatures!' => \$email_git_blame_signatures, + 'git-fallback!' => \$email_git_fallback, + 'git-chief-penguins!' => \$email_git_penguin_chiefs, + 'git-min-signatures=i' => \$email_git_min_signatures, + 'git-max-maintainers=i' => \$email_git_max_maintainers, + 'git-min-percent=i' => \$email_git_min_percent, + 'git-since=s' => \$email_git_since, + 'hg-since=s' => \$email_hg_since, + 'i|interactive!' => \$interactive, + 'remove-duplicates!' => \$email_remove_duplicates, + 'mailmap!' => \$email_use_mailmap, + 'm!' => \$email_maintainer, + 'n!' => \$email_usename, + 'l!' => \$email_list, + 's!' => \$email_subscriber_list, + 'multiline!' => \$output_multiline, + 'roles!' => \$output_roles, + 'rolestats!' => \$output_rolestats, + 'separator=s' => \$output_separator, + 'subsystem!' => \$subsystem, + 'status!' => \$status, + 'scm!' => \$scm, + 'web!' => \$web, + 'pattern-depth=i' => \$pattern_depth, + 'k|keywords!' => \$keywords, + 'sections!' => \$sections, + 'fe|file-emails!' => \$file_emails, + 'f|file' => \$from_filename, + 'v|version' => \$version, + 'h|help|usage' => \$help, + )) { + die "$P: invalid argument - use --help if necessary\n"; +} + +if ($help != 0) { + usage(); + exit 0; +} + +if ($version != 0) { + print("${P} ${V}\n"); + exit 0; +} + +if (-t STDIN && !@ARGV) { + # We're talking to a terminal, but have no command line arguments. + die "$P: missing patchfile or -f file - use --help if necessary\n"; +} + +$output_multiline = 0 if ($output_separator ne ", "); +$output_rolestats = 1 if ($interactive); +$output_roles = 1 if ($output_rolestats); + +if ($sections) { + $email = 0; + $email_list = 0; + $scm = 0; + $status = 0; + $subsystem = 0; + $web = 0; + $keywords = 0; + $interactive = 0; +} else { + my $selections = $email + $scm + $status + $subsystem + $web; + if ($selections == 0) { + die "$P: Missing required option: email, scm, status, subsystem or web\n"; + } +} + +if ($email && + ($email_maintainer + $email_list + $email_subscriber_list + + $email_git + $email_git_penguin_chiefs + $email_git_blame) == 0) { + die "$P: Please select at least 1 email option\n"; +} + +if (!top_of_kernel_tree($lk_path)) { + die "$P: The current directory does not appear to be " + . "a linux kernel source tree.\n"; +} + +## Read MAINTAINERS for type/value pairs + +my @typevalue = (); +my %keyword_hash; + +my @maint_files = (); +push(@maint_files, "${lk_path}MAINTAINERS"); + +sub maint_wanted { + return unless $_ =~ /^MAINTAINERS/; + push(@maint_files, "$File::Find::name"); +} + +File::Find::find(\&maint_wanted, "${lk_path}board"); + +foreach my $maint_file (@maint_files) { + my $maint; + open ($maint, '<', "$maint_file") + or die "$P: Can't open $maint_file: $!\n"; + read_maintainers($maint); + close($maint); +} + +sub read_maintainers { + my ($maint) = @_; + + while (<$maint>) { + my $line = $_; + + if ($line =~ m/^(\C):\s*(.*)/) { + my $type = $1; + my $value = $2; + + ##Filename pattern matching + if ($type eq "F" || $type eq "X") { + $value =~ s@\.@\\\.@g; ##Convert . to \. + $value =~ s/\*/\.\*/g; ##Convert * to .* + $value =~ s/\?/\./g; ##Convert ? to . + ##if pattern is a directory and it lacks a trailing slash, add one + if ((-d $value)) { + $value =~ s@([^/])$@$1/@; + } + } elsif ($type eq "K") { + $keyword_hash{@typevalue} = $value; + } + push(@typevalue, "$type:$value"); + } elsif (!/^(\s)*$/) { + $line =~ s/\n$//g; + push(@typevalue, $line); + } + } +} + + +# +# Read mail address map +# + +my $mailmap; + +read_mailmap(); + +sub read_mailmap { + $mailmap = { + names => {}, + addresses => {} + }; + + return if (!$email_use_mailmap || !(-f "${lk_path}.mailmap")); + + open(my $mailmap_file, '<', "${lk_path}.mailmap") + or warn "$P: Can't open .mailmap: $!\n"; + + while (<$mailmap_file>) { + s/#.*$//; #strip comments + s/^\s+|\s+$//g; #trim + + next if (/^\s*$/); #skip empty lines + #entries have one of the following formats: + # name1 <mail1> + # <mail1> <mail2> + # name1 <mail1> <mail2> + # name1 <mail1> name2 <mail2> + # (see man git-shortlog) + + if (/^([^<]+)<([^>]+)>$/) { + my $real_name = $1; + my $address = $2; + + $real_name =~ s/\s+$//; + ($real_name, $address) = parse_email("$real_name <$address>"); + $mailmap->{names}->{$address} = $real_name; + + } elsif (/^<([^>]+)>\s*<([^>]+)>$/) { + my $real_address = $1; + my $wrong_address = $2; + + $mailmap->{addresses}->{$wrong_address} = $real_address; + + } elsif (/^(.+)<([^>]+)>\s*<([^>]+)>$/) { + my $real_name = $1; + my $real_address = $2; + my $wrong_address = $3; + + $real_name =~ s/\s+$//; + ($real_name, $real_address) = + parse_email("$real_name <$real_address>"); + $mailmap->{names}->{$wrong_address} = $real_name; + $mailmap->{addresses}->{$wrong_address} = $real_address; + + } elsif (/^(.+)<([^>]+)>\s*(.+)\s*<([^>]+)>$/) { + my $real_name = $1; + my $real_address = $2; + my $wrong_name = $3; + my $wrong_address = $4; + + $real_name =~ s/\s+$//; + ($real_name, $real_address) = + parse_email("$real_name <$real_address>"); + + $wrong_name =~ s/\s+$//; + ($wrong_name, $wrong_address) = + parse_email("$wrong_name <$wrong_address>"); + + my $wrong_email = format_email($wrong_name, $wrong_address, 1); + $mailmap->{names}->{$wrong_email} = $real_name; + $mailmap->{addresses}->{$wrong_email} = $real_address; + } + } + close($mailmap_file); +} + +## use the filenames on the command line or find the filenames in the patchfiles + +my @files = (); +my @range = (); +my @keyword_tvi = (); +my @file_emails = (); + +if (!@ARGV) { + push(@ARGV, "&STDIN"); +} + +foreach my $file (@ARGV) { + if ($file ne "&STDIN") { + ##if $file is a directory and it lacks a trailing slash, add one + if ((-d $file)) { + $file =~ s@([^/])$@$1/@; + } elsif (!(-f $file)) { + die "$P: file '${file}' not found\n"; + } + } + if ($from_filename) { + push(@files, $file); + if ($file ne "MAINTAINERS" && -f $file && ($keywords || $file_emails)) { + open(my $f, '<', $file) + or die "$P: Can't open $file: $!\n"; + my $text = do { local($/) ; <$f> }; + close($f); + if ($keywords) { + foreach my $line (keys %keyword_hash) { + if ($text =~ m/$keyword_hash{$line}/x) { + push(@keyword_tvi, $line); + } + } + } + if ($file_emails) { + my @poss_addr = $text =~ m$[A-Za-zÀ-ÿ\"\' \,\.\+-]*\s*[\,]*\s*[\(\<\{]{0,1}[A-Za-z0-9_\.\+-]+\@[A-Za-z0-9\.-]+\.[A-Za-z0-9]+[\)\>\}]{0,1}$g; + push(@file_emails, clean_file_emails(@poss_addr)); + } + } + } else { + my $file_cnt = @files; + my $lastfile; + + open(my $patch, "< $file") + or die "$P: Can't open $file: $!\n"; + + # We can check arbitrary information before the patch + # like the commit message, mail headers, etc... + # This allows us to match arbitrary keywords against any part + # of a git format-patch generated file (subject tags, etc...) + + my $patch_prefix = ""; #Parsing the intro + + while (<$patch>) { + my $patch_line = $_; + if (m/^\+\+\+\s+(\S+)/ or m/^---\s+(\S+)/) { + my $filename = $1; + $filename =~ s@^[^/]*/@@; + $filename =~ s@\n@@; + $lastfile = $filename; + push(@files, $filename); + $patch_prefix = "^[+-].*"; #Now parsing the actual patch + } elsif (m/^\@\@ -(\d+),(\d+)/) { + if ($email_git_blame) { + push(@range, "$lastfile:$1:$2"); + } + } elsif ($keywords) { + foreach my $line (keys %keyword_hash) { + if ($patch_line =~ m/${patch_prefix}$keyword_hash{$line}/x) { + push(@keyword_tvi, $line); + } + } + } + } + close($patch); + + if ($file_cnt == @files) { + warn "$P: file '${file}' doesn't appear to be a patch. " + . "Add -f to options?\n"; + } + @files = sort_and_uniq(@files); + } +} + +@file_emails = uniq(@file_emails); + +my %email_hash_name; +my %email_hash_address; +my @email_to = (); +my %hash_list_to; +my @list_to = (); +my @scm = (); +my @web = (); +my @subsystem = (); +my @status = (); +my %deduplicate_name_hash = (); +my %deduplicate_address_hash = (); + +my @maintainers = get_maintainers(); + +if (@maintainers) { + @maintainers = merge_email(@maintainers); + output(@maintainers); +} + +if ($scm) { + @scm = uniq(@scm); + output(@scm); +} + +if ($status) { + @status = uniq(@status); + output(@status); +} + +if ($subsystem) { + @subsystem = uniq(@subsystem); + output(@subsystem); +} + +if ($web) { + @web = uniq(@web); + output(@web); +} + +exit($exit); + +sub range_is_maintained { + my ($start, $end) = @_; + + for (my $i = $start; $i < $end; $i++) { + my $line = $typevalue[$i]; + if ($line =~ m/^(\C):\s*(.*)/) { + my $type = $1; + my $value = $2; + if ($type eq 'S') { + if ($value =~ /(maintain|support)/i) { + return 1; + } + } + } + } + return 0; +} + +sub range_has_maintainer { + my ($start, $end) = @_; + + for (my $i = $start; $i < $end; $i++) { + my $line = $typevalue[$i]; + if ($line =~ m/^(\C):\s*(.*)/) { + my $type = $1; + my $value = $2; + if ($type eq 'M') { + return 1; + } + } + } + return 0; +} + +sub get_maintainers { + %email_hash_name = (); + %email_hash_address = (); + %commit_author_hash = (); + %commit_signer_hash = (); + @email_to = (); + %hash_list_to = (); + @list_to = (); + @scm = (); + @web = (); + @subsystem = (); + @status = (); + %deduplicate_name_hash = (); + %deduplicate_address_hash = (); + if ($email_git_all_signature_types) { + $signature_pattern = "(.+?)[Bb][Yy]:"; + } else { + $signature_pattern = "\(" . join("|", @signature_tags) . "\)"; + } + + # Find responsible parties + + my %exact_pattern_match_hash = (); + + foreach my $file (@files) { + + my %hash; + my $tvi = find_first_section(); + while ($tvi < @typevalue) { + my $start = find_starting_index($tvi); + my $end = find_ending_index($tvi); + my $exclude = 0; + my $i; + + #Do not match excluded file patterns + + for ($i = $start; $i < $end; $i++) { + my $line = $typevalue[$i]; + if ($line =~ m/^(\C):\s*(.*)/) { + my $type = $1; + my $value = $2; + if ($type eq 'X') { + if (file_match_pattern($file, $value)) { + $exclude = 1; + last; + } + } + } + } + + if (!$exclude) { + for ($i = $start; $i < $end; $i++) { + my $line = $typevalue[$i]; + if ($line =~ m/^(\C):\s*(.*)/) { + my $type = $1; + my $value = $2; + if ($type eq 'F') { + if (file_match_pattern($file, $value)) { + my $value_pd = ($value =~ tr@/@@); + my $file_pd = ($file =~ tr@/@@); + $value_pd++ if (substr($value,-1,1) ne "/"); + $value_pd = -1 if ($value =~ /^\.\*/); + if ($value_pd >= $file_pd && + range_is_maintained($start, $end) && + range_has_maintainer($start, $end)) { + $exact_pattern_match_hash{$file} = 1; + } + if ($pattern_depth == 0 || + (($file_pd - $value_pd) < $pattern_depth)) { + $hash{$tvi} = $value_pd; + } + } + } elsif ($type eq 'N') { + if ($file =~ m/$value/x) { + $hash{$tvi} = 0; + } + } + } + } + } + $tvi = $end + 1; + } + + foreach my $line (sort {$hash{$b} <=> $hash{$a}} keys %hash) { + add_categories($line); + if ($sections) { + my $i; + my $start = find_starting_index($line); + my $end = find_ending_index($line); + for ($i = $start; $i < $end; $i++) { + my $line = $typevalue[$i]; + if ($line =~ /^[FX]:/) { ##Restore file patterns + $line =~ s/([^\\])\.([^\*])/$1\?$2/g; + $line =~ s/([^\\])\.$/$1\?/g; ##Convert . back to ? + $line =~ s/\\\./\./g; ##Convert \. to . + $line =~ s/\.\*/\*/g; ##Convert .* to * + } + $line =~ s/^([A-Z]):/$1:\t/g; + print("$line\n"); + } + print("\n"); + } + } + } + + if ($keywords) { + @keyword_tvi = sort_and_uniq(@keyword_tvi); + foreach my $line (@keyword_tvi) { + add_categories($line); + } + } + + foreach my $email (@email_to, @list_to) { + $email->[0] = deduplicate_email($email->[0]); + } + + foreach my $file (@files) { + if ($email && + ($email_git || ($email_git_fallback && + !$exact_pattern_match_hash{$file}))) { + vcs_file_signoffs($file); + } + if ($email && $email_git_blame) { + vcs_file_blame($file); + } + } + + if ($email) { + foreach my $chief (@penguin_chief) { + if ($chief =~ m/^(.*):(.*)/) { + my $email_address; + + $email_address = format_email($1, $2, $email_usename); + if ($email_git_penguin_chiefs) { + push(@email_to, [$email_address, 'chief penguin']); + } else { + @email_to = grep($_->[0] !~ /${email_address}/, @email_to); + } + } + } + + foreach my $email (@file_emails) { + my ($name, $address) = parse_email($email); + + my $tmp_email = format_email($name, $address, $email_usename); + push_email_address($tmp_email, ''); + add_role($tmp_email, 'in file'); + } + } + + my @to = (); + if ($email || $email_list) { + if ($email) { + @to = (@to, @email_to); + } + if ($email_list) { + @to = (@to, @list_to); + } + } + + if ($interactive) { + @to = interactive_get_maintainers(\@to); + } + + return @to; +} + +sub file_match_pattern { + my ($file, $pattern) = @_; + if (substr($pattern, -1) eq "/") { + if ($file =~ m@^$pattern@) { + return 1; + } + } else { + if ($file =~ m@^$pattern@) { + my $s1 = ($file =~ tr@/@@); + my $s2 = ($pattern =~ tr@/@@); + if ($s1 == $s2) { + return 1; + } + } + } + return 0; +} + +sub usage { + print <<EOT; +usage: $P [options] patchfile + $P [options] -f file|directory +version: $V + +MAINTAINER field selection options: + --email => print email address(es) if any + --git => include recent git \*-by: signers + --git-all-signature-types => include signers regardless of signature type + or use only ${signature_pattern} signers (default: $email_git_all_signature_types) + --git-fallback => use git when no exact MAINTAINERS pattern (default: $email_git_fallback) + --git-chief-penguins => include ${penguin_chiefs} + --git-min-signatures => number of signatures required (default: $email_git_min_signatures) + --git-max-maintainers => maximum maintainers to add (default: $email_git_max_maintainers) + --git-min-percent => minimum percentage of commits required (default: $email_git_min_percent) + --git-blame => use git blame to find modified commits for patch or file + --git-since => git history to use (default: $email_git_since) + --hg-since => hg history to use (default: $email_hg_since) + --interactive => display a menu (mostly useful if used with the --git option) + --m => include maintainer(s) if any + --n => include name 'Full Name <addr\@domain.tld>' + --l => include list(s) if any + --s => include subscriber only list(s) if any + --remove-duplicates => minimize duplicate email names/addresses + --roles => show roles (status:subsystem, git-signer, list, etc...) + --rolestats => show roles and statistics (commits/total_commits, %) + --file-emails => add email addresses found in -f file (default: 0 (off)) + --scm => print SCM tree(s) if any + --status => print status if any + --subsystem => print subsystem name if any + --web => print website(s) if any + +Output type options: + --separator [, ] => separator for multiple entries on 1 line + using --separator also sets --nomultiline if --separator is not [, ] + --multiline => print 1 entry per line + +Other options: + --pattern-depth => Number of pattern directory traversals (default: 0 (all)) + --keywords => scan patch for keywords (default: $keywords) + --sections => print all of the subsystem sections with pattern matches + --mailmap => use .mailmap file (default: $email_use_mailmap) + --version => show version + --help => show this help information + +Default options: + [--email --nogit --git-fallback --m --n --l --multiline -pattern-depth=0 + --remove-duplicates --rolestats] + +Notes: + Using "-f directory" may give unexpected results: + Used with "--git", git signators for _all_ files in and below + directory are examined as git recurses directories. + Any specified X: (exclude) pattern matches are _not_ ignored. + Used with "--nogit", directory is used as a pattern match, + no individual file within the directory or subdirectory + is matched. + Used with "--git-blame", does not iterate all files in directory + Using "--git-blame" is slow and may add old committers and authors + that are no longer active maintainers to the output. + Using "--roles" or "--rolestats" with git send-email --cc-cmd or any + other automated tools that expect only ["name"] <email address> + may not work because of additional output after <email address>. + Using "--rolestats" and "--git-blame" shows the #/total=% commits, + not the percentage of the entire file authored. # of commits is + not a good measure of amount of code authored. 1 major commit may + contain a thousand lines, 5 trivial commits may modify a single line. + If git is not installed, but mercurial (hg) is installed and an .hg + repository exists, the following options apply to mercurial: + --git, + --git-min-signatures, --git-max-maintainers, --git-min-percent, and + --git-blame + Use --hg-since not --git-since to control date selection + File ".get_maintainer.conf", if it exists in the linux kernel source root + directory, can change whatever get_maintainer defaults are desired. + Entries in this file can be any command line argument. + This file is prepended to any additional command line arguments. + Multiple lines and # comments are allowed. +EOT +} + +sub top_of_kernel_tree { + my ($lk_path) = @_; + + if ($lk_path ne "" && substr($lk_path,length($lk_path)-1,1) ne "/") { + $lk_path .= "/"; + } + if ( (-f "${lk_path}CREDITS") + && (-f "${lk_path}Kbuild") + && (-f "${lk_path}MAINTAINERS") + && (-f "${lk_path}Makefile") + && (-f "${lk_path}README") + && (-d "${lk_path}arch") + && (-d "${lk_path}board") + && (-d "${lk_path}common") + && (-d "${lk_path}doc") + && (-d "${lk_path}drivers") + && (-d "${lk_path}dts") + && (-d "${lk_path}fs") + && (-d "${lk_path}lib") + && (-d "${lk_path}include") + && (-d "${lk_path}net") + && (-d "${lk_path}post") + && (-d "${lk_path}scripts") + && (-d "${lk_path}test") + && (-d "${lk_path}tools")) { + return 1; + } + return 0; +} + +sub parse_email { + my ($formatted_email) = @_; + + my $name = ""; + my $address = ""; + + if ($formatted_email =~ /^([^<]+)<(.+\@.*)>.*$/) { + $name = $1; + $address = $2; + } elsif ($formatted_email =~ /^\s*<(.+\@\S*)>.*$/) { + $address = $1; + } elsif ($formatted_email =~ /^(.+\@\S*).*$/) { + $address = $1; + } + + $name =~ s/^\s+|\s+$//g; + $name =~ s/^\"|\"$//g; + $address =~ s/^\s+|\s+$//g; + + if ($name =~ /[^\w \-]/i) { ##has "must quote" chars + $name =~ s/(?<!\\)"/\\"/g; ##escape quotes + $name = "\"$name\""; + } + + return ($name, $address); +} + +sub format_email { + my ($name, $address, $usename) = @_; + + my $formatted_email; + + $name =~ s/^\s+|\s+$//g; + $name =~ s/^\"|\"$//g; + $address =~ s/^\s+|\s+$//g; + + if ($name =~ /[^\w \-]/i) { ##has "must quote" chars + $name =~ s/(?<!\\)"/\\"/g; ##escape quotes + $name = "\"$name\""; + } + + if ($usename) { + if ("$name" eq "") { + $formatted_email = "$address"; + } else { + $formatted_email = "$name <$address>"; + } + } else { + $formatted_email = $address; + } + + return $formatted_email; +} + +sub find_first_section { + my $index = 0; + + while ($index < @typevalue) { + my $tv = $typevalue[$index]; + if (($tv =~ m/^(\C):\s*(.*)/)) { + last; + } + $index++; + } + + return $index; +} + +sub find_starting_index { + my ($index) = @_; + + while ($index > 0) { + my $tv = $typevalue[$index]; + if (!($tv =~ m/^(\C):\s*(.*)/)) { + last; + } + $index--; + } + + return $index; +} + +sub find_ending_index { + my ($index) = @_; + + while ($index < @typevalue) { + my $tv = $typevalue[$index]; + if (!($tv =~ m/^(\C):\s*(.*)/)) { + last; + } + $index++; + } + + return $index; +} + +sub get_maintainer_role { + my ($index) = @_; + + my $i; + my $start = find_starting_index($index); + my $end = find_ending_index($index); + + my $role = "unknown"; + my $subsystem = $typevalue[$start]; + if (length($subsystem) > 20) { + $subsystem = substr($subsystem, 0, 17); + $subsystem =~ s/\s*$//; + $subsystem = $subsystem . "..."; + } + + for ($i = $start + 1; $i < $end; $i++) { + my $tv = $typevalue[$i]; + if ($tv =~ m/^(\C):\s*(.*)/) { + my $ptype = $1; + my $pvalue = $2; + if ($ptype eq "S") { + $role = $pvalue; + } + } + } + + $role = lc($role); + if ($role eq "supported") { + $role = "supporter"; + } elsif ($role eq "maintained") { + $role = "maintainer"; + } elsif ($role eq "odd fixes") { + $role = "odd fixer"; + } elsif ($role eq "orphan") { + $role = "orphan minder"; + } elsif ($role eq "obsolete") { + $role = "obsolete minder"; + } elsif ($role eq "buried alive in reporters") { + $role = "chief penguin"; + } + + return $role . ":" . $subsystem; +} + +sub get_list_role { + my ($index) = @_; + + my $i; + my $start = find_starting_index($index); + my $end = find_ending_index($index); + + my $subsystem = $typevalue[$start]; + if (length($subsystem) > 20) { + $subsystem = substr($subsystem, 0, 17); + $subsystem =~ s/\s*$//; + $subsystem = $subsystem . "..."; + } + + if ($subsystem eq "THE REST") { + $subsystem = ""; + } + + return $subsystem; +} + +sub add_categories { + my ($index) = @_; + + my $i; + my $start = find_starting_index($index); + my $end = find_ending_index($index); + + push(@subsystem, $typevalue[$start]); + + for ($i = $start + 1; $i < $end; $i++) { + my $tv = $typevalue[$i]; + if ($tv =~ m/^(\C):\s*(.*)/) { + my $ptype = $1; + my $pvalue = $2; + if ($ptype eq "L") { + my $list_address = $pvalue; + my $list_additional = ""; + my $list_role = get_list_role($i); + + if ($list_role ne "") { + $list_role = ":" . $list_role; + } + if ($list_address =~ m/([^\s]+)\s+(.*)$/) { + $list_address = $1; + $list_additional = $2; + } + if ($list_additional =~ m/subscribers-only/) { + if ($email_subscriber_list) { + if (!$hash_list_to{lc($list_address)}) { + $hash_list_to{lc($list_address)} = 1; + push(@list_to, [$list_address, + "subscriber list${list_role}"]); + } + } + } else { + if ($email_list) { + if (!$hash_list_to{lc($list_address)}) { + $hash_list_to{lc($list_address)} = 1; + if ($list_additional =~ m/moderated/) { + push(@list_to, [$list_address, + "moderated list${list_role}"]); + } else { + push(@list_to, [$list_address, + "open list${list_role}"]); + } + } + } + } + } elsif ($ptype eq "M") { + my ($name, $address) = parse_email($pvalue); + if ($name eq "") { + if ($i > 0) { + my $tv = $typevalue[$i - 1]; + if ($tv =~ m/^(\C):\s*(.*)/) { + if ($1 eq "P") { + $name = $2; + $pvalue = format_email($name, $address, $email_usename); + } + } + } + } + if ($email_maintainer) { + my $role = get_maintainer_role($i); + push_email_addresses($pvalue, $role); + } + } elsif ($ptype eq "T") { + push(@scm, $pvalue); + } elsif ($ptype eq "W") { + push(@web, $pvalue); + } elsif ($ptype eq "S") { + push(@status, $pvalue); + } + } + } +} + +sub email_inuse { + my ($name, $address) = @_; + + return 1 if (($name eq "") && ($address eq "")); + return 1 if (($name ne "") && exists($email_hash_name{lc($name)})); + return 1 if (($address ne "") && exists($email_hash_address{lc($address)})); + + return 0; +} + +sub push_email_address { + my ($line, $role) = @_; + + my ($name, $address) = parse_email($line); + + if ($address eq "") { + return 0; + } + + if (!$email_remove_duplicates) { + push(@email_to, [format_email($name, $address, $email_usename), $role]); + } elsif (!email_inuse($name, $address)) { + push(@email_to, [format_email($name, $address, $email_usename), $role]); + $email_hash_name{lc($name)}++ if ($name ne ""); + $email_hash_address{lc($address)}++; + } + + return 1; +} + +sub push_email_addresses { + my ($address, $role) = @_; + + my @address_list = (); + + if (rfc822_valid($address)) { + push_email_address($address, $role); + } elsif (@address_list = rfc822_validlist($address)) { + my $array_count = shift(@address_list); + while (my $entry = shift(@address_list)) { + push_email_address($entry, $role); + } + } else { + if (!push_email_address($address, $role)) { + warn("Invalid MAINTAINERS address: '" . $address . "'\n"); + } + } +} + +sub add_role { + my ($line, $role) = @_; + + my ($name, $address) = parse_email($line); + my $email = format_email($name, $address, $email_usename); + + foreach my $entry (@email_to) { + if ($email_remove_duplicates) { + my ($entry_name, $entry_address) = parse_email($entry->[0]); + if (($name eq $entry_name || $address eq $entry_address) + && ($role eq "" || !($entry->[1] =~ m/$role/)) + ) { + if ($entry->[1] eq "") { + $entry->[1] = "$role"; + } else { + $entry->[1] = "$entry->[1],$role"; + } + } + } else { + if ($email eq $entry->[0] + && ($role eq "" || !($entry->[1] =~ m/$role/)) + ) { + if ($entry->[1] eq "") { + $entry->[1] = "$role"; + } else { + $entry->[1] = "$entry->[1],$role"; + } + } + } + } +} + +sub which { + my ($bin) = @_; + + foreach my $path (split(/:/, $ENV{PATH})) { + if (-e "$path/$bin") { + return "$path/$bin"; + } + } + + return ""; +} + +sub which_conf { + my ($conf) = @_; + + foreach my $path (split(/:/, ".:$ENV{HOME}:.scripts")) { + if (-e "$path/$conf") { + return "$path/$conf"; + } + } + + return ""; +} + +sub mailmap_email { + my ($line) = @_; + + my ($name, $address) = parse_email($line); + my $email = format_email($name, $address, 1); + my $real_name = $name; + my $real_address = $address; + + if (exists $mailmap->{names}->{$email} || + exists $mailmap->{addresses}->{$email}) { + if (exists $mailmap->{names}->{$email}) { + $real_name = $mailmap->{names}->{$email}; + } + if (exists $mailmap->{addresses}->{$email}) { + $real_address = $mailmap->{addresses}->{$email}; + } + } else { + if (exists $mailmap->{names}->{$address}) { + $real_name = $mailmap->{names}->{$address}; + } + if (exists $mailmap->{addresses}->{$address}) { + $real_address = $mailmap->{addresses}->{$address}; + } + } + return format_email($real_name, $real_address, 1); +} + +sub mailmap { + my (@addresses) = @_; + + my @mapped_emails = (); + foreach my $line (@addresses) { + push(@mapped_emails, mailmap_email($line)); + } + merge_by_realname(@mapped_emails) if ($email_use_mailmap); + return @mapped_emails; +} + +sub merge_by_realname { + my %address_map; + my (@emails) = @_; + + foreach my $email (@emails) { + my ($name, $address) = parse_email($email); + if (exists $address_map{$name}) { + $address = $address_map{$name}; + $email = format_email($name, $address, 1); + } else { + $address_map{$name} = $address; + } + } +} + +sub git_execute_cmd { + my ($cmd) = @_; + my @lines = (); + + my $output = `$cmd`; + $output =~ s/^\s*//gm; + @lines = split("\n", $output); + + return @lines; +} + +sub hg_execute_cmd { + my ($cmd) = @_; + my @lines = (); + + my $output = `$cmd`; + @lines = split("\n", $output); + + return @lines; +} + +sub extract_formatted_signatures { + my (@signature_lines) = @_; + + my @type = @signature_lines; + + s/\s*(.*):.*/$1/ for (@type); + + # cut -f2- -d":" + s/\s*.*:\s*(.+)\s*/$1/ for (@signature_lines); + +## Reformat email addresses (with names) to avoid badly written signatures + + foreach my $signer (@signature_lines) { + $signer = deduplicate_email($signer); + } + + return (\@type, \@signature_lines); +} + +sub vcs_find_signers { + my ($cmd, $file) = @_; + my $commits; + my @lines = (); + my @signatures = (); + my @authors = (); + my @stats = (); + + @lines = &{$VCS_cmds{"execute_cmd"}}($cmd); + + my $pattern = $VCS_cmds{"commit_pattern"}; + my $author_pattern = $VCS_cmds{"author_pattern"}; + my $stat_pattern = $VCS_cmds{"stat_pattern"}; + + $stat_pattern =~ s/(\$\w+)/$1/eeg; #interpolate $stat_pattern + + $commits = grep(/$pattern/, @lines); # of commits + + @authors = grep(/$author_pattern/, @lines); + @signatures = grep(/^[ \t]*${signature_pattern}.*\@.*$/, @lines); + @stats = grep(/$stat_pattern/, @lines); + +# print("stats: <@stats>\n"); + + return (0, \@signatures, \@authors, \@stats) if !@signatures; + + save_commits_by_author(@lines) if ($interactive); + save_commits_by_signer(@lines) if ($interactive); + + if (!$email_git_penguin_chiefs) { + @signatures = grep(!/${penguin_chiefs}/i, @signatures); + } + + my ($author_ref, $authors_ref) = extract_formatted_signatures(@authors); + my ($types_ref, $signers_ref) = extract_formatted_signatures(@signatures); + + return ($commits, $signers_ref, $authors_ref, \@stats); +} + +sub vcs_find_author { + my ($cmd) = @_; + my @lines = (); + + @lines = &{$VCS_cmds{"execute_cmd"}}($cmd); + + if (!$email_git_penguin_chiefs) { + @lines = grep(!/${penguin_chiefs}/i, @lines); + } + + return @lines if !@lines; + + my @authors = (); + foreach my $line (@lines) { + if ($line =~ m/$VCS_cmds{"author_pattern"}/) { + my $author = $1; + my ($name, $address) = parse_email($author); + $author = format_email($name, $address, 1); + push(@authors, $author); + } + } + + save_commits_by_author(@lines) if ($interactive); + save_commits_by_signer(@lines) if ($interactive); + + return @authors; +} + +sub vcs_save_commits { + my ($cmd) = @_; + my @lines = (); + my @commits = (); + + @lines = &{$VCS_cmds{"execute_cmd"}}($cmd); + + foreach my $line (@lines) { + if ($line =~ m/$VCS_cmds{"blame_commit_pattern"}/) { + push(@commits, $1); + } + } + + return @commits; +} + +sub vcs_blame { + my ($file) = @_; + my $cmd; + my @commits = (); + + return @commits if (!(-f $file)); + + if (@range && $VCS_cmds{"blame_range_cmd"} eq "") { + my @all_commits = (); + + $cmd = $VCS_cmds{"blame_file_cmd"}; + $cmd =~ s/(\$\w+)/$1/eeg; #interpolate $cmd + @all_commits = vcs_save_commits($cmd); + + foreach my $file_range_diff (@range) { + next if (!($file_range_diff =~ m/(.+):(.+):(.+)/)); + my $diff_file = $1; + my $diff_start = $2; + my $diff_length = $3; + next if ("$file" ne "$diff_file"); + for (my $i = $diff_start; $i < $diff_start + $diff_length; $i++) { + push(@commits, $all_commits[$i]); + } + } + } elsif (@range) { + foreach my $file_range_diff (@range) { + next if (!($file_range_diff =~ m/(.+):(.+):(.+)/)); + my $diff_file = $1; + my $diff_start = $2; + my $diff_length = $3; + next if ("$file" ne "$diff_file"); + $cmd = $VCS_cmds{"blame_range_cmd"}; + $cmd =~ s/(\$\w+)/$1/eeg; #interpolate $cmd + push(@commits, vcs_save_commits($cmd)); + } + } else { + $cmd = $VCS_cmds{"blame_file_cmd"}; + $cmd =~ s/(\$\w+)/$1/eeg; #interpolate $cmd + @commits = vcs_save_commits($cmd); + } + + foreach my $commit (@commits) { + $commit =~ s/^\^//g; + } + + return @commits; +} + +my $printed_novcs = 0; +sub vcs_exists { + %VCS_cmds = %VCS_cmds_git; + return 1 if eval $VCS_cmds{"available"}; + %VCS_cmds = %VCS_cmds_hg; + return 2 if eval $VCS_cmds{"available"}; + %VCS_cmds = (); + if (!$printed_novcs) { + warn("$P: No supported VCS found. Add --nogit to options?\n"); + warn("Using a git repository produces better results.\n"); + warn("Try Linus Torvalds' latest git repository using:\n"); + warn("git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git\n"); + $printed_novcs = 1; + } + return 0; +} + +sub vcs_is_git { + vcs_exists(); + return $vcs_used == 1; +} + +sub vcs_is_hg { + return $vcs_used == 2; +} + +sub interactive_get_maintainers { + my ($list_ref) = @_; + my @list = @$list_ref; + + vcs_exists(); + + my %selected; + my %authored; + my %signed; + my $count = 0; + my $maintained = 0; + foreach my $entry (@list) { + $maintained = 1 if ($entry->[1] =~ /^(maintainer|supporter)/i); + $selected{$count} = 1; + $authored{$count} = 0; + $signed{$count} = 0; + $count++; + } + + #menu loop + my $done = 0; + my $print_options = 0; + my $redraw = 1; + while (!$done) { + $count = 0; + if ($redraw) { + printf STDERR "\n%1s %2s %-65s", + "*", "#", "email/list and role:stats"; + if ($email_git || + ($email_git_fallback && !$maintained) || + $email_git_blame) { + print STDERR "auth sign"; + } + print STDERR "\n"; + foreach my $entry (@list) { + my $email = $entry->[0]; + my $role = $entry->[1]; + my $sel = ""; + $sel = "*" if ($selected{$count}); + my $commit_author = $commit_author_hash{$email}; + my $commit_signer = $commit_signer_hash{$email}; + my $authored = 0; + my $signed = 0; + $authored++ for (@{$commit_author}); + $signed++ for (@{$commit_signer}); + printf STDERR "%1s %2d %-65s", $sel, $count + 1, $email; + printf STDERR "%4d %4d", $authored, $signed + if ($authored > 0 || $signed > 0); + printf STDERR "\n %s\n", $role; + if ($authored{$count}) { + my $commit_author = $commit_author_hash{$email}; + foreach my $ref (@{$commit_author}) { + print STDERR " Author: @{$ref}[1]\n"; + } + } + if ($signed{$count}) { + my $commit_signer = $commit_signer_hash{$email}; + foreach my $ref (@{$commit_signer}) { + print STDERR " @{$ref}[2]: @{$ref}[1]\n"; + } + } + + $count++; + } + } + my $date_ref = \$email_git_since; + $date_ref = \$email_hg_since if (vcs_is_hg()); + if ($print_options) { + $print_options = 0; + if (vcs_exists()) { + print STDERR <<EOT + +Version Control options: +g use git history [$email_git] +gf use git-fallback [$email_git_fallback] +b use git blame [$email_git_blame] +bs use blame signatures [$email_git_blame_signatures] +c# minimum commits [$email_git_min_signatures] +%# min percent [$email_git_min_percent] +d# history to use [$$date_ref] +x# max maintainers [$email_git_max_maintainers] +t all signature types [$email_git_all_signature_types] +m use .mailmap [$email_use_mailmap] +EOT + } + print STDERR <<EOT + +Additional options: +0 toggle all +tm toggle maintainers +tg toggle git entries +tl toggle open list entries +ts toggle subscriber list entries +f emails in file [$file_emails] +k keywords in file [$keywords] +r remove duplicates [$email_remove_duplicates] +p# pattern match depth [$pattern_depth] +EOT + } + print STDERR +"\n#(toggle), A#(author), S#(signed) *(all), ^(none), O(options), Y(approve): "; + + my $input = <STDIN>; + chomp($input); + + $redraw = 1; + my $rerun = 0; + my @wish = split(/[, ]+/, $input); + foreach my $nr (@wish) { + $nr = lc($nr); + my $sel = substr($nr, 0, 1); + my $str = substr($nr, 1); + my $val = 0; + $val = $1 if $str =~ /^(\d+)$/; + + if ($sel eq "y") { + $interactive = 0; + $done = 1; + $output_rolestats = 0; + $output_roles = 0; + last; + } elsif ($nr =~ /^\d+$/ && $nr > 0 && $nr <= $count) { + $selected{$nr - 1} = !$selected{$nr - 1}; + } elsif ($sel eq "*" || $sel eq '^') { + my $toggle = 0; + $toggle = 1 if ($sel eq '*'); + for (my $i = 0; $i < $count; $i++) { + $selected{$i} = $toggle; + } + } elsif ($sel eq "0") { + for (my $i = 0; $i < $count; $i++) { + $selected{$i} = !$selected{$i}; + } + } elsif ($sel eq "t") { + if (lc($str) eq "m") { + for (my $i = 0; $i < $count; $i++) { + $selected{$i} = !$selected{$i} + if ($list[$i]->[1] =~ /^(maintainer|supporter)/i); + } + } elsif (lc($str) eq "g") { + for (my $i = 0; $i < $count; $i++) { + $selected{$i} = !$selected{$i} + if ($list[$i]->[1] =~ /^(author|commit|signer)/i); + } + } elsif (lc($str) eq "l") { + for (my $i = 0; $i < $count; $i++) { + $selected{$i} = !$selected{$i} + if ($list[$i]->[1] =~ /^(open list)/i); + } + } elsif (lc($str) eq "s") { + for (my $i = 0; $i < $count; $i++) { + $selected{$i} = !$selected{$i} + if ($list[$i]->[1] =~ /^(subscriber list)/i); + } + } + } elsif ($sel eq "a") { + if ($val > 0 && $val <= $count) { + $authored{$val - 1} = !$authored{$val - 1}; + } elsif ($str eq '*' || $str eq '^') { + my $toggle = 0; + $toggle = 1 if ($str eq '*'); + for (my $i = 0; $i < $count; $i++) { + $authored{$i} = $toggle; + } + } + } elsif ($sel eq "s") { + if ($val > 0 && $val <= $count) { + $signed{$val - 1} = !$signed{$val - 1}; + } elsif ($str eq '*' || $str eq '^') { + my $toggle = 0; + $toggle = 1 if ($str eq '*'); + for (my $i = 0; $i < $count; $i++) { + $signed{$i} = $toggle; + } + } + } elsif ($sel eq "o") { + $print_options = 1; + $redraw = 1; + } elsif ($sel eq "g") { + if ($str eq "f") { + bool_invert(\$email_git_fallback); + } else { + bool_invert(\$email_git); + } + $rerun = 1; + } elsif ($sel eq "b") { + if ($str eq "s") { + bool_invert(\$email_git_blame_signatures); + } else { + bool_invert(\$email_git_blame); + } + $rerun = 1; + } elsif ($sel eq "c") { + if ($val > 0) { + $email_git_min_signatures = $val; + $rerun = 1; + } + } elsif ($sel eq "x") { + if ($val > 0) { + $email_git_max_maintainers = $val; + $rerun = 1; + } + } elsif ($sel eq "%") { + if ($str ne "" && $val >= 0) { + $email_git_min_percent = $val; + $rerun = 1; + } + } elsif ($sel eq "d") { + if (vcs_is_git()) { + $email_git_since = $str; + } elsif (vcs_is_hg()) { + $email_hg_since = $str; + } + $rerun = 1; + } elsif ($sel eq "t") { + bool_invert(\$email_git_all_signature_types); + $rerun = 1; + } elsif ($sel eq "f") { + bool_invert(\$file_emails); + $rerun = 1; + } elsif ($sel eq "r") { + bool_invert(\$email_remove_duplicates); + $rerun = 1; + } elsif ($sel eq "m") { + bool_invert(\$email_use_mailmap); + read_mailmap(); + $rerun = 1; + } elsif ($sel eq "k") { + bool_invert(\$keywords); + $rerun = 1; + } elsif ($sel eq "p") { + if ($str ne "" && $val >= 0) { + $pattern_depth = $val; + $rerun = 1; + } + } elsif ($sel eq "h" || $sel eq "?") { + print STDERR <<EOT + +Interactive mode allows you to select the various maintainers, submitters, +commit signers and mailing lists that could be CC'd on a patch. + +Any *'d entry is selected. + +If you have git or hg installed, you can choose to summarize the commit +history of files in the patch. Also, each line of the current file can +be matched to its commit author and that commits signers with blame. + +Various knobs exist to control the length of time for active commit +tracking, the maximum number of commit authors and signers to add, +and such. + +Enter selections at the prompt until you are satisfied that the selected +maintainers are appropriate. You may enter multiple selections separated +by either commas or spaces. + +EOT + } else { + print STDERR "invalid option: '$nr'\n"; + $redraw = 0; + } + } + if ($rerun) { + print STDERR "git-blame can be very slow, please have patience..." + if ($email_git_blame); + goto &get_maintainers; + } + } + + #drop not selected entries + $count = 0; + my @new_emailto = (); + foreach my $entry (@list) { + if ($selected{$count}) { + push(@new_emailto, $list[$count]); + } + $count++; + } + return @new_emailto; +} + +sub bool_invert { + my ($bool_ref) = @_; + + if ($$bool_ref) { + $$bool_ref = 0; + } else { + $$bool_ref = 1; + } +} + +sub deduplicate_email { + my ($email) = @_; + + my $matched = 0; + my ($name, $address) = parse_email($email); + $email = format_email($name, $address, 1); + $email = mailmap_email($email); + + return $email if (!$email_remove_duplicates); + + ($name, $address) = parse_email($email); + + if ($name ne "" && $deduplicate_name_hash{lc($name)}) { + $name = $deduplicate_name_hash{lc($name)}->[0]; + $address = $deduplicate_name_hash{lc($name)}->[1]; + $matched = 1; + } elsif ($deduplicate_address_hash{lc($address)}) { + $name = $deduplicate_address_hash{lc($address)}->[0]; + $address = $deduplicate_address_hash{lc($address)}->[1]; + $matched = 1; + } + if (!$matched) { + $deduplicate_name_hash{lc($name)} = [ $name, $address ]; + $deduplicate_address_hash{lc($address)} = [ $name, $address ]; + } + $email = format_email($name, $address, 1); + $email = mailmap_email($email); + return $email; +} + +sub save_commits_by_author { + my (@lines) = @_; + + my @authors = (); + my @commits = (); + my @subjects = (); + + foreach my $line (@lines) { + if ($line =~ m/$VCS_cmds{"author_pattern"}/) { + my $author = $1; + $author = deduplicate_email($author); + push(@authors, $author); + } + push(@commits, $1) if ($line =~ m/$VCS_cmds{"commit_pattern"}/); + push(@subjects, $1) if ($line =~ m/$VCS_cmds{"subject_pattern"}/); + } + + for (my $i = 0; $i < @authors; $i++) { + my $exists = 0; + foreach my $ref(@{$commit_author_hash{$authors[$i]}}) { + if (@{$ref}[0] eq $commits[$i] && + @{$ref}[1] eq $subjects[$i]) { + $exists = 1; + last; + } + } + if (!$exists) { + push(@{$commit_author_hash{$authors[$i]}}, + [ ($commits[$i], $subjects[$i]) ]); + } + } +} + +sub save_commits_by_signer { + my (@lines) = @_; + + my $commit = ""; + my $subject = ""; + + foreach my $line (@lines) { + $commit = $1 if ($line =~ m/$VCS_cmds{"commit_pattern"}/); + $subject = $1 if ($line =~ m/$VCS_cmds{"subject_pattern"}/); + if ($line =~ /^[ \t]*${signature_pattern}.*\@.*$/) { + my @signatures = ($line); + my ($types_ref, $signers_ref) = extract_formatted_signatures(@signatures); + my @types = @$types_ref; + my @signers = @$signers_ref; + + my $type = $types[0]; + my $signer = $signers[0]; + + $signer = deduplicate_email($signer); + + my $exists = 0; + foreach my $ref(@{$commit_signer_hash{$signer}}) { + if (@{$ref}[0] eq $commit && + @{$ref}[1] eq $subject && + @{$ref}[2] eq $type) { + $exists = 1; + last; + } + } + if (!$exists) { + push(@{$commit_signer_hash{$signer}}, + [ ($commit, $subject, $type) ]); + } + } + } +} + +sub vcs_assign { + my ($role, $divisor, @lines) = @_; + + my %hash; + my $count = 0; + + return if (@lines <= 0); + + if ($divisor <= 0) { + warn("Bad divisor in " . (caller(0))[3] . ": $divisor\n"); + $divisor = 1; + } + + @lines = mailmap(@lines); + + return if (@lines <= 0); + + @lines = sort(@lines); + + # uniq -c + $hash{$_}++ for @lines; + + # sort -rn + foreach my $line (sort {$hash{$b} <=> $hash{$a}} keys %hash) { + my $sign_offs = $hash{$line}; + my $percent = $sign_offs * 100 / $divisor; + + $percent = 100 if ($percent > 100); + $count++; + last if ($sign_offs < $email_git_min_signatures || + $count > $email_git_max_maintainers || + $percent < $email_git_min_percent); + push_email_address($line, ''); + if ($output_rolestats) { + my $fmt_percent = sprintf("%.0f", $percent); + add_role($line, "$role:$sign_offs/$divisor=$fmt_percent%"); + } else { + add_role($line, $role); + } + } +} + +sub vcs_file_signoffs { + my ($file) = @_; + + my $authors_ref; + my $signers_ref; + my $stats_ref; + my @authors = (); + my @signers = (); + my @stats = (); + my $commits; + + $vcs_used = vcs_exists(); + return if (!$vcs_used); + + my $cmd = $VCS_cmds{"find_signers_cmd"}; + $cmd =~ s/(\$\w+)/$1/eeg; # interpolate $cmd + + ($commits, $signers_ref, $authors_ref, $stats_ref) = vcs_find_signers($cmd, $file); + + @signers = @{$signers_ref} if defined $signers_ref; + @authors = @{$authors_ref} if defined $authors_ref; + @stats = @{$stats_ref} if defined $stats_ref; + +# print("commits: <$commits>\nsigners:<@signers>\nauthors: <@authors>\nstats: <@stats>\n"); + + foreach my $signer (@signers) { + $signer = deduplicate_email($signer); + } + + vcs_assign("commit_signer", $commits, @signers); + vcs_assign("authored", $commits, @authors); + if ($#authors == $#stats) { + my $stat_pattern = $VCS_cmds{"stat_pattern"}; + $stat_pattern =~ s/(\$\w+)/$1/eeg; #interpolate $stat_pattern + + my $added = 0; + my $deleted = 0; + for (my $i = 0; $i <= $#stats; $i++) { + if ($stats[$i] =~ /$stat_pattern/) { + $added += $1; + $deleted += $2; + } + } + my @tmp_authors = uniq(@authors); + foreach my $author (@tmp_authors) { + $author = deduplicate_email($author); + } + @tmp_authors = uniq(@tmp_authors); + my @list_added = (); + my @list_deleted = (); + foreach my $author (@tmp_authors) { + my $auth_added = 0; + my $auth_deleted = 0; + for (my $i = 0; $i <= $#stats; $i++) { + if ($author eq deduplicate_email($authors[$i]) && + $stats[$i] =~ /$stat_pattern/) { + $auth_added += $1; + $auth_deleted += $2; + } + } + for (my $i = 0; $i < $auth_added; $i++) { + push(@list_added, $author); + } + for (my $i = 0; $i < $auth_deleted; $i++) { + push(@list_deleted, $author); + } + } + vcs_assign("added_lines", $added, @list_added); + vcs_assign("removed_lines", $deleted, @list_deleted); + } +} + +sub vcs_file_blame { + my ($file) = @_; + + my @signers = (); + my @all_commits = (); + my @commits = (); + my $total_commits; + my $total_lines; + + $vcs_used = vcs_exists(); + return if (!$vcs_used); + + @all_commits = vcs_blame($file); + @commits = uniq(@all_commits); + $total_commits = @commits; + $total_lines = @all_commits; + + if ($email_git_blame_signatures) { + if (vcs_is_hg()) { + my $commit_count; + my $commit_authors_ref; + my $commit_signers_ref; + my $stats_ref; + my @commit_authors = (); + my @commit_signers = (); + my $commit = join(" -r ", @commits); + my $cmd; + + $cmd = $VCS_cmds{"find_commit_signers_cmd"}; + $cmd =~ s/(\$\w+)/$1/eeg; #substitute variables in $cmd + + ($commit_count, $commit_signers_ref, $commit_authors_ref, $stats_ref) = vcs_find_signers($cmd, $file); + @commit_authors = @{$commit_authors_ref} if defined $commit_authors_ref; + @commit_signers = @{$commit_signers_ref} if defined $commit_signers_ref; + + push(@signers, @commit_signers); + } else { + foreach my $commit (@commits) { + my $commit_count; + my $commit_authors_ref; + my $commit_signers_ref; + my $stats_ref; + my @commit_authors = (); + my @commit_signers = (); + my $cmd; + + $cmd = $VCS_cmds{"find_commit_signers_cmd"}; + $cmd =~ s/(\$\w+)/$1/eeg; #substitute variables in $cmd + + ($commit_count, $commit_signers_ref, $commit_authors_ref, $stats_ref) = vcs_find_signers($cmd, $file); + @commit_authors = @{$commit_authors_ref} if defined $commit_authors_ref; + @commit_signers = @{$commit_signers_ref} if defined $commit_signers_ref; + + push(@signers, @commit_signers); + } + } + } + + if ($from_filename) { + if ($output_rolestats) { + my @blame_signers; + if (vcs_is_hg()) {{ # Double brace for last exit + my $commit_count; + my @commit_signers = (); + @commits = uniq(@commits); + @commits = sort(@commits); + my $commit = join(" -r ", @commits); + my $cmd; + + $cmd = $VCS_cmds{"find_commit_author_cmd"}; + $cmd =~ s/(\$\w+)/$1/eeg; #substitute variables in $cmd + + my @lines = (); + + @lines = &{$VCS_cmds{"execute_cmd"}}($cmd); + + if (!$email_git_penguin_chiefs) { + @lines = grep(!/${penguin_chiefs}/i, @lines); + } + + last if !@lines; + + my @authors = (); + foreach my $line (@lines) { + if ($line =~ m/$VCS_cmds{"author_pattern"}/) { + my $author = $1; + $author = deduplicate_email($author); + push(@authors, $author); + } + } + + save_commits_by_author(@lines) if ($interactive); + save_commits_by_signer(@lines) if ($interactive); + + push(@signers, @authors); + }} + else { + foreach my $commit (@commits) { + my $i; + my $cmd = $VCS_cmds{"find_commit_author_cmd"}; + $cmd =~ s/(\$\w+)/$1/eeg; #interpolate $cmd + my @author = vcs_find_author($cmd); + next if !@author; + + my $formatted_author = deduplicate_email($author[0]); + + my $count = grep(/$commit/, @all_commits); + for ($i = 0; $i < $count ; $i++) { + push(@blame_signers, $formatted_author); + } + } + } + if (@blame_signers) { + vcs_assign("authored lines", $total_lines, @blame_signers); + } + } + foreach my $signer (@signers) { + $signer = deduplicate_email($signer); + } + vcs_assign("commits", $total_commits, @signers); + } else { + foreach my $signer (@signers) { + $signer = deduplicate_email($signer); + } + vcs_assign("modified commits", $total_commits, @signers); + } +} + +sub uniq { + my (@parms) = @_; + + my %saw; + @parms = grep(!$saw{$_}++, @parms); + return @parms; +} + +sub sort_and_uniq { + my (@parms) = @_; + + my %saw; + @parms = sort @parms; + @parms = grep(!$saw{$_}++, @parms); + return @parms; +} + +sub clean_file_emails { + my (@file_emails) = @_; + my @fmt_emails = (); + + foreach my $email (@file_emails) { + $email =~ s/[\(\<\{]{0,1}([A-Za-z0-9_\.\+-]+\@[A-Za-z0-9\.-]+)[\)\>\}]{0,1}/\<$1\>/g; + my ($name, $address) = parse_email($email); + if ($name eq '"[,\.]"') { + $name = ""; + } + + my @nw = split(/[^A-Za-zÀ-ÿ\'\,\.\+-]/, $name); + if (@nw > 2) { + my $first = $nw[@nw - 3]; + my $middle = $nw[@nw - 2]; + my $last = $nw[@nw - 1]; + + if (((length($first) == 1 && $first =~ m/[A-Za-z]/) || + (length($first) == 2 && substr($first, -1) eq ".")) || + (length($middle) == 1 || + (length($middle) == 2 && substr($middle, -1) eq "."))) { + $name = "$first $middle $last"; + } else { + $name = "$middle $last"; + } + } + + if (substr($name, -1) =~ /[,\.]/) { + $name = substr($name, 0, length($name) - 1); + } elsif (substr($name, -2) =~ /[,\.]"/) { + $name = substr($name, 0, length($name) - 2) . '"'; + } + + if (substr($name, 0, 1) =~ /[,\.]/) { + $name = substr($name, 1, length($name) - 1); + } elsif (substr($name, 0, 2) =~ /"[,\.]/) { + $name = '"' . substr($name, 2, length($name) - 2); + } + + my $fmt_email = format_email($name, $address, $email_usename); + push(@fmt_emails, $fmt_email); + } + return @fmt_emails; +} + +sub merge_email { + my @lines; + my %saw; + + for (@_) { + my ($address, $role) = @$_; + if (!$saw{$address}) { + if ($output_roles) { + push(@lines, "$address ($role)"); + } else { + push(@lines, $address); + } + $saw{$address} = 1; + } + } + + return @lines; +} + +sub output { + my (@parms) = @_; + + if ($output_multiline) { + foreach my $line (@parms) { + print("${line}\n"); + } + } else { + print(join($output_separator, @parms)); + print("\n"); + } +} + +my $rfc822re; + +sub make_rfc822re { +# Basic lexical tokens are specials, domain_literal, quoted_string, atom, and +# comment. We must allow for rfc822_lwsp (or comments) after each of these. +# This regexp will only work on addresses which have had comments stripped +# and replaced with rfc822_lwsp. + + my $specials = '()<>@,;:\\\\".\\[\\]'; + my $controls = '\\000-\\037\\177'; + + my $dtext = "[^\\[\\]\\r\\\\]"; + my $domain_literal = "\\[(?:$dtext|\\\\.)*\\]$rfc822_lwsp*"; + + my $quoted_string = "\"(?:[^\\\"\\r\\\\]|\\\\.|$rfc822_lwsp)*\"$rfc822_lwsp*"; + +# Use zero-width assertion to spot the limit of an atom. A simple +# $rfc822_lwsp* causes the regexp engine to hang occasionally. + my $atom = "[^$specials $controls]+(?:$rfc822_lwsp+|\\Z|(?=[\\[\"$specials]))"; + my $word = "(?:$atom|$quoted_string)"; + my $localpart = "$word(?:\\.$rfc822_lwsp*$word)*"; + + my $sub_domain = "(?:$atom|$domain_literal)"; + my $domain = "$sub_domain(?:\\.$rfc822_lwsp*$sub_domain)*"; + + my $addr_spec = "$localpart\@$rfc822_lwsp*$domain"; + + my $phrase = "$word*"; + my $route = "(?:\@$domain(?:,\@$rfc822_lwsp*$domain)*:$rfc822_lwsp*)"; + my $route_addr = "\\<$rfc822_lwsp*$route?$addr_spec\\>$rfc822_lwsp*"; + my $mailbox = "(?:$addr_spec|$phrase$route_addr)"; + + my $group = "$phrase:$rfc822_lwsp*(?:$mailbox(?:,\\s*$mailbox)*)?;\\s*"; + my $address = "(?:$mailbox|$group)"; + + return "$rfc822_lwsp*$address"; +} + +sub rfc822_strip_comments { + my $s = shift; +# Recursively remove comments, and replace with a single space. The simpler +# regexps in the Email Addressing FAQ are imperfect - they will miss escaped +# chars in atoms, for example. + + while ($s =~ s/^((?:[^"\\]|\\.)* + (?:"(?:[^"\\]|\\.)*"(?:[^"\\]|\\.)*)*) + \((?:[^()\\]|\\.)*\)/$1 /osx) {} + return $s; +} + +# valid: returns true if the parameter is an RFC822 valid address +# +sub rfc822_valid { + my $s = rfc822_strip_comments(shift); + + if (!$rfc822re) { + $rfc822re = make_rfc822re(); + } + + return $s =~ m/^$rfc822re$/so && $s =~ m/^$rfc822_char*$/; +} + +# validlist: In scalar context, returns true if the parameter is an RFC822 +# valid list of addresses. +# +# In list context, returns an empty list on failure (an invalid +# address was found); otherwise a list whose first element is the +# number of addresses found and whose remaining elements are the +# addresses. This is needed to disambiguate failure (invalid) +# from success with no addresses found, because an empty string is +# a valid list. + +sub rfc822_validlist { + my $s = rfc822_strip_comments(shift); + + if (!$rfc822re) { + $rfc822re = make_rfc822re(); + } + # * null list items are valid according to the RFC + # * the '1' business is to aid in distinguishing failure from no results + + my @r; + if ($s =~ m/^(?:$rfc822re)?(?:,(?:$rfc822re)?)*$/so && + $s =~ m/^$rfc822_char*$/) { + while ($s =~ m/(?:^|,$rfc822_lwsp*)($rfc822re)/gos) { + push(@r, $1); + } + return wantarray ? (scalar(@r), @r) : 1; + } + return wantarray ? () : 0; +} diff --git a/scripts/mailmapper b/scripts/mailmapper index dd1ddf6..922ada6 100755 --- a/scripts/mailmapper +++ b/scripts/mailmapper @@ -59,8 +59,7 @@ MIN_COMMITS = 50 try: toplevel = subprocess.check_output(['git', 'rev-parse', '--show-toplevel']) except subprocess.CalledProcessError: - print >> sys.stderr, 'Please run in a git repository.' - sys.exit(1) + sys.exit('Please run in a git repository.') # strip '\n' toplevel = toplevel.rstrip() diff --git a/scripts/mkmakefile b/scripts/mkmakefile index 0cc0442..84af27b 100644 --- a/scripts/mkmakefile +++ b/scripts/mkmakefile @@ -42,18 +42,11 @@ MAKEARGS += O=\$(if \$(patsubst /%,,\$(makedir)),\$(CURDIR)/)\$(patsubst %/,%,\$ MAKEFLAGS += --no-print-directory -.PHONY: all \$(MAKECMDGOALS) +.PHONY: __sub-make \$(MAKECMDGOALS) -all := \$(filter-out all Makefile,\$(MAKECMDGOALS)) +__sub-make: + \$(Q)\$(MAKE) \$(MAKEARGS) \$(MAKECMDGOALS) -all: - \$(Q)\$(MAKE) \$(MAKEARGS) \$(all) - -Makefile:; - -\$(all): all - @: - -%/: all +\$(filter-out __sub-make, \$(MAKECMDGOALS)): __sub-make @: EOF diff --git a/scripts/multiconfig.py b/scripts/multiconfig.py deleted file mode 100755 index 749abcb..0000000 --- a/scripts/multiconfig.py +++ /dev/null @@ -1,410 +0,0 @@ -#!/usr/bin/env python -# -# Copyright (C) 2014, Masahiro Yamada <yamada.m@jp.panasonic.com> -# -# SPDX-License-Identifier: GPL-2.0+ -# - -""" -A wrapper script to adjust Kconfig for U-Boot - -The biggest difference between Linux Kernel and U-Boot in terms of the -board configuration is that U-Boot has to configure multiple boot images -per board: Normal, SPL, TPL. -We need to expand the functions of Kconfig to handle multiple boot -images. - -Instead of touching various parts under the scripts/kconfig/ directory, -pushing necessary adjustments into this single script would be better -for code maintainance. All the make targets related to the configuration -(make %config) should be invoked via this script. - -Let's see what is different from the original Kconfig. - -- config, menuconfig, etc. - -The commands 'make config', 'make menuconfig', etc. are used to create -or modify the .config file, which stores configs for Normal boot image. - -The location of the one for SPL, TPL image is spl/.config, tpl/.config, -respectively. Use 'make spl/config', 'make spl/menuconfig', etc. -to create or modify the spl/.config file, which contains configs -for SPL image. -Do likewise for the tpl/.config file. -The generic syntax for SPL, TPL configuration is -'make <target_image>/<config_command>'. - -- silentoldconfig - -The command 'make silentoldconfig' updates .config, if necessary, and -additionally updates include/generated/autoconf.h and files under -include/configs/ directory. In U-Boot, it should do the same things for -SPL, TPL images for boards supporting them. -Depending on whether CONFIG_SPL, CONFIG_TPL is defined or not, -'make silentoldconfig' iterates three times at most changing the target -directory. - -To sum up, 'make silentoldconfig' possibly updates - - .config, include/generated/autoconf.h, include/config/* - - spl/.config, spl/include/generated/autoconf.h, spl/include/config/* - (in case CONFIG_SPL=y) - - tpl/.config, tpl/include/generated/autoconf.h, tpl/include/config/* - (in case CONFIG_TPL=y) - -- defconfig, <board>_defconfig - -The command 'make <board>_defconfig' creates a new .config based on the -file configs/<board>_defconfig. The command 'make defconfig' is the same -but the difference is it uses the file specified with KBUILD_DEFCONFIG -environment. - -We need to create .config, spl/.config, tpl/.config for boards where SPL -and TPL images are supported. One possible solution for that is to have -multiple defconfig files per board, but it would produce duplication -among the defconfigs. -The approach chosen here is to expand the feature and support -conditional definition in defconfig, that is, each line in defconfig -files has the form of: -<condition>:<macro definition> - -The '<condition>:' prefix specifies which image the line is valid for. -The '<condition>:' is one of: - None - the line is valid only for Normal image - S: - the line is valid only for SPL image - T: - the line is valid only for TPL image - ST: - the line is valid for SPL and TPL images - +S: - the line is valid for Normal and SPL images - +T: - the line is valid for Normal and TPL images - +ST: - the line is valid for Normal, SPL and SPL images - -So, if neither CONFIG_SPL nor CONFIG_TPL is defined, the defconfig file -has no '<condition>:' part and therefore has the same form of that of -Linux Kernel. - -In U-Boot, for example, a defconfig file can be written like this: - - CONFIG_FOO=100 - S:CONFIG_FOO=200 - T:CONFIG_FOO=300 - ST:CONFIG_BAR=y - +S:CONFIG_BAZ=y - +T:CONFIG_QUX=y - +ST:CONFIG_QUUX=y - -The defconfig above is parsed by this script and internally divided into -three temporary defconfig files. - - - Temporary defconfig for Normal image - CONFIG_FOO=100 - CONFIG_BAZ=y - CONFIG_QUX=y - CONFIG_QUUX=y - - - Temporary defconfig for SPL image - CONFIG_FOO=200 - CONFIG_BAR=y - CONFIG_BAZ=y - CONFIG_QUUX=y - - - Temporary defconfig for TPL image - CONFIG_FOO=300 - CONFIG_BAR=y - CONFIG_QUX=y - CONFIG_QUUX=y - -They are passed to scripts/kconfig/conf, each is used for generating -.config, spl/.config, tpl/.config, respectively. - -- savedefconfig - -This is the reverse operation of 'make defconfig'. -If neither CONFIG_SPL nor CONFIG_TPL is defined in the .config file, -it works as 'make savedefconfig' in Linux Kernel: create the minimal set -of config based on the .config and save it into 'defconfig' file. - -If CONFIG_SPL or CONFIG_TPL is defined, the common lines among .config, -spl/.config, tpl/.config are coalesced together and output to the file -'defconfig' in the form like: - - CONFIG_FOO=100 - S:CONFIG_FOO=200 - T:CONFIG_FOO=300 - ST:CONFIG_BAR=y - +S:CONFIG_BAZ=y - +T:CONFIG_QUX=y - +ST:CONFIG_QUUX=y - -This can be used as an input of 'make <board>_defconfig' command. -""" - -import errno -import os -import re -import subprocess -import sys - -# Constant variables -SUB_IMAGES = ('spl', 'tpl') -IMAGES = ('',) + SUB_IMAGES -SYMBOL_MAP = {'': '+', 'spl': 'S', 'tpl': 'T'} -PATTERN_SYMBOL = re.compile(r'(\+?)(S?)(T?):(.*)') - -# Environment variables (should be defined in the top Makefile) -# .get('key', 'default_value') method is useful for standalone testing. -MAKE = os.environ.get('MAKE', 'make') -srctree = os.environ.get('srctree', '.') -KCONFIG_CONFIG = os.environ.get('KCONFIG_CONFIG', '.config') - -# Useful shorthand -build = '%s -f %s/scripts/Makefile.build obj=scripts/kconfig %%s' % (MAKE, srctree) -autoconf = '%s -f %s/scripts/Makefile.autoconf obj=%%s %%s' % (MAKE, srctree) - -### helper functions ### -def mkdirs(*dirs): - """Make directories ignoring 'File exists' error.""" - for d in dirs: - try: - os.makedirs(d) - except OSError as exception: - # Ignore 'File exists' error - if exception.errno != errno.EEXIST: - raise - -def rmfiles(*files): - """Remove files ignoring 'No such file or directory' error.""" - for f in files: - try: - os.remove(f) - except OSError as exception: - # Ignore 'No such file or directory' error - if exception.errno != errno.ENOENT: - raise - -def rmdirs(*dirs): - """Remove directories ignoring 'No such file or directory' - and 'Directory not empty' error. - """ - for d in dirs: - try: - os.rmdir(d) - except OSError as exception: - # Ignore 'No such file or directory' - # and 'Directory not empty' error - if exception.errno != errno.ENOENT and \ - exception.errno != errno.ENOTEMPTY: - raise - -def error(msg): - """Output the given argument to stderr and exit with return code 1.""" - print >> sys.stderr, msg - sys.exit(1) - -def run_command(command, callback_on_error=None): - """Run the given command in a sub-shell (and exit if it fails). - - Arguments: - command: A string of the command - callback_on_error: Callback handler invoked just before exit - when the command fails (Default=None) - """ - retcode = subprocess.call(command, shell=True) - if retcode: - if callback_on_error: - callback_on_error() - error("'%s' Failed" % command) - -def run_make_config(cmd, objdir, callback_on_error=None): - """Run the make command in a sub-shell (and exit if it fails). - - Arguments: - cmd: Make target such as 'config', 'menuconfig', 'defconfig', etc. - objdir: Target directory where the make command is run. - Typically '', 'spl', 'tpl' for Normal, SPL, TPL image, - respectively. - callback_on_error: Callback handler invoked just before exit - when the command fails (Default=None) - """ - # Linux expects defconfig files in arch/$(SRCARCH)/configs/ directory, - # but U-Boot puts them in configs/ directory. - # Give SRCARCH=.. to fake scripts/kconfig/Makefile. - options = 'SRCARCH=.. KCONFIG_OBJDIR=%s' % objdir - if objdir: - options += ' KCONFIG_CONFIG=%s/%s' % (objdir, KCONFIG_CONFIG) - mkdirs(objdir) - run_command(build % cmd + ' ' + options, callback_on_error) - -def get_enabled_subimages(ignore_error=False): - """Parse .config file to detect if CONFIG_SPL, CONFIG_TPL is enabled - and return a tuple of enabled subimages. - - Arguments: - ignore_error: Specify the behavior when '.config' is not found; - Raise an exception if this flag is False. - Return a null tuple if this flag is True. - - Returns: - A tuple of enabled subimages as follows: - () if neither CONFIG_SPL nor CONFIG_TPL is defined - ('spl',) if CONFIG_SPL is defined but CONFIG_TPL is not - ('spl', 'tpl') if both CONFIG_SPL and CONFIG_TPL are defined - """ - enabled = () - match_patterns = [ (img, 'CONFIG_' + img.upper() + '=y\n') - for img in SUB_IMAGES ] - try: - f = open(KCONFIG_CONFIG) - except IOError as exception: - if not ignore_error or exception.errno != errno.ENOENT: - raise - return enabled - with f: - for line in f: - for img, pattern in match_patterns: - if line == pattern: - enabled += (img,) - return enabled - -def do_silentoldconfig(cmd): - """Run 'make silentoldconfig' for all the enabled images. - - Arguments: - cmd: should always be a string 'silentoldconfig' - """ - run_make_config(cmd, '') - subimages = get_enabled_subimages() - for obj in subimages: - mkdirs(os.path.join(obj, 'include', 'config'), - os.path.join(obj, 'include', 'generated')) - run_make_config(cmd, obj) - remove_auto_conf = lambda : rmfiles('include/config/auto.conf') - # If the following part failed, include/config/auto.conf should be deleted - # so 'make silentoldconfig' will be re-run on the next build. - run_command(autoconf % - ('include', 'include/autoconf.mk include/autoconf.mk.dep'), - remove_auto_conf) - # include/config.h has been updated after 'make silentoldconfig'. - # We need to touch include/config/auto.conf so it gets newer - # than include/config.h. - # Otherwise, 'make silentoldconfig' would be invoked twice. - os.utime('include/config/auto.conf', None) - for obj in subimages: - run_command(autoconf % (obj + '/include', - obj + '/include/autoconf.mk'), - remove_auto_conf) - -def do_tmp_defconfig(output_lines, img): - """Helper function for do_board_defconfig(). - - Write the defconfig contents into a file '.tmp_defconfig' and - invoke 'make .tmp_defconfig'. - - Arguments: - output_lines: A sequence of defconfig lines of each image - img: Target image. Typically '', 'spl', 'tpl' for - Normal, SPL, TPL images, respectively. - """ - TMP_DEFCONFIG = '.tmp_defconfig' - TMP_DIRS = ('arch', 'configs') - defconfig_path = os.path.join('configs', TMP_DEFCONFIG) - mkdirs(*TMP_DIRS) - with open(defconfig_path, 'w') as f: - f.write(''.join(output_lines[img])) - cleanup = lambda: (rmfiles(defconfig_path), rmdirs(*TMP_DIRS)) - run_make_config(TMP_DEFCONFIG, img, cleanup) - cleanup() - -def do_board_defconfig(cmd): - """Run 'make <board>_defconfig'. - - Arguments: - cmd: should be a string '<board>_defconfig' - """ - defconfig_path = os.path.join(srctree, 'configs', cmd) - output_lines = dict([ (img, []) for img in IMAGES ]) - with open(defconfig_path) as f: - for line in f: - m = PATTERN_SYMBOL.match(line) - if m: - for idx, img in enumerate(IMAGES): - if m.group(idx + 1): - output_lines[img].append(m.group(4) + '\n') - continue - output_lines[''].append(line) - do_tmp_defconfig(output_lines, '') - for img in get_enabled_subimages(): - do_tmp_defconfig(output_lines, img) - -def do_defconfig(cmd): - """Run 'make defconfig'. - - Arguments: - cmd: should always be a string 'defconfig' - """ - KBUILD_DEFCONFIG = os.environ['KBUILD_DEFCONFIG'] - print "*** Default configuration is based on '%s'" % KBUILD_DEFCONFIG - do_board_defconfig(KBUILD_DEFCONFIG) - -def do_savedefconfig(cmd): - """Run 'make savedefconfig'. - - Arguments: - cmd: should always be a string 'savedefconfig' - """ - DEFCONFIG = 'defconfig' - # Continue even if '.config' does not exist - subimages = get_enabled_subimages(True) - run_make_config(cmd, '') - output_lines = [] - prefix = {} - with open(DEFCONFIG) as f: - for line in f: - output_lines.append(line) - prefix[line] = '+' - for img in subimages: - run_make_config(cmd, img) - unmatched_lines = [] - with open(DEFCONFIG) as f: - for line in f: - if line in output_lines: - index = output_lines.index(line) - output_lines[index:index] = unmatched_lines - unmatched_lines = [] - prefix[line] += SYMBOL_MAP[img] - else: - ummatched_lines.append(line) - prefix[line] = SYMBOL_MAP[img] - with open(DEFCONFIG, 'w') as f: - for line in output_lines: - if prefix[line] == '+': - f.write(line) - else: - f.write(prefix[line] + ':' + line) - -def do_others(cmd): - """Run the make command other than 'silentoldconfig', 'defconfig', - '<board>_defconfig' and 'savedefconfig'. - - Arguments: - cmd: Make target in the form of '<target_image>/<config_command>' - The field '<target_image>/' is typically empty, 'spl/', 'tpl/' - for Normal, SPL, TPL images, respectively. - The field '<config_command>' is make target such as 'config', - 'menuconfig', etc. - """ - objdir, _, cmd = cmd.rpartition('/') - run_make_config(cmd, objdir) - -cmd_list = {'silentoldconfig': do_silentoldconfig, - 'defconfig': do_defconfig, - 'savedefconfig': do_savedefconfig} - -def main(): - cmd = sys.argv[1] - if cmd.endswith('_defconfig'): - do_board_defconfig(cmd) - else: - func = cmd_list.get(cmd, do_others) - func(cmd) - -if __name__ == '__main__': - main() diff --git a/scripts/multiconfig.sh b/scripts/multiconfig.sh new file mode 100644 index 0000000..4190798 --- /dev/null +++ b/scripts/multiconfig.sh @@ -0,0 +1,261 @@ +#!/bin/sh +# +# A wrapper script to adjust Kconfig for U-Boot +# +# Instead of touching various parts under the scripts/kconfig/ directory, +# pushing necessary adjustments into this single script would be better +# for code maintainance. All the make targets related to the configuration +# (make %config) should be invoked via this script. +# See doc/README.kconfig for further information of Kconfig. +# +# Copyright (C) 2014, Masahiro Yamada <yamada.m@jp.panasonic.com> +# +# SPDX-License-Identifier: GPL-2.0+ +# + +set -e + +# Set "DEBUG" enavironment variable to show debug messages +debug () { + if [ $DEBUG ]; then + echo "$@" + fi +} + +# Useful shorthands +build () { + debug $progname: $MAKE -f $srctree/scripts/Makefile.build obj="$@" + $MAKE -f $srctree/scripts/Makefile.build obj="$@" +} + +autoconf () { + debug $progname: $MAKE -f $srctree/scripts/Makefile.autoconf obj="$@" + $MAKE -f $srctree/scripts/Makefile.autoconf obj="$@" +} + +# Make a configuration target +# Usage: +# run_make_config <target> <objdir> +# <target>: Make target such as "config", "menuconfig", "defconfig", etc. +# <objdir>: Target directory where the make command is run. +# Typically "", "spl", "tpl" for Normal, SPL, TPL, respectively. +run_make_config () { + target=$1 + objdir=$2 + + # Linux expects defconfig files in arch/$(SRCARCH)/configs/ directory, + # but U-Boot has them in configs/ directory. + # Give SRCARCH=.. to fake scripts/kconfig/Makefile. + options="SRCARCH=.. KCONFIG_OBJDIR=$objdir" + if [ "$objdir" ]; then + options="$options KCONFIG_CONFIG=$objdir/$KCONFIG_CONFIG" + mkdir -p $objdir + fi + + build scripts/kconfig $options $target +} + +# Parse .config file to detect if CONFIG_SPL, CONFIG_TPL is enabled +# and returns: +# "" if neither CONFIG_SPL nor CONFIG_TPL is defined +# "spl" if CONFIG_SPL is defined but CONFIG_TPL is not +# "spl tpl" if both CONFIG_SPL and CONFIG_TPL are defined +get_enabled_subimages() { + if [ ! -r "$KCONFIG_CONFIG" ]; then + # This should never happen + echo "$progname: $KCONFIG_CONFIG not found" >&2 + exit 1 + fi + + # CONFIG_SPL=y -> spl + # CONFIG_TPL=y -> tpl + sed -n -e 's/^CONFIG_\(SPL\|TPL\)=y$/\1/p' $KCONFIG_CONFIG | \ + tr '[A-Z]' '[a-z]' +} + +do_silentoldconfig () { + run_make_config silentoldconfig + subimages=$(get_enabled_subimages) + + for obj in $subimages + do + mkdir -p $obj/include/config $obj/include/generated + run_make_config silentoldconfig $obj + done + + # If the following part fails, include/config/auto.conf should be + # deleted so "make silentoldconfig" will be re-run on the next build. + autoconf include include/autoconf.mk include/autoconf.mk.dep || { + rm -f include/config/auto.conf + exit 1 + } + + # include/config.h has been updated after "make silentoldconfig". + # We need to touch include/config/auto.conf so it gets newer + # than include/config.h. + # Otherwise, 'make silentoldconfig' would be invoked twice. + touch include/config/auto.conf + + for obj in $subimages + do + autoconf $obj/include $obj/include/autoconf.mk || { + rm -f include/config/auto.conf + exit 1 + } + done +} + +cleanup_after_defconfig () { + rm -f configs/.tmp_defconfig + # ignore 'Directory not empty' error + # without using non-POSIX option '--ignore-fail-on-non-empty' + rmdir arch configs 2>/dev/null || true +} + +# Usage: +# do_board_defconfig <board>_defconfig +do_board_defconfig () { + defconfig_path=$srctree/configs/$1 + tmp_defconfig_path=configs/.tmp_defconfig + + mkdir -p arch configs + # defconfig for Normal: + # pick lines without prefixes and lines starting '+' prefix + # and rip the prefixes off. + sed -n -e '/^[+A-Z]*:/!p' -e 's/^+[A-Z]*://p' $defconfig_path \ + > configs/.tmp_defconfig + + run_make_config .tmp_defconfig || { + cleanup_after_defconfig + exit 1 + } + + for img in $(get_enabled_subimages) + do + symbol=$(echo $img | cut -c 1 | tr '[a-z]' '[A-Z]') + # defconfig for SPL, TPL: + # pick lines with 'S', 'T' prefix and rip the prefixes off + sed -n -e 's/^[+A-Z]*'$symbol'[A-Z]*://p' $defconfig_path \ + > configs/.tmp_defconfig + run_make_config .tmp_defconfig $img || { + cleanup_after_defconfig + exit 1 + } + done + + cleanup_after_defconfig +} + +do_defconfig () { + if [ "$KBUILD_DEFCONFIG" ]; then + do_board_defconfig $KBUILD_DEFCONFIG + echo "*** Default configuration is based on '$KBUILD_DEFCONFIG'" + else + run_make_config defconfig + fi +} + +do_savedefconfig () { + if [ -r "$KCONFIG_CONFIG" ]; then + subimages=$(get_enabled_subimages) + else + subimages= + fi + + run_make_config savedefconfig + + output_lines= + + # -r option is necessay because some string-type configs may include + # backslashes as an escape character + while read -r line + do + output_lines="$output_lines $line" + done < defconfig + + for img in $subimages + do + run_make_config savedefconfig $img + + symbol=$(echo $img | cut -c 1 | tr '[a-z]' '[A-Z]') + unmatched= + + while read -r line + do + tmp= + match= + + # coalesce common lines together + for i in $output_lines + do + case "$i" in + "[+A-Z]*:$line") + tmp="$tmp $unmatched" + i=$(echo "$i" | \ + sed -e "s/^\([^:]\)*/\1$symbol/") + tmp="$tmp $i" + match=1 + ;; + "$line") + tmp="$tmp $unmatched" + tmp="$tmp +$symbol:$i" + match=1 + ;; + *) + tmp="$tmp $i" + ;; + esac + done + + if [ "$match" ]; then + output_lines="$tmp" + unmatched= + else + unmatched="$unmatched $symbol:$line" + fi + done < defconfig + done + + rm -f defconfig + for line in $output_lines + do + echo $line >> defconfig + done +} + +# Usage: +# do_others <objdir>/<target> +# The field "<objdir>/" is typically empy, "spl/", "tpl/" for Normal, SPL, TPL, +# respectively. +# The field "<target>" is a configuration target such as "config", +# "menuconfig", etc. +do_others () { + target=${1##*/} + + if [ "$target" = "$1" ]; then + objdir= + else + objdir=${1%/*} + fi + + run_make_config $target $objdir +} + +progname=$(basename $0) +target=$1 + +case $target in +*_defconfig) + do_board_defconfig $target;; +*_config) + # backward compatibility + do_board_defconfig ${target%_config}_defconfig;; +silentoldconfig) + do_silentoldconfig;; +defconfig) + do_defconfig;; +savedefconfig) + do_savedefconfig;; +*) + do_others $target;; +esac diff --git a/scripts/objdiff b/scripts/objdiff index b3e4f10..62e51da 100755 --- a/scripts/objdiff +++ b/scripts/objdiff @@ -25,25 +25,47 @@ # # Note: 'make mrproper' will also remove .tmp_objdiff -GIT_DIR="`git rev-parse --git-dir`" +SRCTREE=$(cd $(git rev-parse --show-toplevel 2>/dev/null); pwd) -if [ -d "$GIT_DIR" ]; then - TMPD="${GIT_DIR%git}tmp_objdiff" - - [ -d "$TMPD" ] || mkdir "$TMPD" -else - echo "ERROR: git directory not found." +if [ -z "$SRCTREE" ]; then + echo >&2 "ERROR: Not a git repository." exit 1 fi +TMPD=$SRCTREE/.tmp_objdiff + usage() { - echo "Usage: $0 <command> <args>" - echo " record <list of object files>" - echo " diff <commitA> <commitB>" - echo " clean all | <commit>" + echo >&2 "Usage: $0 <command> <args>" + echo >&2 " record <list of object files or directories>" + echo >&2 " diff <commitA> <commitB>" + echo >&2 " clean all | <commit>" exit 1 } +get_output_dir() { + dir=${1%/*} + + if [ "$dir" = "$1" ]; then + dir=. + fi + + dir=$(cd $dir; pwd) + + echo $TMPD/$CMT${dir#$SRCTREE} +} + +do_objdump() { + dir=$(get_output_dir $1) + base=${1##*/} + dis=$dir/${base%.o}.dis + + [ ! -d "$dir" ] && mkdir -p $dir + + # remove addresses for a cleaner diff + # http://dummdida.tumblr.com/post/60924060451/binary-diff-between-libc-from-scientificlinux-and + $OBJDUMP -D $1 | sed "s/^[[:space:]]\+[0-9a-f]\+//" > $dis +} + dorecord() { [ $# -eq 0 ] && usage @@ -52,20 +74,16 @@ dorecord() { CMT="`git rev-parse --short HEAD`" OBJDUMP="${CROSS_COMPILE}objdump" - OBJDIFFD="$TMPD/$CMT" - - [ ! -d "$OBJDIFFD" ] && mkdir -p "$OBJDIFFD" - for f in $FILES; do - dn="${f%/*}" - bn="${f##*/}" - - [ ! -d "$OBJDIFFD/$dn" ] && mkdir -p "$OBJDIFFD/$dn" - - # remove addresses for a more clear diff - # http://dummdida.tumblr.com/post/60924060451/binary-diff-between-libc-from-scientificlinux-and - $OBJDUMP -D "$f" | sed "s/^[[:space:]]\+[0-9a-f]\+//" \ - >"$OBJDIFFD/$dn/$bn" + for d in $FILES; do + if [ -d "$d" ]; then + for f in $(find $d -name '*.o') + do + do_objdump $f + done + else + do_objdump $d + fi done } @@ -90,12 +108,12 @@ dodiff() { DSTD="$TMPD/$DST" if [ ! -d "$SRCD" ]; then - echo "ERROR: $SRCD doesn't exist" + echo >&2 "ERROR: $SRCD doesn't exist" exit 1 fi if [ ! -d "$DSTD" ]; then - echo "ERROR: $DSTD doesn't exist" + echo >&2 "ERROR: $DSTD doesn't exist" exit 1 fi @@ -114,7 +132,7 @@ doclean() { if [ -d "$TMPD/$CMT" ]; then rm -rf $TMPD/$CMT else - echo "$CMT not found" + echo >&2 "$CMT not found" fi fi } @@ -135,7 +153,7 @@ case "$1" in doclean $* ;; *) - echo "Unrecognized command '$1'" + echo >&2 "Unrecognized command '$1'" exit 1 ;; esac diff --git a/scripts/setlocalversion b/scripts/setlocalversion index f551b4c..63d91e2 100755 --- a/scripts/setlocalversion +++ b/scripts/setlocalversion @@ -3,8 +3,10 @@ # This scripts adds local version information from the version # control systems git, mercurial (hg) and subversion (svn). # -# It was originally copied from the Linux kernel v3.2.0-rc4 and modified -# to support the U-Boot build-system. +# If something goes wrong, send a mail the kernel build mailinglist +# (see MAINTAINERS) and CC Nico Schottelius +# <nico-linuxsetlocalversion -at- schottelius.org>. +# # usage() { @@ -41,7 +43,8 @@ scm_version() fi # Check for git and a git repo. - if test -e .git && head=`git rev-parse --verify --short HEAD 2>/dev/null`; then + if test -z "$(git rev-parse --show-cdup 2>/dev/null)" && + head=`git rev-parse --verify --short HEAD 2>/dev/null`; then # If we are at a tagged commit (like "v2.6.30-rc6"), we ignore # it, because this version is defined in the top level Makefile. @@ -69,12 +72,8 @@ scm_version() printf -- '-svn%s' "`git svn find-rev $head`" fi - # Update index only on r/w media - [ -w . ] && git update-index --refresh --unmerged > /dev/null - # Check for uncommitted changes - if git diff-index --name-only HEAD | grep -v "^scripts/package" \ - | read dummy; then + if git diff-index --name-only HEAD | grep -qv "^scripts/package"; then printf '%s' -dirty fi @@ -107,7 +106,7 @@ scm_version() fi # Check for svn and a svn repo. - if rev=`svn info 2>/dev/null | grep '^Last Changed Rev'`; then + if rev=`LANG= LC_ALL= LC_MESSAGES=C svn info 2>/dev/null | grep '^Last Changed Rev'`; then rev=`echo $rev | awk '{print $NF}'` printf -- '-svn%s' "$rev" @@ -141,14 +140,12 @@ if $scm_only; then exit fi -#if test -e include/config/auto.conf; then -# . include/config/auto.conf -#else -# echo "Error: kernelrelease not valid - run 'make prepare' to update it" -# exit 1 -#fi -CONFIG_LOCALVERSION= -CONFIG_LOCALVERSION_AUTO=y +if test -e include/config/auto.conf; then + . include/config/auto.conf +else + echo "Error: kernelrelease not valid - run 'make prepare' to update it" + exit 1 +fi # localversion* files in the build and source directory res="$(collect_files localversion*)" diff --git a/test/dfu/README b/test/dfu/README new file mode 100644 index 0000000..5176aba --- /dev/null +++ b/test/dfu/README @@ -0,0 +1,37 @@ +DFU TEST CASE DESCRIPTION: + +The prerequisites for running this script are assured by +dfu_gadget_test_init.sh, which is automatically invoked by dfu_gadget_test.sh. +In this file user is able to generate their own set of test files by altering +the default set of TEST_FILES_SIZES variable. +The dfu_gadget_test_init.sh would generate test images only if they are not +already generated. + +On the target device, environment variable "dfu_alt_info" must contain at +least: + + dfu_test.bin fat 0 6;dfudummy.bin fat 0 6 + +Depending on your device, you may need to replace "fat" with +"ext4", and "6" with the relevant partition number. For reference please +consult the config file for TRATS/TRATS2 devices +(../../include/configs/trats{2}.h) + +One can use fat, ext4 or any other supported file system supported by U-Boot. +These can be created by exporting storage devices via UMS (ums 0 mmc 0) and +using standard tools on host (like mkfs.ext4). + +Example usage: +1. On the target: + setenv dfu_alt_info dfu_test.bin fat 0 6\;dfudummy.bin fat 0 6 + dfu 0 mmc 0 +2. On the host: + test/dfu/dfu_gadget_test.sh X Y [test file name] + e.g. test/dfu/dfu_gadget_test.sh 0 1 + or + e.g. test/dfu/dfu_gadget_test.sh 0 1 ./dat_960.img + +... where X and Y are dfu_test.bin's and dfudummy.bin's alt setting numbers. +They can be obtained from dfu-util -l or $dfu_alt_info. +It is also possible to pass optional [test file name] to force the script to +test one particular file. diff --git a/test/dfu/dfu_gadget_test.sh b/test/dfu/dfu_gadget_test.sh new file mode 100755 index 0000000..2f5b7db --- /dev/null +++ b/test/dfu/dfu_gadget_test.sh @@ -0,0 +1,104 @@ +#! /bin/bash + +# Copyright (C) 2014 Samsung Electronics +# Lukasz Majewski <l.majewski@samsung.com> +# +# Script fixes, enhancements and testing: +# Stephen Warren <swarren@nvidia.com> +# +# DFU operation test script +# +# SPDX-License-Identifier: GPL-2.0+ + +set -e # any command return if not equal to zero +clear + +COLOUR_RED="\33[31m" +COLOUR_GREEN="\33[32m" +COLOUR_DEFAULT="\33[0m" + +DIR=./ +SUFFIX=img +RCV_DIR=rcv/ +LOG_FILE=./log/log-`date +%d-%m-%Y_%H-%M-%S` + +cd `dirname $0` +./dfu_gadget_test_init.sh + +cleanup () { + rm -rf $DIR$RCV_DIR +} + +die () { + printf " $COLOUR_RED FAILED $COLOUR_DEFAULT \n" + cleanup + exit 1 +} + +calculate_md5sum () { + MD5SUM=`md5sum $1` + MD5SUM=`echo $MD5SUM | cut -d ' ' -f1` + echo "md5sum:"$MD5SUM +} + +dfu_test_file () { + printf "$COLOUR_GREEN ========================================================================================= $COLOUR_DEFAULT\n" + printf "File:$COLOUR_GREEN %s $COLOUR_DEFAULT\n" $1 + + dfu-util -D $1 -a $TARGET_ALT_SETTING >> $LOG_FILE 2>&1 || die $? + + echo -n "TX: " + calculate_md5sum $1 + + MD5_TX=$MD5SUM + + dfu-util -D ${DIR}/dfudummy.bin -a $TARGET_ALT_SETTING_B >> $LOG_FILE 2>&1 || die $? + + N_FILE=$DIR$RCV_DIR${1:2}"_rcv" + + dfu-util -U $N_FILE -a $TARGET_ALT_SETTING >> $LOG_FILE 2>&1 || die $? + + echo -n "RX: " + calculate_md5sum $N_FILE + MD5_RX=$MD5SUM + + if [ "$MD5_TX" == "$MD5_RX" ]; then + printf " $COLOUR_GREEN -------> OK $COLOUR_DEFAULT \n" + else + printf " $COLOUR_RED -------> FAILED $COLOUR_DEFAULT \n" + cleanup + exit 1 + fi + +} + +printf "$COLOUR_GREEN========================================================================================= $COLOUR_DEFAULT\n" +echo "DFU EP0 transmission test program" +echo "Trouble shoot -> disable DBG (even the KERN_DEBUG) in the UDC driver" +echo "@ -> TRATS2 # dfu 0 mmc 0" +cleanup +mkdir -p $DIR$RCV_DIR +touch $LOG_FILE + +if [ $# -eq 0 ] +then + printf " $COLOUR_RED Please pass alt setting number!! $COLOUR_DEFAULT \n" + exit 0 +fi + +TARGET_ALT_SETTING=$1 +TARGET_ALT_SETTING_B=$2 + +if [ -n "$3" ] +then + dfu_test_file $3 +else + for file in $DIR*.$SUFFIX + do + dfu_test_file $file + done +fi + +cleanup + +exit 0 diff --git a/test/dfu/dfu_gadget_test_init.sh b/test/dfu/dfu_gadget_test_init.sh new file mode 100755 index 0000000..640628e --- /dev/null +++ b/test/dfu/dfu_gadget_test_init.sh @@ -0,0 +1,45 @@ +#! /bin/bash + +# Copyright (C) 2014 Samsung Electronics +# Lukasz Majewski <l.majewski@samsung.com> +# +# Script fixes, enhancements and testing: +# Stephen Warren <swarren@nvidia.com> +# +# Script for test files generation +# +# SPDX-License-Identifier: GPL-2.0+ + +set -e # any command return if not equal to zero +clear + +COLOUR_RED="\33[31m" +COLOUR_GREEN="\33[32m" +COLOUR_DEFAULT="\33[0m" + +LOG_DIR="./log" + +if [ $# -eq 0 ]; then + TEST_FILES_SIZES="63 64 65 127 128 129 4095 4096 4097 959 960 961 1048575 1048576 8M" +else + TEST_FILES_SIZES=$@ +fi + +printf "Init script for generating data necessary for DFU test script" + +if [ ! -d $LOG_DIR ]; then + `mkdir $LOG_DIR` +fi + +for size in $TEST_FILES_SIZES +do + FILE="./dat_$size.img" + if [ ! -f $FILE ]; then + dd if=/dev/urandom of="./dat_$size.img" bs=$size count=1 > /dev/null 2>&1 || exit $? + fi +done +dd if=/dev/urandom of="./dfudummy.bin" bs=1024 count=1 > /dev/null 2>&1 || exit $? + +printf "$COLOUR_GREEN OK $COLOUR_DEFAULT \n" + +exit 0 diff --git a/test/image/test-fit.py b/test/image/test-fit.py index 7394df7..b065fcb 100755 --- a/test/image/test-fit.py +++ b/test/image/test-fit.py @@ -93,13 +93,13 @@ base_fdt = ''' # then do the 'bootm' command, then save out memory from the places where # we expect 'bootm' to write things. Then quit. base_script = ''' -sb load host 0 %(fit_addr)x %(fit)s +sb load hostfs 0 %(fit_addr)x %(fit)s fdt addr %(fit_addr)x bootm start %(fit_addr)x bootm loados -sb save host 0 %(kernel_out)s %(kernel_addr)x %(kernel_size)x -sb save host 0 %(fdt_out)s %(fdt_addr)x %(fdt_size)x -sb save host 0 %(ramdisk_out)s %(ramdisk_addr)x %(ramdisk_size)x +sb save hostfs 0 %(kernel_out)s %(kernel_addr)x %(kernel_size)x +sb save hostfs 0 %(fdt_out)s %(fdt_addr)x %(fdt_size)x +sb save hostfs 0 %(ramdisk_out)s %(ramdisk_addr)x %(ramdisk_size)x reset ''' diff --git a/test/ums/README b/test/ums/README new file mode 100644 index 0000000..c80fbfe --- /dev/null +++ b/test/ums/README @@ -0,0 +1,30 @@ +UMS test script. + +ums_gadget_test.sh +================== + +Example usage: +1. On the target: + create UMS exportable partitions (with e.g. gpt write), or specify a + partition number (PART_NUM) as "-" to use the entire device + ums 0 mmc 0 +2. On the host: + sudo test/ums/ums_gadget_test.sh VID PID PART_NUM [-f FILE_SYSTEM] [test_file] + e.g. sudo test/ums/ums_gadget_test.sh 0525 a4a5 6 -f vfat ./dat_14M.img + +... where: + VID - UMS device USB Vendor ID + PID - UMS device USB Product ID + PART_NUM - is the partition number on which UMS operates or "-" to use the + whole device + +Information about available partitions on the target one can read with using +the 'mmc part' or 'part list' commands. + +The partition num (PART_NUM) can be specified as '-' for using the whole device. + +The [-f FILE_SYSTEM] optional switch allows for formatting target partition to +FILE_SYSTEM. + +The last, optional [test_file] parameter is for specifying the exact test file +to use. diff --git a/test/ums/ums_gadget_test.sh b/test/ums/ums_gadget_test.sh new file mode 100755 index 0000000..56d4616 --- /dev/null +++ b/test/ums/ums_gadget_test.sh @@ -0,0 +1,175 @@ +#! /bin/bash + +# Copyright (C) 2014 Samsung Electronics +# Lukasz Majewski <l.majewski@samsung.com> +# +# UMS operation test script +# +# SPDX-License-Identifier: GPL-2.0+ + +clear + +COLOUR_RED="\33[31m" +COLOUR_GREEN="\33[32m" +COLOUR_DEFAULT="\33[0m" + +DIR=./ +SUFFIX=img +RCV_DIR=rcv/ +LOG_FILE=./log/log-`date +%d-%m-%Y_%H-%M-%S` + +cd `dirname $0` +../dfu/dfu_gadget_test_init.sh 33M 97M + +cleanup () { + rm -rf $RCV_DIR $MNT_DIR +} + +control_c() +# run if user hits control-c +{ + echo -en "\n*** CTRL+C ***\n" + umount $MNT_DIR + cleanup + exit 0 +} + +# trap keyboard interrupt (control-c) +trap control_c SIGINT + +die () { + printf " $COLOUR_RED FAILED $COLOUR_DEFAULT \n" + cleanup + exit 1 +} + +calculate_md5sum () { + MD5SUM=`md5sum $1` + MD5SUM=`echo $MD5SUM | cut -d ' ' -f1` + echo "md5sum:"$MD5SUM +} + +ums_test_file () { + printf "$COLOUR_GREEN========================================================================================= $COLOUR_DEFAULT\n" + printf "File:$COLOUR_GREEN %s $COLOUR_DEFAULT\n" $1 + + mount /dev/$MEM_DEV $MNT_DIR + if [ -f $MNT_DIR/dat_* ]; then + rm $MNT_DIR/dat_* + fi + + cp ./$1 $MNT_DIR + umount $MNT_DIR + + + echo -n "TX: " + calculate_md5sum $1 + + MD5_TX=$MD5SUM + sleep 1 + N_FILE=$DIR$RCV_DIR${1:2}"_rcv" + + mount /dev/$MEM_DEV $MNT_DIR + cp $MNT_DIR/$1 $N_FILE || die $? + rm $MNT_DIR/$1 + umount $MNT_DIR + + echo -n "RX: " + calculate_md5sum $N_FILE + MD5_RX=$MD5SUM + + if [ "$MD5_TX" == "$MD5_RX" ]; then + printf " $COLOUR_GREEN -------> OK $COLOUR_DEFAULT \n" + else + printf " $COLOUR_RED -------> FAILED $COLOUR_DEFAULT \n" + cleanup + exit 1 + fi +} + +printf "$COLOUR_GREEN========================================================================================= $COLOUR_DEFAULT\n" +echo "U-boot UMS test program" + +if [ $EUID -ne 0 ]; then + echo "You must be root to do this." 1>&2 + exit 100 +fi + +if [ $# -lt 3 ]; then + echo "Wrong number of arguments" + echo "Example:" + echo "sudo ./ums_gadget_test.sh VID PID PART_NUM [-f ext4] [test_file]" + die +fi + +MNT_DIR="/mnt/tmp-ums-test" + +VID=$1; shift +PID=$1; shift +PART_NUM=$1; shift + +if [ "$1" == "-f" ]; then + shift + FS_TO_FORMAT=$1; shift +fi + +TEST_FILE=$1 + +for f in `find /sys -type f -name idProduct`; do + d=`dirname ${f}` + if [ `cat ${d}/idVendor` != "${VID}" ]; then + continue + fi + if [ `cat ${d}/idProduct` != "${PID}" ]; then + continue + fi + USB_DEV=${d} + break +done + +if [ -z "${USB_DEV}" ]; then + echo "Connect target" + echo "e.g. ums 0 mmc 0" + exit 1 +fi + +MEM_DEV=`find $USB_DEV -type d -name "sd[a-z]" | awk -F/ '{print $(NF)}' -` + +mkdir -p $RCV_DIR +if [ ! -d $MNT_DIR ]; then + mkdir -p $MNT_DIR +fi + +if [ "$PART_NUM" == "-" ]; then + PART_NUM="" +fi +MEM_DEV=$MEM_DEV$PART_NUM + +if [ -n "$FS_TO_FORMAT" ]; then + echo -n "Formatting partition /dev/$MEM_DEV to $FS_TO_FORMAT" + mkfs -t $FS_TO_FORMAT /dev/$MEM_DEV > /dev/null 2>&1 + if [ $? -eq 0 ]; then + printf " $COLOUR_GREEN DONE $COLOUR_DEFAULT \n" + else + die + fi +fi + +printf "Mount: /dev/$MEM_DEV \n" + +if [ -n "$TEST_FILE" ]; then + if [ ! -e $TEST_FILE ]; then + echo "No file: $TEST_FILE" + die + fi + ums_test_file $TEST_FILE +else + for file in $DIR*.$SUFFIX + do + ums_test_file $file + done +fi + +cleanup + +exit 0 diff --git a/test/vboot/vboot_test.sh b/test/vboot/vboot_test.sh index 8074fc6..6d7abb8 100755 --- a/test/vboot/vboot_test.sh +++ b/test/vboot/vboot_test.sh @@ -54,8 +54,16 @@ echo ${mkimage} -D "${dtc}" echo "Build keys" mkdir -p ${keys} +PUBLIC_EXPONENT=${1} + +if [ -z "${PUBLIC_EXPONENT}" ]; then + PUBLIC_EXPONENT=65537 +fi + # Create an RSA key pair -openssl genrsa -F4 -out ${keys}/dev.key 2048 2>/dev/null +openssl genpkey -algorithm RSA -out ${keys}/dev.key \ + -pkeyopt rsa_keygen_bits:2048 \ + -pkeyopt rsa_keygen_pubexp:${PUBLIC_EXPONENT} 2>/dev/null # Create a certificate containing the public key openssl req -batch -new -x509 -key ${keys}/dev.key -out ${keys}/dev.crt diff --git a/tools/buildman/README b/tools/buildman/README index c30c1d4..d4e8404 100644 --- a/tools/buildman/README +++ b/tools/buildman/README @@ -3,6 +3,8 @@ # SPDX-License-Identifier: GPL-2.0+ # +(Please read 'How to change from MAKEALL' if you are used to that tool) + What is this? ============= @@ -39,9 +41,10 @@ Theory of Operation Buildman is a builder. It is not make, although it runs make. It does not produce any useful output on the terminal while building, except for -progress information. All the output (errors, warnings and binaries if you -are ask for them) is stored in output directories, which you can look at -while the build is progressing, or when it is finished. +progress information (except with -v, see below). All the output (errors, +warnings and binaries if you are ask for them) is stored in output +directories, which you can look at while the build is progressing, or when +it is finished. Buildman produces a concise summary of which boards succeeded and failed. It shows which commit introduced which board failure using a simple @@ -71,16 +74,21 @@ directory. It clones this repository into a copy for each thread, and the threads do not affect the state of your git repository. Any checkouts done by the thread affect only the working directory for that thread. -Buildman automatically selects the correct toolchain for each board. You -must supply suitable toolchains, but buildman takes care of selecting the +Buildman automatically selects the correct tool chain for each board. You +must supply suitable tool chains, but buildman takes care of selecting the right one. -Buildman always builds a branch, and always builds the upstream commit as -well, for comparison. It cannot build individual commits at present, unless -(maybe) you point it at an empty branch. Put all your commits in a branch, -set the branch's upstream to a valid value, and all will be well. Otherwise -buildman will perform random actions. Use -n to check what the random -actions might be. +Buildman generally builds a branch (with the -b flag), and in this case +builds the upstream commit as well, for comparison. It cannot build +individual commits at present, unless (maybe) you point it at an empty +branch. Put all your commits in a branch, set the branch's upstream to a +valid value, and all will be well. Otherwise buildman will perform random +actions. Use -n to check what the random actions might be. + +If you just want to build the current source tree, leave off the -b flag. +This will display results and errors as they happen. You can still look +at them later using -s. Note that buildman will assume that the source +has changed, and will build all specified boards in this case. Buildman is optimised for building many commits at once, for many boards. On multi-core machines, Buildman is fast because it uses most of the @@ -100,6 +108,15 @@ behaviour is a superset of exact or substring matching. Examples are: * '^tegra[23]0$' All boards with either Tegra20 or Tegra30 SoC * 'powerpc' All PowerPC boards +While the default is to OR the terms together, you can also make use of +the '&' operator to limit the selection: + +* 'freescale & arm sandbox' All Freescale boards with ARM architecture, + plus sandbox + +It is convenient to use the -n option to see whaat will be built based on +the subset given. + Buildman does not store intermediate object files. It optionally copies the binary output into a directory when a build is successful. Size information is always recorded. It needs a fair bit of disk space to work, @@ -126,6 +143,8 @@ example: root: / rest: /toolchains/* eldk: /opt/eldk-4.2 +arm: /opt/linaro/gcc-linaro-arm-linux-gnueabihf-4.8-2013.08_linux +aarch64: /opt/linaro/gcc-linaro-aarch64-none-elf-4.8-2013.10_linux [toolchain-alias] x86: i386 @@ -287,7 +306,7 @@ If it can't detect the upstream branch, try checking out the branch, and doing something like 'git branch --set-upstream <branch> upstream/master' or something similar. -As an exmmple: +As an example: Dry run, so not doing much. But I would do this: @@ -339,7 +358,7 @@ Building 18 commits for 1059 boards (4 threads, 1 job per thread) 528 36 124 /19062 1:13:30 : SIMPC8313_SP This means that it is building 19062 board/commit combinations. So far it -has managed to succesfully build 528. Another 36 have built with warnings, +has managed to successfully build 528. Another 36 have built with warnings, and 124 more didn't build at all. Buildman expects to complete the process in an hour and 15 minutes. Use this time to buy a faster computer. @@ -413,7 +432,7 @@ again. At commit 16, the error moves - you can see that the old error at line 120 is fixed, but there is a new one at line 126. This is probably only because -we added some code and moved the broken line futher down the file. +we added some code and moved the broken line father down the file. If many boards have the same error, then -e will display the error only once. This makes the output as concise as possible. @@ -491,7 +510,7 @@ You can also use -d to see a detailed size breakdown for each board. This list is sorted in order from largest growth to largest reduction. It is possible to go a little further with the -B option (--bloat). This -shows where U-Boot has bloted, breaking the size change down to the function +shows where U-Boot has bloated, breaking the size change down to the function level. Example output is below: $ ./tools/buildman/buildman -b us-mem4 -sSdB @@ -657,12 +676,122 @@ It is expected that any variables added are dealt with in U-Boot's config.mk file and documented in the README. +Quick Sanity Check +================== + +If you have made changes and want to do a quick sanity check of the +currently-checked-out source, run buildman without the -b flag. This will +build the selected boards and display build status and errors as it runs +(i.e. -v amd -e are enabled automatically). + + Other options ============= Buildman has various other command line options. Try --help to see them. +How to change from MAKEALL +========================== + +Buildman includes most of the features of MAKEALL and is generally faster +and easier to use. In particular it builds entire branches: if a particular +commit introduces an error in a particular board, buildman can easily show +you this, even if a later commit fixes that error. + +The reasons to deprecate MAKEALL are: +- We don't want to maintain two build systems +- Buildman is typically faster +- Buildman has a lot more features + +But still, many people will be sad to lose MAKEALL. If you are used to +MAKEALL, here are a few pointers. + +First you need to set up your tool chains - see the 'Setting up' section +for details. Once you have your required toolchain(s) detected then you are +ready to go. + +To build the current source tree, run buildman without a -b flag: + + ./tools/buildman/buildman <list of things to build> + +This will build the current source tree for the given boards and display +the results and errors. + +However buildman usually works on entire branches, and for that you must +specify a board flag: + + ./tools/buildman/buildman -b <branch_name> <list of things to build> + +followed by (afterwards, or perhaps concurrently in another terminal): + + ./tools/buildman/buildman -b <branch_name> -s <list of things to build> + +to see the results of the build. Rather than showing you all the output, +buildman just shows a summary, with red indicating that a commit introduced +an error and green indicating that a commit fixed an error. Use the -e +flag to see the full errors. + +If you really want to see build results as they happen, use -v when doing a +build (and -e if you want to see errors as well). + +You don't need to stick around on that branch while buildman is running. It +checks out its own copy of the source code, so you can change branches, +add commits, etc. without affecting the build in progress. + +The <list of things to build> can include board names, architectures or the +like. There are no flags to disambiguate since ambiguities are rare. Using +the examples from MAKEALL: + +Examples: + - build all Power Architecture boards: + MAKEALL -a powerpc + MAKEALL --arch powerpc + MAKEALL powerpc + ** buildman -b <branch> powerpc + - build all PowerPC boards manufactured by vendor "esd": + MAKEALL -a powerpc -v esd + ** buildman -b <branch> esd + - build all PowerPC boards manufactured either by "keymile" or "siemens": + MAKEALL -a powerpc -v keymile -v siemens + ** buildman -b <branch> keymile siemens + - build all Freescale boards with MPC83xx CPUs, plus all 4xx boards: + MAKEALL -c mpc83xx -v freescale 4xx + ** buildman -b <branch> mpc83xx freescale 4xx + +Buildman automatically tries to use all the CPUs in your machine. If you +are building a lot of boards it will use one thread for every CPU core +it detects in your machine. This is like MAKEALL's BUILD_NBUILDS option. +You can use the -T flag to change the number of threads. If you are only +building a few boards, buildman will automatically run make with the -j +flag to increase the number of concurrent make tasks. It isn't normally +that helpful to fiddle with this option, but if you use the BUILD_NCPUS +option in MAKEALL then -j is the equivalent in buildman. + +Buildman puts its output in ../<branch_name> by default but you can change +this with the -o option. Buildman normally does out-of-tree builds: use -i +to disable that if you really want to. But be careful that once you have +used -i you pollute buildman's copies of the source tree, and you will need +to remove the build directory (normally ../<branch_name>) to run buildman +in normal mode (without -i). + +Buildman doesn't keep the output result normally, but use the -k option to +do this. + +Please read 'Theory of Operation' a few times as it will make a lot of +things clearer. + +Some options you might like are: + + -B shows which functions are growing/shrinking in which commit - great + for finding code bloat. + -S shows image sizes for each commit (just an overall summary) + -u shows boards that you haven't built yet + --step 0 will build just the upstream commit and the last commit of your + branch. This is often a quick sanity check that your branch doesn't + break anything. But note this does not check bisectability! + + TODO ==== diff --git a/tools/buildman/board.py b/tools/buildman/board.py index 7bcc932..a333287 100644 --- a/tools/buildman/board.py +++ b/tools/buildman/board.py @@ -5,6 +5,72 @@ import re +class Expr: + """A single regular expression for matching boards to build""" + + def __init__(self, expr): + """Set up a new Expr object. + + Args: + expr: String cotaining regular expression to store + """ + self._expr = expr + self._re = re.compile(expr) + + def Matches(self, props): + """Check if any of the properties match the regular expression. + + Args: + props: List of properties to check + Returns: + True if any of the properties match the regular expression + """ + for prop in props: + if self._re.match(prop): + return True + return False + + def __str__(self): + return self._expr + +class Term: + """A list of expressions each of which must match with properties. + + This provides a list of 'AND' expressions, meaning that each must + match the board properties for that board to be built. + """ + def __init__(self): + self._expr_list = [] + self._board_count = 0 + + def AddExpr(self, expr): + """Add an Expr object to the list to check. + + Args: + expr: New Expr object to add to the list of those that must + match for a board to be built. + """ + self._expr_list.append(Expr(expr)) + + def __str__(self): + """Return some sort of useful string describing the term""" + return '&'.join([str(expr) for expr in self._expr_list]) + + def Matches(self, props): + """Check if any of the properties match this term + + Each of the expressions in the term is checked. All must match. + + Args: + props: List of properties to check + Returns: + True if all of the expressions in the Term match, else False + """ + for expr in self._expr_list: + if not expr.Matches(props): + return False + return True + class Board: """A particular board that we can build""" def __init__(self, status, arch, cpu, soc, vendor, board_name, target, options): @@ -124,6 +190,55 @@ class Boards: """ return [board.target for board in self._boards if board.build_it] + def _BuildTerms(self, args): + """Convert command line arguments to a list of terms. + + This deals with parsing of the arguments. It handles the '&' + operator, which joins several expressions into a single Term. + + For example: + ['arm & freescale sandbox', 'tegra'] + + will produce 3 Terms containing expressions as follows: + arm, freescale + sandbox + tegra + + The first Term has two expressions, both of which must match for + a board to be selected. + + Args: + args: List of command line arguments + Returns: + A list of Term objects + """ + syms = [] + for arg in args: + for word in arg.split(): + sym_build = [] + for term in word.split('&'): + if term: + sym_build.append(term) + sym_build.append('&') + syms += sym_build[:-1] + terms = [] + term = None + oper = None + for sym in syms: + if sym == '&': + oper = sym + elif oper: + term.AddExpr(sym) + oper = None + else: + if term: + terms.append(term) + term = Term() + term.AddExpr(sym) + if term: + terms.append(term) + return terms + def SelectBoards(self, args): """Mark boards selected based on args @@ -137,26 +252,21 @@ class Boards: due to each argument, arranged by argument. """ result = {} - argres = {} - for arg in args: - result[arg] = 0 - argres[arg] = re.compile(arg) + terms = self._BuildTerms(args) + result['all'] = 0 + for term in terms: + result[str(term)] = 0 for board in self._boards: - if args: - for arg in args: - argre = argres[arg] - match = False - for prop in board.props: - match = argre.match(prop) - if match: - break - if match: - if not board.build_it: - board.build_it = True - result[arg] += 1 - result['all'] += 1 + if terms: + match = False + for term in terms: + if term.Matches(board.props): + board.build_it = True + result[str(term)] += 1 + result['all'] += 1 + break else: board.build_it = True result['all'] += 1 diff --git a/tools/buildman/builder.py b/tools/buildman/builder.py index 48408ff..a555bd8 100644 --- a/tools/buildman/builder.py +++ b/tools/buildman/builder.py @@ -6,7 +6,6 @@ # import collections -import errno from datetime import datetime, timedelta import glob import os @@ -15,9 +14,9 @@ import Queue import shutil import string import sys -import threading import time +import builderthread import command import gitutil import terminal @@ -97,409 +96,6 @@ OUTCOME_OK, OUTCOME_WARNING, OUTCOME_ERROR, OUTCOME_UNKNOWN = range(4) trans_valid_chars = string.maketrans("/: ", "---") -def Mkdir(dirname): - """Make a directory if it doesn't already exist. - - Args: - dirname: Directory to create - """ - try: - os.mkdir(dirname) - except OSError as err: - if err.errno == errno.EEXIST: - pass - else: - raise - -class BuilderJob: - """Holds information about a job to be performed by a thread - - Members: - board: Board object to build - commits: List of commit options to build. - """ - def __init__(self): - self.board = None - self.commits = [] - - -class ResultThread(threading.Thread): - """This thread processes results from builder threads. - - It simply passes the results on to the builder. There is only one - result thread, and this helps to serialise the build output. - """ - def __init__(self, builder): - """Set up a new result thread - - Args: - builder: Builder which will be sent each result - """ - threading.Thread.__init__(self) - self.builder = builder - - def run(self): - """Called to start up the result thread. - - We collect the next result job and pass it on to the build. - """ - while True: - result = self.builder.out_queue.get() - self.builder.ProcessResult(result) - self.builder.out_queue.task_done() - - -class BuilderThread(threading.Thread): - """This thread builds U-Boot for a particular board. - - An input queue provides each new job. We run 'make' to build U-Boot - and then pass the results on to the output queue. - - Members: - builder: The builder which contains information we might need - thread_num: Our thread number (0-n-1), used to decide on a - temporary directory - """ - def __init__(self, builder, thread_num): - """Set up a new builder thread""" - threading.Thread.__init__(self) - self.builder = builder - self.thread_num = thread_num - - def Make(self, commit, brd, stage, cwd, *args, **kwargs): - """Run 'make' on a particular commit and board. - - The source code will already be checked out, so the 'commit' - argument is only for information. - - Args: - commit: Commit object that is being built - brd: Board object that is being built - stage: Stage of the build. Valid stages are: - distclean - can be called to clean source - config - called to configure for a board - build - the main make invocation - it does the build - args: A list of arguments to pass to 'make' - kwargs: A list of keyword arguments to pass to command.RunPipe() - - Returns: - CommandResult object - """ - return self.builder.do_make(commit, brd, stage, cwd, *args, - **kwargs) - - def RunCommit(self, commit_upto, brd, work_dir, do_config, force_build, - force_build_failures): - """Build a particular commit. - - If the build is already done, and we are not forcing a build, we skip - the build and just return the previously-saved results. - - Args: - commit_upto: Commit number to build (0...n-1) - brd: Board object to build - work_dir: Directory to which the source will be checked out - do_config: True to run a make <board>_defconfig on the source - force_build: Force a build even if one was previously done - force_build_failures: Force a bulid if the previous result showed - failure - - Returns: - tuple containing: - - CommandResult object containing the results of the build - - boolean indicating whether 'make config' is still needed - """ - # Create a default result - it will be overwritte by the call to - # self.Make() below, in the event that we do a build. - result = command.CommandResult() - result.return_code = 0 - if self.builder.in_tree: - out_dir = work_dir - else: - out_dir = os.path.join(work_dir, 'build') - - # Check if the job was already completed last time - done_file = self.builder.GetDoneFile(commit_upto, brd.target) - result.already_done = os.path.exists(done_file) - will_build = (force_build or force_build_failures or - not result.already_done) - if result.already_done and will_build: - # Get the return code from that build and use it - with open(done_file, 'r') as fd: - result.return_code = int(fd.readline()) - err_file = self.builder.GetErrFile(commit_upto, brd.target) - if os.path.exists(err_file) and os.stat(err_file).st_size: - result.stderr = 'bad' - elif not force_build: - # The build passed, so no need to build it again - will_build = False - - if will_build: - # We are going to have to build it. First, get a toolchain - if not self.toolchain: - try: - self.toolchain = self.builder.toolchains.Select(brd.arch) - except ValueError as err: - result.return_code = 10 - result.stdout = '' - result.stderr = str(err) - # TODO(sjg@chromium.org): This gets swallowed, but needs - # to be reported. - - if self.toolchain: - # Checkout the right commit - if commit_upto is not None: - commit = self.builder.commits[commit_upto] - if self.builder.checkout: - git_dir = os.path.join(work_dir, '.git') - gitutil.Checkout(commit.hash, git_dir, work_dir, - force=True) - else: - commit = self.builder.commit # Ick, fix this for BuildCommits() - - # Set up the environment and command line - env = self.toolchain.MakeEnvironment() - Mkdir(out_dir) - args = [] - if not self.builder.in_tree: - args.append('O=build') - args.append('-s') - if self.builder.num_jobs is not None: - args.extend(['-j', str(self.builder.num_jobs)]) - config_args = ['%s_defconfig' % brd.target] - config_out = '' - args.extend(self.builder.toolchains.GetMakeArguments(brd)) - - # If we need to reconfigure, do that now - if do_config: - result = self.Make(commit, brd, 'distclean', work_dir, - 'distclean', *args, env=env) - result = self.Make(commit, brd, 'config', work_dir, - *(args + config_args), env=env) - config_out = result.combined - do_config = False # No need to configure next time - if result.return_code == 0: - result = self.Make(commit, brd, 'build', work_dir, *args, - env=env) - result.stdout = config_out + result.stdout - else: - result.return_code = 1 - result.stderr = 'No tool chain for %s\n' % brd.arch - result.already_done = False - - result.toolchain = self.toolchain - result.brd = brd - result.commit_upto = commit_upto - result.out_dir = out_dir - return result, do_config - - def _WriteResult(self, result, keep_outputs): - """Write a built result to the output directory. - - Args: - result: CommandResult object containing result to write - keep_outputs: True to store the output binaries, False - to delete them - """ - # Fatal error - if result.return_code < 0: - return - - # Aborted? - if result.stderr and 'No child processes' in result.stderr: - return - - if result.already_done: - return - - # Write the output and stderr - output_dir = self.builder._GetOutputDir(result.commit_upto) - Mkdir(output_dir) - build_dir = self.builder.GetBuildDir(result.commit_upto, - result.brd.target) - Mkdir(build_dir) - - outfile = os.path.join(build_dir, 'log') - with open(outfile, 'w') as fd: - if result.stdout: - fd.write(result.stdout) - - errfile = self.builder.GetErrFile(result.commit_upto, - result.brd.target) - if result.stderr: - with open(errfile, 'w') as fd: - fd.write(result.stderr) - elif os.path.exists(errfile): - os.remove(errfile) - - if result.toolchain: - # Write the build result and toolchain information. - done_file = self.builder.GetDoneFile(result.commit_upto, - result.brd.target) - with open(done_file, 'w') as fd: - fd.write('%s' % result.return_code) - with open(os.path.join(build_dir, 'toolchain'), 'w') as fd: - print >>fd, 'gcc', result.toolchain.gcc - print >>fd, 'path', result.toolchain.path - print >>fd, 'cross', result.toolchain.cross - print >>fd, 'arch', result.toolchain.arch - fd.write('%s' % result.return_code) - - with open(os.path.join(build_dir, 'toolchain'), 'w') as fd: - print >>fd, 'gcc', result.toolchain.gcc - print >>fd, 'path', result.toolchain.path - - # Write out the image and function size information and an objdump - env = result.toolchain.MakeEnvironment() - lines = [] - for fname in ['u-boot', 'spl/u-boot-spl']: - cmd = ['%snm' % self.toolchain.cross, '--size-sort', fname] - nm_result = command.RunPipe([cmd], capture=True, - capture_stderr=True, cwd=result.out_dir, - raise_on_error=False, env=env) - if nm_result.stdout: - nm = self.builder.GetFuncSizesFile(result.commit_upto, - result.brd.target, fname) - with open(nm, 'w') as fd: - print >>fd, nm_result.stdout, - - cmd = ['%sobjdump' % self.toolchain.cross, '-h', fname] - dump_result = command.RunPipe([cmd], capture=True, - capture_stderr=True, cwd=result.out_dir, - raise_on_error=False, env=env) - rodata_size = '' - if dump_result.stdout: - objdump = self.builder.GetObjdumpFile(result.commit_upto, - result.brd.target, fname) - with open(objdump, 'w') as fd: - print >>fd, dump_result.stdout, - for line in dump_result.stdout.splitlines(): - fields = line.split() - if len(fields) > 5 and fields[1] == '.rodata': - rodata_size = fields[2] - - cmd = ['%ssize' % self.toolchain.cross, fname] - size_result = command.RunPipe([cmd], capture=True, - capture_stderr=True, cwd=result.out_dir, - raise_on_error=False, env=env) - if size_result.stdout: - lines.append(size_result.stdout.splitlines()[1] + ' ' + - rodata_size) - - # Write out the image sizes file. This is similar to the output - # of binutil's 'size' utility, but it omits the header line and - # adds an additional hex value at the end of each line for the - # rodata size - if len(lines): - sizes = self.builder.GetSizesFile(result.commit_upto, - result.brd.target) - with open(sizes, 'w') as fd: - print >>fd, '\n'.join(lines) - - # Now write the actual build output - if keep_outputs: - patterns = ['u-boot', '*.bin', 'u-boot.dtb', '*.map', - 'include/autoconf.mk', 'spl/u-boot-spl', - 'spl/u-boot-spl.bin'] - for pattern in patterns: - file_list = glob.glob(os.path.join(result.out_dir, pattern)) - for fname in file_list: - shutil.copy(fname, build_dir) - - - def RunJob(self, job): - """Run a single job - - A job consists of a building a list of commits for a particular board. - - Args: - job: Job to build - """ - brd = job.board - work_dir = self.builder.GetThreadDir(self.thread_num) - self.toolchain = None - if job.commits: - # Run 'make board_defconfig' on the first commit - do_config = True - commit_upto = 0 - force_build = False - for commit_upto in range(0, len(job.commits), job.step): - result, request_config = self.RunCommit(commit_upto, brd, - work_dir, do_config, - force_build or self.builder.force_build, - self.builder.force_build_failures) - failed = result.return_code or result.stderr - did_config = do_config - if failed and not do_config: - # If our incremental build failed, try building again - # with a reconfig. - if self.builder.force_config_on_failure: - result, request_config = self.RunCommit(commit_upto, - brd, work_dir, True, True, False) - did_config = True - if not self.builder.force_reconfig: - do_config = request_config - - # If we built that commit, then config is done. But if we got - # an warning, reconfig next time to force it to build the same - # files that created warnings this time. Otherwise an - # incremental build may not build the same file, and we will - # think that the warning has gone away. - # We could avoid this by using -Werror everywhere... - # For errors, the problem doesn't happen, since presumably - # the build stopped and didn't generate output, so will retry - # that file next time. So we could detect warnings and deal - # with them specially here. For now, we just reconfigure if - # anything goes work. - # Of course this is substantially slower if there are build - # errors/warnings (e.g. 2-3x slower even if only 10% of builds - # have problems). - if (failed and not result.already_done and not did_config and - self.builder.force_config_on_failure): - # If this build failed, try the next one with a - # reconfigure. - # Sometimes if the board_config.h file changes it can mess - # with dependencies, and we get: - # make: *** No rule to make target `include/autoconf.mk', - # needed by `depend'. - do_config = True - force_build = True - else: - force_build = False - if self.builder.force_config_on_failure: - if failed: - do_config = True - result.commit_upto = commit_upto - if result.return_code < 0: - raise ValueError('Interrupt') - - # We have the build results, so output the result - self._WriteResult(result, job.keep_outputs) - self.builder.out_queue.put(result) - else: - # Just build the currently checked-out build - result = self.RunCommit(None, True) - result.commit_upto = self.builder.upto - self.builder.out_queue.put(result) - - def run(self): - """Our thread's run function - - This thread picks a job from the queue, runs it, and then goes to the - next job. - """ - alive = True - while True: - job = self.builder.queue.get() - try: - if self.builder.active and alive: - self.RunJob(job) - except Exception as err: - alive = False - print err - self.builder.queue.task_done() - - class Builder: """Class for building U-Boot for a particular commit. @@ -614,19 +210,20 @@ class Builder: self.force_reconfig = False self._step = step self.in_tree = False + self._error_lines = 0 self.col = terminal.Color() self.queue = Queue.Queue() self.out_queue = Queue.Queue() for i in range(self.num_threads): - t = BuilderThread(self, i) + t = builderthread.BuilderThread(self, i) t.setDaemon(True) t.start() self.threads.append(t) self.last_line_len = 0 - t = ResultThread(self) + t = builderthread.ResultThread(self) t.setDaemon(True) t.start() self.threads.append(t) @@ -639,6 +236,20 @@ class Builder: for t in self.threads: del t + def SetDisplayOptions(self, show_errors=False, show_sizes=False, + show_detail=False, show_bloat=False): + """Setup display options for the builder. + + show_errors: True to show summarised error/warning info + show_sizes: Show size deltas + show_detail: Show detail for each board + show_bloat: Show detail for each function + """ + self._show_errors = show_errors + self._show_sizes = show_sizes + self._show_detail = show_detail + self._show_bloat = show_bloat + def _AddTimestamp(self): """Add a new timestamp to the list and record the build period. @@ -697,7 +308,7 @@ class Builder: Args: commit: Commit object that is being built brd: Board object that is being built - stage: Stage that we are at (distclean, config, build) + stage: Stage that we are at (mrproper, config, build) cwd: Directory where make should be run args: Arguments to pass to make kwargs: Arguments to pass to command.RunPipe() @@ -711,7 +322,8 @@ class Builder: """Process the result of a build, showing progress information Args: - result: A CommandResult object + result: A CommandResult object, which indicates the result for + a single build """ col = terminal.Color() if result: @@ -729,6 +341,13 @@ class Builder: self.warned += 1 if result.already_done: self.already_done += 1 + if self._verbose: + print '\r', + self.ClearLine(0) + boards_selected = {target : result.brd} + self.ResetResultSummary(boards_selected) + self.ProduceResultSummary(result.commit_upto, self.commits, + boards_selected) else: target = '(starting)' @@ -752,7 +371,7 @@ class Builder: name += target print line + name, - length = 13 + len(name) + length = 14 + len(name) self.ClearLine(length) def _GetOutputDir(self, commit_upto): @@ -763,10 +382,13 @@ class Builder: Args: commit_upto: Commit number to use (0..self.count-1) """ - commit = self.commits[commit_upto] - subject = commit.subject.translate(trans_valid_chars) - commit_dir = ('%02d_of_%02d_g%s_%s' % (commit_upto + 1, - self.commit_count, commit.hash, subject[:20])) + if self.commits: + commit = self.commits[commit_upto] + subject = commit.subject.translate(trans_valid_chars) + commit_dir = ('%02d_of_%02d_g%s_%s' % (commit_upto + 1, + self.commit_count, commit.hash, subject[:20])) + else: + commit_dir = 'current' output_dir = os.path.join(self.base_dir, commit_dir) return output_dir @@ -1270,10 +892,13 @@ class Builder: self.col.MAGENTA) for arch, target_list in arch_list.iteritems(): print '%10s: %s' % (arch, target_list) + self._error_lines += 1 if better_err: print self.col.Color(self.col.GREEN, '\n'.join(better_err)) + self._error_lines += 1 if worse_err: print self.col.Color(self.col.RED, '\n'.join(worse_err)) + self._error_lines += 1 if show_sizes: self.PrintSizeSummary(board_selected, board_dict, show_detail, @@ -1292,9 +917,18 @@ class Builder: print "Boards not built (%d): %s" % (len(not_built), ', '.join(not_built)) + def ProduceResultSummary(self, commit_upto, commits, board_selected): + board_dict, err_lines = self.GetResultSummary(board_selected, + commit_upto, read_func_sizes=self._show_bloat) + if commits: + msg = '%02d: %s' % (commit_upto + 1, + commits[commit_upto].subject) + print self.col.Color(self.col.BLUE, msg) + self.PrintResultSummary(board_selected, board_dict, + err_lines if self._show_errors else [], + self._show_sizes, self._show_detail, self._show_bloat) - def ShowSummary(self, commits, board_selected, show_errors, show_sizes, - show_detail, show_bloat): + def ShowSummary(self, commits, board_selected): """Show a build summary for U-Boot for a given board list. Reset the result summary, then repeatedly call GetResultSummary on @@ -1303,23 +937,16 @@ class Builder: Args: commit: Commit objects to summarise board_selected: Dict containing boards to summarise - show_errors: Show errors that occured - show_sizes: Show size deltas - show_detail: Show detail for each board - show_bloat: Show detail for each function """ - self.commit_count = len(commits) + self.commit_count = len(commits) if commits else 1 self.commits = commits self.ResetResultSummary(board_selected) + self._error_lines = 0 for commit_upto in range(0, self.commit_count, self._step): - board_dict, err_lines = self.GetResultSummary(board_selected, - commit_upto, read_func_sizes=show_bloat) - msg = '%02d: %s' % (commit_upto + 1, commits[commit_upto].subject) - print self.col.Color(self.col.BLUE, msg) - self.PrintResultSummary(board_selected, board_dict, - err_lines if show_errors else [], show_sizes, show_detail, - show_bloat) + self.ProduceResultSummary(commit_upto, commits, board_selected) + if not self._error_lines: + print self.col.Color(self.col.GREEN, '(no errors to report)') def SetupBuild(self, board_selected, commits): @@ -1330,45 +957,11 @@ class Builder: commits: Selected commits to build """ # First work out how many commits we will build - count = (len(commits) + self._step - 1) / self._step + count = (self.commit_count + self._step - 1) / self._step self.count = len(board_selected) * count self.upto = self.warned = self.fail = 0 self._timestamps = collections.deque() - def BuildBoardsForCommit(self, board_selected, keep_outputs): - """Build all boards for a single commit""" - self.SetupBuild(board_selected) - self.count = len(board_selected) - for brd in board_selected.itervalues(): - job = BuilderJob() - job.board = brd - job.commits = None - job.keep_outputs = keep_outputs - self.queue.put(brd) - - self.queue.join() - self.out_queue.join() - print - self.ClearLine(0) - - def BuildCommits(self, commits, board_selected, show_errors, keep_outputs): - """Build all boards for all commits (non-incremental)""" - self.commit_count = len(commits) - - self.ResetResultSummary(board_selected) - for self.commit_upto in range(self.commit_count): - self.SelectCommit(commits[self.commit_upto]) - self.SelectOutputDir() - Mkdir(self.output_dir) - - self.BuildBoardsForCommit(board_selected, keep_outputs) - board_dict, err_lines = self.GetResultSummary() - self.PrintResultSummary(board_selected, board_dict, - err_lines if show_errors else []) - - if self.already_done: - print '%d builds already done' % self.already_done - def GetThreadDir(self, thread_num): """Get the directory path to the working dir for a thread. @@ -1377,22 +970,23 @@ class Builder: """ return os.path.join(self._working_dir, '%02d' % thread_num) - def _PrepareThread(self, thread_num): + def _PrepareThread(self, thread_num, setup_git): """Prepare the working directory for a thread. This clones or fetches the repo into the thread's work directory. Args: thread_num: Thread number (0, 1, ...) + setup_git: True to set up a git repo clone """ thread_dir = self.GetThreadDir(thread_num) - Mkdir(thread_dir) + builderthread.Mkdir(thread_dir) git_dir = os.path.join(thread_dir, '.git') # Clone the repo if it doesn't already exist # TODO(sjg@chromium): Perhaps some git hackery to symlink instead, so # we have a private index but uses the origin repo's contents? - if self.git_dir: + if setup_git and self.git_dir: src_dir = os.path.abspath(self.git_dir) if os.path.exists(git_dir): gitutil.Fetch(git_dir, thread_dir) @@ -1400,17 +994,18 @@ class Builder: print 'Cloning repo for thread %d' % thread_num gitutil.Clone(src_dir, thread_dir) - def _PrepareWorkingSpace(self, max_threads): + def _PrepareWorkingSpace(self, max_threads, setup_git): """Prepare the working directory for use. Set up the git repo for each thread. Args: max_threads: Maximum number of threads we expect to need. + setup_git: True to set up a git repo clone """ - Mkdir(self._working_dir) + builderthread.Mkdir(self._working_dir) for thread in range(max_threads): - self._PrepareThread(thread) + self._PrepareThread(thread, setup_git) def _PrepareOutputSpace(self): """Get the output directories ready to receive files. @@ -1427,29 +1022,31 @@ class Builder: if dirname not in dir_list: shutil.rmtree(dirname) - def BuildBoards(self, commits, board_selected, show_errors, keep_outputs): + def BuildBoards(self, commits, board_selected, keep_outputs, verbose): """Build all commits for a list of boards Args: commits: List of commits to be build, each a Commit object boards_selected: Dict of selected boards, key is target name, value is Board object - show_errors: True to show summarised error/warning info keep_outputs: True to save build output files + verbose: Display build results as they are completed """ - self.commit_count = len(commits) + self.commit_count = len(commits) if commits else 1 self.commits = commits + self._verbose = verbose self.ResetResultSummary(board_selected) - Mkdir(self.base_dir) - self._PrepareWorkingSpace(min(self.num_threads, len(board_selected))) + builderthread.Mkdir(self.base_dir) + self._PrepareWorkingSpace(min(self.num_threads, len(board_selected)), + commits is not None) self._PrepareOutputSpace() self.SetupBuild(board_selected, commits) self.ProcessResult(None) # Create jobs to build all commits for each board for brd in board_selected.itervalues(): - job = BuilderJob() + job = builderthread.BuilderJob() job.board = brd job.commits = commits job.keep_outputs = keep_outputs diff --git a/tools/buildman/builderthread.py b/tools/buildman/builderthread.py new file mode 100644 index 0000000..8214662 --- /dev/null +++ b/tools/buildman/builderthread.py @@ -0,0 +1,434 @@ +# Copyright (c) 2014 Google, Inc +# +# SPDX-License-Identifier: GPL-2.0+ +# + +import errno +import glob +import os +import shutil +import threading + +import command +import gitutil + +def Mkdir(dirname): + """Make a directory if it doesn't already exist. + + Args: + dirname: Directory to create + """ + try: + os.mkdir(dirname) + except OSError as err: + if err.errno == errno.EEXIST: + pass + else: + raise + +class BuilderJob: + """Holds information about a job to be performed by a thread + + Members: + board: Board object to build + commits: List of commit options to build. + """ + def __init__(self): + self.board = None + self.commits = [] + + +class ResultThread(threading.Thread): + """This thread processes results from builder threads. + + It simply passes the results on to the builder. There is only one + result thread, and this helps to serialise the build output. + """ + def __init__(self, builder): + """Set up a new result thread + + Args: + builder: Builder which will be sent each result + """ + threading.Thread.__init__(self) + self.builder = builder + + def run(self): + """Called to start up the result thread. + + We collect the next result job and pass it on to the build. + """ + while True: + result = self.builder.out_queue.get() + self.builder.ProcessResult(result) + self.builder.out_queue.task_done() + + +class BuilderThread(threading.Thread): + """This thread builds U-Boot for a particular board. + + An input queue provides each new job. We run 'make' to build U-Boot + and then pass the results on to the output queue. + + Members: + builder: The builder which contains information we might need + thread_num: Our thread number (0-n-1), used to decide on a + temporary directory + """ + def __init__(self, builder, thread_num): + """Set up a new builder thread""" + threading.Thread.__init__(self) + self.builder = builder + self.thread_num = thread_num + + def Make(self, commit, brd, stage, cwd, *args, **kwargs): + """Run 'make' on a particular commit and board. + + The source code will already be checked out, so the 'commit' + argument is only for information. + + Args: + commit: Commit object that is being built + brd: Board object that is being built + stage: Stage of the build. Valid stages are: + mrproper - can be called to clean source + config - called to configure for a board + build - the main make invocation - it does the build + args: A list of arguments to pass to 'make' + kwargs: A list of keyword arguments to pass to command.RunPipe() + + Returns: + CommandResult object + """ + return self.builder.do_make(commit, brd, stage, cwd, *args, + **kwargs) + + def RunCommit(self, commit_upto, brd, work_dir, do_config, force_build, + force_build_failures): + """Build a particular commit. + + If the build is already done, and we are not forcing a build, we skip + the build and just return the previously-saved results. + + Args: + commit_upto: Commit number to build (0...n-1) + brd: Board object to build + work_dir: Directory to which the source will be checked out + do_config: True to run a make <board>_defconfig on the source + force_build: Force a build even if one was previously done + force_build_failures: Force a bulid if the previous result showed + failure + + Returns: + tuple containing: + - CommandResult object containing the results of the build + - boolean indicating whether 'make config' is still needed + """ + # Create a default result - it will be overwritte by the call to + # self.Make() below, in the event that we do a build. + result = command.CommandResult() + result.return_code = 0 + if self.builder.in_tree: + out_dir = work_dir + else: + out_dir = os.path.join(work_dir, 'build') + + # Check if the job was already completed last time + done_file = self.builder.GetDoneFile(commit_upto, brd.target) + result.already_done = os.path.exists(done_file) + will_build = (force_build or force_build_failures or + not result.already_done) + if result.already_done and will_build: + # Get the return code from that build and use it + with open(done_file, 'r') as fd: + result.return_code = int(fd.readline()) + err_file = self.builder.GetErrFile(commit_upto, brd.target) + if os.path.exists(err_file) and os.stat(err_file).st_size: + result.stderr = 'bad' + elif not force_build: + # The build passed, so no need to build it again + will_build = False + + if will_build: + # We are going to have to build it. First, get a toolchain + if not self.toolchain: + try: + self.toolchain = self.builder.toolchains.Select(brd.arch) + except ValueError as err: + result.return_code = 10 + result.stdout = '' + result.stderr = str(err) + # TODO(sjg@chromium.org): This gets swallowed, but needs + # to be reported. + + if self.toolchain: + # Checkout the right commit + if self.builder.commits: + commit = self.builder.commits[commit_upto] + if self.builder.checkout: + git_dir = os.path.join(work_dir, '.git') + gitutil.Checkout(commit.hash, git_dir, work_dir, + force=True) + else: + commit = 'current' + + # Set up the environment and command line + env = self.toolchain.MakeEnvironment() + Mkdir(out_dir) + args = [] + cwd = work_dir + if not self.builder.in_tree: + if commit_upto is None: + # In this case we are building in the original source + # directory (i.e. the current directory where buildman + # is invoked. The output directory is set to this + # thread's selected work directory. + # + # Symlinks can confuse U-Boot's Makefile since + # we may use '..' in our path, so remove them. + work_dir = os.path.realpath(work_dir) + args.append('O=%s/build' % work_dir) + cwd = None + else: + args.append('O=build') + args.append('-s') + if self.builder.num_jobs is not None: + args.extend(['-j', str(self.builder.num_jobs)]) + config_args = ['%s_defconfig' % brd.target] + config_out = '' + args.extend(self.builder.toolchains.GetMakeArguments(brd)) + + # If we need to reconfigure, do that now + if do_config: + result = self.Make(commit, brd, 'mrproper', cwd, + 'mrproper', *args, env=env) + result = self.Make(commit, brd, 'config', cwd, + *(args + config_args), env=env) + config_out = result.combined + do_config = False # No need to configure next time + if result.return_code == 0: + result = self.Make(commit, brd, 'build', cwd, *args, + env=env) + result.stdout = config_out + result.stdout + else: + result.return_code = 1 + result.stderr = 'No tool chain for %s\n' % brd.arch + result.already_done = False + + result.toolchain = self.toolchain + result.brd = brd + result.commit_upto = commit_upto + result.out_dir = out_dir + return result, do_config + + def _WriteResult(self, result, keep_outputs): + """Write a built result to the output directory. + + Args: + result: CommandResult object containing result to write + keep_outputs: True to store the output binaries, False + to delete them + """ + # Fatal error + if result.return_code < 0: + return + + # Aborted? + if result.stderr and 'No child processes' in result.stderr: + return + + if result.already_done: + return + + # Write the output and stderr + output_dir = self.builder._GetOutputDir(result.commit_upto) + Mkdir(output_dir) + build_dir = self.builder.GetBuildDir(result.commit_upto, + result.brd.target) + Mkdir(build_dir) + + outfile = os.path.join(build_dir, 'log') + with open(outfile, 'w') as fd: + if result.stdout: + fd.write(result.stdout) + + errfile = self.builder.GetErrFile(result.commit_upto, + result.brd.target) + if result.stderr: + with open(errfile, 'w') as fd: + fd.write(result.stderr) + elif os.path.exists(errfile): + os.remove(errfile) + + if result.toolchain: + # Write the build result and toolchain information. + done_file = self.builder.GetDoneFile(result.commit_upto, + result.brd.target) + with open(done_file, 'w') as fd: + fd.write('%s' % result.return_code) + with open(os.path.join(build_dir, 'toolchain'), 'w') as fd: + print >>fd, 'gcc', result.toolchain.gcc + print >>fd, 'path', result.toolchain.path + print >>fd, 'cross', result.toolchain.cross + print >>fd, 'arch', result.toolchain.arch + fd.write('%s' % result.return_code) + + with open(os.path.join(build_dir, 'toolchain'), 'w') as fd: + print >>fd, 'gcc', result.toolchain.gcc + print >>fd, 'path', result.toolchain.path + + # Write out the image and function size information and an objdump + env = result.toolchain.MakeEnvironment() + lines = [] + for fname in ['u-boot', 'spl/u-boot-spl']: + cmd = ['%snm' % self.toolchain.cross, '--size-sort', fname] + nm_result = command.RunPipe([cmd], capture=True, + capture_stderr=True, cwd=result.out_dir, + raise_on_error=False, env=env) + if nm_result.stdout: + nm = self.builder.GetFuncSizesFile(result.commit_upto, + result.brd.target, fname) + with open(nm, 'w') as fd: + print >>fd, nm_result.stdout, + + cmd = ['%sobjdump' % self.toolchain.cross, '-h', fname] + dump_result = command.RunPipe([cmd], capture=True, + capture_stderr=True, cwd=result.out_dir, + raise_on_error=False, env=env) + rodata_size = '' + if dump_result.stdout: + objdump = self.builder.GetObjdumpFile(result.commit_upto, + result.brd.target, fname) + with open(objdump, 'w') as fd: + print >>fd, dump_result.stdout, + for line in dump_result.stdout.splitlines(): + fields = line.split() + if len(fields) > 5 and fields[1] == '.rodata': + rodata_size = fields[2] + + cmd = ['%ssize' % self.toolchain.cross, fname] + size_result = command.RunPipe([cmd], capture=True, + capture_stderr=True, cwd=result.out_dir, + raise_on_error=False, env=env) + if size_result.stdout: + lines.append(size_result.stdout.splitlines()[1] + ' ' + + rodata_size) + + # Write out the image sizes file. This is similar to the output + # of binutil's 'size' utility, but it omits the header line and + # adds an additional hex value at the end of each line for the + # rodata size + if len(lines): + sizes = self.builder.GetSizesFile(result.commit_upto, + result.brd.target) + with open(sizes, 'w') as fd: + print >>fd, '\n'.join(lines) + + # Now write the actual build output + if keep_outputs: + patterns = ['u-boot', '*.bin', 'u-boot.dtb', '*.map', + 'include/autoconf.mk', 'spl/u-boot-spl', + 'spl/u-boot-spl.bin'] + for pattern in patterns: + file_list = glob.glob(os.path.join(result.out_dir, pattern)) + for fname in file_list: + shutil.copy(fname, build_dir) + + + def RunJob(self, job): + """Run a single job + + A job consists of a building a list of commits for a particular board. + + Args: + job: Job to build + """ + brd = job.board + work_dir = self.builder.GetThreadDir(self.thread_num) + self.toolchain = None + if job.commits: + # Run 'make board_defconfig' on the first commit + do_config = True + commit_upto = 0 + force_build = False + for commit_upto in range(0, len(job.commits), job.step): + result, request_config = self.RunCommit(commit_upto, brd, + work_dir, do_config, + force_build or self.builder.force_build, + self.builder.force_build_failures) + failed = result.return_code or result.stderr + did_config = do_config + if failed and not do_config: + # If our incremental build failed, try building again + # with a reconfig. + if self.builder.force_config_on_failure: + result, request_config = self.RunCommit(commit_upto, + brd, work_dir, True, True, False) + did_config = True + if not self.builder.force_reconfig: + do_config = request_config + + # If we built that commit, then config is done. But if we got + # an warning, reconfig next time to force it to build the same + # files that created warnings this time. Otherwise an + # incremental build may not build the same file, and we will + # think that the warning has gone away. + # We could avoid this by using -Werror everywhere... + # For errors, the problem doesn't happen, since presumably + # the build stopped and didn't generate output, so will retry + # that file next time. So we could detect warnings and deal + # with them specially here. For now, we just reconfigure if + # anything goes work. + # Of course this is substantially slower if there are build + # errors/warnings (e.g. 2-3x slower even if only 10% of builds + # have problems). + if (failed and not result.already_done and not did_config and + self.builder.force_config_on_failure): + # If this build failed, try the next one with a + # reconfigure. + # Sometimes if the board_config.h file changes it can mess + # with dependencies, and we get: + # make: *** No rule to make target `include/autoconf.mk', + # needed by `depend'. + do_config = True + force_build = True + else: + force_build = False + if self.builder.force_config_on_failure: + if failed: + do_config = True + result.commit_upto = commit_upto + if result.return_code < 0: + raise ValueError('Interrupt') + + # We have the build results, so output the result + self._WriteResult(result, job.keep_outputs) + self.builder.out_queue.put(result) + else: + # Just build the currently checked-out build + result, request_config = self.RunCommit(None, brd, work_dir, True, + True, self.builder.force_build_failures) + result.commit_upto = 0 + self._WriteResult(result, job.keep_outputs) + self.builder.out_queue.put(result) + + def run(self): + """Our thread's run function + + This thread picks a job from the queue, runs it, and then goes to the + next job. + """ + alive = True + while True: + job = self.builder.queue.get() + if self.builder.active and alive: + self.RunJob(job) + ''' + try: + if self.builder.active and alive: + self.RunJob(job) + except Exception as err: + alive = False + print err + ''' + self.builder.queue.task_done() diff --git a/tools/buildman/buildman.py b/tools/buildman/buildman.py index 42847ac..e18859b 100755 --- a/tools/buildman/buildman.py +++ b/tools/buildman/buildman.py @@ -70,6 +70,9 @@ parser.add_option('-c', '--count', dest='count', type='int', parser.add_option('-C', '--force-reconfig', dest='force_reconfig', action='store_true', default=False, help='Reconfigure for every commit (disable incremental build)') +parser.add_option('-d', '--detail', dest='show_detail', + action='store_true', default=False, + help='Show detailed information for each board in summary') parser.add_option('-e', '--show_errors', action='store_true', default=False, help='Show errors and warnings') parser.add_option('-f', '--force-build', dest='force_build', @@ -78,11 +81,10 @@ parser.add_option('-f', '--force-build', dest='force_build', parser.add_option('-F', '--force-build-failures', dest='force_build_failures', action='store_true', default=False, help='Force build of previously-failed build') -parser.add_option('-d', '--detail', dest='show_detail', - action='store_true', default=False, - help='Show detailed information for each board in summary') parser.add_option('-g', '--git', type='string', help='Git repo containing branch to build', default='.') +parser.add_option('-G', '--config-file', type='string', + help='Path to buildman config file', default='') parser.add_option('-H', '--full-help', action='store_true', dest='full_help', default=False, help='Display the README file') parser.add_option('-i', '--in-tree', dest='in_tree', @@ -96,6 +98,9 @@ parser.add_option('--list-tool-chains', action='store_true', default=False, help='List available tool chains') parser.add_option('-n', '--dry-run', action='store_true', dest='dry_run', default=False, help="Do a try run (describe actions, but no nothing)") +parser.add_option('-o', '--output-dir', type='string', + dest='output_dir', default='..', + help='Directory where all builds happen and buildman has its workspace (default is ../)') parser.add_option('-Q', '--quick', action='store_true', default=False, help='Do a rough build, with limited warning resolution') parser.add_option('-s', '--summary', action='store_true', @@ -110,11 +115,10 @@ parser.add_option('-T', '--threads', type='int', default=None, help='Number of builder threads to use') parser.add_option('-u', '--show_unknown', action='store_true', default=False, help='Show boards with unknown build result') -parser.add_option('-o', '--output-dir', type='string', - dest='output_dir', default='..', - help='Directory where all builds happen and buildman has its workspace (default is ../)') +parser.add_option('-v', '--verbose', action='store_true', + default=False, help='Show build results while the build progresses') -parser.usage = """buildman -b <branch> [options] +parser.usage += """ Build U-Boot for all commits in a branch. Use -n to do a dry run""" diff --git a/tools/buildman/control.py b/tools/buildman/control.py index 75b6498..68ea961 100644 --- a/tools/buildman/control.py +++ b/tools/buildman/control.py @@ -21,15 +21,20 @@ def GetPlural(count): """Returns a plural 's' if count is not 1""" return 's' if count != 1 else '' -def GetActionSummary(is_summary, count, selected, options): +def GetActionSummary(is_summary, commits, selected, options): """Return a string summarising the intended action. Returns: Summary string. """ - count = (count + options.step - 1) / options.step - str = '%s %d commit%s for %d boards' % ( - 'Summary of' if is_summary else 'Building', count, GetPlural(count), + if commits: + count = len(commits) + count = (count + options.step - 1) / options.step + commit_str = '%d commit%s' % (count, GetPlural(count)) + else: + commit_str = 'current source' + str = '%s %s for %d boards' % ( + 'Summary of' if is_summary else 'Building', commit_str, len(selected)) str += ' (%d thread%s, %d job%s per thread)' % (options.threads, GetPlural(options.threads), options.jobs, GetPlural(options.jobs)) @@ -53,13 +58,18 @@ def ShowActions(series, why_selected, boards_selected, builder, options): col = terminal.Color() print 'Dry run, so not doing much. But I would do this:' print - print GetActionSummary(False, len(series.commits), boards_selected, + if series: + commits = series.commits + else: + commits = None + print GetActionSummary(False, commits, boards_selected, options) print 'Build directory: %s' % builder.base_dir - for upto in range(0, len(series.commits), options.step): - commit = series.commits[upto] - print ' ', col.Color(col.YELLOW, commit.hash, bright=False), - print commit.subject + if commits: + for upto in range(0, len(series.commits), options.step): + commit = series.commits[upto] + print ' ', col.Color(col.YELLOW, commit.hash, bright=False), + print commit.subject print for arg in why_selected: if arg != 'all': @@ -76,7 +86,7 @@ def DoBuildman(options, args): """ gitutil.Setup() - bsettings.Setup() + bsettings.Setup(options.config_file) options.git_dir = os.path.join(options.git, '.git') toolchains = toolchain.Toolchains() @@ -93,56 +103,64 @@ def DoBuildman(options, args): count = options.count if count == -1: if not options.branch: - str = 'Please use -b to specify a branch to build' - print col.Color(col.RED, str) - sys.exit(1) - count = gitutil.CountCommitsInBranch(options.git_dir, options.branch) - if count is None: - str = "Branch '%s' not found or has no upstream" % options.branch - print col.Color(col.RED, str) - sys.exit(1) - count += 1 # Build upstream commit also + count = 1 + else: + count = gitutil.CountCommitsInBranch(options.git_dir, + options.branch) + if count is None: + str = ("Branch '%s' not found or has no upstream" % + options.branch) + sys.exit(col.Color(col.RED, str)) + count += 1 # Build upstream commit also if not count: str = ("No commits found to process in branch '%s': " "set branch's upstream or use -c flag" % options.branch) - print col.Color(col.RED, str) - sys.exit(1) + sys.exit(col.Color(col.RED, str)) # Work out what subset of the boards we are building board_file = os.path.join(options.git, 'boards.cfg') - if not os.path.exists(board_file): - print 'Could not find %s' % board_file - status = subprocess.call([os.path.join(options.git, - 'tools/genboardscfg.py')]) - if status != 0: - print >> sys.stderr, "Failed to generate boards.cfg" - sys.exit(1) + status = subprocess.call([os.path.join(options.git, + 'tools/genboardscfg.py')]) + if status != 0: + sys.exit("Failed to generate boards.cfg") boards = board.Boards() boards.ReadBoards(os.path.join(options.git, 'boards.cfg')) why_selected = boards.SelectBoards(args) selected = boards.GetSelected() if not len(selected): - print col.Color(col.RED, 'No matching boards found') - sys.exit(1) + sys.exit(col.Color(col.RED, 'No matching boards found')) # Read the metadata from the commits. First look at the upstream commit, # then the ones in the branch. We would like to do something like # upstream/master~..branch but that isn't possible if upstream/master is # a merge commit (it will list all the commits that form part of the # merge) - range_expr = gitutil.GetRangeInBranch(options.git_dir, options.branch) - upstream_commit = gitutil.GetUpstream(options.git_dir, options.branch) - series = patchstream.GetMetaDataForList(upstream_commit, options.git_dir, - 1) - # Conflicting tags are not a problem for buildman, since it does not use - # them. For example, Series-version is not useful for buildman. On the - # other hand conflicting tags will cause an error. So allow later tags - # to overwrite earlier ones. - series.allow_overwrite = True - series = patchstream.GetMetaDataForList(range_expr, options.git_dir, None, - series) + if options.branch: + if count == -1: + range_expr = gitutil.GetRangeInBranch(options.git_dir, + options.branch) + upstream_commit = gitutil.GetUpstream(options.git_dir, + options.branch) + series = patchstream.GetMetaDataForList(upstream_commit, + options.git_dir, 1) + + # Conflicting tags are not a problem for buildman, since it does + # not use them. For example, Series-version is not useful for + # buildman. On the other hand conflicting tags will cause an + # error. So allow later tags to overwrite earlier ones. + series.allow_overwrite = True + series = patchstream.GetMetaDataForList(range_expr, + options.git_dir, None, series) + else: + # Honour the count + series = patchstream.GetMetaDataForList(options.branch, + options.git_dir, count) + else: + series = None + options.verbose = True + options.show_errors = True # By default we have one thread per CPU. But if there are not enough jobs # we can have fewer threads and use a high '-j' value for make. @@ -158,11 +176,14 @@ def DoBuildman(options, args): gnu_make = command.Output(os.path.join(options.git, 'scripts/show-gnu-make')).rstrip() if not gnu_make: - print >> sys.stderr, 'GNU Make not found' - sys.exit(1) + sys.exit('GNU Make not found') # Create a new builder with the selected options - output_dir = os.path.join(options.output_dir, options.branch) + if options.branch: + dirname = options.branch + else: + dirname = 'current' + output_dir = os.path.join(options.output_dir, dirname) builder = Builder(toolchains, output_dir, options.git_dir, options.threads, options.jobs, gnu_make=gnu_make, checkout=True, show_unknown=options.show_unknown, step=options.step) @@ -180,15 +201,21 @@ def DoBuildman(options, args): # Work out which boards to build board_selected = boards.GetSelectedDict() - print GetActionSummary(options.summary, count, board_selected, options) + if series: + commits = series.commits + else: + commits = None + + print GetActionSummary(options.summary, commits, board_selected, + options) + builder.SetDisplayOptions(options.show_errors, options.show_sizes, + options.show_detail, options.show_bloat) if options.summary: # We can't show function sizes without board details at present if options.show_bloat: options.show_detail = True - builder.ShowSummary(series.commits, board_selected, - options.show_errors, options.show_sizes, - options.show_detail, options.show_bloat) + builder.ShowSummary(commits, board_selected) else: - builder.BuildBoards(series.commits, board_selected, - options.show_errors, options.keep_outputs) + builder.BuildBoards(commits, board_selected, + options.keep_outputs, options.verbose) diff --git a/tools/buildman/test.py b/tools/buildman/test.py index 068784a..a51c942 100644 --- a/tools/buildman/test.py +++ b/tools/buildman/test.py @@ -136,10 +136,10 @@ class TestBuild(unittest.TestCase): build.do_make = self.Make board_selected = self.boards.GetSelectedDict() - #build.BuildCommits(self.commits, board_selected, False) - build.BuildBoards(self.commits, board_selected, False, False) - build.ShowSummary(self.commits, board_selected, True, False, - False, False) + build.BuildBoards(self.commits, board_selected, keep_outputs=False, + verbose=False) + build.SetDisplayOptions(show_errors=True); + build.ShowSummary(self.commits, board_selected) def _testGit(self): """Test basic builder operation by building a branch""" @@ -165,5 +165,53 @@ class TestBuild(unittest.TestCase): args = ['tegra20'] control.DoBuildman(options, args) + def testBoardSingle(self): + """Test single board selection""" + self.assertEqual(self.boards.SelectBoards(['sandbox']), + {'all': 1, 'sandbox': 1}) + + def testBoardArch(self): + """Test single board selection""" + self.assertEqual(self.boards.SelectBoards(['arm']), + {'all': 2, 'arm': 2}) + + def testBoardArchSingle(self): + """Test single board selection""" + self.assertEqual(self.boards.SelectBoards(['arm sandbox']), + {'all': 3, 'arm': 2, 'sandbox' : 1}) + + def testBoardArchSingleMultiWord(self): + """Test single board selection""" + self.assertEqual(self.boards.SelectBoards(['arm', 'sandbox']), + {'all': 3, 'arm': 2, 'sandbox' : 1}) + + def testBoardSingleAnd(self): + """Test single board selection""" + self.assertEqual(self.boards.SelectBoards(['Tester & arm']), + {'all': 2, 'Tester&arm': 2}) + + def testBoardTwoAnd(self): + """Test single board selection""" + self.assertEqual(self.boards.SelectBoards(['Tester', '&', 'arm', + 'Tester' '&', 'powerpc', + 'sandbox']), + {'all': 5, 'Tester&powerpc': 2, 'Tester&arm': 2, + 'sandbox' : 1}) + + def testBoardAll(self): + """Test single board selection""" + self.assertEqual(self.boards.SelectBoards([]), {'all': 5}) + + def testBoardRegularExpression(self): + """Test single board selection""" + self.assertEqual(self.boards.SelectBoards(['T.*r&^Po']), + {'T.*r&^Po': 2, 'all': 2}) + + def testBoardDuplicate(self): + """Test single board selection""" + self.assertEqual(self.boards.SelectBoards(['sandbox sandbox', + 'sandbox']), + {'all': 1, 'sandbox': 1}) + if __name__ == "__main__": unittest.main() diff --git a/tools/fit_info.c b/tools/fit_info.c index 481ac6d..652abcd 100644 --- a/tools/fit_info.c +++ b/tools/fit_info.c @@ -48,10 +48,11 @@ int main(int argc, char **argv) char *fdtfile = NULL; char *nodename = NULL; char *propertyname = NULL; - char cmdname[50]; + char cmdname[256]; int c; - strcpy(cmdname, *argv); + strncpy(cmdname, *argv, sizeof(cmdname) - 1); + cmdname[sizeof(cmdname) - 1] = '\0'; while ((c = getopt(argc, argv, "f:n:p:")) != -1) switch (c) { case 'f': diff --git a/tools/genboardscfg.py b/tools/genboardscfg.py index 734d90b..e6870f5 100755 --- a/tools/genboardscfg.py +++ b/tools/genboardscfg.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 # # Author: Masahiro Yamada <yamada.m@jp.panasonic.com> # @@ -11,6 +11,8 @@ Converter from Kconfig and MAINTAINERS to boards.cfg Run 'tools/genboardscfg.py' to create boards.cfg file. Run 'tools/genboardscfg.py -h' for available options. + +This script only works on python 2.6 or later, but not python 3.x. """ import errno @@ -30,13 +32,13 @@ CONFIG_DIR = 'configs' REFORMAT_CMD = [os.path.join('tools', 'reformat.py'), '-i', '-d', '-', '-s', '8'] SHOW_GNU_MAKE = 'scripts/show-gnu-make' -SLEEP_TIME=0.03 +SLEEP_TIME=0.003 COMMENT_BLOCK = '''# # List of boards # Automatically generated by %s: don't edit # -# Status, Arch, CPU(:SPLCPU), SoC, Vendor, Board, Target, Options, Maintainers +# Status, Arch, CPU, SoC, Vendor, Board, Target, Options, Maintainers ''' % __file__ @@ -58,8 +60,6 @@ def get_terminal_columns(): try: ret = fcntl.ioctl(sys.stdout.fileno(), termios.TIOCGWINSZ, arg) except IOError as exception: - if exception.errno != errno.ENOTTY: - raise # If 'Inappropriate ioctl for device' error occurs, # stdout is probably redirected. Return 0. return 0 @@ -77,18 +77,62 @@ def check_top_directory(): """Exit if we are not at the top of source directory.""" for f in ('README', 'Licenses'): if not os.path.exists(f): - print >> sys.stderr, 'Please run at the top of source directory.' - sys.exit(1) + sys.exit('Please run at the top of source directory.') def get_make_cmd(): """Get the command name of GNU Make.""" process = subprocess.Popen([SHOW_GNU_MAKE], stdout=subprocess.PIPE) ret = process.communicate() if process.returncode: - print >> sys.stderr, 'GNU Make not found' - sys.exit(1) + sys.exit('GNU Make not found') return ret[0].rstrip() +def output_is_new(): + """Check if the boards.cfg file is up to date. + + Returns: + True if the boards.cfg file exists and is newer than any of + *_defconfig, MAINTAINERS and Kconfig*. False otherwise. + """ + try: + ctime = os.path.getctime(BOARD_FILE) + except OSError as exception: + if exception.errno == errno.ENOENT: + # return False on 'No such file or directory' error + return False + else: + raise + + for (dirpath, dirnames, filenames) in os.walk(CONFIG_DIR): + for filename in fnmatch.filter(filenames, '*_defconfig'): + if fnmatch.fnmatch(filename, '.*'): + continue + filepath = os.path.join(dirpath, filename) + if ctime < os.path.getctime(filepath): + return False + + for (dirpath, dirnames, filenames) in os.walk('.'): + for filename in filenames: + if (fnmatch.fnmatch(filename, '*~') or + not fnmatch.fnmatch(filename, 'Kconfig*') and + not filename == 'MAINTAINERS'): + continue + filepath = os.path.join(dirpath, filename) + if ctime < os.path.getctime(filepath): + return False + + # Detect a board that has been removed since the current boards.cfg + # was generated + with open(BOARD_FILE) as f: + for line in f: + if line[0] == '#' or line == '\n': + continue + defconfig = line.split()[6] + '_defconfig' + if not os.path.exists(os.path.join(CONFIG_DIR, defconfig)): + return False + + return True + ### classes ### class MaintainersDatabase: @@ -104,13 +148,19 @@ class MaintainersDatabase: Returns: Either 'Active' or 'Orphan' """ + if not target in self.database: + print >> sys.stderr, "WARNING: no status info for '%s'" % target + return '-' + tmp = self.database[target][0] if tmp.startswith('Maintained'): return 'Active' elif tmp.startswith('Orphan'): return 'Orphan' else: - print >> sys.stderr, 'Error: %s: unknown status' % tmp + print >> sys.stderr, ("WARNING: %s: unknown status for '%s'" % + (tmp, target)) + return '-' def get_maintainers(self, target): """Return the maintainers of the given board. @@ -118,6 +168,10 @@ class MaintainersDatabase: If the board has two or more maintainers, they are separated with colons. """ + if not target in self.database: + print >> sys.stderr, "WARNING: no maintainers for '%s'" % target + return '' + return ':'.join(self.database[target][1]) def parse_file(self, file): @@ -146,7 +200,7 @@ class MaintainersDatabase: targets.append(front) elif tag == 'S:': status = rest - elif line == '\n' and targets: + elif line == '\n': for target in targets: self.database[target] = (status, maintainers) targets = [] @@ -209,16 +263,15 @@ class DotConfigParser: # sanity check of '.config' file for field in self.must_fields: if not field in fields: - print >> sys.stderr, 'Error: %s is not defined in %s' % \ - (field, defconfig) - sys.exit(1) + print >> sys.stderr, ( + "WARNING: '%s' is not defined in '%s'. Skip." % + (field, defconfig)) + return - # fix-up for aarch64 and tegra + # fix-up for aarch64 if fields['arch'] == 'arm' and 'cpu' in fields: if fields['cpu'] == 'armv8': fields['arch'] = 'aarch64' - if 'soc' in fields and re.match('tegra[0-9]*$', fields['soc']): - fields['cpu'] += ':arm720t' target, match, rear = defconfig.partition('_defconfig') assert match and not rear, \ @@ -261,16 +314,26 @@ class Slot: Arguments: output: File object which the result is written to maintainers_database: An instance of class MaintainersDatabase + devnull: file object of 'dev/null' + make_cmd: the command name of Make """ - self.occupied = False self.build_dir = tempfile.mkdtemp() self.devnull = devnull - self.make_cmd = make_cmd + self.ps = subprocess.Popen([make_cmd, 'O=' + self.build_dir, + 'allnoconfig'], stdout=devnull) + self.occupied = True self.parser = DotConfigParser(self.build_dir, output, maintainers_database) + self.env = os.environ.copy() + self.env['srctree'] = os.getcwd() + self.env['UBOOTVERSION'] = 'dummy' + self.env['KCONFIG_OBJDIR'] = '' def __del__(self): """Delete the working directory""" + if not self.occupied: + while self.ps.poll() == None: + pass shutil.rmtree(self.build_dir) def add(self, defconfig): @@ -287,13 +350,31 @@ class Slot: """ if self.occupied: return False - o = 'O=' + self.build_dir - self.ps = subprocess.Popen([self.make_cmd, o, defconfig], - stdout=self.devnull) + + with open(os.path.join(self.build_dir, '.tmp_defconfig'), 'w') as f: + for line in open(os.path.join(CONFIG_DIR, defconfig)): + colon = line.find(':CONFIG_') + if colon == -1: + f.write(line) + else: + f.write(line[colon + 1:]) + + self.ps = subprocess.Popen([os.path.join('scripts', 'kconfig', 'conf'), + '--defconfig=.tmp_defconfig', 'Kconfig'], + stdout=self.devnull, + cwd=self.build_dir, + env=self.env) + self.defconfig = defconfig self.occupied = True return True + def wait(self): + """Wait until the current subprocess finishes.""" + while self.occupied and self.ps.poll() == None: + time.sleep(SLEEP_TIME) + self.occupied = False + def poll(self): """Check if the subprocess is running and invoke the .config parser if the subprocess is terminated. @@ -305,7 +386,11 @@ class Slot: return True if self.ps.poll() == None: return False - self.parser.parse(self.defconfig) + if self.ps.poll() == 0: + self.parser.parse(self.defconfig) + else: + print >> sys.stderr, ("WARNING: failed to process '%s'. skip." % + self.defconfig) self.occupied = False return True @@ -327,6 +412,8 @@ class Slots: for i in range(jobs): self.slots.append(Slot(output, maintainers_database, devnull, make_cmd)) + for slot in self.slots: + slot.wait() def add(self, defconfig): """Add a new subprocess if a vacant slot is available. @@ -401,64 +488,97 @@ class Indicator: sys.stdout.write('\r' + msg) sys.stdout.flush() -def __gen_boards_cfg(jobs): - """Generate boards.cfg file. +class BoardsFileGenerator: - Arguments: - jobs: The number of jobs to run simultaneously + """Generator of boards.cfg.""" - Note: - The incomplete boards.cfg is left over when an error (including - the termination by the keyboard interrupt) occurs on the halfway. - """ - check_top_directory() - print 'Generating %s ... (jobs: %d)' % (BOARD_FILE, jobs) + def __init__(self): + """Prepare basic things for generating boards.cfg.""" + # All the defconfig files to be processed + defconfigs = [] + for (dirpath, dirnames, filenames) in os.walk(CONFIG_DIR): + dirpath = dirpath[len(CONFIG_DIR) + 1:] + for filename in fnmatch.filter(filenames, '*_defconfig'): + if fnmatch.fnmatch(filename, '.*'): + continue + defconfigs.append(os.path.join(dirpath, filename)) + self.defconfigs = defconfigs + self.indicator = Indicator(len(defconfigs)) + + # Parse all the MAINTAINERS files + maintainers_database = MaintainersDatabase() + for (dirpath, dirnames, filenames) in os.walk('.'): + if 'MAINTAINERS' in filenames: + maintainers_database.parse_file(os.path.join(dirpath, + 'MAINTAINERS')) + self.maintainers_database = maintainers_database - # All the defconfig files to be processed - defconfigs = [] - for (dirpath, dirnames, filenames) in os.walk(CONFIG_DIR): - dirpath = dirpath[len(CONFIG_DIR) + 1:] - for filename in fnmatch.filter(filenames, '*_defconfig'): - defconfigs.append(os.path.join(dirpath, filename)) + def __del__(self): + """Delete the incomplete boards.cfg - # Parse all the MAINTAINERS files - maintainers_database = MaintainersDatabase() - for (dirpath, dirnames, filenames) in os.walk('.'): - if 'MAINTAINERS' in filenames: - maintainers_database.parse_file(os.path.join(dirpath, - 'MAINTAINERS')) - - # Output lines should be piped into the reformat tool - reformat_process = subprocess.Popen(REFORMAT_CMD, stdin=subprocess.PIPE, - stdout=open(BOARD_FILE, 'w')) - pipe = reformat_process.stdin - pipe.write(COMMENT_BLOCK) - - indicator = Indicator(len(defconfigs)) - slots = Slots(jobs, pipe, maintainers_database) - - # Main loop to process defconfig files: - # Add a new subprocess into a vacant slot. - # Sleep if there is no available slot. - for defconfig in defconfigs: - while not slots.add(defconfig): - while not slots.available(): - # No available slot: sleep for a while - time.sleep(SLEEP_TIME) - indicator.inc() - - # wait until all the subprocesses finish - while not slots.empty(): - time.sleep(SLEEP_TIME) - print '' - - # wait until the reformat tool finishes - reformat_process.communicate() - if reformat_process.returncode != 0: - print >> sys.stderr, '"%s" failed' % REFORMAT_CMD[0] - sys.exit(1) - -def gen_boards_cfg(jobs): + This destructor deletes boards.cfg if the private member 'in_progress' + is defined as True. The 'in_progress' member is set to True at the + beginning of the generate() method and set to False at its end. + So, in_progress==True means generating boards.cfg was terminated + on the way. + """ + + if hasattr(self, 'in_progress') and self.in_progress: + try: + os.remove(BOARD_FILE) + except OSError as exception: + # Ignore 'No such file or directory' error + if exception.errno != errno.ENOENT: + raise + print 'Removed incomplete %s' % BOARD_FILE + + def generate(self, jobs): + """Generate boards.cfg + + This method sets the 'in_progress' member to True at the beginning + and sets it to False on success. The boards.cfg should not be + touched before/after this method because 'in_progress' is used + to detect the incomplete boards.cfg. + + Arguments: + jobs: The number of jobs to run simultaneously + """ + + self.in_progress = True + print 'Generating %s ... (jobs: %d)' % (BOARD_FILE, jobs) + + # Output lines should be piped into the reformat tool + reformat_process = subprocess.Popen(REFORMAT_CMD, + stdin=subprocess.PIPE, + stdout=open(BOARD_FILE, 'w')) + pipe = reformat_process.stdin + pipe.write(COMMENT_BLOCK) + + slots = Slots(jobs, pipe, self.maintainers_database) + + # Main loop to process defconfig files: + # Add a new subprocess into a vacant slot. + # Sleep if there is no available slot. + for defconfig in self.defconfigs: + while not slots.add(defconfig): + while not slots.available(): + # No available slot: sleep for a while + time.sleep(SLEEP_TIME) + self.indicator.inc() + + # wait until all the subprocesses finish + while not slots.empty(): + time.sleep(SLEEP_TIME) + print '' + + # wait until the reformat tool finishes + reformat_process.communicate() + if reformat_process.returncode != 0: + sys.exit('"%s" failed' % REFORMAT_CMD[0]) + + self.in_progress = False + +def gen_boards_cfg(jobs=1, force=False): """Generate boards.cfg file. The incomplete boards.cfg is deleted if an error (including @@ -467,30 +587,28 @@ def gen_boards_cfg(jobs): Arguments: jobs: The number of jobs to run simultaneously """ - try: - __gen_boards_cfg(jobs) - except: - # We should remove incomplete boards.cfg - try: - os.remove(BOARD_FILE) - except OSError as exception: - # Ignore 'No such file or directory' error - if exception.errno != errno.ENOENT: - raise - raise + check_top_directory() + if not force and output_is_new(): + print "%s is up to date. Nothing to do." % BOARD_FILE + sys.exit(0) + + generator = BoardsFileGenerator() + generator.generate(jobs) def main(): parser = optparse.OptionParser() # Add options here parser.add_option('-j', '--jobs', help='the number of jobs to run simultaneously') + parser.add_option('-f', '--force', action="store_true", default=False, + help='regenerate the output even if it is new') (options, args) = parser.parse_args() + if options.jobs: try: jobs = int(options.jobs) except ValueError: - print >> sys.stderr, 'Option -j (--jobs) takes a number' - sys.exit(1) + sys.exit('Option -j (--jobs) takes a number') else: try: jobs = int(subprocess.Popen(['getconf', '_NPROCESSORS_ONLN'], @@ -498,7 +616,8 @@ def main(): except (OSError, ValueError): print 'info: failed to get the number of CPUs. Set jobs to 1' jobs = 1 - gen_boards_cfg(jobs) + + gen_boards_cfg(jobs, force=options.force) if __name__ == '__main__': main() diff --git a/tools/image-host.c b/tools/image-host.c index 0eff720..7effb6c 100644 --- a/tools/image-host.c +++ b/tools/image-host.c @@ -689,7 +689,7 @@ int fit_add_verification_data(const char *keydir, void *keydest, void *fit, confs_noffset = fdt_path_offset(fit, FIT_CONFS_PATH); if (confs_noffset < 0) { printf("Can't find images parent node '%s' (%s)\n", - FIT_IMAGES_PATH, fdt_strerror(confs_noffset)); + FIT_CONFS_PATH, fdt_strerror(confs_noffset)); return -ENOENT; } diff --git a/tools/mkimage.c b/tools/mkimage.c index 123d0c7..c70408c 100644 --- a/tools/mkimage.c +++ b/tools/mkimage.c @@ -458,6 +458,7 @@ NXTARG: ; #if defined(_POSIX_SYNCHRONIZED_IO) && \ !defined(__sun__) && \ !defined(__FreeBSD__) && \ + !defined(__OpenBSD__) && \ !defined(__APPLE__) (void) fdatasync (ifd); #else @@ -501,6 +502,7 @@ NXTARG: ; #if defined(_POSIX_SYNCHRONIZED_IO) && \ !defined(__sun__) && \ !defined(__FreeBSD__) && \ + !defined(__OpenBSD__) && \ !defined(__APPLE__) (void) fdatasync (ifd); #else diff --git a/tools/patman/checkpatch.py b/tools/patman/checkpatch.py index 0d4e935..34a3bd2 100644 --- a/tools/patman/checkpatch.py +++ b/tools/patman/checkpatch.py @@ -34,9 +34,8 @@ def FindCheckPatch(): return fname path = os.path.dirname(path) - print >> sys.stderr, ('Cannot find checkpatch.pl - please put it in your ' + - '~/bin directory or use --no-check') - sys.exit(1) + sys.exit('Cannot find checkpatch.pl - please put it in your ' + + '~/bin directory or use --no-check') def CheckPatch(fname, verbose=False): """Run checkpatch.pl on a file. diff --git a/tools/patman/gitutil.py b/tools/patman/gitutil.py index 65754f5..e2b4959 100644 --- a/tools/patman/gitutil.py +++ b/tools/patman/gitutil.py @@ -14,6 +14,37 @@ import terminal import checkpatch import settings +# True to use --no-decorate - we check this in Setup() +use_no_decorate = True + +def LogCmd(commit_range, git_dir=None, oneline=False, reverse=False, + count=None): + """Create a command to perform a 'git log' + + Args: + commit_range: Range expression to use for log, None for none + git_dir: Path to git repositiory (None to use default) + oneline: True to use --oneline, else False + reverse: True to reverse the log (--reverse) + count: Number of commits to list, or None for no limit + Return: + List containing command and arguments to run + """ + cmd = ['git'] + if git_dir: + cmd += ['--git-dir', git_dir] + cmd += ['log', '--no-color'] + if oneline: + cmd.append('--oneline') + if use_no_decorate: + cmd.append('--no-decorate') + if reverse: + cmd.append('--reverse') + if count is not None: + cmd.append('-n%d' % count) + if commit_range: + cmd.append(commit_range) + return cmd def CountCommitsToBranch(): """Returns number of commits between HEAD and the tracking branch. @@ -24,8 +55,7 @@ def CountCommitsToBranch(): Return: Number of patches that exist on top of the branch """ - pipe = [['git', 'log', '--no-color', '--oneline', '--no-decorate', - '@{upstream}..'], + pipe = [LogCmd('@{upstream}..', oneline=True), ['wc', '-l']] stdout = command.RunPipe(pipe, capture=True, oneline=True).stdout patch_count = int(stdout) @@ -87,8 +117,7 @@ def CountCommitsInBranch(git_dir, branch, include_upstream=False): range_expr = GetRangeInBranch(git_dir, branch, include_upstream) if not range_expr: return None - pipe = [['git', '--git-dir', git_dir, 'log', '--oneline', '--no-decorate', - range_expr], + pipe = [LogCmd(range_expr, git_dir=git_dir, oneline=True), ['wc', '-l']] result = command.RunPipe(pipe, capture=True, oneline=True) patch_count = int(result.stdout) @@ -102,7 +131,7 @@ def CountCommits(commit_range): Return: Number of patches that exist on top of the branch """ - pipe = [['git', 'log', '--oneline', '--no-decorate', commit_range], + pipe = [LogCmd(commit_range, oneline=True), ['wc', '-l']] stdout = command.RunPipe(pipe, capture=True, oneline=True).stdout patch_count = int(stdout) @@ -543,6 +572,9 @@ def Setup(): alias_fname = GetAliasFile() if alias_fname: settings.ReadGitAliases(alias_fname) + cmd = LogCmd(None, count=0) + use_no_decorate = (command.RunPipe([cmd], raise_on_error=False) + .return_code == 0) def GetHead(): """Get the hash of the current HEAD diff --git a/tools/patman/patchstream.py b/tools/patman/patchstream.py index 3228719..0040468 100644 --- a/tools/patman/patchstream.py +++ b/tools/patman/patchstream.py @@ -379,14 +379,9 @@ def GetMetaDataForList(commit_range, git_dir=None, count=None, Returns: A Series object containing information about the commits. """ - params = ['git', 'log', '--no-color', '--reverse', '--no-decorate', - commit_range] - if count is not None: - params[2:2] = ['-n%d' % count] - if git_dir: - params[1:1] = ['--git-dir', git_dir] - pipe = [params] - stdout = command.RunPipe(pipe, capture=True).stdout + params = gitutil.LogCmd(commit_range,reverse=True, count=count, + git_dir=git_dir) + stdout = command.RunPipe([params], capture=True).stdout ps = PatchStream(series, is_log=True) for line in stdout.splitlines(): ps.ProcessLine(line) diff --git a/tools/patman/patman.py b/tools/patman/patman.py index c60aa5a..ca34cb9 100755 --- a/tools/patman/patman.py +++ b/tools/patman/patman.py @@ -58,7 +58,7 @@ parser.add_option('--no-check', action='store_false', dest='check_patch', parser.add_option('--no-tags', action='store_false', dest='process_tags', default=True, help="Don't process subject tags as aliaes") -parser.usage = """patman [options] +parser.usage += """ Create patches from commits in a branch, check them and email them as specified by tags you place in the commits. Use -n to do a dry run first.""" @@ -122,8 +122,7 @@ else: col = terminal.Color() if not options.count: str = 'No commits found to process - please use -c flag' - print col.Color(col.RED, str) - sys.exit(1) + sys.exit(col.Color(col.RED, str)) # Read the metadata from the commits if options.count: |