diff mbox series

[v2,1/2] ath79: add support for reset key on MikroTik RB912UAG-2HPnD

Message ID 20211116160644.2047-2-denis281089@gmail.com
State Superseded
Delegated to: Koen Vandeputte
Headers show
Series ath79: add a support for reset key on MikroTik RB912 | expand

Commit Message

Denis K Nov. 16, 2021, 4:06 p.m. UTC
On MikroTik RB91x board series a reset key shares SoC gpio
line #15 with NAND ALE and NAND IO7. So we need a custom
gpio driver to manage this non-trivial connection schema.
Also rb91x-nand needs to have ability to disable a polling
of the key while it works with NAND, and we need to add
"cansleep" suffix to several gpiolib calls in gpio-latch.

Signed-off-by: Denis Kalashnikov <denis281089@gmail.com>
---

Changes in v2:
* Fix identations of switch statements.

 target/linux/ath79/config-5.10                |   1 +
 target/linux/ath79/config-5.4                 |   1 +
 ...9342_mikrotik_routerboard-912uag-2hpnd.dts |  23 +-
 .../ath79/files/drivers/gpio/gpio-latch.c     |   4 +-
 .../ath79/files/drivers/gpio/gpio-rb91x-key.c | 216 ++++++++++++++++++
 .../files/drivers/mtd/nand/raw/rb91x_nand.c   |  15 +-
 target/linux/ath79/mikrotik/config-default    |   1 +
 .../patches-5.10/939-mikrotik-rb91x.patch     |  19 ++
 .../patches-5.4/939-mikrotik-rb91x.patch      |  19 ++
 9 files changed, 293 insertions(+), 6 deletions(-)
 create mode 100644 target/linux/ath79/files/drivers/gpio/gpio-rb91x-key.c

Comments

Sergey Ryazanov Nov. 16, 2021, 11:42 p.m. UTC | #1
On Tue, Nov 16, 2021 at 7:07 PM Denis Kalashnikov <denis281089@gmail.com> wrote:
> On MikroTik RB91x board series a reset key shares SoC gpio
> line #15 with NAND ALE and NAND IO7. So we need a custom
> gpio driver to manage this non-trivial connection schema.
> Also rb91x-nand needs to have ability to disable a polling
> of the key while it works with NAND, and we need to add
> "cansleep" suffix to several gpiolib calls in gpio-latch.
>
> Signed-off-by: Denis Kalashnikov <denis281089@gmail.com>

Please find below one nonsignificant nitpick, in all other respects:

Reviewed-by: Sergey Ryazanov <ryazanov.s.a@gmail.com>

> +struct gpio_rb91x_key {
> +       struct gpio_chip gc;
> +       struct mutex mutex;
> +       struct mutex poll_mutex;

These mutexes are worth a line of documentation. I do not care too
much, but if by some other reason you will submit v3, please consider
to describe their purpose.

> +       int polling_disabled;
> +       struct gpio_desc *gpio;
> +};
Denis K Dec. 10, 2021, 3:14 p.m. UTC | #2
Mikrotik RB912UAG needs to be better supported in ath79, imho
and I'm working on it. Reset key, Gigabit Ethernet, mPCIe slot, UART
-- all need to be fixed. This patch adds support for reset key, next
one I'm working on will fix mPCIe slot. No ideas about how to fix
Gigabit, sigh.

Guys, please tell me what should I change in this patch to make
it useful for upstream?

Regards, Denis Kalashnikov
Koen Vandeputte Dec. 10, 2021, 3:29 p.m. UTC | #3
On 10.12.21 16:14, Denis K wrote:
> Mikrotik RB912UAG needs to be better supported in ath79, imho
> and I'm working on it. Reset key, Gigabit Ethernet, mPCIe slot, UART
> -- all need to be fixed. This patch adds support for reset key, next
> one I'm working on will fix mPCIe slot. No ideas about how to fix
> Gigabit, sigh.
>
> Guys, please tell me what should I change in this patch to make
> it useful for upstream?
>
> Regards, Denis Kalashnikov


I missed these as I'm doing lots of stuff outside of OpenWRT the past 
few months.
Added to my staging tree.

Thanks,

Koen
Denis K Dec. 10, 2021, 3:41 p.m. UTC | #4
> Added to my staging tree.
Thank you, Koen! It is very good news for me this Friday night.

Regards, Denis
Thomas Hühn Dec. 10, 2021, 10:04 p.m. UTC | #5
Hi all,

We have 5 Mikrotik 912UAG in our Freifunk Network and just build and updated them this week from ar71xx 2020 to latest tunk ath79...so far so cool.. all wifi routers mesh .. thx for the upstream work!

The first thing that was (is) not working: we can not get your 2nd wifi card in the mPCIe port up and running.. Denis provided a test patch to enable pcie early in the dts file .. works now… thx a mill!

Second issue:

5/5 of our productive Freifunk mesh  rb912uag  nodes are able to provide 1G Ethernet with the old ar71xx but not with current ath79 trunk … force a Ethernet speed downgrade to just 100M (ethtool -s eth0 advertise 0x008) works for 4/5 … 1 node does not even work with this… there is a 30m CAT5 ethernet cable between the  rb912uag mesh router and the first openwrt xiaomi 4A AP (Gigabit version) .. working properly under ar71xx with 1G … with ath79 trunk 1G is not working and 100M is not working either… my guess is that the ethernet power for tx or rx or both is in ath79 different compared to ar71xx … so I would expect to find different register settings on ar71xx and ath79 for the ar8003 chip.. what do you think ? .. devmem2 read the register space from ar71xx und ath79 and compare ? 

Greetings Thomas

> On 10. Dec 2021, at 16:41, Denis K <denis281089@gmail.com> wrote:
> 
>> Added to my staging tree.
> Thank you, Koen! It is very good news for me this Friday night.
> 
> Regards, Denis
> 
> _______________________________________________
> openwrt-devel mailing list
> openwrt-devel@lists.openwrt.org
> https://lists.openwrt.org/mailman/listinfo/openwrt-devel
Koen Vandeputte Jan. 11, 2022, 9:59 a.m. UTC | #6
On 10.12.21 16:14, Denis K wrote:
> Mikrotik RB912UAG needs to be better supported in ath79, imho
> and I'm working on it. Reset key, Gigabit Ethernet, mPCIe slot, UART
> -- all need to be fixed. This patch adds support for reset key, next
> one I'm working on will fix mPCIe slot. No ideas about how to fix
> Gigabit, sigh.
>
> Guys, please tell me what should I change in this patch to make
> it useful for upstream?
>
> Regards, Denis Kalashnikov


I'll check gigabit and pcie today.

I noticed yesterday on the latest master state that the board crashes 
with an oops (pcie driver) when a mini pcie wifi card is inserted.
This wasn't the case in the past.

I also gathered all board revisions here (on some of them, gigabit 
actually works) to check for a delta.


Checking ..

Koen
Koen Vandeputte Jan. 12, 2022, 3:55 p.m. UTC | #7
On 17.11.21 00:42, Sergey Ryazanov wrote:
> On Tue, Nov 16, 2021 at 7:07 PM Denis Kalashnikov <denis281089@gmail.com> wrote:
>> On MikroTik RB91x board series a reset key shares SoC gpio
>> line #15 with NAND ALE and NAND IO7. So we need a custom
>> gpio driver to manage this non-trivial connection schema.
>> Also rb91x-nand needs to have ability to disable a polling
>> of the key while it works with NAND, and we need to add
>> "cansleep" suffix to several gpiolib calls in gpio-latch.
>>
>> Signed-off-by: Denis Kalashnikov <denis281089@gmail.com>
> Please find below one nonsignificant nitpick, in all other respects:
>
> Reviewed-by: Sergey Ryazanov <ryazanov.s.a@gmail.com>
>
>> +struct gpio_rb91x_key {
>> +       struct gpio_chip gc;
>> +       struct mutex mutex;
>> +       struct mutex poll_mutex;
> These mutexes are worth a line of documentation. I do not care too
> much, but if by some other reason you will submit v3, please consider
> to describe their purpose.
>
>> +       int polling_disabled;
>> +       struct gpio_desc *gpio;
>> +};


I'm seeing this in the bootlogs when using this patch:

[    5.183305] gpio-latch gpio_latch: failed to get gpio 7: -517
[    5.235889] rb91x-nand nand_gpio: failed to get gpios: -517


Thanks,

Koen
Koen Vandeputte Jan. 12, 2022, 3:57 p.m. UTC | #8
On 10.12.21 23:04, Thomas Hühn wrote:
> Hi all,
>
> We have 5 Mikrotik 912UAG in our Freifunk Network and just build and updated them this week from ar71xx 2020 to latest tunk ath79...so far so cool.. all wifi routers mesh .. thx for the upstream work!
>
> The first thing that was (is) not working: we can not get your 2nd wifi card in the mPCIe port up and running.. Denis provided a test patch to enable pcie early in the dts file .. works now… thx a mill!
>
> Second issue:
>
> 5/5 of our productive Freifunk mesh  rb912uag  nodes are able to provide 1G Ethernet with the old ar71xx but not with current ath79 trunk … force a Ethernet speed downgrade to just 100M (ethtool -s eth0 advertise 0x008) works for 4/5 … 1 node does not even work with this… there is a 30m CAT5 ethernet cable between the  rb912uag mesh router and the first openwrt xiaomi 4A AP (Gigabit version) .. working properly under ar71xx with 1G … with ath79 trunk 1G is not working and 100M is not working either… my guess is that the ethernet power for tx or rx or both is in ath79 different compared to ar71xx … so I would expect to find different register settings on ar71xx and ath79 for the ar8003 chip.. what do you think ? .. devmem2 read the register space from ar71xx und ath79 and compare ?
>
> Greetings Thomas
>
Regarding ethernet --> Should be fixed

https://git.openwrt.org/?p=openwrt/staging/xback.git;a=commit;h=f25bbb361f33dc92c70f601eb335d67b64f15f54


Please give my staging tree a try for that.

Thanks,

Koen
Sergey Ryazanov Jan. 12, 2022, 8:43 p.m. UTC | #9
On Wed, Jan 12, 2022 at 6:55 PM Koen Vandeputte
<koen.vandeputte@citymesh.com> wrote:
> On 17.11.21 00:42, Sergey Ryazanov wrote:
>> On Tue, Nov 16, 2021 at 7:07 PM Denis Kalashnikov <denis281089@gmail.com> wrote:
>>> On MikroTik RB91x board series a reset key shares SoC gpio
>>> line #15 with NAND ALE and NAND IO7. So we need a custom
>>> gpio driver to manage this non-trivial connection schema.
>>> Also rb91x-nand needs to have ability to disable a polling
>>> of the key while it works with NAND, and we need to add
>>> "cansleep" suffix to several gpiolib calls in gpio-latch.
>>>
>>> Signed-off-by: Denis Kalashnikov <denis281089@gmail.com>
>> Please find below one nonsignificant nitpick, in all other respects:
>>
>> Reviewed-by: Sergey Ryazanov <ryazanov.s.a@gmail.com>
>>
>>> +struct gpio_rb91x_key {
>>> +       struct gpio_chip gc;
>>> +       struct mutex mutex;
>>> +       struct mutex poll_mutex;
>> These mutexes are worth a line of documentation. I do not care too
>> much, but if by some other reason you will submit v3, please consider
>> to describe their purpose.
>>
>>> +       int polling_disabled;
>>> +       struct gpio_desc *gpio;
>>> +};
>
>
> I'm seeing this in the bootlogs when using this patch:
>
> [    5.183305] gpio-latch gpio_latch: failed to get gpio 7: -517
> [    5.235889] rb91x-nand nand_gpio: failed to get gpios: -517

Looks like gpio-rb91x-key did not provide required GPIO lines.

Could you check whether the driver is built and initialized? Does it
create the gpio controller?
Denis K Jan. 17, 2022, 8:51 a.m. UTC | #10
> I'm seeing this in the bootlogs when using this patch:
>
> [    5.183305] gpio-latch gpio_latch: failed to get gpio 7: -517
> [    5.235889] rb91x-nand nand_gpio: failed to get gpios: -517

It's okay. The gpio-latch probe function seems to be called before the
rb91x-key probe, but it also returns  EPROBE_DEFER (-517), so it will
be called later. I've tested master with reset key patch and Koen's
patch that sets ref clock freq to 25000000. All is working: nand,
leds, key and Gigabit Ethernet (setting 100 -- pings okay, setting
back 1000 -- pings still okay).

Should I delete from my rb91x-key patch support for kernel 5.4 and submit v2?

Thanks,
Denis
Koen Vandeputte Jan. 18, 2022, 9:48 a.m. UTC | #11
On 17.01.22 09:51, Denis K wrote:
>> I'm seeing this in the bootlogs when using this patch:
>>
>> [    5.183305] gpio-latch gpio_latch: failed to get gpio 7: -517
>> [    5.235889] rb91x-nand nand_gpio: failed to get gpios: -517
I still think this should be avoided somehow.
> It's okay. The gpio-latch probe function seems to be called before the
> rb91x-key probe, but it also returns  EPROBE_DEFER (-517), so it will
> be called later. I've tested master with reset key patch and Koen's
> patch that sets ref clock freq to 25000000. All is working: nand,
> leds, key and Gigabit Ethernet (setting 100 -- pings okay, setting
> back 1000 -- pings still okay).
>
> Should I delete from my rb91x-key patch support for kernel 5.4 and submit v2?
Please do :-)
>
> Thanks,
> Denis

Thanks!

Koen
Sergey Ryazanov Jan. 18, 2022, 10:28 p.m. UTC | #12
On Mon, Jan 17, 2022 at 11:52 AM Denis K <denis281089@gmail.com> wrote:
>> I'm seeing this in the bootlogs when using this patch:
>>
>> [    5.183305] gpio-latch gpio_latch: failed to get gpio 7: -517
>> [    5.235889] rb91x-nand nand_gpio: failed to get gpios: -517
>
> It's okay. The gpio-latch probe function seems to be called before the
> rb91x-key probe, but it also returns  EPROBE_DEFER (-517), so it will
> be called later.

Nice catch!

Will it be a good idea to avoid printing the error messages from the
gpio-latch/rb91x-nand drivers in case of EPROBE_DEFER? Looks like this
will help to avoid noise in the dmesg and users confusion.

I do not mean doing this immediately in this series, but maybe
somedays this will be a welcome change?
diff mbox series

Patch

diff --git a/target/linux/ath79/config-5.10 b/target/linux/ath79/config-5.10
index 786d70decf..cbd883c4d3 100644
--- a/target/linux/ath79/config-5.10
+++ b/target/linux/ath79/config-5.10
@@ -74,6 +74,7 @@  CONFIG_GPIO_74X164=y
 CONFIG_GPIO_ATH79=y
 CONFIG_GPIO_GENERIC=y
 # CONFIG_GPIO_LATCH is not set
+# CONFIG_GPIO_RB91X_KEY is not set
 CONFIG_HANDLE_DOMAIN_IRQ=y
 CONFIG_HARDWARE_WATCHPOINTS=y
 CONFIG_HAS_DMA=y
diff --git a/target/linux/ath79/config-5.4 b/target/linux/ath79/config-5.4
index e37b728554..6ae5559730 100644
--- a/target/linux/ath79/config-5.4
+++ b/target/linux/ath79/config-5.4
@@ -91,6 +91,7 @@  CONFIG_GPIO_74X164=y
 CONFIG_GPIO_ATH79=y
 CONFIG_GPIO_GENERIC=y
 # CONFIG_GPIO_LATCH is not set
+# CONFIG_GPIO_RB91X_KEY is not set
 CONFIG_HANDLE_DOMAIN_IRQ=y
 CONFIG_HARDWARE_WATCHPOINTS=y
 CONFIG_HAS_DMA=y
diff --git a/target/linux/ath79/dts/ar9342_mikrotik_routerboard-912uag-2hpnd.dts b/target/linux/ath79/dts/ar9342_mikrotik_routerboard-912uag-2hpnd.dts
index ee2c12b4e0..34140a3f63 100644
--- a/target/linux/ath79/dts/ar9342_mikrotik_routerboard-912uag-2hpnd.dts
+++ b/target/linux/ath79/dts/ar9342_mikrotik_routerboard-912uag-2hpnd.dts
@@ -16,6 +16,13 @@ 
                 led-upgrade = &led_power;
         };
 
+	gpio_key: gpio_key {
+		compatible = "mikrotik,gpio-rb91x-key";
+		gpio-controller;
+		#gpio-cells = <2>;
+		gpio = <&gpio 15 GPIO_ACTIVE_HIGH>;
+	};
+
 	gpio_latch: gpio_latch {
 		compatible = "gpio-latch";
 		gpio-controller;
@@ -27,7 +34,7 @@ 
 			<0>, /* Not connected */
 			<&gpio 13 GPIO_ACTIVE_HIGH>,
 			<&gpio 14 GPIO_ACTIVE_HIGH>,
-			<&gpio 15 GPIO_ACTIVE_HIGH>,
+			<&gpio_key 0 GPIO_ACTIVE_HIGH>,
 			<&gpio 11 GPIO_ACTIVE_LOW>; /* Latch Enable */
 	};
 
@@ -40,7 +47,8 @@ 
 			<&gpio_latch 6 GPIO_ACTIVE_HIGH>, /* Command Latch Enable (CLE) */
 			<&gpio_latch 7 GPIO_ACTIVE_HIGH>, /* Address Latch Enable (ALE) */
 			<&gpio 12 GPIO_ACTIVE_LOW>,       /* Read/Write Enable (nRW) */
-			<&gpio_latch 8 GPIO_ACTIVE_LOW>;  /* Latch Enable (nLE) */
+			<&gpio_latch 8 GPIO_ACTIVE_LOW>,  /* Latch Enable (nLE) */
+			<&gpio_key 2 GPIO_ACTIVE_HIGH>;   /* Key poll disable */
 
 		partitions {
 			compatible = "fixed-partitions";
@@ -64,6 +72,17 @@ 
 		};
 	};
 
+	keys {
+		compatible = "gpio-keys-polled";
+		poll-interval = <20>;
+
+		button@0 {
+			label = "reset";
+			linux,code = <KEY_RESTART>;
+			gpios = <&gpio_key 1 GPIO_ACTIVE_LOW>;
+		};
+	};
+
 	leds {
 		compatible = "gpio-leds";
 
diff --git a/target/linux/ath79/files/drivers/gpio/gpio-latch.c b/target/linux/ath79/files/drivers/gpio/gpio-latch.c
index f3545a663e..44d6e3173c 100644
--- a/target/linux/ath79/files/drivers/gpio/gpio-latch.c
+++ b/target/linux/ath79/files/drivers/gpio/gpio-latch.c
@@ -62,7 +62,7 @@  gpio_latch_get(struct gpio_chip *gc, unsigned offset)
 	int ret;
 
 	gpio_latch_lock(glc, false);
-	ret = gpiod_get_value(glc->gpios[offset]);
+	ret = gpiod_get_raw_value_cansleep(glc->gpios[offset]);
 	gpio_latch_unlock(glc, false);
 
 	return ret;
@@ -81,7 +81,7 @@  gpio_latch_set(struct gpio_chip *gc, unsigned offset, int value)
 	}
 
 	gpio_latch_lock(glc, enable_latch);
-	gpiod_set_raw_value(glc->gpios[offset], value);
+	gpiod_set_raw_value_cansleep(glc->gpios[offset], value);
 	gpio_latch_unlock(glc, disable_latch);
 }
 
diff --git a/target/linux/ath79/files/drivers/gpio/gpio-rb91x-key.c b/target/linux/ath79/files/drivers/gpio/gpio-rb91x-key.c
new file mode 100644
index 0000000000..ec7b68dd4f
--- /dev/null
+++ b/target/linux/ath79/files/drivers/gpio/gpio-rb91x-key.c
@@ -0,0 +1,216 @@ 
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Driver for reset key gpio line on MikroTik RB91x board series.
+ * This line is shared between NAND ALE (goes through a latch),
+ * NAND IO7 and reset key. We make 3 virtual gpio lines from the
+ * single physical one:
+ *   1) Capable output one for NAND,
+ *   2) Capable input one for reset key,
+ *   3) And capable output one, aka "key-poll-disable",
+ *      for NAND -- to syncronise NAND operation and key polling.
+ *
+ * Copyright (C) 2021 Denis Kalashnikov <denis281089@gmail.com>
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/gpio/consumer.h>
+#include <linux/gpio/driver.h>
+#include <linux/platform_device.h>
+#include <linux/of_platform.h>
+#include <linux/of_gpio.h>
+#include <linux/delay.h>
+
+#define GPIO_RB91X_KEY_DRIVER_NAME  "gpio-rb91x-key"
+
+enum gpio_rb91x_key_gpios {
+	GPIO_RB91X_KEY_NAND,
+	GPIO_RB91X_KEY_POLL,
+	GPIO_RB91X_KEY_PDIS,
+
+	GPIO_RB91X_KEY_NGPIOS,
+};
+
+struct gpio_rb91x_key {
+	struct gpio_chip gc;
+	struct mutex mutex;
+	struct mutex poll_mutex;
+	int polling_disabled;
+	struct gpio_desc *gpio;
+};
+
+static inline struct gpio_rb91x_key *to_gpio_rb91x_key(struct gpio_chip *gc)
+{
+	return container_of(gc, struct gpio_rb91x_key, gc);
+}
+
+static int gpio_rb91x_key_get(struct gpio_chip *gc, unsigned offset)
+{
+	struct gpio_rb91x_key *drvdata = to_gpio_rb91x_key(gc);
+	struct gpio_desc *gpio = drvdata->gpio;
+	int val, bak_val;
+
+	switch (offset) {
+	case GPIO_RB91X_KEY_NAND:
+		mutex_lock(&drvdata->mutex);
+		val = gpiod_get_value_cansleep(gpio);
+		mutex_unlock(&drvdata->mutex);
+		break;
+	case GPIO_RB91X_KEY_PDIS:
+		mutex_lock(&drvdata->mutex);
+		val = drvdata->polling_disabled;
+		mutex_unlock(&drvdata->mutex);
+		break;
+	case GPIO_RB91X_KEY_POLL:
+		mutex_lock(&drvdata->poll_mutex);
+		mutex_lock(&drvdata->mutex);
+		bak_val = gpiod_get_raw_value_cansleep(gpio);
+		gpiod_direction_input(gpio);
+		/*
+		 * Without this delay nothing works. Get it
+		 * from mikrotik RouterOS linux kernel patches.
+		 */
+		udelay(200);
+		val = gpiod_get_raw_value_cansleep(gpio);
+		gpiod_direction_output_raw(gpio, bak_val);
+		mutex_unlock(&drvdata->mutex);
+		mutex_unlock(&drvdata->poll_mutex);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return val;
+}
+
+static int gpio_rb91x_key_direction_input(struct gpio_chip *gc, unsigned offset)
+{
+	switch (offset) {
+	case GPIO_RB91X_KEY_POLL:
+		return 0;
+	default:
+		return -EINVAL;
+	}
+}
+
+static void gpio_rb91x_key_set(struct gpio_chip *gc, unsigned offset, int value)
+{
+	struct gpio_rb91x_key *drvdata = to_gpio_rb91x_key(gc);
+	struct gpio_desc *gpio = drvdata->gpio;
+
+	mutex_lock(&drvdata->mutex);
+
+	switch (offset) {
+	case GPIO_RB91X_KEY_NAND:
+		gpiod_set_raw_value_cansleep(gpio, value);
+		break;
+	case GPIO_RB91X_KEY_PDIS:
+		if (value) {
+			if (!drvdata->polling_disabled) {
+				mutex_lock(&drvdata->poll_mutex);
+				drvdata->polling_disabled = 1;
+			}
+		} else {
+			if (drvdata->polling_disabled) {
+				mutex_unlock(&drvdata->poll_mutex);
+				drvdata->polling_disabled = 0;
+			}
+		}
+		break;
+	default:
+		break;
+	}
+
+	mutex_unlock(&drvdata->mutex);
+}
+
+static int gpio_rb91x_key_direction_output(struct gpio_chip *gc, unsigned offset,
+					   int value)
+{
+	switch (offset) {
+	case GPIO_RB91X_KEY_NAND:
+	case GPIO_RB91X_KEY_PDIS:
+		gpio_rb91x_key_set(gc, offset, value);
+		return 0;
+	default:
+		return -EINVAL;
+	}
+}
+
+static int gpio_rb91x_key_probe(struct platform_device *pdev)
+{
+	struct gpio_rb91x_key *drvdata;
+	struct gpio_chip *gc;
+	struct device *dev = &pdev->dev;
+	struct device_node *of_node = dev->of_node;
+	int r;
+
+	drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
+	if (!drvdata)
+		return -ENOMEM;
+
+	mutex_init(&drvdata->mutex);
+	mutex_init(&drvdata->poll_mutex);
+
+	drvdata->gpio = devm_gpiod_get(dev, NULL, GPIOD_OUT_LOW);
+	if (IS_ERR(drvdata->gpio)) {
+		dev_err(dev, "failed to get gpio: %ld\n",
+			PTR_ERR(drvdata->gpio));
+			return PTR_ERR(drvdata->gpio);
+	}
+
+	gc = &drvdata->gc;
+	gc->label = GPIO_RB91X_KEY_DRIVER_NAME;
+	gc->can_sleep = 1;
+	gc->base = -1;
+	gc->ngpio = GPIO_RB91X_KEY_NGPIOS;
+	gc->get = gpio_rb91x_key_get;
+	gc->set = gpio_rb91x_key_set;
+	gc->direction_output = gpio_rb91x_key_direction_output;
+	gc->direction_input = gpio_rb91x_key_direction_input;
+	gc->of_node = of_node;
+
+	platform_set_drvdata(pdev, drvdata);
+
+	r = gpiochip_add(&drvdata->gc);
+	if (r) {
+		dev_err(dev, "gpiochip_add() failed: %d\n", r);
+		return r;
+	}
+
+	return 0;
+}
+
+static int gpio_rb91x_key_remove(struct platform_device *pdev)
+{
+	struct gpio_rb91x_key *drvdata = platform_get_drvdata(pdev);
+
+	gpiochip_remove(&drvdata->gc);
+	return 0;
+}
+
+static const struct of_device_id gpio_rb91x_key_match[] = {
+	{ .compatible = "mikrotik,"GPIO_RB91X_KEY_DRIVER_NAME },
+	{},
+};
+
+MODULE_DEVICE_TABLE(of, gpio_rb91x_key_match);
+
+static struct platform_driver gpio_rb91x_key_driver = {
+	.probe = gpio_rb91x_key_probe,
+	.remove = gpio_rb91x_key_remove,
+	.driver = {
+		.name = GPIO_RB91X_KEY_DRIVER_NAME,
+		.owner = THIS_MODULE,
+		.of_match_table = gpio_rb91x_key_match,
+	},
+};
+
+module_platform_driver(gpio_rb91x_key_driver);
+
+MODULE_DESCRIPTION("Driver for reset key gpio line shared with NAND for MikroTik RB91x board series.");
+MODULE_AUTHOR("Denis Kalashnikov <denis281089@gmail.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" GPIO_RB91X_KEY_DRIVER_NAME);
diff --git a/target/linux/ath79/files/drivers/mtd/nand/raw/rb91x_nand.c b/target/linux/ath79/files/drivers/mtd/nand/raw/rb91x_nand.c
index e231944519..3ed17e45be 100644
--- a/target/linux/ath79/files/drivers/mtd/nand/raw/rb91x_nand.c
+++ b/target/linux/ath79/files/drivers/mtd/nand/raw/rb91x_nand.c
@@ -40,6 +40,7 @@  enum rb91x_nand_gpios {
 	RB91X_NAND_ALE, /* Address Latch Enable */
 	RB91X_NAND_NRW, /* Read/Write. Active low */
 	RB91X_NAND_NLE, /* Latch Enable. Active low */
+	RB91X_NAND_PDIS, /* Reset Key Poll Disable. Active high */
 
 	RB91X_NAND_GPIOS,
 };
@@ -57,6 +58,12 @@  static inline void rb91x_nand_latch_lock(struct rb91x_nand_drvdata *drvdata,
 	gpiod_set_value_cansleep(drvdata->gpio[RB91X_NAND_NLE], lock);
 }
 
+static inline void rb91x_nand_rst_key_poll_disable(struct rb91x_nand_drvdata *drvdata,
+						   int disable)
+{
+	gpiod_set_value_cansleep(drvdata->gpio[RB91X_NAND_PDIS], disable);
+}
+
 static int rb91x_ooblayout_ecc(struct mtd_info *mtd, int section,
 			       struct mtd_oob_region *oobregion)
 {
@@ -115,6 +122,7 @@  static void rb91x_nand_write(struct rb91x_nand_drvdata *drvdata,
 	unsigned i;
 
 	rb91x_nand_latch_lock(drvdata, 1);
+	rb91x_nand_rst_key_poll_disable(drvdata, 1);
 
 	oe_reg = __raw_readl(base + AR71XX_GPIO_REG_OE);
 	out_reg = __raw_readl(base + AR71XX_GPIO_REG_OUT);
@@ -146,6 +154,7 @@  static void rb91x_nand_write(struct rb91x_nand_drvdata *drvdata,
 	/* Flush write */
 	__raw_readl(base + AR71XX_GPIO_REG_OUT);
 
+	rb91x_nand_rst_key_poll_disable(drvdata, 0);
 	rb91x_nand_latch_lock(drvdata, 0);
 }
 
@@ -162,6 +171,7 @@  static void rb91x_nand_read(struct rb91x_nand_drvdata *drvdata,
 	gpiod_set_value_cansleep(drvdata->gpio[RB91X_NAND_READ], 1);
 
 	rb91x_nand_latch_lock(drvdata, 1);
+	rb91x_nand_rst_key_poll_disable(drvdata, 1);
 
 	/* Save registers */
 	oe_reg = __raw_readl(base + AR71XX_GPIO_REG_OE);
@@ -199,6 +209,7 @@  static void rb91x_nand_read(struct rb91x_nand_drvdata *drvdata,
 	/* Flush write */
 	__raw_readl(base + AR71XX_GPIO_REG_OUT);
 
+	rb91x_nand_rst_key_poll_disable(drvdata, 0);
 	rb91x_nand_latch_lock(drvdata, 0);
 
 	/* Disable read mode */
@@ -278,8 +289,8 @@  static int rb91x_nand_probe(struct platform_device *pdev)
 
 	gpios = gpiod_get_array(dev, NULL, GPIOD_OUT_LOW);
 	if (IS_ERR(gpios)) {
-		dev_err(dev, "failed to get gpios: %d\n", (int)gpios);
-		return -EINVAL;
+		dev_err(dev, "failed to get gpios: %d\n", PTR_ERR(gpios));
+		return PTR_ERR(gpios);
 	}
 
 	if (gpios->ndescs != RB91X_NAND_GPIOS) {
diff --git a/target/linux/ath79/mikrotik/config-default b/target/linux/ath79/mikrotik/config-default
index 74cfdf0423..f09f5c86bb 100644
--- a/target/linux/ath79/mikrotik/config-default
+++ b/target/linux/ath79/mikrotik/config-default
@@ -1,6 +1,7 @@ 
 CONFIG_CRC16=y
 CONFIG_CRYPTO_DEFLATE=y
 CONFIG_GPIO_LATCH=y
+CONFIG_GPIO_RB91X_KEY=y
 CONFIG_GPIO_RB4XX=y
 CONFIG_GPIO_WATCHDOG=y
 CONFIG_GPIO_WATCHDOG_ARCH_INITCALL=y
diff --git a/target/linux/ath79/patches-5.10/939-mikrotik-rb91x.patch b/target/linux/ath79/patches-5.10/939-mikrotik-rb91x.patch
index 5bbab93c51..4f4344b40f 100644
--- a/target/linux/ath79/patches-5.10/939-mikrotik-rb91x.patch
+++ b/target/linux/ath79/patches-5.10/939-mikrotik-rb91x.patch
@@ -14,6 +14,17 @@ 
  config GPIO_LOGICVC
  	tristate "Xylon LogiCVC GPIO support"
  	depends on MFD_SYSCON && OF
+@@ -495,6 +502,10 @@ config GPIO_REG
+ 	  A 32-bit single register GPIO fixed in/out implementation.  This
+ 	  can be used to represent any register as a set of GPIO signals.
+ 
++config GPIO_RB91X_KEY
++	tristate "MikroTik RB91x board series reset key support"
++	depends on ATH79
++
+ config GPIO_SAMA5D2_PIOBU
+ 	tristate "SAMA5D2 PIOBU GPIO support"
+ 	depends on MFD_SYSCON
 --- a/drivers/gpio/Makefile
 +++ b/drivers/gpio/Makefile
 @@ -72,6 +72,7 @@ obj-$(CONFIG_GPIO_IT87)			+= gpio-it87.o
@@ -24,6 +35,14 @@ 
  obj-$(CONFIG_GPIO_LOGICVC)		+= gpio-logicvc.o
  obj-$(CONFIG_GPIO_LOONGSON1)		+= gpio-loongson1.o
  obj-$(CONFIG_GPIO_LOONGSON)		+= gpio-loongson.o
+@@ -121,6 +122,7 @@ obj-$(CONFIG_GPIO_PMIC_EIC_SPRD)	+= gpio
+ obj-$(CONFIG_GPIO_PXA)			+= gpio-pxa.o
+ obj-$(CONFIG_GPIO_RASPBERRYPI_EXP)	+= gpio-raspberrypi-exp.o
+ obj-$(CONFIG_GPIO_RB4XX)		+= gpio-rb4xx.o
++obj-$(CONFIG_GPIO_RB91X_KEY)		+= gpio-rb91x-key.o
+ obj-$(CONFIG_GPIO_RC5T583)		+= gpio-rc5t583.o
+ obj-$(CONFIG_GPIO_RCAR)			+= gpio-rcar.o
+ obj-$(CONFIG_GPIO_RDA)			+= gpio-rda.o
 --- a/drivers/mtd/nand/raw/Kconfig
 +++ b/drivers/mtd/nand/raw/Kconfig
 @@ -571,4 +571,10 @@ config MTD_NAND_RB4XX
diff --git a/target/linux/ath79/patches-5.4/939-mikrotik-rb91x.patch b/target/linux/ath79/patches-5.4/939-mikrotik-rb91x.patch
index 6d84443945..680269507e 100644
--- a/target/linux/ath79/patches-5.4/939-mikrotik-rb91x.patch
+++ b/target/linux/ath79/patches-5.4/939-mikrotik-rb91x.patch
@@ -32,6 +32,17 @@ 
  config GPIO_RASPBERRYPI_EXP
  	tristate "Raspberry Pi 3 GPIO Expander"
  	default RASPBERRYPI_FIRMWARE
+@@ -441,6 +445,10 @@ config GPIO_REG
+ 	  A 32-bit single register GPIO fixed in/out implementation.  This
+ 	  can be used to represent any register as a set of GPIO signals.
+ 
++config GPIO_RB91X_KEY
++	tristate "MikroTik RB91x board series reset key support"
++	depends on ATH79
++
+ config GPIO_SAMA5D2_PIOBU
+ 	tristate "SAMA5D2 PIOBU GPIO support"
+ 	depends on MFD_SYSCON
 --- a/drivers/gpio/Makefile
 +++ b/drivers/gpio/Makefile
 @@ -67,6 +67,7 @@ obj-$(CONFIG_GPIO_IT87)			+= gpio-it87.o
@@ -42,3 +53,11 @@ 
  obj-$(CONFIG_GPIO_LOONGSON1)		+= gpio-loongson1.o
  obj-$(CONFIG_GPIO_LOONGSON)		+= gpio-loongson.o
  obj-$(CONFIG_GPIO_LP3943)		+= gpio-lp3943.o
+@@ -114,6 +115,7 @@ obj-$(CONFIG_GPIO_PMIC_EIC_SPRD)	+= gpio
+ obj-$(CONFIG_GPIO_PXA)			+= gpio-pxa.o
+ obj-$(CONFIG_GPIO_RASPBERRYPI_EXP)	+= gpio-raspberrypi-exp.o
+ obj-$(CONFIG_GPIO_RB4XX)		+= gpio-rb4xx.o
++obj-$(CONFIG_GPIO_RB91X_KEY)		+= gpio-rb91x-key.o
+ obj-$(CONFIG_GPIO_RC5T583)		+= gpio-rc5t583.o
+ obj-$(CONFIG_GPIO_RCAR)			+= gpio-rcar.o
+ obj-$(CONFIG_GPIO_RDC321X)		+= gpio-rdc321x.o