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