diff options
Diffstat (limited to 'drivers/gpio/gpiolib.h')
| -rw-r--r-- | drivers/gpio/gpiolib.h | 217 |
1 files changed, 138 insertions, 79 deletions
diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h index d900ecdbac46..77f6f2936dc2 100644 --- a/drivers/gpio/gpiolib.h +++ b/drivers/gpio/gpiolib.h @@ -9,36 +9,49 @@ #ifndef GPIOLIB_H #define GPIOLIB_H -#include <linux/gpio/driver.h> -#include <linux/gpio/consumer.h> /* for enum gpiod_flags */ -#include <linux/err.h> +#include <linux/cdev.h> #include <linux/device.h> +#include <linux/err.h> +#include <linux/gpio/consumer.h> /* for enum gpiod_flags */ +#include <linux/gpio/driver.h> #include <linux/module.h> -#include <linux/cdev.h> +#include <linux/notifier.h> +#include <linux/spinlock.h> +#include <linux/srcu.h> +#include <linux/workqueue.h> #define GPIOCHIP_NAME "gpiochip" /** * struct gpio_device - internal state container for GPIO devices - * @id: numerical ID number for the GPIO chip * @dev: the GPIO device struct * @chrdev: character device for the GPIO device - * @mockdev: class device used by the deprecated sysfs interface (may be - * NULL) + * @id: numerical ID number for the GPIO chip * @owner: helps prevent removal of modules exporting active GPIOs * @chip: pointer to the corresponding gpiochip, holding static * data for this device * @descs: array of ngpio descriptors. + * @valid_mask: If not %NULL, holds bitmask of GPIOs which are valid to be + * used from the chip. + * @desc_srcu: ensures consistent state of GPIO descriptors exposed to users * @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 * or name of the IP component in a System on Chip. * @data: per-instance data assigned by the driver * @list: links gpio_device:s together for traversal - * @notifier: used to notify subscribers about lines being requested, released - * or reconfigured + * @line_state_notifier: used to notify subscribers about lines being + * requested, released or reconfigured + * @line_state_lock: RW-spinlock protecting the line state notifier + * @line_state_wq: used to emit line state events from a separate thread in + * process context + * @device_notifier: used to notify character device wait queues about the GPIO + * device being unregistered + * @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 @@ -47,19 +60,25 @@ * userspace. */ struct gpio_device { - int id; struct device dev; struct cdev chrdev; - struct device *mockdev; + int id; struct module *owner; - struct gpio_chip *chip; + struct gpio_chip __rcu *chip; struct gpio_desc *descs; - int base; + unsigned long *valid_mask; + struct srcu_struct desc_srcu; + unsigned int base; u16 ngpio; + bool can_sleep; const char *label; void *data; struct list_head list; - struct blocking_notifier_head notifier; + struct raw_notifier_head line_state_notifier; + rwlock_t line_state_lock; + struct workqueue_struct *line_state_wq; + struct blocking_notifier_head device_notifier; + struct srcu_struct srcu; #ifdef CONFIG_PINCTRL /* @@ -72,15 +91,33 @@ struct gpio_device { #endif }; -/* gpio suffixes used for ACPI and device tree lookup */ -static __maybe_unused const char * const gpio_suffixes[] = { "gpios", "gpio" }; +static inline struct gpio_device *to_gpio_device(struct device *dev) +{ + return container_of(dev, struct gpio_device, dev); +} + +/* GPIO suffixes used for ACPI and device tree lookup */ +extern const char *const gpio_suffixes[]; + +#define for_each_gpio_property_name(propname, con_id) \ + for (const char * const *__suffixes = gpio_suffixes; \ + *__suffixes && ({ \ + const char *__gs = *__suffixes; \ + \ + if (con_id) \ + snprintf(propname, sizeof(propname), "%s-%s", con_id, __gs); \ + else \ + snprintf(propname, sizeof(propname), "%s", __gs); \ + 1; \ + }); \ + __suffixes++) /** * struct gpio_array - Opaque descriptor for a structure of GPIO array attributes * * @desc: Array of pointers to the GPIO descriptors * @size: Number of elements in desc - * @chip: Parent GPIO chip + * @gdev: Parent GPIO device * @get_mask: Get mask used in fastpath * @set_mask: Set mask used in fastpath * @invert_mask: Invert mask used in fastpath @@ -92,14 +129,12 @@ static __maybe_unused const char * const gpio_suffixes[] = { "gpios", "gpio" }; struct gpio_array { struct gpio_desc **desc; unsigned int size; - struct gpio_chip *chip; + struct gpio_device *gdev; unsigned long *get_mask; unsigned long *set_mask; unsigned long invert_mask[]; }; -struct gpio_desc *gpiochip_get_desc(struct gpio_chip *gc, unsigned int hwnum); - #define for_each_gpio_desc(gc, desc) \ for (unsigned int __i = 0; \ __i < gc->ngpio && (desc = gpiochip_get_desc(gc, __i)); \ @@ -120,9 +155,16 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep, struct gpio_array *array_info, unsigned long *value_bitmap); -extern spinlock_t gpio_lock; -extern struct list_head gpio_devices; +int gpiod_set_transitory(struct gpio_desc *desc, bool transitory); + +void gpiod_line_state_notify(struct gpio_desc *desc, unsigned long action); +int gpiod_direction_output_nonotify(struct gpio_desc *desc, int value); +int gpiod_direction_input_nonotify(struct gpio_desc *desc); +struct gpio_desc_label { + struct rcu_head rh; + char str[]; +}; /** * struct gpio_desc - Opaque descriptor for a GPIO @@ -144,27 +186,29 @@ struct gpio_desc { struct gpio_device *gdev; unsigned long flags; /* flag symbols are bit numbers */ -#define FLAG_REQUESTED 0 -#define FLAG_IS_OUT 1 -#define FLAG_EXPORT 2 /* protected by sysfs_lock */ -#define FLAG_SYSFS 3 /* exported via /sys/class/gpio/control */ -#define FLAG_ACTIVE_LOW 6 /* value has active low */ -#define FLAG_OPEN_DRAIN 7 /* Gpio is open drain type */ -#define FLAG_OPEN_SOURCE 8 /* Gpio is open source type */ -#define FLAG_USED_AS_IRQ 9 /* GPIO is connected to an IRQ */ -#define FLAG_IRQ_IS_ENABLED 10 /* GPIO is connected to an enabled IRQ */ -#define FLAG_IS_HOGGED 11 /* GPIO is hogged */ -#define FLAG_TRANSITORY 12 /* GPIO may lose value in sleep or reset */ -#define FLAG_PULL_UP 13 /* GPIO has pull up enabled */ -#define FLAG_PULL_DOWN 14 /* GPIO has pull down enabled */ -#define FLAG_BIAS_DISABLE 15 /* GPIO has pull disabled */ -#define FLAG_EDGE_RISING 16 /* GPIO CDEV detects rising edge events */ -#define FLAG_EDGE_FALLING 17 /* GPIO CDEV detects falling edge events */ -#define FLAG_EVENT_CLOCK_REALTIME 18 /* GPIO CDEV reports REALTIME timestamps in events */ -#define FLAG_EVENT_CLOCK_HTE 19 /* GPIO CDEV reports hardware timestamps in events */ +#define GPIOD_FLAG_REQUESTED 0 /* GPIO is in use */ +#define GPIOD_FLAG_IS_OUT 1 /* GPIO is in output mode */ +#define GPIOD_FLAG_EXPORT 2 /* GPIO is exported to user-space */ +#define GPIOD_FLAG_SYSFS 3 /* GPIO is exported via /sys/class/gpio */ +#define GPIOD_FLAG_ACTIVE_LOW 6 /* GPIO is active-low */ +#define GPIOD_FLAG_OPEN_DRAIN 7 /* GPIO is open drain type */ +#define GPIOD_FLAG_OPEN_SOURCE 8 /* GPIO is open source type */ +#define GPIOD_FLAG_USED_AS_IRQ 9 /* GPIO is connected to an IRQ */ +#define GPIOD_FLAG_IRQ_IS_ENABLED 10 /* GPIO is connected to an enabled IRQ */ +#define GPIOD_FLAG_IS_HOGGED 11 /* GPIO is hogged */ +#define GPIOD_FLAG_TRANSITORY 12 /* GPIO may lose value in sleep or reset */ +#define GPIOD_FLAG_PULL_UP 13 /* GPIO has pull up enabled */ +#define GPIOD_FLAG_PULL_DOWN 14 /* GPIO has pull down enabled */ +#define GPIOD_FLAG_BIAS_DISABLE 15 /* GPIO has pull disabled */ +#define GPIOD_FLAG_EDGE_RISING 16 /* GPIO CDEV detects rising edge events */ +#define GPIOD_FLAG_EDGE_FALLING 17 /* GPIO CDEV detects falling edge events */ +#define GPIOD_FLAG_EVENT_CLOCK_REALTIME 18 /* GPIO CDEV reports REALTIME timestamps in events */ +#define GPIOD_FLAG_EVENT_CLOCK_HTE 19 /* GPIO CDEV reports hardware timestamps in events */ +#define GPIOD_FLAG_SHARED 20 /* GPIO is shared by multiple consumers */ +#define GPIOD_FLAG_SHARED_PROXY 21 /* GPIO is a virtual proxy to a physically shared pin. */ /* Connection label */ - const char *label; + struct gpio_desc_label __rcu *label; /* Name of the GPIO */ const char *name; #ifdef CONFIG_OF_DYNAMIC @@ -178,6 +222,27 @@ struct gpio_desc { #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); @@ -192,54 +257,48 @@ 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 gpio_do_set_config(struct gpio_desc *desc, unsigned long config); 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); - -/* - * Return the GPIO number of the passed descriptor relative to its chip - */ -static inline int gpio_chip_hwgpio(const struct gpio_desc *desc) -{ - return desc - &desc->gdev->descs[0]; -} +int gpiochip_get_ngpios(struct gpio_chip *gc, struct device *dev); +struct gpio_desc *gpiochip_get_desc(struct gpio_chip *gc, unsigned int hwnum); +const char *gpiod_get_label(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_pr(level, desc, fmt, ...) \ +do { \ + scoped_guard(srcu, &desc->gdev->desc_srcu) { \ + pr_##level("gpio-%d (%s): " fmt, desc_to_gpio(desc), \ + gpiod_get_label(desc) ?: "?", ##__VA_ARGS__); \ + } \ +} while (0) + +#define gpiod_err(desc, fmt, ...) __gpiod_pr(err, desc, fmt, ##__VA_ARGS__) +#define gpiod_warn(desc, fmt, ...) __gpiod_pr(warn, desc, fmt, ##__VA_ARGS__) +#define gpiod_dbg(desc, fmt, ...) __gpiod_pr(debug, desc, fmt, ##__VA_ARGS__) /* 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, ...) \ - dev_warn(&gc->gpiodev->dev, "(%s): " fmt, gc->label, ##__VA_ARGS__) -#define chip_info(gc, fmt, ...) \ - dev_info(&gc->gpiodev->dev, "(%s): " fmt, gc->label, ##__VA_ARGS__) -#define chip_dbg(gc, fmt, ...) \ - dev_dbg(&gc->gpiodev->dev, "(%s): " fmt, gc->label, ##__VA_ARGS__) +#define __gpiochip_pr(level, gc, fmt, ...) \ +do { \ + dev_##level(&gc->gpiodev->dev, "(%s): " fmt, gc->label, ##__VA_ARGS__); \ +} while (0) + +#define gpiochip_err(gc, fmt, ...) __gpiochip_pr(err, gc, fmt, ##__VA_ARGS__) +#define gpiochip_warn(gc, fmt, ...) __gpiochip_pr(warn, gc, fmt, ##__VA_ARGS__) +#define gpiochip_info(gc, fmt, ...) __gpiochip_pr(info, gc, fmt, ##__VA_ARGS__) +#define gpiochip_dbg(gc, fmt, ...) __gpiochip_pr(dbg, gc, fmt, ##__VA_ARGS__) #endif /* GPIOLIB_H */ |
