[PATCH v2] mtd: map: new driver for NXP IFC

Boris Brezillon boris.brezillon at free-electrons.com
Wed Aug 30 12:34:50 PDT 2017


Hi,

On Tue, 29 Aug 2017 14:47:27 -0500
Matt Weber <matthew.weber at rockwellcollins.com> wrote:

> From: Sanjay Tandel <sanjay.tandel at rockwellcollins.com>
> 
> This patch adds map driver for parallel flash chips interfaced over
> a NXP Integrated Flash Controller (IFC). This driver allows either
> 8-bit or 16-bit accesses, depending on bank-width, to parallel flash
> chips(like Everspin MR0A16A), which are physically mapped to CPU's
> memory space. For unaligned accesses, it performs read-modify-write
> operations to keep access size same as bank-width.
> 

Did you consider re-using the physmap driver [1] and adjust it to your
needs like the gemini [2] or versatile [3] drivers do? If you did, what
prevents you from using this approach?

Regards,

Boris

[1]http://elixir.free-electrons.com/linux/v4.13-rc7/source/drivers/mtd/maps/physmap_of_core.c
[2]http://elixir.free-electrons.com/linux/v4.13-rc7/source/drivers/mtd/maps/physmap_of_gemini.c
[3]http://elixir.free-electrons.com/linux/v4.13-rc7/source/drivers/mtd/maps/physmap_of_versatile.c

> Signed-off-by: Sanjay Tandel <sanjay.tandel at rockwellcollins.com>
> Signed-off-by: Matt Weber <matthew.weber at rockwellcollins.com>
> ---
> 
> Changes
> v1 -> v2
>  - Refactored driver to be custom for the IFC bus controllder
>   (Suggested by Boris)
>  - Updated patch name
>  - Cleaned up description to be specific about issue and behavior
>  - Version 1 - https://patchwork.ozlabs.org/patch/797787/
> ---
>  Documentation/devicetree/bindings/mtd/ifc-mram.txt |  34 ++
>  drivers/mtd/maps/Kconfig                           |  13 +
>  drivers/mtd/maps/Makefile                          |   1 +
>  drivers/mtd/maps/ifc_mram.c                        | 343 +++++++++++++++++++++
>  4 files changed, 391 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/mtd/ifc-mram.txt
>  create mode 100644 drivers/mtd/maps/ifc_mram.c
> 
> diff --git a/Documentation/devicetree/bindings/mtd/ifc-mram.txt b/Documentation/devicetree/bindings/mtd/ifc-mram.txt
> new file mode 100644
> index 0000000..c5c3210
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/mtd/ifc-mram.txt
> @@ -0,0 +1,34 @@
> +Integrated Flash Controller based physically-mapped parallel MRAM,
> +NOR flash, MTD-RAM (NVRAM...)
> +
> + - compatible : should contain the specific model of mtd chip(s)
> +   used, if known, followed by "ifc-mram".
> + - reg : Address range(s) of the mtd chip(s)
> +   It's possible to (optionally) define multiple "reg" tuples so that
> +   non-identical chips can be described in one node.
> + - bank-width : Width (in bytes) of the bank.  Equal to the
> +   device width times the number of interleaved chips.
> + - device-width : (optional) Width of a single mtd chip.  If
> +   omitted, assumed to be equal to 'bank-width'.
> + - #address-cells, #size-cells : Must be present if the device has
> +   sub-nodes representing partitions (see below).  In this case
> +   both #address-cells and #size-cells must be equal to 1.
> + - linux,mtd-name: allow to specify the mtd name.
> +
> +The device tree may optionally contain sub-nodes describing partitions of the
> +address space. See partition.txt for more detail.
> +
> +Example:
> +
> +        mram at 1,0 {
> +                #address-cells = <1>;
> +                #size-cells = <1>;
> +                compatible = "everspin,mram", "ifc-mram";
> +                reg = <0x1 0x0 0x10000 0x1 0x10000 0x10000>;
> +                bank-width = <2>;
> +
> +                partition at 0 {
> +                        reg = <0 0x00020000>;
> +                        label = "MRAM Data 0";
> +                };
> +        };
> diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig
> index 542fdf8..95bd44e 100644
> --- a/drivers/mtd/maps/Kconfig
> +++ b/drivers/mtd/maps/Kconfig
> @@ -419,4 +419,17 @@ config MTD_LATCH_ADDR
>  
>            If compiled as a module, it will be called latch-addr-flash.
>  
> +config MTD_IFC_MRAM
> +	tristate "Map driver for Integrated Flash Controller"
> +	depends on MTD_COMPLEX_MAPPINGS
> +	help
> +	  Map driver for chips connected parallely to Integrated Flash
> +	  Controller. This driver allows either 8-bit or 16-bit accesses,
> +	  depending on bank-width, to parallel flash chips, which are
> +	  physically mapped to CPU's memory space. For unaligned accesses,
> +	  it does read-modify-write.
> +	  Example: Everspin MR0A16A.
> +
> +	  If compiled as a module, it will be called ifc-mram.
> +
>  endmenu
> diff --git a/drivers/mtd/maps/Makefile b/drivers/mtd/maps/Makefile
> index 5a09a72..0ab793e 100644
> --- a/drivers/mtd/maps/Makefile
> +++ b/drivers/mtd/maps/Makefile
> @@ -47,3 +47,4 @@ obj-$(CONFIG_MTD_VMU)		+= vmu-flash.o
>  obj-$(CONFIG_MTD_GPIO_ADDR)	+= gpio-addr-flash.o
>  obj-$(CONFIG_MTD_LATCH_ADDR)	+= latch-addr-flash.o
>  obj-$(CONFIG_MTD_LANTIQ)	+= lantiq-flash.o
> +obj-$(CONFIG_MTD_IFC_MRAM)	+= ifc_mram.o
> diff --git a/drivers/mtd/maps/ifc_mram.c b/drivers/mtd/maps/ifc_mram.c
> new file mode 100644
> index 0000000..1341e0a
> --- /dev/null
> +++ b/drivers/mtd/maps/ifc_mram.c
> @@ -0,0 +1,343 @@
> +/*
> + * Integrated Flash Controlller Map Driver
> + *
> + * Copyright 2017 Rockwell Collins
> + *
> + * Aug 18 2017  Sanjay Tandel <sanjay.tandel at rockwellcollins.com>
> + *
> + * Based on:
> + * Flash mappings described by the OF (or flattened) device tree
> + *
> + * Copyright (C) 2006 MontaVista Software Inc.
> + * Author: Vitaly Wool <vwool at ru.mvista.com>
> + *
> + * Revised to handle newer style flash binding by:
> + *   Copyright (C) 2007 David Gibson, IBM Corporation.
> + *
> + */
> +
> +#include <linux/module.h>
> +#include <linux/types.h>
> +#include <linux/device.h>
> +#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_address.h>
> +#include <linux/of_platform.h>
> +#include <linux/slab.h>
> +
> +static const struct of_device_id ifc_mram_match[] = {
> +	{
> +		.compatible	= "ifc-mram",
> +	},
> +	{}
> +};
> +MODULE_DEVICE_TABLE(of, ifc_mram_match);
> +struct ifc_mram_list {
> +	struct mtd_info *mtd;
> +	struct map_info map;
> +	struct resource *res;
> +};
> +
> +struct ifc_mram {
> +	struct mtd_info		*cmtd;
> +	int list_size; /* number of elements in ifc_mram_list */
> +	struct ifc_mram_list	list[0];
> +};
> +
> +
> +static void ifc_mram_copy_from8(void *to, void __iomem *from, size_t count)
> +{
> +	u8 *t = to;
> +
> +	while (count > 0) {
> +		*t = (u8)readb_relaxed(from);
> +		t++;
> +		from++;
> +		count--;
> +	}
> +}
> +
> +static void ifc_mram_copy_from16(void *to, void __iomem *from, size_t count)
> +{
> +	u8 *t = to;
> +
> +	if (!(IS_ALIGNED((unsigned long)from, 2))) {
> +		from = (void __iomem *)ALIGN((unsigned long)from, 2) - 2;
> +		*(u8 *)t = (u8)((cpu_to_le16(readw_relaxed(from)) & 0xff00)
> +									>> 8);
> +		count--;
> +		t++;
> +		from += 2;
> +	}
> +	while (count >= 2) {
> +		*(u16 *)t = cpu_to_le16(readw_relaxed(from));
> +		count -= 2;
> +		t += 2;
> +		from += 2;
> +	};
> +	while (count > 0) {
> +		*(u8 *)t = (u8)(cpu_to_le16(readw_relaxed(from)) & 0x00ff);
> +		count--;
> +		t++;
> +		from++;
> +	}
> +}
> +
> +static void ifc_mram_copy_from(struct map_info *map, void *to,
> +					unsigned long from, ssize_t len)
> +{
> +	if (map->cached)
> +		memcpy(to, (char *)map->cached + from, len);
> +	else if (map_bankwidth_is_1(map))
> +		ifc_mram_copy_from8(to, map->virt + from, len);
> +	else if (map_bankwidth_is_2(map))
> +		ifc_mram_copy_from16(to, map->virt + from, len);
> +	else
> +		memcpy_fromio(to, map->virt + from, len);
> +}
> +
> +static void ifc_mram_copy_to8(void __iomem *to, const void *from, size_t count)
> +{
> +	const unsigned char *f = from;
> +
> +	while (count > 0) {
> +		writeb_relaxed(*f, to);
> +		count--;
> +		to++;
> +		f++;
> +	}
> +}
> +
> +static void ifc_mram_copy_to16(void __iomem *to, const void *from, size_t count)
> +{
> +	const unsigned char *f = from;
> +	u16 d;
> +
> +	if (!(IS_ALIGNED((unsigned long)to, 2))) {
> +		to = (void __iomem *)ALIGN((unsigned long)to, 2) - 2;
> +		d = (cpu_to_le16(readw_relaxed(to)) & 0x00ff)
> +					| ((u16)(*(const u8 *)f) << 8);
> +		writew_relaxed(le16_to_cpu(d), to);
> +		count--;
> +		to += 2;
> +		f++;
> +	}
> +	while (count >= 2) {
> +		writew_relaxed(le16_to_cpu(*(const u16 *)f), to);
> +		count -= 2;
> +		to += 2;
> +		f += 2;
> +	};
> +	while (count > 0) {
> +		d = (cpu_to_le16(readw_relaxed(to)) & 0xff00)
> +					| (u16)(*(const u8 *)f);
> +		writew_relaxed(le16_to_cpu(d), to);
> +		count--;
> +		to++;
> +		f++;
> +	}
> +}
> +
> +static void ifc_mram_copy_to(struct map_info *map, unsigned long to,
> +					const void *from, ssize_t len)
> +{
> +	if (map_bankwidth_is_1(map))
> +		ifc_mram_copy_to8(map->virt + to, from, len);
> +	else if (map_bankwidth_is_2(map))
> +		ifc_mram_copy_to16(map->virt + to, from, len);
> +	else
> +		memcpy_toio(map->virt + to, from, len);
> +}
> +static int ifc_mram_remove(struct platform_device *dev)
> +{
> +	struct ifc_mram *info;
> +	int i;
> +
> +	info = dev_get_drvdata(&dev->dev);
> +	if (!info)
> +		return 0;
> +	dev_set_drvdata(&dev->dev, NULL);
> +
> +	if (info->cmtd) {
> +		mtd_device_unregister(info->cmtd);
> +		if (info->cmtd != info->list[0].mtd)
> +			mtd_concat_destroy(info->cmtd);
> +	}
> +
> +	for (i = 0; i < info->list_size; i++) {
> +		if (info->list[i].mtd)
> +			map_destroy(info->list[i].mtd);
> +
> +		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);
> +		}
> +	}
> +	return 0;
> +}
> +
> +static int ifc_mram_probe(struct platform_device *dev)
> +{
> +	const struct of_device_id *match;
> +	struct device_node *dp = dev->dev.of_node;
> +	struct resource res;
> +	struct ifc_mram *info;
> +	const __be32 *width;
> +	int err;
> +	int i;
> +	int count;
> +	const __be32 *p;
> +	int reg_tuple_size;
> +	struct mtd_info **mtd_list = NULL;
> +	resource_size_t res_size;
> +	struct mtd_part_parser_data ppdata;
> +	bool map_indirect;
> +	const char *mtd_name = NULL;
> +
> +	match = of_match_device(ifc_mram_match, &dev->dev);
> +	if (!match) {
> +		pr_info("%s: compatible string not matched\n", __func__);
> +		return -EINVAL;
> +	}
> +	reg_tuple_size =
> +		(of_n_addr_cells(dp) + of_n_size_cells(dp)) * sizeof(u32);
> +
> +	of_property_read_string(dp, "linux,mtd-name", &mtd_name);
> +
> +	p = of_get_property(dp, "reg", &count);
> +	if (count % reg_tuple_size != 0) {
> +		dev_err(&dev->dev, "Malformed reg property on %s\n",
> +				dev->dev.of_node->full_name);
> +		err = -EINVAL;
> +		goto err_flash_remove;
> +	}
> +	count /= reg_tuple_size;
> +
> +	err = -ENOMEM;
> +	info = devm_kzalloc(&dev->dev,
> +			    sizeof(struct ifc_mram) +
> +			    sizeof(struct ifc_mram_list) * count, GFP_KERNEL);
> +	if (!info)
> +		goto err_flash_remove;
> +
> +	dev_set_drvdata(&dev->dev, info);
> +
> +	mtd_list = kcalloc(count, sizeof(*mtd_list), GFP_KERNEL);
> +	if (!mtd_list)
> +		goto err_flash_remove;
> +
> +	for (i = 0; i < count; i++) {
> +		err = -ENXIO;
> +		if (of_address_to_resource(dp, i, &res)) {
> +			/*
> +			 * Continue with next register tuple if this
> +			 * one is not mappable
> +			 */
> +			continue;
> +		}
> +
> +		dev_dbg(&dev->dev, "ifc_mram device: %pR\n", &res);
> +
> +		err = -EBUSY;
> +		res_size = resource_size(&res);
> +		info->list[i].res = request_mem_region(res.start, res_size,
> +						       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 = mtd_name ?: dev_name(&dev->dev);
> +		info->list[i].map.phys = res.start;
> +		info->list[i].map.size = res_size;
> +		info->list[i].map.bankwidth = be32_to_cpup(width);
> +		info->list[i].map.device_node = dp;
> +
> +		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);
> +#ifdef CONFIG_MTD_COMPLEX_MAPPINGS
> +		info->list[i].map.copy_from = ifc_mram_copy_from;
> +		info->list[i].map.copy_to = ifc_mram_copy_to;
> +#endif
> +
> +
> +		info->list[i].mtd = do_map_probe("map_ram",
> +							 &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;
> +	}
> +
> +	err = 0;
> +	info->cmtd = NULL;
> +	if (info->list_size == 1) {
> +		info->cmtd = info->list[0].mtd;
> +	} else if (info->list_size > 1) {
> +		/*
> +		 * We detected multiple devices. Concatenate them together.
> +		 */
> +		info->cmtd = mtd_concat_create(mtd_list, info->list_size,
> +					       dev_name(&dev->dev));
> +	}
> +	if (info->cmtd == NULL)
> +		err = -ENXIO;
> +
> +	if (err)
> +		goto err_out;
> +
> +	ppdata.of_node = dp;
> +	mtd_device_parse_register(info->cmtd, NULL, &ppdata, NULL, 0);
> +	kfree(mtd_list);
> +	return 0;
> +
> +err_out:
> +	kfree(mtd_list);
> +err_flash_remove:
> +	ifc_mram_remove(dev);
> +
> +	return err;
> +}
> +
> +
> +static struct platform_driver ifc_mram_driver = {
> +	.driver = {
> +		.name = "ifc-mram",
> +		.of_match_table = ifc_mram_match,
> +	},
> +	.probe		= ifc_mram_probe,
> +	.remove		= ifc_mram_remove,
> +};
> +
> +module_platform_driver(ifc_mram_driver);
> +
> +MODULE_LICENSE("GPL v2");
> +MODULE_AUTHOR("Sanjay Tandel");
> +MODULE_DESCRIPTION("IFC MRAM Map Driver");




More information about the linux-mtd mailing list