| /* |
| * drivers/mtd/maps/tqm834x.c |
| * |
| * MTD mapping driver for TQM834x boards |
| * |
| * Copyright 2005 Wolfgang Denk, DENX Software Engineering, <wd@denx.de>. |
| * |
| * This file is licensed under the terms of the GNU General Public License |
| * version 2. This program is licensed "as is" without any warranty of any |
| * kind, whether express or implied. |
| * |
| */ |
| |
| #include <linux/init.h> |
| #include <linux/module.h> |
| #include <linux/types.h> |
| #include <linux/kernel.h> |
| #include <linux/slab.h> |
| #include <asm/io.h> |
| #include <asm/ppcboot.h> |
| |
| #include <linux/mtd/mtd.h> |
| #include <linux/mtd/map.h> |
| #include <linux/mtd/partitions.h> |
| |
| #define FLASH_BANK_MAX 2 |
| |
| extern unsigned char __res[]; |
| |
| /* trivial struct to describe partition information */ |
| struct mtd_part_def |
| { |
| int nums; |
| unsigned char *type; |
| struct mtd_partition* mtd_part; |
| }; |
| |
| static struct mtd_info* mtd_banks[FLASH_BANK_MAX]; |
| static struct map_info* map_banks[FLASH_BANK_MAX]; |
| static struct mtd_part_def part_banks[FLASH_BANK_MAX]; |
| |
| static unsigned long num_banks; |
| static unsigned long start_scan_addr; |
| |
| #ifdef CONFIG_MTD_PARTITIONS |
| /* |
| * The following defines the partition layout of TQM834x boards. |
| * |
| * See include/linux/mtd/partitions.h for definition of the |
| * mtd_partition structure. |
| * |
| * Assume minimal initial size of 4 MiB per bank, will be updated |
| * later in init_tqm834x_mtd() routine. |
| */ |
| |
| /* Partition definition for the first flash bank which is always present. */ |
| static struct mtd_partition tqm834x_partitions_bank1[] = { |
| { |
| .name = "u-boot", /* u-boot firmware */ |
| .offset = 0x00000000, |
| .size = 0x00040000, /* 256 KiB */ |
| /*mask_flags: MTD_WRITEABLE, * force read-only */ |
| }, |
| { |
| .name = "env", /* u-boot environment */ |
| .offset = 0x00040000, |
| .size = 0x00020000, /* 128 KiB */ |
| /*mask_flags: MTD_WRITEABLE, * force read-only */ |
| }, |
| { |
| .name = "kernel", /* linux kernel image */ |
| .offset = 0x00060000, |
| .size = 0x00100000, /* 1 MiB */ |
| /*mask_flags: MTD_WRITEABLE, * force read-only */ |
| }, |
| { |
| .name = "initrd", /* ramdisk image */ |
| .offset = 0x00160000, |
| .size = 0x00200000, /* 2 MiB */ |
| }, |
| { |
| .name = "user", /* user data */ |
| .offset = 0x00360000, |
| .size = 0x000a0000, /* remaining space */ |
| /* NOTE: this parttion size is re-calcated in */ |
| /* init_tqm834x_mtd() to cover actual remaining space. */ |
| }, |
| }; |
| |
| /* Partition definition for the second flash bank which may be present on some |
| * TQM834x boards. |
| */ |
| static struct mtd_partition tqm834x_partitions_bank2[] = { |
| { |
| .name = "jffs2", /* jffs2 filesystem */ |
| .offset = 0x00000000, |
| .size = 0x00400000, /* whole device */ |
| /* NOTE: this parttion size is re-calcated in */ |
| /* init_tqm834x_mtd() to cover actual device size. */ |
| }, |
| }; |
| |
| #endif /* CONFIG_MTD_PARTITIONS */ |
| |
| static int __init init_tqm834x_mtd(void) |
| { |
| int idx = 0, ret = 0; |
| unsigned long flash_addr, flash_size, mtd_size = 0; |
| |
| /* pointer to TQM834x board info data */ |
| bd_t *bd = (bd_t *)__res; |
| #ifdef CONFIG_MTD_CMDLINE_PARTS |
| int n; |
| char mtdid[4]; |
| const char *part_probes[] = { "cmdlinepart", NULL }; |
| #endif |
| |
| flash_addr = bd->bi_flashstart; |
| flash_size = bd->bi_flashsize; |
| |
| /* request maximum flash size address space */ |
| start_scan_addr = (unsigned long)ioremap(flash_addr, flash_size); |
| if (!start_scan_addr) { |
| printk("%s: Failed to ioremap address: 0x%lx\n", |
| __FUNCTION__, flash_addr); |
| return -EIO; |
| } |
| |
| for(idx = 0 ; idx < FLASH_BANK_MAX ; idx++) { |
| if (mtd_size >= flash_size) |
| break; |
| |
| pr_debug("%s: chip probing count %d\n", __FUNCTION__, idx); |
| |
| map_banks[idx] = |
| (struct map_info *)kmalloc(sizeof(struct map_info), |
| GFP_KERNEL); |
| if (map_banks[idx] == NULL) { |
| ret = -ENOMEM; |
| goto error_mem; |
| } |
| memset((void *)map_banks[idx], 0, sizeof(struct map_info)); |
| map_banks[idx]->name = (char *)kmalloc(16, GFP_KERNEL); |
| if (map_banks[idx]->name == NULL) { |
| ret = -ENOMEM; |
| goto error_mem; |
| } |
| memset((void *)map_banks[idx]->name, 0, 16); |
| |
| sprintf(map_banks[idx]->name, "TQM834x-%d", idx); |
| map_banks[idx]->size = flash_size; |
| map_banks[idx]->bankwidth = 4; |
| |
| simple_map_init(map_banks[idx]); |
| |
| map_banks[idx]->virt = (void __iomem *) |
| (start_scan_addr + ((idx > 0) ? |
| (mtd_banks[idx-1] ? mtd_banks[idx-1]->size : 0) : 0)); |
| map_banks[idx]->phys = |
| flash_addr + ((idx > 0) ? |
| (mtd_banks[idx-1] ? mtd_banks[idx-1]->size : 0) : 0); |
| |
| /* start to probe flash chips */ |
| mtd_banks[idx] = do_map_probe("cfi_probe", map_banks[idx]); |
| if (mtd_banks[idx]) { |
| mtd_banks[idx]->owner = THIS_MODULE; |
| mtd_size += mtd_banks[idx]->size; |
| num_banks++; |
| pr_debug("%s: bank %ld, name: %s, size: %d bytes \n", |
| __FUNCTION__, num_banks, |
| mtd_banks[idx]->name, mtd_banks[idx]->size); |
| } |
| } |
| |
| /* no supported flash chips found */ |
| if (!num_banks) { |
| printk("TQM834x: No supported flash chips found!\n"); |
| ret = -ENXIO; |
| goto error_mem; |
| } |
| |
| #ifdef CONFIG_MTD_PARTITIONS |
| /* |
| * Select static partition definitions |
| */ |
| n = ARRAY_SIZE(tqm834x_partitions_bank1); |
| part_banks[0].mtd_part = tqm834x_partitions_bank1; |
| part_banks[0].type = "static image bank1"; |
| part_banks[0].nums = n; |
| |
| /* update last partition size to cover actual remaining space */ |
| tqm834x_partitions_bank1[n - 1].size = |
| mtd_banks[0]->size - |
| tqm834x_partitions_bank1[n - 1].offset; |
| |
| /* check if we have second bank? */ |
| if (num_banks == 2) { |
| n = ARRAY_SIZE(tqm834x_partitions_bank2); |
| part_banks[1].mtd_part = tqm834x_partitions_bank2; |
| part_banks[1].type = "static image bank2"; |
| part_banks[1].nums = n; |
| |
| /* update last partition size to cover actual remaining space */ |
| tqm834x_partitions_bank2[n - 1].size = |
| mtd_banks[1]->size - |
| tqm834x_partitions_bank2[n - 1].offset; |
| } |
| |
| for(idx = 0; idx < num_banks ; idx++) { |
| #ifdef CONFIG_MTD_CMDLINE_PARTS |
| sprintf(mtdid, "%d", idx); |
| n = parse_mtd_partitions(mtd_banks[idx], |
| part_probes, |
| &part_banks[idx].mtd_part, |
| 0); |
| pr_debug("%s: %d command line partitions on bank %s\n", |
| __FUNCTION__, n, mtdid); |
| if (n > 0) { |
| part_banks[idx].type = "command line"; |
| part_banks[idx].nums = n; |
| } |
| #endif /* CONFIG_MTD_CMDLINE_PARTS */ |
| if (part_banks[idx].nums == 0) { |
| printk(KERN_NOTICE |
| "TQM834x flash bank %d: no partition info " |
| "available, registering whole device\n", idx); |
| add_mtd_device(mtd_banks[idx]); |
| } else { |
| printk(KERN_NOTICE |
| "TQM834x flash bank %d: Using %s partition " |
| "definition\n", idx, part_banks[idx].type); |
| add_mtd_partitions(mtd_banks[idx], |
| part_banks[idx].mtd_part, |
| part_banks[idx].nums); |
| } |
| } |
| #else /* ! CONFIG_MTD_PARTITIONS */ |
| printk(KERN_NOTICE "TQM834x flash: registering %d flash banks " |
| "at once\n", num_banks); |
| |
| for(idx = 0 ; idx < num_banks ; idx++) |
| add_mtd_device(mtd_banks[idx]); |
| |
| #endif /* CONFIG_MTD_PARTITIONS */ |
| |
| return 0; |
| error_mem: |
| for (idx = 0 ; idx < FLASH_BANK_MAX ; idx++) { |
| if (map_banks[idx] != NULL) { |
| if (map_banks[idx]->name != NULL) { |
| kfree(map_banks[idx]->name); |
| map_banks[idx]->name = NULL; |
| } |
| kfree(map_banks[idx]); |
| map_banks[idx] = NULL; |
| } |
| } |
| |
| iounmap((void *)start_scan_addr); |
| |
| return ret; |
| } |
| |
| static void __exit cleanup_tqm834x_mtd(void) |
| { |
| unsigned int idx = 0; |
| for(idx = 0 ; idx < num_banks ; idx++) { |
| /* destroy mtd_info previously allocated */ |
| if (mtd_banks[idx]) { |
| del_mtd_partitions(mtd_banks[idx]); |
| map_destroy(mtd_banks[idx]); |
| } |
| |
| /* release map_info not used anymore */ |
| kfree(map_banks[idx]->name); |
| kfree(map_banks[idx]); |
| } |
| |
| if (start_scan_addr) { |
| iounmap((void *)start_scan_addr); |
| start_scan_addr = 0; |
| } |
| } |
| |
| module_init(init_tqm834x_mtd); |
| module_exit(cleanup_tqm834x_mtd); |
| |
| MODULE_LICENSE("GPL"); |
| MODULE_AUTHOR("Wolfgang Denk <wd@denx.de>"); |
| MODULE_DESCRIPTION("MTD map driver for TQM834x boards"); |