| 
					
				 | 
			
			
				@@ -0,0 +1,218 @@ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+// 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 <[email protected]> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#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)) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		if (PTR_ERR(drvdata->gpio) != -EPROBE_DEFER) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			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 <[email protected]>"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+MODULE_LICENSE("GPL v2"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+MODULE_ALIAS("platform:" GPIO_RB91X_KEY_DRIVER_NAME); 
			 |