| /* |
| * Normal mappings of chips in physical memory |
| * |
| * Copyright (C) 2003 MontaVista Software Inc. |
| * Author: Jun Sun, jsun@mvista.com or jsun@junsun.net |
| * |
| * 031022 - [jsun] add run-time configure and partition setup |
| */ |
| |
| #include <linux/module.h> |
| #include <linux/types.h> |
| #include <linux/kernel.h> |
| #include <linux/init.h> |
| #include <linux/slab.h> |
| #include <linux/device.h> |
| #include <linux/platform_device.h> |
| #include <linux/mtd/mtd.h> |
| #include <linux/mtd/map.h> |
| #include <linux/mtd/partitions.h> |
| #include <linux/mtd/physmap.h> |
| #include <linux/mtd/concat.h> |
| #include <linux/io.h> |
| #include <linux/of.h> |
| |
| #include <linux/err.h> |
| /* #include <asm/rt2880/rt_mmap.h> */ |
| #include <asm/mach-ralink/rt3883.h> |
| #include "ralink-flash.h" |
| |
| struct physmap_flash_info { |
| struct resource *res; |
| struct mtd_info *mtd; |
| struct map_info *map; |
| #ifdef CONFIG_MTD_PARTITIONS |
| int nr_parts; |
| struct mtd_partition *parts; |
| #endif |
| }; |
| |
| static const char ralink_map_name[] = "ralink_nor"; |
| static const char * const part_probe_types[] = { "cmdlinepart", "RedBoot", NULL }; |
| |
| int ra_check_flash_type(void) |
| { |
| return 0; |
| |
| uint8_t Id[10]; |
| int syscfg=0; |
| int boot_from=0; |
| int chip_mode=0; |
| |
| memset(Id, 0, sizeof(Id)); |
| strncpy(Id, (char *)RALINK_SYSCTL_BASE, 6); |
| syscfg = (*((int *)(RALINK_SYSCTL_BASE + 0x10))); |
| |
| if((strcmp(Id,"RT3052")==0) || (strcmp(Id, "RT3350")==0)) { |
| boot_from = (syscfg >> 16) & 0x3; |
| switch(boot_from) |
| { |
| case 0: |
| case 1: |
| boot_from=BOOT_FROM_NOR; |
| break; |
| case 2: |
| boot_from=BOOT_FROM_NAND; |
| break; |
| case 3: |
| boot_from=BOOT_FROM_SPI; |
| break; |
| } |
| }else if(strcmp(Id,"RT3883")==0) { |
| boot_from = (syscfg >> 4) & 0x3; |
| switch(boot_from) |
| { |
| case 0: |
| case 1: |
| boot_from=BOOT_FROM_NOR; |
| break; |
| case 2: |
| case 3: |
| chip_mode = syscfg & 0xF; |
| if((chip_mode==0) || (chip_mode==7)) { |
| boot_from = BOOT_FROM_SPI; |
| }else if(chip_mode==8) { |
| boot_from = BOOT_FROM_NAND; |
| }else { |
| printk("unknown chip_mode=%d\n",chip_mode); |
| } |
| break; |
| } |
| }else if(strcmp(Id,"RT3352")==0) { |
| boot_from = BOOT_FROM_SPI; |
| }else if(strcmp(Id,"RT5350")==0) { |
| boot_from = BOOT_FROM_SPI; |
| }else if(strcmp(Id,"RT2880")==0) { |
| boot_from = BOOT_FROM_NOR; |
| }else if(strcmp(Id,"RT6855")==0) { |
| boot_from = BOOT_FROM_SPI; |
| } else { |
| printk("%s: %s is not supported\n",__FUNCTION__, Id); |
| } |
| |
| //printk("%s: boot from %d\n",__FUNCTION__, boot_from); |
| return boot_from; |
| } |
| |
| /* |
| Create dynamic MTD mappings. There are hints in |
| the flash image for kernel and filesystem sizes. |
| Use those for the mapping, and the Upgrade |
| partition gets all free flash. |
| */ |
| void __init adjust_dynamic_maps(struct mtd_partition *parts, |
| int number_partitions) |
| { |
| int i; |
| u32 size; |
| u32 aligned_size = 0; |
| u32 magic; |
| u32 *ptr; |
| |
| int kernel = 0, filesystem = 0, upgrade = 0; |
| |
| #define KERNEL_MAGIC 0x27051956 |
| #define FS_MAGIC 0x27051958 |
| |
| //printk("%s\n", __FUNCTION__); |
| |
| #if 0 |
| printk("parts 0x%x, number_partitions %d\n", parts, number_partitions); |
| if (0 == parts) return; |
| for (i = 0; i < number_partitions; i++) { |
| printk("name %s, size 0x%x, offset 0x%x, flags 0x%x \n", |
| parts[i].name, parts[i].size, parts[i].offset, parts[i].mask_flags); |
| } |
| #endif |
| |
| /* Let's determine partitions based on names */ |
| for (i = 0; i < number_partitions; i++) { |
| if (strncmp(parts[i].name, "Kernel", strlen("Kernel")) == 0) { |
| kernel = i; |
| } |
| if (strncmp(parts[i].name, "RootFS", strlen("RootFS")) == 0) { |
| filesystem = i; |
| } |
| if (strncmp(parts[i].name, "Upgrade", strlen("Upgrade")) == 0) { |
| upgrade = i; |
| } |
| } |
| |
| // NOR flash we can check the flash memory locations directly |
| |
| ptr = (u32 *)CKSEG1ADDR(0x1C000000 + 0x40000 + 0x20000 + 0x20000); |
| /* ptr = (u32 *)CKSEG1ADDR(CONFIG_MTD_PHYSMAP_START + MTD_BOOT_PART_SIZE + */ |
| /* MTD_CONFIG_PART_SIZE + MTD_FACTORY_PART_SIZE); */ |
| |
| // Find/set kernel size |
| memcpy(&magic, ptr, sizeof(u32)); |
| //printk("%x\n", ntohl(magic)); |
| if ((ntohl(magic) == KERNEL_MAGIC) && kernel) { |
| memcpy(&size, ptr + 3, sizeof(u32)); |
| size = ntohl(size); |
| //printk("size 0x%x\n", size); |
| aligned_size = (size + 0x20000) & ~(0x20000 - 1); |
| //printk("New size %x\n", aligned_size); |
| parts[kernel].size = aligned_size; |
| } |
| |
| // Find/Set Filesystem size |
| ptr = (u32 *)CKSEG1ADDR(0x1C000000 + 0x40000 + 0x20000 + 0x20000 + aligned_size - 16); |
| /* ptr = (u32 *)CKSEG1ADDR(CONFIG_MTD_PHYSMAP_START + MTD_BOOT_PART_SIZE + */ |
| /* MTD_CONFIG_PART_SIZE + MTD_FACTORY_PART_SIZE + aligned_size - 16); */ |
| memcpy(&magic, ptr, sizeof(u32)); |
| //printk("%p %x\n", ptr, ntohl(magic)); |
| if ((ntohl(magic) == FS_MAGIC) && filesystem) { |
| memcpy(&size, ptr + 2, sizeof(u32)); |
| //printk("%x\n", size); |
| //size = ntohl(size); // size is aligned correctly |
| aligned_size = (size + 0x20000) & ~(0x20000 - 1); |
| parts[filesystem].size = aligned_size; |
| } |
| |
| // Upgrade partition has all the rest of the flash |
| #ifdef CONFIG_MTD_TESTS_MODULE |
| // If I'm using MTD tests, then I want a smaller |
| // partition to play in. |
| if (parts[upgrade].size == 0) |
| #endif |
| { |
| if (upgrade) { |
| parts[upgrade].size = 0x2000000; |
| /* parts[upgrade].size = CONFIG_MTD_PHYSMAP_LEN; */ |
| for (i = 0; i < number_partitions; i++) { |
| if (i != upgrade) |
| parts[upgrade].size -= parts[i].size; |
| } |
| } |
| } |
| |
| #if 0 |
| if (0 == parts) return; |
| for (i = 0; i < number_partitions; i++) { |
| printk("name %s, size 0x%x, offset 0x%x, flags 0x%x \n", |
| parts[i].name, parts[i].size, parts[i].offset, parts[i].mask_flags); |
| } |
| #endif |
| |
| } |
| |
| |
| |
| static int physmap_flash_probe(struct platform_device *dev) |
| { |
| struct mtd_part_parser_data ppdata; |
| struct physmap_flash_info *info; |
| int err; |
| |
| printk("****** %s init\n", __FUNCTION__); |
| info = kzalloc(sizeof(struct physmap_flash_info), GFP_KERNEL); |
| if (!info) |
| return -ENOMEM; |
| |
| platform_set_drvdata(dev, info); |
| info->res = platform_get_resource(dev, IORESOURCE_MEM, 0); |
| if (!info->res) { |
| dev_err(&dev->dev, "failed to get memory resource\n"); |
| err = -ENOENT; |
| goto err_out; |
| } |
| |
| info->map = kzalloc(sizeof(struct map_info), GFP_KERNEL); |
| info->map->phys = info->res->start; |
| info->map->size = resource_size(info->res); |
| info->map->virt = devm_ioremap_resource(&dev->dev, info->res); |
| if (IS_ERR(info->map->virt)) { |
| err = PTR_ERR(info->map->virt); |
| goto err_out; |
| } |
| |
| info->map->name = ralink_map_name; |
| info->map->bankwidth = 2; |
| |
| simple_map_init(info->map); |
| |
| info->mtd = do_map_probe("cfi_probe", info->map); |
| |
| if (!info->mtd) { |
| dev_err(&dev->dev, "probing failed\n"); |
| err = -ENXIO; |
| goto err_free; |
| } |
| |
| info->mtd->owner = THIS_MODULE; |
| info->mtd->dev.parent = &dev->dev; |
| |
| #if 0 |
| #ifdef CONFIG_MTD_PARTITIONS |
| adjust_dynamic_maps(physmap_data->parts, physmap_data->nr_parts); |
| err = parse_mtd_partitions(info->cmtd, part_probe_types, |
| &info->parts, 0); |
| if (err > 0) { |
| mtd_device_register(info->cmtd, info->parts, err); |
| info->nr_parts = err; |
| return 0; |
| } |
| |
| if (physmap_data->nr_parts) { |
| printk(KERN_NOTICE "Using physmap partition information."); |
| mtd_device_register(info->cmtd, physmap_data->parts, |
| physmap_data->nr_parts); |
| return 0; |
| } |
| #endif |
| #endif |
| |
| ppdata.of_node = dev->dev.of_node; |
| err = mtd_device_parse_register(info->mtd, part_probe_types, |
| &ppdata, NULL, 0); |
| //printk("****** %s init\n", __FUNCTION__); |
| if (err) { |
| dev_err(&dev->dev, "failed to add partitions\n"); |
| goto err_destroy; |
| } |
| |
| return 0; |
| |
| err_destroy: |
| map_destroy(info->mtd); |
| err_free: |
| kfree(info->map); |
| err_out: |
| kfree(info); |
| return err; |
| } |
| |
| static int physmap_flash_remove(struct platform_device *dev) |
| { |
| struct physmap_flash_info *info = NULL; |
| |
| info = platform_get_drvdata(dev); |
| if (info) { |
| if (info->mtd) { |
| mtd_device_unregister(info->mtd); |
| map_destroy(info->mtd); |
| } |
| kfree(info->map); |
| kfree(info); |
| } |
| return 0; |
| } |
| |
| static const struct of_device_id ralink_mtd_match[] = { |
| { .compatible = "ralink,nor" }, |
| {}, |
| }; |
| MODULE_DEVICE_TABLE(of, ralink_mtd_match); |
| |
| static struct platform_driver physmap_flash_driver = { |
| .probe = physmap_flash_probe, |
| .remove = physmap_flash_remove, |
| .driver = { |
| .name = "ralink-flash", |
| .owner = THIS_MODULE, |
| .of_match_table = ralink_mtd_match, |
| }, |
| }; |
| |
| static int __init ralink_physmap_init(void) |
| { |
| int err = 0; |
| |
| //printk("*** %s\n", __FUNCTION__); |
| |
| if(ra_check_flash_type()!=BOOT_FROM_NOR) { |
| return 0; |
| } |
| |
| err = platform_driver_register(&physmap_flash_driver); |
| return err; |
| } |
| |
| static void __exit ralink_physmap_exit(void) |
| { |
| platform_driver_unregister(&physmap_flash_driver); |
| } |
| |
| module_init(ralink_physmap_init); |
| module_exit(ralink_physmap_exit); |
| |
| MODULE_LICENSE("GPL"); |
| MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>"); |
| MODULE_DESCRIPTION("Generic configurable MTD map driver"); |
| |
| /* legacy platform drivers can't hotplug or coldplg */ |
| #ifndef CONFIG_MTD_PHYSMAP_COMPAT |
| /* work with hotplug and coldplug */ |
| MODULE_ALIAS("platform:physmap-flash"); |
| #endif |
| |
| /* |
| * Flash API: ra_mtd_read, ra_mtd_write |
| * Arguments: |
| * - num: specific the mtd number |
| * - to/from: the offset to read from or written to |
| * - len: length |
| * - buf: data to be read/written |
| * Returns: |
| * - return -errno if failed |
| * - return the number of bytes read/written if successed |
| */ |
| #ifdef RA_MTD_RW_BY_NUM |
| int ra_mtd_write(int num, loff_t to, size_t len, const u_char *buf) |
| { |
| int ret = -1; |
| size_t rdlen, wrlen; |
| struct mtd_info *mtd; |
| struct erase_info ei; |
| u_char *bak = NULL; |
| |
| mtd = get_mtd_device(NULL, num); |
| if (IS_ERR(mtd)) |
| return (int)mtd; |
| if (len > mtd->erasesize) { |
| put_mtd_device(mtd); |
| return -E2BIG; |
| } |
| |
| bak = kmalloc(mtd->erasesize, GFP_KERNEL); |
| if (bak == NULL) { |
| put_mtd_device(mtd); |
| return -ENOMEM; |
| } |
| |
| ret = mtd_read(mtd, 0, mtd->erasesize, &rdlen, bak); |
| if (ret != 0) { |
| put_mtd_device(mtd); |
| kfree(bak); |
| return ret; |
| } |
| if (rdlen != mtd->erasesize) |
| printk("warning: ra_mtd_write: rdlen is not equal to erasesize\n"); |
| |
| memcpy(bak + to, buf, len); |
| |
| ei.mtd = mtd; |
| ei.callback = NULL; |
| ei.addr = 0; |
| ei.len = mtd->erasesize; |
| ei.priv = 0; |
| ret = mtd_erase(mtd, &ei); |
| if (ret != 0) { |
| put_mtd_device(mtd); |
| kfree(bak); |
| return ret; |
| } |
| |
| ret = mtd_write(mtd, 0, mtd->erasesize, &wrlen, bak); |
| |
| put_mtd_device(mtd); |
| kfree(bak); |
| return ret; |
| } |
| #endif |
| |
| //#ifdef CONFIG_KERNEL_NVRAM |
| int ra_mtd_write_nm(char *name, loff_t to, size_t len, const u_char *buf) |
| { |
| int ret = -1; |
| size_t rdlen, wrlen; |
| struct mtd_info *mtd; |
| struct erase_info ei; |
| u_char *bak = NULL; |
| |
| mtd = get_mtd_device_nm(name); |
| if (IS_ERR(mtd)) |
| return (int)mtd; |
| if (len > mtd->erasesize) { |
| put_mtd_device(mtd); |
| return -E2BIG; |
| } |
| |
| bak = kmalloc(mtd->erasesize, GFP_KERNEL); |
| if (bak == NULL) { |
| put_mtd_device(mtd); |
| return -ENOMEM; |
| } |
| |
| ret = mtd_read(mtd, 0, mtd->erasesize, &rdlen, bak); |
| if (ret != 0) { |
| put_mtd_device(mtd); |
| kfree(bak); |
| return ret; |
| } |
| if (rdlen != mtd->erasesize) |
| printk("warning: ra_mtd_write: rdlen is not equal to erasesize\n"); |
| |
| memcpy(bak + to, buf, len); |
| |
| ei.mtd = mtd; |
| ei.callback = NULL; |
| ei.addr = 0; |
| ei.len = mtd->erasesize; |
| ei.priv = 0; |
| ret = mtd_erase(mtd, &ei); |
| if (ret != 0) { |
| put_mtd_device(mtd); |
| kfree(bak); |
| return ret; |
| } |
| |
| ret = mtd_write(mtd, 0, mtd->erasesize, &wrlen, bak); |
| |
| put_mtd_device(mtd); |
| kfree(bak); |
| return ret; |
| } |
| //#endif |
| |
| #ifdef RA_MTD_RW_BY_NUM |
| int ra_mtd_read(int num, loff_t from, size_t len, u_char *buf) |
| { |
| int ret; |
| size_t rdlen; |
| struct mtd_info *mtd; |
| |
| mtd = get_mtd_device(NULL, num); |
| if (IS_ERR(mtd)) |
| return (int)mtd; |
| |
| ret = mtd_read(mtd, from, len, &rdlen, buf); |
| if (rdlen != len) |
| printk("warning: ra_mtd_read: rdlen is not equal to len\n"); |
| |
| put_mtd_device(mtd); |
| return ret; |
| } |
| #endif |
| |
| //#ifdef CONFIG_KERNEL_NVRAM |
| int ra_mtd_read_nm(char *name, loff_t from, size_t len, u_char *buf) |
| { |
| int ret; |
| size_t rdlen; |
| struct mtd_info *mtd; |
| |
| mtd = get_mtd_device_nm(name); |
| if (IS_ERR(mtd)) |
| return (int)mtd; |
| |
| ret = mtd_read(mtd, from, len, &rdlen, buf); |
| if (rdlen != len) |
| printk("warning: ra_mtd_read_nm: rdlen is not equal to len\n"); |
| |
| put_mtd_device(mtd); |
| return ret; |
| } |
| //#endif |
| |
| #ifdef RA_MTD_RW_BY_NUM |
| EXPORT_SYMBOL(ra_mtd_write); |
| EXPORT_SYMBOL(ra_mtd_read); |
| #endif |
| //#ifdef CONFIG_KERNEL_NVRAM |
| EXPORT_SYMBOL(ra_mtd_write_nm); |
| EXPORT_SYMBOL(ra_mtd_read_nm); |
| //#endif |