summaryrefslogtreecommitdiffstats
path: root/drivers/gpio
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpio')
-rw-r--r--drivers/gpio/Kconfig15
-rw-r--r--drivers/gpio/Makefile1
-rw-r--r--drivers/gpio/gpio-cros-ec.c209
-rw-r--r--drivers/gpio/gpio-eic-sprd.c10
-rw-r--r--drivers/gpio/gpio-sim.c49
-rw-r--r--drivers/gpio/gpiolib-acpi.c15
-rw-r--r--drivers/gpio/gpiolib-acpi.h5
-rw-r--r--drivers/gpio/gpiolib-cdev.c96
-rw-r--r--drivers/gpio/gpiolib-devres.c2
-rw-r--r--drivers/gpio/gpiolib-legacy.c12
-rw-r--r--drivers/gpio/gpiolib-of.c29
-rw-r--r--drivers/gpio/gpiolib-of.h6
-rw-r--r--drivers/gpio/gpiolib-sysfs.c150
-rw-r--r--drivers/gpio/gpiolib.c961
-rw-r--r--drivers/gpio/gpiolib.h95
15 files changed, 1013 insertions, 642 deletions
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 1301cec94f12..3fbb0bdb15c1 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -711,8 +711,9 @@ config GPIO_UNIPHIER
Say yes here to support UniPhier GPIOs.
config GPIO_VF610
- def_bool y
- depends on ARCH_MXC
+ bool "VF610 GPIO support"
+ default y if SOC_VF610
+ depends on ARCH_MXC || COMPILE_TEST
select GPIOLIB_IRQCHIP
help
Say yes here to support i.MX or Vybrid vf610 GPIOs.
@@ -1240,6 +1241,16 @@ config GPIO_BD9571MWV
This driver can also be built as a module. If so, the module
will be called gpio-bd9571mwv.
+config GPIO_CROS_EC
+ tristate "ChromeOS EC GPIO support"
+ depends on CROS_EC
+ help
+ GPIO driver for the ChromeOS Embedded Controller (EC). GPIOs
+ cannot be set unless the system is unlocked.
+
+ This driver can also be built as a module. If so, the module
+ will be called gpio-cros-ec.
+
config GPIO_CRYSTAL_COVE
tristate "GPIO support for Crystal Cove PMIC"
depends on (X86 || COMPILE_TEST) && INTEL_SOC_PMIC
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index 9e40af196aae..7ae4d81de1df 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -46,6 +46,7 @@ obj-$(CONFIG_GPIO_BT8XX) += gpio-bt8xx.o
obj-$(CONFIG_GPIO_CADENCE) += gpio-cadence.o
obj-$(CONFIG_GPIO_CLPS711X) += gpio-clps711x.o
obj-$(CONFIG_GPIO_SNPS_CREG) += gpio-creg-snps.o
+obj-$(CONFIG_GPIO_CROS_EC) += gpio-cros-ec.o
obj-$(CONFIG_GPIO_CRYSTAL_COVE) += gpio-crystalcove.o
obj-$(CONFIG_GPIO_CS5535) += gpio-cs5535.o
obj-$(CONFIG_GPIO_DA9052) += gpio-da9052.o
diff --git a/drivers/gpio/gpio-cros-ec.c b/drivers/gpio/gpio-cros-ec.c
new file mode 100644
index 000000000000..842e1c060414
--- /dev/null
+++ b/drivers/gpio/gpio-cros-ec.c
@@ -0,0 +1,209 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright 2024 Google LLC
+ *
+ * This driver provides the ability to control GPIOs on the Chrome OS EC.
+ * There isn't any direction control, and setting values on GPIOs is only
+ * possible when the system is unlocked.
+ */
+
+#include <linux/bitops.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/gpio/driver.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_data/cros_ec_commands.h>
+#include <linux/platform_data/cros_ec_proto.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <linux/slab.h>
+
+/* Prefix all names to avoid collisions with EC <-> AP nets */
+static const char cros_ec_gpio_prefix[] = "EC:";
+
+/* Setting gpios is only supported when the system is unlocked */
+static void cros_ec_gpio_set(struct gpio_chip *gc, unsigned int gpio, int val)
+{
+ const char *name = gc->names[gpio] + strlen(cros_ec_gpio_prefix);
+ struct cros_ec_device *cros_ec = gpiochip_get_data(gc);
+ struct ec_params_gpio_set params = {
+ .val = val,
+ };
+ int ret;
+ ssize_t copied;
+
+ copied = strscpy(params.name, name, sizeof(params.name));
+ if (copied < 0)
+ return;
+
+ ret = cros_ec_cmd(cros_ec, 0, EC_CMD_GPIO_SET, &params,
+ sizeof(params), NULL, 0);
+ if (ret < 0)
+ dev_err(gc->parent, "error setting gpio%d (%s) on EC: %d\n", gpio, name, ret);
+}
+
+static int cros_ec_gpio_get(struct gpio_chip *gc, unsigned int gpio)
+{
+ const char *name = gc->names[gpio] + strlen(cros_ec_gpio_prefix);
+ struct cros_ec_device *cros_ec = gpiochip_get_data(gc);
+ struct ec_params_gpio_get params;
+ struct ec_response_gpio_get response;
+ int ret;
+ ssize_t copied;
+
+ copied = strscpy(params.name, name, sizeof(params.name));
+ if (copied < 0)
+ return -EINVAL;
+
+ ret = cros_ec_cmd(cros_ec, 0, EC_CMD_GPIO_GET, &params,
+ sizeof(params), &response, sizeof(response));
+ if (ret < 0) {
+ dev_err(gc->parent, "error getting gpio%d (%s) on EC: %d\n", gpio, name, ret);
+ return ret;
+ }
+
+ return response.val;
+}
+
+#define CROS_EC_GPIO_INPUT BIT(8)
+#define CROS_EC_GPIO_OUTPUT BIT(9)
+
+static int cros_ec_gpio_get_direction(struct gpio_chip *gc, unsigned int gpio)
+{
+ const char *name = gc->names[gpio] + strlen(cros_ec_gpio_prefix);
+ struct cros_ec_device *cros_ec = gpiochip_get_data(gc);
+ struct ec_params_gpio_get_v1 params = {
+ .subcmd = EC_GPIO_GET_INFO,
+ .get_info.index = gpio,
+ };
+ struct ec_response_gpio_get_v1 response;
+ int ret;
+
+ ret = cros_ec_cmd(cros_ec, 1, EC_CMD_GPIO_GET, &params,
+ sizeof(params), &response, sizeof(response));
+ if (ret < 0) {
+ dev_err(gc->parent, "error getting direction of gpio%d (%s) on EC: %d\n", gpio, name, ret);
+ return ret;
+ }
+
+ if (response.get_info.flags & CROS_EC_GPIO_INPUT)
+ return GPIO_LINE_DIRECTION_IN;
+
+ if (response.get_info.flags & CROS_EC_GPIO_OUTPUT)
+ return GPIO_LINE_DIRECTION_OUT;
+
+ return -EINVAL;
+}
+
+/* Query EC for all gpio line names */
+static int cros_ec_gpio_init_names(struct cros_ec_device *cros_ec, struct gpio_chip *gc)
+{
+ struct ec_params_gpio_get_v1 params = {
+ .subcmd = EC_GPIO_GET_INFO,
+ };
+ struct ec_response_gpio_get_v1 response;
+ int ret, i;
+ /* EC may not NUL terminate */
+ size_t name_len = strlen(cros_ec_gpio_prefix) + sizeof(response.get_info.name) + 1;
+ ssize_t copied;
+ const char **names;
+ char *str;
+
+ names = devm_kcalloc(gc->parent, gc->ngpio, sizeof(*names), GFP_KERNEL);
+ if (!names)
+ return -ENOMEM;
+ gc->names = names;
+
+ str = devm_kcalloc(gc->parent, gc->ngpio, name_len, GFP_KERNEL);
+ if (!str)
+ return -ENOMEM;
+
+ /* Get gpio line names one at a time */
+ for (i = 0; i < gc->ngpio; i++) {
+ params.get_info.index = i;
+ ret = cros_ec_cmd(cros_ec, 1, EC_CMD_GPIO_GET, &params,
+ sizeof(params), &response, sizeof(response));
+ if (ret < 0) {
+ dev_err_probe(gc->parent, ret, "error getting gpio%d info\n", i);
+ return ret;
+ }
+
+ names[i] = str;
+ copied = scnprintf(str, name_len, "%s%s", cros_ec_gpio_prefix,
+ response.get_info.name);
+ if (copied < 0)
+ return copied;
+
+ str += copied + 1;
+ }
+
+ return 0;
+}
+
+/* Query EC for number of gpios */
+static int cros_ec_gpio_ngpios(struct cros_ec_device *cros_ec)
+{
+ struct ec_params_gpio_get_v1 params = {
+ .subcmd = EC_GPIO_GET_COUNT,
+ };
+ struct ec_response_gpio_get_v1 response;
+ int ret;
+
+ ret = cros_ec_cmd(cros_ec, 1, EC_CMD_GPIO_GET, &params,
+ sizeof(params), &response, sizeof(response));
+ if (ret < 0)
+ return ret;
+
+ return response.get_count.val;
+}
+
+static int cros_ec_gpio_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device *parent = dev->parent;
+ struct cros_ec_dev *ec_dev = dev_get_drvdata(parent);
+ struct cros_ec_device *cros_ec = ec_dev->ec_dev;
+ struct gpio_chip *gc;
+ int ngpios;
+ int ret;
+
+ /* Use the fwnode from the protocol device, e.g. cros-ec-spi */
+ device_set_node(dev, dev_fwnode(cros_ec->dev));
+
+ ngpios = cros_ec_gpio_ngpios(cros_ec);
+ if (ngpios < 0) {
+ dev_err_probe(dev, ngpios, "error getting gpio count\n");
+ return ngpios;
+ }
+
+ gc = devm_kzalloc(dev, sizeof(*gc), GFP_KERNEL);
+ if (!gc)
+ return -ENOMEM;
+
+ gc->ngpio = ngpios;
+ gc->parent = dev;
+ ret = cros_ec_gpio_init_names(cros_ec, gc);
+ if (ret)
+ return ret;
+
+ gc->can_sleep = true;
+ gc->label = dev_name(dev);
+ gc->base = -1;
+ gc->set = cros_ec_gpio_set;
+ gc->get = cros_ec_gpio_get;
+ gc->get_direction = cros_ec_gpio_get_direction;
+
+ return devm_gpiochip_add_data(dev, gc, cros_ec);
+}
+
+static struct platform_driver cros_ec_gpio_driver = {
+ .probe = cros_ec_gpio_probe,
+ .driver = {
+ .name = "cros-ec-gpio",
+ },
+};
+module_platform_driver(cros_ec_gpio_driver);
+
+MODULE_DESCRIPTION("ChromeOS EC GPIO Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpio/gpio-eic-sprd.c b/drivers/gpio/gpio-eic-sprd.c
index 806b88d8dfb7..2dd0e46c42ad 100644
--- a/drivers/gpio/gpio-eic-sprd.c
+++ b/drivers/gpio/gpio-eic-sprd.c
@@ -108,7 +108,6 @@ static struct sprd_eic *to_sprd_eic(struct notifier_block *nb)
struct sprd_eic_variant_data {
enum sprd_eic_type type;
- u32 num_eics;
};
static const char *sprd_eic_label_name[SPRD_EIC_MAX] = {
@@ -118,22 +117,18 @@ static const char *sprd_eic_label_name[SPRD_EIC_MAX] = {
static const struct sprd_eic_variant_data sc9860_eic_dbnc_data = {
.type = SPRD_EIC_DEBOUNCE,
- .num_eics = 8,
};
static const struct sprd_eic_variant_data sc9860_eic_latch_data = {
.type = SPRD_EIC_LATCH,
- .num_eics = 8,
};
static const struct sprd_eic_variant_data sc9860_eic_async_data = {
.type = SPRD_EIC_ASYNC,
- .num_eics = 8,
};
static const struct sprd_eic_variant_data sc9860_eic_sync_data = {
.type = SPRD_EIC_SYNC,
- .num_eics = 8,
};
static inline void __iomem *sprd_eic_offset_base(struct sprd_eic *sprd_eic,
@@ -619,6 +614,7 @@ static int sprd_eic_probe(struct platform_device *pdev)
struct gpio_irq_chip *irq;
struct sprd_eic *sprd_eic;
struct resource *res;
+ u16 num_banks = 0;
int ret, i;
pdata = of_device_get_match_data(dev);
@@ -652,10 +648,12 @@ static int sprd_eic_probe(struct platform_device *pdev)
sprd_eic->base[i] = devm_ioremap_resource(dev, res);
if (IS_ERR(sprd_eic->base[i]))
return PTR_ERR(sprd_eic->base[i]);
+
+ num_banks++;
}
sprd_eic->chip.label = sprd_eic_label_name[sprd_eic->type];
- sprd_eic->chip.ngpio = pdata->num_eics;
+ sprd_eic->chip.ngpio = num_banks * SPRD_EIC_PER_BANK_NR;
sprd_eic->chip.base = -1;
sprd_eic->chip.parent = dev;
sprd_eic->chip.direction_input = sprd_eic_direction_input;
diff --git a/drivers/gpio/gpio-sim.c b/drivers/gpio/gpio-sim.c
index c4106e37e6db..2ed5cbe7c8a8 100644
--- a/drivers/gpio/gpio-sim.c
+++ b/drivers/gpio/gpio-sim.c
@@ -22,6 +22,7 @@
#include <linux/irq_sim.h>
#include <linux/kernel.h>
#include <linux/list.h>
+#include <linux/lockdep.h>
#include <linux/minmax.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
@@ -234,10 +235,10 @@ static void gpio_sim_dbg_show(struct seq_file *seq, struct gpio_chip *gc)
guard(mutex)(&chip->lock);
- for_each_requested_gpio(gc, i, label)
+ for_each_hwgpio(gc, i, label)
seq_printf(seq, " gpio-%-3d (%s) %s,%s\n",
gc->base + i,
- label,
+ label ?: "<unused>",
test_bit(i, chip->direction_map) ? "input" :
test_bit(i, chip->value_map) ? "output-high" :
"output-low",
@@ -420,7 +421,7 @@ static int gpio_sim_add_bank(struct fwnode_handle *swnode, struct device *dev)
ret = fwnode_property_read_string(swnode, "gpio-sim,label", &label);
if (ret) {
- label = devm_kasprintf(dev, GFP_KERNEL, "%s-%pfwP",
+ label = devm_kasprintf(dev, GFP_KERNEL, "%s:%pfwP",
dev_name(dev), swnode);
if (!label)
return -ENOMEM;
@@ -697,8 +698,10 @@ static struct gpio_sim_device *gpio_sim_hog_get_device(struct gpio_sim_hog *hog)
return gpio_sim_line_get_device(line);
}
-static bool gpio_sim_device_is_live_unlocked(struct gpio_sim_device *dev)
+static bool gpio_sim_device_is_live(struct gpio_sim_device *dev)
{
+ lockdep_assert_held(&dev->lock);
+
return !!dev->pdev;
}
@@ -737,7 +740,7 @@ gpio_sim_device_config_live_show(struct config_item *item, char *page)
bool live;
scoped_guard(mutex, &dev->lock)
- live = gpio_sim_device_is_live_unlocked(dev);
+ live = gpio_sim_device_is_live(dev);
return sprintf(page, "%c\n", live ? '1' : '0');
}
@@ -833,7 +836,7 @@ static int gpio_sim_add_hogs(struct gpio_sim_device *dev)
GFP_KERNEL);
else
hog->chip_label = kasprintf(GFP_KERNEL,
- "gpio-sim.%u-%pfwP",
+ "gpio-sim.%u:%pfwP",
dev->id,
bank->swnode);
if (!hog->chip_label) {
@@ -926,7 +929,7 @@ static bool gpio_sim_bank_labels_non_unique(struct gpio_sim_device *dev)
return false;
}
-static int gpio_sim_device_activate_unlocked(struct gpio_sim_device *dev)
+static int gpio_sim_device_activate(struct gpio_sim_device *dev)
{
struct platform_device_info pdevinfo;
struct fwnode_handle *swnode;
@@ -934,6 +937,8 @@ static int gpio_sim_device_activate_unlocked(struct gpio_sim_device *dev)
struct gpio_sim_bank *bank;
int ret;
+ lockdep_assert_held(&dev->lock);
+
if (list_empty(&dev->bank_list))
return -ENODATA;
@@ -998,10 +1003,12 @@ static int gpio_sim_device_activate_unlocked(struct gpio_sim_device *dev)
return 0;
}
-static void gpio_sim_device_deactivate_unlocked(struct gpio_sim_device *dev)
+static void gpio_sim_device_deactivate(struct gpio_sim_device *dev)
{
struct fwnode_handle *swnode;
+ lockdep_assert_held(&dev->lock);
+
swnode = dev_fwnode(&dev->pdev->dev);
platform_device_unregister(dev->pdev);
gpio_sim_remove_hogs(dev);
@@ -1023,12 +1030,12 @@ gpio_sim_device_config_live_store(struct config_item *item,
guard(mutex)(&dev->lock);
- if (live == gpio_sim_device_is_live_unlocked(dev))
+ if (live == gpio_sim_device_is_live(dev))
ret = -EPERM;
else if (live)
- ret = gpio_sim_device_activate_unlocked(dev);
+ ret = gpio_sim_device_activate(dev);
else
- gpio_sim_device_deactivate_unlocked(dev);
+ gpio_sim_device_deactivate(dev);
return ret ?: count;
}
@@ -1069,7 +1076,7 @@ static ssize_t gpio_sim_bank_config_chip_name_show(struct config_item *item,
guard(mutex)(&dev->lock);
- if (gpio_sim_device_is_live_unlocked(dev))
+ if (gpio_sim_device_is_live(dev))
return device_for_each_child(&dev->pdev->dev, &ctx,
gpio_sim_emit_chip_name);
@@ -1098,7 +1105,7 @@ static ssize_t gpio_sim_bank_config_label_store(struct config_item *item,
guard(mutex)(&dev->lock);
- if (gpio_sim_device_is_live_unlocked(dev))
+ if (gpio_sim_device_is_live(dev))
return -EBUSY;
trimmed = gpio_sim_strdup_trimmed(page, count);
@@ -1142,7 +1149,7 @@ gpio_sim_bank_config_num_lines_store(struct config_item *item,
guard(mutex)(&dev->lock);
- if (gpio_sim_device_is_live_unlocked(dev))
+ if (gpio_sim_device_is_live(dev))
return -EBUSY;
bank->num_lines = num_lines;
@@ -1179,7 +1186,7 @@ static ssize_t gpio_sim_line_config_name_store(struct config_item *item,
guard(mutex)(&dev->lock);
- if (gpio_sim_device_is_live_unlocked(dev))
+ if (gpio_sim_device_is_live(dev))
return -EBUSY;
trimmed = gpio_sim_strdup_trimmed(page, count);
@@ -1219,7 +1226,7 @@ static ssize_t gpio_sim_hog_config_name_store(struct config_item *item,
guard(mutex)(&dev->lock);
- if (gpio_sim_device_is_live_unlocked(dev))
+ if (gpio_sim_device_is_live(dev))
return -EBUSY;
trimmed = gpio_sim_strdup_trimmed(page, count);
@@ -1274,7 +1281,7 @@ gpio_sim_hog_config_direction_store(struct config_item *item,
guard(mutex)(&dev->lock);
- if (gpio_sim_device_is_live_unlocked(dev))
+ if (gpio_sim_device_is_live(dev))
return -EBUSY;
if (sysfs_streq(page, "input"))
@@ -1392,7 +1399,7 @@ gpio_sim_bank_config_make_line_group(struct config_group *group,
guard(mutex)(&dev->lock);
- if (gpio_sim_device_is_live_unlocked(dev))
+ if (gpio_sim_device_is_live(dev))
return ERR_PTR(-EBUSY);
line = kzalloc(sizeof(*line), GFP_KERNEL);
@@ -1445,7 +1452,7 @@ gpio_sim_device_config_make_bank_group(struct config_group *group,
guard(mutex)(&dev->lock);
- if (gpio_sim_device_is_live_unlocked(dev))
+ if (gpio_sim_device_is_live(dev))
return ERR_PTR(-EBUSY);
bank = kzalloc(sizeof(*bank), GFP_KERNEL);
@@ -1467,8 +1474,8 @@ static void gpio_sim_device_config_group_release(struct config_item *item)
struct gpio_sim_device *dev = to_gpio_sim_device(item);
scoped_guard(mutex, &dev->lock) {
- if (gpio_sim_device_is_live_unlocked(dev))
- gpio_sim_device_deactivate_unlocked(dev);
+ if (gpio_sim_device_is_live(dev))
+ gpio_sim_device_deactivate(dev);
}
mutex_destroy(&dev->lock);
diff --git a/drivers/gpio/gpiolib-acpi.c b/drivers/gpio/gpiolib-acpi.c
index cd3e9657cc36..7f140df40f35 100644
--- a/drivers/gpio/gpiolib-acpi.c
+++ b/drivers/gpio/gpiolib-acpi.c
@@ -126,7 +126,7 @@ static DEFINE_MUTEX(acpi_gpio_deferred_req_irqs_lock);
static LIST_HEAD(acpi_gpio_deferred_req_irqs_list);
static bool acpi_gpio_deferred_req_irqs_done;
-static int acpi_gpiochip_find(struct gpio_chip *gc, void *data)
+static int acpi_gpiochip_find(struct gpio_chip *gc, const void *data)
{
return device_match_acpi_handle(&gc->gpiodev->dev, data);
}
@@ -1402,17 +1402,17 @@ static int acpi_find_gpio_count(struct acpi_resource *ares, void *data)
}
/**
- * acpi_gpio_count - count the GPIOs associated with a device / function
- * @dev: GPIO consumer, can be %NULL for system-global GPIOs
+ * acpi_gpio_count - count the GPIOs associated with a firmware node / function
+ * @fwnode: firmware node of the GPIO consumer
* @con_id: function within the GPIO consumer
*
* Return:
- * The number of GPIOs associated with a device / function or %-ENOENT,
+ * The number of GPIOs associated with a firmware node / function or %-ENOENT,
* if no GPIO has been assigned to the requested function.
*/
-int acpi_gpio_count(struct device *dev, const char *con_id)
+int acpi_gpio_count(const struct fwnode_handle *fwnode, const char *con_id)
{
- struct acpi_device *adev = ACPI_COMPANION(dev);
+ struct acpi_device *adev = to_acpi_device_node(fwnode);
const union acpi_object *obj;
const struct acpi_gpio_mapping *gm;
int count = -ENOENT;
@@ -1429,8 +1429,7 @@ int acpi_gpio_count(struct device *dev, const char *con_id)
snprintf(propname, sizeof(propname), "%s",
gpio_suffixes[i]);
- ret = acpi_dev_get_property(adev, propname, ACPI_TYPE_ANY,
- &obj);
+ ret = acpi_dev_get_property(adev, propname, ACPI_TYPE_ANY, &obj);
if (ret == 0) {
if (obj->type == ACPI_TYPE_LOCAL_REFERENCE)
count = 1;
diff --git a/drivers/gpio/gpiolib-acpi.h b/drivers/gpio/gpiolib-acpi.h
index 0fcd7e14d7f9..7e1c51d04040 100644
--- a/drivers/gpio/gpiolib-acpi.h
+++ b/drivers/gpio/gpiolib-acpi.h
@@ -33,7 +33,7 @@ struct gpio_desc *acpi_find_gpio(struct fwnode_handle *fwnode,
enum gpiod_flags *dflags,
unsigned long *lookupflags);
-int acpi_gpio_count(struct device *dev, const char *con_id);
+int acpi_gpio_count(const struct fwnode_handle *fwnode, 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) { }
@@ -51,7 +51,8 @@ acpi_find_gpio(struct fwnode_handle *fwnode, const char *con_id,
{
return ERR_PTR(-ENOENT);
}
-static inline int acpi_gpio_count(struct device *dev, const char *con_id)
+static inline int acpi_gpio_count(const struct fwnode_handle *fwnode,
+ const char *con_id)
{
return -ENODEV;
}
diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c
index 2a88736629ef..f384fa278764 100644
--- a/drivers/gpio/gpiolib-cdev.c
+++ b/drivers/gpio/gpiolib-cdev.c
@@ -24,7 +24,6 @@
#include <linux/pinctrl/consumer.h>
#include <linux/poll.h>
#include <linux/rbtree.h>
-#include <linux/rwsem.h>
#include <linux/seq_file.h>
#include <linux/spinlock.h>
#include <linux/timekeeping.h>
@@ -61,11 +60,6 @@ static_assert(IS_ALIGNED(sizeof(struct gpio_v2_line_values), 8));
* interface to gpiolib GPIOs via ioctl()s.
*/
-typedef __poll_t (*poll_fn)(struct file *, struct poll_table_struct *);
-typedef long (*ioctl_fn)(struct file *, unsigned int, unsigned long);
-typedef ssize_t (*read_fn)(struct file *, char __user *,
- size_t count, loff_t *);
-
/*
* GPIO line handle management
*/
@@ -210,9 +204,9 @@ static long linehandle_ioctl(struct file *file, unsigned int cmd,
unsigned int i;
int ret;
- guard(rwsem_read)(&lh->gdev->sem);
+ guard(srcu)(&lh->gdev->srcu);
- if (!lh->gdev->chip)
+ if (!rcu_access_pointer(lh->gdev->chip))
return -ENODEV;
switch (cmd) {
@@ -337,7 +331,7 @@ static int linehandle_create(struct gpio_device *gdev, void __user *ip)
/* Request each GPIO */
for (i = 0; i < handlereq.lines; i++) {
u32 offset = handlereq.lineoffsets[i];
- struct gpio_desc *desc = gpiochip_get_desc(gdev->chip, offset);
+ struct gpio_desc *desc = gpio_device_get_desc(gdev, offset);
if (IS_ERR(desc)) {
ret = PTR_ERR(desc);
@@ -1525,9 +1519,9 @@ static long linereq_ioctl(struct file *file, unsigned int cmd,
struct linereq *lr = file->private_data;
void __user *ip = (void __user *)arg;
- guard(rwsem_read)(&lr->gdev->sem);
+ guard(srcu)(&lr->gdev->srcu);
- if (!lr->gdev->chip)
+ if (!rcu_access_pointer(lr->gdev->chip))
return -ENODEV;
switch (cmd) {
@@ -1556,9 +1550,9 @@ static __poll_t linereq_poll(struct file *file,
struct linereq *lr = file->private_data;
__poll_t events = 0;
- guard(rwsem_read)(&lr->gdev->sem);
+ guard(srcu)(&lr->gdev->srcu);
- if (!lr->gdev->chip)
+ if (!rcu_access_pointer(lr->gdev->chip))
return EPOLLHUP | EPOLLERR;
poll_wait(file, &lr->wait, wait);
@@ -1578,9 +1572,9 @@ static ssize_t linereq_read(struct file *file, char __user *buf,
ssize_t bytes_read = 0;
int ret;
- guard(rwsem_read)(&lr->gdev->sem);
+ guard(srcu)(&lr->gdev->srcu);
- if (!lr->gdev->chip)
+ if (!rcu_access_pointer(lr->gdev->chip))
return -ENODEV;
if (count < sizeof(le))
@@ -1744,7 +1738,7 @@ static int linereq_create(struct gpio_device *gdev, void __user *ip)
/* Request each GPIO */
for (i = 0; i < ulr.num_lines; i++) {
u32 offset = ulr.offsets[i];
- struct gpio_desc *desc = gpiochip_get_desc(gdev->chip, offset);
+ struct gpio_desc *desc = gpio_device_get_desc(gdev, offset);
if (IS_ERR(desc)) {
ret = PTR_ERR(desc);
@@ -1879,9 +1873,9 @@ static __poll_t lineevent_poll(struct file *file,
struct lineevent_state *le = file->private_data;
__poll_t events = 0;
- guard(rwsem_read)(&le->gdev->sem);
+ guard(srcu)(&le->gdev->srcu);
- if (!le->gdev->chip)
+ if (!rcu_access_pointer(le->gdev->chip))
return EPOLLHUP | EPOLLERR;
poll_wait(file, &le->wait, wait);
@@ -1917,9 +1911,9 @@ static ssize_t lineevent_read(struct file *file, char __user *buf,
ssize_t ge_size;
int ret;
- guard(rwsem_read)(&le->gdev->sem);
+ guard(srcu)(&le->gdev->srcu);
- if (!le->gdev->chip)
+ if (!rcu_access_pointer(le->gdev->chip))
return -ENODEV;
/*
@@ -2000,9 +1994,9 @@ static long lineevent_ioctl(struct file *file, unsigned int cmd,
void __user *ip = (void __user *)arg;
struct gpiohandle_data ghd;
- guard(rwsem_read)(&le->gdev->sem);
+ guard(srcu)(&le->gdev->srcu);
- if (!le->gdev->chip)
+ if (!rcu_access_pointer(le->gdev->chip))
return -ENODEV;
/*
@@ -2128,7 +2122,7 @@ static int lineevent_create(struct gpio_device *gdev, void __user *ip)
lflags = eventreq.handleflags;
eflags = eventreq.eventflags;
- desc = gpiochip_get_desc(gdev->chip, offset);
+ desc = gpio_device_get_desc(gdev, offset);
if (IS_ERR(desc))
return PTR_ERR(desc);
@@ -2300,21 +2294,26 @@ static void gpio_v2_line_info_changed_to_v1(
static void gpio_desc_to_lineinfo(struct gpio_desc *desc,
struct gpio_v2_line_info *info)
{
- struct gpio_chip *gc = desc->gdev->chip;
unsigned long dflags;
+ const char *label;
+
+ CLASS(gpio_chip_guard, guard)(desc);
+ if (!guard.gc)
+ return;
memset(info, 0, sizeof(*info));
info->offset = gpio_chip_hwgpio(desc);
- scoped_guard(spinlock_irqsave, &gpio_lock) {
- if (desc->name)
- strscpy(info->name, desc->name, sizeof(info->name));
+ if (desc->name)
+ strscpy(info->name, desc->name, sizeof(info->name));
- if (desc->label)
- strscpy(info->consumer, desc->label,
- sizeof(info->consumer));
+ dflags = READ_ONCE(desc->flags);
- dflags = READ_ONCE(desc->flags);
+ scoped_guard(srcu, &desc->srcu) {
+ label = gpiod_get_label(desc);
+ if (label && test_bit(FLAG_REQUESTED, &dflags))
+ strscpy(info->consumer, label,
+ sizeof(info->consumer));
}
/*
@@ -2334,8 +2333,8 @@ static void gpio_desc_to_lineinfo(struct gpio_desc *desc,
test_bit(FLAG_USED_AS_IRQ, &dflags) ||
test_bit(FLAG_EXPORT, &dflags) ||
test_bit(FLAG_SYSFS, &dflags) ||
- !gpiochip_line_is_valid(gc, info->offset) ||
- !pinctrl_gpio_can_use_line(gc, info->offset))
+ !gpiochip_line_is_valid(guard.gc, info->offset) ||
+ !pinctrl_gpio_can_use_line(guard.gc, info->offset))
info->flags |= GPIO_V2_LINE_FLAG_USED;
if (test_bit(FLAG_IS_OUT, &dflags))
@@ -2422,7 +2421,7 @@ static int lineinfo_get_v1(struct gpio_chardev_data *cdev, void __user *ip,
return -EFAULT;
/* this doubles as a range check on line_offset */
- desc = gpiochip_get_desc(cdev->gdev->chip, lineinfo.line_offset);
+ desc = gpio_device_get_desc(cdev->gdev, lineinfo.line_offset);
if (IS_ERR(desc))
return PTR_ERR(desc);
@@ -2459,7 +2458,7 @@ static int lineinfo_get(struct gpio_chardev_data *cdev, void __user *ip,
if (memchr_inv(lineinfo.padding, 0, sizeof(lineinfo.padding)))
return -EINVAL;
- desc = gpiochip_get_desc(cdev->gdev->chip, lineinfo.offset);
+ desc = gpio_device_get_desc(cdev->gdev, lineinfo.offset);
if (IS_ERR(desc))
return PTR_ERR(desc);
@@ -2508,10 +2507,10 @@ static long gpio_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
struct gpio_device *gdev = cdev->gdev;
void __user *ip = (void __user *)arg;
- guard(rwsem_read)(&gdev->sem);
+ guard(srcu)(&gdev->srcu);
/* We fail any subsequent ioctl():s when the chip is gone */
- if (!gdev->chip)
+ if (!rcu_access_pointer(gdev->chip))
return -ENODEV;
/* Fill in the struct and pass to userspace */
@@ -2594,9 +2593,9 @@ static __poll_t lineinfo_watch_poll(struct file *file,
struct gpio_chardev_data *cdev = file->private_data;
__poll_t events = 0;
- guard(rwsem_read)(&cdev->gdev->sem);
+ guard(srcu)(&cdev->gdev->srcu);
- if (!cdev->gdev->chip)
+ if (!rcu_access_pointer(cdev->gdev->chip))
return EPOLLHUP | EPOLLERR;
poll_wait(file, &cdev->wait, pollt);
@@ -2617,9 +2616,9 @@ static ssize_t lineinfo_watch_read(struct file *file, char __user *buf,
int ret;
size_t event_size;
- guard(rwsem_read)(&cdev->gdev->sem);
+ guard(srcu)(&cdev->gdev->srcu);
- if (!cdev->gdev->chip)
+ if (!rcu_access_pointer(cdev->gdev->chip))
return -ENODEV;
#ifndef CONFIG_GPIO_CDEV_V1
@@ -2694,17 +2693,17 @@ static int gpio_chrdev_open(struct inode *inode, struct file *file)
struct gpio_chardev_data *cdev;
int ret = -ENOMEM;
- guard(rwsem_read)(&gdev->sem);
+ guard(srcu)(&gdev->srcu);
/* Fail on open if the backing gpiochip is gone */
- if (!gdev->chip)
+ if (!rcu_access_pointer(gdev->chip))
return -ENODEV;
cdev = kzalloc(sizeof(*cdev), GFP_KERNEL);
if (!cdev)
return -ENODEV;
- cdev->watched_lines = bitmap_zalloc(gdev->chip->ngpio, GFP_KERNEL);
+ cdev->watched_lines = bitmap_zalloc(gdev->ngpio, GFP_KERNEL);
if (!cdev->watched_lines)
goto out_free_cdev;
@@ -2784,6 +2783,7 @@ static const struct file_operations gpio_fileops = {
int gpiolib_cdev_register(struct gpio_device *gdev, dev_t devt)
{
+ struct gpio_chip *gc;
int ret;
cdev_init(&gdev->chrdev, &gpio_fileops);
@@ -2794,8 +2794,12 @@ int gpiolib_cdev_register(struct gpio_device *gdev, dev_t devt)
if (ret)
return ret;
- chip_dbg(gdev->chip, "added GPIO chardev (%d:%d)\n",
- MAJOR(devt), gdev->id);
+ guard(srcu)(&gdev->srcu);
+ gc = srcu_dereference(gdev->chip, &gdev->srcu);
+ if (!gc)
+ return -ENODEV;
+
+ chip_dbg(gc, "added GPIO chardev (%d:%d)\n", MAJOR(devt), gdev->id);
return 0;
}
diff --git a/drivers/gpio/gpiolib-devres.c b/drivers/gpio/gpiolib-devres.c
index fe9ce6b19f15..4987e62dcb3d 100644
--- a/drivers/gpio/gpiolib-devres.c
+++ b/drivers/gpio/gpiolib-devres.c
@@ -158,7 +158,7 @@ struct gpio_desc *devm_fwnode_gpiod_get_index(struct device *dev,
if (!dr)
return ERR_PTR(-ENOMEM);
- desc = fwnode_gpiod_get_index(fwnode, con_id, index, flags, label);
+ desc = gpiod_find_and_request(dev, fwnode, con_id, index, flags, label, false);
if (IS_ERR(desc)) {
devres_free(dr);
return desc;
diff --git a/drivers/gpio/gpiolib-legacy.c b/drivers/gpio/gpiolib-legacy.c
index 97f4b498e343..b138682fec3d 100644
--- a/drivers/gpio/gpiolib-legacy.c
+++ b/drivers/gpio/gpiolib-legacy.c
@@ -6,6 +6,9 @@
#include "gpiolib.h"
+/*
+ * **DEPRECATED** This function is deprecated and must not be used in new code.
+ */
void gpio_free(unsigned gpio)
{
gpiod_free(gpio_to_desc(gpio));
@@ -17,6 +20,8 @@ EXPORT_SYMBOL_GPL(gpio_free);
* @gpio: the GPIO number
* @flags: GPIO configuration as specified by GPIOF_*
* @label: a literal description string of this GPIO
+ *
+ * **DEPRECATED** This function is deprecated and must not be used in new code.
*/
int gpio_request_one(unsigned gpio, unsigned long flags, const char *label)
{
@@ -53,6 +58,9 @@ int gpio_request_one(unsigned gpio, unsigned long flags, const char *label)
}
EXPORT_SYMBOL_GPL(gpio_request_one);
+/*
+ * **DEPRECATED** This function is deprecated and must not be used in new code.
+ */
int gpio_request(unsigned gpio, const char *label)
{
struct gpio_desc *desc = gpio_to_desc(gpio);
@@ -69,6 +77,8 @@ EXPORT_SYMBOL_GPL(gpio_request);
* gpio_request_array - request multiple GPIOs in a single call
* @array: array of the 'struct gpio'
* @num: how many GPIOs in the array
+ *
+ * **DEPRECATED** This function is deprecated and must not be used in new code.
*/
int gpio_request_array(const struct gpio *array, size_t num)
{
@@ -92,6 +102,8 @@ EXPORT_SYMBOL_GPL(gpio_request_array);
* gpio_free_array - release multiple GPIOs in a single call
* @array: array of the 'struct gpio'
* @num: how many GPIOs in the array
+ *
+ * **DEPRECATED** This function is deprecated and must not be used in new code.
*/
void gpio_free_array(const struct gpio *array, size_t num)
{
diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c
index e7770eedd146..cb0cefaec37e 100644
--- a/drivers/gpio/gpiolib-of.c
+++ b/drivers/gpio/gpiolib-of.c
@@ -68,7 +68,7 @@ static int of_gpio_named_count(const struct device_node *np,
/**
* of_gpio_spi_cs_get_count() - special GPIO counting for SPI
- * @dev: Consuming device
+ * @np: Consuming device node
* @con_id: Function within the GPIO consumer
*
* Some elder GPIO controllers need special quirks. Currently we handle
@@ -78,10 +78,9 @@ static int of_gpio_named_count(const struct device_node *np,
* the counting of "cs-gpios" to count "gpios" transparent to the
* driver.
*/
-static int of_gpio_spi_cs_get_count(struct device *dev, const char *con_id)
+static int of_gpio_spi_cs_get_count(const struct device_node *np,
+ const char *con_id)
{
- struct device_node *np = dev->of_node;
-
if (!IS_ENABLED(CONFIG_SPI_MASTER))
return 0;
if (!con_id || strcmp(con_id, "cs"))
@@ -93,13 +92,14 @@ static int of_gpio_spi_cs_get_count(struct device *dev, const char *con_id)
return of_gpio_named_count(np, "gpios");
}
-int of_gpio_get_count(struct device *dev, const char *con_id)
+int of_gpio_count(const struct fwnode_handle *fwnode, const char *con_id)
{
+ const struct device_node *np = to_of_node(fwnode);
int ret;
char propname[32];
unsigned int i;
- ret = of_gpio_spi_cs_get_count(dev, con_id);
+ ret = of_gpio_spi_cs_get_count(np, con_id);
if (ret > 0)
return ret;
@@ -111,16 +111,17 @@ int of_gpio_get_count(struct device *dev, const char *con_id)
snprintf(propname, sizeof(propname), "%s",
gpio_suffixes[i]);
- ret = of_gpio_named_count(dev->of_node, propname);
+ ret = of_gpio_named_count(np, propname);
if (ret > 0)
break;
}
return ret ? ret : -ENOENT;
}
-static int of_gpiochip_match_node_and_xlate(struct gpio_chip *chip, void *data)
+static int of_gpiochip_match_node_and_xlate(struct gpio_chip *chip,
+ const void *data)
{
- struct of_phandle_args *gpiospec = data;
+ const struct of_phandle_args *gpiospec = data;
return device_match_of_node(&chip->gpiodev->dev, gpiospec->np) &&
chip->of_xlate &&
@@ -128,7 +129,7 @@ static int of_gpiochip_match_node_and_xlate(struct gpio_chip *chip, void *data)
}
static struct gpio_device *
-of_find_gpio_device_by_xlate(struct of_phandle_args *gpiospec)
+of_find_gpio_device_by_xlate(const struct of_phandle_args *gpiospec)
{
return gpio_device_find(gpiospec, of_gpiochip_match_node_and_xlate);
}
@@ -414,6 +415,8 @@ out:
* @propname: Name of property containing gpio specifier(s)
* @index: index of the GPIO
*
+ * **DEPRECATED** This function is deprecated and must not be used in new code.
+ *
* Returns GPIO number to use with Linux generic GPIO API, or one of the errno
* value on the error condition.
*/
@@ -798,7 +801,7 @@ static int of_gpiochip_add_hog(struct gpio_chip *chip, struct device_node *hog)
return ret;
#ifdef CONFIG_OF_DYNAMIC
- desc->hog = hog;
+ WRITE_ONCE(desc->hog, hog);
#endif
}
@@ -846,11 +849,11 @@ static void of_gpiochip_remove_hog(struct gpio_chip *chip,
struct gpio_desc *desc;
for_each_gpio_desc_with_flag(chip, desc, FLAG_IS_HOGGED)
- if (desc->hog == hog)
+ if (READ_ONCE(desc->hog) == hog)
gpiochip_free_own_desc(desc);
}
-static int of_gpiochip_match_node(struct gpio_chip *chip, void *data)
+static int of_gpiochip_match_node(struct gpio_chip *chip, const void *data)
{
return device_match_of_node(&chip->gpiodev->dev, data);
}
diff --git a/drivers/gpio/gpiolib-of.h b/drivers/gpio/gpiolib-of.h
index 6b3a5347c5d9..16d6ac8cb156 100644
--- a/drivers/gpio/gpiolib-of.h
+++ b/drivers/gpio/gpiolib-of.h
@@ -9,6 +9,7 @@
#include <linux/notifier.h>
struct device;
+struct fwnode_handle;
struct gpio_chip;
struct gpio_desc;
@@ -21,7 +22,7 @@ struct gpio_desc *of_find_gpio(struct device_node *np,
unsigned long *lookupflags);
int of_gpiochip_add(struct gpio_chip *gc);
void of_gpiochip_remove(struct gpio_chip *gc);
-int of_gpio_get_count(struct device *dev, const char *con_id);
+int of_gpio_count(const struct fwnode_handle *fwnode, const char *con_id);
#else
static inline struct gpio_desc *of_find_gpio(struct device_node *np,
const char *con_id,
@@ -32,7 +33,8 @@ static inline struct gpio_desc *of_find_gpio(struct device_node *np,
}
static inline int of_gpiochip_add(struct gpio_chip *gc) { return 0; }
static inline void of_gpiochip_remove(struct gpio_chip *gc) { }
-static inline int of_gpio_get_count(struct device *dev, const char *con_id)
+static inline int of_gpio_count(const struct fwnode_handle *fwnode,
+ const char *con_id)
{
return 0;
}
diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c
index 6bf5332136e5..67fc09a57f26 100644
--- a/drivers/gpio/gpiolib-sysfs.c
+++ b/drivers/gpio/gpiolib-sysfs.c
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/bitops.h>
+#include <linux/cleanup.h>
#include <linux/device.h>
#include <linux/idr.h>
#include <linux/init.h>
@@ -13,6 +14,7 @@
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/string.h>
+#include <linux/srcu.h>
#include <linux/sysfs.h>
#include <linux/types.h>
@@ -170,6 +172,10 @@ static int gpio_sysfs_request_irq(struct device *dev, unsigned char flags)
unsigned long irq_flags;
int ret;
+ CLASS(gpio_chip_guard, guard)(desc);
+ if (!guard.gc)
+ return -ENODEV;
+
data->irq = gpiod_to_irq(desc);
if (data->irq < 0)
return -EIO;
@@ -194,7 +200,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->gdev->chip, gpio_chip_hwgpio(desc));
+ ret = gpiochip_lock_as_irq(guard.gc, gpio_chip_hwgpio(desc));
if (ret < 0)
goto err_put_kn;
@@ -208,7 +214,7 @@ static int gpio_sysfs_request_irq(struct device *dev, unsigned char flags)
return 0;
err_unlock:
- gpiochip_unlock_as_irq(desc->gdev->chip, gpio_chip_hwgpio(desc));
+ gpiochip_unlock_as_irq(guard.gc, gpio_chip_hwgpio(desc));
err_put_kn:
sysfs_put(data->value_kn);
@@ -224,9 +230,13 @@ static void gpio_sysfs_free_irq(struct device *dev)
struct gpiod_data *data = dev_get_drvdata(dev);
struct gpio_desc *desc = data->desc;
+ CLASS(gpio_chip_guard, guard)(desc);
+ if (!guard.gc)
+ return;
+
data->irq_flags = 0;
free_irq(data->irq, data);
- gpiochip_unlock_as_irq(desc->gdev->chip, gpio_chip_hwgpio(desc));
+ gpiochip_unlock_as_irq(guard.gc, gpio_chip_hwgpio(desc));
sysfs_put(data->value_kn);
}
@@ -400,27 +410,27 @@ static const struct attribute_group *gpio_groups[] = {
static ssize_t base_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
- const struct gpio_chip *chip = dev_get_drvdata(dev);
+ const struct gpio_device *gdev = dev_get_drvdata(dev);
- return sysfs_emit(buf, "%d\n", chip->base);
+ return sysfs_emit(buf, "%d\n", gdev->base);
}
static DEVICE_ATTR_RO(base);
static ssize_t label_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
- const struct gpio_chip *chip = dev_get_drvdata(dev);
+ const struct gpio_device *gdev = dev_get_drvdata(dev);
- return sysfs_emit(buf, "%s\n", chip->label ?: "");
+ return sysfs_emit(buf, "%s\n", gdev->label);
}
static DEVICE_ATTR_RO(label);
static ssize_t ngpio_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
- const struct gpio_chip *chip = dev_get_drvdata(dev);
+ const struct gpio_device *gdev = dev_get_drvdata(dev);
- return sysfs_emit(buf, "%u\n", chip->ngpio);
+ return sysfs_emit(buf, "%u\n", gdev->ngpio);
}
static DEVICE_ATTR_RO(ngpio);
@@ -443,13 +453,12 @@ static ssize_t export_store(const struct class *class,
const char *buf, size_t len)
{
struct gpio_desc *desc;
- struct gpio_chip *gc;
int status, offset;
long gpio;
status = kstrtol(buf, 0, &gpio);
- if (status < 0)
- goto done;
+ if (status)
+ return status;
desc = gpio_to_desc(gpio);
/* reject invalid GPIOs */
@@ -457,9 +466,13 @@ static ssize_t export_store(const struct class *class,
pr_warn("%s: invalid GPIO %ld\n", __func__, gpio);
return -EINVAL;
}
- gc = desc->gdev->chip;
+
+ CLASS(gpio_chip_guard, guard)(desc);
+ if (!guard.gc)
+ return -ENODEV;
+
offset = gpio_chip_hwgpio(desc);
- if (!gpiochip_line_is_valid(gc, offset)) {
+ if (!gpiochip_line_is_valid(guard.gc, offset)) {
pr_warn("%s: GPIO %ld masked\n", __func__, gpio);
return -EINVAL;
}
@@ -562,8 +575,6 @@ int gpiod_export(struct gpio_desc *desc, bool direction_may_change)
const char *ioname = NULL;
struct gpio_device *gdev;
struct gpiod_data *data;
- struct gpio_chip *chip;
- unsigned long flags;
struct device *dev;
int status, offset;
@@ -578,29 +589,28 @@ int gpiod_export(struct gpio_desc *desc, bool direction_may_change)
return -EINVAL;
}
+ CLASS(gpio_chip_guard, guard)(desc);
+ if (!guard.gc)
+ return -ENODEV;
+
+ if (!test_and_set_bit(FLAG_EXPORT, &desc->flags))
+ return -EPERM;
+
gdev = desc->gdev;
- chip = gdev->chip;
mutex_lock(&sysfs_lock);
/* check if chip is being removed */
- if (!chip || !gdev->mockdev) {
+ if (!gdev->mockdev) {
status = -ENODEV;
goto err_unlock;
}
- spin_lock_irqsave(&gpio_lock, flags);
- if (!test_bit(FLAG_REQUESTED, &desc->flags) ||
- test_bit(FLAG_EXPORT, &desc->flags)) {
- spin_unlock_irqrestore(&gpio_lock, flags);
- gpiod_dbg(desc, "%s: unavailable (requested=%d, exported=%d)\n",
- __func__,
- test_bit(FLAG_REQUESTED, &desc->flags),
- test_bit(FLAG_EXPORT, &desc->flags));
+ if (!test_bit(FLAG_REQUESTED, &desc->flags)) {
+ gpiod_dbg(desc, "%s: unavailable (not requested)\n", __func__);
status = -EPERM;
goto err_unlock;
}
- spin_unlock_irqrestore(&gpio_lock, flags);
data = kzalloc(sizeof(*data), GFP_KERNEL);
if (!data) {
@@ -610,14 +620,14 @@ int gpiod_export(struct gpio_desc *desc, bool direction_may_change)
data->desc = desc;
mutex_init(&data->mutex);
- if (chip->direction_input && chip->direction_output)
+ if (guard.gc->direction_input && guard.gc->direction_output)
data->direction_can_change = direction_may_change;
else
data->direction_can_change = false;
offset = gpio_chip_hwgpio(desc);
- if (chip->names && chip->names[offset])
- ioname = chip->names[offset];
+ if (guard.gc->names && guard.gc->names[offset])
+ ioname = guard.gc->names[offset];
dev = device_create_with_groups(&gpio_class, &gdev->dev,
MKDEV(0, 0), data, gpio_groups,
@@ -628,7 +638,6 @@ int gpiod_export(struct gpio_desc *desc, bool direction_may_change)
goto err_free_data;
}
- set_bit(FLAG_EXPORT, &desc->flags);
mutex_unlock(&sysfs_lock);
return 0;
@@ -636,6 +645,7 @@ err_free_data:
kfree(data);
err_unlock:
mutex_unlock(&sysfs_lock);
+ clear_bit(FLAG_EXPORT, &desc->flags);
gpiod_dbg(desc, "%s: status %d\n", __func__, status);
return status;
}
@@ -732,7 +742,7 @@ EXPORT_SYMBOL_GPL(gpiod_unexport);
int gpiochip_sysfs_register(struct gpio_device *gdev)
{
- struct gpio_chip *chip = gdev->chip;
+ struct gpio_chip *chip;
struct device *parent;
struct device *dev;
@@ -745,6 +755,12 @@ int gpiochip_sysfs_register(struct gpio_device *gdev)
if (!class_is_registered(&gpio_class))
return 0;
+ guard(srcu)(&gdev->srcu);
+
+ chip = srcu_dereference(gdev->chip, &gdev->srcu);
+ if (!chip)
+ return -ENODEV;
+
/*
* For sysfs backward compatibility we need to preserve this
* preferred parenting to the gpio_chip parent field, if set.
@@ -755,7 +771,7 @@ int gpiochip_sysfs_register(struct gpio_device *gdev)
parent = &gdev->dev;
/* use chip->base for the ID; it's already known to be unique */
- dev = device_create_with_groups(&gpio_class, parent, MKDEV(0, 0), chip,
+ dev = device_create_with_groups(&gpio_class, parent, MKDEV(0, 0), gdev,
gpiochip_groups, GPIOCHIP_NAME "%d",
chip->base);
if (IS_ERR(dev))
@@ -771,17 +787,23 @@ int gpiochip_sysfs_register(struct gpio_device *gdev)
void gpiochip_sysfs_unregister(struct gpio_device *gdev)
{
struct gpio_desc *desc;
- struct gpio_chip *chip = gdev->chip;
+ struct gpio_chip *chip;
- if (!gdev->mockdev)
- return;
+ scoped_guard(mutex, &sysfs_lock) {
+ if (!gdev->mockdev)
+ return;
- device_unregister(gdev->mockdev);
+ device_unregister(gdev->mockdev);
- /* prevent further gpiod exports */
- mutex_lock(&sysfs_lock);
- gdev->mockdev = NULL;
- mutex_unlock(&sysfs_lock);
+ /* prevent further gpiod exports */
+ gdev->mockdev = NULL;
+ }
+
+ guard(srcu)(&gdev->srcu);
+
+ chip = srcu_dereference(gdev->chip, &gdev->srcu);
+ if (!chip)
+ return;
/* unregister gpiod class devices owned by sysfs */
for_each_gpio_desc_with_flag(chip, desc, FLAG_SYSFS) {
@@ -790,11 +812,29 @@ void gpiochip_sysfs_unregister(struct gpio_device *gdev)
}
}
+/*
+ * We're not really looking for a device - we just want to iterate over the
+ * list and call this callback for each GPIO device. This is why this function
+ * always returns 0.
+ */
+static int gpiofind_sysfs_register(struct gpio_chip *gc, const void *data)
+{
+ struct gpio_device *gdev = gc->gpiodev;
+ int ret;
+
+ if (gdev->mockdev)
+ return 0;
+
+ ret = gpiochip_sysfs_register(gdev);
+ if (ret)
+ chip_err(gc, "failed to register the sysfs entry: %d\n", ret);
+
+ return 0;
+}
+
static int __init gpiolib_sysfs_init(void)
{
- int status;
- unsigned long flags;
- struct gpio_device *gdev;
+ int status;
status = class_register(&gpio_class);
if (status < 0)
@@ -806,26 +846,8 @@ static int __init gpiolib_sysfs_init(void)
* We run before arch_initcall() so chip->dev nodes can have
* registered, and so arch_initcall() can always gpiod_export().
*/
- spin_lock_irqsave(&gpio_lock, flags);
- list_for_each_entry(gdev, &gpio_devices, list) {
- if (gdev->mockdev)
- continue;
-
- /*
- * TODO we yield gpio_lock here because
- * gpiochip_sysfs_register() acquires a mutex. This is unsafe
- * and needs to be fixed.
- *
- * Also it would be nice to use gpio_device_find() here so we
- * can keep gpio_chips local to gpiolib.c, but the yield of
- * gpio_lock prevents us from doing this.
- */
- spin_unlock_irqrestore(&gpio_lock, flags);
- status = gpiochip_sysfs_register(gdev);
- spin_lock_irqsave(&gpio_lock, flags);
- }
- spin_unlock_irqrestore(&gpio_lock, flags);
+ (void)gpio_device_find(NULL, gpiofind_sysfs_register);
- return status;
+ return 0;
}
postcore_initcall(gpiolib_sysfs_init);
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index 75be4a3ca7f8..e2e583b40207 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -2,6 +2,7 @@
#include <linux/acpi.h>
#include <linux/bitmap.h>
+#include <linux/cleanup.h>
#include <linux/compat.h>
#include <linux/debugfs.h>
#include <linux/device.h>
@@ -14,12 +15,14 @@
#include <linux/irq.h>
#include <linux/kernel.h>
#include <linux/list.h>
+#include <linux/lockdep.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/pinctrl/consumer.h>
#include <linux/seq_file.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
+#include <linux/srcu.h>
#include <linux/string.h>
#include <linux/gpio.h>
@@ -63,7 +66,7 @@ static int gpio_bus_match(struct device *dev, struct device_driver *drv)
return 1;
}
-static struct bus_type gpio_bus_type = {
+static const struct bus_type gpio_bus_type = {
.name = "gpio",
.match = gpio_bus_match,
};
@@ -73,15 +76,14 @@ static struct bus_type gpio_bus_type = {
*/
#define FASTPATH_NGPIO CONFIG_GPIOLIB_FASTPATH_LIMIT
-/* 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.
- */
-DEFINE_SPINLOCK(gpio_lock);
-
static DEFINE_MUTEX(gpio_lookup_lock);
static LIST_HEAD(gpio_lookup_list);
-LIST_HEAD(gpio_devices);
+
+static LIST_HEAD(gpio_devices);
+/* Protects the GPIO device list against concurrent modifications. */
+static DEFINE_MUTEX(gpio_devices_lock);
+/* Ensures coherence during read-only accesses to the list of GPIO devices. */
+DEFINE_STATIC_SRCU(gpio_devices_srcu);
static DEFINE_MUTEX(gpio_machine_hogs_mutex);
static LIST_HEAD(gpio_machine_hogs);
@@ -97,9 +99,34 @@ static void gpiochip_irqchip_free_valid_mask(struct gpio_chip *gc);
static bool gpiolib_initialized;
-static inline void desc_set_label(struct gpio_desc *d, const char *label)
+const char *gpiod_get_label(struct gpio_desc *desc)
{
- d->label = label;
+ unsigned long flags;
+
+ flags = READ_ONCE(desc->flags);
+ if (test_bit(FLAG_USED_AS_IRQ, &flags) &&
+ !test_bit(FLAG_REQUESTED, &flags))
+ return "interrupt";
+
+ return test_bit(FLAG_REQUESTED, &flags) ?
+ srcu_dereference(desc->label, &desc->srcu) : NULL;
+}
+
+static int desc_set_label(struct gpio_desc *desc, const char *label)
+{
+ const char *new = NULL, *old;
+
+ if (label) {
+ new = kstrdup_const(label, GFP_KERNEL);
+ if (!new)
+ return -ENOMEM;
+ }
+
+ old = rcu_replace_pointer(desc->label, new, 1);
+ synchronize_srcu(&desc->srcu);
+ kfree_const(old);
+
+ return 0;
}
/**
@@ -113,20 +140,16 @@ static inline void desc_set_label(struct gpio_desc *d, const char *label)
struct gpio_desc *gpio_to_desc(unsigned gpio)
{
struct gpio_device *gdev;
- unsigned long flags;
-
- spin_lock_irqsave(&gpio_lock, flags);
- list_for_each_entry(gdev, &gpio_devices, list) {
- if (gdev->base <= gpio &&
- gdev->base + gdev->ngpio > gpio) {
- spin_unlock_irqrestore(&gpio_lock, flags);
- return &gdev->descs[gpio - gdev->base];
+ scoped_guard(srcu, &gpio_devices_srcu) {
+ list_for_each_entry_srcu(gdev, &gpio_devices, list,
+ srcu_read_lock_held(&gpio_devices_srcu)) {
+ if (gdev->base <= gpio &&
+ gdev->base + gdev->ngpio > gpio)
+ return &gdev->descs[gpio - gdev->base];
}
}
- spin_unlock_irqrestore(&gpio_lock, flags);
-
if (!gpio_is_valid(gpio))
pr_warn("invalid GPIO %d\n", gpio);
@@ -161,16 +184,6 @@ EXPORT_SYMBOL_GPL(gpiochip_get_desc);
struct gpio_desc *
gpio_device_get_desc(struct gpio_device *gdev, unsigned int hwnum)
{
- struct gpio_chip *gc;
-
- /*
- * FIXME: This will be locked once we protect gdev->chip everywhere
- * with SRCU.
- */
- gc = gdev->chip;
- if (!gc)
- return ERR_PTR(-ENODEV);
-
if (hwnum >= gdev->ngpio)
return ERR_PTR(-EINVAL);
@@ -198,12 +211,18 @@ EXPORT_SYMBOL_GPL(desc_to_gpio);
/**
* gpiod_to_chip - Return the GPIO chip to which a GPIO descriptor belongs
* @desc: descriptor to return the chip of
+ *
+ * *DEPRECATED*
+ * This function is unsafe and should not be used. Using the chip address
+ * without taking the SRCU read lock may result in dereferencing a dangling
+ * pointer.
*/
struct gpio_chip *gpiod_to_chip(const struct gpio_desc *desc)
{
- if (!desc || !desc->gdev)
+ if (!desc)
return NULL;
- return desc->gdev->chip;
+
+ return gpio_device_get_chip(desc->gdev);
}
EXPORT_SYMBOL_GPL(gpiod_to_chip);
@@ -262,6 +281,7 @@ EXPORT_SYMBOL(gpio_device_get_label);
* Returns:
* Address of the GPIO chip backing this device.
*
+ * *DEPRECATED*
* Until we can get rid of all non-driver users of struct gpio_chip, we must
* provide a way of retrieving the pointer to it from struct gpio_device. This
* is *NOT* safe as the GPIO API is considered to be hot-unpluggable and the
@@ -272,7 +292,7 @@ EXPORT_SYMBOL(gpio_device_get_label);
*/
struct gpio_chip *gpio_device_get_chip(struct gpio_device *gdev)
{
- return gdev->chip;
+ return rcu_dereference_check(gdev->chip, 1);
}
EXPORT_SYMBOL_GPL(gpio_device_get_chip);
@@ -282,7 +302,8 @@ static int gpiochip_find_base_unlocked(int ngpio)
struct gpio_device *gdev;
int base = GPIO_DYNAMIC_BASE;
- list_for_each_entry(gdev, &gpio_devices, list) {
+ list_for_each_entry_srcu(gdev, &gpio_devices, list,
+ lockdep_is_held(&gpio_devices_lock)) {
/* found a free space? */
if (gdev->base >= base + ngpio)
break;
@@ -311,25 +332,36 @@ static int gpiochip_find_base_unlocked(int ngpio)
*/
int gpiod_get_direction(struct gpio_desc *desc)
{
- struct gpio_chip *gc;
+ unsigned long flags;
unsigned int offset;
int ret;
- gc = gpiod_to_chip(desc);
+ /*
+ * We cannot use VALIDATE_DESC() as we must not return 0 for a NULL
+ * descriptor like we usually do.
+ */
+ if (!desc || IS_ERR(desc))
+ return -EINVAL;
+
+ CLASS(gpio_chip_guard, guard)(desc);
+ if (!guard.gc)
+ return -ENODEV;
+
offset = gpio_chip_hwgpio(desc);
+ flags = READ_ONCE(desc->flags);
/*
* Open drain emulation using input mode may incorrectly report
* input here, fix that up.
*/
- if (test_bit(FLAG_OPEN_DRAIN, &desc->flags) &&
- test_bit(FLAG_IS_OUT, &desc->flags))
+ if (test_bit(FLAG_OPEN_DRAIN, &flags) &&
+ test_bit(FLAG_IS_OUT, &flags))
return 0;
- if (!gc->get_direction)
+ if (!guard.gc->get_direction)
return -ENOTSUPP;
- ret = gc->get_direction(gc, offset);
+ ret = guard.gc->get_direction(guard.gc, offset);
if (ret < 0)
return ret;
@@ -337,7 +369,8 @@ int gpiod_get_direction(struct gpio_desc *desc)
if (ret > 0)
ret = 1;
- assign_bit(FLAG_IS_OUT, &desc->flags, !ret);
+ assign_bit(FLAG_IS_OUT, &flags, !ret);
+ WRITE_ONCE(desc->flags, flags);
return ret;
}
@@ -354,23 +387,25 @@ static int gpiodev_add_to_list_unlocked(struct gpio_device *gdev)
{
struct gpio_device *prev, *next;
+ lockdep_assert_held(&gpio_devices_lock);
+
if (list_empty(&gpio_devices)) {
/* initial entry in list */
- list_add_tail(&gdev->list, &gpio_devices);
+ list_add_tail_rcu(&gdev->list, &gpio_devices);
return 0;
}
next = list_first_entry(&gpio_devices, struct gpio_device, list);
if (gdev->base + gdev->ngpio <= next->base) {
/* add before first entry */
- list_add(&gdev->list, &gpio_devices);
+ list_add_rcu(&gdev->list, &gpio_devices);
return 0;
}
prev = list_last_entry(&gpio_devices, struct gpio_device, list);
if (prev->base + prev->ngpio <= gdev->base) {
/* add behind last entry */
- list_add_tail(&gdev->list, &gpio_devices);
+ list_add_tail_rcu(&gdev->list, &gpio_devices);
return 0;
}
@@ -382,11 +417,13 @@ static int gpiodev_add_to_list_unlocked(struct gpio_device *gdev)
/* add between prev and next */
if (prev->base + prev->ngpio <= gdev->base
&& gdev->base + gdev->ngpio <= next->base) {
- list_add(&gdev->list, &prev->list);
+ list_add_rcu(&gdev->list, &prev->list);
return 0;
}
}
+ synchronize_srcu(&gpio_devices_srcu);
+
return -EBUSY;
}
@@ -399,26 +436,28 @@ static int gpiodev_add_to_list_unlocked(struct gpio_device *gdev)
static struct gpio_desc *gpio_name_to_desc(const char * const name)
{
struct gpio_device *gdev;
- unsigned long flags;
+ struct gpio_desc *desc;
+ struct gpio_chip *gc;
if (!name)
return NULL;
- spin_lock_irqsave(&gpio_lock, flags);
+ guard(srcu)(&gpio_devices_srcu);
- list_for_each_entry(gdev, &gpio_devices, list) {
- struct gpio_desc *desc;
+ list_for_each_entry_srcu(gdev, &gpio_devices, list,
+ srcu_read_lock_held(&gpio_devices_srcu)) {
+ guard(srcu)(&gdev->srcu);
- for_each_gpio_desc(gdev->chip, desc) {
- if (desc->name && !strcmp(desc->name, name)) {
- spin_unlock_irqrestore(&gpio_lock, flags);
+ gc = srcu_dereference(gdev->chip, &gdev->srcu);
+ if (!gc)
+ continue;
+
+ for_each_gpio_desc(gc, desc) {
+ if (desc->name && !strcmp(desc->name, name))
return desc;
- }
}
}
- spin_unlock_irqrestore(&gpio_lock, flags);
-
return NULL;
}
@@ -656,13 +695,23 @@ EXPORT_SYMBOL_GPL(gpiochip_line_is_valid);
static void gpiodev_release(struct device *dev)
{
struct gpio_device *gdev = to_gpio_device(dev);
+ unsigned int i;
+
+ for (i = 0; i < gdev->ngpio; i++)
+ cleanup_srcu_struct(&gdev->descs[i].srcu);
ida_free(&gpio_ida, gdev->id);
kfree_const(gdev->label);
kfree(gdev->descs);
+ cleanup_srcu_struct(&gdev->srcu);
kfree(gdev);
}
+static const struct device_type gpio_dev_type = {
+ .name = "gpio_chip",
+ .release = gpiodev_release,
+};
+
#ifdef CONFIG_GPIO_CDEV
#define gcdev_register(gdev, devt) gpiolib_cdev_register((gdev), (devt))
#define gcdev_unregister(gdev) gpiolib_cdev_unregister((gdev))
@@ -680,6 +729,8 @@ static int gpiochip_setup_dev(struct gpio_device *gdev)
struct fwnode_handle *fwnode = dev_fwnode(&gdev->dev);
int ret;
+ device_initialize(&gdev->dev);
+
/*
* If fwnode doesn't belong to another device, it's safe to clear its
* initialized flag.
@@ -691,15 +742,12 @@ static int gpiochip_setup_dev(struct gpio_device *gdev)
if (ret)
return ret;
- /* From this point, the .release() function cleans up gpio_device */
- gdev->dev.release = gpiodev_release;
-
ret = gpiochip_sysfs_register(gdev);
if (ret)
goto err_remove_device;
dev_dbg(&gdev->dev, "registered GPIOs %d to %d on %s\n", gdev->base,
- gdev->base + gdev->ngpio - 1, gdev->chip->label ? : "generic");
+ gdev->base + gdev->ngpio - 1, gdev->label);
return 0;
@@ -720,9 +768,6 @@ static void gpiochip_machine_hog(struct gpio_chip *gc, struct gpiod_hog *hog)
return;
}
- if (test_bit(FLAG_IS_HOGGED, &desc->flags))
- return;
-
rv = gpiod_hog(desc, hog->line_name, hog->lflags, hog->dflags);
if (rv)
gpiod_err(desc, "%s: unable to hog GPIO line (%s:%u): %d\n",
@@ -748,7 +793,10 @@ static void gpiochip_setup_devs(void)
struct gpio_device *gdev;
int ret;
- list_for_each_entry(gdev, &gpio_devices, list) {
+ guard(srcu)(&gpio_devices_srcu);
+
+ list_for_each_entry_srcu(gdev, &gpio_devices, list,
+ srcu_read_lock_held(&gpio_devices_srcu)) {
ret = gpiochip_setup_dev(gdev);
if (ret)
dev_err(&gdev->dev,
@@ -813,8 +861,7 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data,
struct lock_class_key *request_key)
{
struct gpio_device *gdev;
- unsigned long flags;
- unsigned int i;
+ unsigned int i, j;
int base = 0;
int ret = 0;
@@ -825,9 +872,11 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data,
gdev = kzalloc(sizeof(*gdev), GFP_KERNEL);
if (!gdev)
return -ENOMEM;
+
+ gdev->dev.type = &gpio_dev_type;
gdev->dev.bus = &gpio_bus_type;
gdev->dev.parent = gc->parent;
- gdev->chip = gc;
+ rcu_assign_pointer(gdev->chip, gc);
gc->gpiodev = gdev;
gpiochip_set_data(gc, data);
@@ -851,7 +900,6 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data,
if (ret)
goto err_free_ida;
- device_initialize(&gdev->dev);
if (gc->parent && gc->parent->driver)
gdev->owner = gc->parent->driver->owner;
else if (gc->owner)
@@ -877,53 +925,55 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data,
}
gdev->ngpio = gc->ngpio;
+ gdev->can_sleep = gc->can_sleep;
- 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.
- */
- base = gc->base;
- if (base < 0) {
- base = gpiochip_find_base_unlocked(gc->ngpio);
- if (base < 0) {
- spin_unlock_irqrestore(&gpio_lock, flags);
- ret = base;
- base = 0;
- goto err_free_label;
- }
+ scoped_guard(mutex, &gpio_devices_lock) {
/*
- * 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.
+ * 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.
*/
- gc->base = base;
- } else {
- dev_warn(&gdev->dev,
- "Static allocation of GPIO base is deprecated, use dynamic allocation.\n");
- }
- gdev->base = base;
+ base = gc->base;
+ if (base < 0) {
+ base = gpiochip_find_base_unlocked(gc->ngpio);
+ if (base < 0) {
+ ret = base;
+ base = 0;
+ goto err_free_label;
+ }
- ret = gpiodev_add_to_list_unlocked(gdev);
- if (ret) {
- spin_unlock_irqrestore(&gpio_lock, flags);
- chip_err(gc, "GPIO integer space overlap, cannot add chip\n");
- 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.
+ */
+ gc->base = base;
+ } else {
+ dev_warn(&gdev->dev,
+ "Static allocation of GPIO base is deprecated, use dynamic allocation.\n");
+ }
+
+ gdev->base = base;
+
+ ret = gpiodev_add_to_list_unlocked(gdev);
+ if (ret) {
+ chip_err(gc, "GPIO integer space overlap, cannot add chip\n");
+ goto err_free_label;
+ }
}
for (i = 0; i < gc->ngpio; i++)
gdev->descs[i].gdev = gdev;
- spin_unlock_irqrestore(&gpio_lock, flags);
-
BLOCKING_INIT_NOTIFIER_HEAD(&gdev->line_state_notifier);
BLOCKING_INIT_NOTIFIER_HEAD(&gdev->device_notifier);
- init_rwsem(&gdev->sem);
+
+ ret = init_srcu_struct(&gdev->srcu);
+ if (ret)
+ goto err_remove_from_list;
#ifdef CONFIG_PINCTRL
INIT_LIST_HEAD(&gdev->pin_ranges);
@@ -932,23 +982,26 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data,
if (gc->names) {
ret = gpiochip_set_desc_names(gc);
if (ret)
- goto err_remove_from_list;
+ goto err_cleanup_gdev_srcu;
}
ret = gpiochip_set_names(gc);
if (ret)
- goto err_remove_from_list;
+ goto err_cleanup_gdev_srcu;
ret = gpiochip_init_valid_mask(gc);
if (ret)
- goto err_remove_from_list;
-
- ret = of_gpiochip_add(gc);
- if (ret)
- goto err_free_gpiochip_mask;
+ goto err_cleanup_gdev_srcu;
for (i = 0; i < gc->ngpio; i++) {
struct gpio_desc *desc = &gdev->descs[i];
+ ret = init_srcu_struct(&desc->srcu);
+ if (ret) {
+ for (j = 0; j < i; j++)
+ cleanup_srcu_struct(&gdev->descs[j].srcu);
+ goto err_free_gpiochip_mask;
+ }
+
if (gc->get_direction && gpiochip_line_is_valid(gc, i)) {
assign_bit(FLAG_IS_OUT,
&desc->flags, !gc->get_direction(gc, i));
@@ -958,6 +1011,10 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data,
}
}
+ ret = of_gpiochip_add(gc);
+ if (ret)
+ goto err_cleanup_desc_srcu;
+
ret = gpiochip_add_pin_ranges(gc);
if (ret)
goto err_remove_of_chip;
@@ -1003,12 +1060,17 @@ err_free_hogs:
gpiochip_remove_pin_ranges(gc);
err_remove_of_chip:
of_gpiochip_remove(gc);
+err_cleanup_desc_srcu:
+ for (i = 0; i < gdev->ngpio; i++)
+ cleanup_srcu_struct(&gdev->descs[i].srcu);
err_free_gpiochip_mask:
gpiochip_free_valid_mask(gc);
+err_cleanup_gdev_srcu:
+ cleanup_srcu_struct(&gdev->srcu);
err_remove_from_list:
- spin_lock_irqsave(&gpio_lock, flags);
- list_del(&gdev->list);
- spin_unlock_irqrestore(&gpio_lock, flags);
+ scoped_guard(mutex, &gpio_devices_lock)
+ list_del_rcu(&gdev->list);
+ synchronize_srcu(&gpio_devices_srcu);
if (gdev->dev.release) {
/* release() has been registered by gpiochip_setup_dev() */
gpio_device_put(gdev);
@@ -1044,16 +1106,18 @@ EXPORT_SYMBOL_GPL(gpiochip_add_data_with_key);
void gpiochip_remove(struct gpio_chip *gc)
{
struct gpio_device *gdev = gc->gpiodev;
- unsigned long flags;
- unsigned int i;
-
- down_write(&gdev->sem);
/* FIXME: should the legacy sysfs handling be moved to gpio_device? */
gpiochip_sysfs_unregister(gdev);
gpiochip_free_hogs(gc);
+
+ scoped_guard(mutex, &gpio_devices_lock)
+ list_del_rcu(&gdev->list);
+ synchronize_srcu(&gpio_devices_srcu);
+
/* Numb the device, cancelling all outstanding operations */
- gdev->chip = NULL;
+ rcu_assign_pointer(gdev->chip, NULL);
+ synchronize_srcu(&gdev->srcu);
gpiochip_irqchip_remove(gc);
acpi_gpiochip_remove(gc);
of_gpiochip_remove(gc);
@@ -1065,20 +1129,6 @@ void gpiochip_remove(struct gpio_chip *gc)
*/
gpiochip_set_data(gc, NULL);
- spin_lock_irqsave(&gpio_lock, flags);
- for (i = 0; i < gdev->ngpio; i++) {
- if (test_bit(FLAG_REQUESTED, &gdev->descs[i].flags))
- break;
- }
- spin_unlock_irqrestore(&gpio_lock, flags);
-
- if (i != gdev->ngpio)
- dev_crit(&gdev->dev,
- "REMOVING GPIOCHIP WITH GPIOS STILL REQUESTED\n");
-
- scoped_guard(spinlock_irqsave, &gpio_lock)
- list_del(&gdev->list);
-
/*
* The gpiochip side puts its use of the device to rest here:
* if there are no userspace clients, the chardev and device will
@@ -1086,7 +1136,6 @@ void gpiochip_remove(struct gpio_chip *gc)
* gone.
*/
gcdev_unregister(gdev);
- up_write(&gdev->sem);
gpio_device_put(gdev);
}
EXPORT_SYMBOL_GPL(gpiochip_remove);
@@ -1112,11 +1161,12 @@ EXPORT_SYMBOL_GPL(gpiochip_remove);
* If the function returns non-NULL, the returned reference must be freed by
* the caller using gpio_device_put().
*/
-struct gpio_device *gpio_device_find(void *data,
+struct gpio_device *gpio_device_find(const void *data,
int (*match)(struct gpio_chip *gc,
- void *data))
+ const void *data))
{
struct gpio_device *gdev;
+ struct gpio_chip *gc;
/*
* Not yet but in the future the spinlock below will become a mutex.
@@ -1125,10 +1175,15 @@ struct gpio_device *gpio_device_find(void *data,
*/
might_sleep();
- guard(spinlock_irqsave)(&gpio_lock);
+ guard(srcu)(&gpio_devices_srcu);
+
+ list_for_each_entry_srcu(gdev, &gpio_devices, list,
+ srcu_read_lock_held(&gpio_devices_srcu)) {
+ guard(srcu)(&gdev->srcu);
- list_for_each_entry(gdev, &gpio_devices, list) {
- if (gdev->chip && match(gdev->chip, data))
+ gc = srcu_dereference(gdev->chip, &gdev->srcu);
+
+ if (gc && match(gc, data))
return gpio_device_get(gdev);
}
@@ -1136,7 +1191,7 @@ struct gpio_device *gpio_device_find(void *data,
}
EXPORT_SYMBOL_GPL(gpio_device_find);
-static int gpio_chip_match_by_label(struct gpio_chip *gc, void *label)
+static int gpio_chip_match_by_label(struct gpio_chip *gc, const void *label)
{
return gc->label && !strcmp(gc->label, label);
}
@@ -1156,7 +1211,7 @@ struct gpio_device *gpio_device_find_by_label(const char *label)
}
EXPORT_SYMBOL_GPL(gpio_device_find_by_label);
-static int gpio_chip_match_by_fwnode(struct gpio_chip *gc, void *fwnode)
+static int gpio_chip_match_by_fwnode(struct gpio_chip *gc, const void *fwnode)
{
return device_match_fwnode(&gc->gpiodev->dev, fwnode);
}
@@ -1254,8 +1309,8 @@ static void gpiochip_irqchip_free_valid_mask(struct gpio_chip *gc)
gpiochip_free_mask(&gc->irq.valid_mask);
}
-bool gpiochip_irqchip_irq_valid(const struct gpio_chip *gc,
- unsigned int offset)
+static bool gpiochip_irqchip_irq_valid(const struct gpio_chip *gc,
+ unsigned int offset)
{
if (!gpiochip_line_is_valid(gc, offset))
return false;
@@ -1264,7 +1319,6 @@ bool gpiochip_irqchip_irq_valid(const struct gpio_chip *gc,
return true;
return test_bit(offset, gc->irq.valid_mask);
}
-EXPORT_SYMBOL_GPL(gpiochip_irqchip_irq_valid);
#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY
@@ -1439,6 +1493,43 @@ static unsigned int gpiochip_child_offset_to_irq_noop(struct gpio_chip *gc,
return offset;
}
+/**
+ * gpiochip_irq_domain_activate() - Lock a GPIO to be used as an IRQ
+ * @domain: The IRQ domain used by this IRQ chip
+ * @data: Outermost irq_data associated with the IRQ
+ * @reserve: If set, only reserve an interrupt vector instead of assigning one
+ *
+ * This function is a wrapper that calls gpiochip_lock_as_irq() and is to be
+ * used as the activate function for the &struct irq_domain_ops. The host_data
+ * for the IRQ domain must be the &struct gpio_chip.
+ */
+static int gpiochip_irq_domain_activate(struct irq_domain *domain,
+ struct irq_data *data, bool reserve)
+{
+ struct gpio_chip *gc = domain->host_data;
+ unsigned int hwirq = irqd_to_hwirq(data);
+
+ return gpiochip_lock_as_irq(gc, hwirq);
+}
+
+/**
+ * gpiochip_irq_domain_deactivate() - Unlock a GPIO used as an IRQ
+ * @domain: The IRQ domain used by this IRQ chip
+ * @data: Outermost irq_data associated with the IRQ
+ *
+ * This function is a wrapper that will call gpiochip_unlock_as_irq() and is to
+ * be used as the deactivate function for the &struct irq_domain_ops. The
+ * host_data for the IRQ domain must be the &struct gpio_chip.
+ */
+static void gpiochip_irq_domain_deactivate(struct irq_domain *domain,
+ struct irq_data *data)
+{
+ struct gpio_chip *gc = domain->host_data;
+ unsigned int hwirq = irqd_to_hwirq(data);
+
+ return gpiochip_unlock_as_irq(gc, hwirq);
+}
+
static void gpiochip_hierarchy_setup_domain_ops(struct irq_domain_ops *ops)
{
ops->activate = gpiochip_irq_domain_activate;
@@ -1556,7 +1647,8 @@ static bool gpiochip_hierarchy_is_hierarchical(struct gpio_chip *gc)
* gpiochip by assigning the gpiochip as chip data, and using the irqchip
* stored inside the gpiochip.
*/
-int gpiochip_irq_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hwirq)
+static int gpiochip_irq_map(struct irq_domain *d, unsigned int irq,
+ irq_hw_number_t hwirq)
{
struct gpio_chip *gc = d->host_data;
int ret = 0;
@@ -1593,9 +1685,8 @@ int gpiochip_irq_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hwi
return 0;
}
-EXPORT_SYMBOL_GPL(gpiochip_irq_map);
-void gpiochip_irq_unmap(struct irq_domain *d, unsigned int irq)
+static void gpiochip_irq_unmap(struct irq_domain *d, unsigned int irq)
{
struct gpio_chip *gc = d->host_data;
@@ -1604,7 +1695,6 @@ void gpiochip_irq_unmap(struct irq_domain *d, unsigned int irq)
irq_set_chip_and_handler(irq, NULL, NULL);
irq_set_chip_data(irq, NULL);
}
-EXPORT_SYMBOL_GPL(gpiochip_irq_unmap);
static const struct irq_domain_ops gpiochip_domain_ops = {
.map = gpiochip_irq_map,
@@ -1626,50 +1716,6 @@ static struct irq_domain *gpiochip_simple_create_domain(struct gpio_chip *gc)
return domain;
}
-/*
- * TODO: move these activate/deactivate in under the hierarchicial
- * irqchip implementation as static once SPMI and SSBI (all external
- * users) are phased over.
- */
-/**
- * gpiochip_irq_domain_activate() - Lock a GPIO to be used as an IRQ
- * @domain: The IRQ domain used by this IRQ chip
- * @data: Outermost irq_data associated with the IRQ
- * @reserve: If set, only reserve an interrupt vector instead of assigning one
- *
- * This function is a wrapper that calls gpiochip_lock_as_irq() and is to be
- * used as the activate function for the &struct irq_domain_ops. The host_data
- * for the IRQ domain must be the &struct gpio_chip.
- */
-int gpiochip_irq_domain_activate(struct irq_domain *domain,
- struct irq_data *data, bool reserve)
-{
- struct gpio_chip *gc = domain->host_data;
- unsigned int hwirq = irqd_to_hwirq(data);
-
- return gpiochip_lock_as_irq(gc, hwirq);
-}
-EXPORT_SYMBOL_GPL(gpiochip_irq_domain_activate);
-
-/**
- * gpiochip_irq_domain_deactivate() - Unlock a GPIO used as an IRQ
- * @domain: The IRQ domain used by this IRQ chip
- * @data: Outermost irq_data associated with the IRQ
- *
- * This function is a wrapper that will call gpiochip_unlock_as_irq() and is to
- * be used as the deactivate function for the &struct irq_domain_ops. The
- * host_data for the IRQ domain must be the &struct gpio_chip.
- */
-void gpiochip_irq_domain_deactivate(struct irq_domain *domain,
- struct irq_data *data)
-{
- struct gpio_chip *gc = domain->host_data;
- unsigned int hwirq = irqd_to_hwirq(data);
-
- return gpiochip_unlock_as_irq(gc, hwirq);
-}
-EXPORT_SYMBOL_GPL(gpiochip_irq_domain_deactivate);
-
static int gpiochip_to_irq(struct gpio_chip *gc, unsigned int offset)
{
struct irq_domain *domain = gc->irq.domain;
@@ -2189,58 +2235,41 @@ EXPORT_SYMBOL_GPL(gpiochip_remove_pin_ranges);
*/
static int gpiod_request_commit(struct gpio_desc *desc, const char *label)
{
- struct gpio_chip *gc = desc->gdev->chip;
- unsigned long flags;
unsigned int offset;
int ret;
- if (label) {
- label = kstrdup_const(label, GFP_KERNEL);
- if (!label)
- return -ENOMEM;
- }
+ CLASS(gpio_chip_guard, guard)(desc);
+ if (!guard.gc)
+ return -ENODEV;
- spin_lock_irqsave(&gpio_lock, flags);
+ if (test_and_set_bit(FLAG_REQUESTED, &desc->flags))
+ return -EBUSY;
/* NOTE: gpio_request() can be called in early boot,
* before IRQs are enabled, for non-sleeping (SOC) GPIOs.
*/
- if (test_and_set_bit(FLAG_REQUESTED, &desc->flags) == 0) {
- desc_set_label(desc, label ? : "?");
- } else {
- ret = -EBUSY;
- goto out_free_unlock;
- }
-
- if (gc->request) {
- /* gc->request may sleep */
- spin_unlock_irqrestore(&gpio_lock, flags);
+ if (guard.gc->request) {
offset = gpio_chip_hwgpio(desc);
- if (gpiochip_line_is_valid(gc, offset))
- ret = gc->request(gc, offset);
+ if (gpiochip_line_is_valid(guard.gc, offset))
+ ret = guard.gc->request(guard.gc, offset);
else
ret = -EINVAL;
- spin_lock_irqsave(&gpio_lock, flags);
-
- if (ret) {
- desc_set_label(desc, NULL);
- clear_bit(FLAG_REQUESTED, &desc->flags);
- goto out_free_unlock;
- }
+ if (ret)
+ goto out_clear_bit;
}
- if (gc->get_direction) {
- /* gc->get_direction may sleep */
- spin_unlock_irqrestore(&gpio_lock, flags);
+
+ if (guard.gc->get_direction)
gpiod_get_direction(desc);
- spin_lock_irqsave(&gpio_lock, flags);
- }
- spin_unlock_irqrestore(&gpio_lock, flags);
+
+ ret = desc_set_label(desc, label ? : "?");
+ if (ret)
+ goto out_clear_bit;
+
return 0;
-out_free_unlock:
- spin_unlock_irqrestore(&gpio_lock, flags);
- kfree_const(label);
+out_clear_bit:
+ clear_bit(FLAG_REQUESTED, &desc->flags);
return ret;
}
@@ -2254,19 +2283,12 @@ static int validate_desc(const struct gpio_desc *desc, const char *func)
{
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;
- }
+
return 1;
}
@@ -2302,60 +2324,45 @@ int gpiod_request(struct gpio_desc *desc, const char *label)
return ret;
}
-static bool gpiod_free_commit(struct gpio_desc *desc)
+static void gpiod_free_commit(struct gpio_desc *desc)
{
- struct gpio_chip *gc;
unsigned long flags;
- bool ret = false;
might_sleep();
- spin_lock_irqsave(&gpio_lock, flags);
+ CLASS(gpio_chip_guard, guard)(desc);
- gc = desc->gdev->chip;
- if (gc && test_bit(FLAG_REQUESTED, &desc->flags)) {
- if (gc->free) {
- spin_unlock_irqrestore(&gpio_lock, flags);
- might_sleep_if(gc->can_sleep);
- gc->free(gc, gpio_chip_hwgpio(desc));
- spin_lock_irqsave(&gpio_lock, flags);
- }
- kfree_const(desc->label);
- desc_set_label(desc, NULL);
- clear_bit(FLAG_ACTIVE_LOW, &desc->flags);
- clear_bit(FLAG_REQUESTED, &desc->flags);
- clear_bit(FLAG_OPEN_DRAIN, &desc->flags);
- clear_bit(FLAG_OPEN_SOURCE, &desc->flags);
- clear_bit(FLAG_PULL_UP, &desc->flags);
- clear_bit(FLAG_PULL_DOWN, &desc->flags);
- clear_bit(FLAG_BIAS_DISABLE, &desc->flags);
- clear_bit(FLAG_EDGE_RISING, &desc->flags);
- clear_bit(FLAG_EDGE_FALLING, &desc->flags);
- clear_bit(FLAG_IS_HOGGED, &desc->flags);
+ flags = READ_ONCE(desc->flags);
+
+ if (guard.gc && test_bit(FLAG_REQUESTED, &flags)) {
+ if (guard.gc->free)
+ guard.gc->free(guard.gc, gpio_chip_hwgpio(desc));
+
+ clear_bit(FLAG_ACTIVE_LOW, &flags);
+ clear_bit(FLAG_REQUESTED, &flags);
+ clear_bit(FLAG_OPEN_DRAIN, &flags);
+ clear_bit(FLAG_OPEN_SOURCE, &flags);
+ clear_bit(FLAG_PULL_UP, &flags);
+ clear_bit(FLAG_PULL_DOWN, &flags);
+ clear_bit(FLAG_BIAS_DISABLE, &flags);
+ clear_bit(FLAG_EDGE_RISING, &flags);
+ clear_bit(FLAG_EDGE_FALLING, &flags);
+ clear_bit(FLAG_IS_HOGGED, &flags);
#ifdef CONFIG_OF_DYNAMIC
- desc->hog = NULL;
+ WRITE_ONCE(desc->hog, NULL);
#endif
- ret = true;
- }
-
- spin_unlock_irqrestore(&gpio_lock, flags);
- gpiod_line_state_notify(desc, GPIOLINE_CHANGED_RELEASED);
+ desc_set_label(desc, NULL);
+ WRITE_ONCE(desc->flags, flags);
- return ret;
+ gpiod_line_state_notify(desc, GPIOLINE_CHANGED_RELEASED);
+ }
}
void gpiod_free(struct gpio_desc *desc)
{
- /*
- * We must not use VALIDATE_DESC_VOID() as the underlying gdev->chip
- * may already be NULL but we still want to put the references.
- */
- if (!desc)
- return;
-
- if (!gpiod_free_commit(desc))
- WARN_ON(1);
+ VALIDATE_DESC_VOID(desc);
+ gpiod_free_commit(desc);
module_put(desc->gdev->owner);
gpio_device_put(desc->gdev);
}
@@ -2381,20 +2388,12 @@ char *gpiochip_dup_line_label(struct gpio_chip *gc, unsigned int offset)
if (IS_ERR(desc))
return NULL;
- guard(spinlock_irqsave)(&gpio_lock);
-
if (!test_bit(FLAG_REQUESTED, &desc->flags))
return NULL;
- /*
- * FIXME: Once we mark gpiod_direction_input/output() and
- * gpiod_get_direction() with might_sleep(), we'll be able to protect
- * the GPIO descriptors with mutex (while value setting operations will
- * become lockless).
- *
- * Until this happens, this allocation needs to be atomic.
- */
- label = kstrdup(desc->label, GFP_ATOMIC);
+ guard(srcu)(&desc->srcu);
+
+ label = kstrdup(gpiod_get_label(desc), GFP_KERNEL);
if (!label)
return ERR_PTR(-ENOMEM);
@@ -2489,11 +2488,14 @@ static int gpio_set_config_with_argument(struct gpio_desc *desc,
enum pin_config_param mode,
u32 argument)
{
- struct gpio_chip *gc = desc->gdev->chip;
unsigned long config;
+ CLASS(gpio_chip_guard, guard)(desc);
+ if (!guard.gc)
+ return -ENODEV;
+
config = pinconf_to_config_packed(mode, argument);
- return gpio_do_set_config(gc, gpio_chip_hwgpio(desc), config);
+ return gpio_do_set_config(guard.gc, gpio_chip_hwgpio(desc), config);
}
static int gpio_set_config_with_argument_optional(struct gpio_desc *desc,
@@ -2527,13 +2529,16 @@ static int gpio_set_config(struct gpio_desc *desc, enum pin_config_param mode)
static int gpio_set_bias(struct gpio_desc *desc)
{
enum pin_config_param bias;
+ unsigned long flags;
unsigned int arg;
- if (test_bit(FLAG_BIAS_DISABLE, &desc->flags))
+ flags = READ_ONCE(desc->flags);
+
+ if (test_bit(FLAG_BIAS_DISABLE, &flags))
bias = PIN_CONFIG_BIAS_DISABLE;
- else if (test_bit(FLAG_PULL_UP, &desc->flags))
+ else if (test_bit(FLAG_PULL_UP, &flags))
bias = PIN_CONFIG_BIAS_PULL_UP;
- else if (test_bit(FLAG_PULL_DOWN, &desc->flags))
+ else if (test_bit(FLAG_PULL_DOWN, &flags))
bias = PIN_CONFIG_BIAS_PULL_DOWN;
else
return 0;
@@ -2580,18 +2585,20 @@ int gpio_set_debounce_timeout(struct gpio_desc *desc, unsigned int debounce)
*/
int gpiod_direction_input(struct gpio_desc *desc)
{
- struct gpio_chip *gc;
int ret = 0;
VALIDATE_DESC(desc);
- gc = desc->gdev->chip;
+
+ CLASS(gpio_chip_guard, guard)(desc);
+ if (!guard.gc)
+ return -ENODEV;
/*
* It is legal to have no .get() and .direction_input() specified if
* the chip is output-only, but you can't specify .direction_input()
* and not support the .get() operation, that doesn't make sense.
*/
- if (!gc->get && gc->direction_input) {
+ if (!guard.gc->get && guard.gc->direction_input) {
gpiod_warn(desc,
"%s: missing get() but have direction_input()\n",
__func__);
@@ -2604,10 +2611,12 @@ int gpiod_direction_input(struct gpio_desc *desc)
* direction (if .get_direction() is supported) else we silently
* assume we are in input mode after this.
*/
- if (gc->direction_input) {
- ret = gc->direction_input(gc, gpio_chip_hwgpio(desc));
- } else if (gc->get_direction &&
- (gc->get_direction(gc, gpio_chip_hwgpio(desc)) != 1)) {
+ if (guard.gc->direction_input) {
+ ret = guard.gc->direction_input(guard.gc,
+ gpio_chip_hwgpio(desc));
+ } else if (guard.gc->get_direction &&
+ (guard.gc->get_direction(guard.gc,
+ gpio_chip_hwgpio(desc)) != 1)) {
gpiod_warn(desc,
"%s: missing direction_input() operation and line is output\n",
__func__);
@@ -2626,28 +2635,31 @@ EXPORT_SYMBOL_GPL(gpiod_direction_input);
static int gpiod_direction_output_raw_commit(struct gpio_desc *desc, int value)
{
- struct gpio_chip *gc = desc->gdev->chip;
- int val = !!value;
- int ret = 0;
+ int val = !!value, ret = 0;
+
+ CLASS(gpio_chip_guard, guard)(desc);
+ if (!guard.gc)
+ return -ENODEV;
/*
* It's OK not to specify .direction_output() if the gpiochip is
* output-only, but if there is then not even a .set() operation it
* is pretty tricky to drive the output line.
*/
- if (!gc->set && !gc->direction_output) {
+ if (!guard.gc->set && !guard.gc->direction_output) {
gpiod_warn(desc,
"%s: missing set() and direction_output() operations\n",
__func__);
return -EIO;
}
- if (gc->direction_output) {
- ret = gc->direction_output(gc, gpio_chip_hwgpio(desc), val);
+ if (guard.gc->direction_output) {
+ ret = guard.gc->direction_output(guard.gc,
+ gpio_chip_hwgpio(desc), val);
} else {
/* Check that we are in output mode if we can */
- if (gc->get_direction &&
- gc->get_direction(gc, gpio_chip_hwgpio(desc))) {
+ if (guard.gc->get_direction &&
+ guard.gc->get_direction(guard.gc, gpio_chip_hwgpio(desc))) {
gpiod_warn(desc,
"%s: missing direction_output() operation\n",
__func__);
@@ -2657,7 +2669,7 @@ static int gpiod_direction_output_raw_commit(struct gpio_desc *desc, int value)
* If we can't actively set the direction, we are some
* output-only chip, so just drive the output as desired.
*/
- gc->set(gc, gpio_chip_hwgpio(desc), val);
+ guard.gc->set(guard.gc, gpio_chip_hwgpio(desc), val);
}
if (!ret)
@@ -2699,24 +2711,28 @@ EXPORT_SYMBOL_GPL(gpiod_direction_output_raw);
*/
int gpiod_direction_output(struct gpio_desc *desc, int value)
{
+ unsigned long flags;
int ret;
VALIDATE_DESC(desc);
- if (test_bit(FLAG_ACTIVE_LOW, &desc->flags))
+
+ flags = READ_ONCE(desc->flags);
+
+ if (test_bit(FLAG_ACTIVE_LOW, &flags))
value = !value;
else
value = !!value;
/* GPIOs used for enabled IRQs shall not be set as output */
- if (test_bit(FLAG_USED_AS_IRQ, &desc->flags) &&
- test_bit(FLAG_IRQ_IS_ENABLED, &desc->flags)) {
+ if (test_bit(FLAG_USED_AS_IRQ, &flags) &&
+ test_bit(FLAG_IRQ_IS_ENABLED, &flags)) {
gpiod_err(desc,
"%s: tried to set a GPIO tied to an IRQ as output\n",
__func__);
return -EIO;
}
- if (test_bit(FLAG_OPEN_DRAIN, &desc->flags)) {
+ if (test_bit(FLAG_OPEN_DRAIN, &flags)) {
/* First see if we can enable open drain in hardware */
ret = gpio_set_config(desc, PIN_CONFIG_DRIVE_OPEN_DRAIN);
if (!ret)
@@ -2726,7 +2742,7 @@ int gpiod_direction_output(struct gpio_desc *desc, int value)
ret = gpiod_direction_input(desc);
goto set_output_flag;
}
- } else if (test_bit(FLAG_OPEN_SOURCE, &desc->flags)) {
+ } else if (test_bit(FLAG_OPEN_SOURCE, &flags)) {
ret = gpio_set_config(desc, PIN_CONFIG_DRIVE_OPEN_SOURCE);
if (!ret)
goto set_output_value;
@@ -2769,17 +2785,20 @@ EXPORT_SYMBOL_GPL(gpiod_direction_output);
int gpiod_enable_hw_timestamp_ns(struct gpio_desc *desc, unsigned long flags)
{
int ret = 0;
- struct gpio_chip *gc;
VALIDATE_DESC(desc);
- gc = desc->gdev->chip;
- if (!gc->en_hw_timestamp) {
+ CLASS(gpio_chip_guard, guard)(desc);
+ if (!guard.gc)
+ return -ENODEV;
+
+ if (!guard.gc->en_hw_timestamp) {
gpiod_warn(desc, "%s: hw ts not supported\n", __func__);
return -ENOTSUPP;
}
- ret = gc->en_hw_timestamp(gc, gpio_chip_hwgpio(desc), flags);
+ ret = guard.gc->en_hw_timestamp(guard.gc,
+ gpio_chip_hwgpio(desc), flags);
if (ret)
gpiod_warn(desc, "%s: hw ts request failed\n", __func__);
@@ -2798,17 +2817,20 @@ EXPORT_SYMBOL_GPL(gpiod_enable_hw_timestamp_ns);
int gpiod_disable_hw_timestamp_ns(struct gpio_desc *desc, unsigned long flags)
{
int ret = 0;
- struct gpio_chip *gc;
VALIDATE_DESC(desc);
- gc = desc->gdev->chip;
- if (!gc->dis_hw_timestamp) {
+ CLASS(gpio_chip_guard, guard)(desc);
+ if (!guard.gc)
+ return -ENODEV;
+
+ if (!guard.gc->dis_hw_timestamp) {
gpiod_warn(desc, "%s: hw ts not supported\n", __func__);
return -ENOTSUPP;
}
- ret = gc->dis_hw_timestamp(gc, gpio_chip_hwgpio(desc), flags);
+ ret = guard.gc->dis_hw_timestamp(guard.gc, gpio_chip_hwgpio(desc),
+ flags);
if (ret)
gpiod_warn(desc, "%s: hw ts release failed\n", __func__);
@@ -2827,12 +2849,13 @@ EXPORT_SYMBOL_GPL(gpiod_disable_hw_timestamp_ns);
*/
int gpiod_set_config(struct gpio_desc *desc, unsigned long config)
{
- struct gpio_chip *gc;
-
VALIDATE_DESC(desc);
- gc = desc->gdev->chip;
- return gpio_do_set_config(gc, gpio_chip_hwgpio(desc), config);
+ CLASS(gpio_chip_guard, guard)(desc);
+ if (!guard.gc)
+ return -ENODEV;
+
+ return gpio_do_set_config(guard.gc, gpio_chip_hwgpio(desc), config);
}
EXPORT_SYMBOL_GPL(gpiod_set_config);
@@ -2930,10 +2953,19 @@ static int gpio_chip_get_value(struct gpio_chip *gc, const struct gpio_desc *des
static int gpiod_get_raw_value_commit(const struct gpio_desc *desc)
{
+ struct gpio_device *gdev;
struct gpio_chip *gc;
int value;
- gc = desc->gdev->chip;
+ /* FIXME Unable to use gpio_chip_guard due to const desc. */
+ gdev = desc->gdev;
+
+ guard(srcu)(&gdev->srcu);
+
+ gc = srcu_dereference(gdev->chip, &gdev->srcu);
+ if (!gc)
+ return -ENODEV;
+
value = gpio_chip_get_value(gc, desc);
value = value < 0 ? value : !!value;
trace_gpio_value(desc_to_gpio(desc), 1, value);
@@ -2959,6 +2991,14 @@ static int gpio_chip_get_multiple(struct gpio_chip *gc,
return -EIO;
}
+/* The 'other' chip must be protected with its GPIO device's SRCU. */
+static bool gpio_device_chip_cmp(struct gpio_device *gdev, struct gpio_chip *gc)
+{
+ guard(srcu)(&gdev->srcu);
+
+ return gc == srcu_dereference(gdev->chip, &gdev->srcu);
+}
+
int gpiod_get_array_value_complex(bool raw, bool can_sleep,
unsigned int array_size,
struct gpio_desc **desc_array,
@@ -2996,33 +3036,36 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep,
}
while (i < array_size) {
- struct gpio_chip *gc = desc_array[i]->gdev->chip;
DECLARE_BITMAP(fastpath_mask, FASTPATH_NGPIO);
DECLARE_BITMAP(fastpath_bits, FASTPATH_NGPIO);
unsigned long *mask, *bits;
int first, j;
- if (likely(gc->ngpio <= FASTPATH_NGPIO)) {
+ CLASS(gpio_chip_guard, guard)(desc_array[i]);
+ if (!guard.gc)
+ return -ENODEV;
+
+ if (likely(guard.gc->ngpio <= FASTPATH_NGPIO)) {
mask = fastpath_mask;
bits = fastpath_bits;
} else {
gfp_t flags = can_sleep ? GFP_KERNEL : GFP_ATOMIC;
- mask = bitmap_alloc(gc->ngpio, flags);
+ mask = bitmap_alloc(guard.gc->ngpio, flags);
if (!mask)
return -ENOMEM;
- bits = bitmap_alloc(gc->ngpio, flags);
+ bits = bitmap_alloc(guard.gc->ngpio, flags);
if (!bits) {
bitmap_free(mask);
return -ENOMEM;
}
}
- bitmap_zero(mask, gc->ngpio);
+ bitmap_zero(mask, guard.gc->ngpio);
if (!can_sleep)
- WARN_ON(gc->can_sleep);
+ WARN_ON(guard.gc->can_sleep);
/* collect all inputs belonging to the same chip */
first = i;
@@ -3037,9 +3080,9 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep,
i = find_next_zero_bit(array_info->get_mask,
array_size, i);
} while ((i < array_size) &&
- (desc_array[i]->gdev->chip == gc));
+ gpio_device_chip_cmp(desc_array[i]->gdev, guard.gc));
- ret = gpio_chip_get_multiple(gc, mask, bits);
+ ret = gpio_chip_get_multiple(guard.gc, mask, bits);
if (ret) {
if (mask != fastpath_mask)
bitmap_free(mask);
@@ -3086,7 +3129,7 @@ int gpiod_get_raw_value(const struct gpio_desc *desc)
{
VALIDATE_DESC(desc);
/* Should be using gpiod_get_raw_value_cansleep() */
- WARN_ON(desc->gdev->chip->can_sleep);
+ WARN_ON(desc->gdev->can_sleep);
return gpiod_get_raw_value_commit(desc);
}
EXPORT_SYMBOL_GPL(gpiod_get_raw_value);
@@ -3107,7 +3150,7 @@ int gpiod_get_value(const struct gpio_desc *desc)
VALIDATE_DESC(desc);
/* Should be using gpiod_get_value_cansleep() */
- WARN_ON(desc->gdev->chip->can_sleep);
+ WARN_ON(desc->gdev->can_sleep);
value = gpiod_get_raw_value_commit(desc);
if (value < 0)
@@ -3180,14 +3223,16 @@ EXPORT_SYMBOL_GPL(gpiod_get_array_value);
*/
static void gpio_set_open_drain_value_commit(struct gpio_desc *desc, bool value)
{
- int ret = 0;
- struct gpio_chip *gc = desc->gdev->chip;
- int offset = gpio_chip_hwgpio(desc);
+ int ret = 0, offset = gpio_chip_hwgpio(desc);
+
+ CLASS(gpio_chip_guard, guard)(desc);
+ if (!guard.gc)
+ return;
if (value) {
- ret = gc->direction_input(gc, offset);
+ ret = guard.gc->direction_input(guard.gc, offset);
} else {
- ret = gc->direction_output(gc, offset, 0);
+ ret = guard.gc->direction_output(guard.gc, offset, 0);
if (!ret)
set_bit(FLAG_IS_OUT, &desc->flags);
}
@@ -3205,16 +3250,18 @@ static void gpio_set_open_drain_value_commit(struct gpio_desc *desc, bool value)
*/
static void gpio_set_open_source_value_commit(struct gpio_desc *desc, bool value)
{
- int ret = 0;
- struct gpio_chip *gc = desc->gdev->chip;
- int offset = gpio_chip_hwgpio(desc);
+ int ret = 0, offset = gpio_chip_hwgpio(desc);
+
+ CLASS(gpio_chip_guard, guard)(desc);
+ if (!guard.gc)
+ return;
if (value) {
- ret = gc->direction_output(gc, offset, 1);
+ ret = guard.gc->direction_output(guard.gc, offset, 1);
if (!ret)
set_bit(FLAG_IS_OUT, &desc->flags);
} else {
- ret = gc->direction_input(gc, offset);
+ ret = guard.gc->direction_input(guard.gc, offset);
}
trace_gpio_direction(desc_to_gpio(desc), !value, ret);
if (ret < 0)
@@ -3225,11 +3272,12 @@ static void gpio_set_open_source_value_commit(struct gpio_desc *desc, bool value
static void gpiod_set_raw_value_commit(struct gpio_desc *desc, bool value)
{
- struct gpio_chip *gc;
+ CLASS(gpio_chip_guard, guard)(desc);
+ if (!guard.gc)
+ return;
- gc = desc->gdev->chip;
trace_gpio_value(desc_to_gpio(desc), 0, value);
- gc->set(gc, gpio_chip_hwgpio(desc), value);
+ guard.gc->set(guard.gc, gpio_chip_hwgpio(desc), value);
}
/*
@@ -3290,33 +3338,36 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep,
}
while (i < array_size) {
- struct gpio_chip *gc = desc_array[i]->gdev->chip;
DECLARE_BITMAP(fastpath_mask, FASTPATH_NGPIO);
DECLARE_BITMAP(fastpath_bits, FASTPATH_NGPIO);
unsigned long *mask, *bits;
int count = 0;
- if (likely(gc->ngpio <= FASTPATH_NGPIO)) {
+ CLASS(gpio_chip_guard, guard)(desc_array[i]);
+ if (!guard.gc)
+ return -ENODEV;
+
+ if (likely(guard.gc->ngpio <= FASTPATH_NGPIO)) {
mask = fastpath_mask;
bits = fastpath_bits;
} else {
gfp_t flags = can_sleep ? GFP_KERNEL : GFP_ATOMIC;
- mask = bitmap_alloc(gc->ngpio, flags);
+ mask = bitmap_alloc(guard.gc->ngpio, flags);
if (!mask)
return -ENOMEM;
- bits = bitmap_alloc(gc->ngpio, flags);
+ bits = bitmap_alloc(guard.gc->ngpio, flags);
if (!bits) {
bitmap_free(mask);
return -ENOMEM;
}
}
- bitmap_zero(mask, gc->ngpio);
+ bitmap_zero(mask, guard.gc->ngpio);
if (!can_sleep)
- WARN_ON(gc->can_sleep);
+ WARN_ON(guard.gc->can_sleep);
do {
struct gpio_desc *desc = desc_array[i];
@@ -3352,10 +3403,10 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep,
i = find_next_zero_bit(array_info->set_mask,
array_size, i);
} while ((i < array_size) &&
- (desc_array[i]->gdev->chip == gc));
+ gpio_device_chip_cmp(desc_array[i]->gdev, guard.gc));
/* push collected bits to outputs */
if (count != 0)
- gpio_chip_set_multiple(gc, mask, bits);
+ gpio_chip_set_multiple(guard.gc, mask, bits);
if (mask != fastpath_mask)
bitmap_free(mask);
@@ -3380,7 +3431,7 @@ void gpiod_set_raw_value(struct gpio_desc *desc, int value)
{
VALIDATE_DESC_VOID(desc);
/* Should be using gpiod_set_raw_value_cansleep() */
- WARN_ON(desc->gdev->chip->can_sleep);
+ WARN_ON(desc->gdev->can_sleep);
gpiod_set_raw_value_commit(desc, value);
}
EXPORT_SYMBOL_GPL(gpiod_set_raw_value);
@@ -3421,7 +3472,7 @@ void gpiod_set_value(struct gpio_desc *desc, int value)
{
VALIDATE_DESC_VOID(desc);
/* Should be using gpiod_set_value_cansleep() */
- WARN_ON(desc->gdev->chip->can_sleep);
+ WARN_ON(desc->gdev->can_sleep);
gpiod_set_value_nocheck(desc, value);
}
EXPORT_SYMBOL_GPL(gpiod_set_value);
@@ -3485,7 +3536,7 @@ EXPORT_SYMBOL_GPL(gpiod_set_array_value);
int gpiod_cansleep(const struct gpio_desc *desc)
{
VALIDATE_DESC(desc);
- return desc->gdev->chip->can_sleep;
+ return desc->gdev->can_sleep;
}
EXPORT_SYMBOL_GPL(gpiod_cansleep);
@@ -3497,16 +3548,8 @@ EXPORT_SYMBOL_GPL(gpiod_cansleep);
int gpiod_set_consumer_name(struct gpio_desc *desc, const char *name)
{
VALIDATE_DESC(desc);
- if (name) {
- name = kstrdup_const(name, GFP_KERNEL);
- if (!name)
- return -ENOMEM;
- }
-
- kfree_const(desc->label);
- desc_set_label(desc, name);
- return 0;
+ return desc_set_label(desc, name);
}
EXPORT_SYMBOL_GPL(gpiod_set_consumer_name);
@@ -3519,6 +3562,7 @@ EXPORT_SYMBOL_GPL(gpiod_set_consumer_name);
*/
int gpiod_to_irq(const struct gpio_desc *desc)
{
+ struct gpio_device *gdev;
struct gpio_chip *gc;
int offset;
@@ -3527,10 +3571,16 @@ int gpiod_to_irq(const struct gpio_desc *desc)
* 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)
+ if (!desc || IS_ERR(desc))
return -EINVAL;
- gc = desc->gdev->chip;
+ gdev = desc->gdev;
+ /* FIXME Cannot use gpio_chip_guard due to const desc. */
+ guard(srcu)(&gdev->srcu);
+ gc = srcu_dereference(gdev->chip, &gdev->srcu);
+ if (!gc)
+ return -ENODEV;
+
offset = gpio_chip_hwgpio(desc);
if (gc->to_irq) {
int retirq = gc->to_irq(gc, offset);
@@ -3597,14 +3647,6 @@ int gpiochip_lock_as_irq(struct gpio_chip *gc, unsigned int offset)
set_bit(FLAG_USED_AS_IRQ, &desc->flags);
set_bit(FLAG_IRQ_IS_ENABLED, &desc->flags);
- /*
- * If the consumer has not set up a label (such as when the
- * IRQ is referenced from .to_irq()) we set up a label here
- * so it is clear this is used as an interrupt.
- */
- if (!desc->label)
- desc_set_label(desc, "interrupt");
-
return 0;
}
EXPORT_SYMBOL_GPL(gpiochip_lock_as_irq);
@@ -3627,10 +3669,6 @@ void gpiochip_unlock_as_irq(struct gpio_chip *gc, unsigned int offset)
clear_bit(FLAG_USED_AS_IRQ, &desc->flags);
clear_bit(FLAG_IRQ_IS_ENABLED, &desc->flags);
-
- /* If we only had this marking, erase it */
- if (desc->label && !strcmp(desc->label, "interrupt"))
- desc_set_label(desc, NULL);
}
EXPORT_SYMBOL_GPL(gpiochip_unlock_as_irq);
@@ -4138,39 +4176,48 @@ static struct gpio_desc *gpiod_find_by_fwnode(struct fwnode_handle *fwnode,
return desc;
}
-static struct gpio_desc *gpiod_find_and_request(struct device *consumer,
- struct fwnode_handle *fwnode,
- const char *con_id,
- unsigned int idx,
- enum gpiod_flags flags,
- const char *label,
- bool platform_lookup_allowed)
+struct gpio_desc *gpiod_find_and_request(struct device *consumer,
+ struct fwnode_handle *fwnode,
+ const char *con_id,
+ unsigned int idx,
+ enum gpiod_flags flags,
+ const char *label,
+ bool platform_lookup_allowed)
{
unsigned long lookupflags = GPIO_LOOKUP_FLAGS_DEFAULT;
- struct gpio_desc *desc;
- int ret;
+ /*
+ * scoped_guard() is implemented as a for loop, meaning static
+ * analyzers will complain about these two not being initialized.
+ */
+ struct gpio_desc *desc = NULL;
+ int ret = 0;
+
+ scoped_guard(srcu, &gpio_devices_srcu) {
+ desc = gpiod_find_by_fwnode(fwnode, consumer, con_id, idx,
+ &flags, &lookupflags);
+ if (gpiod_not_found(desc) && platform_lookup_allowed) {
+ /*
+ * Either we are not using DT or ACPI, or their lookup
+ * did not return a result. In that case, use platform
+ * lookup as a fallback.
+ */
+ dev_dbg(consumer,
+ "using lookup tables for GPIO lookup\n");
+ desc = gpiod_find(consumer, con_id, idx, &lookupflags);
+ }
+
+ if (IS_ERR(desc)) {
+ dev_dbg(consumer, "No GPIO consumer %s found\n",
+ con_id);
+ return desc;
+ }
- desc = gpiod_find_by_fwnode(fwnode, consumer, con_id, idx, &flags, &lookupflags);
- if (gpiod_not_found(desc) && platform_lookup_allowed) {
/*
- * Either we are not using DT or ACPI, or their lookup did not
- * return a result. In that case, use platform lookup as a
- * fallback.
+ * If a connection label was passed use that, else attempt to use
+ * the device name as label
*/
- dev_dbg(consumer, "using lookup tables for GPIO lookup\n");
- desc = gpiod_find(consumer, con_id, idx, &lookupflags);
+ ret = gpiod_request(desc, label);
}
-
- if (IS_ERR(desc)) {
- dev_dbg(consumer, "No GPIO consumer %s found\n", con_id);
- return desc;
- }
-
- /*
- * If a connection label was passed use that, else attempt to use
- * the device name as label
- */
- ret = gpiod_request(desc, label);
if (ret) {
if (!(ret == -EBUSY && flags & GPIOD_FLAGS_BIT_NONEXCLUSIVE))
return ERR_PTR(ret);
@@ -4243,9 +4290,9 @@ int gpiod_count(struct device *dev, const char *con_id)
int count = -ENOENT;
if (is_of_node(fwnode))
- count = of_gpio_get_count(dev, con_id);
+ count = of_gpio_count(fwnode, con_id);
else if (is_acpi_node(fwnode))
- count = acpi_gpio_count(dev, con_id);
+ count = acpi_gpio_count(fwnode, con_id);
else if (is_software_node(fwnode))
count = swnode_gpio_count(fwnode, con_id);
@@ -4429,26 +4476,30 @@ EXPORT_SYMBOL_GPL(gpiod_get_index_optional);
int gpiod_hog(struct gpio_desc *desc, const char *name,
unsigned long lflags, enum gpiod_flags dflags)
{
- struct gpio_chip *gc;
+ struct gpio_device *gdev = desc->gdev;
struct gpio_desc *local_desc;
int hwnum;
int ret;
- gc = gpiod_to_chip(desc);
+ CLASS(gpio_chip_guard, guard)(desc);
+ if (!guard.gc)
+ return -ENODEV;
+
+ if (test_and_set_bit(FLAG_IS_HOGGED, &desc->flags))
+ return 0;
+
hwnum = gpio_chip_hwgpio(desc);
- local_desc = gpiochip_request_own_desc(gc, hwnum, name,
+ local_desc = gpiochip_request_own_desc(guard.gc, hwnum, name,
lflags, dflags);
if (IS_ERR(local_desc)) {
+ clear_bit(FLAG_IS_HOGGED, &desc->flags);
ret = PTR_ERR(local_desc);
pr_err("requesting hog GPIO %s (chip %s, offset %d) failed, %d\n",
- name, gc->label, hwnum, ret);
+ name, gdev->label, hwnum, ret);
return ret;
}
- /* Mark GPIO as hogged so it can be identified and removed later */
- set_bit(FLAG_IS_HOGGED, &desc->flags);
-
gpiod_dbg(desc, "hogged as %s%s\n",
(dflags & GPIOD_FLAGS_BIT_DIR_OUT) ? "output" : "input",
(dflags & GPIOD_FLAGS_BIT_DIR_OUT) ?
@@ -4712,13 +4763,22 @@ core_initcall(gpiolib_dev_init);
static void gpiolib_dbg_show(struct seq_file *s, struct gpio_device *gdev)
{
- struct gpio_chip *gc = gdev->chip;
bool active_low, is_irq, is_out;
unsigned int gpio = gdev->base;
struct gpio_desc *desc;
+ struct gpio_chip *gc;
int value;
+ guard(srcu)(&gdev->srcu);
+
+ gc = srcu_dereference(gdev->chip, &gdev->srcu);
+ if (!gc) {
+ seq_puts(s, "Underlying GPIO chip is gone\n");
+ return;
+ }
+
for_each_gpio_desc(gc, desc) {
+ guard(srcu)(&desc->srcu);
if (test_bit(FLAG_REQUESTED, &desc->flags)) {
gpiod_get_direction(desc);
is_out = test_bit(FLAG_IS_OUT, &desc->flags);
@@ -4726,7 +4786,7 @@ static void gpiolib_dbg_show(struct seq_file *s, struct gpio_device *gdev)
is_irq = test_bit(FLAG_USED_AS_IRQ, &desc->flags);
active_low = test_bit(FLAG_ACTIVE_LOW, &desc->flags);
seq_printf(s, " gpio-%-3d (%-20.20s|%-20.20s) %s %s %s%s\n",
- gpio, desc->name ?: "", desc->label,
+ gpio, desc->name ?: "", gpiod_get_label(desc),
is_out ? "out" : "in ",
value >= 0 ? (value ? "hi" : "lo") : "? ",
is_irq ? "IRQ " : "",
@@ -4739,61 +4799,72 @@ static void gpiolib_dbg_show(struct seq_file *s, struct gpio_device *gdev)
}
}
+struct gpiolib_seq_priv {
+ bool newline;
+ int idx;
+};
+
static void *gpiolib_seq_start(struct seq_file *s, loff_t *pos)
{
- unsigned long flags;
- struct gpio_device *gdev = NULL;
+ struct gpiolib_seq_priv *priv;
+ struct gpio_device *gdev;
loff_t index = *pos;
- s->private = "";
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return NULL;
+
+ s->private = priv;
+ priv->idx = srcu_read_lock(&gpio_devices_srcu);
- spin_lock_irqsave(&gpio_lock, flags);
- list_for_each_entry(gdev, &gpio_devices, list)
- if (index-- == 0) {
- spin_unlock_irqrestore(&gpio_lock, flags);
+ list_for_each_entry_srcu(gdev, &gpio_devices, list,
+ srcu_read_lock_held(&gpio_devices_srcu)) {
+ if (index-- == 0)
return gdev;
- }
- spin_unlock_irqrestore(&gpio_lock, flags);
+ }
return NULL;
}
static void *gpiolib_seq_next(struct seq_file *s, void *v, loff_t *pos)
{
- unsigned long flags;
- struct gpio_device *gdev = v;
- void *ret = NULL;
-
- spin_lock_irqsave(&gpio_lock, flags);
- if (list_is_last(&gdev->list, &gpio_devices))
- ret = NULL;
- else
- ret = list_first_entry(&gdev->list, struct gpio_device, list);
- spin_unlock_irqrestore(&gpio_lock, flags);
+ struct gpiolib_seq_priv *priv = s->private;
+ struct gpio_device *gdev = v, *next;
- s->private = "\n";
+ next = list_entry_rcu(gdev->list.next, struct gpio_device, list);
+ gdev = &next->list == &gpio_devices ? NULL : next;
+ priv->newline = true;
++*pos;
- return ret;
+ return gdev;
}
static void gpiolib_seq_stop(struct seq_file *s, void *v)
{
+ struct gpiolib_seq_priv *priv = s->private;
+
+ srcu_read_unlock(&gpio_devices_srcu, priv->idx);
+ kfree(priv);
}
static int gpiolib_seq_show(struct seq_file *s, void *v)
{
+ struct gpiolib_seq_priv *priv = s->private;
struct gpio_device *gdev = v;
- struct gpio_chip *gc = gdev->chip;
+ struct gpio_chip *gc;
struct device *parent;
+ guard(srcu)(&gdev->srcu);
+
+ gc = srcu_dereference(gdev->chip, &gdev->srcu);
if (!gc) {
- seq_printf(s, "%s%s: (dangling chip)", (char *)s->private,
+ seq_printf(s, "%s%s: (dangling chip)",
+ priv->newline ? "\n" : "",
dev_name(&gdev->dev));
return 0;
}
- seq_printf(s, "%s%s: GPIOs %d-%d", (char *)s->private,
+ seq_printf(s, "%s%s: GPIOs %d-%d", priv->newline ? "\n" : "",
dev_name(&gdev->dev),
gdev->base, gdev->base + gdev->ngpio - 1);
parent = gc->parent;
diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h
index a4a2520b5f31..f67d5991ab1c 100644
--- a/drivers/gpio/gpiolib.h
+++ b/drivers/gpio/gpiolib.h
@@ -16,7 +16,7 @@
#include <linux/gpio/driver.h>
#include <linux/module.h>
#include <linux/notifier.h>
-#include <linux/rwsem.h>
+#include <linux/srcu.h>
#define GPIOCHIP_NAME "gpiochip"
@@ -33,6 +33,8 @@
* @descs: array of ngpio descriptors.
* @ngpio: the number of GPIO lines on this GPIO device, equal to the size
* of the @descs array.
+ * @can_sleep: indicate whether the GPIO chip driver's callbacks can sleep
+ * implying that they cannot be used from atomic context
* @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
@@ -43,9 +45,7 @@
* requested, released or reconfigured
* @device_notifier: used to notify character device wait queues about the GPIO
* device being unregistered
- * @sem: protects the structure from a NULL-pointer dereference of @chip by
- * user-space operations when the device gets unregistered during
- * a hot-unplug event
+ * @srcu: protects the pointer to the underlying GPIO chip
* @pin_ranges: range of pins served by the GPIO driver
*
* This state container holds most of the runtime variable data
@@ -59,16 +59,17 @@ struct gpio_device {
int id;
struct device *mockdev;
struct module *owner;
- struct gpio_chip *chip;
+ struct gpio_chip __rcu *chip;
struct gpio_desc *descs;
int base;
u16 ngpio;
+ bool can_sleep;
const char *label;
void *data;
struct list_head list;
struct blocking_notifier_head line_state_notifier;
struct blocking_notifier_head device_notifier;
- struct rw_semaphore sem;
+ struct srcu_struct srcu;
#ifdef CONFIG_PINCTRL
/*
@@ -134,9 +135,6 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep,
int gpiod_set_transitory(struct gpio_desc *desc, bool transitory);
-extern spinlock_t gpio_lock;
-extern struct list_head gpio_devices;
-
void gpiod_line_state_notify(struct gpio_desc *desc, unsigned long action);
/**
@@ -147,6 +145,7 @@ void gpiod_line_state_notify(struct gpio_desc *desc, unsigned long action);
* @label: Name of the consumer
* @name: Line name
* @hog: Pointer to the device node that hogs this line (if any)
+ * @srcu: SRCU struct protecting the label pointer.
*
* These are obtained using gpiod_get() and are preferable to the old
* integer-based handles.
@@ -178,16 +177,38 @@ struct gpio_desc {
#define FLAG_EVENT_CLOCK_HTE 19 /* GPIO CDEV reports hardware timestamps in events */
/* Connection label */
- const char *label;
+ const char __rcu *label;
/* Name of the GPIO */
const char *name;
#ifdef CONFIG_OF_DYNAMIC
struct device_node *hog;
#endif
+ struct srcu_struct srcu;
};
#define gpiod_not_found(desc) (IS_ERR(desc) && PTR_ERR(desc) == -ENOENT)
+struct gpio_chip_guard {
+ struct gpio_device *gdev;
+ struct gpio_chip *gc;
+ int idx;
+};
+
+DEFINE_CLASS(gpio_chip_guard,
+ struct gpio_chip_guard,
+ srcu_read_unlock(&_T.gdev->srcu, _T.idx),
+ ({
+ struct gpio_chip_guard _guard;
+
+ _guard.gdev = desc->gdev;
+ _guard.idx = srcu_read_lock(&_guard.gdev->srcu);
+ _guard.gc = srcu_dereference(_guard.gdev->chip,
+ &_guard.gdev->srcu);
+
+ _guard;
+ }),
+ struct gpio_desc *desc)
+
int gpiod_request(struct gpio_desc *desc, const char *label);
void gpiod_free(struct gpio_desc *desc);
@@ -202,12 +223,21 @@ static inline int gpiod_request_user(struct gpio_desc *desc, const char *label)
return ret;
}
+struct gpio_desc *gpiod_find_and_request(struct device *consumer,
+ struct fwnode_handle *fwnode,
+ const char *con_id,
+ unsigned int idx,
+ enum gpiod_flags flags,
+ const char *label,
+ bool platform_lookup_allowed);
+
int gpiod_configure_flags(struct gpio_desc *desc, const char *con_id,
unsigned long lflags, enum gpiod_flags dflags);
int gpio_set_debounce_timeout(struct gpio_desc *desc, unsigned int debounce);
int gpiod_hog(struct gpio_desc *desc, const char *name,
unsigned long lflags, enum gpiod_flags dflags);
int gpiochip_get_ngpios(struct gpio_chip *gc, struct device *dev);
+const char *gpiod_get_label(struct gpio_desc *desc);
/*
* Return the GPIO number of the passed descriptor relative to its chip
@@ -219,31 +249,32 @@ static inline int gpio_chip_hwgpio(const struct gpio_desc *desc)
/* With descriptor prefix */
-#define gpiod_emerg(desc, fmt, ...) \
- pr_emerg("gpio-%d (%s): " fmt, desc_to_gpio(desc), desc->label ? : "?",\
- ##__VA_ARGS__)
-#define gpiod_crit(desc, fmt, ...) \
- pr_crit("gpio-%d (%s): " fmt, desc_to_gpio(desc), desc->label ? : "?", \
- ##__VA_ARGS__)
-#define gpiod_err(desc, fmt, ...) \
- pr_err("gpio-%d (%s): " fmt, desc_to_gpio(desc), desc->label ? : "?", \
- ##__VA_ARGS__)
-#define gpiod_warn(desc, fmt, ...) \
- pr_warn("gpio-%d (%s): " fmt, desc_to_gpio(desc), desc->label ? : "?", \
- ##__VA_ARGS__)
-#define gpiod_info(desc, fmt, ...) \
- pr_info("gpio-%d (%s): " fmt, desc_to_gpio(desc), desc->label ? : "?", \
- ##__VA_ARGS__)
-#define gpiod_dbg(desc, fmt, ...) \
- pr_debug("gpio-%d (%s): " fmt, desc_to_gpio(desc), desc->label ? : "?",\
- ##__VA_ARGS__)
+#define gpiod_err(desc, fmt, ...) \
+do { \
+ scoped_guard(srcu, &desc->srcu) { \
+ pr_err("gpio-%d (%s): " fmt, desc_to_gpio(desc), \
+ gpiod_get_label(desc) ? : "?", ##__VA_ARGS__); \
+ } \
+} while (0)
+
+#define gpiod_warn(desc, fmt, ...) \
+do { \
+ scoped_guard(srcu, &desc->srcu) { \
+ pr_warn("gpio-%d (%s): " fmt, desc_to_gpio(desc), \
+ gpiod_get_label(desc) ? : "?", ##__VA_ARGS__); \
+ } \
+} while (0)
+
+#define gpiod_dbg(desc, fmt, ...) \
+do { \
+ scoped_guard(srcu, &desc->srcu) { \
+ pr_debug("gpio-%d (%s): " fmt, desc_to_gpio(desc), \
+ gpiod_get_label(desc) ? : "?", ##__VA_ARGS__); \
+ } \
+} while (0)
/* With chip prefix */
-#define chip_emerg(gc, fmt, ...) \
- dev_emerg(&gc->gpiodev->dev, "(%s): " fmt, gc->label, ##__VA_ARGS__)
-#define chip_crit(gc, fmt, ...) \
- dev_crit(&gc->gpiodev->dev, "(%s): " fmt, gc->label, ##__VA_ARGS__)
#define chip_err(gc, fmt, ...) \
dev_err(&gc->gpiodev->dev, "(%s): " fmt, gc->label, ##__VA_ARGS__)
#define chip_warn(gc, fmt, ...) \