diff options
-rw-r--r-- | Documentation/barebox-main.dox | 4 | ||||
-rw-r--r-- | Makefile | 6 | ||||
-rw-r--r-- | TODO | 3 | ||||
-rw-r--r-- | arch/arm/Makefile | 3 | ||||
-rw-r--r-- | arch/arm/boards/a9m2410/Makefile (renamed from board/a9m2410/Makefile) | 0 | ||||
-rw-r--r-- | arch/arm/boards/a9m2410/a9m2410.c (renamed from board/a9m2410/a9m2410.c) | 0 | ||||
-rw-r--r-- | arch/arm/boards/a9m2410/config.h (renamed from board/a9m2410/config.h) | 0 | ||||
-rw-r--r-- | arch/arm/boards/a9m2410/env/bin/_update (renamed from board/a9m2410/env/bin/_update) | 0 | ||||
-rw-r--r-- | arch/arm/boards/a9m2410/env/bin/boot (renamed from board/a9m2410/env/bin/boot) | 0 | ||||
-rw-r--r-- | arch/arm/boards/a9m2410/env/bin/hush_hack (renamed from board/a9m2410/env/bin/hush_hack) | 0 | ||||
-rw-r--r-- | arch/arm/boards/a9m2410/env/bin/init (renamed from board/a9m2410/env/bin/init) | 0 | ||||
-rw-r--r-- | arch/arm/boards/a9m2410/env/bin/update_kernel (renamed from board/a9m2410/env/bin/update_kernel) | 0 | ||||
-rw-r--r-- | arch/arm/boards/a9m2410/env/bin/update_root (renamed from board/a9m2410/env/bin/update_root) | 0 | ||||
-rw-r--r-- | arch/arm/boards/a9m2410/env/config (renamed from board/a9m2410/env/config) | 0 | ||||
-rw-r--r-- | arch/arm/boards/a9m2410/lowlevel_init.S (renamed from board/a9m2410/lowlevel_init.S) | 0 | ||||
-rw-r--r-- | arch/arm/boards/a9m2440/Makefile (renamed from board/a9m2440/Makefile) | 0 | ||||
-rw-r--r-- | arch/arm/boards/a9m2440/a9m2410dev.c (renamed from board/a9m2440/a9m2410dev.c) | 0 | ||||
-rw-r--r-- | arch/arm/boards/a9m2440/a9m2440.c (renamed from board/a9m2440/a9m2440.c) | 0 | ||||
-rw-r--r-- | arch/arm/boards/a9m2440/baseboards.h (renamed from board/a9m2440/baseboards.h) | 0 | ||||
-rw-r--r-- | arch/arm/boards/a9m2440/config.h (renamed from board/a9m2440/config.h) | 0 | ||||
-rw-r--r-- | arch/arm/boards/a9m2440/env/bin/_update (renamed from board/a9m2440/env/bin/_update) | 0 | ||||
-rw-r--r-- | arch/arm/boards/a9m2440/env/bin/boot (renamed from board/a9m2440/env/bin/boot) | 0 | ||||
-rw-r--r-- | arch/arm/boards/a9m2440/env/bin/hush_hack (renamed from board/a9m2440/env/bin/hush_hack) | 0 | ||||
-rw-r--r-- | arch/arm/boards/a9m2440/env/bin/init (renamed from board/a9m2440/env/bin/init) | 0 | ||||
-rw-r--r-- | arch/arm/boards/a9m2440/env/bin/update_kernel (renamed from board/a9m2440/env/bin/update_kernel) | 0 | ||||
-rw-r--r-- | arch/arm/boards/a9m2440/env/bin/update_root (renamed from board/a9m2440/env/bin/update_root) | 0 | ||||
-rw-r--r-- | arch/arm/boards/a9m2440/env/config (renamed from board/a9m2440/env/config) | 0 | ||||
-rw-r--r-- | arch/arm/boards/a9m2440/lowlevel_init.S (renamed from board/a9m2440/lowlevel_init.S) | 0 | ||||
-rw-r--r-- | arch/arm/boards/at91sam9260ek/Makefile (renamed from board/at91sam9260ek/Makefile) | 0 | ||||
-rw-r--r-- | arch/arm/boards/at91sam9260ek/config.h (renamed from board/at91sam9260ek/config.h) | 0 | ||||
-rw-r--r-- | arch/arm/boards/at91sam9260ek/env/bin/_update (renamed from board/at91sam9260ek/env/bin/_update) | 0 | ||||
-rw-r--r-- | arch/arm/boards/at91sam9260ek/env/bin/boot (renamed from board/at91sam9260ek/env/bin/boot) | 0 | ||||
-rw-r--r-- | arch/arm/boards/at91sam9260ek/env/bin/init (renamed from board/at91sam9260ek/env/bin/init) | 0 | ||||
-rw-r--r-- | arch/arm/boards/at91sam9260ek/env/bin/pcidmaloop (renamed from board/at91sam9260ek/env/bin/pcidmaloop) | 0 | ||||
-rw-r--r-- | arch/arm/boards/at91sam9260ek/env/bin/pciloop (renamed from board/at91sam9260ek/env/bin/pciloop) | 0 | ||||
-rw-r--r-- | arch/arm/boards/at91sam9260ek/env/bin/update_kernel (renamed from board/at91sam9260ek/env/bin/update_kernel) | 0 | ||||
-rw-r--r-- | arch/arm/boards/at91sam9260ek/env/bin/update_root (renamed from board/at91sam9260ek/env/bin/update_root) | 0 | ||||
-rw-r--r-- | arch/arm/boards/at91sam9260ek/env/config (renamed from board/at91sam9260ek/env/config) | 0 | ||||
-rw-r--r-- | arch/arm/boards/at91sam9260ek/init.c (renamed from board/at91sam9260ek/init.c) | 0 | ||||
-rw-r--r-- | arch/arm/boards/at91sam9260ek/lowlevel_init.S (renamed from board/at91sam9260ek/lowlevel_init.S) | 0 | ||||
-rw-r--r-- | arch/arm/boards/at91sam9263ek/Makefile (renamed from board/at91sam9263ek/Makefile) | 0 | ||||
-rw-r--r-- | arch/arm/boards/at91sam9263ek/config.h (renamed from board/at91sam9263ek/config.h) | 0 | ||||
-rw-r--r-- | arch/arm/boards/at91sam9263ek/env/bin/_update (renamed from board/at91sam9263ek/env/bin/_update) | 0 | ||||
-rw-r--r-- | arch/arm/boards/at91sam9263ek/env/bin/boot (renamed from board/at91sam9263ek/env/bin/boot) | 0 | ||||
-rw-r--r-- | arch/arm/boards/at91sam9263ek/env/bin/hush_hack (renamed from board/at91sam9263ek/env/bin/hush_hack) | 0 | ||||
-rw-r--r-- | arch/arm/boards/at91sam9263ek/env/bin/init (renamed from board/at91sam9263ek/env/bin/init) | 0 | ||||
-rw-r--r-- | arch/arm/boards/at91sam9263ek/env/bin/update_barebox_xmodem (renamed from board/at91sam9263ek/env/bin/update_barebox_xmodem) | 0 | ||||
-rw-r--r-- | arch/arm/boards/at91sam9263ek/env/bin/update_kernel (renamed from board/at91sam9263ek/env/bin/update_kernel) | 0 | ||||
-rw-r--r-- | arch/arm/boards/at91sam9263ek/env/bin/update_root (renamed from board/at91sam9263ek/env/bin/update_root) | 0 | ||||
-rw-r--r-- | arch/arm/boards/at91sam9263ek/env/config (renamed from board/at91sam9263ek/env/config) | 0 | ||||
-rw-r--r-- | arch/arm/boards/at91sam9263ek/init.c (renamed from board/at91sam9263ek/init.c) | 0 | ||||
-rw-r--r-- | arch/arm/boards/edb93xx/Makefile (renamed from board/edb93xx/Makefile) | 0 | ||||
-rw-r--r-- | arch/arm/boards/edb93xx/config.h (renamed from board/edb93xx/config.h) | 0 | ||||
-rw-r--r-- | arch/arm/boards/edb93xx/early_udelay.h (renamed from board/edb93xx/early_udelay.h) | 0 | ||||
-rw-r--r-- | arch/arm/boards/edb93xx/edb93xx.c (renamed from board/edb93xx/edb93xx.c) | 0 | ||||
-rw-r--r-- | arch/arm/boards/edb93xx/edb93xx.dox (renamed from board/edb93xx/edb93xx.dox) | 0 | ||||
-rw-r--r-- | arch/arm/boards/edb93xx/edb93xx.h (renamed from board/edb93xx/edb93xx.h) | 0 | ||||
-rw-r--r-- | arch/arm/boards/edb93xx/env/bin/boot (renamed from board/edb93xx/env/bin/boot) | 0 | ||||
-rw-r--r-- | arch/arm/boards/edb93xx/env/bin/flash_partition (renamed from board/edb93xx/env/bin/flash_partition) | 0 | ||||
-rw-r--r-- | arch/arm/boards/edb93xx/env/bin/init (renamed from board/edb93xx/env/bin/init) | 0 | ||||
-rw-r--r-- | arch/arm/boards/edb93xx/env/bin/set_nor_parts (renamed from board/edb93xx/env/bin/set_nor_parts) | 0 | ||||
-rw-r--r-- | arch/arm/boards/edb93xx/env/bin/update_kernel (renamed from board/edb93xx/env/bin/update_kernel) | 0 | ||||
-rw-r--r-- | arch/arm/boards/edb93xx/env/bin/update_rootfs (renamed from board/edb93xx/env/bin/update_rootfs) | 0 | ||||
-rw-r--r-- | arch/arm/boards/edb93xx/env/config (renamed from board/edb93xx/env/config) | 0 | ||||
-rw-r--r-- | arch/arm/boards/edb93xx/flash_cfg.c (renamed from board/edb93xx/flash_cfg.c) | 0 | ||||
-rw-r--r-- | arch/arm/boards/edb93xx/pll_cfg.c (renamed from board/edb93xx/pll_cfg.c) | 0 | ||||
-rw-r--r-- | arch/arm/boards/edb93xx/pll_cfg.h (renamed from board/edb93xx/pll_cfg.h) | 0 | ||||
-rw-r--r-- | arch/arm/boards/edb93xx/sdram_cfg.c (renamed from board/edb93xx/sdram_cfg.c) | 0 | ||||
-rw-r--r-- | arch/arm/boards/edb93xx/sdram_cfg.h (renamed from board/edb93xx/sdram_cfg.h) | 0 | ||||
-rw-r--r-- | arch/arm/boards/eukrea_cpuimx25/Makefile (renamed from board/eukrea_cpuimx25/Makefile) | 0 | ||||
-rw-r--r-- | arch/arm/boards/eukrea_cpuimx25/config.h (renamed from board/eukrea_cpuimx25/config.h) | 0 | ||||
-rw-r--r-- | arch/arm/boards/eukrea_cpuimx25/env/bin/_update (renamed from board/eukrea_cpuimx25/env/bin/_update) | 0 | ||||
-rw-r--r-- | arch/arm/boards/eukrea_cpuimx25/env/bin/boot (renamed from board/eukrea_cpuimx25/env/bin/boot) | 0 | ||||
-rw-r--r-- | arch/arm/boards/eukrea_cpuimx25/env/bin/hush_hack (renamed from board/eukrea_cpuimx25/env/bin/hush_hack) | 0 | ||||
-rw-r--r-- | arch/arm/boards/eukrea_cpuimx25/env/bin/init (renamed from board/eukrea_cpuimx25/env/bin/init) | 0 | ||||
-rw-r--r-- | arch/arm/boards/eukrea_cpuimx25/env/bin/update_kernel (renamed from board/eukrea_cpuimx25/env/bin/update_kernel) | 0 | ||||
-rw-r--r-- | arch/arm/boards/eukrea_cpuimx25/env/bin/update_root (renamed from board/eukrea_cpuimx25/env/bin/update_root) | 0 | ||||
-rw-r--r-- | arch/arm/boards/eukrea_cpuimx25/env/config (renamed from board/eukrea_cpuimx25/env/config) | 0 | ||||
-rw-r--r-- | arch/arm/boards/eukrea_cpuimx25/eukrea_cpuimx25.c (renamed from board/eukrea_cpuimx25/eukrea_cpuimx25.c) | 0 | ||||
-rw-r--r-- | arch/arm/boards/eukrea_cpuimx25/lowlevel.c (renamed from board/eukrea_cpuimx25/lowlevel.c) | 0 | ||||
-rw-r--r-- | arch/arm/boards/eukrea_cpuimx27/Makefile (renamed from board/eukrea_cpuimx27/Makefile) | 0 | ||||
-rw-r--r-- | arch/arm/boards/eukrea_cpuimx27/config.h (renamed from board/eukrea_cpuimx27/config.h) | 0 | ||||
-rw-r--r-- | arch/arm/boards/eukrea_cpuimx27/env/bin/_update (renamed from board/eukrea_cpuimx27/env/bin/_update) | 0 | ||||
-rw-r--r-- | arch/arm/boards/eukrea_cpuimx27/env/bin/boot (renamed from board/eukrea_cpuimx27/env/bin/boot) | 0 | ||||
-rw-r--r-- | arch/arm/boards/eukrea_cpuimx27/env/bin/hush_hack (renamed from board/eukrea_cpuimx27/env/bin/hush_hack) | 0 | ||||
-rw-r--r-- | arch/arm/boards/eukrea_cpuimx27/env/bin/init (renamed from board/eukrea_cpuimx27/env/bin/init) | 0 | ||||
-rw-r--r-- | arch/arm/boards/eukrea_cpuimx27/env/bin/update_kernel (renamed from board/eukrea_cpuimx27/env/bin/update_kernel) | 0 | ||||
-rw-r--r-- | arch/arm/boards/eukrea_cpuimx27/env/bin/update_root (renamed from board/eukrea_cpuimx27/env/bin/update_root) | 0 | ||||
-rw-r--r-- | arch/arm/boards/eukrea_cpuimx27/env/config (renamed from board/eukrea_cpuimx27/env/config) | 0 | ||||
-rw-r--r-- | arch/arm/boards/eukrea_cpuimx27/eukrea_cpuimx27.c (renamed from board/eukrea_cpuimx27/eukrea_cpuimx27.c) | 0 | ||||
-rw-r--r-- | arch/arm/boards/eukrea_cpuimx27/eukrea_cpuimx27.dox (renamed from board/eukrea_cpuimx27/eukrea_cpuimx27.dox) | 0 | ||||
-rw-r--r-- | arch/arm/boards/eukrea_cpuimx27/lowlevel_init.S (renamed from board/eukrea_cpuimx27/lowlevel_init.S) | 0 | ||||
-rw-r--r-- | arch/arm/boards/eukrea_cpuimx35/Makefile (renamed from board/eukrea_cpuimx35/Makefile) | 0 | ||||
-rw-r--r-- | arch/arm/boards/eukrea_cpuimx35/config.h (renamed from board/eukrea_cpuimx35/config.h) | 0 | ||||
-rw-r--r-- | arch/arm/boards/eukrea_cpuimx35/env/bin/_update (renamed from board/eukrea_cpuimx35/env/bin/_update) | 0 | ||||
-rw-r--r-- | arch/arm/boards/eukrea_cpuimx35/env/bin/boot (renamed from board/eukrea_cpuimx35/env/bin/boot) | 0 | ||||
-rw-r--r-- | arch/arm/boards/eukrea_cpuimx35/env/bin/hush_hack (renamed from board/eukrea_cpuimx35/env/bin/hush_hack) | 0 | ||||
-rw-r--r-- | arch/arm/boards/eukrea_cpuimx35/env/bin/init (renamed from board/eukrea_cpuimx35/env/bin/init) | 4 | ||||
-rw-r--r-- | arch/arm/boards/eukrea_cpuimx35/env/bin/update_kernel (renamed from board/eukrea_cpuimx35/env/bin/update_kernel) | 0 | ||||
-rw-r--r-- | arch/arm/boards/eukrea_cpuimx35/env/bin/update_root (renamed from board/eukrea_cpuimx35/env/bin/update_root) | 0 | ||||
-rw-r--r-- | arch/arm/boards/eukrea_cpuimx35/env/config (renamed from board/eukrea_cpuimx35/env/config) | 0 | ||||
-rw-r--r-- | arch/arm/boards/eukrea_cpuimx35/eukrea_cpuimx35.c (renamed from board/eukrea_cpuimx35/eukrea_cpuimx35.c) | 28 | ||||
-rw-r--r-- | arch/arm/boards/eukrea_cpuimx35/eukrea_cpuimx35.dox (renamed from board/eukrea_cpuimx35/eukrea_cpuimx35.dox) | 0 | ||||
-rw-r--r-- | arch/arm/boards/eukrea_cpuimx35/flash_header.c | 41 | ||||
-rw-r--r-- | arch/arm/boards/eukrea_cpuimx35/lowlevel.c (renamed from board/eukrea_cpuimx35/lowlevel.c) | 71 | ||||
-rw-r--r-- | arch/arm/boards/freescale-mx25-3-stack/3stack.c (renamed from board/freescale-mx25-3-stack/3stack.c) | 0 | ||||
-rw-r--r-- | arch/arm/boards/freescale-mx25-3-stack/Makefile (renamed from board/freescale-mx25-3-stack/Makefile) | 0 | ||||
-rw-r--r-- | arch/arm/boards/freescale-mx25-3-stack/config.h (renamed from board/freescale-mx25-3-stack/config.h) | 0 | ||||
-rw-r--r-- | arch/arm/boards/freescale-mx25-3-stack/env/bin/_update (renamed from board/freescale-mx25-3-stack/env/bin/_update) | 0 | ||||
-rw-r--r-- | arch/arm/boards/freescale-mx25-3-stack/env/bin/boot (renamed from board/freescale-mx25-3-stack/env/bin/boot) | 0 | ||||
-rw-r--r-- | arch/arm/boards/freescale-mx25-3-stack/env/bin/hush_hack (renamed from board/freescale-mx25-3-stack/env/bin/hush_hack) | 0 | ||||
-rw-r--r-- | arch/arm/boards/freescale-mx25-3-stack/env/bin/init (renamed from board/freescale-mx25-3-stack/env/bin/init) | 0 | ||||
-rw-r--r-- | arch/arm/boards/freescale-mx25-3-stack/env/bin/update_kernel (renamed from board/freescale-mx25-3-stack/env/bin/update_kernel) | 0 | ||||
-rw-r--r-- | arch/arm/boards/freescale-mx25-3-stack/env/bin/update_root (renamed from board/freescale-mx25-3-stack/env/bin/update_root) | 0 | ||||
-rw-r--r-- | arch/arm/boards/freescale-mx25-3-stack/env/config (renamed from board/freescale-mx25-3-stack/env/config) | 0 | ||||
-rw-r--r-- | arch/arm/boards/freescale-mx25-3-stack/lowlevel_init.S (renamed from board/freescale-mx25-3-stack/lowlevel_init.S) | 0 | ||||
-rw-r--r-- | arch/arm/boards/freescale-mx35-3-stack/3stack.c (renamed from board/freescale-mx35-3-stack/3stack.c) | 0 | ||||
-rw-r--r-- | arch/arm/boards/freescale-mx35-3-stack/3stack.dox (renamed from board/freescale-mx35-3-stack/3stack.dox) | 0 | ||||
-rw-r--r-- | arch/arm/boards/freescale-mx35-3-stack/Makefile (renamed from board/freescale-mx35-3-stack/Makefile) | 0 | ||||
-rw-r--r-- | arch/arm/boards/freescale-mx35-3-stack/board-mx35_3stack.h (renamed from board/freescale-mx35-3-stack/board-mx35_3stack.h) | 0 | ||||
-rw-r--r-- | arch/arm/boards/freescale-mx35-3-stack/config.h (renamed from board/freescale-mx35-3-stack/config.h) | 0 | ||||
-rw-r--r-- | arch/arm/boards/freescale-mx35-3-stack/env/bin/_update (renamed from board/freescale-mx35-3-stack/env/bin/_update) | 0 | ||||
-rw-r--r-- | arch/arm/boards/freescale-mx35-3-stack/env/bin/boot (renamed from board/freescale-mx35-3-stack/env/bin/boot) | 0 | ||||
-rw-r--r-- | arch/arm/boards/freescale-mx35-3-stack/env/bin/hush_hack (renamed from board/freescale-mx35-3-stack/env/bin/hush_hack) | 0 | ||||
-rw-r--r-- | arch/arm/boards/freescale-mx35-3-stack/env/bin/init (renamed from board/freescale-mx35-3-stack/env/bin/init) | 0 | ||||
-rw-r--r-- | arch/arm/boards/freescale-mx35-3-stack/env/bin/update_kernel (renamed from board/freescale-mx35-3-stack/env/bin/update_kernel) | 0 | ||||
-rw-r--r-- | arch/arm/boards/freescale-mx35-3-stack/env/bin/update_rootfs (renamed from board/freescale-mx35-3-stack/env/bin/update_rootfs) | 0 | ||||
-rw-r--r-- | arch/arm/boards/freescale-mx35-3-stack/env/config (renamed from board/freescale-mx35-3-stack/env/config) | 0 | ||||
-rw-r--r-- | arch/arm/boards/freescale-mx35-3-stack/flash_header.c (renamed from board/freescale-mx35-3-stack/flash_header.c) | 0 | ||||
-rw-r--r-- | arch/arm/boards/freescale-mx35-3-stack/lowlevel_init.S (renamed from board/freescale-mx35-3-stack/lowlevel_init.S) | 0 | ||||
-rw-r--r-- | arch/arm/boards/guf-neso/Makefile | 5 | ||||
-rw-r--r-- | arch/arm/boards/guf-neso/board.c | 401 | ||||
-rw-r--r-- | arch/arm/boards/guf-neso/config.h (renamed from board/pcm038/config.h) | 0 | ||||
-rw-r--r-- | arch/arm/boards/guf-neso/env/config | 53 | ||||
-rw-r--r-- | arch/arm/boards/guf-neso/lowlevel.c (renamed from board/pcm038/lowlevel.c) | 0 | ||||
-rw-r--r-- | arch/arm/boards/guf-neso/pll_init.S | 48 | ||||
-rw-r--r-- | arch/arm/boards/imx21ads/Makefile (renamed from board/imx21ads/Makefile) | 0 | ||||
-rw-r--r-- | arch/arm/boards/imx21ads/config.h (renamed from board/imx21ads/config.h) | 0 | ||||
-rw-r--r-- | arch/arm/boards/imx21ads/env/bin/init (renamed from board/imx21ads/env/bin/init) | 0 | ||||
-rw-r--r-- | arch/arm/boards/imx21ads/imx21ads.c (renamed from board/imx21ads/imx21ads.c) | 0 | ||||
-rw-r--r-- | arch/arm/boards/imx21ads/imx21ads.dox (renamed from board/imx21ads/imx21ads.dox) | 0 | ||||
-rw-r--r-- | arch/arm/boards/imx21ads/lowlevel_init.S (renamed from board/imx21ads/lowlevel_init.S) | 0 | ||||
-rw-r--r-- | arch/arm/boards/imx27ads/Makefile (renamed from board/imx27ads/Makefile) | 0 | ||||
-rw-r--r-- | arch/arm/boards/imx27ads/config.h (renamed from board/imx27ads/config.h) | 0 | ||||
-rw-r--r-- | arch/arm/boards/imx27ads/env/bin/_update (renamed from board/imx27ads/env/bin/_update) | 0 | ||||
-rw-r--r-- | arch/arm/boards/imx27ads/env/bin/boot (renamed from board/imx27ads/env/bin/boot) | 0 | ||||
-rw-r--r-- | arch/arm/boards/imx27ads/env/bin/init (renamed from board/imx27ads/env/bin/init) | 0 | ||||
-rw-r--r-- | arch/arm/boards/imx27ads/env/bin/update_kernel (renamed from board/imx27ads/env/bin/update_kernel) | 0 | ||||
-rw-r--r-- | arch/arm/boards/imx27ads/env/bin/update_root (renamed from board/imx27ads/env/bin/update_root) | 0 | ||||
-rw-r--r-- | arch/arm/boards/imx27ads/env/config (renamed from board/imx27ads/env/config) | 0 | ||||
-rw-r--r-- | arch/arm/boards/imx27ads/imx27ads.c (renamed from board/imx27ads/imx27ads.c) | 0 | ||||
-rw-r--r-- | arch/arm/boards/imx27ads/imx27ads.dox (renamed from board/imx27ads/imx27ads.dox) | 0 | ||||
-rw-r--r-- | arch/arm/boards/imx27ads/lowlevel_init.S (renamed from board/imx27ads/lowlevel_init.S) | 0 | ||||
-rw-r--r-- | arch/arm/boards/mmccpu/Makefile (renamed from board/mmccpu/Makefile) | 0 | ||||
-rw-r--r-- | arch/arm/boards/mmccpu/config.h (renamed from board/mmccpu/config.h) | 0 | ||||
-rw-r--r-- | arch/arm/boards/mmccpu/env/bin/_update (renamed from board/kp_ukd_r1_num/env/bin/_update) | 0 | ||||
-rw-r--r-- | arch/arm/boards/mmccpu/env/bin/boot (renamed from board/mmccpu/env/bin/boot) | 0 | ||||
-rw-r--r-- | arch/arm/boards/mmccpu/env/bin/hush_hack (renamed from board/mmccpu/env/bin/hush_hack) | 0 | ||||
-rw-r--r-- | arch/arm/boards/mmccpu/env/bin/init (renamed from board/mmccpu/env/bin/init) | 0 | ||||
-rw-r--r-- | arch/arm/boards/mmccpu/env/bin/update_kernel (renamed from board/mmccpu/env/bin/update_kernel) | 0 | ||||
-rw-r--r-- | arch/arm/boards/mmccpu/env/bin/update_root (renamed from board/mmccpu/env/bin/update_root) | 0 | ||||
-rw-r--r-- | arch/arm/boards/mmccpu/env/config (renamed from board/mmccpu/env/config) | 0 | ||||
-rw-r--r-- | arch/arm/boards/mmccpu/init.c (renamed from board/mmccpu/init.c) | 0 | ||||
-rw-r--r-- | arch/arm/boards/netx/Makefile (renamed from board/netx/Makefile) | 0 | ||||
-rw-r--r-- | arch/arm/boards/netx/config.h (renamed from board/netx/config.h) | 0 | ||||
-rw-r--r-- | arch/arm/boards/netx/netx.c (renamed from board/netx/netx.c) | 0 | ||||
-rw-r--r-- | arch/arm/boards/netx/netx.dox (renamed from board/netx/netx.dox) | 0 | ||||
-rw-r--r-- | arch/arm/boards/netx/platform.S (renamed from board/netx/platform.S) | 0 | ||||
-rw-r--r-- | arch/arm/boards/omap/Kconfig (renamed from board/omap/Kconfig) | 69 | ||||
-rw-r--r-- | arch/arm/boards/omap/Makefile (renamed from board/omap/Makefile) | 2 | ||||
-rw-r--r-- | arch/arm/boards/omap/board-beagle.c | 274 | ||||
-rw-r--r-- | arch/arm/boards/omap/board-omap3evm.c (renamed from board/omap/board-omap3evm.c) | 4 | ||||
-rw-r--r-- | arch/arm/boards/omap/board-sdp343x.c (renamed from board/omap/board-sdp343x.c) | 2 | ||||
-rw-r--r-- | arch/arm/boards/omap/board.h (renamed from board/omap/board.h) | 2 | ||||
-rw-r--r-- | arch/arm/boards/omap/config.h (renamed from board/omap/config.h) | 7 | ||||
-rw-r--r-- | arch/arm/boards/omap/devices-gpmc-nand.c (renamed from board/omap/devices-gpmc-nand.c) | 65 | ||||
-rw-r--r-- | arch/arm/boards/omap/env/bin/init (renamed from board/omap/env/bin/init) | 0 | ||||
-rw-r--r-- | arch/arm/boards/omap/platform.S (renamed from board/omap/platform.S) | 2 | ||||
-rw-r--r-- | arch/arm/boards/pcm037/Makefile (renamed from board/pcm037/Makefile) | 0 | ||||
-rw-r--r-- | arch/arm/boards/pcm037/config.h (renamed from board/pcm037/config.h) | 0 | ||||
-rw-r--r-- | arch/arm/boards/pcm037/env/config (renamed from board/pcm037/env/config) | 0 | ||||
-rw-r--r-- | arch/arm/boards/pcm037/lowlevel_init.S (renamed from board/pcm037/lowlevel_init.S) | 0 | ||||
-rw-r--r-- | arch/arm/boards/pcm037/pcm037.c (renamed from board/pcm037/pcm037.c) | 0 | ||||
-rw-r--r-- | arch/arm/boards/pcm037/pcm037.dox (renamed from board/pcm037/pcm037.dox) | 0 | ||||
-rw-r--r-- | arch/arm/boards/pcm038/Makefile (renamed from board/pcm038/Makefile) | 0 | ||||
-rw-r--r-- | arch/arm/boards/pcm038/config.h (renamed from board/phycard-i.MX27/config.h) | 0 | ||||
-rw-r--r-- | arch/arm/boards/pcm038/env/config (renamed from board/pcm038/env/config) | 0 | ||||
-rw-r--r-- | arch/arm/boards/pcm038/lowlevel.c | 117 | ||||
-rw-r--r-- | arch/arm/boards/pcm038/pcm038.c (renamed from board/pcm038/pcm038.c) | 0 | ||||
-rw-r--r-- | arch/arm/boards/pcm038/pcm038.dox (renamed from board/pcm038/pcm038.dox) | 0 | ||||
-rw-r--r-- | arch/arm/boards/pcm038/pll_init.S (renamed from board/pcm038/pll_init.S) | 0 | ||||
-rw-r--r-- | arch/arm/boards/pcm043/Makefile (renamed from board/pcm043/Makefile) | 0 | ||||
-rw-r--r-- | arch/arm/boards/pcm043/config.h (renamed from board/pcm043/config.h) | 0 | ||||
-rw-r--r-- | arch/arm/boards/pcm043/env/config (renamed from board/pcm043/env/config) | 0 | ||||
-rw-r--r-- | arch/arm/boards/pcm043/lowlevel.c (renamed from board/pcm043/lowlevel.c) | 0 | ||||
-rw-r--r-- | arch/arm/boards/pcm043/pcm043.c (renamed from board/pcm043/pcm043.c) | 0 | ||||
-rw-r--r-- | arch/arm/boards/pcm043/pcm043.dox (renamed from board/pcm043/pcm043.dox) | 0 | ||||
-rw-r--r-- | arch/arm/boards/phycard-i.MX27/Makefile (renamed from board/phycard-i.MX27/Makefile) | 0 | ||||
-rw-r--r-- | arch/arm/boards/phycard-i.MX27/config.h | 26 | ||||
-rw-r--r-- | arch/arm/boards/phycard-i.MX27/env/config (renamed from board/phycard-i.MX27/env/config) | 0 | ||||
-rw-r--r-- | arch/arm/boards/phycard-i.MX27/lowlevel_init.S (renamed from board/phycard-i.MX27/lowlevel_init.S) | 0 | ||||
-rw-r--r-- | arch/arm/boards/phycard-i.MX27/pca100.c (renamed from board/phycard-i.MX27/pca100.c) | 0 | ||||
-rw-r--r-- | arch/arm/boards/phycard-i.MX27/pca100.dox (renamed from board/phycard-i.MX27/pca100.dox) | 0 | ||||
-rw-r--r-- | arch/arm/boards/pm9263/Makefile (renamed from board/pm9263/Makefile) | 0 | ||||
-rw-r--r-- | arch/arm/boards/pm9263/config.h (renamed from board/pm9263/config.h) | 0 | ||||
-rw-r--r-- | arch/arm/boards/pm9263/env/bin/_update (renamed from board/mmccpu/env/bin/_update) | 0 | ||||
-rw-r--r-- | arch/arm/boards/pm9263/env/bin/boot (renamed from board/pm9263/env/bin/boot) | 0 | ||||
-rw-r--r-- | arch/arm/boards/pm9263/env/bin/hush_hack (renamed from board/pm9263/env/bin/hush_hack) | 0 | ||||
-rw-r--r-- | arch/arm/boards/pm9263/env/bin/init (renamed from board/pm9263/env/bin/init) | 0 | ||||
-rw-r--r-- | arch/arm/boards/pm9263/env/bin/update_kernel (renamed from board/pm9263/env/bin/update_kernel) | 0 | ||||
-rw-r--r-- | arch/arm/boards/pm9263/env/bin/update_root (renamed from board/pm9263/env/bin/update_root) | 0 | ||||
-rw-r--r-- | arch/arm/boards/pm9263/env/config (renamed from board/pm9263/env/config) | 0 | ||||
-rw-r--r-- | arch/arm/boards/pm9263/init.c (renamed from board/pm9263/init.c) | 0 | ||||
-rw-r--r-- | arch/arm/boards/scb9328/Makefile (renamed from board/scb9328/Makefile) | 0 | ||||
-rw-r--r-- | arch/arm/boards/scb9328/config.h (renamed from board/scb9328/config.h) | 0 | ||||
-rw-r--r-- | arch/arm/boards/scb9328/env/bin/init (renamed from board/scb9328/env/bin/init) | 0 | ||||
-rw-r--r-- | arch/arm/boards/scb9328/lowlevel_init.S (renamed from board/scb9328/lowlevel_init.S) | 0 | ||||
-rw-r--r-- | arch/arm/boards/scb9328/scb9328.c (renamed from board/scb9328/scb9328.c) | 0 | ||||
-rw-r--r-- | arch/arm/boards/scb9328/scb9328.dox (renamed from board/scb9328/scb9328.dox) | 0 | ||||
-rw-r--r-- | arch/arm/configs/a9m2410_defconfig | 2 | ||||
-rw-r--r-- | arch/arm/configs/a9m2440_defconfig | 2 | ||||
-rw-r--r-- | arch/arm/configs/at91sam9260ek_defconfig | 2 | ||||
-rw-r--r-- | arch/arm/configs/at91sam9263ek_defconfig | 2 | ||||
-rw-r--r-- | arch/arm/configs/edb93xx_defconfig | 2 | ||||
-rw-r--r-- | arch/arm/configs/eukrea_cpuimx25_defconfig | 13 | ||||
-rw-r--r-- | arch/arm/configs/eukrea_cpuimx27_defconfig | 2 | ||||
-rw-r--r-- | arch/arm/configs/eukrea_cpuimx35_defconfig | 18 | ||||
-rw-r--r-- | arch/arm/configs/freescale_mx25_3stack_defconfig | 2 | ||||
-rw-r--r-- | arch/arm/configs/freescale_mx35_3stack_defconfig | 2 | ||||
-rw-r--r-- | arch/arm/configs/mmccpu_defconfig | 2 | ||||
-rw-r--r-- | arch/arm/configs/mx21ads_defconfig | 2 | ||||
-rw-r--r-- | arch/arm/configs/mx27ads_defconfig | 2 | ||||
-rw-r--r-- | arch/arm/configs/neso_defconfig | 264 | ||||
-rw-r--r-- | arch/arm/configs/pca100_defconfig | 2 | ||||
-rw-r--r-- | arch/arm/configs/pcm037_defconfig | 2 | ||||
-rw-r--r-- | arch/arm/configs/pcm038_defconfig | 2 | ||||
-rw-r--r-- | arch/arm/configs/pcm043_defconfig | 2 | ||||
-rw-r--r-- | arch/arm/configs/pm9263_defconfig | 2 | ||||
-rw-r--r-- | arch/arm/configs/scb9328_defconfig | 2 | ||||
-rw-r--r-- | arch/arm/include/asm/barebox-arm.h | 2 | ||||
-rw-r--r-- | arch/arm/mach-imx/Kconfig | 10 | ||||
-rw-r--r-- | arch/arm/mach-omap/Kconfig | 16 | ||||
-rw-r--r-- | arch/arm/mach-omap/arch-omap.dox | 8 | ||||
-rw-r--r-- | arch/arm/mach-omap/include/mach/gpmc_nand.h | 45 | ||||
-rw-r--r-- | arch/arm/mach-s3c24xx/generic.c | 4 | ||||
-rw-r--r-- | arch/blackfin/Makefile | 2 | ||||
-rw-r--r-- | arch/blackfin/boards/ipe337/Makefile (renamed from board/ipe337/Makefile) | 0 | ||||
-rw-r--r-- | arch/blackfin/boards/ipe337/barebox.lds.S (renamed from board/ipe337/barebox.lds.S) | 0 | ||||
-rw-r--r-- | arch/blackfin/boards/ipe337/cmd_alternate.c (renamed from board/ipe337/cmd_alternate.c) | 0 | ||||
-rw-r--r-- | arch/blackfin/boards/ipe337/config.h (renamed from board/ipe337/config.h) | 0 | ||||
-rw-r--r-- | arch/blackfin/boards/ipe337/env/bin/_alternate (renamed from board/ipe337/env/bin/_alternate) | 0 | ||||
-rw-r--r-- | arch/blackfin/boards/ipe337/env/bin/_update (renamed from board/ipe337/env/bin/_update) | 0 | ||||
-rw-r--r-- | arch/blackfin/boards/ipe337/env/bin/boot (renamed from board/ipe337/env/bin/boot) | 0 | ||||
-rw-r--r-- | arch/blackfin/boards/ipe337/env/bin/init (renamed from board/ipe337/env/bin/init) | 0 | ||||
-rw-r--r-- | arch/blackfin/boards/ipe337/env/bin/magic.bin (renamed from board/ipe337/env/bin/magic.bin) | 0 | ||||
-rw-r--r-- | arch/blackfin/boards/ipe337/env/bin/reset_ageing (renamed from board/ipe337/env/bin/reset_ageing) | 0 | ||||
-rw-r--r-- | arch/blackfin/boards/ipe337/env/bin/update_application (renamed from board/ipe337/env/bin/update_application) | 0 | ||||
-rw-r--r-- | arch/blackfin/boards/ipe337/env/bin/update_bareboxenv (renamed from board/ipe337/env/bin/update_bareboxenv) | 0 | ||||
-rw-r--r-- | arch/blackfin/boards/ipe337/env/bin/update_kernel (renamed from board/ipe337/env/bin/update_kernel) | 0 | ||||
-rw-r--r-- | arch/blackfin/boards/ipe337/env/bin/update_persistent (renamed from board/ipe337/env/bin/update_persistent) | 0 | ||||
-rw-r--r-- | arch/blackfin/boards/ipe337/env/bin/update_system (renamed from board/ipe337/env/bin/update_system) | 0 | ||||
-rw-r--r-- | arch/blackfin/boards/ipe337/env/config (renamed from board/ipe337/env/config) | 0 | ||||
-rw-r--r-- | arch/blackfin/boards/ipe337/ipe337.c (renamed from board/ipe337/ipe337.c) | 0 | ||||
-rw-r--r-- | arch/blackfin/boards/ipe337/ipe337.dox (renamed from board/ipe337/ipe337.dox) | 0 | ||||
-rw-r--r-- | arch/blackfin/configs/ipe337_defconfig | 2 | ||||
-rw-r--r-- | arch/m68k/Makefile | 2 | ||||
-rw-r--r-- | arch/m68k/boards/kp_ukd_r1_num/Makefile (renamed from board/kp_ukd_r1_num/Makefile) | 0 | ||||
-rw-r--r-- | arch/m68k/boards/kp_ukd_r1_num/env/bin/_update (renamed from board/phycore_mcf54xx/env/bin/_update) | 0 | ||||
-rw-r--r-- | arch/m68k/boards/kp_ukd_r1_num/env/bin/boot (renamed from board/kp_ukd_r1_num/env/bin/boot) | 0 | ||||
-rw-r--r-- | arch/m68k/boards/kp_ukd_r1_num/env/bin/init (renamed from board/kp_ukd_r1_num/env/bin/init) | 0 | ||||
-rw-r--r-- | arch/m68k/boards/kp_ukd_r1_num/env/bin/pcidmaloop (renamed from board/kp_ukd_r1_num/env/bin/pcidmaloop) | 0 | ||||
-rw-r--r-- | arch/m68k/boards/kp_ukd_r1_num/env/bin/pciloop (renamed from board/kp_ukd_r1_num/env/bin/pciloop) | 0 | ||||
-rw-r--r-- | arch/m68k/boards/kp_ukd_r1_num/env/bin/update_kernel (renamed from board/kp_ukd_r1_num/env/bin/update_kernel) | 0 | ||||
-rw-r--r-- | arch/m68k/boards/kp_ukd_r1_num/env/bin/update_root (renamed from board/kp_ukd_r1_num/env/bin/update_root) | 0 | ||||
-rw-r--r-- | arch/m68k/boards/kp_ukd_r1_num/env/config (renamed from board/kp_ukd_r1_num/env/config) | 0 | ||||
-rw-r--r-- | arch/m68k/boards/kp_ukd_r1_num/highlevel_init.c (renamed from board/kp_ukd_r1_num/highlevel_init.c) | 0 | ||||
-rw-r--r-- | arch/m68k/boards/kp_ukd_r1_num/kp_ukd_r1_num.c (renamed from board/kp_ukd_r1_num/kp_ukd_r1_num.c) | 0 | ||||
-rw-r--r-- | arch/m68k/boards/kp_ukd_r1_num/kp_ukd_r1_num.dox (renamed from board/kp_ukd_r1_num/kp_ukd_r1_num.dox) | 0 | ||||
-rw-r--r-- | arch/m68k/boards/kp_ukd_r1_num/lowlevel_init.c (renamed from board/kp_ukd_r1_num/lowlevel_init.c) | 0 | ||||
-rw-r--r-- | arch/m68k/boards/kp_ukd_r1_num/pci-stubs.c (renamed from board/kp_ukd_r1_num/pci-stubs.c) | 0 | ||||
-rw-r--r-- | arch/m68k/boards/phycore_mcf54xx/Makefile (renamed from board/phycore_mcf54xx/Makefile) | 0 | ||||
-rw-r--r-- | arch/m68k/boards/phycore_mcf54xx/env/bin/_update (renamed from board/pm9263/env/bin/_update) | 0 | ||||
-rw-r--r-- | arch/m68k/boards/phycore_mcf54xx/env/bin/boot (renamed from board/phycore_mcf54xx/env/bin/boot) | 0 | ||||
-rw-r--r-- | arch/m68k/boards/phycore_mcf54xx/env/bin/init (renamed from board/phycore_mcf54xx/env/bin/init) | 0 | ||||
-rw-r--r-- | arch/m68k/boards/phycore_mcf54xx/env/bin/pcidmaloop (renamed from board/phycore_mcf54xx/env/bin/pcidmaloop) | 0 | ||||
-rw-r--r-- | arch/m68k/boards/phycore_mcf54xx/env/bin/pciloop (renamed from board/phycore_mcf54xx/env/bin/pciloop) | 0 | ||||
-rw-r--r-- | arch/m68k/boards/phycore_mcf54xx/env/bin/update_kernel (renamed from board/phycore_mcf54xx/env/bin/update_kernel) | 0 | ||||
-rw-r--r-- | arch/m68k/boards/phycore_mcf54xx/env/bin/update_root (renamed from board/phycore_mcf54xx/env/bin/update_root) | 0 | ||||
-rw-r--r-- | arch/m68k/boards/phycore_mcf54xx/env/config (renamed from board/phycore_mcf54xx/env/config) | 0 | ||||
-rw-r--r-- | arch/m68k/boards/phycore_mcf54xx/highlevel_init.c (renamed from board/phycore_mcf54xx/highlevel_init.c) | 0 | ||||
-rw-r--r-- | arch/m68k/boards/phycore_mcf54xx/lowlevel_init.c (renamed from board/phycore_mcf54xx/lowlevel_init.c) | 0 | ||||
-rw-r--r-- | arch/m68k/boards/phycore_mcf54xx/pci-stubs.c (renamed from board/phycore_mcf54xx/pci-stubs.c) | 0 | ||||
-rw-r--r-- | arch/m68k/boards/phycore_mcf54xx/phyCore_MCF54xx.c (renamed from board/phycore_mcf54xx/phyCore_MCF54xx.c) | 0 | ||||
-rw-r--r-- | arch/m68k/boards/phycore_mcf54xx/phyCore_MCF54xx.dox (renamed from board/phycore_mcf54xx/phyCore_MCF54xx.dox) | 0 | ||||
-rw-r--r-- | arch/m68k/configs/phycore_kpukdr1_5475num_defconfig | 2 | ||||
-rw-r--r-- | arch/m68k/configs/phycore_mcf54xx_defconfig | 2 | ||||
-rw-r--r-- | arch/ppc/Makefile | 2 | ||||
-rw-r--r-- | arch/ppc/boards/pcm030/Makefile (renamed from board/pcm030/Makefile) | 0 | ||||
-rw-r--r-- | arch/ppc/boards/pcm030/barebox.lds.S (renamed from board/pcm030/barebox.lds.S) | 0 | ||||
-rw-r--r-- | arch/ppc/boards/pcm030/config.h (renamed from board/pcm030/config.h) | 0 | ||||
-rw-r--r-- | arch/ppc/boards/pcm030/mt46v32m16-75.h (renamed from board/pcm030/mt46v32m16-75.h) | 0 | ||||
-rw-r--r-- | arch/ppc/boards/pcm030/pcm030.c (renamed from board/pcm030/pcm030.c) | 0 | ||||
-rw-r--r-- | arch/ppc/boards/pcm030/pcm030.dox (renamed from board/pcm030/pcm030.dox) | 0 | ||||
-rw-r--r-- | arch/sandbox/Makefile | 10 | ||||
-rw-r--r-- | arch/sandbox/board/.gitignore (renamed from board/sandbox/.gitignore) | 0 | ||||
-rw-r--r-- | arch/sandbox/board/Makefile (renamed from board/sandbox/Makefile) | 0 | ||||
-rw-r--r-- | arch/sandbox/board/barebox.lds.S (renamed from board/sandbox/barebox.lds.S) | 0 | ||||
-rw-r--r-- | arch/sandbox/board/board.c (renamed from board/sandbox/board.c) | 0 | ||||
-rw-r--r-- | arch/sandbox/board/clock.c (renamed from board/sandbox/clock.c) | 0 | ||||
-rw-r--r-- | arch/sandbox/board/config.h (renamed from board/sandbox/config.h) | 0 | ||||
-rw-r--r-- | arch/sandbox/board/console.c (renamed from board/sandbox/console.c) | 0 | ||||
-rw-r--r-- | arch/sandbox/board/env/bin/init (renamed from board/sandbox/env/bin/init) | 0 | ||||
-rw-r--r-- | arch/sandbox/board/env/config (renamed from board/sandbox/env/config) | 0 | ||||
-rw-r--r-- | arch/sandbox/board/hostfile.c (renamed from board/sandbox/hostfile.c) | 0 | ||||
-rw-r--r-- | arch/sandbox/configs/sandbox_defconfig | 2 | ||||
-rw-r--r-- | arch/x86/Makefile | 2 | ||||
-rw-r--r-- | arch/x86/boards/x86_generic/Makefile (renamed from board/x86_generic/Makefile) | 0 | ||||
-rw-r--r-- | arch/x86/boards/x86_generic/config.h (renamed from board/x86_generic/config.h) | 0 | ||||
-rw-r--r-- | arch/x86/boards/x86_generic/env/bin/boot (renamed from board/x86_generic/env/bin/boot) | 0 | ||||
-rw-r--r-- | arch/x86/boards/x86_generic/env/bin/init (renamed from board/x86_generic/env/bin/init) | 0 | ||||
-rw-r--r-- | arch/x86/boards/x86_generic/env/config (renamed from board/x86_generic/env/config) | 0 | ||||
-rw-r--r-- | arch/x86/boards/x86_generic/generic_pc.c (renamed from board/x86_generic/generic_pc.c) | 0 | ||||
-rw-r--r-- | arch/x86/configs/generic_defconfig | 2 | ||||
-rw-r--r-- | board/board.dox | 16 | ||||
-rw-r--r-- | board/eukrea_cpuimx35/flash_header.c | 60 | ||||
-rw-r--r-- | board/omap/board-beagle.c | 270 | ||||
-rw-r--r-- | commands/Kconfig | 6 | ||||
-rw-r--r-- | commands/Makefile | 1 | ||||
-rw-r--r-- | commands/ubi.c | 129 | ||||
-rw-r--r-- | common/Makefile | 8 | ||||
-rw-r--r-- | drivers/Kconfig | 2 | ||||
-rw-r--r-- | drivers/Makefile | 2 | ||||
-rw-r--r-- | drivers/mtd/Kconfig | 9 | ||||
-rw-r--r-- | drivers/mtd/Makefile | 3 | ||||
-rw-r--r-- | drivers/mtd/nand/Kconfig (renamed from drivers/nand/Kconfig) | 10 | ||||
-rw-r--r-- | drivers/mtd/nand/Makefile (renamed from drivers/nand/Makefile) | 0 | ||||
-rw-r--r-- | drivers/mtd/nand/atmel_nand.c (renamed from drivers/nand/atmel_nand.c) | 4 | ||||
-rw-r--r-- | drivers/mtd/nand/atmel_nand_ecc.h (renamed from drivers/nand/atmel_nand_ecc.h) | 0 | ||||
-rw-r--r-- | drivers/mtd/nand/diskonchip.c (renamed from drivers/nand/diskonchip.c) | 0 | ||||
-rw-r--r-- | drivers/mtd/nand/nand.c (renamed from drivers/nand/nand.c) | 2 | ||||
-rw-r--r-- | drivers/mtd/nand/nand_base.c (renamed from drivers/nand/nand_base.c) | 0 | ||||
-rw-r--r-- | drivers/mtd/nand/nand_bbt.c (renamed from drivers/nand/nand_bbt.c) | 0 | ||||
-rw-r--r-- | drivers/mtd/nand/nand_ecc.c (renamed from drivers/nand/nand_ecc.c) | 0 | ||||
-rw-r--r-- | drivers/mtd/nand/nand_ids.c (renamed from drivers/nand/nand_ids.c) | 0 | ||||
-rw-r--r-- | drivers/mtd/nand/nand_imx.c (renamed from drivers/nand/nand_imx.c) | 0 | ||||
-rw-r--r-- | drivers/mtd/nand/nand_omap_gpmc.c (renamed from drivers/nand/nand_omap_gpmc.c) | 103 | ||||
-rw-r--r-- | drivers/mtd/nand/nand_s3c2410.c (renamed from drivers/nand/nand_s3c2410.c) | 0 | ||||
-rw-r--r-- | drivers/mtd/nand/nand_util.c (renamed from drivers/nand/nand_util.c) | 0 | ||||
-rw-r--r-- | drivers/mtd/partition.c | 143 | ||||
-rw-r--r-- | drivers/mtd/ubi/Kconfig | 6 | ||||
-rw-r--r-- | drivers/mtd/ubi/Makefile | 3 | ||||
-rw-r--r-- | drivers/mtd/ubi/build.c | 1059 | ||||
-rw-r--r-- | drivers/mtd/ubi/cdev.c | 238 | ||||
-rw-r--r-- | drivers/mtd/ubi/crc32defs.h | 32 | ||||
-rw-r--r-- | drivers/mtd/ubi/debug.c | 192 | ||||
-rw-r--r-- | drivers/mtd/ubi/debug.h | 152 | ||||
-rw-r--r-- | drivers/mtd/ubi/eba.c | 1256 | ||||
-rw-r--r-- | drivers/mtd/ubi/io.c | 1274 | ||||
-rw-r--r-- | drivers/mtd/ubi/kapi.c | 638 | ||||
-rw-r--r-- | drivers/mtd/ubi/misc.c | 106 | ||||
-rw-r--r-- | drivers/mtd/ubi/scan.c | 1362 | ||||
-rw-r--r-- | drivers/mtd/ubi/scan.h | 165 | ||||
-rw-r--r-- | drivers/mtd/ubi/ubi-barebox.h | 191 | ||||
-rw-r--r-- | drivers/mtd/ubi/ubi-media.h | 372 | ||||
-rw-r--r-- | drivers/mtd/ubi/ubi.h | 648 | ||||
-rw-r--r-- | drivers/mtd/ubi/upd.c | 445 | ||||
-rw-r--r-- | drivers/mtd/ubi/vmt.c | 866 | ||||
-rw-r--r-- | drivers/mtd/ubi/vtbl.c | 837 | ||||
-rw-r--r-- | drivers/mtd/ubi/wl.c | 1675 | ||||
-rw-r--r-- | drivers/nor/cfi_flash.c | 143 | ||||
-rw-r--r-- | drivers/nor/cfi_flash.h | 55 | ||||
-rw-r--r-- | drivers/nor/cfi_flash_amd.c | 14 | ||||
-rw-r--r-- | drivers/nor/cfi_flash_intel.c | 12 | ||||
-rw-r--r-- | drivers/usb/Kconfig | 10 | ||||
-rw-r--r-- | drivers/usb/Makefile | 7 | ||||
-rw-r--r-- | drivers/usb/core/Makefile | 2 | ||||
-rw-r--r-- | drivers/usb/core/usb.c (renamed from drivers/usb/usb.c) | 0 | ||||
-rw-r--r-- | drivers/usb/host/Kconfig | 2 | ||||
-rw-r--r-- | drivers/usb/host/Makefile | 1 | ||||
-rw-r--r-- | drivers/usb/host/ehci-core.h (renamed from drivers/usb/usb_ehci_core.h) | 0 | ||||
-rw-r--r-- | drivers/usb/host/ehci-hcd.c (renamed from drivers/usb/usb_ehci_core.c) | 2 | ||||
-rw-r--r-- | drivers/usb/host/ehci.h (renamed from drivers/usb/usb_ehci.h) | 0 | ||||
-rw-r--r-- | drivers/usb/otg/Kconfig | 6 | ||||
-rw-r--r-- | drivers/usb/otg/Makefile | 2 | ||||
-rw-r--r-- | drivers/usb/otg/isp1504.c (renamed from drivers/usb/isp1504.c) | 0 | ||||
-rw-r--r-- | drivers/usb/otg/ulpi.c (renamed from drivers/usb/ulpi.c) | 0 | ||||
-rw-r--r-- | fs/Kconfig | 3 | ||||
-rw-r--r-- | fs/devfs.c | 46 | ||||
-rw-r--r-- | fs/fs.c | 34 | ||||
-rw-r--r-- | include/common.h | 4 | ||||
-rw-r--r-- | include/driver.h | 1 | ||||
-rw-r--r-- | include/linux/mtd/mtd-abi.h | 1 | ||||
-rw-r--r-- | include/linux/mtd/mtd.h | 13 | ||||
-rw-r--r-- | include/linux/mtd/ubi.h | 186 | ||||
-rw-r--r-- | include/linux/rbtree.h | 160 | ||||
-rw-r--r-- | include/mtd/ubi-user.h | 300 | ||||
-rw-r--r-- | lib/Makefile | 1 | ||||
-rw-r--r-- | lib/crc32.c | 11 | ||||
-rw-r--r-- | lib/rbtree.c | 389 | ||||
-rwxr-xr-x | scripts/genenv | 8 |
400 files changed, 14595 insertions, 782 deletions
diff --git a/Documentation/barebox-main.dox b/Documentation/barebox-main.dox index 01c45c1d26..fb780e6364 100644 --- a/Documentation/barebox-main.dox +++ b/Documentation/barebox-main.dox @@ -117,9 +117,9 @@ If everything goes well, the result is a file called @p barebox: @a barebox usually needs an environment for storing the configuration data. You can generate an environment using the example environment contained -in board/sandbox/env: +in arch/sanbox/board/env: -@code # ./scripts/bareboxenv -s -p 0x10000 board/sandbox/env/ env.bin @endcode +@code # ./scripts/bareboxenv -s -p 0x10000 arch/sanbox/board/env/ env.bin @endcode To get some files to play with you can generate a cramfs image: @@ -851,11 +851,11 @@ include/asm: $(Q)$(create-symlink) include/config.h: include/config/auto.conf - @echo ' SYMLINK $@ -> board/$(board-y)/config.h' + @echo ' SYMLINK $@ -> $(BOARD)/config.h' ifneq ($(KBUILD_SRC),) - $(Q)ln -fsn $(srctree)/board/$(board-y)/config.h $@ + $(Q)ln -fsn $(srctree)/$(BOARD)/config.h $@ else - @ln -fsn ../board/$(board-y)/config.h $@ + @ln -fsn ../$(BOARD)/config.h $@ endif # Generate some files @@ -33,7 +33,6 @@ TODO [-] Cleanup cpu/*. Many functions there are not cpu specific. For example the cache functions for arm are common for most arm processors. (done for ARM) (I will check this for m68k arch - csc 21.03.2008 19:56:24) -[ ] Board support should go to arch/*/boards/* [ ] Move SoC specific header files from include/ to include/asm/arch/ [ ] Several .c/.h files do not have GNU/copyright headers. [ ] The cramfs driver currently uses direct memory accesses instead of read(). @@ -108,4 +107,4 @@ DONE be any key, ctrl-c or a certain string. Maybe like this: countdown -m msg -t timeout -x [ctrl-c|anykey|string] If done, remove the corresponding stuff from common/main.c - +[X] Board support should go to arch/*/boards/* diff --git a/arch/arm/Makefile b/arch/arm/Makefile index 1c9f24e7db..8b4d64c268 100644 --- a/arch/arm/Makefile +++ b/arch/arm/Makefile @@ -77,6 +77,7 @@ board-$(CONFIG_MACH_PCM038) := pcm038 board-$(CONFIG_MACH_PCM043) := pcm043 board-$(CONFIG_MACH_PM9263) := pm9263 board-$(CONFIG_MACH_SCB9328) := scb9328 +board-$(CONFIG_MACH_NESO) := guf-neso machdirs := $(patsubst %,arch/arm/mach-%/,$(machine-y)) @@ -117,7 +118,7 @@ maketools: PHONY += maketools ifneq ($(board-y),) -BOARD := board/$(board-y)/ +BOARD := arch/arm/boards/$(board-y)/ else BOARD := endif diff --git a/board/a9m2410/Makefile b/arch/arm/boards/a9m2410/Makefile index 63026f08b2..63026f08b2 100644 --- a/board/a9m2410/Makefile +++ b/arch/arm/boards/a9m2410/Makefile diff --git a/board/a9m2410/a9m2410.c b/arch/arm/boards/a9m2410/a9m2410.c index f327f82b7f..f327f82b7f 100644 --- a/board/a9m2410/a9m2410.c +++ b/arch/arm/boards/a9m2410/a9m2410.c diff --git a/board/a9m2410/config.h b/arch/arm/boards/a9m2410/config.h index 87b05fc55d..87b05fc55d 100644 --- a/board/a9m2410/config.h +++ b/arch/arm/boards/a9m2410/config.h diff --git a/board/a9m2410/env/bin/_update b/arch/arm/boards/a9m2410/env/bin/_update index 014bce3512..014bce3512 100644 --- a/board/a9m2410/env/bin/_update +++ b/arch/arm/boards/a9m2410/env/bin/_update diff --git a/board/a9m2410/env/bin/boot b/arch/arm/boards/a9m2410/env/bin/boot index 59fa60e4e9..59fa60e4e9 100644 --- a/board/a9m2410/env/bin/boot +++ b/arch/arm/boards/a9m2410/env/bin/boot diff --git a/board/a9m2410/env/bin/hush_hack b/arch/arm/boards/a9m2410/env/bin/hush_hack index 5fffa92ecd..5fffa92ecd 100644 --- a/board/a9m2410/env/bin/hush_hack +++ b/arch/arm/boards/a9m2410/env/bin/hush_hack diff --git a/board/a9m2410/env/bin/init b/arch/arm/boards/a9m2410/env/bin/init index 5ae44dd455..5ae44dd455 100644 --- a/board/a9m2410/env/bin/init +++ b/arch/arm/boards/a9m2410/env/bin/init diff --git a/board/a9m2410/env/bin/update_kernel b/arch/arm/boards/a9m2410/env/bin/update_kernel index c43a55785b..c43a55785b 100644 --- a/board/a9m2410/env/bin/update_kernel +++ b/arch/arm/boards/a9m2410/env/bin/update_kernel diff --git a/board/a9m2410/env/bin/update_root b/arch/arm/boards/a9m2410/env/bin/update_root index 34139e5dce..34139e5dce 100644 --- a/board/a9m2410/env/bin/update_root +++ b/arch/arm/boards/a9m2410/env/bin/update_root diff --git a/board/a9m2410/env/config b/arch/arm/boards/a9m2410/env/config index 79150ce017..79150ce017 100644 --- a/board/a9m2410/env/config +++ b/arch/arm/boards/a9m2410/env/config diff --git a/board/a9m2410/lowlevel_init.S b/arch/arm/boards/a9m2410/lowlevel_init.S index 461b93c3f1..461b93c3f1 100644 --- a/board/a9m2410/lowlevel_init.S +++ b/arch/arm/boards/a9m2410/lowlevel_init.S diff --git a/board/a9m2440/Makefile b/arch/arm/boards/a9m2440/Makefile index 779e83dc03..779e83dc03 100644 --- a/board/a9m2440/Makefile +++ b/arch/arm/boards/a9m2440/Makefile diff --git a/board/a9m2440/a9m2410dev.c b/arch/arm/boards/a9m2440/a9m2410dev.c index 1220bd9777..1220bd9777 100644 --- a/board/a9m2440/a9m2410dev.c +++ b/arch/arm/boards/a9m2440/a9m2410dev.c diff --git a/board/a9m2440/a9m2440.c b/arch/arm/boards/a9m2440/a9m2440.c index 2567f5ed13..2567f5ed13 100644 --- a/board/a9m2440/a9m2440.c +++ b/arch/arm/boards/a9m2440/a9m2440.c diff --git a/board/a9m2440/baseboards.h b/arch/arm/boards/a9m2440/baseboards.h index ec80312b69..ec80312b69 100644 --- a/board/a9m2440/baseboards.h +++ b/arch/arm/boards/a9m2440/baseboards.h diff --git a/board/a9m2440/config.h b/arch/arm/boards/a9m2440/config.h index 43cb6ab934..43cb6ab934 100644 --- a/board/a9m2440/config.h +++ b/arch/arm/boards/a9m2440/config.h diff --git a/board/a9m2440/env/bin/_update b/arch/arm/boards/a9m2440/env/bin/_update index b10682ece4..b10682ece4 100644 --- a/board/a9m2440/env/bin/_update +++ b/arch/arm/boards/a9m2440/env/bin/_update diff --git a/board/a9m2440/env/bin/boot b/arch/arm/boards/a9m2440/env/bin/boot index 86e22cf9ff..86e22cf9ff 100644 --- a/board/a9m2440/env/bin/boot +++ b/arch/arm/boards/a9m2440/env/bin/boot diff --git a/board/a9m2440/env/bin/hush_hack b/arch/arm/boards/a9m2440/env/bin/hush_hack index 5fffa92ecd..5fffa92ecd 100644 --- a/board/a9m2440/env/bin/hush_hack +++ b/arch/arm/boards/a9m2440/env/bin/hush_hack diff --git a/board/a9m2440/env/bin/init b/arch/arm/boards/a9m2440/env/bin/init index 5ae44dd455..5ae44dd455 100644 --- a/board/a9m2440/env/bin/init +++ b/arch/arm/boards/a9m2440/env/bin/init diff --git a/board/a9m2440/env/bin/update_kernel b/arch/arm/boards/a9m2440/env/bin/update_kernel index c43a55785b..c43a55785b 100644 --- a/board/a9m2440/env/bin/update_kernel +++ b/arch/arm/boards/a9m2440/env/bin/update_kernel diff --git a/board/a9m2440/env/bin/update_root b/arch/arm/boards/a9m2440/env/bin/update_root index 46cbca5beb..46cbca5beb 100644 --- a/board/a9m2440/env/bin/update_root +++ b/arch/arm/boards/a9m2440/env/bin/update_root diff --git a/board/a9m2440/env/config b/arch/arm/boards/a9m2440/env/config index 936c35f9a9..936c35f9a9 100644 --- a/board/a9m2440/env/config +++ b/arch/arm/boards/a9m2440/env/config diff --git a/board/a9m2440/lowlevel_init.S b/arch/arm/boards/a9m2440/lowlevel_init.S index 4b5c596076..4b5c596076 100644 --- a/board/a9m2440/lowlevel_init.S +++ b/arch/arm/boards/a9m2440/lowlevel_init.S diff --git a/board/at91sam9260ek/Makefile b/arch/arm/boards/at91sam9260ek/Makefile index 73ef72e210..73ef72e210 100644 --- a/board/at91sam9260ek/Makefile +++ b/arch/arm/boards/at91sam9260ek/Makefile diff --git a/board/at91sam9260ek/config.h b/arch/arm/boards/at91sam9260ek/config.h index afd8563212..afd8563212 100644 --- a/board/at91sam9260ek/config.h +++ b/arch/arm/boards/at91sam9260ek/config.h diff --git a/board/at91sam9260ek/env/bin/_update b/arch/arm/boards/at91sam9260ek/env/bin/_update index 014bce3512..014bce3512 100644 --- a/board/at91sam9260ek/env/bin/_update +++ b/arch/arm/boards/at91sam9260ek/env/bin/_update diff --git a/board/at91sam9260ek/env/bin/boot b/arch/arm/boards/at91sam9260ek/env/bin/boot index ed6f11a108..ed6f11a108 100644 --- a/board/at91sam9260ek/env/bin/boot +++ b/arch/arm/boards/at91sam9260ek/env/bin/boot diff --git a/board/at91sam9260ek/env/bin/init b/arch/arm/boards/at91sam9260ek/env/bin/init index b8d8399842..b8d8399842 100644 --- a/board/at91sam9260ek/env/bin/init +++ b/arch/arm/boards/at91sam9260ek/env/bin/init diff --git a/board/at91sam9260ek/env/bin/pcidmaloop b/arch/arm/boards/at91sam9260ek/env/bin/pcidmaloop index 24e76cbed7..24e76cbed7 100644 --- a/board/at91sam9260ek/env/bin/pcidmaloop +++ b/arch/arm/boards/at91sam9260ek/env/bin/pcidmaloop diff --git a/board/at91sam9260ek/env/bin/pciloop b/arch/arm/boards/at91sam9260ek/env/bin/pciloop index 4a804f9f31..4a804f9f31 100644 --- a/board/at91sam9260ek/env/bin/pciloop +++ b/arch/arm/boards/at91sam9260ek/env/bin/pciloop diff --git a/board/at91sam9260ek/env/bin/update_kernel b/arch/arm/boards/at91sam9260ek/env/bin/update_kernel index 1ad95fc5d6..1ad95fc5d6 100644 --- a/board/at91sam9260ek/env/bin/update_kernel +++ b/arch/arm/boards/at91sam9260ek/env/bin/update_kernel diff --git a/board/at91sam9260ek/env/bin/update_root b/arch/arm/boards/at91sam9260ek/env/bin/update_root index b757a5b922..b757a5b922 100644 --- a/board/at91sam9260ek/env/bin/update_root +++ b/arch/arm/boards/at91sam9260ek/env/bin/update_root diff --git a/board/at91sam9260ek/env/config b/arch/arm/boards/at91sam9260ek/env/config index 71d6f88cfe..71d6f88cfe 100644 --- a/board/at91sam9260ek/env/config +++ b/arch/arm/boards/at91sam9260ek/env/config diff --git a/board/at91sam9260ek/init.c b/arch/arm/boards/at91sam9260ek/init.c index 9fd75256c6..9fd75256c6 100644 --- a/board/at91sam9260ek/init.c +++ b/arch/arm/boards/at91sam9260ek/init.c diff --git a/board/at91sam9260ek/lowlevel_init.S b/arch/arm/boards/at91sam9260ek/lowlevel_init.S index 4961682322..4961682322 100644 --- a/board/at91sam9260ek/lowlevel_init.S +++ b/arch/arm/boards/at91sam9260ek/lowlevel_init.S diff --git a/board/at91sam9263ek/Makefile b/arch/arm/boards/at91sam9263ek/Makefile index eb072c0161..eb072c0161 100644 --- a/board/at91sam9263ek/Makefile +++ b/arch/arm/boards/at91sam9263ek/Makefile diff --git a/board/at91sam9263ek/config.h b/arch/arm/boards/at91sam9263ek/config.h index 9cc8af2e1d..9cc8af2e1d 100644 --- a/board/at91sam9263ek/config.h +++ b/arch/arm/boards/at91sam9263ek/config.h diff --git a/board/at91sam9263ek/env/bin/_update b/arch/arm/boards/at91sam9263ek/env/bin/_update index 014bce3512..014bce3512 100644 --- a/board/at91sam9263ek/env/bin/_update +++ b/arch/arm/boards/at91sam9263ek/env/bin/_update diff --git a/board/at91sam9263ek/env/bin/boot b/arch/arm/boards/at91sam9263ek/env/bin/boot index 533dea7618..533dea7618 100644 --- a/board/at91sam9263ek/env/bin/boot +++ b/arch/arm/boards/at91sam9263ek/env/bin/boot diff --git a/board/at91sam9263ek/env/bin/hush_hack b/arch/arm/boards/at91sam9263ek/env/bin/hush_hack index 5fffa92ecd..5fffa92ecd 100644 --- a/board/at91sam9263ek/env/bin/hush_hack +++ b/arch/arm/boards/at91sam9263ek/env/bin/hush_hack diff --git a/board/at91sam9263ek/env/bin/init b/arch/arm/boards/at91sam9263ek/env/bin/init index eaa298d651..eaa298d651 100644 --- a/board/at91sam9263ek/env/bin/init +++ b/arch/arm/boards/at91sam9263ek/env/bin/init diff --git a/board/at91sam9263ek/env/bin/update_barebox_xmodem b/arch/arm/boards/at91sam9263ek/env/bin/update_barebox_xmodem index 39818b585c..39818b585c 100644 --- a/board/at91sam9263ek/env/bin/update_barebox_xmodem +++ b/arch/arm/boards/at91sam9263ek/env/bin/update_barebox_xmodem diff --git a/board/at91sam9263ek/env/bin/update_kernel b/arch/arm/boards/at91sam9263ek/env/bin/update_kernel index 05c822d860..05c822d860 100644 --- a/board/at91sam9263ek/env/bin/update_kernel +++ b/arch/arm/boards/at91sam9263ek/env/bin/update_kernel diff --git a/board/at91sam9263ek/env/bin/update_root b/arch/arm/boards/at91sam9263ek/env/bin/update_root index a75137237b..a75137237b 100644 --- a/board/at91sam9263ek/env/bin/update_root +++ b/arch/arm/boards/at91sam9263ek/env/bin/update_root diff --git a/board/at91sam9263ek/env/config b/arch/arm/boards/at91sam9263ek/env/config index 4b322ad4df..4b322ad4df 100644 --- a/board/at91sam9263ek/env/config +++ b/arch/arm/boards/at91sam9263ek/env/config diff --git a/board/at91sam9263ek/init.c b/arch/arm/boards/at91sam9263ek/init.c index 21803ca3b5..21803ca3b5 100644 --- a/board/at91sam9263ek/init.c +++ b/arch/arm/boards/at91sam9263ek/init.c diff --git a/board/edb93xx/Makefile b/arch/arm/boards/edb93xx/Makefile index e19cd7b4c2..e19cd7b4c2 100644 --- a/board/edb93xx/Makefile +++ b/arch/arm/boards/edb93xx/Makefile diff --git a/board/edb93xx/config.h b/arch/arm/boards/edb93xx/config.h index 6ae9a40e19..6ae9a40e19 100644 --- a/board/edb93xx/config.h +++ b/arch/arm/boards/edb93xx/config.h diff --git a/board/edb93xx/early_udelay.h b/arch/arm/boards/edb93xx/early_udelay.h index 185283d98d..185283d98d 100644 --- a/board/edb93xx/early_udelay.h +++ b/arch/arm/boards/edb93xx/early_udelay.h diff --git a/board/edb93xx/edb93xx.c b/arch/arm/boards/edb93xx/edb93xx.c index b0078a5227..b0078a5227 100644 --- a/board/edb93xx/edb93xx.c +++ b/arch/arm/boards/edb93xx/edb93xx.c diff --git a/board/edb93xx/edb93xx.dox b/arch/arm/boards/edb93xx/edb93xx.dox index 3964d55367..3964d55367 100644 --- a/board/edb93xx/edb93xx.dox +++ b/arch/arm/boards/edb93xx/edb93xx.dox diff --git a/board/edb93xx/edb93xx.h b/arch/arm/boards/edb93xx/edb93xx.h index 5e5c6f518c..5e5c6f518c 100644 --- a/board/edb93xx/edb93xx.h +++ b/arch/arm/boards/edb93xx/edb93xx.h diff --git a/board/edb93xx/env/bin/boot b/arch/arm/boards/edb93xx/env/bin/boot index 143f3d018d..143f3d018d 100644 --- a/board/edb93xx/env/bin/boot +++ b/arch/arm/boards/edb93xx/env/bin/boot diff --git a/board/edb93xx/env/bin/flash_partition b/arch/arm/boards/edb93xx/env/bin/flash_partition index ded40aa8a3..ded40aa8a3 100644 --- a/board/edb93xx/env/bin/flash_partition +++ b/arch/arm/boards/edb93xx/env/bin/flash_partition diff --git a/board/edb93xx/env/bin/init b/arch/arm/boards/edb93xx/env/bin/init index c6b5aed271..c6b5aed271 100644 --- a/board/edb93xx/env/bin/init +++ b/arch/arm/boards/edb93xx/env/bin/init diff --git a/board/edb93xx/env/bin/set_nor_parts b/arch/arm/boards/edb93xx/env/bin/set_nor_parts index 38321fa8cc..38321fa8cc 100644 --- a/board/edb93xx/env/bin/set_nor_parts +++ b/arch/arm/boards/edb93xx/env/bin/set_nor_parts diff --git a/board/edb93xx/env/bin/update_kernel b/arch/arm/boards/edb93xx/env/bin/update_kernel index 3e4b9b0b8e..3e4b9b0b8e 100644 --- a/board/edb93xx/env/bin/update_kernel +++ b/arch/arm/boards/edb93xx/env/bin/update_kernel diff --git a/board/edb93xx/env/bin/update_rootfs b/arch/arm/boards/edb93xx/env/bin/update_rootfs index 52a3699fd0..52a3699fd0 100644 --- a/board/edb93xx/env/bin/update_rootfs +++ b/arch/arm/boards/edb93xx/env/bin/update_rootfs diff --git a/board/edb93xx/env/config b/arch/arm/boards/edb93xx/env/config index 47ab209d82..47ab209d82 100644 --- a/board/edb93xx/env/config +++ b/arch/arm/boards/edb93xx/env/config diff --git a/board/edb93xx/flash_cfg.c b/arch/arm/boards/edb93xx/flash_cfg.c index 91a6a4ea96..91a6a4ea96 100644 --- a/board/edb93xx/flash_cfg.c +++ b/arch/arm/boards/edb93xx/flash_cfg.c diff --git a/board/edb93xx/pll_cfg.c b/arch/arm/boards/edb93xx/pll_cfg.c index a687af0a01..a687af0a01 100644 --- a/board/edb93xx/pll_cfg.c +++ b/arch/arm/boards/edb93xx/pll_cfg.c diff --git a/board/edb93xx/pll_cfg.h b/arch/arm/boards/edb93xx/pll_cfg.h index 503507aa17..503507aa17 100644 --- a/board/edb93xx/pll_cfg.h +++ b/arch/arm/boards/edb93xx/pll_cfg.h diff --git a/board/edb93xx/sdram_cfg.c b/arch/arm/boards/edb93xx/sdram_cfg.c index 3d4fe08e19..3d4fe08e19 100644 --- a/board/edb93xx/sdram_cfg.c +++ b/arch/arm/boards/edb93xx/sdram_cfg.c diff --git a/board/edb93xx/sdram_cfg.h b/arch/arm/boards/edb93xx/sdram_cfg.h index c57b76ef18..c57b76ef18 100644 --- a/board/edb93xx/sdram_cfg.h +++ b/arch/arm/boards/edb93xx/sdram_cfg.h diff --git a/board/eukrea_cpuimx25/Makefile b/arch/arm/boards/eukrea_cpuimx25/Makefile index 406c6f32f7..406c6f32f7 100644 --- a/board/eukrea_cpuimx25/Makefile +++ b/arch/arm/boards/eukrea_cpuimx25/Makefile diff --git a/board/eukrea_cpuimx25/config.h b/arch/arm/boards/eukrea_cpuimx25/config.h index efff909eed..efff909eed 100644 --- a/board/eukrea_cpuimx25/config.h +++ b/arch/arm/boards/eukrea_cpuimx25/config.h diff --git a/board/eukrea_cpuimx25/env/bin/_update b/arch/arm/boards/eukrea_cpuimx25/env/bin/_update index 014bce3512..014bce3512 100644 --- a/board/eukrea_cpuimx25/env/bin/_update +++ b/arch/arm/boards/eukrea_cpuimx25/env/bin/_update diff --git a/board/eukrea_cpuimx25/env/bin/boot b/arch/arm/boards/eukrea_cpuimx25/env/bin/boot index 2d9b3af01c..2d9b3af01c 100644 --- a/board/eukrea_cpuimx25/env/bin/boot +++ b/arch/arm/boards/eukrea_cpuimx25/env/bin/boot diff --git a/board/eukrea_cpuimx25/env/bin/hush_hack b/arch/arm/boards/eukrea_cpuimx25/env/bin/hush_hack index 5fffa92ecd..5fffa92ecd 100644 --- a/board/eukrea_cpuimx25/env/bin/hush_hack +++ b/arch/arm/boards/eukrea_cpuimx25/env/bin/hush_hack diff --git a/board/eukrea_cpuimx25/env/bin/init b/arch/arm/boards/eukrea_cpuimx25/env/bin/init index 335d7ae579..335d7ae579 100644 --- a/board/eukrea_cpuimx25/env/bin/init +++ b/arch/arm/boards/eukrea_cpuimx25/env/bin/init diff --git a/board/eukrea_cpuimx25/env/bin/update_kernel b/arch/arm/boards/eukrea_cpuimx25/env/bin/update_kernel index c2d2cc3fa9..c2d2cc3fa9 100644 --- a/board/eukrea_cpuimx25/env/bin/update_kernel +++ b/arch/arm/boards/eukrea_cpuimx25/env/bin/update_kernel diff --git a/board/eukrea_cpuimx25/env/bin/update_root b/arch/arm/boards/eukrea_cpuimx25/env/bin/update_root index dd89a5afad..dd89a5afad 100644 --- a/board/eukrea_cpuimx25/env/bin/update_root +++ b/arch/arm/boards/eukrea_cpuimx25/env/bin/update_root diff --git a/board/eukrea_cpuimx25/env/config b/arch/arm/boards/eukrea_cpuimx25/env/config index 9217ca17be..9217ca17be 100644 --- a/board/eukrea_cpuimx25/env/config +++ b/arch/arm/boards/eukrea_cpuimx25/env/config diff --git a/board/eukrea_cpuimx25/eukrea_cpuimx25.c b/arch/arm/boards/eukrea_cpuimx25/eukrea_cpuimx25.c index caeb46e872..caeb46e872 100644 --- a/board/eukrea_cpuimx25/eukrea_cpuimx25.c +++ b/arch/arm/boards/eukrea_cpuimx25/eukrea_cpuimx25.c diff --git a/board/eukrea_cpuimx25/lowlevel.c b/arch/arm/boards/eukrea_cpuimx25/lowlevel.c index b9d3ce57bb..b9d3ce57bb 100644 --- a/board/eukrea_cpuimx25/lowlevel.c +++ b/arch/arm/boards/eukrea_cpuimx25/lowlevel.c diff --git a/board/eukrea_cpuimx27/Makefile b/arch/arm/boards/eukrea_cpuimx27/Makefile index 5d958fa9aa..5d958fa9aa 100644 --- a/board/eukrea_cpuimx27/Makefile +++ b/arch/arm/boards/eukrea_cpuimx27/Makefile diff --git a/board/eukrea_cpuimx27/config.h b/arch/arm/boards/eukrea_cpuimx27/config.h index ec6f2123bf..ec6f2123bf 100644 --- a/board/eukrea_cpuimx27/config.h +++ b/arch/arm/boards/eukrea_cpuimx27/config.h diff --git a/board/eukrea_cpuimx27/env/bin/_update b/arch/arm/boards/eukrea_cpuimx27/env/bin/_update index 014bce3512..014bce3512 100644 --- a/board/eukrea_cpuimx27/env/bin/_update +++ b/arch/arm/boards/eukrea_cpuimx27/env/bin/_update diff --git a/board/eukrea_cpuimx27/env/bin/boot b/arch/arm/boards/eukrea_cpuimx27/env/bin/boot index 7272e56763..7272e56763 100644 --- a/board/eukrea_cpuimx27/env/bin/boot +++ b/arch/arm/boards/eukrea_cpuimx27/env/bin/boot diff --git a/board/eukrea_cpuimx27/env/bin/hush_hack b/arch/arm/boards/eukrea_cpuimx27/env/bin/hush_hack index 5fffa92ecd..5fffa92ecd 100644 --- a/board/eukrea_cpuimx27/env/bin/hush_hack +++ b/arch/arm/boards/eukrea_cpuimx27/env/bin/hush_hack diff --git a/board/eukrea_cpuimx27/env/bin/init b/arch/arm/boards/eukrea_cpuimx27/env/bin/init index 3bfd194913..3bfd194913 100644 --- a/board/eukrea_cpuimx27/env/bin/init +++ b/arch/arm/boards/eukrea_cpuimx27/env/bin/init diff --git a/board/eukrea_cpuimx27/env/bin/update_kernel b/arch/arm/boards/eukrea_cpuimx27/env/bin/update_kernel index 05c822d860..05c822d860 100644 --- a/board/eukrea_cpuimx27/env/bin/update_kernel +++ b/arch/arm/boards/eukrea_cpuimx27/env/bin/update_kernel diff --git a/board/eukrea_cpuimx27/env/bin/update_root b/arch/arm/boards/eukrea_cpuimx27/env/bin/update_root index eaf36ebcea..eaf36ebcea 100644 --- a/board/eukrea_cpuimx27/env/bin/update_root +++ b/arch/arm/boards/eukrea_cpuimx27/env/bin/update_root diff --git a/board/eukrea_cpuimx27/env/config b/arch/arm/boards/eukrea_cpuimx27/env/config index 505ada39c7..505ada39c7 100644 --- a/board/eukrea_cpuimx27/env/config +++ b/arch/arm/boards/eukrea_cpuimx27/env/config diff --git a/board/eukrea_cpuimx27/eukrea_cpuimx27.c b/arch/arm/boards/eukrea_cpuimx27/eukrea_cpuimx27.c index 1937d21c85..1937d21c85 100644 --- a/board/eukrea_cpuimx27/eukrea_cpuimx27.c +++ b/arch/arm/boards/eukrea_cpuimx27/eukrea_cpuimx27.c diff --git a/board/eukrea_cpuimx27/eukrea_cpuimx27.dox b/arch/arm/boards/eukrea_cpuimx27/eukrea_cpuimx27.dox index 6c2bfede22..6c2bfede22 100644 --- a/board/eukrea_cpuimx27/eukrea_cpuimx27.dox +++ b/arch/arm/boards/eukrea_cpuimx27/eukrea_cpuimx27.dox diff --git a/board/eukrea_cpuimx27/lowlevel_init.S b/arch/arm/boards/eukrea_cpuimx27/lowlevel_init.S index 5295a8a227..5295a8a227 100644 --- a/board/eukrea_cpuimx27/lowlevel_init.S +++ b/arch/arm/boards/eukrea_cpuimx27/lowlevel_init.S diff --git a/board/eukrea_cpuimx35/Makefile b/arch/arm/boards/eukrea_cpuimx35/Makefile index 32ffe42cb1..32ffe42cb1 100644 --- a/board/eukrea_cpuimx35/Makefile +++ b/arch/arm/boards/eukrea_cpuimx35/Makefile diff --git a/board/eukrea_cpuimx35/config.h b/arch/arm/boards/eukrea_cpuimx35/config.h index bfd3b3964e..bfd3b3964e 100644 --- a/board/eukrea_cpuimx35/config.h +++ b/arch/arm/boards/eukrea_cpuimx35/config.h diff --git a/board/eukrea_cpuimx35/env/bin/_update b/arch/arm/boards/eukrea_cpuimx35/env/bin/_update index 014bce3512..014bce3512 100644 --- a/board/eukrea_cpuimx35/env/bin/_update +++ b/arch/arm/boards/eukrea_cpuimx35/env/bin/_update diff --git a/board/eukrea_cpuimx35/env/bin/boot b/arch/arm/boards/eukrea_cpuimx35/env/bin/boot index fca5b8cc19..fca5b8cc19 100644 --- a/board/eukrea_cpuimx35/env/bin/boot +++ b/arch/arm/boards/eukrea_cpuimx35/env/bin/boot diff --git a/board/eukrea_cpuimx35/env/bin/hush_hack b/arch/arm/boards/eukrea_cpuimx35/env/bin/hush_hack index 5fffa92ecd..5fffa92ecd 100644 --- a/board/eukrea_cpuimx35/env/bin/hush_hack +++ b/arch/arm/boards/eukrea_cpuimx35/env/bin/hush_hack diff --git a/board/eukrea_cpuimx35/env/bin/init b/arch/arm/boards/eukrea_cpuimx35/env/bin/init index 49e54c5fdd..90007cd65d 100644 --- a/board/eukrea_cpuimx35/env/bin/init +++ b/arch/arm/boards/eukrea_cpuimx35/env/bin/init @@ -13,10 +13,14 @@ if [ -e /dev/nand0 ]; then fi if [ -f /env/logo.bmp ]; then + fb0.enable=1 bmp /env/logo.bmp + gpio_direction_out 1 1 elif [ -f /env/logo.bmp.lzo ]; then unlzo /env/logo.bmp.lzo /logo.bmp + fb0.enable=1 bmp /logo.bmp + gpio_direction_out 1 1 fi if [ -z $eth0.ethaddr ]; then diff --git a/board/eukrea_cpuimx35/env/bin/update_kernel b/arch/arm/boards/eukrea_cpuimx35/env/bin/update_kernel index c2d2cc3fa9..c2d2cc3fa9 100644 --- a/board/eukrea_cpuimx35/env/bin/update_kernel +++ b/arch/arm/boards/eukrea_cpuimx35/env/bin/update_kernel diff --git a/board/eukrea_cpuimx35/env/bin/update_root b/arch/arm/boards/eukrea_cpuimx35/env/bin/update_root index dd89a5afad..dd89a5afad 100644 --- a/board/eukrea_cpuimx35/env/bin/update_root +++ b/arch/arm/boards/eukrea_cpuimx35/env/bin/update_root diff --git a/board/eukrea_cpuimx35/env/config b/arch/arm/boards/eukrea_cpuimx35/env/config index df2079fa58..df2079fa58 100644 --- a/board/eukrea_cpuimx35/env/config +++ b/arch/arm/boards/eukrea_cpuimx35/env/config diff --git a/board/eukrea_cpuimx35/eukrea_cpuimx35.c b/arch/arm/boards/eukrea_cpuimx35/eukrea_cpuimx35.c index 7f1c782f1e..d41d9b548e 100644 --- a/board/eukrea_cpuimx35/eukrea_cpuimx35.c +++ b/arch/arm/boards/eukrea_cpuimx35/eukrea_cpuimx35.c @@ -99,14 +99,20 @@ static struct fb_videomode imxfb_mode = { .lower_margin = 4, .hsync_len = 30, .vsync_len = 3, - .sync = FB_SYNC_OE_ACT_HIGH, + .sync = 0, .vmode = FB_VMODE_NONINTERLACED, .flag = 0, }; +static void eukrea_cpuimx35_enable_display(int enable) +{ + gpio_direction_output(4, enable); +} + static struct imx_ipu_fb_platform_data ipu_fb_data = { .mode = &imxfb_mode, .bpp = 16, + .enable = eukrea_cpuimx35_enable_display, }; static struct device_d imxfb_dev = { @@ -164,15 +170,6 @@ static int eukrea_cpuimx35_devices_init(void) device_initcall(eukrea_cpuimx35_devices_init); -static int eukrea_cpuimx35_enable_display(void) -{ - gpio_direction_output(1, 1); - gpio_direction_output(0, 0); - return 0; -} - -late_initcall(eukrea_cpuimx35_enable_display); - static struct device_d eukrea_cpuimx35_serial_device = { .name = "imx_serial", .map_base = IMX_UART1_BASE, @@ -205,6 +202,10 @@ static struct pad_desc eukrea_cpuimx35_pads[] = { MX35_PAD_TXD1__UART1_TXD_MUX, MX35_PAD_RTS1__UART1_RTS, MX35_PAD_CTS1__UART1_CTS, + + MX35_PAD_LD23__GPIO3_29, + MX35_PAD_CONTRAST__GPIO1_1, + MX35_PAD_D3_CLS__GPIO1_4, }; static int eukrea_cpuimx35_console_init(void) @@ -212,6 +213,13 @@ static int eukrea_cpuimx35_console_init(void) mxc_iomux_v3_setup_multiple_pads(eukrea_cpuimx35_pads, ARRAY_SIZE(eukrea_cpuimx35_pads)); + /* screen default on to prevent flicker */ + gpio_direction_output(4, 1); + /* backlight default off */ + gpio_direction_output(1, 0); + /* led default off */ + gpio_direction_output(32 * 2 + 29, 1); + register_device(&eukrea_cpuimx35_serial_device); return 0; } diff --git a/board/eukrea_cpuimx35/eukrea_cpuimx35.dox b/arch/arm/boards/eukrea_cpuimx35/eukrea_cpuimx35.dox index cbdf69db35..cbdf69db35 100644 --- a/board/eukrea_cpuimx35/eukrea_cpuimx35.dox +++ b/arch/arm/boards/eukrea_cpuimx35/eukrea_cpuimx35.dox diff --git a/arch/arm/boards/eukrea_cpuimx35/flash_header.c b/arch/arm/boards/eukrea_cpuimx35/flash_header.c new file mode 100644 index 0000000000..78f51f670c --- /dev/null +++ b/arch/arm/boards/eukrea_cpuimx35/flash_header.c @@ -0,0 +1,41 @@ +#include <common.h> +#include <mach/imx-flash-header.h> + +void __naked __flash_header_start go(void) +{ + __asm__ __volatile__("b exception_vectors\n"); +} + +struct imx_dcd_entry __dcd_entry_0x400 dcd_entry[] = { + { .ptr_type = 4, .addr = 0x53F80004, .val = 0x00821000, }, + { .ptr_type = 4, .addr = 0x53F80004, .val = 0x00821000, }, + { .ptr_type = 4, .addr = 0xB8001010, .val = 0x00000004, }, + { .ptr_type = 4, .addr = 0xB8001010, .val = 0x0000000C, }, + { .ptr_type = 4, .addr = 0xB8001004, .val = 0x0009572B, }, + { .ptr_type = 4, .addr = 0xB8001000, .val = 0x92220000, }, + { .ptr_type = 1, .addr = 0x80000400, .val = 0xda, }, + { .ptr_type = 4, .addr = 0xB8001000, .val = 0xA2220000, }, + { .ptr_type = 1, .addr = 0x80000000, .val = 0x87654321, }, + { .ptr_type = 1, .addr = 0x80000000, .val = 0x87654321, }, + { .ptr_type = 4, .addr = 0xB8001000, .val = 0xB2220000, }, + { .ptr_type = 1, .addr = 0x80000033, .val = 0xda, }, + { .ptr_type = 1, .addr = 0x82000000, .val = 0xda, }, + { .ptr_type = 4, .addr = 0xB8001000, .val = 0x82224080, }, + { .ptr_type = 4, .addr = 0xB8001010, .val = 0x00000004, }, +}; + +#define APP_DEST 0x80000000 + +struct imx_flash_header __flash_header_0x400 eukrea_cpuimx35_header = { + .app_code_jump_vector = APP_DEST + 0x1000, + .app_code_barker = APP_CODE_BARKER, + .app_code_csf = 0, + .dcd_ptr_ptr = APP_DEST + 0x400 + offsetof(struct imx_flash_header, dcd), + .super_root_key = 0, + .dcd = APP_DEST + 0x400 + offsetof(struct imx_flash_header, dcd_barker), + .app_dest = APP_DEST, + .dcd_barker = DCD_BARKER, + .dcd_block_len = sizeof (dcd_entry), +}; + +unsigned long __image_len_0x400 barebox_len = 0x40000; diff --git a/board/eukrea_cpuimx35/lowlevel.c b/arch/arm/boards/eukrea_cpuimx35/lowlevel.c index 44f3cf0725..aad334d2e8 100644 --- a/board/eukrea_cpuimx35/lowlevel.c +++ b/arch/arm/boards/eukrea_cpuimx35/lowlevel.c @@ -62,7 +62,6 @@ void __bare_init __naked board_init_lowlevel(void) { uint32_t r, s; unsigned long ccm_base = IMX_CCM_BASE; - unsigned long iomuxc_base = IMX_IOMUXC_BASE; #ifdef CONFIG_NAND_IMX_BOOT unsigned int *trg, *src; int i; @@ -132,80 +131,32 @@ void __bare_init __naked board_init_lowlevel(void) if (r > 0x80000000 && r < 0x90000000) board_init_lowlevel_return(); - /* Set DDR Type to SDRAM, drive strength workaround * - * 0x00000000 MDDR * - * 0x00000800 3,3V SDRAM */ - - r = 0x00000800; - writel(r, iomuxc_base + 0x794); - writel(r, iomuxc_base + 0x798); - writel(r, iomuxc_base + 0x79c); - writel(r, iomuxc_base + 0x7a0); - writel(r, iomuxc_base + 0x7a4); - - /* MDDR init, enable mDDR*/ - writel(0x00000304, ESDMISC); /* was 0x00000004 */ - - /* set timing paramters */ - writel(0x00255417, ESDCFG0); - /* select Precharge-All mode */ + /* Init Mobile DDR */ + writel(0x00000004, ESDMISC); + writel(0x0000000C, ESDMISC); + writel(0x0009572B, ESDCFG0); writel(0x92220000, ESDCTL0); - /* Precharge-All */ - writel(0x12345678, IMX_SDRAM_CS0 + 0x400); - - /* select Load-Mode-Register mode */ - writel(0xB8001000, ESDCTL0); - /* Load reg EMR2 */ - writeb(0xda, 0x84000000); - /* Load reg EMR3 */ - writeb(0xda, 0x86000000); - /* Load reg EMR1 -- enable DLL */ - writeb(0xda, 0x82000400); - /* Load reg MR -- reset DLL */ - writeb(0xda, 0x80000333); - - /* select Precharge-All mode */ - writel(0x92220000, ESDCTL0); - /* Precharge-All */ - writel(0x12345678, IMX_SDRAM_CS0 + 0x400); - - /* select Manual-Refresh mode */ + writeb(0xda, IMX_SDRAM_CS0 + 0x400); writel(0xA2220000, ESDCTL0); - /* Manual-Refresh 2 times */ writel(0x87654321, IMX_SDRAM_CS0); writel(0x87654321, IMX_SDRAM_CS0); - - /* select Load-Mode-Register mode */ writel(0xB2220000, ESDCTL0); - /* Load reg MR -- CL3, BL8, end DLL reset */ - writeb(0xda, 0x80000233); - /* Load reg EMR1 -- OCD default */ - writeb(0xda, 0x82000780); - /* Load reg EMR1 -- OCD exit */ - writeb(0xda, 0x82000400); - - /* select normal-operation mode - * DSIZ32-bit, BL8, COL10-bit, ROW13-bit - * disable PWT & PRCT - * disable Auto-Refresh */ - writel(0x82220080, ESDCTL0); - - /* enable Auto-Refresh */ - writel(0x82228080, ESDCTL0); - /* enable Auto-Refresh */ - writel(0x00002000, ESDCTL1); + writeb(0xda, IMX_SDRAM_CS0 + 0x33); + writeb(0xda, IMX_SDRAM_CS0 + 0x2000000); + writel(0x82224080, ESDCTL0); + writel(0x00000004, ESDMISC); #ifdef CONFIG_NAND_IMX_BOOT /* skip NAND boot if not running from NFC space */ r = get_pc(); - if (r < IMX_NFC_BASE || r > IMX_NFC_BASE + 0x800) + if (r < IMX_NFC_BASE || r > IMX_NFC_BASE + 0x1000) board_init_lowlevel_return(); src = (unsigned int *)IMX_NFC_BASE; trg = (unsigned int *)TEXT_BASE; /* Move ourselves out of NFC SRAM */ - for (i = 0; i < 0x800 / sizeof(int); i++) + for (i = 0; i < 0x1000 / sizeof(int); i++) *trg++ = *src++; /* Jump to SDRAM */ diff --git a/board/freescale-mx25-3-stack/3stack.c b/arch/arm/boards/freescale-mx25-3-stack/3stack.c index a77a02d498..a77a02d498 100644 --- a/board/freescale-mx25-3-stack/3stack.c +++ b/arch/arm/boards/freescale-mx25-3-stack/3stack.c diff --git a/board/freescale-mx25-3-stack/Makefile b/arch/arm/boards/freescale-mx25-3-stack/Makefile index ab853e0d6b..ab853e0d6b 100644 --- a/board/freescale-mx25-3-stack/Makefile +++ b/arch/arm/boards/freescale-mx25-3-stack/Makefile diff --git a/board/freescale-mx25-3-stack/config.h b/arch/arm/boards/freescale-mx25-3-stack/config.h index f35e8a0557..f35e8a0557 100644 --- a/board/freescale-mx25-3-stack/config.h +++ b/arch/arm/boards/freescale-mx25-3-stack/config.h diff --git a/board/freescale-mx25-3-stack/env/bin/_update b/arch/arm/boards/freescale-mx25-3-stack/env/bin/_update index 014bce3512..014bce3512 100644 --- a/board/freescale-mx25-3-stack/env/bin/_update +++ b/arch/arm/boards/freescale-mx25-3-stack/env/bin/_update diff --git a/board/freescale-mx25-3-stack/env/bin/boot b/arch/arm/boards/freescale-mx25-3-stack/env/bin/boot index 7bbff2d1f6..7bbff2d1f6 100644 --- a/board/freescale-mx25-3-stack/env/bin/boot +++ b/arch/arm/boards/freescale-mx25-3-stack/env/bin/boot diff --git a/board/freescale-mx25-3-stack/env/bin/hush_hack b/arch/arm/boards/freescale-mx25-3-stack/env/bin/hush_hack index 5fffa92ecd..5fffa92ecd 100644 --- a/board/freescale-mx25-3-stack/env/bin/hush_hack +++ b/arch/arm/boards/freescale-mx25-3-stack/env/bin/hush_hack diff --git a/board/freescale-mx25-3-stack/env/bin/init b/arch/arm/boards/freescale-mx25-3-stack/env/bin/init index 0600b9e661..0600b9e661 100644 --- a/board/freescale-mx25-3-stack/env/bin/init +++ b/arch/arm/boards/freescale-mx25-3-stack/env/bin/init diff --git a/board/freescale-mx25-3-stack/env/bin/update_kernel b/arch/arm/boards/freescale-mx25-3-stack/env/bin/update_kernel index 05c822d860..05c822d860 100644 --- a/board/freescale-mx25-3-stack/env/bin/update_kernel +++ b/arch/arm/boards/freescale-mx25-3-stack/env/bin/update_kernel diff --git a/board/freescale-mx25-3-stack/env/bin/update_root b/arch/arm/boards/freescale-mx25-3-stack/env/bin/update_root index eaf36ebcea..eaf36ebcea 100644 --- a/board/freescale-mx25-3-stack/env/bin/update_root +++ b/arch/arm/boards/freescale-mx25-3-stack/env/bin/update_root diff --git a/board/freescale-mx25-3-stack/env/config b/arch/arm/boards/freescale-mx25-3-stack/env/config index a5e492e339..a5e492e339 100644 --- a/board/freescale-mx25-3-stack/env/config +++ b/arch/arm/boards/freescale-mx25-3-stack/env/config diff --git a/board/freescale-mx25-3-stack/lowlevel_init.S b/arch/arm/boards/freescale-mx25-3-stack/lowlevel_init.S index 73bb14723e..73bb14723e 100644 --- a/board/freescale-mx25-3-stack/lowlevel_init.S +++ b/arch/arm/boards/freescale-mx25-3-stack/lowlevel_init.S diff --git a/board/freescale-mx35-3-stack/3stack.c b/arch/arm/boards/freescale-mx35-3-stack/3stack.c index 9a66976f9d..9a66976f9d 100644 --- a/board/freescale-mx35-3-stack/3stack.c +++ b/arch/arm/boards/freescale-mx35-3-stack/3stack.c diff --git a/board/freescale-mx35-3-stack/3stack.dox b/arch/arm/boards/freescale-mx35-3-stack/3stack.dox index 15c5b6e1ff..15c5b6e1ff 100644 --- a/board/freescale-mx35-3-stack/3stack.dox +++ b/arch/arm/boards/freescale-mx35-3-stack/3stack.dox diff --git a/board/freescale-mx35-3-stack/Makefile b/arch/arm/boards/freescale-mx35-3-stack/Makefile index a8ea4a3d66..a8ea4a3d66 100644 --- a/board/freescale-mx35-3-stack/Makefile +++ b/arch/arm/boards/freescale-mx35-3-stack/Makefile diff --git a/board/freescale-mx35-3-stack/board-mx35_3stack.h b/arch/arm/boards/freescale-mx35-3-stack/board-mx35_3stack.h index c18066ad9c..c18066ad9c 100644 --- a/board/freescale-mx35-3-stack/board-mx35_3stack.h +++ b/arch/arm/boards/freescale-mx35-3-stack/board-mx35_3stack.h diff --git a/board/freescale-mx35-3-stack/config.h b/arch/arm/boards/freescale-mx35-3-stack/config.h index c724a57bd0..c724a57bd0 100644 --- a/board/freescale-mx35-3-stack/config.h +++ b/arch/arm/boards/freescale-mx35-3-stack/config.h diff --git a/board/freescale-mx35-3-stack/env/bin/_update b/arch/arm/boards/freescale-mx35-3-stack/env/bin/_update index ddd6b84f72..ddd6b84f72 100644 --- a/board/freescale-mx35-3-stack/env/bin/_update +++ b/arch/arm/boards/freescale-mx35-3-stack/env/bin/_update diff --git a/board/freescale-mx35-3-stack/env/bin/boot b/arch/arm/boards/freescale-mx35-3-stack/env/bin/boot index fb2fe614d4..fb2fe614d4 100644 --- a/board/freescale-mx35-3-stack/env/bin/boot +++ b/arch/arm/boards/freescale-mx35-3-stack/env/bin/boot diff --git a/board/freescale-mx35-3-stack/env/bin/hush_hack b/arch/arm/boards/freescale-mx35-3-stack/env/bin/hush_hack index 5fffa92ecd..5fffa92ecd 100644 --- a/board/freescale-mx35-3-stack/env/bin/hush_hack +++ b/arch/arm/boards/freescale-mx35-3-stack/env/bin/hush_hack diff --git a/board/freescale-mx35-3-stack/env/bin/init b/arch/arm/boards/freescale-mx35-3-stack/env/bin/init index c982f22a86..c982f22a86 100644 --- a/board/freescale-mx35-3-stack/env/bin/init +++ b/arch/arm/boards/freescale-mx35-3-stack/env/bin/init diff --git a/board/freescale-mx35-3-stack/env/bin/update_kernel b/arch/arm/boards/freescale-mx35-3-stack/env/bin/update_kernel index 63ad11aaed..63ad11aaed 100644 --- a/board/freescale-mx35-3-stack/env/bin/update_kernel +++ b/arch/arm/boards/freescale-mx35-3-stack/env/bin/update_kernel diff --git a/board/freescale-mx35-3-stack/env/bin/update_rootfs b/arch/arm/boards/freescale-mx35-3-stack/env/bin/update_rootfs index 53dd2ca575..53dd2ca575 100644 --- a/board/freescale-mx35-3-stack/env/bin/update_rootfs +++ b/arch/arm/boards/freescale-mx35-3-stack/env/bin/update_rootfs diff --git a/board/freescale-mx35-3-stack/env/config b/arch/arm/boards/freescale-mx35-3-stack/env/config index 51195f7404..51195f7404 100644 --- a/board/freescale-mx35-3-stack/env/config +++ b/arch/arm/boards/freescale-mx35-3-stack/env/config diff --git a/board/freescale-mx35-3-stack/flash_header.c b/arch/arm/boards/freescale-mx35-3-stack/flash_header.c index 171c499a6d..171c499a6d 100644 --- a/board/freescale-mx35-3-stack/flash_header.c +++ b/arch/arm/boards/freescale-mx35-3-stack/flash_header.c diff --git a/board/freescale-mx35-3-stack/lowlevel_init.S b/arch/arm/boards/freescale-mx35-3-stack/lowlevel_init.S index 1680579b51..1680579b51 100644 --- a/board/freescale-mx35-3-stack/lowlevel_init.S +++ b/arch/arm/boards/freescale-mx35-3-stack/lowlevel_init.S diff --git a/arch/arm/boards/guf-neso/Makefile b/arch/arm/boards/guf-neso/Makefile new file mode 100644 index 0000000000..2b6eb02595 --- /dev/null +++ b/arch/arm/boards/guf-neso/Makefile @@ -0,0 +1,5 @@ + +obj-y += lowlevel.o +obj-y += board.o +obj-y += pll_init.o + diff --git a/arch/arm/boards/guf-neso/board.c b/arch/arm/boards/guf-neso/board.c new file mode 100644 index 0000000000..2f53d34556 --- /dev/null +++ b/arch/arm/boards/guf-neso/board.c @@ -0,0 +1,401 @@ +/* + * Copyright (C) 2010 Sascha Hauer, Pengutronix + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + */ + +#include <common.h> +#include <net.h> +#include <init.h> +#include <environment.h> +#include <fec.h> +#include <notifier.h> +#include <partition.h> +#include <fs.h> +#include <fcntl.h> +#include <nand.h> +#include <command.h> +#include <spi/spi.h> +#include <usb/isp1504.h> + +#include <asm/io.h> +#include <asm/mmu.h> +#include <asm/armlinux.h> +#include <asm/mach-types.h> + +#include <mach/gpio.h> +#include <mach/spi.h> +#include <mach/imx-regs.h> +#include <mach/iomux-mx27.h> +#include <mach/imx-nand.h> +#include <mach/imx-pll.h> +#include <mach/imxfb.h> + +/* two pins are controlling the CS signals to the USB phys */ +#define USBH2_PHY_CS_GPIO (GPIO_PORTF + 20) +#define OTG_PHY_CS_GPIO (GPIO_PORTF + 19) + +/* two pins are controlling the display and its backlight */ +#define LCD_POWER_GPIO (GPIO_PORTF + 18) +#define BACKLIGHT_POWER_GPIO (GPIO_PORTE + 5) + +static struct memory_platform_data ram_pdata = { + .name = "ram0", + .flags = DEVFS_RDWR, +}; + +static struct device_d sdram_dev = { + .name = "mem", + .map_base = 0xa0000000, + .size = 128 * 1024 * 1024, + .platform_data = &ram_pdata, +}; + +static struct fec_platform_data fec_info = { + .xcv_type = MII100, + .phy_addr = 31, +}; + +static struct device_d fec_dev = { + .name = "fec_imx", + .map_base = 0x1002b000, + .platform_data = &fec_info, +}; + +static struct imx_nand_platform_data nand_info = { + .width = 1, + .hw_ecc = 1, + .flash_bbt = 1, +}; + +static struct device_d nand_dev = { + .name = "imx_nand", + .map_base = 0xd8000000, + .platform_data = &nand_info, +}; + +static struct imx_fb_videomode imxfb_mode = { + .mode = { + .name = "CPT CLAA070LC0JCT", + .refresh = 60, + .xres = 800, + .yres = 480, + .pixclock = KHZ2PICOS(27000), + .hsync_len = 1, /* DE only sync */ + .left_margin = 50, + .right_margin = 50, + .vsync_len = 1, /* DE only sync */ + .upper_margin = 10, + .lower_margin = 10, + }, + /* + * - TFT style panel + * - clk enabled while idle + * - clock inverted + * - data not inverted + * - data enable high active + */ + .pcr = PCR_TFT | + PCR_COLOR | + PCR_PBSIZ_8 | + PCR_BPIX_16 | + PCR_CLKPOL | + PCR_SCLK_SEL | + PCR_LPPOL | + PCR_FLMPOL, + .bpp = 16, /* TODO 32 bit does not work: The 'green' component is lacking in this mode */ +}; + +static void neso_fb_enable(int enable) +{ + gpio_direction_output(LCD_POWER_GPIO, enable); + gpio_direction_output(BACKLIGHT_POWER_GPIO, enable); +} + +static struct imx_fb_platform_data neso_fb_data = { + .mode = &imxfb_mode, + .pwmr = 0x00000000, /* doesn't matter */ + .lscr1 = 0x00120300, /* doesn't matter */ + /* dynamic mode -> using the reset values (as recommended in the datasheet) */ + .dmacr = (0 << 31) | (4 << 16) | 96, + .enable = neso_fb_enable, + .framebuffer_ovl = (void *)0xa7f00000, +}; + +static struct device_d imxfb_dev = { + .name = "imxfb", + .map_base = 0x10021000, + .size = 0x1000, + .platform_data = &neso_fb_data, +}; + +#ifdef CONFIG_USB + +static struct device_d usbh2_dev = { + .name = "ehci", + .map_base = IMX_OTG_BASE + 0x400, + .size = 0x200, +}; + +static void neso_usbh_init(void) +{ + uint32_t temp; + + temp = readl(IMX_OTG_BASE + 0x600); + temp &= ~((3 << 21) | 1); + temp |= (1 << 5) | (1 << 16) | (1 << 19) | (1 << 20) | (1<<11); + writel(temp, IMX_OTG_BASE + 0x600); + + temp = readl(IMX_OTG_BASE + 0x584); + temp &= ~(3 << 30); + temp |= 2 << 30; + writel(temp, IMX_OTG_BASE + 0x584); + + mdelay(10); + + gpio_set_value(USBH2_PHY_CS_GPIO, 0); + mdelay(10); + isp1504_set_vbus_power((void *)(IMX_OTG_BASE + 0x570), 1); +} +#endif + +#ifdef CONFIG_MMU +static void neso_mmu_init(void) +{ + mmu_init(); + + arm_create_section(0xa0000000, 0xa0000000, 128, PMD_SECT_DEF_CACHED); + arm_create_section(0xb0000000, 0xa0000000, 128, PMD_SECT_DEF_UNCACHED); + + setup_dma_coherent(0x10000000); + +#if TEXT_BASE & (0x100000 - 1) +#warning cannot create vector section. Adjust TEXT_BASE to a 1M boundary +#else + arm_create_section(0x0, TEXT_BASE, 1, PMD_SECT_DEF_UNCACHED); +#endif + mmu_enable(); +} +#else +static void neso_mmu_init(void) +{ +} +#endif + +static int neso_devices_init(void) +{ + int i; + + unsigned int mode[] = { + /* UART1 */ + PE12_PF_UART1_TXD, + PE13_PF_UART1_RXD, + PE14_PF_UART1_CTS, + PE15_PF_UART1_RTS, + /* FEC */ + PD0_AIN_FEC_TXD0, + PD1_AIN_FEC_TXD1, + PD2_AIN_FEC_TXD2, + PD3_AIN_FEC_TXD3, + PD4_AOUT_FEC_RX_ER, + PD5_AOUT_FEC_RXD1, + PD6_AOUT_FEC_RXD2, + PD7_AOUT_FEC_RXD3, + PD8_AF_FEC_MDIO, + PD9_AIN_FEC_MDC, + PD10_AOUT_FEC_CRS, + PD11_AOUT_FEC_TX_CLK, + PD12_AOUT_FEC_RXD0, + PD13_AOUT_FEC_RX_DV, + PD14_AOUT_FEC_RX_CLK, + PD15_AOUT_FEC_COL, + PD16_AIN_FEC_TX_ER, + PF23_AIN_FEC_TX_EN, + + /* SSI1 connected in AC97 style */ + PC20_PF_SSI1_FS, + PC21_PF_SSI1_RXD, + PC22_PF_SSI1_TXD, + PC23_PF_SSI1_CLK, + + /* LED 1 */ + (GPIO_PORTB | 15 | GPIO_GPIO | GPIO_OUT), + /* LED 2 */ + (GPIO_PORTB | 16 | GPIO_GPIO | GPIO_OUT), + /* CTOUCH reset */ + (GPIO_PORTB | 17 | GPIO_GPIO | GPIO_OUT), + /* CTOUCH IRQ */ + (GPIO_PORTB | 14 | GPIO_GPIO | GPIO_IN), + /* RTC IRQ */ + (GPIO_PORTF | 14 | GPIO_GPIO | GPIO_IN), + /* SD change card detection */ + (GPIO_PORTF | 17 | GPIO_GPIO | GPIO_IN), + /* SDHC1*/ + PE18_PF_SD1_D0, + PE19_PF_SD1_D1, + PE20_PF_SD1_D2, + PE21_PF_SD1_D3, + PE22_PF_SD1_CMD, + PE23_PF_SD1_CLK, + /* I2C1 */ + PD17_PF_I2C_DATA, + PD18_PF_I2C_CLK, + /* I2C2, for CTOUCH */ + PC5_PF_I2C2_SDA, + PC6_PF_I2C2_SCL, + + /* Connected to: Both USB phys and ethernet phy FIXME 1 = RESET? */ + PE17_PF_RESET_OUT, + + /* USB host */ + (USBH2_PHY_CS_GPIO | GPIO_GPIO | GPIO_OUT), + PA0_PF_USBH2_CLK, + PA1_PF_USBH2_DIR, + PA3_PF_USBH2_NXT, + PA4_PF_USBH2_STP, + PD22_AF_USBH2_DATA0, + PD24_AF_USBH2_DATA1, + PD23_AF_USBH2_DATA2, + PD20_AF_USBH2_DATA3, + PD19_AF_USBH2_DATA4, + PD26_AF_USBH2_DATA5, + PD21_AF_USBH2_DATA6, + PA2_PF_USBH2_DATA7, + + /* USB OTG */ + (OTG_PHY_CS_GPIO | GPIO_GPIO | GPIO_OUT), + PE24_PF_USBOTG_CLK, + PE2_PF_USBOTG_DIR, + PE0_PF_USBOTG_NXT, + PE1_PF_USBOTG_STP, + PC9_PF_USBOTG_DATA0, + PC11_PF_USBOTG_DATA1, + PC10_PF_USBOTG_DATA2, + PC13_PF_USBOTG_DATA3, + PC12_PF_USBOTG_DATA4, + PC7_PF_USBOTG_DATA5, + PC8_PF_USBOTG_DATA6, + PE25_PF_USBOTG_DATA7, + + /* Display signals */ + (LCD_POWER_GPIO | GPIO_GPIO | GPIO_OUT), /* LCD power: 1 = LCD on */ + PA5_PF_LSCLK, + PA6_PF_LD0, + PA7_PF_LD1, + PA8_PF_LD2, + PA9_PF_LD3, + PA10_PF_LD4, + PA11_PF_LD5, + PA12_PF_LD6, + PA13_PF_LD7, + PA14_PF_LD8, + PA15_PF_LD9, + PA16_PF_LD10, + PA17_PF_LD11, + PA18_PF_LD12, + PA19_PF_LD13, + PA20_PF_LD14, + PA21_PF_LD15, + PA22_PF_LD16, + PA23_PF_LD17, + PA31_PF_OE_ACD, /* DE */ + + /* Backlight PWM (Use as gpio) */ + (BACKLIGHT_POWER_GPIO | GPIO_GPIO | GPIO_OUT), + }; + + /* reset the chip select lines to the USB/OTG phys to avoid any hang */ + gpio_direction_output(OTG_PHY_CS_GPIO, 1); + gpio_direction_output(USBH2_PHY_CS_GPIO, 1); + + + neso_mmu_init(); + + /* initialize gpios */ + for (i = 0; i < ARRAY_SIZE(mode); i++) + imx_gpio_mode(mode[i]); + + register_device(&nand_dev); + register_device(&sdram_dev); + register_device(&imxfb_dev); + +#ifdef CONFIG_USB + neso_usbh_init(); + register_device(&usbh2_dev); +#endif + + register_device(&fec_dev); + + devfs_add_partition("nand0", 0x00000, 0x40000, PARTITION_FIXED, "self_raw"); + dev_add_bb_dev("self_raw", "self0"); + + devfs_add_partition("nand0", 0x40000, 0x80000, PARTITION_FIXED, "env_raw"); + dev_add_bb_dev("env_raw", "env0"); + + armlinux_add_dram(&sdram_dev); + armlinux_set_bootparams((void *)0xa0000100); + armlinux_set_architecture(MACH_TYPE_NESO); + + return 0; +} + +device_initcall(neso_devices_init); + +static struct device_d neso_serial_device = { + .name = "imx_serial", + .map_base = IMX_UART1_BASE, + .size = 4096, +}; + +static int neso_console_init(void) +{ + register_device(&neso_serial_device); + + return 0; +} + +console_initcall(neso_console_init); + +extern void *neso_pll_init, *neso_pll_init_end; + +static int neso_pll(void) +{ + void *vram = (void *)0xffff4c00; + void (*pllfunc)(void) = vram; + + printf("initialising PLLs\n"); + + memcpy(vram, &neso_pll_init, 0x100); + + console_flush(); + + pllfunc(); + + /* clock gating enable */ + GPCR = 0x00050f08; + + PCDR0 = 0x130410c3; + PCDR1 = 0x09030911; + + /* Clocks have changed. Notify clients */ + clock_notifier_call_chain(); + + return 0; +} + +late_initcall(neso_pll); + diff --git a/board/pcm038/config.h b/arch/arm/boards/guf-neso/config.h index c2f5e7cc58..c2f5e7cc58 100644 --- a/board/pcm038/config.h +++ b/arch/arm/boards/guf-neso/config.h diff --git a/arch/arm/boards/guf-neso/env/config b/arch/arm/boards/guf-neso/env/config new file mode 100644 index 0000000000..6327e69217 --- /dev/null +++ b/arch/arm/boards/guf-neso/env/config @@ -0,0 +1,53 @@ +#!/bin/sh + +machine=guf-neso +eth0.serverip= +user= + +# use 'dhcp' to do dhcp in barebox and in kernel +# use 'none' if you want to skip kernel ip autoconfiguration +ip=dhcp + +# or set your networking parameters here +#eth0.ipaddr=a.b.c.d +#eth0.netmask=a.b.c.d +#eth0.gateway=a.b.c.d +#eth0.serverip=a.b.c.d + +# can be either 'net', 'nor' or 'nand' +kernel_loc=net +# can be either 'net', 'nor', 'nand' or 'initrd' +rootfs_loc=net + +# can be either 'jffs2' or 'ubifs' +rootfs_type=ubifs +rootfsimage=root-$machine.$rootfs_type + +# The image type of the kernel. Can be uimage, zimage, raw, or raw_lzo +kernelimage_type=zimage +kernelimage=zImage-$machine +#kernelimage_type=uimage +#kernelimage=uImage-$machine +#kernelimage_type=raw +#kernelimage=Image-$machine +#kernelimage_type=raw_lzo +#kernelimage=Image-$machine.lzo + +if [ -n $user ]; then + kernelimage="$user"-"$kernelimage" + nfsroot="$eth0.serverip:/home/$user/nfsroot/$machine" + rootfsimage="$user"-"$rootfsimage" +else + nfsroot="$eth0.serverip:/path/to/nfs/root" +fi + +autoboot_timeout=3 + +bootargs="console=ttymxc0,115200" + +nand_parts="256k(barebox)ro,512k(bareboxenv),2M(kernel),-(root)" +rootfs_mtdblock_nand=3 + +# set a fancy prompt (if support is compiled in) +PS1="\e[1;32mbarebox@\e[1;31m\h:\w\e[0m " + diff --git a/board/pcm038/lowlevel.c b/arch/arm/boards/guf-neso/lowlevel.c index 0c376f2fae..0c376f2fae 100644 --- a/board/pcm038/lowlevel.c +++ b/arch/arm/boards/guf-neso/lowlevel.c diff --git a/arch/arm/boards/guf-neso/pll_init.S b/arch/arm/boards/guf-neso/pll_init.S new file mode 100644 index 0000000000..87e5312fb4 --- /dev/null +++ b/arch/arm/boards/guf-neso/pll_init.S @@ -0,0 +1,48 @@ +#include <config.h> +#include <mach/imx-regs.h> +#include <mach/imx-pll.h> +#include <linux/linkage.h> + +#define writel(val, reg) \ + ldr r0, =reg; \ + ldr r1, =val; \ + str r1, [r0]; + +#define CSCR_VAL CSCR_USB_DIV(3) | \ + CSCR_SD_CNT(3) | \ + CSCR_MSHC_SEL | \ + CSCR_H264_SEL | \ + CSCR_SSI1_SEL | \ + CSCR_SSI2_SEL | \ + CSCR_MCU_SEL | \ + CSCR_ARM_SRC_MPLL | \ + CSCR_SP_SEL | \ + CSCR_ARM_DIV(0) | \ + CSCR_FPM_EN | \ + CSCR_SPEN | \ + CSCR_MPEN | \ + CSCR_AHB_DIV(1) + +ENTRY(neso_pll_init) + + writel(IMX_PLL_PD(0) | + IMX_PLL_MFD(51) | + IMX_PLL_MFI(7) | + IMX_PLL_MFN(35), MPCTL0) /* 399 MHz */ + + writel(IMX_PLL_PD(1) | + IMX_PLL_MFD(12) | + IMX_PLL_MFI(9) | + IMX_PLL_MFN(3), SPCTL0) /* SPLL = 2 * 26 * 4.61538 MHz = 240 MHz */ + + writel(CSCR_VAL | CSCR_MPLL_RESTART | CSCR_SPLL_RESTART, CSCR) + + ldr r2, =16000 +1: + subs r2, r2, #1 + nop + bcs 1b + + mov pc, lr +ENDPROC(neso_pll_init) + diff --git a/board/imx21ads/Makefile b/arch/arm/boards/imx21ads/Makefile index 7993fdef8a..7993fdef8a 100644 --- a/board/imx21ads/Makefile +++ b/arch/arm/boards/imx21ads/Makefile diff --git a/board/imx21ads/config.h b/arch/arm/boards/imx21ads/config.h index edfb29ffb0..edfb29ffb0 100644 --- a/board/imx21ads/config.h +++ b/arch/arm/boards/imx21ads/config.h diff --git a/board/imx21ads/env/bin/init b/arch/arm/boards/imx21ads/env/bin/init index 224a6b40be..224a6b40be 100644 --- a/board/imx21ads/env/bin/init +++ b/arch/arm/boards/imx21ads/env/bin/init diff --git a/board/imx21ads/imx21ads.c b/arch/arm/boards/imx21ads/imx21ads.c index 5e88af4fd8..5e88af4fd8 100644 --- a/board/imx21ads/imx21ads.c +++ b/arch/arm/boards/imx21ads/imx21ads.c diff --git a/board/imx21ads/imx21ads.dox b/arch/arm/boards/imx21ads/imx21ads.dox index 9f11ffaa6e..9f11ffaa6e 100644 --- a/board/imx21ads/imx21ads.dox +++ b/arch/arm/boards/imx21ads/imx21ads.dox diff --git a/board/imx21ads/lowlevel_init.S b/arch/arm/boards/imx21ads/lowlevel_init.S index 607da27476..607da27476 100644 --- a/board/imx21ads/lowlevel_init.S +++ b/arch/arm/boards/imx21ads/lowlevel_init.S diff --git a/board/imx27ads/Makefile b/arch/arm/boards/imx27ads/Makefile index bdc905f0ef..bdc905f0ef 100644 --- a/board/imx27ads/Makefile +++ b/arch/arm/boards/imx27ads/Makefile diff --git a/board/imx27ads/config.h b/arch/arm/boards/imx27ads/config.h index b54a3c53d4..b54a3c53d4 100644 --- a/board/imx27ads/config.h +++ b/arch/arm/boards/imx27ads/config.h diff --git a/board/imx27ads/env/bin/_update b/arch/arm/boards/imx27ads/env/bin/_update index 014bce3512..014bce3512 100644 --- a/board/imx27ads/env/bin/_update +++ b/arch/arm/boards/imx27ads/env/bin/_update diff --git a/board/imx27ads/env/bin/boot b/arch/arm/boards/imx27ads/env/bin/boot index 3859dc113b..3859dc113b 100644 --- a/board/imx27ads/env/bin/boot +++ b/arch/arm/boards/imx27ads/env/bin/boot diff --git a/board/imx27ads/env/bin/init b/arch/arm/boards/imx27ads/env/bin/init index 48e2139f7d..48e2139f7d 100644 --- a/board/imx27ads/env/bin/init +++ b/arch/arm/boards/imx27ads/env/bin/init diff --git a/board/imx27ads/env/bin/update_kernel b/arch/arm/boards/imx27ads/env/bin/update_kernel index 1ad95fc5d6..1ad95fc5d6 100644 --- a/board/imx27ads/env/bin/update_kernel +++ b/arch/arm/boards/imx27ads/env/bin/update_kernel diff --git a/board/imx27ads/env/bin/update_root b/arch/arm/boards/imx27ads/env/bin/update_root index b757a5b922..b757a5b922 100644 --- a/board/imx27ads/env/bin/update_root +++ b/arch/arm/boards/imx27ads/env/bin/update_root diff --git a/board/imx27ads/env/config b/arch/arm/boards/imx27ads/env/config index f18a86b7c1..f18a86b7c1 100644 --- a/board/imx27ads/env/config +++ b/arch/arm/boards/imx27ads/env/config diff --git a/board/imx27ads/imx27ads.c b/arch/arm/boards/imx27ads/imx27ads.c index 6f31520ffa..6f31520ffa 100644 --- a/board/imx27ads/imx27ads.c +++ b/arch/arm/boards/imx27ads/imx27ads.c diff --git a/board/imx27ads/imx27ads.dox b/arch/arm/boards/imx27ads/imx27ads.dox index e14d8e3fab..e14d8e3fab 100644 --- a/board/imx27ads/imx27ads.dox +++ b/arch/arm/boards/imx27ads/imx27ads.dox diff --git a/board/imx27ads/lowlevel_init.S b/arch/arm/boards/imx27ads/lowlevel_init.S index df12aead00..df12aead00 100644 --- a/board/imx27ads/lowlevel_init.S +++ b/arch/arm/boards/imx27ads/lowlevel_init.S diff --git a/board/mmccpu/Makefile b/arch/arm/boards/mmccpu/Makefile index eb072c0161..eb072c0161 100644 --- a/board/mmccpu/Makefile +++ b/arch/arm/boards/mmccpu/Makefile diff --git a/board/mmccpu/config.h b/arch/arm/boards/mmccpu/config.h index 1133b8f040..1133b8f040 100644 --- a/board/mmccpu/config.h +++ b/arch/arm/boards/mmccpu/config.h diff --git a/board/kp_ukd_r1_num/env/bin/_update b/arch/arm/boards/mmccpu/env/bin/_update index 014bce3512..014bce3512 100644 --- a/board/kp_ukd_r1_num/env/bin/_update +++ b/arch/arm/boards/mmccpu/env/bin/_update diff --git a/board/mmccpu/env/bin/boot b/arch/arm/boards/mmccpu/env/bin/boot index 533dea7618..533dea7618 100644 --- a/board/mmccpu/env/bin/boot +++ b/arch/arm/boards/mmccpu/env/bin/boot diff --git a/board/mmccpu/env/bin/hush_hack b/arch/arm/boards/mmccpu/env/bin/hush_hack index 5fffa92ecd..5fffa92ecd 100644 --- a/board/mmccpu/env/bin/hush_hack +++ b/arch/arm/boards/mmccpu/env/bin/hush_hack diff --git a/board/mmccpu/env/bin/init b/arch/arm/boards/mmccpu/env/bin/init index ac84bd596f..ac84bd596f 100644 --- a/board/mmccpu/env/bin/init +++ b/arch/arm/boards/mmccpu/env/bin/init diff --git a/board/mmccpu/env/bin/update_kernel b/arch/arm/boards/mmccpu/env/bin/update_kernel index 05c822d860..05c822d860 100644 --- a/board/mmccpu/env/bin/update_kernel +++ b/arch/arm/boards/mmccpu/env/bin/update_kernel diff --git a/board/mmccpu/env/bin/update_root b/arch/arm/boards/mmccpu/env/bin/update_root index a75137237b..a75137237b 100644 --- a/board/mmccpu/env/bin/update_root +++ b/arch/arm/boards/mmccpu/env/bin/update_root diff --git a/board/mmccpu/env/config b/arch/arm/boards/mmccpu/env/config index 5367cd9f56..5367cd9f56 100644 --- a/board/mmccpu/env/config +++ b/arch/arm/boards/mmccpu/env/config diff --git a/board/mmccpu/init.c b/arch/arm/boards/mmccpu/init.c index e010a83104..e010a83104 100644 --- a/board/mmccpu/init.c +++ b/arch/arm/boards/mmccpu/init.c diff --git a/board/netx/Makefile b/arch/arm/boards/netx/Makefile index 8b33fec316..8b33fec316 100644 --- a/board/netx/Makefile +++ b/arch/arm/boards/netx/Makefile diff --git a/board/netx/config.h b/arch/arm/boards/netx/config.h index ca15136817..ca15136817 100644 --- a/board/netx/config.h +++ b/arch/arm/boards/netx/config.h diff --git a/board/netx/netx.c b/arch/arm/boards/netx/netx.c index d6bfcca54d..d6bfcca54d 100644 --- a/board/netx/netx.c +++ b/arch/arm/boards/netx/netx.c diff --git a/board/netx/netx.dox b/arch/arm/boards/netx/netx.dox index e22c5e8554..e22c5e8554 100644 --- a/board/netx/netx.dox +++ b/arch/arm/boards/netx/netx.dox diff --git a/board/netx/platform.S b/arch/arm/boards/netx/platform.S index 4961682322..4961682322 100644 --- a/board/netx/platform.S +++ b/arch/arm/boards/netx/platform.S diff --git a/board/omap/Kconfig b/arch/arm/boards/omap/Kconfig index 361bed436f..d612064710 100644 --- a/board/omap/Kconfig +++ b/arch/arm/boards/omap/Kconfig @@ -90,73 +90,4 @@ config MACH_OMAP_ADVANCED_MUX config HAS_OMAP_NAND bool - -menuconfig MACH_OMAP_GPMC_NAND - tristate "Enable NAND device support for GPMC" - depends on HAS_OMAP_NAND - default n - help - Say Y here if you would like to have NAND device support - -if MACH_OMAP_GPMC_NAND -# Single definition used to club all generic definitions together -config MACH_OMAP_GPMC_GENERICNAND - bool - -choice - prompt "Select NAND Device" - -config MACH_OMAP_GPMC_GENERICNAND_LP_X8 - tristate "Enable 8 bit Large page nand" - depends on MACH_OMAP_GPMC_NAND - select MACH_OMAP_GPMC_GENERICNAND - help - Say Y here if you would support for 8 bit NAND - with large page - NOTE: The timing parameter are the most relaxed - If you need optimized timing, selec appropriate - supported NAND flash - -config MACH_OMAP_GPMC_GENERICNAND_LP_X16 - tristate "Enable 16 bit Large page nand" - depends on MACH_OMAP_GPMC_NAND - select MACH_OMAP_GPMC_GENERICNAND - help - Say Y here if you would support for 16 bit NAND - with large page - NOTE: The timing parameter are the most relaxed - If you need optimized timing, selec appropriate - supported NAND flash - -config MACH_OMAP_GPMC_GENERICNAND_SP_X8 - tristate "Enable 8 bit Small page nand" - depends on MACH_OMAP_GPMC_NAND - select MACH_OMAP_GPMC_GENERICNAND - help - Say Y here if you would support for 8 bit NAND - with small page - NOTE: The timing parameter are the most relaxed - If you need optimized timing, selec appropriate - supported NAND flash - -config MACH_OMAP_GPMC_GENERICNAND_SP_X16 - tristate "Enable 16 bit Small page nand" - depends on MACH_OMAP_GPMC_NAND - select MACH_OMAP_GPMC_GENERICNAND - help - Say Y here if you would support for 16 bit NAND - with small page - NOTE: The timing parameter are the most relaxed - If you need optimized timing, selec appropriate - supported NAND flash -endchoice - -config MACH_OMAP_CS - hex "NAND CS" - depends on MACH_OMAP_GPMC_NAND - default 0x0 - help - Provide the Chip select of the NAND flash -endif - endmenu diff --git a/board/omap/Makefile b/arch/arm/boards/omap/Makefile index b4bb3e1d05..1e74e241b4 100644 --- a/board/omap/Makefile +++ b/arch/arm/boards/omap/Makefile @@ -24,5 +24,5 @@ obj-$(CONFIG_MACH_DO_LOWLEVEL_INIT) += platform.o obj-$(CONFIG_MACH_OMAP343xSDP) += board-sdp343x.o obj-$(CONFIG_MACH_BEAGLE) += board-beagle.o obj-$(CONFIG_MACH_OMAP3EVM) += board-omap3evm.o -obj-$(CONFIG_MACH_OMAP_GPMC_GENERICNAND) += devices-gpmc-nand.o +obj-y += devices-gpmc-nand.o diff --git a/arch/arm/boards/omap/board-beagle.c b/arch/arm/boards/omap/board-beagle.c new file mode 100644 index 0000000000..a4cbf313b8 --- /dev/null +++ b/arch/arm/boards/omap/board-beagle.c @@ -0,0 +1,274 @@ +/* + * (C) Copyright 2008 + * Texas Instruments, <www.ti.com> + * Raghavendra KH <r-khandenahally@ti.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +/** + * @file + * @brief Beagle Specific Board Initialization routines + */ + +/** + * @page ti_beagle Texas Instruments Beagle Board + * + * FileName: arch/arm/boards/omap/board-beagle.c + * + * Beagle Board from Texas Instruments as described here: + * http://www.beagleboard.org + * + * This board is based on OMAP3530. + * More on OMAP3530 (including documentation can be found here): + * http://focus.ti.com/docs/prod/folders/print/omap3530.html + * + * This file provides initialization in two stages: + * @li boot time initialization - do basics required to get SDRAM working. + * This is run from SRAM - so no case constructs and global vars can be used. + * @li run time initialization - this is for the rest of the initializations + * such as flash, uart etc. + * + * Boot time initialization includes: + * @li SDRAM initialization. + * @li Pin Muxing relevant for Beagle. + * + * Run time initialization includes + * @li serial @ref serial_ns16550.c driver device definition + * + * Originally from arch/arm/boards/omap/board-sdp343x.c + */ + +#include <common.h> +#include <console.h> +#include <init.h> +#include <driver.h> +#include <asm/io.h> +#include <ns16550.h> +#include <asm/armlinux.h> +#include <mach/silicon.h> +#include <mach/sdrc.h> +#include <mach/sys_info.h> +#include <mach/syslib.h> +#include <mach/control.h> +#include <mach/omap3-mux.h> +#include <mach/gpmc.h> +#include "board.h" + +/******************** Board Boot Time *******************/ + +/** + * @brief Do the SDRC initialization for 128Meg Micron DDR for CS0 + * + * @return void + */ +static void sdrc_init(void) +{ + /* SDRAM software reset */ + /* No idle ack and RESET enable */ + writel(0x1A, SDRC_REG(SYSCONFIG)); + sdelay(100); + /* No idle ack and RESET disable */ + writel(0x18, SDRC_REG(SYSCONFIG)); + + /* SDRC Sharing register */ + /* 32-bit SDRAM on data lane [31:0] - CS0 */ + /* pin tri-stated = 1 */ + writel(0x00000100, SDRC_REG(SHARING)); + + /* ----- SDRC Registers Configuration --------- */ + /* SDRC_MCFG0 register */ + writel(0x02584099, SDRC_REG(MCFG_0)); + + /* SDRC_RFR_CTRL0 register */ + writel(0x54601, SDRC_REG(RFR_CTRL_0)); + + /* SDRC_ACTIM_CTRLA0 register */ + writel(0xA29DB4C6, SDRC_REG(ACTIM_CTRLA_0)); + + /* SDRC_ACTIM_CTRLB0 register */ + writel(0x12214, SDRC_REG(ACTIM_CTRLB_0)); + + /* Disble Power Down of CKE due to 1 CKE on combo part */ + writel(0x00000081, SDRC_REG(POWER)); + + /* SDRC_MANUAL command register */ + /* NOP command */ + writel(0x00000000, SDRC_REG(MANUAL_0)); + /* Precharge command */ + writel(0x00000001, SDRC_REG(MANUAL_0)); + /* Auto-refresh command */ + writel(0x00000002, SDRC_REG(MANUAL_0)); + /* Auto-refresh command */ + writel(0x00000002, SDRC_REG(MANUAL_0)); + + /* SDRC MR0 register Burst length=4 */ + writel(0x00000032, SDRC_REG(MR_0)); + + /* SDRC DLLA control register */ + writel(0x0000000A, SDRC_REG(DLLA_CTRL)); + + return; +} + +/** + * @brief Do the pin muxing required for Board operation. + * We enable ONLY the pins we require to set. OMAP provides pins which do not + * have alternate modes. Such pins done need to be set. + * + * See @ref MUX_VAL for description of the muxing mode. + * + * @return void + */ +static void mux_config(void) +{ + /* SDRC_D0 - SDRC_D31 default mux mode is mode0 */ + + /* GPMC */ + MUX_VAL(CP(GPMC_A1), (IDIS | PTD | DIS | M0)); + MUX_VAL(CP(GPMC_A2), (IDIS | PTD | DIS | M0)); + MUX_VAL(CP(GPMC_A3), (IDIS | PTD | DIS | M0)); + MUX_VAL(CP(GPMC_A4), (IDIS | PTD | DIS | M0)); + MUX_VAL(CP(GPMC_A5), (IDIS | PTD | DIS | M0)); + MUX_VAL(CP(GPMC_A6), (IDIS | PTD | DIS | M0)); + MUX_VAL(CP(GPMC_A7), (IDIS | PTD | DIS | M0)); + MUX_VAL(CP(GPMC_A8), (IDIS | PTD | DIS | M0)); + MUX_VAL(CP(GPMC_A9), (IDIS | PTD | DIS | M0)); + MUX_VAL(CP(GPMC_A10), (IDIS | PTD | DIS | M0)); + + /* D0-D7 default mux mode is mode0 */ + MUX_VAL(CP(GPMC_D8), (IEN | PTD | DIS | M0)); + MUX_VAL(CP(GPMC_D9), (IEN | PTD | DIS | M0)); + MUX_VAL(CP(GPMC_D10), (IEN | PTD | DIS | M0)); + MUX_VAL(CP(GPMC_D11), (IEN | PTD | DIS | M0)); + MUX_VAL(CP(GPMC_D12), (IEN | PTD | DIS | M0)); + MUX_VAL(CP(GPMC_D13), (IEN | PTD | DIS | M0)); + MUX_VAL(CP(GPMC_D14), (IEN | PTD | DIS | M0)); + MUX_VAL(CP(GPMC_D15), (IEN | PTD | DIS | M0)); + MUX_VAL(CP(GPMC_CLK), (IDIS | PTD | DIS | M0)); + /* GPMC_NADV_ALE default mux mode is mode0 */ + /* GPMC_NOE default mux mode is mode0 */ + /* GPMC_NWE default mux mode is mode0 */ + /* GPMC_NBE0_CLE default mux mode is mode0 */ + MUX_VAL(CP(GPMC_NBE0_CLE), (IDIS | PTD | DIS | M0)); + MUX_VAL(CP(GPMC_NBE1), (IEN | PTD | DIS | M0)); + MUX_VAL(CP(GPMC_NWP), (IEN | PTD | DIS | M0)); + /* GPMC_WAIT0 default mux mode is mode0 */ + MUX_VAL(CP(GPMC_WAIT1), (IEN | PTU | EN | M0)); + + /* SERIAL INTERFACE */ + MUX_VAL(CP(UART3_CTS_RCTX), (IEN | PTD | EN | M0)); + MUX_VAL(CP(UART3_RTS_SD), (IDIS | PTD | DIS | M0)); + MUX_VAL(CP(UART3_RX_IRRX), (IEN | PTD | DIS | M0)); + MUX_VAL(CP(UART3_TX_IRTX), (IDIS | PTD | DIS | M0)); + MUX_VAL(CP(HSUSB0_CLK), (IEN | PTD | DIS | M0)); + MUX_VAL(CP(HSUSB0_STP), (IDIS | PTU | EN | M0)); + MUX_VAL(CP(HSUSB0_DIR), (IEN | PTD | DIS | M0)); + MUX_VAL(CP(HSUSB0_NXT), (IEN | PTD | DIS | M0)); + MUX_VAL(CP(HSUSB0_DATA0), (IEN | PTD | DIS | M0)); + MUX_VAL(CP(HSUSB0_DATA1), (IEN | PTD | DIS | M0)); + MUX_VAL(CP(HSUSB0_DATA2), (IEN | PTD | DIS | M0)); + MUX_VAL(CP(HSUSB0_DATA3), (IEN | PTD | DIS | M0)); + MUX_VAL(CP(HSUSB0_DATA4), (IEN | PTD | DIS | M0)); + MUX_VAL(CP(HSUSB0_DATA5), (IEN | PTD | DIS | M0)); + MUX_VAL(CP(HSUSB0_DATA6), (IEN | PTD | DIS | M0)); + MUX_VAL(CP(HSUSB0_DATA7), (IEN | PTD | DIS | M0)); + /* I2C1_SCL default mux mode is mode0 */ + /* I2C1_SDA default mux mode is mode0 */ +} + +/** + * @brief The basic entry point for board initialization. + * + * This is called as part of machine init (after arch init). + * This is again called with stack in SRAM, so not too many + * constructs possible here. + * + * @return void + */ +void board_init(void) +{ + int in_sdram = running_in_sdram(); + + mux_config(); + /* Dont reconfigure SDRAM while running in SDRAM! */ + if (!in_sdram) + sdrc_init(); +} + +/******************** Board Run Time *******************/ + +#ifdef CONFIG_DRIVER_SERIAL_NS16550 + +static struct NS16550_plat serial_plat = { + .clock = 48000000, /* 48MHz (APLL96/2) */ + .f_caps = CONSOLE_STDIN | CONSOLE_STDOUT | CONSOLE_STDERR, + .reg_read = omap_uart_read, + .reg_write = omap_uart_write, +}; + +static struct device_d beagle_serial_device = { + .name = "serial_ns16550", + .map_base = OMAP_UART3_BASE, + .size = 1024, + .platform_data = (void *)&serial_plat, +}; + +/** + * @brief UART serial port initialization - remember to enable COM clocks in + * arch + * + * @return result of device registration + */ +static int beagle_console_init(void) +{ + /* Register the serial port */ + return register_device(&beagle_serial_device); +} +console_initcall(beagle_console_init); +#endif /* CONFIG_DRIVER_SERIAL_NS16550 */ + +static struct memory_platform_data sram_pdata = { + .name = "ram0", + .flags = DEVFS_RDWR, +}; + +static struct device_d sdram_dev = { + .name = "mem", + .map_base = 0x80000000, + .size = 128 * 1024 * 1024, + .platform_data = &sram_pdata, +}; + +static int beagle_devices_init(void) +{ + int ret; + + ret = register_device(&sdram_dev); + if (ret) + goto failed; + +#ifdef CONFIG_GPMC + /* WP is made high and WAIT1 active Low */ + gpmc_generic_init(0x10); +#endif + gpmc_generic_nand_devices_init(0, 16, 1); + + armlinux_add_dram(&sdram_dev); +failed: + return ret; +} +device_initcall(beagle_devices_init); diff --git a/board/omap/board-omap3evm.c b/arch/arm/boards/omap/board-omap3evm.c index fdea0ff3c5..619ea94485 100644 --- a/board/omap/board-omap3evm.c +++ b/arch/arm/boards/omap/board-omap3evm.c @@ -2,7 +2,7 @@ * @file * @brief Board Initialization routines for OMAP3EVM. * - * FileName: board/omap/board-omap3evm.c + * FileName: arch/arm/boards/omap/board-omap3evm.c * * This board is based on OMAP3530. * More on OMAP3530 (including documentation can be found here): @@ -21,7 +21,7 @@ * Run time initialization includes * @li serial @ref serial_ns16550.c driver device definition * - * Originally from board/omap/board-beagle.c + * Originally from arch/arm/boards/omap/board-beagle.c */ /* diff --git a/board/omap/board-sdp343x.c b/arch/arm/boards/omap/board-sdp343x.c index fe95fc0235..32d1a4235c 100644 --- a/board/omap/board-sdp343x.c +++ b/arch/arm/boards/omap/board-sdp343x.c @@ -27,7 +27,7 @@ /** * @page ti_SDP3430 Texas Instruments SDP3430 * - * FileName: board/omap/board-sdp343x.c + * FileName: arch/arm/boards/omap/board-sdp343x.c * * SDP3430 from Texas Instruments as described here: * http://www.ti.com/omap3430_devplatform diff --git a/board/omap/board.h b/arch/arm/boards/omap/board.h index ee3e25e76f..cf231a208e 100644 --- a/board/omap/board.h +++ b/arch/arm/boards/omap/board.h @@ -2,7 +2,7 @@ * @file * @brief exported generic APIs which various board files implement * - * FileName: board/omap/board.h + * FileName: arch/arm/boards/omap/board.h * * This file will not contain any board specific implementations. */ diff --git a/board/omap/config.h b/arch/arm/boards/omap/config.h index 28b62730e4..29d2ee2317 100644 --- a/board/omap/config.h +++ b/arch/arm/boards/omap/config.h @@ -2,7 +2,7 @@ * @file * @brief provide a wrapper for standard malloc and stack size defines * - * FileName: board/omap/config.h + * FileName: arch/arm/boards/omap/config.h * * Standard defines should be configurable for us to move Stack and malloc * areas around this defines some basics for that @@ -30,9 +30,4 @@ #ifndef __MACH_OMAP_CONFIG_H #define __MACH_OMAP_CONFIG_H -/** define CFG_MALLOC_LEN from Kconfig define */ -#define CFG_MALLOC_LEN CONFIG_OMAP_MALLOC_LEN -/** define CONFIG_STACKSIZE from Kconfig define */ -#define CONFIG_STACKSIZE CONFIG_OMAP_CONFIG_STACKSIZE - #endif /* __MACH_OMAP_CONFIG_H */ diff --git a/board/omap/devices-gpmc-nand.c b/arch/arm/boards/omap/devices-gpmc-nand.c index bbcceafe88..ac23e9d036 100644 --- a/board/omap/devices-gpmc-nand.c +++ b/arch/arm/boards/omap/devices-gpmc-nand.c @@ -2,7 +2,7 @@ * @file * @brief GPMC specific NAND devices * - * FileName: board/omap/devices-gpmc-nand.c + * FileName: arch/arm/boards/omap/devices-gpmc-nand.c * * GPMC NAND Devices such as those from Micron, Samsung are listed here */ @@ -37,47 +37,14 @@ #include <mach/gpmc.h> #include <mach/gpmc_nand.h> -#ifdef CONFIG_MACH_OMAP_GPMC_GENERICNAND - #define GPMC_CONF1_VALx8 0x00000800 #define GPMC_CONF1_VALx16 0x00001800 /* Set up the generic params */ -#ifdef CONFIG_MACH_OMAP_GPMC_GENERICNAND_LP_X8 -#define GPMC_NAND_ECC_LAYOUT GPMC_NAND_ECC_LP_x8_LAYOUT -#define GPMC_CONF1_VAL GPMC_CONF1_VALx8 -#define GPMC_NAND_DEV_WIDTH 8 -#endif - -#ifdef CONFIG_MACH_OMAP_GPMC_GENERICNAND_LP_X16 -#define GPMC_NAND_ECC_LAYOUT GPMC_NAND_ECC_LP_x16_LAYOUT -#define GPMC_CONF1_VAL GPMC_CONF1_VALx16 -#define GPMC_NAND_DEV_WIDTH 16 -#endif - -#ifdef CONFIG_MACH_OMAP_GPMC_GENERICNAND_SP_X8 -#define GPMC_NAND_ECC_LAYOUT GPMC_NAND_ECC_SP_x8_LAYOUT -#define GPMC_CONF1_VAL GPMC_CONF1_VALx8 -#define GPMC_NAND_DEV_WIDTH 8 -#endif - -#ifdef CONFIG_MACH_OMAP_GPMC_GENERICNAND_SP_X16 -#define GPMC_NAND_ECC_LAYOUT GPMC_NAND_ECC_SP_x16_LAYOUT -#define GPMC_CONF1_VAL GPMC_CONF1_VALx16 -#define GPMC_NAND_DEV_WIDTH 16 -#endif - -#ifdef CONFIG_NAND_OMAP_GPMC_HWECC -/** - * The following is the organization of ECC as expected by BootROM. - * Based on the type of device, this can vary. select the config accordingly - */ -static struct nand_ecclayout boot_rom_ecc = GPMC_NAND_ECC_LAYOUT; -#endif /** GPMC timing for our nand device */ static struct gpmc_config nand_cfg = { .cfg = { - GPMC_CONF1_VAL, /*CONF1 */ + 0, /*CONF1 */ 0x00141400, /*CONF2 */ 0x00141400, /*CONF3 */ 0x0F010F01, /*CONF4 */ @@ -97,16 +64,9 @@ static struct gpmc_config nand_cfg = { /** NAND platform specific settings settings */ static struct gpmc_nand_platform_data nand_plat = { - .cs = CONFIG_MACH_OMAP_CS, - .device_width = GPMC_NAND_DEV_WIDTH, + .cs = 0, .max_timeout = MSECOND, .wait_mon_pin = 0, -#ifdef CONFIG_NAND_OMAP_GPMC_HWECC - .plat_options = NAND_HWECC_ENABLE, - .oob = &boot_rom_ecc, -#else - .plat_options = NAND_HWECC_DISABLE, -#endif .priv = (void *)&nand_cfg, }; @@ -123,16 +83,19 @@ static struct device_d gpmc_generic_nand_nand_device = { * * @return success/fail based on device funtion */ -static int gpmc_generic_nand_devices_init(void) +int gpmc_generic_nand_devices_init(int cs, int width, int hwecc) { + nand_plat.cs = cs; + + if (width == 16) + nand_cfg.cfg[0] = GPMC_CONF1_VALx16; + else + nand_cfg.cfg[0] = GPMC_CONF1_VALx8; + + nand_plat.device_width = width; + nand_plat.plat_options = hwecc ? NAND_HWECC_ENABLE : 0; + /* Configure GPMC CS before register */ gpmc_cs_config(nand_plat.cs, &nand_cfg); return register_device(&gpmc_generic_nand_nand_device); } - -device_initcall(gpmc_generic_nand_devices_init); - -#endif /* CONFIG_MACH_OMAP_GPMC_GENERICNAND */ - -/* All specific optimized nand configurations for GPMC NAND comes here */ - diff --git a/board/omap/env/bin/init b/arch/arm/boards/omap/env/bin/init index 224a6b40be..224a6b40be 100644 --- a/board/omap/env/bin/init +++ b/arch/arm/boards/omap/env/bin/init diff --git a/board/omap/platform.S b/arch/arm/boards/omap/platform.S index 9014a8f1c7..77b7eed906 100644 --- a/board/omap/platform.S +++ b/arch/arm/boards/omap/platform.S @@ -2,7 +2,7 @@ * @file * @brief Wrapper to call board level initialization routine * - * FileName: board/omap/platform.S + * FileName: arch/arm/boards/omap/platform.S * * board_init_lowlevel is defined here. This calls board_init which * is linked to the binary - the board_init only has a SRAM stack. diff --git a/board/pcm037/Makefile b/arch/arm/boards/pcm037/Makefile index 7d36b77df0..7d36b77df0 100644 --- a/board/pcm037/Makefile +++ b/arch/arm/boards/pcm037/Makefile diff --git a/board/pcm037/config.h b/arch/arm/boards/pcm037/config.h index 5495d0309c..5495d0309c 100644 --- a/board/pcm037/config.h +++ b/arch/arm/boards/pcm037/config.h diff --git a/board/pcm037/env/config b/arch/arm/boards/pcm037/env/config index bf15620057..bf15620057 100644 --- a/board/pcm037/env/config +++ b/arch/arm/boards/pcm037/env/config diff --git a/board/pcm037/lowlevel_init.S b/arch/arm/boards/pcm037/lowlevel_init.S index 8988db23a2..8988db23a2 100644 --- a/board/pcm037/lowlevel_init.S +++ b/arch/arm/boards/pcm037/lowlevel_init.S diff --git a/board/pcm037/pcm037.c b/arch/arm/boards/pcm037/pcm037.c index 2e6968b844..2e6968b844 100644 --- a/board/pcm037/pcm037.c +++ b/arch/arm/boards/pcm037/pcm037.c diff --git a/board/pcm037/pcm037.dox b/arch/arm/boards/pcm037/pcm037.dox index b2afdd6acd..b2afdd6acd 100644 --- a/board/pcm037/pcm037.dox +++ b/arch/arm/boards/pcm037/pcm037.dox diff --git a/board/pcm038/Makefile b/arch/arm/boards/pcm038/Makefile index a681ddafb9..a681ddafb9 100644 --- a/board/pcm038/Makefile +++ b/arch/arm/boards/pcm038/Makefile diff --git a/board/phycard-i.MX27/config.h b/arch/arm/boards/pcm038/config.h index c2f5e7cc58..c2f5e7cc58 100644 --- a/board/phycard-i.MX27/config.h +++ b/arch/arm/boards/pcm038/config.h diff --git a/board/pcm038/env/config b/arch/arm/boards/pcm038/env/config index a8be5c924d..a8be5c924d 100644 --- a/board/pcm038/env/config +++ b/arch/arm/boards/pcm038/env/config diff --git a/arch/arm/boards/pcm038/lowlevel.c b/arch/arm/boards/pcm038/lowlevel.c new file mode 100644 index 0000000000..0c376f2fae --- /dev/null +++ b/arch/arm/boards/pcm038/lowlevel.c @@ -0,0 +1,117 @@ +/* + * + * (c) 2007 Pengutronix, Sascha Hauer <s.hauer@pengutronix.de> + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ +#include <common.h> +#include <init.h> +#include <mach/imx-regs.h> +#include <mach/imx-pll.h> +#include <mach/esdctl.h> +#include <asm/cache-l2x0.h> +#include <asm/io.h> +#include <mach/imx-nand.h> +#include <asm/barebox-arm.h> +#include <asm/system.h> +#include <asm-generic/memory_layout.h> + +static void __bare_init __naked insdram(void) +{ + uint32_t r; + + PCCR1 |= PCCR1_NFC_BAUDEN; + + /* setup a stack to be able to call imx_nand_load_image() */ + r = STACK_BASE + STACK_SIZE - 12; + __asm__ __volatile__("mov sp, %0" : : "r"(r)); + + imx_nand_load_image((void *)TEXT_BASE, 256 * 1024); + + board_init_lowlevel_return(); +} + +#define ESDCTL0_VAL (ESDCTL0_SDE | ESDCTL0_ROW13 | ESDCTL0_COL10) + +void __bare_init __naked board_init_lowlevel(void) +{ + uint32_t r; + int i; + unsigned int *trg, *src; + + /* ahb lite ip interface */ + AIPI1_PSR0 = 0x20040304; + AIPI1_PSR1 = 0xDFFBFCFB; + AIPI2_PSR0 = 0x00000000; + AIPI2_PSR1 = 0xFFFFFFFF; + + /* Skip SDRAM initialization if we run from RAM */ + r = get_pc(); + if (r > 0xa0000000 && r < 0xb0000000) + board_init_lowlevel_return(); + + /* + * DDR on CSD0 + */ + writel(0x00000008, ESDMISC); /* Enable DDR SDRAM operation */ + + DSCR(3) = 0x55555555; /* Set the driving strength */ + DSCR(5) = 0x55555555; + DSCR(6) = 0x55555555; + DSCR(7) = 0x00005005; + DSCR(8) = 0x15555555; + + writel(0x00000004, ESDMISC); /* Initial reset */ + writel(0x006ac73a, ESDCFG0); + + writel(ESDCTL0_VAL | ESDCTL0_SMODE_PRECHARGE, ESDCTL0); /* precharge CSD0 all banks */ + writel(0x00000000, 0xA0000F00); /* CSD0 precharge address (A10 = 1) */ + writel(ESDCTL0_VAL | ESDCTL0_SMODE_AUTO_REFRESH, ESDCTL0); + + for (i = 0; i < 8; i++) + writel(0, 0xa0000f00); + + writel(ESDCTL0_VAL | ESDCTL0_SMODE_LOAD_MODE, ESDCTL0); + + writeb(0xda, 0xa0000033); + writeb(0xff, 0xa1000000); + writel(ESDCTL0_VAL | ESDCTL0_DSIZ_31_0 | ESDCTL0_REF4 | + ESDCTL0_BL | ESDCTL0_SMODE_NORMAL, ESDCTL0); + +#ifdef CONFIG_NAND_IMX_BOOT + /* skip NAND boot if not running from NFC space */ + r = get_pc(); + if (r < IMX_NFC_BASE || r > IMX_NFC_BASE + 0x800) + board_init_lowlevel_return(); + + src = (unsigned int *)IMX_NFC_BASE; + trg = (unsigned int *)TEXT_BASE; + + /* Move ourselves out of NFC SRAM */ + for (i = 0; i < 0x800 / sizeof(int); i++) + *trg++ = *src++; + + /* Jump to SDRAM */ + r = (unsigned int)&insdram; + __asm__ __volatile__("mov pc, %0" : : "r"(r)); +#else + board_init_lowlevel_return(); +#endif +} + diff --git a/board/pcm038/pcm038.c b/arch/arm/boards/pcm038/pcm038.c index 03794fc13d..03794fc13d 100644 --- a/board/pcm038/pcm038.c +++ b/arch/arm/boards/pcm038/pcm038.c diff --git a/board/pcm038/pcm038.dox b/arch/arm/boards/pcm038/pcm038.dox index 9b17674a21..9b17674a21 100644 --- a/board/pcm038/pcm038.dox +++ b/arch/arm/boards/pcm038/pcm038.dox diff --git a/board/pcm038/pll_init.S b/arch/arm/boards/pcm038/pll_init.S index 0c1ff13415..0c1ff13415 100644 --- a/board/pcm038/pll_init.S +++ b/arch/arm/boards/pcm038/pll_init.S diff --git a/board/pcm043/Makefile b/arch/arm/boards/pcm043/Makefile index 6753bbe81a..6753bbe81a 100644 --- a/board/pcm043/Makefile +++ b/arch/arm/boards/pcm043/Makefile diff --git a/board/pcm043/config.h b/arch/arm/boards/pcm043/config.h index 0e3b175a62..0e3b175a62 100644 --- a/board/pcm043/config.h +++ b/arch/arm/boards/pcm043/config.h diff --git a/board/pcm043/env/config b/arch/arm/boards/pcm043/env/config index 212b6a9cd8..212b6a9cd8 100644 --- a/board/pcm043/env/config +++ b/arch/arm/boards/pcm043/env/config diff --git a/board/pcm043/lowlevel.c b/arch/arm/boards/pcm043/lowlevel.c index 9eff5a6713..9eff5a6713 100644 --- a/board/pcm043/lowlevel.c +++ b/arch/arm/boards/pcm043/lowlevel.c diff --git a/board/pcm043/pcm043.c b/arch/arm/boards/pcm043/pcm043.c index dd178ed787..dd178ed787 100644 --- a/board/pcm043/pcm043.c +++ b/arch/arm/boards/pcm043/pcm043.c diff --git a/board/pcm043/pcm043.dox b/arch/arm/boards/pcm043/pcm043.dox index c6715fffcf..c6715fffcf 100644 --- a/board/pcm043/pcm043.dox +++ b/arch/arm/boards/pcm043/pcm043.dox diff --git a/board/phycard-i.MX27/Makefile b/arch/arm/boards/phycard-i.MX27/Makefile index fd52350fbb..fd52350fbb 100644 --- a/board/phycard-i.MX27/Makefile +++ b/arch/arm/boards/phycard-i.MX27/Makefile diff --git a/arch/arm/boards/phycard-i.MX27/config.h b/arch/arm/boards/phycard-i.MX27/config.h new file mode 100644 index 0000000000..c2f5e7cc58 --- /dev/null +++ b/arch/arm/boards/phycard-i.MX27/config.h @@ -0,0 +1,26 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +/** + * @file + * @brief Global defintions for the ARM i.MX27 based pcm038 + */ + +#ifndef __CONFIG_H +#define __CONFIG_H + +#endif /* __CONFIG_H */ diff --git a/board/phycard-i.MX27/env/config b/arch/arm/boards/phycard-i.MX27/env/config index d0670dec2c..d0670dec2c 100644 --- a/board/phycard-i.MX27/env/config +++ b/arch/arm/boards/phycard-i.MX27/env/config diff --git a/board/phycard-i.MX27/lowlevel_init.S b/arch/arm/boards/phycard-i.MX27/lowlevel_init.S index 9349581165..9349581165 100644 --- a/board/phycard-i.MX27/lowlevel_init.S +++ b/arch/arm/boards/phycard-i.MX27/lowlevel_init.S diff --git a/board/phycard-i.MX27/pca100.c b/arch/arm/boards/phycard-i.MX27/pca100.c index ce59960e22..ce59960e22 100644 --- a/board/phycard-i.MX27/pca100.c +++ b/arch/arm/boards/phycard-i.MX27/pca100.c diff --git a/board/phycard-i.MX27/pca100.dox b/arch/arm/boards/phycard-i.MX27/pca100.dox index 9b17674a21..9b17674a21 100644 --- a/board/phycard-i.MX27/pca100.dox +++ b/arch/arm/boards/phycard-i.MX27/pca100.dox diff --git a/board/pm9263/Makefile b/arch/arm/boards/pm9263/Makefile index eb072c0161..eb072c0161 100644 --- a/board/pm9263/Makefile +++ b/arch/arm/boards/pm9263/Makefile diff --git a/board/pm9263/config.h b/arch/arm/boards/pm9263/config.h index 9a9c5cdcb7..9a9c5cdcb7 100644 --- a/board/pm9263/config.h +++ b/arch/arm/boards/pm9263/config.h diff --git a/board/mmccpu/env/bin/_update b/arch/arm/boards/pm9263/env/bin/_update index 014bce3512..014bce3512 100644 --- a/board/mmccpu/env/bin/_update +++ b/arch/arm/boards/pm9263/env/bin/_update diff --git a/board/pm9263/env/bin/boot b/arch/arm/boards/pm9263/env/bin/boot index 533dea7618..533dea7618 100644 --- a/board/pm9263/env/bin/boot +++ b/arch/arm/boards/pm9263/env/bin/boot diff --git a/board/pm9263/env/bin/hush_hack b/arch/arm/boards/pm9263/env/bin/hush_hack index 5fffa92ecd..5fffa92ecd 100644 --- a/board/pm9263/env/bin/hush_hack +++ b/arch/arm/boards/pm9263/env/bin/hush_hack diff --git a/board/pm9263/env/bin/init b/arch/arm/boards/pm9263/env/bin/init index 02f5cd415b..02f5cd415b 100644 --- a/board/pm9263/env/bin/init +++ b/arch/arm/boards/pm9263/env/bin/init diff --git a/board/pm9263/env/bin/update_kernel b/arch/arm/boards/pm9263/env/bin/update_kernel index 05c822d860..05c822d860 100644 --- a/board/pm9263/env/bin/update_kernel +++ b/arch/arm/boards/pm9263/env/bin/update_kernel diff --git a/board/pm9263/env/bin/update_root b/arch/arm/boards/pm9263/env/bin/update_root index a75137237b..a75137237b 100644 --- a/board/pm9263/env/bin/update_root +++ b/arch/arm/boards/pm9263/env/bin/update_root diff --git a/board/pm9263/env/config b/arch/arm/boards/pm9263/env/config index 75132911c4..75132911c4 100644 --- a/board/pm9263/env/config +++ b/arch/arm/boards/pm9263/env/config diff --git a/board/pm9263/init.c b/arch/arm/boards/pm9263/init.c index 88b91eafa9..88b91eafa9 100644 --- a/board/pm9263/init.c +++ b/arch/arm/boards/pm9263/init.c diff --git a/board/scb9328/Makefile b/arch/arm/boards/scb9328/Makefile index db6fd7ec37..db6fd7ec37 100644 --- a/board/scb9328/Makefile +++ b/arch/arm/boards/scb9328/Makefile diff --git a/board/scb9328/config.h b/arch/arm/boards/scb9328/config.h index cc22b7ae72..cc22b7ae72 100644 --- a/board/scb9328/config.h +++ b/arch/arm/boards/scb9328/config.h diff --git a/board/scb9328/env/bin/init b/arch/arm/boards/scb9328/env/bin/init index 416669f851..416669f851 100644 --- a/board/scb9328/env/bin/init +++ b/arch/arm/boards/scb9328/env/bin/init diff --git a/board/scb9328/lowlevel_init.S b/arch/arm/boards/scb9328/lowlevel_init.S index 5b024286bb..5b024286bb 100644 --- a/board/scb9328/lowlevel_init.S +++ b/arch/arm/boards/scb9328/lowlevel_init.S diff --git a/board/scb9328/scb9328.c b/arch/arm/boards/scb9328/scb9328.c index e781393eae..e781393eae 100644 --- a/board/scb9328/scb9328.c +++ b/arch/arm/boards/scb9328/scb9328.c diff --git a/board/scb9328/scb9328.dox b/arch/arm/boards/scb9328/scb9328.dox index 75bc7c8c1a..75bc7c8c1a 100644 --- a/board/scb9328/scb9328.dox +++ b/arch/arm/boards/scb9328/scb9328.dox diff --git a/arch/arm/configs/a9m2410_defconfig b/arch/arm/configs/a9m2410_defconfig index 9429a3763d..9e888fc6b9 100644 --- a/arch/arm/configs/a9m2410_defconfig +++ b/arch/arm/configs/a9m2410_defconfig @@ -100,7 +100,7 @@ CONFIG_CONSOLE_ACTIVATE_FIRST=y # CONFIG_OF_FLAT_TREE is not set CONFIG_PARTITION=y CONFIG_DEFAULT_ENVIRONMENT=y -CONFIG_DEFAULT_ENVIRONMENT_PATH="board/a9m2410/env" +CONFIG_DEFAULT_ENVIRONMENT_PATH="arch/arm/boards/a9m2410/env" # # Debugging diff --git a/arch/arm/configs/a9m2440_defconfig b/arch/arm/configs/a9m2440_defconfig index 1fcabbb62e..f69dcfd76a 100644 --- a/arch/arm/configs/a9m2440_defconfig +++ b/arch/arm/configs/a9m2440_defconfig @@ -101,7 +101,7 @@ CONFIG_CONSOLE_ACTIVATE_FIRST=y # CONFIG_OF_FLAT_TREE is not set CONFIG_PARTITION=y CONFIG_DEFAULT_ENVIRONMENT=y -CONFIG_DEFAULT_ENVIRONMENT_PATH="board/a9m2440/env" +CONFIG_DEFAULT_ENVIRONMENT_PATH="arch/arm/boards/a9m2440/env" # # Debugging diff --git a/arch/arm/configs/at91sam9260ek_defconfig b/arch/arm/configs/at91sam9260ek_defconfig index 61df756932..b40485b46f 100644 --- a/arch/arm/configs/at91sam9260ek_defconfig +++ b/arch/arm/configs/at91sam9260ek_defconfig @@ -89,7 +89,7 @@ CONFIG_CONSOLE_ACTIVATE_FIRST=y # CONFIG_OF_FLAT_TREE is not set CONFIG_PARTITION=y CONFIG_DEFAULT_ENVIRONMENT=y -CONFIG_DEFAULT_ENVIRONMENT_PATH="board/at91sam9260ek/env" +CONFIG_DEFAULT_ENVIRONMENT_PATH="arch/arm/boards/at91sam9260ek/env" # # Debugging diff --git a/arch/arm/configs/at91sam9263ek_defconfig b/arch/arm/configs/at91sam9263ek_defconfig index eb47856b05..d423c2fdfd 100644 --- a/arch/arm/configs/at91sam9263ek_defconfig +++ b/arch/arm/configs/at91sam9263ek_defconfig @@ -93,7 +93,7 @@ CONFIG_CONSOLE_ACTIVATE_FIRST=y # CONFIG_OF_FLAT_TREE is not set CONFIG_PARTITION=y CONFIG_DEFAULT_ENVIRONMENT=y -CONFIG_DEFAULT_ENVIRONMENT_PATH="board/at91sam9263ek/env" +CONFIG_DEFAULT_ENVIRONMENT_PATH="arch/arm/boards/at91sam9263ek/env" # # Debugging diff --git a/arch/arm/configs/edb93xx_defconfig b/arch/arm/configs/edb93xx_defconfig index d6b4b19f14..d8fe23f70f 100644 --- a/arch/arm/configs/edb93xx_defconfig +++ b/arch/arm/configs/edb93xx_defconfig @@ -103,7 +103,7 @@ CONFIG_CONSOLE_ACTIVATE_FIRST=y # CONFIG_OF_FLAT_TREE is not set CONFIG_PARTITION=y CONFIG_DEFAULT_ENVIRONMENT=y -CONFIG_DEFAULT_ENVIRONMENT_PATH="board/edb93xx/env" +CONFIG_DEFAULT_ENVIRONMENT_PATH="arch/arm/boards/edb93xx/env" # # Debugging diff --git a/arch/arm/configs/eukrea_cpuimx25_defconfig b/arch/arm/configs/eukrea_cpuimx25_defconfig index 574d32292d..14f06c2b80 100644 --- a/arch/arm/configs/eukrea_cpuimx25_defconfig +++ b/arch/arm/configs/eukrea_cpuimx25_defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit -# barebox version: 2010.05.0 -# Wed Jun 2 01:04:00 2010 +# barebox version: 2010.07.0 +# Thu Jul 29 09:54:50 2010 # # CONFIG_BOARD_LINKER_SCRIPT is not set CONFIG_GENERIC_LINKER_SCRIPT=y @@ -108,7 +108,7 @@ CONFIG_CONSOLE_ACTIVATE_FIRST=y # CONFIG_OF_FLAT_TREE is not set CONFIG_PARTITION=y CONFIG_DEFAULT_ENVIRONMENT=y -CONFIG_DEFAULT_ENVIRONMENT_PATH="board/eukrea_cpuimx25/env" +CONFIG_DEFAULT_ENVIRONMENT_PATH="arch/arm/boards/eukrea_cpuimx25/env" # # Debugging @@ -169,6 +169,7 @@ CONFIG_CMD_MTEST=y # flash # CONFIG_CMD_FLASH=y +# CONFIG_CMD_UBI is not set # # booting @@ -193,10 +194,12 @@ CONFIG_CMD_GPIO=y CONFIG_CMD_UNLZO=y CONFIG_NET=y CONFIG_NET_DHCP=y -# CONFIG_NET_RARP is not set # CONFIG_NET_NFS is not set CONFIG_NET_PING=y CONFIG_NET_TFTP=y +# CONFIG_NET_TFTP_PUSH is not set +# CONFIG_NET_NETCONSOLE is not set +# CONFIG_NET_RESOLV is not set # # Drivers @@ -227,12 +230,14 @@ CONFIG_DRIVER_NET_FEC_IMX=y # flash drivers # # CONFIG_DRIVER_CFI is not set +CONFIG_MTD=y CONFIG_NAND=y CONFIG_NAND_IMX=y CONFIG_NAND_IMX_BOOT=y # CONFIG_MTD_NAND_VERIFY_WRITE is not set # CONFIG_MTD_NAND_ECC_SMC is not set CONFIG_MTD_NAND_IDS=y +# CONFIG_UBI is not set # CONFIG_ATA is not set # CONFIG_USB is not set # CONFIG_USB_GADGET is not set diff --git a/arch/arm/configs/eukrea_cpuimx27_defconfig b/arch/arm/configs/eukrea_cpuimx27_defconfig index a1cf1adb74..c7f6b78db0 100644 --- a/arch/arm/configs/eukrea_cpuimx27_defconfig +++ b/arch/arm/configs/eukrea_cpuimx27_defconfig @@ -118,7 +118,7 @@ CONFIG_CONSOLE_ACTIVATE_ALL=y # CONFIG_OF_FLAT_TREE is not set CONFIG_PARTITION=y CONFIG_DEFAULT_ENVIRONMENT=y -CONFIG_DEFAULT_ENVIRONMENT_PATH="board/eukrea_cpuimx27/env" +CONFIG_DEFAULT_ENVIRONMENT_PATH="arch/arm/boards/eukrea_cpuimx27/env" # # Debugging diff --git a/arch/arm/configs/eukrea_cpuimx35_defconfig b/arch/arm/configs/eukrea_cpuimx35_defconfig index ff6547f1d4..7c5b49a8aa 100644 --- a/arch/arm/configs/eukrea_cpuimx35_defconfig +++ b/arch/arm/configs/eukrea_cpuimx35_defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit -# barebox version: 2010.06.0 -# Mon Jun 7 18:25:47 2010 +# barebox version: 2010.07.0 +# Wed Jul 28 21:46:15 2010 # # CONFIG_BOARD_LINKER_SCRIPT is not set CONFIG_GENERIC_LINKER_SCRIPT=y @@ -110,7 +110,8 @@ CONFIG_CONSOLE_FULL=y CONFIG_CONSOLE_ACTIVATE_FIRST=y # CONFIG_OF_FLAT_TREE is not set # CONFIG_PARTITION is not set -# CONFIG_DEFAULT_ENVIRONMENT is not set +CONFIG_DEFAULT_ENVIRONMENT=y +CONFIG_DEFAULT_ENVIRONMENT_PATH="arch/arm/boards/eukrea_cpuimx35/env" # # Debugging @@ -170,7 +171,8 @@ CONFIG_CMD_MTEST=y # # flash # -# CONFIG_CMD_FLASH is not set +CONFIG_CMD_FLASH=y +# CONFIG_CMD_UBI is not set # # booting @@ -195,10 +197,12 @@ CONFIG_CMD_GPIO=y CONFIG_CMD_UNLZO=y CONFIG_NET=y CONFIG_NET_DHCP=y -# CONFIG_NET_RARP is not set # CONFIG_NET_NFS is not set CONFIG_NET_PING=y CONFIG_NET_TFTP=y +# CONFIG_NET_TFTP_PUSH is not set +# CONFIG_NET_NETCONSOLE is not set +# CONFIG_NET_RESOLV is not set # # Drivers @@ -229,12 +233,14 @@ CONFIG_DRIVER_NET_FEC_IMX=y # flash drivers # # CONFIG_DRIVER_CFI is not set +CONFIG_MTD=y CONFIG_NAND=y CONFIG_NAND_IMX=y -# CONFIG_NAND_IMX_BOOT is not set +CONFIG_NAND_IMX_BOOT=y # CONFIG_MTD_NAND_VERIFY_WRITE is not set # CONFIG_MTD_NAND_ECC_SMC is not set CONFIG_MTD_NAND_IDS=y +# CONFIG_UBI is not set # CONFIG_ATA is not set # CONFIG_USB is not set # CONFIG_USB_GADGET is not set diff --git a/arch/arm/configs/freescale_mx25_3stack_defconfig b/arch/arm/configs/freescale_mx25_3stack_defconfig index d308e5bf01..fd7dd4273c 100644 --- a/arch/arm/configs/freescale_mx25_3stack_defconfig +++ b/arch/arm/configs/freescale_mx25_3stack_defconfig @@ -106,7 +106,7 @@ CONFIG_CONSOLE_ACTIVATE_FIRST=y # CONFIG_OF_FLAT_TREE is not set CONFIG_PARTITION=y CONFIG_DEFAULT_ENVIRONMENT=y -CONFIG_DEFAULT_ENVIRONMENT_PATH="board/freescale-mx25-3-stack/env/" +CONFIG_DEFAULT_ENVIRONMENT_PATH="arch/arm/boards/freescale-mx25-3-stack/env/" # # Debugging diff --git a/arch/arm/configs/freescale_mx35_3stack_defconfig b/arch/arm/configs/freescale_mx35_3stack_defconfig index 17a2fdc6d9..4321fbce09 100644 --- a/arch/arm/configs/freescale_mx35_3stack_defconfig +++ b/arch/arm/configs/freescale_mx35_3stack_defconfig @@ -105,7 +105,7 @@ CONFIG_CONSOLE_ACTIVATE_FIRST=y # CONFIG_OF_FLAT_TREE is not set CONFIG_PARTITION=y CONFIG_DEFAULT_ENVIRONMENT=y -CONFIG_DEFAULT_ENVIRONMENT_PATH="board/freescale-mx35-3-stack/env/" +CONFIG_DEFAULT_ENVIRONMENT_PATH="arch/arm/boards/freescale-mx35-3-stack/env/" # # Debugging diff --git a/arch/arm/configs/mmccpu_defconfig b/arch/arm/configs/mmccpu_defconfig index 2b80a30a59..a8c41e7878 100644 --- a/arch/arm/configs/mmccpu_defconfig +++ b/arch/arm/configs/mmccpu_defconfig @@ -93,7 +93,7 @@ CONFIG_CONSOLE_ACTIVATE_FIRST=y # CONFIG_OF_FLAT_TREE is not set CONFIG_PARTITION=y CONFIG_DEFAULT_ENVIRONMENT=y -CONFIG_DEFAULT_ENVIRONMENT_PATH="board/mmccpu/env" +CONFIG_DEFAULT_ENVIRONMENT_PATH="arch/arm/boards/mmccpu/env" # # Debugging diff --git a/arch/arm/configs/mx21ads_defconfig b/arch/arm/configs/mx21ads_defconfig index 99a87143ec..99b5ed6aa4 100644 --- a/arch/arm/configs/mx21ads_defconfig +++ b/arch/arm/configs/mx21ads_defconfig @@ -104,7 +104,7 @@ CONFIG_CONSOLE_ACTIVATE_FIRST=y # CONFIG_OF_FLAT_TREE is not set CONFIG_PARTITION=y CONFIG_DEFAULT_ENVIRONMENT=y -CONFIG_DEFAULT_ENVIRONMENT_PATH="board/imx21ads/env" +CONFIG_DEFAULT_ENVIRONMENT_PATH="arch/arm/boards/imx21ads/env" # # Debugging diff --git a/arch/arm/configs/mx27ads_defconfig b/arch/arm/configs/mx27ads_defconfig index 71880c0b76..a1bf3f9412 100644 --- a/arch/arm/configs/mx27ads_defconfig +++ b/arch/arm/configs/mx27ads_defconfig @@ -108,7 +108,7 @@ CONFIG_CONSOLE_ACTIVATE_FIRST=y # CONFIG_OF_FLAT_TREE is not set CONFIG_PARTITION=y CONFIG_DEFAULT_ENVIRONMENT=y -CONFIG_DEFAULT_ENVIRONMENT_PATH="board/imx27ads/env" +CONFIG_DEFAULT_ENVIRONMENT_PATH="arch/arm/boards/imx27ads/env" # # Debugging diff --git a/arch/arm/configs/neso_defconfig b/arch/arm/configs/neso_defconfig new file mode 100644 index 0000000000..3f2a978b95 --- /dev/null +++ b/arch/arm/configs/neso_defconfig @@ -0,0 +1,264 @@ +# +# Automatically generated make config: don't edit +# barebox version: 2010.06.0 +# Mon Jun 21 14:06:11 2010 +# +# CONFIG_BOARD_LINKER_SCRIPT is not set +CONFIG_GENERIC_LINKER_SCRIPT=y +CONFIG_ARM=y + +# +# System Type +# +# CONFIG_ARCH_AT91 is not set +# CONFIG_ARCH_AT91RM9200 is not set +# CONFIG_ARCH_EP93XX is not set +CONFIG_ARCH_IMX=y +# CONFIG_ARCH_NETX is not set +# CONFIG_ARCH_OMAP is not set +# CONFIG_ARCH_S3C24xx is not set + +# +# Processor Type +# +CONFIG_CPU_32=y +CONFIG_CPU_ARM926T=y +CONFIG_CPU_32v5=y + +# +# processor features +# +CONFIG_ARCH_TEXT_BASE=0xa7e00000 +CONFIG_BOARDINFO="Garz+Fricke Neso" +CONFIG_ARCH_HAS_FEC_IMX=y + +# +# Freescale i.MX System-on-Chip +# +# CONFIG_ARCH_IMX1 is not set +# CONFIG_ARCH_IMX21 is not set +# CONFIG_ARCH_IMX25 is not set +CONFIG_ARCH_IMX27=y +# CONFIG_ARCH_IMX31 is not set +# CONFIG_ARCH_IMX35 is not set +# CONFIG_MACH_EUKREA_CPUIMX27 is not set +# CONFIG_MACH_IMX27ADS is not set +# CONFIG_MACH_PCA100 is not set +# CONFIG_MACH_PCM038 is not set +CONFIG_MACH_NESO=y + +# +# Board specific settings +# + +# +# i.MX specific settings +# +CONFIG_IMX_CLKO=y +CONFIG_AEABI=y + +# +# Arm specific settings +# +CONFIG_CMD_ARM_CPUINFO=y +CONFIG_ARM_OPTIMZED_STRING_FUNCTIONS=y +CONFIG_GREGORIAN_CALENDER=y +CONFIG_HAS_KALLSYMS=y +CONFIG_HAS_MODULES=y +CONFIG_CMD_MEMORY=y +CONFIG_ENV_HANDLING=y +CONFIG_GENERIC_GPIO=y + +# +# General Settings +# +CONFIG_LOCALVERSION_AUTO=y + +# +# memory layout +# +CONFIG_HAVE_MMU=y +CONFIG_MMU=y +CONFIG_HAVE_CONFIGURABLE_TEXT_BASE=y +CONFIG_TEXT_BASE=0xa7e00000 +CONFIG_HAVE_CONFIGURABLE_MEMORY_LAYOUT=y +CONFIG_MEMORY_LAYOUT_DEFAULT=y +# CONFIG_MEMORY_LAYOUT_FIXED is not set +CONFIG_STACK_SIZE=0x8000 +CONFIG_MALLOC_SIZE=0x1000000 +# CONFIG_BROKEN is not set +# CONFIG_EXPERIMENTAL is not set +CONFIG_MACH_HAS_LOWLEVEL_INIT=y +CONFIG_MACH_DO_LOWLEVEL_INIT=y +CONFIG_PROMPT="barebox:" +CONFIG_BAUDRATE=115200 +CONFIG_LONGHELP=y +CONFIG_CBSIZE=1024 +CONFIG_MAXARGS=16 +CONFIG_SHELL_HUSH=y +# CONFIG_SHELL_SIMPLE is not set +CONFIG_GLOB=y +CONFIG_PROMPT_HUSH_PS2="> " +CONFIG_HUSH_FANCY_PROMPT=y +CONFIG_CMDLINE_EDITING=y +CONFIG_AUTO_COMPLETE=y +CONFIG_DYNAMIC_CRC_TABLE=y +CONFIG_ERRNO_MESSAGES=y +CONFIG_TIMESTAMP=y +CONFIG_CONSOLE_FULL=y +CONFIG_CONSOLE_ACTIVATE_FIRST=y +# CONFIG_OF_FLAT_TREE is not set +CONFIG_PARTITION=y +CONFIG_DEFAULT_ENVIRONMENT=y +CONFIG_DEFAULT_ENVIRONMENT_PATH="defaultenv board/guf-neso/env" + +# +# Debugging +# +# CONFIG_DEBUG_INFO is not set +# CONFIG_ENABLE_FLASH_NOISE is not set +# CONFIG_ENABLE_PARTITION_NOISE is not set +# CONFIG_ENABLE_DEVICE_NOISE is not set + +# +# Commands +# + +# +# scripting +# +CONFIG_CMD_EDIT=y +CONFIG_CMD_SLEEP=y +CONFIG_CMD_SAVEENV=y +CONFIG_CMD_LOADENV=y +CONFIG_CMD_EXPORT=y +CONFIG_CMD_PRINTENV=y +CONFIG_CMD_READLINE=y +CONFIG_CMD_TRUE=y +CONFIG_CMD_FALSE=y + +# +# file commands +# +CONFIG_CMD_LS=y +CONFIG_CMD_RM=y +CONFIG_CMD_CAT=y +CONFIG_CMD_MKDIR=y +CONFIG_CMD_RMDIR=y +CONFIG_CMD_CP=y +CONFIG_CMD_PWD=y +CONFIG_CMD_CD=y +CONFIG_CMD_MOUNT=y +CONFIG_CMD_UMOUNT=y + +# +# console +# +CONFIG_CMD_CLEAR=y +CONFIG_CMD_ECHO=y +CONFIG_CMD_ECHO_E=y + +# +# memory +# +# CONFIG_CMD_LOADB is not set +CONFIG_CMD_MEMINFO=y +CONFIG_CMD_CRC=y +CONFIG_CMD_MTEST=y +# CONFIG_CMD_MTEST_ALTERNATIVE is not set + +# +# flash +# +CONFIG_CMD_FLASH=y + +# +# booting +# +CONFIG_CMD_BOOTM=y +# CONFIG_CMD_BOOTM_ZLIB is not set +# CONFIG_CMD_BOOTM_BZLIB is not set +# CONFIG_CMD_BOOTM_SHOW_TYPE is not set +CONFIG_CMD_BOOTZ=y +CONFIG_CMD_BOOTU=y +# CONFIG_CMD_LINUX16 is not set +CONFIG_CMD_RESET=y +CONFIG_CMD_GO=y +CONFIG_CMD_TIMEOUT=y +CONFIG_CMD_PARTITION=y +CONFIG_CMD_TEST=y +CONFIG_CMD_VERSION=y +CONFIG_CMD_HELP=y +CONFIG_CMD_DEVINFO=y +CONFIG_CMD_BMP=y +CONFIG_CMD_GPIO=y +CONFIG_CMD_UNLZO=y +CONFIG_NET=y +CONFIG_NET_DHCP=y +CONFIG_NET_NFS=y +CONFIG_NET_PING=y +CONFIG_NET_TFTP=y +CONFIG_NET_TFTP_PUSH=y +CONFIG_NET_NETCONSOLE=y +CONFIG_NET_RESOLV=y + +# +# Drivers +# + +# +# serial drivers +# +# CONFIG_DRIVER_SERIAL_ARM_DCC is not set +CONFIG_DRIVER_SERIAL_IMX=y +# CONFIG_DRIVER_SERIAL_NS16550 is not set +CONFIG_MIIPHY=y + +# +# Network drivers +# +# CONFIG_DRIVER_NET_SMC911X is not set +# CONFIG_DRIVER_NET_SMC91111 is not set +CONFIG_DRIVER_NET_FEC_IMX=y +CONFIG_NET_USB=y +CONFIG_NET_USB_ASIX=y +# CONFIG_NET_USB_SMSC95XX is not set + +# +# SPI drivers +# +CONFIG_SPI=y +CONFIG_DRIVER_SPI_IMX=y +CONFIG_DRIVER_SPI_MC13783=y +# CONFIG_I2C is not set + +# +# flash drivers +# +# CONFIG_DRIVER_CFI is not set +CONFIG_NAND=y +CONFIG_NAND_IMX=y +CONFIG_NAND_IMX_BOOT=y +# CONFIG_MTD_NAND_VERIFY_WRITE is not set +# CONFIG_MTD_NAND_ECC_SMC is not set +CONFIG_MTD_NAND_IDS=y +# CONFIG_ATA is not set +CONFIG_USB=y +CONFIG_USB_EHCI=y +CONFIG_USB_ULPI=y +CONFIG_USB_ISP1504=y +# CONFIG_USB_GADGET is not set +CONFIG_VIDEO=y +CONFIG_DRIVER_VIDEO_IMX=y +CONFIG_IMXFB_DRIVER_VIDEO_IMX_OVERLAY=y + +# +# Filesystem support +# +# CONFIG_FS_CRAMFS is not set +CONFIG_FS_RAMFS=y +CONFIG_FS_DEVFS=y +CONFIG_CRC32=y +# CONFIG_GENERIC_FIND_NEXT_BIT is not set +CONFIG_PROCESS_ESCAPE_SEQUENCE=y +CONFIG_LZO_DECOMPRESS=y diff --git a/arch/arm/configs/pca100_defconfig b/arch/arm/configs/pca100_defconfig index 68f37d0473..accc6ef8b5 100644 --- a/arch/arm/configs/pca100_defconfig +++ b/arch/arm/configs/pca100_defconfig @@ -109,7 +109,7 @@ CONFIG_CONSOLE_ACTIVATE_FIRST=y # CONFIG_OF_FLAT_TREE is not set CONFIG_PARTITION=y CONFIG_DEFAULT_ENVIRONMENT=y -CONFIG_DEFAULT_ENVIRONMENT_PATH="defaultenv board/phycard-i.MX27/env" +CONFIG_DEFAULT_ENVIRONMENT_PATH="defaultenv arch/arm/boards/phycard-i.MX27/env" # # Debugging diff --git a/arch/arm/configs/pcm037_defconfig b/arch/arm/configs/pcm037_defconfig index 5a4ac70751..bb67995f03 100644 --- a/arch/arm/configs/pcm037_defconfig +++ b/arch/arm/configs/pcm037_defconfig @@ -111,7 +111,7 @@ CONFIG_CONSOLE_ACTIVATE_FIRST=y # CONFIG_OF_FLAT_TREE is not set CONFIG_PARTITION=y CONFIG_DEFAULT_ENVIRONMENT=y -CONFIG_DEFAULT_ENVIRONMENT_PATH="defaultenv board/pcm037/env" +CONFIG_DEFAULT_ENVIRONMENT_PATH="defaultenv arch/arm/boards/pcm037/env" # # Debugging diff --git a/arch/arm/configs/pcm038_defconfig b/arch/arm/configs/pcm038_defconfig index a449a991d0..2925c9c3dc 100644 --- a/arch/arm/configs/pcm038_defconfig +++ b/arch/arm/configs/pcm038_defconfig @@ -109,7 +109,7 @@ CONFIG_CONSOLE_ACTIVATE_FIRST=y # CONFIG_OF_FLAT_TREE is not set CONFIG_PARTITION=y CONFIG_DEFAULT_ENVIRONMENT=y -CONFIG_DEFAULT_ENVIRONMENT_PATH="defaultenv board/pcm038/env" +CONFIG_DEFAULT_ENVIRONMENT_PATH="defaultenv arch/arm/boards/pcm038/env" # # Debugging diff --git a/arch/arm/configs/pcm043_defconfig b/arch/arm/configs/pcm043_defconfig index e5c2d40b1a..31c65f18c4 100644 --- a/arch/arm/configs/pcm043_defconfig +++ b/arch/arm/configs/pcm043_defconfig @@ -113,7 +113,7 @@ CONFIG_CONSOLE_ACTIVATE_FIRST=y # CONFIG_OF_FLAT_TREE is not set CONFIG_PARTITION=y CONFIG_DEFAULT_ENVIRONMENT=y -CONFIG_DEFAULT_ENVIRONMENT_PATH="defaultenv board/pcm043/env" +CONFIG_DEFAULT_ENVIRONMENT_PATH="defaultenv arch/arm/boards/pcm043/env" # # Debugging diff --git a/arch/arm/configs/pm9263_defconfig b/arch/arm/configs/pm9263_defconfig index cde5cbe66e..d5ee46a6f2 100644 --- a/arch/arm/configs/pm9263_defconfig +++ b/arch/arm/configs/pm9263_defconfig @@ -93,7 +93,7 @@ CONFIG_CONSOLE_ACTIVATE_FIRST=y # CONFIG_OF_FLAT_TREE is not set CONFIG_PARTITION=y CONFIG_DEFAULT_ENVIRONMENT=y -CONFIG_DEFAULT_ENVIRONMENT_PATH="board/pm9263/env/" +CONFIG_DEFAULT_ENVIRONMENT_PATH="arch/arm/boards/pm9263/env/" # # Debugging diff --git a/arch/arm/configs/scb9328_defconfig b/arch/arm/configs/scb9328_defconfig index 6638234be3..7dc56dd855 100644 --- a/arch/arm/configs/scb9328_defconfig +++ b/arch/arm/configs/scb9328_defconfig @@ -105,7 +105,7 @@ CONFIG_CONSOLE_ACTIVATE_FIRST=y # CONFIG_OF_FLAT_TREE is not set # CONFIG_PARTITION is not set CONFIG_DEFAULT_ENVIRONMENT=y -CONFIG_DEFAULT_ENVIRONMENT_PATH="board/scb9328/env" +CONFIG_DEFAULT_ENVIRONMENT_PATH="arch/arm/boards/scb9328/env" # # Debugging diff --git a/arch/arm/include/asm/barebox-arm.h b/arch/arm/include/asm/barebox-arm.h index 5f0bb73413..7bb1af1606 100644 --- a/arch/arm/include/asm/barebox-arm.h +++ b/arch/arm/include/asm/barebox-arm.h @@ -32,7 +32,7 @@ /* cpu/.../cpu.c */ int cleanup_before_linux(void); -/* board/.../... */ +/* arch/<arch>board(s)/.../... */ int board_init(void); int dram_init (void); diff --git a/arch/arm/mach-imx/Kconfig b/arch/arm/mach-imx/Kconfig index 0cf6334f01..afee3d1342 100644 --- a/arch/arm/mach-imx/Kconfig +++ b/arch/arm/mach-imx/Kconfig @@ -16,6 +16,7 @@ config ARCH_TEXT_BASE default 0x87f00000 if MACH_PCM037 default 0x87f00000 if MACH_PCM043 default 0x08f80000 if MACH_SCB9328 + default 0xa7e00000 if MACH_NESO config BOARDINFO default "Eukrea CPUIMX25" if MACH_EUKREA_CPUIMX25 @@ -30,6 +31,7 @@ config BOARDINFO default "Phytec phyCORE-i.MX31" if MACH_PCM037 default "Phytec phyCORE-i.MX35" if MACH_PCM043 default "Synertronixx scb9328" if MACH_SCB9328 + default "Garz+Fricke Neso" if MACH_NESO config ARCH_HAS_FEC_IMX bool @@ -195,6 +197,14 @@ config MACH_PCM038 Say Y here if you are using Phytec's phyCORE-i.MX27 (pcm038) equipped with a Freescale i.MX27 Processor +config MACH_NESO + bool "Garz+Fricke Neso" + select MACH_HAS_LOWLEVEL_INIT + select HAVE_MMU + help + Say Y here if you are using the Garz+Fricke Neso board equipped + with a Freescale i.MX27 Processor + endchoice endif diff --git a/arch/arm/mach-omap/Kconfig b/arch/arm/mach-omap/Kconfig index 99b9f9d973..158639ec59 100644 --- a/arch/arm/mach-omap/Kconfig +++ b/arch/arm/mach-omap/Kconfig @@ -39,20 +39,6 @@ config ARCH_OMAP3 endchoice -config OMAP_CONFIG_STACKSIZE - prompt "STACKSIZE" - hex - default 0x00020000 - help - Select the Stack Size when defaults are used - -config OMAP_MALLOC_LEN - prompt "MALLOC LENGTH" - hex - default 0x00001000 - help - Select the Malloc Length when defaults are used - ### Generic Clock configurations to be enabled by Mach - invisible to enable. config OMAP_CLOCK_UART bool @@ -97,6 +83,6 @@ config GPMC NAND, OneNAND etc. # Get the board specific configurations -source board/omap/Kconfig +source arch/arm/boards/omap/Kconfig endmenu diff --git a/arch/arm/mach-omap/arch-omap.dox b/arch/arm/mach-omap/arch-omap.dox index 01e45f291e..df16b7be96 100644 --- a/arch/arm/mach-omap/arch-omap.dox +++ b/arch/arm/mach-omap/arch-omap.dox @@ -39,7 +39,7 @@ Motivation for code organization is driven from: Code is Organized into three main directories: @li arch/arm/mach-omap -contains files for ALL peripherals which are present on board with very few exceptions. We will come to these exceptions in later sections. @li include/asm-arm/arch-omap - contains files for ALL OMAP on-silicon peripherals. No Board specific files here please! -@li board/omap - contains files for ALL boards using OMAP processors. +@li arch/arm/boards/omap - contains files for ALL boards using OMAP processors. @section mach_omap arch/arm/mach-omap directory guidelines It is rather simple: All common peripherals should be isolated as separate driver libraries as far as possible. Exceptions such as clock configuration code may be isolated by the following naming convention: omapX_function_name.[cS], where X belongs to the OMAP variant. The exception is for devices who have existing code locations - potentially drivers/i2c/busses and the like. @@ -52,7 +52,7 @@ All OMAP common headers are located here. Where we have to incorporate a OMAP va include/asm-arm/arch-omap/silicon.h contains includes for omapX-silicon.h which defines the base addresses for the peripherals on that platform. the usual convention is to use #define OMAP_SOMETHING_BASE to allow re-use. -@section board_omap board/omap directory guidelines +@section board_omap arch/arm/boards/omap directory guidelines All Board specific files go here. In u-boot, we always had to use common config file which is shared by other drivers to get serial, ethernet baseaddress etc.. we can easily use the device_d structure to handle it with @a barebox. This is more like programming for Linux kernel - it is pretty easy. Each specific board file has board-XYZ.c and potentially and equivalent h file. @@ -66,7 +66,7 @@ The responsibility of arch_init_lowlevel and related calls is to setup OMAP. No Once this is past, the code returns back to arm common code (cpu/start-arm.S). Here Instruction and Data caches are disabled. The execution proceeds to normal board initialization. @section board_boot The board boot path -If the proper CONFIG_MACH_DO_LOWLEVEL_INIT flag is setup, board_init_lowlevel is called. This again would call a common file board/omap/platform.S which setups a temporary SRAM stack and bumps the control to board_init. +If the proper CONFIG_MACH_DO_LOWLEVEL_INIT flag is setup, board_init_lowlevel is called. This again would call a common file arch/arm/boards/omap/platform.S which setups a temporary SRAM stack and bumps the control to board_init. Every Board in OMAP platform can potentially define a board_init and enable defconfig in arch/arm/configs directory. The responsibility here is to setup OMAP for board configurations - this includes SDRAM configuration and pin muxing configuration. Once this is complete, @a barebox boot process proceeds by calling init functions and finally entering shell prompt @@ -87,7 +87,7 @@ static int my_board_devices_init(void) { device_initcall(my_board_devices_init); @endcode -You may probably be interested in calling console_initcall to get a console.. Modify board/omap/Kconfig to add your OMAP board, create a defconfig, do a make C=2 to enable sparse warnings, you can potentially have a binary done in no time! if you remember to put doxygen comments in your code, you can do a make docs and get the documentation done too.. +You may probably be interested in calling console_initcall to get a console.. Modify arch/arm/boards/omap/Kconfig to add your OMAP board, create a defconfig, do a make C=2 to enable sparse warnings, you can potentially have a binary done in no time! if you remember to put doxygen comments in your code, you can do a make docs and get the documentation done too.. */ diff --git a/arch/arm/mach-omap/include/mach/gpmc_nand.h b/arch/arm/mach-omap/include/mach/gpmc_nand.h index c6c51d5d52..e23faabcac 100644 --- a/arch/arm/mach-omap/include/mach/gpmc_nand.h +++ b/arch/arm/mach-omap/include/mach/gpmc_nand.h @@ -68,54 +68,11 @@ struct gpmc_nand_platform_data { #define NAND_WAITPOL_HIGH (1 << 0) #define NAND_WAITPOL_MASK (1 << 0) -#ifdef CONFIG_NAND_OMAP_GPMC_HWECC /** plat_options: hw ecc enabled */ #define NAND_HWECC_ENABLE (1 << 1) -#endif /** plat_options: hw ecc disabled */ -#define NAND_HWECC_DISABLE (0 << 1) #define NAND_HWECC_MASK (1 << 1) -/* Typical BOOTROM oob layouts-requires hwecc **/ -#ifdef CONFIG_NAND_OMAP_GPMC_HWECC -/** Large Page x8 NAND device Layout */ -#define GPMC_NAND_ECC_LP_x8_LAYOUT {\ - .eccbytes = 12,\ - .eccpos = {1, 2, 3, 4, 5, 6, 7, 8,\ - 9, 10, 11, 12},\ - .oobfree = {\ - {.offset = 60,\ - .length = 2 } } \ -} - -/** Large Page x16 NAND device Layout */ -#define GPMC_NAND_ECC_LP_x16_LAYOUT {\ - .eccbytes = 12,\ - .eccpos = {2, 3, 4, 5, 6, 7, 8, 9,\ - 10, 11, 12, 13},\ - .oobfree = {\ - {.offset = 60,\ - .length = 2 } } \ -} - -/** Small Page x8 NAND device Layout */ -#define GPMC_NAND_ECC_SP_x8_LAYOUT {\ - .eccbytes = 3,\ - .eccpos = {1, 2, 3},\ - .oobfree = {\ - {.offset = 14,\ - .length = 2 } } \ -} - -/** Small Page x16 NAND device Layout */ -#define GPMC_NAND_ECC_SP_x16_LAYOUT {\ - .eccbytes = 3,\ - .eccpos = {2, 3, 4},\ - .oobfree = {\ - {.offset = 14,\ - .length = 2 } } \ -} - -#endif /* CONFIG_NAND_OMAP_GPMC_HWECC */ +int gpmc_generic_nand_devices_init(int cs, int width, int hwecc); #endif /* __ASM_OMAP_NAND_GPMC_H */ diff --git a/arch/arm/mach-s3c24xx/generic.c b/arch/arm/mach-s3c24xx/generic.c index 372904f89d..46b5c50d5b 100644 --- a/arch/arm/mach-s3c24xx/generic.c +++ b/arch/arm/mach-s3c24xx/generic.c @@ -244,8 +244,8 @@ EXPORT_SYMBOL(reset_cpu); @section s3c24xx_boards Boards using S3C24xx Processors -@li @subpage board/a9m2410/a9m2410.c -@li @subpage board/a9m2440/a9m2440.c +@li @subpage arch/arm/boards/a9m2410/a9m2410.c +@li @subpage arch/arm/boards/a9m2440/a9m2440.c @section s3c24xx_arch Documentation for S3C24xx Architectures Files diff --git a/arch/blackfin/Makefile b/arch/blackfin/Makefile index dbb90814f9..902268da70 100644 --- a/arch/blackfin/Makefile +++ b/arch/blackfin/Makefile @@ -22,7 +22,7 @@ PHONY += maketools ifneq ($(board-y),) -BOARD := board/$(board-y)/ +BOARD := arch/blackfin/boards/$(board-y)/ else BOARD := endif diff --git a/board/ipe337/Makefile b/arch/blackfin/boards/ipe337/Makefile index 172dfb688c..172dfb688c 100644 --- a/board/ipe337/Makefile +++ b/arch/blackfin/boards/ipe337/Makefile diff --git a/board/ipe337/barebox.lds.S b/arch/blackfin/boards/ipe337/barebox.lds.S index 4299b8208b..4299b8208b 100644 --- a/board/ipe337/barebox.lds.S +++ b/arch/blackfin/boards/ipe337/barebox.lds.S diff --git a/board/ipe337/cmd_alternate.c b/arch/blackfin/boards/ipe337/cmd_alternate.c index 2883c77d32..2883c77d32 100644 --- a/board/ipe337/cmd_alternate.c +++ b/arch/blackfin/boards/ipe337/cmd_alternate.c diff --git a/board/ipe337/config.h b/arch/blackfin/boards/ipe337/config.h index aa25d0792d..aa25d0792d 100644 --- a/board/ipe337/config.h +++ b/arch/blackfin/boards/ipe337/config.h diff --git a/board/ipe337/env/bin/_alternate b/arch/blackfin/boards/ipe337/env/bin/_alternate index 10ae2134c0..10ae2134c0 100644 --- a/board/ipe337/env/bin/_alternate +++ b/arch/blackfin/boards/ipe337/env/bin/_alternate diff --git a/board/ipe337/env/bin/_update b/arch/blackfin/boards/ipe337/env/bin/_update index 5419ece6a7..5419ece6a7 100644 --- a/board/ipe337/env/bin/_update +++ b/arch/blackfin/boards/ipe337/env/bin/_update diff --git a/board/ipe337/env/bin/boot b/arch/blackfin/boards/ipe337/env/bin/boot index 62807d211f..62807d211f 100644 --- a/board/ipe337/env/bin/boot +++ b/arch/blackfin/boards/ipe337/env/bin/boot diff --git a/board/ipe337/env/bin/init b/arch/blackfin/boards/ipe337/env/bin/init index e864dc5a42..e864dc5a42 100644 --- a/board/ipe337/env/bin/init +++ b/arch/blackfin/boards/ipe337/env/bin/init diff --git a/board/ipe337/env/bin/magic.bin b/arch/blackfin/boards/ipe337/env/bin/magic.bin index f8bff393cf..f8bff393cf 100644 --- a/board/ipe337/env/bin/magic.bin +++ b/arch/blackfin/boards/ipe337/env/bin/magic.bin diff --git a/board/ipe337/env/bin/reset_ageing b/arch/blackfin/boards/ipe337/env/bin/reset_ageing index 2c95ae762e..2c95ae762e 100644 --- a/board/ipe337/env/bin/reset_ageing +++ b/arch/blackfin/boards/ipe337/env/bin/reset_ageing diff --git a/board/ipe337/env/bin/update_application b/arch/blackfin/boards/ipe337/env/bin/update_application index 46ad210e36..46ad210e36 100644 --- a/board/ipe337/env/bin/update_application +++ b/arch/blackfin/boards/ipe337/env/bin/update_application diff --git a/board/ipe337/env/bin/update_bareboxenv b/arch/blackfin/boards/ipe337/env/bin/update_bareboxenv index b0a32c626b..b0a32c626b 100644 --- a/board/ipe337/env/bin/update_bareboxenv +++ b/arch/blackfin/boards/ipe337/env/bin/update_bareboxenv diff --git a/board/ipe337/env/bin/update_kernel b/arch/blackfin/boards/ipe337/env/bin/update_kernel index d5c210eb2b..d5c210eb2b 100644 --- a/board/ipe337/env/bin/update_kernel +++ b/arch/blackfin/boards/ipe337/env/bin/update_kernel diff --git a/board/ipe337/env/bin/update_persistent b/arch/blackfin/boards/ipe337/env/bin/update_persistent index a869b2218d..a869b2218d 100644 --- a/board/ipe337/env/bin/update_persistent +++ b/arch/blackfin/boards/ipe337/env/bin/update_persistent diff --git a/board/ipe337/env/bin/update_system b/arch/blackfin/boards/ipe337/env/bin/update_system index 598fc10e81..598fc10e81 100644 --- a/board/ipe337/env/bin/update_system +++ b/arch/blackfin/boards/ipe337/env/bin/update_system diff --git a/board/ipe337/env/config b/arch/blackfin/boards/ipe337/env/config index 7c5ee76e30..7c5ee76e30 100644 --- a/board/ipe337/env/config +++ b/arch/blackfin/boards/ipe337/env/config diff --git a/board/ipe337/ipe337.c b/arch/blackfin/boards/ipe337/ipe337.c index 269e7743fc..269e7743fc 100644 --- a/board/ipe337/ipe337.c +++ b/arch/blackfin/boards/ipe337/ipe337.c diff --git a/board/ipe337/ipe337.dox b/arch/blackfin/boards/ipe337/ipe337.dox index 4d7925af94..4d7925af94 100644 --- a/board/ipe337/ipe337.dox +++ b/arch/blackfin/boards/ipe337/ipe337.dox diff --git a/arch/blackfin/configs/ipe337_defconfig b/arch/blackfin/configs/ipe337_defconfig index fd4ff66456..33fd2fcde8 100644 --- a/arch/blackfin/configs/ipe337_defconfig +++ b/arch/blackfin/configs/ipe337_defconfig @@ -55,7 +55,7 @@ CONFIG_CONSOLE_ACTIVATE_FIRST=y # CONFIG_OF_FLAT_TREE is not set CONFIG_PARTITION=y CONFIG_DEFAULT_ENVIRONMENT=y -CONFIG_DEFAULT_ENVIRONMENT_PATH="board/ipe337/env" +CONFIG_DEFAULT_ENVIRONMENT_PATH="arch/blackin/boards/ipe337/env" # # Debugging diff --git a/arch/m68k/Makefile b/arch/m68k/Makefile index f377325add..ec70028a16 100644 --- a/arch/m68k/Makefile +++ b/arch/m68k/Makefile @@ -63,7 +63,7 @@ PHONY += maketools ifneq ($(board-y),) -BOARD := board/$(board-y)/ +BOARD := arch/m68k/boards/$(board-y)/ else BOARD := endif diff --git a/board/kp_ukd_r1_num/Makefile b/arch/m68k/boards/kp_ukd_r1_num/Makefile index 65f2a02fca..65f2a02fca 100644 --- a/board/kp_ukd_r1_num/Makefile +++ b/arch/m68k/boards/kp_ukd_r1_num/Makefile diff --git a/board/phycore_mcf54xx/env/bin/_update b/arch/m68k/boards/kp_ukd_r1_num/env/bin/_update index 014bce3512..014bce3512 100644 --- a/board/phycore_mcf54xx/env/bin/_update +++ b/arch/m68k/boards/kp_ukd_r1_num/env/bin/_update diff --git a/board/kp_ukd_r1_num/env/bin/boot b/arch/m68k/boards/kp_ukd_r1_num/env/bin/boot index c9fcbac620..c9fcbac620 100644 --- a/board/kp_ukd_r1_num/env/bin/boot +++ b/arch/m68k/boards/kp_ukd_r1_num/env/bin/boot diff --git a/board/kp_ukd_r1_num/env/bin/init b/arch/m68k/boards/kp_ukd_r1_num/env/bin/init index 48e2139f7d..48e2139f7d 100644 --- a/board/kp_ukd_r1_num/env/bin/init +++ b/arch/m68k/boards/kp_ukd_r1_num/env/bin/init diff --git a/board/kp_ukd_r1_num/env/bin/pcidmaloop b/arch/m68k/boards/kp_ukd_r1_num/env/bin/pcidmaloop index 24e76cbed7..24e76cbed7 100644 --- a/board/kp_ukd_r1_num/env/bin/pcidmaloop +++ b/arch/m68k/boards/kp_ukd_r1_num/env/bin/pcidmaloop diff --git a/board/kp_ukd_r1_num/env/bin/pciloop b/arch/m68k/boards/kp_ukd_r1_num/env/bin/pciloop index 4a804f9f31..4a804f9f31 100644 --- a/board/kp_ukd_r1_num/env/bin/pciloop +++ b/arch/m68k/boards/kp_ukd_r1_num/env/bin/pciloop diff --git a/board/kp_ukd_r1_num/env/bin/update_kernel b/arch/m68k/boards/kp_ukd_r1_num/env/bin/update_kernel index 1ad95fc5d6..1ad95fc5d6 100644 --- a/board/kp_ukd_r1_num/env/bin/update_kernel +++ b/arch/m68k/boards/kp_ukd_r1_num/env/bin/update_kernel diff --git a/board/kp_ukd_r1_num/env/bin/update_root b/arch/m68k/boards/kp_ukd_r1_num/env/bin/update_root index b757a5b922..b757a5b922 100644 --- a/board/kp_ukd_r1_num/env/bin/update_root +++ b/arch/m68k/boards/kp_ukd_r1_num/env/bin/update_root diff --git a/board/kp_ukd_r1_num/env/config b/arch/m68k/boards/kp_ukd_r1_num/env/config index 14958ba1df..14958ba1df 100644 --- a/board/kp_ukd_r1_num/env/config +++ b/arch/m68k/boards/kp_ukd_r1_num/env/config diff --git a/board/kp_ukd_r1_num/highlevel_init.c b/arch/m68k/boards/kp_ukd_r1_num/highlevel_init.c index 3a88cd68c7..3a88cd68c7 100644 --- a/board/kp_ukd_r1_num/highlevel_init.c +++ b/arch/m68k/boards/kp_ukd_r1_num/highlevel_init.c diff --git a/board/kp_ukd_r1_num/kp_ukd_r1_num.c b/arch/m68k/boards/kp_ukd_r1_num/kp_ukd_r1_num.c index 9bf1713cc4..9bf1713cc4 100644 --- a/board/kp_ukd_r1_num/kp_ukd_r1_num.c +++ b/arch/m68k/boards/kp_ukd_r1_num/kp_ukd_r1_num.c diff --git a/board/kp_ukd_r1_num/kp_ukd_r1_num.dox b/arch/m68k/boards/kp_ukd_r1_num/kp_ukd_r1_num.dox index ca0fcbcf39..ca0fcbcf39 100644 --- a/board/kp_ukd_r1_num/kp_ukd_r1_num.dox +++ b/arch/m68k/boards/kp_ukd_r1_num/kp_ukd_r1_num.dox diff --git a/board/kp_ukd_r1_num/lowlevel_init.c b/arch/m68k/boards/kp_ukd_r1_num/lowlevel_init.c index b3de505c24..b3de505c24 100644 --- a/board/kp_ukd_r1_num/lowlevel_init.c +++ b/arch/m68k/boards/kp_ukd_r1_num/lowlevel_init.c diff --git a/board/kp_ukd_r1_num/pci-stubs.c b/arch/m68k/boards/kp_ukd_r1_num/pci-stubs.c index b7ab7c7d72..b7ab7c7d72 100644 --- a/board/kp_ukd_r1_num/pci-stubs.c +++ b/arch/m68k/boards/kp_ukd_r1_num/pci-stubs.c diff --git a/board/phycore_mcf54xx/Makefile b/arch/m68k/boards/phycore_mcf54xx/Makefile index 054123fe75..054123fe75 100644 --- a/board/phycore_mcf54xx/Makefile +++ b/arch/m68k/boards/phycore_mcf54xx/Makefile diff --git a/board/pm9263/env/bin/_update b/arch/m68k/boards/phycore_mcf54xx/env/bin/_update index 014bce3512..014bce3512 100644 --- a/board/pm9263/env/bin/_update +++ b/arch/m68k/boards/phycore_mcf54xx/env/bin/_update diff --git a/board/phycore_mcf54xx/env/bin/boot b/arch/m68k/boards/phycore_mcf54xx/env/bin/boot index c9fcbac620..c9fcbac620 100644 --- a/board/phycore_mcf54xx/env/bin/boot +++ b/arch/m68k/boards/phycore_mcf54xx/env/bin/boot diff --git a/board/phycore_mcf54xx/env/bin/init b/arch/m68k/boards/phycore_mcf54xx/env/bin/init index 48e2139f7d..48e2139f7d 100644 --- a/board/phycore_mcf54xx/env/bin/init +++ b/arch/m68k/boards/phycore_mcf54xx/env/bin/init diff --git a/board/phycore_mcf54xx/env/bin/pcidmaloop b/arch/m68k/boards/phycore_mcf54xx/env/bin/pcidmaloop index 24e76cbed7..24e76cbed7 100644 --- a/board/phycore_mcf54xx/env/bin/pcidmaloop +++ b/arch/m68k/boards/phycore_mcf54xx/env/bin/pcidmaloop diff --git a/board/phycore_mcf54xx/env/bin/pciloop b/arch/m68k/boards/phycore_mcf54xx/env/bin/pciloop index 4a804f9f31..4a804f9f31 100644 --- a/board/phycore_mcf54xx/env/bin/pciloop +++ b/arch/m68k/boards/phycore_mcf54xx/env/bin/pciloop diff --git a/board/phycore_mcf54xx/env/bin/update_kernel b/arch/m68k/boards/phycore_mcf54xx/env/bin/update_kernel index 1ad95fc5d6..1ad95fc5d6 100644 --- a/board/phycore_mcf54xx/env/bin/update_kernel +++ b/arch/m68k/boards/phycore_mcf54xx/env/bin/update_kernel diff --git a/board/phycore_mcf54xx/env/bin/update_root b/arch/m68k/boards/phycore_mcf54xx/env/bin/update_root index b757a5b922..b757a5b922 100644 --- a/board/phycore_mcf54xx/env/bin/update_root +++ b/arch/m68k/boards/phycore_mcf54xx/env/bin/update_root diff --git a/board/phycore_mcf54xx/env/config b/arch/m68k/boards/phycore_mcf54xx/env/config index 58550625d2..58550625d2 100644 --- a/board/phycore_mcf54xx/env/config +++ b/arch/m68k/boards/phycore_mcf54xx/env/config diff --git a/board/phycore_mcf54xx/highlevel_init.c b/arch/m68k/boards/phycore_mcf54xx/highlevel_init.c index 3a88cd68c7..3a88cd68c7 100644 --- a/board/phycore_mcf54xx/highlevel_init.c +++ b/arch/m68k/boards/phycore_mcf54xx/highlevel_init.c diff --git a/board/phycore_mcf54xx/lowlevel_init.c b/arch/m68k/boards/phycore_mcf54xx/lowlevel_init.c index 2837e3ed67..2837e3ed67 100644 --- a/board/phycore_mcf54xx/lowlevel_init.c +++ b/arch/m68k/boards/phycore_mcf54xx/lowlevel_init.c diff --git a/board/phycore_mcf54xx/pci-stubs.c b/arch/m68k/boards/phycore_mcf54xx/pci-stubs.c index b7ab7c7d72..b7ab7c7d72 100644 --- a/board/phycore_mcf54xx/pci-stubs.c +++ b/arch/m68k/boards/phycore_mcf54xx/pci-stubs.c diff --git a/board/phycore_mcf54xx/phyCore_MCF54xx.c b/arch/m68k/boards/phycore_mcf54xx/phyCore_MCF54xx.c index 3bc2d12a42..3bc2d12a42 100644 --- a/board/phycore_mcf54xx/phyCore_MCF54xx.c +++ b/arch/m68k/boards/phycore_mcf54xx/phyCore_MCF54xx.c diff --git a/board/phycore_mcf54xx/phyCore_MCF54xx.dox b/arch/m68k/boards/phycore_mcf54xx/phyCore_MCF54xx.dox index 36dd0ad195..36dd0ad195 100644 --- a/board/phycore_mcf54xx/phyCore_MCF54xx.dox +++ b/arch/m68k/boards/phycore_mcf54xx/phyCore_MCF54xx.dox diff --git a/arch/m68k/configs/phycore_kpukdr1_5475num_defconfig b/arch/m68k/configs/phycore_kpukdr1_5475num_defconfig index ba21a008d3..bb91152210 100644 --- a/arch/m68k/configs/phycore_kpukdr1_5475num_defconfig +++ b/arch/m68k/configs/phycore_kpukdr1_5475num_defconfig @@ -71,7 +71,7 @@ CONFIG_EARLY_CONSOLE_PORT="psc0" CONFIG_EARLY_CONSOLE_BAUDRATE=115200 # CONFIG_OF_FLAT_TREE is not set CONFIG_DEFAULT_ENVIRONMENT=y -CONFIG_DEFAULT_ENVIRONMENT_PATH="board/kp_ukd_r1_num/env/" +CONFIG_DEFAULT_ENVIRONMENT_PATH="arch/m68k/boards/kp_ukd_r1_num/env/" # # Debugging diff --git a/arch/m68k/configs/phycore_mcf54xx_defconfig b/arch/m68k/configs/phycore_mcf54xx_defconfig index 34ca73ffc7..f64ca8b022 100644 --- a/arch/m68k/configs/phycore_mcf54xx_defconfig +++ b/arch/m68k/configs/phycore_mcf54xx_defconfig @@ -71,7 +71,7 @@ CONFIG_EARLY_CONSOLE_PORT="psc0" CONFIG_EARLY_CONSOLE_BAUDRATE=115200 # CONFIG_OF_FLAT_TREE is not set CONFIG_DEFAULT_ENVIRONMENT=y -CONFIG_DEFAULT_ENVIRONMENT_PATH="board/phycore_mcf54xx/env" +CONFIG_DEFAULT_ENVIRONMENT_PATH="arch/m68k/boards/phycore_mcf54xx/env" # # Debugging diff --git a/arch/ppc/Makefile b/arch/ppc/Makefile index c24d3c3685..46d64e5bc6 100644 --- a/arch/ppc/Makefile +++ b/arch/ppc/Makefile @@ -35,7 +35,7 @@ PHONY += maketools ifneq ($(board-y),) -BOARD := board/$(board-y)/ +BOARD := arch/ppc/boards/$(board-y)/ else BOARD := endif diff --git a/board/pcm030/Makefile b/arch/ppc/boards/pcm030/Makefile index e7d744bfb4..e7d744bfb4 100644 --- a/board/pcm030/Makefile +++ b/arch/ppc/boards/pcm030/Makefile diff --git a/board/pcm030/barebox.lds.S b/arch/ppc/boards/pcm030/barebox.lds.S index ab99335675..ab99335675 100644 --- a/board/pcm030/barebox.lds.S +++ b/arch/ppc/boards/pcm030/barebox.lds.S diff --git a/board/pcm030/config.h b/arch/ppc/boards/pcm030/config.h index a772ee6d96..a772ee6d96 100644 --- a/board/pcm030/config.h +++ b/arch/ppc/boards/pcm030/config.h diff --git a/board/pcm030/mt46v32m16-75.h b/arch/ppc/boards/pcm030/mt46v32m16-75.h index 4d191f1f91..4d191f1f91 100644 --- a/board/pcm030/mt46v32m16-75.h +++ b/arch/ppc/boards/pcm030/mt46v32m16-75.h diff --git a/board/pcm030/pcm030.c b/arch/ppc/boards/pcm030/pcm030.c index f3845adf4c..f3845adf4c 100644 --- a/board/pcm030/pcm030.c +++ b/arch/ppc/boards/pcm030/pcm030.c diff --git a/board/pcm030/pcm030.dox b/arch/ppc/boards/pcm030/pcm030.dox index b9ada839f2..b9ada839f2 100644 --- a/board/pcm030/pcm030.dox +++ b/arch/ppc/boards/pcm030/pcm030.dox diff --git a/arch/sandbox/Makefile b/arch/sandbox/Makefile index 6b8942eb16..4ca17ed839 100644 --- a/arch/sandbox/Makefile +++ b/arch/sandbox/Makefile @@ -3,8 +3,10 @@ CPPFLAGS += -fno-strict-aliasing machine-y := sandbox -board-y := sandbox -lds-y := board/sandbox/barebox.lds +board-y := arch/sandbox/board +BOARD := $(board-y)/ +lds-y := $(BOARD)/barebox.lds + TEXT_BASE = $(CONFIG_TEXT_BASE) @@ -62,6 +64,6 @@ cmd_barebox__ = $(CC) -o $@ -Wl,-T,$(barebox-lds) \ -Wl,--start-group $(barebox-common) -Wl,--end-group \ -lrt -lpthread -common-y += board/sandbox/ arch/sandbox/os/ +common-y += $(BOARD) arch/sandbox/os/ -CLEAN_FILES += board/sandbox/barebox.lds +CLEAN_FILES += $(BOARD)/barebox.lds diff --git a/board/sandbox/.gitignore b/arch/sandbox/board/.gitignore index d1165788c9..d1165788c9 100644 --- a/board/sandbox/.gitignore +++ b/arch/sandbox/board/.gitignore diff --git a/board/sandbox/Makefile b/arch/sandbox/board/Makefile index 8abe5dde08..8abe5dde08 100644 --- a/board/sandbox/Makefile +++ b/arch/sandbox/board/Makefile diff --git a/board/sandbox/barebox.lds.S b/arch/sandbox/board/barebox.lds.S index 53e9f6021a..53e9f6021a 100644 --- a/board/sandbox/barebox.lds.S +++ b/arch/sandbox/board/barebox.lds.S diff --git a/board/sandbox/board.c b/arch/sandbox/board/board.c index 84017eb50d..84017eb50d 100644 --- a/board/sandbox/board.c +++ b/arch/sandbox/board/board.c diff --git a/board/sandbox/clock.c b/arch/sandbox/board/clock.c index b15086432c..b15086432c 100644 --- a/board/sandbox/clock.c +++ b/arch/sandbox/board/clock.c diff --git a/board/sandbox/config.h b/arch/sandbox/board/config.h index c96d762f2d..c96d762f2d 100644 --- a/board/sandbox/config.h +++ b/arch/sandbox/board/config.h diff --git a/board/sandbox/console.c b/arch/sandbox/board/console.c index 2959e85c7a..2959e85c7a 100644 --- a/board/sandbox/console.c +++ b/arch/sandbox/board/console.c diff --git a/board/sandbox/env/bin/init b/arch/sandbox/board/env/bin/init index a7cb7d563d..a7cb7d563d 100644 --- a/board/sandbox/env/bin/init +++ b/arch/sandbox/board/env/bin/init diff --git a/board/sandbox/env/config b/arch/sandbox/board/env/config index 2b148b6daa..2b148b6daa 100644 --- a/board/sandbox/env/config +++ b/arch/sandbox/board/env/config diff --git a/board/sandbox/hostfile.c b/arch/sandbox/board/hostfile.c index ad625d7a6e..ad625d7a6e 100644 --- a/board/sandbox/hostfile.c +++ b/arch/sandbox/board/hostfile.c diff --git a/arch/sandbox/configs/sandbox_defconfig b/arch/sandbox/configs/sandbox_defconfig index adcb07e49b..9037c8ba92 100644 --- a/arch/sandbox/configs/sandbox_defconfig +++ b/arch/sandbox/configs/sandbox_defconfig @@ -41,7 +41,7 @@ CONFIG_CONSOLE_ACTIVATE_FIRST=y # CONFIG_OF_FLAT_TREE is not set CONFIG_PARTITION=y CONFIG_DEFAULT_ENVIRONMENT=y -CONFIG_DEFAULT_ENVIRONMENT_PATH="board/sandbox/env" +CONFIG_DEFAULT_ENVIRONMENT_PATH="arch/sandbox/board/env" # # Debugging diff --git a/arch/x86/Makefile b/arch/x86/Makefile index 2e2cb810d7..57c5dbc3b0 100644 --- a/arch/x86/Makefile +++ b/arch/x86/Makefile @@ -26,7 +26,7 @@ all: $(KBUILD_IMAGE) ifneq ($(board-y),) -BOARD := board/$(board-y)/ +BOARD := arch/x86/boards/$(board-y)/ else BOARD := endif diff --git a/board/x86_generic/Makefile b/arch/x86/boards/x86_generic/Makefile index 248240da07..248240da07 100644 --- a/board/x86_generic/Makefile +++ b/arch/x86/boards/x86_generic/Makefile diff --git a/board/x86_generic/config.h b/arch/x86/boards/x86_generic/config.h index 39bea1844b..39bea1844b 100644 --- a/board/x86_generic/config.h +++ b/arch/x86/boards/x86_generic/config.h diff --git a/board/x86_generic/env/bin/boot b/arch/x86/boards/x86_generic/env/bin/boot index fcfffe3194..fcfffe3194 100644 --- a/board/x86_generic/env/bin/boot +++ b/arch/x86/boards/x86_generic/env/bin/boot diff --git a/board/x86_generic/env/bin/init b/arch/x86/boards/x86_generic/env/bin/init index 2924a4449a..2924a4449a 100644 --- a/board/x86_generic/env/bin/init +++ b/arch/x86/boards/x86_generic/env/bin/init diff --git a/board/x86_generic/env/config b/arch/x86/boards/x86_generic/env/config index dd57aad716..dd57aad716 100644 --- a/board/x86_generic/env/config +++ b/arch/x86/boards/x86_generic/env/config diff --git a/board/x86_generic/generic_pc.c b/arch/x86/boards/x86_generic/generic_pc.c index bd93bc168d..bd93bc168d 100644 --- a/board/x86_generic/generic_pc.c +++ b/arch/x86/boards/x86_generic/generic_pc.c diff --git a/arch/x86/configs/generic_defconfig b/arch/x86/configs/generic_defconfig index 091f696e3a..3c72242415 100644 --- a/arch/x86/configs/generic_defconfig +++ b/arch/x86/configs/generic_defconfig @@ -63,7 +63,7 @@ CONFIG_CONSOLE_ACTIVATE_FIRST=y # CONFIG_OF_FLAT_TREE is not set CONFIG_PARTITION=y CONFIG_DEFAULT_ENVIRONMENT=y -CONFIG_DEFAULT_ENVIRONMENT_PATH="board/x86_generic/env" +CONFIG_DEFAULT_ENVIRONMENT_PATH="arch/x86/boards/x86_generic/env" # # Debugging diff --git a/board/board.dox b/board/board.dox index 76bff45060..6bda416ec2 100644 --- a/board/board.dox +++ b/board/board.dox @@ -5,24 +5,24 @@ the @a barebox source tree. @section board_add_files Files/Directories to be added - - board/\<boardname\> - - board/\<boardname\>/Makefile - - board/\<boardname\>/\<boardname\>.c - - board/\<boardname\>/\<boardname\>.dox + - arch/\<architecture\>/boards/\<boardname\> + - arch/\<architecture\>/boards/\<boardname\>/Makefile + - arch/\<architecture\>/boards/\<boardname\>/\<boardname\>.c + - arch/\<architecture\>/boards/\<boardname\>/\<boardname\>.dox - include/configs/\<boardname\>.h - arch/\<architecture\>/configs/\<boardname\>_defconfig -@subsection board_makefile board/\<boardname\>Makefile +@subsection board_makefile arch/\<architecture\>/boards/\<boardname\>Makefile @verbatim obj-y += all files that builds the BSP (Assembler and/or C files) @endverbatim -@subsection board_basefile board/\<boardname\>\<boardname\>.c +@subsection board_basefile arch/\<architecture\>/boards/\<boardname\>\<boardname\>.c TBD -@subsection board_doxygen board/\<boardname\>/\<boardname\>.dox +@subsection board_doxygen arch/\<architecture\>/boards/\<boardname\>/\<boardname\>.dox This file should describe in short words your new board, what CPU it uses, what resources are provided and features it supports. @@ -58,7 +58,7 @@ at the right architecture. @note Consider to use an unique page lable. -@subsection board_lscript board/\<boardname\>/barebox.lds.S +@subsection board_lscript arch/\<architecture\>/boards/\<boardname\>/barebox.lds.S If your board needs a special binary @a barebox layout, you can provide a local board linker script file. This will replace the generic one provided by your diff --git a/board/eukrea_cpuimx35/flash_header.c b/board/eukrea_cpuimx35/flash_header.c deleted file mode 100644 index a0ccf5c8f7..0000000000 --- a/board/eukrea_cpuimx35/flash_header.c +++ /dev/null @@ -1,60 +0,0 @@ -#include <common.h> -#include <mach/imx-flash-header.h> - -extern unsigned long _stext; - -void __naked __flash_header_start go(void) -{ - __asm__ __volatile__("b exception_vectors\n"); -} - -struct imx_dcd_entry __dcd_entry_0x400 dcd_entry[] = { - { .ptr_type = 4, .addr = 0xB8001010, .val = 0x00000004, }, - { .ptr_type = 4, .addr = 0xB8001010, .val = 0x0000000C, }, - { .ptr_type = 4, .addr = 0xB8001004, .val = 0x007ffc3f, }, - { .ptr_type = 4, .addr = 0xB8001000, .val = 0x92220000, }, - { .ptr_type = 1, .addr = 0x80000400, .val = 0x12345678, }, - { .ptr_type = 4, .addr = 0xB8001000, .val = 0xA2220000, }, - { .ptr_type = 1, .addr = 0x80000000, .val = 0x87654321, }, - { .ptr_type = 1, .addr = 0x80000000, .val = 0x87654321, }, - { .ptr_type = 4, .addr = 0xB8001000, .val = 0xB2220000, }, - { .ptr_type = 1, .addr = 0x80000033, .val = 0xda, }, - { .ptr_type = 1, .addr = 0x82000000, .val = 0xda, }, - { .ptr_type = 4, .addr = 0xB8001004, .val = 0x007ffc2f, }, - { .ptr_type = 4, .addr = 0xB8001004, .val = 0x007ffc2f, }, - { .ptr_type = 4, .addr = 0xB8001004, .val = 0x007ffc2f, }, - { .ptr_type = 4, .addr = 0xB8001004, .val = 0x007ffc2f, }, - { .ptr_type = 4, .addr = 0xB8001004, .val = 0x007ffc2f, }, - { .ptr_type = 4, .addr = 0xB8001004, .val = 0x007ffc2f, }, - { .ptr_type = 4, .addr = 0xB8001004, .val = 0x007ffc2f, }, - { .ptr_type = 4, .addr = 0xB8001004, .val = 0x007ffc2f, }, - { .ptr_type = 4, .addr = 0xB8001004, .val = 0x007ffc2f, }, - { .ptr_type = 4, .addr = 0xB8001004, .val = 0x007ffc2f, }, - { .ptr_type = 4, .addr = 0xB8001004, .val = 0x007ffc2f, }, - { .ptr_type = 4, .addr = 0xB8001004, .val = 0x007ffc2f, }, - { .ptr_type = 4, .addr = 0xB8001004, .val = 0x007ffc2f, }, - { .ptr_type = 4, .addr = 0xB8001000, .val = 0x82220080, }, - { .ptr_type = 4, .addr = 0xB8001000, .val = 0x82228080, }, - { .ptr_type = 4, .addr = 0xB8001020, .val = 0x80000028, }, - { .ptr_type = 4, .addr = 0xB8001024, .val = 0x80000028, }, - { .ptr_type = 4, .addr = 0xB8001028, .val = 0x80000028, }, - { .ptr_type = 4, .addr = 0xB800102c, .val = 0x80000028, }, - { .ptr_type = 4, .addr = 0xB8001030, .val = 0x80000028, }, -}; - -#define APP_DEST 0x80000000 - -struct imx_flash_header __flash_header_0x400 flash_header = { - .app_code_jump_vector = APP_DEST + 0x1000, - .app_code_barker = APP_CODE_BARKER, - .app_code_csf = 0, - .dcd_ptr_ptr = APP_DEST + 0x400 + offsetof(struct imx_flash_header, dcd), - .super_root_key = 0, - .dcd = APP_DEST + 0x400 + offsetof(struct imx_flash_header, dcd_barker), - .app_dest = APP_DEST, - .dcd_barker = DCD_BARKER, - .dcd_block_len = sizeof (dcd_entry), -}; - -unsigned long __image_len_0x400 barebox_len = 0x40000; - diff --git a/board/omap/board-beagle.c b/board/omap/board-beagle.c deleted file mode 100644 index 866d8325e5..0000000000 --- a/board/omap/board-beagle.c +++ /dev/null @@ -1,270 +0,0 @@ -/* - * (C) Copyright 2008 - * Texas Instruments, <www.ti.com> - * Raghavendra KH <r-khandenahally@ti.com> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, - * MA 02111-1307 USA - */ - -/** - * @file - * @brief Beagle Specific Board Initialization routines - */ - -/** - * @page ti_beagle Texas Instruments Beagle Board - * - * FileName: board/omap/board-beagle.c - * - * Beagle Board from Texas Instruments as described here: - * http://www.beagleboard.org - * - * This board is based on OMAP3530. - * More on OMAP3530 (including documentation can be found here): - * http://focus.ti.com/docs/prod/folders/print/omap3530.html - * - * This file provides initialization in two stages: - * @li boot time initialization - do basics required to get SDRAM working. - * This is run from SRAM - so no case constructs and global vars can be used. - * @li run time initialization - this is for the rest of the initializations - * such as flash, uart etc. - * - * Boot time initialization includes: - * @li SDRAM initialization. - * @li Pin Muxing relevant for Beagle. - * - * Run time initialization includes - * @li serial @ref serial_ns16550.c driver device definition - * - * Originally from board/omap/board-sdp343x.c - */ - -#include <common.h> -#include <console.h> -#include <init.h> -#include <driver.h> -#include <asm/io.h> -#include <ns16550.h> -#include <asm/armlinux.h> -#include <mach/silicon.h> -#include <mach/sdrc.h> -#include <mach/sys_info.h> -#include <mach/syslib.h> -#include <mach/control.h> -#include <mach/omap3-mux.h> -#include <mach/gpmc.h> -#include "board.h" - -/******************** Board Boot Time *******************/ - -/** - * @brief Do the SDRC initialization for 128Meg Micron DDR for CS0 - * - * @return void - */ -static void sdrc_init(void) -{ - /* SDRAM software reset */ - /* No idle ack and RESET enable */ - writel(0x1A, SDRC_REG(SYSCONFIG)); - sdelay(100); - /* No idle ack and RESET disable */ - writel(0x18, SDRC_REG(SYSCONFIG)); - - /* SDRC Sharing register */ - /* 32-bit SDRAM on data lane [31:0] - CS0 */ - /* pin tri-stated = 1 */ - writel(0x00000100, SDRC_REG(SHARING)); - - /* ----- SDRC Registers Configuration --------- */ - /* SDRC_MCFG0 register */ - writel(0x02584099, SDRC_REG(MCFG_0)); - - /* SDRC_RFR_CTRL0 register */ - writel(0x54601, SDRC_REG(RFR_CTRL_0)); - - /* SDRC_ACTIM_CTRLA0 register */ - writel(0xA29DB4C6, SDRC_REG(ACTIM_CTRLA_0)); - - /* SDRC_ACTIM_CTRLB0 register */ - writel(0x12214, SDRC_REG(ACTIM_CTRLB_0)); - - /* Disble Power Down of CKE due to 1 CKE on combo part */ - writel(0x00000081, SDRC_REG(POWER)); - - /* SDRC_MANUAL command register */ - /* NOP command */ - writel(0x00000000, SDRC_REG(MANUAL_0)); - /* Precharge command */ - writel(0x00000001, SDRC_REG(MANUAL_0)); - /* Auto-refresh command */ - writel(0x00000002, SDRC_REG(MANUAL_0)); - /* Auto-refresh command */ - writel(0x00000002, SDRC_REG(MANUAL_0)); - - /* SDRC MR0 register Burst length=4 */ - writel(0x00000032, SDRC_REG(MR_0)); - - /* SDRC DLLA control register */ - writel(0x0000000A, SDRC_REG(DLLA_CTRL)); - - return; -} - -/** - * @brief Do the pin muxing required for Board operation. - * We enable ONLY the pins we require to set. OMAP provides pins which do not - * have alternate modes. Such pins done need to be set. - * - * See @ref MUX_VAL for description of the muxing mode. - * - * @return void - */ -static void mux_config(void) -{ - - /* SDRC_D0 - SDRC_D31 default mux mode is mode0 */ - - /* GPMC */ - MUX_VAL(CP(GPMC_A1), (IDIS | PTD | DIS | M0)); - MUX_VAL(CP(GPMC_A2), (IDIS | PTD | DIS | M0)); - MUX_VAL(CP(GPMC_A3), (IDIS | PTD | DIS | M0)); - MUX_VAL(CP(GPMC_A4), (IDIS | PTD | DIS | M0)); - MUX_VAL(CP(GPMC_A5), (IDIS | PTD | DIS | M0)); - MUX_VAL(CP(GPMC_A6), (IDIS | PTD | DIS | M0)); - MUX_VAL(CP(GPMC_A7), (IDIS | PTD | DIS | M0)); - MUX_VAL(CP(GPMC_A8), (IDIS | PTD | DIS | M0)); - MUX_VAL(CP(GPMC_A9), (IDIS | PTD | DIS | M0)); - MUX_VAL(CP(GPMC_A10), (IDIS | PTD | DIS | M0)); - - /* D0-D7 default mux mode is mode0 */ - MUX_VAL(CP(GPMC_D8), (IEN | PTD | DIS | M0)); - MUX_VAL(CP(GPMC_D9), (IEN | PTD | DIS | M0)); - MUX_VAL(CP(GPMC_D10), (IEN | PTD | DIS | M0)); - MUX_VAL(CP(GPMC_D11), (IEN | PTD | DIS | M0)); - MUX_VAL(CP(GPMC_D12), (IEN | PTD | DIS | M0)); - MUX_VAL(CP(GPMC_D13), (IEN | PTD | DIS | M0)); - MUX_VAL(CP(GPMC_D14), (IEN | PTD | DIS | M0)); - MUX_VAL(CP(GPMC_D15), (IEN | PTD | DIS | M0)); - MUX_VAL(CP(GPMC_CLK), (IDIS | PTD | DIS | M0)); - /* GPMC_NADV_ALE default mux mode is mode0 */ - /* GPMC_NOE default mux mode is mode0 */ - /* GPMC_NWE default mux mode is mode0 */ - /* GPMC_NBE0_CLE default mux mode is mode0 */ - MUX_VAL(CP(GPMC_NBE0_CLE), (IDIS | PTD | DIS | M0)); - MUX_VAL(CP(GPMC_NBE1), (IEN | PTD | DIS | M0)); - MUX_VAL(CP(GPMC_NWP), (IEN | PTD | DIS | M0)); - /* GPMC_WAIT0 default mux mode is mode0 */ - MUX_VAL(CP(GPMC_WAIT1), (IEN | PTU | EN | M0)); - - /* SERIAL INTERFACE */ - MUX_VAL(CP(UART3_CTS_RCTX), (IEN | PTD | EN | M0)); - MUX_VAL(CP(UART3_RTS_SD), (IDIS | PTD | DIS | M0)); - MUX_VAL(CP(UART3_RX_IRRX), (IEN | PTD | DIS | M0)); - MUX_VAL(CP(UART3_TX_IRTX), (IDIS | PTD | DIS | M0)); - MUX_VAL(CP(HSUSB0_CLK), (IEN | PTD | DIS | M0)); - MUX_VAL(CP(HSUSB0_STP), (IDIS | PTU | EN | M0)); - MUX_VAL(CP(HSUSB0_DIR), (IEN | PTD | DIS | M0)); - MUX_VAL(CP(HSUSB0_NXT), (IEN | PTD | DIS | M0)); - MUX_VAL(CP(HSUSB0_DATA0), (IEN | PTD | DIS | M0)); - MUX_VAL(CP(HSUSB0_DATA1), (IEN | PTD | DIS | M0)); - MUX_VAL(CP(HSUSB0_DATA2), (IEN | PTD | DIS | M0)); - MUX_VAL(CP(HSUSB0_DATA3), (IEN | PTD | DIS | M0)); - MUX_VAL(CP(HSUSB0_DATA4), (IEN | PTD | DIS | M0)); - MUX_VAL(CP(HSUSB0_DATA5), (IEN | PTD | DIS | M0)); - MUX_VAL(CP(HSUSB0_DATA6), (IEN | PTD | DIS | M0)); - MUX_VAL(CP(HSUSB0_DATA7), (IEN | PTD | DIS | M0)); - /* I2C1_SCL default mux mode is mode0 */ - /* I2C1_SDA default mux mode is mode0 */ -} - -/** - * @brief The basic entry point for board initialization. - * - * This is called as part of machine init (after arch init). - * This is again called with stack in SRAM, so not too many - * constructs possible here. - * - * @return void - */ -void board_init(void) -{ - int in_sdram = running_in_sdram(); - mux_config(); - /* Dont reconfigure SDRAM while running in SDRAM! */ - if (!in_sdram) - sdrc_init(); -} - -/******************** Board Run Time *******************/ - -#ifdef CONFIG_DRIVER_SERIAL_NS16550 - -static struct NS16550_plat serial_plat = { - .clock = 48000000, /* 48MHz (APLL96/2) */ - .f_caps = CONSOLE_STDIN | CONSOLE_STDOUT | CONSOLE_STDERR, - .reg_read = omap_uart_read, - .reg_write = omap_uart_write, -}; - -static struct device_d beagle_serial_device = { - .name = "serial_ns16550", - .map_base = OMAP_UART3_BASE, - .size = 1024, - .platform_data = (void *)&serial_plat, -}; - -/** - * @brief UART serial port initialization - remember to enable COM clocks in - * arch - * - * @return result of device registration - */ -static int beagle_console_init(void) -{ - /* Register the serial port */ - return register_device(&beagle_serial_device); -} -console_initcall(beagle_console_init); -#endif /* CONFIG_DRIVER_SERIAL_NS16550 */ - -static struct memory_platform_data sram_pdata = { - .name = "ram0", - .flags = DEVFS_RDWR, -}; - -static struct device_d sdram_dev = { - .name = "mem", - .map_base = 0x80000000, - .size = 128 * 1024 * 1024, - .platform_data = &sram_pdata, -}; - -static int beagle_devices_init(void) -{ - int ret; - ret = register_device(&sdram_dev); - if (ret) - goto failed; -#ifdef CONFIG_GPMC - /* WP is made high and WAIT1 active Low */ - gpmc_generic_init(0x10); -#endif - armlinux_add_dram(&sdram_dev); -failed: - return ret; -} -device_initcall(beagle_devices_init); diff --git a/commands/Kconfig b/commands/Kconfig index 0ea32d963b..1ffc826308 100644 --- a/commands/Kconfig +++ b/commands/Kconfig @@ -180,6 +180,12 @@ menu "flash " config CMD_FLASH tristate prompt "protect/erase" + +config CMD_UBI + tristate + default y if UBI + prompt "ubimkvol, ubirmvol, ubiattach" + endmenu diff --git a/commands/Makefile b/commands/Makefile index 3eef5debf3..b99f042129 100644 --- a/commands/Makefile +++ b/commands/Makefile @@ -49,3 +49,4 @@ obj-$(CONFIG_USB_GADGET_DFU) += dfu.o obj-$(CONFIG_CMD_GPIO) += gpio.o obj-$(CONFIG_CMD_UNLZO) += unlzo.o obj-$(CONFIG_CMD_I2C) += i2c.o +obj-$(CONFIG_CMD_UBI) += ubi.o diff --git a/commands/ubi.c b/commands/ubi.c new file mode 100644 index 0000000000..3da08350b9 --- /dev/null +++ b/commands/ubi.c @@ -0,0 +1,129 @@ +#include <common.h> +#include <command.h> +#include <fs.h> +#include <fcntl.h> +#include <ioctl.h> +#include <errno.h> +#include <getopt.h> +#include <linux/mtd/mtd.h> +#include <linux/kernel.h> +#include <linux/mtd/mtd-abi.h> +#include <mtd/ubi-user.h> +#include <ubi-media.h> + +static int do_ubimkvol(struct command *cmdtp, int argc, char *argv[]) +{ + struct ubi_mkvol_req req; + int fd, ret; + size_t size; + + if (argc != 4) + return COMMAND_ERROR_USAGE; + + size = strtoul_suffix(argv[3], NULL, 0); + req.name_len = min_t(int, strlen(argv[2]), UBI_VOL_NAME_MAX); + strncpy(req.name, argv[2], req.name_len); + req.name[req.name_len] = 0; + + req.vol_type = UBI_DYNAMIC_VOLUME; + req.bytes = size; + req.vol_id = UBI_VOL_NUM_AUTO; + req.alignment = 1; + + fd = open(argv[1], O_WRONLY); + if (fd < 0) { + perror("open"); + return 1; + } + + ret = ioctl(fd, UBI_IOCMKVOL, &req); + if (ret) + printf("failed to create: %s\n", strerror(-ret)); + close(fd); + + return ret ? 1 : 0; +} + +static const __maybe_unused char cmd_ubimkvol_help[] = +"Usage: ubimkvol <ubidev> <name> <size>\n" +"Create an ubi volume on <ubidev> with name <name> and size <size>\n" +"If size os zero all available space is used for the volume\n"; + +BAREBOX_CMD_START(ubimkvol) + .cmd = do_ubimkvol, + .usage = "create an ubi volume", + BAREBOX_CMD_HELP(cmd_ubimkvol_help) +BAREBOX_CMD_END + + +static int do_ubiattach(struct command *cmdtp, int argc, char *argv[]) +{ + struct mtd_info_user user; + int fd, ret; + + if (argc != 2) + return COMMAND_ERROR_USAGE; + + fd = open(argv[1], O_RDWR); + if (fd < 0) { + perror("open"); + return 1; + } + + ret = ioctl(fd, MEMGETINFO, &user); + if (!ret) + ret = ubi_attach_mtd_dev(user.mtd, UBI_DEV_NUM_AUTO, 0); + + if (ret) + printf("failed to attach: %s\n", strerror(-ret)); + + close(fd); + + return ret ? 1 : 0; +} + +static const __maybe_unused char cmd_ubiattach_help[] = +"Usage: ubiattach <mtddev>\n" +"Attach <mtddev> to ubi\n"; + +BAREBOX_CMD_START(ubiattach) + .cmd = do_ubiattach, + .usage = "attach a mtd dev to ubi", + BAREBOX_CMD_HELP(cmd_ubiattach_help) +BAREBOX_CMD_END + +static int do_ubirmvol(struct command *cmdtp, int argc, char *argv[]) +{ + struct ubi_mkvol_req req; + int fd, ret; + + if (argc != 3) + return COMMAND_ERROR_USAGE; + + strncpy(req.name, argv[2], UBI_VOL_NAME_MAX); + req.name[UBI_VOL_NAME_MAX] = 0; + + fd = open(argv[1], O_WRONLY); + if (fd < 0) { + perror("open"); + return 1; + } + + ret = ioctl(fd, UBI_IOCRMVOL, &req); + if (ret) + printf("failed to delete: %s\n", strerror(-ret)); + close(fd); + + return ret ? 1 : 0; +} + +static const __maybe_unused char cmd_ubirmvol_help[] = +"Usage: ubirmvol <ubidev> <name>\n" +"Delete ubi volume <name> from <ubidev>\n"; + +BAREBOX_CMD_START(ubirmvol) + .cmd = do_ubirmvol, + .usage = "delete an ubi volume", + BAREBOX_CMD_HELP(cmd_ubirmvol_help) +BAREBOX_CMD_END + diff --git a/common/Makefile b/common/Makefile index 8b8686db1c..14f8643f15 100644 --- a/common/Makefile +++ b/common/Makefile @@ -26,6 +26,8 @@ ENV_FILES := $(shell cd $(srctree); for i in $(CONFIG_DEFAULT_ENVIRONMENT_PATH); endif # ifdef CONFIG_DEFAULT_ENVIRONMENT -include/barebox_default_env.h: $(ENV_FILES) - $(Q)scripts/genenv $(srctree) $(CONFIG_DEFAULT_ENVIRONMENT_PATH) - $(Q)cat barebox_default_env | scripts/bin2c default_environment > $@ +barebox_default_env: $(ENV_FILES) + $(Q)$(srctree)/scripts/genenv $(srctree) $(objtree) $(CONFIG_DEFAULT_ENVIRONMENT_PATH) + +include/barebox_default_env.h: barebox_default_env + $(Q)cat $< | $(objtree)/scripts/bin2c default_environment > $@ diff --git a/drivers/Kconfig b/drivers/Kconfig index bf559c4054..ae9efce379 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -5,7 +5,7 @@ source "drivers/net/Kconfig" source "drivers/spi/Kconfig" source "drivers/i2c/Kconfig" source "drivers/nor/Kconfig" -source "drivers/nand/Kconfig" +source "drivers/mtd/Kconfig" source "drivers/ata/Kconfig" source "drivers/usb/Kconfig" source "drivers/video/Kconfig" diff --git a/drivers/Makefile b/drivers/Makefile index 7bae6ffcb6..bce68bc7b5 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -1,6 +1,6 @@ obj-y += net/ obj-y += serial/ -obj-y += nand/ +obj-y += mtd/ obj-y += nor/ obj-y += usb/ obj-$(CONFIG_ATA) += ata/ diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig new file mode 100644 index 0000000000..562f0cd8cd --- /dev/null +++ b/drivers/mtd/Kconfig @@ -0,0 +1,9 @@ +menuconfig MTD + bool "Memory Technology Device (MTD) support" + +if MTD + +source "drivers/mtd/nand/Kconfig" +source "drivers/mtd/ubi/Kconfig" + +endif diff --git a/drivers/mtd/Makefile b/drivers/mtd/Makefile new file mode 100644 index 0000000000..85bed11745 --- /dev/null +++ b/drivers/mtd/Makefile @@ -0,0 +1,3 @@ +obj-$(CONFIG_NAND) += nand/ +obj-$(CONFIG_UBI) += ubi/ +obj-$(CONFIG_PARTITION_NEED_MTD) += partition.o diff --git a/drivers/nand/Kconfig b/drivers/mtd/nand/Kconfig index 031b94d4e5..ddc0c34e7d 100644 --- a/drivers/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -1,5 +1,5 @@ menuconfig NAND - bool "NAND support " + bool "NAND support " select MTD_NAND_IDS help This enables support for accessing all type of NAND flash @@ -25,14 +25,6 @@ config NAND_OMAP_GPMC Support for NAND flash using GPMC. GPMC is a common memory interface found on Texas Instrument's OMAP platforms -config NAND_OMAP_GPMC_HWECC - bool "The Hardware ECC support" - depends on NAND && NAND_OMAP_GPMC - default n - help - The ECC compuatation for the data to be written/read can be either by - software or omap has Hw ecc engine which calculates it. - config NAND_ATMEL bool prompt "Atmel (AT91SAM9xxx) NAND driver" diff --git a/drivers/nand/Makefile b/drivers/mtd/nand/Makefile index 73f734688e..73f734688e 100644 --- a/drivers/nand/Makefile +++ b/drivers/mtd/nand/Makefile diff --git a/drivers/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c index c3669e5a31..e8f85fc621 100644 --- a/drivers/nand/atmel_nand.c +++ b/drivers/mtd/nand/atmel_nand.c @@ -11,8 +11,8 @@ * Add Hardware ECC support for AT91SAM9260 / AT91SAM9263 * Richard Genoud (richard.genoud@gmail.com), Adeneo Copyright (C) 2007 * - * Derived from Das barebox source code - * (barebox-1.1.5/board/atmel/at91sam9263ek/nand.c) + * Derived from Das U-Boot source code + * (u-boot-1.1.5/board/atmel/at91sam9263ek/nand.c) * (C) Copyright 2006 ATMEL Rousset, Lacressonniere Nicolas * * diff --git a/drivers/nand/atmel_nand_ecc.h b/drivers/mtd/nand/atmel_nand_ecc.h index 578c776e13..578c776e13 100644 --- a/drivers/nand/atmel_nand_ecc.h +++ b/drivers/mtd/nand/atmel_nand_ecc.h diff --git a/drivers/nand/diskonchip.c b/drivers/mtd/nand/diskonchip.c index e762524540..e762524540 100644 --- a/drivers/nand/diskonchip.c +++ b/drivers/mtd/nand/diskonchip.c diff --git a/drivers/nand/nand.c b/drivers/mtd/nand/nand.c index 4927231c17..6a150fe0cb 100644 --- a/drivers/nand/nand.c +++ b/drivers/mtd/nand/nand.c @@ -123,6 +123,7 @@ static int nand_ioctl(struct cdev *cdev, int request, void *buf) user->size = info->size; user->erasesize = info->erasesize; user->oobsize = info->oobsize; + user->mtd = info; /* The below fields are obsolete */ user->ecctype = -1; user->eccsize = 0; @@ -220,6 +221,7 @@ int add_mtd_device(struct mtd_info *mtd) mtd->cdev.name = asprintf("nand%d", mtd->class_dev.id); mtd->cdev.priv = mtd; mtd->cdev.dev = &mtd->class_dev; + mtd->cdev.mtd = mtd; sprintf(str, "%u", mtd->size); dev_add_param_fixed(&mtd->class_dev, "size", str); diff --git a/drivers/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index b75a450278..b75a450278 100644 --- a/drivers/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c diff --git a/drivers/nand/nand_bbt.c b/drivers/mtd/nand/nand_bbt.c index 4a6bf390a4..4a6bf390a4 100644 --- a/drivers/nand/nand_bbt.c +++ b/drivers/mtd/nand/nand_bbt.c diff --git a/drivers/nand/nand_ecc.c b/drivers/mtd/nand/nand_ecc.c index 266d57318b..266d57318b 100644 --- a/drivers/nand/nand_ecc.c +++ b/drivers/mtd/nand/nand_ecc.c diff --git a/drivers/nand/nand_ids.c b/drivers/mtd/nand/nand_ids.c index f95975c9ea..f95975c9ea 100644 --- a/drivers/nand/nand_ids.c +++ b/drivers/mtd/nand/nand_ids.c diff --git a/drivers/nand/nand_imx.c b/drivers/mtd/nand/nand_imx.c index 5454e32083..5454e32083 100644 --- a/drivers/nand/nand_imx.c +++ b/drivers/mtd/nand/nand_imx.c diff --git a/drivers/nand/nand_omap_gpmc.c b/drivers/mtd/nand/nand_omap_gpmc.c index c6647e56ed..1363808ce0 100644 --- a/drivers/nand/nand_omap_gpmc.c +++ b/drivers/mtd/nand/nand_omap_gpmc.c @@ -102,10 +102,57 @@ struct gpmc_nand_info { uint64_t timeout; unsigned inuse:1; unsigned wait_pol:1; -#ifdef CONFIG_NAND_OMAP_GPMC_HWECC unsigned char ecc_parity_pairs; unsigned int ecc_config; -#endif +}; + +/* Typical BOOTROM oob layouts-requires hwecc **/ + +/** Large Page x8 NAND device Layout */ +static struct nand_ecclayout ecc_lp_x8 = { + .eccbytes = 12, + .eccpos = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}, + .oobfree = { + { + .offset = 60, + .length = 2, + } + } +}; + +/** Large Page x16 NAND device Layout */ +static struct nand_ecclayout ecc_lp_x16 = { + .eccbytes = 12, + .eccpos = {2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13}, + .oobfree = { + { + .offset = 60, + .length = 2, + } + } +}; + +/** Small Page x8 NAND device Layout */ +static struct nand_ecclayout ecc_sp_x8 = { + .eccbytes = 3, + .eccpos = {1, 2, 3}, + .oobfree = { + { + .offset = 14, + .length = 2, + } + } +}; + +/** Small Page x16 NAND device Layout */ +static struct nand_ecclayout ecc_sp_x16 = { + .eccbytes = 3, + .eccpos = {2, 3, 4}, + .oobfree = { + { + .offset = 14, + .length = 2 } + } }; /** @@ -203,8 +250,6 @@ static void omap_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl) return; } -#ifdef CONFIG_NAND_OMAP_GPMC_HWECC - /** * @brief This function will generate true ECC value, which can be used * when correcting data read from NAND flash memory core @@ -345,7 +390,6 @@ static void omap_enable_hwecc(struct mtd_info *mtd, int mode) break; } } -#endif /* CONFIG_NAND_OMAP_GPMC_HWECC */ /** * @brief nand device probe. @@ -362,6 +406,7 @@ static int gpmc_nand_probe(struct device_d *pdev) struct mtd_info *minfo; unsigned long cs_base; int err; + struct nand_ecclayout *layout, *lsp, *llp; gpmcnand_dbg("pdev=%x", (unsigned int)pdev); pdata = (struct gpmc_nand_platform_data *)pdev->platform_data; @@ -453,11 +498,6 @@ static int gpmc_nand_probe(struct device_d *pdev) /* State my controller */ nand->controller = &oinfo->controller; - /* if a different placement scheme is requested */ - if (pdata->oob) - nand->ecc.layout = pdata->oob; - -#ifdef CONFIG_NAND_OMAP_GPMC_HWECC if (pdata->plat_options & NAND_HWECC_ENABLE) { /* Program how many columns we expect+ * enable the cs we want and enable the engine @@ -474,7 +514,6 @@ static int gpmc_nand_probe(struct device_d *pdev) nand->ecc.steps = nand->ecc.layout->eccbytes / nand->ecc.bytes; oinfo->ecc_parity_pairs = 12; } else -#endif nand->ecc.mode = NAND_ECC_SOFT; /* All information is ready.. now lets call setup, if present */ @@ -495,15 +534,47 @@ static int gpmc_nand_probe(struct device_d *pdev) writeb(NAND_CMD_RESET, oinfo->gpmc_command); mdelay(1); - /* In normal mode, we scan to get just the device - * presence and then to get the device geometry - */ - if (nand_scan(minfo, 1)) { - gpmcnand_err("device scan failed\n"); + /* first scan to find the device and get the page size */ + if (nand_scan_ident(minfo, 1)) { + err = -ENXIO; + goto out_release_mem; + } + + switch (pdata->device_width) { + case 8: + lsp = &ecc_sp_x8; + llp = &ecc_lp_x8; + break; + case 16: + lsp = &ecc_sp_x16; + llp = &ecc_lp_x16; + break; + default: + err = -EINVAL; + goto out_release_mem; + } + + switch (minfo->writesize) { + case 512: + layout = lsp; + break; + case 2048: + layout = llp; + break; + default: + err = -EINVAL; + goto out_release_mem; + } + + /* second phase scan */ + if (nand_scan_tail(minfo)) { err = -ENXIO; goto out_release_mem; } + if (pdata->plat_options & NAND_HWECC_ENABLE) + nand->ecc.layout = layout; + /* We are all set to register with the system now! */ err = add_mtd_device(minfo); if (err) { diff --git a/drivers/nand/nand_s3c2410.c b/drivers/mtd/nand/nand_s3c2410.c index b989583050..b989583050 100644 --- a/drivers/nand/nand_s3c2410.c +++ b/drivers/mtd/nand/nand_s3c2410.c diff --git a/drivers/nand/nand_util.c b/drivers/mtd/nand/nand_util.c index d57294e624..d57294e624 100644 --- a/drivers/nand/nand_util.c +++ b/drivers/mtd/nand/nand_util.c diff --git a/drivers/mtd/partition.c b/drivers/mtd/partition.c new file mode 100644 index 0000000000..df2eb40bf8 --- /dev/null +++ b/drivers/mtd/partition.c @@ -0,0 +1,143 @@ +#include <common.h> +#include <errno.h> +#include <malloc.h> +#include <linux/err.h> +#include <linux/mtd/mtd.h> + +struct mtd_part { + struct mtd_info mtd; + struct mtd_info *master; + uint64_t offset; + struct list_head list; +}; + +#define PART(x) ((struct mtd_part *)(x)) + +static int mtd_part_read(struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, u_char *buf) +{ + struct mtd_part *part = PART(mtd); + struct mtd_ecc_stats stats; + int res; + + stats = part->master->ecc_stats; + + if (from >= mtd->size) + len = 0; + else if (from + len > mtd->size) + len = mtd->size - from; + res = part->master->read(part->master, from + part->offset, + len, retlen, buf); + return res; +} + +static int mtd_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); + + if (!(mtd->flags & MTD_WRITEABLE)) + return -EROFS; + if (to >= mtd->size) + len = 0; + else if (to + len > mtd->size) + len = mtd->size - to; + return part->master->write(part->master, to + part->offset, + len, retlen, buf); +} + +static int mtd_part_erase(struct mtd_info *mtd, struct erase_info *instr) +{ + struct mtd_part *part = PART(mtd); + int ret; + + if (!(mtd->flags & MTD_WRITEABLE)) + return -EROFS; + if (instr->addr >= mtd->size) + return -EINVAL; + instr->addr += part->offset; + ret = part->master->erase(part->master, instr); + if (ret) { + if (instr->fail_addr != 0xffffffff) + instr->fail_addr -= part->offset; + instr->addr -= part->offset; + } + return ret; +} + +static int mtd_part_block_isbad(struct mtd_info *mtd, loff_t ofs) +{ + struct mtd_part *part = PART(mtd); + if (ofs >= mtd->size) + return -EINVAL; + ofs += part->offset; + return part->master->block_isbad(part->master, ofs); +} + +static int mtd_part_block_markbad(struct mtd_info *mtd, loff_t ofs) +{ + struct mtd_part *part = PART(mtd); + int res; + + if (!(mtd->flags & MTD_WRITEABLE)) + return -EROFS; + if (ofs >= mtd->size) + return -EINVAL; + ofs += part->offset; + res = part->master->block_markbad(part->master, ofs); + if (!res) + mtd->ecc_stats.badblocks++; + return res; +} + +struct mtd_info *mtd_add_partition(struct mtd_info *mtd, off_t offset, size_t size, + unsigned long flags, const char *name) +{ + struct mtd_part *slave; + struct mtd_info *slave_mtd; + int start = 0, end = 0, i; + + slave = xzalloc(sizeof(*slave)); + slave_mtd = &slave->mtd; + + memcpy(slave_mtd, mtd, sizeof(*slave)); + + /* + * find the number of eraseregions the partition includes. + * Do not bother to create the mtd_erase_region_infos as + * ubi is only interested in its number. UBI does not + * yet support multiple erase regions. + */ + for (i = mtd->numeraseregions - 1; i >= 0; i--) { + struct mtd_erase_region_info *region = &mtd->eraseregions[i]; + if (offset >= region->offset && + offset < region->offset + region->erasesize * region->numblocks) + start = i; + if (offset + size >= region->offset && + offset + size <= region->offset + region->erasesize * region->numblocks) + end = i; + } + + slave_mtd->numeraseregions = end - start; + + slave_mtd->read = mtd_part_read; + slave_mtd->write = mtd_part_write; + slave_mtd->erase = mtd_part_erase; + slave_mtd->block_isbad = mtd->block_isbad ? mtd_part_block_isbad : NULL; + slave_mtd->block_markbad = mtd->block_markbad ? mtd_part_block_markbad : NULL; + slave_mtd->size = size; + slave_mtd->name = strdup(name); + + slave->offset = offset; + slave->master = mtd; + + return slave_mtd; +} + +void mtd_del_partition(struct mtd_info *mtd) +{ + struct mtd_part *part = PART(mtd); + + free(mtd->name); + free(part); +} diff --git a/drivers/mtd/ubi/Kconfig b/drivers/mtd/ubi/Kconfig new file mode 100644 index 0000000000..35d321b37f --- /dev/null +++ b/drivers/mtd/ubi/Kconfig @@ -0,0 +1,6 @@ +config UBI + bool "UBI support " + select PARTITION_NEED_MTD + help + This enables support for UBI (unsorted block images) + diff --git a/drivers/mtd/ubi/Makefile b/drivers/mtd/ubi/Makefile new file mode 100644 index 0000000000..cef11416a1 --- /dev/null +++ b/drivers/mtd/ubi/Makefile @@ -0,0 +1,3 @@ +obj-y += build.o vtbl.o vmt.o upd.o kapi.o eba.o io.o wl.o scan.o misc.o debug.o cdev.o + + diff --git a/drivers/mtd/ubi/build.c b/drivers/mtd/ubi/build.c new file mode 100644 index 0000000000..a59972faa6 --- /dev/null +++ b/drivers/mtd/ubi/build.c @@ -0,0 +1,1059 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * Copyright (c) Nokia Corporation, 2007 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Artem Bityutskiy (Битюцкий Артём), + * Frank Haverkamp + */ + +/* + * This file includes UBI initialization and building of UBI devices. + * + * When UBI is initialized, it attaches all the MTD devices specified as the + * module load parameters or the kernel boot parameters. If MTD devices were + * specified, UBI does not attach any MTD device, but it is possible to do + * later using the "UBI control device". + * + * At the moment we only attach UBI devices by scanning, which will become a + * bottleneck when flashes reach certain large size. Then one may improve UBI + * and add other methods, although it does not seem to be easy to do. + */ + +#ifdef UBI_LINUX +#include <linux/err.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/stringify.h> +#include <linux/stat.h> +#include <linux/miscdevice.h> +#include <linux/log2.h> +#include <linux/kthread.h> +#endif +#include "ubi-barebox.h" +#include "ubi.h" + +/* Maximum length of the 'mtd=' parameter */ +#define MTD_PARAM_LEN_MAX 64 + +/** + * struct mtd_dev_param - MTD device parameter description data structure. + * @name: MTD device name or number string + * @vid_hdr_offs: VID header offset + */ +struct mtd_dev_param +{ + char name[MTD_PARAM_LEN_MAX]; + int vid_hdr_offs; +}; + +/* Numbers of elements set in the @mtd_dev_param array */ +static int mtd_devs = 0; + +/* MTD devices specification parameters */ +static struct mtd_dev_param mtd_dev_param[UBI_MAX_DEVICES]; + +/* Root UBI "class" object (corresponds to '/<sysfs>/class/ubi/') */ +struct class *ubi_class; + +#ifdef UBI_LINUX +/* Slab cache for wear-leveling entries */ +struct kmem_cache *ubi_wl_entry_slab; + +/* UBI control character device */ +static struct miscdevice ubi_ctrl_cdev = { + .minor = MISC_DYNAMIC_MINOR, + .name = "ubi_ctrl", + .fops = &ubi_ctrl_cdev_operations, +}; +#endif + +/* All UBI devices in system */ +struct ubi_device *ubi_devices[UBI_MAX_DEVICES]; + +#ifdef UBI_LINUX +/* Serializes UBI devices creations and removals */ +DEFINE_MUTEX(ubi_devices_mutex); + +/* Protects @ubi_devices and @ubi->ref_count */ +static DEFINE_SPINLOCK(ubi_devices_lock); + +/* "Show" method for files in '/<sysfs>/class/ubi/' */ +static ssize_t ubi_version_show(struct class *class, char *buf) +{ + return sprintf(buf, "%d\n", UBI_VERSION); +} + +/* UBI version attribute ('/<sysfs>/class/ubi/version') */ +static struct class_attribute ubi_version = + __ATTR(version, S_IRUGO, ubi_version_show, NULL); + +static ssize_t dev_attribute_show(struct device *dev, + struct device_attribute *attr, char *buf); + +/* UBI device attributes (correspond to files in '/<sysfs>/class/ubi/ubiX') */ +static struct device_attribute dev_eraseblock_size = + __ATTR(eraseblock_size, S_IRUGO, dev_attribute_show, NULL); +static struct device_attribute dev_avail_eraseblocks = + __ATTR(avail_eraseblocks, S_IRUGO, dev_attribute_show, NULL); +static struct device_attribute dev_total_eraseblocks = + __ATTR(total_eraseblocks, S_IRUGO, dev_attribute_show, NULL); +static struct device_attribute dev_volumes_count = + __ATTR(volumes_count, S_IRUGO, dev_attribute_show, NULL); +static struct device_attribute dev_max_ec = + __ATTR(max_ec, S_IRUGO, dev_attribute_show, NULL); +static struct device_attribute dev_reserved_for_bad = + __ATTR(reserved_for_bad, S_IRUGO, dev_attribute_show, NULL); +static struct device_attribute dev_bad_peb_count = + __ATTR(bad_peb_count, S_IRUGO, dev_attribute_show, NULL); +static struct device_attribute dev_max_vol_count = + __ATTR(max_vol_count, S_IRUGO, dev_attribute_show, NULL); +static struct device_attribute dev_min_io_size = + __ATTR(min_io_size, S_IRUGO, dev_attribute_show, NULL); +static struct device_attribute dev_bgt_enabled = + __ATTR(bgt_enabled, S_IRUGO, dev_attribute_show, NULL); +static struct device_attribute dev_mtd_num = + __ATTR(mtd_num, S_IRUGO, dev_attribute_show, NULL); +#endif + +/** + * ubi_get_device - get UBI device. + * @ubi_num: UBI device number + * + * This function returns UBI device description object for UBI device number + * @ubi_num, or %NULL if the device does not exist. This function increases the + * device reference count to prevent removal of the device. In other words, the + * device cannot be removed if its reference count is not zero. + */ +struct ubi_device *ubi_get_device(int ubi_num) +{ + struct ubi_device *ubi; + + spin_lock(&ubi_devices_lock); + ubi = ubi_devices[ubi_num]; + if (ubi) { + ubi_assert(ubi->ref_count >= 0); + ubi->ref_count += 1; + get_device(&ubi->dev); + } + spin_unlock(&ubi_devices_lock); + + return ubi; +} + +/** + * ubi_put_device - drop an UBI device reference. + * @ubi: UBI device description object + */ +void ubi_put_device(struct ubi_device *ubi) +{ + spin_lock(&ubi_devices_lock); + ubi->ref_count -= 1; + put_device(&ubi->dev); + spin_unlock(&ubi_devices_lock); +} + +/** + * ubi_get_by_major - get UBI device description object by character device + * major number. + * @major: major number + * + * This function is similar to 'ubi_get_device()', but it searches the device + * by its major number. + */ +struct ubi_device *ubi_get_by_major(int major) +{ + int i; + struct ubi_device *ubi; + + spin_lock(&ubi_devices_lock); + for (i = 0; i < UBI_MAX_DEVICES; i++) { + ubi = ubi_devices[i]; + if (ubi && MAJOR(ubi->cdev.dev) == major) { + ubi_assert(ubi->ref_count >= 0); + ubi->ref_count += 1; + get_device(&ubi->dev); + spin_unlock(&ubi_devices_lock); + return ubi; + } + } + spin_unlock(&ubi_devices_lock); + + return NULL; +} + +/** + * ubi_major2num - get UBI device number by character device major number. + * @major: major number + * + * This function searches UBI device number object by its major number. If UBI + * device was not found, this function returns -ENODEV, otherwise the UBI device + * number is returned. + */ +int ubi_major2num(int major) +{ + int i, ubi_num = -ENODEV; + + spin_lock(&ubi_devices_lock); + for (i = 0; i < UBI_MAX_DEVICES; i++) { + struct ubi_device *ubi = ubi_devices[i]; + + if (ubi && MAJOR(ubi->cdev.dev) == major) { + ubi_num = ubi->ubi_num; + break; + } + } + spin_unlock(&ubi_devices_lock); + + return ubi_num; +} + +#ifdef UBI_LINUX +/* "Show" method for files in '/<sysfs>/class/ubi/ubiX/' */ +static ssize_t dev_attribute_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret; + struct ubi_device *ubi; + + /* + * The below code looks weird, but it actually makes sense. We get the + * UBI device reference from the contained 'struct ubi_device'. But it + * is unclear if the device was removed or not yet. Indeed, if the + * device was removed before we increased its reference count, + * 'ubi_get_device()' will return -ENODEV and we fail. + * + * Remember, 'struct ubi_device' is freed in the release function, so + * we still can use 'ubi->ubi_num'. + */ + ubi = container_of(dev, struct ubi_device, dev); + ubi = ubi_get_device(ubi->ubi_num); + if (!ubi) + return -ENODEV; + + if (attr == &dev_eraseblock_size) + ret = sprintf(buf, "%d\n", ubi->leb_size); + else if (attr == &dev_avail_eraseblocks) + ret = sprintf(buf, "%d\n", ubi->avail_pebs); + else if (attr == &dev_total_eraseblocks) + ret = sprintf(buf, "%d\n", ubi->good_peb_count); + else if (attr == &dev_volumes_count) + ret = sprintf(buf, "%d\n", ubi->vol_count - UBI_INT_VOL_COUNT); + else if (attr == &dev_max_ec) + ret = sprintf(buf, "%d\n", ubi->max_ec); + else if (attr == &dev_reserved_for_bad) + ret = sprintf(buf, "%d\n", ubi->beb_rsvd_pebs); + else if (attr == &dev_bad_peb_count) + ret = sprintf(buf, "%d\n", ubi->bad_peb_count); + else if (attr == &dev_max_vol_count) + ret = sprintf(buf, "%d\n", ubi->vtbl_slots); + else if (attr == &dev_min_io_size) + ret = sprintf(buf, "%d\n", ubi->min_io_size); + else if (attr == &dev_bgt_enabled) + ret = sprintf(buf, "%d\n", ubi->thread_enabled); + else if (attr == &dev_mtd_num) + ret = sprintf(buf, "%d\n", ubi->mtd->index); + else + ret = -EINVAL; + + ubi_put_device(ubi); + return ret; +} + +/* Fake "release" method for UBI devices */ +static void dev_release(struct device *dev) { } + +/** + * ubi_sysfs_init - initialize sysfs for an UBI device. + * @ubi: UBI device description object + * + * This function returns zero in case of success and a negative error code in + * case of failure. + */ +static int ubi_sysfs_init(struct ubi_device *ubi) +{ + int err; + + ubi->dev.release = dev_release; + ubi->dev.devt = ubi->cdev.dev; + ubi->dev.class = ubi_class; + sprintf(&ubi->dev.bus_id[0], UBI_NAME_STR"%d", ubi->ubi_num); + err = device_register(&ubi->dev); + if (err) + return err; + + err = device_create_file(&ubi->dev, &dev_eraseblock_size); + if (err) + return err; + err = device_create_file(&ubi->dev, &dev_avail_eraseblocks); + if (err) + return err; + err = device_create_file(&ubi->dev, &dev_total_eraseblocks); + if (err) + return err; + err = device_create_file(&ubi->dev, &dev_volumes_count); + if (err) + return err; + err = device_create_file(&ubi->dev, &dev_max_ec); + if (err) + return err; + err = device_create_file(&ubi->dev, &dev_reserved_for_bad); + if (err) + return err; + err = device_create_file(&ubi->dev, &dev_bad_peb_count); + if (err) + return err; + err = device_create_file(&ubi->dev, &dev_max_vol_count); + if (err) + return err; + err = device_create_file(&ubi->dev, &dev_min_io_size); + if (err) + return err; + err = device_create_file(&ubi->dev, &dev_bgt_enabled); + if (err) + return err; + err = device_create_file(&ubi->dev, &dev_mtd_num); + return err; +} + +/** + * ubi_sysfs_close - close sysfs for an UBI device. + * @ubi: UBI device description object + */ +static void ubi_sysfs_close(struct ubi_device *ubi) +{ + device_remove_file(&ubi->dev, &dev_mtd_num); + device_remove_file(&ubi->dev, &dev_bgt_enabled); + device_remove_file(&ubi->dev, &dev_min_io_size); + device_remove_file(&ubi->dev, &dev_max_vol_count); + device_remove_file(&ubi->dev, &dev_bad_peb_count); + device_remove_file(&ubi->dev, &dev_reserved_for_bad); + device_remove_file(&ubi->dev, &dev_max_ec); + device_remove_file(&ubi->dev, &dev_volumes_count); + device_remove_file(&ubi->dev, &dev_total_eraseblocks); + device_remove_file(&ubi->dev, &dev_avail_eraseblocks); + device_remove_file(&ubi->dev, &dev_eraseblock_size); + device_unregister(&ubi->dev); +} +#endif + +/** + * kill_volumes - destroy all volumes. + * @ubi: UBI device description object + */ +static void kill_volumes(struct ubi_device *ubi) +{ + int i; + + for (i = 0; i < ubi->vtbl_slots; i++) + if (ubi->volumes[i]) + ubi_free_volume(ubi, ubi->volumes[i]); +} + +/** + * uif_init - initialize user interfaces for an UBI device. + * @ubi: UBI device description object + * + * This function returns zero in case of success and a negative error code in + * case of failure. + */ +static int uif_init(struct ubi_device *ubi) +{ + int i, err; +#ifdef UBI_LINUX + dev_t dev; +#endif + + sprintf(ubi->ubi_name, UBI_NAME_STR "%d", ubi->ubi_num); + + /* + * Major numbers for the UBI character devices are allocated + * dynamically. Major numbers of volume character devices are + * equivalent to ones of the corresponding UBI character device. Minor + * numbers of UBI character devices are 0, while minor numbers of + * volume character devices start from 1. Thus, we allocate one major + * number and ubi->vtbl_slots + 1 minor numbers. + */ + err = alloc_chrdev_region(&dev, 0, ubi->vtbl_slots + 1, ubi->ubi_name); + if (err) { + ubi_err("cannot register UBI character devices"); + return err; + } + + ubi_assert(MINOR(dev) == 0); + cdev_init(&ubi->cdev, &ubi_cdev_operations); + dbg_msg("%s major is %u", ubi->ubi_name, MAJOR(dev)); +#ifdef UBI_LINUX + ubi->cdev.owner = THIS_MODULE; +#endif + err = ubi_cdev_add(ubi); + if (err) { + ubi_err("cannot add character device"); + goto out_unreg; + } + + err = ubi_sysfs_init(ubi); + if (err) + goto out_sysfs; + + for (i = 0; i < ubi->vtbl_slots; i++) + if (ubi->volumes[i]) { + err = ubi_add_volume(ubi, ubi->volumes[i]); + if (err) { + ubi_err("cannot add volume %d", i); + goto out_volumes; + } + } + + return 0; + +out_volumes: + kill_volumes(ubi); +out_sysfs: + ubi_sysfs_close(ubi); + ubi_cdev_remove(ubi); +out_unreg: + unregister_chrdev_region(ubi->cdev.dev, ubi->vtbl_slots + 1); + ubi_err("cannot initialize UBI %s, error %d", ubi->ubi_name, err); + return err; +} + +/** + * uif_close - close user interfaces for an UBI device. + * @ubi: UBI device description object + */ +static void uif_close(struct ubi_device *ubi) +{ + kill_volumes(ubi); + ubi_sysfs_close(ubi); + ubi_cdev_remove(ubi); + unregister_chrdev_region(ubi->cdev.dev, ubi->vtbl_slots + 1); +} + +/** + * attach_by_scanning - attach an MTD device using scanning method. + * @ubi: UBI device descriptor + * + * This function returns zero in case of success and a negative error code in + * case of failure. + * + * Note, currently this is the only method to attach UBI devices. Hopefully in + * the future we'll have more scalable attaching methods and avoid full media + * scanning. But even in this case scanning will be needed as a fall-back + * attaching method if there are some on-flash table corruptions. + */ +static int attach_by_scanning(struct ubi_device *ubi) +{ + int err; + struct ubi_scan_info *si; + + si = ubi_scan(ubi); + if (IS_ERR(si)) + return PTR_ERR(si); + + ubi->bad_peb_count = si->bad_peb_count; + ubi->good_peb_count = ubi->peb_count - ubi->bad_peb_count; + ubi->max_ec = si->max_ec; + ubi->mean_ec = si->mean_ec; + + err = ubi_read_volume_table(ubi, si); + if (err) + goto out_si; + + err = ubi_wl_init_scan(ubi, si); + if (err) + goto out_vtbl; + + err = ubi_eba_init_scan(ubi, si); + if (err) + goto out_wl; + + ubi_scan_destroy_si(si); + return 0; + +out_wl: + ubi_wl_close(ubi); +out_vtbl: + vfree(ubi->vtbl); +out_si: + ubi_scan_destroy_si(si); + return err; +} + +/** + * io_init - initialize I/O unit for a given UBI device. + * @ubi: UBI device description object + * + * If @ubi->vid_hdr_offset or @ubi->leb_start is zero, default offsets are + * assumed: + * o EC header is always at offset zero - this cannot be changed; + * o VID header starts just after the EC header at the closest address + * aligned to @io->hdrs_min_io_size; + * o data starts just after the VID header at the closest address aligned to + * @io->min_io_size + * + * This function returns zero in case of success and a negative error code in + * case of failure. + */ +static int io_init(struct ubi_device *ubi) +{ + if (ubi->mtd->numeraseregions != 0) { + /* + * Some flashes have several erase regions. Different regions + * may have different eraseblock size and other + * characteristics. It looks like mostly multi-region flashes + * have one "main" region and one or more small regions to + * store boot loader code or boot parameters or whatever. I + * guess we should just pick the largest region. But this is + * not implemented. + */ + ubi_err("multiple regions, not implemented"); + return -EINVAL; + } + + if (ubi->vid_hdr_offset < 0) + return -EINVAL; + + /* + * Note, in this implementation we support MTD devices with 0x7FFFFFFF + * physical eraseblocks maximum. + */ + + ubi->peb_size = ubi->mtd->erasesize; + ubi->peb_count = mtd_div_by_eb(ubi->mtd->size, ubi->mtd); + ubi->flash_size = ubi->mtd->size; + + if (ubi->mtd->block_isbad && ubi->mtd->block_markbad) + ubi->bad_allowed = 1; + + ubi->min_io_size = ubi->mtd->writesize; + ubi->hdrs_min_io_size = ubi->mtd->writesize >> ubi->mtd->subpage_sft; + + /* + * Make sure minimal I/O unit is power of 2. Note, there is no + * fundamental reason for this assumption. It is just an optimization + * which allows us to avoid costly division operations. + */ + if (!is_power_of_2(ubi->min_io_size)) { + ubi_err("min. I/O unit (%d) is not power of 2", + ubi->min_io_size); + return -EINVAL; + } + + ubi_assert(ubi->hdrs_min_io_size > 0); + ubi_assert(ubi->hdrs_min_io_size <= ubi->min_io_size); + ubi_assert(ubi->min_io_size % ubi->hdrs_min_io_size == 0); + + /* Calculate default aligned sizes of EC and VID headers */ + ubi->ec_hdr_alsize = ALIGN(UBI_EC_HDR_SIZE, ubi->hdrs_min_io_size); + ubi->vid_hdr_alsize = ALIGN(UBI_VID_HDR_SIZE, ubi->hdrs_min_io_size); + + dbg_msg("min_io_size %d", ubi->min_io_size); + dbg_msg("hdrs_min_io_size %d", ubi->hdrs_min_io_size); + dbg_msg("ec_hdr_alsize %d", ubi->ec_hdr_alsize); + dbg_msg("vid_hdr_alsize %d", ubi->vid_hdr_alsize); + + if (ubi->vid_hdr_offset == 0) + /* Default offset */ + ubi->vid_hdr_offset = ubi->vid_hdr_aloffset = + ubi->ec_hdr_alsize; + else { + ubi->vid_hdr_aloffset = ubi->vid_hdr_offset & + ~(ubi->hdrs_min_io_size - 1); + ubi->vid_hdr_shift = ubi->vid_hdr_offset - + ubi->vid_hdr_aloffset; + } + + /* Similar for the data offset */ + ubi->leb_start = ubi->vid_hdr_offset + UBI_EC_HDR_SIZE; + ubi->leb_start = ALIGN(ubi->leb_start, ubi->min_io_size); + + dbg_msg("vid_hdr_offset %d", ubi->vid_hdr_offset); + dbg_msg("vid_hdr_aloffset %d", ubi->vid_hdr_aloffset); + dbg_msg("vid_hdr_shift %d", ubi->vid_hdr_shift); + dbg_msg("leb_start %d", ubi->leb_start); + + /* The shift must be aligned to 32-bit boundary */ + if (ubi->vid_hdr_shift % 4) { + ubi_err("unaligned VID header shift %d", + ubi->vid_hdr_shift); + return -EINVAL; + } + + /* Check sanity */ + if (ubi->vid_hdr_offset < UBI_EC_HDR_SIZE || + ubi->leb_start < ubi->vid_hdr_offset + UBI_VID_HDR_SIZE || + ubi->leb_start > ubi->peb_size - UBI_VID_HDR_SIZE || + ubi->leb_start & (ubi->min_io_size - 1)) { + ubi_err("bad VID header (%d) or data offsets (%d)", + ubi->vid_hdr_offset, ubi->leb_start); + return -EINVAL; + } + + /* + * It may happen that EC and VID headers are situated in one minimal + * I/O unit. In this case we can only accept this UBI image in + * read-only mode. + */ + if (ubi->vid_hdr_offset + UBI_VID_HDR_SIZE <= ubi->hdrs_min_io_size) { + ubi_warn("EC and VID headers are in the same minimal I/O unit, " + "switch to read-only mode"); + ubi->ro_mode = 1; + } + + ubi->leb_size = ubi->peb_size - ubi->leb_start; + + if (!(ubi->mtd->flags & MTD_WRITEABLE)) { + ubi_msg("MTD device %d is write-protected, attach in " + "read-only mode", ubi->mtd->index); + ubi->ro_mode = 1; + } + + ubi_msg("physical eraseblock size: %d bytes (%d KiB)", + ubi->peb_size, ubi->peb_size >> 10); + ubi_msg("logical eraseblock size: %d bytes", ubi->leb_size); + ubi_msg("smallest flash I/O unit: %d", ubi->min_io_size); + if (ubi->hdrs_min_io_size != ubi->min_io_size) + ubi_msg("sub-page size: %d", + ubi->hdrs_min_io_size); + ubi_msg("VID header offset: %d (aligned %d)", + ubi->vid_hdr_offset, ubi->vid_hdr_aloffset); + ubi_msg("data offset: %d", ubi->leb_start); + + /* + * Note, ideally, we have to initialize ubi->bad_peb_count here. But + * unfortunately, MTD does not provide this information. We should loop + * over all physical eraseblocks and invoke mtd->block_is_bad() for + * each physical eraseblock. So, we skip ubi->bad_peb_count + * uninitialized and initialize it after scanning. + */ + + return 0; +} + +/** + * autoresize - re-size the volume which has the "auto-resize" flag set. + * @ubi: UBI device description object + * @vol_id: ID of the volume to re-size + * + * This function re-sizes the volume marked by the @UBI_VTBL_AUTORESIZE_FLG in + * the volume table to the largest possible size. See comments in ubi-header.h + * for more description of the flag. Returns zero in case of success and a + * negative error code in case of failure. + */ +static int autoresize(struct ubi_device *ubi, int vol_id) +{ + struct ubi_volume_desc desc; + struct ubi_volume *vol = ubi->volumes[vol_id]; + int err, old_reserved_pebs = vol->reserved_pebs; + + /* + * Clear the auto-resize flag in the volume in-memory copy of the + * volume table, and 'ubi_resize_volume()' will propogate this change + * to the flash. + */ + ubi->vtbl[vol_id].flags &= ~UBI_VTBL_AUTORESIZE_FLG; + + if (ubi->avail_pebs == 0) { + struct ubi_vtbl_record vtbl_rec; + + /* + * No avalilable PEBs to re-size the volume, clear the flag on + * flash and exit. + */ + memcpy(&vtbl_rec, &ubi->vtbl[vol_id], + sizeof(struct ubi_vtbl_record)); + err = ubi_change_vtbl_record(ubi, vol_id, &vtbl_rec); + if (err) + ubi_err("cannot clean auto-resize flag for volume %d", + vol_id); + } else { + desc.vol = vol; + err = ubi_resize_volume(&desc, + old_reserved_pebs + ubi->avail_pebs); + if (err) + ubi_err("cannot auto-resize volume %d", vol_id); + } + + if (err) + return err; + + ubi_msg("volume %d (\"%s\") re-sized from %d to %d LEBs", vol_id, + vol->name, old_reserved_pebs, vol->reserved_pebs); + return 0; +} + +/** + * ubi_attach_mtd_dev - attach an MTD device. + * @mtd_dev: MTD device description object + * @ubi_num: number to assign to the new UBI device + * @vid_hdr_offset: VID header offset + * + * This function attaches MTD device @mtd_dev to UBI and assign @ubi_num number + * to the newly created UBI device, unless @ubi_num is %UBI_DEV_NUM_AUTO, in + * which case this function finds a vacant device nubert and assings it + * automatically. Returns the new UBI device number in case of success and a + * negative error code in case of failure. + * + * Note, the invocations of this function has to be serialized by the + * @ubi_devices_mutex. + */ +int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num, int vid_hdr_offset) +{ + struct ubi_device *ubi; + int i, err; + + /* + * Check if we already have the same MTD device attached. + * + * Note, this function assumes that UBI devices creations and deletions + * are serialized, so it does not take the &ubi_devices_lock. + */ + for (i = 0; i < UBI_MAX_DEVICES; i++) { + ubi = ubi_devices[i]; + if (ubi && mtd == ubi->mtd) { + dbg_err("mtd%d is already attached to ubi%d", + mtd->index, i); + return -EEXIST; + } + } + + /* + * Make sure this MTD device is not emulated on top of an UBI volume + * already. Well, generally this recursion works fine, but there are + * different problems like the UBI module takes a reference to itself + * by attaching (and thus, opening) the emulated MTD device. This + * results in inability to unload the module. And in general it makes + * no sense to attach emulated MTD devices, so we prohibit this. + */ + if (mtd->type == MTD_UBIVOLUME) { + ubi_err("refuse attaching mtd%d - it is already emulated on " + "top of UBI", mtd->index); + return -EINVAL; + } + + if (ubi_num == UBI_DEV_NUM_AUTO) { + /* Search for an empty slot in the @ubi_devices array */ + for (ubi_num = 0; ubi_num < UBI_MAX_DEVICES; ubi_num++) + if (!ubi_devices[ubi_num]) + break; + if (ubi_num == UBI_MAX_DEVICES) { + dbg_err("only %d UBI devices may be created", UBI_MAX_DEVICES); + return -ENFILE; + } + } else { + if (ubi_num >= UBI_MAX_DEVICES) + return -EINVAL; + + /* Make sure ubi_num is not busy */ + if (ubi_devices[ubi_num]) { + dbg_err("ubi%d already exists", ubi_num); + return -EEXIST; + } + } + + ubi = kzalloc(sizeof(struct ubi_device), GFP_KERNEL); + if (!ubi) + return -ENOMEM; + + ubi->mtd = mtd; + ubi->ubi_num = ubi_num; + ubi->vid_hdr_offset = vid_hdr_offset; + ubi->autoresize_vol_id = -1; + + mutex_init(&ubi->buf_mutex); + mutex_init(&ubi->ckvol_mutex); + mutex_init(&ubi->volumes_mutex); + spin_lock_init(&ubi->volumes_lock); + + ubi_msg("attaching mtd%d to ubi%d", mtd->index, ubi_num); + + err = io_init(ubi); + if (err) + goto out_free; + + 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) + goto out_free; + +#ifdef CONFIG_MTD_UBI_DEBUG + mutex_init(&ubi->dbg_buf_mutex); + ubi->dbg_peb_buf = vmalloc(ubi->peb_size); + if (!ubi->dbg_peb_buf) + goto out_free; +#endif + + err = attach_by_scanning(ubi); + if (err) { + dbg_err("failed to attach by scanning, error %d", err); + goto out_free; + } + + if (ubi->autoresize_vol_id != -1) { + err = autoresize(ubi, ubi->autoresize_vol_id); + if (err) + goto out_detach; + } + + err = uif_init(ubi); + if (err) + goto out_detach; + + ubi->bgt_thread = kthread_create(ubi_thread, ubi, ubi->bgt_name); + if (IS_ERR(ubi->bgt_thread)) { + err = PTR_ERR(ubi->bgt_thread); + ubi_err("cannot spawn \"%s\", error %d", ubi->bgt_name, + err); + goto out_uif; + } + + ubi_msg("attached mtd%d to ubi%d", mtd->index, ubi_num); + ubi_msg("MTD device name: \"%s\"", mtd->name); + ubi_msg("MTD device size: %llu MiB", ubi->flash_size >> 20); + ubi_msg("number of good PEBs: %d", ubi->good_peb_count); + ubi_msg("number of bad PEBs: %d", ubi->bad_peb_count); + ubi_msg("max. allowed volumes: %d", ubi->vtbl_slots); + ubi_msg("wear-leveling threshold: %d", CONFIG_MTD_UBI_WL_THRESHOLD); + ubi_msg("number of internal volumes: %d", UBI_INT_VOL_COUNT); + ubi_msg("number of user volumes: %d", + ubi->vol_count - UBI_INT_VOL_COUNT); + ubi_msg("available PEBs: %d", ubi->avail_pebs); + ubi_msg("total number of reserved PEBs: %d", ubi->rsvd_pebs); + ubi_msg("number of PEBs reserved for bad PEB handling: %d", + ubi->beb_rsvd_pebs); + ubi_msg("max/mean erase counter: %d/%d", ubi->max_ec, ubi->mean_ec); + + /* Enable the background thread */ + if (!DBG_DISABLE_BGT) { + ubi->thread_enabled = 1; + wake_up_process(ubi->bgt_thread); + } + + ubi_devices[ubi_num] = ubi; + return ubi_num; + +out_uif: + uif_close(ubi); +out_detach: + ubi_eba_close(ubi); + ubi_wl_close(ubi); + vfree(ubi->vtbl); +out_free: + vfree(ubi->peb_buf1); + vfree(ubi->peb_buf2); +#ifdef CONFIG_MTD_UBI_DEBUG + vfree(ubi->dbg_peb_buf); +#endif + kfree(ubi); + return err; +} + +/** + * ubi_detach_mtd_dev - detach an MTD device. + * @ubi_num: UBI device number to detach from + * @anyway: detach MTD even if device reference count is not zero + * + * This function destroys an UBI device number @ubi_num and detaches the + * underlying MTD device. Returns zero in case of success and %-EBUSY if the + * UBI device is busy and cannot be destroyed, and %-EINVAL if it does not + * exist. + * + * Note, the invocations of this function has to be serialized by the + * @ubi_devices_mutex. + */ +int ubi_detach_mtd_dev(struct mtd_info *mtd, int anyway) +{ + struct ubi_device *ubi; + int ubi_num = 0, i; + + spin_lock(&ubi_devices_lock); + + for (i = 0; i < UBI_MAX_DEVICES; i++) { + ubi = ubi_devices[i]; + if (ubi && mtd == ubi->mtd) { + ubi_num = i; + break; + } + } + + if (!ubi) { + spin_unlock(&ubi_devices_lock); + return -EINVAL; + } + + if (ubi->ref_count) { + if (!anyway) { + spin_unlock(&ubi_devices_lock); + return -EBUSY; + } + /* This may only happen if there is a bug */ + ubi_err("%s reference count %d, destroy anyway", + ubi->ubi_name, ubi->ref_count); + } + ubi_devices[ubi_num] = NULL; + spin_unlock(&ubi_devices_lock); + + ubi_assert(ubi_num == ubi->ubi_num); + dbg_msg("detaching mtd%d from ubi%d", ubi->mtd->index, ubi_num); + + /* + * Before freeing anything, we have to stop the background thread to + * prevent it from doing anything on this device while we are freeing. + */ + if (ubi->bgt_thread) + kthread_stop(ubi->bgt_thread); + + uif_close(ubi); + ubi_eba_close(ubi); + ubi_wl_close(ubi); + vfree(ubi->vtbl); + vfree(ubi->peb_buf1); + vfree(ubi->peb_buf2); +#ifdef CONFIG_MTD_UBI_DEBUG + vfree(ubi->dbg_peb_buf); +#endif + ubi_msg("mtd%d is detached from ubi%d", ubi->mtd->index, ubi->ubi_num); + kfree(ubi); + return 0; +} + +/** + * bytes_str_to_int - convert a string representing number of bytes to an + * integer. + * @str: the string to convert + * + * This function returns positive resulting integer in case of success and a + * negative error code in case of failure. + */ +static int __init bytes_str_to_int(const char *str) +{ + char *endp; + unsigned long result; + + result = simple_strtoul(str, &endp, 0); + if (str == endp || result < 0) { + printk(KERN_ERR "UBI error: incorrect bytes count: \"%s\"\n", + str); + return -EINVAL; + } + + switch (*endp) { + case 'G': + result *= 1024; + case 'M': + result *= 1024; + case 'K': + result *= 1024; + if (endp[1] == 'i' && endp[2] == 'B') + endp += 2; + case '\0': + break; + default: + printk(KERN_ERR "UBI error: incorrect bytes count: \"%s\"\n", + str); + return -EINVAL; + } + + return result; +} + +/** + * ubi_mtd_param_parse - parse the 'mtd=' UBI parameter. + * @val: the parameter value to parse + * @kp: not used + * + * This function returns zero in case of success and a negative error code in + * case of error. + */ +int __init ubi_mtd_param_parse(const char *val, struct kernel_param *kp) +{ + int i, len; + struct mtd_dev_param *p; + char buf[MTD_PARAM_LEN_MAX]; + char *pbuf = &buf[0]; + char *tokens[2] = {NULL, NULL}; + + if (!val) + return -EINVAL; + + if (mtd_devs == UBI_MAX_DEVICES) { + printk(KERN_ERR "UBI error: too many parameters, max. is %d\n", + UBI_MAX_DEVICES); + return -EINVAL; + } + + len = strnlen(val, MTD_PARAM_LEN_MAX); + if (len == MTD_PARAM_LEN_MAX) { + printk(KERN_ERR "UBI error: parameter \"%s\" is too long, " + "max. is %d\n", val, MTD_PARAM_LEN_MAX); + return -EINVAL; + } + + if (len == 0) { + printk(KERN_WARNING "UBI warning: empty 'mtd=' parameter - " + "ignored\n"); + return 0; + } + + strcpy(buf, val); + + /* Get rid of the final newline */ + if (buf[len - 1] == '\n') + buf[len - 1] = '\0'; + + for (i = 0; i < 2; i++) + tokens[i] = strsep(&pbuf, ","); + + if (pbuf) { + printk(KERN_ERR "UBI error: too many arguments at \"%s\"\n", + val); + return -EINVAL; + } + + p = &mtd_dev_param[mtd_devs]; + strcpy(&p->name[0], tokens[0]); + + if (tokens[1]) + p->vid_hdr_offs = bytes_str_to_int(tokens[1]); + + if (p->vid_hdr_offs < 0) + return p->vid_hdr_offs; + + mtd_devs += 1; + return 0; +} + +module_param_call(mtd, ubi_mtd_param_parse, NULL, NULL, 000); +MODULE_PARM_DESC(mtd, "MTD devices to attach. Parameter format: " + "mtd=<name|num>[,<vid_hdr_offs>].\n" + "Multiple \"mtd\" parameters may be specified.\n" + "MTD devices may be specified by their number or name.\n" + "Optional \"vid_hdr_offs\" parameter specifies UBI VID " + "header position and data starting position to be used " + "by UBI.\n" + "Example: mtd=content,1984 mtd=4 - attach MTD device" + "with name \"content\" using VID header offset 1984, and " + "MTD device number 4 with default VID header offset."); + +MODULE_VERSION(__stringify(UBI_VERSION)); +MODULE_DESCRIPTION("UBI - Unsorted Block Images"); +MODULE_AUTHOR("Artem Bityutskiy"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mtd/ubi/cdev.c b/drivers/mtd/ubi/cdev.c new file mode 100644 index 0000000000..30b8308dcc --- /dev/null +++ b/drivers/mtd/ubi/cdev.c @@ -0,0 +1,238 @@ +#include <common.h> +#include <fcntl.h> +#include <fs.h> +#include <ioctl.h> +#include "ubi-barebox.h" +#include "ubi.h" + +struct ubi_volume_cdev_priv { + struct ubi_device *ubi; + struct ubi_volume *vol; + int updating; + unsigned long mode; +}; + +static ssize_t ubi_volume_cdev_read(struct cdev *cdev, void *buf, size_t size, + unsigned long offset, unsigned long flags) +{ + struct ubi_volume_cdev_priv *priv = cdev->priv; + struct ubi_volume *vol = priv->vol; + struct ubi_device *ubi = priv->ubi; + int err, lnum, off, len; + size_t count_save = size; + unsigned long long tmp; + loff_t offp = offset; + int usable_leb_size = vol->usable_leb_size; + + printf("%s: %d @ 0x%08x\n", __func__, size, offset); + + len = size > usable_leb_size ? usable_leb_size : size; + + tmp = offp; + off = do_div(tmp, usable_leb_size); + lnum = tmp; + do { + if (off + len >= usable_leb_size) + len = usable_leb_size - off; + + err = ubi_eba_read_leb(ubi, vol, lnum, buf, off, len, 0); + if (err) { + printf("read err %x\n", err); + break; + } + off += len; + if (off == usable_leb_size) { + lnum += 1; + off -= usable_leb_size; + } + + size -= len; + offp += len; + + buf += len; + len = size > usable_leb_size ? usable_leb_size : size; + } while (size); + + return count_save; +} + +static ssize_t ubi_volume_cdev_write(struct cdev* cdev, const void *buf, + size_t size, unsigned long offset, unsigned long flags) +{ + struct ubi_volume_cdev_priv *priv = cdev->priv; + struct ubi_volume *vol = priv->vol; + struct ubi_device *ubi = priv->ubi; + int err; + + if (!priv->updating) { + err = ubi_start_update(ubi, vol, 16*1024*1024); + if (err < 0) { + printf("Cannot start volume update\n"); + return err; + } + priv->updating = 1; + } + + err = ubi_more_update_data(ubi, vol, buf, size); + if (err < 0) { + printf("Couldnt or partially wrote data \n"); + return err; + } + + return err; +} + +static int ubi_volume_cdev_open(struct cdev *cdev, struct filep *f) +{ + struct ubi_volume_cdev_priv *priv = cdev->priv; + + /* only allow read or write, but not both */ + if ((f->flags & O_ACCMODE) == O_RDWR) + return -EINVAL; + + priv->updating = 0; + priv->mode = f->flags & O_ACCMODE; + + return 0; +} + +static int ubi_volume_cdev_close(struct cdev *cdev, struct filep *f) +{ + struct ubi_volume_cdev_priv *priv = cdev->priv; + struct ubi_volume *vol = priv->vol; + struct ubi_device *ubi = priv->ubi; + int err; + + if (priv->updating) { + err = ubi_finish_update(ubi, vol); + if (err) + return err; + + err = ubi_check_volume(ubi, vol->vol_id); + if (err < 0) { + printf("check failed: %s\n", strerror(err)); + return err; + } + + if (err) { + ubi_warn("volume %d on UBI device %d is corrupted", + vol->vol_id, ubi->ubi_num); + vol->corrupted = 1; + } + + vol->checked = 1; + ubi_gluebi_updated(vol); + } + + return 0; +} + +static off_t ubi_volume_cdev_lseek(struct cdev *cdev, off_t ofs) +{ + struct ubi_volume_cdev_priv *priv = cdev->priv; + + /* We can only update ubi volumes sequentially */ + if (priv->mode == O_WRONLY) + return -EINVAL; + + return ofs; +} + +static struct file_operations ubi_volume_fops = { + .open = ubi_volume_cdev_open, + .close = ubi_volume_cdev_close, + .read = ubi_volume_cdev_read, + .write = ubi_volume_cdev_write, + .lseek = ubi_volume_cdev_lseek, +}; + +int ubi_volume_cdev_add(struct ubi_device *ubi, struct ubi_volume *vol) +{ + struct cdev *cdev = &vol->cdev; + struct ubi_volume_cdev_priv *priv; + int ret; + + priv = xzalloc(sizeof(*priv)); + + priv->vol = vol; + priv->ubi = ubi; + + cdev->ops = &ubi_volume_fops; + cdev->name = asprintf("ubi%d.%s", ubi->ubi_num, vol->name); + cdev->priv = priv; + cdev->size = vol->used_bytes; + printf("registering %s as /dev/%s\n", vol->name, cdev->name); + ret = devfs_create(cdev); + if (ret) { + free(priv); + free(cdev->name); + } + + return 0; +} + +void ubi_volume_cdev_remove(struct ubi_volume *vol) +{ + struct cdev *cdev = &vol->cdev; + struct ubi_volume_cdev_priv *priv = cdev->priv; + + devfs_remove(cdev); + free(cdev->name); + free(priv); +} + +static int ubi_cdev_ioctl(struct cdev *cdev, int cmd, void *buf) +{ + struct ubi_volume_desc *desc; + struct ubi_device *ubi = cdev->priv; + struct ubi_mkvol_req *req = buf; + + switch (cmd) { + case UBI_IOCRMVOL: + desc = ubi_open_volume_nm(ubi->ubi_num, req->name, + UBI_EXCLUSIVE); + if (IS_ERR(desc)) + return PTR_ERR(desc); + ubi_remove_volume(desc); + break; + case UBI_IOCMKVOL: + if (!req->bytes) + req->bytes = ubi->avail_pebs * ubi->leb_size; + return ubi_create_volume(ubi, req); + }; + + return 0; +} + + +static struct file_operations ubi_fops = { + .ioctl = ubi_cdev_ioctl, +}; + +int ubi_cdev_add(struct ubi_device *ubi) +{ + struct cdev *cdev = &ubi->cdev; + int ret; + + cdev->ops = &ubi_fops; + cdev->name = asprintf("ubi%d", ubi->ubi_num); + cdev->priv = ubi; + cdev->size = 0; + + printf("registering /dev/%s\n", cdev->name); + ret = devfs_create(cdev); + if (ret) + free(cdev->name); + + return ret; +} + +void ubi_cdev_remove(struct ubi_device *ubi) +{ + struct cdev *cdev = &ubi->cdev; + + printf("removing %s\n", cdev->name); + + devfs_remove(cdev); + free(cdev->name); +} diff --git a/drivers/mtd/ubi/crc32defs.h b/drivers/mtd/ubi/crc32defs.h new file mode 100644 index 0000000000..f5a5401765 --- /dev/null +++ b/drivers/mtd/ubi/crc32defs.h @@ -0,0 +1,32 @@ +/* + * There are multiple 16-bit CRC polynomials in common use, but this is + * *the* standard CRC-32 polynomial, first popularized by Ethernet. + * x^32+x^26+x^23+x^22+x^16+x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x^1+x^0 + */ +#define CRCPOLY_LE 0xedb88320 +#define CRCPOLY_BE 0x04c11db7 + +/* How many bits at a time to use. Requires a table of 4<<CRC_xx_BITS bytes. */ +/* For less performance-sensitive, use 4 */ +#ifndef CRC_LE_BITS +# define CRC_LE_BITS 8 +#endif +#ifndef CRC_BE_BITS +# define CRC_BE_BITS 8 +#endif + +/* + * Little-endian CRC computation. Used with serial bit streams sent + * lsbit-first. Be sure to use cpu_to_le32() to append the computed CRC. + */ +#if CRC_LE_BITS > 8 || CRC_LE_BITS < 1 || CRC_LE_BITS & CRC_LE_BITS-1 +# error CRC_LE_BITS must be a power of 2 between 1 and 8 +#endif + +/* + * Big-endian CRC computation. Used with serial bit streams sent + * msbit-first. Be sure to use cpu_to_be32() to append the computed CRC. + */ +#if CRC_BE_BITS > 8 || CRC_BE_BITS < 1 || CRC_BE_BITS & CRC_BE_BITS-1 +# error CRC_BE_BITS must be a power of 2 between 1 and 8 +#endif diff --git a/drivers/mtd/ubi/debug.c b/drivers/mtd/ubi/debug.c new file mode 100644 index 0000000000..6e4b0ff23a --- /dev/null +++ b/drivers/mtd/ubi/debug.c @@ -0,0 +1,192 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Artem Bityutskiy (Битюцкий Артём) + */ + +/* + * Here we keep all the UBI debugging stuff which should normally be disabled + * and compiled-out, but it is extremely helpful when hunting bugs or doing big + * changes. + */ +#include "ubi-barebox.h" + +#ifdef CONFIG_MTD_UBI_DEBUG_MSG + +#include "ubi.h" + +/** + * ubi_dbg_dump_ec_hdr - dump an erase counter header. + * @ec_hdr: the erase counter header to dump + */ +void ubi_dbg_dump_ec_hdr(const struct ubi_ec_hdr *ec_hdr) +{ + dbg_msg("erase counter header dump:"); + dbg_msg("magic %#08x", be32_to_cpu(ec_hdr->magic)); + dbg_msg("version %d", (int)ec_hdr->version); + dbg_msg("ec %llu", (long long)be64_to_cpu(ec_hdr->ec)); + dbg_msg("vid_hdr_offset %d", be32_to_cpu(ec_hdr->vid_hdr_offset)); + dbg_msg("data_offset %d", be32_to_cpu(ec_hdr->data_offset)); + dbg_msg("hdr_crc %#08x", be32_to_cpu(ec_hdr->hdr_crc)); + dbg_msg("erase counter header hexdump:"); + print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET, 32, 1, + ec_hdr, UBI_EC_HDR_SIZE, 1); +} + +/** + * ubi_dbg_dump_vid_hdr - dump a volume identifier header. + * @vid_hdr: the volume identifier header to dump + */ +void ubi_dbg_dump_vid_hdr(const struct ubi_vid_hdr *vid_hdr) +{ + dbg_msg("volume identifier header dump:"); + dbg_msg("magic %08x", be32_to_cpu(vid_hdr->magic)); + dbg_msg("version %d", (int)vid_hdr->version); + dbg_msg("vol_type %d", (int)vid_hdr->vol_type); + dbg_msg("copy_flag %d", (int)vid_hdr->copy_flag); + dbg_msg("compat %d", (int)vid_hdr->compat); + dbg_msg("vol_id %d", be32_to_cpu(vid_hdr->vol_id)); + dbg_msg("lnum %d", be32_to_cpu(vid_hdr->lnum)); + dbg_msg("leb_ver %u", be32_to_cpu(vid_hdr->leb_ver)); + dbg_msg("data_size %d", be32_to_cpu(vid_hdr->data_size)); + dbg_msg("used_ebs %d", be32_to_cpu(vid_hdr->used_ebs)); + dbg_msg("data_pad %d", be32_to_cpu(vid_hdr->data_pad)); + dbg_msg("sqnum %llu", + (unsigned long long)be64_to_cpu(vid_hdr->sqnum)); + dbg_msg("hdr_crc %08x", be32_to_cpu(vid_hdr->hdr_crc)); + dbg_msg("volume identifier header hexdump:"); +} + +/** + * ubi_dbg_dump_vol_info- dump volume information. + * @vol: UBI volume description object + */ +void ubi_dbg_dump_vol_info(const struct ubi_volume *vol) +{ + dbg_msg("volume information dump:"); + dbg_msg("vol_id %d", vol->vol_id); + dbg_msg("reserved_pebs %d", vol->reserved_pebs); + dbg_msg("alignment %d", vol->alignment); + dbg_msg("data_pad %d", vol->data_pad); + dbg_msg("vol_type %d", vol->vol_type); + dbg_msg("name_len %d", vol->name_len); + dbg_msg("usable_leb_size %d", vol->usable_leb_size); + dbg_msg("used_ebs %d", vol->used_ebs); + dbg_msg("used_bytes %lld", vol->used_bytes); + dbg_msg("last_eb_bytes %d", vol->last_eb_bytes); + dbg_msg("corrupted %d", vol->corrupted); + dbg_msg("upd_marker %d", vol->upd_marker); + + if (vol->name_len <= UBI_VOL_NAME_MAX && + strnlen(vol->name, vol->name_len + 1) == vol->name_len) { + dbg_msg("name %s", vol->name); + } else { + dbg_msg("the 1st 5 characters of the name: %c%c%c%c%c", + vol->name[0], vol->name[1], vol->name[2], + vol->name[3], vol->name[4]); + } +} + +/** + * ubi_dbg_dump_vtbl_record - dump a &struct ubi_vtbl_record object. + * @r: the object to dump + * @idx: volume table index + */ +void ubi_dbg_dump_vtbl_record(const struct ubi_vtbl_record *r, int idx) +{ + int name_len = be16_to_cpu(r->name_len); + + dbg_msg("volume table record %d dump:", idx); + dbg_msg("reserved_pebs %d", be32_to_cpu(r->reserved_pebs)); + dbg_msg("alignment %d", be32_to_cpu(r->alignment)); + dbg_msg("data_pad %d", be32_to_cpu(r->data_pad)); + dbg_msg("vol_type %d", (int)r->vol_type); + dbg_msg("upd_marker %d", (int)r->upd_marker); + dbg_msg("name_len %d", name_len); + + if (r->name[0] == '\0') { + dbg_msg("name NULL"); + return; + } + + if (name_len <= UBI_VOL_NAME_MAX && + strnlen(&r->name[0], name_len + 1) == name_len) { + dbg_msg("name %s", &r->name[0]); + } else { + dbg_msg("1st 5 characters of the name: %c%c%c%c%c", + r->name[0], r->name[1], r->name[2], r->name[3], + r->name[4]); + } + dbg_msg("crc %#08x", be32_to_cpu(r->crc)); +} + +/** + * ubi_dbg_dump_sv - dump a &struct ubi_scan_volume object. + * @sv: the object to dump + */ +void ubi_dbg_dump_sv(const struct ubi_scan_volume *sv) +{ + dbg_msg("volume scanning information dump:"); + dbg_msg("vol_id %d", sv->vol_id); + dbg_msg("highest_lnum %d", sv->highest_lnum); + dbg_msg("leb_count %d", sv->leb_count); + dbg_msg("compat %d", sv->compat); + dbg_msg("vol_type %d", sv->vol_type); + dbg_msg("used_ebs %d", sv->used_ebs); + dbg_msg("last_data_size %d", sv->last_data_size); + dbg_msg("data_pad %d", sv->data_pad); +} + +/** + * ubi_dbg_dump_seb - dump a &struct ubi_scan_leb object. + * @seb: the object to dump + * @type: object type: 0 - not corrupted, 1 - corrupted + */ +void ubi_dbg_dump_seb(const struct ubi_scan_leb *seb, int type) +{ + dbg_msg("eraseblock scanning information dump:"); + dbg_msg("ec %d", seb->ec); + dbg_msg("pnum %d", seb->pnum); + if (type == 0) { + dbg_msg("lnum %d", seb->lnum); + dbg_msg("scrub %d", seb->scrub); + dbg_msg("sqnum %llu", seb->sqnum); + dbg_msg("leb_ver %u", seb->leb_ver); + } +} + +/** + * ubi_dbg_dump_mkvol_req - dump a &struct ubi_mkvol_req object. + * @req: the object to dump + */ +void ubi_dbg_dump_mkvol_req(const struct ubi_mkvol_req *req) +{ + char nm[17]; + + dbg_msg("volume creation request dump:"); + dbg_msg("vol_id %d", req->vol_id); + dbg_msg("alignment %d", req->alignment); + dbg_msg("bytes %lld", (long long)req->bytes); + dbg_msg("vol_type %d", req->vol_type); + dbg_msg("name_len %d", req->name_len); + + memcpy(nm, req->name, 16); + nm[16] = 0; + dbg_msg("the 1st 16 characters of the name: %s", nm); +} + +#endif /* CONFIG_MTD_UBI_DEBUG_MSG */ diff --git a/drivers/mtd/ubi/debug.h b/drivers/mtd/ubi/debug.h new file mode 100644 index 0000000000..b44380b630 --- /dev/null +++ b/drivers/mtd/ubi/debug.h @@ -0,0 +1,152 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Artem Bityutskiy (Битюцкий Артём) + */ + +#ifndef __UBI_DEBUG_H__ +#define __UBI_DEBUG_H__ + +#ifdef CONFIG_MTD_UBI_DEBUG +#ifdef UBI_LINUX +#include <linux/random.h> +#endif + +#define ubi_assert(expr) BUG_ON(!(expr)) +#define dbg_err(fmt, ...) ubi_err(fmt, ##__VA_ARGS__) +#else +#define ubi_assert(expr) ({}) +#define dbg_err(fmt, ...) ({}) +#endif + +#ifdef CONFIG_MTD_UBI_DEBUG_DISABLE_BGT +#define DBG_DISABLE_BGT 1 +#else +#define DBG_DISABLE_BGT 0 +#endif + +#ifdef CONFIG_MTD_UBI_DEBUG_MSG +/* Generic debugging message */ +#define dbg_msg(fmt, ...) \ + printk(KERN_DEBUG "UBI DBG: %s: " fmt "\n", \ + __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 + +#ifdef CONFIG_MTD_UBI_DEBUG_MSG_IO +/* Messages from the input/output unit */ +#define dbg_io(fmt, ...) dbg_msg(fmt, ##__VA_ARGS__) +#else +#define dbg_io(fmt, ...) ({}) +#endif + +#ifdef CONFIG_MTD_UBI_DEBUG_MSG_BLD +/* Initialization and build messages */ +#define dbg_bld(fmt, ...) dbg_msg(fmt, ##__VA_ARGS__) +#else +#define dbg_bld(fmt, ...) ({}) +#endif + +#ifdef CONFIG_MTD_UBI_DEBUG_EMULATE_BITFLIPS +/** + * ubi_dbg_is_bitflip - if it is time to emulate a bit-flip. + * + * Returns non-zero if a bit-flip should be emulated, otherwise returns zero. + */ +static inline int ubi_dbg_is_bitflip(void) +{ + return !(random32() % 200); +} +#else +#define ubi_dbg_is_bitflip() 0 +#endif + +#ifdef CONFIG_MTD_UBI_DEBUG_EMULATE_WRITE_FAILURES +/** + * ubi_dbg_is_write_failure - if it is time to emulate a write failure. + * + * Returns non-zero if a write failure should be emulated, otherwise returns + * zero. + */ +static inline int ubi_dbg_is_write_failure(void) +{ + return !(random32() % 500); +} +#else +#define ubi_dbg_is_write_failure() 0 +#endif + +#ifdef CONFIG_MTD_UBI_DEBUG_EMULATE_ERASE_FAILURES +/** + * ubi_dbg_is_erase_failure - if its time to emulate an erase failure. + * + * Returns non-zero if an erase failure should be emulated, otherwise returns + * zero. + */ +static inline int ubi_dbg_is_erase_failure(void) +{ + return !(random32() % 400); +} +#else +#define ubi_dbg_is_erase_failure() 0 +#endif + +#endif /* !__UBI_DEBUG_H__ */ diff --git a/drivers/mtd/ubi/eba.c b/drivers/mtd/ubi/eba.c new file mode 100644 index 0000000000..8f2b3b1ddf --- /dev/null +++ b/drivers/mtd/ubi/eba.c @@ -0,0 +1,1256 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Artem Bityutskiy (Битюцкий Артём) + */ + +/* + * The UBI Eraseblock Association (EBA) unit. + * + * This unit 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. + * + * EBA also maintains the global sequence counter which is incremented each + * time a logical eraseblock is mapped to a physical eraseblock and it is + * stored in the volume identifier header. This means that each VID header has + * a unique sequence number. The sequence number is only increased an we assume + * 64 bits is enough to never overflow. + */ + +#ifdef UBI_LINUX +#include <linux/slab.h> +#include <linux/crc32.h> +#include <linux/err.h> +#endif + +#include "ubi-barebox.h" +#include "ubi.h" + +/* Number of physical eraseblocks reserved for atomic LEB change operation */ +#define EBA_RESERVED_PEBS 1 + +/** + * next_sqnum - get next sequence number. + * @ubi: UBI device description object + * + * This function returns next sequence number to use, which is just the current + * global sequence counter value. It also increases the global sequence + * counter. + */ +static unsigned long long next_sqnum(struct ubi_device *ubi) +{ + unsigned long long sqnum; + + spin_lock(&ubi->ltree_lock); + sqnum = ubi->global_sqnum++; + spin_unlock(&ubi->ltree_lock); + + return sqnum; +} + +/** + * ubi_get_compat - get compatibility flags of a volume. + * @ubi: UBI device description object + * @vol_id: volume ID + * + * This function returns compatibility flags for an internal volume. User + * volumes have no compatibility flags, so %0 is returned. + */ +static int ubi_get_compat(const struct ubi_device *ubi, int vol_id) +{ + if (vol_id == UBI_LAYOUT_VOLUME_ID) + return UBI_LAYOUT_VOLUME_COMPAT; + return 0; +} + +/** + * ltree_lookup - look up the lock tree. + * @ubi: UBI device description object + * @vol_id: volume ID + * @lnum: logical eraseblock number + * + * This function returns a pointer to the corresponding &struct ubi_ltree_entry + * object if the logical eraseblock is locked and %NULL if it is not. + * @ubi->ltree_lock has to be locked. + */ +static struct ubi_ltree_entry *ltree_lookup(struct ubi_device *ubi, int vol_id, + int lnum) +{ + struct rb_node *p; + + p = ubi->ltree.rb_node; + while (p) { + struct ubi_ltree_entry *le; + + le = rb_entry(p, struct ubi_ltree_entry, rb); + + if (vol_id < le->vol_id) + p = p->rb_left; + else if (vol_id > le->vol_id) + p = p->rb_right; + else { + if (lnum < le->lnum) + p = p->rb_left; + else if (lnum > le->lnum) + p = p->rb_right; + else + return le; + } + } + + return NULL; +} + +/** + * ltree_add_entry - add new entry to the lock tree. + * @ubi: UBI device description object + * @vol_id: volume ID + * @lnum: logical eraseblock number + * + * This function adds new entry for logical eraseblock (@vol_id, @lnum) to the + * lock tree. If such entry is already there, its usage counter is increased. + * Returns pointer to the lock tree entry or %-ENOMEM if memory allocation + * failed. + */ +static struct ubi_ltree_entry *ltree_add_entry(struct ubi_device *ubi, + int vol_id, int lnum) +{ + struct ubi_ltree_entry *le, *le1, *le_free; + + le = kmalloc(sizeof(struct ubi_ltree_entry), GFP_NOFS); + if (!le) + return ERR_PTR(-ENOMEM); + + le->users = 0; + init_rwsem(&le->mutex); + le->vol_id = vol_id; + le->lnum = lnum; + + spin_lock(&ubi->ltree_lock); + le1 = ltree_lookup(ubi, vol_id, lnum); + + if (le1) { + /* + * This logical eraseblock is already locked. The newly + * allocated lock entry is not needed. + */ + le_free = le; + le = le1; + } else { + struct rb_node **p, *parent = NULL; + + /* + * No lock entry, add the newly allocated one to the + * @ubi->ltree RB-tree. + */ + le_free = NULL; + + p = &ubi->ltree.rb_node; + while (*p) { + parent = *p; + le1 = rb_entry(parent, struct ubi_ltree_entry, rb); + + if (vol_id < le1->vol_id) + p = &(*p)->rb_left; + else if (vol_id > le1->vol_id) + p = &(*p)->rb_right; + else { + ubi_assert(lnum != le1->lnum); + if (lnum < le1->lnum) + p = &(*p)->rb_left; + else + p = &(*p)->rb_right; + } + } + + rb_link_node(&le->rb, parent, p); + rb_insert_color(&le->rb, &ubi->ltree); + } + le->users += 1; + spin_unlock(&ubi->ltree_lock); + + if (le_free) + kfree(le_free); + + return le; +} + +/** + * leb_read_lock - lock logical eraseblock for reading. + * @ubi: UBI device description object + * @vol_id: volume ID + * @lnum: logical eraseblock number + * + * This function locks a logical eraseblock for reading. Returns zero in case + * of success and a negative error code in case of failure. + */ +static int leb_read_lock(struct ubi_device *ubi, int vol_id, int lnum) +{ + struct ubi_ltree_entry *le; + + le = ltree_add_entry(ubi, vol_id, lnum); + if (IS_ERR(le)) + return PTR_ERR(le); + down_read(&le->mutex); + return 0; +} + +/** + * leb_read_unlock - unlock logical eraseblock. + * @ubi: UBI device description object + * @vol_id: volume ID + * @lnum: logical eraseblock number + */ +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); + if (le->users == 0) { + rb_erase(&le->rb, &ubi->ltree); + _free = 1; + } + spin_unlock(&ubi->ltree_lock); + + up_read(&le->mutex); + if (_free) + kfree(le); +} + +/** + * leb_write_lock - lock logical eraseblock for writing. + * @ubi: UBI device description object + * @vol_id: volume ID + * @lnum: logical eraseblock number + * + * This function locks a logical eraseblock for writing. Returns zero in case + * of success and a negative error code in case of failure. + */ +static int leb_write_lock(struct ubi_device *ubi, int vol_id, int lnum) +{ + struct ubi_ltree_entry *le; + + le = ltree_add_entry(ubi, vol_id, lnum); + if (IS_ERR(le)) + return PTR_ERR(le); + down_write(&le->mutex); + return 0; +} + +/** + * leb_write_lock - lock logical eraseblock for writing. + * @ubi: UBI device description object + * @vol_id: volume ID + * @lnum: logical eraseblock number + * + * This function locks a logical eraseblock for writing if there is no + * contention and does nothing if there is contention. Returns %0 in case of + * success, %1 in case of contention, and and a negative error code in case of + * failure. + */ +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); + if (IS_ERR(le)) + return PTR_ERR(le); + if (down_write_trylock(&le->mutex)) + return 0; + + /* Contention, cancel */ + spin_lock(&ubi->ltree_lock); + le->users -= 1; + 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); + + return 1; +} + +/** + * leb_write_unlock - unlock logical eraseblock. + * @ubi: UBI device description object + * @vol_id: volume ID + * @lnum: logical eraseblock number + */ +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); + 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); +} + +/** + * ubi_eba_unmap_leb - un-map logical eraseblock. + * @ubi: UBI device description object + * @vol: volume description object + * @lnum: logical eraseblock number + * + * This function un-maps logical eraseblock @lnum and schedules corresponding + * physical eraseblock for erasure. Returns zero in case of success and a + * negative error code in case of failure. + */ +int ubi_eba_unmap_leb(struct ubi_device *ubi, struct ubi_volume *vol, + int lnum) +{ + int err, pnum, vol_id = vol->vol_id; + + if (ubi->ro_mode) + return -EROFS; + + err = leb_write_lock(ubi, vol_id, lnum); + if (err) + return err; + + pnum = vol->eba_tbl[lnum]; + if (pnum < 0) + /* This logical eraseblock is already unmapped */ + goto out_unlock; + + dbg_eba("erase LEB %d:%d, PEB %d", vol_id, lnum, pnum); + + vol->eba_tbl[lnum] = UBI_LEB_UNMAPPED; + err = ubi_wl_put_peb(ubi, pnum, 0); + +out_unlock: + leb_write_unlock(ubi, vol_id, lnum); + return err; +} + +/** + * ubi_eba_read_leb - read data. + * @ubi: UBI device description object + * @vol: volume description object + * @lnum: logical eraseblock number + * @buf: buffer to store the read data + * @offset: offset from where to read + * @len: how many bytes to read + * @check: data CRC check flag + * + * If the logical eraseblock @lnum is unmapped, @buf is filled with 0xFF + * bytes. The @check flag only makes sense for static volumes and forces + * eraseblock data CRC checking. + * + * In case of success this function returns zero. In case of a static volume, + * if data CRC mismatches - %-EBADMSG is returned. %-EBADMSG may also be + * returned for any volume type if an ECC error was detected by the MTD device + * driver. Other negative error cored may be returned in case of other errors. + */ +int ubi_eba_read_leb(struct ubi_device *ubi, struct ubi_volume *vol, int lnum, + void *buf, int offset, int len, int check) +{ + int err, pnum, scrub = 0, vol_id = vol->vol_id; + struct ubi_vid_hdr *vid_hdr; + uint32_t uninitialized_var(crc); + + err = leb_read_lock(ubi, vol_id, lnum); + if (err) + return err; + + pnum = vol->eba_tbl[lnum]; + if (pnum < 0) { + /* + * The logical eraseblock is not mapped, fill the whole buffer + * with 0xFF bytes. The exception is static volumes for which + * it is an error to read unmapped logical eraseblocks. + */ + dbg_eba("read %d bytes from offset %d of LEB %d:%d (unmapped)", + len, offset, vol_id, lnum); + leb_read_unlock(ubi, vol_id, lnum); + ubi_assert(vol->vol_type != UBI_STATIC_VOLUME); + memset(buf, 0xFF, len); + return 0; + } + + dbg_eba("read %d bytes from offset %d of LEB %d:%d, PEB %d", + len, offset, vol_id, lnum, pnum); + + if (vol->vol_type == UBI_DYNAMIC_VOLUME) + check = 0; + +retry: + if (check) { + vid_hdr = ubi_zalloc_vid_hdr(ubi, GFP_NOFS); + if (!vid_hdr) { + err = -ENOMEM; + goto out_unlock; + } + + err = ubi_io_read_vid_hdr(ubi, pnum, vid_hdr, 1); + if (err && err != UBI_IO_BITFLIPS) { + if (err > 0) { + /* + * The header is either absent or corrupted. + * The former case means there is a bug - + * switch to read-only mode just in case. + * The latter case means a real corruption - we + * 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); + err = -EBADMSG; + } else + ubi_ro_mode(ubi); + } + goto out_free; + } else if (err == UBI_IO_BITFLIPS) + scrub = 1; + + ubi_assert(lnum < be32_to_cpu(vid_hdr->used_ebs)); + ubi_assert(len == be32_to_cpu(vid_hdr->data_size)); + + crc = be32_to_cpu(vid_hdr->data_crc); + ubi_free_vid_hdr(ubi, vid_hdr); + } + + err = ubi_io_read_data(ubi, buf, pnum, offset, len); + if (err) { + if (err == UBI_IO_BITFLIPS) { + scrub = 1; + err = 0; + } else if (err == -EBADMSG) { + if (vol->vol_type == UBI_DYNAMIC_VOLUME) + goto out_unlock; + scrub = 1; + if (!check) { + ubi_msg("force data checking"); + check = 1; + goto retry; + } + } else + goto out_unlock; + } + + if (check) { + uint32_t crc1 = crc32(UBI_CRC32_INIT, buf, len); + if (crc1 != crc) { + ubi_warn("CRC error: calculated %#08x, must be %#08x", + crc1, crc); + err = -EBADMSG; + goto out_unlock; + } + } + + if (scrub) + err = ubi_wl_scrub_peb(ubi, pnum); + + leb_read_unlock(ubi, vol_id, lnum); + return err; + +out_free: + ubi_free_vid_hdr(ubi, vid_hdr); +out_unlock: + leb_read_unlock(ubi, vol_id, lnum); + return err; +} + +/** + * recover_peb - recover from write failure. + * @ubi: UBI device description object + * @pnum: the physical eraseblock to recover + * @vol_id: volume ID + * @lnum: logical eraseblock number + * @buf: data which was not written because of the write failure + * @offset: offset of the failed write + * @len: how many bytes should have been written + * + * This function is called in case of a write failure and moves all good data + * from the potentially bad physical eraseblock to a good physical eraseblock. + * This function also writes the data which was not written due to the failure. + * Returns new physical eraseblock number in case of success, and a negative + * error code in case of failure. + */ +static int recover_peb(struct ubi_device *ubi, int pnum, int vol_id, int lnum, + const void *buf, int offset, int len) +{ + int err, idx = vol_id2idx(ubi, vol_id), new_pnum, data_size, tries = 0; + struct ubi_volume *vol = ubi->volumes[idx]; + struct ubi_vid_hdr *vid_hdr; + + vid_hdr = ubi_zalloc_vid_hdr(ubi, GFP_NOFS); + if (!vid_hdr) { + return -ENOMEM; + } + + mutex_lock(&ubi->buf_mutex); + +retry: + new_pnum = ubi_wl_get_peb(ubi, UBI_UNKNOWN); + if (new_pnum < 0) { + mutex_unlock(&ubi->buf_mutex); + ubi_free_vid_hdr(ubi, vid_hdr); + return new_pnum; + } + + ubi_msg("recover PEB %d, move data to PEB %d", pnum, new_pnum); + + err = ubi_io_read_vid_hdr(ubi, pnum, vid_hdr, 1); + if (err && err != UBI_IO_BITFLIPS) { + if (err > 0) + err = -EIO; + goto out_put; + } + + vid_hdr->sqnum = cpu_to_be64(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); + + /* 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); + if (err && err != UBI_IO_BITFLIPS) + goto out_put; + } + + memcpy(ubi->peb_buf1 + offset, buf, len); + + err = ubi_io_write_data(ubi, ubi->peb_buf1, new_pnum, 0, data_size); + if (err) + goto write_error; + + mutex_unlock(&ubi->buf_mutex); + ubi_free_vid_hdr(ubi, vid_hdr); + + vol->eba_tbl[lnum] = new_pnum; + ubi_wl_put_peb(ubi, pnum, 1); + + ubi_msg("data was successfully recovered"); + return 0; + +out_put: + mutex_unlock(&ubi->buf_mutex); + ubi_wl_put_peb(ubi, new_pnum, 1); + ubi_free_vid_hdr(ubi, vid_hdr); + return err; + +write_error: + /* + * Bad luck? This physical eraseblock is bad too? Crud. Let's try to + * get another one. + */ + ubi_warn("failed to write to PEB %d", new_pnum); + ubi_wl_put_peb(ubi, new_pnum, 1); + if (++tries > UBI_IO_RETRIES) { + mutex_unlock(&ubi->buf_mutex); + ubi_free_vid_hdr(ubi, vid_hdr); + return err; + } + ubi_msg("try again"); + goto retry; +} + +/** + * ubi_eba_write_leb - write data to dynamic volume. + * @ubi: UBI device description object + * @vol: volume description object + * @lnum: logical eraseblock number + * @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 + * of failure. In case of error, it is possible that something was still + * 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) +{ + int err, pnum, tries = 0, vol_id = vol->vol_id; + struct ubi_vid_hdr *vid_hdr; + + if (ubi->ro_mode) + return -EROFS; + + err = leb_write_lock(ubi, vol_id, lnum); + if (err) + return err; + + pnum = vol->eba_tbl[lnum]; + if (pnum >= 0) { + dbg_eba("write %d bytes at offset %d of LEB %d:%d, PEB %d", + len, offset, vol_id, lnum, pnum); + + err = ubi_io_write_data(ubi, buf, pnum, offset, len); + if (err) { + ubi_warn("failed to write data to PEB %d", pnum); + if (err == -EIO && ubi->bad_allowed) + err = recover_peb(ubi, pnum, vol_id, lnum, buf, + offset, len); + if (err) + ubi_ro_mode(ubi); + } + leb_write_unlock(ubi, vol_id, lnum); + return err; + } + + /* + * The logical eraseblock is not mapped. We have to get a free physical + * eraseblock and write the volume identifier header there first. + */ + vid_hdr = ubi_zalloc_vid_hdr(ubi, GFP_NOFS); + if (!vid_hdr) { + leb_write_unlock(ubi, vol_id, lnum); + return -ENOMEM; + } + + vid_hdr->vol_type = UBI_VID_DYNAMIC; + vid_hdr->sqnum = cpu_to_be64(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); + if (pnum < 0) { + ubi_free_vid_hdr(ubi, vid_hdr); + leb_write_unlock(ubi, vol_id, lnum); + return pnum; + } + + dbg_eba("write VID hdr and %d bytes at offset %d of LEB %d:%d, PEB %d", + len, offset, vol_id, lnum, pnum); + + err = ubi_io_write_vid_hdr(ubi, pnum, vid_hdr); + if (err) { + ubi_warn("failed to write VID header to LEB %d:%d, PEB %d", + vol_id, lnum, pnum); + goto write_error; + } + + 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); + goto write_error; + } + } + + vol->eba_tbl[lnum] = pnum; + + leb_write_unlock(ubi, vol_id, lnum); + ubi_free_vid_hdr(ubi, vid_hdr); + return 0; + +write_error: + if (err != -EIO || !ubi->bad_allowed) { + ubi_ro_mode(ubi); + leb_write_unlock(ubi, vol_id, lnum); + ubi_free_vid_hdr(ubi, vid_hdr); + return err; + } + + /* + * Fortunately, this is the first write operation to this physical + * 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); + if (err || ++tries > UBI_IO_RETRIES) { + ubi_ro_mode(ubi); + leb_write_unlock(ubi, vol_id, lnum); + ubi_free_vid_hdr(ubi, vid_hdr); + return err; + } + + vid_hdr->sqnum = cpu_to_be64(next_sqnum(ubi)); + ubi_msg("try another PEB"); + goto retry; +} + +/** + * ubi_eba_write_leb_st - write data to static volume. + * @ubi: UBI device description object + * @vol: volume description object + * @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 + * @vol. The @used_ebs argument should contain total number of logical + * eraseblock in this static volume. + * + * When writing to the last logical eraseblock, the @len argument doesn't have + * to be aligned to the minimal I/O unit size. Instead, it has to be equivalent + * 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 + * 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 err, pnum, tries = 0, data_size = len, vol_id = vol->vol_id; + struct ubi_vid_hdr *vid_hdr; + uint32_t crc; + + if (ubi->ro_mode) + return -EROFS; + + if (lnum == used_ebs - 1) + /* If this is the last LEB @len may be unaligned */ + len = ALIGN(data_size, ubi->min_io_size); + else + ubi_assert(!(len & (ubi->min_io_size - 1))); + + vid_hdr = ubi_zalloc_vid_hdr(ubi, GFP_NOFS); + if (!vid_hdr) + return -ENOMEM; + + err = leb_write_lock(ubi, vol_id, lnum); + if (err) { + ubi_free_vid_hdr(ubi, vid_hdr); + return err; + } + + vid_hdr->sqnum = cpu_to_be64(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); + + crc = crc32(UBI_CRC32_INIT, buf, data_size); + vid_hdr->vol_type = UBI_VID_STATIC; + vid_hdr->data_size = cpu_to_be32(data_size); + vid_hdr->used_ebs = cpu_to_be32(used_ebs); + vid_hdr->data_crc = cpu_to_be32(crc); + +retry: + pnum = ubi_wl_get_peb(ubi, dtype); + if (pnum < 0) { + ubi_free_vid_hdr(ubi, vid_hdr); + leb_write_unlock(ubi, vol_id, lnum); + return pnum; + } + + dbg_eba("write VID hdr and %d bytes at LEB %d:%d, PEB %d, used_ebs %d", + len, vol_id, lnum, pnum, used_ebs); + + err = ubi_io_write_vid_hdr(ubi, pnum, vid_hdr); + if (err) { + ubi_warn("failed to write VID header to LEB %d:%d, PEB %d", + vol_id, lnum, pnum); + goto write_error; + } + + err = ubi_io_write_data(ubi, buf, pnum, 0, len); + if (err) { + ubi_warn("failed to write %d bytes of data to PEB %d", + len, pnum); + goto write_error; + } + + ubi_assert(vol->eba_tbl[lnum] < 0); + vol->eba_tbl[lnum] = pnum; + + leb_write_unlock(ubi, vol_id, lnum); + ubi_free_vid_hdr(ubi, vid_hdr); + return 0; + +write_error: + if (err != -EIO || !ubi->bad_allowed) { + /* + * This flash device does not admit of bad eraseblocks or + * something nasty and unexpected happened. Switch to read-only + * mode just in case. + */ + ubi_ro_mode(ubi); + leb_write_unlock(ubi, vol_id, lnum); + ubi_free_vid_hdr(ubi, vid_hdr); + return err; + } + + err = ubi_wl_put_peb(ubi, pnum, 1); + if (err || ++tries > UBI_IO_RETRIES) { + ubi_ro_mode(ubi); + leb_write_unlock(ubi, vol_id, lnum); + ubi_free_vid_hdr(ubi, vid_hdr); + return err; + } + + vid_hdr->sqnum = cpu_to_be64(next_sqnum(ubi)); + ubi_msg("try another PEB"); + goto retry; +} + +/* + * ubi_eba_atomic_leb_change - change logical eraseblock atomically. + * @ubi: UBI device description object + * @vol: volume description object + * @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 + * data, which has to be aligned. 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. + * + * UBI reserves one LEB for the "atomic LEB change" operation, so only one + * 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 err, pnum, tries = 0, vol_id = vol->vol_id; + struct ubi_vid_hdr *vid_hdr; + uint32_t crc; + + if (ubi->ro_mode) + return -EROFS; + + if (len == 0) { + /* + * Special case when data length is zero. In this case the LEB + * has to be unmapped and mapped somewhere else. + */ + err = ubi_eba_unmap_leb(ubi, vol, lnum); + if (err) + return err; + return ubi_eba_write_leb(ubi, vol, lnum, NULL, 0, 0, dtype); + } + + vid_hdr = ubi_zalloc_vid_hdr(ubi, GFP_NOFS); + if (!vid_hdr) + return -ENOMEM; + + mutex_lock(&ubi->alc_mutex); + err = leb_write_lock(ubi, vol_id, lnum); + if (err) + goto out_mutex; + + vid_hdr->sqnum = cpu_to_be64(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); + + crc = crc32(UBI_CRC32_INIT, buf, len); + vid_hdr->vol_type = UBI_VID_DYNAMIC; + vid_hdr->data_size = cpu_to_be32(len); + vid_hdr->copy_flag = 1; + vid_hdr->data_crc = cpu_to_be32(crc); + +retry: + pnum = ubi_wl_get_peb(ubi, dtype); + if (pnum < 0) { + err = pnum; + goto out_leb_unlock; + } + + dbg_eba("change LEB %d:%d, PEB %d, write VID hdr to PEB %d", + vol_id, lnum, vol->eba_tbl[lnum], pnum); + + err = ubi_io_write_vid_hdr(ubi, pnum, vid_hdr); + if (err) { + ubi_warn("failed to write VID header to LEB %d:%d, PEB %d", + vol_id, lnum, pnum); + goto write_error; + } + + err = ubi_io_write_data(ubi, buf, pnum, 0, len); + if (err) { + ubi_warn("failed to write %d bytes of data to PEB %d", + len, pnum); + goto write_error; + } + + if (vol->eba_tbl[lnum] >= 0) { + err = ubi_wl_put_peb(ubi, vol->eba_tbl[lnum], 1); + if (err) + goto out_leb_unlock; + } + + vol->eba_tbl[lnum] = pnum; + +out_leb_unlock: + leb_write_unlock(ubi, vol_id, lnum); +out_mutex: + mutex_unlock(&ubi->alc_mutex); + ubi_free_vid_hdr(ubi, vid_hdr); + return err; + +write_error: + if (err != -EIO || !ubi->bad_allowed) { + /* + * This flash device does not admit of bad eraseblocks or + * something nasty and unexpected happened. Switch to read-only + * mode just in case. + */ + ubi_ro_mode(ubi); + goto out_leb_unlock; + } + + err = ubi_wl_put_peb(ubi, 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)); + ubi_msg("try another PEB"); + goto retry; +} + +/** + * ubi_eba_copy_leb - copy logical eraseblock. + * @ubi: UBI device description object + * @from: physical eraseblock number from where to copy + * @to: physical eraseblock number where to copy + * @vid_hdr: VID header of the @from physical eraseblock + * + * 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. + */ +int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to, + struct ubi_vid_hdr *vid_hdr) +{ + int err, vol_id, lnum, data_size, aldata_size, idx; + struct ubi_volume *vol; + uint32_t crc; + + 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); + + if (vid_hdr->vol_type == UBI_VID_STATIC) { + data_size = be32_to_cpu(vid_hdr->data_size); + aldata_size = ALIGN(data_size, ubi->min_io_size); + } else + data_size = aldata_size = + ubi->leb_size - be32_to_cpu(vid_hdr->data_pad); + + idx = vol_id2idx(ubi, vol_id); + spin_lock(&ubi->volumes_lock); + /* + * 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 + * be locked in 'ubi_wl_put_peb()' and wait for the WL worker to finish. + */ + vol = ubi->volumes[idx]; + 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; + } + spin_unlock(&ubi->volumes_lock); + + /* + * We do not want anybody to write to this logical eraseblock while we + * are moving it, so lock it. + * + * Note, we are using non-waiting locking here, because we cannot sleep + * on the LEB, since it may cause deadlocks. Indeed, imagine a task is + * unmapping the LEB which is mapped to the PEB we are going to move + * (@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. + */ + err = leb_write_trylock(ubi, vol_id, lnum); + if (err) { + dbg_eba("contention on LEB %d:%d, cancel", vol_id, lnum); + return err; + } + + /* + * The LEB might have been put meanwhile, and the task which put it is + * probably waiting on @ubi->move_mutex. No need to continue the work, + * 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; + 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 + * @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); + if (err && err != UBI_IO_BITFLIPS) { + ubi_warn("error %d while reading data from PEB %d", + err, from); + goto out_unlock_buf; + } + + /* + * Now we have got to calculate how much data we have to 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 + * the first part. We must do this to avoid writing 0xFF bytes as it + * may have some side-effects. And not only this. It is important not + * to include those 0xFFs to CRC because later the they may be filled + * by data. + */ + if (vid_hdr->vol_type == UBI_VID_DYNAMIC) + aldata_size = data_size = + ubi_calc_data_len(ubi, ubi->peb_buf1, data_size); + + cond_resched(); + crc = crc32(UBI_CRC32_INIT, ubi->peb_buf1, data_size); + cond_resched(); + + /* + * It may turn out to me 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. + */ + if (data_size > 0) { + vid_hdr->copy_flag = 1; + 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)); + + err = ubi_io_write_vid_hdr(ubi, to, vid_hdr); + if (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; + goto out_unlock_buf; + } + + if (data_size > 0) { + err = ubi_io_write_data(ubi, ubi->peb_buf1, to, 0, aldata_size); + if (err) + goto out_unlock_buf; + + cond_resched(); + + /* + * 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); + if (err) { + if (err != UBI_IO_BITFLIPS) + ubi_warn("cannot read data back from PEB %d", + to); + else + err = 1; + 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", + to); + goto out_unlock_buf; + } + } + + ubi_assert(vol->eba_tbl[lnum] == from); + vol->eba_tbl[lnum] = to; + +out_unlock_buf: + mutex_unlock(&ubi->buf_mutex); +out_unlock_leb: + leb_write_unlock(ubi, vol_id, lnum); + return err; +} + +/** + * ubi_eba_init_scan - initialize the EBA unit using scanning information. + * @ubi: UBI device description object + * @si: scanning information + * + * This function returns zero in case of success and a negative error code in + * case of failure. + */ +int ubi_eba_init_scan(struct ubi_device *ubi, struct ubi_scan_info *si) +{ + int i, j, err, num_volumes; + struct ubi_scan_volume *sv; + struct ubi_volume *vol; + struct ubi_scan_leb *seb; + struct rb_node *rb; + + dbg_eba("initialize EBA unit"); + + spin_lock_init(&ubi->ltree_lock); + mutex_init(&ubi->alc_mutex); + ubi->ltree = RB_ROOT; + + ubi->global_sqnum = si->max_sqnum + 1; + num_volumes = ubi->vtbl_slots + UBI_INT_VOL_COUNT; + + for (i = 0; i < num_volumes; i++) { + vol = ubi->volumes[i]; + if (!vol) + continue; + + cond_resched(); + + vol->eba_tbl = kmalloc(vol->reserved_pebs * sizeof(int), + GFP_KERNEL); + if (!vol->eba_tbl) { + err = -ENOMEM; + goto out_free; + } + + 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) + continue; + + ubi_rb_for_each_entry(rb, seb, &sv->root, u.rb) { + if (seb->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; + } + } + + if (ubi->avail_pebs < EBA_RESERVED_PEBS) { + ubi_err("no enough physical eraseblocks (%d, need %d)", + ubi->avail_pebs, EBA_RESERVED_PEBS); + err = -ENOSPC; + goto out_free; + } + ubi->avail_pebs -= EBA_RESERVED_PEBS; + ubi->rsvd_pebs += EBA_RESERVED_PEBS; + + if (ubi->bad_allowed) { + ubi_calculate_reserved(ubi); + + 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); + } else + ubi->beb_rsvd_pebs = ubi->beb_rsvd_level; + + ubi->avail_pebs -= ubi->beb_rsvd_pebs; + ubi->rsvd_pebs += ubi->beb_rsvd_pebs; + } + + dbg_eba("EBA unit is initialized"); + return 0; + +out_free: + for (i = 0; i < num_volumes; i++) { + if (!ubi->volumes[i]) + continue; + kfree(ubi->volumes[i]->eba_tbl); + } + 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/io.c b/drivers/mtd/ubi/io.c new file mode 100644 index 0000000000..96d2772338 --- /dev/null +++ b/drivers/mtd/ubi/io.c @@ -0,0 +1,1274 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * Copyright (c) Nokia Corporation, 2006, 2007 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Artem Bityutskiy (Битюцкий Артём) + */ + +/* + * UBI input/output unit. + * + * 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. + * + * 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. + * + * Some words about how the eraseblock headers are stored. + * + * The erase counter header is always stored at offset zero. By default, the + * VID header is stored after the EC header at the closest aligned offset + * (i.e. aligned to the minimum I/O unit size). Data starts next to the VID + * header at the closest aligned offset. But this default layout may be + * changed. For example, for different reasons (e.g., optimization) UBI may be + * asked to put the VID header at further offset, and even at an unaligned + * offset. Of course, if the offset of the VID header is unaligned, UBI adds + * proper padding in front of it. Data offset may also be changed but it has to + * be aligned. + * + * About minimal I/O units. In general, UBI assumes flash device model where + * there is only one minimal I/O unit size. E.g., in case of NOR flash it is 1, + * in case of NAND flash it is a NAND page, etc. This is reported by MTD in the + * @ubi->mtd->writesize field. But as an exception, UBI admits of using another + * (smaller) minimal I/O unit size for EC and VID headers to make it possible + * to do different optimizations. + * + * This is extremely useful in case of NAND flashes which admit of several + * write operations to one NAND page. In this case UBI can fit EC and VID + * headers at one NAND page. Thus, UBI may use "sub-page" size as the minimal + * I/O unit for the headers (the @ubi->hdrs_min_io_size field). But it still + * reports NAND page size (@ubi->min_io_size) as a minimal I/O unit for the UBI + * users. + * + * Example: some Samsung NANDs with 2KiB pages allow 4x 512-byte writes, so + * although the minimal I/O unit is 2K, UBI uses 512 bytes for EC and VID + * headers. + * + * Q: why not just to treat sub-page as a minimal I/O unit of this flash + * 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. + * + * 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, + * the VID header may reside at offset 1984 which is the last 64 bytes of the + * last sub-page (EC header is always at offset zero). This causes some + * difficulties when reading and writing VID headers. + * + * Suppose we have a 64-byte buffer and we read a VID header at it. We change + * the data and want to write this VID header out. As we can only write in + * 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. + */ + +#ifdef UBI_LINUX +#include <linux/crc32.h> +#include <linux/err.h> +#endif + +#include "ubi-barebox.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 + +/** + * ubi_io_read - read data from a physical eraseblock. + * @ubi: UBI device description object + * @buf: buffer where to store the read data + * @pnum: physical eraseblock number to read from + * @offset: offset within the physical eraseblock from where to read + * @len: how many bytes to read + * + * This function reads data from offset @offset of physical eraseblock @pnum + * and stores the read data in the @buf buffer. The following return codes are + * possible: + * + * o %0 if all the requested data were successfully read; + * o %UBI_IO_BITFLIPS if all the requested data were successfully read, but + * correctable bit-flips were detected; this is harmless but may indicate + * that this eraseblock may become bad soon (but do not have to); + * o %-EBADMSG if the MTD subsystem reported about data integrity problems, for + * example it can be an ECC error in case of NAND; this most probably means + * that the data is corrupted; + * o %-EIO if some I/O error occurred; + * o other negative error codes in case of other errors. + */ +int ubi_io_read(const struct ubi_device *ubi, void *buf, int pnum, int offset, + int len) +{ + int err, retries = 0; + size_t read; + loff_t addr; + + dbg_io("read %d bytes from PEB %d:%d", len, pnum, offset); + + ubi_assert(pnum >= 0 && pnum < ubi->peb_count); + ubi_assert(offset >= 0 && offset + len <= ubi->peb_size); + ubi_assert(len > 0); + + err = paranoid_check_not_bad(ubi, pnum); + if (err) + return err > 0 ? -EINVAL : err; + + addr = (loff_t)pnum * ubi->peb_size + offset; +retry: + err = ubi->mtd->read(ubi->mtd, addr, len, &read, buf); + if (err) { + if (err == -EUCLEAN) { + /* + * -EUCLEAN is reported if there was a bit-flip which + * was corrected, so this is harmless. + */ + 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); + 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(); + + /* + * 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) { + ubi_assert(0); + printk("%s[%d] not here\n", __func__, __LINE__); +/* err = -EIO; */ + } + } else { + ubi_assert(len == read); + + if (ubi_dbg_is_bitflip()) { + dbg_msg("bit-flip (emulated)"); + err = UBI_IO_BITFLIPS; + } + } + + return err; +} + +/** + * ubi_io_write - write data to a physical eraseblock. + * @ubi: UBI device description object + * @buf: buffer with the data to write + * @pnum: physical eraseblock number to write to + * @offset: offset within the physical eraseblock where to write + * @len: how many bytes to write + * + * This function writes @len bytes of data from buffer @buf to offset @offset + * of physical eraseblock @pnum. If all the data were successfully written, + * zero is returned. If an error occurred, this function returns a negative + * error code. If %-EIO is returned, the physical eraseblock most probably went + * bad. + * + * Note, in case of an error, it is possible that something was still written + * to the flash media, but may be some garbage. + */ +int ubi_io_write(struct ubi_device *ubi, const void *buf, int pnum, int offset, + int len) +{ + int err; + size_t written; + loff_t addr; + + dbg_io("write %d bytes to PEB %d:%d", len, pnum, offset); + + ubi_assert(pnum >= 0 && pnum < ubi->peb_count); + ubi_assert(offset >= 0 && offset + len <= ubi->peb_size); + ubi_assert(offset % ubi->hdrs_min_io_size == 0); + ubi_assert(len > 0 && len % ubi->hdrs_min_io_size == 0); + + if (ubi->ro_mode) { + ubi_err("read-only mode"); + return -EROFS; + } + + /* The below has to be compiled out if paranoid checks are disabled */ + + err = paranoid_check_not_bad(ubi, pnum); + if (err) + return err > 0 ? -EINVAL : err; + + /* The area we are writing to has to contain all 0xFF bytes */ + err = paranoid_check_all_ff(ubi, pnum, offset, len); + if (err) + return err > 0 ? -EINVAL : 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); + if (err) + return err > 0 ? -EINVAL : err; + err = paranoid_check_peb_vid_hdr(ubi, pnum); + if (err) + return err > 0 ? -EINVAL : 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(); + return -EIO; + } + + addr = (loff_t)pnum * ubi->peb_size + offset; + err = ubi->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(); + } else + ubi_assert(written == len); + + return err; +} + +/** + * erase_callback - MTD erasure call-back. + * @ei: MTD erase information object. + * + * Note, even though MTD erase interface is asynchronous, all the current + * implementations are synchronous anyway. + */ +static void erase_callback(struct erase_info *ei) +{ + wake_up_interruptible((wait_queue_head_t *)ei->priv); +} + +/** + * do_sync_erase - synchronously erase a physical eraseblock. + * @ubi: UBI device description object + * @pnum: the physical eraseblock number to erase + * + * This function synchronously erases physical eraseblock @pnum and returns + * zero in case of success and a negative error code in case of failure. If + * %-EIO is returned, the physical eraseblock most probably went bad. + */ +static int do_sync_erase(struct ubi_device *ubi, int pnum) +{ + int err, retries = 0; + struct erase_info ei; + wait_queue_head_t wq; + + dbg_io("erase PEB %d", pnum); + +retry: + init_waitqueue_head(&wq); + memset(&ei, 0, sizeof(struct erase_info)); + + ei.mtd = ubi->mtd; + ei.addr = (loff_t)pnum * ubi->peb_size; + ei.len = ubi->peb_size; + ei.callback = erase_callback; + ei.priv = (unsigned long)&wq; + + err = ubi->mtd->erase(ubi->mtd, &ei); + if (err) { + if (retries++ < UBI_IO_RETRIES) { + dbg_io("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(); + return err; + } + + err = wait_event_interruptible(wq, ei.state == MTD_ERASE_DONE || + ei.state == MTD_ERASE_FAILED); + if (err) { + ubi_err("interrupted PEB %d erasure", pnum); + return -EINTR; + } + + if (ei.state == MTD_ERASE_FAILED) { + if (retries++ < UBI_IO_RETRIES) { + dbg_io("error while erasing PEB %d, retry", pnum); + yield(); + goto retry; + } + ubi_err("cannot erase PEB %d", pnum); + ubi_dbg_dump_stack(); + return -EIO; + } + + err = paranoid_check_all_ff(ubi, pnum, 0, ubi->peb_size); + if (err) + return err > 0 ? -EINVAL : err; + + if (ubi_dbg_is_erase_failure() && !err) { + dbg_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}; + +/** + * torture_peb - test a supposedly bad physical eraseblock. + * @ubi: UBI device description object + * @pnum: the physical eraseblock number to test + * + * This function returns %-EIO if the physical eraseblock did not pass the + * test, a positive number of erase operations done if the test was + * successfully passed, and other negative error codes in case of other errors. + */ +static int torture_peb(struct ubi_device *ubi, int pnum) +{ + int err, i, patt_count; + + patt_count = ARRAY_SIZE(patterns); + ubi_assert(patt_count > 0); + + mutex_lock(&ubi->buf_mutex); + for (i = 0; i < patt_count; i++) { + err = do_sync_erase(ubi, pnum); + if (err) + goto out; + + /* Make sure the PEB contains only 0xFF bytes */ + err = ubi_io_read(ubi, ubi->peb_buf1, pnum, 0, ubi->peb_size); + if (err) + goto out; + + err = check_pattern(ubi->peb_buf1, 0xFF, ubi->peb_size); + if (err == 0) { + ubi_err("erased PEB %d, but a non-0xFF byte found", + pnum); + err = -EIO; + goto out; + } + + /* 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); + 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); + if (err) + goto out; + + err = check_pattern(ubi->peb_buf1, patterns[i], ubi->peb_size); + if (err == 0) { + ubi_err("pattern %x checking failed for PEB %d", + patterns[i], pnum); + err = -EIO; + goto out; + } + } + + err = patt_count; + +out: + mutex_unlock(&ubi->buf_mutex); + if (err == UBI_IO_BITFLIPS || err == -EBADMSG) { + /* + * If a bit-flip or data integrity error was detected, the test + * has not passed because it happened on a freshly erased + * physical eraseblock which means something is wrong with it. + */ + ubi_err("read problems on freshly erased PEB %d, must be bad", + pnum); + err = -EIO; + } + return err; +} + +/** + * ubi_io_sync_erase - synchronously erase a physical eraseblock. + * @ubi: UBI device description object + * @pnum: physical eraseblock number to erase + * @torture: if this physical eraseblock has to be tortured + * + * 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. + * + * 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 + * codes in case of other errors. Note, %-EIO means that the physical + * eraseblock is bad. + */ +int ubi_io_sync_erase(struct ubi_device *ubi, int pnum, int torture) +{ + int err, ret = 0; + + ubi_assert(pnum >= 0 && pnum < ubi->peb_count); + + err = paranoid_check_not_bad(ubi, pnum); + if (err != 0) + return err > 0 ? -EINVAL : err; + + if (ubi->ro_mode) { + ubi_err("read-only mode"); + return -EROFS; + } + + if (torture) { + ret = torture_peb(ubi, pnum); + if (ret < 0) + return ret; + } + + err = do_sync_erase(ubi, pnum); + if (err) + return err; + + return ret + 1; +} + +/** + * ubi_io_is_bad - check if a physical eraseblock is bad. + * @ubi: UBI device description object + * @pnum: the physical eraseblock number to check + * + * This function returns a positive number if the physical eraseblock is bad, + * zero if not, and a negative error code if an error occurred. + */ +int ubi_io_is_bad(const struct ubi_device *ubi, int pnum) +{ + struct mtd_info *mtd = ubi->mtd; + + ubi_assert(pnum >= 0 && pnum < ubi->peb_count); + + if (ubi->bad_allowed) { + int ret; + + ret = mtd->block_isbad(mtd, (loff_t)pnum * ubi->peb_size); + if (ret < 0) + ubi_err("error %d while checking if PEB %d is bad", + ret, pnum); + else if (ret) + dbg_io("PEB %d is bad", pnum); + return ret; + } + + return 0; +} + +/** + * ubi_io_mark_bad - mark a physical eraseblock as bad. + * @ubi: UBI device description object + * @pnum: the physical eraseblock number to mark + * + * This function returns zero in case of success and a negative error code in + * case of failure. + */ +int ubi_io_mark_bad(const struct ubi_device *ubi, int pnum) +{ + int err; + struct mtd_info *mtd = ubi->mtd; + + ubi_assert(pnum >= 0 && pnum < ubi->peb_count); + + if (ubi->ro_mode) { + ubi_err("read-only mode"); + return -EROFS; + } + + if (!ubi->bad_allowed) + return 0; + + err = mtd->block_markbad(mtd, (loff_t)pnum * ubi->peb_size); + if (err) + ubi_err("cannot mark PEB %d bad, error %d", pnum, err); + return err; +} + +/** + * validate_ec_hdr - validate an erase counter header. + * @ubi: UBI device description object + * @ec_hdr: the erase counter header to check + * + * This function returns zero if the erase counter header is OK, and %1 if + * not. + */ +static int validate_ec_hdr(const struct ubi_device *ubi, + const struct ubi_ec_hdr *ec_hdr) +{ + long long ec; + int vid_hdr_offset, leb_start; + + ec = be64_to_cpu(ec_hdr->ec); + vid_hdr_offset = be32_to_cpu(ec_hdr->vid_hdr_offset); + 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_VERSION, (int)ec_hdr->version); + goto bad; + } + + if (vid_hdr_offset != ubi->vid_hdr_offset) { + ubi_err("bad VID header offset %d, expected %d", + vid_hdr_offset, ubi->vid_hdr_offset); + goto bad; + } + + if (leb_start != ubi->leb_start) { + ubi_err("bad data offset %d, expected %d", + leb_start, ubi->leb_start); + goto bad; + } + + if (ec < 0 || ec > UBI_MAX_ERASECOUNTER) { + ubi_err("bad erase counter %lld", ec); + goto bad; + } + + return 0; + +bad: + ubi_err("bad EC header"); + ubi_dbg_dump_ec_hdr(ec_hdr); + ubi_dbg_dump_stack(); + return 1; +} + +/** + * ubi_io_read_ec_hdr - read and check an erase counter header. + * @ubi: UBI device description object + * @pnum: physical eraseblock to read from + * @ec_hdr: a &struct ubi_ec_hdr object where to store the read erase counter + * header + * @verbose: be verbose if the header is corrupted or was not found + * + * This function reads erase counter header from physical eraseblock @pnum and + * stores it in @ec_hdr. This function also checks CRC checksum of the read + * erase counter header. The following codes may be returned: + * + * 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 (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 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; + 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; + + /* + * 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. + */ + read_err = err; + } + + magic = be32_to_cpu(ec_hdr->magic); + if (magic != UBI_EC_HDR_MAGIC) { + /* + * 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)) { + /* 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; + } + + /* + * This is not a valid erase counter 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_EC_HDR_MAGIC); + ubi_dbg_dump_ec_hdr(ec_hdr); + } + return UBI_IO_BAD_EC_HDR; + } + + 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) { + 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); + } + return UBI_IO_BAD_EC_HDR; + } + + /* And of course validate what has just been read from the media */ + err = validate_ec_hdr(ubi, ec_hdr); + if (err) { + ubi_err("validation failed for PEB %d", pnum); + return -EINVAL; + } + + return read_err ? UBI_IO_BITFLIPS : 0; +} + +/** + * ubi_io_write_ec_hdr - write an erase counter header. + * @ubi: UBI device description object + * @pnum: physical eraseblock to write to + * @ec_hdr: the erase counter header to write + * + * This function writes erase counter header described by @ec_hdr to physical + * eraseblock @pnum. It also fills most fields of @ec_hdr before writing, so + * the caller do not have to fill them. Callers must only fill the @ec_hdr->ec + * field. + * + * This function returns zero in case of success and a negative error code in + * case of failure. If %-EIO is returned, the physical eraseblock most probably + * went bad. + */ +int ubi_io_write_ec_hdr(struct ubi_device *ubi, int pnum, + struct ubi_ec_hdr *ec_hdr) +{ + int err; + uint32_t crc; + + dbg_io("write EC header to PEB %d", pnum); + ubi_assert(pnum >= 0 && pnum < ubi->peb_count); + + ec_hdr->magic = cpu_to_be32(UBI_EC_HDR_MAGIC); + 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); + 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); + if (err) + return -EINVAL; + + err = ubi_io_write(ubi, ec_hdr, pnum, 0, ubi->ec_hdr_alsize); + return err; +} + +/** + * validate_vid_hdr - validate a volume identifier header. + * @ubi: UBI device description object + * @vid_hdr: the volume identifier header to check + * + * This function checks that data stored in the volume identifier header + * @vid_hdr. Returns zero if the VID header is OK and %1 if not. + */ +static int validate_vid_hdr(const struct ubi_device *ubi, + const struct ubi_vid_hdr *vid_hdr) +{ + int vol_type = vid_hdr->vol_type; + int copy_flag = vid_hdr->copy_flag; + int vol_id = be32_to_cpu(vid_hdr->vol_id); + int lnum = be32_to_cpu(vid_hdr->lnum); + int compat = vid_hdr->compat; + int data_size = be32_to_cpu(vid_hdr->data_size); + int used_ebs = be32_to_cpu(vid_hdr->used_ebs); + int data_pad = be32_to_cpu(vid_hdr->data_pad); + int data_crc = be32_to_cpu(vid_hdr->data_crc); + int usable_leb_size = ubi->leb_size - data_pad; + + if (copy_flag != 0 && copy_flag != 1) { + dbg_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"); + goto bad; + } + + if (vol_id >= UBI_MAX_VOLUMES && vol_id < UBI_INTERNAL_VOL_START) { + dbg_err("bad vol_id"); + goto bad; + } + + if (vol_id < UBI_INTERNAL_VOL_START && compat != 0) { + dbg_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"); + goto bad; + } + + if (vol_type != UBI_VID_DYNAMIC && vol_type != UBI_VID_STATIC) { + dbg_err("bad vol_type"); + goto bad; + } + + if (data_pad >= ubi->leb_size / 2) { + dbg_err("bad data_pad"); + goto bad; + } + + if (vol_type == UBI_VID_STATIC) { + /* + * Although from high-level point of view static volumes may + * contain zero bytes of data, but no VID headers can contain + * zero at these fields, because they empty volumes do not have + * mapped logical eraseblocks. + */ + if (used_ebs == 0) { + dbg_err("zero used_ebs"); + goto bad; + } + if (data_size == 0) { + dbg_err("zero data_size"); + goto bad; + } + if (lnum < used_ebs - 1) { + if (data_size != usable_leb_size) { + dbg_err("bad data_size"); + goto bad; + } + } else if (lnum == used_ebs - 1) { + if (data_size == 0) { + dbg_err("bad data_size at last LEB"); + goto bad; + } + } else { + dbg_err("too high lnum"); + goto bad; + } + } else { + if (copy_flag == 0) { + if (data_crc != 0) { + dbg_err("non-zero data CRC"); + goto bad; + } + if (data_size != 0) { + dbg_err("non-zero data_size"); + goto bad; + } + } else { + if (data_size == 0) { + dbg_err("zero data_size of copy"); + goto bad; + } + } + if (used_ebs != 0) { + dbg_err("bad used_ebs"); + goto bad; + } + } + + return 0; + +bad: + ubi_err("bad VID header"); + ubi_dbg_dump_vid_hdr(vid_hdr); + ubi_dbg_dump_stack(); + return 1; +} + +/** + * ubi_io_read_vid_hdr - read and check a volume identifier header. + * @ubi: UBI device description object + * @pnum: physical eraseblock number to read from + * @vid_hdr: &struct ubi_vid_hdr object where to store the read volume + * identifier header + * @verbose: be verbose if the header is corrupted or wasn't found + * + * 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: + * + * 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. + */ +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; + 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, + 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; + } + + 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 (verbose) + ubi_warn("no VID header found at PEB %d, " + "only 0xFF bytes", pnum); + return UBI_IO_PEB_FREE; + } + + /* + * 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); + } + return UBI_IO_BAD_VID_HDR; + } + + crc = crc32(UBI_CRC32_INIT, vid_hdr, UBI_VID_HDR_SIZE_CRC); + hdr_crc = be32_to_cpu(vid_hdr->hdr_crc); + + 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); + } + return UBI_IO_BAD_VID_HDR; + } + + /* 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); + return -EINVAL; + } + + return read_err ? UBI_IO_BITFLIPS : 0; +} + +/** + * ubi_io_write_vid_hdr - write a volume identifier header. + * @ubi: UBI device description object + * @pnum: the physical eraseblock number to write to + * @vid_hdr: the volume identifier header to write + * + * This function writes the volume identifier header described by @vid_hdr to + * physical eraseblock @pnum. This function automatically fills the + * @vid_hdr->magic and the @vid_hdr->version fields, as well as calculates + * header CRC checksum and stores it at vid_hdr->hdr_crc. + * + * This function returns zero in case of success and a negative error code in + * case of failure. If %-EIO is returned, the physical eraseblock probably went + * bad. + */ +int ubi_io_write_vid_hdr(struct ubi_device *ubi, int pnum, + struct ubi_vid_hdr *vid_hdr) +{ + int err; + uint32_t crc; + void *p; + + 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); + if (err) + return err > 0 ? -EINVAL: 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); + if (err) + return -EINVAL; + + p = (char *)vid_hdr - ubi->vid_hdr_shift; + err = ubi_io_write(ubi, p, pnum, ubi->vid_hdr_aloffset, + ubi->vid_hdr_alsize); + return err; +} + +#ifdef CONFIG_MTD_UBI_DEBUG_PARANOID + +/** + * paranoid_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. + */ +static int paranoid_check_not_bad(const struct ubi_device *ubi, int pnum) +{ + int err; + + 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; +} + +/** + * paranoid_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. + */ +static int paranoid_check_ec_hdr(const struct ubi_device *ubi, int pnum, + const struct ubi_ec_hdr *ec_hdr) +{ + int err; + uint32_t magic; + + magic = be32_to_cpu(ec_hdr->magic); + if (magic != UBI_EC_HDR_MAGIC) { + ubi_err("bad magic %#08x, must be %#08x", + magic, UBI_EC_HDR_MAGIC); + goto fail; + } + + err = validate_ec_hdr(ubi, ec_hdr); + if (err) { + ubi_err("paranoid check failed for PEB %d", pnum); + goto fail; + } + + return 0; + +fail: + ubi_dbg_dump_ec_hdr(ec_hdr); + ubi_dbg_dump_stack(); + return 1; +} + +/** + * paranoid_check_peb_ec_hdr - check that the erase counter header of a + * physical eraseblock is in-place and is all right. + * @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. + */ +static int paranoid_check_peb_ec_hdr(const struct ubi_device *ubi, int pnum) +{ + int err; + uint32_t crc, hdr_crc; + struct ubi_ec_hdr *ec_hdr; + + 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) + 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; + goto exit; + } + + err = paranoid_check_ec_hdr(ubi, pnum, ec_hdr); + +exit: + kfree(ec_hdr); + return err; +} + +/** + * paranoid_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. + */ +static int paranoid_check_vid_hdr(const struct ubi_device *ubi, int pnum, + const struct ubi_vid_hdr *vid_hdr) +{ + int err; + uint32_t magic; + + 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", + magic, pnum, UBI_VID_HDR_MAGIC); + goto fail; + } + + err = validate_vid_hdr(ubi, vid_hdr); + if (err) { + ubi_err("paranoid 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; + +} + +/** + * paranoid_check_peb_vid_hdr - check that the volume identifier header of a + * physical eraseblock is in-place and is all right. + * @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. + */ +static int paranoid_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; + + vid_hdr = ubi_zalloc_vid_hdr(ubi, GFP_NOFS); + if (!vid_hdr) + return -ENOMEM; + + 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) + 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; + goto exit; + } + + err = paranoid_check_vid_hdr(ubi, pnum, vid_hdr); + +exit: + ubi_free_vid_hdr(ubi, vid_hdr); + return err; +} + +/** + * paranoid_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. + */ +static int paranoid_check_all_ff(struct ubi_device *ubi, int pnum, int offset, + int len) +{ + size_t read; + int err; + loff_t addr = (loff_t)pnum * ubi->peb_size + offset; + + mutex_lock(&ubi->dbg_buf_mutex); + err = ubi->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); + goto error; + } + + err = check_pattern(ubi->dbg_peb_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); + goto fail; + } + mutex_unlock(&ubi->dbg_buf_mutex); + + 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; +error: + ubi_dbg_dump_stack(); + mutex_unlock(&ubi->dbg_buf_mutex); + return err; +} + +#endif /* CONFIG_MTD_UBI_DEBUG_PARANOID */ diff --git a/drivers/mtd/ubi/kapi.c b/drivers/mtd/ubi/kapi.c new file mode 100644 index 0000000000..d423f87908 --- /dev/null +++ b/drivers/mtd/ubi/kapi.c @@ -0,0 +1,638 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Artem Bityutskiy (Битюцкий Артём) + */ + +/* This file mostly implements UBI kernel API functions */ + +#ifdef UBI_LINUX +#include <linux/module.h> +#include <linux/err.h> +#include <asm/div64.h> +#endif + +#include "ubi-barebox.h" +#include "ubi.h" + +/** + * ubi_get_device_info - get information about UBI device. + * @ubi_num: UBI device number + * @di: the information is stored here + * + * This function returns %0 in case of success, %-EINVAL if the UBI device + * number is invalid, and %-ENODEV if there is no such UBI device. + */ +int ubi_get_device_info(int ubi_num, struct ubi_device_info *di) +{ + struct ubi_device *ubi; + + 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; + + 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 + * @vi: the information is stored here + */ +void ubi_get_volume_info(struct ubi_volume_desc *desc, + struct ubi_volume_info *vi) +{ + struct ubi_volume *vol = desc->vol; + struct ubi_device *ubi = vol->ubi; + + vi->vol_id = vol->vol_id; + vi->ubi_num = ubi->ubi_num; + vi->size = vol->reserved_pebs; + vi->used_bytes = vol->used_bytes; + vi->vol_type = vol->vol_type; + vi->corrupted = vol->corrupted; + vi->upd_marker = vol->upd_marker; + vi->alignment = vol->alignment; + vi->usable_leb_size = vol->usable_leb_size; + vi->name_len = vol->name_len; + vi->name = vol->name; + vi->cdev = &vol->cdev; +} +EXPORT_SYMBOL_GPL(ubi_get_volume_info); + +/** + * ubi_open_volume - open UBI volume. + * @ubi_num: UBI device number + * @vol_id: volume ID + * @mode: open mode + * + * The @mode parameter specifies if the volume should be opened in read-only + * mode, read-write mode, or exclusive mode. The exclusive mode guarantees that + * nobody else will be able to open this volume. UBI allows to have many volume + * readers and one writer at a time. + * + * If a static volume is being opened for the first time since boot, it will be + * checked by this function, which means it will be fully read and the CRC + * checksum of each logical eraseblock will be checked. + * + * This function returns volume descriptor in case of success and a negative + * error code in case of failure. + */ +struct ubi_volume_desc *ubi_open_volume(int ubi_num, int vol_id, int mode) +{ + int err; + struct ubi_volume_desc *desc; + struct ubi_device *ubi; + struct ubi_volume *vol; + + dbg_msg("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); + + if (mode != UBI_READONLY && mode != UBI_READWRITE && + mode != UBI_EXCLUSIVE) + return ERR_PTR(-EINVAL); + + /* + * First of all, we have to get the UBI device to prevent its removal. + */ + ubi = ubi_get_device(ubi_num); + if (!ubi) + return ERR_PTR(-ENODEV); + + if (vol_id < 0 || vol_id >= ubi->vtbl_slots) { + err = -EINVAL; + goto out_put_ubi; + } + + desc = kmalloc(sizeof(struct ubi_volume_desc), GFP_KERNEL); + if (!desc) { + err = -ENOMEM; + goto out_put_ubi; + } + + err = -ENODEV; + if (!try_module_get(THIS_MODULE)) + goto out_free; + + spin_lock(&ubi->volumes_lock); + vol = ubi->volumes[vol_id]; + if (!vol) + goto out_unlock; + + err = -EBUSY; + switch (mode) { + case UBI_READONLY: + if (vol->exclusive) + goto out_unlock; + vol->readers += 1; + break; + + case UBI_READWRITE: + if (vol->exclusive || vol->writers > 0) + goto out_unlock; + vol->writers += 1; + break; + + case UBI_EXCLUSIVE: + if (vol->exclusive || vol->writers || vol->readers) + goto out_unlock; + vol->exclusive = 1; + break; + } + get_device(&vol->dev); + vol->ref_count += 1; + spin_unlock(&ubi->volumes_lock); + + desc->vol = vol; + desc->mode = mode; + + mutex_lock(&ubi->ckvol_mutex); + if (!vol->checked) { + /* This is the first open - check the volume */ + err = ubi_check_volume(ubi, vol_id); + if (err < 0) { + mutex_unlock(&ubi->ckvol_mutex); + ubi_close_volume(desc); + return ERR_PTR(err); + } + if (err == 1) { + ubi_warn("volume %d on UBI device %d is corrupted", + vol_id, ubi->ubi_num); + vol->corrupted = 1; + } + vol->checked = 1; + } + mutex_unlock(&ubi->ckvol_mutex); + + return desc; + +out_unlock: + spin_unlock(&ubi->volumes_lock); + module_put(THIS_MODULE); +out_free: + kfree(desc); +out_put_ubi: + ubi_put_device(ubi); + return ERR_PTR(err); +} +EXPORT_SYMBOL_GPL(ubi_open_volume); + +/** + * ubi_open_volume_nm - open UBI volume by name. + * @ubi_num: UBI device number + * @name: volume name + * @mode: open mode + * + * This function is similar to 'ubi_open_volume()', but opens a volume by name. + */ +struct ubi_volume_desc *ubi_open_volume_nm(int ubi_num, const char *name, + int mode) +{ + int i, vol_id = -1, len; + struct ubi_device *ubi; + struct ubi_volume_desc *ret; + + dbg_msg("open volume %s, mode %d", name, mode); + + if (!name) + return ERR_PTR(-EINVAL); + + len = strnlen(name, UBI_VOL_NAME_MAX + 1); + if (len > UBI_VOL_NAME_MAX) + return ERR_PTR(-EINVAL); + + if (ubi_num < 0 || ubi_num >= UBI_MAX_DEVICES) + return ERR_PTR(-EINVAL); + + ubi = ubi_get_device(ubi_num); + if (!ubi) + return ERR_PTR(-ENODEV); + + spin_lock(&ubi->volumes_lock); + /* Walk all volumes of this UBI device */ + for (i = 0; i < ubi->vtbl_slots; i++) { + struct ubi_volume *vol = ubi->volumes[i]; + + if (vol && len == vol->name_len && !strcmp(name, vol->name)) { + vol_id = i; + break; + } + } + spin_unlock(&ubi->volumes_lock); + + if (vol_id >= 0) + ret = ubi_open_volume(ubi_num, vol_id, mode); + else + ret = ERR_PTR(-ENODEV); + + /* + * We should put the UBI device even in case of success, because + * 'ubi_open_volume()' took a reference as well. + */ + ubi_put_device(ubi); + return ret; +} +EXPORT_SYMBOL_GPL(ubi_open_volume_nm); + +/** + * ubi_close_volume - close UBI volume. + * @desc: volume descriptor + */ +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); + + spin_lock(&ubi->volumes_lock); + switch (desc->mode) { + case UBI_READONLY: + vol->readers -= 1; + break; + case UBI_READWRITE: + vol->writers -= 1; + break; + case UBI_EXCLUSIVE: + vol->exclusive = 0; + } + vol->ref_count -= 1; + spin_unlock(&ubi->volumes_lock); + + kfree(desc); + put_device(&vol->dev); + ubi_put_device(ubi); + module_put(THIS_MODULE); +} +EXPORT_SYMBOL_GPL(ubi_close_volume); + +/** + * ubi_leb_read - read data. + * @desc: volume descriptor + * @lnum: logical eraseblock number to read from + * @buf: buffer where to store the read data + * @offset: offset within the logical eraseblock to read from + * @len: how many bytes to read + * @check: whether UBI has to check the read data's CRC or not. + * + * This function reads data from offset @offset of logical eraseblock @lnum and + * stores the data at @buf. When reading from static volumes, @check specifies + * whether the data has to be checked or not. If yes, the whole logical + * eraseblock will be read and its CRC checksum will be checked (i.e., the CRC + * checksum is per-eraseblock). So checking may substantially slow down the + * read speed. The @check argument is ignored for dynamic volumes. + * + * In case of success, this function returns zero. In case of failure, this + * function returns a negative error code. + * + * %-EBADMSG error code is returned: + * o for both static and dynamic volumes if MTD driver has detected a data + * integrity problem (unrecoverable ECC checksum mismatch in case of NAND); + * o for static volumes in case of data CRC mismatch. + * + * If the volume is damaged because of an interrupted update this function just + * returns immediately with %-EBADF error code. + */ +int ubi_leb_read(struct ubi_volume_desc *desc, int lnum, char *buf, int offset, + int len, int check) +{ + struct ubi_volume *vol = desc->vol; + 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); + + if (vol_id < 0 || vol_id >= ubi->vtbl_slots || lnum < 0 || + lnum >= vol->used_ebs || offset < 0 || len < 0 || + offset + len > vol->usable_leb_size) + return -EINVAL; + + if (vol->vol_type == UBI_STATIC_VOLUME) { + if (vol->used_ebs == 0) + /* Empty static UBI volume */ + return 0; + if (lnum == vol->used_ebs - 1 && + offset + len > vol->last_eb_bytes) + return -EINVAL; + } + + if (vol->upd_marker) + return -EBADF; + if (len == 0) + return 0; + + err = ubi_eba_read_leb(ubi, vol, lnum, buf, offset, len, check); + if (err && err == -EBADMSG && vol->vol_type == UBI_STATIC_VOLUME) { + ubi_warn("mark volume %d as corrupted", vol_id); + vol->corrupted = 1; + } + + return err; +} +EXPORT_SYMBOL_GPL(ubi_leb_read); + +/** + * ubi_leb_write - write data. + * @desc: volume descriptor + * @lnum: logical eraseblock number to write to + * @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. + * + * This function takes care of physical eraseblock write failures. If write to + * the physical eraseblock write operation fails, the logical eraseblock is + * re-mapped to another physical eraseblock, the data is recovered, and the + * write finishes. UBI has a pool of reserved physical eraseblocks for this. + * + * If all the data were successfully written, zero is returned. If an error + * occurred and UBI has not been able to recover from it, this function returns + * a negative error code. Note, in case of an error, it is possible that + * something was still written to the flash media, but that may be some + * garbage. + * + * If the volume is damaged because of an interrupted update this function just + * 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) +{ + 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); + + if (vol_id < 0 || vol_id >= ubi->vtbl_slots) + return -EINVAL; + + if (desc->mode == UBI_READONLY || vol->vol_type == UBI_STATIC_VOLUME) + return -EROFS; + + if (lnum < 0 || lnum >= vol->reserved_pebs || offset < 0 || len < 0 || + offset + len > vol->usable_leb_size || + 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); +} +EXPORT_SYMBOL_GPL(ubi_leb_write); + +/* + * ubi_leb_change - change logical eraseblock atomically. + * @desc: volume descriptor + * @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 + * 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) +{ + 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); + + if (vol_id < 0 || vol_id >= ubi->vtbl_slots) + return -EINVAL; + + if (desc->mode == UBI_READONLY || vol->vol_type == UBI_STATIC_VOLUME) + return -EROFS; + + if (lnum < 0 || lnum >= vol->reserved_pebs || len < 0 || + 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); +} +EXPORT_SYMBOL_GPL(ubi_leb_change); + +/** + * ubi_leb_erase - erase logical eraseblock. + * @desc: volume descriptor + * @lnum: logical eraseblock number + * + * This function un-maps logical eraseblock @lnum and synchronously erases the + * correspondent physical eraseblock. Returns zero in case of success and a + * negative error code in case of failure. + * + * If the volume is damaged because of an interrupted update this function just + * returns immediately with %-EBADF code. + */ +int ubi_leb_erase(struct ubi_volume_desc *desc, int lnum) +{ + struct ubi_volume *vol = desc->vol; + struct ubi_device *ubi = vol->ubi; + int err; + + dbg_msg("erase LEB %d:%d", vol->vol_id, lnum); + + if (desc->mode == UBI_READONLY || vol->vol_type == UBI_STATIC_VOLUME) + return -EROFS; + + if (lnum < 0 || lnum >= vol->reserved_pebs) + return -EINVAL; + + if (vol->upd_marker) + return -EBADF; + + err = ubi_eba_unmap_leb(ubi, vol, lnum); + if (err) + return err; + + return ubi_wl_flush(ubi); +} +EXPORT_SYMBOL_GPL(ubi_leb_erase); + +/** + * ubi_leb_unmap - un-map logical eraseblock. + * @desc: volume descriptor + * @lnum: logical eraseblock number + * + * 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 + * erase operation. + * + * Unlike erase, the un-map operation does not guarantee that the logical + * eraseblock will contain all 0xFF bytes when UBI is initialized again. For + * example, if several logical eraseblocks are un-mapped, and an unclean reboot + * happens after this, the logical eraseblocks will not necessarily be + * un-mapped again when this MTD device is attached. They may actually be + * mapped to the same physical eraseblocks again. So, this function has to be + * used with care. + * + * In other words, when un-mapping a logical eraseblock, UBI does not store + * any information about this on the flash media, it just marks the logical + * eraseblock as "un-mapped" in RAM. If UBI is detached before the physical + * eraseblock is physically erased, it will be mapped again to the same logical + * eraseblock when the MTD device is attached again. + * + * 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 + * 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 + * then written to, it will contain the last written data. + * + * This function returns zero in case of success and a negative error code in + * case of failure. If the volume is damaged because of an interrupted update + * this function just returns immediately with %-EBADF code. + */ +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); + + if (desc->mode == UBI_READONLY || vol->vol_type == UBI_STATIC_VOLUME) + return -EROFS; + + if (lnum < 0 || lnum >= vol->reserved_pebs) + return -EINVAL; + + if (vol->upd_marker) + return -EBADF; + + return ubi_eba_unmap_leb(ubi, vol, lnum); +} +EXPORT_SYMBOL_GPL(ubi_leb_unmap); + +/** + * ubi_leb_map - map logical erasblock 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 + * 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. + * + * This function returns zero in case of success, %-EBADF if the volume is + * damaged because of an interrupted update, %-EBADMSG if the logical + * 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) +{ + struct ubi_volume *vol = desc->vol; + struct ubi_device *ubi = vol->ubi; + + dbg_msg("unmap LEB %d:%d", vol->vol_id, lnum); + + if (desc->mode == UBI_READONLY || vol->vol_type == UBI_STATIC_VOLUME) + return -EROFS; + + 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); +} +EXPORT_SYMBOL_GPL(ubi_leb_map); + +/** + * ubi_is_mapped - check if logical eraseblock is mapped. + * @desc: volume descriptor + * @lnum: logical eraseblock number + * + * This function checks if logical eraseblock @lnum is mapped to a physical + * eraseblock. If a logical eraseblock is un-mapped, this does not necessarily + * mean it will still be un-mapped after the UBI device is re-attached. The + * logical eraseblock may become mapped to the physical eraseblock it was last + * mapped to. + * + * This function returns %1 if the LEB is mapped, %0 if not, and a negative + * error code in case of failure. If the volume is damaged because of an + * interrupted update this function just returns immediately with %-EBADF error + * code. + */ +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); + + if (lnum < 0 || lnum >= vol->reserved_pebs) + return -EINVAL; + + if (vol->upd_marker) + return -EBADF; + + return vol->eba_tbl[lnum] >= 0; +} +EXPORT_SYMBOL_GPL(ubi_is_mapped); diff --git a/drivers/mtd/ubi/misc.c b/drivers/mtd/ubi/misc.c new file mode 100644 index 0000000000..298925d39a --- /dev/null +++ b/drivers/mtd/ubi/misc.c @@ -0,0 +1,106 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Artem Bityutskiy (Битюцкий Артём) + */ + +/* Here we keep miscellaneous functions which are used all over the UBI code */ + +#include "ubi-barebox.h" +#include "ubi.h" + +/** + * calc_data_len - calculate how much real data is stored in a buffer. + * @ubi: UBI device description object + * @buf: a buffer with the contents of the physical eraseblock + * @length: the buffer length + * + * This function calculates how much "real data" is stored in @buf and returnes + * the length. Continuous 0xFF bytes at the end of the buffer are not + * considered as "real data". + */ +int ubi_calc_data_len(const struct ubi_device *ubi, const void *buf, + int length) +{ + int i; + + ubi_assert(!(length & (ubi->min_io_size - 1))); + + for (i = length - 1; i >= 0; i--) + if (((const uint8_t *)buf)[i] != 0xFF) + break; + + /* The resulting length must be aligned to the minimum flash I/O size */ + length = ALIGN(i + 1, ubi->min_io_size); + return length; +} + +/** + * ubi_check_volume - check the contents of a static volume. + * @ubi: UBI device description object + * @vol_id: ID of the volume to check + * + * This function checks if static volume @vol_id is corrupted by fully reading + * it and checking data CRC. This function returns %0 if the volume is not + * corrupted, %1 if it is corrupted and a negative error code in case of + * failure. Dynamic volumes are not checked and zero is returned immediately. + */ +int ubi_check_volume(struct ubi_device *ubi, int vol_id) +{ + void *buf; + int err = 0, i; + struct ubi_volume *vol = ubi->volumes[vol_id]; + + if (vol->vol_type != UBI_STATIC_VOLUME) + return 0; + + buf = vmalloc(vol->usable_leb_size); + if (!buf) + return -ENOMEM; + + for (i = 0; i < vol->used_ebs; i++) { + int size; + + if (i == vol->used_ebs - 1) + size = vol->last_eb_bytes; + else + size = vol->usable_leb_size; + + err = ubi_eba_read_leb(ubi, vol, i, buf, 0, size, 1); + if (err) { + if (err == -EBADMSG) + err = 1; + break; + } + } + + vfree(buf); + return err; +} + +/** + * ubi_calculate_rsvd_pool - 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; +} diff --git a/drivers/mtd/ubi/scan.c b/drivers/mtd/ubi/scan.c new file mode 100644 index 0000000000..3e7fc35bae --- /dev/null +++ b/drivers/mtd/ubi/scan.c @@ -0,0 +1,1362 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Artem Bityutskiy (Битюцкий Артём) + */ + +/* + * 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-barebox.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) + printf("err: %d\n", err); + 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 new file mode 100644 index 0000000000..966b9b682a --- /dev/null +++ b/drivers/mtd/ubi/scan.h @@ -0,0 +1,165 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Artem Bityutskiy (Битюцкий Артём) + */ + +#ifndef __UBI_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-barebox.h b/drivers/mtd/ubi/ubi-barebox.h new file mode 100644 index 0000000000..84c589f5d2 --- /dev/null +++ b/drivers/mtd/ubi/ubi-barebox.h @@ -0,0 +1,191 @@ +/* + * Header file for UBI support for U-Boot + * + * Adaptation from kernel to U-Boot + * + * Copyright (C) 2005-2007 Samsung Electronics + * Kyungmin Park <kyungmin.park@samsung.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __UBOOT_UBI_H +#define __UBOOT_UBI_H + +#include <common.h> +#include <malloc.h> +#include <asm-generic/div64.h> +#include <errno.h> +#include <linux/err.h> +#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> + +#define crc32(seed, data, length) crc32_no_comp(seed, (unsigned char const *)data, length) + +#define DPRINTK(format, args...) \ +do { \ + printf("%s[%d]: " format "\n", __func__, __LINE__, ##args); \ +} while (0) + +/* configurable */ +#define CONFIG_MTD_UBI_WL_THRESHOLD 4096 +#define CONFIG_MTD_UBI_BEB_RESERVE 1 +#define UBI_IO_DEBUG 0 + +/* debug options (Linux: drivers/mtd/ubi/Kconfig.debug) */ +#undef CONFIG_MTD_UBI_DEBUG +#undef CONFIG_MTD_UBI_DEBUG_PARANOID +#undef CONFIG_MTD_UBI_DEBUG_MSG +#undef CONFIG_MTD_UBI_DEBUG_MSG_EBA +#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 + +/* 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 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 + +/* 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_GPL(...) +#define module_param_call(...) +#define MODULE_PARM_DESC(...) +#define MODULE_VERSION(...) + +#ifndef __UBIFS_H__ +#include "ubi.h" +#endif + +/* functions */ +extern int ubi_mtd_param_parse(const char *val, struct kernel_param *kp); +extern int ubi_init(void); +extern void ubi_exit(void); + +extern struct ubi_device *ubi_devices[]; + +#endif diff --git a/drivers/mtd/ubi/ubi-media.h b/drivers/mtd/ubi/ubi-media.h new file mode 100644 index 0000000000..c3185d9fd0 --- /dev/null +++ b/drivers/mtd/ubi/ubi-media.h @@ -0,0 +1,372 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: Artem Bityutskiy (Битюцкий Артём) + * Thomas Gleixner + * Frank Haverkamp + * Oliver Lohmann + * Andreas Arnez + */ + +/* + * This file defines the layout of UBI headers and all the other UBI on-flash + * data structures. + */ + +#ifndef __UBI_MEDIA_H__ +#define __UBI_MEDIA_H__ + +#include <asm/byteorder.h> + +/* The version of UBI images supported by this implementation */ +#define UBI_VERSION 1 + +/* The highest erase counter value supported by this implementation */ +#define UBI_MAX_ERASECOUNTER 0x7FFFFFFF + +/* The initial CRC32 value used when calculating CRC checksums */ +#define UBI_CRC32_INIT 0xFFFFFFFFU + +/* Erase counter header magic number (ASCII "UBI#") */ +#define UBI_EC_HDR_MAGIC 0x55424923 +/* Volume identifier header magic number (ASCII "UBI!") */ +#define UBI_VID_HDR_MAGIC 0x55424921 + +/* + * Volume type constants used in the volume identifier header. + * + * @UBI_VID_DYNAMIC: dynamic volume + * @UBI_VID_STATIC: static volume + */ +enum { + UBI_VID_DYNAMIC = 1, + UBI_VID_STATIC = 2 +}; + +/* + * Volume flags used in the volume table record. + * + * @UBI_VTBL_AUTORESIZE_FLG: auto-resize this volume + * + * %UBI_VTBL_AUTORESIZE_FLG flag can be set only for one volume in the volume + * table. UBI automatically re-sizes the volume which has this flag and makes + * the volume to be of largest possible size. This means that if after the + * initialization UBI finds out that there are available physical eraseblocks + * present on the device, it automatically appends all of them to the volume + * (the physical eraseblocks reserved for bad eraseblocks handling and other + * reserved physical eraseblocks are not taken). So, if there is a volume with + * the %UBI_VTBL_AUTORESIZE_FLG flag set, the amount of available logical + * eraseblocks will be zero after UBI is loaded, because all of them will be + * reserved for this volume. Note, the %UBI_VTBL_AUTORESIZE_FLG bit is cleared + * after the volume had been initialized. + * + * The auto-resize feature is useful for device production purposes. For + * example, different NAND flash chips may have different amount of initial bad + * eraseblocks, depending of particular chip instance. Manufacturers of NAND + * chips usually guarantee that the amount of initial bad eraseblocks does not + * exceed certain percent, e.g. 2%. When one creates an UBI image which will be + * flashed to the end devices in production, he does not know the exact amount + * of good physical eraseblocks the NAND chip on the device will have, but this + * number is required to calculate the volume sized and put them to the volume + * table of the UBI image. In this case, one of the volumes (e.g., the one + * which will store the root file system) is marked as "auto-resizable", and + * UBI will adjust its size on the first boot if needed. + * + * Note, first UBI reserves some amount of physical eraseblocks for bad + * eraseblock handling, and then re-sizes the volume, not vice-versa. This + * means that the pool of reserved physical eraseblocks will always be present. + */ +enum { + UBI_VTBL_AUTORESIZE_FLG = 0x01, +}; + +/* + * Compatibility constants used by internal volumes. + * + * @UBI_COMPAT_DELETE: delete this internal volume before anything is written + * 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 + * @UBI_COMPAT_REJECT: reject this UBI image + */ +enum { + UBI_COMPAT_DELETE = 1, + UBI_COMPAT_RO = 2, + UBI_COMPAT_PRESERVE = 4, + UBI_COMPAT_REJECT = 5 +}; + +/* Sizes of UBI headers */ +#define UBI_EC_HDR_SIZE sizeof(struct ubi_ec_hdr) +#define UBI_VID_HDR_SIZE sizeof(struct ubi_vid_hdr) + +/* Sizes of UBI headers without the ending CRC */ +#define UBI_EC_HDR_SIZE_CRC (UBI_EC_HDR_SIZE - sizeof(__be32)) +#define UBI_VID_HDR_SIZE_CRC (UBI_VID_HDR_SIZE - sizeof(__be32)) + +/** + * 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 + * @padding1: reserved for future, zeroes + * @ec: the erase counter + * @vid_hdr_offset: where the VID header starts + * @data_offset: where the user data start + * @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 + * 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. + * + * The @vid_hdr_offset and @data_offset fields contain the offset of the the + * 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. + */ +struct ubi_ec_hdr { + __be32 magic; + __u8 version; + __u8 padding1[3]; + __be64 ec; /* Warning: the current limit is 31-bit anyway! */ + __be32 vid_hdr_offset; + __be32 data_offset; + __u8 padding2[36]; + __be32 hdr_crc; +} __attribute__ ((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) + * @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) + * @compat: compatibility of this volume (%0, %UBI_COMPAT_DELETE, + * %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) + * @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 + * @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 + * @hdr_crc: volume identifier header CRC checksum + * + * The @sqnum is the value of the global sequence counter at the time when this + * VID header was created. The global sequence counter is incremented each time + * UBI writes a new VID header to the flash, i.e. when it maps a logical + * eraseblock to a new physical eraseblock. The global sequence counter is an + * unsigned 64-bit integer and we assume it never overflows. The @sqnum + * (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 + * 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. + * + * 1. Because UBI may erase physical eraseblocks asynchronously, the following + * situation is possible: L is asynchronously erased, so P is scheduled for + * erasure, then L is written to,i.e. mapped to another physical eraseblock P1, + * so P1 is written to, then an unclean reboot happens. Result - there are 2 + * physical eraseblocks P and P1 corresponding to the same logical eraseblock + * L. But P1 has greater sequence number, so UBI picks P1 when it attaches the + * flash. + * + * 2. From time to time UBI moves logical eraseblocks to other physical + * eraseblocks for wear-leveling reasons. If, for example, UBI moves L from P + * to P1, and an unclean reboot happens before P is physically erased, there + * are two physical eraseblocks P and P1 corresponding to L and UBI has to + * select one of them when the flash is attached. The @sqnum field says which + * PEB is the original (obviously P will have lower @sqnum) and the copy. But + * it is not enough to select the physical eraseblock with the higher sequence + * number, because the unclean reboot could have happen in the middle of the + * copying process, so the data in P is corrupted. It is also not enough to + * just select the physical eraseblock with lower sequence number, because the + * data there may be old (consider a case if more data was added to P1 after + * the copying). Moreover, the unclean reboot may happen when the erasure of P + * was just started, so it result in unstable P, which is "mostly" OK, but + * still has unstable bits. + * + * UBI uses the @copy_flag field to indicate that this logical eraseblock is a + * copy. UBI also calculates data CRC when the data is moved and stores it at + * the @data_crc field of the copy (P1). So when UBI needs to pick one physical + * eraseblock of two (P or P1), the @copy_flag of the newer one (P1) is + * examined. If it is cleared, the situation* is simple and the newer one is + * picked. If it is set, the data CRC of the copy (P1) is examined. If the CRC + * 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 + * layout volume. Internal volumes are the main mechanism of UBI extensions. + * For example, in future one may introduce a journal internal volume. Internal + * volumes have their own reserved range of IDs. + * + * The @compat field is only used for internal volumes and contains the "degree + * of their compatibility". It is always zero for user volumes. This field + * provides a mechanism to introduce UBI extensions and to be still compatible + * with older UBI binaries. For example, if someone introduced a journal in + * future, he would probably use %UBI_COMPAT_DELETE compatibility for the + * journal volume. And in this case, older UBI binaries, which know nothing + * about the journal volume, would just delete this volume and work perfectly + * fine. This is similar to what Ext2fs does when it is fed by an Ext3fs image + * - it just ignores the Ext3fs journal. + * + * 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. + * + * 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, + * this field usually contains zero. The only exception is when the data of the + * physical eraseblock was moved to another physical eraseblock for + * wear-leveling reasons. In this case, UBI calculates CRC checksum of the + * contents and uses both @data_crc and @data_size fields. In this case, the + * @data_size field contains data size. + * + * The @used_ebs field is used only for static volumes and indicates how many + * eraseblocks the data of the volume takes. For dynamic volumes this field is + * not used and always contains zero. + * + * The @data_pad is calculated when volumes are created using the alignment + * parameter. So, effectively, the @data_pad field reduces the size of logical + * eraseblocks of this volume. This is very handy when one uses block-oriented + * software (say, cramfs) on top of the UBI volume. + */ +struct ubi_vid_hdr { + __be32 magic; + __u8 version; + __u8 vol_type; + __u8 copy_flag; + __u8 compat; + __be32 vol_id; + __be32 lnum; + __be32 leb_ver; /* obsolete, to be removed, don't use */ + __be32 data_size; + __be32 used_ebs; + __be32 data_pad; + __be32 data_crc; + __u8 padding1[4]; + __be64 sqnum; + __u8 padding2[12]; + __be32 hdr_crc; +} __attribute__ ((packed)); + +/* Internal UBI volumes count */ +#define UBI_INT_VOL_COUNT 1 + +/* + * Starting ID of internal volumes. There is reserved room for 4096 internal + * volumes. + */ +#define UBI_INTERNAL_VOL_START (0x7FFFFFFF - 4096) + +/* The layout volume contains the volume table */ + +#define UBI_LAYOUT_VOLUME_ID UBI_INTERNAL_VOL_START +#define UBI_LAYOUT_VOLUME_TYPE UBI_VID_DYNAMIC +#define UBI_LAYOUT_VOLUME_ALIGN 1 +#define UBI_LAYOUT_VOLUME_EBS 2 +#define UBI_LAYOUT_VOLUME_NAME "layout volume" +#define UBI_LAYOUT_VOLUME_COMPAT UBI_COMPAT_REJECT + +/* The maximum number of volumes per one UBI device */ +#define UBI_MAX_VOLUMES 128 + +/* The maximum volume name length */ +#define UBI_VOL_NAME_MAX 127 + +/* Size of the volume table record */ +#define UBI_VTBL_RECORD_SIZE sizeof(struct ubi_vtbl_record) + +/* Size of the volume table record without the ending CRC */ +#define UBI_VTBL_RECORD_SIZE_CRC (UBI_VTBL_RECORD_SIZE - sizeof(__be32)) + +/** + * struct ubi_vtbl_record - a record in the volume table. + * @reserved_pebs: how many physical eraseblocks are reserved for this volume + * @alignment: volume alignment + * @data_pad: how many bytes are unused at the end of the each physical + * eraseblock to satisfy the requested alignment + * @vol_type: volume type (%UBI_DYNAMIC_VOLUME or %UBI_STATIC_VOLUME) + * @upd_marker: if volume update was started but not finished + * @name_len: volume name length + * @name: the volume name + * @flags: volume flags (%UBI_VTBL_AUTORESIZE_FLG) + * @padding: reserved, zeroes + * @crc: a CRC32 checksum of the record + * + * The volume table records are stored in the volume table, which is stored in + * the layout volume. The layout volume consists of 2 logical eraseblock, each + * of which contains a copy of the volume table (i.e., the volume table is + * duplicated). The volume table is an array of &struct ubi_vtbl_record + * objects indexed by the volume ID. + * + * If the size of the logical eraseblock is large enough to fit + * %UBI_MAX_VOLUMES records, the volume table contains %UBI_MAX_VOLUMES + * records. Otherwise, it contains as many records as it can fit (i.e., size of + * logical eraseblock divided by sizeof(struct ubi_vtbl_record)). + * + * The @upd_marker flag is used to implement volume update. It is set to %1 + * before update and set to %0 after the update. So if the update operation was + * interrupted, UBI knows that the volume is corrupted. + * + * The @alignment field is specified when the volume is created and cannot be + * later changed. It may be useful, for example, when a block-oriented file + * system works on top of UBI. The @data_pad field is calculated using the + * logical eraseblock size and @alignment. The alignment must be multiple to the + * minimal flash I/O unit. If @alignment is 1, all the available space of + * the physical eraseblocks is used. + * + * Empty records contain all zeroes and the CRC checksum of those zeroes. + */ +struct ubi_vtbl_record { + __be32 reserved_pebs; + __be32 alignment; + __be32 data_pad; + __u8 vol_type; + __u8 upd_marker; + __be16 name_len; + __u8 name[UBI_VOL_NAME_MAX+1]; + __u8 flags; + __u8 padding[23]; + __be32 crc; +} __attribute__ ((packed)); + +#endif /* !__UBI_MEDIA_H__ */ diff --git a/drivers/mtd/ubi/ubi.h b/drivers/mtd/ubi/ubi.h new file mode 100644 index 0000000000..5552237910 --- /dev/null +++ b/drivers/mtd/ubi/ubi.h @@ -0,0 +1,648 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * Copyright (c) Nokia Corporation, 2006, 2007 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Artem Bityutskiy (Битюцкий Артём) + */ + +#ifndef __UBI_UBI_H__ +#define __UBI_UBI_H__ + +#ifdef UBI_LINUX +#include <linux/init.h> +#include <linux/types.h> +#include <linux/list.h> +#include <linux/rbtree.h> +#include <linux/sched.h> +#include <linux/wait.h> +#include <linux/mutex.h> +#include <linux/rwsem.h> +#include <linux/spinlock.h> +#include <linux/fs.h> +#include <linux/cdev.h> +#include <linux/device.h> +#include <linux/string.h> +#include <linux/vmalloc.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/ubi.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" + +/* Maximum number of supported UBI devices */ +#define UBI_MAX_DEVICES 32 + +/* UBI name used for character devices, sysfs, etc */ +#define UBI_NAME_STR "ubi" + +/* Normal UBI messages */ +#define ubi_msg(fmt, ...) printk(KERN_NOTICE "UBI: " fmt "\n", ##__VA_ARGS__) +/* UBI warning messages */ +#define ubi_warn(fmt, ...) printk(KERN_WARNING "UBI warning: %s: " fmt "\n", \ + __func__, ##__VA_ARGS__) +/* UBI error messages */ +#define ubi_err(fmt, ...) printk(KERN_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 */ +#define UBI_LEB_UNMAPPED -1 + +/* + * In case of errors, UBI tries to repeat the operation several times before + * returning error. The below constant defines how many times UBI re-tries. + */ +#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) + * UBI_IO_BITFLIPS: bit-flips were detected and corrected + */ +enum { + UBI_IO_PEB_EMPTY = 1, + UBI_IO_PEB_FREE, + UBI_IO_BAD_EC_HDR, + UBI_IO_BAD_VID_HDR, + UBI_IO_BITFLIPS +}; + +/** + * struct ubi_wl_entry - wear-leveling entry. + * @rb: link in the corresponding RB-tree + * @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. + */ +struct ubi_wl_entry { + struct rb_node rb; + int ec; + int pnum; +}; + +/** + * struct ubi_ltree_entry - an entry in the lock tree. + * @rb: links RB-tree nodes + * @vol_id: volume ID of the locked logical eraseblock + * @lnum: locked logical eraseblock number + * @users: how many tasks are using this logical eraseblock or wait for it + * @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 + * &struct ubi_ltree_entry object is inserted to the lock tree (@ubi->ltree). + * See EBA unit for details. + */ +struct ubi_ltree_entry { + struct rb_node rb; + int vol_id; + int lnum; + int users; + struct rw_semaphore mutex; +}; + +struct ubi_volume_desc; + +/** + * 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 + * @ubi: reference to the UBI device description object + * @vol_id: volume ID + * @ref_count: volume reference count + * @readers: number of users holding this volume in read-only mode + * @writers: number of users holding this volume in read-write mode + * @exclusive: whether somebody holds this volume in exclusive mode + * + * @reserved_pebs: how many physical eraseblocks are reserved for this volume + * @vol_type: volume type (%UBI_DYNAMIC_VOLUME or %UBI_STATIC_VOLUME) + * @usable_leb_size: logical eraseblock size without padding + * @used_ebs: how many logical eraseblocks in this volume contain data + * @last_eb_bytes: how many bytes are stored in the last logical eraseblock + * @used_bytes: how many bytes of data this volume contains + * @alignment: volume alignment + * @data_pad: how many bytes are not used at the end of physical eraseblocks to + * satisfy the requested alignment + * @name_len: volume name length + * @name: volume name + * + * @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 + * atomic LEB change + * @upd_buf: update buffer which is used to collect update data or data for + * atomic LEB change + * + * @eba_tbl: EBA table of this volume (LEB->PEB mapping) + * @checked: %1 if this static volume was checked + * @corrupted: %1 if the volume is corrupted (static volumes only) + * @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 + * + * The @corrupted field indicates that the volume's contents is corrupted. + * Since UBI protects only static volumes, this field is not relevant to + * dynamic volumes - it is user's responsibility to assure their data + * integrity. + * + * The @upd_marker flag indicates that this volume is either being updated at + * the moment or is damaged because of an unclean reboot. + */ +struct ubi_volume { + struct device dev; + struct cdev cdev; + struct ubi_device *ubi; + int vol_id; + int ref_count; + int readers; + int writers; + int exclusive; + + int reserved_pebs; + int vol_type; + int usable_leb_size; + int used_ebs; + int last_eb_bytes; + long long used_bytes; + int alignment; + int data_pad; + int name_len; + 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; + + int *eba_tbl; + unsigned int checked:1; + unsigned int corrupted:1; + 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 +}; + +/** + * struct ubi_volume_desc - descriptor of the UBI volume returned when it is + * opened. + * @vol: reference to the corresponding volume description object + * @mode: open mode (%UBI_READONLY, %UBI_READWRITE, or %UBI_EXCLUSIVE) + */ +struct ubi_volume_desc { + struct ubi_volume *vol; + int mode; +}; + +struct ubi_wl_entry; + +/** + * 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 + * @ubi_num: UBI device number + * @ubi_name: UBI device name + * @vol_count: number of volumes in this UBI device + * @volumes: volumes of this UBI device + * @volumes_lock: protects @volumes, @rsvd_pebs, @avail_pebs, beb_rsvd_pebs, + * @beb_rsvd_level, @bad_peb_count, @good_peb_count, @vol_count, + * @vol->readers, @vol->writers, @vol->exclusive, + * @vol->ref_count, @vol->mapping and @vol->eba_tbl. + * @ref_count: count of references on the UBI device + * + * @rsvd_pebs: count of reserved physical eraseblocks + * @avail_pebs: count of available physical eraseblocks + * @beb_rsvd_pebs: how many physical eraseblocks are reserved for bad PEB + * handling + * @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 + * @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 + * + * @max_ec: current highest erase counter value + * @mean_ec: current mean erase counter value + * + * @global_sqnum: global sequence number + * @ltree_lock: protects the lock tree and @global_sqnum + * @ltree: the lock tree + * @alc_mutex: serializes "atomic LEB change" operations + * + * @used: RB-tree of used physical eraseblocks + * @free: RB-tree of free physical eraseblocks + * @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 + * @move_mutex: serializes eraseblock moves + * @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 + * @works: list of pending works + * @works_count: count of pending works + * @bgt_thread: background thread description object + * @thread_enabled: if the background thread is enabled + * @bgt_name: background thread name + * + * @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_count: count of bad physical eraseblocks + * @good_peb_count: count of good 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 + * @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) + * @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 + * @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 + */ +struct ubi_device { + struct cdev cdev; + struct device dev; + int ubi_num; + char ubi_name[sizeof(UBI_NAME_STR)+5]; + int vol_count; + struct ubi_volume *volumes[UBI_MAX_VOLUMES+UBI_INT_VOL_COUNT]; + spinlock_t volumes_lock; + int ref_count; + + int rsvd_pebs; + int avail_pebs; + int beb_rsvd_pebs; + int beb_rsvd_level; + + int autoresize_vol_id; + int vtbl_slots; + int vtbl_size; + struct ubi_vtbl_record *vtbl; + struct mutex volumes_mutex; + + int max_ec; + /* TODO: mean_ec is not updated run-time, fix */ + int mean_ec; + + /* EBA unit's stuff */ + unsigned long long global_sqnum; + spinlock_t ltree_lock; + struct rb_root ltree; + struct mutex alc_mutex; + + /* Wear-leveling unit's stuff */ + struct rb_root used; + struct rb_root free; + struct rb_root scrub; + struct { + struct rb_root pnum; + struct rb_root aec; + } prot; + 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; + struct list_head works; + int works_count; + struct task_struct *bgt_thread; + int thread_enabled; + char bgt_name[sizeof(UBI_BGT_NAME_PATTERN)+2]; + + /* I/O unit's stuff */ + long long flash_size; + int peb_count; + int peb_size; + int bad_peb_count; + int good_peb_count; + int min_io_size; + int hdrs_min_io_size; + int ro_mode; + int leb_size; + int leb_start; + int ec_hdr_alsize; + int vid_hdr_alsize; + int vid_hdr_offset; + int vid_hdr_aloffset; + int vid_hdr_shift; + int bad_allowed; + struct mtd_info *mtd; + + void *peb_buf1; + void *peb_buf2; + struct mutex buf_mutex; + struct mutex ckvol_mutex; +#ifdef CONFIG_MTD_UBI_DEBUG + void *dbg_peb_buf; + struct mutex dbg_buf_mutex; +#endif +}; + +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 struct class *ubi_class; +extern struct mutex ubi_devices_mutex; + +/* 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); + +/* 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_resize_volume(struct ubi_volume_desc *desc, int reserved_pebs); +int ubi_add_volume(struct ubi_device *ubi, struct ubi_volume *vol); +void ubi_free_volume(struct ubi_device *ubi, struct ubi_volume *vol); + +/* upd.c */ +int ubi_start_update(struct ubi_device *ubi, struct ubi_volume *vol, + long long bytes); +int ubi_finish_update(struct ubi_device *ubi, struct ubi_volume *vol); +int ubi_more_update_data(struct ubi_device *ubi, struct ubi_volume *vol, + const void __user *buf, int count); +int ubi_start_leb_change(struct ubi_device *ubi, struct ubi_volume *vol, + const struct ubi_leb_change_req *req); +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_check_volume(struct ubi_device *ubi, int vol_id); +void ubi_calculate_reserved(struct ubi_device *ubi); + +/* gluebi.c */ +#ifdef CONFIG_MTD_UBI_GLUEBI +int ubi_create_gluebi(struct ubi_device *ubi, struct ubi_volume *vol); +int ubi_destroy_gluebi(struct ubi_volume *vol); +void ubi_gluebi_updated(struct ubi_volume *vol); +#else +#define ubi_create_gluebi(ubi, vol) 0 +#define ubi_destroy_gluebi(vol) 0 +#define ubi_gluebi_updated(vol) +#endif + +/* eba.c */ +int ubi_eba_unmap_leb(struct ubi_device *ubi, struct ubi_volume *vol, + int lnum); +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); +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 ubi_eba_atomic_leb_change(struct ubi_device *ubi, struct ubi_volume *vol, + int lnum, const void *buf, int len, int dtype); +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); + +/* 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_scrub_peb(struct ubi_device *ubi, int pnum); +int ubi_wl_init_scan(struct ubi_device *ubi, struct ubi_scan_info *si); +void ubi_wl_close(struct ubi_device *ubi); +int ubi_thread(void *u); + +/* io.c */ +int ubi_io_read(const struct ubi_device *ubi, void *buf, int pnum, int offset, + int len); +int ubi_io_write(struct ubi_device *ubi, const void *buf, int pnum, int offset, + int len); +int ubi_io_sync_erase(struct ubi_device *ubi, int pnum, int torture); +int ubi_io_is_bad(const struct ubi_device *ubi, int pnum); +int ubi_io_mark_bad(const struct ubi_device *ubi, int pnum); +int ubi_io_read_ec_hdr(struct ubi_device *ubi, int pnum, + struct ubi_ec_hdr *ec_hdr, int verbose); +int ubi_io_write_ec_hdr(struct ubi_device *ubi, int pnum, + struct ubi_ec_hdr *ec_hdr); +int ubi_io_read_vid_hdr(struct ubi_device *ubi, int pnum, + struct ubi_vid_hdr *vid_hdr, int verbose); +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_detach_mtd_dev(struct mtd_info *mtd, 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); + +/* cdev.c */ +int ubi_cdev_add(struct ubi_device *ubi); +void ubi_cdev_remove(struct ubi_device *ubi); +int ubi_volume_cdev_add(struct ubi_device *ubi, struct ubi_volume *vol); +void ubi_volume_cdev_remove(struct ubi_volume *vol); + +/* + * ubi_rb_for_each_entry - walk an RB-tree. + * @rb: a pointer to type 'struct rb_node' to 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 + */ +#define ubi_rb_for_each_entry(rb, pos, root, member) \ + 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)) + +/** + * ubi_zalloc_vid_hdr - allocate a volume identifier header object. + * @ubi: UBI device description object + * @gfp_flags: GFP flags to allocate with + * + * This function returns a pointer to the newly allocated and zero-filled + * volume identifier header object in case of success and %NULL in case of + * failure. + */ +static inline struct ubi_vid_hdr * +ubi_zalloc_vid_hdr(const struct ubi_device *ubi, unsigned int gfp_flags) +{ + void *vid_hdr; + + vid_hdr = kzalloc(ubi->vid_hdr_alsize, gfp_flags); + if (!vid_hdr) + return NULL; + + /* + * VID headers may be stored at un-aligned flash offsets, so we shift + * the pointer. + */ + return vid_hdr + ubi->vid_hdr_shift; +} + +/** + * ubi_free_vid_hdr - free a volume identifier header object. + * @ubi: UBI device description object + * @vid_hdr: the object to free + */ +static inline void ubi_free_vid_hdr(const struct ubi_device *ubi, + struct ubi_vid_hdr *vid_hdr) +{ + void *p = vid_hdr; + + if (!p) + return; + + kfree(p - ubi->vid_hdr_shift); +} + +/* + * This function is equivalent to 'ubi_io_read()', but @offset is relative to + * the beginning of the logical eraseblock, not to the beginning of the + * physical eraseblock. + */ +static inline int ubi_io_read_data(const struct ubi_device *ubi, void *buf, + int pnum, int offset, int len) +{ + ubi_assert(offset >= 0); + return ubi_io_read(ubi, buf, pnum, offset + ubi->leb_start, len); +} + +/* + * This function is equivalent to 'ubi_io_write()', but @offset is relative to + * the beginning of the logical eraseblock, not to the beginning of the + * physical eraseblock. + */ +static inline int ubi_io_write_data(struct ubi_device *ubi, const void *buf, + int pnum, int offset, int len) +{ + ubi_assert(offset >= 0); + return ubi_io_write(ubi, buf, pnum, offset + ubi->leb_start, len); +} + +/** + * ubi_ro_mode - switch to read-only mode. + * @ubi: UBI device description object + */ +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"); + } +} + +/** + * vol_id2idx - get table index by volume ID. + * @ubi: UBI device description object + * @vol_id: volume ID + */ +static inline int vol_id2idx(const struct ubi_device *ubi, int vol_id) +{ + if (vol_id >= UBI_INTERNAL_VOL_START) + return vol_id - UBI_INTERNAL_VOL_START + ubi->vtbl_slots; + else + return vol_id; +} + +/** + * idx2vol_id - get volume ID by table index. + * @ubi: UBI device description object + * @idx: table index + */ +static inline int idx2vol_id(const struct ubi_device *ubi, int idx) +{ + if (idx >= ubi->vtbl_slots) + return idx - ubi->vtbl_slots + UBI_INTERNAL_VOL_START; + else + return idx; +} + +#endif /* !__UBI_UBI_H__ */ diff --git a/drivers/mtd/ubi/upd.c b/drivers/mtd/ubi/upd.c new file mode 100644 index 0000000000..fda2043183 --- /dev/null +++ b/drivers/mtd/ubi/upd.c @@ -0,0 +1,445 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * Copyright (c) Nokia Corporation, 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Artem Bityutskiy (Битюцкий Артём) + * + * Jan 2007: Alexander Schmidt, hacked per-volume update. + */ + +/* + * This file contains implementation of the volume update and atomic LEB change + * functionality. + * + * The update operation is based on the per-volume update marker which is + * stored in the volume table. The update marker is set before the update + * starts, and removed after the update has been finished. So if the update was + * interrupted by an unclean re-boot or due to some other reasons, the update + * marker stays on the flash media and UBI finds it when it attaches the MTD + * device next time. If the update marker is set for a volume, the volume is + * treated as damaged and most I/O operations are prohibited. Only a new update + * operation is allowed. + * + * Note, in general it is possible to implement the update operation as a + * transaction with a roll-back capability. + */ + +#ifdef UBI_LINUX +#include <linux/err.h> +#include <asm/uaccess.h> +#include <asm/div64.h> +#endif + +#include "ubi-barebox.h" +#include "ubi.h" + +/** + * set_update_marker - set update marker. + * @ubi: UBI device description object + * @vol: volume description object + * + * This function sets the update marker flag for volume @vol. Returns zero + * in case of success and a negative error code in case of failure. + */ +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); + + if (vol->upd_marker) { + ubi_assert(ubi->vtbl[vol->vol_id].upd_marker); + dbg_msg("already set"); + return 0; + } + + memcpy(&vtbl_rec, &ubi->vtbl[vol->vol_id], + sizeof(struct ubi_vtbl_record)); + vtbl_rec.upd_marker = 1; + + mutex_lock(&ubi->volumes_mutex); + err = ubi_change_vtbl_record(ubi, vol->vol_id, &vtbl_rec); + mutex_unlock(&ubi->volumes_mutex); + vol->upd_marker = 1; + return err; +} + +/** + * clear_update_marker - clear update marker. + * @ubi: UBI device description object + * @vol: volume description object + * @bytes: new data size in bytes + * + * This function clears the update marker for volume @vol, sets new volume + * data size and clears the "corrupted" flag (static volumes only). Returns + * zero in case of success and a negative error code in case of failure. + */ +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); + + memcpy(&vtbl_rec, &ubi->vtbl[vol->vol_id], + sizeof(struct ubi_vtbl_record)); + 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; + if (vol->last_eb_bytes) + vol->used_ebs += 1; + else + vol->last_eb_bytes = vol->usable_leb_size; + } + + mutex_lock(&ubi->volumes_mutex); + err = ubi_change_vtbl_record(ubi, vol->vol_id, &vtbl_rec); + mutex_unlock(&ubi->volumes_mutex); + vol->upd_marker = 0; + return err; +} + +/** + * ubi_start_update - start volume update. + * @ubi: UBI device description object + * @vol: volume description object + * @bytes: update bytes + * + * This function starts volume update operation. If @bytes is zero, the volume + * is just wiped out. Returns zero in case of success and a negative error code + * in case of failure. + */ +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); + ubi_assert(!vol->updating && !vol->changing_leb); + vol->updating = 1; + + err = set_update_marker(ubi, vol); + if (err) + return err; + + /* Before updating - wipe out the volume */ + for (i = 0; i < vol->reserved_pebs; i++) { + err = ubi_eba_unmap_leb(ubi, vol, i); + if (err) + return err; + } + + if (bytes == 0) { + err = clear_update_marker(ubi, vol, 0); + if (err) + return err; + err = ubi_wl_flush(ubi); + if (!err) + vol->updating = 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_bytes = bytes; + vol->upd_received = 0; + return 0; +} + +int ubi_finish_update(struct ubi_device *ubi, struct ubi_volume *vol) +{ + int 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; + vfree(vol->upd_buf); + } + + return err; +} + +/** + * ubi_start_leb_change - start atomic LEB change. + * @ubi: UBI device description object + * @vol: volume description object + * @req: operation request + * + * This function starts atomic LEB change operation. Returns zero in case of + * success and a negative error code in case of failure. + */ +int ubi_start_leb_change(struct ubi_device *ubi, struct ubi_volume *vol, + const struct ubi_leb_change_req *req) +{ + ubi_assert(!vol->updating && !vol->changing_leb); + + dbg_msg("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); + + 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) + return -ENOMEM; + + return 0; +} + +/** + * write_leb - write update data. + * @ubi: UBI device description object + * @vol: volume description object + * @lnum: logical eraseblock number + * @buf: data to write + * @len: data size + * @used_ebs: how many logical eraseblocks will this volume contain (static + * volumes only) + * + * This function writes update data to corresponding logical eraseblock. In + * case of dynamic volume, this function checks if the data contains 0xFF bytes + * at the end. If yes, the 0xFF bytes are cut and not written. So if the whole + * buffer contains only 0xFF bytes, the LEB is left unmapped. + * + * The reason why we skip the trailing 0xFF bytes in case of dynamic volume is + * that we want to make sure that more data may be appended to the logical + * eraseblock in future. Indeed, writing 0xFF bytes may have side effects and + * this PEB won't be writable anymore. So if one writes the file-system image + * to the UBI volume where 0xFFs mean free space - UBI makes sure this free + * space is writable after the update. + * + * We do not do this for static volumes because they are read-only. But this + * also cannot be done because we have to store per-LEB CRC and the correct + * data length. + * + * This function returns zero in case of success and a negative error code in + * case of failure. + */ +static int write_leb(struct ubi_device *ubi, struct ubi_volume *vol, int lnum, + void *buf, int len, int used_ebs) +{ + int err; + + if (vol->vol_type == UBI_DYNAMIC_VOLUME) { + int l = ALIGN(len, ubi->min_io_size); + + 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); + return 0; + } + + err = ubi_eba_write_leb(ubi, vol, lnum, buf, 0, len, UBI_UNKNOWN); + } else { + /* + * When writing static volume, and this is the last logical + * eraseblock, the length (@len) does not have to be aligned to + * the minimal flash I/O unit. The 'ubi_eba_write_leb_st()' + * function accepts exact (unaligned) length and stores it in + * the VID header. And it takes care of proper alignment by + * padding the buffer. Here we just make sure the padding will + * 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); + } + + return err; +} + +/** + * ubi_more_update_data - write more update data. + * @vol: volume description object + * @buf: write data (user-space memory buffer) + * @count: how much bytes to write + * + * This function writes more data to the volume which is being updated. It may + * be called arbitrary number of times until all the update data arriveis. This + * function returns %0 in case of success, number of bytes written during the + * last call if the whole volume update has been successfully finished, and a + * negative error code in case of failure. + */ +int ubi_more_update_data(struct ubi_device *ubi, struct ubi_volume *vol, + const void __user *buf, int count) +{ + uint64_t tmp; + int lnum, offs, err = 0, len, to_write = count; + + dbg_msg("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; + + if (vol->upd_received + count > vol->upd_bytes) + to_write = count = vol->upd_bytes - vol->upd_received; + + /* + * When updating volumes, we accumulate whole logical eraseblock of + * data and write it at once. + */ + if (offs != 0) { + /* + * This is a write to the middle of the logical eraseblock. We + * copy the data to our update buffer and wait for more data or + * flush it if the whole eraseblock is written or the update + * is finished. + */ + + len = vol->usable_leb_size - offs; + if (len > count) + len = count; + + err = copy_from_user(vol->upd_buf + offs, buf, len); + if (err) + return -EFAULT; + + if (offs + len == vol->usable_leb_size) { + int flush_len = offs + len; + + /* + * OK, we gathered the whole eraseblock, it's time to flush + * the buffer. + */ + ubi_assert(flush_len <= vol->usable_leb_size); + err = write_leb(ubi, vol, lnum, vol->upd_buf, flush_len, + vol->upd_ebs); + if (err) + return err; + } + + vol->upd_received += len; + count -= len; + buf += len; + lnum += 1; + } + + /* + * If we've got more to write, let's continue. At this point we know we + * are starting from the beginning of an eraseblock. + */ + while (count) { + if (count > vol->usable_leb_size) + len = vol->usable_leb_size; + else + len = count; + + err = copy_from_user(vol->upd_buf, buf, len); + if (err) + return -EFAULT; + + if (len == vol->usable_leb_size || + vol->upd_received + len == vol->upd_bytes) { + err = write_leb(ubi, vol, lnum, vol->upd_buf, + len, vol->upd_ebs); + if (err) + break; + } + + vol->upd_received += len; + count -= len; + lnum += 1; + buf += len; + } + + ubi_assert(vol->upd_received <= vol->upd_bytes); + + return err; +} + +/** + * ubi_more_leb_change_data - accept more data for atomic LEB change. + * @vol: volume description object + * @buf: write data (user-space memory buffer) + * @count: how much bytes to write + * + * This function accepts more data to the volume which is being under the + * "atomic LEB change" operation. It may be called arbitrary number of times + * until all data arrives. This function returns %0 in case of success, number + * of bytes written during the last call if the whole "atomic LEB change" + * operation has been successfully finished, and a negative error code in case + * of failure. + */ +int ubi_more_leb_change_data(struct ubi_device *ubi, struct ubi_volume *vol, + const void __user *buf, int count) +{ + int err; + + dbg_msg("write %d of %lld bytes, %lld already passed", + count, vol->upd_bytes, vol->upd_received); + + if (ubi->ro_mode) + return -EROFS; + + if (vol->upd_received + count > vol->upd_bytes) + count = vol->upd_bytes - vol->upd_received; + + err = copy_from_user(vol->upd_buf + vol->upd_received, buf, count); + if (err) + return -EFAULT; + + vol->upd_received += count; + + 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); + 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); + if (err) + return err; + } + + ubi_assert(vol->upd_received <= vol->upd_bytes); + if (vol->upd_received == vol->upd_bytes) { + vol->changing_leb = 0; + err = count; + vfree(vol->upd_buf); + } + + return err; +} diff --git a/drivers/mtd/ubi/vmt.c b/drivers/mtd/ubi/vmt.c new file mode 100644 index 0000000000..4f1d0f4209 --- /dev/null +++ b/drivers/mtd/ubi/vmt.c @@ -0,0 +1,866 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Artem Bityutskiy (Битюцкий Артём) + */ + +/* + * This file contains implementation of volume creation, deletion, updating and + * resizing. + */ + +#ifdef UBI_LINUX +#include <linux/err.h> +#include <asm/div64.h> +#endif + +#include "ubi-barebox.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 + +#ifdef UBI_LINUX +static ssize_t vol_attribute_show(struct device *dev, + struct device_attribute *attr, char *buf); + +/* Device attributes corresponding to files in '/<sysfs>/class/ubi/ubiX_Y' */ +static struct device_attribute attr_vol_reserved_ebs = + __ATTR(reserved_ebs, S_IRUGO, vol_attribute_show, NULL); +static struct device_attribute attr_vol_type = + __ATTR(type, S_IRUGO, vol_attribute_show, NULL); +static struct device_attribute attr_vol_name = + __ATTR(name, S_IRUGO, vol_attribute_show, NULL); +static struct device_attribute attr_vol_corrupted = + __ATTR(corrupted, S_IRUGO, vol_attribute_show, NULL); +static struct device_attribute attr_vol_alignment = + __ATTR(alignment, S_IRUGO, vol_attribute_show, NULL); +static struct device_attribute attr_vol_usable_eb_size = + __ATTR(usable_eb_size, S_IRUGO, vol_attribute_show, NULL); +static struct device_attribute attr_vol_data_bytes = + __ATTR(data_bytes, S_IRUGO, vol_attribute_show, NULL); +static struct device_attribute attr_vol_upd_marker = + __ATTR(upd_marker, S_IRUGO, vol_attribute_show, NULL); + +/* + * "Show" method for files in '/<sysfs>/class/ubi/ubiX_Y/'. + * + * Consider a situation: + * A. process 1 opens a sysfs file related to volume Y, say + * /<sysfs>/class/ubi/ubiX_Y/reserved_ebs; + * B. process 2 removes volume Y; + * C. process 1 starts reading the /<sysfs>/class/ubi/ubiX_Y/reserved_ebs file; + * + * In this situation, this function will return %-ENODEV because it will find + * out that the volume was removed from the @ubi->volumes array. + */ +static ssize_t vol_attribute_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int ret; + struct ubi_volume *vol = container_of(dev, struct ubi_volume, dev); + struct ubi_device *ubi; + + ubi = ubi_get_device(vol->ubi->ubi_num); + if (!ubi) + return -ENODEV; + + spin_lock(&ubi->volumes_lock); + if (!ubi->volumes[vol->vol_id]) { + spin_unlock(&ubi->volumes_lock); + ubi_put_device(ubi); + return -ENODEV; + } + /* Take a reference to prevent volume removal */ + vol->ref_count += 1; + spin_unlock(&ubi->volumes_lock); + + if (attr == &attr_vol_reserved_ebs) + ret = sprintf(buf, "%d\n", vol->reserved_pebs); + else if (attr == &attr_vol_type) { + const char *tp; + + if (vol->vol_type == UBI_DYNAMIC_VOLUME) + tp = "dynamic"; + else + tp = "static"; + ret = sprintf(buf, "%s\n", tp); + } else if (attr == &attr_vol_name) + ret = sprintf(buf, "%s\n", vol->name); + else if (attr == &attr_vol_corrupted) + ret = sprintf(buf, "%d\n", vol->corrupted); + else if (attr == &attr_vol_alignment) + ret = sprintf(buf, "%d\n", vol->alignment); + else if (attr == &attr_vol_usable_eb_size) + ret = sprintf(buf, "%d\n", vol->usable_leb_size); + else if (attr == &attr_vol_data_bytes) + ret = sprintf(buf, "%lld\n", vol->used_bytes); + else if (attr == &attr_vol_upd_marker) + ret = sprintf(buf, "%d\n", vol->upd_marker); + else + /* This must be a bug */ + ret = -EINVAL; + + /* We've done the operation, drop volume and UBI device references */ + spin_lock(&ubi->volumes_lock); + vol->ref_count -= 1; + ubi_assert(vol->ref_count >= 0); + spin_unlock(&ubi->volumes_lock); + ubi_put_device(ubi); + return ret; +} +#endif + +/* Release method for volume devices */ +static void vol_release(struct device *dev) +{ + struct ubi_volume *vol = container_of(dev, struct ubi_volume, dev); + + kfree(vol); +} + +#ifdef UBI_LINUX +/** + * volume_sysfs_init - initialize sysfs for new volume. + * @ubi: UBI device description object + * @vol: volume description object + * + * This function returns zero in case of success and a negative error code in + * case of failure. + * + * Note, this function does not free allocated resources in case of failure - + * the caller does it. This is because this would cause release() here and the + * caller would oops. + */ +static int volume_sysfs_init(struct ubi_device *ubi, struct ubi_volume *vol) +{ + int err; + + err = device_create_file(&vol->dev, &attr_vol_reserved_ebs); + if (err) + return err; + err = device_create_file(&vol->dev, &attr_vol_type); + if (err) + return err; + err = device_create_file(&vol->dev, &attr_vol_name); + if (err) + return err; + err = device_create_file(&vol->dev, &attr_vol_corrupted); + if (err) + return err; + err = device_create_file(&vol->dev, &attr_vol_alignment); + if (err) + return err; + err = device_create_file(&vol->dev, &attr_vol_usable_eb_size); + if (err) + return err; + err = device_create_file(&vol->dev, &attr_vol_data_bytes); + if (err) + return err; + err = device_create_file(&vol->dev, &attr_vol_upd_marker); + return err; +} + +/** + * volume_sysfs_close - close sysfs for a volume. + * @vol: volume description object + */ +static void volume_sysfs_close(struct ubi_volume *vol) +{ + device_remove_file(&vol->dev, &attr_vol_upd_marker); + device_remove_file(&vol->dev, &attr_vol_data_bytes); + device_remove_file(&vol->dev, &attr_vol_usable_eb_size); + device_remove_file(&vol->dev, &attr_vol_alignment); + device_remove_file(&vol->dev, &attr_vol_corrupted); + device_remove_file(&vol->dev, &attr_vol_name); + device_remove_file(&vol->dev, &attr_vol_type); + device_remove_file(&vol->dev, &attr_vol_reserved_ebs); + device_unregister(&vol->dev); +} +#endif + +/** + * ubi_create_volume - create volume. + * @ubi: UBI device description object + * @req: volume creation request + * + * This function creates volume described by @req. If @req->vol_id id + * %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. + */ +int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req) +{ + int i, err, vol_id = req->vol_id, dont_free = 0; + struct ubi_volume *vol; + struct ubi_vtbl_record vtbl_rec; + uint64_t bytes; + dev_t dev; + + if (ubi->ro_mode) + return -EROFS; + + vol = kzalloc(sizeof(struct ubi_volume), GFP_KERNEL); + if (!vol) + return -ENOMEM; + + spin_lock(&ubi->volumes_lock); + if (vol_id == UBI_VOL_NUM_AUTO) { + /* Find unused volume ID */ + dbg_msg("search for vacant volume ID"); + for (i = 0; i < ubi->vtbl_slots; i++) + if (!ubi->volumes[i]) { + vol_id = i; + break; + } + + if (vol_id == UBI_VOL_NUM_AUTO) { + dbg_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, + (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); + goto out_unlock; + } + + /* Ensure that the name is unique */ + for (i = 0; i < ubi->vtbl_slots; i++) + 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); + 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; + + /* Reserve physical eraseblocks */ + if (vol->reserved_pebs > ubi->avail_pebs) { + dbg_err("not enough PEBs, only %d available", ubi->avail_pebs); + err = -ENOSPC; + goto out_unlock; + } + ubi->avail_pebs -= vol->reserved_pebs; + ubi->rsvd_pebs += vol->reserved_pebs; + spin_unlock(&ubi->volumes_lock); + + vol->vol_id = vol_id; + vol->alignment = req->alignment; + 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); + vol->ubi = ubi; + + /* + * Finish all pending erases because there may be some LEBs belonging + * to the same volume ID. + */ + err = ubi_wl_flush(ubi); + if (err) + goto out_acc; + + vol->eba_tbl = kmalloc(vol->reserved_pebs * sizeof(int), GFP_KERNEL); + if (!vol->eba_tbl) { + err = -ENOMEM; + goto out_acc; + } + + for (i = 0; i < vol->reserved_pebs; i++) + vol->eba_tbl[i] = UBI_LEB_UNMAPPED; + + if (vol->vol_type == UBI_DYNAMIC_VOLUME) { + vol->used_ebs = vol->reserved_pebs; + vol->last_eb_bytes = vol->usable_leb_size; + 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 += 1; + else + vol->last_eb_bytes = vol->usable_leb_size; + } + + /* Register character device for the volume */ + cdev_init(&vol->cdev, &ubi_vol_cdev_operations); +#ifdef UBI_LINUX + vol->cdev.owner = THIS_MODULE; +#endif + dev = MKDEV(MAJOR(ubi->cdev.dev), vol_id + 1); + err = ubi_volume_cdev_add(ubi, vol); + if (err) { + ubi_err("cannot add character device"); + 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); + err = device_register(&vol->dev); + if (err) { + ubi_err("cannot register device"); + goto out_gluebi; + } + + err = volume_sysfs_init(ubi, vol); + if (err) + goto out_sysfs; + + /* Fill volume table record */ + memset(&vtbl_rec, 0, sizeof(struct ubi_vtbl_record)); + vtbl_rec.reserved_pebs = cpu_to_be32(vol->reserved_pebs); + vtbl_rec.alignment = cpu_to_be32(vol->alignment); + vtbl_rec.data_pad = cpu_to_be32(vol->data_pad); + vtbl_rec.name_len = cpu_to_be16(vol->name_len); + if (vol->vol_type == UBI_DYNAMIC_VOLUME) + 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); + + err = ubi_change_vtbl_record(ubi, vol_id, &vtbl_rec); + if (err) + goto out_sysfs; + + spin_lock(&ubi->volumes_lock); + ubi->volumes[vol_id] = vol; + ubi->vol_count += 1; + spin_unlock(&ubi->volumes_lock); + + paranoid_check_volumes(ubi); + return 0; + +out_sysfs: + /* + * 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; + 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: + ubi_volume_cdev_remove(vol); +out_mapping: + 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 + kfree(vol); + ubi_err("cannot create volume %d, error %d", vol_id, err); + return err; +} + +/** + * ubi_remove_volume - remove volume. + * @desc: volume descriptor + * + * 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 + * locked. + */ +int ubi_remove_volume(struct ubi_volume_desc *desc) +{ + 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); + ubi_assert(desc->mode == UBI_EXCLUSIVE); + ubi_assert(vol == ubi->volumes[vol_id]); + + if (ubi->ro_mode) + return -EROFS; + + spin_lock(&ubi->volumes_lock); + if (vol->ref_count > 1) { + /* + * The volume is busy, probably someone is reading one of its + * sysfs files. + */ + err = -EBUSY; + goto out_unlock; + } + 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; + + for (i = 0; i < vol->reserved_pebs; i++) { + err = ubi_eba_unmap_leb(ubi, vol, i); + if (err) + goto out_err; + } + + kfree(vol->eba_tbl); + vol->eba_tbl = NULL; + ubi_volume_cdev_remove(vol); + 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->vol_count -= 1; + spin_unlock(&ubi->volumes_lock); + + paranoid_check_volumes(ubi); + return 0; + +out_err: + ubi_err("cannot remove volume %d, error %d", vol_id, err); + spin_lock(&ubi->volumes_lock); + ubi->volumes[vol_id] = vol; +out_unlock: + spin_unlock(&ubi->volumes_lock); + return err; +} + +/** + * ubi_resize_volume - re-size volume. + * @desc: volume descriptor + * @reserved_pebs: new size in physical eraseblocks + * + * 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. + */ +int ubi_resize_volume(struct ubi_volume_desc *desc, int reserved_pebs) +{ + int i, err, pebs, *new_mapping; + struct ubi_volume *vol = desc->vol; + struct ubi_device *ubi = vol->ubi; + struct ubi_vtbl_record vtbl_rec; + int vol_id = vol->vol_id; + + 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); + + if (vol->vol_type == UBI_STATIC_VOLUME && + reserved_pebs < vol->used_ebs) { + dbg_err("too small size %d, %d LEBs contain data", + reserved_pebs, vol->used_ebs); + return -EINVAL; + } + + /* If the size is the same, we have nothing to do */ + if (reserved_pebs == vol->reserved_pebs) + return 0; + + new_mapping = kmalloc(reserved_pebs * sizeof(int), GFP_KERNEL); + if (!new_mapping) + return -ENOMEM; + + for (i = 0; i < reserved_pebs; i++) + new_mapping[i] = UBI_LEB_UNMAPPED; + + spin_lock(&ubi->volumes_lock); + if (vol->ref_count > 1) { + spin_unlock(&ubi->volumes_lock); + err = -EBUSY; + goto out_free; + } + spin_unlock(&ubi->volumes_lock); + + /* Reserve physical eraseblocks */ + pebs = reserved_pebs - vol->reserved_pebs; + if (pebs > 0) { + spin_lock(&ubi->volumes_lock); + if (pebs > ubi->avail_pebs) { + dbg_err("not enough PEBs: requested %d, available %d", + pebs, ubi->avail_pebs); + spin_unlock(&ubi->volumes_lock); + err = -ENOSPC; + goto out_free; + } + ubi->avail_pebs -= pebs; + ubi->rsvd_pebs += pebs; + for (i = 0; i < vol->reserved_pebs; i++) + new_mapping[i] = vol->eba_tbl[i]; + kfree(vol->eba_tbl); + vol->eba_tbl = new_mapping; + spin_unlock(&ubi->volumes_lock); + } + + /* Change volume table record */ + memcpy(&vtbl_rec, &ubi->vtbl[vol_id], sizeof(struct ubi_vtbl_record)); + vtbl_rec.reserved_pebs = cpu_to_be32(reserved_pebs); + err = ubi_change_vtbl_record(ubi, vol_id, &vtbl_rec); + if (err) + goto out_acc; + + if (pebs < 0) { + for (i = 0; i < -pebs; i++) { + err = ubi_eba_unmap_leb(ubi, vol, reserved_pebs + i); + if (err) + goto out_acc; + } + 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); + } + for (i = 0; i < reserved_pebs; i++) + new_mapping[i] = vol->eba_tbl[i]; + kfree(vol->eba_tbl); + vol->eba_tbl = new_mapping; + spin_unlock(&ubi->volumes_lock); + } + + vol->reserved_pebs = reserved_pebs; + if (vol->vol_type == UBI_DYNAMIC_VOLUME) { + vol->used_ebs = reserved_pebs; + vol->last_eb_bytes = vol->usable_leb_size; + vol->used_bytes = + (long long)vol->used_ebs * vol->usable_leb_size; + } + + paranoid_check_volumes(ubi); + return 0; + +out_acc: + if (pebs > 0) { + spin_lock(&ubi->volumes_lock); + ubi->rsvd_pebs -= pebs; + ubi->avail_pebs += pebs; + spin_unlock(&ubi->volumes_lock); + } +out_free: + kfree(new_mapping); + return err; +} + +/** + * ubi_add_volume - add volume. + * @ubi: UBI device description object + * @vol: volume description object + * + * This function adds an existing volume and initializes all its data + * structures. Returns zero in case of success and a negative error code in + * case of failure. + */ +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); + + /* Register character device for the volume */ + cdev_init(&vol->cdev, &ubi_vol_cdev_operations); +#ifdef UBI_LINUX + vol->cdev.owner = THIS_MODULE; +#endif + dev = MKDEV(MAJOR(ubi->cdev.dev), vol->vol_id + 1); + err = ubi_volume_cdev_add(ubi, vol); + if (err) { + ubi_err("cannot add character device for volume %d, error %d", + vol_id, err); + 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); + err = device_register(&vol->dev); + if (err) + goto out_gluebi; + + err = volume_sysfs_init(ubi, vol); + if (err) { + ubi_volume_cdev_remove(vol); + err = ubi_destroy_gluebi(vol); + volume_sysfs_close(vol); + return err; + } + + paranoid_check_volumes(ubi); + return 0; + +out_gluebi: + err = ubi_destroy_gluebi(vol); +out_cdev: + ubi_volume_cdev_remove(vol); + return err; +} + +/** + * ubi_free_volume - free volume. + * @ubi: UBI device description object + * @vol: volume description object + * + * This function frees all resources for volume @vol but does not remove it. + * Used only when the UBI device is detached. + */ +void ubi_free_volume(struct ubi_device *ubi, struct ubi_volume *vol) +{ + int err; + + dbg_msg("free volume %d", vol->vol_id); + + ubi->volumes[vol->vol_id] = NULL; + err = ubi_destroy_gluebi(vol); + ubi_volume_cdev_remove(vol); + volume_sysfs_close(vol); +} + +#ifdef CONFIG_MTD_UBI_DEBUG_PARANOID + +/** + * paranoid_check_volume - check volume information. + * @ubi: UBI device description object + * @vol_id: volume ID + */ +static void paranoid_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; + const struct ubi_volume *vol; + long long n; + const char *name; + + spin_lock(&ubi->volumes_lock); + reserved_pebs = be32_to_cpu(ubi->vtbl[vol_id].reserved_pebs); + vol = ubi->volumes[idx]; + + if (!vol) { + if (reserved_pebs) { + ubi_err("no volume info, but volume exists"); + 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; + } + + if (vol->reserved_pebs < 0 || vol->alignment < 0 || vol->data_pad < 0 || + vol->name_len < 0) { + ubi_err("negative values"); + goto fail; + } + if (vol->alignment > ubi->leb_size || vol->alignment == 0) { + ubi_err("bad alignment"); + goto fail; + } + + n = vol->alignment & (ubi->min_io_size - 1); + if (vol->alignment != 1 && n) { + ubi_err("alignment is not multiple of min I/O unit"); + goto fail; + } + + n = ubi->leb_size % vol->alignment; + if (vol->data_pad != n) { + ubi_err("bad data_pad, has to be %lld", n); + goto fail; + } + + if (vol->vol_type != UBI_DYNAMIC_VOLUME && + vol->vol_type != UBI_STATIC_VOLUME) { + ubi_err("bad vol_type"); + goto fail; + } + + if (vol->upd_marker && vol->corrupted) { + dbg_err("update marker and corrupted simultaneously"); + goto fail; + } + + if (vol->reserved_pebs > ubi->good_peb_count) { + ubi_err("too large reserved_pebs"); + goto fail; + } + + n = ubi->leb_size - vol->data_pad; + if (vol->usable_leb_size != ubi->leb_size - vol->data_pad) { + ubi_err("bad usable_leb_size, has to be %lld", n); + goto fail; + } + + if (vol->name_len > UBI_VOL_NAME_MAX) { + ubi_err("too long volume name, max is %d", UBI_VOL_NAME_MAX); + 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); + goto fail; + } + + n = (long long)vol->used_ebs * vol->usable_leb_size; + if (vol->vol_type == UBI_DYNAMIC_VOLUME) { + if (vol->corrupted) { + ubi_err("corrupted dynamic volume"); + goto fail; + } + if (vol->used_ebs != vol->reserved_pebs) { + ubi_err("bad used_ebs"); + goto fail; + } + if (vol->last_eb_bytes != vol->usable_leb_size) { + ubi_err("bad last_eb_bytes"); + goto fail; + } + if (vol->used_bytes != n) { + ubi_err("bad used_bytes"); + goto fail; + } + } else { + if (vol->used_ebs < 0 || vol->used_ebs > vol->reserved_pebs) { + ubi_err("bad used_ebs"); + goto fail; + } + if (vol->last_eb_bytes < 0 || + vol->last_eb_bytes > vol->usable_leb_size) { + ubi_err("bad last_eb_bytes"); + goto fail; + } + if (vol->used_bytes < 0 || vol->used_bytes > n || + vol->used_bytes < n - vol->usable_leb_size) { + ubi_err("bad used_bytes"); + goto fail; + } + } + + alignment = be32_to_cpu(ubi->vtbl[vol_id].alignment); + data_pad = be32_to_cpu(ubi->vtbl[vol_id].data_pad); + name_len = be16_to_cpu(ubi->vtbl[vol_id].name_len); + upd_marker = ubi->vtbl[vol_id].upd_marker; + name = &ubi->vtbl[vol_id].name[0]; + if (ubi->vtbl[vol_id].vol_type == UBI_VID_DYNAMIC) + vol_type = UBI_DYNAMIC_VOLUME; + else + vol_type = UBI_STATIC_VOLUME; + + 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)) { + ubi_err("volume info is different"); + goto fail; + } + + spin_unlock(&ubi->volumes_lock); + return; + +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); + spin_unlock(&ubi->volumes_lock); + BUG(); +} + +/** + * paranoid_check_volumes - check information about all volumes. + * @ubi: UBI device description object + */ +static void paranoid_check_volumes(struct ubi_device *ubi) +{ + int i; + + for (i = 0; i < ubi->vtbl_slots; i++) + paranoid_check_volume(ubi, i); +} +#endif diff --git a/drivers/mtd/ubi/vtbl.c b/drivers/mtd/ubi/vtbl.c new file mode 100644 index 0000000000..765c8113e5 --- /dev/null +++ b/drivers/mtd/ubi/vtbl.c @@ -0,0 +1,837 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * Copyright (c) Nokia Corporation, 2006, 2007 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Artem Bityutskiy (Битюцкий Артём) + */ + +/* + * This file includes volume table manipulation code. The volume table is an + * on-flash table containing volume meta-data like name, number of reserved + * physical eraseblocks, type, etc. The volume table is stored in the so-called + * "layout volume". + * + * The layout volume is an internal volume which is organized as follows. It + * consists of two logical eraseblocks - LEB 0 and LEB 1. Each logical + * eraseblock stores one volume table copy, i.e. LEB 0 and LEB 1 duplicate each + * other. This redundancy guarantees robustness to unclean reboots. The volume + * table is basically an array of volume table records. Each record contains + * full information about the volume and protected by a CRC checksum. + * + * The volume table is changed, it is first changed in RAM. Then LEB 0 is + * erased, and the updated volume table is written back to LEB 0. Then same for + * 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. + * + * 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 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. + * + * 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 + * after the update operation is finished, the update marker is cleared. So if + * the update operation was interrupted (e.g. by an unclean reboot) - the + * update marker is still there and we know that the volume's contents is + * damaged. + */ + +#ifdef UBI_LINUX +#include <linux/crc32.h> +#include <linux/err.h> +#include <asm/div64.h> +#endif + +#include "ubi-barebox.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 + +/* Empty volume table record */ +static struct ubi_vtbl_record empty_vtbl_record; + +/** + * ubi_change_vtbl_record - change volume table record. + * @ubi: UBI device description object + * @idx: table index to change + * @vtbl_rec: new volume table record + * + * This function changes volume table record @idx. If @vtbl_rec is %NULL, empty + * volume table record is written. The caller does not have to calculate CRC of + * the record as it is done by this function. Returns zero in case of success + * and a negative error code in case of failure. + */ +int ubi_change_vtbl_record(struct ubi_device *ubi, int idx, + struct ubi_vtbl_record *vtbl_rec) +{ + int i, err; + uint32_t crc; + struct ubi_volume *layout_vol; + + ubi_assert(idx >= 0 && idx < ubi->vtbl_slots); + layout_vol = ubi->volumes[vol_id2idx(ubi, UBI_LAYOUT_VOLUME_ID)]; + + if (!vtbl_rec) + vtbl_rec = &empty_vtbl_record; + else { + crc = crc32(UBI_CRC32_INIT, vtbl_rec, UBI_VTBL_RECORD_SIZE_CRC); + vtbl_rec->crc = cpu_to_be32(crc); + } + + memcpy(&ubi->vtbl[idx], vtbl_rec, sizeof(struct ubi_vtbl_record)); + 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, UBI_LONGTERM); + if (err) + return err; + } + + paranoid_vtbl_check(ubi); + return 0; +} + +/** + * vtbl_check - check if volume table is not corrupted and contains sensible + * data. + * @ubi: UBI device description object + * @vtbl: volume table + * + * This function returns zero if @vtbl is all right, %1 if CRC is incorrect, + * and %-EINVAL if it contains inconsistent data. + */ +static int vtbl_check(const struct ubi_device *ubi, + const struct ubi_vtbl_record *vtbl) +{ + int i, n, reserved_pebs, alignment, data_pad, vol_type, name_len; + int upd_marker, err; + uint32_t crc; + const char *name; + + for (i = 0; i < ubi->vtbl_slots; i++) { + cond_resched(); + + reserved_pebs = be32_to_cpu(vtbl[i].reserved_pebs); + alignment = be32_to_cpu(vtbl[i].alignment); + data_pad = be32_to_cpu(vtbl[i].data_pad); + 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]; + + 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); + return 1; + } + + if (reserved_pebs == 0) { + if (memcmp(&vtbl[i], &empty_vtbl_record, + UBI_VTBL_RECORD_SIZE)) { + err = 2; + goto bad; + } + continue; + } + + if (reserved_pebs < 0 || alignment < 0 || data_pad < 0 || + name_len < 0) { + err = 3; + goto bad; + } + + if (alignment > ubi->leb_size || alignment == 0) { + err = 4; + goto bad; + } + + n = alignment & (ubi->min_io_size - 1); + if (alignment != 1 && n) { + err = 5; + goto bad; + } + + n = ubi->leb_size % alignment; + if (data_pad != n) { + dbg_err("bad data_pad, has to be %d", n); + err = 6; + goto bad; + } + + if (vol_type != UBI_VID_DYNAMIC && vol_type != UBI_VID_STATIC) { + err = 7; + goto bad; + } + + if (upd_marker != 0 && upd_marker != 1) { + err = 8; + goto bad; + } + + if (reserved_pebs > ubi->good_peb_count) { + dbg_err("too large reserved_pebs, good PEBs %d", + ubi->good_peb_count); + err = 9; + goto bad; + } + + if (name_len > UBI_VOL_NAME_MAX) { + err = 10; + goto bad; + } + + if (name[0] == '\0') { + err = 11; + goto bad; + } + + if (name_len != strnlen(name, name_len + 1)) { + err = 12; + goto bad; + } + } + + /* Checks that all names are unique */ + for (i = 0; i < ubi->vtbl_slots - 1; i++) { + for (n = i + 1; n < ubi->vtbl_slots; n++) { + int len1 = be16_to_cpu(vtbl[i].name_len); + 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); + return -EINVAL; + } + } + } + + return 0; + +bad: + ubi_err("volume table check failed: record %d, error %d", i, err); + ubi_dbg_dump_vtbl_record(&vtbl[i], i); + return -EINVAL; +} + +/** + * create_vtbl - create a copy of volume table. + * @ubi: UBI device description object + * @si: scanning 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, + 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; + + ubi_msg("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); + goto out_free; + } + + vid_hdr->vol_type = UBI_VID_DYNAMIC; + 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); + + /* The EC header is already there, write the VID header */ + err = ubi_io_write_vid_hdr(ubi, new_seb->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); + 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()'. + */ + err = ubi_scan_add_used(ubi, si, new_seb->pnum, new_seb->ec, + vid_hdr, 0); + kfree(new_seb); + ubi_free_vid_hdr(ubi, vid_hdr); + return err; + +write_error: + if (err == -EIO && ++tries <= 5) { + /* + * Probably this physical eraseblock went bad, try to pick + * another one. + */ + list_add_tail(&new_seb->u.list, &si->corr); + goto retry; + } + kfree(new_seb); +out_free: + ubi_free_vid_hdr(ubi, vid_hdr); + return err; + +} + +/** + * process_lvol - process the layout volume. + * @ubi: UBI device description object + * @si: scanning information + * @sv: layout volume scanning 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) +{ + int err; + struct rb_node *rb; + struct ubi_scan_leb *seb; + struct ubi_vtbl_record *leb[UBI_LAYOUT_VOLUME_EBS] = { NULL, NULL }; + int leb_corrupted[UBI_LAYOUT_VOLUME_EBS] = {1, 1}; + + /* + * UBI goes through the following steps when it changes the layout + * volume: + * a. erase LEB 0; + * b. write new data to LEB 0; + * c. erase LEB 1; + * d. write new data to LEB 1. + * + * Before the change, both LEBs contain the same data. + * + * Due to unclean reboots, the contents of LEB 0 may be lost, but there + * should LEB 1. So it is OK if LEB 0 is corrupted while LEB 1 is not. + * Similarly, LEB 1 may be lost, but there should be LEB 0. And + * finally, unclean reboots may result in a situation when neither LEB + * 0 nor LEB 1 are corrupted, but they are different. In this case, LEB + * 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 + * 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"); + + /* 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]) { + 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, + ubi->vtbl_size); + if (err == UBI_IO_BITFLIPS || err == -EBADMSG) + /* + * Scrub the PEB later. Note, -EBADMSG indicates an + * 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 + * the PEB will be recovered from the second copy, and + * seb->scrub will be cleared in + * 'ubi_scan_add_used()'. + */ + seb->scrub = 1; + else if (err) + goto out_free; + } + + err = -EINVAL; + if (leb[0]) { + leb_corrupted[0] = vtbl_check(ubi, leb[0]); + if (leb_corrupted[0] < 0) + goto out_free; + } + + if (!leb_corrupted[0]) { + /* LEB 0 is OK */ + if (leb[1]) + 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]); + if (err) + goto out_free; + ubi_msg("volume table was restored"); + } + + /* Both LEB 1 and LEB 2 are OK and consistent */ + vfree(leb[1]); + return leb[0]; + } else { + /* LEB 0 is corrupted or does not exist */ + if (leb[1]) { + leb_corrupted[1] = vtbl_check(ubi, leb[1]); + if (leb_corrupted[1] < 0) + goto out_free; + } + if (leb_corrupted[1]) { + /* Both LEB 0 and LEB 1 are corrupted */ + ubi_err("both volume tables are corrupted"); + goto out_free; + } + + ubi_warn("volume table copy #1 is corrupted"); + err = create_vtbl(ubi, si, 0, leb[1]); + if (err) + goto out_free; + ubi_msg("volume table was restored"); + + vfree(leb[0]); + return leb[1]; + } + +out_free: + vfree(leb[0]); + vfree(leb[1]); + return ERR_PTR(err); +} + +/** + * create_empty_lvol - create empty layout volume. + * @ubi: UBI device description object + * @si: scanning 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) +{ + int i; + struct ubi_vtbl_record *vtbl; + + vtbl = vmalloc(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); + + for (i = 0; i < UBI_LAYOUT_VOLUME_EBS; i++) { + int err; + + err = create_vtbl(ubi, si, i, vtbl); + if (err) { + vfree(vtbl); + return ERR_PTR(err); + } + } + + return vtbl; +} + +/** + * init_volumes - initialize volume information for existing volumes. + * @ubi: UBI device description object + * @si: 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, + const struct ubi_vtbl_record *vtbl) +{ + int i, reserved_pebs = 0; + struct ubi_scan_volume *sv; + struct ubi_volume *vol; + + for (i = 0; i < ubi->vtbl_slots; i++) { + cond_resched(); + + if (be32_to_cpu(vtbl[i].reserved_pebs) == 0) + continue; /* Empty record */ + + vol = kzalloc(sizeof(struct ubi_volume), GFP_KERNEL); + if (!vol) + return -ENOMEM; + + vol->reserved_pebs = be32_to_cpu(vtbl[i].reserved_pebs); + vol->alignment = be32_to_cpu(vtbl[i].alignment); + vol->data_pad = be32_to_cpu(vtbl[i].data_pad); + vol->vol_type = vtbl[i].vol_type == UBI_VID_DYNAMIC ? + UBI_DYNAMIC_VOLUME : UBI_STATIC_VOLUME; + vol->name_len = be16_to_cpu(vtbl[i].name_len); + vol->usable_leb_size = ubi->leb_size - vol->data_pad; + memcpy(vol->name, vtbl[i].name, vol->name_len); + vol->name[vol->name_len] = '\0'; + vol->vol_id = i; + + 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); + kfree(vol); + return -EINVAL; + } + + ubi->autoresize_vol_id = i; + } + + ubi_assert(!ubi->volumes[i]); + ubi->volumes[i] = vol; + ubi->vol_count += 1; + vol->ubi = ubi; + reserved_pebs += vol->reserved_pebs; + + /* + * In case of dynamic volume UBI knows nothing about how many + * data is stored there. So assume the whole volume is used. + */ + if (vol->vol_type == UBI_DYNAMIC_VOLUME) { + vol->used_ebs = vol->reserved_pebs; + vol->last_eb_bytes = vol->usable_leb_size; + vol->used_bytes = + (long long)vol->used_ebs * vol->usable_leb_size; + continue; + } + + /* Static volumes only */ + sv = ubi_scan_find_sv(si, i); + if (!sv) { + /* + * No eraseblocks belonging to this volume found. We + * don't actually know whether this static volume is + * completely corrupted or just contains no data. And + * we cannot know this as long as data size is not + * stored on flash. So we just assume the volume is + * empty. FIXME: this should be handled. + */ + continue; + } + + if (sv->leb_count != sv->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); + vol->corrupted = 1; + continue; + } + + vol->used_ebs = sv->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; + } + + /* And add the layout volume */ + vol = kzalloc(sizeof(struct ubi_volume), GFP_KERNEL); + if (!vol) + return -ENOMEM; + + vol->reserved_pebs = UBI_LAYOUT_VOLUME_EBS; + vol->alignment = 1; + 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); + vol->usable_leb_size = ubi->leb_size; + vol->used_ebs = vol->reserved_pebs; + vol->last_eb_bytes = vol->reserved_pebs; + vol->used_bytes = + (long long)vol->used_ebs * (ubi->leb_size - vol->data_pad); + vol->vol_id = UBI_LAYOUT_VOLUME_ID; + vol->ref_count = 1; + + ubi_assert(!ubi->volumes[i]); + ubi->volumes[vol_id2idx(ubi, vol->vol_id)] = vol; + reserved_pebs += vol->reserved_pebs; + ubi->vol_count += 1; + vol->ubi = ubi; + + if (reserved_pebs > ubi->avail_pebs) + ubi_err("not enough PEBs, required %d, available %d", + reserved_pebs, ubi->avail_pebs); + ubi->rsvd_pebs += reserved_pebs; + ubi->avail_pebs -= reserved_pebs; + + return 0; +} + +/** + * check_sv - check volume scanning information. + * @vol: UBI volume description object + * @sv: volume scanning information + * + * This function returns zero if the volume scanning 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) +{ + int err; + + if (sv->highest_lnum >= vol->reserved_pebs) { + err = 1; + goto bad; + } + if (sv->leb_count > vol->reserved_pebs) { + err = 2; + goto bad; + } + if (sv->vol_type != vol->vol_type) { + err = 3; + goto bad; + } + if (sv->used_ebs > vol->reserved_pebs) { + err = 4; + goto bad; + } + if (sv->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); + return -EINVAL; +} + +/** + * check_scanning_info - check that scanning information. + * @ubi: UBI device description object + * @si: scanning 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 + * information is OK and %-EINVAL if it is not. + */ +static int check_scanning_info(const struct ubi_device *ubi, + struct ubi_scan_info *si) +{ + int err, i; + struct ubi_scan_volume *sv; + 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); + 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); + return -EINVAL; + } + + for (i = 0; i < ubi->vtbl_slots + UBI_INT_VOL_COUNT; i++) { + cond_resched(); + + sv = ubi_scan_find_sv(si, i); + vol = ubi->volumes[i]; + if (!vol) { + if (sv) + ubi_scan_rm_volume(si, sv); + continue; + } + + if (vol->reserved_pebs == 0) { + ubi_assert(i < ubi->vtbl_slots); + + if (!sv) + continue; + + /* + * During scanning 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); + if (err) + return err; + } + } + + return 0; +} + +/** + * ubi_read_volume_table - read volume table. + * information. + * @ubi: UBI device description object + * @si: scanning 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 i, err; + struct ubi_scan_volume *sv; + + empty_vtbl_record.crc = cpu_to_be32(0xf116c36b); + + /* + * The number of supported volumes is limited by the eraseblock size + * and by the UBI_MAX_VOLUMES constant. + */ + ubi->vtbl_slots = ubi->leb_size / UBI_VTBL_RECORD_SIZE; + if (ubi->vtbl_slots > UBI_MAX_VOLUMES) + ubi->vtbl_slots = UBI_MAX_VOLUMES; + + 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) { + /* + * No logical eraseblocks belonging to the layout volume were + * found. This could mean that the flash is just empty. In + * this case we create empty layout volume. + * + * 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 (IS_ERR(ubi->vtbl)) + return PTR_ERR(ubi->vtbl); + } else { + ubi_err("the layout volume was not found"); + return -EINVAL; + } + } else { + if (sv->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); + return -EINVAL; + } + + ubi->vtbl = process_lvol(ubi, si, sv); + if (IS_ERR(ubi->vtbl)) + return PTR_ERR(ubi->vtbl); + } + + ubi->avail_pebs = ubi->good_peb_count; + + /* + * The layout volume is OK, initialize the corresponding in-RAM data + * structures. + */ + err = init_volumes(ubi, si, ubi->vtbl); + if (err) + goto out_free; + + /* + * Get sure that the scanning information is consistent to the + * information stored in the volume table. + */ + err = check_scanning_info(ubi, si); + if (err) + goto out_free; + + return 0; + +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; + } + return err; +} + +#ifdef CONFIG_MTD_UBI_DEBUG_PARANOID + +/** + * paranoid_vtbl_check - check volume table. + * @ubi: UBI device description object + */ +static void paranoid_vtbl_check(const struct ubi_device *ubi) +{ + if (vtbl_check(ubi, ubi->vtbl)) { + ubi_err("paranoid check failed"); + BUG(); + } +} + +#endif /* CONFIG_MTD_UBI_DEBUG_PARANOID */ diff --git a/drivers/mtd/ubi/wl.c b/drivers/mtd/ubi/wl.c new file mode 100644 index 0000000000..137e600e13 --- /dev/null +++ b/drivers/mtd/ubi/wl.c @@ -0,0 +1,1675 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: Artem Bityutskiy (Битюцкий Артём), Thomas Gleixner + */ + +/* + * UBI wear-leveling unit. + * + * This unit is responsible for wear-leveling. It works in terms of physical + * eraseblocks and erase counters and knows nothing about logical eraseblocks, + * volumes, etc. From this unit's perspective all physical eraseblocks are of + * two types - used and free. Used physical eraseblocks are those that were + * "get" by the 'ubi_wl_get_peb()' function, and free physical eraseblocks are + * those that were put by the 'ubi_wl_put_peb()' function. + * + * Physical eraseblocks returned by 'ubi_wl_get_peb()' have only erase counter + * header. The rest of the physical eraseblock contains only 0xFF bytes. + * + * When physical eraseblocks are returned to the WL unit by means of the + * 'ubi_wl_put_peb()' function, they are scheduled for erasure. The erasure is + * done asynchronously in context of the per-UBI device background thread, + * which is also managed by the WL unit. + * + * The wear-leveling is ensured by means of moving the contents of used + * physical eraseblocks with low erase counter to free physical eraseblocks + * with high erase counter. + * + * The 'ubi_wl_get_peb()' function accepts data type hints which help to pick + * an "optimal" physical eraseblock. For example, when it is known that the + * physical eraseblock will be "put" soon because it contains short-term data, + * the WL unit may pick a free physical eraseblock with low erase counter, and + * so forth. + * + * If the WL unit fails to erase a physical eraseblock, it marks it as bad. + * + * This unit is also responsible for scrubbing. If a bit-flip is detected in a + * physical eraseblock, it has to be moved. Technically this is the same as + * moving it for wear-leveling reasons. + * + * As it was said, for the UBI unit all physical eraseblocks are either "free" + * or "used". Free eraseblock are kept in the @wl->free RB-tree, while used + * eraseblocks are kept in a set of different RB-trees: @wl->used, + * @wl->prot.pnum, @wl->prot.aec, and @wl->scrub. + * + * Note, in this implementation, we keep a small in-RAM object for each physical + * eraseblock. This is surely not a scalable solution. But it appears to be good + * enough for moderately large flashes and it is simple. In future, one may + * re-work this unit and make it more scalable. + * + * At the moment this unit does not utilize the sequence number, which was + * introduced relatively recently. But it would be wise to do this because the + * sequence number of a logical eraseblock characterizes how old is it. For + * example, when we move a PEB with low erase counter, and we need to pick the + * target PEB, we pick a PEB with the highest EC if our PEB is "old" and we + * pick target PEB with an average EC if our PEB is not very "old". This is a + * room for future re-works of the WL unit. + * + * FIXME: looks too complex, should be simplified (later). + */ + +#ifdef UBI_LINUX +#include <linux/slab.h> +#include <linux/crc32.h> +#include <linux/freezer.h> +#include <linux/kthread.h> +#endif + +#include "ubi-barebox.h" +#include "ubi.h" + +/* Number of physical eraseblocks reserved for wear-leveling purposes */ +#define WL_RESERVED_PEBS 1 + +/* + * How many erase cycles are short term, unknown, and long term physical + * eraseblocks protected. + */ +#define ST_PROTECTION 16 +#define U_PROTECTION 10 +#define LT_PROTECTION 4 + +/* + * Maximum difference between two erase counters. If this threshold is + * exceeded, the WL unit starts moving data from used physical eraseblocks with + * low erase counter to free physical eraseblocks with high erase counter. + */ +#define UBI_WL_THRESHOLD CONFIG_MTD_UBI_WL_THRESHOLD + +/* + * When a physical eraseblock is moved, the WL unit has to pick the target + * physical eraseblock to move to. The simplest way would be just to pick the + * one with the highest erase counter. But in certain workloads this could lead + * to an unlimited wear of one or few physical eraseblock. Indeed, imagine a + * situation when the picked physical eraseblock is constantly erased after the + * data is written to it. So, we have a constant which limits the highest erase + * counter of the free physical eraseblock to pick. Namely, the WL unit does + * not pick eraseblocks with erase counter greater then the lowest erase + * counter plus %WL_FREE_MAX_DIFF. + */ +#define WL_FREE_MAX_DIFF (2*UBI_WL_THRESHOLD) + +/* + * Maximum number of consecutive background thread failures which is enough to + * switch to read-only mode. + */ +#define WL_MAX_FAILURES 32 + +/** + * struct ubi_wl_prot_entry - PEB protection entry. + * @rb_pnum: link in the @wl->prot.pnum RB-tree + * @rb_aec: link in the @wl->prot.aec RB-tree + * @abs_ec: the absolute erase counter value when the protection ends + * @e: the wear-leveling entry of the physical eraseblock under protection + * + * When the WL unit returns a physical eraseblock, the physical eraseblock is + * protected from being moved for some "time". For this reason, the physical + * eraseblock is not directly moved from the @wl->free tree to the @wl->used + * tree. There is one more tree in between where this physical eraseblock is + * temporarily stored (@wl->prot). + * + * All this protection stuff is needed because: + * o we don't want to move physical eraseblocks just after we have given them + * to the user; instead, we first want to let users fill them up with data; + * + * o there is a chance that the user will put the physical eraseblock very + * soon, so it makes sense not to move it for some time, but wait; this is + * especially important in case of "short term" physical eraseblocks. + * + * Physical eraseblocks stay protected only for limited time. But the "time" is + * measured in erase cycles in this case. This is implemented with help of the + * absolute erase counter (@wl->abs_ec). When it reaches certain value, the + * physical eraseblocks are moved from the protection trees (@wl->prot.*) to + * the @wl->used tree. + * + * Protected physical eraseblocks are searched by physical eraseblock number + * (when they are put) and by the absolute erase counter (to check if it is + * time to move them to the @wl->used tree). So there are actually 2 RB-trees + * storing the protected physical eraseblocks: @wl->prot.pnum and + * @wl->prot.aec. They are referred to as the "protection" trees. The + * first one is indexed by the physical eraseblock number. The second one is + * indexed by the absolute erase counter. Both trees store + * &struct ubi_wl_prot_entry objects. + * + * Each physical eraseblock has 2 main states: free and used. The former state + * corresponds to the @wl->free tree. The latter state is split up on several + * sub-states: + * o the WL movement is allowed (@wl->used tree); + * o the WL movement is temporarily prohibited (@wl->prot.pnum and + * @wl->prot.aec trees); + * o scrubbing is needed (@wl->scrub tree). + * + * Depending on the sub-state, wear-leveling entries of the used physical + * eraseblocks may be kept in one of those trees. + */ +struct ubi_wl_prot_entry { + struct rb_node rb_pnum; + struct rb_node rb_aec; + unsigned long long abs_ec; + struct ubi_wl_entry *e; +}; + +/** + * struct ubi_work - UBI work description data structure. + * @list: a link in the list of pending works + * @func: worker function + * @priv: private data of the worker function + * + * @e: physical eraseblock to erase + * @torture: if the physical eraseblock has to be tortured + * + * The @func pointer points to the worker function. If the @cancel argument is + * not zero, the worker has to free the resources and exit immediately. The + * worker has to return zero in case of success and a negative error code in + * case of failure. + */ +struct ubi_work { + struct list_head list; + int (*func)(struct ubi_device *ubi, struct ubi_work *wrk, int cancel); + /* The below fields are only relevant to erasure works */ + struct ubi_wl_entry *e; + int torture; +}; + +#ifdef CONFIG_MTD_UBI_DEBUG_PARANOID +static int paranoid_check_ec(struct ubi_device *ubi, int pnum, int ec); +static int paranoid_check_in_wl_tree(struct ubi_wl_entry *e, + struct rb_root *root); +#else +#define paranoid_check_ec(ubi, pnum, ec) 0 +#define paranoid_check_in_wl_tree(e, root) +#endif + +/** + * wl_tree_add - add a wear-leveling entry to a WL RB-tree. + * @e: the wear-leveling entry to add + * @root: the root of the tree + * + * Note, we use (erase counter, physical eraseblock number) pairs as keys in + * the @ubi->used and @ubi->free RB-trees. + */ +static void wl_tree_add(struct ubi_wl_entry *e, struct rb_root *root) +{ + struct rb_node **p, *parent = NULL; + + p = &root->rb_node; + while (*p) { + struct ubi_wl_entry *e1; + + parent = *p; + e1 = rb_entry(parent, struct ubi_wl_entry, rb); + + if (e->ec < e1->ec) + p = &(*p)->rb_left; + else if (e->ec > e1->ec) + p = &(*p)->rb_right; + else { + ubi_assert(e->pnum != e1->pnum); + if (e->pnum < e1->pnum) + p = &(*p)->rb_left; + else + p = &(*p)->rb_right; + } + } + + rb_link_node(&e->rb, parent, p); + rb_insert_color(&e->rb, root); +} + +/** + * do_work - do one pending work. + * @ubi: UBI device description object + * + * This function returns zero in case of success and a negative error code in + * case of failure. + */ +static int do_work(struct ubi_device *ubi) +{ + int err; + struct ubi_work *wrk; + + cond_resched(); + + /* + * @ubi->work_sem is used to synchronize with the workers. Workers take + * it in read mode, so many of them may be doing works at a time. But + * the queue flush code has to be sure the whole queue of works is + * done, and it takes the mutex in write mode. + */ + down_read(&ubi->work_sem); + spin_lock(&ubi->wl_lock); + if (list_empty(&ubi->works)) { + spin_unlock(&ubi->wl_lock); + up_read(&ubi->work_sem); + return 0; + } + + wrk = list_entry(ubi->works.next, struct ubi_work, list); + list_del(&wrk->list); + ubi->works_count -= 1; + ubi_assert(ubi->works_count >= 0); + spin_unlock(&ubi->wl_lock); + + /* + * Call the worker function. Do not touch the work structure + * after this call as it will have been freed or reused by that + * time by the worker function. + */ + err = wrk->func(ubi, wrk, 0); + if (err) + ubi_err("work failed with error code %d", err); + up_read(&ubi->work_sem); + + return err; +} + +/** + * produce_free_peb - produce a free physical eraseblock. + * @ubi: UBI device description object + * + * This function tries to make a free PEB by means of synchronous execution of + * pending works. This may be needed if, for example the background thread is + * disabled. Returns zero in case of success and a negative error code in case + * of failure. + */ +static int produce_free_peb(struct ubi_device *ubi) +{ + int err; + + spin_lock(&ubi->wl_lock); + while (!ubi->free.rb_node) { + spin_unlock(&ubi->wl_lock); + + dbg_wl("do one work synchronously"); + err = do_work(ubi); + if (err) + return err; + + spin_lock(&ubi->wl_lock); + } + spin_unlock(&ubi->wl_lock); + + return 0; +} + +/** + * in_wl_tree - check if wear-leveling entry is present in a WL RB-tree. + * @e: the wear-leveling entry to check + * @root: the root of the tree + * + * This function returns non-zero if @e is in the @root RB-tree and zero if it + * is not. + */ +static int in_wl_tree(struct ubi_wl_entry *e, struct rb_root *root) +{ + struct rb_node *p; + + p = root->rb_node; + while (p) { + struct ubi_wl_entry *e1; + + e1 = rb_entry(p, struct ubi_wl_entry, rb); + + if (e->pnum == e1->pnum) { + ubi_assert(e == e1); + return 1; + } + + if (e->ec < e1->ec) + p = p->rb_left; + else if (e->ec > e1->ec) + p = p->rb_right; + else { + ubi_assert(e->pnum != e1->pnum); + if (e->pnum < e1->pnum) + p = p->rb_left; + else + p = p->rb_right; + } + } + + return 0; +} + +/** + * prot_tree_add - add physical eraseblock to protection trees. + * @ubi: UBI device description object + * @e: the physical eraseblock to add + * @pe: protection entry object to use + * @abs_ec: absolute erase counter value when this physical eraseblock has + * to be removed from the protection trees. + * + * @wl->lock has to be locked. + */ +static void prot_tree_add(struct ubi_device *ubi, struct ubi_wl_entry *e, + struct ubi_wl_prot_entry *pe, int abs_ec) +{ + struct rb_node **p, *parent = NULL; + struct ubi_wl_prot_entry *pe1; + + pe->e = e; + pe->abs_ec = ubi->abs_ec + abs_ec; + + p = &ubi->prot.pnum.rb_node; + while (*p) { + parent = *p; + pe1 = rb_entry(parent, struct ubi_wl_prot_entry, rb_pnum); + + if (e->pnum < pe1->e->pnum) + p = &(*p)->rb_left; + else + p = &(*p)->rb_right; + } + rb_link_node(&pe->rb_pnum, parent, p); + rb_insert_color(&pe->rb_pnum, &ubi->prot.pnum); + + p = &ubi->prot.aec.rb_node; + parent = NULL; + while (*p) { + parent = *p; + pe1 = rb_entry(parent, struct ubi_wl_prot_entry, rb_aec); + + if (pe->abs_ec < pe1->abs_ec) + p = &(*p)->rb_left; + else + p = &(*p)->rb_right; + } + rb_link_node(&pe->rb_aec, parent, p); + rb_insert_color(&pe->rb_aec, &ubi->prot.aec); +} + +/** + * find_wl_entry - find wear-leveling entry closest to certain erase counter. + * @root: the RB-tree where to look for + * @max: highest possible erase counter + * + * This function looks for a wear leveling entry with erase counter closest to + * @max and less then @max. + */ +static struct ubi_wl_entry *find_wl_entry(struct rb_root *root, int max) +{ + struct rb_node *p; + struct ubi_wl_entry *e; + + e = rb_entry(rb_first(root), struct ubi_wl_entry, rb); + max += e->ec; + + p = root->rb_node; + while (p) { + struct ubi_wl_entry *e1; + + e1 = rb_entry(p, struct ubi_wl_entry, rb); + if (e1->ec >= max) + p = p->rb_left; + else { + p = p->rb_right; + e = e1; + } + } + + return e; +} + +/** + * ubi_wl_get_peb - get a physical eraseblock. + * @ubi: UBI device description object + * @dtype: type of data which will be stored in this physical eraseblock + * + * This function returns a physical eraseblock in case of success and a + * negative error code in case of failure. Might sleep. + */ +int ubi_wl_get_peb(struct ubi_device *ubi, int dtype) +{ + int err, protect, medium_ec; + struct ubi_wl_entry *e, *first, *last; + struct ubi_wl_prot_entry *pe; + + ubi_assert(dtype == UBI_LONGTERM || dtype == UBI_SHORTTERM || + dtype == UBI_UNKNOWN); + + pe = kmalloc(sizeof(struct ubi_wl_prot_entry), GFP_NOFS); + if (!pe) + return -ENOMEM; + +retry: + spin_lock(&ubi->wl_lock); + if (!ubi->free.rb_node) { + if (ubi->works_count == 0) { + ubi_assert(list_empty(&ubi->works)); + ubi_err("no free eraseblocks"); + spin_unlock(&ubi->wl_lock); + kfree(pe); + return -ENOSPC; + } + spin_unlock(&ubi->wl_lock); + + err = produce_free_peb(ubi); + if (err < 0) { + kfree(pe); + return err; + } + goto retry; + } + + switch (dtype) { + case UBI_LONGTERM: + /* + * For long term data we pick a physical eraseblock + * with high erase counter. But the highest erase + * counter we can pick is bounded by the the lowest + * erase counter plus %WL_FREE_MAX_DIFF. + */ + e = find_wl_entry(&ubi->free, WL_FREE_MAX_DIFF); + protect = LT_PROTECTION; + break; + case UBI_UNKNOWN: + /* + * For unknown data we pick a physical eraseblock with + * medium erase counter. But we by no means can pick a + * physical eraseblock with erase counter greater or + * equivalent than the lowest erase counter plus + * %WL_FREE_MAX_DIFF. + */ + first = rb_entry(rb_first(&ubi->free), + struct ubi_wl_entry, rb); + last = rb_entry(rb_last(&ubi->free), + struct ubi_wl_entry, rb); + + if (last->ec - first->ec < WL_FREE_MAX_DIFF) + e = rb_entry(ubi->free.rb_node, + struct ubi_wl_entry, rb); + else { + medium_ec = (first->ec + WL_FREE_MAX_DIFF)/2; + e = find_wl_entry(&ubi->free, medium_ec); + } + protect = U_PROTECTION; + break; + case UBI_SHORTTERM: + /* + * For short term data we pick a physical eraseblock + * with the lowest erase counter as we expect it will + * be erased soon. + */ + e = rb_entry(rb_first(&ubi->free), + struct ubi_wl_entry, rb); + protect = ST_PROTECTION; + break; + default: + protect = 0; + e = NULL; + BUG(); + } + + /* + * Move the physical eraseblock to the protection trees where it will + * be protected from being moved for some time. + */ + paranoid_check_in_wl_tree(e, &ubi->free); + rb_erase(&e->rb, &ubi->free); + prot_tree_add(ubi, e, pe, protect); + + dbg_wl("PEB %d EC %d, protection %d", e->pnum, e->ec, protect); + spin_unlock(&ubi->wl_lock); + + return e->pnum; +} + +/** + * prot_tree_del - remove a physical eraseblock from the protection trees + * @ubi: UBI device description object + * @pnum: the physical eraseblock to remove + * + * This function returns PEB @pnum from the protection trees and returns zero + * in case of success and %-ENODEV if the PEB was not found in the protection + * trees. + */ +static int prot_tree_del(struct ubi_device *ubi, int pnum) +{ + struct rb_node *p; + struct ubi_wl_prot_entry *pe = NULL; + + p = ubi->prot.pnum.rb_node; + while (p) { + + pe = rb_entry(p, struct ubi_wl_prot_entry, rb_pnum); + + if (pnum == pe->e->pnum) + goto found; + + if (pnum < pe->e->pnum) + p = p->rb_left; + else + p = p->rb_right; + } + + return -ENODEV; + +found: + ubi_assert(pe->e->pnum == pnum); + rb_erase(&pe->rb_aec, &ubi->prot.aec); + rb_erase(&pe->rb_pnum, &ubi->prot.pnum); + kfree(pe); + return 0; +} + +/** + * sync_erase - synchronously erase a physical eraseblock. + * @ubi: UBI device description object + * @e: the the physical eraseblock to erase + * @torture: if the physical eraseblock has to be tortured + * + * This function returns zero in case of success and a negative error code in + * case of failure. + */ +static int sync_erase(struct ubi_device *ubi, struct ubi_wl_entry *e, int torture) +{ + int err; + struct ubi_ec_hdr *ec_hdr; + unsigned long long ec = e->ec; + + dbg_wl("erase PEB %d, old EC %llu", e->pnum, ec); + + err = paranoid_check_ec(ubi, e->pnum, e->ec); + if (err > 0) + return -EINVAL; + + ec_hdr = kzalloc(ubi->ec_hdr_alsize, GFP_NOFS); + if (!ec_hdr) + return -ENOMEM; + + err = ubi_io_sync_erase(ubi, e->pnum, torture); + if (err < 0) + goto out_free; + + ec += err; + if (ec > UBI_MAX_ERASECOUNTER) { + /* + * Erase counter overflow. Upgrade UBI and use 64-bit + * erase counters internally. + */ + ubi_err("erase counter overflow at PEB %d, EC %llu", + e->pnum, ec); + err = -EINVAL; + goto out_free; + } + + dbg_wl("erased PEB %d, new EC %llu", e->pnum, ec); + + ec_hdr->ec = cpu_to_be64(ec); + + err = ubi_io_write_ec_hdr(ubi, e->pnum, ec_hdr); + if (err) + goto out_free; + + e->ec = ec; + spin_lock(&ubi->wl_lock); + if (e->ec > ubi->max_ec) + ubi->max_ec = e->ec; + spin_unlock(&ubi->wl_lock); + +out_free: + kfree(ec_hdr); + return err; +} + +/** + * check_protection_over - check if it is time to stop protecting some + * physical eraseblocks. + * @ubi: UBI device description object + * + * This function is called after each erase operation, when the absolute erase + * counter is incremented, to check if some physical eraseblock have not to be + * protected any longer. These physical eraseblocks are moved from the + * protection trees to the used tree. + */ +static void check_protection_over(struct ubi_device *ubi) +{ + struct ubi_wl_prot_entry *pe; + + /* + * There may be several protected physical eraseblock to remove, + * process them all. + */ + while (1) { + spin_lock(&ubi->wl_lock); + if (!ubi->prot.aec.rb_node) { + spin_unlock(&ubi->wl_lock); + break; + } + + pe = rb_entry(rb_first(&ubi->prot.aec), + struct ubi_wl_prot_entry, rb_aec); + + if (pe->abs_ec > ubi->abs_ec) { + spin_unlock(&ubi->wl_lock); + break; + } + + dbg_wl("PEB %d protection over, abs_ec %llu, PEB abs_ec %llu", + pe->e->pnum, ubi->abs_ec, pe->abs_ec); + rb_erase(&pe->rb_aec, &ubi->prot.aec); + rb_erase(&pe->rb_pnum, &ubi->prot.pnum); + wl_tree_add(pe->e, &ubi->used); + spin_unlock(&ubi->wl_lock); + + kfree(pe); + cond_resched(); + } +} + +/** + * schedule_ubi_work - schedule a work. + * @ubi: UBI device description object + * @wrk: the work to schedule + * + * This function enqueues a work defined by @wrk to the tail of the pending + * works list. + */ +static void schedule_ubi_work(struct ubi_device *ubi, struct ubi_work *wrk) +{ + spin_lock(&ubi->wl_lock); + list_add_tail(&wrk->list, &ubi->works); + ubi_assert(ubi->works_count >= 0); + ubi->works_count += 1; + + /* + * U-Boot special: We have no bgt_thread in U-Boot! + * So just call do_work() here directly. + */ + do_work(ubi); + + spin_unlock(&ubi->wl_lock); +} + +static int erase_worker(struct ubi_device *ubi, struct ubi_work *wl_wrk, + int cancel); + +/** + * schedule_erase - schedule an erase work. + * @ubi: UBI device description object + * @e: the WL entry of the physical eraseblock to erase + * @torture: if the physical eraseblock has to be tortured + * + * This function returns zero in case of success and a %-ENOMEM in case of + * failure. + */ +static int schedule_erase(struct ubi_device *ubi, struct ubi_wl_entry *e, + int torture) +{ + struct ubi_work *wl_wrk; + + dbg_wl("schedule erasure of PEB %d, EC %d, torture %d", + e->pnum, e->ec, torture); + + wl_wrk = kmalloc(sizeof(struct ubi_work), GFP_NOFS); + if (!wl_wrk) + return -ENOMEM; + + wl_wrk->func = &erase_worker; + wl_wrk->e = e; + wl_wrk->torture = torture; + + schedule_ubi_work(ubi, wl_wrk); + return 0; +} + +/** + * wear_leveling_worker - wear-leveling worker function. + * @ubi: UBI device description object + * @wrk: the work object + * @cancel: non-zero if the worker has to free memory and exit + * + * This function copies a more worn out physical eraseblock to a less worn out + * one. Returns zero in case of success and a negative error code in case of + * failure. + */ +static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk, + int cancel) +{ + int err, put = 0, scrubbing = 0, protect = 0; + struct ubi_wl_prot_entry *uninitialized_var(pe); + struct ubi_wl_entry *e1, *e2; + struct ubi_vid_hdr *vid_hdr; + + kfree(wrk); + + if (cancel) + return 0; + + vid_hdr = ubi_zalloc_vid_hdr(ubi, GFP_NOFS); + if (!vid_hdr) + return -ENOMEM; + + mutex_lock(&ubi->move_mutex); + spin_lock(&ubi->wl_lock); + ubi_assert(!ubi->move_from && !ubi->move_to); + ubi_assert(!ubi->move_to_put); + + if (!ubi->free.rb_node || + (!ubi->used.rb_node && !ubi->scrub.rb_node)) { + /* + * No free physical eraseblocks? Well, they must be waiting in + * the queue to be erased. Cancel movement - it will be + * triggered again when a free physical eraseblock appears. + * + * No used physical eraseblocks? They must be temporarily + * protected from being moved. They will be moved to the + * @ubi->used tree later and the wear-leveling will be + * triggered again. + */ + dbg_wl("cancel WL, a list is empty: free %d, used %d", + !ubi->free.rb_node, !ubi->used.rb_node); + goto out_cancel; + } + + if (!ubi->scrub.rb_node) { + /* + * Now pick the least worn-out used physical eraseblock and a + * highly worn-out free physical eraseblock. If the erase + * counters differ much enough, start wear-leveling. + */ + e1 = rb_entry(rb_first(&ubi->used), struct ubi_wl_entry, rb); + e2 = find_wl_entry(&ubi->free, WL_FREE_MAX_DIFF); + + if (!(e2->ec - e1->ec >= UBI_WL_THRESHOLD)) { + dbg_wl("no WL needed: min used EC %d, max free EC %d", + e1->ec, e2->ec); + goto out_cancel; + } + paranoid_check_in_wl_tree(e1, &ubi->used); + rb_erase(&e1->rb, &ubi->used); + dbg_wl("move PEB %d EC %d to PEB %d EC %d", + e1->pnum, e1->ec, e2->pnum, e2->ec); + } else { + /* Perform scrubbing */ + scrubbing = 1; + e1 = rb_entry(rb_first(&ubi->scrub), struct ubi_wl_entry, rb); + e2 = find_wl_entry(&ubi->free, WL_FREE_MAX_DIFF); + paranoid_check_in_wl_tree(e1, &ubi->scrub); + rb_erase(&e1->rb, &ubi->scrub); + dbg_wl("scrub PEB %d to PEB %d", e1->pnum, e2->pnum); + } + + paranoid_check_in_wl_tree(e2, &ubi->free); + rb_erase(&e2->rb, &ubi->free); + ubi->move_from = e1; + ubi->move_to = e2; + spin_unlock(&ubi->wl_lock); + + /* + * Now we are going to copy physical eraseblock @e1->pnum to @e2->pnum. + * We so far do not know which logical eraseblock our physical + * eraseblock (@e1) belongs to. We have to read the volume identifier + * header first. + * + * Note, we are protected from this PEB being unmapped and erased. The + * 'ubi_wl_put_peb()' would wait for moving to be finished if the PEB + * which is being moved was unmapped. + */ + + err = ubi_io_read_vid_hdr(ubi, e1->pnum, vid_hdr, 0); + if (err && err != UBI_IO_BITFLIPS) { + if (err == UBI_IO_PEB_FREE) { + /* + * We are trying to move PEB without a VID header. UBI + * always write VID headers shortly after the PEB was + * given, so we have a situation when it did not have + * chance to write it down because it was preempted. + * Just re-schedule the work, so that next time it will + * likely have the VID header in place. + */ + dbg_wl("PEB %d has no VID header", e1->pnum); + goto out_not_moved; + } + + ubi_err("error %d while reading VID header from PEB %d", + err, e1->pnum); + if (err > 0) + err = -EIO; + goto out_error; + } + + err = ubi_eba_copy_leb(ubi, e1->pnum, e2->pnum, vid_hdr); + if (err) { + + if (err < 0) + goto out_error; + if (err == 1) + goto out_not_moved; + + /* + * For some reason the LEB was not moved - it might be because + * the volume is being deleted. We should prevent this PEB from + * being selected for wear-levelling movement for some "time", + * so put it to the protection tree. + */ + + dbg_wl("cancelled moving PEB %d", e1->pnum); + pe = kmalloc(sizeof(struct ubi_wl_prot_entry), GFP_NOFS); + if (!pe) { + err = -ENOMEM; + goto out_error; + } + + protect = 1; + } + + ubi_free_vid_hdr(ubi, vid_hdr); + spin_lock(&ubi->wl_lock); + if (protect) + prot_tree_add(ubi, e1, pe, protect); + if (!ubi->move_to_put) + wl_tree_add(e2, &ubi->used); + else + put = 1; + ubi->move_from = ubi->move_to = NULL; + ubi->move_to_put = ubi->wl_scheduled = 0; + spin_unlock(&ubi->wl_lock); + + if (put) { + /* + * Well, the target PEB was put meanwhile, schedule it for + * erasure. + */ + dbg_wl("PEB %d was put meanwhile, erase", e2->pnum); + err = schedule_erase(ubi, e2, 0); + if (err) + goto out_error; + } + + if (!protect) { + err = schedule_erase(ubi, e1, 0); + if (err) + goto out_error; + } + + + dbg_wl("done"); + mutex_unlock(&ubi->move_mutex); + return 0; + + /* + * For some reasons the LEB was not moved, might be an error, might be + * something else. @e1 was not changed, so return it back. @e2 might + * be changed, schedule it for erasure. + */ +out_not_moved: + ubi_free_vid_hdr(ubi, vid_hdr); + spin_lock(&ubi->wl_lock); + if (scrubbing) + wl_tree_add(e1, &ubi->scrub); + else + wl_tree_add(e1, &ubi->used); + ubi->move_from = ubi->move_to = NULL; + ubi->move_to_put = ubi->wl_scheduled = 0; + spin_unlock(&ubi->wl_lock); + + err = schedule_erase(ubi, e2, 0); + if (err) + goto out_error; + + mutex_unlock(&ubi->move_mutex); + return 0; + +out_error: + ubi_err("error %d while moving PEB %d to PEB %d", + err, e1->pnum, e2->pnum); + + ubi_free_vid_hdr(ubi, vid_hdr); + spin_lock(&ubi->wl_lock); + ubi->move_from = ubi->move_to = NULL; + ubi->move_to_put = ubi->wl_scheduled = 0; + spin_unlock(&ubi->wl_lock); + + kmem_cache_free(ubi_wl_entry_slab, e1); + kmem_cache_free(ubi_wl_entry_slab, e2); + ubi_ro_mode(ubi); + + mutex_unlock(&ubi->move_mutex); + return err; + +out_cancel: + ubi->wl_scheduled = 0; + spin_unlock(&ubi->wl_lock); + mutex_unlock(&ubi->move_mutex); + ubi_free_vid_hdr(ubi, vid_hdr); + return 0; +} + +/** + * ensure_wear_leveling - schedule wear-leveling if it is needed. + * @ubi: UBI device description object + * + * This function checks if it is time to start wear-leveling and schedules it + * if yes. This function returns zero in case of success and a negative error + * code in case of failure. + */ +static int ensure_wear_leveling(struct ubi_device *ubi) +{ + int err = 0; + struct ubi_wl_entry *e1; + struct ubi_wl_entry *e2; + struct ubi_work *wrk; + + spin_lock(&ubi->wl_lock); + if (ubi->wl_scheduled) + /* Wear-leveling is already in the work queue */ + goto out_unlock; + + /* + * If the ubi->scrub tree is not empty, scrubbing is needed, and the + * the WL worker has to be scheduled anyway. + */ + if (!ubi->scrub.rb_node) { + if (!ubi->used.rb_node || !ubi->free.rb_node) + /* No physical eraseblocks - no deal */ + goto out_unlock; + + /* + * We schedule wear-leveling only if the difference between the + * lowest erase counter of used physical eraseblocks and a high + * erase counter of free physical eraseblocks is greater then + * %UBI_WL_THRESHOLD. + */ + e1 = rb_entry(rb_first(&ubi->used), struct ubi_wl_entry, rb); + e2 = find_wl_entry(&ubi->free, WL_FREE_MAX_DIFF); + + if (!(e2->ec - e1->ec >= UBI_WL_THRESHOLD)) + goto out_unlock; + dbg_wl("schedule wear-leveling"); + } else + dbg_wl("schedule scrubbing"); + + ubi->wl_scheduled = 1; + spin_unlock(&ubi->wl_lock); + + wrk = kmalloc(sizeof(struct ubi_work), GFP_NOFS); + if (!wrk) { + err = -ENOMEM; + goto out_cancel; + } + + wrk->func = &wear_leveling_worker; + schedule_ubi_work(ubi, wrk); + return err; + +out_cancel: + spin_lock(&ubi->wl_lock); + ubi->wl_scheduled = 0; +out_unlock: + spin_unlock(&ubi->wl_lock); + return err; +} + +/** + * erase_worker - physical eraseblock erase worker function. + * @ubi: UBI device description object + * @wl_wrk: the work object + * @cancel: non-zero if the worker has to free memory and exit + * + * This function erases a physical eraseblock and perform torture testing if + * needed. It also takes care about marking the physical eraseblock bad if + * needed. Returns zero in case of success and a negative error code in case of + * failure. + */ +static int erase_worker(struct ubi_device *ubi, struct ubi_work *wl_wrk, + int cancel) +{ + struct ubi_wl_entry *e = wl_wrk->e; + int pnum = e->pnum, err, need; + + if (cancel) { + dbg_wl("cancel erasure of PEB %d EC %d", pnum, e->ec); + kfree(wl_wrk); + kmem_cache_free(ubi_wl_entry_slab, e); + return 0; + } + + dbg_wl("erase PEB %d EC %d", pnum, e->ec); + + err = sync_erase(ubi, e, wl_wrk->torture); + if (!err) { + /* Fine, we've erased it successfully */ + kfree(wl_wrk); + + spin_lock(&ubi->wl_lock); + ubi->abs_ec += 1; + wl_tree_add(e, &ubi->free); + spin_unlock(&ubi->wl_lock); + + /* + * One more erase operation has happened, take care about protected + * physical eraseblocks. + */ + check_protection_over(ubi); + + /* And take care about wear-leveling */ + err = ensure_wear_leveling(ubi); + return err; + } + + ubi_err("failed to erase PEB %d, error %d", pnum, err); + kfree(wl_wrk); + kmem_cache_free(ubi_wl_entry_slab, e); + + if (err == -EINTR || err == -ENOMEM || err == -EAGAIN || + err == -EBUSY) { + int err1; + + /* Re-schedule the LEB for erasure */ + err1 = schedule_erase(ubi, e, 0); + if (err1) { + err = err1; + goto out_ro; + } + return err; + } else if (err != -EIO) { + /* + * If this is not %-EIO, we have no idea what to do. Scheduling + * this physical eraseblock for erasure again would cause + * errors again and again. Well, lets switch to RO mode. + */ + goto out_ro; + } + + /* It is %-EIO, the PEB went bad */ + + if (!ubi->bad_allowed) { + ubi_err("bad physical eraseblock %d detected", pnum); + goto out_ro; + } + + spin_lock(&ubi->volumes_lock); + need = ubi->beb_rsvd_level - ubi->beb_rsvd_pebs + 1; + if (need > 0) { + need = ubi->avail_pebs >= need ? need : ubi->avail_pebs; + ubi->avail_pebs -= need; + ubi->rsvd_pebs += need; + ubi->beb_rsvd_pebs += need; + if (need > 0) + ubi_msg("reserve more %d PEBs", need); + } + + if (ubi->beb_rsvd_pebs == 0) { + spin_unlock(&ubi->volumes_lock); + ubi_err("no reserved physical eraseblocks"); + goto out_ro; + } + + spin_unlock(&ubi->volumes_lock); + ubi_msg("mark PEB %d as bad", pnum); + + err = ubi_io_mark_bad(ubi, pnum); + if (err) + goto out_ro; + + spin_lock(&ubi->volumes_lock); + ubi->beb_rsvd_pebs -= 1; + ubi->bad_peb_count += 1; + ubi->good_peb_count -= 1; + ubi_calculate_reserved(ubi); + if (ubi->beb_rsvd_pebs == 0) + ubi_warn("last PEB from the reserved pool was used"); + spin_unlock(&ubi->volumes_lock); + + return err; + +out_ro: + ubi_ro_mode(ubi); + return err; +} + +/** + * ubi_wl_put_peb - return a physical eraseblock to the wear-leveling unit. + * @ubi: UBI device description object + * @pnum: physical eraseblock to return + * @torture: if this physical eraseblock has to be tortured + * + * This function is called to return physical eraseblock @pnum to the pool of + * free physical eraseblocks. The @torture flag has to be set if an I/O error + * occurred to this @pnum and it has to be tested. This function returns zero + * in case of success, and a negative error code in case of failure. + */ +int ubi_wl_put_peb(struct ubi_device *ubi, int pnum, int torture) +{ + int err; + struct ubi_wl_entry *e; + + dbg_wl("PEB %d", pnum); + ubi_assert(pnum >= 0); + ubi_assert(pnum < ubi->peb_count); + +retry: + spin_lock(&ubi->wl_lock); + e = ubi->lookuptbl[pnum]; + if (e == ubi->move_from) { + /* + * User is putting the physical eraseblock which was selected to + * be moved. It will be scheduled for erasure in the + * wear-leveling worker. + */ + dbg_wl("PEB %d is being moved, wait", pnum); + spin_unlock(&ubi->wl_lock); + + /* Wait for the WL worker by taking the @ubi->move_mutex */ + mutex_lock(&ubi->move_mutex); + mutex_unlock(&ubi->move_mutex); + goto retry; + } else if (e == ubi->move_to) { + /* + * User is putting the physical eraseblock which was selected + * as the target the data is moved to. It may happen if the EBA + * unit already re-mapped the LEB in 'ubi_eba_copy_leb()' but + * the WL unit has not put the PEB to the "used" tree yet, but + * it is about to do this. So we just set a flag which will + * tell the WL worker that the PEB is not needed anymore and + * should be scheduled for erasure. + */ + dbg_wl("PEB %d is the target of data moving", pnum); + ubi_assert(!ubi->move_to_put); + ubi->move_to_put = 1; + spin_unlock(&ubi->wl_lock); + return 0; + } else { + if (in_wl_tree(e, &ubi->used)) { + paranoid_check_in_wl_tree(e, &ubi->used); + rb_erase(&e->rb, &ubi->used); + } else if (in_wl_tree(e, &ubi->scrub)) { + paranoid_check_in_wl_tree(e, &ubi->scrub); + rb_erase(&e->rb, &ubi->scrub); + } else { + err = prot_tree_del(ubi, e->pnum); + if (err) { + ubi_err("PEB %d not found", pnum); + ubi_ro_mode(ubi); + spin_unlock(&ubi->wl_lock); + return err; + } + } + } + spin_unlock(&ubi->wl_lock); + + err = schedule_erase(ubi, e, torture); + if (err) { + spin_lock(&ubi->wl_lock); + wl_tree_add(e, &ubi->used); + spin_unlock(&ubi->wl_lock); + } + + return err; +} + +/** + * ubi_wl_scrub_peb - schedule a physical eraseblock for scrubbing. + * @ubi: UBI device description object + * @pnum: the physical eraseblock to schedule + * + * If a bit-flip in a physical eraseblock is detected, this physical eraseblock + * needs scrubbing. This function schedules a physical eraseblock for + * scrubbing which is done in background. This function returns zero in case of + * success and a negative error code in case of failure. + */ +int ubi_wl_scrub_peb(struct ubi_device *ubi, int pnum) +{ + struct ubi_wl_entry *e; + + ubi_msg("schedule PEB %d for scrubbing", pnum); + +retry: + spin_lock(&ubi->wl_lock); + e = ubi->lookuptbl[pnum]; + if (e == ubi->move_from || in_wl_tree(e, &ubi->scrub)) { + spin_unlock(&ubi->wl_lock); + return 0; + } + + if (e == ubi->move_to) { + /* + * This physical eraseblock was used to move data to. The data + * was moved but the PEB was not yet inserted to the proper + * tree. We should just wait a little and let the WL worker + * proceed. + */ + spin_unlock(&ubi->wl_lock); + dbg_wl("the PEB %d is not in proper tree, retry", pnum); + yield(); + goto retry; + } + + if (in_wl_tree(e, &ubi->used)) { + paranoid_check_in_wl_tree(e, &ubi->used); + rb_erase(&e->rb, &ubi->used); + } else { + int err; + + err = prot_tree_del(ubi, e->pnum); + if (err) { + ubi_err("PEB %d not found", pnum); + ubi_ro_mode(ubi); + spin_unlock(&ubi->wl_lock); + return err; + } + } + + wl_tree_add(e, &ubi->scrub); + spin_unlock(&ubi->wl_lock); + + /* + * Technically scrubbing is the same as wear-leveling, so it is done + * by the WL worker. + */ + return ensure_wear_leveling(ubi); +} + +/** + * ubi_wl_flush - flush all pending works. + * @ubi: UBI device description object + * + * This function returns zero in case of success and a negative error code in + * case of failure. + */ +int ubi_wl_flush(struct ubi_device *ubi) +{ + int err; + + /* + * Erase while the pending works queue is not empty, but not more then + * the number of currently pending works. + */ + dbg_wl("flush (%d pending works)", ubi->works_count); + while (ubi->works_count) { + err = do_work(ubi); + if (err) + return err; + } + + /* + * Make sure all the works which have been done in parallel are + * finished. + */ + down_write(&ubi->work_sem); + up_write(&ubi->work_sem); + + /* + * And in case last was the WL worker and it cancelled the LEB + * movement, flush again. + */ + while (ubi->works_count) { + dbg_wl("flush more (%d pending works)", ubi->works_count); + err = do_work(ubi); + if (err) + return err; + } + + return 0; +} + +/** + * tree_destroy - destroy an RB-tree. + * @root: the root of the tree to destroy + */ +static void tree_destroy(struct rb_root *root) +{ + struct rb_node *rb; + struct ubi_wl_entry *e; + + rb = root->rb_node; + while (rb) { + if (rb->rb_left) + rb = rb->rb_left; + else if (rb->rb_right) + rb = rb->rb_right; + else { + e = rb_entry(rb, struct ubi_wl_entry, rb); + + rb = rb_parent(rb); + if (rb) { + if (rb->rb_left == &e->rb) + rb->rb_left = NULL; + else + rb->rb_right = NULL; + } + + kmem_cache_free(ubi_wl_entry_slab, e); + } + } +} + +/** + * ubi_thread - UBI background thread. + * @u: the UBI device description object pointer + */ +int ubi_thread(void *u) +{ + int failures = 0; + struct ubi_device *ubi = u; + + ubi_msg("background thread \"%s\" started, PID %d", + ubi->bgt_name, task_pid_nr(current)); + + set_freezable(); + for (;;) { + int err; + + if (kthread_should_stop()) + break; + + if (try_to_freeze()) + continue; + + spin_lock(&ubi->wl_lock); + if (list_empty(&ubi->works) || ubi->ro_mode || + !ubi->thread_enabled) { + set_current_state(TASK_INTERRUPTIBLE); + spin_unlock(&ubi->wl_lock); + schedule(); + continue; + } + spin_unlock(&ubi->wl_lock); + + err = do_work(ubi); + if (err) { + ubi_err("%s: work failed with error code %d", + ubi->bgt_name, err); + if (failures++ > WL_MAX_FAILURES) { + /* + * Too many failures, disable the thread and + * switch to read-only mode. + */ + ubi_msg("%s: %d consecutive failures", + ubi->bgt_name, WL_MAX_FAILURES); + ubi_ro_mode(ubi); + break; + } + } else + failures = 0; + + cond_resched(); + } + + dbg_wl("background thread \"%s\" is killed", ubi->bgt_name); + return 0; +} + +/** + * cancel_pending - cancel all pending works. + * @ubi: UBI device description object + */ +static void cancel_pending(struct ubi_device *ubi) +{ + while (!list_empty(&ubi->works)) { + struct ubi_work *wrk; + + wrk = list_entry(ubi->works.next, struct ubi_work, list); + list_del(&wrk->list); + wrk->func(ubi, wrk, 1); + ubi->works_count -= 1; + ubi_assert(ubi->works_count >= 0); + } +} + +/** + * ubi_wl_init_scan - initialize the wear-leveling unit using scanning + * information. + * @ubi: UBI device description object + * @si: scanning information + * + * This function returns zero in case of success, and a negative error code in + * case of failure. + */ +int ubi_wl_init_scan(struct ubi_device *ubi, struct ubi_scan_info *si) +{ + int err; + struct rb_node *rb1, *rb2; + struct ubi_scan_volume *sv; + struct ubi_scan_leb *seb, *tmp; + struct ubi_wl_entry *e; + + + ubi->used = ubi->free = ubi->scrub = RB_ROOT; + ubi->prot.pnum = ubi->prot.aec = RB_ROOT; + spin_lock_init(&ubi->wl_lock); + mutex_init(&ubi->move_mutex); + init_rwsem(&ubi->work_sem); + ubi->max_ec = si->max_ec; + INIT_LIST_HEAD(&ubi->works); + + sprintf(ubi->bgt_name, UBI_BGT_NAME_PATTERN, ubi->ubi_num); + + err = -ENOMEM; + ubi->lookuptbl = kzalloc(ubi->peb_count * sizeof(void *), GFP_KERNEL); + if (!ubi->lookuptbl) + return err; + + list_for_each_entry_safe(seb, tmp, &si->erase, u.list) { + cond_resched(); + + e = kmem_cache_alloc(ubi_wl_entry_slab, GFP_KERNEL); + if (!e) + goto out_free; + + e->pnum = seb->pnum; + e->ec = seb->ec; + ubi->lookuptbl[e->pnum] = e; + if (schedule_erase(ubi, e, 0)) { + kmem_cache_free(ubi_wl_entry_slab, e); + goto out_free; + } + } + + list_for_each_entry(seb, &si->free, u.list) { + cond_resched(); + + e = kmem_cache_alloc(ubi_wl_entry_slab, GFP_KERNEL); + if (!e) + goto out_free; + + e->pnum = seb->pnum; + e->ec = seb->ec; + ubi_assert(e->ec >= 0); + wl_tree_add(e, &ubi->free); + ubi->lookuptbl[e->pnum] = e; + } + + list_for_each_entry(seb, &si->corr, u.list) { + cond_resched(); + + e = kmem_cache_alloc(ubi_wl_entry_slab, GFP_KERNEL); + if (!e) + goto out_free; + + e->pnum = seb->pnum; + e->ec = seb->ec; + ubi->lookuptbl[e->pnum] = e; + if (schedule_erase(ubi, e, 0)) { + kmem_cache_free(ubi_wl_entry_slab, e); + goto out_free; + } + } + + ubi_rb_for_each_entry(rb1, sv, &si->volumes, rb) { + ubi_rb_for_each_entry(rb2, seb, &sv->root, u.rb) { + cond_resched(); + + e = kmem_cache_alloc(ubi_wl_entry_slab, GFP_KERNEL); + if (!e) + goto out_free; + + e->pnum = seb->pnum; + e->ec = seb->ec; + ubi->lookuptbl[e->pnum] = e; + if (!seb->scrub) { + dbg_wl("add PEB %d EC %d to the used tree", + e->pnum, e->ec); + wl_tree_add(e, &ubi->used); + } else { + dbg_wl("add PEB %d EC %d to the scrub tree", + e->pnum, e->ec); + wl_tree_add(e, &ubi->scrub); + } + } + } + + if (ubi->avail_pebs < WL_RESERVED_PEBS) { + ubi_err("no enough physical eraseblocks (%d, need %d)", + ubi->avail_pebs, WL_RESERVED_PEBS); + goto out_free; + } + ubi->avail_pebs -= WL_RESERVED_PEBS; + ubi->rsvd_pebs += WL_RESERVED_PEBS; + + /* Schedule wear-leveling if needed */ + err = ensure_wear_leveling(ubi); + if (err) + goto out_free; + + return 0; + +out_free: + cancel_pending(ubi); + tree_destroy(&ubi->used); + tree_destroy(&ubi->free); + tree_destroy(&ubi->scrub); + kfree(ubi->lookuptbl); + return err; +} + +/** + * protection_trees_destroy - destroy the protection RB-trees. + * @ubi: UBI device description object + */ +static void protection_trees_destroy(struct ubi_device *ubi) +{ + struct rb_node *rb; + struct ubi_wl_prot_entry *pe; + + rb = ubi->prot.aec.rb_node; + while (rb) { + if (rb->rb_left) + rb = rb->rb_left; + else if (rb->rb_right) + rb = rb->rb_right; + else { + pe = rb_entry(rb, struct ubi_wl_prot_entry, rb_aec); + + rb = rb_parent(rb); + if (rb) { + if (rb->rb_left == &pe->rb_aec) + rb->rb_left = NULL; + else + rb->rb_right = NULL; + } + + kmem_cache_free(ubi_wl_entry_slab, pe->e); + kfree(pe); + } + } +} + +/** + * ubi_wl_close - close the wear-leveling unit. + * @ubi: UBI device description object + */ +void ubi_wl_close(struct ubi_device *ubi) +{ + dbg_wl("close the UBI wear-leveling unit"); + + cancel_pending(ubi); + protection_trees_destroy(ubi); + tree_destroy(&ubi->used); + tree_destroy(&ubi->free); + tree_destroy(&ubi->scrub); + kfree(ubi->lookuptbl); +} + +#ifdef CONFIG_MTD_UBI_DEBUG_PARANOID + +/** + * paranoid_check_ec - make sure that the erase counter of a physical eraseblock + * is correct. + * @ubi: UBI device description object + * @pnum: the physical eraseblock number to check + * @ec: the erase counter to check + * + * This function returns zero if the erase counter of physical eraseblock @pnum + * is equivalent to @ec, %1 if not, and a negative error code if an error + * occurred. + */ +static int paranoid_check_ec(struct ubi_device *ubi, int pnum, int ec) +{ + int err; + long long read_ec; + struct ubi_ec_hdr *ec_hdr; + + ec_hdr = kzalloc(ubi->ec_hdr_alsize, GFP_NOFS); + if (!ec_hdr) + return -ENOMEM; + + err = ubi_io_read_ec_hdr(ubi, pnum, ec_hdr, 0); + if (err && err != UBI_IO_BITFLIPS) { + /* The header does not have to exist */ + err = 0; + goto out_free; + } + + read_ec = be64_to_cpu(ec_hdr->ec); + if (ec != read_ec) { + ubi_err("paranoid check failed for PEB %d", pnum); + ubi_err("read EC is %lld, should be %d", read_ec, ec); + ubi_dbg_dump_stack(); + err = 1; + } else + err = 0; + +out_free: + kfree(ec_hdr); + return err; +} + +/** + * paranoid_check_in_wl_tree - make sure that a wear-leveling entry is present + * in a WL RB-tree. + * @e: the wear-leveling entry to check + * @root: the root of the tree + * + * This function returns zero if @e is in the @root RB-tree and %1 if it + * is not. + */ +static int paranoid_check_in_wl_tree(struct ubi_wl_entry *e, + struct rb_root *root) +{ + if (in_wl_tree(e, root)) + return 0; + + ubi_err("paranoid check failed for PEB %d, EC %d, RB-tree %p ", + e->pnum, e->ec, root); + ubi_dbg_dump_stack(); + return 1; +} + +#endif /* CONFIG_MTD_UBI_DEBUG_PARANOID */ diff --git a/drivers/nor/cfi_flash.c b/drivers/nor/cfi_flash.c index b21739bd28..fa5e5ee86c 100644 --- a/drivers/nor/cfi_flash.c +++ b/drivers/nor/cfi_flash.c @@ -77,7 +77,7 @@ static uint flash_offset_cfi[2]={FLASH_OFFSET_CFI,FLASH_OFFSET_CFI_ALT}; * Functions */ -static void flash_add_byte (flash_info_t * info, cfiword_t * cword, uchar c) +static void flash_add_byte (struct flash_info *info, cfiword_t * cword, uchar c) { #if defined(__LITTLE_ENDIAN) unsigned short w; @@ -114,7 +114,7 @@ static void flash_add_byte (flash_info_t * info, cfiword_t * cword, uchar c) } } -static int flash_write_cfiword (flash_info_t * info, ulong dest, +static int flash_write_cfiword (struct flash_info *info, ulong dest, cfiword_t cword) { cfiptr_t cptr; @@ -159,7 +159,7 @@ void print_longlong (char *str, unsigned long long data) sprintf (&str[i * 2], "%2.2x", *cp++); } -static void flash_printqry (flash_info_t * info, flash_sect_t sect) +static void flash_printqry (struct flash_info *info, flash_sect_t sect) { cfiptr_t cptr; int x, y; @@ -188,7 +188,7 @@ static void flash_printqry (flash_info_t * info, flash_sect_t sect) /* * read a short word by swapping for ppc format. */ -static ushort flash_read_ushort (flash_info_t * info, flash_sect_t sect, uint offset) +static ushort flash_read_ushort (struct flash_info *info, flash_sect_t sect, uint offset) { uchar *addr; ushort retval; @@ -220,7 +220,7 @@ static ushort flash_read_ushort (flash_info_t * info, flash_sect_t sect, uint of * read a long word by picking the least significant byte of each maximum * port size word. Swap for ppc format. */ -static ulong flash_read_long (flash_info_t * info, flash_sect_t sect, uint offset) +static ulong flash_read_long (struct flash_info *info, flash_sect_t sect, uint offset) { uchar *addr; ulong retval; @@ -254,7 +254,7 @@ static ulong flash_read_long (flash_info_t * info, flash_sect_t sect, uint offse * http://www.jedec.org/download/search/jesd68.pdf * */ -static int flash_detect_cfi (flash_info_t * info) +static int flash_detect_cfi (struct flash_info *info) { int cfi_offset; debug ("flash detect cfi\n"); @@ -291,7 +291,7 @@ static int flash_detect_cfi (flash_info_t * info) /* * The following code cannot be run from FLASH! */ -static ulong flash_get_size (flash_info_t *info, ulong base) +static ulong flash_get_size (struct flash_info *info, ulong base) { int i, j; flash_sect_t sect_cnt; @@ -302,6 +302,7 @@ static ulong flash_get_size (flash_info_t *info, ulong base) int erase_region_size; int erase_region_count; int geometry_reversed = 0; + int cur_offset = 0; info->ext_addr = 0; info->cfi_version = 0; @@ -401,10 +402,14 @@ static ulong flash_get_size (flash_info_t *info, ulong base) size_ratio, info->portwidth << CFI_FLASH_SHIFT_WIDTH, info->chipwidth << CFI_FLASH_SHIFT_WIDTH); debug ("found %d erase regions\n", num_erase_regions); + info->eraseregions = xzalloc(sizeof(*(info->eraseregions)) * num_erase_regions); + info->numeraseregions = num_erase_regions; sect_cnt = 0; sector = base; for (i = 0; i < num_erase_regions; i++) { + struct mtd_erase_region_info *region = &info->eraseregions[i]; + if (i > NUM_ERASE_REGIONS) { printf ("%d erase regions found, only %d used\n", num_erase_regions, NUM_ERASE_REGIONS); @@ -425,6 +430,11 @@ static ulong flash_get_size (flash_info_t *info, ulong base) debug ("erase_region_count = %d erase_region_size = %d\n", erase_region_count, erase_region_size); + region->offset = cur_offset; + region->erasesize = erase_region_size; + region->numblocks = erase_region_count; + cur_offset += erase_region_size * erase_region_count; + /* increase the space malloced for the sector start addresses */ info->start = realloc(info->start, sizeof(ulong) * (erase_region_count + sect_cnt)); info->protect = realloc(info->protect, sizeof(uchar) * (erase_region_count + sect_cnt)); @@ -478,7 +488,7 @@ static ulong flash_get_size (flash_info_t *info, ulong base) * when the passed address is greater or equal to the sector address * we have a match */ -flash_sect_t find_sector (flash_info_t * info, ulong addr) +flash_sect_t find_sector (struct flash_info *info, ulong addr) { flash_sect_t sector; @@ -489,37 +499,47 @@ flash_sect_t find_sector (flash_info_t * info, ulong addr) return sector; } -static int cfi_erase(struct cdev *cdev, size_t count, unsigned long offset) +static int __cfi_erase(struct cdev *cdev, size_t count, unsigned long offset, + int verbose) { - flash_info_t *finfo = (flash_info_t *)cdev->priv; + struct flash_info *finfo = (struct flash_info *)cdev->priv; unsigned long start, end; int i, ret = 0; - printf("%s: erase 0x%08x (size %d)\n", __FUNCTION__, offset, count); + debug("%s: erase 0x%08x (size %d)\n", __func__, offset, count); start = find_sector(finfo, cdev->dev->map_base + offset); end = find_sector(finfo, cdev->dev->map_base + offset + count - 1); - init_progression_bar(end - start); + if (verbose) + init_progression_bar(end - start); for (i = start; i <= end; i++) { ret = finfo->cfi_cmd_set->flash_erase_one(finfo, i); if (ret) goto out; - show_progress(i - start); + + if (verbose) + show_progress(i - start); } out: - putchar('\n'); + if (verbose) + putchar('\n'); return ret; } +static int cfi_erase(struct cdev *cdev, size_t count, unsigned long offset) +{ + return __cfi_erase(cdev, count, offset, 1); +} + /* * Copy memory to flash, returns: * 0 - OK * 1 - write timeout * 2 - Flash not erased */ -static int write_buff (flash_info_t * info, const uchar * src, ulong addr, ulong cnt) +static int write_buff (struct flash_info *info, const uchar * src, ulong addr, ulong cnt) { ulong wp; ulong cp; @@ -614,7 +634,7 @@ static int write_buff (flash_info_t * info, const uchar * src, ulong addr, ulong return flash_write_cfiword (info, wp, cword); } -static int flash_real_protect (flash_info_t * info, long sector, int prot) +static int flash_real_protect (struct flash_info *info, long sector, int prot) { int retcode = 0; @@ -649,7 +669,7 @@ static int flash_real_protect (flash_info_t * info, long sector, int prot) static int cfi_protect(struct cdev *cdev, size_t count, unsigned long offset, int prot) { - flash_info_t *finfo = (flash_info_t *)cdev->priv; + struct flash_info *finfo = (struct flash_info *)cdev->priv; unsigned long start, end; int i, ret = 0; const char *action = (prot? "protect" : "unprotect"); @@ -672,7 +692,7 @@ out: static ssize_t cfi_write(struct cdev *cdev, const void *buf, size_t count, unsigned long offset, ulong flags) { - flash_info_t *finfo = (flash_info_t *)cdev->priv; + struct flash_info *finfo = (struct flash_info *)cdev->priv; int ret; debug("cfi_write: buf=0x%08x addr=0x%08x count=0x%08x\n",buf, cdev->dev->map_base + offset, count); @@ -683,7 +703,7 @@ static ssize_t cfi_write(struct cdev *cdev, const void *buf, size_t count, unsig static void cfi_info (struct device_d* dev) { - flash_info_t *info = (flash_info_t *)dev->priv; + struct flash_info *info = (struct flash_info *)dev->priv; int i; if (info->flash_id != FLASH_MAN_CFI) { @@ -775,7 +795,7 @@ static void cfi_info (struct device_d* dev) /* * flash_read_user_serial - read the OneTimeProgramming cells */ -static void flash_read_user_serial (flash_info_t * info, void *buffer, int offset, +static void flash_read_user_serial (struct flash_info *info, void *buffer, int offset, int len) { uchar *src; @@ -791,7 +811,7 @@ static void flash_read_user_serial (flash_info_t * info, void *buffer, int offse /* * flash_read_factory_serial - read the device Id from the protection area */ -static void flash_read_factory_serial (flash_info_t * info, void *buffer, int offset, +static void flash_read_factory_serial (struct flash_info *info, void *buffer, int offset, int len) { uchar *src; @@ -804,7 +824,7 @@ static void flash_read_factory_serial (flash_info_t * info, void *buffer, int of #endif -int flash_status_check (flash_info_t * info, flash_sect_t sector, +int flash_status_check (struct flash_info *info, flash_sect_t sector, uint64_t tout, char *prompt) { return info->cfi_cmd_set->flash_status_check(info, sector, tout, prompt); @@ -814,7 +834,7 @@ int flash_status_check (flash_info_t * info, flash_sect_t sector, * wait for XSR.7 to be set. Time out with an error if it does not. * This routine does not set the flash to read-array mode. */ -int flash_generic_status_check (flash_info_t * info, flash_sect_t sector, +int flash_generic_status_check (struct flash_info *info, flash_sect_t sector, uint64_t tout, char *prompt) { uint64_t start; @@ -839,7 +859,7 @@ int flash_generic_status_check (flash_info_t * info, flash_sect_t sector, /* * make a proper sized command based on the port and chip widths */ -void flash_make_cmd (flash_info_t * info, uchar cmd, void *cmdbuf) +void flash_make_cmd (struct flash_info *info, uchar cmd, void *cmdbuf) { int i; uchar *cp = (uchar *) cmdbuf; @@ -855,7 +875,7 @@ void flash_make_cmd (flash_info_t * info, uchar cmd, void *cmdbuf) /* * Write a proper sized command to the correct address */ -void flash_write_cmd (flash_info_t * info, flash_sect_t sect, uint offset, uchar cmd) +void flash_write_cmd (struct flash_info *info, flash_sect_t sect, uint offset, uchar cmd) { uchar *addr; @@ -866,7 +886,7 @@ void flash_write_cmd (flash_info_t * info, flash_sect_t sect, uint offset, uchar flash_write_word(info, cword, addr); } -int flash_isequal (flash_info_t * info, flash_sect_t sect, uint offset, uchar cmd) +int flash_isequal (struct flash_info *info, flash_sect_t sect, uint offset, uchar cmd) { cfiptr_t cptr; cfiword_t cword; @@ -903,7 +923,7 @@ int flash_isequal (flash_info_t * info, flash_sect_t sect, uint offset, uchar cm return retval; } -int flash_isset (flash_info_t * info, flash_sect_t sect, uint offset, uchar cmd) +int flash_isset (struct flash_info *info, flash_sect_t sect, uint offset, uchar cmd) { cfiptr_t cptr; cfiword_t cword; @@ -934,10 +954,72 @@ struct file_operations cfi_ops = { .memmap = generic_memmap_ro, }; +#ifdef CONFIG_PARTITION_NEED_MTD +static int cfi_mtd_read(struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, u_char *buf) +{ + struct flash_info *info = container_of(mtd, struct flash_info, mtd); + + memcpy(buf, info->base + from, len); + *retlen = len; + + return 0; +} + +static int cfi_mtd_write(struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, const u_char *buf) +{ + struct flash_info *info = container_of(mtd, struct flash_info, mtd); + int ret; + + ret = write_buff(info, buf, (unsigned long)info->base + to, len); + *retlen = len; + + return ret; +} + +static int cfi_mtd_erase(struct mtd_info *mtd, struct erase_info *instr) +{ + struct flash_info *info = container_of(mtd, struct flash_info, mtd); + struct cdev *cdev = &info->cdev; + int ret; + + ret = __cfi_erase(cdev, instr->len, instr->addr, 0); + + if (ret) { + instr->state = MTD_ERASE_FAILED; + return -EIO; + } + + instr->state = MTD_ERASE_DONE; + mtd_erase_callback(instr); + + return 0; +} + +static void cfi_init_mtd(struct flash_info *info) +{ + struct mtd_info *mtd = &info->mtd; + + mtd->read = cfi_mtd_read; + mtd->write = cfi_mtd_write; + mtd->erase = cfi_mtd_erase; + mtd->size = info->size; + mtd->name = info->cdev.name; + mtd->erasesize = info->eraseregions[1].erasesize; /* FIXME */ + mtd->writesize = 1; + mtd->subpage_sft = 0; + mtd->eraseregions = info->eraseregions; + mtd->numeraseregions = info->numeraseregions; + mtd->flags = MTD_CAP_NORFLASH; + info->cdev.mtd = mtd; +} +#endif + static int cfi_probe (struct device_d *dev) { unsigned long size = 0; - flash_info_t *info = xzalloc(sizeof(flash_info_t)); + struct flash_info *info = xzalloc(sizeof(*info)); dev->priv = (void *)info; @@ -946,6 +1028,7 @@ static int cfi_probe (struct device_d *dev) /* Init: no FLASHes known */ info->flash_id = FLASH_UNKNOWN; size += info->size = flash_get_size(info, dev->map_base); + info->base = (void __iomem *)dev->map_base; if (dev->size == 0) { printf("cfi_probe: size : 0x%08x\n", info->size); @@ -963,6 +1046,10 @@ static int cfi_probe (struct device_d *dev) info->cdev.dev = dev; info->cdev.ops = &cfi_ops; info->cdev.priv = info; + +#ifdef CONFIG_PARTITION_NEED_MTD + cfi_init_mtd(info); +#endif devfs_create(&info->cdev); return 0; diff --git a/drivers/nor/cfi_flash.h b/drivers/nor/cfi_flash.h index a8fa8794fc..057e56c6eb 100644 --- a/drivers/nor/cfi_flash.h +++ b/drivers/nor/cfi_flash.h @@ -26,6 +26,7 @@ #include <driver.h> #include <asm/io.h> +#include <linux/mtd/mtd.h> typedef unsigned long flash_sect_t; struct cfi_cmd_set; @@ -34,7 +35,7 @@ struct cfi_cmd_set; * FLASH Info: contains chip specific data, per FLASH bank */ -typedef struct { +struct flash_info { struct driver_d driver; ulong size; /* total bank size in bytes */ ushort sector_count; /* number of erase units */ @@ -60,15 +61,21 @@ typedef struct { ushort cfi_offset; /* offset for cfi query */ struct cfi_cmd_set *cfi_cmd_set; struct cdev cdev; -} flash_info_t; +#ifdef CONFIG_PARTITION_NEED_MTD + struct mtd_info mtd; +#endif + int numeraseregions; + struct mtd_erase_region_info *eraseregions; + void *base; +}; struct cfi_cmd_set { - int (*flash_write_cfibuffer) (flash_info_t * info, ulong dest, const uchar * cp, int len); - int (*flash_erase_one) (flash_info_t * info, long sect); - int (*flash_is_busy) (flash_info_t * info, flash_sect_t sect); - void (*flash_read_jedec_ids) (flash_info_t * info); - void (*flash_prepare_write) (flash_info_t * info); - int (*flash_status_check) (flash_info_t * info, flash_sect_t sector, uint64_t tout, char *prompt); + int (*flash_write_cfibuffer) (struct flash_info *info, ulong dest, const uchar * cp, int len); + int (*flash_erase_one) (struct flash_info *info, long sect); + int (*flash_is_busy) (struct flash_info *info, flash_sect_t sect); + void (*flash_read_jedec_ids) (struct flash_info *info); + void (*flash_prepare_write) (struct flash_info *info); + int (*flash_status_check) (struct flash_info *info, flash_sect_t sector, uint64_t tout, char *prompt); }; extern struct cfi_cmd_set cfi_cmd_set_intel; @@ -180,21 +187,21 @@ extern struct cfi_cmd_set cfi_cmd_set_amd; #define CFI_FLASH_SHIFT_WIDTH 3 /* Prototypes */ -int flash_isset (flash_info_t * info, flash_sect_t sect, uint offset, uchar cmd); -void flash_write_cmd (flash_info_t * info, flash_sect_t sect, uint offset, uchar cmd); -flash_sect_t find_sector (flash_info_t * info, ulong addr); -int flash_status_check (flash_info_t * info, flash_sect_t sector, +int flash_isset (struct flash_info *info, flash_sect_t sect, uint offset, uchar cmd); +void flash_write_cmd (struct flash_info *info, flash_sect_t sect, uint offset, uchar cmd); +flash_sect_t find_sector (struct flash_info *info, ulong addr); +int flash_status_check (struct flash_info *info, flash_sect_t sector, uint64_t tout, char *prompt); -int flash_generic_status_check (flash_info_t * info, flash_sect_t sector, +int flash_generic_status_check (struct flash_info *info, flash_sect_t sector, uint64_t tout, char *prompt); -int flash_isequal (flash_info_t * info, flash_sect_t sect, uint offset, uchar cmd); -void flash_make_cmd (flash_info_t * info, uchar cmd, void *cmdbuf); +int flash_isequal (struct flash_info *info, flash_sect_t sect, uint offset, uchar cmd); +void flash_make_cmd (struct flash_info *info, uchar cmd, void *cmdbuf); /* * create an address based on the offset and the port width */ -static inline uchar *flash_make_addr (flash_info_t * info, flash_sect_t sect, uint offset) +static inline uchar *flash_make_addr (struct flash_info *info, flash_sect_t sect, uint offset) { return ((uchar *) (info->start[sect] + (offset * info->portwidth))); } @@ -202,7 +209,7 @@ static inline uchar *flash_make_addr (flash_info_t * info, flash_sect_t sect, ui /* * read a character at a port width address */ -static inline uchar flash_read_uchar (flash_info_t * info, uint offset) +static inline uchar flash_read_uchar (struct flash_info *info, uint offset) { uchar *cp; @@ -252,7 +259,7 @@ typedef union { volatile unsigned long long *llp; } cfiptr_t; -static inline void flash_write_word(flash_info_t *info, cfiword_t datum, void *addr) +static inline void flash_write_word(struct flash_info *info, cfiword_t datum, void *addr) { if (bankwidth_is_1(info)) { debug("fw addr %p val %02x\n", addr, datum.c); @@ -268,21 +275,21 @@ static inline void flash_write_word(flash_info_t *info, cfiword_t datum, void *a } } -extern void flash_print_info (flash_info_t *); +extern void flash_print_info (struct flash_info *); extern int flash_sect_erase (ulong addr_first, ulong addr_last); extern int flash_sect_protect (int flag, ulong addr_first, ulong addr_last); /* common/flash.c */ -extern void flash_protect (int flag, ulong from, ulong to, flash_info_t *info); +extern void flash_protect (int flag, ulong from, ulong to, struct flash_info *info); extern int flash_write (char *, ulong, ulong); -extern flash_info_t *addr2info (ulong); +extern struct flash_info *addr2info (ulong); //extern int write_buff (flash_info_t *info, const uchar *src, ulong addr, ulong cnt); /* board/?/flash.c */ #if defined(CFG_FLASH_PROTECTION) -extern int flash_real_protect(flash_info_t *info, long sector, int prot); -extern void flash_read_user_serial(flash_info_t * info, void * buffer, int offset, int len); -extern void flash_read_factory_serial(flash_info_t * info, void * buffer, int offset, int len); +extern int flash_real_protect(struct flash_info *info, long sector, int prot); +extern void flash_read_user_serial(struct flash_info *info, void * buffer, int offset, int len); +extern void flash_read_factory_serial(struct flash_info *info, void * buffer, int offset, int len); #endif /* CFG_FLASH_PROTECTION */ /*----------------------------------------------------------------------- diff --git a/drivers/nor/cfi_flash_amd.c b/drivers/nor/cfi_flash_amd.c index ea4c2c99eb..45b7e5ce99 100644 --- a/drivers/nor/cfi_flash_amd.c +++ b/drivers/nor/cfi_flash_amd.c @@ -2,7 +2,7 @@ #include <stdio.h> #include "cfi_flash.h" -static void flash_unlock_seq (flash_info_t * info) +static void flash_unlock_seq (struct flash_info *info) { flash_write_cmd (info, 0, AMD_ADDR_START, AMD_CMD_UNLOCK_START); flash_write_cmd (info, 0, AMD_ADDR_ACK, AMD_CMD_UNLOCK_ACK); @@ -14,7 +14,7 @@ static void flash_unlock_seq (flash_info_t * info) * Note: assume cfi->vendor, cfi->portwidth and cfi->chipwidth are correct * */ -static void amd_read_jedec_ids (flash_info_t * info) +static void amd_read_jedec_ids (struct flash_info *info) { info->manufacturer_id = 0; info->device_id = 0; @@ -39,7 +39,7 @@ static void amd_read_jedec_ids (flash_info_t * info) flash_write_cmd(info, 0, 0, AMD_CMD_RESET); } -static int flash_toggle (flash_info_t * info, flash_sect_t sect, uint offset, uchar cmd) +static int flash_toggle (struct flash_info *info, flash_sect_t sect, uint offset, uchar cmd) { cfiptr_t cptr; cfiword_t cword; @@ -66,12 +66,12 @@ static int flash_toggle (flash_info_t * info, flash_sect_t sect, uint offset, uc * flash_is_busy - check to see if the flash is busy * This routine checks the status of the chip and returns true if the chip is busy */ -static int amd_flash_is_busy (flash_info_t * info, flash_sect_t sect) +static int amd_flash_is_busy (struct flash_info *info, flash_sect_t sect) { return flash_toggle (info, sect, 0, AMD_STATUS_TOGGLE); } -static int amd_flash_erase_one (flash_info_t * info, long sect) +static int amd_flash_erase_one (struct flash_info *info, long sect) { flash_unlock_seq(info); flash_write_cmd (info, 0, AMD_ADDR_ERASE_START, AMD_CMD_ERASE_START); @@ -81,14 +81,14 @@ static int amd_flash_erase_one (flash_info_t * info, long sect) return flash_status_check(info, sect, info->erase_blk_tout, "erase"); } -static void amd_flash_prepare_write(flash_info_t * info) +static void amd_flash_prepare_write(struct flash_info *info) { flash_unlock_seq(info); flash_write_cmd (info, 0, AMD_ADDR_START, AMD_CMD_WRITE); } #ifdef CONFIG_CFI_BUFFER_WRITE -static int amd_flash_write_cfibuffer (flash_info_t * info, ulong dest, const uchar * cp, +static int amd_flash_write_cfibuffer (struct flash_info *info, ulong dest, const uchar * cp, int len) { flash_sect_t sector; diff --git a/drivers/nor/cfi_flash_intel.c b/drivers/nor/cfi_flash_intel.c index e4dfdface9..43447609d8 100644 --- a/drivers/nor/cfi_flash_intel.c +++ b/drivers/nor/cfi_flash_intel.c @@ -7,7 +7,7 @@ * Note: assume cfi->vendor, cfi->portwidth and cfi->chipwidth are correct * */ -static void intel_read_jedec_ids (flash_info_t * info) +static void intel_read_jedec_ids (struct flash_info *info) { info->manufacturer_id = 0; info->device_id = 0; @@ -27,12 +27,12 @@ static void intel_read_jedec_ids (flash_info_t * info) * flash_is_busy - check to see if the flash is busy * This routine checks the status of the chip and returns true if the chip is busy */ -static int intel_flash_is_busy (flash_info_t * info, flash_sect_t sect) +static int intel_flash_is_busy (struct flash_info *info, flash_sect_t sect) { return !flash_isset (info, sect, 0, FLASH_STATUS_DONE); } -static int intel_flash_erase_one (flash_info_t * info, long sect) +static int intel_flash_erase_one (struct flash_info *info, long sect) { flash_write_cmd (info, sect, 0, FLASH_CMD_CLEAR_STATUS); flash_write_cmd (info, sect, 0, FLASH_CMD_BLOCK_ERASE); @@ -41,14 +41,14 @@ static int intel_flash_erase_one (flash_info_t * info, long sect) return flash_status_check(info, sect, info->erase_blk_tout, "erase"); } -static void intel_flash_prepare_write(flash_info_t * info) +static void intel_flash_prepare_write(struct flash_info *info) { flash_write_cmd (info, 0, 0, FLASH_CMD_CLEAR_STATUS); flash_write_cmd (info, 0, 0, FLASH_CMD_WRITE); } #ifdef CONFIG_CFI_BUFFER_WRITE -static int intel_flash_write_cfibuffer (flash_info_t * info, ulong dest, const uchar * cp, +static int intel_flash_write_cfibuffer (struct flash_info *info, ulong dest, const uchar * cp, int len) { flash_sect_t sector; @@ -94,7 +94,7 @@ static int intel_flash_write_cfibuffer (flash_info_t * info, ulong dest, const u #define intel_flash_write_cfibuffer NULL #endif /* CONFIG_CFI_BUFFER_WRITE */ -static int intel_flash_status_check (flash_info_t * info, flash_sect_t sector, +static int intel_flash_status_check (struct flash_info *info, flash_sect_t sector, uint64_t tout, char *prompt) { int retcode; diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig index 839efeb885..c7b2c52660 100644 --- a/drivers/usb/Kconfig +++ b/drivers/usb/Kconfig @@ -3,15 +3,9 @@ menuconfig USB if USB -config USB_EHCI - bool "EHCI driver" +source drivers/usb/host/Kconfig -config USB_ULPI - bool - -config USB_ISP1504 - select USB_ULPI - bool "ISP1504 Tranceiver support" +source drivers/usb/otg/Kconfig endif diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile index 57d0bed862..e6f683bb29 100644 --- a/drivers/usb/Makefile +++ b/drivers/usb/Makefile @@ -1,6 +1,5 @@ -obj-$(CONFIG_USB) += usb.o -obj-$(CONFIG_USB_EHCI) += usb_ehci_core.o -obj-$(CONFIG_USB_ULPI) += ulpi.o -obj-$(CONFIG_USB_ISP1504) += isp1504.o +obj-$(CONFIG_USB) += core/ obj-$(CONFIG_USB_GADGET) += gadget/ +obj-y += host/ +obj-y += otg/ diff --git a/drivers/usb/core/Makefile b/drivers/usb/core/Makefile new file mode 100644 index 0000000000..d49c68dbab --- /dev/null +++ b/drivers/usb/core/Makefile @@ -0,0 +1,2 @@ + +obj-y += usb.o diff --git a/drivers/usb/usb.c b/drivers/usb/core/usb.c index 76e033eb7b..76e033eb7b 100644 --- a/drivers/usb/usb.c +++ b/drivers/usb/core/usb.c diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig new file mode 100644 index 0000000000..e1549e8186 --- /dev/null +++ b/drivers/usb/host/Kconfig @@ -0,0 +1,2 @@ +config USB_EHCI + bool "EHCI driver" diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile new file mode 100644 index 0000000000..6c48e3aa39 --- /dev/null +++ b/drivers/usb/host/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_USB_EHCI) += ehci-hcd.o diff --git a/drivers/usb/usb_ehci_core.h b/drivers/usb/host/ehci-core.h index 39e5c5e58c..39e5c5e58c 100644 --- a/drivers/usb/usb_ehci_core.h +++ b/drivers/usb/host/ehci-core.h diff --git a/drivers/usb/usb_ehci_core.c b/drivers/usb/host/ehci-hcd.c index af066de4ba..8995fa3c44 100644 --- a/drivers/usb/usb_ehci_core.c +++ b/drivers/usb/host/ehci-hcd.c @@ -34,7 +34,7 @@ #include <usb/ehci.h> #include <asm/mmu.h> -#include "usb_ehci.h" +#include "ehci.h" struct ehci_priv { int rootdev; diff --git a/drivers/usb/usb_ehci.h b/drivers/usb/host/ehci.h index af4924955f..af4924955f 100644 --- a/drivers/usb/usb_ehci.h +++ b/drivers/usb/host/ehci.h diff --git a/drivers/usb/otg/Kconfig b/drivers/usb/otg/Kconfig new file mode 100644 index 0000000000..0191c87a9b --- /dev/null +++ b/drivers/usb/otg/Kconfig @@ -0,0 +1,6 @@ +config USB_ULPI + bool + +config USB_ISP1504 + select USB_ULPI + bool "ISP1504 Tranceiver support" diff --git a/drivers/usb/otg/Makefile b/drivers/usb/otg/Makefile new file mode 100644 index 0000000000..785f922445 --- /dev/null +++ b/drivers/usb/otg/Makefile @@ -0,0 +1,2 @@ +obj-$(CONFIG_USB_ULPI) += ulpi.o +obj-$(CONFIG_USB_ISP1504) += isp1504.o diff --git a/drivers/usb/isp1504.c b/drivers/usb/otg/isp1504.c index 9ba74b157b..9ba74b157b 100644 --- a/drivers/usb/isp1504.c +++ b/drivers/usb/otg/isp1504.c diff --git a/drivers/usb/ulpi.c b/drivers/usb/otg/ulpi.c index 4a898fb223..4a898fb223 100644 --- a/drivers/usb/ulpi.c +++ b/drivers/usb/otg/ulpi.c diff --git a/fs/Kconfig b/fs/Kconfig index 3e9de96fad..d05797ab1f 100644 --- a/fs/Kconfig +++ b/fs/Kconfig @@ -16,4 +16,7 @@ config FS_DEVFS default y prompt "devfs support" +config PARTITION_NEED_MTD + bool + endmenu diff --git a/fs/devfs.c b/fs/devfs.c index 7478ef9f76..7019c8d920 100644 --- a/fs/devfs.c +++ b/fs/devfs.c @@ -31,6 +31,8 @@ #include <linux/stat.h> #include <ioctl.h> #include <nand.h> +#include <linux/err.h> +#include <linux/mtd/mtd.h> #include <linux/mtd/mtd-abi.h> #include <partition.h> @@ -170,6 +172,7 @@ static int devfs_close(struct device_d *_dev, FILE *f) static int partition_ioctl(struct cdev *cdev, int request, void *buf) { size_t offset; + struct mtd_info_user *user = buf; switch (request) { case MEMSETBADBLOCK: @@ -178,6 +181,20 @@ static int partition_ioctl(struct cdev *cdev, int request, void *buf) offset += cdev->offset; return cdev->ops->ioctl(cdev, request, (void *)offset); case MEMGETINFO: + if (cdev->mtd) { + user->type = cdev->mtd->type; + user->flags = cdev->mtd->flags; + user->size = cdev->mtd->size; + user->erasesize = cdev->mtd->erasesize; + user->oobsize = cdev->mtd->oobsize; + user->mtd = cdev->mtd; + /* The below fields are obsolete */ + user->ecctype = -1; + user->eccsize = 0; + return 0; + } + if (!cdev->ops->ioctl) + return -EINVAL; return cdev->ops->ioctl(cdev, request, buf); default: return -EINVAL; @@ -187,17 +204,14 @@ static int partition_ioctl(struct cdev *cdev, int request, void *buf) static int devfs_ioctl(struct device_d *_dev, FILE *f, int request, void *buf) { struct cdev *cdev = f->inode; - int ret = -EINVAL; + + if (cdev->flags & DEVFS_IS_PARTITION) + return partition_ioctl(cdev, request, buf); if (!cdev->ops->ioctl) - goto out; + return -EINVAL; - if (cdev->flags & DEVFS_IS_PARTITION) - ret = partition_ioctl(cdev, request, buf); - else - ret = cdev->ops->ioctl(cdev, request, buf); -out: - return ret; + return cdev->ops->ioctl(cdev, request, buf); } static int devfs_truncate(struct device_d *dev, FILE *f, ulong size) @@ -351,6 +365,17 @@ int devfs_add_partition(const char *devname, unsigned long offset, size_t size, new->dev = cdev->dev; new->flags = flags | DEVFS_IS_PARTITION; +#ifdef CONFIG_PARTITION_NEED_MTD + if (cdev->mtd) { + new->mtd = mtd_add_partition(cdev->mtd, offset, size, flags, name); + if (IS_ERR(new->mtd)) { + int ret = PTR_ERR(new->mtd); + free(new); + return ret; + } + } +#endif + devfs_create(new); return 0; @@ -370,6 +395,11 @@ int devfs_del_partition(const char *name) if (cdev->flags & DEVFS_PARTITION_FIXED) return -EPERM; +#ifdef CONFIG_PARTITION_NEED_MTD + if (cdev->mtd) + mtd_del_partition(cdev->mtd); +#endif + ret = devfs_remove(cdev); if (ret) return ret; @@ -229,6 +229,16 @@ static void put_file(FILE *f) files[f->no].in_use = 0; } +static int check_fd(int fd) +{ + if (fd < 0 || fd >= MAX_FILES || !files[fd].in_use) { + errno = -EBADF; + return errno; + } + + return 0; +} + static struct device_d *get_fs_device_by_path(char **path) { struct device_d *dev; @@ -457,6 +467,9 @@ int ioctl(int fd, int request, void *buf) struct fs_driver_d *fsdrv; FILE *f = &files[fd]; + if (check_fd(fd)) + return errno; + dev = f->dev; fsdrv = (struct fs_driver_d *)dev->driver->type_data; @@ -474,6 +487,9 @@ int read(int fd, void *buf, size_t count) struct fs_driver_d *fsdrv; FILE *f = &files[fd]; + if (check_fd(fd)) + return errno; + dev = f->dev; fsdrv = (struct fs_driver_d *)dev->driver->type_data; @@ -494,6 +510,9 @@ ssize_t write(int fd, const void *buf, size_t count) struct fs_driver_d *fsdrv; FILE *f = &files[fd]; + if (check_fd(fd)) + return errno; + dev = f->dev; fsdrv = (struct fs_driver_d *)dev->driver->type_data; @@ -524,6 +543,9 @@ off_t lseek(int fildes, off_t offset, int whence) FILE *f = &files[fildes]; off_t pos; + if (check_fd(fildes)) + return -1; + errno = 0; dev = f->dev; @@ -567,6 +589,9 @@ int erase(int fd, size_t count, unsigned long offset) struct fs_driver_d *fsdrv; FILE *f = &files[fd]; + if (check_fd(fd)) + return errno; + dev = f->dev; fsdrv = (struct fs_driver_d *)dev->driver->type_data; @@ -589,6 +614,9 @@ int protect(int fd, size_t count, unsigned long offset, int prot) struct fs_driver_d *fsdrv; FILE *f = &files[fd]; + if (check_fd(fd)) + return errno; + dev = f->dev; fsdrv = (struct fs_driver_d *)dev->driver->type_data; @@ -627,6 +655,9 @@ void *memmap(int fd, int flags) FILE *f = &files[fd]; void *ret = (void *)-1; + if (check_fd(fd)) + return ret; + dev = f->dev; fsdrv = (struct fs_driver_d *)dev->driver->type_data; @@ -646,6 +677,9 @@ int close(int fd) struct fs_driver_d *fsdrv; FILE *f = &files[fd]; + if (check_fd(fd)) + return errno; + dev = f->dev; fsdrv = (struct fs_driver_d *)dev->driver->type_data; diff --git a/include/common.h b/include/common.h index 319535b376..0edc778095 100644 --- a/include/common.h +++ b/include/common.h @@ -109,8 +109,8 @@ unsigned long long simple_strtoull(const char *cp,char **endp,unsigned int base) long simple_strtol(const char *cp,char **endp,unsigned int base); /* lib_generic/crc32.c */ -ulong crc32 (ulong, const unsigned char *, uint); -ulong crc32_no_comp (ulong, const unsigned char *, uint); +uint32_t crc32(uint32_t, const void*, unsigned int); +uint32_t crc32_no_comp(uint32_t, const void*, unsigned int); /* common/console.c */ int ctrlc (void); diff --git a/include/driver.h b/include/driver.h index 6950c02047..ae3e7774b1 100644 --- a/include/driver.h +++ b/include/driver.h @@ -307,6 +307,7 @@ struct cdev { size_t size; unsigned int flags; int open; + struct mtd_info *mtd; }; int devfs_create(struct cdev *); diff --git a/include/linux/mtd/mtd-abi.h b/include/linux/mtd/mtd-abi.h index 04b422792f..33e1fe2805 100644 --- a/include/linux/mtd/mtd-abi.h +++ b/include/linux/mtd/mtd-abi.h @@ -62,6 +62,7 @@ struct mtd_info_user { * (TODO: remove at some point) */ uint32_t ecctype; uint32_t eccsize; + struct mtd_info *mtd; }; struct region_info_user { diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h index ca98a16a38..01980f375c 100644 --- a/include/linux/mtd/mtd.h +++ b/include/linux/mtd/mtd.h @@ -15,6 +15,7 @@ #include <linux/types.h> #include <linux/list.h> #include <linux/mtd/mtd-abi.h> +#include <asm-generic/div64.h> #define MTD_CHAR_MAJOR 90 #define MTD_BLOCK_MAJOR 31 @@ -211,7 +212,16 @@ struct mtd_info { char *size_str; }; +static inline uint32_t mtd_div_by_eb(uint64_t sz, struct mtd_info *mtd) +{ + do_div(sz, mtd->erasesize); + return sz; +} +static inline uint32_t mtd_mod_by_eb(uint64_t sz, struct mtd_info *mtd) +{ + return do_div(sz, mtd->erasesize); +} /* Kernel-side ioctl definitions */ extern int add_mtd_device(struct mtd_info *mtd); @@ -229,6 +239,9 @@ struct mtd_notifier { struct list_head list; }; +struct mtd_info *mtd_add_partition(struct mtd_info *mtd, off_t offset, size_t size, + unsigned long flags, const char *name); +void mtd_del_partition(struct mtd_info *mtd); extern void register_mtd_user (struct mtd_notifier *new); extern int unregister_mtd_user (struct mtd_notifier *old); diff --git a/include/linux/mtd/ubi.h b/include/linux/mtd/ubi.h new file mode 100644 index 0000000000..2f1db318d2 --- /dev/null +++ b/include/linux/mtd/ubi.h @@ -0,0 +1,186 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Artem Bityutskiy (Битюцкий Артём) + */ + +#ifndef __LINUX_UBI_H__ +#define __LINUX_UBI_H__ + +/* #include <asm/ioctl.h> */ +#include <linux/types.h> +#include <mtd/ubi-user.h> + +/* + * enum ubi_open_mode - UBI volume open mode constants. + * + * UBI_READONLY: read-only mode + * UBI_READWRITE: read-write mode + * UBI_EXCLUSIVE: exclusive mode + */ +enum { + UBI_READONLY = 1, + UBI_READWRITE, + UBI_EXCLUSIVE +}; + +/** + * struct ubi_volume_info - UBI volume description data structure. + * @vol_id: volume ID + * @ubi_num: UBI device number this volume belongs to + * @size: how many physical eraseblocks are reserved for this volume + * @used_bytes: how many bytes of data this volume contains + * @used_ebs: how many physical eraseblocks of this volume actually contain any + * data + * @vol_type: volume type (%UBI_DYNAMIC_VOLUME or %UBI_STATIC_VOLUME) + * @corrupted: non-zero if the volume is corrupted (static volumes only) + * @upd_marker: non-zero if the volume has update marker set + * @alignment: volume alignment + * @usable_leb_size: how many bytes are available in logical eraseblocks of + * this volume + * @name_len: volume name length + * @name: volume name + * @cdev: UBI volume character device major and minor numbers + * + * The @corrupted flag is only relevant to static volumes and is always zero + * for dynamic ones. This is because UBI does not care about dynamic volume + * data protection and only cares about protecting static volume data. + * + * The @upd_marker flag is set if the volume update operation was interrupted. + * Before touching the volume data during the update operation, UBI first sets + * the update marker flag for this volume. If the volume update operation was + * further interrupted, the update marker indicates this. If the update marker + * is set, the contents of the volume is certainly damaged and a new volume + * update operation has to be started. + * + * To put it differently, @corrupted and @upd_marker fields have different + * semantics: + * o the @corrupted flag means that this static volume is corrupted for some + * reasons, but not because an interrupted volume update + * o the @upd_marker field means that the volume is damaged because of an + * interrupted update operation. + * + * I.e., the @corrupted flag is never set if the @upd_marker flag is set. + * + * The @used_bytes and @used_ebs fields are only really needed for static + * volumes and contain the number of bytes stored in this static volume and how + * many eraseblock this data occupies. In case of dynamic volumes, the + * @used_bytes field is equivalent to @size*@usable_leb_size, and the @used_ebs + * field is equivalent to @size. + * + * In general, logical eraseblock size is a property of the UBI device, not + * of the UBI volume. Indeed, the logical eraseblock size depends on the + * physical eraseblock size and on how much bytes UBI headers consume. But + * because of the volume alignment (@alignment), the usable size of logical + * eraseblocks if a volume may be less. The following equation is true: + * @usable_leb_size = LEB size - (LEB size mod @alignment), + * where LEB size is the logical eraseblock size defined by the UBI device. + * + * The alignment is multiple to the minimal flash input/output unit size or %1 + * if all the available space is used. + * + * To put this differently, alignment may be considered is a way to change + * volume logical eraseblock sizes. + */ +struct ubi_volume_info { + int ubi_num; + int vol_id; + int size; + long long used_bytes; + int used_ebs; + int vol_type; + int corrupted; + int upd_marker; + int alignment; + int usable_leb_size; + int name_len; + const char *name; + struct cdev *cdev; +}; + +/** + * struct ubi_device_info - UBI device description data structure. + * @ubi_num: ubi device number + * @leb_size: logical eraseblock size on this UBI device + * @min_io_size: minimal I/O unit size + * @ro_mode: if this device is in read-only mode + * @cdev: UBI character device major and minor numbers + * + * Note, @leb_size is the logical eraseblock size offered by the UBI device. + * Volumes of this UBI device may have smaller logical eraseblock size if their + * alignment is not equivalent to %1. + */ +struct ubi_device_info { + int ubi_num; + int leb_size; + int min_io_size; + int ro_mode; + struct cdev *cdev; +}; + +/* UBI descriptor given to users when they open UBI volumes */ +struct ubi_volume_desc; + +int ubi_get_device_info(int ubi_num, struct ubi_device_info *di); +void ubi_get_volume_info(struct ubi_volume_desc *desc, + struct ubi_volume_info *vi); +struct ubi_volume_desc *ubi_open_volume(int ubi_num, int vol_id, int mode); +struct ubi_volume_desc *ubi_open_volume_nm(int ubi_num, const char *name, + int mode); +void ubi_close_volume(struct ubi_volume_desc *desc); +int ubi_leb_read(struct ubi_volume_desc *desc, int lnum, char *buf, int offset, + int len, int check); +int ubi_leb_write(struct ubi_volume_desc *desc, int lnum, const void *buf, + int offset, int len, int dtype); +int ubi_leb_change(struct ubi_volume_desc *desc, int lnum, const void *buf, + int len, int dtype); +int ubi_leb_erase(struct ubi_volume_desc *desc, int lnum); +int ubi_leb_unmap(struct ubi_volume_desc *desc, int lnum); +int ubi_leb_map(struct ubi_volume_desc *desc, int lnum, int dtype); +int ubi_is_mapped(struct ubi_volume_desc *desc, int lnum); + +/* + * This function is the same as the 'ubi_leb_read()' function, but it does not + * provide the checking capability. + */ +static inline int ubi_read(struct ubi_volume_desc *desc, int lnum, char *buf, + int offset, int len) +{ + return ubi_leb_read(desc, lnum, buf, offset, len, 0); +} + +/* + * This function is the same as the 'ubi_leb_write()' functions, but it does + * not have the data type argument. + */ +static inline int ubi_write(struct ubi_volume_desc *desc, int lnum, + const void *buf, int offset, int len) +{ + return ubi_leb_write(desc, lnum, buf, offset, len, UBI_UNKNOWN); +} + +/* + * This function is the same as the 'ubi_leb_change()' functions, but it does + * not have the data type argument. + */ +static inline int ubi_change(struct ubi_volume_desc *desc, int lnum, + const void *buf, int len) +{ + return ubi_leb_change(desc, lnum, buf, len, UBI_UNKNOWN); +} + +#endif /* !__LINUX_UBI_H__ */ diff --git a/include/linux/rbtree.h b/include/linux/rbtree.h new file mode 100644 index 0000000000..6ff28e026b --- /dev/null +++ b/include/linux/rbtree.h @@ -0,0 +1,160 @@ +/* + Red Black Trees + (C) 1999 Andrea Arcangeli <andrea@suse.de> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + linux/include/linux/rbtree.h + + To use rbtrees you'll have to implement your own insert and search cores. + This will avoid us to use callbacks and to drop drammatically performances. + 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; +} +----------------------------------------------------------------------- +*/ + +#ifndef _LINUX_RBTREE_H +#define _LINUX_RBTREE_H + +#include <linux/stddef.h> + +struct rb_node +{ + unsigned long rb_parent_color; +#define RB_RED 0 +#define RB_BLACK 1 + 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_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_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)) + +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 *); + +/* Fast replacement of a single node without remove/rebalance/add/rebalance */ +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_left = node->rb_right = NULL; + + *rb_link = node; +} + +#endif /* _LINUX_RBTREE_H */ diff --git a/include/mtd/ubi-user.h b/include/mtd/ubi-user.h new file mode 100644 index 0000000000..62d8c936b2 --- /dev/null +++ b/include/mtd/ubi-user.h @@ -0,0 +1,300 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Artem Bityutskiy (Битюцкий Артём) + */ + +#ifndef __UBI_USER_H__ +#define __UBI_USER_H__ + +/* + * UBI device creation (the same as MTD device attachment) + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * MTD devices may be attached using %UBI_IOCATT ioctl command of the UBI + * control device. The caller has to properly fill and pass + * &struct ubi_attach_req object - UBI will attach the MTD device specified in + * the request and return the newly created UBI device number as the ioctl + * return value. + * + * UBI device deletion (the same as MTD device detachment) + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * An UBI device maybe deleted with %UBI_IOCDET ioctl command of the UBI + * control device. + * + * UBI volume creation + * ~~~~~~~~~~~~~~~~~~~ + * + * 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. + * + * UBI volume deletion + * ~~~~~~~~~~~~~~~~~~~ + * + * 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. + * + * UBI volume re-size + * ~~~~~~~~~~~~~~~~~~ + * + * 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. + * + * UBI volume update + * ~~~~~~~~~~~~~~~~~ + * + * 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 + * 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: + * + * fd = open("/dev/my_volume"); + * ioctl(fd, UBI_IOCVOLUP, &image_size); + * write(fd, buf, image_size); + * close(fd); + * + * Atomic eraseblock change + * ~~~~~~~~~~~~~~~~~~~~~~~~ + * + * 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. + */ + +/* + * When a new UBI volume or UBI device is created, users may either specify the + * volume/device number they want to create or to let UBI automatically assign + * the number using these constants. + */ +#define UBI_VOL_NUM_AUTO (-1) +#define UBI_DEV_NUM_AUTO (-1) + +/* Maximum volume name length */ +#define UBI_MAX_VOLUME_NAME 127 + +/* 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) +/* Re-size an UBI volume */ +#define UBI_IOCRSVOL _IOW(UBI_IOC_MAGIC, 2, struct ubi_rsvol_req) + +/* 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) + +/* 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) + +/* 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, +}; + +/* + * UBI volume type constants. + * + * @UBI_DYNAMIC_VOLUME: dynamic volume + * @UBI_STATIC_VOLUME: static volume + */ +enum { + UBI_DYNAMIC_VOLUME = 3, + UBI_STATIC_VOLUME = 4, +}; + +/** + * 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) + * @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 + * parameters it has to use. The number which should be assigned to the new UBI + * device is passed in @ubi_num. UBI may automatically assign the number if + * @UBI_DEV_NUM_AUTO is passed. In this case, the device number is returned in + * @ubi_num. + * + * Most applications should pass %0 in @vid_hdr_offset to make UBI use default + * offset of the VID header within physical eraseblocks. The default offset is + * the next min. I/O unit after the EC header. For example, it will be offset + * 512 in case of a 512 bytes page NAND flash with no sub-page support. Or + * 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. + */ +struct ubi_attach_req { + int32_t ubi_num; + int32_t mtd_num; + int32_t vid_hdr_offset; + uint8_t padding[12]; +}; + +/** + * struct ubi_mkvol_req - volume description data structure used in + * volume creation requests. + * @vol_id: volume number + * @alignment: volume alignment + * @bytes: volume size in bytes + * @vol_type: volume type (%UBI_DYNAMIC_VOLUME or %UBI_STATIC_VOLUME) + * @padding1: reserved for future, not used, has to be zeroed + * @name_len: volume name length + * @padding2: reserved for future, not used, has to be zeroed + * @name: volume name + * + * This structure is used by user-space programs when creating new volumes. The + * @used_bytes field is only necessary when creating static volumes. + * + * The @alignment field specifies the required alignment of the volume logical + * eraseblock. This means, that the size of logical eraseblocks will be aligned + * to this number, i.e., + * (UBI device logical eraseblock size) mod (@alignment) = 0. + * + * To put it differently, the logical eraseblock of this volume may be slightly + * shortened in order to make it properly aligned. The alignment has to be + * multiple of the flash minimal input/output unit, or %1 to utilize the entire + * available space of logical eraseblocks. + * + * The @alignment field may be useful, for example, when one wants to maintain + * a block device on top of an UBI volume. In this case, it is desirable to fit + * an integer number of blocks in logical eraseblocks of this UBI volume. With + * alignment it is possible to update this volume using plane UBI volume image + * 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]; + char name[UBI_MAX_VOLUME_NAME + 1]; +} __attribute__ ((packed)); + +/** + * struct ubi_rsvol_req - a data structure used in volume re-size requests. + * @vol_id: ID of the volume to re-size + * @bytes: new size of the volume in bytes + * + * 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 + * 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)); + +/** + * struct ubi_leb_change_req - a data structure used in atomic logical + * eraseblock 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) + * @padding: reserved for future, not used, has to be zeroed + */ +struct ubi_leb_change_req { + int32_t lnum; + int32_t bytes; + uint8_t dtype; + uint8_t padding[7]; +} __attribute__ ((packed)); + +/** + * ubi_attach_mtd_dev - attach an MTD device. + * @mtd_dev: MTD device description object + * @ubi_num: number to assign to the new UBI device + * @vid_hdr_offset: VID header offset + * + * This function attaches MTD device @mtd_dev to UBI and assign @ubi_num number + * to the newly created UBI device, unless @ubi_num is %UBI_DEV_NUM_AUTO, in + * which case this function finds a vacant device nubert and assings it + * automatically. Returns the new UBI device number in case of success and a + * negative error code in case of failure. + * + * This of course is originally not exported but is now part of the UBI + * interface to barebox. + */ +int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num, int vid_hdr_offset); + +/** + * ubi_detach_mtd_dev - detach an MTD device. + * @ubi_num: UBI device number to detach from + * @anyway: detach MTD even if device reference count is not zero + * + * This function destroys an UBI device number @ubi_num and detaches the + * underlying MTD device. Returns zero in case of success and %-EBUSY if the + * UBI device is busy and cannot be destroyed, and %-EINVAL if it does not + * exist. + * + * This of course is originally not exported but is now part of the UBI + * interface to barebox. + */ +int ubi_detach_mtd_dev(struct mtd_info *mtd, int anyway); + +#endif /* __UBI_USER_H__ */ diff --git a/lib/Makefile b/lib/Makefile index 5afbf132aa..8c5df085b0 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -1,4 +1,5 @@ obj-y += ctype.o +obj-y += rbtree.o obj-y += display_options.o obj-y += ldiv.o obj-y += string.o diff --git a/lib/crc32.c b/lib/crc32.c index be35366fb6..34817824f1 100644 --- a/lib/crc32.c +++ b/lib/crc32.c @@ -132,8 +132,10 @@ static const ulong crc_table[256] = { #define DO8(buf) DO4(buf); DO4(buf); /* ========================================================================= */ -ulong crc32(ulong crc, const unsigned char *buf, uint len) +uint32_t crc32(uint32_t crc, const void *_buf, unsigned int len) { + const unsigned char *buf = _buf; + #ifdef CONFIG_DYNAMIC_CRC_TABLE if (crc_table_empty) make_crc_table(); @@ -153,13 +155,13 @@ ulong crc32(ulong crc, const unsigned char *buf, uint len) EXPORT_SYMBOL(crc32); #endif -#if 0 - /* No ones complement version. JFFS2 (and other things ?) * don't use ones compliment in their CRC calculations. */ -uLong crc32_no_comp(uLong crc, const Bytef *buf, uint len) +uint32_t crc32_no_comp(uint32_t crc, const void *_buf, unsigned int len) { + const unsigned char *buf = _buf; + #ifdef CONFIG_DYNAMIC_CRC_TABLE if (crc_table_empty) make_crc_table(); @@ -176,4 +178,3 @@ uLong crc32_no_comp(uLong crc, const Bytef *buf, uint len) return crc; } -#endif diff --git a/lib/rbtree.c b/lib/rbtree.c new file mode 100644 index 0000000000..99e69bb74c --- /dev/null +++ b/lib/rbtree.c @@ -0,0 +1,389 @@ +/* + Red Black Trees + (C) 1999 Andrea Arcangeli <andrea@suse.de> + (C) 2002 David Woodhouse <dwmw2@infradead.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + linux/lib/rbtree.c +*/ + +#include <linux/rbtree.h> + +static void __rb_rotate_left(struct rb_node *node, struct rb_root *root) +{ + 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); +} + +static void __rb_rotate_right(struct rb_node *node, struct rb_root *root) +{ + 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); + + 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); +} + +void rb_insert_color(struct rb_node *node, struct rb_root *root) +{ + 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; + } + } + + if (parent->rb_right == node) + { + register struct rb_node *tmp; + __rb_rotate_left(parent, root); + tmp = parent; + parent = node; + node = tmp; + } + + rb_set_black(parent); + rb_set_red(gparent); + __rb_rotate_right(gparent, root); + } 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; + } + } + + if (parent->rb_left == node) + { + register struct rb_node *tmp; + __rb_rotate_right(parent, root); + tmp = parent; + parent = node; + node = tmp; + } + + rb_set_black(parent); + rb_set_red(gparent); + __rb_rotate_left(gparent, root); + } + } + + rb_set_black(root->rb_node); +} + +static void __rb_erase_color(struct rb_node *node, struct rb_node *parent, + struct rb_root *root) +{ + 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); + } + 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; + } + 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; + } + } + 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); + } + 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; + } + 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; + } + } + } + if (node) + rb_set_black(node); +} + +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; + } + + 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; + + color: + if (color == RB_BLACK) + __rb_erase_color(child, parent, root); +} + +/* + * This function returns the first node (in sort order) of the tree. + */ +struct rb_node *rb_first(struct rb_root *root) +{ + struct rb_node *n; + + n = root->rb_node; + if (!n) + return NULL; + while (n->rb_left) + n = n->rb_left; + return n; +} + +struct rb_node *rb_last(struct rb_root *root) +{ + struct rb_node *n; + + n = root->rb_node; + if (!n) + return NULL; + while (n->rb_right) + n = n->rb_right; + return n; +} + +struct rb_node *rb_next(struct rb_node *node) +{ + struct rb_node *parent; + + if (rb_parent(node) == node) + return NULL; + + /* 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; + while (node->rb_left) + node=node->rb_left; + return 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; +} + +struct rb_node *rb_prev(struct rb_node *node) +{ + struct rb_node *parent; + + if (rb_parent(node) == node) + return NULL; + + /* 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; + while (node->rb_right) + node=node->rb_right; + return node; + } + + /* 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; +} + +void rb_replace_node(struct rb_node *victim, struct rb_node *new, + struct rb_root *root) +{ + 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; + } + if (victim->rb_left) + rb_set_parent(victim->rb_left, new); + if (victim->rb_right) + rb_set_parent(victim->rb_right, new); + + /* Copy the pointers/colour from the victim to the replacement */ + *new = *victim; +} diff --git a/scripts/genenv b/scripts/genenv index 6a833b161d..de8b4f138d 100755 --- a/scripts/genenv +++ b/scripts/genenv @@ -1,17 +1,19 @@ #!/bin/bash # Generate the default environment file from a list of directories -# usage: genenv <basedir> <dir>... +# usage: genenv <basedir> <objdir> <dir>... # where <basedir> is the base directory for relative pathes in <dir> +# where <objdir> is the base directory for relative pathes for result +objtree=$2 cd $1 || exit 1 -shift +shift 2 tempdir=$(mktemp -d) for i in $*; do cp -r $i/* $tempdir done -scripts/bareboxenv -s $tempdir barebox_default_env +$objtree/scripts/bareboxenv -s $tempdir $objtree/barebox_default_env rm -r $tempdir |