[RFC PATCH v1 19/20] gpio: Add GPIO monitor line to Intel(R) Timed I/O Driver

From: lakshmi . sowjanya . d
Date: Tue Aug 24 2021 - 12:49:52 EST


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

The device's PWM output is initially aligned with the system clock using
the 'alignment' state parameter. Small differences between
the system clock rate and nominal ART rate cause the Timed I/O clock to
drift over time.

To solve this: add a secondary input only 'virtual' GPIO line to monitor
the PWM output. This is possible because the I/O lines in the Intel PMC
Timed I/O driver can simultaneously generate output and capture input.
By timestamping the PWM output using the monitor line, the output can be
adjusted using a PI controller to maintain alignment with the system
clock (or any related clock).

NOTE: It is not possible to capture either rising or falling edges
using the 'monitor' interface. Requesting anything other than any/all
edges results in an error.

Co-developed-by: Christopher Hall <christopher.s.hall@xxxxxxxxx>
Signed-off-by: Christopher Hall <christopher.s.hall@xxxxxxxxx>
Signed-off-by: Tamal Saha <tamal.saha@xxxxxxxxx>
Signed-off-by: Lakshmi Sowjanya D <lakshmi.sowjanya.d@xxxxxxxxx>
Reviewed-by: Mark Gross <mgross@xxxxxxxxxxxxxxx>
---
drivers/gpio/gpio-intel-tio-pmc.c | 82 +++++++++++++++++++++++++++++--
1 file changed, 78 insertions(+), 4 deletions(-)

diff --git a/drivers/gpio/gpio-intel-tio-pmc.c b/drivers/gpio/gpio-intel-tio-pmc.c
index 1b0eea7b3b2f..b0db6f545ec6 100644
--- a/drivers/gpio/gpio-intel-tio-pmc.c
+++ b/drivers/gpio/gpio-intel-tio-pmc.c
@@ -40,10 +40,14 @@
#define TGPIOCTL_PM BIT(4)

#define DRIVER_NAME "intel-pmc-tio"
-#define GPIO_COUNT 1
+#define GPIO_COUNT 2
#define INPUT_SNAPSHOT_FREQ 8
#define INPUT_SNAPSHOT_COUNT 3

+#define EDGE_FLAGS \
+ (GPIO_V2_LINE_FLAG_EDGE_RISING | \
+ GPIO_V2_LINE_FLAG_EDGE_FALLING)
+
struct intel_pmc_tio_chip {
struct gpio_chip gch;
struct platform_device *pdev;
@@ -54,7 +58,9 @@ struct intel_pmc_tio_chip {
struct delayed_work input_work;
bool input_work_running;
bool systime_valid;
+ bool input;
bool output_high;
+ bool monitor;
unsigned int systime_index;
u32 half_period;
u32 alignment;
@@ -243,14 +249,25 @@ static int intel_pmc_tio_setup_poll(struct gpio_chip *chip, unsigned int offset,
{
struct intel_pmc_tio_chip *tio;

- if (offset != 0)
+ if (offset > 1)
+ return -EINVAL;
+
+ /* The monitor line inherits the configuration from the output line */
+ if (offset == 1 && (*eflags & EDGE_FLAGS) != EDGE_FLAGS)
return -EINVAL;

tio = gch_to_intel_pmc_tio(chip);

mutex_lock(&tio->lock);
+ if (!tio->monitor && !tio->input) {
+ mutex_unlock(&tio->lock);
+ return -EINVAL;
+ }
+
intel_pmc_tio_start_input_work(tio);
- intel_pmc_tio_enable_input(tio, *eflags);
+ if (offset == 0)
+ intel_pmc_tio_enable_input(tio, *eflags);
+
mutex_unlock(&tio->lock);

return 0;
@@ -341,6 +358,54 @@ static int intel_pmc_tio_do_poll(struct gpio_chip *chip, unsigned int offset,
return err;
}

+static int intel_pcm_tio_get(struct gpio_chip *chip, unsigned int offset)
+{
+ return -EIO;
+}
+
+static int intel_pcm_tio_direction_input(struct gpio_chip *chip, unsigned int offset)
+{
+ struct intel_pmc_tio_chip *tio = gch_to_intel_pmc_tio(chip);
+ int ret = 0;
+
+ mutex_lock(&tio->lock);
+
+ if (offset == 0) {
+ if (!tio->monitor)
+ tio->input = true;
+ else
+ ret = -EBUSY;
+ } else { /* offset = 1 */
+ if (!tio->input)
+ tio->monitor = true;
+ else
+ ret = -EBUSY;
+ }
+
+ if (!ret)
+ tio->last_event_count = 0;
+
+ mutex_unlock(&tio->lock);
+
+ return ret;
+}
+
+static void intel_pmc_tio_gpio_free(struct gpio_chip *chip, unsigned int offset)
+{
+ struct intel_pmc_tio_chip *tio = gch_to_intel_pmc_tio(chip);
+
+ if (offset == 0 && tio->input) {
+ tio->input = false;
+ intel_pmc_tio_disable(tio);
+ }
+
+ if (offset == 1)
+ tio->monitor = false;
+
+ if (!tio->monitor && !tio->input)
+ intel_pmc_tio_stop_input_work(tio);
+}
+
static int intel_pmc_tio_insert_edge(struct intel_pmc_tio_chip *tio, u32 *ctrl)
{
struct system_counterval_t sys_counter;
@@ -428,6 +493,9 @@ static int intel_pmc_tio_direction_output(struct gpio_chip *chip,
struct intel_pmc_tio_chip *tio = gch_to_intel_pmc_tio(chip);
int ret;

+ if (offset != 0)
+ return -ENODEV;
+
mutex_lock(&tio->lock);
ret = _intel_pmc_tio_direction_output(tio, offset, value, 0);
mutex_unlock(&tio->lock);
@@ -443,6 +511,9 @@ static int _intel_pmc_tio_generate_output(struct intel_pmc_tio_chip *tio,
u64 art_timestamp;
int err;

+ if (offset != 0)
+ return -ENODEV;
+
if (timestamp != 0) {
sys_realtime = ns_to_ktime(timestamp);
} else {
@@ -667,6 +738,9 @@ static int intel_pmc_tio_probe(struct platform_device *pdev)
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;
+ tio->gch.free = intel_pmc_tio_gpio_free;
+ tio->gch.direction_input = intel_pcm_tio_direction_input;
+ tio->gch.get = intel_pcm_tio_get;

platform_set_drvdata(pdev, tio);
mutex_init(&tio->lock);
@@ -686,7 +760,7 @@ static int intel_pmc_tio_probe(struct platform_device *pdev)
tio_pwm->tio = tio;
tio_pwm->pch.dev = &pdev->dev;
tio_pwm->pch.ops = &intel_pmc_tio_pwm_ops;
- tio_pwm->pch.npwm = GPIO_COUNT;
+ tio_pwm->pch.npwm = GPIO_COUNT - 1;
tio_pwm->pch.base = -1;

err = pwmchip_add(&tio_pwm->pch);
--
2.17.1