diff options
Diffstat (limited to 'target/mips/mikrotik-rb4xx/patches/3.14.13/0008-gpio-add-GPIO-latch-driver.patch')
-rw-r--r-- | target/mips/mikrotik-rb4xx/patches/3.14.13/0008-gpio-add-GPIO-latch-driver.patch | 290 |
1 files changed, 290 insertions, 0 deletions
diff --git a/target/mips/mikrotik-rb4xx/patches/3.14.13/0008-gpio-add-GPIO-latch-driver.patch b/target/mips/mikrotik-rb4xx/patches/3.14.13/0008-gpio-add-GPIO-latch-driver.patch new file mode 100644 index 000000000..188cec3b2 --- /dev/null +++ b/target/mips/mikrotik-rb4xx/patches/3.14.13/0008-gpio-add-GPIO-latch-driver.patch @@ -0,0 +1,290 @@ +From dd93d7e5b6530f1574860776fe6f960c4fd2661d Mon Sep 17 00:00:00 2001 +From: Phil Sutter <phil@nwl.cc> +Date: Tue, 13 May 2014 00:21:54 +0200 +Subject: [PATCH] gpio: add GPIO latch driver + +--- + drivers/gpio/Kconfig | 7 + + drivers/gpio/Makefile | 1 + + drivers/gpio/gpio-latch.c | 219 +++++++++++++++++++++++++++++++ + include/linux/platform_data/gpio-latch.h | 14 ++ + 4 files changed, 241 insertions(+) + create mode 100644 drivers/gpio/gpio-latch.c + create mode 100644 include/linux/platform_data/gpio-latch.h + +diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig +index 903f24d..905730b 100644 +--- a/drivers/gpio/Kconfig ++++ b/drivers/gpio/Kconfig +@@ -834,4 +834,11 @@ config GPIO_VIPERBOARD + River Tech's viperboard.h for detailed meaning + of the module parameters. + ++comment "Other GPIO expanders" ++ ++config GPIO_LATCH ++ tristate "GPIO latch driver" ++ help ++ Say yes here to enable a GPIO latch driver. ++ + endif +diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile +index 5d50179..7d03524 100644 +--- a/drivers/gpio/Makefile ++++ b/drivers/gpio/Makefile +@@ -36,6 +36,7 @@ obj-$(CONFIG_GPIO_KEMPLD) += gpio-kempld.o + obj-$(CONFIG_ARCH_KS8695) += gpio-ks8695.o + obj-$(CONFIG_GPIO_INTEL_MID) += gpio-intel-mid.o + obj-$(CONFIG_GPIO_LP3943) += gpio-lp3943.o ++obj-$(CONFIG_GPIO_LATCH) += gpio-latch.o + obj-$(CONFIG_ARCH_LPC32XX) += gpio-lpc32xx.o + obj-$(CONFIG_GPIO_LYNXPOINT) += gpio-lynxpoint.o + obj-$(CONFIG_GPIO_MAX730X) += gpio-max730x.o +diff --git a/drivers/gpio/gpio-latch.c b/drivers/gpio/gpio-latch.c +new file mode 100644 +index 0000000..1efa1a1 +--- /dev/null ++++ b/drivers/gpio/gpio-latch.c +@@ -0,0 +1,219 @@ ++/* ++ * GPIO latch driver ++ * ++ * Copyright (C) 2014 Gabor Juhos <juhosg@openwrt.org> ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation. ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/init.h> ++#include <linux/module.h> ++#include <linux/types.h> ++#include <linux/gpio.h> ++#include <linux/slab.h> ++#include <linux/platform_device.h> ++ ++#include <linux/platform_data/gpio-latch.h> ++ ++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; ++ int *gpios; ++}; ++ ++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 = gpio_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; ++ int gpio; ++ ++ gpio = glc->gpios[offset]; ++ ++ if (gpio == glc->le_gpio) { ++ enable_latch = value ^ glc->le_active_low; ++ disable_latch = !enable_latch; ++ } ++ ++ gpio_latch_lock(glc, enable_latch); ++ gpio_set_value(gpio, value); ++ gpio_latch_unlock(glc, disable_latch); ++} ++ ++static int ++gpio_latch_direction_input(struct gpio_chip *gc, unsigned offset) ++{ ++ struct gpio_latch_chip *glc = to_gpio_latch_chip(gc); ++ int ret; ++ ++ gpio_latch_lock(glc, false); ++ ret = gpio_direction_input(glc->gpios[offset]); ++ gpio_latch_unlock(glc, false); ++ ++ return ret; ++} ++ ++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 gpio; ++ int ret; ++ ++ gpio = glc->gpios[offset]; ++ ++ if (gpio == glc->le_gpio) { ++ enable_latch = value ^ glc->le_active_low; ++ disable_latch = !enable_latch; ++ } ++ ++ gpio_latch_lock(glc, enable_latch); ++ ret = gpio_direction_output(gpio, 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_latch_platform_data *pdata; ++ struct gpio_chip *gc; ++ int size; ++ int ret; ++ int i; ++ ++ pdata = dev_get_platdata(&pdev->dev); ++ if (!pdata) ++ return -EINVAL; ++ ++ if (pdata->le_gpio_index >= pdata->num_gpios || ++ !pdata->num_gpios || ++ !pdata->gpios) ++ return -EINVAL; ++ ++ for (i = 0; i < pdata->num_gpios; i++) { ++ int gpio = pdata->gpios[i]; ++ ++ ret = devm_gpio_request(&pdev->dev, gpio, ++ GPIO_LATCH_DRIVER_NAME); ++ if (ret) ++ return ret; ++ } ++ ++ glc = devm_kzalloc(&pdev->dev, sizeof(*glc), GFP_KERNEL); ++ if (!glc) ++ return -ENOMEM; ++ ++ mutex_init(&glc->mutex); ++ mutex_init(&glc->latch_mutex); ++ ++ size = pdata->num_gpios * sizeof(glc->gpios[0]); ++ glc->gpios = devm_kzalloc(&pdev->dev, size , GFP_KERNEL); ++ if (!glc->gpios) ++ return -ENOMEM; ++ ++ memcpy(glc->gpios, pdata->gpios, size); ++ ++ glc->le_gpio = glc->gpios[pdata->le_gpio_index]; ++ glc->le_active_low = pdata->le_active_low; ++ ++ gc = &glc->gc; ++ ++ gc->label = GPIO_LATCH_DRIVER_NAME; ++ gc->base = pdata->base; ++ gc->can_sleep = true; ++ gc->ngpio = pdata->num_gpios; ++ gc->get = gpio_latch_get; ++ gc->set = gpio_latch_set; ++ gc->direction_input = gpio_latch_direction_input, ++ gc->direction_output = gpio_latch_direction_output; ++ ++ platform_set_drvdata(pdev, glc); ++ ++ ret = gpiochip_add(&glc->gc); ++ if (ret) ++ return ret; ++ ++ return 0; ++} ++ ++static int gpio_latch_remove(struct platform_device *pdev) ++{ ++ struct gpio_latch_chip *glc = platform_get_drvdata(pdev); ++ ++ return gpiochip_remove(&glc->gc);; ++} ++ ++ ++static struct platform_driver gpio_latch_driver = { ++ .probe = gpio_latch_probe, ++ .remove = gpio_latch_remove, ++ .driver = { ++ .name = GPIO_LATCH_DRIVER_NAME, ++ .owner = THIS_MODULE, ++ }, ++}; ++ ++static int __init gpio_latch_init(void) ++{ ++ return platform_driver_register(&gpio_latch_driver); ++} ++ ++postcore_initcall(gpio_latch_init); ++ ++MODULE_DESCRIPTION("GPIO latch driver"); ++MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org>"); ++MODULE_LICENSE("GPL v2"); ++MODULE_ALIAS("platform:" GPIO_LATCH_DRIVER_NAME); +diff --git a/include/linux/platform_data/gpio-latch.h b/include/linux/platform_data/gpio-latch.h +new file mode 100644 +index 0000000..0450e67 +--- /dev/null ++++ b/include/linux/platform_data/gpio-latch.h +@@ -0,0 +1,14 @@ ++#ifndef _GPIO_LATCH_H_ ++#define _GPIO_LATCH_H_ ++ ++#define GPIO_LATCH_DRIVER_NAME "gpio-latch" ++ ++struct gpio_latch_platform_data { ++ int base; ++ int num_gpios; ++ int *gpios; ++ int le_gpio_index; ++ bool le_active_low; ++}; ++ ++#endif /* _GPIO_LATCH_H_ */ +-- +1.8.5.3 + |