2018-07-18 23:57:44

by Janusz Krzysztofik

[permalink] [raw]
Subject: [RFC PATCH 0/8] mtd: rawnand: ams-delta: Use gpio-omap accessors for data I/O


Implement the idea suggested by Artem Bityutskiy and Tony Lindgren
described in commit b027274d2e3a ("mtd: ams-delta: fix
request_mem_region() failure").

arch/arm/mach-omap1/board-ams-delta.c | 22 -
drivers/gpio/gpio-omap.c | 88 ++++++
drivers/mtd/nand/raw/ams-delta.c | 436 +++++++++++++++++++++-------------
3 files changed, 376 insertions(+), 170 deletions(-)



2018-07-18 23:57:55

by Janusz Krzysztofik

[permalink] [raw]
Subject: [RFC PATCH 8/8] mtd: rawnand: ams-delta: Use GPIO callbacks for data I/O

Don't readw()/writew() data directly from/to GPIO port which is under
control of gpio-omap driver, use GPIO chip callbacks instead.

Thanks to utilization of get/set_multiple() callbacks, performance
degrade is minor for typical data transfers.

The driver should now work with any 8+-bit bidirectional GPIO port,
not only OMAP.

Signed-off-by: Janusz Krzysztofik <[email protected]>

# Conflicts:
# drivers/mtd/nand/raw/ams-delta.c
---
arch/arm/mach-omap1/board-ams-delta.c | 11 ----
drivers/mtd/nand/raw/ams-delta.c | 119 +++++++++++++++-------------------
2 files changed, 52 insertions(+), 78 deletions(-)

diff --git a/arch/arm/mach-omap1/board-ams-delta.c b/arch/arm/mach-omap1/board-ams-delta.c
index 16f7bbe47607..08e732bc1cd2 100644
--- a/arch/arm/mach-omap1/board-ams-delta.c
+++ b/arch/arm/mach-omap1/board-ams-delta.c
@@ -321,20 +321,9 @@ struct modem_private_data {

static struct modem_private_data modem_priv;

-static struct resource ams_delta_nand_resources[] = {
- [0] = {
- .start = OMAP1_MPUIO_BASE,
- .end = OMAP1_MPUIO_BASE +
- OMAP_MPUIO_IO_CNTL + sizeof(u32) - 1,
- .flags = IORESOURCE_MEM,
- },
-};
-
static struct platform_device ams_delta_nand_device = {
.name = "ams-delta-nand",
.id = -1,
- .num_resources = ARRAY_SIZE(ams_delta_nand_resources),
- .resource = ams_delta_nand_resources,
};

#define OMAP_GPIO_LABEL "gpio-0-15"
diff --git a/drivers/mtd/nand/raw/ams-delta.c b/drivers/mtd/nand/raw/ams-delta.c
index bd501f385e78..8fac8d2a444a 100644
--- a/drivers/mtd/nand/raw/ams-delta.c
+++ b/drivers/mtd/nand/raw/ams-delta.c
@@ -25,13 +25,11 @@
#include <linux/mtd/mtd.h>
#include <linux/mtd/rawnand.h>
#include <linux/mtd/partitions.h>
-#include <linux/platform_data/gpio-omap.h>
+#include <linux/platform_device.h>

#include <asm/io.h>
#include <asm/sizes.h>

-#include <mach/hardware.h>
-
/*
* MTD structure for E3 (Delta)
*/
@@ -45,7 +43,9 @@ struct ams_delta_nand {
struct gpio_desc *gpiod_nwe;
struct gpio_desc *gpiod_ale;
struct gpio_desc *gpiod_cle;
- void __iomem *io_base;
+ struct gpio_chip *data_gpioc;
+ unsigned long data_mask;
+ int data_width;
};

/*
@@ -73,43 +73,55 @@ static const struct mtd_partition partition_info[] = {
.size = 3 * SZ_256K },
};

-static void ams_delta_write_next_byte(struct ams_delta_nand *priv, u_char byte)
+static void ams_delta_write_commit(struct ams_delta_nand *priv)
{
- struct nand_chip *this = &priv->nand_chip;
-
- writew(byte, this->IO_ADDR_W);
gpiod_set_value(priv->gpiod_nwe, 0);
ndelay(40);
gpiod_set_value(priv->gpiod_nwe, 1);
}

+static void ams_delta_write_next_byte(struct ams_delta_nand *priv, u_char byte)
+{
+ struct gpio_chip *data_gpioc = priv->data_gpioc;
+ unsigned long bits = byte;
+
+ data_gpioc->set_multiple(data_gpioc, &priv->data_mask, &bits);
+
+ ams_delta_write_commit(priv);
+}
+
static void ams_delta_write_first_byte(struct ams_delta_nand *priv, u_char byte)
{
- void __iomem *io_base = priv->io_base;
+ struct gpio_chip *data_gpioc = priv->data_gpioc;
+ unsigned long bits = byte;
+ int i;

- writew(0, io_base + OMAP_MPUIO_IO_CNTL);
+ for (i = 0; i < priv->data_width; i++)
+ data_gpioc->direction_output(data_gpioc, i, test_bit(i, &bits));

- ams_delta_write_next_byte(priv, byte);
+ ams_delta_write_commit(priv);
}

static u_char ams_delta_read_next_byte(struct ams_delta_nand *priv)
{
- struct nand_chip *this = &priv->nand_chip;
- u_char res;
+ struct gpio_chip *data_gpioc = priv->data_gpioc;
+ unsigned long bits;

gpiod_set_value(priv->gpiod_nre, 0);
ndelay(40);
- res = readw(this->IO_ADDR_R);
+ data_gpioc->get_multiple(data_gpioc, &priv->data_mask, &bits);
gpiod_set_value(priv->gpiod_nre, 1);

- return res;
+ return bits;
}

static u_char ams_delta_read_first_byte(struct ams_delta_nand *priv)
{
- void __iomem *io_base = priv->io_base;
+ struct gpio_chip *data_gpioc = priv->data_gpioc;
+ int i;

- writew(~0, io_base + OMAP_MPUIO_IO_CNTL);
+ for (i = 0; i < priv->data_width; i++)
+ data_gpioc->direction_input(data_gpioc, i);

return ams_delta_read_next_byte(priv);
}
@@ -188,16 +200,11 @@ static int ams_delta_init(struct platform_device *pdev)
struct ams_delta_nand *priv;
struct nand_chip *this;
struct mtd_info *mtd;
- struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- void __iomem *io_base;
struct gpio_descs *data_gpiods;
struct gpio_chip *data_gpioc;
unsigned long mask, bits;
int i, err = 0;

- if (!res)
- return -ENXIO;
-
/* Allocate memory for MTD device structure and private data */
priv = devm_kzalloc(&pdev->dev, sizeof(struct ams_delta_nand),
GFP_KERNEL);
@@ -210,25 +217,8 @@ static int ams_delta_init(struct platform_device *pdev)
mtd = nand_to_mtd(this);
mtd->dev.parent = &pdev->dev;

- /*
- * Don't try to request the memory region from here,
- * it should have been already requested from the
- * gpio-omap driver and requesting it again would fail.
- */
-
- io_base = ioremap(res->start, resource_size(res));
- if (io_base == NULL) {
- dev_err(&pdev->dev, "ioremap failed\n");
- err = -EIO;
- goto out_free;
- }
-
- priv->io_base = io_base;
nand_set_controller_data(this, priv);

- /* Set address of NAND IO lines */
- this->IO_ADDR_R = io_base + OMAP_MPUIO_INPUT_LATCH;
- this->IO_ADDR_W = io_base + OMAP_MPUIO_OUTPUT;
this->read_byte = ams_delta_read_byte;
this->write_buf = ams_delta_write_buf;
this->read_buf = ams_delta_read_buf;
@@ -238,7 +228,7 @@ static int ams_delta_init(struct platform_device *pdev)
if (IS_ERR(priv->gpiod_rdy)) {
err = PTR_ERR(priv->gpiod_rdy);
dev_warn(&pdev->dev, "RDY GPIO request failed (%d)\n", err);
- goto out_mtd;
+ return err;
}

if (priv->gpiod_rdy)
@@ -256,49 +246,49 @@ static int ams_delta_init(struct platform_device *pdev)
if (IS_ERR(priv->gpiod_nwp)) {
err = PTR_ERR(priv->gpiod_nwp);
dev_err(&pdev->dev, "NWP GPIO request failed (%d)\n", err);
- goto out_mtd;
+ return err;
}

priv->gpiod_nce = devm_gpiod_get(&pdev->dev, "nce", GPIOD_OUT_HIGH);
if (IS_ERR(priv->gpiod_nce)) {
err = PTR_ERR(priv->gpiod_nce);
dev_err(&pdev->dev, "NCE GPIO request failed (%d)\n", err);
- goto out_mtd;
+ return err;
}

priv->gpiod_nre = devm_gpiod_get(&pdev->dev, "nre", GPIOD_OUT_HIGH);
if (IS_ERR(priv->gpiod_nre)) {
err = PTR_ERR(priv->gpiod_nre);
dev_err(&pdev->dev, "NRE GPIO request failed (%d)\n", err);
- goto out_mtd;
+ return err;
}

priv->gpiod_nwe = devm_gpiod_get(&pdev->dev, "nwe", GPIOD_OUT_HIGH);
if (IS_ERR(priv->gpiod_nwe)) {
err = PTR_ERR(priv->gpiod_nwe);
dev_err(&pdev->dev, "NWE GPIO request failed (%d)\n", err);
- goto out_mtd;
+ return err;
}

priv->gpiod_ale = devm_gpiod_get(&pdev->dev, "ale", GPIOD_OUT_LOW);
if (IS_ERR(priv->gpiod_ale)) {
err = PTR_ERR(priv->gpiod_ale);
dev_err(&pdev->dev, "ALE GPIO request failed (%d)\n", err);
- goto out_mtd;
+ return err;
}

priv->gpiod_cle = devm_gpiod_get(&pdev->dev, "cle", GPIOD_OUT_LOW);
if (IS_ERR(priv->gpiod_cle)) {
err = PTR_ERR(priv->gpiod_cle);
dev_err(&pdev->dev, "CLE GPIO request failed (%d)\n", err);
- goto out_mtd;
+ return err;
}
/* Request array of data pins, initialize them as output and set low */
data_gpiods = devm_gpiod_get_array(&pdev->dev, "data", GPIOD_OUT_LOW);
if (IS_ERR(data_gpiods)) {
err = PTR_ERR(data_gpiods);
dev_err(&pdev->dev, "data GPIO request failed: %d\n", err);
- goto out_mtd;
+ return err;
}

/* Use GPIO chip of first data GPIO pin descriptor */
@@ -312,7 +302,7 @@ static int ams_delta_init(struct platform_device *pdev)
err = -EINVAL;
dev_err(&pdev->dev,
"data GPIO chip does not support get/set_multiple()\n");
- goto out_mtd;
+ return err;
}

/* Verify if get_multiple() returns all pins low as initialized above */
@@ -321,13 +311,13 @@ static int ams_delta_init(struct platform_device *pdev)
if (err) {
dev_err(&pdev->dev,
"data GPIO chip get_multiple() failed: %d\n", err);
- goto out_mtd;
+ return err;
}
if (bits) {
err = -EINVAL;
dev_err(&pdev->dev,
"mismmatch of data GPIO initial value: %lu\n", bits);
- goto out_mtd;
+ return err;
}

/* Verify each data GPIO pin */
@@ -337,7 +327,7 @@ static int ams_delta_init(struct platform_device *pdev)
err = -EINVAL;
dev_err(&pdev->dev, "GPIO chip mismatch of data bit %d\n",
i);
- goto out_mtd;
+ return err;
}

/* Require all pins active high (not inverted) */
@@ -346,7 +336,7 @@ static int ams_delta_init(struct platform_device *pdev)
dev_err(&pdev->dev,
"unsupported polarity of data GPIO bit %d\n",
i);
- goto out_mtd;
+ return err;
}

/*
@@ -360,13 +350,13 @@ static int ams_delta_init(struct platform_device *pdev)
dev_err(&pdev->dev,
"data bit %d GPIO chip get() failed: %d\n", i,
err);
- goto out_mtd;
+ return err;
}
if (!err) {
err = -EINVAL;
dev_err(&pdev->dev, "mismatch of data GPIO bit %d value\n",
i);
- goto out_mtd;
+ return err;
}

/*
@@ -379,14 +369,18 @@ static int ams_delta_init(struct platform_device *pdev)
dev_err(&pdev->dev,
"unsupported mode of data GPIO bit %d\n",
i);
- goto out_mtd;
+ return err;
}
}

+ priv->data_gpioc = data_gpioc;
+ priv->data_width = data_gpiods->ndescs;
+ priv->data_mask = (1 << data_gpiods->ndescs) - 1;
+
/* Scan to find existence of the device */
err = nand_scan(mtd, 1);
if (err)
- goto out_mtd;
+ return err;

/* As soon as the device is found, release write protection */
gpiod_set_value(priv->gpiod_nwp, 1);
@@ -394,13 +388,7 @@ static int ams_delta_init(struct platform_device *pdev)
/* Register the partitions */
mtd_device_register(mtd, partition_info, ARRAY_SIZE(partition_info));

- goto out;
-
- out_mtd:
- iounmap(io_base);
-out_free:
- out:
- return err;
+ return 0;
}

/*
@@ -410,16 +398,13 @@ static int ams_delta_cleanup(struct platform_device *pdev)
{
struct ams_delta_nand *priv = platform_get_drvdata(pdev);
struct mtd_info *mtd = nand_to_mtd(&priv->nand_chip);
- void __iomem *io_base = priv->io_base;

/* Apply write protection */
gpiod_set_value(priv->gpiod_nwp, 0);

- /* Release resources, unregister device */
+ /* Unregister device */
nand_release(mtd);

- iounmap(io_base);
-
return 0;
}

--
2.16.4


2018-07-18 23:58:04

by Janusz Krzysztofik

[permalink] [raw]
Subject: [RFC PATCH 7/8] mtd: rawnand: ams-delta: Check sanity of data GPIO resource

The plan is to replace data port readw()/writew() operations with GPIO
callbacks provided by gpio-omap driver. For acceptable performance the
GPIO chip must support get/set_multiple() GPIO callbacks.

In order to avoid data corruption, we require the array of data GPIO
descriptors obtained with gpiod_get_array() to meet some strict
requirements:
- all pins must belong to the same single GPIO chip,
- array index of each pin descriptor must match its hardware number,
- pin polarity must not be inverted,
- pin hardware configuration must not be open drain nor open source.

Let's implement the above described sanity checks before replacing the
readw()/writew() operations witn GPIO callbacks. If a check fails,
return -EINVAL to indicate the board provided GPIO setup is invalid.

Signed-off-by: Janusz Krzysztofik <[email protected]>
---
drivers/mtd/nand/raw/ams-delta.c | 87 +++++++++++++++++++++++++++++++++++++++-
1 file changed, 86 insertions(+), 1 deletion(-)

diff --git a/drivers/mtd/nand/raw/ams-delta.c b/drivers/mtd/nand/raw/ams-delta.c
index ad62c0245458..bd501f385e78 100644
--- a/drivers/mtd/nand/raw/ams-delta.c
+++ b/drivers/mtd/nand/raw/ams-delta.c
@@ -21,6 +21,7 @@
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/gpio/consumer.h>
+#include <linux/gpio/driver.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/rawnand.h>
#include <linux/mtd/partitions.h>
@@ -190,7 +191,9 @@ static int ams_delta_init(struct platform_device *pdev)
struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
void __iomem *io_base;
struct gpio_descs *data_gpiods;
- int err = 0;
+ struct gpio_chip *data_gpioc;
+ unsigned long mask, bits;
+ int i, err = 0;

if (!res)
return -ENXIO;
@@ -298,6 +301,88 @@ static int ams_delta_init(struct platform_device *pdev)
goto out_mtd;
}

+ /* Use GPIO chip of first data GPIO pin descriptor */
+ data_gpioc = gpiod_to_chip(data_gpiods->desc[0]);
+
+ /*
+ * For acceptable performance require the data GPIO
+ * chip to support get/set_multiple() callbacks.
+ */
+ if (!data_gpioc->get_multiple || !data_gpioc->set_multiple) {
+ err = -EINVAL;
+ dev_err(&pdev->dev,
+ "data GPIO chip does not support get/set_multiple()\n");
+ goto out_mtd;
+ }
+
+ /* Verify if get_multiple() returns all pins low as initialized above */
+ mask = (1 << data_gpiods->ndescs) - 1;
+ err = data_gpioc->get_multiple(data_gpioc, &mask, &bits);
+ if (err) {
+ dev_err(&pdev->dev,
+ "data GPIO chip get_multiple() failed: %d\n", err);
+ goto out_mtd;
+ }
+ if (bits) {
+ err = -EINVAL;
+ dev_err(&pdev->dev,
+ "mismmatch of data GPIO initial value: %lu\n", bits);
+ goto out_mtd;
+ }
+
+ /* Verify each data GPIO pin */
+ for (i = 0; i < data_gpiods->ndescs; i++) {
+ /* Require all pins belong to the same GPIO chip */
+ if (gpiod_to_chip(data_gpiods->desc[i]) != data_gpioc) {
+ err = -EINVAL;
+ dev_err(&pdev->dev, "GPIO chip mismatch of data bit %d\n",
+ i);
+ goto out_mtd;
+ }
+
+ /* Require all pins active high (not inverted) */
+ if (gpiod_is_active_low(data_gpiods->desc[i])) {
+ err = -EINVAL;
+ dev_err(&pdev->dev,
+ "unsupported polarity of data GPIO bit %d\n",
+ i);
+ goto out_mtd;
+ }
+
+ /*
+ * Require pin gpiod array index to match hardware pin number.
+ * Verified by setting the pin high with gpiod_set_raw_value()
+ * then reading it back with gpiochip->get() for comparison.
+ */
+ gpiod_set_raw_value(data_gpiods->desc[i], 1);
+ err = data_gpioc->get(data_gpioc, i);
+ if (err < 0) {
+ dev_err(&pdev->dev,
+ "data bit %d GPIO chip get() failed: %d\n", i,
+ err);
+ goto out_mtd;
+ }
+ if (!err) {
+ err = -EINVAL;
+ dev_err(&pdev->dev, "mismatch of data GPIO bit %d value\n",
+ i);
+ goto out_mtd;
+ }
+
+ /*
+ * Check for unsupported pin hardware configuration. Use
+ * just verified gpiod array index as hardware pin number.
+ */
+ if (gpiochip_line_is_open_drain(data_gpioc, i) ||
+ gpiochip_line_is_open_source(data_gpioc, i)) {
+ err = -EINVAL;
+ dev_err(&pdev->dev,
+ "unsupported mode of data GPIO bit %d\n",
+ i);
+ goto out_mtd;
+ }
+ }
+
/* Scan to find existence of the device */
err = nand_scan(mtd, 1);
if (err)
--
2.16.4


2018-07-18 23:58:10

by Janusz Krzysztofik

[permalink] [raw]
Subject: [RFC PATCH 6/8] gpio: omap: Add get/set_multiple() callbacks

This should make applications utilizing whole banks work faster.

Signed-off-by: Janusz Krzysztofik <[email protected]>
---
drivers/gpio/gpio-omap.c | 88 ++++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 86 insertions(+), 2 deletions(-)

diff --git a/drivers/gpio/gpio-omap.c b/drivers/gpio/gpio-omap.c
index d1afedf4dcbf..e81008678a38 100644
--- a/drivers/gpio/gpio-omap.c
+++ b/drivers/gpio/gpio-omap.c
@@ -77,6 +77,8 @@ struct gpio_bank {
bool workaround_enabled;

void (*set_dataout)(struct gpio_bank *bank, unsigned gpio, int enable);
+ void (*set_dataout_multiple)(struct gpio_bank *bank,
+ unsigned long *mask, unsigned long *bits);
int (*get_context_loss_count)(struct device *dev);

struct omap_gpio_reg_offs *regs;
@@ -161,6 +163,51 @@ static int omap_get_gpio_dataout(struct gpio_bank *bank, int offset)
return (readl_relaxed(reg) & (BIT(offset))) != 0;
}

+/* set multiple data out values using dedicate set/clear register */
+static void omap_set_gpio_dataout_reg_multiple(struct gpio_bank *bank,
+ unsigned long *mask,
+ unsigned long *bits)
+{
+ void __iomem *reg = bank->base;
+ u32 l;
+
+ l = *bits & *mask;
+ writel_relaxed(l, reg + bank->regs->set_dataout);
+ bank->context.dataout |= l;
+
+ l = ~*bits & *mask;
+ writel_relaxed(l, reg + bank->regs->clr_dataout);
+ bank->context.dataout &= ~l;
+}
+
+/* set multiple data out values using mask register */
+static void omap_set_gpio_dataout_mask_multiple(struct gpio_bank *bank,
+ unsigned long *mask,
+ unsigned long *bits)
+{
+ void __iomem *reg = bank->base + bank->regs->dataout;
+ u32 l = (readl_relaxed(reg) & ~*mask) | (*bits & *mask);
+
+ writel_relaxed(l, reg);
+ bank->context.dataout = l;
+}
+
+static unsigned long omap_get_gpio_datain_multiple(struct gpio_bank *bank,
+ unsigned long *mask)
+{
+ void __iomem *reg = bank->base + bank->regs->datain;
+
+ return readl_relaxed(reg) & *mask;
+}
+
+static unsigned long omap_get_gpio_dataout_multiple(struct gpio_bank *bank,
+ unsigned long *mask)
+{
+ void __iomem *reg = bank->base + bank->regs->dataout;
+
+ return readl_relaxed(reg) & *mask;
+}
+
static inline void omap_gpio_rmw(void __iomem *base, u32 reg, u32 mask, bool set)
{
int l = readl_relaxed(base + reg);
@@ -968,6 +1015,26 @@ static int omap_gpio_output(struct gpio_chip *chip, unsigned offset, int value)
return 0;
}

+static int omap_gpio_get_multiple(struct gpio_chip *chip, unsigned long *mask,
+ unsigned long *bits)
+{
+ struct gpio_bank *bank = gpiochip_get_data(chip);
+ void __iomem *reg = bank->base + bank->regs->direction;
+ unsigned long in = readl_relaxed(reg), l;
+
+ *bits = 0;
+
+ l = in & *mask;
+ if (l)
+ *bits |= omap_get_gpio_datain_multiple(bank, &l);
+
+ l = ~in & *mask;
+ if (l)
+ *bits |= omap_get_gpio_dataout_multiple(bank, &l);
+
+ return 0;
+}
+
static int omap_gpio_debounce(struct gpio_chip *chip, unsigned offset,
unsigned debounce)
{
@@ -1012,6 +1079,17 @@ static void omap_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
raw_spin_unlock_irqrestore(&bank->lock, flags);
}

+static void omap_gpio_set_multiple(struct gpio_chip *chip, unsigned long *mask,
+ unsigned long *bits)
+{
+ struct gpio_bank *bank = gpiochip_get_data(chip);
+ unsigned long flags;
+
+ raw_spin_lock_irqsave(&bank->lock, flags);
+ bank->set_dataout_multiple(bank, mask, bits);
+ raw_spin_unlock_irqrestore(&bank->lock, flags);
+}
+
/*---------------------------------------------------------------------*/

static void omap_gpio_show_rev(struct gpio_bank *bank)
@@ -1073,9 +1151,11 @@ static int omap_gpio_chip_init(struct gpio_bank *bank, struct irq_chip *irqc)
bank->chip.get_direction = omap_gpio_get_direction;
bank->chip.direction_input = omap_gpio_input;
bank->chip.get = omap_gpio_get;
+ bank->chip.get_multiple = omap_gpio_get_multiple;
bank->chip.direction_output = omap_gpio_output;
bank->chip.set_config = omap_gpio_set_config;
bank->chip.set = omap_gpio_set;
+ bank->chip.set_multiple = omap_gpio_set_multiple;
if (bank->is_mpuio) {
bank->chip.label = "mpuio";
if (bank->regs->wkup_en)
@@ -1209,10 +1289,14 @@ static int omap_gpio_probe(struct platform_device *pdev)
pdata->get_context_loss_count;
}

- if (bank->regs->set_dataout && bank->regs->clr_dataout)
+ if (bank->regs->set_dataout && bank->regs->clr_dataout) {
bank->set_dataout = omap_set_gpio_dataout_reg;
- else
+ bank->set_dataout_multiple = omap_set_gpio_dataout_reg_multiple;
+ } else {
bank->set_dataout = omap_set_gpio_dataout_mask;
+ bank->set_dataout_multiple =
+ omap_set_gpio_dataout_mask_multiple;
+ }

raw_spin_lock_init(&bank->lock);
raw_spin_lock_init(&bank->wa_lock);
--
2.16.4


2018-07-18 23:58:19

by Janusz Krzysztofik

[permalink] [raw]
Subject: [RFC PATCH 5/8] mtd: rawnand: ams-delta: Request data port GPIO resource

Data port used by the driver is actually an OMAP MPUIO device, already
under control of gpio-omap driver. For that reason we used to not
request the memory region of the port as that would fail because the
region is already busy. Despite that, we are still accessing the port
by just ioremapping it and performing read/write operations. Moreover,
we are doing that without any proteciton from other users legally
manipulating the port pins over GPIO API.

The plan is to convert the driver to access the port over functions
exposed by the gpio-omap driver. Before that happens, we can already
obtain exclusive access to the port by requesting an array of its GPIO
descriptors. For that to work on Amstrad Delta, respective GPIO lookup
table entries are added to the board init file.

Signed-off-by: Janusz Krzysztofik <[email protected]>
---
arch/arm/mach-omap1/board-ams-delta.c | 11 ++++++++++-
drivers/mtd/nand/raw/ams-delta.c | 8 ++++++++
2 files changed, 18 insertions(+), 1 deletion(-)

diff --git a/arch/arm/mach-omap1/board-ams-delta.c b/arch/arm/mach-omap1/board-ams-delta.c
index eedacdfe9725..16f7bbe47607 100644
--- a/arch/arm/mach-omap1/board-ams-delta.c
+++ b/arch/arm/mach-omap1/board-ams-delta.c
@@ -337,7 +337,8 @@ static struct platform_device ams_delta_nand_device = {
.resource = ams_delta_nand_resources,
};

-#define OMAP_GPIO_LABEL "gpio-0-15"
+#define OMAP_GPIO_LABEL "gpio-0-15"
+#define OMAP_MPUIO_LABEL "mpuio"

static struct gpiod_lookup_table ams_delta_nand_gpio_table = {
.table = {
@@ -349,6 +350,14 @@ static struct gpiod_lookup_table ams_delta_nand_gpio_table = {
GPIO_LOOKUP(LATCH2_LABEL, LATCH2_PIN_NAND_NWE, "nwe", 0),
GPIO_LOOKUP(LATCH2_LABEL, LATCH2_PIN_NAND_ALE, "ale", 0),
GPIO_LOOKUP(LATCH2_LABEL, LATCH2_PIN_NAND_CLE, "cle", 0),
+ GPIO_LOOKUP_IDX(OMAP_MPUIO_LABEL, 0, "data", 0, 0),
+ GPIO_LOOKUP_IDX(OMAP_MPUIO_LABEL, 1, "data", 1, 0),
+ GPIO_LOOKUP_IDX(OMAP_MPUIO_LABEL, 2, "data", 2, 0),
+ GPIO_LOOKUP_IDX(OMAP_MPUIO_LABEL, 3, "data", 3, 0),
+ GPIO_LOOKUP_IDX(OMAP_MPUIO_LABEL, 4, "data", 4, 0),
+ GPIO_LOOKUP_IDX(OMAP_MPUIO_LABEL, 5, "data", 5, 0),
+ GPIO_LOOKUP_IDX(OMAP_MPUIO_LABEL, 6, "data", 6, 0),
+ GPIO_LOOKUP_IDX(OMAP_MPUIO_LABEL, 7, "data", 7, 0),
{ },
},
};
diff --git a/drivers/mtd/nand/raw/ams-delta.c b/drivers/mtd/nand/raw/ams-delta.c
index d7e4c9dbef67..ad62c0245458 100644
--- a/drivers/mtd/nand/raw/ams-delta.c
+++ b/drivers/mtd/nand/raw/ams-delta.c
@@ -189,6 +189,7 @@ static int ams_delta_init(struct platform_device *pdev)
struct mtd_info *mtd;
struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
void __iomem *io_base;
+ struct gpio_descs *data_gpiods;
int err = 0;

if (!res)
@@ -289,6 +290,13 @@ static int ams_delta_init(struct platform_device *pdev)
dev_err(&pdev->dev, "CLE GPIO request failed (%d)\n", err);
goto out_mtd;
}
+ /* Request array of data pins, initialize them as output and set low */
+ data_gpiods = devm_gpiod_get_array(&pdev->dev, "data", GPIOD_OUT_LOW);
+ if (IS_ERR(data_gpiods)) {
+ err = PTR_ERR(data_gpiods);
+ dev_err(&pdev->dev, "data GPIO request failed: %d\n", err);
+ goto out_mtd;
+ }

/* Scan to find existence of the device */
err = nand_scan(mtd, 1);
--
2.16.4


2018-07-18 23:58:28

by Janusz Krzysztofik

[permalink] [raw]
Subject: [RFC PATCH 4/8] mtd: rawnand: ams-delta: Optimize pointer resolution on read/write

Further optimize processing speed of read/write callback functions by
resolving private structure pointer only once per callback and passing
it to all subfunctions instead of mtd_info.

Signed-off-by: Janusz Krzysztofik <[email protected]>
---
drivers/mtd/nand/raw/ams-delta.c | 44 +++++++++++++++++++++++-----------------
1 file changed, 25 insertions(+), 19 deletions(-)

diff --git a/drivers/mtd/nand/raw/ams-delta.c b/drivers/mtd/nand/raw/ams-delta.c
index dfefcd79b420..d7e4c9dbef67 100644
--- a/drivers/mtd/nand/raw/ams-delta.c
+++ b/drivers/mtd/nand/raw/ams-delta.c
@@ -72,10 +72,9 @@ static const struct mtd_partition partition_info[] = {
.size = 3 * SZ_256K },
};

-static void ams_delta_write_next_byte(struct mtd_info *mtd, u_char byte)
+static void ams_delta_write_next_byte(struct ams_delta_nand *priv, u_char byte)
{
- struct nand_chip *this = mtd_to_nand(mtd);
- struct ams_delta_nand *priv = nand_get_controller_data(this);
+ struct nand_chip *this = &priv->nand_chip;

writew(byte, this->IO_ADDR_W);
gpiod_set_value(priv->gpiod_nwe, 0);
@@ -83,21 +82,18 @@ static void ams_delta_write_next_byte(struct mtd_info *mtd, u_char byte)
gpiod_set_value(priv->gpiod_nwe, 1);
}

-static void ams_delta_write_byte(struct mtd_info *mtd, u_char byte)
+static void ams_delta_write_first_byte(struct ams_delta_nand *priv, u_char byte)
{
- struct nand_chip *this = mtd_to_nand(mtd);
- struct ams_delta_nand *priv = nand_get_controller_data(this);
void __iomem *io_base = priv->io_base;

writew(0, io_base + OMAP_MPUIO_IO_CNTL);

- ams_delta_write_next_byte(mtd, byte);
+ ams_delta_write_next_byte(priv, byte);
}

-static u_char ams_delta_read_next_byte(struct mtd_info *mtd)
+static u_char ams_delta_read_next_byte(struct ams_delta_nand *priv)
{
- struct nand_chip *this = mtd_to_nand(mtd);
- struct ams_delta_nand *priv = nand_get_controller_data(this);
+ struct nand_chip *this = &priv->nand_chip;
u_char res;

gpiod_set_value(priv->gpiod_nre, 0);
@@ -108,36 +104,46 @@ static u_char ams_delta_read_next_byte(struct mtd_info *mtd)
return res;
}

-static u_char ams_delta_read_byte(struct mtd_info *mtd)
+static u_char ams_delta_read_first_byte(struct ams_delta_nand *priv)
{
- struct nand_chip *this = mtd_to_nand(mtd);
- struct ams_delta_nand *priv = nand_get_controller_data(this);
void __iomem *io_base = priv->io_base;

writew(~0, io_base + OMAP_MPUIO_IO_CNTL);

- return ams_delta_read_next_byte(mtd);
+ return ams_delta_read_next_byte(priv);
+}
+
+static u_char ams_delta_read_byte(struct mtd_info *mtd)
+{
+ struct nand_chip *this = mtd_to_nand(mtd);
+ struct ams_delta_nand *priv = nand_get_controller_data(this);
+
+ return ams_delta_read_first_byte(priv);
}

static void ams_delta_write_buf(struct mtd_info *mtd, const u_char *buf,
int len)
{
+ struct nand_chip *this = mtd_to_nand(mtd);
+ struct ams_delta_nand *priv = nand_get_controller_data(this);
int i;

if (len > 0)
- ams_delta_write_byte(mtd, buf[0]);
+ ams_delta_write_first_byte(priv, buf[0]);
for (i = 1; i < len; i++)
- ams_delta_write_next_byte(mtd, buf[i]);
+ ams_delta_write_next_byte(priv, buf[i]);
}

static void ams_delta_read_buf(struct mtd_info *mtd, u_char *buf, int len)
{
+ struct nand_chip *this = mtd_to_nand(mtd);
+ struct ams_delta_nand *priv = nand_get_controller_data(this);
int i;

if (len > 0)
- buf[0] = ams_delta_read_byte(mtd);
+ buf[0] = ams_delta_read_first_byte(priv);
for (i = 1; i < len; i++)
- buf[i] = ams_delta_read_next_byte(mtd);
+ buf[i] = ams_delta_read_next_byte(priv);
}

/*
@@ -161,7 +167,7 @@ static void ams_delta_hwcontrol(struct mtd_info *mtd, int cmd,
}

if (cmd != NAND_CMD_NONE)
- ams_delta_write_byte(mtd, cmd);
+ ams_delta_write_first_byte(priv, cmd);
}

static int ams_delta_nand_ready(struct mtd_info *mtd)
--
2.16.4


2018-07-18 23:58:29

by Janusz Krzysztofik

[permalink] [raw]
Subject: [RFC PATCH 1/8] mtd: rawnand: ams-delta: Use private structure

Introduce a driver private structure and allocate it on device probe.
Use it for storing nand_chip structure, GPIO descriptors prevoiusly
stored in static variables as well as io_base pointer previously passed
as nand controller data or platform driver data. Subsequent patches
may populate the structure with more members as needed.

While at it, fix missing mtd->dev.parent and drop useless mtd->owner.

Signed-off-by: Janusz Krzysztofik <[email protected]>
---
drivers/mtd/nand/raw/ams-delta.c | 126 +++++++++++++++++++++------------------
1 file changed, 69 insertions(+), 57 deletions(-)

diff --git a/drivers/mtd/nand/raw/ams-delta.c b/drivers/mtd/nand/raw/ams-delta.c
index 2a8872ebd14a..48233d638d2a 100644
--- a/drivers/mtd/nand/raw/ams-delta.c
+++ b/drivers/mtd/nand/raw/ams-delta.c
@@ -34,14 +34,18 @@
/*
* MTD structure for E3 (Delta)
*/
-static struct mtd_info *ams_delta_mtd = NULL;
-static struct gpio_desc *gpiod_rdy;
-static struct gpio_desc *gpiod_nce;
-static struct gpio_desc *gpiod_nre;
-static struct gpio_desc *gpiod_nwp;
-static struct gpio_desc *gpiod_nwe;
-static struct gpio_desc *gpiod_ale;
-static struct gpio_desc *gpiod_cle;
+
+struct ams_delta_nand {
+ struct nand_chip nand_chip;
+ struct gpio_desc *gpiod_rdy;
+ struct gpio_desc *gpiod_nce;
+ struct gpio_desc *gpiod_nre;
+ struct gpio_desc *gpiod_nwp;
+ struct gpio_desc *gpiod_nwe;
+ struct gpio_desc *gpiod_ale;
+ struct gpio_desc *gpiod_cle;
+ void __iomem *io_base;
+};

/*
* Define partitions for flash devices
@@ -71,26 +75,28 @@ static const struct mtd_partition partition_info[] = {
static void ams_delta_write_byte(struct mtd_info *mtd, u_char byte)
{
struct nand_chip *this = mtd_to_nand(mtd);
- void __iomem *io_base = (void __iomem *)nand_get_controller_data(this);
+ struct ams_delta_nand *priv = nand_get_controller_data(this);
+ void __iomem *io_base = priv->io_base;

writew(0, io_base + OMAP_MPUIO_IO_CNTL);
writew(byte, this->IO_ADDR_W);
- gpiod_set_value(gpiod_nwe, 0);
+ gpiod_set_value(priv->gpiod_nwe, 0);
ndelay(40);
- gpiod_set_value(gpiod_nwe, 1);
+ gpiod_set_value(priv->gpiod_nwe, 1);
}

static u_char ams_delta_read_byte(struct mtd_info *mtd)
{
u_char res;
struct nand_chip *this = mtd_to_nand(mtd);
- void __iomem *io_base = (void __iomem *)nand_get_controller_data(this);
+ struct ams_delta_nand *priv = nand_get_controller_data(this);
+ void __iomem *io_base = priv->io_base;

- gpiod_set_value(gpiod_nre, 0);
+ gpiod_set_value(priv->gpiod_nre, 0);
ndelay(40);
writew(~0, io_base + OMAP_MPUIO_IO_CNTL);
res = readw(this->IO_ADDR_R);
- gpiod_set_value(gpiod_nre, 1);
+ gpiod_set_value(priv->gpiod_nre, 1);

return res;
}
@@ -123,11 +129,13 @@ static void ams_delta_read_buf(struct mtd_info *mtd, u_char *buf, int len)
static void ams_delta_hwcontrol(struct mtd_info *mtd, int cmd,
unsigned int ctrl)
{
+ struct nand_chip *this = mtd_to_nand(mtd);
+ struct ams_delta_nand *priv = nand_get_controller_data(this);

if (ctrl & NAND_CTRL_CHANGE) {
- gpiod_set_value(gpiod_nce, !(ctrl & NAND_NCE));
- gpiod_set_value(gpiod_cle, !!(ctrl & NAND_CLE));
- gpiod_set_value(gpiod_ale, !!(ctrl & NAND_ALE));
+ gpiod_set_value(priv->gpiod_nce, !(ctrl & NAND_NCE));
+ gpiod_set_value(priv->gpiod_cle, !!(ctrl & NAND_CLE));
+ gpiod_set_value(priv->gpiod_ale, !!(ctrl & NAND_ALE));
}

if (cmd != NAND_CMD_NONE)
@@ -136,7 +144,10 @@ static void ams_delta_hwcontrol(struct mtd_info *mtd, int cmd,

static int ams_delta_nand_ready(struct mtd_info *mtd)
{
- return gpiod_get_value(gpiod_rdy);
+ struct nand_chip *this = mtd_to_nand(mtd);
+ struct ams_delta_nand *priv = nand_get_controller_data(this);
+
+ return gpiod_get_value(priv->gpiod_rdy);
}


@@ -145,7 +156,9 @@ static int ams_delta_nand_ready(struct mtd_info *mtd)
*/
static int ams_delta_init(struct platform_device *pdev)
{
+ struct ams_delta_nand *priv;
struct nand_chip *this;
+ struct mtd_info *mtd;
struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
void __iomem *io_base;
int err = 0;
@@ -154,15 +167,16 @@ static int ams_delta_init(struct platform_device *pdev)
return -ENXIO;

/* Allocate memory for MTD device structure and private data */
- this = kzalloc(sizeof(struct nand_chip), GFP_KERNEL);
- if (!this) {
+ priv = devm_kzalloc(&pdev->dev, sizeof(struct ams_delta_nand),
+ GFP_KERNEL);
+ if (!priv) {
pr_warn("Unable to allocate E3 NAND MTD device structure.\n");
- err = -ENOMEM;
- goto out;
+ return -ENOMEM;
}
+ this = &priv->nand_chip;

- ams_delta_mtd = nand_to_mtd(this);
- ams_delta_mtd->owner = THIS_MODULE;
+ mtd = nand_to_mtd(this);
+ mtd->dev.parent = &pdev->dev;

/*
* Don't try to request the memory region from here,
@@ -177,7 +191,8 @@ static int ams_delta_init(struct platform_device *pdev)
goto out_free;
}

- nand_set_controller_data(this, (void *)io_base);
+ priv->io_base = io_base;
+ nand_set_controller_data(this, priv);

/* Set address of NAND IO lines */
this->IO_ADDR_R = io_base + OMAP_MPUIO_INPUT_LATCH;
@@ -187,14 +202,14 @@ static int ams_delta_init(struct platform_device *pdev)
this->read_buf = ams_delta_read_buf;
this->cmd_ctrl = ams_delta_hwcontrol;

- gpiod_rdy = devm_gpiod_get_optional(&pdev->dev, "rdy", GPIOD_IN);
- if (IS_ERR(gpiod_rdy)) {
- err = PTR_ERR(gpiod_rdy);
+ priv->gpiod_rdy = devm_gpiod_get_optional(&pdev->dev, "rdy", GPIOD_IN);
+ if (IS_ERR(priv->gpiod_rdy)) {
+ err = PTR_ERR(priv->gpiod_rdy);
dev_warn(&pdev->dev, "RDY GPIO request failed (%d)\n", err);
goto out_mtd;
}

- if (gpiod_rdy)
+ if (priv->gpiod_rdy)
this->dev_ready = ams_delta_nand_ready;

/* 25 us command delay time */
@@ -202,66 +217,64 @@ static int ams_delta_init(struct platform_device *pdev)
this->ecc.mode = NAND_ECC_SOFT;
this->ecc.algo = NAND_ECC_HAMMING;

- platform_set_drvdata(pdev, io_base);
+ platform_set_drvdata(pdev, priv);

/* Set chip enabled, but */
- gpiod_nwp = devm_gpiod_get(&pdev->dev, "nwp", GPIOD_OUT_HIGH);
- if (IS_ERR(gpiod_nwp)) {
- err = PTR_ERR(gpiod_nwp);
+ priv->gpiod_nwp = devm_gpiod_get(&pdev->dev, "nwp", GPIOD_OUT_HIGH);
+ if (IS_ERR(priv->gpiod_nwp)) {
+ err = PTR_ERR(priv->gpiod_nwp);
dev_err(&pdev->dev, "NWP GPIO request failed (%d)\n", err);
goto out_mtd;
}

- gpiod_nce = devm_gpiod_get(&pdev->dev, "nce", GPIOD_OUT_HIGH);
- if (IS_ERR(gpiod_nce)) {
- err = PTR_ERR(gpiod_nce);
+ priv->gpiod_nce = devm_gpiod_get(&pdev->dev, "nce", GPIOD_OUT_HIGH);
+ if (IS_ERR(priv->gpiod_nce)) {
+ err = PTR_ERR(priv->gpiod_nce);
dev_err(&pdev->dev, "NCE GPIO request failed (%d)\n", err);
goto out_mtd;
}

- gpiod_nre = devm_gpiod_get(&pdev->dev, "nre", GPIOD_OUT_HIGH);
- if (IS_ERR(gpiod_nre)) {
- err = PTR_ERR(gpiod_nre);
+ priv->gpiod_nre = devm_gpiod_get(&pdev->dev, "nre", GPIOD_OUT_HIGH);
+ if (IS_ERR(priv->gpiod_nre)) {
+ err = PTR_ERR(priv->gpiod_nre);
dev_err(&pdev->dev, "NRE GPIO request failed (%d)\n", err);
goto out_mtd;
}

- gpiod_nwe = devm_gpiod_get(&pdev->dev, "nwe", GPIOD_OUT_HIGH);
- if (IS_ERR(gpiod_nwe)) {
- err = PTR_ERR(gpiod_nwe);
+ priv->gpiod_nwe = devm_gpiod_get(&pdev->dev, "nwe", GPIOD_OUT_HIGH);
+ if (IS_ERR(priv->gpiod_nwe)) {
+ err = PTR_ERR(priv->gpiod_nwe);
dev_err(&pdev->dev, "NWE GPIO request failed (%d)\n", err);
goto out_mtd;
}

- gpiod_ale = devm_gpiod_get(&pdev->dev, "ale", GPIOD_OUT_LOW);
- if (IS_ERR(gpiod_ale)) {
- err = PTR_ERR(gpiod_ale);
+ priv->gpiod_ale = devm_gpiod_get(&pdev->dev, "ale", GPIOD_OUT_LOW);
+ if (IS_ERR(priv->gpiod_ale)) {
+ err = PTR_ERR(priv->gpiod_ale);
dev_err(&pdev->dev, "ALE GPIO request failed (%d)\n", err);
goto out_mtd;
}

- gpiod_cle = devm_gpiod_get(&pdev->dev, "cle", GPIOD_OUT_LOW);
- if (IS_ERR(gpiod_cle)) {
- err = PTR_ERR(gpiod_cle);
+ priv->gpiod_cle = devm_gpiod_get(&pdev->dev, "cle", GPIOD_OUT_LOW);
+ if (IS_ERR(priv->gpiod_cle)) {
+ err = PTR_ERR(priv->gpiod_cle);
dev_err(&pdev->dev, "CLE GPIO request failed (%d)\n", err);
goto out_mtd;
}

/* Scan to find existence of the device */
- err = nand_scan(ams_delta_mtd, 1);
+ err = nand_scan(mtd, 1);
if (err)
goto out_mtd;

/* Register the partitions */
- mtd_device_register(ams_delta_mtd, partition_info,
- ARRAY_SIZE(partition_info));
+ mtd_device_register(mtd, partition_info, ARRAY_SIZE(partition_info));

goto out;

out_mtd:
iounmap(io_base);
out_free:
- kfree(this);
out:
return err;
}
@@ -271,16 +284,15 @@ static int ams_delta_init(struct platform_device *pdev)
*/
static int ams_delta_cleanup(struct platform_device *pdev)
{
- void __iomem *io_base = platform_get_drvdata(pdev);
+ struct ams_delta_nand *priv = platform_get_drvdata(pdev);
+ struct mtd_info *mtd = nand_to_mtd(&priv->nand_chip);
+ void __iomem *io_base = priv->io_base;

/* Release resources, unregister device */
- nand_release(ams_delta_mtd);
+ nand_release(mtd);

iounmap(io_base);

- /* Free the MTD device structure */
- kfree(mtd_to_nand(ams_delta_mtd));
-
return 0;
}

--
2.16.4


2018-07-18 23:59:13

by Janusz Krzysztofik

[permalink] [raw]
Subject: [RFC PATCH 3/8] mtd: rawnand: ams-delta: Set port direction once per transfer

In its current shape, the driver sets data port direction before each
byte read/write operation, even during multi-byte transfers. Optimize
that by setting the port direction only on first byte of each transfer.

Signed-off-by: Janusz Krzysztofik <[email protected]>
---
drivers/mtd/nand/raw/ams-delta.c | 42 ++++++++++++++++++++++++++++++----------
1 file changed, 32 insertions(+), 10 deletions(-)

diff --git a/drivers/mtd/nand/raw/ams-delta.c b/drivers/mtd/nand/raw/ams-delta.c
index 6ac38e9cfa1a..dfefcd79b420 100644
--- a/drivers/mtd/nand/raw/ams-delta.c
+++ b/drivers/mtd/nand/raw/ams-delta.c
@@ -72,50 +72,72 @@ static const struct mtd_partition partition_info[] = {
.size = 3 * SZ_256K },
};

-static void ams_delta_write_byte(struct mtd_info *mtd, u_char byte)
+static void ams_delta_write_next_byte(struct mtd_info *mtd, u_char byte)
{
struct nand_chip *this = mtd_to_nand(mtd);
struct ams_delta_nand *priv = nand_get_controller_data(this);
- void __iomem *io_base = priv->io_base;

- writew(0, io_base + OMAP_MPUIO_IO_CNTL);
writew(byte, this->IO_ADDR_W);
gpiod_set_value(priv->gpiod_nwe, 0);
ndelay(40);
gpiod_set_value(priv->gpiod_nwe, 1);
}

-static u_char ams_delta_read_byte(struct mtd_info *mtd)
+static void ams_delta_write_byte(struct mtd_info *mtd, u_char byte)
{
- u_char res;
struct nand_chip *this = mtd_to_nand(mtd);
struct ams_delta_nand *priv = nand_get_controller_data(this);
void __iomem *io_base = priv->io_base;

+ writew(0, io_base + OMAP_MPUIO_IO_CNTL);
+
+ ams_delta_write_next_byte(mtd, byte);
+}
+
+static u_char ams_delta_read_next_byte(struct mtd_info *mtd)
+{
+ struct nand_chip *this = mtd_to_nand(mtd);
+ struct ams_delta_nand *priv = nand_get_controller_data(this);
+ u_char res;
+
gpiod_set_value(priv->gpiod_nre, 0);
ndelay(40);
- writew(~0, io_base + OMAP_MPUIO_IO_CNTL);
res = readw(this->IO_ADDR_R);
gpiod_set_value(priv->gpiod_nre, 1);

return res;
}

+static u_char ams_delta_read_byte(struct mtd_info *mtd)
+{
+ struct nand_chip *this = mtd_to_nand(mtd);
+ struct ams_delta_nand *priv = nand_get_controller_data(this);
+ void __iomem *io_base = priv->io_base;
+
+ writew(~0, io_base + OMAP_MPUIO_IO_CNTL);
+
+ return ams_delta_read_next_byte(mtd);
+}
+
static void ams_delta_write_buf(struct mtd_info *mtd, const u_char *buf,
int len)
{
int i;

- for (i=0; i<len; i++)
- ams_delta_write_byte(mtd, buf[i]);
+ if (len > 0)
+ ams_delta_write_byte(mtd, buf[0]);
+ for (i = 1; i < len; i++)
+ ams_delta_write_next_byte(mtd, buf[i]);
}

static void ams_delta_read_buf(struct mtd_info *mtd, u_char *buf, int len)
{
int i;

- for (i=0; i<len; i++)
- buf[i] = ams_delta_read_byte(mtd);
+ if (len > 0)
+ buf[0] = ams_delta_read_byte(mtd);
+ for (i = 1; i < len; i++)
+ buf[i] = ams_delta_read_next_byte(mtd);
}

/*
--
2.16.4


2018-07-18 23:59:40

by Janusz Krzysztofik

[permalink] [raw]
Subject: [RFC PATCH 2/8] mtd: rawnand: ams-delta: Write protect device during probe

Initialize NWP GPIO pin low to protect the device from hazard during
probe. Release write protection as soon as the device is under
control.

Signed-off-by: Janusz Krzysztofik <[email protected]>
---
drivers/mtd/nand/raw/ams-delta.c | 10 ++++++++--
1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/drivers/mtd/nand/raw/ams-delta.c b/drivers/mtd/nand/raw/ams-delta.c
index 48233d638d2a..6ac38e9cfa1a 100644
--- a/drivers/mtd/nand/raw/ams-delta.c
+++ b/drivers/mtd/nand/raw/ams-delta.c
@@ -219,8 +219,8 @@ static int ams_delta_init(struct platform_device *pdev)

platform_set_drvdata(pdev, priv);

- /* Set chip enabled, but */
- priv->gpiod_nwp = devm_gpiod_get(&pdev->dev, "nwp", GPIOD_OUT_HIGH);
+ /* Set chip enabled but write protected */
+ priv->gpiod_nwp = devm_gpiod_get(&pdev->dev, "nwp", GPIOD_OUT_LOW);
if (IS_ERR(priv->gpiod_nwp)) {
err = PTR_ERR(priv->gpiod_nwp);
dev_err(&pdev->dev, "NWP GPIO request failed (%d)\n", err);
@@ -267,6 +267,9 @@ static int ams_delta_init(struct platform_device *pdev)
if (err)
goto out_mtd;

+ /* As soon as the device is found, release write protection */
+ gpiod_set_value(priv->gpiod_nwp, 1);
+
/* Register the partitions */
mtd_device_register(mtd, partition_info, ARRAY_SIZE(partition_info));

@@ -288,6 +291,9 @@ static int ams_delta_cleanup(struct platform_device *pdev)
struct mtd_info *mtd = nand_to_mtd(&priv->nand_chip);
void __iomem *io_base = priv->io_base;

+ /* Apply write protection */
+ gpiod_set_value(priv->gpiod_nwp, 0);
+
/* Release resources, unregister device */
nand_release(mtd);

--
2.16.4


2018-07-19 06:16:02

by Boris Brezillon

[permalink] [raw]
Subject: Re: [RFC PATCH 0/8] mtd: rawnand: ams-delta: Use gpio-omap accessors for data I/O

Hi Janusz,

On Thu, 19 Jul 2018 01:57:02 +0200
Janusz Krzysztofik <[email protected]> wrote:

> Implement the idea suggested by Artem Bityutskiy and Tony Lindgren
> described in commit b027274d2e3a ("mtd: ams-delta: fix
> request_mem_region() failure").

Thanks for doing that. I'll review the patches, but I already have a
extra request: can you convert this driver to ->exec_op()? It does not
necessarily have to be done in this patchset, but, that'd be great to
have one more driver converted to the new interface.

BTW, there seems to be a nand_gpio driver, maybe we should merge
ams-delta code into nand_gpio after it's been patched to use the GPIO
consumer API.

>
> arch/arm/mach-omap1/board-ams-delta.c | 22 -
> drivers/gpio/gpio-omap.c | 88 ++++++
> drivers/mtd/nand/raw/ams-delta.c | 436 +++++++++++++++++++++-------------
> 3 files changed, 376 insertions(+), 170 deletions(-)
>


2018-07-19 06:18:20

by Boris Brezillon

[permalink] [raw]
Subject: Re: [RFC PATCH 1/8] mtd: rawnand: ams-delta: Use private structure

On Thu, 19 Jul 2018 01:57:03 +0200
Janusz Krzysztofik <[email protected]> wrote:

> Introduce a driver private structure and allocate it on device probe.
> Use it for storing nand_chip structure, GPIO descriptors prevoiusly
> stored in static variables as well as io_base pointer previously passed
> as nand controller data or platform driver data.

That's a good thing. Thanks for doing that.

> Subsequent patches
> may populate the structure with more members as needed.
>
> While at it, fix missing mtd->dev.parent and drop useless mtd->owner.

I'd prefer to have it done in a separate patch if you don't mind.

2018-07-19 06:23:25

by Boris Brezillon

[permalink] [raw]
Subject: Re: [RFC PATCH 2/8] mtd: rawnand: ams-delta: Write protect device during probe

On Thu, 19 Jul 2018 01:57:04 +0200
Janusz Krzysztofik <[email protected]> wrote:

> Initialize NWP GPIO pin low to protect the device from hazard during
> probe. Release write protection as soon as the device is under
> control.
>
> Signed-off-by: Janusz Krzysztofik <[email protected]>
> ---
> drivers/mtd/nand/raw/ams-delta.c | 10 ++++++++--
> 1 file changed, 8 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/mtd/nand/raw/ams-delta.c b/drivers/mtd/nand/raw/ams-delta.c
> index 48233d638d2a..6ac38e9cfa1a 100644
> --- a/drivers/mtd/nand/raw/ams-delta.c
> +++ b/drivers/mtd/nand/raw/ams-delta.c
> @@ -219,8 +219,8 @@ static int ams_delta_init(struct platform_device *pdev)
>
> platform_set_drvdata(pdev, priv);
>
> - /* Set chip enabled, but */
> - priv->gpiod_nwp = devm_gpiod_get(&pdev->dev, "nwp", GPIOD_OUT_HIGH);
> + /* Set chip enabled but write protected */
> + priv->gpiod_nwp = devm_gpiod_get(&pdev->dev, "nwp", GPIOD_OUT_LOW);
> if (IS_ERR(priv->gpiod_nwp)) {
> err = PTR_ERR(priv->gpiod_nwp);
> dev_err(&pdev->dev, "NWP GPIO request failed (%d)\n", err);
> @@ -267,6 +267,9 @@ static int ams_delta_init(struct platform_device *pdev)
> if (err)
> goto out_mtd;
>
> + /* As soon as the device is found, release write protection */
> + gpiod_set_value(priv->gpiod_nwp, 1);

Please don't do that until we have a generic way to assert/deassert WP
from the core. I agree that we shouldn't write things in the
nand_scan() path, but I'd be more comfortable if the core had control
on this pin just in case we ever need to disable write-protection
during chip detection/initialization.

> +
> /* Register the partitions */
> mtd_device_register(mtd, partition_info, ARRAY_SIZE(partition_info));
>
> @@ -288,6 +291,9 @@ static int ams_delta_cleanup(struct platform_device *pdev)
> struct mtd_info *mtd = nand_to_mtd(&priv->nand_chip);
> void __iomem *io_base = priv->io_base;
>
> + /* Apply write protection */
> + gpiod_set_value(priv->gpiod_nwp, 0);
> +
> /* Release resources, unregister device */
> nand_release(mtd);
>


2018-07-19 06:24:46

by Boris Brezillon

[permalink] [raw]
Subject: Re: [RFC PATCH 3/8] mtd: rawnand: ams-delta: Set port direction once per transfer

On Thu, 19 Jul 2018 01:57:05 +0200
Janusz Krzysztofik <[email protected]> wrote:

> In its current shape, the driver sets data port direction before each
> byte read/write operation, even during multi-byte transfers. Optimize
> that by setting the port direction only on first byte of each transfer.

Sounds like premature optimization for something you'll rework when
fully switching to the GPIO consumer API to control the DATA bus.

>
> Signed-off-by: Janusz Krzysztofik <[email protected]>
> ---
> drivers/mtd/nand/raw/ams-delta.c | 42 ++++++++++++++++++++++++++++++----------
> 1 file changed, 32 insertions(+), 10 deletions(-)
>
> diff --git a/drivers/mtd/nand/raw/ams-delta.c b/drivers/mtd/nand/raw/ams-delta.c
> index 6ac38e9cfa1a..dfefcd79b420 100644
> --- a/drivers/mtd/nand/raw/ams-delta.c
> +++ b/drivers/mtd/nand/raw/ams-delta.c
> @@ -72,50 +72,72 @@ static const struct mtd_partition partition_info[] = {
> .size = 3 * SZ_256K },
> };
>
> -static void ams_delta_write_byte(struct mtd_info *mtd, u_char byte)
> +static void ams_delta_write_next_byte(struct mtd_info *mtd, u_char byte)
> {
> struct nand_chip *this = mtd_to_nand(mtd);
> struct ams_delta_nand *priv = nand_get_controller_data(this);
> - void __iomem *io_base = priv->io_base;
>
> - writew(0, io_base + OMAP_MPUIO_IO_CNTL);
> writew(byte, this->IO_ADDR_W);
> gpiod_set_value(priv->gpiod_nwe, 0);
> ndelay(40);
> gpiod_set_value(priv->gpiod_nwe, 1);
> }
>
> -static u_char ams_delta_read_byte(struct mtd_info *mtd)
> +static void ams_delta_write_byte(struct mtd_info *mtd, u_char byte)
> {
> - u_char res;
> struct nand_chip *this = mtd_to_nand(mtd);
> struct ams_delta_nand *priv = nand_get_controller_data(this);
> void __iomem *io_base = priv->io_base;
>
> + writew(0, io_base + OMAP_MPUIO_IO_CNTL);
> +
> + ams_delta_write_next_byte(mtd, byte);
> +}
> +
> +static u_char ams_delta_read_next_byte(struct mtd_info *mtd)
> +{
> + struct nand_chip *this = mtd_to_nand(mtd);
> + struct ams_delta_nand *priv = nand_get_controller_data(this);
> + u_char res;
> +
> gpiod_set_value(priv->gpiod_nre, 0);
> ndelay(40);
> - writew(~0, io_base + OMAP_MPUIO_IO_CNTL);
> res = readw(this->IO_ADDR_R);
> gpiod_set_value(priv->gpiod_nre, 1);
>
> return res;
> }
>
> +static u_char ams_delta_read_byte(struct mtd_info *mtd)
> +{
> + struct nand_chip *this = mtd_to_nand(mtd);
> + struct ams_delta_nand *priv = nand_get_controller_data(this);
> + void __iomem *io_base = priv->io_base;
> +
> + writew(~0, io_base + OMAP_MPUIO_IO_CNTL);
> +
> + return ams_delta_read_next_byte(mtd);
> +}
> +
> static void ams_delta_write_buf(struct mtd_info *mtd, const u_char *buf,
> int len)
> {
> int i;
>
> - for (i=0; i<len; i++)
> - ams_delta_write_byte(mtd, buf[i]);
> + if (len > 0)
> + ams_delta_write_byte(mtd, buf[0]);
> + for (i = 1; i < len; i++)
> + ams_delta_write_next_byte(mtd, buf[i]);
> }
>
> static void ams_delta_read_buf(struct mtd_info *mtd, u_char *buf, int len)
> {
> int i;
>
> - for (i=0; i<len; i++)
> - buf[i] = ams_delta_read_byte(mtd);
> + if (len > 0)
> + buf[0] = ams_delta_read_byte(mtd);
> + for (i = 1; i < len; i++)
> + buf[i] = ams_delta_read_next_byte(mtd);
> }
>
> /*


2018-07-19 06:28:09

by Boris Brezillon

[permalink] [raw]
Subject: Re: [RFC PATCH 4/8] mtd: rawnand: ams-delta: Optimize pointer resolution on read/write

On Thu, 19 Jul 2018 01:57:06 +0200
Janusz Krzysztofik <[email protected]> wrote:

> Further optimize processing speed of read/write callback functions by
> resolving private structure pointer only once per callback and passing
> it to all subfunctions instead of mtd_info.

Not sure this has a real impact on perfs, but I also prefer not using
mtd_info objects within NAND drivers, so I'm good with the change
itself (its probably worth changing the commit message though)

>
> Signed-off-by: Janusz Krzysztofik <[email protected]>
> ---
> drivers/mtd/nand/raw/ams-delta.c | 44 +++++++++++++++++++++++-----------------
> 1 file changed, 25 insertions(+), 19 deletions(-)
>
> diff --git a/drivers/mtd/nand/raw/ams-delta.c b/drivers/mtd/nand/raw/ams-delta.c
> index dfefcd79b420..d7e4c9dbef67 100644
> --- a/drivers/mtd/nand/raw/ams-delta.c
> +++ b/drivers/mtd/nand/raw/ams-delta.c
> @@ -72,10 +72,9 @@ static const struct mtd_partition partition_info[] = {
> .size = 3 * SZ_256K },
> };
>
> -static void ams_delta_write_next_byte(struct mtd_info *mtd, u_char byte)
> +static void ams_delta_write_next_byte(struct ams_delta_nand *priv, u_char byte)
> {
> - struct nand_chip *this = mtd_to_nand(mtd);
> - struct ams_delta_nand *priv = nand_get_controller_data(this);
> + struct nand_chip *this = &priv->nand_chip;
>
> writew(byte, this->IO_ADDR_W);
> gpiod_set_value(priv->gpiod_nwe, 0);
> @@ -83,21 +82,18 @@ static void ams_delta_write_next_byte(struct mtd_info *mtd, u_char byte)
> gpiod_set_value(priv->gpiod_nwe, 1);
> }
>
> -static void ams_delta_write_byte(struct mtd_info *mtd, u_char byte)
> +static void ams_delta_write_first_byte(struct ams_delta_nand *priv, u_char byte)
> {
> - struct nand_chip *this = mtd_to_nand(mtd);
> - struct ams_delta_nand *priv = nand_get_controller_data(this);
> void __iomem *io_base = priv->io_base;
>
> writew(0, io_base + OMAP_MPUIO_IO_CNTL);
>
> - ams_delta_write_next_byte(mtd, byte);
> + ams_delta_write_next_byte(priv, byte);
> }
>
> -static u_char ams_delta_read_next_byte(struct mtd_info *mtd)
> +static u_char ams_delta_read_next_byte(struct ams_delta_nand *priv)
> {
> - struct nand_chip *this = mtd_to_nand(mtd);
> - struct ams_delta_nand *priv = nand_get_controller_data(this);
> + struct nand_chip *this = &priv->nand_chip;
> u_char res;
>
> gpiod_set_value(priv->gpiod_nre, 0);
> @@ -108,36 +104,46 @@ static u_char ams_delta_read_next_byte(struct mtd_info *mtd)
> return res;
> }
>
> -static u_char ams_delta_read_byte(struct mtd_info *mtd)
> +static u_char ams_delta_read_first_byte(struct ams_delta_nand *priv)
> {
> - struct nand_chip *this = mtd_to_nand(mtd);
> - struct ams_delta_nand *priv = nand_get_controller_data(this);
> void __iomem *io_base = priv->io_base;
>
> writew(~0, io_base + OMAP_MPUIO_IO_CNTL);
>
> - return ams_delta_read_next_byte(mtd);
> + return ams_delta_read_next_byte(priv);
> +}
> +
> +static u_char ams_delta_read_byte(struct mtd_info *mtd)
> +{
> + struct nand_chip *this = mtd_to_nand(mtd);
> + struct ams_delta_nand *priv = nand_get_controller_data(this);
> +
> + return ams_delta_read_first_byte(priv);
> }
>
> static void ams_delta_write_buf(struct mtd_info *mtd, const u_char *buf,
> int len)
> {
> + struct nand_chip *this = mtd_to_nand(mtd);
> + struct ams_delta_nand *priv = nand_get_controller_data(this);
> int i;
>
> if (len > 0)
> - ams_delta_write_byte(mtd, buf[0]);
> + ams_delta_write_first_byte(priv, buf[0]);
> for (i = 1; i < len; i++)
> - ams_delta_write_next_byte(mtd, buf[i]);
> + ams_delta_write_next_byte(priv, buf[i]);
> }
>
> static void ams_delta_read_buf(struct mtd_info *mtd, u_char *buf, int len)
> {
> + struct nand_chip *this = mtd_to_nand(mtd);
> + struct ams_delta_nand *priv = nand_get_controller_data(this);
> int i;
>
> if (len > 0)
> - buf[0] = ams_delta_read_byte(mtd);
> + buf[0] = ams_delta_read_first_byte(priv);
> for (i = 1; i < len; i++)
> - buf[i] = ams_delta_read_next_byte(mtd);
> + buf[i] = ams_delta_read_next_byte(priv);
> }
>
> /*
> @@ -161,7 +167,7 @@ static void ams_delta_hwcontrol(struct mtd_info *mtd, int cmd,
> }
>
> if (cmd != NAND_CMD_NONE)
> - ams_delta_write_byte(mtd, cmd);
> + ams_delta_write_first_byte(priv, cmd);
> }
>
> static int ams_delta_nand_ready(struct mtd_info *mtd)


2018-07-19 06:30:04

by Boris Brezillon

[permalink] [raw]
Subject: Re: [RFC PATCH 5/8] mtd: rawnand: ams-delta: Request data port GPIO resource

On Thu, 19 Jul 2018 01:57:07 +0200
Janusz Krzysztofik <[email protected]> wrote:

> Data port used by the driver is actually an OMAP MPUIO device, already
> under control of gpio-omap driver. For that reason we used to not
> request the memory region of the port as that would fail because the
> region is already busy. Despite that, we are still accessing the port
> by just ioremapping it and performing read/write operations. Moreover,
> we are doing that without any proteciton from other users legally
> manipulating the port pins over GPIO API.
>
> The plan is to convert the driver to access the port over functions
> exposed by the gpio-omap driver. Before that happens, we can already
> obtain exclusive access to the port by requesting an array of its GPIO
> descriptors. For that to work on Amstrad Delta, respective GPIO lookup
> table entries are added to the board init file.
>
> Signed-off-by: Janusz Krzysztofik <[email protected]>
> ---
> arch/arm/mach-omap1/board-ams-delta.c | 11 ++++++++++-
> drivers/mtd/nand/raw/ams-delta.c | 8 ++++++++

Can we split that in 2 patches: one adding data entries to the lookup
table, and another one patching the driver to request the gpiod array.

> 2 files changed, 18 insertions(+), 1 deletion(-)
>
> diff --git a/arch/arm/mach-omap1/board-ams-delta.c b/arch/arm/mach-omap1/board-ams-delta.c
> index eedacdfe9725..16f7bbe47607 100644
> --- a/arch/arm/mach-omap1/board-ams-delta.c
> +++ b/arch/arm/mach-omap1/board-ams-delta.c
> @@ -337,7 +337,8 @@ static struct platform_device ams_delta_nand_device = {
> .resource = ams_delta_nand_resources,
> };
>
> -#define OMAP_GPIO_LABEL "gpio-0-15"
> +#define OMAP_GPIO_LABEL "gpio-0-15"
> +#define OMAP_MPUIO_LABEL "mpuio"
>
> static struct gpiod_lookup_table ams_delta_nand_gpio_table = {
> .table = {
> @@ -349,6 +350,14 @@ static struct gpiod_lookup_table ams_delta_nand_gpio_table = {
> GPIO_LOOKUP(LATCH2_LABEL, LATCH2_PIN_NAND_NWE, "nwe", 0),
> GPIO_LOOKUP(LATCH2_LABEL, LATCH2_PIN_NAND_ALE, "ale", 0),
> GPIO_LOOKUP(LATCH2_LABEL, LATCH2_PIN_NAND_CLE, "cle", 0),
> + GPIO_LOOKUP_IDX(OMAP_MPUIO_LABEL, 0, "data", 0, 0),
> + GPIO_LOOKUP_IDX(OMAP_MPUIO_LABEL, 1, "data", 1, 0),
> + GPIO_LOOKUP_IDX(OMAP_MPUIO_LABEL, 2, "data", 2, 0),
> + GPIO_LOOKUP_IDX(OMAP_MPUIO_LABEL, 3, "data", 3, 0),
> + GPIO_LOOKUP_IDX(OMAP_MPUIO_LABEL, 4, "data", 4, 0),
> + GPIO_LOOKUP_IDX(OMAP_MPUIO_LABEL, 5, "data", 5, 0),
> + GPIO_LOOKUP_IDX(OMAP_MPUIO_LABEL, 6, "data", 6, 0),
> + GPIO_LOOKUP_IDX(OMAP_MPUIO_LABEL, 7, "data", 7, 0),
> { },
> },
> };
> diff --git a/drivers/mtd/nand/raw/ams-delta.c b/drivers/mtd/nand/raw/ams-delta.c
> index d7e4c9dbef67..ad62c0245458 100644
> --- a/drivers/mtd/nand/raw/ams-delta.c
> +++ b/drivers/mtd/nand/raw/ams-delta.c
> @@ -189,6 +189,7 @@ static int ams_delta_init(struct platform_device *pdev)
> struct mtd_info *mtd;
> struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> void __iomem *io_base;
> + struct gpio_descs *data_gpiods;
> int err = 0;
>
> if (!res)
> @@ -289,6 +290,13 @@ static int ams_delta_init(struct platform_device *pdev)
> dev_err(&pdev->dev, "CLE GPIO request failed (%d)\n", err);
> goto out_mtd;
> }
> + /* Request array of data pins, initialize them as output and set low */
> + data_gpiods = devm_gpiod_get_array(&pdev->dev, "data", GPIOD_OUT_LOW);
> + if (IS_ERR(data_gpiods)) {
> + err = PTR_ERR(data_gpiods);
> + dev_err(&pdev->dev, "data GPIO request failed: %d\n", err);
> + goto out_mtd;
> + }
>
> /* Scan to find existence of the device */
> err = nand_scan(mtd, 1);


2018-07-19 06:45:40

by Boris Brezillon

[permalink] [raw]
Subject: Re: [RFC PATCH 7/8] mtd: rawnand: ams-delta: Check sanity of data GPIO resource

On Thu, 19 Jul 2018 01:57:09 +0200
Janusz Krzysztofik <[email protected]> wrote:

> The plan is to replace data port readw()/writew() operations with GPIO
> callbacks provided by gpio-omap driver. For acceptable performance the
> GPIO chip must support get/set_multiple() GPIO callbacks.
>
> In order to avoid data corruption, we require the array of data GPIO
> descriptors obtained with gpiod_get_array() to meet some strict
> requirements:
> - all pins must belong to the same single GPIO chip,

You shouldn't care. The parallel NAND interface has WE/RE signals to
trigger a write/read on the data bus, that means you can change data
signals independently without risking data corruption as long as RE/WE
stay high (or low, I don't remember the active state on these pins). Of
course it's slower if you have to toggle data pins independently, but
that's not your problem. It's up to the HW designer to route things
correctly.

> - array index of each pin descriptor must match its hardware number,

Again, this is not really a problem. You'll just have to swap bits if
this is not the case. Not a big deal.

> - pin polarity must not be inverted,

Why?

> - pin hardware configuration must not be open drain nor open source.

This should be taken care of when requesting the pins.

>
> Let's implement the above described sanity checks before replacing the
> readw()/writew() operations witn GPIO callbacks. If a check fails,
> return -EINVAL to indicate the board provided GPIO setup is invalid.
>
> Signed-off-by: Janusz Krzysztofik <[email protected]>
> ---
> drivers/mtd/nand/raw/ams-delta.c | 87 +++++++++++++++++++++++++++++++++++++++-
> 1 file changed, 86 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/mtd/nand/raw/ams-delta.c b/drivers/mtd/nand/raw/ams-delta.c
> index ad62c0245458..bd501f385e78 100644
> --- a/drivers/mtd/nand/raw/ams-delta.c
> +++ b/drivers/mtd/nand/raw/ams-delta.c
> @@ -21,6 +21,7 @@
> #include <linux/module.h>
> #include <linux/delay.h>
> #include <linux/gpio/consumer.h>
> +#include <linux/gpio/driver.h>

Wow! This is a very very bad idea. There's a clear separation between
the GPIO consumer and the GPIO driver API for a good reason, and
you're violating this. linux/gpio/driver.h should only be included by
GPIO controller drivers.

> #include <linux/mtd/mtd.h>
> #include <linux/mtd/rawnand.h>
> #include <linux/mtd/partitions.h>
> @@ -190,7 +191,9 @@ static int ams_delta_init(struct platform_device *pdev)
> struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> void __iomem *io_base;
> struct gpio_descs *data_gpiods;
> - int err = 0;
> + struct gpio_chip *data_gpioc;
> + unsigned long mask, bits;
> + int i, err = 0;
>
> if (!res)
> return -ENXIO;
> @@ -298,6 +301,88 @@ static int ams_delta_init(struct platform_device *pdev)
> goto out_mtd;
> }
>
> + /* Use GPIO chip of first data GPIO pin descriptor */
> + data_gpioc = gpiod_to_chip(data_gpiods->desc[0]);
> +
> + /*
> + * For acceptable performance require the data GPIO
> + * chip to support get/set_multiple() callbacks.
> + */
> + if (!data_gpioc->get_multiple || !data_gpioc->set_multiple) {
> + err = -EINVAL;
> + dev_err(&pdev->dev,
> + "data GPIO chip does not support get/set_multiple()\n");
> + goto out_mtd;
> + }
> +
> + /* Verify if get_multiple() returns all pins low as initialized above */
> + mask = (1 << data_gpiods->ndescs) - 1;
> + err = data_gpioc->get_multiple(data_gpioc, &mask, &bits);

And this, you shouldn't do. You should instead go through the GPIO
consumer API to get the pin state (gpiod_get_raw_array_value()).

I guess you'd prefer to have the pin values in a bitmap instead of an
array of integers. That's probably something you can discuss with
Linus, see if he would accept to change the prototype of
gpiod_get_raw_array_value().

> + if (err) {
> + dev_err(&pdev->dev,
> + "data GPIO chip get_multiple() failed: %d\n", err);
> + goto out_mtd;
> + }
> + if (bits) {
> + err = -EINVAL;
> + dev_err(&pdev->dev,
> + "mismmatch of data GPIO initial value: %lu\n", bits);
> + goto out_mtd;
> + }
> +
> + /* Verify each data GPIO pin */
> + for (i = 0; i < data_gpiods->ndescs; i++) {
> + /* Require all pins belong to the same GPIO chip */
> + if (gpiod_to_chip(data_gpiods->desc[i]) != data_gpioc) {
> + err = -EINVAL;
> + dev_err(&pdev->dev, "GPIO chip mismatch of data bit %d\n",
> + i);
> + goto out_mtd;
> + }
> +
> + /* Require all pins active high (not inverted) */
> + if (gpiod_is_active_low(data_gpiods->desc[i])) {
> + err = -EINVAL;
> + dev_err(&pdev->dev,
> + "unsupported polarity of data GPIO bit %d\n",
> + i);
> + goto out_mtd;
> + }
> +
> + /*
> + * Require pin gpiod array index to match hardware pin number.
> + * Verified by setting the pin high with gpiod_set_raw_value()
> + * then reading it back with gpiochip->get() for comparison.
> + */
> + gpiod_set_raw_value(data_gpiods->desc[i], 1);
> + err = data_gpioc->get(data_gpioc, i);
> + if (err < 0) {
> + dev_err(&pdev->dev,
> + "data bit %d GPIO chip get() failed: %d\n", i,
> + err);
> + goto out_mtd;
> + }
> + if (!err) {
> + err = -EINVAL;
> + dev_err(&pdev->dev, "mismatch of data GPIO bit %d value\n",
> + i);
> + goto out_mtd;
> + }
> +
> + /*
> + * Check for unsupported pin hardware configuration. Use
> + * just verified gpiod array index as hardware pin number.
> + */
> + if (gpiochip_line_is_open_drain(data_gpioc, i) ||
> + gpiochip_line_is_open_source(data_gpioc, i)) {
> + err = -EINVAL;
> + dev_err(&pdev->dev,
> + "unsupported mode of data GPIO bit %d\n",
> + i);
> + goto out_mtd;
> + }
> + }
> +
> /* Scan to find existence of the device */
> err = nand_scan(mtd, 1);
> if (err)


2018-07-19 06:48:42

by Boris Brezillon

[permalink] [raw]
Subject: Re: [RFC PATCH 8/8] mtd: rawnand: ams-delta: Use GPIO callbacks for data I/O

On Thu, 19 Jul 2018 01:57:10 +0200
Janusz Krzysztofik <[email protected]> wrote:

> Don't readw()/writew() data directly from/to GPIO port which is under
> control of gpio-omap driver, use GPIO chip callbacks instead.
>
> Thanks to utilization of get/set_multiple() callbacks, performance
> degrade is minor for typical data transfers.

Same comment here, don't use the gpio_chip hooks directly, use the
consumer API instead.

>
> The driver should now work with any 8+-bit bidirectional GPIO port,
> not only OMAP.
>
> Signed-off-by: Janusz Krzysztofik <[email protected]>
>
> # Conflicts:
> # drivers/mtd/nand/raw/ams-delta.c

Oh, some leftovers from a conflict resolution :).

2018-07-20 17:56:04

by Janusz Krzysztofik

[permalink] [raw]
Subject: Re: [RFC PATCH 0/8] mtd: rawnand: ams-delta: Use gpio-omap accessors for data I/O

Hi Boris,

On Thursday, July 19, 2018 8:15:08 AM CEST Boris Brezillon wrote:
> Hi Janusz,
>
> On Thu, 19 Jul 2018 01:57:02 +0200
> Janusz Krzysztofik <[email protected]> wrote:
>
> > Implement the idea suggested by Artem Bityutskiy and Tony Lindgren
> > described in commit b027274d2e3a ("mtd: ams-delta: fix
> > request_mem_region() failure").
>
> Thanks for doing that. I'll review the patches, but I already have a
> extra request: can you convert this driver to ->exec_op()? It does not
> necessarily have to be done in this patchset, but, that'd be great to
> have one more driver converted to the new interface.

OK, I'll have a look as my free time permits.

> BTW, there seems to be a nand_gpio driver, maybe we should merge
> ams-delta code into nand_gpio after it's been patched to use the GPIO
> consumer API.

nand_gpio uses a form of read()/write() on a single bidirectional register for
data transfer, not GPIO, and it does not make use of NRE / NWE signals. Maybe
the two drivers could be merged to some extent but not entirely, I think.

Thanks,
Janusz



2018-07-20 17:58:03

by Janusz Krzysztofik

[permalink] [raw]
Subject: Re: [RFC PATCH 1/8] mtd: rawnand: ams-delta: Use private structure

On Thursday, July 19, 2018 8:17:28 AM CEST Boris Brezillon wrote:
> On Thu, 19 Jul 2018 01:57:03 +0200
> Janusz Krzysztofik <[email protected]> wrote:
>
> > Introduce a driver private structure and allocate it on device probe.
> > Use it for storing nand_chip structure, GPIO descriptors prevoiusly
> > stored in static variables as well as io_base pointer previously passed
> > as nand controller data or platform driver data.
>
> That's a good thing. Thanks for doing that.
>
> > Subsequent patches
> > may populate the structure with more members as needed.
> >
> > While at it, fix missing mtd->dev.parent and drop useless mtd->owner.
>
> I'd prefer to have it done in a separate patch if you don't mind.

No problem, I'll split it.

Thanks,
Janusz






2018-07-20 18:02:56

by Janusz Krzysztofik

[permalink] [raw]
Subject: Re: [RFC PATCH 2/8] mtd: rawnand: ams-delta: Write protect device during probe

On Thursday, July 19, 2018 8:22:00 AM CEST Boris Brezillon wrote:
> On Thu, 19 Jul 2018 01:57:04 +0200
> Janusz Krzysztofik <[email protected]> wrote:
>
> > Initialize NWP GPIO pin low to protect the device from hazard during
> > probe. Release write protection as soon as the device is under
> > control.
> >
> > Signed-off-by: Janusz Krzysztofik <[email protected]>
> > ---
> > drivers/mtd/nand/raw/ams-delta.c | 10 ++++++++--
> > 1 file changed, 8 insertions(+), 2 deletions(-)
> >
> > diff --git a/drivers/mtd/nand/raw/ams-delta.c b/drivers/mtd/nand/raw/ams-delta.c
> > index 48233d638d2a..6ac38e9cfa1a 100644
> > --- a/drivers/mtd/nand/raw/ams-delta.c
> > +++ b/drivers/mtd/nand/raw/ams-delta.c
> > @@ -219,8 +219,8 @@ static int ams_delta_init(struct platform_device *pdev)
> >
> > platform_set_drvdata(pdev, priv);
> >
> > - /* Set chip enabled, but */
> > - priv->gpiod_nwp = devm_gpiod_get(&pdev->dev, "nwp", GPIOD_OUT_HIGH);
> > + /* Set chip enabled but write protected */
> > + priv->gpiod_nwp = devm_gpiod_get(&pdev->dev, "nwp", GPIOD_OUT_LOW);
> > if (IS_ERR(priv->gpiod_nwp)) {
> > err = PTR_ERR(priv->gpiod_nwp);
> > dev_err(&pdev->dev, "NWP GPIO request failed (%d)\n", err);
> > @@ -267,6 +267,9 @@ static int ams_delta_init(struct platform_device *pdev)
> > if (err)
> > goto out_mtd;
> >
> > + /* As soon as the device is found, release write protection */
> > + gpiod_set_value(priv->gpiod_nwp, 1);
>
> Please don't do that until we have a generic way to assert/deassert WP
> from the core. I agree that we shouldn't write things in the
> nand_scan() path, but I'd be more comfortable if the core had control
> on this pin just in case we ever need to disable write-protection
> during chip detection/initialization.

OK, I can drop this patch for now if that's what you suggest.

Thanks,
Janusz



2018-07-20 18:12:14

by Janusz Krzysztofik

[permalink] [raw]
Subject: Re: [RFC PATCH 3/8] mtd: rawnand: ams-delta: Set port direction once per transfer

On Thursday, July 19, 2018 8:23:18 AM CEST Boris Brezillon wrote:
> On Thu, 19 Jul 2018 01:57:05 +0200
> Janusz Krzysztofik <[email protected]> wrote:
>
> > In its current shape, the driver sets data port direction before each
> > byte read/write operation, even during multi-byte transfers. Optimize
> > that by setting the port direction only on first byte of each transfer.
>
> Sounds like premature optimization for something you'll rework when
> fully switching to the GPIO consumer API to control the DATA bus.

Indeed, this optimization was crucial for getting acceptable performance of
data transfers over GPIO. I'm only not sure if there is any action in
response to your comment expected on my side, e.g., did you want to say I
should modify the patch description, or change the order of patches?

Thanks,
Janusz




2018-07-20 18:16:23

by Janusz Krzysztofik

[permalink] [raw]
Subject: Re: [RFC PATCH 4/8] mtd: rawnand: ams-delta: Optimize pointer resolution on read/write

On Thursday, July 19, 2018 8:25:38 AM CEST Boris Brezillon wrote:
> On Thu, 19 Jul 2018 01:57:06 +0200
> Janusz Krzysztofik <[email protected]> wrote:
>
> > Further optimize processing speed of read/write callback functions by
> > resolving private structure pointer only once per callback and passing
> > it to all subfunctions instead of mtd_info.

OK, I'll call it simplification (the code looks more simple after that, doesn't
it), not optimization if you agree.

Thanks,
Janusz




2018-07-20 18:38:26

by Janusz Krzysztofik

[permalink] [raw]
Subject: Re: [RFC PATCH 8/8] mtd: rawnand: ams-delta: Use GPIO callbacks for data I/O

On Thursday, July 19, 2018 8:47:49 AM CEST Boris Brezillon wrote:
> On Thu, 19 Jul 2018 01:57:10 +0200
> Janusz Krzysztofik <[email protected]> wrote:
>
> > Don't readw()/writew() data directly from/to GPIO port which is under
> > control of gpio-omap driver, use GPIO chip callbacks instead.
> >
> > Thanks to utilization of get/set_multiple() callbacks, performance
> > degrade is minor for typical data transfers.
>
> Same comment here, don't use the gpio_chip hooks directly, use the
> consumer API instead.

I tired but performance was not acceptable.

I see your point but please understand, what I'm trying to do here is not to
develop a shiny general purpose fully GPIO based NAND driver, I'm trying to
resolve issues with NAND driver for Amstrad Delta I like to play with, without
loosing much performance.

I'm going to reconsider all possible options, not only doing data I/O over
GPIO inside the driver, to have the task completed. Once done, I can get
back to the GPIO based code I developed so far and create a new generic driver
as my free time permits, or anyone can do that if needed, the code is open
source after all.

Thanks,
Janusz



2018-07-20 19:26:24

by Boris Brezillon

[permalink] [raw]
Subject: Re: [RFC PATCH 0/8] mtd: rawnand: ams-delta: Use gpio-omap accessors for data I/O

On Fri, 20 Jul 2018 19:55:42 +0200
Janusz Krzysztofik <[email protected]> wrote:

> Hi Boris,
>
> On Thursday, July 19, 2018 8:15:08 AM CEST Boris Brezillon wrote:
> > Hi Janusz,
> >
> > On Thu, 19 Jul 2018 01:57:02 +0200
> > Janusz Krzysztofik <[email protected]> wrote:
> >
> > > Implement the idea suggested by Artem Bityutskiy and Tony Lindgren
> > > described in commit b027274d2e3a ("mtd: ams-delta: fix
> > > request_mem_region() failure").
> >
> > Thanks for doing that. I'll review the patches, but I already have a
> > extra request: can you convert this driver to ->exec_op()? It does not
> > necessarily have to be done in this patchset, but, that'd be great to
> > have one more driver converted to the new interface.
>
> OK, I'll have a look as my free time permits.
>
> > BTW, there seems to be a nand_gpio driver, maybe we should merge
> > ams-delta code into nand_gpio after it's been patched to use the GPIO
> > consumer API.
>
> nand_gpio uses a form of read()/write() on a single bidirectional register for
> data transfer, not GPIO, and it does not make use of NRE / NWE signals. Maybe
> the two drivers could be merged to some extent but not entirely, I think.

Well, yes, right now the nand-gpio driver is assuming the data bus
is not bit-banged, but that's something we can easily add. Anyway, my
remark was more a "nice to have" not a hard requirement.

2018-07-20 19:30:15

by Boris Brezillon

[permalink] [raw]
Subject: Re: [RFC PATCH 3/8] mtd: rawnand: ams-delta: Set port direction once per transfer

On Fri, 20 Jul 2018 20:12:05 +0200
Janusz Krzysztofik <[email protected]> wrote:

> On Thursday, July 19, 2018 8:23:18 AM CEST Boris Brezillon wrote:
> > On Thu, 19 Jul 2018 01:57:05 +0200
> > Janusz Krzysztofik <[email protected]> wrote:
> >
> > > In its current shape, the driver sets data port direction before each
> > > byte read/write operation, even during multi-byte transfers. Optimize
> > > that by setting the port direction only on first byte of each transfer.
> >
> > Sounds like premature optimization for something you'll rework when
> > fully switching to the GPIO consumer API to control the DATA bus.
>
> Indeed, this optimization was crucial for getting acceptable performance of
> data transfers over GPIO. I'm only not sure if there is any action in
> response to your comment expected on my side, e.g., did you want to say I
> should modify the patch description, or change the order of patches?

I'm just saying that, since you switch to a solution that goes through
the GPIO framework to control the data bus, making sure the the pin
direction change is done only once when reading/writing several bytes is
something you can do after/when transitioning to the new approach.

So yes, I suggest to re-order patches, except that this patch won't
look the same at all if you move it after the "use the GPIO consumer
API to control data bus" patch.

2018-07-20 19:30:53

by Boris Brezillon

[permalink] [raw]
Subject: Re: [RFC PATCH 4/8] mtd: rawnand: ams-delta: Optimize pointer resolution on read/write

On Fri, 20 Jul 2018 20:14:55 +0200
Janusz Krzysztofik <[email protected]> wrote:

> On Thursday, July 19, 2018 8:25:38 AM CEST Boris Brezillon wrote:
> > On Thu, 19 Jul 2018 01:57:06 +0200
> > Janusz Krzysztofik <[email protected]> wrote:
> >
> > > Further optimize processing speed of read/write callback functions by
> > > resolving private structure pointer only once per callback and passing
> > > it to all subfunctions instead of mtd_info.
>
> OK, I'll call it simplification (the code looks more simple after that, doesn't
> it),

Yep.

> not optimization if you agree.
>
> Thanks,
> Janusz
>
>
>


2018-07-20 19:49:13

by Boris Brezillon

[permalink] [raw]
Subject: Re: [RFC PATCH 8/8] mtd: rawnand: ams-delta: Use GPIO callbacks for data I/O

Janusz,

On Fri, 20 Jul 2018 20:38:15 +0200
Janusz Krzysztofik <[email protected]> wrote:

> On Thursday, July 19, 2018 8:47:49 AM CEST Boris Brezillon wrote:
> > On Thu, 19 Jul 2018 01:57:10 +0200
> > Janusz Krzysztofik <[email protected]> wrote:
> >
> > > Don't readw()/writew() data directly from/to GPIO port which is under
> > > control of gpio-omap driver, use GPIO chip callbacks instead.
> > >
> > > Thanks to utilization of get/set_multiple() callbacks, performance
> > > degrade is minor for typical data transfers.
> >
> > Same comment here, don't use the gpio_chip hooks directly, use the
> > consumer API instead.
>
> I tired but performance was not acceptable.

You tried to use gpiod_{get,set}_array_value(), right? Did you
investigate on where the overhead comes from?

>
> I see your point but please understand, what I'm trying to do here is not to
> develop a shiny general purpose fully GPIO based NAND driver, I'm trying to
> resolve issues with NAND driver for Amstrad Delta I like to play with, without
> loosing much performance.

That's not a reason to violate the consumer/driver separation provided
by the GPIO framework. I'm not saying the current consumer APIs are
good enough for what you want to do (bit-bang a parallel data bus in
an efficient way), but bypassing the GPIO core like you do is
definitely not a good thing. Maybe you should discuss with Linus the
possibility of introducing a gpio_bitbang API that would provide you
some guarantees on the access time by making sure the pins all belong
to the same bank (and can thus be accessed in an atomic way). And maybe
provide a way to read/write several bytes by defining a delay between
each access, the size of the bus and the control pin if any (in
our case NRE/NWE).

>
> I'm going to reconsider all possible options, not only doing data I/O over
> GPIO inside the driver, to have the task completed. Once done, I can get
> back to the GPIO based code I developed so far and create a new generic driver
> as my free time permits, or anyone can do that if needed, the code is open
> source after all.

Let's forget the generic nand-gpio driver for now. All I'm asking is
that you do not bypass the GPIO framework like you intend do in this
patch.

Regards,

Boris

2018-07-29 20:40:44

by Linus Walleij

[permalink] [raw]
Subject: Re: [RFC PATCH 6/8] gpio: omap: Add get/set_multiple() callbacks

On Thu, Jul 19, 2018 at 1:56 AM Janusz Krzysztofik <[email protected]> wrote:

> This should make applications utilizing whole banks work faster.
>
> Signed-off-by: Janusz Krzysztofik <[email protected]>

This patch looks good to me and I have an urge to just apply it.

Can we get some feedback from the maintainers?

Yours,
Linus Walleij

2018-07-29 20:40:50

by Linus Walleij

[permalink] [raw]
Subject: Re: [RFC PATCH 7/8] mtd: rawnand: ams-delta: Check sanity of data GPIO resource

Hi Janusz!

Nice work overall! Some feedback:

On Thu, Jul 19, 2018 at 1:57 AM Janusz Krzysztofik <[email protected]> wrote:

> +#include <linux/gpio/driver.h>

Let's skip that.

> + /*
> + * For acceptable performance require the data GPIO
> + * chip to support get/set_multiple() callbacks.
> + */
> + if (!data_gpioc->get_multiple || !data_gpioc->set_multiple) {
> + err = -EINVAL;
> + dev_err(&pdev->dev,
> + "data GPIO chip does not support get/set_multiple()\n");
> + goto out_mtd;
> + }

Since we know which platform it is, we know that we applied the previous
get/set multiple patch, so we need not check this if the patches are
applied in sequence.

As long as all patches go in the same merge window, no problem.

I'm BTW ready to apply the get/set multiple patch already.

Yours,
Linus Walleij

2018-07-29 21:06:45

by Linus Walleij

[permalink] [raw]
Subject: Re: [RFC PATCH 7/8] mtd: rawnand: ams-delta: Check sanity of data GPIO resource

On Thu, Jul 19, 2018 at 8:44 AM Boris Brezillon
<[email protected]> wrote:

> I guess you'd prefer to have the pin values in a bitmap instead of an
> array of integers. That's probably something you can discuss with
> Linus, see if he would accept to change the prototype of
> gpiod_get_raw_array_value().

I am not so smart as to see the overall effects but if what
you're saying is that we shouldn't have designed these functions
and callbacks using arrays of integers (or longs) and instead
pass bitmaps, you may be just right. It intuitively sounds better.
But I'm not good with bitmaps.

We would have to refactor the world though.

Yours,
Linus Walleij

2018-07-29 23:20:31

by Boris Brezillon

[permalink] [raw]
Subject: Re: [RFC PATCH 7/8] mtd: rawnand: ams-delta: Check sanity of data GPIO resource

On Sun, 29 Jul 2018 22:36:49 +0200
Linus Walleij <[email protected]> wrote:

> On Thu, Jul 19, 2018 at 8:44 AM Boris Brezillon
> <[email protected]> wrote:
>
> > I guess you'd prefer to have the pin values in a bitmap instead of an
> > array of integers. That's probably something you can discuss with
> > Linus, see if he would accept to change the prototype of
> > gpiod_get_raw_array_value().
>
> I am not so smart as to see the overall effects but if what
> you're saying is that we shouldn't have designed these functions
> and callbacks using arrays of integers (or longs) and instead
> pass bitmaps, you may be just right. It intuitively sounds better.

Maybe, maybe not. Only an real evaluation of where the overhead is can
tell us. So I'd suggest trying to use the existing interface and doing
the int_array -> u8 conversion in the NAND driver first, and see how
much moving to a bitmap imprsoves things.

> But I'm not good with bitmaps.
>
> We would have to refactor the world though.

I checked and, AFAICT, there are no external users of this API (only
core code is using those funcs, for ioctls I guess). Anyway, I think
we should wait for real numbers before we consider doing this change
(It's not unusual to get this sort of things wrong).

2018-08-03 01:55:11

by Grygorii Strashko

[permalink] [raw]
Subject: Re: [RFC PATCH 6/8] gpio: omap: Add get/set_multiple() callbacks



On 07/29/2018 03:29 PM, Linus Walleij wrote:
> On Thu, Jul 19, 2018 at 1:56 AM Janusz Krzysztofik <[email protected]> wrote:
>
>> This should make applications utilizing whole banks work faster.
>>
>> Signed-off-by: Janusz Krzysztofik <[email protected]>
>
> This patch looks good to me and I have an urge to just apply it.
>
> Can we get some feedback from the maintainers?
>

Acked-by: Grygorii Strashko <[email protected]>
--
regards,
-grygorii

2018-08-03 16:50:07

by Linus Walleij

[permalink] [raw]
Subject: Re: [RFC PATCH 6/8] gpio: omap: Add get/set_multiple() callbacks

On Thu, Jul 19, 2018 at 1:56 AM Janusz Krzysztofik <[email protected]> wrote:

> This should make applications utilizing whole banks work faster.
>
> Signed-off-by: Janusz Krzysztofik <[email protected]>

Patch applied with Grygorii's ACK.
No reason to delay it, if there are any problems we will just revert it.

Yours,
Linus Walleij

2018-08-06 22:32:04

by Janusz Krzysztofik

[permalink] [raw]
Subject: [RFC PATCH v2 01/12] mtd: rawnand: ams-delta: Assign mtd->dev.parent, not mtd->owner

Fix missing mtd->dev.parent assignment and drop useless mtd->owner.

Signed-off-by: Janusz Krzysztofik <[email protected]>
---
drivers/mtd/nand/raw/ams-delta.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/mtd/nand/raw/ams-delta.c b/drivers/mtd/nand/raw/ams-delta.c
index 2a8872ebd14a..af313c620264 100644
--- a/drivers/mtd/nand/raw/ams-delta.c
+++ b/drivers/mtd/nand/raw/ams-delta.c
@@ -162,7 +162,7 @@ static int ams_delta_init(struct platform_device *pdev)
}

ams_delta_mtd = nand_to_mtd(this);
- ams_delta_mtd->owner = THIS_MODULE;
+ ams_delta_mtd->dev.parent = &pdev->dev;

/*
* Don't try to request the memory region from here,
--
2.16.4


2018-08-06 22:32:14

by Janusz Krzysztofik

[permalink] [raw]
Subject: [RFC PATCH v2 10/12] gpiolib: Introduce bitmap get/set array API extension

Certain GPIO array lookups may return arrays marked as applicable for
fast get/set array operations. In order to make use of that
information, a new API extension which allows passing it to get/set
functions is needed.

Create a set of frontends to get/set array functions which accept
strict descriptor array structures returned by gpiod_get_array()
instead of arbitrary descriptor arrays.

Since the intended purpose of the new API extension is to speed up
get/set array operations, also replace array of integer values argument
with their bitmap representation, ready for being passed directly to
chip callback functions, without iterating them.

Applicability of the new API is limited to arrays not exceeding bit
length of type unsigned long (32 pins).

Signed-off-by: Janusz Krzysztofik <[email protected]>
---
Documentation/driver-api/gpio/consumer.rst | 26 ++++
drivers/gpio/gpiolib.c | 209 +++++++++++++++++++++++++++++
include/linux/gpio/consumer.h | 14 ++
3 files changed, 249 insertions(+)

diff --git a/Documentation/driver-api/gpio/consumer.rst b/Documentation/driver-api/gpio/consumer.rst
index 38a990b5f3b6..bec4eab3b87c 100644
--- a/Documentation/driver-api/gpio/consumer.rst
+++ b/Documentation/driver-api/gpio/consumer.rst
@@ -383,6 +383,32 @@ or negative on error. Note the difference to gpiod_get_value(), which returns
0 or 1 on success to convey the GPIO value. With the array functions, the GPIO
values are stored in value_array rather than passed back as return value.

+Additionally, the following variants of the above functions exist which operate
+on bitmaps of values instead of arrays of values::
+
+ int gpiod_get_array_bitmap(struct gpio_descs *desc_array,
+ unsigned long *bits);
+ int gpiod_get_raw_array_bitmap(struct gpio_descs *desc_array,
+ unsigned long *bits);
+ int gpiod_get_array_bitmap_cansleep(struct gpio_desc *desc_array,
+ unsigned long *bits);
+ int gpiod_get_raw_array_bitmap_cansleep(struct gpio_desc **desc_array,
+ unsigned long *bits)
+
+ void gpiod_set_array_bitmap(struct gpio_descs *desc_array,
+ unsigned long *bits)
+ void gpiod_set_raw_array_bitmap(struct gpio_descs *desc_array,
+ unsigned long *bitmap)
+ void gpiod_set_array_bitmap_cansleep(struct gpio_descs *desc_array,
+ unsigned long *bits)
+ void gpiod_set_raw_array_bitmap_cansleep(struct gpio_descs *desc_array,
+ unsigned long *bits)
+
+Unlike their counterparts, these functions don't accept arbitrary GPIO
+descriptor arrays, only those of type struct gpio_descs returned by
+gpiod_get_array() and its variants. Supported array size is limited to the size
+of the bitmap, i.e., sizeof(unsigned long).
+

GPIOs mapped to IRQs
--------------------
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index c50bcec6e2d7..5b541364dee0 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -2836,6 +2836,27 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep,
return 0;
}

+int gpiod_get_array_bitmap_complex(bool raw, bool can_sleep,
+ struct gpio_descs *array,
+ unsigned long *bits)
+{
+ int value_array[sizeof(*bits)];
+ int i;
+
+ if (array->ndescs > sizeof(*bits))
+ return -EINVAL;
+
+ i = gpiod_get_array_value_complex(raw, can_sleep, array->ndescs,
+ array->desc, value_array);
+ if (i)
+ return i;
+
+ for (i = 0; i < array->ndescs; i++)
+ __assign_bit(i, bits, value_array[i]);
+
+ return 0;
+}
+
/**
* gpiod_get_raw_value() - return a gpio's raw value
* @desc: gpio whose value will be returned
@@ -2929,6 +2950,50 @@ int gpiod_get_array_value(unsigned int array_size,
}
EXPORT_SYMBOL_GPL(gpiod_get_array_value);

+/**
+ * gpiod_get_raw_array_bitmap() - read raw values from an array of GPIOs
+ * @desc_array: array of GPIO descriptors whose values will be read, obtained
+ * with gpiod_get_array(),
+ * @bits: bitmap to store the read values
+ *
+ * Read the raw values of the GPIOs, i.e. the values of the physical lines
+ * without regard for their ACTIVE_LOW status. Return 0 in case of success,
+ * else an error code.
+ *
+ * This function should be called from contexts where we cannot sleep,
+ * and it will complain if the GPIO chip functions potentially sleep.
+ */
+int gpiod_get_raw_array_bitmap(struct gpio_descs *desc_array,
+ unsigned long *bits)
+{
+ if (!desc_array)
+ return -EINVAL;
+ return gpiod_get_array_bitmap_complex(true, false, desc_array, bits);
+}
+EXPORT_SYMBOL_GPL(gpiod_get_raw_array_bitmap);
+
+/**
+ * gpiod_get_array_bitmap() - read values from an array of GPIOs
+ * @desc_array: array of GPIO descriptors whose values will be read, obtained
+ * with gpiod_get_array(),
+ * @bits: bitmap to store the read values
+ *
+ * Read the logical values of the GPIOs, i.e. taking their ACTIVE_LOW status
+ * into account. Return 0 in case of success, else an error code.
+ *
+ * This function should be called from contexts where we cannot sleep,
+ * and it will complain if the GPIO chip functions potentially sleep.
+ */
+
+int gpiod_get_array_bitmap(struct gpio_descs *desc_array,
+ unsigned long *bits)
+{
+ if (!desc_array)
+ return -EINVAL;
+ return gpiod_get_array_bitmap_complex(false, false, desc_array, bits);
+}
+EXPORT_SYMBOL_GPL(gpiod_get_raw_array_bitmap);
+
/*
* gpio_set_open_drain_value_commit() - Set the open drain gpio's value.
* @desc: gpio descriptor whose state need to be set.
@@ -3081,6 +3146,23 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep,
return 0;
}

+int gpiod_set_array_bitmap_complex(bool raw, bool can_sleep,
+ struct gpio_descs *array,
+ unsigned long *bits)
+{
+ int value_array[sizeof(*bits)];
+ int i;
+
+ if (array->ndescs > sizeof(*bits))
+ return -EINVAL;
+
+ for (i = 0; i < array->ndescs; i++)
+ value_array[i] = test_bit(i, bits);
+
+ return gpiod_set_array_value_complex(raw, can_sleep, array->ndescs,
+ array->desc, value_array);
+}
+
/**
* gpiod_set_raw_value() - assign a gpio's raw value
* @desc: gpio whose value will be assigned
@@ -3185,6 +3267,48 @@ void gpiod_set_array_value(unsigned int array_size,
}
EXPORT_SYMBOL_GPL(gpiod_set_array_value);

+/**
+ * gpiod_set_raw_array_bitmap() - assign values to an array of GPIOs
+ * @desc_array: array of GPIO descriptors whose values will be assigned,
+ * obtained with gpiod_get_array(),
+ * @bits: bitmap of values to assign
+ *
+ * Set the raw values of the GPIOs, i.e. the values of the physical lines
+ * without regard for their ACTIVE_LOW status.
+ *
+ * This function should be called from contexts where we cannot sleep, and will
+ * complain if the GPIO chip functions potentially sleep.
+ */
+int gpiod_set_raw_array_bitmap(struct gpio_descs *desc_array,
+ unsigned long *bits)
+{
+ if (!desc_array)
+ return -EINVAL;
+ return gpiod_set_array_bitmap_complex(true, false, desc_array, bits);
+}
+EXPORT_SYMBOL_GPL(gpiod_set_raw_array_bitmap);
+
+/**
+ * gpiod_set_array_bitmap() - assign values to an array of GPIOs
+ * @array_size: number of elements in the descriptor / value arrays
+ * @desc_array: array of GPIO descriptors whose values will be assigned
+ * @bits: bitmap of values to assign
+ *
+ * Set the logical values of the GPIOs, i.e. taking their ACTIVE_LOW status
+ * into account.
+ *
+ * This function should be called from contexts where we cannot sleep, and will
+ * complain if the GPIO chip functions potentially sleep.
+ */
+void gpiod_set_array_bitmap(struct gpio_descs *desc_array,
+ unsigned long *bits)
+{
+ if (!desc_array)
+ return;
+ gpiod_set_array_bitmap_complex(false, false, desc_array, bits);
+}
+EXPORT_SYMBOL_GPL(gpiod_set_array_bitmap);
+
/**
* gpiod_cansleep() - report whether gpio value access may sleep
* @desc: gpio to check
@@ -3446,6 +3570,49 @@ int gpiod_get_array_value_cansleep(unsigned int array_size,
}
EXPORT_SYMBOL_GPL(gpiod_get_array_value_cansleep);

+/**
+ * gpiod_get_raw_array_bitmap_cansleep() - read raw values from array of GPIOs
+ * @desc_array: array of GPIO descriptors whose values will be read, obtained
+ * with gpiod_get_array(),
+ * @bits: bitmap to store the read values
+ *
+ * Read the raw values of the GPIOs, i.e. the values of the physical lines
+ * without regard for their ACTIVE_LOW status. Return 0 in case of success,
+ * else an error code.
+ *
+ * This function is to be called from contexts that can sleep.
+ */
+int gpiod_get_raw_array_bitmap_cansleep(struct gpio_descs *desc_array,
+ unsigned long *bits)
+{
+ might_sleep_if(extra_checks);
+ if (!desc_array)
+ return -EINVAL;
+ return gpiod_get_array_bitmap_complex(true, true, desc_array, bits);
+}
+EXPORT_SYMBOL_GPL(gpiod_get_raw_array_bitmap_cansleep);
+
+/**
+ * gpiod_get_array_bitmap_cansleep() - read values from an array of GPIOs
+ * @desc_array: array of GPIO descriptors whose values will be read, obtained
+ * with gpiod_get_array(),
+ * @bits: bitmap to store the read values
+ *
+ * Read the logical values of the GPIOs, i.e. taking their ACTIVE_LOW status
+ * into account. Return 0 in case of success, else an error code.
+ *
+ * This function is to be called from contexts that can sleep.
+ */
+int gpiod_get_array_bitmap_cansleep(struct gpio_descs *desc_array,
+ unsigned long *bits)
+{
+ might_sleep_if(extra_checks);
+ if (!desc_array)
+ return -EINVAL;
+ return gpiod_get_array_bitmap_complex(false, true, desc_array, bits);
+}
+EXPORT_SYMBOL_GPL(gpiod_get_array_bitmap_cansleep);
+
/**
* gpiod_set_raw_value_cansleep() - assign a gpio's raw value
* @desc: gpio whose value will be assigned
@@ -3545,6 +3712,48 @@ void gpiod_set_array_value_cansleep(unsigned int array_size,
}
EXPORT_SYMBOL_GPL(gpiod_set_array_value_cansleep);

+/**
+ * gpiod_set_raw_array_bitmap_cansleep() - assign values to an array of GPIOs
+ * @desc_array: array of GPIO descriptors whose values will be assigned,
+ * obtained with gpiod_get_array(),
+ * @bits: bitmap of values to assign
+ *
+ * Set the raw values of the GPIOs, i.e. the values of the physical lines
+ * without regard for their ACTIVE_LOW status.
+ *
+ * This function is to be called from contexts that can sleep.
+ */
+int gpiod_set_raw_array_bitmap_cansleep(struct gpio_descs *desc_array,
+ unsigned long *bits)
+{
+ might_sleep_if(extra_checks);
+ if (!desc_array)
+ return -EINVAL;
+ return gpiod_set_array_bitmap_complex(true, true, desc_array, bits);
+}
+EXPORT_SYMBOL_GPL(gpiod_set_raw_array_bitmap_cansleep);
+
+/**
+ * gpiod_set_array_bitmap_cansleep() - assign values to an array of GPIOs
+ * @desc_array: array of GPIO descriptors whose values will be assigned,
+ * obtained with gpiod_get_array(),
+ * @bits: bitmap of values to assign
+ *
+ * Set the logical values of the GPIOs, i.e. taking their ACTIVE_LOW status
+ * into account.
+ *
+ * This function is to be called from contexts that can sleep.
+ */
+void gpiod_set_array_bitmap_cansleep(struct gpio_descs *desc_array,
+ unsigned long *bits)
+{
+ might_sleep_if(extra_checks);
+ if (!desc_array)
+ return;
+ gpiod_set_array_bitmap_complex(false, true, desc_array, bits);
+}
+EXPORT_SYMBOL_GPL(gpiod_set_array_bitmap_cansleep);
+
/**
* gpiod_add_lookup_table() - register GPIO device consumers
* @table: table of consumers to register
diff --git a/include/linux/gpio/consumer.h b/include/linux/gpio/consumer.h
index 862ee027a02f..1eabce4fc6c5 100644
--- a/include/linux/gpio/consumer.h
+++ b/include/linux/gpio/consumer.h
@@ -106,35 +106,49 @@ int gpiod_direction_output_raw(struct gpio_desc *desc, int value);
int gpiod_get_value(const struct gpio_desc *desc);
int gpiod_get_array_value(unsigned int array_size,
struct gpio_desc **desc_array, int *value_array);
+int gpiod_get_array_bitmap(struct gpio_descs *desc_array, unsigned long *bits);
void gpiod_set_value(struct gpio_desc *desc, int value);
void gpiod_set_array_value(unsigned int array_size,
struct gpio_desc **desc_array, int *value_array);
+void gpiod_set_array_bitmap(struct gpio_descs *desc_array, unsigned long *bits);
int gpiod_get_raw_value(const struct gpio_desc *desc);
int gpiod_get_raw_array_value(unsigned int array_size,
struct gpio_desc **desc_array,
int *value_array);
+int gpiod_get_raw_array_bitmap(struct gpio_descs *desc_array,
+ unsigned long *bits);
void gpiod_set_raw_value(struct gpio_desc *desc, int value);
int gpiod_set_raw_array_value(unsigned int array_size,
struct gpio_desc **desc_array,
int *value_array);
+int gpiod_set_raw_array_bitmap(struct gpio_descs *desc_array,
+ unsigned long *bits);

/* Value get/set from sleeping context */
int gpiod_get_value_cansleep(const struct gpio_desc *desc);
int gpiod_get_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
int *value_array);
+int gpiod_get_array_bitmap_cansleep(struct gpio_descs *desc_array,
+ unsigned long *bits);
void gpiod_set_value_cansleep(struct gpio_desc *desc, int value);
void gpiod_set_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
int *value_array);
+void gpiod_set_array_bitmap_cansleep(struct gpio_descs *desc_array,
+ unsigned long *bits);
int gpiod_get_raw_value_cansleep(const struct gpio_desc *desc);
int gpiod_get_raw_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
int *value_array);
+int gpiod_get_raw_array_bitmap_cansleep(struct gpio_descs *desc_array,
+ unsigned long *bits);
void gpiod_set_raw_value_cansleep(struct gpio_desc *desc, int value);
int gpiod_set_raw_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
int *value_array);
+int gpiod_set_raw_array_bitmap_cansleep(struct gpio_descs *desc_array,
+ unsigned long *bits);

int gpiod_set_debounce(struct gpio_desc *desc, unsigned debounce);
int gpiod_set_transitory(struct gpio_desc *desc, bool transitory);
--
2.16.4


2018-08-06 22:32:18

by Janusz Krzysztofik

[permalink] [raw]
Subject: [RFC PATCH v2 11/12] mtd: rawnand: ams-delta: Use GPIO API bitmap extension

Try to address the driver performance issues by replacing traditional
get/set array function calls with their bitmap based equivalents.

As long as fast bitmap processing path is not implemented in the new
API extension, performance of the driver remains unchanged.

Signed-off-by: Janusz Krzysztofik <[email protected]>
---
drivers/mtd/nand/raw/ams-delta.c | 19 +++----------------
1 file changed, 3 insertions(+), 16 deletions(-)

diff --git a/drivers/mtd/nand/raw/ams-delta.c b/drivers/mtd/nand/raw/ams-delta.c
index 30c461138195..7b08b2c441d3 100644
--- a/drivers/mtd/nand/raw/ams-delta.c
+++ b/drivers/mtd/nand/raw/ams-delta.c
@@ -78,15 +78,9 @@ static void ams_delta_write_commit(struct ams_delta_nand *priv)

static void ams_delta_write_next_byte(struct ams_delta_nand *priv, u_char byte)
{
- struct gpio_descs *data_gpiods = priv->data_gpiods;
unsigned long bits = byte;
- int i, value_array[data_gpiods->ndescs];
-
- for (i = 0; i < data_gpiods->ndescs; i++)
- value_array[i] = test_bit(i, &bits);

- gpiod_set_raw_array_value(data_gpiods->ndescs, data_gpiods->desc,
- value_array);
+ gpiod_set_raw_array_bitmap(priv->data_gpiods, &bits);

ams_delta_write_commit(priv);
}
@@ -106,22 +100,15 @@ static void ams_delta_write_first_byte(struct ams_delta_nand *priv, u_char byte)

static u_char ams_delta_read_next_byte(struct ams_delta_nand *priv)
{
- struct gpio_descs *data_gpiods = priv->data_gpiods;
- unsigned long bits = 0;
- int i, value_array[data_gpiods->ndescs];
+ unsigned long bits;

gpiod_set_value(priv->gpiod_nre, 0);
ndelay(40);

- gpiod_get_raw_array_value(data_gpiods->ndescs, data_gpiods->desc,
- value_array);
+ gpiod_get_raw_array_bitmap(priv->data_gpiods, &bits);

gpiod_set_value(priv->gpiod_nre, 1);

- for (i = 0; i < data_gpiods->ndescs; i++)
- if (value_array[i])
- __set_bit(i, &bits);
-
return bits;
}

--
2.16.4


2018-08-06 22:32:43

by Janusz Krzysztofik

[permalink] [raw]
Subject: [RFC PATCH v2 02/12] mtd: rawnand: ams-delta: Use private structure

Introduce a driver private structure and allocate it on device probe.
Use it for storing nand_chip structure, GPIO descriptors prevoiusly
stored in static variables as well as io_base pointer previously passed
as nand controller data or platform driver data. Subsequent patches
may populate the structure with more members as needed.

Signed-off-by: Janusz Krzysztofik <[email protected]>
---
drivers/mtd/nand/raw/ams-delta.c | 126 +++++++++++++++++++++------------------
1 file changed, 69 insertions(+), 57 deletions(-)

diff --git a/drivers/mtd/nand/raw/ams-delta.c b/drivers/mtd/nand/raw/ams-delta.c
index af313c620264..48233d638d2a 100644
--- a/drivers/mtd/nand/raw/ams-delta.c
+++ b/drivers/mtd/nand/raw/ams-delta.c
@@ -34,14 +34,18 @@
/*
* MTD structure for E3 (Delta)
*/
-static struct mtd_info *ams_delta_mtd = NULL;
-static struct gpio_desc *gpiod_rdy;
-static struct gpio_desc *gpiod_nce;
-static struct gpio_desc *gpiod_nre;
-static struct gpio_desc *gpiod_nwp;
-static struct gpio_desc *gpiod_nwe;
-static struct gpio_desc *gpiod_ale;
-static struct gpio_desc *gpiod_cle;
+
+struct ams_delta_nand {
+ struct nand_chip nand_chip;
+ struct gpio_desc *gpiod_rdy;
+ struct gpio_desc *gpiod_nce;
+ struct gpio_desc *gpiod_nre;
+ struct gpio_desc *gpiod_nwp;
+ struct gpio_desc *gpiod_nwe;
+ struct gpio_desc *gpiod_ale;
+ struct gpio_desc *gpiod_cle;
+ void __iomem *io_base;
+};

/*
* Define partitions for flash devices
@@ -71,26 +75,28 @@ static const struct mtd_partition partition_info[] = {
static void ams_delta_write_byte(struct mtd_info *mtd, u_char byte)
{
struct nand_chip *this = mtd_to_nand(mtd);
- void __iomem *io_base = (void __iomem *)nand_get_controller_data(this);
+ struct ams_delta_nand *priv = nand_get_controller_data(this);
+ void __iomem *io_base = priv->io_base;

writew(0, io_base + OMAP_MPUIO_IO_CNTL);
writew(byte, this->IO_ADDR_W);
- gpiod_set_value(gpiod_nwe, 0);
+ gpiod_set_value(priv->gpiod_nwe, 0);
ndelay(40);
- gpiod_set_value(gpiod_nwe, 1);
+ gpiod_set_value(priv->gpiod_nwe, 1);
}

static u_char ams_delta_read_byte(struct mtd_info *mtd)
{
u_char res;
struct nand_chip *this = mtd_to_nand(mtd);
- void __iomem *io_base = (void __iomem *)nand_get_controller_data(this);
+ struct ams_delta_nand *priv = nand_get_controller_data(this);
+ void __iomem *io_base = priv->io_base;

- gpiod_set_value(gpiod_nre, 0);
+ gpiod_set_value(priv->gpiod_nre, 0);
ndelay(40);
writew(~0, io_base + OMAP_MPUIO_IO_CNTL);
res = readw(this->IO_ADDR_R);
- gpiod_set_value(gpiod_nre, 1);
+ gpiod_set_value(priv->gpiod_nre, 1);

return res;
}
@@ -123,11 +129,13 @@ static void ams_delta_read_buf(struct mtd_info *mtd, u_char *buf, int len)
static void ams_delta_hwcontrol(struct mtd_info *mtd, int cmd,
unsigned int ctrl)
{
+ struct nand_chip *this = mtd_to_nand(mtd);
+ struct ams_delta_nand *priv = nand_get_controller_data(this);

if (ctrl & NAND_CTRL_CHANGE) {
- gpiod_set_value(gpiod_nce, !(ctrl & NAND_NCE));
- gpiod_set_value(gpiod_cle, !!(ctrl & NAND_CLE));
- gpiod_set_value(gpiod_ale, !!(ctrl & NAND_ALE));
+ gpiod_set_value(priv->gpiod_nce, !(ctrl & NAND_NCE));
+ gpiod_set_value(priv->gpiod_cle, !!(ctrl & NAND_CLE));
+ gpiod_set_value(priv->gpiod_ale, !!(ctrl & NAND_ALE));
}

if (cmd != NAND_CMD_NONE)
@@ -136,7 +144,10 @@ static void ams_delta_hwcontrol(struct mtd_info *mtd, int cmd,

static int ams_delta_nand_ready(struct mtd_info *mtd)
{
- return gpiod_get_value(gpiod_rdy);
+ struct nand_chip *this = mtd_to_nand(mtd);
+ struct ams_delta_nand *priv = nand_get_controller_data(this);
+
+ return gpiod_get_value(priv->gpiod_rdy);
}


@@ -145,7 +156,9 @@ static int ams_delta_nand_ready(struct mtd_info *mtd)
*/
static int ams_delta_init(struct platform_device *pdev)
{
+ struct ams_delta_nand *priv;
struct nand_chip *this;
+ struct mtd_info *mtd;
struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
void __iomem *io_base;
int err = 0;
@@ -154,15 +167,16 @@ static int ams_delta_init(struct platform_device *pdev)
return -ENXIO;

/* Allocate memory for MTD device structure and private data */
- this = kzalloc(sizeof(struct nand_chip), GFP_KERNEL);
- if (!this) {
+ priv = devm_kzalloc(&pdev->dev, sizeof(struct ams_delta_nand),
+ GFP_KERNEL);
+ if (!priv) {
pr_warn("Unable to allocate E3 NAND MTD device structure.\n");
- err = -ENOMEM;
- goto out;
+ return -ENOMEM;
}
+ this = &priv->nand_chip;

- ams_delta_mtd = nand_to_mtd(this);
- ams_delta_mtd->dev.parent = &pdev->dev;
+ mtd = nand_to_mtd(this);
+ mtd->dev.parent = &pdev->dev;

/*
* Don't try to request the memory region from here,
@@ -177,7 +191,8 @@ static int ams_delta_init(struct platform_device *pdev)
goto out_free;
}

- nand_set_controller_data(this, (void *)io_base);
+ priv->io_base = io_base;
+ nand_set_controller_data(this, priv);

/* Set address of NAND IO lines */
this->IO_ADDR_R = io_base + OMAP_MPUIO_INPUT_LATCH;
@@ -187,14 +202,14 @@ static int ams_delta_init(struct platform_device *pdev)
this->read_buf = ams_delta_read_buf;
this->cmd_ctrl = ams_delta_hwcontrol;

- gpiod_rdy = devm_gpiod_get_optional(&pdev->dev, "rdy", GPIOD_IN);
- if (IS_ERR(gpiod_rdy)) {
- err = PTR_ERR(gpiod_rdy);
+ priv->gpiod_rdy = devm_gpiod_get_optional(&pdev->dev, "rdy", GPIOD_IN);
+ if (IS_ERR(priv->gpiod_rdy)) {
+ err = PTR_ERR(priv->gpiod_rdy);
dev_warn(&pdev->dev, "RDY GPIO request failed (%d)\n", err);
goto out_mtd;
}

- if (gpiod_rdy)
+ if (priv->gpiod_rdy)
this->dev_ready = ams_delta_nand_ready;

/* 25 us command delay time */
@@ -202,66 +217,64 @@ static int ams_delta_init(struct platform_device *pdev)
this->ecc.mode = NAND_ECC_SOFT;
this->ecc.algo = NAND_ECC_HAMMING;

- platform_set_drvdata(pdev, io_base);
+ platform_set_drvdata(pdev, priv);

/* Set chip enabled, but */
- gpiod_nwp = devm_gpiod_get(&pdev->dev, "nwp", GPIOD_OUT_HIGH);
- if (IS_ERR(gpiod_nwp)) {
- err = PTR_ERR(gpiod_nwp);
+ priv->gpiod_nwp = devm_gpiod_get(&pdev->dev, "nwp", GPIOD_OUT_HIGH);
+ if (IS_ERR(priv->gpiod_nwp)) {
+ err = PTR_ERR(priv->gpiod_nwp);
dev_err(&pdev->dev, "NWP GPIO request failed (%d)\n", err);
goto out_mtd;
}

- gpiod_nce = devm_gpiod_get(&pdev->dev, "nce", GPIOD_OUT_HIGH);
- if (IS_ERR(gpiod_nce)) {
- err = PTR_ERR(gpiod_nce);
+ priv->gpiod_nce = devm_gpiod_get(&pdev->dev, "nce", GPIOD_OUT_HIGH);
+ if (IS_ERR(priv->gpiod_nce)) {
+ err = PTR_ERR(priv->gpiod_nce);
dev_err(&pdev->dev, "NCE GPIO request failed (%d)\n", err);
goto out_mtd;
}

- gpiod_nre = devm_gpiod_get(&pdev->dev, "nre", GPIOD_OUT_HIGH);
- if (IS_ERR(gpiod_nre)) {
- err = PTR_ERR(gpiod_nre);
+ priv->gpiod_nre = devm_gpiod_get(&pdev->dev, "nre", GPIOD_OUT_HIGH);
+ if (IS_ERR(priv->gpiod_nre)) {
+ err = PTR_ERR(priv->gpiod_nre);
dev_err(&pdev->dev, "NRE GPIO request failed (%d)\n", err);
goto out_mtd;
}

- gpiod_nwe = devm_gpiod_get(&pdev->dev, "nwe", GPIOD_OUT_HIGH);
- if (IS_ERR(gpiod_nwe)) {
- err = PTR_ERR(gpiod_nwe);
+ priv->gpiod_nwe = devm_gpiod_get(&pdev->dev, "nwe", GPIOD_OUT_HIGH);
+ if (IS_ERR(priv->gpiod_nwe)) {
+ err = PTR_ERR(priv->gpiod_nwe);
dev_err(&pdev->dev, "NWE GPIO request failed (%d)\n", err);
goto out_mtd;
}

- gpiod_ale = devm_gpiod_get(&pdev->dev, "ale", GPIOD_OUT_LOW);
- if (IS_ERR(gpiod_ale)) {
- err = PTR_ERR(gpiod_ale);
+ priv->gpiod_ale = devm_gpiod_get(&pdev->dev, "ale", GPIOD_OUT_LOW);
+ if (IS_ERR(priv->gpiod_ale)) {
+ err = PTR_ERR(priv->gpiod_ale);
dev_err(&pdev->dev, "ALE GPIO request failed (%d)\n", err);
goto out_mtd;
}

- gpiod_cle = devm_gpiod_get(&pdev->dev, "cle", GPIOD_OUT_LOW);
- if (IS_ERR(gpiod_cle)) {
- err = PTR_ERR(gpiod_cle);
+ priv->gpiod_cle = devm_gpiod_get(&pdev->dev, "cle", GPIOD_OUT_LOW);
+ if (IS_ERR(priv->gpiod_cle)) {
+ err = PTR_ERR(priv->gpiod_cle);
dev_err(&pdev->dev, "CLE GPIO request failed (%d)\n", err);
goto out_mtd;
}

/* Scan to find existence of the device */
- err = nand_scan(ams_delta_mtd, 1);
+ err = nand_scan(mtd, 1);
if (err)
goto out_mtd;

/* Register the partitions */
- mtd_device_register(ams_delta_mtd, partition_info,
- ARRAY_SIZE(partition_info));
+ mtd_device_register(mtd, partition_info, ARRAY_SIZE(partition_info));

goto out;

out_mtd:
iounmap(io_base);
out_free:
- kfree(this);
out:
return err;
}
@@ -271,16 +284,15 @@ static int ams_delta_init(struct platform_device *pdev)
*/
static int ams_delta_cleanup(struct platform_device *pdev)
{
- void __iomem *io_base = platform_get_drvdata(pdev);
+ struct ams_delta_nand *priv = platform_get_drvdata(pdev);
+ struct mtd_info *mtd = nand_to_mtd(&priv->nand_chip);
+ void __iomem *io_base = priv->io_base;

/* Release resources, unregister device */
- nand_release(ams_delta_mtd);
+ nand_release(mtd);

iounmap(io_base);

- /* Free the MTD device structure */
- kfree(mtd_to_nand(ams_delta_mtd));
-
return 0;
}

--
2.16.4


2018-08-06 22:32:49

by Janusz Krzysztofik

[permalink] [raw]
Subject: [RFC PATCH v2 09/12] gpiolib: Identify GPIO descriptor arrays with direct mapping

Certain GPIO array lookup results may map directly to GPIO pins of a
single GPIO chip in hardware order. If that condition is recognized
and handled efficiently, significant performance gain of get/set array
functions may be possible.

While processing a request for an array of GPIO descriptors, verify if
the descriptors just collected represent consecutive pins of a single
GPIO chip. Pass that information with the array to the caller so it
can benefit from enhanced performance as soon as bitmap based get/set
array functions which can make efficient use of that are available.

Signed-off-by: Janusz Krzysztofik <[email protected]>
---
Documentation/driver-api/gpio/consumer.rst | 4 +++-
drivers/gpio/gpiolib.c | 14 ++++++++++++++
include/linux/gpio/consumer.h | 1 +
3 files changed, 18 insertions(+), 1 deletion(-)

diff --git a/Documentation/driver-api/gpio/consumer.rst b/Documentation/driver-api/gpio/consumer.rst
index aa03f389d41d..38a990b5f3b6 100644
--- a/Documentation/driver-api/gpio/consumer.rst
+++ b/Documentation/driver-api/gpio/consumer.rst
@@ -109,11 +109,13 @@ For a function using multiple GPIOs all of those can be obtained with one call::
enum gpiod_flags flags)

This function returns a struct gpio_descs which contains an array of
-descriptors::
+descriptors. It may also contain a valid descriptor of a single GPIO chip in
+case the array strictly matches pin hardware layout of the chip::

struct gpio_descs {
unsigned int ndescs;
struct gpio_desc *desc[];
+ struct gpio_chip *chip;
}

The following function returns NULL instead of -ENOENT if no GPIOs have been
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index bdbfc95793e7..c50bcec6e2d7 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -4161,6 +4161,7 @@ struct gpio_descs *__must_check gpiod_get_array(struct device *dev,
{
struct gpio_desc *desc;
struct gpio_descs *descs;
+ struct gpio_chip *chip;
int count;

count = gpiod_count(dev, con_id);
@@ -4177,6 +4178,19 @@ struct gpio_descs *__must_check gpiod_get_array(struct device *dev,
gpiod_put_array(descs);
return ERR_CAST(desc);
}
+
+ /*
+ * Verify if the array qualifies for fast bitmap operations
+ * (single chip, pins in hardware order starting from 0)
+ * and mark the array with the chip descriptor if true.
+ */
+ chip = gpiod_to_chip(desc);
+ if (descs->chip == NULL)
+ descs->chip = chip;
+ if (!IS_ERR(descs->chip) && (chip != descs->chip ||
+ gpio_chip_hwgpio(desc) != descs->ndescs))
+ descs->chip = ERR_PTR(-EINVAL);
+
descs->desc[descs->ndescs] = desc;
descs->ndescs++;
}
diff --git a/include/linux/gpio/consumer.h b/include/linux/gpio/consumer.h
index 21ddbe440030..862ee027a02f 100644
--- a/include/linux/gpio/consumer.h
+++ b/include/linux/gpio/consumer.h
@@ -22,6 +22,7 @@ struct gpio_desc;
* gpiod_get_array().
*/
struct gpio_descs {
+ struct gpio_chip *chip;
unsigned int ndescs;
struct gpio_desc *desc[];
};
--
2.16.4


2018-08-06 22:32:53

by Janusz Krzysztofik

[permalink] [raw]
Subject: [RFC PATCH v2 05/12] mtd: rawnand: ams-delta: use GPIO API for data read/write

Don't readw()/writew() data directly from/to GPIO port which is under
control of gpio-omap driver, use GPIO API instead.

Degrade of performance on Amstrad Delta is completely not acceptable.

The driver should work with any 8+-bit bidirectional GPIO port, not
only OMAP.

Signed-off-by: Janusz Krzysztofik <[email protected]>
---
drivers/mtd/nand/raw/ams-delta.c | 97 ++++++++++++++++------------------------
1 file changed, 38 insertions(+), 59 deletions(-)

diff --git a/drivers/mtd/nand/raw/ams-delta.c b/drivers/mtd/nand/raw/ams-delta.c
index 09d6901fc94d..78996ddf82e0 100644
--- a/drivers/mtd/nand/raw/ams-delta.c
+++ b/drivers/mtd/nand/raw/ams-delta.c
@@ -24,13 +24,10 @@
#include <linux/mtd/mtd.h>
#include <linux/mtd/rawnand.h>
#include <linux/mtd/partitions.h>
-#include <linux/platform_data/gpio-omap.h>
+#include <linux/platform_device.h>

-#include <asm/io.h>
#include <asm/sizes.h>

-#include <mach/hardware.h>
-
/*
* MTD structure for E3 (Delta)
*/
@@ -44,7 +41,7 @@ struct ams_delta_nand {
struct gpio_desc *gpiod_nwe;
struct gpio_desc *gpiod_ale;
struct gpio_desc *gpiod_cle;
- void __iomem *io_base;
+ struct gpio_descs *data_gpiods;
};

/*
@@ -76,10 +73,14 @@ static void ams_delta_write_byte(struct mtd_info *mtd, u_char byte)
{
struct nand_chip *this = mtd_to_nand(mtd);
struct ams_delta_nand *priv = nand_get_controller_data(this);
- void __iomem *io_base = priv->io_base;
+ struct gpio_descs *data_gpiods = priv->data_gpiods;
+ unsigned long bits = byte;
+ int i;
+
+ for (i = 0; i < data_gpiods->ndescs; i++)
+ gpiod_direction_output_raw(data_gpiods->desc[i],
+ test_bit(i, &bits));

- writew(0, io_base + OMAP_MPUIO_IO_CNTL);
- writew(byte, this->IO_ADDR_W);
gpiod_set_value(priv->gpiod_nwe, 0);
ndelay(40);
gpiod_set_value(priv->gpiod_nwe, 1);
@@ -87,18 +88,28 @@ static void ams_delta_write_byte(struct mtd_info *mtd, u_char byte)

static u_char ams_delta_read_byte(struct mtd_info *mtd)
{
- u_char res;
struct nand_chip *this = mtd_to_nand(mtd);
struct ams_delta_nand *priv = nand_get_controller_data(this);
- void __iomem *io_base = priv->io_base;
+ struct gpio_descs *data_gpiods = priv->data_gpiods;
+ unsigned long bits = 0;
+ int i, value_array[data_gpiods->ndescs];
+
+ for (i = 0; i < data_gpiods->ndescs; i++)
+ gpiod_direction_input(data_gpiods->desc[i]);

gpiod_set_value(priv->gpiod_nre, 0);
ndelay(40);
- writew(~0, io_base + OMAP_MPUIO_IO_CNTL);
- res = readw(this->IO_ADDR_R);
+
+ gpiod_get_raw_array_value(data_gpiods->ndescs, data_gpiods->desc,
+ value_array);
+
gpiod_set_value(priv->gpiod_nre, 1);

- return res;
+ for (i = 0; i < data_gpiods->ndescs; i++)
+ if (value_array[i])
+ __set_bit(i, &bits);
+
+ return bits;
}

static void ams_delta_write_buf(struct mtd_info *mtd, const u_char *buf,
@@ -159,14 +170,8 @@ static int ams_delta_init(struct platform_device *pdev)
struct ams_delta_nand *priv;
struct nand_chip *this;
struct mtd_info *mtd;
- struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- void __iomem *io_base;
- struct gpio_descs *data_gpiods;
int err = 0;

- if (!res)
- return -ENXIO;
-
/* Allocate memory for MTD device structure and private data */
priv = devm_kzalloc(&pdev->dev, sizeof(struct ams_delta_nand),
GFP_KERNEL);
@@ -179,25 +184,8 @@ static int ams_delta_init(struct platform_device *pdev)
mtd = nand_to_mtd(this);
mtd->dev.parent = &pdev->dev;

- /*
- * Don't try to request the memory region from here,
- * it should have been already requested from the
- * gpio-omap driver and requesting it again would fail.
- */
-
- io_base = ioremap(res->start, resource_size(res));
- if (io_base == NULL) {
- dev_err(&pdev->dev, "ioremap failed\n");
- err = -EIO;
- goto out_free;
- }
-
- priv->io_base = io_base;
nand_set_controller_data(this, priv);

- /* Set address of NAND IO lines */
- this->IO_ADDR_R = io_base + OMAP_MPUIO_INPUT_LATCH;
- this->IO_ADDR_W = io_base + OMAP_MPUIO_OUTPUT;
this->read_byte = ams_delta_read_byte;
this->write_buf = ams_delta_write_buf;
this->read_buf = ams_delta_read_buf;
@@ -207,7 +195,7 @@ static int ams_delta_init(struct platform_device *pdev)
if (IS_ERR(priv->gpiod_rdy)) {
err = PTR_ERR(priv->gpiod_rdy);
dev_warn(&pdev->dev, "RDY GPIO request failed (%d)\n", err);
- goto out_mtd;
+ return err;
}

if (priv->gpiod_rdy)
@@ -225,66 +213,60 @@ static int ams_delta_init(struct platform_device *pdev)
if (IS_ERR(priv->gpiod_nwp)) {
err = PTR_ERR(priv->gpiod_nwp);
dev_err(&pdev->dev, "NWP GPIO request failed (%d)\n", err);
- goto out_mtd;
+ return err;
}

priv->gpiod_nce = devm_gpiod_get(&pdev->dev, "nce", GPIOD_OUT_HIGH);
if (IS_ERR(priv->gpiod_nce)) {
err = PTR_ERR(priv->gpiod_nce);
dev_err(&pdev->dev, "NCE GPIO request failed (%d)\n", err);
- goto out_mtd;
+ return err;
}

priv->gpiod_nre = devm_gpiod_get(&pdev->dev, "nre", GPIOD_OUT_HIGH);
if (IS_ERR(priv->gpiod_nre)) {
err = PTR_ERR(priv->gpiod_nre);
dev_err(&pdev->dev, "NRE GPIO request failed (%d)\n", err);
- goto out_mtd;
+ return err;
}

priv->gpiod_nwe = devm_gpiod_get(&pdev->dev, "nwe", GPIOD_OUT_HIGH);
if (IS_ERR(priv->gpiod_nwe)) {
err = PTR_ERR(priv->gpiod_nwe);
dev_err(&pdev->dev, "NWE GPIO request failed (%d)\n", err);
- goto out_mtd;
+ return err;
}

priv->gpiod_ale = devm_gpiod_get(&pdev->dev, "ale", GPIOD_OUT_LOW);
if (IS_ERR(priv->gpiod_ale)) {
err = PTR_ERR(priv->gpiod_ale);
dev_err(&pdev->dev, "ALE GPIO request failed (%d)\n", err);
- goto out_mtd;
+ return err;
}

priv->gpiod_cle = devm_gpiod_get(&pdev->dev, "cle", GPIOD_OUT_LOW);
if (IS_ERR(priv->gpiod_cle)) {
err = PTR_ERR(priv->gpiod_cle);
dev_err(&pdev->dev, "CLE GPIO request failed (%d)\n", err);
- goto out_mtd;
+ return err;
}
/* Request array of data pins, initialize them as input */
- data_gpiods = devm_gpiod_get_array(&pdev->dev, "data", GPIOD_IN);
- if (IS_ERR(data_gpiods)) {
- err = PTR_ERR(data_gpiods);
+ priv->data_gpiods = devm_gpiod_get_array(&pdev->dev, "data", GPIOD_IN);
+ if (IS_ERR(priv->data_gpiods)) {
+ err = PTR_ERR(priv->data_gpiods);
dev_err(&pdev->dev, "data GPIO request failed: %d\n", err);
- goto out_mtd;
+ return err;
}

/* Scan to find existence of the device */
err = nand_scan(mtd, 1);
if (err)
- goto out_mtd;
+ return err;

/* Register the partitions */
mtd_device_register(mtd, partition_info, ARRAY_SIZE(partition_info));

- goto out;
-
- out_mtd:
- iounmap(io_base);
-out_free:
- out:
- return err;
+ return 0;
}

/*
@@ -294,13 +276,10 @@ static int ams_delta_cleanup(struct platform_device *pdev)
{
struct ams_delta_nand *priv = platform_get_drvdata(pdev);
struct mtd_info *mtd = nand_to_mtd(&priv->nand_chip);
- void __iomem *io_base = priv->io_base;

- /* Release resources, unregister device */
+ /* Unregister device */
nand_release(mtd);

- iounmap(io_base);
-
return 0;
}

--
2.16.4


2018-08-06 22:33:03

by Janusz Krzysztofik

[permalink] [raw]
Subject: [RFC PATCH v2 07/12] mtd: rawnand: ams-delta: Set port direction once per transfer

In its current shape, the driver sets data port direction before each
byte read/write operation, even during multi-byte transfers. Since
performance of the driver is completely not acceptable on Amstrad Delta
after it has been converted to GPIO bitbang, try to improve things a
bit by setting the port direction only on first byte of each transfer.

Resulting performance on Amstrad Delta is still far from acceptable.

Signed-off-by: Janusz Krzysztofik <[email protected]>
---
drivers/mtd/nand/raw/ams-delta.c | 58 ++++++++++++++++++++++++++++++++--------
1 file changed, 47 insertions(+), 11 deletions(-)

diff --git a/drivers/mtd/nand/raw/ams-delta.c b/drivers/mtd/nand/raw/ams-delta.c
index 78996ddf82e0..d02c48c013e8 100644
--- a/drivers/mtd/nand/raw/ams-delta.c
+++ b/drivers/mtd/nand/raw/ams-delta.c
@@ -69,6 +69,30 @@ static const struct mtd_partition partition_info[] = {
.size = 3 * SZ_256K },
};

+static void ams_delta_write_commit(struct ams_delta_nand *priv)
+{
+ gpiod_set_value(priv->gpiod_nwe, 0);
+ ndelay(40);
+ gpiod_set_value(priv->gpiod_nwe, 1);
+}
+
+static void ams_delta_write_next_byte(struct mtd_info *mtd, u_char byte)
+{
+ struct nand_chip *this = mtd_to_nand(mtd);
+ struct ams_delta_nand *priv = nand_get_controller_data(this);
+ struct gpio_descs *data_gpiods = priv->data_gpiods;
+ unsigned long bits = byte;
+ int i, value_array[data_gpiods->ndescs];
+
+ for (i = 0; i < data_gpiods->ndescs; i++)
+ value_array[i] = test_bit(i, &bits);
+
+ gpiod_set_raw_array_value(data_gpiods->ndescs, data_gpiods->desc,
+ value_array);
+
+ ams_delta_write_commit(priv);
+}
+
static void ams_delta_write_byte(struct mtd_info *mtd, u_char byte)
{
struct nand_chip *this = mtd_to_nand(mtd);
@@ -81,12 +105,10 @@ static void ams_delta_write_byte(struct mtd_info *mtd, u_char byte)
gpiod_direction_output_raw(data_gpiods->desc[i],
test_bit(i, &bits));

- gpiod_set_value(priv->gpiod_nwe, 0);
- ndelay(40);
- gpiod_set_value(priv->gpiod_nwe, 1);
+ ams_delta_write_commit(priv);
}

-static u_char ams_delta_read_byte(struct mtd_info *mtd)
+static u_char ams_delta_read_next_byte(struct mtd_info *mtd)
{
struct nand_chip *this = mtd_to_nand(mtd);
struct ams_delta_nand *priv = nand_get_controller_data(this);
@@ -94,9 +116,6 @@ static u_char ams_delta_read_byte(struct mtd_info *mtd)
unsigned long bits = 0;
int i, value_array[data_gpiods->ndescs];

- for (i = 0; i < data_gpiods->ndescs; i++)
- gpiod_direction_input(data_gpiods->desc[i]);
-
gpiod_set_value(priv->gpiod_nre, 0);
ndelay(40);

@@ -112,21 +131,38 @@ static u_char ams_delta_read_byte(struct mtd_info *mtd)
return bits;
}

+static u_char ams_delta_read_byte(struct mtd_info *mtd)
+{
+ struct nand_chip *this = mtd_to_nand(mtd);
+ struct ams_delta_nand *priv = nand_get_controller_data(this);
+ struct gpio_descs *data_gpiods = priv->data_gpiods;
+ int i;
+
+ for (i = 0; i < data_gpiods->ndescs; i++)
+ gpiod_direction_input(data_gpiods->desc[i]);
+
+ return ams_delta_read_next_byte(mtd);
+}
+
static void ams_delta_write_buf(struct mtd_info *mtd, const u_char *buf,
int len)
{
int i;

- for (i=0; i<len; i++)
- ams_delta_write_byte(mtd, buf[i]);
+ if (len > 0)
+ ams_delta_write_byte(mtd, buf[0]);
+ for (i = 1; i < len; i++)
+ ams_delta_write_next_byte(mtd, buf[i]);
}

static void ams_delta_read_buf(struct mtd_info *mtd, u_char *buf, int len)
{
int i;

- for (i=0; i<len; i++)
- buf[i] = ams_delta_read_byte(mtd);
+ if (len > 0)
+ buf[0] = ams_delta_read_byte(mtd);
+ for (i = 1; i < len; i++)
+ buf[i] = ams_delta_read_next_byte(mtd);
}

/*
--
2.16.4


2018-08-06 22:33:03

by Janusz Krzysztofik

[permalink] [raw]
Subject: [RFC PATCH v2 03/12] ARM: OMAP1: ams-delta: Provide GPIO lookup table for NAND data port

Data port used by Amstrad Delta NAND driver is actually an OMAP MPUIO
device, already under control of gpio-omap driver. The NAND driver
gets access to the port by ioremapping it and performs read/write
operations. That is done without any proteciton from other users
legally manipulating the port pins over GPIO API.

The plan is to convert the driver to access the port over GPIO consumer
API. Before that is implemented, the driver can already obtain
exclusive access to the port by requesting an array of its GPIO
descriptors.

Add respective entries to the NAND GPIO lookup table.

Signed-off-by: Janusz Krzysztofik <[email protected]>
---
arch/arm/mach-omap1/board-ams-delta.c | 11 ++++++++++-
1 file changed, 10 insertions(+), 1 deletion(-)

diff --git a/arch/arm/mach-omap1/board-ams-delta.c b/arch/arm/mach-omap1/board-ams-delta.c
index eedacdfe9725..16f7bbe47607 100644
--- a/arch/arm/mach-omap1/board-ams-delta.c
+++ b/arch/arm/mach-omap1/board-ams-delta.c
@@ -337,7 +337,8 @@ static struct platform_device ams_delta_nand_device = {
.resource = ams_delta_nand_resources,
};

-#define OMAP_GPIO_LABEL "gpio-0-15"
+#define OMAP_GPIO_LABEL "gpio-0-15"
+#define OMAP_MPUIO_LABEL "mpuio"

static struct gpiod_lookup_table ams_delta_nand_gpio_table = {
.table = {
@@ -349,6 +350,14 @@ static struct gpiod_lookup_table ams_delta_nand_gpio_table = {
GPIO_LOOKUP(LATCH2_LABEL, LATCH2_PIN_NAND_NWE, "nwe", 0),
GPIO_LOOKUP(LATCH2_LABEL, LATCH2_PIN_NAND_ALE, "ale", 0),
GPIO_LOOKUP(LATCH2_LABEL, LATCH2_PIN_NAND_CLE, "cle", 0),
+ GPIO_LOOKUP_IDX(OMAP_MPUIO_LABEL, 0, "data", 0, 0),
+ GPIO_LOOKUP_IDX(OMAP_MPUIO_LABEL, 1, "data", 1, 0),
+ GPIO_LOOKUP_IDX(OMAP_MPUIO_LABEL, 2, "data", 2, 0),
+ GPIO_LOOKUP_IDX(OMAP_MPUIO_LABEL, 3, "data", 3, 0),
+ GPIO_LOOKUP_IDX(OMAP_MPUIO_LABEL, 4, "data", 4, 0),
+ GPIO_LOOKUP_IDX(OMAP_MPUIO_LABEL, 5, "data", 5, 0),
+ GPIO_LOOKUP_IDX(OMAP_MPUIO_LABEL, 6, "data", 6, 0),
+ GPIO_LOOKUP_IDX(OMAP_MPUIO_LABEL, 7, "data", 7, 0),
{ },
},
};
--
2.16.4


2018-08-06 22:33:09

by Janusz Krzysztofik

[permalink] [raw]
Subject: [RFC PATCH v2] mtd: rawnand: ams-delta: Use GPIO API for data I/O

Implement the idea suggested by Artem Bityutskiy and Tony Lindgren,
described in commit b027274d2e3a ("mtd: ams-delta: fix
request_mem_region() failure"). Use pure GPIO API as suggested by
Boris Brezillon.


Janusz Krzysztofik (12):
mtd: rawnand: ams-delta: Assign mtd->dev.parent, not mtd->owner
mtd: rawnand: ams-delta: Use private structure
ARM: OMAP1: ams-delta: Provide GPIO lookup table for NAND data port
mtd: rawnand: ams-delta: request data port GPIO resource
mtd: rawnand: ams-delta: use GPIO API for data read/write
ARM: OMAP1: ams-delta: drop obsolete NAND resources
mtd: rawnand: ams-delta: Set port direction once per transfer
mtd: rawnand: ams-delta: Simplify pointer resolution on read/write
gpiolib: Identify GPIO descriptor arrays with direct mapping
gpiolib: Introduce bitmap get/set array API extension
mtd: rawnand: ams-delta: Use GPIO API bitmap extension
gpiolib: Add fast processing path to bitmap API functions


Changelog:
v2:
[RFC PATCH v2 00/12] mtd: rawnand: ams-delta: Use GPIO API for data I/O
- renamed from former [RFC PATCH 0/8] mtd: rawnand: ams-delta: Use
gpio-omap accessors for data I/O
[RFC PATCH v2 01/12] mtd: rawnand: ams-delta: Assign mtd->dev.parent,
not mtd->owner
- split out from former [RFC PATCH 1/8] on Boris request, thanks.
[RFC PATCH v2 02/12] mtd: rawnand: ams-delta: Use private structure
- remaining part of the former [RFC PATCH 1/8].
[RFC PATCH v2 03/12] ARM: OMAP1: ams-delta: Provide GPIO lookup table
for NAND data port
- split out from former [RFC PATCH 5/8] on Boris requesst, thanks,
[RFC PATCH v2 04/12] mtd: rawnand: ams-delta: request data port GPIO resource
- remaining part of the former [RFC PATCH 5/8],
[RFC PATCH v2 05/12] mtd: rawnand: ams-delta: use GPIO API for data read/write
- reworked from former [RFC PATCH 8/8] on Boris requesst to use pure
GPIO API, thanks,
- moved up in front of former [RFC PATCH 3/8] on Boris request, thanks.
[RFC PATCH v2 06/12] ARM: OMAP1: ams-delta: drop obsolete NAND resources
- split out from former [RFC PATCH 8/8].
[RFC PATCH v2 07/12] mtd: rawnand: ams-delta: Set port direction once per
transfer
- reworked from former [RFC PATCH 3/8] on top of [RFC PATCH v2 05/12].
[RFC PATCH v2 08/12] mtd: rawnand: ams-delta: Simplify pointer resolution
on read/write
- reworked from former [RFC PATCH 4/8] on top of [RFC PATCH v2 08/12],
- renamed from 'Optimize' to 'Simplify' on Boris request, thanks.
[RFC PATCH v2 09/12] gpiolib: Identify GPIO descriptor arrays with direct
mapping
- new patch.
[RFC PATCH v2 10/12] gpiolib: Introduce bitmap get/set array API extension
- new patch.
[RFC PATCH v2 11/12] mtd: rawnand: ams-delta: Use GPIO API bitmap extension
- new patch.
[RFC PATCH v2 12/12] gpiolib: Add fast processing path to bitmap API functions
- new patch.
Removed from the series:
[RFC PATCH 2/8] mtd: rawnand: ams-delta: Write protect device during probe
- postponed on Boris request, thanks.
[RFC PATCH 6/8] gpio: omap: Add get/set_multiple() callbacks
- already applied by Linux in linux-gpio tree, thanks.
[RFC PATCH 7/8] mtd: rawnand: ams-delta: Check sanity of data GPIO resource
- most controversial one, no longer needed after switching to GPIO API.


diffstat:
Documentation/driver-api/gpio/consumer.rst | 36 ++
arch/arm/mach-omap1/board-ams-delta.c | 22 -
drivers/gpio/gpiolib.c | 237 +++++++++++++++++++
drivers/mtd/nand/raw/ams-delta.c | 350 +++++++++++++++--------------
include/linux/gpio/consumer.h | 15 +
5 files changed, 485 insertions(+), 175 deletions(-)


2018-08-06 22:33:21

by Janusz Krzysztofik

[permalink] [raw]
Subject: [RFC PATCH v2 12/12] gpiolib: Add fast processing path to bitmap API functions

Certain GPIO descriptor arrays returned by gpio_get_array() may contain
information on a single GPIO chip driving array member pins in hardware
order. In such cases, bitmaps of values can be passed directly to the
chip callback functions without wasting time on iterations.

Add respective code to gpiod_get/set_array_bitmap_complex() functions.

Signed-off-by: Janusz Krzysztofik <[email protected]>
---
Documentation/driver-api/gpio/consumer.rst | 6 ++++++
drivers/gpio/gpiolib.c | 14 ++++++++++++++
2 files changed, 20 insertions(+)

diff --git a/Documentation/driver-api/gpio/consumer.rst b/Documentation/driver-api/gpio/consumer.rst
index bec4eab3b87c..b82f134dc352 100644
--- a/Documentation/driver-api/gpio/consumer.rst
+++ b/Documentation/driver-api/gpio/consumer.rst
@@ -409,6 +409,12 @@ descriptor arrays, only those of type struct gpio_descs returned by
gpiod_get_array() and its variants. Supported array size is limited to the size
of the bitmap, i.e., sizeof(unsigned long).

+If the .chip member of the array structure, filled in by gpiod_get_array() in
+certain circumstances, contains a valid GPIO chip descriptor, the raw variants
+of the functions can take fast processing paths, passing bitmap arguments
+directly to the chip callback functions. That allows for utilization of GPIO
+banks as data I/O ports without much loss of performance.
+

GPIOs mapped to IRQs
--------------------
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index 5b541364dee0..bf95f2964bc5 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -2846,6 +2846,12 @@ int gpiod_get_array_bitmap_complex(bool raw, bool can_sleep,
if (array->ndescs > sizeof(*bits))
return -EINVAL;

+ if (raw && !IS_ERR_OR_NULL(array->chip)) {
+ unsigned long mask = (1ULL << array->ndescs) - 1;
+
+ return gpio_chip_get_multiple(array->chip, &mask, bits);
+ }
+
i = gpiod_get_array_value_complex(raw, can_sleep, array->ndescs,
array->desc, value_array);
if (i)
@@ -3156,6 +3162,14 @@ int gpiod_set_array_bitmap_complex(bool raw, bool can_sleep,
if (array->ndescs > sizeof(*bits))
return -EINVAL;

+ if (raw && !IS_ERR_OR_NULL(array->chip)) {
+ unsigned long mask = (1ULL << array->ndescs) - 1;
+
+ gpio_chip_set_multiple(array->chip, &mask, bits);
+
+ return 0;
+ }
+
for (i = 0; i < array->ndescs; i++)
value_array[i] = test_bit(i, bits);

--
2.16.4


2018-08-06 22:33:34

by Janusz Krzysztofik

[permalink] [raw]
Subject: [RFC PATCH v2 06/12] ARM: OMAP1: ams-delta: drop obsolete NAND resources

Amstrad Delta NAND device now uses GPIO API for data I/O so there is no
need to assign memory I/O resource to the device any longer. Drop it.

Signed-off-by: Janusz Krzysztofik <[email protected]>
---
arch/arm/mach-omap1/board-ams-delta.c | 11 -----------
1 file changed, 11 deletions(-)

diff --git a/arch/arm/mach-omap1/board-ams-delta.c b/arch/arm/mach-omap1/board-ams-delta.c
index 16f7bbe47607..08e732bc1cd2 100644
--- a/arch/arm/mach-omap1/board-ams-delta.c
+++ b/arch/arm/mach-omap1/board-ams-delta.c
@@ -321,20 +321,9 @@ struct modem_private_data {

static struct modem_private_data modem_priv;

-static struct resource ams_delta_nand_resources[] = {
- [0] = {
- .start = OMAP1_MPUIO_BASE,
- .end = OMAP1_MPUIO_BASE +
- OMAP_MPUIO_IO_CNTL + sizeof(u32) - 1,
- .flags = IORESOURCE_MEM,
- },
-};
-
static struct platform_device ams_delta_nand_device = {
.name = "ams-delta-nand",
.id = -1,
- .num_resources = ARRAY_SIZE(ams_delta_nand_resources),
- .resource = ams_delta_nand_resources,
};

#define OMAP_GPIO_LABEL "gpio-0-15"
--
2.16.4


2018-08-06 22:33:40

by Janusz Krzysztofik

[permalink] [raw]
Subject: [RFC PATCH v2 04/12] mtd: rawnand: ams-delta: request data port GPIO resource

Data port used by the driver is actually an OMAP MPUIO device, already
under control of gpio-omap driver. For that reason we used to not
request the memory region of the port as that would fail because the
region is already busy. Despite that, we are still accessing the port
by just ioremapping it and performing read/write operations. Moreover,
we are doing that without any proteciton from other users legally
manipulating the port pins over GPIO API.

The plan is to convert the driver to access the port over functions
exposed by the gpio-omap driver. Before that happens, already prevent
from other users accessing the port pins by requesting an array of its
GPIO descriptors.

Signed-off-by: Janusz Krzysztofik <[email protected]>
---
drivers/mtd/nand/raw/ams-delta.c | 8 ++++++++
1 file changed, 8 insertions(+)

diff --git a/drivers/mtd/nand/raw/ams-delta.c b/drivers/mtd/nand/raw/ams-delta.c
index 48233d638d2a..09d6901fc94d 100644
--- a/drivers/mtd/nand/raw/ams-delta.c
+++ b/drivers/mtd/nand/raw/ams-delta.c
@@ -161,6 +161,7 @@ static int ams_delta_init(struct platform_device *pdev)
struct mtd_info *mtd;
struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
void __iomem *io_base;
+ struct gpio_descs *data_gpiods;
int err = 0;

if (!res)
@@ -261,6 +262,13 @@ static int ams_delta_init(struct platform_device *pdev)
dev_err(&pdev->dev, "CLE GPIO request failed (%d)\n", err);
goto out_mtd;
}
+ /* Request array of data pins, initialize them as input */
+ data_gpiods = devm_gpiod_get_array(&pdev->dev, "data", GPIOD_IN);
+ if (IS_ERR(data_gpiods)) {
+ err = PTR_ERR(data_gpiods);
+ dev_err(&pdev->dev, "data GPIO request failed: %d\n", err);
+ goto out_mtd;
+ }

/* Scan to find existence of the device */
err = nand_scan(mtd, 1);
--
2.16.4


2018-08-06 23:00:30

by Janusz Krzysztofik

[permalink] [raw]
Subject: [RFC PATCH v2 08/12] mtd: rawnand: ams-delta: Simplify pointer resolution on read/write

Simplify data read/write sub-functions by changing their APIs so they
accept driver private structure pointer instead of mtd_info.

Signed-off-by: Janusz Krzysztofik <[email protected]>
---
drivers/mtd/nand/raw/ams-delta.c | 40 ++++++++++++++++++++++------------------
1 file changed, 22 insertions(+), 18 deletions(-)

diff --git a/drivers/mtd/nand/raw/ams-delta.c b/drivers/mtd/nand/raw/ams-delta.c
index d02c48c013e8..30c461138195 100644
--- a/drivers/mtd/nand/raw/ams-delta.c
+++ b/drivers/mtd/nand/raw/ams-delta.c
@@ -76,10 +76,8 @@ static void ams_delta_write_commit(struct ams_delta_nand *priv)
gpiod_set_value(priv->gpiod_nwe, 1);
}

-static void ams_delta_write_next_byte(struct mtd_info *mtd, u_char byte)
+static void ams_delta_write_next_byte(struct ams_delta_nand *priv, u_char byte)
{
- struct nand_chip *this = mtd_to_nand(mtd);
- struct ams_delta_nand *priv = nand_get_controller_data(this);
struct gpio_descs *data_gpiods = priv->data_gpiods;
unsigned long bits = byte;
int i, value_array[data_gpiods->ndescs];
@@ -93,10 +91,8 @@ static void ams_delta_write_next_byte(struct mtd_info *mtd, u_char byte)
ams_delta_write_commit(priv);
}

-static void ams_delta_write_byte(struct mtd_info *mtd, u_char byte)
+static void ams_delta_write_first_byte(struct ams_delta_nand *priv, u_char byte)
{
- struct nand_chip *this = mtd_to_nand(mtd);
- struct ams_delta_nand *priv = nand_get_controller_data(this);
struct gpio_descs *data_gpiods = priv->data_gpiods;
unsigned long bits = byte;
int i;
@@ -108,10 +104,8 @@ static void ams_delta_write_byte(struct mtd_info *mtd, u_char byte)
ams_delta_write_commit(priv);
}

-static u_char ams_delta_read_next_byte(struct mtd_info *mtd)
+static u_char ams_delta_read_next_byte(struct ams_delta_nand *priv)
{
- struct nand_chip *this = mtd_to_nand(mtd);
- struct ams_delta_nand *priv = nand_get_controller_data(this);
struct gpio_descs *data_gpiods = priv->data_gpiods;
unsigned long bits = 0;
int i, value_array[data_gpiods->ndescs];
@@ -131,38 +125,48 @@ static u_char ams_delta_read_next_byte(struct mtd_info *mtd)
return bits;
}

-static u_char ams_delta_read_byte(struct mtd_info *mtd)
+static u_char ams_delta_read_first_byte(struct ams_delta_nand *priv)
{
- struct nand_chip *this = mtd_to_nand(mtd);
- struct ams_delta_nand *priv = nand_get_controller_data(this);
struct gpio_descs *data_gpiods = priv->data_gpiods;
int i;

for (i = 0; i < data_gpiods->ndescs; i++)
gpiod_direction_input(data_gpiods->desc[i]);

- return ams_delta_read_next_byte(mtd);
+ return ams_delta_read_next_byte(priv);
+}
+
+static u_char ams_delta_read_byte(struct mtd_info *mtd)
+{
+ struct nand_chip *this = mtd_to_nand(mtd);
+ struct ams_delta_nand *priv = nand_get_controller_data(this);
+
+ return ams_delta_read_first_byte(priv);
}

static void ams_delta_write_buf(struct mtd_info *mtd, const u_char *buf,
int len)
{
+ struct nand_chip *this = mtd_to_nand(mtd);
+ struct ams_delta_nand *priv = nand_get_controller_data(this);
int i;

if (len > 0)
- ams_delta_write_byte(mtd, buf[0]);
+ ams_delta_write_first_byte(priv, buf[0]);
for (i = 1; i < len; i++)
- ams_delta_write_next_byte(mtd, buf[i]);
+ ams_delta_write_next_byte(priv, buf[i]);
}

static void ams_delta_read_buf(struct mtd_info *mtd, u_char *buf, int len)
{
+ struct nand_chip *this = mtd_to_nand(mtd);
+ struct ams_delta_nand *priv = nand_get_controller_data(this);
int i;

if (len > 0)
- buf[0] = ams_delta_read_byte(mtd);
+ buf[0] = ams_delta_read_first_byte(priv);
for (i = 1; i < len; i++)
- buf[i] = ams_delta_read_next_byte(mtd);
+ buf[i] = ams_delta_read_next_byte(priv);
}

/*
@@ -186,7 +190,7 @@ static void ams_delta_hwcontrol(struct mtd_info *mtd, int cmd,
}

if (cmd != NAND_CMD_NONE)
- ams_delta_write_byte(mtd, cmd);
+ ams_delta_write_first_byte(priv, cmd);
}

static int ams_delta_nand_ready(struct mtd_info *mtd)
--
2.16.4


2018-08-06 23:31:07

by Linus Walleij

[permalink] [raw]
Subject: Re: [RFC PATCH v2 09/12] gpiolib: Identify GPIO descriptor arrays with direct mapping

Hi Janusz!

On Tue, Aug 7, 2018 at 12:29 AM Janusz Krzysztofik <[email protected]> wrote:

> Certain GPIO array lookup results may map directly to GPIO pins of a
> single GPIO chip in hardware order. If that condition is recognized
> and handled efficiently, significant performance gain of get/set array
> functions may be possible.
>
> While processing a request for an array of GPIO descriptors, verify if
> the descriptors just collected represent consecutive pins of a single
> GPIO chip. Pass that information with the array to the caller so it
> can benefit from enhanced performance as soon as bitmap based get/set
> array functions which can make efficient use of that are available.
>
> Signed-off-by: Janusz Krzysztofik <[email protected]>
(...)
> This function returns a struct gpio_descs which contains an array of
> -descriptors::
> +descriptors. It may also contain a valid descriptor of a single GPIO chip in
> +case the array strictly matches pin hardware layout of the chip::
>
> struct gpio_descs {
> unsigned int ndescs;
> struct gpio_desc *desc[];
> + struct gpio_chip *chip;

This must be motivated: if the only purpose is to indicate to the consumer that
all GPIOs are on the same chip, why not just have a

bool all_on_same_chip;

That you set to true if these are all on the same chip?

Yours,
Linus Walleij

2018-08-07 00:07:16

by Marek Vasut

[permalink] [raw]
Subject: Re: [RFC PATCH v2 01/12] mtd: rawnand: ams-delta: Assign mtd->dev.parent, not mtd->owner

On 08/07/2018 12:29 AM, Janusz Krzysztofik wrote:
> Fix missing mtd->dev.parent assignment and drop useless mtd->owner.

You fail to explain why this fix is required.

> Signed-off-by: Janusz Krzysztofik <[email protected]>
> ---
> drivers/mtd/nand/raw/ams-delta.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/drivers/mtd/nand/raw/ams-delta.c b/drivers/mtd/nand/raw/ams-delta.c
> index 2a8872ebd14a..af313c620264 100644
> --- a/drivers/mtd/nand/raw/ams-delta.c
> +++ b/drivers/mtd/nand/raw/ams-delta.c
> @@ -162,7 +162,7 @@ static int ams_delta_init(struct platform_device *pdev)
> }
>
> ams_delta_mtd = nand_to_mtd(this);
> - ams_delta_mtd->owner = THIS_MODULE;
> + ams_delta_mtd->dev.parent = &pdev->dev;
>
> /*
> * Don't try to request the memory region from here,
>


--
Best regards,
Marek Vasut

2018-08-07 00:09:08

by Marek Vasut

[permalink] [raw]
Subject: Re: [RFC PATCH v2 05/12] mtd: rawnand: ams-delta: use GPIO API for data read/write

On 08/07/2018 12:29 AM, Janusz Krzysztofik wrote:
> Don't readw()/writew() data directly from/to GPIO port which is under
> control of gpio-omap driver, use GPIO API instead.
>
> Degrade of performance on Amstrad Delta is completely not acceptable.

I'd expect that changing from direct PIO to access through GPIO API
would degrade the performance. Maybe I misunderstood something ?

> The driver should work with any 8+-bit bidirectional GPIO port, not
> only OMAP.
>
> Signed-off-by: Janusz Krzysztofik <[email protected]>
> ---
> drivers/mtd/nand/raw/ams-delta.c | 97 ++++++++++++++++------------------------
> 1 file changed, 38 insertions(+), 59 deletions(-)
>
> diff --git a/drivers/mtd/nand/raw/ams-delta.c b/drivers/mtd/nand/raw/ams-delta.c
> index 09d6901fc94d..78996ddf82e0 100644
> --- a/drivers/mtd/nand/raw/ams-delta.c
> +++ b/drivers/mtd/nand/raw/ams-delta.c
> @@ -24,13 +24,10 @@
> #include <linux/mtd/mtd.h>
> #include <linux/mtd/rawnand.h>
> #include <linux/mtd/partitions.h>
> -#include <linux/platform_data/gpio-omap.h>
> +#include <linux/platform_device.h>
>
> -#include <asm/io.h>
> #include <asm/sizes.h>
>
> -#include <mach/hardware.h>
> -
> /*
> * MTD structure for E3 (Delta)
> */
> @@ -44,7 +41,7 @@ struct ams_delta_nand {
> struct gpio_desc *gpiod_nwe;
> struct gpio_desc *gpiod_ale;
> struct gpio_desc *gpiod_cle;
> - void __iomem *io_base;
> + struct gpio_descs *data_gpiods;
> };
>
> /*
> @@ -76,10 +73,14 @@ static void ams_delta_write_byte(struct mtd_info *mtd, u_char byte)
> {
> struct nand_chip *this = mtd_to_nand(mtd);
> struct ams_delta_nand *priv = nand_get_controller_data(this);
> - void __iomem *io_base = priv->io_base;
> + struct gpio_descs *data_gpiods = priv->data_gpiods;
> + unsigned long bits = byte;
> + int i;
> +
> + for (i = 0; i < data_gpiods->ndescs; i++)
> + gpiod_direction_output_raw(data_gpiods->desc[i],
> + test_bit(i, &bits));
>
> - writew(0, io_base + OMAP_MPUIO_IO_CNTL);
> - writew(byte, this->IO_ADDR_W);
> gpiod_set_value(priv->gpiod_nwe, 0);
> ndelay(40);
> gpiod_set_value(priv->gpiod_nwe, 1);
> @@ -87,18 +88,28 @@ static void ams_delta_write_byte(struct mtd_info *mtd, u_char byte)
>
> static u_char ams_delta_read_byte(struct mtd_info *mtd)
> {
> - u_char res;
> struct nand_chip *this = mtd_to_nand(mtd);
> struct ams_delta_nand *priv = nand_get_controller_data(this);
> - void __iomem *io_base = priv->io_base;
> + struct gpio_descs *data_gpiods = priv->data_gpiods;
> + unsigned long bits = 0;
> + int i, value_array[data_gpiods->ndescs];
> +
> + for (i = 0; i < data_gpiods->ndescs; i++)
> + gpiod_direction_input(data_gpiods->desc[i]);
>
> gpiod_set_value(priv->gpiod_nre, 0);
> ndelay(40);
> - writew(~0, io_base + OMAP_MPUIO_IO_CNTL);
> - res = readw(this->IO_ADDR_R);
> +
> + gpiod_get_raw_array_value(data_gpiods->ndescs, data_gpiods->desc,
> + value_array);
> +
> gpiod_set_value(priv->gpiod_nre, 1);
>
> - return res;
> + for (i = 0; i < data_gpiods->ndescs; i++)
> + if (value_array[i])
> + __set_bit(i, &bits);
> +
> + return bits;
> }
>
> static void ams_delta_write_buf(struct mtd_info *mtd, const u_char *buf,
> @@ -159,14 +170,8 @@ static int ams_delta_init(struct platform_device *pdev)
> struct ams_delta_nand *priv;
> struct nand_chip *this;
> struct mtd_info *mtd;
> - struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> - void __iomem *io_base;
> - struct gpio_descs *data_gpiods;
> int err = 0;
>
> - if (!res)
> - return -ENXIO;
> -
> /* Allocate memory for MTD device structure and private data */
> priv = devm_kzalloc(&pdev->dev, sizeof(struct ams_delta_nand),
> GFP_KERNEL);
> @@ -179,25 +184,8 @@ static int ams_delta_init(struct platform_device *pdev)
> mtd = nand_to_mtd(this);
> mtd->dev.parent = &pdev->dev;
>
> - /*
> - * Don't try to request the memory region from here,
> - * it should have been already requested from the
> - * gpio-omap driver and requesting it again would fail.
> - */
> -
> - io_base = ioremap(res->start, resource_size(res));
> - if (io_base == NULL) {
> - dev_err(&pdev->dev, "ioremap failed\n");
> - err = -EIO;
> - goto out_free;
> - }
> -
> - priv->io_base = io_base;
> nand_set_controller_data(this, priv);
>
> - /* Set address of NAND IO lines */
> - this->IO_ADDR_R = io_base + OMAP_MPUIO_INPUT_LATCH;
> - this->IO_ADDR_W = io_base + OMAP_MPUIO_OUTPUT;
> this->read_byte = ams_delta_read_byte;
> this->write_buf = ams_delta_write_buf;
> this->read_buf = ams_delta_read_buf;
> @@ -207,7 +195,7 @@ static int ams_delta_init(struct platform_device *pdev)
> if (IS_ERR(priv->gpiod_rdy)) {
> err = PTR_ERR(priv->gpiod_rdy);
> dev_warn(&pdev->dev, "RDY GPIO request failed (%d)\n", err);
> - goto out_mtd;
> + return err;
> }
>
> if (priv->gpiod_rdy)
> @@ -225,66 +213,60 @@ static int ams_delta_init(struct platform_device *pdev)
> if (IS_ERR(priv->gpiod_nwp)) {
> err = PTR_ERR(priv->gpiod_nwp);
> dev_err(&pdev->dev, "NWP GPIO request failed (%d)\n", err);
> - goto out_mtd;
> + return err;
> }
>
> priv->gpiod_nce = devm_gpiod_get(&pdev->dev, "nce", GPIOD_OUT_HIGH);
> if (IS_ERR(priv->gpiod_nce)) {
> err = PTR_ERR(priv->gpiod_nce);
> dev_err(&pdev->dev, "NCE GPIO request failed (%d)\n", err);
> - goto out_mtd;
> + return err;
> }
>
> priv->gpiod_nre = devm_gpiod_get(&pdev->dev, "nre", GPIOD_OUT_HIGH);
> if (IS_ERR(priv->gpiod_nre)) {
> err = PTR_ERR(priv->gpiod_nre);
> dev_err(&pdev->dev, "NRE GPIO request failed (%d)\n", err);
> - goto out_mtd;
> + return err;
> }
>
> priv->gpiod_nwe = devm_gpiod_get(&pdev->dev, "nwe", GPIOD_OUT_HIGH);
> if (IS_ERR(priv->gpiod_nwe)) {
> err = PTR_ERR(priv->gpiod_nwe);
> dev_err(&pdev->dev, "NWE GPIO request failed (%d)\n", err);
> - goto out_mtd;
> + return err;
> }
>
> priv->gpiod_ale = devm_gpiod_get(&pdev->dev, "ale", GPIOD_OUT_LOW);
> if (IS_ERR(priv->gpiod_ale)) {
> err = PTR_ERR(priv->gpiod_ale);
> dev_err(&pdev->dev, "ALE GPIO request failed (%d)\n", err);
> - goto out_mtd;
> + return err;
> }
>
> priv->gpiod_cle = devm_gpiod_get(&pdev->dev, "cle", GPIOD_OUT_LOW);
> if (IS_ERR(priv->gpiod_cle)) {
> err = PTR_ERR(priv->gpiod_cle);
> dev_err(&pdev->dev, "CLE GPIO request failed (%d)\n", err);
> - goto out_mtd;
> + return err;
> }
> /* Request array of data pins, initialize them as input */
> - data_gpiods = devm_gpiod_get_array(&pdev->dev, "data", GPIOD_IN);
> - if (IS_ERR(data_gpiods)) {
> - err = PTR_ERR(data_gpiods);
> + priv->data_gpiods = devm_gpiod_get_array(&pdev->dev, "data", GPIOD_IN);
> + if (IS_ERR(priv->data_gpiods)) {
> + err = PTR_ERR(priv->data_gpiods);
> dev_err(&pdev->dev, "data GPIO request failed: %d\n", err);
> - goto out_mtd;
> + return err;
> }
>
> /* Scan to find existence of the device */
> err = nand_scan(mtd, 1);
> if (err)
> - goto out_mtd;
> + return err;
>
> /* Register the partitions */
> mtd_device_register(mtd, partition_info, ARRAY_SIZE(partition_info));
>
> - goto out;
> -
> - out_mtd:
> - iounmap(io_base);
> -out_free:
> - out:
> - return err;
> + return 0;
> }
>
> /*
> @@ -294,13 +276,10 @@ static int ams_delta_cleanup(struct platform_device *pdev)
> {
> struct ams_delta_nand *priv = platform_get_drvdata(pdev);
> struct mtd_info *mtd = nand_to_mtd(&priv->nand_chip);
> - void __iomem *io_base = priv->io_base;
>
> - /* Release resources, unregister device */
> + /* Unregister device */
> nand_release(mtd);
>
> - iounmap(io_base);
> -
> return 0;
> }
>
>


--
Best regards,
Marek Vasut

2018-08-07 00:22:24

by Linus Walleij

[permalink] [raw]
Subject: Re: [RFC PATCH v2 12/12] gpiolib: Add fast processing path to bitmap API functions

On Tue, Aug 7, 2018 at 12:29 AM Janusz Krzysztofik <[email protected]> wrote:

Hi Janusz!

> Certain GPIO descriptor arrays returned by gpio_get_array() may contain
> information on a single GPIO chip driving array member pins in hardware
> order. In such cases, bitmaps of values can be passed directly to the
> chip callback functions without wasting time on iterations.
>
> Add respective code to gpiod_get/set_array_bitmap_complex() functions.
>
> Signed-off-by: Janusz Krzysztofik <[email protected]>

I think it would be disappointing to leave all the users of the old
array API without the performance improvement. I think we need to
deal with this in a way such that everyone can benefit from it.

Also it is kludgy if users (consumers) would need to handle the case
where all lines are on the same chip separately, through the bitmap
API.

What we need is an API that:

- All drivers handling arrays can use (including current users).

- Enables speed-up if the lines are all on the same chip/register.

- Doesn't require consumers to know if they are all on the same
chip or not.

This means a deep API with a small surface.

How do we achieve this the best way?

Yours,
Linus Walleij

2018-08-07 17:02:03

by Boris Brezillon

[permalink] [raw]
Subject: Re: [RFC PATCH v2 03/12] ARM: OMAP1: ams-delta: Provide GPIO lookup table for NAND data port

On Tue, 7 Aug 2018 00:29:09 +0200
Janusz Krzysztofik <[email protected]> wrote:

> Data port used by Amstrad Delta NAND driver is actually an OMAP MPUIO
> device, already under control of gpio-omap driver. The NAND driver
> gets access to the port by ioremapping it and performs read/write
> operations. That is done without any proteciton from other users
> legally manipulating the port pins over GPIO API.
>
> The plan is to convert the driver to access the port over GPIO consumer
> API. Before that is implemented, the driver can already obtain
> exclusive access to the port by requesting an array of its GPIO
> descriptors.
>
> Add respective entries to the NAND GPIO lookup table.
>
> Signed-off-by: Janusz Krzysztofik <[email protected]>

Reviewed-by: Boris Brezillon <[email protected]>

> ---
> arch/arm/mach-omap1/board-ams-delta.c | 11 ++++++++++-
> 1 file changed, 10 insertions(+), 1 deletion(-)
>
> diff --git a/arch/arm/mach-omap1/board-ams-delta.c b/arch/arm/mach-omap1/board-ams-delta.c
> index eedacdfe9725..16f7bbe47607 100644
> --- a/arch/arm/mach-omap1/board-ams-delta.c
> +++ b/arch/arm/mach-omap1/board-ams-delta.c
> @@ -337,7 +337,8 @@ static struct platform_device ams_delta_nand_device = {
> .resource = ams_delta_nand_resources,
> };
>
> -#define OMAP_GPIO_LABEL "gpio-0-15"
> +#define OMAP_GPIO_LABEL "gpio-0-15"
> +#define OMAP_MPUIO_LABEL "mpuio"
>
> static struct gpiod_lookup_table ams_delta_nand_gpio_table = {
> .table = {
> @@ -349,6 +350,14 @@ static struct gpiod_lookup_table ams_delta_nand_gpio_table = {
> GPIO_LOOKUP(LATCH2_LABEL, LATCH2_PIN_NAND_NWE, "nwe", 0),
> GPIO_LOOKUP(LATCH2_LABEL, LATCH2_PIN_NAND_ALE, "ale", 0),
> GPIO_LOOKUP(LATCH2_LABEL, LATCH2_PIN_NAND_CLE, "cle", 0),
> + GPIO_LOOKUP_IDX(OMAP_MPUIO_LABEL, 0, "data", 0, 0),
> + GPIO_LOOKUP_IDX(OMAP_MPUIO_LABEL, 1, "data", 1, 0),
> + GPIO_LOOKUP_IDX(OMAP_MPUIO_LABEL, 2, "data", 2, 0),
> + GPIO_LOOKUP_IDX(OMAP_MPUIO_LABEL, 3, "data", 3, 0),
> + GPIO_LOOKUP_IDX(OMAP_MPUIO_LABEL, 4, "data", 4, 0),
> + GPIO_LOOKUP_IDX(OMAP_MPUIO_LABEL, 5, "data", 5, 0),
> + GPIO_LOOKUP_IDX(OMAP_MPUIO_LABEL, 6, "data", 6, 0),
> + GPIO_LOOKUP_IDX(OMAP_MPUIO_LABEL, 7, "data", 7, 0),
> { },
> },
> };


2018-08-07 17:02:44

by Boris Brezillon

[permalink] [raw]
Subject: Re: [RFC PATCH v2 04/12] mtd: rawnand: ams-delta: request data port GPIO resource

On Tue, 7 Aug 2018 00:29:10 +0200
Janusz Krzysztofik <[email protected]> wrote:

> Data port used by the driver is actually an OMAP MPUIO device, already
> under control of gpio-omap driver. For that reason we used to not
> request the memory region of the port as that would fail because the
> region is already busy. Despite that, we are still accessing the port
> by just ioremapping it and performing read/write operations. Moreover,
> we are doing that without any proteciton from other users legally
> manipulating the port pins over GPIO API.
>
> The plan is to convert the driver to access the port over functions
> exposed by the gpio-omap driver. Before that happens, already prevent
> from other users accessing the port pins by requesting an array of its
> GPIO descriptors.
>
> Signed-off-by: Janusz Krzysztofik <[email protected]>

Reviewed-by: Boris Brezillon <[email protected]>

> ---
> drivers/mtd/nand/raw/ams-delta.c | 8 ++++++++
> 1 file changed, 8 insertions(+)
>
> diff --git a/drivers/mtd/nand/raw/ams-delta.c b/drivers/mtd/nand/raw/ams-delta.c
> index 48233d638d2a..09d6901fc94d 100644
> --- a/drivers/mtd/nand/raw/ams-delta.c
> +++ b/drivers/mtd/nand/raw/ams-delta.c
> @@ -161,6 +161,7 @@ static int ams_delta_init(struct platform_device *pdev)
> struct mtd_info *mtd;
> struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> void __iomem *io_base;
> + struct gpio_descs *data_gpiods;
> int err = 0;
>
> if (!res)
> @@ -261,6 +262,13 @@ static int ams_delta_init(struct platform_device *pdev)
> dev_err(&pdev->dev, "CLE GPIO request failed (%d)\n", err);
> goto out_mtd;
> }
> + /* Request array of data pins, initialize them as input */
> + data_gpiods = devm_gpiod_get_array(&pdev->dev, "data", GPIOD_IN);
> + if (IS_ERR(data_gpiods)) {
> + err = PTR_ERR(data_gpiods);
> + dev_err(&pdev->dev, "data GPIO request failed: %d\n", err);
> + goto out_mtd;
> + }
>
> /* Scan to find existence of the device */
> err = nand_scan(mtd, 1);


2018-08-07 17:04:47

by Boris Brezillon

[permalink] [raw]
Subject: Re: [RFC PATCH v2 02/12] mtd: rawnand: ams-delta: Use private structure

On Tue, 7 Aug 2018 00:29:08 +0200
Janusz Krzysztofik <[email protected]> wrote:

> Introduce a driver private structure and allocate it on device probe.
> Use it for storing nand_chip structure, GPIO descriptors prevoiusly
> stored in static variables as well as io_base pointer previously passed
> as nand controller data or platform driver data. Subsequent patches
> may populate the structure with more members as needed.
>
> Signed-off-by: Janusz Krzysztofik <[email protected]>

Reviewed-by: Boris Brezillon <[email protected]>

> ---
> drivers/mtd/nand/raw/ams-delta.c | 126 +++++++++++++++++++++------------------
> 1 file changed, 69 insertions(+), 57 deletions(-)
>
> diff --git a/drivers/mtd/nand/raw/ams-delta.c b/drivers/mtd/nand/raw/ams-delta.c
> index af313c620264..48233d638d2a 100644
> --- a/drivers/mtd/nand/raw/ams-delta.c
> +++ b/drivers/mtd/nand/raw/ams-delta.c
> @@ -34,14 +34,18 @@
> /*
> * MTD structure for E3 (Delta)
> */
> -static struct mtd_info *ams_delta_mtd = NULL;
> -static struct gpio_desc *gpiod_rdy;
> -static struct gpio_desc *gpiod_nce;
> -static struct gpio_desc *gpiod_nre;
> -static struct gpio_desc *gpiod_nwp;
> -static struct gpio_desc *gpiod_nwe;
> -static struct gpio_desc *gpiod_ale;
> -static struct gpio_desc *gpiod_cle;
> +
> +struct ams_delta_nand {
> + struct nand_chip nand_chip;
> + struct gpio_desc *gpiod_rdy;
> + struct gpio_desc *gpiod_nce;
> + struct gpio_desc *gpiod_nre;
> + struct gpio_desc *gpiod_nwp;
> + struct gpio_desc *gpiod_nwe;
> + struct gpio_desc *gpiod_ale;
> + struct gpio_desc *gpiod_cle;
> + void __iomem *io_base;
> +};
>
> /*
> * Define partitions for flash devices
> @@ -71,26 +75,28 @@ static const struct mtd_partition partition_info[] = {
> static void ams_delta_write_byte(struct mtd_info *mtd, u_char byte)
> {
> struct nand_chip *this = mtd_to_nand(mtd);
> - void __iomem *io_base = (void __iomem *)nand_get_controller_data(this);
> + struct ams_delta_nand *priv = nand_get_controller_data(this);
> + void __iomem *io_base = priv->io_base;
>
> writew(0, io_base + OMAP_MPUIO_IO_CNTL);
> writew(byte, this->IO_ADDR_W);
> - gpiod_set_value(gpiod_nwe, 0);
> + gpiod_set_value(priv->gpiod_nwe, 0);
> ndelay(40);
> - gpiod_set_value(gpiod_nwe, 1);
> + gpiod_set_value(priv->gpiod_nwe, 1);
> }
>
> static u_char ams_delta_read_byte(struct mtd_info *mtd)
> {
> u_char res;
> struct nand_chip *this = mtd_to_nand(mtd);
> - void __iomem *io_base = (void __iomem *)nand_get_controller_data(this);
> + struct ams_delta_nand *priv = nand_get_controller_data(this);
> + void __iomem *io_base = priv->io_base;
>
> - gpiod_set_value(gpiod_nre, 0);
> + gpiod_set_value(priv->gpiod_nre, 0);
> ndelay(40);
> writew(~0, io_base + OMAP_MPUIO_IO_CNTL);
> res = readw(this->IO_ADDR_R);
> - gpiod_set_value(gpiod_nre, 1);
> + gpiod_set_value(priv->gpiod_nre, 1);
>
> return res;
> }
> @@ -123,11 +129,13 @@ static void ams_delta_read_buf(struct mtd_info *mtd, u_char *buf, int len)
> static void ams_delta_hwcontrol(struct mtd_info *mtd, int cmd,
> unsigned int ctrl)
> {
> + struct nand_chip *this = mtd_to_nand(mtd);
> + struct ams_delta_nand *priv = nand_get_controller_data(this);
>
> if (ctrl & NAND_CTRL_CHANGE) {
> - gpiod_set_value(gpiod_nce, !(ctrl & NAND_NCE));
> - gpiod_set_value(gpiod_cle, !!(ctrl & NAND_CLE));
> - gpiod_set_value(gpiod_ale, !!(ctrl & NAND_ALE));
> + gpiod_set_value(priv->gpiod_nce, !(ctrl & NAND_NCE));
> + gpiod_set_value(priv->gpiod_cle, !!(ctrl & NAND_CLE));
> + gpiod_set_value(priv->gpiod_ale, !!(ctrl & NAND_ALE));
> }
>
> if (cmd != NAND_CMD_NONE)
> @@ -136,7 +144,10 @@ static void ams_delta_hwcontrol(struct mtd_info *mtd, int cmd,
>
> static int ams_delta_nand_ready(struct mtd_info *mtd)
> {
> - return gpiod_get_value(gpiod_rdy);
> + struct nand_chip *this = mtd_to_nand(mtd);
> + struct ams_delta_nand *priv = nand_get_controller_data(this);
> +
> + return gpiod_get_value(priv->gpiod_rdy);
> }
>
>
> @@ -145,7 +156,9 @@ static int ams_delta_nand_ready(struct mtd_info *mtd)
> */
> static int ams_delta_init(struct platform_device *pdev)
> {
> + struct ams_delta_nand *priv;
> struct nand_chip *this;
> + struct mtd_info *mtd;
> struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> void __iomem *io_base;
> int err = 0;
> @@ -154,15 +167,16 @@ static int ams_delta_init(struct platform_device *pdev)
> return -ENXIO;
>
> /* Allocate memory for MTD device structure and private data */
> - this = kzalloc(sizeof(struct nand_chip), GFP_KERNEL);
> - if (!this) {
> + priv = devm_kzalloc(&pdev->dev, sizeof(struct ams_delta_nand),
> + GFP_KERNEL);
> + if (!priv) {
> pr_warn("Unable to allocate E3 NAND MTD device structure.\n");
> - err = -ENOMEM;
> - goto out;
> + return -ENOMEM;
> }
> + this = &priv->nand_chip;
>
> - ams_delta_mtd = nand_to_mtd(this);
> - ams_delta_mtd->dev.parent = &pdev->dev;
> + mtd = nand_to_mtd(this);
> + mtd->dev.parent = &pdev->dev;
>
> /*
> * Don't try to request the memory region from here,
> @@ -177,7 +191,8 @@ static int ams_delta_init(struct platform_device *pdev)
> goto out_free;
> }
>
> - nand_set_controller_data(this, (void *)io_base);
> + priv->io_base = io_base;
> + nand_set_controller_data(this, priv);
>
> /* Set address of NAND IO lines */
> this->IO_ADDR_R = io_base + OMAP_MPUIO_INPUT_LATCH;
> @@ -187,14 +202,14 @@ static int ams_delta_init(struct platform_device *pdev)
> this->read_buf = ams_delta_read_buf;
> this->cmd_ctrl = ams_delta_hwcontrol;
>
> - gpiod_rdy = devm_gpiod_get_optional(&pdev->dev, "rdy", GPIOD_IN);
> - if (IS_ERR(gpiod_rdy)) {
> - err = PTR_ERR(gpiod_rdy);
> + priv->gpiod_rdy = devm_gpiod_get_optional(&pdev->dev, "rdy", GPIOD_IN);
> + if (IS_ERR(priv->gpiod_rdy)) {
> + err = PTR_ERR(priv->gpiod_rdy);
> dev_warn(&pdev->dev, "RDY GPIO request failed (%d)\n", err);
> goto out_mtd;
> }
>
> - if (gpiod_rdy)
> + if (priv->gpiod_rdy)
> this->dev_ready = ams_delta_nand_ready;
>
> /* 25 us command delay time */
> @@ -202,66 +217,64 @@ static int ams_delta_init(struct platform_device *pdev)
> this->ecc.mode = NAND_ECC_SOFT;
> this->ecc.algo = NAND_ECC_HAMMING;
>
> - platform_set_drvdata(pdev, io_base);
> + platform_set_drvdata(pdev, priv);
>
> /* Set chip enabled, but */
> - gpiod_nwp = devm_gpiod_get(&pdev->dev, "nwp", GPIOD_OUT_HIGH);
> - if (IS_ERR(gpiod_nwp)) {
> - err = PTR_ERR(gpiod_nwp);
> + priv->gpiod_nwp = devm_gpiod_get(&pdev->dev, "nwp", GPIOD_OUT_HIGH);
> + if (IS_ERR(priv->gpiod_nwp)) {
> + err = PTR_ERR(priv->gpiod_nwp);
> dev_err(&pdev->dev, "NWP GPIO request failed (%d)\n", err);
> goto out_mtd;
> }
>
> - gpiod_nce = devm_gpiod_get(&pdev->dev, "nce", GPIOD_OUT_HIGH);
> - if (IS_ERR(gpiod_nce)) {
> - err = PTR_ERR(gpiod_nce);
> + priv->gpiod_nce = devm_gpiod_get(&pdev->dev, "nce", GPIOD_OUT_HIGH);
> + if (IS_ERR(priv->gpiod_nce)) {
> + err = PTR_ERR(priv->gpiod_nce);
> dev_err(&pdev->dev, "NCE GPIO request failed (%d)\n", err);
> goto out_mtd;
> }
>
> - gpiod_nre = devm_gpiod_get(&pdev->dev, "nre", GPIOD_OUT_HIGH);
> - if (IS_ERR(gpiod_nre)) {
> - err = PTR_ERR(gpiod_nre);
> + priv->gpiod_nre = devm_gpiod_get(&pdev->dev, "nre", GPIOD_OUT_HIGH);
> + if (IS_ERR(priv->gpiod_nre)) {
> + err = PTR_ERR(priv->gpiod_nre);
> dev_err(&pdev->dev, "NRE GPIO request failed (%d)\n", err);
> goto out_mtd;
> }
>
> - gpiod_nwe = devm_gpiod_get(&pdev->dev, "nwe", GPIOD_OUT_HIGH);
> - if (IS_ERR(gpiod_nwe)) {
> - err = PTR_ERR(gpiod_nwe);
> + priv->gpiod_nwe = devm_gpiod_get(&pdev->dev, "nwe", GPIOD_OUT_HIGH);
> + if (IS_ERR(priv->gpiod_nwe)) {
> + err = PTR_ERR(priv->gpiod_nwe);
> dev_err(&pdev->dev, "NWE GPIO request failed (%d)\n", err);
> goto out_mtd;
> }
>
> - gpiod_ale = devm_gpiod_get(&pdev->dev, "ale", GPIOD_OUT_LOW);
> - if (IS_ERR(gpiod_ale)) {
> - err = PTR_ERR(gpiod_ale);
> + priv->gpiod_ale = devm_gpiod_get(&pdev->dev, "ale", GPIOD_OUT_LOW);
> + if (IS_ERR(priv->gpiod_ale)) {
> + err = PTR_ERR(priv->gpiod_ale);
> dev_err(&pdev->dev, "ALE GPIO request failed (%d)\n", err);
> goto out_mtd;
> }
>
> - gpiod_cle = devm_gpiod_get(&pdev->dev, "cle", GPIOD_OUT_LOW);
> - if (IS_ERR(gpiod_cle)) {
> - err = PTR_ERR(gpiod_cle);
> + priv->gpiod_cle = devm_gpiod_get(&pdev->dev, "cle", GPIOD_OUT_LOW);
> + if (IS_ERR(priv->gpiod_cle)) {
> + err = PTR_ERR(priv->gpiod_cle);
> dev_err(&pdev->dev, "CLE GPIO request failed (%d)\n", err);
> goto out_mtd;
> }
>
> /* Scan to find existence of the device */
> - err = nand_scan(ams_delta_mtd, 1);
> + err = nand_scan(mtd, 1);
> if (err)
> goto out_mtd;
>
> /* Register the partitions */
> - mtd_device_register(ams_delta_mtd, partition_info,
> - ARRAY_SIZE(partition_info));
> + mtd_device_register(mtd, partition_info, ARRAY_SIZE(partition_info));
>
> goto out;
>
> out_mtd:
> iounmap(io_base);
> out_free:
> - kfree(this);
> out:
> return err;
> }
> @@ -271,16 +284,15 @@ static int ams_delta_init(struct platform_device *pdev)
> */
> static int ams_delta_cleanup(struct platform_device *pdev)
> {
> - void __iomem *io_base = platform_get_drvdata(pdev);
> + struct ams_delta_nand *priv = platform_get_drvdata(pdev);
> + struct mtd_info *mtd = nand_to_mtd(&priv->nand_chip);
> + void __iomem *io_base = priv->io_base;
>
> /* Release resources, unregister device */
> - nand_release(ams_delta_mtd);
> + nand_release(mtd);
>
> iounmap(io_base);
>
> - /* Free the MTD device structure */
> - kfree(mtd_to_nand(ams_delta_mtd));
> -
> return 0;
> }
>


2018-08-07 17:06:41

by Boris Brezillon

[permalink] [raw]
Subject: Re: [RFC PATCH v2 08/12] mtd: rawnand: ams-delta: Simplify pointer resolution on read/write

On Tue, 7 Aug 2018 00:29:14 +0200
Janusz Krzysztofik <[email protected]> wrote:

> Simplify data read/write sub-functions by changing their APIs so they
> accept driver private structure pointer instead of mtd_info.
>
> Signed-off-by: Janusz Krzysztofik <[email protected]>

Reviewed-by: Boris Brezillon <[email protected]>

Can you move that one earlier in the series so that it can be applied
even if we're still discussing the GPIO bitmap changes?

> ---
> drivers/mtd/nand/raw/ams-delta.c | 40 ++++++++++++++++++++++------------------
> 1 file changed, 22 insertions(+), 18 deletions(-)
>
> diff --git a/drivers/mtd/nand/raw/ams-delta.c b/drivers/mtd/nand/raw/ams-delta.c
> index d02c48c013e8..30c461138195 100644
> --- a/drivers/mtd/nand/raw/ams-delta.c
> +++ b/drivers/mtd/nand/raw/ams-delta.c
> @@ -76,10 +76,8 @@ static void ams_delta_write_commit(struct ams_delta_nand *priv)
> gpiod_set_value(priv->gpiod_nwe, 1);
> }
>
> -static void ams_delta_write_next_byte(struct mtd_info *mtd, u_char byte)
> +static void ams_delta_write_next_byte(struct ams_delta_nand *priv, u_char byte)
> {
> - struct nand_chip *this = mtd_to_nand(mtd);
> - struct ams_delta_nand *priv = nand_get_controller_data(this);
> struct gpio_descs *data_gpiods = priv->data_gpiods;
> unsigned long bits = byte;
> int i, value_array[data_gpiods->ndescs];
> @@ -93,10 +91,8 @@ static void ams_delta_write_next_byte(struct mtd_info *mtd, u_char byte)
> ams_delta_write_commit(priv);
> }
>
> -static void ams_delta_write_byte(struct mtd_info *mtd, u_char byte)
> +static void ams_delta_write_first_byte(struct ams_delta_nand *priv, u_char byte)
> {
> - struct nand_chip *this = mtd_to_nand(mtd);
> - struct ams_delta_nand *priv = nand_get_controller_data(this);
> struct gpio_descs *data_gpiods = priv->data_gpiods;
> unsigned long bits = byte;
> int i;
> @@ -108,10 +104,8 @@ static void ams_delta_write_byte(struct mtd_info *mtd, u_char byte)
> ams_delta_write_commit(priv);
> }
>
> -static u_char ams_delta_read_next_byte(struct mtd_info *mtd)
> +static u_char ams_delta_read_next_byte(struct ams_delta_nand *priv)
> {
> - struct nand_chip *this = mtd_to_nand(mtd);
> - struct ams_delta_nand *priv = nand_get_controller_data(this);
> struct gpio_descs *data_gpiods = priv->data_gpiods;
> unsigned long bits = 0;
> int i, value_array[data_gpiods->ndescs];
> @@ -131,38 +125,48 @@ static u_char ams_delta_read_next_byte(struct mtd_info *mtd)
> return bits;
> }
>
> -static u_char ams_delta_read_byte(struct mtd_info *mtd)
> +static u_char ams_delta_read_first_byte(struct ams_delta_nand *priv)
> {
> - struct nand_chip *this = mtd_to_nand(mtd);
> - struct ams_delta_nand *priv = nand_get_controller_data(this);
> struct gpio_descs *data_gpiods = priv->data_gpiods;
> int i;
>
> for (i = 0; i < data_gpiods->ndescs; i++)
> gpiod_direction_input(data_gpiods->desc[i]);
>
> - return ams_delta_read_next_byte(mtd);
> + return ams_delta_read_next_byte(priv);
> +}
> +
> +static u_char ams_delta_read_byte(struct mtd_info *mtd)
> +{
> + struct nand_chip *this = mtd_to_nand(mtd);
> + struct ams_delta_nand *priv = nand_get_controller_data(this);
> +
> + return ams_delta_read_first_byte(priv);
> }
>
> static void ams_delta_write_buf(struct mtd_info *mtd, const u_char *buf,
> int len)
> {
> + struct nand_chip *this = mtd_to_nand(mtd);
> + struct ams_delta_nand *priv = nand_get_controller_data(this);
> int i;
>
> if (len > 0)
> - ams_delta_write_byte(mtd, buf[0]);
> + ams_delta_write_first_byte(priv, buf[0]);
> for (i = 1; i < len; i++)
> - ams_delta_write_next_byte(mtd, buf[i]);
> + ams_delta_write_next_byte(priv, buf[i]);
> }
>
> static void ams_delta_read_buf(struct mtd_info *mtd, u_char *buf, int len)
> {
> + struct nand_chip *this = mtd_to_nand(mtd);
> + struct ams_delta_nand *priv = nand_get_controller_data(this);
> int i;
>
> if (len > 0)
> - buf[0] = ams_delta_read_byte(mtd);
> + buf[0] = ams_delta_read_first_byte(priv);
> for (i = 1; i < len; i++)
> - buf[i] = ams_delta_read_next_byte(mtd);
> + buf[i] = ams_delta_read_next_byte(priv);
> }
>
> /*
> @@ -186,7 +190,7 @@ static void ams_delta_hwcontrol(struct mtd_info *mtd, int cmd,
> }
>
> if (cmd != NAND_CMD_NONE)
> - ams_delta_write_byte(mtd, cmd);
> + ams_delta_write_first_byte(priv, cmd);
> }
>
> static int ams_delta_nand_ready(struct mtd_info *mtd)


2018-08-07 17:08:40

by Boris Brezillon

[permalink] [raw]
Subject: Re: [RFC PATCH v2 05/12] mtd: rawnand: ams-delta: use GPIO API for data read/write

On Tue, 7 Aug 2018 00:29:11 +0200
Janusz Krzysztofik <[email protected]> wrote:

> Don't readw()/writew() data directly from/to GPIO port which is under
> control of gpio-omap driver, use GPIO API instead.
>
> Degrade of performance on Amstrad Delta is completely not acceptable.

Can we have numbers along with information about where the overhead is
when using gpiod_{get,set}_raw_array_value()?

>
> The driver should work with any 8+-bit bidirectional GPIO port, not
> only OMAP.

That's cool!

2018-08-07 17:12:15

by Janusz Krzysztofik

[permalink] [raw]
Subject: Re: [RFC PATCH v2 05/12] mtd: rawnand: ams-delta: use GPIO API for data read/write

Hi Boris,

On Tuesday, August 7, 2018 7:06:27 PM CEST Boris Brezillon wrote:
> On Tue, 7 Aug 2018 00:29:11 +0200
> Janusz Krzysztofik <[email protected]> wrote:
>
> > Don't readw()/writew() data directly from/to GPIO port which is under
> > control of gpio-omap driver, use GPIO API instead.
> >
> > Degrade of performance on Amstrad Delta is completely not acceptable.
>
> Can we have numbers along with information about where the overhead is
> when using gpiod_{get,set}_raw_array_value()?

Yes, as soon as I get physical access to the device, probably this or next
weekend.

Thanks,
Janusz



2018-08-07 17:13:27

by Boris Brezillon

[permalink] [raw]
Subject: Re: [RFC PATCH v2 09/12] gpiolib: Identify GPIO descriptor arrays with direct mapping

On Tue, 07 Aug 2018 18:50:22 +0200
Janusz Krzysztofik <[email protected]> wrote:

> Hi Linus,
>
> On Tuesday, August 7, 2018 1:29:43 AM CEST Linus Walleij wrote:
> > Hi Janusz!
> >
> > On Tue, Aug 7, 2018 at 12:29 AM Janusz Krzysztofik <[email protected]>
> wrote:
> >
> > > Certain GPIO array lookup results may map directly to GPIO pins of a
> > > single GPIO chip in hardware order. If that condition is recognized
> > > and handled efficiently, significant performance gain of get/set array
> > > functions may be possible.
> > >
> > > While processing a request for an array of GPIO descriptors, verify if
> > > the descriptors just collected represent consecutive pins of a single
> > > GPIO chip. Pass that information with the array to the caller so it
> > > can benefit from enhanced performance as soon as bitmap based get/set
> > > array functions which can make efficient use of that are available.
> > >
> > > Signed-off-by: Janusz Krzysztofik <[email protected]>
> > (...)
> > > This function returns a struct gpio_descs which contains an array of
> > > -descriptors::
> > > +descriptors. It may also contain a valid descriptor of a single GPIO
> chip in
> > > +case the array strictly matches pin hardware layout of the chip::
> > >
> > > struct gpio_descs {
> > > unsigned int ndescs;
> > > struct gpio_desc *desc[];
> > > + struct gpio_chip *chip;
> >
> > This must be motivated: if the only purpose is to indicate to the consumer
> that
> > all GPIOs are on the same chip, why not just have a
> >
> > bool all_on_same_chip;
> >
> > That you set to true if these are all on the same chip?
>
> My approach would probably save one or two instructions per get/set call, but
> I'm not stuck to it and will be happy to find a better solution.
>
> How about folding the chip descriptor inside an additional structure, private
> to drivers, with internals not revealed to consumers?

Or just get the chip from gpio_descs->desc[0]->gdev->chip when
->all_on_same_chip is true...

That adds 2 dereferencing though.

2018-08-07 17:16:11

by Janusz Krzysztofik

[permalink] [raw]
Subject: Re: [RFC PATCH v2 08/12] mtd: rawnand: ams-delta: Simplify pointer resolution on read/write

On Tuesday, August 7, 2018 7:02:56 PM CEST Boris Brezillon wrote:
> On Tue, 7 Aug 2018 00:29:14 +0200
> Janusz Krzysztofik <[email protected]> wrote:
>
> > Simplify data read/write sub-functions by changing their APIs so they
> > accept driver private structure pointer instead of mtd_info.
> >
> > Signed-off-by: Janusz Krzysztofik <[email protected]>
>
> Reviewed-by: Boris Brezillon <[email protected]>
>
> Can you move that one earlier in the series so that it can be applied
> even if we're still discussing the GPIO bitmap changes?

Sure, I will, and I would be still more happy if you agreed on me doing the
same with [RFC PATCH v2 07/12] mtd: rawnand: ams-delta: Set port direction
once per transfer.

Thanks,
Janusz



2018-08-07 17:57:18

by Boris Brezillon

[permalink] [raw]
Subject: Re: [RFC PATCH v2 01/12] mtd: rawnand: ams-delta: Assign mtd->dev.parent, not mtd->owner

On Tue, 7 Aug 2018 00:29:07 +0200
Janusz Krzysztofik <[email protected]> wrote:

> Fix missing mtd->dev.parent assignment and drop useless mtd->owner.
>
> Signed-off-by: Janusz Krzysztofik <[email protected]>

Reviewed-by: Boris Brezillon <[email protected]>

> ---
> drivers/mtd/nand/raw/ams-delta.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/drivers/mtd/nand/raw/ams-delta.c b/drivers/mtd/nand/raw/ams-delta.c
> index 2a8872ebd14a..af313c620264 100644
> --- a/drivers/mtd/nand/raw/ams-delta.c
> +++ b/drivers/mtd/nand/raw/ams-delta.c
> @@ -162,7 +162,7 @@ static int ams_delta_init(struct platform_device *pdev)
> }
>
> ams_delta_mtd = nand_to_mtd(this);
> - ams_delta_mtd->owner = THIS_MODULE;
> + ams_delta_mtd->dev.parent = &pdev->dev;
>
> /*
> * Don't try to request the memory region from here,


2018-08-07 18:07:48

by Boris Brezillon

[permalink] [raw]
Subject: Re: [RFC PATCH v2 09/12] gpiolib: Identify GPIO descriptor arrays with direct mapping

On Tue, 7 Aug 2018 00:29:15 +0200
Janusz Krzysztofik <[email protected]> wrote:

> Certain GPIO array lookup results may map directly to GPIO pins of a
> single GPIO chip in hardware order. If that condition is recognized
> and handled efficiently, significant performance gain of get/set array
> functions may be possible.
>
> While processing a request for an array of GPIO descriptors, verify if
> the descriptors just collected represent consecutive pins of a single
> GPIO chip. Pass that information with the array to the caller so it
> can benefit from enhanced performance as soon as bitmap based get/set
> array functions which can make efficient use of that are available.
>
> Signed-off-by: Janusz Krzysztofik <[email protected]>
> ---
> Documentation/driver-api/gpio/consumer.rst | 4 +++-
> drivers/gpio/gpiolib.c | 14 ++++++++++++++
> include/linux/gpio/consumer.h | 1 +
> 3 files changed, 18 insertions(+), 1 deletion(-)
>
> diff --git a/Documentation/driver-api/gpio/consumer.rst b/Documentation/driver-api/gpio/consumer.rst
> index aa03f389d41d..38a990b5f3b6 100644
> --- a/Documentation/driver-api/gpio/consumer.rst
> +++ b/Documentation/driver-api/gpio/consumer.rst
> @@ -109,11 +109,13 @@ For a function using multiple GPIOs all of those can be obtained with one call::
> enum gpiod_flags flags)
>
> This function returns a struct gpio_descs which contains an array of
> -descriptors::
> +descriptors. It may also contain a valid descriptor of a single GPIO chip in
> +case the array strictly matches pin hardware layout of the chip::
>
> struct gpio_descs {
> unsigned int ndescs;
> struct gpio_desc *desc[];
> + struct gpio_chip *chip;

chip is placed at the beginning of the struct in the real code, which
is expected since putting it at the end won't work because of the
desc[] declaration.

...

> diff --git a/include/linux/gpio/consumer.h b/include/linux/gpio/consumer.h
> index 21ddbe440030..862ee027a02f 100644
> --- a/include/linux/gpio/consumer.h
> +++ b/include/linux/gpio/consumer.h
> @@ -22,6 +22,7 @@ struct gpio_desc;
> * gpiod_get_array().
> */
> struct gpio_descs {
> + struct gpio_chip *chip;
> unsigned int ndescs;
> struct gpio_desc *desc[];
> };


2018-08-07 18:07:58

by Janusz Krzysztofik

[permalink] [raw]
Subject: Re: [RFC PATCH v2 09/12] gpiolib: Identify GPIO descriptor arrays with direct mapping

On Tuesday, August 7, 2018 7:14:20 PM CEST Boris Brezillon wrote:
> On Tue, 7 Aug 2018 00:29:15 +0200
> Janusz Krzysztofik <[email protected]> wrote:
>
> > Certain GPIO array lookup results may map directly to GPIO pins of a
> > single GPIO chip in hardware order. If that condition is recognized
> > and handled efficiently, significant performance gain of get/set array
> > functions may be possible.
> >
> > While processing a request for an array of GPIO descriptors, verify if
> > the descriptors just collected represent consecutive pins of a single
> > GPIO chip. Pass that information with the array to the caller so it
> > can benefit from enhanced performance as soon as bitmap based get/set
> > array functions which can make efficient use of that are available.
> >
> > Signed-off-by: Janusz Krzysztofik <[email protected]>
> > ---
> > Documentation/driver-api/gpio/consumer.rst | 4 +++-
> > drivers/gpio/gpiolib.c | 14 ++++++++++++++
> > include/linux/gpio/consumer.h | 1 +
> > 3 files changed, 18 insertions(+), 1 deletion(-)
> >
> > diff --git a/Documentation/driver-api/gpio/consumer.rst b/Documentation/driver-api/gpio/consumer.rst
> > index aa03f389d41d..38a990b5f3b6 100644
> > --- a/Documentation/driver-api/gpio/consumer.rst
> > +++ b/Documentation/driver-api/gpio/consumer.rst
> > @@ -109,11 +109,13 @@ For a function using multiple GPIOs all of those can be obtained with one call::
> > enum gpiod_flags flags)
> >
> > This function returns a struct gpio_descs which contains an array of
> > -descriptors::
> > +descriptors. It may also contain a valid descriptor of a single GPIO chip in
> > +case the array strictly matches pin hardware layout of the chip::
> >
> > struct gpio_descs {
> > unsigned int ndescs;
> > struct gpio_desc *desc[];
> > + struct gpio_chip *chip;
>
> chip is placed at the beginning of the struct in the real code, which
> is expected since putting it at the end won't work because of the
> desc[] declaration.

Yes, I've already noticed that and will fix on next iteration, thanks.

Janusz



2018-08-07 18:08:57

by Janusz Krzysztofik

[permalink] [raw]
Subject: Re: [RFC PATCH v2 12/12] gpiolib: Add fast processing path to bitmap API functions

On Tuesday, August 7, 2018 1:43:56 AM CEST Linus Walleij wrote:
> On Tue, Aug 7, 2018 at 12:29 AM Janusz Krzysztofik <[email protected]>
wrote:
>
> Hi Janusz!
>
> > Certain GPIO descriptor arrays returned by gpio_get_array() may contain
> > information on a single GPIO chip driving array member pins in hardware
> > order. In such cases, bitmaps of values can be passed directly to the
> > chip callback functions without wasting time on iterations.
> >
> > Add respective code to gpiod_get/set_array_bitmap_complex() functions.
> >
> > Signed-off-by: Janusz Krzysztofik <[email protected]>
>
> I think it would be disappointing to leave all the users of the old
> array API without the performance improvement. I think we need to
> deal with this in a way such that everyone can benefit from it.

There are a few issues to be resolved:

1) array size limited by bitmap size:
- are we ready to limit array size to a single bitmap for all users?
- if not, how can we pass a bitmap of an arbitrary size?
- if as an array of bitmaps, is that still clear enough and easy to use?
- other ideas?

2) arbitrary array support:
- are we ready to drop that?
- if not, do we agree to require all users to pack their arbitrary arrays
inside the gpio_descs structure?

Maybe more.

> Also it is kludgy if users (consumers) would need to handle the case
> where all lines are on the same chip separately, through the bitmap
> API.

Not true as long as array size fits (arbitrary arrays can be packed by users),
but I see your point.

> What we need is an API that:
>
> - All drivers handling arrays can use (including current users).
>
> - Enables speed-up if the lines are all on the same chip/register.
>
> - Doesn't require consumers to know if they are all on the same
> chip or not.
>
> This means a deep API with a small surface.
>
> How do we achieve this the best way?

I think widely accepted solutions to those two issues I've mentioned above can
give the answer.

Thanks,
Janusz



2018-08-07 18:13:19

by Boris Brezillon

[permalink] [raw]
Subject: Re: [RFC PATCH v2 12/12] gpiolib: Add fast processing path to bitmap API functions

Hi Janusz,

On Tue, 07 Aug 2018 19:29:53 +0200
Janusz Krzysztofik <[email protected]> wrote:

> On Tuesday, August 7, 2018 1:43:56 AM CEST Linus Walleij wrote:
> > On Tue, Aug 7, 2018 at 12:29 AM Janusz Krzysztofik <[email protected]>
> wrote:
> >
> > Hi Janusz!
> >
> > > Certain GPIO descriptor arrays returned by gpio_get_array() may contain
> > > information on a single GPIO chip driving array member pins in hardware
> > > order. In such cases, bitmaps of values can be passed directly to the
> > > chip callback functions without wasting time on iterations.
> > >
> > > Add respective code to gpiod_get/set_array_bitmap_complex() functions.
> > >
> > > Signed-off-by: Janusz Krzysztofik <[email protected]>
> >
> > I think it would be disappointing to leave all the users of the old
> > array API without the performance improvement. I think we need to
> > deal with this in a way such that everyone can benefit from it.

I agree with Linus on that one. When I initially proposed the gpio
bitbanging API I had something more advanced in mind where the GPIO
framework would be responsible for toggling the GPIOs on its own when
it's given an array of bytes to transmit (this way you avoid going
back and forth between the GPIO user and the GPIO framework). But this
approach would clearly be more invasive than what you propose
here (turning the int array into a bitmap and optimizing). So, if we go
for the "int array -> bitmap" approach I think all users should be
converted so that we end up with a single API.

>
> There are a few issues to be resolved:
>
> 1) array size limited by bitmap size:
> - are we ready to limit array size to a single bitmap for all users?
> - if not, how can we pass a bitmap of an arbitrary size?
> - if as an array of bitmaps, is that still clear enough and easy to use?
> - other ideas?

What we call a bitmap is an array of unsigned longs each entry
containing NBITS_PER_LONG bits, so yes, it's an arbitrary size (see the
bitmap API here [1]).

>
> 2) arbitrary array support:
> - are we ready to drop that?
> - if not, do we agree to require all users to pack their arbitrary arrays
> inside the gpio_descs structure?

I could only find one user, and it's the core itself (for the ioctl),
so that shouldn't be too hard to convert all users. Did you find more.

>
> Maybe more.
>
> > Also it is kludgy if users (consumers) would need to handle the case
> > where all lines are on the same chip separately, through the bitmap
> > API.
>
> Not true as long as array size fits (arbitrary arrays can be packed by users),
> but I see your point.

I think the API should be the same and the framework should decide to
take the fast path if all gpios belong to the same chip (which AFAICT
is already the case, except it's putting the result in an int array
instead of a bitmap)

>
> > What we need is an API that:
> >
> > - All drivers handling arrays can use (including current users).
> >
> > - Enables speed-up if the lines are all on the same chip/register.
> >
> > - Doesn't require consumers to know if they are all on the same
> > chip or not.
> >
> > This means a deep API with a small surface.
> >
> > How do we achieve this the best way?
>
> I think widely accepted solutions to those two issues I've mentioned above can
> give the answer.

I'd still like to see how far we are from the initial perfs (the one
poking the GPIO regs directly) with this approach, and what's the
improvement compared to the int array solution we already have in place.

Regards,

Boris

[1]https://elixir.bootlin.com/linux/v4.18-rc8/source/include/linux/bitmap.h

2018-08-07 19:05:00

by Boris Brezillon

[permalink] [raw]
Subject: Re: [RFC PATCH v2 07/12] mtd: rawnand: ams-delta: Set port direction once per transfer

On Tue, 7 Aug 2018 00:29:13 +0200
Janusz Krzysztofik <[email protected]> wrote:

> In its current shape, the driver sets data port direction before each
> byte read/write operation, even during multi-byte transfers. Since
> performance of the driver is completely not acceptable on Amstrad Delta
> after it has been converted to GPIO bitbang, try to improve things a
> bit by setting the port direction only on first byte of each transfer.
>
> Resulting performance on Amstrad Delta is still far from acceptable.
>
> Signed-off-by: Janusz Krzysztofik <[email protected]>
> ---
> drivers/mtd/nand/raw/ams-delta.c | 58 ++++++++++++++++++++++++++++++++--------
> 1 file changed, 47 insertions(+), 11 deletions(-)
>
> diff --git a/drivers/mtd/nand/raw/ams-delta.c b/drivers/mtd/nand/raw/ams-delta.c
> index 78996ddf82e0..d02c48c013e8 100644
> --- a/drivers/mtd/nand/raw/ams-delta.c
> +++ b/drivers/mtd/nand/raw/ams-delta.c
> @@ -69,6 +69,30 @@ static const struct mtd_partition partition_info[] = {
> .size = 3 * SZ_256K },
> };
>
> +static void ams_delta_write_commit(struct ams_delta_nand *priv)
> +{
> + gpiod_set_value(priv->gpiod_nwe, 0);
> + ndelay(40);
> + gpiod_set_value(priv->gpiod_nwe, 1);
> +}
> +
> +static void ams_delta_write_next_byte(struct mtd_info *mtd, u_char byte)
> +{
> + struct nand_chip *this = mtd_to_nand(mtd);
> + struct ams_delta_nand *priv = nand_get_controller_data(this);
> + struct gpio_descs *data_gpiods = priv->data_gpiods;
> + unsigned long bits = byte;
> + int i, value_array[data_gpiods->ndescs];
> +
> + for (i = 0; i < data_gpiods->ndescs; i++)
> + value_array[i] = test_bit(i, &bits);
> +
> + gpiod_set_raw_array_value(data_gpiods->ndescs, data_gpiods->desc,
> + value_array);
> +
> + ams_delta_write_commit(priv);
> +}
> +
> static void ams_delta_write_byte(struct mtd_info *mtd, u_char byte)
> {
> struct nand_chip *this = mtd_to_nand(mtd);
> @@ -81,12 +105,10 @@ static void ams_delta_write_byte(struct mtd_info *mtd, u_char byte)
> gpiod_direction_output_raw(data_gpiods->desc[i],
> test_bit(i, &bits));
>
> - gpiod_set_value(priv->gpiod_nwe, 0);
> - ndelay(40);
> - gpiod_set_value(priv->gpiod_nwe, 1);
> + ams_delta_write_commit(priv);
> }
>
> -static u_char ams_delta_read_byte(struct mtd_info *mtd)
> +static u_char ams_delta_read_next_byte(struct mtd_info *mtd)
> {
> struct nand_chip *this = mtd_to_nand(mtd);
> struct ams_delta_nand *priv = nand_get_controller_data(this);
> @@ -94,9 +116,6 @@ static u_char ams_delta_read_byte(struct mtd_info *mtd)
> unsigned long bits = 0;
> int i, value_array[data_gpiods->ndescs];
>
> - for (i = 0; i < data_gpiods->ndescs; i++)
> - gpiod_direction_input(data_gpiods->desc[i]);
> -
> gpiod_set_value(priv->gpiod_nre, 0);
> ndelay(40);
>
> @@ -112,21 +131,38 @@ static u_char ams_delta_read_byte(struct mtd_info *mtd)
> return bits;
> }
>
> +static u_char ams_delta_read_byte(struct mtd_info *mtd)
> +{
> + struct nand_chip *this = mtd_to_nand(mtd);
> + struct ams_delta_nand *priv = nand_get_controller_data(this);
> + struct gpio_descs *data_gpiods = priv->data_gpiods;
> + int i;
> +
> + for (i = 0; i < data_gpiods->ndescs; i++)
> + gpiod_direction_input(data_gpiods->desc[i]);
> +
> + return ams_delta_read_next_byte(mtd);
> +}
> +
> static void ams_delta_write_buf(struct mtd_info *mtd, const u_char *buf,
> int len)
> {
> int i;
>
> - for (i=0; i<len; i++)
> - ams_delta_write_byte(mtd, buf[i]);
> + if (len > 0)
> + ams_delta_write_byte(mtd, buf[0]);
> + for (i = 1; i < len; i++)
> + ams_delta_write_next_byte(mtd, buf[i]);
> }
>
> static void ams_delta_read_buf(struct mtd_info *mtd, u_char *buf, int len)
> {
> int i;
>
> - for (i=0; i<len; i++)
> - buf[i] = ams_delta_read_byte(mtd);
> + if (len > 0)
> + buf[0] = ams_delta_read_byte(mtd);
> + for (i = 1; i < len; i++)
> + buf[i] = ams_delta_read_next_byte(mtd);
> }

I'd suggest a slightly different approach where the data pins
direction state is stored in the the priv struct and only changed when
required. This way you just have to add a test in
ams_delta_read/write_byte().

2018-08-07 19:20:18

by Janusz Krzysztofik

[permalink] [raw]
Subject: Re: [RFC PATCH v2 09/12] gpiolib: Identify GPIO descriptor arrays with direct mapping

Hi Linus,

On Tuesday, August 7, 2018 1:29:43 AM CEST Linus Walleij wrote:
> Hi Janusz!
>
> On Tue, Aug 7, 2018 at 12:29 AM Janusz Krzysztofik <[email protected]>
wrote:
>
> > Certain GPIO array lookup results may map directly to GPIO pins of a
> > single GPIO chip in hardware order. If that condition is recognized
> > and handled efficiently, significant performance gain of get/set array
> > functions may be possible.
> >
> > While processing a request for an array of GPIO descriptors, verify if
> > the descriptors just collected represent consecutive pins of a single
> > GPIO chip. Pass that information with the array to the caller so it
> > can benefit from enhanced performance as soon as bitmap based get/set
> > array functions which can make efficient use of that are available.
> >
> > Signed-off-by: Janusz Krzysztofik <[email protected]>
> (...)
> > This function returns a struct gpio_descs which contains an array of
> > -descriptors::
> > +descriptors. It may also contain a valid descriptor of a single GPIO
chip in
> > +case the array strictly matches pin hardware layout of the chip::
> >
> > struct gpio_descs {
> > unsigned int ndescs;
> > struct gpio_desc *desc[];
> > + struct gpio_chip *chip;
>
> This must be motivated: if the only purpose is to indicate to the consumer
that
> all GPIOs are on the same chip, why not just have a
>
> bool all_on_same_chip;
>
> That you set to true if these are all on the same chip?

My approach would probably save one or two instructions per get/set call, but
I'm not stuck to it and will be happy to find a better solution.

How about folding the chip descriptor inside an additional structure, private
to drivers, with internals not revealed to consumers?

Thanks,
Janusz



2018-08-07 21:55:21

by Janusz Krzysztofik

[permalink] [raw]
Subject: Re: [RFC PATCH v2 01/12] mtd: rawnand: ams-delta: Assign mtd->dev.parent, not mtd->owner

Hi Marek,

On Tuesday, August 7, 2018 1:54:10 AM CEST Marek Vasut wrote:
> On 08/07/2018 12:29 AM, Janusz Krzysztofik wrote:
> > Fix missing mtd->dev.parent assignment and drop useless mtd->owner.
>
> You fail to explain why this fix is required.

OK, I'll have a look at similar patches from the past and add an explanation.

Thanks,
Janusz



2018-08-08 16:57:05

by Janusz Krzysztofik

[permalink] [raw]
Subject: Re: [RFC PATCH v2 07/12] mtd: rawnand: ams-delta: Set port direction once per transfer

Hi Boris,

On Tuesday, August 7, 2018 8:57:52 PM CEST Boris Brezillon wrote:
> On Tue, 7 Aug 2018 00:29:13 +0200
> Janusz Krzysztofik <[email protected]> wrote:
>
> > In its current shape, the driver sets data port direction before each
> > byte read/write operation, even during multi-byte transfers. Since
> > performance of the driver is completely not acceptable on Amstrad Delta
> > after it has been converted to GPIO bitbang, try to improve things a
> > bit by setting the port direction only on first byte of each transfer.
> ...
> I'd suggest a slightly different approach where the data pins
> direction state is stored in the the priv struct and only changed when
> required. This way you just have to add a test in
> ams_delta_read/write_byte().

Good idea, I'm going to use it, thanks.

Once done, may I also move that one earlier in the series so that it can be
applied while our discussion on GPIO bitmap changes still continues?

Thanks,
Janusz





2018-08-08 17:43:53

by Miquel Raynal

[permalink] [raw]
Subject: Re: [RFC PATCH v2 07/12] mtd: rawnand: ams-delta: Set port direction once per transfer

Hi Janusz,

Janusz Krzysztofik <[email protected]> wrote on Wed, 08 Aug 2018
18:55:35 +0200:

> Hi Boris,
>
> On Tuesday, August 7, 2018 8:57:52 PM CEST Boris Brezillon wrote:
> > On Tue, 7 Aug 2018 00:29:13 +0200
> > Janusz Krzysztofik <[email protected]> wrote:
> >
> > > In its current shape, the driver sets data port direction before each
> > > byte read/write operation, even during multi-byte transfers. Since
> > > performance of the driver is completely not acceptable on Amstrad Delta
> > > after it has been converted to GPIO bitbang, try to improve things a
> > > bit by setting the port direction only on first byte of each transfer.
> > ...
> > I'd suggest a slightly different approach where the data pins
> > direction state is stored in the the priv struct and only changed when
> > required. This way you just have to add a test in
> > ams_delta_read/write_byte().
>
> Good idea, I'm going to use it, thanks.
>
> Once done, may I also move that one earlier in the series so that it can be
> applied while our discussion on GPIO bitmap changes still continues?

I think I may answer on his behalf: yes! You can move the GPIO bitmap
changes at the end of the series, checking that you never break the
bisectability. Then I could apply the major changes and let us iterate
on the GPIO bitmap stuff only.

Thanks,
Miquèl

2018-08-10 10:12:59

by Linus Walleij

[permalink] [raw]
Subject: Re: [RFC PATCH v2 04/12] mtd: rawnand: ams-delta: request data port GPIO resource

On Tue, Aug 7, 2018 at 12:29 AM Janusz Krzysztofik <[email protected]> wrote:

> Data port used by the driver is actually an OMAP MPUIO device, already
> under control of gpio-omap driver. For that reason we used to not
> request the memory region of the port as that would fail because the
> region is already busy. Despite that, we are still accessing the port
> by just ioremapping it and performing read/write operations. Moreover,
> we are doing that without any proteciton from other users legally
> manipulating the port pins over GPIO API.
>
> The plan is to convert the driver to access the port over functions
> exposed by the gpio-omap driver. Before that happens, already prevent
> from other users accessing the port pins by requesting an array of its
> GPIO descriptors.
>
> Signed-off-by: Janusz Krzysztofik <[email protected]>

Reviewed-by: Linus Walleij <[email protected]>

Yours,
Linus Walleij

2018-08-10 10:27:53

by Linus Walleij

[permalink] [raw]
Subject: Re: [RFC PATCH v2 03/12] ARM: OMAP1: ams-delta: Provide GPIO lookup table for NAND data port

On Tue, Aug 7, 2018 at 12:29 AM Janusz Krzysztofik <[email protected]> wrote:

> Data port used by Amstrad Delta NAND driver is actually an OMAP MPUIO
> device, already under control of gpio-omap driver. The NAND driver
> gets access to the port by ioremapping it and performs read/write
> operations. That is done without any proteciton from other users
> legally manipulating the port pins over GPIO API.
>
> The plan is to convert the driver to access the port over GPIO consumer
> API. Before that is implemented, the driver can already obtain
> exclusive access to the port by requesting an array of its GPIO
> descriptors.
>
> Add respective entries to the NAND GPIO lookup table.
>
> Signed-off-by: Janusz Krzysztofik <[email protected]>

Reviewed-by: Linus Walleij <[email protected]>

Yours,
Linus Walleij

2018-08-10 10:44:13

by Linus Walleij

[permalink] [raw]
Subject: Re: [RFC PATCH v2 05/12] mtd: rawnand: ams-delta: use GPIO API for data read/write

On Tue, Aug 7, 2018 at 12:29 AM Janusz Krzysztofik <[email protected]> wrote:

> Don't readw()/writew() data directly from/to GPIO port which is under
> control of gpio-omap driver, use GPIO API instead.
>
> Degrade of performance on Amstrad Delta is completely not acceptable.
>
> The driver should work with any 8+-bit bidirectional GPIO port, not
> only OMAP.
>
> Signed-off-by: Janusz Krzysztofik <[email protected]>

Reviewed-by: Linus Walleij <[email protected]>

Yours,
Linus Walleij

2018-08-10 11:02:34

by Linus Walleij

[permalink] [raw]
Subject: Re: [RFC PATCH v2 12/12] gpiolib: Add fast processing path to bitmap API functions

On Tue, Aug 7, 2018 at 7:47 PM Boris Brezillon
<[email protected]> wrote:
> Janusz Krzysztofik <[email protected]> wrote:
>
> > On Tuesday, August 7, 2018 1:43:56 AM CEST Linus Walleij wrote:
> > > On Tue, Aug 7, 2018 at 12:29 AM Janusz Krzysztofik <[email protected]>
> > wrote:
> > >
> > > Hi Janusz!
> > >
> > > > Certain GPIO descriptor arrays returned by gpio_get_array() may contain
> > > > information on a single GPIO chip driving array member pins in hardware
> > > > order. In such cases, bitmaps of values can be passed directly to the
> > > > chip callback functions without wasting time on iterations.
> > > >
> > > > Add respective code to gpiod_get/set_array_bitmap_complex() functions.
> > > >
> > > > Signed-off-by: Janusz Krzysztofik <[email protected]>
> > >
> > > I think it would be disappointing to leave all the users of the old
> > > array API without the performance improvement. I think we need to
> > > deal with this in a way such that everyone can benefit from it.
>
> I agree with Linus on that one. When I initially proposed the gpio
> bitbanging API I had something more advanced in mind where the GPIO
> framework would be responsible for toggling the GPIOs on its own when
> it's given an array of bytes to transmit (this way you avoid going
> back and forth between the GPIO user and the GPIO framework). But this
> approach would clearly be more invasive than what you propose
> here (turning the int array into a bitmap and optimizing). So, if we go
> for the "int array -> bitmap" approach I think all users should be
> converted so that we end up with a single API.

I thought about this the recent days and something must have gone
wrong in the development process of the array API because this
was the (mine atleast) intention all the time.

If we look at the GPIOchip driver API it looks like this:

int (*get_multiple)(struct gpio_chip *chip,
unsigned long *mask,
unsigned long *bits);
void (*set_multiple)(struct gpio_chip *chip,
unsigned long *mask,
unsigned long *bits);

So there is nothing hindering the drivers from optimizing a call
here into a single register write, which is what e.g. the gpio-mmio.c
driver does: if the hardware has a dedicated register for clearing
and setting lines, it will simply just write the register with
whatever is passed in, also cache the current value so it doesn't
need to read back the register every time.

When an array comes down to gpiod_set_array_value_complex()
it loops over the descriptors in order to handle e.g. open drain
settings separately. Then the remainder (lines that should just
be set 1/0) is pushed to the .set_multiple() callback if they
are on the same chip.

This is assuming:

1. The CPU is not the bottleneck so we can do a bit
of complex looping over structs etc in each write.

2. We want to perform as much in a single register write
as possible to avoid I/O and glitches as all lines (e.g.
clock and data) get written at the same time, if possible.
(No skew.)

It seems Janusz has problems with assumption (1) and therefore
is trying to optimize the read/write path. This can be done if all
descs are on the same chip and none of them is using open drain
or open source.

To keep track of "quick path" the array needs to have a state.
So a magic "cookie" or something like that needs to be passed
to the array API.

I would suggest that struct gpiod_descs contain a magic cookie
returned from [devm_]gpiod_get_array[_optional]() that can be
passed along to get/set array operations or left as NULL to just
fall back to the default:

void gpiod_set_array_value(unsigned int array_size,
struct gpio_desc **desc_array, int *value_array,
struct gpiod_array_cookie *cookie);

Cookie is just a dummy name, I don't know what makes most
sense. It reflects a state for the entire array.

If this cookie exist in some struct gpio_chip state variable, it
informs gpiolib that this array:

- Has all descriptors in the same gpiochip
- Has no open drain or open source-flagged lines

It can thenbypass the complex check and just write the values
directly by calling down into .set_multiple().

Maybe this cookie could just be a bool that is true when the above
is fulfilled. But it's best if that is hidden from the consumers
I guess, they shouldn't try to half-guess if the criteria is true,
gpiolib should do that.

The current users would have to be augmented to store the
cookie and pass it along when getting/setting arrays, but it would
be pretty simple and straight-forward compared to adding a new
API.

Yours,
Linus Walleij

2018-08-13 22:36:34

by Janusz Krzysztofik

[permalink] [raw]
Subject: [PATCH v3 1/7] mtd: rawnand: ams-delta: show parent device in sysfs

Fix a bug where parent device symlinks aren't shown in sysfs.

While at it, make use of the default owner set by mtdcore.

Signed-off-by: Janusz Krzysztofik <[email protected]>
Reviewed-by: Boris Brezillon <[email protected]>
---
drivers/mtd/nand/raw/ams-delta.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/mtd/nand/raw/ams-delta.c b/drivers/mtd/nand/raw/ams-delta.c
index 2a8872ebd14a..af313c620264 100644
--- a/drivers/mtd/nand/raw/ams-delta.c
+++ b/drivers/mtd/nand/raw/ams-delta.c
@@ -162,7 +162,7 @@ static int ams_delta_init(struct platform_device *pdev)
}

ams_delta_mtd = nand_to_mtd(this);
- ams_delta_mtd->owner = THIS_MODULE;
+ ams_delta_mtd->dev.parent = &pdev->dev;

/*
* Don't try to request the memory region from here,
--
2.16.4


2018-08-13 22:36:38

by Janusz Krzysztofik

[permalink] [raw]
Subject: [PATCH v3 4/7] mtd: rawnand: ams-delta: request data port GPIO resource

Data port used by the driver is actually an OMAP MPUIO device, already
under control of gpio-omap driver. For that reason we used to not
request the memory region of the port as that would fail because the
region is already busy. Despite that, we are still accessing the port
by just ioremapping it and performing read/write operations. Moreover,
we are doing that without any proteciton from other users legally
manipulating the port pins over GPIO API.

The plan is to convert the driver to access the port over functions
exposed by the gpio-omap driver. Before that happens, already prevent
from other users accessing the port pins by requesting an array of its
GPIO descriptors.

Signed-off-by: Janusz Krzysztofik <[email protected]>
Reviewed-by: Boris Brezillon <[email protected]>
Reviewed-by: Linus Walleij <[email protected]>
---
drivers/mtd/nand/raw/ams-delta.c | 8 ++++++++
1 file changed, 8 insertions(+)

diff --git a/drivers/mtd/nand/raw/ams-delta.c b/drivers/mtd/nand/raw/ams-delta.c
index 48233d638d2a..09d6901fc94d 100644
--- a/drivers/mtd/nand/raw/ams-delta.c
+++ b/drivers/mtd/nand/raw/ams-delta.c
@@ -161,6 +161,7 @@ static int ams_delta_init(struct platform_device *pdev)
struct mtd_info *mtd;
struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
void __iomem *io_base;
+ struct gpio_descs *data_gpiods;
int err = 0;

if (!res)
@@ -261,6 +262,13 @@ static int ams_delta_init(struct platform_device *pdev)
dev_err(&pdev->dev, "CLE GPIO request failed (%d)\n", err);
goto out_mtd;
}
+ /* Request array of data pins, initialize them as input */
+ data_gpiods = devm_gpiod_get_array(&pdev->dev, "data", GPIOD_IN);
+ if (IS_ERR(data_gpiods)) {
+ err = PTR_ERR(data_gpiods);
+ dev_err(&pdev->dev, "data GPIO request failed: %d\n", err);
+ goto out_mtd;
+ }

/* Scan to find existence of the device */
err = nand_scan(mtd, 1);
--
2.16.4


2018-08-13 22:36:43

by Janusz Krzysztofik

[permalink] [raw]
Subject: [PATCH v3 7/7] mtd: rawnand: ams-delta: use GPIO API for data I/O

Don't readw()/writew() data directly from/to GPIO port which is under
control of gpio-omap driver, use GPIO API instead.

Degrade of performance on Amstrad Delta is significant, can be
recognized as a regression, that's why I'm still submitting this patch
as RFC.

The driver should work with any 8+-bit bidirectional GPIO port, not
only OMAP.

Signed-off-by: Janusz Krzysztofik <[email protected]>
Reviewed-by: Linus Walleij <[email protected]>
---
Exceprts fro timestamped boot logs showing performance degrade.
Before the change:
[ 5.469426] Creating 6 MTD partitions on "ams-delta-nand":
[ 5.480909] 0x000000000000-0x000000380000 : "Kernel"
[ 5.502659] 0x000000380000-0x0000003c0000 : "u-boot"
[ 5.523055] 0x0000003c0000-0x000000400000 : "u-boot params"
[ 5.543612] 0x000000400000-0x000000440000 : "Amstrad LDR"
[ 5.564607] 0x000000440000-0x000001f40000 : "File system"
[ 5.601760] 0x000001f40000-0x000002000000 : "PBL reserved"
[ 5.624369] usbcore: registered new interface driver dm9601
[ 5.636233] ohci_hcd: USB 1.1 'Open' Host Controller (OHCI) Driver
[ 5.649191] ohci-omap: OHCI OMAP driver
[ 5.660713] ohci ohci: OMAP OHCI
[ 5.671299] ohci ohci: new USB bus registered, assigned bus number 1
[ 5.686862] ohci ohci: irq 54, io mem 0xfffba000
[ 5.785897] hub 1-0:1.0: USB hub found
[ 5.797856] hub 1-0:1.0: 3 ports detected
[ 5.817576] usbcore: registered new interface driver usb-storage
[ 5.832551] ams-delta-serio ams-delta-serio: incomplete constraints, dummy supplies not allowed
[ 5.858588] ams-delta-serio ams-delta-serio: regulator request failed (-19)
[ 5.879312] input: omap-keypad as /devices/platform/omap-keypad/input/input0
[ 5.902490] omap_rtc omap_rtc: already running
[ 5.922929] omap_rtc omap_rtc: registered as rtc0
[ 5.945570] softdog: initialized. soft_noboot=0 soft_margin=60 sec soft_panic=0 (nowayout=0)
[ 5.976712] usbcore: registered new interface driver btusb
[ 6.007348] cx20442-codec cx20442-codec: incomplete constraints, dummy supplies not allowed
[ 6.040575] cx20442-codec cx20442-codec: failed to get POR supply (-19)
[ 6.060916] cx20442-codec cx20442-codec: ASoC: failed to probe component -517
[ 6.083486] ams-delta-audio ams-delta-audio: ASoC: failed to instantiate card -517
[ 6.121850] ams-delta-audio ams-delta-audio: snd_soc_register_card failed (-517)
[ 6.163047] NET: Registered protocol family 17
[ 6.182770] Bluetooth: BNEP (Ethernet Emulation) ver 1.3
[ 6.203517] Bluetooth: BNEP socket layer initialized
[ 6.283017] serial8250 serial8250.1: Linked as a consumer to regulator.1
[ 6.306113] usb 1-1: new full-speed USB device number 2 using ohci
[ 6.328825] clock: disabling unused clocks to save power
[ 6.350426] Skipping reset check for DSP domain clock "dsptim_ck"
[ 6.372272] Skipping reset check for DSP domain clock "dspxor_ck"
[ 6.393712] Skipping reset check for DSP domain clock "dspper_ck"
[ 6.428311] ams-delta-serio ams-delta-serio: Linked as a consumer to regulator.2
[ 6.467801] serio serio0: AMS DELTA keyboard adapter
[ 6.492511] cx20442-codec cx20442-codec: Linked as a consumer to regulator.1
[ 6.527382] ams-delta-audio ams-delta-audio: cx20442-voice <-> omap-mcbsp.1 mapping ok
[ 6.577387] input: AMS_DELTA hook_switch as /devices/platform/ams-delta-audio/sound/card0/input1
[ 6.627497] input: AT Raw Set 2 keyboard as /devices/platform/ams-delta-serio/serio0/input/input2
[ 6.673663] omap_rtc omap_rtc: setting system clock to 2013-02-09 07:22:13 UTC (1360394533)
[ 6.715895] modem_nreset: incomplete constraints, leaving on
[ 6.738677] ALSA device list:
[ 6.758398] #0: AMS_DELTA
[ 7.036234] dm9601 1-1:1.0 eth0: register 'dm9601' at usb-ohci-1, Davicom DM96xx USB 10/100 Ethernet, 00:60:6e:00:00:11
[ 133.860599] random: crng init done
[ 138.275853] VFS: Mounted root (jffs2 filesystem) on device 31:4.

After the change:
[ 6.261107] Creating 6 MTD partitions on "ams-delta-nand":
[ 6.272046] 0x000000000000-0x000000380000 : "Kernel"
[ 6.294436] 0x000000380000-0x0000003c0000 : "u-boot"
[ 6.314454] 0x0000003c0000-0x000000400000 : "u-boot params"
[ 6.335353] 0x000000400000-0x000000440000 : "Amstrad LDR"
[ 6.356669] 0x000000440000-0x000001f40000 : "File system"
[ 6.393713] 0x000001f40000-0x000002000000 : "PBL reserved"
[ 6.416771] usbcore: registered new interface driver dm9601
[ 6.428631] ohci_hcd: USB 1.1 'Open' Host Controller (OHCI) Driver
[ 6.441533] ohci-omap: OHCI OMAP driver
[ 6.452758] ohci ohci: OMAP OHCI
[ 6.463300] ohci ohci: new USB bus registered, assigned bus number 1
[ 6.478817] ohci ohci: irq 54, io mem 0xfffba000
[ 6.580520] hub 1-0:1.0: USB hub found
[ 6.592424] hub 1-0:1.0: 3 ports detected
[ 6.612363] usbcore: registered new interface driver usb-storage
[ 6.627358] ams-delta-serio ams-delta-serio: incomplete constraints, dummy supplies not allowed
[ 6.653296] ams-delta-serio ams-delta-serio: regulator request failed (-19)
[ 6.674219] input: omap-keypad as /devices/platform/omap-keypad/input/input0
[ 6.697910] omap_rtc omap_rtc: already running
[ 6.718376] omap_rtc omap_rtc: registered as rtc0
[ 6.740942] softdog: initialized. soft_noboot=0 soft_margin=60 sec soft_panic=0 (nowayout=0)
[ 6.772085] usbcore: registered new interface driver btusb
[ 6.803187] cx20442-codec cx20442-codec: incomplete constraints, dummy supplies not allowed
[ 6.836386] cx20442-codec cx20442-codec: failed to get POR supply (-19)
[ 6.856730] cx20442-codec cx20442-codec: ASoC: failed to probe component -517
[ 6.879234] ams-delta-audio ams-delta-audio: ASoC: failed to instantiate card -517
[ 6.917325] ams-delta-audio ams-delta-audio: snd_soc_register_card failed (-517)
[ 6.958519] NET: Registered protocol family 17
[ 6.978224] Bluetooth: BNEP (Ethernet Emulation) ver 1.3
[ 6.998989] Bluetooth: BNEP socket layer initialized
[ 7.077593] serial8250 serial8250.1: Linked as a consumer to regulator.1
[ 7.100678] usb 1-1: new full-speed USB device number 2 using ohci
[ 7.123429] clock: disabling unused clocks to save power
[ 7.145074] Skipping reset check for DSP domain clock "dsptim_ck"
[ 7.166983] Skipping reset check for DSP domain clock "dspxor_ck"
[ 7.188434] Skipping reset check for DSP domain clock "dspper_ck"
[ 7.223321] ams-delta-serio ams-delta-serio: Linked as a consumer to regulator.2
[ 7.262882] serio serio0: AMS DELTA keyboard adapter
[ 7.287656] cx20442-codec cx20442-codec: Linked as a consumer to regulator.1
[ 7.322824] ams-delta-audio ams-delta-audio: cx20442-voice <-> omap-mcbsp.1 mapping ok
[ 7.373165] input: AMS_DELTA hook_switch as /devices/platform/ams-delta-audio/sound/card0/input1
[ 7.423520] input: AT Raw Set 2 keyboard as /devices/platform/ams-delta-serio/serio0/input/input2
[ 7.469578] omap_rtc omap_rtc: setting system clock to 2013-02-09 07:34:10 UTC (1360395250)
[ 7.511830] modem_nreset: incomplete constraints, leaving on
[ 7.534812] ALSA device list:
[ 7.554541] #0: AMS_DELTA
[ 7.971899] dm9601 1-1:1.0 eth0: register 'dm9601' at usb-ohci-1, Davicom DM96xx USB 10/100 Ethernet, 00:60:6e:00:00:11
[ 133.935226] random: crng init done
[ 320.764645] VFS: Mounted root (jffs2 filesystem) on device 31:4.

I think most of the overhead is in iterations performed both inside and
outside get/set array functions:
- building a mask for get_multiple() and transfering results to value array
in gpiod_get_array_value_complex(), then again from the array by a caller,
- building a value array by the caller, the building a mask and tranferiing
values from array to bitmap for .set_multiple() in
gpiod_set_array_value_comples().

Thanks,
Janusz

drivers/mtd/nand/raw/ams-delta.c | 126 ++++++++++++++++++---------------------
1 file changed, 59 insertions(+), 67 deletions(-)

diff --git a/drivers/mtd/nand/raw/ams-delta.c b/drivers/mtd/nand/raw/ams-delta.c
index 59fc417e8fa9..8bedcd7c7928 100644
--- a/drivers/mtd/nand/raw/ams-delta.c
+++ b/drivers/mtd/nand/raw/ams-delta.c
@@ -24,13 +24,10 @@
#include <linux/mtd/mtd.h>
#include <linux/mtd/rawnand.h>
#include <linux/mtd/partitions.h>
-#include <linux/platform_data/gpio-omap.h>
+#include <linux/platform_device.h>

-#include <asm/io.h>
#include <asm/sizes.h>

-#include <mach/hardware.h>
-
/*
* MTD structure for E3 (Delta)
*/
@@ -44,7 +41,7 @@ struct ams_delta_nand {
struct gpio_desc *gpiod_nwe;
struct gpio_desc *gpiod_ale;
struct gpio_desc *gpiod_cle;
- void __iomem *io_base;
+ struct gpio_descs *data_gpiods;
bool data_in;
};

@@ -73,52 +70,79 @@ static const struct mtd_partition partition_info[] = {
.size = 3 * SZ_256K },
};

-static void ams_delta_write_next_byte(struct ams_delta_nand *priv, u_char byte)
+static void ams_delta_write_commit(struct ams_delta_nand *priv)
{
- struct nand_chip *this = &priv->nand_chip;
-
- writew(byte, this->IO_ADDR_W);
-
gpiod_set_value(priv->gpiod_nwe, 0);
ndelay(40);
gpiod_set_value(priv->gpiod_nwe, 1);
}

-static void ams_delta_write_byte(struct ams_delta_nand *priv, u_char byte)
+static void ams_delta_write_next_byte(struct ams_delta_nand *priv, u_char byte)
{
- void __iomem *io_base = priv->io_base;
+ struct gpio_descs *data_gpiods = priv->data_gpiods;
+ unsigned long bits = byte;
+ int i, value_array[data_gpiods->ndescs];

+ for (i = 0; i < data_gpiods->ndescs; i++)
+ value_array[i] = test_bit(i, &bits);
+
+ gpiod_set_raw_array_value(data_gpiods->ndescs, data_gpiods->desc,
+ value_array);
+
+ ams_delta_write_commit(priv);
+}
+
+static void ams_delta_write_byte(struct ams_delta_nand *priv, u_char byte)
+{
if (priv->data_in) {
- writew(0, io_base + OMAP_MPUIO_IO_CNTL);
+ struct gpio_descs *data_gpiods = priv->data_gpiods;
+ unsigned long bits = byte;
+ int i;
+
+ for (i = 0; i < data_gpiods->ndescs; i++)
+ gpiod_direction_output_raw(data_gpiods->desc[i],
+ test_bit(i, &bits));
priv->data_in = false;
- }

- ams_delta_write_next_byte(priv, byte);
+ ams_delta_write_commit(priv);
+ } else {
+ ams_delta_write_next_byte(priv, byte);
+ }
}

static u_char ams_delta_read_next_byte(struct ams_delta_nand *priv)
{
- struct nand_chip *this = &priv->nand_chip;
- u_char res;
+ struct gpio_descs *data_gpiods = priv->data_gpiods;
+ unsigned long bits = 0;
+ int i, value_array[data_gpiods->ndescs];

gpiod_set_value(priv->gpiod_nre, 0);
ndelay(40);

- res = readw(this->IO_ADDR_R);
+ gpiod_get_raw_array_value(data_gpiods->ndescs, data_gpiods->desc,
+ value_array);

gpiod_set_value(priv->gpiod_nre, 1);

- return res;
+ for (i = 0; i < data_gpiods->ndescs; i++)
+ if (value_array[i])
+ __set_bit(i, &bits);
+
+ return bits;
}

static u_char ams_delta_read_byte(struct mtd_info *mtd)
{
struct nand_chip *this = mtd_to_nand(mtd);
struct ams_delta_nand *priv = nand_get_controller_data(this);
- void __iomem *io_base = priv->io_base;

if (!priv->data_in) {
- writew(~0, io_base + OMAP_MPUIO_IO_CNTL);
+ struct gpio_descs *data_gpiods = priv->data_gpiods;
+ int i;
+
+ for (i = 0; i < data_gpiods->ndescs; i++)
+ gpiod_direction_input(data_gpiods->desc[i]);
+
priv->data_in = true;
}

@@ -193,14 +217,8 @@ static int ams_delta_init(struct platform_device *pdev)
struct ams_delta_nand *priv;
struct nand_chip *this;
struct mtd_info *mtd;
- struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- void __iomem *io_base;
- struct gpio_descs *data_gpiods;
int err = 0;

- if (!res)
- return -ENXIO;
-
/* Allocate memory for MTD device structure and private data */
priv = devm_kzalloc(&pdev->dev, sizeof(struct ams_delta_nand),
GFP_KERNEL);
@@ -213,25 +231,8 @@ static int ams_delta_init(struct platform_device *pdev)
mtd = nand_to_mtd(this);
mtd->dev.parent = &pdev->dev;

- /*
- * Don't try to request the memory region from here,
- * it should have been already requested from the
- * gpio-omap driver and requesting it again would fail.
- */
-
- io_base = ioremap(res->start, resource_size(res));
- if (io_base == NULL) {
- dev_err(&pdev->dev, "ioremap failed\n");
- err = -EIO;
- goto out_free;
- }
-
- priv->io_base = io_base;
nand_set_controller_data(this, priv);

- /* Set address of NAND IO lines */
- this->IO_ADDR_R = io_base + OMAP_MPUIO_INPUT_LATCH;
- this->IO_ADDR_W = io_base + OMAP_MPUIO_OUTPUT;
this->read_byte = ams_delta_read_byte;
this->write_buf = ams_delta_write_buf;
this->read_buf = ams_delta_read_buf;
@@ -241,7 +242,7 @@ static int ams_delta_init(struct platform_device *pdev)
if (IS_ERR(priv->gpiod_rdy)) {
err = PTR_ERR(priv->gpiod_rdy);
dev_warn(&pdev->dev, "RDY GPIO request failed (%d)\n", err);
- goto out_mtd;
+ return err;
}

if (priv->gpiod_rdy)
@@ -259,67 +260,61 @@ static int ams_delta_init(struct platform_device *pdev)
if (IS_ERR(priv->gpiod_nwp)) {
err = PTR_ERR(priv->gpiod_nwp);
dev_err(&pdev->dev, "NWP GPIO request failed (%d)\n", err);
- goto out_mtd;
+ return err;
}

priv->gpiod_nce = devm_gpiod_get(&pdev->dev, "nce", GPIOD_OUT_HIGH);
if (IS_ERR(priv->gpiod_nce)) {
err = PTR_ERR(priv->gpiod_nce);
dev_err(&pdev->dev, "NCE GPIO request failed (%d)\n", err);
- goto out_mtd;
+ return err;
}

priv->gpiod_nre = devm_gpiod_get(&pdev->dev, "nre", GPIOD_OUT_HIGH);
if (IS_ERR(priv->gpiod_nre)) {
err = PTR_ERR(priv->gpiod_nre);
dev_err(&pdev->dev, "NRE GPIO request failed (%d)\n", err);
- goto out_mtd;
+ return err;
}

priv->gpiod_nwe = devm_gpiod_get(&pdev->dev, "nwe", GPIOD_OUT_HIGH);
if (IS_ERR(priv->gpiod_nwe)) {
err = PTR_ERR(priv->gpiod_nwe);
dev_err(&pdev->dev, "NWE GPIO request failed (%d)\n", err);
- goto out_mtd;
+ return err;
}

priv->gpiod_ale = devm_gpiod_get(&pdev->dev, "ale", GPIOD_OUT_LOW);
if (IS_ERR(priv->gpiod_ale)) {
err = PTR_ERR(priv->gpiod_ale);
dev_err(&pdev->dev, "ALE GPIO request failed (%d)\n", err);
- goto out_mtd;
+ return err;
}

priv->gpiod_cle = devm_gpiod_get(&pdev->dev, "cle", GPIOD_OUT_LOW);
if (IS_ERR(priv->gpiod_cle)) {
err = PTR_ERR(priv->gpiod_cle);
dev_err(&pdev->dev, "CLE GPIO request failed (%d)\n", err);
- goto out_mtd;
+ return err;
}
/* Request array of data pins, initialize them as input */
- data_gpiods = devm_gpiod_get_array(&pdev->dev, "data", GPIOD_IN);
- if (IS_ERR(data_gpiods)) {
- err = PTR_ERR(data_gpiods);
+ priv->data_gpiods = devm_gpiod_get_array(&pdev->dev, "data", GPIOD_IN);
+ if (IS_ERR(priv->data_gpiods)) {
+ err = PTR_ERR(priv->data_gpiods);
dev_err(&pdev->dev, "data GPIO request failed: %d\n", err);
- goto out_mtd;
+ return err;
}
priv->data_in = true;

/* Scan to find existence of the device */
err = nand_scan(mtd, 1);
if (err)
- goto out_mtd;
+ return err;

/* Register the partitions */
mtd_device_register(mtd, partition_info, ARRAY_SIZE(partition_info));

- goto out;
-
- out_mtd:
- iounmap(io_base);
-out_free:
- out:
- return err;
+ return 0;
}

/*
@@ -329,13 +324,10 @@ static int ams_delta_cleanup(struct platform_device *pdev)
{
struct ams_delta_nand *priv = platform_get_drvdata(pdev);
struct mtd_info *mtd = nand_to_mtd(&priv->nand_chip);
- void __iomem *io_base = priv->io_base;

- /* Release resources, unregister device */
+ /* Unregister device */
nand_release(mtd);

- iounmap(io_base);
-
return 0;
}

--
2.16.4


2018-08-13 22:37:06

by Janusz Krzysztofik

[permalink] [raw]
Subject: [PATCH v3 3/7] ARM: OMAP1: ams-delta: Provide GPIO lookup table for NAND data port

Data port used by Amstrad Delta NAND driver is actually an OMAP MPUIO
device, already under control of gpio-omap driver. The NAND driver
gets access to the port by ioremapping it and performs read/write
operations. That is done without any proteciton from other users
legally manipulating the port pins over GPIO API.

The plan is to convert the driver to access the port over GPIO consumer
API. Before that is implemented, the driver can already obtain
exclusive access to the port by requesting an array of its GPIO
descriptors.

Add respective entries to the NAND GPIO lookup table.

Signed-off-by: Janusz Krzysztofik <[email protected]>
Reviewed-by: Boris Brezillon <[email protected]>
Reviewed-by: Linus Walleij <[email protected]>
---
arch/arm/mach-omap1/board-ams-delta.c | 11 ++++++++++-
1 file changed, 10 insertions(+), 1 deletion(-)

diff --git a/arch/arm/mach-omap1/board-ams-delta.c b/arch/arm/mach-omap1/board-ams-delta.c
index eedacdfe9725..16f7bbe47607 100644
--- a/arch/arm/mach-omap1/board-ams-delta.c
+++ b/arch/arm/mach-omap1/board-ams-delta.c
@@ -337,7 +337,8 @@ static struct platform_device ams_delta_nand_device = {
.resource = ams_delta_nand_resources,
};

-#define OMAP_GPIO_LABEL "gpio-0-15"
+#define OMAP_GPIO_LABEL "gpio-0-15"
+#define OMAP_MPUIO_LABEL "mpuio"

static struct gpiod_lookup_table ams_delta_nand_gpio_table = {
.table = {
@@ -349,6 +350,14 @@ static struct gpiod_lookup_table ams_delta_nand_gpio_table = {
GPIO_LOOKUP(LATCH2_LABEL, LATCH2_PIN_NAND_NWE, "nwe", 0),
GPIO_LOOKUP(LATCH2_LABEL, LATCH2_PIN_NAND_ALE, "ale", 0),
GPIO_LOOKUP(LATCH2_LABEL, LATCH2_PIN_NAND_CLE, "cle", 0),
+ GPIO_LOOKUP_IDX(OMAP_MPUIO_LABEL, 0, "data", 0, 0),
+ GPIO_LOOKUP_IDX(OMAP_MPUIO_LABEL, 1, "data", 1, 0),
+ GPIO_LOOKUP_IDX(OMAP_MPUIO_LABEL, 2, "data", 2, 0),
+ GPIO_LOOKUP_IDX(OMAP_MPUIO_LABEL, 3, "data", 3, 0),
+ GPIO_LOOKUP_IDX(OMAP_MPUIO_LABEL, 4, "data", 4, 0),
+ GPIO_LOOKUP_IDX(OMAP_MPUIO_LABEL, 5, "data", 5, 0),
+ GPIO_LOOKUP_IDX(OMAP_MPUIO_LABEL, 6, "data", 6, 0),
+ GPIO_LOOKUP_IDX(OMAP_MPUIO_LABEL, 7, "data", 7, 0),
{ },
},
};
--
2.16.4


2018-08-13 22:37:18

by Janusz Krzysztofik

[permalink] [raw]
Subject: [PATCH v3 2/7] mtd: rawnand: ams-delta: Use private structure

Introduce a driver private structure and allocate it on device probe.
Use it for storing nand_chip structure, GPIO descriptors prevoiusly
stored in static variables as well as io_base pointer previously passed
as nand controller data or platform driver data. Subsequent patches
may populate the structure with more members as needed.

Signed-off-by: Janusz Krzysztofik <[email protected]>
Reviewed-by: Boris Brezillon <[email protected]>
---
drivers/mtd/nand/raw/ams-delta.c | 126 +++++++++++++++++++++------------------
1 file changed, 69 insertions(+), 57 deletions(-)

diff --git a/drivers/mtd/nand/raw/ams-delta.c b/drivers/mtd/nand/raw/ams-delta.c
index af313c620264..48233d638d2a 100644
--- a/drivers/mtd/nand/raw/ams-delta.c
+++ b/drivers/mtd/nand/raw/ams-delta.c
@@ -34,14 +34,18 @@
/*
* MTD structure for E3 (Delta)
*/
-static struct mtd_info *ams_delta_mtd = NULL;
-static struct gpio_desc *gpiod_rdy;
-static struct gpio_desc *gpiod_nce;
-static struct gpio_desc *gpiod_nre;
-static struct gpio_desc *gpiod_nwp;
-static struct gpio_desc *gpiod_nwe;
-static struct gpio_desc *gpiod_ale;
-static struct gpio_desc *gpiod_cle;
+
+struct ams_delta_nand {
+ struct nand_chip nand_chip;
+ struct gpio_desc *gpiod_rdy;
+ struct gpio_desc *gpiod_nce;
+ struct gpio_desc *gpiod_nre;
+ struct gpio_desc *gpiod_nwp;
+ struct gpio_desc *gpiod_nwe;
+ struct gpio_desc *gpiod_ale;
+ struct gpio_desc *gpiod_cle;
+ void __iomem *io_base;
+};

/*
* Define partitions for flash devices
@@ -71,26 +75,28 @@ static const struct mtd_partition partition_info[] = {
static void ams_delta_write_byte(struct mtd_info *mtd, u_char byte)
{
struct nand_chip *this = mtd_to_nand(mtd);
- void __iomem *io_base = (void __iomem *)nand_get_controller_data(this);
+ struct ams_delta_nand *priv = nand_get_controller_data(this);
+ void __iomem *io_base = priv->io_base;

writew(0, io_base + OMAP_MPUIO_IO_CNTL);
writew(byte, this->IO_ADDR_W);
- gpiod_set_value(gpiod_nwe, 0);
+ gpiod_set_value(priv->gpiod_nwe, 0);
ndelay(40);
- gpiod_set_value(gpiod_nwe, 1);
+ gpiod_set_value(priv->gpiod_nwe, 1);
}

static u_char ams_delta_read_byte(struct mtd_info *mtd)
{
u_char res;
struct nand_chip *this = mtd_to_nand(mtd);
- void __iomem *io_base = (void __iomem *)nand_get_controller_data(this);
+ struct ams_delta_nand *priv = nand_get_controller_data(this);
+ void __iomem *io_base = priv->io_base;

- gpiod_set_value(gpiod_nre, 0);
+ gpiod_set_value(priv->gpiod_nre, 0);
ndelay(40);
writew(~0, io_base + OMAP_MPUIO_IO_CNTL);
res = readw(this->IO_ADDR_R);
- gpiod_set_value(gpiod_nre, 1);
+ gpiod_set_value(priv->gpiod_nre, 1);

return res;
}
@@ -123,11 +129,13 @@ static void ams_delta_read_buf(struct mtd_info *mtd, u_char *buf, int len)
static void ams_delta_hwcontrol(struct mtd_info *mtd, int cmd,
unsigned int ctrl)
{
+ struct nand_chip *this = mtd_to_nand(mtd);
+ struct ams_delta_nand *priv = nand_get_controller_data(this);

if (ctrl & NAND_CTRL_CHANGE) {
- gpiod_set_value(gpiod_nce, !(ctrl & NAND_NCE));
- gpiod_set_value(gpiod_cle, !!(ctrl & NAND_CLE));
- gpiod_set_value(gpiod_ale, !!(ctrl & NAND_ALE));
+ gpiod_set_value(priv->gpiod_nce, !(ctrl & NAND_NCE));
+ gpiod_set_value(priv->gpiod_cle, !!(ctrl & NAND_CLE));
+ gpiod_set_value(priv->gpiod_ale, !!(ctrl & NAND_ALE));
}

if (cmd != NAND_CMD_NONE)
@@ -136,7 +144,10 @@ static void ams_delta_hwcontrol(struct mtd_info *mtd, int cmd,

static int ams_delta_nand_ready(struct mtd_info *mtd)
{
- return gpiod_get_value(gpiod_rdy);
+ struct nand_chip *this = mtd_to_nand(mtd);
+ struct ams_delta_nand *priv = nand_get_controller_data(this);
+
+ return gpiod_get_value(priv->gpiod_rdy);
}


@@ -145,7 +156,9 @@ static int ams_delta_nand_ready(struct mtd_info *mtd)
*/
static int ams_delta_init(struct platform_device *pdev)
{
+ struct ams_delta_nand *priv;
struct nand_chip *this;
+ struct mtd_info *mtd;
struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
void __iomem *io_base;
int err = 0;
@@ -154,15 +167,16 @@ static int ams_delta_init(struct platform_device *pdev)
return -ENXIO;

/* Allocate memory for MTD device structure and private data */
- this = kzalloc(sizeof(struct nand_chip), GFP_KERNEL);
- if (!this) {
+ priv = devm_kzalloc(&pdev->dev, sizeof(struct ams_delta_nand),
+ GFP_KERNEL);
+ if (!priv) {
pr_warn("Unable to allocate E3 NAND MTD device structure.\n");
- err = -ENOMEM;
- goto out;
+ return -ENOMEM;
}
+ this = &priv->nand_chip;

- ams_delta_mtd = nand_to_mtd(this);
- ams_delta_mtd->dev.parent = &pdev->dev;
+ mtd = nand_to_mtd(this);
+ mtd->dev.parent = &pdev->dev;

/*
* Don't try to request the memory region from here,
@@ -177,7 +191,8 @@ static int ams_delta_init(struct platform_device *pdev)
goto out_free;
}

- nand_set_controller_data(this, (void *)io_base);
+ priv->io_base = io_base;
+ nand_set_controller_data(this, priv);

/* Set address of NAND IO lines */
this->IO_ADDR_R = io_base + OMAP_MPUIO_INPUT_LATCH;
@@ -187,14 +202,14 @@ static int ams_delta_init(struct platform_device *pdev)
this->read_buf = ams_delta_read_buf;
this->cmd_ctrl = ams_delta_hwcontrol;

- gpiod_rdy = devm_gpiod_get_optional(&pdev->dev, "rdy", GPIOD_IN);
- if (IS_ERR(gpiod_rdy)) {
- err = PTR_ERR(gpiod_rdy);
+ priv->gpiod_rdy = devm_gpiod_get_optional(&pdev->dev, "rdy", GPIOD_IN);
+ if (IS_ERR(priv->gpiod_rdy)) {
+ err = PTR_ERR(priv->gpiod_rdy);
dev_warn(&pdev->dev, "RDY GPIO request failed (%d)\n", err);
goto out_mtd;
}

- if (gpiod_rdy)
+ if (priv->gpiod_rdy)
this->dev_ready = ams_delta_nand_ready;

/* 25 us command delay time */
@@ -202,66 +217,64 @@ static int ams_delta_init(struct platform_device *pdev)
this->ecc.mode = NAND_ECC_SOFT;
this->ecc.algo = NAND_ECC_HAMMING;

- platform_set_drvdata(pdev, io_base);
+ platform_set_drvdata(pdev, priv);

/* Set chip enabled, but */
- gpiod_nwp = devm_gpiod_get(&pdev->dev, "nwp", GPIOD_OUT_HIGH);
- if (IS_ERR(gpiod_nwp)) {
- err = PTR_ERR(gpiod_nwp);
+ priv->gpiod_nwp = devm_gpiod_get(&pdev->dev, "nwp", GPIOD_OUT_HIGH);
+ if (IS_ERR(priv->gpiod_nwp)) {
+ err = PTR_ERR(priv->gpiod_nwp);
dev_err(&pdev->dev, "NWP GPIO request failed (%d)\n", err);
goto out_mtd;
}

- gpiod_nce = devm_gpiod_get(&pdev->dev, "nce", GPIOD_OUT_HIGH);
- if (IS_ERR(gpiod_nce)) {
- err = PTR_ERR(gpiod_nce);
+ priv->gpiod_nce = devm_gpiod_get(&pdev->dev, "nce", GPIOD_OUT_HIGH);
+ if (IS_ERR(priv->gpiod_nce)) {
+ err = PTR_ERR(priv->gpiod_nce);
dev_err(&pdev->dev, "NCE GPIO request failed (%d)\n", err);
goto out_mtd;
}

- gpiod_nre = devm_gpiod_get(&pdev->dev, "nre", GPIOD_OUT_HIGH);
- if (IS_ERR(gpiod_nre)) {
- err = PTR_ERR(gpiod_nre);
+ priv->gpiod_nre = devm_gpiod_get(&pdev->dev, "nre", GPIOD_OUT_HIGH);
+ if (IS_ERR(priv->gpiod_nre)) {
+ err = PTR_ERR(priv->gpiod_nre);
dev_err(&pdev->dev, "NRE GPIO request failed (%d)\n", err);
goto out_mtd;
}

- gpiod_nwe = devm_gpiod_get(&pdev->dev, "nwe", GPIOD_OUT_HIGH);
- if (IS_ERR(gpiod_nwe)) {
- err = PTR_ERR(gpiod_nwe);
+ priv->gpiod_nwe = devm_gpiod_get(&pdev->dev, "nwe", GPIOD_OUT_HIGH);
+ if (IS_ERR(priv->gpiod_nwe)) {
+ err = PTR_ERR(priv->gpiod_nwe);
dev_err(&pdev->dev, "NWE GPIO request failed (%d)\n", err);
goto out_mtd;
}

- gpiod_ale = devm_gpiod_get(&pdev->dev, "ale", GPIOD_OUT_LOW);
- if (IS_ERR(gpiod_ale)) {
- err = PTR_ERR(gpiod_ale);
+ priv->gpiod_ale = devm_gpiod_get(&pdev->dev, "ale", GPIOD_OUT_LOW);
+ if (IS_ERR(priv->gpiod_ale)) {
+ err = PTR_ERR(priv->gpiod_ale);
dev_err(&pdev->dev, "ALE GPIO request failed (%d)\n", err);
goto out_mtd;
}

- gpiod_cle = devm_gpiod_get(&pdev->dev, "cle", GPIOD_OUT_LOW);
- if (IS_ERR(gpiod_cle)) {
- err = PTR_ERR(gpiod_cle);
+ priv->gpiod_cle = devm_gpiod_get(&pdev->dev, "cle", GPIOD_OUT_LOW);
+ if (IS_ERR(priv->gpiod_cle)) {
+ err = PTR_ERR(priv->gpiod_cle);
dev_err(&pdev->dev, "CLE GPIO request failed (%d)\n", err);
goto out_mtd;
}

/* Scan to find existence of the device */
- err = nand_scan(ams_delta_mtd, 1);
+ err = nand_scan(mtd, 1);
if (err)
goto out_mtd;

/* Register the partitions */
- mtd_device_register(ams_delta_mtd, partition_info,
- ARRAY_SIZE(partition_info));
+ mtd_device_register(mtd, partition_info, ARRAY_SIZE(partition_info));

goto out;

out_mtd:
iounmap(io_base);
out_free:
- kfree(this);
out:
return err;
}
@@ -271,16 +284,15 @@ static int ams_delta_init(struct platform_device *pdev)
*/
static int ams_delta_cleanup(struct platform_device *pdev)
{
- void __iomem *io_base = platform_get_drvdata(pdev);
+ struct ams_delta_nand *priv = platform_get_drvdata(pdev);
+ struct mtd_info *mtd = nand_to_mtd(&priv->nand_chip);
+ void __iomem *io_base = priv->io_base;

/* Release resources, unregister device */
- nand_release(ams_delta_mtd);
+ nand_release(mtd);

iounmap(io_base);

- /* Free the MTD device structure */
- kfree(mtd_to_nand(ams_delta_mtd));
-
return 0;
}

--
2.16.4


2018-08-13 22:37:32

by Janusz Krzysztofik

[permalink] [raw]
Subject: [PATCH v3 6/7] mtd: rawnand: ams-delta: Simplify pointer resolution on read/write

Simplify data read/write sub-functions by making them accept private
structure pointer instead of resolving it again from mtd_info.

Signed-off-by: Janusz Krzysztofik <[email protected]>
Reviewed-by: Boris Brezillon <[email protected]>
---
drivers/mtd/nand/raw/ams-delta.c | 30 +++++++++++++++---------------
1 file changed, 15 insertions(+), 15 deletions(-)

diff --git a/drivers/mtd/nand/raw/ams-delta.c b/drivers/mtd/nand/raw/ams-delta.c
index 5f9180fe4f8b..59fc417e8fa9 100644
--- a/drivers/mtd/nand/raw/ams-delta.c
+++ b/drivers/mtd/nand/raw/ams-delta.c
@@ -73,10 +73,9 @@ static const struct mtd_partition partition_info[] = {
.size = 3 * SZ_256K },
};

-static void ams_delta_write_next_byte(struct mtd_info *mtd, u_char byte)
+static void ams_delta_write_next_byte(struct ams_delta_nand *priv, u_char byte)
{
- struct nand_chip *this = mtd_to_nand(mtd);
- struct ams_delta_nand *priv = nand_get_controller_data(this);
+ struct nand_chip *this = &priv->nand_chip;

writew(byte, this->IO_ADDR_W);

@@ -85,10 +84,8 @@ static void ams_delta_write_next_byte(struct mtd_info *mtd, u_char byte)
gpiod_set_value(priv->gpiod_nwe, 1);
}

-static void ams_delta_write_byte(struct mtd_info *mtd, u_char byte)
+static void ams_delta_write_byte(struct ams_delta_nand *priv, u_char byte)
{
- struct nand_chip *this = mtd_to_nand(mtd);
- struct ams_delta_nand *priv = nand_get_controller_data(this);
void __iomem *io_base = priv->io_base;

if (priv->data_in) {
@@ -96,13 +93,12 @@ static void ams_delta_write_byte(struct mtd_info *mtd, u_char byte)
priv->data_in = false;
}

- ams_delta_write_next_byte(mtd, byte);
+ ams_delta_write_next_byte(priv, byte);
}

-static u_char ams_delta_read_next_byte(struct mtd_info *mtd)
+static u_char ams_delta_read_next_byte(struct ams_delta_nand *priv)
{
- struct nand_chip *this = mtd_to_nand(mtd);
- struct ams_delta_nand *priv = nand_get_controller_data(this);
+ struct nand_chip *this = &priv->nand_chip;
u_char res;

gpiod_set_value(priv->gpiod_nre, 0);
@@ -126,30 +122,34 @@ static u_char ams_delta_read_byte(struct mtd_info *mtd)
priv->data_in = true;
}

- return ams_delta_read_next_byte(mtd);
+ return ams_delta_read_next_byte(priv);
}

static void ams_delta_write_buf(struct mtd_info *mtd, const u_char *buf,
int len)
{
+ struct nand_chip *this = mtd_to_nand(mtd);
+ struct ams_delta_nand *priv = nand_get_controller_data(this);
int i = 0;

if (len > 0)
- ams_delta_write_byte(mtd, buf[i++]);
+ ams_delta_write_byte(priv, buf[i++]);

while (i < len)
- ams_delta_write_next_byte(mtd, buf[i++]);
+ ams_delta_write_next_byte(priv, buf[i++]);
}

static void ams_delta_read_buf(struct mtd_info *mtd, u_char *buf, int len)
{
+ struct nand_chip *this = mtd_to_nand(mtd);
+ struct ams_delta_nand *priv = nand_get_controller_data(this);
int i = 0;

if (len > 0)
buf[i++] = ams_delta_read_byte(mtd);

while (i < len)
- buf[i++] = ams_delta_read_next_byte(mtd);
+ buf[i++] = ams_delta_read_next_byte(priv);
}

/*
@@ -173,7 +173,7 @@ static void ams_delta_hwcontrol(struct mtd_info *mtd, int cmd,
}

if (cmd != NAND_CMD_NONE)
- ams_delta_write_byte(mtd, cmd);
+ ams_delta_write_byte(priv, cmd);
}

static int ams_delta_nand_ready(struct mtd_info *mtd)
--
2.16.4


2018-08-13 22:37:53

by Janusz Krzysztofik

[permalink] [raw]
Subject: [PATCH v3 5/7] mtd: rawnand: ams-delta: Set port direction when needed

In its current shape, the driver sets data port direction before each
byte read/write operation, even during multi-byte transfers. Improve
performance of the driver by setting the port direction only when
needed.

This optimisation will become particularly important as soon as
planned conversion of the driver to GPIO API for data I/O will be
implemented.

Signed-off-by: Janusz Krzysztofik <[email protected]>
---
drivers/mtd/nand/raw/ams-delta.c | 59 ++++++++++++++++++++++++++++++++--------
1 file changed, 47 insertions(+), 12 deletions(-)

diff --git a/drivers/mtd/nand/raw/ams-delta.c b/drivers/mtd/nand/raw/ams-delta.c
index 09d6901fc94d..5f9180fe4f8b 100644
--- a/drivers/mtd/nand/raw/ams-delta.c
+++ b/drivers/mtd/nand/raw/ams-delta.c
@@ -45,6 +45,7 @@ struct ams_delta_nand {
struct gpio_desc *gpiod_ale;
struct gpio_desc *gpiod_cle;
void __iomem *io_base;
+ bool data_in;
};

/*
@@ -72,50 +73,83 @@ static const struct mtd_partition partition_info[] = {
.size = 3 * SZ_256K },
};

-static void ams_delta_write_byte(struct mtd_info *mtd, u_char byte)
+static void ams_delta_write_next_byte(struct mtd_info *mtd, u_char byte)
{
struct nand_chip *this = mtd_to_nand(mtd);
struct ams_delta_nand *priv = nand_get_controller_data(this);
- void __iomem *io_base = priv->io_base;

- writew(0, io_base + OMAP_MPUIO_IO_CNTL);
writew(byte, this->IO_ADDR_W);
+
gpiod_set_value(priv->gpiod_nwe, 0);
ndelay(40);
gpiod_set_value(priv->gpiod_nwe, 1);
}

-static u_char ams_delta_read_byte(struct mtd_info *mtd)
+static void ams_delta_write_byte(struct mtd_info *mtd, u_char byte)
{
- u_char res;
struct nand_chip *this = mtd_to_nand(mtd);
struct ams_delta_nand *priv = nand_get_controller_data(this);
void __iomem *io_base = priv->io_base;

+ if (priv->data_in) {
+ writew(0, io_base + OMAP_MPUIO_IO_CNTL);
+ priv->data_in = false;
+ }
+
+ ams_delta_write_next_byte(mtd, byte);
+}
+
+static u_char ams_delta_read_next_byte(struct mtd_info *mtd)
+{
+ struct nand_chip *this = mtd_to_nand(mtd);
+ struct ams_delta_nand *priv = nand_get_controller_data(this);
+ u_char res;
+
gpiod_set_value(priv->gpiod_nre, 0);
ndelay(40);
- writew(~0, io_base + OMAP_MPUIO_IO_CNTL);
+
res = readw(this->IO_ADDR_R);
+
gpiod_set_value(priv->gpiod_nre, 1);

return res;
}

+static u_char ams_delta_read_byte(struct mtd_info *mtd)
+{
+ struct nand_chip *this = mtd_to_nand(mtd);
+ struct ams_delta_nand *priv = nand_get_controller_data(this);
+ void __iomem *io_base = priv->io_base;
+
+ if (!priv->data_in) {
+ writew(~0, io_base + OMAP_MPUIO_IO_CNTL);
+ priv->data_in = true;
+ }
+
+ return ams_delta_read_next_byte(mtd);
+}
+
static void ams_delta_write_buf(struct mtd_info *mtd, const u_char *buf,
int len)
{
- int i;
+ int i = 0;
+
+ if (len > 0)
+ ams_delta_write_byte(mtd, buf[i++]);

- for (i=0; i<len; i++)
- ams_delta_write_byte(mtd, buf[i]);
+ while (i < len)
+ ams_delta_write_next_byte(mtd, buf[i++]);
}

static void ams_delta_read_buf(struct mtd_info *mtd, u_char *buf, int len)
{
- int i;
+ int i = 0;
+
+ if (len > 0)
+ buf[i++] = ams_delta_read_byte(mtd);

- for (i=0; i<len; i++)
- buf[i] = ams_delta_read_byte(mtd);
+ while (i < len)
+ buf[i++] = ams_delta_read_next_byte(mtd);
}

/*
@@ -269,6 +303,7 @@ static int ams_delta_init(struct platform_device *pdev)
dev_err(&pdev->dev, "data GPIO request failed: %d\n", err);
goto out_mtd;
}
+ priv->data_in = true;

/* Scan to find existence of the device */
err = nand_scan(mtd, 1);
--
2.16.4


2018-08-13 22:37:53

by Janusz Krzysztofik

[permalink] [raw]
Subject: [PATCH v3 0/7] mtd: rawnand: ams-delta: Use GPIO API for data I/O


Implement the idea suggested by Artem Bityutskiy and Tony Lindgren,
described in commit b027274d2e3a ("mtd: ams-delta: fix
request_mem_region() failure"). Use pure GPIO API as suggested by
Boris Brezillon.


Janusz Krzysztofik (7):
mtd: rawnand: ams-delta: show parent device in sysfs
mtd: rawnand: ams-delta: Use private structure
ARM: OMAP1: ams-delta: Provide GPIO lookup table for NAND data port
mtd: rawnand: ams-delta: request data port GPIO resource
mtd: rawnand: ams-delta: Set port direction when needed
mtd: rawnand: ams-delta: Simplify pointer resolution on read/write
mtd: rawnand: ams-delta: use GPIO API for data I/O


Changelog:
v3:
[PATCH v3 1/7] mtd: rawnand: ams-delta: show parent device in sysfs
- renamed and an explanation added based on other similar patches on
Marek Vasut request, thanks.
[PATCH v3 2/7] mtd: rawnand: ams-delta: Use private structure
- no changes.
[PATCH v3 3/7] ARM: OMAP1: ams-delta: Provide GPIO lookup table for NAND
data port
- no changes.
[PATCH v3 4/7] mtd: rawnand: ams-delta: request data port GPIO resource
- no changes.
[PATCH v3 5/7] mtd: rawnand: ams-delta: Set port direction when needed
- modified to set port direction only when needed instead of on each
transfer as suggested by Boris, thanks, though I kept separate
*_next_byte() functions to maximize performance as much as possible,
- moved back in front of "mtd: rawnand: ams-delta: use GPIO API for data
I/O" with a comment added referring to the planned switch to GPIO API.
[PATCH v3 6/7] mtd: rawnand: ams-delta: Simplify pointer resolution
- moved back in front of "mtd: rawnand: ams-delta: use GPIO API for data
I/O" on Boris request, thanks.
[RFC PATCH v3 7/7] mtd: rawnand: ams-delta: use GPIO API for data I/O
- rebased back on top of the two mentioned above,
- not intended to apply it yet due to performance issues on Amstrad Delta.
Removed from the series:
[RFC PATCH v2 09/12] gpiolib: Identify GPIO descriptor arrays with direct
mapping
[RFC PATCH v2 10/12] gpiolib: Introduce bitmap get/set array API extension
[RFC PATCH v2 11/12] mtd: rawnand: ams-delta: Use GPIO API bitmap extension
[RFC PATCH v2 12/12] gpiolib: Add fast processing path to bitmap API functions
- intended to be still iterated in a follow up series until performance
issues are resolved.
[RFC PATCH v2 06/12] ARM: OMAP1: ams-delta: drop obsolete NAND resources
- postponed until acceptable performance on Amstrad Delta is achieved.

v2:
[RFC PATCH v2 00/12] mtd: rawnand: ams-delta: Use GPIO API for data I/O
- renamed from former [RFC PATCH 0/8] mtd: rawnand: ams-delta: Use
gpio-omap accessors for data I/O
[RFC PATCH v2 01/12] mtd: rawnand: ams-delta: Assign mtd->dev.parent,
not mtd->owner
- split out from former [RFC PATCH 1/8] on Boris request, thanks.
[RFC PATCH v2 02/12] mtd: rawnand: ams-delta: Use private structure
- remaining part of the former [RFC PATCH 1/8].
[RFC PATCH v2 03/12] ARM: OMAP1: ams-delta: Provide GPIO lookup table
for NAND data port
- split out from former [RFC PATCH 5/8] on Boris requesst, thanks,
[RFC PATCH v2 04/12] mtd: rawnand: ams-delta: request data port GPIO resource
- remaining part of the former [RFC PATCH 5/8],
[RFC PATCH v2 05/12] mtd: rawnand: ams-delta: use GPIO API for data read/write
- reworked from former [RFC PATCH 8/8] on Boris requesst to use pure
GPIO API, thanks,
- moved up in front of former [RFC PATCH 3/8] on Boris request, thanks.
[RFC PATCH v2 06/12] ARM: OMAP1: ams-delta: drop obsolete NAND resources
- split out from former [RFC PATCH 8/8].
[RFC PATCH v2 07/12] mtd: rawnand: ams-delta: Set port direction once per
transfer
- reworked from former [RFC PATCH 3/8] on top of [RFC PATCH v2 05/12].
[RFC PATCH v2 08/12] mtd: rawnand: ams-delta: Simplify pointer resolution
on read/write
- reworked from former [RFC PATCH 4/8] on top of [RFC PATCH v2 08/12],
- renamed from 'Optimize' to 'Simplify' on Boris request, thanks.
[RFC PATCH v2 09/12] gpiolib: Identify GPIO descriptor arrays with direct
mapping
- new patch.
[RFC PATCH v2 10/12] gpiolib: Introduce bitmap get/set array API extension
- new patch.
[RFC PATCH v2 11/12] mtd: rawnand: ams-delta: Use GPIO API bitmap extension
- new patch.
[RFC PATCH v2 12/12] gpiolib: Add fast processing path to bitmap API functions
- new patch.
Removed from the series:
[RFC PATCH 2/8] mtd: rawnand: ams-delta: Write protect device during probe
- postponed on Boris request, thanks.
[RFC PATCH 6/8] gpio: omap: Add get/set_multiple() callbacks
- already applied by Linux in linux-gpio tree, thanks.
[RFC PATCH 7/8] mtd: rawnand: ams-delta: Check sanity of data GPIO resource
- most controversial one, no longer needed after switching to GPIO API.


diffstat
arch/arm/mach-omap1/board-ams-delta.c | 11 -
drivers/mtd/nand/raw/ams-delta.c | 351 +++++++++++++++++++---------------
2 files changed, 209 insertions(+), 153 deletions(-)

Thanks,
Janusz


2018-08-16 13:04:47

by Boris Brezillon

[permalink] [raw]
Subject: Re: [PATCH v3 5/7] mtd: rawnand: ams-delta: Set port direction when needed

On Tue, 14 Aug 2018 00:34:46 +0200
Janusz Krzysztofik <[email protected]> wrote:

> In its current shape, the driver sets data port direction before each
> byte read/write operation, even during multi-byte transfers. Improve
> performance of the driver by setting the port direction only when
> needed.
>
> This optimisation will become particularly important as soon as
> planned conversion of the driver to GPIO API for data I/O will be
> implemented.
>
> Signed-off-by: Janusz Krzysztofik <[email protected]>
> ---
> drivers/mtd/nand/raw/ams-delta.c | 59 ++++++++++++++++++++++++++++++++--------
> 1 file changed, 47 insertions(+), 12 deletions(-)
>
> diff --git a/drivers/mtd/nand/raw/ams-delta.c b/drivers/mtd/nand/raw/ams-delta.c
> index 09d6901fc94d..5f9180fe4f8b 100644
> --- a/drivers/mtd/nand/raw/ams-delta.c
> +++ b/drivers/mtd/nand/raw/ams-delta.c
> @@ -45,6 +45,7 @@ struct ams_delta_nand {
> struct gpio_desc *gpiod_ale;
> struct gpio_desc *gpiod_cle;
> void __iomem *io_base;
> + bool data_in;
> };
>
> /*
> @@ -72,50 +73,83 @@ static const struct mtd_partition partition_info[] = {
> .size = 3 * SZ_256K },
> };
>
> -static void ams_delta_write_byte(struct mtd_info *mtd, u_char byte)
> +static void ams_delta_write_next_byte(struct mtd_info *mtd, u_char byte)
> {
> struct nand_chip *this = mtd_to_nand(mtd);
> struct ams_delta_nand *priv = nand_get_controller_data(this);
> - void __iomem *io_base = priv->io_base;
>
> - writew(0, io_base + OMAP_MPUIO_IO_CNTL);
> writew(byte, this->IO_ADDR_W);
> +
> gpiod_set_value(priv->gpiod_nwe, 0);
> ndelay(40);
> gpiod_set_value(priv->gpiod_nwe, 1);
> }
>
> -static u_char ams_delta_read_byte(struct mtd_info *mtd)
> +static void ams_delta_write_byte(struct mtd_info *mtd, u_char byte)
> {
> - u_char res;
> struct nand_chip *this = mtd_to_nand(mtd);
> struct ams_delta_nand *priv = nand_get_controller_data(this);
> void __iomem *io_base = priv->io_base;
>
> + if (priv->data_in) {
> + writew(0, io_base + OMAP_MPUIO_IO_CNTL);
> + priv->data_in = false;
> + }
> +
> + ams_delta_write_next_byte(mtd, byte);
> +}

I'm not a big fan of this {read,write}_byte/next_byte() approach.

Can't we do something like:

static void ams_delta_io_write(struct ams_delta_nand *priv, u8 data)
{
writew(byte, priv->nand_chip.IO_ADDR_W);
gpiod_set_value(priv->gpiod_nwe, 0);
ndelay(40);
gpiod_set_value(priv->gpiod_nwe, 1);
}

static u8 ams_delta_io_read(struct ams_delta_nand *priv)
{
u8 res;

gpiod_set_value(priv->gpiod_nre, 0);
ndelay(40);
res = readw(priv->nand_chip.IO_ADDR_R);
gpiod_set_value(priv->gpiod_nre, 1);

return res;
}

static void ams_delta_set_io_dir(struct ams_delta_nand *priv, bool in)
{
if (in == priv->data_in)
return;

writew(in ? ~0 : 0, priv->io_base + OMAP_MPUIO_IO_CNTL);
priv->data_in = in;
}

static void ams_delta_write_buf(struct mtd_info *mtd, const u8 *buf,
int len)
{
struct nand_chip *this = mtd_to_nand(mtd);
struct ams_delta_nand *priv = nand_get_controller_data(this);
int i;

ams_delta_set_io_dir(priv, false);

for (i =0; i < len; i++)
ams_delta_io_write(priv, buf[i]);
}

static void ams_delta_write_byte(struct mtd_info *mtd, u8 byte)
{
ams_delta_write_buf(mtd, &byte, 1);
}

static void ams_delta_read_buf(struct mtd_info *mtd, u8 *buf, int len)
{
struct nand_chip *this = mtd_to_nand(mtd);
struct ams_delta_nand *priv = nand_get_controller_data(this);
int i;

ams_delta_set_io_dir(priv, true);

for (i = 0; i < len; i++)
buf[i] = ams_delta_io_read(priv);
}

static u8 ams_delta_read_byte(struct mtd_info *mtd)
{
u8 res;

ams_delta_read_buf(mtd, &res, 1);

return res;
}


2018-08-16 13:16:10

by Boris Brezillon

[permalink] [raw]
Subject: Re: [PATCH v3 7/7] mtd: rawnand: ams-delta: use GPIO API for data I/O

On Tue, 14 Aug 2018 00:34:48 +0200
Janusz Krzysztofik <[email protected]> wrote:

> Don't readw()/writew() data directly from/to GPIO port which is under
> control of gpio-omap driver, use GPIO API instead.
>
> Degrade of performance on Amstrad Delta is significant, can be
> recognized as a regression, that's why I'm still submitting this patch
> as RFC.
>
> The driver should work with any 8+-bit bidirectional GPIO port, not
> only OMAP.
>
> Signed-off-by: Janusz Krzysztofik <[email protected]>
> Reviewed-by: Linus Walleij <[email protected]>
> ---
> Exceprts fro timestamped boot logs showing performance degrade.
> Before the change:
> [ 5.469426] Creating 6 MTD partitions on "ams-delta-nand":
> [ 5.480909] 0x000000000000-0x000000380000 : "Kernel"
> [ 5.502659] 0x000000380000-0x0000003c0000 : "u-boot"
> [ 5.523055] 0x0000003c0000-0x000000400000 : "u-boot params"
> [ 5.543612] 0x000000400000-0x000000440000 : "Amstrad LDR"
> [ 5.564607] 0x000000440000-0x000001f40000 : "File system"
> [ 5.601760] 0x000001f40000-0x000002000000 : "PBL reserved"
> [ 5.624369] usbcore: registered new interface driver dm9601
> [ 5.636233] ohci_hcd: USB 1.1 'Open' Host Controller (OHCI) Driver
> [ 5.649191] ohci-omap: OHCI OMAP driver
> [ 5.660713] ohci ohci: OMAP OHCI
> [ 5.671299] ohci ohci: new USB bus registered, assigned bus number 1
> [ 5.686862] ohci ohci: irq 54, io mem 0xfffba000
> [ 5.785897] hub 1-0:1.0: USB hub found
> [ 5.797856] hub 1-0:1.0: 3 ports detected
> [ 5.817576] usbcore: registered new interface driver usb-storage
> [ 5.832551] ams-delta-serio ams-delta-serio: incomplete constraints, dummy supplies not allowed
> [ 5.858588] ams-delta-serio ams-delta-serio: regulator request failed (-19)
> [ 5.879312] input: omap-keypad as /devices/platform/omap-keypad/input/input0
> [ 5.902490] omap_rtc omap_rtc: already running
> [ 5.922929] omap_rtc omap_rtc: registered as rtc0
> [ 5.945570] softdog: initialized. soft_noboot=0 soft_margin=60 sec soft_panic=0 (nowayout=0)
> [ 5.976712] usbcore: registered new interface driver btusb
> [ 6.007348] cx20442-codec cx20442-codec: incomplete constraints, dummy supplies not allowed
> [ 6.040575] cx20442-codec cx20442-codec: failed to get POR supply (-19)
> [ 6.060916] cx20442-codec cx20442-codec: ASoC: failed to probe component -517
> [ 6.083486] ams-delta-audio ams-delta-audio: ASoC: failed to instantiate card -517
> [ 6.121850] ams-delta-audio ams-delta-audio: snd_soc_register_card failed (-517)
> [ 6.163047] NET: Registered protocol family 17
> [ 6.182770] Bluetooth: BNEP (Ethernet Emulation) ver 1.3
> [ 6.203517] Bluetooth: BNEP socket layer initialized
> [ 6.283017] serial8250 serial8250.1: Linked as a consumer to regulator.1
> [ 6.306113] usb 1-1: new full-speed USB device number 2 using ohci
> [ 6.328825] clock: disabling unused clocks to save power
> [ 6.350426] Skipping reset check for DSP domain clock "dsptim_ck"
> [ 6.372272] Skipping reset check for DSP domain clock "dspxor_ck"
> [ 6.393712] Skipping reset check for DSP domain clock "dspper_ck"
> [ 6.428311] ams-delta-serio ams-delta-serio: Linked as a consumer to regulator.2
> [ 6.467801] serio serio0: AMS DELTA keyboard adapter
> [ 6.492511] cx20442-codec cx20442-codec: Linked as a consumer to regulator.1
> [ 6.527382] ams-delta-audio ams-delta-audio: cx20442-voice <-> omap-mcbsp.1 mapping ok
> [ 6.577387] input: AMS_DELTA hook_switch as /devices/platform/ams-delta-audio/sound/card0/input1
> [ 6.627497] input: AT Raw Set 2 keyboard as /devices/platform/ams-delta-serio/serio0/input/input2
> [ 6.673663] omap_rtc omap_rtc: setting system clock to 2013-02-09 07:22:13 UTC (1360394533)
> [ 6.715895] modem_nreset: incomplete constraints, leaving on
> [ 6.738677] ALSA device list:
> [ 6.758398] #0: AMS_DELTA
> [ 7.036234] dm9601 1-1:1.0 eth0: register 'dm9601' at usb-ohci-1, Davicom DM96xx USB 10/100 Ethernet, 00:60:6e:00:00:11
> [ 133.860599] random: crng init done
> [ 138.275853] VFS: Mounted root (jffs2 filesystem) on device 31:4.
>
> After the change:
> [ 6.261107] Creating 6 MTD partitions on "ams-delta-nand":
> [ 6.272046] 0x000000000000-0x000000380000 : "Kernel"
> [ 6.294436] 0x000000380000-0x0000003c0000 : "u-boot"
> [ 6.314454] 0x0000003c0000-0x000000400000 : "u-boot params"
> [ 6.335353] 0x000000400000-0x000000440000 : "Amstrad LDR"
> [ 6.356669] 0x000000440000-0x000001f40000 : "File system"
> [ 6.393713] 0x000001f40000-0x000002000000 : "PBL reserved"
> [ 6.416771] usbcore: registered new interface driver dm9601
> [ 6.428631] ohci_hcd: USB 1.1 'Open' Host Controller (OHCI) Driver
> [ 6.441533] ohci-omap: OHCI OMAP driver
> [ 6.452758] ohci ohci: OMAP OHCI
> [ 6.463300] ohci ohci: new USB bus registered, assigned bus number 1
> [ 6.478817] ohci ohci: irq 54, io mem 0xfffba000
> [ 6.580520] hub 1-0:1.0: USB hub found
> [ 6.592424] hub 1-0:1.0: 3 ports detected
> [ 6.612363] usbcore: registered new interface driver usb-storage
> [ 6.627358] ams-delta-serio ams-delta-serio: incomplete constraints, dummy supplies not allowed
> [ 6.653296] ams-delta-serio ams-delta-serio: regulator request failed (-19)
> [ 6.674219] input: omap-keypad as /devices/platform/omap-keypad/input/input0
> [ 6.697910] omap_rtc omap_rtc: already running
> [ 6.718376] omap_rtc omap_rtc: registered as rtc0
> [ 6.740942] softdog: initialized. soft_noboot=0 soft_margin=60 sec soft_panic=0 (nowayout=0)
> [ 6.772085] usbcore: registered new interface driver btusb
> [ 6.803187] cx20442-codec cx20442-codec: incomplete constraints, dummy supplies not allowed
> [ 6.836386] cx20442-codec cx20442-codec: failed to get POR supply (-19)
> [ 6.856730] cx20442-codec cx20442-codec: ASoC: failed to probe component -517
> [ 6.879234] ams-delta-audio ams-delta-audio: ASoC: failed to instantiate card -517
> [ 6.917325] ams-delta-audio ams-delta-audio: snd_soc_register_card failed (-517)
> [ 6.958519] NET: Registered protocol family 17
> [ 6.978224] Bluetooth: BNEP (Ethernet Emulation) ver 1.3
> [ 6.998989] Bluetooth: BNEP socket layer initialized
> [ 7.077593] serial8250 serial8250.1: Linked as a consumer to regulator.1
> [ 7.100678] usb 1-1: new full-speed USB device number 2 using ohci
> [ 7.123429] clock: disabling unused clocks to save power
> [ 7.145074] Skipping reset check for DSP domain clock "dsptim_ck"
> [ 7.166983] Skipping reset check for DSP domain clock "dspxor_ck"
> [ 7.188434] Skipping reset check for DSP domain clock "dspper_ck"
> [ 7.223321] ams-delta-serio ams-delta-serio: Linked as a consumer to regulator.2
> [ 7.262882] serio serio0: AMS DELTA keyboard adapter
> [ 7.287656] cx20442-codec cx20442-codec: Linked as a consumer to regulator.1
> [ 7.322824] ams-delta-audio ams-delta-audio: cx20442-voice <-> omap-mcbsp.1 mapping ok
> [ 7.373165] input: AMS_DELTA hook_switch as /devices/platform/ams-delta-audio/sound/card0/input1
> [ 7.423520] input: AT Raw Set 2 keyboard as /devices/platform/ams-delta-serio/serio0/input/input2
> [ 7.469578] omap_rtc omap_rtc: setting system clock to 2013-02-09 07:34:10 UTC (1360395250)
> [ 7.511830] modem_nreset: incomplete constraints, leaving on
> [ 7.534812] ALSA device list:
> [ 7.554541] #0: AMS_DELTA
> [ 7.971899] dm9601 1-1:1.0 eth0: register 'dm9601' at usb-ohci-1, Davicom DM96xx USB 10/100 Ethernet, 00:60:6e:00:00:11
> [ 133.935226] random: crng init done
> [ 320.764645] VFS: Mounted root (jffs2 filesystem) on device 31:4.
>
> I think most of the overhead is in iterations performed both inside and
> outside get/set array functions:
> - building a mask for get_multiple() and transfering results to value array
> in gpiod_get_array_value_complex(), then again from the array by a caller,
> - building a value array by the caller, the building a mask and tranferiing
> values from array to bitmap for .set_multiple() in
> gpiod_set_array_value_comples().

Sorry but we need more than just speculations here, and using
bootlog timestamps is clearly not enough to prove any of these
suppositions. You'll have to setup ftrace/perf and trigger read/write
requests (using nanddump/nandwrite) to figure out where the overhead
comes from.

Also, I'd recommend keeping this patch for the series changing the GPIO
API.

2018-08-20 21:39:33

by Janusz Krzysztofik

[permalink] [raw]
Subject: [PATCH v4 0/3] mtd: rawnand: ams-delta: Cleanups and optimizations


This series consist of possibly ready to apply patches extracted from
a former one titled "mtd: rawnand: ams-delta: Use GPIO API for data I/O".
Remaining patches implementing conversion of data I/O to GPIO have been
postponed until gpiolib is optimized to ensure sufficient performance.


Janusz Krzysztofik (3):
mtd: rawnand: ams-delta: show parent device in sysfs
mtd: rawnand: ams-delta: Use private structure
mtd: rawnand: ams-delta: Set port direction when needed


Changelog:
v4:
[PATCH v4 1/3] mtd: rawnand: ams-delta: show parent device in sysfs
- no changes.
[PATCH v4 2/3] mtd: rawnand: ams-delta: Use private structure
- no changes.
[PATCH v4 3/3] mtd: rawnand: ams-delta: Set port direction when
- formerly submitted as [PATCH v3 5/7], now rebased on top of 2/3 from
the top of the postponed "[PATCH v3 4/7] mtd: rawnand: ams-delta:
request data port GPIO resource",
- refactored as requested by Boris Brezillon, thanks.
Rrmoved from this series:
[PATCH v3 3/7] ARM: OMAP1: ams-delta: Provide GPIO lookup table for NAND
data port
- postponed, will be resubmitted when gpiolib optimizations are ready.
[PATCH v3 4/7] mtd: rawnand: ams-delta: request data port GPIO resource
- postponed, will be resubmitted when gpiolib optimizations are ready.
[PATCH v3 6/7] mtd: rawnand: ams-delta: Simplify pointer resolution
- no longer needed, the idea has been implemented in the refactored 3/3.
[RFC PATCH v3 7/7] mtd: rawnand: ams-delta: use GPIO API for data I/O
- postponed, will be resubmitted when gpiolib optimizations are ready.

v3:
[PATCH v3 1/7] mtd: rawnand: ams-delta: show parent device in sysfs
- renamed and an explanation added based on other similar patches on
Marek Vasut request, thanks.
[PATCH v3 2/7] mtd: rawnand: ams-delta: Use private structure
- no changes.
[PATCH v3 3/7] ARM: OMAP1: ams-delta: Provide GPIO lookup table for NAND
data port
- no changes.
[PATCH v3 4/7] mtd: rawnand: ams-delta: request data port GPIO resource
- no changes.
[PATCH v3 5/7] mtd: rawnand: ams-delta: Set port direction when needed
- modified to set port direction only when needed instead of on each
transfer as suggested by Boris, thanks, though I kept separate
*_next_byte() functions to maximize performance as much as possible,
- moved back in front of "mtd: rawnand: ams-delta: use GPIO API for data
I/O" with a comment added referring to the planned switch to GPIO API.
[PATCH v3 6/7] mtd: rawnand: ams-delta: Simplify pointer resolution
- moved back in front of "mtd: rawnand: ams-delta: use GPIO API for data
I/O" on Boris request, thanks.
[RFC PATCH v3 7/7] mtd: rawnand: ams-delta: use GPIO API for data I/O
- rebased back on top of the two mentioned above,
- not intended to apply it yet due to performance issues on Amstrad Delta.
Removed from the series:
[RFC PATCH v2 09/12] gpiolib: Identify GPIO descriptor arrays with direct
mapping
[RFC PATCH v2 10/12] gpiolib: Introduce bitmap get/set array API extension
[RFC PATCH v2 11/12] mtd: rawnand: ams-delta: Use GPIO API bitmap extension
[RFC PATCH v2 12/12] gpiolib: Add fast processing path to bitmap API functions
- intended to be still iterated in a follow up series until performance
issues are resolved.
[RFC PATCH v2 06/12] ARM: OMAP1: ams-delta: drop obsolete NAND resources
- postponed until acceptable performance on Amstrad Delta is achieved.

v2:
[RFC PATCH v2 00/12] mtd: rawnand: ams-delta: Use GPIO API for data I/O
- renamed from former [RFC PATCH 0/8] mtd: rawnand: ams-delta: Use
gpio-omap accessors for data I/O
[RFC PATCH v2 01/12] mtd: rawnand: ams-delta: Assign mtd->dev.parent,
not mtd->owner
- split out from former [RFC PATCH 1/8] on Boris request, thanks.
[RFC PATCH v2 02/12] mtd: rawnand: ams-delta: Use private structure
- remaining part of the former [RFC PATCH 1/8].
[RFC PATCH v2 03/12] ARM: OMAP1: ams-delta: Provide GPIO lookup table
for NAND data port
- split out from former [RFC PATCH 5/8] on Boris requesst, thanks,
[RFC PATCH v2 04/12] mtd: rawnand: ams-delta: request data port GPIO resource
- remaining part of the former [RFC PATCH 5/8],
[RFC PATCH v2 05/12] mtd: rawnand: ams-delta: use GPIO API for data read/write
- reworked from former [RFC PATCH 8/8] on Boris requesst to use pure
GPIO API, thanks,
- moved up in front of former [RFC PATCH 3/8] on Boris request, thanks.
[RFC PATCH v2 06/12] ARM: OMAP1: ams-delta: drop obsolete NAND resources
- split out from former [RFC PATCH 8/8].
[RFC PATCH v2 07/12] mtd: rawnand: ams-delta: Set port direction once per
transfer
- reworked from former [RFC PATCH 3/8] on top of [RFC PATCH v2 05/12].
[RFC PATCH v2 08/12] mtd: rawnand: ams-delta: Simplify pointer resolution
on read/write
- reworked from former [RFC PATCH 4/8] on top of [RFC PATCH v2 08/12],
- renamed from 'Optimize' to 'Simplify' on Boris request, thanks.
[RFC PATCH v2 09/12] gpiolib: Identify GPIO descriptor arrays with direct
mapping
- new patch.
[RFC PATCH v2 10/12] gpiolib: Introduce bitmap get/set array API extension
- new patch.
[RFC PATCH v2 11/12] mtd: rawnand: ams-delta: Use GPIO API bitmap extension
- new patch.
[RFC PATCH v2 12/12] gpiolib: Add fast processing path to bitmap API functions
- new patch.
Removed from the series:
[RFC PATCH 2/8] mtd: rawnand: ams-delta: Write protect device during probe
- postponed on Boris request, thanks.
[RFC PATCH 6/8] gpio: omap: Add get/set_multiple() callbacks
- already applied by Linux in linux-gpio tree, thanks.
[RFC PATCH 7/8] mtd: rawnand: ams-delta: Check sanity of data GPIO resource
- most controversial one, no longer needed after switching to GPIO API.


diffstat:
ams-delta.c | 196 +++++++++++++++++++++++++++++++++++-------------------------
1 file changed, 115 insertions(+), 81 deletions(-)

2018-08-20 21:39:44

by Janusz Krzysztofik

[permalink] [raw]
Subject: [PATCH v4 1/3] mtd: rawnand: ams-delta: show parent device in sysfs

Fix a bug where parent device symlinks aren't shown in sysfs.

While at it, make use of the default owner set by mtdcore.

Signed-off-by: Janusz Krzysztofik <[email protected]>
Reviewed-by: Boris Brezillon <[email protected]>
---
drivers/mtd/nand/raw/ams-delta.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/mtd/nand/raw/ams-delta.c b/drivers/mtd/nand/raw/ams-delta.c
index 2a8872ebd14a..af313c620264 100644
--- a/drivers/mtd/nand/raw/ams-delta.c
+++ b/drivers/mtd/nand/raw/ams-delta.c
@@ -162,7 +162,7 @@ static int ams_delta_init(struct platform_device *pdev)
}

ams_delta_mtd = nand_to_mtd(this);
- ams_delta_mtd->owner = THIS_MODULE;
+ ams_delta_mtd->dev.parent = &pdev->dev;

/*
* Don't try to request the memory region from here,
--
2.16.4


2018-08-20 21:39:48

by Janusz Krzysztofik

[permalink] [raw]
Subject: [PATCH v4 2/3] mtd: rawnand: ams-delta: Use private structure

Introduce a driver private structure and allocate it on device probe.
Use it for storing nand_chip structure, GPIO descriptors prevoiusly
stored in static variables as well as io_base pointer previously passed
as nand controller data or platform driver data. Subsequent patches
may populate the structure with more members as needed.

Signed-off-by: Janusz Krzysztofik <[email protected]>
Reviewed-by: Boris Brezillon <[email protected]>
---
drivers/mtd/nand/raw/ams-delta.c | 126 +++++++++++++++++++++------------------
1 file changed, 69 insertions(+), 57 deletions(-)

diff --git a/drivers/mtd/nand/raw/ams-delta.c b/drivers/mtd/nand/raw/ams-delta.c
index af313c620264..48233d638d2a 100644
--- a/drivers/mtd/nand/raw/ams-delta.c
+++ b/drivers/mtd/nand/raw/ams-delta.c
@@ -34,14 +34,18 @@
/*
* MTD structure for E3 (Delta)
*/
-static struct mtd_info *ams_delta_mtd = NULL;
-static struct gpio_desc *gpiod_rdy;
-static struct gpio_desc *gpiod_nce;
-static struct gpio_desc *gpiod_nre;
-static struct gpio_desc *gpiod_nwp;
-static struct gpio_desc *gpiod_nwe;
-static struct gpio_desc *gpiod_ale;
-static struct gpio_desc *gpiod_cle;
+
+struct ams_delta_nand {
+ struct nand_chip nand_chip;
+ struct gpio_desc *gpiod_rdy;
+ struct gpio_desc *gpiod_nce;
+ struct gpio_desc *gpiod_nre;
+ struct gpio_desc *gpiod_nwp;
+ struct gpio_desc *gpiod_nwe;
+ struct gpio_desc *gpiod_ale;
+ struct gpio_desc *gpiod_cle;
+ void __iomem *io_base;
+};

/*
* Define partitions for flash devices
@@ -71,26 +75,28 @@ static const struct mtd_partition partition_info[] = {
static void ams_delta_write_byte(struct mtd_info *mtd, u_char byte)
{
struct nand_chip *this = mtd_to_nand(mtd);
- void __iomem *io_base = (void __iomem *)nand_get_controller_data(this);
+ struct ams_delta_nand *priv = nand_get_controller_data(this);
+ void __iomem *io_base = priv->io_base;

writew(0, io_base + OMAP_MPUIO_IO_CNTL);
writew(byte, this->IO_ADDR_W);
- gpiod_set_value(gpiod_nwe, 0);
+ gpiod_set_value(priv->gpiod_nwe, 0);
ndelay(40);
- gpiod_set_value(gpiod_nwe, 1);
+ gpiod_set_value(priv->gpiod_nwe, 1);
}

static u_char ams_delta_read_byte(struct mtd_info *mtd)
{
u_char res;
struct nand_chip *this = mtd_to_nand(mtd);
- void __iomem *io_base = (void __iomem *)nand_get_controller_data(this);
+ struct ams_delta_nand *priv = nand_get_controller_data(this);
+ void __iomem *io_base = priv->io_base;

- gpiod_set_value(gpiod_nre, 0);
+ gpiod_set_value(priv->gpiod_nre, 0);
ndelay(40);
writew(~0, io_base + OMAP_MPUIO_IO_CNTL);
res = readw(this->IO_ADDR_R);
- gpiod_set_value(gpiod_nre, 1);
+ gpiod_set_value(priv->gpiod_nre, 1);

return res;
}
@@ -123,11 +129,13 @@ static void ams_delta_read_buf(struct mtd_info *mtd, u_char *buf, int len)
static void ams_delta_hwcontrol(struct mtd_info *mtd, int cmd,
unsigned int ctrl)
{
+ struct nand_chip *this = mtd_to_nand(mtd);
+ struct ams_delta_nand *priv = nand_get_controller_data(this);

if (ctrl & NAND_CTRL_CHANGE) {
- gpiod_set_value(gpiod_nce, !(ctrl & NAND_NCE));
- gpiod_set_value(gpiod_cle, !!(ctrl & NAND_CLE));
- gpiod_set_value(gpiod_ale, !!(ctrl & NAND_ALE));
+ gpiod_set_value(priv->gpiod_nce, !(ctrl & NAND_NCE));
+ gpiod_set_value(priv->gpiod_cle, !!(ctrl & NAND_CLE));
+ gpiod_set_value(priv->gpiod_ale, !!(ctrl & NAND_ALE));
}

if (cmd != NAND_CMD_NONE)
@@ -136,7 +144,10 @@ static void ams_delta_hwcontrol(struct mtd_info *mtd, int cmd,

static int ams_delta_nand_ready(struct mtd_info *mtd)
{
- return gpiod_get_value(gpiod_rdy);
+ struct nand_chip *this = mtd_to_nand(mtd);
+ struct ams_delta_nand *priv = nand_get_controller_data(this);
+
+ return gpiod_get_value(priv->gpiod_rdy);
}


@@ -145,7 +156,9 @@ static int ams_delta_nand_ready(struct mtd_info *mtd)
*/
static int ams_delta_init(struct platform_device *pdev)
{
+ struct ams_delta_nand *priv;
struct nand_chip *this;
+ struct mtd_info *mtd;
struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
void __iomem *io_base;
int err = 0;
@@ -154,15 +167,16 @@ static int ams_delta_init(struct platform_device *pdev)
return -ENXIO;

/* Allocate memory for MTD device structure and private data */
- this = kzalloc(sizeof(struct nand_chip), GFP_KERNEL);
- if (!this) {
+ priv = devm_kzalloc(&pdev->dev, sizeof(struct ams_delta_nand),
+ GFP_KERNEL);
+ if (!priv) {
pr_warn("Unable to allocate E3 NAND MTD device structure.\n");
- err = -ENOMEM;
- goto out;
+ return -ENOMEM;
}
+ this = &priv->nand_chip;

- ams_delta_mtd = nand_to_mtd(this);
- ams_delta_mtd->dev.parent = &pdev->dev;
+ mtd = nand_to_mtd(this);
+ mtd->dev.parent = &pdev->dev;

/*
* Don't try to request the memory region from here,
@@ -177,7 +191,8 @@ static int ams_delta_init(struct platform_device *pdev)
goto out_free;
}

- nand_set_controller_data(this, (void *)io_base);
+ priv->io_base = io_base;
+ nand_set_controller_data(this, priv);

/* Set address of NAND IO lines */
this->IO_ADDR_R = io_base + OMAP_MPUIO_INPUT_LATCH;
@@ -187,14 +202,14 @@ static int ams_delta_init(struct platform_device *pdev)
this->read_buf = ams_delta_read_buf;
this->cmd_ctrl = ams_delta_hwcontrol;

- gpiod_rdy = devm_gpiod_get_optional(&pdev->dev, "rdy", GPIOD_IN);
- if (IS_ERR(gpiod_rdy)) {
- err = PTR_ERR(gpiod_rdy);
+ priv->gpiod_rdy = devm_gpiod_get_optional(&pdev->dev, "rdy", GPIOD_IN);
+ if (IS_ERR(priv->gpiod_rdy)) {
+ err = PTR_ERR(priv->gpiod_rdy);
dev_warn(&pdev->dev, "RDY GPIO request failed (%d)\n", err);
goto out_mtd;
}

- if (gpiod_rdy)
+ if (priv->gpiod_rdy)
this->dev_ready = ams_delta_nand_ready;

/* 25 us command delay time */
@@ -202,66 +217,64 @@ static int ams_delta_init(struct platform_device *pdev)
this->ecc.mode = NAND_ECC_SOFT;
this->ecc.algo = NAND_ECC_HAMMING;

- platform_set_drvdata(pdev, io_base);
+ platform_set_drvdata(pdev, priv);

/* Set chip enabled, but */
- gpiod_nwp = devm_gpiod_get(&pdev->dev, "nwp", GPIOD_OUT_HIGH);
- if (IS_ERR(gpiod_nwp)) {
- err = PTR_ERR(gpiod_nwp);
+ priv->gpiod_nwp = devm_gpiod_get(&pdev->dev, "nwp", GPIOD_OUT_HIGH);
+ if (IS_ERR(priv->gpiod_nwp)) {
+ err = PTR_ERR(priv->gpiod_nwp);
dev_err(&pdev->dev, "NWP GPIO request failed (%d)\n", err);
goto out_mtd;
}

- gpiod_nce = devm_gpiod_get(&pdev->dev, "nce", GPIOD_OUT_HIGH);
- if (IS_ERR(gpiod_nce)) {
- err = PTR_ERR(gpiod_nce);
+ priv->gpiod_nce = devm_gpiod_get(&pdev->dev, "nce", GPIOD_OUT_HIGH);
+ if (IS_ERR(priv->gpiod_nce)) {
+ err = PTR_ERR(priv->gpiod_nce);
dev_err(&pdev->dev, "NCE GPIO request failed (%d)\n", err);
goto out_mtd;
}

- gpiod_nre = devm_gpiod_get(&pdev->dev, "nre", GPIOD_OUT_HIGH);
- if (IS_ERR(gpiod_nre)) {
- err = PTR_ERR(gpiod_nre);
+ priv->gpiod_nre = devm_gpiod_get(&pdev->dev, "nre", GPIOD_OUT_HIGH);
+ if (IS_ERR(priv->gpiod_nre)) {
+ err = PTR_ERR(priv->gpiod_nre);
dev_err(&pdev->dev, "NRE GPIO request failed (%d)\n", err);
goto out_mtd;
}

- gpiod_nwe = devm_gpiod_get(&pdev->dev, "nwe", GPIOD_OUT_HIGH);
- if (IS_ERR(gpiod_nwe)) {
- err = PTR_ERR(gpiod_nwe);
+ priv->gpiod_nwe = devm_gpiod_get(&pdev->dev, "nwe", GPIOD_OUT_HIGH);
+ if (IS_ERR(priv->gpiod_nwe)) {
+ err = PTR_ERR(priv->gpiod_nwe);
dev_err(&pdev->dev, "NWE GPIO request failed (%d)\n", err);
goto out_mtd;
}

- gpiod_ale = devm_gpiod_get(&pdev->dev, "ale", GPIOD_OUT_LOW);
- if (IS_ERR(gpiod_ale)) {
- err = PTR_ERR(gpiod_ale);
+ priv->gpiod_ale = devm_gpiod_get(&pdev->dev, "ale", GPIOD_OUT_LOW);
+ if (IS_ERR(priv->gpiod_ale)) {
+ err = PTR_ERR(priv->gpiod_ale);
dev_err(&pdev->dev, "ALE GPIO request failed (%d)\n", err);
goto out_mtd;
}

- gpiod_cle = devm_gpiod_get(&pdev->dev, "cle", GPIOD_OUT_LOW);
- if (IS_ERR(gpiod_cle)) {
- err = PTR_ERR(gpiod_cle);
+ priv->gpiod_cle = devm_gpiod_get(&pdev->dev, "cle", GPIOD_OUT_LOW);
+ if (IS_ERR(priv->gpiod_cle)) {
+ err = PTR_ERR(priv->gpiod_cle);
dev_err(&pdev->dev, "CLE GPIO request failed (%d)\n", err);
goto out_mtd;
}

/* Scan to find existence of the device */
- err = nand_scan(ams_delta_mtd, 1);
+ err = nand_scan(mtd, 1);
if (err)
goto out_mtd;

/* Register the partitions */
- mtd_device_register(ams_delta_mtd, partition_info,
- ARRAY_SIZE(partition_info));
+ mtd_device_register(mtd, partition_info, ARRAY_SIZE(partition_info));

goto out;

out_mtd:
iounmap(io_base);
out_free:
- kfree(this);
out:
return err;
}
@@ -271,16 +284,15 @@ static int ams_delta_init(struct platform_device *pdev)
*/
static int ams_delta_cleanup(struct platform_device *pdev)
{
- void __iomem *io_base = platform_get_drvdata(pdev);
+ struct ams_delta_nand *priv = platform_get_drvdata(pdev);
+ struct mtd_info *mtd = nand_to_mtd(&priv->nand_chip);
+ void __iomem *io_base = priv->io_base;

/* Release resources, unregister device */
- nand_release(ams_delta_mtd);
+ nand_release(mtd);

iounmap(io_base);

- /* Free the MTD device structure */
- kfree(mtd_to_nand(ams_delta_mtd));
-
return 0;
}

--
2.16.4


2018-08-20 21:39:56

by Janusz Krzysztofik

[permalink] [raw]
Subject: [PATCH v4 3/3] mtd: rawnand: ams-delta: Set port direction when needed

In its current shape, the driver sets data port direction before each
byte read/write operation, even during multi-byte transfers. Improve
performance of the driver by setting the port direction only when
needed.

This optimisation will become particularly important as soon as
planned conversion of the driver to GPIO API for data I/O will be
implemented.

Signed-off-by: Janusz Krzysztofik <[email protected]>
---
drivers/mtd/nand/raw/ams-delta.c | 68 ++++++++++++++++++++++++++--------------
1 file changed, 45 insertions(+), 23 deletions(-)

diff --git a/drivers/mtd/nand/raw/ams-delta.c b/drivers/mtd/nand/raw/ams-delta.c
index 48233d638d2a..4d5e5790f18e 100644
--- a/drivers/mtd/nand/raw/ams-delta.c
+++ b/drivers/mtd/nand/raw/ams-delta.c
@@ -45,6 +45,7 @@ struct ams_delta_nand {
struct gpio_desc *gpiod_ale;
struct gpio_desc *gpiod_cle;
void __iomem *io_base;
+ bool data_in;
};

/*
@@ -72,50 +73,65 @@ static const struct mtd_partition partition_info[] = {
.size = 3 * SZ_256K },
};

-static void ams_delta_write_byte(struct mtd_info *mtd, u_char byte)
+static void ams_delta_io_write(struct ams_delta_nand *priv, u8 byte)
{
- struct nand_chip *this = mtd_to_nand(mtd);
- struct ams_delta_nand *priv = nand_get_controller_data(this);
- void __iomem *io_base = priv->io_base;
-
- writew(0, io_base + OMAP_MPUIO_IO_CNTL);
- writew(byte, this->IO_ADDR_W);
+ writew(byte, priv->nand_chip.IO_ADDR_W);
gpiod_set_value(priv->gpiod_nwe, 0);
ndelay(40);
gpiod_set_value(priv->gpiod_nwe, 1);
}

-static u_char ams_delta_read_byte(struct mtd_info *mtd)
+static u8 ams_delta_io_read(struct ams_delta_nand *priv)
{
- u_char res;
- struct nand_chip *this = mtd_to_nand(mtd);
- struct ams_delta_nand *priv = nand_get_controller_data(this);
- void __iomem *io_base = priv->io_base;
+ u8 res;

gpiod_set_value(priv->gpiod_nre, 0);
ndelay(40);
- writew(~0, io_base + OMAP_MPUIO_IO_CNTL);
- res = readw(this->IO_ADDR_R);
+ res = readw(priv->nand_chip.IO_ADDR_R);
gpiod_set_value(priv->gpiod_nre, 1);

return res;
}

-static void ams_delta_write_buf(struct mtd_info *mtd, const u_char *buf,
- int len)
+static void ams_delta_dir_input(struct ams_delta_nand *priv, bool in)
{
+ writew(in ? ~0 : 0, priv->io_base + OMAP_MPUIO_IO_CNTL);
+ priv->data_in = in;
+}
+
+static void ams_delta_write_buf(struct mtd_info *mtd, const u8 *buf, int len)
+{
+ struct nand_chip *this = mtd_to_nand(mtd);
+ struct ams_delta_nand *priv = nand_get_controller_data(this);
int i;

- for (i=0; i<len; i++)
- ams_delta_write_byte(mtd, buf[i]);
+ if (priv->data_in)
+ ams_delta_dir_input(priv, false);
+
+ for (i = 0; i < len; i++)
+ ams_delta_io_write(priv, buf[i]);
}

-static void ams_delta_read_buf(struct mtd_info *mtd, u_char *buf, int len)
+static void ams_delta_read_buf(struct mtd_info *mtd, u8 *buf, int len)
{
+ struct nand_chip *this = mtd_to_nand(mtd);
+ struct ams_delta_nand *priv = nand_get_controller_data(this);
int i;

- for (i=0; i<len; i++)
- buf[i] = ams_delta_read_byte(mtd);
+ if (!priv->data_in)
+ ams_delta_dir_input(priv, true);
+
+ for (i = 0; i < len; i++)
+ buf[i] = ams_delta_io_read(priv);
+}
+
+static u8 ams_delta_read_byte(struct mtd_info *mtd)
+{
+ u8 res;
+
+ ams_delta_read_buf(mtd, &res, 1);
+
+ return res;
}

/*
@@ -138,8 +154,11 @@ static void ams_delta_hwcontrol(struct mtd_info *mtd, int cmd,
gpiod_set_value(priv->gpiod_ale, !!(ctrl & NAND_ALE));
}

- if (cmd != NAND_CMD_NONE)
- ams_delta_write_byte(mtd, cmd);
+ if (cmd != NAND_CMD_NONE) {
+ u8 byte = cmd;
+
+ ams_delta_write_buf(mtd, &byte, 1);
+ }
}

static int ams_delta_nand_ready(struct mtd_info *mtd)
@@ -262,6 +281,9 @@ static int ams_delta_init(struct platform_device *pdev)
goto out_mtd;
}

+ /* Initialize data port direction to a known state */
+ ams_delta_dir_input(priv, true);
+
/* Scan to find existence of the device */
err = nand_scan(mtd, 1);
if (err)
--
2.16.4


2018-08-20 23:44:15

by Janusz Krzysztofik

[permalink] [raw]
Subject: [RFC RFT PATCH 0/4] gpiolib: speed up GPIO array processing


This series is a follow up of the former "mtd: rawnand: ams-delta: Use
gpio-omap accessors for data I/O" which already contained some changes
to gpiolib. Those previous attempts were commented by Borris Brezillon
who suggested using GPIO API modified to accept bitmaps, and by Linus
Walleij who suggested still more great ideas for further immprovement
of the proposed API changes - thanks!

The goal is to boost performans of get/set array functions while
processing GPIO arrays which represent pins of a signle chip in
hardware order. If resulting performance is close to PIO, GPIO API
can be used for data I/O without much loss of speed.

Created and tested on a low end Amstrad Delta board with NAND driver
updated to use GPIO API for data I/O. Performance degrade compared to
PIO is much better than before the optimization but still not quite
satisfactory.


Janusz Krzysztofik (4):
gpiolib: Pass bitmaps, not integer arrays, to get/set array
gpiolib: Identify arrays matching GPIO hardware
gpiolib: Pass array info to get/set array functions
gpiolib: Implement fast processing path in get/set array


Documentation/driver-api/gpio/board.rst | 15 +
Documentation/driver-api/gpio/consumer.rst | 48 +++-
drivers/auxdisplay/hd44780.c | 64 +++---
drivers/bus/ts-nbus.c | 25 --
drivers/gpio/gpio-max3191x.c | 23 +-
drivers/gpio/gpiolib.c | 279 ++++++++++++++++++++++------
drivers/gpio/gpiolib.h | 15 +
drivers/i2c/muxes/i2c-mux-gpio.c | 5
drivers/mmc/core/pwrseq_simple.c | 15 -
drivers/mux/gpio.c | 7
drivers/net/phy/mdio-mux-gpio.c | 5
drivers/pcmcia/soc_common.c | 14 -
drivers/phy/motorola/phy-mapphone-mdm6600.c | 21 +-
drivers/staging/iio/adc/ad7606.c | 12 -
drivers/tty/serial/serial_mctrl_gpio.c | 9
include/linux/gpio/consumer.h | 35 ++-
16 files changed, 410 insertions(+), 182 deletions(-)


2018-08-20 23:44:24

by Janusz Krzysztofik

[permalink] [raw]
Subject: [RFC RFT PATCH v4 4/4] gpiolib: Implement fast processing path in get/set array

Certain GPIO descriptor arrays returned by gpio_get_array() may contain
information on direct mapping of array members to pins of a single GPIO
chip in hardware order. In such cases, bitmaps of values can be passed
directly from/to the chip's .get/set_multiple() callbacks without
wasting time on iterations.

Add respective code to gpiod_get/set_array_bitmap_complex() functions.
Pins not applicable for fast path are processed as before, skipping
over the 'fast' ones.

Signed-off-by: Janusz Krzysztofik <[email protected]>
---
Documentation/driver-api/gpio/board.rst | 15 ++++++
Documentation/driver-api/gpio/consumer.rst | 8 +++
drivers/gpio/gpiolib.c | 87 ++++++++++++++++++++++++++++--
3 files changed, 105 insertions(+), 5 deletions(-)

diff --git a/Documentation/driver-api/gpio/board.rst b/Documentation/driver-api/gpio/board.rst
index 2c112553df84..c66821e033c2 100644
--- a/Documentation/driver-api/gpio/board.rst
+++ b/Documentation/driver-api/gpio/board.rst
@@ -193,3 +193,18 @@ And the table can be added to the board code as follows::

The line will be hogged as soon as the gpiochip is created or - in case the
chip was created earlier - when the hog table is registered.
+
+Arrays of pins
+--------------
+In addition to requesting pins belonging to a function one by one, a device may
+also request an array of pins assigned to the function. The way those pins are
+mapped to the device determines if the array qualifies for fast bitmap
+processing. If yes, a bitmap is passed over get/set array functions directly
+between a caller and a respective .get/set_multiple() callback of a GPIO chip.
+
+In order to qualify for fast bitmap processing, the pin mapping must meet the
+following requirements:
+- it must belong to the same chip as other 'fast' pins of the function,
+- its index within the function must match its hardware number within the chip.
+
+Open drain and open source pins are excluded from fast bitmap output processing.
diff --git a/Documentation/driver-api/gpio/consumer.rst b/Documentation/driver-api/gpio/consumer.rst
index 0afd95a12b10..cf992e5ab976 100644
--- a/Documentation/driver-api/gpio/consumer.rst
+++ b/Documentation/driver-api/gpio/consumer.rst
@@ -388,6 +388,14 @@ array_info should be set to NULL.
Note that for optimal performance GPIOs belonging to the same chip should be
contiguous within the array of descriptors.

+Still better performance may be achieved if array indexes of the descriptors
+match hardware pin numbers of a single chip. If an array passed to a get/set
+array function matches the one obtained from gpiod_get_array() and array_info
+associated with the array is also passed, the function may take a fast bitmap
+processing path, passing the value_bitmap argument directly to the respective
+.get/set_multiple() callback of the chip. That allows for utilization of GPIO
+banks as data I/O ports without much loss of performance.
+
The return value of gpiod_get_array_value() and its variants is 0 on success
or negative on error. Note the difference to gpiod_get_value(), which returns
0 or 1 on success to convey the GPIO value. With the array functions, the GPIO
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index 4d26cdbdb7cf..b799a89c4c17 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -2787,7 +2787,36 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep,
struct gpio_array *array_info,
unsigned long *value_bitmap)
{
- int i = 0;
+ int err, i = 0;
+
+ /*
+ * Validate array_info against desc_array and its size.
+ * It should immediately follow desc_array if both
+ * have been obtained from the same gpiod_get_array() call.
+ */
+ if (array_info && array_info->desc == desc_array &&
+ array_size <= array_info->size &&
+ (void *)array_info == desc_array + array_info->size) {
+ if (!can_sleep)
+ WARN_ON(array_info->chip->can_sleep);
+
+ err = gpio_chip_get_multiple(array_info->chip,
+ array_info->get_mask,
+ value_bitmap);
+ if (err)
+ return err;
+
+ if (!raw && !bitmap_empty(array_info->invert_mask, array_size))
+ bitmap_xor(value_bitmap, value_bitmap,
+ array_info->invert_mask, array_size);
+
+ if (bitmap_full(array_info->get_mask, array_size))
+ return 0;
+
+ i = find_first_zero_bit(array_info->get_mask, array_size);
+ } else {
+ array_info = NULL;
+ }

while (i < array_size) {
struct gpio_chip *chip = desc_array[i]->gdev->chip;
@@ -2818,7 +2847,12 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep,
int hwgpio = gpio_chip_hwgpio(desc);

__set_bit(hwgpio, mask);
- i++;
+
+ if (array_info)
+ find_next_zero_bit(array_info->get_mask,
+ array_size, i);
+ else
+ i++;
} while ((i < array_size) &&
(desc_array[i]->gdev->chip == chip));

@@ -2829,7 +2863,7 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep,
return ret;
}

- for (j = first; j < i; j++) {
+ for (j = first; j < i; ) {
const struct gpio_desc *desc = desc_array[j];
int hwgpio = gpio_chip_hwgpio(desc);
int value = test_bit(hwgpio, bits);
@@ -2838,6 +2872,11 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep,
value = !value;
__assign_bit(j, value_bitmap, value);
trace_gpio_value(desc_to_gpio(desc), 1, value);
+
+ if (array_info)
+ find_next_zero_bit(array_info->get_mask, i, j);
+ else
+ j++;
}

if (mask != fastpath)
@@ -3039,6 +3078,32 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep,
{
int i = 0;

+ /*
+ * Validate array_info against desc_array and its size.
+ * It should immediately follow desc_array if both
+ * have been obtained from the same gpiod_get_array() call.
+ */
+ if (array_info && array_info->desc == desc_array &&
+ array_size <= array_info->size &&
+ (void *)array_info == desc_array + array_info->size) {
+ if (!can_sleep)
+ WARN_ON(array_info->chip->can_sleep);
+
+ if (!raw && !bitmap_empty(array_info->invert_mask, array_size))
+ bitmap_xor(value_bitmap, value_bitmap,
+ array_info->invert_mask, array_size);
+
+ gpio_chip_set_multiple(array_info->chip, array_info->set_mask,
+ value_bitmap);
+
+ if (bitmap_full(array_info->set_mask, array_size))
+ return 0;
+
+ i = find_first_zero_bit(array_info->set_mask, array_size);
+ } else {
+ array_info = NULL;
+ }
+
while (i < array_size) {
struct gpio_chip *chip = desc_array[i]->gdev->chip;
unsigned long fastpath[2 * BITS_TO_LONGS(FASTPATH_NGPIO)];
@@ -3066,7 +3131,14 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep,
int hwgpio = gpio_chip_hwgpio(desc);
int value = test_bit(i, value_bitmap);

- if (!raw && test_bit(FLAG_ACTIVE_LOW, &desc->flags))
+ /*
+ * Pins applicable for fast input but not for
+ * fast output processing may have been already
+ * inverted inside the fast path, skip them.
+ */
+ if (!raw && !(array_info &&
+ test_bit(i, array_info->invert_mask)) &&
+ test_bit(FLAG_ACTIVE_LOW, &desc->flags))
value = !value;
trace_gpio_value(desc_to_gpio(desc), 0, value);
/*
@@ -3085,7 +3157,12 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep,
__clear_bit(hwgpio, bits);
count++;
}
- i++;
+
+ if (array_info)
+ find_next_zero_bit(array_info->set_mask,
+ array_size, i);
+ else
+ i++;
} while ((i < array_size) &&
(desc_array[i]->gdev->chip == chip));
/* push collected bits to outputs */
--
2.16.4


2018-08-20 23:44:49

by Janusz Krzysztofik

[permalink] [raw]
Subject: [RFC RFT PATCH v4 3/4] gpiolib: Pass array info to get/set array functions

In order to make use of array info obtained from gpiod_get_array() and
speed up processing of arrays matching single GPIO chip layout, that
information must be passed to get/set array functions. Extend the
functions' API with that additional parameter and update all users.
Pass NULL if a user bulids an array itself from single GPIOs.

Signed-off-by: Janusz Krzysztofik <[email protected]>
---
Documentation/driver-api/gpio/consumer.rst | 14 ++++++++++--
drivers/auxdisplay/hd44780.c | 12 ++++++----
drivers/bus/ts-nbus.c | 6 +++--
drivers/gpio/gpio-max3191x.c | 6 +++--
drivers/gpio/gpiolib.c | 34 ++++++++++++++++++++---------
drivers/gpio/gpiolib.h | 2 ++
drivers/i2c/muxes/i2c-mux-gpio.c | 2 +-
drivers/mmc/core/pwrseq_simple.c | 2 +-
drivers/mux/gpio.c | 3 ++-
drivers/net/phy/mdio-mux-gpio.c | 2 +-
drivers/pcmcia/soc_common.c | 3 ++-
drivers/phy/motorola/phy-mapphone-mdm6600.c | 4 +++-
drivers/staging/iio/adc/ad7606.c | 3 ++-
drivers/tty/serial/serial_mctrl_gpio.c | 2 +-
include/linux/gpio/consumer.h | 8 +++++++
15 files changed, 75 insertions(+), 28 deletions(-)

diff --git a/Documentation/driver-api/gpio/consumer.rst b/Documentation/driver-api/gpio/consumer.rst
index 7e0298b9a7b9..0afd95a12b10 100644
--- a/Documentation/driver-api/gpio/consumer.rst
+++ b/Documentation/driver-api/gpio/consumer.rst
@@ -325,28 +325,36 @@ The following functions get or set the values of an array of GPIOs::

int gpiod_get_array_value(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap);
int gpiod_get_raw_array_value(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap);
int gpiod_get_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap);
int gpiod_get_raw_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap);

void gpiod_set_array_value(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap)
void gpiod_set_raw_array_value(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap)
void gpiod_set_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap)
void gpiod_set_raw_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap)

The array can be an arbitrary set of GPIOs. The functions will try to access
@@ -358,6 +366,7 @@ accessed sequentially.
The functions take three arguments:
* array_size - the number of array elements
* desc_array - an array of GPIO descriptors
+ * array_info - optional information obtained from gpiod_array_get()
* value_bitmap - a bitmap to store the GPIOs' values (get) or
a bitmap of values to assign to the GPIOs (set)

@@ -368,12 +377,13 @@ the struct gpio_descs returned by gpiod_get_array()::

struct gpio_descs *my_gpio_descs = gpiod_get_array(...);
gpiod_set_array_value(my_gpio_descs->ndescs, my_gpio_descs->desc,
- my_gpio_value_bitmap);
+ my_gpio_descs->info, my_gpio_value_bitmap);

It is also possible to access a completely arbitrary array of descriptors. The
descriptors may be obtained using any combination of gpiod_get() and
gpiod_get_array(). Afterwards the array of descriptors has to be setup
-manually before it can be passed to one of the above functions.
+manually before it can be passed to one of the above functions. In that case,
+array_info should be set to NULL.

Note that for optimal performance GPIOs belonging to the same chip should be
contiguous within the array of descriptors.
diff --git a/drivers/auxdisplay/hd44780.c b/drivers/auxdisplay/hd44780.c
index d340473aa142..6ae81632bc44 100644
--- a/drivers/auxdisplay/hd44780.c
+++ b/drivers/auxdisplay/hd44780.c
@@ -74,7 +74,8 @@ static void hd44780_write_gpio8(struct hd44780 *hd, u8 val, unsigned int rs)
}

/* Present the data to the port */
- gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA0], value_bitmap);
+ gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA0], NULL,
+ value_bitmap);

hd44780_strobe_gpio(hd);
}
@@ -97,7 +98,8 @@ static void hd44780_write_gpio4(struct hd44780 *hd, u8 val, unsigned int rs)
value_bitmap[0] = value_bitmap[0] >> PIN_DATA4;

/* Present the data to the port */
- gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA4], value_bitmap);
+ gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA4], NULL,
+ value_bitmap);

hd44780_strobe_gpio(hd);

@@ -106,7 +108,8 @@ static void hd44780_write_gpio4(struct hd44780 *hd, u8 val, unsigned int rs)
value_bitmap[0] |= val & ~((1 << PIN_DATA4) - 1);

/* Present the data to the port */
- gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA4], value_bitmap);
+ gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA4], NULL,
+ value_bitmap);

hd44780_strobe_gpio(hd);
}
@@ -169,7 +172,8 @@ static void hd44780_write_cmd_raw_gpio4(struct charlcd *lcd, int cmd)
value_bitmap[0] = value_bitmap[0] >> PIN_DATA4;

/* Present the data to the port */
- gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA4], value_bitmap);
+ gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA4], NULL,
+ value_bitmap);

hd44780_strobe_gpio(hd);
}
diff --git a/drivers/bus/ts-nbus.c b/drivers/bus/ts-nbus.c
index ce6c1e89236d..000d756eb42c 100644
--- a/drivers/bus/ts-nbus.c
+++ b/drivers/bus/ts-nbus.c
@@ -112,7 +112,8 @@ static void ts_nbus_reset_bus(struct ts_nbus *ts_nbus)
{
unsigned long value_bitmap[1] = { 0, };

- gpiod_set_array_value_cansleep(8, ts_nbus->data->desc, value_bitmap);
+ gpiod_set_array_value_cansleep(8, ts_nbus->data->desc,
+ ts_nbus->data->info, value_bitmap);
gpiod_set_value_cansleep(ts_nbus->csn, 0);
gpiod_set_value_cansleep(ts_nbus->strobe, 0);
gpiod_set_value_cansleep(ts_nbus->ale, 0);
@@ -155,7 +156,8 @@ static void ts_nbus_write_byte(struct ts_nbus *ts_nbus, u8 byte)
struct gpio_descs *gpios = ts_nbus->data;
unsigned long value_bitmap[1] = { byte, };

- gpiod_set_array_value_cansleep(8, gpios->desc, value_bitmap);
+ gpiod_set_array_value_cansleep(8, gpios->desc, gpios->info,
+ value_bitmap);
}

/*
diff --git a/drivers/gpio/gpio-max3191x.c b/drivers/gpio/gpio-max3191x.c
index c4ec1c82af27..4b43b5dabfd2 100644
--- a/drivers/gpio/gpio-max3191x.c
+++ b/drivers/gpio/gpio-max3191x.c
@@ -313,6 +313,7 @@ static int max3191x_set_config(struct gpio_chip *gpio, unsigned int offset,

static void gpiod_set_array_single_value_cansleep(unsigned int ndescs,
struct gpio_desc **desc,
+ struct gpio_array *info,
int value)
{
unsigned long *value_bitmap;
@@ -327,7 +328,7 @@ static void gpiod_set_array_single_value_cansleep(unsigned int ndescs,
else
bitmap_zero(value_bitmap, ndescs);

- gpiod_set_array_value_cansleep(ndescs, desc, value_bitmap);
+ gpiod_set_array_value_cansleep(ndescs, desc, info, value_bitmap);
kfree(value_bitmap);
}

@@ -400,7 +401,8 @@ static int max3191x_probe(struct spi_device *spi)
if (max3191x->modesel_pins)
gpiod_set_array_single_value_cansleep(
max3191x->modesel_pins->ndescs,
- max3191x->modesel_pins->desc, max3191x->mode);
+ max3191x->modesel_pins->desc,
+ max3191x->modesel_pins->info, max3191x->mode);

max3191x->ignore_uv = device_property_read_bool(dev,
"maxim,ignore-undervoltage");
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index c1ed1c759345..4d26cdbdb7cf 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -435,7 +435,7 @@ static long linehandle_ioctl(struct file *filep, unsigned int cmd,
int ret = gpiod_get_array_value_complex(false,
true,
lh->numdescs,
- lh->descs,
+ lh->descs, NULL,
value_bitmap);
if (ret)
return ret;
@@ -467,7 +467,7 @@ static long linehandle_ioctl(struct file *filep, unsigned int cmd,
return gpiod_set_array_value_complex(false,
true,
lh->numdescs,
- lh->descs,
+ lh->descs, NULL,
value_bitmap);
}
return -EINVAL;
@@ -2784,6 +2784,7 @@ static int gpio_chip_get_multiple(struct gpio_chip *chip,
int gpiod_get_array_value_complex(bool raw, bool can_sleep,
unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap)
{
int i = 0;
@@ -2908,12 +2909,14 @@ EXPORT_SYMBOL_GPL(gpiod_get_value);
*/
int gpiod_get_raw_array_value(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap)
{
if (!desc_array)
return -EINVAL;
return gpiod_get_array_value_complex(true, false, array_size,
- desc_array, value_bitmap);
+ desc_array, array_info,
+ value_bitmap);
}
EXPORT_SYMBOL_GPL(gpiod_get_raw_array_value);

@@ -2931,12 +2934,14 @@ EXPORT_SYMBOL_GPL(gpiod_get_raw_array_value);
*/
int gpiod_get_array_value(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap)
{
if (!desc_array)
return -EINVAL;
return gpiod_get_array_value_complex(false, false, array_size,
- desc_array, value_bitmap);
+ desc_array, array_info,
+ value_bitmap);
}
EXPORT_SYMBOL_GPL(gpiod_get_array_value);

@@ -3029,6 +3034,7 @@ static void gpio_chip_set_multiple(struct gpio_chip *chip,
int gpiod_set_array_value_complex(bool raw, bool can_sleep,
unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap)
{
int i = 0;
@@ -3166,12 +3172,13 @@ EXPORT_SYMBOL_GPL(gpiod_set_value);
*/
int gpiod_set_raw_array_value(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap)
{
if (!desc_array)
return -EINVAL;
return gpiod_set_array_value_complex(true, false, array_size,
- desc_array, value_bitmap);
+ desc_array, array_info, value_bitmap);
}
EXPORT_SYMBOL_GPL(gpiod_set_raw_array_value);

@@ -3189,12 +3196,13 @@ EXPORT_SYMBOL_GPL(gpiod_set_raw_array_value);
*/
void gpiod_set_array_value(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap)
{
if (!desc_array)
return;
gpiod_set_array_value_complex(false, false, array_size, desc_array,
- value_bitmap);
+ array_info, value_bitmap);
}
EXPORT_SYMBOL_GPL(gpiod_set_array_value);

@@ -3426,13 +3434,15 @@ EXPORT_SYMBOL_GPL(gpiod_get_value_cansleep);
*/
int gpiod_get_raw_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap)
{
might_sleep_if(extra_checks);
if (!desc_array)
return -EINVAL;
return gpiod_get_array_value_complex(true, true, array_size,
- desc_array, value_bitmap);
+ desc_array, array_info,
+ value_bitmap);
}
EXPORT_SYMBOL_GPL(gpiod_get_raw_array_value_cansleep);

@@ -3449,13 +3459,15 @@ EXPORT_SYMBOL_GPL(gpiod_get_raw_array_value_cansleep);
*/
int gpiod_get_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap)
{
might_sleep_if(extra_checks);
if (!desc_array)
return -EINVAL;
return gpiod_get_array_value_complex(false, true, array_size,
- desc_array, value_bitmap);
+ desc_array, array_info,
+ value_bitmap);
}
EXPORT_SYMBOL_GPL(gpiod_get_array_value_cansleep);

@@ -3508,13 +3520,14 @@ EXPORT_SYMBOL_GPL(gpiod_set_value_cansleep);
*/
int gpiod_set_raw_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap)
{
might_sleep_if(extra_checks);
if (!desc_array)
return -EINVAL;
return gpiod_set_array_value_complex(true, true, array_size, desc_array,
- value_bitmap);
+ array_info, value_bitmap);
}
EXPORT_SYMBOL_GPL(gpiod_set_raw_array_value_cansleep);

@@ -3548,13 +3561,14 @@ void gpiod_add_lookup_tables(struct gpiod_lookup_table **tables, size_t n)
*/
void gpiod_set_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap)
{
might_sleep_if(extra_checks);
if (!desc_array)
return;
gpiod_set_array_value_complex(false, true, array_size, desc_array,
- value_bitmap);
+ array_info, value_bitmap);
}
EXPORT_SYMBOL_GPL(gpiod_set_array_value_cansleep);

diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h
index b60905d558b1..b65ca896b24d 100644
--- a/drivers/gpio/gpiolib.h
+++ b/drivers/gpio/gpiolib.h
@@ -196,10 +196,12 @@ struct gpio_desc *gpiochip_get_desc(struct gpio_chip *chip, u16 hwnum);
int gpiod_get_array_value_complex(bool raw, bool can_sleep,
unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap);
int gpiod_set_array_value_complex(bool raw, bool can_sleep,
unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap);

/* This is just passed between gpiolib and devres */
diff --git a/drivers/i2c/muxes/i2c-mux-gpio.c b/drivers/i2c/muxes/i2c-mux-gpio.c
index d675e0ca2fa4..aa3857ab42b5 100644
--- a/drivers/i2c/muxes/i2c-mux-gpio.c
+++ b/drivers/i2c/muxes/i2c-mux-gpio.c
@@ -34,7 +34,7 @@ static void i2c_mux_gpio_set(const struct gpiomux *mux, unsigned val)
mux->values[i] = (val >> i) & 1;

gpiod_set_array_value_cansleep(mux->data.n_gpios,
- mux->gpios, value_bitmap);
+ mux->gpios, NULL, value_bitmap);
}

static int i2c_mux_gpio_select(struct i2c_mux_core *muxc, u32 chan)
diff --git a/drivers/mmc/core/pwrseq_simple.c b/drivers/mmc/core/pwrseq_simple.c
index 0d6e3a5be3ba..5cf7eda8f68f 100644
--- a/drivers/mmc/core/pwrseq_simple.c
+++ b/drivers/mmc/core/pwrseq_simple.c
@@ -46,7 +46,7 @@ static void mmc_pwrseq_simple_set_gpios_value(struct mmc_pwrseq_simple *pwrseq,
value_bitmap[0] = value;

gpiod_set_array_value_cansleep(nvalues, reset_gpios->desc,
- value_bitmap);
+ reset_gpios->info, value_bitmap);
}
}

diff --git a/drivers/mux/gpio.c b/drivers/mux/gpio.c
index cc2d5f50472a..879f9f3f45dd 100644
--- a/drivers/mux/gpio.c
+++ b/drivers/mux/gpio.c
@@ -30,7 +30,8 @@ static int mux_gpio_set(struct mux_control *mux, int state)
mux_gpio->val[i] = (state >> i) & 1;

gpiod_set_array_value_cansleep(mux_gpio->gpios->ndescs,
- mux_gpio->gpios->desc, value_bitmap);
+ mux_gpio->gpios->desc,
+ mux_gpio->gpios->info, value_bitmap);

return 0;
}
diff --git a/drivers/net/phy/mdio-mux-gpio.c b/drivers/net/phy/mdio-mux-gpio.c
index 8e1ec750277e..c0ffa03c916b 100644
--- a/drivers/net/phy/mdio-mux-gpio.c
+++ b/drivers/net/phy/mdio-mux-gpio.c
@@ -37,7 +37,7 @@ static int mdio_mux_gpio_switch_fn(int current_child, int desired_child,
s->values[n] = (desired_child >> n) & 1;

gpiod_set_array_value_cansleep(s->gpios->ndescs, s->gpios->desc,
- value_bitmap);
+ s->gpios->info, value_bitmap);

return 0;
}
diff --git a/drivers/pcmcia/soc_common.c b/drivers/pcmcia/soc_common.c
index e0f89155c474..55978198cd2b 100644
--- a/drivers/pcmcia/soc_common.c
+++ b/drivers/pcmcia/soc_common.c
@@ -366,7 +366,8 @@ static int soc_common_pcmcia_config_skt(
}

if (n)
- gpiod_set_array_value_cansleep(n, descs, value_bitmap);
+ gpiod_set_array_value_cansleep(n, descs, NULL,
+ value_bitmap);

/*
* This really needs a better solution. The IRQ
diff --git a/drivers/phy/motorola/phy-mapphone-mdm6600.c b/drivers/phy/motorola/phy-mapphone-mdm6600.c
index b6477c3599c4..8f508338ec56 100644
--- a/drivers/phy/motorola/phy-mapphone-mdm6600.c
+++ b/drivers/phy/motorola/phy-mapphone-mdm6600.c
@@ -162,7 +162,8 @@ static void phy_mdm6600_cmd(struct phy_mdm6600 *ddata, int val)
value_bitmap[0] = val & ((1 << PHY_MDM6600_NR_CMD_LINES) - 1);

gpiod_set_array_value_cansleep(PHY_MDM6600_NR_CMD_LINES,
- ddata->cmd_gpios->desc, value_bitmap);
+ ddata->cmd_gpios->desc,
+ ddata->cmd_gpios->info, value_bitmap);
}

/**
@@ -181,6 +182,7 @@ static void phy_mdm6600_status(struct work_struct *work)

error = gpiod_get_array_value_cansleep(PHY_MDM6600_NR_STATUS_LINES,
ddata->status_gpios->desc,
+ ddata->status_gpios->info,
value_bitmap);
if (error)
return;
diff --git a/drivers/staging/iio/adc/ad7606.c b/drivers/staging/iio/adc/ad7606.c
index 0eca047bc1cc..eb779d825724 100644
--- a/drivers/staging/iio/adc/ad7606.c
+++ b/drivers/staging/iio/adc/ad7606.c
@@ -230,7 +230,8 @@ static int ad7606_write_raw(struct iio_dev *indio_dev,
value_bitmap[0] = ret;

mutex_lock(&st->lock);
- gpiod_set_array_value(3, st->gpio_os->desc, value_bitmap);
+ gpiod_set_array_value(3, st->gpio_os->desc, st->gpio_os->info,
+ value_bitmap);
st->oversampling = val;
mutex_unlock(&st->lock);

diff --git a/drivers/tty/serial/serial_mctrl_gpio.c b/drivers/tty/serial/serial_mctrl_gpio.c
index bb8b4756d72d..8a04e3be5419 100644
--- a/drivers/tty/serial/serial_mctrl_gpio.c
+++ b/drivers/tty/serial/serial_mctrl_gpio.c
@@ -53,7 +53,7 @@ void mctrl_gpio_set(struct mctrl_gpios *gpios, unsigned int mctrl)
!!(mctrl & mctrl_gpios_desc[i].mctrl));
count++;
}
- gpiod_set_array_value(count, desc_array, value_bitmap);
+ gpiod_set_array_value(count, desc_array, NULL, value_bitmap);
}
EXPORT_SYMBOL_GPL(mctrl_gpio_set);

diff --git a/include/linux/gpio/consumer.h b/include/linux/gpio/consumer.h
index 8dede3e886af..bf037ebe2ed8 100644
--- a/include/linux/gpio/consumer.h
+++ b/include/linux/gpio/consumer.h
@@ -114,36 +114,44 @@ int gpiod_direction_output_raw(struct gpio_desc *desc, int value);
int gpiod_get_value(const struct gpio_desc *desc);
int gpiod_get_array_value(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap);
void gpiod_set_value(struct gpio_desc *desc, int value);
void gpiod_set_array_value(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap);
int gpiod_get_raw_value(const struct gpio_desc *desc);
int gpiod_get_raw_array_value(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap);
void gpiod_set_raw_value(struct gpio_desc *desc, int value);
int gpiod_set_raw_array_value(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap);

/* Value get/set from sleeping context */
int gpiod_get_value_cansleep(const struct gpio_desc *desc);
int gpiod_get_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap);
void gpiod_set_value_cansleep(struct gpio_desc *desc, int value);
void gpiod_set_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap);
int gpiod_get_raw_value_cansleep(const struct gpio_desc *desc);
int gpiod_get_raw_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap);
void gpiod_set_raw_value_cansleep(struct gpio_desc *desc, int value);
int gpiod_set_raw_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap);

int gpiod_set_debounce(struct gpio_desc *desc, unsigned debounce);
--
2.16.4


2018-08-20 23:45:16

by Janusz Krzysztofik

[permalink] [raw]
Subject: [RFC RFT PATCH v4 2/4] gpiolib: Identify arrays matching GPIO hardware

Certain GPIO array lookup results may map directly to GPIO pins of a
single GPIO chip in hardware order. If that condition is recognized
and handled efficiently, significant performance gain of get/set array
functions may be possible.

While processing a request for an array of GPIO descriptors, identify
those which represent corresponding pins of a single GPIO chip. Skip
over pins which require open source or open drain special processing.
Moreover, identify pins which require inversion. Pass a pointer to
that information with the array to the caller so it can benefit from
enhanced performance as soon as get/set array functions can accept and
make efficient use of it.

Signed-off-by: Janusz Krzysztofik <[email protected]>
---
Documentation/driver-api/gpio/consumer.rst | 4 +-
drivers/gpio/gpiolib.c | 72 +++++++++++++++++++++++++++++-
drivers/gpio/gpiolib.h | 9 ++++
include/linux/gpio/consumer.h | 9 ++++
4 files changed, 92 insertions(+), 2 deletions(-)

diff --git a/Documentation/driver-api/gpio/consumer.rst b/Documentation/driver-api/gpio/consumer.rst
index ed68042ddccf..7e0298b9a7b9 100644
--- a/Documentation/driver-api/gpio/consumer.rst
+++ b/Documentation/driver-api/gpio/consumer.rst
@@ -109,9 +109,11 @@ For a function using multiple GPIOs all of those can be obtained with one call::
enum gpiod_flags flags)

This function returns a struct gpio_descs which contains an array of
-descriptors::
+descriptors. It also contains a pointer to a gpiolib private structure which,
+if passed back to get/set array functions, may speed up I/O proocessing::

struct gpio_descs {
+ struct gpio_array *info;
unsigned int ndescs;
struct gpio_desc *desc[];
}
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index f0e9ffa8cab6..c1ed1c759345 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -4174,7 +4174,9 @@ struct gpio_descs *__must_check gpiod_get_array(struct device *dev,
{
struct gpio_desc *desc;
struct gpio_descs *descs;
- int count;
+ struct gpio_array *array_info = NULL;
+ struct gpio_chip *chip;
+ int count, bitmap_size;

count = gpiod_count(dev, con_id);
if (count < 0)
@@ -4190,9 +4192,77 @@ struct gpio_descs *__must_check gpiod_get_array(struct device *dev,
gpiod_put_array(descs);
return ERR_CAST(desc);
}
+
descs->desc[descs->ndescs] = desc;
+
+ chip = gpiod_to_chip(desc);
+ /*
+ * Select a chip of first array member
+ * whose index matches its pin hardware number
+ * as a candidate for fast bitmap processing.
+ */
+ if (!array_info && gpio_chip_hwgpio(desc) == descs->ndescs) {
+ struct gpio_descs *array;
+
+ bitmap_size = BITS_TO_LONGS(chip->ngpio > count ?
+ chip->ngpio : count);
+
+ array = kzalloc(struct_size(descs, desc, count) +
+ struct_size(array_info, invert_mask,
+ 3 * bitmap_size), GFP_KERNEL);
+ if (!array) {
+ gpiod_put_array(descs);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ memcpy(array, descs,
+ struct_size(descs, desc, descs->ndescs + 1));
+ kfree(descs);
+
+ descs = array;
+ array_info = (void *)(descs->desc + count);
+ array_info->get_mask = array_info->invert_mask +
+ bitmap_size;
+ array_info->set_mask = array_info->get_mask +
+ bitmap_size;
+
+ array_info->desc = descs->desc;
+ array_info->size = count;
+ array_info->chip = chip;
+ bitmap_set(array_info->get_mask, descs->ndescs,
+ count - descs->ndescs);
+ bitmap_set(array_info->set_mask, descs->ndescs,
+ count - descs->ndescs);
+ descs->info = array_info;
+ }
+ /*
+ * Unmark members which don't qualify for fast bitmap
+ * processing (different chip, not in hardware order)
+ */
+ if (array_info && (chip != array_info->chip ||
+ gpio_chip_hwgpio(desc) != descs->ndescs)) {
+ __clear_bit(descs->ndescs, array_info->get_mask);
+ __clear_bit(descs->ndescs, array_info->set_mask);
+ } else if (array_info) {
+ /* Exclude open drain or open source from fast output */
+ if (gpiochip_line_is_open_drain(chip, descs->ndescs) ||
+ gpiochip_line_is_open_source(chip, descs->ndescs))
+ __clear_bit(descs->ndescs,
+ array_info->set_mask);
+ /* Identify 'fast' pins which require invertion */
+ if (gpiod_is_active_low(desc))
+ __set_bit(descs->ndescs,
+ array_info->invert_mask);
+ }
+
descs->ndescs++;
}
+ if (array_info)
+ dev_dbg(dev,
+ "GPIO array info: chip=%s, size=%d, get_mask=%lx, set_mask=%lx, invert_mask=%lx\n",
+ array_info->chip->label, array_info->size,
+ *array_info->get_mask, *array_info->set_mask,
+ *array_info->invert_mask);
return descs;
}
EXPORT_SYMBOL_GPL(gpiod_get_array);
diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h
index 11e83d2eef89..b60905d558b1 100644
--- a/drivers/gpio/gpiolib.h
+++ b/drivers/gpio/gpiolib.h
@@ -183,6 +183,15 @@ static inline bool acpi_can_fallback_to_crs(struct acpi_device *adev,
}
#endif

+struct gpio_array {
+ struct gpio_desc **desc;
+ unsigned int size;
+ struct gpio_chip *chip;
+ unsigned long *get_mask;
+ unsigned long *set_mask;
+ unsigned long invert_mask[];
+};
+
struct gpio_desc *gpiochip_get_desc(struct gpio_chip *chip, u16 hwnum);
int gpiod_get_array_value_complex(bool raw, bool can_sleep,
unsigned int array_size,
diff --git a/include/linux/gpio/consumer.h b/include/linux/gpio/consumer.h
index 1b21dc7b0fad..8dede3e886af 100644
--- a/include/linux/gpio/consumer.h
+++ b/include/linux/gpio/consumer.h
@@ -17,11 +17,20 @@ struct device;
*/
struct gpio_desc;

+/**
+ * Opaque descriptor for a structure of GPIO array attributes. This structure
+ * is attached to struct gpiod_descs obtained from gpiod_get_array() and can be
+ * passed back to get/set array functions in order to activate fast processing
+ * path if applicable.
+ */
+struct gpio_array;
+
/**
* Struct containing an array of descriptors that can be obtained using
* gpiod_get_array().
*/
struct gpio_descs {
+ struct gpio_array *info;
unsigned int ndescs;
struct gpio_desc *desc[];
};
--
2.16.4


2018-08-20 23:45:31

by Janusz Krzysztofik

[permalink] [raw]
Subject: [RFC RFT PATCH v4 1/4] gpiolib: Pass bitmaps, not integer arrays, to get/set array

Most users of get/set array functions iterate consecutive bits of data,
usually a single integer, while or processing array of results obtained
from or building an array of values to be passed to those functions.
Save time wasted on those iterations by changing the functions' API to
accept bitmaps.

All current users are updated as well.

More benefits from the change are expected as soon as planned support
for accepting/passing those bitmaps directly from/to respective GPIO
chip callbacks if applicable is implemented.

Signed-off-by: Janusz Krzysztofik <[email protected]>
---
Documentation/driver-api/gpio/consumer.rst | 22 ++++----
drivers/auxdisplay/hd44780.c | 52 +++++++++--------
drivers/bus/ts-nbus.c | 19 ++-----
drivers/gpio/gpio-max3191x.c | 17 +++---
drivers/gpio/gpiolib.c | 86 +++++++++++++++--------------
drivers/gpio/gpiolib.h | 4 +-
drivers/i2c/muxes/i2c-mux-gpio.c | 3 +-
drivers/mmc/core/pwrseq_simple.c | 13 ++---
drivers/mux/gpio.c | 4 +-
drivers/net/phy/mdio-mux-gpio.c | 3 +-
drivers/pcmcia/soc_common.c | 11 ++--
drivers/phy/motorola/phy-mapphone-mdm6600.c | 17 +++---
drivers/staging/iio/adc/ad7606.c | 9 +--
drivers/tty/serial/serial_mctrl_gpio.c | 7 ++-
include/linux/gpio/consumer.h | 18 +++---
15 files changed, 138 insertions(+), 147 deletions(-)

diff --git a/Documentation/driver-api/gpio/consumer.rst b/Documentation/driver-api/gpio/consumer.rst
index aa03f389d41d..ed68042ddccf 100644
--- a/Documentation/driver-api/gpio/consumer.rst
+++ b/Documentation/driver-api/gpio/consumer.rst
@@ -323,29 +323,29 @@ The following functions get or set the values of an array of GPIOs::

int gpiod_get_array_value(unsigned int array_size,
struct gpio_desc **desc_array,
- int *value_array);
+ unsigned long *value_bitmap);
int gpiod_get_raw_array_value(unsigned int array_size,
struct gpio_desc **desc_array,
- int *value_array);
+ unsigned long *value_bitmap);
int gpiod_get_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
- int *value_array);
+ unsigned long *value_bitmap);
int gpiod_get_raw_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
- int *value_array);
+ unsigned long *value_bitmap);

void gpiod_set_array_value(unsigned int array_size,
struct gpio_desc **desc_array,
- int *value_array)
+ unsigned long *value_bitmap)
void gpiod_set_raw_array_value(unsigned int array_size,
struct gpio_desc **desc_array,
- int *value_array)
+ unsigned long *value_bitmap)
void gpiod_set_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
- int *value_array)
+ unsigned long *value_bitmap)
void gpiod_set_raw_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
- int *value_array)
+ unsigned long *value_bitmap)

The array can be an arbitrary set of GPIOs. The functions will try to access
GPIOs belonging to the same bank or chip simultaneously if supported by the
@@ -356,8 +356,8 @@ accessed sequentially.
The functions take three arguments:
* array_size - the number of array elements
* desc_array - an array of GPIO descriptors
- * value_array - an array to store the GPIOs' values (get) or
- an array of values to assign to the GPIOs (set)
+ * value_bitmap - a bitmap to store the GPIOs' values (get) or
+ a bitmap of values to assign to the GPIOs (set)

The descriptor array can be obtained using the gpiod_get_array() function
or one of its variants. If the group of descriptors returned by that function
@@ -366,7 +366,7 @@ the struct gpio_descs returned by gpiod_get_array()::

struct gpio_descs *my_gpio_descs = gpiod_get_array(...);
gpiod_set_array_value(my_gpio_descs->ndescs, my_gpio_descs->desc,
- my_gpio_values);
+ my_gpio_value_bitmap);

It is also possible to access a completely arbitrary array of descriptors. The
descriptors may be obtained using any combination of gpiod_get() and
diff --git a/drivers/auxdisplay/hd44780.c b/drivers/auxdisplay/hd44780.c
index f1a42f0f1ded..d340473aa142 100644
--- a/drivers/auxdisplay/hd44780.c
+++ b/drivers/auxdisplay/hd44780.c
@@ -62,20 +62,19 @@ static void hd44780_strobe_gpio(struct hd44780 *hd)
/* write to an LCD panel register in 8 bit GPIO mode */
static void hd44780_write_gpio8(struct hd44780 *hd, u8 val, unsigned int rs)
{
- int values[10]; /* for DATA[0-7], RS, RW */
- unsigned int i, n;
+ unsigned long value_bitmap[1]; /* for DATA[0-7], RS, RW */
+ unsigned int n;

- for (i = 0; i < 8; i++)
- values[PIN_DATA0 + i] = !!(val & BIT(i));
- values[PIN_CTRL_RS] = rs;
+ value_bitmap[0] = val;
+ __assign_bit(PIN_CTRL_RS, value_bitmap, rs);
n = 9;
if (hd->pins[PIN_CTRL_RW]) {
- values[PIN_CTRL_RW] = 0;
+ __clear_bit(PIN_CTRL_RW, value_bitmap);
n++;
}

/* Present the data to the port */
- gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA0], values);
+ gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA0], value_bitmap);

hd44780_strobe_gpio(hd);
}
@@ -83,32 +82,31 @@ static void hd44780_write_gpio8(struct hd44780 *hd, u8 val, unsigned int rs)
/* write to an LCD panel register in 4 bit GPIO mode */
static void hd44780_write_gpio4(struct hd44780 *hd, u8 val, unsigned int rs)
{
- int values[10]; /* for DATA[0-7], RS, RW, but DATA[0-3] is unused */
- unsigned int i, n;
+ /* for DATA[0-7], RS, RW, but DATA[0-3] is unused */
+ unsigned long value_bitmap[0];
+ unsigned int n;

/* High nibble + RS, RW */
- for (i = 4; i < 8; i++)
- values[PIN_DATA0 + i] = !!(val & BIT(i));
- values[PIN_CTRL_RS] = rs;
+ value_bitmap[0] = val;
+ __assign_bit(PIN_CTRL_RS, value_bitmap, rs);
n = 5;
if (hd->pins[PIN_CTRL_RW]) {
- values[PIN_CTRL_RW] = 0;
+ __clear_bit(PIN_CTRL_RW, value_bitmap);
n++;
}
+ value_bitmap[0] = value_bitmap[0] >> PIN_DATA4;

/* Present the data to the port */
- gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA4],
- &values[PIN_DATA4]);
+ gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA4], value_bitmap);

hd44780_strobe_gpio(hd);

/* Low nibble */
- for (i = 0; i < 4; i++)
- values[PIN_DATA4 + i] = !!(val & BIT(i));
+ value_bitmap[0] &= ~((1 << PIN_DATA4) - 1);
+ value_bitmap[0] |= val & ~((1 << PIN_DATA4) - 1);

/* Present the data to the port */
- gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA4],
- &values[PIN_DATA4]);
+ gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA4], value_bitmap);

hd44780_strobe_gpio(hd);
}
@@ -155,23 +153,23 @@ static void hd44780_write_cmd_gpio4(struct charlcd *lcd, int cmd)
/* Send 4-bits of a command to the LCD panel in raw 4 bit GPIO mode */
static void hd44780_write_cmd_raw_gpio4(struct charlcd *lcd, int cmd)
{
- int values[10]; /* for DATA[0-7], RS, RW, but DATA[0-3] is unused */
+ /* for DATA[0-7], RS, RW, but DATA[0-3] is unused */
+ unsigned long value_bitmap[1];
struct hd44780 *hd = lcd->drvdata;
- unsigned int i, n;
+ unsigned int n;

/* Command nibble + RS, RW */
- for (i = 0; i < 4; i++)
- values[PIN_DATA4 + i] = !!(cmd & BIT(i));
- values[PIN_CTRL_RS] = 0;
+ value_bitmap[0] = cmd << PIN_DATA4;
+ __clear_bit(PIN_CTRL_RS, value_bitmap);
n = 5;
if (hd->pins[PIN_CTRL_RW]) {
- values[PIN_CTRL_RW] = 0;
+ __clear_bit(PIN_CTRL_RW, value_bitmap);
n++;
}
+ value_bitmap[0] = value_bitmap[0] >> PIN_DATA4;

/* Present the data to the port */
- gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA4],
- &values[PIN_DATA4]);
+ gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA4], value_bitmap);

hd44780_strobe_gpio(hd);
}
diff --git a/drivers/bus/ts-nbus.c b/drivers/bus/ts-nbus.c
index 073fd9011154..ce6c1e89236d 100644
--- a/drivers/bus/ts-nbus.c
+++ b/drivers/bus/ts-nbus.c
@@ -110,13 +110,9 @@ static void ts_nbus_set_direction(struct ts_nbus *ts_nbus, int direction)
*/
static void ts_nbus_reset_bus(struct ts_nbus *ts_nbus)
{
- int i;
- int values[8];
-
- for (i = 0; i < 8; i++)
- values[i] = 0;
+ unsigned long value_bitmap[1] = { 0, };

- gpiod_set_array_value_cansleep(8, ts_nbus->data->desc, values);
+ gpiod_set_array_value_cansleep(8, ts_nbus->data->desc, value_bitmap);
gpiod_set_value_cansleep(ts_nbus->csn, 0);
gpiod_set_value_cansleep(ts_nbus->strobe, 0);
gpiod_set_value_cansleep(ts_nbus->ale, 0);
@@ -157,16 +153,9 @@ static int ts_nbus_read_byte(struct ts_nbus *ts_nbus, u8 *val)
static void ts_nbus_write_byte(struct ts_nbus *ts_nbus, u8 byte)
{
struct gpio_descs *gpios = ts_nbus->data;
- int i;
- int values[8];
-
- for (i = 0; i < 8; i++)
- if (byte & BIT(i))
- values[i] = 1;
- else
- values[i] = 0;
+ unsigned long value_bitmap[1] = { byte, };

- gpiod_set_array_value_cansleep(8, gpios->desc, values);
+ gpiod_set_array_value_cansleep(8, gpios->desc, value_bitmap);
}

/*
diff --git a/drivers/gpio/gpio-max3191x.c b/drivers/gpio/gpio-max3191x.c
index b5b9cb1fda50..c4ec1c82af27 100644
--- a/drivers/gpio/gpio-max3191x.c
+++ b/drivers/gpio/gpio-max3191x.c
@@ -315,17 +315,20 @@ static void gpiod_set_array_single_value_cansleep(unsigned int ndescs,
struct gpio_desc **desc,
int value)
{
- int i, *values;
+ unsigned long *value_bitmap;

- values = kmalloc_array(ndescs, sizeof(*values), GFP_KERNEL);
- if (!values)
+ value_bitmap = kmalloc_array(BITS_TO_LONGS(ndescs),
+ sizeof(*value_bitmap), GFP_KERNEL);
+ if (!value_bitmap)
return;

- for (i = 0; i < ndescs; i++)
- values[i] = value;
+ if (value)
+ bitmap_fill(value_bitmap, ndescs);
+ else
+ bitmap_zero(value_bitmap, ndescs);

- gpiod_set_array_value_cansleep(ndescs, desc, values);
- kfree(values);
+ gpiod_set_array_value_cansleep(ndescs, desc, value_bitmap);
+ kfree(value_bitmap);
}

static struct gpio_descs *devm_gpiod_get_array_optional_count(
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index e8f8a1999393..f0e9ffa8cab6 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -427,7 +427,7 @@ static long linehandle_ioctl(struct file *filep, unsigned int cmd,
struct linehandle_state *lh = filep->private_data;
void __user *ip = (void __user *)arg;
struct gpiohandle_data ghd;
- int vals[GPIOHANDLES_MAX];
+ unsigned long value_bitmap[BITS_TO_LONGS(GPIOHANDLES_MAX)];
int i;

if (cmd == GPIOHANDLE_GET_LINE_VALUES_IOCTL) {
@@ -436,13 +436,13 @@ static long linehandle_ioctl(struct file *filep, unsigned int cmd,
true,
lh->numdescs,
lh->descs,
- vals);
+ value_bitmap);
if (ret)
return ret;

memset(&ghd, 0, sizeof(ghd));
for (i = 0; i < lh->numdescs; i++)
- ghd.values[i] = vals[i];
+ ghd.values[i] = test_bit(i, value_bitmap);

if (copy_to_user(ip, &ghd, sizeof(ghd)))
return -EFAULT;
@@ -461,14 +461,14 @@ static long linehandle_ioctl(struct file *filep, unsigned int cmd,

/* Clamp all values to [0,1] */
for (i = 0; i < lh->numdescs; i++)
- vals[i] = !!ghd.values[i];
+ __assign_bit(i, value_bitmap, !!ghd.values[i]);

/* Reuse the array setting function */
return gpiod_set_array_value_complex(false,
true,
lh->numdescs,
lh->descs,
- vals);
+ value_bitmap);
}
return -EINVAL;
}
@@ -2784,7 +2784,7 @@ static int gpio_chip_get_multiple(struct gpio_chip *chip,
int gpiod_get_array_value_complex(bool raw, bool can_sleep,
unsigned int array_size,
struct gpio_desc **desc_array,
- int *value_array)
+ unsigned long *value_bitmap)
{
int i = 0;

@@ -2835,7 +2835,7 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep,

if (!raw && test_bit(FLAG_ACTIVE_LOW, &desc->flags))
value = !value;
- value_array[j] = value;
+ __assign_bit(j, value_bitmap, value);
trace_gpio_value(desc_to_gpio(desc), 1, value);
}

@@ -2895,9 +2895,9 @@ EXPORT_SYMBOL_GPL(gpiod_get_value);

/**
* gpiod_get_raw_array_value() - read raw values from an array of GPIOs
- * @array_size: number of elements in the descriptor / value arrays
+ * @array_size: number of elements in the descriptor array / value bitmap
* @desc_array: array of GPIO descriptors whose values will be read
- * @value_array: array to store the read values
+ * @value_bitmap: bitmap to store the read values
*
* Read the raw values of the GPIOs, i.e. the values of the physical lines
* without regard for their ACTIVE_LOW status. Return 0 in case of success,
@@ -2907,20 +2907,21 @@ EXPORT_SYMBOL_GPL(gpiod_get_value);
* and it will complain if the GPIO chip functions potentially sleep.
*/
int gpiod_get_raw_array_value(unsigned int array_size,
- struct gpio_desc **desc_array, int *value_array)
+ struct gpio_desc **desc_array,
+ unsigned long *value_bitmap)
{
if (!desc_array)
return -EINVAL;
return gpiod_get_array_value_complex(true, false, array_size,
- desc_array, value_array);
+ desc_array, value_bitmap);
}
EXPORT_SYMBOL_GPL(gpiod_get_raw_array_value);

/**
* gpiod_get_array_value() - read values from an array of GPIOs
- * @array_size: number of elements in the descriptor / value arrays
+ * @array_size: number of elements in the descriptor array / value bitmap
* @desc_array: array of GPIO descriptors whose values will be read
- * @value_array: array to store the read values
+ * @value_bitnap: bitmap to store the read values
*
* Read the logical values of the GPIOs, i.e. taking their ACTIVE_LOW status
* into account. Return 0 in case of success, else an error code.
@@ -2929,12 +2930,13 @@ EXPORT_SYMBOL_GPL(gpiod_get_raw_array_value);
* and it will complain if the GPIO chip functions potentially sleep.
*/
int gpiod_get_array_value(unsigned int array_size,
- struct gpio_desc **desc_array, int *value_array)
+ struct gpio_desc **desc_array,
+ unsigned long *value_bitmap)
{
if (!desc_array)
return -EINVAL;
return gpiod_get_array_value_complex(false, false, array_size,
- desc_array, value_array);
+ desc_array, value_bitmap);
}
EXPORT_SYMBOL_GPL(gpiod_get_array_value);

@@ -3027,7 +3029,7 @@ static void gpio_chip_set_multiple(struct gpio_chip *chip,
int gpiod_set_array_value_complex(bool raw, bool can_sleep,
unsigned int array_size,
struct gpio_desc **desc_array,
- int *value_array)
+ unsigned long *value_bitmap)
{
int i = 0;

@@ -3056,7 +3058,7 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep,
do {
struct gpio_desc *desc = desc_array[i];
int hwgpio = gpio_chip_hwgpio(desc);
- int value = value_array[i];
+ int value = test_bit(i, value_bitmap);

if (!raw && test_bit(FLAG_ACTIVE_LOW, &desc->flags))
value = !value;
@@ -3152,9 +3154,9 @@ EXPORT_SYMBOL_GPL(gpiod_set_value);

/**
* gpiod_set_raw_array_value() - assign values to an array of GPIOs
- * @array_size: number of elements in the descriptor / value arrays
+ * @array_size: number of elements in the descriptor array / value bitmap
* @desc_array: array of GPIO descriptors whose values will be assigned
- * @value_array: array of values to assign
+ * @value_bitmap: bitmap of values to assign
*
* Set the raw values of the GPIOs, i.e. the values of the physical lines
* without regard for their ACTIVE_LOW status.
@@ -3163,20 +3165,21 @@ EXPORT_SYMBOL_GPL(gpiod_set_value);
* complain if the GPIO chip functions potentially sleep.
*/
int gpiod_set_raw_array_value(unsigned int array_size,
- struct gpio_desc **desc_array, int *value_array)
+ struct gpio_desc **desc_array,
+ unsigned long *value_bitmap)
{
if (!desc_array)
return -EINVAL;
return gpiod_set_array_value_complex(true, false, array_size,
- desc_array, value_array);
+ desc_array, value_bitmap);
}
EXPORT_SYMBOL_GPL(gpiod_set_raw_array_value);

/**
* gpiod_set_array_value() - assign values to an array of GPIOs
- * @array_size: number of elements in the descriptor / value arrays
+ * @array_size: number of elements in the descriptor array / value bitmap
* @desc_array: array of GPIO descriptors whose values will be assigned
- * @value_array: array of values to assign
+ * @value_bitmap: bitmap of values to assign
*
* Set the logical values of the GPIOs, i.e. taking their ACTIVE_LOW status
* into account.
@@ -3185,12 +3188,13 @@ EXPORT_SYMBOL_GPL(gpiod_set_raw_array_value);
* complain if the GPIO chip functions potentially sleep.
*/
void gpiod_set_array_value(unsigned int array_size,
- struct gpio_desc **desc_array, int *value_array)
+ struct gpio_desc **desc_array,
+ unsigned long *value_bitmap)
{
if (!desc_array)
return;
gpiod_set_array_value_complex(false, false, array_size, desc_array,
- value_array);
+ value_bitmap);
}
EXPORT_SYMBOL_GPL(gpiod_set_array_value);

@@ -3410,9 +3414,9 @@ EXPORT_SYMBOL_GPL(gpiod_get_value_cansleep);

/**
* gpiod_get_raw_array_value_cansleep() - read raw values from an array of GPIOs
- * @array_size: number of elements in the descriptor / value arrays
+ * @array_size: number of elements in the descriptor array / value bitmap
* @desc_array: array of GPIO descriptors whose values will be read
- * @value_array: array to store the read values
+ * @value_bitmap: bitmap to store the read values
*
* Read the raw values of the GPIOs, i.e. the values of the physical lines
* without regard for their ACTIVE_LOW status. Return 0 in case of success,
@@ -3422,21 +3426,21 @@ EXPORT_SYMBOL_GPL(gpiod_get_value_cansleep);
*/
int gpiod_get_raw_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
- int *value_array)
+ unsigned long *value_bitmap)
{
might_sleep_if(extra_checks);
if (!desc_array)
return -EINVAL;
return gpiod_get_array_value_complex(true, true, array_size,
- desc_array, value_array);
+ desc_array, value_bitmap);
}
EXPORT_SYMBOL_GPL(gpiod_get_raw_array_value_cansleep);

/**
* gpiod_get_array_value_cansleep() - read values from an array of GPIOs
- * @array_size: number of elements in the descriptor / value arrays
+ * @array_size: number of elements in the descriptor array / value bitmap
* @desc_array: array of GPIO descriptors whose values will be read
- * @value_array: array to store the read values
+ * @value_bitmap: bitmap to store the read values
*
* Read the logical values of the GPIOs, i.e. taking their ACTIVE_LOW status
* into account. Return 0 in case of success, else an error code.
@@ -3445,13 +3449,13 @@ EXPORT_SYMBOL_GPL(gpiod_get_raw_array_value_cansleep);
*/
int gpiod_get_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
- int *value_array)
+ unsigned long *value_bitmap)
{
might_sleep_if(extra_checks);
if (!desc_array)
return -EINVAL;
return gpiod_get_array_value_complex(false, true, array_size,
- desc_array, value_array);
+ desc_array, value_bitmap);
}
EXPORT_SYMBOL_GPL(gpiod_get_array_value_cansleep);

@@ -3493,9 +3497,9 @@ EXPORT_SYMBOL_GPL(gpiod_set_value_cansleep);

/**
* gpiod_set_raw_array_value_cansleep() - assign values to an array of GPIOs
- * @array_size: number of elements in the descriptor / value arrays
+ * @array_size: number of elements in the descriptor array / value bitmap
* @desc_array: array of GPIO descriptors whose values will be assigned
- * @value_array: array of values to assign
+ * @value_bitmap: bitmap of values to assign
*
* Set the raw values of the GPIOs, i.e. the values of the physical lines
* without regard for their ACTIVE_LOW status.
@@ -3504,13 +3508,13 @@ EXPORT_SYMBOL_GPL(gpiod_set_value_cansleep);
*/
int gpiod_set_raw_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
- int *value_array)
+ unsigned long *value_bitmap)
{
might_sleep_if(extra_checks);
if (!desc_array)
return -EINVAL;
return gpiod_set_array_value_complex(true, true, array_size, desc_array,
- value_array);
+ value_bitmap);
}
EXPORT_SYMBOL_GPL(gpiod_set_raw_array_value_cansleep);

@@ -3533,9 +3537,9 @@ void gpiod_add_lookup_tables(struct gpiod_lookup_table **tables, size_t n)

/**
* gpiod_set_array_value_cansleep() - assign values to an array of GPIOs
- * @array_size: number of elements in the descriptor / value arrays
+ * @array_size: number of elements in the descriptor array / value bitmap
* @desc_array: array of GPIO descriptors whose values will be assigned
- * @value_array: array of values to assign
+ * @value_bitmap: bitmap of values to assign
*
* Set the logical values of the GPIOs, i.e. taking their ACTIVE_LOW status
* into account.
@@ -3544,13 +3548,13 @@ void gpiod_add_lookup_tables(struct gpiod_lookup_table **tables, size_t n)
*/
void gpiod_set_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
- int *value_array)
+ unsigned long *value_bitmap)
{
might_sleep_if(extra_checks);
if (!desc_array)
return;
gpiod_set_array_value_complex(false, true, array_size, desc_array,
- value_array);
+ value_bitmap);
}
EXPORT_SYMBOL_GPL(gpiod_set_array_value_cansleep);

diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h
index a7e49fef73d4..11e83d2eef89 100644
--- a/drivers/gpio/gpiolib.h
+++ b/drivers/gpio/gpiolib.h
@@ -187,11 +187,11 @@ struct gpio_desc *gpiochip_get_desc(struct gpio_chip *chip, u16 hwnum);
int gpiod_get_array_value_complex(bool raw, bool can_sleep,
unsigned int array_size,
struct gpio_desc **desc_array,
- int *value_array);
+ unsigned long *value_bitmap);
int gpiod_set_array_value_complex(bool raw, bool can_sleep,
unsigned int array_size,
struct gpio_desc **desc_array,
- int *value_array);
+ unsigned long *value_bitmap);

/* This is just passed between gpiolib and devres */
struct gpio_desc *gpiod_get_from_of_node(struct device_node *node,
diff --git a/drivers/i2c/muxes/i2c-mux-gpio.c b/drivers/i2c/muxes/i2c-mux-gpio.c
index 401308e3d036..d675e0ca2fa4 100644
--- a/drivers/i2c/muxes/i2c-mux-gpio.c
+++ b/drivers/i2c/muxes/i2c-mux-gpio.c
@@ -27,13 +27,14 @@ struct gpiomux {

static void i2c_mux_gpio_set(const struct gpiomux *mux, unsigned val)
{
+ unsigned long value_bitmap[1] = { val, };
int i;

for (i = 0; i < mux->data.n_gpios; i++)
mux->values[i] = (val >> i) & 1;

gpiod_set_array_value_cansleep(mux->data.n_gpios,
- mux->gpios, mux->values);
+ mux->gpios, value_bitmap);
}

static int i2c_mux_gpio_select(struct i2c_mux_core *muxc, u32 chan)
diff --git a/drivers/mmc/core/pwrseq_simple.c b/drivers/mmc/core/pwrseq_simple.c
index a8b9fee4d62a..0d6e3a5be3ba 100644
--- a/drivers/mmc/core/pwrseq_simple.c
+++ b/drivers/mmc/core/pwrseq_simple.c
@@ -40,18 +40,13 @@ static void mmc_pwrseq_simple_set_gpios_value(struct mmc_pwrseq_simple *pwrseq,
struct gpio_descs *reset_gpios = pwrseq->reset_gpios;

if (!IS_ERR(reset_gpios)) {
- int i, *values;
+ unsigned long value_bitmap[1];
int nvalues = reset_gpios->ndescs;

- values = kmalloc_array(nvalues, sizeof(int), GFP_KERNEL);
- if (!values)
- return;
+ value_bitmap[0] = value;

- for (i = 0; i < nvalues; i++)
- values[i] = value;
-
- gpiod_set_array_value_cansleep(nvalues, reset_gpios->desc, values);
- kfree(values);
+ gpiod_set_array_value_cansleep(nvalues, reset_gpios->desc,
+ value_bitmap);
}
}

diff --git a/drivers/mux/gpio.c b/drivers/mux/gpio.c
index 6fdd9316db8b..cc2d5f50472a 100644
--- a/drivers/mux/gpio.c
+++ b/drivers/mux/gpio.c
@@ -23,14 +23,14 @@ struct mux_gpio {
static int mux_gpio_set(struct mux_control *mux, int state)
{
struct mux_gpio *mux_gpio = mux_chip_priv(mux->chip);
+ unsigned long value_bitmap[1] = { state, };
int i;

for (i = 0; i < mux_gpio->gpios->ndescs; i++)
mux_gpio->val[i] = (state >> i) & 1;

gpiod_set_array_value_cansleep(mux_gpio->gpios->ndescs,
- mux_gpio->gpios->desc,
- mux_gpio->val);
+ mux_gpio->gpios->desc, value_bitmap);

return 0;
}
diff --git a/drivers/net/phy/mdio-mux-gpio.c b/drivers/net/phy/mdio-mux-gpio.c
index bc90764a8b8d..8e1ec750277e 100644
--- a/drivers/net/phy/mdio-mux-gpio.c
+++ b/drivers/net/phy/mdio-mux-gpio.c
@@ -27,6 +27,7 @@ static int mdio_mux_gpio_switch_fn(int current_child, int desired_child,
void *data)
{
struct mdio_mux_gpio_state *s = data;
+ unsigned long value_bitmap[1] = { desired_child, };
unsigned int n;

if (current_child == desired_child)
@@ -36,7 +37,7 @@ static int mdio_mux_gpio_switch_fn(int current_child, int desired_child,
s->values[n] = (desired_child >> n) & 1;

gpiod_set_array_value_cansleep(s->gpios->ndescs, s->gpios->desc,
- s->values);
+ value_bitmap);

return 0;
}
diff --git a/drivers/pcmcia/soc_common.c b/drivers/pcmcia/soc_common.c
index c5f2344c189b..e0f89155c474 100644
--- a/drivers/pcmcia/soc_common.c
+++ b/drivers/pcmcia/soc_common.c
@@ -351,19 +351,22 @@ static int soc_common_pcmcia_config_skt(

if (ret == 0) {
struct gpio_desc *descs[2];
- int values[2], n = 0;
+ unsigned long value_bitmap[1];
+ int n = 0;

if (skt->gpio_reset) {
descs[n] = skt->gpio_reset;
- values[n++] = !!(state->flags & SS_RESET);
+ __assign_bit(n++, value_bitmap,
+ !!(state->flags & SS_RESET));
}
if (skt->gpio_bus_enable) {
descs[n] = skt->gpio_bus_enable;
- values[n++] = !!(state->flags & SS_OUTPUT_ENA);
+ __assign_bit(n++, value_bitmap,
+ !!(state->flags & SS_OUTPUT_ENA));
}

if (n)
- gpiod_set_array_value_cansleep(n, descs, values);
+ gpiod_set_array_value_cansleep(n, descs, value_bitmap);

/*
* This really needs a better solution. The IRQ
diff --git a/drivers/phy/motorola/phy-mapphone-mdm6600.c b/drivers/phy/motorola/phy-mapphone-mdm6600.c
index 0075fb0bef8c..b6477c3599c4 100644
--- a/drivers/phy/motorola/phy-mapphone-mdm6600.c
+++ b/drivers/phy/motorola/phy-mapphone-mdm6600.c
@@ -157,15 +157,12 @@ static const struct phy_ops gpio_usb_ops = {
*/
static void phy_mdm6600_cmd(struct phy_mdm6600 *ddata, int val)
{
- int values[PHY_MDM6600_NR_CMD_LINES];
- int i;
+ unsigned long value_bitmap[1];

- val &= (1 << PHY_MDM6600_NR_CMD_LINES) - 1;
- for (i = 0; i < PHY_MDM6600_NR_CMD_LINES; i++)
- values[i] = (val & BIT(i)) >> i;
+ value_bitmap[0] = val & ((1 << PHY_MDM6600_NR_CMD_LINES) - 1);

gpiod_set_array_value_cansleep(PHY_MDM6600_NR_CMD_LINES,
- ddata->cmd_gpios->desc, values);
+ ddata->cmd_gpios->desc, value_bitmap);
}

/**
@@ -176,7 +173,7 @@ static void phy_mdm6600_status(struct work_struct *work)
{
struct phy_mdm6600 *ddata;
struct device *dev;
- int values[PHY_MDM6600_NR_STATUS_LINES];
+ unsigned long value_bitmap[1] = { 0, };
int error, i, val = 0;

ddata = container_of(work, struct phy_mdm6600, status_work.work);
@@ -184,14 +181,14 @@ static void phy_mdm6600_status(struct work_struct *work)

error = gpiod_get_array_value_cansleep(PHY_MDM6600_NR_STATUS_LINES,
ddata->status_gpios->desc,
- values);
+ value_bitmap);
if (error)
return;

for (i = 0; i < PHY_MDM6600_NR_STATUS_LINES; i++) {
- val |= values[i] << i;
+ val |= test_bit(i, value_bitmap) << i;
dev_dbg(ddata->dev, "XXX %s: i: %i values[i]: %i val: %i\n",
- __func__, i, values[i], val);
+ __func__, i, test_bit(i, value_bitmap), val);
}
ddata->status = val;

diff --git a/drivers/staging/iio/adc/ad7606.c b/drivers/staging/iio/adc/ad7606.c
index 25b9fcd5e3a4..0eca047bc1cc 100644
--- a/drivers/staging/iio/adc/ad7606.c
+++ b/drivers/staging/iio/adc/ad7606.c
@@ -202,7 +202,7 @@ static int ad7606_write_raw(struct iio_dev *indio_dev,
long mask)
{
struct ad7606_state *st = iio_priv(indio_dev);
- int values[3];
+ unsigned long value_bitmap[1];
int ret, i;

switch (mask) {
@@ -227,13 +227,10 @@ static int ad7606_write_raw(struct iio_dev *indio_dev,
if (ret < 0)
return ret;

- values[0] = (ret >> 0) & 1;
- values[1] = (ret >> 1) & 1;
- values[2] = (ret >> 2) & 1;
+ value_bitmap[0] = ret;

mutex_lock(&st->lock);
- gpiod_set_array_value(ARRAY_SIZE(values), st->gpio_os->desc,
- values);
+ gpiod_set_array_value(3, st->gpio_os->desc, value_bitmap);
st->oversampling = val;
mutex_unlock(&st->lock);

diff --git a/drivers/tty/serial/serial_mctrl_gpio.c b/drivers/tty/serial/serial_mctrl_gpio.c
index 1c06325beaca..bb8b4756d72d 100644
--- a/drivers/tty/serial/serial_mctrl_gpio.c
+++ b/drivers/tty/serial/serial_mctrl_gpio.c
@@ -40,7 +40,7 @@ void mctrl_gpio_set(struct mctrl_gpios *gpios, unsigned int mctrl)
{
enum mctrl_gpio_idx i;
struct gpio_desc *desc_array[UART_GPIO_MAX];
- int value_array[UART_GPIO_MAX];
+ unsigned long value_bitmap[BITS_TO_LONGS(UART_GPIO_MAX)];
unsigned int count = 0;

if (gpios == NULL)
@@ -49,10 +49,11 @@ void mctrl_gpio_set(struct mctrl_gpios *gpios, unsigned int mctrl)
for (i = 0; i < UART_GPIO_MAX; i++)
if (gpios->gpio[i] && mctrl_gpios_desc[i].dir_out) {
desc_array[count] = gpios->gpio[i];
- value_array[count] = !!(mctrl & mctrl_gpios_desc[i].mctrl);
+ __assign_bit(count, value_bitmap,
+ !!(mctrl & mctrl_gpios_desc[i].mctrl));
count++;
}
- gpiod_set_array_value(count, desc_array, value_array);
+ gpiod_set_array_value(count, desc_array, value_bitmap);
}
EXPORT_SYMBOL_GPL(mctrl_gpio_set);

diff --git a/include/linux/gpio/consumer.h b/include/linux/gpio/consumer.h
index 21ddbe440030..1b21dc7b0fad 100644
--- a/include/linux/gpio/consumer.h
+++ b/include/linux/gpio/consumer.h
@@ -104,36 +104,38 @@ int gpiod_direction_output_raw(struct gpio_desc *desc, int value);
/* Value get/set from non-sleeping context */
int gpiod_get_value(const struct gpio_desc *desc);
int gpiod_get_array_value(unsigned int array_size,
- struct gpio_desc **desc_array, int *value_array);
+ struct gpio_desc **desc_array,
+ unsigned long *value_bitmap);
void gpiod_set_value(struct gpio_desc *desc, int value);
void gpiod_set_array_value(unsigned int array_size,
- struct gpio_desc **desc_array, int *value_array);
+ struct gpio_desc **desc_array,
+ unsigned long *value_bitmap);
int gpiod_get_raw_value(const struct gpio_desc *desc);
int gpiod_get_raw_array_value(unsigned int array_size,
struct gpio_desc **desc_array,
- int *value_array);
+ unsigned long *value_bitmap);
void gpiod_set_raw_value(struct gpio_desc *desc, int value);
int gpiod_set_raw_array_value(unsigned int array_size,
struct gpio_desc **desc_array,
- int *value_array);
+ unsigned long *value_bitmap);

/* Value get/set from sleeping context */
int gpiod_get_value_cansleep(const struct gpio_desc *desc);
int gpiod_get_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
- int *value_array);
+ unsigned long *value_bitmap);
void gpiod_set_value_cansleep(struct gpio_desc *desc, int value);
void gpiod_set_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
- int *value_array);
+ unsigned long *value_bitmap);
int gpiod_get_raw_value_cansleep(const struct gpio_desc *desc);
int gpiod_get_raw_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
- int *value_array);
+ unsigned long *value_bitmap);
void gpiod_set_raw_value_cansleep(struct gpio_desc *desc, int value);
int gpiod_set_raw_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
- int *value_array);
+ unsigned long *value_bitmap);

int gpiod_set_debounce(struct gpio_desc *desc, unsigned debounce);
int gpiod_set_transitory(struct gpio_desc *desc, bool transitory);
--
2.16.4


2018-08-21 06:51:10

by Peter Rosin

[permalink] [raw]
Subject: Re: [RFC RFT PATCH v4 1/4] gpiolib: Pass bitmaps, not integer arrays, to get/set array

On 2018-08-21 01:43, Janusz Krzysztofik wrote:
> Most users of get/set array functions iterate consecutive bits of data,
> usually a single integer, while or processing array of results obtained
> from or building an array of values to be passed to those functions.
> Save time wasted on those iterations by changing the functions' API to
> accept bitmaps.
>
> All current users are updated as well.
>
> More benefits from the change are expected as soon as planned support
> for accepting/passing those bitmaps directly from/to respective GPIO
> chip callbacks if applicable is implemented.
>
> Signed-off-by: Janusz Krzysztofik <[email protected]>
> ---
> Documentation/driver-api/gpio/consumer.rst | 22 ++++----
> drivers/auxdisplay/hd44780.c | 52 +++++++++--------
> drivers/bus/ts-nbus.c | 19 ++-----
> drivers/gpio/gpio-max3191x.c | 17 +++---
> drivers/gpio/gpiolib.c | 86 +++++++++++++++--------------
> drivers/gpio/gpiolib.h | 4 +-
> drivers/i2c/muxes/i2c-mux-gpio.c | 3 +-
> drivers/mmc/core/pwrseq_simple.c | 13 ++---
> drivers/mux/gpio.c | 4 +-
> drivers/net/phy/mdio-mux-gpio.c | 3 +-
> drivers/pcmcia/soc_common.c | 11 ++--
> drivers/phy/motorola/phy-mapphone-mdm6600.c | 17 +++---
> drivers/staging/iio/adc/ad7606.c | 9 +--
> drivers/tty/serial/serial_mctrl_gpio.c | 7 ++-
> include/linux/gpio/consumer.h | 18 +++---
> 15 files changed, 138 insertions(+), 147 deletions(-)
>

*snip*

> diff --git a/drivers/i2c/muxes/i2c-mux-gpio.c b/drivers/i2c/muxes/i2c-mux-gpio.c
> index 401308e3d036..d675e0ca2fa4 100644
> --- a/drivers/i2c/muxes/i2c-mux-gpio.c
> +++ b/drivers/i2c/muxes/i2c-mux-gpio.c
> @@ -27,13 +27,14 @@ struct gpiomux {
>
> static void i2c_mux_gpio_set(const struct gpiomux *mux, unsigned val)
> {
> + unsigned long value_bitmap[1] = { val, };
> int i;
>
> for (i = 0; i < mux->data.n_gpios; i++)
> mux->values[i] = (val >> i) & 1;
>
> gpiod_set_array_value_cansleep(mux->data.n_gpios,
> - mux->gpios, mux->values);
> + mux->gpios, value_bitmap);
> }
>
> static int i2c_mux_gpio_select(struct i2c_mux_core *muxc, u32 chan)

Please take the opportunity to completely get rid of the values member
in struct mux_gpio. It no longer serves any purpose.

*snip*

> diff --git a/drivers/mux/gpio.c b/drivers/mux/gpio.c
> index 6fdd9316db8b..cc2d5f50472a 100644
> --- a/drivers/mux/gpio.c
> +++ b/drivers/mux/gpio.c
> @@ -23,14 +23,14 @@ struct mux_gpio {
> static int mux_gpio_set(struct mux_control *mux, int state)
> {
> struct mux_gpio *mux_gpio = mux_chip_priv(mux->chip);
> + unsigned long value_bitmap[1] = { state, };
> int i;
>
> for (i = 0; i < mux_gpio->gpios->ndescs; i++)
> mux_gpio->val[i] = (state >> i) & 1;
>
> gpiod_set_array_value_cansleep(mux_gpio->gpios->ndescs,
> - mux_gpio->gpios->desc,
> - mux_gpio->val);
> + mux_gpio->gpios->desc, value_bitmap);
>
> return 0;
> }

Dito (but the member name is val).

*snip*

Cheers,
Peter

2018-08-21 06:54:13

by Peter Rosin

[permalink] [raw]
Subject: Re: [RFC RFT PATCH v4 1/4] gpiolib: Pass bitmaps, not integer arrays, to get/set array

Sorry for replying to self...

On 2018-08-21 08:49, Peter Rosin wrote:
> On 2018-08-21 01:43, Janusz Krzysztofik wrote:
>> Most users of get/set array functions iterate consecutive bits of data,
>> usually a single integer, while or processing array of results obtained
>> from or building an array of values to be passed to those functions.
>> Save time wasted on those iterations by changing the functions' API to
>> accept bitmaps.
>>
>> All current users are updated as well.
>>
>> More benefits from the change are expected as soon as planned support
>> for accepting/passing those bitmaps directly from/to respective GPIO
>> chip callbacks if applicable is implemented.
>>
>> Signed-off-by: Janusz Krzysztofik <[email protected]>
>> ---
>> Documentation/driver-api/gpio/consumer.rst | 22 ++++----
>> drivers/auxdisplay/hd44780.c | 52 +++++++++--------
>> drivers/bus/ts-nbus.c | 19 ++-----
>> drivers/gpio/gpio-max3191x.c | 17 +++---
>> drivers/gpio/gpiolib.c | 86 +++++++++++++++--------------
>> drivers/gpio/gpiolib.h | 4 +-
>> drivers/i2c/muxes/i2c-mux-gpio.c | 3 +-
>> drivers/mmc/core/pwrseq_simple.c | 13 ++---
>> drivers/mux/gpio.c | 4 +-
>> drivers/net/phy/mdio-mux-gpio.c | 3 +-
>> drivers/pcmcia/soc_common.c | 11 ++--
>> drivers/phy/motorola/phy-mapphone-mdm6600.c | 17 +++---
>> drivers/staging/iio/adc/ad7606.c | 9 +--
>> drivers/tty/serial/serial_mctrl_gpio.c | 7 ++-
>> include/linux/gpio/consumer.h | 18 +++---
>> 15 files changed, 138 insertions(+), 147 deletions(-)
>>
>
> *snip*
>
>> diff --git a/drivers/i2c/muxes/i2c-mux-gpio.c b/drivers/i2c/muxes/i2c-mux-gpio.c
>> index 401308e3d036..d675e0ca2fa4 100644
>> --- a/drivers/i2c/muxes/i2c-mux-gpio.c
>> +++ b/drivers/i2c/muxes/i2c-mux-gpio.c
>> @@ -27,13 +27,14 @@ struct gpiomux {
>>
>> static void i2c_mux_gpio_set(const struct gpiomux *mux, unsigned val)
>> {
>> + unsigned long value_bitmap[1] = { val, };
>> int i;
>>
>> for (i = 0; i < mux->data.n_gpios; i++)
>> mux->values[i] = (val >> i) & 1;
>>
>> gpiod_set_array_value_cansleep(mux->data.n_gpios,
>> - mux->gpios, mux->values);
>> + mux->gpios, value_bitmap);
>> }
>>
>> static int i2c_mux_gpio_select(struct i2c_mux_core *muxc, u32 chan)
>
> Please take the opportunity to completely get rid of the values member
> in struct mux_gpio. It no longer serves any purpose.

struct gpiomux

>
> *snip*
>
>> diff --git a/drivers/mux/gpio.c b/drivers/mux/gpio.c
>> index 6fdd9316db8b..cc2d5f50472a 100644
>> --- a/drivers/mux/gpio.c
>> +++ b/drivers/mux/gpio.c
>> @@ -23,14 +23,14 @@ struct mux_gpio {
>> static int mux_gpio_set(struct mux_control *mux, int state)
>> {
>> struct mux_gpio *mux_gpio = mux_chip_priv(mux->chip);
>> + unsigned long value_bitmap[1] = { state, };
>> int i;
>>
>> for (i = 0; i < mux_gpio->gpios->ndescs; i++)
>> mux_gpio->val[i] = (state >> i) & 1;
>>
>> gpiod_set_array_value_cansleep(mux_gpio->gpios->ndescs,
>> - mux_gpio->gpios->desc,
>> - mux_gpio->val);
>> + mux_gpio->gpios->desc, value_bitmap);
>>
>> return 0;
>> }
>
> Dito (but the member name is val).

Here is where struct mux_gpio fits.

Cheers,
Peter

>
> *snip*
>
> Cheers,
> Peter
>


2018-08-24 12:58:44

by Boris Brezillon

[permalink] [raw]
Subject: Re: [PATCH v4 3/3] mtd: rawnand: ams-delta: Set port direction when needed

On Mon, 20 Aug 2018 23:39:04 +0200
Janusz Krzysztofik <[email protected]> wrote:

> In its current shape, the driver sets data port direction before each
> byte read/write operation, even during multi-byte transfers. Improve
> performance of the driver by setting the port direction only when
> needed.
>
> This optimisation will become particularly important as soon as
> planned conversion of the driver to GPIO API for data I/O will be
> implemented.
>
> Signed-off-by: Janusz Krzysztofik <[email protected]>

Reviewed-by: Boris Brezillon <[email protected]>

> ---
> drivers/mtd/nand/raw/ams-delta.c | 68 ++++++++++++++++++++++++++--------------
> 1 file changed, 45 insertions(+), 23 deletions(-)
>
> diff --git a/drivers/mtd/nand/raw/ams-delta.c b/drivers/mtd/nand/raw/ams-delta.c
> index 48233d638d2a..4d5e5790f18e 100644
> --- a/drivers/mtd/nand/raw/ams-delta.c
> +++ b/drivers/mtd/nand/raw/ams-delta.c
> @@ -45,6 +45,7 @@ struct ams_delta_nand {
> struct gpio_desc *gpiod_ale;
> struct gpio_desc *gpiod_cle;
> void __iomem *io_base;
> + bool data_in;
> };
>
> /*
> @@ -72,50 +73,65 @@ static const struct mtd_partition partition_info[] = {
> .size = 3 * SZ_256K },
> };
>
> -static void ams_delta_write_byte(struct mtd_info *mtd, u_char byte)
> +static void ams_delta_io_write(struct ams_delta_nand *priv, u8 byte)
> {
> - struct nand_chip *this = mtd_to_nand(mtd);
> - struct ams_delta_nand *priv = nand_get_controller_data(this);
> - void __iomem *io_base = priv->io_base;
> -
> - writew(0, io_base + OMAP_MPUIO_IO_CNTL);
> - writew(byte, this->IO_ADDR_W);
> + writew(byte, priv->nand_chip.IO_ADDR_W);
> gpiod_set_value(priv->gpiod_nwe, 0);
> ndelay(40);
> gpiod_set_value(priv->gpiod_nwe, 1);
> }
>
> -static u_char ams_delta_read_byte(struct mtd_info *mtd)
> +static u8 ams_delta_io_read(struct ams_delta_nand *priv)
> {
> - u_char res;
> - struct nand_chip *this = mtd_to_nand(mtd);
> - struct ams_delta_nand *priv = nand_get_controller_data(this);
> - void __iomem *io_base = priv->io_base;
> + u8 res;
>
> gpiod_set_value(priv->gpiod_nre, 0);
> ndelay(40);
> - writew(~0, io_base + OMAP_MPUIO_IO_CNTL);
> - res = readw(this->IO_ADDR_R);
> + res = readw(priv->nand_chip.IO_ADDR_R);
> gpiod_set_value(priv->gpiod_nre, 1);
>
> return res;
> }
>
> -static void ams_delta_write_buf(struct mtd_info *mtd, const u_char *buf,
> - int len)
> +static void ams_delta_dir_input(struct ams_delta_nand *priv, bool in)
> {
> + writew(in ? ~0 : 0, priv->io_base + OMAP_MPUIO_IO_CNTL);
> + priv->data_in = in;
> +}
> +
> +static void ams_delta_write_buf(struct mtd_info *mtd, const u8 *buf, int len)
> +{
> + struct nand_chip *this = mtd_to_nand(mtd);
> + struct ams_delta_nand *priv = nand_get_controller_data(this);
> int i;
>
> - for (i=0; i<len; i++)
> - ams_delta_write_byte(mtd, buf[i]);
> + if (priv->data_in)
> + ams_delta_dir_input(priv, false);
> +
> + for (i = 0; i < len; i++)
> + ams_delta_io_write(priv, buf[i]);
> }
>
> -static void ams_delta_read_buf(struct mtd_info *mtd, u_char *buf, int len)
> +static void ams_delta_read_buf(struct mtd_info *mtd, u8 *buf, int len)
> {
> + struct nand_chip *this = mtd_to_nand(mtd);
> + struct ams_delta_nand *priv = nand_get_controller_data(this);
> int i;
>
> - for (i=0; i<len; i++)
> - buf[i] = ams_delta_read_byte(mtd);
> + if (!priv->data_in)
> + ams_delta_dir_input(priv, true);
> +
> + for (i = 0; i < len; i++)
> + buf[i] = ams_delta_io_read(priv);
> +}
> +
> +static u8 ams_delta_read_byte(struct mtd_info *mtd)
> +{
> + u8 res;
> +
> + ams_delta_read_buf(mtd, &res, 1);
> +
> + return res;
> }
>
> /*
> @@ -138,8 +154,11 @@ static void ams_delta_hwcontrol(struct mtd_info *mtd, int cmd,
> gpiod_set_value(priv->gpiod_ale, !!(ctrl & NAND_ALE));
> }
>
> - if (cmd != NAND_CMD_NONE)
> - ams_delta_write_byte(mtd, cmd);
> + if (cmd != NAND_CMD_NONE) {
> + u8 byte = cmd;
> +
> + ams_delta_write_buf(mtd, &byte, 1);
> + }
> }
>
> static int ams_delta_nand_ready(struct mtd_info *mtd)
> @@ -262,6 +281,9 @@ static int ams_delta_init(struct platform_device *pdev)
> goto out_mtd;
> }
>
> + /* Initialize data port direction to a known state */
> + ams_delta_dir_input(priv, true);
> +
> /* Scan to find existence of the device */
> err = nand_scan(mtd, 1);
> if (err)


2018-08-29 09:07:57

by Linus Walleij

[permalink] [raw]
Subject: Re: [RFC RFT PATCH 0/4] gpiolib: speed up GPIO array processing

On Tue, Aug 21, 2018 at 1:42 AM Janusz Krzysztofik <[email protected]> wrote:

> This series is a follow up of the former "mtd: rawnand: ams-delta: Use
> gpio-omap accessors for data I/O" which already contained some changes
> to gpiolib. Those previous attempts were commented by Borris Brezillon
> who suggested using GPIO API modified to accept bitmaps, and by Linus
> Walleij who suggested still more great ideas for further immprovement
> of the proposed API changes - thanks!
>
> The goal is to boost performans of get/set array functions while
> processing GPIO arrays which represent pins of a signle chip in
> hardware order. If resulting performance is close to PIO, GPIO API
> can be used for data I/O without much loss of speed.

Hands down, this is a very pretty patch set. I'm a big fan already.

This is mainly because it fulfills the requirement for libraries
to be narrow and deep, which is what we want.
This refers to John Ousterhouts software design philosophy,
here is a great lecture if you haven't seen it already:
https://www.youtube.com/watch?v=bmSAYlu0NcY

Let's get this into v1 and get some testing and merge it for v4.20
ASAP so we get some proper testing before the v4.20 merge
window. It would be excellent if some of the current users of
the array API could provide tested-by's or at least ACKs.

For example ts-nbus.c must be a big benefactor.

Yours,
Linus Walleij

2018-08-29 10:21:05

by Ulf Hansson

[permalink] [raw]
Subject: Re: [RFC RFT PATCH 0/4] gpiolib: speed up GPIO array processing

On 21 August 2018 at 01:43, Janusz Krzysztofik <[email protected]> wrote:
>
> This series is a follow up of the former "mtd: rawnand: ams-delta: Use
> gpio-omap accessors for data I/O" which already contained some changes
> to gpiolib. Those previous attempts were commented by Borris Brezillon
> who suggested using GPIO API modified to accept bitmaps, and by Linus
> Walleij who suggested still more great ideas for further immprovement
> of the proposed API changes - thanks!
>
> The goal is to boost performans of get/set array functions while
> processing GPIO arrays which represent pins of a signle chip in
> hardware order. If resulting performance is close to PIO, GPIO API
> can be used for data I/O without much loss of speed.
>
> Created and tested on a low end Amstrad Delta board with NAND driver
> updated to use GPIO API for data I/O. Performance degrade compared to
> PIO is much better than before the optimization but still not quite
> satisfactory.
>
>
> Janusz Krzysztofik (4):
> gpiolib: Pass bitmaps, not integer arrays, to get/set array
> gpiolib: Identify arrays matching GPIO hardware
> gpiolib: Pass array info to get/set array functions
> gpiolib: Implement fast processing path in get/set array
>
>
> Documentation/driver-api/gpio/board.rst | 15 +
> Documentation/driver-api/gpio/consumer.rst | 48 +++-
> drivers/auxdisplay/hd44780.c | 64 +++---
> drivers/bus/ts-nbus.c | 25 --
> drivers/gpio/gpio-max3191x.c | 23 +-
> drivers/gpio/gpiolib.c | 279 ++++++++++++++++++++++------
> drivers/gpio/gpiolib.h | 15 +
> drivers/i2c/muxes/i2c-mux-gpio.c | 5
> drivers/mmc/core/pwrseq_simple.c | 15 -
> drivers/mux/gpio.c | 7
> drivers/net/phy/mdio-mux-gpio.c | 5
> drivers/pcmcia/soc_common.c | 14 -
> drivers/phy/motorola/phy-mapphone-mdm6600.c | 21 +-
> drivers/staging/iio/adc/ad7606.c | 12 -
> drivers/tty/serial/serial_mctrl_gpio.c | 9
> include/linux/gpio/consumer.h | 35 ++-
> 16 files changed, 410 insertions(+), 182 deletions(-)
>

For the mmc related changes:

Acked-by: Ulf Hansson <[email protected]>

Kind regards
Uffe

2018-08-29 12:05:36

by Miguel Ojeda

[permalink] [raw]
Subject: Re: [RFC RFT PATCH v4 1/4] gpiolib: Pass bitmaps, not integer arrays, to get/set array

Hi Janusz,

On Tue, Aug 21, 2018 at 1:43 AM, Janusz Krzysztofik <[email protected]> wrote:
> Most users of get/set array functions iterate consecutive bits of data,
> usually a single integer, while or processing array of results obtained
> from or building an array of values to be passed to those functions.
> Save time wasted on those iterations by changing the functions' API to
> accept bitmaps.
>
> All current users are updated as well.
>
> More benefits from the change are expected as soon as planned support
> for accepting/passing those bitmaps directly from/to respective GPIO
> chip callbacks if applicable is implemented.
>
> Signed-off-by: Janusz Krzysztofik <[email protected]>
> ---
> Documentation/driver-api/gpio/consumer.rst | 22 ++++----
> drivers/auxdisplay/hd44780.c | 52 +++++++++--------

[CC'ing Willy and Geert for hd44780]

> diff --git a/drivers/auxdisplay/hd44780.c b/drivers/auxdisplay/hd44780.c
> index f1a42f0f1ded..d340473aa142 100644
> --- a/drivers/auxdisplay/hd44780.c
> +++ b/drivers/auxdisplay/hd44780.c
> @@ -62,20 +62,19 @@ static void hd44780_strobe_gpio(struct hd44780 *hd)
> /* write to an LCD panel register in 8 bit GPIO mode */
> static void hd44780_write_gpio8(struct hd44780 *hd, u8 val, unsigned int rs)
> {
> - int values[10]; /* for DATA[0-7], RS, RW */
> - unsigned int i, n;
> + unsigned long value_bitmap[1]; /* for DATA[0-7], RS, RW */

Why [1]? I understand it is because in other cases it may be more than
one, but...

> + unsigned int n;
>
> - for (i = 0; i < 8; i++)
> - values[PIN_DATA0 + i] = !!(val & BIT(i));
> - values[PIN_CTRL_RS] = rs;
> + value_bitmap[0] = val;
> + __assign_bit(PIN_CTRL_RS, value_bitmap, rs);
> n = 9;
> if (hd->pins[PIN_CTRL_RW]) {
> - values[PIN_CTRL_RW] = 0;
> + __clear_bit(PIN_CTRL_RW, value_bitmap);
> n++;
> }
>
> /* Present the data to the port */
> - gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA0], values);
> + gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA0], value_bitmap);
>
> hd44780_strobe_gpio(hd);
> }
> @@ -83,32 +82,31 @@ static void hd44780_write_gpio8(struct hd44780 *hd, u8 val, unsigned int rs)
> /* write to an LCD panel register in 4 bit GPIO mode */
> static void hd44780_write_gpio4(struct hd44780 *hd, u8 val, unsigned int rs)
> {
> - int values[10]; /* for DATA[0-7], RS, RW, but DATA[0-3] is unused */
> - unsigned int i, n;
> + /* for DATA[0-7], RS, RW, but DATA[0-3] is unused */
> + unsigned long value_bitmap[0];

This one is even more strange... :-)

> + unsigned int n;
>
> /* High nibble + RS, RW */
> - for (i = 4; i < 8; i++)
> - values[PIN_DATA0 + i] = !!(val & BIT(i));
> - values[PIN_CTRL_RS] = rs;
> + value_bitmap[0] = val;
> + __assign_bit(PIN_CTRL_RS, value_bitmap, rs);
> n = 5;
> if (hd->pins[PIN_CTRL_RW]) {
> - values[PIN_CTRL_RW] = 0;
> + __clear_bit(PIN_CTRL_RW, value_bitmap);
> n++;
> }
> + value_bitmap[0] = value_bitmap[0] >> PIN_DATA4;

Maybe >>=?

>
> /* Present the data to the port */
> - gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA4],
> - &values[PIN_DATA4]);
> + gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA4], value_bitmap);
>
> hd44780_strobe_gpio(hd);
>
> /* Low nibble */
> - for (i = 0; i < 4; i++)
> - values[PIN_DATA4 + i] = !!(val & BIT(i));
> + value_bitmap[0] &= ~((1 << PIN_DATA4) - 1);
> + value_bitmap[0] |= val & ~((1 << PIN_DATA4) - 1);

Are you sure this is correct? You are basically doing an or of
value_bitmap and val and clearing the low-nibble.

>
> /* Present the data to the port */
> - gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA4],
> - &values[PIN_DATA4]);
> + gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA4], value_bitmap);
>
> hd44780_strobe_gpio(hd);
> }

Cheers,
Miguel

2018-08-29 18:01:26

by Janusz Krzysztofik

[permalink] [raw]
Subject: Re: [RFC RFT PATCH v4 1/4] gpiolib: Pass bitmaps, not integer arrays, to get/set array

On Wednesday, August 29, 2018 2:03:18 PM CEST Miguel Ojeda wrote:
> Hi Janusz,
>
> On Tue, Aug 21, 2018 at 1:43 AM, Janusz Krzysztofik <[email protected]> wrote:
> > Most users of get/set array functions iterate consecutive bits of data,
> > usually a single integer, while or processing array of results obtained
> > from or building an array of values to be passed to those functions.
> > Save time wasted on those iterations by changing the functions' API to
> > accept bitmaps.
> >
> > All current users are updated as well.
> >
> > More benefits from the change are expected as soon as planned support
> > for accepting/passing those bitmaps directly from/to respective GPIO
> > chip callbacks if applicable is implemented.
> >
> > Signed-off-by: Janusz Krzysztofik <[email protected]>
> > ---
> > Documentation/driver-api/gpio/consumer.rst | 22 ++++----
> > drivers/auxdisplay/hd44780.c | 52 +++++++++--------
>
> [CC'ing Willy and Geert for hd44780]
>
> > diff --git a/drivers/auxdisplay/hd44780.c b/drivers/auxdisplay/hd44780.c
> > index f1a42f0f1ded..d340473aa142 100644
> > --- a/drivers/auxdisplay/hd44780.c
> > +++ b/drivers/auxdisplay/hd44780.c
> > @@ -62,20 +62,19 @@ static void hd44780_strobe_gpio(struct hd44780 *hd)
> > /* write to an LCD panel register in 8 bit GPIO mode */
> > static void hd44780_write_gpio8(struct hd44780 *hd, u8 val, unsigned int rs)
> > {
> > - int values[10]; /* for DATA[0-7], RS, RW */
> > - unsigned int i, n;
> > + unsigned long value_bitmap[1]; /* for DATA[0-7], RS, RW */
>
> Why [1]? I understand it is because in other cases it may be more than
> one,

Yes, I tried to point out the fact the new API accepts a bitmap of an arbitrary
length, and I tried to use the same code pattern across changes to the API
users.

> but...
>
> > + unsigned int n;
> >
> > - for (i = 0; i < 8; i++)
> > - values[PIN_DATA0 + i] = !!(val & BIT(i));
> > - values[PIN_CTRL_RS] = rs;
> > + value_bitmap[0] = val;
> > + __assign_bit(PIN_CTRL_RS, value_bitmap, rs);
> > n = 9;
> > if (hd->pins[PIN_CTRL_RW]) {
> > - values[PIN_CTRL_RW] = 0;
> > + __clear_bit(PIN_CTRL_RW, value_bitmap);
> > n++;
> > }
> >
> > /* Present the data to the port */
> > - gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA0], values);
> > + gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA0], value_bitmap);
> >
> > hd44780_strobe_gpio(hd);
> > }
> > @@ -83,32 +82,31 @@ static void hd44780_write_gpio8(struct hd44780 *hd, u8 val, unsigned int rs)
> > /* write to an LCD panel register in 4 bit GPIO mode */
> > static void hd44780_write_gpio4(struct hd44780 *hd, u8 val, unsigned int rs)
> > {
> > - int values[10]; /* for DATA[0-7], RS, RW, but DATA[0-3] is unused */
> > - unsigned int i, n;
> > + /* for DATA[0-7], RS, RW, but DATA[0-3] is unused */
> > + unsigned long value_bitmap[0];
>
> This one is even more strange... :-)

This one is an error, should be 1 of course :-), thanks.

> > + unsigned int n;
> >
> > /* High nibble + RS, RW */
> > - for (i = 4; i < 8; i++)
> > - values[PIN_DATA0 + i] = !!(val & BIT(i));
> > - values[PIN_CTRL_RS] = rs;
> > + value_bitmap[0] = val;
> > + __assign_bit(PIN_CTRL_RS, value_bitmap, rs);
> > n = 5;
> > if (hd->pins[PIN_CTRL_RW]) {
> > - values[PIN_CTRL_RW] = 0;
> > + __clear_bit(PIN_CTRL_RW, value_bitmap);
> > n++;
> > }
> > + value_bitmap[0] = value_bitmap[0] >> PIN_DATA4;
>
> Maybe >>=?

OK.

Answering you question below:
To make my changes as clear as I could imagine, I decided to use the same indexing as in the original code, i.e., assign high nibble of val to bits 4-7 and two other values - rs and an optional 0 - to bits 8 and 9, respectively.
Unlike in case of array of integers, where for the high nibble part you could just pass a pointer to a sub-array starting at the 5th value (i.e., &values[PIN_DATA4]), it was not possible to do the same for and arbitrary bit of a bitmap, e.g., pass a pointer to the 5th bit of *value_bitmap as an argument pointing to bit 0 of a bitmap to be processed. That's why I shifted the bitmap right by 4 bits.
Then, ...

> >
> > /* Present the data to the port */
> > - gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA4],
> > - &values[PIN_DATA4]);
> > + gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA4], value_bitmap);
> >
> > hd44780_strobe_gpio(hd);
> >
> > /* Low nibble */
> > - for (i = 0; i < 4; i++)
> > - values[PIN_DATA4 + i] = !!(val & BIT(i));
> > + value_bitmap[0] &= ~((1 << PIN_DATA4) - 1);
> > + value_bitmap[0] |= val & ~((1 << PIN_DATA4) - 1);
>
> Are you sure this is correct? You are basically doing an or of
> value_bitmap and val and clearing the low-nibble.

having the rs and optional 0 already assigned to bits 4 and 5 of the bitmap, I just cleared bits 0-3 still containing the high nibble of val and assigned the low nibble of it to those bits, getting a result ready to be passed as an argument to gpiod_set_array_value_cansleep() below.

I hope I didn't miss anything.

Thanks,
Janusz

> >
> > /* Present the data to the port */
> > - gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA4],
> > - &values[PIN_DATA4]);
> > + gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA4], value_bitmap);
> >
> > hd44780_strobe_gpio(hd);
> > }
>
> Cheers,
> Miguel
>





2018-08-29 18:16:57

by Janusz Krzysztofik

[permalink] [raw]
Subject: Re: [RFC RFT PATCH 0/4] gpiolib: speed up GPIO array processing

Hi Linus,

On Wednesday, August 29, 2018 11:06:21 AM CEST Linus Walleij wrote:
> On Tue, Aug 21, 2018 at 1:42 AM Janusz Krzysztofik <[email protected]>
wrote:
>
> > This series is a follow up of the former "mtd: rawnand: ams-delta: Use
> > gpio-omap accessors for data I/O" which already contained some changes
> > to gpiolib. Those previous attempts were commented by Borris Brezillon
> > who suggested using GPIO API modified to accept bitmaps, and by Linus
> > Walleij who suggested still more great ideas for further immprovement
> > of the proposed API changes - thanks!
> >
> > The goal is to boost performans of get/set array functions while
> > processing GPIO arrays which represent pins of a signle chip in
> > hardware order. If resulting performance is close to PIO, GPIO API
> > can be used for data I/O without much loss of speed.
>
> Hands down, this is a very pretty patch set. I'm a big fan already.
>
> This is mainly because it fulfills the requirement for libraries
> to be narrow and deep, which is what we want.
> This refers to John Ousterhouts software design philosophy,
> here is a great lecture if you haven't seen it already:
> https://www.youtube.com/watch?v=bmSAYlu0NcY
>
> Let's get this into v1 and get some testing and merge it for v4.20
> ASAP

Please hold on for a while, I'm going to resubmit soon, with the comment from
Peter Rosin on i2c-mux-gpio addressed and the error discovered by Miguel Ojeda
in hd44780 fixed.

Thanks,
Janusz

> so we get some proper testing before the v4.20 merge
> window. It would be excellent if some of the current users of
> the array API could provide tested-by's or at least ACKs.
>
> For example ts-nbus.c must be a big benefactor.
>
> Yours,
> Linus Walleij
>





2018-08-29 20:49:48

by Janusz Krzysztofik

[permalink] [raw]
Subject: [PATH v5 0/4] gpiolib: speed up GPIO array processing


The goal is to boost performance of get/set array functions while
processing GPIO arrays which represent pins of a signle chip in
hardware order. If resulting performance is close to PIO, GPIO API
can be used for data I/O without much loss of speed.

Created and tested on a low end Amstrad Delta board with NAND driver
updated to use GPIO API for data I/O. Performance degrade compared to
PIO is much better than before the optimization but still not quite
satisfactory.

Janusz Krzysztofik (4):
gpiolib: Pass bitmaps, not integer arrays, to get/set array
gpiolib: Identify arrays matching GPIO hardware
gpiolib: Pass array info to get/set array functions
gpiolib: Implement fast processing path in get/set array

Changelog:
v5:
[PATCH v5 1/4] gpiolib: Pass bitmaps, not integer arrays, to get/set
- drivers/i2c/muxes/i2c-mux-gpio.c:
- drop assigment of values to struct gpiomux.values, as recommended
by Peter Rosin - thanks!,
- mark the .values member of the structure as obsolete,
- drivers/mux/gpio.c:
- drop assigment of values to struct mux_gpio.val, also recommended
by Peter Rosin - thanks!,
- merk the .val member of the structure as obsolete,
- drivers/auxdisplay/hd44780.c:
- fix incorrect bitmap size,
- use >>= operator to simplify notation,
both catched by Miguel Ojeda - thanks!,
- add Cc: clauses as well as Acked-by: collected so far.
[PATCH v5 2/4] gpiolib: Identify arrays matching GPIO hardware
- add Cc: clause.
[PATCH v5 3/4] gpiolib: Pass array info to get/set array functions
- add Cc: clauses as well as Acked-by: collected so far.
[PATCH v5 4/4] gpiolib: Implement fast processing path in get/set
- add Cc: clause.

v4:
That series was a follow up of the former "mtd: rawnand: ams-delta: Use
gpio-omap accessors for data I/O" which already contained some changes
to gpiolib. Those previous attempts were commented by Borris Brezillon
who suggested using GPIO API modified to accept bitmaps, and by Linus
Walleij who suggested still more great ideas for further immprovement
of the proposed API changes - thanks!

diffstat:
Documentation/driver-api/gpio/board.rst | 15 +
Documentation/driver-api/gpio/consumer.rst | 48 +++-
drivers/auxdisplay/hd44780.c | 64 +++---
drivers/bus/ts-nbus.c | 25 --
drivers/gpio/gpio-max3191x.c | 23 +-
drivers/gpio/gpiolib.c | 279 ++++++++++++++++++++++------
drivers/gpio/gpiolib.h | 15 +
drivers/i2c/muxes/i2c-mux-gpio.c | 10 -
drivers/mmc/core/pwrseq_simple.c | 15 -
drivers/mux/gpio.c | 12 -
drivers/net/phy/mdio-mux-gpio.c | 5
drivers/pcmcia/soc_common.c | 14 -
drivers/phy/motorola/phy-mapphone-mdm6600.c | 21 +-
drivers/staging/iio/adc/ad7606.c | 12 -
drivers/tty/serial/serial_mctrl_gpio.c | 9
include/linux/gpio/consumer.h | 35 ++-
16 files changed, 412 insertions(+), 190 deletions(-)


2018-08-29 20:49:55

by Janusz Krzysztofik

[permalink] [raw]
Subject: [PATCH v5 3/4] gpiolib: Pass array info to get/set array functions

In order to make use of array info obtained from gpiod_get_array() and
speed up processing of arrays matching single GPIO chip layout, that
information must be passed to get/set array functions. Extend the
functions' API with that additional parameter and update all users.
Pass NULL if a user bulids an array itself from single GPIOs.

Cc: Jonathan Corbet <[email protected]>
Cc: Miguel Ojeda Sandonis <[email protected]>
Cc: Peter Korsgaard <[email protected]>
Cc: Peter Rosin <[email protected]>
Cc: Andrew Lunn <[email protected]>
Cc: Florian Fainelli <[email protected]>
Cc: "David S. Miller" <[email protected]>
Cc: Dominik Brodowski <[email protected]>
Cc: Kishon Vijay Abraham I <[email protected]>
Cc: Lars-Peter Clausen <[email protected]>
Cc: Michael Hennerich <[email protected]>
Cc: Jonathan Cameron <[email protected]>
Cc: Hartmut Knaack <[email protected]>
Cc: Peter Meerwald-Stadler <[email protected]>
Cc: Greg Kroah-Hartman <[email protected]>
Cc: Jiri Slaby <[email protected]>
Signed-off-by: Janusz Krzysztofik <[email protected]>
Acked-by: Ulf Hansson <[email protected]>
---
Documentation/driver-api/gpio/consumer.rst | 14 ++++++++++--
drivers/auxdisplay/hd44780.c | 12 ++++++----
drivers/bus/ts-nbus.c | 6 +++--
drivers/gpio/gpio-max3191x.c | 6 +++--
drivers/gpio/gpiolib.c | 34 ++++++++++++++++++++---------
drivers/gpio/gpiolib.h | 2 ++
drivers/i2c/muxes/i2c-mux-gpio.c | 2 +-
drivers/mmc/core/pwrseq_simple.c | 2 +-
drivers/mux/gpio.c | 3 ++-
drivers/net/phy/mdio-mux-gpio.c | 2 +-
drivers/pcmcia/soc_common.c | 3 ++-
drivers/phy/motorola/phy-mapphone-mdm6600.c | 4 +++-
drivers/staging/iio/adc/ad7606.c | 3 ++-
drivers/tty/serial/serial_mctrl_gpio.c | 2 +-
include/linux/gpio/consumer.h | 8 +++++++
15 files changed, 75 insertions(+), 28 deletions(-)

diff --git a/Documentation/driver-api/gpio/consumer.rst b/Documentation/driver-api/gpio/consumer.rst
index 7e0298b9a7b9..0afd95a12b10 100644
--- a/Documentation/driver-api/gpio/consumer.rst
+++ b/Documentation/driver-api/gpio/consumer.rst
@@ -325,28 +325,36 @@ The following functions get or set the values of an array of GPIOs::

int gpiod_get_array_value(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap);
int gpiod_get_raw_array_value(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap);
int gpiod_get_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap);
int gpiod_get_raw_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap);

void gpiod_set_array_value(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap)
void gpiod_set_raw_array_value(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap)
void gpiod_set_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap)
void gpiod_set_raw_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap)

The array can be an arbitrary set of GPIOs. The functions will try to access
@@ -358,6 +366,7 @@ accessed sequentially.
The functions take three arguments:
* array_size - the number of array elements
* desc_array - an array of GPIO descriptors
+ * array_info - optional information obtained from gpiod_array_get()
* value_bitmap - a bitmap to store the GPIOs' values (get) or
a bitmap of values to assign to the GPIOs (set)

@@ -368,12 +377,13 @@ the struct gpio_descs returned by gpiod_get_array()::

struct gpio_descs *my_gpio_descs = gpiod_get_array(...);
gpiod_set_array_value(my_gpio_descs->ndescs, my_gpio_descs->desc,
- my_gpio_value_bitmap);
+ my_gpio_descs->info, my_gpio_value_bitmap);

It is also possible to access a completely arbitrary array of descriptors. The
descriptors may be obtained using any combination of gpiod_get() and
gpiod_get_array(). Afterwards the array of descriptors has to be setup
-manually before it can be passed to one of the above functions.
+manually before it can be passed to one of the above functions. In that case,
+array_info should be set to NULL.

Note that for optimal performance GPIOs belonging to the same chip should be
contiguous within the array of descriptors.
diff --git a/drivers/auxdisplay/hd44780.c b/drivers/auxdisplay/hd44780.c
index bbbd6a29bf01..ec20c41831b0 100644
--- a/drivers/auxdisplay/hd44780.c
+++ b/drivers/auxdisplay/hd44780.c
@@ -74,7 +74,8 @@ static void hd44780_write_gpio8(struct hd44780 *hd, u8 val, unsigned int rs)
}

/* Present the data to the port */
- gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA0], value_bitmap);
+ gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA0], NULL,
+ value_bitmap);

hd44780_strobe_gpio(hd);
}
@@ -97,7 +98,8 @@ static void hd44780_write_gpio4(struct hd44780 *hd, u8 val, unsigned int rs)
value_bitmap[0] >>= PIN_DATA4;

/* Present the data to the port */
- gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA4], value_bitmap);
+ gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA4], NULL,
+ value_bitmap);

hd44780_strobe_gpio(hd);

@@ -106,7 +108,8 @@ static void hd44780_write_gpio4(struct hd44780 *hd, u8 val, unsigned int rs)
value_bitmap[0] |= val & ~((1 << PIN_DATA4) - 1);

/* Present the data to the port */
- gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA4], value_bitmap);
+ gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA4], NULL,
+ value_bitmap);

hd44780_strobe_gpio(hd);
}
@@ -169,7 +172,8 @@ static void hd44780_write_cmd_raw_gpio4(struct charlcd *lcd, int cmd)
value_bitmap[0] = value_bitmap[0] >> PIN_DATA4;

/* Present the data to the port */
- gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA4], value_bitmap);
+ gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA4], NULL,
+ value_bitmap);

hd44780_strobe_gpio(hd);
}
diff --git a/drivers/bus/ts-nbus.c b/drivers/bus/ts-nbus.c
index ce6c1e89236d..000d756eb42c 100644
--- a/drivers/bus/ts-nbus.c
+++ b/drivers/bus/ts-nbus.c
@@ -112,7 +112,8 @@ static void ts_nbus_reset_bus(struct ts_nbus *ts_nbus)
{
unsigned long value_bitmap[1] = { 0, };

- gpiod_set_array_value_cansleep(8, ts_nbus->data->desc, value_bitmap);
+ gpiod_set_array_value_cansleep(8, ts_nbus->data->desc,
+ ts_nbus->data->info, value_bitmap);
gpiod_set_value_cansleep(ts_nbus->csn, 0);
gpiod_set_value_cansleep(ts_nbus->strobe, 0);
gpiod_set_value_cansleep(ts_nbus->ale, 0);
@@ -155,7 +156,8 @@ static void ts_nbus_write_byte(struct ts_nbus *ts_nbus, u8 byte)
struct gpio_descs *gpios = ts_nbus->data;
unsigned long value_bitmap[1] = { byte, };

- gpiod_set_array_value_cansleep(8, gpios->desc, value_bitmap);
+ gpiod_set_array_value_cansleep(8, gpios->desc, gpios->info,
+ value_bitmap);
}

/*
diff --git a/drivers/gpio/gpio-max3191x.c b/drivers/gpio/gpio-max3191x.c
index c4ec1c82af27..4b43b5dabfd2 100644
--- a/drivers/gpio/gpio-max3191x.c
+++ b/drivers/gpio/gpio-max3191x.c
@@ -313,6 +313,7 @@ static int max3191x_set_config(struct gpio_chip *gpio, unsigned int offset,

static void gpiod_set_array_single_value_cansleep(unsigned int ndescs,
struct gpio_desc **desc,
+ struct gpio_array *info,
int value)
{
unsigned long *value_bitmap;
@@ -327,7 +328,7 @@ static void gpiod_set_array_single_value_cansleep(unsigned int ndescs,
else
bitmap_zero(value_bitmap, ndescs);

- gpiod_set_array_value_cansleep(ndescs, desc, value_bitmap);
+ gpiod_set_array_value_cansleep(ndescs, desc, info, value_bitmap);
kfree(value_bitmap);
}

@@ -400,7 +401,8 @@ static int max3191x_probe(struct spi_device *spi)
if (max3191x->modesel_pins)
gpiod_set_array_single_value_cansleep(
max3191x->modesel_pins->ndescs,
- max3191x->modesel_pins->desc, max3191x->mode);
+ max3191x->modesel_pins->desc,
+ max3191x->modesel_pins->info, max3191x->mode);

max3191x->ignore_uv = device_property_read_bool(dev,
"maxim,ignore-undervoltage");
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index c1ed1c759345..4d26cdbdb7cf 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -435,7 +435,7 @@ static long linehandle_ioctl(struct file *filep, unsigned int cmd,
int ret = gpiod_get_array_value_complex(false,
true,
lh->numdescs,
- lh->descs,
+ lh->descs, NULL,
value_bitmap);
if (ret)
return ret;
@@ -467,7 +467,7 @@ static long linehandle_ioctl(struct file *filep, unsigned int cmd,
return gpiod_set_array_value_complex(false,
true,
lh->numdescs,
- lh->descs,
+ lh->descs, NULL,
value_bitmap);
}
return -EINVAL;
@@ -2784,6 +2784,7 @@ static int gpio_chip_get_multiple(struct gpio_chip *chip,
int gpiod_get_array_value_complex(bool raw, bool can_sleep,
unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap)
{
int i = 0;
@@ -2908,12 +2909,14 @@ EXPORT_SYMBOL_GPL(gpiod_get_value);
*/
int gpiod_get_raw_array_value(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap)
{
if (!desc_array)
return -EINVAL;
return gpiod_get_array_value_complex(true, false, array_size,
- desc_array, value_bitmap);
+ desc_array, array_info,
+ value_bitmap);
}
EXPORT_SYMBOL_GPL(gpiod_get_raw_array_value);

@@ -2931,12 +2934,14 @@ EXPORT_SYMBOL_GPL(gpiod_get_raw_array_value);
*/
int gpiod_get_array_value(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap)
{
if (!desc_array)
return -EINVAL;
return gpiod_get_array_value_complex(false, false, array_size,
- desc_array, value_bitmap);
+ desc_array, array_info,
+ value_bitmap);
}
EXPORT_SYMBOL_GPL(gpiod_get_array_value);

@@ -3029,6 +3034,7 @@ static void gpio_chip_set_multiple(struct gpio_chip *chip,
int gpiod_set_array_value_complex(bool raw, bool can_sleep,
unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap)
{
int i = 0;
@@ -3166,12 +3172,13 @@ EXPORT_SYMBOL_GPL(gpiod_set_value);
*/
int gpiod_set_raw_array_value(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap)
{
if (!desc_array)
return -EINVAL;
return gpiod_set_array_value_complex(true, false, array_size,
- desc_array, value_bitmap);
+ desc_array, array_info, value_bitmap);
}
EXPORT_SYMBOL_GPL(gpiod_set_raw_array_value);

@@ -3189,12 +3196,13 @@ EXPORT_SYMBOL_GPL(gpiod_set_raw_array_value);
*/
void gpiod_set_array_value(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap)
{
if (!desc_array)
return;
gpiod_set_array_value_complex(false, false, array_size, desc_array,
- value_bitmap);
+ array_info, value_bitmap);
}
EXPORT_SYMBOL_GPL(gpiod_set_array_value);

@@ -3426,13 +3434,15 @@ EXPORT_SYMBOL_GPL(gpiod_get_value_cansleep);
*/
int gpiod_get_raw_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap)
{
might_sleep_if(extra_checks);
if (!desc_array)
return -EINVAL;
return gpiod_get_array_value_complex(true, true, array_size,
- desc_array, value_bitmap);
+ desc_array, array_info,
+ value_bitmap);
}
EXPORT_SYMBOL_GPL(gpiod_get_raw_array_value_cansleep);

@@ -3449,13 +3459,15 @@ EXPORT_SYMBOL_GPL(gpiod_get_raw_array_value_cansleep);
*/
int gpiod_get_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap)
{
might_sleep_if(extra_checks);
if (!desc_array)
return -EINVAL;
return gpiod_get_array_value_complex(false, true, array_size,
- desc_array, value_bitmap);
+ desc_array, array_info,
+ value_bitmap);
}
EXPORT_SYMBOL_GPL(gpiod_get_array_value_cansleep);

@@ -3508,13 +3520,14 @@ EXPORT_SYMBOL_GPL(gpiod_set_value_cansleep);
*/
int gpiod_set_raw_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap)
{
might_sleep_if(extra_checks);
if (!desc_array)
return -EINVAL;
return gpiod_set_array_value_complex(true, true, array_size, desc_array,
- value_bitmap);
+ array_info, value_bitmap);
}
EXPORT_SYMBOL_GPL(gpiod_set_raw_array_value_cansleep);

@@ -3548,13 +3561,14 @@ void gpiod_add_lookup_tables(struct gpiod_lookup_table **tables, size_t n)
*/
void gpiod_set_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap)
{
might_sleep_if(extra_checks);
if (!desc_array)
return;
gpiod_set_array_value_complex(false, true, array_size, desc_array,
- value_bitmap);
+ array_info, value_bitmap);
}
EXPORT_SYMBOL_GPL(gpiod_set_array_value_cansleep);

diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h
index b60905d558b1..b65ca896b24d 100644
--- a/drivers/gpio/gpiolib.h
+++ b/drivers/gpio/gpiolib.h
@@ -196,10 +196,12 @@ struct gpio_desc *gpiochip_get_desc(struct gpio_chip *chip, u16 hwnum);
int gpiod_get_array_value_complex(bool raw, bool can_sleep,
unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap);
int gpiod_set_array_value_complex(bool raw, bool can_sleep,
unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap);

/* This is just passed between gpiolib and devres */
diff --git a/drivers/i2c/muxes/i2c-mux-gpio.c b/drivers/i2c/muxes/i2c-mux-gpio.c
index 4e36e0eac7a3..4439a92c86a2 100644
--- a/drivers/i2c/muxes/i2c-mux-gpio.c
+++ b/drivers/i2c/muxes/i2c-mux-gpio.c
@@ -31,7 +31,7 @@ static void i2c_mux_gpio_set(const struct gpiomux *mux, unsigned val)
int i;

gpiod_set_array_value_cansleep(mux->data.n_gpios,
- mux->gpios, value_bitmap);
+ mux->gpios, NULL, value_bitmap);
}

static int i2c_mux_gpio_select(struct i2c_mux_core *muxc, u32 chan)
diff --git a/drivers/mmc/core/pwrseq_simple.c b/drivers/mmc/core/pwrseq_simple.c
index 0d6e3a5be3ba..5cf7eda8f68f 100644
--- a/drivers/mmc/core/pwrseq_simple.c
+++ b/drivers/mmc/core/pwrseq_simple.c
@@ -46,7 +46,7 @@ static void mmc_pwrseq_simple_set_gpios_value(struct mmc_pwrseq_simple *pwrseq,
value_bitmap[0] = value;

gpiod_set_array_value_cansleep(nvalues, reset_gpios->desc,
- value_bitmap);
+ reset_gpios->info, value_bitmap);
}
}

diff --git a/drivers/mux/gpio.c b/drivers/mux/gpio.c
index 734e1b43aed6..be8c86680e10 100644
--- a/drivers/mux/gpio.c
+++ b/drivers/mux/gpio.c
@@ -27,7 +27,8 @@ static int mux_gpio_set(struct mux_control *mux, int state)
int i;

gpiod_set_array_value_cansleep(mux_gpio->gpios->ndescs,
- mux_gpio->gpios->desc, value_bitmap);
+ mux_gpio->gpios->desc,
+ mux_gpio->gpios->info, value_bitmap);

return 0;
}
diff --git a/drivers/net/phy/mdio-mux-gpio.c b/drivers/net/phy/mdio-mux-gpio.c
index 8e1ec750277e..c0ffa03c916b 100644
--- a/drivers/net/phy/mdio-mux-gpio.c
+++ b/drivers/net/phy/mdio-mux-gpio.c
@@ -37,7 +37,7 @@ static int mdio_mux_gpio_switch_fn(int current_child, int desired_child,
s->values[n] = (desired_child >> n) & 1;

gpiod_set_array_value_cansleep(s->gpios->ndescs, s->gpios->desc,
- value_bitmap);
+ s->gpios->info, value_bitmap);

return 0;
}
diff --git a/drivers/pcmcia/soc_common.c b/drivers/pcmcia/soc_common.c
index e0f89155c474..55978198cd2b 100644
--- a/drivers/pcmcia/soc_common.c
+++ b/drivers/pcmcia/soc_common.c
@@ -366,7 +366,8 @@ static int soc_common_pcmcia_config_skt(
}

if (n)
- gpiod_set_array_value_cansleep(n, descs, value_bitmap);
+ gpiod_set_array_value_cansleep(n, descs, NULL,
+ value_bitmap);

/*
* This really needs a better solution. The IRQ
diff --git a/drivers/phy/motorola/phy-mapphone-mdm6600.c b/drivers/phy/motorola/phy-mapphone-mdm6600.c
index b6477c3599c4..8f508338ec56 100644
--- a/drivers/phy/motorola/phy-mapphone-mdm6600.c
+++ b/drivers/phy/motorola/phy-mapphone-mdm6600.c
@@ -162,7 +162,8 @@ static void phy_mdm6600_cmd(struct phy_mdm6600 *ddata, int val)
value_bitmap[0] = val & ((1 << PHY_MDM6600_NR_CMD_LINES) - 1);

gpiod_set_array_value_cansleep(PHY_MDM6600_NR_CMD_LINES,
- ddata->cmd_gpios->desc, value_bitmap);
+ ddata->cmd_gpios->desc,
+ ddata->cmd_gpios->info, value_bitmap);
}

/**
@@ -181,6 +182,7 @@ static void phy_mdm6600_status(struct work_struct *work)

error = gpiod_get_array_value_cansleep(PHY_MDM6600_NR_STATUS_LINES,
ddata->status_gpios->desc,
+ ddata->status_gpios->info,
value_bitmap);
if (error)
return;
diff --git a/drivers/staging/iio/adc/ad7606.c b/drivers/staging/iio/adc/ad7606.c
index 0eca047bc1cc..eb779d825724 100644
--- a/drivers/staging/iio/adc/ad7606.c
+++ b/drivers/staging/iio/adc/ad7606.c
@@ -230,7 +230,8 @@ static int ad7606_write_raw(struct iio_dev *indio_dev,
value_bitmap[0] = ret;

mutex_lock(&st->lock);
- gpiod_set_array_value(3, st->gpio_os->desc, value_bitmap);
+ gpiod_set_array_value(3, st->gpio_os->desc, st->gpio_os->info,
+ value_bitmap);
st->oversampling = val;
mutex_unlock(&st->lock);

diff --git a/drivers/tty/serial/serial_mctrl_gpio.c b/drivers/tty/serial/serial_mctrl_gpio.c
index bb8b4756d72d..8a04e3be5419 100644
--- a/drivers/tty/serial/serial_mctrl_gpio.c
+++ b/drivers/tty/serial/serial_mctrl_gpio.c
@@ -53,7 +53,7 @@ void mctrl_gpio_set(struct mctrl_gpios *gpios, unsigned int mctrl)
!!(mctrl & mctrl_gpios_desc[i].mctrl));
count++;
}
- gpiod_set_array_value(count, desc_array, value_bitmap);
+ gpiod_set_array_value(count, desc_array, NULL, value_bitmap);
}
EXPORT_SYMBOL_GPL(mctrl_gpio_set);

diff --git a/include/linux/gpio/consumer.h b/include/linux/gpio/consumer.h
index 8dede3e886af..bf037ebe2ed8 100644
--- a/include/linux/gpio/consumer.h
+++ b/include/linux/gpio/consumer.h
@@ -114,36 +114,44 @@ int gpiod_direction_output_raw(struct gpio_desc *desc, int value);
int gpiod_get_value(const struct gpio_desc *desc);
int gpiod_get_array_value(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap);
void gpiod_set_value(struct gpio_desc *desc, int value);
void gpiod_set_array_value(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap);
int gpiod_get_raw_value(const struct gpio_desc *desc);
int gpiod_get_raw_array_value(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap);
void gpiod_set_raw_value(struct gpio_desc *desc, int value);
int gpiod_set_raw_array_value(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap);

/* Value get/set from sleeping context */
int gpiod_get_value_cansleep(const struct gpio_desc *desc);
int gpiod_get_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap);
void gpiod_set_value_cansleep(struct gpio_desc *desc, int value);
void gpiod_set_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap);
int gpiod_get_raw_value_cansleep(const struct gpio_desc *desc);
int gpiod_get_raw_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap);
void gpiod_set_raw_value_cansleep(struct gpio_desc *desc, int value);
int gpiod_set_raw_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap);

int gpiod_set_debounce(struct gpio_desc *desc, unsigned debounce);
--
2.16.4


2018-08-29 20:50:25

by Janusz Krzysztofik

[permalink] [raw]
Subject: [PATCH v5 1/4] gpiolib: Pass bitmaps, not integer arrays, to get/set array

Most users of get/set array functions iterate consecutive bits of data,
usually a single integer, while processing array of results obtained
from, or building an array of values to be passed to those functions.
Save time wasted on those iterations by changing the functions' API to
accept bitmaps.

All current users are updated as well.

More benefits from the change are expected as soon as planned support
for accepting/passing those bitmaps directly from/to respective GPIO
chip callbacks if applicable is implemented.

Cc: Jonathan Corbet <[email protected]>
Cc: Miguel Ojeda Sandonis <[email protected]>
Cc: Peter Korsgaard <[email protected]>
Cc: Peter Rosin <[email protected]>
Cc: Andrew Lunn <[email protected]>
Cc: Florian Fainelli <[email protected]>
Cc: "David S. Miller" <[email protected]>
Cc: Dominik Brodowski <[email protected]>
Cc: Kishon Vijay Abraham I <[email protected]>
Cc: Lars-Peter Clausen <[email protected]>
Cc: Michael Hennerich <[email protected]>
Cc: Jonathan Cameron <[email protected]>
Cc: Hartmut Knaack <[email protected]>
Cc: Peter Meerwald-Stadler <[email protected]>
Cc: Greg Kroah-Hartman <[email protected]>
Cc: Jiri Slaby <[email protected]>
Signed-off-by: Janusz Krzysztofik <[email protected]>
Acked-by: Ulf Hansson <[email protected]>
---
Documentation/driver-api/gpio/consumer.rst | 22 ++++----
drivers/auxdisplay/hd44780.c | 52 +++++++++--------
drivers/bus/ts-nbus.c | 19 ++-----
drivers/gpio/gpio-max3191x.c | 17 +++---
drivers/gpio/gpiolib.c | 86 +++++++++++++++--------------
drivers/gpio/gpiolib.h | 4 +-
drivers/i2c/muxes/i2c-mux-gpio.c | 8 +--
drivers/mmc/core/pwrseq_simple.c | 13 ++---
drivers/mux/gpio.c | 9 +--
drivers/net/phy/mdio-mux-gpio.c | 3 +-
drivers/pcmcia/soc_common.c | 11 ++--
drivers/phy/motorola/phy-mapphone-mdm6600.c | 17 +++---
drivers/staging/iio/adc/ad7606.c | 9 +--
drivers/tty/serial/serial_mctrl_gpio.c | 7 ++-
include/linux/gpio/consumer.h | 18 +++---
15 files changed, 140 insertions(+), 155 deletions(-)

diff --git a/Documentation/driver-api/gpio/consumer.rst b/Documentation/driver-api/gpio/consumer.rst
index aa03f389d41d..ed68042ddccf 100644
--- a/Documentation/driver-api/gpio/consumer.rst
+++ b/Documentation/driver-api/gpio/consumer.rst
@@ -323,29 +323,29 @@ The following functions get or set the values of an array of GPIOs::

int gpiod_get_array_value(unsigned int array_size,
struct gpio_desc **desc_array,
- int *value_array);
+ unsigned long *value_bitmap);
int gpiod_get_raw_array_value(unsigned int array_size,
struct gpio_desc **desc_array,
- int *value_array);
+ unsigned long *value_bitmap);
int gpiod_get_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
- int *value_array);
+ unsigned long *value_bitmap);
int gpiod_get_raw_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
- int *value_array);
+ unsigned long *value_bitmap);

void gpiod_set_array_value(unsigned int array_size,
struct gpio_desc **desc_array,
- int *value_array)
+ unsigned long *value_bitmap)
void gpiod_set_raw_array_value(unsigned int array_size,
struct gpio_desc **desc_array,
- int *value_array)
+ unsigned long *value_bitmap)
void gpiod_set_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
- int *value_array)
+ unsigned long *value_bitmap)
void gpiod_set_raw_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
- int *value_array)
+ unsigned long *value_bitmap)

The array can be an arbitrary set of GPIOs. The functions will try to access
GPIOs belonging to the same bank or chip simultaneously if supported by the
@@ -356,8 +356,8 @@ accessed sequentially.
The functions take three arguments:
* array_size - the number of array elements
* desc_array - an array of GPIO descriptors
- * value_array - an array to store the GPIOs' values (get) or
- an array of values to assign to the GPIOs (set)
+ * value_bitmap - a bitmap to store the GPIOs' values (get) or
+ a bitmap of values to assign to the GPIOs (set)

The descriptor array can be obtained using the gpiod_get_array() function
or one of its variants. If the group of descriptors returned by that function
@@ -366,7 +366,7 @@ the struct gpio_descs returned by gpiod_get_array()::

struct gpio_descs *my_gpio_descs = gpiod_get_array(...);
gpiod_set_array_value(my_gpio_descs->ndescs, my_gpio_descs->desc,
- my_gpio_values);
+ my_gpio_value_bitmap);

It is also possible to access a completely arbitrary array of descriptors. The
descriptors may be obtained using any combination of gpiod_get() and
diff --git a/drivers/auxdisplay/hd44780.c b/drivers/auxdisplay/hd44780.c
index f1a42f0f1ded..bbbd6a29bf01 100644
--- a/drivers/auxdisplay/hd44780.c
+++ b/drivers/auxdisplay/hd44780.c
@@ -62,20 +62,19 @@ static void hd44780_strobe_gpio(struct hd44780 *hd)
/* write to an LCD panel register in 8 bit GPIO mode */
static void hd44780_write_gpio8(struct hd44780 *hd, u8 val, unsigned int rs)
{
- int values[10]; /* for DATA[0-7], RS, RW */
- unsigned int i, n;
+ unsigned long value_bitmap[1]; /* for DATA[0-7], RS, RW */
+ unsigned int n;

- for (i = 0; i < 8; i++)
- values[PIN_DATA0 + i] = !!(val & BIT(i));
- values[PIN_CTRL_RS] = rs;
+ value_bitmap[0] = val;
+ __assign_bit(PIN_CTRL_RS, value_bitmap, rs);
n = 9;
if (hd->pins[PIN_CTRL_RW]) {
- values[PIN_CTRL_RW] = 0;
+ __clear_bit(PIN_CTRL_RW, value_bitmap);
n++;
}

/* Present the data to the port */
- gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA0], values);
+ gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA0], value_bitmap);

hd44780_strobe_gpio(hd);
}
@@ -83,32 +82,31 @@ static void hd44780_write_gpio8(struct hd44780 *hd, u8 val, unsigned int rs)
/* write to an LCD panel register in 4 bit GPIO mode */
static void hd44780_write_gpio4(struct hd44780 *hd, u8 val, unsigned int rs)
{
- int values[10]; /* for DATA[0-7], RS, RW, but DATA[0-3] is unused */
- unsigned int i, n;
+ /* for DATA[0-7], RS, RW, but DATA[0-3] is unused */
+ unsigned long value_bitmap[1];
+ unsigned int n;

/* High nibble + RS, RW */
- for (i = 4; i < 8; i++)
- values[PIN_DATA0 + i] = !!(val & BIT(i));
- values[PIN_CTRL_RS] = rs;
+ value_bitmap[0] = val;
+ __assign_bit(PIN_CTRL_RS, value_bitmap, rs);
n = 5;
if (hd->pins[PIN_CTRL_RW]) {
- values[PIN_CTRL_RW] = 0;
+ __clear_bit(PIN_CTRL_RW, value_bitmap);
n++;
}
+ value_bitmap[0] >>= PIN_DATA4;

/* Present the data to the port */
- gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA4],
- &values[PIN_DATA4]);
+ gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA4], value_bitmap);

hd44780_strobe_gpio(hd);

/* Low nibble */
- for (i = 0; i < 4; i++)
- values[PIN_DATA4 + i] = !!(val & BIT(i));
+ value_bitmap[0] &= ~((1 << PIN_DATA4) - 1);
+ value_bitmap[0] |= val & ~((1 << PIN_DATA4) - 1);

/* Present the data to the port */
- gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA4],
- &values[PIN_DATA4]);
+ gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA4], value_bitmap);

hd44780_strobe_gpio(hd);
}
@@ -155,23 +153,23 @@ static void hd44780_write_cmd_gpio4(struct charlcd *lcd, int cmd)
/* Send 4-bits of a command to the LCD panel in raw 4 bit GPIO mode */
static void hd44780_write_cmd_raw_gpio4(struct charlcd *lcd, int cmd)
{
- int values[10]; /* for DATA[0-7], RS, RW, but DATA[0-3] is unused */
+ /* for DATA[0-7], RS, RW, but DATA[0-3] is unused */
+ unsigned long value_bitmap[1];
struct hd44780 *hd = lcd->drvdata;
- unsigned int i, n;
+ unsigned int n;

/* Command nibble + RS, RW */
- for (i = 0; i < 4; i++)
- values[PIN_DATA4 + i] = !!(cmd & BIT(i));
- values[PIN_CTRL_RS] = 0;
+ value_bitmap[0] = cmd << PIN_DATA4;
+ __clear_bit(PIN_CTRL_RS, value_bitmap);
n = 5;
if (hd->pins[PIN_CTRL_RW]) {
- values[PIN_CTRL_RW] = 0;
+ __clear_bit(PIN_CTRL_RW, value_bitmap);
n++;
}
+ value_bitmap[0] = value_bitmap[0] >> PIN_DATA4;

/* Present the data to the port */
- gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA4],
- &values[PIN_DATA4]);
+ gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA4], value_bitmap);

hd44780_strobe_gpio(hd);
}
diff --git a/drivers/bus/ts-nbus.c b/drivers/bus/ts-nbus.c
index 073fd9011154..ce6c1e89236d 100644
--- a/drivers/bus/ts-nbus.c
+++ b/drivers/bus/ts-nbus.c
@@ -110,13 +110,9 @@ static void ts_nbus_set_direction(struct ts_nbus *ts_nbus, int direction)
*/
static void ts_nbus_reset_bus(struct ts_nbus *ts_nbus)
{
- int i;
- int values[8];
-
- for (i = 0; i < 8; i++)
- values[i] = 0;
+ unsigned long value_bitmap[1] = { 0, };

- gpiod_set_array_value_cansleep(8, ts_nbus->data->desc, values);
+ gpiod_set_array_value_cansleep(8, ts_nbus->data->desc, value_bitmap);
gpiod_set_value_cansleep(ts_nbus->csn, 0);
gpiod_set_value_cansleep(ts_nbus->strobe, 0);
gpiod_set_value_cansleep(ts_nbus->ale, 0);
@@ -157,16 +153,9 @@ static int ts_nbus_read_byte(struct ts_nbus *ts_nbus, u8 *val)
static void ts_nbus_write_byte(struct ts_nbus *ts_nbus, u8 byte)
{
struct gpio_descs *gpios = ts_nbus->data;
- int i;
- int values[8];
-
- for (i = 0; i < 8; i++)
- if (byte & BIT(i))
- values[i] = 1;
- else
- values[i] = 0;
+ unsigned long value_bitmap[1] = { byte, };

- gpiod_set_array_value_cansleep(8, gpios->desc, values);
+ gpiod_set_array_value_cansleep(8, gpios->desc, value_bitmap);
}

/*
diff --git a/drivers/gpio/gpio-max3191x.c b/drivers/gpio/gpio-max3191x.c
index b5b9cb1fda50..c4ec1c82af27 100644
--- a/drivers/gpio/gpio-max3191x.c
+++ b/drivers/gpio/gpio-max3191x.c
@@ -315,17 +315,20 @@ static void gpiod_set_array_single_value_cansleep(unsigned int ndescs,
struct gpio_desc **desc,
int value)
{
- int i, *values;
+ unsigned long *value_bitmap;

- values = kmalloc_array(ndescs, sizeof(*values), GFP_KERNEL);
- if (!values)
+ value_bitmap = kmalloc_array(BITS_TO_LONGS(ndescs),
+ sizeof(*value_bitmap), GFP_KERNEL);
+ if (!value_bitmap)
return;

- for (i = 0; i < ndescs; i++)
- values[i] = value;
+ if (value)
+ bitmap_fill(value_bitmap, ndescs);
+ else
+ bitmap_zero(value_bitmap, ndescs);

- gpiod_set_array_value_cansleep(ndescs, desc, values);
- kfree(values);
+ gpiod_set_array_value_cansleep(ndescs, desc, value_bitmap);
+ kfree(value_bitmap);
}

static struct gpio_descs *devm_gpiod_get_array_optional_count(
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index e8f8a1999393..f0e9ffa8cab6 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -427,7 +427,7 @@ static long linehandle_ioctl(struct file *filep, unsigned int cmd,
struct linehandle_state *lh = filep->private_data;
void __user *ip = (void __user *)arg;
struct gpiohandle_data ghd;
- int vals[GPIOHANDLES_MAX];
+ unsigned long value_bitmap[BITS_TO_LONGS(GPIOHANDLES_MAX)];
int i;

if (cmd == GPIOHANDLE_GET_LINE_VALUES_IOCTL) {
@@ -436,13 +436,13 @@ static long linehandle_ioctl(struct file *filep, unsigned int cmd,
true,
lh->numdescs,
lh->descs,
- vals);
+ value_bitmap);
if (ret)
return ret;

memset(&ghd, 0, sizeof(ghd));
for (i = 0; i < lh->numdescs; i++)
- ghd.values[i] = vals[i];
+ ghd.values[i] = test_bit(i, value_bitmap);

if (copy_to_user(ip, &ghd, sizeof(ghd)))
return -EFAULT;
@@ -461,14 +461,14 @@ static long linehandle_ioctl(struct file *filep, unsigned int cmd,

/* Clamp all values to [0,1] */
for (i = 0; i < lh->numdescs; i++)
- vals[i] = !!ghd.values[i];
+ __assign_bit(i, value_bitmap, !!ghd.values[i]);

/* Reuse the array setting function */
return gpiod_set_array_value_complex(false,
true,
lh->numdescs,
lh->descs,
- vals);
+ value_bitmap);
}
return -EINVAL;
}
@@ -2784,7 +2784,7 @@ static int gpio_chip_get_multiple(struct gpio_chip *chip,
int gpiod_get_array_value_complex(bool raw, bool can_sleep,
unsigned int array_size,
struct gpio_desc **desc_array,
- int *value_array)
+ unsigned long *value_bitmap)
{
int i = 0;

@@ -2835,7 +2835,7 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep,

if (!raw && test_bit(FLAG_ACTIVE_LOW, &desc->flags))
value = !value;
- value_array[j] = value;
+ __assign_bit(j, value_bitmap, value);
trace_gpio_value(desc_to_gpio(desc), 1, value);
}

@@ -2895,9 +2895,9 @@ EXPORT_SYMBOL_GPL(gpiod_get_value);

/**
* gpiod_get_raw_array_value() - read raw values from an array of GPIOs
- * @array_size: number of elements in the descriptor / value arrays
+ * @array_size: number of elements in the descriptor array / value bitmap
* @desc_array: array of GPIO descriptors whose values will be read
- * @value_array: array to store the read values
+ * @value_bitmap: bitmap to store the read values
*
* Read the raw values of the GPIOs, i.e. the values of the physical lines
* without regard for their ACTIVE_LOW status. Return 0 in case of success,
@@ -2907,20 +2907,21 @@ EXPORT_SYMBOL_GPL(gpiod_get_value);
* and it will complain if the GPIO chip functions potentially sleep.
*/
int gpiod_get_raw_array_value(unsigned int array_size,
- struct gpio_desc **desc_array, int *value_array)
+ struct gpio_desc **desc_array,
+ unsigned long *value_bitmap)
{
if (!desc_array)
return -EINVAL;
return gpiod_get_array_value_complex(true, false, array_size,
- desc_array, value_array);
+ desc_array, value_bitmap);
}
EXPORT_SYMBOL_GPL(gpiod_get_raw_array_value);

/**
* gpiod_get_array_value() - read values from an array of GPIOs
- * @array_size: number of elements in the descriptor / value arrays
+ * @array_size: number of elements in the descriptor array / value bitmap
* @desc_array: array of GPIO descriptors whose values will be read
- * @value_array: array to store the read values
+ * @value_bitnap: bitmap to store the read values
*
* Read the logical values of the GPIOs, i.e. taking their ACTIVE_LOW status
* into account. Return 0 in case of success, else an error code.
@@ -2929,12 +2930,13 @@ EXPORT_SYMBOL_GPL(gpiod_get_raw_array_value);
* and it will complain if the GPIO chip functions potentially sleep.
*/
int gpiod_get_array_value(unsigned int array_size,
- struct gpio_desc **desc_array, int *value_array)
+ struct gpio_desc **desc_array,
+ unsigned long *value_bitmap)
{
if (!desc_array)
return -EINVAL;
return gpiod_get_array_value_complex(false, false, array_size,
- desc_array, value_array);
+ desc_array, value_bitmap);
}
EXPORT_SYMBOL_GPL(gpiod_get_array_value);

@@ -3027,7 +3029,7 @@ static void gpio_chip_set_multiple(struct gpio_chip *chip,
int gpiod_set_array_value_complex(bool raw, bool can_sleep,
unsigned int array_size,
struct gpio_desc **desc_array,
- int *value_array)
+ unsigned long *value_bitmap)
{
int i = 0;

@@ -3056,7 +3058,7 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep,
do {
struct gpio_desc *desc = desc_array[i];
int hwgpio = gpio_chip_hwgpio(desc);
- int value = value_array[i];
+ int value = test_bit(i, value_bitmap);

if (!raw && test_bit(FLAG_ACTIVE_LOW, &desc->flags))
value = !value;
@@ -3152,9 +3154,9 @@ EXPORT_SYMBOL_GPL(gpiod_set_value);

/**
* gpiod_set_raw_array_value() - assign values to an array of GPIOs
- * @array_size: number of elements in the descriptor / value arrays
+ * @array_size: number of elements in the descriptor array / value bitmap
* @desc_array: array of GPIO descriptors whose values will be assigned
- * @value_array: array of values to assign
+ * @value_bitmap: bitmap of values to assign
*
* Set the raw values of the GPIOs, i.e. the values of the physical lines
* without regard for their ACTIVE_LOW status.
@@ -3163,20 +3165,21 @@ EXPORT_SYMBOL_GPL(gpiod_set_value);
* complain if the GPIO chip functions potentially sleep.
*/
int gpiod_set_raw_array_value(unsigned int array_size,
- struct gpio_desc **desc_array, int *value_array)
+ struct gpio_desc **desc_array,
+ unsigned long *value_bitmap)
{
if (!desc_array)
return -EINVAL;
return gpiod_set_array_value_complex(true, false, array_size,
- desc_array, value_array);
+ desc_array, value_bitmap);
}
EXPORT_SYMBOL_GPL(gpiod_set_raw_array_value);

/**
* gpiod_set_array_value() - assign values to an array of GPIOs
- * @array_size: number of elements in the descriptor / value arrays
+ * @array_size: number of elements in the descriptor array / value bitmap
* @desc_array: array of GPIO descriptors whose values will be assigned
- * @value_array: array of values to assign
+ * @value_bitmap: bitmap of values to assign
*
* Set the logical values of the GPIOs, i.e. taking their ACTIVE_LOW status
* into account.
@@ -3185,12 +3188,13 @@ EXPORT_SYMBOL_GPL(gpiod_set_raw_array_value);
* complain if the GPIO chip functions potentially sleep.
*/
void gpiod_set_array_value(unsigned int array_size,
- struct gpio_desc **desc_array, int *value_array)
+ struct gpio_desc **desc_array,
+ unsigned long *value_bitmap)
{
if (!desc_array)
return;
gpiod_set_array_value_complex(false, false, array_size, desc_array,
- value_array);
+ value_bitmap);
}
EXPORT_SYMBOL_GPL(gpiod_set_array_value);

@@ -3410,9 +3414,9 @@ EXPORT_SYMBOL_GPL(gpiod_get_value_cansleep);

/**
* gpiod_get_raw_array_value_cansleep() - read raw values from an array of GPIOs
- * @array_size: number of elements in the descriptor / value arrays
+ * @array_size: number of elements in the descriptor array / value bitmap
* @desc_array: array of GPIO descriptors whose values will be read
- * @value_array: array to store the read values
+ * @value_bitmap: bitmap to store the read values
*
* Read the raw values of the GPIOs, i.e. the values of the physical lines
* without regard for their ACTIVE_LOW status. Return 0 in case of success,
@@ -3422,21 +3426,21 @@ EXPORT_SYMBOL_GPL(gpiod_get_value_cansleep);
*/
int gpiod_get_raw_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
- int *value_array)
+ unsigned long *value_bitmap)
{
might_sleep_if(extra_checks);
if (!desc_array)
return -EINVAL;
return gpiod_get_array_value_complex(true, true, array_size,
- desc_array, value_array);
+ desc_array, value_bitmap);
}
EXPORT_SYMBOL_GPL(gpiod_get_raw_array_value_cansleep);

/**
* gpiod_get_array_value_cansleep() - read values from an array of GPIOs
- * @array_size: number of elements in the descriptor / value arrays
+ * @array_size: number of elements in the descriptor array / value bitmap
* @desc_array: array of GPIO descriptors whose values will be read
- * @value_array: array to store the read values
+ * @value_bitmap: bitmap to store the read values
*
* Read the logical values of the GPIOs, i.e. taking their ACTIVE_LOW status
* into account. Return 0 in case of success, else an error code.
@@ -3445,13 +3449,13 @@ EXPORT_SYMBOL_GPL(gpiod_get_raw_array_value_cansleep);
*/
int gpiod_get_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
- int *value_array)
+ unsigned long *value_bitmap)
{
might_sleep_if(extra_checks);
if (!desc_array)
return -EINVAL;
return gpiod_get_array_value_complex(false, true, array_size,
- desc_array, value_array);
+ desc_array, value_bitmap);
}
EXPORT_SYMBOL_GPL(gpiod_get_array_value_cansleep);

@@ -3493,9 +3497,9 @@ EXPORT_SYMBOL_GPL(gpiod_set_value_cansleep);

/**
* gpiod_set_raw_array_value_cansleep() - assign values to an array of GPIOs
- * @array_size: number of elements in the descriptor / value arrays
+ * @array_size: number of elements in the descriptor array / value bitmap
* @desc_array: array of GPIO descriptors whose values will be assigned
- * @value_array: array of values to assign
+ * @value_bitmap: bitmap of values to assign
*
* Set the raw values of the GPIOs, i.e. the values of the physical lines
* without regard for their ACTIVE_LOW status.
@@ -3504,13 +3508,13 @@ EXPORT_SYMBOL_GPL(gpiod_set_value_cansleep);
*/
int gpiod_set_raw_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
- int *value_array)
+ unsigned long *value_bitmap)
{
might_sleep_if(extra_checks);
if (!desc_array)
return -EINVAL;
return gpiod_set_array_value_complex(true, true, array_size, desc_array,
- value_array);
+ value_bitmap);
}
EXPORT_SYMBOL_GPL(gpiod_set_raw_array_value_cansleep);

@@ -3533,9 +3537,9 @@ void gpiod_add_lookup_tables(struct gpiod_lookup_table **tables, size_t n)

/**
* gpiod_set_array_value_cansleep() - assign values to an array of GPIOs
- * @array_size: number of elements in the descriptor / value arrays
+ * @array_size: number of elements in the descriptor array / value bitmap
* @desc_array: array of GPIO descriptors whose values will be assigned
- * @value_array: array of values to assign
+ * @value_bitmap: bitmap of values to assign
*
* Set the logical values of the GPIOs, i.e. taking their ACTIVE_LOW status
* into account.
@@ -3544,13 +3548,13 @@ void gpiod_add_lookup_tables(struct gpiod_lookup_table **tables, size_t n)
*/
void gpiod_set_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
- int *value_array)
+ unsigned long *value_bitmap)
{
might_sleep_if(extra_checks);
if (!desc_array)
return;
gpiod_set_array_value_complex(false, true, array_size, desc_array,
- value_array);
+ value_bitmap);
}
EXPORT_SYMBOL_GPL(gpiod_set_array_value_cansleep);

diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h
index a7e49fef73d4..11e83d2eef89 100644
--- a/drivers/gpio/gpiolib.h
+++ b/drivers/gpio/gpiolib.h
@@ -187,11 +187,11 @@ struct gpio_desc *gpiochip_get_desc(struct gpio_chip *chip, u16 hwnum);
int gpiod_get_array_value_complex(bool raw, bool can_sleep,
unsigned int array_size,
struct gpio_desc **desc_array,
- int *value_array);
+ unsigned long *value_bitmap);
int gpiod_set_array_value_complex(bool raw, bool can_sleep,
unsigned int array_size,
struct gpio_desc **desc_array,
- int *value_array);
+ unsigned long *value_bitmap);

/* This is just passed between gpiolib and devres */
struct gpio_desc *gpiod_get_from_of_node(struct device_node *node,
diff --git a/drivers/i2c/muxes/i2c-mux-gpio.c b/drivers/i2c/muxes/i2c-mux-gpio.c
index 401308e3d036..4e36e0eac7a3 100644
--- a/drivers/i2c/muxes/i2c-mux-gpio.c
+++ b/drivers/i2c/muxes/i2c-mux-gpio.c
@@ -22,18 +22,16 @@ struct gpiomux {
struct i2c_mux_gpio_platform_data data;
unsigned gpio_base;
struct gpio_desc **gpios;
- int *values;
+ int *values; /* FIXME: no longer needed */
};

static void i2c_mux_gpio_set(const struct gpiomux *mux, unsigned val)
{
+ unsigned long value_bitmap[1] = { val, };
int i;

- for (i = 0; i < mux->data.n_gpios; i++)
- mux->values[i] = (val >> i) & 1;
-
gpiod_set_array_value_cansleep(mux->data.n_gpios,
- mux->gpios, mux->values);
+ mux->gpios, value_bitmap);
}

static int i2c_mux_gpio_select(struct i2c_mux_core *muxc, u32 chan)
diff --git a/drivers/mmc/core/pwrseq_simple.c b/drivers/mmc/core/pwrseq_simple.c
index a8b9fee4d62a..0d6e3a5be3ba 100644
--- a/drivers/mmc/core/pwrseq_simple.c
+++ b/drivers/mmc/core/pwrseq_simple.c
@@ -40,18 +40,13 @@ static void mmc_pwrseq_simple_set_gpios_value(struct mmc_pwrseq_simple *pwrseq,
struct gpio_descs *reset_gpios = pwrseq->reset_gpios;

if (!IS_ERR(reset_gpios)) {
- int i, *values;
+ unsigned long value_bitmap[1];
int nvalues = reset_gpios->ndescs;

- values = kmalloc_array(nvalues, sizeof(int), GFP_KERNEL);
- if (!values)
- return;
+ value_bitmap[0] = value;

- for (i = 0; i < nvalues; i++)
- values[i] = value;
-
- gpiod_set_array_value_cansleep(nvalues, reset_gpios->desc, values);
- kfree(values);
+ gpiod_set_array_value_cansleep(nvalues, reset_gpios->desc,
+ value_bitmap);
}
}

diff --git a/drivers/mux/gpio.c b/drivers/mux/gpio.c
index 6fdd9316db8b..734e1b43aed6 100644
--- a/drivers/mux/gpio.c
+++ b/drivers/mux/gpio.c
@@ -17,20 +17,17 @@

struct mux_gpio {
struct gpio_descs *gpios;
- int *val;
+ int *val; /* FIXME: no longer needed */
};

static int mux_gpio_set(struct mux_control *mux, int state)
{
struct mux_gpio *mux_gpio = mux_chip_priv(mux->chip);
+ unsigned long value_bitmap[1] = { state, };
int i;

- for (i = 0; i < mux_gpio->gpios->ndescs; i++)
- mux_gpio->val[i] = (state >> i) & 1;
-
gpiod_set_array_value_cansleep(mux_gpio->gpios->ndescs,
- mux_gpio->gpios->desc,
- mux_gpio->val);
+ mux_gpio->gpios->desc, value_bitmap);

return 0;
}
diff --git a/drivers/net/phy/mdio-mux-gpio.c b/drivers/net/phy/mdio-mux-gpio.c
index bc90764a8b8d..8e1ec750277e 100644
--- a/drivers/net/phy/mdio-mux-gpio.c
+++ b/drivers/net/phy/mdio-mux-gpio.c
@@ -27,6 +27,7 @@ static int mdio_mux_gpio_switch_fn(int current_child, int desired_child,
void *data)
{
struct mdio_mux_gpio_state *s = data;
+ unsigned long value_bitmap[1] = { desired_child, };
unsigned int n;

if (current_child == desired_child)
@@ -36,7 +37,7 @@ static int mdio_mux_gpio_switch_fn(int current_child, int desired_child,
s->values[n] = (desired_child >> n) & 1;

gpiod_set_array_value_cansleep(s->gpios->ndescs, s->gpios->desc,
- s->values);
+ value_bitmap);

return 0;
}
diff --git a/drivers/pcmcia/soc_common.c b/drivers/pcmcia/soc_common.c
index c5f2344c189b..e0f89155c474 100644
--- a/drivers/pcmcia/soc_common.c
+++ b/drivers/pcmcia/soc_common.c
@@ -351,19 +351,22 @@ static int soc_common_pcmcia_config_skt(

if (ret == 0) {
struct gpio_desc *descs[2];
- int values[2], n = 0;
+ unsigned long value_bitmap[1];
+ int n = 0;

if (skt->gpio_reset) {
descs[n] = skt->gpio_reset;
- values[n++] = !!(state->flags & SS_RESET);
+ __assign_bit(n++, value_bitmap,
+ !!(state->flags & SS_RESET));
}
if (skt->gpio_bus_enable) {
descs[n] = skt->gpio_bus_enable;
- values[n++] = !!(state->flags & SS_OUTPUT_ENA);
+ __assign_bit(n++, value_bitmap,
+ !!(state->flags & SS_OUTPUT_ENA));
}

if (n)
- gpiod_set_array_value_cansleep(n, descs, values);
+ gpiod_set_array_value_cansleep(n, descs, value_bitmap);

/*
* This really needs a better solution. The IRQ
diff --git a/drivers/phy/motorola/phy-mapphone-mdm6600.c b/drivers/phy/motorola/phy-mapphone-mdm6600.c
index 0075fb0bef8c..b6477c3599c4 100644
--- a/drivers/phy/motorola/phy-mapphone-mdm6600.c
+++ b/drivers/phy/motorola/phy-mapphone-mdm6600.c
@@ -157,15 +157,12 @@ static const struct phy_ops gpio_usb_ops = {
*/
static void phy_mdm6600_cmd(struct phy_mdm6600 *ddata, int val)
{
- int values[PHY_MDM6600_NR_CMD_LINES];
- int i;
+ unsigned long value_bitmap[1];

- val &= (1 << PHY_MDM6600_NR_CMD_LINES) - 1;
- for (i = 0; i < PHY_MDM6600_NR_CMD_LINES; i++)
- values[i] = (val & BIT(i)) >> i;
+ value_bitmap[0] = val & ((1 << PHY_MDM6600_NR_CMD_LINES) - 1);

gpiod_set_array_value_cansleep(PHY_MDM6600_NR_CMD_LINES,
- ddata->cmd_gpios->desc, values);
+ ddata->cmd_gpios->desc, value_bitmap);
}

/**
@@ -176,7 +173,7 @@ static void phy_mdm6600_status(struct work_struct *work)
{
struct phy_mdm6600 *ddata;
struct device *dev;
- int values[PHY_MDM6600_NR_STATUS_LINES];
+ unsigned long value_bitmap[1] = { 0, };
int error, i, val = 0;

ddata = container_of(work, struct phy_mdm6600, status_work.work);
@@ -184,14 +181,14 @@ static void phy_mdm6600_status(struct work_struct *work)

error = gpiod_get_array_value_cansleep(PHY_MDM6600_NR_STATUS_LINES,
ddata->status_gpios->desc,
- values);
+ value_bitmap);
if (error)
return;

for (i = 0; i < PHY_MDM6600_NR_STATUS_LINES; i++) {
- val |= values[i] << i;
+ val |= test_bit(i, value_bitmap) << i;
dev_dbg(ddata->dev, "XXX %s: i: %i values[i]: %i val: %i\n",
- __func__, i, values[i], val);
+ __func__, i, test_bit(i, value_bitmap), val);
}
ddata->status = val;

diff --git a/drivers/staging/iio/adc/ad7606.c b/drivers/staging/iio/adc/ad7606.c
index 25b9fcd5e3a4..0eca047bc1cc 100644
--- a/drivers/staging/iio/adc/ad7606.c
+++ b/drivers/staging/iio/adc/ad7606.c
@@ -202,7 +202,7 @@ static int ad7606_write_raw(struct iio_dev *indio_dev,
long mask)
{
struct ad7606_state *st = iio_priv(indio_dev);
- int values[3];
+ unsigned long value_bitmap[1];
int ret, i;

switch (mask) {
@@ -227,13 +227,10 @@ static int ad7606_write_raw(struct iio_dev *indio_dev,
if (ret < 0)
return ret;

- values[0] = (ret >> 0) & 1;
- values[1] = (ret >> 1) & 1;
- values[2] = (ret >> 2) & 1;
+ value_bitmap[0] = ret;

mutex_lock(&st->lock);
- gpiod_set_array_value(ARRAY_SIZE(values), st->gpio_os->desc,
- values);
+ gpiod_set_array_value(3, st->gpio_os->desc, value_bitmap);
st->oversampling = val;
mutex_unlock(&st->lock);

diff --git a/drivers/tty/serial/serial_mctrl_gpio.c b/drivers/tty/serial/serial_mctrl_gpio.c
index 1c06325beaca..bb8b4756d72d 100644
--- a/drivers/tty/serial/serial_mctrl_gpio.c
+++ b/drivers/tty/serial/serial_mctrl_gpio.c
@@ -40,7 +40,7 @@ void mctrl_gpio_set(struct mctrl_gpios *gpios, unsigned int mctrl)
{
enum mctrl_gpio_idx i;
struct gpio_desc *desc_array[UART_GPIO_MAX];
- int value_array[UART_GPIO_MAX];
+ unsigned long value_bitmap[BITS_TO_LONGS(UART_GPIO_MAX)];
unsigned int count = 0;

if (gpios == NULL)
@@ -49,10 +49,11 @@ void mctrl_gpio_set(struct mctrl_gpios *gpios, unsigned int mctrl)
for (i = 0; i < UART_GPIO_MAX; i++)
if (gpios->gpio[i] && mctrl_gpios_desc[i].dir_out) {
desc_array[count] = gpios->gpio[i];
- value_array[count] = !!(mctrl & mctrl_gpios_desc[i].mctrl);
+ __assign_bit(count, value_bitmap,
+ !!(mctrl & mctrl_gpios_desc[i].mctrl));
count++;
}
- gpiod_set_array_value(count, desc_array, value_array);
+ gpiod_set_array_value(count, desc_array, value_bitmap);
}
EXPORT_SYMBOL_GPL(mctrl_gpio_set);

diff --git a/include/linux/gpio/consumer.h b/include/linux/gpio/consumer.h
index 21ddbe440030..1b21dc7b0fad 100644
--- a/include/linux/gpio/consumer.h
+++ b/include/linux/gpio/consumer.h
@@ -104,36 +104,38 @@ int gpiod_direction_output_raw(struct gpio_desc *desc, int value);
/* Value get/set from non-sleeping context */
int gpiod_get_value(const struct gpio_desc *desc);
int gpiod_get_array_value(unsigned int array_size,
- struct gpio_desc **desc_array, int *value_array);
+ struct gpio_desc **desc_array,
+ unsigned long *value_bitmap);
void gpiod_set_value(struct gpio_desc *desc, int value);
void gpiod_set_array_value(unsigned int array_size,
- struct gpio_desc **desc_array, int *value_array);
+ struct gpio_desc **desc_array,
+ unsigned long *value_bitmap);
int gpiod_get_raw_value(const struct gpio_desc *desc);
int gpiod_get_raw_array_value(unsigned int array_size,
struct gpio_desc **desc_array,
- int *value_array);
+ unsigned long *value_bitmap);
void gpiod_set_raw_value(struct gpio_desc *desc, int value);
int gpiod_set_raw_array_value(unsigned int array_size,
struct gpio_desc **desc_array,
- int *value_array);
+ unsigned long *value_bitmap);

/* Value get/set from sleeping context */
int gpiod_get_value_cansleep(const struct gpio_desc *desc);
int gpiod_get_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
- int *value_array);
+ unsigned long *value_bitmap);
void gpiod_set_value_cansleep(struct gpio_desc *desc, int value);
void gpiod_set_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
- int *value_array);
+ unsigned long *value_bitmap);
int gpiod_get_raw_value_cansleep(const struct gpio_desc *desc);
int gpiod_get_raw_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
- int *value_array);
+ unsigned long *value_bitmap);
void gpiod_set_raw_value_cansleep(struct gpio_desc *desc, int value);
int gpiod_set_raw_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
- int *value_array);
+ unsigned long *value_bitmap);

int gpiod_set_debounce(struct gpio_desc *desc, unsigned debounce);
int gpiod_set_transitory(struct gpio_desc *desc, bool transitory);
--
2.16.4


2018-08-29 20:50:37

by Janusz Krzysztofik

[permalink] [raw]
Subject: [PATCH v5 4/4] gpiolib: Implement fast processing path in get/set array

Certain GPIO descriptor arrays returned by gpio_get_array() may contain
information on direct mapping of array members to pins of a single GPIO
chip in hardware order. In such cases, bitmaps of values can be passed
directly from/to the chip's .get/set_multiple() callbacks without
wasting time on iterations.

Add respective code to gpiod_get/set_array_bitmap_complex() functions.
Pins not applicable for fast path are processed as before, skipping
over the 'fast' ones.

Cc: Jonathan Corbet <[email protected]>
Signed-off-by: Janusz Krzysztofik <[email protected]>
---
Documentation/driver-api/gpio/board.rst | 15 ++++++
Documentation/driver-api/gpio/consumer.rst | 8 +++
drivers/gpio/gpiolib.c | 87 ++++++++++++++++++++++++++++--
3 files changed, 105 insertions(+), 5 deletions(-)

diff --git a/Documentation/driver-api/gpio/board.rst b/Documentation/driver-api/gpio/board.rst
index 2c112553df84..c66821e033c2 100644
--- a/Documentation/driver-api/gpio/board.rst
+++ b/Documentation/driver-api/gpio/board.rst
@@ -193,3 +193,18 @@ And the table can be added to the board code as follows::

The line will be hogged as soon as the gpiochip is created or - in case the
chip was created earlier - when the hog table is registered.
+
+Arrays of pins
+--------------
+In addition to requesting pins belonging to a function one by one, a device may
+also request an array of pins assigned to the function. The way those pins are
+mapped to the device determines if the array qualifies for fast bitmap
+processing. If yes, a bitmap is passed over get/set array functions directly
+between a caller and a respective .get/set_multiple() callback of a GPIO chip.
+
+In order to qualify for fast bitmap processing, the pin mapping must meet the
+following requirements:
+- it must belong to the same chip as other 'fast' pins of the function,
+- its index within the function must match its hardware number within the chip.
+
+Open drain and open source pins are excluded from fast bitmap output processing.
diff --git a/Documentation/driver-api/gpio/consumer.rst b/Documentation/driver-api/gpio/consumer.rst
index 0afd95a12b10..cf992e5ab976 100644
--- a/Documentation/driver-api/gpio/consumer.rst
+++ b/Documentation/driver-api/gpio/consumer.rst
@@ -388,6 +388,14 @@ array_info should be set to NULL.
Note that for optimal performance GPIOs belonging to the same chip should be
contiguous within the array of descriptors.

+Still better performance may be achieved if array indexes of the descriptors
+match hardware pin numbers of a single chip. If an array passed to a get/set
+array function matches the one obtained from gpiod_get_array() and array_info
+associated with the array is also passed, the function may take a fast bitmap
+processing path, passing the value_bitmap argument directly to the respective
+.get/set_multiple() callback of the chip. That allows for utilization of GPIO
+banks as data I/O ports without much loss of performance.
+
The return value of gpiod_get_array_value() and its variants is 0 on success
or negative on error. Note the difference to gpiod_get_value(), which returns
0 or 1 on success to convey the GPIO value. With the array functions, the GPIO
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index 4d26cdbdb7cf..b799a89c4c17 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -2787,7 +2787,36 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep,
struct gpio_array *array_info,
unsigned long *value_bitmap)
{
- int i = 0;
+ int err, i = 0;
+
+ /*
+ * Validate array_info against desc_array and its size.
+ * It should immediately follow desc_array if both
+ * have been obtained from the same gpiod_get_array() call.
+ */
+ if (array_info && array_info->desc == desc_array &&
+ array_size <= array_info->size &&
+ (void *)array_info == desc_array + array_info->size) {
+ if (!can_sleep)
+ WARN_ON(array_info->chip->can_sleep);
+
+ err = gpio_chip_get_multiple(array_info->chip,
+ array_info->get_mask,
+ value_bitmap);
+ if (err)
+ return err;
+
+ if (!raw && !bitmap_empty(array_info->invert_mask, array_size))
+ bitmap_xor(value_bitmap, value_bitmap,
+ array_info->invert_mask, array_size);
+
+ if (bitmap_full(array_info->get_mask, array_size))
+ return 0;
+
+ i = find_first_zero_bit(array_info->get_mask, array_size);
+ } else {
+ array_info = NULL;
+ }

while (i < array_size) {
struct gpio_chip *chip = desc_array[i]->gdev->chip;
@@ -2818,7 +2847,12 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep,
int hwgpio = gpio_chip_hwgpio(desc);

__set_bit(hwgpio, mask);
- i++;
+
+ if (array_info)
+ find_next_zero_bit(array_info->get_mask,
+ array_size, i);
+ else
+ i++;
} while ((i < array_size) &&
(desc_array[i]->gdev->chip == chip));

@@ -2829,7 +2863,7 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep,
return ret;
}

- for (j = first; j < i; j++) {
+ for (j = first; j < i; ) {
const struct gpio_desc *desc = desc_array[j];
int hwgpio = gpio_chip_hwgpio(desc);
int value = test_bit(hwgpio, bits);
@@ -2838,6 +2872,11 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep,
value = !value;
__assign_bit(j, value_bitmap, value);
trace_gpio_value(desc_to_gpio(desc), 1, value);
+
+ if (array_info)
+ find_next_zero_bit(array_info->get_mask, i, j);
+ else
+ j++;
}

if (mask != fastpath)
@@ -3039,6 +3078,32 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep,
{
int i = 0;

+ /*
+ * Validate array_info against desc_array and its size.
+ * It should immediately follow desc_array if both
+ * have been obtained from the same gpiod_get_array() call.
+ */
+ if (array_info && array_info->desc == desc_array &&
+ array_size <= array_info->size &&
+ (void *)array_info == desc_array + array_info->size) {
+ if (!can_sleep)
+ WARN_ON(array_info->chip->can_sleep);
+
+ if (!raw && !bitmap_empty(array_info->invert_mask, array_size))
+ bitmap_xor(value_bitmap, value_bitmap,
+ array_info->invert_mask, array_size);
+
+ gpio_chip_set_multiple(array_info->chip, array_info->set_mask,
+ value_bitmap);
+
+ if (bitmap_full(array_info->set_mask, array_size))
+ return 0;
+
+ i = find_first_zero_bit(array_info->set_mask, array_size);
+ } else {
+ array_info = NULL;
+ }
+
while (i < array_size) {
struct gpio_chip *chip = desc_array[i]->gdev->chip;
unsigned long fastpath[2 * BITS_TO_LONGS(FASTPATH_NGPIO)];
@@ -3066,7 +3131,14 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep,
int hwgpio = gpio_chip_hwgpio(desc);
int value = test_bit(i, value_bitmap);

- if (!raw && test_bit(FLAG_ACTIVE_LOW, &desc->flags))
+ /*
+ * Pins applicable for fast input but not for
+ * fast output processing may have been already
+ * inverted inside the fast path, skip them.
+ */
+ if (!raw && !(array_info &&
+ test_bit(i, array_info->invert_mask)) &&
+ test_bit(FLAG_ACTIVE_LOW, &desc->flags))
value = !value;
trace_gpio_value(desc_to_gpio(desc), 0, value);
/*
@@ -3085,7 +3157,12 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep,
__clear_bit(hwgpio, bits);
count++;
}
- i++;
+
+ if (array_info)
+ find_next_zero_bit(array_info->set_mask,
+ array_size, i);
+ else
+ i++;
} while ((i < array_size) &&
(desc_array[i]->gdev->chip == chip));
/* push collected bits to outputs */
--
2.16.4


2018-08-29 20:51:26

by Janusz Krzysztofik

[permalink] [raw]
Subject: [PATCH v5 2/4] gpiolib: Identify arrays matching GPIO hardware

Certain GPIO array lookup results may map directly to GPIO pins of a
single GPIO chip in hardware order. If that condition is recognized
and handled efficiently, significant performance gain of get/set array
functions may be possible.

While processing a request for an array of GPIO descriptors, identify
those which represent corresponding pins of a single GPIO chip. Skip
over pins which require open source or open drain special processing.
Moreover, identify pins which require inversion. Pass a pointer to
that information with the array to the caller so it can benefit from
enhanced performance as soon as get/set array functions can accept and
make efficient use of it.

Cc: Jonathan Corbet <[email protected]>
Signed-off-by: Janusz Krzysztofik <[email protected]>
---
Documentation/driver-api/gpio/consumer.rst | 4 +-
drivers/gpio/gpiolib.c | 72 +++++++++++++++++++++++++++++-
drivers/gpio/gpiolib.h | 9 ++++
include/linux/gpio/consumer.h | 9 ++++
4 files changed, 92 insertions(+), 2 deletions(-)

diff --git a/Documentation/driver-api/gpio/consumer.rst b/Documentation/driver-api/gpio/consumer.rst
index ed68042ddccf..7e0298b9a7b9 100644
--- a/Documentation/driver-api/gpio/consumer.rst
+++ b/Documentation/driver-api/gpio/consumer.rst
@@ -109,9 +109,11 @@ For a function using multiple GPIOs all of those can be obtained with one call::
enum gpiod_flags flags)

This function returns a struct gpio_descs which contains an array of
-descriptors::
+descriptors. It also contains a pointer to a gpiolib private structure which,
+if passed back to get/set array functions, may speed up I/O proocessing::

struct gpio_descs {
+ struct gpio_array *info;
unsigned int ndescs;
struct gpio_desc *desc[];
}
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index f0e9ffa8cab6..c1ed1c759345 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -4174,7 +4174,9 @@ struct gpio_descs *__must_check gpiod_get_array(struct device *dev,
{
struct gpio_desc *desc;
struct gpio_descs *descs;
- int count;
+ struct gpio_array *array_info = NULL;
+ struct gpio_chip *chip;
+ int count, bitmap_size;

count = gpiod_count(dev, con_id);
if (count < 0)
@@ -4190,9 +4192,77 @@ struct gpio_descs *__must_check gpiod_get_array(struct device *dev,
gpiod_put_array(descs);
return ERR_CAST(desc);
}
+
descs->desc[descs->ndescs] = desc;
+
+ chip = gpiod_to_chip(desc);
+ /*
+ * Select a chip of first array member
+ * whose index matches its pin hardware number
+ * as a candidate for fast bitmap processing.
+ */
+ if (!array_info && gpio_chip_hwgpio(desc) == descs->ndescs) {
+ struct gpio_descs *array;
+
+ bitmap_size = BITS_TO_LONGS(chip->ngpio > count ?
+ chip->ngpio : count);
+
+ array = kzalloc(struct_size(descs, desc, count) +
+ struct_size(array_info, invert_mask,
+ 3 * bitmap_size), GFP_KERNEL);
+ if (!array) {
+ gpiod_put_array(descs);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ memcpy(array, descs,
+ struct_size(descs, desc, descs->ndescs + 1));
+ kfree(descs);
+
+ descs = array;
+ array_info = (void *)(descs->desc + count);
+ array_info->get_mask = array_info->invert_mask +
+ bitmap_size;
+ array_info->set_mask = array_info->get_mask +
+ bitmap_size;
+
+ array_info->desc = descs->desc;
+ array_info->size = count;
+ array_info->chip = chip;
+ bitmap_set(array_info->get_mask, descs->ndescs,
+ count - descs->ndescs);
+ bitmap_set(array_info->set_mask, descs->ndescs,
+ count - descs->ndescs);
+ descs->info = array_info;
+ }
+ /*
+ * Unmark members which don't qualify for fast bitmap
+ * processing (different chip, not in hardware order)
+ */
+ if (array_info && (chip != array_info->chip ||
+ gpio_chip_hwgpio(desc) != descs->ndescs)) {
+ __clear_bit(descs->ndescs, array_info->get_mask);
+ __clear_bit(descs->ndescs, array_info->set_mask);
+ } else if (array_info) {
+ /* Exclude open drain or open source from fast output */
+ if (gpiochip_line_is_open_drain(chip, descs->ndescs) ||
+ gpiochip_line_is_open_source(chip, descs->ndescs))
+ __clear_bit(descs->ndescs,
+ array_info->set_mask);
+ /* Identify 'fast' pins which require invertion */
+ if (gpiod_is_active_low(desc))
+ __set_bit(descs->ndescs,
+ array_info->invert_mask);
+ }
+
descs->ndescs++;
}
+ if (array_info)
+ dev_dbg(dev,
+ "GPIO array info: chip=%s, size=%d, get_mask=%lx, set_mask=%lx, invert_mask=%lx\n",
+ array_info->chip->label, array_info->size,
+ *array_info->get_mask, *array_info->set_mask,
+ *array_info->invert_mask);
return descs;
}
EXPORT_SYMBOL_GPL(gpiod_get_array);
diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h
index 11e83d2eef89..b60905d558b1 100644
--- a/drivers/gpio/gpiolib.h
+++ b/drivers/gpio/gpiolib.h
@@ -183,6 +183,15 @@ static inline bool acpi_can_fallback_to_crs(struct acpi_device *adev,
}
#endif

+struct gpio_array {
+ struct gpio_desc **desc;
+ unsigned int size;
+ struct gpio_chip *chip;
+ unsigned long *get_mask;
+ unsigned long *set_mask;
+ unsigned long invert_mask[];
+};
+
struct gpio_desc *gpiochip_get_desc(struct gpio_chip *chip, u16 hwnum);
int gpiod_get_array_value_complex(bool raw, bool can_sleep,
unsigned int array_size,
diff --git a/include/linux/gpio/consumer.h b/include/linux/gpio/consumer.h
index 1b21dc7b0fad..8dede3e886af 100644
--- a/include/linux/gpio/consumer.h
+++ b/include/linux/gpio/consumer.h
@@ -17,11 +17,20 @@ struct device;
*/
struct gpio_desc;

+/**
+ * Opaque descriptor for a structure of GPIO array attributes. This structure
+ * is attached to struct gpiod_descs obtained from gpiod_get_array() and can be
+ * passed back to get/set array functions in order to activate fast processing
+ * path if applicable.
+ */
+struct gpio_array;
+
/**
* Struct containing an array of descriptors that can be obtained using
* gpiod_get_array().
*/
struct gpio_descs {
+ struct gpio_array *info;
unsigned int ndescs;
struct gpio_desc *desc[];
};
--
2.16.4


2018-08-30 04:31:48

by Peter Rosin

[permalink] [raw]
Subject: Re: [PATCH v5 1/4] gpiolib: Pass bitmaps, not integer arrays, to get/set array

On 2018-08-29 22:48, Janusz Krzysztofik wrote:
> Most users of get/set array functions iterate consecutive bits of data,
> usually a single integer, while processing array of results obtained
> from, or building an array of values to be passed to those functions.
> Save time wasted on those iterations by changing the functions' API to
> accept bitmaps.
>
> All current users are updated as well.
>
> More benefits from the change are expected as soon as planned support
> for accepting/passing those bitmaps directly from/to respective GPIO
> chip callbacks if applicable is implemented.
> diff --git a/drivers/i2c/muxes/i2c-mux-gpio.c b/drivers/i2c/muxes/i2c-mux-gpio.c
> index 401308e3d036..4e36e0eac7a3 100644
> --- a/drivers/i2c/muxes/i2c-mux-gpio.c
> +++ b/drivers/i2c/muxes/i2c-mux-gpio.c
> @@ -22,18 +22,16 @@ struct gpiomux {
> struct i2c_mux_gpio_platform_data data;
> unsigned gpio_base;
> struct gpio_desc **gpios;
> - int *values;
> + int *values; /* FIXME: no longer needed */

That's a half-measure...

> };
>
> static void i2c_mux_gpio_set(const struct gpiomux *mux, unsigned val)
> {
> + unsigned long value_bitmap[1] = { val, };
> int i;

...and i is no longer needed. Hmm, I'd expect a warning about that?

>
> - for (i = 0; i < mux->data.n_gpios; i++)
> - mux->values[i] = (val >> i) & 1;
> -
> gpiod_set_array_value_cansleep(mux->data.n_gpios,
> - mux->gpios, mux->values);
> + mux->gpios, value_bitmap);
> }
>
> static int i2c_mux_gpio_select(struct i2c_mux_core *muxc, u32 chan)
> diff --git a/drivers/mux/gpio.c b/drivers/mux/gpio.c
> index 6fdd9316db8b..734e1b43aed6 100644
> --- a/drivers/mux/gpio.c
> +++ b/drivers/mux/gpio.c
> @@ -17,20 +17,17 @@
>
> struct mux_gpio {
> struct gpio_descs *gpios;
> - int *val;
> + int *val; /* FIXME: no longer needed */
> };
>
> static int mux_gpio_set(struct mux_control *mux, int state)
> {
> struct mux_gpio *mux_gpio = mux_chip_priv(mux->chip);
> + unsigned long value_bitmap[1] = { state, };
> int i;
>
> - for (i = 0; i < mux_gpio->gpios->ndescs; i++)
> - mux_gpio->val[i] = (state >> i) & 1;
> -
> gpiod_set_array_value_cansleep(mux_gpio->gpios->ndescs,
> - mux_gpio->gpios->desc,
> - mux_gpio->val);
> + mux_gpio->gpios->desc, value_bitmap);
>
> return 0;
> }

Dito for this driver. Just squash the following and be done with
it, no attribution needed...

With that (for the changes in i2c-mux-gpio.c and mux/gpio.c)

Reviewed-by: Peter Rosin <[email protected]>

Linus, I expect this will will end up in some immutable branch for me
to pick up, in case I need to? Not that I expect further work to clash
in these two drivers, but...

Cheers,
Peter

diff --git a/drivers/i2c/muxes/i2c-mux-gpio.c b/drivers/i2c/muxes/i2c-mux-gpio.c
index 4e36e0eac7a3..06a89a29250a 100644
--- a/drivers/i2c/muxes/i2c-mux-gpio.c
+++ b/drivers/i2c/muxes/i2c-mux-gpio.c
@@ -22,13 +22,11 @@ struct gpiomux {
struct i2c_mux_gpio_platform_data data;
unsigned gpio_base;
struct gpio_desc **gpios;
- int *values; /* FIXME: no longer needed */
};

static void i2c_mux_gpio_set(const struct gpiomux *mux, unsigned val)
{
unsigned long value_bitmap[1] = { val, };
- int i;

gpiod_set_array_value_cansleep(mux->data.n_gpios,
mux->gpios, value_bitmap);
@@ -180,15 +178,13 @@ static int i2c_mux_gpio_probe(struct platform_device *pdev)
return -EPROBE_DEFER;

muxc = i2c_mux_alloc(parent, &pdev->dev, mux->data.n_values,
- mux->data.n_gpios * sizeof(*mux->gpios) +
- mux->data.n_gpios * sizeof(*mux->values), 0,
+ mux->data.n_gpios * sizeof(*mux->gpios), 0,
i2c_mux_gpio_select, NULL);
if (!muxc) {
ret = -ENOMEM;
goto alloc_failed;
}
mux->gpios = muxc->priv;
- mux->values = (int *)(mux->gpios + mux->data.n_gpios);
muxc->priv = mux;

platform_set_drvdata(pdev, muxc);
diff --git a/drivers/mux/gpio.c b/drivers/mux/gpio.c
index 734e1b43aed6..eb1798677dfd 100644
--- a/drivers/mux/gpio.c
+++ b/drivers/mux/gpio.c
@@ -17,14 +17,12 @@

struct mux_gpio {
struct gpio_descs *gpios;
- int *val; /* FIXME: no longer needed */
};

static int mux_gpio_set(struct mux_control *mux, int state)
{
struct mux_gpio *mux_gpio = mux_chip_priv(mux->chip);
unsigned long value_bitmap[1] = { state, };
- int i;

gpiod_set_array_value_cansleep(mux_gpio->gpios->ndescs,
mux_gpio->gpios->desc, value_bitmap);
@@ -55,13 +53,11 @@ static int mux_gpio_probe(struct platform_device *pdev)
if (pins < 0)
return pins;

- mux_chip = devm_mux_chip_alloc(dev, 1, sizeof(*mux_gpio) +
- pins * sizeof(*mux_gpio->val));
+ mux_chip = devm_mux_chip_alloc(dev, 1, sizeof(*mux_gpio));
if (IS_ERR(mux_chip))
return PTR_ERR(mux_chip);

mux_gpio = mux_chip_priv(mux_chip);
- mux_gpio->val = (int *)(mux_gpio + 1);
mux_chip->ops = &mux_gpio_ops;

mux_gpio->gpios = devm_gpiod_get_array(dev, "mux", GPIOD_OUT_LOW);

2018-08-30 07:43:07

by Geert Uytterhoeven

[permalink] [raw]
Subject: Re: [PATCH v5 1/4] gpiolib: Pass bitmaps, not integer arrays, to get/set array

Hi Janusz,

On Wed, Aug 29, 2018 at 10:48 PM Janusz Krzysztofik <[email protected]> wrote:
> Most users of get/set array functions iterate consecutive bits of data,
> usually a single integer, while processing array of results obtained
> from, or building an array of values to be passed to those functions.
> Save time wasted on those iterations by changing the functions' API to
> accept bitmaps.
>
> All current users are updated as well.
>
> More benefits from the change are expected as soon as planned support
> for accepting/passing those bitmaps directly from/to respective GPIO
> chip callbacks if applicable is implemented.
>
> Cc: Jonathan Corbet <[email protected]>
> Cc: Miguel Ojeda Sandonis <[email protected]>
> Cc: Peter Korsgaard <[email protected]>
> Cc: Peter Rosin <[email protected]>
> Cc: Andrew Lunn <[email protected]>
> Cc: Florian Fainelli <[email protected]>
> Cc: "David S. Miller" <[email protected]>
> Cc: Dominik Brodowski <[email protected]>
> Cc: Kishon Vijay Abraham I <[email protected]>
> Cc: Lars-Peter Clausen <[email protected]>
> Cc: Michael Hennerich <[email protected]>
> Cc: Jonathan Cameron <[email protected]>
> Cc: Hartmut Knaack <[email protected]>
> Cc: Peter Meerwald-Stadler <[email protected]>
> Cc: Greg Kroah-Hartman <[email protected]>
> Cc: Jiri Slaby <[email protected]>
> Signed-off-by: Janusz Krzysztofik <[email protected]>
> Acked-by: Ulf Hansson <[email protected]>

Thanks for your patch!

> --- a/drivers/auxdisplay/hd44780.c
> +++ b/drivers/auxdisplay/hd44780.c
> @@ -62,20 +62,19 @@ static void hd44780_strobe_gpio(struct hd44780 *hd)
> /* write to an LCD panel register in 8 bit GPIO mode */
> static void hd44780_write_gpio8(struct hd44780 *hd, u8 val, unsigned int rs)
> {
> - int values[10]; /* for DATA[0-7], RS, RW */
> - unsigned int i, n;
> + unsigned long value_bitmap[1]; /* for DATA[0-7], RS, RW */
> + unsigned int n;
>
> - for (i = 0; i < 8; i++)
> - values[PIN_DATA0 + i] = !!(val & BIT(i));
> - values[PIN_CTRL_RS] = rs;
> + value_bitmap[0] = val;
> + __assign_bit(PIN_CTRL_RS, value_bitmap, rs);


> n = 9;
> if (hd->pins[PIN_CTRL_RW]) {
> - values[PIN_CTRL_RW] = 0;
> + __clear_bit(PIN_CTRL_RW, value_bitmap);

The clearing is not needed, as this has been done by 'value_bitmap[0] = val;'

> n++;
> }

So the above block can be simplified to:

n = hd->pins[PIN_CTRL_RW] ? 10 : 9;

>
> /* Present the data to the port */
> - gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA0], values);
> + gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA0], value_bitmap);
>
> hd44780_strobe_gpio(hd);
> }
> @@ -83,32 +82,31 @@ static void hd44780_write_gpio8(struct hd44780 *hd, u8 val, unsigned int rs)
> /* write to an LCD panel register in 4 bit GPIO mode */
> static void hd44780_write_gpio4(struct hd44780 *hd, u8 val, unsigned int rs)
> {
> - int values[10]; /* for DATA[0-7], RS, RW, but DATA[0-3] is unused */
> - unsigned int i, n;
> + /* for DATA[0-7], RS, RW, but DATA[0-3] is unused */

This comment is not correct, as the low bits will be used.

/* DATA[4-7], RS, RW */

> + unsigned long value_bitmap[1];
> + unsigned int n;
>
> /* High nibble + RS, RW */
> - for (i = 4; i < 8; i++)
> - values[PIN_DATA0 + i] = !!(val & BIT(i));
> - values[PIN_CTRL_RS] = rs;
> + value_bitmap[0] = val;
> + __assign_bit(PIN_CTRL_RS, value_bitmap, rs);
> n = 5;
> if (hd->pins[PIN_CTRL_RW]) {
> - values[PIN_CTRL_RW] = 0;
> + __clear_bit(PIN_CTRL_RW, value_bitmap);

Not needed.

> n++;
> }

Hence:

n = hd->pins[PIN_CTRL_RW] ? 6: 5;

> + value_bitmap[0] >>= PIN_DATA4;

Yuck?!?

Isn't it more readable to just do:

/* High nibble + RS, RW */
value_bitmap[0] = val >> 4;
__assign_bit(4, value_bitmap, rs);
n = hd->pins[PIN_CTRL_RW] ? 6: 5;

>
> /* Present the data to the port */
> - gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA4],
> - &values[PIN_DATA4]);
> + gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA4], value_bitmap);
>
> hd44780_strobe_gpio(hd);
>
> /* Low nibble */
> - for (i = 0; i < 4; i++)
> - values[PIN_DATA4 + i] = !!(val & BIT(i));
> + value_bitmap[0] &= ~((1 << PIN_DATA4) - 1);
> + value_bitmap[0] |= val & ~((1 << PIN_DATA4) - 1);

... and:

/* Low nibble */
value_bitmap[0] &= ~0x0f;
value_bitmap[0] |= val & 0x0f;

>
> /* Present the data to the port */
> - gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA4],
> - &values[PIN_DATA4]);
> + gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA4], value_bitmap);
>
> hd44780_strobe_gpio(hd);
> }
> @@ -155,23 +153,23 @@ static void hd44780_write_cmd_gpio4(struct charlcd *lcd, int cmd)
> /* Send 4-bits of a command to the LCD panel in raw 4 bit GPIO mode */
> static void hd44780_write_cmd_raw_gpio4(struct charlcd *lcd, int cmd)
> {
> - int values[10]; /* for DATA[0-7], RS, RW, but DATA[0-3] is unused */
> + /* for DATA[0-7], RS, RW, but DATA[0-3] is unused */

This comment is not correct, as the low bits will be used.

/* DATA[4-7], RS, RW */

> + unsigned long value_bitmap[1];
> struct hd44780 *hd = lcd->drvdata;
> - unsigned int i, n;
> + unsigned int n;
>
> /* Command nibble + RS, RW */
> - for (i = 0; i < 4; i++)
> - values[PIN_DATA4 + i] = !!(cmd & BIT(i));
> - values[PIN_CTRL_RS] = 0;
> + value_bitmap[0] = cmd << PIN_DATA4;
> + __clear_bit(PIN_CTRL_RS, value_bitmap);

Implied by the assignment above.

> n = 5;
> if (hd->pins[PIN_CTRL_RW]) {
> - values[PIN_CTRL_RW] = 0;
> + __clear_bit(PIN_CTRL_RW, value_bitmap);
> n++;
> }
> + value_bitmap[0] = value_bitmap[0] >> PIN_DATA4;

Hence:

/* Command nibble + RS, RW */
value_bitmap[0] = cmd;
n = hd->pins[PIN_CTRL_RW] ? 6: 5;


>
> /* Present the data to the port */
> - gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA4],
> - &values[PIN_DATA4]);
> + gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA4], value_bitmap);
>
> hd44780_strobe_gpio(hd);
> }

Gr{oetje,eeting}s,

Geert

--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- [email protected]

In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
-- Linus Torvalds

2018-08-30 11:12:44

by Miguel Ojeda

[permalink] [raw]
Subject: Re: [PATCH v5 1/4] gpiolib: Pass bitmaps, not integer arrays, to get/set array

Hi Janusz,

On Wed, Aug 29, 2018 at 10:48 PM, Janusz Krzysztofik
<[email protected]> wrote:
> Most users of get/set array functions iterate consecutive bits of data,
> usually a single integer, while processing array of results obtained
> from, or building an array of values to be passed to those functions.
> Save time wasted on those iterations by changing the functions' API to
> accept bitmaps.
>
> All current users are updated as well.
>
> More benefits from the change are expected as soon as planned support
> for accepting/passing those bitmaps directly from/to respective GPIO
> chip callbacks if applicable is implemented.
>
> Cc: Jonathan Corbet <[email protected]>
> Cc: Miguel Ojeda Sandonis <[email protected]>
> Cc: Peter Korsgaard <[email protected]>
> Cc: Peter Rosin <[email protected]>
> Cc: Andrew Lunn <[email protected]>
> Cc: Florian Fainelli <[email protected]>
> Cc: "David S. Miller" <[email protected]>
> Cc: Dominik Brodowski <[email protected]>
> Cc: Kishon Vijay Abraham I <[email protected]>
> Cc: Lars-Peter Clausen <[email protected]>
> Cc: Michael Hennerich <[email protected]>
> Cc: Jonathan Cameron <[email protected]>
> Cc: Hartmut Knaack <[email protected]>
> Cc: Peter Meerwald-Stadler <[email protected]>
> Cc: Greg Kroah-Hartman <[email protected]>
> Cc: Jiri Slaby <[email protected]>
> Signed-off-by: Janusz Krzysztofik <[email protected]>
> Acked-by: Ulf Hansson <[email protected]>
> ---
> Documentation/driver-api/gpio/consumer.rst | 22 ++++----
> drivers/auxdisplay/hd44780.c | 52 +++++++++--------
> drivers/bus/ts-nbus.c | 19 ++-----
> drivers/gpio/gpio-max3191x.c | 17 +++---
> drivers/gpio/gpiolib.c | 86 +++++++++++++++--------------
> drivers/gpio/gpiolib.h | 4 +-
> drivers/i2c/muxes/i2c-mux-gpio.c | 8 +--
> drivers/mmc/core/pwrseq_simple.c | 13 ++---
> drivers/mux/gpio.c | 9 +--
> drivers/net/phy/mdio-mux-gpio.c | 3 +-
> drivers/pcmcia/soc_common.c | 11 ++--
> drivers/phy/motorola/phy-mapphone-mdm6600.c | 17 +++---
> drivers/staging/iio/adc/ad7606.c | 9 +--
> drivers/tty/serial/serial_mctrl_gpio.c | 7 ++-
> include/linux/gpio/consumer.h | 18 +++---
> 15 files changed, 140 insertions(+), 155 deletions(-)
>
> diff --git a/Documentation/driver-api/gpio/consumer.rst b/Documentation/driver-api/gpio/consumer.rst
> index aa03f389d41d..ed68042ddccf 100644
> --- a/Documentation/driver-api/gpio/consumer.rst
> +++ b/Documentation/driver-api/gpio/consumer.rst
> @@ -323,29 +323,29 @@ The following functions get or set the values of an array of GPIOs::
>
> int gpiod_get_array_value(unsigned int array_size,
> struct gpio_desc **desc_array,
> - int *value_array);
> + unsigned long *value_bitmap);
> int gpiod_get_raw_array_value(unsigned int array_size,
> struct gpio_desc **desc_array,
> - int *value_array);
> + unsigned long *value_bitmap);
> int gpiod_get_array_value_cansleep(unsigned int array_size,
> struct gpio_desc **desc_array,
> - int *value_array);
> + unsigned long *value_bitmap);
> int gpiod_get_raw_array_value_cansleep(unsigned int array_size,
> struct gpio_desc **desc_array,
> - int *value_array);
> + unsigned long *value_bitmap);
>
> void gpiod_set_array_value(unsigned int array_size,
> struct gpio_desc **desc_array,
> - int *value_array)
> + unsigned long *value_bitmap)
> void gpiod_set_raw_array_value(unsigned int array_size,
> struct gpio_desc **desc_array,
> - int *value_array)
> + unsigned long *value_bitmap)
> void gpiod_set_array_value_cansleep(unsigned int array_size,
> struct gpio_desc **desc_array,
> - int *value_array)
> + unsigned long *value_bitmap)
> void gpiod_set_raw_array_value_cansleep(unsigned int array_size,
> struct gpio_desc **desc_array,
> - int *value_array)
> + unsigned long *value_bitmap)
>
> The array can be an arbitrary set of GPIOs. The functions will try to access
> GPIOs belonging to the same bank or chip simultaneously if supported by the
> @@ -356,8 +356,8 @@ accessed sequentially.
> The functions take three arguments:
> * array_size - the number of array elements
> * desc_array - an array of GPIO descriptors
> - * value_array - an array to store the GPIOs' values (get) or
> - an array of values to assign to the GPIOs (set)
> + * value_bitmap - a bitmap to store the GPIOs' values (get) or
> + a bitmap of values to assign to the GPIOs (set)
>
> The descriptor array can be obtained using the gpiod_get_array() function
> or one of its variants. If the group of descriptors returned by that function
> @@ -366,7 +366,7 @@ the struct gpio_descs returned by gpiod_get_array()::
>
> struct gpio_descs *my_gpio_descs = gpiod_get_array(...);
> gpiod_set_array_value(my_gpio_descs->ndescs, my_gpio_descs->desc,
> - my_gpio_values);
> + my_gpio_value_bitmap);
>
> It is also possible to access a completely arbitrary array of descriptors. The
> descriptors may be obtained using any combination of gpiod_get() and
> diff --git a/drivers/auxdisplay/hd44780.c b/drivers/auxdisplay/hd44780.c
> index f1a42f0f1ded..bbbd6a29bf01 100644
> --- a/drivers/auxdisplay/hd44780.c
> +++ b/drivers/auxdisplay/hd44780.c
> @@ -62,20 +62,19 @@ static void hd44780_strobe_gpio(struct hd44780 *hd)
> /* write to an LCD panel register in 8 bit GPIO mode */
> static void hd44780_write_gpio8(struct hd44780 *hd, u8 val, unsigned int rs)
> {
> - int values[10]; /* for DATA[0-7], RS, RW */
> - unsigned int i, n;
> + unsigned long value_bitmap[1]; /* for DATA[0-7], RS, RW */

(I read your comments in the other email)

I still find this odd, but if everyone is going to have this change
done like this, consistency is better.

> + unsigned int n;
>
> - for (i = 0; i < 8; i++)
> - values[PIN_DATA0 + i] = !!(val & BIT(i));
> - values[PIN_CTRL_RS] = rs;
> + value_bitmap[0] = val;
> + __assign_bit(PIN_CTRL_RS, value_bitmap, rs);
> n = 9;
> if (hd->pins[PIN_CTRL_RW]) {
> - values[PIN_CTRL_RW] = 0;
> + __clear_bit(PIN_CTRL_RW, value_bitmap);
> n++;
> }
>
> /* Present the data to the port */
> - gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA0], values);
> + gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA0], value_bitmap);
>
> hd44780_strobe_gpio(hd);
> }
> @@ -83,32 +82,31 @@ static void hd44780_write_gpio8(struct hd44780 *hd, u8 val, unsigned int rs)
> /* write to an LCD panel register in 4 bit GPIO mode */
> static void hd44780_write_gpio4(struct hd44780 *hd, u8 val, unsigned int rs)
> {
> - int values[10]; /* for DATA[0-7], RS, RW, but DATA[0-3] is unused */
> - unsigned int i, n;
> + /* for DATA[0-7], RS, RW, but DATA[0-3] is unused */
> + unsigned long value_bitmap[1];

Ditto.

> + unsigned int n;
>
> /* High nibble + RS, RW */
> - for (i = 4; i < 8; i++)
> - values[PIN_DATA0 + i] = !!(val & BIT(i));
> - values[PIN_CTRL_RS] = rs;
> + value_bitmap[0] = val;
> + __assign_bit(PIN_CTRL_RS, value_bitmap, rs);
> n = 5;
> if (hd->pins[PIN_CTRL_RW]) {
> - values[PIN_CTRL_RW] = 0;
> + __clear_bit(PIN_CTRL_RW, value_bitmap);
> n++;
> }
> + value_bitmap[0] >>= PIN_DATA4;
>
> /* Present the data to the port */
> - gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA4],
> - &values[PIN_DATA4]);
> + gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA4], value_bitmap);
>
> hd44780_strobe_gpio(hd);
>
> /* Low nibble */
> - for (i = 0; i < 4; i++)
> - values[PIN_DATA4 + i] = !!(val & BIT(i));
> + value_bitmap[0] &= ~((1 << PIN_DATA4) - 1);
> + value_bitmap[0] |= val & ~((1 << PIN_DATA4) - 1);

This is still wrong! What I originally meant in my v4 review is that
there is an extra ~ in the second line.

Also, a couple of general comments:

- Please review the list of CCs (I was not CC'd originally, so maybe
there are other maintainers that aren't, either)
- In general, the new code seems harder to read than the original one
(but that is my impression).

Thanks for your effort! :-)

Cheers,
Miguel

2018-08-30 15:35:35

by David Laight

[permalink] [raw]
Subject: RE: [PATCH v5 1/4] gpiolib: Pass bitmaps, not integer arrays, to get/set array

From: Miguel Ojeda
> Sent: 30 August 2018 12:11
...
> > + unsigned long value_bitmap[1]; /* for DATA[0-7], RS, RW */
>
> (I read your comments in the other email)
>
> I still find this odd, but if everyone is going to have this change
> done like this, consistency is better.

Maybe there ought to be a define so you can do:
DEFINE_BITMAP(value_bitmap, 32);

While it might just generate an unsigned long [] there is probably
scope for stronger typing.

David

-
Registered Address Lakeside, Bramley Road, Mount Farm, Milton Keynes, MK1 1PT, UK
Registration No: 1397386 (Wales)

2018-08-31 09:32:10

by Linus Walleij

[permalink] [raw]
Subject: Re: [PATCH v5 1/4] gpiolib: Pass bitmaps, not integer arrays, to get/set array

On Wed, Aug 29, 2018 at 10:48 PM Janusz Krzysztofik <[email protected]> wrote:

So it's no secret that I strongly fancy this patch set.

What would be great at this point is to have some people test
that the drivers still work as expected, even better if they can do
some benchmarking.

> - values[PIN_DATA0 + i] = !!(val & BIT(i));
> - values[PIN_CTRL_RS] = rs;
> + value_bitmap[0] = val;
> + __assign_bit(PIN_CTRL_RS, value_bitmap, rs);
> n = 9;
> if (hd->pins[PIN_CTRL_RW]) {
> - values[PIN_CTRL_RW] = 0;
> + __clear_bit(PIN_CTRL_RW, value_bitmap);

This seems fine to me, but I understand the comment that the
code becomes harder to read.

I think part of it is those __assign_bit() and __clear_bit() with
the double-underscore of unclear meaning. The meaning is
"non atomic" IIRC, so maybe I should move forward
with my plan to send a sed script to Torvalds just renaming all
of those to something sane in the next merge window.

Like __assign_bit() -> assign_bit_nonatomic()

Yours,
Linus Walleij

2018-08-31 22:57:02

by Janusz Krzysztofik

[permalink] [raw]
Subject: [PATH v6 0/4] gpiolib: speed up GPIO array processing


The goal is to boost performance of get/set array functions while
processing GPIO arrays which represent pins of a signle chip in
hardware order. If resulting performance is close to PIO, GPIO API
can be used for data I/O without much loss of speed.

Created and tested on a low end Amstrad Delta board with NAND driver
updated to use GPIO API for data I/O. Performance degrade compared to
PIO is much better than before the optimization but still not quite
satisfactory.

Janusz Krzysztofik (4):
gpiolib: Pass bitmaps, not integer arrays, to get/set array
gpiolib: Identify arrays matching GPIO hardware
gpiolib: Pass array info to get/set array functions
gpiolib: Implement fast processing path in get/set array

Changelog:
v6:
[PATCH v6 1/4] gpiolib: Pass bitmaps, not integer arrays, to get/set
- use DECLARE_BITMAP() macro for declaring value_bitmap - great idea by
David Laight, thanks!
drivers/auxdisplay/hd44780.c:
- simplify the code and adjust comments as recommended by Geert
Uytterhoeven - thanks!,
drivers/i2c/muxes/i2c-mux-gpio.c:
- drop .values member of struct gpiomux - details prvided by Peter
Rosin, thanks!,
drivers/mux/gpio.c:
- drop .val member of struct mux_gpio - details prvided by Peter
Rosin, thanks!,
drivers/net/phy/mdio-mux-gpio.c:
- drop .values member of struct mdio_mux_gpio_state and is processsing.

v5:
[PATCH v5 1/4] gpiolib: Pass bitmaps, not integer arrays, to get/set
- drivers/i2c/muxes/i2c-mux-gpio.c:
- drop assigment of values to struct gpiomux.values, as recommended
by Peter Rosin - thanks!,
- mark the .values member of the structure as obsolete,
- drivers/mux/gpio.c:
- drop assigment of values to struct mux_gpio.val, also recommended
by Peter Rosin - thanks!,
- merk the .val member of the structure as obsolete,
- drivers/auxdisplay/hd44780.c:
- fix incorrect bitmap size,
- use >>= operator to simplify notation,
both catched by Miguel Ojeda - thanks!,
- add Cc: clauses as well as Acked-by: collected so far.
[PATCH v5 2/4] gpiolib: Identify arrays matching GPIO hardware
- add Cc: clause.
[PATCH v5 3/4] gpiolib: Pass array info to get/set array functions
- add Cc: clauses as well as Acked-by: collected so far.
[PATCH v5 4/4] gpiolib: Implement fast processing path in get/set
- add Cc: clause.

v4:
That series was a follow up of the former "mtd: rawnand: ams-delta: Use
gpio-omap accessors for data I/O" which already contained some changes
to gpiolib. Those previous attempts were commented by Borris Brezillon
who suggested using GPIO API modified to accept bitmaps, and by Linus
Walleij who suggested still more great ideas for further immprovement
of the proposed API changes - thanks!

diffstat:
Documentation/driver-api/gpio/board.rst | 15 +
Documentation/driver-api/gpio/consumer.rst | 48 +++-
drivers/auxdisplay/hd44780.c | 74 +++----
drivers/bus/ts-nbus.c | 27 +-
drivers/gpio/gpio-max3191x.c | 23 +-
drivers/gpio/gpiolib.c | 279 ++++++++++++++++++++++------
drivers/gpio/gpiolib.h | 15 +
drivers/i2c/muxes/i2c-mux-gpio.c | 16 -
drivers/mmc/core/pwrseq_simple.c | 15 -
drivers/mux/gpio.c | 18 -
drivers/net/phy/mdio-mux-gpio.c | 13 -
drivers/pcmcia/soc_common.c | 14 -
drivers/phy/motorola/phy-mapphone-mdm6600.c | 21 +-
drivers/staging/iio/adc/ad7606.c | 12 -
drivers/tty/serial/serial_mctrl_gpio.c | 9
include/linux/gpio/consumer.h | 35 ++-
16 files changed, 417 insertions(+), 217 deletions(-)


2018-08-31 22:57:15

by Janusz Krzysztofik

[permalink] [raw]
Subject: [PATCH v6 1/4] gpiolib: Pass bitmaps, not integer arrays, to get/set array

Most users of get/set array functions iterate consecutive bits of data,
usually a single integer, while processing array of results obtained
from, or building an array of values to be passed to those functions.
Save time wasted on those iterations by changing the functions' API to
accept bitmaps.

All current users are updated as well.

More benefits from the change are expected as soon as planned support
for accepting/passing those bitmaps directly from/to respective GPIO
chip callbacks if applicable is implemented.

Cc: Jonathan Corbet <[email protected]>
Cc: Miguel Ojeda Sandonis <[email protected]>
Cc: Peter Korsgaard <[email protected]>
Cc: Peter Rosin <[email protected]>
Cc: Andrew Lunn <[email protected]>
Cc: Florian Fainelli <[email protected]>
Cc: "David S. Miller" <[email protected]>
Cc: Dominik Brodowski <[email protected]>
Cc: Kishon Vijay Abraham I <[email protected]>
Cc: Lars-Peter Clausen <[email protected]>
Cc: Michael Hennerich <[email protected]>
Cc: Jonathan Cameron <[email protected]>
Cc: Hartmut Knaack <[email protected]>
Cc: Peter Meerwald-Stadler <[email protected]>
Cc: Greg Kroah-Hartman <[email protected]>
Cc: Jiri Slaby <[email protected]>
Signed-off-by: Janusz Krzysztofik <[email protected]>
Acked-by: Ulf Hansson <[email protected]>
---
Documentation/driver-api/gpio/consumer.rst | 22 ++++----
drivers/auxdisplay/hd44780.c | 62 ++++++++-------------
drivers/bus/ts-nbus.c | 21 ++-----
drivers/gpio/gpio-max3191x.c | 17 +++---
drivers/gpio/gpiolib.c | 86 +++++++++++++++--------------
drivers/gpio/gpiolib.h | 4 +-
drivers/i2c/muxes/i2c-mux-gpio.c | 14 ++---
drivers/mmc/core/pwrseq_simple.c | 13 ++---
drivers/mux/gpio.c | 15 ++---
drivers/net/phy/mdio-mux-gpio.c | 11 ++--
drivers/pcmcia/soc_common.c | 11 ++--
drivers/phy/motorola/phy-mapphone-mdm6600.c | 17 +++---
drivers/staging/iio/adc/ad7606.c | 9 +--
drivers/tty/serial/serial_mctrl_gpio.c | 7 ++-
include/linux/gpio/consumer.h | 18 +++---
15 files changed, 145 insertions(+), 182 deletions(-)

diff --git a/Documentation/driver-api/gpio/consumer.rst b/Documentation/driver-api/gpio/consumer.rst
index aa03f389d41d..ed68042ddccf 100644
--- a/Documentation/driver-api/gpio/consumer.rst
+++ b/Documentation/driver-api/gpio/consumer.rst
@@ -323,29 +323,29 @@ The following functions get or set the values of an array of GPIOs::

int gpiod_get_array_value(unsigned int array_size,
struct gpio_desc **desc_array,
- int *value_array);
+ unsigned long *value_bitmap);
int gpiod_get_raw_array_value(unsigned int array_size,
struct gpio_desc **desc_array,
- int *value_array);
+ unsigned long *value_bitmap);
int gpiod_get_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
- int *value_array);
+ unsigned long *value_bitmap);
int gpiod_get_raw_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
- int *value_array);
+ unsigned long *value_bitmap);

void gpiod_set_array_value(unsigned int array_size,
struct gpio_desc **desc_array,
- int *value_array)
+ unsigned long *value_bitmap)
void gpiod_set_raw_array_value(unsigned int array_size,
struct gpio_desc **desc_array,
- int *value_array)
+ unsigned long *value_bitmap)
void gpiod_set_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
- int *value_array)
+ unsigned long *value_bitmap)
void gpiod_set_raw_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
- int *value_array)
+ unsigned long *value_bitmap)

The array can be an arbitrary set of GPIOs. The functions will try to access
GPIOs belonging to the same bank or chip simultaneously if supported by the
@@ -356,8 +356,8 @@ accessed sequentially.
The functions take three arguments:
* array_size - the number of array elements
* desc_array - an array of GPIO descriptors
- * value_array - an array to store the GPIOs' values (get) or
- an array of values to assign to the GPIOs (set)
+ * value_bitmap - a bitmap to store the GPIOs' values (get) or
+ a bitmap of values to assign to the GPIOs (set)

The descriptor array can be obtained using the gpiod_get_array() function
or one of its variants. If the group of descriptors returned by that function
@@ -366,7 +366,7 @@ the struct gpio_descs returned by gpiod_get_array()::

struct gpio_descs *my_gpio_descs = gpiod_get_array(...);
gpiod_set_array_value(my_gpio_descs->ndescs, my_gpio_descs->desc,
- my_gpio_values);
+ my_gpio_value_bitmap);

It is also possible to access a completely arbitrary array of descriptors. The
descriptors may be obtained using any combination of gpiod_get() and
diff --git a/drivers/auxdisplay/hd44780.c b/drivers/auxdisplay/hd44780.c
index f1a42f0f1ded..7ee4f9a65bfc 100644
--- a/drivers/auxdisplay/hd44780.c
+++ b/drivers/auxdisplay/hd44780.c
@@ -62,20 +62,15 @@ static void hd44780_strobe_gpio(struct hd44780 *hd)
/* write to an LCD panel register in 8 bit GPIO mode */
static void hd44780_write_gpio8(struct hd44780 *hd, u8 val, unsigned int rs)
{
- int values[10]; /* for DATA[0-7], RS, RW */
- unsigned int i, n;
-
- for (i = 0; i < 8; i++)
- values[PIN_DATA0 + i] = !!(val & BIT(i));
- values[PIN_CTRL_RS] = rs;
- n = 9;
- if (hd->pins[PIN_CTRL_RW]) {
- values[PIN_CTRL_RW] = 0;
- n++;
- }
+ DECLARE_BITMAP(value_bitmap, 10); /* for DATA[0-7], RS, RW */
+ unsigned int n;
+
+ *value_bitmap = val;
+ __assign_bit(8, value_bitmap, rs);
+ n = hd->pins[PIN_CTRL_RW] ? 10 : 9;

/* Present the data to the port */
- gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA0], values);
+ gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA0], value_bitmap);

hd44780_strobe_gpio(hd);
}
@@ -83,32 +78,25 @@ static void hd44780_write_gpio8(struct hd44780 *hd, u8 val, unsigned int rs)
/* write to an LCD panel register in 4 bit GPIO mode */
static void hd44780_write_gpio4(struct hd44780 *hd, u8 val, unsigned int rs)
{
- int values[10]; /* for DATA[0-7], RS, RW, but DATA[0-3] is unused */
- unsigned int i, n;
+ DECLARE_BITMAP(value_bitmap, 6); /* for DATA[4-7], RS, RW */
+ unsigned int n;

/* High nibble + RS, RW */
- for (i = 4; i < 8; i++)
- values[PIN_DATA0 + i] = !!(val & BIT(i));
- values[PIN_CTRL_RS] = rs;
- n = 5;
- if (hd->pins[PIN_CTRL_RW]) {
- values[PIN_CTRL_RW] = 0;
- n++;
- }
+ *value_bitmap = val >> 4;
+ __assign_bit(4, value_bitmap, rs);
+ n = hd->pins[PIN_CTRL_RW] ? 6 : 5;

/* Present the data to the port */
- gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA4],
- &values[PIN_DATA4]);
+ gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA4], value_bitmap);

hd44780_strobe_gpio(hd);

/* Low nibble */
- for (i = 0; i < 4; i++)
- values[PIN_DATA4 + i] = !!(val & BIT(i));
+ *value_bitmap &= ~0x0f;
+ *value_bitmap |= val & 0x0f;

/* Present the data to the port */
- gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA4],
- &values[PIN_DATA4]);
+ gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA4], value_bitmap);

hd44780_strobe_gpio(hd);
}
@@ -155,23 +143,17 @@ static void hd44780_write_cmd_gpio4(struct charlcd *lcd, int cmd)
/* Send 4-bits of a command to the LCD panel in raw 4 bit GPIO mode */
static void hd44780_write_cmd_raw_gpio4(struct charlcd *lcd, int cmd)
{
- int values[10]; /* for DATA[0-7], RS, RW, but DATA[0-3] is unused */
+ /* for DATA[0-7], RS, RW, but DATA[0-3] is unused */
+ DECLARE_BITMAP(value_bitmap, 6);
struct hd44780 *hd = lcd->drvdata;
- unsigned int i, n;
+ unsigned int n;

/* Command nibble + RS, RW */
- for (i = 0; i < 4; i++)
- values[PIN_DATA4 + i] = !!(cmd & BIT(i));
- values[PIN_CTRL_RS] = 0;
- n = 5;
- if (hd->pins[PIN_CTRL_RW]) {
- values[PIN_CTRL_RW] = 0;
- n++;
- }
+ *value_bitmap = cmd & 0x0f;
+ n = hd->pins[PIN_CTRL_RW] ? 6 : 5;

/* Present the data to the port */
- gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA4],
- &values[PIN_DATA4]);
+ gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA4], value_bitmap);

hd44780_strobe_gpio(hd);
}
diff --git a/drivers/bus/ts-nbus.c b/drivers/bus/ts-nbus.c
index 073fd9011154..2ac5239df865 100644
--- a/drivers/bus/ts-nbus.c
+++ b/drivers/bus/ts-nbus.c
@@ -110,13 +110,11 @@ static void ts_nbus_set_direction(struct ts_nbus *ts_nbus, int direction)
*/
static void ts_nbus_reset_bus(struct ts_nbus *ts_nbus)
{
- int i;
- int values[8];
-
- for (i = 0; i < 8; i++)
- values[i] = 0;
+ DECLARE_BITMAP(value_bitmap, 8);
+
+ *value_bitmap = 0;

- gpiod_set_array_value_cansleep(8, ts_nbus->data->desc, values);
+ gpiod_set_array_value_cansleep(8, ts_nbus->data->desc, value_bitmap);
gpiod_set_value_cansleep(ts_nbus->csn, 0);
gpiod_set_value_cansleep(ts_nbus->strobe, 0);
gpiod_set_value_cansleep(ts_nbus->ale, 0);
@@ -157,16 +155,9 @@ static int ts_nbus_read_byte(struct ts_nbus *ts_nbus, u8 *val)
static void ts_nbus_write_byte(struct ts_nbus *ts_nbus, u8 byte)
{
struct gpio_descs *gpios = ts_nbus->data;
- int i;
- int values[8];
-
- for (i = 0; i < 8; i++)
- if (byte & BIT(i))
- values[i] = 1;
- else
- values[i] = 0;
+ DECLARE_BITMAP(value_bitmap, 8) = { byte, };

- gpiod_set_array_value_cansleep(8, gpios->desc, values);
+ gpiod_set_array_value_cansleep(8, gpios->desc, value_bitmap);
}

/*
diff --git a/drivers/gpio/gpio-max3191x.c b/drivers/gpio/gpio-max3191x.c
index b5b9cb1fda50..c4ec1c82af27 100644
--- a/drivers/gpio/gpio-max3191x.c
+++ b/drivers/gpio/gpio-max3191x.c
@@ -315,17 +315,20 @@ static void gpiod_set_array_single_value_cansleep(unsigned int ndescs,
struct gpio_desc **desc,
int value)
{
- int i, *values;
+ unsigned long *value_bitmap;

- values = kmalloc_array(ndescs, sizeof(*values), GFP_KERNEL);
- if (!values)
+ value_bitmap = kmalloc_array(BITS_TO_LONGS(ndescs),
+ sizeof(*value_bitmap), GFP_KERNEL);
+ if (!value_bitmap)
return;

- for (i = 0; i < ndescs; i++)
- values[i] = value;
+ if (value)
+ bitmap_fill(value_bitmap, ndescs);
+ else
+ bitmap_zero(value_bitmap, ndescs);

- gpiod_set_array_value_cansleep(ndescs, desc, values);
- kfree(values);
+ gpiod_set_array_value_cansleep(ndescs, desc, value_bitmap);
+ kfree(value_bitmap);
}

static struct gpio_descs *devm_gpiod_get_array_optional_count(
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index e8f8a1999393..f0e9ffa8cab6 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -427,7 +427,7 @@ static long linehandle_ioctl(struct file *filep, unsigned int cmd,
struct linehandle_state *lh = filep->private_data;
void __user *ip = (void __user *)arg;
struct gpiohandle_data ghd;
- int vals[GPIOHANDLES_MAX];
+ unsigned long value_bitmap[BITS_TO_LONGS(GPIOHANDLES_MAX)];
int i;

if (cmd == GPIOHANDLE_GET_LINE_VALUES_IOCTL) {
@@ -436,13 +436,13 @@ static long linehandle_ioctl(struct file *filep, unsigned int cmd,
true,
lh->numdescs,
lh->descs,
- vals);
+ value_bitmap);
if (ret)
return ret;

memset(&ghd, 0, sizeof(ghd));
for (i = 0; i < lh->numdescs; i++)
- ghd.values[i] = vals[i];
+ ghd.values[i] = test_bit(i, value_bitmap);

if (copy_to_user(ip, &ghd, sizeof(ghd)))
return -EFAULT;
@@ -461,14 +461,14 @@ static long linehandle_ioctl(struct file *filep, unsigned int cmd,

/* Clamp all values to [0,1] */
for (i = 0; i < lh->numdescs; i++)
- vals[i] = !!ghd.values[i];
+ __assign_bit(i, value_bitmap, !!ghd.values[i]);

/* Reuse the array setting function */
return gpiod_set_array_value_complex(false,
true,
lh->numdescs,
lh->descs,
- vals);
+ value_bitmap);
}
return -EINVAL;
}
@@ -2784,7 +2784,7 @@ static int gpio_chip_get_multiple(struct gpio_chip *chip,
int gpiod_get_array_value_complex(bool raw, bool can_sleep,
unsigned int array_size,
struct gpio_desc **desc_array,
- int *value_array)
+ unsigned long *value_bitmap)
{
int i = 0;

@@ -2835,7 +2835,7 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep,

if (!raw && test_bit(FLAG_ACTIVE_LOW, &desc->flags))
value = !value;
- value_array[j] = value;
+ __assign_bit(j, value_bitmap, value);
trace_gpio_value(desc_to_gpio(desc), 1, value);
}

@@ -2895,9 +2895,9 @@ EXPORT_SYMBOL_GPL(gpiod_get_value);

/**
* gpiod_get_raw_array_value() - read raw values from an array of GPIOs
- * @array_size: number of elements in the descriptor / value arrays
+ * @array_size: number of elements in the descriptor array / value bitmap
* @desc_array: array of GPIO descriptors whose values will be read
- * @value_array: array to store the read values
+ * @value_bitmap: bitmap to store the read values
*
* Read the raw values of the GPIOs, i.e. the values of the physical lines
* without regard for their ACTIVE_LOW status. Return 0 in case of success,
@@ -2907,20 +2907,21 @@ EXPORT_SYMBOL_GPL(gpiod_get_value);
* and it will complain if the GPIO chip functions potentially sleep.
*/
int gpiod_get_raw_array_value(unsigned int array_size,
- struct gpio_desc **desc_array, int *value_array)
+ struct gpio_desc **desc_array,
+ unsigned long *value_bitmap)
{
if (!desc_array)
return -EINVAL;
return gpiod_get_array_value_complex(true, false, array_size,
- desc_array, value_array);
+ desc_array, value_bitmap);
}
EXPORT_SYMBOL_GPL(gpiod_get_raw_array_value);

/**
* gpiod_get_array_value() - read values from an array of GPIOs
- * @array_size: number of elements in the descriptor / value arrays
+ * @array_size: number of elements in the descriptor array / value bitmap
* @desc_array: array of GPIO descriptors whose values will be read
- * @value_array: array to store the read values
+ * @value_bitnap: bitmap to store the read values
*
* Read the logical values of the GPIOs, i.e. taking their ACTIVE_LOW status
* into account. Return 0 in case of success, else an error code.
@@ -2929,12 +2930,13 @@ EXPORT_SYMBOL_GPL(gpiod_get_raw_array_value);
* and it will complain if the GPIO chip functions potentially sleep.
*/
int gpiod_get_array_value(unsigned int array_size,
- struct gpio_desc **desc_array, int *value_array)
+ struct gpio_desc **desc_array,
+ unsigned long *value_bitmap)
{
if (!desc_array)
return -EINVAL;
return gpiod_get_array_value_complex(false, false, array_size,
- desc_array, value_array);
+ desc_array, value_bitmap);
}
EXPORT_SYMBOL_GPL(gpiod_get_array_value);

@@ -3027,7 +3029,7 @@ static void gpio_chip_set_multiple(struct gpio_chip *chip,
int gpiod_set_array_value_complex(bool raw, bool can_sleep,
unsigned int array_size,
struct gpio_desc **desc_array,
- int *value_array)
+ unsigned long *value_bitmap)
{
int i = 0;

@@ -3056,7 +3058,7 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep,
do {
struct gpio_desc *desc = desc_array[i];
int hwgpio = gpio_chip_hwgpio(desc);
- int value = value_array[i];
+ int value = test_bit(i, value_bitmap);

if (!raw && test_bit(FLAG_ACTIVE_LOW, &desc->flags))
value = !value;
@@ -3152,9 +3154,9 @@ EXPORT_SYMBOL_GPL(gpiod_set_value);

/**
* gpiod_set_raw_array_value() - assign values to an array of GPIOs
- * @array_size: number of elements in the descriptor / value arrays
+ * @array_size: number of elements in the descriptor array / value bitmap
* @desc_array: array of GPIO descriptors whose values will be assigned
- * @value_array: array of values to assign
+ * @value_bitmap: bitmap of values to assign
*
* Set the raw values of the GPIOs, i.e. the values of the physical lines
* without regard for their ACTIVE_LOW status.
@@ -3163,20 +3165,21 @@ EXPORT_SYMBOL_GPL(gpiod_set_value);
* complain if the GPIO chip functions potentially sleep.
*/
int gpiod_set_raw_array_value(unsigned int array_size,
- struct gpio_desc **desc_array, int *value_array)
+ struct gpio_desc **desc_array,
+ unsigned long *value_bitmap)
{
if (!desc_array)
return -EINVAL;
return gpiod_set_array_value_complex(true, false, array_size,
- desc_array, value_array);
+ desc_array, value_bitmap);
}
EXPORT_SYMBOL_GPL(gpiod_set_raw_array_value);

/**
* gpiod_set_array_value() - assign values to an array of GPIOs
- * @array_size: number of elements in the descriptor / value arrays
+ * @array_size: number of elements in the descriptor array / value bitmap
* @desc_array: array of GPIO descriptors whose values will be assigned
- * @value_array: array of values to assign
+ * @value_bitmap: bitmap of values to assign
*
* Set the logical values of the GPIOs, i.e. taking their ACTIVE_LOW status
* into account.
@@ -3185,12 +3188,13 @@ EXPORT_SYMBOL_GPL(gpiod_set_raw_array_value);
* complain if the GPIO chip functions potentially sleep.
*/
void gpiod_set_array_value(unsigned int array_size,
- struct gpio_desc **desc_array, int *value_array)
+ struct gpio_desc **desc_array,
+ unsigned long *value_bitmap)
{
if (!desc_array)
return;
gpiod_set_array_value_complex(false, false, array_size, desc_array,
- value_array);
+ value_bitmap);
}
EXPORT_SYMBOL_GPL(gpiod_set_array_value);

@@ -3410,9 +3414,9 @@ EXPORT_SYMBOL_GPL(gpiod_get_value_cansleep);

/**
* gpiod_get_raw_array_value_cansleep() - read raw values from an array of GPIOs
- * @array_size: number of elements in the descriptor / value arrays
+ * @array_size: number of elements in the descriptor array / value bitmap
* @desc_array: array of GPIO descriptors whose values will be read
- * @value_array: array to store the read values
+ * @value_bitmap: bitmap to store the read values
*
* Read the raw values of the GPIOs, i.e. the values of the physical lines
* without regard for their ACTIVE_LOW status. Return 0 in case of success,
@@ -3422,21 +3426,21 @@ EXPORT_SYMBOL_GPL(gpiod_get_value_cansleep);
*/
int gpiod_get_raw_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
- int *value_array)
+ unsigned long *value_bitmap)
{
might_sleep_if(extra_checks);
if (!desc_array)
return -EINVAL;
return gpiod_get_array_value_complex(true, true, array_size,
- desc_array, value_array);
+ desc_array, value_bitmap);
}
EXPORT_SYMBOL_GPL(gpiod_get_raw_array_value_cansleep);

/**
* gpiod_get_array_value_cansleep() - read values from an array of GPIOs
- * @array_size: number of elements in the descriptor / value arrays
+ * @array_size: number of elements in the descriptor array / value bitmap
* @desc_array: array of GPIO descriptors whose values will be read
- * @value_array: array to store the read values
+ * @value_bitmap: bitmap to store the read values
*
* Read the logical values of the GPIOs, i.e. taking their ACTIVE_LOW status
* into account. Return 0 in case of success, else an error code.
@@ -3445,13 +3449,13 @@ EXPORT_SYMBOL_GPL(gpiod_get_raw_array_value_cansleep);
*/
int gpiod_get_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
- int *value_array)
+ unsigned long *value_bitmap)
{
might_sleep_if(extra_checks);
if (!desc_array)
return -EINVAL;
return gpiod_get_array_value_complex(false, true, array_size,
- desc_array, value_array);
+ desc_array, value_bitmap);
}
EXPORT_SYMBOL_GPL(gpiod_get_array_value_cansleep);

@@ -3493,9 +3497,9 @@ EXPORT_SYMBOL_GPL(gpiod_set_value_cansleep);

/**
* gpiod_set_raw_array_value_cansleep() - assign values to an array of GPIOs
- * @array_size: number of elements in the descriptor / value arrays
+ * @array_size: number of elements in the descriptor array / value bitmap
* @desc_array: array of GPIO descriptors whose values will be assigned
- * @value_array: array of values to assign
+ * @value_bitmap: bitmap of values to assign
*
* Set the raw values of the GPIOs, i.e. the values of the physical lines
* without regard for their ACTIVE_LOW status.
@@ -3504,13 +3508,13 @@ EXPORT_SYMBOL_GPL(gpiod_set_value_cansleep);
*/
int gpiod_set_raw_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
- int *value_array)
+ unsigned long *value_bitmap)
{
might_sleep_if(extra_checks);
if (!desc_array)
return -EINVAL;
return gpiod_set_array_value_complex(true, true, array_size, desc_array,
- value_array);
+ value_bitmap);
}
EXPORT_SYMBOL_GPL(gpiod_set_raw_array_value_cansleep);

@@ -3533,9 +3537,9 @@ void gpiod_add_lookup_tables(struct gpiod_lookup_table **tables, size_t n)

/**
* gpiod_set_array_value_cansleep() - assign values to an array of GPIOs
- * @array_size: number of elements in the descriptor / value arrays
+ * @array_size: number of elements in the descriptor array / value bitmap
* @desc_array: array of GPIO descriptors whose values will be assigned
- * @value_array: array of values to assign
+ * @value_bitmap: bitmap of values to assign
*
* Set the logical values of the GPIOs, i.e. taking their ACTIVE_LOW status
* into account.
@@ -3544,13 +3548,13 @@ void gpiod_add_lookup_tables(struct gpiod_lookup_table **tables, size_t n)
*/
void gpiod_set_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
- int *value_array)
+ unsigned long *value_bitmap)
{
might_sleep_if(extra_checks);
if (!desc_array)
return;
gpiod_set_array_value_complex(false, true, array_size, desc_array,
- value_array);
+ value_bitmap);
}
EXPORT_SYMBOL_GPL(gpiod_set_array_value_cansleep);

diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h
index a7e49fef73d4..11e83d2eef89 100644
--- a/drivers/gpio/gpiolib.h
+++ b/drivers/gpio/gpiolib.h
@@ -187,11 +187,11 @@ struct gpio_desc *gpiochip_get_desc(struct gpio_chip *chip, u16 hwnum);
int gpiod_get_array_value_complex(bool raw, bool can_sleep,
unsigned int array_size,
struct gpio_desc **desc_array,
- int *value_array);
+ unsigned long *value_bitmap);
int gpiod_set_array_value_complex(bool raw, bool can_sleep,
unsigned int array_size,
struct gpio_desc **desc_array,
- int *value_array);
+ unsigned long *value_bitmap);

/* This is just passed between gpiolib and devres */
struct gpio_desc *gpiod_get_from_of_node(struct device_node *node,
diff --git a/drivers/i2c/muxes/i2c-mux-gpio.c b/drivers/i2c/muxes/i2c-mux-gpio.c
index 401308e3d036..e28ddc20000d 100644
--- a/drivers/i2c/muxes/i2c-mux-gpio.c
+++ b/drivers/i2c/muxes/i2c-mux-gpio.c
@@ -22,18 +22,16 @@ struct gpiomux {
struct i2c_mux_gpio_platform_data data;
unsigned gpio_base;
struct gpio_desc **gpios;
- int *values;
};

static void i2c_mux_gpio_set(const struct gpiomux *mux, unsigned val)
{
- int i;
-
- for (i = 0; i < mux->data.n_gpios; i++)
- mux->values[i] = (val >> i) & 1;
+ DECLARE_BITMAP(value_bitmap, mux->data.n_gpios);
+
+ *value_bitmap = val;

gpiod_set_array_value_cansleep(mux->data.n_gpios,
- mux->gpios, mux->values);
+ mux->gpios, value_bitmap);
}

static int i2c_mux_gpio_select(struct i2c_mux_core *muxc, u32 chan)
@@ -182,15 +180,13 @@ static int i2c_mux_gpio_probe(struct platform_device *pdev)
return -EPROBE_DEFER;

muxc = i2c_mux_alloc(parent, &pdev->dev, mux->data.n_values,
- mux->data.n_gpios * sizeof(*mux->gpios) +
- mux->data.n_gpios * sizeof(*mux->values), 0,
+ mux->data.n_gpios * sizeof(*mux->gpios), 0,
i2c_mux_gpio_select, NULL);
if (!muxc) {
ret = -ENOMEM;
goto alloc_failed;
}
mux->gpios = muxc->priv;
- mux->values = (int *)(mux->gpios + mux->data.n_gpios);
muxc->priv = mux;

platform_set_drvdata(pdev, muxc);
diff --git a/drivers/mmc/core/pwrseq_simple.c b/drivers/mmc/core/pwrseq_simple.c
index a8b9fee4d62a..fd7791e072b3 100644
--- a/drivers/mmc/core/pwrseq_simple.c
+++ b/drivers/mmc/core/pwrseq_simple.c
@@ -40,18 +40,13 @@ static void mmc_pwrseq_simple_set_gpios_value(struct mmc_pwrseq_simple *pwrseq,
struct gpio_descs *reset_gpios = pwrseq->reset_gpios;

if (!IS_ERR(reset_gpios)) {
- int i, *values;
int nvalues = reset_gpios->ndescs;
+ DECLARE_BITMAP(value_bitmap, nvalues);

- values = kmalloc_array(nvalues, sizeof(int), GFP_KERNEL);
- if (!values)
- return;
+ *value_bitmap = value;

- for (i = 0; i < nvalues; i++)
- values[i] = value;
-
- gpiod_set_array_value_cansleep(nvalues, reset_gpios->desc, values);
- kfree(values);
+ gpiod_set_array_value_cansleep(nvalues, reset_gpios->desc,
+ value_bitmap);
}
}

diff --git a/drivers/mux/gpio.c b/drivers/mux/gpio.c
index 6fdd9316db8b..17b7b1c21d90 100644
--- a/drivers/mux/gpio.c
+++ b/drivers/mux/gpio.c
@@ -17,20 +17,17 @@

struct mux_gpio {
struct gpio_descs *gpios;
- int *val;
};

static int mux_gpio_set(struct mux_control *mux, int state)
{
struct mux_gpio *mux_gpio = mux_chip_priv(mux->chip);
- int i;
-
- for (i = 0; i < mux_gpio->gpios->ndescs; i++)
- mux_gpio->val[i] = (state >> i) & 1;
+ DECLARE_BITMAP(value_bitmap, mux_gpio->gpios->ndescs);
+
+ *value_bitmap = state;

gpiod_set_array_value_cansleep(mux_gpio->gpios->ndescs,
- mux_gpio->gpios->desc,
- mux_gpio->val);
+ mux_gpio->gpios->desc, value_bitmap);

return 0;
}
@@ -58,13 +55,11 @@ static int mux_gpio_probe(struct platform_device *pdev)
if (pins < 0)
return pins;

- mux_chip = devm_mux_chip_alloc(dev, 1, sizeof(*mux_gpio) +
- pins * sizeof(*mux_gpio->val));
+ mux_chip = devm_mux_chip_alloc(dev, 1, sizeof(*mux_gpio));
if (IS_ERR(mux_chip))
return PTR_ERR(mux_chip);

mux_gpio = mux_chip_priv(mux_chip);
- mux_gpio->val = (int *)(mux_gpio + 1);
mux_chip->ops = &mux_gpio_ops;

mux_gpio->gpios = devm_gpiod_get_array(dev, "mux", GPIOD_OUT_LOW);
diff --git a/drivers/net/phy/mdio-mux-gpio.c b/drivers/net/phy/mdio-mux-gpio.c
index bc90764a8b8d..3907e9528949 100644
--- a/drivers/net/phy/mdio-mux-gpio.c
+++ b/drivers/net/phy/mdio-mux-gpio.c
@@ -20,23 +20,21 @@
struct mdio_mux_gpio_state {
struct gpio_descs *gpios;
void *mux_handle;
- int values[];
};

static int mdio_mux_gpio_switch_fn(int current_child, int desired_child,
void *data)
{
struct mdio_mux_gpio_state *s = data;
- unsigned int n;
+ DECLARE_BITMAP(value_bitmap, s->gpios->ndescs);

if (current_child == desired_child)
return 0;

- for (n = 0; n < s->gpios->ndescs; n++)
- s->values[n] = (desired_child >> n) & 1;
+ *value_bitmap = desired_child;

gpiod_set_array_value_cansleep(s->gpios->ndescs, s->gpios->desc,
- s->values);
+ value_bitmap);

return 0;
}
@@ -51,8 +49,7 @@ static int mdio_mux_gpio_probe(struct platform_device *pdev)
if (IS_ERR(gpios))
return PTR_ERR(gpios);

- s = devm_kzalloc(&pdev->dev, struct_size(s, values, gpios->ndescs),
- GFP_KERNEL);
+ s = devm_kzalloc(&pdev->dev, sizeof(*s), GFP_KERNEL);
if (!s) {
gpiod_put_array(gpios);
return -ENOMEM;
diff --git a/drivers/pcmcia/soc_common.c b/drivers/pcmcia/soc_common.c
index c5f2344c189b..44288b1a57ee 100644
--- a/drivers/pcmcia/soc_common.c
+++ b/drivers/pcmcia/soc_common.c
@@ -351,19 +351,22 @@ static int soc_common_pcmcia_config_skt(

if (ret == 0) {
struct gpio_desc *descs[2];
- int values[2], n = 0;
+ DECLARE_BITMAP(value_bitmap, 2);
+ int n = 0;

if (skt->gpio_reset) {
descs[n] = skt->gpio_reset;
- values[n++] = !!(state->flags & SS_RESET);
+ __assign_bit(n++, value_bitmap,
+ !!(state->flags & SS_RESET));
}
if (skt->gpio_bus_enable) {
descs[n] = skt->gpio_bus_enable;
- values[n++] = !!(state->flags & SS_OUTPUT_ENA);
+ __assign_bit(n++, value_bitmap,
+ !!(state->flags & SS_OUTPUT_ENA));
}

if (n)
- gpiod_set_array_value_cansleep(n, descs, values);
+ gpiod_set_array_value_cansleep(n, descs, value_bitmap);

/*
* This really needs a better solution. The IRQ
diff --git a/drivers/phy/motorola/phy-mapphone-mdm6600.c b/drivers/phy/motorola/phy-mapphone-mdm6600.c
index 0075fb0bef8c..b348272f7f1a 100644
--- a/drivers/phy/motorola/phy-mapphone-mdm6600.c
+++ b/drivers/phy/motorola/phy-mapphone-mdm6600.c
@@ -157,15 +157,12 @@ static const struct phy_ops gpio_usb_ops = {
*/
static void phy_mdm6600_cmd(struct phy_mdm6600 *ddata, int val)
{
- int values[PHY_MDM6600_NR_CMD_LINES];
- int i;
+ DECLARE_BITMAP(value_bitmap, PHY_MDM6600_NR_CMD_LINES);

- val &= (1 << PHY_MDM6600_NR_CMD_LINES) - 1;
- for (i = 0; i < PHY_MDM6600_NR_CMD_LINES; i++)
- values[i] = (val & BIT(i)) >> i;
+ *value_bitmap = val & ((1 << PHY_MDM6600_NR_CMD_LINES) - 1);

gpiod_set_array_value_cansleep(PHY_MDM6600_NR_CMD_LINES,
- ddata->cmd_gpios->desc, values);
+ ddata->cmd_gpios->desc, value_bitmap);
}

/**
@@ -176,7 +173,7 @@ static void phy_mdm6600_status(struct work_struct *work)
{
struct phy_mdm6600 *ddata;
struct device *dev;
- int values[PHY_MDM6600_NR_STATUS_LINES];
+ DECLARE_BITMAP(value_bitmap, PHY_MDM6600_NR_STATUS_LINES);
int error, i, val = 0;

ddata = container_of(work, struct phy_mdm6600, status_work.work);
@@ -184,14 +181,14 @@ static void phy_mdm6600_status(struct work_struct *work)

error = gpiod_get_array_value_cansleep(PHY_MDM6600_NR_STATUS_LINES,
ddata->status_gpios->desc,
- values);
+ value_bitmap);
if (error)
return;

for (i = 0; i < PHY_MDM6600_NR_STATUS_LINES; i++) {
- val |= values[i] << i;
+ val |= test_bit(i, value_bitmap) << i;
dev_dbg(ddata->dev, "XXX %s: i: %i values[i]: %i val: %i\n",
- __func__, i, values[i], val);
+ __func__, i, test_bit(i, value_bitmap), val);
}
ddata->status = val;

diff --git a/drivers/staging/iio/adc/ad7606.c b/drivers/staging/iio/adc/ad7606.c
index 25b9fcd5e3a4..18b6d10b0176 100644
--- a/drivers/staging/iio/adc/ad7606.c
+++ b/drivers/staging/iio/adc/ad7606.c
@@ -202,7 +202,7 @@ static int ad7606_write_raw(struct iio_dev *indio_dev,
long mask)
{
struct ad7606_state *st = iio_priv(indio_dev);
- int values[3];
+ DECLARE_BITMAP(value_bitmap, 3);
int ret, i;

switch (mask) {
@@ -227,13 +227,10 @@ static int ad7606_write_raw(struct iio_dev *indio_dev,
if (ret < 0)
return ret;

- values[0] = (ret >> 0) & 1;
- values[1] = (ret >> 1) & 1;
- values[2] = (ret >> 2) & 1;
+ *value_bitmap = ret;

mutex_lock(&st->lock);
- gpiod_set_array_value(ARRAY_SIZE(values), st->gpio_os->desc,
- values);
+ gpiod_set_array_value(3, st->gpio_os->desc, value_bitmap);
st->oversampling = val;
mutex_unlock(&st->lock);

diff --git a/drivers/tty/serial/serial_mctrl_gpio.c b/drivers/tty/serial/serial_mctrl_gpio.c
index 1c06325beaca..edfe8c688479 100644
--- a/drivers/tty/serial/serial_mctrl_gpio.c
+++ b/drivers/tty/serial/serial_mctrl_gpio.c
@@ -40,7 +40,7 @@ void mctrl_gpio_set(struct mctrl_gpios *gpios, unsigned int mctrl)
{
enum mctrl_gpio_idx i;
struct gpio_desc *desc_array[UART_GPIO_MAX];
- int value_array[UART_GPIO_MAX];
+ DECLARE_BITMAP(value_bitmap, UART_GPIO_MAX);
unsigned int count = 0;

if (gpios == NULL)
@@ -49,10 +49,11 @@ void mctrl_gpio_set(struct mctrl_gpios *gpios, unsigned int mctrl)
for (i = 0; i < UART_GPIO_MAX; i++)
if (gpios->gpio[i] && mctrl_gpios_desc[i].dir_out) {
desc_array[count] = gpios->gpio[i];
- value_array[count] = !!(mctrl & mctrl_gpios_desc[i].mctrl);
+ __assign_bit(count, value_bitmap,
+ !!(mctrl & mctrl_gpios_desc[i].mctrl));
count++;
}
- gpiod_set_array_value(count, desc_array, value_array);
+ gpiod_set_array_value(count, desc_array, value_bitmap);
}
EXPORT_SYMBOL_GPL(mctrl_gpio_set);

diff --git a/include/linux/gpio/consumer.h b/include/linux/gpio/consumer.h
index 21ddbe440030..1b21dc7b0fad 100644
--- a/include/linux/gpio/consumer.h
+++ b/include/linux/gpio/consumer.h
@@ -104,36 +104,38 @@ int gpiod_direction_output_raw(struct gpio_desc *desc, int value);
/* Value get/set from non-sleeping context */
int gpiod_get_value(const struct gpio_desc *desc);
int gpiod_get_array_value(unsigned int array_size,
- struct gpio_desc **desc_array, int *value_array);
+ struct gpio_desc **desc_array,
+ unsigned long *value_bitmap);
void gpiod_set_value(struct gpio_desc *desc, int value);
void gpiod_set_array_value(unsigned int array_size,
- struct gpio_desc **desc_array, int *value_array);
+ struct gpio_desc **desc_array,
+ unsigned long *value_bitmap);
int gpiod_get_raw_value(const struct gpio_desc *desc);
int gpiod_get_raw_array_value(unsigned int array_size,
struct gpio_desc **desc_array,
- int *value_array);
+ unsigned long *value_bitmap);
void gpiod_set_raw_value(struct gpio_desc *desc, int value);
int gpiod_set_raw_array_value(unsigned int array_size,
struct gpio_desc **desc_array,
- int *value_array);
+ unsigned long *value_bitmap);

/* Value get/set from sleeping context */
int gpiod_get_value_cansleep(const struct gpio_desc *desc);
int gpiod_get_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
- int *value_array);
+ unsigned long *value_bitmap);
void gpiod_set_value_cansleep(struct gpio_desc *desc, int value);
void gpiod_set_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
- int *value_array);
+ unsigned long *value_bitmap);
int gpiod_get_raw_value_cansleep(const struct gpio_desc *desc);
int gpiod_get_raw_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
- int *value_array);
+ unsigned long *value_bitmap);
void gpiod_set_raw_value_cansleep(struct gpio_desc *desc, int value);
int gpiod_set_raw_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
- int *value_array);
+ unsigned long *value_bitmap);

int gpiod_set_debounce(struct gpio_desc *desc, unsigned debounce);
int gpiod_set_transitory(struct gpio_desc *desc, bool transitory);
--
2.16.4


2018-08-31 22:57:51

by Janusz Krzysztofik

[permalink] [raw]
Subject: [PATCH v6 3/4] gpiolib: Pass array info to get/set array functions

In order to make use of array info obtained from gpiod_get_array() and
speed up processing of arrays matching single GPIO chip layout, that
information must be passed to get/set array functions. Extend the
functions' API with that additional parameter and update all users.
Pass NULL if a user bulids an array itself from single GPIOs.

Cc: Jonathan Corbet <[email protected]>
Cc: Miguel Ojeda Sandonis <[email protected]>
Cc: Peter Korsgaard <[email protected]>
Cc: Peter Rosin <[email protected]>
Cc: Andrew Lunn <[email protected]>
Cc: Florian Fainelli <[email protected]>
Cc: "David S. Miller" <[email protected]>
Cc: Dominik Brodowski <[email protected]>
Cc: Kishon Vijay Abraham I <[email protected]>
Cc: Lars-Peter Clausen <[email protected]>
Cc: Michael Hennerich <[email protected]>
Cc: Jonathan Cameron <[email protected]>
Cc: Hartmut Knaack <[email protected]>
Cc: Peter Meerwald-Stadler <[email protected]>
Cc: Greg Kroah-Hartman <[email protected]>
Cc: Jiri Slaby <[email protected]>
Signed-off-by: Janusz Krzysztofik <[email protected]>
Acked-by: Ulf Hansson <[email protected]>
---
Documentation/driver-api/gpio/consumer.rst | 14 ++++++++++--
drivers/auxdisplay/hd44780.c | 12 ++++++----
drivers/bus/ts-nbus.c | 6 +++--
drivers/gpio/gpio-max3191x.c | 6 +++--
drivers/gpio/gpiolib.c | 34 ++++++++++++++++++++---------
drivers/gpio/gpiolib.h | 2 ++
drivers/i2c/muxes/i2c-mux-gpio.c | 2 +-
drivers/mmc/core/pwrseq_simple.c | 2 +-
drivers/mux/gpio.c | 3 ++-
drivers/net/phy/mdio-mux-gpio.c | 2 +-
drivers/pcmcia/soc_common.c | 3 ++-
drivers/phy/motorola/phy-mapphone-mdm6600.c | 4 +++-
drivers/staging/iio/adc/ad7606.c | 3 ++-
drivers/tty/serial/serial_mctrl_gpio.c | 2 +-
include/linux/gpio/consumer.h | 8 +++++++
15 files changed, 75 insertions(+), 28 deletions(-)

diff --git a/Documentation/driver-api/gpio/consumer.rst b/Documentation/driver-api/gpio/consumer.rst
index 7e0298b9a7b9..0afd95a12b10 100644
--- a/Documentation/driver-api/gpio/consumer.rst
+++ b/Documentation/driver-api/gpio/consumer.rst
@@ -325,28 +325,36 @@ The following functions get or set the values of an array of GPIOs::

int gpiod_get_array_value(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap);
int gpiod_get_raw_array_value(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap);
int gpiod_get_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap);
int gpiod_get_raw_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap);

void gpiod_set_array_value(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap)
void gpiod_set_raw_array_value(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap)
void gpiod_set_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap)
void gpiod_set_raw_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap)

The array can be an arbitrary set of GPIOs. The functions will try to access
@@ -358,6 +366,7 @@ accessed sequentially.
The functions take three arguments:
* array_size - the number of array elements
* desc_array - an array of GPIO descriptors
+ * array_info - optional information obtained from gpiod_array_get()
* value_bitmap - a bitmap to store the GPIOs' values (get) or
a bitmap of values to assign to the GPIOs (set)

@@ -368,12 +377,13 @@ the struct gpio_descs returned by gpiod_get_array()::

struct gpio_descs *my_gpio_descs = gpiod_get_array(...);
gpiod_set_array_value(my_gpio_descs->ndescs, my_gpio_descs->desc,
- my_gpio_value_bitmap);
+ my_gpio_descs->info, my_gpio_value_bitmap);

It is also possible to access a completely arbitrary array of descriptors. The
descriptors may be obtained using any combination of gpiod_get() and
gpiod_get_array(). Afterwards the array of descriptors has to be setup
-manually before it can be passed to one of the above functions.
+manually before it can be passed to one of the above functions. In that case,
+array_info should be set to NULL.

Note that for optimal performance GPIOs belonging to the same chip should be
contiguous within the array of descriptors.
diff --git a/drivers/auxdisplay/hd44780.c b/drivers/auxdisplay/hd44780.c
index 7ee4f9a65bfc..4134fd090206 100644
--- a/drivers/auxdisplay/hd44780.c
+++ b/drivers/auxdisplay/hd44780.c
@@ -70,7 +70,8 @@ static void hd44780_write_gpio8(struct hd44780 *hd, u8 val, unsigned int rs)
n = hd->pins[PIN_CTRL_RW] ? 10 : 9;

/* Present the data to the port */
- gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA0], value_bitmap);
+ gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA0], NULL,
+ value_bitmap);

hd44780_strobe_gpio(hd);
}
@@ -87,7 +88,8 @@ static void hd44780_write_gpio4(struct hd44780 *hd, u8 val, unsigned int rs)
n = hd->pins[PIN_CTRL_RW] ? 6 : 5;

/* Present the data to the port */
- gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA4], value_bitmap);
+ gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA4], NULL,
+ value_bitmap);

hd44780_strobe_gpio(hd);

@@ -96,7 +98,8 @@ static void hd44780_write_gpio4(struct hd44780 *hd, u8 val, unsigned int rs)
*value_bitmap |= val & 0x0f;

/* Present the data to the port */
- gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA4], value_bitmap);
+ gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA4], NULL,
+ value_bitmap);

hd44780_strobe_gpio(hd);
}
@@ -153,7 +156,8 @@ static void hd44780_write_cmd_raw_gpio4(struct charlcd *lcd, int cmd)
n = hd->pins[PIN_CTRL_RW] ? 6 : 5;

/* Present the data to the port */
- gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA4], value_bitmap);
+ gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA4], NULL,
+ value_bitmap);

hd44780_strobe_gpio(hd);
}
diff --git a/drivers/bus/ts-nbus.c b/drivers/bus/ts-nbus.c
index 2ac5239df865..0a8fa64a4050 100644
--- a/drivers/bus/ts-nbus.c
+++ b/drivers/bus/ts-nbus.c
@@ -114,7 +114,8 @@ static void ts_nbus_reset_bus(struct ts_nbus *ts_nbus)

*value_bitmap = 0;

- gpiod_set_array_value_cansleep(8, ts_nbus->data->desc, value_bitmap);
+ gpiod_set_array_value_cansleep(8, ts_nbus->data->desc,
+ ts_nbus->data->info, value_bitmap);
gpiod_set_value_cansleep(ts_nbus->csn, 0);
gpiod_set_value_cansleep(ts_nbus->strobe, 0);
gpiod_set_value_cansleep(ts_nbus->ale, 0);
@@ -157,7 +158,8 @@ static void ts_nbus_write_byte(struct ts_nbus *ts_nbus, u8 byte)
struct gpio_descs *gpios = ts_nbus->data;
DECLARE_BITMAP(value_bitmap, 8) = { byte, };

- gpiod_set_array_value_cansleep(8, gpios->desc, value_bitmap);
+ gpiod_set_array_value_cansleep(8, gpios->desc, gpios->info,
+ value_bitmap);
}

/*
diff --git a/drivers/gpio/gpio-max3191x.c b/drivers/gpio/gpio-max3191x.c
index c4ec1c82af27..4b43b5dabfd2 100644
--- a/drivers/gpio/gpio-max3191x.c
+++ b/drivers/gpio/gpio-max3191x.c
@@ -313,6 +313,7 @@ static int max3191x_set_config(struct gpio_chip *gpio, unsigned int offset,

static void gpiod_set_array_single_value_cansleep(unsigned int ndescs,
struct gpio_desc **desc,
+ struct gpio_array *info,
int value)
{
unsigned long *value_bitmap;
@@ -327,7 +328,7 @@ static void gpiod_set_array_single_value_cansleep(unsigned int ndescs,
else
bitmap_zero(value_bitmap, ndescs);

- gpiod_set_array_value_cansleep(ndescs, desc, value_bitmap);
+ gpiod_set_array_value_cansleep(ndescs, desc, info, value_bitmap);
kfree(value_bitmap);
}

@@ -400,7 +401,8 @@ static int max3191x_probe(struct spi_device *spi)
if (max3191x->modesel_pins)
gpiod_set_array_single_value_cansleep(
max3191x->modesel_pins->ndescs,
- max3191x->modesel_pins->desc, max3191x->mode);
+ max3191x->modesel_pins->desc,
+ max3191x->modesel_pins->info, max3191x->mode);

max3191x->ignore_uv = device_property_read_bool(dev,
"maxim,ignore-undervoltage");
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index c1ed1c759345..4d26cdbdb7cf 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -435,7 +435,7 @@ static long linehandle_ioctl(struct file *filep, unsigned int cmd,
int ret = gpiod_get_array_value_complex(false,
true,
lh->numdescs,
- lh->descs,
+ lh->descs, NULL,
value_bitmap);
if (ret)
return ret;
@@ -467,7 +467,7 @@ static long linehandle_ioctl(struct file *filep, unsigned int cmd,
return gpiod_set_array_value_complex(false,
true,
lh->numdescs,
- lh->descs,
+ lh->descs, NULL,
value_bitmap);
}
return -EINVAL;
@@ -2784,6 +2784,7 @@ static int gpio_chip_get_multiple(struct gpio_chip *chip,
int gpiod_get_array_value_complex(bool raw, bool can_sleep,
unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap)
{
int i = 0;
@@ -2908,12 +2909,14 @@ EXPORT_SYMBOL_GPL(gpiod_get_value);
*/
int gpiod_get_raw_array_value(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap)
{
if (!desc_array)
return -EINVAL;
return gpiod_get_array_value_complex(true, false, array_size,
- desc_array, value_bitmap);
+ desc_array, array_info,
+ value_bitmap);
}
EXPORT_SYMBOL_GPL(gpiod_get_raw_array_value);

@@ -2931,12 +2934,14 @@ EXPORT_SYMBOL_GPL(gpiod_get_raw_array_value);
*/
int gpiod_get_array_value(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap)
{
if (!desc_array)
return -EINVAL;
return gpiod_get_array_value_complex(false, false, array_size,
- desc_array, value_bitmap);
+ desc_array, array_info,
+ value_bitmap);
}
EXPORT_SYMBOL_GPL(gpiod_get_array_value);

@@ -3029,6 +3034,7 @@ static void gpio_chip_set_multiple(struct gpio_chip *chip,
int gpiod_set_array_value_complex(bool raw, bool can_sleep,
unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap)
{
int i = 0;
@@ -3166,12 +3172,13 @@ EXPORT_SYMBOL_GPL(gpiod_set_value);
*/
int gpiod_set_raw_array_value(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap)
{
if (!desc_array)
return -EINVAL;
return gpiod_set_array_value_complex(true, false, array_size,
- desc_array, value_bitmap);
+ desc_array, array_info, value_bitmap);
}
EXPORT_SYMBOL_GPL(gpiod_set_raw_array_value);

@@ -3189,12 +3196,13 @@ EXPORT_SYMBOL_GPL(gpiod_set_raw_array_value);
*/
void gpiod_set_array_value(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap)
{
if (!desc_array)
return;
gpiod_set_array_value_complex(false, false, array_size, desc_array,
- value_bitmap);
+ array_info, value_bitmap);
}
EXPORT_SYMBOL_GPL(gpiod_set_array_value);

@@ -3426,13 +3434,15 @@ EXPORT_SYMBOL_GPL(gpiod_get_value_cansleep);
*/
int gpiod_get_raw_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap)
{
might_sleep_if(extra_checks);
if (!desc_array)
return -EINVAL;
return gpiod_get_array_value_complex(true, true, array_size,
- desc_array, value_bitmap);
+ desc_array, array_info,
+ value_bitmap);
}
EXPORT_SYMBOL_GPL(gpiod_get_raw_array_value_cansleep);

@@ -3449,13 +3459,15 @@ EXPORT_SYMBOL_GPL(gpiod_get_raw_array_value_cansleep);
*/
int gpiod_get_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap)
{
might_sleep_if(extra_checks);
if (!desc_array)
return -EINVAL;
return gpiod_get_array_value_complex(false, true, array_size,
- desc_array, value_bitmap);
+ desc_array, array_info,
+ value_bitmap);
}
EXPORT_SYMBOL_GPL(gpiod_get_array_value_cansleep);

@@ -3508,13 +3520,14 @@ EXPORT_SYMBOL_GPL(gpiod_set_value_cansleep);
*/
int gpiod_set_raw_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap)
{
might_sleep_if(extra_checks);
if (!desc_array)
return -EINVAL;
return gpiod_set_array_value_complex(true, true, array_size, desc_array,
- value_bitmap);
+ array_info, value_bitmap);
}
EXPORT_SYMBOL_GPL(gpiod_set_raw_array_value_cansleep);

@@ -3548,13 +3561,14 @@ void gpiod_add_lookup_tables(struct gpiod_lookup_table **tables, size_t n)
*/
void gpiod_set_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap)
{
might_sleep_if(extra_checks);
if (!desc_array)
return;
gpiod_set_array_value_complex(false, true, array_size, desc_array,
- value_bitmap);
+ array_info, value_bitmap);
}
EXPORT_SYMBOL_GPL(gpiod_set_array_value_cansleep);

diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h
index b60905d558b1..b65ca896b24d 100644
--- a/drivers/gpio/gpiolib.h
+++ b/drivers/gpio/gpiolib.h
@@ -196,10 +196,12 @@ struct gpio_desc *gpiochip_get_desc(struct gpio_chip *chip, u16 hwnum);
int gpiod_get_array_value_complex(bool raw, bool can_sleep,
unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap);
int gpiod_set_array_value_complex(bool raw, bool can_sleep,
unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap);

/* This is just passed between gpiolib and devres */
diff --git a/drivers/i2c/muxes/i2c-mux-gpio.c b/drivers/i2c/muxes/i2c-mux-gpio.c
index e28ddc20000d..ac77519d4a82 100644
--- a/drivers/i2c/muxes/i2c-mux-gpio.c
+++ b/drivers/i2c/muxes/i2c-mux-gpio.c
@@ -31,7 +31,7 @@ static void i2c_mux_gpio_set(const struct gpiomux *mux, unsigned val)
*value_bitmap = val;

gpiod_set_array_value_cansleep(mux->data.n_gpios,
- mux->gpios, value_bitmap);
+ mux->gpios, NULL, value_bitmap);
}

static int i2c_mux_gpio_select(struct i2c_mux_core *muxc, u32 chan)
diff --git a/drivers/mmc/core/pwrseq_simple.c b/drivers/mmc/core/pwrseq_simple.c
index fd7791e072b3..a57f8a7d5266 100644
--- a/drivers/mmc/core/pwrseq_simple.c
+++ b/drivers/mmc/core/pwrseq_simple.c
@@ -46,7 +46,7 @@ static void mmc_pwrseq_simple_set_gpios_value(struct mmc_pwrseq_simple *pwrseq,
*value_bitmap = value;

gpiod_set_array_value_cansleep(nvalues, reset_gpios->desc,
- value_bitmap);
+ reset_gpios->info, value_bitmap);
}
}

diff --git a/drivers/mux/gpio.c b/drivers/mux/gpio.c
index 17b7b1c21d90..728323207b8c 100644
--- a/drivers/mux/gpio.c
+++ b/drivers/mux/gpio.c
@@ -27,7 +27,8 @@ static int mux_gpio_set(struct mux_control *mux, int state)
*value_bitmap = state;

gpiod_set_array_value_cansleep(mux_gpio->gpios->ndescs,
- mux_gpio->gpios->desc, value_bitmap);
+ mux_gpio->gpios->desc,
+ mux_gpio->gpios->info, value_bitmap);

return 0;
}
diff --git a/drivers/net/phy/mdio-mux-gpio.c b/drivers/net/phy/mdio-mux-gpio.c
index 3907e9528949..42f5c253b62c 100644
--- a/drivers/net/phy/mdio-mux-gpio.c
+++ b/drivers/net/phy/mdio-mux-gpio.c
@@ -34,7 +34,7 @@ static int mdio_mux_gpio_switch_fn(int current_child, int desired_child,
*value_bitmap = desired_child;

gpiod_set_array_value_cansleep(s->gpios->ndescs, s->gpios->desc,
- value_bitmap);
+ s->gpios->info, value_bitmap);

return 0;
}
diff --git a/drivers/pcmcia/soc_common.c b/drivers/pcmcia/soc_common.c
index 44288b1a57ee..75c3e0bb2737 100644
--- a/drivers/pcmcia/soc_common.c
+++ b/drivers/pcmcia/soc_common.c
@@ -366,7 +366,8 @@ static int soc_common_pcmcia_config_skt(
}

if (n)
- gpiod_set_array_value_cansleep(n, descs, value_bitmap);
+ gpiod_set_array_value_cansleep(n, descs, NULL,
+ value_bitmap);

/*
* This really needs a better solution. The IRQ
diff --git a/drivers/phy/motorola/phy-mapphone-mdm6600.c b/drivers/phy/motorola/phy-mapphone-mdm6600.c
index b348272f7f1a..5611a04bc286 100644
--- a/drivers/phy/motorola/phy-mapphone-mdm6600.c
+++ b/drivers/phy/motorola/phy-mapphone-mdm6600.c
@@ -162,7 +162,8 @@ static void phy_mdm6600_cmd(struct phy_mdm6600 *ddata, int val)
*value_bitmap = val & ((1 << PHY_MDM6600_NR_CMD_LINES) - 1);

gpiod_set_array_value_cansleep(PHY_MDM6600_NR_CMD_LINES,
- ddata->cmd_gpios->desc, value_bitmap);
+ ddata->cmd_gpios->desc,
+ ddata->cmd_gpios->info, value_bitmap);
}

/**
@@ -181,6 +182,7 @@ static void phy_mdm6600_status(struct work_struct *work)

error = gpiod_get_array_value_cansleep(PHY_MDM6600_NR_STATUS_LINES,
ddata->status_gpios->desc,
+ ddata->status_gpios->info,
value_bitmap);
if (error)
return;
diff --git a/drivers/staging/iio/adc/ad7606.c b/drivers/staging/iio/adc/ad7606.c
index 18b6d10b0176..9115b5ee3601 100644
--- a/drivers/staging/iio/adc/ad7606.c
+++ b/drivers/staging/iio/adc/ad7606.c
@@ -230,7 +230,8 @@ static int ad7606_write_raw(struct iio_dev *indio_dev,
*value_bitmap = ret;

mutex_lock(&st->lock);
- gpiod_set_array_value(3, st->gpio_os->desc, value_bitmap);
+ gpiod_set_array_value(3, st->gpio_os->desc, st->gpio_os->info,
+ value_bitmap);
st->oversampling = val;
mutex_unlock(&st->lock);

diff --git a/drivers/tty/serial/serial_mctrl_gpio.c b/drivers/tty/serial/serial_mctrl_gpio.c
index edfe8c688479..66408108a951 100644
--- a/drivers/tty/serial/serial_mctrl_gpio.c
+++ b/drivers/tty/serial/serial_mctrl_gpio.c
@@ -53,7 +53,7 @@ void mctrl_gpio_set(struct mctrl_gpios *gpios, unsigned int mctrl)
!!(mctrl & mctrl_gpios_desc[i].mctrl));
count++;
}
- gpiod_set_array_value(count, desc_array, value_bitmap);
+ gpiod_set_array_value(count, desc_array, NULL, value_bitmap);
}
EXPORT_SYMBOL_GPL(mctrl_gpio_set);

diff --git a/include/linux/gpio/consumer.h b/include/linux/gpio/consumer.h
index 8dede3e886af..bf037ebe2ed8 100644
--- a/include/linux/gpio/consumer.h
+++ b/include/linux/gpio/consumer.h
@@ -114,36 +114,44 @@ int gpiod_direction_output_raw(struct gpio_desc *desc, int value);
int gpiod_get_value(const struct gpio_desc *desc);
int gpiod_get_array_value(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap);
void gpiod_set_value(struct gpio_desc *desc, int value);
void gpiod_set_array_value(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap);
int gpiod_get_raw_value(const struct gpio_desc *desc);
int gpiod_get_raw_array_value(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap);
void gpiod_set_raw_value(struct gpio_desc *desc, int value);
int gpiod_set_raw_array_value(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap);

/* Value get/set from sleeping context */
int gpiod_get_value_cansleep(const struct gpio_desc *desc);
int gpiod_get_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap);
void gpiod_set_value_cansleep(struct gpio_desc *desc, int value);
void gpiod_set_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap);
int gpiod_get_raw_value_cansleep(const struct gpio_desc *desc);
int gpiod_get_raw_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap);
void gpiod_set_raw_value_cansleep(struct gpio_desc *desc, int value);
int gpiod_set_raw_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap);

int gpiod_set_debounce(struct gpio_desc *desc, unsigned debounce);
--
2.16.4


2018-08-31 22:57:51

by Janusz Krzysztofik

[permalink] [raw]
Subject: [PATCH v6 2/4] gpiolib: Identify arrays matching GPIO hardware

Certain GPIO array lookup results may map directly to GPIO pins of a
single GPIO chip in hardware order. If that condition is recognized
and handled efficiently, significant performance gain of get/set array
functions may be possible.

While processing a request for an array of GPIO descriptors, identify
those which represent corresponding pins of a single GPIO chip. Skip
over pins which require open source or open drain special processing.
Moreover, identify pins which require inversion. Pass a pointer to
that information with the array to the caller so it can benefit from
enhanced performance as soon as get/set array functions can accept and
make efficient use of it.

Cc: Jonathan Corbet <[email protected]>
Signed-off-by: Janusz Krzysztofik <[email protected]>
---
Documentation/driver-api/gpio/consumer.rst | 4 +-
drivers/gpio/gpiolib.c | 72 +++++++++++++++++++++++++++++-
drivers/gpio/gpiolib.h | 9 ++++
include/linux/gpio/consumer.h | 9 ++++
4 files changed, 92 insertions(+), 2 deletions(-)

diff --git a/Documentation/driver-api/gpio/consumer.rst b/Documentation/driver-api/gpio/consumer.rst
index ed68042ddccf..7e0298b9a7b9 100644
--- a/Documentation/driver-api/gpio/consumer.rst
+++ b/Documentation/driver-api/gpio/consumer.rst
@@ -109,9 +109,11 @@ For a function using multiple GPIOs all of those can be obtained with one call::
enum gpiod_flags flags)

This function returns a struct gpio_descs which contains an array of
-descriptors::
+descriptors. It also contains a pointer to a gpiolib private structure which,
+if passed back to get/set array functions, may speed up I/O proocessing::

struct gpio_descs {
+ struct gpio_array *info;
unsigned int ndescs;
struct gpio_desc *desc[];
}
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index f0e9ffa8cab6..c1ed1c759345 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -4174,7 +4174,9 @@ struct gpio_descs *__must_check gpiod_get_array(struct device *dev,
{
struct gpio_desc *desc;
struct gpio_descs *descs;
- int count;
+ struct gpio_array *array_info = NULL;
+ struct gpio_chip *chip;
+ int count, bitmap_size;

count = gpiod_count(dev, con_id);
if (count < 0)
@@ -4190,9 +4192,77 @@ struct gpio_descs *__must_check gpiod_get_array(struct device *dev,
gpiod_put_array(descs);
return ERR_CAST(desc);
}
+
descs->desc[descs->ndescs] = desc;
+
+ chip = gpiod_to_chip(desc);
+ /*
+ * Select a chip of first array member
+ * whose index matches its pin hardware number
+ * as a candidate for fast bitmap processing.
+ */
+ if (!array_info && gpio_chip_hwgpio(desc) == descs->ndescs) {
+ struct gpio_descs *array;
+
+ bitmap_size = BITS_TO_LONGS(chip->ngpio > count ?
+ chip->ngpio : count);
+
+ array = kzalloc(struct_size(descs, desc, count) +
+ struct_size(array_info, invert_mask,
+ 3 * bitmap_size), GFP_KERNEL);
+ if (!array) {
+ gpiod_put_array(descs);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ memcpy(array, descs,
+ struct_size(descs, desc, descs->ndescs + 1));
+ kfree(descs);
+
+ descs = array;
+ array_info = (void *)(descs->desc + count);
+ array_info->get_mask = array_info->invert_mask +
+ bitmap_size;
+ array_info->set_mask = array_info->get_mask +
+ bitmap_size;
+
+ array_info->desc = descs->desc;
+ array_info->size = count;
+ array_info->chip = chip;
+ bitmap_set(array_info->get_mask, descs->ndescs,
+ count - descs->ndescs);
+ bitmap_set(array_info->set_mask, descs->ndescs,
+ count - descs->ndescs);
+ descs->info = array_info;
+ }
+ /*
+ * Unmark members which don't qualify for fast bitmap
+ * processing (different chip, not in hardware order)
+ */
+ if (array_info && (chip != array_info->chip ||
+ gpio_chip_hwgpio(desc) != descs->ndescs)) {
+ __clear_bit(descs->ndescs, array_info->get_mask);
+ __clear_bit(descs->ndescs, array_info->set_mask);
+ } else if (array_info) {
+ /* Exclude open drain or open source from fast output */
+ if (gpiochip_line_is_open_drain(chip, descs->ndescs) ||
+ gpiochip_line_is_open_source(chip, descs->ndescs))
+ __clear_bit(descs->ndescs,
+ array_info->set_mask);
+ /* Identify 'fast' pins which require invertion */
+ if (gpiod_is_active_low(desc))
+ __set_bit(descs->ndescs,
+ array_info->invert_mask);
+ }
+
descs->ndescs++;
}
+ if (array_info)
+ dev_dbg(dev,
+ "GPIO array info: chip=%s, size=%d, get_mask=%lx, set_mask=%lx, invert_mask=%lx\n",
+ array_info->chip->label, array_info->size,
+ *array_info->get_mask, *array_info->set_mask,
+ *array_info->invert_mask);
return descs;
}
EXPORT_SYMBOL_GPL(gpiod_get_array);
diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h
index 11e83d2eef89..b60905d558b1 100644
--- a/drivers/gpio/gpiolib.h
+++ b/drivers/gpio/gpiolib.h
@@ -183,6 +183,15 @@ static inline bool acpi_can_fallback_to_crs(struct acpi_device *adev,
}
#endif

+struct gpio_array {
+ struct gpio_desc **desc;
+ unsigned int size;
+ struct gpio_chip *chip;
+ unsigned long *get_mask;
+ unsigned long *set_mask;
+ unsigned long invert_mask[];
+};
+
struct gpio_desc *gpiochip_get_desc(struct gpio_chip *chip, u16 hwnum);
int gpiod_get_array_value_complex(bool raw, bool can_sleep,
unsigned int array_size,
diff --git a/include/linux/gpio/consumer.h b/include/linux/gpio/consumer.h
index 1b21dc7b0fad..8dede3e886af 100644
--- a/include/linux/gpio/consumer.h
+++ b/include/linux/gpio/consumer.h
@@ -17,11 +17,20 @@ struct device;
*/
struct gpio_desc;

+/**
+ * Opaque descriptor for a structure of GPIO array attributes. This structure
+ * is attached to struct gpiod_descs obtained from gpiod_get_array() and can be
+ * passed back to get/set array functions in order to activate fast processing
+ * path if applicable.
+ */
+struct gpio_array;
+
/**
* Struct containing an array of descriptors that can be obtained using
* gpiod_get_array().
*/
struct gpio_descs {
+ struct gpio_array *info;
unsigned int ndescs;
struct gpio_desc *desc[];
};
--
2.16.4


2018-08-31 22:58:30

by Janusz Krzysztofik

[permalink] [raw]
Subject: [PATCH v6 4/4] gpiolib: Implement fast processing path in get/set array

Certain GPIO descriptor arrays returned by gpio_get_array() may contain
information on direct mapping of array members to pins of a single GPIO
chip in hardware order. In such cases, bitmaps of values can be passed
directly from/to the chip's .get/set_multiple() callbacks without
wasting time on iterations.

Add respective code to gpiod_get/set_array_bitmap_complex() functions.
Pins not applicable for fast path are processed as before, skipping
over the 'fast' ones.

Cc: Jonathan Corbet <[email protected]>
Signed-off-by: Janusz Krzysztofik <[email protected]>
---
Documentation/driver-api/gpio/board.rst | 15 ++++++
Documentation/driver-api/gpio/consumer.rst | 8 +++
drivers/gpio/gpiolib.c | 87 ++++++++++++++++++++++++++++--
3 files changed, 105 insertions(+), 5 deletions(-)

diff --git a/Documentation/driver-api/gpio/board.rst b/Documentation/driver-api/gpio/board.rst
index 2c112553df84..c66821e033c2 100644
--- a/Documentation/driver-api/gpio/board.rst
+++ b/Documentation/driver-api/gpio/board.rst
@@ -193,3 +193,18 @@ And the table can be added to the board code as follows::

The line will be hogged as soon as the gpiochip is created or - in case the
chip was created earlier - when the hog table is registered.
+
+Arrays of pins
+--------------
+In addition to requesting pins belonging to a function one by one, a device may
+also request an array of pins assigned to the function. The way those pins are
+mapped to the device determines if the array qualifies for fast bitmap
+processing. If yes, a bitmap is passed over get/set array functions directly
+between a caller and a respective .get/set_multiple() callback of a GPIO chip.
+
+In order to qualify for fast bitmap processing, the pin mapping must meet the
+following requirements:
+- it must belong to the same chip as other 'fast' pins of the function,
+- its index within the function must match its hardware number within the chip.
+
+Open drain and open source pins are excluded from fast bitmap output processing.
diff --git a/Documentation/driver-api/gpio/consumer.rst b/Documentation/driver-api/gpio/consumer.rst
index 0afd95a12b10..cf992e5ab976 100644
--- a/Documentation/driver-api/gpio/consumer.rst
+++ b/Documentation/driver-api/gpio/consumer.rst
@@ -388,6 +388,14 @@ array_info should be set to NULL.
Note that for optimal performance GPIOs belonging to the same chip should be
contiguous within the array of descriptors.

+Still better performance may be achieved if array indexes of the descriptors
+match hardware pin numbers of a single chip. If an array passed to a get/set
+array function matches the one obtained from gpiod_get_array() and array_info
+associated with the array is also passed, the function may take a fast bitmap
+processing path, passing the value_bitmap argument directly to the respective
+.get/set_multiple() callback of the chip. That allows for utilization of GPIO
+banks as data I/O ports without much loss of performance.
+
The return value of gpiod_get_array_value() and its variants is 0 on success
or negative on error. Note the difference to gpiod_get_value(), which returns
0 or 1 on success to convey the GPIO value. With the array functions, the GPIO
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index 4d26cdbdb7cf..b799a89c4c17 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -2787,7 +2787,36 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep,
struct gpio_array *array_info,
unsigned long *value_bitmap)
{
- int i = 0;
+ int err, i = 0;
+
+ /*
+ * Validate array_info against desc_array and its size.
+ * It should immediately follow desc_array if both
+ * have been obtained from the same gpiod_get_array() call.
+ */
+ if (array_info && array_info->desc == desc_array &&
+ array_size <= array_info->size &&
+ (void *)array_info == desc_array + array_info->size) {
+ if (!can_sleep)
+ WARN_ON(array_info->chip->can_sleep);
+
+ err = gpio_chip_get_multiple(array_info->chip,
+ array_info->get_mask,
+ value_bitmap);
+ if (err)
+ return err;
+
+ if (!raw && !bitmap_empty(array_info->invert_mask, array_size))
+ bitmap_xor(value_bitmap, value_bitmap,
+ array_info->invert_mask, array_size);
+
+ if (bitmap_full(array_info->get_mask, array_size))
+ return 0;
+
+ i = find_first_zero_bit(array_info->get_mask, array_size);
+ } else {
+ array_info = NULL;
+ }

while (i < array_size) {
struct gpio_chip *chip = desc_array[i]->gdev->chip;
@@ -2818,7 +2847,12 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep,
int hwgpio = gpio_chip_hwgpio(desc);

__set_bit(hwgpio, mask);
- i++;
+
+ if (array_info)
+ find_next_zero_bit(array_info->get_mask,
+ array_size, i);
+ else
+ i++;
} while ((i < array_size) &&
(desc_array[i]->gdev->chip == chip));

@@ -2829,7 +2863,7 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep,
return ret;
}

- for (j = first; j < i; j++) {
+ for (j = first; j < i; ) {
const struct gpio_desc *desc = desc_array[j];
int hwgpio = gpio_chip_hwgpio(desc);
int value = test_bit(hwgpio, bits);
@@ -2838,6 +2872,11 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep,
value = !value;
__assign_bit(j, value_bitmap, value);
trace_gpio_value(desc_to_gpio(desc), 1, value);
+
+ if (array_info)
+ find_next_zero_bit(array_info->get_mask, i, j);
+ else
+ j++;
}

if (mask != fastpath)
@@ -3039,6 +3078,32 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep,
{
int i = 0;

+ /*
+ * Validate array_info against desc_array and its size.
+ * It should immediately follow desc_array if both
+ * have been obtained from the same gpiod_get_array() call.
+ */
+ if (array_info && array_info->desc == desc_array &&
+ array_size <= array_info->size &&
+ (void *)array_info == desc_array + array_info->size) {
+ if (!can_sleep)
+ WARN_ON(array_info->chip->can_sleep);
+
+ if (!raw && !bitmap_empty(array_info->invert_mask, array_size))
+ bitmap_xor(value_bitmap, value_bitmap,
+ array_info->invert_mask, array_size);
+
+ gpio_chip_set_multiple(array_info->chip, array_info->set_mask,
+ value_bitmap);
+
+ if (bitmap_full(array_info->set_mask, array_size))
+ return 0;
+
+ i = find_first_zero_bit(array_info->set_mask, array_size);
+ } else {
+ array_info = NULL;
+ }
+
while (i < array_size) {
struct gpio_chip *chip = desc_array[i]->gdev->chip;
unsigned long fastpath[2 * BITS_TO_LONGS(FASTPATH_NGPIO)];
@@ -3066,7 +3131,14 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep,
int hwgpio = gpio_chip_hwgpio(desc);
int value = test_bit(i, value_bitmap);

- if (!raw && test_bit(FLAG_ACTIVE_LOW, &desc->flags))
+ /*
+ * Pins applicable for fast input but not for
+ * fast output processing may have been already
+ * inverted inside the fast path, skip them.
+ */
+ if (!raw && !(array_info &&
+ test_bit(i, array_info->invert_mask)) &&
+ test_bit(FLAG_ACTIVE_LOW, &desc->flags))
value = !value;
trace_gpio_value(desc_to_gpio(desc), 0, value);
/*
@@ -3085,7 +3157,12 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep,
__clear_bit(hwgpio, bits);
count++;
}
- i++;
+
+ if (array_info)
+ find_next_zero_bit(array_info->set_mask,
+ array_size, i);
+ else
+ i++;
} while ((i < array_size) &&
(desc_array[i]->gdev->chip == chip));
/* push collected bits to outputs */
--
2.16.4


2018-09-01 00:25:50

by Peter Rosin

[permalink] [raw]
Subject: Re: [PATCH v6 1/4] gpiolib: Pass bitmaps, not integer arrays, to get/set array

On 2018-09-01 00:56, Janusz Krzysztofik wrote:
> Most users of get/set array functions iterate consecutive bits of data,
> usually a single integer, while processing array of results obtained
> from, or building an array of values to be passed to those functions.
> Save time wasted on those iterations by changing the functions' API to
> accept bitmaps.
>
> All current users are updated as well.
>
> More benefits from the change are expected as soon as planned support
> for accepting/passing those bitmaps directly from/to respective GPIO
> chip callbacks if applicable is implemented.

> diff --git a/drivers/i2c/muxes/i2c-mux-gpio.c b/drivers/i2c/muxes/i2c-mux-gpio.c
> index 401308e3d036..e28ddc20000d 100644
> --- a/drivers/i2c/muxes/i2c-mux-gpio.c
> +++ b/drivers/i2c/muxes/i2c-mux-gpio.c
> @@ -22,18 +22,16 @@ struct gpiomux {
> struct i2c_mux_gpio_platform_data data;
> unsigned gpio_base;
> struct gpio_desc **gpios;
> - int *values;
> };
>
> static void i2c_mux_gpio_set(const struct gpiomux *mux, unsigned val)
> {
> - int i;
> -
> - for (i = 0; i < mux->data.n_gpios; i++)
> - mux->values[i] = (val >> i) & 1;
> + DECLARE_BITMAP(value_bitmap, mux->data.n_gpios);

Picking a random driver for this comment, it applies to many of them.

I think this creates a VLA? Can't you, for the bit-count, just go with
BITS_PER_TYPE(unsigned)? Or whatever is appropriate for the driver
in question.

Also, I find that where you use DECLARE_BITMAP, the _bitmap suffix is
just noise and I would very much like to zap it.

Cheers,
Peter

> +
> + *value_bitmap = val;
>
> gpiod_set_array_value_cansleep(mux->data.n_gpios,
> - mux->gpios, mux->values);
> + mux->gpios, value_bitmap);
> }
>
> static int i2c_mux_gpio_select(struct i2c_mux_core *muxc, u32 chan)
> @@ -182,15 +180,13 @@ static int i2c_mux_gpio_probe(struct platform_device *pdev)
> return -EPROBE_DEFER;
>
> muxc = i2c_mux_alloc(parent, &pdev->dev, mux->data.n_values,
> - mux->data.n_gpios * sizeof(*mux->gpios) +
> - mux->data.n_gpios * sizeof(*mux->values), 0,
> + mux->data.n_gpios * sizeof(*mux->gpios), 0,
> i2c_mux_gpio_select, NULL);
> if (!muxc) {
> ret = -ENOMEM;
> goto alloc_failed;
> }
> mux->gpios = muxc->priv;
> - mux->values = (int *)(mux->gpios + mux->data.n_gpios);
> muxc->priv = mux;
>
> platform_set_drvdata(pdev, muxc);

2018-09-02 10:19:34

by Janusz Krzysztofik

[permalink] [raw]
Subject: Re: [PATCH v5 1/4] gpiolib: Pass bitmaps, not integer arrays, to get/set array

Hi Miguel,

On Thursday, August 30, 2018 1:10:59 PM CEST Miguel Ojeda wrote:
> Hi Janusz,
>
> On Wed, Aug 29, 2018 at 10:48 PM, Janusz Krzysztofik
> <[email protected]> wrote:
> > ...
> > /* High nibble + RS, RW */
> > - for (i = 4; i < 8; i++)
> > - values[PIN_DATA0 + i] = !!(val & BIT(i));
> > - values[PIN_CTRL_RS] = rs;
> > + value_bitmap[0] = val;
> > + __assign_bit(PIN_CTRL_RS, value_bitmap, rs);
> > n = 5;
> > if (hd->pins[PIN_CTRL_RW]) {
> > - values[PIN_CTRL_RW] = 0;
> > + __clear_bit(PIN_CTRL_RW, value_bitmap);
> > n++;
> > }
> > + value_bitmap[0] >>= PIN_DATA4;
> >
> > /* Present the data to the port */
> > - gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA4],
> > - &values[PIN_DATA4]);
> > + gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA4],
value_bitmap);
> >
> > hd44780_strobe_gpio(hd);
> >
> > /* Low nibble */
> > - for (i = 0; i < 4; i++)
> > - values[PIN_DATA4 + i] = !!(val & BIT(i));
> > + value_bitmap[0] &= ~((1 << PIN_DATA4) - 1);
> > + value_bitmap[0] |= val & ~((1 << PIN_DATA4) - 1);
>
> This is still wrong! What I originally meant in my v4 review is that
> there is an extra ~ in the second line.

Indeed, that's wrong, I missed your original point, sorry.

> Also, a couple of general comments:
>
> - Please review the list of CCs (I was not CC'd originally, so maybe
> there are other maintainers that aren't, either)

That's probably because early versions of the series, prior to v4, were not
touching existing GPIO API so there were no changes to users of gpiod_get/
set_array_value() and their variants. From v4 on, you are in the loop so don't
worry, you haven't missed anything.
But anyway, thanks for your suggestion to review the Cc; list, I've done that
for v7 and added still a few people who contributed most to the code being
changed.

> - In general, the new code seems harder to read than the original one
> (but that is my impression).

I hope we are slowly approaching acceptable readability in recent iterations.

Thanks,
Janusz




2018-09-02 12:03:16

by Janusz Krzysztofik

[permalink] [raw]
Subject: [PATCH v7 0/4] gpiolib: speed up GPIO array processing


The goal is to boost performance of get/set array functions while
processing GPIO arrays which represent pins of a signle chip in
hardware order. If resulting performance is close to PIO, GPIO API
can be used for data I/O without much loss of speed.

Created and tested on a low end Amstrad Delta board with NAND driver
updated to use GPIO API for data I/O. Performance degrade compared to
PIO is much better than before the optimization though not quite
satisfactory on my test hardware.


Janusz Krzysztofik (4):
gpiolib: Pass bitmaps, not integer arrays, to get/set array
gpiolib: Identify arrays matching GPIO hardware
gpiolib: Pass array info to get/set array functions
gpiolib: Implement fast processing path in get/set array


Changelog:
v7:
- add more people to Cc: - authors and/or those who contributed most to
the drivers in scope of the change,
[PATCH v7 1/4] gpiolib: Pass bitmaps, not integer arrays, to get/set:
- avoid VLAs, use data source type bit number as bitmap size if not
constant - great recommendation by Peter Rosin, thanks,
- revert names of local variables declared with DECLARE_BITMAP() from
'value_bitmap' to original names of value arrays they replace (but not
'value_array') - inspired by Peter Rosin suggestion - thanks!
drivers/gpio/gpio-max3191x.c:
- use bitmap_alloc() to be more consistent with DECLARE_BITMAP() pattern
used by other consumers,
drivers/phy/motorola/phy-mapphone-mdm6600.c:
- no need to mask unused bits of val before its assignment to bitmap,
passing PHY_MDM6600_NR_CMD_LINES to gpiod_set_array_value() as array/
bitmap size provides sufficient protection.

v6:
[PATCH v6 1/4] gpiolib: Pass bitmaps, not integer arrays, to get/set
- use DECLARE_BITMAP() macro for declaring value_bitmap - great idea by
David Laight, thanks!
drivers/auxdisplay/hd44780.c:
- simplify the code and adjust comments as recommended by Geert
Uytterhoeven - thanks!,
drivers/i2c/muxes/i2c-mux-gpio.c:
- drop .values member of struct gpiomux - details provided by Peter
Rosin, thanks!,
drivers/mux/gpio.c:
- drop .val member of struct mux_gpio - details provided by Peter
Rosin, thanks!,
drivers/net/phy/mdio-mux-gpio.c:
- drop .values member of struct mdio_mux_gpio_state and its processsing.

v5:
[PATCH v5 1/4] gpiolib: Pass bitmaps, not integer arrays, to get/set
- drivers/i2c/muxes/i2c-mux-gpio.c:
- drop assigment of values to struct gpiomux.values, as recommended
by Peter Rosin - thanks!,
- mark the .values member of the structure as obsolete,
- drivers/mux/gpio.c:
- drop assigment of values to struct mux_gpio.val, also recommended
by Peter Rosin - thanks!,
- merk the .val member of the structure as obsolete,
- drivers/auxdisplay/hd44780.c:
- fix incorrect bitmap size,
- use >>= operator to simplify notation,
both catched by Miguel Ojeda - thanks!,
- add Cc: clauses as well as Acked-by: collected so far.
[PATCH v5 2/4] gpiolib: Identify arrays matching GPIO hardware
- add Cc: clause.
[PATCH v5 3/4] gpiolib: Pass array info to get/set array functions
- add Cc: clauses as well as Acked-by: collected so far.
[PATCH v5 4/4] gpiolib: Implement fast processing path in get/set
- add Cc: clause.

v4:
That series was a follow up of the former "mtd: rawnand: ams-delta: Use
gpio-omap accessors for data I/O" which already contained some changes
to gpiolib. Those previous attempts were commented by Borris Brezillon
who suggested using GPIO API modified to accept bitmaps, and by Linus
Walleij who suggested still more great ideas for further immprovement
of the proposed API changes - thanks!


diffstat:
Documentation/driver-api/gpio/board.rst | 15 +
Documentation/driver-api/gpio/consumer.rst | 48 +++-
drivers/auxdisplay/hd44780.c | 67 ++----
drivers/bus/ts-nbus.c | 20 --
drivers/gpio/gpio-max3191x.c | 16 +
drivers/gpio/gpiolib.c | 273 ++++++++++++++++++++++------
drivers/gpio/gpiolib.h | 15 +
drivers/i2c/muxes/i2c-mux-gpio.c | 16 -
drivers/mmc/core/pwrseq_simple.c | 15 -
drivers/mux/gpio.c | 16 -
drivers/net/phy/mdio-mux-gpio.c | 13 -
drivers/pcmcia/soc_common.c | 10 -
drivers/phy/motorola/phy-mapphone-mdm6600.c | 17 -
drivers/staging/iio/adc/ad7606.c | 12 -
drivers/tty/serial/serial_mctrl_gpio.c | 9
include/linux/gpio/consumer.h | 35 ++-
16 files changed, 396 insertions(+), 201 deletions(-)

2018-09-02 12:03:16

by Janusz Krzysztofik

[permalink] [raw]
Subject: [PATCH v7 3/4] gpiolib: Pass array info to get/set array functions

In order to make use of array info obtained from gpiod_get_array() and
speed up processing of arrays matching single GPIO chip layout, that
information must be passed to get/set array functions. Extend the
functions' API with that additional parameter and update all users.
Pass NULL if a user bulids an array itself from single GPIOs.

Cc: Jonathan Corbet <[email protected]>
Cc: Miguel Ojeda Sandonis <[email protected]>
Cc: Geert Uytterhoeven <[email protected]>
Cc: Sebastien Bourdelin <[email protected]>
Cc: Lukas Wunner <[email protected]>
Cc: Peter Korsgaard <[email protected]>
Cc: Peter Rosin <[email protected]>
Cc: Andrew Lunn <[email protected]>
Cc: Florian Fainelli <[email protected]>
Cc: "David S. Miller" <[email protected]>
Cc: Rojhalat Ibrahim <[email protected]>
Cc: Dominik Brodowski <[email protected]>
Cc: Russell King <[email protected]>
Cc: Kishon Vijay Abraham I <[email protected]>
Cc: Tony Lindgren <[email protected]>
Cc: Lars-Peter Clausen <[email protected]>
Cc: Michael Hennerich <[email protected]>
Cc: Jonathan Cameron <[email protected]>
Cc: Hartmut Knaack <[email protected]>
Cc: Peter Meerwald-Stadler <[email protected]>
Cc: Greg Kroah-Hartman <[email protected]>
Cc: Jiri Slaby <[email protected]>
Cc: Yegor Yefremov <[email protected]>
Cc: Uwe Kleine-König <[email protected]>
Signed-off-by: Janusz Krzysztofik <[email protected]>
Acked-by: Ulf Hansson <[email protected]>
---
Documentation/driver-api/gpio/consumer.rst | 14 +++++++++++--
drivers/auxdisplay/hd44780.c | 8 ++++----
drivers/bus/ts-nbus.c | 5 +++--
drivers/gpio/gpio-max3191x.c | 6 ++++--
drivers/gpio/gpiolib.c | 32 +++++++++++++++++++++--------
drivers/gpio/gpiolib.h | 2 ++
drivers/i2c/muxes/i2c-mux-gpio.c | 3 ++-
drivers/mmc/core/pwrseq_simple.c | 2 +-
drivers/mux/gpio.c | 3 ++-
drivers/net/phy/mdio-mux-gpio.c | 2 +-
drivers/pcmcia/soc_common.c | 2 +-
drivers/phy/motorola/phy-mapphone-mdm6600.c | 4 +++-
drivers/staging/iio/adc/ad7606.c | 3 ++-
drivers/tty/serial/serial_mctrl_gpio.c | 2 +-
include/linux/gpio/consumer.h | 8 ++++++++
15 files changed, 70 insertions(+), 26 deletions(-)

diff --git a/Documentation/driver-api/gpio/consumer.rst b/Documentation/driver-api/gpio/consumer.rst
index 7e0298b9a7b9..0afd95a12b10 100644
--- a/Documentation/driver-api/gpio/consumer.rst
+++ b/Documentation/driver-api/gpio/consumer.rst
@@ -325,28 +325,36 @@ The following functions get or set the values of an array of GPIOs::

int gpiod_get_array_value(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap);
int gpiod_get_raw_array_value(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap);
int gpiod_get_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap);
int gpiod_get_raw_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap);

void gpiod_set_array_value(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap)
void gpiod_set_raw_array_value(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap)
void gpiod_set_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap)
void gpiod_set_raw_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap)

The array can be an arbitrary set of GPIOs. The functions will try to access
@@ -358,6 +366,7 @@ accessed sequentially.
The functions take three arguments:
* array_size - the number of array elements
* desc_array - an array of GPIO descriptors
+ * array_info - optional information obtained from gpiod_array_get()
* value_bitmap - a bitmap to store the GPIOs' values (get) or
a bitmap of values to assign to the GPIOs (set)

@@ -368,12 +377,13 @@ the struct gpio_descs returned by gpiod_get_array()::

struct gpio_descs *my_gpio_descs = gpiod_get_array(...);
gpiod_set_array_value(my_gpio_descs->ndescs, my_gpio_descs->desc,
- my_gpio_value_bitmap);
+ my_gpio_descs->info, my_gpio_value_bitmap);

It is also possible to access a completely arbitrary array of descriptors. The
descriptors may be obtained using any combination of gpiod_get() and
gpiod_get_array(). Afterwards the array of descriptors has to be setup
-manually before it can be passed to one of the above functions.
+manually before it can be passed to one of the above functions. In that case,
+array_info should be set to NULL.

Note that for optimal performance GPIOs belonging to the same chip should be
contiguous within the array of descriptors.
diff --git a/drivers/auxdisplay/hd44780.c b/drivers/auxdisplay/hd44780.c
index 333e30e378b5..033024df3962 100644
--- a/drivers/auxdisplay/hd44780.c
+++ b/drivers/auxdisplay/hd44780.c
@@ -70,7 +70,7 @@ static void hd44780_write_gpio8(struct hd44780 *hd, u8 val, unsigned int rs)
n = hd->pins[PIN_CTRL_RW] ? 10 : 9;

/* Present the data to the port */
- gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA0], values);
+ gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA0], NULL, values);

hd44780_strobe_gpio(hd);
}
@@ -87,7 +87,7 @@ static void hd44780_write_gpio4(struct hd44780 *hd, u8 val, unsigned int rs)
n = hd->pins[PIN_CTRL_RW] ? 6 : 5;

/* Present the data to the port */
- gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA4], values);
+ gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA4], NULL, values);

hd44780_strobe_gpio(hd);

@@ -96,7 +96,7 @@ static void hd44780_write_gpio4(struct hd44780 *hd, u8 val, unsigned int rs)
*values |= val & 0x0f;

/* Present the data to the port */
- gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA4], values);
+ gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA4], NULL, values);

hd44780_strobe_gpio(hd);
}
@@ -152,7 +152,7 @@ static void hd44780_write_cmd_raw_gpio4(struct charlcd *lcd, int cmd)
n = hd->pins[PIN_CTRL_RW] ? 6 : 5;

/* Present the data to the port */
- gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA4], values);
+ gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA4], NULL, values);

hd44780_strobe_gpio(hd);
}
diff --git a/drivers/bus/ts-nbus.c b/drivers/bus/ts-nbus.c
index 6499957a8044..60796f2db6fc 100644
--- a/drivers/bus/ts-nbus.c
+++ b/drivers/bus/ts-nbus.c
@@ -114,7 +114,8 @@ static void ts_nbus_reset_bus(struct ts_nbus *ts_nbus)

*values = 0;

- gpiod_set_array_value_cansleep(8, ts_nbus->data->desc, values);
+ gpiod_set_array_value_cansleep(8, ts_nbus->data->desc,
+ ts_nbus->data->info, values);
gpiod_set_value_cansleep(ts_nbus->csn, 0);
gpiod_set_value_cansleep(ts_nbus->strobe, 0);
gpiod_set_value_cansleep(ts_nbus->ale, 0);
@@ -159,7 +160,7 @@ static void ts_nbus_write_byte(struct ts_nbus *ts_nbus, u8 byte)

*values = byte;

- gpiod_set_array_value_cansleep(8, gpios->desc, values);
+ gpiod_set_array_value_cansleep(8, gpios->desc, gpios->info, values);
}

/*
diff --git a/drivers/gpio/gpio-max3191x.c b/drivers/gpio/gpio-max3191x.c
index bd4a245fc5a0..9a8876abeb57 100644
--- a/drivers/gpio/gpio-max3191x.c
+++ b/drivers/gpio/gpio-max3191x.c
@@ -313,6 +313,7 @@ static int max3191x_set_config(struct gpio_chip *gpio, unsigned int offset,

static void gpiod_set_array_single_value_cansleep(unsigned int ndescs,
struct gpio_desc **desc,
+ struct gpio_array *info,
int value)
{
unsigned long *values;
@@ -326,7 +327,7 @@ static void gpiod_set_array_single_value_cansleep(unsigned int ndescs,
else
bitmap_zero(values, ndescs);

- gpiod_set_array_value_cansleep(ndescs, desc, values);
+ gpiod_set_array_value_cansleep(ndescs, desc, info, values);
kfree(values);
}

@@ -399,7 +400,8 @@ static int max3191x_probe(struct spi_device *spi)
if (max3191x->modesel_pins)
gpiod_set_array_single_value_cansleep(
max3191x->modesel_pins->ndescs,
- max3191x->modesel_pins->desc, max3191x->mode);
+ max3191x->modesel_pins->desc,
+ max3191x->modesel_pins->info, max3191x->mode);

max3191x->ignore_uv = device_property_read_bool(dev,
"maxim,ignore-undervoltage");
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index 141f2f290538..cef6ee31fe05 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -436,6 +436,7 @@ static long linehandle_ioctl(struct file *filep, unsigned int cmd,
true,
lh->numdescs,
lh->descs,
+ NULL,
vals);
if (ret)
return ret;
@@ -468,6 +469,7 @@ static long linehandle_ioctl(struct file *filep, unsigned int cmd,
true,
lh->numdescs,
lh->descs,
+ NULL,
vals);
}
return -EINVAL;
@@ -2784,6 +2786,7 @@ static int gpio_chip_get_multiple(struct gpio_chip *chip,
int gpiod_get_array_value_complex(bool raw, bool can_sleep,
unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap)
{
int i = 0;
@@ -2908,12 +2911,14 @@ EXPORT_SYMBOL_GPL(gpiod_get_value);
*/
int gpiod_get_raw_array_value(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap)
{
if (!desc_array)
return -EINVAL;
return gpiod_get_array_value_complex(true, false, array_size,
- desc_array, value_bitmap);
+ desc_array, array_info,
+ value_bitmap);
}
EXPORT_SYMBOL_GPL(gpiod_get_raw_array_value);

@@ -2931,12 +2936,14 @@ EXPORT_SYMBOL_GPL(gpiod_get_raw_array_value);
*/
int gpiod_get_array_value(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap)
{
if (!desc_array)
return -EINVAL;
return gpiod_get_array_value_complex(false, false, array_size,
- desc_array, value_bitmap);
+ desc_array, array_info,
+ value_bitmap);
}
EXPORT_SYMBOL_GPL(gpiod_get_array_value);

@@ -3029,6 +3036,7 @@ static void gpio_chip_set_multiple(struct gpio_chip *chip,
int gpiod_set_array_value_complex(bool raw, bool can_sleep,
unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap)
{
int i = 0;
@@ -3166,12 +3174,13 @@ EXPORT_SYMBOL_GPL(gpiod_set_value);
*/
int gpiod_set_raw_array_value(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap)
{
if (!desc_array)
return -EINVAL;
return gpiod_set_array_value_complex(true, false, array_size,
- desc_array, value_bitmap);
+ desc_array, array_info, value_bitmap);
}
EXPORT_SYMBOL_GPL(gpiod_set_raw_array_value);

@@ -3189,12 +3198,13 @@ EXPORT_SYMBOL_GPL(gpiod_set_raw_array_value);
*/
void gpiod_set_array_value(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap)
{
if (!desc_array)
return;
gpiod_set_array_value_complex(false, false, array_size, desc_array,
- value_bitmap);
+ array_info, value_bitmap);
}
EXPORT_SYMBOL_GPL(gpiod_set_array_value);

@@ -3426,13 +3436,15 @@ EXPORT_SYMBOL_GPL(gpiod_get_value_cansleep);
*/
int gpiod_get_raw_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap)
{
might_sleep_if(extra_checks);
if (!desc_array)
return -EINVAL;
return gpiod_get_array_value_complex(true, true, array_size,
- desc_array, value_bitmap);
+ desc_array, array_info,
+ value_bitmap);
}
EXPORT_SYMBOL_GPL(gpiod_get_raw_array_value_cansleep);

@@ -3449,13 +3461,15 @@ EXPORT_SYMBOL_GPL(gpiod_get_raw_array_value_cansleep);
*/
int gpiod_get_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap)
{
might_sleep_if(extra_checks);
if (!desc_array)
return -EINVAL;
return gpiod_get_array_value_complex(false, true, array_size,
- desc_array, value_bitmap);
+ desc_array, array_info,
+ value_bitmap);
}
EXPORT_SYMBOL_GPL(gpiod_get_array_value_cansleep);

@@ -3508,13 +3522,14 @@ EXPORT_SYMBOL_GPL(gpiod_set_value_cansleep);
*/
int gpiod_set_raw_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap)
{
might_sleep_if(extra_checks);
if (!desc_array)
return -EINVAL;
return gpiod_set_array_value_complex(true, true, array_size, desc_array,
- value_bitmap);
+ array_info, value_bitmap);
}
EXPORT_SYMBOL_GPL(gpiod_set_raw_array_value_cansleep);

@@ -3548,13 +3563,14 @@ void gpiod_add_lookup_tables(struct gpiod_lookup_table **tables, size_t n)
*/
void gpiod_set_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap)
{
might_sleep_if(extra_checks);
if (!desc_array)
return;
gpiod_set_array_value_complex(false, true, array_size, desc_array,
- value_bitmap);
+ array_info, value_bitmap);
}
EXPORT_SYMBOL_GPL(gpiod_set_array_value_cansleep);

diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h
index b60905d558b1..b65ca896b24d 100644
--- a/drivers/gpio/gpiolib.h
+++ b/drivers/gpio/gpiolib.h
@@ -196,10 +196,12 @@ struct gpio_desc *gpiochip_get_desc(struct gpio_chip *chip, u16 hwnum);
int gpiod_get_array_value_complex(bool raw, bool can_sleep,
unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap);
int gpiod_set_array_value_complex(bool raw, bool can_sleep,
unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap);

/* This is just passed between gpiolib and devres */
diff --git a/drivers/i2c/muxes/i2c-mux-gpio.c b/drivers/i2c/muxes/i2c-mux-gpio.c
index 0717cfb56732..c6e1c7776a56 100644
--- a/drivers/i2c/muxes/i2c-mux-gpio.c
+++ b/drivers/i2c/muxes/i2c-mux-gpio.c
@@ -30,7 +30,8 @@ static void i2c_mux_gpio_set(const struct gpiomux *mux, unsigned val)

*values = val;

- gpiod_set_array_value_cansleep(mux->data.n_gpios, mux->gpios, values);
+ gpiod_set_array_value_cansleep(mux->data.n_gpios, mux->gpios, NULL,
+ values);
}

static int i2c_mux_gpio_select(struct i2c_mux_core *muxc, u32 chan)
diff --git a/drivers/mmc/core/pwrseq_simple.c b/drivers/mmc/core/pwrseq_simple.c
index 5ad764c88b50..a17252f51418 100644
--- a/drivers/mmc/core/pwrseq_simple.c
+++ b/drivers/mmc/core/pwrseq_simple.c
@@ -46,7 +46,7 @@ static void mmc_pwrseq_simple_set_gpios_value(struct mmc_pwrseq_simple *pwrseq,
*values = value;

gpiod_set_array_value_cansleep(nvalues, reset_gpios->desc,
- values);
+ reset_gpios->info, values);
}
}

diff --git a/drivers/mux/gpio.c b/drivers/mux/gpio.c
index 1b6b4cc22a2c..4f58a3198c4e 100644
--- a/drivers/mux/gpio.c
+++ b/drivers/mux/gpio.c
@@ -27,7 +27,8 @@ static int mux_gpio_set(struct mux_control *mux, int state)
*values = state;

gpiod_set_array_value_cansleep(mux_gpio->gpios->ndescs,
- mux_gpio->gpios->desc, values);
+ mux_gpio->gpios->desc,
+ mux_gpio->gpios->info, values);

return 0;
}
diff --git a/drivers/net/phy/mdio-mux-gpio.c b/drivers/net/phy/mdio-mux-gpio.c
index eb8c56b83c70..7d8bea21f099 100644
--- a/drivers/net/phy/mdio-mux-gpio.c
+++ b/drivers/net/phy/mdio-mux-gpio.c
@@ -34,7 +34,7 @@ static int mdio_mux_gpio_switch_fn(int current_child, int desired_child,
*values = desired_child;

gpiod_set_array_value_cansleep(s->gpios->ndescs, s->gpios->desc,
- values);
+ s->gpios->info, values);

return 0;
}
diff --git a/drivers/pcmcia/soc_common.c b/drivers/pcmcia/soc_common.c
index 388dbdc46129..8447eeeaf3fe 100644
--- a/drivers/pcmcia/soc_common.c
+++ b/drivers/pcmcia/soc_common.c
@@ -365,7 +365,7 @@ static int soc_common_pcmcia_config_skt(
}

if (n)
- gpiod_set_array_value_cansleep(n, descs, values);
+ gpiod_set_array_value_cansleep(n, descs, NULL, values);

/*
* This really needs a better solution. The IRQ
diff --git a/drivers/phy/motorola/phy-mapphone-mdm6600.c b/drivers/phy/motorola/phy-mapphone-mdm6600.c
index 4de7f4577433..08e71c7ae199 100644
--- a/drivers/phy/motorola/phy-mapphone-mdm6600.c
+++ b/drivers/phy/motorola/phy-mapphone-mdm6600.c
@@ -162,7 +162,8 @@ static void phy_mdm6600_cmd(struct phy_mdm6600 *ddata, int val)
*values = val;

gpiod_set_array_value_cansleep(PHY_MDM6600_NR_CMD_LINES,
- ddata->cmd_gpios->desc, values);
+ ddata->cmd_gpios->desc,
+ ddata->cmd_gpios->info, values);
}

/**
@@ -181,6 +182,7 @@ static void phy_mdm6600_status(struct work_struct *work)

error = gpiod_get_array_value_cansleep(PHY_MDM6600_NR_STATUS_LINES,
ddata->status_gpios->desc,
+ ddata->status_gpios->info,
values);
if (error)
return;
diff --git a/drivers/staging/iio/adc/ad7606.c b/drivers/staging/iio/adc/ad7606.c
index 053c9b7f1084..d32dba23e782 100644
--- a/drivers/staging/iio/adc/ad7606.c
+++ b/drivers/staging/iio/adc/ad7606.c
@@ -230,7 +230,8 @@ static int ad7606_write_raw(struct iio_dev *indio_dev,
*values = ret;

mutex_lock(&st->lock);
- gpiod_set_array_value(3, st->gpio_os->desc, values);
+ gpiod_set_array_value(3, st->gpio_os->desc, st->gpio_os->info,
+ values);
st->oversampling = val;
mutex_unlock(&st->lock);

diff --git a/drivers/tty/serial/serial_mctrl_gpio.c b/drivers/tty/serial/serial_mctrl_gpio.c
index 30444fd6cf32..d7934ca55e2f 100644
--- a/drivers/tty/serial/serial_mctrl_gpio.c
+++ b/drivers/tty/serial/serial_mctrl_gpio.c
@@ -53,7 +53,7 @@ void mctrl_gpio_set(struct mctrl_gpios *gpios, unsigned int mctrl)
!!(mctrl & mctrl_gpios_desc[i].mctrl));
count++;
}
- gpiod_set_array_value(count, desc_array, values);
+ gpiod_set_array_value(count, desc_array, NULL, values);
}
EXPORT_SYMBOL_GPL(mctrl_gpio_set);

diff --git a/include/linux/gpio/consumer.h b/include/linux/gpio/consumer.h
index 8dede3e886af..bf037ebe2ed8 100644
--- a/include/linux/gpio/consumer.h
+++ b/include/linux/gpio/consumer.h
@@ -114,36 +114,44 @@ int gpiod_direction_output_raw(struct gpio_desc *desc, int value);
int gpiod_get_value(const struct gpio_desc *desc);
int gpiod_get_array_value(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap);
void gpiod_set_value(struct gpio_desc *desc, int value);
void gpiod_set_array_value(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap);
int gpiod_get_raw_value(const struct gpio_desc *desc);
int gpiod_get_raw_array_value(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap);
void gpiod_set_raw_value(struct gpio_desc *desc, int value);
int gpiod_set_raw_array_value(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap);

/* Value get/set from sleeping context */
int gpiod_get_value_cansleep(const struct gpio_desc *desc);
int gpiod_get_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap);
void gpiod_set_value_cansleep(struct gpio_desc *desc, int value);
void gpiod_set_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap);
int gpiod_get_raw_value_cansleep(const struct gpio_desc *desc);
int gpiod_get_raw_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap);
void gpiod_set_raw_value_cansleep(struct gpio_desc *desc, int value);
int gpiod_set_raw_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap);

int gpiod_set_debounce(struct gpio_desc *desc, unsigned debounce);
--
2.16.4


2018-09-02 12:03:16

by Janusz Krzysztofik

[permalink] [raw]
Subject: [PATCH v7 1/4] gpiolib: Pass bitmaps, not integer arrays, to get/set array

Most users of get/set array functions iterate consecutive bits of data,
usually a single integer, while processing array of results obtained
from, or building an array of values to be passed to those functions.
Save time wasted on those iterations by changing the functions' API to
accept bitmaps.

All current users are updated as well.

More benefits from the change are expected as soon as planned support
for accepting/passing those bitmaps directly from/to respective GPIO
chip callbacks if applicable is implemented.

Cc: Jonathan Corbet <[email protected]>
Cc: Miguel Ojeda Sandonis <[email protected]>
Cc: Geert Uytterhoeven <[email protected]>
Cc: Sebastien Bourdelin <[email protected]>
Cc: Lukas Wunner <[email protected]>
Cc: Peter Korsgaard <[email protected]>
Cc: Peter Rosin <[email protected]>
Cc: Andrew Lunn <[email protected]>
Cc: Florian Fainelli <[email protected]>
Cc: "David S. Miller" <[email protected]>
Cc: Rojhalat Ibrahim <[email protected]>
Cc: Dominik Brodowski <[email protected]>
Cc: Russell King <[email protected]>
Cc: Kishon Vijay Abraham I <[email protected]>
Cc: Tony Lindgren <[email protected]>
Cc: Lars-Peter Clausen <[email protected]>
Cc: Michael Hennerich <[email protected]>
Cc: Jonathan Cameron <[email protected]>
Cc: Hartmut Knaack <[email protected]>
Cc: Peter Meerwald-Stadler <[email protected]>
Cc: Greg Kroah-Hartman <[email protected]>
Cc: Jiri Slaby <[email protected]>
Cc: Yegor Yefremov <[email protected]>
Cc: Uwe Kleine-König <[email protected]>
Signed-off-by: Janusz Krzysztofik <[email protected]>
Acked-by: Ulf Hansson <[email protected]>
---
Documentation/driver-api/gpio/consumer.rst | 22 ++++----
drivers/auxdisplay/hd44780.c | 59 +++++++--------------
drivers/bus/ts-nbus.c | 15 ++----
drivers/gpio/gpio-max3191x.c | 10 ++--
drivers/gpio/gpiolib.c | 82 +++++++++++++++--------------
drivers/gpio/gpiolib.h | 4 +-
drivers/i2c/muxes/i2c-mux-gpio.c | 13 ++---
drivers/mmc/core/pwrseq_simple.c | 13 ++---
drivers/mux/gpio.c | 13 ++---
drivers/net/phy/mdio-mux-gpio.c | 11 ++--
drivers/pcmcia/soc_common.c | 8 +--
drivers/phy/motorola/phy-mapphone-mdm6600.c | 13 ++---
drivers/staging/iio/adc/ad7606.c | 9 ++--
drivers/tty/serial/serial_mctrl_gpio.c | 7 +--
include/linux/gpio/consumer.h | 18 ++++---
15 files changed, 129 insertions(+), 168 deletions(-)

diff --git a/Documentation/driver-api/gpio/consumer.rst b/Documentation/driver-api/gpio/consumer.rst
index aa03f389d41d..ed68042ddccf 100644
--- a/Documentation/driver-api/gpio/consumer.rst
+++ b/Documentation/driver-api/gpio/consumer.rst
@@ -323,29 +323,29 @@ The following functions get or set the values of an array of GPIOs::

int gpiod_get_array_value(unsigned int array_size,
struct gpio_desc **desc_array,
- int *value_array);
+ unsigned long *value_bitmap);
int gpiod_get_raw_array_value(unsigned int array_size,
struct gpio_desc **desc_array,
- int *value_array);
+ unsigned long *value_bitmap);
int gpiod_get_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
- int *value_array);
+ unsigned long *value_bitmap);
int gpiod_get_raw_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
- int *value_array);
+ unsigned long *value_bitmap);

void gpiod_set_array_value(unsigned int array_size,
struct gpio_desc **desc_array,
- int *value_array)
+ unsigned long *value_bitmap)
void gpiod_set_raw_array_value(unsigned int array_size,
struct gpio_desc **desc_array,
- int *value_array)
+ unsigned long *value_bitmap)
void gpiod_set_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
- int *value_array)
+ unsigned long *value_bitmap)
void gpiod_set_raw_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
- int *value_array)
+ unsigned long *value_bitmap)

The array can be an arbitrary set of GPIOs. The functions will try to access
GPIOs belonging to the same bank or chip simultaneously if supported by the
@@ -356,8 +356,8 @@ accessed sequentially.
The functions take three arguments:
* array_size - the number of array elements
* desc_array - an array of GPIO descriptors
- * value_array - an array to store the GPIOs' values (get) or
- an array of values to assign to the GPIOs (set)
+ * value_bitmap - a bitmap to store the GPIOs' values (get) or
+ a bitmap of values to assign to the GPIOs (set)

The descriptor array can be obtained using the gpiod_get_array() function
or one of its variants. If the group of descriptors returned by that function
@@ -366,7 +366,7 @@ the struct gpio_descs returned by gpiod_get_array()::

struct gpio_descs *my_gpio_descs = gpiod_get_array(...);
gpiod_set_array_value(my_gpio_descs->ndescs, my_gpio_descs->desc,
- my_gpio_values);
+ my_gpio_value_bitmap);

It is also possible to access a completely arbitrary array of descriptors. The
descriptors may be obtained using any combination of gpiod_get() and
diff --git a/drivers/auxdisplay/hd44780.c b/drivers/auxdisplay/hd44780.c
index f1a42f0f1ded..333e30e378b5 100644
--- a/drivers/auxdisplay/hd44780.c
+++ b/drivers/auxdisplay/hd44780.c
@@ -62,17 +62,12 @@ static void hd44780_strobe_gpio(struct hd44780 *hd)
/* write to an LCD panel register in 8 bit GPIO mode */
static void hd44780_write_gpio8(struct hd44780 *hd, u8 val, unsigned int rs)
{
- int values[10]; /* for DATA[0-7], RS, RW */
- unsigned int i, n;
-
- for (i = 0; i < 8; i++)
- values[PIN_DATA0 + i] = !!(val & BIT(i));
- values[PIN_CTRL_RS] = rs;
- n = 9;
- if (hd->pins[PIN_CTRL_RW]) {
- values[PIN_CTRL_RW] = 0;
- n++;
- }
+ DECLARE_BITMAP(values, 10); /* for DATA[0-7], RS, RW */
+ unsigned int n;
+
+ *values = val;
+ __assign_bit(8, values, rs);
+ n = hd->pins[PIN_CTRL_RW] ? 10 : 9;

/* Present the data to the port */
gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA0], values);
@@ -83,32 +78,25 @@ static void hd44780_write_gpio8(struct hd44780 *hd, u8 val, unsigned int rs)
/* write to an LCD panel register in 4 bit GPIO mode */
static void hd44780_write_gpio4(struct hd44780 *hd, u8 val, unsigned int rs)
{
- int values[10]; /* for DATA[0-7], RS, RW, but DATA[0-3] is unused */
- unsigned int i, n;
+ DECLARE_BITMAP(values, 6); /* for DATA[4-7], RS, RW */
+ unsigned int n;

/* High nibble + RS, RW */
- for (i = 4; i < 8; i++)
- values[PIN_DATA0 + i] = !!(val & BIT(i));
- values[PIN_CTRL_RS] = rs;
- n = 5;
- if (hd->pins[PIN_CTRL_RW]) {
- values[PIN_CTRL_RW] = 0;
- n++;
- }
+ *values = val >> 4;
+ __assign_bit(4, values, rs);
+ n = hd->pins[PIN_CTRL_RW] ? 6 : 5;

/* Present the data to the port */
- gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA4],
- &values[PIN_DATA4]);
+ gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA4], values);

hd44780_strobe_gpio(hd);

/* Low nibble */
- for (i = 0; i < 4; i++)
- values[PIN_DATA4 + i] = !!(val & BIT(i));
+ *values &= ~0x0fUL;
+ *values |= val & 0x0f;

/* Present the data to the port */
- gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA4],
- &values[PIN_DATA4]);
+ gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA4], values);

hd44780_strobe_gpio(hd);
}
@@ -155,23 +143,16 @@ static void hd44780_write_cmd_gpio4(struct charlcd *lcd, int cmd)
/* Send 4-bits of a command to the LCD panel in raw 4 bit GPIO mode */
static void hd44780_write_cmd_raw_gpio4(struct charlcd *lcd, int cmd)
{
- int values[10]; /* for DATA[0-7], RS, RW, but DATA[0-3] is unused */
+ DECLARE_BITMAP(values, 6); /* for DATA[4-7], RS, RW */
struct hd44780 *hd = lcd->drvdata;
- unsigned int i, n;
+ unsigned int n;

/* Command nibble + RS, RW */
- for (i = 0; i < 4; i++)
- values[PIN_DATA4 + i] = !!(cmd & BIT(i));
- values[PIN_CTRL_RS] = 0;
- n = 5;
- if (hd->pins[PIN_CTRL_RW]) {
- values[PIN_CTRL_RW] = 0;
- n++;
- }
+ *values = cmd & 0x0f;
+ n = hd->pins[PIN_CTRL_RW] ? 6 : 5;

/* Present the data to the port */
- gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA4],
- &values[PIN_DATA4]);
+ gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA4], values);

hd44780_strobe_gpio(hd);
}
diff --git a/drivers/bus/ts-nbus.c b/drivers/bus/ts-nbus.c
index 073fd9011154..6499957a8044 100644
--- a/drivers/bus/ts-nbus.c
+++ b/drivers/bus/ts-nbus.c
@@ -110,11 +110,9 @@ static void ts_nbus_set_direction(struct ts_nbus *ts_nbus, int direction)
*/
static void ts_nbus_reset_bus(struct ts_nbus *ts_nbus)
{
- int i;
- int values[8];
+ DECLARE_BITMAP(values, 8);

- for (i = 0; i < 8; i++)
- values[i] = 0;
+ *values = 0;

gpiod_set_array_value_cansleep(8, ts_nbus->data->desc, values);
gpiod_set_value_cansleep(ts_nbus->csn, 0);
@@ -157,14 +155,9 @@ static int ts_nbus_read_byte(struct ts_nbus *ts_nbus, u8 *val)
static void ts_nbus_write_byte(struct ts_nbus *ts_nbus, u8 byte)
{
struct gpio_descs *gpios = ts_nbus->data;
- int i;
- int values[8];
+ DECLARE_BITMAP(values, 8);

- for (i = 0; i < 8; i++)
- if (byte & BIT(i))
- values[i] = 1;
- else
- values[i] = 0;
+ *values = byte;

gpiod_set_array_value_cansleep(8, gpios->desc, values);
}
diff --git a/drivers/gpio/gpio-max3191x.c b/drivers/gpio/gpio-max3191x.c
index b5b9cb1fda50..bd4a245fc5a0 100644
--- a/drivers/gpio/gpio-max3191x.c
+++ b/drivers/gpio/gpio-max3191x.c
@@ -315,14 +315,16 @@ static void gpiod_set_array_single_value_cansleep(unsigned int ndescs,
struct gpio_desc **desc,
int value)
{
- int i, *values;
+ unsigned long *values;

- values = kmalloc_array(ndescs, sizeof(*values), GFP_KERNEL);
+ values = bitmap_alloc(ndescs, GFP_KERNEL);
if (!values)
return;

- for (i = 0; i < ndescs; i++)
- values[i] = value;
+ if (value)
+ bitmap_fill(values, ndescs);
+ else
+ bitmap_zero(values, ndescs);

gpiod_set_array_value_cansleep(ndescs, desc, values);
kfree(values);
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index e8f8a1999393..434d09779a1f 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -427,7 +427,7 @@ static long linehandle_ioctl(struct file *filep, unsigned int cmd,
struct linehandle_state *lh = filep->private_data;
void __user *ip = (void __user *)arg;
struct gpiohandle_data ghd;
- int vals[GPIOHANDLES_MAX];
+ DECLARE_BITMAP(vals, GPIOHANDLES_MAX);
int i;

if (cmd == GPIOHANDLE_GET_LINE_VALUES_IOCTL) {
@@ -442,7 +442,7 @@ static long linehandle_ioctl(struct file *filep, unsigned int cmd,

memset(&ghd, 0, sizeof(ghd));
for (i = 0; i < lh->numdescs; i++)
- ghd.values[i] = vals[i];
+ ghd.values[i] = test_bit(i, vals);

if (copy_to_user(ip, &ghd, sizeof(ghd)))
return -EFAULT;
@@ -461,7 +461,7 @@ static long linehandle_ioctl(struct file *filep, unsigned int cmd,

/* Clamp all values to [0,1] */
for (i = 0; i < lh->numdescs; i++)
- vals[i] = !!ghd.values[i];
+ __assign_bit(i, vals, !!ghd.values[i]);

/* Reuse the array setting function */
return gpiod_set_array_value_complex(false,
@@ -2784,7 +2784,7 @@ static int gpio_chip_get_multiple(struct gpio_chip *chip,
int gpiod_get_array_value_complex(bool raw, bool can_sleep,
unsigned int array_size,
struct gpio_desc **desc_array,
- int *value_array)
+ unsigned long *value_bitmap)
{
int i = 0;

@@ -2835,7 +2835,7 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep,

if (!raw && test_bit(FLAG_ACTIVE_LOW, &desc->flags))
value = !value;
- value_array[j] = value;
+ __assign_bit(j, value_bitmap, value);
trace_gpio_value(desc_to_gpio(desc), 1, value);
}

@@ -2895,9 +2895,9 @@ EXPORT_SYMBOL_GPL(gpiod_get_value);

/**
* gpiod_get_raw_array_value() - read raw values from an array of GPIOs
- * @array_size: number of elements in the descriptor / value arrays
+ * @array_size: number of elements in the descriptor array / value bitmap
* @desc_array: array of GPIO descriptors whose values will be read
- * @value_array: array to store the read values
+ * @value_bitmap: bitmap to store the read values
*
* Read the raw values of the GPIOs, i.e. the values of the physical lines
* without regard for their ACTIVE_LOW status. Return 0 in case of success,
@@ -2907,20 +2907,21 @@ EXPORT_SYMBOL_GPL(gpiod_get_value);
* and it will complain if the GPIO chip functions potentially sleep.
*/
int gpiod_get_raw_array_value(unsigned int array_size,
- struct gpio_desc **desc_array, int *value_array)
+ struct gpio_desc **desc_array,
+ unsigned long *value_bitmap)
{
if (!desc_array)
return -EINVAL;
return gpiod_get_array_value_complex(true, false, array_size,
- desc_array, value_array);
+ desc_array, value_bitmap);
}
EXPORT_SYMBOL_GPL(gpiod_get_raw_array_value);

/**
* gpiod_get_array_value() - read values from an array of GPIOs
- * @array_size: number of elements in the descriptor / value arrays
+ * @array_size: number of elements in the descriptor array / value bitmap
* @desc_array: array of GPIO descriptors whose values will be read
- * @value_array: array to store the read values
+ * @value_bitnap: bitmap to store the read values
*
* Read the logical values of the GPIOs, i.e. taking their ACTIVE_LOW status
* into account. Return 0 in case of success, else an error code.
@@ -2929,12 +2930,13 @@ EXPORT_SYMBOL_GPL(gpiod_get_raw_array_value);
* and it will complain if the GPIO chip functions potentially sleep.
*/
int gpiod_get_array_value(unsigned int array_size,
- struct gpio_desc **desc_array, int *value_array)
+ struct gpio_desc **desc_array,
+ unsigned long *value_bitmap)
{
if (!desc_array)
return -EINVAL;
return gpiod_get_array_value_complex(false, false, array_size,
- desc_array, value_array);
+ desc_array, value_bitmap);
}
EXPORT_SYMBOL_GPL(gpiod_get_array_value);

@@ -3027,7 +3029,7 @@ static void gpio_chip_set_multiple(struct gpio_chip *chip,
int gpiod_set_array_value_complex(bool raw, bool can_sleep,
unsigned int array_size,
struct gpio_desc **desc_array,
- int *value_array)
+ unsigned long *value_bitmap)
{
int i = 0;

@@ -3056,7 +3058,7 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep,
do {
struct gpio_desc *desc = desc_array[i];
int hwgpio = gpio_chip_hwgpio(desc);
- int value = value_array[i];
+ int value = test_bit(i, value_bitmap);

if (!raw && test_bit(FLAG_ACTIVE_LOW, &desc->flags))
value = !value;
@@ -3152,9 +3154,9 @@ EXPORT_SYMBOL_GPL(gpiod_set_value);

/**
* gpiod_set_raw_array_value() - assign values to an array of GPIOs
- * @array_size: number of elements in the descriptor / value arrays
+ * @array_size: number of elements in the descriptor array / value bitmap
* @desc_array: array of GPIO descriptors whose values will be assigned
- * @value_array: array of values to assign
+ * @value_bitmap: bitmap of values to assign
*
* Set the raw values of the GPIOs, i.e. the values of the physical lines
* without regard for their ACTIVE_LOW status.
@@ -3163,20 +3165,21 @@ EXPORT_SYMBOL_GPL(gpiod_set_value);
* complain if the GPIO chip functions potentially sleep.
*/
int gpiod_set_raw_array_value(unsigned int array_size,
- struct gpio_desc **desc_array, int *value_array)
+ struct gpio_desc **desc_array,
+ unsigned long *value_bitmap)
{
if (!desc_array)
return -EINVAL;
return gpiod_set_array_value_complex(true, false, array_size,
- desc_array, value_array);
+ desc_array, value_bitmap);
}
EXPORT_SYMBOL_GPL(gpiod_set_raw_array_value);

/**
* gpiod_set_array_value() - assign values to an array of GPIOs
- * @array_size: number of elements in the descriptor / value arrays
+ * @array_size: number of elements in the descriptor array / value bitmap
* @desc_array: array of GPIO descriptors whose values will be assigned
- * @value_array: array of values to assign
+ * @value_bitmap: bitmap of values to assign
*
* Set the logical values of the GPIOs, i.e. taking their ACTIVE_LOW status
* into account.
@@ -3185,12 +3188,13 @@ EXPORT_SYMBOL_GPL(gpiod_set_raw_array_value);
* complain if the GPIO chip functions potentially sleep.
*/
void gpiod_set_array_value(unsigned int array_size,
- struct gpio_desc **desc_array, int *value_array)
+ struct gpio_desc **desc_array,
+ unsigned long *value_bitmap)
{
if (!desc_array)
return;
gpiod_set_array_value_complex(false, false, array_size, desc_array,
- value_array);
+ value_bitmap);
}
EXPORT_SYMBOL_GPL(gpiod_set_array_value);

@@ -3410,9 +3414,9 @@ EXPORT_SYMBOL_GPL(gpiod_get_value_cansleep);

/**
* gpiod_get_raw_array_value_cansleep() - read raw values from an array of GPIOs
- * @array_size: number of elements in the descriptor / value arrays
+ * @array_size: number of elements in the descriptor array / value bitmap
* @desc_array: array of GPIO descriptors whose values will be read
- * @value_array: array to store the read values
+ * @value_bitmap: bitmap to store the read values
*
* Read the raw values of the GPIOs, i.e. the values of the physical lines
* without regard for their ACTIVE_LOW status. Return 0 in case of success,
@@ -3422,21 +3426,21 @@ EXPORT_SYMBOL_GPL(gpiod_get_value_cansleep);
*/
int gpiod_get_raw_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
- int *value_array)
+ unsigned long *value_bitmap)
{
might_sleep_if(extra_checks);
if (!desc_array)
return -EINVAL;
return gpiod_get_array_value_complex(true, true, array_size,
- desc_array, value_array);
+ desc_array, value_bitmap);
}
EXPORT_SYMBOL_GPL(gpiod_get_raw_array_value_cansleep);

/**
* gpiod_get_array_value_cansleep() - read values from an array of GPIOs
- * @array_size: number of elements in the descriptor / value arrays
+ * @array_size: number of elements in the descriptor array / value bitmap
* @desc_array: array of GPIO descriptors whose values will be read
- * @value_array: array to store the read values
+ * @value_bitmap: bitmap to store the read values
*
* Read the logical values of the GPIOs, i.e. taking their ACTIVE_LOW status
* into account. Return 0 in case of success, else an error code.
@@ -3445,13 +3449,13 @@ EXPORT_SYMBOL_GPL(gpiod_get_raw_array_value_cansleep);
*/
int gpiod_get_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
- int *value_array)
+ unsigned long *value_bitmap)
{
might_sleep_if(extra_checks);
if (!desc_array)
return -EINVAL;
return gpiod_get_array_value_complex(false, true, array_size,
- desc_array, value_array);
+ desc_array, value_bitmap);
}
EXPORT_SYMBOL_GPL(gpiod_get_array_value_cansleep);

@@ -3493,9 +3497,9 @@ EXPORT_SYMBOL_GPL(gpiod_set_value_cansleep);

/**
* gpiod_set_raw_array_value_cansleep() - assign values to an array of GPIOs
- * @array_size: number of elements in the descriptor / value arrays
+ * @array_size: number of elements in the descriptor array / value bitmap
* @desc_array: array of GPIO descriptors whose values will be assigned
- * @value_array: array of values to assign
+ * @value_bitmap: bitmap of values to assign
*
* Set the raw values of the GPIOs, i.e. the values of the physical lines
* without regard for their ACTIVE_LOW status.
@@ -3504,13 +3508,13 @@ EXPORT_SYMBOL_GPL(gpiod_set_value_cansleep);
*/
int gpiod_set_raw_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
- int *value_array)
+ unsigned long *value_bitmap)
{
might_sleep_if(extra_checks);
if (!desc_array)
return -EINVAL;
return gpiod_set_array_value_complex(true, true, array_size, desc_array,
- value_array);
+ value_bitmap);
}
EXPORT_SYMBOL_GPL(gpiod_set_raw_array_value_cansleep);

@@ -3533,9 +3537,9 @@ void gpiod_add_lookup_tables(struct gpiod_lookup_table **tables, size_t n)

/**
* gpiod_set_array_value_cansleep() - assign values to an array of GPIOs
- * @array_size: number of elements in the descriptor / value arrays
+ * @array_size: number of elements in the descriptor array / value bitmap
* @desc_array: array of GPIO descriptors whose values will be assigned
- * @value_array: array of values to assign
+ * @value_bitmap: bitmap of values to assign
*
* Set the logical values of the GPIOs, i.e. taking their ACTIVE_LOW status
* into account.
@@ -3544,13 +3548,13 @@ void gpiod_add_lookup_tables(struct gpiod_lookup_table **tables, size_t n)
*/
void gpiod_set_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
- int *value_array)
+ unsigned long *value_bitmap)
{
might_sleep_if(extra_checks);
if (!desc_array)
return;
gpiod_set_array_value_complex(false, true, array_size, desc_array,
- value_array);
+ value_bitmap);
}
EXPORT_SYMBOL_GPL(gpiod_set_array_value_cansleep);

diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h
index a7e49fef73d4..11e83d2eef89 100644
--- a/drivers/gpio/gpiolib.h
+++ b/drivers/gpio/gpiolib.h
@@ -187,11 +187,11 @@ struct gpio_desc *gpiochip_get_desc(struct gpio_chip *chip, u16 hwnum);
int gpiod_get_array_value_complex(bool raw, bool can_sleep,
unsigned int array_size,
struct gpio_desc **desc_array,
- int *value_array);
+ unsigned long *value_bitmap);
int gpiod_set_array_value_complex(bool raw, bool can_sleep,
unsigned int array_size,
struct gpio_desc **desc_array,
- int *value_array);
+ unsigned long *value_bitmap);

/* This is just passed between gpiolib and devres */
struct gpio_desc *gpiod_get_from_of_node(struct device_node *node,
diff --git a/drivers/i2c/muxes/i2c-mux-gpio.c b/drivers/i2c/muxes/i2c-mux-gpio.c
index 401308e3d036..0717cfb56732 100644
--- a/drivers/i2c/muxes/i2c-mux-gpio.c
+++ b/drivers/i2c/muxes/i2c-mux-gpio.c
@@ -22,18 +22,15 @@ struct gpiomux {
struct i2c_mux_gpio_platform_data data;
unsigned gpio_base;
struct gpio_desc **gpios;
- int *values;
};

static void i2c_mux_gpio_set(const struct gpiomux *mux, unsigned val)
{
- int i;
+ DECLARE_BITMAP(values, BITS_PER_TYPE(val));

- for (i = 0; i < mux->data.n_gpios; i++)
- mux->values[i] = (val >> i) & 1;
+ *values = val;

- gpiod_set_array_value_cansleep(mux->data.n_gpios,
- mux->gpios, mux->values);
+ gpiod_set_array_value_cansleep(mux->data.n_gpios, mux->gpios, values);
}

static int i2c_mux_gpio_select(struct i2c_mux_core *muxc, u32 chan)
@@ -182,15 +179,13 @@ static int i2c_mux_gpio_probe(struct platform_device *pdev)
return -EPROBE_DEFER;

muxc = i2c_mux_alloc(parent, &pdev->dev, mux->data.n_values,
- mux->data.n_gpios * sizeof(*mux->gpios) +
- mux->data.n_gpios * sizeof(*mux->values), 0,
+ mux->data.n_gpios * sizeof(*mux->gpios), 0,
i2c_mux_gpio_select, NULL);
if (!muxc) {
ret = -ENOMEM;
goto alloc_failed;
}
mux->gpios = muxc->priv;
- mux->values = (int *)(mux->gpios + mux->data.n_gpios);
muxc->priv = mux;

platform_set_drvdata(pdev, muxc);
diff --git a/drivers/mmc/core/pwrseq_simple.c b/drivers/mmc/core/pwrseq_simple.c
index a8b9fee4d62a..5ad764c88b50 100644
--- a/drivers/mmc/core/pwrseq_simple.c
+++ b/drivers/mmc/core/pwrseq_simple.c
@@ -40,18 +40,13 @@ static void mmc_pwrseq_simple_set_gpios_value(struct mmc_pwrseq_simple *pwrseq,
struct gpio_descs *reset_gpios = pwrseq->reset_gpios;

if (!IS_ERR(reset_gpios)) {
- int i, *values;
+ DECLARE_BITMAP(values, BITS_PER_TYPE(value));
int nvalues = reset_gpios->ndescs;

- values = kmalloc_array(nvalues, sizeof(int), GFP_KERNEL);
- if (!values)
- return;
+ *values = value;

- for (i = 0; i < nvalues; i++)
- values[i] = value;
-
- gpiod_set_array_value_cansleep(nvalues, reset_gpios->desc, values);
- kfree(values);
+ gpiod_set_array_value_cansleep(nvalues, reset_gpios->desc,
+ values);
}
}

diff --git a/drivers/mux/gpio.c b/drivers/mux/gpio.c
index 6fdd9316db8b..1b6b4cc22a2c 100644
--- a/drivers/mux/gpio.c
+++ b/drivers/mux/gpio.c
@@ -17,20 +17,17 @@

struct mux_gpio {
struct gpio_descs *gpios;
- int *val;
};

static int mux_gpio_set(struct mux_control *mux, int state)
{
struct mux_gpio *mux_gpio = mux_chip_priv(mux->chip);
- int i;
+ DECLARE_BITMAP(values, BITS_PER_TYPE(state));

- for (i = 0; i < mux_gpio->gpios->ndescs; i++)
- mux_gpio->val[i] = (state >> i) & 1;
+ *values = state;

gpiod_set_array_value_cansleep(mux_gpio->gpios->ndescs,
- mux_gpio->gpios->desc,
- mux_gpio->val);
+ mux_gpio->gpios->desc, values);

return 0;
}
@@ -58,13 +55,11 @@ static int mux_gpio_probe(struct platform_device *pdev)
if (pins < 0)
return pins;

- mux_chip = devm_mux_chip_alloc(dev, 1, sizeof(*mux_gpio) +
- pins * sizeof(*mux_gpio->val));
+ mux_chip = devm_mux_chip_alloc(dev, 1, sizeof(*mux_gpio));
if (IS_ERR(mux_chip))
return PTR_ERR(mux_chip);

mux_gpio = mux_chip_priv(mux_chip);
- mux_gpio->val = (int *)(mux_gpio + 1);
mux_chip->ops = &mux_gpio_ops;

mux_gpio->gpios = devm_gpiod_get_array(dev, "mux", GPIOD_OUT_LOW);
diff --git a/drivers/net/phy/mdio-mux-gpio.c b/drivers/net/phy/mdio-mux-gpio.c
index bc90764a8b8d..eb8c56b83c70 100644
--- a/drivers/net/phy/mdio-mux-gpio.c
+++ b/drivers/net/phy/mdio-mux-gpio.c
@@ -20,23 +20,21 @@
struct mdio_mux_gpio_state {
struct gpio_descs *gpios;
void *mux_handle;
- int values[];
};

static int mdio_mux_gpio_switch_fn(int current_child, int desired_child,
void *data)
{
struct mdio_mux_gpio_state *s = data;
- unsigned int n;
+ DECLARE_BITMAP(values, BITS_PER_TYPE(desired_child));

if (current_child == desired_child)
return 0;

- for (n = 0; n < s->gpios->ndescs; n++)
- s->values[n] = (desired_child >> n) & 1;
+ *values = desired_child;

gpiod_set_array_value_cansleep(s->gpios->ndescs, s->gpios->desc,
- s->values);
+ values);

return 0;
}
@@ -51,8 +49,7 @@ static int mdio_mux_gpio_probe(struct platform_device *pdev)
if (IS_ERR(gpios))
return PTR_ERR(gpios);

- s = devm_kzalloc(&pdev->dev, struct_size(s, values, gpios->ndescs),
- GFP_KERNEL);
+ s = devm_kzalloc(&pdev->dev, sizeof(*s), GFP_KERNEL);
if (!s) {
gpiod_put_array(gpios);
return -ENOMEM;
diff --git a/drivers/pcmcia/soc_common.c b/drivers/pcmcia/soc_common.c
index c5f2344c189b..388dbdc46129 100644
--- a/drivers/pcmcia/soc_common.c
+++ b/drivers/pcmcia/soc_common.c
@@ -351,15 +351,17 @@ static int soc_common_pcmcia_config_skt(

if (ret == 0) {
struct gpio_desc *descs[2];
- int values[2], n = 0;
+ DECLARE_BITMAP(values, 2);
+ int n = 0;

if (skt->gpio_reset) {
descs[n] = skt->gpio_reset;
- values[n++] = !!(state->flags & SS_RESET);
+ __assign_bit(n++, values, !!(state->flags & SS_RESET));
}
if (skt->gpio_bus_enable) {
descs[n] = skt->gpio_bus_enable;
- values[n++] = !!(state->flags & SS_OUTPUT_ENA);
+ __assign_bit(n++, values,
+ !!(state->flags & SS_OUTPUT_ENA));
}

if (n)
diff --git a/drivers/phy/motorola/phy-mapphone-mdm6600.c b/drivers/phy/motorola/phy-mapphone-mdm6600.c
index 0075fb0bef8c..4de7f4577433 100644
--- a/drivers/phy/motorola/phy-mapphone-mdm6600.c
+++ b/drivers/phy/motorola/phy-mapphone-mdm6600.c
@@ -157,12 +157,9 @@ static const struct phy_ops gpio_usb_ops = {
*/
static void phy_mdm6600_cmd(struct phy_mdm6600 *ddata, int val)
{
- int values[PHY_MDM6600_NR_CMD_LINES];
- int i;
+ DECLARE_BITMAP(values, PHY_MDM6600_NR_CMD_LINES);

- val &= (1 << PHY_MDM6600_NR_CMD_LINES) - 1;
- for (i = 0; i < PHY_MDM6600_NR_CMD_LINES; i++)
- values[i] = (val & BIT(i)) >> i;
+ *values = val;

gpiod_set_array_value_cansleep(PHY_MDM6600_NR_CMD_LINES,
ddata->cmd_gpios->desc, values);
@@ -176,7 +173,7 @@ static void phy_mdm6600_status(struct work_struct *work)
{
struct phy_mdm6600 *ddata;
struct device *dev;
- int values[PHY_MDM6600_NR_STATUS_LINES];
+ DECLARE_BITMAP(values, PHY_MDM6600_NR_STATUS_LINES);
int error, i, val = 0;

ddata = container_of(work, struct phy_mdm6600, status_work.work);
@@ -189,9 +186,9 @@ static void phy_mdm6600_status(struct work_struct *work)
return;

for (i = 0; i < PHY_MDM6600_NR_STATUS_LINES; i++) {
- val |= values[i] << i;
+ val |= test_bit(i, values) << i;
dev_dbg(ddata->dev, "XXX %s: i: %i values[i]: %i val: %i\n",
- __func__, i, values[i], val);
+ __func__, i, test_bit(i, values), val);
}
ddata->status = val;

diff --git a/drivers/staging/iio/adc/ad7606.c b/drivers/staging/iio/adc/ad7606.c
index 25b9fcd5e3a4..053c9b7f1084 100644
--- a/drivers/staging/iio/adc/ad7606.c
+++ b/drivers/staging/iio/adc/ad7606.c
@@ -202,7 +202,7 @@ static int ad7606_write_raw(struct iio_dev *indio_dev,
long mask)
{
struct ad7606_state *st = iio_priv(indio_dev);
- int values[3];
+ DECLARE_BITMAP(values, 3);
int ret, i;

switch (mask) {
@@ -227,13 +227,10 @@ static int ad7606_write_raw(struct iio_dev *indio_dev,
if (ret < 0)
return ret;

- values[0] = (ret >> 0) & 1;
- values[1] = (ret >> 1) & 1;
- values[2] = (ret >> 2) & 1;
+ *values = ret;

mutex_lock(&st->lock);
- gpiod_set_array_value(ARRAY_SIZE(values), st->gpio_os->desc,
- values);
+ gpiod_set_array_value(3, st->gpio_os->desc, values);
st->oversampling = val;
mutex_unlock(&st->lock);

diff --git a/drivers/tty/serial/serial_mctrl_gpio.c b/drivers/tty/serial/serial_mctrl_gpio.c
index 1c06325beaca..30444fd6cf32 100644
--- a/drivers/tty/serial/serial_mctrl_gpio.c
+++ b/drivers/tty/serial/serial_mctrl_gpio.c
@@ -40,7 +40,7 @@ void mctrl_gpio_set(struct mctrl_gpios *gpios, unsigned int mctrl)
{
enum mctrl_gpio_idx i;
struct gpio_desc *desc_array[UART_GPIO_MAX];
- int value_array[UART_GPIO_MAX];
+ DECLARE_BITMAP(values, UART_GPIO_MAX);
unsigned int count = 0;

if (gpios == NULL)
@@ -49,10 +49,11 @@ void mctrl_gpio_set(struct mctrl_gpios *gpios, unsigned int mctrl)
for (i = 0; i < UART_GPIO_MAX; i++)
if (gpios->gpio[i] && mctrl_gpios_desc[i].dir_out) {
desc_array[count] = gpios->gpio[i];
- value_array[count] = !!(mctrl & mctrl_gpios_desc[i].mctrl);
+ __assign_bit(count, values,
+ !!(mctrl & mctrl_gpios_desc[i].mctrl));
count++;
}
- gpiod_set_array_value(count, desc_array, value_array);
+ gpiod_set_array_value(count, desc_array, values);
}
EXPORT_SYMBOL_GPL(mctrl_gpio_set);

diff --git a/include/linux/gpio/consumer.h b/include/linux/gpio/consumer.h
index 21ddbe440030..1b21dc7b0fad 100644
--- a/include/linux/gpio/consumer.h
+++ b/include/linux/gpio/consumer.h
@@ -104,36 +104,38 @@ int gpiod_direction_output_raw(struct gpio_desc *desc, int value);
/* Value get/set from non-sleeping context */
int gpiod_get_value(const struct gpio_desc *desc);
int gpiod_get_array_value(unsigned int array_size,
- struct gpio_desc **desc_array, int *value_array);
+ struct gpio_desc **desc_array,
+ unsigned long *value_bitmap);
void gpiod_set_value(struct gpio_desc *desc, int value);
void gpiod_set_array_value(unsigned int array_size,
- struct gpio_desc **desc_array, int *value_array);
+ struct gpio_desc **desc_array,
+ unsigned long *value_bitmap);
int gpiod_get_raw_value(const struct gpio_desc *desc);
int gpiod_get_raw_array_value(unsigned int array_size,
struct gpio_desc **desc_array,
- int *value_array);
+ unsigned long *value_bitmap);
void gpiod_set_raw_value(struct gpio_desc *desc, int value);
int gpiod_set_raw_array_value(unsigned int array_size,
struct gpio_desc **desc_array,
- int *value_array);
+ unsigned long *value_bitmap);

/* Value get/set from sleeping context */
int gpiod_get_value_cansleep(const struct gpio_desc *desc);
int gpiod_get_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
- int *value_array);
+ unsigned long *value_bitmap);
void gpiod_set_value_cansleep(struct gpio_desc *desc, int value);
void gpiod_set_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
- int *value_array);
+ unsigned long *value_bitmap);
int gpiod_get_raw_value_cansleep(const struct gpio_desc *desc);
int gpiod_get_raw_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
- int *value_array);
+ unsigned long *value_bitmap);
void gpiod_set_raw_value_cansleep(struct gpio_desc *desc, int value);
int gpiod_set_raw_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
- int *value_array);
+ unsigned long *value_bitmap);

int gpiod_set_debounce(struct gpio_desc *desc, unsigned debounce);
int gpiod_set_transitory(struct gpio_desc *desc, bool transitory);
--
2.16.4


2018-09-02 12:03:49

by Janusz Krzysztofik

[permalink] [raw]
Subject: [PATCH v7 4/4] gpiolib: Implement fast processing path in get/set array

Certain GPIO descriptor arrays returned by gpio_get_array() may contain
information on direct mapping of array members to pins of a single GPIO
chip in hardware order. In such cases, bitmaps of values can be passed
directly from/to the chip's .get/set_multiple() callbacks without
wasting time on iterations.

Add respective code to gpiod_get/set_array_bitmap_complex() functions.
Pins not applicable for fast path are processed as before, skipping
over the 'fast' ones.

Cc: Jonathan Corbet <[email protected]>
Signed-off-by: Janusz Krzysztofik <[email protected]>
---
Documentation/driver-api/gpio/board.rst | 15 ++++++
Documentation/driver-api/gpio/consumer.rst | 8 +++
drivers/gpio/gpiolib.c | 87 ++++++++++++++++++++++++++++--
3 files changed, 105 insertions(+), 5 deletions(-)

diff --git a/Documentation/driver-api/gpio/board.rst b/Documentation/driver-api/gpio/board.rst
index 2c112553df84..c66821e033c2 100644
--- a/Documentation/driver-api/gpio/board.rst
+++ b/Documentation/driver-api/gpio/board.rst
@@ -193,3 +193,18 @@ And the table can be added to the board code as follows::

The line will be hogged as soon as the gpiochip is created or - in case the
chip was created earlier - when the hog table is registered.
+
+Arrays of pins
+--------------
+In addition to requesting pins belonging to a function one by one, a device may
+also request an array of pins assigned to the function. The way those pins are
+mapped to the device determines if the array qualifies for fast bitmap
+processing. If yes, a bitmap is passed over get/set array functions directly
+between a caller and a respective .get/set_multiple() callback of a GPIO chip.
+
+In order to qualify for fast bitmap processing, the pin mapping must meet the
+following requirements:
+- it must belong to the same chip as other 'fast' pins of the function,
+- its index within the function must match its hardware number within the chip.
+
+Open drain and open source pins are excluded from fast bitmap output processing.
diff --git a/Documentation/driver-api/gpio/consumer.rst b/Documentation/driver-api/gpio/consumer.rst
index 0afd95a12b10..cf992e5ab976 100644
--- a/Documentation/driver-api/gpio/consumer.rst
+++ b/Documentation/driver-api/gpio/consumer.rst
@@ -388,6 +388,14 @@ array_info should be set to NULL.
Note that for optimal performance GPIOs belonging to the same chip should be
contiguous within the array of descriptors.

+Still better performance may be achieved if array indexes of the descriptors
+match hardware pin numbers of a single chip. If an array passed to a get/set
+array function matches the one obtained from gpiod_get_array() and array_info
+associated with the array is also passed, the function may take a fast bitmap
+processing path, passing the value_bitmap argument directly to the respective
+.get/set_multiple() callback of the chip. That allows for utilization of GPIO
+banks as data I/O ports without much loss of performance.
+
The return value of gpiod_get_array_value() and its variants is 0 on success
or negative on error. Note the difference to gpiod_get_value(), which returns
0 or 1 on success to convey the GPIO value. With the array functions, the GPIO
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index cef6ee31fe05..b9d083fb13ee 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -2789,7 +2789,36 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep,
struct gpio_array *array_info,
unsigned long *value_bitmap)
{
- int i = 0;
+ int err, i = 0;
+
+ /*
+ * Validate array_info against desc_array and its size.
+ * It should immediately follow desc_array if both
+ * have been obtained from the same gpiod_get_array() call.
+ */
+ if (array_info && array_info->desc == desc_array &&
+ array_size <= array_info->size &&
+ (void *)array_info == desc_array + array_info->size) {
+ if (!can_sleep)
+ WARN_ON(array_info->chip->can_sleep);
+
+ err = gpio_chip_get_multiple(array_info->chip,
+ array_info->get_mask,
+ value_bitmap);
+ if (err)
+ return err;
+
+ if (!raw && !bitmap_empty(array_info->invert_mask, array_size))
+ bitmap_xor(value_bitmap, value_bitmap,
+ array_info->invert_mask, array_size);
+
+ if (bitmap_full(array_info->get_mask, array_size))
+ return 0;
+
+ i = find_first_zero_bit(array_info->get_mask, array_size);
+ } else {
+ array_info = NULL;
+ }

while (i < array_size) {
struct gpio_chip *chip = desc_array[i]->gdev->chip;
@@ -2820,7 +2849,12 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep,
int hwgpio = gpio_chip_hwgpio(desc);

__set_bit(hwgpio, mask);
- i++;
+
+ if (array_info)
+ find_next_zero_bit(array_info->get_mask,
+ array_size, i);
+ else
+ i++;
} while ((i < array_size) &&
(desc_array[i]->gdev->chip == chip));

@@ -2831,7 +2865,7 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep,
return ret;
}

- for (j = first; j < i; j++) {
+ for (j = first; j < i; ) {
const struct gpio_desc *desc = desc_array[j];
int hwgpio = gpio_chip_hwgpio(desc);
int value = test_bit(hwgpio, bits);
@@ -2840,6 +2874,11 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep,
value = !value;
__assign_bit(j, value_bitmap, value);
trace_gpio_value(desc_to_gpio(desc), 1, value);
+
+ if (array_info)
+ find_next_zero_bit(array_info->get_mask, i, j);
+ else
+ j++;
}

if (mask != fastpath)
@@ -3041,6 +3080,32 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep,
{
int i = 0;

+ /*
+ * Validate array_info against desc_array and its size.
+ * It should immediately follow desc_array if both
+ * have been obtained from the same gpiod_get_array() call.
+ */
+ if (array_info && array_info->desc == desc_array &&
+ array_size <= array_info->size &&
+ (void *)array_info == desc_array + array_info->size) {
+ if (!can_sleep)
+ WARN_ON(array_info->chip->can_sleep);
+
+ if (!raw && !bitmap_empty(array_info->invert_mask, array_size))
+ bitmap_xor(value_bitmap, value_bitmap,
+ array_info->invert_mask, array_size);
+
+ gpio_chip_set_multiple(array_info->chip, array_info->set_mask,
+ value_bitmap);
+
+ if (bitmap_full(array_info->set_mask, array_size))
+ return 0;
+
+ i = find_first_zero_bit(array_info->set_mask, array_size);
+ } else {
+ array_info = NULL;
+ }
+
while (i < array_size) {
struct gpio_chip *chip = desc_array[i]->gdev->chip;
unsigned long fastpath[2 * BITS_TO_LONGS(FASTPATH_NGPIO)];
@@ -3068,7 +3133,14 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep,
int hwgpio = gpio_chip_hwgpio(desc);
int value = test_bit(i, value_bitmap);

- if (!raw && test_bit(FLAG_ACTIVE_LOW, &desc->flags))
+ /*
+ * Pins applicable for fast input but not for
+ * fast output processing may have been already
+ * inverted inside the fast path, skip them.
+ */
+ if (!raw && !(array_info &&
+ test_bit(i, array_info->invert_mask)) &&
+ test_bit(FLAG_ACTIVE_LOW, &desc->flags))
value = !value;
trace_gpio_value(desc_to_gpio(desc), 0, value);
/*
@@ -3087,7 +3159,12 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep,
__clear_bit(hwgpio, bits);
count++;
}
- i++;
+
+ if (array_info)
+ find_next_zero_bit(array_info->set_mask,
+ array_size, i);
+ else
+ i++;
} while ((i < array_size) &&
(desc_array[i]->gdev->chip == chip));
/* push collected bits to outputs */
--
2.16.4


2018-09-02 12:04:21

by Janusz Krzysztofik

[permalink] [raw]
Subject: [PATCH v7 2/4] gpiolib: Identify arrays matching GPIO hardware

Certain GPIO array lookup results may map directly to GPIO pins of a
single GPIO chip in hardware order. If that condition is recognized
and handled efficiently, significant performance gain of get/set array
functions may be possible.

While processing a request for an array of GPIO descriptors, identify
those which represent corresponding pins of a single GPIO chip. Skip
over pins which require open source or open drain special processing.
Moreover, identify pins which require inversion. Pass a pointer to
that information with the array to the caller so it can benefit from
enhanced performance as soon as get/set array functions can accept and
make efficient use of it.

Cc: Jonathan Corbet <[email protected]>
Signed-off-by: Janusz Krzysztofik <[email protected]>
---
Documentation/driver-api/gpio/consumer.rst | 4 +-
drivers/gpio/gpiolib.c | 72 +++++++++++++++++++++++++++++-
drivers/gpio/gpiolib.h | 9 ++++
include/linux/gpio/consumer.h | 9 ++++
4 files changed, 92 insertions(+), 2 deletions(-)

diff --git a/Documentation/driver-api/gpio/consumer.rst b/Documentation/driver-api/gpio/consumer.rst
index ed68042ddccf..7e0298b9a7b9 100644
--- a/Documentation/driver-api/gpio/consumer.rst
+++ b/Documentation/driver-api/gpio/consumer.rst
@@ -109,9 +109,11 @@ For a function using multiple GPIOs all of those can be obtained with one call::
enum gpiod_flags flags)

This function returns a struct gpio_descs which contains an array of
-descriptors::
+descriptors. It also contains a pointer to a gpiolib private structure which,
+if passed back to get/set array functions, may speed up I/O proocessing::

struct gpio_descs {
+ struct gpio_array *info;
unsigned int ndescs;
struct gpio_desc *desc[];
}
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index 434d09779a1f..141f2f290538 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -4174,7 +4174,9 @@ struct gpio_descs *__must_check gpiod_get_array(struct device *dev,
{
struct gpio_desc *desc;
struct gpio_descs *descs;
- int count;
+ struct gpio_array *array_info = NULL;
+ struct gpio_chip *chip;
+ int count, bitmap_size;

count = gpiod_count(dev, con_id);
if (count < 0)
@@ -4190,9 +4192,77 @@ struct gpio_descs *__must_check gpiod_get_array(struct device *dev,
gpiod_put_array(descs);
return ERR_CAST(desc);
}
+
descs->desc[descs->ndescs] = desc;
+
+ chip = gpiod_to_chip(desc);
+ /*
+ * Select a chip of first array member
+ * whose index matches its pin hardware number
+ * as a candidate for fast bitmap processing.
+ */
+ if (!array_info && gpio_chip_hwgpio(desc) == descs->ndescs) {
+ struct gpio_descs *array;
+
+ bitmap_size = BITS_TO_LONGS(chip->ngpio > count ?
+ chip->ngpio : count);
+
+ array = kzalloc(struct_size(descs, desc, count) +
+ struct_size(array_info, invert_mask,
+ 3 * bitmap_size), GFP_KERNEL);
+ if (!array) {
+ gpiod_put_array(descs);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ memcpy(array, descs,
+ struct_size(descs, desc, descs->ndescs + 1));
+ kfree(descs);
+
+ descs = array;
+ array_info = (void *)(descs->desc + count);
+ array_info->get_mask = array_info->invert_mask +
+ bitmap_size;
+ array_info->set_mask = array_info->get_mask +
+ bitmap_size;
+
+ array_info->desc = descs->desc;
+ array_info->size = count;
+ array_info->chip = chip;
+ bitmap_set(array_info->get_mask, descs->ndescs,
+ count - descs->ndescs);
+ bitmap_set(array_info->set_mask, descs->ndescs,
+ count - descs->ndescs);
+ descs->info = array_info;
+ }
+ /*
+ * Unmark members which don't qualify for fast bitmap
+ * processing (different chip, not in hardware order)
+ */
+ if (array_info && (chip != array_info->chip ||
+ gpio_chip_hwgpio(desc) != descs->ndescs)) {
+ __clear_bit(descs->ndescs, array_info->get_mask);
+ __clear_bit(descs->ndescs, array_info->set_mask);
+ } else if (array_info) {
+ /* Exclude open drain or open source from fast output */
+ if (gpiochip_line_is_open_drain(chip, descs->ndescs) ||
+ gpiochip_line_is_open_source(chip, descs->ndescs))
+ __clear_bit(descs->ndescs,
+ array_info->set_mask);
+ /* Identify 'fast' pins which require invertion */
+ if (gpiod_is_active_low(desc))
+ __set_bit(descs->ndescs,
+ array_info->invert_mask);
+ }
+
descs->ndescs++;
}
+ if (array_info)
+ dev_dbg(dev,
+ "GPIO array info: chip=%s, size=%d, get_mask=%lx, set_mask=%lx, invert_mask=%lx\n",
+ array_info->chip->label, array_info->size,
+ *array_info->get_mask, *array_info->set_mask,
+ *array_info->invert_mask);
return descs;
}
EXPORT_SYMBOL_GPL(gpiod_get_array);
diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h
index 11e83d2eef89..b60905d558b1 100644
--- a/drivers/gpio/gpiolib.h
+++ b/drivers/gpio/gpiolib.h
@@ -183,6 +183,15 @@ static inline bool acpi_can_fallback_to_crs(struct acpi_device *adev,
}
#endif

+struct gpio_array {
+ struct gpio_desc **desc;
+ unsigned int size;
+ struct gpio_chip *chip;
+ unsigned long *get_mask;
+ unsigned long *set_mask;
+ unsigned long invert_mask[];
+};
+
struct gpio_desc *gpiochip_get_desc(struct gpio_chip *chip, u16 hwnum);
int gpiod_get_array_value_complex(bool raw, bool can_sleep,
unsigned int array_size,
diff --git a/include/linux/gpio/consumer.h b/include/linux/gpio/consumer.h
index 1b21dc7b0fad..8dede3e886af 100644
--- a/include/linux/gpio/consumer.h
+++ b/include/linux/gpio/consumer.h
@@ -17,11 +17,20 @@ struct device;
*/
struct gpio_desc;

+/**
+ * Opaque descriptor for a structure of GPIO array attributes. This structure
+ * is attached to struct gpiod_descs obtained from gpiod_get_array() and can be
+ * passed back to get/set array functions in order to activate fast processing
+ * path if applicable.
+ */
+struct gpio_array;
+
/**
* Struct containing an array of descriptors that can be obtained using
* gpiod_get_array().
*/
struct gpio_descs {
+ struct gpio_array *info;
unsigned int ndescs;
struct gpio_desc *desc[];
};
--
2.16.4


2018-09-02 13:23:43

by Lukas Wunner

[permalink] [raw]
Subject: Re: [PATCH v7 1/4] gpiolib: Pass bitmaps, not integer arrays, to get/set array

On Sun, Sep 02, 2018 at 02:01:41PM +0200, Janusz Krzysztofik wrote:
> @@ -461,7 +461,7 @@ static long linehandle_ioctl(struct file *filep, unsigned int cmd,
>
> /* Clamp all values to [0,1] */
> for (i = 0; i < lh->numdescs; i++)
> - vals[i] = !!ghd.values[i];
> + __assign_bit(i, vals, !!ghd.values[i]);

The "!!" becomes unnecessary and can be removed, same for the code
comment above.


> /**
> * gpiod_get_array_value() - read values from an array of GPIOs
> - * @array_size: number of elements in the descriptor / value arrays
> + * @array_size: number of elements in the descriptor array / value bitmap
> * @desc_array: array of GPIO descriptors whose values will be read
> - * @value_array: array to store the read values
> + * @value_bitnap: bitmap to store the read values

Typo, s/bitnap/bitmap/

Otherwise LGTM.

2018-09-03 04:33:15

by Matthew Wilcox

[permalink] [raw]
Subject: Re: [PATCH v7 1/4] gpiolib: Pass bitmaps, not integer arrays, to get/set array

> +++ b/drivers/auxdisplay/hd44780.c
> @@ -62,17 +62,12 @@ static void hd44780_strobe_gpio(struct hd44780 *hd)
> /* write to an LCD panel register in 8 bit GPIO mode */
> static void hd44780_write_gpio8(struct hd44780 *hd, u8 val, unsigned int rs)
> {
> - int values[10]; /* for DATA[0-7], RS, RW */
> - unsigned int i, n;
> -
> - for (i = 0; i < 8; i++)
> - values[PIN_DATA0 + i] = !!(val & BIT(i));
> - values[PIN_CTRL_RS] = rs;
> - n = 9;
> - if (hd->pins[PIN_CTRL_RW]) {
> - values[PIN_CTRL_RW] = 0;
> - n++;
> - }
> + DECLARE_BITMAP(values, 10); /* for DATA[0-7], RS, RW */
> + unsigned int n;
> +
> + *values = val;
> + __assign_bit(8, values, rs);
> + n = hd->pins[PIN_CTRL_RW] ? 10 : 9;

Doesn't this assume little endian bitmaps? Has anyone tested this on
big-endian machines?

2018-09-03 14:23:15

by Geert Uytterhoeven

[permalink] [raw]
Subject: Re: [PATCH v7 3/4] gpiolib: Pass array info to get/set array functions

On Sun, Sep 2, 2018 at 2:01 PM Janusz Krzysztofik <[email protected]> wrote:
> In order to make use of array info obtained from gpiod_get_array() and
> speed up processing of arrays matching single GPIO chip layout, that
> information must be passed to get/set array functions. Extend the
> functions' API with that additional parameter and update all users.
> Pass NULL if a user bulids an array itself from single GPIOs.

builds

Gr{oetje,eeting}s,

Geert

--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- [email protected]

In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
-- Linus Torvalds

2018-09-03 14:27:12

by Geert Uytterhoeven

[permalink] [raw]
Subject: Re: [PATCH v7 1/4] gpiolib: Pass bitmaps, not integer arrays, to get/set array

On Mon, Sep 3, 2018 at 6:31 AM Matthew Wilcox <[email protected]> wrote:
> > +++ b/drivers/auxdisplay/hd44780.c
> > @@ -62,17 +62,12 @@ static void hd44780_strobe_gpio(struct hd44780 *hd)
> > /* write to an LCD panel register in 8 bit GPIO mode */
> > static void hd44780_write_gpio8(struct hd44780 *hd, u8 val, unsigned int rs)
> > {
> > - int values[10]; /* for DATA[0-7], RS, RW */
> > - unsigned int i, n;
> > -
> > - for (i = 0; i < 8; i++)
> > - values[PIN_DATA0 + i] = !!(val & BIT(i));
> > - values[PIN_CTRL_RS] = rs;
> > - n = 9;
> > - if (hd->pins[PIN_CTRL_RW]) {
> > - values[PIN_CTRL_RW] = 0;
> > - n++;
> > - }
> > + DECLARE_BITMAP(values, 10); /* for DATA[0-7], RS, RW */
> > + unsigned int n;
> > +
> > + *values = val;
> > + __assign_bit(8, values, rs);
> > + n = hd->pins[PIN_CTRL_RW] ? 10 : 9;
>
> Doesn't this assume little endian bitmaps? Has anyone tested this on
> big-endian machines?

include/linux/bitops.h:

static __always_inline void __assign_bit(long nr, volatile unsigned long *addr,
bool value)
{
if (value)
__set_bit(nr, addr);
else
__clear_bit(nr, addr);
}

include/asm-generic/bitops/non-atomic.h:

static inline void __set_bit(int nr, volatile unsigned long *addr)
{
unsigned long mask = BIT_MASK(nr);
unsigned long *p = ((unsigned long *)addr) + BIT_WORD(nr);

*p |= mask;
}

include/linux/bits.h:

#define BIT_MASK(nr) (1UL << ((nr) % BITS_PER_LONG))

Looks like native endianness to me.

Gr{oetje,eeting}s,

Geert

--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- [email protected]

In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
-- Linus Torvalds

2018-09-03 15:09:41

by Geert Uytterhoeven

[permalink] [raw]
Subject: Re: [PATCH v7 1/4] gpiolib: Pass bitmaps, not integer arrays, to get/set array

Hi Janusz,

On Sun, Sep 2, 2018 at 2:01 PM Janusz Krzysztofik <[email protected]> wrote:
> Most users of get/set array functions iterate consecutive bits of data,
> usually a single integer, while processing array of results obtained
> from, or building an array of values to be passed to those functions.
> Save time wasted on those iterations by changing the functions' API to
> accept bitmaps.
>
> All current users are updated as well.
>
> More benefits from the change are expected as soon as planned support
> for accepting/passing those bitmaps directly from/to respective GPIO
> chip callbacks if applicable is implemented.
>
> Cc: Jonathan Corbet <[email protected]>
> Cc: Miguel Ojeda Sandonis <[email protected]>
> Cc: Geert Uytterhoeven <[email protected]>
> Cc: Sebastien Bourdelin <[email protected]>
> Cc: Lukas Wunner <[email protected]>
> Cc: Peter Korsgaard <[email protected]>
> Cc: Peter Rosin <[email protected]>
> Cc: Andrew Lunn <[email protected]>
> Cc: Florian Fainelli <[email protected]>
> Cc: "David S. Miller" <[email protected]>
> Cc: Rojhalat Ibrahim <[email protected]>
> Cc: Dominik Brodowski <[email protected]>
> Cc: Russell King <[email protected]>
> Cc: Kishon Vijay Abraham I <[email protected]>
> Cc: Tony Lindgren <[email protected]>
> Cc: Lars-Peter Clausen <[email protected]>
> Cc: Michael Hennerich <[email protected]>
> Cc: Jonathan Cameron <[email protected]>
> Cc: Hartmut Knaack <[email protected]>
> Cc: Peter Meerwald-Stadler <[email protected]>
> Cc: Greg Kroah-Hartman <[email protected]>
> Cc: Jiri Slaby <[email protected]>
> Cc: Yegor Yefremov <[email protected]>
> Cc: Uwe Kleine-König <[email protected]>
> Signed-off-by: Janusz Krzysztofik <[email protected]>
> Acked-by: Ulf Hansson <[email protected]>

> --- a/drivers/auxdisplay/hd44780.c
> +++ b/drivers/auxdisplay/hd44780.c
> @@ -62,17 +62,12 @@ static void hd44780_strobe_gpio(struct hd44780 *hd)
> /* write to an LCD panel register in 8 bit GPIO mode */
> static void hd44780_write_gpio8(struct hd44780 *hd, u8 val, unsigned int rs)
> {
> - int values[10]; /* for DATA[0-7], RS, RW */
> - unsigned int i, n;
> -
> - for (i = 0; i < 8; i++)
> - values[PIN_DATA0 + i] = !!(val & BIT(i));
> - values[PIN_CTRL_RS] = rs;
> - n = 9;
> - if (hd->pins[PIN_CTRL_RW]) {
> - values[PIN_CTRL_RW] = 0;
> - n++;
> - }
> + DECLARE_BITMAP(values, 10); /* for DATA[0-7], RS, RW */
> + unsigned int n;
> +
> + *values = val;

Given DECLARE_BITMAP() creates an array, the above line looks a bit funny now.

IMHO, either you use

unsigned long values;
values = val;
__assign_bit(8, &values, rs);

or

DECLARE_BITMAP(values, 10);
values[0] = val;
__assign_bit(8, values, rs);

Nevertheless, for hd44780.c:
Reviewed-by: Geert Uytterhoeven <[email protected]>
Tested-by: Geert Uytterhoeven <[email protected]>

Gr{oetje,eeting}s,

Geert

--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- [email protected]

In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
-- Linus Torvalds

2018-09-04 15:14:23

by kernel test robot

[permalink] [raw]
Subject: Re: [PATCH v7 3/4] gpiolib: Pass array info to get/set array functions

Hi Janusz,

Thank you for the patch! Perhaps something to improve:

[auto build test WARNING on gpio/for-next]
[also build test WARNING on v4.19-rc2 next-20180831]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url: https://github.com/0day-ci/linux/commits/Janusz-Krzysztofik/gpiolib-Pass-bitmaps-not-integer-arrays-to-get-set-array/20180903-172834
base: https://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-gpio.git for-next
reproduce: make htmldocs
:::::: branch date: 8 hours ago
:::::: commit date: 8 hours ago

All warnings (new ones prefixed by >>):

WARNING: convert(1) not found, for SVG to PDF conversion install ImageMagick (https://www.imagemagick.org)
include/linux/srcu.h:175: warning: Function parameter or member 'p' not described in 'srcu_dereference_notrace'
include/linux/srcu.h:175: warning: Function parameter or member 'sp' not described in 'srcu_dereference_notrace'
include/linux/gfp.h:1: warning: no structured comments found
include/net/cfg80211.h:4381: warning: Function parameter or member 'wext.ibss' not described in 'wireless_dev'
include/net/cfg80211.h:4381: warning: Function parameter or member 'wext.connect' not described in 'wireless_dev'
include/net/cfg80211.h:4381: warning: Function parameter or member 'wext.keys' not described in 'wireless_dev'
include/net/cfg80211.h:4381: warning: Function parameter or member 'wext.ie' not described in 'wireless_dev'
include/net/cfg80211.h:4381: warning: Function parameter or member 'wext.ie_len' not described in 'wireless_dev'
include/net/cfg80211.h:4381: warning: Function parameter or member 'wext.bssid' not described in 'wireless_dev'
include/net/cfg80211.h:4381: warning: Function parameter or member 'wext.ssid' not described in 'wireless_dev'
include/net/cfg80211.h:4381: warning: Function parameter or member 'wext.default_key' not described in 'wireless_dev'
include/net/cfg80211.h:4381: warning: Function parameter or member 'wext.default_mgmt_key' not described in 'wireless_dev'
include/net/cfg80211.h:4381: warning: Function parameter or member 'wext.prev_bssid_valid' not described in 'wireless_dev'
include/net/mac80211.h:2328: warning: Function parameter or member 'radiotap_timestamp.units_pos' not described in 'ieee80211_hw'
include/net/mac80211.h:2328: warning: Function parameter or member 'radiotap_timestamp.accuracy' not described in 'ieee80211_hw'
include/net/mac80211.h:977: warning: Function parameter or member 'control.rates' not described in 'ieee80211_tx_info'
include/net/mac80211.h:977: warning: Function parameter or member 'control.rts_cts_rate_idx' not described in 'ieee80211_tx_info'
include/net/mac80211.h:977: warning: Function parameter or member 'control.use_rts' not described in 'ieee80211_tx_info'
include/net/mac80211.h:977: warning: Function parameter or member 'control.use_cts_prot' not described in 'ieee80211_tx_info'
include/net/mac80211.h:977: warning: Function parameter or member 'control.short_preamble' not described in 'ieee80211_tx_info'
include/net/mac80211.h:977: warning: Function parameter or member 'control.skip_table' not described in 'ieee80211_tx_info'
include/net/mac80211.h:977: warning: Function parameter or member 'control.jiffies' not described in 'ieee80211_tx_info'
include/net/mac80211.h:977: warning: Function parameter or member 'control.vif' not described in 'ieee80211_tx_info'
include/net/mac80211.h:977: warning: Function parameter or member 'control.hw_key' not described in 'ieee80211_tx_info'
include/net/mac80211.h:977: warning: Function parameter or member 'control.flags' not described in 'ieee80211_tx_info'
include/net/mac80211.h:977: warning: Function parameter or member 'control.enqueue_time' not described in 'ieee80211_tx_info'
include/net/mac80211.h:977: warning: Function parameter or member 'ack' not described in 'ieee80211_tx_info'
include/net/mac80211.h:977: warning: Function parameter or member 'ack.cookie' not described in 'ieee80211_tx_info'
include/net/mac80211.h:977: warning: Function parameter or member 'status.rates' not described in 'ieee80211_tx_info'
include/net/mac80211.h:977: warning: Function parameter or member 'status.ack_signal' not described in 'ieee80211_tx_info'
include/net/mac80211.h:977: warning: Function parameter or member 'status.ampdu_ack_len' not described in 'ieee80211_tx_info'
include/net/mac80211.h:977: warning: Function parameter or member 'status.ampdu_len' not described in 'ieee80211_tx_info'
include/net/mac80211.h:977: warning: Function parameter or member 'status.antenna' not described in 'ieee80211_tx_info'
include/net/mac80211.h:977: warning: Function parameter or member 'status.tx_time' not described in 'ieee80211_tx_info'
include/net/mac80211.h:977: warning: Function parameter or member 'status.is_valid_ack_signal' not described in 'ieee80211_tx_info'
include/net/mac80211.h:977: warning: Function parameter or member 'status.status_driver_data' not described in 'ieee80211_tx_info'
include/net/mac80211.h:977: warning: Function parameter or member 'driver_rates' not described in 'ieee80211_tx_info'
include/net/mac80211.h:977: warning: Function parameter or member 'pad' not described in 'ieee80211_tx_info'
include/net/mac80211.h:977: warning: Function parameter or member 'rate_driver_data' not described in 'ieee80211_tx_info'
net/mac80211/sta_info.h:588: warning: Function parameter or member 'rx_stats_avg' not described in 'sta_info'
net/mac80211/sta_info.h:588: warning: Function parameter or member 'rx_stats_avg.signal' not described in 'sta_info'
net/mac80211/sta_info.h:588: warning: Function parameter or member 'rx_stats_avg.chain_signal' not described in 'sta_info'
net/mac80211/sta_info.h:588: warning: Function parameter or member 'status_stats.filtered' not described in 'sta_info'
net/mac80211/sta_info.h:588: warning: Function parameter or member 'status_stats.retry_failed' not described in 'sta_info'
net/mac80211/sta_info.h:588: warning: Function parameter or member 'status_stats.retry_count' not described in 'sta_info'
net/mac80211/sta_info.h:588: warning: Function parameter or member 'status_stats.lost_packets' not described in 'sta_info'
net/mac80211/sta_info.h:588: warning: Function parameter or member 'status_stats.last_tdls_pkt_time' not described in 'sta_info'
net/mac80211/sta_info.h:588: warning: Function parameter or member 'status_stats.msdu_retries' not described in 'sta_info'
net/mac80211/sta_info.h:588: warning: Function parameter or member 'status_stats.msdu_failed' not described in 'sta_info'
net/mac80211/sta_info.h:588: warning: Function parameter or member 'status_stats.last_ack' not described in 'sta_info'
net/mac80211/sta_info.h:588: warning: Function parameter or member 'status_stats.last_ack_signal' not described in 'sta_info'
net/mac80211/sta_info.h:588: warning: Function parameter or member 'status_stats.ack_signal_filled' not described in 'sta_info'
net/mac80211/sta_info.h:588: warning: Function parameter or member 'status_stats.avg_ack_signal' not described in 'sta_info'
net/mac80211/sta_info.h:588: warning: Function parameter or member 'tx_stats.packets' not described in 'sta_info'
net/mac80211/sta_info.h:588: warning: Function parameter or member 'tx_stats.bytes' not described in 'sta_info'
net/mac80211/sta_info.h:588: warning: Function parameter or member 'tx_stats.last_rate' not described in 'sta_info'
net/mac80211/sta_info.h:588: warning: Function parameter or member 'tx_stats.msdu' not described in 'sta_info'
include/linux/mod_devicetable.h:763: warning: Function parameter or member 'driver_data' not described in 'typec_device_id'
kernel/sched/fair.c:3371: warning: Function parameter or member 'flags' not described in 'attach_entity_load_avg'
arch/x86/include/asm/atomic.h:84: warning: Excess function parameter 'i' description in 'arch_atomic_sub_and_test'
arch/x86/include/asm/atomic.h:84: warning: Excess function parameter 'v' description in 'arch_atomic_sub_and_test'
arch/x86/include/asm/atomic.h:96: warning: Excess function parameter 'v' description in 'arch_atomic_inc'
arch/x86/include/asm/atomic.h:109: warning: Excess function parameter 'v' description in 'arch_atomic_dec'
arch/x86/include/asm/atomic.h:124: warning: Excess function parameter 'v' description in 'arch_atomic_dec_and_test'
arch/x86/include/asm/atomic.h:138: warning: Excess function parameter 'v' description in 'arch_atomic_inc_and_test'
arch/x86/include/asm/atomic.h:153: warning: Excess function parameter 'i' description in 'arch_atomic_add_negative'
arch/x86/include/asm/atomic.h:153: warning: Excess function parameter 'v' description in 'arch_atomic_add_negative'
include/linux/dma-buf.h:304: warning: Function parameter or member 'cb_excl.cb' not described in 'dma_buf'
include/linux/dma-buf.h:304: warning: Function parameter or member 'cb_excl.poll' not described in 'dma_buf'
include/linux/dma-buf.h:304: warning: Function parameter or member 'cb_excl.active' not described in 'dma_buf'
include/linux/dma-buf.h:304: warning: Function parameter or member 'cb_shared.cb' not described in 'dma_buf'
include/linux/dma-buf.h:304: warning: Function parameter or member 'cb_shared.poll' not described in 'dma_buf'
include/linux/dma-buf.h:304: warning: Function parameter or member 'cb_shared.active' not described in 'dma_buf'
include/linux/dma-fence-array.h:54: warning: Function parameter or member 'work' not described in 'dma_fence_array'
include/linux/gpio/driver.h:142: warning: Function parameter or member 'request_key' not described in 'gpio_irq_chip'
>> drivers/gpio/gpiolib.c:2917: warning: Function parameter or member 'array_info' not described in 'gpiod_get_raw_array_value'
>> drivers/gpio/gpiolib.c:2942: warning: Function parameter or member 'array_info' not described in 'gpiod_get_array_value'
drivers/gpio/gpiolib.c:2942: warning: Function parameter or member 'value_bitmap' not described in 'gpiod_get_array_value'
drivers/gpio/gpiolib.c:2942: warning: Excess function parameter 'value_bitnap' description in 'gpiod_get_array_value'
>> drivers/gpio/gpiolib.c:3180: warning: Function parameter or member 'array_info' not described in 'gpiod_set_raw_array_value'
>> drivers/gpio/gpiolib.c:3204: warning: Function parameter or member 'array_info' not described in 'gpiod_set_array_value'
>> drivers/gpio/gpiolib.c:3442: warning: Function parameter or member 'array_info' not described in 'gpiod_get_raw_array_value_cansleep'
>> drivers/gpio/gpiolib.c:3467: warning: Function parameter or member 'array_info' not described in 'gpiod_get_array_value_cansleep'
>> drivers/gpio/gpiolib.c:3528: warning: Function parameter or member 'array_info' not described in 'gpiod_set_raw_array_value_cansleep'
>> drivers/gpio/gpiolib.c:3569: warning: Function parameter or member 'array_info' not described in 'gpiod_set_array_value_cansleep'
include/linux/iio/hw-consumer.h:1: warning: no structured comments found
include/linux/input/sparse-keymap.h:46: warning: Function parameter or member 'sw' not described in 'key_entry'
drivers/pci/pci.c:218: warning: Excess function parameter 'p' description in 'pci_dev_str_match_path'
include/linux/regulator/driver.h:227: warning: Function parameter or member 'resume' not described in 'regulator_ops'
drivers/regulator/core.c:4479: warning: Excess function parameter 'state' description in 'regulator_suspend'
arch/s390/include/asm/cio.h:245: warning: Function parameter or member 'esw.esw0' not described in 'irb'
arch/s390/include/asm/cio.h:245: warning: Function parameter or member 'esw.esw1' not described in 'irb'
arch/s390/include/asm/cio.h:245: warning: Function parameter or member 'esw.esw2' not described in 'irb'
arch/s390/include/asm/cio.h:245: warning: Function parameter or member 'esw.esw3' not described in 'irb'
arch/s390/include/asm/cio.h:245: warning: Function parameter or member 'esw.eadm' not described in 'irb'
drivers/slimbus/stream.c:1: warning: no structured comments found
drivers/target/target_core_device.c:1: warning: no structured comments found
drivers/usb/dwc3/gadget.c:510: warning: Excess function parameter 'dwc' description in 'dwc3_gadget_start_config'
drivers/usb/typec/class.c:1497: warning: Excess function parameter 'drvdata' description in 'typec_port_register_altmode'
drivers/usb/typec/class.c:1497: warning: Excess function parameter 'drvdata' description in 'typec_port_register_altmode'
drivers/usb/typec/class.c:1497: warning: Excess function parameter 'drvdata' description in 'typec_port_register_altmode'
drivers/usb/typec/class.c:1497: warning: Excess function parameter 'drvdata' description in 'typec_port_register_altmode'
drivers/usb/typec/class.c:1497: warning: Excess function parameter 'drvdata' description in 'typec_port_register_altmode'
drivers/usb/typec/class.c:1497: warning: Excess function parameter 'drvdata' description in 'typec_port_register_altmode'
drivers/usb/typec/class.c:1497: warning: Excess function parameter 'drvdata' description in 'typec_port_register_altmode'
drivers/usb/typec/class.c:1497: warning: Excess function parameter 'drvdata' description in 'typec_port_register_altmode'
drivers/usb/typec/class.c:1497: warning: Excess function parameter 'drvdata' description in 'typec_port_register_altmode'
drivers/usb/typec/class.c:1497: warning: Excess function parameter 'drvdata' description in 'typec_port_register_altmode'
drivers/usb/typec/class.c:1497: warning: Excess function parameter 'drvdata' description in 'typec_port_register_altmode'
drivers/usb/typec/class.c:1497: warning: Excess function parameter 'drvdata' description in 'typec_port_register_altmode'
drivers/usb/typec/bus.c:1: warning: no structured comments found
drivers/usb/typec/bus.c:268: warning: Function parameter or member 'mode' not described in 'typec_match_altmode'
drivers/usb/typec/class.c:1497: warning: Excess function parameter 'drvdata' description in 'typec_port_register_altmode'
drivers/usb/typec/class.c:1: warning: no structured comments found
include/linux/w1.h:281: warning: Function parameter or member 'of_match_table' not described in 'w1_family'
fs/direct-io.c:257: warning: Excess function parameter 'offset' description in 'dio_complete'
fs/file_table.c:1: warning: no structured comments found
fs/libfs.c:477: warning: Excess function parameter 'available' description in 'simple_write_end'
fs/posix_acl.c:646: warning: Function parameter or member 'inode' not described in 'posix_acl_update_mode'
fs/posix_acl.c:646: warning: Function parameter or member 'mode_p' not described in 'posix_acl_update_mode'
fs/posix_acl.c:646: warning: Function parameter or member 'acl' not described in 'posix_acl_update_mode'
drivers/gpu/drm/amd/amdgpu/amdgpu_mn.c:183: warning: Function parameter or member 'blockable' not described in 'amdgpu_mn_read_lock'
drivers/gpu/drm/amd/amdgpu/amdgpu_mn.c:254: warning: Function parameter or member 'blockable' not described in 'amdgpu_mn_invalidate_range_start_gfx'
drivers/gpu/drm/amd/amdgpu/amdgpu_mn.c:302: warning: Function parameter or member 'blockable' not described in 'amdgpu_mn_invalidate_range_start_hsa'
drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c:2986: warning: Excess function parameter 'dev' description in 'amdgpu_vm_get_task_info'
drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c:2987: warning: Function parameter or member 'adev' not described in 'amdgpu_vm_get_task_info'
drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c:2987: warning: Excess function parameter 'dev' description in 'amdgpu_vm_get_task_info'
include/drm/drm_drv.h:610: warning: Function parameter or member 'gem_prime_pin' not described in 'drm_driver'
include/drm/drm_drv.h:610: warning: Function parameter or member 'gem_prime_unpin' not described in 'drm_driver'
include/drm/drm_drv.h:610: warning: Function parameter or member 'gem_prime_res_obj' not described in 'drm_driver'
include/drm/drm_drv.h:610: warning: Function parameter or member 'gem_prime_get_sg_table' not described in 'drm_driver'
include/drm/drm_drv.h:610: warning: Function parameter or member 'gem_prime_import_sg_table' not described in 'drm_driver'
include/drm/drm_drv.h:610: warning: Function parameter or member 'gem_prime_vmap' not described in 'drm_driver'
include/drm/drm_drv.h:610: warning: Function parameter or member 'gem_prime_vunmap' not described in 'drm_driver'
include/drm/drm_drv.h:610: warning: Function parameter or member 'gem_prime_mmap' not described in 'drm_driver'
include/drm/drm_panel.h:98: warning: Function parameter or member 'link' not described in 'drm_panel'
drivers/gpu/drm/i915/i915_vma.h:49: warning: cannot understand function prototype: 'struct i915_vma '
drivers/gpu/drm/i915/i915_vma.h:1: warning: no structured comments found
drivers/gpu/drm/i915/intel_guc_fwif.h:553: warning: cannot understand function prototype: 'struct guc_log_buffer_state '
drivers/gpu/drm/i915/i915_trace.h:1: warning: no structured comments found
include/linux/skbuff.h:860: warning: Function parameter or member 'dev_scratch' not described in 'sk_buff'
include/linux/skbuff.h:860: warning: Function parameter or member 'list' not described in 'sk_buff'
include/linux/skbuff.h:860: warning: Function parameter or member 'ip_defrag_offset' not described in 'sk_buff'
include/linux/skbuff.h:860: warning: Function parameter or member 'skb_mstamp' not described in 'sk_buff'
include/linux/skbuff.h:860: warning: Function parameter or member '__cloned_offset' not described in 'sk_buff'
include/linux/skbuff.h:860: warning: Function parameter or member 'head_frag' not described in 'sk_buff'
include/linux/skbuff.h:860: warning: Function parameter or member '__pkt_type_offset' not described in 'sk_buff'
include/linux/skbuff.h:860: warning: Function parameter or member 'encapsulation' not described in 'sk_buff'
include/linux/skbuff.h:860: warning: Function parameter or member 'encap_hdr_csum' not described in 'sk_buff'
include/linux/skbuff.h:860: warning: Function parameter or member 'csum_valid' not described in 'sk_buff'
include/linux/skbuff.h:860: warning: Function parameter or member 'csum_complete_sw' not described in 'sk_buff'
include/linux/skbuff.h:860: warning: Function parameter or member 'csum_level' not described in 'sk_buff'
include/linux/skbuff.h:860: warning: Function parameter or member 'inner_protocol_type' not described in 'sk_buff'
include/linux/skbuff.h:860: warning: Function parameter or member 'remcsum_offload' not described in 'sk_buff'
include/linux/skbuff.h:860: warning: Function parameter or member 'offload_fwd_mark' not described in 'sk_buff'
include/linux/skbuff.h:860: warning: Function parameter or member 'offload_mr_fwd_mark' not described in 'sk_buff'
include/linux/skbuff.h:860: warning: Function parameter or member 'sender_cpu' not described in 'sk_buff'
include/linux/skbuff.h:860: warning: Function parameter or member 'reserved_tailroom' not described in 'sk_buff'
include/linux/skbuff.h:860: warning: Function parameter or member 'inner_ipproto' not described in 'sk_buff'
include/net/sock.h:238: warning: Function parameter or member 'skc_addrpair' not described in 'sock_common'
include/net/sock.h:238: warning: Function parameter or member 'skc_portpair' not described in 'sock_common'
include/net/sock.h:238: warning: Function parameter or member 'skc_ipv6only' not described in 'sock_common'
include/net/sock.h:238: warning: Function parameter or member 'skc_net_refcnt' not described in 'sock_common'
include/net/sock.h:238: warning: Function parameter or member 'skc_v6_daddr' not described in 'sock_common'
include/net/sock.h:238: warning: Function parameter or member 'skc_v6_rcv_saddr' not described in 'sock_common'
include/net/sock.h:238: warning: Function parameter or member 'skc_cookie' not described in 'sock_common'
include/net/sock.h:238: warning: Function parameter or member 'skc_listener' not described in 'sock_common'
include/net/sock.h:238: warning: Function parameter or member 'skc_tw_dr' not described in 'sock_common'
include/net/sock.h:238: warning: Function parameter or member 'skc_rcv_wnd' not described in 'sock_common'
include/net/sock.h:238: warning: Function parameter or member 'skc_tw_rcv_nxt' not described in 'sock_common'
include/net/sock.h:509: warning: Function parameter or member 'sk_backlog.rmem_alloc' not described in 'sock'
include/net/sock.h:509: warning: Function parameter or member 'sk_backlog.len' not described in 'sock'
include/net/sock.h:509: warning: Function parameter or member 'sk_backlog.head' not described in 'sock'
include/net/sock.h:509: warning: Function parameter or member 'sk_backlog.tail' not described in 'sock'
include/net/sock.h:509: warning: Function parameter or member 'sk_wq_raw' not described in 'sock'
include/net/sock.h:509: warning: Function parameter or member 'tcp_rtx_queue' not described in 'sock'
include/net/sock.h:509: warning: Function parameter or member 'sk_route_forced_caps' not described in 'sock'
include/net/sock.h:509: warning: Function parameter or member 'sk_txtime_report_errors' not described in 'sock'
include/net/sock.h:509: warning: Function parameter or member 'sk_validate_xmit_skb' not described in 'sock'
include/linux/netdevice.h:2018: warning: Function parameter or member 'adj_list.upper' not described in 'net_device'
include/linux/netdevice.h:2018: warning: Function parameter or member 'adj_list.lower' not described in 'net_device'
include/linux/netdevice.h:2018: warning: Function parameter or member 'gso_partial_features' not described in 'net_device'
include/linux/netdevice.h:2018: warning: Function parameter or member 'switchdev_ops' not described in 'net_device'
include/linux/netdevice.h:2018: warning: Function parameter or member 'l3mdev_ops' not described in 'net_device'
include/linux/netdevice.h:2018: warning: Function parameter or member 'xfrmdev_ops' not described in 'net_device'

# https://github.com/0day-ci/linux/commit/dea6937cb6f545e86b0d5bc4c7e31be802de175d
git remote add linux-review https://github.com/0day-ci/linux
git remote update linux-review
git checkout dea6937cb6f545e86b0d5bc4c7e31be802de175d
vim +2917 drivers/gpio/gpiolib.c

d2876d08 David Brownell 2008-02-04 2898
eec1d566 Lukas Wunner 2017-10-12 2899 /**
eec1d566 Lukas Wunner 2017-10-12 2900 * gpiod_get_raw_array_value() - read raw values from an array of GPIOs
916010a7 Janusz Krzysztofik 2018-09-02 2901 * @array_size: number of elements in the descriptor array / value bitmap
eec1d566 Lukas Wunner 2017-10-12 2902 * @desc_array: array of GPIO descriptors whose values will be read
916010a7 Janusz Krzysztofik 2018-09-02 2903 * @value_bitmap: bitmap to store the read values
eec1d566 Lukas Wunner 2017-10-12 2904 *
eec1d566 Lukas Wunner 2017-10-12 2905 * Read the raw values of the GPIOs, i.e. the values of the physical lines
eec1d566 Lukas Wunner 2017-10-12 2906 * without regard for their ACTIVE_LOW status. Return 0 in case of success,
eec1d566 Lukas Wunner 2017-10-12 2907 * else an error code.
eec1d566 Lukas Wunner 2017-10-12 2908 *
eec1d566 Lukas Wunner 2017-10-12 2909 * This function should be called from contexts where we cannot sleep,
eec1d566 Lukas Wunner 2017-10-12 2910 * and it will complain if the GPIO chip functions potentially sleep.
eec1d566 Lukas Wunner 2017-10-12 2911 */
eec1d566 Lukas Wunner 2017-10-12 2912 int gpiod_get_raw_array_value(unsigned int array_size,
916010a7 Janusz Krzysztofik 2018-09-02 2913 struct gpio_desc **desc_array,
dea6937c Janusz Krzysztofik 2018-09-02 2914 struct gpio_array *array_info,
916010a7 Janusz Krzysztofik 2018-09-02 2915 unsigned long *value_bitmap)
eec1d566 Lukas Wunner 2017-10-12 2916 {
eec1d566 Lukas Wunner 2017-10-12 @2917 if (!desc_array)
eec1d566 Lukas Wunner 2017-10-12 2918 return -EINVAL;
eec1d566 Lukas Wunner 2017-10-12 2919 return gpiod_get_array_value_complex(true, false, array_size,
dea6937c Janusz Krzysztofik 2018-09-02 2920 desc_array, array_info,
dea6937c Janusz Krzysztofik 2018-09-02 2921 value_bitmap);
eec1d566 Lukas Wunner 2017-10-12 2922 }
eec1d566 Lukas Wunner 2017-10-12 2923 EXPORT_SYMBOL_GPL(gpiod_get_raw_array_value);
eec1d566 Lukas Wunner 2017-10-12 2924
eec1d566 Lukas Wunner 2017-10-12 2925 /**
eec1d566 Lukas Wunner 2017-10-12 2926 * gpiod_get_array_value() - read values from an array of GPIOs
916010a7 Janusz Krzysztofik 2018-09-02 2927 * @array_size: number of elements in the descriptor array / value bitmap
eec1d566 Lukas Wunner 2017-10-12 2928 * @desc_array: array of GPIO descriptors whose values will be read
916010a7 Janusz Krzysztofik 2018-09-02 2929 * @value_bitnap: bitmap to store the read values
eec1d566 Lukas Wunner 2017-10-12 2930 *
eec1d566 Lukas Wunner 2017-10-12 2931 * Read the logical values of the GPIOs, i.e. taking their ACTIVE_LOW status
eec1d566 Lukas Wunner 2017-10-12 2932 * into account. Return 0 in case of success, else an error code.
eec1d566 Lukas Wunner 2017-10-12 2933 *
eec1d566 Lukas Wunner 2017-10-12 2934 * This function should be called from contexts where we cannot sleep,
eec1d566 Lukas Wunner 2017-10-12 2935 * and it will complain if the GPIO chip functions potentially sleep.
eec1d566 Lukas Wunner 2017-10-12 2936 */
eec1d566 Lukas Wunner 2017-10-12 2937 int gpiod_get_array_value(unsigned int array_size,
916010a7 Janusz Krzysztofik 2018-09-02 2938 struct gpio_desc **desc_array,
dea6937c Janusz Krzysztofik 2018-09-02 2939 struct gpio_array *array_info,
916010a7 Janusz Krzysztofik 2018-09-02 2940 unsigned long *value_bitmap)
eec1d566 Lukas Wunner 2017-10-12 2941 {
eec1d566 Lukas Wunner 2017-10-12 @2942 if (!desc_array)
eec1d566 Lukas Wunner 2017-10-12 2943 return -EINVAL;
eec1d566 Lukas Wunner 2017-10-12 2944 return gpiod_get_array_value_complex(false, false, array_size,
dea6937c Janusz Krzysztofik 2018-09-02 2945 desc_array, array_info,
dea6937c Janusz Krzysztofik 2018-09-02 2946 value_bitmap);
eec1d566 Lukas Wunner 2017-10-12 2947 }
eec1d566 Lukas Wunner 2017-10-12 2948 EXPORT_SYMBOL_GPL(gpiod_get_array_value);
eec1d566 Lukas Wunner 2017-10-12 2949

:::::: The code at line 2917 was first introduced by commit
:::::: eec1d566cdf94b57e8f5ba9fe60eea214929bcfc gpio: Introduce ->get_multiple callback

:::::: TO: Lukas Wunner <[email protected]>
:::::: CC: Linus Walleij <[email protected]>

---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation


Attachments:
(No filename) (26.87 kB)
.config.gz (6.43 kB)
Download all attachments

2018-09-04 15:17:22

by kernel test robot

[permalink] [raw]
Subject: Re: [PATCH v6 3/4] gpiolib: Pass array info to get/set array functions

Hi Janusz,

Thank you for the patch! Yet something to improve:

[auto build test ERROR on gpio/for-next]
[also build test ERROR on v4.19-rc2 next-20180831]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url: https://github.com/0day-ci/linux/commits/Janusz-Krzysztofik/gpiolib-speed-up-GPIO-array-processing/20180903-174241
base: https://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-gpio.git for-next
config: arm-cns3420vb_defconfig (attached as .config)
compiler: arm-linux-gnueabi-gcc (Debian 7.2.0-11) 7.2.0
reproduce:
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# save the attached .config to linux build tree
GCC_VERSION=7.2.0 make.cross ARCH=arm
:::::: branch date: 6 hours ago
:::::: commit date: 6 hours ago

All errors (new ones prefixed by >>):

drivers/mmc/core/pwrseq_simple.c: In function 'mmc_pwrseq_simple_set_gpios_value':
drivers/mmc/core/pwrseq_simple.c:49:13: error: passing argument 3 of 'gpiod_set_array_value_cansleep' from incompatible pointer type [-Werror=incompatible-pointer-types]
reset_gpios->info, value_bitmap);
^~~~~~~~~~~
In file included from drivers/mmc/core/pwrseq_simple.c:18:0:
include/linux/gpio/consumer.h:417:20: note: expected 'int *' but argument is of type 'struct gpio_array *'
static inline void gpiod_set_array_value_cansleep(unsigned int array_size,
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
>> drivers/mmc/core/pwrseq_simple.c:48:3: error: too many arguments to function 'gpiod_set_array_value_cansleep'
gpiod_set_array_value_cansleep(nvalues, reset_gpios->desc,
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In file included from drivers/mmc/core/pwrseq_simple.c:18:0:
include/linux/gpio/consumer.h:417:20: note: declared here
static inline void gpiod_set_array_value_cansleep(unsigned int array_size,
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
cc1: some warnings being treated as errors

# https://github.com/0day-ci/linux/commit/2313edb413de154774fb6a3431f5f135f9c78180
git remote add linux-review https://github.com/0day-ci/linux
git remote update linux-review
git checkout 2313edb413de154774fb6a3431f5f135f9c78180
vim +/gpiod_set_array_value_cansleep +48 drivers/mmc/core/pwrseq_simple.c

5b96fea73 Srinivas Kandagatla 2016-04-14 36
934f1f483 Javier Martinez Canillas 2015-01-29 37 static void mmc_pwrseq_simple_set_gpios_value(struct mmc_pwrseq_simple *pwrseq,
934f1f483 Javier Martinez Canillas 2015-01-29 38 int value)
934f1f483 Javier Martinez Canillas 2015-01-29 39 {
ce0372758 Javier Martinez Canillas 2015-09-21 40 struct gpio_descs *reset_gpios = pwrseq->reset_gpios;
64a67d476 Martin Fuzzey 2016-01-20 41
64a67d476 Martin Fuzzey 2016-01-20 42 if (!IS_ERR(reset_gpios)) {
486e66613 Tobin C. Harding 2018-03-26 43 int nvalues = reset_gpios->ndescs;
00db98568 Janusz Krzysztofik 2018-09-01 44 DECLARE_BITMAP(value_bitmap, nvalues);
934f1f483 Javier Martinez Canillas 2015-01-29 45
00db98568 Janusz Krzysztofik 2018-09-01 46 *value_bitmap = value;
486e66613 Tobin C. Harding 2018-03-26 47
00db98568 Janusz Krzysztofik 2018-09-01 @48 gpiod_set_array_value_cansleep(nvalues, reset_gpios->desc,
2313edb41 Janusz Krzysztofik 2018-09-01 @49 reset_gpios->info, value_bitmap);
64a67d476 Martin Fuzzey 2016-01-20 50 }
934f1f483 Javier Martinez Canillas 2015-01-29 51 }
934f1f483 Javier Martinez Canillas 2015-01-29 52

:::::: The code at line 48 was first introduced by commit
:::::: 00db98568b73a7b04c5120e5b87934c7355cc015 gpiolib: Pass bitmaps, not integer arrays, to get/set array

:::::: TO: Janusz Krzysztofik <[email protected]>
:::::: CC: 0day robot <[email protected]>

---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation


Attachments:
(No filename) (4.07 kB)
.config.gz (9.40 kB)
Download all attachments

2018-09-04 15:18:01

by kernel test robot

[permalink] [raw]
Subject: Re: [PATCH v6 1/4] gpiolib: Pass bitmaps, not integer arrays, to get/set array

Hi Janusz,

Thank you for the patch! Perhaps something to improve:

[auto build test WARNING on gpio/for-next]
[also build test WARNING on v4.19-rc2 next-20180831]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url: https://github.com/0day-ci/linux/commits/Janusz-Krzysztofik/gpiolib-speed-up-GPIO-array-processing/20180903-174241
base: https://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-gpio.git for-next
reproduce:
# apt-get install sparse
make ARCH=x86_64 allmodconfig
make C=1 CF=-D__CHECK_ENDIAN__
:::::: branch date: 9 hours ago
:::::: commit date: 9 hours ago

>> drivers/mux/gpio.c:25:9: sparse: Variable length array is used.
--
>> drivers/i2c/muxes/i2c-mux-gpio.c:29:9: sparse: Variable length array is used.
include/linux/device.h:685:13: sparse: undefined identifier '__builtin_mul_overflow'
include/linux/device.h:685:13: sparse: not a function <noident>
include/linux/device.h:685:13: sparse: call with no type!

# https://github.com/0day-ci/linux/commit/00db98568b73a7b04c5120e5b87934c7355cc015
git remote add linux-review https://github.com/0day-ci/linux
git remote update linux-review
git checkout 00db98568b73a7b04c5120e5b87934c7355cc015
vim +25 drivers/mux/gpio.c

2c089f08e drivers/mux/mux-gpio.c Peter Rosin 2017-05-14 21
2c089f08e drivers/mux/mux-gpio.c Peter Rosin 2017-05-14 22 static int mux_gpio_set(struct mux_control *mux, int state)
2c089f08e drivers/mux/mux-gpio.c Peter Rosin 2017-05-14 23 {
2c089f08e drivers/mux/mux-gpio.c Peter Rosin 2017-05-14 24 struct mux_gpio *mux_gpio = mux_chip_priv(mux->chip);
00db98568 drivers/mux/gpio.c Janusz Krzysztofik 2018-09-01 @25 DECLARE_BITMAP(value_bitmap, mux_gpio->gpios->ndescs);
2c089f08e drivers/mux/mux-gpio.c Peter Rosin 2017-05-14 26
00db98568 drivers/mux/gpio.c Janusz Krzysztofik 2018-09-01 27 *value_bitmap = state;
2c089f08e drivers/mux/mux-gpio.c Peter Rosin 2017-05-14 28
2c089f08e drivers/mux/mux-gpio.c Peter Rosin 2017-05-14 29 gpiod_set_array_value_cansleep(mux_gpio->gpios->ndescs,
00db98568 drivers/mux/gpio.c Janusz Krzysztofik 2018-09-01 30 mux_gpio->gpios->desc, value_bitmap);
2c089f08e drivers/mux/mux-gpio.c Peter Rosin 2017-05-14 31
2c089f08e drivers/mux/mux-gpio.c Peter Rosin 2017-05-14 32 return 0;
2c089f08e drivers/mux/mux-gpio.c Peter Rosin 2017-05-14 33 }
2c089f08e drivers/mux/mux-gpio.c Peter Rosin 2017-05-14 34

---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation

2018-09-04 15:19:36

by kernel test robot

[permalink] [raw]
Subject: Re: [PATCH v6 1/4] gpiolib: Pass bitmaps, not integer arrays, to get/set array

Hi Janusz,

Thank you for the patch! Yet something to improve:

[auto build test ERROR on gpio/for-next]
[also build test ERROR on v4.19-rc2 next-20180831]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url: https://github.com/0day-ci/linux/commits/Janusz-Krzysztofik/gpiolib-speed-up-GPIO-array-processing/20180903-174241
base: https://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-gpio.git for-next
config: arm-cns3420vb_defconfig (attached as .config)
compiler: arm-linux-gnueabi-gcc (Debian 7.2.0-11) 7.2.0
reproduce:
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# save the attached .config to linux build tree
GCC_VERSION=7.2.0 make.cross ARCH=arm
:::::: branch date: 5 hours ago
:::::: commit date: 5 hours ago

All errors (new ones prefixed by >>):

drivers/mmc/core/pwrseq_simple.c: In function 'mmc_pwrseq_simple_set_gpios_value':
>> drivers/mmc/core/pwrseq_simple.c:49:13: error: passing argument 3 of 'gpiod_set_array_value_cansleep' from incompatible pointer type [-Werror=incompatible-pointer-types]
value_bitmap);
^~~~~~~~~~~~
In file included from drivers/mmc/core/pwrseq_simple.c:18:0:
include/linux/gpio/consumer.h:400:20: note: expected 'int *' but argument is of type 'long unsigned int *'
static inline void gpiod_set_array_value_cansleep(unsigned int array_size,
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
cc1: some warnings being treated as errors

# https://github.com/0day-ci/linux/commit/00db98568b73a7b04c5120e5b87934c7355cc015
git remote add linux-review https://github.com/0day-ci/linux
git remote update linux-review
git checkout 00db98568b73a7b04c5120e5b87934c7355cc015
vim +/gpiod_set_array_value_cansleep +49 drivers/mmc/core/pwrseq_simple.c

5b96fea73 Srinivas Kandagatla 2016-04-14 36
934f1f483 Javier Martinez Canillas 2015-01-29 37 static void mmc_pwrseq_simple_set_gpios_value(struct mmc_pwrseq_simple *pwrseq,
934f1f483 Javier Martinez Canillas 2015-01-29 38 int value)
934f1f483 Javier Martinez Canillas 2015-01-29 39 {
ce0372758 Javier Martinez Canillas 2015-09-21 40 struct gpio_descs *reset_gpios = pwrseq->reset_gpios;
64a67d476 Martin Fuzzey 2016-01-20 41
64a67d476 Martin Fuzzey 2016-01-20 42 if (!IS_ERR(reset_gpios)) {
486e66613 Tobin C. Harding 2018-03-26 43 int nvalues = reset_gpios->ndescs;
00db98568 Janusz Krzysztofik 2018-09-01 44 DECLARE_BITMAP(value_bitmap, nvalues);
934f1f483 Javier Martinez Canillas 2015-01-29 45
00db98568 Janusz Krzysztofik 2018-09-01 46 *value_bitmap = value;
486e66613 Tobin C. Harding 2018-03-26 47
00db98568 Janusz Krzysztofik 2018-09-01 48 gpiod_set_array_value_cansleep(nvalues, reset_gpios->desc,
00db98568 Janusz Krzysztofik 2018-09-01 @49 value_bitmap);
64a67d476 Martin Fuzzey 2016-01-20 50 }
934f1f483 Javier Martinez Canillas 2015-01-29 51 }
934f1f483 Javier Martinez Canillas 2015-01-29 52

---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation


Attachments:
(No filename) (3.31 kB)
.config.gz (9.40 kB)
Download all attachments

2018-09-04 15:21:58

by kernel test robot

[permalink] [raw]
Subject: Re: [PATCH v7 1/4] gpiolib: Pass bitmaps, not integer arrays, to get/set array

Hi Janusz,

Thank you for the patch! Perhaps something to improve:

[auto build test WARNING on gpio/for-next]
[also build test WARNING on v4.19-rc2 next-20180831]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url: https://github.com/0day-ci/linux/commits/Janusz-Krzysztofik/gpiolib-Pass-bitmaps-not-integer-arrays-to-get-set-array/20180903-172834
base: https://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-gpio.git for-next
reproduce: make htmldocs
:::::: branch date: 5 hours ago
:::::: commit date: 5 hours ago

All warnings (new ones prefixed by >>):

WARNING: convert(1) not found, for SVG to PDF conversion install ImageMagick (https://www.imagemagick.org)
include/linux/srcu.h:175: warning: Function parameter or member 'p' not described in 'srcu_dereference_notrace'
include/linux/srcu.h:175: warning: Function parameter or member 'sp' not described in 'srcu_dereference_notrace'
include/linux/gfp.h:1: warning: no structured comments found
include/net/cfg80211.h:4381: warning: Function parameter or member 'wext.ibss' not described in 'wireless_dev'
include/net/cfg80211.h:4381: warning: Function parameter or member 'wext.connect' not described in 'wireless_dev'
include/net/cfg80211.h:4381: warning: Function parameter or member 'wext.keys' not described in 'wireless_dev'
include/net/cfg80211.h:4381: warning: Function parameter or member 'wext.ie' not described in 'wireless_dev'
include/net/cfg80211.h:4381: warning: Function parameter or member 'wext.ie_len' not described in 'wireless_dev'
include/net/cfg80211.h:4381: warning: Function parameter or member 'wext.bssid' not described in 'wireless_dev'
include/net/cfg80211.h:4381: warning: Function parameter or member 'wext.ssid' not described in 'wireless_dev'
include/net/cfg80211.h:4381: warning: Function parameter or member 'wext.default_key' not described in 'wireless_dev'
include/net/cfg80211.h:4381: warning: Function parameter or member 'wext.default_mgmt_key' not described in 'wireless_dev'
include/net/cfg80211.h:4381: warning: Function parameter or member 'wext.prev_bssid_valid' not described in 'wireless_dev'
include/net/mac80211.h:2328: warning: Function parameter or member 'radiotap_timestamp.units_pos' not described in 'ieee80211_hw'
include/net/mac80211.h:2328: warning: Function parameter or member 'radiotap_timestamp.accuracy' not described in 'ieee80211_hw'
include/net/mac80211.h:977: warning: Function parameter or member 'control.rates' not described in 'ieee80211_tx_info'
include/net/mac80211.h:977: warning: Function parameter or member 'control.rts_cts_rate_idx' not described in 'ieee80211_tx_info'
include/net/mac80211.h:977: warning: Function parameter or member 'control.use_rts' not described in 'ieee80211_tx_info'
include/net/mac80211.h:977: warning: Function parameter or member 'control.use_cts_prot' not described in 'ieee80211_tx_info'
include/net/mac80211.h:977: warning: Function parameter or member 'control.short_preamble' not described in 'ieee80211_tx_info'
include/net/mac80211.h:977: warning: Function parameter or member 'control.skip_table' not described in 'ieee80211_tx_info'
include/net/mac80211.h:977: warning: Function parameter or member 'control.jiffies' not described in 'ieee80211_tx_info'
include/net/mac80211.h:977: warning: Function parameter or member 'control.vif' not described in 'ieee80211_tx_info'
include/net/mac80211.h:977: warning: Function parameter or member 'control.hw_key' not described in 'ieee80211_tx_info'
include/net/mac80211.h:977: warning: Function parameter or member 'control.flags' not described in 'ieee80211_tx_info'
include/net/mac80211.h:977: warning: Function parameter or member 'control.enqueue_time' not described in 'ieee80211_tx_info'
include/net/mac80211.h:977: warning: Function parameter or member 'ack' not described in 'ieee80211_tx_info'
include/net/mac80211.h:977: warning: Function parameter or member 'ack.cookie' not described in 'ieee80211_tx_info'
include/net/mac80211.h:977: warning: Function parameter or member 'status.rates' not described in 'ieee80211_tx_info'
include/net/mac80211.h:977: warning: Function parameter or member 'status.ack_signal' not described in 'ieee80211_tx_info'
include/net/mac80211.h:977: warning: Function parameter or member 'status.ampdu_ack_len' not described in 'ieee80211_tx_info'
include/net/mac80211.h:977: warning: Function parameter or member 'status.ampdu_len' not described in 'ieee80211_tx_info'
include/net/mac80211.h:977: warning: Function parameter or member 'status.antenna' not described in 'ieee80211_tx_info'
include/net/mac80211.h:977: warning: Function parameter or member 'status.tx_time' not described in 'ieee80211_tx_info'
include/net/mac80211.h:977: warning: Function parameter or member 'status.is_valid_ack_signal' not described in 'ieee80211_tx_info'
include/net/mac80211.h:977: warning: Function parameter or member 'status.status_driver_data' not described in 'ieee80211_tx_info'
include/net/mac80211.h:977: warning: Function parameter or member 'driver_rates' not described in 'ieee80211_tx_info'
include/net/mac80211.h:977: warning: Function parameter or member 'pad' not described in 'ieee80211_tx_info'
include/net/mac80211.h:977: warning: Function parameter or member 'rate_driver_data' not described in 'ieee80211_tx_info'
net/mac80211/sta_info.h:588: warning: Function parameter or member 'rx_stats_avg' not described in 'sta_info'
net/mac80211/sta_info.h:588: warning: Function parameter or member 'rx_stats_avg.signal' not described in 'sta_info'
net/mac80211/sta_info.h:588: warning: Function parameter or member 'rx_stats_avg.chain_signal' not described in 'sta_info'
net/mac80211/sta_info.h:588: warning: Function parameter or member 'status_stats.filtered' not described in 'sta_info'
net/mac80211/sta_info.h:588: warning: Function parameter or member 'status_stats.retry_failed' not described in 'sta_info'
net/mac80211/sta_info.h:588: warning: Function parameter or member 'status_stats.retry_count' not described in 'sta_info'
net/mac80211/sta_info.h:588: warning: Function parameter or member 'status_stats.lost_packets' not described in 'sta_info'
net/mac80211/sta_info.h:588: warning: Function parameter or member 'status_stats.last_tdls_pkt_time' not described in 'sta_info'
net/mac80211/sta_info.h:588: warning: Function parameter or member 'status_stats.msdu_retries' not described in 'sta_info'
net/mac80211/sta_info.h:588: warning: Function parameter or member 'status_stats.msdu_failed' not described in 'sta_info'
net/mac80211/sta_info.h:588: warning: Function parameter or member 'status_stats.last_ack' not described in 'sta_info'
net/mac80211/sta_info.h:588: warning: Function parameter or member 'status_stats.last_ack_signal' not described in 'sta_info'
net/mac80211/sta_info.h:588: warning: Function parameter or member 'status_stats.ack_signal_filled' not described in 'sta_info'
net/mac80211/sta_info.h:588: warning: Function parameter or member 'status_stats.avg_ack_signal' not described in 'sta_info'
net/mac80211/sta_info.h:588: warning: Function parameter or member 'tx_stats.packets' not described in 'sta_info'
net/mac80211/sta_info.h:588: warning: Function parameter or member 'tx_stats.bytes' not described in 'sta_info'
net/mac80211/sta_info.h:588: warning: Function parameter or member 'tx_stats.last_rate' not described in 'sta_info'
net/mac80211/sta_info.h:588: warning: Function parameter or member 'tx_stats.msdu' not described in 'sta_info'
include/linux/mod_devicetable.h:763: warning: Function parameter or member 'driver_data' not described in 'typec_device_id'
kernel/sched/fair.c:3371: warning: Function parameter or member 'flags' not described in 'attach_entity_load_avg'
arch/x86/include/asm/atomic.h:84: warning: Excess function parameter 'i' description in 'arch_atomic_sub_and_test'
arch/x86/include/asm/atomic.h:84: warning: Excess function parameter 'v' description in 'arch_atomic_sub_and_test'
arch/x86/include/asm/atomic.h:96: warning: Excess function parameter 'v' description in 'arch_atomic_inc'
arch/x86/include/asm/atomic.h:109: warning: Excess function parameter 'v' description in 'arch_atomic_dec'
arch/x86/include/asm/atomic.h:124: warning: Excess function parameter 'v' description in 'arch_atomic_dec_and_test'
arch/x86/include/asm/atomic.h:138: warning: Excess function parameter 'v' description in 'arch_atomic_inc_and_test'
arch/x86/include/asm/atomic.h:153: warning: Excess function parameter 'i' description in 'arch_atomic_add_negative'
arch/x86/include/asm/atomic.h:153: warning: Excess function parameter 'v' description in 'arch_atomic_add_negative'
include/linux/dma-buf.h:304: warning: Function parameter or member 'cb_excl.cb' not described in 'dma_buf'
include/linux/dma-buf.h:304: warning: Function parameter or member 'cb_excl.poll' not described in 'dma_buf'
include/linux/dma-buf.h:304: warning: Function parameter or member 'cb_excl.active' not described in 'dma_buf'
include/linux/dma-buf.h:304: warning: Function parameter or member 'cb_shared.cb' not described in 'dma_buf'
include/linux/dma-buf.h:304: warning: Function parameter or member 'cb_shared.poll' not described in 'dma_buf'
include/linux/dma-buf.h:304: warning: Function parameter or member 'cb_shared.active' not described in 'dma_buf'
include/linux/dma-fence-array.h:54: warning: Function parameter or member 'work' not described in 'dma_fence_array'
include/linux/gpio/driver.h:142: warning: Function parameter or member 'request_key' not described in 'gpio_irq_chip'
>> drivers/gpio/gpiolib.c:2936: warning: Function parameter or member 'value_bitmap' not described in 'gpiod_get_array_value'
drivers/gpio/gpiolib.c:2936: warning: Excess function parameter 'value_bitnap' description in 'gpiod_get_array_value'
include/linux/iio/hw-consumer.h:1: warning: no structured comments found
include/linux/input/sparse-keymap.h:46: warning: Function parameter or member 'sw' not described in 'key_entry'
drivers/pci/pci.c:218: warning: Excess function parameter 'p' description in 'pci_dev_str_match_path'
include/linux/regulator/driver.h:227: warning: Function parameter or member 'resume' not described in 'regulator_ops'
drivers/regulator/core.c:4479: warning: Excess function parameter 'state' description in 'regulator_suspend'
arch/s390/include/asm/cio.h:245: warning: Function parameter or member 'esw.esw0' not described in 'irb'
arch/s390/include/asm/cio.h:245: warning: Function parameter or member 'esw.esw1' not described in 'irb'
arch/s390/include/asm/cio.h:245: warning: Function parameter or member 'esw.esw2' not described in 'irb'
arch/s390/include/asm/cio.h:245: warning: Function parameter or member 'esw.esw3' not described in 'irb'
arch/s390/include/asm/cio.h:245: warning: Function parameter or member 'esw.eadm' not described in 'irb'
drivers/slimbus/stream.c:1: warning: no structured comments found
drivers/target/target_core_device.c:1: warning: no structured comments found
drivers/usb/dwc3/gadget.c:510: warning: Excess function parameter 'dwc' description in 'dwc3_gadget_start_config'
drivers/usb/typec/class.c:1497: warning: Excess function parameter 'drvdata' description in 'typec_port_register_altmode'
drivers/usb/typec/class.c:1497: warning: Excess function parameter 'drvdata' description in 'typec_port_register_altmode'
drivers/usb/typec/class.c:1497: warning: Excess function parameter 'drvdata' description in 'typec_port_register_altmode'
drivers/usb/typec/class.c:1497: warning: Excess function parameter 'drvdata' description in 'typec_port_register_altmode'
drivers/usb/typec/class.c:1497: warning: Excess function parameter 'drvdata' description in 'typec_port_register_altmode'
drivers/usb/typec/class.c:1497: warning: Excess function parameter 'drvdata' description in 'typec_port_register_altmode'
drivers/usb/typec/class.c:1497: warning: Excess function parameter 'drvdata' description in 'typec_port_register_altmode'
drivers/usb/typec/class.c:1497: warning: Excess function parameter 'drvdata' description in 'typec_port_register_altmode'
drivers/usb/typec/class.c:1497: warning: Excess function parameter 'drvdata' description in 'typec_port_register_altmode'
drivers/usb/typec/class.c:1497: warning: Excess function parameter 'drvdata' description in 'typec_port_register_altmode'
drivers/usb/typec/class.c:1497: warning: Excess function parameter 'drvdata' description in 'typec_port_register_altmode'
drivers/usb/typec/class.c:1497: warning: Excess function parameter 'drvdata' description in 'typec_port_register_altmode'
drivers/usb/typec/bus.c:1: warning: no structured comments found
drivers/usb/typec/bus.c:268: warning: Function parameter or member 'mode' not described in 'typec_match_altmode'
drivers/usb/typec/class.c:1497: warning: Excess function parameter 'drvdata' description in 'typec_port_register_altmode'
drivers/usb/typec/class.c:1: warning: no structured comments found
include/linux/w1.h:281: warning: Function parameter or member 'of_match_table' not described in 'w1_family'
fs/direct-io.c:257: warning: Excess function parameter 'offset' description in 'dio_complete'
fs/file_table.c:1: warning: no structured comments found
fs/libfs.c:477: warning: Excess function parameter 'available' description in 'simple_write_end'
fs/posix_acl.c:646: warning: Function parameter or member 'inode' not described in 'posix_acl_update_mode'
fs/posix_acl.c:646: warning: Function parameter or member 'mode_p' not described in 'posix_acl_update_mode'
fs/posix_acl.c:646: warning: Function parameter or member 'acl' not described in 'posix_acl_update_mode'
drivers/gpu/drm/amd/amdgpu/amdgpu_mn.c:183: warning: Function parameter or member 'blockable' not described in 'amdgpu_mn_read_lock'
drivers/gpu/drm/amd/amdgpu/amdgpu_mn.c:254: warning: Function parameter or member 'blockable' not described in 'amdgpu_mn_invalidate_range_start_gfx'
drivers/gpu/drm/amd/amdgpu/amdgpu_mn.c:302: warning: Function parameter or member 'blockable' not described in 'amdgpu_mn_invalidate_range_start_hsa'
drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c:2986: warning: Excess function parameter 'dev' description in 'amdgpu_vm_get_task_info'
drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c:2987: warning: Function parameter or member 'adev' not described in 'amdgpu_vm_get_task_info'
drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c:2987: warning: Excess function parameter 'dev' description in 'amdgpu_vm_get_task_info'
include/drm/drm_drv.h:610: warning: Function parameter or member 'gem_prime_pin' not described in 'drm_driver'
include/drm/drm_drv.h:610: warning: Function parameter or member 'gem_prime_unpin' not described in 'drm_driver'
include/drm/drm_drv.h:610: warning: Function parameter or member 'gem_prime_res_obj' not described in 'drm_driver'
include/drm/drm_drv.h:610: warning: Function parameter or member 'gem_prime_get_sg_table' not described in 'drm_driver'
include/drm/drm_drv.h:610: warning: Function parameter or member 'gem_prime_import_sg_table' not described in 'drm_driver'
include/drm/drm_drv.h:610: warning: Function parameter or member 'gem_prime_vmap' not described in 'drm_driver'
include/drm/drm_drv.h:610: warning: Function parameter or member 'gem_prime_vunmap' not described in 'drm_driver'
include/drm/drm_drv.h:610: warning: Function parameter or member 'gem_prime_mmap' not described in 'drm_driver'
include/drm/drm_panel.h:98: warning: Function parameter or member 'link' not described in 'drm_panel'
drivers/gpu/drm/i915/i915_vma.h:49: warning: cannot understand function prototype: 'struct i915_vma '
drivers/gpu/drm/i915/i915_vma.h:1: warning: no structured comments found
drivers/gpu/drm/i915/intel_guc_fwif.h:553: warning: cannot understand function prototype: 'struct guc_log_buffer_state '
drivers/gpu/drm/i915/i915_trace.h:1: warning: no structured comments found
include/linux/skbuff.h:860: warning: Function parameter or member 'dev_scratch' not described in 'sk_buff'
include/linux/skbuff.h:860: warning: Function parameter or member 'list' not described in 'sk_buff'
include/linux/skbuff.h:860: warning: Function parameter or member 'ip_defrag_offset' not described in 'sk_buff'
include/linux/skbuff.h:860: warning: Function parameter or member 'skb_mstamp' not described in 'sk_buff'
include/linux/skbuff.h:860: warning: Function parameter or member '__cloned_offset' not described in 'sk_buff'
include/linux/skbuff.h:860: warning: Function parameter or member 'head_frag' not described in 'sk_buff'
include/linux/skbuff.h:860: warning: Function parameter or member '__pkt_type_offset' not described in 'sk_buff'
include/linux/skbuff.h:860: warning: Function parameter or member 'encapsulation' not described in 'sk_buff'
include/linux/skbuff.h:860: warning: Function parameter or member 'encap_hdr_csum' not described in 'sk_buff'
include/linux/skbuff.h:860: warning: Function parameter or member 'csum_valid' not described in 'sk_buff'
include/linux/skbuff.h:860: warning: Function parameter or member 'csum_complete_sw' not described in 'sk_buff'
include/linux/skbuff.h:860: warning: Function parameter or member 'csum_level' not described in 'sk_buff'
include/linux/skbuff.h:860: warning: Function parameter or member 'inner_protocol_type' not described in 'sk_buff'
include/linux/skbuff.h:860: warning: Function parameter or member 'remcsum_offload' not described in 'sk_buff'
include/linux/skbuff.h:860: warning: Function parameter or member 'offload_fwd_mark' not described in 'sk_buff'
include/linux/skbuff.h:860: warning: Function parameter or member 'offload_mr_fwd_mark' not described in 'sk_buff'
include/linux/skbuff.h:860: warning: Function parameter or member 'sender_cpu' not described in 'sk_buff'
include/linux/skbuff.h:860: warning: Function parameter or member 'reserved_tailroom' not described in 'sk_buff'
include/linux/skbuff.h:860: warning: Function parameter or member 'inner_ipproto' not described in 'sk_buff'
include/net/sock.h:238: warning: Function parameter or member 'skc_addrpair' not described in 'sock_common'
include/net/sock.h:238: warning: Function parameter or member 'skc_portpair' not described in 'sock_common'
include/net/sock.h:238: warning: Function parameter or member 'skc_ipv6only' not described in 'sock_common'
include/net/sock.h:238: warning: Function parameter or member 'skc_net_refcnt' not described in 'sock_common'
include/net/sock.h:238: warning: Function parameter or member 'skc_v6_daddr' not described in 'sock_common'
include/net/sock.h:238: warning: Function parameter or member 'skc_v6_rcv_saddr' not described in 'sock_common'
include/net/sock.h:238: warning: Function parameter or member 'skc_cookie' not described in 'sock_common'
include/net/sock.h:238: warning: Function parameter or member 'skc_listener' not described in 'sock_common'
include/net/sock.h:238: warning: Function parameter or member 'skc_tw_dr' not described in 'sock_common'
include/net/sock.h:238: warning: Function parameter or member 'skc_rcv_wnd' not described in 'sock_common'
include/net/sock.h:238: warning: Function parameter or member 'skc_tw_rcv_nxt' not described in 'sock_common'
include/net/sock.h:509: warning: Function parameter or member 'sk_backlog.rmem_alloc' not described in 'sock'
include/net/sock.h:509: warning: Function parameter or member 'sk_backlog.len' not described in 'sock'
include/net/sock.h:509: warning: Function parameter or member 'sk_backlog.head' not described in 'sock'
include/net/sock.h:509: warning: Function parameter or member 'sk_backlog.tail' not described in 'sock'
include/net/sock.h:509: warning: Function parameter or member 'sk_wq_raw' not described in 'sock'
include/net/sock.h:509: warning: Function parameter or member 'tcp_rtx_queue' not described in 'sock'
include/net/sock.h:509: warning: Function parameter or member 'sk_route_forced_caps' not described in 'sock'
include/net/sock.h:509: warning: Function parameter or member 'sk_txtime_report_errors' not described in 'sock'
include/net/sock.h:509: warning: Function parameter or member 'sk_validate_xmit_skb' not described in 'sock'
include/linux/netdevice.h:2018: warning: Function parameter or member 'adj_list.upper' not described in 'net_device'
include/linux/netdevice.h:2018: warning: Function parameter or member 'adj_list.lower' not described in 'net_device'
include/linux/netdevice.h:2018: warning: Function parameter or member 'gso_partial_features' not described in 'net_device'
include/linux/netdevice.h:2018: warning: Function parameter or member 'switchdev_ops' not described in 'net_device'
include/linux/netdevice.h:2018: warning: Function parameter or member 'l3mdev_ops' not described in 'net_device'

# https://github.com/0day-ci/linux/commit/916010a7343fd0931103acedebff6835ed3314f0
git remote add linux-review https://github.com/0day-ci/linux
git remote update linux-review
git checkout 916010a7343fd0931103acedebff6835ed3314f0
vim +2936 drivers/gpio/gpiolib.c

eec1d566 Lukas Wunner 2017-10-12 2919
eec1d566 Lukas Wunner 2017-10-12 2920 /**
eec1d566 Lukas Wunner 2017-10-12 2921 * gpiod_get_array_value() - read values from an array of GPIOs
916010a7 Janusz Krzysztofik 2018-09-02 2922 * @array_size: number of elements in the descriptor array / value bitmap
eec1d566 Lukas Wunner 2017-10-12 2923 * @desc_array: array of GPIO descriptors whose values will be read
916010a7 Janusz Krzysztofik 2018-09-02 2924 * @value_bitnap: bitmap to store the read values
eec1d566 Lukas Wunner 2017-10-12 2925 *
eec1d566 Lukas Wunner 2017-10-12 2926 * Read the logical values of the GPIOs, i.e. taking their ACTIVE_LOW status
eec1d566 Lukas Wunner 2017-10-12 2927 * into account. Return 0 in case of success, else an error code.
eec1d566 Lukas Wunner 2017-10-12 2928 *
eec1d566 Lukas Wunner 2017-10-12 2929 * This function should be called from contexts where we cannot sleep,
eec1d566 Lukas Wunner 2017-10-12 2930 * and it will complain if the GPIO chip functions potentially sleep.
eec1d566 Lukas Wunner 2017-10-12 2931 */
eec1d566 Lukas Wunner 2017-10-12 2932 int gpiod_get_array_value(unsigned int array_size,
916010a7 Janusz Krzysztofik 2018-09-02 2933 struct gpio_desc **desc_array,
916010a7 Janusz Krzysztofik 2018-09-02 2934 unsigned long *value_bitmap)
eec1d566 Lukas Wunner 2017-10-12 2935 {
eec1d566 Lukas Wunner 2017-10-12 @2936 if (!desc_array)
eec1d566 Lukas Wunner 2017-10-12 2937 return -EINVAL;
eec1d566 Lukas Wunner 2017-10-12 2938 return gpiod_get_array_value_complex(false, false, array_size,
916010a7 Janusz Krzysztofik 2018-09-02 2939 desc_array, value_bitmap);
eec1d566 Lukas Wunner 2017-10-12 2940 }
eec1d566 Lukas Wunner 2017-10-12 2941 EXPORT_SYMBOL_GPL(gpiod_get_array_value);
eec1d566 Lukas Wunner 2017-10-12 2942

:::::: The code at line 2936 was first introduced by commit
:::::: eec1d566cdf94b57e8f5ba9fe60eea214929bcfc gpio: Introduce ->get_multiple callback

:::::: TO: Lukas Wunner <[email protected]>
:::::: CC: Linus Walleij <[email protected]>

---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation


Attachments:
(No filename) (23.44 kB)
.config.gz (6.43 kB)
Download all attachments

2018-09-05 06:49:17

by kernel test robot

[permalink] [raw]
Subject: Re: [PATCH v7 1/4] gpiolib: Pass bitmaps, not integer arrays, to get/set array

Hi Janusz,

Thank you for the patch! Yet something to improve:

[auto build test ERROR on gpio/for-next]
[also build test ERROR on v4.19-rc2 next-20180905]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url: https://github.com/0day-ci/linux/commits/Janusz-Krzysztofik/gpiolib-Pass-bitmaps-not-integer-arrays-to-get-set-array/20180903-172834
base: https://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-gpio.git for-next
config: x86_64-randconfig-f2-201835 (attached as .config)
compiler: gcc-6 (Debian 6.4.0-9) 6.4.0 20171026
reproduce:
# save the attached .config to linux build tree
make ARCH=x86_64

All errors (new ones prefixed by >>):

drivers/phy/motorola/phy-mapphone-mdm6600.c: In function 'phy_mdm6600_cmd':
>> drivers/phy/motorola/phy-mapphone-mdm6600.c:165:36: error: passing argument 3 of 'gpiod_set_array_value_cansleep' from incompatible pointer type [-Werror=incompatible-pointer-types]
ddata->cmd_gpios->desc, values);
^~~~~~
In file included from drivers/phy/motorola/phy-mapphone-mdm6600.c:16:0:
include/linux/gpio/consumer.h:400:20: note: expected 'int *' but argument is of type 'long unsigned int *'
static inline void gpiod_set_array_value_cansleep(unsigned int array_size,
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
drivers/phy/motorola/phy-mapphone-mdm6600.c: In function 'phy_mdm6600_status':
>> drivers/phy/motorola/phy-mapphone-mdm6600.c:184:13: error: passing argument 3 of 'gpiod_get_array_value_cansleep' from incompatible pointer type [-Werror=incompatible-pointer-types]
values);
^~~~~~
In file included from drivers/phy/motorola/phy-mapphone-mdm6600.c:16:0:
include/linux/gpio/consumer.h:387:19: note: expected 'int *' but argument is of type 'long unsigned int *'
static inline int gpiod_get_array_value_cansleep(unsigned int array_size,
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
cc1: some warnings being treated as errors

vim +/gpiod_set_array_value_cansleep +165 drivers/phy/motorola/phy-mapphone-mdm6600.c

5d1ebbda0 Tony Lindgren 2018-03-08 151
5d1ebbda0 Tony Lindgren 2018-03-08 152 /**
5d1ebbda0 Tony Lindgren 2018-03-08 153 * phy_mdm6600_cmd() - send a command request to mdm6600
5d1ebbda0 Tony Lindgren 2018-03-08 154 * @ddata: device driver data
5d1ebbda0 Tony Lindgren 2018-03-08 155 *
5d1ebbda0 Tony Lindgren 2018-03-08 156 * Configures the three command request GPIOs to the specified value.
5d1ebbda0 Tony Lindgren 2018-03-08 157 */
5d1ebbda0 Tony Lindgren 2018-03-08 158 static void phy_mdm6600_cmd(struct phy_mdm6600 *ddata, int val)
5d1ebbda0 Tony Lindgren 2018-03-08 159 {
916010a73 Janusz Krzysztofik 2018-09-02 160 DECLARE_BITMAP(values, PHY_MDM6600_NR_CMD_LINES);
5d1ebbda0 Tony Lindgren 2018-03-08 161
916010a73 Janusz Krzysztofik 2018-09-02 162 *values = val;
5d1ebbda0 Tony Lindgren 2018-03-08 163
5d1ebbda0 Tony Lindgren 2018-03-08 164 gpiod_set_array_value_cansleep(PHY_MDM6600_NR_CMD_LINES,
5d1ebbda0 Tony Lindgren 2018-03-08 @165 ddata->cmd_gpios->desc, values);
5d1ebbda0 Tony Lindgren 2018-03-08 166 }
5d1ebbda0 Tony Lindgren 2018-03-08 167
5d1ebbda0 Tony Lindgren 2018-03-08 168 /**
5d1ebbda0 Tony Lindgren 2018-03-08 169 * phy_mdm6600_status() - read mdm6600 status lines
5d1ebbda0 Tony Lindgren 2018-03-08 170 * @ddata: device driver data
5d1ebbda0 Tony Lindgren 2018-03-08 171 */
5d1ebbda0 Tony Lindgren 2018-03-08 172 static void phy_mdm6600_status(struct work_struct *work)
5d1ebbda0 Tony Lindgren 2018-03-08 173 {
5d1ebbda0 Tony Lindgren 2018-03-08 174 struct phy_mdm6600 *ddata;
5d1ebbda0 Tony Lindgren 2018-03-08 175 struct device *dev;
916010a73 Janusz Krzysztofik 2018-09-02 176 DECLARE_BITMAP(values, PHY_MDM6600_NR_STATUS_LINES);
5d1ebbda0 Tony Lindgren 2018-03-08 177 int error, i, val = 0;
5d1ebbda0 Tony Lindgren 2018-03-08 178
5d1ebbda0 Tony Lindgren 2018-03-08 179 ddata = container_of(work, struct phy_mdm6600, status_work.work);
5d1ebbda0 Tony Lindgren 2018-03-08 180 dev = ddata->dev;
5d1ebbda0 Tony Lindgren 2018-03-08 181
ad5003300 Tony Lindgren 2018-05-31 182 error = gpiod_get_array_value_cansleep(PHY_MDM6600_NR_STATUS_LINES,
5d1ebbda0 Tony Lindgren 2018-03-08 183 ddata->status_gpios->desc,
5d1ebbda0 Tony Lindgren 2018-03-08 @184 values);
5d1ebbda0 Tony Lindgren 2018-03-08 185 if (error)
5d1ebbda0 Tony Lindgren 2018-03-08 186 return;
5d1ebbda0 Tony Lindgren 2018-03-08 187
ad5003300 Tony Lindgren 2018-05-31 188 for (i = 0; i < PHY_MDM6600_NR_STATUS_LINES; i++) {
916010a73 Janusz Krzysztofik 2018-09-02 189 val |= test_bit(i, values) << i;
5d1ebbda0 Tony Lindgren 2018-03-08 190 dev_dbg(ddata->dev, "XXX %s: i: %i values[i]: %i val: %i\n",
916010a73 Janusz Krzysztofik 2018-09-02 191 __func__, i, test_bit(i, values), val);
5d1ebbda0 Tony Lindgren 2018-03-08 192 }
5d1ebbda0 Tony Lindgren 2018-03-08 193 ddata->status = val;
5d1ebbda0 Tony Lindgren 2018-03-08 194
5d1ebbda0 Tony Lindgren 2018-03-08 195 dev_info(dev, "modem status: %i %s\n",
5d1ebbda0 Tony Lindgren 2018-03-08 196 ddata->status,
5d1ebbda0 Tony Lindgren 2018-03-08 197 phy_mdm6600_status_name[ddata->status & 7]);
5d1ebbda0 Tony Lindgren 2018-03-08 198 complete(&ddata->ack);
5d1ebbda0 Tony Lindgren 2018-03-08 199 }
5d1ebbda0 Tony Lindgren 2018-03-08 200

:::::: The code at line 165 was first introduced by commit
:::::: 5d1ebbda0318b1ba55eaa1fae3fd867af17b0774 phy: mapphone-mdm6600: Add USB PHY driver for MDM6600 on Droid 4

:::::: TO: Tony Lindgren <[email protected]>
:::::: CC: Kishon Vijay Abraham I <[email protected]>

---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation


Attachments:
(No filename) (6.12 kB)
.config.gz (29.89 kB)
Download all attachments

2018-09-05 06:50:06

by Miquel Raynal

[permalink] [raw]
Subject: Re: [PATCH v4 0/3] mtd: rawnand: ams-delta: Cleanups and optimizations

Hi Janusz,

Janusz Krzysztofik <[email protected]> wrote on Mon, 20 Aug 2018
23:39:01 +0200:

> This series consist of possibly ready to apply patches extracted from
> a former one titled "mtd: rawnand: ams-delta: Use GPIO API for data I/O".
> Remaining patches implementing conversion of data I/O to GPIO have been
> postponed until gpiolib is optimized to ensure sufficient performance.
>
>
> Janusz Krzysztofik (3):
> mtd: rawnand: ams-delta: show parent device in sysfs
> mtd: rawnand: ams-delta: Use private structure
> mtd: rawnand: ams-delta: Set port direction when needed

Patch 2/3 does not apply on nand/next. Indeed the driver does not look
the same as in the diff. I don't see any changes on my side that could
explain this so perhaps you could rebase on top of 4.19-rc2 (or
nand/next, as you wish) and resend the series?

Thanks,
Miquèl

2018-09-05 07:14:08

by kernel test robot

[permalink] [raw]
Subject: Re: [PATCH v7 3/4] gpiolib: Pass array info to get/set array functions

Hi Janusz,

Thank you for the patch! Yet something to improve:

[auto build test ERROR on gpio/for-next]
[also build test ERROR on v4.19-rc2 next-20180905]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url: https://github.com/0day-ci/linux/commits/Janusz-Krzysztofik/gpiolib-Pass-bitmaps-not-integer-arrays-to-get-set-array/20180903-172834
base: https://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-gpio.git for-next
config: x86_64-randconfig-f2-201835 (attached as .config)
compiler: gcc-6 (Debian 6.4.0-9) 6.4.0 20171026
reproduce:
# save the attached .config to linux build tree
make ARCH=x86_64

All errors (new ones prefixed by >>):

drivers/phy/motorola/phy-mapphone-mdm6600.c: In function 'phy_mdm6600_cmd':
drivers/phy/motorola/phy-mapphone-mdm6600.c:166:12: error: passing argument 3 of 'gpiod_set_array_value_cansleep' from incompatible pointer type [-Werror=incompatible-pointer-types]
ddata->cmd_gpios->info, values);
^~~~~
In file included from drivers/phy/motorola/phy-mapphone-mdm6600.c:16:0:
include/linux/gpio/consumer.h:417:20: note: expected 'int *' but argument is of type 'struct gpio_array *'
static inline void gpiod_set_array_value_cansleep(unsigned int array_size,
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
>> drivers/phy/motorola/phy-mapphone-mdm6600.c:164:2: error: too many arguments to function 'gpiod_set_array_value_cansleep'
gpiod_set_array_value_cansleep(PHY_MDM6600_NR_CMD_LINES,
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In file included from drivers/phy/motorola/phy-mapphone-mdm6600.c:16:0:
include/linux/gpio/consumer.h:417:20: note: declared here
static inline void gpiod_set_array_value_cansleep(unsigned int array_size,
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
drivers/phy/motorola/phy-mapphone-mdm6600.c: In function 'phy_mdm6600_status':
drivers/phy/motorola/phy-mapphone-mdm6600.c:185:13: error: passing argument 3 of 'gpiod_get_array_value_cansleep' from incompatible pointer type [-Werror=incompatible-pointer-types]
ddata->status_gpios->info,
^~~~~
In file included from drivers/phy/motorola/phy-mapphone-mdm6600.c:16:0:
include/linux/gpio/consumer.h:404:19: note: expected 'int *' but argument is of type 'struct gpio_array *'
static inline int gpiod_get_array_value_cansleep(unsigned int array_size,
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
>> drivers/phy/motorola/phy-mapphone-mdm6600.c:183:10: error: too many arguments to function 'gpiod_get_array_value_cansleep'
error = gpiod_get_array_value_cansleep(PHY_MDM6600_NR_STATUS_LINES,
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In file included from drivers/phy/motorola/phy-mapphone-mdm6600.c:16:0:
include/linux/gpio/consumer.h:404:19: note: declared here
static inline int gpiod_get_array_value_cansleep(unsigned int array_size,
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
cc1: some warnings being treated as errors

vim +/gpiod_set_array_value_cansleep +164 drivers/phy/motorola/phy-mapphone-mdm6600.c

5d1ebbda0 Tony Lindgren 2018-03-08 151
5d1ebbda0 Tony Lindgren 2018-03-08 152 /**
5d1ebbda0 Tony Lindgren 2018-03-08 153 * phy_mdm6600_cmd() - send a command request to mdm6600
5d1ebbda0 Tony Lindgren 2018-03-08 154 * @ddata: device driver data
5d1ebbda0 Tony Lindgren 2018-03-08 155 *
5d1ebbda0 Tony Lindgren 2018-03-08 156 * Configures the three command request GPIOs to the specified value.
5d1ebbda0 Tony Lindgren 2018-03-08 157 */
5d1ebbda0 Tony Lindgren 2018-03-08 158 static void phy_mdm6600_cmd(struct phy_mdm6600 *ddata, int val)
5d1ebbda0 Tony Lindgren 2018-03-08 159 {
916010a73 Janusz Krzysztofik 2018-09-02 160 DECLARE_BITMAP(values, PHY_MDM6600_NR_CMD_LINES);
5d1ebbda0 Tony Lindgren 2018-03-08 161
916010a73 Janusz Krzysztofik 2018-09-02 162 *values = val;
5d1ebbda0 Tony Lindgren 2018-03-08 163
5d1ebbda0 Tony Lindgren 2018-03-08 @164 gpiod_set_array_value_cansleep(PHY_MDM6600_NR_CMD_LINES,
dea6937cb Janusz Krzysztofik 2018-09-02 165 ddata->cmd_gpios->desc,
dea6937cb Janusz Krzysztofik 2018-09-02 @166 ddata->cmd_gpios->info, values);
5d1ebbda0 Tony Lindgren 2018-03-08 167 }
5d1ebbda0 Tony Lindgren 2018-03-08 168
5d1ebbda0 Tony Lindgren 2018-03-08 169 /**
5d1ebbda0 Tony Lindgren 2018-03-08 170 * phy_mdm6600_status() - read mdm6600 status lines
5d1ebbda0 Tony Lindgren 2018-03-08 171 * @ddata: device driver data
5d1ebbda0 Tony Lindgren 2018-03-08 172 */
5d1ebbda0 Tony Lindgren 2018-03-08 173 static void phy_mdm6600_status(struct work_struct *work)
5d1ebbda0 Tony Lindgren 2018-03-08 174 {
5d1ebbda0 Tony Lindgren 2018-03-08 175 struct phy_mdm6600 *ddata;
5d1ebbda0 Tony Lindgren 2018-03-08 176 struct device *dev;
916010a73 Janusz Krzysztofik 2018-09-02 177 DECLARE_BITMAP(values, PHY_MDM6600_NR_STATUS_LINES);
5d1ebbda0 Tony Lindgren 2018-03-08 178 int error, i, val = 0;
5d1ebbda0 Tony Lindgren 2018-03-08 179
5d1ebbda0 Tony Lindgren 2018-03-08 180 ddata = container_of(work, struct phy_mdm6600, status_work.work);
5d1ebbda0 Tony Lindgren 2018-03-08 181 dev = ddata->dev;
5d1ebbda0 Tony Lindgren 2018-03-08 182
ad5003300 Tony Lindgren 2018-05-31 @183 error = gpiod_get_array_value_cansleep(PHY_MDM6600_NR_STATUS_LINES,
5d1ebbda0 Tony Lindgren 2018-03-08 184 ddata->status_gpios->desc,
dea6937cb Janusz Krzysztofik 2018-09-02 @185 ddata->status_gpios->info,
5d1ebbda0 Tony Lindgren 2018-03-08 186 values);
5d1ebbda0 Tony Lindgren 2018-03-08 187 if (error)
5d1ebbda0 Tony Lindgren 2018-03-08 188 return;
5d1ebbda0 Tony Lindgren 2018-03-08 189
ad5003300 Tony Lindgren 2018-05-31 190 for (i = 0; i < PHY_MDM6600_NR_STATUS_LINES; i++) {
916010a73 Janusz Krzysztofik 2018-09-02 191 val |= test_bit(i, values) << i;
5d1ebbda0 Tony Lindgren 2018-03-08 192 dev_dbg(ddata->dev, "XXX %s: i: %i values[i]: %i val: %i\n",
916010a73 Janusz Krzysztofik 2018-09-02 193 __func__, i, test_bit(i, values), val);
5d1ebbda0 Tony Lindgren 2018-03-08 194 }
5d1ebbda0 Tony Lindgren 2018-03-08 195 ddata->status = val;
5d1ebbda0 Tony Lindgren 2018-03-08 196
5d1ebbda0 Tony Lindgren 2018-03-08 197 dev_info(dev, "modem status: %i %s\n",
5d1ebbda0 Tony Lindgren 2018-03-08 198 ddata->status,
5d1ebbda0 Tony Lindgren 2018-03-08 199 phy_mdm6600_status_name[ddata->status & 7]);
5d1ebbda0 Tony Lindgren 2018-03-08 200 complete(&ddata->ack);
5d1ebbda0 Tony Lindgren 2018-03-08 201 }
5d1ebbda0 Tony Lindgren 2018-03-08 202

:::::: The code at line 164 was first introduced by commit
:::::: 5d1ebbda0318b1ba55eaa1fae3fd867af17b0774 phy: mapphone-mdm6600: Add USB PHY driver for MDM6600 on Droid 4

:::::: TO: Tony Lindgren <[email protected]>
:::::: CC: Kishon Vijay Abraham I <[email protected]>

---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation


Attachments:
(No filename) (7.27 kB)
.config.gz (29.89 kB)
Download all attachments

2018-09-05 20:54:38

by Janusz Krzysztofik

[permalink] [raw]
Subject: Re: [PATCH v4 0/3] mtd: rawnand: ams-delta: Cleanups and optimizations

Hi Miquel, Tony,

On Wednesday, September 5, 2018 8:47:57 AM CEST Miquel Raynal wrote:
> Hi Janusz,
>
> Janusz Krzysztofik <[email protected]> wrote on Mon, 20 Aug 2018
> 23:39:01 +0200:
>
> > This series consist of possibly ready to apply patches extracted from
> > a former one titled "mtd: rawnand: ams-delta: Use GPIO API for data I/O".
> > Remaining patches implementing conversion of data I/O to GPIO have been
> > postponed until gpiolib is optimized to ensure sufficient performance.
> >
> >
> > Janusz Krzysztofik (3):
> > mtd: rawnand: ams-delta: show parent device in sysfs
> > mtd: rawnand: ams-delta: Use private structure
> > mtd: rawnand: ams-delta: Set port direction when needed
>
> Patch 2/3 does not apply on nand/next. Indeed the driver does not look
> the same as in the diff.

That's because I built it on top of my former series from the mid of July,
containing "[PATCH v2 2/3 v4] mtd: rawnand: ams-delta: use GPIO lookup table".
It was acked by you, Miquel, and supposed to be merged via linux-omap tree.

> I don't see any changes on my side that could
> explain this so perhaps you could rebase on top of 4.19-rc2 (or
> nand/next, as you wish) and resend the series?

As far as I can see, Tony hasn't applied that series yet, so maybe I can still
move that patch out of there and insert it into this series in front of the
other 3 patches and resend. That would however make patch 3/3 of that old
series depend on this one.

Tony, what do you think?

Thanks,
Janusz




2018-09-05 21:50:42

by Janusz Krzysztofik

[permalink] [raw]
Subject: [PATCH v8 0/4] gpiolib: speed up GPIO array processing


The goal is to boost performance of get/set array functions while
processing GPIO arrays which represent pins of a signle chip in
hardware order. If resulting performance is close to PIO, GPIO API
can be used for data I/O without much loss of speed.

Created and tested on a low end Amstrad Delta board with NAND driver
updated to use GPIO API for data I/O. Performance degrade compared to
PIO is much better than before the optimization though not quite
satisfactory on my test hardware.


Janusz Krzysztofik (4):
gpiolib: Pass bitmaps, not integer arrays, to get/set array
gpiolib: Identify arrays matching GPIO hardware
gpiolib: Pass array info to get/set array functions
gpiolib: Implement fast processing path in get/set array


Changelog:
v8:
[PATCH v8 1/4] gpiolib: Pass bitmaps, not integer arrays, to get/set array:
- replace *values with values[0] where applicable - suggessted by Geert
Uytterhoeven, thanks,
- drop !! from 3rd argument of __assign_bit() where applicable - suggested
by Lukas Wunner, thanks,
drivers/gpio/gpiolib.c:
- s/bitnap/bitmap/ - catched by Lukas Wunner, thanks,
drivers/phy/motorola/phy-mapphone-mdm6600.c:
- assign ddata->status directly from bitmap, not an intermediate 'val'
variable, now used only for debugging purposes,
include/linux/gpio/consumer.h:
- also update API of inline function replacements used if CONFIG_GPIOLIB
is not defined - catched by kbuild test robot,
[PATCH v8 3/4] gpiolib: Pass array info to get/set array functions:
commit message:
- s/bulids/builds/ - catched by Geert Uytterhoeven, thanks,
drivers/gpio/gpiolib.c:
- add information on array_info arguments to array function descriptions -
catched by kbuild test robot,
include/linux/gpio/consumer.h:
- also update API of inline function replacements used if CONFIG_GPIOLIB
is not defined - catched by kbuild test robot.

v7:
- add more people to Cc: - authors and/or those who contributed most to
the drivers in scope of the change,
[PATCH v7 1/4] gpiolib: Pass bitmaps, not integer arrays, to get/set:
- avoid VLAs, use data source type bit number as bitmap size if not
constant - great recommendation by Peter Rosin, thanks,
- revert names of local variables declared with DECLARE_BITMAP() from
'value_bitmap' to original names of value arrays they replace (but not
'value_array') - inspired by Peter Rosin suggestion - thanks!
drivers/gpio/gpio-max3191x.c:
- use bitmap_alloc() to be more consistent with DECLARE_BITMAP() pattern
used by other consumers,
drivers/phy/motorola/phy-mapphone-mdm6600.c:
- no need to mask unused bits of val before its assignment to bitmap,
passing PHY_MDM6600_NR_CMD_LINES to gpiod_set_array_value() as array/
bitmap size provides sufficient protection.

v6:
[PATCH v6 1/4] gpiolib: Pass bitmaps, not integer arrays, to get/set
- use DECLARE_BITMAP() macro for declaring value_bitmap - great idea by
David Laight, thanks!
drivers/auxdisplay/hd44780.c:
- simplify the code and adjust comments as recommended by Geert
Uytterhoeven - thanks!,
drivers/i2c/muxes/i2c-mux-gpio.c:
- drop .values member of struct gpiomux - details provided by Peter
Rosin, thanks!,
drivers/mux/gpio.c:
- drop .val member of struct mux_gpio - details provided by Peter
Rosin, thanks!,
drivers/net/phy/mdio-mux-gpio.c:
- drop .values member of struct mdio_mux_gpio_state and its processsing.

v5:
[PATCH v5 1/4] gpiolib: Pass bitmaps, not integer arrays, to get/set
- drivers/i2c/muxes/i2c-mux-gpio.c:
- drop assigment of values to struct gpiomux.values, as recommended
by Peter Rosin - thanks!,
- mark the .values member of the structure as obsolete,
- drivers/mux/gpio.c:
- drop assigment of values to struct mux_gpio.val, also recommended
by Peter Rosin - thanks!,
- merk the .val member of the structure as obsolete,
- drivers/auxdisplay/hd44780.c:
- fix incorrect bitmap size,
- use >>= operator to simplify notation,
both catched by Miguel Ojeda - thanks!,
- add Cc: clauses as well as Acked-by: collected so far.
[PATCH v5 2/4] gpiolib: Identify arrays matching GPIO hardware
- add Cc: clause.
[PATCH v5 3/4] gpiolib: Pass array info to get/set array functions
- add Cc: clauses as well as Acked-by: collected so far.
[PATCH v5 4/4] gpiolib: Implement fast processing path in get/set
- add Cc: clause.

v4:
That series was a follow up of the former "mtd: rawnand: ams-delta: Use
gpio-omap accessors for data I/O" which already contained some changes
to gpiolib. Those previous attempts were commented by Borris Brezillon
who suggested using GPIO API modified to accept bitmaps, and by Linus
Walleij who suggested still more great ideas for further immprovement
of the proposed API changes - thanks!


diffstat:
Documentation/driver-api/gpio/board.rst | 15 +
Documentation/driver-api/gpio/consumer.rst | 48 +++-
drivers/auxdisplay/hd44780.c | 67 ++----
drivers/bus/ts-nbus.c | 20 -
drivers/gpio/gpio-max3191x.c | 16 -
drivers/gpio/gpiolib.c | 281 ++++++++++++++++++++++------
drivers/gpio/gpiolib.h | 15 +
drivers/i2c/muxes/i2c-mux-gpio.c | 16 -
drivers/mmc/core/pwrseq_simple.c | 15 -
drivers/mux/gpio.c | 16 -
drivers/net/phy/mdio-mux-gpio.c | 13 -
drivers/pcmcia/soc_common.c | 9
drivers/phy/motorola/phy-mapphone-mdm6600.c | 19 -
drivers/staging/iio/adc/ad7606.c | 12 -
drivers/tty/serial/serial_mctrl_gpio.c | 9
include/linux/gpio/consumer.h | 59 ++++-
16 files changed, 420 insertions(+), 210 deletions(-)

2018-09-05 21:51:02

by Janusz Krzysztofik

[permalink] [raw]
Subject: [PATCH v8 4/4] gpiolib: Implement fast processing path in get/set array

Certain GPIO descriptor arrays returned by gpio_get_array() may contain
information on direct mapping of array members to pins of a single GPIO
chip in hardware order. In such cases, bitmaps of values can be passed
directly from/to the chip's .get/set_multiple() callbacks without
wasting time on iterations.

Add respective code to gpiod_get/set_array_bitmap_complex() functions.
Pins not applicable for fast path are processed as before, skipping
over the 'fast' ones.

Cc: Jonathan Corbet <[email protected]>
Signed-off-by: Janusz Krzysztofik <[email protected]>
---
Documentation/driver-api/gpio/board.rst | 15 ++++++
Documentation/driver-api/gpio/consumer.rst | 8 +++
drivers/gpio/gpiolib.c | 87 ++++++++++++++++++++++++++++--
3 files changed, 105 insertions(+), 5 deletions(-)

diff --git a/Documentation/driver-api/gpio/board.rst b/Documentation/driver-api/gpio/board.rst
index 2c112553df84..c66821e033c2 100644
--- a/Documentation/driver-api/gpio/board.rst
+++ b/Documentation/driver-api/gpio/board.rst
@@ -193,3 +193,18 @@ And the table can be added to the board code as follows::

The line will be hogged as soon as the gpiochip is created or - in case the
chip was created earlier - when the hog table is registered.
+
+Arrays of pins
+--------------
+In addition to requesting pins belonging to a function one by one, a device may
+also request an array of pins assigned to the function. The way those pins are
+mapped to the device determines if the array qualifies for fast bitmap
+processing. If yes, a bitmap is passed over get/set array functions directly
+between a caller and a respective .get/set_multiple() callback of a GPIO chip.
+
+In order to qualify for fast bitmap processing, the pin mapping must meet the
+following requirements:
+- it must belong to the same chip as other 'fast' pins of the function,
+- its index within the function must match its hardware number within the chip.
+
+Open drain and open source pins are excluded from fast bitmap output processing.
diff --git a/Documentation/driver-api/gpio/consumer.rst b/Documentation/driver-api/gpio/consumer.rst
index 0afd95a12b10..cf992e5ab976 100644
--- a/Documentation/driver-api/gpio/consumer.rst
+++ b/Documentation/driver-api/gpio/consumer.rst
@@ -388,6 +388,14 @@ array_info should be set to NULL.
Note that for optimal performance GPIOs belonging to the same chip should be
contiguous within the array of descriptors.

+Still better performance may be achieved if array indexes of the descriptors
+match hardware pin numbers of a single chip. If an array passed to a get/set
+array function matches the one obtained from gpiod_get_array() and array_info
+associated with the array is also passed, the function may take a fast bitmap
+processing path, passing the value_bitmap argument directly to the respective
+.get/set_multiple() callback of the chip. That allows for utilization of GPIO
+banks as data I/O ports without much loss of performance.
+
The return value of gpiod_get_array_value() and its variants is 0 on success
or negative on error. Note the difference to gpiod_get_value(), which returns
0 or 1 on success to convey the GPIO value. With the array functions, the GPIO
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index cd7c1deed132..d7532aa6c0bc 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -2789,7 +2789,36 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep,
struct gpio_array *array_info,
unsigned long *value_bitmap)
{
- int i = 0;
+ int err, i = 0;
+
+ /*
+ * Validate array_info against desc_array and its size.
+ * It should immediately follow desc_array if both
+ * have been obtained from the same gpiod_get_array() call.
+ */
+ if (array_info && array_info->desc == desc_array &&
+ array_size <= array_info->size &&
+ (void *)array_info == desc_array + array_info->size) {
+ if (!can_sleep)
+ WARN_ON(array_info->chip->can_sleep);
+
+ err = gpio_chip_get_multiple(array_info->chip,
+ array_info->get_mask,
+ value_bitmap);
+ if (err)
+ return err;
+
+ if (!raw && !bitmap_empty(array_info->invert_mask, array_size))
+ bitmap_xor(value_bitmap, value_bitmap,
+ array_info->invert_mask, array_size);
+
+ if (bitmap_full(array_info->get_mask, array_size))
+ return 0;
+
+ i = find_first_zero_bit(array_info->get_mask, array_size);
+ } else {
+ array_info = NULL;
+ }

while (i < array_size) {
struct gpio_chip *chip = desc_array[i]->gdev->chip;
@@ -2820,7 +2849,12 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep,
int hwgpio = gpio_chip_hwgpio(desc);

__set_bit(hwgpio, mask);
- i++;
+
+ if (array_info)
+ find_next_zero_bit(array_info->get_mask,
+ array_size, i);
+ else
+ i++;
} while ((i < array_size) &&
(desc_array[i]->gdev->chip == chip));

@@ -2831,7 +2865,7 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep,
return ret;
}

- for (j = first; j < i; j++) {
+ for (j = first; j < i; ) {
const struct gpio_desc *desc = desc_array[j];
int hwgpio = gpio_chip_hwgpio(desc);
int value = test_bit(hwgpio, bits);
@@ -2840,6 +2874,11 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep,
value = !value;
__assign_bit(j, value_bitmap, value);
trace_gpio_value(desc_to_gpio(desc), 1, value);
+
+ if (array_info)
+ find_next_zero_bit(array_info->get_mask, i, j);
+ else
+ j++;
}

if (mask != fastpath)
@@ -3043,6 +3082,32 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep,
{
int i = 0;

+ /*
+ * Validate array_info against desc_array and its size.
+ * It should immediately follow desc_array if both
+ * have been obtained from the same gpiod_get_array() call.
+ */
+ if (array_info && array_info->desc == desc_array &&
+ array_size <= array_info->size &&
+ (void *)array_info == desc_array + array_info->size) {
+ if (!can_sleep)
+ WARN_ON(array_info->chip->can_sleep);
+
+ if (!raw && !bitmap_empty(array_info->invert_mask, array_size))
+ bitmap_xor(value_bitmap, value_bitmap,
+ array_info->invert_mask, array_size);
+
+ gpio_chip_set_multiple(array_info->chip, array_info->set_mask,
+ value_bitmap);
+
+ if (bitmap_full(array_info->set_mask, array_size))
+ return 0;
+
+ i = find_first_zero_bit(array_info->set_mask, array_size);
+ } else {
+ array_info = NULL;
+ }
+
while (i < array_size) {
struct gpio_chip *chip = desc_array[i]->gdev->chip;
unsigned long fastpath[2 * BITS_TO_LONGS(FASTPATH_NGPIO)];
@@ -3070,7 +3135,14 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep,
int hwgpio = gpio_chip_hwgpio(desc);
int value = test_bit(i, value_bitmap);

- if (!raw && test_bit(FLAG_ACTIVE_LOW, &desc->flags))
+ /*
+ * Pins applicable for fast input but not for
+ * fast output processing may have been already
+ * inverted inside the fast path, skip them.
+ */
+ if (!raw && !(array_info &&
+ test_bit(i, array_info->invert_mask)) &&
+ test_bit(FLAG_ACTIVE_LOW, &desc->flags))
value = !value;
trace_gpio_value(desc_to_gpio(desc), 0, value);
/*
@@ -3089,7 +3161,12 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep,
__clear_bit(hwgpio, bits);
count++;
}
- i++;
+
+ if (array_info)
+ find_next_zero_bit(array_info->set_mask,
+ array_size, i);
+ else
+ i++;
} while ((i < array_size) &&
(desc_array[i]->gdev->chip == chip));
/* push collected bits to outputs */
--
2.16.4


2018-09-05 21:51:39

by Janusz Krzysztofik

[permalink] [raw]
Subject: [PATCH v8 2/4] gpiolib: Identify arrays matching GPIO hardware

Certain GPIO array lookup results may map directly to GPIO pins of a
single GPIO chip in hardware order. If that condition is recognized
and handled efficiently, significant performance gain of get/set array
functions may be possible.

While processing a request for an array of GPIO descriptors, identify
those which represent corresponding pins of a single GPIO chip. Skip
over pins which require open source or open drain special processing.
Moreover, identify pins which require inversion. Pass a pointer to
that information with the array to the caller so it can benefit from
enhanced performance as soon as get/set array functions can accept and
make efficient use of it.

Cc: Jonathan Corbet <[email protected]>
Signed-off-by: Janusz Krzysztofik <[email protected]>
---
Documentation/driver-api/gpio/consumer.rst | 4 +-
drivers/gpio/gpiolib.c | 72 +++++++++++++++++++++++++++++-
drivers/gpio/gpiolib.h | 9 ++++
include/linux/gpio/consumer.h | 9 ++++
4 files changed, 92 insertions(+), 2 deletions(-)

diff --git a/Documentation/driver-api/gpio/consumer.rst b/Documentation/driver-api/gpio/consumer.rst
index ed68042ddccf..7e0298b9a7b9 100644
--- a/Documentation/driver-api/gpio/consumer.rst
+++ b/Documentation/driver-api/gpio/consumer.rst
@@ -109,9 +109,11 @@ For a function using multiple GPIOs all of those can be obtained with one call::
enum gpiod_flags flags)

This function returns a struct gpio_descs which contains an array of
-descriptors::
+descriptors. It also contains a pointer to a gpiolib private structure which,
+if passed back to get/set array functions, may speed up I/O proocessing::

struct gpio_descs {
+ struct gpio_array *info;
unsigned int ndescs;
struct gpio_desc *desc[];
}
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index b66b2191c5c5..141f39308a53 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -4174,7 +4174,9 @@ struct gpio_descs *__must_check gpiod_get_array(struct device *dev,
{
struct gpio_desc *desc;
struct gpio_descs *descs;
- int count;
+ struct gpio_array *array_info = NULL;
+ struct gpio_chip *chip;
+ int count, bitmap_size;

count = gpiod_count(dev, con_id);
if (count < 0)
@@ -4190,9 +4192,77 @@ struct gpio_descs *__must_check gpiod_get_array(struct device *dev,
gpiod_put_array(descs);
return ERR_CAST(desc);
}
+
descs->desc[descs->ndescs] = desc;
+
+ chip = gpiod_to_chip(desc);
+ /*
+ * Select a chip of first array member
+ * whose index matches its pin hardware number
+ * as a candidate for fast bitmap processing.
+ */
+ if (!array_info && gpio_chip_hwgpio(desc) == descs->ndescs) {
+ struct gpio_descs *array;
+
+ bitmap_size = BITS_TO_LONGS(chip->ngpio > count ?
+ chip->ngpio : count);
+
+ array = kzalloc(struct_size(descs, desc, count) +
+ struct_size(array_info, invert_mask,
+ 3 * bitmap_size), GFP_KERNEL);
+ if (!array) {
+ gpiod_put_array(descs);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ memcpy(array, descs,
+ struct_size(descs, desc, descs->ndescs + 1));
+ kfree(descs);
+
+ descs = array;
+ array_info = (void *)(descs->desc + count);
+ array_info->get_mask = array_info->invert_mask +
+ bitmap_size;
+ array_info->set_mask = array_info->get_mask +
+ bitmap_size;
+
+ array_info->desc = descs->desc;
+ array_info->size = count;
+ array_info->chip = chip;
+ bitmap_set(array_info->get_mask, descs->ndescs,
+ count - descs->ndescs);
+ bitmap_set(array_info->set_mask, descs->ndescs,
+ count - descs->ndescs);
+ descs->info = array_info;
+ }
+ /*
+ * Unmark members which don't qualify for fast bitmap
+ * processing (different chip, not in hardware order)
+ */
+ if (array_info && (chip != array_info->chip ||
+ gpio_chip_hwgpio(desc) != descs->ndescs)) {
+ __clear_bit(descs->ndescs, array_info->get_mask);
+ __clear_bit(descs->ndescs, array_info->set_mask);
+ } else if (array_info) {
+ /* Exclude open drain or open source from fast output */
+ if (gpiochip_line_is_open_drain(chip, descs->ndescs) ||
+ gpiochip_line_is_open_source(chip, descs->ndescs))
+ __clear_bit(descs->ndescs,
+ array_info->set_mask);
+ /* Identify 'fast' pins which require invertion */
+ if (gpiod_is_active_low(desc))
+ __set_bit(descs->ndescs,
+ array_info->invert_mask);
+ }
+
descs->ndescs++;
}
+ if (array_info)
+ dev_dbg(dev,
+ "GPIO array info: chip=%s, size=%d, get_mask=%lx, set_mask=%lx, invert_mask=%lx\n",
+ array_info->chip->label, array_info->size,
+ *array_info->get_mask, *array_info->set_mask,
+ *array_info->invert_mask);
return descs;
}
EXPORT_SYMBOL_GPL(gpiod_get_array);
diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h
index 11e83d2eef89..b60905d558b1 100644
--- a/drivers/gpio/gpiolib.h
+++ b/drivers/gpio/gpiolib.h
@@ -183,6 +183,15 @@ static inline bool acpi_can_fallback_to_crs(struct acpi_device *adev,
}
#endif

+struct gpio_array {
+ struct gpio_desc **desc;
+ unsigned int size;
+ struct gpio_chip *chip;
+ unsigned long *get_mask;
+ unsigned long *set_mask;
+ unsigned long invert_mask[];
+};
+
struct gpio_desc *gpiochip_get_desc(struct gpio_chip *chip, u16 hwnum);
int gpiod_get_array_value_complex(bool raw, bool can_sleep,
unsigned int array_size,
diff --git a/include/linux/gpio/consumer.h b/include/linux/gpio/consumer.h
index 2b0389f0bec4..0ffd71c0a77c 100644
--- a/include/linux/gpio/consumer.h
+++ b/include/linux/gpio/consumer.h
@@ -17,11 +17,20 @@ struct device;
*/
struct gpio_desc;

+/**
+ * Opaque descriptor for a structure of GPIO array attributes. This structure
+ * is attached to struct gpiod_descs obtained from gpiod_get_array() and can be
+ * passed back to get/set array functions in order to activate fast processing
+ * path if applicable.
+ */
+struct gpio_array;
+
/**
* Struct containing an array of descriptors that can be obtained using
* gpiod_get_array().
*/
struct gpio_descs {
+ struct gpio_array *info;
unsigned int ndescs;
struct gpio_desc *desc[];
};
--
2.16.4


2018-09-05 21:51:39

by Janusz Krzysztofik

[permalink] [raw]
Subject: [PATCH v8 3/4] gpiolib: Pass array info to get/set array functions

In order to make use of array info obtained from gpiod_get_array() and
speed up processing of arrays matching single GPIO chip layout, that
information must be passed to get/set array functions. Extend the
functions' API with that additional parameter and update all users.
Pass NULL if a user builds an array itself from single GPIOs.

Cc: Jonathan Corbet <[email protected]>
Cc: Miguel Ojeda Sandonis <[email protected]>
Cc: Geert Uytterhoeven <[email protected]>
Cc: Sebastien Bourdelin <[email protected]>
Cc: Lukas Wunner <[email protected]>
Cc: Peter Korsgaard <[email protected]>
Cc: Peter Rosin <[email protected]>
Cc: Andrew Lunn <[email protected]>
Cc: Florian Fainelli <[email protected]>
Cc: "David S. Miller" <[email protected]>
Cc: Rojhalat Ibrahim <[email protected]>
Cc: Dominik Brodowski <[email protected]>
Cc: Russell King <[email protected]>
Cc: Kishon Vijay Abraham I <[email protected]>
Cc: Tony Lindgren <[email protected]>
Cc: Lars-Peter Clausen <[email protected]>
Cc: Michael Hennerich <[email protected]>
Cc: Jonathan Cameron <[email protected]>
Cc: Hartmut Knaack <[email protected]>
Cc: Peter Meerwald-Stadler <[email protected]>
Cc: Greg Kroah-Hartman <[email protected]>
Cc: Jiri Slaby <[email protected]>
Cc: Yegor Yefremov <[email protected]>
Cc: Uwe Kleine-König <[email protected]>
Signed-off-by: Janusz Krzysztofik <[email protected]>
Acked-by: Ulf Hansson <[email protected]>
---
Documentation/driver-api/gpio/consumer.rst | 14 ++++++++--
drivers/auxdisplay/hd44780.c | 8 +++---
drivers/bus/ts-nbus.c | 5 ++--
drivers/gpio/gpio-max3191x.c | 6 +++--
drivers/gpio/gpiolib.c | 40 +++++++++++++++++++++++------
drivers/gpio/gpiolib.h | 2 ++
drivers/i2c/muxes/i2c-mux-gpio.c | 3 ++-
drivers/mmc/core/pwrseq_simple.c | 2 +-
drivers/mux/gpio.c | 3 ++-
drivers/net/phy/mdio-mux-gpio.c | 2 +-
drivers/pcmcia/soc_common.c | 2 +-
drivers/phy/motorola/phy-mapphone-mdm6600.c | 4 ++-
drivers/staging/iio/adc/ad7606.c | 3 ++-
drivers/tty/serial/serial_mctrl_gpio.c | 2 +-
include/linux/gpio/consumer.h | 16 ++++++++++++
15 files changed, 86 insertions(+), 26 deletions(-)

diff --git a/Documentation/driver-api/gpio/consumer.rst b/Documentation/driver-api/gpio/consumer.rst
index 7e0298b9a7b9..0afd95a12b10 100644
--- a/Documentation/driver-api/gpio/consumer.rst
+++ b/Documentation/driver-api/gpio/consumer.rst
@@ -325,28 +325,36 @@ The following functions get or set the values of an array of GPIOs::

int gpiod_get_array_value(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap);
int gpiod_get_raw_array_value(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap);
int gpiod_get_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap);
int gpiod_get_raw_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap);

void gpiod_set_array_value(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap)
void gpiod_set_raw_array_value(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap)
void gpiod_set_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap)
void gpiod_set_raw_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap)

The array can be an arbitrary set of GPIOs. The functions will try to access
@@ -358,6 +366,7 @@ accessed sequentially.
The functions take three arguments:
* array_size - the number of array elements
* desc_array - an array of GPIO descriptors
+ * array_info - optional information obtained from gpiod_array_get()
* value_bitmap - a bitmap to store the GPIOs' values (get) or
a bitmap of values to assign to the GPIOs (set)

@@ -368,12 +377,13 @@ the struct gpio_descs returned by gpiod_get_array()::

struct gpio_descs *my_gpio_descs = gpiod_get_array(...);
gpiod_set_array_value(my_gpio_descs->ndescs, my_gpio_descs->desc,
- my_gpio_value_bitmap);
+ my_gpio_descs->info, my_gpio_value_bitmap);

It is also possible to access a completely arbitrary array of descriptors. The
descriptors may be obtained using any combination of gpiod_get() and
gpiod_get_array(). Afterwards the array of descriptors has to be setup
-manually before it can be passed to one of the above functions.
+manually before it can be passed to one of the above functions. In that case,
+array_info should be set to NULL.

Note that for optimal performance GPIOs belonging to the same chip should be
contiguous within the array of descriptors.
diff --git a/drivers/auxdisplay/hd44780.c b/drivers/auxdisplay/hd44780.c
index e9a893384362..9ad93ea42fdc 100644
--- a/drivers/auxdisplay/hd44780.c
+++ b/drivers/auxdisplay/hd44780.c
@@ -70,7 +70,7 @@ static void hd44780_write_gpio8(struct hd44780 *hd, u8 val, unsigned int rs)
n = hd->pins[PIN_CTRL_RW] ? 10 : 9;

/* Present the data to the port */
- gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA0], values);
+ gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA0], NULL, values);

hd44780_strobe_gpio(hd);
}
@@ -87,7 +87,7 @@ static void hd44780_write_gpio4(struct hd44780 *hd, u8 val, unsigned int rs)
n = hd->pins[PIN_CTRL_RW] ? 6 : 5;

/* Present the data to the port */
- gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA4], values);
+ gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA4], NULL, values);

hd44780_strobe_gpio(hd);

@@ -96,7 +96,7 @@ static void hd44780_write_gpio4(struct hd44780 *hd, u8 val, unsigned int rs)
values[0] |= val & 0x0f;

/* Present the data to the port */
- gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA4], values);
+ gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA4], NULL, values);

hd44780_strobe_gpio(hd);
}
@@ -152,7 +152,7 @@ static void hd44780_write_cmd_raw_gpio4(struct charlcd *lcd, int cmd)
n = hd->pins[PIN_CTRL_RW] ? 6 : 5;

/* Present the data to the port */
- gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA4], values);
+ gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA4], NULL, values);

hd44780_strobe_gpio(hd);
}
diff --git a/drivers/bus/ts-nbus.c b/drivers/bus/ts-nbus.c
index 8dde7c77f62c..9989ce904a37 100644
--- a/drivers/bus/ts-nbus.c
+++ b/drivers/bus/ts-nbus.c
@@ -114,7 +114,8 @@ static void ts_nbus_reset_bus(struct ts_nbus *ts_nbus)

values[0] = 0;

- gpiod_set_array_value_cansleep(8, ts_nbus->data->desc, values);
+ gpiod_set_array_value_cansleep(8, ts_nbus->data->desc,
+ ts_nbus->data->info, values);
gpiod_set_value_cansleep(ts_nbus->csn, 0);
gpiod_set_value_cansleep(ts_nbus->strobe, 0);
gpiod_set_value_cansleep(ts_nbus->ale, 0);
@@ -159,7 +160,7 @@ static void ts_nbus_write_byte(struct ts_nbus *ts_nbus, u8 byte)

values[0] = byte;

- gpiod_set_array_value_cansleep(8, gpios->desc, values);
+ gpiod_set_array_value_cansleep(8, gpios->desc, gpios->info, values);
}

/*
diff --git a/drivers/gpio/gpio-max3191x.c b/drivers/gpio/gpio-max3191x.c
index bd4a245fc5a0..9a8876abeb57 100644
--- a/drivers/gpio/gpio-max3191x.c
+++ b/drivers/gpio/gpio-max3191x.c
@@ -313,6 +313,7 @@ static int max3191x_set_config(struct gpio_chip *gpio, unsigned int offset,

static void gpiod_set_array_single_value_cansleep(unsigned int ndescs,
struct gpio_desc **desc,
+ struct gpio_array *info,
int value)
{
unsigned long *values;
@@ -326,7 +327,7 @@ static void gpiod_set_array_single_value_cansleep(unsigned int ndescs,
else
bitmap_zero(values, ndescs);

- gpiod_set_array_value_cansleep(ndescs, desc, values);
+ gpiod_set_array_value_cansleep(ndescs, desc, info, values);
kfree(values);
}

@@ -399,7 +400,8 @@ static int max3191x_probe(struct spi_device *spi)
if (max3191x->modesel_pins)
gpiod_set_array_single_value_cansleep(
max3191x->modesel_pins->ndescs,
- max3191x->modesel_pins->desc, max3191x->mode);
+ max3191x->modesel_pins->desc,
+ max3191x->modesel_pins->info, max3191x->mode);

max3191x->ignore_uv = device_property_read_bool(dev,
"maxim,ignore-undervoltage");
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index 141f39308a53..cd7c1deed132 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -436,6 +436,7 @@ static long linehandle_ioctl(struct file *filep, unsigned int cmd,
true,
lh->numdescs,
lh->descs,
+ NULL,
vals);
if (ret)
return ret;
@@ -468,6 +469,7 @@ static long linehandle_ioctl(struct file *filep, unsigned int cmd,
true,
lh->numdescs,
lh->descs,
+ NULL,
vals);
}
return -EINVAL;
@@ -2784,6 +2786,7 @@ static int gpio_chip_get_multiple(struct gpio_chip *chip,
int gpiod_get_array_value_complex(bool raw, bool can_sleep,
unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap)
{
int i = 0;
@@ -2897,6 +2900,7 @@ EXPORT_SYMBOL_GPL(gpiod_get_value);
* gpiod_get_raw_array_value() - read raw values from an array of GPIOs
* @array_size: number of elements in the descriptor array / value bitmap
* @desc_array: array of GPIO descriptors whose values will be read
+ * @array_info: information on applicability of fast bitmap processing path
* @value_bitmap: bitmap to store the read values
*
* Read the raw values of the GPIOs, i.e. the values of the physical lines
@@ -2908,12 +2912,14 @@ EXPORT_SYMBOL_GPL(gpiod_get_value);
*/
int gpiod_get_raw_array_value(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap)
{
if (!desc_array)
return -EINVAL;
return gpiod_get_array_value_complex(true, false, array_size,
- desc_array, value_bitmap);
+ desc_array, array_info,
+ value_bitmap);
}
EXPORT_SYMBOL_GPL(gpiod_get_raw_array_value);

@@ -2921,6 +2927,7 @@ EXPORT_SYMBOL_GPL(gpiod_get_raw_array_value);
* gpiod_get_array_value() - read values from an array of GPIOs
* @array_size: number of elements in the descriptor array / value bitmap
* @desc_array: array of GPIO descriptors whose values will be read
+ * @array_info: information on applicability of fast bitmap processing path
* @value_bitmap: bitmap to store the read values
*
* Read the logical values of the GPIOs, i.e. taking their ACTIVE_LOW status
@@ -2931,12 +2938,14 @@ EXPORT_SYMBOL_GPL(gpiod_get_raw_array_value);
*/
int gpiod_get_array_value(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap)
{
if (!desc_array)
return -EINVAL;
return gpiod_get_array_value_complex(false, false, array_size,
- desc_array, value_bitmap);
+ desc_array, array_info,
+ value_bitmap);
}
EXPORT_SYMBOL_GPL(gpiod_get_array_value);

@@ -3029,6 +3038,7 @@ static void gpio_chip_set_multiple(struct gpio_chip *chip,
int gpiod_set_array_value_complex(bool raw, bool can_sleep,
unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap)
{
int i = 0;
@@ -3156,6 +3166,7 @@ EXPORT_SYMBOL_GPL(gpiod_set_value);
* gpiod_set_raw_array_value() - assign values to an array of GPIOs
* @array_size: number of elements in the descriptor array / value bitmap
* @desc_array: array of GPIO descriptors whose values will be assigned
+ * @array_info: information on applicability of fast bitmap processing path
* @value_bitmap: bitmap of values to assign
*
* Set the raw values of the GPIOs, i.e. the values of the physical lines
@@ -3166,12 +3177,13 @@ EXPORT_SYMBOL_GPL(gpiod_set_value);
*/
int gpiod_set_raw_array_value(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap)
{
if (!desc_array)
return -EINVAL;
return gpiod_set_array_value_complex(true, false, array_size,
- desc_array, value_bitmap);
+ desc_array, array_info, value_bitmap);
}
EXPORT_SYMBOL_GPL(gpiod_set_raw_array_value);

@@ -3179,6 +3191,7 @@ EXPORT_SYMBOL_GPL(gpiod_set_raw_array_value);
* gpiod_set_array_value() - assign values to an array of GPIOs
* @array_size: number of elements in the descriptor array / value bitmap
* @desc_array: array of GPIO descriptors whose values will be assigned
+ * @array_info: information on applicability of fast bitmap processing path
* @value_bitmap: bitmap of values to assign
*
* Set the logical values of the GPIOs, i.e. taking their ACTIVE_LOW status
@@ -3189,12 +3202,13 @@ EXPORT_SYMBOL_GPL(gpiod_set_raw_array_value);
*/
void gpiod_set_array_value(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap)
{
if (!desc_array)
return;
gpiod_set_array_value_complex(false, false, array_size, desc_array,
- value_bitmap);
+ array_info, value_bitmap);
}
EXPORT_SYMBOL_GPL(gpiod_set_array_value);

@@ -3416,6 +3430,7 @@ EXPORT_SYMBOL_GPL(gpiod_get_value_cansleep);
* gpiod_get_raw_array_value_cansleep() - read raw values from an array of GPIOs
* @array_size: number of elements in the descriptor array / value bitmap
* @desc_array: array of GPIO descriptors whose values will be read
+ * @array_info: information on applicability of fast bitmap processing path
* @value_bitmap: bitmap to store the read values
*
* Read the raw values of the GPIOs, i.e. the values of the physical lines
@@ -3426,13 +3441,15 @@ EXPORT_SYMBOL_GPL(gpiod_get_value_cansleep);
*/
int gpiod_get_raw_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap)
{
might_sleep_if(extra_checks);
if (!desc_array)
return -EINVAL;
return gpiod_get_array_value_complex(true, true, array_size,
- desc_array, value_bitmap);
+ desc_array, array_info,
+ value_bitmap);
}
EXPORT_SYMBOL_GPL(gpiod_get_raw_array_value_cansleep);

@@ -3440,6 +3457,7 @@ EXPORT_SYMBOL_GPL(gpiod_get_raw_array_value_cansleep);
* gpiod_get_array_value_cansleep() - read values from an array of GPIOs
* @array_size: number of elements in the descriptor array / value bitmap
* @desc_array: array of GPIO descriptors whose values will be read
+ * @array_info: information on applicability of fast bitmap processing path
* @value_bitmap: bitmap to store the read values
*
* Read the logical values of the GPIOs, i.e. taking their ACTIVE_LOW status
@@ -3449,13 +3467,15 @@ EXPORT_SYMBOL_GPL(gpiod_get_raw_array_value_cansleep);
*/
int gpiod_get_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap)
{
might_sleep_if(extra_checks);
if (!desc_array)
return -EINVAL;
return gpiod_get_array_value_complex(false, true, array_size,
- desc_array, value_bitmap);
+ desc_array, array_info,
+ value_bitmap);
}
EXPORT_SYMBOL_GPL(gpiod_get_array_value_cansleep);

@@ -3499,6 +3519,7 @@ EXPORT_SYMBOL_GPL(gpiod_set_value_cansleep);
* gpiod_set_raw_array_value_cansleep() - assign values to an array of GPIOs
* @array_size: number of elements in the descriptor array / value bitmap
* @desc_array: array of GPIO descriptors whose values will be assigned
+ * @array_info: information on applicability of fast bitmap processing path
* @value_bitmap: bitmap of values to assign
*
* Set the raw values of the GPIOs, i.e. the values of the physical lines
@@ -3508,13 +3529,14 @@ EXPORT_SYMBOL_GPL(gpiod_set_value_cansleep);
*/
int gpiod_set_raw_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap)
{
might_sleep_if(extra_checks);
if (!desc_array)
return -EINVAL;
return gpiod_set_array_value_complex(true, true, array_size, desc_array,
- value_bitmap);
+ array_info, value_bitmap);
}
EXPORT_SYMBOL_GPL(gpiod_set_raw_array_value_cansleep);

@@ -3539,6 +3561,7 @@ void gpiod_add_lookup_tables(struct gpiod_lookup_table **tables, size_t n)
* gpiod_set_array_value_cansleep() - assign values to an array of GPIOs
* @array_size: number of elements in the descriptor array / value bitmap
* @desc_array: array of GPIO descriptors whose values will be assigned
+ * @array_info: information on applicability of fast bitmap processing path
* @value_bitmap: bitmap of values to assign
*
* Set the logical values of the GPIOs, i.e. taking their ACTIVE_LOW status
@@ -3548,13 +3571,14 @@ void gpiod_add_lookup_tables(struct gpiod_lookup_table **tables, size_t n)
*/
void gpiod_set_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap)
{
might_sleep_if(extra_checks);
if (!desc_array)
return;
gpiod_set_array_value_complex(false, true, array_size, desc_array,
- value_bitmap);
+ array_info, value_bitmap);
}
EXPORT_SYMBOL_GPL(gpiod_set_array_value_cansleep);

diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h
index b60905d558b1..b65ca896b24d 100644
--- a/drivers/gpio/gpiolib.h
+++ b/drivers/gpio/gpiolib.h
@@ -196,10 +196,12 @@ struct gpio_desc *gpiochip_get_desc(struct gpio_chip *chip, u16 hwnum);
int gpiod_get_array_value_complex(bool raw, bool can_sleep,
unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap);
int gpiod_set_array_value_complex(bool raw, bool can_sleep,
unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap);

/* This is just passed between gpiolib and devres */
diff --git a/drivers/i2c/muxes/i2c-mux-gpio.c b/drivers/i2c/muxes/i2c-mux-gpio.c
index d835857bb094..13882a2a4f60 100644
--- a/drivers/i2c/muxes/i2c-mux-gpio.c
+++ b/drivers/i2c/muxes/i2c-mux-gpio.c
@@ -30,7 +30,8 @@ static void i2c_mux_gpio_set(const struct gpiomux *mux, unsigned val)

values[0] = val;

- gpiod_set_array_value_cansleep(mux->data.n_gpios, mux->gpios, values);
+ gpiod_set_array_value_cansleep(mux->data.n_gpios, mux->gpios, NULL,
+ values);
}

static int i2c_mux_gpio_select(struct i2c_mux_core *muxc, u32 chan)
diff --git a/drivers/mmc/core/pwrseq_simple.c b/drivers/mmc/core/pwrseq_simple.c
index 902476ef9a0e..7f882a2bb872 100644
--- a/drivers/mmc/core/pwrseq_simple.c
+++ b/drivers/mmc/core/pwrseq_simple.c
@@ -46,7 +46,7 @@ static void mmc_pwrseq_simple_set_gpios_value(struct mmc_pwrseq_simple *pwrseq,
values[0] = value;

gpiod_set_array_value_cansleep(nvalues, reset_gpios->desc,
- values);
+ reset_gpios->info, values);
}
}

diff --git a/drivers/mux/gpio.c b/drivers/mux/gpio.c
index 46c44532fbd5..02c1f2c014e8 100644
--- a/drivers/mux/gpio.c
+++ b/drivers/mux/gpio.c
@@ -27,7 +27,8 @@ static int mux_gpio_set(struct mux_control *mux, int state)
values[0] = state;

gpiod_set_array_value_cansleep(mux_gpio->gpios->ndescs,
- mux_gpio->gpios->desc, values);
+ mux_gpio->gpios->desc,
+ mux_gpio->gpios->info, values);

return 0;
}
diff --git a/drivers/net/phy/mdio-mux-gpio.c b/drivers/net/phy/mdio-mux-gpio.c
index e25ccfc8c070..fe34576262bd 100644
--- a/drivers/net/phy/mdio-mux-gpio.c
+++ b/drivers/net/phy/mdio-mux-gpio.c
@@ -34,7 +34,7 @@ static int mdio_mux_gpio_switch_fn(int current_child, int desired_child,
values[0] = desired_child;

gpiod_set_array_value_cansleep(s->gpios->ndescs, s->gpios->desc,
- values);
+ s->gpios->info, values);

return 0;
}
diff --git a/drivers/pcmcia/soc_common.c b/drivers/pcmcia/soc_common.c
index ac033d555700..3a8c84bb174d 100644
--- a/drivers/pcmcia/soc_common.c
+++ b/drivers/pcmcia/soc_common.c
@@ -364,7 +364,7 @@ static int soc_common_pcmcia_config_skt(
}

if (n)
- gpiod_set_array_value_cansleep(n, descs, values);
+ gpiod_set_array_value_cansleep(n, descs, NULL, values);

/*
* This really needs a better solution. The IRQ
diff --git a/drivers/phy/motorola/phy-mapphone-mdm6600.c b/drivers/phy/motorola/phy-mapphone-mdm6600.c
index 9162b61ddb95..25d456a323c2 100644
--- a/drivers/phy/motorola/phy-mapphone-mdm6600.c
+++ b/drivers/phy/motorola/phy-mapphone-mdm6600.c
@@ -162,7 +162,8 @@ static void phy_mdm6600_cmd(struct phy_mdm6600 *ddata, int val)
values[0] = val;

gpiod_set_array_value_cansleep(PHY_MDM6600_NR_CMD_LINES,
- ddata->cmd_gpios->desc, values);
+ ddata->cmd_gpios->desc,
+ ddata->cmd_gpios->info, values);
}

/**
@@ -181,6 +182,7 @@ static void phy_mdm6600_status(struct work_struct *work)

error = gpiod_get_array_value_cansleep(PHY_MDM6600_NR_STATUS_LINES,
ddata->status_gpios->desc,
+ ddata->status_gpios->info,
values);
if (error)
return;
diff --git a/drivers/staging/iio/adc/ad7606.c b/drivers/staging/iio/adc/ad7606.c
index 9c1d77d48700..b7810b1aad07 100644
--- a/drivers/staging/iio/adc/ad7606.c
+++ b/drivers/staging/iio/adc/ad7606.c
@@ -230,7 +230,8 @@ static int ad7606_write_raw(struct iio_dev *indio_dev,
values[0] = ret;

mutex_lock(&st->lock);
- gpiod_set_array_value(3, st->gpio_os->desc, values);
+ gpiod_set_array_value(3, st->gpio_os->desc, st->gpio_os->info,
+ values);
st->oversampling = val;
mutex_unlock(&st->lock);

diff --git a/drivers/tty/serial/serial_mctrl_gpio.c b/drivers/tty/serial/serial_mctrl_gpio.c
index 7d9d2c7b6c39..39ed56214cd3 100644
--- a/drivers/tty/serial/serial_mctrl_gpio.c
+++ b/drivers/tty/serial/serial_mctrl_gpio.c
@@ -53,7 +53,7 @@ void mctrl_gpio_set(struct mctrl_gpios *gpios, unsigned int mctrl)
mctrl & mctrl_gpios_desc[i].mctrl);
count++;
}
- gpiod_set_array_value(count, desc_array, values);
+ gpiod_set_array_value(count, desc_array, NULL, values);
}
EXPORT_SYMBOL_GPL(mctrl_gpio_set);

diff --git a/include/linux/gpio/consumer.h b/include/linux/gpio/consumer.h
index 0ffd71c0a77c..d7fbe30ece84 100644
--- a/include/linux/gpio/consumer.h
+++ b/include/linux/gpio/consumer.h
@@ -114,36 +114,44 @@ int gpiod_direction_output_raw(struct gpio_desc *desc, int value);
int gpiod_get_value(const struct gpio_desc *desc);
int gpiod_get_array_value(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap);
void gpiod_set_value(struct gpio_desc *desc, int value);
void gpiod_set_array_value(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap);
int gpiod_get_raw_value(const struct gpio_desc *desc);
int gpiod_get_raw_array_value(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap);
void gpiod_set_raw_value(struct gpio_desc *desc, int value);
int gpiod_set_raw_array_value(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap);

/* Value get/set from sleeping context */
int gpiod_get_value_cansleep(const struct gpio_desc *desc);
int gpiod_get_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap);
void gpiod_set_value_cansleep(struct gpio_desc *desc, int value);
void gpiod_set_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap);
int gpiod_get_raw_value_cansleep(const struct gpio_desc *desc);
int gpiod_get_raw_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap);
void gpiod_set_raw_value_cansleep(struct gpio_desc *desc, int value);
int gpiod_set_raw_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap);

int gpiod_set_debounce(struct gpio_desc *desc, unsigned debounce);
@@ -341,6 +349,7 @@ static inline int gpiod_get_value(const struct gpio_desc *desc)
}
static inline int gpiod_get_array_value(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap)
{
/* GPIO can never have been requested */
@@ -354,6 +363,7 @@ static inline void gpiod_set_value(struct gpio_desc *desc, int value)
}
static inline void gpiod_set_array_value(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap)
{
/* GPIO can never have been requested */
@@ -367,6 +377,7 @@ static inline int gpiod_get_raw_value(const struct gpio_desc *desc)
}
static inline int gpiod_get_raw_array_value(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap)
{
/* GPIO can never have been requested */
@@ -380,6 +391,7 @@ static inline void gpiod_set_raw_value(struct gpio_desc *desc, int value)
}
static inline int gpiod_set_raw_array_value(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap)
{
/* GPIO can never have been requested */
@@ -395,6 +407,7 @@ static inline int gpiod_get_value_cansleep(const struct gpio_desc *desc)
}
static inline int gpiod_get_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap)
{
/* GPIO can never have been requested */
@@ -408,6 +421,7 @@ static inline void gpiod_set_value_cansleep(struct gpio_desc *desc, int value)
}
static inline void gpiod_set_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap)
{
/* GPIO can never have been requested */
@@ -421,6 +435,7 @@ static inline int gpiod_get_raw_value_cansleep(const struct gpio_desc *desc)
}
static inline int gpiod_get_raw_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap)
{
/* GPIO can never have been requested */
@@ -435,6 +450,7 @@ static inline void gpiod_set_raw_value_cansleep(struct gpio_desc *desc,
}
static inline int gpiod_set_raw_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
unsigned long *value_bitmap)
{
/* GPIO can never have been requested */
--
2.16.4


2018-09-05 21:51:59

by Janusz Krzysztofik

[permalink] [raw]
Subject: [PATCH v8 1/4] gpiolib: Pass bitmaps, not integer arrays, to get/set array

Most users of get/set array functions iterate consecutive bits of data,
usually a single integer, while processing array of results obtained
from, or building an array of values to be passed to those functions.
Save time wasted on those iterations by changing the functions' API to
accept bitmaps.

All current users are updated as well.

More benefits from the change are expected as soon as planned support
for accepting/passing those bitmaps directly from/to respective GPIO
chip callbacks if applicable is implemented.

Cc: Jonathan Corbet <[email protected]>
Cc: Miguel Ojeda Sandonis <[email protected]>
Cc: Sebastien Bourdelin <[email protected]>
Cc: Lukas Wunner <[email protected]>
Cc: Peter Korsgaard <[email protected]>
Cc: Peter Rosin <[email protected]>
Cc: Andrew Lunn <[email protected]>
Cc: Florian Fainelli <[email protected]>
Cc: "David S. Miller" <[email protected]>
Cc: Rojhalat Ibrahim <[email protected]>
Cc: Dominik Brodowski <[email protected]>
Cc: Russell King <[email protected]>
Cc: Kishon Vijay Abraham I <[email protected]>
Cc: Tony Lindgren <[email protected]>
Cc: Lars-Peter Clausen <[email protected]>
Cc: Michael Hennerich <[email protected]>
Cc: Jonathan Cameron <[email protected]>
Cc: Hartmut Knaack <[email protected]>
Cc: Peter Meerwald-Stadler <[email protected]>
Cc: Greg Kroah-Hartman <[email protected]>
Cc: Jiri Slaby <[email protected]>
Cc: Yegor Yefremov <[email protected]>
Cc: Uwe Kleine-König <[email protected]>
Signed-off-by: Janusz Krzysztofik <[email protected]>
Acked-by: Ulf Hansson <[email protected]>
Reviewed-by: Geert Uytterhoeven <[email protected]>
Tested-by: Geert Uytterhoeven <[email protected]>
---
Documentation/driver-api/gpio/consumer.rst | 22 ++++----
drivers/auxdisplay/hd44780.c | 59 +++++++--------------
drivers/bus/ts-nbus.c | 15 ++----
drivers/gpio/gpio-max3191x.c | 10 ++--
drivers/gpio/gpiolib.c | 82 +++++++++++++++--------------
drivers/gpio/gpiolib.h | 4 +-
drivers/i2c/muxes/i2c-mux-gpio.c | 13 ++---
drivers/mmc/core/pwrseq_simple.c | 13 ++---
drivers/mux/gpio.c | 13 ++---
drivers/net/phy/mdio-mux-gpio.c | 11 ++--
drivers/pcmcia/soc_common.c | 7 +--
drivers/phy/motorola/phy-mapphone-mdm6600.c | 15 +++---
drivers/staging/iio/adc/ad7606.c | 9 ++--
drivers/tty/serial/serial_mctrl_gpio.c | 7 +--
include/linux/gpio/consumer.h | 34 ++++++------
15 files changed, 137 insertions(+), 177 deletions(-)

diff --git a/Documentation/driver-api/gpio/consumer.rst b/Documentation/driver-api/gpio/consumer.rst
index aa03f389d41d..ed68042ddccf 100644
--- a/Documentation/driver-api/gpio/consumer.rst
+++ b/Documentation/driver-api/gpio/consumer.rst
@@ -323,29 +323,29 @@ The following functions get or set the values of an array of GPIOs::

int gpiod_get_array_value(unsigned int array_size,
struct gpio_desc **desc_array,
- int *value_array);
+ unsigned long *value_bitmap);
int gpiod_get_raw_array_value(unsigned int array_size,
struct gpio_desc **desc_array,
- int *value_array);
+ unsigned long *value_bitmap);
int gpiod_get_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
- int *value_array);
+ unsigned long *value_bitmap);
int gpiod_get_raw_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
- int *value_array);
+ unsigned long *value_bitmap);

void gpiod_set_array_value(unsigned int array_size,
struct gpio_desc **desc_array,
- int *value_array)
+ unsigned long *value_bitmap)
void gpiod_set_raw_array_value(unsigned int array_size,
struct gpio_desc **desc_array,
- int *value_array)
+ unsigned long *value_bitmap)
void gpiod_set_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
- int *value_array)
+ unsigned long *value_bitmap)
void gpiod_set_raw_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
- int *value_array)
+ unsigned long *value_bitmap)

The array can be an arbitrary set of GPIOs. The functions will try to access
GPIOs belonging to the same bank or chip simultaneously if supported by the
@@ -356,8 +356,8 @@ accessed sequentially.
The functions take three arguments:
* array_size - the number of array elements
* desc_array - an array of GPIO descriptors
- * value_array - an array to store the GPIOs' values (get) or
- an array of values to assign to the GPIOs (set)
+ * value_bitmap - a bitmap to store the GPIOs' values (get) or
+ a bitmap of values to assign to the GPIOs (set)

The descriptor array can be obtained using the gpiod_get_array() function
or one of its variants. If the group of descriptors returned by that function
@@ -366,7 +366,7 @@ the struct gpio_descs returned by gpiod_get_array()::

struct gpio_descs *my_gpio_descs = gpiod_get_array(...);
gpiod_set_array_value(my_gpio_descs->ndescs, my_gpio_descs->desc,
- my_gpio_values);
+ my_gpio_value_bitmap);

It is also possible to access a completely arbitrary array of descriptors. The
descriptors may be obtained using any combination of gpiod_get() and
diff --git a/drivers/auxdisplay/hd44780.c b/drivers/auxdisplay/hd44780.c
index f1a42f0f1ded..e9a893384362 100644
--- a/drivers/auxdisplay/hd44780.c
+++ b/drivers/auxdisplay/hd44780.c
@@ -62,17 +62,12 @@ static void hd44780_strobe_gpio(struct hd44780 *hd)
/* write to an LCD panel register in 8 bit GPIO mode */
static void hd44780_write_gpio8(struct hd44780 *hd, u8 val, unsigned int rs)
{
- int values[10]; /* for DATA[0-7], RS, RW */
- unsigned int i, n;
-
- for (i = 0; i < 8; i++)
- values[PIN_DATA0 + i] = !!(val & BIT(i));
- values[PIN_CTRL_RS] = rs;
- n = 9;
- if (hd->pins[PIN_CTRL_RW]) {
- values[PIN_CTRL_RW] = 0;
- n++;
- }
+ DECLARE_BITMAP(values, 10); /* for DATA[0-7], RS, RW */
+ unsigned int n;
+
+ values[0] = val;
+ __assign_bit(8, values, rs);
+ n = hd->pins[PIN_CTRL_RW] ? 10 : 9;

/* Present the data to the port */
gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA0], values);
@@ -83,32 +78,25 @@ static void hd44780_write_gpio8(struct hd44780 *hd, u8 val, unsigned int rs)
/* write to an LCD panel register in 4 bit GPIO mode */
static void hd44780_write_gpio4(struct hd44780 *hd, u8 val, unsigned int rs)
{
- int values[10]; /* for DATA[0-7], RS, RW, but DATA[0-3] is unused */
- unsigned int i, n;
+ DECLARE_BITMAP(values, 6); /* for DATA[4-7], RS, RW */
+ unsigned int n;

/* High nibble + RS, RW */
- for (i = 4; i < 8; i++)
- values[PIN_DATA0 + i] = !!(val & BIT(i));
- values[PIN_CTRL_RS] = rs;
- n = 5;
- if (hd->pins[PIN_CTRL_RW]) {
- values[PIN_CTRL_RW] = 0;
- n++;
- }
+ values[0] = val >> 4;
+ __assign_bit(4, values, rs);
+ n = hd->pins[PIN_CTRL_RW] ? 6 : 5;

/* Present the data to the port */
- gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA4],
- &values[PIN_DATA4]);
+ gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA4], values);

hd44780_strobe_gpio(hd);

/* Low nibble */
- for (i = 0; i < 4; i++)
- values[PIN_DATA4 + i] = !!(val & BIT(i));
+ values[0] &= ~0x0fUL;
+ values[0] |= val & 0x0f;

/* Present the data to the port */
- gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA4],
- &values[PIN_DATA4]);
+ gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA4], values);

hd44780_strobe_gpio(hd);
}
@@ -155,23 +143,16 @@ static void hd44780_write_cmd_gpio4(struct charlcd *lcd, int cmd)
/* Send 4-bits of a command to the LCD panel in raw 4 bit GPIO mode */
static void hd44780_write_cmd_raw_gpio4(struct charlcd *lcd, int cmd)
{
- int values[10]; /* for DATA[0-7], RS, RW, but DATA[0-3] is unused */
+ DECLARE_BITMAP(values, 6); /* for DATA[4-7], RS, RW */
struct hd44780 *hd = lcd->drvdata;
- unsigned int i, n;
+ unsigned int n;

/* Command nibble + RS, RW */
- for (i = 0; i < 4; i++)
- values[PIN_DATA4 + i] = !!(cmd & BIT(i));
- values[PIN_CTRL_RS] = 0;
- n = 5;
- if (hd->pins[PIN_CTRL_RW]) {
- values[PIN_CTRL_RW] = 0;
- n++;
- }
+ values[0] = cmd & 0x0f;
+ n = hd->pins[PIN_CTRL_RW] ? 6 : 5;

/* Present the data to the port */
- gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA4],
- &values[PIN_DATA4]);
+ gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA4], values);

hd44780_strobe_gpio(hd);
}
diff --git a/drivers/bus/ts-nbus.c b/drivers/bus/ts-nbus.c
index 073fd9011154..8dde7c77f62c 100644
--- a/drivers/bus/ts-nbus.c
+++ b/drivers/bus/ts-nbus.c
@@ -110,11 +110,9 @@ static void ts_nbus_set_direction(struct ts_nbus *ts_nbus, int direction)
*/
static void ts_nbus_reset_bus(struct ts_nbus *ts_nbus)
{
- int i;
- int values[8];
+ DECLARE_BITMAP(values, 8);

- for (i = 0; i < 8; i++)
- values[i] = 0;
+ values[0] = 0;

gpiod_set_array_value_cansleep(8, ts_nbus->data->desc, values);
gpiod_set_value_cansleep(ts_nbus->csn, 0);
@@ -157,14 +155,9 @@ static int ts_nbus_read_byte(struct ts_nbus *ts_nbus, u8 *val)
static void ts_nbus_write_byte(struct ts_nbus *ts_nbus, u8 byte)
{
struct gpio_descs *gpios = ts_nbus->data;
- int i;
- int values[8];
+ DECLARE_BITMAP(values, 8);

- for (i = 0; i < 8; i++)
- if (byte & BIT(i))
- values[i] = 1;
- else
- values[i] = 0;
+ values[0] = byte;

gpiod_set_array_value_cansleep(8, gpios->desc, values);
}
diff --git a/drivers/gpio/gpio-max3191x.c b/drivers/gpio/gpio-max3191x.c
index b5b9cb1fda50..bd4a245fc5a0 100644
--- a/drivers/gpio/gpio-max3191x.c
+++ b/drivers/gpio/gpio-max3191x.c
@@ -315,14 +315,16 @@ static void gpiod_set_array_single_value_cansleep(unsigned int ndescs,
struct gpio_desc **desc,
int value)
{
- int i, *values;
+ unsigned long *values;

- values = kmalloc_array(ndescs, sizeof(*values), GFP_KERNEL);
+ values = bitmap_alloc(ndescs, GFP_KERNEL);
if (!values)
return;

- for (i = 0; i < ndescs; i++)
- values[i] = value;
+ if (value)
+ bitmap_fill(values, ndescs);
+ else
+ bitmap_zero(values, ndescs);

gpiod_set_array_value_cansleep(ndescs, desc, values);
kfree(values);
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index e8f8a1999393..b66b2191c5c5 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -427,7 +427,7 @@ static long linehandle_ioctl(struct file *filep, unsigned int cmd,
struct linehandle_state *lh = filep->private_data;
void __user *ip = (void __user *)arg;
struct gpiohandle_data ghd;
- int vals[GPIOHANDLES_MAX];
+ DECLARE_BITMAP(vals, GPIOHANDLES_MAX);
int i;

if (cmd == GPIOHANDLE_GET_LINE_VALUES_IOCTL) {
@@ -442,7 +442,7 @@ static long linehandle_ioctl(struct file *filep, unsigned int cmd,

memset(&ghd, 0, sizeof(ghd));
for (i = 0; i < lh->numdescs; i++)
- ghd.values[i] = vals[i];
+ ghd.values[i] = test_bit(i, vals);

if (copy_to_user(ip, &ghd, sizeof(ghd)))
return -EFAULT;
@@ -461,7 +461,7 @@ static long linehandle_ioctl(struct file *filep, unsigned int cmd,

/* Clamp all values to [0,1] */
for (i = 0; i < lh->numdescs; i++)
- vals[i] = !!ghd.values[i];
+ __assign_bit(i, vals, ghd.values[i]);

/* Reuse the array setting function */
return gpiod_set_array_value_complex(false,
@@ -2784,7 +2784,7 @@ static int gpio_chip_get_multiple(struct gpio_chip *chip,
int gpiod_get_array_value_complex(bool raw, bool can_sleep,
unsigned int array_size,
struct gpio_desc **desc_array,
- int *value_array)
+ unsigned long *value_bitmap)
{
int i = 0;

@@ -2835,7 +2835,7 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep,

if (!raw && test_bit(FLAG_ACTIVE_LOW, &desc->flags))
value = !value;
- value_array[j] = value;
+ __assign_bit(j, value_bitmap, value);
trace_gpio_value(desc_to_gpio(desc), 1, value);
}

@@ -2895,9 +2895,9 @@ EXPORT_SYMBOL_GPL(gpiod_get_value);

/**
* gpiod_get_raw_array_value() - read raw values from an array of GPIOs
- * @array_size: number of elements in the descriptor / value arrays
+ * @array_size: number of elements in the descriptor array / value bitmap
* @desc_array: array of GPIO descriptors whose values will be read
- * @value_array: array to store the read values
+ * @value_bitmap: bitmap to store the read values
*
* Read the raw values of the GPIOs, i.e. the values of the physical lines
* without regard for their ACTIVE_LOW status. Return 0 in case of success,
@@ -2907,20 +2907,21 @@ EXPORT_SYMBOL_GPL(gpiod_get_value);
* and it will complain if the GPIO chip functions potentially sleep.
*/
int gpiod_get_raw_array_value(unsigned int array_size,
- struct gpio_desc **desc_array, int *value_array)
+ struct gpio_desc **desc_array,
+ unsigned long *value_bitmap)
{
if (!desc_array)
return -EINVAL;
return gpiod_get_array_value_complex(true, false, array_size,
- desc_array, value_array);
+ desc_array, value_bitmap);
}
EXPORT_SYMBOL_GPL(gpiod_get_raw_array_value);

/**
* gpiod_get_array_value() - read values from an array of GPIOs
- * @array_size: number of elements in the descriptor / value arrays
+ * @array_size: number of elements in the descriptor array / value bitmap
* @desc_array: array of GPIO descriptors whose values will be read
- * @value_array: array to store the read values
+ * @value_bitmap: bitmap to store the read values
*
* Read the logical values of the GPIOs, i.e. taking their ACTIVE_LOW status
* into account. Return 0 in case of success, else an error code.
@@ -2929,12 +2930,13 @@ EXPORT_SYMBOL_GPL(gpiod_get_raw_array_value);
* and it will complain if the GPIO chip functions potentially sleep.
*/
int gpiod_get_array_value(unsigned int array_size,
- struct gpio_desc **desc_array, int *value_array)
+ struct gpio_desc **desc_array,
+ unsigned long *value_bitmap)
{
if (!desc_array)
return -EINVAL;
return gpiod_get_array_value_complex(false, false, array_size,
- desc_array, value_array);
+ desc_array, value_bitmap);
}
EXPORT_SYMBOL_GPL(gpiod_get_array_value);

@@ -3027,7 +3029,7 @@ static void gpio_chip_set_multiple(struct gpio_chip *chip,
int gpiod_set_array_value_complex(bool raw, bool can_sleep,
unsigned int array_size,
struct gpio_desc **desc_array,
- int *value_array)
+ unsigned long *value_bitmap)
{
int i = 0;

@@ -3056,7 +3058,7 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep,
do {
struct gpio_desc *desc = desc_array[i];
int hwgpio = gpio_chip_hwgpio(desc);
- int value = value_array[i];
+ int value = test_bit(i, value_bitmap);

if (!raw && test_bit(FLAG_ACTIVE_LOW, &desc->flags))
value = !value;
@@ -3152,9 +3154,9 @@ EXPORT_SYMBOL_GPL(gpiod_set_value);

/**
* gpiod_set_raw_array_value() - assign values to an array of GPIOs
- * @array_size: number of elements in the descriptor / value arrays
+ * @array_size: number of elements in the descriptor array / value bitmap
* @desc_array: array of GPIO descriptors whose values will be assigned
- * @value_array: array of values to assign
+ * @value_bitmap: bitmap of values to assign
*
* Set the raw values of the GPIOs, i.e. the values of the physical lines
* without regard for their ACTIVE_LOW status.
@@ -3163,20 +3165,21 @@ EXPORT_SYMBOL_GPL(gpiod_set_value);
* complain if the GPIO chip functions potentially sleep.
*/
int gpiod_set_raw_array_value(unsigned int array_size,
- struct gpio_desc **desc_array, int *value_array)
+ struct gpio_desc **desc_array,
+ unsigned long *value_bitmap)
{
if (!desc_array)
return -EINVAL;
return gpiod_set_array_value_complex(true, false, array_size,
- desc_array, value_array);
+ desc_array, value_bitmap);
}
EXPORT_SYMBOL_GPL(gpiod_set_raw_array_value);

/**
* gpiod_set_array_value() - assign values to an array of GPIOs
- * @array_size: number of elements in the descriptor / value arrays
+ * @array_size: number of elements in the descriptor array / value bitmap
* @desc_array: array of GPIO descriptors whose values will be assigned
- * @value_array: array of values to assign
+ * @value_bitmap: bitmap of values to assign
*
* Set the logical values of the GPIOs, i.e. taking their ACTIVE_LOW status
* into account.
@@ -3185,12 +3188,13 @@ EXPORT_SYMBOL_GPL(gpiod_set_raw_array_value);
* complain if the GPIO chip functions potentially sleep.
*/
void gpiod_set_array_value(unsigned int array_size,
- struct gpio_desc **desc_array, int *value_array)
+ struct gpio_desc **desc_array,
+ unsigned long *value_bitmap)
{
if (!desc_array)
return;
gpiod_set_array_value_complex(false, false, array_size, desc_array,
- value_array);
+ value_bitmap);
}
EXPORT_SYMBOL_GPL(gpiod_set_array_value);

@@ -3410,9 +3414,9 @@ EXPORT_SYMBOL_GPL(gpiod_get_value_cansleep);

/**
* gpiod_get_raw_array_value_cansleep() - read raw values from an array of GPIOs
- * @array_size: number of elements in the descriptor / value arrays
+ * @array_size: number of elements in the descriptor array / value bitmap
* @desc_array: array of GPIO descriptors whose values will be read
- * @value_array: array to store the read values
+ * @value_bitmap: bitmap to store the read values
*
* Read the raw values of the GPIOs, i.e. the values of the physical lines
* without regard for their ACTIVE_LOW status. Return 0 in case of success,
@@ -3422,21 +3426,21 @@ EXPORT_SYMBOL_GPL(gpiod_get_value_cansleep);
*/
int gpiod_get_raw_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
- int *value_array)
+ unsigned long *value_bitmap)
{
might_sleep_if(extra_checks);
if (!desc_array)
return -EINVAL;
return gpiod_get_array_value_complex(true, true, array_size,
- desc_array, value_array);
+ desc_array, value_bitmap);
}
EXPORT_SYMBOL_GPL(gpiod_get_raw_array_value_cansleep);

/**
* gpiod_get_array_value_cansleep() - read values from an array of GPIOs
- * @array_size: number of elements in the descriptor / value arrays
+ * @array_size: number of elements in the descriptor array / value bitmap
* @desc_array: array of GPIO descriptors whose values will be read
- * @value_array: array to store the read values
+ * @value_bitmap: bitmap to store the read values
*
* Read the logical values of the GPIOs, i.e. taking their ACTIVE_LOW status
* into account. Return 0 in case of success, else an error code.
@@ -3445,13 +3449,13 @@ EXPORT_SYMBOL_GPL(gpiod_get_raw_array_value_cansleep);
*/
int gpiod_get_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
- int *value_array)
+ unsigned long *value_bitmap)
{
might_sleep_if(extra_checks);
if (!desc_array)
return -EINVAL;
return gpiod_get_array_value_complex(false, true, array_size,
- desc_array, value_array);
+ desc_array, value_bitmap);
}
EXPORT_SYMBOL_GPL(gpiod_get_array_value_cansleep);

@@ -3493,9 +3497,9 @@ EXPORT_SYMBOL_GPL(gpiod_set_value_cansleep);

/**
* gpiod_set_raw_array_value_cansleep() - assign values to an array of GPIOs
- * @array_size: number of elements in the descriptor / value arrays
+ * @array_size: number of elements in the descriptor array / value bitmap
* @desc_array: array of GPIO descriptors whose values will be assigned
- * @value_array: array of values to assign
+ * @value_bitmap: bitmap of values to assign
*
* Set the raw values of the GPIOs, i.e. the values of the physical lines
* without regard for their ACTIVE_LOW status.
@@ -3504,13 +3508,13 @@ EXPORT_SYMBOL_GPL(gpiod_set_value_cansleep);
*/
int gpiod_set_raw_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
- int *value_array)
+ unsigned long *value_bitmap)
{
might_sleep_if(extra_checks);
if (!desc_array)
return -EINVAL;
return gpiod_set_array_value_complex(true, true, array_size, desc_array,
- value_array);
+ value_bitmap);
}
EXPORT_SYMBOL_GPL(gpiod_set_raw_array_value_cansleep);

@@ -3533,9 +3537,9 @@ void gpiod_add_lookup_tables(struct gpiod_lookup_table **tables, size_t n)

/**
* gpiod_set_array_value_cansleep() - assign values to an array of GPIOs
- * @array_size: number of elements in the descriptor / value arrays
+ * @array_size: number of elements in the descriptor array / value bitmap
* @desc_array: array of GPIO descriptors whose values will be assigned
- * @value_array: array of values to assign
+ * @value_bitmap: bitmap of values to assign
*
* Set the logical values of the GPIOs, i.e. taking their ACTIVE_LOW status
* into account.
@@ -3544,13 +3548,13 @@ void gpiod_add_lookup_tables(struct gpiod_lookup_table **tables, size_t n)
*/
void gpiod_set_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
- int *value_array)
+ unsigned long *value_bitmap)
{
might_sleep_if(extra_checks);
if (!desc_array)
return;
gpiod_set_array_value_complex(false, true, array_size, desc_array,
- value_array);
+ value_bitmap);
}
EXPORT_SYMBOL_GPL(gpiod_set_array_value_cansleep);

diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h
index a7e49fef73d4..11e83d2eef89 100644
--- a/drivers/gpio/gpiolib.h
+++ b/drivers/gpio/gpiolib.h
@@ -187,11 +187,11 @@ struct gpio_desc *gpiochip_get_desc(struct gpio_chip *chip, u16 hwnum);
int gpiod_get_array_value_complex(bool raw, bool can_sleep,
unsigned int array_size,
struct gpio_desc **desc_array,
- int *value_array);
+ unsigned long *value_bitmap);
int gpiod_set_array_value_complex(bool raw, bool can_sleep,
unsigned int array_size,
struct gpio_desc **desc_array,
- int *value_array);
+ unsigned long *value_bitmap);

/* This is just passed between gpiolib and devres */
struct gpio_desc *gpiod_get_from_of_node(struct device_node *node,
diff --git a/drivers/i2c/muxes/i2c-mux-gpio.c b/drivers/i2c/muxes/i2c-mux-gpio.c
index 401308e3d036..d835857bb094 100644
--- a/drivers/i2c/muxes/i2c-mux-gpio.c
+++ b/drivers/i2c/muxes/i2c-mux-gpio.c
@@ -22,18 +22,15 @@ struct gpiomux {
struct i2c_mux_gpio_platform_data data;
unsigned gpio_base;
struct gpio_desc **gpios;
- int *values;
};

static void i2c_mux_gpio_set(const struct gpiomux *mux, unsigned val)
{
- int i;
+ DECLARE_BITMAP(values, BITS_PER_TYPE(val));

- for (i = 0; i < mux->data.n_gpios; i++)
- mux->values[i] = (val >> i) & 1;
+ values[0] = val;

- gpiod_set_array_value_cansleep(mux->data.n_gpios,
- mux->gpios, mux->values);
+ gpiod_set_array_value_cansleep(mux->data.n_gpios, mux->gpios, values);
}

static int i2c_mux_gpio_select(struct i2c_mux_core *muxc, u32 chan)
@@ -182,15 +179,13 @@ static int i2c_mux_gpio_probe(struct platform_device *pdev)
return -EPROBE_DEFER;

muxc = i2c_mux_alloc(parent, &pdev->dev, mux->data.n_values,
- mux->data.n_gpios * sizeof(*mux->gpios) +
- mux->data.n_gpios * sizeof(*mux->values), 0,
+ mux->data.n_gpios * sizeof(*mux->gpios), 0,
i2c_mux_gpio_select, NULL);
if (!muxc) {
ret = -ENOMEM;
goto alloc_failed;
}
mux->gpios = muxc->priv;
- mux->values = (int *)(mux->gpios + mux->data.n_gpios);
muxc->priv = mux;

platform_set_drvdata(pdev, muxc);
diff --git a/drivers/mmc/core/pwrseq_simple.c b/drivers/mmc/core/pwrseq_simple.c
index a8b9fee4d62a..902476ef9a0e 100644
--- a/drivers/mmc/core/pwrseq_simple.c
+++ b/drivers/mmc/core/pwrseq_simple.c
@@ -40,18 +40,13 @@ static void mmc_pwrseq_simple_set_gpios_value(struct mmc_pwrseq_simple *pwrseq,
struct gpio_descs *reset_gpios = pwrseq->reset_gpios;

if (!IS_ERR(reset_gpios)) {
- int i, *values;
+ DECLARE_BITMAP(values, BITS_PER_TYPE(value));
int nvalues = reset_gpios->ndescs;

- values = kmalloc_array(nvalues, sizeof(int), GFP_KERNEL);
- if (!values)
- return;
+ values[0] = value;

- for (i = 0; i < nvalues; i++)
- values[i] = value;
-
- gpiod_set_array_value_cansleep(nvalues, reset_gpios->desc, values);
- kfree(values);
+ gpiod_set_array_value_cansleep(nvalues, reset_gpios->desc,
+ values);
}
}

diff --git a/drivers/mux/gpio.c b/drivers/mux/gpio.c
index 6fdd9316db8b..46c44532fbd5 100644
--- a/drivers/mux/gpio.c
+++ b/drivers/mux/gpio.c
@@ -17,20 +17,17 @@

struct mux_gpio {
struct gpio_descs *gpios;
- int *val;
};

static int mux_gpio_set(struct mux_control *mux, int state)
{
struct mux_gpio *mux_gpio = mux_chip_priv(mux->chip);
- int i;
+ DECLARE_BITMAP(values, BITS_PER_TYPE(state));

- for (i = 0; i < mux_gpio->gpios->ndescs; i++)
- mux_gpio->val[i] = (state >> i) & 1;
+ values[0] = state;

gpiod_set_array_value_cansleep(mux_gpio->gpios->ndescs,
- mux_gpio->gpios->desc,
- mux_gpio->val);
+ mux_gpio->gpios->desc, values);

return 0;
}
@@ -58,13 +55,11 @@ static int mux_gpio_probe(struct platform_device *pdev)
if (pins < 0)
return pins;

- mux_chip = devm_mux_chip_alloc(dev, 1, sizeof(*mux_gpio) +
- pins * sizeof(*mux_gpio->val));
+ mux_chip = devm_mux_chip_alloc(dev, 1, sizeof(*mux_gpio));
if (IS_ERR(mux_chip))
return PTR_ERR(mux_chip);

mux_gpio = mux_chip_priv(mux_chip);
- mux_gpio->val = (int *)(mux_gpio + 1);
mux_chip->ops = &mux_gpio_ops;

mux_gpio->gpios = devm_gpiod_get_array(dev, "mux", GPIOD_OUT_LOW);
diff --git a/drivers/net/phy/mdio-mux-gpio.c b/drivers/net/phy/mdio-mux-gpio.c
index bc90764a8b8d..e25ccfc8c070 100644
--- a/drivers/net/phy/mdio-mux-gpio.c
+++ b/drivers/net/phy/mdio-mux-gpio.c
@@ -20,23 +20,21 @@
struct mdio_mux_gpio_state {
struct gpio_descs *gpios;
void *mux_handle;
- int values[];
};

static int mdio_mux_gpio_switch_fn(int current_child, int desired_child,
void *data)
{
struct mdio_mux_gpio_state *s = data;
- unsigned int n;
+ DECLARE_BITMAP(values, BITS_PER_TYPE(desired_child));

if (current_child == desired_child)
return 0;

- for (n = 0; n < s->gpios->ndescs; n++)
- s->values[n] = (desired_child >> n) & 1;
+ values[0] = desired_child;

gpiod_set_array_value_cansleep(s->gpios->ndescs, s->gpios->desc,
- s->values);
+ values);

return 0;
}
@@ -51,8 +49,7 @@ static int mdio_mux_gpio_probe(struct platform_device *pdev)
if (IS_ERR(gpios))
return PTR_ERR(gpios);

- s = devm_kzalloc(&pdev->dev, struct_size(s, values, gpios->ndescs),
- GFP_KERNEL);
+ s = devm_kzalloc(&pdev->dev, sizeof(*s), GFP_KERNEL);
if (!s) {
gpiod_put_array(gpios);
return -ENOMEM;
diff --git a/drivers/pcmcia/soc_common.c b/drivers/pcmcia/soc_common.c
index c5f2344c189b..ac033d555700 100644
--- a/drivers/pcmcia/soc_common.c
+++ b/drivers/pcmcia/soc_common.c
@@ -351,15 +351,16 @@ static int soc_common_pcmcia_config_skt(

if (ret == 0) {
struct gpio_desc *descs[2];
- int values[2], n = 0;
+ DECLARE_BITMAP(values, 2);
+ int n = 0;

if (skt->gpio_reset) {
descs[n] = skt->gpio_reset;
- values[n++] = !!(state->flags & SS_RESET);
+ __assign_bit(n++, values, state->flags & SS_RESET);
}
if (skt->gpio_bus_enable) {
descs[n] = skt->gpio_bus_enable;
- values[n++] = !!(state->flags & SS_OUTPUT_ENA);
+ __assign_bit(n++, values, state->flags & SS_OUTPUT_ENA);
}

if (n)
diff --git a/drivers/phy/motorola/phy-mapphone-mdm6600.c b/drivers/phy/motorola/phy-mapphone-mdm6600.c
index 0075fb0bef8c..9162b61ddb95 100644
--- a/drivers/phy/motorola/phy-mapphone-mdm6600.c
+++ b/drivers/phy/motorola/phy-mapphone-mdm6600.c
@@ -157,12 +157,9 @@ static const struct phy_ops gpio_usb_ops = {
*/
static void phy_mdm6600_cmd(struct phy_mdm6600 *ddata, int val)
{
- int values[PHY_MDM6600_NR_CMD_LINES];
- int i;
+ DECLARE_BITMAP(values, PHY_MDM6600_NR_CMD_LINES);

- val &= (1 << PHY_MDM6600_NR_CMD_LINES) - 1;
- for (i = 0; i < PHY_MDM6600_NR_CMD_LINES; i++)
- values[i] = (val & BIT(i)) >> i;
+ values[0] = val;

gpiod_set_array_value_cansleep(PHY_MDM6600_NR_CMD_LINES,
ddata->cmd_gpios->desc, values);
@@ -176,7 +173,7 @@ static void phy_mdm6600_status(struct work_struct *work)
{
struct phy_mdm6600 *ddata;
struct device *dev;
- int values[PHY_MDM6600_NR_STATUS_LINES];
+ DECLARE_BITMAP(values, PHY_MDM6600_NR_STATUS_LINES);
int error, i, val = 0;

ddata = container_of(work, struct phy_mdm6600, status_work.work);
@@ -189,11 +186,11 @@ static void phy_mdm6600_status(struct work_struct *work)
return;

for (i = 0; i < PHY_MDM6600_NR_STATUS_LINES; i++) {
- val |= values[i] << i;
+ val |= test_bit(i, values) << i;
dev_dbg(ddata->dev, "XXX %s: i: %i values[i]: %i val: %i\n",
- __func__, i, values[i], val);
+ __func__, i, test_bit(i, values), val);
}
- ddata->status = val;
+ ddata->status = values[0];

dev_info(dev, "modem status: %i %s\n",
ddata->status,
diff --git a/drivers/staging/iio/adc/ad7606.c b/drivers/staging/iio/adc/ad7606.c
index 25b9fcd5e3a4..9c1d77d48700 100644
--- a/drivers/staging/iio/adc/ad7606.c
+++ b/drivers/staging/iio/adc/ad7606.c
@@ -202,7 +202,7 @@ static int ad7606_write_raw(struct iio_dev *indio_dev,
long mask)
{
struct ad7606_state *st = iio_priv(indio_dev);
- int values[3];
+ DECLARE_BITMAP(values, 3);
int ret, i;

switch (mask) {
@@ -227,13 +227,10 @@ static int ad7606_write_raw(struct iio_dev *indio_dev,
if (ret < 0)
return ret;

- values[0] = (ret >> 0) & 1;
- values[1] = (ret >> 1) & 1;
- values[2] = (ret >> 2) & 1;
+ values[0] = ret;

mutex_lock(&st->lock);
- gpiod_set_array_value(ARRAY_SIZE(values), st->gpio_os->desc,
- values);
+ gpiod_set_array_value(3, st->gpio_os->desc, values);
st->oversampling = val;
mutex_unlock(&st->lock);

diff --git a/drivers/tty/serial/serial_mctrl_gpio.c b/drivers/tty/serial/serial_mctrl_gpio.c
index 1c06325beaca..7d9d2c7b6c39 100644
--- a/drivers/tty/serial/serial_mctrl_gpio.c
+++ b/drivers/tty/serial/serial_mctrl_gpio.c
@@ -40,7 +40,7 @@ void mctrl_gpio_set(struct mctrl_gpios *gpios, unsigned int mctrl)
{
enum mctrl_gpio_idx i;
struct gpio_desc *desc_array[UART_GPIO_MAX];
- int value_array[UART_GPIO_MAX];
+ DECLARE_BITMAP(values, UART_GPIO_MAX);
unsigned int count = 0;

if (gpios == NULL)
@@ -49,10 +49,11 @@ void mctrl_gpio_set(struct mctrl_gpios *gpios, unsigned int mctrl)
for (i = 0; i < UART_GPIO_MAX; i++)
if (gpios->gpio[i] && mctrl_gpios_desc[i].dir_out) {
desc_array[count] = gpios->gpio[i];
- value_array[count] = !!(mctrl & mctrl_gpios_desc[i].mctrl);
+ __assign_bit(count, values,
+ mctrl & mctrl_gpios_desc[i].mctrl);
count++;
}
- gpiod_set_array_value(count, desc_array, value_array);
+ gpiod_set_array_value(count, desc_array, values);
}
EXPORT_SYMBOL_GPL(mctrl_gpio_set);

diff --git a/include/linux/gpio/consumer.h b/include/linux/gpio/consumer.h
index 21ddbe440030..2b0389f0bec4 100644
--- a/include/linux/gpio/consumer.h
+++ b/include/linux/gpio/consumer.h
@@ -104,36 +104,38 @@ int gpiod_direction_output_raw(struct gpio_desc *desc, int value);
/* Value get/set from non-sleeping context */
int gpiod_get_value(const struct gpio_desc *desc);
int gpiod_get_array_value(unsigned int array_size,
- struct gpio_desc **desc_array, int *value_array);
+ struct gpio_desc **desc_array,
+ unsigned long *value_bitmap);
void gpiod_set_value(struct gpio_desc *desc, int value);
void gpiod_set_array_value(unsigned int array_size,
- struct gpio_desc **desc_array, int *value_array);
+ struct gpio_desc **desc_array,
+ unsigned long *value_bitmap);
int gpiod_get_raw_value(const struct gpio_desc *desc);
int gpiod_get_raw_array_value(unsigned int array_size,
struct gpio_desc **desc_array,
- int *value_array);
+ unsigned long *value_bitmap);
void gpiod_set_raw_value(struct gpio_desc *desc, int value);
int gpiod_set_raw_array_value(unsigned int array_size,
struct gpio_desc **desc_array,
- int *value_array);
+ unsigned long *value_bitmap);

/* Value get/set from sleeping context */
int gpiod_get_value_cansleep(const struct gpio_desc *desc);
int gpiod_get_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
- int *value_array);
+ unsigned long *value_bitmap);
void gpiod_set_value_cansleep(struct gpio_desc *desc, int value);
void gpiod_set_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
- int *value_array);
+ unsigned long *value_bitmap);
int gpiod_get_raw_value_cansleep(const struct gpio_desc *desc);
int gpiod_get_raw_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
- int *value_array);
+ unsigned long *value_bitmap);
void gpiod_set_raw_value_cansleep(struct gpio_desc *desc, int value);
int gpiod_set_raw_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
- int *value_array);
+ unsigned long *value_bitmap);

int gpiod_set_debounce(struct gpio_desc *desc, unsigned debounce);
int gpiod_set_transitory(struct gpio_desc *desc, bool transitory);
@@ -330,7 +332,7 @@ static inline int gpiod_get_value(const struct gpio_desc *desc)
}
static inline int gpiod_get_array_value(unsigned int array_size,
struct gpio_desc **desc_array,
- int *value_array)
+ unsigned long *value_bitmap)
{
/* GPIO can never have been requested */
WARN_ON(1);
@@ -343,7 +345,7 @@ static inline void gpiod_set_value(struct gpio_desc *desc, int value)
}
static inline void gpiod_set_array_value(unsigned int array_size,
struct gpio_desc **desc_array,
- int *value_array)
+ unsigned long *value_bitmap)
{
/* GPIO can never have been requested */
WARN_ON(1);
@@ -356,7 +358,7 @@ static inline int gpiod_get_raw_value(const struct gpio_desc *desc)
}
static inline int gpiod_get_raw_array_value(unsigned int array_size,
struct gpio_desc **desc_array,
- int *value_array)
+ unsigned long *value_bitmap)
{
/* GPIO can never have been requested */
WARN_ON(1);
@@ -369,7 +371,7 @@ static inline void gpiod_set_raw_value(struct gpio_desc *desc, int value)
}
static inline int gpiod_set_raw_array_value(unsigned int array_size,
struct gpio_desc **desc_array,
- int *value_array)
+ unsigned long *value_bitmap)
{
/* GPIO can never have been requested */
WARN_ON(1);
@@ -384,7 +386,7 @@ static inline int gpiod_get_value_cansleep(const struct gpio_desc *desc)
}
static inline int gpiod_get_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
- int *value_array)
+ unsigned long *value_bitmap)
{
/* GPIO can never have been requested */
WARN_ON(1);
@@ -397,7 +399,7 @@ static inline void gpiod_set_value_cansleep(struct gpio_desc *desc, int value)
}
static inline void gpiod_set_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
- int *value_array)
+ unsigned long *value_bitmap)
{
/* GPIO can never have been requested */
WARN_ON(1);
@@ -410,7 +412,7 @@ static inline int gpiod_get_raw_value_cansleep(const struct gpio_desc *desc)
}
static inline int gpiod_get_raw_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
- int *value_array)
+ unsigned long *value_bitmap)
{
/* GPIO can never have been requested */
WARN_ON(1);
@@ -424,7 +426,7 @@ static inline void gpiod_set_raw_value_cansleep(struct gpio_desc *desc,
}
static inline int gpiod_set_raw_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
- int *value_array)
+ unsigned long *value_bitmap)
{
/* GPIO can never have been requested */
WARN_ON(1);
--
2.16.4


2018-09-07 16:47:04

by Tony Lindgren

[permalink] [raw]
Subject: Re: [PATCH v4 0/3] mtd: rawnand: ams-delta: Cleanups and optimizations

Hi,

* Janusz Krzysztofik <[email protected]> [180905 20:56]:
> On Wednesday, September 5, 2018 8:47:57 AM CEST Miquel Raynal wrote:
> > Patch 2/3 does not apply on nand/next. Indeed the driver does not look
> > the same as in the diff.
>
> That's because I built it on top of my former series from the mid of July,
> containing "[PATCH v2 2/3 v4] mtd: rawnand: ams-delta: use GPIO lookup table".
> It was acked by you, Miquel, and supposed to be merged via linux-omap tree.

Hmm I thought the plan was for dependencies to clear and then
merge the rest via various driver trees.. Or at least I don't have
the patch above tagged anywhere for me to merge. Then again, I
try to forget everything posted before -rc1 just to stay sane :)

> > I don't see any changes on my side that could
> > explain this so perhaps you could rebase on top of 4.19-rc2 (or
> > nand/next, as you wish) and resend the series?
>
> As far as I can see, Tony hasn't applied that series yet, so maybe I can still
> move that patch out of there and insert it into this series in front of the
> other 3 patches and resend. That would however make patch 3/3 of that old
> series depend on this one.
>
> Tony, what do you think?

Yes please resend based on v4.19-rc1 or MTD next. If there
are still pending dependencies, please let us know and we can
set up an immutable branch against v4.19-rc1 with those for
MTD and me to merge in as needed.

Regards,

Tony

2018-09-13 09:23:01

by Linus Walleij

[permalink] [raw]
Subject: Re: [PATCH v8 0/4] gpiolib: speed up GPIO array processing

On Wed, Sep 5, 2018 at 11:49 PM Janusz Krzysztofik <[email protected]> wrote:

> The goal is to boost performance of get/set array functions while
> processing GPIO arrays which represent pins of a signle chip in
> hardware order. If resulting performance is close to PIO, GPIO API
> can be used for data I/O without much loss of speed.

I applied the v8 to an immutable branch and pushed to kernelorg
so the build servers can churn it a bit, and if it works fine
then we can merge this into the devel branch and also set up
that as something other subsystems can pull in if they need it.

I'm really excited to merge this!

Yours,
Linus Walleij

2018-09-19 08:47:52

by Miquel Raynal

[permalink] [raw]
Subject: Re: [PATCH v4 0/3] mtd: rawnand: ams-delta: Cleanups and optimizations

Hi Janusz,

Tony Lindgren <[email protected]> wrote on Fri, 7 Sep 2018 09:45:24
-0700:

> Hi,
>
> * Janusz Krzysztofik <[email protected]> [180905 20:56]:
> > On Wednesday, September 5, 2018 8:47:57 AM CEST Miquel Raynal wrote:
> > > Patch 2/3 does not apply on nand/next. Indeed the driver does not look
> > > the same as in the diff.
> >
> > That's because I built it on top of my former series from the mid of July,
> > containing "[PATCH v2 2/3 v4] mtd: rawnand: ams-delta: use GPIO lookup table".
> > It was acked by you, Miquel, and supposed to be merged via linux-omap tree.
>
> Hmm I thought the plan was for dependencies to clear and then
> merge the rest via various driver trees.. Or at least I don't have
> the patch above tagged anywhere for me to merge. Then again, I
> try to forget everything posted before -rc1 just to stay sane :)
>
> > > I don't see any changes on my side that could
> > > explain this so perhaps you could rebase on top of 4.19-rc2 (or
> > > nand/next, as you wish) and resend the series?
> >
> > As far as I can see, Tony hasn't applied that series yet, so maybe I can still
> > move that patch out of there and insert it into this series in front of the
> > other 3 patches and resend. That would however make patch 3/3 of that old
> > series depend on this one.
> >
> > Tony, what do you think?
>
> Yes please resend based on v4.19-rc1 or MTD next. If there
> are still pending dependencies, please let us know and we can
> set up an immutable branch against v4.19-rc1 with those for
> MTD and me to merge in as needed.
>
> Regards,
>
> Tony

AFAIR I didn't received any new patches from you since Tony suggestion.
If you haven't done it already, please rebase against nand/next before
sending patches (internal API changed a bit, it might be worth fixing
the conflicts before sending them).


Thanks,
Miquèl

2018-09-19 18:56:33

by Linus Walleij

[permalink] [raw]
Subject: Re: [PATCH v8 0/4] gpiolib: speed up GPIO array processing

On Thu, Sep 13, 2018 at 2:22 AM Linus Walleij <[email protected]> wrote:
> On Wed, Sep 5, 2018 at 11:49 PM Janusz Krzysztofik <[email protected]> wrote:
>
> > The goal is to boost performance of get/set array functions while
> > processing GPIO arrays which represent pins of a signle chip in
> > hardware order. If resulting performance is close to PIO, GPIO API
> > can be used for data I/O without much loss of speed.
>
> I applied the v8 to an immutable branch and pushed to kernelorg
> so the build servers can churn it a bit, and if it works fine
> then we can merge this into the devel branch and also set up
> that as something other subsystems can pull in if they need it.
>
> I'm really excited to merge this!

The branch built with no problems, and now I merged this into
devel. If that also builds fine, I will let it hit linux-next so we can
stabilize it for v4.20.

Yours,
Linus Walleij

2018-09-19 22:53:24

by Janusz Krzysztofik

[permalink] [raw]
Subject: [PATCH v5 0/3] mtd: rawnand: ams-delta: Cleanups and optimizations


This series consist of possibly ready to apply patches extracted from
a former one titled "mtd: rawnand: ams-delta: Use GPIO API for data I/O".
Remaining patches implementing conversion of data I/O to GPIO have been
postponed until gpiolib is optimized to ensure sufficient performance.


Janusz Krzysztofik (3):
mtd: rawnand: ams-delta: show parent device in sysfs
mtd: rawnand: ams-delta: Use private structure
mtd: rawnand: ams-delta: Set port direction when needed


Changelog:
v5:
- rebased against just resubmitted "[PATCH v5] mtd: rawnand: ams-delta:
use GPIO lookup table" applied on top of nand/next

v4:
[PATCH v4 1/3] mtd: rawnand: ams-delta: show parent device in sysfs
- no changes.
[PATCH v4 2/3] mtd: rawnand: ams-delta: Use private structure
- no changes.
[PATCH v4 3/3] mtd: rawnand: ams-delta: Set port direction when
- formerly submitted as [PATCH v3 5/7], now rebased on top of 2/3 from
the top of the postponed "[PATCH v3 4/7] mtd: rawnand: ams-delta:
request data port GPIO resource",
- refactored as requested by Boris Brezillon, thanks.
Rrmoved from this series:
[PATCH v3 3/7] ARM: OMAP1: ams-delta: Provide GPIO lookup table for NAND
data port
- postponed, will be resubmitted when gpiolib optimizations are ready.
[PATCH v3 4/7] mtd: rawnand: ams-delta: request data port GPIO resource
- postponed, will be resubmitted when gpiolib optimizations are ready.
[PATCH v3 6/7] mtd: rawnand: ams-delta: Simplify pointer resolution
- no longer needed, the idea has been implemented in the refactored 3/3.
[RFC PATCH v3 7/7] mtd: rawnand: ams-delta: use GPIO API for data I/O
- postponed, will be resubmitted when gpiolib optimizations are ready.

v3:
[PATCH v3 1/7] mtd: rawnand: ams-delta: show parent device in sysfs
- renamed and an explanation added based on other similar patches on
Marek Vasut request, thanks.
[PATCH v3 2/7] mtd: rawnand: ams-delta: Use private structure
- no changes.
[PATCH v3 3/7] ARM: OMAP1: ams-delta: Provide GPIO lookup table for NAND
data port
- no changes.
[PATCH v3 4/7] mtd: rawnand: ams-delta: request data port GPIO resource
- no changes.
[PATCH v3 5/7] mtd: rawnand: ams-delta: Set port direction when needed
- modified to set port direction only when needed instead of on each
transfer as suggested by Boris, thanks, though I kept separate
*_next_byte() functions to maximize performance as much as possible,
- moved back in front of "mtd: rawnand: ams-delta: use GPIO API for data
I/O" with a comment added referring to the planned switch to GPIO API.
[PATCH v3 6/7] mtd: rawnand: ams-delta: Simplify pointer resolution
- moved back in front of "mtd: rawnand: ams-delta: use GPIO API for data
I/O" on Boris request, thanks.
[RFC PATCH v3 7/7] mtd: rawnand: ams-delta: use GPIO API for data I/O
- rebased back on top of the two mentioned above,
- not intended to apply it yet due to performance issues on Amstrad Delta.
Removed from the series:
[RFC PATCH v2 09/12] gpiolib: Identify GPIO descriptor arrays with direct
mapping
[RFC PATCH v2 10/12] gpiolib: Introduce bitmap get/set array API extension
[RFC PATCH v2 11/12] mtd: rawnand: ams-delta: Use GPIO API bitmap extension
[RFC PATCH v2 12/12] gpiolib: Add fast processing path to bitmap API functions
- intended to be still iterated in a follow up series until performance
issues are resolved.
[RFC PATCH v2 06/12] ARM: OMAP1: ams-delta: drop obsolete NAND resources
- postponed until acceptable performance on Amstrad Delta is achieved.

v2:
[RFC PATCH v2 00/12] mtd: rawnand: ams-delta: Use GPIO API for data I/O
- renamed from former [RFC PATCH 0/8] mtd: rawnand: ams-delta: Use
gpio-omap accessors for data I/O
[RFC PATCH v2 01/12] mtd: rawnand: ams-delta: Assign mtd->dev.parent,
not mtd->owner
- split out from former [RFC PATCH 1/8] on Boris request, thanks.
[RFC PATCH v2 02/12] mtd: rawnand: ams-delta: Use private structure
- remaining part of the former [RFC PATCH 1/8].
[RFC PATCH v2 03/12] ARM: OMAP1: ams-delta: Provide GPIO lookup table
for NAND data port
- split out from former [RFC PATCH 5/8] on Boris requesst, thanks,
[RFC PATCH v2 04/12] mtd: rawnand: ams-delta: request data port GPIO resource
- remaining part of the former [RFC PATCH 5/8],
[RFC PATCH v2 05/12] mtd: rawnand: ams-delta: use GPIO API for data read/write
- reworked from former [RFC PATCH 8/8] on Boris requesst to use pure
GPIO API, thanks,
- moved up in front of former [RFC PATCH 3/8] on Boris request, thanks.
[RFC PATCH v2 06/12] ARM: OMAP1: ams-delta: drop obsolete NAND resources
- split out from former [RFC PATCH 8/8].
[RFC PATCH v2 07/12] mtd: rawnand: ams-delta: Set port direction once per
transfer
- reworked from former [RFC PATCH 3/8] on top of [RFC PATCH v2 05/12].
[RFC PATCH v2 08/12] mtd: rawnand: ams-delta: Simplify pointer resolution
on read/write
- reworked from former [RFC PATCH 4/8] on top of [RFC PATCH v2 08/12],
- renamed from 'Optimize' to 'Simplify' on Boris request, thanks.
[RFC PATCH v2 09/12] gpiolib: Identify GPIO descriptor arrays with direct
mapping
- new patch.
[RFC PATCH v2 10/12] gpiolib: Introduce bitmap get/set array API extension
- new patch.
[RFC PATCH v2 11/12] mtd: rawnand: ams-delta: Use GPIO API bitmap extension
- new patch.
[RFC PATCH v2 12/12] gpiolib: Add fast processing path to bitmap API functions
- new patch.
Removed from the series:
[RFC PATCH 2/8] mtd: rawnand: ams-delta: Write protect device during probe
- postponed on Boris request, thanks.
[RFC PATCH 6/8] gpio: omap: Add get/set_multiple() callbacks
- already applied by Linux in linux-gpio tree, thanks.
[RFC PATCH 7/8] mtd: rawnand: ams-delta: Check sanity of data GPIO resource
- most controversial one, no longer needed after switching to GPIO API.


diffstat:
ams-delta.c | 181 +++++++++++++++++++++++++++++++++++-------------------------
1 file changed, 107 insertions(+), 74 deletions(-)


2018-09-19 22:53:41

by Janusz Krzysztofik

[permalink] [raw]
Subject: [PATCH v5 2/3] mtd: rawnand: ams-delta: Use private structure

Introduce a driver private structure and allocate it on device probe.
Use it for storing nand_chip structure, GPIO descriptors prevoiusly
stored in static variables as well as io_base pointer previously passed
as nand controller data or platform driver data. Subsequent patches
may populate the structure with more members as needed.

Signed-off-by: Janusz Krzysztofik <[email protected]>
Reviewed-by: Boris Brezillon <[email protected]>
---
drivers/mtd/nand/raw/ams-delta.c | 122 +++++++++++++++++++++------------------
1 file changed, 66 insertions(+), 56 deletions(-)

diff --git a/drivers/mtd/nand/raw/ams-delta.c b/drivers/mtd/nand/raw/ams-delta.c
index efe12066dfbc..eb2aeadc3b65 100644
--- a/drivers/mtd/nand/raw/ams-delta.c
+++ b/drivers/mtd/nand/raw/ams-delta.c
@@ -34,14 +34,18 @@
/*
* MTD structure for E3 (Delta)
*/
-static struct mtd_info *ams_delta_mtd = NULL;
-static struct gpio_desc *gpiod_rdy;
-static struct gpio_desc *gpiod_nce;
-static struct gpio_desc *gpiod_nre;
-static struct gpio_desc *gpiod_nwp;
-static struct gpio_desc *gpiod_nwe;
-static struct gpio_desc *gpiod_ale;
-static struct gpio_desc *gpiod_cle;
+
+struct ams_delta_nand {
+ struct nand_chip nand_chip;
+ struct gpio_desc *gpiod_rdy;
+ struct gpio_desc *gpiod_nce;
+ struct gpio_desc *gpiod_nre;
+ struct gpio_desc *gpiod_nwp;
+ struct gpio_desc *gpiod_nwe;
+ struct gpio_desc *gpiod_ale;
+ struct gpio_desc *gpiod_cle;
+ void __iomem *io_base;
+};

/*
* Define partitions for flash devices
@@ -70,25 +74,27 @@ static const struct mtd_partition partition_info[] = {

static void ams_delta_write_byte(struct nand_chip *this, u_char byte)
{
- void __iomem *io_base = (void __iomem *)nand_get_controller_data(this);
+ struct ams_delta_nand *priv = nand_get_controller_data(this);
+ void __iomem *io_base = priv->io_base;

writew(0, io_base + OMAP_MPUIO_IO_CNTL);
writew(byte, this->legacy.IO_ADDR_W);
- gpiod_set_value(gpiod_nwe, 0);
+ gpiod_set_value(priv->gpiod_nwe, 0);
ndelay(40);
- gpiod_set_value(gpiod_nwe, 1);
+ gpiod_set_value(priv->gpiod_nwe, 1);
}

static u_char ams_delta_read_byte(struct nand_chip *this)
{
u_char res;
- void __iomem *io_base = (void __iomem *)nand_get_controller_data(this);
+ struct ams_delta_nand *priv = nand_get_controller_data(this);
+ void __iomem *io_base = priv->io_base;

- gpiod_set_value(gpiod_nre, 0);
+ gpiod_set_value(priv->gpiod_nre, 0);
ndelay(40);
writew(~0, io_base + OMAP_MPUIO_IO_CNTL);
res = readw(this->legacy.IO_ADDR_R);
- gpiod_set_value(gpiod_nre, 1);
+ gpiod_set_value(priv->gpiod_nre, 1);

return res;
}
@@ -121,11 +127,12 @@ static void ams_delta_read_buf(struct nand_chip *this, u_char *buf, int len)
static void ams_delta_hwcontrol(struct nand_chip *this, int cmd,
unsigned int ctrl)
{
+ struct ams_delta_nand *priv = nand_get_controller_data(this);

if (ctrl & NAND_CTRL_CHANGE) {
- gpiod_set_value(gpiod_nce, !(ctrl & NAND_NCE));
- gpiod_set_value(gpiod_cle, !!(ctrl & NAND_CLE));
- gpiod_set_value(gpiod_ale, !!(ctrl & NAND_ALE));
+ gpiod_set_value(priv->gpiod_nce, !(ctrl & NAND_NCE));
+ gpiod_set_value(priv->gpiod_cle, !!(ctrl & NAND_CLE));
+ gpiod_set_value(priv->gpiod_ale, !!(ctrl & NAND_ALE));
}

if (cmd != NAND_CMD_NONE)
@@ -134,7 +141,9 @@ static void ams_delta_hwcontrol(struct nand_chip *this, int cmd,

static int ams_delta_nand_ready(struct nand_chip *this)
{
- return gpiod_get_value(gpiod_rdy);
+ struct ams_delta_nand *priv = nand_get_controller_data(this);
+
+ return gpiod_get_value(priv->gpiod_rdy);
}


@@ -143,7 +152,9 @@ static int ams_delta_nand_ready(struct nand_chip *this)
*/
static int ams_delta_init(struct platform_device *pdev)
{
+ struct ams_delta_nand *priv;
struct nand_chip *this;
+ struct mtd_info *mtd;
struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
void __iomem *io_base;
int err = 0;
@@ -152,15 +163,16 @@ static int ams_delta_init(struct platform_device *pdev)
return -ENXIO;

/* Allocate memory for MTD device structure and private data */
- this = kzalloc(sizeof(struct nand_chip), GFP_KERNEL);
- if (!this) {
+ priv = devm_kzalloc(&pdev->dev, sizeof(struct ams_delta_nand),
+ GFP_KERNEL);
+ if (!priv) {
pr_warn("Unable to allocate E3 NAND MTD device structure.\n");
- err = -ENOMEM;
- goto out;
+ return -ENOMEM;
}
+ this = &priv->nand_chip;

- ams_delta_mtd = nand_to_mtd(this);
- ams_delta_mtd->dev.parent = &pdev->dev;
+ mtd = nand_to_mtd(this);
+ mtd->dev.parent = &pdev->dev;

/*
* Don't try to request the memory region from here,
@@ -175,7 +187,8 @@ static int ams_delta_init(struct platform_device *pdev)
goto out_free;
}

- nand_set_controller_data(this, (void *)io_base);
+ priv->io_base = io_base;
+ nand_set_controller_data(this, priv);

/* Set address of NAND IO lines */
this->legacy.IO_ADDR_R = io_base + OMAP_MPUIO_INPUT_LATCH;
@@ -185,14 +198,14 @@ static int ams_delta_init(struct platform_device *pdev)
this->legacy.read_buf = ams_delta_read_buf;
this->legacy.cmd_ctrl = ams_delta_hwcontrol;

- gpiod_rdy = devm_gpiod_get_optional(&pdev->dev, "rdy", GPIOD_IN);
- if (IS_ERR(gpiod_rdy)) {
- err = PTR_ERR(gpiod_rdy);
+ priv->gpiod_rdy = devm_gpiod_get_optional(&pdev->dev, "rdy", GPIOD_IN);
+ if (IS_ERR(priv->gpiod_rdy)) {
+ err = PTR_ERR(priv->gpiod_rdy);
dev_warn(&pdev->dev, "RDY GPIO request failed (%d)\n", err);
goto out_mtd;
}

- if (gpiod_rdy)
+ if (priv->gpiod_rdy)
this->legacy.dev_ready = ams_delta_nand_ready;

/* 25 us command delay time */
@@ -200,47 +213,47 @@ static int ams_delta_init(struct platform_device *pdev)
this->ecc.mode = NAND_ECC_SOFT;
this->ecc.algo = NAND_ECC_HAMMING;

- platform_set_drvdata(pdev, io_base);
+ platform_set_drvdata(pdev, priv);

/* Set chip enabled, but */
- gpiod_nwp = devm_gpiod_get(&pdev->dev, "nwp", GPIOD_OUT_HIGH);
- if (IS_ERR(gpiod_nwp)) {
- err = PTR_ERR(gpiod_nwp);
+ priv->gpiod_nwp = devm_gpiod_get(&pdev->dev, "nwp", GPIOD_OUT_HIGH);
+ if (IS_ERR(priv->gpiod_nwp)) {
+ err = PTR_ERR(priv->gpiod_nwp);
dev_err(&pdev->dev, "NWP GPIO request failed (%d)\n", err);
goto out_mtd;
}

- gpiod_nce = devm_gpiod_get(&pdev->dev, "nce", GPIOD_OUT_HIGH);
- if (IS_ERR(gpiod_nce)) {
- err = PTR_ERR(gpiod_nce);
+ priv->gpiod_nce = devm_gpiod_get(&pdev->dev, "nce", GPIOD_OUT_HIGH);
+ if (IS_ERR(priv->gpiod_nce)) {
+ err = PTR_ERR(priv->gpiod_nce);
dev_err(&pdev->dev, "NCE GPIO request failed (%d)\n", err);
goto out_mtd;
}

- gpiod_nre = devm_gpiod_get(&pdev->dev, "nre", GPIOD_OUT_HIGH);
- if (IS_ERR(gpiod_nre)) {
- err = PTR_ERR(gpiod_nre);
+ priv->gpiod_nre = devm_gpiod_get(&pdev->dev, "nre", GPIOD_OUT_HIGH);
+ if (IS_ERR(priv->gpiod_nre)) {
+ err = PTR_ERR(priv->gpiod_nre);
dev_err(&pdev->dev, "NRE GPIO request failed (%d)\n", err);
goto out_mtd;
}

- gpiod_nwe = devm_gpiod_get(&pdev->dev, "nwe", GPIOD_OUT_HIGH);
- if (IS_ERR(gpiod_nwe)) {
- err = PTR_ERR(gpiod_nwe);
+ priv->gpiod_nwe = devm_gpiod_get(&pdev->dev, "nwe", GPIOD_OUT_HIGH);
+ if (IS_ERR(priv->gpiod_nwe)) {
+ err = PTR_ERR(priv->gpiod_nwe);
dev_err(&pdev->dev, "NWE GPIO request failed (%d)\n", err);
goto out_mtd;
}

- gpiod_ale = devm_gpiod_get(&pdev->dev, "ale", GPIOD_OUT_LOW);
- if (IS_ERR(gpiod_ale)) {
- err = PTR_ERR(gpiod_ale);
+ priv->gpiod_ale = devm_gpiod_get(&pdev->dev, "ale", GPIOD_OUT_LOW);
+ if (IS_ERR(priv->gpiod_ale)) {
+ err = PTR_ERR(priv->gpiod_ale);
dev_err(&pdev->dev, "ALE GPIO request failed (%d)\n", err);
goto out_mtd;
}

- gpiod_cle = devm_gpiod_get(&pdev->dev, "cle", GPIOD_OUT_LOW);
- if (IS_ERR(gpiod_cle)) {
- err = PTR_ERR(gpiod_cle);
+ priv->gpiod_cle = devm_gpiod_get(&pdev->dev, "cle", GPIOD_OUT_LOW);
+ if (IS_ERR(priv->gpiod_cle)) {
+ err = PTR_ERR(priv->gpiod_cle);
dev_err(&pdev->dev, "CLE GPIO request failed (%d)\n", err);
goto out_mtd;
}
@@ -251,15 +264,13 @@ static int ams_delta_init(struct platform_device *pdev)
goto out_mtd;

/* Register the partitions */
- mtd_device_register(ams_delta_mtd, partition_info,
- ARRAY_SIZE(partition_info));
+ mtd_device_register(mtd, partition_info, ARRAY_SIZE(partition_info));

goto out;

out_mtd:
iounmap(io_base);
out_free:
- kfree(this);
out:
return err;
}
@@ -269,16 +280,15 @@ static int ams_delta_init(struct platform_device *pdev)
*/
static int ams_delta_cleanup(struct platform_device *pdev)
{
- void __iomem *io_base = platform_get_drvdata(pdev);
+ struct ams_delta_nand *priv = platform_get_drvdata(pdev);
+ struct mtd_info *mtd = nand_to_mtd(&priv->nand_chip);
+ void __iomem *io_base = priv->io_base;

/* Release resources, unregister device */
- nand_release(mtd_to_nand(ams_delta_mtd));
+ nand_release(mtd_to_nand(mtd));

iounmap(io_base);

- /* Free the MTD device structure */
- kfree(mtd_to_nand(ams_delta_mtd));
-
return 0;
}

--
2.16.4


2018-09-19 22:54:13

by Janusz Krzysztofik

[permalink] [raw]
Subject: [PATCH v5 3/3] mtd: rawnand: ams-delta: Set port direction when needed

In its current shape, the driver sets data port direction before each
byte read/write operation, even during multi-byte transfers. Improve
performance of the driver by setting the port direction only when
needed.

This optimisation will become particularly important as soon as
planned conversion of the driver to GPIO API for data I/O will be
implemented.

Reviewed-by: Boris Brezillon <[email protected]>
Signed-off-by: Janusz Krzysztofik <[email protected]>
---
drivers/mtd/nand/raw/ams-delta.c | 57 ++++++++++++++++++++++++++++------------
1 file changed, 40 insertions(+), 17 deletions(-)

diff --git a/drivers/mtd/nand/raw/ams-delta.c b/drivers/mtd/nand/raw/ams-delta.c
index eb2aeadc3b65..5ba180a291eb 100644
--- a/drivers/mtd/nand/raw/ams-delta.c
+++ b/drivers/mtd/nand/raw/ams-delta.c
@@ -45,6 +45,7 @@ struct ams_delta_nand {
struct gpio_desc *gpiod_ale;
struct gpio_desc *gpiod_cle;
void __iomem *io_base;
+ bool data_in;
};

/*
@@ -72,48 +73,64 @@ static const struct mtd_partition partition_info[] = {
.size = 3 * SZ_256K },
};

-static void ams_delta_write_byte(struct nand_chip *this, u_char byte)
+static void ams_delta_io_write(struct ams_delta_nand *priv, u_char byte)
{
- struct ams_delta_nand *priv = nand_get_controller_data(this);
- void __iomem *io_base = priv->io_base;
-
- writew(0, io_base + OMAP_MPUIO_IO_CNTL);
- writew(byte, this->legacy.IO_ADDR_W);
+ writew(byte, priv->nand_chip.legacy.IO_ADDR_W);
gpiod_set_value(priv->gpiod_nwe, 0);
ndelay(40);
gpiod_set_value(priv->gpiod_nwe, 1);
}

-static u_char ams_delta_read_byte(struct nand_chip *this)
+static u_char ams_delta_io_read(struct ams_delta_nand *priv)
{
u_char res;
- struct ams_delta_nand *priv = nand_get_controller_data(this);
- void __iomem *io_base = priv->io_base;

gpiod_set_value(priv->gpiod_nre, 0);
ndelay(40);
- writew(~0, io_base + OMAP_MPUIO_IO_CNTL);
- res = readw(this->legacy.IO_ADDR_R);
+ res = readw(priv->nand_chip.legacy.IO_ADDR_R);
gpiod_set_value(priv->gpiod_nre, 1);

return res;
}

+static void ams_delta_dir_input(struct ams_delta_nand *priv, bool in)
+{
+ writew(in ? ~0 : 0, priv->io_base + OMAP_MPUIO_IO_CNTL);
+ priv->data_in = in;
+}
+
static void ams_delta_write_buf(struct nand_chip *this, const u_char *buf,
int len)
{
+ struct ams_delta_nand *priv = nand_get_controller_data(this);
int i;

- for (i=0; i<len; i++)
- ams_delta_write_byte(this, buf[i]);
+ if (priv->data_in)
+ ams_delta_dir_input(priv, false);
+
+ for (i = 0; i < len; i++)
+ ams_delta_io_write(priv, buf[i]);
}

static void ams_delta_read_buf(struct nand_chip *this, u_char *buf, int len)
{
+ struct ams_delta_nand *priv = nand_get_controller_data(this);
int i;

- for (i=0; i<len; i++)
- buf[i] = ams_delta_read_byte(this);
+ if (!priv->data_in)
+ ams_delta_dir_input(priv, true);
+
+ for (i = 0; i < len; i++)
+ buf[i] = ams_delta_io_read(priv);
+}
+
+static u_char ams_delta_read_byte(struct nand_chip *this)
+{
+ u_char res;
+
+ ams_delta_read_buf(this, &res, 1);
+
+ return res;
}

/*
@@ -135,8 +152,11 @@ static void ams_delta_hwcontrol(struct nand_chip *this, int cmd,
gpiod_set_value(priv->gpiod_ale, !!(ctrl & NAND_ALE));
}

- if (cmd != NAND_CMD_NONE)
- ams_delta_write_byte(this, cmd);
+ if (cmd != NAND_CMD_NONE) {
+ u_char byte = cmd;
+
+ ams_delta_write_buf(this, &byte, 1);
+ }
}

static int ams_delta_nand_ready(struct nand_chip *this)
@@ -258,6 +278,9 @@ static int ams_delta_init(struct platform_device *pdev)
goto out_mtd;
}

+ /* Initialize data port direction to a known state */
+ ams_delta_dir_input(priv, true);
+
/* Scan to find existence of the device */
err = nand_scan(this, 1);
if (err)
--
2.16.4


2018-09-19 22:54:30

by Janusz Krzysztofik

[permalink] [raw]
Subject: [PATCH v5 1/3] mtd: rawnand: ams-delta: show parent device in sysfs

Fix a bug where parent device symlinks aren't shown in sysfs.

While at it, make use of the default owner set by mtdcore.

Signed-off-by: Janusz Krzysztofik <[email protected]>
Reviewed-by: Boris Brezillon <[email protected]>
---
drivers/mtd/nand/raw/ams-delta.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/mtd/nand/raw/ams-delta.c b/drivers/mtd/nand/raw/ams-delta.c
index a2d2dfc55984..efe12066dfbc 100644
--- a/drivers/mtd/nand/raw/ams-delta.c
+++ b/drivers/mtd/nand/raw/ams-delta.c
@@ -160,7 +160,7 @@ static int ams_delta_init(struct platform_device *pdev)
}

ams_delta_mtd = nand_to_mtd(this);
- ams_delta_mtd->owner = THIS_MODULE;
+ ams_delta_mtd->dev.parent = &pdev->dev;

/*
* Don't try to request the memory region from here,
--
2.16.4


2018-09-20 10:13:45

by Marek Szyprowski

[permalink] [raw]
Subject: Re: [PATCH v7 4/4] gpiolib: Implement fast processing path in get/set array

Hi All,

On 2018-09-02 14:01, Janusz Krzysztofik wrote:
> Certain GPIO descriptor arrays returned by gpio_get_array() may contain
> information on direct mapping of array members to pins of a single GPIO
> chip in hardware order. In such cases, bitmaps of values can be passed
> directly from/to the chip's .get/set_multiple() callbacks without
> wasting time on iterations.
>
> Add respective code to gpiod_get/set_array_bitmap_complex() functions.
> Pins not applicable for fast path are processed as before, skipping
> over the 'fast' ones.
>
> Cc: Jonathan Corbet <[email protected]>
> Signed-off-by: Janusz Krzysztofik <[email protected]>

I've just noticed that this patch landed in today's linux-next. Sadly it
breaks booting of Exynos5250-based Samsung Snow Chromebook (ARM 32bit,
device-tree source arch/arm/boot/dts/exynos5250-snow.dts).

Booting hangs after detecting MMC cards. Reverting this patch fixes the
boot. I will try later to add some debugs and investigate it further what
really happens when booting hangs.

> ---
> Documentation/driver-api/gpio/board.rst | 15 ++++++
> Documentation/driver-api/gpio/consumer.rst | 8 +++
> drivers/gpio/gpiolib.c | 87 ++++++++++++++++++++++++++++--
> 3 files changed, 105 insertions(+), 5 deletions(-)
>
> diff --git a/Documentation/driver-api/gpio/board.rst b/Documentation/driver-api/gpio/board.rst
> index 2c112553df84..c66821e033c2 100644
> --- a/Documentation/driver-api/gpio/board.rst
> +++ b/Documentation/driver-api/gpio/board.rst
> @@ -193,3 +193,18 @@ And the table can be added to the board code as follows::
>
> The line will be hogged as soon as the gpiochip is created or - in case the
> chip was created earlier - when the hog table is registered.
> +
> +Arrays of pins
> +--------------
> +In addition to requesting pins belonging to a function one by one, a device may
> +also request an array of pins assigned to the function. The way those pins are
> +mapped to the device determines if the array qualifies for fast bitmap
> +processing. If yes, a bitmap is passed over get/set array functions directly
> +between a caller and a respective .get/set_multiple() callback of a GPIO chip.
> +
> +In order to qualify for fast bitmap processing, the pin mapping must meet the
> +following requirements:
> +- it must belong to the same chip as other 'fast' pins of the function,
> +- its index within the function must match its hardware number within the chip.
> +
> +Open drain and open source pins are excluded from fast bitmap output processing.
> diff --git a/Documentation/driver-api/gpio/consumer.rst b/Documentation/driver-api/gpio/consumer.rst
> index 0afd95a12b10..cf992e5ab976 100644
> --- a/Documentation/driver-api/gpio/consumer.rst
> +++ b/Documentation/driver-api/gpio/consumer.rst
> @@ -388,6 +388,14 @@ array_info should be set to NULL.
> Note that for optimal performance GPIOs belonging to the same chip should be
> contiguous within the array of descriptors.
>
> +Still better performance may be achieved if array indexes of the descriptors
> +match hardware pin numbers of a single chip. If an array passed to a get/set
> +array function matches the one obtained from gpiod_get_array() and array_info
> +associated with the array is also passed, the function may take a fast bitmap
> +processing path, passing the value_bitmap argument directly to the respective
> +.get/set_multiple() callback of the chip. That allows for utilization of GPIO
> +banks as data I/O ports without much loss of performance.
> +
> The return value of gpiod_get_array_value() and its variants is 0 on success
> or negative on error. Note the difference to gpiod_get_value(), which returns
> 0 or 1 on success to convey the GPIO value. With the array functions, the GPIO
> diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
> index cef6ee31fe05..b9d083fb13ee 100644
> --- a/drivers/gpio/gpiolib.c
> +++ b/drivers/gpio/gpiolib.c
> @@ -2789,7 +2789,36 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep,
> struct gpio_array *array_info,
> unsigned long *value_bitmap)
> {
> - int i = 0;
> + int err, i = 0;
> +
> + /*
> + * Validate array_info against desc_array and its size.
> + * It should immediately follow desc_array if both
> + * have been obtained from the same gpiod_get_array() call.
> + */
> + if (array_info && array_info->desc == desc_array &&
> + array_size <= array_info->size &&
> + (void *)array_info == desc_array + array_info->size) {
> + if (!can_sleep)
> + WARN_ON(array_info->chip->can_sleep);
> +
> + err = gpio_chip_get_multiple(array_info->chip,
> + array_info->get_mask,
> + value_bitmap);
> + if (err)
> + return err;
> +
> + if (!raw && !bitmap_empty(array_info->invert_mask, array_size))
> + bitmap_xor(value_bitmap, value_bitmap,
> + array_info->invert_mask, array_size);
> +
> + if (bitmap_full(array_info->get_mask, array_size))
> + return 0;
> +
> + i = find_first_zero_bit(array_info->get_mask, array_size);
> + } else {
> + array_info = NULL;
> + }
>
> while (i < array_size) {
> struct gpio_chip *chip = desc_array[i]->gdev->chip;
> @@ -2820,7 +2849,12 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep,
> int hwgpio = gpio_chip_hwgpio(desc);
>
> __set_bit(hwgpio, mask);
> - i++;
> +
> + if (array_info)
> + find_next_zero_bit(array_info->get_mask,
> + array_size, i);
> + else
> + i++;
> } while ((i < array_size) &&
> (desc_array[i]->gdev->chip == chip));
>
> @@ -2831,7 +2865,7 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep,
> return ret;
> }
>
> - for (j = first; j < i; j++) {
> + for (j = first; j < i; ) {
> const struct gpio_desc *desc = desc_array[j];
> int hwgpio = gpio_chip_hwgpio(desc);
> int value = test_bit(hwgpio, bits);
> @@ -2840,6 +2874,11 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep,
> value = !value;
> __assign_bit(j, value_bitmap, value);
> trace_gpio_value(desc_to_gpio(desc), 1, value);
> +
> + if (array_info)
> + find_next_zero_bit(array_info->get_mask, i, j);
> + else
> + j++;
> }
>
> if (mask != fastpath)
> @@ -3041,6 +3080,32 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep,
> {
> int i = 0;
>
> + /*
> + * Validate array_info against desc_array and its size.
> + * It should immediately follow desc_array if both
> + * have been obtained from the same gpiod_get_array() call.
> + */
> + if (array_info && array_info->desc == desc_array &&
> + array_size <= array_info->size &&
> + (void *)array_info == desc_array + array_info->size) {
> + if (!can_sleep)
> + WARN_ON(array_info->chip->can_sleep);
> +
> + if (!raw && !bitmap_empty(array_info->invert_mask, array_size))
> + bitmap_xor(value_bitmap, value_bitmap,
> + array_info->invert_mask, array_size);
> +
> + gpio_chip_set_multiple(array_info->chip, array_info->set_mask,
> + value_bitmap);
> +
> + if (bitmap_full(array_info->set_mask, array_size))
> + return 0;
> +
> + i = find_first_zero_bit(array_info->set_mask, array_size);
> + } else {
> + array_info = NULL;
> + }
> +
> while (i < array_size) {
> struct gpio_chip *chip = desc_array[i]->gdev->chip;
> unsigned long fastpath[2 * BITS_TO_LONGS(FASTPATH_NGPIO)];
> @@ -3068,7 +3133,14 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep,
> int hwgpio = gpio_chip_hwgpio(desc);
> int value = test_bit(i, value_bitmap);
>
> - if (!raw && test_bit(FLAG_ACTIVE_LOW, &desc->flags))
> + /*
> + * Pins applicable for fast input but not for
> + * fast output processing may have been already
> + * inverted inside the fast path, skip them.
> + */
> + if (!raw && !(array_info &&
> + test_bit(i, array_info->invert_mask)) &&
> + test_bit(FLAG_ACTIVE_LOW, &desc->flags))
> value = !value;
> trace_gpio_value(desc_to_gpio(desc), 0, value);
> /*
> @@ -3087,7 +3159,12 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep,
> __clear_bit(hwgpio, bits);
> count++;
> }
> - i++;
> +
> + if (array_info)
> + find_next_zero_bit(array_info->set_mask,
> + array_size, i);
> + else
> + i++;
> } while ((i < array_size) &&
> (desc_array[i]->gdev->chip == chip));
> /* push collected bits to outputs */
>

Best regards
--
Marek Szyprowski, PhD
Samsung R&D Institute Poland


2018-09-20 15:47:27

by Janusz Krzysztofik

[permalink] [raw]
Subject: Re: [PATCH v7 4/4] gpiolib: Implement fast processing path in get/set array

On Thursday, September 20, 2018 12:11:48 PM CEST Marek Szyprowski wrote:
> Hi All,
>
> On 2018-09-02 14:01, Janusz Krzysztofik wrote:
> > Certain GPIO descriptor arrays returned by gpio_get_array() may contain
> > information on direct mapping of array members to pins of a single GPIO
> > chip in hardware order. In such cases, bitmaps of values can be passed
> > directly from/to the chip's .get/set_multiple() callbacks without
> > wasting time on iterations.
> >
> > Add respective code to gpiod_get/set_array_bitmap_complex() functions.
> > Pins not applicable for fast path are processed as before, skipping
> > over the 'fast' ones.
> >
> > Cc: Jonathan Corbet <[email protected]>
> > Signed-off-by: Janusz Krzysztofik <[email protected]>
>
> I've just noticed that this patch landed in today's linux-next. Sadly it
> breaks booting of Exynos5250-based Samsung Snow Chromebook (ARM 32bit,
> device-tree source arch/arm/boot/dts/exynos5250-snow.dts).
>
> Booting hangs after detecting MMC cards. Reverting this patch fixes the
> boot. I will try later to add some debugs and investigate it further what
> really happens when booting hangs.

Hi Marek,

Thanks for reporting. Could you please try the following fix?

Thanks,
Janusz

From d7ecd435bfb4972766b63ac383a43875700c7452 Mon Sep 17 00:00:00 2001
From: Janusz Krzysztofik <[email protected]>
Date: Thu, 20 Sep 2018 17:37:21 +0200
Subject: [PATCH] gpiolib: Fix bitmap index not updated

While skipping fast path bits, bitmap index is not updated with next
found zero bit position. Fix it.

Signed-off-by: Janusz Krzysztofik <[email protected]>
---
drivers/gpio/gpiolib.c | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index a53d17745d21..5bc3447949c9 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -2880,7 +2880,7 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep,
__set_bit(hwgpio, mask);

if (array_info)
- find_next_zero_bit(array_info->get_mask,
+ i = find_next_zero_bit(array_info->get_mask,
array_size, i);
else
i++;
@@ -2905,7 +2905,8 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep,
trace_gpio_value(desc_to_gpio(desc), 1, value);

if (array_info)
- find_next_zero_bit(array_info->get_mask, i, j);
+ i = find_next_zero_bit(array_info->get_mask, i,
+ j);
else
j++;
}
@@ -3192,7 +3193,7 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep,
}

if (array_info)
- find_next_zero_bit(array_info->set_mask,
+ i = find_next_zero_bit(array_info->set_mask,
array_size, i);
else
i++;
--
2.16.4





2018-09-20 15:49:43

by Linus Walleij

[permalink] [raw]
Subject: Re: [PATCH v7 4/4] gpiolib: Implement fast processing path in get/set array

On Thu, Sep 20, 2018 at 3:11 AM Marek Szyprowski
<[email protected]> wrote:

> I've just noticed that this patch landed in today's linux-next. Sadly it
> breaks booting of Exynos5250-based Samsung Snow Chromebook (ARM 32bit,
> device-tree source arch/arm/boot/dts/exynos5250-snow.dts).

Thanks for testing on this platform!

> Booting hangs after detecting MMC cards. Reverting this patch fixes the
> boot. I will try later to add some debugs and investigate it further what
> really happens when booting hangs.

How typical. I hope we can fix it, because this should mean speedups
for your platform.

Yours,
Linus Walleij

2018-09-20 16:21:16

by Janusz Krzysztofik

[permalink] [raw]
Subject: Re: [PATCH v7 4/4] gpiolib: Implement fast processing path in get/set array

On Thursday, September 20, 2018 5:48:22 PM CEST Janusz Krzysztofik wrote:
> On Thursday, September 20, 2018 12:11:48 PM CEST Marek Szyprowski wrote:
> > Hi All,
> >
> > On 2018-09-02 14:01, Janusz Krzysztofik wrote:
> > > Certain GPIO descriptor arrays returned by gpio_get_array() may contain
> > > information on direct mapping of array members to pins of a single GPIO
> > > chip in hardware order. In such cases, bitmaps of values can be passed
> > > directly from/to the chip's .get/set_multiple() callbacks without
> > > wasting time on iterations.
> > >
> > > Add respective code to gpiod_get/set_array_bitmap_complex() functions.
> > > Pins not applicable for fast path are processed as before, skipping
> > > over the 'fast' ones.
> > >
> > > Cc: Jonathan Corbet <[email protected]>
> > > Signed-off-by: Janusz Krzysztofik <[email protected]>
> >
> > I've just noticed that this patch landed in today's linux-next. Sadly it
> > breaks booting of Exynos5250-based Samsung Snow Chromebook (ARM 32bit,
> > device-tree source arch/arm/boot/dts/exynos5250-snow.dts).
> >
> > Booting hangs after detecting MMC cards. Reverting this patch fixes the
> > boot. I will try later to add some debugs and investigate it further what
> > really happens when booting hangs.
>
> Hi Marek,
>
> Thanks for reporting. Could you please try the following fix?

Hi again,

I realized the patch was not correct, j, not i, should be updated in second
hunk. Please try the following one.

Thanks,
Janusz

From a919c504850f6cb40e8e81267a3a37537f7c4fd4 Mon Sep 17 00:00:00 2001
From: Janusz Krzysztofik <[email protected]>
Date: Thu, 20 Sep 2018 17:37:21 +0200
Subject: [PATCH] gpiolib: Fix bitmap index not updated

While skipping fast path bits, bitmap index is not updated with next
found zero bit position. Fix it.

Signed-off-by: Janusz Krzysztofik <[email protected]>
---
drivers/gpio/gpiolib.c | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index a53d17745d21..369bdd358fcc 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -2880,7 +2880,7 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep,
__set_bit(hwgpio, mask);

if (array_info)
- find_next_zero_bit(array_info->get_mask,
+ i = find_next_zero_bit(array_info->get_mask,
array_size, i);
else
i++;
@@ -2905,7 +2905,8 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep,
trace_gpio_value(desc_to_gpio(desc), 1, value);

if (array_info)
- find_next_zero_bit(array_info->get_mask, i, j);
+ j = find_next_zero_bit(array_info->get_mask, i,
+ j);
else
j++;
}
@@ -3192,7 +3193,7 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep,
}

if (array_info)
- find_next_zero_bit(array_info->set_mask,
+ i = find_next_zero_bit(array_info->set_mask,
array_size, i);
else
i++;
--
2.16.4





2018-09-20 18:08:44

by Dan Carpenter

[permalink] [raw]
Subject: Re: [PATCH v7 4/4] gpiolib: Implement fast processing path in get/set array

On Thu, Sep 20, 2018 at 05:48:22PM +0200, Janusz Krzysztofik wrote:
> diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
> index a53d17745d21..5bc3447949c9 100644
> --- a/drivers/gpio/gpiolib.c
> +++ b/drivers/gpio/gpiolib.c
> @@ -2880,7 +2880,7 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep,
> __set_bit(hwgpio, mask);
>
> if (array_info)
> - find_next_zero_bit(array_info->get_mask,
> + i = find_next_zero_bit(array_info->get_mask,
> array_size, i);

We could mark find_next_zero_bit() and friends as a __must_check
functions so we avoid this bug in the future. I have a more complicated
idea how to detect these bugs in a generic way using Smatch but it will
take longer to implement.

regards,
dan carpenter


2018-09-21 08:20:00

by Marek Szyprowski

[permalink] [raw]
Subject: Re: [PATCH v7 4/4] gpiolib: Implement fast processing path in get/set array

Hi Janusz,

On 2018-09-20 18:21, Janusz Krzysztofik wrote:
> On Thursday, September 20, 2018 5:48:22 PM CEST Janusz Krzysztofik wrote:
>> On Thursday, September 20, 2018 12:11:48 PM CEST Marek Szyprowski wrote:
>>> On 2018-09-02 14:01, Janusz Krzysztofik wrote:
>>>> Certain GPIO descriptor arrays returned by gpio_get_array() may contain
>>>> information on direct mapping of array members to pins of a single GPIO
>>>> chip in hardware order. In such cases, bitmaps of values can be passed
>>>> directly from/to the chip's .get/set_multiple() callbacks without
>>>> wasting time on iterations.
>>>>
>>>> Add respective code to gpiod_get/set_array_bitmap_complex() functions.
>>>> Pins not applicable for fast path are processed as before, skipping
>>>> over the 'fast' ones.
>>>>
>>>> Cc: Jonathan Corbet <[email protected]>
>>>> Signed-off-by: Janusz Krzysztofik <[email protected]>
>>> I've just noticed that this patch landed in today's linux-next. Sadly it
>>> breaks booting of Exynos5250-based Samsung Snow Chromebook (ARM 32bit,
>>> device-tree source arch/arm/boot/dts/exynos5250-snow.dts).
>>>
>>> Booting hangs after detecting MMC cards. Reverting this patch fixes the
>>> boot. I will try later to add some debugs and investigate it further what
>>> really happens when booting hangs.
>> Hi Marek,
>>
>> Thanks for reporting. Could you please try the following fix?
> Hi again,
>
> I realized the patch was not correct, j, not i, should be updated in second
> hunk. Please try the following one.
>
> Thanks,
> Janusz
>
> >From a919c504850f6cb40e8e81267a3a37537f7c4fd4 Mon Sep 17 00:00:00 2001
> From: Janusz Krzysztofik <[email protected]>
> Date: Thu, 20 Sep 2018 17:37:21 +0200
> Subject: [PATCH] gpiolib: Fix bitmap index not updated
> While skipping fast path bits, bitmap index is not updated with next
> found zero bit position. Fix it.
>
> Signed-off-by: Janusz Krzysztofik <[email protected]>

This one also doesn't help. A quick compare of logs with this version and
a working system shows, that with your patch (and fix) there are no calls to
gpx0-2 pin (which are a part of mmc pwrseq), what causes mmc failure. If
you need any more information (what kind of logs will help?), let me know.

> ---
> drivers/gpio/gpiolib.c | 7 ++++---
> 1 file changed, 4 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
> index a53d17745d21..369bdd358fcc 100644
> --- a/drivers/gpio/gpiolib.c
> +++ b/drivers/gpio/gpiolib.c
> @@ -2880,7 +2880,7 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep,
> __set_bit(hwgpio, mask);
>
> if (array_info)
> - find_next_zero_bit(array_info->get_mask,
> + i = find_next_zero_bit(array_info->get_mask,
> array_size, i);
> else
> i++;
> @@ -2905,7 +2905,8 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep,
> trace_gpio_value(desc_to_gpio(desc), 1, value);
>
> if (array_info)
> - find_next_zero_bit(array_info->get_mask, i, j);
> + j = find_next_zero_bit(array_info->get_mask, i,
> + j);
> else
> j++;
> }
> @@ -3192,7 +3193,7 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep,
> }
>
> if (array_info)
> - find_next_zero_bit(array_info->set_mask,
> + i = find_next_zero_bit(array_info->set_mask,
> array_size, i);
> else
> i++;

Best regards
--
Marek Szyprowski, PhD
Samsung R&D Institute Poland


2018-09-21 10:52:10

by Janusz Krzysztofik

[permalink] [raw]
Subject: Re: [PATCH v7 4/4] gpiolib: Implement fast processing path in get/set array

Hi Marek,

2018-09-21 10:18 GMT+02:00, Marek Szyprowski <[email protected]>:
> Hi Janusz,
>
> On 2018-09-20 18:21, Janusz Krzysztofik wrote:
>> On Thursday, September 20, 2018 5:48:22 PM CEST Janusz Krzysztofik wrote:
>>> On Thursday, September 20, 2018 12:11:48 PM CEST Marek Szyprowski wrote:
>>>> On 2018-09-02 14:01, Janusz Krzysztofik wrote:
>>>>> Certain GPIO descriptor arrays returned by gpio_get_array() may
>>>>> contain
>>>>> information on direct mapping of array members to pins of a single
>>>>> GPIO
>>>>> chip in hardware order. In such cases, bitmaps of values can be
>>>>> passed
>>>>> directly from/to the chip's .get/set_multiple() callbacks without
>>>>> wasting time on iterations.
>>>>>
>>>>> Add respective code to gpiod_get/set_array_bitmap_complex() functions.
>>>>> Pins not applicable for fast path are processed as before, skipping
>>>>> over the 'fast' ones.
>>>>>
>>>>> Cc: Jonathan Corbet <[email protected]>
>>>>> Signed-off-by: Janusz Krzysztofik <[email protected]>
>>>> I've just noticed that this patch landed in today's linux-next. Sadly
>>>> it
>>>> breaks booting of Exynos5250-based Samsung Snow Chromebook (ARM 32bit,
>>>> device-tree source arch/arm/boot/dts/exynos5250-snow.dts).
>>>>
>>>> Booting hangs after detecting MMC cards. Reverting this patch fixes the
>>>> boot. I will try later to add some debugs and investigate it further
>>>> what
>>>> really happens when booting hangs.
>>> Hi Marek,
>>>
>>> Thanks for reporting. Could you please try the following fix?
>> Hi again,
>>
>> I realized the patch was not correct, j, not i, should be updated in
>> second
>> hunk. Please try the following one.
>>
>> Thanks,
>> Janusz
>>
>> >From a919c504850f6cb40e8e81267a3a37537f7c4fd4 Mon Sep 17 00:00:00 2001
>> From: Janusz Krzysztofik <[email protected]>
>> Date: Thu, 20 Sep 2018 17:37:21 +0200
>> Subject: [PATCH] gpiolib: Fix bitmap index not updated
>> While skipping fast path bits, bitmap index is not updated with next
>> found zero bit position. Fix it.
>>
>> Signed-off-by: Janusz Krzysztofik <[email protected]>
>
> This one also doesn't help. A quick compare of logs with this version and
> a working system shows, that with your patch (and fix) there are no calls
> to
> gpx0-2 pin (which are a part of mmc pwrseq), what causes mmc failure. If
> you need any more information (what kind of logs will help?), let me know.

There is a debug message on array_info content available at the end of
gpiod_get_array(), could you please activate it and post the message so
we can understand better what is going on?

On the other hand, I've had a look your device-tree configuration and
it looks like that specific setup won't benefit from the fast bitmap path.
You have pin 2 at position 0 and pin 1 at position 1 of the array.
Hence, the fast bitmap path covers only pin 1, and pin 2 is processed
by the old path with apparently buggy code for skipping over fast pins.

As a temporary workaround, you could try to revert the order of pins in
your dts file (pin 1 at position 0, pin 2 at 1) and the mmc pwrseq code
should work for you again by taking the original old path, not skipping
over fast pins. Results of such check may also help us to better
understand and resolve the issue.

Thanks,
Janusz

>
>> ---
>> drivers/gpio/gpiolib.c | 7 ++++---
>> 1 file changed, 4 insertions(+), 3 deletions(-)
>>
>> diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
>> index a53d17745d21..369bdd358fcc 100644
>> --- a/drivers/gpio/gpiolib.c
>> +++ b/drivers/gpio/gpiolib.c
>> @@ -2880,7 +2880,7 @@ int gpiod_get_array_value_complex(bool raw, bool
>> can_sleep,
>> __set_bit(hwgpio, mask);
>>
>> if (array_info)
>> - find_next_zero_bit(array_info->get_mask,
>> + i = find_next_zero_bit(array_info->get_mask,
>> array_size, i);
>> else
>> i++;
>> @@ -2905,7 +2905,8 @@ int gpiod_get_array_value_complex(bool raw, bool
>> can_sleep,
>> trace_gpio_value(desc_to_gpio(desc), 1, value);
>>
>> if (array_info)
>> - find_next_zero_bit(array_info->get_mask, i, j);
>> + j = find_next_zero_bit(array_info->get_mask, i,
>> + j);
>> else
>> j++;
>> }
>> @@ -3192,7 +3193,7 @@ int gpiod_set_array_value_complex(bool raw, bool
>> can_sleep,
>> }
>>
>> if (array_info)
>> - find_next_zero_bit(array_info->set_mask,
>> + i = find_next_zero_bit(array_info->set_mask,
>> array_size, i);
>> else
>> i++;
>
> Best regards
> --
> Marek Szyprowski, PhD
> Samsung R&D Institute Poland
>
>

2018-09-21 11:28:40

by Janusz Krzysztofik

[permalink] [raw]
Subject: Re: [PATCH v7 4/4] gpiolib: Implement fast processing path in get/set array

Hi Marek,

2018-09-21 12:51 GMT+02:00, Janusz Krzysztofik <[email protected]>:
> Hi Marek,
>
> 2018-09-21 10:18 GMT+02:00, Marek Szyprowski <[email protected]>:
>> Hi Janusz,
>>
>> On 2018-09-20 18:21, Janusz Krzysztofik wrote:
>>> On Thursday, September 20, 2018 5:48:22 PM CEST Janusz Krzysztofik
>>> wrote:
>>>> On Thursday, September 20, 2018 12:11:48 PM CEST Marek Szyprowski
>>>> wrote:
>>>>> On 2018-09-02 14:01, Janusz Krzysztofik wrote:
>>>>>> Certain GPIO descriptor arrays returned by gpio_get_array() may
>>>>>> contain
>>>>>> information on direct mapping of array members to pins of a single
>>>>>> GPIO
>>>>>> chip in hardware order. In such cases, bitmaps of values can be
>>>>>> passed
>>>>>> directly from/to the chip's .get/set_multiple() callbacks without
>>>>>> wasting time on iterations.
>>>>>>
>>>>>> Add respective code to gpiod_get/set_array_bitmap_complex()
>>>>>> functions.
>>>>>> Pins not applicable for fast path are processed as before, skipping
>>>>>> over the 'fast' ones.
>>>>>>
>>>>>> Cc: Jonathan Corbet <[email protected]>
>>>>>> Signed-off-by: Janusz Krzysztofik <[email protected]>
>>>>> I've just noticed that this patch landed in today's linux-next. Sadly
>>>>> it
>>>>> breaks booting of Exynos5250-based Samsung Snow Chromebook (ARM 32bit,
>>>>> device-tree source arch/arm/boot/dts/exynos5250-snow.dts).
>>>>>
>>>>> Booting hangs after detecting MMC cards. Reverting this patch fixes
>>>>> the
>>>>> boot. I will try later to add some debugs and investigate it further
>>>>> what
>>>>> really happens when booting hangs.
>>>> Hi Marek,
>>>>
>>>> Thanks for reporting. Could you please try the following fix?
>>> Hi again,
>>>
>>> I realized the patch was not correct, j, not i, should be updated in
>>> second
>>> hunk. Please try the following one.
>>>
>>> Thanks,
>>> Janusz
>>>
>>> >From a919c504850f6cb40e8e81267a3a37537f7c4fd4 Mon Sep 17 00:00:00 2001
>>> From: Janusz Krzysztofik <[email protected]>
>>> Date: Thu, 20 Sep 2018 17:37:21 +0200
>>> Subject: [PATCH] gpiolib: Fix bitmap index not updated
>>> While skipping fast path bits, bitmap index is not updated with next
>>> found zero bit position. Fix it.
>>>
>>> Signed-off-by: Janusz Krzysztofik <[email protected]>
>>
>> This one also doesn't help. A quick compare of logs with this version and
>> a working system shows, that with your patch (and fix) there are no calls
>> to
>> gpx0-2 pin (which are a part of mmc pwrseq), what causes mmc failure. If
>> you need any more information (what kind of logs will help?), let me
>> know.

One more question. You said before that booting hanged after detecting MMC
cards. Without the fix, I could imagine it keeps iterating with index not
updated and simply never returns from gpiod_get/set_array_bitmap_complex().
Is the behaviour you observe the same with the fix applied?

Thanks,
Janusz

> There is a debug message on array_info content available at the end of
> gpiod_get_array(), could you please activate it and post the message so
> we can understand better what is going on?
>
> On the other hand, I've had a look your device-tree configuration and
> it looks like that specific setup won't benefit from the fast bitmap path.
> You have pin 2 at position 0 and pin 1 at position 1 of the array.
> Hence, the fast bitmap path covers only pin 1, and pin 2 is processed
> by the old path with apparently buggy code for skipping over fast pins.
>
> As a temporary workaround, you could try to revert the order of pins in
> your dts file (pin 1 at position 0, pin 2 at 1) and the mmc pwrseq code
> should work for you again by taking the original old path, not skipping
> over fast pins. Results of such check may also help us to better
> understand and resolve the issue.
>
> Thanks,
> Janusz
>
>>
>>> ---
>>> drivers/gpio/gpiolib.c | 7 ++++---
>>> 1 file changed, 4 insertions(+), 3 deletions(-)
>>>
>>> diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
>>> index a53d17745d21..369bdd358fcc 100644
>>> --- a/drivers/gpio/gpiolib.c
>>> +++ b/drivers/gpio/gpiolib.c
>>> @@ -2880,7 +2880,7 @@ int gpiod_get_array_value_complex(bool raw, bool
>>> can_sleep,
>>> __set_bit(hwgpio, mask);
>>>
>>> if (array_info)
>>> - find_next_zero_bit(array_info->get_mask,
>>> + i = find_next_zero_bit(array_info->get_mask,
>>> array_size, i);
>>> else
>>> i++;
>>> @@ -2905,7 +2905,8 @@ int gpiod_get_array_value_complex(bool raw, bool
>>> can_sleep,
>>> trace_gpio_value(desc_to_gpio(desc), 1, value);
>>>
>>> if (array_info)
>>> - find_next_zero_bit(array_info->get_mask, i, j);
>>> + j = find_next_zero_bit(array_info->get_mask, i,
>>> + j);
>>> else
>>> j++;
>>> }
>>> @@ -3192,7 +3193,7 @@ int gpiod_set_array_value_complex(bool raw, bool
>>> can_sleep,
>>> }
>>>
>>> if (array_info)
>>> - find_next_zero_bit(array_info->set_mask,
>>> + i = find_next_zero_bit(array_info->set_mask,
>>> array_size, i);
>>> else
>>> i++;
>>
>> Best regards
>> --
>> Marek Szyprowski, PhD
>> Samsung R&D Institute Poland
>>
>>
>

2018-09-21 14:14:49

by Marek Szyprowski

[permalink] [raw]
Subject: Re: [PATCH v7 4/4] gpiolib: Implement fast processing path in get/set array

Hi Janusz,


On 2018-09-21 12:51, Janusz Krzysztofik wrote:
> 2018-09-21 10:18 GMT+02:00, Marek Szyprowski <[email protected]>:
>> On 2018-09-20 18:21, Janusz Krzysztofik wrote:
>>> On Thursday, September 20, 2018 5:48:22 PM CEST Janusz Krzysztofik wrote:
>>>> On Thursday, September 20, 2018 12:11:48 PM CEST Marek Szyprowski wrote:
>>>>> On 2018-09-02 14:01, Janusz Krzysztofik wrote:
>>>>>> Certain GPIO descriptor arrays returned by gpio_get_array() may
>>>>>> contain
>>>>>> information on direct mapping of array members to pins of a single
>>>>>> GPIO
>>>>>> chip in hardware order. In such cases, bitmaps of values can be
>>>>>> passed
>>>>>> directly from/to the chip's .get/set_multiple() callbacks without
>>>>>> wasting time on iterations.
>>>>>>
>>>>>> Add respective code to gpiod_get/set_array_bitmap_complex() functions.
>>>>>> Pins not applicable for fast path are processed as before, skipping
>>>>>> over the 'fast' ones.
>>>>>>
>>>>>> Cc: Jonathan Corbet <[email protected]>
>>>>>> Signed-off-by: Janusz Krzysztofik <[email protected]>
>>>>> I've just noticed that this patch landed in today's linux-next. Sadly
>>>>> it
>>>>> breaks booting of Exynos5250-based Samsung Snow Chromebook (ARM 32bit,
>>>>> device-tree source arch/arm/boot/dts/exynos5250-snow.dts).
>>>>>
>>>>> Booting hangs after detecting MMC cards. Reverting this patch fixes the
>>>>> boot. I will try later to add some debugs and investigate it further
>>>>> what
>>>>> really happens when booting hangs.
>>>> Hi Marek,
>>>>
>>>> Thanks for reporting. Could you please try the following fix?
>>> Hi again,
>>>
>>> I realized the patch was not correct, j, not i, should be updated in
>>> second
>>> hunk. Please try the following one.
>>>
>>> Thanks,
>>> Janusz
>>>
>>> >From a919c504850f6cb40e8e81267a3a37537f7c4fd4 Mon Sep 17 00:00:00 2001
>>> From: Janusz Krzysztofik <[email protected]>
>>> Date: Thu, 20 Sep 2018 17:37:21 +0200
>>> Subject: [PATCH] gpiolib: Fix bitmap index not updated
>>> While skipping fast path bits, bitmap index is not updated with next
>>> found zero bit position. Fix it.
>>>
>>> Signed-off-by: Janusz Krzysztofik <[email protected]>
>> This one also doesn't help. A quick compare of logs with this version and
>> a working system shows, that with your patch (and fix) there are no calls
>> to
>> gpx0-2 pin (which are a part of mmc pwrseq), what causes mmc failure. If
>> you need any more information (what kind of logs will help?), let me know.
> There is a debug message on array_info content available at the end of
> gpiod_get_array(), could you please activate it and post the message so
> we can understand better what is going on?

With debug enabled on next-20180919:
[    2.499153] pwrseq_simple mmc3_pwrseq: GPIO array info: chip=gpx0,
size=2, get_mask=2, set_mask=2, invert_mask=2

On next-20180920 I get no this message and booting hangs.

Same with next-20180920 + your second fix from this thread.

I will try to debug this more on Monday.

> On the other hand, I've had a look your device-tree configuration and
> it looks like that specific setup won't benefit from the fast bitmap path.
> You have pin 2 at position 0 and pin 1 at position 1 of the array.
> Hence, the fast bitmap path covers only pin 1, and pin 2 is processed
> by the old path with apparently buggy code for skipping over fast pins.
>
> As a temporary workaround, you could try to revert the order of pins in
> your dts file (pin 1 at position 0, pin 2 at 1) and the mmc pwrseq code
> should work for you again by taking the original old path, not skipping
> over fast pins. Results of such check may also help us to better
> understand and resolve the issue.

Changing the order of mmc pwrseq gpio pins fixes boot hang.

Best regards
--
Marek Szyprowski, PhD
Samsung R&D Institute Poland


2018-09-23 10:52:35

by Janusz Krzysztofik

[permalink] [raw]
Subject: Re: [PATCH v7 4/4] gpiolib: Implement fast processing path in get/set array

On Friday, September 21, 2018 4:14:06 PM CEST Marek Szyprowski wrote:
> Hi Janusz,
>
>
> On 2018-09-21 12:51, Janusz Krzysztofik wrote:
> > 2018-09-21 10:18 GMT+02:00, Marek Szyprowski <[email protected]>:
> >> On 2018-09-20 18:21, Janusz Krzysztofik wrote:
> >>> On Thursday, September 20, 2018 5:48:22 PM CEST Janusz Krzysztofik wrote:
> >>>> On Thursday, September 20, 2018 12:11:48 PM CEST Marek Szyprowski
wrote:
> >>>>> On 2018-09-02 14:01, Janusz Krzysztofik wrote:
> >>>>>> Certain GPIO descriptor arrays returned by gpio_get_array() may
> >>>>>> contain
> >>>>>> information on direct mapping of array members to pins of a single
> >>>>>> GPIO
> >>>>>> chip in hardware order. In such cases, bitmaps of values can be
> >>>>>> passed
> >>>>>> directly from/to the chip's .get/set_multiple() callbacks without
> >>>>>> wasting time on iterations.
> >>>>>>
> >>>>>> Add respective code to gpiod_get/set_array_bitmap_complex()
functions.
> >>>>>> Pins not applicable for fast path are processed as before, skipping
> >>>>>> over the 'fast' ones.
> >>>>>>
> >>>>>> Cc: Jonathan Corbet <[email protected]>
> >>>>>> Signed-off-by: Janusz Krzysztofik <[email protected]>
> >>>>> I've just noticed that this patch landed in today's linux-next. Sadly
> >>>>> it
> >>>>> breaks booting of Exynos5250-based Samsung Snow Chromebook (ARM 32bit,
> >>>>> device-tree source arch/arm/boot/dts/exynos5250-snow.dts).
> >>>>>
> >>>>> Booting hangs after detecting MMC cards. Reverting this patch fixes the
> >>>>> boot. I will try later to add some debugs and investigate it further
> >>>>> what
> >>>>> really happens when booting hangs.
> >>>> Hi Marek,
> >>>>
> >>>> Thanks for reporting. Could you please try the following fix?
> >>> Hi again,
> >>>
> >>> I realized the patch was not correct, j, not i, should be updated in
> >>> second
> >>> hunk. Please try the following one.
> >>>
> >>> Thanks,
> >>> Janusz
> >>>
> >>> >From a919c504850f6cb40e8e81267a3a37537f7c4fd4 Mon Sep 17 00:00:00 2001
> >>> From: Janusz Krzysztofik <[email protected]>
> >>> Date: Thu, 20 Sep 2018 17:37:21 +0200
> >>> Subject: [PATCH] gpiolib: Fix bitmap index not updated
> >>> While skipping fast path bits, bitmap index is not updated with next
> >>> found zero bit position. Fix it.
> >>>
> >>> Signed-off-by: Janusz Krzysztofik <[email protected]>
> >> This one also doesn't help. A quick compare of logs with this version and
> >> a working system shows, that with your patch (and fix) there are no calls
> >> to
> >> gpx0-2 pin (which are a part of mmc pwrseq), what causes mmc failure. If
> >> you need any more information (what kind of logs will help?), let me
know.
> > There is a debug message on array_info content available at the end of
> > gpiod_get_array(), could you please activate it and post the message so
> > we can understand better what is going on?
>
> With debug enabled on next-20180919:
> [ 2.499153] pwrseq_simple mmc3_pwrseq: GPIO array info: chip=gpx0,
> size=2, get_mask=2, set_mask=2, invert_mask=2

Looks good to me, i..e., in line with what one could expect. However, ...

> On next-20180920 I get no this message and booting hangs.
>
> Same with next-20180920 + your second fix from this thread.
>
> I will try to debug this more on Monday.
>
> > On the other hand, I've had a look your device-tree configuration and
> > it looks like that specific setup won't benefit from the fast bitmap path.
> > You have pin 2 at position 0 and pin 1 at position 1 of the array.
> > Hence, the fast bitmap path covers only pin 1, and pin 2 is processed
> > by the old path with apparently buggy code for skipping over fast pins.
> >
> > As a temporary workaround, you could try to revert the order of pins in
> > your dts file (pin 1 at position 0, pin 2 at 1) and the mmc pwrseq code
> > should work for you again by taking the original old path, not skipping
> > over fast pins. Results of such check may also help us to better
> > understand and resolve the issue.
>
> Changing the order of mmc pwrseq gpio pins fixes boot hang.

Not being able to discover more coding bugs in the code modified by the series,
I'm wondering if the reason for the issue you are observing comes from the
fact both pins are no longer manipulated together within a single
.set_multiple() chip callback. I'm working on a fix which prevents from that.

Thanks,
Janusz



2018-09-23 11:38:35

by Miquel Raynal

[permalink] [raw]
Subject: Re: [PATCH v5 0/3] mtd: rawnand: ams-delta: Cleanups and optimizations

Hi Janusz,

Janusz Krzysztofik <[email protected]> wrote on Thu, 20 Sep 2018
00:52:52 +0200:

> This series consist of possibly ready to apply patches extracted from
> a former one titled "mtd: rawnand: ams-delta: Use GPIO API for data I/O".
> Remaining patches implementing conversion of data I/O to GPIO have been
> postponed until gpiolib is optimized to ensure sufficient performance.
>
>
> Janusz Krzysztofik (3):
> mtd: rawnand: ams-delta: show parent device in sysfs
> mtd: rawnand: ams-delta: Use private structure
> mtd: rawnand: ams-delta: Set port direction when needed
>
>

Series applied on nand/next.

Thanks,
Miquèl

2018-09-23 23:53:07

by Janusz Krzysztofik

[permalink] [raw]
Subject: [PATCH 1/2] gpiolib: Fix missing updates of bitmap index

In new code introduced by commit b17566a6b08b ("gpiolib: Implement fast
processing path in get/set array"), bitmap index is not updated with
next found zero bit position as it should while skipping over pins
already processed via fast bitmap path, possibly resulting in an
infinite loop. Fix it.

Signed-off-by: Janusz Krzysztofik <[email protected]>
---
drivers/gpio/gpiolib.c | 11 ++++++-----
1 file changed, 6 insertions(+), 5 deletions(-)

diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index a53d17745d21..7d9536a79a66 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -2880,8 +2880,8 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep,
__set_bit(hwgpio, mask);

if (array_info)
- find_next_zero_bit(array_info->get_mask,
- array_size, i);
+ i = find_next_zero_bit(array_info->get_mask,
+ array_size, i);
else
i++;
} while ((i < array_size) &&
@@ -2905,7 +2905,8 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep,
trace_gpio_value(desc_to_gpio(desc), 1, value);

if (array_info)
- find_next_zero_bit(array_info->get_mask, i, j);
+ j = find_next_zero_bit(array_info->get_mask, i,
+ j);
else
j++;
}
@@ -3192,8 +3193,8 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep,
}

if (array_info)
- find_next_zero_bit(array_info->set_mask,
- array_size, i);
+ i = find_next_zero_bit(array_info->set_mask,
+ array_size, i);
else
i++;
} while ((i < array_size) &&
--
2.16.4


2018-09-23 23:53:15

by Janusz Krzysztofik

[permalink] [raw]
Subject: [PATCH 2/2] gpiolib: Fix array members of same chip processed separately

New code introduced by commit bf9346f5d47b ("gpiolib: Identify arrays
matching GPIO hardware") forcibly tries to find an array member which
has its array index number equal to its hardware pin number and set
up an array info for possible fast bitmap processing of all arrray
pins belonging to that chip which also satisfy that numbering rule.

Depending on array content, it may happen that consecutive array
members which belong to the same chip but don't have array indexes
equal to their pin hardware numbers will be split into groups, some of
them processed together via the fast bitmap path, and rest of them
separetely. However, applications may expect all those pins being
processed together with a single call to .set_multiple() chip callback,
like that was done before the change.

Limit applicability of fast bitmap processing path to cases where all
pins of consecutive array members starting from 0 which belong to the
same chip have their hardware numbers equal to their corresponding
array indexes. That should still speed up processing of applications
using whole GPIO banks as I/O ports, while not breaking simultaneous
manipulation of consecutive pins of the same chip which don't follow
the equal numbering rule.

Cc: Jonathan Corbet <[email protected]>
Signed-off-by: Janusz Krzysztofik <[email protected]>
---
Documentation/driver-api/gpio/board.rst | 19 +++++++++++++-----
drivers/gpio/gpiolib.c | 35 +++++++++++++++++++++++----------
2 files changed, 39 insertions(+), 15 deletions(-)

diff --git a/Documentation/driver-api/gpio/board.rst b/Documentation/driver-api/gpio/board.rst
index c66821e033c2..a0f294e2e250 100644
--- a/Documentation/driver-api/gpio/board.rst
+++ b/Documentation/driver-api/gpio/board.rst
@@ -202,9 +202,18 @@ mapped to the device determines if the array qualifies for fast bitmap
processing. If yes, a bitmap is passed over get/set array functions directly
between a caller and a respective .get/set_multiple() callback of a GPIO chip.

-In order to qualify for fast bitmap processing, the pin mapping must meet the
+In order to qualify for fast bitmap processing, the array must meet the
following requirements:
-- it must belong to the same chip as other 'fast' pins of the function,
-- its index within the function must match its hardware number within the chip.
-
-Open drain and open source pins are excluded from fast bitmap output processing.
+- pin hardware number of array member 0 must also be 0,
+- pin hardware numbers of consecutive array members which belong to the same
+ chip as member 0 does must also match their array indexes.
+
+Otherwise fast bitmap processing path is not used in order to avoid consecutive
+pins which belong to the same chip but are not in hardware order being processed
+separately.
+
+If the array applies for fast bitmap processing path, pins which belong to
+different chips than member 0 does, as well as those with indexes different from
+their hardware pin numbers, are excluded from the fast path, both input and
+output. Moreover, open drain and open source pins are excluded from fast bitmap
+output processing.
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index 7d9536a79a66..6ae13e3e05f1 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -4376,11 +4376,10 @@ struct gpio_descs *__must_check gpiod_get_array(struct device *dev,

chip = gpiod_to_chip(desc);
/*
- * Select a chip of first array member
- * whose index matches its pin hardware number
- * as a candidate for fast bitmap processing.
+ * If pin hardware number of array member 0 is also 0, select
+ * its chip as a candidate for fast bitmap processing path.
*/
- if (!array_info && gpio_chip_hwgpio(desc) == descs->ndescs) {
+ if (descs->ndescs == 0 && gpio_chip_hwgpio(desc) == 0) {
struct gpio_descs *array;

bitmap_size = BITS_TO_LONGS(chip->ngpio > count ?
@@ -4414,14 +4413,30 @@ struct gpio_descs *__must_check gpiod_get_array(struct device *dev,
count - descs->ndescs);
descs->info = array_info;
}
- /*
- * Unmark members which don't qualify for fast bitmap
- * processing (different chip, not in hardware order)
- */
- if (array_info && (chip != array_info->chip ||
- gpio_chip_hwgpio(desc) != descs->ndescs)) {
+ /* Unmark array members which don't belong to the 'fast' chip */
+ if (array_info && array_info->chip != chip) {
__clear_bit(descs->ndescs, array_info->get_mask);
__clear_bit(descs->ndescs, array_info->set_mask);
+ }
+ /*
+ * Detect array members which belong to the 'fast' chip
+ * but their pins are not in hardware order.
+ */
+ else if (array_info &&
+ gpio_chip_hwgpio(desc) != descs->ndescs) {
+ /*
+ * Don't use fast path if all array members processed so
+ * far belong to the same chip as this one but its pin
+ * hardware number is different from its array index.
+ */
+ if (bitmap_full(array_info->get_mask, descs->ndescs)) {
+ array_info = NULL;
+ } else {
+ __clear_bit(descs->ndescs,
+ array_info->get_mask);
+ __clear_bit(descs->ndescs,
+ array_info->set_mask);
+ }
} else if (array_info) {
/* Exclude open drain or open source from fast output */
if (gpiochip_line_is_open_drain(chip, descs->ndescs) ||
--
2.16.4


2018-09-23 23:54:48

by Janusz Krzysztofik

[permalink] [raw]
Subject: [PATCH 0/2] gpiolib: Fix issues introduced by fast bitmap processing path


While investigating possible reasons of GPIO fast bitmap processing
related boot hang on Samsung Snow Chromebook, reported by Marek
Szyprowski (thanks!), I've discovered one coding bug, addressed by
PATCH 1/2 of this series, and one potential regression introduced at
design level of the solution, hopefully fixed by PATCH 2/2. See
commit messages for details.

Janusz Krzysztofik (2):
gpiolib: Fix missing updates of bitmap index
gpiolib: Fix array members of same chip processed separately

The fixes should resolve the boot hang observed by Marek, however the
second change excludes that particular case from fast bitmap processing
and restores the old behaviour. Hence, it is possible still another
issue which have had an influence on that boot hang exists in the code.
In order to fully verify the fix, it would have to be tested on a
platform where an array of GPIO descriptors is used which starts from
at least two consecutive pins of one GPIO chip in hardware order,
starting ftom 0, followed by one or more pins belonging to other
chip(s).

In order to verify if separate calls to .set() chip callback for each
pin instead of one call to .set_multiple() is actually the reason of
boot hang on Samsung Snow Chromebook, the affected driver -
drivers/mmc/core/pwrseq_simple.c - would have to be temporarily
modified for testing purposes so it calls gpiod_set_value() for each
pin instead of gpiod_set_array_value() for all of them. If that would
also result in boot hang, we could be sure the issue was really the
one addressed by the second fix. Marek, could you please try to
perform such test?

Thanks,
Janusz


diffstat:
Documentation/driver-api/gpio/board.rst | 19 +++++++++----
drivers/gpio/gpiolib.c | 46 +++++++++++++++++++++-----------
2 files changed, 45 insertions(+), 20 deletions(-)


2018-09-24 08:12:38

by Linus Walleij

[permalink] [raw]
Subject: Re: [PATCH 1/2] gpiolib: Fix missing updates of bitmap index

On Mon, Sep 24, 2018 at 1:52 AM Janusz Krzysztofik <[email protected]> wrote:

> In new code introduced by commit b17566a6b08b ("gpiolib: Implement fast
> processing path in get/set array"), bitmap index is not updated with
> next found zero bit position as it should while skipping over pins
> already processed via fast bitmap path, possibly resulting in an
> infinite loop. Fix it.
>
> Signed-off-by: Janusz Krzysztofik <[email protected]>

Patch applied!

Thanks for working on getting this into shape!

Yours,
Linus Walleij

2018-09-24 08:14:57

by Linus Walleij

[permalink] [raw]
Subject: Re: [PATCH 2/2] gpiolib: Fix array members of same chip processed separately

On Mon, Sep 24, 2018 at 1:52 AM Janusz Krzysztofik <[email protected]> wrote:

> New code introduced by commit bf9346f5d47b ("gpiolib: Identify arrays
> matching GPIO hardware") forcibly tries to find an array member which
> has its array index number equal to its hardware pin number and set
> up an array info for possible fast bitmap processing of all arrray
> pins belonging to that chip which also satisfy that numbering rule.
>
> Depending on array content, it may happen that consecutive array
> members which belong to the same chip but don't have array indexes
> equal to their pin hardware numbers will be split into groups, some of
> them processed together via the fast bitmap path, and rest of them
> separetely. However, applications may expect all those pins being
> processed together with a single call to .set_multiple() chip callback,
> like that was done before the change.
>
> Limit applicability of fast bitmap processing path to cases where all
> pins of consecutive array members starting from 0 which belong to the
> same chip have their hardware numbers equal to their corresponding
> array indexes. That should still speed up processing of applications
> using whole GPIO banks as I/O ports, while not breaking simultaneous
> manipulation of consecutive pins of the same chip which don't follow
> the equal numbering rule.
>
> Cc: Jonathan Corbet <[email protected]>
> Signed-off-by: Janusz Krzysztofik <[email protected]>

Patch applied!

Yours,
Linus Walleij

2018-09-24 10:03:54

by Marek Szyprowski

[permalink] [raw]
Subject: Re: [PATCH 0/2] gpiolib: Fix issues introduced by fast bitmap processing path

Hi Janusz,

On 2018-09-24 01:53, Janusz Krzysztofik wrote:
> While investigating possible reasons of GPIO fast bitmap processing
> related boot hang on Samsung Snow Chromebook, reported by Marek
> Szyprowski (thanks!), I've discovered one coding bug, addressed by
> PATCH 1/2 of this series, and one potential regression introduced at
> design level of the solution, hopefully fixed by PATCH 2/2. See
> commit messages for details.
>
> Janusz Krzysztofik (2):
> gpiolib: Fix missing updates of bitmap index
> gpiolib: Fix array members of same chip processed separately
>
> The fixes should resolve the boot hang observed by Marek, however the
> second change excludes that particular case from fast bitmap processing
> and restores the old behaviour.

I confirm, that the above 2 patches fixes boot issue on Samsung Snow
Chromebook with next-20180920.

Tested-by: Marek Szyprowski <[email protected]>

> Hence, it is possible still another
> issue which have had an influence on that boot hang exists in the code.
> In order to fully verify the fix, it would have to be tested on a
> platform where an array of GPIO descriptors is used which starts from
> at least two consecutive pins of one GPIO chip in hardware order,
> starting ftom 0, followed by one or more pins belonging to other
> chip(s).
>
> In order to verify if separate calls to .set() chip callback for each
> pin instead of one call to .set_multiple() is actually the reason of
> boot hang on Samsung Snow Chromebook, the affected driver -
> drivers/mmc/core/pwrseq_simple.c - would have to be temporarily
> modified for testing purposes so it calls gpiod_set_value() for each
> pin instead of gpiod_set_array_value() for all of them. If that would
> also result in boot hang, we could be sure the issue was really the
> one addressed by the second fix. Marek, could you please try to
> perform such test?

Yes, I've just tested next-20180920 only with the first patch from this
patchset and the mentioned change to drivers/mmc/core/pwrseq_simple.c.
It boots fine, so indeed the issue is in handling of arrays of gpios.

Just to be sure I did it right, this is my change to the mentioned file:

diff --git a/drivers/mmc/core/pwrseq_simple.c
b/drivers/mmc/core/pwrseq_simple.c
index 7f882a2bb872..9397dc1f2e38 100644
--- a/drivers/mmc/core/pwrseq_simple.c
+++ b/drivers/mmc/core/pwrseq_simple.c
@@ -38,16 +38,11 @@ static void mmc_pwrseq_simple_set_gpios_value(struct
mmc_pwrseq_simple *pwrseq,
                                              int value)
 {
        struct gpio_descs *reset_gpios = pwrseq->reset_gpios;
+       int i;

-       if (!IS_ERR(reset_gpios)) {
-               DECLARE_BITMAP(values, BITS_PER_TYPE(value));
-               int nvalues = reset_gpios->ndescs;
-
-               values[0] = value;
-
-               gpiod_set_array_value_cansleep(nvalues, reset_gpios->desc,
-                                              reset_gpios->info, values);
-       }
+       if (!IS_ERR(reset_gpios))
+               for (i = 0; i < reset_gpios->ndescs; i++)
+ gpiod_set_value_cansleep(reset_gpios->desc[i], value);
 }

 static void mmc_pwrseq_simple_pre_power_on(struct mmc_host *host)


Best regards
--
Marek Szyprowski, PhD
Samsung R&D Institute Poland


2018-09-24 11:08:44

by Janusz Krzysztofik

[permalink] [raw]
Subject: Re: [PATCH 0/2] gpiolib: Fix issues introduced by fast bitmap processing path

Hi Marek,

2018-09-24 11:43 GMT+02:00, Marek Szyprowski <[email protected]>:
> Hi Janusz,
>
> On 2018-09-24 01:53, Janusz Krzysztofik wrote:
>> While investigating possible reasons of GPIO fast bitmap processing
>> related boot hang on Samsung Snow Chromebook, reported by Marek
>> Szyprowski (thanks!), I've discovered one coding bug, addressed by
>> PATCH 1/2 of this series, and one potential regression introduced at
>> design level of the solution, hopefully fixed by PATCH 2/2. See
>> commit messages for details.
>>
>> Janusz Krzysztofik (2):
>> gpiolib: Fix missing updates of bitmap index
>> gpiolib: Fix array members of same chip processed separately
>>
>> The fixes should resolve the boot hang observed by Marek, however the
>> second change excludes that particular case from fast bitmap processing
>> and restores the old behaviour.
>
> I confirm, that the above 2 patches fixes boot issue on Samsung Snow
> Chromebook with next-20180920.
>
> Tested-by: Marek Szyprowski <[email protected]>
>
>> Hence, it is possible still another
>> issue which have had an influence on that boot hang exists in the code.
>> In order to fully verify the fix, it would have to be tested on a
>> platform where an array of GPIO descriptors is used which starts from
>> at least two consecutive pins of one GPIO chip in hardware order,
>> starting ftom 0, followed by one or more pins belonging to other
>> chip(s).
>>
>> In order to verify if separate calls to .set() chip callback for each
>> pin instead of one call to .set_multiple() is actually the reason of
>> boot hang on Samsung Snow Chromebook, the affected driver -
>> drivers/mmc/core/pwrseq_simple.c - would have to be temporarily
>> modified for testing purposes so it calls gpiod_set_value() for each
>> pin instead of gpiod_set_array_value() for all of them. If that would
>> also result in boot hang, we could be sure the issue was really the
>> one addressed by the second fix. Marek, could you please try to
>> perform such test?
>
> Yes, I've just tested next-20180920 only with the first patch from this
> patchset and the mentioned change to drivers/mmc/core/pwrseq_simple.c.
> It boots fine, so indeed the issue is in handling of arrays of gpios.
>
> Just to be sure I did it right, this is my change to the mentioned file:

Yeah, that's what I had on mind. However, I'd be more lucky if it didn't work
for you. Setting the pins sequentially, not simultaneously as before, was
exactly what I hoped was the reason of the hang.

> diff --git a/drivers/mmc/core/pwrseq_simple.c
> b/drivers/mmc/core/pwrseq_simple.c
> index 7f882a2bb872..9397dc1f2e38 100644
> --- a/drivers/mmc/core/pwrseq_simple.c
> +++ b/drivers/mmc/core/pwrseq_simple.c
> @@ -38,16 +38,11 @@ static void mmc_pwrseq_simple_set_gpios_value(struct
> mmc_pwrseq_simple *pwrseq,
> int value)
> {
> struct gpio_descs *reset_gpios = pwrseq->reset_gpios;
> + int i;
>
> - if (!IS_ERR(reset_gpios)) {
> - DECLARE_BITMAP(values, BITS_PER_TYPE(value));
> - int nvalues = reset_gpios->ndescs;
> -
> - values[0] = value;
> -
> - gpiod_set_array_value_cansleep(nvalues, reset_gpios->desc,
> - reset_gpios->info, values);
> - }
> + if (!IS_ERR(reset_gpios))
> + for (i = 0; i < reset_gpios->ndescs; i++)

The only difference from the behaviour when the hang was occurring is now
the order the pins are manipulated. Maybe that matters?
Could you please retry the same with the order of pins reversed, either in
the .dts file or here inside this for loop?

Thanks,
Janusz

> + gpiod_set_value_cansleep(reset_gpios->desc[i], value);
> }
>
> static void mmc_pwrseq_simple_pre_power_on(struct mmc_host *host)
>
>
> Best regards
> --
> Marek Szyprowski, PhD
> Samsung R&D Institute Poland
>
>

2018-09-24 11:40:21

by Marek Szyprowski

[permalink] [raw]
Subject: Re: [PATCH 0/2] gpiolib: Fix issues introduced by fast bitmap processing path

Hi Janusz,

On 2018-09-24 13:08, Janusz Krzysztofik wrote:
> 2018-09-24 11:43 GMT+02:00, Marek Szyprowski <[email protected]>:
>> On 2018-09-24 01:53, Janusz Krzysztofik wrote:
>>> While investigating possible reasons of GPIO fast bitmap processing
>>> related boot hang on Samsung Snow Chromebook, reported by Marek
>>> Szyprowski (thanks!), I've discovered one coding bug, addressed by
>>> PATCH 1/2 of this series, and one potential regression introduced at
>>> design level of the solution, hopefully fixed by PATCH 2/2. See
>>> commit messages for details.
>>>
>>> Janusz Krzysztofik (2):
>>> gpiolib: Fix missing updates of bitmap index
>>> gpiolib: Fix array members of same chip processed separately
>>>
>>> The fixes should resolve the boot hang observed by Marek, however the
>>> second change excludes that particular case from fast bitmap processing
>>> and restores the old behaviour.
>> I confirm, that the above 2 patches fixes boot issue on Samsung Snow
>> Chromebook with next-20180920.
>>
>> Tested-by: Marek Szyprowski <[email protected]>
>>
>>> Hence, it is possible still another
>>> issue which have had an influence on that boot hang exists in the code.
>>> In order to fully verify the fix, it would have to be tested on a
>>> platform where an array of GPIO descriptors is used which starts from
>>> at least two consecutive pins of one GPIO chip in hardware order,
>>> starting ftom 0, followed by one or more pins belonging to other
>>> chip(s).
>>>
>>> In order to verify if separate calls to .set() chip callback for each
>>> pin instead of one call to .set_multiple() is actually the reason of
>>> boot hang on Samsung Snow Chromebook, the affected driver -
>>> drivers/mmc/core/pwrseq_simple.c - would have to be temporarily
>>> modified for testing purposes so it calls gpiod_set_value() for each
>>> pin instead of gpiod_set_array_value() for all of them. If that would
>>> also result in boot hang, we could be sure the issue was really the
>>> one addressed by the second fix. Marek, could you please try to
>>> perform such test?
>> Yes, I've just tested next-20180920 only with the first patch from this
>> patchset and the mentioned change to drivers/mmc/core/pwrseq_simple.c.
>> It boots fine, so indeed the issue is in handling of arrays of gpios.
>>
>> Just to be sure I did it right, this is my change to the mentioned file:
> Yeah, that's what I had on mind. However, I'd be more lucky if it didn't work
> for you. Setting the pins sequentially, not simultaneously as before, was
> exactly what I hoped was the reason of the hang.
>
>> diff --git a/drivers/mmc/core/pwrseq_simple.c
>> b/drivers/mmc/core/pwrseq_simple.c
>> index 7f882a2bb872..9397dc1f2e38 100644
>> --- a/drivers/mmc/core/pwrseq_simple.c
>> +++ b/drivers/mmc/core/pwrseq_simple.c
>> @@ -38,16 +38,11 @@ static void mmc_pwrseq_simple_set_gpios_value(struct
>> mmc_pwrseq_simple *pwrseq,
>> int value)
>> {
>> struct gpio_descs *reset_gpios = pwrseq->reset_gpios;
>> + int i;
>>
>> - if (!IS_ERR(reset_gpios)) {
>> - DECLARE_BITMAP(values, BITS_PER_TYPE(value));
>> - int nvalues = reset_gpios->ndescs;
>> -
>> - values[0] = value;
>> -
>> - gpiod_set_array_value_cansleep(nvalues, reset_gpios->desc,
>> - reset_gpios->info, values);
>> - }
>> + if (!IS_ERR(reset_gpios))
>> + for (i = 0; i < reset_gpios->ndescs; i++)
> The only difference from the behaviour when the hang was occurring is now
> the order the pins are manipulated. Maybe that matters?
> Could you please retry the same with the order of pins reversed, either in
> the .dts file or here inside this for loop?

I've switched the order of pins in dts and next-20180920 + first patch +
above
change also boots fine.

Best regards
--
Marek Szyprowski, PhD
Samsung R&D Institute Poland


2018-09-24 15:00:35

by Janusz Krzysztofik

[permalink] [raw]
Subject: Re: [PATCH 0/2] gpiolib: Fix issues introduced by fast bitmap processing path

Hi Marek,

2018-09-24 13:38 GMT+02:00, Marek Szyprowski <[email protected]>:
> Hi Janusz,
>
> On 2018-09-24 13:08, Janusz Krzysztofik wrote:
>> 2018-09-24 11:43 GMT+02:00, Marek Szyprowski <[email protected]>:
>>> On 2018-09-24 01:53, Janusz Krzysztofik wrote:
>>>> While investigating possible reasons of GPIO fast bitmap processing
>>>> related boot hang on Samsung Snow Chromebook, reported by Marek
>>>> Szyprowski (thanks!), I've discovered one coding bug, addressed by
>>>> PATCH 1/2 of this series, and one potential regression introduced at
>>>> design level of the solution, hopefully fixed by PATCH 2/2. See
>>>> commit messages for details.
>>>>
>>>> Janusz Krzysztofik (2):
>>>> gpiolib: Fix missing updates of bitmap index
>>>> gpiolib: Fix array members of same chip processed separately
>>>>
>>>> The fixes should resolve the boot hang observed by Marek, however the
>>>> second change excludes that particular case from fast bitmap processing
>>>> and restores the old behaviour.
>>> I confirm, that the above 2 patches fixes boot issue on Samsung Snow
>>> Chromebook with next-20180920.
>>>
>>> Tested-by: Marek Szyprowski <[email protected]>
>>>
>>>> Hence, it is possible still another
>>>> issue which have had an influence on that boot hang exists in the code.
>>>> In order to fully verify the fix, it would have to be tested on a
>>>> platform where an array of GPIO descriptors is used which starts from
>>>> at least two consecutive pins of one GPIO chip in hardware order,
>>>> starting ftom 0, followed by one or more pins belonging to other
>>>> chip(s).
>>>>
>>>> In order to verify if separate calls to .set() chip callback for each
>>>> pin instead of one call to .set_multiple() is actually the reason of
>>>> boot hang on Samsung Snow Chromebook, the affected driver -
>>>> drivers/mmc/core/pwrseq_simple.c - would have to be temporarily
>>>> modified for testing purposes so it calls gpiod_set_value() for each
>>>> pin instead of gpiod_set_array_value() for all of them. If that would
>>>> also result in boot hang, we could be sure the issue was really the
>>>> one addressed by the second fix. Marek, could you please try to
>>>> perform such test?
>>> Yes, I've just tested next-20180920 only with the first patch from this
>>> patchset and the mentioned change to drivers/mmc/core/pwrseq_simple.c.
>>> It boots fine, so indeed the issue is in handling of arrays of gpios.
>>>
>>> Just to be sure I did it right, this is my change to the mentioned file:
>> Yeah, that's what I had on mind. However, I'd be more lucky if it didn't
>> work
>> for you. Setting the pins sequentially, not simultaneously as before,
>> was
>> exactly what I hoped was the reason of the hang.
>>
>>> diff --git a/drivers/mmc/core/pwrseq_simple.c
>>> b/drivers/mmc/core/pwrseq_simple.c
>>> index 7f882a2bb872..9397dc1f2e38 100644
>>> --- a/drivers/mmc/core/pwrseq_simple.c
>>> +++ b/drivers/mmc/core/pwrseq_simple.c
>>> @@ -38,16 +38,11 @@ static void mmc_pwrseq_simple_set_gpios_value(struct
>>> mmc_pwrseq_simple *pwrseq,
>>> int value)
>>> {
>>> struct gpio_descs *reset_gpios = pwrseq->reset_gpios;
>>> + int i;
>>>
>>> - if (!IS_ERR(reset_gpios)) {
>>> - DECLARE_BITMAP(values, BITS_PER_TYPE(value));
>>> - int nvalues = reset_gpios->ndescs;
>>> -
>>> - values[0] = value;
>>> -
>>> - gpiod_set_array_value_cansleep(nvalues,
>>> reset_gpios->desc,
>>> - reset_gpios->info,
>>> values);
>>> - }
>>> + if (!IS_ERR(reset_gpios))
>>> + for (i = 0; i < reset_gpios->ndescs; i++)
>> The only difference from the behaviour when the hang was occurring is now
>> the order the pins are manipulated. Maybe that matters?
>> Could you please retry the same with the order of pins reversed, either
>> in
>> the .dts file or here inside this for loop?
>
> I've switched the order of pins in dts and next-20180920 + first patch +
> above
> change also boots fine.

Thanks for performing those tests.

Since we are not able to reproduce the issue by any means other than
using the original code introduced by fast bitmap processing changes,
regardless of the first fix being applied or not, and we are only able to
resolve the hangup by excluding affected use case from the fast path,
we have to assume one or more bugs which affect mixed arrays, i.e.,
those which apply for fast bitmap processing only in part, may still exist
in the code introduced by the fast bitmap processing series. I hope we
are able to resolve it soon, before the changes reach mainline.

Thanks,
Janusz


> Best regards
> --
> Marek Szyprowski, PhD
> Samsung R&D Institute Poland
>
>

2018-09-25 19:25:20

by Janusz Krzysztofik

[permalink] [raw]
Subject: [PATCH] mmc: pwrseq_simple: Fix incorrect handling of GPIO bitmap

Commit b9762bebc633 ("gpiolib: Pass bitmaps, not integer arrays, to
get/set array") changed the way GPIO values are passed to
gpiod_get/set_array_value() and friends. The updated code of
mmc_pwrseq_simple_set_gpios_value() incorrectly uses the 'value'
argument as a bitmap of GPIO values and assigns it directly to the
'values' bitmap variable passed to gpiod_set_array_value_cansleep()
instead of filling that bitmap with bits equal to the 'value' argument.
As a result, boot hanging caused by incorrectly handled MMC device
has been observed.

As a side effect of that incorrect interpreation of the 'value'
argument, wrong assumption is taken about the 'values' bitmap size
never exceding the number of bits of the 'value' argument type.

Fix it.

Reported-by: Marek Szyprowski <[email protected]>
Signed-off-by: Janusz Krzysztofik <[email protected]>
---
Hi,

I hope I've finally identified the root cause of the boot hang reported
by Marek Szyprowski. I've reviewed the code of other divers updated for
the modified GPIO API and found no more issues of that kind.

Marek, can you please test this fix on top of next-20180920 with the fix
"gpiolib: Fix missing updates of bitmap index" also applied?

I've assumed this fix, if tested successfully, will be merged via GPIO
tree, hence I've selected Linus as the main recipient of this message.

Thanks,
Janusz

drivers/mmc/core/pwrseq_simple.c | 13 +++++++++++--
1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/drivers/mmc/core/pwrseq_simple.c b/drivers/mmc/core/pwrseq_simple.c
index 7f882a2bb872..ece34c734693 100644
--- a/drivers/mmc/core/pwrseq_simple.c
+++ b/drivers/mmc/core/pwrseq_simple.c
@@ -40,13 +40,22 @@ static void mmc_pwrseq_simple_set_gpios_value(struct mmc_pwrseq_simple *pwrseq,
struct gpio_descs *reset_gpios = pwrseq->reset_gpios;

if (!IS_ERR(reset_gpios)) {
- DECLARE_BITMAP(values, BITS_PER_TYPE(value));
+ unsigned long *values;
int nvalues = reset_gpios->ndescs;

- values[0] = value;
+ values = bitmap_alloc(nvalues, GFP_KERNEL);
+ if (!values)
+ return;
+
+ if (value)
+ bitmap_fill(values, nvalues);
+ else
+ bitmap_zero(values, nvalues);

gpiod_set_array_value_cansleep(nvalues, reset_gpios->desc,
reset_gpios->info, values);
+
+ kfree(values);
}
}

--
2.16.4


2018-09-26 07:51:30

by Linus Walleij

[permalink] [raw]
Subject: Re: [PATCH] mmc: pwrseq_simple: Fix incorrect handling of GPIO bitmap

On Tue, Sep 25, 2018 at 9:23 PM Janusz Krzysztofik <[email protected]> wrote:

> Commit b9762bebc633 ("gpiolib: Pass bitmaps, not integer arrays, to
> get/set array") changed the way GPIO values are passed to
> gpiod_get/set_array_value() and friends. The updated code of
> mmc_pwrseq_simple_set_gpios_value() incorrectly uses the 'value'
> argument as a bitmap of GPIO values and assigns it directly to the
> 'values' bitmap variable passed to gpiod_set_array_value_cansleep()
> instead of filling that bitmap with bits equal to the 'value' argument.
> As a result, boot hanging caused by incorrectly handled MMC device
> has been observed.
>
> As a side effect of that incorrect interpreation of the 'value'
> argument, wrong assumption is taken about the 'values' bitmap size
> never exceding the number of bits of the 'value' argument type.
>
> Fix it.
>
> Reported-by: Marek Szyprowski <[email protected]>
> Signed-off-by: Janusz Krzysztofik <[email protected]>

Nice!

Provided this works, Ulf can I have your ACK so I can queue this
with the rest of the gpio array rework in the GPIO tree?

Yours,
Linus Walleij

2018-09-26 08:16:25

by Marek Szyprowski

[permalink] [raw]
Subject: Re: [PATCH] mmc: pwrseq_simple: Fix incorrect handling of GPIO bitmap

Hi Janusz,

On 2018-09-25 21:24, Janusz Krzysztofik wrote:
> Commit b9762bebc633 ("gpiolib: Pass bitmaps, not integer arrays, to
> get/set array") changed the way GPIO values are passed to
> gpiod_get/set_array_value() and friends. The updated code of
> mmc_pwrseq_simple_set_gpios_value() incorrectly uses the 'value'
> argument as a bitmap of GPIO values and assigns it directly to the
> 'values' bitmap variable passed to gpiod_set_array_value_cansleep()
> instead of filling that bitmap with bits equal to the 'value' argument.
> As a result, boot hanging caused by incorrectly handled MMC device
> has been observed.
>
> As a side effect of that incorrect interpreation of the 'value'
> argument, wrong assumption is taken about the 'values' bitmap size
> never exceding the number of bits of the 'value' argument type.
>
> Fix it.
>
> Reported-by: Marek Szyprowski <[email protected]>
> Signed-off-by: Janusz Krzysztofik <[email protected]>
> ---
> Hi,
>
> I hope I've finally identified the root cause of the boot hang reported
> by Marek Szyprowski. I've reviewed the code of other divers updated for
> the modified GPIO API and found no more issues of that kind.
>
> Marek, can you please test this fix on top of next-20180920 with the fix
> "gpiolib: Fix missing updates of bitmap index" also applied?

Yes, I've just did such test (next-20180920 + "gpiolib: Fix missing
updates of bitmap index" + "mmc: pwrseq_simple: Fix incorrect handling
of GPIO bitmap") and sadly it doesn't fix the boot hang.

With some more debugs in mmc_pwrseq_simple I've noticed that
gpiod_set_array_value_cansleep() never ends and busyloops somewhere.
I'm checking this now.

> I've assumed this fix, if tested successfully, will be merged via GPIO
> tree, hence I've selected Linus as the main recipient of this message.
>
> Thanks,
> Janusz
>
> drivers/mmc/core/pwrseq_simple.c | 13 +++++++++++--
> 1 file changed, 11 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/mmc/core/pwrseq_simple.c b/drivers/mmc/core/pwrseq_simple.c
> index 7f882a2bb872..ece34c734693 100644
> --- a/drivers/mmc/core/pwrseq_simple.c
> +++ b/drivers/mmc/core/pwrseq_simple.c
> @@ -40,13 +40,22 @@ static void mmc_pwrseq_simple_set_gpios_value(struct mmc_pwrseq_simple *pwrseq,
> struct gpio_descs *reset_gpios = pwrseq->reset_gpios;
>
> if (!IS_ERR(reset_gpios)) {
> - DECLARE_BITMAP(values, BITS_PER_TYPE(value));
> + unsigned long *values;
> int nvalues = reset_gpios->ndescs;
>
> - values[0] = value;
> + values = bitmap_alloc(nvalues, GFP_KERNEL);
> + if (!values)
> + return;
> +
> + if (value)
> + bitmap_fill(values, nvalues);
> + else
> + bitmap_zero(values, nvalues);
>
> gpiod_set_array_value_cansleep(nvalues, reset_gpios->desc,
> reset_gpios->info, values);
> +
> + kfree(values);
> }
> }
>

Best regards
--
Marek Szyprowski, PhD
Samsung R&D Institute Poland


2018-09-26 08:29:54

by Marek Szyprowski

[permalink] [raw]
Subject: Re: [PATCH] mmc: pwrseq_simple: Fix incorrect handling of GPIO bitmap

Hi again,

On 2018-09-26 10:14, Marek Szyprowski wrote:
> On 2018-09-25 21:24, Janusz Krzysztofik wrote:
>> Commit b9762bebc633 ("gpiolib: Pass bitmaps, not integer arrays, to
>> get/set array") changed the way GPIO values are passed to
>> gpiod_get/set_array_value() and friends.  The updated code of
>> mmc_pwrseq_simple_set_gpios_value() incorrectly uses the 'value'
>> argument as a bitmap of GPIO values and assigns it directly to the
>> 'values' bitmap variable passed to gpiod_set_array_value_cansleep()
>> instead of filling that bitmap with bits equal to the 'value' argument.
>> As a result, boot hanging caused by incorrectly handled MMC device
>> has been observed.
>>
>> As a side effect of that incorrect interpreation of the 'value'
>> argument, wrong assumption is taken about the 'values' bitmap size
>> never exceding the number of bits of the 'value' argument type.
>>
>> Fix it.
>>
>> Reported-by: Marek Szyprowski <[email protected]>
>> Signed-off-by: Janusz Krzysztofik <[email protected]>
>> ---
>> Hi,
>>
>> I hope I've finally identified the root cause of the boot hang reported
>> by Marek Szyprowski.  I've reviewed the code of other divers updated for
>> the modified GPIO API and found no more issues of that kind.
>>
>> Marek, can you please test this fix on top of next-20180920 with the fix
>> "gpiolib: Fix missing updates of bitmap index" also applied?
>
> Yes, I've just did such test (next-20180920 + "gpiolib: Fix missing
> updates of bitmap index" + "mmc: pwrseq_simple: Fix incorrect handling
> of GPIO bitmap") and sadly it doesn't fix the boot hang.
>
> With some more debugs in mmc_pwrseq_simple I've noticed that
> gpiod_set_array_value_cansleep() never ends and busyloops somewhere.
> I'm checking this now.

I busyloops inside the internal do { } while loop (lines 3163-3201) in
gpiod_set_array_value_complex(). 'i' is never incremented.

> ...

Best regards
--
Marek Szyprowski, PhD
Samsung R&D Institute Poland


2018-09-29 12:19:48

by Janusz Krzysztofik

[permalink] [raw]
Subject: [PATCH] gpiolib: Fix incorrect use of find_next_zero_bit()

Commit b17566a6b08b ("gpiolib: Implement fast processing path in
get/set array"), already fixed to some extent with commit 5d581d7e8cdc
("gpiolib: Fix missing updates of bitmap index"), introduced a new mode
of processing bitmaps where bits applicable for fast bitmap processing
path are supposed to be skipped while iterating bits which don't apply.
Unfortunately, find_next_zero_bit() function supposed to skip over
those fast bits is always called with a 'start' argument equal to an
index of last zero bit found and returns that index value again an
again, causing an infinite loop.

Fix it by incrementing the index uncoditionally before
find_next_zero_bit() is optionally called.

Reported-by: Marek Szyprowski <[email protected]>
Signed-off-by: Janusz Krzysztofik <[email protected]>
---
Marek,

Could you please test it on top of next-20180920 with "gpiolib: Fix
missing updates of bitmap index" and optionally "mmc: pwrseq_simple:
Fix incorrect handling of GPIO bitmap" also applied?

Thanks,
Janusz


drivers/gpio/gpiolib.c | 9 +++------
1 file changed, 3 insertions(+), 6 deletions(-)

diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index 6ae13e3e05f1..940b543e966d 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -2878,12 +2878,11 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep,
int hwgpio = gpio_chip_hwgpio(desc);

__set_bit(hwgpio, mask);
+ i++;

if (array_info)
i = find_next_zero_bit(array_info->get_mask,
array_size, i);
- else
- i++;
} while ((i < array_size) &&
(desc_array[i]->gdev->chip == chip));

@@ -2903,12 +2902,11 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep,
value = !value;
__assign_bit(j, value_bitmap, value);
trace_gpio_value(desc_to_gpio(desc), 1, value);
+ j++;

if (array_info)
j = find_next_zero_bit(array_info->get_mask, i,
j);
- else
- j++;
}

if (mask != fastpath)
@@ -3191,12 +3189,11 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep,
__clear_bit(hwgpio, bits);
count++;
}
+ i++;

if (array_info)
i = find_next_zero_bit(array_info->set_mask,
array_size, i);
- else
- i++;
} while ((i < array_size) &&
(desc_array[i]->gdev->chip == chip));
/* push collected bits to outputs */
--
2.16.4


2018-10-01 06:46:52

by Marek Szyprowski

[permalink] [raw]
Subject: Re: [PATCH] gpiolib: Fix incorrect use of find_next_zero_bit()

Hi Janusz,

On 2018-09-29 14:20, Janusz Krzysztofik wrote:
> Commit b17566a6b08b ("gpiolib: Implement fast processing path in
> get/set array"), already fixed to some extent with commit 5d581d7e8cdc
> ("gpiolib: Fix missing updates of bitmap index"), introduced a new mode
> of processing bitmaps where bits applicable for fast bitmap processing
> path are supposed to be skipped while iterating bits which don't apply.
> Unfortunately, find_next_zero_bit() function supposed to skip over
> those fast bits is always called with a 'start' argument equal to an
> index of last zero bit found and returns that index value again an
> again, causing an infinite loop.
>
> Fix it by incrementing the index uncoditionally before
> find_next_zero_bit() is optionally called.
>
> Reported-by: Marek Szyprowski <[email protected]>
> Signed-off-by: Janusz Krzysztofik <[email protected]>

Tested-by: Marek Szyprowski <[email protected]>

> ---
> Marek,
>
> Could you please test it on top of next-20180920 with "gpiolib: Fix
> missing updates of bitmap index" and optionally "mmc: pwrseq_simple:
> Fix incorrect handling of GPIO bitmap" also applied?

This patch finally fixes the boot issue on Samsung Chromebook Snow.
Thanks!

>
> Thanks,
> Janusz
>
>
> drivers/gpio/gpiolib.c | 9 +++------
> 1 file changed, 3 insertions(+), 6 deletions(-)
>
> diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
> index 6ae13e3e05f1..940b543e966d 100644
> --- a/drivers/gpio/gpiolib.c
> +++ b/drivers/gpio/gpiolib.c
> @@ -2878,12 +2878,11 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep,
> int hwgpio = gpio_chip_hwgpio(desc);
>
> __set_bit(hwgpio, mask);
> + i++;
>
> if (array_info)
> i = find_next_zero_bit(array_info->get_mask,
> array_size, i);
> - else
> - i++;
> } while ((i < array_size) &&
> (desc_array[i]->gdev->chip == chip));
>
> @@ -2903,12 +2902,11 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep,
> value = !value;
> __assign_bit(j, value_bitmap, value);
> trace_gpio_value(desc_to_gpio(desc), 1, value);
> + j++;
>
> if (array_info)
> j = find_next_zero_bit(array_info->get_mask, i,
> j);
> - else
> - j++;
> }
>
> if (mask != fastpath)
> @@ -3191,12 +3189,11 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep,
> __clear_bit(hwgpio, bits);
> count++;
> }
> + i++;
>
> if (array_info)
> i = find_next_zero_bit(array_info->set_mask,
> array_size, i);
> - else
> - i++;
> } while ((i < array_size) &&
> (desc_array[i]->gdev->chip == chip));
> /* push collected bits to outputs */

Best regards
--
Marek Szyprowski, PhD
Samsung R&D Institute Poland


2018-10-01 09:39:09

by Linus Walleij

[permalink] [raw]
Subject: Re: [PATCH] gpiolib: Fix incorrect use of find_next_zero_bit()

On Sat, Sep 29, 2018 at 2:19 PM Janusz Krzysztofik <[email protected]> wrote:

> Commit b17566a6b08b ("gpiolib: Implement fast processing path in
> get/set array"), already fixed to some extent with commit 5d581d7e8cdc
> ("gpiolib: Fix missing updates of bitmap index"), introduced a new mode
> of processing bitmaps where bits applicable for fast bitmap processing
> path are supposed to be skipped while iterating bits which don't apply.
> Unfortunately, find_next_zero_bit() function supposed to skip over
> those fast bits is always called with a 'start' argument equal to an
> index of last zero bit found and returns that index value again an
> again, causing an infinite loop.
>
> Fix it by incrementing the index uncoditionally before
> find_next_zero_bit() is optionally called.
>
> Reported-by: Marek Szyprowski <[email protected]>
> Signed-off-by: Janusz Krzysztofik <[email protected]>

Patch applied with Marek's Tested-by.

Thanks to both of you for digging in and fixing this up!
Now we are in good shape for the v4.20 cycle :)

Yours,
Linus Walleij

2018-10-03 11:59:38

by Janusz Krzysztofik

[permalink] [raw]
Subject: [RFC PATCH] mtd: rawnand: ams-delta: use ->exec_op()

Replace legacy callbacks with ->select_chip() and ->exec_op().

Implementation of NAND_OP_WAITRDY_INSTR has been based on legacy
nand_wait_ready(), otherwise that function would probabaly have to be
reimplemented inside the driver. Hence, legacy callback ->dev_ready()
is still used.

Use of IO_ADDR_R and IO_ADDR_W legacy structure members will be dropped
later, as soon as the driver is converted to use GPIO API for data I/O.

Suggested-by: Boris Brezillon <[email protected]>
Signed-off-by: Janusz Krzysztofik <[email protected]>
---
Hi,

I've not tested the change on hardware yet as I'm not sure if:
- handling of NCE limited to that inside ->select_chip() is
sufficient,
- releasing ALE / CLE immediately after ams_delta_write_buf() is
correct.
Please advise before I break my test hardware :-).

Thanks,
Janusz

drivers/mtd/nand/raw/ams-delta.c | 83 +++++++++++++++++++++++++---------------
1 file changed, 52 insertions(+), 31 deletions(-)

diff --git a/drivers/mtd/nand/raw/ams-delta.c b/drivers/mtd/nand/raw/ams-delta.c
index 5ba180a291eb..90c283a2c1b7 100644
--- a/drivers/mtd/nand/raw/ams-delta.c
+++ b/drivers/mtd/nand/raw/ams-delta.c
@@ -124,46 +124,69 @@ static void ams_delta_read_buf(struct nand_chip *this, u_char *buf, int len)
buf[i] = ams_delta_io_read(priv);
}

-static u_char ams_delta_read_byte(struct nand_chip *this)
+static int ams_delta_nand_ready(struct nand_chip *this)
{
- u_char res;
-
- ams_delta_read_buf(this, &res, 1);
+ struct ams_delta_nand *priv = nand_get_controller_data(this);

- return res;
+ return gpiod_get_value(priv->gpiod_rdy);
}

-/*
- * Command control function
- *
- * ctrl:
- * NAND_NCE: bit 0 -> bit 2
- * NAND_CLE: bit 1 -> bit 7
- * NAND_ALE: bit 2 -> bit 6
- */
-static void ams_delta_hwcontrol(struct nand_chip *this, int cmd,
- unsigned int ctrl)
+static void ams_delta_select_chip(struct nand_chip *this, int n)
{
struct ams_delta_nand *priv = nand_get_controller_data(this);

- if (ctrl & NAND_CTRL_CHANGE) {
- gpiod_set_value(priv->gpiod_nce, !(ctrl & NAND_NCE));
- gpiod_set_value(priv->gpiod_cle, !!(ctrl & NAND_CLE));
- gpiod_set_value(priv->gpiod_ale, !!(ctrl & NAND_ALE));
- }
-
- if (cmd != NAND_CMD_NONE) {
- u_char byte = cmd;
+ if (n > 0)
+ return;

- ams_delta_write_buf(this, &byte, 1);
- }
+ gpiod_set_value(priv->gpiod_nce, n < 0);
}

-static int ams_delta_nand_ready(struct nand_chip *this)
+static int ams_delta_exec_op(struct nand_chip *this,
+ const struct nand_operation *op, bool check_only)
{
struct ams_delta_nand *priv = nand_get_controller_data(this);
+ const struct nand_op_instr *instr;
+ int i;

- return gpiod_get_value(priv->gpiod_rdy);
+ for (i = 0; i < op->ninstrs; i++) {
+ instr = &op->instrs[i];
+
+ switch (instr->type) {
+ case NAND_OP_CMD_INSTR:
+ gpiod_set_value(priv->gpiod_cle, 1);
+ ams_delta_write_buf(this, &instr->ctx.cmd.opcode, 1);
+ gpiod_set_value(priv->gpiod_cle, 0);
+ break;
+
+ case NAND_OP_ADDR_INSTR:
+ gpiod_set_value(priv->gpiod_ale, 1);
+ ams_delta_write_buf(this, instr->ctx.addr.addrs,
+ instr->ctx.addr.naddrs);
+ gpiod_set_value(priv->gpiod_ale, 0);
+ break;
+
+ case NAND_OP_DATA_IN_INSTR:
+ ams_delta_read_buf(this, instr->ctx.data.buf.in,
+ instr->ctx.data.len);
+ break;
+
+ case NAND_OP_DATA_OUT_INSTR:
+ ams_delta_write_buf(this, instr->ctx.data.buf.out,
+ instr->ctx.data.len);
+ break;
+
+ case NAND_OP_WAITRDY_INSTR:
+ if (this->legacy.dev_ready) {
+ nand_wait_ready(this);
+ break;
+ }
+
+ return nand_soft_waitrdy(this,
+ instr->ctx.waitrdy.timeout_ms);
+ }
+ }
+
+ return 0;
}


@@ -213,10 +236,8 @@ static int ams_delta_init(struct platform_device *pdev)
/* Set address of NAND IO lines */
this->legacy.IO_ADDR_R = io_base + OMAP_MPUIO_INPUT_LATCH;
this->legacy.IO_ADDR_W = io_base + OMAP_MPUIO_OUTPUT;
- this->legacy.read_byte = ams_delta_read_byte;
- this->legacy.write_buf = ams_delta_write_buf;
- this->legacy.read_buf = ams_delta_read_buf;
- this->legacy.cmd_ctrl = ams_delta_hwcontrol;
+ this->select_chip = ams_delta_select_chip;
+ this->exec_op = ams_delta_exec_op;

priv->gpiod_rdy = devm_gpiod_get_optional(&pdev->dev, "rdy", GPIOD_IN);
if (IS_ERR(priv->gpiod_rdy)) {
--
2.16.4


2018-10-03 12:31:34

by Boris Brezillon

[permalink] [raw]
Subject: Re: [RFC PATCH] mtd: rawnand: ams-delta: use ->exec_op()

Hi Janusz,

On Wed, 3 Oct 2018 14:00:28 +0200
Janusz Krzysztofik <[email protected]> wrote:

> Replace legacy callbacks with ->select_chip() and ->exec_op().

Thanks for working on that, that's really appreciated.

>
> Implementation of NAND_OP_WAITRDY_INSTR has been based on legacy
> nand_wait_ready(),

I don't remember what the ams-delta ->dev_ready()/->waitfunc() hooks
are doing, but is shouldn't be too hard to replace them by an
ams_delta_wait_ready() func.

> otherwise that function would probabaly have to be

^ probably


> reimplemented inside the driver. Hence, legacy callback ->dev_ready()
> is still used.
>
> Use of IO_ADDR_R and IO_ADDR_W legacy structure members will be dropped
> later, as soon as the driver is converted to use GPIO API for data I/O.

In the meantime, can you move the iomem pointer to the ams_delta
private struct so that this driver no longer uses the ->IO_ADDR_R/W
fields?

>
> Suggested-by: Boris Brezillon <[email protected]>
> Signed-off-by: Janusz Krzysztofik <[email protected]>
> ---
> Hi,
>
> I've not tested the change on hardware yet as I'm not sure if:
> - handling of NCE limited to that inside ->select_chip() is
> sufficient,

I think it is.

> - releasing ALE / CLE immediately after ams_delta_write_buf() is
> correct.

Well, you should probably consider waiting for instr->ctx.delay_ns
nanoseconds after each instruction, but, if it was working before the
conversion to ->exec_op(), it should work just fine now.

Regards,

Boris

2018-10-03 13:55:53

by Janusz Krzysztofik

[permalink] [raw]
Subject: Re: [RFC PATCH] mtd: rawnand: ams-delta: use ->exec_op()

On Wednesday, October 3, 2018 2:30:54 PM CEST Boris Brezillon wrote:
> Hi Janusz,
>
> On Wed, 3 Oct 2018 14:00:28 +0200
> Janusz Krzysztofik <[email protected]> wrote:
>
> > Replace legacy callbacks with ->select_chip() and ->exec_op().
>
> Thanks for working on that, that's really appreciated.
>
> >
> > Implementation of NAND_OP_WAITRDY_INSTR has been based on legacy
> > nand_wait_ready(),
>
> I don't remember what the ams-delta ->dev_ready()/->waitfunc() hooks
> are doing, but is shouldn't be too hard to replace them by an
> ams_delta_wait_ready() func.

Default nand_wait() is used as ->waitfunc(), and ->dev_ready() returns R/B
GPIO pin status.

> > otherwise that function would probabaly have to be
>
> ^ probably

Do you think other drivers which now provide ->dev_ready() won't require
reimplementation of nand_wait_ready()?

> > reimplemented inside the driver. Hence, legacy callback ->dev_ready()
> > is still used.
> >
> > Use of IO_ADDR_R and IO_ADDR_W legacy structure members will be dropped
> > later, as soon as the driver is converted to use GPIO API for data I/O.
>
> In the meantime, can you move the iomem pointer to the ams_delta
> private struct so that this driver no longer uses the ->IO_ADDR_R/W
> fields?

OK

> >
> > Suggested-by: Boris Brezillon <[email protected]>
> > Signed-off-by: Janusz Krzysztofik <[email protected]>
> > ---
> > Hi,
> >
> > I've not tested the change on hardware yet as I'm not sure if:
> > - handling of NCE limited to that inside ->select_chip() is
> > sufficient,
>
> I think it is.
>
> > - releasing ALE / CLE immediately after ams_delta_write_buf() is
> > correct.
>
> Well, you should probably consider waiting for instr->ctx.delay_ns
> nanoseconds after each instruction, but, if it was working before the
> conversion to ->exec_op(), it should work just fine now.

OK, I'll give it a try.

Thanks,
Janusz




2018-10-03 14:06:59

by Boris Brezillon

[permalink] [raw]
Subject: Re: [RFC PATCH] mtd: rawnand: ams-delta: use ->exec_op()

On Wed, 03 Oct 2018 15:55:25 +0200
Janusz Krzysztofik <[email protected]> wrote:

> > >
> > > Implementation of NAND_OP_WAITRDY_INSTR has been based on legacy
> > > nand_wait_ready(),
> >
> > I don't remember what the ams-delta ->dev_ready()/->waitfunc() hooks
> > are doing, but is shouldn't be too hard to replace them by an
> > ams_delta_wait_ready() func.
>
> Default nand_wait() is used as ->waitfunc(), and ->dev_ready() returns R/B
> GPIO pin status.

Okay. Then it might make sense to provide a generic helper to poll a
gpio.

void nand_gpio_waitrdy(struct nand_chip *chip, struct gpio_desc *gpiod)
{
...
}

>
> > > otherwise that function would probabaly have to be
> >
> > ^ probably
>
> Do you think other drivers which now provide ->dev_ready() won't require
> reimplementation of nand_wait_ready()?

It depends. I mean, most controllers support native R/B sensing, and in
case they do actually require you to poll the RB pin status, duplicating
the wait_ready() logic shouldn't be a problem. On the other hand, I
really want to get rid of ->dev_ready().

2018-10-04 13:52:19

by Janusz Krzysztofik

[permalink] [raw]
Subject: Re: [RFC PATCH] mtd: rawnand: ams-delta: use ->exec_op()

Hi Boris,

On Wednesday, October 3, 2018 4:06:34 PM CEST Boris Brezillon wrote:
> On Wed, 03 Oct 2018 15:55:25 +0200
> Janusz Krzysztofik <[email protected]> wrote:
>
> > > >
> > > > Implementation of NAND_OP_WAITRDY_INSTR has been based on legacy
> > > > nand_wait_ready(),
> > >
> > > I don't remember what the ams-delta ->dev_ready()/->waitfunc() hooks
> > > are doing, but is shouldn't be too hard to replace them by an
> > > ams_delta_wait_ready() func.
> >
> > Default nand_wait() is used as ->waitfunc(), and ->dev_ready() returns R/B
> > GPIO pin status.
>
> Okay. Then it might make sense to provide a generic helper to poll a
> gpio.
>
> void nand_gpio_waitrdy(struct nand_chip *chip, struct gpio_desc *gpiod)
> {
> ...
> }

How about a still more generic helper which accepts dev_ready() callback as an
argument?

Thanks,
Janusz



2018-10-04 14:01:38

by Boris Brezillon

[permalink] [raw]
Subject: Re: [RFC PATCH] mtd: rawnand: ams-delta: use ->exec_op()

On Thu, 04 Oct 2018 15:52:57 +0200
Janusz Krzysztofik <[email protected]> wrote:

> Hi Boris,
>
> On Wednesday, October 3, 2018 4:06:34 PM CEST Boris Brezillon wrote:
> > On Wed, 03 Oct 2018 15:55:25 +0200
> > Janusz Krzysztofik <[email protected]> wrote:
> >
> > > > >
> > > > > Implementation of NAND_OP_WAITRDY_INSTR has been based on legacy
> > > > > nand_wait_ready(),
> > > >
> > > > I don't remember what the ams-delta ->dev_ready()/->waitfunc() hooks
> > > > are doing, but is shouldn't be too hard to replace them by an
> > > > ams_delta_wait_ready() func.
> > >
> > > Default nand_wait() is used as ->waitfunc(), and ->dev_ready() returns R/B
> > > GPIO pin status.
> >
> > Okay. Then it might make sense to provide a generic helper to poll a
> > gpio.
> >
> > void nand_gpio_waitrdy(struct nand_chip *chip, struct gpio_desc *gpiod)
> > {
> > ...
> > }
>
> How about a still more generic helper which accepts dev_ready() callback as an
> argument?

Nope, I still prefer the GPIO based one. We'll see if others need a
a more generic helper, but I doubt it.

2018-10-04 14:12:00

by Janusz Krzysztofik

[permalink] [raw]
Subject: Re: [RFC PATCH] mtd: rawnand: ams-delta: use ->exec_op()

On Thursday, October 4, 2018 3:59:33 PM CEST Boris Brezillon wrote:
> On Thu, 04 Oct 2018 15:52:57 +0200
> Janusz Krzysztofik <[email protected]> wrote:
>
> > Hi Boris,
> >
> > On Wednesday, October 3, 2018 4:06:34 PM CEST Boris Brezillon wrote:
> > > On Wed, 03 Oct 2018 15:55:25 +0200
> > > Janusz Krzysztofik <[email protected]> wrote:
> > >
> > > > > >
> > > > > > Implementation of NAND_OP_WAITRDY_INSTR has been based on legacy
> > > > > > nand_wait_ready(),
> > > > >
> > > > > I don't remember what the ams-delta ->dev_ready()/->waitfunc() hooks
> > > > > are doing, but is shouldn't be too hard to replace them by an
> > > > > ams_delta_wait_ready() func.
> > > >
> > > > Default nand_wait() is used as ->waitfunc(), and ->dev_ready() returns
R/B
> > > > GPIO pin status.
> > >
> > > Okay. Then it might make sense to provide a generic helper to poll a
> > > gpio.
> > >
> > > void nand_gpio_waitrdy(struct nand_chip *chip, struct gpio_desc *gpiod)
> > > {
> > > ...
> > > }
> >
> > How about a still more generic helper which accepts dev_ready() callback
as an
> > argument?
>
> Nope, I still prefer the GPIO based one. We'll see if others need a
> a more generic helper, but I doubt it.

OK.

Legacy nand_wait_ready() uses a hardcoded timeout value of 400 ms. Should we
follow the same approach in nand_gpio_waitrdy(), or should we rather let
drivers pass the timeout value, like in case of nand_soft_waitrdy()?

Thanks,
Janusz



2018-10-04 14:23:14

by Boris Brezillon

[permalink] [raw]
Subject: Re: [RFC PATCH] mtd: rawnand: ams-delta: use ->exec_op()

On Thu, 04 Oct 2018 16:11:42 +0200
Janusz Krzysztofik <[email protected]> wrote:

>
> Legacy nand_wait_ready() uses a hardcoded timeout value of 400 ms. Should we
> follow the same approach in nand_gpio_waitrdy(), or should we rather let
> drivers pass the timeout value, like in case of nand_soft_waitrdy()?

The latter.

2018-10-12 19:09:59

by Janusz Krzysztofik

[permalink] [raw]
Subject: [RFT PATCH] mmc: pwrseq_simple: Fix incorrect handling of GPIO bitmap

Commit b9762bebc633 ("gpiolib: Pass bitmaps, not integer arrays, to
get/set array") changed the way GPIO values are passed to
gpiod_get/set_array_value() and friends. The new code introduced into
mmc_pwrseq_simple_set_gpios_value() incorrectly interpretes the 'value'
argument as a bitmap of GPIO values and assigns it directly to the
'values' bitmap variable passed to gpiod_set_array_value_cansleep()
instead of filling that bitmap with bits equal to the 'value' argument.
As a result, only member 0 of the array is handled correctly.

Moreover, wrong assumption is taken about the 'values' bitmap size not
exceding the number of bits of the 'value' argument type.

Fix it.

Signed-off-by: Janusz Krzysztofik <[email protected]>
---
Hi,

I think that patch has been missed while we were resolving issues
related to GPIO fast bitmap processing. Since all issues other than the
one addreessed by this patch have been been hopefully identified and
fixed, GPIO tree seems now to be in good shape in regard to that.
However, I believe pwrseq_simple is still broken. Hence, I'm
resubmitting this patch to Ulf for inclusion in MMC tree, Cc: many other
people who are kindly requested to test it if possible.

I've identified the following DT files representing devices which may be
affected (have more than one GPIO assigned to pwrseq_simple):
- arch/arm/boot/dts/imx6qdl-sr-som-brcm.dtsi
- arch/arm/boot/dts/exynos5250-snow-common.dtsi
- arch/arm/boot/dts/imx6sl-warp.dts
- arch/arm/boot/dts/omap3-igep0030.dts
- arch/arm/boot/dts/omap3-igep0020.dts
- arch/arm/boot/dts/rk3036-kylin.dts
- arch/arm64/boot/dts/rockchip/rk3368-r88.dts
- arch/arm64/boot/dts/amlogic/meson-gxbb-vega-s95.dtsi

Please start with checking if pwrseq_simple from linux-next works for
you and if not, please test if this patch fixes the issue.

Thanks,
Janusz


drivers/mmc/core/pwrseq_simple.c | 13 +++++++++++--
1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/drivers/mmc/core/pwrseq_simple.c b/drivers/mmc/core/pwrseq_simple.c
index 7f882a2bb872..ece34c734693 100644
--- a/drivers/mmc/core/pwrseq_simple.c
+++ b/drivers/mmc/core/pwrseq_simple.c
@@ -40,13 +40,22 @@ static void mmc_pwrseq_simple_set_gpios_value(struct mmc_pwrseq_simple *pwrseq,
struct gpio_descs *reset_gpios = pwrseq->reset_gpios;

if (!IS_ERR(reset_gpios)) {
- DECLARE_BITMAP(values, BITS_PER_TYPE(value));
+ unsigned long *values;
int nvalues = reset_gpios->ndescs;

- values[0] = value;
+ values = bitmap_alloc(nvalues, GFP_KERNEL);
+ if (!values)
+ return;
+
+ if (value)
+ bitmap_fill(values, nvalues);
+ else
+ bitmap_zero(values, nvalues);

gpiod_set_array_value_cansleep(nvalues, reset_gpios->desc,
reset_gpios->info, values);
+
+ kfree(values);
}
}

--
2.16.4


2018-10-12 20:40:18

by Janusz Krzysztofik

[permalink] [raw]
Subject: [PATCH v2 1/2] mtd: rawnand: Provide helper for polling GPIO R/B pin

Each controller driver with access to NAND R/B pin over GPIO would have
to reimplement the polling loop otherwise.

Signed-off-by: Janusz Krzysztofik <[email protected]>
---
Changelog:
v2:
New patch - v1 consisted of only one patch (the followning one)


drivers/mtd/nand/raw/nand_base.c | 38 ++++++++++++++++++++++++++++++++++++++
include/linux/mtd/rawnand.h | 10 ++++++++++
2 files changed, 48 insertions(+)

diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c
index 05bd0779fe9b..ff1ac4a3c647 100644
--- a/drivers/mtd/nand/raw/nand_base.c
+++ b/drivers/mtd/nand/raw/nand_base.c
@@ -45,6 +45,9 @@
#include <linux/io.h>
#include <linux/mtd/partitions.h>
#include <linux/of.h>
+#ifdef CONFIG_GPIOLIB
+#include <linux/gpio/consumer.h>
+#endif

#include "internals.h"

@@ -531,6 +534,41 @@ int nand_soft_waitrdy(struct nand_chip *chip, unsigned long timeout_ms)
};
EXPORT_SYMBOL_GPL(nand_soft_waitrdy);

+#ifdef CONFIG_GPIOLIB
+/**
+ * nand_gpio_waitrdy - Poll R/B GPIO pin until ready
+ * @chip: NAND chip structure
+ * @gpiod: GPIO descriptor of R/B pin
+ * @timeout_ms: Timeout in ms
+ *
+ * Poll the R/B GPIO pin until it becomes ready. If that does not happen
+ * whitin the specified timeout, -ETIMEDOUT is returned.
+ *
+ * This helper is intended to be used when the controller has access to the
+ * NAND R/B pin over GPIO.
+ *
+ * Be aware that calling this helper from an ->exec_op() implementation means
+ * ->exec_op() must be re-entrant.
+ *
+ * Return 0 if the R/B pin indicates chip is ready, a negative error otherwise.
+ */
+int nand_gpio_waitrdy(struct nand_chip *chip, struct gpio_desc *gpiod,
+ unsigned long timeout_ms)
+{
+ /* Wait until command is processed or timeout occurs */
+ timeout_ms = jiffies + msecs_to_jiffies(timeout_ms);
+ do {
+ if (gpiod_get_value_cansleep(gpiod))
+ return 0;
+
+ cond_resched();
+ } while (time_before(jiffies, timeout_ms));
+
+ return gpiod_get_value_cansleep(gpiod) ? 0 : -ETIMEDOUT;
+};
+EXPORT_SYMBOL_GPL(nand_gpio_waitrdy);
+#endif
+
/**
* panic_nand_get_device - [GENERIC] Get chip for selected access
* @chip: the nand chip descriptor
diff --git a/include/linux/mtd/rawnand.h b/include/linux/mtd/rawnand.h
index e10b126e148f..09f0ed1345b1 100644
--- a/include/linux/mtd/rawnand.h
+++ b/include/linux/mtd/rawnand.h
@@ -1346,4 +1346,14 @@ void nand_release(struct nand_chip *chip);
*/
int nand_soft_waitrdy(struct nand_chip *chip, unsigned long timeout_ms);

+#ifdef CONFIG_GPIOLIB
+struct gpio_desc;
+/*
+ * External helper for controller drivers that have to implement the WAITRDY
+ * instruction and do have GPIO pin to check it.
+ */
+int nand_gpio_waitrdy(struct nand_chip *chip, struct gpio_desc *gpiod,
+ unsigned long timeout_ms);
+#endif
+
#endif /* __LINUX_MTD_RAWNAND_H */
--
2.16.4


2018-10-12 20:40:21

by Janusz Krzysztofik

[permalink] [raw]
Subject: [PATCH v2 2/2] mtd: rawnand: ams-delta: Use ->exec_op()

Replace legacy callbacks with ->select_chip() and ->exec_op().

In order to remove any references to legacy structure members, use of
.IO_ADDR_R/W has been replaced wit runtime calculations based on
priv->io_base.

Suggested-by: Boris Brezillon <[email protected]>
Signed-off-by: Janusz Krzysztofik <[email protected]>
---
Changelog:
v2:
- replace references to legacy structure .IO_ADDR_R/W members with
runtime calculated values - requested by Boris Brezillon, thanks!
- modify ams_delta_read/write_buf() functions, no longer exposed as
callbacks, to accept driver private structure instead of struct
nand_chip,
- use newly introduced nand_gpio_waitrdy() helper instead of legacy
nand_wait_ready() - suggested by Boris Brezillon, thanks!
- remove no longer needed ams_delta_dev_ready() legacy callback and
legacy structure member .chip_delay.

drivers/mtd/nand/raw/ams-delta.c | 103 +++++++++++++++++++++------------------
1 file changed, 55 insertions(+), 48 deletions(-)

diff --git a/drivers/mtd/nand/raw/ams-delta.c b/drivers/mtd/nand/raw/ams-delta.c
index 5ba180a291eb..f0745aeecb1c 100644
--- a/drivers/mtd/nand/raw/ams-delta.c
+++ b/drivers/mtd/nand/raw/ams-delta.c
@@ -75,7 +75,7 @@ static const struct mtd_partition partition_info[] = {

static void ams_delta_io_write(struct ams_delta_nand *priv, u_char byte)
{
- writew(byte, priv->nand_chip.legacy.IO_ADDR_W);
+ writew(byte, priv->io_base + OMAP_MPUIO_OUTPUT);
gpiod_set_value(priv->gpiod_nwe, 0);
ndelay(40);
gpiod_set_value(priv->gpiod_nwe, 1);
@@ -87,7 +87,7 @@ static u_char ams_delta_io_read(struct ams_delta_nand *priv)

gpiod_set_value(priv->gpiod_nre, 0);
ndelay(40);
- res = readw(priv->nand_chip.legacy.IO_ADDR_R);
+ res = readw(priv->io_base + OMAP_MPUIO_INPUT_LATCH);
gpiod_set_value(priv->gpiod_nre, 1);

return res;
@@ -99,10 +99,9 @@ static void ams_delta_dir_input(struct ams_delta_nand *priv, bool in)
priv->data_in = in;
}

-static void ams_delta_write_buf(struct nand_chip *this, const u_char *buf,
+static void ams_delta_write_buf(struct ams_delta_nand *priv, const u_char *buf,
int len)
{
- struct ams_delta_nand *priv = nand_get_controller_data(this);
int i;

if (priv->data_in)
@@ -112,9 +111,9 @@ static void ams_delta_write_buf(struct nand_chip *this, const u_char *buf,
ams_delta_io_write(priv, buf[i]);
}

-static void ams_delta_read_buf(struct nand_chip *this, u_char *buf, int len)
+static void ams_delta_read_buf(struct ams_delta_nand *priv, u_char *buf,
+ int len)
{
- struct ams_delta_nand *priv = nand_get_controller_data(this);
int i;

if (!priv->data_in)
@@ -124,46 +123,63 @@ static void ams_delta_read_buf(struct nand_chip *this, u_char *buf, int len)
buf[i] = ams_delta_io_read(priv);
}

-static u_char ams_delta_read_byte(struct nand_chip *this)
-{
- u_char res;
-
- ams_delta_read_buf(this, &res, 1);
-
- return res;
-}
-
-/*
- * Command control function
- *
- * ctrl:
- * NAND_NCE: bit 0 -> bit 2
- * NAND_CLE: bit 1 -> bit 7
- * NAND_ALE: bit 2 -> bit 6
- */
-static void ams_delta_hwcontrol(struct nand_chip *this, int cmd,
- unsigned int ctrl)
+static void ams_delta_select_chip(struct nand_chip *this, int n)
{
struct ams_delta_nand *priv = nand_get_controller_data(this);

- if (ctrl & NAND_CTRL_CHANGE) {
- gpiod_set_value(priv->gpiod_nce, !(ctrl & NAND_NCE));
- gpiod_set_value(priv->gpiod_cle, !!(ctrl & NAND_CLE));
- gpiod_set_value(priv->gpiod_ale, !!(ctrl & NAND_ALE));
- }
-
- if (cmd != NAND_CMD_NONE) {
- u_char byte = cmd;
+ if (n > 0)
+ return;

- ams_delta_write_buf(this, &byte, 1);
- }
+ gpiod_set_value(priv->gpiod_nce, n < 0);
}

-static int ams_delta_nand_ready(struct nand_chip *this)
+static int ams_delta_exec_op(struct nand_chip *this,
+ const struct nand_operation *op, bool check_only)
{
struct ams_delta_nand *priv = nand_get_controller_data(this);
+ const struct nand_op_instr *instr;
+ int ret = 0;
+
+ for (instr = op->instrs; instr < op->instrs + op->ninstrs; instr++) {
+
+ switch (instr->type) {
+ case NAND_OP_CMD_INSTR:
+ gpiod_set_value(priv->gpiod_cle, 1);
+ ams_delta_write_buf(priv, &instr->ctx.cmd.opcode, 1);
+ gpiod_set_value(priv->gpiod_cle, 0);
+ break;
+
+ case NAND_OP_ADDR_INSTR:
+ gpiod_set_value(priv->gpiod_ale, 1);
+ ams_delta_write_buf(priv, instr->ctx.addr.addrs,
+ instr->ctx.addr.naddrs);
+ gpiod_set_value(priv->gpiod_ale, 0);
+ break;
+
+ case NAND_OP_DATA_IN_INSTR:
+ ams_delta_read_buf(priv, instr->ctx.data.buf.in,
+ instr->ctx.data.len);
+ break;
+
+ case NAND_OP_DATA_OUT_INSTR:
+ ams_delta_write_buf(priv, instr->ctx.data.buf.out,
+ instr->ctx.data.len);
+ break;
+
+ case NAND_OP_WAITRDY_INSTR:
+ ret = priv->gpiod_rdy ?
+ nand_gpio_waitrdy(this, priv->gpiod_rdy,
+ instr->ctx.waitrdy.timeout_ms) :
+ nand_soft_waitrdy(this,
+ instr->ctx.waitrdy.timeout_ms);
+ break;
+ }
+
+ if (ret)
+ break;
+ }

- return gpiod_get_value(priv->gpiod_rdy);
+ return ret;
}


@@ -211,12 +227,8 @@ static int ams_delta_init(struct platform_device *pdev)
nand_set_controller_data(this, priv);

/* Set address of NAND IO lines */
- this->legacy.IO_ADDR_R = io_base + OMAP_MPUIO_INPUT_LATCH;
- this->legacy.IO_ADDR_W = io_base + OMAP_MPUIO_OUTPUT;
- this->legacy.read_byte = ams_delta_read_byte;
- this->legacy.write_buf = ams_delta_write_buf;
- this->legacy.read_buf = ams_delta_read_buf;
- this->legacy.cmd_ctrl = ams_delta_hwcontrol;
+ this->select_chip = ams_delta_select_chip;
+ this->exec_op = ams_delta_exec_op;

priv->gpiod_rdy = devm_gpiod_get_optional(&pdev->dev, "rdy", GPIOD_IN);
if (IS_ERR(priv->gpiod_rdy)) {
@@ -225,11 +237,6 @@ static int ams_delta_init(struct platform_device *pdev)
goto out_mtd;
}

- if (priv->gpiod_rdy)
- this->legacy.dev_ready = ams_delta_nand_ready;
-
- /* 25 us command delay time */
- this->legacy.chip_delay = 30;
this->ecc.mode = NAND_ECC_SOFT;
this->ecc.algo = NAND_ECC_HAMMING;

--
2.16.4


2018-10-13 05:56:44

by Boris Brezillon

[permalink] [raw]
Subject: Re: [PATCH v2 1/2] mtd: rawnand: Provide helper for polling GPIO R/B pin

Hi Janusz,

On Fri, 12 Oct 2018 22:41:00 +0200
Janusz Krzysztofik <[email protected]> wrote:

> Each controller driver with access to NAND R/B pin over GPIO would have
> to reimplement the polling loop otherwise.
>
> Signed-off-by: Janusz Krzysztofik <[email protected]>
> ---
> Changelog:
> v2:
> New patch - v1 consisted of only one patch (the followning one)
>
>
> drivers/mtd/nand/raw/nand_base.c | 38 ++++++++++++++++++++++++++++++++++++++
> include/linux/mtd/rawnand.h | 10 ++++++++++
> 2 files changed, 48 insertions(+)
>
> diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c
> index 05bd0779fe9b..ff1ac4a3c647 100644
> --- a/drivers/mtd/nand/raw/nand_base.c
> +++ b/drivers/mtd/nand/raw/nand_base.c
> @@ -45,6 +45,9 @@
> #include <linux/io.h>
> #include <linux/mtd/partitions.h>
> #include <linux/of.h>
> +#ifdef CONFIG_GPIOLIB
> +#include <linux/gpio/consumer.h>
> +#endif

The ifdef is not needed here, linux/gpio/consumer.h already has dummy
wrappers when CONFIG_GPIOLIB is not enabled.

>
> #include "internals.h"
>
> @@ -531,6 +534,41 @@ int nand_soft_waitrdy(struct nand_chip *chip, unsigned long timeout_ms)
> };
> EXPORT_SYMBOL_GPL(nand_soft_waitrdy);
>
> +#ifdef CONFIG_GPIOLIB
> +/**
> + * nand_gpio_waitrdy - Poll R/B GPIO pin until ready
> + * @chip: NAND chip structure
> + * @gpiod: GPIO descriptor of R/B pin
> + * @timeout_ms: Timeout in ms
> + *
> + * Poll the R/B GPIO pin until it becomes ready. If that does not happen
> + * whitin the specified timeout, -ETIMEDOUT is returned.
> + *
> + * This helper is intended to be used when the controller has access to the
> + * NAND R/B pin over GPIO.
> + *
> + * Be aware that calling this helper from an ->exec_op() implementation means
> + * ->exec_op() must be re-entrant.

This is not true for this function: it does not call nand_exec_op().

> + *
> + * Return 0 if the R/B pin indicates chip is ready, a negative error otherwise.
> + */
> +int nand_gpio_waitrdy(struct nand_chip *chip, struct gpio_desc *gpiod,
> + unsigned long timeout_ms)
> +{
> + /* Wait until command is processed or timeout occurs */
> + timeout_ms = jiffies + msecs_to_jiffies(timeout_ms);
> + do {
> + if (gpiod_get_value_cansleep(gpiod))
> + return 0;
> +
> + cond_resched();
> + } while (time_before(jiffies, timeout_ms));

> +
> + return gpiod_get_value_cansleep(gpiod) ? 0 : -ETIMEDOUT;
> +};
> +EXPORT_SYMBOL_GPL(nand_gpio_waitrdy);
> +#endif

Hm, I don't see any other helpers defined in #ifdef blocks though most
of them are optionals and are most of the time not used by drivers.
Let's keep things consistent (at the expense of embedding unused code
in nand.o) and remove the #ifdef here. If someone starts complaining
about the size of the rawnand core, we'll consider doing that.

> +
> /**
> * panic_nand_get_device - [GENERIC] Get chip for selected access
> * @chip: the nand chip descriptor
> diff --git a/include/linux/mtd/rawnand.h b/include/linux/mtd/rawnand.h
> index e10b126e148f..09f0ed1345b1 100644
> --- a/include/linux/mtd/rawnand.h
> +++ b/include/linux/mtd/rawnand.h
> @@ -1346,4 +1346,14 @@ void nand_release(struct nand_chip *chip);
> */
> int nand_soft_waitrdy(struct nand_chip *chip, unsigned long timeout_ms);
>
> +#ifdef CONFIG_GPIOLIB
> +struct gpio_desc;
> +/*
> + * External helper for controller drivers that have to implement the WAITRDY
> + * instruction and do have GPIO pin to check it.
> + */

You can drop this comment, this is already explained in the kerneldoc
header above the function def.

> +int nand_gpio_waitrdy(struct nand_chip *chip, struct gpio_desc *gpiod,
> + unsigned long timeout_ms);
> +#endif
> +
> #endif /* __LINUX_MTD_RAWNAND_H */

Regards,

Boris

2018-10-13 06:06:24

by Boris Brezillon

[permalink] [raw]
Subject: Re: [PATCH v2 2/2] mtd: rawnand: ams-delta: Use ->exec_op()

Hi Janusz,

On Fri, 12 Oct 2018 22:41:01 +0200
Janusz Krzysztofik <[email protected]> wrote:

> Replace legacy callbacks with ->select_chip() and ->exec_op().
>
> In order to remove any references to legacy structure members, use of
> .IO_ADDR_R/W has been replaced wit runtime calculations based on

^ with

> priv->io_base.

Can we do that in 2 steps?

1/ Stop using .IO_ADDR_R/W
2/ Convert the driver to ->exec_op()

>
> Suggested-by: Boris Brezillon <[email protected]>
> Signed-off-by: Janusz Krzysztofik <[email protected]>
> ---

[...]

> -static int ams_delta_nand_ready(struct nand_chip *this)
> +static int ams_delta_exec_op(struct nand_chip *this,
> + const struct nand_operation *op, bool check_only)
> {
> struct ams_delta_nand *priv = nand_get_controller_data(this);
> + const struct nand_op_instr *instr;
> + int ret = 0;
> +

You should have:

if (check_only)
return 0;

Other than that, the conversion looks good, so you can add

Reviewed-by: Boris Brezillon <[email protected]> once you've
addressed my comments.

Regards,

Boris

> + for (instr = op->instrs; instr < op->instrs + op->ninstrs; instr++) {
> +
> + switch (instr->type) {
> + case NAND_OP_CMD_INSTR:
> + gpiod_set_value(priv->gpiod_cle, 1);
> + ams_delta_write_buf(priv, &instr->ctx.cmd.opcode, 1);
> + gpiod_set_value(priv->gpiod_cle, 0);
> + break;
> +
> + case NAND_OP_ADDR_INSTR:
> + gpiod_set_value(priv->gpiod_ale, 1);
> + ams_delta_write_buf(priv, instr->ctx.addr.addrs,
> + instr->ctx.addr.naddrs);
> + gpiod_set_value(priv->gpiod_ale, 0);
> + break;
> +
> + case NAND_OP_DATA_IN_INSTR:
> + ams_delta_read_buf(priv, instr->ctx.data.buf.in,
> + instr->ctx.data.len);
> + break;
> +
> + case NAND_OP_DATA_OUT_INSTR:
> + ams_delta_write_buf(priv, instr->ctx.data.buf.out,
> + instr->ctx.data.len);
> + break;
> +
> + case NAND_OP_WAITRDY_INSTR:
> + ret = priv->gpiod_rdy ?
> + nand_gpio_waitrdy(this, priv->gpiod_rdy,
> + instr->ctx.waitrdy.timeout_ms) :
> + nand_soft_waitrdy(this,
> + instr->ctx.waitrdy.timeout_ms);
> + break;
> + }
> +
> + if (ret)
> + break;
> + }
>
> - return gpiod_get_value(priv->gpiod_rdy);
> + return ret;
> }

2018-10-15 08:34:24

by Marek Szyprowski

[permalink] [raw]
Subject: Re: [RFT PATCH] mmc: pwrseq_simple: Fix incorrect handling of GPIO bitmap

Hi Janusz,

On 2018-10-12 21:09, Janusz Krzysztofik wrote:
> Commit b9762bebc633 ("gpiolib: Pass bitmaps, not integer arrays, to
> get/set array") changed the way GPIO values are passed to
> gpiod_get/set_array_value() and friends. The new code introduced into
> mmc_pwrseq_simple_set_gpios_value() incorrectly interpretes the 'value'
> argument as a bitmap of GPIO values and assigns it directly to the
> 'values' bitmap variable passed to gpiod_set_array_value_cansleep()
> instead of filling that bitmap with bits equal to the 'value' argument.
> As a result, only member 0 of the array is handled correctly.
>
> Moreover, wrong assumption is taken about the 'values' bitmap size not
> exceding the number of bits of the 'value' argument type.
>
> Fix it.
>
> Signed-off-by: Janusz Krzysztofik <[email protected]>
> ---
> Hi,
>
> I think that patch has been missed while we were resolving issues
> related to GPIO fast bitmap processing. Since all issues other than the
> one addreessed by this patch have been been hopefully identified and
> fixed, GPIO tree seems now to be in good shape in regard to that.
> However, I believe pwrseq_simple is still broken. Hence, I'm
> resubmitting this patch to Ulf for inclusion in MMC tree, Cc: many other
> people who are kindly requested to test it if possible.
>
> I've identified the following DT files representing devices which may be
> affected (have more than one GPIO assigned to pwrseq_simple):
> - arch/arm/boot/dts/imx6qdl-sr-som-brcm.dtsi
> - arch/arm/boot/dts/exynos5250-snow-common.dtsi

On Samsung Snow Chromebook it doesn't change anything. Board boots and
detects WiFi SDIO card before and after applying it on top on Linux
next-20181012.

Tested-by: Marek Szyprowski <m.szyprowski

> - arch/arm/boot/dts/imx6sl-warp.dts
> - arch/arm/boot/dts/omap3-igep0030.dts
> - arch/arm/boot/dts/omap3-igep0020.dts
> - arch/arm/boot/dts/rk3036-kylin.dts
> - arch/arm64/boot/dts/rockchip/rk3368-r88.dts
> - arch/arm64/boot/dts/amlogic/meson-gxbb-vega-s95.dtsi
>
> Please start with checking if pwrseq_simple from linux-next works for
> you and if not, please test if this patch fixes the issue.
>
> Thanks,
> Janusz
>
>
> drivers/mmc/core/pwrseq_simple.c | 13 +++++++++++--
> 1 file changed, 11 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/mmc/core/pwrseq_simple.c b/drivers/mmc/core/pwrseq_simple.c
> index 7f882a2bb872..ece34c734693 100644
> --- a/drivers/mmc/core/pwrseq_simple.c
> +++ b/drivers/mmc/core/pwrseq_simple.c
> @@ -40,13 +40,22 @@ static void mmc_pwrseq_simple_set_gpios_value(struct mmc_pwrseq_simple *pwrseq,
> struct gpio_descs *reset_gpios = pwrseq->reset_gpios;
>
> if (!IS_ERR(reset_gpios)) {
> - DECLARE_BITMAP(values, BITS_PER_TYPE(value));
> + unsigned long *values;
> int nvalues = reset_gpios->ndescs;
>
> - values[0] = value;
> + values = bitmap_alloc(nvalues, GFP_KERNEL);
> + if (!values)
> + return;
> +
> + if (value)
> + bitmap_fill(values, nvalues);
> + else
> + bitmap_zero(values, nvalues);
>
> gpiod_set_array_value_cansleep(nvalues, reset_gpios->desc,
> reset_gpios->info, values);
> +
> + kfree(values);
> }
> }
>

Best regards
--
Marek Szyprowski, PhD
Samsung R&D Institute Poland


2018-10-15 10:31:46

by Ulf Hansson

[permalink] [raw]
Subject: Re: [RFT PATCH] mmc: pwrseq_simple: Fix incorrect handling of GPIO bitmap

On 12 October 2018 at 21:09, Janusz Krzysztofik <[email protected]> wrote:
> Commit b9762bebc633 ("gpiolib: Pass bitmaps, not integer arrays, to
> get/set array") changed the way GPIO values are passed to
> gpiod_get/set_array_value() and friends. The new code introduced into
> mmc_pwrseq_simple_set_gpios_value() incorrectly interpretes the 'value'
> argument as a bitmap of GPIO values and assigns it directly to the
> 'values' bitmap variable passed to gpiod_set_array_value_cansleep()
> instead of filling that bitmap with bits equal to the 'value' argument.
> As a result, only member 0 of the array is handled correctly.
>
> Moreover, wrong assumption is taken about the 'values' bitmap size not
> exceding the number of bits of the 'value' argument type.
>
> Fix it.
>
> Signed-off-by: Janusz Krzysztofik <[email protected]>

Acked-by: Ulf Hansson <[email protected]>

Kind regards
Uffe

> ---
> Hi,
>
> I think that patch has been missed while we were resolving issues
> related to GPIO fast bitmap processing. Since all issues other than the
> one addreessed by this patch have been been hopefully identified and
> fixed, GPIO tree seems now to be in good shape in regard to that.
> However, I believe pwrseq_simple is still broken. Hence, I'm
> resubmitting this patch to Ulf for inclusion in MMC tree, Cc: many other
> people who are kindly requested to test it if possible.
>
> I've identified the following DT files representing devices which may be
> affected (have more than one GPIO assigned to pwrseq_simple):
> - arch/arm/boot/dts/imx6qdl-sr-som-brcm.dtsi
> - arch/arm/boot/dts/exynos5250-snow-common.dtsi
> - arch/arm/boot/dts/imx6sl-warp.dts
> - arch/arm/boot/dts/omap3-igep0030.dts
> - arch/arm/boot/dts/omap3-igep0020.dts
> - arch/arm/boot/dts/rk3036-kylin.dts
> - arch/arm64/boot/dts/rockchip/rk3368-r88.dts
> - arch/arm64/boot/dts/amlogic/meson-gxbb-vega-s95.dtsi
>
> Please start with checking if pwrseq_simple from linux-next works for
> you and if not, please test if this patch fixes the issue.
>
> Thanks,
> Janusz
>
>
> drivers/mmc/core/pwrseq_simple.c | 13 +++++++++++--
> 1 file changed, 11 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/mmc/core/pwrseq_simple.c b/drivers/mmc/core/pwrseq_simple.c
> index 7f882a2bb872..ece34c734693 100644
> --- a/drivers/mmc/core/pwrseq_simple.c
> +++ b/drivers/mmc/core/pwrseq_simple.c
> @@ -40,13 +40,22 @@ static void mmc_pwrseq_simple_set_gpios_value(struct mmc_pwrseq_simple *pwrseq,
> struct gpio_descs *reset_gpios = pwrseq->reset_gpios;
>
> if (!IS_ERR(reset_gpios)) {
> - DECLARE_BITMAP(values, BITS_PER_TYPE(value));
> + unsigned long *values;
> int nvalues = reset_gpios->ndescs;
>
> - values[0] = value;
> + values = bitmap_alloc(nvalues, GFP_KERNEL);
> + if (!values)
> + return;
> +
> + if (value)
> + bitmap_fill(values, nvalues);
> + else
> + bitmap_zero(values, nvalues);
>
> gpiod_set_array_value_cansleep(nvalues, reset_gpios->desc,
> reset_gpios->info, values);
> +
> + kfree(values);
> }
> }
>
> --
> 2.16.4
>

2018-10-15 14:28:11

by Linus Walleij

[permalink] [raw]
Subject: Re: [RFT PATCH] mmc: pwrseq_simple: Fix incorrect handling of GPIO bitmap

On Fri, Oct 12, 2018 at 9:07 PM Janusz Krzysztofik <[email protected]> wrote:

> Commit b9762bebc633 ("gpiolib: Pass bitmaps, not integer arrays, to
> get/set array") changed the way GPIO values are passed to
> gpiod_get/set_array_value() and friends. The new code introduced into
> mmc_pwrseq_simple_set_gpios_value() incorrectly interpretes the 'value'
> argument as a bitmap of GPIO values and assigns it directly to the
> 'values' bitmap variable passed to gpiod_set_array_value_cansleep()
> instead of filling that bitmap with bits equal to the 'value' argument.
> As a result, only member 0 of the array is handled correctly.
>
> Moreover, wrong assumption is taken about the 'values' bitmap size not
> exceding the number of bits of the 'value' argument type.
>
> Fix it.
>
> Signed-off-by: Janusz Krzysztofik <[email protected]>

Patch applied with Ulf's and Marek's tags!

Yours,
Linus Walleij

2018-10-15 19:40:50

by Janusz Krzysztofik

[permalink] [raw]
Subject: [PATCH v3 3/3] mtd: rawnand: ams-delta: Convert the driver to ->exec_op()

Replace legacy callbacks with ->select_chip() and ->exec_op().

Suggested-by: Boris Brezillon <[email protected]>
Signed-off-by: Janusz Krzysztofik <[email protected]>
Reviewed-by: Boris Brezillon <[email protected]>
---
Changelog:
v3:
- former PATCH 2/2 from which PATCH v3 2/3 has been split out,
- added missing return on check_only - requested by Boris Brezillon,
thanks
v2:
- replace references to legacy structure .IO_ADDR_R/W members with
runtime calculated values - requested by Boris Brezillon, thanks!
- modify ams_delta_read/write_buf() functions, no longer exposed as
callbacks, to accept driver private structure instead of struct
nand_chip,
- use newly introduced nand_gpio_waitrdy() helper instead of legacy
nand_wait_ready() - suggested by Boris Brezillon, thanks!
- remove no longer needed ams_delta_dev_ready() legacy callback and
legacy structure member .chip_delay.

drivers/mtd/nand/raw/ams-delta.c | 100 ++++++++++++++++++++++-----------------
1 file changed, 56 insertions(+), 44 deletions(-)

diff --git a/drivers/mtd/nand/raw/ams-delta.c b/drivers/mtd/nand/raw/ams-delta.c
index 46193db6d368..9ca70aab199d 100644
--- a/drivers/mtd/nand/raw/ams-delta.c
+++ b/drivers/mtd/nand/raw/ams-delta.c
@@ -99,10 +99,9 @@ static void ams_delta_dir_input(struct ams_delta_nand *priv, bool in)
priv->data_in = in;
}

-static void ams_delta_write_buf(struct nand_chip *this, const u_char *buf,
+static void ams_delta_write_buf(struct ams_delta_nand *priv, const u_char *buf,
int len)
{
- struct ams_delta_nand *priv = nand_get_controller_data(this);
int i;

if (priv->data_in)
@@ -112,9 +111,9 @@ static void ams_delta_write_buf(struct nand_chip *this, const u_char *buf,
ams_delta_io_write(priv, buf[i]);
}

-static void ams_delta_read_buf(struct nand_chip *this, u_char *buf, int len)
+static void ams_delta_read_buf(struct ams_delta_nand *priv, u_char *buf,
+ int len)
{
- struct ams_delta_nand *priv = nand_get_controller_data(this);
int i;

if (!priv->data_in)
@@ -124,46 +123,66 @@ static void ams_delta_read_buf(struct nand_chip *this, u_char *buf, int len)
buf[i] = ams_delta_io_read(priv);
}

-static u_char ams_delta_read_byte(struct nand_chip *this)
-{
- u_char res;
-
- ams_delta_read_buf(this, &res, 1);
-
- return res;
-}
-
-/*
- * Command control function
- *
- * ctrl:
- * NAND_NCE: bit 0 -> bit 2
- * NAND_CLE: bit 1 -> bit 7
- * NAND_ALE: bit 2 -> bit 6
- */
-static void ams_delta_hwcontrol(struct nand_chip *this, int cmd,
- unsigned int ctrl)
+static void ams_delta_select_chip(struct nand_chip *this, int n)
{
struct ams_delta_nand *priv = nand_get_controller_data(this);

- if (ctrl & NAND_CTRL_CHANGE) {
- gpiod_set_value(priv->gpiod_nce, !(ctrl & NAND_NCE));
- gpiod_set_value(priv->gpiod_cle, !!(ctrl & NAND_CLE));
- gpiod_set_value(priv->gpiod_ale, !!(ctrl & NAND_ALE));
- }
-
- if (cmd != NAND_CMD_NONE) {
- u_char byte = cmd;
+ if (n > 0)
+ return;

- ams_delta_write_buf(this, &byte, 1);
- }
+ gpiod_set_value(priv->gpiod_nce, n < 0);
}

-static int ams_delta_nand_ready(struct nand_chip *this)
+static int ams_delta_exec_op(struct nand_chip *this,
+ const struct nand_operation *op, bool check_only)
{
struct ams_delta_nand *priv = nand_get_controller_data(this);
+ const struct nand_op_instr *instr;
+ int ret = 0;
+
+ if (check_only)
+ return 0;
+
+ for (instr = op->instrs; instr < op->instrs + op->ninstrs; instr++) {
+
+ switch (instr->type) {
+ case NAND_OP_CMD_INSTR:
+ gpiod_set_value(priv->gpiod_cle, 1);
+ ams_delta_write_buf(priv, &instr->ctx.cmd.opcode, 1);
+ gpiod_set_value(priv->gpiod_cle, 0);
+ break;
+
+ case NAND_OP_ADDR_INSTR:
+ gpiod_set_value(priv->gpiod_ale, 1);
+ ams_delta_write_buf(priv, instr->ctx.addr.addrs,
+ instr->ctx.addr.naddrs);
+ gpiod_set_value(priv->gpiod_ale, 0);
+ break;
+
+ case NAND_OP_DATA_IN_INSTR:
+ ams_delta_read_buf(priv, instr->ctx.data.buf.in,
+ instr->ctx.data.len);
+ break;
+
+ case NAND_OP_DATA_OUT_INSTR:
+ ams_delta_write_buf(priv, instr->ctx.data.buf.out,
+ instr->ctx.data.len);
+ break;
+
+ case NAND_OP_WAITRDY_INSTR:
+ ret = priv->gpiod_rdy ?
+ nand_gpio_waitrdy(this, priv->gpiod_rdy,
+ instr->ctx.waitrdy.timeout_ms) :
+ nand_soft_waitrdy(this,
+ instr->ctx.waitrdy.timeout_ms);
+ break;
+ }
+
+ if (ret)
+ break;
+ }

- return gpiod_get_value(priv->gpiod_rdy);
+ return ret;
}


@@ -211,10 +230,8 @@ static int ams_delta_init(struct platform_device *pdev)
nand_set_controller_data(this, priv);

/* Set address of NAND IO lines */
- this->legacy.read_byte = ams_delta_read_byte;
- this->legacy.write_buf = ams_delta_write_buf;
- this->legacy.read_buf = ams_delta_read_buf;
- this->legacy.cmd_ctrl = ams_delta_hwcontrol;
+ this->select_chip = ams_delta_select_chip;
+ this->exec_op = ams_delta_exec_op;

priv->gpiod_rdy = devm_gpiod_get_optional(&pdev->dev, "rdy", GPIOD_IN);
if (IS_ERR(priv->gpiod_rdy)) {
@@ -223,11 +240,6 @@ static int ams_delta_init(struct platform_device *pdev)
goto out_mtd;
}

- if (priv->gpiod_rdy)
- this->legacy.dev_ready = ams_delta_nand_ready;
-
- /* 25 us command delay time */
- this->legacy.chip_delay = 30;
this->ecc.mode = NAND_ECC_SOFT;
this->ecc.algo = NAND_ECC_HAMMING;

--
2.16.4


2018-10-15 19:41:18

by Janusz Krzysztofik

[permalink] [raw]
Subject: [PATCH v3 2/3] mtd: rawnand: ams-delta: Stop using legacy .IOADDR_R/W

Replace use of legacy .IOADDR_R/W with runtime calculations based on
priv->io_base.

Suggested-by: Boris Brezillon <[email protected]>
Signed-off-by: Janusz Krzysztofik <[email protected]>
Reviewed-by: Boris Brezillon <[email protected]>
---
Changelog:
v3:
New patch, split out from former PATCH 2/2 on Boris request - thanks

drivers/mtd/nand/raw/ams-delta.c | 6 ++----
1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/drivers/mtd/nand/raw/ams-delta.c b/drivers/mtd/nand/raw/ams-delta.c
index 5ba180a291eb..46193db6d368 100644
--- a/drivers/mtd/nand/raw/ams-delta.c
+++ b/drivers/mtd/nand/raw/ams-delta.c
@@ -75,7 +75,7 @@ static const struct mtd_partition partition_info[] = {

static void ams_delta_io_write(struct ams_delta_nand *priv, u_char byte)
{
- writew(byte, priv->nand_chip.legacy.IO_ADDR_W);
+ writew(byte, priv->io_base + OMAP_MPUIO_OUTPUT);
gpiod_set_value(priv->gpiod_nwe, 0);
ndelay(40);
gpiod_set_value(priv->gpiod_nwe, 1);
@@ -87,7 +87,7 @@ static u_char ams_delta_io_read(struct ams_delta_nand *priv)

gpiod_set_value(priv->gpiod_nre, 0);
ndelay(40);
- res = readw(priv->nand_chip.legacy.IO_ADDR_R);
+ res = readw(priv->io_base + OMAP_MPUIO_INPUT_LATCH);
gpiod_set_value(priv->gpiod_nre, 1);

return res;
@@ -211,8 +211,6 @@ static int ams_delta_init(struct platform_device *pdev)
nand_set_controller_data(this, priv);

/* Set address of NAND IO lines */
- this->legacy.IO_ADDR_R = io_base + OMAP_MPUIO_INPUT_LATCH;
- this->legacy.IO_ADDR_W = io_base + OMAP_MPUIO_OUTPUT;
this->legacy.read_byte = ams_delta_read_byte;
this->legacy.write_buf = ams_delta_write_buf;
this->legacy.read_buf = ams_delta_read_buf;
--
2.16.4


2018-10-15 19:41:30

by Janusz Krzysztofik

[permalink] [raw]
Subject: [PATCH v3 1/3] mtd: rawnand: Provide helper for polling GPIO R/B pin

Each controller driver having access to NAND R/B pin over GPIO would
have to reimplement the polling loop otherwise.

Suggested-by: Boris Brezillon <[email protected]>
Signed-off-by: Janusz Krzysztofik <[email protected]>
Reviewed-by: Boris Brezillon <[email protected]>
---
Changelog:
v3:
- don't surround added code with #ifdef CONFIG_GPIOLIB - sugeested by
Boris Brezillon - thanks,
- drop inadequate comment on calling ->exec_op() recursively - catched
by Boris Brezillon, thanks,
- drop redundant commend from header file - suggensted by Boris
Brezillon - thanks,
- update comment on waiting for R/B pin rather than command completion
v2:
New patch - v1 consisted of only one patch reusing legacy helper

drivers/mtd/nand/raw/nand_base.c | 31 +++++++++++++++++++++++++++++++
include/linux/mtd/rawnand.h | 4 ++++
2 files changed, 35 insertions(+)

diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c
index 05bd0779fe9b..0d5a2dc59b8d 100644
--- a/drivers/mtd/nand/raw/nand_base.c
+++ b/drivers/mtd/nand/raw/nand_base.c
@@ -45,6 +45,7 @@
#include <linux/io.h>
#include <linux/mtd/partitions.h>
#include <linux/of.h>
+#include <linux/gpio/consumer.h>

#include "internals.h"

@@ -531,6 +532,36 @@ int nand_soft_waitrdy(struct nand_chip *chip, unsigned long timeout_ms)
};
EXPORT_SYMBOL_GPL(nand_soft_waitrdy);

+/**
+ * nand_gpio_waitrdy - Poll R/B GPIO pin until ready
+ * @chip: NAND chip structure
+ * @gpiod: GPIO descriptor of R/B pin
+ * @timeout_ms: Timeout in ms
+ *
+ * Poll the R/B GPIO pin until it becomes ready. If that does not happen
+ * whitin the specified timeout, -ETIMEDOUT is returned.
+ *
+ * This helper is intended to be used when the controller has access to the
+ * NAND R/B pin over GPIO.
+ *
+ * Return 0 if the R/B pin indicates chip is ready, a negative error otherwise.
+ */
+int nand_gpio_waitrdy(struct nand_chip *chip, struct gpio_desc *gpiod,
+ unsigned long timeout_ms)
+{
+ /* Wait until R/B pin indicates chip is ready or timeout occurs */
+ timeout_ms = jiffies + msecs_to_jiffies(timeout_ms);
+ do {
+ if (gpiod_get_value_cansleep(gpiod))
+ return 0;
+
+ cond_resched();
+ } while (time_before(jiffies, timeout_ms));
+
+ return gpiod_get_value_cansleep(gpiod) ? 0 : -ETIMEDOUT;
+};
+EXPORT_SYMBOL_GPL(nand_gpio_waitrdy);
+
/**
* panic_nand_get_device - [GENERIC] Get chip for selected access
* @chip: the nand chip descriptor
diff --git a/include/linux/mtd/rawnand.h b/include/linux/mtd/rawnand.h
index e10b126e148f..4e91a70ede10 100644
--- a/include/linux/mtd/rawnand.h
+++ b/include/linux/mtd/rawnand.h
@@ -1346,4 +1346,8 @@ void nand_release(struct nand_chip *chip);
*/
int nand_soft_waitrdy(struct nand_chip *chip, unsigned long timeout_ms);

+struct gpio_desc;
+int nand_gpio_waitrdy(struct nand_chip *chip, struct gpio_desc *gpiod,
+ unsigned long timeout_ms);
+
#endif /* __LINUX_MTD_RAWNAND_H */
--
2.16.4


2018-11-11 20:53:56

by Miquel Raynal

[permalink] [raw]
Subject: Re: [PATCH v3 1/3] mtd: rawnand: Provide helper for polling GPIO R/B pin

Hi Janusz,

Janusz Krzysztofik <[email protected]> wrote on Mon, 15 Oct 2018
21:41:28 +0200:

> Each controller driver having access to NAND R/B pin over GPIO would
> have to reimplement the polling loop otherwise.
>
> Suggested-by: Boris Brezillon <[email protected]>
> Signed-off-by: Janusz Krzysztofik <[email protected]>
> Reviewed-by: Boris Brezillon <[email protected]>
> ---

Series applied to nand/next.

Thanks,
Miquèl

2018-11-21 11:08:04

by Janusz Krzysztofik

[permalink] [raw]
Subject: [PATCH v4 4/4] ARM: OMAP1: ams-delta: Drop obsolete NAND resources

Amstrad Delta NAND driver now uses GPIO API for data I/O so there is no
need to assign memory I/O resource to the device any longer. Drop it.

Signed-off-by: Janusz Krzysztofik <[email protected]>
---
arch/arm/mach-omap1/board-ams-delta.c | 11 -----------
1 file changed, 11 deletions(-)

diff --git a/arch/arm/mach-omap1/board-ams-delta.c b/arch/arm/mach-omap1/board-ams-delta.c
index 30c0d18f372e..d594f60c3224 100644
--- a/arch/arm/mach-omap1/board-ams-delta.c
+++ b/arch/arm/mach-omap1/board-ams-delta.c
@@ -296,20 +296,9 @@ struct modem_private_data {

static struct modem_private_data modem_priv;

-static struct resource ams_delta_nand_resources[] = {
- [0] = {
- .start = OMAP1_MPUIO_BASE,
- .end = OMAP1_MPUIO_BASE +
- OMAP_MPUIO_IO_CNTL + sizeof(u32) - 1,
- .flags = IORESOURCE_MEM,
- },
-};
-
static struct platform_device ams_delta_nand_device = {
.name = "ams-delta-nand",
.id = -1,
- .num_resources = ARRAY_SIZE(ams_delta_nand_resources),
- .resource = ams_delta_nand_resources,
};

#define OMAP_GPIO_LABEL "gpio-0-15"
--
2.18.1


2018-11-21 11:09:05

by Janusz Krzysztofik

[permalink] [raw]
Subject: [PATCH v4 1/4] ARM: OMAP1: ams-delta: Provide GPIO lookup table for NAND data port

Data port used by Amstrad Delta NAND driver is actually an OMAP MPUIO
device, already under control of gpio-omap driver. The NAND driver
gets access to the port by ioremapping it and performs read/write
operations. That is done without any proteciton from other users
legally manipulating the port pins over GPIO API.

The plan is to convert the driver to access the port over GPIO consumer
API. Before that is implemented, the driver can already obtain
exclusive access to the port by requesting an array of its GPIO
descriptors.

Add respective entries to the NAND GPIO lookup table.

Signed-off-by: Janusz Krzysztofik <[email protected]>
Reviewed-by: Boris Brezillon <[email protected]>
Reviewed-by: Linus Walleij <[email protected]>
---
arch/arm/mach-omap1/board-ams-delta.c | 11 ++++++++++-
1 file changed, 10 insertions(+), 1 deletion(-)

diff --git a/arch/arm/mach-omap1/board-ams-delta.c b/arch/arm/mach-omap1/board-ams-delta.c
index 3d191fd52910..30c0d18f372e 100644
--- a/arch/arm/mach-omap1/board-ams-delta.c
+++ b/arch/arm/mach-omap1/board-ams-delta.c
@@ -312,7 +312,8 @@ static struct platform_device ams_delta_nand_device = {
.resource = ams_delta_nand_resources,
};

-#define OMAP_GPIO_LABEL "gpio-0-15"
+#define OMAP_GPIO_LABEL "gpio-0-15"
+#define OMAP_MPUIO_LABEL "mpuio"

static struct gpiod_lookup_table ams_delta_nand_gpio_table = {
.table = {
@@ -324,6 +325,14 @@ static struct gpiod_lookup_table ams_delta_nand_gpio_table = {
GPIO_LOOKUP(LATCH2_LABEL, LATCH2_PIN_NAND_NWE, "nwe", 0),
GPIO_LOOKUP(LATCH2_LABEL, LATCH2_PIN_NAND_ALE, "ale", 0),
GPIO_LOOKUP(LATCH2_LABEL, LATCH2_PIN_NAND_CLE, "cle", 0),
+ GPIO_LOOKUP_IDX(OMAP_MPUIO_LABEL, 0, "data", 0, 0),
+ GPIO_LOOKUP_IDX(OMAP_MPUIO_LABEL, 1, "data", 1, 0),
+ GPIO_LOOKUP_IDX(OMAP_MPUIO_LABEL, 2, "data", 2, 0),
+ GPIO_LOOKUP_IDX(OMAP_MPUIO_LABEL, 3, "data", 3, 0),
+ GPIO_LOOKUP_IDX(OMAP_MPUIO_LABEL, 4, "data", 4, 0),
+ GPIO_LOOKUP_IDX(OMAP_MPUIO_LABEL, 5, "data", 5, 0),
+ GPIO_LOOKUP_IDX(OMAP_MPUIO_LABEL, 6, "data", 6, 0),
+ GPIO_LOOKUP_IDX(OMAP_MPUIO_LABEL, 7, "data", 7, 0),
{ },
},
};
--
2.18.1


2018-11-21 11:09:10

by Janusz Krzysztofik

[permalink] [raw]
Subject: Subject: [PATCH v4 0/4] mtd: rawnand: ams-delta: Use GPIO API for data I/O


Finalize implementation of the idea suggested by Artem Bityutskiy and
Tony Lindgren, described in commit b027274d2e3a ("mtd: ams-delta: fix
request_mem_region() failure"). Use pure GPIO consumer API, as reqested
by Boris Brezillon.

Janusz Krzysztofik (4):
ARM: OMAP1: ams-delta: Provide GPIO lookup table for NAND data port
mtd: rawnand: ams-delta: Request data port GPIO resource
mtd: rawnand: ams-delta: Use GPIO API for data I/O
ARM: OMAP1: ams-delta: Drop obsolete NAND resources

arch/arm/mach-omap1/board-ams-delta.c | 22 ++----
drivers/mtd/nand/raw/ams-delta.c | 120 +++++++++++++++++++---------------
2 files changed, 80 insertions(+), 62 deletions(-)

Performance on Amstrad Delta is now acceptable after recent extensions
to GPIO API and rawnanad enhancements.

Series intented to be merged via mtd tree, should not conflict with
other OMAP1 patches submitted for 4.21.

Changelog:
v4:
- drop patches 1/7, 2/7, 5/7 6/7 - changes already merged,
- reintroduce postponed PATCH v2 06/12 as 4/4,
- rebase on nand-next for 4.21.

v3:
[PATCH v3 1/7] mtd: rawnand: ams-delta: show parent device in sysfs
- renamed and an explanation added based on other similar patches on
Marek Vasut request, thanks.
[PATCH v3 2/7] mtd: rawnand: ams-delta: Use private structure
- no changes.
[PATCH v3 3/7] ARM: OMAP1: ams-delta: Provide GPIO lookup table for NAND
data port
- no changes.
[PATCH v3 4/7] mtd: rawnand: ams-delta: request data port GPIO resource
- no changes.
[PATCH v3 5/7] mtd: rawnand: ams-delta: Set port direction when needed
- modified to set port direction only when needed instead of on each
transfer as suggested by Boris, thanks, though I kept separate
*_next_byte() functions to maximize performance as much as possible,
- moved back in front of "mtd: rawnand: ams-delta: use GPIO API for data
I/O" with a comment added referring to the planned switch to GPIO API.
[PATCH v3 6/7] mtd: rawnand: ams-delta: Simplify pointer resolution
- moved back in front of "mtd: rawnand: ams-delta: use GPIO API for data
I/O" on Boris request, thanks.
[RFC PATCH v3 7/7] mtd: rawnand: ams-delta: use GPIO API for data I/O
- rebased back on top of the two mentioned above,
- not intended to apply it yet due to performance issues on Amstrad Delta.
Removed from the series:
[RFC PATCH v2 09/12] gpiolib: Identify GPIO descriptor arrays with direct
mapping
[RFC PATCH v2 10/12] gpiolib: Introduce bitmap get/set array API extension
[RFC PATCH v2 11/12] mtd: rawnand: ams-delta: Use GPIO API bitmap extension
[RFC PATCH v2 12/12] gpiolib: Add fast processing path to bitmap API functions
- intended to be still iterated in a follow up series until performance
issues are resolved.
[RFC PATCH v2 06/12] ARM: OMAP1: ams-delta: drop obsolete NAND resources
- postponed until acceptable performance on Amstrad Delta is achieved.

v2:
[RFC PATCH v2 00/12] mtd: rawnand: ams-delta: Use GPIO API for data I/O
- renamed from former [RFC PATCH 0/8] mtd: rawnand: ams-delta: Use
gpio-omap accessors for data I/O
[RFC PATCH v2 01/12] mtd: rawnand: ams-delta: Assign mtd->dev.parent,
not mtd->owner
- split out from former [RFC PATCH 1/8] on Boris request, thanks.
[RFC PATCH v2 02/12] mtd: rawnand: ams-delta: Use private structure
- remaining part of the former [RFC PATCH 1/8].
[RFC PATCH v2 03/12] ARM: OMAP1: ams-delta: Provide GPIO lookup table
for NAND data port
- split out from former [RFC PATCH 5/8] on Boris requesst, thanks,
[RFC PATCH v2 04/12] mtd: rawnand: ams-delta: request data port GPIO resource
- remaining part of the former [RFC PATCH 5/8],
[RFC PATCH v2 05/12] mtd: rawnand: ams-delta: use GPIO API for data read/write
- reworked from former [RFC PATCH 8/8] on Boris requesst to use pure
GPIO API, thanks,
- moved up in front of former [RFC PATCH 3/8] on Boris request, thanks.
[RFC PATCH v2 06/12] ARM: OMAP1: ams-delta: drop obsolete NAND resources
- split out from former [RFC PATCH 8/8].
[RFC PATCH v2 07/12] mtd: rawnand: ams-delta: Set port direction once per
transfer
- reworked from former [RFC PATCH 3/8] on top of [RFC PATCH v2 05/12].
[RFC PATCH v2 08/12] mtd: rawnand: ams-delta: Simplify pointer resolution
on read/write
- reworked from former [RFC PATCH 4/8] on top of [RFC PATCH v2 08/12],
- renamed from 'Optimize' to 'Simplify' on Boris request, thanks.
[RFC PATCH v2 09/12] gpiolib: Identify GPIO descriptor arrays with direct
mapping
- new patch.
[RFC PATCH v2 10/12] gpiolib: Introduce bitmap get/set array API extension
- new patch.
[RFC PATCH v2 11/12] mtd: rawnand: ams-delta: Use GPIO API bitmap extension
- new patch.
[RFC PATCH v2 12/12] gpiolib: Add fast processing path to bitmap API functions
- new patch.
Removed from the series:
[RFC PATCH 2/8] mtd: rawnand: ams-delta: Write protect device during probe
- postponed on Boris request, thanks.
[RFC PATCH 6/8] gpio: omap: Add get/set_multiple() callbacks
- already applied by Linux in linux-gpio tree, thanks.
[RFC PATCH 7/8] mtd: rawnand: ams-delta: Check sanity of data GPIO resource
- most controversial one, no longer needed after switching to GPIO API.

Thanks,
Janusz


2018-11-21 12:52:44

by Janusz Krzysztofik

[permalink] [raw]
Subject: [PATCH v4 2/4] mtd: rawnand: ams-delta: Request data port GPIO resource

Data port used by the driver is actually an OMAP MPUIO device, already
under control of gpio-omap driver. For that reason we used to not
request the memory region of the port as that would fail because the
region is already busy. Despite that, we are still accessing the port
by just ioremapping it and performing read/write operations. Moreover,
we are doing that without any proteciton from other users legally
manipulating the port pins over GPIO API.

The plan is to convert the driver to access the port over GPIO consumer
API. Before that happens, already prevent from other users accessing
the port pins by requesting an array of its GPIO descriptors.

Signed-off-by: Janusz Krzysztofik <[email protected]>
Reviewed-by: Boris Brezillon <[email protected]>
Reviewed-by: Linus Walleij <[email protected]>
---
drivers/mtd/nand/raw/ams-delta.c | 11 +++++++++--
1 file changed, 9 insertions(+), 2 deletions(-)

diff --git a/drivers/mtd/nand/raw/ams-delta.c b/drivers/mtd/nand/raw/ams-delta.c
index f8eb4a419e77..bb50dda05654 100644
--- a/drivers/mtd/nand/raw/ams-delta.c
+++ b/drivers/mtd/nand/raw/ams-delta.c
@@ -190,6 +190,7 @@ static int ams_delta_init(struct platform_device *pdev)
struct mtd_info *mtd;
struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
void __iomem *io_base;
+ struct gpio_descs *data_gpiods;
int err = 0;

if (!res)
@@ -275,8 +276,14 @@ static int ams_delta_init(struct platform_device *pdev)
goto err_unmap;
}

- /* Initialize data port direction to a known state */
- ams_delta_dir_input(priv, true);
+ /* Request array of data pins, initialize them as input */
+ data_gpiods = devm_gpiod_get_array(&pdev->dev, "data", GPIOD_IN);
+ if (IS_ERR(data_gpiods)) {
+ err = PTR_ERR(data_gpiods);
+ dev_err(&pdev->dev, "data GPIO request failed: %d\n", err);
+ goto err_unmap;
+ }
+ priv->data_in = true;

/* Initialize the NAND controller object embedded in ams_delta_nand. */
priv->base.ops = &ams_delta_ops;
--
2.18.1


2018-11-21 12:53:37

by Janusz Krzysztofik

[permalink] [raw]
Subject: [PATCH v4 3/4] mtd: rawnand: ams-delta: Use GPIO API for data I/O

Don't readw()/writew() data directly from/to GPIO port which is under
control of gpio-omap driver, use GPIO consumer API instead.

The driver should now work with any 8-bit bidirectional GPIO port, not
only OMAP.

Signed-off-by: Janusz Krzysztofik <[email protected]>
Reviewed-by: Linus Walleij <[email protected]>
---
drivers/mtd/nand/raw/ams-delta.c | 109 +++++++++++++++++--------------
1 file changed, 61 insertions(+), 48 deletions(-)

diff --git a/drivers/mtd/nand/raw/ams-delta.c b/drivers/mtd/nand/raw/ams-delta.c
index bb50dda05654..8312182088c1 100644
--- a/drivers/mtd/nand/raw/ams-delta.c
+++ b/drivers/mtd/nand/raw/ams-delta.c
@@ -18,11 +18,10 @@
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/gpio/consumer.h>
-#include <linux/io.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/rawnand.h>
#include <linux/mtd/partitions.h>
-#include <linux/platform_data/gpio-omap.h>
+#include <linux/platform_device.h>
#include <linux/sizes.h>

/*
@@ -38,7 +37,7 @@ struct ams_delta_nand {
struct gpio_desc *gpiod_nwe;
struct gpio_desc *gpiod_ale;
struct gpio_desc *gpiod_cle;
- void __iomem *io_base;
+ struct gpio_descs *data_gpiods;
bool data_in;
};

@@ -67,42 +66,78 @@ static const struct mtd_partition partition_info[] = {
.size = 3 * SZ_256K },
};

-static void ams_delta_io_write(struct ams_delta_nand *priv, u8 byte)
+static void ams_delta_write_commit(struct ams_delta_nand *priv)
{
- writew(byte, priv->io_base + OMAP_MPUIO_OUTPUT);
gpiod_set_value(priv->gpiod_nwe, 0);
ndelay(40);
gpiod_set_value(priv->gpiod_nwe, 1);
}

+static void ams_delta_io_write(struct ams_delta_nand *priv, u8 byte)
+{
+ struct gpio_descs *data_gpiods = priv->data_gpiods;
+ DECLARE_BITMAP(values, BITS_PER_TYPE(byte)) = { byte, };
+
+ gpiod_set_raw_array_value(data_gpiods->ndescs, data_gpiods->desc,
+ data_gpiods->info, values);
+
+ ams_delta_write_commit(priv);
+}
+
+static void ams_delta_dir_output(struct ams_delta_nand *priv, u8 byte)
+{
+ struct gpio_descs *data_gpiods = priv->data_gpiods;
+ DECLARE_BITMAP(values, BITS_PER_TYPE(byte)) = { byte, };
+ int i;
+
+ for (i = 0; i < data_gpiods->ndescs; i++)
+ gpiod_direction_output_raw(data_gpiods->desc[i],
+ test_bit(i, values));
+
+ ams_delta_write_commit(priv);
+
+ priv->data_in = false;
+}
+
static u8 ams_delta_io_read(struct ams_delta_nand *priv)
{
u8 res;
+ struct gpio_descs *data_gpiods = priv->data_gpiods;
+ DECLARE_BITMAP(values, BITS_PER_TYPE(res)) = { 0, };

gpiod_set_value(priv->gpiod_nre, 0);
ndelay(40);
- res = readw(priv->io_base + OMAP_MPUIO_INPUT_LATCH);
+
+ gpiod_get_raw_array_value(data_gpiods->ndescs, data_gpiods->desc,
+ data_gpiods->info, values);
+
gpiod_set_value(priv->gpiod_nre, 1);

+ res = values[0];
return res;
}

-static void ams_delta_dir_input(struct ams_delta_nand *priv, bool in)
+static void ams_delta_dir_input(struct ams_delta_nand *priv)
{
- writew(in ? ~0 : 0, priv->io_base + OMAP_MPUIO_IO_CNTL);
- priv->data_in = in;
+ struct gpio_descs *data_gpiods = priv->data_gpiods;
+ int i;
+
+ for (i = 0; i < data_gpiods->ndescs; i++)
+ gpiod_direction_input(data_gpiods->desc[i]);
+
+ priv->data_in = true;
}

static void ams_delta_write_buf(struct ams_delta_nand *priv, const u8 *buf,
int len)
{
- int i;
+ int i = 0;

- if (priv->data_in)
- ams_delta_dir_input(priv, false);
+ if (len > 0 && priv->data_in)
+ ams_delta_dir_output(priv, buf[i++]);

- for (i = 0; i < len; i++)
- ams_delta_io_write(priv, buf[i]);
+ while (i < len)
+ ams_delta_io_write(priv, buf[i++]);
}

static void ams_delta_read_buf(struct ams_delta_nand *priv, u8 *buf, int len)
@@ -110,7 +145,7 @@ static void ams_delta_read_buf(struct ams_delta_nand *priv, u8 *buf, int len)
int i;

if (!priv->data_in)
- ams_delta_dir_input(priv, true);
+ ams_delta_dir_input(priv);

for (i = 0; i < len; i++)
buf[i] = ams_delta_io_read(priv);
@@ -188,14 +223,9 @@ static int ams_delta_init(struct platform_device *pdev)
struct ams_delta_nand *priv;
struct nand_chip *this;
struct mtd_info *mtd;
- struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- void __iomem *io_base;
struct gpio_descs *data_gpiods;
int err = 0;

- if (!res)
- return -ENXIO;
-
/* Allocate memory for MTD device structure and private data */
priv = devm_kzalloc(&pdev->dev, sizeof(struct ams_delta_nand),
GFP_KERNEL);
@@ -207,25 +237,13 @@ static int ams_delta_init(struct platform_device *pdev)
mtd = nand_to_mtd(this);
mtd->dev.parent = &pdev->dev;

- /*
- * Don't try to request the memory region from here,
- * it should have been already requested from the
- * gpio-omap driver and requesting it again would fail.
- */
- io_base = ioremap(res->start, resource_size(res));
- if (!io_base) {
- dev_err(&pdev->dev, "ioremap failed\n");
- return -EIO;
- }
-
- priv->io_base = io_base;
nand_set_controller_data(this, priv);

priv->gpiod_rdy = devm_gpiod_get_optional(&pdev->dev, "rdy", GPIOD_IN);
if (IS_ERR(priv->gpiod_rdy)) {
err = PTR_ERR(priv->gpiod_rdy);
dev_warn(&pdev->dev, "RDY GPIO request failed (%d)\n", err);
- goto err_unmap;
+ return err;
}

this->ecc.mode = NAND_ECC_SOFT;
@@ -238,42 +256,42 @@ static int ams_delta_init(struct platform_device *pdev)
if (IS_ERR(priv->gpiod_nwp)) {
err = PTR_ERR(priv->gpiod_nwp);
dev_err(&pdev->dev, "NWP GPIO request failed (%d)\n", err);
- goto err_unmap;
+ return err;
}

priv->gpiod_nce = devm_gpiod_get(&pdev->dev, "nce", GPIOD_OUT_HIGH);
if (IS_ERR(priv->gpiod_nce)) {
err = PTR_ERR(priv->gpiod_nce);
dev_err(&pdev->dev, "NCE GPIO request failed (%d)\n", err);
- goto err_unmap;
+ return err;
}

priv->gpiod_nre = devm_gpiod_get(&pdev->dev, "nre", GPIOD_OUT_HIGH);
if (IS_ERR(priv->gpiod_nre)) {
err = PTR_ERR(priv->gpiod_nre);
dev_err(&pdev->dev, "NRE GPIO request failed (%d)\n", err);
- goto err_unmap;
+ return err;
}

priv->gpiod_nwe = devm_gpiod_get(&pdev->dev, "nwe", GPIOD_OUT_HIGH);
if (IS_ERR(priv->gpiod_nwe)) {
err = PTR_ERR(priv->gpiod_nwe);
dev_err(&pdev->dev, "NWE GPIO request failed (%d)\n", err);
- goto err_unmap;
+ return err;
}

priv->gpiod_ale = devm_gpiod_get(&pdev->dev, "ale", GPIOD_OUT_LOW);
if (IS_ERR(priv->gpiod_ale)) {
err = PTR_ERR(priv->gpiod_ale);
dev_err(&pdev->dev, "ALE GPIO request failed (%d)\n", err);
- goto err_unmap;
+ return err;
}

priv->gpiod_cle = devm_gpiod_get(&pdev->dev, "cle", GPIOD_OUT_LOW);
if (IS_ERR(priv->gpiod_cle)) {
err = PTR_ERR(priv->gpiod_cle);
dev_err(&pdev->dev, "CLE GPIO request failed (%d)\n", err);
- goto err_unmap;
+ return err;
}

/* Request array of data pins, initialize them as input */
@@ -281,8 +299,9 @@ static int ams_delta_init(struct platform_device *pdev)
if (IS_ERR(data_gpiods)) {
err = PTR_ERR(data_gpiods);
dev_err(&pdev->dev, "data GPIO request failed: %d\n", err);
- goto err_unmap;
+ return err;
}
+ priv->data_gpiods = data_gpiods;
priv->data_in = true;

/* Initialize the NAND controller object embedded in ams_delta_nand. */
@@ -293,7 +312,7 @@ static int ams_delta_init(struct platform_device *pdev)
/* Scan to find existence of the device */
err = nand_scan(this, 1);
if (err)
- goto err_unmap;
+ return err;

/* Register the partitions */
err = mtd_device_register(mtd, partition_info,
@@ -306,9 +325,6 @@ static int ams_delta_init(struct platform_device *pdev)
err_nand_cleanup:
nand_cleanup(this);

-err_unmap:
- iounmap(io_base);
-
return err;
}

@@ -319,13 +335,10 @@ static int ams_delta_cleanup(struct platform_device *pdev)
{
struct ams_delta_nand *priv = platform_get_drvdata(pdev);
struct mtd_info *mtd = nand_to_mtd(&priv->nand_chip);
- void __iomem *io_base = priv->io_base;

- /* Release resources, unregister device */
+ /* Unregister device */
nand_release(mtd_to_nand(mtd));

- iounmap(io_base);
-
return 0;
}

--
2.18.1


2018-11-21 14:56:48

by Boris Brezillon

[permalink] [raw]
Subject: Re: [PATCH v4 3/4] mtd: rawnand: ams-delta: Use GPIO API for data I/O

On Wed, 21 Nov 2018 12:08:05 +0100
Janusz Krzysztofik <[email protected]> wrote:

> Don't readw()/writew() data directly from/to GPIO port which is under
> control of gpio-omap driver, use GPIO consumer API instead.
>
> The driver should now work with any 8-bit bidirectional GPIO port, not
> only OMAP.
>
> Signed-off-by: Janusz Krzysztofik <[email protected]>
> Reviewed-by: Linus Walleij <[email protected]>

Reviewed-by: Boris Brezillon <[email protected]>

And thanks a lot for keeping up with that. I like the new ams-delta
driver, and I wonder if we couldn't extend it to replace the gpio-nand
driver.

> ---
> drivers/mtd/nand/raw/ams-delta.c | 109 +++++++++++++++++--------------
> 1 file changed, 61 insertions(+), 48 deletions(-)
>
> diff --git a/drivers/mtd/nand/raw/ams-delta.c b/drivers/mtd/nand/raw/ams-delta.c
> index bb50dda05654..8312182088c1 100644
> --- a/drivers/mtd/nand/raw/ams-delta.c
> +++ b/drivers/mtd/nand/raw/ams-delta.c
> @@ -18,11 +18,10 @@
> #include <linux/module.h>
> #include <linux/delay.h>
> #include <linux/gpio/consumer.h>
> -#include <linux/io.h>
> #include <linux/mtd/mtd.h>
> #include <linux/mtd/rawnand.h>
> #include <linux/mtd/partitions.h>
> -#include <linux/platform_data/gpio-omap.h>
> +#include <linux/platform_device.h>
> #include <linux/sizes.h>
>
> /*
> @@ -38,7 +37,7 @@ struct ams_delta_nand {
> struct gpio_desc *gpiod_nwe;
> struct gpio_desc *gpiod_ale;
> struct gpio_desc *gpiod_cle;
> - void __iomem *io_base;
> + struct gpio_descs *data_gpiods;
> bool data_in;
> };
>
> @@ -67,42 +66,78 @@ static const struct mtd_partition partition_info[] = {
> .size = 3 * SZ_256K },
> };
>
> -static void ams_delta_io_write(struct ams_delta_nand *priv, u8 byte)
> +static void ams_delta_write_commit(struct ams_delta_nand *priv)
> {
> - writew(byte, priv->io_base + OMAP_MPUIO_OUTPUT);
> gpiod_set_value(priv->gpiod_nwe, 0);
> ndelay(40);
> gpiod_set_value(priv->gpiod_nwe, 1);
> }
>
> +static void ams_delta_io_write(struct ams_delta_nand *priv, u8 byte)
> +{
> + struct gpio_descs *data_gpiods = priv->data_gpiods;
> + DECLARE_BITMAP(values, BITS_PER_TYPE(byte)) = { byte, };
> +
> + gpiod_set_raw_array_value(data_gpiods->ndescs, data_gpiods->desc,
> + data_gpiods->info, values);
> +
> + ams_delta_write_commit(priv);
> +}
> +
> +static void ams_delta_dir_output(struct ams_delta_nand *priv, u8 byte)
> +{
> + struct gpio_descs *data_gpiods = priv->data_gpiods;
> + DECLARE_BITMAP(values, BITS_PER_TYPE(byte)) = { byte, };
> + int i;
> +
> + for (i = 0; i < data_gpiods->ndescs; i++)
> + gpiod_direction_output_raw(data_gpiods->desc[i],
> + test_bit(i, values));
> +
> + ams_delta_write_commit(priv);
> +
> + priv->data_in = false;
> +}
> +
> static u8 ams_delta_io_read(struct ams_delta_nand *priv)
> {
> u8 res;
> + struct gpio_descs *data_gpiods = priv->data_gpiods;
> + DECLARE_BITMAP(values, BITS_PER_TYPE(res)) = { 0, };
>
> gpiod_set_value(priv->gpiod_nre, 0);
> ndelay(40);
> - res = readw(priv->io_base + OMAP_MPUIO_INPUT_LATCH);
> +
> + gpiod_get_raw_array_value(data_gpiods->ndescs, data_gpiods->desc,
> + data_gpiods->info, values);
> +
> gpiod_set_value(priv->gpiod_nre, 1);
>
> + res = values[0];
> return res;
> }
>
> -static void ams_delta_dir_input(struct ams_delta_nand *priv, bool in)
> +static void ams_delta_dir_input(struct ams_delta_nand *priv)
> {
> - writew(in ? ~0 : 0, priv->io_base + OMAP_MPUIO_IO_CNTL);
> - priv->data_in = in;
> + struct gpio_descs *data_gpiods = priv->data_gpiods;
> + int i;
> +
> + for (i = 0; i < data_gpiods->ndescs; i++)
> + gpiod_direction_input(data_gpiods->desc[i]);
> +
> + priv->data_in = true;
> }
>
> static void ams_delta_write_buf(struct ams_delta_nand *priv, const u8 *buf,
> int len)
> {
> - int i;
> + int i = 0;
>
> - if (priv->data_in)
> - ams_delta_dir_input(priv, false);
> + if (len > 0 && priv->data_in)
> + ams_delta_dir_output(priv, buf[i++]);
>
> - for (i = 0; i < len; i++)
> - ams_delta_io_write(priv, buf[i]);
> + while (i < len)
> + ams_delta_io_write(priv, buf[i++]);
> }
>
> static void ams_delta_read_buf(struct ams_delta_nand *priv, u8 *buf, int len)
> @@ -110,7 +145,7 @@ static void ams_delta_read_buf(struct ams_delta_nand *priv, u8 *buf, int len)
> int i;
>
> if (!priv->data_in)
> - ams_delta_dir_input(priv, true);
> + ams_delta_dir_input(priv);
>
> for (i = 0; i < len; i++)
> buf[i] = ams_delta_io_read(priv);
> @@ -188,14 +223,9 @@ static int ams_delta_init(struct platform_device *pdev)
> struct ams_delta_nand *priv;
> struct nand_chip *this;
> struct mtd_info *mtd;
> - struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> - void __iomem *io_base;
> struct gpio_descs *data_gpiods;
> int err = 0;
>
> - if (!res)
> - return -ENXIO;
> -
> /* Allocate memory for MTD device structure and private data */
> priv = devm_kzalloc(&pdev->dev, sizeof(struct ams_delta_nand),
> GFP_KERNEL);
> @@ -207,25 +237,13 @@ static int ams_delta_init(struct platform_device *pdev)
> mtd = nand_to_mtd(this);
> mtd->dev.parent = &pdev->dev;
>
> - /*
> - * Don't try to request the memory region from here,
> - * it should have been already requested from the
> - * gpio-omap driver and requesting it again would fail.
> - */
> - io_base = ioremap(res->start, resource_size(res));
> - if (!io_base) {
> - dev_err(&pdev->dev, "ioremap failed\n");
> - return -EIO;
> - }
> -
> - priv->io_base = io_base;
> nand_set_controller_data(this, priv);
>
> priv->gpiod_rdy = devm_gpiod_get_optional(&pdev->dev, "rdy", GPIOD_IN);
> if (IS_ERR(priv->gpiod_rdy)) {
> err = PTR_ERR(priv->gpiod_rdy);
> dev_warn(&pdev->dev, "RDY GPIO request failed (%d)\n", err);
> - goto err_unmap;
> + return err;
> }
>
> this->ecc.mode = NAND_ECC_SOFT;
> @@ -238,42 +256,42 @@ static int ams_delta_init(struct platform_device *pdev)
> if (IS_ERR(priv->gpiod_nwp)) {
> err = PTR_ERR(priv->gpiod_nwp);
> dev_err(&pdev->dev, "NWP GPIO request failed (%d)\n", err);
> - goto err_unmap;
> + return err;
> }
>
> priv->gpiod_nce = devm_gpiod_get(&pdev->dev, "nce", GPIOD_OUT_HIGH);
> if (IS_ERR(priv->gpiod_nce)) {
> err = PTR_ERR(priv->gpiod_nce);
> dev_err(&pdev->dev, "NCE GPIO request failed (%d)\n", err);
> - goto err_unmap;
> + return err;
> }
>
> priv->gpiod_nre = devm_gpiod_get(&pdev->dev, "nre", GPIOD_OUT_HIGH);
> if (IS_ERR(priv->gpiod_nre)) {
> err = PTR_ERR(priv->gpiod_nre);
> dev_err(&pdev->dev, "NRE GPIO request failed (%d)\n", err);
> - goto err_unmap;
> + return err;
> }
>
> priv->gpiod_nwe = devm_gpiod_get(&pdev->dev, "nwe", GPIOD_OUT_HIGH);
> if (IS_ERR(priv->gpiod_nwe)) {
> err = PTR_ERR(priv->gpiod_nwe);
> dev_err(&pdev->dev, "NWE GPIO request failed (%d)\n", err);
> - goto err_unmap;
> + return err;
> }
>
> priv->gpiod_ale = devm_gpiod_get(&pdev->dev, "ale", GPIOD_OUT_LOW);
> if (IS_ERR(priv->gpiod_ale)) {
> err = PTR_ERR(priv->gpiod_ale);
> dev_err(&pdev->dev, "ALE GPIO request failed (%d)\n", err);
> - goto err_unmap;
> + return err;
> }
>
> priv->gpiod_cle = devm_gpiod_get(&pdev->dev, "cle", GPIOD_OUT_LOW);
> if (IS_ERR(priv->gpiod_cle)) {
> err = PTR_ERR(priv->gpiod_cle);
> dev_err(&pdev->dev, "CLE GPIO request failed (%d)\n", err);
> - goto err_unmap;
> + return err;
> }
>
> /* Request array of data pins, initialize them as input */
> @@ -281,8 +299,9 @@ static int ams_delta_init(struct platform_device *pdev)
> if (IS_ERR(data_gpiods)) {
> err = PTR_ERR(data_gpiods);
> dev_err(&pdev->dev, "data GPIO request failed: %d\n", err);
> - goto err_unmap;
> + return err;
> }
> + priv->data_gpiods = data_gpiods;
> priv->data_in = true;
>
> /* Initialize the NAND controller object embedded in ams_delta_nand. */
> @@ -293,7 +312,7 @@ static int ams_delta_init(struct platform_device *pdev)
> /* Scan to find existence of the device */
> err = nand_scan(this, 1);
> if (err)
> - goto err_unmap;
> + return err;
>
> /* Register the partitions */
> err = mtd_device_register(mtd, partition_info,
> @@ -306,9 +325,6 @@ static int ams_delta_init(struct platform_device *pdev)
> err_nand_cleanup:
> nand_cleanup(this);
>
> -err_unmap:
> - iounmap(io_base);
> -
> return err;
> }
>
> @@ -319,13 +335,10 @@ static int ams_delta_cleanup(struct platform_device *pdev)
> {
> struct ams_delta_nand *priv = platform_get_drvdata(pdev);
> struct mtd_info *mtd = nand_to_mtd(&priv->nand_chip);
> - void __iomem *io_base = priv->io_base;
>
> - /* Release resources, unregister device */
> + /* Unregister device */
> nand_release(mtd_to_nand(mtd));
>
> - iounmap(io_base);
> -
> return 0;
> }
>


2018-11-21 17:57:24

by Boris Brezillon

[permalink] [raw]
Subject: Re: Subject: [PATCH v4 0/4] mtd: rawnand: ams-delta: Use GPIO API for data I/O

On Wed, 21 Nov 2018 12:08:02 +0100
Janusz Krzysztofik <[email protected]> wrote:

> Finalize implementation of the idea suggested by Artem Bityutskiy and
> Tony Lindgren, described in commit b027274d2e3a ("mtd: ams-delta: fix
> request_mem_region() failure"). Use pure GPIO consumer API, as reqested
> by Boris Brezillon.
>
> Janusz Krzysztofik (4):
> ARM: OMAP1: ams-delta: Provide GPIO lookup table for NAND data port
> mtd: rawnand: ams-delta: Request data port GPIO resource
> mtd: rawnand: ams-delta: Use GPIO API for data I/O
> ARM: OMAP1: ams-delta: Drop obsolete NAND resources
>
> arch/arm/mach-omap1/board-ams-delta.c | 22 ++----
> drivers/mtd/nand/raw/ams-delta.c | 120 +++++++++++++++++++---------------
> 2 files changed, 80 insertions(+), 62 deletions(-)
>
> Performance on Amstrad Delta is now acceptable after recent extensions
> to GPIO API and rawnanad enhancements.
>
> Series intented to be merged via mtd tree, should not conflict with
> other OMAP1 patches submitted for 4.21.

We'll prepare an immutable tag, just in case.

2018-11-24 08:45:28

by Tony Lindgren

[permalink] [raw]
Subject: Re: [PATCH v4 1/4] ARM: OMAP1: ams-delta: Provide GPIO lookup table for NAND data port

* Janusz Krzysztofik <[email protected]> [181121 11:06]:
> Data port used by Amstrad Delta NAND driver is actually an OMAP MPUIO
> device, already under control of gpio-omap driver. The NAND driver
> gets access to the port by ioremapping it and performs read/write
> operations. That is done without any proteciton from other users
> legally manipulating the port pins over GPIO API.
>
> The plan is to convert the driver to access the port over GPIO consumer
> API. Before that is implemented, the driver can already obtain
> exclusive access to the port by requesting an array of its GPIO
> descriptors.
>
> Add respective entries to the NAND GPIO lookup table.
>
> Signed-off-by: Janusz Krzysztofik <[email protected]>
> Reviewed-by: Boris Brezillon <[email protected]>
> Reviewed-by: Linus Walleij <[email protected]>

Best to merge this along with the MFD patches:

Acked-by: Tony Lindgren <[email protected]>

2018-11-24 08:45:31

by Tony Lindgren

[permalink] [raw]
Subject: Re: Subject: [PATCH v4 0/4] mtd: rawnand: ams-delta: Use GPIO API for data I/O

* Boris Brezillon <[email protected]> [181121 14:56]:
> On Wed, 21 Nov 2018 12:08:02 +0100
> Janusz Krzysztofik <[email protected]> wrote:
>
> > Finalize implementation of the idea suggested by Artem Bityutskiy and
> > Tony Lindgren, described in commit b027274d2e3a ("mtd: ams-delta: fix
> > request_mem_region() failure"). Use pure GPIO consumer API, as reqested
> > by Boris Brezillon.
> >
> > Janusz Krzysztofik (4):
> > ARM: OMAP1: ams-delta: Provide GPIO lookup table for NAND data port
> > mtd: rawnand: ams-delta: Request data port GPIO resource
> > mtd: rawnand: ams-delta: Use GPIO API for data I/O
> > ARM: OMAP1: ams-delta: Drop obsolete NAND resources
> >
> > arch/arm/mach-omap1/board-ams-delta.c | 22 ++----
> > drivers/mtd/nand/raw/ams-delta.c | 120 +++++++++++++++++++---------------
> > 2 files changed, 80 insertions(+), 62 deletions(-)
> >
> > Performance on Amstrad Delta is now acceptable after recent extensions
> > to GPIO API and rawnanad enhancements.
> >
> > Series intented to be merged via mtd tree, should not conflict with
> > other OMAP1 patches submitted for 4.21.
>
> We'll prepare an immutable tag, just in case.

Sounds good to me thanks:

Acked-by: Tony Lindgren <[email protected]>

2018-12-07 08:05:32

by Miquel Raynal

[permalink] [raw]
Subject: Re: Subject: [PATCH v4 0/4] mtd: rawnand: ams-delta: Use GPIO API for data I/O

Hi Tony,

Tony Lindgren <[email protected]> wrote on Fri, 23 Nov 2018 09:03:33
-0800:

> * Boris Brezillon <[email protected]> [181121 14:56]:
> > On Wed, 21 Nov 2018 12:08:02 +0100
> > Janusz Krzysztofik <[email protected]> wrote:
> >
> > > Finalize implementation of the idea suggested by Artem Bityutskiy and
> > > Tony Lindgren, described in commit b027274d2e3a ("mtd: ams-delta: fix
> > > request_mem_region() failure"). Use pure GPIO consumer API, as reqested
> > > by Boris Brezillon.
> > >
> > > Janusz Krzysztofik (4):
> > > ARM: OMAP1: ams-delta: Provide GPIO lookup table for NAND data port
> > > mtd: rawnand: ams-delta: Request data port GPIO resource
> > > mtd: rawnand: ams-delta: Use GPIO API for data I/O
> > > ARM: OMAP1: ams-delta: Drop obsolete NAND resources
> > >
> > > arch/arm/mach-omap1/board-ams-delta.c | 22 ++----
> > > drivers/mtd/nand/raw/ams-delta.c | 120 +++++++++++++++++++---------------
> > > 2 files changed, 80 insertions(+), 62 deletions(-)
> > >
> > > Performance on Amstrad Delta is now acceptable after recent extensions
> > > to GPIO API and rawnanad enhancements.
> > >
> > > Series intented to be merged via mtd tree, should not conflict with
> > > other OMAP1 patches submitted for 4.21.
> >
> > We'll prepare an immutable tag, just in case.
>
> Sounds good to me thanks:
>
> Acked-by: Tony Lindgren <[email protected]>

Actually, I can't setup a topic branch with this series because it
depends on other changes in nand/next. If a conflict happens in -next,
I suppose we will have to send you a PR.


Thanks,
Miquèl

2018-12-07 16:12:12

by Tony Lindgren

[permalink] [raw]
Subject: Re: Subject: [PATCH v4 0/4] mtd: rawnand: ams-delta: Use GPIO API for data I/O

* Miquel Raynal <[email protected]> [181207 08:04]:
> Hi Tony,
>
> Tony Lindgren <[email protected]> wrote on Fri, 23 Nov 2018 09:03:33
> -0800:
>
> > * Boris Brezillon <[email protected]> [181121 14:56]:
> > > On Wed, 21 Nov 2018 12:08:02 +0100
> > > Janusz Krzysztofik <[email protected]> wrote:
> > >
> > > > Finalize implementation of the idea suggested by Artem Bityutskiy and
> > > > Tony Lindgren, described in commit b027274d2e3a ("mtd: ams-delta: fix
> > > > request_mem_region() failure"). Use pure GPIO consumer API, as reqested
> > > > by Boris Brezillon.
> > > >
> > > > Janusz Krzysztofik (4):
> > > > ARM: OMAP1: ams-delta: Provide GPIO lookup table for NAND data port
> > > > mtd: rawnand: ams-delta: Request data port GPIO resource
> > > > mtd: rawnand: ams-delta: Use GPIO API for data I/O
> > > > ARM: OMAP1: ams-delta: Drop obsolete NAND resources
> > > >
> > > > arch/arm/mach-omap1/board-ams-delta.c | 22 ++----
> > > > drivers/mtd/nand/raw/ams-delta.c | 120 +++++++++++++++++++---------------
> > > > 2 files changed, 80 insertions(+), 62 deletions(-)
> > > >
> > > > Performance on Amstrad Delta is now acceptable after recent extensions
> > > > to GPIO API and rawnanad enhancements.
> > > >
> > > > Series intented to be merged via mtd tree, should not conflict with
> > > > other OMAP1 patches submitted for 4.21.
> > >
> > > We'll prepare an immutable tag, just in case.
> >
> > Sounds good to me thanks:
> >
> > Acked-by: Tony Lindgren <[email protected]>
>
> Actually, I can't setup a topic branch with this series because it
> depends on other changes in nand/next. If a conflict happens in -next,
> I suppose we will have to send you a PR.

Hmm OK I have pretty much all the stuff applied and in next
already so if it's not conflicting now it should not conflict.

Regards,

Tony