From nobody Tue Oct 7 09:56:36 2025 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 3747227932F for ; Thu, 10 Jul 2025 15:39:02 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1752161946; cv=none; b=H+hhHUvYgot+9EQpfg5LWKRp+RBhg9QH1Vt3ltVZ+72lTXnLvWb7ucKRGExBbKDhGixBoQ5Ymz46dJMPAX03ehzsVzio3bpSFn7lXpODO7K+0hnbx9cRL9+sPFx48EQ3v9iVvNSj4Lkce+xD2n8brI5B9l+ojw4suyGmkWbVpd0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1752161946; c=relaxed/simple; bh=R8PNX7wzoL4iG8GiNk4i7zD8+Q8LLiaHnRhSPL+iCOo=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=AYDDtfXITJQwulU9nqRdiokbo4+INdPMEtKlaYXPf0HNN7iTxWRafLb3Wa5aKExQsRNVjBHYoe7w7benEGtlj6sFGZNNW4kyZD/O04Jf+k2E6daMDBmoWkwCQQCwc5Bpm9HBmb6L9/Z1N9BmLPZw3y3LAB+Hr+vbUVXnw9m9VSk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com; spf=pass smtp.mailfrom=redhat.com; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b=NVAOTAxh; arc=none smtp.client-ip=170.10.133.124 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=redhat.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="NVAOTAxh" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1752161942; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=duTZQ9YhBCO7IBFPzBw36ZRjd8wFlVFZtkxUZFIs9zE=; b=NVAOTAxhssxrku/sIeYM9WUPW2fLs0/WVNlactp7hOtkDkO6y6AFiNGqPUiCXuUuOYBmAF e3Hr/hnLIL2Myu71z9vz2cPo7TlFvyqRvYUfqMCkGA1F4q+CtulFlSWrr9hejeH66mdqYT NF05aswXkwPEyW3AMh1gKkPNmRvzDYM= Received: from mx-prod-mc-02.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-370-LzoN7qQPNUGkV3CuG8VlsA-1; Thu, 10 Jul 2025 11:38:58 -0400 X-MC-Unique: LzoN7qQPNUGkV3CuG8VlsA-1 X-Mimecast-MFC-AGG-ID: LzoN7qQPNUGkV3CuG8VlsA_1752161937 Received: from mx-prod-int-02.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-02.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.15]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-02.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 540D61956066; Thu, 10 Jul 2025 15:38:57 +0000 (UTC) Received: from p16v.luc.cera.cz (unknown [10.44.33.211]) by mx-prod-int-02.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id EFB901956094; Thu, 10 Jul 2025 15:38:53 +0000 (UTC) From: Ivan Vecera To: netdev@vger.kernel.org Cc: Prathosh Satish , Arkadiusz Kubalewski , Jiri Pirko , "David S. Miller" , Jakub Kicinski , Paolo Abeni , linux-kernel@vger.kernel.org, Michal Schmidt , Petr Oros Subject: [PATCH net-next 1/5] dpll: zl3073x: Add support to get/set esync on pins Date: Thu, 10 Jul 2025 17:38:44 +0200 Message-ID: <20250710153848.928531-2-ivecera@redhat.com> In-Reply-To: <20250710153848.928531-1-ivecera@redhat.com> References: <20250710153848.928531-1-ivecera@redhat.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 3.0 on 10.30.177.15 Content-Type: text/plain; charset="utf-8" Add support to get/set embedded sync for both input and output pins. The DPLL is able to lock on input reference when the embedded sync frequency is 1 PPS and pulse width 25%. The esync on outputs are more versatille and theoretically supports any esync frequency that divides current output frequency but for now support the same that supported on input pins (1 PPS & 25% pulse). Note that for the output pins the esync divisor shares the same register used for N-divided signal formats. Due to this the esync cannot be enabled on outputs configured with one of the N-divided signal formats. Co-developed-by: Prathosh Satish Signed-off-by: Prathosh Satish Signed-off-by: Ivan Vecera Reviewed-by: Jiri Pirko Tested-by: Prathosh Satish --- drivers/dpll/zl3073x/dpll.c | 343 +++++++++++++++++++++++++++++++++++- drivers/dpll/zl3073x/regs.h | 11 ++ 2 files changed, 353 insertions(+), 1 deletion(-) diff --git a/drivers/dpll/zl3073x/dpll.c b/drivers/dpll/zl3073x/dpll.c index cb0f1a43c5fbd..2af20532b1ca0 100644 --- a/drivers/dpll/zl3073x/dpll.c +++ b/drivers/dpll/zl3073x/dpll.c @@ -34,6 +34,7 @@ * @id: pin id * @prio: pin priority <0, 14> * @selectable: pin is selectable in automatic mode + * @esync_control: embedded sync is controllable * @pin_state: last saved pin state */ struct zl3073x_dpll_pin { @@ -45,9 +46,17 @@ struct zl3073x_dpll_pin { u8 id; u8 prio; bool selectable; + bool esync_control; enum dpll_pin_state pin_state; }; =20 +/* + * Supported esync ranges for input and for output per output pair type + */ +static const struct dpll_pin_frequency esync_freq_ranges[] =3D { + DPLL_PIN_FREQUENCY_RANGE(0, 1), +}; + /** * zl3073x_dpll_is_input_pin - check if the pin is input one * @pin: pin to check @@ -139,6 +148,126 @@ zl3073x_dpll_input_ref_frequency_get(struct zl3073x_d= pll *zldpll, u8 ref_id, return rc; } =20 +static int +zl3073x_dpll_input_pin_esync_get(const struct dpll_pin *dpll_pin, + void *pin_priv, + const struct dpll_device *dpll, + void *dpll_priv, + struct dpll_pin_esync *esync, + struct netlink_ext_ack *extack) +{ + struct zl3073x_dpll *zldpll =3D dpll_priv; + struct zl3073x_dev *zldev =3D zldpll->dev; + struct zl3073x_dpll_pin *pin =3D pin_priv; + u8 ref, ref_sync_ctrl, sync_mode; + u32 esync_div, ref_freq; + int rc; + + /* Get reference frequency */ + ref =3D zl3073x_input_pin_ref_get(pin->id); + rc =3D zl3073x_dpll_input_ref_frequency_get(zldpll, pin->id, &ref_freq); + if (rc) + return rc; + + guard(mutex)(&zldev->multiop_lock); + + /* Read reference configuration into mailbox */ + rc =3D zl3073x_mb_op(zldev, ZL_REG_REF_MB_SEM, ZL_REF_MB_SEM_RD, + ZL_REG_REF_MB_MASK, BIT(ref)); + if (rc) + return rc; + + /* Get ref sync mode */ + rc =3D zl3073x_read_u8(zldev, ZL_REG_REF_SYNC_CTRL, &ref_sync_ctrl); + if (rc) + return rc; + + /* Get esync divisor */ + rc =3D zl3073x_read_u32(zldev, ZL_REG_REF_ESYNC_DIV, &esync_div); + if (rc) + return rc; + + sync_mode =3D FIELD_GET(ZL_REF_SYNC_CTRL_MODE, ref_sync_ctrl); + + switch (sync_mode) { + case ZL_REF_SYNC_CTRL_MODE_50_50_ESYNC_25_75: + esync->freq =3D (esync_div =3D=3D ZL_REF_ESYNC_DIV_1HZ) ? 1 : 0; + esync->pulse =3D 25; + break; + default: + esync->freq =3D 0; + esync->pulse =3D 0; + break; + } + + /* If the pin supports esync control expose its range but only + * if the current reference frequency is > 1 Hz. + */ + if (pin->esync_control && ref_freq > 1) { + esync->range =3D esync_freq_ranges; + esync->range_num =3D ARRAY_SIZE(esync_freq_ranges); + } else { + esync->range =3D NULL; + esync->range_num =3D 0; + } + + return rc; +} + +static int +zl3073x_dpll_input_pin_esync_set(const struct dpll_pin *dpll_pin, + void *pin_priv, + const struct dpll_device *dpll, + void *dpll_priv, u64 freq, + struct netlink_ext_ack *extack) +{ + struct zl3073x_dpll *zldpll =3D dpll_priv; + struct zl3073x_dev *zldev =3D zldpll->dev; + struct zl3073x_dpll_pin *pin =3D pin_priv; + u8 ref, ref_sync_ctrl, sync_mode; + int rc; + + guard(mutex)(&zldev->multiop_lock); + + /* Read reference configuration into mailbox */ + ref =3D zl3073x_input_pin_ref_get(pin->id); + rc =3D zl3073x_mb_op(zldev, ZL_REG_REF_MB_SEM, ZL_REF_MB_SEM_RD, + ZL_REG_REF_MB_MASK, BIT(ref)); + if (rc) + return rc; + + /* Get ref sync mode */ + rc =3D zl3073x_read_u8(zldev, ZL_REG_REF_SYNC_CTRL, &ref_sync_ctrl); + if (rc) + return rc; + + /* Use freq =3D=3D 0 to disable esync */ + if (!freq) + sync_mode =3D ZL_REF_SYNC_CTRL_MODE_REFSYNC_PAIR_OFF; + else + sync_mode =3D ZL_REF_SYNC_CTRL_MODE_50_50_ESYNC_25_75; + + ref_sync_ctrl &=3D ~ZL_REF_SYNC_CTRL_MODE; + ref_sync_ctrl |=3D FIELD_PREP(ZL_REF_SYNC_CTRL_MODE, sync_mode); + + /* Update ref sync control register */ + rc =3D zl3073x_write_u8(zldev, ZL_REG_REF_SYNC_CTRL, ref_sync_ctrl); + if (rc) + return rc; + + if (freq) { + /* 1 Hz is only supported frequnecy currently */ + rc =3D zl3073x_write_u32(zldev, ZL_REG_REF_ESYNC_DIV, + ZL_REF_ESYNC_DIV_1HZ); + if (rc) + return rc; + } + + /* Commit reference configuration */ + return zl3073x_mb_op(zldev, ZL_REG_REF_MB_SEM, ZL_REF_MB_SEM_WR, + ZL_REG_REF_MB_MASK, BIT(ref)); +} + static int zl3073x_dpll_input_pin_frequency_get(const struct dpll_pin *dpll_pin, void *pin_priv, @@ -640,6 +769,213 @@ zl3073x_dpll_input_pin_prio_set(const struct dpll_pin= *dpll_pin, void *pin_priv, return 0; } =20 +static int +zl3073x_dpll_output_pin_esync_get(const struct dpll_pin *dpll_pin, + void *pin_priv, + const struct dpll_device *dpll, + void *dpll_priv, + struct dpll_pin_esync *esync, + struct netlink_ext_ack *extack) +{ + struct zl3073x_dpll *zldpll =3D dpll_priv; + struct zl3073x_dev *zldev =3D zldpll->dev; + struct zl3073x_dpll_pin *pin =3D pin_priv; + struct device *dev =3D zldev->dev; + u32 esync_period, esync_width; + u8 clock_type, synth; + u8 out, output_mode; + u32 output_div; + u32 synth_freq; + int rc; + + out =3D zl3073x_output_pin_out_get(pin->id); + + /* If N-division is enabled, esync is not supported. The register used + * for N-division is also used for the esync divider so both cannot + * be used. + */ + switch (zl3073x_out_signal_format_get(zldev, out)) { + case ZL_OUTPUT_MODE_SIGNAL_FORMAT_2_NDIV: + case ZL_OUTPUT_MODE_SIGNAL_FORMAT_2_NDIV_INV: + return -EOPNOTSUPP; + default: + break; + } + + guard(mutex)(&zldev->multiop_lock); + + /* Read output configuration into mailbox */ + rc =3D zl3073x_mb_op(zldev, ZL_REG_OUTPUT_MB_SEM, ZL_OUTPUT_MB_SEM_RD, + ZL_REG_OUTPUT_MB_MASK, BIT(out)); + if (rc) + return rc; + + /* Read output mode */ + rc =3D zl3073x_read_u8(zldev, ZL_REG_OUTPUT_MODE, &output_mode); + if (rc) + return rc; + + /* Read output divisor */ + rc =3D zl3073x_read_u32(zldev, ZL_REG_OUTPUT_DIV, &output_div); + if (rc) + return rc; + + /* Check output divisor for zero */ + if (!output_div) { + dev_err(dev, "Zero divisor for OUTPUT%u got from device\n", + out); + return -EINVAL; + } + + /* Get synth attached to output pin */ + synth =3D zl3073x_out_synth_get(zldev, out); + + /* Get synth frequency */ + synth_freq =3D zl3073x_synth_freq_get(zldev, synth); + + clock_type =3D FIELD_GET(ZL_OUTPUT_MODE_CLOCK_TYPE, output_mode); + if (clock_type !=3D ZL_OUTPUT_MODE_CLOCK_TYPE_ESYNC) { + /* No need to read esync data if it is not enabled */ + esync->freq =3D 0; + esync->pulse =3D 0; + + goto finish; + } + + /* Read esync period */ + rc =3D zl3073x_read_u32(zldev, ZL_REG_OUTPUT_ESYNC_PERIOD, &esync_period); + if (rc) + return rc; + + /* Check esync divisor for zero */ + if (!esync_period) { + dev_err(dev, "Zero esync divisor for OUTPUT%u got from device\n", + out); + return -EINVAL; + } + + /* Get esync pulse width in units of half synth cycles */ + rc =3D zl3073x_read_u32(zldev, ZL_REG_OUTPUT_ESYNC_WIDTH, &esync_width); + if (rc) + return rc; + + /* Compute esync frequency */ + esync->freq =3D synth_freq / output_div / esync_period; + + /* By comparing the esync_pulse_width to the half of the pulse width + * the esync pulse percentage can be determined. + * Note that half pulse width is in units of half synth cycles, which + * is why it reduces down to be output_div. + */ + esync->pulse =3D (50 * esync_width) / output_div; + +finish: + /* Set supported esync ranges if the pin supports esync control and + * if the output frequency is > 1 Hz. + */ + if (pin->esync_control && (synth_freq / output_div) > 1) { + esync->range =3D esync_freq_ranges; + esync->range_num =3D ARRAY_SIZE(esync_freq_ranges); + } else { + esync->range =3D NULL; + esync->range_num =3D 0; + } + + return 0; +} + +static int +zl3073x_dpll_output_pin_esync_set(const struct dpll_pin *dpll_pin, + void *pin_priv, + const struct dpll_device *dpll, + void *dpll_priv, u64 freq, + struct netlink_ext_ack *extack) +{ + struct zl3073x_dpll *zldpll =3D dpll_priv; + struct zl3073x_dev *zldev =3D zldpll->dev; + struct zl3073x_dpll_pin *pin =3D pin_priv; + u32 esync_period, esync_width, output_div; + u8 clock_type, out, output_mode, synth; + u32 synth_freq; + int rc; + + out =3D zl3073x_output_pin_out_get(pin->id); + + /* If N-division is enabled, esync is not supported. The register used + * for N-division is also used for the esync divider so both cannot + * be used. + */ + switch (zl3073x_out_signal_format_get(zldev, out)) { + case ZL_OUTPUT_MODE_SIGNAL_FORMAT_2_NDIV: + case ZL_OUTPUT_MODE_SIGNAL_FORMAT_2_NDIV_INV: + return -EOPNOTSUPP; + default: + break; + } + + guard(mutex)(&zldev->multiop_lock); + + /* Read output configuration into mailbox */ + rc =3D zl3073x_mb_op(zldev, ZL_REG_OUTPUT_MB_SEM, ZL_OUTPUT_MB_SEM_RD, + ZL_REG_OUTPUT_MB_MASK, BIT(out)); + if (rc) + return rc; + + /* Read output mode */ + rc =3D zl3073x_read_u8(zldev, ZL_REG_OUTPUT_MODE, &output_mode); + if (rc) + return rc; + + /* Select clock type */ + if (freq) + clock_type =3D ZL_OUTPUT_MODE_CLOCK_TYPE_ESYNC; + else + clock_type =3D ZL_OUTPUT_MODE_CLOCK_TYPE_NORMAL; + + /* Update clock type in output mode */ + output_mode &=3D ~ZL_OUTPUT_MODE_CLOCK_TYPE; + output_mode |=3D FIELD_PREP(ZL_OUTPUT_MODE_CLOCK_TYPE, clock_type); + rc =3D zl3073x_write_u8(zldev, ZL_REG_OUTPUT_MODE, output_mode); + if (rc) + return rc; + + /* If esync is being disabled just write mailbox and finish */ + if (!freq) + goto write_mailbox; + + /* Get synth attached to output pin */ + synth =3D zl3073x_out_synth_get(zldev, out); + + /* Get synth frequency */ + synth_freq =3D zl3073x_synth_freq_get(zldev, synth); + + rc =3D zl3073x_read_u32(zldev, ZL_REG_OUTPUT_DIV, &output_div); + if (rc) + return rc; + + /* Compute and update esync period */ + esync_period =3D synth_freq / (u32)freq / output_div; + rc =3D zl3073x_write_u32(zldev, ZL_REG_OUTPUT_ESYNC_PERIOD, esync_period); + if (rc) + return rc; + + /* Half of the period in units of 1/2 synth cycle can be represented by + * the output_div. To get the supported esync pulse width of 25% of the + * period the output_div can just be divided by 2. Note that this + * assumes that output_div is even, otherwise some resolution will be + * lost. + */ + esync_width =3D output_div / 2; + rc =3D zl3073x_write_u32(zldev, ZL_REG_OUTPUT_ESYNC_WIDTH, esync_width); + if (rc) + return rc; + +write_mailbox: + /* Commit output configuration */ + return zl3073x_mb_op(zldev, ZL_REG_OUTPUT_MB_SEM, ZL_OUTPUT_MB_SEM_WR, + ZL_REG_OUTPUT_MB_MASK, BIT(out)); +} + static int zl3073x_dpll_output_pin_frequency_get(const struct dpll_pin *dpll_pin, void *pin_priv, @@ -956,6 +1292,8 @@ zl3073x_dpll_mode_get(const struct dpll_device *dpll, = void *dpll_priv, =20 static const struct dpll_pin_ops zl3073x_dpll_input_pin_ops =3D { .direction_get =3D zl3073x_dpll_pin_direction_get, + .esync_get =3D zl3073x_dpll_input_pin_esync_get, + .esync_set =3D zl3073x_dpll_input_pin_esync_set, .frequency_get =3D zl3073x_dpll_input_pin_frequency_get, .frequency_set =3D zl3073x_dpll_input_pin_frequency_set, .prio_get =3D zl3073x_dpll_input_pin_prio_get, @@ -966,6 +1304,8 @@ static const struct dpll_pin_ops zl3073x_dpll_input_pi= n_ops =3D { =20 static const struct dpll_pin_ops zl3073x_dpll_output_pin_ops =3D { .direction_get =3D zl3073x_dpll_pin_direction_get, + .esync_get =3D zl3073x_dpll_output_pin_esync_get, + .esync_set =3D zl3073x_dpll_output_pin_esync_set, .frequency_get =3D zl3073x_dpll_output_pin_frequency_get, .frequency_set =3D zl3073x_dpll_output_pin_frequency_set, .state_on_dpll_get =3D zl3073x_dpll_output_pin_state_on_dpll_get, @@ -1040,8 +1380,9 @@ zl3073x_dpll_pin_register(struct zl3073x_dpll_pin *pi= n, u32 index) if (IS_ERR(props)) return PTR_ERR(props); =20 - /* Save package label */ + /* Save package label & esync capability */ strscpy(pin->label, props->package_label); + pin->esync_control =3D props->esync_control; =20 if (zl3073x_dpll_is_input_pin(pin)) { rc =3D zl3073x_dpll_ref_prio_get(pin, &pin->prio); diff --git a/drivers/dpll/zl3073x/regs.h b/drivers/dpll/zl3073x/regs.h index 493c63e729208..64bb43bbc3168 100644 --- a/drivers/dpll/zl3073x/regs.h +++ b/drivers/dpll/zl3073x/regs.h @@ -146,6 +146,14 @@ #define ZL_REF_CONFIG_ENABLE BIT(0) #define ZL_REF_CONFIG_DIFF_EN BIT(2) =20 +#define ZL_REG_REF_SYNC_CTRL ZL_REG(10, 0x2e, 1) +#define ZL_REF_SYNC_CTRL_MODE GENMASK(2, 0) +#define ZL_REF_SYNC_CTRL_MODE_REFSYNC_PAIR_OFF 0 +#define ZL_REF_SYNC_CTRL_MODE_50_50_ESYNC_25_75 2 + +#define ZL_REG_REF_ESYNC_DIV ZL_REG(10, 0x30, 4) +#define ZL_REF_ESYNC_DIV_1HZ 0 + /******************************** * Register Page 12, DPLL Mailbox ********************************/ @@ -188,6 +196,9 @@ #define ZL_OUTPUT_MB_SEM_RD BIT(1) =20 #define ZL_REG_OUTPUT_MODE ZL_REG(14, 0x05, 1) +#define ZL_OUTPUT_MODE_CLOCK_TYPE GENMASK(2, 0) +#define ZL_OUTPUT_MODE_CLOCK_TYPE_NORMAL 0 +#define ZL_OUTPUT_MODE_CLOCK_TYPE_ESYNC 1 #define ZL_OUTPUT_MODE_SIGNAL_FORMAT GENMASK(7, 4) #define ZL_OUTPUT_MODE_SIGNAL_FORMAT_DISABLED 0 #define ZL_OUTPUT_MODE_SIGNAL_FORMAT_LVDS 1 --=20 2.49.0 From nobody Tue Oct 7 09:56:36 2025 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A927C2798FA for ; Thu, 10 Jul 2025 15:39:08 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1752161950; cv=none; b=O3u1CIFoTo8X4BunPvQZVgCnIsbUs4j6fGsjk6LQRP6tuQke8FNGJcsMBO6TgX59xi1mvD2XY1zQY3RT4aoQV5qA7wJl72GOeSUd1FCyZOLhEJzWaoDw4NryEMyMVGr6ManZQ7aDx+FH2XPV4IAVEfPQh71Cqv74+nqRvg9NSYE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1752161950; c=relaxed/simple; bh=fDTCLEcoLTEYLrqhScu3MiK/4bZ/utt039W2mQR94LY=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=TGmS4XcCStnTgqnQkGo+rcQO1dDSRLPEETdblricGmibXM5SAJT0inBriUEvoxTLzC+1R5ZgzksoRSnOsSRQBS4OAZOCaCu2Fp54KqLH2Rl/CSyK8ewJqWLZ54z6ZtYhyD3I8ZmcFJh92cUJyO6IagtTen5QJdZf9rFf1zKK6Os= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com; spf=pass smtp.mailfrom=redhat.com; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b=gfgawlGz; arc=none smtp.client-ip=170.10.133.124 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=redhat.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="gfgawlGz" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1752161947; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=tewXCl7aPES6lAsQMFoyqNd1rcWypSmm5JQyoOpONic=; b=gfgawlGzrkVTZS7b9sprIr5OGAQvR4nGvj4S88qSKFHVg9YIT8pH6TNc3CEMKSThqRG/uN +echk/IHrCNlJVmQEJeoNTtgIw5mTUtP25VDbyS+HYUO7CvNEC8UIVhbHUn1uU4YcXxX8U ZHlGb9ZDGUwCZ0G9aM6Bd+BgBkgycOw= Received: from mx-prod-mc-04.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-562-trqHGjyMNTi7TanHC5wPYQ-1; Thu, 10 Jul 2025 11:39:03 -0400 X-MC-Unique: trqHGjyMNTi7TanHC5wPYQ-1 X-Mimecast-MFC-AGG-ID: trqHGjyMNTi7TanHC5wPYQ_1752161941 Received: from mx-prod-int-02.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-02.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.15]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-04.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 0CE9719560B0; Thu, 10 Jul 2025 15:39:01 +0000 (UTC) Received: from p16v.luc.cera.cz (unknown [10.44.33.211]) by mx-prod-int-02.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id C57B71956094; Thu, 10 Jul 2025 15:38:57 +0000 (UTC) From: Ivan Vecera To: netdev@vger.kernel.org Cc: Prathosh Satish , Arkadiusz Kubalewski , Jiri Pirko , "David S. Miller" , Jakub Kicinski , Paolo Abeni , linux-kernel@vger.kernel.org, Michal Schmidt , Petr Oros Subject: [PATCH net-next 2/5] dpll: zl3073x: Add support to get phase offset on connected input pin Date: Thu, 10 Jul 2025 17:38:45 +0200 Message-ID: <20250710153848.928531-3-ivecera@redhat.com> In-Reply-To: <20250710153848.928531-1-ivecera@redhat.com> References: <20250710153848.928531-1-ivecera@redhat.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 3.0 on 10.30.177.15 Content-Type: text/plain; charset="utf-8" Add support to get phase offset for the connected input pin. Implement the appropriate callback and function that performs DPLL to connected reference phase error measurement and notifies DPLL core about changes. The measurement is performed internally by device on background 40 times per second but the measured value is read each second and compared with previous value. Co-developed-by: Prathosh Satish Signed-off-by: Prathosh Satish Signed-off-by: Ivan Vecera Reviewed-by: Jiri Pirko Tested-by: Prathosh Satish --- drivers/dpll/zl3073x/core.c | 86 +++++++++++++++++++++++++++++ drivers/dpll/zl3073x/dpll.c | 105 +++++++++++++++++++++++++++++++++++- drivers/dpll/zl3073x/dpll.h | 2 + drivers/dpll/zl3073x/regs.h | 16 ++++++ 4 files changed, 208 insertions(+), 1 deletion(-) diff --git a/drivers/dpll/zl3073x/core.c b/drivers/dpll/zl3073x/core.c index f2d58e1a56726..c980c85e7ac51 100644 --- a/drivers/dpll/zl3073x/core.c +++ b/drivers/dpll/zl3073x/core.c @@ -669,12 +669,52 @@ zl3073x_dev_state_fetch(struct zl3073x_dev *zldev) return rc; } =20 +/** + * zl3073x_ref_phase_offsets_update - update reference phase offsets + * @zldev: pointer to zl3073x_dev structure + * + * Ask device to update phase offsets latch registers with the latest + * measured values. + * + * Return: 0 on success, <0 on error + */ +static int +zl3073x_ref_phase_offsets_update(struct zl3073x_dev *zldev) +{ + int rc; + + /* Per datasheet we have to wait for 'dpll_ref_phase_err_rqst_rd' + * to be zero to ensure that the measured data are coherent. + */ + rc =3D zl3073x_poll_zero_u8(zldev, ZL_REG_REF_PHASE_ERR_READ_RQST, + ZL_REF_PHASE_ERR_READ_RQST_RD); + if (rc) + return rc; + + /* Request to update phase offsets measurement values */ + rc =3D zl3073x_write_u8(zldev, ZL_REG_REF_PHASE_ERR_READ_RQST, + ZL_REF_PHASE_ERR_READ_RQST_RD); + if (rc) + return rc; + + /* Wait for finish */ + return zl3073x_poll_zero_u8(zldev, ZL_REG_REF_PHASE_ERR_READ_RQST, + ZL_REF_PHASE_ERR_READ_RQST_RD); +} + static void zl3073x_dev_periodic_work(struct kthread_work *work) { struct zl3073x_dev *zldev =3D container_of(work, struct zl3073x_dev, work.work); struct zl3073x_dpll *zldpll; + int rc; + + /* Update DPLL-to-connected-ref phase offsets registers */ + rc =3D zl3073x_ref_phase_offsets_update(zldev); + if (rc) + dev_warn(zldev->dev, "Failed to update phase offsets: %pe\n", + ERR_PTR(rc)); =20 list_for_each_entry(zldpll, &zldev->dplls, list) zl3073x_dpll_changes_check(zldpll); @@ -767,6 +807,46 @@ zl3073x_devm_dpll_init(struct zl3073x_dev *zldev, u8 n= um_dplls) return rc; } =20 +/** + * zl3073x_dev_phase_meas_setup - setup phase offset measurement + * @zldev: pointer to zl3073x_dev structure + * @num_channels: number of DPLL channels + * + * Enable phase offset measurement block, set measurement averaging factor + * and enable DPLL-to-its-ref phase measurement for all DPLLs. + * + * Returns: 0 on success, <0 on error + */ +static int +zl3073x_dev_phase_meas_setup(struct zl3073x_dev *zldev, int num_channels) +{ + u8 dpll_meas_ctrl, mask; + int i, rc; + + /* Read DPLL phase measurement control register */ + rc =3D zl3073x_read_u8(zldev, ZL_REG_DPLL_MEAS_CTRL, &dpll_meas_ctrl); + if (rc) + return rc; + + /* Setup phase measurement averaging factor */ + dpll_meas_ctrl &=3D ~ZL_DPLL_MEAS_CTRL_AVG_FACTOR; + dpll_meas_ctrl |=3D FIELD_PREP(ZL_DPLL_MEAS_CTRL_AVG_FACTOR, 3); + + /* Enable DPLL measurement block */ + dpll_meas_ctrl |=3D ZL_DPLL_MEAS_CTRL_EN; + + /* Update phase measurement control register */ + rc =3D zl3073x_write_u8(zldev, ZL_REG_DPLL_MEAS_CTRL, dpll_meas_ctrl); + if (rc) + return rc; + + /* Enable DPLL-to-connected-ref measurement for each channel */ + for (i =3D 0, mask =3D 0; i < num_channels; i++) + mask |=3D BIT(i); + + return zl3073x_write_u8(zldev, ZL_REG_DPLL_PHASE_ERR_READ_MASK, mask); +} + /** * zl3073x_dev_probe - initialize zl3073x device * @zldev: pointer to zl3073x device @@ -839,6 +919,12 @@ int zl3073x_dev_probe(struct zl3073x_dev *zldev, if (rc) return rc; =20 + /* Setup phase offset measurement block */ + rc =3D zl3073x_dev_phase_meas_setup(zldev, chip_info->num_channels); + if (rc) + return dev_err_probe(zldev->dev, rc, + "Failed to setup phase measurement\n"); + /* Register DPLL channels */ rc =3D zl3073x_devm_dpll_init(zldev, chip_info->num_channels); if (rc) diff --git a/drivers/dpll/zl3073x/dpll.c b/drivers/dpll/zl3073x/dpll.c index 2af20532b1ca0..1a1d4de5b9de5 100644 --- a/drivers/dpll/zl3073x/dpll.c +++ b/drivers/dpll/zl3073x/dpll.c @@ -36,6 +36,7 @@ * @selectable: pin is selectable in automatic mode * @esync_control: embedded sync is controllable * @pin_state: last saved pin state + * @phase_offset: last saved pin phase offset */ struct zl3073x_dpll_pin { struct list_head list; @@ -48,6 +49,7 @@ struct zl3073x_dpll_pin { bool selectable; bool esync_control; enum dpll_pin_state pin_state; + s64 phase_offset; }; =20 /* @@ -496,6 +498,50 @@ zl3073x_dpll_connected_ref_get(struct zl3073x_dpll *zl= dpll, u8 *ref) return 0; } =20 +static int +zl3073x_dpll_input_pin_phase_offset_get(const struct dpll_pin *dpll_pin, + void *pin_priv, + const struct dpll_device *dpll, + void *dpll_priv, s64 *phase_offset, + struct netlink_ext_ack *extack) +{ + struct zl3073x_dpll *zldpll =3D dpll_priv; + struct zl3073x_dev *zldev =3D zldpll->dev; + struct zl3073x_dpll_pin *pin =3D pin_priv; + u8 conn_ref, ref, ref_status; + int rc; + + /* Get currently connected reference */ + rc =3D zl3073x_dpll_connected_ref_get(zldpll, &conn_ref); + if (rc) + return rc; + + /* Report phase offset only for currently connected pin */ + ref =3D zl3073x_input_pin_ref_get(pin->id); + if (ref !=3D conn_ref) { + *phase_offset =3D 0; + + return 0; + } + + /* Get this pin monitor status */ + rc =3D zl3073x_read_u8(zldev, ZL_REG_REF_MON_STATUS(ref), &ref_status); + if (rc) + return rc; + + /* Report phase offset only if the input pin signal is present */ + if (ref_status !=3D ZL_REF_MON_STATUS_OK) { + *phase_offset =3D 0; + + return 0; + } + + /* Report the latest measured phase offset for the connected ref */ + *phase_offset =3D pin->phase_offset * DPLL_PHASE_OFFSET_DIVIDER; + + return rc; +} + /** * zl3073x_dpll_ref_prio_get - get priority for given input pin * @pin: pointer to pin @@ -1296,6 +1342,7 @@ static const struct dpll_pin_ops zl3073x_dpll_input_p= in_ops =3D { .esync_set =3D zl3073x_dpll_input_pin_esync_set, .frequency_get =3D zl3073x_dpll_input_pin_frequency_get, .frequency_set =3D zl3073x_dpll_input_pin_frequency_set, + .phase_offset_get =3D zl3073x_dpll_input_pin_phase_offset_get, .prio_get =3D zl3073x_dpll_input_pin_prio_get, .prio_set =3D zl3073x_dpll_input_pin_prio_set, .state_on_dpll_get =3D zl3073x_dpll_input_pin_state_on_dpll_get, @@ -1666,6 +1713,51 @@ zl3073x_dpll_device_unregister(struct zl3073x_dpll *= zldpll) zldpll->dpll_dev =3D NULL; } =20 +/** + * zl3073x_dpll_pin_phase_offset_check - check for pin phase offset change + * @pin: pin to check + * + * Check for the change of DPLL to connected pin phase offset change. + * + * Return: true on phase offset change, false otherwise + */ +static bool +zl3073x_dpll_pin_phase_offset_check(struct zl3073x_dpll_pin *pin) +{ + struct zl3073x_dpll *zldpll =3D pin->dpll; + struct zl3073x_dev *zldev =3D zldpll->dev; + s64 phase_offset; + int rc; + + /* Do not check phase offset if the pin is not connected one */ + if (pin->pin_state !=3D DPLL_PIN_STATE_CONNECTED) + return false; + + /* Read DPLL-to-connected-ref phase offset measurement value */ + rc =3D zl3073x_read_u48(zldev, ZL_REG_DPLL_PHASE_ERR_DATA(zldpll->id), + &phase_offset); + if (rc) { + dev_err(zldev->dev, "Failed to read ref phase offset: %pe\n", + ERR_PTR(rc)); + + return false; + } + + /* Convert to ps */ + phase_offset =3D div_s64(sign_extend64(phase_offset, 47), 100); + + /* Compare with previous value */ + if (phase_offset !=3D pin->phase_offset) { + dev_dbg(zldev->dev, "%s phase offset changed: %lld -> %lld\n", + pin->label, pin->phase_offset, phase_offset); + pin->phase_offset =3D phase_offset; + + return true; + } + + return false; +} + /** * zl3073x_dpll_changes_check - check for changes and send notifications * @zldpll: pointer to zl3073x_dpll structure @@ -1684,6 +1776,8 @@ zl3073x_dpll_changes_check(struct zl3073x_dpll *zldpl= l) struct zl3073x_dpll_pin *pin; int rc; =20 + zldpll->check_count++; + /* Get current lock status for the DPLL */ rc =3D zl3073x_dpll_lock_status_get(zldpll->dpll_dev, zldpll, &lock_status, NULL, NULL); @@ -1708,6 +1802,7 @@ zl3073x_dpll_changes_check(struct zl3073x_dpll *zldpl= l) =20 list_for_each_entry(pin, &zldpll->pins, list) { enum dpll_pin_state state; + bool pin_changed =3D false; =20 /* Output pins change checks are not necessary because output * states are constant. @@ -1727,8 +1822,16 @@ zl3073x_dpll_changes_check(struct zl3073x_dpll *zldp= ll) dev_dbg(dev, "%s state changed: %u->%u\n", pin->label, pin->pin_state, state); pin->pin_state =3D state; - dpll_pin_change_ntf(pin->dpll_pin); + pin_changed =3D true; } + + /* Check for phase offset change once per second */ + if (zldpll->check_count % 2 =3D=3D 0) + if (zl3073x_dpll_pin_phase_offset_check(pin)) + pin_changed =3D true; + + if (pin_changed) + dpll_pin_change_ntf(pin->dpll_pin); } } =20 diff --git a/drivers/dpll/zl3073x/dpll.h b/drivers/dpll/zl3073x/dpll.h index db7388cc377fd..2e84e56f8c9e1 100644 --- a/drivers/dpll/zl3073x/dpll.h +++ b/drivers/dpll/zl3073x/dpll.h @@ -15,6 +15,7 @@ * @id: DPLL index * @refsel_mode: reference selection mode * @forced_ref: selected reference in forced reference lock mode + * @check_count: periodic check counter * @dpll_dev: pointer to registered DPLL device * @lock_status: last saved DPLL lock status * @pins: list of pins @@ -25,6 +26,7 @@ struct zl3073x_dpll { u8 id; u8 refsel_mode; u8 forced_ref; + u8 check_count; struct dpll_device *dpll_dev; enum dpll_lock_status lock_status; struct list_head pins; diff --git a/drivers/dpll/zl3073x/regs.h b/drivers/dpll/zl3073x/regs.h index 64bb43bbc3168..8dde92e623f76 100644 --- a/drivers/dpll/zl3073x/regs.h +++ b/drivers/dpll/zl3073x/regs.h @@ -94,6 +94,13 @@ #define ZL_DPLL_REFSEL_STATUS_STATE GENMASK(6, 4) #define ZL_DPLL_REFSEL_STATUS_STATE_LOCK 4 =20 +/********************** + * Register Page 4, Ref + **********************/ + +#define ZL_REG_REF_PHASE_ERR_READ_RQST ZL_REG(4, 0x0f, 1) +#define ZL_REF_PHASE_ERR_READ_RQST_RD BIT(0) + /*********************** * Register Page 5, DPLL ***********************/ @@ -108,6 +115,15 @@ #define ZL_DPLL_MODE_REFSEL_MODE_NCO 4 #define ZL_DPLL_MODE_REFSEL_REF GENMASK(7, 4) =20 +#define ZL_REG_DPLL_MEAS_CTRL ZL_REG(5, 0x50, 1) +#define ZL_DPLL_MEAS_CTRL_EN BIT(0) +#define ZL_DPLL_MEAS_CTRL_AVG_FACTOR GENMASK(7, 4) + +#define ZL_REG_DPLL_PHASE_ERR_READ_MASK ZL_REG(5, 0x54, 1) + +#define ZL_REG_DPLL_PHASE_ERR_DATA(_idx) \ + ZL_REG_IDX(_idx, 5, 0x55, 6, ZL3073X_MAX_CHANNELS, 6) + /*********************************** * Register Page 9, Synth and Output ***********************************/ --=20 2.49.0 From nobody Tue Oct 7 09:56:36 2025 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 02D47279DD5 for ; Thu, 10 Jul 2025 15:39:11 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1752161954; cv=none; b=B9rzY0cTjy2fPotUkv6TM2MQFW1lIxrOj81WvI7HwgqHPmsEnOlF27mTnm4Rq0TMwASj8BRJv66ViawEXAlgG6N+eOnIVqCSk0HDac1tzMH8y/73U5Sc42wV4EjWmGim3tOOdD1559eaXT6pO5neupIyd1DIUpsSQjvEwtDxwA8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1752161954; c=relaxed/simple; bh=RjX6KkNi1mxyVtnOf83UcvxyCpdtZuUkjVrquP0wcxQ=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=FUpokCbJLX0SrsXEYSPGTZWXq/OBNE4jcjr3WKzKdduOVbIsk8kWs4eM9NnNPnNYwEAytU8MOn3+R6QMwhmlzA4QxEjTYXzw2xNMyvi6kULX+GHaXyrvmF+A0AxOBn/QZdfw6S74m7ELiBX7RwwX8n8N//SPTYVKuQgS/Ea6SW8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com; spf=pass smtp.mailfrom=redhat.com; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b=KWT+5qTA; arc=none smtp.client-ip=170.10.133.124 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=redhat.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="KWT+5qTA" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1752161951; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=PTUQwm0DlUF8GSWxRoFkFG9EAX17NOGlsEvtiBOeWnM=; b=KWT+5qTApmRAaGkLH78fXlzkDygjhWjew6Z0/Hg0X3scttp42Fb3O2fnjZtQt5ZMh3MzZD NgGvInUZ/qqNmtXt9mYMBgb2be5pG2ijG9rGQyTpyXCcJrq16srJlAJWwndvfrdw1u1SH2 cxItwTVuy0tsNVPnlE6OYd94XUbdQ2s= Received: from mx-prod-mc-02.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-478-TcCBHRAAPvewXxy7Gk59Xg-1; Thu, 10 Jul 2025 11:39:07 -0400 X-MC-Unique: TcCBHRAAPvewXxy7Gk59Xg-1 X-Mimecast-MFC-AGG-ID: TcCBHRAAPvewXxy7Gk59Xg_1752161946 Received: from mx-prod-int-02.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-02.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.15]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-02.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 2C5791956086; Thu, 10 Jul 2025 15:39:06 +0000 (UTC) Received: from p16v.luc.cera.cz (unknown [10.44.33.211]) by mx-prod-int-02.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 7D3AD1956094; Thu, 10 Jul 2025 15:39:01 +0000 (UTC) From: Ivan Vecera To: netdev@vger.kernel.org Cc: Prathosh Satish , Arkadiusz Kubalewski , Jiri Pirko , "David S. Miller" , Jakub Kicinski , Paolo Abeni , linux-kernel@vger.kernel.org, Michal Schmidt , Petr Oros Subject: [PATCH net-next 3/5] dpll: zl3073x: Implement phase offset monitor feature Date: Thu, 10 Jul 2025 17:38:46 +0200 Message-ID: <20250710153848.928531-4-ivecera@redhat.com> In-Reply-To: <20250710153848.928531-1-ivecera@redhat.com> References: <20250710153848.928531-1-ivecera@redhat.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 3.0 on 10.30.177.15 Content-Type: text/plain; charset="utf-8" Implement phase offset monitor feature to allow a user to monitor phase offsets across all available inputs. The device firmware periodically performs phase offsets measurements for all available DPLL channels and input references. The driver can ask the firmware to fill appropriate latch registers with measured values. There are 2 sets of latch registers for phase offsets reporting: 1) DPLL-to-connected-ref: up to 5 registers that contain values for phase offset between particular DPLL channel and its connected input reference. 2) selected-DPLL-to-ref: 10 registers that contain values for phase offsets between selected DPLL channel and all available input references. Both are filled with single read request so the driver can read DPLL-to-connected-ref phase offset for all DPLL channels at once. This was implemented in the previous patch. To read selected-DPLL-to-ref registers for all DPLLs a separate read request has to be sent to device firmware for each DPLL channel. To implement phase offset monitor feature: * Extend zl3073x_ref_phase_offsets_update() to select given DPLL channel in phase offset read request. The caller can set channel=3D=3D-1 if it will not read Type2 registers. * Use this extended function to update phase offset latch registers during zl3073x_dpll_changes_check() call if phase monitor is enabled * Extend zl3073x_dpll_pin_phase_offset_check() to check phase offset changes for all available input references * Extend zl3073x_dpll_input_pin_phase_offset_get() to report phase offset values for all available input references * Implement phase offset monitor callbacks to enable/disable this feature Co-developed-by: Prathosh Satish Signed-off-by: Prathosh Satish Signed-off-by: Ivan Vecera Reviewed-by: Jiri Pirko Tested-by: Prathosh Satish --- drivers/dpll/zl3073x/core.c | 28 ++++++-- drivers/dpll/zl3073x/core.h | 1 + drivers/dpll/zl3073x/dpll.c | 126 +++++++++++++++++++++++++++++++++--- drivers/dpll/zl3073x/dpll.h | 2 + drivers/dpll/zl3073x/regs.h | 6 ++ 5 files changed, 149 insertions(+), 14 deletions(-) diff --git a/drivers/dpll/zl3073x/core.c b/drivers/dpll/zl3073x/core.c index c980c85e7ac51..eb62a492b1727 100644 --- a/drivers/dpll/zl3073x/core.c +++ b/drivers/dpll/zl3073x/core.c @@ -672,14 +672,25 @@ zl3073x_dev_state_fetch(struct zl3073x_dev *zldev) /** * zl3073x_ref_phase_offsets_update - update reference phase offsets * @zldev: pointer to zl3073x_dev structure + * @channel: DPLL channel number or -1 * - * Ask device to update phase offsets latch registers with the latest - * measured values. + * The function asks device to update phase offsets latch registers with + * the latest measured values. There are 2 sets of latch registers: + * + * 1) Up to 5 DPLL-to-connected-ref registers that contain phase offset + * values between particular DPLL channel and its *connected* input + * reference. + * + * 2) 10 selected-DPLL-to-all-ref registers that contain phase offset valu= es + * between selected DPLL channel and all input references. + * + * If the caller is interested in 2) then it has to pass DPLL channel numb= er + * in @channel parameter. If it is interested only in 1) then it should pa= ss + * @channel parameter with value of -1. * * Return: 0 on success, <0 on error */ -static int -zl3073x_ref_phase_offsets_update(struct zl3073x_dev *zldev) +int zl3073x_ref_phase_offsets_update(struct zl3073x_dev *zldev, int channe= l) { int rc; =20 @@ -691,6 +702,13 @@ zl3073x_ref_phase_offsets_update(struct zl3073x_dev *z= ldev) if (rc) return rc; =20 + /* Select DPLL channel if it is specified */ + if (channel !=3D -1) { + rc =3D zl3073x_write_u8(zldev, ZL_REG_DPLL_MEAS_IDX, channel); + if (rc) + return rc; + } + /* Request to update phase offsets measurement values */ rc =3D zl3073x_write_u8(zldev, ZL_REG_REF_PHASE_ERR_READ_RQST, ZL_REF_PHASE_ERR_READ_RQST_RD); @@ -711,7 +729,7 @@ zl3073x_dev_periodic_work(struct kthread_work *work) int rc; =20 /* Update DPLL-to-connected-ref phase offsets registers */ - rc =3D zl3073x_ref_phase_offsets_update(zldev); + rc =3D zl3073x_ref_phase_offsets_update(zldev, -1); if (rc) dev_warn(zldev->dev, "Failed to update phase offsets: %pe\n", ERR_PTR(rc)); diff --git a/drivers/dpll/zl3073x/core.h b/drivers/dpll/zl3073x/core.h index 97b1032e392d6..1a5edc4975735 100644 --- a/drivers/dpll/zl3073x/core.h +++ b/drivers/dpll/zl3073x/core.h @@ -130,6 +130,7 @@ int zl3073x_write_u48(struct zl3073x_dev *zldev, unsign= ed int reg, u64 val); *****************/ =20 int zl3073x_ref_freq_factorize(u32 freq, u16 *base, u16 *mult); +int zl3073x_ref_phase_offsets_update(struct zl3073x_dev *zldev, int channe= l); =20 static inline bool zl3073x_is_n_pin(u8 id) diff --git a/drivers/dpll/zl3073x/dpll.c b/drivers/dpll/zl3073x/dpll.c index 1a1d4de5b9de5..198e19f6fb152 100644 --- a/drivers/dpll/zl3073x/dpll.c +++ b/drivers/dpll/zl3073x/dpll.c @@ -509,6 +509,7 @@ zl3073x_dpll_input_pin_phase_offset_get(const struct dp= ll_pin *dpll_pin, struct zl3073x_dev *zldev =3D zldpll->dev; struct zl3073x_dpll_pin *pin =3D pin_priv; u8 conn_ref, ref, ref_status; + s64 ref_phase; int rc; =20 /* Get currently connected reference */ @@ -516,9 +517,11 @@ zl3073x_dpll_input_pin_phase_offset_get(const struct d= pll_pin *dpll_pin, if (rc) return rc; =20 - /* Report phase offset only for currently connected pin */ + /* Report phase offset only for currently connected pin if the phase + * monitor feature is disabled. + */ ref =3D zl3073x_input_pin_ref_get(pin->id); - if (ref !=3D conn_ref) { + if (!zldpll->phase_monitor && ref !=3D conn_ref) { *phase_offset =3D 0; =20 return 0; @@ -536,8 +539,38 @@ zl3073x_dpll_input_pin_phase_offset_get(const struct d= pll_pin *dpll_pin, return 0; } =20 - /* Report the latest measured phase offset for the connected ref */ - *phase_offset =3D pin->phase_offset * DPLL_PHASE_OFFSET_DIVIDER; + ref_phase =3D pin->phase_offset; + + /* The DPLL being locked to a higher freq than the current ref + * the phase offset is modded to the period of the signal + * the dpll is locked to. + */ + if (ZL3073X_DPLL_REF_IS_VALID(conn_ref) && conn_ref !=3D ref) { + u32 conn_freq, ref_freq; + + /* Get frequency of connected ref */ + rc =3D zl3073x_dpll_input_ref_frequency_get(zldpll, conn_ref, + &conn_freq); + if (rc) + return rc; + + /* Get frequency of given ref */ + rc =3D zl3073x_dpll_input_ref_frequency_get(zldpll, ref, + &ref_freq); + if (rc) + return rc; + + if (conn_freq > ref_freq) { + s64 conn_period; + int div_factor; + + conn_period =3D div_s64(PSEC_PER_SEC, conn_freq); + div_factor =3D div64_s64(ref_phase, conn_period); + ref_phase -=3D conn_period * div_factor; + } + } + + *phase_offset =3D ref_phase * DPLL_PHASE_OFFSET_DIVIDER; =20 return rc; } @@ -1336,6 +1369,35 @@ zl3073x_dpll_mode_get(const struct dpll_device *dpll= , void *dpll_priv, return 0; } =20 +static int +zl3073x_dpll_phase_offset_monitor_get(const struct dpll_device *dpll, + void *dpll_priv, + enum dpll_feature_state *state, + struct netlink_ext_ack *extack) +{ + struct zl3073x_dpll *zldpll =3D dpll_priv; + + if (zldpll->phase_monitor) + *state =3D DPLL_FEATURE_STATE_ENABLE; + else + *state =3D DPLL_FEATURE_STATE_DISABLE; + + return 0; +} + +static int +zl3073x_dpll_phase_offset_monitor_set(const struct dpll_device *dpll, + void *dpll_priv, + enum dpll_feature_state state, + struct netlink_ext_ack *extack) +{ + struct zl3073x_dpll *zldpll =3D dpll_priv; + + zldpll->phase_monitor =3D (state =3D=3D DPLL_FEATURE_STATE_ENABLE); + + return 0; +} + static const struct dpll_pin_ops zl3073x_dpll_input_pin_ops =3D { .direction_get =3D zl3073x_dpll_pin_direction_get, .esync_get =3D zl3073x_dpll_input_pin_esync_get, @@ -1361,6 +1423,8 @@ static const struct dpll_pin_ops zl3073x_dpll_output_= pin_ops =3D { static const struct dpll_device_ops zl3073x_dpll_device_ops =3D { .lock_status_get =3D zl3073x_dpll_lock_status_get, .mode_get =3D zl3073x_dpll_mode_get, + .phase_offset_monitor_get =3D zl3073x_dpll_phase_offset_monitor_get, + .phase_offset_monitor_set =3D zl3073x_dpll_phase_offset_monitor_set, }; =20 /** @@ -1726,16 +1790,47 @@ zl3073x_dpll_pin_phase_offset_check(struct zl3073x_= dpll_pin *pin) { struct zl3073x_dpll *zldpll =3D pin->dpll; struct zl3073x_dev *zldev =3D zldpll->dev; + unsigned int reg; s64 phase_offset; + u8 ref; int rc; =20 - /* Do not check phase offset if the pin is not connected one */ - if (pin->pin_state !=3D DPLL_PIN_STATE_CONNECTED) + ref =3D zl3073x_input_pin_ref_get(pin->id); + + /* Select register to read phase offset value depending on pin and + * phase monitor state: + * 1) For connected pin use dpll_phase_err_data register + * 2) For other pins use appropriate ref_phase register if the phase + * monitor feature is enabled and reference monitor does not + * report signal errors for given input pin + */ + if (pin->pin_state =3D=3D DPLL_PIN_STATE_CONNECTED) { + reg =3D ZL_REG_DPLL_PHASE_ERR_DATA(zldpll->id); + } else if (zldpll->phase_monitor) { + u8 status; + + /* Get reference monitor status */ + rc =3D zl3073x_read_u8(zldev, ZL_REG_REF_MON_STATUS(ref), + &status); + if (rc) { + dev_err(zldev->dev, + "Failed to read %s refmon status: %pe\n", + pin->label, ERR_PTR(rc)); + + return false; + } + + if (status !=3D ZL_REF_MON_STATUS_OK) + return false; + + reg =3D ZL_REG_REF_PHASE(ref); + } else { + /* The pin is not connected or phase monitor disabled */ return false; + } =20 - /* Read DPLL-to-connected-ref phase offset measurement value */ - rc =3D zl3073x_read_u48(zldev, ZL_REG_DPLL_PHASE_ERR_DATA(zldpll->id), - &phase_offset); + /* Read measured phase offset value */ + rc =3D zl3073x_read_u48(zldev, reg, &phase_offset); if (rc) { dev_err(zldev->dev, "Failed to read ref phase offset: %pe\n", ERR_PTR(rc)); @@ -1800,6 +1895,19 @@ zl3073x_dpll_changes_check(struct zl3073x_dpll *zldp= ll) zldpll->refsel_mode !=3D ZL_DPLL_MODE_REFSEL_MODE_REFLOCK) return; =20 + /* Update phase offset latch registers for this DPLL if the phase + * offset monitor feature is enabled. + */ + if (zldpll->phase_monitor) { + rc =3D zl3073x_ref_phase_offsets_update(zldev, zldpll->id); + if (rc) { + dev_err(zldev->dev, + "Failed to update phase offsets: %pe\n", + ERR_PTR(rc)); + return; + } + } + list_for_each_entry(pin, &zldpll->pins, list) { enum dpll_pin_state state; bool pin_changed =3D false; diff --git a/drivers/dpll/zl3073x/dpll.h b/drivers/dpll/zl3073x/dpll.h index 2e84e56f8c9e1..304910ffc9c07 100644 --- a/drivers/dpll/zl3073x/dpll.h +++ b/drivers/dpll/zl3073x/dpll.h @@ -16,6 +16,7 @@ * @refsel_mode: reference selection mode * @forced_ref: selected reference in forced reference lock mode * @check_count: periodic check counter + * @phase_monitor: is phase offset monitor enabled * @dpll_dev: pointer to registered DPLL device * @lock_status: last saved DPLL lock status * @pins: list of pins @@ -27,6 +28,7 @@ struct zl3073x_dpll { u8 refsel_mode; u8 forced_ref; u8 check_count; + bool phase_monitor; struct dpll_device *dpll_dev; enum dpll_lock_status lock_status; struct list_head pins; diff --git a/drivers/dpll/zl3073x/regs.h b/drivers/dpll/zl3073x/regs.h index 8dde92e623f76..9ee2f44a2eec7 100644 --- a/drivers/dpll/zl3073x/regs.h +++ b/drivers/dpll/zl3073x/regs.h @@ -101,6 +101,9 @@ #define ZL_REG_REF_PHASE_ERR_READ_RQST ZL_REG(4, 0x0f, 1) #define ZL_REF_PHASE_ERR_READ_RQST_RD BIT(0) =20 +#define ZL_REG_REF_PHASE(_idx) \ + ZL_REG_IDX(_idx, 4, 0x20, 6, ZL3073X_NUM_REFS, 6) + /*********************** * Register Page 5, DPLL ***********************/ @@ -119,6 +122,9 @@ #define ZL_DPLL_MEAS_CTRL_EN BIT(0) #define ZL_DPLL_MEAS_CTRL_AVG_FACTOR GENMASK(7, 4) =20 +#define ZL_REG_DPLL_MEAS_IDX ZL_REG(5, 0x51, 1) +#define ZL_DPLL_MEAS_IDX GENMASK(2, 0) + #define ZL_REG_DPLL_PHASE_ERR_READ_MASK ZL_REG(5, 0x54, 1) =20 #define ZL_REG_DPLL_PHASE_ERR_DATA(_idx) \ --=20 2.49.0 From nobody Tue Oct 7 09:56:36 2025 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 36F8B27A92A for ; Thu, 10 Jul 2025 15:39:21 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1752161963; cv=none; b=cRpVbEcNSque+bZTL0gcoQBs55tPm+lURnESKtkYKAhg5tSTH0UPXLQ0YG/VuGHCTsUfiqBSIcGZNDE4LRH9+sRJ/WiOObUMZfksamnNKifLCGSBK0KetHDMKo6Z/fycbK3h0VCPkWbpJJXOuuRNliz2AoVgfXW5m0qIJKIjjag= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1752161963; c=relaxed/simple; bh=lYsTQwvbPjwo9SKP6F9ST1FEjgraIzAPkXnbaN9aFcQ=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=K85zGArwop1bG34IqPyAgpafaUzyl/nurRFjUer4+9JCrDsdgii4v3XbLhLjRwkKSVLi8Z+WFbw1Qg/OvZX78fig94gyALcd41JiOQ6+6b6Xy5gkc5Os/ubnxfYaaHVd/oYsWViy7m1zbX1+wxdwNFte0zr3drmBxTg9mulfd4Y= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com; spf=pass smtp.mailfrom=redhat.com; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b=P+j+w/7G; arc=none smtp.client-ip=170.10.133.124 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=redhat.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="P+j+w/7G" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1752161961; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=/uB4sVuQ3rkZtaIdq9OKeV9DklUJLkLAq+Vft4pdXlc=; b=P+j+w/7GxJ5EKT6bOd1OzgGT7ZSiA077rAUqFYoBJyAbj1iFlHvXr0iqv31KXzWx7DV8DT yExc6tpf+iwtDkeD+8TCIC287C+8+o3iBI1VQucwsnQrCunpmRvsmM9LIXRiKMGzPb11C3 vUOI8sOVrqo8dhkRTMBn5pquddwXRZA= Received: from mx-prod-mc-06.mail-002.prod.us-west-2.aws.redhat.com (ec2-35-165-154-97.us-west-2.compute.amazonaws.com [35.165.154.97]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-96-ltjtO8f_P8i-jIahXqTWcg-1; Thu, 10 Jul 2025 11:39:16 -0400 X-MC-Unique: ltjtO8f_P8i-jIahXqTWcg-1 X-Mimecast-MFC-AGG-ID: ltjtO8f_P8i-jIahXqTWcg_1752161952 Received: from mx-prod-int-02.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-02.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.15]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-06.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 0071618011FB; Thu, 10 Jul 2025 15:39:10 +0000 (UTC) Received: from p16v.luc.cera.cz (unknown [10.44.33.211]) by mx-prod-int-02.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 9CDB51956094; Thu, 10 Jul 2025 15:39:06 +0000 (UTC) From: Ivan Vecera To: netdev@vger.kernel.org Cc: Prathosh Satish , Arkadiusz Kubalewski , Jiri Pirko , "David S. Miller" , Jakub Kicinski , Paolo Abeni , linux-kernel@vger.kernel.org, Michal Schmidt , Petr Oros Subject: [PATCH net-next 4/5] dpll: zl3073x: Add support to adjust phase Date: Thu, 10 Jul 2025 17:38:47 +0200 Message-ID: <20250710153848.928531-5-ivecera@redhat.com> In-Reply-To: <20250710153848.928531-1-ivecera@redhat.com> References: <20250710153848.928531-1-ivecera@redhat.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 3.0 on 10.30.177.15 Content-Type: text/plain; charset="utf-8" Add support to get/set phase adjustment for both input and output pins. The phase adjustment is implemented using reference and output phase offset compensation registers. For input pins the adjustment value can be arbitrary number but for outputs the value has to be a multiple of half synthesizer clock cycles. Co-developed-by: Prathosh Satish Signed-off-by: Prathosh Satish Signed-off-by: Ivan Vecera Reviewed-by: Jiri Pirko Tested-by: Prathosh Satish --- drivers/dpll/zl3073x/dpll.c | 184 ++++++++++++++++++++++++++++++++++++ drivers/dpll/zl3073x/regs.h | 3 + 2 files changed, 187 insertions(+) diff --git a/drivers/dpll/zl3073x/dpll.c b/drivers/dpll/zl3073x/dpll.c index 198e19f6fb152..4e05120c30b9a 100644 --- a/drivers/dpll/zl3073x/dpll.c +++ b/drivers/dpll/zl3073x/dpll.c @@ -575,6 +575,85 @@ zl3073x_dpll_input_pin_phase_offset_get(const struct d= pll_pin *dpll_pin, return rc; } =20 +static int +zl3073x_dpll_input_pin_phase_adjust_get(const struct dpll_pin *dpll_pin, + void *pin_priv, + const struct dpll_device *dpll, + void *dpll_priv, + s32 *phase_adjust, + struct netlink_ext_ack *extack) +{ + struct zl3073x_dpll *zldpll =3D dpll_priv; + struct zl3073x_dev *zldev =3D zldpll->dev; + struct zl3073x_dpll_pin *pin =3D pin_priv; + s64 phase_comp; + u8 ref; + int rc; + + guard(mutex)(&zldev->multiop_lock); + + /* Read reference configuration */ + ref =3D zl3073x_input_pin_ref_get(pin->id); + rc =3D zl3073x_mb_op(zldev, ZL_REG_REF_MB_SEM, ZL_REF_MB_SEM_RD, + ZL_REG_REF_MB_MASK, BIT(ref)); + if (rc) + return rc; + + /* Read current phase offset compensation */ + rc =3D zl3073x_read_u48(zldev, ZL_REG_REF_PHASE_OFFSET_COMP, &phase_comp); + if (rc) + return rc; + + /* Perform sign extension for 48bit signed value */ + phase_comp =3D sign_extend64(phase_comp, 47); + + /* Reverse two's complement negation applied during set and convert + * to 32bit signed int + */ + *phase_adjust =3D (s32) -phase_comp; + + return rc; +} + +static int +zl3073x_dpll_input_pin_phase_adjust_set(const struct dpll_pin *dpll_pin, + void *pin_priv, + const struct dpll_device *dpll, + void *dpll_priv, + s32 phase_adjust, + struct netlink_ext_ack *extack) +{ + struct zl3073x_dpll *zldpll =3D dpll_priv; + struct zl3073x_dev *zldev =3D zldpll->dev; + struct zl3073x_dpll_pin *pin =3D pin_priv; + s64 phase_comp; + u8 ref; + int rc; + + /* The value in the register is stored as two's complement negation + * of requested value. + */ + phase_comp =3D (s64) -phase_adjust; + + guard(mutex)(&zldev->multiop_lock); + + /* Read reference configuration */ + ref =3D zl3073x_input_pin_ref_get(pin->id); + rc =3D zl3073x_mb_op(zldev, ZL_REG_REF_MB_SEM, ZL_REF_MB_SEM_RD, + ZL_REG_REF_MB_MASK, BIT(ref)); + if (rc) + return rc; + + /* Write the requested value into the compensation register */ + rc =3D zl3073x_write_u48(zldev, ZL_REG_REF_PHASE_OFFSET_COMP, phase_comp); + if (rc) + return rc; + + /* Commit reference configuration */ + return zl3073x_mb_op(zldev, ZL_REG_REF_MB_SEM, ZL_REF_MB_SEM_WR, + ZL_REG_REF_MB_MASK, BIT(ref)); +} + /** * zl3073x_dpll_ref_prio_get - get priority for given input pin * @pin: pointer to pin @@ -1278,6 +1357,107 @@ zl3073x_dpll_output_pin_frequency_set(const struct = dpll_pin *dpll_pin, ZL_REG_OUTPUT_MB_MASK, BIT(out)); } =20 +static int +zl3073x_dpll_output_pin_phase_adjust_get(const struct dpll_pin *dpll_pin, + void *pin_priv, + const struct dpll_device *dpll, + void *dpll_priv, + s32 *phase_adjust, + struct netlink_ext_ack *extack) +{ + struct zl3073x_dpll *zldpll =3D dpll_priv; + struct zl3073x_dev *zldev =3D zldpll->dev; + struct zl3073x_dpll_pin *pin =3D pin_priv; + u32 synth_freq; + s32 phase_comp; + u8 out, synth; + int rc; + + out =3D zl3073x_output_pin_out_get(pin->id); + synth =3D zl3073x_out_synth_get(zldev, out); + synth_freq =3D zl3073x_synth_freq_get(zldev, synth); + + guard(mutex)(&zldev->multiop_lock); + + /* Read output configuration */ + rc =3D zl3073x_mb_op(zldev, ZL_REG_OUTPUT_MB_SEM, ZL_OUTPUT_MB_SEM_RD, + ZL_REG_OUTPUT_MB_MASK, BIT(out)); + if (rc) + return rc; + + /* Read current output phase compensation */ + rc =3D zl3073x_read_u32(zldev, ZL_REG_OUTPUT_PHASE_COMP, &phase_comp); + if (rc) + return rc; + + /* Value in register is expressed in half synth clock cycles */ + phase_comp *=3D (int)div_u64(PSEC_PER_SEC, 2 * synth_freq); + + /* Reverse two's complement negation applied during 'set' */ + *phase_adjust =3D -phase_comp; + + return rc; +} + +static int +zl3073x_dpll_output_pin_phase_adjust_set(const struct dpll_pin *dpll_pin, + void *pin_priv, + const struct dpll_device *dpll, + void *dpll_priv, + s32 phase_adjust, + struct netlink_ext_ack *extack) +{ + struct zl3073x_dpll *zldpll =3D dpll_priv; + struct zl3073x_dev *zldev =3D zldpll->dev; + struct zl3073x_dpll_pin *pin =3D pin_priv; + int half_synth_cycle; + u32 synth_freq; + u8 out, synth; + int rc; + + /* Get attached synth */ + out =3D zl3073x_output_pin_out_get(pin->id); + synth =3D zl3073x_out_synth_get(zldev, out); + + /* Get synth's frequency */ + synth_freq =3D zl3073x_synth_freq_get(zldev, synth); + + /* Value in register is expressed in half synth clock cycles so + * the given phase adjustment a multiple of half synth clock. + */ + half_synth_cycle =3D (int)div_u64(PSEC_PER_SEC, 2 * synth_freq); + + if ((phase_adjust % half_synth_cycle) !=3D 0) { + NL_SET_ERR_MSG_FMT(extack, + "Phase adjustment value has to be multiple of %d", + half_synth_cycle); + return -EINVAL; + } + phase_adjust /=3D half_synth_cycle; + + /* The value in the register is stored as two's complement negation + * of requested value. + */ + phase_adjust =3D -phase_adjust; + + guard(mutex)(&zldev->multiop_lock); + + /* Read output configuration */ + rc =3D zl3073x_mb_op(zldev, ZL_REG_OUTPUT_MB_SEM, ZL_OUTPUT_MB_SEM_RD, + ZL_REG_OUTPUT_MB_MASK, BIT(out)); + if (rc) + return rc; + + /* Write the requested value into the compensation register */ + rc =3D zl3073x_write_u32(zldev, ZL_REG_OUTPUT_PHASE_COMP, phase_adjust); + if (rc) + return rc; + + /* Update output configuration from mailbox */ + return zl3073x_mb_op(zldev, ZL_REG_OUTPUT_MB_SEM, ZL_OUTPUT_MB_SEM_WR, + ZL_REG_OUTPUT_MB_MASK, BIT(out)); +} + static int zl3073x_dpll_output_pin_state_on_dpll_get(const struct dpll_pin *dpll_pin, void *pin_priv, @@ -1405,6 +1585,8 @@ static const struct dpll_pin_ops zl3073x_dpll_input_p= in_ops =3D { .frequency_get =3D zl3073x_dpll_input_pin_frequency_get, .frequency_set =3D zl3073x_dpll_input_pin_frequency_set, .phase_offset_get =3D zl3073x_dpll_input_pin_phase_offset_get, + .phase_adjust_get =3D zl3073x_dpll_input_pin_phase_adjust_get, + .phase_adjust_set =3D zl3073x_dpll_input_pin_phase_adjust_set, .prio_get =3D zl3073x_dpll_input_pin_prio_get, .prio_set =3D zl3073x_dpll_input_pin_prio_set, .state_on_dpll_get =3D zl3073x_dpll_input_pin_state_on_dpll_get, @@ -1417,6 +1599,8 @@ static const struct dpll_pin_ops zl3073x_dpll_output_= pin_ops =3D { .esync_set =3D zl3073x_dpll_output_pin_esync_set, .frequency_get =3D zl3073x_dpll_output_pin_frequency_get, .frequency_set =3D zl3073x_dpll_output_pin_frequency_set, + .phase_adjust_get =3D zl3073x_dpll_output_pin_phase_adjust_get, + .phase_adjust_set =3D zl3073x_dpll_output_pin_phase_adjust_set, .state_on_dpll_get =3D zl3073x_dpll_output_pin_state_on_dpll_get, }; =20 diff --git a/drivers/dpll/zl3073x/regs.h b/drivers/dpll/zl3073x/regs.h index 9ee2f44a2eec7..a382cd4a109f5 100644 --- a/drivers/dpll/zl3073x/regs.h +++ b/drivers/dpll/zl3073x/regs.h @@ -168,6 +168,8 @@ #define ZL_REF_CONFIG_ENABLE BIT(0) #define ZL_REF_CONFIG_DIFF_EN BIT(2) =20 +#define ZL_REG_REF_PHASE_OFFSET_COMP ZL_REG(10, 0x28, 6) + #define ZL_REG_REF_SYNC_CTRL ZL_REG(10, 0x2e, 1) #define ZL_REF_SYNC_CTRL_MODE GENMASK(2, 0) #define ZL_REF_SYNC_CTRL_MODE_REFSYNC_PAIR_OFF 0 @@ -237,5 +239,6 @@ #define ZL_REG_OUTPUT_WIDTH ZL_REG(14, 0x10, 4) #define ZL_REG_OUTPUT_ESYNC_PERIOD ZL_REG(14, 0x14, 4) #define ZL_REG_OUTPUT_ESYNC_WIDTH ZL_REG(14, 0x18, 4) +#define ZL_REG_OUTPUT_PHASE_COMP ZL_REG(14, 0x20, 4) =20 #endif /* _ZL3073X_REGS_H */ --=20 2.49.0 From nobody Tue Oct 7 09:56:36 2025 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 79E1227CCE4 for ; Thu, 10 Jul 2025 15:39:49 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1752161991; cv=none; b=jlyPDdhspuE4pPZdSZ8rakDCl8HVPurTV2zyRpHc6mEEKhsr6bXGZ/lpg0D/oHZ2CNLwJAzqX1PtV3iEGTbAseg8w75AvnmRHgKzhCk35nWzkQSf5OfR8qdtErWUBuigDqduroxrvoLwYZCOC0L6xo278ad2l7kf049k9R4Y9aA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1752161991; c=relaxed/simple; bh=OpL2FDt9RCQRjb5xtdvRP249v/kqaDhGqSWlRUDnUJ8=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=uYq/eH5WwFnIQE5dWf261jqZ7j4zHDK/OeCpsLBCEGsJyy+z30FylFWwRTd337eqPzaemFTGGwmi4hc728VuCSndpJ8+yNiGTOBtrejD/KSpY1Y/UFI80RZWqT90W2G888H29DDVU7g7OdHJ3v255y78TGDlZDhz0OoITkogq2Q= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com; spf=pass smtp.mailfrom=redhat.com; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b=CTi9nNgh; arc=none smtp.client-ip=170.10.133.124 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=redhat.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="CTi9nNgh" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1752161988; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=Vw0GvPhTqoaGSG8Ri24kcD2QAy/l1QmTinEC6MhnSoM=; b=CTi9nNghRyZvH4JQ8ag6ikL+tXRKjoOApbpZNmm3T4MRk+8a53vZFvdJfb/HblIyvNIJfR StdVICtlb0d+PQq9BUdy66U9E4wQMz0GKlQgWkQPU2mfkU6OmHNGjnrBU/67gaz079uCP3 xV3yQrg1/PRXRAv5s8UyPxG5z4w6XEo= Received: from mx-prod-mc-05.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-424-Qg4uvgukNdyQun6pGf3JJQ-1; Thu, 10 Jul 2025 11:39:15 -0400 X-MC-Unique: Qg4uvgukNdyQun6pGf3JJQ-1 X-Mimecast-MFC-AGG-ID: Qg4uvgukNdyQun6pGf3JJQ_1752161953 Received: from mx-prod-int-02.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-02.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.15]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-05.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id A6C941956095; Thu, 10 Jul 2025 15:39:13 +0000 (UTC) Received: from p16v.luc.cera.cz (unknown [10.44.33.211]) by mx-prod-int-02.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 7133C1956094; Thu, 10 Jul 2025 15:39:10 +0000 (UTC) From: Ivan Vecera To: netdev@vger.kernel.org Cc: Prathosh Satish , Arkadiusz Kubalewski , Jiri Pirko , "David S. Miller" , Jakub Kicinski , Paolo Abeni , linux-kernel@vger.kernel.org, Michal Schmidt , Petr Oros Subject: [PATCH net-next 5/5] dpll: zl3073x: Add support to get fractional frequency offset Date: Thu, 10 Jul 2025 17:38:48 +0200 Message-ID: <20250710153848.928531-6-ivecera@redhat.com> In-Reply-To: <20250710153848.928531-1-ivecera@redhat.com> References: <20250710153848.928531-1-ivecera@redhat.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 3.0 on 10.30.177.15 Content-Type: text/plain; charset="utf-8" Adds support to get fractional frequency offset for input pins. Implement the appropriate callback and function that periodicaly performs reference frequency measurement and notifies DPLL core about changes. Co-developed-by: Prathosh Satish Signed-off-by: Prathosh Satish Signed-off-by: Ivan Vecera Reviewed-by: Jiri Pirko Tested-by: Prathosh Satish --- drivers/dpll/zl3073x/core.c | 67 +++++++++++++++++++++++++++++++++++ drivers/dpll/zl3073x/core.h | 15 ++++++++ drivers/dpll/zl3073x/dpll.c | 69 +++++++++++++++++++++++++++++++++++-- drivers/dpll/zl3073x/regs.h | 19 ++++++++++ 4 files changed, 168 insertions(+), 2 deletions(-) diff --git a/drivers/dpll/zl3073x/core.c b/drivers/dpll/zl3073x/core.c index eb62a492b1727..7ebcfc5ec1f09 100644 --- a/drivers/dpll/zl3073x/core.c +++ b/drivers/dpll/zl3073x/core.c @@ -720,6 +720,66 @@ int zl3073x_ref_phase_offsets_update(struct zl3073x_de= v *zldev, int channel) ZL_REF_PHASE_ERR_READ_RQST_RD); } =20 +/** + * zl3073x_ref_ffo_update - update reference fractional frequency offsets + * @zldev: pointer to zl3073x_dev structure + * + * The function asks device to update fractional frequency offsets latch + * registers the latest measured values, reads and stores them into + * + * Return: 0 on success, <0 on error + */ +static int +zl3073x_ref_ffo_update(struct zl3073x_dev *zldev) +{ + int i, rc; + + /* Per datasheet we have to wait for 'ref_freq_meas_ctrl' to be zero + * to ensure that the measured data are coherent. + */ + rc =3D zl3073x_poll_zero_u8(zldev, ZL_REG_REF_FREQ_MEAS_CTRL, + ZL_REF_FREQ_MEAS_CTRL); + if (rc) + return rc; + + /* Select all references for measurement */ + rc =3D zl3073x_write_u8(zldev, ZL_REG_REF_FREQ_MEAS_MASK_3_0, + GENMASK(7, 0)); /* REF0P..REF3N */ + if (rc) + return rc; + rc =3D zl3073x_write_u8(zldev, ZL_REG_REF_FREQ_MEAS_MASK_4, + GENMASK(1, 0)); /* REF4P..REF4N */ + if (rc) + return rc; + + /* Request frequency offset measurement */ + rc =3D zl3073x_write_u8(zldev, ZL_REG_REF_FREQ_MEAS_CTRL, + ZL_REF_FREQ_MEAS_CTRL_REF_FREQ_OFF); + if (rc) + return rc; + + /* Wait for finish */ + rc =3D zl3073x_poll_zero_u8(zldev, ZL_REG_REF_FREQ_MEAS_CTRL, + ZL_REF_FREQ_MEAS_CTRL); + if (rc) + return rc; + + /* Read DPLL-to-REFx frequency offset measurements */ + for (i =3D 0; i < ZL3073X_NUM_REFS; i++) { + s32 value; + + /* Read value stored in units of 2^-32 signed */ + rc =3D zl3073x_read_u32(zldev, ZL_REG_REF_FREQ(i), &value); + if (rc) + return rc; + + /* Convert to ppm -> ffo =3D (10^6 * value) / 2^32 */ + zldev->ref[i].ffo =3D mul_s64_u64_shr(value, 1000000, 32); + } + + return 0; +} + static void zl3073x_dev_periodic_work(struct kthread_work *work) { @@ -734,6 +794,13 @@ zl3073x_dev_periodic_work(struct kthread_work *work) dev_warn(zldev->dev, "Failed to update phase offsets: %pe\n", ERR_PTR(rc)); =20 + /* Update references' fractional frequency offsets */ + rc =3D zl3073x_ref_ffo_update(zldev); + if (rc) + dev_warn(zldev->dev, + "Failed to update fractional frequency offsets: %pe\n", + ERR_PTR(rc)); + list_for_each_entry(zldpll, &zldev->dplls, list) zl3073x_dpll_changes_check(zldpll); =20 diff --git a/drivers/dpll/zl3073x/core.h b/drivers/dpll/zl3073x/core.h index 1a5edc4975735..71af2c8001109 100644 --- a/drivers/dpll/zl3073x/core.h +++ b/drivers/dpll/zl3073x/core.h @@ -30,10 +30,12 @@ struct zl3073x_dpll; * struct zl3073x_ref - input reference invariant info * @enabled: input reference is enabled or disabled * @diff: true if input reference is differential + * @ffo: current fractional frequency offset */ struct zl3073x_ref { bool enabled; bool diff; + s64 ffo; }; =20 /** @@ -170,6 +172,19 @@ zl3073x_output_pin_out_get(u8 id) return id / 2; } =20 +/** + * zl3073x_ref_ffo_get - get current fractional frequency offset + * @zldev: pointer to zl3073x device + * @index: input reference index + * + * Return: the latest measured fractional frequency offset + */ +static inline s64 +zl3073x_ref_ffo_get(struct zl3073x_dev *zldev, u8 index) +{ + return zldev->ref[index].ffo; +} + /** * zl3073x_ref_is_diff - check if the given input reference is differential * @zldev: pointer to zl3073x device diff --git a/drivers/dpll/zl3073x/dpll.c b/drivers/dpll/zl3073x/dpll.c index 4e05120c30b9a..b061a75aad1a7 100644 --- a/drivers/dpll/zl3073x/dpll.c +++ b/drivers/dpll/zl3073x/dpll.c @@ -37,6 +37,7 @@ * @esync_control: embedded sync is controllable * @pin_state: last saved pin state * @phase_offset: last saved pin phase offset + * @freq_offset: last saved fractional frequency offset */ struct zl3073x_dpll_pin { struct list_head list; @@ -50,6 +51,7 @@ struct zl3073x_dpll_pin { bool esync_control; enum dpll_pin_state pin_state; s64 phase_offset; + s64 freq_offset; }; =20 /* @@ -270,6 +272,18 @@ zl3073x_dpll_input_pin_esync_set(const struct dpll_pin= *dpll_pin, ZL_REG_REF_MB_MASK, BIT(ref)); } =20 +static int +zl3073x_dpll_input_pin_ffo_get(const struct dpll_pin *dpll_pin, void *pin_= priv, + const struct dpll_device *dpll, void *dpll_priv, + s64 *ffo, struct netlink_ext_ack *extack) +{ + struct zl3073x_dpll_pin *pin =3D pin_priv; + + *ffo =3D pin->freq_offset; + + return 0; +} + static int zl3073x_dpll_input_pin_frequency_get(const struct dpll_pin *dpll_pin, void *pin_priv, @@ -1582,6 +1596,7 @@ static const struct dpll_pin_ops zl3073x_dpll_input_p= in_ops =3D { .direction_get =3D zl3073x_dpll_pin_direction_get, .esync_get =3D zl3073x_dpll_input_pin_esync_get, .esync_set =3D zl3073x_dpll_input_pin_esync_set, + .ffo_get =3D zl3073x_dpll_input_pin_ffo_get, .frequency_get =3D zl3073x_dpll_input_pin_frequency_get, .frequency_set =3D zl3073x_dpll_input_pin_frequency_set, .phase_offset_get =3D zl3073x_dpll_input_pin_phase_offset_get, @@ -2037,6 +2052,52 @@ zl3073x_dpll_pin_phase_offset_check(struct zl3073x_d= pll_pin *pin) return false; } =20 +/** + * zl3073x_dpll_pin_ffo_check - check for pin fractional frequency offset = change + * @pin: pin to check + * + * Check for the given pin's fractional frequency change. + * + * Return: true on fractional frequency offset change, false otherwise + */ +static bool +zl3073x_dpll_pin_ffo_check(struct zl3073x_dpll_pin *pin) +{ + struct zl3073x_dpll *zldpll =3D pin->dpll; + struct zl3073x_dev *zldev =3D zldpll->dev; + u8 ref, status; + s64 ffo; + int rc; + + /* Get reference monitor status */ + ref =3D zl3073x_input_pin_ref_get(pin->id); + rc =3D zl3073x_read_u8(zldev, ZL_REG_REF_MON_STATUS(ref), &status); + if (rc) { + dev_err(zldev->dev, "Failed to read %s refmon status: %pe\n", + pin->label, ERR_PTR(rc)); + + return false; + } + + /* Do not report ffo changes if the reference monitor report errors */ + if (status !=3D ZL_REF_MON_STATUS_OK) + return false; + + /* Get the latest measured ref's ffo */ + ffo =3D zl3073x_ref_ffo_get(zldev, ref); + + /* Compare with previous value */ + if (pin->freq_offset !=3D ffo) { + dev_dbg(zldev->dev, "%s freq offset changed: %lld -> %lld\n", + pin->label, pin->freq_offset, ffo); + pin->freq_offset =3D ffo; + + return true; + } + + return false; +} + /** * zl3073x_dpll_changes_check - check for changes and send notifications * @zldpll: pointer to zl3073x_dpll structure @@ -2117,11 +2178,15 @@ zl3073x_dpll_changes_check(struct zl3073x_dpll *zld= pll) pin_changed =3D true; } =20 - /* Check for phase offset change once per second */ - if (zldpll->check_count % 2 =3D=3D 0) + /* Check for phase offset and ffo change once per second */ + if (zldpll->check_count % 2 =3D=3D 0) { if (zl3073x_dpll_pin_phase_offset_check(pin)) pin_changed =3D true; =20 + if (zl3073x_dpll_pin_ffo_check(pin)) + pin_changed =3D true; + } + if (pin_changed) dpll_pin_change_ntf(pin->dpll_pin); } diff --git a/drivers/dpll/zl3073x/regs.h b/drivers/dpll/zl3073x/regs.h index a382cd4a109f5..614e33128a5c9 100644 --- a/drivers/dpll/zl3073x/regs.h +++ b/drivers/dpll/zl3073x/regs.h @@ -94,6 +94,9 @@ #define ZL_DPLL_REFSEL_STATUS_STATE GENMASK(6, 4) #define ZL_DPLL_REFSEL_STATUS_STATE_LOCK 4 =20 +#define ZL_REG_REF_FREQ(_idx) \ + ZL_REG_IDX(_idx, 2, 0x44, 4, ZL3073X_NUM_REFS, 4) + /********************** * Register Page 4, Ref **********************/ @@ -101,6 +104,22 @@ #define ZL_REG_REF_PHASE_ERR_READ_RQST ZL_REG(4, 0x0f, 1) #define ZL_REF_PHASE_ERR_READ_RQST_RD BIT(0) =20 +#define ZL_REG_REF_FREQ_MEAS_CTRL ZL_REG(4, 0x1c, 1) +#define ZL_REF_FREQ_MEAS_CTRL GENMASK(1, 0) +#define ZL_REF_FREQ_MEAS_CTRL_REF_FREQ 1 +#define ZL_REF_FREQ_MEAS_CTRL_REF_FREQ_OFF 2 +#define ZL_REF_FREQ_MEAS_CTRL_DPLL_FREQ_OFF 3 + +#define ZL_REG_REF_FREQ_MEAS_MASK_3_0 ZL_REG(4, 0x1d, 1) +#define ZL_REF_FREQ_MEAS_MASK_3_0(_ref) BIT(_ref) + +#define ZL_REG_REF_FREQ_MEAS_MASK_4 ZL_REG(4, 0x1e, 1) +#define ZL_REF_FREQ_MEAS_MASK_4(_ref) BIT((_ref) - 8) + +#define ZL_REG_DPLL_MEAS_REF_FREQ_CTRL ZL_REG(4, 0x1f, 1) +#define ZL_DPLL_MEAS_REF_FREQ_CTRL_EN BIT(0) +#define ZL_DPLL_MEAS_REF_FREQ_CTRL_IDX GENMASK(6, 4) + #define ZL_REG_REF_PHASE(_idx) \ ZL_REG_IDX(_idx, 4, 0x20, 6, ZL3073X_NUM_REFS, 6) =20 --=20 2.49.0