Message ID | 20230625142134.33690-2-farbere@amazon.com |
---|---|
State | New |
Headers |
Return-Path: <linux-kernel-owner@vger.kernel.org> Delivered-To: ouuuleilei@gmail.com Received: by 2002:a59:994d:0:b0:3d9:f83d:47d9 with SMTP id k13csp6955045vqr; Sun, 25 Jun 2023 07:56:16 -0700 (PDT) X-Google-Smtp-Source: ACHHUZ7wIIhwylkNcuk5G7zhD1izWaqt+mEdZvplBcAoV5M1UzKz6UdXM2YY7NQH++39J8/sHlsM X-Received: by 2002:a05:6a20:430e:b0:125:9d2e:ae1a with SMTP id h14-20020a056a20430e00b001259d2eae1amr10333471pzk.9.1687704976067; Sun, 25 Jun 2023 07:56:16 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1687704976; cv=none; d=google.com; s=arc-20160816; b=r/H5n1tAYhBg8wl/WPbD12pkM6F9fvuMoaKl2SK6DYdH5QqNMOMiLIn8h9V6fDZRff JSYzWcDz65g9wxmVg5OveLSEiC3qWISp3XJnTZEJ0xBUCht2kLgrUSSRAqAjX4nfo5pD 9E62Uu/3EanEuNFKDI/kG/Zk4KRYhoXOvnwo/Sa6Gzqt/vlCJVjL4Lzyps8QPuavyHC6 MfutcMkhUvLfcfYgKHJxF49of6fRbFyLn/F8NtIXNRQSx6GPrbq6K1J6FXAEyz3pN0OA 8wARnpx8Bfn+RNAZEPFEe+FjdE+XLsCjglqbdO4hgSQVFKGqsmLbl24Rri7BB1hfAgPT uCVg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:mime-version :references:in-reply-to:message-id:date:subject:cc:to:from :dkim-signature; bh=K9olSALxwiqvfXFwnOq4yhThTzE4wPH5nlr+cWBRTlc=; fh=/9rTqK0zswNCgLim86exJ2HNVgKWrjH8Cr/T0xC8qdo=; b=QuBqq8ZOkNmcoyE7IxTyTzhmgBhxjhpKGgmC6oxglrmQnIYTprGQ1fzFr8Fa31Qm49 NY1xIJOib2Q2OXX/a/kuNmpEFEOoYDALGFHNah5hHn9PAEZIZxUEV17670O6ISkt4e5Y trJ+5d/qR0aKIC8D9Oz2y2lcGuT2Nf6QOmz5A/pfiDYsNmjI8tKeZP8C6O6TtrPg7MR8 /ROtHUKARdkVpYdnBSuKyc7Py/fb6AI04JIKrciEQfC4eao+58r1J+TGKUAN0gGC9CrM KwnMPCllRPME9e/Q+EB/v74lfMoBeQgAiqo3YB7fSD8/I6AFkpWO/gJxqTm7r1iwQt1f lCHw== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@amazon.com header.s=amazon201209 header.b=SDJIQhpc; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=QUARANTINE sp=QUARANTINE dis=NONE) header.from=amazon.com Received: from out1.vger.email (out1.vger.email. [2620:137:e000::1:20]) by mx.google.com with ESMTP id c5-20020a6566c5000000b0055acfb661c0si978930pgw.524.2023.06.25.07.56.03; Sun, 25 Jun 2023 07:56:16 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) client-ip=2620:137:e000::1:20; Authentication-Results: mx.google.com; dkim=pass header.i=@amazon.com header.s=amazon201209 header.b=SDJIQhpc; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=QUARANTINE sp=QUARANTINE dis=NONE) header.from=amazon.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230458AbjFYOVx (ORCPT <rfc822;duw91626@gmail.com> + 99 others); Sun, 25 Jun 2023 10:21:53 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:51982 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230309AbjFYOVm (ORCPT <rfc822;linux-kernel@vger.kernel.org>); Sun, 25 Jun 2023 10:21:42 -0400 Received: from smtp-fw-9105.amazon.com (smtp-fw-9105.amazon.com [207.171.188.204]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 8FEC91B1; Sun, 25 Jun 2023 07:21:41 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=amazon.com; i=@amazon.com; q=dns/txt; s=amazon201209; t=1687702902; x=1719238902; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=K9olSALxwiqvfXFwnOq4yhThTzE4wPH5nlr+cWBRTlc=; b=SDJIQhpc1bN1h3cyiCuMKxM5viemm/wEDp84rSIuQ1Pq8PfMwmjEI+XB UpY3w7fqJ2j+8q3qD2a6Y7wzbbVOlEC3f6u2RrXQJkk611Ntp6y9Uw9Lt Cyz1J7ZeKQsQ6VwAheq1rq3taYTJtq5s36enZri+g8J2R8ecgFJEYF28B A=; X-IronPort-AV: E=Sophos;i="6.01,157,1684800000"; d="scan'208";a="656892651" Received: from pdx4-co-svc-p1-lb2-vlan3.amazon.com (HELO email-inbound-relay-pdx-2c-m6i4x-e7094f15.us-west-2.amazon.com) ([10.25.36.214]) by smtp-border-fw-9105.sea19.amazon.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 25 Jun 2023 14:21:37 +0000 Received: from EX19MTAUWC002.ant.amazon.com (pdx1-ws-svc-p6-lb9-vlan2.pdx.amazon.com [10.236.137.194]) by email-inbound-relay-pdx-2c-m6i4x-e7094f15.us-west-2.amazon.com (Postfix) with ESMTPS id B89B740D61; Sun, 25 Jun 2023 14:21:35 +0000 (UTC) Received: from EX19D013UWB003.ant.amazon.com (10.13.138.111) by EX19MTAUWC002.ant.amazon.com (10.250.64.143) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1118.26; Sun, 25 Jun 2023 14:21:34 +0000 Received: from EX19MTAUWA001.ant.amazon.com (10.250.64.204) by EX19D013UWB003.ant.amazon.com (10.13.138.111) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1118.26; Sun, 25 Jun 2023 14:21:34 +0000 Received: from dev-dsk-farbere-1a-46ecabed.eu-west-1.amazon.com (172.19.116.181) by mail-relay.amazon.com (10.250.64.204) with Microsoft SMTP Server id 15.2.1118.26 via Frontend Transport; Sun, 25 Jun 2023 14:21:34 +0000 Received: by dev-dsk-farbere-1a-46ecabed.eu-west-1.amazon.com (Postfix, from userid 14301484) id 115AC5AA; Sun, 25 Jun 2023 14:21:34 +0000 (UTC) From: Eliav Farber <farbere@amazon.com> To: <giometti@enneenne.com>, <robh+dt@kernel.org>, <krzysztof.kozlowski+dt@linaro.org>, <conor+dt@kernel.org>, <farbere@amazon.com>, <devicetree@vger.kernel.org>, <linux-kernel@vger.kernel.org> CC: <ronenk@amazon.com>, <talel@amazon.com>, <hhhawa@amazon.com>, <jonnyc@amazon.com>, <itamark@amazon.com>, <shellykz@amazon.com>, <amitlavi@amazon.com>, <almogbs@amazon.com> Subject: [PATCH 1/5] pps: add pulse-width calculation in nsec Date: Sun, 25 Jun 2023 14:21:30 +0000 Message-ID: <20230625142134.33690-2-farbere@amazon.com> X-Mailer: git-send-email 2.40.1 In-Reply-To: <20230625142134.33690-1-farbere@amazon.com> References: <20230625142134.33690-1-farbere@amazon.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Content-Type: text/plain X-Spam-Status: No, score=-2.8 required=5.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,RCVD_IN_DNSWL_LOW, RCVD_IN_MSPIKE_H4,RCVD_IN_MSPIKE_WL,SPF_HELO_NONE,SPF_NONE, T_SCC_BODY_TEXT_LINE autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: <linux-kernel.vger.kernel.org> X-Mailing-List: linux-kernel@vger.kernel.org X-getmail-retrieved-from-mailbox: =?utf-8?q?INBOX?= X-GMAIL-THRID: =?utf-8?q?1769686933142975398?= X-GMAIL-MSGID: =?utf-8?q?1769686933142975398?= |
Series |
Add PPS pulse-width support
|
|
Commit Message
Farber, Eliav
June 25, 2023, 2:21 p.m. UTC
This change adds PPS pulse-width calculation in nano seconds.
Width time can be calculated for both assert time and reset time.
Calculation can be done only if capture ASSERT and capture CLEAR modes
are both enabled.
Assert width is calculated as:
clear-time - assert-time
and clear width is calculated as:
assert-time - clear-time
Read-only sysfs were added to get the last pulse-width time and event
sequence.
Examples:
* cat /sys/class/pps/pps0/pulse_width_assert
20001450#85
* cat /sys/class/pps/pps1/pulse_width_clear
979893314#16
Signed-off-by: Eliav Farber <farbere@amazon.com>
---
drivers/pps/kapi.c | 49 ++++++++++++++++++++++++++++++++++++++
drivers/pps/pps.c | 9 +++++++
drivers/pps/sysfs.c | 30 +++++++++++++++++++++++
include/linux/pps_kernel.h | 3 +++
include/uapi/linux/pps.h | 19 +++++++++++++++
5 files changed, 110 insertions(+)
Comments
On 25/06/23 16:21, Eliav Farber wrote: > This change adds PPS pulse-width calculation in nano seconds. > Width time can be calculated for both assert time and reset time. > > Calculation can be done only if capture ASSERT and capture CLEAR modes > are both enabled. > > Assert width is calculated as: > clear-time - assert-time > and clear width is calculated as: > assert-time - clear-time > > Read-only sysfs were added to get the last pulse-width time and event > sequence. > Examples: > * cat /sys/class/pps/pps0/pulse_width_assert > 20001450#85 > * cat /sys/class/pps/pps1/pulse_width_clear > 979893314#16 > > Signed-off-by: Eliav Farber <farbere@amazon.com> > --- > drivers/pps/kapi.c | 49 ++++++++++++++++++++++++++++++++++++++ > drivers/pps/pps.c | 9 +++++++ > drivers/pps/sysfs.c | 30 +++++++++++++++++++++++ > include/linux/pps_kernel.h | 3 +++ > include/uapi/linux/pps.h | 19 +++++++++++++++ > 5 files changed, 110 insertions(+) > > diff --git a/drivers/pps/kapi.c b/drivers/pps/kapi.c > index d9d566f70ed1..deeecfc0a3ee 100644 > --- a/drivers/pps/kapi.c > +++ b/drivers/pps/kapi.c > @@ -82,6 +82,14 @@ struct pps_device *pps_register_source(struct pps_source_info *info, > goto pps_register_source_exit; > } > > + if ((info->mode & PPS_WIDTHBOTH) && > + ((info->mode & PPS_CAPTUREBOTH) != PPS_CAPTUREBOTH)) { > + pr_err("%s: width can't be calculated without both captures (mode = 0x%x)\n", > + info->name, info->mode); > + err = -EINVAL; > + goto pps_register_source_exit; > + } See the comment below where you define PPS_WIDTHBOTH. > /* Allocate memory for the new PPS source struct */ > pps = kzalloc(sizeof(struct pps_device), GFP_KERNEL); > if (pps == NULL) { > @@ -143,6 +151,39 @@ void pps_unregister_source(struct pps_device *pps) > } > EXPORT_SYMBOL(pps_unregister_source); > > +static u64 pps_ktime_sub(struct pps_ktime *ts1, struct pps_ktime *ts2) > +{ > + if (ts1->sec == ts2->sec) > + return (ts1->nsec > ts2->nsec) ? (ts1->nsec - ts2->nsec) : (ts2->nsec - ts1->nsec); > + > + if (ts1->sec > ts2->sec) > + return (ts1->sec - ts2->sec) * NSEC_PER_SEC + ts1->nsec - ts2->nsec; > + > + return (ts2->sec - ts1->sec) * NSEC_PER_SEC + ts2->nsec - ts1->nsec; > +} > + > +static void pps_calc_clear_width(struct pps_device *pps) > +{ > + if (pps->clear_sequence == 0) > + return; > + > + pps->clear_width.sequence++; I don't understand the meaning of this field... regarding assert and clear it states the n-th sample but in this case...? Why do you need it? > + pps->clear_width.nsec = pps_ktime_sub(&pps->assert_tu, &pps->clear_tu); > + dev_dbg(pps->dev, "PPS clear width = %llu#%u\n", > + pps->clear_width.nsec, pps->clear_width.sequence); > +} > + > +static void pps_calc_assert_width(struct pps_device *pps) > +{ > + if (pps->assert_sequence == 0) > + return; > + > + pps->assert_width.sequence++; Ditto. > + pps->assert_width.nsec = pps_ktime_sub(&pps->clear_tu, &pps->assert_tu); > + dev_dbg(pps->dev, "PPS assert width = %llu#%u\n", > + pps->assert_width.nsec, pps->assert_width.sequence); > +} > + > /* pps_event - register a PPS event into the system > * @pps: the PPS device > * @ts: the event timestamp > @@ -191,6 +232,10 @@ void pps_event(struct pps_device *pps, struct pps_event_time *ts, int event, > dev_dbg(pps->dev, "capture assert seq #%u\n", > pps->assert_sequence); > > + /* Calculate clear pulse-width */ > + if (pps->params.mode & PPS_WIDTHCLEAR) > + pps_calc_clear_width(pps); > + > captured = ~0; > } > if (event & pps->params.mode & PPS_CAPTURECLEAR) { > @@ -205,6 +250,10 @@ void pps_event(struct pps_device *pps, struct pps_event_time *ts, int event, > dev_dbg(pps->dev, "capture clear seq #%u\n", > pps->clear_sequence); > > + /* Calculate assert pulse-width */ > + if (pps->params.mode & PPS_WIDTHASSERT) > + pps_calc_assert_width(pps); > + > captured = ~0; > } > > diff --git a/drivers/pps/pps.c b/drivers/pps/pps.c > index 5d19baae6a38..8299a272af11 100644 > --- a/drivers/pps/pps.c > +++ b/drivers/pps/pps.c > @@ -195,6 +195,11 @@ static long pps_cdev_ioctl(struct file *file, > fdata.info.clear_tu = pps->clear_tu; > fdata.info.current_mode = pps->current_mode; > > + memcpy(&fdata.info.assert_width, &pps->assert_width, > + sizeof(struct pps_kwidth)); > + memcpy(&fdata.info.clear_width, &pps->clear_width, > + sizeof(struct pps_kwidth)); > + > spin_unlock_irq(&pps->lock); > > err = copy_to_user(uarg, &fdata, sizeof(struct pps_fdata)); > @@ -283,6 +288,10 @@ static long pps_cdev_compat_ioctl(struct file *file, > sizeof(struct pps_ktime_compat)); > memcpy(&compat.info.clear_tu, &pps->clear_tu, > sizeof(struct pps_ktime_compat)); > + memcpy(&compat.info.assert_width, &pps->assert_width, > + sizeof(struct pps_kwidth_compat)); > + memcpy(&compat.info.clear_width, &pps->clear_width, > + sizeof(struct pps_kwidth_compat)); > > spin_unlock_irq(&pps->lock); > > diff --git a/drivers/pps/sysfs.c b/drivers/pps/sysfs.c > index 134bc33f6ad0..3e34de77dba6 100644 > --- a/drivers/pps/sysfs.c > +++ b/drivers/pps/sysfs.c > @@ -79,6 +79,34 @@ static ssize_t path_show(struct device *dev, struct device_attribute *attr, > } > static DEVICE_ATTR_RO(path); > > +static ssize_t pulse_width_assert_show(struct device *dev, > + struct device_attribute *attr, > + char *buf) > +{ > + struct pps_device *pps = dev_get_drvdata(dev); > + > + if (!(pps->info.mode & PPS_WIDTHASSERT)) > + return 0; > + > + return sprintf(buf, "%llu#%u\n", > + pps->assert_width.nsec, pps->assert_width.sequence); > +} > +static DEVICE_ATTR_RO(pulse_width_assert); > + > +static ssize_t pulse_width_clear_show(struct device *dev, > + struct device_attribute *attr, > + char *buf) > +{ > + struct pps_device *pps = dev_get_drvdata(dev); > + > + if (!(pps->info.mode & PPS_WIDTHCLEAR)) > + return 0; > + > + return sprintf(buf, "%llu#%u\n", > + pps->clear_width.nsec, pps->clear_width.sequence); > +} > +static DEVICE_ATTR_RO(pulse_width_clear); > + > static struct attribute *pps_attrs[] = { > &dev_attr_assert.attr, > &dev_attr_clear.attr, > @@ -86,6 +114,8 @@ static struct attribute *pps_attrs[] = { > &dev_attr_echo.attr, > &dev_attr_name.attr, > &dev_attr_path.attr, > + &dev_attr_pulse_width_assert.attr, > + &dev_attr_pulse_width_clear.attr, > NULL, > }; > > diff --git a/include/linux/pps_kernel.h b/include/linux/pps_kernel.h > index 78c8ac4951b5..15f2338095c6 100644 > --- a/include/linux/pps_kernel.h > +++ b/include/linux/pps_kernel.h > @@ -51,6 +51,9 @@ struct pps_device { > struct pps_ktime clear_tu; > int current_mode; /* PPS mode at event time */ > > + struct pps_kwidth assert_width; /* PPS assert pulse-width time and event seq # */ > + struct pps_kwidth clear_width; /* PPS clear pulse-width time and event seq # */ > + > unsigned int last_ev; /* last PPS event id */ > wait_queue_head_t queue; /* PPS event queue */ > > diff --git a/include/uapi/linux/pps.h b/include/uapi/linux/pps.h > index 009ebcd8ced5..dd93dac0afc1 100644 > --- a/include/uapi/linux/pps.h > +++ b/include/uapi/linux/pps.h > @@ -64,12 +64,24 @@ struct pps_ktime_compat { > } __attribute__((packed, aligned(4))); > #define PPS_TIME_INVALID (1<<0) /* used to specify timeout==NULL */ > > +struct pps_kwidth { > + __u64 nsec; > + __u32 sequence; > +}; > + > +struct pps_kwidth_compat { > + __u64 nsec; > + __u32 sequence; > +} __attribute__((packed, aligned(4))); Why do you need a new type? Since both assert_width and clear_width are time quantities as far as assert_tu and clear_tu, they can be of the same type, can't they? Or, at least they can simply be __u64 since having an assert_width or clear_width longer than 1 second is a non-sense... > struct pps_kinfo { > __u32 assert_sequence; /* seq. num. of assert event */ > __u32 clear_sequence; /* seq. num. of clear event */ > struct pps_ktime assert_tu; /* time of assert event */ > struct pps_ktime clear_tu; /* time of clear event */ > int current_mode; /* current mode bits */ > + struct pps_kwidth assert_width; /* assert pulse-width time and seq. num. */ > + struct pps_kwidth clear_width; /* clear pulse-width time and seq. num. */ > }; Altering this structure may break userspace code... also rfc2783 at section-3.2 states that: The API defines these new data structures: typedef struct { pps_seq_t assert_sequence; /* assert event seq # */ pps_seq_t clear_sequence; /* clear event seq # */ pps_timeu_t assert_tu; pps_timeu_t clear_tu; int current_mode; /* current mode bits */ } pps_info_t; So, I'm not willing to change this structure just to add this new data that I don't even know where it's used... If you just read these information via sysfs, please drop these part. > struct pps_kinfo_compat { > @@ -78,6 +90,8 @@ struct pps_kinfo_compat { > struct pps_ktime_compat assert_tu; /* time of assert event */ > struct pps_ktime_compat clear_tu; /* time of clear event */ > int current_mode; /* current mode bits */ > + struct pps_kwidth_compat assert_width; /* assert pulse-width time and seq. num. */ > + struct pps_kwidth_compat clear_width; /* clear pulse-width time and seq. num. */ > }; > > struct pps_kparams { > @@ -96,6 +110,11 @@ struct pps_kparams { > #define PPS_CAPTURECLEAR 0x02 /* capture clear events */ > #define PPS_CAPTUREBOTH 0x03 /* capture assert and clear events */ > > +/* Pulse-width calculation */ > +#define PPS_WIDTHASSERT 0x04 /* calculate assert width */ > +#define PPS_WIDTHCLEAR 0x08 /* calculate clear width */ > +#define PPS_WIDTHBOTH 0x0c /* calculate assert and clear width */ > + I don't understand why a process should ask for just PPS_WIDTHASSERT or PPS_WIDTHCLEAR... I think you can avoid defining these values and just enabling pulse width calculation when both assert and clear events are available. > #define PPS_OFFSETASSERT 0x10 /* apply compensation for assert event */ > #define PPS_OFFSETCLEAR 0x20 /* apply compensation for clear event */ However, the real point is: since an userpsace program can retrieve the time of assert and clear events, why it cannot compute the pulses width by itself? :) Ciao, Rodolfo
On 6/27/2023 5:27 PM, Rodolfo Giometti wrote: > On 25/06/23 16:21, Eliav Farber wrote: >> This change adds PPS pulse-width calculation in nano seconds. >> Width time can be calculated for both assert time and reset time. >> >> Calculation can be done only if capture ASSERT and capture CLEAR modes >> are both enabled. >> >> Assert width is calculated as: >> clear-time - assert-time >> and clear width is calculated as: >> assert-time - clear-time >> >> Read-only sysfs were added to get the last pulse-width time and event >> sequence. >> Examples: >> * cat /sys/class/pps/pps0/pulse_width_assert >> 20001450#85 >> * cat /sys/class/pps/pps1/pulse_width_clear >> 979893314#16 >> >> Signed-off-by: Eliav Farber <farbere@amazon.com> >> --- >> drivers/pps/kapi.c | 49 ++++++++++++++++++++++++++++++++++++++ >> drivers/pps/pps.c | 9 +++++++ >> drivers/pps/sysfs.c | 30 +++++++++++++++++++++++ >> include/linux/pps_kernel.h | 3 +++ >> include/uapi/linux/pps.h | 19 +++++++++++++++ >> 5 files changed, 110 insertions(+) >> >> diff --git a/drivers/pps/kapi.c b/drivers/pps/kapi.c >> index d9d566f70ed1..deeecfc0a3ee 100644 >> --- a/drivers/pps/kapi.c >> +++ b/drivers/pps/kapi.c >> @@ -82,6 +82,14 @@ struct pps_device *pps_register_source(struct >> pps_source_info *info, >> goto pps_register_source_exit; >> } >> >> + if ((info->mode & PPS_WIDTHBOTH) && >> + ((info->mode & PPS_CAPTUREBOTH) != PPS_CAPTUREBOTH)) { >> + pr_err("%s: width can't be calculated without both >> captures (mode = 0x%x)\n", >> + info->name, info->mode); >> + err = -EINVAL; >> + goto pps_register_source_exit; >> + } > > See the comment below where you define PPS_WIDTHBOTH. > >> /* Allocate memory for the new PPS source struct */ >> pps = kzalloc(sizeof(struct pps_device), GFP_KERNEL); >> if (pps == NULL) { >> @@ -143,6 +151,39 @@ void pps_unregister_source(struct pps_device *pps) >> } >> EXPORT_SYMBOL(pps_unregister_source); >> >> +static u64 pps_ktime_sub(struct pps_ktime *ts1, struct pps_ktime *ts2) >> +{ >> + if (ts1->sec == ts2->sec) >> + return (ts1->nsec > ts2->nsec) ? (ts1->nsec - >> ts2->nsec) : (ts2->nsec - ts1->nsec); >> + >> + if (ts1->sec > ts2->sec) >> + return (ts1->sec - ts2->sec) * NSEC_PER_SEC + ts1->nsec >> - ts2->nsec; >> + >> + return (ts2->sec - ts1->sec) * NSEC_PER_SEC + ts2->nsec - >> ts1->nsec; >> +} >> + >> +static void pps_calc_clear_width(struct pps_device *pps) >> +{ >> + if (pps->clear_sequence == 0) >> + return; >> + >> + pps->clear_width.sequence++; > > I don't understand the meaning of this field... regarding assert and > clear it > states the n-th sample but in this case...? Why do you need it? For assert and clear, the sequence parameter is basically the counter of assert/clear events. Similarly, I wanted to have a counter for the number of pulses which there width was counted. The sequence was used by me in the sysfs to show the pulse counter and pulse width in nano-seconds. Will counter make more sense instead of sequence? Initially, I used the assert_sequence and clear_sequence as the pulse counter, but there were few cases to handle. In case first interrupt happened during a pulse, then assert_sequence != clear_sequence, but if not then assert_sequence == clear_sequence. So I preferred to add an new independent value. >> + pps->clear_width.nsec = pps_ktime_sub(&pps->assert_tu, >> &pps->clear_tu); >> + dev_dbg(pps->dev, "PPS clear width = %llu#%u\n", >> + pps->clear_width.nsec, pps->clear_width.sequence); >> +} >> + >> +static void pps_calc_assert_width(struct pps_device *pps) >> +{ >> + if (pps->assert_sequence == 0) >> + return; >> + >> + pps->assert_width.sequence++; > > Ditto. > >> + pps->assert_width.nsec = pps_ktime_sub(&pps->clear_tu, >> &pps->assert_tu); >> + dev_dbg(pps->dev, "PPS assert width = %llu#%u\n", >> + pps->assert_width.nsec, pps->assert_width.sequence); >> +} >> + >> /* pps_event - register a PPS event into the system >> * @pps: the PPS device >> * @ts: the event timestamp >> @@ -191,6 +232,10 @@ void pps_event(struct pps_device *pps, struct >> pps_event_time *ts, int event, >> dev_dbg(pps->dev, "capture assert seq #%u\n", >> pps->assert_sequence); >> >> + /* Calculate clear pulse-width */ >> + if (pps->params.mode & PPS_WIDTHCLEAR) >> + pps_calc_clear_width(pps); >> + >> captured = ~0; >> } >> if (event & pps->params.mode & PPS_CAPTURECLEAR) { >> @@ -205,6 +250,10 @@ void pps_event(struct pps_device *pps, struct >> pps_event_time *ts, int event, >> dev_dbg(pps->dev, "capture clear seq #%u\n", >> pps->clear_sequence); >> >> + /* Calculate assert pulse-width */ >> + if (pps->params.mode & PPS_WIDTHASSERT) >> + pps_calc_assert_width(pps); >> + >> captured = ~0; >> } >> >> diff --git a/drivers/pps/pps.c b/drivers/pps/pps.c >> index 5d19baae6a38..8299a272af11 100644 >> --- a/drivers/pps/pps.c >> +++ b/drivers/pps/pps.c >> @@ -195,6 +195,11 @@ static long pps_cdev_ioctl(struct file *file, >> fdata.info.clear_tu = pps->clear_tu; >> fdata.info.current_mode = pps->current_mode; >> >> + memcpy(&fdata.info.assert_width, &pps->assert_width, >> + sizeof(struct pps_kwidth)); >> + memcpy(&fdata.info.clear_width, &pps->clear_width, >> + sizeof(struct pps_kwidth)); >> + >> spin_unlock_irq(&pps->lock); >> >> err = copy_to_user(uarg, &fdata, sizeof(struct >> pps_fdata)); >> @@ -283,6 +288,10 @@ static long pps_cdev_compat_ioctl(struct file >> *file, >> sizeof(struct pps_ktime_compat)); >> memcpy(&compat.info.clear_tu, &pps->clear_tu, >> sizeof(struct pps_ktime_compat)); >> + memcpy(&compat.info.assert_width, &pps->assert_width, >> + sizeof(struct pps_kwidth_compat)); >> + memcpy(&compat.info.clear_width, &pps->clear_width, >> + sizeof(struct pps_kwidth_compat)); >> >> spin_unlock_irq(&pps->lock); >> >> diff --git a/drivers/pps/sysfs.c b/drivers/pps/sysfs.c >> index 134bc33f6ad0..3e34de77dba6 100644 >> --- a/drivers/pps/sysfs.c >> +++ b/drivers/pps/sysfs.c >> @@ -79,6 +79,34 @@ static ssize_t path_show(struct device *dev, >> struct device_attribute *attr, >> } >> static DEVICE_ATTR_RO(path); >> >> +static ssize_t pulse_width_assert_show(struct device *dev, >> + struct device_attribute *attr, >> + char *buf) >> +{ >> + struct pps_device *pps = dev_get_drvdata(dev); >> + >> + if (!(pps->info.mode & PPS_WIDTHASSERT)) >> + return 0; >> + >> + return sprintf(buf, "%llu#%u\n", >> + pps->assert_width.nsec, >> pps->assert_width.sequence); >> +} >> +static DEVICE_ATTR_RO(pulse_width_assert); >> + >> +static ssize_t pulse_width_clear_show(struct device *dev, >> + struct device_attribute *attr, >> + char *buf) >> +{ >> + struct pps_device *pps = dev_get_drvdata(dev); >> + >> + if (!(pps->info.mode & PPS_WIDTHCLEAR)) >> + return 0; >> + >> + return sprintf(buf, "%llu#%u\n", >> + pps->clear_width.nsec, pps->clear_width.sequence); >> +} >> +static DEVICE_ATTR_RO(pulse_width_clear); >> + >> static struct attribute *pps_attrs[] = { >> &dev_attr_assert.attr, >> &dev_attr_clear.attr, >> @@ -86,6 +114,8 @@ static struct attribute *pps_attrs[] = { >> &dev_attr_echo.attr, >> &dev_attr_name.attr, >> &dev_attr_path.attr, >> + &dev_attr_pulse_width_assert.attr, >> + &dev_attr_pulse_width_clear.attr, >> NULL, >> }; >> >> diff --git a/include/linux/pps_kernel.h b/include/linux/pps_kernel.h >> index 78c8ac4951b5..15f2338095c6 100644 >> --- a/include/linux/pps_kernel.h >> +++ b/include/linux/pps_kernel.h >> @@ -51,6 +51,9 @@ struct pps_device { >> struct pps_ktime clear_tu; >> int current_mode; /* PPS mode at event >> time */ >> >> + struct pps_kwidth assert_width; /* PPS assert >> pulse-width time and event seq # */ >> + struct pps_kwidth clear_width; /* PPS clear >> pulse-width time and event seq # */ >> + >> unsigned int last_ev; /* last PPS event id */ >> wait_queue_head_t queue; /* PPS event queue */ >> >> diff --git a/include/uapi/linux/pps.h b/include/uapi/linux/pps.h >> index 009ebcd8ced5..dd93dac0afc1 100644 >> --- a/include/uapi/linux/pps.h >> +++ b/include/uapi/linux/pps.h >> @@ -64,12 +64,24 @@ struct pps_ktime_compat { >> } __attribute__((packed, aligned(4))); >> #define PPS_TIME_INVALID (1<<0) /* used to specify >> timeout==NULL */ >> >> +struct pps_kwidth { >> + __u64 nsec; >> + __u32 sequence; >> +}; >> + >> +struct pps_kwidth_compat { >> + __u64 nsec; >> + __u32 sequence; >> +} __attribute__((packed, aligned(4))); > > Why do you need a new type? Since both assert_width and clear_width > are time > quantities as far as assert_tu and clear_tu, they can be of the same > type, can't > they? Or, at least they can simply be __u64 since having an > assert_width or > clear_width longer than 1 second is a non-sense... For each pulse I wanted to save width in nsec (without sec) and counter. I need it twice for both assert and clear, hence I added a new structure for it. >> struct pps_kinfo { >> __u32 assert_sequence; /* seq. num. of assert event */ >> __u32 clear_sequence; /* seq. num. of clear event */ >> struct pps_ktime assert_tu; /* time of assert event */ >> struct pps_ktime clear_tu; /* time of clear event */ >> int current_mode; /* current mode bits */ >> + struct pps_kwidth assert_width; /* assert pulse-width time and >> seq. num. */ >> + struct pps_kwidth clear_width; /* clear pulse-width time and >> seq. num. */ >> }; > > Altering this structure may break userspace code... also rfc2783 at > section-3.2 > states that: > > The API defines these new data structures: > > typedef struct { > pps_seq_t assert_sequence; /* assert event seq # */ > pps_seq_t clear_sequence; /* clear event seq # */ > pps_timeu_t assert_tu; > pps_timeu_t clear_tu; > int current_mode; /* current mode bits */ > } pps_info_t; > > So, I'm not willing to change this structure just to add this new data > that I > don't even know where it's used... > > If you just read these information via sysfs, please drop these part. ACK. I'll drop this part. >> struct pps_kinfo_compat { >> @@ -78,6 +90,8 @@ struct pps_kinfo_compat { >> struct pps_ktime_compat assert_tu; /* time of assert event */ >> struct pps_ktime_compat clear_tu; /* time of clear event */ >> int current_mode; /* current mode bits */ >> + struct pps_kwidth_compat assert_width; /* assert pulse-width >> time and seq. num. */ >> + struct pps_kwidth_compat clear_width; /* clear pulse-width >> time and seq. num. */ >> }; >> >> struct pps_kparams { >> @@ -96,6 +110,11 @@ struct pps_kparams { >> #define PPS_CAPTURECLEAR 0x02 /* capture clear events */ >> #define PPS_CAPTUREBOTH 0x03 /* capture assert and >> clear events */ >> >> +/* Pulse-width calculation */ >> +#define PPS_WIDTHASSERT 0x04 /* calculate assert >> width */ >> +#define PPS_WIDTHCLEAR 0x08 /* calculate clear >> width */ >> +#define PPS_WIDTHBOTH 0x0c /* calculate assert and >> clear width */ >> + > > I don't understand why a process should ask for just PPS_WIDTHASSERT or > PPS_WIDTHCLEAR... I think you can avoid defining these values and just > enabling > pulse width calculation when both assert and clear events are available. ACK. I'll drop the new defines and enable width calculation when PPS_CAPTUREASSERT and PPS_CAPTURECLEAR are both defined. >> #define PPS_OFFSETASSERT 0x10 /* apply compensation for >> assert event */ >> #define PPS_OFFSETCLEAR 0x20 /* apply compensation >> for clear event */ > > However, the real point is: since an userpsace program can retrieve > the time of > assert and clear events, why it cannot compute the pulses width by > itself? :) The userpsace program can retrieve the time of assert and clear events, but it is not always clear how to compute it. Initially that was how I did it: Read both times, make sure sequence of both times was identical, and then compute: clear_time – assert_time. But as I mentioned, when using wide pulses, it might be that when driver starts, it is a the middle of a pulse. In that case clear_time will be captured first (seq #1). Then assert_time is captured (seq #1). However, assert pulse width can only be calculated for the second clear-time sequence and first assert-time sequence. So to simplify this for the user, I added the calculation to the driver. Hope this was clear. Please let me know if this satisfies you, and then I’ll share a second version of patches which fixes all the other comments you gave. --- Regards, Eliav
On 02/07/23 14:20, Farber, Eliav wrote: > On 6/27/2023 5:27 PM, Rodolfo Giometti wrote: >> On 25/06/23 16:21, Eliav Farber wrote: >>> This change adds PPS pulse-width calculation in nano seconds. >>> Width time can be calculated for both assert time and reset time. >>> >>> Calculation can be done only if capture ASSERT and capture CLEAR modes >>> are both enabled. >>> >>> Assert width is calculated as: >>> clear-time - assert-time >>> and clear width is calculated as: >>> assert-time - clear-time >>> >>> Read-only sysfs were added to get the last pulse-width time and event >>> sequence. >>> Examples: >>> * cat /sys/class/pps/pps0/pulse_width_assert >>> 20001450#85 >>> * cat /sys/class/pps/pps1/pulse_width_clear >>> 979893314#16 >>> >>> Signed-off-by: Eliav Farber <farbere@amazon.com> >>> --- >>> drivers/pps/kapi.c | 49 ++++++++++++++++++++++++++++++++++++++ >>> drivers/pps/pps.c | 9 +++++++ >>> drivers/pps/sysfs.c | 30 +++++++++++++++++++++++ >>> include/linux/pps_kernel.h | 3 +++ >>> include/uapi/linux/pps.h | 19 +++++++++++++++ >>> 5 files changed, 110 insertions(+) >>> >>> diff --git a/drivers/pps/kapi.c b/drivers/pps/kapi.c >>> index d9d566f70ed1..deeecfc0a3ee 100644 >>> --- a/drivers/pps/kapi.c >>> +++ b/drivers/pps/kapi.c >>> @@ -82,6 +82,14 @@ struct pps_device *pps_register_source(struct >>> pps_source_info *info, >>> goto pps_register_source_exit; >>> } >>> >>> + if ((info->mode & PPS_WIDTHBOTH) && >>> + ((info->mode & PPS_CAPTUREBOTH) != PPS_CAPTUREBOTH)) { >>> + pr_err("%s: width can't be calculated without both captures >>> (mode = 0x%x)\n", >>> + info->name, info->mode); >>> + err = -EINVAL; >>> + goto pps_register_source_exit; >>> + } >> >> See the comment below where you define PPS_WIDTHBOTH. >> >>> /* Allocate memory for the new PPS source struct */ >>> pps = kzalloc(sizeof(struct pps_device), GFP_KERNEL); >>> if (pps == NULL) { >>> @@ -143,6 +151,39 @@ void pps_unregister_source(struct pps_device *pps) >>> } >>> EXPORT_SYMBOL(pps_unregister_source); >>> >>> +static u64 pps_ktime_sub(struct pps_ktime *ts1, struct pps_ktime *ts2) >>> +{ >>> + if (ts1->sec == ts2->sec) >>> + return (ts1->nsec > ts2->nsec) ? (ts1->nsec - ts2->nsec) : >>> (ts2->nsec - ts1->nsec); >>> + >>> + if (ts1->sec > ts2->sec) >>> + return (ts1->sec - ts2->sec) * NSEC_PER_SEC + ts1->nsec - >>> ts2->nsec; >>> + >>> + return (ts2->sec - ts1->sec) * NSEC_PER_SEC + ts2->nsec - ts1->nsec; >>> +} >>> + >>> +static void pps_calc_clear_width(struct pps_device *pps) >>> +{ >>> + if (pps->clear_sequence == 0) >>> + return; >>> + >>> + pps->clear_width.sequence++; >> >> I don't understand the meaning of this field... regarding assert and clear it >> states the n-th sample but in this case...? Why do you need it? > > For assert and clear, the sequence parameter is basically the counter > of assert/clear events. > Similarly, I wanted to have a counter for the number of pulses which > there width was counted. > The sequence was used by me in the sysfs to show the pulse counter and > pulse width in nano-seconds. > Will counter make more sense instead of sequence? > Initially, I used the assert_sequence and clear_sequence as the pulse > counter, but there were few cases to handle. > In case first interrupt happened during a pulse, then > assert_sequence != clear_sequence, but if not then > assert_sequence == clear_sequence. > So I preferred to add an new independent value. OK. >>> + pps->clear_width.nsec = pps_ktime_sub(&pps->assert_tu, &pps->clear_tu); >>> + dev_dbg(pps->dev, "PPS clear width = %llu#%u\n", >>> + pps->clear_width.nsec, pps->clear_width.sequence); >>> +} >>> + >>> +static void pps_calc_assert_width(struct pps_device *pps) >>> +{ >>> + if (pps->assert_sequence == 0) >>> + return; >>> + >>> + pps->assert_width.sequence++; >> >> Ditto. >> >>> + pps->assert_width.nsec = pps_ktime_sub(&pps->clear_tu, &pps->assert_tu); >>> + dev_dbg(pps->dev, "PPS assert width = %llu#%u\n", >>> + pps->assert_width.nsec, pps->assert_width.sequence); >>> +} >>> + >>> /* pps_event - register a PPS event into the system >>> * @pps: the PPS device >>> * @ts: the event timestamp >>> @@ -191,6 +232,10 @@ void pps_event(struct pps_device *pps, struct >>> pps_event_time *ts, int event, >>> dev_dbg(pps->dev, "capture assert seq #%u\n", >>> pps->assert_sequence); >>> >>> + /* Calculate clear pulse-width */ >>> + if (pps->params.mode & PPS_WIDTHCLEAR) >>> + pps_calc_clear_width(pps); >>> + >>> captured = ~0; >>> } >>> if (event & pps->params.mode & PPS_CAPTURECLEAR) { >>> @@ -205,6 +250,10 @@ void pps_event(struct pps_device *pps, struct >>> pps_event_time *ts, int event, >>> dev_dbg(pps->dev, "capture clear seq #%u\n", >>> pps->clear_sequence); >>> >>> + /* Calculate assert pulse-width */ >>> + if (pps->params.mode & PPS_WIDTHASSERT) >>> + pps_calc_assert_width(pps); >>> + >>> captured = ~0; >>> } >>> >>> diff --git a/drivers/pps/pps.c b/drivers/pps/pps.c >>> index 5d19baae6a38..8299a272af11 100644 >>> --- a/drivers/pps/pps.c >>> +++ b/drivers/pps/pps.c >>> @@ -195,6 +195,11 @@ static long pps_cdev_ioctl(struct file *file, >>> fdata.info.clear_tu = pps->clear_tu; >>> fdata.info.current_mode = pps->current_mode; >>> >>> + memcpy(&fdata.info.assert_width, &pps->assert_width, >>> + sizeof(struct pps_kwidth)); >>> + memcpy(&fdata.info.clear_width, &pps->clear_width, >>> + sizeof(struct pps_kwidth)); >>> + >>> spin_unlock_irq(&pps->lock); >>> >>> err = copy_to_user(uarg, &fdata, sizeof(struct pps_fdata)); >>> @@ -283,6 +288,10 @@ static long pps_cdev_compat_ioctl(struct file *file, >>> sizeof(struct pps_ktime_compat)); >>> memcpy(&compat.info.clear_tu, &pps->clear_tu, >>> sizeof(struct pps_ktime_compat)); >>> + memcpy(&compat.info.assert_width, &pps->assert_width, >>> + sizeof(struct pps_kwidth_compat)); >>> + memcpy(&compat.info.clear_width, &pps->clear_width, >>> + sizeof(struct pps_kwidth_compat)); >>> >>> spin_unlock_irq(&pps->lock); >>> >>> diff --git a/drivers/pps/sysfs.c b/drivers/pps/sysfs.c >>> index 134bc33f6ad0..3e34de77dba6 100644 >>> --- a/drivers/pps/sysfs.c >>> +++ b/drivers/pps/sysfs.c >>> @@ -79,6 +79,34 @@ static ssize_t path_show(struct device *dev, struct >>> device_attribute *attr, >>> } >>> static DEVICE_ATTR_RO(path); >>> >>> +static ssize_t pulse_width_assert_show(struct device *dev, >>> + struct device_attribute *attr, >>> + char *buf) >>> +{ >>> + struct pps_device *pps = dev_get_drvdata(dev); >>> + >>> + if (!(pps->info.mode & PPS_WIDTHASSERT)) >>> + return 0; >>> + >>> + return sprintf(buf, "%llu#%u\n", >>> + pps->assert_width.nsec, pps->assert_width.sequence); >>> +} >>> +static DEVICE_ATTR_RO(pulse_width_assert); >>> + >>> +static ssize_t pulse_width_clear_show(struct device *dev, >>> + struct device_attribute *attr, >>> + char *buf) >>> +{ >>> + struct pps_device *pps = dev_get_drvdata(dev); >>> + >>> + if (!(pps->info.mode & PPS_WIDTHCLEAR)) >>> + return 0; >>> + >>> + return sprintf(buf, "%llu#%u\n", >>> + pps->clear_width.nsec, pps->clear_width.sequence); >>> +} >>> +static DEVICE_ATTR_RO(pulse_width_clear); >>> + >>> static struct attribute *pps_attrs[] = { >>> &dev_attr_assert.attr, >>> &dev_attr_clear.attr, >>> @@ -86,6 +114,8 @@ static struct attribute *pps_attrs[] = { >>> &dev_attr_echo.attr, >>> &dev_attr_name.attr, >>> &dev_attr_path.attr, >>> + &dev_attr_pulse_width_assert.attr, >>> + &dev_attr_pulse_width_clear.attr, >>> NULL, >>> }; >>> >>> diff --git a/include/linux/pps_kernel.h b/include/linux/pps_kernel.h >>> index 78c8ac4951b5..15f2338095c6 100644 >>> --- a/include/linux/pps_kernel.h >>> +++ b/include/linux/pps_kernel.h >>> @@ -51,6 +51,9 @@ struct pps_device { >>> struct pps_ktime clear_tu; >>> int current_mode; /* PPS mode at event time */ >>> >>> + struct pps_kwidth assert_width; /* PPS assert pulse-width time >>> and event seq # */ >>> + struct pps_kwidth clear_width; /* PPS clear pulse-width time >>> and event seq # */ >>> + >>> unsigned int last_ev; /* last PPS event id */ >>> wait_queue_head_t queue; /* PPS event queue */ >>> >>> diff --git a/include/uapi/linux/pps.h b/include/uapi/linux/pps.h >>> index 009ebcd8ced5..dd93dac0afc1 100644 >>> --- a/include/uapi/linux/pps.h >>> +++ b/include/uapi/linux/pps.h >>> @@ -64,12 +64,24 @@ struct pps_ktime_compat { >>> } __attribute__((packed, aligned(4))); >>> #define PPS_TIME_INVALID (1<<0) /* used to specify timeout==NULL */ >>> >>> +struct pps_kwidth { >>> + __u64 nsec; >>> + __u32 sequence; >>> +}; >>> + >>> +struct pps_kwidth_compat { >>> + __u64 nsec; >>> + __u32 sequence; >>> +} __attribute__((packed, aligned(4))); >> >> Why do you need a new type? Since both assert_width and clear_width are time >> quantities as far as assert_tu and clear_tu, they can be of the same type, can't >> they? Or, at least they can simply be __u64 since having an assert_width or >> clear_width longer than 1 second is a non-sense... > > For each pulse I wanted to save width in nsec (without sec) and > counter. > I need it twice for both assert and clear, hence I added a new > structure for it. I see, but I prefere you do as in struct pps_kinfo where times are times and sequence numbers are numbers, and not mixing them. >>> struct pps_kinfo { >>> __u32 assert_sequence; /* seq. num. of assert event */ >>> __u32 clear_sequence; /* seq. num. of clear event */ >>> struct pps_ktime assert_tu; /* time of assert event */ >>> struct pps_ktime clear_tu; /* time of clear event */ >>> int current_mode; /* current mode bits */ >>> + struct pps_kwidth assert_width; /* assert pulse-width time and seq. >>> num. */ >>> + struct pps_kwidth clear_width; /* clear pulse-width time and seq. num. */ >>> }; >> >> Altering this structure may break userspace code... also rfc2783 at section-3.2 >> states that: >> >> The API defines these new data structures: >> >> typedef struct { >> pps_seq_t assert_sequence; /* assert event seq # */ >> pps_seq_t clear_sequence; /* clear event seq # */ >> pps_timeu_t assert_tu; >> pps_timeu_t clear_tu; >> int current_mode; /* current mode bits */ >> } pps_info_t; >> >> So, I'm not willing to change this structure just to add this new data that I >> don't even know where it's used... >> >> If you just read these information via sysfs, please drop these part. > > ACK. I'll drop this part. > >>> struct pps_kinfo_compat { >>> @@ -78,6 +90,8 @@ struct pps_kinfo_compat { >>> struct pps_ktime_compat assert_tu; /* time of assert event */ >>> struct pps_ktime_compat clear_tu; /* time of clear event */ >>> int current_mode; /* current mode bits */ >>> + struct pps_kwidth_compat assert_width; /* assert pulse-width time and >>> seq. num. */ >>> + struct pps_kwidth_compat clear_width; /* clear pulse-width time and >>> seq. num. */ >>> }; >>> >>> struct pps_kparams { >>> @@ -96,6 +110,11 @@ struct pps_kparams { >>> #define PPS_CAPTURECLEAR 0x02 /* capture clear events */ >>> #define PPS_CAPTUREBOTH 0x03 /* capture assert and clear >>> events */ >>> >>> +/* Pulse-width calculation */ >>> +#define PPS_WIDTHASSERT 0x04 /* calculate assert width */ >>> +#define PPS_WIDTHCLEAR 0x08 /* calculate clear width */ >>> +#define PPS_WIDTHBOTH 0x0c /* calculate assert and clear >>> width */ >>> + >> >> I don't understand why a process should ask for just PPS_WIDTHASSERT or >> PPS_WIDTHCLEAR... I think you can avoid defining these values and just enabling >> pulse width calculation when both assert and clear events are available. > > ACK. I'll drop the new defines and enable width calculation when > PPS_CAPTUREASSERT and PPS_CAPTURECLEAR are both defined. > >>> #define PPS_OFFSETASSERT 0x10 /* apply compensation for assert event */ >>> #define PPS_OFFSETCLEAR 0x20 /* apply compensation for clear >>> event */ >> >> However, the real point is: since an userpsace program can retrieve the time of >> assert and clear events, why it cannot compute the pulses width by itself? :) > > The userpsace program can retrieve the time of assert and clear events, > but it is not always clear how to compute it. > Initially that was how I did it: > Read both times, make sure sequence of both times was identical, and > then compute: clear_time – assert_time. > But as I mentioned, when using wide pulses, it might be that when > driver starts, it is a the middle of a pulse. > In that case clear_time will be captured first (seq #1). > Then assert_time is captured (seq #1). > However, assert pulse width can only be calculated for the second > clear-time sequence and first assert-time sequence. > So to simplify this for the user, I added the calculation to the > driver. > Hope this was clear. > Please let me know if this satisfies you, and then I’ll share a second > version of patches which fixes all the other comments you gave. Mmm... kernel drivers should implement mechanisms and not policies and since RFC2783 doesn't state this computation I think you are implementing a policy. Let me suggest to add this piece of code to the pps-utils (maybe within the ppstest.c utility). Ciao, Rodolfo
diff --git a/drivers/pps/kapi.c b/drivers/pps/kapi.c index d9d566f70ed1..deeecfc0a3ee 100644 --- a/drivers/pps/kapi.c +++ b/drivers/pps/kapi.c @@ -82,6 +82,14 @@ struct pps_device *pps_register_source(struct pps_source_info *info, goto pps_register_source_exit; } + if ((info->mode & PPS_WIDTHBOTH) && + ((info->mode & PPS_CAPTUREBOTH) != PPS_CAPTUREBOTH)) { + pr_err("%s: width can't be calculated without both captures (mode = 0x%x)\n", + info->name, info->mode); + err = -EINVAL; + goto pps_register_source_exit; + } + /* Allocate memory for the new PPS source struct */ pps = kzalloc(sizeof(struct pps_device), GFP_KERNEL); if (pps == NULL) { @@ -143,6 +151,39 @@ void pps_unregister_source(struct pps_device *pps) } EXPORT_SYMBOL(pps_unregister_source); +static u64 pps_ktime_sub(struct pps_ktime *ts1, struct pps_ktime *ts2) +{ + if (ts1->sec == ts2->sec) + return (ts1->nsec > ts2->nsec) ? (ts1->nsec - ts2->nsec) : (ts2->nsec - ts1->nsec); + + if (ts1->sec > ts2->sec) + return (ts1->sec - ts2->sec) * NSEC_PER_SEC + ts1->nsec - ts2->nsec; + + return (ts2->sec - ts1->sec) * NSEC_PER_SEC + ts2->nsec - ts1->nsec; +} + +static void pps_calc_clear_width(struct pps_device *pps) +{ + if (pps->clear_sequence == 0) + return; + + pps->clear_width.sequence++; + pps->clear_width.nsec = pps_ktime_sub(&pps->assert_tu, &pps->clear_tu); + dev_dbg(pps->dev, "PPS clear width = %llu#%u\n", + pps->clear_width.nsec, pps->clear_width.sequence); +} + +static void pps_calc_assert_width(struct pps_device *pps) +{ + if (pps->assert_sequence == 0) + return; + + pps->assert_width.sequence++; + pps->assert_width.nsec = pps_ktime_sub(&pps->clear_tu, &pps->assert_tu); + dev_dbg(pps->dev, "PPS assert width = %llu#%u\n", + pps->assert_width.nsec, pps->assert_width.sequence); +} + /* pps_event - register a PPS event into the system * @pps: the PPS device * @ts: the event timestamp @@ -191,6 +232,10 @@ void pps_event(struct pps_device *pps, struct pps_event_time *ts, int event, dev_dbg(pps->dev, "capture assert seq #%u\n", pps->assert_sequence); + /* Calculate clear pulse-width */ + if (pps->params.mode & PPS_WIDTHCLEAR) + pps_calc_clear_width(pps); + captured = ~0; } if (event & pps->params.mode & PPS_CAPTURECLEAR) { @@ -205,6 +250,10 @@ void pps_event(struct pps_device *pps, struct pps_event_time *ts, int event, dev_dbg(pps->dev, "capture clear seq #%u\n", pps->clear_sequence); + /* Calculate assert pulse-width */ + if (pps->params.mode & PPS_WIDTHASSERT) + pps_calc_assert_width(pps); + captured = ~0; } diff --git a/drivers/pps/pps.c b/drivers/pps/pps.c index 5d19baae6a38..8299a272af11 100644 --- a/drivers/pps/pps.c +++ b/drivers/pps/pps.c @@ -195,6 +195,11 @@ static long pps_cdev_ioctl(struct file *file, fdata.info.clear_tu = pps->clear_tu; fdata.info.current_mode = pps->current_mode; + memcpy(&fdata.info.assert_width, &pps->assert_width, + sizeof(struct pps_kwidth)); + memcpy(&fdata.info.clear_width, &pps->clear_width, + sizeof(struct pps_kwidth)); + spin_unlock_irq(&pps->lock); err = copy_to_user(uarg, &fdata, sizeof(struct pps_fdata)); @@ -283,6 +288,10 @@ static long pps_cdev_compat_ioctl(struct file *file, sizeof(struct pps_ktime_compat)); memcpy(&compat.info.clear_tu, &pps->clear_tu, sizeof(struct pps_ktime_compat)); + memcpy(&compat.info.assert_width, &pps->assert_width, + sizeof(struct pps_kwidth_compat)); + memcpy(&compat.info.clear_width, &pps->clear_width, + sizeof(struct pps_kwidth_compat)); spin_unlock_irq(&pps->lock); diff --git a/drivers/pps/sysfs.c b/drivers/pps/sysfs.c index 134bc33f6ad0..3e34de77dba6 100644 --- a/drivers/pps/sysfs.c +++ b/drivers/pps/sysfs.c @@ -79,6 +79,34 @@ static ssize_t path_show(struct device *dev, struct device_attribute *attr, } static DEVICE_ATTR_RO(path); +static ssize_t pulse_width_assert_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct pps_device *pps = dev_get_drvdata(dev); + + if (!(pps->info.mode & PPS_WIDTHASSERT)) + return 0; + + return sprintf(buf, "%llu#%u\n", + pps->assert_width.nsec, pps->assert_width.sequence); +} +static DEVICE_ATTR_RO(pulse_width_assert); + +static ssize_t pulse_width_clear_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct pps_device *pps = dev_get_drvdata(dev); + + if (!(pps->info.mode & PPS_WIDTHCLEAR)) + return 0; + + return sprintf(buf, "%llu#%u\n", + pps->clear_width.nsec, pps->clear_width.sequence); +} +static DEVICE_ATTR_RO(pulse_width_clear); + static struct attribute *pps_attrs[] = { &dev_attr_assert.attr, &dev_attr_clear.attr, @@ -86,6 +114,8 @@ static struct attribute *pps_attrs[] = { &dev_attr_echo.attr, &dev_attr_name.attr, &dev_attr_path.attr, + &dev_attr_pulse_width_assert.attr, + &dev_attr_pulse_width_clear.attr, NULL, }; diff --git a/include/linux/pps_kernel.h b/include/linux/pps_kernel.h index 78c8ac4951b5..15f2338095c6 100644 --- a/include/linux/pps_kernel.h +++ b/include/linux/pps_kernel.h @@ -51,6 +51,9 @@ struct pps_device { struct pps_ktime clear_tu; int current_mode; /* PPS mode at event time */ + struct pps_kwidth assert_width; /* PPS assert pulse-width time and event seq # */ + struct pps_kwidth clear_width; /* PPS clear pulse-width time and event seq # */ + unsigned int last_ev; /* last PPS event id */ wait_queue_head_t queue; /* PPS event queue */ diff --git a/include/uapi/linux/pps.h b/include/uapi/linux/pps.h index 009ebcd8ced5..dd93dac0afc1 100644 --- a/include/uapi/linux/pps.h +++ b/include/uapi/linux/pps.h @@ -64,12 +64,24 @@ struct pps_ktime_compat { } __attribute__((packed, aligned(4))); #define PPS_TIME_INVALID (1<<0) /* used to specify timeout==NULL */ +struct pps_kwidth { + __u64 nsec; + __u32 sequence; +}; + +struct pps_kwidth_compat { + __u64 nsec; + __u32 sequence; +} __attribute__((packed, aligned(4))); + struct pps_kinfo { __u32 assert_sequence; /* seq. num. of assert event */ __u32 clear_sequence; /* seq. num. of clear event */ struct pps_ktime assert_tu; /* time of assert event */ struct pps_ktime clear_tu; /* time of clear event */ int current_mode; /* current mode bits */ + struct pps_kwidth assert_width; /* assert pulse-width time and seq. num. */ + struct pps_kwidth clear_width; /* clear pulse-width time and seq. num. */ }; struct pps_kinfo_compat { @@ -78,6 +90,8 @@ struct pps_kinfo_compat { struct pps_ktime_compat assert_tu; /* time of assert event */ struct pps_ktime_compat clear_tu; /* time of clear event */ int current_mode; /* current mode bits */ + struct pps_kwidth_compat assert_width; /* assert pulse-width time and seq. num. */ + struct pps_kwidth_compat clear_width; /* clear pulse-width time and seq. num. */ }; struct pps_kparams { @@ -96,6 +110,11 @@ struct pps_kparams { #define PPS_CAPTURECLEAR 0x02 /* capture clear events */ #define PPS_CAPTUREBOTH 0x03 /* capture assert and clear events */ +/* Pulse-width calculation */ +#define PPS_WIDTHASSERT 0x04 /* calculate assert width */ +#define PPS_WIDTHCLEAR 0x08 /* calculate clear width */ +#define PPS_WIDTHBOTH 0x0c /* calculate assert and clear width */ + #define PPS_OFFSETASSERT 0x10 /* apply compensation for assert event */ #define PPS_OFFSETCLEAR 0x20 /* apply compensation for clear event */