mirror of
https://github.com/FUjr/gl-infra-builder.git
synced 2025-12-16 09:10:02 +00:00
3999 lines
118 KiB
Diff
3999 lines
118 KiB
Diff
From 189928dc823649579e5cf11f48ed94b5895939b3 Mon Sep 17 00:00:00 2001
|
|
From: Lancer <luoyejiang0701@gmail.com>
|
|
Date: Sat, 10 Apr 2021 17:09:23 +0800
|
|
Subject: [PATCH] =?UTF-8?q?=E6=96=B0GPIO=E9=A9=B1=E5=8A=A8=E4=BB=A5?=
|
|
=?UTF-8?q?=E5=85=BC=E5=AE=B9openthread=E5=AF=B9SPI=E7=9A=84=E6=93=8D?=
|
|
=?UTF-8?q?=E4=BD=9C?=
|
|
MIME-Version: 1.0
|
|
Content-Type: text/plain; charset=UTF-8
|
|
Content-Transfer-Encoding: 8bit
|
|
|
|
---
|
|
.../arch/arm/boot/dts/qcom-ipq4019-ap.dk04.1.dtsi | 3 +-
|
|
qca/src/linux-4.4/drivers/gpio/gpiolib-of.c | 257 ++-
|
|
qca/src/linux-4.4/drivers/gpio/gpiolib-sysfs.c | 56 +-
|
|
qca/src/linux-4.4/drivers/gpio/gpiolib.c | 1855 ++++++++++++++++----
|
|
qca/src/linux-4.4/drivers/gpio/gpiolib.h | 143 +-
|
|
.../linux-4.4/drivers/pinctrl/qcom/pinctrl-msm.c | 2 +-
|
|
.../drivers/pinctrl/qcom/pinctrl-spmi-gpio.c | 2 +-
|
|
.../drivers/pinctrl/qcom/pinctrl-spmi-mpp.c | 2 +-
|
|
qca/src/linux-4.4/include/linux/gpio/driver.h | 129 +-
|
|
qca/src/linux-4.4/include/uapi/linux/Kbuild | 1 +
|
|
qca/src/linux-4.4/include/uapi/linux/gpio.h | 157 ++
|
|
11 files changed, 2107 insertions(+), 500 deletions(-)
|
|
mode change 100644 => 100755 qca/src/linux-4.4/include/uapi/linux/Kbuild
|
|
create mode 100755 qca/src/linux-4.4/include/uapi/linux/gpio.h
|
|
|
|
diff --git a/qca/src/linux-4.4/arch/arm/boot/dts/qcom-ipq4019-ap.dk04.1.dtsi b/qca/src/linux-4.4/arch/arm/boot/dts/qcom-ipq4019-ap.dk04.1.dtsi
|
|
index d71eeb9..1140bd7 100644
|
|
--- a/qca/src/linux-4.4/arch/arm/boot/dts/qcom-ipq4019-ap.dk04.1.dtsi
|
|
+++ b/qca/src/linux-4.4/arch/arm/boot/dts/qcom-ipq4019-ap.dk04.1.dtsi
|
|
@@ -326,7 +326,8 @@
|
|
spi_1: spi@78b6000 { /* BLSP1 QUP2 */
|
|
pinctrl-0 = <&spi_1_pins>;
|
|
pinctrl-names = "default";
|
|
- status = "disabled";
|
|
+ cs-gpios = <&tlmm 45 GPIO_ACTIVE_HIGH>;
|
|
+ status = "ok";
|
|
|
|
spidev1: spi@1 {
|
|
compatible = "qca,spidev";
|
|
diff --git a/qca/src/linux-4.4/drivers/gpio/gpiolib-of.c b/qca/src/linux-4.4/drivers/gpio/gpiolib-of.c
|
|
index 5fe34a9..b863386 100644
|
|
--- a/qca/src/linux-4.4/drivers/gpio/gpiolib-of.c
|
|
+++ b/qca/src/linux-4.4/drivers/gpio/gpiolib-of.c
|
|
@@ -26,38 +26,35 @@
|
|
|
|
#include "gpiolib.h"
|
|
|
|
-/* Private data structure for of_gpiochip_find_and_xlate */
|
|
-struct gg_data {
|
|
- enum of_gpio_flags *flags;
|
|
- struct of_phandle_args gpiospec;
|
|
+static int of_gpiochip_match_node_and_xlate(struct gpio_chip *chip, void *data)
|
|
+{
|
|
+ struct of_phandle_args *gpiospec = data;
|
|
|
|
- struct gpio_desc *out_gpio;
|
|
-};
|
|
+ return chip->gpiodev->dev.of_node == gpiospec->np &&
|
|
+ chip->of_xlate &&
|
|
+ chip->of_xlate(chip, gpiospec, NULL) >= 0;
|
|
+}
|
|
|
|
-/* Private function for resolving node pointer to gpio_chip */
|
|
-static int of_gpiochip_find_and_xlate(struct gpio_chip *gc, void *data)
|
|
+static struct gpio_chip *of_find_gpiochip_by_xlate(
|
|
+ struct of_phandle_args *gpiospec)
|
|
+{
|
|
+ return gpiochip_find(gpiospec, of_gpiochip_match_node_and_xlate);
|
|
+}
|
|
+
|
|
+static struct gpio_desc *of_xlate_and_get_gpiod_flags(struct gpio_chip *chip,
|
|
+ struct of_phandle_args *gpiospec,
|
|
+ enum of_gpio_flags *flags)
|
|
{
|
|
- struct gg_data *gg_data = data;
|
|
int ret;
|
|
|
|
- if ((gc->of_node != gg_data->gpiospec.np) ||
|
|
- (gc->of_gpio_n_cells != gg_data->gpiospec.args_count) ||
|
|
- (!gc->of_xlate))
|
|
- return false;
|
|
-
|
|
- ret = gc->of_xlate(gc, &gg_data->gpiospec, gg_data->flags);
|
|
- if (ret < 0) {
|
|
- /* We've found a gpio chip, but the translation failed.
|
|
- * Store translation error in out_gpio.
|
|
- * Return false to keep looking, as more than one gpio chip
|
|
- * could be registered per of-node.
|
|
- */
|
|
- gg_data->out_gpio = ERR_PTR(ret);
|
|
- return false;
|
|
- }
|
|
-
|
|
- gg_data->out_gpio = gpiochip_get_desc(gc, ret);
|
|
- return true;
|
|
+ if (chip->of_gpio_n_cells != gpiospec->args_count)
|
|
+ return ERR_PTR(-EINVAL);
|
|
+
|
|
+ ret = chip->of_xlate(chip, gpiospec, flags);
|
|
+ if (ret < 0)
|
|
+ return ERR_PTR(ret);
|
|
+
|
|
+ return gpiochip_get_desc(chip, ret);
|
|
}
|
|
|
|
/**
|
|
@@ -74,34 +71,37 @@ static int of_gpiochip_find_and_xlate(struct gpio_chip *gc, void *data)
|
|
struct gpio_desc *of_get_named_gpiod_flags(struct device_node *np,
|
|
const char *propname, int index, enum of_gpio_flags *flags)
|
|
{
|
|
- /* Return -EPROBE_DEFER to support probe() functions to be called
|
|
- * later when the GPIO actually becomes available
|
|
- */
|
|
- struct gg_data gg_data = {
|
|
- .flags = flags,
|
|
- .out_gpio = ERR_PTR(-EPROBE_DEFER)
|
|
- };
|
|
+ struct of_phandle_args gpiospec;
|
|
+ struct gpio_chip *chip;
|
|
+ struct gpio_desc *desc;
|
|
int ret;
|
|
|
|
- /* .of_xlate might decide to not fill in the flags, so clear it. */
|
|
- if (flags)
|
|
- *flags = 0;
|
|
-
|
|
ret = of_parse_phandle_with_args(np, propname, "#gpio-cells", index,
|
|
- &gg_data.gpiospec);
|
|
+ &gpiospec);
|
|
if (ret) {
|
|
pr_debug("%s: can't parse '%s' property of node '%s[%d]'\n",
|
|
__func__, propname, np->full_name, index);
|
|
return ERR_PTR(ret);
|
|
}
|
|
|
|
- gpiochip_find(&gg_data, of_gpiochip_find_and_xlate);
|
|
+ chip = of_find_gpiochip_by_xlate(&gpiospec);
|
|
+ if (!chip) {
|
|
+ desc = ERR_PTR(-EPROBE_DEFER);
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ desc = of_xlate_and_get_gpiod_flags(chip, &gpiospec, flags);
|
|
+ if (IS_ERR(desc))
|
|
+ goto out;
|
|
|
|
- of_node_put(gg_data.gpiospec.np);
|
|
pr_debug("%s: parsed '%s' property of node '%s[%d]' - status (%d)\n",
|
|
__func__, propname, np->full_name, index,
|
|
- PTR_ERR_OR_ZERO(gg_data.out_gpio));
|
|
- return gg_data.out_gpio;
|
|
+ PTR_ERR_OR_ZERO(desc));
|
|
+
|
|
+out:
|
|
+ of_node_put(gpiospec.np);
|
|
+
|
|
+ return desc;
|
|
}
|
|
|
|
int of_get_named_gpio_flags(struct device_node *np, const char *list_name,
|
|
@@ -118,9 +118,49 @@ int of_get_named_gpio_flags(struct device_node *np, const char *list_name,
|
|
}
|
|
EXPORT_SYMBOL(of_get_named_gpio_flags);
|
|
|
|
+struct gpio_desc *of_find_gpio(struct device *dev, const char *con_id,
|
|
+ unsigned int idx,
|
|
+ enum gpio_lookup_flags *flags)
|
|
+{
|
|
+ char prop_name[32]; /* 32 is max size of property name */
|
|
+ enum of_gpio_flags of_flags;
|
|
+ struct gpio_desc *desc;
|
|
+ unsigned int i;
|
|
+
|
|
+ for (i = 0; i < ARRAY_SIZE(gpio_suffixes); i++) {
|
|
+ if (con_id)
|
|
+ snprintf(prop_name, sizeof(prop_name), "%s-%s", con_id,
|
|
+ gpio_suffixes[i]);
|
|
+ else
|
|
+ snprintf(prop_name, sizeof(prop_name), "%s",
|
|
+ gpio_suffixes[i]);
|
|
+
|
|
+ desc = of_get_named_gpiod_flags(dev->of_node, prop_name, idx,
|
|
+ &of_flags);
|
|
+ if (!IS_ERR(desc) || (PTR_ERR(desc) != -ENOENT))
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ if (IS_ERR(desc))
|
|
+ return desc;
|
|
+
|
|
+ if (of_flags & OF_GPIO_ACTIVE_LOW)
|
|
+ *flags |= GPIO_ACTIVE_LOW;
|
|
+
|
|
+ if (of_flags & OF_GPIO_SINGLE_ENDED) {
|
|
+ if (of_flags & OF_GPIO_ACTIVE_LOW)
|
|
+ *flags |= GPIO_OPEN_DRAIN;
|
|
+ else
|
|
+ *flags |= GPIO_OPEN_SOURCE;
|
|
+ }
|
|
+
|
|
+ return desc;
|
|
+}
|
|
+
|
|
/**
|
|
* of_parse_own_gpio() - Get a GPIO hog descriptor, names and flags for GPIO API
|
|
* @np: device node to get GPIO from
|
|
+ * @chip: GPIO chip whose hog is parsed
|
|
* @name: GPIO line name
|
|
* @lflags: gpio_lookup_flags - returned from of_find_gpio() or
|
|
* of_parse_own_gpio()
|
|
@@ -130,19 +170,19 @@ EXPORT_SYMBOL(of_get_named_gpio_flags);
|
|
* value on the error condition.
|
|
*/
|
|
static struct gpio_desc *of_parse_own_gpio(struct device_node *np,
|
|
+ struct gpio_chip *chip,
|
|
const char **name,
|
|
enum gpio_lookup_flags *lflags,
|
|
enum gpiod_flags *dflags)
|
|
{
|
|
struct device_node *chip_np;
|
|
enum of_gpio_flags xlate_flags;
|
|
- struct gg_data gg_data = {
|
|
- .flags = &xlate_flags,
|
|
- };
|
|
+ struct of_phandle_args gpiospec;
|
|
+ struct gpio_desc *desc;
|
|
u32 tmp;
|
|
- int i, ret;
|
|
+ int ret;
|
|
|
|
- chip_np = np->parent;
|
|
+ chip_np = chip->of_node;
|
|
if (!chip_np)
|
|
return ERR_PTR(-EINVAL);
|
|
|
|
@@ -154,25 +194,16 @@ static struct gpio_desc *of_parse_own_gpio(struct device_node *np,
|
|
if (ret)
|
|
return ERR_PTR(ret);
|
|
|
|
- if (tmp > MAX_PHANDLE_ARGS)
|
|
- return ERR_PTR(-EINVAL);
|
|
+ gpiospec.np = chip_np;
|
|
+ gpiospec.args_count = tmp;
|
|
|
|
- gg_data.gpiospec.args_count = tmp;
|
|
- gg_data.gpiospec.np = chip_np;
|
|
- for (i = 0; i < tmp; i++) {
|
|
- ret = of_property_read_u32_index(np, "gpios", i,
|
|
- &gg_data.gpiospec.args[i]);
|
|
- if (ret)
|
|
- return ERR_PTR(ret);
|
|
- }
|
|
+ ret = of_property_read_u32_array(np, "gpios", gpiospec.args, tmp);
|
|
+ if (ret)
|
|
+ return ERR_PTR(ret);
|
|
|
|
- gpiochip_find(&gg_data, of_gpiochip_find_and_xlate);
|
|
- if (!gg_data.out_gpio) {
|
|
- if (np->parent == np)
|
|
- return ERR_PTR(-ENXIO);
|
|
- else
|
|
- return ERR_PTR(-EINVAL);
|
|
- }
|
|
+ desc = of_xlate_and_get_gpiod_flags(chip, &gpiospec, &xlate_flags);
|
|
+ if (IS_ERR(desc))
|
|
+ return desc;
|
|
|
|
if (xlate_flags & OF_GPIO_ACTIVE_LOW)
|
|
*lflags |= GPIO_ACTIVE_LOW;
|
|
@@ -185,14 +216,59 @@ static struct gpio_desc *of_parse_own_gpio(struct device_node *np,
|
|
*dflags |= GPIOD_OUT_HIGH;
|
|
else {
|
|
pr_warn("GPIO line %d (%s): no hogging state specified, bailing out\n",
|
|
- desc_to_gpio(gg_data.out_gpio), np->name);
|
|
+ desc_to_gpio(desc), np->name);
|
|
return ERR_PTR(-EINVAL);
|
|
}
|
|
|
|
if (name && of_property_read_string(np, "line-name", name))
|
|
*name = np->name;
|
|
|
|
- return gg_data.out_gpio;
|
|
+ return desc;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * of_gpiochip_set_names() - set up the names of the lines
|
|
+ * @chip: GPIO chip whose lines should be named, if possible
|
|
+ */
|
|
+static void of_gpiochip_set_names(struct gpio_chip *gc)
|
|
+{
|
|
+ struct gpio_device *gdev = gc->gpiodev;
|
|
+ struct device_node *np = gc->of_node;
|
|
+ int i;
|
|
+ int nstrings;
|
|
+
|
|
+ nstrings = of_property_count_strings(np, "gpio-line-names");
|
|
+ if (nstrings <= 0)
|
|
+ /* Lines names not present */
|
|
+ return;
|
|
+
|
|
+ /* This is normally not what you want */
|
|
+ if (gdev->ngpio != nstrings)
|
|
+ dev_info(&gdev->dev, "gpio-line-names specifies %d line "
|
|
+ "names but there are %d lines on the chip\n",
|
|
+ nstrings, gdev->ngpio);
|
|
+
|
|
+ /*
|
|
+ * Make sure to not index beyond the end of the number of descriptors
|
|
+ * of the GPIO device.
|
|
+ */
|
|
+ for (i = 0; i < gdev->ngpio; i++) {
|
|
+ const char *name;
|
|
+ int ret;
|
|
+
|
|
+ ret = of_property_read_string_index(np,
|
|
+ "gpio-line-names",
|
|
+ i,
|
|
+ &name);
|
|
+ if (ret) {
|
|
+ if (ret != -ENODATA)
|
|
+ dev_err(&gdev->dev,
|
|
+ "unable to name line %d: %d\n",
|
|
+ i, ret);
|
|
+ break;
|
|
+ }
|
|
+ gdev->descs[i].name = name;
|
|
+ }
|
|
}
|
|
|
|
/**
|
|
@@ -201,26 +277,31 @@ static struct gpio_desc *of_parse_own_gpio(struct device_node *np,
|
|
*
|
|
* This is only used by of_gpiochip_add to request/set GPIO initial
|
|
* configuration.
|
|
+ * It retures error if it fails otherwise 0 on success.
|
|
*/
|
|
-static void of_gpiochip_scan_gpios(struct gpio_chip *chip)
|
|
+static int of_gpiochip_scan_gpios(struct gpio_chip *chip)
|
|
{
|
|
struct gpio_desc *desc = NULL;
|
|
struct device_node *np;
|
|
const char *name;
|
|
enum gpio_lookup_flags lflags;
|
|
enum gpiod_flags dflags;
|
|
+ int ret;
|
|
|
|
- for_each_child_of_node(chip->of_node, np) {
|
|
+ for_each_available_child_of_node(chip->of_node, np) {
|
|
if (!of_property_read_bool(np, "gpio-hog"))
|
|
continue;
|
|
|
|
- desc = of_parse_own_gpio(np, &name, &lflags, &dflags);
|
|
+ desc = of_parse_own_gpio(np, chip, &name, &lflags, &dflags);
|
|
if (IS_ERR(desc))
|
|
continue;
|
|
|
|
- if (gpiod_hog(desc, name, lflags, dflags))
|
|
- continue;
|
|
+ ret = gpiod_hog(desc, name, lflags, dflags);
|
|
+ if (ret < 0)
|
|
+ return ret;
|
|
}
|
|
+
|
|
+ return 0;
|
|
}
|
|
|
|
/**
|
|
@@ -262,9 +343,10 @@ int of_gpio_simple_xlate(struct gpio_chip *gc,
|
|
EXPORT_SYMBOL(of_gpio_simple_xlate);
|
|
|
|
/**
|
|
- * of_mm_gpiochip_add - Add memory mapped GPIO chip (bank)
|
|
+ * of_mm_gpiochip_add_data - Add memory mapped GPIO chip (bank)
|
|
* @np: device node of the GPIO chip
|
|
* @mm_gc: pointer to the of_mm_gpio_chip allocated structure
|
|
+ * @data: driver data to store in the struct gpio_chip
|
|
*
|
|
* To use this function you should allocate and fill mm_gc with:
|
|
*
|
|
@@ -280,8 +362,9 @@ EXPORT_SYMBOL(of_gpio_simple_xlate);
|
|
* do all necessary work for you. Then you'll able to use .regs
|
|
* to manage GPIOs from the callbacks.
|
|
*/
|
|
-int of_mm_gpiochip_add(struct device_node *np,
|
|
- struct of_mm_gpio_chip *mm_gc)
|
|
+int of_mm_gpiochip_add_data(struct device_node *np,
|
|
+ struct of_mm_gpio_chip *mm_gc,
|
|
+ void *data)
|
|
{
|
|
int ret = -ENOMEM;
|
|
struct gpio_chip *gc = &mm_gc->gc;
|
|
@@ -301,7 +384,7 @@ int of_mm_gpiochip_add(struct device_node *np,
|
|
|
|
mm_gc->gc.of_node = np;
|
|
|
|
- ret = gpiochip_add(gc);
|
|
+ ret = gpiochip_add_data(gc, data);
|
|
if (ret)
|
|
goto err2;
|
|
|
|
@@ -315,7 +398,7 @@ err0:
|
|
np->full_name, ret);
|
|
return ret;
|
|
}
|
|
-EXPORT_SYMBOL(of_mm_gpiochip_add);
|
|
+EXPORT_SYMBOL(of_mm_gpiochip_add_data);
|
|
|
|
/**
|
|
* of_mm_gpiochip_remove - Remove memory mapped GPIO chip (bank)
|
|
@@ -357,6 +440,7 @@ static int of_gpiochip_add_pin_range(struct gpio_chip *chip)
|
|
break;
|
|
|
|
pctldev = of_pinctrl_get(pinspec.np);
|
|
+ of_node_put(pinspec.np);
|
|
if (!pctldev)
|
|
return -EPROBE_DEFER;
|
|
|
|
@@ -423,8 +507,8 @@ int of_gpiochip_add(struct gpio_chip *chip)
|
|
{
|
|
int status;
|
|
|
|
- if ((!chip->of_node) && (chip->dev))
|
|
- chip->of_node = chip->dev->of_node;
|
|
+ if ((!chip->of_node) && (chip->parent))
|
|
+ chip->of_node = chip->parent->of_node;
|
|
|
|
if (!chip->of_node)
|
|
return 0;
|
|
@@ -434,15 +518,26 @@ int of_gpiochip_add(struct gpio_chip *chip)
|
|
chip->of_xlate = of_gpio_simple_xlate;
|
|
}
|
|
|
|
+ if (chip->of_gpio_n_cells > MAX_PHANDLE_ARGS)
|
|
+ return -EINVAL;
|
|
+
|
|
status = of_gpiochip_add_pin_range(chip);
|
|
if (status)
|
|
return status;
|
|
|
|
+ /* If the chip defines names itself, these take precedence */
|
|
+ if (!chip->names)
|
|
+ of_gpiochip_set_names(chip);
|
|
+
|
|
of_node_get(chip->of_node);
|
|
|
|
- of_gpiochip_scan_gpios(chip);
|
|
+ status = of_gpiochip_scan_gpios(chip);
|
|
+ if (status) {
|
|
+ of_node_put(chip->of_node);
|
|
+ gpiochip_remove_pin_ranges(chip);
|
|
+ }
|
|
|
|
- return 0;
|
|
+ return status;
|
|
}
|
|
|
|
void of_gpiochip_remove(struct gpio_chip *chip)
|
|
diff --git a/qca/src/linux-4.4/drivers/gpio/gpiolib-sysfs.c b/qca/src/linux-4.4/drivers/gpio/gpiolib-sysfs.c
|
|
index b57ed8e..4b44dd9 100644
|
|
--- a/qca/src/linux-4.4/drivers/gpio/gpiolib-sysfs.c
|
|
+++ b/qca/src/linux-4.4/drivers/gpio/gpiolib-sysfs.c
|
|
@@ -180,7 +180,7 @@ static int gpio_sysfs_request_irq(struct device *dev, unsigned char flags)
|
|
* Remove this redundant call (along with the corresponding
|
|
* unlock) when those drivers have been fixed.
|
|
*/
|
|
- ret = gpiochip_lock_as_irq(desc->chip, gpio_chip_hwgpio(desc));
|
|
+ ret = gpiochip_lock_as_irq(desc->gdev->chip, gpio_chip_hwgpio(desc));
|
|
if (ret < 0)
|
|
goto err_put_kn;
|
|
|
|
@@ -194,7 +194,7 @@ static int gpio_sysfs_request_irq(struct device *dev, unsigned char flags)
|
|
return 0;
|
|
|
|
err_unlock:
|
|
- gpiochip_unlock_as_irq(desc->chip, gpio_chip_hwgpio(desc));
|
|
+ gpiochip_unlock_as_irq(desc->gdev->chip, gpio_chip_hwgpio(desc));
|
|
err_put_kn:
|
|
sysfs_put(data->value_kn);
|
|
|
|
@@ -212,7 +212,7 @@ static void gpio_sysfs_free_irq(struct device *dev)
|
|
|
|
data->irq_flags = 0;
|
|
free_irq(data->irq, data);
|
|
- gpiochip_unlock_as_irq(desc->chip, gpio_chip_hwgpio(desc));
|
|
+ gpiochip_unlock_as_irq(desc->gdev->chip, gpio_chip_hwgpio(desc));
|
|
sysfs_put(data->value_kn);
|
|
}
|
|
|
|
@@ -547,6 +547,7 @@ static struct class gpio_class = {
|
|
int gpiod_export(struct gpio_desc *desc, bool direction_may_change)
|
|
{
|
|
struct gpio_chip *chip;
|
|
+ struct gpio_device *gdev;
|
|
struct gpiod_data *data;
|
|
unsigned long flags;
|
|
int status;
|
|
@@ -565,12 +566,13 @@ int gpiod_export(struct gpio_desc *desc, bool direction_may_change)
|
|
return -EINVAL;
|
|
}
|
|
|
|
- chip = desc->chip;
|
|
+ gdev = desc->gdev;
|
|
+ chip = gdev->chip;
|
|
|
|
mutex_lock(&sysfs_lock);
|
|
|
|
/* check if chip is being removed */
|
|
- if (!chip || !chip->cdev) {
|
|
+ if (!chip || !gdev->mockdev) {
|
|
status = -ENODEV;
|
|
goto err_unlock;
|
|
}
|
|
@@ -605,7 +607,7 @@ int gpiod_export(struct gpio_desc *desc, bool direction_may_change)
|
|
if (chip->names && chip->names[offset])
|
|
ioname = chip->names[offset];
|
|
|
|
- dev = device_create_with_groups(&gpio_class, chip->dev,
|
|
+ dev = device_create_with_groups(&gpio_class, &gdev->dev,
|
|
MKDEV(0, 0), data, gpio_groups,
|
|
ioname ? ioname : "gpio%u",
|
|
desc_to_gpio(desc));
|
|
@@ -668,10 +670,10 @@ int gpiod_export_link(struct device *dev, const char *name,
|
|
EXPORT_SYMBOL_GPL(gpiod_export_link);
|
|
|
|
/**
|
|
- * gpiod_unexport - reverse effect of gpio_export()
|
|
+ * gpiod_unexport - reverse effect of gpiod_export()
|
|
* @gpio: gpio to make unavailable
|
|
*
|
|
- * This is implicit on gpio_free().
|
|
+ * This is implicit on gpiod_free().
|
|
*/
|
|
void gpiod_unexport(struct gpio_desc *desc)
|
|
{
|
|
@@ -716,9 +718,11 @@ err_unlock:
|
|
}
|
|
EXPORT_SYMBOL_GPL(gpiod_unexport);
|
|
|
|
-int gpiochip_sysfs_register(struct gpio_chip *chip)
|
|
+int gpiochip_sysfs_register(struct gpio_device *gdev)
|
|
{
|
|
struct device *dev;
|
|
+ struct device *parent;
|
|
+ struct gpio_chip *chip = gdev->chip;
|
|
|
|
/*
|
|
* Many systems add gpio chips for SOC support very early,
|
|
@@ -729,38 +733,49 @@ int gpiochip_sysfs_register(struct gpio_chip *chip)
|
|
if (!gpio_class.p)
|
|
return 0;
|
|
|
|
+ /*
|
|
+ * For sysfs backward compatibility we need to preserve this
|
|
+ * preferred parenting to the gpio_chip parent field, if set.
|
|
+ */
|
|
+ if (chip->parent)
|
|
+ parent = chip->parent;
|
|
+ else
|
|
+ parent = &gdev->dev;
|
|
+
|
|
/* use chip->base for the ID; it's already known to be unique */
|
|
- dev = device_create_with_groups(&gpio_class, chip->dev, MKDEV(0, 0),
|
|
+ dev = device_create_with_groups(&gpio_class, parent,
|
|
+ MKDEV(0, 0),
|
|
chip, gpiochip_groups,
|
|
"gpiochip%d", chip->base);
|
|
if (IS_ERR(dev))
|
|
return PTR_ERR(dev);
|
|
|
|
mutex_lock(&sysfs_lock);
|
|
- chip->cdev = dev;
|
|
+ gdev->mockdev = dev;
|
|
mutex_unlock(&sysfs_lock);
|
|
|
|
return 0;
|
|
}
|
|
|
|
-void gpiochip_sysfs_unregister(struct gpio_chip *chip)
|
|
+void gpiochip_sysfs_unregister(struct gpio_device *gdev)
|
|
{
|
|
struct gpio_desc *desc;
|
|
+ struct gpio_chip *chip = gdev->chip;
|
|
unsigned int i;
|
|
|
|
- if (!chip->cdev)
|
|
+ if (!gdev->mockdev)
|
|
return;
|
|
|
|
- device_unregister(chip->cdev);
|
|
+ device_unregister(gdev->mockdev);
|
|
|
|
/* prevent further gpiod exports */
|
|
mutex_lock(&sysfs_lock);
|
|
- chip->cdev = NULL;
|
|
+ gdev->mockdev = NULL;
|
|
mutex_unlock(&sysfs_lock);
|
|
|
|
/* unregister gpiod class devices owned by sysfs */
|
|
for (i = 0; i < chip->ngpio; i++) {
|
|
- desc = &chip->desc[i];
|
|
+ desc = &gdev->descs[i];
|
|
if (test_and_clear_bit(FLAG_SYSFS, &desc->flags))
|
|
gpiod_free(desc);
|
|
}
|
|
@@ -770,7 +785,7 @@ static int __init gpiolib_sysfs_init(void)
|
|
{
|
|
int status;
|
|
unsigned long flags;
|
|
- struct gpio_chip *chip;
|
|
+ struct gpio_device *gdev;
|
|
|
|
status = class_register(&gpio_class);
|
|
if (status < 0)
|
|
@@ -783,8 +798,8 @@ static int __init gpiolib_sysfs_init(void)
|
|
* registered, and so arch_initcall() can always gpio_export().
|
|
*/
|
|
spin_lock_irqsave(&gpio_lock, flags);
|
|
- list_for_each_entry(chip, &gpio_chips, list) {
|
|
- if (chip->cdev)
|
|
+ list_for_each_entry(gdev, &gpio_devices, list) {
|
|
+ if (gdev->mockdev)
|
|
continue;
|
|
|
|
/*
|
|
@@ -797,12 +812,11 @@ static int __init gpiolib_sysfs_init(void)
|
|
* gpio_lock prevents us from doing this.
|
|
*/
|
|
spin_unlock_irqrestore(&gpio_lock, flags);
|
|
- status = gpiochip_sysfs_register(chip);
|
|
+ status = gpiochip_sysfs_register(gdev);
|
|
spin_lock_irqsave(&gpio_lock, flags);
|
|
}
|
|
spin_unlock_irqrestore(&gpio_lock, flags);
|
|
|
|
-
|
|
return status;
|
|
}
|
|
postcore_initcall(gpiolib_sysfs_init);
|
|
diff --git a/qca/src/linux-4.4/drivers/gpio/gpiolib.c b/qca/src/linux-4.4/drivers/gpio/gpiolib.c
|
|
index 06d345b..11b6846 100644
|
|
--- a/qca/src/linux-4.4/drivers/gpio/gpiolib.c
|
|
+++ b/qca/src/linux-4.4/drivers/gpio/gpiolib.c
|
|
@@ -16,6 +16,16 @@
|
|
#include <linux/gpio/driver.h>
|
|
#include <linux/gpio/machine.h>
|
|
#include <linux/pinctrl/consumer.h>
|
|
+#include <linux/cdev.h>
|
|
+#include <linux/fs.h>
|
|
+#include <linux/uaccess.h>
|
|
+#include <linux/compat.h>
|
|
+#include <linux/anon_inodes.h>
|
|
+#include <linux/file.h>
|
|
+#include <linux/kfifo.h>
|
|
+#include <linux/poll.h>
|
|
+#include <linux/timekeeping.h>
|
|
+#include <uapi/linux/gpio.h>
|
|
|
|
#include "gpiolib.h"
|
|
|
|
@@ -42,6 +52,14 @@
|
|
#define extra_checks 0
|
|
#endif
|
|
|
|
+/* Device and char device-related information */
|
|
+static DEFINE_IDA(gpio_ida);
|
|
+static dev_t gpio_devt;
|
|
+#define GPIO_DEV_MAX 256 /* 256 GPIO chip devices supported */
|
|
+static struct bus_type gpio_bus_type = {
|
|
+ .name = "gpio",
|
|
+};
|
|
+
|
|
/* gpio_lock prevents conflicts during gpio_desc[] table updates.
|
|
* While any GPIO is requested, its gpio_chip is not removable;
|
|
* each GPIO's "requested" flag serves as a lock and refcount.
|
|
@@ -50,12 +68,14 @@ DEFINE_SPINLOCK(gpio_lock);
|
|
|
|
static DEFINE_MUTEX(gpio_lookup_lock);
|
|
static LIST_HEAD(gpio_lookup_list);
|
|
-LIST_HEAD(gpio_chips);
|
|
-
|
|
+LIST_HEAD(gpio_devices);
|
|
|
|
static void gpiochip_free_hogs(struct gpio_chip *chip);
|
|
static void gpiochip_irqchip_remove(struct gpio_chip *gpiochip);
|
|
+static int gpiochip_irqchip_init_valid_mask(struct gpio_chip *gpiochip);
|
|
+static void gpiochip_irqchip_free_valid_mask(struct gpio_chip *gpiochip);
|
|
|
|
+static bool gpiolib_initialized;
|
|
|
|
static inline void desc_set_label(struct gpio_desc *d, const char *label)
|
|
{
|
|
@@ -67,15 +87,16 @@ static inline void desc_set_label(struct gpio_desc *d, const char *label)
|
|
*/
|
|
struct gpio_desc *gpio_to_desc(unsigned gpio)
|
|
{
|
|
- struct gpio_chip *chip;
|
|
+ struct gpio_device *gdev;
|
|
unsigned long flags;
|
|
|
|
spin_lock_irqsave(&gpio_lock, flags);
|
|
|
|
- list_for_each_entry(chip, &gpio_chips, list) {
|
|
- if (chip->base <= gpio && chip->base + chip->ngpio > gpio) {
|
|
+ list_for_each_entry(gdev, &gpio_devices, list) {
|
|
+ if (gdev->base <= gpio &&
|
|
+ gdev->base + gdev->ngpio > gpio) {
|
|
spin_unlock_irqrestore(&gpio_lock, flags);
|
|
- return &chip->desc[gpio - chip->base];
|
|
+ return &gdev->descs[gpio - gdev->base];
|
|
}
|
|
}
|
|
|
|
@@ -94,10 +115,12 @@ EXPORT_SYMBOL_GPL(gpio_to_desc);
|
|
struct gpio_desc *gpiochip_get_desc(struct gpio_chip *chip,
|
|
u16 hwnum)
|
|
{
|
|
- if (hwnum >= chip->ngpio)
|
|
+ struct gpio_device *gdev = chip->gpiodev;
|
|
+
|
|
+ if (hwnum >= gdev->ngpio)
|
|
return ERR_PTR(-EINVAL);
|
|
|
|
- return &chip->desc[hwnum];
|
|
+ return &gdev->descs[hwnum];
|
|
}
|
|
|
|
/**
|
|
@@ -107,7 +130,7 @@ struct gpio_desc *gpiochip_get_desc(struct gpio_chip *chip,
|
|
*/
|
|
int desc_to_gpio(const struct gpio_desc *desc)
|
|
{
|
|
- return desc->chip->base + (desc - &desc->chip->desc[0]);
|
|
+ return desc->gdev->base + (desc - &desc->gdev->descs[0]);
|
|
}
|
|
EXPORT_SYMBOL_GPL(desc_to_gpio);
|
|
|
|
@@ -118,23 +141,25 @@ EXPORT_SYMBOL_GPL(desc_to_gpio);
|
|
*/
|
|
struct gpio_chip *gpiod_to_chip(const struct gpio_desc *desc)
|
|
{
|
|
- return desc ? desc->chip : NULL;
|
|
+ if (!desc || !desc->gdev || !desc->gdev->chip)
|
|
+ return NULL;
|
|
+ return desc->gdev->chip;
|
|
}
|
|
EXPORT_SYMBOL_GPL(gpiod_to_chip);
|
|
|
|
/* dynamic allocation of GPIOs, e.g. on a hotplugged device */
|
|
static int gpiochip_find_base(int ngpio)
|
|
{
|
|
- struct gpio_chip *chip;
|
|
+ struct gpio_device *gdev;
|
|
int base = ARCH_NR_GPIOS - ngpio;
|
|
|
|
- list_for_each_entry_reverse(chip, &gpio_chips, list) {
|
|
+ list_for_each_entry_reverse(gdev, &gpio_devices, list) {
|
|
/* found a free space? */
|
|
- if (chip->base + chip->ngpio <= base)
|
|
+ if (gdev->base + gdev->ngpio <= base)
|
|
break;
|
|
else
|
|
/* nope, check the space right before the chip */
|
|
- base = chip->base - ngpio;
|
|
+ base = gdev->base - ngpio;
|
|
}
|
|
|
|
if (gpio_is_valid(base)) {
|
|
@@ -182,39 +207,50 @@ EXPORT_SYMBOL_GPL(gpiod_get_direction);
|
|
|
|
/*
|
|
* Add a new chip to the global chips list, keeping the list of chips sorted
|
|
- * by base order.
|
|
+ * by range(means [base, base + ngpio - 1]) order.
|
|
*
|
|
* Return -EBUSY if the new chip overlaps with some other chip's integer
|
|
* space.
|
|
*/
|
|
-static int gpiochip_add_to_list(struct gpio_chip *chip)
|
|
+static int gpiodev_add_to_list(struct gpio_device *gdev)
|
|
{
|
|
- struct list_head *pos;
|
|
- struct gpio_chip *_chip;
|
|
- int err = 0;
|
|
+ struct gpio_device *prev, *next;
|
|
|
|
- /* find where to insert our chip */
|
|
- list_for_each(pos, &gpio_chips) {
|
|
- _chip = list_entry(pos, struct gpio_chip, list);
|
|
- /* shall we insert before _chip? */
|
|
- if (_chip->base >= chip->base + chip->ngpio)
|
|
- break;
|
|
+ if (list_empty(&gpio_devices)) {
|
|
+ /* initial entry in list */
|
|
+ list_add_tail(&gdev->list, &gpio_devices);
|
|
+ return 0;
|
|
}
|
|
|
|
- /* are we stepping on the chip right before? */
|
|
- if (pos != &gpio_chips && pos->prev != &gpio_chips) {
|
|
- _chip = list_entry(pos->prev, struct gpio_chip, list);
|
|
- if (_chip->base + _chip->ngpio > chip->base) {
|
|
- dev_err(chip->dev,
|
|
- "GPIO integer space overlap, cannot add chip\n");
|
|
- err = -EBUSY;
|
|
- }
|
|
+ next = list_entry(gpio_devices.next, struct gpio_device, list);
|
|
+ if (gdev->base + gdev->ngpio <= next->base) {
|
|
+ /* add before first entry */
|
|
+ list_add(&gdev->list, &gpio_devices);
|
|
+ return 0;
|
|
}
|
|
|
|
- if (!err)
|
|
- list_add_tail(&chip->list, pos);
|
|
+ prev = list_entry(gpio_devices.prev, struct gpio_device, list);
|
|
+ if (prev->base + prev->ngpio <= gdev->base) {
|
|
+ /* add behind last entry */
|
|
+ list_add_tail(&gdev->list, &gpio_devices);
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ list_for_each_entry_safe(prev, next, &gpio_devices, list) {
|
|
+ /* at the end of the list */
|
|
+ if (&next->list == &gpio_devices)
|
|
+ break;
|
|
+
|
|
+ /* add between prev and next */
|
|
+ if (prev->base + prev->ngpio <= gdev->base
|
|
+ && gdev->base + gdev->ngpio <= next->base) {
|
|
+ list_add(&gdev->list, &prev->list);
|
|
+ return 0;
|
|
+ }
|
|
+ }
|
|
|
|
- return err;
|
|
+ dev_err(&gdev->dev, "GPIO integer space overlap, cannot add chip\n");
|
|
+ return -EBUSY;
|
|
}
|
|
|
|
/**
|
|
@@ -222,23 +258,23 @@ static int gpiochip_add_to_list(struct gpio_chip *chip)
|
|
*/
|
|
static struct gpio_desc *gpio_name_to_desc(const char * const name)
|
|
{
|
|
- struct gpio_chip *chip;
|
|
+ struct gpio_device *gdev;
|
|
unsigned long flags;
|
|
|
|
spin_lock_irqsave(&gpio_lock, flags);
|
|
|
|
- list_for_each_entry(chip, &gpio_chips, list) {
|
|
+ list_for_each_entry(gdev, &gpio_devices, list) {
|
|
int i;
|
|
|
|
- for (i = 0; i != chip->ngpio; ++i) {
|
|
- struct gpio_desc *gpio = &chip->desc[i];
|
|
+ for (i = 0; i != gdev->ngpio; ++i) {
|
|
+ struct gpio_desc *desc = &gdev->descs[i];
|
|
|
|
- if (!gpio->name || !name)
|
|
+ if (!desc->name || !name)
|
|
continue;
|
|
|
|
- if (!strcmp(gpio->name, name)) {
|
|
+ if (!strcmp(desc->name, name)) {
|
|
spin_unlock_irqrestore(&gpio_lock, flags);
|
|
- return gpio;
|
|
+ return desc;
|
|
}
|
|
}
|
|
}
|
|
@@ -252,10 +288,11 @@ static struct gpio_desc *gpio_name_to_desc(const char * const name)
|
|
* Takes the names from gc->names and checks if they are all unique. If they
|
|
* are, they are assigned to their gpio descriptors.
|
|
*
|
|
- * Returns -EEXIST if one of the names is already used for a different GPIO.
|
|
+ * Warning if one of the names is already used for a different GPIO.
|
|
*/
|
|
static int gpiochip_set_desc_names(struct gpio_chip *gc)
|
|
{
|
|
+ struct gpio_device *gdev = gc->gpiodev;
|
|
int i;
|
|
|
|
if (!gc->names)
|
|
@@ -267,20 +304,801 @@ static int gpiochip_set_desc_names(struct gpio_chip *gc)
|
|
|
|
gpio = gpio_name_to_desc(gc->names[i]);
|
|
if (gpio)
|
|
- dev_warn(gc->dev, "Detected name collision for "
|
|
- "GPIO name '%s'\n",
|
|
+ dev_warn(&gdev->dev,
|
|
+ "Detected name collision for GPIO name '%s'\n",
|
|
gc->names[i]);
|
|
}
|
|
|
|
/* Then add all names to the GPIO descriptors */
|
|
for (i = 0; i != gc->ngpio; ++i)
|
|
- gc->desc[i].name = gc->names[i];
|
|
+ gdev->descs[i].name = gc->names[i];
|
|
|
|
return 0;
|
|
}
|
|
|
|
+/*
|
|
+ * GPIO line handle management
|
|
+ */
|
|
+
|
|
/**
|
|
- * gpiochip_add() - register a gpio_chip
|
|
+ * struct linehandle_state - contains the state of a userspace handle
|
|
+ * @gdev: the GPIO device the handle pertains to
|
|
+ * @label: consumer label used to tag descriptors
|
|
+ * @descs: the GPIO descriptors held by this handle
|
|
+ * @numdescs: the number of descriptors held in the descs array
|
|
+ */
|
|
+struct linehandle_state {
|
|
+ struct gpio_device *gdev;
|
|
+ const char *label;
|
|
+ struct gpio_desc *descs[GPIOHANDLES_MAX];
|
|
+ u32 numdescs;
|
|
+};
|
|
+
|
|
+#define GPIOHANDLE_REQUEST_VALID_FLAGS \
|
|
+ (GPIOHANDLE_REQUEST_INPUT | \
|
|
+ GPIOHANDLE_REQUEST_OUTPUT | \
|
|
+ GPIOHANDLE_REQUEST_ACTIVE_LOW | \
|
|
+ GPIOHANDLE_REQUEST_OPEN_DRAIN | \
|
|
+ GPIOHANDLE_REQUEST_OPEN_SOURCE)
|
|
+
|
|
+static long linehandle_ioctl(struct file *filep, unsigned int cmd,
|
|
+ unsigned long arg)
|
|
+{
|
|
+ struct linehandle_state *lh = filep->private_data;
|
|
+ void __user *ip = (void __user *)arg;
|
|
+ struct gpiohandle_data ghd;
|
|
+ int i;
|
|
+
|
|
+ if (cmd == GPIOHANDLE_GET_LINE_VALUES_IOCTL) {
|
|
+ int val;
|
|
+
|
|
+ memset(&ghd, 0, sizeof(ghd));
|
|
+
|
|
+ /* TODO: check if descriptors are really input */
|
|
+ for (i = 0; i < lh->numdescs; i++) {
|
|
+ val = gpiod_get_value_cansleep(lh->descs[i]);
|
|
+ if (val < 0)
|
|
+ return val;
|
|
+ ghd.values[i] = val;
|
|
+ }
|
|
+
|
|
+ if (copy_to_user(ip, &ghd, sizeof(ghd)))
|
|
+ return -EFAULT;
|
|
+
|
|
+ return 0;
|
|
+ } else if (cmd == GPIOHANDLE_SET_LINE_VALUES_IOCTL) {
|
|
+ int vals[GPIOHANDLES_MAX];
|
|
+
|
|
+ /* TODO: check if descriptors are really output */
|
|
+ if (copy_from_user(&ghd, ip, sizeof(ghd)))
|
|
+ return -EFAULT;
|
|
+
|
|
+ /* Clamp all values to [0,1] */
|
|
+ for (i = 0; i < lh->numdescs; i++)
|
|
+ vals[i] = !!ghd.values[i];
|
|
+
|
|
+ /* Reuse the array setting function */
|
|
+ gpiod_set_array_value_complex(false,
|
|
+ true,
|
|
+ lh->numdescs,
|
|
+ lh->descs,
|
|
+ vals);
|
|
+ return 0;
|
|
+ }
|
|
+ return -EINVAL;
|
|
+}
|
|
+
|
|
+#ifdef CONFIG_COMPAT
|
|
+static long linehandle_ioctl_compat(struct file *filep, unsigned int cmd,
|
|
+ unsigned long arg)
|
|
+{
|
|
+ return linehandle_ioctl(filep, cmd, (unsigned long)compat_ptr(arg));
|
|
+}
|
|
+#endif
|
|
+
|
|
+static int linehandle_release(struct inode *inode, struct file *filep)
|
|
+{
|
|
+ struct linehandle_state *lh = filep->private_data;
|
|
+ struct gpio_device *gdev = lh->gdev;
|
|
+ int i;
|
|
+
|
|
+ for (i = 0; i < lh->numdescs; i++)
|
|
+ gpiod_free(lh->descs[i]);
|
|
+ kfree(lh->label);
|
|
+ kfree(lh);
|
|
+ put_device(&gdev->dev);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static const struct file_operations linehandle_fileops = {
|
|
+ .release = linehandle_release,
|
|
+ .owner = THIS_MODULE,
|
|
+ .llseek = noop_llseek,
|
|
+ .unlocked_ioctl = linehandle_ioctl,
|
|
+#ifdef CONFIG_COMPAT
|
|
+ .compat_ioctl = linehandle_ioctl_compat,
|
|
+#endif
|
|
+};
|
|
+
|
|
+static int linehandle_create(struct gpio_device *gdev, void __user *ip)
|
|
+{
|
|
+ struct gpiohandle_request handlereq;
|
|
+ struct linehandle_state *lh;
|
|
+ struct file *file;
|
|
+ int fd, i, count = 0, ret;
|
|
+ u32 lflags;
|
|
+
|
|
+ if (copy_from_user(&handlereq, ip, sizeof(handlereq)))
|
|
+ return -EFAULT;
|
|
+ if ((handlereq.lines == 0) || (handlereq.lines > GPIOHANDLES_MAX))
|
|
+ return -EINVAL;
|
|
+
|
|
+ lflags = handlereq.flags;
|
|
+
|
|
+ /*
|
|
+ * Do not allow both INPUT & OUTPUT flags to be set as they are
|
|
+ * contradictory.
|
|
+ */
|
|
+ if ((lflags & GPIOHANDLE_REQUEST_INPUT) &&
|
|
+ (lflags & GPIOHANDLE_REQUEST_OUTPUT))
|
|
+ return -EINVAL;
|
|
+
|
|
+ lh = kzalloc(sizeof(*lh), GFP_KERNEL);
|
|
+ if (!lh)
|
|
+ return -ENOMEM;
|
|
+ lh->gdev = gdev;
|
|
+ get_device(&gdev->dev);
|
|
+
|
|
+ /* Make sure this is terminated */
|
|
+ handlereq.consumer_label[sizeof(handlereq.consumer_label)-1] = '\0';
|
|
+ if (strlen(handlereq.consumer_label)) {
|
|
+ lh->label = kstrdup(handlereq.consumer_label,
|
|
+ GFP_KERNEL);
|
|
+ if (!lh->label) {
|
|
+ ret = -ENOMEM;
|
|
+ goto out_free_lh;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* Request each GPIO */
|
|
+ for (i = 0; i < handlereq.lines; i++) {
|
|
+ u32 offset = handlereq.lineoffsets[i];
|
|
+ struct gpio_desc *desc;
|
|
+
|
|
+ if (offset >= gdev->ngpio) {
|
|
+ ret = -EINVAL;
|
|
+ goto out_free_descs;
|
|
+ }
|
|
+
|
|
+ /* Return an error if a unknown flag is set */
|
|
+ if (lflags & ~GPIOHANDLE_REQUEST_VALID_FLAGS) {
|
|
+ ret = -EINVAL;
|
|
+ goto out_free_descs;
|
|
+ }
|
|
+
|
|
+ desc = &gdev->descs[offset];
|
|
+ ret = gpiod_request(desc, lh->label);
|
|
+ if (ret)
|
|
+ goto out_free_descs;
|
|
+ lh->descs[i] = desc;
|
|
+ count = i + 1;
|
|
+
|
|
+ if (lflags & GPIOHANDLE_REQUEST_ACTIVE_LOW)
|
|
+ set_bit(FLAG_ACTIVE_LOW, &desc->flags);
|
|
+ if (lflags & GPIOHANDLE_REQUEST_OPEN_DRAIN)
|
|
+ set_bit(FLAG_OPEN_DRAIN, &desc->flags);
|
|
+ if (lflags & GPIOHANDLE_REQUEST_OPEN_SOURCE)
|
|
+ set_bit(FLAG_OPEN_SOURCE, &desc->flags);
|
|
+
|
|
+ /*
|
|
+ * Lines have to be requested explicitly for input
|
|
+ * or output, else the line will be treated "as is".
|
|
+ */
|
|
+ if (lflags & GPIOHANDLE_REQUEST_OUTPUT) {
|
|
+ int val = !!handlereq.default_values[i];
|
|
+
|
|
+ ret = gpiod_direction_output(desc, val);
|
|
+ if (ret)
|
|
+ goto out_free_descs;
|
|
+ } else if (lflags & GPIOHANDLE_REQUEST_INPUT) {
|
|
+ ret = gpiod_direction_input(desc);
|
|
+ if (ret)
|
|
+ goto out_free_descs;
|
|
+ }
|
|
+ dev_dbg(&gdev->dev, "registered chardev handle for line %d\n",
|
|
+ offset);
|
|
+ }
|
|
+ /* Let i point at the last handle */
|
|
+ i--;
|
|
+ lh->numdescs = handlereq.lines;
|
|
+
|
|
+ fd = get_unused_fd_flags(O_RDONLY | O_CLOEXEC);
|
|
+ if (fd < 0) {
|
|
+ ret = fd;
|
|
+ goto out_free_descs;
|
|
+ }
|
|
+
|
|
+ file = anon_inode_getfile("gpio-linehandle",
|
|
+ &linehandle_fileops,
|
|
+ lh,
|
|
+ O_RDONLY | O_CLOEXEC);
|
|
+ if (IS_ERR(file)) {
|
|
+ ret = PTR_ERR(file);
|
|
+ goto out_put_unused_fd;
|
|
+ }
|
|
+
|
|
+ handlereq.fd = fd;
|
|
+ if (copy_to_user(ip, &handlereq, sizeof(handlereq))) {
|
|
+ /*
|
|
+ * fput() will trigger the release() callback, so do not go onto
|
|
+ * the regular error cleanup path here.
|
|
+ */
|
|
+ fput(file);
|
|
+ put_unused_fd(fd);
|
|
+ return -EFAULT;
|
|
+ }
|
|
+
|
|
+ fd_install(fd, file);
|
|
+
|
|
+ dev_dbg(&gdev->dev, "registered chardev handle for %d lines\n",
|
|
+ lh->numdescs);
|
|
+
|
|
+ return 0;
|
|
+
|
|
+out_put_unused_fd:
|
|
+ put_unused_fd(fd);
|
|
+out_free_descs:
|
|
+ for (i = 0; i < count; i++)
|
|
+ gpiod_free(lh->descs[i]);
|
|
+ kfree(lh->label);
|
|
+out_free_lh:
|
|
+ kfree(lh);
|
|
+ put_device(&gdev->dev);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * GPIO line event management
|
|
+ */
|
|
+
|
|
+/**
|
|
+ * struct lineevent_state - contains the state of a userspace event
|
|
+ * @gdev: the GPIO device the event pertains to
|
|
+ * @label: consumer label used to tag descriptors
|
|
+ * @desc: the GPIO descriptor held by this event
|
|
+ * @eflags: the event flags this line was requested with
|
|
+ * @irq: the interrupt that trigger in response to events on this GPIO
|
|
+ * @wait: wait queue that handles blocking reads of events
|
|
+ * @events: KFIFO for the GPIO events
|
|
+ * @read_lock: mutex lock to protect reads from colliding with adding
|
|
+ * new events to the FIFO
|
|
+ */
|
|
+struct lineevent_state {
|
|
+ struct gpio_device *gdev;
|
|
+ const char *label;
|
|
+ struct gpio_desc *desc;
|
|
+ u32 eflags;
|
|
+ int irq;
|
|
+ wait_queue_head_t wait;
|
|
+ DECLARE_KFIFO(events, struct gpioevent_data, 16);
|
|
+ struct mutex read_lock;
|
|
+};
|
|
+
|
|
+#define GPIOEVENT_REQUEST_VALID_FLAGS \
|
|
+ (GPIOEVENT_REQUEST_RISING_EDGE | \
|
|
+ GPIOEVENT_REQUEST_FALLING_EDGE)
|
|
+
|
|
+static unsigned int lineevent_poll(struct file *filep,
|
|
+ struct poll_table_struct *wait)
|
|
+{
|
|
+ struct lineevent_state *le = filep->private_data;
|
|
+ unsigned int events = 0;
|
|
+
|
|
+ poll_wait(filep, &le->wait, wait);
|
|
+
|
|
+ if (!kfifo_is_empty(&le->events))
|
|
+ events = POLLIN | POLLRDNORM;
|
|
+
|
|
+ return events;
|
|
+}
|
|
+
|
|
+
|
|
+static ssize_t lineevent_read(struct file *filep,
|
|
+ char __user *buf,
|
|
+ size_t count,
|
|
+ loff_t *f_ps)
|
|
+{
|
|
+ struct lineevent_state *le = filep->private_data;
|
|
+ unsigned int copied;
|
|
+ int ret;
|
|
+
|
|
+ if (count < sizeof(struct gpioevent_data))
|
|
+ return -EINVAL;
|
|
+
|
|
+ do {
|
|
+ if (kfifo_is_empty(&le->events)) {
|
|
+ if (filep->f_flags & O_NONBLOCK)
|
|
+ return -EAGAIN;
|
|
+
|
|
+ ret = wait_event_interruptible(le->wait,
|
|
+ !kfifo_is_empty(&le->events));
|
|
+ if (ret)
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ if (mutex_lock_interruptible(&le->read_lock))
|
|
+ return -ERESTARTSYS;
|
|
+ ret = kfifo_to_user(&le->events, buf, count, &copied);
|
|
+ mutex_unlock(&le->read_lock);
|
|
+
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ /*
|
|
+ * If we couldn't read anything from the fifo (a different
|
|
+ * thread might have been faster) we either return -EAGAIN if
|
|
+ * the file descriptor is non-blocking, otherwise we go back to
|
|
+ * sleep and wait for more data to arrive.
|
|
+ */
|
|
+ if (copied == 0 && (filep->f_flags & O_NONBLOCK))
|
|
+ return -EAGAIN;
|
|
+
|
|
+ } while (copied == 0);
|
|
+
|
|
+ return copied;
|
|
+}
|
|
+
|
|
+static int lineevent_release(struct inode *inode, struct file *filep)
|
|
+{
|
|
+ struct lineevent_state *le = filep->private_data;
|
|
+ struct gpio_device *gdev = le->gdev;
|
|
+
|
|
+ free_irq(le->irq, le);
|
|
+ gpiod_free(le->desc);
|
|
+ kfree(le->label);
|
|
+ kfree(le);
|
|
+ put_device(&gdev->dev);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static long lineevent_ioctl(struct file *filep, unsigned int cmd,
|
|
+ unsigned long arg)
|
|
+{
|
|
+ struct lineevent_state *le = filep->private_data;
|
|
+ void __user *ip = (void __user *)arg;
|
|
+ struct gpiohandle_data ghd;
|
|
+
|
|
+ /*
|
|
+ * We can get the value for an event line but not set it,
|
|
+ * because it is input by definition.
|
|
+ */
|
|
+ if (cmd == GPIOHANDLE_GET_LINE_VALUES_IOCTL) {
|
|
+ int val;
|
|
+
|
|
+ memset(&ghd, 0, sizeof(ghd));
|
|
+
|
|
+ val = gpiod_get_value_cansleep(le->desc);
|
|
+ if (val < 0)
|
|
+ return val;
|
|
+ ghd.values[0] = val;
|
|
+
|
|
+ if (copy_to_user(ip, &ghd, sizeof(ghd)))
|
|
+ return -EFAULT;
|
|
+
|
|
+ return 0;
|
|
+ }
|
|
+ return -EINVAL;
|
|
+}
|
|
+
|
|
+#ifdef CONFIG_COMPAT
|
|
+static long lineevent_ioctl_compat(struct file *filep, unsigned int cmd,
|
|
+ unsigned long arg)
|
|
+{
|
|
+ return lineevent_ioctl(filep, cmd, (unsigned long)compat_ptr(arg));
|
|
+}
|
|
+#endif
|
|
+
|
|
+static const struct file_operations lineevent_fileops = {
|
|
+ .release = lineevent_release,
|
|
+ .read = lineevent_read,
|
|
+ .poll = lineevent_poll,
|
|
+ .owner = THIS_MODULE,
|
|
+ .llseek = noop_llseek,
|
|
+ .unlocked_ioctl = lineevent_ioctl,
|
|
+#ifdef CONFIG_COMPAT
|
|
+ .compat_ioctl = lineevent_ioctl_compat,
|
|
+#endif
|
|
+};
|
|
+
|
|
+static irqreturn_t lineevent_irq_thread(int irq, void *p)
|
|
+{
|
|
+ struct lineevent_state *le = p;
|
|
+ struct gpioevent_data ge;
|
|
+ int ret, level;
|
|
+
|
|
+ /* Do not leak kernel stack to userspace */
|
|
+ memset(&ge, 0, sizeof(ge));
|
|
+
|
|
+ ge.timestamp = ktime_get_real_ns();
|
|
+ level = gpiod_get_value_cansleep(le->desc);
|
|
+
|
|
+ if (le->eflags & GPIOEVENT_REQUEST_RISING_EDGE
|
|
+ && le->eflags & GPIOEVENT_REQUEST_FALLING_EDGE) {
|
|
+ if (level)
|
|
+ /* Emit low-to-high event */
|
|
+ ge.id = GPIOEVENT_EVENT_RISING_EDGE;
|
|
+ else
|
|
+ /* Emit high-to-low event */
|
|
+ ge.id = GPIOEVENT_EVENT_FALLING_EDGE;
|
|
+ } else if (le->eflags & GPIOEVENT_REQUEST_RISING_EDGE && level) {
|
|
+ /* Emit low-to-high event */
|
|
+ ge.id = GPIOEVENT_EVENT_RISING_EDGE;
|
|
+ } else if (le->eflags & GPIOEVENT_REQUEST_FALLING_EDGE && !level) {
|
|
+ /* Emit high-to-low event */
|
|
+ ge.id = GPIOEVENT_EVENT_FALLING_EDGE;
|
|
+ } else {
|
|
+ return IRQ_NONE;
|
|
+ }
|
|
+
|
|
+ ret = kfifo_put(&le->events, ge);
|
|
+ if (ret != 0)
|
|
+ wake_up_poll(&le->wait, POLLIN);
|
|
+
|
|
+ return IRQ_HANDLED;
|
|
+}
|
|
+
|
|
+static int lineevent_create(struct gpio_device *gdev, void __user *ip)
|
|
+{
|
|
+ struct gpioevent_request eventreq;
|
|
+ struct lineevent_state *le;
|
|
+ struct gpio_desc *desc;
|
|
+ struct file *file;
|
|
+ u32 offset;
|
|
+ u32 lflags;
|
|
+ u32 eflags;
|
|
+ int fd;
|
|
+ int ret;
|
|
+ int irqflags = 0;
|
|
+
|
|
+ if (copy_from_user(&eventreq, ip, sizeof(eventreq)))
|
|
+ return -EFAULT;
|
|
+
|
|
+ le = kzalloc(sizeof(*le), GFP_KERNEL);
|
|
+ if (!le)
|
|
+ return -ENOMEM;
|
|
+ le->gdev = gdev;
|
|
+ get_device(&gdev->dev);
|
|
+
|
|
+ /* Make sure this is terminated */
|
|
+ eventreq.consumer_label[sizeof(eventreq.consumer_label)-1] = '\0';
|
|
+ if (strlen(eventreq.consumer_label)) {
|
|
+ le->label = kstrdup(eventreq.consumer_label,
|
|
+ GFP_KERNEL);
|
|
+ if (!le->label) {
|
|
+ ret = -ENOMEM;
|
|
+ goto out_free_le;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ offset = eventreq.lineoffset;
|
|
+ lflags = eventreq.handleflags;
|
|
+ eflags = eventreq.eventflags;
|
|
+
|
|
+ if (offset >= gdev->ngpio) {
|
|
+ ret = -EINVAL;
|
|
+ goto out_free_label;
|
|
+ }
|
|
+
|
|
+ /* Return an error if a unknown flag is set */
|
|
+ if ((lflags & ~GPIOHANDLE_REQUEST_VALID_FLAGS) ||
|
|
+ (eflags & ~GPIOEVENT_REQUEST_VALID_FLAGS)) {
|
|
+ ret = -EINVAL;
|
|
+ goto out_free_label;
|
|
+ }
|
|
+
|
|
+ /* This is just wrong: we don't look for events on output lines */
|
|
+ if ((lflags & GPIOHANDLE_REQUEST_OUTPUT) ||
|
|
+ (lflags & GPIOHANDLE_REQUEST_OPEN_DRAIN) ||
|
|
+ (lflags & GPIOHANDLE_REQUEST_OPEN_SOURCE)) {
|
|
+ ret = -EINVAL;
|
|
+ goto out_free_label;
|
|
+ }
|
|
+
|
|
+ desc = &gdev->descs[offset];
|
|
+ ret = gpiod_request(desc, le->label);
|
|
+ if (ret)
|
|
+ goto out_free_label;
|
|
+ le->desc = desc;
|
|
+ le->eflags = eflags;
|
|
+
|
|
+ if (lflags & GPIOHANDLE_REQUEST_ACTIVE_LOW)
|
|
+ set_bit(FLAG_ACTIVE_LOW, &desc->flags);
|
|
+
|
|
+ ret = gpiod_direction_input(desc);
|
|
+ if (ret)
|
|
+ goto out_free_desc;
|
|
+
|
|
+ le->irq = gpiod_to_irq(desc);
|
|
+ if (le->irq <= 0) {
|
|
+ ret = -ENODEV;
|
|
+ goto out_free_desc;
|
|
+ }
|
|
+
|
|
+ if (eflags & GPIOEVENT_REQUEST_RISING_EDGE)
|
|
+ irqflags |= test_bit(FLAG_ACTIVE_LOW, &desc->flags) ?
|
|
+ IRQF_TRIGGER_FALLING : IRQF_TRIGGER_RISING;
|
|
+ if (eflags & GPIOEVENT_REQUEST_FALLING_EDGE)
|
|
+ irqflags |= test_bit(FLAG_ACTIVE_LOW, &desc->flags) ?
|
|
+ IRQF_TRIGGER_RISING : IRQF_TRIGGER_FALLING;
|
|
+ irqflags |= IRQF_ONESHOT;
|
|
+ irqflags |= IRQF_SHARED;
|
|
+
|
|
+ INIT_KFIFO(le->events);
|
|
+ init_waitqueue_head(&le->wait);
|
|
+ mutex_init(&le->read_lock);
|
|
+
|
|
+ /* Request a thread to read the events */
|
|
+ ret = request_threaded_irq(le->irq,
|
|
+ NULL,
|
|
+ lineevent_irq_thread,
|
|
+ irqflags,
|
|
+ le->label,
|
|
+ le);
|
|
+ if (ret)
|
|
+ goto out_free_desc;
|
|
+
|
|
+ fd = get_unused_fd_flags(O_RDONLY | O_CLOEXEC);
|
|
+ if (fd < 0) {
|
|
+ ret = fd;
|
|
+ goto out_free_irq;
|
|
+ }
|
|
+
|
|
+ file = anon_inode_getfile("gpio-event",
|
|
+ &lineevent_fileops,
|
|
+ le,
|
|
+ O_RDONLY | O_CLOEXEC);
|
|
+ if (IS_ERR(file)) {
|
|
+ ret = PTR_ERR(file);
|
|
+ goto out_put_unused_fd;
|
|
+ }
|
|
+
|
|
+ eventreq.fd = fd;
|
|
+ if (copy_to_user(ip, &eventreq, sizeof(eventreq))) {
|
|
+ /*
|
|
+ * fput() will trigger the release() callback, so do not go onto
|
|
+ * the regular error cleanup path here.
|
|
+ */
|
|
+ fput(file);
|
|
+ put_unused_fd(fd);
|
|
+ return -EFAULT;
|
|
+ }
|
|
+
|
|
+ fd_install(fd, file);
|
|
+
|
|
+ return 0;
|
|
+
|
|
+out_put_unused_fd:
|
|
+ put_unused_fd(fd);
|
|
+out_free_irq:
|
|
+ free_irq(le->irq, le);
|
|
+out_free_desc:
|
|
+ gpiod_free(le->desc);
|
|
+out_free_label:
|
|
+ kfree(le->label);
|
|
+out_free_le:
|
|
+ kfree(le);
|
|
+ put_device(&gdev->dev);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * gpio_ioctl() - ioctl handler for the GPIO chardev
|
|
+ */
|
|
+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;
|
|
+
|
|
+ /* 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),
|
|
+ sizeof(chipinfo.name));
|
|
+ chipinfo.name[sizeof(chipinfo.name)-1] = '\0';
|
|
+ strncpy(chipinfo.label, gdev->label,
|
|
+ sizeof(chipinfo.label));
|
|
+ chipinfo.label[sizeof(chipinfo.label)-1] = '\0';
|
|
+ chipinfo.lines = gdev->ngpio;
|
|
+ if (copy_to_user(ip, &chipinfo, sizeof(chipinfo)))
|
|
+ return -EFAULT;
|
|
+ return 0;
|
|
+ } else if (cmd == GPIO_GET_LINEINFO_IOCTL) {
|
|
+ struct gpioline_info lineinfo;
|
|
+ struct gpio_desc *desc;
|
|
+
|
|
+ if (copy_from_user(&lineinfo, ip, sizeof(lineinfo)))
|
|
+ return -EFAULT;
|
|
+ if (lineinfo.line_offset >= gdev->ngpio)
|
|
+ return -EINVAL;
|
|
+
|
|
+ desc = &gdev->descs[lineinfo.line_offset];
|
|
+ if (desc->name) {
|
|
+ strncpy(lineinfo.name, desc->name,
|
|
+ sizeof(lineinfo.name));
|
|
+ lineinfo.name[sizeof(lineinfo.name)-1] = '\0';
|
|
+ } else {
|
|
+ lineinfo.name[0] = '\0';
|
|
+ }
|
|
+ if (desc->label) {
|
|
+ strncpy(lineinfo.consumer, desc->label,
|
|
+ sizeof(lineinfo.consumer));
|
|
+ lineinfo.consumer[sizeof(lineinfo.consumer)-1] = '\0';
|
|
+ } else {
|
|
+ lineinfo.consumer[0] = '\0';
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * Userspace only need to know that the kernel is using
|
|
+ * this GPIO so it can't use it.
|
|
+ */
|
|
+ lineinfo.flags = 0;
|
|
+ if (test_bit(FLAG_REQUESTED, &desc->flags) ||
|
|
+ test_bit(FLAG_IS_HOGGED, &desc->flags) ||
|
|
+ test_bit(FLAG_USED_AS_IRQ, &desc->flags) ||
|
|
+ test_bit(FLAG_EXPORT, &desc->flags) ||
|
|
+ test_bit(FLAG_SYSFS, &desc->flags))
|
|
+ lineinfo.flags |= GPIOLINE_FLAG_KERNEL;
|
|
+ if (test_bit(FLAG_IS_OUT, &desc->flags))
|
|
+ lineinfo.flags |= GPIOLINE_FLAG_IS_OUT;
|
|
+ if (test_bit(FLAG_ACTIVE_LOW, &desc->flags))
|
|
+ lineinfo.flags |= GPIOLINE_FLAG_ACTIVE_LOW;
|
|
+ if (test_bit(FLAG_OPEN_DRAIN, &desc->flags))
|
|
+ lineinfo.flags |= (GPIOLINE_FLAG_OPEN_DRAIN |
|
|
+ GPIOLINE_FLAG_IS_OUT);
|
|
+ if (test_bit(FLAG_OPEN_SOURCE, &desc->flags))
|
|
+ lineinfo.flags |= (GPIOLINE_FLAG_OPEN_SOURCE |
|
|
+ GPIOLINE_FLAG_IS_OUT);
|
|
+
|
|
+ if (copy_to_user(ip, &lineinfo, sizeof(lineinfo)))
|
|
+ return -EFAULT;
|
|
+ return 0;
|
|
+ } else if (cmd == GPIO_GET_LINEHANDLE_IOCTL) {
|
|
+ return linehandle_create(gdev, ip);
|
|
+ } else if (cmd == GPIO_GET_LINEEVENT_IOCTL) {
|
|
+ return lineevent_create(gdev, ip);
|
|
+ }
|
|
+ return -EINVAL;
|
|
+}
|
|
+
|
|
+#ifdef CONFIG_COMPAT
|
|
+static long gpio_ioctl_compat(struct file *filp, unsigned int cmd,
|
|
+ unsigned long arg)
|
|
+{
|
|
+ return gpio_ioctl(filp, cmd, (unsigned long)compat_ptr(arg));
|
|
+}
|
|
+#endif
|
|
+
|
|
+/**
|
|
+ * gpio_chrdev_open() - open the chardev for ioctl operations
|
|
+ * @inode: inode for this chardev
|
|
+ * @filp: file struct for storing private data
|
|
+ * Returns 0 on success
|
|
+ */
|
|
+static int gpio_chrdev_open(struct inode *inode, struct file *filp)
|
|
+{
|
|
+ struct gpio_device *gdev = container_of(inode->i_cdev,
|
|
+ struct gpio_device, chrdev);
|
|
+
|
|
+ /* Fail on open if the backing gpiochip is gone */
|
|
+ if (!gdev || !gdev->chip)
|
|
+ return -ENODEV;
|
|
+ get_device(&gdev->dev);
|
|
+ filp->private_data = gdev;
|
|
+
|
|
+ return nonseekable_open(inode, filp);
|
|
+}
|
|
+
|
|
+/**
|
|
+ * gpio_chrdev_release() - close chardev after ioctl operations
|
|
+ * @inode: inode for this chardev
|
|
+ * @filp: file struct for storing private data
|
|
+ * Returns 0 on success
|
|
+ */
|
|
+static int gpio_chrdev_release(struct inode *inode, struct file *filp)
|
|
+{
|
|
+ struct gpio_device *gdev = container_of(inode->i_cdev,
|
|
+ struct gpio_device, chrdev);
|
|
+
|
|
+ if (!gdev)
|
|
+ return -ENODEV;
|
|
+ put_device(&gdev->dev);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+
|
|
+static const struct file_operations gpio_fileops = {
|
|
+ .release = gpio_chrdev_release,
|
|
+ .open = gpio_chrdev_open,
|
|
+ .owner = THIS_MODULE,
|
|
+ .llseek = no_llseek,
|
|
+ .unlocked_ioctl = gpio_ioctl,
|
|
+#ifdef CONFIG_COMPAT
|
|
+ .compat_ioctl = gpio_ioctl_compat,
|
|
+#endif
|
|
+};
|
|
+
|
|
+static void gpiodevice_release(struct device *dev)
|
|
+{
|
|
+ struct gpio_device *gdev = dev_get_drvdata(dev);
|
|
+
|
|
+ list_del(&gdev->list);
|
|
+ ida_simple_remove(&gpio_ida, gdev->id);
|
|
+ kfree(gdev->label);
|
|
+ kfree(gdev->descs);
|
|
+ kfree(gdev);
|
|
+}
|
|
+
|
|
+static int gpiochip_setup_dev(struct gpio_device *gdev)
|
|
+{
|
|
+ int status;
|
|
+
|
|
+ cdev_init(&gdev->chrdev, &gpio_fileops);
|
|
+ gdev->chrdev.owner = THIS_MODULE;
|
|
+ gdev->chrdev.kobj.parent = &gdev->dev.kobj;
|
|
+ gdev->dev.devt = MKDEV(MAJOR(gpio_devt), gdev->id);
|
|
+ status = cdev_add(&gdev->chrdev, gdev->dev.devt, 1);
|
|
+ if (status < 0)
|
|
+ chip_warn(gdev->chip, "failed to add char device %d:%d\n",
|
|
+ MAJOR(gpio_devt), gdev->id);
|
|
+ else
|
|
+ chip_dbg(gdev->chip, "added GPIO chardev (%d:%d)\n",
|
|
+ MAJOR(gpio_devt), gdev->id);
|
|
+ status = device_add(&gdev->dev);
|
|
+ if (status)
|
|
+ goto err_remove_chardev;
|
|
+
|
|
+ status = gpiochip_sysfs_register(gdev);
|
|
+ if (status)
|
|
+ goto err_remove_device;
|
|
+
|
|
+ /* From this point, the .release() function cleans up gpio_device */
|
|
+ gdev->dev.release = gpiodevice_release;
|
|
+ pr_debug("%s: registered GPIOs %d to %d on device: %s (%s)\n",
|
|
+ __func__, gdev->base, gdev->base + gdev->ngpio - 1,
|
|
+ dev_name(&gdev->dev), gdev->chip->label ? : "generic");
|
|
+
|
|
+ return 0;
|
|
+
|
|
+err_remove_device:
|
|
+ device_del(&gdev->dev);
|
|
+err_remove_chardev:
|
|
+ cdev_del(&gdev->chrdev);
|
|
+ return status;
|
|
+}
|
|
+
|
|
+static void gpiochip_setup_devs(void)
|
|
+{
|
|
+ struct gpio_device *gdev;
|
|
+ int err;
|
|
+
|
|
+ list_for_each_entry(gdev, &gpio_devices, list) {
|
|
+ err = gpiochip_setup_dev(gdev);
|
|
+ if (err)
|
|
+ pr_err("%s: Failed to initialize gpio device (%d)\n",
|
|
+ dev_name(&gdev->dev), err);
|
|
+ }
|
|
+}
|
|
+
|
|
+/**
|
|
+ * gpiochip_add_data() - register a gpio_chip
|
|
* @chip: the chip to register, with chip->base initialized
|
|
* Context: potentially before irqs will work
|
|
*
|
|
@@ -288,110 +1106,242 @@ static int gpiochip_set_desc_names(struct gpio_chip *gc)
|
|
* because the chip->base is invalid or already associated with a
|
|
* different chip. Otherwise it returns zero as a success code.
|
|
*
|
|
- * When gpiochip_add() is called very early during boot, so that GPIOs
|
|
- * can be freely used, the chip->dev device must be registered before
|
|
+ * When gpiochip_add_data() is called very early during boot, so that GPIOs
|
|
+ * can be freely used, the chip->parent device must be registered before
|
|
* the gpio framework's arch_initcall(). Otherwise sysfs initialization
|
|
* for GPIOs will fail rudely.
|
|
*
|
|
+ * gpiochip_add_data() must only be called after gpiolib initialization,
|
|
+ * ie after core_initcall().
|
|
+ *
|
|
* If chip->base is negative, this requests dynamic assignment of
|
|
* a range of valid GPIOs.
|
|
*/
|
|
-int gpiochip_add(struct gpio_chip *chip)
|
|
+int gpiochip_add_data(struct gpio_chip *chip, void *data)
|
|
{
|
|
unsigned long flags;
|
|
int status = 0;
|
|
- unsigned id;
|
|
+ unsigned i;
|
|
int base = chip->base;
|
|
- struct gpio_desc *descs;
|
|
+ struct gpio_device *gdev;
|
|
|
|
- descs = kcalloc(chip->ngpio, sizeof(descs[0]), GFP_KERNEL);
|
|
- if (!descs)
|
|
+ /*
|
|
+ * First: allocate and populate the internal stat container, and
|
|
+ * set up the struct device.
|
|
+ */
|
|
+ gdev = kzalloc(sizeof(*gdev), GFP_KERNEL);
|
|
+ if (!gdev)
|
|
return -ENOMEM;
|
|
+ gdev->dev.bus = &gpio_bus_type;
|
|
+ gdev->chip = chip;
|
|
+ chip->gpiodev = gdev;
|
|
+ if (chip->parent) {
|
|
+ gdev->dev.parent = chip->parent;
|
|
+ gdev->dev.of_node = chip->parent->of_node;
|
|
+ }
|
|
+
|
|
+#ifdef CONFIG_OF_GPIO
|
|
+ /* If the gpiochip has an assigned OF node this takes precedence */
|
|
+ if (chip->of_node)
|
|
+ gdev->dev.of_node = chip->of_node;
|
|
+#endif
|
|
+
|
|
+ gdev->id = ida_simple_get(&gpio_ida, 0, 0, GFP_KERNEL);
|
|
+ if (gdev->id < 0) {
|
|
+ status = gdev->id;
|
|
+ goto err_free_gdev;
|
|
+ }
|
|
+ dev_set_name(&gdev->dev, "gpiochip%d", gdev->id);
|
|
+ device_initialize(&gdev->dev);
|
|
+ dev_set_drvdata(&gdev->dev, gdev);
|
|
+ if (chip->parent && chip->parent->driver)
|
|
+ gdev->owner = chip->parent->driver->owner;
|
|
+ else if (chip->owner)
|
|
+ /* TODO: remove chip->owner */
|
|
+ gdev->owner = chip->owner;
|
|
+ else
|
|
+ gdev->owner = THIS_MODULE;
|
|
+
|
|
+ gdev->descs = kcalloc(chip->ngpio, sizeof(gdev->descs[0]), GFP_KERNEL);
|
|
+ if (!gdev->descs) {
|
|
+ status = -ENOMEM;
|
|
+ goto err_free_ida;
|
|
+ }
|
|
+
|
|
+ if (chip->ngpio == 0) {
|
|
+ chip_err(chip, "tried to insert a GPIO chip with zero lines\n");
|
|
+ status = -EINVAL;
|
|
+ goto err_free_descs;
|
|
+ }
|
|
+
|
|
+ if (chip->label)
|
|
+ gdev->label = kstrdup(chip->label, GFP_KERNEL);
|
|
+ else
|
|
+ gdev->label = kstrdup("unknown", GFP_KERNEL);
|
|
+ if (!gdev->label) {
|
|
+ status = -ENOMEM;
|
|
+ goto err_free_descs;
|
|
+ }
|
|
+
|
|
+ gdev->ngpio = chip->ngpio;
|
|
+ gdev->data = data;
|
|
|
|
spin_lock_irqsave(&gpio_lock, flags);
|
|
|
|
+ /*
|
|
+ * TODO: this allocates a Linux GPIO number base in the global
|
|
+ * GPIO numberspace for this chip. In the long run we want to
|
|
+ * get *rid* of this numberspace and use only descriptors, but
|
|
+ * it may be a pipe dream. It will not happen before we get rid
|
|
+ * of the sysfs interface anyways.
|
|
+ */
|
|
if (base < 0) {
|
|
base = gpiochip_find_base(chip->ngpio);
|
|
if (base < 0) {
|
|
status = base;
|
|
spin_unlock_irqrestore(&gpio_lock, flags);
|
|
- goto err_free_descs;
|
|
+ goto err_free_label;
|
|
}
|
|
+ /*
|
|
+ * TODO: it should not be necessary to reflect the assigned
|
|
+ * base outside of the GPIO subsystem. Go over drivers and
|
|
+ * see if anyone makes use of this, else drop this and assign
|
|
+ * a poison instead.
|
|
+ */
|
|
chip->base = base;
|
|
}
|
|
+ gdev->base = base;
|
|
|
|
- status = gpiochip_add_to_list(chip);
|
|
+ status = gpiodev_add_to_list(gdev);
|
|
if (status) {
|
|
spin_unlock_irqrestore(&gpio_lock, flags);
|
|
- goto err_free_descs;
|
|
+ goto err_free_label;
|
|
}
|
|
|
|
- for (id = 0; id < chip->ngpio; id++) {
|
|
- struct gpio_desc *desc = &descs[id];
|
|
+ spin_unlock_irqrestore(&gpio_lock, flags);
|
|
|
|
- desc->chip = chip;
|
|
+ for (i = 0; i < chip->ngpio; i++) {
|
|
+ struct gpio_desc *desc = &gdev->descs[i];
|
|
|
|
- /* REVISIT: most hardware initializes GPIOs as inputs (often
|
|
- * with pullups enabled) so power usage is minimized. Linux
|
|
- * code should set the gpio direction first thing; but until
|
|
- * it does, and in case chip->get_direction is not set, we may
|
|
- * expose the wrong direction in sysfs.
|
|
+ desc->gdev = gdev;
|
|
+ /*
|
|
+ * REVISIT: most hardware initializes GPIOs as inputs
|
|
+ * (often with pullups enabled) so power usage is
|
|
+ * minimized. Linux code should set the gpio direction
|
|
+ * first thing; but until it does, and in case
|
|
+ * chip->get_direction is not set, we may expose the
|
|
+ * wrong direction in sysfs.
|
|
*/
|
|
- desc->flags = !chip->direction_input ? (1 << FLAG_IS_OUT) : 0;
|
|
- }
|
|
|
|
- chip->desc = descs;
|
|
+ if (chip->get_direction) {
|
|
+ /*
|
|
+ * If we have .get_direction, set up the initial
|
|
+ * direction flag from the hardware.
|
|
+ */
|
|
+ int dir = chip->get_direction(chip, i);
|
|
|
|
- spin_unlock_irqrestore(&gpio_lock, flags);
|
|
+ if (!dir)
|
|
+ set_bit(FLAG_IS_OUT, &desc->flags);
|
|
+ } else if (!chip->direction_input) {
|
|
+ /*
|
|
+ * If the chip lacks the .direction_input callback
|
|
+ * we logically assume all lines are outputs.
|
|
+ */
|
|
+ set_bit(FLAG_IS_OUT, &desc->flags);
|
|
+ }
|
|
+ }
|
|
|
|
#ifdef CONFIG_PINCTRL
|
|
- INIT_LIST_HEAD(&chip->pin_ranges);
|
|
+ INIT_LIST_HEAD(&gdev->pin_ranges);
|
|
#endif
|
|
|
|
- if (!chip->owner && chip->dev && chip->dev->driver)
|
|
- chip->owner = chip->dev->driver->owner;
|
|
-
|
|
status = gpiochip_set_desc_names(chip);
|
|
if (status)
|
|
goto err_remove_from_list;
|
|
|
|
- status = of_gpiochip_add(chip);
|
|
+ status = gpiochip_irqchip_init_valid_mask(chip);
|
|
if (status)
|
|
- goto err_remove_chip;
|
|
-
|
|
- acpi_gpiochip_add(chip);
|
|
+ goto err_remove_from_list;
|
|
|
|
- status = gpiochip_sysfs_register(chip);
|
|
+ status = of_gpiochip_add(chip);
|
|
if (status)
|
|
goto err_remove_chip;
|
|
|
|
- pr_debug("%s: registered GPIOs %d to %d on device: %s\n", __func__,
|
|
- chip->base, chip->base + chip->ngpio - 1,
|
|
- chip->label ? : "generic");
|
|
+ acpi_gpiochip_add(chip);
|
|
|
|
+ /*
|
|
+ * By first adding the chardev, and then adding the device,
|
|
+ * we get a device node entry in sysfs under
|
|
+ * /sys/bus/gpio/devices/gpiochipN/dev that can be used for
|
|
+ * coldplug of device nodes and other udev business.
|
|
+ * We can do this only if gpiolib has been initialized.
|
|
+ * Otherwise, defer until later.
|
|
+ */
|
|
+ if (gpiolib_initialized) {
|
|
+ status = gpiochip_setup_dev(gdev);
|
|
+ if (status)
|
|
+ goto err_remove_chip;
|
|
+ }
|
|
return 0;
|
|
|
|
err_remove_chip:
|
|
acpi_gpiochip_remove(chip);
|
|
gpiochip_free_hogs(chip);
|
|
of_gpiochip_remove(chip);
|
|
+ gpiochip_irqchip_free_valid_mask(chip);
|
|
err_remove_from_list:
|
|
spin_lock_irqsave(&gpio_lock, flags);
|
|
- list_del(&chip->list);
|
|
+ list_del(&gdev->list);
|
|
spin_unlock_irqrestore(&gpio_lock, flags);
|
|
- chip->desc = NULL;
|
|
+err_free_label:
|
|
+ kfree(gdev->label);
|
|
err_free_descs:
|
|
- kfree(descs);
|
|
-
|
|
+ kfree(gdev->descs);
|
|
+err_free_ida:
|
|
+ ida_simple_remove(&gpio_ida, gdev->id);
|
|
+err_free_gdev:
|
|
/* failures here can mean systems won't boot... */
|
|
pr_err("%s: GPIOs %d..%d (%s) failed to register\n", __func__,
|
|
- chip->base, chip->base + chip->ngpio - 1,
|
|
- chip->label ? : "generic");
|
|
+ gdev->base, gdev->base + gdev->ngpio - 1,
|
|
+ chip->label ? : "generic");
|
|
+ kfree(gdev);
|
|
return status;
|
|
}
|
|
+EXPORT_SYMBOL_GPL(gpiochip_add_data);
|
|
+
|
|
+/**
|
|
+ * gpiochip_add() - register a gpio_chip
|
|
+ * @chip: the chip to register, with chip->base initialized
|
|
+ * Context: potentially before irqs will work
|
|
+ *
|
|
+ * Returns a negative errno if the chip can't be registered, such as
|
|
+ * because the chip->base is invalid or already associated with a
|
|
+ * different chip. Otherwise it returns zero as a success code.
|
|
+ *
|
|
+ * When gpiochip_add() is called very early during boot, so that GPIOs
|
|
+ * can be freely used, the chip->dev device must be registered before
|
|
+ * the gpio framework's arch_initcall(). Otherwise sysfs initialization
|
|
+ * for GPIOs will fail rudely.
|
|
+ *
|
|
+ * If chip->base is negative, this requests dynamic assignment of
|
|
+ * a range of valid GPIOs.
|
|
+ */
|
|
+int gpiochip_add(struct gpio_chip *chip)
|
|
+{
|
|
+ return gpiochip_add_data(chip, NULL);
|
|
+}
|
|
EXPORT_SYMBOL_GPL(gpiochip_add);
|
|
|
|
/**
|
|
+ * gpiochip_get_data() - get per-subdriver data for the chip
|
|
+ */
|
|
+void *gpiochip_get_data(struct gpio_chip *chip)
|
|
+{
|
|
+ return chip->gpiodev->data;
|
|
+}
|
|
+EXPORT_SYMBOL_GPL(gpiochip_get_data);
|
|
+
|
|
+/**
|
|
* gpiochip_remove() - unregister a gpio_chip
|
|
* @chip: the chip to unregister
|
|
*
|
|
@@ -399,38 +1349,125 @@ EXPORT_SYMBOL_GPL(gpiochip_add);
|
|
*/
|
|
void gpiochip_remove(struct gpio_chip *chip)
|
|
{
|
|
+ struct gpio_device *gdev = chip->gpiodev;
|
|
struct gpio_desc *desc;
|
|
unsigned long flags;
|
|
- unsigned id;
|
|
+ unsigned i;
|
|
bool requested = false;
|
|
|
|
- gpiochip_sysfs_unregister(chip);
|
|
-
|
|
+ /* FIXME: should the legacy sysfs handling be moved to gpio_device? */
|
|
+ gpiochip_sysfs_unregister(gdev);
|
|
+ gpiochip_free_hogs(chip);
|
|
+ /* Numb the device, cancelling all outstanding operations */
|
|
+ gdev->chip = NULL;
|
|
gpiochip_irqchip_remove(chip);
|
|
-
|
|
acpi_gpiochip_remove(chip);
|
|
gpiochip_remove_pin_ranges(chip);
|
|
- gpiochip_free_hogs(chip);
|
|
of_gpiochip_remove(chip);
|
|
+ /*
|
|
+ * We accept no more calls into the driver from this point, so
|
|
+ * NULL the driver data pointer
|
|
+ */
|
|
+ gdev->data = NULL;
|
|
|
|
spin_lock_irqsave(&gpio_lock, flags);
|
|
- for (id = 0; id < chip->ngpio; id++) {
|
|
- desc = &chip->desc[id];
|
|
- desc->chip = NULL;
|
|
+ for (i = 0; i < gdev->ngpio; i++) {
|
|
+ desc = &gdev->descs[i];
|
|
if (test_bit(FLAG_REQUESTED, &desc->flags))
|
|
requested = true;
|
|
}
|
|
- list_del(&chip->list);
|
|
spin_unlock_irqrestore(&gpio_lock, flags);
|
|
|
|
if (requested)
|
|
- dev_crit(chip->dev, "REMOVING GPIOCHIP WITH GPIOS STILL REQUESTED\n");
|
|
+ dev_crit(&gdev->dev,
|
|
+ "REMOVING GPIOCHIP WITH GPIOS STILL REQUESTED\n");
|
|
|
|
- kfree(chip->desc);
|
|
- chip->desc = NULL;
|
|
+ /*
|
|
+ * The gpiochip side puts its use of the device to rest here:
|
|
+ * if there are no userspace clients, the chardev and device will
|
|
+ * be removed, else it will be dangling until the last user is
|
|
+ * gone.
|
|
+ */
|
|
+ cdev_del(&gdev->chrdev);
|
|
+ device_del(&gdev->dev);
|
|
+ put_device(&gdev->dev);
|
|
}
|
|
EXPORT_SYMBOL_GPL(gpiochip_remove);
|
|
|
|
+static void devm_gpio_chip_release(struct device *dev, void *res)
|
|
+{
|
|
+ struct gpio_chip *chip = *(struct gpio_chip **)res;
|
|
+
|
|
+ gpiochip_remove(chip);
|
|
+}
|
|
+
|
|
+static int devm_gpio_chip_match(struct device *dev, void *res, void *data)
|
|
+
|
|
+{
|
|
+ struct gpio_chip **r = res;
|
|
+
|
|
+ if (!r || !*r) {
|
|
+ WARN_ON(!r || !*r);
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ return *r == data;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * devm_gpiochip_add_data() - Resource manager piochip_add_data()
|
|
+ * @dev: the device pointer on which irq_chip belongs to.
|
|
+ * @chip: the chip to register, with chip->base initialized
|
|
+ * Context: potentially before irqs will work
|
|
+ *
|
|
+ * Returns a negative errno if the chip can't be registered, such as
|
|
+ * because the chip->base is invalid or already associated with a
|
|
+ * different chip. Otherwise it returns zero as a success code.
|
|
+ *
|
|
+ * The gpio chip automatically be released when the device is unbound.
|
|
+ */
|
|
+int devm_gpiochip_add_data(struct device *dev, struct gpio_chip *chip,
|
|
+ void *data)
|
|
+{
|
|
+ struct gpio_chip **ptr;
|
|
+ int ret;
|
|
+
|
|
+ ptr = devres_alloc(devm_gpio_chip_release, sizeof(*ptr),
|
|
+ GFP_KERNEL);
|
|
+ if (!ptr)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ ret = gpiochip_add_data(chip, data);
|
|
+ if (ret < 0) {
|
|
+ devres_free(ptr);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ *ptr = chip;
|
|
+ devres_add(dev, ptr);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+EXPORT_SYMBOL_GPL(devm_gpiochip_add_data);
|
|
+
|
|
+/**
|
|
+ * devm_gpiochip_remove() - Resource manager of gpiochip_remove()
|
|
+ * @dev: device for which which resource was allocated
|
|
+ * @chip: the chip to remove
|
|
+ *
|
|
+ * A gpio_chip with any GPIOs still requested may not be removed.
|
|
+ */
|
|
+void devm_gpiochip_remove(struct device *dev, struct gpio_chip *chip)
|
|
+{
|
|
+ int ret;
|
|
+
|
|
+ ret = devres_release(dev, devm_gpio_chip_release,
|
|
+ devm_gpio_chip_match, chip);
|
|
+ if (!ret)
|
|
+ WARN_ON(ret);
|
|
+}
|
|
+EXPORT_SYMBOL_GPL(devm_gpiochip_remove);
|
|
+
|
|
/**
|
|
* gpiochip_find() - iterator for locating a specific gpio_chip
|
|
* @data: data to pass to match function
|
|
@@ -446,17 +1483,17 @@ struct gpio_chip *gpiochip_find(void *data,
|
|
int (*match)(struct gpio_chip *chip,
|
|
void *data))
|
|
{
|
|
- struct gpio_chip *chip;
|
|
+ struct gpio_device *gdev;
|
|
+ struct gpio_chip *chip = NULL;
|
|
unsigned long flags;
|
|
|
|
spin_lock_irqsave(&gpio_lock, flags);
|
|
- list_for_each_entry(chip, &gpio_chips, list)
|
|
- if (match(chip, data))
|
|
+ list_for_each_entry(gdev, &gpio_devices, list)
|
|
+ if (gdev->chip && match(gdev->chip, data)) {
|
|
+ chip = gdev->chip;
|
|
break;
|
|
+ }
|
|
|
|
- /* No match? */
|
|
- if (&chip->list == &gpio_chips)
|
|
- chip = NULL;
|
|
spin_unlock_irqrestore(&gpio_lock, flags);
|
|
|
|
return chip;
|
|
@@ -481,6 +1518,40 @@ static struct gpio_chip *find_chip_by_name(const char *name)
|
|
* The following is irqchip helper code for gpiochips.
|
|
*/
|
|
|
|
+static int gpiochip_irqchip_init_valid_mask(struct gpio_chip *gpiochip)
|
|
+{
|
|
+ int i;
|
|
+
|
|
+ if (!gpiochip->irq_need_valid_mask)
|
|
+ return 0;
|
|
+
|
|
+ gpiochip->irq_valid_mask = kcalloc(BITS_TO_LONGS(gpiochip->ngpio),
|
|
+ sizeof(long), GFP_KERNEL);
|
|
+ if (!gpiochip->irq_valid_mask)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ /* Assume by default all GPIOs are valid */
|
|
+ for (i = 0; i < gpiochip->ngpio; i++)
|
|
+ set_bit(i, gpiochip->irq_valid_mask);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void gpiochip_irqchip_free_valid_mask(struct gpio_chip *gpiochip)
|
|
+{
|
|
+ kfree(gpiochip->irq_valid_mask);
|
|
+ gpiochip->irq_valid_mask = NULL;
|
|
+}
|
|
+
|
|
+static bool gpiochip_irqchip_irq_valid(const struct gpio_chip *gpiochip,
|
|
+ unsigned int offset)
|
|
+{
|
|
+ /* No mask means all valid */
|
|
+ if (likely(!gpiochip->irq_valid_mask))
|
|
+ return true;
|
|
+ return test_bit(offset, gpiochip->irq_valid_mask);
|
|
+}
|
|
+
|
|
/**
|
|
* gpiochip_set_chained_irqchip() - sets a chained irqchip to a gpiochip
|
|
* @gpiochip: the gpiochip to set the irqchip chain to
|
|
@@ -522,9 +1593,12 @@ void gpiochip_set_chained_irqchip(struct gpio_chip *gpiochip,
|
|
}
|
|
|
|
/* Set the parent IRQ for all affected IRQs */
|
|
- for (offset = 0; offset < gpiochip->ngpio; offset++)
|
|
+ for (offset = 0; offset < gpiochip->ngpio; offset++) {
|
|
+ if (!gpiochip_irqchip_irq_valid(gpiochip, offset))
|
|
+ continue;
|
|
irq_set_parent(irq_find_mapping(gpiochip->irqdomain, offset),
|
|
parent_irq);
|
|
+ }
|
|
}
|
|
EXPORT_SYMBOL_GPL(gpiochip_set_chained_irqchip);
|
|
|
|
@@ -586,14 +1660,14 @@ static int gpiochip_irq_reqres(struct irq_data *d)
|
|
{
|
|
struct gpio_chip *chip = irq_data_get_irq_chip_data(d);
|
|
|
|
- if (!try_module_get(chip->owner))
|
|
+ if (!try_module_get(chip->gpiodev->owner))
|
|
return -ENODEV;
|
|
|
|
if (gpiochip_lock_as_irq(chip, d->hwirq)) {
|
|
chip_err(chip,
|
|
"unable to lock HW IRQ %lu for IRQ\n",
|
|
d->hwirq);
|
|
- module_put(chip->owner);
|
|
+ module_put(chip->gpiodev->owner);
|
|
return -EINVAL;
|
|
}
|
|
return 0;
|
|
@@ -604,7 +1678,7 @@ static void gpiochip_irq_relres(struct irq_data *d)
|
|
struct gpio_chip *chip = irq_data_get_irq_chip_data(d);
|
|
|
|
gpiochip_unlock_as_irq(chip, d->hwirq);
|
|
- module_put(chip->owner);
|
|
+ module_put(chip->gpiodev->owner);
|
|
}
|
|
|
|
static int gpiochip_to_irq(struct gpio_chip *chip, unsigned offset)
|
|
@@ -631,9 +1705,12 @@ static void gpiochip_irqchip_remove(struct gpio_chip *gpiochip)
|
|
|
|
/* Remove all IRQ mappings and delete the domain */
|
|
if (gpiochip->irqdomain) {
|
|
- for (offset = 0; offset < gpiochip->ngpio; offset++)
|
|
+ for (offset = 0; offset < gpiochip->ngpio; offset++) {
|
|
+ if (!gpiochip_irqchip_irq_valid(gpiochip, offset))
|
|
+ continue;
|
|
irq_dispose_mapping(
|
|
irq_find_mapping(gpiochip->irqdomain, offset));
|
|
+ }
|
|
irq_domain_remove(gpiochip->irqdomain);
|
|
}
|
|
|
|
@@ -642,6 +1719,8 @@ static void gpiochip_irqchip_remove(struct gpio_chip *gpiochip)
|
|
gpiochip->irqchip->irq_release_resources = NULL;
|
|
gpiochip->irqchip = NULL;
|
|
}
|
|
+
|
|
+ gpiochip_irqchip_free_valid_mask(gpiochip);
|
|
}
|
|
|
|
/**
|
|
@@ -659,7 +1738,7 @@ static void gpiochip_irqchip_remove(struct gpio_chip *gpiochip)
|
|
* gpiochip, providing an irq domain to translate the local IRQs to
|
|
* global irqs in the gpiolib core, and making sure that the gpiochip
|
|
* is passed as chip data to all related functions. Driver callbacks
|
|
- * need to use container_of() to get their local state containers back
|
|
+ * need to use gpiochip_get_data() to get their local state containers back
|
|
* from the gpiochip passed as chip data. An irqdomain will be stored
|
|
* in the gpiochip that shall be used by the driver to handle IRQ number
|
|
* translation. The gpiochip will need to be initialized and registered
|
|
@@ -677,25 +1756,41 @@ int _gpiochip_irqchip_add(struct gpio_chip *gpiochip,
|
|
struct lock_class_key *lock_key)
|
|
{
|
|
struct device_node *of_node;
|
|
+ bool irq_base_set = false;
|
|
unsigned int offset;
|
|
unsigned irq_base = 0;
|
|
|
|
if (!gpiochip || !irqchip)
|
|
return -EINVAL;
|
|
|
|
- if (!gpiochip->dev) {
|
|
+ if (!gpiochip->parent) {
|
|
pr_err("missing gpiochip .dev parent pointer\n");
|
|
return -EINVAL;
|
|
}
|
|
- of_node = gpiochip->dev->of_node;
|
|
+ of_node = gpiochip->parent->of_node;
|
|
#ifdef CONFIG_OF_GPIO
|
|
/*
|
|
* If the gpiochip has an assigned OF node this takes precedence
|
|
- * FIXME: get rid of this and use gpiochip->dev->of_node everywhere
|
|
+ * FIXME: get rid of this and use gpiochip->parent->of_node
|
|
+ * everywhere
|
|
*/
|
|
if (gpiochip->of_node)
|
|
of_node = gpiochip->of_node;
|
|
#endif
|
|
+ /*
|
|
+ * Specifying a default trigger is a terrible idea if DT or ACPI is
|
|
+ * used to configure the interrupts, as you may end-up with
|
|
+ * conflicting triggers. Tell the user, and reset to NONE.
|
|
+ */
|
|
+ if (WARN(of_node && type != IRQ_TYPE_NONE,
|
|
+ "%s: Ignoring %d default trigger\n", of_node->full_name, type))
|
|
+ type = IRQ_TYPE_NONE;
|
|
+ if (has_acpi_companion(gpiochip->parent) && type != IRQ_TYPE_NONE) {
|
|
+ acpi_handle_warn(ACPI_HANDLE(gpiochip->parent),
|
|
+ "Ignoring %d default trigger\n", type);
|
|
+ type = IRQ_TYPE_NONE;
|
|
+ }
|
|
+
|
|
gpiochip->irqchip = irqchip;
|
|
gpiochip->irq_handler = handler;
|
|
gpiochip->irq_default_type = type;
|
|
@@ -725,13 +1820,17 @@ int _gpiochip_irqchip_add(struct gpio_chip *gpiochip,
|
|
* necessary to allocate descriptors for all IRQs.
|
|
*/
|
|
for (offset = 0; offset < gpiochip->ngpio; offset++) {
|
|
+ if (!gpiochip_irqchip_irq_valid(gpiochip, offset))
|
|
+ continue;
|
|
irq_base = irq_create_mapping(gpiochip->irqdomain, offset);
|
|
- if (offset == 0)
|
|
+ if (!irq_base_set) {
|
|
/*
|
|
* Store the base into the gpiochip to be used when
|
|
* unmapping the irqs.
|
|
*/
|
|
gpiochip->irq_base = irq_base;
|
|
+ irq_base_set = true;
|
|
+ }
|
|
}
|
|
|
|
acpi_gpiochip_request_interrupts(gpiochip);
|
|
@@ -743,6 +1842,12 @@ EXPORT_SYMBOL_GPL(_gpiochip_irqchip_add);
|
|
#else /* CONFIG_GPIOLIB_IRQCHIP */
|
|
|
|
static void gpiochip_irqchip_remove(struct gpio_chip *gpiochip) {}
|
|
+static inline int gpiochip_irqchip_init_valid_mask(struct gpio_chip *gpiochip)
|
|
+{
|
|
+ return 0;
|
|
+}
|
|
+static inline void gpiochip_irqchip_free_valid_mask(struct gpio_chip *gpiochip)
|
|
+{ }
|
|
|
|
#endif /* CONFIG_GPIOLIB_IRQCHIP */
|
|
|
|
@@ -753,7 +1858,7 @@ static void gpiochip_irqchip_remove(struct gpio_chip *gpiochip) {}
|
|
*/
|
|
int gpiochip_generic_request(struct gpio_chip *chip, unsigned offset)
|
|
{
|
|
- return pinctrl_request_gpio(chip->base + offset);
|
|
+ return pinctrl_request_gpio(chip->gpiodev->base + offset);
|
|
}
|
|
EXPORT_SYMBOL_GPL(gpiochip_generic_request);
|
|
|
|
@@ -764,7 +1869,7 @@ EXPORT_SYMBOL_GPL(gpiochip_generic_request);
|
|
*/
|
|
void gpiochip_generic_free(struct gpio_chip *chip, unsigned offset)
|
|
{
|
|
- pinctrl_free_gpio(chip->base + offset);
|
|
+ pinctrl_free_gpio(chip->gpiodev->base + offset);
|
|
}
|
|
EXPORT_SYMBOL_GPL(gpiochip_generic_free);
|
|
|
|
@@ -782,6 +1887,7 @@ int gpiochip_add_pingroup_range(struct gpio_chip *chip,
|
|
unsigned int gpio_offset, const char *pin_group)
|
|
{
|
|
struct gpio_pin_range *pin_range;
|
|
+ struct gpio_device *gdev = chip->gpiodev;
|
|
int ret;
|
|
|
|
pin_range = kzalloc(sizeof(*pin_range), GFP_KERNEL);
|
|
@@ -794,7 +1900,7 @@ int gpiochip_add_pingroup_range(struct gpio_chip *chip,
|
|
pin_range->range.id = gpio_offset;
|
|
pin_range->range.gc = chip;
|
|
pin_range->range.name = chip->label;
|
|
- pin_range->range.base = chip->base + gpio_offset;
|
|
+ pin_range->range.base = gdev->base + gpio_offset;
|
|
pin_range->pctldev = pctldev;
|
|
|
|
ret = pinctrl_get_group_pins(pctldev, pin_group,
|
|
@@ -811,7 +1917,7 @@ int gpiochip_add_pingroup_range(struct gpio_chip *chip,
|
|
gpio_offset, gpio_offset + pin_range->range.npins - 1,
|
|
pinctrl_dev_get_devname(pctldev), pin_group);
|
|
|
|
- list_add_tail(&pin_range->node, &chip->pin_ranges);
|
|
+ list_add_tail(&pin_range->node, &gdev->pin_ranges);
|
|
|
|
return 0;
|
|
}
|
|
@@ -831,6 +1937,7 @@ int gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name,
|
|
unsigned int npins)
|
|
{
|
|
struct gpio_pin_range *pin_range;
|
|
+ struct gpio_device *gdev = chip->gpiodev;
|
|
int ret;
|
|
|
|
pin_range = kzalloc(sizeof(*pin_range), GFP_KERNEL);
|
|
@@ -843,7 +1950,7 @@ int gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name,
|
|
pin_range->range.id = gpio_offset;
|
|
pin_range->range.gc = chip;
|
|
pin_range->range.name = chip->label;
|
|
- pin_range->range.base = chip->base + gpio_offset;
|
|
+ pin_range->range.base = gdev->base + gpio_offset;
|
|
pin_range->range.pin_base = pin_offset;
|
|
pin_range->range.npins = npins;
|
|
pin_range->pctldev = pinctrl_find_and_add_gpio_range(pinctl_name,
|
|
@@ -859,7 +1966,7 @@ int gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name,
|
|
pinctl_name,
|
|
pin_offset, pin_offset + npins - 1);
|
|
|
|
- list_add_tail(&pin_range->node, &chip->pin_ranges);
|
|
+ list_add_tail(&pin_range->node, &gdev->pin_ranges);
|
|
|
|
return 0;
|
|
}
|
|
@@ -872,8 +1979,9 @@ EXPORT_SYMBOL_GPL(gpiochip_add_pin_range);
|
|
void gpiochip_remove_pin_ranges(struct gpio_chip *chip)
|
|
{
|
|
struct gpio_pin_range *pin_range, *tmp;
|
|
+ struct gpio_device *gdev = chip->gpiodev;
|
|
|
|
- list_for_each_entry_safe(pin_range, tmp, &chip->pin_ranges, node) {
|
|
+ list_for_each_entry_safe(pin_range, tmp, &gdev->pin_ranges, node) {
|
|
list_del(&pin_range->node);
|
|
pinctrl_remove_gpio_range(pin_range->pctldev,
|
|
&pin_range->range);
|
|
@@ -890,7 +1998,7 @@ EXPORT_SYMBOL_GPL(gpiochip_remove_pin_ranges);
|
|
*/
|
|
static int __gpiod_request(struct gpio_desc *desc, const char *label)
|
|
{
|
|
- struct gpio_chip *chip = desc->chip;
|
|
+ struct gpio_chip *chip = desc->gdev->chip;
|
|
int status;
|
|
unsigned long flags;
|
|
|
|
@@ -931,27 +2039,63 @@ done:
|
|
return status;
|
|
}
|
|
|
|
+/*
|
|
+ * This descriptor validation needs to be inserted verbatim into each
|
|
+ * function taking a descriptor, so we need to use a preprocessor
|
|
+ * macro to avoid endless duplication. If the desc is NULL it is an
|
|
+ * optional GPIO and calls should just bail out.
|
|
+ */
|
|
+#define VALIDATE_DESC(desc) do { \
|
|
+ if (!desc) \
|
|
+ return 0; \
|
|
+ if (IS_ERR(desc)) { \
|
|
+ pr_warn("%s: invalid GPIO (errorpointer)\n", __func__); \
|
|
+ return PTR_ERR(desc); \
|
|
+ } \
|
|
+ if (!desc->gdev) { \
|
|
+ pr_warn("%s: invalid GPIO (no device)\n", __func__); \
|
|
+ return -EINVAL; \
|
|
+ } \
|
|
+ if ( !desc->gdev->chip ) { \
|
|
+ dev_warn(&desc->gdev->dev, \
|
|
+ "%s: backing chip is gone\n", __func__); \
|
|
+ return 0; \
|
|
+ } } while (0)
|
|
+
|
|
+#define VALIDATE_DESC_VOID(desc) do { \
|
|
+ if (!desc) \
|
|
+ return; \
|
|
+ if (IS_ERR(desc)) { \
|
|
+ pr_warn("%s: invalid GPIO (errorpointer)\n", __func__); \
|
|
+ return; \
|
|
+ } \
|
|
+ if (!desc->gdev) { \
|
|
+ pr_warn("%s: invalid GPIO (no device)\n", __func__); \
|
|
+ return; \
|
|
+ } \
|
|
+ if (!desc->gdev->chip) { \
|
|
+ dev_warn(&desc->gdev->dev, \
|
|
+ "%s: backing chip is gone\n", __func__); \
|
|
+ return; \
|
|
+ } } while (0)
|
|
+
|
|
+
|
|
int gpiod_request(struct gpio_desc *desc, const char *label)
|
|
{
|
|
int status = -EPROBE_DEFER;
|
|
- struct gpio_chip *chip;
|
|
+ struct gpio_device *gdev;
|
|
|
|
- if (!desc) {
|
|
- pr_warn("%s: invalid GPIO\n", __func__);
|
|
- return -EINVAL;
|
|
- }
|
|
-
|
|
- chip = desc->chip;
|
|
- if (!chip)
|
|
- goto done;
|
|
+ VALIDATE_DESC(desc);
|
|
+ gdev = desc->gdev;
|
|
|
|
- if (try_module_get(chip->owner)) {
|
|
+ if (try_module_get(gdev->owner)) {
|
|
status = __gpiod_request(desc, label);
|
|
if (status < 0)
|
|
- module_put(chip->owner);
|
|
+ module_put(gdev->owner);
|
|
+ else
|
|
+ get_device(&gdev->dev);
|
|
}
|
|
|
|
-done:
|
|
if (status)
|
|
gpiod_dbg(desc, "%s: status %d\n", __func__, status);
|
|
|
|
@@ -970,7 +2114,7 @@ static bool __gpiod_free(struct gpio_desc *desc)
|
|
|
|
spin_lock_irqsave(&gpio_lock, flags);
|
|
|
|
- chip = desc->chip;
|
|
+ chip = desc->gdev->chip;
|
|
if (chip && test_bit(FLAG_REQUESTED, &desc->flags)) {
|
|
if (chip->free) {
|
|
spin_unlock_irqrestore(&gpio_lock, flags);
|
|
@@ -993,10 +2137,12 @@ static bool __gpiod_free(struct gpio_desc *desc)
|
|
|
|
void gpiod_free(struct gpio_desc *desc)
|
|
{
|
|
- if (desc && __gpiod_free(desc))
|
|
- module_put(desc->chip->owner);
|
|
- else
|
|
+ if (desc && desc->gdev && __gpiod_free(desc)) {
|
|
+ module_put(desc->gdev->owner);
|
|
+ put_device(&desc->gdev->dev);
|
|
+ } else {
|
|
WARN_ON(extra_checks);
|
|
+ }
|
|
}
|
|
|
|
/**
|
|
@@ -1019,7 +2165,7 @@ const char *gpiochip_is_requested(struct gpio_chip *chip, unsigned offset)
|
|
if (offset >= chip->ngpio)
|
|
return NULL;
|
|
|
|
- desc = &chip->desc[offset];
|
|
+ desc = &chip->gpiodev->descs[offset];
|
|
|
|
if (test_bit(FLAG_REQUESTED, &desc->flags) == 0)
|
|
return NULL;
|
|
@@ -1071,7 +2217,8 @@ void gpiochip_free_own_desc(struct gpio_desc *desc)
|
|
}
|
|
EXPORT_SYMBOL_GPL(gpiochip_free_own_desc);
|
|
|
|
-/* Drivers MUST set GPIO direction before making get/set calls. In
|
|
+/*
|
|
+ * Drivers MUST set GPIO direction before making get/set calls. In
|
|
* some cases this is done in early boot, before IRQs are enabled.
|
|
*
|
|
* As a rule these aren't called more than once (except for drivers
|
|
@@ -1094,12 +2241,9 @@ int gpiod_direction_input(struct gpio_desc *desc)
|
|
struct gpio_chip *chip;
|
|
int status = -EINVAL;
|
|
|
|
- if (!desc || !desc->chip) {
|
|
- pr_warn("%s: invalid GPIO\n", __func__);
|
|
- return -EINVAL;
|
|
- }
|
|
+ VALIDATE_DESC(desc);
|
|
+ chip = desc->gdev->chip;
|
|
|
|
- chip = desc->chip;
|
|
if (!chip->get || !chip->direction_input) {
|
|
gpiod_warn(desc,
|
|
"%s: missing get() or direction_input() operations\n",
|
|
@@ -1119,8 +2263,8 @@ EXPORT_SYMBOL_GPL(gpiod_direction_input);
|
|
|
|
static int _gpiod_direction_output_raw(struct gpio_desc *desc, int value)
|
|
{
|
|
- struct gpio_chip *chip;
|
|
- int status = -EINVAL;
|
|
+ struct gpio_chip *gc = desc->gdev->chip;
|
|
+ int ret;
|
|
|
|
/* GPIOs used for IRQs shall not be set as output */
|
|
if (test_bit(FLAG_USED_AS_IRQ, &desc->flags)) {
|
|
@@ -1130,28 +2274,50 @@ static int _gpiod_direction_output_raw(struct gpio_desc *desc, int value)
|
|
return -EIO;
|
|
}
|
|
|
|
- /* Open drain pin should not be driven to 1 */
|
|
- if (value && test_bit(FLAG_OPEN_DRAIN, &desc->flags))
|
|
- return gpiod_direction_input(desc);
|
|
-
|
|
- /* Open source pin should not be driven to 0 */
|
|
- if (!value && test_bit(FLAG_OPEN_SOURCE, &desc->flags))
|
|
- return gpiod_direction_input(desc);
|
|
+ if (test_bit(FLAG_OPEN_DRAIN, &desc->flags)) {
|
|
+ /* First see if we can enable open drain in hardware */
|
|
+ if (gc->set_single_ended) {
|
|
+ ret = gc->set_single_ended(gc, gpio_chip_hwgpio(desc),
|
|
+ LINE_MODE_OPEN_DRAIN);
|
|
+ if (!ret)
|
|
+ goto set_output_value;
|
|
+ }
|
|
+ /* Emulate open drain by not actively driving the line high */
|
|
+ if (value)
|
|
+ return gpiod_direction_input(desc);
|
|
+ }
|
|
+ else if (test_bit(FLAG_OPEN_SOURCE, &desc->flags)) {
|
|
+ if (gc->set_single_ended) {
|
|
+ ret = gc->set_single_ended(gc, gpio_chip_hwgpio(desc),
|
|
+ LINE_MODE_OPEN_SOURCE);
|
|
+ if (!ret)
|
|
+ goto set_output_value;
|
|
+ }
|
|
+ /* Emulate open source by not actively driving the line low */
|
|
+ if (!value)
|
|
+ return gpiod_direction_input(desc);
|
|
+ } else {
|
|
+ /* Make sure to disable open drain/source hardware, if any */
|
|
+ if (gc->set_single_ended)
|
|
+ gc->set_single_ended(gc,
|
|
+ gpio_chip_hwgpio(desc),
|
|
+ LINE_MODE_PUSH_PULL);
|
|
+ }
|
|
|
|
- chip = desc->chip;
|
|
- if (!chip->set || !chip->direction_output) {
|
|
+set_output_value:
|
|
+ if (!gc->set || !gc->direction_output) {
|
|
gpiod_warn(desc,
|
|
"%s: missing set() or direction_output() operations\n",
|
|
__func__);
|
|
return -EIO;
|
|
}
|
|
|
|
- status = chip->direction_output(chip, gpio_chip_hwgpio(desc), value);
|
|
- if (status == 0)
|
|
+ ret = gc->direction_output(gc, gpio_chip_hwgpio(desc), value);
|
|
+ if (!ret)
|
|
set_bit(FLAG_IS_OUT, &desc->flags);
|
|
trace_gpio_value(desc_to_gpio(desc), 0, value);
|
|
- trace_gpio_direction(desc_to_gpio(desc), 0, status);
|
|
- return status;
|
|
+ trace_gpio_direction(desc_to_gpio(desc), 0, ret);
|
|
+ return ret;
|
|
}
|
|
|
|
/**
|
|
@@ -1167,10 +2333,7 @@ static int _gpiod_direction_output_raw(struct gpio_desc *desc, int value)
|
|
*/
|
|
int gpiod_direction_output_raw(struct gpio_desc *desc, int value)
|
|
{
|
|
- if (!desc || !desc->chip) {
|
|
- pr_warn("%s: invalid GPIO\n", __func__);
|
|
- return -EINVAL;
|
|
- }
|
|
+ VALIDATE_DESC(desc);
|
|
return _gpiod_direction_output_raw(desc, value);
|
|
}
|
|
EXPORT_SYMBOL_GPL(gpiod_direction_output_raw);
|
|
@@ -1189,10 +2352,7 @@ EXPORT_SYMBOL_GPL(gpiod_direction_output_raw);
|
|
*/
|
|
int gpiod_direction_output(struct gpio_desc *desc, int value)
|
|
{
|
|
- if (!desc || !desc->chip) {
|
|
- pr_warn("%s: invalid GPIO\n", __func__);
|
|
- return -EINVAL;
|
|
- }
|
|
+ VALIDATE_DESC(desc);
|
|
if (test_bit(FLAG_ACTIVE_LOW, &desc->flags))
|
|
value = !value;
|
|
return _gpiod_direction_output_raw(desc, value);
|
|
@@ -1211,12 +2371,8 @@ int gpiod_set_debounce(struct gpio_desc *desc, unsigned debounce)
|
|
{
|
|
struct gpio_chip *chip;
|
|
|
|
- if (!desc || !desc->chip) {
|
|
- pr_warn("%s: invalid GPIO\n", __func__);
|
|
- return -EINVAL;
|
|
- }
|
|
-
|
|
- chip = desc->chip;
|
|
+ VALIDATE_DESC(desc);
|
|
+ chip = desc->gdev->chip;
|
|
if (!chip->set || !chip->set_debounce) {
|
|
gpiod_dbg(desc,
|
|
"%s: missing set() or set_debounce() operations\n",
|
|
@@ -1236,6 +2392,7 @@ EXPORT_SYMBOL_GPL(gpiod_set_debounce);
|
|
*/
|
|
int gpiod_is_active_low(const struct gpio_desc *desc)
|
|
{
|
|
+ VALIDATE_DESC(desc);
|
|
return test_bit(FLAG_ACTIVE_LOW, &desc->flags);
|
|
}
|
|
EXPORT_SYMBOL_GPL(gpiod_is_active_low);
|
|
@@ -1268,16 +2425,10 @@ static int _gpiod_get_raw_value(const struct gpio_desc *desc)
|
|
int offset;
|
|
int value;
|
|
|
|
- chip = desc->chip;
|
|
+ chip = desc->gdev->chip;
|
|
offset = gpio_chip_hwgpio(desc);
|
|
value = chip->get ? chip->get(chip, offset) : -EIO;
|
|
- /*
|
|
- * FIXME: fix all drivers to clamp to [0,1] or return negative,
|
|
- * then change this to:
|
|
- * value = value < 0 ? value : !!value;
|
|
- * so we can properly propagate error codes.
|
|
- */
|
|
- value = !!value;
|
|
+ value = value < 0 ? value : !!value;
|
|
trace_gpio_value(desc_to_gpio(desc), 1, value);
|
|
return value;
|
|
}
|
|
@@ -1294,10 +2445,9 @@ static int _gpiod_get_raw_value(const struct gpio_desc *desc)
|
|
*/
|
|
int gpiod_get_raw_value(const struct gpio_desc *desc)
|
|
{
|
|
- if (!desc)
|
|
- return 0;
|
|
- /* Should be using gpio_get_value_cansleep() */
|
|
- WARN_ON(desc->chip->can_sleep);
|
|
+ VALIDATE_DESC(desc);
|
|
+ /* Should be using gpiod_get_raw_value_cansleep() */
|
|
+ WARN_ON(desc->gdev->chip->can_sleep);
|
|
return _gpiod_get_raw_value(desc);
|
|
}
|
|
EXPORT_SYMBOL_GPL(gpiod_get_raw_value);
|
|
@@ -1315,10 +2465,10 @@ EXPORT_SYMBOL_GPL(gpiod_get_raw_value);
|
|
int gpiod_get_value(const struct gpio_desc *desc)
|
|
{
|
|
int value;
|
|
- if (!desc)
|
|
- return 0;
|
|
- /* Should be using gpio_get_value_cansleep() */
|
|
- WARN_ON(desc->chip->can_sleep);
|
|
+
|
|
+ VALIDATE_DESC(desc);
|
|
+ /* Should be using gpiod_get_value_cansleep() */
|
|
+ WARN_ON(desc->gdev->chip->can_sleep);
|
|
|
|
value = _gpiod_get_raw_value(desc);
|
|
if (value < 0)
|
|
@@ -1339,7 +2489,7 @@ EXPORT_SYMBOL_GPL(gpiod_get_value);
|
|
static void _gpio_set_open_drain_value(struct gpio_desc *desc, bool value)
|
|
{
|
|
int err = 0;
|
|
- struct gpio_chip *chip = desc->chip;
|
|
+ struct gpio_chip *chip = desc->gdev->chip;
|
|
int offset = gpio_chip_hwgpio(desc);
|
|
|
|
if (value) {
|
|
@@ -1366,7 +2516,7 @@ static void _gpio_set_open_drain_value(struct gpio_desc *desc, bool value)
|
|
static void _gpio_set_open_source_value(struct gpio_desc *desc, bool value)
|
|
{
|
|
int err = 0;
|
|
- struct gpio_chip *chip = desc->chip;
|
|
+ struct gpio_chip *chip = desc->gdev->chip;
|
|
int offset = gpio_chip_hwgpio(desc);
|
|
|
|
if (value) {
|
|
@@ -1389,7 +2539,7 @@ static void _gpiod_set_raw_value(struct gpio_desc *desc, bool value)
|
|
{
|
|
struct gpio_chip *chip;
|
|
|
|
- chip = desc->chip;
|
|
+ chip = desc->gdev->chip;
|
|
trace_gpio_value(desc_to_gpio(desc), 0, value);
|
|
if (test_bit(FLAG_OPEN_DRAIN, &desc->flags))
|
|
_gpio_set_open_drain_value(desc, value);
|
|
@@ -1429,15 +2579,15 @@ static void gpio_chip_set_multiple(struct gpio_chip *chip,
|
|
}
|
|
}
|
|
|
|
-static void gpiod_set_array_value_priv(bool raw, bool can_sleep,
|
|
- unsigned int array_size,
|
|
- struct gpio_desc **desc_array,
|
|
- int *value_array)
|
|
+void gpiod_set_array_value_complex(bool raw, bool can_sleep,
|
|
+ unsigned int array_size,
|
|
+ struct gpio_desc **desc_array,
|
|
+ int *value_array)
|
|
{
|
|
int i = 0;
|
|
|
|
while (i < array_size) {
|
|
- struct gpio_chip *chip = desc_array[i]->chip;
|
|
+ struct gpio_chip *chip = desc_array[i]->gdev->chip;
|
|
unsigned long mask[BITS_TO_LONGS(chip->ngpio)];
|
|
unsigned long bits[BITS_TO_LONGS(chip->ngpio)];
|
|
int count = 0;
|
|
@@ -1471,7 +2621,8 @@ static void gpiod_set_array_value_priv(bool raw, bool can_sleep,
|
|
count++;
|
|
}
|
|
i++;
|
|
- } while ((i < array_size) && (desc_array[i]->chip == chip));
|
|
+ } while ((i < array_size) &&
|
|
+ (desc_array[i]->gdev->chip == chip));
|
|
/* push collected bits to outputs */
|
|
if (count != 0)
|
|
gpio_chip_set_multiple(chip, mask, bits);
|
|
@@ -1491,10 +2642,9 @@ static void gpiod_set_array_value_priv(bool raw, bool can_sleep,
|
|
*/
|
|
void gpiod_set_raw_value(struct gpio_desc *desc, int value)
|
|
{
|
|
- if (!desc)
|
|
- return;
|
|
- /* Should be using gpio_set_value_cansleep() */
|
|
- WARN_ON(desc->chip->can_sleep);
|
|
+ VALIDATE_DESC_VOID(desc);
|
|
+ /* Should be using gpiod_set_raw_value_cansleep() */
|
|
+ WARN_ON(desc->gdev->chip->can_sleep);
|
|
_gpiod_set_raw_value(desc, value);
|
|
}
|
|
EXPORT_SYMBOL_GPL(gpiod_set_raw_value);
|
|
@@ -1512,10 +2662,9 @@ EXPORT_SYMBOL_GPL(gpiod_set_raw_value);
|
|
*/
|
|
void gpiod_set_value(struct gpio_desc *desc, int value)
|
|
{
|
|
- if (!desc)
|
|
- return;
|
|
- /* Should be using gpio_set_value_cansleep() */
|
|
- WARN_ON(desc->chip->can_sleep);
|
|
+ VALIDATE_DESC_VOID(desc);
|
|
+ /* Should be using gpiod_set_value_cansleep() */
|
|
+ WARN_ON(desc->gdev->chip->can_sleep);
|
|
if (test_bit(FLAG_ACTIVE_LOW, &desc->flags))
|
|
value = !value;
|
|
_gpiod_set_raw_value(desc, value);
|
|
@@ -1539,8 +2688,8 @@ void gpiod_set_raw_array_value(unsigned int array_size,
|
|
{
|
|
if (!desc_array)
|
|
return;
|
|
- gpiod_set_array_value_priv(true, false, array_size, desc_array,
|
|
- value_array);
|
|
+ gpiod_set_array_value_complex(true, false, array_size, desc_array,
|
|
+ value_array);
|
|
}
|
|
EXPORT_SYMBOL_GPL(gpiod_set_raw_array_value);
|
|
|
|
@@ -1561,8 +2710,8 @@ void gpiod_set_array_value(unsigned int array_size,
|
|
{
|
|
if (!desc_array)
|
|
return;
|
|
- gpiod_set_array_value_priv(false, false, array_size, desc_array,
|
|
- value_array);
|
|
+ gpiod_set_array_value_complex(false, false, array_size, desc_array,
|
|
+ value_array);
|
|
}
|
|
EXPORT_SYMBOL_GPL(gpiod_set_array_value);
|
|
|
|
@@ -1573,9 +2722,8 @@ EXPORT_SYMBOL_GPL(gpiod_set_array_value);
|
|
*/
|
|
int gpiod_cansleep(const struct gpio_desc *desc)
|
|
{
|
|
- if (!desc)
|
|
- return 0;
|
|
- return desc->chip->can_sleep;
|
|
+ VALIDATE_DESC(desc);
|
|
+ return desc->gdev->chip->can_sleep;
|
|
}
|
|
EXPORT_SYMBOL_GPL(gpiod_cansleep);
|
|
|
|
@@ -1588,14 +2736,29 @@ EXPORT_SYMBOL_GPL(gpiod_cansleep);
|
|
*/
|
|
int gpiod_to_irq(const struct gpio_desc *desc)
|
|
{
|
|
- struct gpio_chip *chip;
|
|
- int offset;
|
|
+ struct gpio_chip *chip;
|
|
+ int offset;
|
|
|
|
- if (!desc)
|
|
+ /*
|
|
+ * Cannot VALIDATE_DESC() here as gpiod_to_irq() consumer semantics
|
|
+ * requires this function to not return zero on an invalid descriptor
|
|
+ * but rather a negative error number.
|
|
+ */
|
|
+ if (!desc || IS_ERR(desc) || !desc->gdev || !desc->gdev->chip)
|
|
return -EINVAL;
|
|
- chip = desc->chip;
|
|
+
|
|
+ chip = desc->gdev->chip;
|
|
offset = gpio_chip_hwgpio(desc);
|
|
- return chip->to_irq ? chip->to_irq(chip, offset) : -ENXIO;
|
|
+ if (chip->to_irq) {
|
|
+ int retirq = chip->to_irq(chip, offset);
|
|
+
|
|
+ /* Zero means NO_IRQ */
|
|
+ if (!retirq)
|
|
+ return -ENXIO;
|
|
+
|
|
+ return retirq;
|
|
+ }
|
|
+ return -ENXIO;
|
|
}
|
|
EXPORT_SYMBOL_GPL(gpiod_to_irq);
|
|
|
|
@@ -1609,17 +2772,33 @@ EXPORT_SYMBOL_GPL(gpiod_to_irq);
|
|
*/
|
|
int gpiochip_lock_as_irq(struct gpio_chip *chip, unsigned int offset)
|
|
{
|
|
- if (offset >= chip->ngpio)
|
|
- return -EINVAL;
|
|
+ struct gpio_desc *desc;
|
|
+
|
|
+ desc = gpiochip_get_desc(chip, offset);
|
|
+ if (IS_ERR(desc))
|
|
+ return PTR_ERR(desc);
|
|
+
|
|
+ /*
|
|
+ * If it's fast: flush the direction setting if something changed
|
|
+ * behind our back
|
|
+ */
|
|
+ if (!chip->can_sleep && chip->get_direction) {
|
|
+ int dir = chip->get_direction(chip, offset);
|
|
+
|
|
+ if (dir)
|
|
+ clear_bit(FLAG_IS_OUT, &desc->flags);
|
|
+ else
|
|
+ set_bit(FLAG_IS_OUT, &desc->flags);
|
|
+ }
|
|
|
|
- if (test_bit(FLAG_IS_OUT, &chip->desc[offset].flags)) {
|
|
+ if (test_bit(FLAG_IS_OUT, &desc->flags)) {
|
|
chip_err(chip,
|
|
"%s: tried to flag a GPIO set as output for IRQ\n",
|
|
__func__);
|
|
return -EIO;
|
|
}
|
|
|
|
- set_bit(FLAG_USED_AS_IRQ, &chip->desc[offset].flags);
|
|
+ set_bit(FLAG_USED_AS_IRQ, &desc->flags);
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL_GPL(gpiochip_lock_as_irq);
|
|
@@ -1637,10 +2816,37 @@ void gpiochip_unlock_as_irq(struct gpio_chip *chip, unsigned int offset)
|
|
if (offset >= chip->ngpio)
|
|
return;
|
|
|
|
- clear_bit(FLAG_USED_AS_IRQ, &chip->desc[offset].flags);
|
|
+ clear_bit(FLAG_USED_AS_IRQ, &chip->gpiodev->descs[offset].flags);
|
|
}
|
|
EXPORT_SYMBOL_GPL(gpiochip_unlock_as_irq);
|
|
|
|
+bool gpiochip_line_is_irq(struct gpio_chip *chip, unsigned int offset)
|
|
+{
|
|
+ if (offset >= chip->ngpio)
|
|
+ return false;
|
|
+
|
|
+ return test_bit(FLAG_USED_AS_IRQ, &chip->gpiodev->descs[offset].flags);
|
|
+}
|
|
+EXPORT_SYMBOL_GPL(gpiochip_line_is_irq);
|
|
+
|
|
+bool gpiochip_line_is_open_drain(struct gpio_chip *chip, unsigned int offset)
|
|
+{
|
|
+ if (offset >= chip->ngpio)
|
|
+ return false;
|
|
+
|
|
+ return test_bit(FLAG_OPEN_DRAIN, &chip->gpiodev->descs[offset].flags);
|
|
+}
|
|
+EXPORT_SYMBOL_GPL(gpiochip_line_is_open_drain);
|
|
+
|
|
+bool gpiochip_line_is_open_source(struct gpio_chip *chip, unsigned int offset)
|
|
+{
|
|
+ if (offset >= chip->ngpio)
|
|
+ return false;
|
|
+
|
|
+ return test_bit(FLAG_OPEN_SOURCE, &chip->gpiodev->descs[offset].flags);
|
|
+}
|
|
+EXPORT_SYMBOL_GPL(gpiochip_line_is_open_source);
|
|
+
|
|
/**
|
|
* gpiod_get_raw_value_cansleep() - return a gpio's raw value
|
|
* @desc: gpio whose value will be returned
|
|
@@ -1653,8 +2859,7 @@ EXPORT_SYMBOL_GPL(gpiochip_unlock_as_irq);
|
|
int gpiod_get_raw_value_cansleep(const struct gpio_desc *desc)
|
|
{
|
|
might_sleep_if(extra_checks);
|
|
- if (!desc)
|
|
- return 0;
|
|
+ VALIDATE_DESC(desc);
|
|
return _gpiod_get_raw_value(desc);
|
|
}
|
|
EXPORT_SYMBOL_GPL(gpiod_get_raw_value_cansleep);
|
|
@@ -1673,9 +2878,7 @@ int gpiod_get_value_cansleep(const struct gpio_desc *desc)
|
|
int value;
|
|
|
|
might_sleep_if(extra_checks);
|
|
- if (!desc)
|
|
- return 0;
|
|
-
|
|
+ VALIDATE_DESC(desc);
|
|
value = _gpiod_get_raw_value(desc);
|
|
if (value < 0)
|
|
return value;
|
|
@@ -1700,8 +2903,7 @@ EXPORT_SYMBOL_GPL(gpiod_get_value_cansleep);
|
|
void gpiod_set_raw_value_cansleep(struct gpio_desc *desc, int value)
|
|
{
|
|
might_sleep_if(extra_checks);
|
|
- if (!desc)
|
|
- return;
|
|
+ VALIDATE_DESC_VOID(desc);
|
|
_gpiod_set_raw_value(desc, value);
|
|
}
|
|
EXPORT_SYMBOL_GPL(gpiod_set_raw_value_cansleep);
|
|
@@ -1719,9 +2921,7 @@ EXPORT_SYMBOL_GPL(gpiod_set_raw_value_cansleep);
|
|
void gpiod_set_value_cansleep(struct gpio_desc *desc, int value)
|
|
{
|
|
might_sleep_if(extra_checks);
|
|
- if (!desc)
|
|
- return;
|
|
-
|
|
+ VALIDATE_DESC_VOID(desc);
|
|
if (test_bit(FLAG_ACTIVE_LOW, &desc->flags))
|
|
value = !value;
|
|
_gpiod_set_raw_value(desc, value);
|
|
@@ -1746,8 +2946,8 @@ void gpiod_set_raw_array_value_cansleep(unsigned int array_size,
|
|
might_sleep_if(extra_checks);
|
|
if (!desc_array)
|
|
return;
|
|
- gpiod_set_array_value_priv(true, true, array_size, desc_array,
|
|
- value_array);
|
|
+ gpiod_set_array_value_complex(true, true, array_size, desc_array,
|
|
+ value_array);
|
|
}
|
|
EXPORT_SYMBOL_GPL(gpiod_set_raw_array_value_cansleep);
|
|
|
|
@@ -1769,8 +2969,8 @@ void gpiod_set_array_value_cansleep(unsigned int array_size,
|
|
might_sleep_if(extra_checks);
|
|
if (!desc_array)
|
|
return;
|
|
- gpiod_set_array_value_priv(false, true, array_size, desc_array,
|
|
- value_array);
|
|
+ gpiod_set_array_value_complex(false, true, array_size, desc_array,
|
|
+ value_array);
|
|
}
|
|
EXPORT_SYMBOL_GPL(gpiod_set_array_value_cansleep);
|
|
|
|
@@ -1800,83 +3000,6 @@ void gpiod_remove_lookup_table(struct gpiod_lookup_table *table)
|
|
mutex_unlock(&gpio_lookup_lock);
|
|
}
|
|
|
|
-static struct gpio_desc *of_find_gpio(struct device *dev, const char *con_id,
|
|
- unsigned int idx,
|
|
- enum gpio_lookup_flags *flags)
|
|
-{
|
|
- char prop_name[32]; /* 32 is max size of property name */
|
|
- enum of_gpio_flags of_flags;
|
|
- struct gpio_desc *desc;
|
|
- unsigned int i;
|
|
-
|
|
- for (i = 0; i < ARRAY_SIZE(gpio_suffixes); i++) {
|
|
- if (con_id)
|
|
- snprintf(prop_name, sizeof(prop_name), "%s-%s", con_id,
|
|
- gpio_suffixes[i]);
|
|
- else
|
|
- snprintf(prop_name, sizeof(prop_name), "%s",
|
|
- gpio_suffixes[i]);
|
|
-
|
|
- desc = of_get_named_gpiod_flags(dev->of_node, prop_name, idx,
|
|
- &of_flags);
|
|
- if (!IS_ERR(desc) || (PTR_ERR(desc) == -EPROBE_DEFER))
|
|
- break;
|
|
- }
|
|
-
|
|
- if (IS_ERR(desc))
|
|
- return desc;
|
|
-
|
|
- if (of_flags & OF_GPIO_ACTIVE_LOW)
|
|
- *flags |= GPIO_ACTIVE_LOW;
|
|
-
|
|
- if (of_flags & OF_GPIO_SINGLE_ENDED) {
|
|
- if (of_flags & OF_GPIO_ACTIVE_LOW)
|
|
- *flags |= GPIO_OPEN_DRAIN;
|
|
- else
|
|
- *flags |= GPIO_OPEN_SOURCE;
|
|
- }
|
|
-
|
|
- return desc;
|
|
-}
|
|
-
|
|
-static struct gpio_desc *acpi_find_gpio(struct device *dev, const char *con_id,
|
|
- unsigned int idx,
|
|
- enum gpio_lookup_flags *flags)
|
|
-{
|
|
- struct acpi_device *adev = ACPI_COMPANION(dev);
|
|
- struct acpi_gpio_info info;
|
|
- struct gpio_desc *desc;
|
|
- char propname[32];
|
|
- int i;
|
|
-
|
|
- /* Try first from _DSD */
|
|
- for (i = 0; i < ARRAY_SIZE(gpio_suffixes); i++) {
|
|
- if (con_id && strcmp(con_id, "gpios")) {
|
|
- snprintf(propname, sizeof(propname), "%s-%s",
|
|
- con_id, gpio_suffixes[i]);
|
|
- } else {
|
|
- snprintf(propname, sizeof(propname), "%s",
|
|
- gpio_suffixes[i]);
|
|
- }
|
|
-
|
|
- desc = acpi_get_gpiod_by_index(adev, propname, idx, &info);
|
|
- if (!IS_ERR(desc) || (PTR_ERR(desc) == -EPROBE_DEFER))
|
|
- break;
|
|
- }
|
|
-
|
|
- /* Then from plain _CRS GPIOs */
|
|
- if (IS_ERR(desc)) {
|
|
- desc = acpi_get_gpiod_by_index(adev, NULL, idx, &info);
|
|
- if (IS_ERR(desc))
|
|
- return desc;
|
|
- }
|
|
-
|
|
- if (info.active_low)
|
|
- *flags |= GPIO_ACTIVE_LOW;
|
|
-
|
|
- return desc;
|
|
-}
|
|
-
|
|
static struct gpiod_lookup_table *gpiod_find_lookup_table(struct device *dev)
|
|
{
|
|
const char *dev_id = dev ? dev_name(dev) : NULL;
|
|
@@ -2117,6 +3240,8 @@ struct gpio_desc *__must_check gpiod_get_index(struct device *dev,
|
|
struct gpio_desc *desc = NULL;
|
|
int status;
|
|
enum gpio_lookup_flags lookupflags = 0;
|
|
+ /* Maybe we have a device name, maybe not */
|
|
+ const char *devname = dev ? dev_name(dev) : "?";
|
|
|
|
dev_dbg(dev, "GPIO lookup for consumer %s\n", con_id);
|
|
|
|
@@ -2127,7 +3252,7 @@ struct gpio_desc *__must_check gpiod_get_index(struct device *dev,
|
|
desc = of_find_gpio(dev, con_id, idx, &lookupflags);
|
|
} else if (ACPI_COMPANION(dev)) {
|
|
dev_dbg(dev, "using ACPI for GPIO lookup\n");
|
|
- desc = acpi_find_gpio(dev, con_id, idx, &lookupflags);
|
|
+ desc = acpi_find_gpio(dev, con_id, idx, flags, &lookupflags);
|
|
}
|
|
}
|
|
|
|
@@ -2145,7 +3270,11 @@ struct gpio_desc *__must_check gpiod_get_index(struct device *dev,
|
|
return desc;
|
|
}
|
|
|
|
- status = gpiod_request(desc, con_id);
|
|
+ /*
|
|
+ * If a connection label was passed use that, else attempt to use
|
|
+ * the device name as label
|
|
+ */
|
|
+ status = gpiod_request(desc, con_id ? con_id : devname);
|
|
if (status < 0)
|
|
return ERR_PTR(status);
|
|
|
|
@@ -2199,7 +3328,7 @@ struct gpio_desc *fwnode_get_named_gpiod(struct fwnode_handle *fwnode,
|
|
|
|
desc = acpi_node_get_gpiod(fwnode, propname, 0, &info);
|
|
if (!IS_ERR(desc))
|
|
- active_low = info.active_low;
|
|
+ active_low = info.polarity == GPIO_ACTIVE_LOW;
|
|
}
|
|
|
|
if (IS_ERR(desc))
|
|
@@ -2273,15 +3402,16 @@ int gpiod_hog(struct gpio_desc *desc, const char *name,
|
|
|
|
local_desc = gpiochip_request_own_desc(chip, hwnum, name);
|
|
if (IS_ERR(local_desc)) {
|
|
- pr_err("requesting hog GPIO %s (chip %s, offset %d) failed\n",
|
|
- name, chip->label, hwnum);
|
|
- return PTR_ERR(local_desc);
|
|
+ status = PTR_ERR(local_desc);
|
|
+ pr_err("requesting hog GPIO %s (chip %s, offset %d) failed, %d\n",
|
|
+ name, chip->label, hwnum, status);
|
|
+ return status;
|
|
}
|
|
|
|
status = gpiod_configure_flags(desc, name, lflags, dflags);
|
|
if (status < 0) {
|
|
- pr_err("setup of hog GPIO %s (chip %s, offset %d) failed\n",
|
|
- name, chip->label, hwnum);
|
|
+ pr_err("setup of hog GPIO %s (chip %s, offset %d) failed, %d\n",
|
|
+ name, chip->label, hwnum, status);
|
|
gpiochip_free_own_desc(desc);
|
|
return status;
|
|
}
|
|
@@ -2309,8 +3439,8 @@ static void gpiochip_free_hogs(struct gpio_chip *chip)
|
|
int id;
|
|
|
|
for (id = 0; id < chip->ngpio; id++) {
|
|
- if (test_bit(FLAG_IS_HOGGED, &chip->desc[id].flags))
|
|
- gpiochip_free_own_desc(&chip->desc[id]);
|
|
+ if (test_bit(FLAG_IS_HOGGED, &chip->gpiodev->descs[id].flags))
|
|
+ gpiochip_free_own_desc(&chip->gpiodev->descs[id]);
|
|
}
|
|
}
|
|
|
|
@@ -2407,17 +3537,41 @@ void gpiod_put_array(struct gpio_descs *descs)
|
|
}
|
|
EXPORT_SYMBOL_GPL(gpiod_put_array);
|
|
|
|
+static int __init gpiolib_dev_init(void)
|
|
+{
|
|
+ int ret;
|
|
+
|
|
+ /* Register GPIO sysfs bus */
|
|
+ ret = bus_register(&gpio_bus_type);
|
|
+ if (ret < 0) {
|
|
+ pr_err("gpiolib: could not register GPIO bus type\n");
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ ret = alloc_chrdev_region(&gpio_devt, 0, GPIO_DEV_MAX, "gpiochip");
|
|
+ if (ret < 0) {
|
|
+ pr_err("gpiolib: failed to allocate char dev region\n");
|
|
+ bus_unregister(&gpio_bus_type);
|
|
+ } else {
|
|
+ gpiolib_initialized = true;
|
|
+ gpiochip_setup_devs();
|
|
+ }
|
|
+ return ret;
|
|
+}
|
|
+core_initcall(gpiolib_dev_init);
|
|
+
|
|
#ifdef CONFIG_DEBUG_FS
|
|
|
|
-static void gpiolib_dbg_show(struct seq_file *s, struct gpio_chip *chip)
|
|
+static void gpiolib_dbg_show(struct seq_file *s, struct gpio_device *gdev)
|
|
{
|
|
unsigned i;
|
|
- unsigned gpio = chip->base;
|
|
- struct gpio_desc *gdesc = &chip->desc[0];
|
|
+ struct gpio_chip *chip = gdev->chip;
|
|
+ unsigned gpio = gdev->base;
|
|
+ struct gpio_desc *gdesc = &gdev->descs[0];
|
|
int is_out;
|
|
int is_irq;
|
|
|
|
- for (i = 0; i < chip->ngpio; i++, gpio++, gdesc++) {
|
|
+ for (i = 0; i < gdev->ngpio; i++, gpio++, gdesc++) {
|
|
if (!test_bit(FLAG_REQUESTED, &gdesc->flags)) {
|
|
if (gdesc->name) {
|
|
seq_printf(s, " gpio-%-3d (%-20.20s)\n",
|
|
@@ -2443,16 +3597,16 @@ static void gpiolib_dbg_show(struct seq_file *s, struct gpio_chip *chip)
|
|
static void *gpiolib_seq_start(struct seq_file *s, loff_t *pos)
|
|
{
|
|
unsigned long flags;
|
|
- struct gpio_chip *chip = NULL;
|
|
+ struct gpio_device *gdev = NULL;
|
|
loff_t index = *pos;
|
|
|
|
s->private = "";
|
|
|
|
spin_lock_irqsave(&gpio_lock, flags);
|
|
- list_for_each_entry(chip, &gpio_chips, list)
|
|
+ list_for_each_entry(gdev, &gpio_devices, list)
|
|
if (index-- == 0) {
|
|
spin_unlock_irqrestore(&gpio_lock, flags);
|
|
- return chip;
|
|
+ return gdev;
|
|
}
|
|
spin_unlock_irqrestore(&gpio_lock, flags);
|
|
|
|
@@ -2462,14 +3616,14 @@ static void *gpiolib_seq_start(struct seq_file *s, loff_t *pos)
|
|
static void *gpiolib_seq_next(struct seq_file *s, void *v, loff_t *pos)
|
|
{
|
|
unsigned long flags;
|
|
- struct gpio_chip *chip = v;
|
|
+ struct gpio_device *gdev = v;
|
|
void *ret = NULL;
|
|
|
|
spin_lock_irqsave(&gpio_lock, flags);
|
|
- if (list_is_last(&chip->list, &gpio_chips))
|
|
+ if (list_is_last(&gdev->list, &gpio_devices))
|
|
ret = NULL;
|
|
else
|
|
- ret = list_entry(chip->list.next, struct gpio_chip, list);
|
|
+ ret = list_entry(gdev->list.next, struct gpio_device, list);
|
|
spin_unlock_irqrestore(&gpio_lock, flags);
|
|
|
|
s->private = "\n";
|
|
@@ -2484,15 +3638,24 @@ static void gpiolib_seq_stop(struct seq_file *s, void *v)
|
|
|
|
static int gpiolib_seq_show(struct seq_file *s, void *v)
|
|
{
|
|
- struct gpio_chip *chip = v;
|
|
- struct device *dev;
|
|
+ struct gpio_device *gdev = v;
|
|
+ struct gpio_chip *chip = gdev->chip;
|
|
+ struct device *parent;
|
|
+
|
|
+ if (!chip) {
|
|
+ seq_printf(s, "%s%s: (dangling chip)", (char *)s->private,
|
|
+ dev_name(&gdev->dev));
|
|
+ return 0;
|
|
+ }
|
|
|
|
- seq_printf(s, "%sGPIOs %d-%d", (char *)s->private,
|
|
- chip->base, chip->base + chip->ngpio - 1);
|
|
- dev = chip->dev;
|
|
- if (dev)
|
|
- seq_printf(s, ", %s/%s", dev->bus ? dev->bus->name : "no-bus",
|
|
- dev_name(dev));
|
|
+ seq_printf(s, "%s%s: GPIOs %d-%d", (char *)s->private,
|
|
+ dev_name(&gdev->dev),
|
|
+ gdev->base, gdev->base + gdev->ngpio - 1);
|
|
+ parent = chip->parent;
|
|
+ if (parent)
|
|
+ seq_printf(s, ", parent: %s/%s",
|
|
+ parent->bus ? parent->bus->name : "no-bus",
|
|
+ dev_name(parent));
|
|
if (chip->label)
|
|
seq_printf(s, ", %s", chip->label);
|
|
if (chip->can_sleep)
|
|
@@ -2502,7 +3665,7 @@ static int gpiolib_seq_show(struct seq_file *s, void *v)
|
|
if (chip->dbg_show)
|
|
chip->dbg_show(s, chip);
|
|
else
|
|
- gpiolib_dbg_show(s, chip);
|
|
+ gpiolib_dbg_show(s, gdev);
|
|
|
|
return 0;
|
|
}
|
|
diff --git a/qca/src/linux-4.4/drivers/gpio/gpiolib.h b/qca/src/linux-4.4/drivers/gpio/gpiolib.h
|
|
index 98ab08c..6c4d728 100644
|
|
--- a/qca/src/linux-4.4/drivers/gpio/gpiolib.h
|
|
+++ b/qca/src/linux-4.4/drivers/gpio/gpiolib.h
|
|
@@ -12,25 +12,106 @@
|
|
#ifndef GPIOLIB_H
|
|
#define GPIOLIB_H
|
|
|
|
+#include <linux/gpio/driver.h>
|
|
#include <linux/err.h>
|
|
#include <linux/device.h>
|
|
+#include <linux/module.h>
|
|
+#include <linux/cdev.h>
|
|
|
|
enum of_gpio_flags;
|
|
-
|
|
+enum gpiod_flags;
|
|
+enum gpio_lookup_flags;
|
|
struct acpi_device;
|
|
|
|
/**
|
|
+ * struct gpio_device - internal state container for GPIO devices
|
|
+ * @id: numerical ID number for the GPIO chip
|
|
+ * @dev: the GPIO device struct
|
|
+ * @chrdev: character device for the GPIO device
|
|
+ * @mockdev: class device used by the deprecated sysfs interface (may be
|
|
+ * NULL)
|
|
+ * @owner: helps prevent removal of modules exporting active GPIOs
|
|
+ * @chip: pointer to the corresponding gpiochip, holding static
|
|
+ * data for this device
|
|
+ * @descs: array of ngpio descriptors.
|
|
+ * @ngpio: the number of GPIO lines on this GPIO device, equal to the size
|
|
+ * of the @descs array.
|
|
+ * @base: GPIO base in the DEPRECATED global Linux GPIO numberspace, assigned
|
|
+ * at device creation time.
|
|
+ * @label: a descriptive name for the GPIO device, such as the part number
|
|
+ * or name of the IP component in a System on Chip.
|
|
+ * @data: per-instance data assigned by the driver
|
|
+ * @list: links gpio_device:s together for traversal
|
|
+ *
|
|
+ * This state container holds most of the runtime variable data
|
|
+ * for a GPIO device and can hold references and live on after the
|
|
+ * GPIO chip has been removed, if it is still being used from
|
|
+ * userspace.
|
|
+ */
|
|
+struct gpio_device {
|
|
+ int id;
|
|
+ struct device dev;
|
|
+ struct cdev chrdev;
|
|
+ struct device *mockdev;
|
|
+ struct module *owner;
|
|
+ struct gpio_chip *chip;
|
|
+ struct gpio_desc *descs;
|
|
+ int base;
|
|
+ u16 ngpio;
|
|
+ char *label;
|
|
+ void *data;
|
|
+ struct list_head list;
|
|
+
|
|
+#ifdef CONFIG_PINCTRL
|
|
+ /*
|
|
+ * If CONFIG_PINCTRL is enabled, then gpio controllers can optionally
|
|
+ * describe the actual pin range which they serve in an SoC. This
|
|
+ * information would be used by pinctrl subsystem to configure
|
|
+ * corresponding pins for gpio usage.
|
|
+ */
|
|
+ struct list_head pin_ranges;
|
|
+#endif
|
|
+};
|
|
+
|
|
+/**
|
|
* struct acpi_gpio_info - ACPI GPIO specific information
|
|
* @gpioint: if %true this GPIO is of type GpioInt otherwise type is GpioIo
|
|
* @active_low: in case of @gpioint, the pin is active low
|
|
*/
|
|
struct acpi_gpio_info {
|
|
bool gpioint;
|
|
- bool active_low;
|
|
+ int polarity;
|
|
+ int triggering;
|
|
};
|
|
|
|
/* gpio suffixes used for ACPI and device tree lookup */
|
|
-static const char * const gpio_suffixes[] = { "gpios", "gpio" };
|
|
+static __maybe_unused const char * const gpio_suffixes[] = { "gpios", "gpio" };
|
|
+
|
|
+#ifdef CONFIG_OF_GPIO
|
|
+struct gpio_desc *of_find_gpio(struct device *dev,
|
|
+ const char *con_id,
|
|
+ unsigned int idx,
|
|
+ enum gpio_lookup_flags *flags);
|
|
+struct gpio_desc *of_get_named_gpiod_flags(struct device_node *np,
|
|
+ const char *list_name, int index, enum of_gpio_flags *flags);
|
|
+int of_gpiochip_add(struct gpio_chip *gc);
|
|
+void of_gpiochip_remove(struct gpio_chip *gc);
|
|
+#else
|
|
+static inline struct gpio_desc *of_find_gpio(struct device *dev,
|
|
+ const char *con_id,
|
|
+ unsigned int idx,
|
|
+ enum gpio_lookup_flags *flags)
|
|
+{
|
|
+ return ERR_PTR(-ENOENT);
|
|
+}
|
|
+static inline struct gpio_desc *of_get_named_gpiod_flags(struct device_node *np,
|
|
+ const char *list_name, int index, enum of_gpio_flags *flags)
|
|
+{
|
|
+ return ERR_PTR(-ENOENT);
|
|
+}
|
|
+static inline int of_gpiochip_add(struct gpio_chip *gc) { return 0; }
|
|
+static inline void of_gpiochip_remove(struct gpio_chip *gc) { }
|
|
+#endif /* CONFIG_OF_GPIO */
|
|
|
|
#ifdef CONFIG_ACPI
|
|
void acpi_gpiochip_add(struct gpio_chip *chip);
|
|
@@ -39,14 +120,18 @@ void acpi_gpiochip_remove(struct gpio_chip *chip);
|
|
void acpi_gpiochip_request_interrupts(struct gpio_chip *chip);
|
|
void acpi_gpiochip_free_interrupts(struct gpio_chip *chip);
|
|
|
|
-struct gpio_desc *acpi_get_gpiod_by_index(struct acpi_device *adev,
|
|
- const char *propname, int index,
|
|
- struct acpi_gpio_info *info);
|
|
+struct gpio_desc *acpi_find_gpio(struct device *dev,
|
|
+ const char *con_id,
|
|
+ unsigned int idx,
|
|
+ enum gpiod_flags flags,
|
|
+ enum gpio_lookup_flags *lookupflags);
|
|
struct gpio_desc *acpi_node_get_gpiod(struct fwnode_handle *fwnode,
|
|
const char *propname, int index,
|
|
struct acpi_gpio_info *info);
|
|
|
|
int acpi_gpio_count(struct device *dev, const char *con_id);
|
|
+
|
|
+bool acpi_can_fallback_to_crs(struct acpi_device *adev, const char *con_id);
|
|
#else
|
|
static inline void acpi_gpiochip_add(struct gpio_chip *chip) { }
|
|
static inline void acpi_gpiochip_remove(struct gpio_chip *chip) { }
|
|
@@ -58,10 +143,11 @@ static inline void
|
|
acpi_gpiochip_free_interrupts(struct gpio_chip *chip) { }
|
|
|
|
static inline struct gpio_desc *
|
|
-acpi_get_gpiod_by_index(struct acpi_device *adev, const char *propname,
|
|
- int index, struct acpi_gpio_info *info)
|
|
+acpi_find_gpio(struct device *dev, const char *con_id,
|
|
+ unsigned int idx, enum gpiod_flags flags,
|
|
+ enum gpio_lookup_flags *lookupflags)
|
|
{
|
|
- return ERR_PTR(-ENOSYS);
|
|
+ return ERR_PTR(-ENOENT);
|
|
}
|
|
static inline struct gpio_desc *
|
|
acpi_node_get_gpiod(struct fwnode_handle *fwnode, const char *propname,
|
|
@@ -73,18 +159,25 @@ static inline int acpi_gpio_count(struct device *dev, const char *con_id)
|
|
{
|
|
return -ENODEV;
|
|
}
|
|
-#endif
|
|
|
|
-struct gpio_desc *of_get_named_gpiod_flags(struct device_node *np,
|
|
- const char *list_name, int index, enum of_gpio_flags *flags);
|
|
+static inline bool acpi_can_fallback_to_crs(struct acpi_device *adev,
|
|
+ const char *con_id)
|
|
+{
|
|
+ return false;
|
|
+}
|
|
+#endif
|
|
|
|
struct gpio_desc *gpiochip_get_desc(struct gpio_chip *chip, u16 hwnum);
|
|
+void gpiod_set_array_value_complex(bool raw, bool can_sleep,
|
|
+ unsigned int array_size,
|
|
+ struct gpio_desc **desc_array,
|
|
+ int *value_array);
|
|
|
|
extern struct spinlock gpio_lock;
|
|
-extern struct list_head gpio_chips;
|
|
+extern struct list_head gpio_devices;
|
|
|
|
struct gpio_desc {
|
|
- struct gpio_chip *chip;
|
|
+ struct gpio_device *gdev;
|
|
unsigned long flags;
|
|
/* flag symbols are bit numbers */
|
|
#define FLAG_REQUESTED 0
|
|
@@ -113,7 +206,7 @@ int gpiod_hog(struct gpio_desc *desc, const char *name,
|
|
*/
|
|
static int __maybe_unused gpio_chip_hwgpio(const struct gpio_desc *desc)
|
|
{
|
|
- return desc - &desc->chip->desc[0];
|
|
+ return desc - &desc->gdev->descs[0];
|
|
}
|
|
|
|
/* With descriptor prefix */
|
|
@@ -140,31 +233,31 @@ static int __maybe_unused gpio_chip_hwgpio(const struct gpio_desc *desc)
|
|
/* With chip prefix */
|
|
|
|
#define chip_emerg(chip, fmt, ...) \
|
|
- pr_emerg("GPIO chip %s: " fmt, chip->label, ##__VA_ARGS__)
|
|
+ dev_emerg(&chip->gpiodev->dev, "(%s): " fmt, chip->label, ##__VA_ARGS__)
|
|
#define chip_crit(chip, fmt, ...) \
|
|
- pr_crit("GPIO chip %s: " fmt, chip->label, ##__VA_ARGS__)
|
|
+ dev_crit(&chip->gpiodev->dev, "(%s): " fmt, chip->label, ##__VA_ARGS__)
|
|
#define chip_err(chip, fmt, ...) \
|
|
- pr_err("GPIO chip %s: " fmt, chip->label, ##__VA_ARGS__)
|
|
+ dev_err(&chip->gpiodev->dev, "(%s): " fmt, chip->label, ##__VA_ARGS__)
|
|
#define chip_warn(chip, fmt, ...) \
|
|
- pr_warn("GPIO chip %s: " fmt, chip->label, ##__VA_ARGS__)
|
|
+ dev_warn(&chip->gpiodev->dev, "(%s): " fmt, chip->label, ##__VA_ARGS__)
|
|
#define chip_info(chip, fmt, ...) \
|
|
- pr_info("GPIO chip %s: " fmt, chip->label, ##__VA_ARGS__)
|
|
+ dev_info(&chip->gpiodev->dev, "(%s): " fmt, chip->label, ##__VA_ARGS__)
|
|
#define chip_dbg(chip, fmt, ...) \
|
|
- pr_debug("GPIO chip %s: " fmt, chip->label, ##__VA_ARGS__)
|
|
+ dev_dbg(&chip->gpiodev->dev, "(%s): " fmt, chip->label, ##__VA_ARGS__)
|
|
|
|
#ifdef CONFIG_GPIO_SYSFS
|
|
|
|
-int gpiochip_sysfs_register(struct gpio_chip *chip);
|
|
-void gpiochip_sysfs_unregister(struct gpio_chip *chip);
|
|
+int gpiochip_sysfs_register(struct gpio_device *gdev);
|
|
+void gpiochip_sysfs_unregister(struct gpio_device *gdev);
|
|
|
|
#else
|
|
|
|
-static inline int gpiochip_sysfs_register(struct gpio_chip *chip)
|
|
+static inline int gpiochip_sysfs_register(struct gpio_device *gdev)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
-static inline void gpiochip_sysfs_unregister(struct gpio_chip *chip)
|
|
+static inline void gpiochip_sysfs_unregister(struct gpio_device *gdev)
|
|
{
|
|
}
|
|
|
|
diff --git a/qca/src/linux-4.4/drivers/pinctrl/qcom/pinctrl-msm.c b/qca/src/linux-4.4/drivers/pinctrl/qcom/pinctrl-msm.c
|
|
index 45e7dbc..8fee210 100644
|
|
--- a/qca/src/linux-4.4/drivers/pinctrl/qcom/pinctrl-msm.c
|
|
+++ b/qca/src/linux-4.4/drivers/pinctrl/qcom/pinctrl-msm.c
|
|
@@ -854,7 +854,7 @@ static int msm_gpio_init(struct msm_pinctrl *pctrl)
|
|
chip->base = 0;
|
|
chip->ngpio = ngpio;
|
|
chip->label = dev_name(pctrl->dev);
|
|
- chip->dev = pctrl->dev;
|
|
+ chip->parent = pctrl->dev;
|
|
chip->owner = THIS_MODULE;
|
|
chip->of_node = pctrl->dev->of_node;
|
|
|
|
diff --git a/qca/src/linux-4.4/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c b/qca/src/linux-4.4/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c
|
|
index 6c42ca1..3e5ccc7 100644
|
|
--- a/qca/src/linux-4.4/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c
|
|
+++ b/qca/src/linux-4.4/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c
|
|
@@ -760,7 +760,7 @@ static int pmic_gpio_probe(struct platform_device *pdev)
|
|
}
|
|
|
|
state->chip = pmic_gpio_gpio_template;
|
|
- state->chip.dev = dev;
|
|
+ state->chip.parent = dev;
|
|
state->chip.base = -1;
|
|
state->chip.ngpio = npins;
|
|
state->chip.label = dev_name(dev);
|
|
diff --git a/qca/src/linux-4.4/drivers/pinctrl/qcom/pinctrl-spmi-mpp.c b/qca/src/linux-4.4/drivers/pinctrl/qcom/pinctrl-spmi-mpp.c
|
|
index 9ce0e30..69c14ba 100644
|
|
--- a/qca/src/linux-4.4/drivers/pinctrl/qcom/pinctrl-spmi-mpp.c
|
|
+++ b/qca/src/linux-4.4/drivers/pinctrl/qcom/pinctrl-spmi-mpp.c
|
|
@@ -862,7 +862,7 @@ static int pmic_mpp_probe(struct platform_device *pdev)
|
|
}
|
|
|
|
state->chip = pmic_mpp_gpio_template;
|
|
- state->chip.dev = dev;
|
|
+ state->chip.parent = dev;
|
|
state->chip.base = -1;
|
|
state->chip.ngpio = npins;
|
|
state->chip.label = dev_name(dev);
|
|
diff --git a/qca/src/linux-4.4/include/linux/gpio/driver.h b/qca/src/linux-4.4/include/linux/gpio/driver.h
|
|
index d1baebf..966d663 100644
|
|
--- a/qca/src/linux-4.4/include/linux/gpio/driver.h
|
|
+++ b/qca/src/linux-4.4/include/linux/gpio/driver.h
|
|
@@ -1,29 +1,42 @@
|
|
#ifndef __LINUX_GPIO_DRIVER_H
|
|
#define __LINUX_GPIO_DRIVER_H
|
|
|
|
+#include <linux/device.h>
|
|
#include <linux/types.h>
|
|
-#include <linux/module.h>
|
|
#include <linux/irq.h>
|
|
#include <linux/irqchip/chained_irq.h>
|
|
#include <linux/irqdomain.h>
|
|
#include <linux/lockdep.h>
|
|
#include <linux/pinctrl/pinctrl.h>
|
|
|
|
-struct device;
|
|
struct gpio_desc;
|
|
struct of_phandle_args;
|
|
struct device_node;
|
|
struct seq_file;
|
|
+struct gpio_device;
|
|
+struct module;
|
|
|
|
#ifdef CONFIG_GPIOLIB
|
|
|
|
/**
|
|
+ * enum single_ended_mode - mode for single ended operation
|
|
+ * @LINE_MODE_PUSH_PULL: normal mode for a GPIO line, drive actively high/low
|
|
+ * @LINE_MODE_OPEN_DRAIN: set line to be open drain
|
|
+ * @LINE_MODE_OPEN_SOURCE: set line to be open source
|
|
+ */
|
|
+enum single_ended_mode {
|
|
+ LINE_MODE_PUSH_PULL,
|
|
+ LINE_MODE_OPEN_DRAIN,
|
|
+ LINE_MODE_OPEN_SOURCE,
|
|
+};
|
|
+
|
|
+/**
|
|
* struct gpio_chip - abstract a GPIO controller
|
|
- * @label: for diagnostics
|
|
- * @dev: optional device providing the GPIOs
|
|
- * @cdev: class device used by sysfs interface (may be NULL)
|
|
+ * @label: a functional name for the GPIO device, such as a part
|
|
+ * number or the name of the SoC IP-block implementing it.
|
|
+ * @gpiodev: the internal state holder, opaque struct
|
|
+ * @parent: optional parent device providing the GPIOs
|
|
* @owner: helps prevent removal of modules exporting active GPIOs
|
|
- * @list: links gpio_chips together for traversal
|
|
* @request: optional hook for chip-specific activation, such as
|
|
* enabling module power and clock; may sleep
|
|
* @free: optional hook for chip-specific deactivation, such as
|
|
@@ -32,12 +45,19 @@ struct seq_file;
|
|
* (same as GPIOF_DIR_XXX), or negative error
|
|
* @direction_input: configures signal "offset" as input, or returns error
|
|
* @direction_output: configures signal "offset" as output, or returns error
|
|
- * @get: returns value for signal "offset"; for output signals this
|
|
- * returns either the value actually sensed, or zero
|
|
+ * @get: returns value for signal "offset", 0=low, 1=high, or negative error
|
|
* @set: assigns output value for signal "offset"
|
|
* @set_multiple: assigns output values for multiple signals defined by "mask"
|
|
* @set_debounce: optional hook for setting debounce time for specified gpio in
|
|
- * interrupt triggered gpio chips
|
|
+ * interrupt triggered gpio chips
|
|
+ * @set_single_ended: optional hook for setting a line as open drain, open
|
|
+ * source, or non-single ended (restore from open drain/source to normal
|
|
+ * push-pull mode) this should be implemented if the hardware supports
|
|
+ * open drain or open source settings. The GPIOlib will otherwise try
|
|
+ * to emulate open drain/source by not actively driving lines high/low
|
|
+ * if a consumer request this. The driver may return -ENOTSUPP if e.g.
|
|
+ * it supports just open drain but not open source and is called
|
|
+ * with LINE_MODE_OPEN_SOURCE as mode argument.
|
|
* @to_irq: optional hook supporting non-static gpio_to_irq() mappings;
|
|
* implementation may not sleep
|
|
* @dbg_show: optional routine to show contents in debugfs; default code
|
|
@@ -51,7 +71,6 @@ struct seq_file;
|
|
* get rid of the static GPIO number space in the long run.
|
|
* @ngpio: the number of GPIOs handled by this controller; the last GPIO
|
|
* handled is (base + ngpio - 1).
|
|
- * @desc: array of ngpio descriptors. Private.
|
|
* @names: if set, must be an array of strings to use as alternative
|
|
* names for the GPIOs in this chip. Any entry in the array
|
|
* may be NULL if there is no alias for the GPIO, however the
|
|
@@ -65,6 +84,23 @@ struct seq_file;
|
|
* registers.
|
|
* @irq_not_threaded: flag must be set if @can_sleep is set but the
|
|
* IRQs don't need to be threaded
|
|
+ * @read_reg: reader function for generic GPIO
|
|
+ * @write_reg: writer function for generic GPIO
|
|
+ * @pin2mask: some generic GPIO controllers work with the big-endian bits
|
|
+ * notation, e.g. in a 8-bits register, GPIO7 is the least significant
|
|
+ * bit. This callback assigns the right bit mask.
|
|
+ * @reg_dat: data (in) register for generic GPIO
|
|
+ * @reg_set: output set register (out=high) for generic GPIO
|
|
+ * @reg_clk: output clear register (out=low) for generic GPIO
|
|
+ * @reg_dir: direction setting register for generic GPIO
|
|
+ * @bgpio_bits: number of register bits used for a generic GPIO i.e.
|
|
+ * <register width> * 8
|
|
+ * @bgpio_lock: used to lock chip->bgpio_data. Also, this is needed to keep
|
|
+ * shadowed and real data registers writes together.
|
|
+ * @bgpio_data: shadowed data register for generic GPIO to clear/set bits
|
|
+ * safely.
|
|
+ * @bgpio_dir: shadowed direction register for generic GPIO to clear/set
|
|
+ * direction safely.
|
|
* @irqchip: GPIO IRQ chip impl, provided by GPIO driver
|
|
* @irqdomain: Interrupt translation domain; responsible for mapping
|
|
* between GPIO hwirq number and linux irq number
|
|
@@ -75,6 +111,10 @@ struct seq_file;
|
|
* initialization, provided by GPIO driver
|
|
* @irq_parent: GPIO IRQ chip parent/bank linux irq number,
|
|
* provided by GPIO driver
|
|
+ * @irq_need_valid_mask: If set core allocates @irq_valid_mask with all
|
|
+ * bits set to one
|
|
+ * @irq_valid_mask: If not %NULL holds bitmask of GPIOs which are valid to
|
|
+ * be included in IRQ domain of the chip
|
|
* @lock_key: per GPIO IRQ chip lockdep class
|
|
*
|
|
* A gpio_chip can help platforms abstract various sources of GPIOs so
|
|
@@ -89,10 +129,9 @@ struct seq_file;
|
|
*/
|
|
struct gpio_chip {
|
|
const char *label;
|
|
- struct device *dev;
|
|
- struct device *cdev;
|
|
+ struct gpio_device *gpiodev;
|
|
+ struct device *parent;
|
|
struct module *owner;
|
|
- struct list_head list;
|
|
|
|
int (*request)(struct gpio_chip *chip,
|
|
unsigned offset);
|
|
@@ -114,6 +153,9 @@ struct gpio_chip {
|
|
int (*set_debounce)(struct gpio_chip *chip,
|
|
unsigned offset,
|
|
unsigned debounce);
|
|
+ int (*set_single_ended)(struct gpio_chip *chip,
|
|
+ unsigned offset,
|
|
+ enum single_ended_mode mode);
|
|
|
|
int (*to_irq)(struct gpio_chip *chip,
|
|
unsigned offset);
|
|
@@ -122,11 +164,24 @@ struct gpio_chip {
|
|
struct gpio_chip *chip);
|
|
int base;
|
|
u16 ngpio;
|
|
- struct gpio_desc *desc;
|
|
const char *const *names;
|
|
bool can_sleep;
|
|
bool irq_not_threaded;
|
|
|
|
+#if IS_ENABLED(CONFIG_GPIO_GENERIC)
|
|
+ unsigned long (*read_reg)(void __iomem *reg);
|
|
+ void (*write_reg)(void __iomem *reg, unsigned long data);
|
|
+ unsigned long (*pin2mask)(struct gpio_chip *gc, unsigned int pin);
|
|
+ void __iomem *reg_dat;
|
|
+ void __iomem *reg_set;
|
|
+ void __iomem *reg_clr;
|
|
+ void __iomem *reg_dir;
|
|
+ int bgpio_bits;
|
|
+ spinlock_t bgpio_lock;
|
|
+ unsigned long bgpio_data;
|
|
+ unsigned long bgpio_dir;
|
|
+#endif
|
|
+
|
|
#ifdef CONFIG_GPIOLIB_IRQCHIP
|
|
/*
|
|
* With CONFIG_GPIOLIB_IRQCHIP we get an irqchip inside the gpiolib
|
|
@@ -138,6 +193,8 @@ struct gpio_chip {
|
|
irq_flow_handler_t irq_handler;
|
|
unsigned int irq_default_type;
|
|
int irq_parent;
|
|
+ bool irq_need_valid_mask;
|
|
+ unsigned long *irq_valid_mask;
|
|
struct lock_class_key *lock_key;
|
|
#endif
|
|
|
|
@@ -151,32 +208,58 @@ struct gpio_chip {
|
|
int (*of_xlate)(struct gpio_chip *gc,
|
|
const struct of_phandle_args *gpiospec, u32 *flags);
|
|
#endif
|
|
-#ifdef CONFIG_PINCTRL
|
|
- /*
|
|
- * If CONFIG_PINCTRL is enabled, then gpio controllers can optionally
|
|
- * describe the actual pin range which they serve in an SoC. This
|
|
- * information would be used by pinctrl subsystem to configure
|
|
- * corresponding pins for gpio usage.
|
|
- */
|
|
- struct list_head pin_ranges;
|
|
-#endif
|
|
};
|
|
|
|
extern const char *gpiochip_is_requested(struct gpio_chip *chip,
|
|
unsigned offset);
|
|
|
|
/* add/remove chips */
|
|
+extern int gpiochip_add_data(struct gpio_chip *chip, void *data);
|
|
extern int gpiochip_add(struct gpio_chip *chip);
|
|
extern void gpiochip_remove(struct gpio_chip *chip);
|
|
+extern int devm_gpiochip_add_data(struct device *dev, struct gpio_chip *chip,
|
|
+ void *data);
|
|
+extern void devm_gpiochip_remove(struct device *dev, struct gpio_chip *chip);
|
|
+
|
|
extern struct gpio_chip *gpiochip_find(void *data,
|
|
int (*match)(struct gpio_chip *chip, void *data));
|
|
|
|
/* lock/unlock as IRQ */
|
|
int gpiochip_lock_as_irq(struct gpio_chip *chip, unsigned int offset);
|
|
void gpiochip_unlock_as_irq(struct gpio_chip *chip, unsigned int offset);
|
|
+bool gpiochip_line_is_irq(struct gpio_chip *chip, unsigned int offset);
|
|
+
|
|
+/* Line status inquiry for drivers */
|
|
+bool gpiochip_line_is_open_drain(struct gpio_chip *chip, unsigned int offset);
|
|
+bool gpiochip_line_is_open_source(struct gpio_chip *chip, unsigned int offset);
|
|
+
|
|
+/* get driver data */
|
|
+void *gpiochip_get_data(struct gpio_chip *chip);
|
|
|
|
struct gpio_chip *gpiod_to_chip(const struct gpio_desc *desc);
|
|
|
|
+struct bgpio_pdata {
|
|
+ const char *label;
|
|
+ int base;
|
|
+ int ngpio;
|
|
+};
|
|
+
|
|
+#if IS_ENABLED(CONFIG_GPIO_GENERIC)
|
|
+
|
|
+int bgpio_init(struct gpio_chip *gc, struct device *dev,
|
|
+ unsigned long sz, void __iomem *dat, void __iomem *set,
|
|
+ void __iomem *clr, void __iomem *dirout, void __iomem *dirin,
|
|
+ unsigned long flags);
|
|
+
|
|
+#define BGPIOF_BIG_ENDIAN BIT(0)
|
|
+#define BGPIOF_UNREADABLE_REG_SET BIT(1) /* reg_set is unreadable */
|
|
+#define BGPIOF_UNREADABLE_REG_DIR BIT(2) /* reg_dir is unreadable */
|
|
+#define BGPIOF_BIG_ENDIAN_BYTE_ORDER BIT(3)
|
|
+#define BGPIOF_READ_OUTPUT_REG_SET BIT(4) /* reg_set stores output value */
|
|
+#define BGPIOF_NO_OUTPUT BIT(5) /* only input */
|
|
+
|
|
+#endif
|
|
+
|
|
#ifdef CONFIG_GPIOLIB_IRQCHIP
|
|
|
|
void gpiochip_set_chained_irqchip(struct gpio_chip *gpiochip,
|
|
diff --git a/qca/src/linux-4.4/include/uapi/linux/Kbuild b/qca/src/linux-4.4/include/uapi/linux/Kbuild
|
|
old mode 100644
|
|
new mode 100755
|
|
index caeef4b..2c380ed
|
|
--- a/qca/src/linux-4.4/include/uapi/linux/Kbuild
|
|
+++ b/qca/src/linux-4.4/include/uapi/linux/Kbuild
|
|
@@ -138,6 +138,7 @@ header-y += genetlink.h
|
|
header-y += gen_stats.h
|
|
header-y += gfs2_ondisk.h
|
|
header-y += gigaset_dev.h
|
|
+header-y += gpio.h
|
|
header-y += gsmmux.h
|
|
header-y += hdlcdrv.h
|
|
header-y += hdlc.h
|
|
diff --git a/qca/src/linux-4.4/include/uapi/linux/gpio.h b/qca/src/linux-4.4/include/uapi/linux/gpio.h
|
|
new file mode 100755
|
|
index 0000000..333d354
|
|
--- /dev/null
|
|
+++ b/qca/src/linux-4.4/include/uapi/linux/gpio.h
|
|
@@ -0,0 +1,157 @@
|
|
+/*
|
|
+ * <linux/gpio.h> - userspace ABI for the GPIO character devices
|
|
+ *
|
|
+ * Copyright (C) 2016 Linus Walleij
|
|
+ *
|
|
+ * 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.
|
|
+ */
|
|
+#ifndef _UAPI_GPIO_H_
|
|
+#define _UAPI_GPIO_H_
|
|
+
|
|
+#include <linux/ioctl.h>
|
|
+#include <linux/types.h>
|
|
+
|
|
+/**
|
|
+ * struct gpiochip_info - Information about a certain GPIO chip
|
|
+ * @name: the Linux kernel name of this GPIO chip
|
|
+ * @label: a functional name for this GPIO chip, such as a product
|
|
+ * number, may be NULL
|
|
+ * @lines: number of GPIO lines on this chip
|
|
+ */
|
|
+struct gpiochip_info {
|
|
+ char name[32];
|
|
+ char label[32];
|
|
+ __u32 lines;
|
|
+};
|
|
+
|
|
+/* Informational flags */
|
|
+#define GPIOLINE_FLAG_KERNEL (1UL << 0) /* Line used by the kernel */
|
|
+#define GPIOLINE_FLAG_IS_OUT (1UL << 1)
|
|
+#define GPIOLINE_FLAG_ACTIVE_LOW (1UL << 2)
|
|
+#define GPIOLINE_FLAG_OPEN_DRAIN (1UL << 3)
|
|
+#define GPIOLINE_FLAG_OPEN_SOURCE (1UL << 4)
|
|
+
|
|
+/**
|
|
+ * struct gpioline_info - Information about a certain GPIO line
|
|
+ * @line_offset: the local offset on this GPIO device, fill this in when
|
|
+ * requesting the line information from the kernel
|
|
+ * @flags: various flags for this line
|
|
+ * @name: the name of this GPIO line, such as the output pin of the line on the
|
|
+ * chip, a rail or a pin header name on a board, as specified by the gpio
|
|
+ * chip, may be NULL
|
|
+ * @consumer: a functional name for the consumer of this GPIO line as set by
|
|
+ * whatever is using it, will be NULL if there is no current user but may
|
|
+ * also be NULL if the consumer doesn't set this up
|
|
+ */
|
|
+struct gpioline_info {
|
|
+ __u32 line_offset;
|
|
+ __u32 flags;
|
|
+ char name[32];
|
|
+ char consumer[32];
|
|
+};
|
|
+
|
|
+/* Maximum number of requested handles */
|
|
+#define GPIOHANDLES_MAX 64
|
|
+
|
|
+/* Linerequest flags */
|
|
+#define GPIOHANDLE_REQUEST_INPUT (1UL << 0)
|
|
+#define GPIOHANDLE_REQUEST_OUTPUT (1UL << 1)
|
|
+#define GPIOHANDLE_REQUEST_ACTIVE_LOW (1UL << 2)
|
|
+#define GPIOHANDLE_REQUEST_OPEN_DRAIN (1UL << 3)
|
|
+#define GPIOHANDLE_REQUEST_OPEN_SOURCE (1UL << 4)
|
|
+
|
|
+/**
|
|
+ * struct gpiohandle_request - Information about a GPIO handle request
|
|
+ * @lineoffsets: an array desired lines, specified by offset index for the
|
|
+ * associated GPIO device
|
|
+ * @flags: desired flags for the desired GPIO lines, such as
|
|
+ * GPIOHANDLE_REQUEST_OUTPUT, GPIOHANDLE_REQUEST_ACTIVE_LOW etc, OR:ed
|
|
+ * together. Note that even if multiple lines are requested, the same flags
|
|
+ * must be applicable to all of them, if you want lines with individual
|
|
+ * flags set, request them one by one. It is possible to select
|
|
+ * a batch of input or output lines, but they must all have the same
|
|
+ * characteristics, i.e. all inputs or all outputs, all active low etc
|
|
+ * @default_values: if the GPIOHANDLE_REQUEST_OUTPUT is set for a requested
|
|
+ * line, this specifies the default output value, should be 0 (low) or
|
|
+ * 1 (high), anything else than 0 or 1 will be interpreted as 1 (high)
|
|
+ * @consumer_label: a desired consumer label for the selected GPIO line(s)
|
|
+ * such as "my-bitbanged-relay"
|
|
+ * @lines: number of lines requested in this request, i.e. the number of
|
|
+ * valid fields in the above arrays, set to 1 to request a single line
|
|
+ * @fd: if successful this field will contain a valid anonymous file handle
|
|
+ * after a GPIO_GET_LINEHANDLE_IOCTL operation, zero or negative value
|
|
+ * means error
|
|
+ */
|
|
+struct gpiohandle_request {
|
|
+ __u32 lineoffsets[GPIOHANDLES_MAX];
|
|
+ __u32 flags;
|
|
+ __u8 default_values[GPIOHANDLES_MAX];
|
|
+ char consumer_label[32];
|
|
+ __u32 lines;
|
|
+ int fd;
|
|
+};
|
|
+
|
|
+/**
|
|
+ * struct gpiohandle_data - Information of values on a GPIO handle
|
|
+ * @values: when getting the state of lines this contains the current
|
|
+ * state of a line, when setting the state of lines these should contain
|
|
+ * the desired target state
|
|
+ */
|
|
+struct gpiohandle_data {
|
|
+ __u8 values[GPIOHANDLES_MAX];
|
|
+};
|
|
+
|
|
+#define GPIOHANDLE_GET_LINE_VALUES_IOCTL _IOWR(0xB4, 0x08, struct gpiohandle_data)
|
|
+#define GPIOHANDLE_SET_LINE_VALUES_IOCTL _IOWR(0xB4, 0x09, struct gpiohandle_data)
|
|
+
|
|
+/* Eventrequest flags */
|
|
+#define GPIOEVENT_REQUEST_RISING_EDGE (1UL << 0)
|
|
+#define GPIOEVENT_REQUEST_FALLING_EDGE (1UL << 1)
|
|
+#define GPIOEVENT_REQUEST_BOTH_EDGES ((1UL << 0) | (1UL << 1))
|
|
+
|
|
+/**
|
|
+ * struct gpioevent_request - Information about a GPIO event request
|
|
+ * @lineoffset: the desired line to subscribe to events from, specified by
|
|
+ * offset index for the associated GPIO device
|
|
+ * @handleflags: desired handle flags for the desired GPIO line, such as
|
|
+ * GPIOHANDLE_REQUEST_ACTIVE_LOW or GPIOHANDLE_REQUEST_OPEN_DRAIN
|
|
+ * @eventflags: desired flags for the desired GPIO event line, such as
|
|
+ * GPIOEVENT_REQUEST_RISING_EDGE or GPIOEVENT_REQUEST_FALLING_EDGE
|
|
+ * @consumer_label: a desired consumer label for the selected GPIO line(s)
|
|
+ * such as "my-listener"
|
|
+ * @fd: if successful this field will contain a valid anonymous file handle
|
|
+ * after a GPIO_GET_LINEEVENT_IOCTL operation, zero or negative value
|
|
+ * means error
|
|
+ */
|
|
+struct gpioevent_request {
|
|
+ __u32 lineoffset;
|
|
+ __u32 handleflags;
|
|
+ __u32 eventflags;
|
|
+ char consumer_label[32];
|
|
+ int fd;
|
|
+};
|
|
+
|
|
+/**
|
|
+ * GPIO event types
|
|
+ */
|
|
+#define GPIOEVENT_EVENT_RISING_EDGE 0x01
|
|
+#define GPIOEVENT_EVENT_FALLING_EDGE 0x02
|
|
+
|
|
+/**
|
|
+ * struct gpioevent_data - The actual event being pushed to userspace
|
|
+ * @timestamp: best estimate of time of event occurrence, in nanoseconds
|
|
+ * @id: event identifier
|
|
+ */
|
|
+struct gpioevent_data {
|
|
+ __u64 timestamp;
|
|
+ __u32 id;
|
|
+};
|
|
+
|
|
+#define GPIO_GET_CHIPINFO_IOCTL _IOR(0xB4, 0x01, struct gpiochip_info)
|
|
+#define GPIO_GET_LINEINFO_IOCTL _IOWR(0xB4, 0x02, struct gpioline_info)
|
|
+#define GPIO_GET_LINEHANDLE_IOCTL _IOWR(0xB4, 0x03, struct gpiohandle_request)
|
|
+#define GPIO_GET_LINEEVENT_IOCTL _IOWR(0xB4, 0x04, struct gpioevent_request)
|
|
+
|
|
+#endif /* _UAPI_GPIO_H_ */
|
|
--
|
|
2.7.4
|
|
|