@@ -79,6 +79,7 @@ ZL3073X_REG16_DEF(ref_freq_base, 0x505);
ZL3073X_REG16_DEF(ref_freq_mult, 0x507);
ZL3073X_REG16_DEF(ref_ratio_m, 0x509);
ZL3073X_REG16_DEF(ref_ratio_n, 0x50b);
+ZL3073X_REG48_DEF(ref_phase_compensation, 0x528);
/*
* Register Map Page 12, DPLL Mailbox
@@ -98,6 +99,8 @@ ZL3073X_REG32_DEF(output_width, 0x710);
ZL3073X_REG32_DEF(output_ndiv_period, 0x714);
ZL3073X_REG32_DEF(output_ndiv_width, 0x718);
+ZL3073X_REG32_DEF(output_phase_compensation, 0x720);
+
#define ZL3073X_REF_NONE ZL3073X_NUM_INPUT_PINS
#define ZL3073X_REF_IS_VALID(_ref) ((_ref) != ZL3073X_REF_NONE)
@@ -690,6 +693,85 @@ zl3073x_dpll_input_pin_phase_offset_get(const struct dpll_pin *dpll_pin,
return rc;
}
+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 = dpll_priv;
+ struct zl3073x_dev *zldev = zldpll->mfd;
+ struct zl3073x_dpll_pin *pin = pin_priv;
+ s64 phase_comp;
+ u8 ref_id;
+ int rc;
+
+ guard(zl3073x)(zldev);
+
+ /* Get index of the pin */
+ ref_id = zl3073x_dpll_pin_index_get(pin);
+
+ /* Read reference configuration into mailbox */
+ rc = zl3073x_mb_ref_read(zldev, ref_id);
+ if (rc)
+ return rc;
+
+ /* Read current phase offset compensation */
+ rc = zl3073x_read_ref_phase_compensation(zldev, &phase_comp);
+ if (rc)
+ return rc;
+
+ /* Perform sign extension for 48bit signed value */
+ phase_comp = sign_extend64(phase_comp, 47);
+
+ /* Reverse two's complement negation applied during set and convert
+ * to 32bit signed int
+ */
+ *phase_adjust = (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 = dpll_priv;
+ struct zl3073x_dev *zldev = zldpll->mfd;
+ struct zl3073x_dpll_pin *pin = pin_priv;
+ s64 phase_comp;
+ u8 ref_id;
+ int rc;
+
+ guard(zl3073x)(zldev);
+
+ /* The value in the register is stored as two's complement negation
+ * of requested value.
+ */
+ phase_comp = (s64) -phase_adjust;
+
+ /* Write the requested value into the compensation register */
+ rc = zl3073x_write_ref_phase_compensation(zldev, phase_comp);
+ if (rc)
+ return rc;
+
+ /* Get index of the pin */
+ ref_id = zl3073x_dpll_pin_index_get(pin);
+
+ /* Update reference configuration from mailbox */
+ rc = zl3073x_mb_ref_write(zldev, ref_id);
+ if (rc)
+ return rc;
+
+ return rc;
+}
+
static int
zl3073x_dpll_ref_prio_get(struct zl3073x_dpll_pin *pin, u8 *prio)
{
@@ -1165,6 +1247,102 @@ zl3073x_dpll_output_pin_frequency_set(const struct dpll_pin *dpll_pin,
return zl3073x_mb_output_write(zldev, output);
}
+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 = dpll_priv;
+ struct zl3073x_dev *zldev = zldpll->mfd;
+ struct zl3073x_dpll_pin *pin = pin_priv;
+ u8 output, synth;
+ u64 synth_freq;
+ s32 phase_comp;
+ int rc;
+
+ guard(zl3073x)(zldev);
+
+ output = zl3073x_dpll_output_pin_output_get(pin);
+ synth = zl3073x_dpll_pin_synth_get(pin);
+ synth_freq = zl3073x_synth_freq_get(zldev, synth);
+
+ /* Read output configuration into mailbox */
+ rc = zl3073x_mb_output_read(zldev, output);
+ if (rc)
+ return rc;
+
+ /* Read current output phase compensation */
+ rc = zl3073x_read_output_phase_compensation(zldev, &phase_comp);
+ if (rc)
+ return rc;
+
+ /* Value in register is expressed in half synth clock cycles */
+ phase_comp *= (int)div_u64(PSEC_PER_SEC, 2 * synth_freq);
+
+ /* Reverse two's complement negation applied during 'set' */
+ *phase_adjust = -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 = dpll_priv;
+ struct zl3073x_dev *zldev = zldpll->mfd;
+ struct zl3073x_dpll_pin *pin = pin_priv;
+ int half_synth_cycle;
+ u8 output, synth;
+ u64 synth_freq;
+ int phase_comp;
+ int rc;
+
+ /* Get attached synth */
+ synth = zl3073x_dpll_pin_synth_get(pin);
+
+ /* Get synth's frequency */
+ synth_freq = 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 = (int)div_u64(PSEC_PER_SEC, 2 * synth_freq);
+
+ if ((phase_adjust % half_synth_cycle) != 0) {
+ NL_SET_ERR_MSG_FMT(extack,
+ "Phase adjustment value has to be multiple of %d",
+ half_synth_cycle);
+ return -EINVAL;
+ }
+ phase_adjust /= half_synth_cycle;
+
+ guard(zl3073x)(zldev);
+
+ /* The value in the register is stored as two's complement negation
+ * of requested value.
+ */
+ phase_comp = -phase_adjust;
+
+ /* Write the requested value into the compensation register */
+ rc = zl3073x_write_output_phase_compensation(zldev, phase_comp);
+ if (rc)
+ return rc;
+
+ /* Update output configuration from mailbox */
+ output = zl3073x_dpll_output_pin_output_get(pin);
+ rc = zl3073x_mb_output_write(zldev, output);
+
+ return rc;
+}
+
static int
zl3073x_dpll_output_pin_state_on_dpll_get(const struct dpll_pin *dpll_pin,
void *pin_priv,
@@ -1243,6 +1421,8 @@ static const struct dpll_pin_ops zl3073x_dpll_input_pin_ops = {
.frequency_get = zl3073x_dpll_input_pin_frequency_get,
.frequency_set = zl3073x_dpll_input_pin_frequency_set,
.phase_offset_get = zl3073x_dpll_input_pin_phase_offset_get,
+ .phase_adjust_get = zl3073x_dpll_input_pin_phase_adjust_get,
+ .phase_adjust_set = zl3073x_dpll_input_pin_phase_adjust_set,
.prio_get = zl3073x_dpll_input_pin_prio_get,
.prio_set = zl3073x_dpll_input_pin_prio_set,
.state_on_dpll_get = zl3073x_dpll_input_pin_state_on_dpll_get,
@@ -1253,6 +1433,8 @@ static const struct dpll_pin_ops zl3073x_dpll_output_pin_ops = {
.direction_get = zl3073x_dpll_pin_direction_get,
.frequency_get = zl3073x_dpll_output_pin_frequency_get,
.frequency_set = zl3073x_dpll_output_pin_frequency_set,
+ .phase_adjust_get = zl3073x_dpll_output_pin_phase_adjust_get,
+ .phase_adjust_set = zl3073x_dpll_output_pin_phase_adjust_set,
.state_on_dpll_get = zl3073x_dpll_output_pin_state_on_dpll_get,
};