blob: d4768741e25fe382eb591b45c7b10c5d8d6606f7 [file] [log] [blame]
/*
* 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