mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: <Prathosh.Satish@microchip.com>
To: <ivecera@redhat.com>, <netdev@vger.kernel.org>
Cc: <arkadiusz.kubalewski@intel.com>, <jiri@resnulli.us>,
	<davem@davemloft.net>, <kuba@kernel.org>, <pabeni@redhat.com>,
	<linux-kernel@vger.kernel.org>, <mschmidt@redhat.com>,
	<poros@redhat.com>
Subject: Re: [PATCH net-next 1/5] dpll: zl3073x: Add support to get/set esync on pins
Date: Mon, 14 Jul 2025 14:13:58 +0000	[thread overview]
Message-ID: <d5285a54-2ffc-413f-8371-431a6d8d8b32@microchip.com> (raw)
In-Reply-To: <20250710153848.928531-2-ivecera@redhat.com>

On 10/07/2025 16:38, Ivan Vecera wrote:
> EXTERNAL EMAIL: Do not click links or open attachments unless you know the content is safe
>
> 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 <Prathosh.Satish@microchip.com>
> Signed-off-by: Prathosh Satish <Prathosh.Satish@microchip.com>
> Signed-off-by: Ivan Vecera <ivecera@redhat.com>
> ---
>   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;
>   };
>
> +/*
> + * Supported esync ranges for input and for output per output pair type
> + */
> +static const struct dpll_pin_frequency esync_freq_ranges[] = {
> +       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_dpll *zldpll, u8 ref_id,
>          return rc;
>   }
>
> +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 = dpll_priv;
> +       struct zl3073x_dev *zldev = zldpll->dev;
> +       struct zl3073x_dpll_pin *pin = pin_priv;
> +       u8 ref, ref_sync_ctrl, sync_mode;
> +       u32 esync_div, ref_freq;
> +       int rc;
> +
> +       /* Get reference frequency */
> +       ref = zl3073x_input_pin_ref_get(pin->id);
> +       rc = 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 = 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 = zl3073x_read_u8(zldev, ZL_REG_REF_SYNC_CTRL, &ref_sync_ctrl);
> +       if (rc)
> +               return rc;
> +
> +       /* Get esync divisor */
> +       rc = zl3073x_read_u32(zldev, ZL_REG_REF_ESYNC_DIV, &esync_div);
> +       if (rc)
> +               return rc;
> +
> +       sync_mode = 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 = (esync_div == ZL_REF_ESYNC_DIV_1HZ) ? 1 : 0;
> +               esync->pulse = 25;
> +               break;
> +       default:
> +               esync->freq = 0;
> +               esync->pulse = 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 = esync_freq_ranges;
> +               esync->range_num = ARRAY_SIZE(esync_freq_ranges);
> +       } else {
> +               esync->range = NULL;
> +               esync->range_num = 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 = dpll_priv;
> +       struct zl3073x_dev *zldev = zldpll->dev;
> +       struct zl3073x_dpll_pin *pin = pin_priv;
> +       u8 ref, ref_sync_ctrl, sync_mode;
> +       int rc;
> +
> +       guard(mutex)(&zldev->multiop_lock);
> +
> +       /* Read reference configuration into mailbox */
> +       ref = zl3073x_input_pin_ref_get(pin->id);
> +       rc = 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 = zl3073x_read_u8(zldev, ZL_REG_REF_SYNC_CTRL, &ref_sync_ctrl);
> +       if (rc)
> +               return rc;
> +
> +       /* Use freq == 0 to disable esync */
> +       if (!freq)
> +               sync_mode = ZL_REF_SYNC_CTRL_MODE_REFSYNC_PAIR_OFF;
> +       else
> +               sync_mode = ZL_REF_SYNC_CTRL_MODE_50_50_ESYNC_25_75;
> +
> +       ref_sync_ctrl &= ~ZL_REF_SYNC_CTRL_MODE;
> +       ref_sync_ctrl |= FIELD_PREP(ZL_REF_SYNC_CTRL_MODE, sync_mode);
> +
> +       /* Update ref sync control register */
> +       rc = 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 = 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;
>   }
>
> +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 = dpll_priv;
> +       struct zl3073x_dev *zldev = zldpll->dev;
> +       struct zl3073x_dpll_pin *pin = pin_priv;
> +       struct device *dev = zldev->dev;
> +       u32 esync_period, esync_width;
> +       u8 clock_type, synth;
> +       u8 out, output_mode;
> +       u32 output_div;
> +       u32 synth_freq;
> +       int rc;
> +
> +       out = 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 = 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 = zl3073x_read_u8(zldev, ZL_REG_OUTPUT_MODE, &output_mode);
> +       if (rc)
> +               return rc;
> +
> +       /* Read output divisor */
> +       rc = 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 = zl3073x_out_synth_get(zldev, out);
> +
> +       /* Get synth frequency */
> +       synth_freq = zl3073x_synth_freq_get(zldev, synth);
> +
> +       clock_type = FIELD_GET(ZL_OUTPUT_MODE_CLOCK_TYPE, output_mode);
> +       if (clock_type != ZL_OUTPUT_MODE_CLOCK_TYPE_ESYNC) {
> +               /* No need to read esync data if it is not enabled */
> +               esync->freq = 0;
> +               esync->pulse = 0;
> +
> +               goto finish;
> +       }
> +
> +       /* Read esync period */
> +       rc = 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 = zl3073x_read_u32(zldev, ZL_REG_OUTPUT_ESYNC_WIDTH, &esync_width);
> +       if (rc)
> +               return rc;
> +
> +       /* Compute esync frequency */
> +       esync->freq = 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 = (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 = esync_freq_ranges;
> +               esync->range_num = ARRAY_SIZE(esync_freq_ranges);
> +       } else {
> +               esync->range = NULL;
> +               esync->range_num = 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 = dpll_priv;
> +       struct zl3073x_dev *zldev = zldpll->dev;
> +       struct zl3073x_dpll_pin *pin = pin_priv;
> +       u32 esync_period, esync_width, output_div;
> +       u8 clock_type, out, output_mode, synth;
> +       u32 synth_freq;
> +       int rc;
> +
> +       out = 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 = 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 = zl3073x_read_u8(zldev, ZL_REG_OUTPUT_MODE, &output_mode);
> +       if (rc)
> +               return rc;
> +
> +       /* Select clock type */
> +       if (freq)
> +               clock_type = ZL_OUTPUT_MODE_CLOCK_TYPE_ESYNC;
> +       else
> +               clock_type = ZL_OUTPUT_MODE_CLOCK_TYPE_NORMAL;
> +
> +       /* Update clock type in output mode */
> +       output_mode &= ~ZL_OUTPUT_MODE_CLOCK_TYPE;
> +       output_mode |= FIELD_PREP(ZL_OUTPUT_MODE_CLOCK_TYPE, clock_type);
> +       rc = 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 = zl3073x_out_synth_get(zldev, out);
> +
> +       /* Get synth frequency */
> +       synth_freq = zl3073x_synth_freq_get(zldev, synth);
> +
> +       rc = zl3073x_read_u32(zldev, ZL_REG_OUTPUT_DIV, &output_div);
> +       if (rc)
> +               return rc;
> +
> +       /* Compute and update esync period */
> +       esync_period = synth_freq / (u32)freq / output_div;
> +       rc = 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 = output_div / 2;
> +       rc = 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,
>
>   static const struct dpll_pin_ops zl3073x_dpll_input_pin_ops = {
>          .direction_get = zl3073x_dpll_pin_direction_get,
> +       .esync_get = zl3073x_dpll_input_pin_esync_get,
> +       .esync_set = zl3073x_dpll_input_pin_esync_set,
>          .frequency_get = zl3073x_dpll_input_pin_frequency_get,
>          .frequency_set = zl3073x_dpll_input_pin_frequency_set,
>          .prio_get = zl3073x_dpll_input_pin_prio_get,
> @@ -966,6 +1304,8 @@ static const struct dpll_pin_ops zl3073x_dpll_input_pin_ops = {
>
>   static const struct dpll_pin_ops zl3073x_dpll_output_pin_ops = {
>          .direction_get = zl3073x_dpll_pin_direction_get,
> +       .esync_get = zl3073x_dpll_output_pin_esync_get,
> +       .esync_set = zl3073x_dpll_output_pin_esync_set,
>          .frequency_get = zl3073x_dpll_output_pin_frequency_get,
>          .frequency_set = zl3073x_dpll_output_pin_frequency_set,
>          .state_on_dpll_get = zl3073x_dpll_output_pin_state_on_dpll_get,
> @@ -1040,8 +1380,9 @@ zl3073x_dpll_pin_register(struct zl3073x_dpll_pin *pin, u32 index)
>          if (IS_ERR(props))
>                  return PTR_ERR(props);
>
> -       /* Save package label */
> +       /* Save package label & esync capability */
>          strscpy(pin->label, props->package_label);
> +       pin->esync_control = props->esync_control;
>
>          if (zl3073x_dpll_is_input_pin(pin)) {
>                  rc = 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)
>
> +#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)
>
>   #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
> --
> 2.49.0
>
Tested-by: Prathosh Satish <prathosh.satish@microchip.com>


  reply	other threads:[~2025-07-14 14:14 UTC|newest]

Thread overview: 18+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-07-10 15:38 [PATCH net-next 0/5] dpll: zl3073x: Add misc features Ivan Vecera
2025-07-10 15:38 ` [PATCH net-next 1/5] dpll: zl3073x: Add support to get/set esync on pins Ivan Vecera
2025-07-14 14:13   ` Prathosh.Satish [this message]
2025-07-15 12:52   ` Paolo Abeni
2025-07-15 14:10     ` Ivan Vecera
2025-07-10 15:38 ` [PATCH net-next 2/5] dpll: zl3073x: Add support to get phase offset on connected input pin Ivan Vecera
2025-07-14 14:14   ` Prathosh.Satish
2025-07-10 15:38 ` [PATCH net-next 3/5] dpll: zl3073x: Implement phase offset monitor feature Ivan Vecera
2025-07-14 14:15   ` Prathosh.Satish
2025-07-15 13:02   ` Paolo Abeni
2025-07-15 14:18     ` Ivan Vecera
2025-07-10 15:38 ` [PATCH net-next 4/5] dpll: zl3073x: Add support to adjust phase Ivan Vecera
2025-07-14 14:15   ` Prathosh.Satish
2025-07-15 13:12   ` Paolo Abeni
2025-07-15 14:24     ` Ivan Vecera
2025-07-10 15:38 ` [PATCH net-next 5/5] dpll: zl3073x: Add support to get fractional frequency offset Ivan Vecera
2025-07-14 10:55 ` [PATCH net-next 0/5] dpll: zl3073x: Add misc features Jiri Pirko
2025-07-14 14:11 ` Prathosh.Satish

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=d5285a54-2ffc-413f-8371-431a6d8d8b32@microchip.com \
    --to=prathosh.satish@microchip.com \
    --cc=arkadiusz.kubalewski@intel.com \
    --cc=davem@davemloft.net \
    --cc=ivecera@redhat.com \
    --cc=jiri@resnulli.us \
    --cc=kuba@kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=mschmidt@redhat.com \
    --cc=netdev@vger.kernel.org \
    --cc=pabeni@redhat.com \
    --cc=poros@redhat.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®