2018-10-04 13:01:49

by Ricardo Ribalda Delgado

[permalink] [raw]
Subject: [PATCH v6 01/10] mtd: physmap_of: Release resources on error

During probe, if there was an error the memory region and the memory
map were not properly released.

This can lead a system unusable if deferred probe is in use.

Signed-off-by: Ricardo Ribalda Delgado <[email protected]>
---
drivers/mtd/maps/physmap_of_core.c | 12 ++++++++----
1 file changed, 8 insertions(+), 4 deletions(-)

diff --git a/drivers/mtd/maps/physmap_of_core.c b/drivers/mtd/maps/physmap_of_core.c
index 4129535b8e46..062add8b3a6e 100644
--- a/drivers/mtd/maps/physmap_of_core.c
+++ b/drivers/mtd/maps/physmap_of_core.c
@@ -241,10 +241,10 @@ static int of_flash_probe(struct platform_device *dev)

err = of_flash_probe_gemini(dev, dp, &info->list[i].map);
if (err)
- goto err_out;
+ goto err_out_release;
err = of_flash_probe_versatile(dev, dp, &info->list[i].map);
if (err)
- goto err_out;
+ goto err_out_release;

err = -ENOMEM;
info->list[i].map.virt = ioremap(info->list[i].map.phys,
@@ -252,7 +252,7 @@ static int of_flash_probe(struct platform_device *dev)
if (!info->list[i].map.virt) {
dev_err(&dev->dev, "Failed to ioremap() flash"
" region\n");
- goto err_out;
+ goto err_out_release;
}

simple_map_init(&info->list[i].map);
@@ -290,7 +290,7 @@ static int of_flash_probe(struct platform_device *dev)
err = -ENXIO;
if (!info->list[i].mtd) {
dev_err(&dev->dev, "do_map_probe() failed\n");
- goto err_out;
+ goto err_out_iounmap;
} else {
info->list_size++;
}
@@ -329,6 +329,10 @@ static int of_flash_probe(struct platform_device *dev)

return 0;

+err_out_iounmap:
+ iounmap(info->list[i].map.virt);
+err_out_release:
+ release_mem_region(res.start, resource_size(&res));
err_out:
kfree(mtd_list);
err_flash_remove:
--
2.19.0



2018-10-04 13:01:54

by Ricardo Ribalda Delgado

[permalink] [raw]
Subject: [PATCH v6 03/10] mtd: maps: gpio-addr-flash: Replace custom printk

Use preferred print methods dev_*

Signed-off-by: Ricardo Ribalda Delgado <[email protected]>
---
drivers/mtd/maps/gpio-addr-flash.c | 10 +++-------
1 file changed, 3 insertions(+), 7 deletions(-)

diff --git a/drivers/mtd/maps/gpio-addr-flash.c b/drivers/mtd/maps/gpio-addr-flash.c
index 9d9723693217..17be47f72973 100644
--- a/drivers/mtd/maps/gpio-addr-flash.c
+++ b/drivers/mtd/maps/gpio-addr-flash.c
@@ -25,11 +25,7 @@
#include <linux/slab.h>
#include <linux/types.h>

-#define pr_devinit(fmt, args...) \
- ({ static const char __fmt[] = fmt; printk(__fmt, ## args); })
-
#define DRIVER_NAME "gpio-addr-flash"
-#define PFX DRIVER_NAME ": "

/**
* struct async_state - keep GPIO flash state
@@ -250,7 +246,7 @@ static int gpio_flash_probe(struct platform_device *pdev)
i = 0;
do {
if (gpio_request(state->gpio_addrs[i], DRIVER_NAME)) {
- pr_devinit(KERN_ERR PFX "failed to request gpio %d\n",
+ dev_err(&pdev->dev, "failed to request gpio %d\n",
state->gpio_addrs[i]);
while (i--)
gpio_free(state->gpio_addrs[i]);
@@ -260,8 +256,8 @@ static int gpio_flash_probe(struct platform_device *pdev)
gpio_direction_output(state->gpio_addrs[i], 0);
} while (++i < state->gpio_count);

- pr_devinit(KERN_NOTICE PFX "probing %d-bit flash bus\n",
- state->map.bankwidth * 8);
+ 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) {
for (i = 0; i < state->gpio_count; ++i)
--
2.19.0


2018-10-04 13:02:00

by Ricardo Ribalda Delgado

[permalink] [raw]
Subject: [PATCH v6 04/10] mtd: maps: gpio-addr-flash: Fix ioremapped size

We should only iomap the area of the chip that is memory mapped.
Otherwise we could be mapping devices beyond the memory space or that
belong to other devices.

Signed-off-by: Ricardo Ribalda Delgado <[email protected]>
Fixes: ebd71e3a4861 ("mtd: maps: gpio-addr-flash: fix warnings and make more portable")
Cc: <[email protected]>
---
drivers/mtd/maps/gpio-addr-flash.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/mtd/maps/gpio-addr-flash.c b/drivers/mtd/maps/gpio-addr-flash.c
index 17be47f72973..6de16e81994c 100644
--- a/drivers/mtd/maps/gpio-addr-flash.c
+++ b/drivers/mtd/maps/gpio-addr-flash.c
@@ -234,7 +234,7 @@ static int gpio_flash_probe(struct platform_device *pdev)
state->map.copy_to = gf_copy_to;
state->map.bankwidth = pdata->width;
state->map.size = state->win_size * (1 << state->gpio_count);
- state->map.virt = ioremap_nocache(memory->start, state->map.size);
+ state->map.virt = ioremap_nocache(memory->start, state->win_size);
if (!state->map.virt)
return -ENOMEM;

--
2.19.0


2018-10-04 13:02:03

by Ricardo Ribalda Delgado

[permalink] [raw]
Subject: [PATCH v6 05/10] mtd: maps: gpio-addr-flash: Use devm_* functions

By using devm functions we can make the code cleaner.

Signed-off-by: Ricardo Ribalda Delgado <[email protected]>
---
drivers/mtd/maps/gpio-addr-flash.c | 26 ++++++++------------------
1 file changed, 8 insertions(+), 18 deletions(-)

diff --git a/drivers/mtd/maps/gpio-addr-flash.c b/drivers/mtd/maps/gpio-addr-flash.c
index 6de16e81994c..84404dcc7824 100644
--- a/drivers/mtd/maps/gpio-addr-flash.c
+++ b/drivers/mtd/maps/gpio-addr-flash.c
@@ -213,7 +213,7 @@ static int gpio_flash_probe(struct platform_device *pdev)
return -EINVAL;

arr_size = sizeof(int) * gpios->end;
- state = kzalloc(sizeof(*state) + arr_size, GFP_KERNEL);
+ state = devm_kzalloc(&pdev->dev, sizeof(*state) + arr_size, GFP_KERNEL);
if (!state)
return -ENOMEM;

@@ -234,9 +234,9 @@ static int gpio_flash_probe(struct platform_device *pdev)
state->map.copy_to = gf_copy_to;
state->map.bankwidth = pdata->width;
state->map.size = state->win_size * (1 << state->gpio_count);
- state->map.virt = ioremap_nocache(memory->start, state->win_size);
- if (!state->map.virt)
- return -ENOMEM;
+ state->map.virt = devm_ioremap_resource(&pdev->dev, memory);
+ if (IS_ERR(state->map.virt))
+ return PTR_ERR(state->map.virt);

state->map.phys = NO_XIP;
state->map.map_priv_1 = (unsigned long)state;
@@ -245,12 +245,10 @@ static int gpio_flash_probe(struct platform_device *pdev)

i = 0;
do {
- if (gpio_request(state->gpio_addrs[i], DRIVER_NAME)) {
+ if (devm_gpio_request(&pdev->dev, state->gpio_addrs[i],
+ DRIVER_NAME)) {
dev_err(&pdev->dev, "failed to request gpio %d\n",
state->gpio_addrs[i]);
- while (i--)
- gpio_free(state->gpio_addrs[i]);
- kfree(state);
return -EBUSY;
}
gpio_direction_output(state->gpio_addrs[i], 0);
@@ -259,12 +257,8 @@ static int gpio_flash_probe(struct platform_device *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) {
- for (i = 0; i < state->gpio_count; ++i)
- gpio_free(state->gpio_addrs[i]);
- kfree(state);
+ if (!state->mtd)
return -ENXIO;
- }
state->mtd->dev.parent = &pdev->dev;

mtd_device_parse_register(state->mtd, part_probe_types, NULL,
@@ -276,13 +270,9 @@ 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);
- size_t i = 0;
- do {
- gpio_free(state->gpio_addrs[i]);
- } while (++i < state->gpio_count);
+
mtd_device_unregister(state->mtd);
map_destroy(state->mtd);
- kfree(state);
return 0;
}

--
2.19.0


2018-10-04 13:02:15

by Ricardo Ribalda Delgado

[permalink] [raw]
Subject: [PATCH v6 10/10] mtd: maps: gpio-addr-flash: Add support for device-tree devices

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 <[email protected]>
---
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 | 7 +++
drivers/mtd/maps/physmap_of_gpio.c | 24 ++++++++
6 files changed, 127 insertions(+), 37 deletions(-)
create mode 100644 drivers/mtd/maps/gpio-addr-flash.h
create mode 100644 drivers/mtd/maps/physmap_of_gpio.c

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..19a300232969 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,12 @@ 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){
+ iounmap(info->list[i].map.virt);
+ goto err_out;
+ }
+
/*
* 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 <[email protected]>
+ *
+ */
+#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);
+}
--
2.19.0


2018-10-04 13:02:27

by Ricardo Ribalda Delgado

[permalink] [raw]
Subject: [PATCH v6 08/10] mtd: maps: gpio-addr-flash: Convert to gpiod

Convert from legacy gpio API to gpiod.
Board files will have to use gpiod_lookup_tables.

Signed-off-by: Ricardo Ribalda Delgado <[email protected]>
Suggested-by: Boris Brezillon <[email protected]>
---
drivers/mtd/maps/gpio-addr-flash.c | 55 ++++++++++++------------------
1 file changed, 21 insertions(+), 34 deletions(-)

diff --git a/drivers/mtd/maps/gpio-addr-flash.c b/drivers/mtd/maps/gpio-addr-flash.c
index 47b12a6fead4..a20e85aa770e 100644
--- a/drivers/mtd/maps/gpio-addr-flash.c
+++ b/drivers/mtd/maps/gpio-addr-flash.c
@@ -14,6 +14,7 @@
*/

#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
@@ -33,16 +34,14 @@
* struct async_state - keep GPIO flash state
* @mtd: MTD state for this mapping
* @map: MTD map state for this flash
- * @gpio_count: number of GPIOs used to address
- * @gpio_addrs: array of GPIOs to twiddle
+ * @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 mtd_info *mtd;
struct map_info map;
- size_t gpio_count;
- unsigned *gpio_addrs;
+ struct gpio_descs *gpios;
unsigned int gpio_values;
unsigned int win_order;
};
@@ -66,11 +65,11 @@ static void gf_set_gpios(struct async_state *state, unsigned long ofs)
if (ofs == state->gpio_values)
return;

- for (i = 0; i < state->gpio_count; i++) {
+ for (i = 0; i < state->gpios->ndescs; i++) {
if ((ofs & BIT(i)) == (state->gpio_values & BIT(i)))
continue;

- gpio_set_value(state->gpio_addrs[i], !!(ofs & BIT(i)));
+ gpiod_set_value(state->gpios->desc[i], !!(ofs & BIT(i)));
}

state->gpio_values = ofs;
@@ -182,18 +181,22 @@ static const char * const part_probe_types[] = {
* The platform resource layout expected looks something like:
* struct mtd_partition partitions[] = { ... };
* struct physmap_flash_data flash_data = { ... };
- * unsigned flash_gpios[] = { GPIO_XX, GPIO_XX, ... };
+ * static struct gpiod_lookup_table addr_flash_gpios = {
+ * .dev_id = "gpio-addr-flash.0",
+ * .table = {
+ * GPIO_LOOKUP_IDX("gpio.0", 15, "addr", 0, GPIO_ACTIVE_HIGH),
+ * GPIO_LOOKUP_IDX("gpio.0", 16, "addr", 1, GPIO_ACTIVE_HIGH),
+ * );
+ * };
+ * gpiod_add_lookup_table(&addr_flash_gpios);
+ *
* struct resource flash_resource[] = {
* {
* .name = "cfi_probe",
* .start = 0x20000000,
* .end = 0x201fffff,
* .flags = IORESOURCE_MEM,
- * }, {
- * .start = (unsigned long)flash_gpios,
- * .end = ARRAY_SIZE(flash_gpios),
- * .flags = IORESOURCE_IRQ,
- * }
+ * },
* };
* struct platform_device flash_device = {
* .name = "gpio-addr-flash",
@@ -205,29 +208,24 @@ static const char * const part_probe_types[] = {
*/
static int gpio_flash_probe(struct platform_device *pdev)
{
- size_t i;
struct physmap_flash_data *pdata;
struct resource *memory;
- struct resource *gpios;
struct async_state *state;

pdata = dev_get_platdata(&pdev->dev);
memory = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- gpios = platform_get_resource(pdev, IORESOURCE_IRQ, 0);

- if (!memory || !gpios || !gpios->end)
+ if (!memory)
return -EINVAL;

state = devm_kzalloc(&pdev->dev, sizeof(*state), GFP_KERNEL);
if (!state)
return -ENOMEM;

- /*
- * We cast start/end to known types in the boards file, so cast
- * away their pointer types here to the known types (gpios->xxx).
- */
- state->gpio_count = gpios->end;
- state->gpio_addrs = (void *)(unsigned long)gpios->start;
+ 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->map.name = DRIVER_NAME;
@@ -236,7 +234,7 @@ static int gpio_flash_probe(struct platform_device *pdev)
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->gpio_count);
+ 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);
@@ -246,17 +244,6 @@ static int gpio_flash_probe(struct platform_device *pdev)

platform_set_drvdata(pdev, state);

- i = 0;
- do {
- if (devm_gpio_request(&pdev->dev, state->gpio_addrs[i],
- DRIVER_NAME)) {
- dev_err(&pdev->dev, "failed to request gpio %d\n",
- state->gpio_addrs[i]);
- return -EBUSY;
- }
- gpio_direction_output(state->gpio_addrs[i], 0);
- } while (++i < state->gpio_count);
-
dev_notice(&pdev->dev, "probing %d-bit flash bus\n",
state->map.bankwidth * 8);
state->mtd = do_map_probe(memory->name, &state->map);
--
2.19.0


2018-10-04 13:02:40

by Ricardo Ribalda Delgado

[permalink] [raw]
Subject: [PATCH v6 06/10] mtd: maps: gpio-addr-flash: Use order insted of size

By using the order of the window instead of the size, we can replace a
lot of expensive division and modulus on the code with simple bit
operations.

Signed-off-by: Ricardo Ribalda Delgado <[email protected]>
---
drivers/mtd/maps/gpio-addr-flash.c | 39 ++++++++++++++++--------------
1 file changed, 21 insertions(+), 18 deletions(-)

diff --git a/drivers/mtd/maps/gpio-addr-flash.c b/drivers/mtd/maps/gpio-addr-flash.c
index 84404dcc7824..89cc8cce161b 100644
--- a/drivers/mtd/maps/gpio-addr-flash.c
+++ b/drivers/mtd/maps/gpio-addr-flash.c
@@ -25,6 +25,8 @@
#include <linux/slab.h>
#include <linux/types.h>

+#define win_mask(x) ((BIT(x)) - 1)
+
#define DRIVER_NAME "gpio-addr-flash"

/**
@@ -34,7 +36,7 @@
* @gpio_count: number of GPIOs used to address
* @gpio_addrs: array of GPIOs to twiddle
* @gpio_values: cached GPIO values
- * @win_size: dedicated memory size (if no GPIOs)
+ * @win_order: dedicated memory size (if no GPIOs)
*/
struct async_state {
struct mtd_info *mtd;
@@ -42,7 +44,7 @@ struct async_state {
size_t gpio_count;
unsigned *gpio_addrs;
int *gpio_values;
- unsigned long win_size;
+ unsigned int win_order;
};
#define gf_map_info_to_state(mi) ((struct async_state *)(mi)->map_priv_1)

@@ -60,7 +62,8 @@ static void gf_set_gpios(struct async_state *state, unsigned long ofs)
{
size_t i = 0;
int value;
- ofs /= state->win_size;
+
+ ofs >>= state->win_order;
do {
value = ofs & (1 << i);
if (state->gpio_values[i] != value) {
@@ -83,7 +86,7 @@ static map_word gf_read(struct map_info *map, unsigned long ofs)

gf_set_gpios(state, ofs);

- word = readw(map->virt + (ofs % state->win_size));
+ word = readw(map->virt + (ofs & win_mask(state->win_order)));
test.x[0] = word;
return test;
}
@@ -105,14 +108,14 @@ static void gf_copy_from(struct map_info *map, void *to, unsigned long from, ssi
int this_len;

while (len) {
- if ((from % state->win_size) + len > state->win_size)
- this_len = state->win_size - (from % state->win_size);
- else
- this_len = len;
+ this_len = from & win_mask(state->win_order);
+ this_len = BIT(state->win_order) - this_len;
+ this_len = min_t(int, len, this_len);

gf_set_gpios(state, from);
- memcpy_fromio(to, map->virt + (from % state->win_size),
- this_len);
+ memcpy_fromio(to,
+ map->virt + (from & win_mask(state->win_order)),
+ this_len);
len -= this_len;
from += this_len;
to += this_len;
@@ -132,7 +135,7 @@ static void gf_write(struct map_info *map, map_word d1, unsigned long ofs)
gf_set_gpios(state, ofs);

d = d1.x[0];
- writew(d, map->virt + (ofs % state->win_size));
+ writew(d, map->virt + (ofs & win_mask(state->win_order)));
}

/**
@@ -152,13 +155,13 @@ static void gf_copy_to(struct map_info *map, unsigned long to,
int this_len;

while (len) {
- if ((to % state->win_size) + len > state->win_size)
- this_len = state->win_size - (to % state->win_size);
- else
- this_len = len;
+ this_len = to & win_mask(state->win_order);
+ this_len = BIT(state->win_order) - this_len;
+ this_len = min_t(int, len, this_len);

gf_set_gpios(state, to);
- memcpy_toio(map->virt + (to % state->win_size), from, len);
+ memcpy_toio(map->virt + (to & win_mask(state->win_order)),
+ from, len);

len -= this_len;
to += this_len;
@@ -224,7 +227,7 @@ static int gpio_flash_probe(struct platform_device *pdev)
state->gpio_count = gpios->end;
state->gpio_addrs = (void *)(unsigned long)gpios->start;
state->gpio_values = (void *)(state + 1);
- state->win_size = resource_size(memory);
+ state->win_order = get_bitmask_order(resource_size(memory)) - 1;
memset(state->gpio_values, 0xff, arr_size);

state->map.name = DRIVER_NAME;
@@ -233,7 +236,7 @@ static int gpio_flash_probe(struct platform_device *pdev)
state->map.write = gf_write;
state->map.copy_to = gf_copy_to;
state->map.bankwidth = pdata->width;
- state->map.size = state->win_size * (1 << state->gpio_count);
+ state->map.size = BIT(state->win_order + state->gpio_count);
state->map.virt = devm_ioremap_resource(&pdev->dev, memory);
if (IS_ERR(state->map.virt))
return PTR_ERR(state->map.virt);
--
2.19.0


2018-10-04 13:03:49

by Ricardo Ribalda Delgado

[permalink] [raw]
Subject: [PATCH v6 07/10] mtd: maps: gpio-addr-flash: Replace array with an integer

By replacing the array with an integer we can avoid completely
the bit comparison loop if the value has not changed (by far
the most common case).

Signed-off-by: Ricardo Ribalda Delgado <[email protected]>
---
drivers/mtd/maps/gpio-addr-flash.c | 34 +++++++++++++++---------------
1 file changed, 17 insertions(+), 17 deletions(-)

diff --git a/drivers/mtd/maps/gpio-addr-flash.c b/drivers/mtd/maps/gpio-addr-flash.c
index 89cc8cce161b..47b12a6fead4 100644
--- a/drivers/mtd/maps/gpio-addr-flash.c
+++ b/drivers/mtd/maps/gpio-addr-flash.c
@@ -43,7 +43,7 @@ struct async_state {
struct map_info map;
size_t gpio_count;
unsigned *gpio_addrs;
- int *gpio_values;
+ unsigned int gpio_values;
unsigned int win_order;
};
#define gf_map_info_to_state(mi) ((struct async_state *)(mi)->map_priv_1)
@@ -55,22 +55,25 @@ struct async_state {
*
* Rather than call the GPIO framework every time, cache the last-programmed
* value. This speeds up sequential accesses (which are by far the most common
- * type). We rely on the GPIO framework to treat non-zero value as high so
- * that we don't have to normalize the bits.
+ * type).
*/
static void gf_set_gpios(struct async_state *state, unsigned long ofs)
{
- size_t i = 0;
- int value;
+ int i;

ofs >>= state->win_order;
- do {
- value = ofs & (1 << i);
- if (state->gpio_values[i] != value) {
- gpio_set_value(state->gpio_addrs[i], value);
- state->gpio_values[i] = value;
- }
- } while (++i < state->gpio_count);
+
+ if (ofs == state->gpio_values)
+ return;
+
+ for (i = 0; i < state->gpio_count; i++) {
+ if ((ofs & BIT(i)) == (state->gpio_values & BIT(i)))
+ continue;
+
+ gpio_set_value(state->gpio_addrs[i], !!(ofs & BIT(i)));
+ }
+
+ state->gpio_values = ofs;
}

/**
@@ -202,7 +205,7 @@ static const char * const part_probe_types[] = {
*/
static int gpio_flash_probe(struct platform_device *pdev)
{
- size_t i, arr_size;
+ size_t i;
struct physmap_flash_data *pdata;
struct resource *memory;
struct resource *gpios;
@@ -215,8 +218,7 @@ static int gpio_flash_probe(struct platform_device *pdev)
if (!memory || !gpios || !gpios->end)
return -EINVAL;

- arr_size = sizeof(int) * gpios->end;
- state = devm_kzalloc(&pdev->dev, sizeof(*state) + arr_size, GFP_KERNEL);
+ state = devm_kzalloc(&pdev->dev, sizeof(*state), GFP_KERNEL);
if (!state)
return -ENOMEM;

@@ -226,9 +228,7 @@ static int gpio_flash_probe(struct platform_device *pdev)
*/
state->gpio_count = gpios->end;
state->gpio_addrs = (void *)(unsigned long)gpios->start;
- state->gpio_values = (void *)(state + 1);
state->win_order = get_bitmask_order(resource_size(memory)) - 1;
- memset(state->gpio_values, 0xff, arr_size);

state->map.name = DRIVER_NAME;
state->map.read = gf_read;
--
2.19.0


2018-10-04 13:03:51

by Ricardo Ribalda Delgado

[permalink] [raw]
Subject: [PATCH v6 09/10] dt-binding: mtd: Document gpio-addr-flash

Add documentation for gpio-addr-flash. This binding allow creating
flash devices that are paged using GPIOs.

Cc: [email protected]
Reviewed-by: Rob Herring <[email protected]>
Signed-off-by: Ricardo Ribalda Delgado <[email protected]>
---
.../bindings/mtd/mtd,gpio-addr-flash.txt | 25 +++++++++++++++++++
1 file changed, 25 insertions(+)
create mode 100644 Documentation/devicetree/bindings/mtd/mtd,gpio-addr-flash.txt

diff --git a/Documentation/devicetree/bindings/mtd/mtd,gpio-addr-flash.txt b/Documentation/devicetree/bindings/mtd/mtd,gpio-addr-flash.txt
new file mode 100644
index 000000000000..304a33880f9e
--- /dev/null
+++ b/Documentation/devicetree/bindings/mtd/mtd,gpio-addr-flash.txt
@@ -0,0 +1,25 @@
+Memory Mapped flash with some address lines addressed using GPIOs
+
+Handle the case where a flash device is mostly addressed using physical
+line and supplemented by GPIOs. This way you can hook up say a 8MiB flash
+to a 2MiB memory range and use the GPIOs to select a particular range.
+
+ - compatible : must be "mtd,gpio-addr-flash", "cfi-flash";
+ - reg : Address range of the mtd chip that is memory mapped, this is,
+ on the previous example 2MiB.
+ - addr-gpios: List of GPIO specifiers that will be used to address the MSBs
+ address lines. The order goes from LSB to MSB.
+
+For the rest of the properties, see mtd-physmap.txt.
+
+The device tree may optionally contain sub-nodes describing partitions of the
+address space. Check partition.txt for more details.
+
+Example:
+
+ flash@300000 {
+ compatible = "mtd,gpio-addr-flash", "cfi-flash";
+ bank-width = <2>;
+ reg = < 0x00300000 0x00200000 >;
+ addr-gpios = <&gpio_0 3 0>, <&gpio_0 4 0>;
+ } ;
--
2.19.0


2018-10-04 13:04:12

by Ricardo Ribalda Delgado

[permalink] [raw]
Subject: [PATCH v6 02/10] mtd: physmap_of: Remove unused struct of_device_id

This struct does not seem to be used anywhere on the code

Signed-off-by: Ricardo Ribalda Delgado <[email protected]>
---
drivers/mtd/maps/physmap_of_gemini.c | 5 -----
1 file changed, 5 deletions(-)

diff --git a/drivers/mtd/maps/physmap_of_gemini.c b/drivers/mtd/maps/physmap_of_gemini.c
index 830b1b7e702b..9df62ca721d5 100644
--- a/drivers/mtd/maps/physmap_of_gemini.c
+++ b/drivers/mtd/maps/physmap_of_gemini.c
@@ -44,11 +44,6 @@

#define FLASH_PARALLEL_HIGH_PIN_CNT (1 << 20) /* else low pin cnt */

-static const struct of_device_id syscon_match[] = {
- { .compatible = "cortina,gemini-syscon" },
- { },
-};
-
int of_flash_probe_gemini(struct platform_device *pdev,
struct device_node *np,
struct map_info *map)
--
2.19.0


2018-10-04 13:09:34

by Boris Brezillon

[permalink] [raw]
Subject: Re: [PATCH v6 01/10] mtd: physmap_of: Release resources on error

Hi Ricardo,

On Thu, 4 Oct 2018 15:01:01 +0200
Ricardo Ribalda Delgado <[email protected]> wrote:

> During probe, if there was an error the memory region and the memory
> map were not properly released.
>
> This can lead a system unusable if deferred probe is in use.
>
> Signed-off-by: Ricardo Ribalda Delgado <[email protected]>
> ---
> drivers/mtd/maps/physmap_of_core.c | 12 ++++++++----
> 1 file changed, 8 insertions(+), 4 deletions(-)
>
> diff --git a/drivers/mtd/maps/physmap_of_core.c b/drivers/mtd/maps/physmap_of_core.c
> index 4129535b8e46..062add8b3a6e 100644
> --- a/drivers/mtd/maps/physmap_of_core.c
> +++ b/drivers/mtd/maps/physmap_of_core.c
> @@ -241,10 +241,10 @@ static int of_flash_probe(struct platform_device *dev)
>
> err = of_flash_probe_gemini(dev, dp, &info->list[i].map);
> if (err)
> - goto err_out;
> + goto err_out_release;
> err = of_flash_probe_versatile(dev, dp, &info->list[i].map);
> if (err)
> - goto err_out;
> + goto err_out_release;
>
> err = -ENOMEM;
> info->list[i].map.virt = ioremap(info->list[i].map.phys,
> @@ -252,7 +252,7 @@ static int of_flash_probe(struct platform_device *dev)
> if (!info->list[i].map.virt) {
> dev_err(&dev->dev, "Failed to ioremap() flash"
> " region\n");
> - goto err_out;
> + goto err_out_release;
> }
>
> simple_map_init(&info->list[i].map);
> @@ -290,7 +290,7 @@ static int of_flash_probe(struct platform_device *dev)
> err = -ENXIO;
> if (!info->list[i].mtd) {
> dev_err(&dev->dev, "do_map_probe() failed\n");
> - goto err_out;
> + goto err_out_iounmap;
> } else {
> info->list_size++;
> }
> @@ -329,6 +329,10 @@ static int of_flash_probe(struct platform_device *dev)
>
> return 0;
>
> +err_out_iounmap:
> + iounmap(info->list[i].map.virt);
> +err_out_release:
> + release_mem_region(res.start, resource_size(&res));
> err_out:
> kfree(mtd_list);
> err_flash_remove:

Please fix that using devm_ioremap_resource() instead.

Thanks,

Boris

2018-10-04 13:09:58

by Boris Brezillon

[permalink] [raw]
Subject: Re: [PATCH v6 02/10] mtd: physmap_of: Remove unused struct of_device_id

On Thu, 4 Oct 2018 15:01:02 +0200
Ricardo Ribalda Delgado <[email protected]> wrote:

> This struct does not seem to be used anywhere on the code
>
> Signed-off-by: Ricardo Ribalda Delgado <[email protected]>

Already queued that one.

> ---
> drivers/mtd/maps/physmap_of_gemini.c | 5 -----
> 1 file changed, 5 deletions(-)
>
> diff --git a/drivers/mtd/maps/physmap_of_gemini.c b/drivers/mtd/maps/physmap_of_gemini.c
> index 830b1b7e702b..9df62ca721d5 100644
> --- a/drivers/mtd/maps/physmap_of_gemini.c
> +++ b/drivers/mtd/maps/physmap_of_gemini.c
> @@ -44,11 +44,6 @@
>
> #define FLASH_PARALLEL_HIGH_PIN_CNT (1 << 20) /* else low pin cnt */
>
> -static const struct of_device_id syscon_match[] = {
> - { .compatible = "cortina,gemini-syscon" },
> - { },
> -};
> -
> int of_flash_probe_gemini(struct platform_device *pdev,
> struct device_node *np,
> struct map_info *map)


2018-10-04 14:02:39

by Boris Brezillon

[permalink] [raw]
Subject: Re: [PATCH v6 09/10] dt-binding: mtd: Document gpio-addr-flash

On Thu, 4 Oct 2018 15:01:09 +0200
Ricardo Ribalda Delgado <[email protected]> wrote:

> Add documentation for gpio-addr-flash. This binding allow creating
> flash devices that are paged using GPIOs.
>
> Cc: [email protected]
> Reviewed-by: Rob Herring <[email protected]>
> Signed-off-by: Ricardo Ribalda Delgado <[email protected]>
> ---
> .../bindings/mtd/mtd,gpio-addr-flash.txt | 25 +++++++++++++++++++
> 1 file changed, 25 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/mtd/mtd,gpio-addr-flash.txt
>
> diff --git a/Documentation/devicetree/bindings/mtd/mtd,gpio-addr-flash.txt b/Documentation/devicetree/bindings/mtd/mtd,gpio-addr-flash.txt
> new file mode 100644
> index 000000000000..304a33880f9e
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/mtd/mtd,gpio-addr-flash.txt
> @@ -0,0 +1,25 @@
> +Memory Mapped flash with some address lines addressed using GPIOs
> +
> +Handle the case where a flash device is mostly addressed using physical
> +line and supplemented by GPIOs. This way you can hook up say a 8MiB flash
> +to a 2MiB memory range and use the GPIOs to select a particular range.
> +
> + - compatible : must be "mtd,gpio-addr-flash", "cfi-flash";

I keep thinking you don't need a specific binding, just add an optional
addr-gpios property to the generic physmap binding and that should do
the trick. Plus, "*mtd*,gpio-addr-flash" is a linux-ism, and DT
bindings should be OS-agnostic.

> + - reg : Address range of the mtd chip that is memory mapped, this is,
> + on the previous example 2MiB.
> + - addr-gpios: List of GPIO specifiers that will be used to address the MSBs
> + address lines. The order goes from LSB to MSB.
> +
> +For the rest of the properties, see mtd-physmap.txt.
> +
> +The device tree may optionally contain sub-nodes describing partitions of the
> +address space. Check partition.txt for more details.
> +
> +Example:
> +
> + flash@300000 {
> + compatible = "mtd,gpio-addr-flash", "cfi-flash";
> + bank-width = <2>;
> + reg = < 0x00300000 0x00200000 >;
> + addr-gpios = <&gpio_0 3 0>, <&gpio_0 4 0>;
> + } ;


2018-10-04 14:33:23

by Ricardo Ribalda Delgado

[permalink] [raw]
Subject: Re: [PATCH v6 09/10] dt-binding: mtd: Document gpio-addr-flash

Hi Boris
On Thu, Oct 4, 2018 at 4:02 PM Boris Brezillon
<[email protected]> wrote:
>
> On Thu, 4 Oct 2018 15:01:09 +0200
> Ricardo Ribalda Delgado <[email protected]> wrote:
>
> > Add documentation for gpio-addr-flash. This binding allow creating
> > flash devices that are paged using GPIOs.
> >
> > Cc: [email protected]
> > Reviewed-by: Rob Herring <[email protected]>
> > Signed-off-by: Ricardo Ribalda Delgado <[email protected]>
> > ---
> > .../bindings/mtd/mtd,gpio-addr-flash.txt | 25 +++++++++++++++++++
> > 1 file changed, 25 insertions(+)
> > create mode 100644 Documentation/devicetree/bindings/mtd/mtd,gpio-addr-flash.txt
> >
> > diff --git a/Documentation/devicetree/bindings/mtd/mtd,gpio-addr-flash.txt b/Documentation/devicetree/bindings/mtd/mtd,gpio-addr-flash.txt
> > new file mode 100644
> > index 000000000000..304a33880f9e
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/mtd/mtd,gpio-addr-flash.txt
> > @@ -0,0 +1,25 @@
> > +Memory Mapped flash with some address lines addressed using GPIOs
> > +
> > +Handle the case where a flash device is mostly addressed using physical
> > +line and supplemented by GPIOs. This way you can hook up say a 8MiB flash
> > +to a 2MiB memory range and use the GPIOs to select a particular range.
> > +
> > + - compatible : must be "mtd,gpio-addr-flash", "cfi-flash";
>
> I keep thinking you don't need a specific binding, just add an optional
> addr-gpios property to the generic physmap binding and that should do
> the trick. Plus, "*mtd*,gpio-addr-flash" is a linux-ism, and DT
> bindings should be OS-agnostic.
>

That is a very good point. Please take a look to v10, and tell me what
do you think.

It does not require a specific binding and also it can be build optionally.

Regards!

> > + - reg : Address range of the mtd chip that is memory mapped, this is,
> > + on the previous example 2MiB.
> > + - addr-gpios: List of GPIO specifiers that will be used to address the MSBs
> > + address lines. The order goes from LSB to MSB.
> > +
> > +For the rest of the properties, see mtd-physmap.txt.
> > +
> > +The device tree may optionally contain sub-nodes describing partitions of the
> > +address space. Check partition.txt for more details.
> > +
> > +Example:
> > +
> > + flash@300000 {
> > + compatible = "mtd,gpio-addr-flash", "cfi-flash";
> > + bank-width = <2>;
> > + reg = < 0x00300000 0x00200000 >;
> > + addr-gpios = <&gpio_0 3 0>, <&gpio_0 4 0>;
> > + } ;
>


--
Ricardo Ribalda