[PATCH 1/3 v3] mtd: physmap_of: Add multiple regions and concatenation support

Grant Likely grant.likely at secretlab.ca
Thu Apr 16 09:26:53 EDT 2009


On Thu, Apr 16, 2009 at 6:10 AM, Stefan Roese <sr at denx.de> wrote:
> 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 at f0000000,0 {
>                #address-cells = <1>;
>                #size-cells = <1>;
>                compatible = "cfi-flash";
>                reg = <0 0x00000000 0x02000000
>                       0 0x02000000 0x02000000>;
>                bank-width = <2>;
>                partition at 0 {
>                        label = "test-part1";
>                        reg = <0 0x04000000>;
>                };
>        };
>
> Signed-off-by: Stefan Roese <sr at denx.de>
> Reviewd-by: Grant Likely <grant.likely at secretlab.ca>

Yup, still looks good to me.  What boards has this been tested on?

g.

> ---
> Changes in ver3:
> - s/4/sizeof(u32)
>
> Changes in ver2 (as suggested by Grant Likely):
> - Removed MAX_RESOURCES introduced in ver1. Now we don't have a hard limit
>  for "reg" tuples anymore.
> - Used of_n_addr_cells() and of_n_size_cells() to determine size of each tuple.
>
>  drivers/mtd/maps/physmap_of.c |  199 +++++++++++++++++++++++++++++------------
>  1 files changed, 143 insertions(+), 56 deletions(-)
>
> diff --git a/drivers/mtd/maps/physmap_of.c b/drivers/mtd/maps/physmap_of.c
> index c83a60f..39d357b 100644
> --- a/drivers/mtd/maps/physmap_of.c
> +++ b/drivers/mtd/maps/physmap_of.c
> @@ -20,16 +20,23 @@
>  #include <linux/mtd/mtd.h>
>  #include <linux/mtd/map.h>
>  #include <linux/mtd/partitions.h>
> +#include <linux/mtd/concat.h>
>  #include <linux/of.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 mtd_info         *mtd;
> -       struct map_info         map;
> -       struct resource         *res;
> +       struct mtd_info         *cmtd;
>  #ifdef CONFIG_MTD_PARTITIONS
>        struct mtd_partition    *parts;
>  #endif
> +       int list_size; /* number of elements in of_flash_list */
> +       struct of_flash_list    list[0];
>  };
>
>  #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)
>  {
>        struct of_flash *info;
> +       int i;
>
>        info = dev_get_drvdata(&dev->dev);
>        if (!info)
>                return 0;
>        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)) {
> -                       del_mtd_partitions(info->mtd);
> +                       del_mtd_partitions(info->cmtd);
>                        kfree(OF_FLASH_PARTS(info));
>                } else {
> -                       del_mtd_device(info->mtd);
> +                       del_mtd_device(info->cmtd);
>                }
> -               map_destroy(info->mtd);
>        }
>
> -       if (info->map.virt)
> -               iounmap(info->map.virt);
> +       for (i = 0; i < info->list_size; i++) {
> +               if (info->list[i].mtd)
> +                       map_destroy(info->list[i].mtd);
>
> -       if (info->res) {
> -               release_resource(info->res);
> -               kfree(info->res);
> +               if (info->list[i].map.virt)
> +                       iounmap(info->list[i].map.virt);
> +
> +               if (info->list[i].res) {
> +                       release_resource(info->list[i].res);
> +                       kfree(info->list[i].res);
> +               }
>        }
>
> +       kfree(info);
> +
>        return 0;
>  }
>
> @@ -164,68 +185,130 @@ static int __devinit of_flash_probe(struct of_device *dev,
>        const char *probe_type = match->data;
>        const u32 *width;
>        int err;
> -
> -       err = -ENXIO;
> -       if (of_address_to_resource(dp, 0, &res)) {
> -               dev_err(&dev->dev, "Can't get IO address from device tree\n");
> +       int i;
> +       int count;
> +       const u32 *p;
> +       int reg_tuple_size;
> +       struct mtd_info **mtd_list = NULL;
> +
> +       reg_tuple_size = (of_n_addr_cells(dp) + of_n_size_cells(dp)) * sizeof(u32);
> +
> +       /*
> +        * 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;
>        }
> -
> -               dev_dbg(&dev->dev, "of_flash device: %.8llx-%.8llx\n",
> -               (unsigned long long)res.start, (unsigned long long)res.end);
> +       count /= reg_tuple_size;
>
>        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)
>                goto err_out;
>
>        dev_set_drvdata(&dev->dev, info);
>
> -       err = -EBUSY;
> -       info->res = request_mem_region(res.start, res.end - res.start + 1,
> -                                      dev_name(&dev->dev));
> -       if (!info->res)
> -               goto err_out;
> +       for (i = 0; i < count; i++) {
> +               err = -ENXIO;
> +               if (of_address_to_resource(dp, i, &res)) {
> +                       dev_err(&dev->dev, "Can't get IO address from device"
> +                               " tree\n");
> +                       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;
> -       }
> +               dev_dbg(&dev->dev, "of_flash device: %.8llx-%.8llx\n",
> +                       (unsigned long long)res.start,
> +                       (unsigned long long)res.end);
> +
> +               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->map.name = dev_name(&dev->dev);
> -       info->map.phys = res.start;
> -       info->map.size = res.end - res.start + 1;
> -       info->map.bankwidth = *width;
> +               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;
> +               }
>
> -       err = -ENOMEM;
> -       info->map.virt = ioremap(info->map.phys, info->map.size);
> -       if (!info->map.virt) {
> -               dev_err(&dev->dev, "Failed to ioremap() flash region\n");
> -               goto err_out;
> -       }
> +               simple_map_init(&info->list[i].map);
>
> -       simple_map_init(&info->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;
>
> -       if (probe_type)
> -               info->mtd = do_map_probe(probe_type, &info->map);
> -       else
> -               info->mtd = obsolete_probe(dev, &info->map);
> +               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;
> +       }
>
> -       err = -ENXIO;
> -       if (!info->mtd) {
> -               dev_err(&dev->dev, "do_map_probe() failed\n");
> -               goto err_out;
> +       err = 0;
> +       if (info->list_size == 1) {
> +               info->cmtd = info->list[0].mtd;
> +       } else if (info->list_size > 1) {
> +               /*
> +                * We detected multiple devices. Concatenate them together.
> +                */
> +#ifdef CONFIG_MTD_CONCAT
> +               info->cmtd = mtd_concat_create(mtd_list, info->list_size,
> +                                              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
>        }
> -       info->mtd->owner = THIS_MODULE;
> -       info->mtd->dev.parent = &dev->dev;
> +       if (err)
> +               goto err_out;
>
>  #ifdef CONFIG_MTD_PARTITIONS
>        /* First look for RedBoot table or partitions on the command
>         * line, these take precedence over device tree information */
> -       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)
>                return err;
>
> @@ -244,15 +327,19 @@ static int __devinit of_flash_probe(struct of_device *dev,
>        }
>
>        if (err > 0)
> -               add_mtd_partitions(info->mtd, info->parts, err);
> +               add_mtd_partitions(info->cmtd, info->parts, err);
>        else
>  #endif
> -               add_mtd_device(info->mtd);
> +               add_mtd_device(info->cmtd);
> +
> +       kfree(mtd_list);
>
>        return 0;
>
>  err_out:
> +       kfree(mtd_list);
>        of_flash_remove(dev);
> +
>        return err;
>  }
>
> --
> 1.6.2.3
>
>



-- 
Grant Likely, B.Sc., P.Eng.
Secret Lab Technologies Ltd.



More information about the linux-mtd mailing list