/* * ---------------------------------------------------------------------------- * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. * ---------------------------------------------------------------------------- * */ /************************************************************************** * Included Files **************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined(CONFIG_MTD_PHYSMAP) || defined(CONFIG_MTD_PHYSMAP_MODULE) || defined(CONFIG_VR9) || defined(CONFIG_AR9) || defined(CONFIG_AR10) #define DO_MTD #include #include #include #include #include #include <../fs/squashfs/squashfs_fs.h> #include #include #if defined(CONFIG_TFFS) && defined(CONFIG_TFFS_PANIC_LOG) && (defined(CONFIG_VR9) || defined(CONFIG_AR10)) #include #endif /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ enum _flash_map_enum { MAP_UNKNOWN, MAP_RAM, MAP_NOR_FLASH, MAP_NAND_FLASH, MAP_SPI_FLASH }; /*-------------------------------------------------------------------------------------*\ * Zuerst wird das JFFS2 gesucht, dann das Squash-FS! \*-------------------------------------------------------------------------------------*/ static const char * const probes[] = { "find_jffs2", "find_ext2fs", "find_squashfs", NULL }; void ifx_ram_mtd_set_rw(struct device *pdev, int); extern int __init root_dev_setup(char *line); static int __init mtdnand_setup_parts(void); unsigned long long ifxmips_flashsize_nand; unsigned int use_nor = 0; unsigned int use_spi = 0; /*------------------------------------------------------------------------------------------*\ * NOR \*------------------------------------------------------------------------------------------*/ struct mtd_partition ifx_nor_partitions[IFX_MTD_NOR_PARTS]; /*--- wird in ifxmips_mtd_nor.c genutzt ---*/ static struct resource ifx_nor_resource[] = { { .start = CPHYSADDR(0x90000000), .end = CPHYSADDR(0x90000000), /*--- wird aus der Flashgroesse berechnet ---*/ .flags = IORESOURCE_MEM, .parent = &iomem_resource }, }; static struct physmap_flash_data ifx_nor_data = { .width = 2, .parts = ifx_nor_partitions, .nr_parts = ARRAY_SIZE(ifx_nor_partitions), .part_probe_types = probes, }; struct platform_device ifx_nor_device[] = { { .name = "ltq-nor", .id = -1, .dev = { .platform_data = &ifx_nor_data, }, .num_resources = 1, .resource = &ifx_nor_resource[0], } }; /*------------------------------------------------------------------------------------------*\ * NAND \*------------------------------------------------------------------------------------------*/ struct mtd_partition ifx_nand_partitions[IFX_MTD_NAND_PARTS]; /*--- wird in ifxmips_mtd_nand.c genutzt ---*/ static struct resource ifx_nand_resource[] = { { .start = 0, .end = 0 + (128), /* 128 MB */ .flags = IORESOURCE_MEM, .parent = &nand_flash_resource, }, }; static struct physmap_flash_data ifx_nand_data = { .width = 1, .parts = ifx_nand_partitions, .nr_parts = ARRAY_SIZE(ifx_nand_partitions), .part_probe_types = NULL }; struct platform_device ifx_nand_device[] = { { /*--- TODO ---*/ .name = "ifx_hsnand", .id = -1, .dev = { .platform_data = &ifx_nand_data, }, .num_resources = 1, .resource = &ifx_nand_resource[0], } }; /*------------------------------------------------------------------------------------------*\ * SPI \*------------------------------------------------------------------------------------------*/ struct mtd_partition ifx_spi_partitions[IFX_MTD_SPI_PARTS]; /*--- wird in ifxmips_sflash.c genutzt ---*/ /* NOTE: CFI probe will correctly detect flash part as 32M, but EMIF * limits addresses to 16M, so using addresses past 16M will wrap */ static struct resource ifx_spi_resource[] = { { .start = 0, .end = 0 + (256 << 10), /* 256 KB */ .flags = IORESOURCE_MEM, .parent = &sflash_resource }, }; static struct physmap_flash_data ifx_spi_data = { .width = 2, .parts = ifx_spi_partitions, .nr_parts = ARRAY_SIZE(ifx_spi_partitions), .part_probe_types = NULL, }; struct platform_device ifx_spi_device[] = { { .name = "ifx_sflash", .id = 0, .dev = { .platform_data = &ifx_spi_data, }, .num_resources = 1, .resource = &ifx_spi_resource[0], } }; /*------------------------------------------------------------------------------------------*\ * RAM \*------------------------------------------------------------------------------------------*/ static struct mtd_partition ifx_ram_partitions[2]; static struct resource ifx_ram_resource[] = { { /* fuer ins RAM geladenes Filesystem */ .start = 0, .end = 0 + (32 << 20), .flags = IORESOURCE_MEM, } }; static struct platdata_mtd_ram ifx_ram_data = { .mapname = "ram-filesystem", .bankwidth = 4, .partitions = ifx_ram_partitions, .nr_partitions = ARRAY_SIZE(ifx_ram_partitions), .set_rw = ifx_ram_mtd_set_rw, .probes = probes }; struct platform_device ifx_ram_device = { .name = "mtd-ram", .id = -1, .dev = { .platform_data = &ifx_ram_data, }, .num_resources = 1, .resource = &ifx_ram_resource[0], }; /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static int ifx_ext2fs_parser_function(struct mtd_info *mtd, struct mtd_partition **p_mtd_pat, struct mtd_part_parser_data *parse_data) { unsigned int magic = 0, readlen = 0; loff_t pos, start_offset, add_offset = 0; /*--- printk(KERN_ERR "[%s] mtd_info->name %s mtd_info->index %u mtd_part_parser_data=0x%p p_mtd_pat=0x%p\n", ---*/ /*--- __func__, mtd->name, mtd->index, parse_data, p_mtd_pat); ---*/ if (strcmp(mtd->name, "ram-filesystem")) { /*--- nur im ram-fs suchen ---*/ printk(KERN_ERR "[%s] exit on mtd %s\n", __func__, mtd->name); return 0; } if(p_mtd_pat) { *p_mtd_pat = kmemdup(ifx_ram_partitions, ARRAY_SIZE(ifx_ram_partitions) * sizeof(struct mtd_partition), GFP_KERNEL); BUG_ON(!*p_mtd_pat); start_offset = pos = (*p_mtd_pat)[1].offset; pr_debug("[%s] mtd[%s]: start_offset := (*p_mtd_pat)[1].offset = %lld\n", __func__, mtd->name, start_offset); // Starten mit einer 256-Byte aligned Adresse. // Begruendung: // Das Ext2 wird 256-Byte aligned. Der Kernel steht davor. Die Startadresse der MTD-RAM-Partition ist also nicht aligned. // Der Suchalgorythmus kann also nicht im schlimmsten Fall das Ext2-Magic nicht finden. // pos wird als auf die ersten 256 Byte NACH dem Kernel-Start positioniert. if((ifx_ram_resource[0].start & ((1 << 8) - 1))) { pos = ((ifx_ram_resource[0].start & ~((1 << 8) - 1)) + 256) - ifx_ram_resource[0].start; printk(KERN_ERR "[%s:%d] Use offset of 0x%llx to search ext2 magic.\n", __FUNCTION__, __LINE__, pos); } while(pos < ((*p_mtd_pat)[1].offset + (*p_mtd_pat)[1].size)) { int ret; /*--- printk(KERN_ERR "[%s] mtd[%s]: searching for magic at mtd%d position 0x%llx\n", ---*/ /*--- __func__, mtd->name, mtd->index, pos); ---*/ ret = mtd_read(mtd, pos, sizeof(unsigned int), &readlen, (u_char*)&magic); /*--- printk(KERN_ERR "[%s] mtd[%s]: mtd_read: read %u bytes, magic = 0x%08x, " ---*/ /*--- "index = %u, pos = 0x%x, return value = %d\n", ---*/ /*--- __func__, mtd->name, readlen, magic, mtd->index, ---*/ /*--- (unsigned int)pos, ret); ---*/ if ((ret < 0) || (readlen < sizeof(unsigned int))) { printk(KERN_ERR "[%s] breaking due to incomplete or erronous mtd_read\n", __func__); return 0; } if(magic == SQUASHFS_MAGIC) { pos += 0x100; add_offset = EXT2_SB_MAGIC_OFFSET + 0x400; pr_debug("[%s] first match at 0x%llx add_offset 0x%llx\n", __func__, pos, add_offset); ret = mtd_read(mtd, pos + add_offset, sizeof(unsigned int), &readlen, (u_char*)&magic); if ((ret < 0) || (readlen < sizeof(unsigned int))) { printk(KERN_ERR "[%s] breaking due to incomplete or erronous mtd_read ext2_magic\n", __func__); return 0; } if((le16_to_cpu(magic) == EXT2_SUPER_MAGIC) || (le16_to_cpu(magic >> 16) == EXT2_SUPER_MAGIC)) { /*-------------------------------------------------------------------------------------*\ * * +---+---------------------+-----------------------+--------------------+ * | | Kernel |SquashFS| ext2 | JFFS2 | * +---+---------------------+-----------------------+--------------------+ * A ^_pos E * * Zu Beginn ist das Layout obiges: * start_offset = A * MTD1 mit Kernel reicht von A bis E * MTD5 fuer JFFS2 kann gesetzt sein, wenn JFFS2 Parser vorher schon was gefunden hat * * Wenn SquashFS gefunden wird, wird MTD1 auf den Kernel verkleinert, * MTD0 fuer das FS wird von pos bis E angelegt * Wenn noch kein MTD5 mit JFFS2 existiert wird dieses innerhalb von MTD0 angelegt * \*-------------------------------------------------------------------------------------*/ (*p_mtd_pat)[0].offset = pos; (*p_mtd_pat)[0].size = ((u_int32_t)start_offset + (u_int32_t)(*p_mtd_pat)[1].size - (u_int32_t)pos); (*p_mtd_pat)[1].size = ((u_int32_t)pos - (u_int32_t)start_offset); /*--- printk(KERN_ERR "[%s:%d] pos: 0x%x | offs[0]: 0x%llx | size[0]: %llu " ---*/ /*--- "| offs[1]: 0x%llx | size[1]: %llu\n", ---*/ /*--- __func__, __LINE__, ---*/ /*--- (unsigned int)pos, ---*/ /*--- (*p_mtd_pat)[0].offset, ---*/ /*--- (*p_mtd_pat)[0].size, ---*/ /*--- (*p_mtd_pat)[1].offset, ---*/ /*--- (*p_mtd_pat)[1].size); ---*/ printk(KERN_ERR "[%s] found partition @pos 0x%x\n", __func__, (unsigned int)pos); return ARRAY_SIZE(ifx_ram_partitions); } } pos += 256; } } return 0; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static int ifx_squashfs_parser_function(struct mtd_info *mtd, struct mtd_partition **p_mtd_pat, struct mtd_part_parser_data *parse_data) { enum _flash_map_enum maptype = MAP_UNKNOWN; unsigned count = 1, maxcount = 0; /*--- printk(KERN_ERR "[%s] mtd_info->name %s mtd_info->index %u " ---*/ /*--- "mtd_part_parser_data=0x%p p_mtd_pat=0x%p\n", ---*/ /*--- __FUNCTION__, mtd->name, mtd->index, parse_data, p_mtd_pat); ---*/ if (!strcmp(mtd->name, "ram-filesystem")) { maptype = MAP_RAM; } else if (!strcmp(mtd->name, "ltq-nor")) { maptype = MAP_NOR_FLASH; } else { printk(KERN_ERR "[%s] with unknown mtd type %s\n", __FUNCTION__, mtd->name); return 0; } pr_debug("[%s] mtd[%s]: detected maptype: %s\n", __func__, mtd->name, maptype == MAP_RAM ? "MAP_RAM" : maptype == MAP_NOR_FLASH ? "MAP_NOR_FLASH" : "(unknown)"); if(p_mtd_pat) { unsigned int magic = 0, readlen = 0; char* p; loff_t pos, start_offset; switch (maptype) { case MAP_NOR_FLASH: *p_mtd_pat = ifx_nor_partitions; maxcount = ARRAY_SIZE(ifx_nor_partitions); break; case MAP_RAM: *p_mtd_pat = ifx_ram_partitions; maxcount = ARRAY_SIZE(ifx_ram_partitions); break; default: break; } /*--- pr_debug("[%s] mtd[%s]: initialised *p_mtd_pat to %p " ---*/ /*--- "(ifx_nor_partitions=%p, ifx_ram_partitions=%p\n", __func__, ---*/ /*--- mtd->name, *p_mtd_pat, ifx_nor_partitions, ifx_ram_partitions); ---*/ *p_mtd_pat = kmemdup(*p_mtd_pat, maxcount * sizeof(struct mtd_partition), GFP_KERNEL); BUG_ON(!*p_mtd_pat); start_offset = pos = (*p_mtd_pat)[count].offset; /*--- pr_debug("[%s] mtd[%s]: start_offset := (*p_mtd_pat)[%d].offset = %lld\n", __func__, mtd->name, count, start_offset); ---*/ // Starten mit einer 256-Byte aligned Adresse. // Begruendung: // Das Squashfs wird 256-Byte aligned. Der Kernel steht davor. Die Startadresse der MTD-RAM-Partition ist also nicht aligned. // Der Suchalgorythmus kann also nicht im schlimmsten Fall das Squashfs-Magic nicht finden. // pos wird als auf die ersten 256 Byte NACH dem Kernel-Start positioniert. if(maptype == MAP_RAM) { if((ifx_ram_resource[0].start & ((1 << 8) - 1))) { pos = ((ifx_ram_resource[0].start & ~((1 << 8) - 1)) + 256) - ifx_ram_resource[0].start; /*--- printk(KERN_ERR "[%s:%d] Use offset of 0x%x to search squashfs magic.\n", __func__, __LINE__, (unsigned int)pos); ---*/ } } while(pos < ((*p_mtd_pat)[1].offset + (*p_mtd_pat)[count].size)) { int ret; /*--- pr_debug("[%s] mtd[%s]: searching for magic at mtd%d position 0x%llx", ---*/ /*--- __func__, mtd->name, mtd->index, pos); ---*/ ret = mtd_read(mtd, pos, sizeof(unsigned int), &readlen, (u_char*)&magic); if ((ret < 0) || (readlen < sizeof(unsigned int))) { printk(KERN_ERR "[%s:%d] breaking due to incomplete or erronous mtd_read\n", __func__, __LINE__); return 0; } /*--- pr_debug("[%s] mtd[%s]: mtd_read: read %u bytes, magic = 0x%08x, " ---*/ /*--- "index = %u, pos = 0x%x, return value = %d\n", ---*/ /*--- __func__, mtd->name, readlen, magic, mtd->index, ---*/ /*--- (unsigned int)pos, ret); ---*/ if(magic == SQUASHFS_MAGIC) { /*-------------------------------------------------------------------------------------*\ * * +---+---------------------+-----------------------+--------------------+ * | | Kernel | SquashFS | JFFS2 | * +---+---------------------+-----------------------+--------------------+ * A ^_pos E * * Zu Beginn ist das Layout obiges: * start_offset = A * MTD1 mit Kernel reicht von A bis E * MTD5 fuer JFFS2 kann gesetzt sein, wenn JFFS2 Parser vorher schon was gefunden hat * * Wenn SquashFS gefunden wird, wird MTD1 auf den Kernel verkleinert, * MTD0 fuer das FS wird von pos bis E angelegt * Wenn noch kein MTD5 mit JFFS2 existiert wird dieses innerhalb von MTD0 angelegt * \*-------------------------------------------------------------------------------------*/ (*p_mtd_pat)[0].offset = pos; (*p_mtd_pat)[0].size = start_offset + (*p_mtd_pat)[1].size - pos; (*p_mtd_pat)[1].size = pos - start_offset; /*--- pr_debug("[%s:%d] pos: 0x%x | offs[0]: 0x%llx | size[0]: %llu " ---*/ /*--- "| offs[1]: 0x%llx | size[1]: %llu\n", ---*/ /*--- __func__, __LINE__, ---*/ /*--- (unsigned int)pos, ---*/ /*--- (*p_mtd_pat)[0].offset, ---*/ /*--- (*p_mtd_pat)[0].size, ---*/ /*--- (*p_mtd_pat)[1].offset, ---*/ /*--- (*p_mtd_pat)[1].size); ---*/ // Die RAM-Partitions sollen nicht umbenannt werden! if(maptype != MAP_RAM) { (*p_mtd_pat)[0].name = "rootfs"; (*p_mtd_pat)[1].name = "kernel"; } pr_debug("[%s] magic found @pos 0x%llx\n", __func__, pos); if ((maptype == MAP_NOR_FLASH) && (memcmp(ifx_nor_partitions[5].name, "jffs2", sizeof("jffs2") - 1) != 0)) { /* JFFS2 nicht gefunden: Wenn jffs2_size gesetzt ist, ggf. verkleinern */ /* sonst anlegen mit der verbleibenden Flash Groesse nach Filesystem % 64k */ loff_t jffs2_size, jffs2_start, jffs2_earliest_start, urlader_jff2_size; struct squashfs_super_block squashfs_sb; unsigned int jffs_size_changed = 0; ret = mtd_read(mtd, pos, sizeof(struct squashfs_super_block), &readlen, (u_char*)&squashfs_sb); if ((ret < 0) || (readlen < sizeof(struct squashfs_super_block))) { printk(KERN_ERR "[%s:%d] breaking due to incomplete or erronous mtd_read\n", __func__, __LINE__); return 0; } jffs2_earliest_start = pos + ALIGN(squashfs_sb.bytes_used, SQUASHFS_DEVBLK_SIZE); /*--- printk("squashfs pos: %llx\n", pos); ---*/ /*--- printk("squashfs size: %llx\n", squashfs_sb.bytes_used); ---*/ /*--- printk("jffs2_start (squashfs pos + len) = %llx\n", jffs2_earliest_start); ---*/ if (jffs2_earliest_start & (mtd->erasesize-1)) { /*--- printk("align jffs: start: %llx\n", jffs2_earliest_start); ---*/ jffs2_earliest_start = (jffs2_earliest_start & ~(mtd->erasesize-1)) + mtd->erasesize; } /*--- printk("jffs2_earliest_start (aligned) = %llx\n", jffs2_earliest_start); ---*/ jffs2_size = ((*p_mtd_pat)[0].offset + (*p_mtd_pat)[0].size - jffs2_earliest_start) >> 16; /* jffs2_size in 64k Bloecken. Muss ggf. um 1 veringert werden fuer 128k Block Flash */ /*--- printk("jffs2_size = %llx\n", jffs2_size); ---*/ jffs2_size = jffs2_size & ~((mtd->erasesize / 0x10000)-1); /*--- printk("jffs2_size = %llx\n", jffs2_size); ---*/ urlader_jff2_size = 0; p = prom_getenv("jffs2_size"); if (p) { urlader_jff2_size = simple_strtoul(p, NULL, 10); } else { jffs_size_changed = 1; printk(KERN_ERR "[%s] jffs2_size not set\n", __func__); } if (jffs2_size < (IFX_MTD_JFFS2_MIN_SIZE * (mtd->erasesize/0x10000))) { printk(KERN_WARNING "[%s]: not enough space for JFFS2!\n", __func__); } else { if (urlader_jff2_size == 0) { /* Fuer 7320 ohne JFFS_SIZE im Urlader-Env. die Groesse * auf 16 begrenzen und nach hinten schieben, damit nicht bei jedem FW Update das * JFFS ueberschrieben wird */ if (jffs2_size > IFX_MTD_JFFS2_MAX_SIZE) { printk(KERN_WARNING "[%s]: limiting jffs2_size to %d\n", __func__, IFX_MTD_JFFS2_MAX_SIZE); jffs2_start = jffs2_earliest_start + (jffs2_size - IFX_MTD_JFFS2_MAX_SIZE) * 0x10000; jffs2_size = IFX_MTD_JFFS2_MAX_SIZE; jffs_size_changed = 1; } else { jffs2_start = jffs2_earliest_start; } } else { /* jffs2_size aus dem Urlader verwenden */ if (jffs2_size > urlader_jff2_size) { if (urlader_jff2_size < (IFX_MTD_JFFS2_MIN_SIZE * (mtd->erasesize/0x10000))) { urlader_jff2_size = (IFX_MTD_JFFS2_MIN_SIZE * (mtd->erasesize/0x10000)); printk(KERN_WARNING "[%s]: jffs2_size too small, use %lld\n", __func__, urlader_jff2_size); jffs_size_changed = 1; } jffs2_start = jffs2_earliest_start + (jffs2_size - urlader_jff2_size) * 0x10000; jffs2_size = urlader_jff2_size; } else { if(jffs2_size < urlader_jff2_size) { jffs_size_changed = 1; } jffs2_start = jffs2_earliest_start; } } (*p_mtd_pat)[5].offset = jffs2_start; (*p_mtd_pat)[5].name = "jffs2"; (*p_mtd_pat)[5].size = jffs2_size * 0x10000; (*p_mtd_pat)[0].size -= jffs2_size * 0x10000; /*--- File System partition verkleinern ---*/ printk(KERN_ERR "[%s] jffs2_start@%llx size: %lld\n", __func__, jffs2_start, jffs2_size); if(jffs_size_changed) { struct erase_info instr; int ret; printk(KERN_ERR "[%s] JFFS2 size changed, erase old filesystem\n", __func__); memset(&instr, 0, sizeof(instr)); instr.mtd = mtd; instr.addr = jffs2_start; instr.len = jffs2_size * 0x10000; instr.callback = NULL; instr.fail_addr = 0xffffffff; ret = mtd_erase(mtd, &instr); if (ret) { printk(KERN_ERR "jffs mtd erase failed %d\n", ret); } } } } return maxcount; } pos += 256; } } return maxcount; } #define JFFS_NODES ( JFFS2_NODETYPE_DIRENT | JFFS2_NODETYPE_INODE | JFFS2_NODETYPE_CLEANMARKER | JFFS2_NODETYPE_PADDING | JFFS2_NODETYPE_SUMMARY | JFFS2_NODETYPE_XATTR | JFFS2_NODETYPE_XREF) /*------------------------------------------------------------------------------------------*\ * nur im NOR-Flash suchen \*------------------------------------------------------------------------------------------*/ static int ifx_jffs2_parser_function(struct mtd_info *mtd, struct mtd_partition **p_mtd_pat, struct mtd_part_parser_data *pdata) { int ret; unsigned int magic = 0, readlen = 0; loff_t pos; /*--- printk(KERN_ERR "[ifx_jffs2_parser_function] mtd_info->name %s mtd_info->index %u " ---*/ /*--- "mtd_part_parser_data=0x%p p_mtd_pat=0x%p\n", ---*/ /*--- mtd->name, mtd->index, pdata, p_mtd_pat); ---*/ if (strcmp(mtd->name, "ltq-nor")) { printk(KERN_WARNING "[%s] exit on mtd %s\n", __func__, mtd->name); return 0; } if(p_mtd_pat) { *p_mtd_pat = ifx_nor_partitions; /*--- printk(KERN_ERR "[%s] try partition %s (offset 0x%llx len %llu)\n", ---*/ /*--- __func__, (*p_mtd_pat)[1].name, (*p_mtd_pat)[1].offset, (*p_mtd_pat)[1].size); ---*/ pos = (*p_mtd_pat)[1].offset; while(pos < (*p_mtd_pat)[1].offset + (*p_mtd_pat)[1].size) { ret = mtd_read(mtd, (loff_t)pos, sizeof(unsigned int), &readlen, (u_char*)&magic); if ((ret < 0) || (readlen < sizeof(unsigned int))) { printk(KERN_ERR "[%s] breaking due to incomplete or erronous mtd_read\n", __func__); return 0; } #ifdef __LITTLE_ENDIAN if ((((magic >> 16) & ~JFFS_NODES) == 0) && ((magic & 0xFFFF) == JFFS2_MAGIC_BITMASK)) { #else if (((magic >> 16) == JFFS2_MAGIC_BITMASK) && (((magic & 0xFFFF) & ~JFFS_NODES) == 0)) { #endif (*p_mtd_pat)[5].size = (*p_mtd_pat)[1].offset + (*p_mtd_pat)[1].size - pos; (*p_mtd_pat)[5].offset = pos; (*p_mtd_pat)[5].name = "jffs2"; printk(KERN_ERR "[%s] magic %04x found @pos 0x%llx, size %llu\n", __func__, magic, pos, (*p_mtd_pat)[5].size); return 0; } pos += mtd->erasesize; } } return 0; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ struct mtd_part_parser ifx_ext2fs_parser = { .name = "find_ext2fs", .parse_fn = ifx_ext2fs_parser_function, }; /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ struct mtd_part_parser ifx_squashfs_parser = { .name = "find_squashfs", .parse_fn = ifx_squashfs_parser_function, }; /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ struct mtd_part_parser ifx_jffs2_parser = { .name = "find_jffs2", .parse_fn = ifx_jffs2_parser_function, }; /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ struct platform_device *ifx_platform_devices[20]; unsigned int ifx_platform_devices_count = 0; void add_to_platform_device_list(struct platform_device *device, char *name __attribute__ ((unused))) { pr_debug("[%s] %s: add %s to the platform device list (to be registered)\n", __func__, name, name); ifx_platform_devices[ifx_platform_devices_count++] = device; } /*-------------------------------------------------------------------------------------*\ \*-------------------------------------------------------------------------------------*/ void ifx_init_platform_devices(void) { mtdnand_setup_parts(); pr_debug("[%s] registering %d platform device(s)\n", __func__, ifx_platform_devices_count); platform_add_devices(ifx_platform_devices, ifx_platform_devices_count); } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ void ifx_ram_mtd_set_rw(struct device *pdev, int mode) { if(mode == PLATRAM_RO) { dev_dbg(pdev, "PLATRAM_RO"); } else if(mode == PLATRAM_RW) { dev_dbg(pdev, "PLATRAM_RW"); } } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ unsigned int get_erase_block_size_on_ram_device(struct mtd_info *mtd) { unsigned int readlen = 0; loff_t pos = 0; unsigned int value1, value2; mtd_read(mtd, pos, sizeof(unsigned int), &readlen, (u_char*)&value1); if(readlen != sizeof(unsigned int)) return 0; pos += 0x10000ULL; mtd_read(mtd, pos, sizeof(unsigned int), &readlen, (u_char*)&value2); if(readlen != sizeof(unsigned int)) return 0; if(value1 == value2) { pos += 0x10000ULL; mtd_read(mtd, pos, sizeof(unsigned int), &readlen, (u_char*)&value2); if(readlen != sizeof(unsigned int)) return 0; if(value1 == value2) { pr_debug("[%s] eraseblocksize=0x10000\n", __func__); return 0x10000; } return 0; } pos += 0x10000ULL; mtd_read(mtd, pos, sizeof(unsigned int), &readlen, (u_char*)&value2); if(readlen != sizeof(unsigned int)) return 0; pr_debug("[%s] name=%s pos=0x%Lx value2=0x%x\n", __func__, mtd->name, pos, value2); if(value1 == value2) { pr_debug("[%s] eraseblocksize=0x20000\n", __func__); return 0x20000; } return 0; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ #if defined(CONFIG_TFFS_DEV_LEGACY) extern int tffs_mtd[2]; extern int tffs_mtd_offset[2]; #endif static int found_rootfs_ram = 0; char *str_rootfs[] = { "rootfs_ram", "rootfs", "filesystem" }; struct mtd_info *urlader_mtd; void ifx_mtd_add_notifier(struct mtd_info *mtd) { int i; if(!mtd->name) { pr_debug("[%s] Leeres MTD uebergeben!", __func__); return; } pr_debug("[%s] issued for mtd %s\n", __func__, mtd->name); for (i = 0; i < sizeof(str_rootfs) / sizeof(char*) ; i++) { if (!strcmp(mtd->name, str_rootfs[i])) { static char root_device[64]; pr_debug("[%s] found %s", __func__, mtd->name); if (found_rootfs_ram) /*--- we found a rootfs in RAM and use only this ---*/ return; if (!strcmp(mtd->name, str_rootfs[0])) found_rootfs_ram = 1; /*--- signal that we found a rootfs in RAM ---*/ sprintf(root_device, "/dev/mtdblock%d", mtd->index); pr_debug("mtd[%s] is root device: %s\n", mtd->name, root_device); root_dev_setup(root_device); return; } } if(!strcmp(mtd->name, "urlader")) { pr_debug("[%s] mtd%d[%s] set urlader_mtd", __func__, mtd->index, mtd->name); urlader_mtd = mtd; #if defined(CONFIG_TFFS_DEV_LEGACY) } else if(!strcmp(mtd->name, "tffs (1)")) { tffs_mtd[0] = mtd->index; pr_debug("[%s] mtd%d[%s]: tffs (1)\n", __func__, mtd->index, mtd->name); } else if(!strcmp(mtd->name, "tffs (2)")) { tffs_mtd[1] = mtd->index; pr_debug("[%s] mtd%d[%s]: tffs (2)\n", __func__, mtd->index, mtd->name); #endif /*--- #if defined(CONFIG_TFFS_DEV_LEGACY) ---*/ #if defined(CONFIG_TFFS_DEV_MTDNAND) } else if(!strcmp(mtd->name, "nand-tffs")) { if (mtd->size > 0) { TFFS3_Register_NAND(mtd); pr_debug("tffs3 on Index %d\n", mtd->index); } #endif /*--- #if defined(CONFIG_TFFS) ---*/ } else { pr_debug("[%s] mtd%d[%s] skipping\n", __func__, mtd->index, mtd->name); } } void ifx_mtd_rm_notifier(struct mtd_info *mtd) { pr_debug("[%s] mtd%d[%s] ignore removal\n", __func__, mtd->index, mtd->name); } struct mtd_notifier ifx_mtd_notifier_ops = { add: ifx_mtd_add_notifier, remove: ifx_mtd_rm_notifier }; /*------------------------------------------------------------------------------------------*\ * disable AUTO precharge im Urlader (NOR-Flash) \*------------------------------------------------------------------------------------------*/ int __init ifx_fix_timing(void) { unsigned int buffer[2]; unsigned int readlen, writelen; if (urlader_mtd && ifx_spi_partitions[0].name && ifx_spi_partitions[0].size) { pr_notice("<%s>\n", __func__); mtd_read(urlader_mtd, 0x40, 2 * sizeof(unsigned int), &readlen, (unsigned char *)buffer); if (readlen == (2 * sizeof(unsigned int))) { if ((buffer[0] == 0xbf401000) && (buffer[1] == 0x10101)) { buffer[1] &= ~(1<<16); pr_err("[%s] try to fix 0x%x = 0x%x\n", __func__, buffer[0], buffer[1]); mtd_write(urlader_mtd, 0x44, sizeof(unsigned int), &writelen, (unsigned char *)&buffer[1]); if (writelen != sizeof(unsigned int)) { pr_err("[%s] failed to write\n", __func__); } } } else { pr_err("[%s] failed to read\n", __func__); } } return 0; } late_initcall(ifx_fix_timing); /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ int __init ifx_mtd_init(void) { ifx_init_platform_devices(); register_mtd_user(&ifx_mtd_notifier_ops); register_mtd_parser(&ifx_ext2fs_parser); register_mtd_parser(&ifx_squashfs_parser); register_mtd_parser(&ifx_jffs2_parser); return 0; } subsys_initcall(ifx_mtd_init); /*------------------------------------------------------------------------------------------*\ * Parst die erste Groesse in einem Groessenangaben String vom Urlader * Format der Groessenangaben: xxx_size={,KB,MB} \*------------------------------------------------------------------------------------------*/ unsigned long long parse_mtd_size(char *p) { unsigned long long size; pr_debug("{%s} '%s'\n", __func__, p); if((p[0] == '0') && (p[1] == 'x')) { size = simple_strtoul(p, NULL, 16); } else { size = simple_strtoul(p, NULL, 10); } p = strchr(p, 'B'); if(p) { /*--- Die Groesse enthält mindestens eine KB Angabe ---*/ size *= 1024; if(p[-1] == 'M') { size *= 1024; } } return size; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static int __init mtdram_setup(char *p) { if(!p) return 0; pr_debug("[%s] [mtdram1] %s\n", __func__, p); ifx_ram_resource[0].start = CPHYSADDR((unsigned int)simple_strtoul(p, NULL, 16)); ifx_ram_resource[0].flags = IORESOURCE_MEM; p = strchr(p, ','); if(p) { p++; ifx_ram_resource[0].end = CPHYSADDR((unsigned int)simple_strtoul(p, NULL, 16)); ifx_ram_resource[0].end -= 1; } else { ifx_ram_resource[0].start = 0; } pr_info("[%s] mtdram1 0x%08x-0x%08x\n", __func__, ifx_ram_resource[0].start, ifx_ram_resource[0].end ); ifx_ram_partitions[0].name = "rootfs_ram"; ifx_ram_partitions[0].offset = 0; ifx_ram_partitions[0].size = ifx_ram_resource[0].end - ifx_ram_resource[0].start + 1; ifx_ram_partitions[0].mask_flags = MTD_ROM; ifx_ram_partitions[1].name = "kernel_ram"; ifx_ram_partitions[1].offset = 0; ifx_ram_partitions[1].size = ifx_ram_resource[0].end - ifx_ram_resource[0].start + 1; ifx_ram_partitions[1].mask_flags = MTD_ROM; ifx_ram_data.nr_partitions = 2; add_to_platform_device_list(&ifx_ram_device, "mtd-ram"); return 0; } __setup("mtdram1=", mtdram_setup); /*------------------------------------------------------------------------------------------*\ * NAND Parameter parsen \*------------------------------------------------------------------------------------------*/ static int __init mtdnand_setup_parts(void) { unsigned long mtd_start, mtd_end; char *_start, *p; unsigned int i, config_size = 0, kernel_size = 0, filesystem_size = 0, tffs_size = 0, urlader_size = 0; unsigned long long flash_end; if(ifxmips_flashsize_nand == 0) { return 0; } flash_end = ifxmips_flashsize_nand; _start = prom_getenv("linux_fs_start"); if (!_start) _start = "0"; /*--- default setzen ---*/ if(_start[0] == '0') { ifx_nand_partitions[0].name = (char *)"kernel"; ifx_nand_partitions[1].name = (char *)"filesystem"; ifx_nand_partitions[2].name = (char *)"reserved-kernel"; ifx_nand_partitions[3].name = (char *)"reserved-filesystem"; } if(_start[0] == '1') { ifx_nand_partitions[0].name = (char *)"reserved-kernel"; ifx_nand_partitions[1].name = (char *)"reserved-filesystem"; ifx_nand_partitions[2].name = (char *)"kernel"; ifx_nand_partitions[3].name = (char *)"filesystem"; } ifx_nand_partitions[4].name = (char *)"config"; ifx_nand_partitions[5].name = (char *)"nand-filesystem"; /*--------------------------------------------------------------------------------------*\ * Groessen ermitteln \*--------------------------------------------------------------------------------------*/ if((use_spi == 0) && (use_nor == 0)){ p = prom_getenv("mtd2"); if(p) { pr_debug("mtd2 = %s\n", p); mtd_start = (unsigned int)simple_strtoul(p, NULL, 16); p = strchr(p, ','); if(p) { p++; mtd_end = (unsigned int)simple_strtoul(p, NULL, 16); urlader_size = mtd_end - mtd_start; } } ifx_nand_partitions[6].name = (char *)"urlader"; p = prom_getenv("mtd3"); if(p) { pr_debug("mtd3 = %s\n", p); mtd_start = (unsigned int)simple_strtoul(p, NULL, 16); p = strchr(p, ','); if(p) { p++; mtd_end = (unsigned int)simple_strtoul(p, NULL, 16); tffs_size = mtd_end - mtd_start; } } ifx_nand_partitions[7].name = (char *)"nand-tffs"; p = prom_getenv("mtd4"); if(p) { pr_debug("mtd4 = %s\n", p); mtd_start = (unsigned int)simple_strtoul(p, NULL, 16); p = strchr(p, ','); if(p) { p++; mtd_end = (unsigned int)simple_strtoul(p, NULL, 16); config_size = mtd_end - mtd_start; } } } else { p = prom_getenv("mtd5"); if(p) { pr_debug("mtd5 = %s\n", p); mtd_start = (unsigned int)simple_strtoul(p, NULL, 16); p = strchr(p, ','); if(p) { p++; mtd_end = (unsigned int)simple_strtoul(p, NULL, 16); config_size = mtd_end - mtd_start; } } ifx_nand_data.nr_parts = 6; /*--- urlader und nand-tffs sind nicht an Board ---*/ } #if 0 p = prom_getenv("mtd6"); if(p) { pr_debug("[%s] mtd6 = %s\n", __func__, p); mtd_start = (unsigned int)simple_strtoul(p, NULL, 16); p = strchr(p, ','); if(p) { p++; mtd_end = (unsigned int)simple_strtoul(p, NULL, 16); tffs_size = mtd_end - mtd_start; } } #endif p = prom_getenv("mtd1"); if(p) { pr_debug("[%s] mtd1 = %s\n", __func__, p); mtd_start = (unsigned int)simple_strtoul(p, NULL, 16); p = strchr(p, ','); if(p) { p++; mtd_end = (unsigned int)simple_strtoul(p, NULL, 16); kernel_size = mtd_end - mtd_start; } } p = prom_getenv("mtd0"); if(p) { pr_debug("[%s] mtd0 = %s\n", __func__, p); mtd_start = (unsigned int)simple_strtoul(p, NULL, 16); p = strchr(p, ','); if(p) { p++; mtd_end = (unsigned int)simple_strtoul(p, NULL, 16); filesystem_size = mtd_end - mtd_start; } } /*--------------------------------------------------------------------------------------*\ * pruefen ob die einzelnen Teile in das Flash passen \*--------------------------------------------------------------------------------------*/ if(filesystem_size * 2 + kernel_size * 2 + urlader_size + config_size + tffs_size > ifxmips_flashsize_nand) { panic("NAND device too small\n"); } /*--------------------------------------------------------------------------------------*\ * Groessen aufsetzen \*--------------------------------------------------------------------------------------*/ ifx_nand_partitions[6].size = urlader_size; ifx_nand_partitions[6].offset = 0; ifxmips_flashsize_nand -= urlader_size; ifx_nand_partitions[7].size = tffs_size; ifx_nand_partitions[7].offset = ifx_nand_partitions[6].offset + ifx_nand_partitions[6].size; ifxmips_flashsize_nand -= tffs_size; ifx_nand_partitions[0].size = kernel_size; ifx_nand_partitions[0].offset = ifx_nand_partitions[7].offset + ifx_nand_partitions[7].size; ifxmips_flashsize_nand -= kernel_size; ifx_nand_partitions[1].size = filesystem_size; ifx_nand_partitions[1].offset = ifx_nand_partitions[0].offset + ifx_nand_partitions[0].size; ifxmips_flashsize_nand -= filesystem_size; ifx_nand_partitions[2].size = kernel_size; ifx_nand_partitions[2].offset = ifx_nand_partitions[1].offset + ifx_nand_partitions[1].size; ifxmips_flashsize_nand -= kernel_size; ifx_nand_partitions[3].size = filesystem_size; ifx_nand_partitions[3].offset = ifx_nand_partitions[2].offset + ifx_nand_partitions[2].size; ifxmips_flashsize_nand -= filesystem_size; ifx_nand_partitions[4].size = config_size; ifx_nand_partitions[4].offset = ifx_nand_partitions[3].offset + ifx_nand_partitions[3].size; ifxmips_flashsize_nand -= config_size; ifx_nand_partitions[5].size = ifxmips_flashsize_nand; ifx_nand_partitions[5].offset = ifx_nand_partitions[4].offset + ifx_nand_partitions[4].size; /** * move empty partitions' offsets to end of flash. Otherwise the mtd layer * will assume size == 0 to mean "from start to end" */ for(i = 0; i < IFX_MTD_NAND_PARTS; ++i){ if(ifx_nand_partitions[i].size == 0){ ifx_nand_partitions[i].offset = flash_end; } } add_to_platform_device_list(&ifx_nand_device[0], "mtd-nand"); return 0; } static int __init mtdnand_setup(char *p) { ifxmips_flashsize_nand = parse_mtd_size(p); pr_debug("nand_size = 0x%llx\n" , ifxmips_flashsize_nand); printk(KERN_ERR "[NAND] nand_size = 0x%llx\n" , ifxmips_flashsize_nand); return 0; } __setup("nand_size=", mtdnand_setup); /*------------------------------------------------------------------------------------------*\ * NOR Parameter parsen \*------------------------------------------------------------------------------------------*/ static int __init mtdnor_setup(char *p) { unsigned long flashsize_nor; unsigned long mtd_start, mtd_end; unsigned long flashoffset_nor = 0; if(!p) return 0; flashsize_nor = parse_mtd_size(p); if (!flashsize_nor) return 0; ifx_nor_partitions[0].name = (char *)"filesystem"; ifx_nor_partitions[1].name = (char *)"kernel"; ifx_nor_partitions[2].name = (char *)"urlader"; ifx_nor_partitions[3].name = (char *)"tffs (1)"; ifx_nor_partitions[4].name = (char *)"tffs (2)"; ifx_nor_partitions[5].name = (char *)"reserved"; /* Nie mit "jffs2" initialisieren! */ /* Die mtds werden vom Userland nach jffs2 gegrept und ggf. beschrieben, daher das * mtd nie jffs2 nennen, solange die Position/Groesse noch nicht stimmt. */ pr_debug("[%s] nor_size = 0x%lx\n" , __func__, flashsize_nor); /*--------------------------------------------------------------------------------------*\ * Groessen ermitteln \*--------------------------------------------------------------------------------------*/ p = prom_getenv("mtd2"); if(p) { pr_debug("[%s] mtd2 = %s\n", __func__, p); mtd_start = (unsigned int)simple_strtoul(p, NULL, 16); p = strchr(p, ','); if(p) { p++; mtd_end = (unsigned int)simple_strtoul(p, NULL, 16); flashoffset_nor = mtd_start; ifx_nor_partitions[2].size = mtd_end - mtd_start; ifx_nor_partitions[2].offset = mtd_start - flashoffset_nor; ifx_nor_resource[0].end += flashsize_nor - 1; } } p = prom_getenv("mtd0"); if(p) { pr_debug("[%s] mtd0 = %s\n", __func__, p); mtd_start = (unsigned int)simple_strtoul(p, NULL, 16); p = strchr(p, ','); if(p) { p++; mtd_end = (unsigned int)simple_strtoul(p, NULL, 16); ifx_nor_partitions[0].size = mtd_end - mtd_start; ifx_nor_partitions[0].offset = mtd_start - flashoffset_nor; } } p = prom_getenv("mtd1"); if(p) { pr_debug("[%s] mtd1 = %s\n", __func__, p); mtd_start = (unsigned int)simple_strtoul(p, NULL, 16); p = strchr(p, ','); if(p) { p++; mtd_end = (unsigned int)simple_strtoul(p, NULL, 16); ifx_nor_partitions[1].size = mtd_end - mtd_start; ifx_nor_partitions[1].offset = mtd_start - flashoffset_nor; } } p = prom_getenv("mtd3"); if(p) { pr_debug("[%s] mtd3 = %s\n", __func__, p); mtd_start = (unsigned int)simple_strtoul(p, NULL, 16); p = strchr(p, ','); if(p) { p++; mtd_end = (unsigned int)simple_strtoul(p, NULL, 16); ifx_nor_partitions[3].size = mtd_end - mtd_start; ifx_nor_partitions[3].offset = mtd_start - flashoffset_nor; } } p = prom_getenv("mtd4"); if(p) { pr_debug("[%s] mtd4 = %s\n", __func__, p); mtd_start = (unsigned int)simple_strtoul(p, NULL, 16); p = strchr(p, ','); if(p) { p++; mtd_end = (unsigned int)simple_strtoul(p, NULL, 16); ifx_nor_partitions[4].size = mtd_end - mtd_start; ifx_nor_partitions[4].offset = mtd_start - flashoffset_nor; } } p = prom_getenv("mtd5"); if(p) { pr_debug("[%s] mtd5 = %s\n", __func__, p); mtd_start = (unsigned int)simple_strtoul(p, NULL, 16); p = strchr(p, ','); if(p) { p++; mtd_end = (unsigned int)simple_strtoul(p, NULL, 16); ifx_nor_partitions[5].size = mtd_end - mtd_start; ifx_nor_partitions[5].offset = mtd_start - flashoffset_nor; } } add_to_platform_device_list(&ifx_nor_device[0], "ltq-nor"); use_nor = 1; return 0; } __setup("nor_size=", mtdnor_setup); /*------------------------------------------------------------------------------------------*\ * SPI Flash Parameter parsen \*------------------------------------------------------------------------------------------*/ static int __init mtdspi_setup(char *p) { unsigned long flashsize_spi, mtd_start, mtd_end; unsigned int i; if(!p) return 0; flashsize_spi = (unsigned long)parse_mtd_size(p); if(flashsize_spi == 0) return 0; pr_debug("[%s] sflash_size = 0x%lx\n", __func__, flashsize_spi); p = prom_getenv("mtd2"); if(p) { pr_debug("[%s] mtd2 = %s\n", __func__, p); mtd_start = (unsigned int)simple_strtoul(p, NULL, 16); p = strchr(p, ','); if(p) { p++; mtd_end = (unsigned int)simple_strtoul(p, NULL, 16); if(mtd_end && (mtd_end - mtd_start <= flashsize_spi)) { ifx_spi_partitions[0].name = (char*)"urlader"; ifx_spi_partitions[0].size = mtd_end - mtd_start; ifx_spi_partitions[0].offset = 0; ifx_spi_partitions[0].mask_flags = 0; ifx_spi_partitions[1].name = (char*)"tffs (1)"; ifx_spi_partitions[1].size = (flashsize_spi - ifx_spi_partitions[0].size) / 2; ifx_spi_partitions[1].offset = ifx_spi_partitions[0].size; ifx_spi_partitions[1].mask_flags = 0; ifx_spi_partitions[2].name = (char*)"tffs (2)"; ifx_spi_partitions[2].size = (flashsize_spi - ifx_spi_partitions[0].size) / 2; ifx_spi_partitions[2].offset = ifx_spi_partitions[0].size + ifx_spi_partitions[1].size; ifx_spi_partitions[2].mask_flags = 0; } } } for(i = 0 ; i < IFX_MTD_SPI_PARTS ; i++){ pr_debug("[%s] mtd%d: %20s: 0x%08llx - 0x%08llx (size 0x%llx)\n", __func__, i + IFX_MTD_NAND_PARTS, ifx_spi_partitions[i].name, ifx_spi_partitions[i].offset, ifx_spi_partitions[i].offset + ifx_spi_partitions[i].size, ifx_spi_partitions[i].size); } add_to_platform_device_list(&ifx_spi_device[0], "mtd-spi"); use_spi = 1; return 0; } __setup("sflash_size=", mtdspi_setup); #endif