ath79: add gpio-latch driver for MikroTik RouterBOARDs
This is a slighty modified version of ar71xx gpio-latch driver
written by Gabor Juhos <juhosg@openwrt.org>.
Changes:
* DTS support,
* New gpio API (gpiod_*).
Reviewed-by: Sergey Ryazanov <ryazanov.s.a@gmail.com>
Signed-off-by: Denis Kalashnikov <denis281089@gmail.com>
(cherry-picked from commit 7b8931678c)
			
			
This commit is contained in:
		 Denis Kalashnikov
					Denis Kalashnikov
				
			
				
					committed by
					
						 Koen Vandeputte
						Koen Vandeputte
					
				
			
			
				
	
			
			
			 Koen Vandeputte
						Koen Vandeputte
					
				
			
						parent
						
							3eb34bc251
						
					
				
				
					commit
					43723e6db9
				
			
							
								
								
									
										203
									
								
								target/linux/ath79/files/drivers/gpio/gpio-latch.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										203
									
								
								target/linux/ath79/files/drivers/gpio/gpio-latch.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,203 @@ | |||||||
|  | // SPDX-License-Identifier: GPL-2.0-or-later | ||||||
|  | /* | ||||||
|  |  *  GPIO latch driver | ||||||
|  |  * | ||||||
|  |  *  Copyright (C) 2014 Gabor Juhos <juhosg@openwrt.org> | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #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> | ||||||
|  |  | ||||||
|  | #define GPIO_LATCH_DRIVER_NAME  "gpio-latch" | ||||||
|  | #define GPIO_LATCH_LINES 9 | ||||||
|  |  | ||||||
|  | struct gpio_latch_chip { | ||||||
|  | 	struct gpio_chip gc; | ||||||
|  | 	struct mutex mutex; | ||||||
|  | 	struct mutex latch_mutex; | ||||||
|  | 	bool latch_enabled; | ||||||
|  | 	int le_gpio; | ||||||
|  | 	bool le_active_low; | ||||||
|  | 	struct gpio_desc *gpios[GPIO_LATCH_LINES]; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | static inline struct gpio_latch_chip *to_gpio_latch_chip(struct gpio_chip *gc) | ||||||
|  | { | ||||||
|  | 	return container_of(gc, struct gpio_latch_chip, gc); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void gpio_latch_lock(struct gpio_latch_chip *glc, bool enable) | ||||||
|  | { | ||||||
|  | 	mutex_lock(&glc->mutex); | ||||||
|  |  | ||||||
|  | 	if (enable) | ||||||
|  | 		glc->latch_enabled = true; | ||||||
|  |  | ||||||
|  | 	if (glc->latch_enabled) | ||||||
|  | 		mutex_lock(&glc->latch_mutex); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void gpio_latch_unlock(struct gpio_latch_chip *glc, bool disable) | ||||||
|  | { | ||||||
|  | 	if (glc->latch_enabled) | ||||||
|  | 		mutex_unlock(&glc->latch_mutex); | ||||||
|  |  | ||||||
|  | 	if (disable) | ||||||
|  | 		glc->latch_enabled = true; | ||||||
|  |  | ||||||
|  | 	mutex_unlock(&glc->mutex); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int | ||||||
|  | gpio_latch_get(struct gpio_chip *gc, unsigned offset) | ||||||
|  | { | ||||||
|  | 	struct gpio_latch_chip *glc = to_gpio_latch_chip(gc); | ||||||
|  | 	int ret; | ||||||
|  |  | ||||||
|  | 	gpio_latch_lock(glc, false); | ||||||
|  | 	ret = gpiod_get_value(glc->gpios[offset]); | ||||||
|  | 	gpio_latch_unlock(glc, false); | ||||||
|  |  | ||||||
|  | 	return ret; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void | ||||||
|  | gpio_latch_set(struct gpio_chip *gc, unsigned offset, int value) | ||||||
|  | { | ||||||
|  | 	struct gpio_latch_chip *glc = to_gpio_latch_chip(gc); | ||||||
|  | 	bool enable_latch = false; | ||||||
|  | 	bool disable_latch = false; | ||||||
|  |  | ||||||
|  | 	if (offset == glc->le_gpio) { | ||||||
|  | 		enable_latch = value ^ glc->le_active_low; | ||||||
|  | 		disable_latch = !enable_latch; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	gpio_latch_lock(glc, enable_latch); | ||||||
|  | 	gpiod_set_raw_value(glc->gpios[offset], value); | ||||||
|  | 	gpio_latch_unlock(glc, disable_latch); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int | ||||||
|  | gpio_latch_direction_output(struct gpio_chip *gc, unsigned offset, int value) | ||||||
|  | { | ||||||
|  | 	struct gpio_latch_chip *glc = to_gpio_latch_chip(gc); | ||||||
|  | 	bool enable_latch = false; | ||||||
|  | 	bool disable_latch = false; | ||||||
|  | 	int ret; | ||||||
|  |  | ||||||
|  | 	if (offset == glc->le_gpio) { | ||||||
|  | 		enable_latch = value ^ glc->le_active_low; | ||||||
|  | 		disable_latch = !enable_latch; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	gpio_latch_lock(glc, enable_latch); | ||||||
|  | 	ret = gpiod_direction_output_raw(glc->gpios[offset], value); | ||||||
|  | 	gpio_latch_unlock(glc, disable_latch); | ||||||
|  |  | ||||||
|  | 	return ret; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int gpio_latch_probe(struct platform_device *pdev) | ||||||
|  | { | ||||||
|  | 	struct gpio_latch_chip *glc; | ||||||
|  | 	struct gpio_chip *gc; | ||||||
|  | 	struct device *dev = &pdev->dev; | ||||||
|  | 	struct device_node *of_node = dev->of_node; | ||||||
|  | 	int i, n; | ||||||
|  |  | ||||||
|  | 	glc = devm_kzalloc(dev, sizeof(*glc), GFP_KERNEL); | ||||||
|  | 	if (!glc) | ||||||
|  | 		return -ENOMEM; | ||||||
|  |  | ||||||
|  | 	mutex_init(&glc->mutex); | ||||||
|  | 	mutex_init(&glc->latch_mutex); | ||||||
|  |  | ||||||
|  | 	n = gpiod_count(dev, NULL); | ||||||
|  | 	if (n <= 0) { | ||||||
|  | 		dev_err(dev, "failed to get gpios: %d\n", n); | ||||||
|  | 		return n; | ||||||
|  | 	} else if (n != GPIO_LATCH_LINES) { | ||||||
|  | 		dev_err(dev, "expected %d gpios\n", GPIO_LATCH_LINES); | ||||||
|  | 		return -EINVAL; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for (i = 0; i < n; i++) { | ||||||
|  | 		glc->gpios[i] = devm_gpiod_get_index_optional(dev, NULL, i, | ||||||
|  | 			GPIOD_OUT_LOW); | ||||||
|  | 		if (IS_ERR(glc->gpios[i])) { | ||||||
|  | 			dev_err(dev, "failed to get gpio %d: %d\n", i, | ||||||
|  | 				PTR_ERR(glc->gpios[i])); | ||||||
|  | 			return PTR_ERR(glc->gpios[i]); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	glc->le_gpio = 8; | ||||||
|  | 	glc->le_active_low = gpiod_is_active_low(glc->gpios[glc->le_gpio]); | ||||||
|  |  | ||||||
|  | 	if (!glc->gpios[glc->le_gpio]) { | ||||||
|  | 		dev_err(dev, "missing required latch-enable gpio %d\n", | ||||||
|  | 			glc->le_gpio); | ||||||
|  | 		return -EINVAL; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	gc = &glc->gc; | ||||||
|  | 	gc->label = GPIO_LATCH_DRIVER_NAME; | ||||||
|  | 	gc->can_sleep = true; | ||||||
|  | 	gc->base = -1; | ||||||
|  | 	gc->ngpio = GPIO_LATCH_LINES; | ||||||
|  | 	gc->get = gpio_latch_get; | ||||||
|  | 	gc->set = gpio_latch_set; | ||||||
|  | 	gc->direction_output = gpio_latch_direction_output; | ||||||
|  | 	gc->of_node = of_node; | ||||||
|  |  | ||||||
|  | 	platform_set_drvdata(pdev, glc); | ||||||
|  |  | ||||||
|  | 	i = gpiochip_add(&glc->gc); | ||||||
|  | 	if (i) { | ||||||
|  | 		dev_err(dev, "gpiochip_add() failed: %d\n", i); | ||||||
|  | 		return i; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int gpio_latch_remove(struct platform_device *pdev) | ||||||
|  | { | ||||||
|  | 	struct gpio_latch_chip *glc = platform_get_drvdata(pdev); | ||||||
|  |  | ||||||
|  | 	gpiochip_remove(&glc->gc); | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static const struct of_device_id gpio_latch_match[] = { | ||||||
|  | 	{ .compatible = GPIO_LATCH_DRIVER_NAME }, | ||||||
|  | 	{}, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | MODULE_DEVICE_TABLE(of, gpio_latch_match); | ||||||
|  |  | ||||||
|  | static struct platform_driver gpio_latch_driver = { | ||||||
|  | 	.probe = gpio_latch_probe, | ||||||
|  | 	.remove = gpio_latch_remove, | ||||||
|  | 	.driver = { | ||||||
|  | 		.name = GPIO_LATCH_DRIVER_NAME, | ||||||
|  | 		.owner = THIS_MODULE, | ||||||
|  | 		.of_match_table = gpio_latch_match, | ||||||
|  | 	}, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | module_platform_driver(gpio_latch_driver); | ||||||
|  |  | ||||||
|  | MODULE_DESCRIPTION("GPIO latch driver"); | ||||||
|  | MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org>"); | ||||||
|  | MODULE_AUTHOR("Denis Kalashnikov <denis281089@gmail.com>"); | ||||||
|  | MODULE_LICENSE("GPL v2"); | ||||||
|  | MODULE_ALIAS("platform:" GPIO_LATCH_DRIVER_NAME); | ||||||
							
								
								
									
										23
									
								
								target/linux/ath79/patches-5.4/939-mikrotik-rb91x.patch
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								target/linux/ath79/patches-5.4/939-mikrotik-rb91x.patch
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,23 @@ | |||||||
|  | --- a/drivers/gpio/Kconfig | ||||||
|  | +++ b/drivers/gpio/Kconfig | ||||||
|  | @@ -130,6 +130,10 @@ config GPIO_ATH79 | ||||||
|  |  	  Select this option to enable GPIO driver for | ||||||
|  |  	  Atheros AR71XX/AR724X/AR913X SoC devices. | ||||||
|  |   | ||||||
|  | +config GPIO_LATCH | ||||||
|  | +	tristate "MikroTik RouterBOARD GPIO latch support" | ||||||
|  | +	depends on ATH79 | ||||||
|  | + | ||||||
|  |  config GPIO_RASPBERRYPI_EXP | ||||||
|  |  	tristate "Raspberry Pi 3 GPIO Expander" | ||||||
|  |  	default RASPBERRYPI_FIRMWARE | ||||||
|  | --- a/drivers/gpio/Makefile | ||||||
|  | +++ b/drivers/gpio/Makefile | ||||||
|  | @@ -67,6 +67,7 @@ obj-$(CONFIG_GPIO_IT87)			+= gpio-it87.o | ||||||
|  |  obj-$(CONFIG_GPIO_IXP4XX)		+= gpio-ixp4xx.o | ||||||
|  |  obj-$(CONFIG_GPIO_JANZ_TTL)		+= gpio-janz-ttl.o | ||||||
|  |  obj-$(CONFIG_GPIO_KEMPLD)		+= gpio-kempld.o | ||||||
|  | +obj-$(CONFIG_GPIO_LATCH)		+= gpio-latch.o | ||||||
|  |  obj-$(CONFIG_GPIO_LOONGSON1)		+= gpio-loongson1.o | ||||||
|  |  obj-$(CONFIG_GPIO_LOONGSON)		+= gpio-loongson.o | ||||||
|  |  obj-$(CONFIG_GPIO_LP3943)		+= gpio-lp3943.o | ||||||
		Reference in New Issue
	
	Block a user