mtd: physmap_of: Add multiple regions and concatenation support
This patch adds support to handle multiple non-identical chips in one flash device tree node. It also adds concat support to physmap_of. This makes it possible to support e.g. the Intel P30 48F4400 chips which internally consists of 2 non-identical NOR chips on one die. Additionally partitions now can span over multiple chips. To describe such a chip's, multiple "reg" tuples are now supported in one flash device tree node. Here an dts example: flash@f0000000,0 { #address-cells = <1>; #size-cells = <1>; compatible = "cfi-flash"; reg = <0 0x00000000 0x02000000 0 0x02000000 0x02000000>; bank-width = <2>; partition@0 { label = "test-part1"; reg = <0 0x04000000>; }; }; Signed-off-by: Stefan Roese <sr@denx.de> Reviewed-by: Grant Likely <grant.likely@secretlab.ca> Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
This commit is contained in:
parent
43950a605d
commit
143070e746
1 changed files with 144 additions and 57 deletions
|
@ -20,16 +20,23 @@
|
||||||
#include <linux/mtd/mtd.h>
|
#include <linux/mtd/mtd.h>
|
||||||
#include <linux/mtd/map.h>
|
#include <linux/mtd/map.h>
|
||||||
#include <linux/mtd/partitions.h>
|
#include <linux/mtd/partitions.h>
|
||||||
|
#include <linux/mtd/concat.h>
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
#include <linux/of_platform.h>
|
#include <linux/of_platform.h>
|
||||||
|
|
||||||
|
struct of_flash_list {
|
||||||
|
struct mtd_info *mtd;
|
||||||
|
struct map_info map;
|
||||||
|
struct resource *res;
|
||||||
|
};
|
||||||
|
|
||||||
struct of_flash {
|
struct of_flash {
|
||||||
struct mtd_info *mtd;
|
struct mtd_info *cmtd;
|
||||||
struct map_info map;
|
|
||||||
struct resource *res;
|
|
||||||
#ifdef CONFIG_MTD_PARTITIONS
|
#ifdef CONFIG_MTD_PARTITIONS
|
||||||
struct mtd_partition *parts;
|
struct mtd_partition *parts;
|
||||||
#endif
|
#endif
|
||||||
|
int list_size; /* number of elements in of_flash_list */
|
||||||
|
struct of_flash_list list[0];
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifdef CONFIG_MTD_PARTITIONS
|
#ifdef CONFIG_MTD_PARTITIONS
|
||||||
|
@ -88,30 +95,44 @@ static int parse_obsolete_partitions(struct of_device *dev,
|
||||||
static int of_flash_remove(struct of_device *dev)
|
static int of_flash_remove(struct of_device *dev)
|
||||||
{
|
{
|
||||||
struct of_flash *info;
|
struct of_flash *info;
|
||||||
|
int i;
|
||||||
|
|
||||||
info = dev_get_drvdata(&dev->dev);
|
info = dev_get_drvdata(&dev->dev);
|
||||||
if (!info)
|
if (!info)
|
||||||
return 0;
|
return 0;
|
||||||
dev_set_drvdata(&dev->dev, NULL);
|
dev_set_drvdata(&dev->dev, NULL);
|
||||||
|
|
||||||
if (info->mtd) {
|
#ifdef CONFIG_MTD_CONCAT
|
||||||
|
if (info->cmtd != info->list[0].mtd) {
|
||||||
|
del_mtd_device(info->cmtd);
|
||||||
|
mtd_concat_destroy(info->cmtd);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (info->cmtd) {
|
||||||
if (OF_FLASH_PARTS(info)) {
|
if (OF_FLASH_PARTS(info)) {
|
||||||
del_mtd_partitions(info->mtd);
|
del_mtd_partitions(info->cmtd);
|
||||||
kfree(OF_FLASH_PARTS(info));
|
kfree(OF_FLASH_PARTS(info));
|
||||||
} else {
|
} else {
|
||||||
del_mtd_device(info->mtd);
|
del_mtd_device(info->cmtd);
|
||||||
}
|
}
|
||||||
map_destroy(info->mtd);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (info->map.virt)
|
for (i = 0; i < info->list_size; i++) {
|
||||||
iounmap(info->map.virt);
|
if (info->list[i].mtd)
|
||||||
|
map_destroy(info->list[i].mtd);
|
||||||
|
|
||||||
if (info->res) {
|
if (info->list[i].map.virt)
|
||||||
release_resource(info->res);
|
iounmap(info->list[i].map.virt);
|
||||||
kfree(info->res);
|
|
||||||
|
if (info->list[i].res) {
|
||||||
|
release_resource(info->list[i].res);
|
||||||
|
kfree(info->list[i].res);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
kfree(info);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -164,68 +185,130 @@ static int __devinit of_flash_probe(struct of_device *dev,
|
||||||
const char *probe_type = match->data;
|
const char *probe_type = match->data;
|
||||||
const u32 *width;
|
const u32 *width;
|
||||||
int err;
|
int err;
|
||||||
|
int i;
|
||||||
|
int count;
|
||||||
|
const u32 *p;
|
||||||
|
int reg_tuple_size;
|
||||||
|
struct mtd_info **mtd_list = NULL;
|
||||||
|
|
||||||
err = -ENXIO;
|
reg_tuple_size = (of_n_addr_cells(dp) + of_n_size_cells(dp)) * sizeof(u32);
|
||||||
if (of_address_to_resource(dp, 0, &res)) {
|
|
||||||
dev_err(&dev->dev, "Can't get IO address from device tree\n");
|
/*
|
||||||
|
* Get number of "reg" tuples. Scan for MTD devices on area's
|
||||||
|
* described by each "reg" region. This makes it possible (including
|
||||||
|
* the concat support) to support the Intel P30 48F4400 chips which
|
||||||
|
* consists internally of 2 non-identical NOR chips on one die.
|
||||||
|
*/
|
||||||
|
p = of_get_property(dp, "reg", &count);
|
||||||
|
if (count % reg_tuple_size != 0) {
|
||||||
|
dev_err(&dev->dev, "Malformed reg property on %s\n",
|
||||||
|
dev->node->full_name);
|
||||||
|
err = -EINVAL;
|
||||||
goto err_out;
|
goto err_out;
|
||||||
}
|
}
|
||||||
|
count /= reg_tuple_size;
|
||||||
dev_dbg(&dev->dev, "of_flash device: %.8llx-%.8llx\n",
|
|
||||||
(unsigned long long)res.start, (unsigned long long)res.end);
|
|
||||||
|
|
||||||
err = -ENOMEM;
|
err = -ENOMEM;
|
||||||
info = kzalloc(sizeof(*info), GFP_KERNEL);
|
info = kzalloc(sizeof(struct of_flash) +
|
||||||
|
sizeof(struct of_flash_list) * count, GFP_KERNEL);
|
||||||
|
if (!info)
|
||||||
|
goto err_out;
|
||||||
|
|
||||||
|
mtd_list = kzalloc(sizeof(struct mtd_info) * count, GFP_KERNEL);
|
||||||
if (!info)
|
if (!info)
|
||||||
goto err_out;
|
goto err_out;
|
||||||
|
|
||||||
dev_set_drvdata(&dev->dev, info);
|
dev_set_drvdata(&dev->dev, info);
|
||||||
|
|
||||||
err = -EBUSY;
|
for (i = 0; i < count; i++) {
|
||||||
info->res = request_mem_region(res.start, res.end - res.start + 1,
|
err = -ENXIO;
|
||||||
dev_name(&dev->dev));
|
if (of_address_to_resource(dp, i, &res)) {
|
||||||
if (!info->res)
|
dev_err(&dev->dev, "Can't get IO address from device"
|
||||||
goto err_out;
|
" tree\n");
|
||||||
|
goto err_out;
|
||||||
|
}
|
||||||
|
|
||||||
err = -ENXIO;
|
dev_dbg(&dev->dev, "of_flash device: %.8llx-%.8llx\n",
|
||||||
width = of_get_property(dp, "bank-width", NULL);
|
(unsigned long long)res.start,
|
||||||
if (!width) {
|
(unsigned long long)res.end);
|
||||||
dev_err(&dev->dev, "Can't get bank width from device tree\n");
|
|
||||||
goto err_out;
|
err = -EBUSY;
|
||||||
|
info->list[i].res = request_mem_region(res.start, res.end -
|
||||||
|
res.start + 1,
|
||||||
|
dev_name(&dev->dev));
|
||||||
|
if (!info->list[i].res)
|
||||||
|
goto err_out;
|
||||||
|
|
||||||
|
err = -ENXIO;
|
||||||
|
width = of_get_property(dp, "bank-width", NULL);
|
||||||
|
if (!width) {
|
||||||
|
dev_err(&dev->dev, "Can't get bank width from device"
|
||||||
|
" tree\n");
|
||||||
|
goto err_out;
|
||||||
|
}
|
||||||
|
|
||||||
|
info->list[i].map.name = dev_name(&dev->dev);
|
||||||
|
info->list[i].map.phys = res.start;
|
||||||
|
info->list[i].map.size = res.end - res.start + 1;
|
||||||
|
info->list[i].map.bankwidth = *width;
|
||||||
|
|
||||||
|
err = -ENOMEM;
|
||||||
|
info->list[i].map.virt = ioremap(info->list[i].map.phys,
|
||||||
|
info->list[i].map.size);
|
||||||
|
if (!info->list[i].map.virt) {
|
||||||
|
dev_err(&dev->dev, "Failed to ioremap() flash"
|
||||||
|
" region\n");
|
||||||
|
goto err_out;
|
||||||
|
}
|
||||||
|
|
||||||
|
simple_map_init(&info->list[i].map);
|
||||||
|
|
||||||
|
if (probe_type) {
|
||||||
|
info->list[i].mtd = do_map_probe(probe_type,
|
||||||
|
&info->list[i].map);
|
||||||
|
} else {
|
||||||
|
info->list[i].mtd = obsolete_probe(dev,
|
||||||
|
&info->list[i].map);
|
||||||
|
}
|
||||||
|
mtd_list[i] = info->list[i].mtd;
|
||||||
|
|
||||||
|
err = -ENXIO;
|
||||||
|
if (!info->list[i].mtd) {
|
||||||
|
dev_err(&dev->dev, "do_map_probe() failed\n");
|
||||||
|
goto err_out;
|
||||||
|
} else {
|
||||||
|
info->list_size++;
|
||||||
|
}
|
||||||
|
info->list[i].mtd->owner = THIS_MODULE;
|
||||||
|
info->list[i].mtd->dev.parent = &dev->dev;
|
||||||
}
|
}
|
||||||
|
|
||||||
info->map.name = dev_name(&dev->dev);
|
err = 0;
|
||||||
info->map.phys = res.start;
|
if (info->list_size == 1) {
|
||||||
info->map.size = res.end - res.start + 1;
|
info->cmtd = info->list[0].mtd;
|
||||||
info->map.bankwidth = *width;
|
} else if (info->list_size > 1) {
|
||||||
|
/*
|
||||||
err = -ENOMEM;
|
* We detected multiple devices. Concatenate them together.
|
||||||
info->map.virt = ioremap(info->map.phys, info->map.size);
|
*/
|
||||||
if (!info->map.virt) {
|
#ifdef CONFIG_MTD_CONCAT
|
||||||
dev_err(&dev->dev, "Failed to ioremap() flash region\n");
|
info->cmtd = mtd_concat_create(mtd_list, info->list_size,
|
||||||
goto err_out;
|
dev_name(&dev->dev));
|
||||||
|
if (info->cmtd == NULL)
|
||||||
|
err = -ENXIO;
|
||||||
|
#else
|
||||||
|
printk(KERN_ERR "physmap_of: multiple devices "
|
||||||
|
"found but MTD concat support disabled.\n");
|
||||||
|
err = -ENXIO;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
if (err)
|
||||||
simple_map_init(&info->map);
|
|
||||||
|
|
||||||
if (probe_type)
|
|
||||||
info->mtd = do_map_probe(probe_type, &info->map);
|
|
||||||
else
|
|
||||||
info->mtd = obsolete_probe(dev, &info->map);
|
|
||||||
|
|
||||||
err = -ENXIO;
|
|
||||||
if (!info->mtd) {
|
|
||||||
dev_err(&dev->dev, "do_map_probe() failed\n");
|
|
||||||
goto err_out;
|
goto err_out;
|
||||||
}
|
|
||||||
info->mtd->owner = THIS_MODULE;
|
|
||||||
info->mtd->dev.parent = &dev->dev;
|
|
||||||
|
|
||||||
#ifdef CONFIG_MTD_PARTITIONS
|
#ifdef CONFIG_MTD_PARTITIONS
|
||||||
/* First look for RedBoot table or partitions on the command
|
/* First look for RedBoot table or partitions on the command
|
||||||
* line, these take precedence over device tree information */
|
* line, these take precedence over device tree information */
|
||||||
err = parse_mtd_partitions(info->mtd, part_probe_types,
|
err = parse_mtd_partitions(info->cmtd, part_probe_types,
|
||||||
&info->parts, 0);
|
&info->parts, 0);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
|
@ -244,15 +327,19 @@ static int __devinit of_flash_probe(struct of_device *dev,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (err > 0)
|
if (err > 0)
|
||||||
add_mtd_partitions(info->mtd, info->parts, err);
|
add_mtd_partitions(info->cmtd, info->parts, err);
|
||||||
else
|
else
|
||||||
#endif
|
#endif
|
||||||
add_mtd_device(info->mtd);
|
add_mtd_device(info->cmtd);
|
||||||
|
|
||||||
|
kfree(mtd_list);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
err_out:
|
err_out:
|
||||||
|
kfree(mtd_list);
|
||||||
of_flash_remove(dev);
|
of_flash_remove(dev);
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue