|
@@ -20,11 +20,15 @@
|
|
#include <linux/mtd/map.h>
|
|
#include <linux/mtd/map.h>
|
|
#include <linux/mtd/partitions.h>
|
|
#include <linux/mtd/partitions.h>
|
|
#include <linux/mtd/physmap.h>
|
|
#include <linux/mtd/physmap.h>
|
|
|
|
+#include <linux/mtd/concat.h>
|
|
#include <asm/io.h>
|
|
#include <asm/io.h>
|
|
|
|
|
|
|
|
+#define MAX_RESOURCES 4
|
|
|
|
+
|
|
struct physmap_flash_info {
|
|
struct physmap_flash_info {
|
|
- struct mtd_info *mtd;
|
|
|
|
- struct map_info map;
|
|
|
|
|
|
+ struct mtd_info *mtd[MAX_RESOURCES];
|
|
|
|
+ struct mtd_info *cmtd;
|
|
|
|
+ struct map_info map[MAX_RESOURCES];
|
|
struct resource *res;
|
|
struct resource *res;
|
|
#ifdef CONFIG_MTD_PARTITIONS
|
|
#ifdef CONFIG_MTD_PARTITIONS
|
|
int nr_parts;
|
|
int nr_parts;
|
|
@@ -32,11 +36,11 @@ struct physmap_flash_info {
|
|
#endif
|
|
#endif
|
|
};
|
|
};
|
|
|
|
|
|
-
|
|
|
|
static int physmap_flash_remove(struct platform_device *dev)
|
|
static int physmap_flash_remove(struct platform_device *dev)
|
|
{
|
|
{
|
|
struct physmap_flash_info *info;
|
|
struct physmap_flash_info *info;
|
|
struct physmap_flash_data *physmap_data;
|
|
struct physmap_flash_data *physmap_data;
|
|
|
|
+ int i;
|
|
|
|
|
|
info = platform_get_drvdata(dev);
|
|
info = platform_get_drvdata(dev);
|
|
if (info == NULL)
|
|
if (info == NULL)
|
|
@@ -45,24 +49,33 @@ static int physmap_flash_remove(struct platform_device *dev)
|
|
|
|
|
|
physmap_data = dev->dev.platform_data;
|
|
physmap_data = dev->dev.platform_data;
|
|
|
|
|
|
- if (info->mtd != NULL) {
|
|
|
|
|
|
+#ifdef CONFIG_MTD_CONCAT
|
|
|
|
+ if (info->cmtd != info->mtd[0]) {
|
|
|
|
+ del_mtd_device(info->cmtd);
|
|
|
|
+ mtd_concat_destroy(info->cmtd);
|
|
|
|
+ }
|
|
|
|
+#endif
|
|
|
|
+
|
|
|
|
+ for (i = 0; i < MAX_RESOURCES; i++) {
|
|
|
|
+ if (info->mtd[i] != NULL) {
|
|
#ifdef CONFIG_MTD_PARTITIONS
|
|
#ifdef CONFIG_MTD_PARTITIONS
|
|
- if (info->nr_parts) {
|
|
|
|
- del_mtd_partitions(info->mtd);
|
|
|
|
- kfree(info->parts);
|
|
|
|
- } else if (physmap_data->nr_parts) {
|
|
|
|
- del_mtd_partitions(info->mtd);
|
|
|
|
- } else {
|
|
|
|
- del_mtd_device(info->mtd);
|
|
|
|
- }
|
|
|
|
|
|
+ if (info->nr_parts) {
|
|
|
|
+ del_mtd_partitions(info->mtd[i]);
|
|
|
|
+ kfree(info->parts);
|
|
|
|
+ } else if (physmap_data->nr_parts) {
|
|
|
|
+ del_mtd_partitions(info->mtd[i]);
|
|
|
|
+ } else {
|
|
|
|
+ del_mtd_device(info->mtd[i]);
|
|
|
|
+ }
|
|
#else
|
|
#else
|
|
- del_mtd_device(info->mtd);
|
|
|
|
|
|
+ del_mtd_device(info->mtd[i]);
|
|
#endif
|
|
#endif
|
|
- map_destroy(info->mtd);
|
|
|
|
- }
|
|
|
|
|
|
+ map_destroy(info->mtd[i]);
|
|
|
|
+ }
|
|
|
|
|
|
- if (info->map.virt != NULL)
|
|
|
|
- iounmap(info->map.virt);
|
|
|
|
|
|
+ if (info->map[i].virt != NULL)
|
|
|
|
+ iounmap(info->map[i].virt);
|
|
|
|
+ }
|
|
|
|
|
|
if (info->res != NULL) {
|
|
if (info->res != NULL) {
|
|
release_resource(info->res);
|
|
release_resource(info->res);
|
|
@@ -82,16 +95,14 @@ static int physmap_flash_probe(struct platform_device *dev)
|
|
struct physmap_flash_data *physmap_data;
|
|
struct physmap_flash_data *physmap_data;
|
|
struct physmap_flash_info *info;
|
|
struct physmap_flash_info *info;
|
|
const char **probe_type;
|
|
const char **probe_type;
|
|
- int err;
|
|
|
|
|
|
+ int err = 0;
|
|
|
|
+ int i;
|
|
|
|
+ int devices_found = 0;
|
|
|
|
|
|
physmap_data = dev->dev.platform_data;
|
|
physmap_data = dev->dev.platform_data;
|
|
if (physmap_data == NULL)
|
|
if (physmap_data == NULL)
|
|
return -ENODEV;
|
|
return -ENODEV;
|
|
|
|
|
|
- printk(KERN_NOTICE "physmap platform flash device: %.8llx at %.8llx\n",
|
|
|
|
- (unsigned long long)(dev->resource->end - dev->resource->start + 1),
|
|
|
|
- (unsigned long long)dev->resource->start);
|
|
|
|
-
|
|
|
|
info = kzalloc(sizeof(struct physmap_flash_info), GFP_KERNEL);
|
|
info = kzalloc(sizeof(struct physmap_flash_info), GFP_KERNEL);
|
|
if (info == NULL) {
|
|
if (info == NULL) {
|
|
err = -ENOMEM;
|
|
err = -ENOMEM;
|
|
@@ -100,56 +111,83 @@ static int physmap_flash_probe(struct platform_device *dev)
|
|
|
|
|
|
platform_set_drvdata(dev, info);
|
|
platform_set_drvdata(dev, info);
|
|
|
|
|
|
- info->res = request_mem_region(dev->resource->start,
|
|
|
|
- dev->resource->end - dev->resource->start + 1,
|
|
|
|
- dev->dev.bus_id);
|
|
|
|
- if (info->res == NULL) {
|
|
|
|
- dev_err(&dev->dev, "Could not reserve memory region\n");
|
|
|
|
- err = -ENOMEM;
|
|
|
|
- goto err_out;
|
|
|
|
- }
|
|
|
|
|
|
+ for (i = 0; i < dev->num_resources; i++) {
|
|
|
|
+ printk(KERN_NOTICE "physmap platform flash device: %.8llx at %.8llx\n",
|
|
|
|
+ (unsigned long long)(dev->resource[i].end - dev->resource[i].start + 1),
|
|
|
|
+ (unsigned long long)dev->resource[i].start);
|
|
|
|
+
|
|
|
|
+ info->res = request_mem_region(dev->resource[i].start,
|
|
|
|
+ dev->resource[i].end - dev->resource[i].start + 1,
|
|
|
|
+ dev->dev.bus_id);
|
|
|
|
+ if (info->res == NULL) {
|
|
|
|
+ dev_err(&dev->dev, "Could not reserve memory region\n");
|
|
|
|
+ err = -ENOMEM;
|
|
|
|
+ goto err_out;
|
|
|
|
+ }
|
|
|
|
|
|
- info->map.name = dev->dev.bus_id;
|
|
|
|
- info->map.phys = dev->resource->start;
|
|
|
|
- info->map.size = dev->resource->end - dev->resource->start + 1;
|
|
|
|
- info->map.bankwidth = physmap_data->width;
|
|
|
|
- info->map.set_vpp = physmap_data->set_vpp;
|
|
|
|
|
|
+ info->map[i].name = dev->dev.bus_id;
|
|
|
|
+ info->map[i].phys = dev->resource[i].start;
|
|
|
|
+ info->map[i].size = dev->resource[i].end - dev->resource[i].start + 1;
|
|
|
|
+ info->map[i].bankwidth = physmap_data->width;
|
|
|
|
+ info->map[i].set_vpp = physmap_data->set_vpp;
|
|
|
|
+
|
|
|
|
+ info->map[i].virt = ioremap(info->map[i].phys, info->map[i].size);
|
|
|
|
+ if (info->map[i].virt == NULL) {
|
|
|
|
+ dev_err(&dev->dev, "Failed to ioremap flash region\n");
|
|
|
|
+ err = EIO;
|
|
|
|
+ goto err_out;
|
|
|
|
+ }
|
|
|
|
|
|
- info->map.virt = ioremap(info->map.phys, info->map.size);
|
|
|
|
- if (info->map.virt == NULL) {
|
|
|
|
- dev_err(&dev->dev, "Failed to ioremap flash region\n");
|
|
|
|
- err = EIO;
|
|
|
|
- goto err_out;
|
|
|
|
- }
|
|
|
|
|
|
+ simple_map_init(&info->map[i]);
|
|
|
|
|
|
- simple_map_init(&info->map);
|
|
|
|
|
|
+ probe_type = rom_probe_types;
|
|
|
|
+ for (; info->mtd[i] == NULL && *probe_type != NULL; probe_type++)
|
|
|
|
+ info->mtd[i] = do_map_probe(*probe_type, &info->map[i]);
|
|
|
|
+ if (info->mtd[i] == NULL) {
|
|
|
|
+ dev_err(&dev->dev, "map_probe failed\n");
|
|
|
|
+ err = -ENXIO;
|
|
|
|
+ goto err_out;
|
|
|
|
+ } else {
|
|
|
|
+ devices_found++;
|
|
|
|
+ }
|
|
|
|
+ info->mtd[i]->owner = THIS_MODULE;
|
|
|
|
+ }
|
|
|
|
|
|
- probe_type = rom_probe_types;
|
|
|
|
- for (; info->mtd == NULL && *probe_type != NULL; probe_type++)
|
|
|
|
- info->mtd = do_map_probe(*probe_type, &info->map);
|
|
|
|
- if (info->mtd == NULL) {
|
|
|
|
- dev_err(&dev->dev, "map_probe failed\n");
|
|
|
|
|
|
+ if (devices_found == 1) {
|
|
|
|
+ info->cmtd = info->mtd[0];
|
|
|
|
+ } else if (devices_found > 1) {
|
|
|
|
+ /*
|
|
|
|
+ * We detected multiple devices. Concatenate them together.
|
|
|
|
+ */
|
|
|
|
+#ifdef CONFIG_MTD_CONCAT
|
|
|
|
+ info->cmtd = mtd_concat_create(info->mtd, devices_found, dev->dev.bus_id);
|
|
|
|
+ if (info->cmtd == NULL)
|
|
|
|
+ err = -ENXIO;
|
|
|
|
+#else
|
|
|
|
+ printk(KERN_ERR "physmap-flash: multiple devices "
|
|
|
|
+ "found but MTD concat support disabled.\n");
|
|
err = -ENXIO;
|
|
err = -ENXIO;
|
|
- goto err_out;
|
|
|
|
|
|
+#endif
|
|
}
|
|
}
|
|
- info->mtd->owner = THIS_MODULE;
|
|
|
|
|
|
+ if (err)
|
|
|
|
+ goto err_out;
|
|
|
|
|
|
#ifdef CONFIG_MTD_PARTITIONS
|
|
#ifdef CONFIG_MTD_PARTITIONS
|
|
- err = parse_mtd_partitions(info->mtd, part_probe_types, &info->parts, 0);
|
|
|
|
|
|
+ err = parse_mtd_partitions(info->cmtd, part_probe_types, &info->parts, 0);
|
|
if (err > 0) {
|
|
if (err > 0) {
|
|
- add_mtd_partitions(info->mtd, info->parts, err);
|
|
|
|
|
|
+ add_mtd_partitions(info->cmtd, info->parts, err);
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
if (physmap_data->nr_parts) {
|
|
if (physmap_data->nr_parts) {
|
|
printk(KERN_NOTICE "Using physmap partition information\n");
|
|
printk(KERN_NOTICE "Using physmap partition information\n");
|
|
- add_mtd_partitions(info->mtd, physmap_data->parts,
|
|
|
|
- physmap_data->nr_parts);
|
|
|
|
|
|
+ add_mtd_partitions(info->cmtd, physmap_data->parts,
|
|
|
|
+ physmap_data->nr_parts);
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
|
|
- add_mtd_device(info->mtd);
|
|
|
|
|
|
+ add_mtd_device(info->cmtd);
|
|
return 0;
|
|
return 0;
|
|
|
|
|
|
err_out:
|
|
err_out:
|
|
@@ -162,9 +200,11 @@ static int physmap_flash_suspend(struct platform_device *dev, pm_message_t state
|
|
{
|
|
{
|
|
struct physmap_flash_info *info = platform_get_drvdata(dev);
|
|
struct physmap_flash_info *info = platform_get_drvdata(dev);
|
|
int ret = 0;
|
|
int ret = 0;
|
|
|
|
+ int i;
|
|
|
|
|
|
if (info)
|
|
if (info)
|
|
- ret = info->mtd->suspend(info->mtd);
|
|
|
|
|
|
+ for (i = 0; i < MAX_RESOURCES; i++)
|
|
|
|
+ ret |= info->mtd[i].suspend(info->mtd[i]);
|
|
|
|
|
|
return ret;
|
|
return ret;
|
|
}
|
|
}
|
|
@@ -172,16 +212,22 @@ static int physmap_flash_suspend(struct platform_device *dev, pm_message_t state
|
|
static int physmap_flash_resume(struct platform_device *dev)
|
|
static int physmap_flash_resume(struct platform_device *dev)
|
|
{
|
|
{
|
|
struct physmap_flash_info *info = platform_get_drvdata(dev);
|
|
struct physmap_flash_info *info = platform_get_drvdata(dev);
|
|
|
|
+ int i;
|
|
|
|
+
|
|
if (info)
|
|
if (info)
|
|
- info->mtd->resume(info->mtd);
|
|
|
|
|
|
+ for (i = 0; i < MAX_RESOURCES; i++)
|
|
|
|
+ info->mtd[i].resume(info->mtd[i]);
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
static void physmap_flash_shutdown(struct platform_device *dev)
|
|
static void physmap_flash_shutdown(struct platform_device *dev)
|
|
{
|
|
{
|
|
struct physmap_flash_info *info = platform_get_drvdata(dev);
|
|
struct physmap_flash_info *info = platform_get_drvdata(dev);
|
|
- if (info && info->mtd->suspend(info->mtd) == 0)
|
|
|
|
- info->mtd->resume(info->mtd);
|
|
|
|
|
|
+ int i;
|
|
|
|
+
|
|
|
|
+ for (i = 0; i < MAX_RESOURCES; i++)
|
|
|
|
+ if (info && info->mtd[i].suspend(info->mtd[i]) == 0)
|
|
|
|
+ info->mtd[i].resume(info->mtd[i]);
|
|
}
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
|