summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--recipes/linux/linux-2.6.24/hipox/hipox-nand.patch333
-rw-r--r--recipes/linux/linux-2.6.24/hipox/hipox-ubifs.patch43974
-rw-r--r--recipes/linux/linux_2.6.24.bb4
3 files changed, 44310 insertions, 1 deletions
diff --git a/recipes/linux/linux-2.6.24/hipox/hipox-nand.patch b/recipes/linux/linux-2.6.24/hipox/hipox-nand.patch
new file mode 100644
index 0000000000..1e37d12774
--- /dev/null
+++ b/recipes/linux/linux-2.6.24/hipox/hipox-nand.patch
@@ -0,0 +1,333 @@
+diff -Nurd linux-2.6.24-base/arch/arm/mach-hipox/hipox.c linux-2.6.24/arch/arm/mach-hipox/hipox.c
+--- linux-2.6.24-base/arch/arm/mach-hipox/hipox.c 2009-04-16 13:21:06.000000000 +0000
++++ linux-2.6.24/arch/arm/mach-hipox/hipox.c 2009-04-17 07:04:33.000000000 +0000
+@@ -155,7 +155,7 @@
+ { CORE_MODULE_BASE, __phys_to_pfn(CORE_MODULE_BASE_PA), SZ_4K, MT_DEVICE },
+ { APB_BRIDGE_A_BASE, __phys_to_pfn(APB_BRIDGE_A_BASE_PA), SZ_16M, MT_DEVICE },
+ { STATIC_CONTROL_BASE, __phys_to_pfn(STATIC_CONTROL_BASE_PA), SZ_4K, MT_DEVICE },
+- { STATIC_CS0_BASE, __phys_to_pfn(STATIC_CS0_BASE_PA), SZ_4K, MT_DEVICE },
++ { STATIC_CS0_BASE, __phys_to_pfn(STATIC_CS0_BASE_PA), SZ_4M, MT_DEVICE },
+ { STATIC_CS1_BASE, __phys_to_pfn(STATIC_CS1_BASE_PA), SZ_4K, MT_DEVICE },
+ { STATIC_CS2_BASE, __phys_to_pfn(STATIC_CS2_BASE_PA), SZ_4K, MT_DEVICE },
+ { APB_BRIDGE_B_BASE, __phys_to_pfn(APB_BRIDGE_B_BASE_PA), SZ_16M, MT_DEVICE },
+diff -Nurd linux-2.6.24-base/drivers/mtd/maps/physmap.c linux-2.6.24/drivers/mtd/maps/physmap.c
+--- linux-2.6.24-base/drivers/mtd/maps/physmap.c 2008-01-24 22:58:37.000000000 +0000
++++ linux-2.6.24/drivers/mtd/maps/physmap.c 2009-04-17 07:04:33.000000000 +0000
+@@ -22,6 +22,12 @@
+ #include <linux/mtd/physmap.h>
+ #include <asm/io.h>
+
++#if defined (CONFIG_ARCH_HIPOX)
++#include <asm/arch/hardware.h>
++/* timing for NOR flash */
++#define STATIC_BUS_FLASH_CONFIG 0x4f1f3f0d /* fast ASIC settings, 70ns */
++#endif /* CONFIG_ARCH_HIPOX */
++
+ struct physmap_flash_info {
+ struct mtd_info *mtd;
+ struct map_info map;
+@@ -88,6 +94,11 @@
+ if (physmap_data == NULL)
+ return -ENODEV;
+
++#if defined (CONFIG_ARCH_HIPOX)
++/* init timing for static memory controller */
++ writel(STATIC_BUS_FLASH_CONFIG, STATIC_CONTROL_BANK0);
++#endif /* CONFIG_ARCH_HIPOX */
++
+ printk(KERN_NOTICE "physmap platform flash device: %.8llx at %.8llx\n",
+ (unsigned long long)(dev->resource->end - dev->resource->start + 1),
+ (unsigned long long)dev->resource->start);
+@@ -207,6 +218,10 @@
+ #endif
+
+ #ifdef PHYSMAP_COMPAT
++static void physmap_flash_release(struct device *dev)
++{
++}
++
+ static struct physmap_flash_data physmap_flash_data = {
+ .width = CONFIG_MTD_PHYSMAP_BANKWIDTH,
+ };
+@@ -222,6 +237,7 @@
+ .id = 0,
+ .dev = {
+ .platform_data = &physmap_flash_data,
++ .release = physmap_flash_release, /* needed for module build */
+ },
+ .num_resources = 1,
+ .resource = &physmap_flash_resource,
+diff -Nurd linux-2.6.24-base/drivers/mtd/nand/Kconfig linux-2.6.24/drivers/mtd/nand/Kconfig
+--- linux-2.6.24-base/drivers/mtd/nand/Kconfig 2008-01-24 22:58:37.000000000 +0000
++++ linux-2.6.24/drivers/mtd/nand/Kconfig 2009-04-17 07:04:33.000000000 +0000
+@@ -283,6 +283,11 @@
+ tristate "Support for NAND Flash on CM-X270 modules"
+ depends on MTD_NAND && MACH_ARMCORE
+
++config MTD_NAND_HIPOX
++ tristate "NAND Flash device on OXE810 based HydraIP board"
++ depends on MTD_NAND && ARCH_HIPOX
++ help
++ Support for NAND flash on OXE180 based HydraIP platform.
+
+ config MTD_NAND_NANDSIM
+ tristate "Support for NAND Flash Simulator"
+diff -Nurd linux-2.6.24-base/drivers/mtd/nand/Makefile linux-2.6.24/drivers/mtd/nand/Makefile
+--- linux-2.6.24-base/drivers/mtd/nand/Makefile 2008-01-24 22:58:37.000000000 +0000
++++ linux-2.6.24/drivers/mtd/nand/Makefile 2009-04-17 07:04:33.000000000 +0000
+@@ -29,5 +29,6 @@
+ obj-$(CONFIG_MTD_NAND_BASLER_EXCITE) += excite_nandflash.o
+ obj-$(CONFIG_MTD_NAND_PLATFORM) += plat_nand.o
+ obj-$(CONFIG_MTD_ALAUDA) += alauda.o
++obj-$(CONFIG_MTD_NAND_HIPOX) += hipox_nand.o
+
+ nand-objs := nand_base.o nand_bbt.o
+diff -Nurd linux-2.6.24-base/drivers/mtd/nand/hipox_nand.c linux-2.6.24/drivers/mtd/nand/hipox_nand.c
+--- linux-2.6.24-base/drivers/mtd/nand/hipox_nand.c 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.24/drivers/mtd/nand/hipox_nand.c 2009-04-17 07:26:17.000000000 +0000
+@@ -0,0 +1,230 @@
++/*
++ * drivers/mtd/nand/hipox_nand.c
++ *
++ * Copyright (C) 2009 DResearch Digital Media Systems GmbH
++ *
++ * $Id:$
++ *
++ * 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.
++ *
++ * Overview:
++ * This is a device driver for the NAND flash device found on the
++ * OXE810 based HydraIP board.
++ */
++
++#include <linux/slab.h>
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/nand.h>
++#include <linux/mtd/partitions.h>
++#include <linux/delay.h>
++#include <asm/io.h>
++#include <asm/arch/hardware.h>
++#include <asm/sizes.h>
++#include <asm/mach-types.h>
++
++// the testboards ran down to a value of 4
++//#define STATIC_BUS_FLASH_CONFIG 0x4f1f3f3f /* slow settings, 345 ns */
++//#define STATIC_BUS_FLASH_CONFIG 0x4f1f3f0d /* fast settings, 70 ns */
++//#define STATIC_BUS_FLASH_CONFIG 0x4f1f3f09 /* ultra fast settings, 50 ns */
++#define STATIC_BUS_FLASH_CONFIG 0x4f1f3f04 /* warp settings, 27 ns */
++
++/*
++ * MTD structure for HydraIP board
++ */
++static struct mtd_info *hipox_nand_mtd = NULL;
++
++#ifdef CONFIG_MTD_PARTITIONS
++static const char *part_probes[] = { "cmdlinepart", NULL };
++
++#define NUM_PARTITIONS 2
++
++/*
++ * Define static partitions for flash device
++ */
++static struct mtd_partition partition_info[NUM_PARTITIONS] = {
++ {
++ .name = "boot",
++ .offset = 0x00000000,
++ .size = 0x02000000,
++ }, {
++ .name = "system",
++ .offset = 0x02000000,
++ .size = 0x0e000000,
++ },
++};
++#endif
++
++
++/*
++ * hardware specific access to control-lines
++ */
++static void hipox_nand_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl)
++{
++ struct nand_chip *this = mtd->priv;
++
++ if (ctrl & NAND_CTRL_CHANGE) {
++ unsigned long IO_ADDR_W = (unsigned long) this->IO_ADDR_W;
++
++ IO_ADDR_W = CONFIG_SYS_NAND_BASE;
++
++ if (ctrl & NAND_NCE)
++ writel(0x20000000, GPIO_A_OUTPUT_CLEAR); /* assert CS-NAND */
++ else
++ writel(0x20000000, GPIO_A_OUTPUT_SET); /* deassert CS-NAND */
++
++ if (ctrl & NAND_CLE)
++ IO_ADDR_W = CONFIG_SYS_NAND_COMMAND_LATCH;
++ if (ctrl & NAND_ALE)
++ IO_ADDR_W = CONFIG_SYS_NAND_ADDRESS_LATCH;
++
++ this->IO_ADDR_W = (void *)IO_ADDR_W;
++ }
++
++ if (cmd != NAND_CMD_NONE)
++ writeb(cmd, this->IO_ADDR_W);
++}
++
++static void hipox_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
++{
++ struct nand_chip *chip = mtd->priv;
++ volatile uint8_t *io = chip->IO_ADDR_R;
++
++ if((((int)buf) & 1) || (len & 1))
++ {
++ while(len-- > 0)
++ *buf++ = *io;
++
++ return;
++ }
++
++ // now it's aligned, group to 16 bit access
++ {
++ uint16_t *ptr16 = (uint16_t *)buf;
++ len >>= 1;
++
++ while(len-- > 0)
++ *ptr16++ = *io | (*io << 8);
++ }
++}
++
++/*
++ * Main initialization routine
++ */
++static int __init hipox_nand_init(void)
++{
++ struct nand_chip *this;
++ const char *part_type = NULL;
++ int mtd_parts_nb = 0;
++ struct mtd_partition *mtd_parts = NULL;
++
++ if (!machine_is_hipox())
++ return -ENXIO;
++
++ /* Allocate memory for MTD device structure and private data */
++ hipox_nand_mtd = kmalloc(sizeof(struct mtd_info)+sizeof(struct nand_chip), GFP_KERNEL);
++
++ if (!hipox_nand_mtd) {
++ printk("Unable to allocate HIPOX_NAND MTD device structure.\n");
++ return -ENOMEM;
++ }
++
++ /* Get pointer to private data */
++ this = (struct nand_chip *)&hipox_nand_mtd[1];
++
++ if (!this) {
++ printk("Unable to allocate HIPOX_NAND MTD NAND device structure.\n");
++ kfree(hipox_nand_mtd);
++ return -ENOMEM;
++ }
++
++ writel(STATIC_BUS_FLASH_CONFIG, STATIC_CONTROL_BANK0);
++
++ /* Initialize structures */
++ memset(hipox_nand_mtd, 0, sizeof(struct mtd_info));
++ memset(this, 0, sizeof(struct nand_chip));
++
++ // assert CS-NAND
++ writel(0x20000000, GPIO_A_OUTPUT_SET);
++ writel(0x20000000, GPIO_A_OUTPUT_ENABLE_SET);
++
++ // deselect alternate function
++ writel(readl(SYS_CTRL_GPIO_PRIMSEL_CTRL_0) & ~0x20000000,
++ SYS_CTRL_GPIO_PRIMSEL_CTRL_0);
++ writel(readl(SYS_CTRL_GPIO_SECSEL_CTRL_0) & ~0x20000000,
++ SYS_CTRL_GPIO_SECSEL_CTRL_0);
++ writel(readl(SYS_CTRL_GPIO_TERTSEL_CTRL_0) & ~0x20000000,
++ SYS_CTRL_GPIO_TERTSEL_CTRL_0);
++
++ writel(0x20000000, GPIO_A_OUTPUT_CLEAR);
++
++ // reset NAND unit
++ writeb(0xff, CONFIG_SYS_NAND_COMMAND_LATCH); // reset command
++ udelay(500);
++
++ // deassert CS-NAND
++ writel(0x20000000, GPIO_A_OUTPUT_SET);
++
++ /* Link the private data with the MTD structure */
++ hipox_nand_mtd->priv = this;
++ hipox_nand_mtd->owner = THIS_MODULE;
++
++ /* insert callbacks */
++ this->IO_ADDR_R = (void *)CONFIG_SYS_NAND_BASE;
++ this->IO_ADDR_W = (void *)CONFIG_SYS_NAND_BASE;
++ this->cmd_ctrl = hipox_nand_hwcontrol;
++ this->read_buf = hipox_read_buf;
++ this->chip_delay = 25; // 23 still worked on our EvalBoard
++ this->ecc.mode = NAND_ECC_SOFT;
++ printk("Searching for NAND flash...\n");
++
++ /* Scan to find existence of the device */
++ if (nand_scan(hipox_nand_mtd, 1)) {
++ kfree(hipox_nand_mtd);
++ return -ENXIO;
++ }
++#ifdef CONFIG_MTD_PARTITIONS
++ hipox_nand_mtd->name = "hipox-nand";
++ mtd_parts_nb = parse_mtd_partitions(hipox_nand_mtd, part_probes, &mtd_parts, 0);
++ if (mtd_parts_nb > 0)
++ part_type = "command line";
++ else
++ mtd_parts_nb = 0;
++#endif
++ if (mtd_parts_nb == 0) {
++ mtd_parts = partition_info;
++ mtd_parts_nb = NUM_PARTITIONS;
++ part_type = "static";
++ }
++
++ /* Register the partitions */
++ printk(KERN_NOTICE "Using %s partition definition\n", part_type);
++ add_mtd_partitions(hipox_nand_mtd, mtd_parts, mtd_parts_nb);
++
++ /* Return happy */
++ return 0;
++}
++
++module_init(hipox_nand_init);
++
++/*
++ * Clean up routine
++ */
++static void __exit hipox_nand_cleanup(void)
++{
++ /* Unregister the device */
++ //del_mtd_device(hipox_nand_mtd);
++ nand_release(hipox_nand_mtd);
++
++ /* Free the MTD device structure */
++ kfree(hipox_nand_mtd);
++}
++
++module_exit(hipox_nand_cleanup);
++
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("Rene Grosser <rgrosser@dresearch.de>, Steffen Sledz <sledz@dresearch.de>");
++MODULE_DESCRIPTION("MTD map driver for OXE810 based HydraIP device");
+diff -Nurd linux-2.6.24-base/include/asm-arm/arch-hipox/hardware.h linux-2.6.24/include/asm-arm/arch-hipox/hardware.h
+--- linux-2.6.24-base/include/asm-arm/arch-hipox/hardware.h 2009-04-16 13:21:35.000000000 +0000
++++ linux-2.6.24/include/asm-arm/arch-hipox/hardware.h 2009-04-17 07:25:53.000000000 +0000
+@@ -134,6 +134,11 @@
+ #define COPRO_REGS_BASE (APB_BRIDGE_B_BASE + 0xB00000)
+ #define DMA_SG_BASE (APB_BRIDGE_B_BASE + 0xC00000)
+
++/* NAND access */
++#define CONFIG_SYS_NAND_BASE STATIC_CS0_BASE
++#define CONFIG_SYS_NAND_ADDRESS_LATCH (CONFIG_SYS_NAND_BASE + 0x8000)
++#define CONFIG_SYS_NAND_COMMAND_LATCH (CONFIG_SYS_NAND_BASE + 0x4000)
++
+ /* Interrupt Controller registers */
+ #define RPS_IC_BASE RPS_BASE
+ #define RPS_IRQ_STATUS (RPS_IC_BASE)
diff --git a/recipes/linux/linux-2.6.24/hipox/hipox-ubifs.patch b/recipes/linux/linux-2.6.24/hipox/hipox-ubifs.patch
new file mode 100644
index 0000000000..2aef97d4eb
--- /dev/null
+++ b/recipes/linux/linux-2.6.24/hipox/hipox-ubifs.patch
@@ -0,0 +1,43974 @@
+diff -Nurd linux-2.6.24.orig/crypto/Kconfig linux-2.6.24/crypto/Kconfig
+--- linux-2.6.24.orig/crypto/Kconfig 2009-04-17 09:45:12.000000000 +0200
++++ linux-2.6.24/crypto/Kconfig 2009-04-17 09:49:26.000000000 +0200
+@@ -502,6 +502,14 @@
+ Authenc: Combined mode wrapper for IPsec.
+ This is required for IPSec.
+
++config CRYPTO_LZO
++ tristate "LZO compression algorithm"
++ select CRYPTO_ALGAPI
++ select LZO_COMPRESS
++ select LZO_DECOMPRESS
++ help
++ This is the LZO algorithm.
++
+ source "drivers/crypto/Kconfig"
+
+ endif # if CRYPTO
+diff -Nurd linux-2.6.24.orig/crypto/Makefile linux-2.6.24/crypto/Makefile
+--- linux-2.6.24.orig/crypto/Makefile 2009-04-17 09:45:12.000000000 +0200
++++ linux-2.6.24/crypto/Makefile 2009-04-17 09:49:26.000000000 +0200
+@@ -51,6 +51,7 @@
+ obj-$(CONFIG_CRYPTO_DEFLATE) += deflate.o
+ obj-$(CONFIG_CRYPTO_MICHAEL_MIC) += michael_mic.o
+ obj-$(CONFIG_CRYPTO_CRC32C) += crc32c.o
++obj-$(CONFIG_CRYPTO_LZO) += lzo.o
+ obj-$(CONFIG_CRYPTO_AUTHENC) += authenc.o
+
+ obj-$(CONFIG_CRYPTO_TEST) += tcrypt.o
+diff -Nurd linux-2.6.24.orig/crypto/lzo.c linux-2.6.24/crypto/lzo.c
+--- linux-2.6.24.orig/crypto/lzo.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.24/crypto/lzo.c 2009-04-17 09:49:26.000000000 +0200
+@@ -0,0 +1,106 @@
++/*
++ * Cryptographic API.
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License version 2 as published by
++ * the Free Software Foundation.
++ *
++ * This program is distributed in the hope that it will be useful, but WITHOUT
++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
++ * more details.
++ *
++ * You should have received a copy of the GNU General Public License along with
++ * this program; if not, write to the Free Software Foundation, Inc., 51
++ * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
++ *
++ */
++
++#include <linux/init.h>
++#include <linux/module.h>
++#include <linux/crypto.h>
++#include <linux/vmalloc.h>
++#include <linux/lzo.h>
++
++struct lzo_ctx {
++ void *lzo_comp_mem;
++};
++
++static int lzo_init(struct crypto_tfm *tfm)
++{
++ struct lzo_ctx *ctx = crypto_tfm_ctx(tfm);
++
++ ctx->lzo_comp_mem = vmalloc(LZO1X_MEM_COMPRESS);
++ if (!ctx->lzo_comp_mem)
++ return -ENOMEM;
++
++ return 0;
++}
++
++static void lzo_exit(struct crypto_tfm *tfm)
++{
++ struct lzo_ctx *ctx = crypto_tfm_ctx(tfm);
++
++ vfree(ctx->lzo_comp_mem);
++}
++
++static int lzo_compress(struct crypto_tfm *tfm, const u8 *src,
++ unsigned int slen, u8 *dst, unsigned int *dlen)
++{
++ struct lzo_ctx *ctx = crypto_tfm_ctx(tfm);
++ size_t tmp_len = *dlen; /* size_t(ulong) <-> uint on 64 bit */
++ int err;
++
++ err = lzo1x_1_compress(src, slen, dst, &tmp_len, ctx->lzo_comp_mem);
++
++ if (err != LZO_E_OK)
++ return -EINVAL;
++
++ *dlen = tmp_len;
++ return 0;
++}
++
++static int lzo_decompress(struct crypto_tfm *tfm, const u8 *src,
++ unsigned int slen, u8 *dst, unsigned int *dlen)
++{
++ int err;
++ size_t tmp_len = *dlen; /* size_t(ulong) <-> uint on 64 bit */
++
++ err = lzo1x_decompress_safe(src, slen, dst, &tmp_len);
++
++ if (err != LZO_E_OK)
++ return -EINVAL;
++
++ *dlen = tmp_len;
++ return 0;
++
++}
++
++static struct crypto_alg alg = {
++ .cra_name = "lzo",
++ .cra_flags = CRYPTO_ALG_TYPE_COMPRESS,
++ .cra_ctxsize = sizeof(struct lzo_ctx),
++ .cra_module = THIS_MODULE,
++ .cra_list = LIST_HEAD_INIT(alg.cra_list),
++ .cra_init = lzo_init,
++ .cra_exit = lzo_exit,
++ .cra_u = { .compress = {
++ .coa_compress = lzo_compress,
++ .coa_decompress = lzo_decompress } }
++};
++
++static int __init init(void)
++{
++ return crypto_register_alg(&alg);
++}
++
++static void __exit fini(void)
++{
++ crypto_unregister_alg(&alg);
++}
++
++module_init(init);
++module_exit(fini);
++
++MODULE_LICENSE("GPL");
++MODULE_DESCRIPTION("LZO Compression Algorithm");
+diff -Nurd linux-2.6.24.orig/drivers/mtd/ubi/Kconfig linux-2.6.24/drivers/mtd/ubi/Kconfig
+--- linux-2.6.24.orig/drivers/mtd/ubi/Kconfig 2009-04-17 09:45:11.000000000 +0200
++++ linux-2.6.24/drivers/mtd/ubi/Kconfig 2009-04-17 09:49:26.000000000 +0200
+@@ -24,8 +24,13 @@
+ erase counter value and the lowest erase counter value of eraseblocks
+ of UBI devices. When this threshold is exceeded, UBI starts performing
+ wear leveling by means of moving data from eraseblock with low erase
+- counter to eraseblocks with high erase counter. Leave the default
+- value if unsure.
++ counter to eraseblocks with high erase counter.
++
++ The default value should be OK for SLC NAND flashes, NOR flashes and
++ other flashes which have eraseblock life-cycle 100000 or more.
++ However, in case of MLC NAND flashes which typically have eraseblock
++ life-cycle less then 10000, the threshold should be lessened (e.g.,
++ to 128 or 256, although it does not have to be power of 2).
+
+ config MTD_UBI_BEB_RESERVE
+ int "Percentage of reserved eraseblocks for bad eraseblocks handling"
+diff -Nurd linux-2.6.24.orig/drivers/mtd/ubi/build.c linux-2.6.24/drivers/mtd/ubi/build.c
+--- linux-2.6.24.orig/drivers/mtd/ubi/build.c 2009-04-17 09:45:11.000000000 +0200
++++ linux-2.6.24/drivers/mtd/ubi/build.c 2009-04-17 09:49:26.000000000 +0200
+@@ -21,11 +21,16 @@
+ */
+
+ /*
+- * This file includes UBI initialization and building of UBI devices. At the
+- * moment UBI devices may only be added while UBI is initialized, but dynamic
+- * device add/remove functionality is planned. Also, 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.
++ * 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.
+ */
+
+ #include <linux/err.h>
+@@ -33,7 +38,9 @@
+ #include <linux/moduleparam.h>
+ #include <linux/stringify.h>
+ #include <linux/stat.h>
++#include <linux/miscdevice.h>
+ #include <linux/log2.h>
++#include <linux/kthread.h>
+ #include "ubi.h"
+
+ /* Maximum length of the 'mtd=' parameter */
+@@ -43,29 +50,39 @@
+ * struct mtd_dev_param - MTD device parameter description data structure.
+ * @name: MTD device name or number string
+ * @vid_hdr_offs: VID header offset
+- * @data_offs: data offset
+ */
+-struct mtd_dev_param
+-{
++struct mtd_dev_param {
+ char name[MTD_PARAM_LEN_MAX];
+ int vid_hdr_offs;
+- int data_offs;
+ };
+
+ /* Numbers of elements set in the @mtd_dev_param array */
+-static int mtd_devs = 0;
++static int mtd_devs;
+
+ /* MTD devices specification parameters */
+ static struct mtd_dev_param mtd_dev_param[UBI_MAX_DEVICES];
+
+-/* Number of UBI devices in system */
+-int ubi_devices_cnt;
++/* Root UBI "class" object (corresponds to '/<sysfs>/class/ubi/') */
++struct class *ubi_class;
++
++/* 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,
++};
+
+ /* All UBI devices in system */
+-struct ubi_device *ubi_devices[UBI_MAX_DEVICES];
++static struct ubi_device *ubi_devices[UBI_MAX_DEVICES];
+
+-/* Root UBI "class" object (corresponds to '/<sysfs>/class/ubi/') */
+-struct class *ubi_class;
++/* 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)
+@@ -101,42 +118,157 @@
+ __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);
++
++/**
++ * 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 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;
++}
+
+ /* "Show" method for files in '/<sysfs>/class/ubi/ubiX/' */
+ static ssize_t dev_attribute_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+ {
+- const struct ubi_device *ubi;
++ 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)
+- return sprintf(buf, "%d\n", ubi->leb_size);
++ ret = sprintf(buf, "%d\n", ubi->leb_size);
+ else if (attr == &dev_avail_eraseblocks)
+- return sprintf(buf, "%d\n", ubi->avail_pebs);
++ ret = sprintf(buf, "%d\n", ubi->avail_pebs);
+ else if (attr == &dev_total_eraseblocks)
+- return sprintf(buf, "%d\n", ubi->good_peb_count);
++ ret = sprintf(buf, "%d\n", ubi->good_peb_count);
+ else if (attr == &dev_volumes_count)
+- return sprintf(buf, "%d\n", ubi->vol_count);
++ ret = sprintf(buf, "%d\n", ubi->vol_count - UBI_INT_VOL_COUNT);
+ else if (attr == &dev_max_ec)
+- return sprintf(buf, "%d\n", ubi->max_ec);
++ ret = sprintf(buf, "%d\n", ubi->max_ec);
+ else if (attr == &dev_reserved_for_bad)
+- return sprintf(buf, "%d\n", ubi->beb_rsvd_pebs);
++ ret = sprintf(buf, "%d\n", ubi->beb_rsvd_pebs);
+ else if (attr == &dev_bad_peb_count)
+- return sprintf(buf, "%d\n", ubi->bad_peb_count);
++ ret = sprintf(buf, "%d\n", ubi->bad_peb_count);
+ else if (attr == &dev_max_vol_count)
+- return sprintf(buf, "%d\n", ubi->vtbl_slots);
++ ret = sprintf(buf, "%d\n", ubi->vtbl_slots);
+ else if (attr == &dev_min_io_size)
+- return sprintf(buf, "%d\n", ubi->min_io_size);
++ ret = sprintf(buf, "%d\n", ubi->min_io_size);
+ else if (attr == &dev_bgt_enabled)
+- return sprintf(buf, "%d\n", ubi->thread_enabled);
++ ret = sprintf(buf, "%d\n", ubi->thread_enabled);
++ else if (attr == &dev_mtd_num)
++ ret = sprintf(buf, "%d\n", ubi->mtd->index);
+ else
+- BUG();
++ ret = -EINVAL;
+
+- return 0;
++ ubi_put_device(ubi);
++ return ret;
+ }
+
+-/* Fake "release" method for UBI devices */
+-static void dev_release(struct device *dev) { }
++static void dev_release(struct device *dev)
++{
++ struct ubi_device *ubi = container_of(dev, struct ubi_device, dev);
++
++ kfree(ubi);
++}
+
+ /**
+ * ubi_sysfs_init - initialize sysfs for an UBI device.
+@@ -150,68 +282,44 @@
+ int err;
+
+ ubi->dev.release = dev_release;
+- ubi->dev.devt = MKDEV(ubi->major, 0);
++ 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)
+- goto out;
++ return err;
+
+ err = device_create_file(&ubi->dev, &dev_eraseblock_size);
+ if (err)
+- goto out_unregister;
++ return err;
+ err = device_create_file(&ubi->dev, &dev_avail_eraseblocks);
+ if (err)
+- goto out_eraseblock_size;
++ return err;
+ err = device_create_file(&ubi->dev, &dev_total_eraseblocks);
+ if (err)
+- goto out_avail_eraseblocks;
++ return err;
+ err = device_create_file(&ubi->dev, &dev_volumes_count);
+ if (err)
+- goto out_total_eraseblocks;
++ return err;
+ err = device_create_file(&ubi->dev, &dev_max_ec);
+ if (err)
+- goto out_volumes_count;
++ return err;
+ err = device_create_file(&ubi->dev, &dev_reserved_for_bad);
+ if (err)
+- goto out_volumes_max_ec;
++ return err;
+ err = device_create_file(&ubi->dev, &dev_bad_peb_count);
+ if (err)
+- goto out_reserved_for_bad;
++ return err;
+ err = device_create_file(&ubi->dev, &dev_max_vol_count);
+ if (err)
+- goto out_bad_peb_count;
++ return err;
+ err = device_create_file(&ubi->dev, &dev_min_io_size);
+ if (err)
+- goto out_max_vol_count;
++ return err;
+ err = device_create_file(&ubi->dev, &dev_bgt_enabled);
+ if (err)
+- goto out_min_io_size;
+-
+- return 0;
+-
+-out_min_io_size:
+- device_remove_file(&ubi->dev, &dev_min_io_size);
+-out_max_vol_count:
+- device_remove_file(&ubi->dev, &dev_max_vol_count);
+-out_bad_peb_count:
+- device_remove_file(&ubi->dev, &dev_bad_peb_count);
+-out_reserved_for_bad:
+- device_remove_file(&ubi->dev, &dev_reserved_for_bad);
+-out_volumes_max_ec:
+- device_remove_file(&ubi->dev, &dev_max_ec);
+-out_volumes_count:
+- device_remove_file(&ubi->dev, &dev_volumes_count);
+-out_total_eraseblocks:
+- device_remove_file(&ubi->dev, &dev_total_eraseblocks);
+-out_avail_eraseblocks:
+- device_remove_file(&ubi->dev, &dev_avail_eraseblocks);
+-out_eraseblock_size:
+- device_remove_file(&ubi->dev, &dev_eraseblock_size);
+-out_unregister:
+- device_unregister(&ubi->dev);
+-out:
+- ubi_err("failed to initialize sysfs for %s", ubi->ubi_name);
++ return err;
++ err = device_create_file(&ubi->dev, &dev_mtd_num);
+ return err;
+ }
+
+@@ -221,6 +329,7 @@
+ */
+ 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);
+@@ -244,7 +353,26 @@
+
+ for (i = 0; i < ubi->vtbl_slots; i++)
+ if (ubi->volumes[i])
+- ubi_free_volume(ubi, i);
++ ubi_free_volume(ubi, ubi->volumes[i]);
++}
++
++/**
++ * free_user_volumes - free all user volumes.
++ * @ubi: UBI device description object
++ *
++ * Normally the volumes are freed at the release function of the volume device
++ * objects. However, on error paths the volumes have to be freed before the
++ * device objects have been initialized.
++ */
++static void free_user_volumes(struct ubi_device *ubi)
++{
++ int i;
++
++ for (i = 0; i < ubi->vtbl_slots; i++)
++ if (ubi->volumes[i]) {
++ kfree(ubi->volumes[i]->eba_tbl);
++ kfree(ubi->volumes[i]);
++ }
+ }
+
+ /**
+@@ -252,16 +380,13 @@
+ * @ubi: UBI device description object
+ *
+ * This function returns zero in case of success and a negative error code in
+- * case of failure.
++ * case of failure. Note, this function destroys all volumes if it failes.
+ */
+ static int uif_init(struct ubi_device *ubi)
+ {
+ int i, err;
+ dev_t dev;
+
+- mutex_init(&ubi->vtbl_mutex);
+- spin_lock_init(&ubi->volumes_lock);
+-
+ sprintf(ubi->ubi_name, UBI_NAME_STR "%d", ubi->ubi_num);
+
+ /*
+@@ -278,52 +403,72 @@
+ return err;
+ }
+
++ ubi_assert(MINOR(dev) == 0);
+ cdev_init(&ubi->cdev, &ubi_cdev_operations);
+- ubi->major = MAJOR(dev);
+- dbg_msg("%s major is %u", ubi->ubi_name, ubi->major);
++ dbg_gen("%s major is %u", ubi->ubi_name, MAJOR(dev));
+ ubi->cdev.owner = THIS_MODULE;
+
+- dev = MKDEV(ubi->major, 0);
+ err = cdev_add(&ubi->cdev, dev, 1);
+ if (err) {
+- ubi_err("cannot add character device %s", ubi->ubi_name);
++ ubi_err("cannot add character device");
+ goto out_unreg;
+ }
+
+ err = ubi_sysfs_init(ubi);
+ if (err)
+- goto out_cdev;
++ goto out_sysfs;
+
+ for (i = 0; i < ubi->vtbl_slots; i++)
+ if (ubi->volumes[i]) {
+- err = ubi_add_volume(ubi, i);
+- if (err)
++ 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);
+-out_cdev:
+ cdev_del(&ubi->cdev);
+ out_unreg:
+- unregister_chrdev_region(MKDEV(ubi->major, 0),
+- ubi->vtbl_slots + 1);
++ 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
++ *
++ * Note, since this function un-registers UBI volume device objects (@vol->dev),
++ * the memory allocated voe the volumes is freed as well (in the release
++ * function).
+ */
+ static void uif_close(struct ubi_device *ubi)
+ {
+ kill_volumes(ubi);
+ ubi_sysfs_close(ubi);
+ cdev_del(&ubi->cdev);
+- unregister_chrdev_region(MKDEV(ubi->major, 0), ubi->vtbl_slots + 1);
++ unregister_chrdev_region(ubi->cdev.dev, ubi->vtbl_slots + 1);
++}
++
++/**
++ * free_internal_volumes - free internal volumes.
++ * @ubi: UBI device description object
++ */
++static void free_internal_volumes(struct ubi_device *ubi)
++{
++ int i;
++
++ for (i = ubi->vtbl_slots;
++ i < ubi->vtbl_slots + UBI_INT_VOL_COUNT; i++) {
++ kfree(ubi->volumes[i]->eba_tbl);
++ kfree(ubi->volumes[i]);
++ }
+ }
+
+ /**
+@@ -370,6 +515,7 @@
+ out_wl:
+ ubi_wl_close(ubi);
+ out_vtbl:
++ free_internal_volumes(ubi);
+ vfree(ubi->vtbl);
+ out_si:
+ ubi_scan_destroy_si(si);
+@@ -377,16 +523,16 @@
+ }
+
+ /**
+- * io_init - initialize I/O unit for a given UBI device.
++ * io_init - initialize I/O sub-system for a given UBI device.
+ * @ubi: UBI device description object
+ *
+ * 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;
++ * 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
++ * @io->min_io_size
+ *
+ * This function returns zero in case of success and a negative error code in
+ * case of failure.
+@@ -407,6 +553,9 @@
+ return -EINVAL;
+ }
+
++ if (ubi->vid_hdr_offset < 0)
++ return -EINVAL;
++
+ /*
+ * Note, in this implementation we support MTD devices with 0x7FFFFFFF
+ * physical eraseblocks maximum.
+@@ -422,9 +571,14 @@
+ 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 */
++ /*
++ * 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("bad min. I/O unit");
++ ubi_err("min. I/O unit (%d) is not power of 2",
++ ubi->min_io_size);
+ return -EINVAL;
+ }
+
+@@ -453,10 +607,8 @@
+ }
+
+ /* Similar for the data offset */
+- if (ubi->leb_start == 0) {
+- ubi->leb_start = ubi->vid_hdr_offset + ubi->vid_hdr_alsize;
+- ubi->leb_start = ALIGN(ubi->leb_start, ubi->min_io_size);
+- }
++ 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);
+@@ -474,7 +626,7 @@
+ 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) {
++ 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;
+@@ -499,8 +651,16 @@
+ ubi->ro_mode = 1;
+ }
+
+- dbg_msg("leb_size %d", ubi->leb_size);
+- dbg_msg("ro_mode %d", ubi->ro_mode);
++ 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
+@@