All of lore.kernel.org
 help / color / mirror / Atom feed
From: lakshmi.sowjanya.d@intel.com
To: linus.walleij@linaro.org
Cc: linux-gpio@vger.kernel.org, bgolaszewski@baylibre.com,
	linux-kernel@vger.kernel.org, mgross@linux.intel.com,
	andriy.shevchenko@linux.intel.com, tamal.saha@intel.com,
	bala.senthil@intel.com, lakshmi.sowjanya.d@intel.com
Subject: [RFC PATCH v1 07/20] gpio: Add output event generation method to GPIOLIB and PMC Driver
Date: Tue, 24 Aug 2021 22:17:48 +0530	[thread overview]
Message-ID: <20210824164801.28896-8-lakshmi.sowjanya.d@intel.com> (raw)
In-Reply-To: <20210824164801.28896-1-lakshmi.sowjanya.d@intel.com>

From: Lakshmi Sowjanya D <lakshmi.sowjanya.d@intel.com>

Intel Timed I/O hardware supports output scheduled in hardware. Enable
this functionality using GPIOlib

Adds GPIOlib generate_output() hook into the driver. The driver is
supplied with a timestamp in terms of realtime system clock (the same
used for input timestamping). The driver must know how to translate this
into a timebase meaningful for the hardware.

Adds userspace write() interface. Output can be selected using the line
event create ioctl. The write() interface takes a single timestamp
event request parameter. An output edge rising or falling is generated
for each event request.

The user application supplies a trigger time in terms of the realtime
clock the driver converts this into the corresponding ART clock value
that is used to 'arm' the output.

Work around device quirk that doesn't allow the output to be explicitly
set. Instead, count the output edges and insert an additional edge as
needed to reset the output to zero.

Co-developed-by: Christopher Hall <christopher.s.hall@intel.com>
Signed-off-by: Christopher Hall <christopher.s.hall@intel.com>
Signed-off-by: Tamal Saha <tamal.saha@intel.com>
Signed-off-by: Lakshmi Sowjanya D <lakshmi.sowjanya.d@intel.com>
Reviewed-by: Mark Gross <mgross@linux.intel.com>
---
 drivers/gpio/gpio-intel-tio-pmc.c | 154 ++++++++++++++++++++++++++++--
 drivers/gpio/gpiolib-cdev.c       |  49 +++++++++-
 include/linux/gpio/driver.h       |   9 ++
 include/uapi/linux/gpio.h         |   8 ++
 4 files changed, 210 insertions(+), 10 deletions(-)

diff --git a/drivers/gpio/gpio-intel-tio-pmc.c b/drivers/gpio/gpio-intel-tio-pmc.c
index 7e5e61054dea..f57f521edc40 100644
--- a/drivers/gpio/gpio-intel-tio-pmc.c
+++ b/drivers/gpio/gpio-intel-tio-pmc.c
@@ -6,6 +6,7 @@
 
 #include <linux/acpi.h>
 #include <linux/debugfs.h>
+#include <linux/delay.h>
 #include <linux/gpio/driver.h>
 #include <linux/module.h>
 #include <linux/mutex.h>
@@ -48,6 +49,7 @@ struct intel_pmc_tio_chip {
 	struct delayed_work input_work;
 	bool input_work_running;
 	bool systime_valid;
+	bool output_high;
 	unsigned int systime_index;
 	struct system_time_snapshot systime_snapshot[INPUT_SNAPSHOT_COUNT];
 	u64 last_event_count;
@@ -123,6 +125,38 @@ static inline void intel_pmc_tio_writel(struct intel_pmc_tio_chip *tio,
 #define INTEL_PMC_TIO_WR_REG(offset, value)(			\
 		intel_pmc_tio_writel((tio), (offset), (value)))
 
+/* Must hold mutex */
+static u32 intel_pmc_tio_disable(struct intel_pmc_tio_chip *tio)
+{
+	u32 ctrl;
+	u64 art;
+
+	ctrl = INTEL_PMC_TIO_RD_REG(TGPIOCTL);
+	if (!(ctrl & TGPIOCTL_DIR) && ctrl & TGPIOCTL_EN) {
+		/* 'compare' value is invalid */
+		art = read_art_time();
+		--art;
+		INTEL_PMC_TIO_WR_REG(TGPIOCOMPV31_0, art & 0xFFFFFFFF);
+		INTEL_PMC_TIO_WR_REG(TGPIOCOMPV63_32, art >> 32);
+		udelay(1);
+		tio->output_high = (INTEL_PMC_TIO_RD_REG(TGPIOEC31_0) & 0x1);
+	}
+
+	if (ctrl & TGPIOCTL_EN) {
+		ctrl &= ~TGPIOCTL_EN;
+		INTEL_PMC_TIO_WR_REG(TGPIOCTL, ctrl);
+	}
+
+	return ctrl;
+}
+
+static void intel_pmc_tio_enable(struct intel_pmc_tio_chip *tio, u32 ctrl)
+{
+	INTEL_PMC_TIO_WR_REG(TGPIOCTL, ctrl);
+	ctrl |= TGPIOCTL_EN;
+	INTEL_PMC_TIO_WR_REG(TGPIOCTL, ctrl);
+}
+
 static void intel_pmc_tio_enable_input(struct intel_pmc_tio_chip *tio,
 				       u32 eflags)
 {
@@ -131,10 +165,6 @@ static void intel_pmc_tio_enable_input(struct intel_pmc_tio_chip *tio,
 
 	/* Disable */
 	ctrl = INTEL_PMC_TIO_RD_REG(TGPIOCTL);
-	ctrl &= ~TGPIOCTL_EN;
-	INTEL_PMC_TIO_WR_REG(TGPIOCTL, ctrl);
-
-	tio->last_event_count = 0;
 
 	/* Configure Input */
 	ctrl |= TGPIOCTL_DIR;
@@ -150,9 +180,7 @@ static void intel_pmc_tio_enable_input(struct intel_pmc_tio_chip *tio,
 		ctrl |= TGPIOCTL_EP_FALLING_EDGE;
 
 	/* Enable */
-	INTEL_PMC_TIO_WR_REG(TGPIOCTL, ctrl);
-	ctrl |= TGPIOCTL_EN;
-	INTEL_PMC_TIO_WR_REG(TGPIOCTL, ctrl);
+	intel_pmc_tio_enable(tio, ctrl);
 }
 
 static void intel_pmc_tio_input_work(struct work_struct *input_work)
@@ -293,6 +321,115 @@ static int intel_pmc_tio_do_poll(struct gpio_chip *chip, unsigned int offset,
 	return err;
 }
 
+static int intel_pmc_tio_insert_edge(struct intel_pmc_tio_chip *tio, u32 *ctrl)
+{
+	struct system_counterval_t sys_counter;
+	ktime_t trigger;
+	int err;
+	u64 art;
+
+	trigger = ktime_get_real();
+	trigger = ktime_add_ns(trigger, NSEC_PER_SEC / 20);
+
+	err = ktime_convert_real_to_system_counter(trigger, &sys_counter);
+	if (err)
+		return err;
+
+	err = convert_tsc_to_art(&sys_counter, &art);
+	if (err)
+		return err;
+
+	/* In disabled state */
+	*ctrl &= ~(TGPIOCTL_DIR | TGPIOCTL_PM);
+	*ctrl &= ~TGPIOCTL_EP;
+	*ctrl |= TGPIOCTL_EP_TOGGLE_EDGE;
+
+	INTEL_PMC_TIO_WR_REG(TGPIOCOMPV31_0, art & 0xFFFFFFFF);
+	INTEL_PMC_TIO_WR_REG(TGPIOCOMPV63_32, art >> 32);
+
+	intel_pmc_tio_enable(tio, *ctrl);
+
+	/* sleep for 100 milli-second */
+	msleep(2 * (MSEC_PER_SEC / 20));
+
+	*ctrl = intel_pmc_tio_disable(tio);
+
+	return 0;
+}
+
+static int intel_pmc_tio_direction_output(struct gpio_chip *chip, unsigned int offset,
+					  int value)
+{
+	struct intel_pmc_tio_chip *tio;
+	int err = 0;
+	u32 ctrl;
+	u64 art;
+
+	if (value)
+		return -EINVAL;
+
+	tio = gch_to_intel_pmc_tio(chip);
+
+	mutex_lock(&tio->lock);
+	ctrl = intel_pmc_tio_disable(tio);
+
+	/*
+	 * Make sure the output is zero'ed by inserting an edge as needed
+	 * Only need to worry about this when restarting output
+	 */
+	if (tio->output_high) {
+		err = intel_pmc_tio_insert_edge(tio, &ctrl);
+		if (err)
+			goto out;
+		tio->output_high = false;
+	}
+
+	/* Enable the device, be sure that the 'compare(COMPV)' value is invalid */
+	art = read_art_time();
+	--art;
+	INTEL_PMC_TIO_WR_REG(TGPIOCOMPV31_0, art & 0xFFFFFFFF);
+	INTEL_PMC_TIO_WR_REG(TGPIOCOMPV63_32, art >> 32);
+
+	ctrl &= ~(TGPIOCTL_DIR | TGPIOCTL_PM);
+	ctrl &= ~TGPIOCTL_EP;
+	ctrl |= TGPIOCTL_EP_TOGGLE_EDGE;
+
+	intel_pmc_tio_enable(tio, ctrl);
+
+out:
+	mutex_unlock(&tio->lock);
+
+	return err;
+}
+
+static int intel_pmc_tio_generate_output(struct gpio_chip *chip,
+					 unsigned int offset,
+					 struct gpio_output_event_data *data)
+{
+	struct intel_pmc_tio_chip *tio = gch_to_intel_pmc_tio(chip);
+	ktime_t sys_realtime = ns_to_ktime(data->timestamp);
+	struct system_counterval_t sys_counter;
+	u64 art_timestamp;
+	int err;
+
+	err = ktime_convert_real_to_system_counter(sys_realtime, &sys_counter);
+	if (err)
+		return err;
+
+	err = convert_tsc_to_art(&sys_counter, &art_timestamp);
+	if (err)
+		return err;
+
+	mutex_lock(&tio->lock);
+
+	INTEL_PMC_TIO_WR_REG(TGPIOCOMPV63_32, art_timestamp >> 32);
+	INTEL_PMC_TIO_WR_REG(TGPIOCOMPV31_0, art_timestamp);
+
+	mutex_unlock(&tio->lock);
+
+	return 0;
+}
+
 static int intel_pmc_tio_probe(struct platform_device *pdev)
 {
 	struct intel_pmc_tio_chip *tio;
@@ -327,10 +464,13 @@ static int intel_pmc_tio_probe(struct platform_device *pdev)
 	tio->gch.base = -1;
 	tio->gch.setup_poll = intel_pmc_tio_setup_poll;
 	tio->gch.do_poll = intel_pmc_tio_do_poll;
+	tio->gch.generate_output = intel_pmc_tio_generate_output;
+	tio->gch.direction_output = intel_pmc_tio_direction_output;
 
 	platform_set_drvdata(pdev, tio);
 	mutex_init(&tio->lock);
 	INIT_DELAYED_WORK(&tio->input_work, intel_pmc_tio_input_work);
+	tio->output_high = false;
 
 	err = devm_gpiochip_add_data(&pdev->dev, &tio->gch, tio);
 	if (err < 0)
diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c
index cb6b9155884c..1df28a71f88b 100644
--- a/drivers/gpio/gpiolib-cdev.c
+++ b/drivers/gpio/gpiolib-cdev.c
@@ -1221,6 +1221,29 @@ static __poll_t linereq_poll(struct file *file,
 	return events;
 }
 
+static ssize_t linereq_write(struct file *filep, const char __user *buf,
+			     size_t count, loff_t *f_ps)
+{
+	struct linereq *lr = filep->private_data;
+	struct gpio_output_event out_event;
+	struct gpio_output_event_data out_data;
+	int offset, err;
+
+	if (count < sizeof(struct gpio_output_event))
+		return -EINVAL;
+
+	if (copy_from_user(&out_event, buf, sizeof(out_event)))
+		return -EFAULT;
+
+	out_data.timestamp = out_event.timestamp;
+	offset = gpio_chip_hwgpio(lr->lines[0].desc);
+	err = lr->gdev->chip->generate_output(lr->gdev->chip, offset, &out_data);
+	if (err)
+		return err;
+
+	return sizeof(struct gpio_output_event);
+}
+
 static ssize_t linereq_read(struct file *file,
 			    char __user *buf,
 			    size_t count,
@@ -1302,6 +1325,8 @@ static void linereq_free(struct linereq *lr)
 
 	for (i = 0; i < lr->num_lines; i++) {
 		edge_detector_stop(&lr->lines[i]);
+		if (lr->lines[i].irq)
+			free_irq(lr->lines[i].irq, lr);
 		if (lr->lines[i].desc)
 			gpiod_free(lr->lines[i].desc);
 	}
@@ -1319,7 +1344,18 @@ static int linereq_release(struct inode *inode, struct file *file)
 	return 0;
 }
 
-static const struct file_operations line_fileops = {
+static const struct file_operations line_output_fileops = {
+	.release = linereq_release,
+	.write = linereq_write,
+	.owner = THIS_MODULE,
+	.llseek = noop_llseek,
+	.unlocked_ioctl = linereq_ioctl,
+#ifdef CONFIG_COMPAT
+	.compat_ioctl = linereq_ioctl_compat,
+#endif
+};
+
+static const struct file_operations line_input_fileops = {
 	.release = linereq_release,
 	.read = linereq_read,
 	.poll = linereq_poll,
@@ -1382,6 +1418,7 @@ static int linereq_create(struct gpio_device *gdev, void __user *ip)
 	struct gpio_v2_line_request ulr;
 	struct gpio_v2_line_config *lc;
 	unsigned int file_flags;
+	bool output = false;
 	struct linereq *lr;
 	struct file *file;
 	u64 flags;
@@ -1458,11 +1495,12 @@ static int linereq_create(struct gpio_device *gdev, void __user *ip)
 		if (ret < 0)
 			goto out_free_linereq;
 
+		output = flags & GPIO_V2_LINE_FLAG_OUTPUT;
 		/*
 		 * Lines have to be requested explicitly for input
 		 * or output, else the line will be treated "as is".
 		 */
-		if (flags & GPIO_V2_LINE_FLAG_OUTPUT) {
+		if (output) {
 			int val = gpio_v2_line_config_output_value(lc, i);
 
 			ret = gpiod_direction_output(desc, val);
@@ -1476,6 +1514,8 @@ static int linereq_create(struct gpio_device *gdev, void __user *ip)
 		}
 
 		file_flags = O_RDONLY | O_CLOEXEC;
+		file_flags |= output ? O_WRONLY : O_RDONLY;
+		file_flags |= (!output && !lr->lines[i].irq) ? O_NONBLOCK : 0;
 
 		blocking_notifier_call_chain(&desc->gdev->notifier,
 					     GPIO_V2_LINE_CHANGED_REQUESTED, desc);
@@ -1490,7 +1530,10 @@ static int linereq_create(struct gpio_device *gdev, void __user *ip)
 		goto out_free_linereq;
 	}
 
-	file = anon_inode_getfile("gpio-line", &line_fileops, lr,
+	file = anon_inode_getfile("gpio-line",
+				  output ? &line_output_fileops :
+				  &line_input_fileops,
+				  lr,
 				  file_flags);
 	if (IS_ERR(file)) {
 		ret = PTR_ERR(file);
diff --git a/include/linux/gpio/driver.h b/include/linux/gpio/driver.h
index f5b971ad40bc..561e289434aa 100644
--- a/include/linux/gpio/driver.h
+++ b/include/linux/gpio/driver.h
@@ -18,6 +18,7 @@ struct seq_file;
 struct gpio_device;
 struct module;
 struct gpioevent_poll_data;
+struct gpio_output_event_data;
 enum gpiod_flags;
 enum gpio_lookup_flags;
 
@@ -310,6 +311,7 @@ struct gpio_irq_chip {
  *	event flags and driver returns accepted flags.
  * @do_poll: optional routine for devices that don't support interrupts.
  *	Returns event specification in data parameter.
+ * @generate_output: generate out event. Takes timestamp as input.
  * @base: identifies the first GPIO number handled by this chip;
  *	or, if negative during registration, requests dynamic ID allocation.
  *	DEPRECATION: providing anything non-negative and nailing the base
@@ -409,6 +411,9 @@ struct gpio_chip {
 	int                     (*do_poll)(struct gpio_chip *chip,
 					   unsigned int offset, u32 eflags,
 					   struct gpioevent_poll_data *data);
+	int                     (*generate_output)(struct gpio_chip *chip,
+						   unsigned int offset,
+						   struct gpio_output_event_data *data);
 
 	int			base;
 	u16			ngpio;
@@ -490,6 +495,10 @@ struct gpioevent_poll_data {
 	__u32 id;
 };
 
+struct gpio_output_event_data {
+	__u64 timestamp;
+};
+
 extern const char *gpiochip_is_requested(struct gpio_chip *gc,
 			unsigned int offset);
 
diff --git a/include/uapi/linux/gpio.h b/include/uapi/linux/gpio.h
index ed84805baee8..c39efc459b9f 100644
--- a/include/uapi/linux/gpio.h
+++ b/include/uapi/linux/gpio.h
@@ -298,6 +298,14 @@ struct gpio_v2_line_event {
 	__u32 padding[6];
 };
 
+/**
+ * struct gpio_output_event - Output event request
+ * @timestamp: When the time should occur
+ */
+struct gpio_output_event {
+	__u64 timestamp;
+};
+
 /*
  * ABI v1
  *
-- 
2.17.1


  parent reply	other threads:[~2021-08-24 16:48 UTC|newest]

Thread overview: 35+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-08-24 16:47 [RFC PATCH v1 00/20] Review Request: Add support for Intel PMC lakshmi.sowjanya.d
2021-08-24 16:47 ` [RFC PATCH v1 01/20] gpio: Add basic GPIO driver for Intel PMC Timed I/O device lakshmi.sowjanya.d
2021-08-24 16:47 ` [RFC PATCH v1 02/20] gpio: Add GPIO polling interface to GPIO lib lakshmi.sowjanya.d
2021-09-22 10:03   ` Bartosz Golaszewski
2021-10-07  2:14     ` Kent Gibson
2021-08-24 16:47 ` [RFC PATCH v1 03/20] arch: x86: Add ART support function to tsc code lakshmi.sowjanya.d
2021-08-24 16:47 ` [RFC PATCH v1 04/20] gpio: Add input code to Intel PMC Timed I/O Driver lakshmi.sowjanya.d
2021-08-24 16:47 ` [RFC PATCH v1 05/20] tools: gpio: Add additional polling support to gpio-event-mon lakshmi.sowjanya.d
2021-08-24 16:47 ` [RFC PATCH v1 06/20] gpio: Add set_input and polling interface to GPIO lib code lakshmi.sowjanya.d
2021-08-24 16:47 ` lakshmi.sowjanya.d [this message]
2021-09-16 21:42   ` [RFC PATCH v1 07/20] gpio: Add output event generation method to GPIOLIB and PMC Driver Linus Walleij
2021-09-17  7:27     ` Uwe Kleine-König
2021-09-19 19:38       ` Linus Walleij
2021-09-19 21:21         ` Clemens Gruber
2021-09-20  7:14           ` Uwe Kleine-König
2021-08-24 16:47 ` [RFC PATCH v1 08/20] kernel: time: Add system time to system counter translation lakshmi.sowjanya.d
2021-09-16 21:48   ` Linus Walleij
2021-08-24 16:47 ` [RFC PATCH v1 09/20] arch: x86: Add TSC to ART translation lakshmi.sowjanya.d
2021-08-24 16:47 ` [RFC PATCH v1 10/20] tools: gpio: Add GPIO output generation user application lakshmi.sowjanya.d
2021-09-16 21:52   ` Linus Walleij
2021-08-24 16:47 ` [RFC PATCH v1 11/20] gpio: Add event count interface to gpiolib lakshmi.sowjanya.d
2021-09-22  9:53   ` Bartosz Golaszewski
2021-08-24 16:47 ` [RFC PATCH v1 12/20] gpio: Add event count to Intel(R) PMC Timed I/O driver lakshmi.sowjanya.d
2021-08-24 16:47 ` [RFC PATCH v1 13/20] tools: gpio: Add event count capability to event monitor application lakshmi.sowjanya.d
2021-09-16 21:57   ` Linus Walleij
2021-08-24 16:47 ` [RFC PATCH v1 14/20] arch/x86: Add ART nanosecond to ART conversion lakshmi.sowjanya.d
2021-08-24 16:47 ` [RFC PATCH v1 15/20] pwm: Add capability for PWM Driver managed state lakshmi.sowjanya.d
2021-09-16 22:00   ` Linus Walleij
2021-08-24 16:47 ` [RFC PATCH v1 16/20] gpio: Add PWM capabilities to Intel(R) PMC TIO driver lakshmi.sowjanya.d
2021-08-24 16:47 ` [RFC PATCH v1 17/20] pwm: Add second alignment to the core PWM interface lakshmi.sowjanya.d
2021-08-24 16:47 ` [RFC PATCH v1 18/20] gpio: Add PWM alignment support to the Intel(R) PMC Timed I/O driver lakshmi.sowjanya.d
2021-08-24 16:48 ` [RFC PATCH v1 19/20] gpio: Add GPIO monitor line to Intel(R) Timed I/O Driver lakshmi.sowjanya.d
2021-08-24 16:48 ` [RFC PATCH v1 20/20] tools: gpio: Add PWM monitor application lakshmi.sowjanya.d
2021-09-16 21:21 ` [RFC PATCH v1 00/20] Review Request: Add support for Intel PMC Linus Walleij
2021-10-11 21:14   ` Dipen Patel

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20210824164801.28896-8-lakshmi.sowjanya.d@intel.com \
    --to=lakshmi.sowjanya.d@intel.com \
    --cc=andriy.shevchenko@linux.intel.com \
    --cc=bala.senthil@intel.com \
    --cc=bgolaszewski@baylibre.com \
    --cc=linus.walleij@linaro.org \
    --cc=linux-gpio@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=mgross@linux.intel.com \
    --cc=tamal.saha@intel.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.