The CPTS hardware doesn't support PPS signal generation. Using the GenFx
(periodic signal generator) function, it is possible to model a PPS signal
followed by routing it via the time sync router to the CPTS_HWy_TS_PUSH
(hardware time stamp) input, in order to generate timestamps at 1 second
intervals.
This series adds driver support for enabling PPS signal generation.
Additionally, the documentation for the am65-cpts driver is updated with
the bindings for the "ti,pps" property, which is used to inform the
pair [CPTS_HWy_TS_PUSH, GenFx] to the cpts driver.
Changes from v1:
1. Drop device-tree patches.
2. Address Roger's comments on the:
"net: ethernet: ti: am65-cpts: add pps support" patch.
3. Collect Reviewed-by tag from Rob Herring.
v1:
https://lore.kernel.org/r/[email protected]/
Grygorii Strashko (3):
dt-binding: net: ti: am65x-cpts: add 'ti,pps' property
net: ethernet: ti: am65-cpts: add pps support
net: ethernet: ti: am65-cpts: adjust pps following ptp changes
.../bindings/net/ti,k3-am654-cpts.yaml | 8 +
drivers/net/ethernet/ti/am65-cpts.c | 155 ++++++++++++++++--
2 files changed, 148 insertions(+), 15 deletions(-)
--
2.25.1
From: Grygorii Strashko <[email protected]>
CPTS doesn't have HW support for PPS ("pulse per second”) signal
generation, but it can be modeled by using Time Sync Router and routing
GenFx (periodic signal generator) output to CPTS_HWy_TS_PUSH (hardware time
stamp) input, and configuring GenFx to generate 1sec pulses.
+------------------------+
| CPTS |
| |
+--->CPTS_HW4_PUSH GENFx+---+
| | | |
| +------------------------+ |
| |
+--------------------------------+
Add corresponding support to am65-cpts driver. The DT property "ti,pps"
has to be used to enable PPS support and configure pair
[CPTS_HWy_TS_PUSH, GenFx].
Once enabled, PPS can be tested using ppstest tool:
# ./ppstest /dev/pps0
Signed-off-by: Grygorii Strashko <[email protected]>
Signed-off-by: Siddharth Vadapalli <[email protected]>
---
drivers/net/ethernet/ti/am65-cpts.c | 96 +++++++++++++++++++++++++++--
1 file changed, 91 insertions(+), 5 deletions(-)
diff --git a/drivers/net/ethernet/ti/am65-cpts.c b/drivers/net/ethernet/ti/am65-cpts.c
index 9535396b28cd..c405af266b41 100644
--- a/drivers/net/ethernet/ti/am65-cpts.c
+++ b/drivers/net/ethernet/ti/am65-cpts.c
@@ -176,6 +176,10 @@ struct am65_cpts {
u32 genf_enable;
u32 hw_ts_enable;
struct sk_buff_head txq;
+ bool pps_enabled;
+ bool pps_present;
+ u32 pps_hw_ts_idx;
+ u32 pps_genf_idx;
/* context save/restore */
u64 sr_cpts_ns;
u64 sr_ktime_ns;
@@ -319,8 +323,15 @@ static int am65_cpts_fifo_read(struct am65_cpts *cpts)
case AM65_CPTS_EV_HW:
pevent.index = am65_cpts_event_get_port(event) - 1;
pevent.timestamp = event->timestamp;
- pevent.type = PTP_CLOCK_EXTTS;
- dev_dbg(cpts->dev, "AM65_CPTS_EV_HW p:%d t:%llu\n",
+ if (cpts->pps_enabled && pevent.index == cpts->pps_hw_ts_idx) {
+ pevent.type = PTP_CLOCK_PPSUSR;
+ pevent.pps_times.ts_real = ns_to_timespec64(pevent.timestamp);
+ } else {
+ pevent.type = PTP_CLOCK_EXTTS;
+ }
+ dev_dbg(cpts->dev, "AM65_CPTS_EV_HW:%s p:%d t:%llu\n",
+ pevent.type == PTP_CLOCK_EXTTS ?
+ "extts" : "pps",
pevent.index, event->timestamp);
ptp_clock_event(cpts->ptp_clock, &pevent);
@@ -507,7 +518,13 @@ static void am65_cpts_extts_enable_hw(struct am65_cpts *cpts, u32 index, int on)
static int am65_cpts_extts_enable(struct am65_cpts *cpts, u32 index, int on)
{
- if (!!(cpts->hw_ts_enable & BIT(index)) == !!on)
+ if (index >= cpts->ptp_info.n_ext_ts)
+ return -ENXIO;
+
+ if (cpts->pps_present && index == cpts->pps_hw_ts_idx)
+ return -EINVAL;
+
+ if (((cpts->hw_ts_enable & BIT(index)) >> index) == on)
return 0;
mutex_lock(&cpts->ptp_clk_lock);
@@ -591,6 +608,12 @@ static void am65_cpts_perout_enable_hw(struct am65_cpts *cpts,
static int am65_cpts_perout_enable(struct am65_cpts *cpts,
struct ptp_perout_request *req, int on)
{
+ if (req->index >= cpts->ptp_info.n_per_out)
+ return -ENXIO;
+
+ if (cpts->pps_present && req->index == cpts->pps_genf_idx)
+ return -EINVAL;
+
if (!!(cpts->genf_enable & BIT(req->index)) == !!on)
return 0;
@@ -604,6 +627,48 @@ static int am65_cpts_perout_enable(struct am65_cpts *cpts,
return 0;
}
+static int am65_cpts_pps_enable(struct am65_cpts *cpts, int on)
+{
+ int ret = 0;
+ struct timespec64 ts;
+ struct ptp_clock_request rq;
+ u64 ns;
+
+ if (!cpts->pps_present)
+ return -EINVAL;
+
+ if (cpts->pps_enabled == !!on)
+ return 0;
+
+ mutex_lock(&cpts->ptp_clk_lock);
+
+ if (on) {
+ am65_cpts_extts_enable_hw(cpts, cpts->pps_hw_ts_idx, on);
+
+ ns = am65_cpts_gettime(cpts, NULL);
+ ts = ns_to_timespec64(ns);
+ rq.perout.period.sec = 1;
+ rq.perout.period.nsec = 0;
+ rq.perout.start.sec = ts.tv_sec + 2;
+ rq.perout.start.nsec = 0;
+ rq.perout.index = cpts->pps_genf_idx;
+
+ am65_cpts_perout_enable_hw(cpts, &rq.perout, on);
+ cpts->pps_enabled = true;
+ } else {
+ rq.perout.index = cpts->pps_genf_idx;
+ am65_cpts_perout_enable_hw(cpts, &rq.perout, on);
+ am65_cpts_extts_enable_hw(cpts, cpts->pps_hw_ts_idx, on);
+ cpts->pps_enabled = false;
+ }
+
+ mutex_unlock(&cpts->ptp_clk_lock);
+
+ dev_dbg(cpts->dev, "%s: pps: %s\n",
+ __func__, on ? "enabled" : "disabled");
+ return ret;
+}
+
static int am65_cpts_ptp_enable(struct ptp_clock_info *ptp,
struct ptp_clock_request *rq, int on)
{
@@ -614,6 +679,8 @@ static int am65_cpts_ptp_enable(struct ptp_clock_info *ptp,
return am65_cpts_extts_enable(cpts, rq->extts.index, on);
case PTP_CLK_REQ_PEROUT:
return am65_cpts_perout_enable(cpts, &rq->perout, on);
+ case PTP_CLK_REQ_PPS:
+ return am65_cpts_pps_enable(cpts, on);
default:
break;
}
@@ -926,6 +993,23 @@ static int am65_cpts_of_parse(struct am65_cpts *cpts, struct device_node *node)
if (!of_property_read_u32(node, "ti,cpts-periodic-outputs", &prop[0]))
cpts->genf_num = prop[0];
+ if (!of_property_read_u32_array(node, "ti,pps", prop, 2)) {
+ cpts->pps_present = true;
+
+ if (prop[0] > 7) {
+ dev_err(cpts->dev, "invalid HWx_TS_PUSH index: %u provided\n", prop[0]);
+ cpts->pps_present = false;
+ }
+ if (prop[1] > 1) {
+ dev_err(cpts->dev, "invalid GENFy index: %u provided\n", prop[1]);
+ cpts->pps_present = false;
+ }
+ if (cpts->pps_present) {
+ cpts->pps_hw_ts_idx = prop[0];
+ cpts->pps_genf_idx = prop[1];
+ }
+ }
+
return cpts_of_mux_clk_setup(cpts, node);
}
@@ -993,6 +1077,8 @@ struct am65_cpts *am65_cpts_create(struct device *dev, void __iomem *regs,
cpts->ptp_info.n_ext_ts = cpts->ext_ts_inputs;
if (cpts->genf_num)
cpts->ptp_info.n_per_out = cpts->genf_num;
+ if (cpts->pps_present)
+ cpts->ptp_info.pps = 1;
am65_cpts_set_add_val(cpts);
@@ -1028,9 +1114,9 @@ struct am65_cpts *am65_cpts_create(struct device *dev, void __iomem *regs,
return ERR_PTR(ret);
}
- dev_info(dev, "CPTS ver 0x%08x, freq:%u, add_val:%u\n",
+ dev_info(dev, "CPTS ver 0x%08x, freq:%u, add_val:%u pps:%d\n",
am65_cpts_read32(cpts, idver),
- cpts->refclk_freq, cpts->ts_add_val);
+ cpts->refclk_freq, cpts->ts_add_val, cpts->pps_present);
return cpts;
--
2.25.1
From: Grygorii Strashko <[email protected]>
Add the ti,pps property used to indicate the pair of HWx_TS_PUSH input and
the TS_GENFy output.
Signed-off-by: Grygorii Strashko <[email protected]>
Signed-off-by: Siddharth Vadapalli <[email protected]>
Reviewed-by: Rob Herring <[email protected]>
---
.../devicetree/bindings/net/ti,k3-am654-cpts.yaml | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/Documentation/devicetree/bindings/net/ti,k3-am654-cpts.yaml b/Documentation/devicetree/bindings/net/ti,k3-am654-cpts.yaml
index 6230f576134b..3e910d3b24a0 100644
--- a/Documentation/devicetree/bindings/net/ti,k3-am654-cpts.yaml
+++ b/Documentation/devicetree/bindings/net/ti,k3-am654-cpts.yaml
@@ -93,6 +93,14 @@ properties:
description:
Number of timestamp Generator function outputs (TS_GENFx)
+ ti,pps:
+ $ref: /schemas/types.yaml#/definitions/uint32-array
+ minItems: 2
+ maxItems: 2
+ description: |
+ The pair of HWx_TS_PUSH input and TS_GENFy output indexes used for
+ PPS events generation. Platform/board specific.
+
refclk-mux:
type: object
additionalProperties: false
--
2.25.1
From: Grygorii Strashko <[email protected]>
When CPTS clock is sync/adjusted by running linuxptp (ptp4l) it will cause
PPS jitter as Genf running PPS is not adjusted.
The same PPM adjustment has to be applied to GenF as to PHC clock to
correct PPS length and keep them in sync.
Testing:
Master:
ptp4l -P -2 -H -i eth0 -l 6 -m -q -p /dev/ptp1 -f ptp.cfg &
testptp -d /dev/ptp1 -P 1
ppstest /dev/pps0
Slave:
linuxptp/ptp4l -P -2 -H -i eth0 -l 6 -m -q -p /dev/ptp1 -f ptp1.cfg -s &
<port 1: UNCALIBRATED to SLAVE on MASTER_CLOCK_SELECTED;>
testptp -d /dev/ptp1 -P 1
ppstest /dev/pps0
Master log:
source 0 - assert 620.000000689, sequence: 530
source 0 - assert 621.000000689, sequence: 531
source 0 - assert 622.000000689, sequence: 532
source 0 - assert 623.000000689, sequence: 533
source 0 - assert 624.000000689, sequence: 534
source 0 - assert 625.000000689, sequence: 535
source 0 - assert 626.000000689, sequence: 536
source 0 - assert 627.000000689, sequence: 537
source 0 - assert 628.000000689, sequence: 538
source 0 - assert 629.000000689, sequence: 539
source 0 - assert 630.000000689, sequence: 540
source 0 - assert 631.000000689, sequence: 541
source 0 - assert 632.000000689, sequence: 542
source 0 - assert 633.000000689, sequence: 543
source 0 - assert 634.000000689, sequence: 544
source 0 - assert 635.000000689, sequence: 545
Slave log:
source 0 - assert 620.000000706, sequence: 252
source 0 - assert 621.000000709, sequence: 253
source 0 - assert 622.000000707, sequence: 254
source 0 - assert 623.000000707, sequence: 255
source 0 - assert 624.000000706, sequence: 256
source 0 - assert 625.000000705, sequence: 257
source 0 - assert 626.000000709, sequence: 258
source 0 - assert 627.000000709, sequence: 259
source 0 - assert 628.000000707, sequence: 260
source 0 - assert 629.000000706, sequence: 261
source 0 - assert 630.000000710, sequence: 262
source 0 - assert 631.000000708, sequence: 263
source 0 - assert 632.000000705, sequence: 264
source 0 - assert 633.000000710, sequence: 265
source 0 - assert 634.000000708, sequence: 266
source 0 - assert 635.000000707, sequence: 267
Signed-off-by: Grygorii Strashko <[email protected]>
Signed-off-by: Siddharth Vadapalli <[email protected]>
---
drivers/net/ethernet/ti/am65-cpts.c | 59 ++++++++++++++++++++++++-----
1 file changed, 49 insertions(+), 10 deletions(-)
diff --git a/drivers/net/ethernet/ti/am65-cpts.c b/drivers/net/ethernet/ti/am65-cpts.c
index c405af266b41..bf0f74b20ba6 100644
--- a/drivers/net/ethernet/ti/am65-cpts.c
+++ b/drivers/net/ethernet/ti/am65-cpts.c
@@ -405,10 +405,13 @@ static irqreturn_t am65_cpts_interrupt(int irq, void *dev_id)
static int am65_cpts_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm)
{
struct am65_cpts *cpts = container_of(ptp, struct am65_cpts, ptp_info);
+ u32 pps_ctrl_val = 0, pps_ppm_hi = 0, pps_ppm_low = 0;
s32 ppb = scaled_ppm_to_ppb(scaled_ppm);
+ int pps_index = cpts->pps_genf_idx;
+ u64 adj_period, pps_adj_period;
+ u32 ctrl_val, ppm_hi, ppm_low;
+ unsigned long flags;
int neg_adj = 0;
- u64 adj_period;
- u32 val;
if (ppb < 0) {
neg_adj = 1;
@@ -428,17 +431,53 @@ static int am65_cpts_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm)
mutex_lock(&cpts->ptp_clk_lock);
- val = am65_cpts_read32(cpts, control);
+ ctrl_val = am65_cpts_read32(cpts, control);
if (neg_adj)
- val |= AM65_CPTS_CONTROL_TS_PPM_DIR;
+ ctrl_val |= AM65_CPTS_CONTROL_TS_PPM_DIR;
else
- val &= ~AM65_CPTS_CONTROL_TS_PPM_DIR;
- am65_cpts_write32(cpts, val, control);
+ ctrl_val &= ~AM65_CPTS_CONTROL_TS_PPM_DIR;
+
+ ppm_hi = upper_32_bits(adj_period) & 0x3FF;
+ ppm_low = lower_32_bits(adj_period);
+
+ if (cpts->pps_enabled) {
+ pps_ctrl_val = am65_cpts_read32(cpts, genf[pps_index].control);
+ if (neg_adj)
+ pps_ctrl_val &= ~BIT(1);
+ else
+ pps_ctrl_val |= BIT(1);
+
+ /* GenF PPM will do correction using cpts refclk tick which is
+ * (cpts->ts_add_val + 1) ns, so GenF length PPM adj period
+ * need to be corrected.
+ */
+ pps_adj_period = adj_period * (cpts->ts_add_val + 1);
+ pps_ppm_hi = upper_32_bits(pps_adj_period) & 0x3FF;
+ pps_ppm_low = lower_32_bits(pps_adj_period);
+ }
+
+ spin_lock_irqsave(&cpts->lock, flags);
- val = upper_32_bits(adj_period) & 0x3FF;
- am65_cpts_write32(cpts, val, ts_ppm_hi);
- val = lower_32_bits(adj_period);
- am65_cpts_write32(cpts, val, ts_ppm_low);
+ /* All below writes must be done extremely fast:
+ * - delay between PPM dir and PPM value changes can cause err due old
+ * PPM correction applied in wrong direction
+ * - delay between CPTS-clock PPM cfg and GenF PPM cfg can cause err
+ * due CPTS-clock PPM working with new cfg while GenF PPM cfg still
+ * with old for short period of time
+ */
+
+ am65_cpts_write32(cpts, ctrl_val, control);
+ am65_cpts_write32(cpts, ppm_hi, ts_ppm_hi);
+ am65_cpts_write32(cpts, ppm_low, ts_ppm_low);
+
+ if (cpts->pps_enabled) {
+ am65_cpts_write32(cpts, pps_ctrl_val, genf[pps_index].control);
+ am65_cpts_write32(cpts, pps_ppm_hi, genf[pps_index].ppm_hi);
+ am65_cpts_write32(cpts, pps_ppm_low, genf[pps_index].ppm_low);
+ }
+
+ /* All GenF/EstF can be updated here the same way */
+ spin_unlock_irqrestore(&cpts->lock, flags);
mutex_unlock(&cpts->ptp_clk_lock);
--
2.25.1
On 16/01/2023 10:55, Siddharth Vadapalli wrote:
> From: Grygorii Strashko <[email protected]>
>
> CPTS doesn't have HW support for PPS ("pulse per second”) signal
> generation, but it can be modeled by using Time Sync Router and routing
> GenFx (periodic signal generator) output to CPTS_HWy_TS_PUSH (hardware time
> stamp) input, and configuring GenFx to generate 1sec pulses.
>
> +------------------------+
> | CPTS |
> | |
> +--->CPTS_HW4_PUSH GENFx+---+
> | | | |
> | +------------------------+ |
> | |
> +--------------------------------+
>
> Add corresponding support to am65-cpts driver. The DT property "ti,pps"
> has to be used to enable PPS support and configure pair
> [CPTS_HWy_TS_PUSH, GenFx].
>
> Once enabled, PPS can be tested using ppstest tool:
> # ./ppstest /dev/pps0
>
> Signed-off-by: Grygorii Strashko <[email protected]>
> Signed-off-by: Siddharth Vadapalli <[email protected]>
Reviewed-by: Roger Quadros <[email protected]>
On 16/01/2023 10:55, Siddharth Vadapalli wrote:
> From: Grygorii Strashko <[email protected]>
>
> When CPTS clock is sync/adjusted by running linuxptp (ptp4l) it will cause
> PPS jitter as Genf running PPS is not adjusted.
>
> The same PPM adjustment has to be applied to GenF as to PHC clock to
> correct PPS length and keep them in sync.
>
> Testing:
> Master:
> ptp4l -P -2 -H -i eth0 -l 6 -m -q -p /dev/ptp1 -f ptp.cfg &
> testptp -d /dev/ptp1 -P 1
> ppstest /dev/pps0
>
> Slave:
> linuxptp/ptp4l -P -2 -H -i eth0 -l 6 -m -q -p /dev/ptp1 -f ptp1.cfg -s &
> <port 1: UNCALIBRATED to SLAVE on MASTER_CLOCK_SELECTED;>
> testptp -d /dev/ptp1 -P 1
> ppstest /dev/pps0
>
> Master log:
> source 0 - assert 620.000000689, sequence: 530
> source 0 - assert 621.000000689, sequence: 531
> source 0 - assert 622.000000689, sequence: 532
> source 0 - assert 623.000000689, sequence: 533
> source 0 - assert 624.000000689, sequence: 534
> source 0 - assert 625.000000689, sequence: 535
> source 0 - assert 626.000000689, sequence: 536
> source 0 - assert 627.000000689, sequence: 537
> source 0 - assert 628.000000689, sequence: 538
> source 0 - assert 629.000000689, sequence: 539
> source 0 - assert 630.000000689, sequence: 540
> source 0 - assert 631.000000689, sequence: 541
> source 0 - assert 632.000000689, sequence: 542
> source 0 - assert 633.000000689, sequence: 543
> source 0 - assert 634.000000689, sequence: 544
> source 0 - assert 635.000000689, sequence: 545
>
> Slave log:
> source 0 - assert 620.000000706, sequence: 252
> source 0 - assert 621.000000709, sequence: 253
> source 0 - assert 622.000000707, sequence: 254
> source 0 - assert 623.000000707, sequence: 255
> source 0 - assert 624.000000706, sequence: 256
> source 0 - assert 625.000000705, sequence: 257
> source 0 - assert 626.000000709, sequence: 258
> source 0 - assert 627.000000709, sequence: 259
> source 0 - assert 628.000000707, sequence: 260
> source 0 - assert 629.000000706, sequence: 261
> source 0 - assert 630.000000710, sequence: 262
> source 0 - assert 631.000000708, sequence: 263
> source 0 - assert 632.000000705, sequence: 264
> source 0 - assert 633.000000710, sequence: 265
> source 0 - assert 634.000000708, sequence: 266
> source 0 - assert 635.000000707, sequence: 267
>
> Signed-off-by: Grygorii Strashko <[email protected]>
> Signed-off-by: Siddharth Vadapalli <[email protected]>
> ---
> drivers/net/ethernet/ti/am65-cpts.c | 59 ++++++++++++++++++++++++-----
> 1 file changed, 49 insertions(+), 10 deletions(-)
Reviewed-by: Roger Quadros <[email protected]>
Hello:
This series was applied to netdev/net-next.git (master)
by David S. Miller <[email protected]>:
On Mon, 16 Jan 2023 14:25:31 +0530 you wrote:
> The CPTS hardware doesn't support PPS signal generation. Using the GenFx
> (periodic signal generator) function, it is possible to model a PPS signal
> followed by routing it via the time sync router to the CPTS_HWy_TS_PUSH
> (hardware time stamp) input, in order to generate timestamps at 1 second
> intervals.
>
> This series adds driver support for enabling PPS signal generation.
> Additionally, the documentation for the am65-cpts driver is updated with
> the bindings for the "ti,pps" property, which is used to inform the
> pair [CPTS_HWy_TS_PUSH, GenFx] to the cpts driver.
>
> [...]
Here is the summary with links:
- [net-next,v2,1/3] dt-binding: net: ti: am65x-cpts: add 'ti,pps' property
https://git.kernel.org/netdev/net-next/c/2b76af68d8e5
- [net-next,v2,2/3] net: ethernet: ti: am65-cpts: add pps support
https://git.kernel.org/netdev/net-next/c/b6d787123427
- [net-next,v2,3/3] net: ethernet: ti: am65-cpts: adjust pps following ptp changes
https://git.kernel.org/netdev/net-next/c/eb9233ce6751
You are awesome, thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html