162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci
362306a36Sopenharmony_ci#include <linux/anon_inodes.h>
462306a36Sopenharmony_ci#include <linux/atomic.h>
562306a36Sopenharmony_ci#include <linux/bitmap.h>
662306a36Sopenharmony_ci#include <linux/build_bug.h>
762306a36Sopenharmony_ci#include <linux/cdev.h>
862306a36Sopenharmony_ci#include <linux/compat.h>
962306a36Sopenharmony_ci#include <linux/compiler.h>
1062306a36Sopenharmony_ci#include <linux/device.h>
1162306a36Sopenharmony_ci#include <linux/err.h>
1262306a36Sopenharmony_ci#include <linux/file.h>
1362306a36Sopenharmony_ci#include <linux/gpio.h>
1462306a36Sopenharmony_ci#include <linux/gpio/driver.h>
1562306a36Sopenharmony_ci#include <linux/hte.h>
1662306a36Sopenharmony_ci#include <linux/interrupt.h>
1762306a36Sopenharmony_ci#include <linux/irqreturn.h>
1862306a36Sopenharmony_ci#include <linux/kernel.h>
1962306a36Sopenharmony_ci#include <linux/kfifo.h>
2062306a36Sopenharmony_ci#include <linux/module.h>
2162306a36Sopenharmony_ci#include <linux/mutex.h>
2262306a36Sopenharmony_ci#include <linux/pinctrl/consumer.h>
2362306a36Sopenharmony_ci#include <linux/poll.h>
2462306a36Sopenharmony_ci#include <linux/seq_file.h>
2562306a36Sopenharmony_ci#include <linux/spinlock.h>
2662306a36Sopenharmony_ci#include <linux/timekeeping.h>
2762306a36Sopenharmony_ci#include <linux/uaccess.h>
2862306a36Sopenharmony_ci#include <linux/workqueue.h>
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci#include <uapi/linux/gpio.h>
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci#include "gpiolib.h"
3362306a36Sopenharmony_ci#include "gpiolib-cdev.h"
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci/*
3662306a36Sopenharmony_ci * Array sizes must ensure 64-bit alignment and not create holes in the
3762306a36Sopenharmony_ci * struct packing.
3862306a36Sopenharmony_ci */
3962306a36Sopenharmony_cistatic_assert(IS_ALIGNED(GPIO_V2_LINES_MAX, 2));
4062306a36Sopenharmony_cistatic_assert(IS_ALIGNED(GPIO_MAX_NAME_SIZE, 8));
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci/*
4362306a36Sopenharmony_ci * Check that uAPI structs are 64-bit aligned for 32/64-bit compatibility
4462306a36Sopenharmony_ci */
4562306a36Sopenharmony_cistatic_assert(IS_ALIGNED(sizeof(struct gpio_v2_line_attribute), 8));
4662306a36Sopenharmony_cistatic_assert(IS_ALIGNED(sizeof(struct gpio_v2_line_config_attribute), 8));
4762306a36Sopenharmony_cistatic_assert(IS_ALIGNED(sizeof(struct gpio_v2_line_config), 8));
4862306a36Sopenharmony_cistatic_assert(IS_ALIGNED(sizeof(struct gpio_v2_line_request), 8));
4962306a36Sopenharmony_cistatic_assert(IS_ALIGNED(sizeof(struct gpio_v2_line_info), 8));
5062306a36Sopenharmony_cistatic_assert(IS_ALIGNED(sizeof(struct gpio_v2_line_info_changed), 8));
5162306a36Sopenharmony_cistatic_assert(IS_ALIGNED(sizeof(struct gpio_v2_line_event), 8));
5262306a36Sopenharmony_cistatic_assert(IS_ALIGNED(sizeof(struct gpio_v2_line_values), 8));
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci/* Character device interface to GPIO.
5562306a36Sopenharmony_ci *
5662306a36Sopenharmony_ci * The GPIO character device, /dev/gpiochipN, provides userspace an
5762306a36Sopenharmony_ci * interface to gpiolib GPIOs via ioctl()s.
5862306a36Sopenharmony_ci */
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_citypedef __poll_t (*poll_fn)(struct file *, struct poll_table_struct *);
6162306a36Sopenharmony_citypedef long (*ioctl_fn)(struct file *, unsigned int, unsigned long);
6262306a36Sopenharmony_citypedef ssize_t (*read_fn)(struct file *, char __user *,
6362306a36Sopenharmony_ci			   size_t count, loff_t *);
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_cistatic __poll_t call_poll_locked(struct file *file,
6662306a36Sopenharmony_ci				 struct poll_table_struct *wait,
6762306a36Sopenharmony_ci				 struct gpio_device *gdev, poll_fn func)
6862306a36Sopenharmony_ci{
6962306a36Sopenharmony_ci	__poll_t ret;
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	down_read(&gdev->sem);
7262306a36Sopenharmony_ci	ret = func(file, wait);
7362306a36Sopenharmony_ci	up_read(&gdev->sem);
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	return ret;
7662306a36Sopenharmony_ci}
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_cistatic long call_ioctl_locked(struct file *file, unsigned int cmd,
7962306a36Sopenharmony_ci			      unsigned long arg, struct gpio_device *gdev,
8062306a36Sopenharmony_ci			      ioctl_fn func)
8162306a36Sopenharmony_ci{
8262306a36Sopenharmony_ci	long ret;
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	down_read(&gdev->sem);
8562306a36Sopenharmony_ci	ret = func(file, cmd, arg);
8662306a36Sopenharmony_ci	up_read(&gdev->sem);
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	return ret;
8962306a36Sopenharmony_ci}
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_cistatic ssize_t call_read_locked(struct file *file, char __user *buf,
9262306a36Sopenharmony_ci				size_t count, loff_t *f_ps,
9362306a36Sopenharmony_ci				struct gpio_device *gdev, read_fn func)
9462306a36Sopenharmony_ci{
9562306a36Sopenharmony_ci	ssize_t ret;
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	down_read(&gdev->sem);
9862306a36Sopenharmony_ci	ret = func(file, buf, count, f_ps);
9962306a36Sopenharmony_ci	up_read(&gdev->sem);
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	return ret;
10262306a36Sopenharmony_ci}
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci/*
10562306a36Sopenharmony_ci * GPIO line handle management
10662306a36Sopenharmony_ci */
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci#ifdef CONFIG_GPIO_CDEV_V1
10962306a36Sopenharmony_ci/**
11062306a36Sopenharmony_ci * struct linehandle_state - contains the state of a userspace handle
11162306a36Sopenharmony_ci * @gdev: the GPIO device the handle pertains to
11262306a36Sopenharmony_ci * @label: consumer label used to tag descriptors
11362306a36Sopenharmony_ci * @descs: the GPIO descriptors held by this handle
11462306a36Sopenharmony_ci * @num_descs: the number of descriptors held in the descs array
11562306a36Sopenharmony_ci */
11662306a36Sopenharmony_cistruct linehandle_state {
11762306a36Sopenharmony_ci	struct gpio_device *gdev;
11862306a36Sopenharmony_ci	const char *label;
11962306a36Sopenharmony_ci	struct gpio_desc *descs[GPIOHANDLES_MAX];
12062306a36Sopenharmony_ci	u32 num_descs;
12162306a36Sopenharmony_ci};
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci#define GPIOHANDLE_REQUEST_VALID_FLAGS \
12462306a36Sopenharmony_ci	(GPIOHANDLE_REQUEST_INPUT | \
12562306a36Sopenharmony_ci	GPIOHANDLE_REQUEST_OUTPUT | \
12662306a36Sopenharmony_ci	GPIOHANDLE_REQUEST_ACTIVE_LOW | \
12762306a36Sopenharmony_ci	GPIOHANDLE_REQUEST_BIAS_PULL_UP | \
12862306a36Sopenharmony_ci	GPIOHANDLE_REQUEST_BIAS_PULL_DOWN | \
12962306a36Sopenharmony_ci	GPIOHANDLE_REQUEST_BIAS_DISABLE | \
13062306a36Sopenharmony_ci	GPIOHANDLE_REQUEST_OPEN_DRAIN | \
13162306a36Sopenharmony_ci	GPIOHANDLE_REQUEST_OPEN_SOURCE)
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_cistatic int linehandle_validate_flags(u32 flags)
13462306a36Sopenharmony_ci{
13562306a36Sopenharmony_ci	/* Return an error if an unknown flag is set */
13662306a36Sopenharmony_ci	if (flags & ~GPIOHANDLE_REQUEST_VALID_FLAGS)
13762306a36Sopenharmony_ci		return -EINVAL;
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	/*
14062306a36Sopenharmony_ci	 * Do not allow both INPUT & OUTPUT flags to be set as they are
14162306a36Sopenharmony_ci	 * contradictory.
14262306a36Sopenharmony_ci	 */
14362306a36Sopenharmony_ci	if ((flags & GPIOHANDLE_REQUEST_INPUT) &&
14462306a36Sopenharmony_ci	    (flags & GPIOHANDLE_REQUEST_OUTPUT))
14562306a36Sopenharmony_ci		return -EINVAL;
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	/*
14862306a36Sopenharmony_ci	 * Do not allow OPEN_SOURCE & OPEN_DRAIN flags in a single request. If
14962306a36Sopenharmony_ci	 * the hardware actually supports enabling both at the same time the
15062306a36Sopenharmony_ci	 * electrical result would be disastrous.
15162306a36Sopenharmony_ci	 */
15262306a36Sopenharmony_ci	if ((flags & GPIOHANDLE_REQUEST_OPEN_DRAIN) &&
15362306a36Sopenharmony_ci	    (flags & GPIOHANDLE_REQUEST_OPEN_SOURCE))
15462306a36Sopenharmony_ci		return -EINVAL;
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	/* OPEN_DRAIN and OPEN_SOURCE flags only make sense for output mode. */
15762306a36Sopenharmony_ci	if (!(flags & GPIOHANDLE_REQUEST_OUTPUT) &&
15862306a36Sopenharmony_ci	    ((flags & GPIOHANDLE_REQUEST_OPEN_DRAIN) ||
15962306a36Sopenharmony_ci	     (flags & GPIOHANDLE_REQUEST_OPEN_SOURCE)))
16062306a36Sopenharmony_ci		return -EINVAL;
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	/* Bias flags only allowed for input or output mode. */
16362306a36Sopenharmony_ci	if (!((flags & GPIOHANDLE_REQUEST_INPUT) ||
16462306a36Sopenharmony_ci	      (flags & GPIOHANDLE_REQUEST_OUTPUT)) &&
16562306a36Sopenharmony_ci	    ((flags & GPIOHANDLE_REQUEST_BIAS_DISABLE) ||
16662306a36Sopenharmony_ci	     (flags & GPIOHANDLE_REQUEST_BIAS_PULL_UP) ||
16762306a36Sopenharmony_ci	     (flags & GPIOHANDLE_REQUEST_BIAS_PULL_DOWN)))
16862306a36Sopenharmony_ci		return -EINVAL;
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	/* Only one bias flag can be set. */
17162306a36Sopenharmony_ci	if (((flags & GPIOHANDLE_REQUEST_BIAS_DISABLE) &&
17262306a36Sopenharmony_ci	     (flags & (GPIOHANDLE_REQUEST_BIAS_PULL_DOWN |
17362306a36Sopenharmony_ci		       GPIOHANDLE_REQUEST_BIAS_PULL_UP))) ||
17462306a36Sopenharmony_ci	    ((flags & GPIOHANDLE_REQUEST_BIAS_PULL_DOWN) &&
17562306a36Sopenharmony_ci	     (flags & GPIOHANDLE_REQUEST_BIAS_PULL_UP)))
17662306a36Sopenharmony_ci		return -EINVAL;
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	return 0;
17962306a36Sopenharmony_ci}
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_cistatic void linehandle_flags_to_desc_flags(u32 lflags, unsigned long *flagsp)
18262306a36Sopenharmony_ci{
18362306a36Sopenharmony_ci	assign_bit(FLAG_ACTIVE_LOW, flagsp,
18462306a36Sopenharmony_ci		   lflags & GPIOHANDLE_REQUEST_ACTIVE_LOW);
18562306a36Sopenharmony_ci	assign_bit(FLAG_OPEN_DRAIN, flagsp,
18662306a36Sopenharmony_ci		   lflags & GPIOHANDLE_REQUEST_OPEN_DRAIN);
18762306a36Sopenharmony_ci	assign_bit(FLAG_OPEN_SOURCE, flagsp,
18862306a36Sopenharmony_ci		   lflags & GPIOHANDLE_REQUEST_OPEN_SOURCE);
18962306a36Sopenharmony_ci	assign_bit(FLAG_PULL_UP, flagsp,
19062306a36Sopenharmony_ci		   lflags & GPIOHANDLE_REQUEST_BIAS_PULL_UP);
19162306a36Sopenharmony_ci	assign_bit(FLAG_PULL_DOWN, flagsp,
19262306a36Sopenharmony_ci		   lflags & GPIOHANDLE_REQUEST_BIAS_PULL_DOWN);
19362306a36Sopenharmony_ci	assign_bit(FLAG_BIAS_DISABLE, flagsp,
19462306a36Sopenharmony_ci		   lflags & GPIOHANDLE_REQUEST_BIAS_DISABLE);
19562306a36Sopenharmony_ci}
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_cistatic long linehandle_set_config(struct linehandle_state *lh,
19862306a36Sopenharmony_ci				  void __user *ip)
19962306a36Sopenharmony_ci{
20062306a36Sopenharmony_ci	struct gpiohandle_config gcnf;
20162306a36Sopenharmony_ci	struct gpio_desc *desc;
20262306a36Sopenharmony_ci	int i, ret;
20362306a36Sopenharmony_ci	u32 lflags;
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	if (copy_from_user(&gcnf, ip, sizeof(gcnf)))
20662306a36Sopenharmony_ci		return -EFAULT;
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	lflags = gcnf.flags;
20962306a36Sopenharmony_ci	ret = linehandle_validate_flags(lflags);
21062306a36Sopenharmony_ci	if (ret)
21162306a36Sopenharmony_ci		return ret;
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	for (i = 0; i < lh->num_descs; i++) {
21462306a36Sopenharmony_ci		desc = lh->descs[i];
21562306a36Sopenharmony_ci		linehandle_flags_to_desc_flags(gcnf.flags, &desc->flags);
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci		/*
21862306a36Sopenharmony_ci		 * Lines have to be requested explicitly for input
21962306a36Sopenharmony_ci		 * or output, else the line will be treated "as is".
22062306a36Sopenharmony_ci		 */
22162306a36Sopenharmony_ci		if (lflags & GPIOHANDLE_REQUEST_OUTPUT) {
22262306a36Sopenharmony_ci			int val = !!gcnf.default_values[i];
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci			ret = gpiod_direction_output(desc, val);
22562306a36Sopenharmony_ci			if (ret)
22662306a36Sopenharmony_ci				return ret;
22762306a36Sopenharmony_ci		} else if (lflags & GPIOHANDLE_REQUEST_INPUT) {
22862306a36Sopenharmony_ci			ret = gpiod_direction_input(desc);
22962306a36Sopenharmony_ci			if (ret)
23062306a36Sopenharmony_ci				return ret;
23162306a36Sopenharmony_ci		}
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci		gpiod_line_state_notify(desc, GPIO_V2_LINE_CHANGED_CONFIG);
23462306a36Sopenharmony_ci	}
23562306a36Sopenharmony_ci	return 0;
23662306a36Sopenharmony_ci}
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_cistatic long linehandle_ioctl_unlocked(struct file *file, unsigned int cmd,
23962306a36Sopenharmony_ci				      unsigned long arg)
24062306a36Sopenharmony_ci{
24162306a36Sopenharmony_ci	struct linehandle_state *lh = file->private_data;
24262306a36Sopenharmony_ci	void __user *ip = (void __user *)arg;
24362306a36Sopenharmony_ci	struct gpiohandle_data ghd;
24462306a36Sopenharmony_ci	DECLARE_BITMAP(vals, GPIOHANDLES_MAX);
24562306a36Sopenharmony_ci	unsigned int i;
24662306a36Sopenharmony_ci	int ret;
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	if (!lh->gdev->chip)
24962306a36Sopenharmony_ci		return -ENODEV;
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	switch (cmd) {
25262306a36Sopenharmony_ci	case GPIOHANDLE_GET_LINE_VALUES_IOCTL:
25362306a36Sopenharmony_ci		/* NOTE: It's okay to read values of output lines */
25462306a36Sopenharmony_ci		ret = gpiod_get_array_value_complex(false, true,
25562306a36Sopenharmony_ci						    lh->num_descs, lh->descs,
25662306a36Sopenharmony_ci						    NULL, vals);
25762306a36Sopenharmony_ci		if (ret)
25862306a36Sopenharmony_ci			return ret;
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci		memset(&ghd, 0, sizeof(ghd));
26162306a36Sopenharmony_ci		for (i = 0; i < lh->num_descs; i++)
26262306a36Sopenharmony_ci			ghd.values[i] = test_bit(i, vals);
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci		if (copy_to_user(ip, &ghd, sizeof(ghd)))
26562306a36Sopenharmony_ci			return -EFAULT;
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci		return 0;
26862306a36Sopenharmony_ci	case GPIOHANDLE_SET_LINE_VALUES_IOCTL:
26962306a36Sopenharmony_ci		/*
27062306a36Sopenharmony_ci		 * All line descriptors were created at once with the same
27162306a36Sopenharmony_ci		 * flags so just check if the first one is really output.
27262306a36Sopenharmony_ci		 */
27362306a36Sopenharmony_ci		if (!test_bit(FLAG_IS_OUT, &lh->descs[0]->flags))
27462306a36Sopenharmony_ci			return -EPERM;
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci		if (copy_from_user(&ghd, ip, sizeof(ghd)))
27762306a36Sopenharmony_ci			return -EFAULT;
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci		/* Clamp all values to [0,1] */
28062306a36Sopenharmony_ci		for (i = 0; i < lh->num_descs; i++)
28162306a36Sopenharmony_ci			__assign_bit(i, vals, ghd.values[i]);
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci		/* Reuse the array setting function */
28462306a36Sopenharmony_ci		return gpiod_set_array_value_complex(false,
28562306a36Sopenharmony_ci						     true,
28662306a36Sopenharmony_ci						     lh->num_descs,
28762306a36Sopenharmony_ci						     lh->descs,
28862306a36Sopenharmony_ci						     NULL,
28962306a36Sopenharmony_ci						     vals);
29062306a36Sopenharmony_ci	case GPIOHANDLE_SET_CONFIG_IOCTL:
29162306a36Sopenharmony_ci		return linehandle_set_config(lh, ip);
29262306a36Sopenharmony_ci	default:
29362306a36Sopenharmony_ci		return -EINVAL;
29462306a36Sopenharmony_ci	}
29562306a36Sopenharmony_ci}
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_cistatic long linehandle_ioctl(struct file *file, unsigned int cmd,
29862306a36Sopenharmony_ci			     unsigned long arg)
29962306a36Sopenharmony_ci{
30062306a36Sopenharmony_ci	struct linehandle_state *lh = file->private_data;
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	return call_ioctl_locked(file, cmd, arg, lh->gdev,
30362306a36Sopenharmony_ci				 linehandle_ioctl_unlocked);
30462306a36Sopenharmony_ci}
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci#ifdef CONFIG_COMPAT
30762306a36Sopenharmony_cistatic long linehandle_ioctl_compat(struct file *file, unsigned int cmd,
30862306a36Sopenharmony_ci				    unsigned long arg)
30962306a36Sopenharmony_ci{
31062306a36Sopenharmony_ci	return linehandle_ioctl(file, cmd, (unsigned long)compat_ptr(arg));
31162306a36Sopenharmony_ci}
31262306a36Sopenharmony_ci#endif
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_cistatic void linehandle_free(struct linehandle_state *lh)
31562306a36Sopenharmony_ci{
31662306a36Sopenharmony_ci	int i;
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci	for (i = 0; i < lh->num_descs; i++)
31962306a36Sopenharmony_ci		if (lh->descs[i])
32062306a36Sopenharmony_ci			gpiod_free(lh->descs[i]);
32162306a36Sopenharmony_ci	kfree(lh->label);
32262306a36Sopenharmony_ci	gpio_device_put(lh->gdev);
32362306a36Sopenharmony_ci	kfree(lh);
32462306a36Sopenharmony_ci}
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_cistatic int linehandle_release(struct inode *inode, struct file *file)
32762306a36Sopenharmony_ci{
32862306a36Sopenharmony_ci	linehandle_free(file->private_data);
32962306a36Sopenharmony_ci	return 0;
33062306a36Sopenharmony_ci}
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_cistatic const struct file_operations linehandle_fileops = {
33362306a36Sopenharmony_ci	.release = linehandle_release,
33462306a36Sopenharmony_ci	.owner = THIS_MODULE,
33562306a36Sopenharmony_ci	.llseek = noop_llseek,
33662306a36Sopenharmony_ci	.unlocked_ioctl = linehandle_ioctl,
33762306a36Sopenharmony_ci#ifdef CONFIG_COMPAT
33862306a36Sopenharmony_ci	.compat_ioctl = linehandle_ioctl_compat,
33962306a36Sopenharmony_ci#endif
34062306a36Sopenharmony_ci};
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_cistatic int linehandle_create(struct gpio_device *gdev, void __user *ip)
34362306a36Sopenharmony_ci{
34462306a36Sopenharmony_ci	struct gpiohandle_request handlereq;
34562306a36Sopenharmony_ci	struct linehandle_state *lh;
34662306a36Sopenharmony_ci	struct file *file;
34762306a36Sopenharmony_ci	int fd, i, ret;
34862306a36Sopenharmony_ci	u32 lflags;
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci	if (copy_from_user(&handlereq, ip, sizeof(handlereq)))
35162306a36Sopenharmony_ci		return -EFAULT;
35262306a36Sopenharmony_ci	if ((handlereq.lines == 0) || (handlereq.lines > GPIOHANDLES_MAX))
35362306a36Sopenharmony_ci		return -EINVAL;
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci	lflags = handlereq.flags;
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci	ret = linehandle_validate_flags(lflags);
35862306a36Sopenharmony_ci	if (ret)
35962306a36Sopenharmony_ci		return ret;
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci	lh = kzalloc(sizeof(*lh), GFP_KERNEL);
36262306a36Sopenharmony_ci	if (!lh)
36362306a36Sopenharmony_ci		return -ENOMEM;
36462306a36Sopenharmony_ci	lh->gdev = gpio_device_get(gdev);
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci	if (handlereq.consumer_label[0] != '\0') {
36762306a36Sopenharmony_ci		/* label is only initialized if consumer_label is set */
36862306a36Sopenharmony_ci		lh->label = kstrndup(handlereq.consumer_label,
36962306a36Sopenharmony_ci				     sizeof(handlereq.consumer_label) - 1,
37062306a36Sopenharmony_ci				     GFP_KERNEL);
37162306a36Sopenharmony_ci		if (!lh->label) {
37262306a36Sopenharmony_ci			ret = -ENOMEM;
37362306a36Sopenharmony_ci			goto out_free_lh;
37462306a36Sopenharmony_ci		}
37562306a36Sopenharmony_ci	}
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci	lh->num_descs = handlereq.lines;
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci	/* Request each GPIO */
38062306a36Sopenharmony_ci	for (i = 0; i < handlereq.lines; i++) {
38162306a36Sopenharmony_ci		u32 offset = handlereq.lineoffsets[i];
38262306a36Sopenharmony_ci		struct gpio_desc *desc = gpiochip_get_desc(gdev->chip, offset);
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ci		if (IS_ERR(desc)) {
38562306a36Sopenharmony_ci			ret = PTR_ERR(desc);
38662306a36Sopenharmony_ci			goto out_free_lh;
38762306a36Sopenharmony_ci		}
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci		ret = gpiod_request_user(desc, lh->label);
39062306a36Sopenharmony_ci		if (ret)
39162306a36Sopenharmony_ci			goto out_free_lh;
39262306a36Sopenharmony_ci		lh->descs[i] = desc;
39362306a36Sopenharmony_ci		linehandle_flags_to_desc_flags(handlereq.flags, &desc->flags);
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci		ret = gpiod_set_transitory(desc, false);
39662306a36Sopenharmony_ci		if (ret < 0)
39762306a36Sopenharmony_ci			goto out_free_lh;
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci		/*
40062306a36Sopenharmony_ci		 * Lines have to be requested explicitly for input
40162306a36Sopenharmony_ci		 * or output, else the line will be treated "as is".
40262306a36Sopenharmony_ci		 */
40362306a36Sopenharmony_ci		if (lflags & GPIOHANDLE_REQUEST_OUTPUT) {
40462306a36Sopenharmony_ci			int val = !!handlereq.default_values[i];
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci			ret = gpiod_direction_output(desc, val);
40762306a36Sopenharmony_ci			if (ret)
40862306a36Sopenharmony_ci				goto out_free_lh;
40962306a36Sopenharmony_ci		} else if (lflags & GPIOHANDLE_REQUEST_INPUT) {
41062306a36Sopenharmony_ci			ret = gpiod_direction_input(desc);
41162306a36Sopenharmony_ci			if (ret)
41262306a36Sopenharmony_ci				goto out_free_lh;
41362306a36Sopenharmony_ci		}
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci		gpiod_line_state_notify(desc, GPIO_V2_LINE_CHANGED_REQUESTED);
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci		dev_dbg(&gdev->dev, "registered chardev handle for line %d\n",
41862306a36Sopenharmony_ci			offset);
41962306a36Sopenharmony_ci	}
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ci	fd = get_unused_fd_flags(O_RDONLY | O_CLOEXEC);
42262306a36Sopenharmony_ci	if (fd < 0) {
42362306a36Sopenharmony_ci		ret = fd;
42462306a36Sopenharmony_ci		goto out_free_lh;
42562306a36Sopenharmony_ci	}
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci	file = anon_inode_getfile("gpio-linehandle",
42862306a36Sopenharmony_ci				  &linehandle_fileops,
42962306a36Sopenharmony_ci				  lh,
43062306a36Sopenharmony_ci				  O_RDONLY | O_CLOEXEC);
43162306a36Sopenharmony_ci	if (IS_ERR(file)) {
43262306a36Sopenharmony_ci		ret = PTR_ERR(file);
43362306a36Sopenharmony_ci		goto out_put_unused_fd;
43462306a36Sopenharmony_ci	}
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci	handlereq.fd = fd;
43762306a36Sopenharmony_ci	if (copy_to_user(ip, &handlereq, sizeof(handlereq))) {
43862306a36Sopenharmony_ci		/*
43962306a36Sopenharmony_ci		 * fput() will trigger the release() callback, so do not go onto
44062306a36Sopenharmony_ci		 * the regular error cleanup path here.
44162306a36Sopenharmony_ci		 */
44262306a36Sopenharmony_ci		fput(file);
44362306a36Sopenharmony_ci		put_unused_fd(fd);
44462306a36Sopenharmony_ci		return -EFAULT;
44562306a36Sopenharmony_ci	}
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ci	fd_install(fd, file);
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci	dev_dbg(&gdev->dev, "registered chardev handle for %d lines\n",
45062306a36Sopenharmony_ci		lh->num_descs);
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ci	return 0;
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ciout_put_unused_fd:
45562306a36Sopenharmony_ci	put_unused_fd(fd);
45662306a36Sopenharmony_ciout_free_lh:
45762306a36Sopenharmony_ci	linehandle_free(lh);
45862306a36Sopenharmony_ci	return ret;
45962306a36Sopenharmony_ci}
46062306a36Sopenharmony_ci#endif /* CONFIG_GPIO_CDEV_V1 */
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_ci/**
46362306a36Sopenharmony_ci * struct line - contains the state of a requested line
46462306a36Sopenharmony_ci * @desc: the GPIO descriptor for this line.
46562306a36Sopenharmony_ci * @req: the corresponding line request
46662306a36Sopenharmony_ci * @irq: the interrupt triggered in response to events on this GPIO
46762306a36Sopenharmony_ci * @edflags: the edge flags, GPIO_V2_LINE_FLAG_EDGE_RISING and/or
46862306a36Sopenharmony_ci * GPIO_V2_LINE_FLAG_EDGE_FALLING, indicating the edge detection applied
46962306a36Sopenharmony_ci * @timestamp_ns: cache for the timestamp storing it between hardirq and
47062306a36Sopenharmony_ci * IRQ thread, used to bring the timestamp close to the actual event
47162306a36Sopenharmony_ci * @req_seqno: the seqno for the current edge event in the sequence of
47262306a36Sopenharmony_ci * events for the corresponding line request. This is drawn from the @req.
47362306a36Sopenharmony_ci * @line_seqno: the seqno for the current edge event in the sequence of
47462306a36Sopenharmony_ci * events for this line.
47562306a36Sopenharmony_ci * @work: the worker that implements software debouncing
47662306a36Sopenharmony_ci * @sw_debounced: flag indicating if the software debouncer is active
47762306a36Sopenharmony_ci * @level: the current debounced physical level of the line
47862306a36Sopenharmony_ci * @hdesc: the Hardware Timestamp Engine (HTE) descriptor
47962306a36Sopenharmony_ci * @raw_level: the line level at the time of event
48062306a36Sopenharmony_ci * @total_discard_seq: the running counter of the discarded events
48162306a36Sopenharmony_ci * @last_seqno: the last sequence number before debounce period expires
48262306a36Sopenharmony_ci */
48362306a36Sopenharmony_cistruct line {
48462306a36Sopenharmony_ci	struct gpio_desc *desc;
48562306a36Sopenharmony_ci	/*
48662306a36Sopenharmony_ci	 * -- edge detector specific fields --
48762306a36Sopenharmony_ci	 */
48862306a36Sopenharmony_ci	struct linereq *req;
48962306a36Sopenharmony_ci	unsigned int irq;
49062306a36Sopenharmony_ci	/*
49162306a36Sopenharmony_ci	 * The flags for the active edge detector configuration.
49262306a36Sopenharmony_ci	 *
49362306a36Sopenharmony_ci	 * edflags is set by linereq_create(), linereq_free(), and
49462306a36Sopenharmony_ci	 * linereq_set_config_unlocked(), which are themselves mutually
49562306a36Sopenharmony_ci	 * exclusive, and is accessed by edge_irq_thread(),
49662306a36Sopenharmony_ci	 * process_hw_ts_thread() and debounce_work_func(),
49762306a36Sopenharmony_ci	 * which can all live with a slightly stale value.
49862306a36Sopenharmony_ci	 */
49962306a36Sopenharmony_ci	u64 edflags;
50062306a36Sopenharmony_ci	/*
50162306a36Sopenharmony_ci	 * timestamp_ns and req_seqno are accessed only by
50262306a36Sopenharmony_ci	 * edge_irq_handler() and edge_irq_thread(), which are themselves
50362306a36Sopenharmony_ci	 * mutually exclusive, so no additional protection is necessary.
50462306a36Sopenharmony_ci	 */
50562306a36Sopenharmony_ci	u64 timestamp_ns;
50662306a36Sopenharmony_ci	u32 req_seqno;
50762306a36Sopenharmony_ci	/*
50862306a36Sopenharmony_ci	 * line_seqno is accessed by either edge_irq_thread() or
50962306a36Sopenharmony_ci	 * debounce_work_func(), which are themselves mutually exclusive,
51062306a36Sopenharmony_ci	 * so no additional protection is necessary.
51162306a36Sopenharmony_ci	 */
51262306a36Sopenharmony_ci	u32 line_seqno;
51362306a36Sopenharmony_ci	/*
51462306a36Sopenharmony_ci	 * -- debouncer specific fields --
51562306a36Sopenharmony_ci	 */
51662306a36Sopenharmony_ci	struct delayed_work work;
51762306a36Sopenharmony_ci	/*
51862306a36Sopenharmony_ci	 * sw_debounce is accessed by linereq_set_config(), which is the
51962306a36Sopenharmony_ci	 * only setter, and linereq_get_values(), which can live with a
52062306a36Sopenharmony_ci	 * slightly stale value.
52162306a36Sopenharmony_ci	 */
52262306a36Sopenharmony_ci	unsigned int sw_debounced;
52362306a36Sopenharmony_ci	/*
52462306a36Sopenharmony_ci	 * level is accessed by debounce_work_func(), which is the only
52562306a36Sopenharmony_ci	 * setter, and linereq_get_values() which can live with a slightly
52662306a36Sopenharmony_ci	 * stale value.
52762306a36Sopenharmony_ci	 */
52862306a36Sopenharmony_ci	unsigned int level;
52962306a36Sopenharmony_ci#ifdef CONFIG_HTE
53062306a36Sopenharmony_ci	struct hte_ts_desc hdesc;
53162306a36Sopenharmony_ci	/*
53262306a36Sopenharmony_ci	 * HTE provider sets line level at the time of event. The valid
53362306a36Sopenharmony_ci	 * value is 0 or 1 and negative value for an error.
53462306a36Sopenharmony_ci	 */
53562306a36Sopenharmony_ci	int raw_level;
53662306a36Sopenharmony_ci	/*
53762306a36Sopenharmony_ci	 * when sw_debounce is set on HTE enabled line, this is running
53862306a36Sopenharmony_ci	 * counter of the discarded events.
53962306a36Sopenharmony_ci	 */
54062306a36Sopenharmony_ci	u32 total_discard_seq;
54162306a36Sopenharmony_ci	/*
54262306a36Sopenharmony_ci	 * when sw_debounce is set on HTE enabled line, this variable records
54362306a36Sopenharmony_ci	 * last sequence number before debounce period expires.
54462306a36Sopenharmony_ci	 */
54562306a36Sopenharmony_ci	u32 last_seqno;
54662306a36Sopenharmony_ci#endif /* CONFIG_HTE */
54762306a36Sopenharmony_ci};
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_ci/**
55062306a36Sopenharmony_ci * struct linereq - contains the state of a userspace line request
55162306a36Sopenharmony_ci * @gdev: the GPIO device the line request pertains to
55262306a36Sopenharmony_ci * @label: consumer label used to tag GPIO descriptors
55362306a36Sopenharmony_ci * @num_lines: the number of lines in the lines array
55462306a36Sopenharmony_ci * @wait: wait queue that handles blocking reads of events
55562306a36Sopenharmony_ci * @device_unregistered_nb: notifier block for receiving gdev unregister events
55662306a36Sopenharmony_ci * @event_buffer_size: the number of elements allocated in @events
55762306a36Sopenharmony_ci * @events: KFIFO for the GPIO events
55862306a36Sopenharmony_ci * @seqno: the sequence number for edge events generated on all lines in
55962306a36Sopenharmony_ci * this line request.  Note that this is not used when @num_lines is 1, as
56062306a36Sopenharmony_ci * the line_seqno is then the same and is cheaper to calculate.
56162306a36Sopenharmony_ci * @config_mutex: mutex for serializing ioctl() calls to ensure consistency
56262306a36Sopenharmony_ci * of configuration, particularly multi-step accesses to desc flags.
56362306a36Sopenharmony_ci * @lines: the lines held by this line request, with @num_lines elements.
56462306a36Sopenharmony_ci */
56562306a36Sopenharmony_cistruct linereq {
56662306a36Sopenharmony_ci	struct gpio_device *gdev;
56762306a36Sopenharmony_ci	const char *label;
56862306a36Sopenharmony_ci	u32 num_lines;
56962306a36Sopenharmony_ci	wait_queue_head_t wait;
57062306a36Sopenharmony_ci	struct notifier_block device_unregistered_nb;
57162306a36Sopenharmony_ci	u32 event_buffer_size;
57262306a36Sopenharmony_ci	DECLARE_KFIFO_PTR(events, struct gpio_v2_line_event);
57362306a36Sopenharmony_ci	atomic_t seqno;
57462306a36Sopenharmony_ci	struct mutex config_mutex;
57562306a36Sopenharmony_ci	struct line lines[];
57662306a36Sopenharmony_ci};
57762306a36Sopenharmony_ci
57862306a36Sopenharmony_ci#define GPIO_V2_LINE_BIAS_FLAGS \
57962306a36Sopenharmony_ci	(GPIO_V2_LINE_FLAG_BIAS_PULL_UP | \
58062306a36Sopenharmony_ci	 GPIO_V2_LINE_FLAG_BIAS_PULL_DOWN | \
58162306a36Sopenharmony_ci	 GPIO_V2_LINE_FLAG_BIAS_DISABLED)
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_ci#define GPIO_V2_LINE_DIRECTION_FLAGS \
58462306a36Sopenharmony_ci	(GPIO_V2_LINE_FLAG_INPUT | \
58562306a36Sopenharmony_ci	 GPIO_V2_LINE_FLAG_OUTPUT)
58662306a36Sopenharmony_ci
58762306a36Sopenharmony_ci#define GPIO_V2_LINE_DRIVE_FLAGS \
58862306a36Sopenharmony_ci	(GPIO_V2_LINE_FLAG_OPEN_DRAIN | \
58962306a36Sopenharmony_ci	 GPIO_V2_LINE_FLAG_OPEN_SOURCE)
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ci#define GPIO_V2_LINE_EDGE_FLAGS \
59262306a36Sopenharmony_ci	(GPIO_V2_LINE_FLAG_EDGE_RISING | \
59362306a36Sopenharmony_ci	 GPIO_V2_LINE_FLAG_EDGE_FALLING)
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_ci#define GPIO_V2_LINE_FLAG_EDGE_BOTH GPIO_V2_LINE_EDGE_FLAGS
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_ci#define GPIO_V2_LINE_VALID_FLAGS \
59862306a36Sopenharmony_ci	(GPIO_V2_LINE_FLAG_ACTIVE_LOW | \
59962306a36Sopenharmony_ci	 GPIO_V2_LINE_DIRECTION_FLAGS | \
60062306a36Sopenharmony_ci	 GPIO_V2_LINE_DRIVE_FLAGS | \
60162306a36Sopenharmony_ci	 GPIO_V2_LINE_EDGE_FLAGS | \
60262306a36Sopenharmony_ci	 GPIO_V2_LINE_FLAG_EVENT_CLOCK_REALTIME | \
60362306a36Sopenharmony_ci	 GPIO_V2_LINE_FLAG_EVENT_CLOCK_HTE | \
60462306a36Sopenharmony_ci	 GPIO_V2_LINE_BIAS_FLAGS)
60562306a36Sopenharmony_ci
60662306a36Sopenharmony_ci/* subset of flags relevant for edge detector configuration */
60762306a36Sopenharmony_ci#define GPIO_V2_LINE_EDGE_DETECTOR_FLAGS \
60862306a36Sopenharmony_ci	(GPIO_V2_LINE_FLAG_ACTIVE_LOW | \
60962306a36Sopenharmony_ci	 GPIO_V2_LINE_FLAG_EVENT_CLOCK_HTE | \
61062306a36Sopenharmony_ci	 GPIO_V2_LINE_EDGE_FLAGS)
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_cistatic int linereq_unregistered_notify(struct notifier_block *nb,
61362306a36Sopenharmony_ci				       unsigned long action, void *data)
61462306a36Sopenharmony_ci{
61562306a36Sopenharmony_ci	struct linereq *lr = container_of(nb, struct linereq,
61662306a36Sopenharmony_ci					  device_unregistered_nb);
61762306a36Sopenharmony_ci
61862306a36Sopenharmony_ci	wake_up_poll(&lr->wait, EPOLLIN | EPOLLERR);
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_ci	return NOTIFY_OK;
62162306a36Sopenharmony_ci}
62262306a36Sopenharmony_ci
62362306a36Sopenharmony_cistatic void linereq_put_event(struct linereq *lr,
62462306a36Sopenharmony_ci			      struct gpio_v2_line_event *le)
62562306a36Sopenharmony_ci{
62662306a36Sopenharmony_ci	bool overflow = false;
62762306a36Sopenharmony_ci
62862306a36Sopenharmony_ci	spin_lock(&lr->wait.lock);
62962306a36Sopenharmony_ci	if (kfifo_is_full(&lr->events)) {
63062306a36Sopenharmony_ci		overflow = true;
63162306a36Sopenharmony_ci		kfifo_skip(&lr->events);
63262306a36Sopenharmony_ci	}
63362306a36Sopenharmony_ci	kfifo_in(&lr->events, le, 1);
63462306a36Sopenharmony_ci	spin_unlock(&lr->wait.lock);
63562306a36Sopenharmony_ci	if (!overflow)
63662306a36Sopenharmony_ci		wake_up_poll(&lr->wait, EPOLLIN);
63762306a36Sopenharmony_ci	else
63862306a36Sopenharmony_ci		pr_debug_ratelimited("event FIFO is full - event dropped\n");
63962306a36Sopenharmony_ci}
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_cistatic u64 line_event_timestamp(struct line *line)
64262306a36Sopenharmony_ci{
64362306a36Sopenharmony_ci	if (test_bit(FLAG_EVENT_CLOCK_REALTIME, &line->desc->flags))
64462306a36Sopenharmony_ci		return ktime_get_real_ns();
64562306a36Sopenharmony_ci	else if (IS_ENABLED(CONFIG_HTE) &&
64662306a36Sopenharmony_ci		 test_bit(FLAG_EVENT_CLOCK_HTE, &line->desc->flags))
64762306a36Sopenharmony_ci		return line->timestamp_ns;
64862306a36Sopenharmony_ci
64962306a36Sopenharmony_ci	return ktime_get_ns();
65062306a36Sopenharmony_ci}
65162306a36Sopenharmony_ci
65262306a36Sopenharmony_cistatic u32 line_event_id(int level)
65362306a36Sopenharmony_ci{
65462306a36Sopenharmony_ci	return level ? GPIO_V2_LINE_EVENT_RISING_EDGE :
65562306a36Sopenharmony_ci		       GPIO_V2_LINE_EVENT_FALLING_EDGE;
65662306a36Sopenharmony_ci}
65762306a36Sopenharmony_ci
65862306a36Sopenharmony_ci#ifdef CONFIG_HTE
65962306a36Sopenharmony_ci
66062306a36Sopenharmony_cistatic enum hte_return process_hw_ts_thread(void *p)
66162306a36Sopenharmony_ci{
66262306a36Sopenharmony_ci	struct line *line;
66362306a36Sopenharmony_ci	struct linereq *lr;
66462306a36Sopenharmony_ci	struct gpio_v2_line_event le;
66562306a36Sopenharmony_ci	u64 edflags;
66662306a36Sopenharmony_ci	int level;
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_ci	if (!p)
66962306a36Sopenharmony_ci		return HTE_CB_HANDLED;
67062306a36Sopenharmony_ci
67162306a36Sopenharmony_ci	line = p;
67262306a36Sopenharmony_ci	lr = line->req;
67362306a36Sopenharmony_ci
67462306a36Sopenharmony_ci	memset(&le, 0, sizeof(le));
67562306a36Sopenharmony_ci
67662306a36Sopenharmony_ci	le.timestamp_ns = line->timestamp_ns;
67762306a36Sopenharmony_ci	edflags = READ_ONCE(line->edflags);
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_ci	switch (edflags & GPIO_V2_LINE_EDGE_FLAGS) {
68062306a36Sopenharmony_ci	case GPIO_V2_LINE_FLAG_EDGE_BOTH:
68162306a36Sopenharmony_ci		level = (line->raw_level >= 0) ?
68262306a36Sopenharmony_ci				line->raw_level :
68362306a36Sopenharmony_ci				gpiod_get_raw_value_cansleep(line->desc);
68462306a36Sopenharmony_ci
68562306a36Sopenharmony_ci		if (edflags & GPIO_V2_LINE_FLAG_ACTIVE_LOW)
68662306a36Sopenharmony_ci			level = !level;
68762306a36Sopenharmony_ci
68862306a36Sopenharmony_ci		le.id = line_event_id(level);
68962306a36Sopenharmony_ci		break;
69062306a36Sopenharmony_ci	case GPIO_V2_LINE_FLAG_EDGE_RISING:
69162306a36Sopenharmony_ci		le.id = GPIO_V2_LINE_EVENT_RISING_EDGE;
69262306a36Sopenharmony_ci		break;
69362306a36Sopenharmony_ci	case GPIO_V2_LINE_FLAG_EDGE_FALLING:
69462306a36Sopenharmony_ci		le.id = GPIO_V2_LINE_EVENT_FALLING_EDGE;
69562306a36Sopenharmony_ci		break;
69662306a36Sopenharmony_ci	default:
69762306a36Sopenharmony_ci		return HTE_CB_HANDLED;
69862306a36Sopenharmony_ci	}
69962306a36Sopenharmony_ci	le.line_seqno = line->line_seqno;
70062306a36Sopenharmony_ci	le.seqno = (lr->num_lines == 1) ? le.line_seqno : line->req_seqno;
70162306a36Sopenharmony_ci	le.offset = gpio_chip_hwgpio(line->desc);
70262306a36Sopenharmony_ci
70362306a36Sopenharmony_ci	linereq_put_event(lr, &le);
70462306a36Sopenharmony_ci
70562306a36Sopenharmony_ci	return HTE_CB_HANDLED;
70662306a36Sopenharmony_ci}
70762306a36Sopenharmony_ci
70862306a36Sopenharmony_cistatic enum hte_return process_hw_ts(struct hte_ts_data *ts, void *p)
70962306a36Sopenharmony_ci{
71062306a36Sopenharmony_ci	struct line *line;
71162306a36Sopenharmony_ci	struct linereq *lr;
71262306a36Sopenharmony_ci	int diff_seqno = 0;
71362306a36Sopenharmony_ci
71462306a36Sopenharmony_ci	if (!ts || !p)
71562306a36Sopenharmony_ci		return HTE_CB_HANDLED;
71662306a36Sopenharmony_ci
71762306a36Sopenharmony_ci	line = p;
71862306a36Sopenharmony_ci	line->timestamp_ns = ts->tsc;
71962306a36Sopenharmony_ci	line->raw_level = ts->raw_level;
72062306a36Sopenharmony_ci	lr = line->req;
72162306a36Sopenharmony_ci
72262306a36Sopenharmony_ci	if (READ_ONCE(line->sw_debounced)) {
72362306a36Sopenharmony_ci		line->total_discard_seq++;
72462306a36Sopenharmony_ci		line->last_seqno = ts->seq;
72562306a36Sopenharmony_ci		mod_delayed_work(system_wq, &line->work,
72662306a36Sopenharmony_ci		  usecs_to_jiffies(READ_ONCE(line->desc->debounce_period_us)));
72762306a36Sopenharmony_ci	} else {
72862306a36Sopenharmony_ci		if (unlikely(ts->seq < line->line_seqno))
72962306a36Sopenharmony_ci			return HTE_CB_HANDLED;
73062306a36Sopenharmony_ci
73162306a36Sopenharmony_ci		diff_seqno = ts->seq - line->line_seqno;
73262306a36Sopenharmony_ci		line->line_seqno = ts->seq;
73362306a36Sopenharmony_ci		if (lr->num_lines != 1)
73462306a36Sopenharmony_ci			line->req_seqno = atomic_add_return(diff_seqno,
73562306a36Sopenharmony_ci							    &lr->seqno);
73662306a36Sopenharmony_ci
73762306a36Sopenharmony_ci		return HTE_RUN_SECOND_CB;
73862306a36Sopenharmony_ci	}
73962306a36Sopenharmony_ci
74062306a36Sopenharmony_ci	return HTE_CB_HANDLED;
74162306a36Sopenharmony_ci}
74262306a36Sopenharmony_ci
74362306a36Sopenharmony_cistatic int hte_edge_setup(struct line *line, u64 eflags)
74462306a36Sopenharmony_ci{
74562306a36Sopenharmony_ci	int ret;
74662306a36Sopenharmony_ci	unsigned long flags = 0;
74762306a36Sopenharmony_ci	struct hte_ts_desc *hdesc = &line->hdesc;
74862306a36Sopenharmony_ci
74962306a36Sopenharmony_ci	if (eflags & GPIO_V2_LINE_FLAG_EDGE_RISING)
75062306a36Sopenharmony_ci		flags |= test_bit(FLAG_ACTIVE_LOW, &line->desc->flags) ?
75162306a36Sopenharmony_ci				 HTE_FALLING_EDGE_TS :
75262306a36Sopenharmony_ci				 HTE_RISING_EDGE_TS;
75362306a36Sopenharmony_ci	if (eflags & GPIO_V2_LINE_FLAG_EDGE_FALLING)
75462306a36Sopenharmony_ci		flags |= test_bit(FLAG_ACTIVE_LOW, &line->desc->flags) ?
75562306a36Sopenharmony_ci				 HTE_RISING_EDGE_TS :
75662306a36Sopenharmony_ci				 HTE_FALLING_EDGE_TS;
75762306a36Sopenharmony_ci
75862306a36Sopenharmony_ci	line->total_discard_seq = 0;
75962306a36Sopenharmony_ci
76062306a36Sopenharmony_ci	hte_init_line_attr(hdesc, desc_to_gpio(line->desc), flags, NULL,
76162306a36Sopenharmony_ci			   line->desc);
76262306a36Sopenharmony_ci
76362306a36Sopenharmony_ci	ret = hte_ts_get(NULL, hdesc, 0);
76462306a36Sopenharmony_ci	if (ret)
76562306a36Sopenharmony_ci		return ret;
76662306a36Sopenharmony_ci
76762306a36Sopenharmony_ci	return hte_request_ts_ns(hdesc, process_hw_ts, process_hw_ts_thread,
76862306a36Sopenharmony_ci				 line);
76962306a36Sopenharmony_ci}
77062306a36Sopenharmony_ci
77162306a36Sopenharmony_ci#else
77262306a36Sopenharmony_ci
77362306a36Sopenharmony_cistatic int hte_edge_setup(struct line *line, u64 eflags)
77462306a36Sopenharmony_ci{
77562306a36Sopenharmony_ci	return 0;
77662306a36Sopenharmony_ci}
77762306a36Sopenharmony_ci#endif /* CONFIG_HTE */
77862306a36Sopenharmony_ci
77962306a36Sopenharmony_cistatic irqreturn_t edge_irq_thread(int irq, void *p)
78062306a36Sopenharmony_ci{
78162306a36Sopenharmony_ci	struct line *line = p;
78262306a36Sopenharmony_ci	struct linereq *lr = line->req;
78362306a36Sopenharmony_ci	struct gpio_v2_line_event le;
78462306a36Sopenharmony_ci
78562306a36Sopenharmony_ci	/* Do not leak kernel stack to userspace */
78662306a36Sopenharmony_ci	memset(&le, 0, sizeof(le));
78762306a36Sopenharmony_ci
78862306a36Sopenharmony_ci	if (line->timestamp_ns) {
78962306a36Sopenharmony_ci		le.timestamp_ns = line->timestamp_ns;
79062306a36Sopenharmony_ci	} else {
79162306a36Sopenharmony_ci		/*
79262306a36Sopenharmony_ci		 * We may be running from a nested threaded interrupt in
79362306a36Sopenharmony_ci		 * which case we didn't get the timestamp from
79462306a36Sopenharmony_ci		 * edge_irq_handler().
79562306a36Sopenharmony_ci		 */
79662306a36Sopenharmony_ci		le.timestamp_ns = line_event_timestamp(line);
79762306a36Sopenharmony_ci		if (lr->num_lines != 1)
79862306a36Sopenharmony_ci			line->req_seqno = atomic_inc_return(&lr->seqno);
79962306a36Sopenharmony_ci	}
80062306a36Sopenharmony_ci	line->timestamp_ns = 0;
80162306a36Sopenharmony_ci
80262306a36Sopenharmony_ci	switch (READ_ONCE(line->edflags) & GPIO_V2_LINE_EDGE_FLAGS) {
80362306a36Sopenharmony_ci	case GPIO_V2_LINE_FLAG_EDGE_BOTH:
80462306a36Sopenharmony_ci		le.id = line_event_id(gpiod_get_value_cansleep(line->desc));
80562306a36Sopenharmony_ci		break;
80662306a36Sopenharmony_ci	case GPIO_V2_LINE_FLAG_EDGE_RISING:
80762306a36Sopenharmony_ci		le.id = GPIO_V2_LINE_EVENT_RISING_EDGE;
80862306a36Sopenharmony_ci		break;
80962306a36Sopenharmony_ci	case GPIO_V2_LINE_FLAG_EDGE_FALLING:
81062306a36Sopenharmony_ci		le.id = GPIO_V2_LINE_EVENT_FALLING_EDGE;
81162306a36Sopenharmony_ci		break;
81262306a36Sopenharmony_ci	default:
81362306a36Sopenharmony_ci		return IRQ_NONE;
81462306a36Sopenharmony_ci	}
81562306a36Sopenharmony_ci	line->line_seqno++;
81662306a36Sopenharmony_ci	le.line_seqno = line->line_seqno;
81762306a36Sopenharmony_ci	le.seqno = (lr->num_lines == 1) ? le.line_seqno : line->req_seqno;
81862306a36Sopenharmony_ci	le.offset = gpio_chip_hwgpio(line->desc);
81962306a36Sopenharmony_ci
82062306a36Sopenharmony_ci	linereq_put_event(lr, &le);
82162306a36Sopenharmony_ci
82262306a36Sopenharmony_ci	return IRQ_HANDLED;
82362306a36Sopenharmony_ci}
82462306a36Sopenharmony_ci
82562306a36Sopenharmony_cistatic irqreturn_t edge_irq_handler(int irq, void *p)
82662306a36Sopenharmony_ci{
82762306a36Sopenharmony_ci	struct line *line = p;
82862306a36Sopenharmony_ci	struct linereq *lr = line->req;
82962306a36Sopenharmony_ci
83062306a36Sopenharmony_ci	/*
83162306a36Sopenharmony_ci	 * Just store the timestamp in hardirq context so we get it as
83262306a36Sopenharmony_ci	 * close in time as possible to the actual event.
83362306a36Sopenharmony_ci	 */
83462306a36Sopenharmony_ci	line->timestamp_ns = line_event_timestamp(line);
83562306a36Sopenharmony_ci
83662306a36Sopenharmony_ci	if (lr->num_lines != 1)
83762306a36Sopenharmony_ci		line->req_seqno = atomic_inc_return(&lr->seqno);
83862306a36Sopenharmony_ci
83962306a36Sopenharmony_ci	return IRQ_WAKE_THREAD;
84062306a36Sopenharmony_ci}
84162306a36Sopenharmony_ci
84262306a36Sopenharmony_ci/*
84362306a36Sopenharmony_ci * returns the current debounced logical value.
84462306a36Sopenharmony_ci */
84562306a36Sopenharmony_cistatic bool debounced_value(struct line *line)
84662306a36Sopenharmony_ci{
84762306a36Sopenharmony_ci	bool value;
84862306a36Sopenharmony_ci
84962306a36Sopenharmony_ci	/*
85062306a36Sopenharmony_ci	 * minor race - debouncer may be stopped here, so edge_detector_stop()
85162306a36Sopenharmony_ci	 * must leave the value unchanged so the following will read the level
85262306a36Sopenharmony_ci	 * from when the debouncer was last running.
85362306a36Sopenharmony_ci	 */
85462306a36Sopenharmony_ci	value = READ_ONCE(line->level);
85562306a36Sopenharmony_ci
85662306a36Sopenharmony_ci	if (test_bit(FLAG_ACTIVE_LOW, &line->desc->flags))
85762306a36Sopenharmony_ci		value = !value;
85862306a36Sopenharmony_ci
85962306a36Sopenharmony_ci	return value;
86062306a36Sopenharmony_ci}
86162306a36Sopenharmony_ci
86262306a36Sopenharmony_cistatic irqreturn_t debounce_irq_handler(int irq, void *p)
86362306a36Sopenharmony_ci{
86462306a36Sopenharmony_ci	struct line *line = p;
86562306a36Sopenharmony_ci
86662306a36Sopenharmony_ci	mod_delayed_work(system_wq, &line->work,
86762306a36Sopenharmony_ci		usecs_to_jiffies(READ_ONCE(line->desc->debounce_period_us)));
86862306a36Sopenharmony_ci
86962306a36Sopenharmony_ci	return IRQ_HANDLED;
87062306a36Sopenharmony_ci}
87162306a36Sopenharmony_ci
87262306a36Sopenharmony_cistatic void debounce_work_func(struct work_struct *work)
87362306a36Sopenharmony_ci{
87462306a36Sopenharmony_ci	struct gpio_v2_line_event le;
87562306a36Sopenharmony_ci	struct line *line = container_of(work, struct line, work.work);
87662306a36Sopenharmony_ci	struct linereq *lr;
87762306a36Sopenharmony_ci	u64 eflags, edflags = READ_ONCE(line->edflags);
87862306a36Sopenharmony_ci	int level = -1;
87962306a36Sopenharmony_ci#ifdef CONFIG_HTE
88062306a36Sopenharmony_ci	int diff_seqno;
88162306a36Sopenharmony_ci
88262306a36Sopenharmony_ci	if (edflags & GPIO_V2_LINE_FLAG_EVENT_CLOCK_HTE)
88362306a36Sopenharmony_ci		level = line->raw_level;
88462306a36Sopenharmony_ci#endif
88562306a36Sopenharmony_ci	if (level < 0)
88662306a36Sopenharmony_ci		level = gpiod_get_raw_value_cansleep(line->desc);
88762306a36Sopenharmony_ci	if (level < 0) {
88862306a36Sopenharmony_ci		pr_debug_ratelimited("debouncer failed to read line value\n");
88962306a36Sopenharmony_ci		return;
89062306a36Sopenharmony_ci	}
89162306a36Sopenharmony_ci
89262306a36Sopenharmony_ci	if (READ_ONCE(line->level) == level)
89362306a36Sopenharmony_ci		return;
89462306a36Sopenharmony_ci
89562306a36Sopenharmony_ci	WRITE_ONCE(line->level, level);
89662306a36Sopenharmony_ci
89762306a36Sopenharmony_ci	/* -- edge detection -- */
89862306a36Sopenharmony_ci	eflags = edflags & GPIO_V2_LINE_EDGE_FLAGS;
89962306a36Sopenharmony_ci	if (!eflags)
90062306a36Sopenharmony_ci		return;
90162306a36Sopenharmony_ci
90262306a36Sopenharmony_ci	/* switch from physical level to logical - if they differ */
90362306a36Sopenharmony_ci	if (edflags & GPIO_V2_LINE_FLAG_ACTIVE_LOW)
90462306a36Sopenharmony_ci		level = !level;
90562306a36Sopenharmony_ci
90662306a36Sopenharmony_ci	/* ignore edges that are not being monitored */
90762306a36Sopenharmony_ci	if (((eflags == GPIO_V2_LINE_FLAG_EDGE_RISING) && !level) ||
90862306a36Sopenharmony_ci	    ((eflags == GPIO_V2_LINE_FLAG_EDGE_FALLING) && level))
90962306a36Sopenharmony_ci		return;
91062306a36Sopenharmony_ci
91162306a36Sopenharmony_ci	/* Do not leak kernel stack to userspace */
91262306a36Sopenharmony_ci	memset(&le, 0, sizeof(le));
91362306a36Sopenharmony_ci
91462306a36Sopenharmony_ci	lr = line->req;
91562306a36Sopenharmony_ci	le.timestamp_ns = line_event_timestamp(line);
91662306a36Sopenharmony_ci	le.offset = gpio_chip_hwgpio(line->desc);
91762306a36Sopenharmony_ci#ifdef CONFIG_HTE
91862306a36Sopenharmony_ci	if (edflags & GPIO_V2_LINE_FLAG_EVENT_CLOCK_HTE) {
91962306a36Sopenharmony_ci		/* discard events except the last one */
92062306a36Sopenharmony_ci		line->total_discard_seq -= 1;
92162306a36Sopenharmony_ci		diff_seqno = line->last_seqno - line->total_discard_seq -
92262306a36Sopenharmony_ci				line->line_seqno;
92362306a36Sopenharmony_ci		line->line_seqno = line->last_seqno - line->total_discard_seq;
92462306a36Sopenharmony_ci		le.line_seqno = line->line_seqno;
92562306a36Sopenharmony_ci		le.seqno = (lr->num_lines == 1) ?
92662306a36Sopenharmony_ci			le.line_seqno : atomic_add_return(diff_seqno, &lr->seqno);
92762306a36Sopenharmony_ci	} else
92862306a36Sopenharmony_ci#endif /* CONFIG_HTE */
92962306a36Sopenharmony_ci	{
93062306a36Sopenharmony_ci		line->line_seqno++;
93162306a36Sopenharmony_ci		le.line_seqno = line->line_seqno;
93262306a36Sopenharmony_ci		le.seqno = (lr->num_lines == 1) ?
93362306a36Sopenharmony_ci			le.line_seqno : atomic_inc_return(&lr->seqno);
93462306a36Sopenharmony_ci	}
93562306a36Sopenharmony_ci
93662306a36Sopenharmony_ci	le.id = line_event_id(level);
93762306a36Sopenharmony_ci
93862306a36Sopenharmony_ci	linereq_put_event(lr, &le);
93962306a36Sopenharmony_ci}
94062306a36Sopenharmony_ci
94162306a36Sopenharmony_cistatic int debounce_setup(struct line *line, unsigned int debounce_period_us)
94262306a36Sopenharmony_ci{
94362306a36Sopenharmony_ci	unsigned long irqflags;
94462306a36Sopenharmony_ci	int ret, level, irq;
94562306a36Sopenharmony_ci
94662306a36Sopenharmony_ci	/* try hardware */
94762306a36Sopenharmony_ci	ret = gpiod_set_debounce(line->desc, debounce_period_us);
94862306a36Sopenharmony_ci	if (!ret) {
94962306a36Sopenharmony_ci		WRITE_ONCE(line->desc->debounce_period_us, debounce_period_us);
95062306a36Sopenharmony_ci		return ret;
95162306a36Sopenharmony_ci	}
95262306a36Sopenharmony_ci	if (ret != -ENOTSUPP)
95362306a36Sopenharmony_ci		return ret;
95462306a36Sopenharmony_ci
95562306a36Sopenharmony_ci	if (debounce_period_us) {
95662306a36Sopenharmony_ci		/* setup software debounce */
95762306a36Sopenharmony_ci		level = gpiod_get_raw_value_cansleep(line->desc);
95862306a36Sopenharmony_ci		if (level < 0)
95962306a36Sopenharmony_ci			return level;
96062306a36Sopenharmony_ci
96162306a36Sopenharmony_ci		if (!(IS_ENABLED(CONFIG_HTE) &&
96262306a36Sopenharmony_ci		      test_bit(FLAG_EVENT_CLOCK_HTE, &line->desc->flags))) {
96362306a36Sopenharmony_ci			irq = gpiod_to_irq(line->desc);
96462306a36Sopenharmony_ci			if (irq < 0)
96562306a36Sopenharmony_ci				return -ENXIO;
96662306a36Sopenharmony_ci
96762306a36Sopenharmony_ci			irqflags = IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING;
96862306a36Sopenharmony_ci			ret = request_irq(irq, debounce_irq_handler, irqflags,
96962306a36Sopenharmony_ci					  line->req->label, line);
97062306a36Sopenharmony_ci			if (ret)
97162306a36Sopenharmony_ci				return ret;
97262306a36Sopenharmony_ci			line->irq = irq;
97362306a36Sopenharmony_ci		} else {
97462306a36Sopenharmony_ci			ret = hte_edge_setup(line, GPIO_V2_LINE_FLAG_EDGE_BOTH);
97562306a36Sopenharmony_ci			if (ret)
97662306a36Sopenharmony_ci				return ret;
97762306a36Sopenharmony_ci		}
97862306a36Sopenharmony_ci
97962306a36Sopenharmony_ci		WRITE_ONCE(line->level, level);
98062306a36Sopenharmony_ci		WRITE_ONCE(line->sw_debounced, 1);
98162306a36Sopenharmony_ci	}
98262306a36Sopenharmony_ci	return 0;
98362306a36Sopenharmony_ci}
98462306a36Sopenharmony_ci
98562306a36Sopenharmony_cistatic bool gpio_v2_line_config_debounced(struct gpio_v2_line_config *lc,
98662306a36Sopenharmony_ci					  unsigned int line_idx)
98762306a36Sopenharmony_ci{
98862306a36Sopenharmony_ci	unsigned int i;
98962306a36Sopenharmony_ci	u64 mask = BIT_ULL(line_idx);
99062306a36Sopenharmony_ci
99162306a36Sopenharmony_ci	for (i = 0; i < lc->num_attrs; i++) {
99262306a36Sopenharmony_ci		if ((lc->attrs[i].attr.id == GPIO_V2_LINE_ATTR_ID_DEBOUNCE) &&
99362306a36Sopenharmony_ci		    (lc->attrs[i].mask & mask))
99462306a36Sopenharmony_ci			return true;
99562306a36Sopenharmony_ci	}
99662306a36Sopenharmony_ci	return false;
99762306a36Sopenharmony_ci}
99862306a36Sopenharmony_ci
99962306a36Sopenharmony_cistatic u32 gpio_v2_line_config_debounce_period(struct gpio_v2_line_config *lc,
100062306a36Sopenharmony_ci					       unsigned int line_idx)
100162306a36Sopenharmony_ci{
100262306a36Sopenharmony_ci	unsigned int i;
100362306a36Sopenharmony_ci	u64 mask = BIT_ULL(line_idx);
100462306a36Sopenharmony_ci
100562306a36Sopenharmony_ci	for (i = 0; i < lc->num_attrs; i++) {
100662306a36Sopenharmony_ci		if ((lc->attrs[i].attr.id == GPIO_V2_LINE_ATTR_ID_DEBOUNCE) &&
100762306a36Sopenharmony_ci		    (lc->attrs[i].mask & mask))
100862306a36Sopenharmony_ci			return lc->attrs[i].attr.debounce_period_us;
100962306a36Sopenharmony_ci	}
101062306a36Sopenharmony_ci	return 0;
101162306a36Sopenharmony_ci}
101262306a36Sopenharmony_ci
101362306a36Sopenharmony_cistatic void edge_detector_stop(struct line *line)
101462306a36Sopenharmony_ci{
101562306a36Sopenharmony_ci	if (line->irq) {
101662306a36Sopenharmony_ci		free_irq(line->irq, line);
101762306a36Sopenharmony_ci		line->irq = 0;
101862306a36Sopenharmony_ci	}
101962306a36Sopenharmony_ci
102062306a36Sopenharmony_ci#ifdef CONFIG_HTE
102162306a36Sopenharmony_ci	if (READ_ONCE(line->edflags) & GPIO_V2_LINE_FLAG_EVENT_CLOCK_HTE)
102262306a36Sopenharmony_ci		hte_ts_put(&line->hdesc);
102362306a36Sopenharmony_ci#endif
102462306a36Sopenharmony_ci
102562306a36Sopenharmony_ci	cancel_delayed_work_sync(&line->work);
102662306a36Sopenharmony_ci	WRITE_ONCE(line->sw_debounced, 0);
102762306a36Sopenharmony_ci	WRITE_ONCE(line->edflags, 0);
102862306a36Sopenharmony_ci	if (line->desc)
102962306a36Sopenharmony_ci		WRITE_ONCE(line->desc->debounce_period_us, 0);
103062306a36Sopenharmony_ci	/* do not change line->level - see comment in debounced_value() */
103162306a36Sopenharmony_ci}
103262306a36Sopenharmony_ci
103362306a36Sopenharmony_cistatic int edge_detector_setup(struct line *line,
103462306a36Sopenharmony_ci			       struct gpio_v2_line_config *lc,
103562306a36Sopenharmony_ci			       unsigned int line_idx, u64 edflags)
103662306a36Sopenharmony_ci{
103762306a36Sopenharmony_ci	u32 debounce_period_us;
103862306a36Sopenharmony_ci	unsigned long irqflags = 0;
103962306a36Sopenharmony_ci	u64 eflags;
104062306a36Sopenharmony_ci	int irq, ret;
104162306a36Sopenharmony_ci
104262306a36Sopenharmony_ci	eflags = edflags & GPIO_V2_LINE_EDGE_FLAGS;
104362306a36Sopenharmony_ci	if (eflags && !kfifo_initialized(&line->req->events)) {
104462306a36Sopenharmony_ci		ret = kfifo_alloc(&line->req->events,
104562306a36Sopenharmony_ci				  line->req->event_buffer_size, GFP_KERNEL);
104662306a36Sopenharmony_ci		if (ret)
104762306a36Sopenharmony_ci			return ret;
104862306a36Sopenharmony_ci	}
104962306a36Sopenharmony_ci	if (gpio_v2_line_config_debounced(lc, line_idx)) {
105062306a36Sopenharmony_ci		debounce_period_us = gpio_v2_line_config_debounce_period(lc, line_idx);
105162306a36Sopenharmony_ci		ret = debounce_setup(line, debounce_period_us);
105262306a36Sopenharmony_ci		if (ret)
105362306a36Sopenharmony_ci			return ret;
105462306a36Sopenharmony_ci		WRITE_ONCE(line->desc->debounce_period_us, debounce_period_us);
105562306a36Sopenharmony_ci	}
105662306a36Sopenharmony_ci
105762306a36Sopenharmony_ci	/* detection disabled or sw debouncer will provide edge detection */
105862306a36Sopenharmony_ci	if (!eflags || READ_ONCE(line->sw_debounced))
105962306a36Sopenharmony_ci		return 0;
106062306a36Sopenharmony_ci
106162306a36Sopenharmony_ci	if (IS_ENABLED(CONFIG_HTE) &&
106262306a36Sopenharmony_ci	    (edflags & GPIO_V2_LINE_FLAG_EVENT_CLOCK_HTE))
106362306a36Sopenharmony_ci		return hte_edge_setup(line, edflags);
106462306a36Sopenharmony_ci
106562306a36Sopenharmony_ci	irq = gpiod_to_irq(line->desc);
106662306a36Sopenharmony_ci	if (irq < 0)
106762306a36Sopenharmony_ci		return -ENXIO;
106862306a36Sopenharmony_ci
106962306a36Sopenharmony_ci	if (eflags & GPIO_V2_LINE_FLAG_EDGE_RISING)
107062306a36Sopenharmony_ci		irqflags |= test_bit(FLAG_ACTIVE_LOW, &line->desc->flags) ?
107162306a36Sopenharmony_ci			IRQF_TRIGGER_FALLING : IRQF_TRIGGER_RISING;
107262306a36Sopenharmony_ci	if (eflags & GPIO_V2_LINE_FLAG_EDGE_FALLING)
107362306a36Sopenharmony_ci		irqflags |= test_bit(FLAG_ACTIVE_LOW, &line->desc->flags) ?
107462306a36Sopenharmony_ci			IRQF_TRIGGER_RISING : IRQF_TRIGGER_FALLING;
107562306a36Sopenharmony_ci	irqflags |= IRQF_ONESHOT;
107662306a36Sopenharmony_ci
107762306a36Sopenharmony_ci	/* Request a thread to read the events */
107862306a36Sopenharmony_ci	ret = request_threaded_irq(irq, edge_irq_handler, edge_irq_thread,
107962306a36Sopenharmony_ci				   irqflags, line->req->label, line);
108062306a36Sopenharmony_ci	if (ret)
108162306a36Sopenharmony_ci		return ret;
108262306a36Sopenharmony_ci
108362306a36Sopenharmony_ci	line->irq = irq;
108462306a36Sopenharmony_ci	return 0;
108562306a36Sopenharmony_ci}
108662306a36Sopenharmony_ci
108762306a36Sopenharmony_cistatic int edge_detector_update(struct line *line,
108862306a36Sopenharmony_ci				struct gpio_v2_line_config *lc,
108962306a36Sopenharmony_ci				unsigned int line_idx, u64 edflags)
109062306a36Sopenharmony_ci{
109162306a36Sopenharmony_ci	u64 active_edflags = READ_ONCE(line->edflags);
109262306a36Sopenharmony_ci	unsigned int debounce_period_us =
109362306a36Sopenharmony_ci			gpio_v2_line_config_debounce_period(lc, line_idx);
109462306a36Sopenharmony_ci
109562306a36Sopenharmony_ci	if ((active_edflags == edflags) &&
109662306a36Sopenharmony_ci	    (READ_ONCE(line->desc->debounce_period_us) == debounce_period_us))
109762306a36Sopenharmony_ci		return 0;
109862306a36Sopenharmony_ci
109962306a36Sopenharmony_ci	/* sw debounced and still will be...*/
110062306a36Sopenharmony_ci	if (debounce_period_us && READ_ONCE(line->sw_debounced)) {
110162306a36Sopenharmony_ci		WRITE_ONCE(line->desc->debounce_period_us, debounce_period_us);
110262306a36Sopenharmony_ci		return 0;
110362306a36Sopenharmony_ci	}
110462306a36Sopenharmony_ci
110562306a36Sopenharmony_ci	/* reconfiguring edge detection or sw debounce being disabled */
110662306a36Sopenharmony_ci	if ((line->irq && !READ_ONCE(line->sw_debounced)) ||
110762306a36Sopenharmony_ci	    (active_edflags & GPIO_V2_LINE_FLAG_EVENT_CLOCK_HTE) ||
110862306a36Sopenharmony_ci	    (!debounce_period_us && READ_ONCE(line->sw_debounced)))
110962306a36Sopenharmony_ci		edge_detector_stop(line);
111062306a36Sopenharmony_ci
111162306a36Sopenharmony_ci	return edge_detector_setup(line, lc, line_idx, edflags);
111262306a36Sopenharmony_ci}
111362306a36Sopenharmony_ci
111462306a36Sopenharmony_cistatic u64 gpio_v2_line_config_flags(struct gpio_v2_line_config *lc,
111562306a36Sopenharmony_ci				     unsigned int line_idx)
111662306a36Sopenharmony_ci{
111762306a36Sopenharmony_ci	unsigned int i;
111862306a36Sopenharmony_ci	u64 mask = BIT_ULL(line_idx);
111962306a36Sopenharmony_ci
112062306a36Sopenharmony_ci	for (i = 0; i < lc->num_attrs; i++) {
112162306a36Sopenharmony_ci		if ((lc->attrs[i].attr.id == GPIO_V2_LINE_ATTR_ID_FLAGS) &&
112262306a36Sopenharmony_ci		    (lc->attrs[i].mask & mask))
112362306a36Sopenharmony_ci			return lc->attrs[i].attr.flags;
112462306a36Sopenharmony_ci	}
112562306a36Sopenharmony_ci	return lc->flags;
112662306a36Sopenharmony_ci}
112762306a36Sopenharmony_ci
112862306a36Sopenharmony_cistatic int gpio_v2_line_config_output_value(struct gpio_v2_line_config *lc,
112962306a36Sopenharmony_ci					    unsigned int line_idx)
113062306a36Sopenharmony_ci{
113162306a36Sopenharmony_ci	unsigned int i;
113262306a36Sopenharmony_ci	u64 mask = BIT_ULL(line_idx);
113362306a36Sopenharmony_ci
113462306a36Sopenharmony_ci	for (i = 0; i < lc->num_attrs; i++) {
113562306a36Sopenharmony_ci		if ((lc->attrs[i].attr.id == GPIO_V2_LINE_ATTR_ID_OUTPUT_VALUES) &&
113662306a36Sopenharmony_ci		    (lc->attrs[i].mask & mask))
113762306a36Sopenharmony_ci			return !!(lc->attrs[i].attr.values & mask);
113862306a36Sopenharmony_ci	}
113962306a36Sopenharmony_ci	return 0;
114062306a36Sopenharmony_ci}
114162306a36Sopenharmony_ci
114262306a36Sopenharmony_cistatic int gpio_v2_line_flags_validate(u64 flags)
114362306a36Sopenharmony_ci{
114462306a36Sopenharmony_ci	/* Return an error if an unknown flag is set */
114562306a36Sopenharmony_ci	if (flags & ~GPIO_V2_LINE_VALID_FLAGS)
114662306a36Sopenharmony_ci		return -EINVAL;
114762306a36Sopenharmony_ci
114862306a36Sopenharmony_ci	if (!IS_ENABLED(CONFIG_HTE) &&
114962306a36Sopenharmony_ci	    (flags & GPIO_V2_LINE_FLAG_EVENT_CLOCK_HTE))
115062306a36Sopenharmony_ci		return -EOPNOTSUPP;
115162306a36Sopenharmony_ci
115262306a36Sopenharmony_ci	/*
115362306a36Sopenharmony_ci	 * Do not allow both INPUT and OUTPUT flags to be set as they are
115462306a36Sopenharmony_ci	 * contradictory.
115562306a36Sopenharmony_ci	 */
115662306a36Sopenharmony_ci	if ((flags & GPIO_V2_LINE_FLAG_INPUT) &&
115762306a36Sopenharmony_ci	    (flags & GPIO_V2_LINE_FLAG_OUTPUT))
115862306a36Sopenharmony_ci		return -EINVAL;
115962306a36Sopenharmony_ci
116062306a36Sopenharmony_ci	/* Only allow one event clock source */
116162306a36Sopenharmony_ci	if (IS_ENABLED(CONFIG_HTE) &&
116262306a36Sopenharmony_ci	    (flags & GPIO_V2_LINE_FLAG_EVENT_CLOCK_REALTIME) &&
116362306a36Sopenharmony_ci	    (flags & GPIO_V2_LINE_FLAG_EVENT_CLOCK_HTE))
116462306a36Sopenharmony_ci		return -EINVAL;
116562306a36Sopenharmony_ci
116662306a36Sopenharmony_ci	/* Edge detection requires explicit input. */
116762306a36Sopenharmony_ci	if ((flags & GPIO_V2_LINE_EDGE_FLAGS) &&
116862306a36Sopenharmony_ci	    !(flags & GPIO_V2_LINE_FLAG_INPUT))
116962306a36Sopenharmony_ci		return -EINVAL;
117062306a36Sopenharmony_ci
117162306a36Sopenharmony_ci	/*
117262306a36Sopenharmony_ci	 * Do not allow OPEN_SOURCE and OPEN_DRAIN flags in a single
117362306a36Sopenharmony_ci	 * request. If the hardware actually supports enabling both at the
117462306a36Sopenharmony_ci	 * same time the electrical result would be disastrous.
117562306a36Sopenharmony_ci	 */
117662306a36Sopenharmony_ci	if ((flags & GPIO_V2_LINE_FLAG_OPEN_DRAIN) &&
117762306a36Sopenharmony_ci	    (flags & GPIO_V2_LINE_FLAG_OPEN_SOURCE))
117862306a36Sopenharmony_ci		return -EINVAL;
117962306a36Sopenharmony_ci
118062306a36Sopenharmony_ci	/* Drive requires explicit output direction. */
118162306a36Sopenharmony_ci	if ((flags & GPIO_V2_LINE_DRIVE_FLAGS) &&
118262306a36Sopenharmony_ci	    !(flags & GPIO_V2_LINE_FLAG_OUTPUT))
118362306a36Sopenharmony_ci		return -EINVAL;
118462306a36Sopenharmony_ci
118562306a36Sopenharmony_ci	/* Bias requires explicit direction. */
118662306a36Sopenharmony_ci	if ((flags & GPIO_V2_LINE_BIAS_FLAGS) &&
118762306a36Sopenharmony_ci	    !(flags & GPIO_V2_LINE_DIRECTION_FLAGS))
118862306a36Sopenharmony_ci		return -EINVAL;
118962306a36Sopenharmony_ci
119062306a36Sopenharmony_ci	/* Only one bias flag can be set. */
119162306a36Sopenharmony_ci	if (((flags & GPIO_V2_LINE_FLAG_BIAS_DISABLED) &&
119262306a36Sopenharmony_ci	     (flags & (GPIO_V2_LINE_FLAG_BIAS_PULL_DOWN |
119362306a36Sopenharmony_ci		       GPIO_V2_LINE_FLAG_BIAS_PULL_UP))) ||
119462306a36Sopenharmony_ci	    ((flags & GPIO_V2_LINE_FLAG_BIAS_PULL_DOWN) &&
119562306a36Sopenharmony_ci	     (flags & GPIO_V2_LINE_FLAG_BIAS_PULL_UP)))
119662306a36Sopenharmony_ci		return -EINVAL;
119762306a36Sopenharmony_ci
119862306a36Sopenharmony_ci	return 0;
119962306a36Sopenharmony_ci}
120062306a36Sopenharmony_ci
120162306a36Sopenharmony_cistatic int gpio_v2_line_config_validate(struct gpio_v2_line_config *lc,
120262306a36Sopenharmony_ci					unsigned int num_lines)
120362306a36Sopenharmony_ci{
120462306a36Sopenharmony_ci	unsigned int i;
120562306a36Sopenharmony_ci	u64 flags;
120662306a36Sopenharmony_ci	int ret;
120762306a36Sopenharmony_ci
120862306a36Sopenharmony_ci	if (lc->num_attrs > GPIO_V2_LINE_NUM_ATTRS_MAX)
120962306a36Sopenharmony_ci		return -EINVAL;
121062306a36Sopenharmony_ci
121162306a36Sopenharmony_ci	if (memchr_inv(lc->padding, 0, sizeof(lc->padding)))
121262306a36Sopenharmony_ci		return -EINVAL;
121362306a36Sopenharmony_ci
121462306a36Sopenharmony_ci	for (i = 0; i < num_lines; i++) {
121562306a36Sopenharmony_ci		flags = gpio_v2_line_config_flags(lc, i);
121662306a36Sopenharmony_ci		ret = gpio_v2_line_flags_validate(flags);
121762306a36Sopenharmony_ci		if (ret)
121862306a36Sopenharmony_ci			return ret;
121962306a36Sopenharmony_ci
122062306a36Sopenharmony_ci		/* debounce requires explicit input */
122162306a36Sopenharmony_ci		if (gpio_v2_line_config_debounced(lc, i) &&
122262306a36Sopenharmony_ci		    !(flags & GPIO_V2_LINE_FLAG_INPUT))
122362306a36Sopenharmony_ci			return -EINVAL;
122462306a36Sopenharmony_ci	}
122562306a36Sopenharmony_ci	return 0;
122662306a36Sopenharmony_ci}
122762306a36Sopenharmony_ci
122862306a36Sopenharmony_cistatic void gpio_v2_line_config_flags_to_desc_flags(u64 flags,
122962306a36Sopenharmony_ci						    unsigned long *flagsp)
123062306a36Sopenharmony_ci{
123162306a36Sopenharmony_ci	assign_bit(FLAG_ACTIVE_LOW, flagsp,
123262306a36Sopenharmony_ci		   flags & GPIO_V2_LINE_FLAG_ACTIVE_LOW);
123362306a36Sopenharmony_ci
123462306a36Sopenharmony_ci	if (flags & GPIO_V2_LINE_FLAG_OUTPUT)
123562306a36Sopenharmony_ci		set_bit(FLAG_IS_OUT, flagsp);
123662306a36Sopenharmony_ci	else if (flags & GPIO_V2_LINE_FLAG_INPUT)
123762306a36Sopenharmony_ci		clear_bit(FLAG_IS_OUT, flagsp);
123862306a36Sopenharmony_ci
123962306a36Sopenharmony_ci	assign_bit(FLAG_EDGE_RISING, flagsp,
124062306a36Sopenharmony_ci		   flags & GPIO_V2_LINE_FLAG_EDGE_RISING);
124162306a36Sopenharmony_ci	assign_bit(FLAG_EDGE_FALLING, flagsp,
124262306a36Sopenharmony_ci		   flags & GPIO_V2_LINE_FLAG_EDGE_FALLING);
124362306a36Sopenharmony_ci
124462306a36Sopenharmony_ci	assign_bit(FLAG_OPEN_DRAIN, flagsp,
124562306a36Sopenharmony_ci		   flags & GPIO_V2_LINE_FLAG_OPEN_DRAIN);
124662306a36Sopenharmony_ci	assign_bit(FLAG_OPEN_SOURCE, flagsp,
124762306a36Sopenharmony_ci		   flags & GPIO_V2_LINE_FLAG_OPEN_SOURCE);
124862306a36Sopenharmony_ci
124962306a36Sopenharmony_ci	assign_bit(FLAG_PULL_UP, flagsp,
125062306a36Sopenharmony_ci		   flags & GPIO_V2_LINE_FLAG_BIAS_PULL_UP);
125162306a36Sopenharmony_ci	assign_bit(FLAG_PULL_DOWN, flagsp,
125262306a36Sopenharmony_ci		   flags & GPIO_V2_LINE_FLAG_BIAS_PULL_DOWN);
125362306a36Sopenharmony_ci	assign_bit(FLAG_BIAS_DISABLE, flagsp,
125462306a36Sopenharmony_ci		   flags & GPIO_V2_LINE_FLAG_BIAS_DISABLED);
125562306a36Sopenharmony_ci
125662306a36Sopenharmony_ci	assign_bit(FLAG_EVENT_CLOCK_REALTIME, flagsp,
125762306a36Sopenharmony_ci		   flags & GPIO_V2_LINE_FLAG_EVENT_CLOCK_REALTIME);
125862306a36Sopenharmony_ci	assign_bit(FLAG_EVENT_CLOCK_HTE, flagsp,
125962306a36Sopenharmony_ci		   flags & GPIO_V2_LINE_FLAG_EVENT_CLOCK_HTE);
126062306a36Sopenharmony_ci}
126162306a36Sopenharmony_ci
126262306a36Sopenharmony_cistatic long linereq_get_values(struct linereq *lr, void __user *ip)
126362306a36Sopenharmony_ci{
126462306a36Sopenharmony_ci	struct gpio_v2_line_values lv;
126562306a36Sopenharmony_ci	DECLARE_BITMAP(vals, GPIO_V2_LINES_MAX);
126662306a36Sopenharmony_ci	struct gpio_desc **descs;
126762306a36Sopenharmony_ci	unsigned int i, didx, num_get;
126862306a36Sopenharmony_ci	bool val;
126962306a36Sopenharmony_ci	int ret;
127062306a36Sopenharmony_ci
127162306a36Sopenharmony_ci	/* NOTE: It's ok to read values of output lines. */
127262306a36Sopenharmony_ci	if (copy_from_user(&lv, ip, sizeof(lv)))
127362306a36Sopenharmony_ci		return -EFAULT;
127462306a36Sopenharmony_ci
127562306a36Sopenharmony_ci	for (num_get = 0, i = 0; i < lr->num_lines; i++) {
127662306a36Sopenharmony_ci		if (lv.mask & BIT_ULL(i)) {
127762306a36Sopenharmony_ci			num_get++;
127862306a36Sopenharmony_ci			descs = &lr->lines[i].desc;
127962306a36Sopenharmony_ci		}
128062306a36Sopenharmony_ci	}
128162306a36Sopenharmony_ci
128262306a36Sopenharmony_ci	if (num_get == 0)
128362306a36Sopenharmony_ci		return -EINVAL;
128462306a36Sopenharmony_ci
128562306a36Sopenharmony_ci	if (num_get != 1) {
128662306a36Sopenharmony_ci		descs = kmalloc_array(num_get, sizeof(*descs), GFP_KERNEL);
128762306a36Sopenharmony_ci		if (!descs)
128862306a36Sopenharmony_ci			return -ENOMEM;
128962306a36Sopenharmony_ci		for (didx = 0, i = 0; i < lr->num_lines; i++) {
129062306a36Sopenharmony_ci			if (lv.mask & BIT_ULL(i)) {
129162306a36Sopenharmony_ci				descs[didx] = lr->lines[i].desc;
129262306a36Sopenharmony_ci				didx++;
129362306a36Sopenharmony_ci			}
129462306a36Sopenharmony_ci		}
129562306a36Sopenharmony_ci	}
129662306a36Sopenharmony_ci	ret = gpiod_get_array_value_complex(false, true, num_get,
129762306a36Sopenharmony_ci					    descs, NULL, vals);
129862306a36Sopenharmony_ci
129962306a36Sopenharmony_ci	if (num_get != 1)
130062306a36Sopenharmony_ci		kfree(descs);
130162306a36Sopenharmony_ci	if (ret)
130262306a36Sopenharmony_ci		return ret;
130362306a36Sopenharmony_ci
130462306a36Sopenharmony_ci	lv.bits = 0;
130562306a36Sopenharmony_ci	for (didx = 0, i = 0; i < lr->num_lines; i++) {
130662306a36Sopenharmony_ci		if (lv.mask & BIT_ULL(i)) {
130762306a36Sopenharmony_ci			if (lr->lines[i].sw_debounced)
130862306a36Sopenharmony_ci				val = debounced_value(&lr->lines[i]);
130962306a36Sopenharmony_ci			else
131062306a36Sopenharmony_ci				val = test_bit(didx, vals);
131162306a36Sopenharmony_ci			if (val)
131262306a36Sopenharmony_ci				lv.bits |= BIT_ULL(i);
131362306a36Sopenharmony_ci			didx++;
131462306a36Sopenharmony_ci		}
131562306a36Sopenharmony_ci	}
131662306a36Sopenharmony_ci
131762306a36Sopenharmony_ci	if (copy_to_user(ip, &lv, sizeof(lv)))
131862306a36Sopenharmony_ci		return -EFAULT;
131962306a36Sopenharmony_ci
132062306a36Sopenharmony_ci	return 0;
132162306a36Sopenharmony_ci}
132262306a36Sopenharmony_ci
132362306a36Sopenharmony_cistatic long linereq_set_values_unlocked(struct linereq *lr,
132462306a36Sopenharmony_ci					struct gpio_v2_line_values *lv)
132562306a36Sopenharmony_ci{
132662306a36Sopenharmony_ci	DECLARE_BITMAP(vals, GPIO_V2_LINES_MAX);
132762306a36Sopenharmony_ci	struct gpio_desc **descs;
132862306a36Sopenharmony_ci	unsigned int i, didx, num_set;
132962306a36Sopenharmony_ci	int ret;
133062306a36Sopenharmony_ci
133162306a36Sopenharmony_ci	bitmap_zero(vals, GPIO_V2_LINES_MAX);
133262306a36Sopenharmony_ci	for (num_set = 0, i = 0; i < lr->num_lines; i++) {
133362306a36Sopenharmony_ci		if (lv->mask & BIT_ULL(i)) {
133462306a36Sopenharmony_ci			if (!test_bit(FLAG_IS_OUT, &lr->lines[i].desc->flags))
133562306a36Sopenharmony_ci				return -EPERM;
133662306a36Sopenharmony_ci			if (lv->bits & BIT_ULL(i))
133762306a36Sopenharmony_ci				__set_bit(num_set, vals);
133862306a36Sopenharmony_ci			num_set++;
133962306a36Sopenharmony_ci			descs = &lr->lines[i].desc;
134062306a36Sopenharmony_ci		}
134162306a36Sopenharmony_ci	}
134262306a36Sopenharmony_ci	if (num_set == 0)
134362306a36Sopenharmony_ci		return -EINVAL;
134462306a36Sopenharmony_ci
134562306a36Sopenharmony_ci	if (num_set != 1) {
134662306a36Sopenharmony_ci		/* build compacted desc array and values */
134762306a36Sopenharmony_ci		descs = kmalloc_array(num_set, sizeof(*descs), GFP_KERNEL);
134862306a36Sopenharmony_ci		if (!descs)
134962306a36Sopenharmony_ci			return -ENOMEM;
135062306a36Sopenharmony_ci		for (didx = 0, i = 0; i < lr->num_lines; i++) {
135162306a36Sopenharmony_ci			if (lv->mask & BIT_ULL(i)) {
135262306a36Sopenharmony_ci				descs[didx] = lr->lines[i].desc;
135362306a36Sopenharmony_ci				didx++;
135462306a36Sopenharmony_ci			}
135562306a36Sopenharmony_ci		}
135662306a36Sopenharmony_ci	}
135762306a36Sopenharmony_ci	ret = gpiod_set_array_value_complex(false, true, num_set,
135862306a36Sopenharmony_ci					    descs, NULL, vals);
135962306a36Sopenharmony_ci
136062306a36Sopenharmony_ci	if (num_set != 1)
136162306a36Sopenharmony_ci		kfree(descs);
136262306a36Sopenharmony_ci	return ret;
136362306a36Sopenharmony_ci}
136462306a36Sopenharmony_ci
136562306a36Sopenharmony_cistatic long linereq_set_values(struct linereq *lr, void __user *ip)
136662306a36Sopenharmony_ci{
136762306a36Sopenharmony_ci	struct gpio_v2_line_values lv;
136862306a36Sopenharmony_ci	int ret;
136962306a36Sopenharmony_ci
137062306a36Sopenharmony_ci	if (copy_from_user(&lv, ip, sizeof(lv)))
137162306a36Sopenharmony_ci		return -EFAULT;
137262306a36Sopenharmony_ci
137362306a36Sopenharmony_ci	mutex_lock(&lr->config_mutex);
137462306a36Sopenharmony_ci
137562306a36Sopenharmony_ci	ret = linereq_set_values_unlocked(lr, &lv);
137662306a36Sopenharmony_ci
137762306a36Sopenharmony_ci	mutex_unlock(&lr->config_mutex);
137862306a36Sopenharmony_ci
137962306a36Sopenharmony_ci	return ret;
138062306a36Sopenharmony_ci}
138162306a36Sopenharmony_ci
138262306a36Sopenharmony_cistatic long linereq_set_config_unlocked(struct linereq *lr,
138362306a36Sopenharmony_ci					struct gpio_v2_line_config *lc)
138462306a36Sopenharmony_ci{
138562306a36Sopenharmony_ci	struct gpio_desc *desc;
138662306a36Sopenharmony_ci	struct line *line;
138762306a36Sopenharmony_ci	unsigned int i;
138862306a36Sopenharmony_ci	u64 flags, edflags;
138962306a36Sopenharmony_ci	int ret;
139062306a36Sopenharmony_ci
139162306a36Sopenharmony_ci	for (i = 0; i < lr->num_lines; i++) {
139262306a36Sopenharmony_ci		line = &lr->lines[i];
139362306a36Sopenharmony_ci		desc = lr->lines[i].desc;
139462306a36Sopenharmony_ci		flags = gpio_v2_line_config_flags(lc, i);
139562306a36Sopenharmony_ci		gpio_v2_line_config_flags_to_desc_flags(flags, &desc->flags);
139662306a36Sopenharmony_ci		edflags = flags & GPIO_V2_LINE_EDGE_DETECTOR_FLAGS;
139762306a36Sopenharmony_ci		/*
139862306a36Sopenharmony_ci		 * Lines have to be requested explicitly for input
139962306a36Sopenharmony_ci		 * or output, else the line will be treated "as is".
140062306a36Sopenharmony_ci		 */
140162306a36Sopenharmony_ci		if (flags & GPIO_V2_LINE_FLAG_OUTPUT) {
140262306a36Sopenharmony_ci			int val = gpio_v2_line_config_output_value(lc, i);
140362306a36Sopenharmony_ci
140462306a36Sopenharmony_ci			edge_detector_stop(line);
140562306a36Sopenharmony_ci			ret = gpiod_direction_output(desc, val);
140662306a36Sopenharmony_ci			if (ret)
140762306a36Sopenharmony_ci				return ret;
140862306a36Sopenharmony_ci		} else if (flags & GPIO_V2_LINE_FLAG_INPUT) {
140962306a36Sopenharmony_ci			ret = gpiod_direction_input(desc);
141062306a36Sopenharmony_ci			if (ret)
141162306a36Sopenharmony_ci				return ret;
141262306a36Sopenharmony_ci
141362306a36Sopenharmony_ci			ret = edge_detector_update(line, lc, i, edflags);
141462306a36Sopenharmony_ci			if (ret)
141562306a36Sopenharmony_ci				return ret;
141662306a36Sopenharmony_ci		}
141762306a36Sopenharmony_ci
141862306a36Sopenharmony_ci		WRITE_ONCE(line->edflags, edflags);
141962306a36Sopenharmony_ci
142062306a36Sopenharmony_ci		gpiod_line_state_notify(desc, GPIO_V2_LINE_CHANGED_CONFIG);
142162306a36Sopenharmony_ci	}
142262306a36Sopenharmony_ci	return 0;
142362306a36Sopenharmony_ci}
142462306a36Sopenharmony_ci
142562306a36Sopenharmony_cistatic long linereq_set_config(struct linereq *lr, void __user *ip)
142662306a36Sopenharmony_ci{
142762306a36Sopenharmony_ci	struct gpio_v2_line_config lc;
142862306a36Sopenharmony_ci	int ret;
142962306a36Sopenharmony_ci
143062306a36Sopenharmony_ci	if (copy_from_user(&lc, ip, sizeof(lc)))
143162306a36Sopenharmony_ci		return -EFAULT;
143262306a36Sopenharmony_ci
143362306a36Sopenharmony_ci	ret = gpio_v2_line_config_validate(&lc, lr->num_lines);
143462306a36Sopenharmony_ci	if (ret)
143562306a36Sopenharmony_ci		return ret;
143662306a36Sopenharmony_ci
143762306a36Sopenharmony_ci	mutex_lock(&lr->config_mutex);
143862306a36Sopenharmony_ci
143962306a36Sopenharmony_ci	ret = linereq_set_config_unlocked(lr, &lc);
144062306a36Sopenharmony_ci
144162306a36Sopenharmony_ci	mutex_unlock(&lr->config_mutex);
144262306a36Sopenharmony_ci
144362306a36Sopenharmony_ci	return ret;
144462306a36Sopenharmony_ci}
144562306a36Sopenharmony_ci
144662306a36Sopenharmony_cistatic long linereq_ioctl_unlocked(struct file *file, unsigned int cmd,
144762306a36Sopenharmony_ci				   unsigned long arg)
144862306a36Sopenharmony_ci{
144962306a36Sopenharmony_ci	struct linereq *lr = file->private_data;
145062306a36Sopenharmony_ci	void __user *ip = (void __user *)arg;
145162306a36Sopenharmony_ci
145262306a36Sopenharmony_ci	if (!lr->gdev->chip)
145362306a36Sopenharmony_ci		return -ENODEV;
145462306a36Sopenharmony_ci
145562306a36Sopenharmony_ci	switch (cmd) {
145662306a36Sopenharmony_ci	case GPIO_V2_LINE_GET_VALUES_IOCTL:
145762306a36Sopenharmony_ci		return linereq_get_values(lr, ip);
145862306a36Sopenharmony_ci	case GPIO_V2_LINE_SET_VALUES_IOCTL:
145962306a36Sopenharmony_ci		return linereq_set_values(lr, ip);
146062306a36Sopenharmony_ci	case GPIO_V2_LINE_SET_CONFIG_IOCTL:
146162306a36Sopenharmony_ci		return linereq_set_config(lr, ip);
146262306a36Sopenharmony_ci	default:
146362306a36Sopenharmony_ci		return -EINVAL;
146462306a36Sopenharmony_ci	}
146562306a36Sopenharmony_ci}
146662306a36Sopenharmony_ci
146762306a36Sopenharmony_cistatic long linereq_ioctl(struct file *file, unsigned int cmd,
146862306a36Sopenharmony_ci			  unsigned long arg)
146962306a36Sopenharmony_ci{
147062306a36Sopenharmony_ci	struct linereq *lr = file->private_data;
147162306a36Sopenharmony_ci
147262306a36Sopenharmony_ci	return call_ioctl_locked(file, cmd, arg, lr->gdev,
147362306a36Sopenharmony_ci				 linereq_ioctl_unlocked);
147462306a36Sopenharmony_ci}
147562306a36Sopenharmony_ci
147662306a36Sopenharmony_ci#ifdef CONFIG_COMPAT
147762306a36Sopenharmony_cistatic long linereq_ioctl_compat(struct file *file, unsigned int cmd,
147862306a36Sopenharmony_ci				 unsigned long arg)
147962306a36Sopenharmony_ci{
148062306a36Sopenharmony_ci	return linereq_ioctl(file, cmd, (unsigned long)compat_ptr(arg));
148162306a36Sopenharmony_ci}
148262306a36Sopenharmony_ci#endif
148362306a36Sopenharmony_ci
148462306a36Sopenharmony_cistatic __poll_t linereq_poll_unlocked(struct file *file,
148562306a36Sopenharmony_ci				      struct poll_table_struct *wait)
148662306a36Sopenharmony_ci{
148762306a36Sopenharmony_ci	struct linereq *lr = file->private_data;
148862306a36Sopenharmony_ci	__poll_t events = 0;
148962306a36Sopenharmony_ci
149062306a36Sopenharmony_ci	if (!lr->gdev->chip)
149162306a36Sopenharmony_ci		return EPOLLHUP | EPOLLERR;
149262306a36Sopenharmony_ci
149362306a36Sopenharmony_ci	poll_wait(file, &lr->wait, wait);
149462306a36Sopenharmony_ci
149562306a36Sopenharmony_ci	if (!kfifo_is_empty_spinlocked_noirqsave(&lr->events,
149662306a36Sopenharmony_ci						 &lr->wait.lock))
149762306a36Sopenharmony_ci		events = EPOLLIN | EPOLLRDNORM;
149862306a36Sopenharmony_ci
149962306a36Sopenharmony_ci	return events;
150062306a36Sopenharmony_ci}
150162306a36Sopenharmony_ci
150262306a36Sopenharmony_cistatic __poll_t linereq_poll(struct file *file,
150362306a36Sopenharmony_ci			     struct poll_table_struct *wait)
150462306a36Sopenharmony_ci{
150562306a36Sopenharmony_ci	struct linereq *lr = file->private_data;
150662306a36Sopenharmony_ci
150762306a36Sopenharmony_ci	return call_poll_locked(file, wait, lr->gdev, linereq_poll_unlocked);
150862306a36Sopenharmony_ci}
150962306a36Sopenharmony_ci
151062306a36Sopenharmony_cistatic ssize_t linereq_read_unlocked(struct file *file, char __user *buf,
151162306a36Sopenharmony_ci				     size_t count, loff_t *f_ps)
151262306a36Sopenharmony_ci{
151362306a36Sopenharmony_ci	struct linereq *lr = file->private_data;
151462306a36Sopenharmony_ci	struct gpio_v2_line_event le;
151562306a36Sopenharmony_ci	ssize_t bytes_read = 0;
151662306a36Sopenharmony_ci	int ret;
151762306a36Sopenharmony_ci
151862306a36Sopenharmony_ci	if (!lr->gdev->chip)
151962306a36Sopenharmony_ci		return -ENODEV;
152062306a36Sopenharmony_ci
152162306a36Sopenharmony_ci	if (count < sizeof(le))
152262306a36Sopenharmony_ci		return -EINVAL;
152362306a36Sopenharmony_ci
152462306a36Sopenharmony_ci	do {
152562306a36Sopenharmony_ci		spin_lock(&lr->wait.lock);
152662306a36Sopenharmony_ci		if (kfifo_is_empty(&lr->events)) {
152762306a36Sopenharmony_ci			if (bytes_read) {
152862306a36Sopenharmony_ci				spin_unlock(&lr->wait.lock);
152962306a36Sopenharmony_ci				return bytes_read;
153062306a36Sopenharmony_ci			}
153162306a36Sopenharmony_ci
153262306a36Sopenharmony_ci			if (file->f_flags & O_NONBLOCK) {
153362306a36Sopenharmony_ci				spin_unlock(&lr->wait.lock);
153462306a36Sopenharmony_ci				return -EAGAIN;
153562306a36Sopenharmony_ci			}
153662306a36Sopenharmony_ci
153762306a36Sopenharmony_ci			ret = wait_event_interruptible_locked(lr->wait,
153862306a36Sopenharmony_ci					!kfifo_is_empty(&lr->events));
153962306a36Sopenharmony_ci			if (ret) {
154062306a36Sopenharmony_ci				spin_unlock(&lr->wait.lock);
154162306a36Sopenharmony_ci				return ret;
154262306a36Sopenharmony_ci			}
154362306a36Sopenharmony_ci		}
154462306a36Sopenharmony_ci
154562306a36Sopenharmony_ci		ret = kfifo_out(&lr->events, &le, 1);
154662306a36Sopenharmony_ci		spin_unlock(&lr->wait.lock);
154762306a36Sopenharmony_ci		if (ret != 1) {
154862306a36Sopenharmony_ci			/*
154962306a36Sopenharmony_ci			 * This should never happen - we were holding the
155062306a36Sopenharmony_ci			 * lock from the moment we learned the fifo is no
155162306a36Sopenharmony_ci			 * longer empty until now.
155262306a36Sopenharmony_ci			 */
155362306a36Sopenharmony_ci			ret = -EIO;
155462306a36Sopenharmony_ci			break;
155562306a36Sopenharmony_ci		}
155662306a36Sopenharmony_ci
155762306a36Sopenharmony_ci		if (copy_to_user(buf + bytes_read, &le, sizeof(le)))
155862306a36Sopenharmony_ci			return -EFAULT;
155962306a36Sopenharmony_ci		bytes_read += sizeof(le);
156062306a36Sopenharmony_ci	} while (count >= bytes_read + sizeof(le));
156162306a36Sopenharmony_ci
156262306a36Sopenharmony_ci	return bytes_read;
156362306a36Sopenharmony_ci}
156462306a36Sopenharmony_ci
156562306a36Sopenharmony_cistatic ssize_t linereq_read(struct file *file, char __user *buf,
156662306a36Sopenharmony_ci			    size_t count, loff_t *f_ps)
156762306a36Sopenharmony_ci{
156862306a36Sopenharmony_ci	struct linereq *lr = file->private_data;
156962306a36Sopenharmony_ci
157062306a36Sopenharmony_ci	return call_read_locked(file, buf, count, f_ps, lr->gdev,
157162306a36Sopenharmony_ci				linereq_read_unlocked);
157262306a36Sopenharmony_ci}
157362306a36Sopenharmony_ci
157462306a36Sopenharmony_cistatic void linereq_free(struct linereq *lr)
157562306a36Sopenharmony_ci{
157662306a36Sopenharmony_ci	unsigned int i;
157762306a36Sopenharmony_ci
157862306a36Sopenharmony_ci	if (lr->device_unregistered_nb.notifier_call)
157962306a36Sopenharmony_ci		blocking_notifier_chain_unregister(&lr->gdev->device_notifier,
158062306a36Sopenharmony_ci						   &lr->device_unregistered_nb);
158162306a36Sopenharmony_ci
158262306a36Sopenharmony_ci	for (i = 0; i < lr->num_lines; i++) {
158362306a36Sopenharmony_ci		if (lr->lines[i].desc) {
158462306a36Sopenharmony_ci			edge_detector_stop(&lr->lines[i]);
158562306a36Sopenharmony_ci			gpiod_free(lr->lines[i].desc);
158662306a36Sopenharmony_ci		}
158762306a36Sopenharmony_ci	}
158862306a36Sopenharmony_ci	kfifo_free(&lr->events);
158962306a36Sopenharmony_ci	kfree(lr->label);
159062306a36Sopenharmony_ci	gpio_device_put(lr->gdev);
159162306a36Sopenharmony_ci	kfree(lr);
159262306a36Sopenharmony_ci}
159362306a36Sopenharmony_ci
159462306a36Sopenharmony_cistatic int linereq_release(struct inode *inode, struct file *file)
159562306a36Sopenharmony_ci{
159662306a36Sopenharmony_ci	struct linereq *lr = file->private_data;
159762306a36Sopenharmony_ci
159862306a36Sopenharmony_ci	linereq_free(lr);
159962306a36Sopenharmony_ci	return 0;
160062306a36Sopenharmony_ci}
160162306a36Sopenharmony_ci
160262306a36Sopenharmony_ci#ifdef CONFIG_PROC_FS
160362306a36Sopenharmony_cistatic void linereq_show_fdinfo(struct seq_file *out, struct file *file)
160462306a36Sopenharmony_ci{
160562306a36Sopenharmony_ci	struct linereq *lr = file->private_data;
160662306a36Sopenharmony_ci	struct device *dev = &lr->gdev->dev;
160762306a36Sopenharmony_ci	u16 i;
160862306a36Sopenharmony_ci
160962306a36Sopenharmony_ci	seq_printf(out, "gpio-chip:\t%s\n", dev_name(dev));
161062306a36Sopenharmony_ci
161162306a36Sopenharmony_ci	for (i = 0; i < lr->num_lines; i++)
161262306a36Sopenharmony_ci		seq_printf(out, "gpio-line:\t%d\n",
161362306a36Sopenharmony_ci			   gpio_chip_hwgpio(lr->lines[i].desc));
161462306a36Sopenharmony_ci}
161562306a36Sopenharmony_ci#endif
161662306a36Sopenharmony_ci
161762306a36Sopenharmony_cistatic const struct file_operations line_fileops = {
161862306a36Sopenharmony_ci	.release = linereq_release,
161962306a36Sopenharmony_ci	.read = linereq_read,
162062306a36Sopenharmony_ci	.poll = linereq_poll,
162162306a36Sopenharmony_ci	.owner = THIS_MODULE,
162262306a36Sopenharmony_ci	.llseek = noop_llseek,
162362306a36Sopenharmony_ci	.unlocked_ioctl = linereq_ioctl,
162462306a36Sopenharmony_ci#ifdef CONFIG_COMPAT
162562306a36Sopenharmony_ci	.compat_ioctl = linereq_ioctl_compat,
162662306a36Sopenharmony_ci#endif
162762306a36Sopenharmony_ci#ifdef CONFIG_PROC_FS
162862306a36Sopenharmony_ci	.show_fdinfo = linereq_show_fdinfo,
162962306a36Sopenharmony_ci#endif
163062306a36Sopenharmony_ci};
163162306a36Sopenharmony_ci
163262306a36Sopenharmony_cistatic int linereq_create(struct gpio_device *gdev, void __user *ip)
163362306a36Sopenharmony_ci{
163462306a36Sopenharmony_ci	struct gpio_v2_line_request ulr;
163562306a36Sopenharmony_ci	struct gpio_v2_line_config *lc;
163662306a36Sopenharmony_ci	struct linereq *lr;
163762306a36Sopenharmony_ci	struct file *file;
163862306a36Sopenharmony_ci	u64 flags, edflags;
163962306a36Sopenharmony_ci	unsigned int i;
164062306a36Sopenharmony_ci	int fd, ret;
164162306a36Sopenharmony_ci
164262306a36Sopenharmony_ci	if (copy_from_user(&ulr, ip, sizeof(ulr)))
164362306a36Sopenharmony_ci		return -EFAULT;
164462306a36Sopenharmony_ci
164562306a36Sopenharmony_ci	if ((ulr.num_lines == 0) || (ulr.num_lines > GPIO_V2_LINES_MAX))
164662306a36Sopenharmony_ci		return -EINVAL;
164762306a36Sopenharmony_ci
164862306a36Sopenharmony_ci	if (memchr_inv(ulr.padding, 0, sizeof(ulr.padding)))
164962306a36Sopenharmony_ci		return -EINVAL;
165062306a36Sopenharmony_ci
165162306a36Sopenharmony_ci	lc = &ulr.config;
165262306a36Sopenharmony_ci	ret = gpio_v2_line_config_validate(lc, ulr.num_lines);
165362306a36Sopenharmony_ci	if (ret)
165462306a36Sopenharmony_ci		return ret;
165562306a36Sopenharmony_ci
165662306a36Sopenharmony_ci	lr = kzalloc(struct_size(lr, lines, ulr.num_lines), GFP_KERNEL);
165762306a36Sopenharmony_ci	if (!lr)
165862306a36Sopenharmony_ci		return -ENOMEM;
165962306a36Sopenharmony_ci
166062306a36Sopenharmony_ci	lr->gdev = gpio_device_get(gdev);
166162306a36Sopenharmony_ci
166262306a36Sopenharmony_ci	for (i = 0; i < ulr.num_lines; i++) {
166362306a36Sopenharmony_ci		lr->lines[i].req = lr;
166462306a36Sopenharmony_ci		WRITE_ONCE(lr->lines[i].sw_debounced, 0);
166562306a36Sopenharmony_ci		INIT_DELAYED_WORK(&lr->lines[i].work, debounce_work_func);
166662306a36Sopenharmony_ci	}
166762306a36Sopenharmony_ci
166862306a36Sopenharmony_ci	if (ulr.consumer[0] != '\0') {
166962306a36Sopenharmony_ci		/* label is only initialized if consumer is set */
167062306a36Sopenharmony_ci		lr->label = kstrndup(ulr.consumer, sizeof(ulr.consumer) - 1,
167162306a36Sopenharmony_ci				     GFP_KERNEL);
167262306a36Sopenharmony_ci		if (!lr->label) {
167362306a36Sopenharmony_ci			ret = -ENOMEM;
167462306a36Sopenharmony_ci			goto out_free_linereq;
167562306a36Sopenharmony_ci		}
167662306a36Sopenharmony_ci	}
167762306a36Sopenharmony_ci
167862306a36Sopenharmony_ci	mutex_init(&lr->config_mutex);
167962306a36Sopenharmony_ci	init_waitqueue_head(&lr->wait);
168062306a36Sopenharmony_ci	lr->event_buffer_size = ulr.event_buffer_size;
168162306a36Sopenharmony_ci	if (lr->event_buffer_size == 0)
168262306a36Sopenharmony_ci		lr->event_buffer_size = ulr.num_lines * 16;
168362306a36Sopenharmony_ci	else if (lr->event_buffer_size > GPIO_V2_LINES_MAX * 16)
168462306a36Sopenharmony_ci		lr->event_buffer_size = GPIO_V2_LINES_MAX * 16;
168562306a36Sopenharmony_ci
168662306a36Sopenharmony_ci	atomic_set(&lr->seqno, 0);
168762306a36Sopenharmony_ci	lr->num_lines = ulr.num_lines;
168862306a36Sopenharmony_ci
168962306a36Sopenharmony_ci	/* Request each GPIO */
169062306a36Sopenharmony_ci	for (i = 0; i < ulr.num_lines; i++) {
169162306a36Sopenharmony_ci		u32 offset = ulr.offsets[i];
169262306a36Sopenharmony_ci		struct gpio_desc *desc = gpiochip_get_desc(gdev->chip, offset);
169362306a36Sopenharmony_ci
169462306a36Sopenharmony_ci		if (IS_ERR(desc)) {
169562306a36Sopenharmony_ci			ret = PTR_ERR(desc);
169662306a36Sopenharmony_ci			goto out_free_linereq;
169762306a36Sopenharmony_ci		}
169862306a36Sopenharmony_ci
169962306a36Sopenharmony_ci		ret = gpiod_request_user(desc, lr->label);
170062306a36Sopenharmony_ci		if (ret)
170162306a36Sopenharmony_ci			goto out_free_linereq;
170262306a36Sopenharmony_ci
170362306a36Sopenharmony_ci		lr->lines[i].desc = desc;
170462306a36Sopenharmony_ci		flags = gpio_v2_line_config_flags(lc, i);
170562306a36Sopenharmony_ci		gpio_v2_line_config_flags_to_desc_flags(flags, &desc->flags);
170662306a36Sopenharmony_ci
170762306a36Sopenharmony_ci		ret = gpiod_set_transitory(desc, false);
170862306a36Sopenharmony_ci		if (ret < 0)
170962306a36Sopenharmony_ci			goto out_free_linereq;
171062306a36Sopenharmony_ci
171162306a36Sopenharmony_ci		edflags = flags & GPIO_V2_LINE_EDGE_DETECTOR_FLAGS;
171262306a36Sopenharmony_ci		/*
171362306a36Sopenharmony_ci		 * Lines have to be requested explicitly for input
171462306a36Sopenharmony_ci		 * or output, else the line will be treated "as is".
171562306a36Sopenharmony_ci		 */
171662306a36Sopenharmony_ci		if (flags & GPIO_V2_LINE_FLAG_OUTPUT) {
171762306a36Sopenharmony_ci			int val = gpio_v2_line_config_output_value(lc, i);
171862306a36Sopenharmony_ci
171962306a36Sopenharmony_ci			ret = gpiod_direction_output(desc, val);
172062306a36Sopenharmony_ci			if (ret)
172162306a36Sopenharmony_ci				goto out_free_linereq;
172262306a36Sopenharmony_ci		} else if (flags & GPIO_V2_LINE_FLAG_INPUT) {
172362306a36Sopenharmony_ci			ret = gpiod_direction_input(desc);
172462306a36Sopenharmony_ci			if (ret)
172562306a36Sopenharmony_ci				goto out_free_linereq;
172662306a36Sopenharmony_ci
172762306a36Sopenharmony_ci			ret = edge_detector_setup(&lr->lines[i], lc, i,
172862306a36Sopenharmony_ci						  edflags);
172962306a36Sopenharmony_ci			if (ret)
173062306a36Sopenharmony_ci				goto out_free_linereq;
173162306a36Sopenharmony_ci		}
173262306a36Sopenharmony_ci
173362306a36Sopenharmony_ci		lr->lines[i].edflags = edflags;
173462306a36Sopenharmony_ci
173562306a36Sopenharmony_ci		gpiod_line_state_notify(desc, GPIO_V2_LINE_CHANGED_REQUESTED);
173662306a36Sopenharmony_ci
173762306a36Sopenharmony_ci		dev_dbg(&gdev->dev, "registered chardev handle for line %d\n",
173862306a36Sopenharmony_ci			offset);
173962306a36Sopenharmony_ci	}
174062306a36Sopenharmony_ci
174162306a36Sopenharmony_ci	lr->device_unregistered_nb.notifier_call = linereq_unregistered_notify;
174262306a36Sopenharmony_ci	ret = blocking_notifier_chain_register(&gdev->device_notifier,
174362306a36Sopenharmony_ci					       &lr->device_unregistered_nb);
174462306a36Sopenharmony_ci	if (ret)
174562306a36Sopenharmony_ci		goto out_free_linereq;
174662306a36Sopenharmony_ci
174762306a36Sopenharmony_ci	fd = get_unused_fd_flags(O_RDONLY | O_CLOEXEC);
174862306a36Sopenharmony_ci	if (fd < 0) {
174962306a36Sopenharmony_ci		ret = fd;
175062306a36Sopenharmony_ci		goto out_free_linereq;
175162306a36Sopenharmony_ci	}
175262306a36Sopenharmony_ci
175362306a36Sopenharmony_ci	file = anon_inode_getfile("gpio-line", &line_fileops, lr,
175462306a36Sopenharmony_ci				  O_RDONLY | O_CLOEXEC);
175562306a36Sopenharmony_ci	if (IS_ERR(file)) {
175662306a36Sopenharmony_ci		ret = PTR_ERR(file);
175762306a36Sopenharmony_ci		goto out_put_unused_fd;
175862306a36Sopenharmony_ci	}
175962306a36Sopenharmony_ci
176062306a36Sopenharmony_ci	ulr.fd = fd;
176162306a36Sopenharmony_ci	if (copy_to_user(ip, &ulr, sizeof(ulr))) {
176262306a36Sopenharmony_ci		/*
176362306a36Sopenharmony_ci		 * fput() will trigger the release() callback, so do not go onto
176462306a36Sopenharmony_ci		 * the regular error cleanup path here.
176562306a36Sopenharmony_ci		 */
176662306a36Sopenharmony_ci		fput(file);
176762306a36Sopenharmony_ci		put_unused_fd(fd);
176862306a36Sopenharmony_ci		return -EFAULT;
176962306a36Sopenharmony_ci	}
177062306a36Sopenharmony_ci
177162306a36Sopenharmony_ci	fd_install(fd, file);
177262306a36Sopenharmony_ci
177362306a36Sopenharmony_ci	dev_dbg(&gdev->dev, "registered chardev handle for %d lines\n",
177462306a36Sopenharmony_ci		lr->num_lines);
177562306a36Sopenharmony_ci
177662306a36Sopenharmony_ci	return 0;
177762306a36Sopenharmony_ci
177862306a36Sopenharmony_ciout_put_unused_fd:
177962306a36Sopenharmony_ci	put_unused_fd(fd);
178062306a36Sopenharmony_ciout_free_linereq:
178162306a36Sopenharmony_ci	linereq_free(lr);
178262306a36Sopenharmony_ci	return ret;
178362306a36Sopenharmony_ci}
178462306a36Sopenharmony_ci
178562306a36Sopenharmony_ci#ifdef CONFIG_GPIO_CDEV_V1
178662306a36Sopenharmony_ci
178762306a36Sopenharmony_ci/*
178862306a36Sopenharmony_ci * GPIO line event management
178962306a36Sopenharmony_ci */
179062306a36Sopenharmony_ci
179162306a36Sopenharmony_ci/**
179262306a36Sopenharmony_ci * struct lineevent_state - contains the state of a userspace event
179362306a36Sopenharmony_ci * @gdev: the GPIO device the event pertains to
179462306a36Sopenharmony_ci * @label: consumer label used to tag descriptors
179562306a36Sopenharmony_ci * @desc: the GPIO descriptor held by this event
179662306a36Sopenharmony_ci * @eflags: the event flags this line was requested with
179762306a36Sopenharmony_ci * @irq: the interrupt that trigger in response to events on this GPIO
179862306a36Sopenharmony_ci * @wait: wait queue that handles blocking reads of events
179962306a36Sopenharmony_ci * @device_unregistered_nb: notifier block for receiving gdev unregister events
180062306a36Sopenharmony_ci * @events: KFIFO for the GPIO events
180162306a36Sopenharmony_ci * @timestamp: cache for the timestamp storing it between hardirq
180262306a36Sopenharmony_ci * and IRQ thread, used to bring the timestamp close to the actual
180362306a36Sopenharmony_ci * event
180462306a36Sopenharmony_ci */
180562306a36Sopenharmony_cistruct lineevent_state {
180662306a36Sopenharmony_ci	struct gpio_device *gdev;
180762306a36Sopenharmony_ci	const char *label;
180862306a36Sopenharmony_ci	struct gpio_desc *desc;
180962306a36Sopenharmony_ci	u32 eflags;
181062306a36Sopenharmony_ci	int irq;
181162306a36Sopenharmony_ci	wait_queue_head_t wait;
181262306a36Sopenharmony_ci	struct notifier_block device_unregistered_nb;
181362306a36Sopenharmony_ci	DECLARE_KFIFO(events, struct gpioevent_data, 16);
181462306a36Sopenharmony_ci	u64 timestamp;
181562306a36Sopenharmony_ci};
181662306a36Sopenharmony_ci
181762306a36Sopenharmony_ci#define GPIOEVENT_REQUEST_VALID_FLAGS \
181862306a36Sopenharmony_ci	(GPIOEVENT_REQUEST_RISING_EDGE | \
181962306a36Sopenharmony_ci	GPIOEVENT_REQUEST_FALLING_EDGE)
182062306a36Sopenharmony_ci
182162306a36Sopenharmony_cistatic __poll_t lineevent_poll_unlocked(struct file *file,
182262306a36Sopenharmony_ci					struct poll_table_struct *wait)
182362306a36Sopenharmony_ci{
182462306a36Sopenharmony_ci	struct lineevent_state *le = file->private_data;
182562306a36Sopenharmony_ci	__poll_t events = 0;
182662306a36Sopenharmony_ci
182762306a36Sopenharmony_ci	if (!le->gdev->chip)
182862306a36Sopenharmony_ci		return EPOLLHUP | EPOLLERR;
182962306a36Sopenharmony_ci
183062306a36Sopenharmony_ci	poll_wait(file, &le->wait, wait);
183162306a36Sopenharmony_ci
183262306a36Sopenharmony_ci	if (!kfifo_is_empty_spinlocked_noirqsave(&le->events, &le->wait.lock))
183362306a36Sopenharmony_ci		events = EPOLLIN | EPOLLRDNORM;
183462306a36Sopenharmony_ci
183562306a36Sopenharmony_ci	return events;
183662306a36Sopenharmony_ci}
183762306a36Sopenharmony_ci
183862306a36Sopenharmony_cistatic __poll_t lineevent_poll(struct file *file,
183962306a36Sopenharmony_ci			       struct poll_table_struct *wait)
184062306a36Sopenharmony_ci{
184162306a36Sopenharmony_ci	struct lineevent_state *le = file->private_data;
184262306a36Sopenharmony_ci
184362306a36Sopenharmony_ci	return call_poll_locked(file, wait, le->gdev, lineevent_poll_unlocked);
184462306a36Sopenharmony_ci}
184562306a36Sopenharmony_ci
184662306a36Sopenharmony_cistatic int lineevent_unregistered_notify(struct notifier_block *nb,
184762306a36Sopenharmony_ci					 unsigned long action, void *data)
184862306a36Sopenharmony_ci{
184962306a36Sopenharmony_ci	struct lineevent_state *le = container_of(nb, struct lineevent_state,
185062306a36Sopenharmony_ci						  device_unregistered_nb);
185162306a36Sopenharmony_ci
185262306a36Sopenharmony_ci	wake_up_poll(&le->wait, EPOLLIN | EPOLLERR);
185362306a36Sopenharmony_ci
185462306a36Sopenharmony_ci	return NOTIFY_OK;
185562306a36Sopenharmony_ci}
185662306a36Sopenharmony_ci
185762306a36Sopenharmony_cistruct compat_gpioeevent_data {
185862306a36Sopenharmony_ci	compat_u64	timestamp;
185962306a36Sopenharmony_ci	u32		id;
186062306a36Sopenharmony_ci};
186162306a36Sopenharmony_ci
186262306a36Sopenharmony_cistatic ssize_t lineevent_read_unlocked(struct file *file, char __user *buf,
186362306a36Sopenharmony_ci				       size_t count, loff_t *f_ps)
186462306a36Sopenharmony_ci{
186562306a36Sopenharmony_ci	struct lineevent_state *le = file->private_data;
186662306a36Sopenharmony_ci	struct gpioevent_data ge;
186762306a36Sopenharmony_ci	ssize_t bytes_read = 0;
186862306a36Sopenharmony_ci	ssize_t ge_size;
186962306a36Sopenharmony_ci	int ret;
187062306a36Sopenharmony_ci
187162306a36Sopenharmony_ci	if (!le->gdev->chip)
187262306a36Sopenharmony_ci		return -ENODEV;
187362306a36Sopenharmony_ci
187462306a36Sopenharmony_ci	/*
187562306a36Sopenharmony_ci	 * When compatible system call is being used the struct gpioevent_data,
187662306a36Sopenharmony_ci	 * in case of at least ia32, has different size due to the alignment
187762306a36Sopenharmony_ci	 * differences. Because we have first member 64 bits followed by one of
187862306a36Sopenharmony_ci	 * 32 bits there is no gap between them. The only difference is the
187962306a36Sopenharmony_ci	 * padding at the end of the data structure. Hence, we calculate the
188062306a36Sopenharmony_ci	 * actual sizeof() and pass this as an argument to copy_to_user() to
188162306a36Sopenharmony_ci	 * drop unneeded bytes from the output.
188262306a36Sopenharmony_ci	 */
188362306a36Sopenharmony_ci	if (compat_need_64bit_alignment_fixup())
188462306a36Sopenharmony_ci		ge_size = sizeof(struct compat_gpioeevent_data);
188562306a36Sopenharmony_ci	else
188662306a36Sopenharmony_ci		ge_size = sizeof(struct gpioevent_data);
188762306a36Sopenharmony_ci	if (count < ge_size)
188862306a36Sopenharmony_ci		return -EINVAL;
188962306a36Sopenharmony_ci
189062306a36Sopenharmony_ci	do {
189162306a36Sopenharmony_ci		spin_lock(&le->wait.lock);
189262306a36Sopenharmony_ci		if (kfifo_is_empty(&le->events)) {
189362306a36Sopenharmony_ci			if (bytes_read) {
189462306a36Sopenharmony_ci				spin_unlock(&le->wait.lock);
189562306a36Sopenharmony_ci				return bytes_read;
189662306a36Sopenharmony_ci			}
189762306a36Sopenharmony_ci
189862306a36Sopenharmony_ci			if (file->f_flags & O_NONBLOCK) {
189962306a36Sopenharmony_ci				spin_unlock(&le->wait.lock);
190062306a36Sopenharmony_ci				return -EAGAIN;
190162306a36Sopenharmony_ci			}
190262306a36Sopenharmony_ci
190362306a36Sopenharmony_ci			ret = wait_event_interruptible_locked(le->wait,
190462306a36Sopenharmony_ci					!kfifo_is_empty(&le->events));
190562306a36Sopenharmony_ci			if (ret) {
190662306a36Sopenharmony_ci				spin_unlock(&le->wait.lock);
190762306a36Sopenharmony_ci				return ret;
190862306a36Sopenharmony_ci			}
190962306a36Sopenharmony_ci		}
191062306a36Sopenharmony_ci
191162306a36Sopenharmony_ci		ret = kfifo_out(&le->events, &ge, 1);
191262306a36Sopenharmony_ci		spin_unlock(&le->wait.lock);
191362306a36Sopenharmony_ci		if (ret != 1) {
191462306a36Sopenharmony_ci			/*
191562306a36Sopenharmony_ci			 * This should never happen - we were holding the lock
191662306a36Sopenharmony_ci			 * from the moment we learned the fifo is no longer
191762306a36Sopenharmony_ci			 * empty until now.
191862306a36Sopenharmony_ci			 */
191962306a36Sopenharmony_ci			ret = -EIO;
192062306a36Sopenharmony_ci			break;
192162306a36Sopenharmony_ci		}
192262306a36Sopenharmony_ci
192362306a36Sopenharmony_ci		if (copy_to_user(buf + bytes_read, &ge, ge_size))
192462306a36Sopenharmony_ci			return -EFAULT;
192562306a36Sopenharmony_ci		bytes_read += ge_size;
192662306a36Sopenharmony_ci	} while (count >= bytes_read + ge_size);
192762306a36Sopenharmony_ci
192862306a36Sopenharmony_ci	return bytes_read;
192962306a36Sopenharmony_ci}
193062306a36Sopenharmony_ci
193162306a36Sopenharmony_cistatic ssize_t lineevent_read(struct file *file, char __user *buf,
193262306a36Sopenharmony_ci			      size_t count, loff_t *f_ps)
193362306a36Sopenharmony_ci{
193462306a36Sopenharmony_ci	struct lineevent_state *le = file->private_data;
193562306a36Sopenharmony_ci
193662306a36Sopenharmony_ci	return call_read_locked(file, buf, count, f_ps, le->gdev,
193762306a36Sopenharmony_ci				lineevent_read_unlocked);
193862306a36Sopenharmony_ci}
193962306a36Sopenharmony_ci
194062306a36Sopenharmony_cistatic void lineevent_free(struct lineevent_state *le)
194162306a36Sopenharmony_ci{
194262306a36Sopenharmony_ci	if (le->device_unregistered_nb.notifier_call)
194362306a36Sopenharmony_ci		blocking_notifier_chain_unregister(&le->gdev->device_notifier,
194462306a36Sopenharmony_ci						   &le->device_unregistered_nb);
194562306a36Sopenharmony_ci	if (le->irq)
194662306a36Sopenharmony_ci		free_irq(le->irq, le);
194762306a36Sopenharmony_ci	if (le->desc)
194862306a36Sopenharmony_ci		gpiod_free(le->desc);
194962306a36Sopenharmony_ci	kfree(le->label);
195062306a36Sopenharmony_ci	gpio_device_put(le->gdev);
195162306a36Sopenharmony_ci	kfree(le);
195262306a36Sopenharmony_ci}
195362306a36Sopenharmony_ci
195462306a36Sopenharmony_cistatic int lineevent_release(struct inode *inode, struct file *file)
195562306a36Sopenharmony_ci{
195662306a36Sopenharmony_ci	lineevent_free(file->private_data);
195762306a36Sopenharmony_ci	return 0;
195862306a36Sopenharmony_ci}
195962306a36Sopenharmony_ci
196062306a36Sopenharmony_cistatic long lineevent_ioctl_unlocked(struct file *file, unsigned int cmd,
196162306a36Sopenharmony_ci				     unsigned long arg)
196262306a36Sopenharmony_ci{
196362306a36Sopenharmony_ci	struct lineevent_state *le = file->private_data;
196462306a36Sopenharmony_ci	void __user *ip = (void __user *)arg;
196562306a36Sopenharmony_ci	struct gpiohandle_data ghd;
196662306a36Sopenharmony_ci
196762306a36Sopenharmony_ci	if (!le->gdev->chip)
196862306a36Sopenharmony_ci		return -ENODEV;
196962306a36Sopenharmony_ci
197062306a36Sopenharmony_ci	/*
197162306a36Sopenharmony_ci	 * We can get the value for an event line but not set it,
197262306a36Sopenharmony_ci	 * because it is input by definition.
197362306a36Sopenharmony_ci	 */
197462306a36Sopenharmony_ci	if (cmd == GPIOHANDLE_GET_LINE_VALUES_IOCTL) {
197562306a36Sopenharmony_ci		int val;
197662306a36Sopenharmony_ci
197762306a36Sopenharmony_ci		memset(&ghd, 0, sizeof(ghd));
197862306a36Sopenharmony_ci
197962306a36Sopenharmony_ci		val = gpiod_get_value_cansleep(le->desc);
198062306a36Sopenharmony_ci		if (val < 0)
198162306a36Sopenharmony_ci			return val;
198262306a36Sopenharmony_ci		ghd.values[0] = val;
198362306a36Sopenharmony_ci
198462306a36Sopenharmony_ci		if (copy_to_user(ip, &ghd, sizeof(ghd)))
198562306a36Sopenharmony_ci			return -EFAULT;
198662306a36Sopenharmony_ci
198762306a36Sopenharmony_ci		return 0;
198862306a36Sopenharmony_ci	}
198962306a36Sopenharmony_ci	return -EINVAL;
199062306a36Sopenharmony_ci}
199162306a36Sopenharmony_ci
199262306a36Sopenharmony_cistatic long lineevent_ioctl(struct file *file, unsigned int cmd,
199362306a36Sopenharmony_ci			    unsigned long arg)
199462306a36Sopenharmony_ci{
199562306a36Sopenharmony_ci	struct lineevent_state *le = file->private_data;
199662306a36Sopenharmony_ci
199762306a36Sopenharmony_ci	return call_ioctl_locked(file, cmd, arg, le->gdev,
199862306a36Sopenharmony_ci				 lineevent_ioctl_unlocked);
199962306a36Sopenharmony_ci}
200062306a36Sopenharmony_ci
200162306a36Sopenharmony_ci#ifdef CONFIG_COMPAT
200262306a36Sopenharmony_cistatic long lineevent_ioctl_compat(struct file *file, unsigned int cmd,
200362306a36Sopenharmony_ci				   unsigned long arg)
200462306a36Sopenharmony_ci{
200562306a36Sopenharmony_ci	return lineevent_ioctl(file, cmd, (unsigned long)compat_ptr(arg));
200662306a36Sopenharmony_ci}
200762306a36Sopenharmony_ci#endif
200862306a36Sopenharmony_ci
200962306a36Sopenharmony_cistatic const struct file_operations lineevent_fileops = {
201062306a36Sopenharmony_ci	.release = lineevent_release,
201162306a36Sopenharmony_ci	.read = lineevent_read,
201262306a36Sopenharmony_ci	.poll = lineevent_poll,
201362306a36Sopenharmony_ci	.owner = THIS_MODULE,
201462306a36Sopenharmony_ci	.llseek = noop_llseek,
201562306a36Sopenharmony_ci	.unlocked_ioctl = lineevent_ioctl,
201662306a36Sopenharmony_ci#ifdef CONFIG_COMPAT
201762306a36Sopenharmony_ci	.compat_ioctl = lineevent_ioctl_compat,
201862306a36Sopenharmony_ci#endif
201962306a36Sopenharmony_ci};
202062306a36Sopenharmony_ci
202162306a36Sopenharmony_cistatic irqreturn_t lineevent_irq_thread(int irq, void *p)
202262306a36Sopenharmony_ci{
202362306a36Sopenharmony_ci	struct lineevent_state *le = p;
202462306a36Sopenharmony_ci	struct gpioevent_data ge;
202562306a36Sopenharmony_ci	int ret;
202662306a36Sopenharmony_ci
202762306a36Sopenharmony_ci	/* Do not leak kernel stack to userspace */
202862306a36Sopenharmony_ci	memset(&ge, 0, sizeof(ge));
202962306a36Sopenharmony_ci
203062306a36Sopenharmony_ci	/*
203162306a36Sopenharmony_ci	 * We may be running from a nested threaded interrupt in which case
203262306a36Sopenharmony_ci	 * we didn't get the timestamp from lineevent_irq_handler().
203362306a36Sopenharmony_ci	 */
203462306a36Sopenharmony_ci	if (!le->timestamp)
203562306a36Sopenharmony_ci		ge.timestamp = ktime_get_ns();
203662306a36Sopenharmony_ci	else
203762306a36Sopenharmony_ci		ge.timestamp = le->timestamp;
203862306a36Sopenharmony_ci
203962306a36Sopenharmony_ci	if (le->eflags & GPIOEVENT_REQUEST_RISING_EDGE
204062306a36Sopenharmony_ci	    && le->eflags & GPIOEVENT_REQUEST_FALLING_EDGE) {
204162306a36Sopenharmony_ci		int level = gpiod_get_value_cansleep(le->desc);
204262306a36Sopenharmony_ci
204362306a36Sopenharmony_ci		if (level)
204462306a36Sopenharmony_ci			/* Emit low-to-high event */
204562306a36Sopenharmony_ci			ge.id = GPIOEVENT_EVENT_RISING_EDGE;
204662306a36Sopenharmony_ci		else
204762306a36Sopenharmony_ci			/* Emit high-to-low event */
204862306a36Sopenharmony_ci			ge.id = GPIOEVENT_EVENT_FALLING_EDGE;
204962306a36Sopenharmony_ci	} else if (le->eflags & GPIOEVENT_REQUEST_RISING_EDGE) {
205062306a36Sopenharmony_ci		/* Emit low-to-high event */
205162306a36Sopenharmony_ci		ge.id = GPIOEVENT_EVENT_RISING_EDGE;
205262306a36Sopenharmony_ci	} else if (le->eflags & GPIOEVENT_REQUEST_FALLING_EDGE) {
205362306a36Sopenharmony_ci		/* Emit high-to-low event */
205462306a36Sopenharmony_ci		ge.id = GPIOEVENT_EVENT_FALLING_EDGE;
205562306a36Sopenharmony_ci	} else {
205662306a36Sopenharmony_ci		return IRQ_NONE;
205762306a36Sopenharmony_ci	}
205862306a36Sopenharmony_ci
205962306a36Sopenharmony_ci	ret = kfifo_in_spinlocked_noirqsave(&le->events, &ge,
206062306a36Sopenharmony_ci					    1, &le->wait.lock);
206162306a36Sopenharmony_ci	if (ret)
206262306a36Sopenharmony_ci		wake_up_poll(&le->wait, EPOLLIN);
206362306a36Sopenharmony_ci	else
206462306a36Sopenharmony_ci		pr_debug_ratelimited("event FIFO is full - event dropped\n");
206562306a36Sopenharmony_ci
206662306a36Sopenharmony_ci	return IRQ_HANDLED;
206762306a36Sopenharmony_ci}
206862306a36Sopenharmony_ci
206962306a36Sopenharmony_cistatic irqreturn_t lineevent_irq_handler(int irq, void *p)
207062306a36Sopenharmony_ci{
207162306a36Sopenharmony_ci	struct lineevent_state *le = p;
207262306a36Sopenharmony_ci
207362306a36Sopenharmony_ci	/*
207462306a36Sopenharmony_ci	 * Just store the timestamp in hardirq context so we get it as
207562306a36Sopenharmony_ci	 * close in time as possible to the actual event.
207662306a36Sopenharmony_ci	 */
207762306a36Sopenharmony_ci	le->timestamp = ktime_get_ns();
207862306a36Sopenharmony_ci
207962306a36Sopenharmony_ci	return IRQ_WAKE_THREAD;
208062306a36Sopenharmony_ci}
208162306a36Sopenharmony_ci
208262306a36Sopenharmony_cistatic int lineevent_create(struct gpio_device *gdev, void __user *ip)
208362306a36Sopenharmony_ci{
208462306a36Sopenharmony_ci	struct gpioevent_request eventreq;
208562306a36Sopenharmony_ci	struct lineevent_state *le;
208662306a36Sopenharmony_ci	struct gpio_desc *desc;
208762306a36Sopenharmony_ci	struct file *file;
208862306a36Sopenharmony_ci	u32 offset;
208962306a36Sopenharmony_ci	u32 lflags;
209062306a36Sopenharmony_ci	u32 eflags;
209162306a36Sopenharmony_ci	int fd;
209262306a36Sopenharmony_ci	int ret;
209362306a36Sopenharmony_ci	int irq, irqflags = 0;
209462306a36Sopenharmony_ci
209562306a36Sopenharmony_ci	if (copy_from_user(&eventreq, ip, sizeof(eventreq)))
209662306a36Sopenharmony_ci		return -EFAULT;
209762306a36Sopenharmony_ci
209862306a36Sopenharmony_ci	offset = eventreq.lineoffset;
209962306a36Sopenharmony_ci	lflags = eventreq.handleflags;
210062306a36Sopenharmony_ci	eflags = eventreq.eventflags;
210162306a36Sopenharmony_ci
210262306a36Sopenharmony_ci	desc = gpiochip_get_desc(gdev->chip, offset);
210362306a36Sopenharmony_ci	if (IS_ERR(desc))
210462306a36Sopenharmony_ci		return PTR_ERR(desc);
210562306a36Sopenharmony_ci
210662306a36Sopenharmony_ci	/* Return an error if a unknown flag is set */
210762306a36Sopenharmony_ci	if ((lflags & ~GPIOHANDLE_REQUEST_VALID_FLAGS) ||
210862306a36Sopenharmony_ci	    (eflags & ~GPIOEVENT_REQUEST_VALID_FLAGS))
210962306a36Sopenharmony_ci		return -EINVAL;
211062306a36Sopenharmony_ci
211162306a36Sopenharmony_ci	/* This is just wrong: we don't look for events on output lines */
211262306a36Sopenharmony_ci	if ((lflags & GPIOHANDLE_REQUEST_OUTPUT) ||
211362306a36Sopenharmony_ci	    (lflags & GPIOHANDLE_REQUEST_OPEN_DRAIN) ||
211462306a36Sopenharmony_ci	    (lflags & GPIOHANDLE_REQUEST_OPEN_SOURCE))
211562306a36Sopenharmony_ci		return -EINVAL;
211662306a36Sopenharmony_ci
211762306a36Sopenharmony_ci	/* Only one bias flag can be set. */
211862306a36Sopenharmony_ci	if (((lflags & GPIOHANDLE_REQUEST_BIAS_DISABLE) &&
211962306a36Sopenharmony_ci	     (lflags & (GPIOHANDLE_REQUEST_BIAS_PULL_DOWN |
212062306a36Sopenharmony_ci			GPIOHANDLE_REQUEST_BIAS_PULL_UP))) ||
212162306a36Sopenharmony_ci	    ((lflags & GPIOHANDLE_REQUEST_BIAS_PULL_DOWN) &&
212262306a36Sopenharmony_ci	     (lflags & GPIOHANDLE_REQUEST_BIAS_PULL_UP)))
212362306a36Sopenharmony_ci		return -EINVAL;
212462306a36Sopenharmony_ci
212562306a36Sopenharmony_ci	le = kzalloc(sizeof(*le), GFP_KERNEL);
212662306a36Sopenharmony_ci	if (!le)
212762306a36Sopenharmony_ci		return -ENOMEM;
212862306a36Sopenharmony_ci	le->gdev = gpio_device_get(gdev);
212962306a36Sopenharmony_ci
213062306a36Sopenharmony_ci	if (eventreq.consumer_label[0] != '\0') {
213162306a36Sopenharmony_ci		/* label is only initialized if consumer_label is set */
213262306a36Sopenharmony_ci		le->label = kstrndup(eventreq.consumer_label,
213362306a36Sopenharmony_ci				     sizeof(eventreq.consumer_label) - 1,
213462306a36Sopenharmony_ci				     GFP_KERNEL);
213562306a36Sopenharmony_ci		if (!le->label) {
213662306a36Sopenharmony_ci			ret = -ENOMEM;
213762306a36Sopenharmony_ci			goto out_free_le;
213862306a36Sopenharmony_ci		}
213962306a36Sopenharmony_ci	}
214062306a36Sopenharmony_ci
214162306a36Sopenharmony_ci	ret = gpiod_request_user(desc, le->label);
214262306a36Sopenharmony_ci	if (ret)
214362306a36Sopenharmony_ci		goto out_free_le;
214462306a36Sopenharmony_ci	le->desc = desc;
214562306a36Sopenharmony_ci	le->eflags = eflags;
214662306a36Sopenharmony_ci
214762306a36Sopenharmony_ci	linehandle_flags_to_desc_flags(lflags, &desc->flags);
214862306a36Sopenharmony_ci
214962306a36Sopenharmony_ci	ret = gpiod_direction_input(desc);
215062306a36Sopenharmony_ci	if (ret)
215162306a36Sopenharmony_ci		goto out_free_le;
215262306a36Sopenharmony_ci
215362306a36Sopenharmony_ci	gpiod_line_state_notify(desc, GPIO_V2_LINE_CHANGED_REQUESTED);
215462306a36Sopenharmony_ci
215562306a36Sopenharmony_ci	irq = gpiod_to_irq(desc);
215662306a36Sopenharmony_ci	if (irq <= 0) {
215762306a36Sopenharmony_ci		ret = -ENODEV;
215862306a36Sopenharmony_ci		goto out_free_le;
215962306a36Sopenharmony_ci	}
216062306a36Sopenharmony_ci
216162306a36Sopenharmony_ci	if (eflags & GPIOEVENT_REQUEST_RISING_EDGE)
216262306a36Sopenharmony_ci		irqflags |= test_bit(FLAG_ACTIVE_LOW, &desc->flags) ?
216362306a36Sopenharmony_ci			IRQF_TRIGGER_FALLING : IRQF_TRIGGER_RISING;
216462306a36Sopenharmony_ci	if (eflags & GPIOEVENT_REQUEST_FALLING_EDGE)
216562306a36Sopenharmony_ci		irqflags |= test_bit(FLAG_ACTIVE_LOW, &desc->flags) ?
216662306a36Sopenharmony_ci			IRQF_TRIGGER_RISING : IRQF_TRIGGER_FALLING;
216762306a36Sopenharmony_ci	irqflags |= IRQF_ONESHOT;
216862306a36Sopenharmony_ci
216962306a36Sopenharmony_ci	INIT_KFIFO(le->events);
217062306a36Sopenharmony_ci	init_waitqueue_head(&le->wait);
217162306a36Sopenharmony_ci
217262306a36Sopenharmony_ci	le->device_unregistered_nb.notifier_call = lineevent_unregistered_notify;
217362306a36Sopenharmony_ci	ret = blocking_notifier_chain_register(&gdev->device_notifier,
217462306a36Sopenharmony_ci					       &le->device_unregistered_nb);
217562306a36Sopenharmony_ci	if (ret)
217662306a36Sopenharmony_ci		goto out_free_le;
217762306a36Sopenharmony_ci
217862306a36Sopenharmony_ci	/* Request a thread to read the events */
217962306a36Sopenharmony_ci	ret = request_threaded_irq(irq,
218062306a36Sopenharmony_ci				   lineevent_irq_handler,
218162306a36Sopenharmony_ci				   lineevent_irq_thread,
218262306a36Sopenharmony_ci				   irqflags,
218362306a36Sopenharmony_ci				   le->label,
218462306a36Sopenharmony_ci				   le);
218562306a36Sopenharmony_ci	if (ret)
218662306a36Sopenharmony_ci		goto out_free_le;
218762306a36Sopenharmony_ci
218862306a36Sopenharmony_ci	le->irq = irq;
218962306a36Sopenharmony_ci
219062306a36Sopenharmony_ci	fd = get_unused_fd_flags(O_RDONLY | O_CLOEXEC);
219162306a36Sopenharmony_ci	if (fd < 0) {
219262306a36Sopenharmony_ci		ret = fd;
219362306a36Sopenharmony_ci		goto out_free_le;
219462306a36Sopenharmony_ci	}
219562306a36Sopenharmony_ci
219662306a36Sopenharmony_ci	file = anon_inode_getfile("gpio-event",
219762306a36Sopenharmony_ci				  &lineevent_fileops,
219862306a36Sopenharmony_ci				  le,
219962306a36Sopenharmony_ci				  O_RDONLY | O_CLOEXEC);
220062306a36Sopenharmony_ci	if (IS_ERR(file)) {
220162306a36Sopenharmony_ci		ret = PTR_ERR(file);
220262306a36Sopenharmony_ci		goto out_put_unused_fd;
220362306a36Sopenharmony_ci	}
220462306a36Sopenharmony_ci
220562306a36Sopenharmony_ci	eventreq.fd = fd;
220662306a36Sopenharmony_ci	if (copy_to_user(ip, &eventreq, sizeof(eventreq))) {
220762306a36Sopenharmony_ci		/*
220862306a36Sopenharmony_ci		 * fput() will trigger the release() callback, so do not go onto
220962306a36Sopenharmony_ci		 * the regular error cleanup path here.
221062306a36Sopenharmony_ci		 */
221162306a36Sopenharmony_ci		fput(file);
221262306a36Sopenharmony_ci		put_unused_fd(fd);
221362306a36Sopenharmony_ci		return -EFAULT;
221462306a36Sopenharmony_ci	}
221562306a36Sopenharmony_ci
221662306a36Sopenharmony_ci	fd_install(fd, file);
221762306a36Sopenharmony_ci
221862306a36Sopenharmony_ci	return 0;
221962306a36Sopenharmony_ci
222062306a36Sopenharmony_ciout_put_unused_fd:
222162306a36Sopenharmony_ci	put_unused_fd(fd);
222262306a36Sopenharmony_ciout_free_le:
222362306a36Sopenharmony_ci	lineevent_free(le);
222462306a36Sopenharmony_ci	return ret;
222562306a36Sopenharmony_ci}
222662306a36Sopenharmony_ci
222762306a36Sopenharmony_cistatic void gpio_v2_line_info_to_v1(struct gpio_v2_line_info *info_v2,
222862306a36Sopenharmony_ci				    struct gpioline_info *info_v1)
222962306a36Sopenharmony_ci{
223062306a36Sopenharmony_ci	u64 flagsv2 = info_v2->flags;
223162306a36Sopenharmony_ci
223262306a36Sopenharmony_ci	memcpy(info_v1->name, info_v2->name, sizeof(info_v1->name));
223362306a36Sopenharmony_ci	memcpy(info_v1->consumer, info_v2->consumer, sizeof(info_v1->consumer));
223462306a36Sopenharmony_ci	info_v1->line_offset = info_v2->offset;
223562306a36Sopenharmony_ci	info_v1->flags = 0;
223662306a36Sopenharmony_ci
223762306a36Sopenharmony_ci	if (flagsv2 & GPIO_V2_LINE_FLAG_USED)
223862306a36Sopenharmony_ci		info_v1->flags |= GPIOLINE_FLAG_KERNEL;
223962306a36Sopenharmony_ci
224062306a36Sopenharmony_ci	if (flagsv2 & GPIO_V2_LINE_FLAG_OUTPUT)
224162306a36Sopenharmony_ci		info_v1->flags |= GPIOLINE_FLAG_IS_OUT;
224262306a36Sopenharmony_ci
224362306a36Sopenharmony_ci	if (flagsv2 & GPIO_V2_LINE_FLAG_ACTIVE_LOW)
224462306a36Sopenharmony_ci		info_v1->flags |= GPIOLINE_FLAG_ACTIVE_LOW;
224562306a36Sopenharmony_ci
224662306a36Sopenharmony_ci	if (flagsv2 & GPIO_V2_LINE_FLAG_OPEN_DRAIN)
224762306a36Sopenharmony_ci		info_v1->flags |= GPIOLINE_FLAG_OPEN_DRAIN;
224862306a36Sopenharmony_ci	if (flagsv2 & GPIO_V2_LINE_FLAG_OPEN_SOURCE)
224962306a36Sopenharmony_ci		info_v1->flags |= GPIOLINE_FLAG_OPEN_SOURCE;
225062306a36Sopenharmony_ci
225162306a36Sopenharmony_ci	if (flagsv2 & GPIO_V2_LINE_FLAG_BIAS_PULL_UP)
225262306a36Sopenharmony_ci		info_v1->flags |= GPIOLINE_FLAG_BIAS_PULL_UP;
225362306a36Sopenharmony_ci	if (flagsv2 & GPIO_V2_LINE_FLAG_BIAS_PULL_DOWN)
225462306a36Sopenharmony_ci		info_v1->flags |= GPIOLINE_FLAG_BIAS_PULL_DOWN;
225562306a36Sopenharmony_ci	if (flagsv2 & GPIO_V2_LINE_FLAG_BIAS_DISABLED)
225662306a36Sopenharmony_ci		info_v1->flags |= GPIOLINE_FLAG_BIAS_DISABLE;
225762306a36Sopenharmony_ci}
225862306a36Sopenharmony_ci
225962306a36Sopenharmony_cistatic void gpio_v2_line_info_changed_to_v1(
226062306a36Sopenharmony_ci		struct gpio_v2_line_info_changed *lic_v2,
226162306a36Sopenharmony_ci		struct gpioline_info_changed *lic_v1)
226262306a36Sopenharmony_ci{
226362306a36Sopenharmony_ci	memset(lic_v1, 0, sizeof(*lic_v1));
226462306a36Sopenharmony_ci	gpio_v2_line_info_to_v1(&lic_v2->info, &lic_v1->info);
226562306a36Sopenharmony_ci	lic_v1->timestamp = lic_v2->timestamp_ns;
226662306a36Sopenharmony_ci	lic_v1->event_type = lic_v2->event_type;
226762306a36Sopenharmony_ci}
226862306a36Sopenharmony_ci
226962306a36Sopenharmony_ci#endif /* CONFIG_GPIO_CDEV_V1 */
227062306a36Sopenharmony_ci
227162306a36Sopenharmony_cistatic void gpio_desc_to_lineinfo(struct gpio_desc *desc,
227262306a36Sopenharmony_ci				  struct gpio_v2_line_info *info)
227362306a36Sopenharmony_ci{
227462306a36Sopenharmony_ci	struct gpio_chip *gc = desc->gdev->chip;
227562306a36Sopenharmony_ci	bool ok_for_pinctrl;
227662306a36Sopenharmony_ci	unsigned long flags;
227762306a36Sopenharmony_ci	u32 debounce_period_us;
227862306a36Sopenharmony_ci	unsigned int num_attrs = 0;
227962306a36Sopenharmony_ci
228062306a36Sopenharmony_ci	memset(info, 0, sizeof(*info));
228162306a36Sopenharmony_ci	info->offset = gpio_chip_hwgpio(desc);
228262306a36Sopenharmony_ci
228362306a36Sopenharmony_ci	/*
228462306a36Sopenharmony_ci	 * This function takes a mutex so we must check this before taking
228562306a36Sopenharmony_ci	 * the spinlock.
228662306a36Sopenharmony_ci	 *
228762306a36Sopenharmony_ci	 * FIXME: find a non-racy way to retrieve this information. Maybe a
228862306a36Sopenharmony_ci	 * lock common to both frameworks?
228962306a36Sopenharmony_ci	 */
229062306a36Sopenharmony_ci	ok_for_pinctrl =
229162306a36Sopenharmony_ci		pinctrl_gpio_can_use_line(gc->base + info->offset);
229262306a36Sopenharmony_ci
229362306a36Sopenharmony_ci	spin_lock_irqsave(&gpio_lock, flags);
229462306a36Sopenharmony_ci
229562306a36Sopenharmony_ci	if (desc->name)
229662306a36Sopenharmony_ci		strscpy(info->name, desc->name, sizeof(info->name));
229762306a36Sopenharmony_ci
229862306a36Sopenharmony_ci	if (desc->label)
229962306a36Sopenharmony_ci		strscpy(info->consumer, desc->label, sizeof(info->consumer));
230062306a36Sopenharmony_ci
230162306a36Sopenharmony_ci	/*
230262306a36Sopenharmony_ci	 * Userspace only need to know that the kernel is using this GPIO so
230362306a36Sopenharmony_ci	 * it can't use it.
230462306a36Sopenharmony_ci	 */
230562306a36Sopenharmony_ci	info->flags = 0;
230662306a36Sopenharmony_ci	if (test_bit(FLAG_REQUESTED, &desc->flags) ||
230762306a36Sopenharmony_ci	    test_bit(FLAG_IS_HOGGED, &desc->flags) ||
230862306a36Sopenharmony_ci	    test_bit(FLAG_USED_AS_IRQ, &desc->flags) ||
230962306a36Sopenharmony_ci	    test_bit(FLAG_EXPORT, &desc->flags) ||
231062306a36Sopenharmony_ci	    test_bit(FLAG_SYSFS, &desc->flags) ||
231162306a36Sopenharmony_ci	    !gpiochip_line_is_valid(gc, info->offset) ||
231262306a36Sopenharmony_ci	    !ok_for_pinctrl)
231362306a36Sopenharmony_ci		info->flags |= GPIO_V2_LINE_FLAG_USED;
231462306a36Sopenharmony_ci
231562306a36Sopenharmony_ci	if (test_bit(FLAG_IS_OUT, &desc->flags))
231662306a36Sopenharmony_ci		info->flags |= GPIO_V2_LINE_FLAG_OUTPUT;
231762306a36Sopenharmony_ci	else
231862306a36Sopenharmony_ci		info->flags |= GPIO_V2_LINE_FLAG_INPUT;
231962306a36Sopenharmony_ci
232062306a36Sopenharmony_ci	if (test_bit(FLAG_ACTIVE_LOW, &desc->flags))
232162306a36Sopenharmony_ci		info->flags |= GPIO_V2_LINE_FLAG_ACTIVE_LOW;
232262306a36Sopenharmony_ci
232362306a36Sopenharmony_ci	if (test_bit(FLAG_OPEN_DRAIN, &desc->flags))
232462306a36Sopenharmony_ci		info->flags |= GPIO_V2_LINE_FLAG_OPEN_DRAIN;
232562306a36Sopenharmony_ci	if (test_bit(FLAG_OPEN_SOURCE, &desc->flags))
232662306a36Sopenharmony_ci		info->flags |= GPIO_V2_LINE_FLAG_OPEN_SOURCE;
232762306a36Sopenharmony_ci
232862306a36Sopenharmony_ci	if (test_bit(FLAG_BIAS_DISABLE, &desc->flags))
232962306a36Sopenharmony_ci		info->flags |= GPIO_V2_LINE_FLAG_BIAS_DISABLED;
233062306a36Sopenharmony_ci	if (test_bit(FLAG_PULL_DOWN, &desc->flags))
233162306a36Sopenharmony_ci		info->flags |= GPIO_V2_LINE_FLAG_BIAS_PULL_DOWN;
233262306a36Sopenharmony_ci	if (test_bit(FLAG_PULL_UP, &desc->flags))
233362306a36Sopenharmony_ci		info->flags |= GPIO_V2_LINE_FLAG_BIAS_PULL_UP;
233462306a36Sopenharmony_ci
233562306a36Sopenharmony_ci	if (test_bit(FLAG_EDGE_RISING, &desc->flags))
233662306a36Sopenharmony_ci		info->flags |= GPIO_V2_LINE_FLAG_EDGE_RISING;
233762306a36Sopenharmony_ci	if (test_bit(FLAG_EDGE_FALLING, &desc->flags))
233862306a36Sopenharmony_ci		info->flags |= GPIO_V2_LINE_FLAG_EDGE_FALLING;
233962306a36Sopenharmony_ci
234062306a36Sopenharmony_ci	if (test_bit(FLAG_EVENT_CLOCK_REALTIME, &desc->flags))
234162306a36Sopenharmony_ci		info->flags |= GPIO_V2_LINE_FLAG_EVENT_CLOCK_REALTIME;
234262306a36Sopenharmony_ci	else if (test_bit(FLAG_EVENT_CLOCK_HTE, &desc->flags))
234362306a36Sopenharmony_ci		info->flags |= GPIO_V2_LINE_FLAG_EVENT_CLOCK_HTE;
234462306a36Sopenharmony_ci
234562306a36Sopenharmony_ci	debounce_period_us = READ_ONCE(desc->debounce_period_us);
234662306a36Sopenharmony_ci	if (debounce_period_us) {
234762306a36Sopenharmony_ci		info->attrs[num_attrs].id = GPIO_V2_LINE_ATTR_ID_DEBOUNCE;
234862306a36Sopenharmony_ci		info->attrs[num_attrs].debounce_period_us = debounce_period_us;
234962306a36Sopenharmony_ci		num_attrs++;
235062306a36Sopenharmony_ci	}
235162306a36Sopenharmony_ci	info->num_attrs = num_attrs;
235262306a36Sopenharmony_ci
235362306a36Sopenharmony_ci	spin_unlock_irqrestore(&gpio_lock, flags);
235462306a36Sopenharmony_ci}
235562306a36Sopenharmony_ci
235662306a36Sopenharmony_cistruct gpio_chardev_data {
235762306a36Sopenharmony_ci	struct gpio_device *gdev;
235862306a36Sopenharmony_ci	wait_queue_head_t wait;
235962306a36Sopenharmony_ci	DECLARE_KFIFO(events, struct gpio_v2_line_info_changed, 32);
236062306a36Sopenharmony_ci	struct notifier_block lineinfo_changed_nb;
236162306a36Sopenharmony_ci	struct notifier_block device_unregistered_nb;
236262306a36Sopenharmony_ci	unsigned long *watched_lines;
236362306a36Sopenharmony_ci#ifdef CONFIG_GPIO_CDEV_V1
236462306a36Sopenharmony_ci	atomic_t watch_abi_version;
236562306a36Sopenharmony_ci#endif
236662306a36Sopenharmony_ci};
236762306a36Sopenharmony_ci
236862306a36Sopenharmony_cistatic int chipinfo_get(struct gpio_chardev_data *cdev, void __user *ip)
236962306a36Sopenharmony_ci{
237062306a36Sopenharmony_ci	struct gpio_device *gdev = cdev->gdev;
237162306a36Sopenharmony_ci	struct gpiochip_info chipinfo;
237262306a36Sopenharmony_ci
237362306a36Sopenharmony_ci	memset(&chipinfo, 0, sizeof(chipinfo));
237462306a36Sopenharmony_ci
237562306a36Sopenharmony_ci	strscpy(chipinfo.name, dev_name(&gdev->dev), sizeof(chipinfo.name));
237662306a36Sopenharmony_ci	strscpy(chipinfo.label, gdev->label, sizeof(chipinfo.label));
237762306a36Sopenharmony_ci	chipinfo.lines = gdev->ngpio;
237862306a36Sopenharmony_ci	if (copy_to_user(ip, &chipinfo, sizeof(chipinfo)))
237962306a36Sopenharmony_ci		return -EFAULT;
238062306a36Sopenharmony_ci	return 0;
238162306a36Sopenharmony_ci}
238262306a36Sopenharmony_ci
238362306a36Sopenharmony_ci#ifdef CONFIG_GPIO_CDEV_V1
238462306a36Sopenharmony_ci/*
238562306a36Sopenharmony_ci * returns 0 if the versions match, else the previously selected ABI version
238662306a36Sopenharmony_ci */
238762306a36Sopenharmony_cistatic int lineinfo_ensure_abi_version(struct gpio_chardev_data *cdata,
238862306a36Sopenharmony_ci				       unsigned int version)
238962306a36Sopenharmony_ci{
239062306a36Sopenharmony_ci	int abiv = atomic_cmpxchg(&cdata->watch_abi_version, 0, version);
239162306a36Sopenharmony_ci
239262306a36Sopenharmony_ci	if (abiv == version)
239362306a36Sopenharmony_ci		return 0;
239462306a36Sopenharmony_ci
239562306a36Sopenharmony_ci	return abiv;
239662306a36Sopenharmony_ci}
239762306a36Sopenharmony_ci
239862306a36Sopenharmony_cistatic int lineinfo_get_v1(struct gpio_chardev_data *cdev, void __user *ip,
239962306a36Sopenharmony_ci			   bool watch)
240062306a36Sopenharmony_ci{
240162306a36Sopenharmony_ci	struct gpio_desc *desc;
240262306a36Sopenharmony_ci	struct gpioline_info lineinfo;
240362306a36Sopenharmony_ci	struct gpio_v2_line_info lineinfo_v2;
240462306a36Sopenharmony_ci
240562306a36Sopenharmony_ci	if (copy_from_user(&lineinfo, ip, sizeof(lineinfo)))
240662306a36Sopenharmony_ci		return -EFAULT;
240762306a36Sopenharmony_ci
240862306a36Sopenharmony_ci	/* this doubles as a range check on line_offset */
240962306a36Sopenharmony_ci	desc = gpiochip_get_desc(cdev->gdev->chip, lineinfo.line_offset);
241062306a36Sopenharmony_ci	if (IS_ERR(desc))
241162306a36Sopenharmony_ci		return PTR_ERR(desc);
241262306a36Sopenharmony_ci
241362306a36Sopenharmony_ci	if (watch) {
241462306a36Sopenharmony_ci		if (lineinfo_ensure_abi_version(cdev, 1))
241562306a36Sopenharmony_ci			return -EPERM;
241662306a36Sopenharmony_ci
241762306a36Sopenharmony_ci		if (test_and_set_bit(lineinfo.line_offset, cdev->watched_lines))
241862306a36Sopenharmony_ci			return -EBUSY;
241962306a36Sopenharmony_ci	}
242062306a36Sopenharmony_ci
242162306a36Sopenharmony_ci	gpio_desc_to_lineinfo(desc, &lineinfo_v2);
242262306a36Sopenharmony_ci	gpio_v2_line_info_to_v1(&lineinfo_v2, &lineinfo);
242362306a36Sopenharmony_ci
242462306a36Sopenharmony_ci	if (copy_to_user(ip, &lineinfo, sizeof(lineinfo))) {
242562306a36Sopenharmony_ci		if (watch)
242662306a36Sopenharmony_ci			clear_bit(lineinfo.line_offset, cdev->watched_lines);
242762306a36Sopenharmony_ci		return -EFAULT;
242862306a36Sopenharmony_ci	}
242962306a36Sopenharmony_ci
243062306a36Sopenharmony_ci	return 0;
243162306a36Sopenharmony_ci}
243262306a36Sopenharmony_ci#endif
243362306a36Sopenharmony_ci
243462306a36Sopenharmony_cistatic int lineinfo_get(struct gpio_chardev_data *cdev, void __user *ip,
243562306a36Sopenharmony_ci			bool watch)
243662306a36Sopenharmony_ci{
243762306a36Sopenharmony_ci	struct gpio_desc *desc;
243862306a36Sopenharmony_ci	struct gpio_v2_line_info lineinfo;
243962306a36Sopenharmony_ci
244062306a36Sopenharmony_ci	if (copy_from_user(&lineinfo, ip, sizeof(lineinfo)))
244162306a36Sopenharmony_ci		return -EFAULT;
244262306a36Sopenharmony_ci
244362306a36Sopenharmony_ci	if (memchr_inv(lineinfo.padding, 0, sizeof(lineinfo.padding)))
244462306a36Sopenharmony_ci		return -EINVAL;
244562306a36Sopenharmony_ci
244662306a36Sopenharmony_ci	desc = gpiochip_get_desc(cdev->gdev->chip, lineinfo.offset);
244762306a36Sopenharmony_ci	if (IS_ERR(desc))
244862306a36Sopenharmony_ci		return PTR_ERR(desc);
244962306a36Sopenharmony_ci
245062306a36Sopenharmony_ci	if (watch) {
245162306a36Sopenharmony_ci#ifdef CONFIG_GPIO_CDEV_V1
245262306a36Sopenharmony_ci		if (lineinfo_ensure_abi_version(cdev, 2))
245362306a36Sopenharmony_ci			return -EPERM;
245462306a36Sopenharmony_ci#endif
245562306a36Sopenharmony_ci		if (test_and_set_bit(lineinfo.offset, cdev->watched_lines))
245662306a36Sopenharmony_ci			return -EBUSY;
245762306a36Sopenharmony_ci	}
245862306a36Sopenharmony_ci	gpio_desc_to_lineinfo(desc, &lineinfo);
245962306a36Sopenharmony_ci
246062306a36Sopenharmony_ci	if (copy_to_user(ip, &lineinfo, sizeof(lineinfo))) {
246162306a36Sopenharmony_ci		if (watch)
246262306a36Sopenharmony_ci			clear_bit(lineinfo.offset, cdev->watched_lines);
246362306a36Sopenharmony_ci		return -EFAULT;
246462306a36Sopenharmony_ci	}
246562306a36Sopenharmony_ci
246662306a36Sopenharmony_ci	return 0;
246762306a36Sopenharmony_ci}
246862306a36Sopenharmony_ci
246962306a36Sopenharmony_cistatic int lineinfo_unwatch(struct gpio_chardev_data *cdev, void __user *ip)
247062306a36Sopenharmony_ci{
247162306a36Sopenharmony_ci	__u32 offset;
247262306a36Sopenharmony_ci
247362306a36Sopenharmony_ci	if (copy_from_user(&offset, ip, sizeof(offset)))
247462306a36Sopenharmony_ci		return -EFAULT;
247562306a36Sopenharmony_ci
247662306a36Sopenharmony_ci	if (offset >= cdev->gdev->ngpio)
247762306a36Sopenharmony_ci		return -EINVAL;
247862306a36Sopenharmony_ci
247962306a36Sopenharmony_ci	if (!test_and_clear_bit(offset, cdev->watched_lines))
248062306a36Sopenharmony_ci		return -EBUSY;
248162306a36Sopenharmony_ci
248262306a36Sopenharmony_ci	return 0;
248362306a36Sopenharmony_ci}
248462306a36Sopenharmony_ci
248562306a36Sopenharmony_cistatic long gpio_ioctl_unlocked(struct file *file, unsigned int cmd, unsigned long arg)
248662306a36Sopenharmony_ci{
248762306a36Sopenharmony_ci	struct gpio_chardev_data *cdev = file->private_data;
248862306a36Sopenharmony_ci	struct gpio_device *gdev = cdev->gdev;
248962306a36Sopenharmony_ci	void __user *ip = (void __user *)arg;
249062306a36Sopenharmony_ci
249162306a36Sopenharmony_ci	/* We fail any subsequent ioctl():s when the chip is gone */
249262306a36Sopenharmony_ci	if (!gdev->chip)
249362306a36Sopenharmony_ci		return -ENODEV;
249462306a36Sopenharmony_ci
249562306a36Sopenharmony_ci	/* Fill in the struct and pass to userspace */
249662306a36Sopenharmony_ci	switch (cmd) {
249762306a36Sopenharmony_ci	case GPIO_GET_CHIPINFO_IOCTL:
249862306a36Sopenharmony_ci		return chipinfo_get(cdev, ip);
249962306a36Sopenharmony_ci#ifdef CONFIG_GPIO_CDEV_V1
250062306a36Sopenharmony_ci	case GPIO_GET_LINEHANDLE_IOCTL:
250162306a36Sopenharmony_ci		return linehandle_create(gdev, ip);
250262306a36Sopenharmony_ci	case GPIO_GET_LINEEVENT_IOCTL:
250362306a36Sopenharmony_ci		return lineevent_create(gdev, ip);
250462306a36Sopenharmony_ci	case GPIO_GET_LINEINFO_IOCTL:
250562306a36Sopenharmony_ci		return lineinfo_get_v1(cdev, ip, false);
250662306a36Sopenharmony_ci	case GPIO_GET_LINEINFO_WATCH_IOCTL:
250762306a36Sopenharmony_ci		return lineinfo_get_v1(cdev, ip, true);
250862306a36Sopenharmony_ci#endif /* CONFIG_GPIO_CDEV_V1 */
250962306a36Sopenharmony_ci	case GPIO_V2_GET_LINEINFO_IOCTL:
251062306a36Sopenharmony_ci		return lineinfo_get(cdev, ip, false);
251162306a36Sopenharmony_ci	case GPIO_V2_GET_LINEINFO_WATCH_IOCTL:
251262306a36Sopenharmony_ci		return lineinfo_get(cdev, ip, true);
251362306a36Sopenharmony_ci	case GPIO_V2_GET_LINE_IOCTL:
251462306a36Sopenharmony_ci		return linereq_create(gdev, ip);
251562306a36Sopenharmony_ci	case GPIO_GET_LINEINFO_UNWATCH_IOCTL:
251662306a36Sopenharmony_ci		return lineinfo_unwatch(cdev, ip);
251762306a36Sopenharmony_ci	default:
251862306a36Sopenharmony_ci		return -EINVAL;
251962306a36Sopenharmony_ci	}
252062306a36Sopenharmony_ci}
252162306a36Sopenharmony_ci
252262306a36Sopenharmony_ci/*
252362306a36Sopenharmony_ci * gpio_ioctl() - ioctl handler for the GPIO chardev
252462306a36Sopenharmony_ci */
252562306a36Sopenharmony_cistatic long gpio_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
252662306a36Sopenharmony_ci{
252762306a36Sopenharmony_ci	struct gpio_chardev_data *cdev = file->private_data;
252862306a36Sopenharmony_ci
252962306a36Sopenharmony_ci	return call_ioctl_locked(file, cmd, arg, cdev->gdev,
253062306a36Sopenharmony_ci				 gpio_ioctl_unlocked);
253162306a36Sopenharmony_ci}
253262306a36Sopenharmony_ci
253362306a36Sopenharmony_ci#ifdef CONFIG_COMPAT
253462306a36Sopenharmony_cistatic long gpio_ioctl_compat(struct file *file, unsigned int cmd,
253562306a36Sopenharmony_ci			      unsigned long arg)
253662306a36Sopenharmony_ci{
253762306a36Sopenharmony_ci	return gpio_ioctl(file, cmd, (unsigned long)compat_ptr(arg));
253862306a36Sopenharmony_ci}
253962306a36Sopenharmony_ci#endif
254062306a36Sopenharmony_ci
254162306a36Sopenharmony_cistatic int lineinfo_changed_notify(struct notifier_block *nb,
254262306a36Sopenharmony_ci				   unsigned long action, void *data)
254362306a36Sopenharmony_ci{
254462306a36Sopenharmony_ci	struct gpio_chardev_data *cdev =
254562306a36Sopenharmony_ci		container_of(nb, struct gpio_chardev_data, lineinfo_changed_nb);
254662306a36Sopenharmony_ci	struct gpio_v2_line_info_changed chg;
254762306a36Sopenharmony_ci	struct gpio_desc *desc = data;
254862306a36Sopenharmony_ci	int ret;
254962306a36Sopenharmony_ci
255062306a36Sopenharmony_ci	if (!test_bit(gpio_chip_hwgpio(desc), cdev->watched_lines))
255162306a36Sopenharmony_ci		return NOTIFY_DONE;
255262306a36Sopenharmony_ci
255362306a36Sopenharmony_ci	memset(&chg, 0, sizeof(chg));
255462306a36Sopenharmony_ci	chg.event_type = action;
255562306a36Sopenharmony_ci	chg.timestamp_ns = ktime_get_ns();
255662306a36Sopenharmony_ci	gpio_desc_to_lineinfo(desc, &chg.info);
255762306a36Sopenharmony_ci
255862306a36Sopenharmony_ci	ret = kfifo_in_spinlocked(&cdev->events, &chg, 1, &cdev->wait.lock);
255962306a36Sopenharmony_ci	if (ret)
256062306a36Sopenharmony_ci		wake_up_poll(&cdev->wait, EPOLLIN);
256162306a36Sopenharmony_ci	else
256262306a36Sopenharmony_ci		pr_debug_ratelimited("lineinfo event FIFO is full - event dropped\n");
256362306a36Sopenharmony_ci
256462306a36Sopenharmony_ci	return NOTIFY_OK;
256562306a36Sopenharmony_ci}
256662306a36Sopenharmony_ci
256762306a36Sopenharmony_cistatic int gpio_device_unregistered_notify(struct notifier_block *nb,
256862306a36Sopenharmony_ci					   unsigned long action, void *data)
256962306a36Sopenharmony_ci{
257062306a36Sopenharmony_ci	struct gpio_chardev_data *cdev = container_of(nb,
257162306a36Sopenharmony_ci						      struct gpio_chardev_data,
257262306a36Sopenharmony_ci						      device_unregistered_nb);
257362306a36Sopenharmony_ci
257462306a36Sopenharmony_ci	wake_up_poll(&cdev->wait, EPOLLIN | EPOLLERR);
257562306a36Sopenharmony_ci
257662306a36Sopenharmony_ci	return NOTIFY_OK;
257762306a36Sopenharmony_ci}
257862306a36Sopenharmony_ci
257962306a36Sopenharmony_cistatic __poll_t lineinfo_watch_poll_unlocked(struct file *file,
258062306a36Sopenharmony_ci					     struct poll_table_struct *pollt)
258162306a36Sopenharmony_ci{
258262306a36Sopenharmony_ci	struct gpio_chardev_data *cdev = file->private_data;
258362306a36Sopenharmony_ci	__poll_t events = 0;
258462306a36Sopenharmony_ci
258562306a36Sopenharmony_ci	if (!cdev->gdev->chip)
258662306a36Sopenharmony_ci		return EPOLLHUP | EPOLLERR;
258762306a36Sopenharmony_ci
258862306a36Sopenharmony_ci	poll_wait(file, &cdev->wait, pollt);
258962306a36Sopenharmony_ci
259062306a36Sopenharmony_ci	if (!kfifo_is_empty_spinlocked_noirqsave(&cdev->events,
259162306a36Sopenharmony_ci						 &cdev->wait.lock))
259262306a36Sopenharmony_ci		events = EPOLLIN | EPOLLRDNORM;
259362306a36Sopenharmony_ci
259462306a36Sopenharmony_ci	return events;
259562306a36Sopenharmony_ci}
259662306a36Sopenharmony_ci
259762306a36Sopenharmony_cistatic __poll_t lineinfo_watch_poll(struct file *file,
259862306a36Sopenharmony_ci				    struct poll_table_struct *pollt)
259962306a36Sopenharmony_ci{
260062306a36Sopenharmony_ci	struct gpio_chardev_data *cdev = file->private_data;
260162306a36Sopenharmony_ci
260262306a36Sopenharmony_ci	return call_poll_locked(file, pollt, cdev->gdev,
260362306a36Sopenharmony_ci				lineinfo_watch_poll_unlocked);
260462306a36Sopenharmony_ci}
260562306a36Sopenharmony_ci
260662306a36Sopenharmony_cistatic ssize_t lineinfo_watch_read_unlocked(struct file *file, char __user *buf,
260762306a36Sopenharmony_ci					    size_t count, loff_t *off)
260862306a36Sopenharmony_ci{
260962306a36Sopenharmony_ci	struct gpio_chardev_data *cdev = file->private_data;
261062306a36Sopenharmony_ci	struct gpio_v2_line_info_changed event;
261162306a36Sopenharmony_ci	ssize_t bytes_read = 0;
261262306a36Sopenharmony_ci	int ret;
261362306a36Sopenharmony_ci	size_t event_size;
261462306a36Sopenharmony_ci
261562306a36Sopenharmony_ci	if (!cdev->gdev->chip)
261662306a36Sopenharmony_ci		return -ENODEV;
261762306a36Sopenharmony_ci
261862306a36Sopenharmony_ci#ifndef CONFIG_GPIO_CDEV_V1
261962306a36Sopenharmony_ci	event_size = sizeof(struct gpio_v2_line_info_changed);
262062306a36Sopenharmony_ci	if (count < event_size)
262162306a36Sopenharmony_ci		return -EINVAL;
262262306a36Sopenharmony_ci#endif
262362306a36Sopenharmony_ci
262462306a36Sopenharmony_ci	do {
262562306a36Sopenharmony_ci		spin_lock(&cdev->wait.lock);
262662306a36Sopenharmony_ci		if (kfifo_is_empty(&cdev->events)) {
262762306a36Sopenharmony_ci			if (bytes_read) {
262862306a36Sopenharmony_ci				spin_unlock(&cdev->wait.lock);
262962306a36Sopenharmony_ci				return bytes_read;
263062306a36Sopenharmony_ci			}
263162306a36Sopenharmony_ci
263262306a36Sopenharmony_ci			if (file->f_flags & O_NONBLOCK) {
263362306a36Sopenharmony_ci				spin_unlock(&cdev->wait.lock);
263462306a36Sopenharmony_ci				return -EAGAIN;
263562306a36Sopenharmony_ci			}
263662306a36Sopenharmony_ci
263762306a36Sopenharmony_ci			ret = wait_event_interruptible_locked(cdev->wait,
263862306a36Sopenharmony_ci					!kfifo_is_empty(&cdev->events));
263962306a36Sopenharmony_ci			if (ret) {
264062306a36Sopenharmony_ci				spin_unlock(&cdev->wait.lock);
264162306a36Sopenharmony_ci				return ret;
264262306a36Sopenharmony_ci			}
264362306a36Sopenharmony_ci		}
264462306a36Sopenharmony_ci#ifdef CONFIG_GPIO_CDEV_V1
264562306a36Sopenharmony_ci		/* must be after kfifo check so watch_abi_version is set */
264662306a36Sopenharmony_ci		if (atomic_read(&cdev->watch_abi_version) == 2)
264762306a36Sopenharmony_ci			event_size = sizeof(struct gpio_v2_line_info_changed);
264862306a36Sopenharmony_ci		else
264962306a36Sopenharmony_ci			event_size = sizeof(struct gpioline_info_changed);
265062306a36Sopenharmony_ci		if (count < event_size) {
265162306a36Sopenharmony_ci			spin_unlock(&cdev->wait.lock);
265262306a36Sopenharmony_ci			return -EINVAL;
265362306a36Sopenharmony_ci		}
265462306a36Sopenharmony_ci#endif
265562306a36Sopenharmony_ci		ret = kfifo_out(&cdev->events, &event, 1);
265662306a36Sopenharmony_ci		spin_unlock(&cdev->wait.lock);
265762306a36Sopenharmony_ci		if (ret != 1) {
265862306a36Sopenharmony_ci			ret = -EIO;
265962306a36Sopenharmony_ci			break;
266062306a36Sopenharmony_ci			/* We should never get here. See lineevent_read(). */
266162306a36Sopenharmony_ci		}
266262306a36Sopenharmony_ci
266362306a36Sopenharmony_ci#ifdef CONFIG_GPIO_CDEV_V1
266462306a36Sopenharmony_ci		if (event_size == sizeof(struct gpio_v2_line_info_changed)) {
266562306a36Sopenharmony_ci			if (copy_to_user(buf + bytes_read, &event, event_size))
266662306a36Sopenharmony_ci				return -EFAULT;
266762306a36Sopenharmony_ci		} else {
266862306a36Sopenharmony_ci			struct gpioline_info_changed event_v1;
266962306a36Sopenharmony_ci
267062306a36Sopenharmony_ci			gpio_v2_line_info_changed_to_v1(&event, &event_v1);
267162306a36Sopenharmony_ci			if (copy_to_user(buf + bytes_read, &event_v1,
267262306a36Sopenharmony_ci					 event_size))
267362306a36Sopenharmony_ci				return -EFAULT;
267462306a36Sopenharmony_ci		}
267562306a36Sopenharmony_ci#else
267662306a36Sopenharmony_ci		if (copy_to_user(buf + bytes_read, &event, event_size))
267762306a36Sopenharmony_ci			return -EFAULT;
267862306a36Sopenharmony_ci#endif
267962306a36Sopenharmony_ci		bytes_read += event_size;
268062306a36Sopenharmony_ci	} while (count >= bytes_read + sizeof(event));
268162306a36Sopenharmony_ci
268262306a36Sopenharmony_ci	return bytes_read;
268362306a36Sopenharmony_ci}
268462306a36Sopenharmony_ci
268562306a36Sopenharmony_cistatic ssize_t lineinfo_watch_read(struct file *file, char __user *buf,
268662306a36Sopenharmony_ci				   size_t count, loff_t *off)
268762306a36Sopenharmony_ci{
268862306a36Sopenharmony_ci	struct gpio_chardev_data *cdev = file->private_data;
268962306a36Sopenharmony_ci
269062306a36Sopenharmony_ci	return call_read_locked(file, buf, count, off, cdev->gdev,
269162306a36Sopenharmony_ci				lineinfo_watch_read_unlocked);
269262306a36Sopenharmony_ci}
269362306a36Sopenharmony_ci
269462306a36Sopenharmony_ci/**
269562306a36Sopenharmony_ci * gpio_chrdev_open() - open the chardev for ioctl operations
269662306a36Sopenharmony_ci * @inode: inode for this chardev
269762306a36Sopenharmony_ci * @file: file struct for storing private data
269862306a36Sopenharmony_ci * Returns 0 on success
269962306a36Sopenharmony_ci */
270062306a36Sopenharmony_cistatic int gpio_chrdev_open(struct inode *inode, struct file *file)
270162306a36Sopenharmony_ci{
270262306a36Sopenharmony_ci	struct gpio_device *gdev = container_of(inode->i_cdev,
270362306a36Sopenharmony_ci						struct gpio_device, chrdev);
270462306a36Sopenharmony_ci	struct gpio_chardev_data *cdev;
270562306a36Sopenharmony_ci	int ret = -ENOMEM;
270662306a36Sopenharmony_ci
270762306a36Sopenharmony_ci	down_read(&gdev->sem);
270862306a36Sopenharmony_ci
270962306a36Sopenharmony_ci	/* Fail on open if the backing gpiochip is gone */
271062306a36Sopenharmony_ci	if (!gdev->chip) {
271162306a36Sopenharmony_ci		ret = -ENODEV;
271262306a36Sopenharmony_ci		goto out_unlock;
271362306a36Sopenharmony_ci	}
271462306a36Sopenharmony_ci
271562306a36Sopenharmony_ci	cdev = kzalloc(sizeof(*cdev), GFP_KERNEL);
271662306a36Sopenharmony_ci	if (!cdev)
271762306a36Sopenharmony_ci		goto out_unlock;
271862306a36Sopenharmony_ci
271962306a36Sopenharmony_ci	cdev->watched_lines = bitmap_zalloc(gdev->chip->ngpio, GFP_KERNEL);
272062306a36Sopenharmony_ci	if (!cdev->watched_lines)
272162306a36Sopenharmony_ci		goto out_free_cdev;
272262306a36Sopenharmony_ci
272362306a36Sopenharmony_ci	init_waitqueue_head(&cdev->wait);
272462306a36Sopenharmony_ci	INIT_KFIFO(cdev->events);
272562306a36Sopenharmony_ci	cdev->gdev = gpio_device_get(gdev);
272662306a36Sopenharmony_ci
272762306a36Sopenharmony_ci	cdev->lineinfo_changed_nb.notifier_call = lineinfo_changed_notify;
272862306a36Sopenharmony_ci	ret = blocking_notifier_chain_register(&gdev->line_state_notifier,
272962306a36Sopenharmony_ci					       &cdev->lineinfo_changed_nb);
273062306a36Sopenharmony_ci	if (ret)
273162306a36Sopenharmony_ci		goto out_free_bitmap;
273262306a36Sopenharmony_ci
273362306a36Sopenharmony_ci	cdev->device_unregistered_nb.notifier_call =
273462306a36Sopenharmony_ci					gpio_device_unregistered_notify;
273562306a36Sopenharmony_ci	ret = blocking_notifier_chain_register(&gdev->device_notifier,
273662306a36Sopenharmony_ci					       &cdev->device_unregistered_nb);
273762306a36Sopenharmony_ci	if (ret)
273862306a36Sopenharmony_ci		goto out_unregister_line_notifier;
273962306a36Sopenharmony_ci
274062306a36Sopenharmony_ci	file->private_data = cdev;
274162306a36Sopenharmony_ci
274262306a36Sopenharmony_ci	ret = nonseekable_open(inode, file);
274362306a36Sopenharmony_ci	if (ret)
274462306a36Sopenharmony_ci		goto out_unregister_device_notifier;
274562306a36Sopenharmony_ci
274662306a36Sopenharmony_ci	up_read(&gdev->sem);
274762306a36Sopenharmony_ci
274862306a36Sopenharmony_ci	return ret;
274962306a36Sopenharmony_ci
275062306a36Sopenharmony_ciout_unregister_device_notifier:
275162306a36Sopenharmony_ci	blocking_notifier_chain_unregister(&gdev->device_notifier,
275262306a36Sopenharmony_ci					   &cdev->device_unregistered_nb);
275362306a36Sopenharmony_ciout_unregister_line_notifier:
275462306a36Sopenharmony_ci	blocking_notifier_chain_unregister(&gdev->line_state_notifier,
275562306a36Sopenharmony_ci					   &cdev->lineinfo_changed_nb);
275662306a36Sopenharmony_ciout_free_bitmap:
275762306a36Sopenharmony_ci	gpio_device_put(gdev);
275862306a36Sopenharmony_ci	bitmap_free(cdev->watched_lines);
275962306a36Sopenharmony_ciout_free_cdev:
276062306a36Sopenharmony_ci	kfree(cdev);
276162306a36Sopenharmony_ciout_unlock:
276262306a36Sopenharmony_ci	up_read(&gdev->sem);
276362306a36Sopenharmony_ci	return ret;
276462306a36Sopenharmony_ci}
276562306a36Sopenharmony_ci
276662306a36Sopenharmony_ci/**
276762306a36Sopenharmony_ci * gpio_chrdev_release() - close chardev after ioctl operations
276862306a36Sopenharmony_ci * @inode: inode for this chardev
276962306a36Sopenharmony_ci * @file: file struct for storing private data
277062306a36Sopenharmony_ci * Returns 0 on success
277162306a36Sopenharmony_ci */
277262306a36Sopenharmony_cistatic int gpio_chrdev_release(struct inode *inode, struct file *file)
277362306a36Sopenharmony_ci{
277462306a36Sopenharmony_ci	struct gpio_chardev_data *cdev = file->private_data;
277562306a36Sopenharmony_ci	struct gpio_device *gdev = cdev->gdev;
277662306a36Sopenharmony_ci
277762306a36Sopenharmony_ci	bitmap_free(cdev->watched_lines);
277862306a36Sopenharmony_ci	blocking_notifier_chain_unregister(&gdev->device_notifier,
277962306a36Sopenharmony_ci					   &cdev->device_unregistered_nb);
278062306a36Sopenharmony_ci	blocking_notifier_chain_unregister(&gdev->line_state_notifier,
278162306a36Sopenharmony_ci					   &cdev->lineinfo_changed_nb);
278262306a36Sopenharmony_ci	gpio_device_put(gdev);
278362306a36Sopenharmony_ci	kfree(cdev);
278462306a36Sopenharmony_ci
278562306a36Sopenharmony_ci	return 0;
278662306a36Sopenharmony_ci}
278762306a36Sopenharmony_ci
278862306a36Sopenharmony_cistatic const struct file_operations gpio_fileops = {
278962306a36Sopenharmony_ci	.release = gpio_chrdev_release,
279062306a36Sopenharmony_ci	.open = gpio_chrdev_open,
279162306a36Sopenharmony_ci	.poll = lineinfo_watch_poll,
279262306a36Sopenharmony_ci	.read = lineinfo_watch_read,
279362306a36Sopenharmony_ci	.owner = THIS_MODULE,
279462306a36Sopenharmony_ci	.llseek = no_llseek,
279562306a36Sopenharmony_ci	.unlocked_ioctl = gpio_ioctl,
279662306a36Sopenharmony_ci#ifdef CONFIG_COMPAT
279762306a36Sopenharmony_ci	.compat_ioctl = gpio_ioctl_compat,
279862306a36Sopenharmony_ci#endif
279962306a36Sopenharmony_ci};
280062306a36Sopenharmony_ci
280162306a36Sopenharmony_ciint gpiolib_cdev_register(struct gpio_device *gdev, dev_t devt)
280262306a36Sopenharmony_ci{
280362306a36Sopenharmony_ci	int ret;
280462306a36Sopenharmony_ci
280562306a36Sopenharmony_ci	cdev_init(&gdev->chrdev, &gpio_fileops);
280662306a36Sopenharmony_ci	gdev->chrdev.owner = THIS_MODULE;
280762306a36Sopenharmony_ci	gdev->dev.devt = MKDEV(MAJOR(devt), gdev->id);
280862306a36Sopenharmony_ci
280962306a36Sopenharmony_ci	ret = cdev_device_add(&gdev->chrdev, &gdev->dev);
281062306a36Sopenharmony_ci	if (ret)
281162306a36Sopenharmony_ci		return ret;
281262306a36Sopenharmony_ci
281362306a36Sopenharmony_ci	chip_dbg(gdev->chip, "added GPIO chardev (%d:%d)\n",
281462306a36Sopenharmony_ci		 MAJOR(devt), gdev->id);
281562306a36Sopenharmony_ci
281662306a36Sopenharmony_ci	return 0;
281762306a36Sopenharmony_ci}
281862306a36Sopenharmony_ci
281962306a36Sopenharmony_civoid gpiolib_cdev_unregister(struct gpio_device *gdev)
282062306a36Sopenharmony_ci{
282162306a36Sopenharmony_ci	cdev_device_del(&gdev->chrdev, &gdev->dev);
282262306a36Sopenharmony_ci	blocking_notifier_call_chain(&gdev->device_notifier, 0, NULL);
282362306a36Sopenharmony_ci}
2824