/* * Copyright (C) 2006 Texas Instruments. * Copyright (C) 2007 AVM GmbH * * ---------------------------------------------------------------------------- * * 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 #include #include #include <../fs/squashfs/squashfs_fs.h> #include #include #include #include /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ /*--- #define ATH_MTD_DEBUG ---*/ #if defined(ATH_MTD_DEBUG) #define DEBUG_MTD(fmt, arg...) pr_err("[%d:%s/%d] " fmt "\n", smp_processor_id(), __func__, __LINE__, ##arg); #else #define DEBUG_MTD(fmt, arg...) #endif /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ #if defined(CONFIG_MTD_ATH_FLASH) struct _nmi_vector_location { unsigned int firmware_length; unsigned int vector_gap; char vector_id[32]; }; struct _nmi_vector_location *nmi_vector_location = (struct _nmi_vector_location *)0xbfc00040; extern void set_nmi_vetor_gap(unsigned int start, unsigned int firmware_size, unsigned int gap_size); #endif /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ #define MAX_FLASH_MTD 6 #define MAX_NAND_MTD 8 #define MAX_RAM_MTD 3 #define MAX_NAND_SPI_MTD (MAX_NAND_MTD - 2) /*--- URLADER und TFFS3 entfallen ---*/ #define JFFS2_MIN_SIZE 6 #define JFFS2_MAX_SIZE 16 static struct mtd_partition ath_partitions[MAX_FLASH_MTD]; /*-------------------------------------------------------------------------------------*\ * Zuerst wird das JFFS2 gesucht, dann das Squash-FS! \*-------------------------------------------------------------------------------------*/ static const char *probes[] = { "avm_jffs2", "avm_squashfs" , NULL }; static const char *ram_probes[] = { "avm_ext2fs", "avm_squashfs" , "avm_tarplugin", NULL }; static const char *nand_probes[] = { "avm_nand", NULL }; static unsigned int flash_erase_block_size = 64 << 10; extern int __init root_dev_setup(char *line); static struct ar7240_flash_data ath_flash_data = { .flash_size = 16, .parts = ath_partitions, .nr_parts = 0, .probes = probes }; /* 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 ath_flash_resource[1] = { { .start = 0x1f000000, .end = 0x1f000000 + (16 << 20) - 1, /* 16 MB */ .flags = IORESOURCE_MEM, }, }; static void ath_ram_mtd_set_rw(struct device *pdev, int); static struct platform_device ath_flash_device = { .name = "ath-nor", .id = 0, .dev = { .platform_data = &ath_flash_data, }, .num_resources = 1, .resource = &ath_flash_resource[0], }; #if defined(CONFIG_ATH79_DEV_TARPLUGIN) #define TAR_ALIGN_SHIFT 9 #define TAR_ALIGN (1 << TAR_ALIGN_SHIFT) #define TAR_MAGIC_VALUE "TAR_PLUGIN_AVM" #define TAR_MAGIC_VALUE_LEN (sizeof(TAR_MAGIC_VALUE) - 1) #define TAR_MAGIC_START_POS (TAR_ALIGN - TAR_MAGIC_VALUE_LEN) /** */ static size_t find_tarplugin(struct mtd_info *mtd, size_t startpos) { u_char buf[TAR_MAGIC_VALUE_LEN]; size_t pos, readlen; for (pos = startpos + TAR_MAGIC_START_POS; pos < mtd->size; pos += TAR_ALIGN) { if (mtd_read(mtd, pos, TAR_MAGIC_VALUE_LEN, &readlen, buf) == -EINVAL) { return mtd->size; } if ((readlen == TAR_MAGIC_VALUE_LEN) && (memcmp(buf, TAR_MAGIC_VALUE, TAR_MAGIC_VALUE_LEN) == 0)) { pos += TAR_MAGIC_VALUE_LEN; pr_err("[%s] found tar-plugin-avm at mtd offset 0x%x.\n", __func__, pos); break; } } return pos; } #endif /* #if defined(CONFIG_ATH79_DEV_TARPLUGIN) */ /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static unsigned int ath_use_mtdram; static struct mtd_partition ath_ram_partitions[MAX_RAM_MTD]; static struct resource ath_ram_resource[1] = { { /* für ins RAM geladenes Filesystem */ .start = 0, .end = 0 + (32 << 20), .flags = IORESOURCE_MEM, } }; static struct platdata_mtd_ram ath_ram_data = { .mapname = "ram-filesystem", .bankwidth = 4, .partitions = ath_ram_partitions, .nr_partitions = 0, // Dieser Wert muss 0 sein, denn sonst wirft er den Parser nicht an. .set_rw = ath_ram_mtd_set_rw, .probes = ram_probes }; static struct platform_device ath_ram_device = { .name = "mtd-ram", .id = -1, .dev = { .platform_data = &ath_ram_data, }, .num_resources = 1, .resource = &ath_ram_resource[0], }; /*------------------------------------------------------------------------------------------*\ * NAND \*------------------------------------------------------------------------------------------*/ static unsigned long long ath_nand_flashsize; static loff_t tffs3_offset; static struct resource ath_nand_resource[] = { { .start = ATH_NAND_FLASH_BASE, .end = ATH_NAND_FLASH_BASE + 0x300, /*--- .end = 0 + (128 << 20), ---*/ /* 128 MB */ .flags = IORESOURCE_MEM, //.parent = &nand_flash_resource }, }; static struct physmap_flash_data ath_nand_data = { .width = 2, .parts = NULL, .nr_parts = 0, .part_probe_types = nand_probes }; static struct platform_device ath_nand_device = { .name = "ath-avmnand", .id = -1, .dev = { .platform_data = &ath_nand_data, }, .num_resources = 1, .resource = &ath_nand_resource[0], }; /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static void ath_ram_mtd_set_rw(struct device *pdev, int mode) { if(mode == PLATRAM_RO) { DEBUG_MTD("PLATRAM_RO"); } else if(mode == PLATRAM_RW) { DEBUG_MTD("PLATRAM_RW"); } } enum _flash_map_enum { MAP_UNKNOWN, MAP_RAM, MAP_FLASH }; /** * * +---+---------------------+-----------------------+--------------------+ * | | Kernel | SquashFS | JFFS2 | * +---+---------------------+-----------------------+--------------------+ * A ^_pos E * * Zu Beginn ist das Layout obiges: * start_offset = A * MTD1 mit Kernel reicht von A bis E * MTD5 für JFFS2 kann gesetzt sein, wenn JFFS2 Parser vorher schon was gefunden hat * * Wenn SquashFS gefunden wird, wird MTD1 auf den Kernel verkleinert, * MTD0 für das FS wird von pos bis E angelegt * Wenn noch kein MTD5 mit JFFS2 existiert wird dieses innerhalb von MTD0 angelegt * */ static int __init ath_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; struct mtd_partition *parts; DEBUG_MTD("mtd_info->name %s mtd_info->index %u p_mtd_pat=0x%p", mtd->name, mtd->index, p_mtd_pat); if (!strcmp(mtd->name, "ram-filesystem")) { maptype = MAP_RAM; } else if (!strcmp(mtd->name, "ath-nor")) { maptype = MAP_FLASH; flash_erase_block_size = mtd->erasesize; } else { pr_warn("[%s] with unknown mtd type %s\n",__FUNCTION__, mtd->name); return 0; } if (p_mtd_pat) { unsigned int magic = 0, readlen = 0; loff_t pos, start_offset; if (*p_mtd_pat) DEBUG_MTD("*p_mtd_pat->name %s", (*p_mtd_pat)->name); switch (maptype) { case MAP_FLASH: *p_mtd_pat = ath_partitions; maxcount = ath_flash_data.nr_parts; break; case MAP_RAM: *p_mtd_pat = ath_ram_partitions; maxcount = 2; break; default: break; } DEBUG_MTD("try partition %s (offset 0x%lx len %lu blocksize=%x) read='%pF'", (*p_mtd_pat)[count].name, (unsigned long)((*p_mtd_pat)[count].offset), (unsigned long)((*p_mtd_pat)[count].size), mtd->erasesize, mtd->_read); // Allocate 1 part more to fit jffs2 if needed *p_mtd_pat = kmemdup(*p_mtd_pat, (maxcount + 1) * sizeof(struct mtd_partition), GFP_KERNEL); BUG_ON(!*p_mtd_pat); parts = *p_mtd_pat; start_offset = pos = parts[count].offset; DEBUG_MTD("mtd[%s]: start_offset := (*p_mtd_pat)[%d].offset = %lld", 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 ((ath_ram_resource[0].start & ((1 << 8) - 1))) { pos = ((ath_ram_resource[0].start & ~((1 << 8) - 1)) + 256) - ath_ram_resource[0].start; pr_err("[%s:%d] Use offset of 0x%x to search squashfs magic.\n", __func__, __LINE__, (unsigned int)pos); } } while (pos < (parts[1].offset + parts[count].size)) { int ret; ret = mtd->_read(mtd, (loff_t)pos, sizeof(unsigned int), &readlen, (u_char*)&magic); if ((ret < 0) || (readlen < sizeof(unsigned int))) { pr_err("[%s] breaking due to incomplete or erronous mtd_read\n", __func__); return 0; } if (magic == 0x73717368) { #if defined(CONFIG_JFFS2_FS) const char* hw_revision; #endif parts[0].offset = pos; parts[0].size = (u_int32_t)start_offset + (u_int32_t)parts[1].size - (u_int32_t)pos; if (maptype == MAP_RAM) { parts[0].name = "rootfs_ram"; parts[1].name = "kernel_ram"; } else { parts[0].name = "rootfs"; parts[1].name = "kernel"; } parts[1].size = (u_int32_t)pos - (u_int32_t)start_offset; DEBUG_MTD("magic found @pos 0x%x", (unsigned int)pos); #if defined(CONFIG_JFFS2_FS) hw_revision = prom_getenv("HWRevision"); if ((hw_revision != NULL) && (strcmp(hw_revision, "180") == 0) && (maptype == MAP_FLASH) && (memcmp(parts[5].name, "jffs2", 4) == 0)) { /* JFFS2 vorhanden, auf 6810 prüfen ob verfügbarer Platz > 6 Blöcke und */ /* JFFS2 6 Blöcke groß. Dann haben wir eine Box mit fehlerhaft zu kleinem JFFS2. */ /* Dann neu anlegen mit min(JFFS2_MAX_SIZE, verfügb. Platz) */ u_int32_t jffs2_size, jffs2_earliest_start; struct squashfs_super_block squashfs_sb; mtd->_read(mtd, (loff_t)pos, sizeof(struct squashfs_super_block), &readlen, (u_char*)&squashfs_sb); jffs2_earliest_start = (u_int32_t)pos + (u_int32_t)squashfs_sb.bytes_used; /*--- pr_err("squashfs pos: %x\n", (u_int32_t)pos); ---*/ /*--- pr_err("squashfs size: %x\n", (u_int32_t)squashfs_sb.bytes_used); ---*/ /*--- pr_err("jffs2_start (squashfs pos + len) = %x\n", (u_int32_t)jffs2_earliest_start); ---*/ if (jffs2_earliest_start & (mtd->erasesize-1)) { /*--- pr_err("align jffs: start: %x\n", jffs2_earliest_start); ---*/ jffs2_earliest_start = (jffs2_earliest_start & ~(mtd->erasesize-1)) + mtd->erasesize; } /*--- pr_err("jffs2_earliest_start (aligned) = %x\n", jffs2_earliest_start); ---*/ jffs2_size = ((*p_mtd_pat)[0].offset + (*p_mtd_pat)[0].size - jffs2_earliest_start) >> 16; /* jffs2_size in 64k Blöcken. Muss ggf. um 1 veringert werden für 128k Block Flash */ /*--- pr_err("jffs2_size = %x\n", jffs2_size); ---*/ jffs2_size = jffs2_size & ~((mtd->erasesize / 0x10000)-1); /*--- pr_err("jffs2_size = %x\n", jffs2_size); ---*/ if (jffs2_size > JFFS2_MAX_SIZE) jffs2_size = JFFS2_MAX_SIZE; if (jffs2_size >= (JFFS2_MIN_SIZE * (mtd->erasesize/0x10000))) { u_int32_t current_jffs2_size = ath_partitions[5].size >> 16; if ((current_jffs2_size == 6) && (jffs2_size > 6)) { DEBUG_MTD("resize jffs2"); ath_partitions[5].name = (char *)"reserved"; } } } if ((maptype == MAP_FLASH) && (parts[5].name == NULL || memcmp(parts[5].name, "jffs2", 4) != 0)) { /* JFFS2 nicht gefunden: Wenn jffs2_size gesetzt ist, ggf. verkleinern */ /* sonst anlegen mit der verbleibenden Flash Grösse nach Filesystem % 64k */ u_int32_t jffs2_size, jffs2_start, jffs2_earliest_start; struct squashfs_super_block squashfs_sb; mtd->_read(mtd, (loff_t)pos, sizeof(struct squashfs_super_block), &readlen, (u_char*)&squashfs_sb); jffs2_earliest_start = (u_int32_t)pos + (u_int32_t)squashfs_sb.bytes_used; /*--- pr_err("squashfs pos: %x\n", (u_int32_t)pos); ---*/ /*--- pr_err("squashfs size: %x\n", (u_int32_t)squashfs_sb.bytes_used); ---*/ /*--- pr_err("jffs2_start (squashfs pos + len) = %x\n", (u_int32_t)jffs2_earliest_start); ---*/ if (jffs2_earliest_start & (mtd->erasesize-1)) { /*--- pr_err("align jffs: start: %x\n", jffs2_earliest_start); ---*/ jffs2_earliest_start = (jffs2_earliest_start & ~(mtd->erasesize-1)) + mtd->erasesize; } /*--- pr_err("jffs2_earliest_start (aligned) = %x\n", jffs2_earliest_start); ---*/ jffs2_size = ((*p_mtd_pat)[0].offset + (*p_mtd_pat)[0].size - jffs2_earliest_start) >> 16; /* jffs2_size in 64k Blöcken. Muss ggf. um 1 veringert werden für 128k Block Flash */ /*--- pr_err("jffs2_size = %x\n", jffs2_size); ---*/ jffs2_size = jffs2_size & ~((mtd->erasesize / 0x10000)-1); /*--- pr_err("jffs2_size = %x\n", jffs2_size); ---*/ if (jffs2_size < (JFFS2_MIN_SIZE * (mtd->erasesize/0x10000))) { pr_warn("[%s]: not enough space for JFFS2!\n",__FUNCTION__); } else { /*--- Größe auf JFFS2_MAX_SIZE begrenzen ---*/ if (jffs2_size > JFFS2_MAX_SIZE) { jffs2_start = jffs2_earliest_start + (jffs2_size - JFFS2_MAX_SIZE) * 0x10000; jffs2_size = JFFS2_MAX_SIZE; } else { jffs2_start = jffs2_earliest_start; } maxcount += 1; /*--- eine Partition mehr ! ---*/ parts[5].offset = jffs2_start; parts[5].size = jffs2_size * 0x10000; parts[5].name = "jffs2"; DEBUG_MTD("jffs2_start@%x size: %d", jffs2_start, jffs2_size); { struct erase_info instr; int ret; pr_err("erasing jffs2\n"); 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) { pr_err("jffs mtd erase failed %d\n", ret); } } } } #endif #if defined(CONFIG_ATH79_DEV_TARPLUGIN) if (maptype == MAP_RAM) { size_t offset = find_tarplugin(mtd, pos); if (offset < mtd->size) { maxcount++; /*--- we found tarplugin ---*/ parts[2].name = "tarplugin_ram"; parts[2].offset = offset; parts[2].size = mtd->size - offset; /*--- shrink filesystem ---*/ parts[0].size -= parts[2].size; pr_err("[%s] tar-plugin found at offset 0x%llx size=0x%llx - shrink fs to 0x%llx\n", __func__, parts[2].offset, parts[2].size, parts[0].size); } } #endif /* #if defined(CONFIG_ATH79_DEV_TARPLUGIN) */ return maxcount; } pos += 256; } } return maxcount; } static int __init ath_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; DEBUG_MTD("mtd_info->name %s mtd_info->index %u mtd_part_parser_data=0x%p p_mtd_pat=0x%p", mtd->name, mtd->index, parse_data, p_mtd_pat); if (strcmp(mtd->name, "ram-filesystem")) { /*--- nur im ram-fs suchen ---*/ pr_err("[%s] exit on mtd %s\n", __func__, mtd->name); return 0; } if(p_mtd_pat) { *p_mtd_pat = kmemdup(ath_ram_partitions, ARRAY_SIZE(ath_ram_partitions) * sizeof(struct mtd_partition), GFP_KERNEL); BUG_ON(!*p_mtd_pat); start_offset = pos = (*p_mtd_pat)[1].offset; DEBUG_MTD("mtd[%s]: start_offset := (*p_mtd_pat)[1].offset = %lld", 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((ath_ram_resource[0].start & ((1 << 8) - 1))) { pos = ((ath_ram_resource[0].start & ~((1 << 8) - 1)) + 256) - ath_ram_resource[0].start; DEBUG_MTD("Use offset of 0x%llx to search ext2 magic.", 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))) { pr_err("[%s] breaking due to incomplete or erronous mtd_read\n", __func__); return 0; } if(magic == SQUASHFS_MAGIC) { unsigned int n_part = 2; size_t offset __maybe_unused = pos; pos += 0x100; add_offset = EXT2_SB_MAGIC_OFFSET + 0x400; DEBUG_MTD("first match at 0x%llx add_offset 0x%llx", pos, add_offset); ret = mtd_read(mtd, pos + add_offset, sizeof(unsigned int), &readlen, (u_char*)&magic); if ((ret < 0) || (readlen < sizeof(unsigned int))) { pr_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 | TAR-Plugin | * +---+---------------------+-----------------------+--------------+ * A ^_pos E * * Zu Beginn ist das Layout obiges: * start_offset = A */ (*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); ---*/ #if defined(CONFIG_ATH79_DEV_TARPLUGIN) offset = find_tarplugin(mtd, offset); if (offset < mtd->size) { n_part++; /*--- we found tarplugin ---*/ (*p_mtd_pat)[2].name = "tarplugin_ram"; (*p_mtd_pat)[2].offset = offset; (*p_mtd_pat)[2].size = mtd->size - offset; /*--- shrink filesystem ---*/ (*p_mtd_pat)[0].size -= (*p_mtd_pat)[2].size; pr_err("[%s] tar-plugin found at offset 0x%llx size=0x%llx - shrink fs to 0x%llx\n", __func__, (*p_mtd_pat)[2].offset, (*p_mtd_pat)[2].size, (*p_mtd_pat)[0].size); } #endif /* #if defined(CONFIG_ATH79_DEV_TARPLUGIN) */ DEBUG_MTD("found partition @pos 0x%x", (unsigned int)pos); return n_part; } } pos += 256; } } return 0; } #if defined(CONFIG_JFFS2_FS) #define JFFS_NODES ( JFFS2_NODETYPE_DIRENT | JFFS2_NODETYPE_INODE | JFFS2_NODETYPE_CLEANMARKER | JFFS2_NODETYPE_PADDING | JFFS2_NODETYPE_SUMMARY | JFFS2_NODETYPE_XATTR | JFFS2_NODETYPE_XREF) /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static int __init ath_jffs2_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; DEBUG_MTD("mtd_info->name %s mtd_info->index %u p_mtd_pat=0x%p", mtd->name, mtd->index, p_mtd_pat); if (!strcmp(mtd->name, "ram-filesystem")) { maptype = MAP_RAM; } else if (!strcmp(mtd->name, "ath-nor")) { maptype = MAP_FLASH; } else { pr_warn("[%s] with unknown mtd type %s\n",__FUNCTION__, mtd->name); return 0; } if(p_mtd_pat) { unsigned int magic = 0, readlen = 0; loff_t pos; if(*p_mtd_pat) DEBUG_MTD("*p_mtd_pat->name %s", (*p_mtd_pat)->name); switch (maptype) { case MAP_FLASH: if(*p_mtd_pat == NULL) { *p_mtd_pat = ath_partitions; } break; case MAP_RAM: count = 2; if(*p_mtd_pat == NULL) { *p_mtd_pat = ath_ram_partitions; } /*--- return 0; ---*/ /* nicht im RAM suchen */ break; default: break; } DEBUG_MTD("try partition %s (offset 0x%lx len %lu)", (*p_mtd_pat)[count].name, (unsigned long)((*p_mtd_pat)[count].offset), (unsigned long)((*p_mtd_pat)[count].size)); pos = (*p_mtd_pat)[count].offset; while(pos < (*p_mtd_pat)[count].offset + (*p_mtd_pat)[count].size) { mtd->_read(mtd, (loff_t)pos, sizeof(unsigned int), &readlen, (u_char*)&magic); /*--- pr_err("[%s] read %u bytes, magic = 0x%08x index %u pos 0x%x\n",__FUNCTION__, readlen, magic, mtd->index, pos); ---*/ #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 switch (maptype) { case MAP_FLASH: ath_flash_data.nr_parts += 1; /*--- eine Partition mehr ! ---*/ (*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"; DEBUG_MTD("magic %04x found @pos 0x%x, size %ld", magic, (unsigned int)pos, (unsigned long)((*p_mtd_pat)[5].size)); break; case MAP_RAM: (*p_mtd_pat)[2].size = (*p_mtd_pat)[count].offset + (*p_mtd_pat)[count].size - pos; (*p_mtd_pat)[2].offset = pos; (*p_mtd_pat)[2].name = "ram-jffs2"; DEBUG_MTD("magic %04x found @pos 0x%x, size %ld", magic, (unsigned int)pos, (unsigned long)((*p_mtd_pat)[2].size)); break; default: break; } return 0; } pos += mtd->erasesize; } } return 0; } #endif /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static int __init ath_nand_parser_function(struct mtd_info *mtd, struct mtd_partition **p_mtd_pat, struct mtd_part_parser_data *parse_data) { unsigned long mtd_start, mtd_end; unsigned int config_size = 0, kernel_size = 0, filesystem_size = 0, tffs_size = 0, urlader_size = 0; char *p, *fs_start = prom_getenv("linux_fs_start"); DEBUG_MTD("[%s] mtd_info->name %s mtd_info->index %u p_mtd_pat=0x%p", __func__, mtd->name, mtd->index, p_mtd_pat); if ( ! strcmp(mtd->name, "ath-avmnand")) { struct mtd_partition *parts = kzalloc(MAX_NAND_MTD * sizeof(struct mtd_partition), GFP_KERNEL); *p_mtd_pat = parts; BUG_ON(!parts); if ( ! fs_start) fs_start = "0"; if(fs_start[0] == '1') { parts[0].name = (char *)"reserved-kernel"; parts[1].name = (char *)"reserved-filesystem"; parts[2].name = (char *)"kernel"; parts[3].name = (char *)"filesystem"; } else { parts[0].name = (char *)"kernel"; parts[1].name = (char *)"filesystem"; parts[2].name = (char *)"reserved-kernel"; parts[3].name = (char *)"reserved-filesystem"; } parts[4].name = (char *)"config"; parts[5].name = (char *)"nand-filesystem"; /*--------------------------------------------------------------------------------------*\ * Größen ermitteln \*--------------------------------------------------------------------------------------*/ p = prom_getenv("mtd0"); if(p) { DEBUG_MTD("mtd0 = %s", 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; parts[1].size = filesystem_size; parts[3].size = filesystem_size; } } p = prom_getenv("mtd1"); if(p) { DEBUG_MTD("mtd1 = %s", 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; parts[0].size = kernel_size; parts[2].size = kernel_size; } } if (ath_nand_data.nr_parts > MAX_NAND_SPI_MTD) { /*--- kein SPI-Flash vorhanden ---*/ p = prom_getenv("mtd2"); if(p) { DEBUG_MTD("mtd2 = %s", p); simple_strtoul(p, NULL, 16); p = strchr(p, ','); if(p) { p++; urlader_size = (unsigned int)simple_strtoul(p, NULL, 16); parts[6].name = (char *)"urlader"; parts[6].size = urlader_size; parts[6].offset = 0; } } p = prom_getenv("mtd3"); if(p) { DEBUG_MTD("mtd3 = %s", 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; parts[7].name = (char *)"nand-tffs"; parts[7].size = tffs_size; parts[7].offset = parts[6].size; tffs3_offset = parts[7].offset; } } p = prom_getenv("mtd4"); if(p) { DEBUG_MTD("mtd4 = %s", 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 { /*--- es gibt einen SPI-Flash, der Config-Bereich ist mtd5 ---*/ p = prom_getenv("mtd5"); if(p) { DEBUG_MTD("mtd5 = %s", 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; } } } /*--------------------------------------------------------------------------------------*\ * prüfen ob die einzelnen Teile in das Flash passen \*--------------------------------------------------------------------------------------*/ if(filesystem_size * 2 + kernel_size * 2 + urlader_size + config_size + tffs_size > ath_nand_flashsize) { panic("NAND device too small\n"); } /*--------------------------------------------------------------------------------------*\ * Groesen aufsetzen, wenn SPI-Flash vorhanden, ist parts[7] = 0 \*--------------------------------------------------------------------------------------*/ ath_nand_flashsize -= (urlader_size + tffs_size); parts[0].offset = parts[7].offset + parts[7].size; ath_nand_flashsize -= kernel_size; parts[1].offset = parts[0].offset + parts[0].size; ath_nand_flashsize -= filesystem_size; parts[2].offset = parts[1].offset + parts[1].size; ath_nand_flashsize -= kernel_size; parts[3].offset = parts[2].offset + parts[2].size; ath_nand_flashsize -= filesystem_size; parts[4].size = config_size; parts[4].offset = parts[3].offset + parts[3].size; ath_nand_flashsize -= config_size; parts[5].size = ath_nand_flashsize; /*--- der Rest ---*/ parts[5].offset = parts[4].offset + parts[4].size; return 5; } else { pr_warn("[%s] with unknown mtd type %s\n", __func__, mtd->name); } return 0; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static struct mtd_part_parser ath_squashfs_parser = { .name = "avm_squashfs", .parse_fn = ath_squashfs_parser_function }; /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ #if defined(CONFIG_JFFS2_FS) static struct mtd_part_parser ath_jffs2_parser = { .name = "avm_jffs2", .parse_fn = ath_jffs2_parser_function }; #endif static struct mtd_part_parser ath_ext2fs_parser = { .name = "avm_ext2fs", .parse_fn = ath_ext2fs_parser_function }; /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static struct mtd_part_parser ath_nand_parser = { .name = "avm_nand", .parse_fn = ath_nand_parser_function }; /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static void ath_mtd_add_notifier(struct mtd_info *mtd) { if(!mtd->name) { DEBUG_MTD("Leeres MTD uebergeben!"); return; } DEBUG_MTD("name %s" , mtd->name); #if defined(CONFIG_TFFS) if (!strcmp(mtd->name, "tffs (1)")) { tffs_mtd[0] = mtd->index; } else if(!strcmp(mtd->name, "tffs (2)")) { tffs_mtd[1] = mtd->index; #endif /*--- #if defined(CONFIG_TFFS) ---*/ #if defined(CONFIG_TFFS3) } else if(!strcmp(mtd->name, "nand-tffs")) { if (mtd->size > 0) { TFFS3_Register_NAND(mtd->index, tffs3_offset); DEBUG_MTD("tffs3 on Index %d, raw offset 0x%llx", mtd->index, tffs3_offset); } #endif /*--- #if defined(CONFIG_TFFS) ---*/ } else { DEBUG_MTD("skip %s" , mtd->name); } } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static void ath_mtd_rm_notifier(struct mtd_info *mtd) { DEBUG_MTD("[ath_mtd_rm_notifier] ignore %s\n", mtd->name); } static struct mtd_notifier ath_mtd_notifier = { add: ath_mtd_add_notifier, remove: ath_mtd_rm_notifier }; /*------------------------------------------------------------------------------------------*\ * Parst die erste Größe in einem Größenangaben String vom Urlader * Format der Größenangaben: xxx_size={,KB,MB} \*------------------------------------------------------------------------------------------*/ static unsigned long long __init parse_mtd_size(char *p) { unsigned long long size; 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 Größe enthält mindestens eine KB Angabe ---*/ if(p[-1] == 'K') { size *= 1024; } else if(p[-1] == 'M') { size *= (1024 * 1024); } } DEBUG_MTD("%Ld", size); return size; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ #if defined(CONFIG_MTD_ATH_FLASH) static void __init find_nmi_vector(void) { unsigned int len, i; if(strcmp(nmi_vector_location->vector_id, "NMI Boot Vector")) { pr_err("[%s] no nmi vector found\n", __FUNCTION__); return; } len = (nmi_vector_location->firmware_length + flash_erase_block_size) & ~(flash_erase_block_size - 1); pr_info("[%s] nmi vector found. Firmware length 0x%x bytes (erase block align 0x%x) vector gap size 0x%x bytes.\n", __FUNCTION__, nmi_vector_location->firmware_length, len, nmi_vector_location->vector_gap); for (i = 0; i < ARRAY_SIZE(ath_partitions); i++) { if (ath_partitions[i].name && strcmp(ath_partitions[i].name, "urlader") == 0) { len += ath_partitions[i].size; /*--- urlader size ---*/ pr_info("[%s] add '%s' size 0x%llx to length\n", __FUNCTION__, ath_partitions[i].name, ath_partitions[i].size); } } if (nmi_vector_location->vector_gap) { set_nmi_vetor_gap(0xbfc00000, len, nmi_vector_location->vector_gap); } } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static void __init find_nmi_vector_gap(unsigned int base, unsigned int end) { unsigned int len; struct _nmi_vector_location *first_loc = (struct _nmi_vector_location *)(base + 0x40); struct _nmi_vector_location *last_loc = (struct _nmi_vector_location *)(base + 0xbe0040); while((unsigned long)first_loc <= (unsigned long)last_loc) { if((unsigned int)first_loc >= end) break; /*--- if(((unsigned long)first_loc > 0x87d95a00UL) && ((unsigned long)first_loc < 0x87d96000UL)) ---*/ /*--- printk(KERN_ERR "[NMI] %p => %10pB\n", first_loc, first_loc); ---*/ if(!strcmp(first_loc->vector_id, "NMI Boot Vector")) { len = end - ((unsigned int)first_loc - 0x40) + first_loc->vector_gap; DEBUG_MTD("[%s] nmi vector found. Firmware length 0x%x bytes (move length 0x%x) vector gap size 0x%x bytes.", __FUNCTION__, first_loc->firmware_length, len, first_loc->vector_gap); memmove((void *)((unsigned int)first_loc - 0x40), (void *)((unsigned int)first_loc + first_loc->vector_gap - 0x40), len); return; } first_loc = (struct _nmi_vector_location *)((unsigned int)first_loc + 256); } pr_err("[%s] no nmi vector found at base %#x\n", __FUNCTION__, base); } #endif static struct platform_device *ath_platform_devices[12]; static unsigned int ath_platform_devices_count = 0; static void __init add_to_platform_device_list(struct platform_device *device, char *name __attribute__ ((unused))) { DEBUG_MTD("%s: add %s to the platform device list (to be registered)", name, device->name); ath_platform_devices[ath_platform_devices_count++] = device; } /*-------------------------------------------------------------------------------------*\ \*-------------------------------------------------------------------------------------*/ static void __init ath_init_platform_devices(void) { DEBUG_MTD("[%s] registering %d platform device(s)", __func__, ath_platform_devices_count); platform_add_devices(ath_platform_devices, ath_platform_devices_count); } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static int __init ath_mtd_init(void) { register_mtd_parser(&ath_nand_parser); register_mtd_parser(&ath_squashfs_parser); register_mtd_parser(&ath_ext2fs_parser); #if defined(CONFIG_JFFS2_FS) register_mtd_parser(&ath_jffs2_parser); #endif register_mtd_user(&ath_mtd_notifier); ath_init_platform_devices(); #if defined(CONFIG_MTD_ATH_FLASH) if ( ath_use_mtdram || ath_flash_data.nr_parts ) { find_nmi_vector(); } #endif return 0; } subsys_initcall(ath_mtd_init); static int __init parse_mtd_str(char *str, unsigned long *start, unsigned long *end) { *start = simple_strtoul(str, NULL, 16); str = strchr(str, ','); if (!str) { return -EINVAL; } str++; *end = simple_strtoul(str, NULL, 16); return 0; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static int __init mtdflash_setup(char *p) { static const char* names[] = { "filesystem", "kernel", "urlader", "tffs (1)", "tffs (2)", "reserved-kernel" }; unsigned long flashsize; unsigned long mtd_start, mtd_end; unsigned long flashoffset = 0; unsigned int mtd_index = 0; unsigned int i = 0; unsigned int n = ARRAY_SIZE(names); char *fs_start = prom_getenv("linux_fs_start"); if(!p) return 0; flashsize = parse_mtd_size(p); if (flashsize == 0) return 0; /*--------------------------------------------------------------------------------------*\ * Größen ermitteln \*--------------------------------------------------------------------------------------*/ p = prom_getenv("mtd2"); if(p) { DEBUG_MTD("mtd2 = %s", p); flashoffset = CPHYSADDR((unsigned int)simple_strtoul(p, NULL, 16)); } else { panic("no MTD2 found\n"); } ath_flash_resource[0].start = flashoffset; ath_flash_resource[0].end = flashoffset + flashsize; /* * Devices with less or equal 1MiB of spi flash will use nand to hold * the kernel and filesystem images. * * Therefore we need to ignore mtds provided by the urlader that may * indicate kernel and filesystem related partitions */ if (flashsize <= parse_mtd_size("1024KB")) { i = 2; // Skip filesystem, kernel n = ARRAY_SIZE(names) - 1; // Skip reserved-kernel } for (; i < n; i++) { struct mtd_partition *part = ath_partitions + mtd_index; char mtd_name[] = { 'm', 't', 'd', '0' + i, '\0' }; // Default values mtd_start = flashoffset; mtd_end = flashoffset; if( fs_start && (fs_start[0] == '1')) { if (i == 1) mtd_name[3] = '5'; /*--- "reserved" ---*/ if (i == 5) mtd_name[3] = '1'; /*--- "kernel" ---*/ } p = prom_getenv(mtd_name); if (p) { DEBUG_MTD("[mtd] parse %s=\"%s\"", mtd_name, p); parse_mtd_str(p, &mtd_start, &mtd_end); } part->name = names[i]; part->size = mtd_end - mtd_start; part->offset = CPHYSADDR(mtd_start) - flashoffset; DEBUG_MTD("[mtd] use %s (0x%lx->0x%lx) as %s (ofs=0x%llx, size=0x%llx)", mtd_name, mtd_start, mtd_end, part->name, part->offset, part->size); mtd_index++; } add_to_platform_device_list(&ath_flash_device, "mtd-flash"); ath_flash_data.nr_parts = mtd_index; return 0; } __setup("sflash_size=", mtdflash_setup); /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static int __init mtdram_setup(char *p) { char *start; DEBUG_MTD("str=\"%s\"", p); if(p) { start = prom_getenv("linux_fs_start"); if(start && !strcmp(start, "nfs")) { DEBUG_MTD(KERN_ERR "dont use RAM filesystem, use NFS"); return 0; } DEBUG_MTD("mtdram1 %s", p); ath_ram_resource[0].start = CPHYSADDR(simple_strtoul(p, NULL, 16)); ath_ram_resource[0].flags = IORESOURCE_MEM, p = strchr(p, ','); if(p) { p++; ath_ram_resource[0].end =CPHYSADDR(simple_strtoul(p, NULL, 16)); ath_ram_resource[0].end -= 1; } else { ath_ram_resource[0].start = 0; } DEBUG_MTD("mtdram1 0x%08x - 0x%08x", ath_ram_resource[0].start, ath_ram_resource[0].end ); ath_ram_partitions[0].name = "rootfs_ram"; ath_ram_partitions[0].offset = 0; ath_ram_partitions[0].size = ath_ram_resource[0].end - ath_ram_resource[0].start + 1; ath_ram_partitions[0].mask_flags = MTD_ROM; ath_ram_partitions[1].name = "kernel_ram"; ath_ram_partitions[1].offset = 0; ath_ram_partitions[1].size = ath_ram_resource[0].end - ath_ram_resource[0].start + 1; ath_ram_partitions[1].mask_flags = MTD_ROM; ath_ram_data.nr_partitions = 2; } ath_use_mtdram = 1; add_to_platform_device_list(&ath_ram_device, "mtd-ram"); #if defined(CONFIG_MTD_ATH_FLASH) find_nmi_vector_gap(ath_ram_resource[0].start | 0x80000000, ath_ram_resource[0].end | 0x80000000); #endif return 0; } __setup("mtdram1=", mtdram_setup); /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static int __init mtdnand_setup(char *p) { if(!p) return 0; DEBUG_MTD("str=\"%s\"", p); ath_nand_flashsize = parse_mtd_size(p); if (ath_nand_flashsize > 0) { add_to_platform_device_list(&ath_nand_device, "mtd-nand"); } return 0; } __setup("nand_size=", mtdnand_setup);