[PATCH 1/4] ath79: add MFD driver (NAND and GPIO) for Mikrotik RB91xG

Adrian Schmutzler mail at adrianschmutzler.de
Sat May 8 18:36:22 BST 2021


Hi,

> -----Original Message-----
> From: openwrt-devel [mailto:openwrt-devel-bounces at lists.openwrt.org]
> On Behalf Of Denis Kalashnikov
> Sent: Donnerstag, 6. Mai 2021 18:25
> To: openwrt-devel at lists.openwrt.org
> Cc: Gabor Juhos <juhosg at openwrt.org>
> Subject: [PATCH 1/4] ath79: add MFD driver (NAND and GPIO) for Mikrotik
> RB91xG
> 
> 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 <denis281089 at gmail.com>
> ---
>  .../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

Please also take care of 5.10.

Best

Adrian

> 
> 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 <denis281089 at gmail.com>
> + *
> + */
> +#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 <denis281089 at gmail.com>"); 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 <denis281089 at gmail.com>  */
> +
> +#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
> openwrt-devel at lists.openwrt.org
> https://lists.openwrt.org/mailman/listinfo/openwrt-devel
-------------- next part --------------
A non-text attachment was scrubbed...
Name: openpgp-digital-signature.asc
Type: application/pgp-signature
Size: 834 bytes
Desc: not available
URL: <http://lists.openwrt.org/pipermail/openwrt-devel/attachments/20210508/1064b591/attachment.sig>


More information about the openwrt-devel mailing list