/* * ---------------------------------------------------------------------------- * * 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 #define DO_MTD #include #include #include #include //#include #include #include #include #include //#include #include #ifdef CONFIG_FUSIV_VX185 #include #include #include #include #endif //#define FUSIV_MTD_DEBUG #if defined(FUSIV_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 /* * tables mapping bootloader mtd names to runtime mtd names, depending * on value of envvariable linux_fs_start */ struct mtd_entry { char *urlader_name; char *runtime_name_0; char *runtime_name_1; }; #if defined(CONFIG_FUSIV_VX185) struct mtd_entry mtd_names_nand[] = { { "mtd0", "filesystem", "reserved-filesystem" }, { "mtd1", "kernel", "reserved-kernel" }, { "mtd0", "reserved-filesystem", "filesystem" }, { "mtd1", "reserved-kernel", "kernel" }, { "mtd5", "config", "config" }, { "", "nand-filesystem", "nand-filesystem" }, // no mtd name, use remaining space { NULL, NULL, NULL }, }; struct mtd_entry mtd_names_spi[] = { { "mtd2", "urlader", "urlader" }, { "mtd3", "tffs (1)", "tffs (1)" }, { "mtd4", "tffs (2)", "tffs (2)" }, { NULL, NULL, NULL }, }; struct mtd_entry mtd_names_nor[] = { { NULL, NULL, NULL }, }; #else #error MTD name tables missing #endif #define FUSIV_MTD_NAND_PARTS (ARRAY_SIZE(mtd_names_nand) - 1) #define FUSIV_MTD_SPI_PARTS (ARRAY_SIZE(mtd_names_spi) - 1) #define FUSIV_MTD_NOR_PARTS (ARRAY_SIZE(mtd_names_nor) - 1) #define FUSIV_MTD_JFFS2_MIN_SIZE 6 #define FUSIV_MTD_JFFS2_MAX_SIZE 16 /*-------------------------------------------------------------------------------------*\ * Zuerst wird das JFFS2 gesucht, dann das Squash-FS! \*-------------------------------------------------------------------------------------*/ // static const char *probes[] = { "find_jffs2", "find_squashfs", NULL }; void fusiv_ram_mtd_set_rw(struct device *pdev, int); extern int __init root_dev_setup(char *line); /* * forward declarations */ static int __init fusiv_mtd_parser_function(struct mtd_info *mtd, struct mtd_partition **p_mtd_pat, struct mtd_part_parser_data *parse_data); /* * AVM partition parser */ static struct mtd_part_parser fusiv_mtd_parser __initdata = { .owner = THIS_MODULE, .parse_fn = fusiv_mtd_parser_function, .name = "avmpart", }; /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ enum _flash_map_enum { MAP_UNKNOWN, MAP_RAM, MAP_NOR_FLASH, MAP_NAND_FLASH, MAP_SPI_FLASH }; #if defined(CONFIG_MTD_NAND) struct mtd_partition fusiv_nand_partitions[FUSIV_MTD_NAND_PARTS]; static struct resource fusiv_nand_resources[] = { [0] = { .start = NFC_BASEADDR + NFC_DATA, .end = NFC_BASEADDR + NFC_SECT7_SYNDROME78, .flags = IORESOURCE_MEM, }, [1] = { .start = NAND_FLASH_INT, .end = NAND_FLASH_INT, .flags = IORESOURCE_IRQ, }, }; static struct fusiv_nand_platform nand_platform_data = { .partitions = fusiv_nand_partitions, .nr_partitions = 0, // will be updated by mtdnand_setup() }; static struct platform_device fusiv_nand_device[] = { { .name = "fusiv-nand", .id = 0, .dev = { .platform_data = &nand_platform_data, }, .num_resources = ARRAY_SIZE(fusiv_nand_resources), .resource = fusiv_nand_resources, } }; #endif // #if defined(CONFIG_MTD_NAND) /*------------------------------------------------------------------------------------------*\ * SPI \*------------------------------------------------------------------------------------------*/ struct mtd_partition fusiv_spi_partitions[FUSIV_MTD_SPI_PARTS]; /* 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 fusiv_spi_resources[] __maybe_unused ={ { .name = "vx185_snor_fifos", .start = VX185_SPI0_RX_FIFO, .end = VX185_SPI0_TX_FIFO + 128 - 1, /* Datasheet says 256 bytes per FIFO, * RX/TX base addresses are * 128 bytes apart, example code * says 64 bytes... :-\ */ .flags = IORESOURCE_MEM, .parent = &sflash_resource }, }; static struct vx185_snor_platform vx185_snor_data = { .partitions = fusiv_spi_partitions, .nr_partitions = 0, // will be updated by mtdspi_setup() }; struct platform_device fusiv_spi_device[] = { { .name = "vx185_snor", .id = 0, .dev = { .platform_data = &vx185_snor_data, }, // .num_resources = ARRAY_SIZE(fusiv_spi_resources), // .resource = fusiv_spi_resources, } }; /*------------------------------------------------------------------------------------------*\ * RAM \*------------------------------------------------------------------------------------------*/ static struct mtd_partition fusiv_ram_partitions[2]; static struct resource fusiv_ram_resource[] = { { /* fuer ins RAM geladenes Filesystem */ .start = 0, .end = 0 + (32 << 20), .flags = IORESOURCE_MEM, } }; static struct platdata_mtd_ram fusiv_ram_data = { .mapname = "ram-filesystem", .bankwidth = 4, .partitions = NULL, //fusiv_ram_partitions, .nr_partitions = 0, .set_rw = fusiv_ram_mtd_set_rw, // .probes = probes }; struct platform_device fusiv_ram_device = { .name = "mtd-ram", .id = -1, .dev = { .platform_data = &fusiv_ram_data, }, .num_resources = 1, .resource = &fusiv_ram_resource[0], }; /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ #define EXT2_SB_OFFSET 0x400 static int fusiv_mtd_parser_function(struct mtd_info *mtd, struct mtd_partition **p_mtd_pat, struct mtd_part_parser_data *parse_data) { struct mtd_partition *pat_tbl; unsigned int maxcount, first_match; size_t readlen, kernel_len; uint32_t magic; loff_t pos; int result; pr_err("[%s] Called for mtd %s\n", __func__, mtd->name); if(p_mtd_pat == NULL){ pr_err("[%s] p_mtd_pat == NULL!\n", __func__); return 0; } maxcount = 0; pat_tbl = NULL; if(strcmp(mtd->name, "ram-filesystem")){ goto err_out; } pat_tbl = kmemdup(fusiv_ram_partitions, sizeof(fusiv_ram_partitions), GFP_KERNEL); BUG_ON(pat_tbl == NULL); /* ---------------...--------------------------------------...------------ * | kernel image ... |padding|sqsh|padding| ext2 image ... | * ---------------...--------------------------------------...------------ */ // 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. pos = ALIGN(fusiv_ram_resource[0].start, 256) - fusiv_ram_resource[0].start; first_match = 0; kernel_len = 0; do{ result = mtd_read(mtd, pos, sizeof(magic), &readlen, (u_char*) &magic); if((result < 0) || (readlen < sizeof(magic))){ pr_err("[%s] breaking due to incomplete or erronous mtd_read\n", __func__); goto err_out; } if(magic == be32_to_cpu(0x73717368) || magic == le32_to_cpu(0x73717368)){ pr_err("[%s] first match found at 0x%08llx\n", __func__, pos); /* * found pseudo squashfs header. Kernel image ends here, so remember * its length. pos will be incremented by 256 before exiting the loop, * conveniently skipping directly to start of ext2 image. */ first_match = 1; kernel_len = pos; } pos += 256; }while(first_match == 0 && pos <= (pat_tbl[0].size + EXT2_SB_OFFSET)); if(first_match == 0){ pr_err("[%s] magic not found\n", __func__); goto err_out; } // check ex2tf super block for magic number result = mtd_read(mtd, pos + EXT2_SB_OFFSET + EXT2_SB_MAGIC_OFFSET, sizeof(magic), &readlen, (u_char*) &magic); if((result < 0) || (readlen < sizeof(magic))){ pr_err("[%s] breaking due to incomplete or erronous mtd_read\n", __func__); goto err_out; } if( le16_to_cpu(magic & 0xffff) != EXT2_SUPER_MAGIC && le16_to_cpu((magic >> 16) & 0xffff) != EXT2_SUPER_MAGIC) { goto err_out; } /* * we found a valid filesystem image. Split mtd into fs(0) and kernel(1) * partition by adjusting start offset and size for fs and size for kernel * partition. */ pat_tbl[0].offset = pos; pat_tbl[0].size -= pos; pat_tbl[1].size = kernel_len; /* * if kernel partition has size 0 (capiterm RAM load), move kernel start * offset beyond end of mtd. Otherwise the mtd core will set up the * partition to cover the whole mtd. */ if(kernel_len == 0){ pat_tbl[1].offset = mtd->size; } maxcount = 2; err_out: if(maxcount == 0 && pat_tbl != NULL){ kfree(pat_tbl); pat_tbl = NULL; } *p_mtd_pat = pat_tbl; return maxcount; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ struct platform_device *fusiv_platform_devices[20]; unsigned int fusiv_platform_devices_count = 0; void add_to_platform_device_list(struct platform_device *device) { pr_info("[FUSIV] add %s to the platform device list\n", device->name); fusiv_platform_devices[fusiv_platform_devices_count++] = device; } /*-------------------------------------------------------------------------------------*\ \*-------------------------------------------------------------------------------------*/ void fusiv_init_platform_devices(void) { pr_info("[FUSIV] register %d platform device(s)\n", fusiv_platform_devices_count); platform_add_devices(fusiv_platform_devices, fusiv_platform_devices_count); } #if 0 /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static inline unsigned int get_flash_base(unsigned int flash_size) { return 0x48000000; } #endif /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ void fusiv_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"); } } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ extern int tffs_mtd[2]; extern int tffs_mtd_offset[2]; static int found_rootfs_ram = 0; static char *str_rootfs[] = { "rootfs_ram", "rootfs", "filesystem" }; static char root_device[64]; struct mtd_info *urlader_mtd; EXPORT_SYMBOL(urlader_mtd); void fusiv_mtd_add_notifier(struct mtd_info *mtd) { int i; if(!mtd->name){ DEBUG_MTD("Leeres MTD uebergeben!"); return; } DEBUG_MTD("name %s" , mtd->name); for(i = 0; i < sizeof(str_rootfs) / sizeof(char*); i++){ if(!strcmp(mtd->name, str_rootfs[i])){ DEBUG_MTD("found %s", 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 ---*/ } DEBUG_MTD("use %s" , mtd->name); sprintf(root_device, "/dev/mtdblock%d", mtd->index); DEBUG_MTD("root device: %s (%s)" , root_device, mtd->name); root_dev_setup(root_device); return; } } if(!strcmp(mtd->name, "urlader")){ DEBUG_MTD("set urlader_mtd"); urlader_mtd = mtd; #if defined(CONFIG_TFFS) }else if(!strcmp(mtd->name, "tffs (1)")){ DEBUG_MTD("tffs (1) on Index %d", mtd->index); tffs_mtd[0] = mtd->index; }else if(!strcmp(mtd->name, "tffs (2)")){ DEBUG_MTD("tffs (2) on Index %d", mtd->index); tffs_mtd[1] = mtd->index; #endif /*--- #if defined(CONFIG_TFFS) ---*/ }else{ DEBUG_MTD("skip %s" , mtd->name); } } void fusiv_mtd_rm_notifier(struct mtd_info *mtd) { DEBUG_MTD("ignore %s", mtd->name); } struct mtd_notifier fusiv_mtd_notifier = { add: fusiv_mtd_add_notifier, remove: fusiv_mtd_rm_notifier }; /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ int __init fusiv_mtd_init(void) { register_mtd_user(&fusiv_mtd_notifier); register_mtd_parser(&fusiv_mtd_parser); fusiv_init_platform_devices(); return 0; } subsys_initcall(fusiv_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; DEBUG_MTD("'%s'", 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 enthaelt mindestens eine KB Angabe ---*/ size *= 1024; if(p[-1] == 'M'){ size *= 1024; } } return size; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static int __init mtdram_setup(char *p) { char *np; uint32_t mem_start, mem_end; int result; if(p == NULL){ return 0; } pr_info("[%s] mtdram1 %s\n", __func__, p); np = prom_getenv("linux_fs_start"); if(np != NULL && !strcmp(np, "nfs")){ pr_info("[%s] ignoring RAM filesystem, using NFS\n", __func__); return 0; } np = strchr(p, ','); if(np == NULL){ goto err_out; } *np = '\0'; ++np; result = kstrtou32(p, 0, &mem_start); if(result != 0){ pr_err("[%s] error parsing start for %s\n", __func__, p); goto err_out; } mem_start = CPHYSADDR(mem_start); result = kstrtou32(np, 0, &mem_end); if(result != 0){ pr_err("[%s] error parsing end for %s\n", __func__, np); goto err_out; } mem_end = CPHYSADDR(mem_end); if(mem_start > 0 && mem_end > mem_start){ fusiv_ram_resource[0].start = mem_start; fusiv_ram_resource[0].end = mem_end - 1; fusiv_ram_resource[0].flags = IORESOURCE_MEM; pr_info("[%s] mtdram1 0x%08x-0x%08x", __func__, mem_start, mem_end); fusiv_ram_partitions[0].name = "rootfs_ram"; fusiv_ram_partitions[0].offset = 0; fusiv_ram_partitions[0].size = mem_end - mem_start; fusiv_ram_partitions[0].mask_flags = MTD_ROM; fusiv_ram_partitions[1].name = "kernel_ram"; fusiv_ram_partitions[1].offset = 0; fusiv_ram_partitions[1].size = fusiv_ram_partitions[0].size; fusiv_ram_partitions[1].mask_flags = MTD_ROM; //fusiv_ram_data.nr_partitions = ARRAY_SIZE(fusiv_ram_partitions); add_to_platform_device_list(&fusiv_ram_device); } err_out: return 0; } __setup("mtdram1=", mtdram_setup); static unsigned int __init mtdpart_setup(struct mtd_entry mtd_names[], unsigned int num_names, struct mtd_partition partitions[], size_t flashsize, unsigned int relative) { loff_t max_seen, size, mtd_start, mtd_end; unsigned long linux_fs_start; unsigned int i, cnt; int result; char *p, *np, tmpbuf[256]; linux_fs_start = 0; p = prom_getenv("linux_fs_start"); if(p != NULL && strcmp(p, "nfs")){ result = kstrtoul(p, 0, &linux_fs_start); if(result == 0 && linux_fs_start != 0){ linux_fs_start = 1; } } result = 0; max_seen = 0; cnt = 0; for(i = 0; i < num_names; ++i){ if(linux_fs_start == 0){ partitions[cnt].name = mtd_names[i].runtime_name_0; }else{ partitions[cnt].name = mtd_names[i].runtime_name_1; } if(strlen(mtd_names[i].urlader_name) > 0){ p = prom_getenv(mtd_names[i].urlader_name); if(p == NULL){ cnt = 0; goto err_out; } memcpy(tmpbuf, p, strlen(p) + 1); p = tmpbuf; np = strchr(p, ','); if(np == NULL){ pr_warn("[%s] invalid value for %s: %s\n", __func__, mtd_names[i].urlader_name, p); continue; } *np = '\0'; ++np; result = kstrtoull(p, 0, &mtd_start); if(result != 0){ pr_err("[%s] error parsing start for %s\n", __func__, mtd_names[i].urlader_name); goto err_out; } result = kstrtoull(np, 0, &mtd_end); if(result != 0){ pr_err("[%s] error parsing end for %s\n", __func__, mtd_names[i].urlader_name); goto err_out; } size = mtd_end - mtd_start; partitions[cnt].offset = relative ? max_seen : mtd_start; partitions[cnt].size = size; partitions[cnt].mask_flags = 0; max_seen = max(max_seen, (loff_t) (partitions[cnt].offset + partitions[cnt].size)); pr_err("[%s] name: %s start: 0x%08llx size: 0x%08llx\n", __func__, partitions[cnt].name, partitions[cnt].offset, partitions[cnt].size); ++cnt; }else{ /* * unnamed partition. Current offset to end. Must be last */ partitions[cnt].offset = max_seen; partitions[cnt].size = flashsize - max_seen; partitions[cnt].mask_flags = 0; max_seen += partitions[cnt].size; ++cnt; break; } } err_out: if(max_seen > flashsize){ pr_err("[%s] flashsize 0x%x too small for sum of partitions 0x%llx.\n", __func__, flashsize, max_seen); cnt = 0; } return cnt; } /*------------------------------------------------------------------------------------------*\ * NAND Parameter parsen \*------------------------------------------------------------------------------------------*/ static int __init mtdnand_setup(char *p) { int result; unsigned long long flashsize_nand; if(!p){ return 0; } flashsize_nand = parse_mtd_size(p); pr_err("[%s] nand_size = 0x%llx\n", __func__, flashsize_nand); if(flashsize_nand == 0){ return 0; } result = mtdpart_setup(mtd_names_nand, ARRAY_SIZE(mtd_names_nand) - 1, fusiv_nand_partitions, flashsize_nand, 0); if(result > 0 && result <= ARRAY_SIZE(fusiv_nand_partitions)){ nand_platform_data.nr_partitions = result; add_to_platform_device_list(&fusiv_nand_device[0]); }else{ pr_err("[%s] invalid number of partitions found: %d\n", __func__, result); } return 0; } __setup("nand_size=", mtdnand_setup); /*------------------------------------------------------------------------------------------*\ * SPI Flash Parameter parsen \*------------------------------------------------------------------------------------------*/ static int __init mtdspi_setup(char *p) { size_t flashsize_spi; int result; result = 0; if(!p){ goto err_out; } flashsize_spi = (unsigned long) parse_mtd_size(p); DEBUG_MTD("sflash_size = 0x%x" , flashsize_spi); result = mtdpart_setup(mtd_names_spi, ARRAY_SIZE(mtd_names_spi) - 1, fusiv_spi_partitions, flashsize_spi, 0); if(result > 0 && result <= ARRAY_SIZE(fusiv_spi_partitions)){ vx185_snor_data.nr_partitions = result; add_to_platform_device_list(&fusiv_spi_device[0]); }else{ pr_err("[%s] invalid number of partitions found: %d\n", __func__, result); } err_out: return result; } __setup("sflash_size=", mtdspi_setup);