@@ -196,8 +196,6 @@ static long linehandle_set_config(struct linehandle_state *lh,
if (ret)
return ret;
}
-
- gpiod_line_state_notify(desc, GPIO_V2_LINE_CHANGED_CONFIG);
}
return 0;
}
@@ -363,11 +361,11 @@ static int linehandle_create(struct gpio_device *gdev, void __user *ip)
if (lflags & GPIOHANDLE_REQUEST_OUTPUT) {
int val = !!handlereq.default_values[i];
- ret = gpiod_direction_output(desc, val);
+ ret = gpiod_direction_output_nonotify(desc, val);
if (ret)
goto out_free_lh;
} else if (lflags & GPIOHANDLE_REQUEST_INPUT) {
- ret = gpiod_direction_input(desc);
+ ret = gpiod_direction_input_nonotify(desc);
if (ret)
goto out_free_lh;
}
@@ -1566,8 +1564,6 @@ static long linereq_set_config(struct linereq *lr, void __user *ip)
}
WRITE_ONCE(line->edflags, edflags);
-
- gpiod_line_state_notify(desc, GPIO_V2_LINE_CHANGED_CONFIG);
}
return 0;
}
@@ -1824,11 +1820,11 @@ static int linereq_create(struct gpio_device *gdev, void __user *ip)
if (flags & GPIO_V2_LINE_FLAG_OUTPUT) {
int val = gpio_v2_line_config_output_value(lc, i);
- ret = gpiod_direction_output(desc, val);
+ ret = gpiod_direction_output_nonotify(desc, val);
if (ret)
goto out_free_linereq;
} else if (flags & GPIO_V2_LINE_FLAG_INPUT) {
- ret = gpiod_direction_input(desc);
+ ret = gpiod_direction_input_nonotify(desc);
if (ret)
goto out_free_linereq;
@@ -870,6 +870,26 @@ static void gpiochip_set_data(struct gpio_chip *gc, void *data)
gc->gpiodev->data = data;
}
+static void gpiod_line_state_notify(struct gpio_desc *desc,
+ unsigned long action)
+{
+ blocking_notifier_call_chain(&desc->gdev->line_state_notifier,
+ action, desc);
+}
+
+static void gpiod_config_change_func(struct work_struct *work)
+{
+ struct gpio_desc *desc = container_of(work, struct gpio_desc,
+ line_state_work);
+
+ gpiod_line_state_notify(desc, GPIO_V2_LINE_CHANGED_CONFIG);
+}
+
+static void gpiod_line_config_change_notify(struct gpio_desc *desc)
+{
+ queue_work(system_unbound_wq, &desc->line_state_work);
+}
+
/**
* gpiochip_get_data() - get per-subdriver data for the chip
* @gc: GPIO chip
@@ -1064,6 +1084,8 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data,
assign_bit(FLAG_IS_OUT,
&desc->flags, !gc->direction_input);
}
+
+ INIT_WORK(&desc->line_state_work, gpiod_config_change_func);
}
ret = of_gpiochip_add(gc);
@@ -2673,6 +2695,18 @@ int gpio_set_debounce_timeout(struct gpio_desc *desc, unsigned int debounce)
* 0 on success, or negative errno on failure.
*/
int gpiod_direction_input(struct gpio_desc *desc)
+{
+ int ret;
+
+ ret = gpiod_direction_input_nonotify(desc);
+ if (ret == 0)
+ gpiod_line_config_change_notify(desc);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(gpiod_direction_input);
+
+int gpiod_direction_input_nonotify(struct gpio_desc *desc)
{
int ret = 0;
@@ -2720,7 +2754,6 @@ int gpiod_direction_input(struct gpio_desc *desc)
return ret;
}
-EXPORT_SYMBOL_GPL(gpiod_direction_input);
static int gpiod_direction_output_raw_commit(struct gpio_desc *desc, int value)
{
@@ -2782,8 +2815,15 @@ static int gpiod_direction_output_raw_commit(struct gpio_desc *desc, int value)
*/
int gpiod_direction_output_raw(struct gpio_desc *desc, int value)
{
+ int ret;
+
VALIDATE_DESC(desc);
- return gpiod_direction_output_raw_commit(desc, value);
+
+ ret = gpiod_direction_output_raw_commit(desc, value);
+ if (ret == 0)
+ gpiod_line_config_change_notify(desc);
+
+ return ret;
}
EXPORT_SYMBOL_GPL(gpiod_direction_output_raw);
@@ -2801,6 +2841,18 @@ EXPORT_SYMBOL_GPL(gpiod_direction_output_raw);
* 0 on success, or negative errno on failure.
*/
int gpiod_direction_output(struct gpio_desc *desc, int value)
+{
+ int ret;
+
+ ret = gpiod_direction_output_nonotify(desc, value);
+ if (ret == 0)
+ gpiod_line_config_change_notify(desc);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(gpiod_direction_output);
+
+int gpiod_direction_output_nonotify(struct gpio_desc *desc, int value)
{
unsigned long flags;
int ret;
@@ -2863,7 +2915,6 @@ int gpiod_direction_output(struct gpio_desc *desc, int value)
set_bit(FLAG_IS_OUT, &desc->flags);
return ret;
}
-EXPORT_SYMBOL_GPL(gpiod_direction_output);
/**
* gpiod_enable_hw_timestamp_ns - Enable hardware timestamp in nanoseconds.
@@ -2942,13 +2993,34 @@ EXPORT_SYMBOL_GPL(gpiod_disable_hw_timestamp_ns);
*/
int gpiod_set_config(struct gpio_desc *desc, unsigned long config)
{
+ int ret;
+
VALIDATE_DESC(desc);
CLASS(gpio_chip_guard, guard)(desc);
if (!guard.gc)
return -ENODEV;
- return gpio_do_set_config(guard.gc, gpio_chip_hwgpio(desc), config);
+ ret = gpio_do_set_config(guard.gc, gpio_chip_hwgpio(desc), config);
+ if (ret == 0) {
+ /* These are the only options we notify the userspace about. */
+ switch (pinconf_to_config_param(config)) {
+ case PIN_CONFIG_BIAS_DISABLE:
+ case PIN_CONFIG_BIAS_PULL_DOWN:
+ case PIN_CONFIG_BIAS_PULL_UP:
+ case PIN_CONFIG_DRIVE_OPEN_DRAIN:
+ case PIN_CONFIG_DRIVE_OPEN_SOURCE:
+ case PIN_CONFIG_DRIVE_PUSH_PULL:
+ case PIN_CONFIG_INPUT_DEBOUNCE:
+ gpiod_line_state_notify(desc,
+ GPIO_V2_LINE_CHANGED_CONFIG);
+ break;
+ default:
+ break;
+ }
+ }
+
+ return ret;
}
EXPORT_SYMBOL_GPL(gpiod_set_config);
@@ -3015,6 +3087,7 @@ void gpiod_toggle_active_low(struct gpio_desc *desc)
{
VALIDATE_DESC_VOID(desc);
change_bit(FLAG_ACTIVE_LOW, &desc->flags);
+ gpiod_line_state_notify(desc, GPIO_V2_LINE_CHANGED_CONFIG);
}
EXPORT_SYMBOL_GPL(gpiod_toggle_active_low);
@@ -3659,9 +3732,15 @@ EXPORT_SYMBOL_GPL(gpiod_cansleep);
*/
int gpiod_set_consumer_name(struct gpio_desc *desc, const char *name)
{
+ int ret;
+
VALIDATE_DESC(desc);
- return desc_set_label(desc, name);
+ ret = desc_set_label(desc, name);
+ if (ret == 0)
+ gpiod_line_state_notify(desc, GPIO_V2_LINE_CHANGED_CONFIG);
+
+ return ret;
}
EXPORT_SYMBOL_GPL(gpiod_set_consumer_name);
@@ -4087,12 +4166,6 @@ int gpiod_set_array_value_cansleep(unsigned int array_size,
}
EXPORT_SYMBOL_GPL(gpiod_set_array_value_cansleep);
-void gpiod_line_state_notify(struct gpio_desc *desc, unsigned long action)
-{
- blocking_notifier_call_chain(&desc->gdev->line_state_notifier,
- action, desc);
-}
-
/**
* gpiod_add_lookup_table() - register GPIO device consumers
* @table: table of consumers to register
@@ -4537,10 +4610,10 @@ int gpiod_configure_flags(struct gpio_desc *desc, const char *con_id,
/* Process flags */
if (dflags & GPIOD_FLAGS_BIT_DIR_OUT)
- ret = gpiod_direction_output(desc,
+ ret = gpiod_direction_output_nonotify(desc,
!!(dflags & GPIOD_FLAGS_BIT_DIR_VAL));
else
- ret = gpiod_direction_input(desc);
+ ret = gpiod_direction_input_nonotify(desc);
return ret;
}
@@ -17,6 +17,7 @@
#include <linux/module.h>
#include <linux/notifier.h>
#include <linux/srcu.h>
+#include <linux/workqueue.h>
#define GPIOCHIP_NAME "gpiochip"
@@ -137,6 +138,9 @@ struct gpio_array {
for_each_gpio_desc(gc, desc) \
if (!test_bit(flag, &desc->flags)) {} else
+int gpiod_direction_output_nonotify(struct gpio_desc *desc, int value);
+int gpiod_direction_input_nonotify(struct gpio_desc *desc);
+
int gpiod_get_array_value_complex(bool raw, bool can_sleep,
unsigned int array_size,
struct gpio_desc **desc_array,
@@ -150,8 +154,6 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep,
int gpiod_set_transitory(struct gpio_desc *desc, bool transitory);
-void gpiod_line_state_notify(struct gpio_desc *desc, unsigned long action);
-
struct gpio_desc_label {
struct rcu_head rh;
char str[];
@@ -165,6 +167,8 @@ struct gpio_desc_label {
* @label: Name of the consumer
* @name: Line name
* @hog: Pointer to the device node that hogs this line (if any)
+ * @line_state_work: Used to schedule line state updates to user-space from
+ * atomic context.
*
* These are obtained using gpiod_get() and are preferable to the old
* integer-based handles.
@@ -202,6 +206,7 @@ struct gpio_desc {
#ifdef CONFIG_OF_DYNAMIC
struct device_node *hog;
#endif
+ struct work_struct line_state_work;
};
#define gpiod_not_found(desc) (IS_ERR(desc) && PTR_ERR(desc) == -ENOENT)