[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <27762fd2-123c-4286-904c-7ecaa748e9ff@enneenne.com>
Date: Wed, 20 Mar 2024 17:17:49 +0100
From: Rodolfo Giometti <giometti@...eenne.com>
To: lakshmi.sowjanya.d@...el.com, tglx@...utronix.de, jstultz@...gle.com,
corbet@....net, linux-kernel@...r.kernel.org
Cc: x86@...nel.org, netdev@...r.kernel.org, linux-doc@...r.kernel.org,
intel-wired-lan@...ts.osuosl.org, andriy.shevchenko@...ux.intel.com,
eddie.dong@...el.com, christopher.s.hall@...el.com,
jesse.brandeburg@...el.com, davem@...emloft.net,
alexandre.torgue@...s.st.com, joabreu@...opsys.com,
mcoquelin.stm32@...il.com, perex@...ex.cz, linux-sound@...r.kernel.org,
anthony.l.nguyen@...el.com, peter.hilber@...nsynergy.com,
pandith.n@...el.com, mallikarjunappa.sangannavar@...el.com,
subramanian.mohan@...el.com, basavaraj.goudar@...el.com,
thejesh.reddy.t.r@...el.com
Subject: Re: [PATCH v5 09/11] pps: generators: Add PPS Generator TIO Driver
On 19/03/24 14:05, lakshmi.sowjanya.d@...el.com wrote:
> From: Lakshmi Sowjanya D <lakshmi.sowjanya.d@...el.com>
>
> The Intel Timed IO PPS generator driver outputs a PPS signal using
> dedicated hardware that is more accurate than software actuated PPS.
> The Timed IO hardware generates output events using the ART timer.
> The ART timer period varies based on platform type, but is less than 100
> nanoseconds for all current platforms. Timed IO output accuracy is
> within 1 ART period.
>
> PPS output is enabled by writing '1' the 'enable' sysfs attribute. The
> driver uses hrtimers to schedule a wake-up 10 ms before each event
> (edge) target time. At wakeup, the driver converts the target time in
> terms of CLOCK_REALTIME to ART trigger time and writes this to the Timed
> IO hardware. The Timed IO hardware generates an event precisely at the
> requested system time without software involvement.
>
> Co-developed-by: Christopher Hall <christopher.s.hall@...el.com>
> Signed-off-by: Christopher Hall <christopher.s.hall@...el.com>
> Co-developed-by: Pandith N <pandith.n@...el.com>
> Signed-off-by: Pandith N <pandith.n@...el.com>
> Co-developed-by: Thejesh Reddy T R <thejesh.reddy.t.r@...el.com>
> Signed-off-by: Thejesh Reddy T R <thejesh.reddy.t.r@...el.com>
> Signed-off-by: Lakshmi Sowjanya D <lakshmi.sowjanya.d@...el.com>
> Reviewed-by: Eddie Dong <eddie.dong@...el.com>
> Reviewed-by: Andy Shevchenko <andriy.shevchenko@...ux.intel.com>
Acked-by: Rodolfo Giometti <giometti@...eenne.com>
> ---
> drivers/pps/generators/Kconfig | 16 ++
> drivers/pps/generators/Makefile | 1 +
> drivers/pps/generators/pps_gen_tio.c | 245 +++++++++++++++++++++++++++
> 3 files changed, 262 insertions(+)
> create mode 100644 drivers/pps/generators/pps_gen_tio.c
>
> diff --git a/drivers/pps/generators/Kconfig b/drivers/pps/generators/Kconfig
> index d615e640fcad..0f090932336f 100644
> --- a/drivers/pps/generators/Kconfig
> +++ b/drivers/pps/generators/Kconfig
> @@ -12,3 +12,19 @@ config PPS_GENERATOR_PARPORT
> If you say yes here you get support for a PPS signal generator which
> utilizes STROBE pin of a parallel port to send PPS signals. It uses
> parport abstraction layer and hrtimers to precisely control the signal.
> +
> +config PPS_GENERATOR_TIO
> + tristate "TIO PPS signal generator"
> + depends on X86 && CPU_SUP_INTEL
> + help
> + If you say yes here you get support for a PPS TIO signal generator
> + which generates a pulse at a prescribed time based on the system clock.
> + It uses time translation and hrtimers to precisely generate a pulse.
> + This hardware is present on 2019 and newer Intel CPUs. However, this
> + driver is not useful without adding highly specialized hardware outside
> + the Linux system to observe these pulses.
> +
> + To compile this driver as a module, choose M here: the module
> + will be called pps_gen_tio.
> +
> + If unsure, say N.
> diff --git a/drivers/pps/generators/Makefile b/drivers/pps/generators/Makefile
> index 2d56dd0495d5..07004cfd3996 100644
> --- a/drivers/pps/generators/Makefile
> +++ b/drivers/pps/generators/Makefile
> @@ -4,6 +4,7 @@
> #
>
> obj-$(CONFIG_PPS_GENERATOR_PARPORT) += pps_gen_parport.o
> +obj-$(CONFIG_PPS_GENERATOR_TIO) += pps_gen_tio.o
>
> ifeq ($(CONFIG_PPS_DEBUG),y)
> EXTRA_CFLAGS += -DDEBUG
> diff --git a/drivers/pps/generators/pps_gen_tio.c b/drivers/pps/generators/pps_gen_tio.c
> new file mode 100644
> index 000000000000..3ee271524482
> --- /dev/null
> +++ b/drivers/pps/generators/pps_gen_tio.c
> @@ -0,0 +1,245 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Intel PPS signal Generator Driver
> + *
> + * Copyright (C) 2023 Intel Corporation
> + */
> +
> +#include <linux/bits.h>
> +#include <linux/bitfield.h>
> +#include <linux/cleanup.h>
> +#include <linux/container_of.h>
> +#include <linux/cpu.h>
> +#include <linux/device.h>
> +#include <linux/err.h>
> +#include <linux/hrtimer.h>
> +#include <linux/io-64-nonatomic-hi-lo.h>
> +#include <linux/kstrtox.h>
> +#include <linux/mod_devicetable.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/spinlock.h>
> +#include <linux/sysfs.h>
> +#include <linux/timekeeping.h>
> +#include <linux/types.h>
> +
> +#include <asm/cpu_device_id.h>
> +
> +#define TIOCTL 0x00
> +#define TIOCOMPV 0x10
> +
> +/* Control Register */
> +#define TIOCTL_EN BIT(0)
> +#define TIOCTL_DIR BIT(1)
> +#define TIOCTL_EP GENMASK(3, 2)
> +#define TIOCTL_EP_RISING_EDGE FIELD_PREP(TIOCTL_EP, 0)
> +#define TIOCTL_EP_FALLING_EDGE FIELD_PREP(TIOCTL_EP, 1)
> +#define TIOCTL_EP_TOGGLE_EDGE FIELD_PREP(TIOCTL_EP, 2)
> +
> +#define SAFE_TIME_NS (10 * NSEC_PER_MSEC) /* Safety time to set hrtimer early */
> +#define MAGIC_CONST (NSEC_PER_SEC - SAFE_TIME_NS)
> +#define ART_HW_DELAY_CYCLES 2
> +
> +struct pps_tio {
> + struct hrtimer timer;
> + struct device *dev;
> + spinlock_t lock;
> + struct attribute_group attrs;
> + void __iomem *base;
> + bool enabled;
> +};
> +
> +static inline u32 pps_ctl_read(struct pps_tio *tio)
> +{
> + return readl(tio->base + TIOCTL);
> +}
> +
> +static inline void pps_ctl_write(struct pps_tio *tio, u32 value)
> +{
> + writel(value, tio->base + TIOCTL);
> +}
> +
> +/* For COMPV register, It's safer to write higher 32-bit followed by lower 32-bit */
> +static inline void pps_compv_write(struct pps_tio *tio, u64 value)
> +{
> + hi_lo_writeq(value, tio->base + TIOCOMPV);
> +}
> +
> +static inline ktime_t first_event(struct pps_tio *tio)
> +{
> + return ktime_set(ktime_get_real_seconds() + 1, MAGIC_CONST);
> +}
> +
> +static u32 pps_tio_disable(struct pps_tio *tio)
> +{
> + u32 ctrl;
> +
> + ctrl = pps_ctl_read(tio);
> + pps_compv_write(tio, 0);
> +
> + ctrl &= ~TIOCTL_EN;
> + pps_ctl_write(tio, ctrl);
> +
> + return ctrl;
> +}
> +
> +static void pps_tio_direction_output(struct pps_tio *tio)
> +{
> + u32 ctrl;
> +
> + ctrl = pps_tio_disable(tio);
> +
> + /* We enable the device, be sure that the 'compare' value is invalid */
> + pps_compv_write(tio, 0);
> +
> + ctrl &= ~(TIOCTL_DIR | TIOCTL_EP);
> + ctrl |= TIOCTL_EP_TOGGLE_EDGE;
> + pps_ctl_write(tio, ctrl);
> +
> + ctrl |= TIOCTL_EN;
> + pps_ctl_write(tio, ctrl);
> +}
> +
> +static bool pps_generate_next_pulse(struct pps_tio *tio, ktime_t expires)
> +{
> + u64 art;
> +
> + if (!ktime_real_to_base_clock(expires, CSID_X86_ART, &art)) {
> + pps_tio_disable(tio);
> + return false;
> + }
> +
> + pps_compv_write(tio, art - ART_HW_DELAY_CYCLES);
> + return true;
> +}
> +
> +static enum hrtimer_restart hrtimer_callback(struct hrtimer *timer)
> +{
> + struct pps_tio *tio = container_of(timer, struct pps_tio, timer);
> + ktime_t expires, now;
> +
> + guard(spinlock)(&tio->lock);
> +
> + expires = hrtimer_get_expires(timer);
> + now = ktime_get_real();
> +
> + if (now - expires < SAFE_TIME_NS) {
> + if (!pps_generate_next_pulse(tio, expires + SAFE_TIME_NS))
> + return HRTIMER_NORESTART;
> + }
> +
> + hrtimer_forward(timer, now, NSEC_PER_SEC / 2);
> + return HRTIMER_RESTART;
> +}
> +
> +static ssize_t enable_store(struct device *dev, struct device_attribute *attr, const char *buf,
> + size_t count)
> +{
> + struct pps_tio *tio = dev_get_drvdata(dev);
> + bool enable;
> + int err;
> +
> + err = kstrtobool(buf, &enable);
> + if (err)
> + return err;
> +
> + guard(spinlock_irqsave)(&tio->lock);
> + if (enable && !tio->enabled) {
> + if (!timekeeping_clocksource_has_base(CSID_X86_ART)) {
> + dev_err(tio->dev, "PPS cannot be started as clock is not related to ART");
> + return -EPERM;
> + }
> + pps_tio_direction_output(tio);
> + hrtimer_start(&tio->timer, first_event(tio), HRTIMER_MODE_ABS);
> + tio->enabled = true;
> + } else if (!enable && tio->enabled) {
> + hrtimer_cancel(&tio->timer);
> + pps_tio_disable(tio);
> + tio->enabled = false;
> + }
> + return count;
> +}
> +
> +static ssize_t enable_show(struct device *dev, struct device_attribute *devattr, char *buf)
> +{
> + struct pps_tio *tio = dev_get_drvdata(dev);
> + u32 ctrl;
> +
> + ctrl = pps_ctl_read(tio);
> + ctrl &= TIOCTL_EN;
> +
> + return sysfs_emit(buf, "%u\n", ctrl);
> +}
> +static DEVICE_ATTR_RW(enable);
> +
> +static struct attribute *pps_tio_attrs[] = {
> + &dev_attr_enable.attr,
> + NULL
> +};
> +ATTRIBUTE_GROUPS(pps_tio);
> +
> +static int pps_tio_probe(struct platform_device *pdev)
> +{
> + struct pps_tio *tio;
> +
> + if (!(cpu_feature_enabled(X86_FEATURE_TSC_KNOWN_FREQ) &&
> + cpu_feature_enabled(X86_FEATURE_ART))) {
> + dev_warn(&pdev->dev, "TSC/ART is not enabled");
> + return -ENODEV;
> + }
> +
> + tio = devm_kzalloc(&pdev->dev, sizeof(*tio), GFP_KERNEL);
> + if (!tio)
> + return -ENOMEM;
> +
> + tio->dev = &pdev->dev;
> + tio->base = devm_platform_ioremap_resource(pdev, 0);
> + if (IS_ERR(tio->base))
> + return PTR_ERR(tio->base);
> +
> + pps_tio_disable(tio);
> + hrtimer_init(&tio->timer, CLOCK_REALTIME, HRTIMER_MODE_ABS);
> + tio->timer.function = hrtimer_callback;
> + spin_lock_init(&tio->lock);
> + tio->enabled = false;
> + platform_set_drvdata(pdev, tio);
> +
> + return 0;
> +}
> +
> +static int pps_tio_remove(struct platform_device *pdev)
> +{
> + struct pps_tio *tio = platform_get_drvdata(pdev);
> +
> + hrtimer_cancel(&tio->timer);
> + pps_tio_disable(tio);
> +
> + return 0;
> +}
> +
> +static const struct acpi_device_id intel_pmc_tio_acpi_match[] = {
> + { "INTC1021" },
> + { "INTC1022" },
> + { "INTC1023" },
> + { "INTC1024" },
> + {}
> +};
> +MODULE_DEVICE_TABLE(acpi, intel_pmc_tio_acpi_match);
> +
> +static struct platform_driver pps_tio_driver = {
> + .probe = pps_tio_probe,
> + .remove = pps_tio_remove,
> + .driver = {
> + .name = "intel-pps-generator",
> + .acpi_match_table = intel_pmc_tio_acpi_match,
> + .dev_groups = pps_tio_groups,
> + },
> +};
> +module_platform_driver(pps_tio_driver);
> +
> +MODULE_AUTHOR("Lakshmi Sowjanya D <lakshmi.sowjanya.d@...el.com>");
> +MODULE_AUTHOR("Christopher Hall <christopher.s.hall@...el.com>");
> +MODULE_AUTHOR("Pandith N <pandith.n@...el.com>");
> +MODULE_AUTHOR("Thejesh Reddy T R <thejesh.reddy.t.r@...el.com>");
> +MODULE_DESCRIPTION("Intel PMC Time-Aware IO Generator Driver");
> +MODULE_LICENSE("GPL");
--
GNU/Linux Solutions e-mail: giometti@...eenne.com
Linux Device Driver giometti@...ux.it
Embedded Systems phone: +39 349 2432127
UNIX programming
Powered by blists - more mailing lists