summaryrefslogtreecommitdiff
path: root/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0041-Enable-passthrough-based-gpio-character-device.patch
diff options
context:
space:
mode:
Diffstat (limited to 'meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0041-Enable-passthrough-based-gpio-character-device.patch')
-rw-r--r--meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0041-Enable-passthrough-based-gpio-character-device.patch287
1 files changed, 287 insertions, 0 deletions
diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0041-Enable-passthrough-based-gpio-character-device.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0041-Enable-passthrough-based-gpio-character-device.patch
new file mode 100644
index 000000000..9e7757011
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0041-Enable-passthrough-based-gpio-character-device.patch
@@ -0,0 +1,287 @@
+From d34efc982a9206db87da49be3d9b1e20f59be56f Mon Sep 17 00:00:00 2001
+From: Kuiying Wang <kuiying.wang@intel.com>
+Date: Thu, 31 Jan 2019 17:47:39 +0800
+Subject: [PATCH] Enable passthrough based gpio character device.
+
+Signed-off-by: Kuiying Wang <kuiying.wang@intel.com>
+---
+ drivers/gpio/gpio-aspeed.c | 47 ++++++++++++++++++++++-
+ drivers/gpio/gpiolib.c | 51 +++++++++++++++++++++++--
+ drivers/gpio/gpiolib.h | 1 +
+ include/linux/gpio/consumer.h | 9 +++++
+ include/linux/pinctrl/pinconf-generic.h | 2 +
+ include/uapi/linux/gpio.h | 1 +
+ 6 files changed, 106 insertions(+), 5 deletions(-)
+
+diff --git a/drivers/gpio/gpio-aspeed.c b/drivers/gpio/gpio-aspeed.c
+index 2342e154029b..06fd95197684 100644
+--- a/drivers/gpio/gpio-aspeed.c
++++ b/drivers/gpio/gpio-aspeed.c
+@@ -17,9 +17,11 @@
+ #include <linux/init.h>
+ #include <linux/io.h>
+ #include <linux/kernel.h>
++#include <linux/mfd/syscon.h>
+ #include <linux/module.h>
+ #include <linux/pinctrl/consumer.h>
+ #include <linux/platform_device.h>
++#include <linux/regmap.h>
+ #include <linux/spinlock.h>
+ #include <linux/string.h>
+
+@@ -58,6 +60,7 @@ struct aspeed_gpio {
+ struct gpio_chip chip;
+ spinlock_t lock;
+ void __iomem *base;
++ struct regmap *scu;
+ int irq;
+ const struct aspeed_gpio_config *config;
+
+@@ -91,6 +94,13 @@ struct aspeed_gpio_bank {
+ * and thus can be used to read back what was last written
+ * reliably.
+ */
++#define SCU8C 0x8C /* Multi-function Pin Control #4 */
++#define PASS_THROUGH1 32
++#define PASS_THROUGH2 34
++#define PASS_THROUGH2_MASK 0x2000
++#define PASS_THROUGH1_MASK 0x1000
++#define PASS_THROUGH2_ON 0x2000
++#define PASS_THROUGH1_ON 0x1000
+
+ static const int debounce_timers[4] = { 0x00, 0x50, 0x54, 0x58 };
+
+@@ -988,12 +998,38 @@ static int set_debounce(struct gpio_chip *chip, unsigned int offset,
+ return disable_debounce(chip, offset);
+ }
+
++static int aspeed_gpio_pass_through(struct gpio_chip *chip, unsigned int offset,
++ unsigned long param)
++{
++ struct aspeed_gpio *gpio = gpiochip_get_data(chip);
++ u32 value;
++
++ if (!gpio->scu)
++ return -ENOTSUPP;
++ if (param == PIN_CONFIG_PASS_THROUGH_ENABLE){
++ if (offset == PASS_THROUGH2){
++ regmap_update_bits(gpio->scu, SCU8C, PASS_THROUGH2_MASK, PASS_THROUGH2_ON);
++ } else if (offset == PASS_THROUGH1){
++ regmap_update_bits(gpio->scu, SCU8C, PASS_THROUGH1_MASK, PASS_THROUGH1_ON);
++ }
++ } else if (param == PIN_CONFIG_PASS_THROUGH_DISABLE){
++ if (offset == PASS_THROUGH2){
++ regmap_update_bits(gpio->scu, SCU8C, PASS_THROUGH2_MASK, ~(PASS_THROUGH2_ON));
++ } else if (offset == PASS_THROUGH1){
++ regmap_update_bits(gpio->scu, SCU8C, PASS_THROUGH1_MASK, ~(PASS_THROUGH1_ON));
++ }
++ } else {
++ return -ENOTSUPP;
++ }
++
++ return 0;
++}
++
+ static int aspeed_gpio_set_config(struct gpio_chip *chip, unsigned int offset,
+ unsigned long config)
+ {
+ unsigned long param = pinconf_to_config_param(config);
+ u32 arg = pinconf_to_config_argument(config);
+-
+ if (param == PIN_CONFIG_INPUT_DEBOUNCE)
+ return set_debounce(chip, offset, arg);
+ else if (param == PIN_CONFIG_BIAS_DISABLE ||
+@@ -1006,6 +1042,9 @@ static int aspeed_gpio_set_config(struct gpio_chip *chip, unsigned int offset,
+ return -ENOTSUPP;
+ else if (param == PIN_CONFIG_PERSIST_STATE)
+ return aspeed_gpio_reset_tolerance(chip, offset, arg);
++ else if (param == PIN_CONFIG_PASS_THROUGH_ENABLE ||
++ param == PIN_CONFIG_PASS_THROUGH_DISABLE)
++ return aspeed_gpio_pass_through(chip, offset, param);
+
+ return -ENOTSUPP;
+ }
+@@ -1167,7 +1206,11 @@ static int __init aspeed_gpio_probe(struct platform_device *pdev)
+ gpio->base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(gpio->base))
+ return PTR_ERR(gpio->base);
+-
++ gpio->scu = syscon_regmap_lookup_by_compatible("aspeed,ast2500-scu");
++ if (IS_ERR(gpio->scu)) {
++ dev_err(&pdev->dev, "Failed to find SCU regmap\n");
++ gpio->scu = NULL;
++ }
+ spin_lock_init(&gpio->lock);
+
+ gpio_id = of_match_node(aspeed_gpio_of_table, pdev->dev.of_node);
+diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
+index a8e01d99919c..21eeca17583d 100644
+--- a/drivers/gpio/gpiolib.c
++++ b/drivers/gpio/gpiolib.c
+@@ -419,6 +419,7 @@ struct linehandle_state {
+ GPIOHANDLE_REQUEST_OUTPUT | \
+ GPIOHANDLE_REQUEST_ACTIVE_LOW | \
+ GPIOHANDLE_REQUEST_OPEN_DRAIN | \
++ GPIOHANDLE_REQUEST_PASS_THROUGH | \
+ GPIOHANDLE_REQUEST_OPEN_SOURCE)
+
+ static long linehandle_ioctl(struct file *filep, unsigned int cmd,
+@@ -519,7 +520,6 @@ static int linehandle_create(struct gpio_device *gdev, void __user *ip)
+ return -EINVAL;
+
+ lflags = handlereq.flags;
+-
+ /* Return an error if an unknown flag is set */
+ if (lflags & ~GPIOHANDLE_REQUEST_VALID_FLAGS)
+ return -EINVAL;
+@@ -579,6 +579,8 @@ static int linehandle_create(struct gpio_device *gdev, void __user *ip)
+ set_bit(FLAG_OPEN_DRAIN, &desc->flags);
+ if (lflags & GPIOHANDLE_REQUEST_OPEN_SOURCE)
+ set_bit(FLAG_OPEN_SOURCE, &desc->flags);
++ if (lflags & GPIOHANDLE_REQUEST_PASS_THROUGH)
++ set_bit(FLAG_PASS_THROUGH, &desc->flags);
+
+ ret = gpiod_set_transitory(desc, false);
+ if (ret < 0)
+@@ -598,6 +600,11 @@ static int linehandle_create(struct gpio_device *gdev, void __user *ip)
+ ret = gpiod_direction_input(desc);
+ if (ret)
+ goto out_free_descs;
++ } else if (lflags & GPIOHANDLE_REQUEST_PASS_THROUGH) {
++ int val = !!handlereq.default_values[i];
++ ret = gpiod_direction_pass_through(desc, val);
++ if (ret)
++ goto out_free_descs;
+ }
+ dev_dbg(&gdev->dev, "registered chardev handle for line %d\n",
+ offset);
+@@ -1010,7 +1017,6 @@ static long gpio_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+ struct gpio_device *gdev = filp->private_data;
+ struct gpio_chip *chip = gdev->chip;
+ void __user *ip = (void __user *)arg;
+-
+ /* We fail any subsequent ioctl():s when the chip is gone */
+ if (!chip)
+ return -ENODEV;
+@@ -1018,7 +1024,6 @@ static long gpio_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+ /* Fill in the struct and pass to userspace */
+ if (cmd == GPIO_GET_CHIPINFO_IOCTL) {
+ struct gpiochip_info chipinfo;
+-
+ memset(&chipinfo, 0, sizeof(chipinfo));
+
+ strncpy(chipinfo.name, dev_name(&gdev->dev),
+@@ -2643,6 +2648,46 @@ int gpiod_direction_output(struct gpio_desc *desc, int value)
+ }
+ EXPORT_SYMBOL_GPL(gpiod_direction_output);
+
++/**
++ * gpiod_direction_pass_through - set the GPIO direction to pass-through
++ * @desc: GPIO to set to pass-through
++ *
++ * Set the direction of the passed GPIO to passthrough.
++ *
++ * Return 0 in case of success, else an error code.
++ */
++int gpiod_direction_pass_through(struct gpio_desc *desc, int val)
++{
++ struct gpio_chip *gc;
++
++ VALIDATE_DESC(desc);
++ /* GPIOs used for IRQs shall not be set as pass-through */
++ if (test_bit(FLAG_USED_AS_IRQ, &desc->flags)) {
++ gpiod_err(desc,
++ "%s: tried to set a GPIO tied to an IRQ as pass-through\n",
++ __func__);
++ return -EIO;
++ }
++ gc = desc->gdev->chip;
++ val = !!val;
++ if (test_bit(FLAG_PASS_THROUGH, &desc->flags)) {
++ if (val)
++ gpio_set_drive_single_ended(gc, gpio_chip_hwgpio(desc),
++ PIN_CONFIG_PASS_THROUGH_ENABLE);
++ else
++ gpio_set_drive_single_ended(gc, gpio_chip_hwgpio(desc),
++ PIN_CONFIG_PASS_THROUGH_DISABLE);
++ } else {
++ gpiod_err(desc,
++ "%s: desc->flags is not set to FLAG_PASS_THROUGH\n",
++ __func__);
++ return -EIO;
++ }
++
++ return 0;
++}
++EXPORT_SYMBOL_GPL(gpiod_direction_pass_through);
++
+ /**
+ * gpiod_set_debounce - sets @debounce time for a GPIO
+ * @desc: descriptor of the GPIO for which to set debounce time
+diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h
+index a7e49fef73d4..b143ee47870a 100644
+--- a/drivers/gpio/gpiolib.h
++++ b/drivers/gpio/gpiolib.h
+@@ -210,6 +210,7 @@ struct gpio_desc {
+ #define FLAG_IS_OUT 1
+ #define FLAG_EXPORT 2 /* protected by sysfs_lock */
+ #define FLAG_SYSFS 3 /* exported via /sys/class/gpio/control */
++#define FLAG_PASS_THROUGH 4 /*Gpio is passthrough type*/
+ #define FLAG_ACTIVE_LOW 6 /* value has active low */
+ #define FLAG_OPEN_DRAIN 7 /* Gpio is open drain type */
+ #define FLAG_OPEN_SOURCE 8 /* Gpio is open source type */
+diff --git a/include/linux/gpio/consumer.h b/include/linux/gpio/consumer.h
+index 21ddbe440030..96551839c191 100644
+--- a/include/linux/gpio/consumer.h
++++ b/include/linux/gpio/consumer.h
+@@ -99,6 +99,7 @@ void devm_gpiod_put_array(struct device *dev, struct gpio_descs *descs);
+ int gpiod_get_direction(struct gpio_desc *desc);
+ int gpiod_direction_input(struct gpio_desc *desc);
+ int gpiod_direction_output(struct gpio_desc *desc, int value);
++int gpiod_direction_pass_through(struct gpio_desc *desc, int val);
+ int gpiod_direction_output_raw(struct gpio_desc *desc, int value);
+
+ /* Value get/set from non-sleeping context */
+@@ -314,6 +315,14 @@ static inline int gpiod_direction_output(struct gpio_desc *desc, int value)
+ WARN_ON(1);
+ return -ENOSYS;
+ }
++
++static inline int gpiod_direction_pass_through(struct gpio_desc *desc, int val)
++{
++ /* GPIO can never have been requested */
++ WARN_ON(1);
++ return -ENOSYS;
++}
++
+ static inline int gpiod_direction_output_raw(struct gpio_desc *desc, int value)
+ {
+ /* GPIO can never have been requested */
+diff --git a/include/linux/pinctrl/pinconf-generic.h b/include/linux/pinctrl/pinconf-generic.h
+index 6c0680641108..59f0cbabb685 100644
+--- a/include/linux/pinctrl/pinconf-generic.h
++++ b/include/linux/pinctrl/pinconf-generic.h
+@@ -124,6 +124,8 @@ enum pin_config_param {
+ PIN_CONFIG_SLEW_RATE,
+ PIN_CONFIG_SKEW_DELAY,
+ PIN_CONFIG_PERSIST_STATE,
++ PIN_CONFIG_PASS_THROUGH_ENABLE,
++ PIN_CONFIG_PASS_THROUGH_DISABLE,
+ PIN_CONFIG_END = 0x7F,
+ PIN_CONFIG_MAX = 0xFF,
+ };
+diff --git a/include/uapi/linux/gpio.h b/include/uapi/linux/gpio.h
+index 1bf6e6df084b..384ced158412 100644
+--- a/include/uapi/linux/gpio.h
++++ b/include/uapi/linux/gpio.h
+@@ -62,6 +62,7 @@ struct gpioline_info {
+ #define GPIOHANDLE_REQUEST_ACTIVE_LOW (1UL << 2)
+ #define GPIOHANDLE_REQUEST_OPEN_DRAIN (1UL << 3)
+ #define GPIOHANDLE_REQUEST_OPEN_SOURCE (1UL << 4)
++#define GPIOHANDLE_REQUEST_PASS_THROUGH (1UL << 5)
+
+ /**
+ * struct gpiohandle_request - Information about a GPIO handle request
+--
+2.19.1
+