diff mbox series

[v7,10/10] mtd: maps: gpio-addr-flash: Add support for device-tree devices

Message ID 20181004130819.9647-1-ricardo.ribalda@gmail.com
State Changes Requested
Headers show
Series None | expand

Commit Message

Ricardo Ribalda Delgado Oct. 4, 2018, 1:08 p.m. UTC
Allow creating gpio-addr-flash via device-tree and not just via platform
data.

Mimic what physmap_of_versatile and physmap_of_gemini does to reduce
code duplicity.

Signed-off-by: Ricardo Ribalda Delgado <ricardo.ribalda@gmail.com>
---
 drivers/mtd/maps/Kconfig           |  8 +++
 drivers/mtd/maps/Makefile          |  3 +-
 drivers/mtd/maps/gpio-addr-flash.c | 91 ++++++++++++++++++------------
 drivers/mtd/maps/gpio-addr-flash.h | 31 ++++++++++
 drivers/mtd/maps/physmap_of_core.c |  5 ++
 drivers/mtd/maps/physmap_of_gpio.c | 24 ++++++++
 6 files changed, 125 insertions(+), 37 deletions(-)
 create mode 100644 drivers/mtd/maps/gpio-addr-flash.h
 create mode 100644 drivers/mtd/maps/physmap_of_gpio.c
diff mbox series

Patch

diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig
index afb36bff13a7..427143d42168 100644
--- a/drivers/mtd/maps/Kconfig
+++ b/drivers/mtd/maps/Kconfig
@@ -94,6 +94,14 @@  config MTD_PHYSMAP_OF_GEMINI
 	  platforms, some detection and setting up parallel mode on the
 	  external interface.
 
+config MTD_PHYSMAP_OF_GPIO
+	bool "GPIO-assisted OF-based physical memory map handling"
+	depends on MTD_PHYSMAP_OF
+	depends on MTD_GPIO_ADDR
+	help
+	  This provides some extra DT physmap parsing for flashes that are
+	  partially physically addressed and assisted by GPIOs.
+
 config MTD_PMC_MSP_EVM
 	tristate "CFI Flash device mapped on PMC-Sierra MSP"
 	depends on PMC_MSP && MTD_CFI
diff --git a/drivers/mtd/maps/Makefile b/drivers/mtd/maps/Makefile
index 51acf1fec19b..c232ccf05bee 100644
--- a/drivers/mtd/maps/Makefile
+++ b/drivers/mtd/maps/Makefile
@@ -21,6 +21,7 @@  obj-$(CONFIG_MTD_PHYSMAP)	+= physmap.o
 physmap_of-objs-y		+= physmap_of_core.o
 physmap_of-objs-$(CONFIG_MTD_PHYSMAP_OF_VERSATILE) += physmap_of_versatile.o
 physmap_of-objs-$(CONFIG_MTD_PHYSMAP_OF_GEMINI) += physmap_of_gemini.o
+physmap_of-objs-$(CONFIG_MTD_PHYSMAP_OF_GPIO) += physmap_of_gpio.o
 physmap_of-objs			:= $(physmap_of-objs-y)
 obj-$(CONFIG_MTD_PHYSMAP_OF)	+= physmap_of.o
 obj-$(CONFIG_MTD_PISMO)		+= pismo.o
@@ -44,6 +45,6 @@  obj-$(CONFIG_MTD_PLATRAM)	+= plat-ram.o
 obj-$(CONFIG_MTD_INTEL_VR_NOR)	+= intel_vr_nor.o
 obj-$(CONFIG_MTD_RBTX4939)	+= rbtx4939-flash.o
 obj-$(CONFIG_MTD_VMU)		+= vmu-flash.o
-obj-$(CONFIG_MTD_GPIO_ADDR)	+= gpio-addr-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
diff --git a/drivers/mtd/maps/gpio-addr-flash.c b/drivers/mtd/maps/gpio-addr-flash.c
index a20e85aa770e..7837fc7b8de8 100644
--- a/drivers/mtd/maps/gpio-addr-flash.c
+++ b/drivers/mtd/maps/gpio-addr-flash.c
@@ -25,25 +25,25 @@ 
 #include <linux/platform_device.h>
 #include <linux/slab.h>
 #include <linux/types.h>
+#include "gpio-addr-flash.h"
 
 #define win_mask(x) ((BIT(x)) - 1)
 
 #define DRIVER_NAME "gpio-addr-flash"
 
+#define gf_map_info_to_state(mi) ((struct async_state *)(mi)->map_priv_1)
+
 /**
- * struct async_state - keep GPIO flash state
+ * struct async_state_pdev - Async state platform device
  *	@mtd:         MTD state for this mapping
  *	@map:         MTD map state for this flash
  *	@gpios:       Struct containing the array of GPIO descriptors
- *	@gpio_values: cached GPIO values
- *	@win_order:   dedicated memory size (if no GPIOs)
+ *	@state:       GPIO flash state
  */
-struct async_state {
+struct async_state_pdev {
 	struct mtd_info *mtd;
 	struct map_info map;
-	struct gpio_descs *gpios;
-	unsigned int gpio_values;
-	unsigned int win_order;
+	struct async_state state;
 };
 #define gf_map_info_to_state(mi) ((struct async_state *)(mi)->map_priv_1)
 
@@ -174,6 +174,31 @@  static void gf_copy_to(struct map_info *map, unsigned long to,
 static const char * const part_probe_types[] = {
 	"cmdlinepart", "RedBoot", NULL };
 
+int gpio_flash_probe_common(struct device *dev, struct async_state *state,
+			    struct map_info *map)
+{
+	if (!is_power_of_2(map->size)) {
+		dev_err(dev, "Window size must be aligned\n");
+		return -EIO;
+	}
+
+	state->gpios = devm_gpiod_get_array(dev, "addr", GPIOD_OUT_LOW);
+	if (IS_ERR(state->gpios))
+		return PTR_ERR(state->gpios);
+
+	state->win_order  = get_bitmask_order(map->size) - 1;
+	map->read       = gf_read;
+	map->copy_from  = gf_copy_from;
+	map->write      = gf_write;
+	map->copy_to    = gf_copy_to;
+	map->size       = BIT(state->win_order + state->gpios->ndescs);
+	map->phys	= NO_XIP;
+	map->map_priv_1 = (unsigned long)state;
+
+	return 0;
+}
+EXPORT_SYMBOL(gpio_flash_probe_common);
+
 /**
  * gpio_flash_probe() - setup a mapping for a GPIO assisted flash
  *	@pdev: platform device
@@ -210,7 +235,8 @@  static int gpio_flash_probe(struct platform_device *pdev)
 {
 	struct physmap_flash_data *pdata;
 	struct resource *memory;
-	struct async_state *state;
+	struct async_state_pdev *state_pdev;
+	int ret;
 
 	pdata = dev_get_platdata(&pdev->dev);
 	memory = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -218,40 +244,33 @@  static int gpio_flash_probe(struct platform_device *pdev)
 	if (!memory)
 		return -EINVAL;
 
-	state = devm_kzalloc(&pdev->dev, sizeof(*state), GFP_KERNEL);
-	if (!state)
+	state_pdev = devm_kzalloc(&pdev->dev, sizeof(*state_pdev), GFP_KERNEL);
+	if (!state_pdev)
 		return -ENOMEM;
 
-	state->gpios = devm_gpiod_get_array(&pdev->dev, "addr", GPIOD_OUT_LOW);
-	if (IS_ERR(state->gpios))
-		return PTR_ERR(state->gpios);
-
-	state->win_order      = get_bitmask_order(resource_size(memory)) - 1;
+	state_pdev->map.virt = devm_ioremap_resource(&pdev->dev, memory);
+	if (IS_ERR(state_pdev->map.virt))
+		return PTR_ERR(state_pdev->map.virt);
 
-	state->map.name       = DRIVER_NAME;
-	state->map.read       = gf_read;
-	state->map.copy_from  = gf_copy_from;
-	state->map.write      = gf_write;
-	state->map.copy_to    = gf_copy_to;
-	state->map.bankwidth  = pdata->width;
-	state->map.size       = BIT(state->win_order + state->gpios->ndescs);
-	state->map.virt	      = devm_ioremap_resource(&pdev->dev, memory);
-	if (IS_ERR(state->map.virt))
-		return PTR_ERR(state->map.virt);
+	state_pdev->map.name       = DRIVER_NAME;
+	state_pdev->map.bankwidth  = pdata->width;
+	state_pdev->map.size       = resource_size(memory);
 
-	state->map.phys       = NO_XIP;
-	state->map.map_priv_1 = (unsigned long)state;
+	ret = gpio_flash_probe_common(&pdev->dev, &state_pdev->state,
+				      &state_pdev->map);
+	if (ret)
+		return ret;
 
-	platform_set_drvdata(pdev, state);
+	platform_set_drvdata(pdev, state_pdev);
 
 	dev_notice(&pdev->dev, "probing %d-bit flash bus\n",
-		   state->map.bankwidth * 8);
-	state->mtd = do_map_probe(memory->name, &state->map);
-	if (!state->mtd)
+		   state_pdev->map.bankwidth * 8);
+	state_pdev->mtd = do_map_probe(memory->name, &state_pdev->map);
+	if (!state_pdev->mtd)
 		return -ENXIO;
-	state->mtd->dev.parent = &pdev->dev;
+	state_pdev->mtd->dev.parent = &pdev->dev;
 
-	mtd_device_parse_register(state->mtd, part_probe_types, NULL,
+	mtd_device_parse_register(state_pdev->mtd, part_probe_types, NULL,
 				  pdata->parts, pdata->nr_parts);
 
 	return 0;
@@ -259,10 +278,10 @@  static int gpio_flash_probe(struct platform_device *pdev)
 
 static int gpio_flash_remove(struct platform_device *pdev)
 {
-	struct async_state *state = platform_get_drvdata(pdev);
+	struct async_state_pdev *state_pdev = platform_get_drvdata(pdev);
 
-	mtd_device_unregister(state->mtd);
-	map_destroy(state->mtd);
+	mtd_device_unregister(state_pdev->mtd);
+	map_destroy(state_pdev->mtd);
 	return 0;
 }
 
diff --git a/drivers/mtd/maps/gpio-addr-flash.h b/drivers/mtd/maps/gpio-addr-flash.h
new file mode 100644
index 000000000000..46d97a8031eb
--- /dev/null
+++ b/drivers/mtd/maps/gpio-addr-flash.h
@@ -0,0 +1,31 @@ 
+/* SPDX-License-Identifier: GPL-2.0 */
+#include <linux/of.h>
+#include <linux/mtd/map.h>
+
+/**
+ * struct async_state - keep GPIO flash state
+ *	@gpios:       Struct containing the array of GPIO descriptors
+ *	@gpio_values: cached GPIO values
+ *	@win_order:   dedicated memory size (if no GPIOs)
+ */
+struct async_state {
+	struct gpio_descs *gpios;
+	unsigned int gpio_values;
+	unsigned int win_order;
+};
+
+int gpio_flash_probe_common(struct device *dev,
+				   struct async_state *state,
+				   struct map_info *map);
+
+#ifdef CONFIG_MTD_PHYSMAP_OF_GPIO
+int of_flash_probe_gpio(struct platform_device *pdev, struct device_node *np,
+			struct map_info *map);
+#else
+static inline
+int of_flash_probe_gpio(struct platform_device *pdev, struct device_node *np,
+			struct map_info *map)
+{
+	return 0;
+}
+#endif
diff --git a/drivers/mtd/maps/physmap_of_core.c b/drivers/mtd/maps/physmap_of_core.c
index 062add8b3a6e..b532b8cf16c4 100644
--- a/drivers/mtd/maps/physmap_of_core.c
+++ b/drivers/mtd/maps/physmap_of_core.c
@@ -27,6 +27,7 @@ 
 #include <linux/slab.h>
 #include "physmap_of_gemini.h"
 #include "physmap_of_versatile.h"
+#include "gpio-addr-flash.h"
 
 struct of_flash_list {
 	struct mtd_info *mtd;
@@ -257,6 +258,10 @@  static int of_flash_probe(struct platform_device *dev)
 
 		simple_map_init(&info->list[i].map);
 
+		err = of_flash_probe_gpio(dev, dp, &info->list[i].map);
+		if (err)
+			goto err_out_release;
+
 		/*
 		 * On some platforms (e.g. MPC5200) a direct 1:1 mapping
 		 * may cause problems with JFFS2 usage, as the local bus (LPB)
diff --git a/drivers/mtd/maps/physmap_of_gpio.c b/drivers/mtd/maps/physmap_of_gpio.c
new file mode 100644
index 000000000000..97e02737ed67
--- /dev/null
+++ b/drivers/mtd/maps/physmap_of_gpio.c
@@ -0,0 +1,24 @@ 
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2018 Qtechnology A/S
+ *
+ * Ricardo Ribalda <ricardo.ribalda@gmail.com>
+ *
+ */
+#include <linux/platform_device.h>
+#include "gpio-addr-flash.h"
+
+int of_flash_probe_gpio(struct platform_device *pdev, struct device_node *np,
+			struct map_info *map)
+{
+	struct async_state *state;
+
+	if (!of_device_is_compatible(np, "mtd,gpio-addr-flash"))
+		return 0;
+
+	state = devm_kzalloc(&pdev->dev, sizeof(*state), GFP_KERNEL);
+	if (!state)
+		return -ENOMEM;
+
+	return gpio_flash_probe_common(&pdev->dev, state, map);
+}