* @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 {
        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
        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,
        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)
+{
+       u32 esync_period, esync_width, output_div;
+       struct zl3073x_dpll *zldpll = dpll_priv;
+       struct zl3073x_dev *zldev = zldpll->dev;
+       struct zl3073x_dpll_pin *pin = pin_priv;
+       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;
+
+       /* Check output divisor for zero */
+       if (!output_div) {
+               dev_err(zldev->dev,
+                       "Zero divisor for OUTPUT%u got from device\n", out);
+               return -EINVAL;
+       }
+
+       /* 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,
 
 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,
 
 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,
        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);