diff options
| -rw-r--r-- | recipes/linux/linux-2.6.24/hipox/hipox-nand.patch | 333 | ||||
| -rw-r--r-- | recipes/linux/linux-2.6.24/hipox/hipox-ubifs.patch | 43974 | ||||
| -rw-r--r-- | recipes/linux/linux_2.6.24.bb | 4 |
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 +@@ |
