rb91x-ngl (nand-gpio-latch) requests and controls SoC GPIO
lines that are used for NAND control and data lines multiplexed
with a latch. Lines of the latch that are not used for NAND
control lines, are used for power LED and user LED and a Shift
Register nCS.

Like rb4xx-cpld driver rb91x-ngl provides API for separate
NAND driver and latch-GPIO driver.

This driver is used in place of the ar71xx gpio-latch driver.

Signed-off-by: Denis Kalashnikov <[email protected]>
---
 .../linux/ath79/files/drivers/mfd/rb91x-ngl.c | 331 ++++++++++++++++++
 .../linux/ath79/files/include/mfd/rb91x-ngl.h |  59 ++++
 target/linux/ath79/mikrotik/config-default    |   1 +
 .../patches-5.4/939-mikrotik-rb91x.patch      |  21 ++
 4 files changed, 412 insertions(+)
 create mode 100644 target/linux/ath79/files/drivers/mfd/rb91x-ngl.c
 create mode 100644 target/linux/ath79/files/include/mfd/rb91x-ngl.h
 create mode 100644 target/linux/ath79/patches-5.4/939-mikrotik-rb91x.patch

diff --git a/target/linux/ath79/files/drivers/mfd/rb91x-ngl.c 
b/target/linux/ath79/files/drivers/mfd/rb91x-ngl.c
new file mode 100644
index 0000000000..c6ab4631f5
--- /dev/null
+++ b/target/linux/ath79/files/drivers/mfd/rb91x-ngl.c
@@ -0,0 +1,331 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * MFD driver for the MikroTik RouterBoard NAND controlled through GPIO
+ * multiplexed with latch. Why MFD, not pure NAND driver? Since the latch
+ * lines, that are not used for NAND control lines, are used for GPIO
+ * output function -- for leds and other.
+ *
+ * Copyright (C) 2021 Denis Kalashnikov <[email protected]>
+ *
+ */
+#include <linux/mutex.h>
+#include <linux/mfd/core.h>
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/of_gpio.h>
+
+#include <mfd/rb91x-ngl.h>
+
+#define DRIVER_NAME "rb91x-nand-gpio-latch"
+
+#define NAND_DATAS 8
+#define LATCH_GPIOS 3
+
+static int nand_datas_count(struct rb91x_ngl *ngl)
+{
+       return NAND_DATAS;
+}
+
+static int latch_gpios_count(struct rb91x_ngl *ngl)
+{
+       return LATCH_GPIOS;
+}
+
+static void latch_lock(struct rb91x_ngl *ngl)
+{
+       mutex_lock(&ngl->mutex);
+
+       gpio_set_value_cansleep(ngl->gpio[RB91X_NGL_NLE], 0);
+}
+
+static void latch_unlock(struct rb91x_ngl *ngl)
+{
+       gpio_set_value_cansleep(ngl->gpio[RB91X_NGL_NLE], 1);
+
+       mutex_unlock(&ngl->mutex);
+}
+
+#define OFFSET_INVAL(offset) ((offset) < 0 || (offset) >= RB91X_NGL_GPIOS)
+
+static void rb91x_ngl_gpio_set_value(struct rb91x_ngl *ngl, int offset, int 
val)
+{
+       if (OFFSET_INVAL(offset))
+               return;
+
+       gpio_set_value_cansleep(ngl->gpio[offset], val);
+}
+
+static void latch_gpio_set_value(struct rb91x_ngl *ngl, int offset, int val)
+{
+       if (offset >= LATCH_GPIOS)
+               return;
+
+       mutex_lock(&ngl->mutex);
+
+       gpio_set_value_cansleep(ngl->gpio[RB91X_NGL_LATCH_GPIO0 + offset], val);
+
+       mutex_unlock(&ngl->mutex);
+}
+
+static int rb91x_ngl_gpio_get_value(struct rb91x_ngl *ngl, int offset)
+{
+       if (OFFSET_INVAL(offset))
+               return -EINVAL;
+
+       return gpio_get_value(ngl->gpio[offset]);
+}
+
+static void rb91x_ngl_gpio_direction_output(struct rb91x_ngl *ngl, int offset,
+                                      int val)
+{
+       if (OFFSET_INVAL(offset))
+               return;
+
+       gpio_direction_output(ngl->gpio[offset], val);
+}
+
+static void rb91x_ngl_gpio_direction_input(struct rb91x_ngl *ngl, int offset)
+{
+       if (OFFSET_INVAL(offset))
+               return;
+
+       gpio_direction_input(ngl->gpio[offset]);
+}
+
+static const struct mfd_cell mfd_cells[] = {
+       {
+               .name = "mikrotik,rb91x-nand",
+               .of_compatible = "mikrotik,rb91x-nand",
+       }, {
+               .name = "mikrotik,rb91x-gpio-latch",
+               .of_compatible = "mikrotik,rb91x-gpio-latch",
+       },
+};
+
+static int get_gpios(struct device *dev, const char *prop_name)
+{
+       int n;
+
+       n = of_get_named_gpio(dev->of_node, prop_name, 0);
+       if (n < 0)
+               dev_err(dev, "Could not read required '%s' property: %d\n", 
prop_name, n);
+       //pr_info(DRIVER_NAME ": %s = %d\n", prop_name, n);
+
+       return n;
+}
+
+/*
+ * NOTE: all gpios are labeled with driver name, not with @name.
+ * @name is used only in error message. Since we failed to choose
+ * a good names for multiplexed gpios.
+ */
+static int req_gpio(struct device *dev, int gpio, const char *name)
+{
+       int ret;
+
+       ret = devm_gpio_request(dev, gpio, DRIVER_NAME);
+       if (ret) {
+               pr_err(DRIVER_NAME ": failed to request gpio %d ('%s'): %d\n",
+                       gpio, name, ret);
+               return ret;
+       }
+
+       //pr_info(DRIVER_NAME ": request gpio %d ('%s')\n", gpio, name);
+
+       return ret;
+}
+
+static int probe(struct platform_device *pdev)
+{
+       struct device_node *of_node = pdev->dev.of_node;
+       struct device *dev = &pdev->dev;
+       struct rb91x_ngl *ngl;
+       int i, n, ret;
+
+       pr_info("rb91x-nand-gpio-latch driver probe\n");
+
+       ngl = devm_kzalloc(dev, sizeof(*ngl), GFP_KERNEL);
+       if (!ngl)
+               return -ENOMEM;
+
+       /* TODO: read gpios flags (active high/low) */
+
+       for (i = 0; i < RB91X_NGL_GPIOS; i++) {
+               ngl->gpio[i] = -ENOENT;
+       }
+
+       /* Read NAND control gpios */
+       ngl->gpio[RB91X_NGL_NAND_NCE] = get_gpios(dev, "nand-nce-gpios");
+       ngl->gpio[RB91X_NGL_NAND_CLE] = get_gpios(dev, "nand-cle-gpios");
+       ngl->gpio[RB91X_NGL_NAND_ALE] = get_gpios(dev, "nand-ale-gpios");
+       ngl->gpio[RB91X_NGL_NAND_NRW] = get_gpios(dev, "nand-nrw-gpios");
+       ngl->gpio[RB91X_NGL_NAND_RDY] = get_gpios(dev, "nand-rdy-gpios");
+       ngl->gpio[RB91X_NGL_NAND_READ] = get_gpios(dev, "nand-read-gpios");
+
+       ngl->gpio[RB91X_NGL_NLE] = get_gpios(dev, "nle-gpios");
+
+       /* Read NAND data gpios */
+
+       n = of_gpio_named_count(of_node, "nand-data-gpios");
+       if (n != NAND_DATAS) {
+               dev_err(dev, DRIVER_NAME
+                 ": required 'nand-data-gpios' property must have %d gpios\n",
+                 NAND_DATAS);
+               return -EINVAL;
+       }
+
+       //dev_info(dev, DRIVER_NAME ": nand-data-gpios count = %d\n", n);
+
+       for (i = 0; i < n; i++) {
+               ret = of_get_named_gpio(of_node, "nand-data-gpios", i);
+               if (ret < 0) {
+                       dev_err(dev, DRIVER_NAME
+                         ": Couldn't read required 'nand-data-gpios': %d\n",
+                         ret);
+                       return -EINVAL;
+               }
+
+               //dev_info(dev, DRIVER_NAME ": nand-data-gpios = %d\n", ret);
+
+               ngl->gpio[RB91X_NGL_NAND_DATA0 + i] = ret;
+       }
+
+       /* Read latch gpios */
+
+       n = of_gpio_named_count(of_node, "latch-gpios");
+       if (n != LATCH_GPIOS) {
+               dev_err(dev, DRIVER_NAME
+                 ": required 'latch-gpios' property must have %d gpios\n",
+                 LATCH_GPIOS);
+               return -EINVAL;
+       }
+
+       //dev_info(dev, DRIVER_NAME ": latch-gpios count = %d\n", n);
+
+       for (i = 0; i < n; i++) {
+               ret = of_get_named_gpio(of_node, "latch-gpios", i);
+               if (ret < 0) {
+                       dev_err(dev, DRIVER_NAME
+                         ": Couldn't read required 'latch-gpios': %d\n",
+                         ret);
+                       return -EINVAL;
+               }
+
+               //dev_info(dev, DRIVER_NAME ": latch-gpios = %d\n", ret);
+
+               ngl->gpio[RB91X_NGL_LATCH_GPIO0 + i] = ret;
+       }
+
+       if (ngl->gpio[RB91X_NGL_NAND_NCE] < 0
+        || ngl->gpio[RB91X_NGL_NAND_CLE] < 0
+        || ngl->gpio[RB91X_NGL_NAND_ALE] < 0
+        || ngl->gpio[RB91X_NGL_NAND_NRW] < 0
+        || ngl->gpio[RB91X_NGL_NAND_RDY] < 0
+        || ngl->gpio[RB91X_NGL_NAND_READ] < 0
+        || ngl->gpio[RB91X_NGL_NLE] < 0)
+           return -EINVAL;
+
+       /* Request gpios */
+
+       if (req_gpio(dev, ngl->gpio[RB91X_NGL_NLE], "nLE"))
+               return -EINVAL;
+
+       if (req_gpio(dev, ngl->gpio[RB91X_NGL_NAND_NCE], "NAND-nCE"))
+               return -EINVAL;
+
+       if (req_gpio(dev, ngl->gpio[RB91X_NGL_NAND_CLE], "NAND-CLE"))
+               return -EINVAL;
+
+       if (req_gpio(dev, ngl->gpio[RB91X_NGL_NAND_ALE], "NAND-ALE"))
+               return -EINVAL;
+
+       if (req_gpio(dev, ngl->gpio[RB91X_NGL_NAND_NRW], "NAND-nRW"))
+               return -EINVAL;
+
+       if (req_gpio(dev, ngl->gpio[RB91X_NGL_NAND_RDY], "NAND-RDY"))
+               return -EINVAL;
+
+       if (req_gpio(dev, ngl->gpio[RB91X_NGL_NAND_READ], "NAND-READ"))
+               return -EINVAL;
+
+       for (i = 0; i < NAND_DATAS; i++) {
+               /*
+                * Some data gpios are equal to control gpios.
+                * Check this.
+                */
+               n = ngl->gpio[RB91X_NGL_NAND_DATA0 + i];
+               if (n == ngl->gpio[RB91X_NGL_NAND_NCE]
+                || n == ngl->gpio[RB91X_NGL_NAND_CLE]
+                || n == ngl->gpio[RB91X_NGL_NAND_ALE]
+                || n == ngl->gpio[RB91X_NGL_NAND_NRW]
+                || n == ngl->gpio[RB91X_NGL_NAND_RDY]
+                || n == ngl->gpio[RB91X_NGL_NAND_READ])
+                       continue;
+               if (req_gpio(dev, n, "NAND-DATAx"))
+                       return -EINVAL;
+       }
+
+       /*
+        * NOTE: We suppose that latch gpios are equal to some
+        * control gpios, so they have been already requested.
+        */
+
+       ngl->nand_datas_count = nand_datas_count;
+       ngl->latch_lock = latch_lock;
+       ngl->latch_unlock = latch_unlock;
+       ngl->gpio_set_value = rb91x_ngl_gpio_set_value;
+       ngl->gpio_get_value = rb91x_ngl_gpio_get_value;
+       ngl->gpio_direction_input = rb91x_ngl_gpio_direction_input;
+       ngl->gpio_direction_output = rb91x_ngl_gpio_direction_output;
+       ngl->latch_gpio_set_value = latch_gpio_set_value;
+       ngl->latch_gpios_count = latch_gpios_count;
+
+       mutex_init(&ngl->mutex);
+
+       dev_set_drvdata(dev, ngl);
+
+       /*
+        * All gpios and the latch are controlled by NAND driver,
+        * but we need to init gpio lines for the latch gpio in case
+        * of NAND driver is missing.
+        */
+
+       /* Unlock the latch */
+       gpio_direction_output(ngl->gpio[RB91X_NGL_NLE], 1);
+
+       /* TODO: are latch gpio lines active high or low? */
+       for (i = 0; i < LATCH_GPIOS; i++) {
+               gpio_direction_output(ngl->gpio[RB91X_NGL_LATCH_GPIO0 + i], 0);
+       }
+
+       return devm_mfd_add_devices(dev, PLATFORM_DEVID_NONE,
+                                   mfd_cells, ARRAY_SIZE(mfd_cells),
+                                   NULL, 0, NULL);
+}
+
+static int remove(struct platform_device *pdev)
+{
+       return 0;
+}
+
+static const struct of_device_id match[] = {
+       { .compatible = "mikrotik,nand-gpio-latch", },
+       { },
+};
+MODULE_DEVICE_TABLE(of, match);
+
+static struct platform_driver rb91x_ngl_driver = {
+       .probe = probe,
+       .remove = remove,
+       .driver = {
+               .name = DRIVER_NAME,
+               .owner = THIS_MODULE,
+               .of_match_table = of_match_ptr(match),
+       },
+};
+
+module_platform_driver(rb91x_ngl_driver);
+
+MODULE_DESCRIPTION("Driver for Mikrotik RouterBoard 91x NAND controlled 
through GPIOs multiplexed with latch");
+MODULE_AUTHOR("Denis Kalashnikov <[email protected]>");
+MODULE_LICENSE("GPL v2");
diff --git a/target/linux/ath79/files/include/mfd/rb91x-ngl.h 
b/target/linux/ath79/files/include/mfd/rb91x-ngl.h
new file mode 100644
index 0000000000..5360aa7548
--- /dev/null
+++ b/target/linux/ath79/files/include/mfd/rb91x-ngl.h
@@ -0,0 +1,59 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * MFD driver for the MikroTik RouterBoard 91xG NAND controlled
+ * through GPIOs multiplexed with latch (rb91x-nand-gpio-latch).
+ *
+ * Copyright (C) 2021 Denis Kalashnikov <[email protected]>
+ */
+
+#include <linux/mutex.h>
+
+enum rb91x_ngl_gpios {
+       /* NAND control gpios */
+       RB91X_NGL_NAND_NCE, /* nCE -- Chip Enable, Active Low */
+       RB91X_NGL_NAND_CLE, /* CLE -- Command Latch Enable */
+       RB91X_NGL_NAND_ALE, /* ALE -- Address Latch Enable */
+       RB91X_NGL_NAND_NRW, /* nRW -- Read/Write Enable, Active Low */
+       RB91X_NGL_NAND_RDY, /* RDY -- NAND Ready */
+       RB91X_NGL_NAND_READ, /* READ */
+
+       RB91X_NGL_NLE, /* nLE -- Latch Enable, Active Low */
+
+       /* NAND data gpios */
+       RB91X_NGL_NAND_DATA0,
+
+       /* Latch gpios */
+       RB91X_NGL_LATCH_GPIO0 = RB91X_NGL_NAND_DATA0 + 32,
+
+       RB91X_NGL_GPIOS = RB91X_NGL_LATCH_GPIO0 + 32, /* Total number of gpios 
*/
+};
+
+struct rb91x_ngl {
+       /* Public */
+
+       /* API for RB91x NAND controller driver */
+       void (*gpio_set_value)(struct rb91x_ngl *ngl, int offset, int val);
+       void (*gpio_direction_input)(struct rb91x_ngl *ngl, int offset);
+       void (*gpio_direction_output)(struct rb91x_ngl *ngl, int offset,
+         int val);
+       int (*gpio_get_value)(struct rb91x_ngl *ngl, int offset);
+       void (*latch_lock)(struct rb91x_ngl *ngl);
+       void (*latch_unlock)(struct rb91x_ngl *ngl);
+       int (*nand_datas_count)(struct rb91x_ngl *ngl);
+
+       /* API for RB91x gpio latch controller driver */
+       void (*latch_gpio_set_value)(struct rb91x_ngl *ngl, int offset,
+         int val);
+       int (*latch_gpios_count)(struct rb91x_ngl *ngl);
+
+
+       /* Private */
+
+       /*
+        * To synchronize access of NAND driver and GPIO driver
+        * to shared gpio lines.
+        */
+       struct mutex mutex;
+
+       int gpio[RB91X_NGL_GPIOS];
+};
diff --git a/target/linux/ath79/mikrotik/config-default 
b/target/linux/ath79/mikrotik/config-default
index 1e637bdfd3..67c980a491 100644
--- a/target/linux/ath79/mikrotik/config-default
+++ b/target/linux/ath79/mikrotik/config-default
@@ -8,6 +8,7 @@ CONFIG_LEDS_RESET=y
 CONFIG_LZO_DECOMPRESS=y
 CONFIG_MDIO_GPIO=y
 CONFIG_MFD_RB4XX_CPLD=y
+CONFIG_MFD_RB91X_NGL=y
 CONFIG_MIKROTIK=y
 CONFIG_MIKROTIK_RB_SYSFS=y
 CONFIG_MTD_NAND=y
diff --git a/target/linux/ath79/patches-5.4/939-mikrotik-rb91x.patch 
b/target/linux/ath79/patches-5.4/939-mikrotik-rb91x.patch
new file mode 100644
index 0000000000..a85db0892c
--- /dev/null
+++ b/target/linux/ath79/patches-5.4/939-mikrotik-rb91x.patch
@@ -0,0 +1,21 @@
+--- a/drivers/mfd/Kconfig
++++ b/drivers/mfd/Kconfig
+@@ -2020,5 +2020,10 @@ config MFD_RB4XX_CPLD
+         Enables support for the CPLD chip (NAND & GPIO) on Mikrotik
+         Routerboard RB4xx series.
+ 
++config MFD_RB91X_NGL
++      tristate "Mikrotik RB91x NAND and GPIO driver
++      select MFD_CORE
++      depends on ATH79 || COMPILE_TEST
++
+ endmenu
+ endif
+--- a/drivers/mfd/Makefile
++++ b/drivers/mfd/Makefile
+@@ -257,3 +257,5 @@ obj-$(CONFIG_MFD_ROHM_BD718XX)     += rohm-b
+ obj-$(CONFIG_MFD_STMFX)       += stmfx.o
+ 
+ obj-$(CONFIG_MFD_RB4XX_CPLD)  += rb4xx-cpld.o
++
++obj-$(CONFIG_MFD_RB91X_NGL)   += rb91x-ngl.o
-- 
2.26.3


_______________________________________________
openwrt-devel mailing list
[email protected]
https://lists.openwrt.org/mailman/listinfo/openwrt-devel

Reply via email to