@@ -36,12 +36,31 @@ ZL3073X_REG8_IDX_DEF(dpll_refsel_status, 0x130, ZL3073X_NUM_CHANNELS, 1);
#define DPLL_REFSEL_STATUS_STATE_ACQUIRING 3
#define DPLL_REFSEL_STATUS_STATE_LOCK 4
+ZL3073X_REG32_IDX_DEF(ref_freq, 0x144,
+ ZL3073X_NUM_INPUT_PINS, 4);
+
/*
* Register Map Page 4, Ref
*/
ZL3073X_REG8_DEF(ref_phase_err_read_rqst, 0x20f);
#define REF_PHASE_ERR_READ_RQST_RD BIT(0)
+ZL3073X_REG8_DEF(ref_freq_meas_ctrl, 0x21c);
+#define REF_FREQ_MEAS_CTRL_LATCH GENMASK(1, 0)
+#define REF_FREQ_MEAS_CTRL_LATCH_REF_FREQ 1
+#define REF_FREQ_MEAS_CTRL_LATCH_REF_FREQ_OFF 2
+#define REF_FREQ_MEAS_CTRL_LATCH_DPLL_FREQ_OFF 3
+
+ZL3073X_REG8_DEF(ref_freq_meas_mask_3_0, 0x21d);
+#define REF_FREQ_MEAS_MASK_3_0(_ref) BIT(_ref)
+
+ZL3073X_REG8_DEF(ref_freq_meas_mask_4, 0x21e);
+#define REF_FREQ_MEAS_MASK_4(_ref) BIT((_ref) - 8)
+
+ZL3073X_REG8_DEF(dpll_meas_ref_freq_ctrl, 0x21f);
+#define DPLL_MEAS_REF_FREQ_CTRL_EN BIT(0)
+#define DPLL_MEAS_REF_FREQ_CTRL_IDX GENMASK(6, 4)
+
ZL3073X_REG48_IDX_DEF(ref_phase, 0x220,
ZL3073X_NUM_INPUT_PINS, 6);
@@ -140,6 +159,7 @@ struct zl3073x_dpll_pin_info {
* @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 dpll_pin *dpll_pin;
@@ -149,6 +169,7 @@ struct zl3073x_dpll_pin {
bool esync_control;
enum dpll_pin_state pin_state;
s64 phase_offset;
+ s64 freq_offset;
};
/**
@@ -498,6 +519,79 @@ zl3073x_dpll_input_pin_esync_set(const struct dpll_pin *dpll_pin,
return rc;
}
+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 *zldpll = dpll_priv;
+ struct zl3073x_dev *zldev = zldpll->mfd;
+ struct zl3073x_dpll_pin *pin = pin_priv;
+ u8 dpll_meas_ref_freq_ctrl, ref_id;
+ u8 ref_freq_meas_ctrl, ref_mask;
+ s32 freq_offset;
+ int rc;
+
+ /* Take device lock */
+ guard(zl3073x)(zldev);
+
+ /* Get index of the pin */
+ ref_id = zl3073x_dpll_pin_index_get(pin);
+
+ /* Wait for being ready */
+ rc = zl3073x_wait_clear_bits(zldev, ref_freq_meas_ctrl,
+ REF_FREQ_MEAS_CTRL_LATCH);
+ if (rc)
+ return rc;
+
+ /* Select channel index in the mask and enable freq measurement */
+ dpll_meas_ref_freq_ctrl =
+ DPLL_MEAS_REF_FREQ_CTRL_EN |
+ FIELD_PREP(DPLL_MEAS_REF_FREQ_CTRL_IDX, zldpll->id);
+
+ rc = zl3073x_write_dpll_meas_ref_freq_ctrl(zldev,
+ dpll_meas_ref_freq_ctrl);
+ if (rc)
+ return rc;
+
+ /* Set reference mask
+ * REF0P,REF0N..REF3P,REF3N are set in ref_freq_meas_mask_3_0 register
+ * REF4P and REF4N are set in ref_freq_meas_mask_4 register
+ */
+ if (ref_id < 8) {
+ ref_mask = REF_FREQ_MEAS_MASK_3_0(ref_id);
+ rc = zl3073x_write_ref_freq_meas_mask_3_0(zldev, ref_mask);
+ } else {
+ ref_mask = REF_FREQ_MEAS_MASK_4(ref_id);
+ rc = zl3073x_write_ref_freq_meas_mask_4(zldev, ref_mask);
+ }
+ if (rc)
+ return rc;
+
+ /* Request a reading of the frequency offset between the DPLL and
+ * the reference
+ */
+ ref_freq_meas_ctrl = REF_FREQ_MEAS_CTRL_LATCH_DPLL_FREQ_OFF;
+ rc = zl3073x_write_ref_freq_meas_ctrl(zldev, ref_freq_meas_ctrl);
+ if (rc)
+ return rc;
+
+ /* Wait for the command to actually finish */
+ rc = zl3073x_wait_clear_bits(zldev, ref_freq_meas_ctrl,
+ REF_FREQ_MEAS_CTRL_LATCH);
+ if (rc)
+ return rc;
+
+ /* Read the frequency offset between DPLL and reference */
+ rc = zl3073x_read_ref_freq(zldev, ref_id, &freq_offset);
+ if (rc)
+ return rc;
+
+ *ffo = freq_offset;
+
+ return rc;
+}
+
static int
zl3073x_dpll_input_pin_frequency_get(const struct dpll_pin *dpll_pin,
void *pin_priv,
@@ -1778,6 +1872,7 @@ 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,
+ .ffo_get = zl3073x_dpll_input_pin_ffo_get,
.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,
@@ -2484,9 +2579,9 @@ zl3073x_dpll_periodic_work(struct kthread_work *work)
* are constant.
*/
for (i = 0; i < ZL3073X_NUM_INPUT_PINS; i++) {
+ s64 freq_offset, phase_offset;
struct zl3073x_dpll_pin *pin;
enum dpll_pin_state state;
- s64 phase_offset;
bool pin_changed;
/* Input pins starts are stored after output pins */
@@ -2513,6 +2608,12 @@ zl3073x_dpll_periodic_work(struct kthread_work *work)
if (rc)
goto out;
+ rc = zl3073x_dpll_input_pin_ffo_get(pin->dpll_pin, pin,
+ zldpll->dpll_dev, zldpll,
+ &freq_offset, NULL);
+ if (rc)
+ goto out;
+
if (state != pin->pin_state) {
dev_dbg(zldev->dev,
"INPUT%u state changed to %u\n",
@@ -2527,6 +2628,13 @@ zl3073x_dpll_periodic_work(struct kthread_work *work)
pin->phase_offset = phase_offset;
pin_changed = true;
}
+ if (freq_offset != pin->freq_offset) {
+ dev_dbg(zldev->dev,
+ "INPUT%u frequency offset changed to %llu\n",
+ pin->index, freq_offset);
+ pin->freq_offset = freq_offset;
+ pin_changed = true;
+ }
if (pin_changed)
dpll_pin_change_ntf(pin->dpll_pin);