Commit e0c7e315 authored by Paolo Abeni's avatar Paolo Abeni
Browse files

Merge branch 'dpll-zl3073x-add-misc-features'

Ivan Vecera says:

====================
dpll: zl3073x: Add misc features

Add several new features missing in initial submission:

* Embedded sync for both pin types
* Phase offset reporting for connected input pin
* Selectable phase offset monitoring (aka all inputs phase monitor)
* Phase adjustments for both pin types
* Fractional frequency offset reporting for input pins

Everything was tested on Microchip EVB-LAN9668 EDS2 development board.
====================

Link: https://patch.msgid.link/20250715144633.149156-1-ivecera@redhat.com


Signed-off-by: default avatarPaolo Abeni <pabeni@redhat.com>
parents 44eb62e1 904c99ea
Loading
Loading
Loading
Loading
+171 −0
Original line number Diff line number Diff line
@@ -669,12 +669,137 @@ zl3073x_dev_state_fetch(struct zl3073x_dev *zldev)
	return rc;
}

/**
 * zl3073x_ref_phase_offsets_update - update reference phase offsets
 * @zldev: pointer to zl3073x_dev structure
 * @channel: DPLL channel number or -1
 *
 * 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 values
 *    between selected DPLL channel and all input references.
 *
 * If the caller is interested in 2) then it has to pass DPLL channel number
 * in @channel parameter. If it is interested only in 1) then it should pass
 * @channel parameter with value of -1.
 *
 * Return: 0 on success, <0 on error
 */
int zl3073x_ref_phase_offsets_update(struct zl3073x_dev *zldev, int channel)
{
	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 = zl3073x_poll_zero_u8(zldev, ZL_REG_REF_PHASE_ERR_READ_RQST,
				  ZL_REF_PHASE_ERR_READ_RQST_RD);
	if (rc)
		return rc;

	/* Select DPLL channel if it is specified */
	if (channel != -1) {
		rc = zl3073x_write_u8(zldev, ZL_REG_DPLL_MEAS_IDX, channel);
		if (rc)
			return rc;
	}

	/* Request to update phase offsets measurement values */
	rc = 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);
}

/**
 * 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 = 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 = zl3073x_write_u8(zldev, ZL_REG_REF_FREQ_MEAS_MASK_3_0,
			      GENMASK(7, 0)); /* REF0P..REF3N */
	if (rc)
		return rc;
	rc = 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 = 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 = 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 = 0; i < ZL3073X_NUM_REFS; i++) {
		s32 value;

		/* Read value stored in units of 2^-32 signed */
		rc = zl3073x_read_u32(zldev, ZL_REG_REF_FREQ(i), &value);
		if (rc)
			return rc;

		/* Convert to ppm -> ffo = (10^6 * value) / 2^32 */
		zldev->ref[i].ffo = mul_s64_u64_shr(value, 1000000, 32);
	}

	return 0;
}

static void
zl3073x_dev_periodic_work(struct kthread_work *work)
{
	struct zl3073x_dev *zldev = container_of(work, struct zl3073x_dev,
						 work.work);
	struct zl3073x_dpll *zldpll;
	int rc;

	/* Update DPLL-to-connected-ref phase offsets registers */
	rc = zl3073x_ref_phase_offsets_update(zldev, -1);
	if (rc)
		dev_warn(zldev->dev, "Failed to update phase offsets: %pe\n",
			 ERR_PTR(rc));

	/* Update references' fractional frequency offsets */
	rc = 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);
@@ -767,6 +892,46 @@ zl3073x_devm_dpll_init(struct zl3073x_dev *zldev, u8 num_dplls)
	return rc;
}

/**
 * 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 = zl3073x_read_u8(zldev, ZL_REG_DPLL_MEAS_CTRL, &dpll_meas_ctrl);
	if (rc)
		return rc;

	/* Setup phase measurement averaging factor */
	dpll_meas_ctrl &= ~ZL_DPLL_MEAS_CTRL_AVG_FACTOR;
	dpll_meas_ctrl |= FIELD_PREP(ZL_DPLL_MEAS_CTRL_AVG_FACTOR, 3);

	/* Enable DPLL measurement block */
	dpll_meas_ctrl |= ZL_DPLL_MEAS_CTRL_EN;

	/* Update phase measurement control register */
	rc = 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 = 0, mask = 0; i < num_channels; i++)
		mask |= 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 +1004,12 @@ int zl3073x_dev_probe(struct zl3073x_dev *zldev,
	if (rc)
		return rc;

	/* Setup phase offset measurement block */
	rc = 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 = zl3073x_devm_dpll_init(zldev, chip_info->num_channels);
	if (rc)
+16 −0
Original line number Diff line number Diff line
@@ -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;
};

/**
@@ -130,6 +132,7 @@ int zl3073x_write_u48(struct zl3073x_dev *zldev, unsigned int reg, u64 val);
 *****************/

int zl3073x_ref_freq_factorize(u32 freq, u16 *base, u16 *mult);
int zl3073x_ref_phase_offsets_update(struct zl3073x_dev *zldev, int channel);

static inline bool
zl3073x_is_n_pin(u8 id)
@@ -169,6 +172,19 @@ zl3073x_output_pin_out_get(u8 id)
	return id / 2;
}

/**
 * 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
+816 −2

File changed.

Preview size limit exceeded, changes collapsed.

+4 −0
Original line number Diff line number Diff line
@@ -15,6 +15,8 @@
 * @id: DPLL index
 * @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
@@ -25,6 +27,8 @@ struct zl3073x_dpll {
	u8				id;
	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;
+55 −0
Original line number Diff line number Diff line
@@ -94,6 +94,35 @@
#define ZL_DPLL_REFSEL_STATUS_STATE		GENMASK(6, 4)
#define ZL_DPLL_REFSEL_STATUS_STATE_LOCK	4

#define ZL_REG_REF_FREQ(_idx)						\
	ZL_REG_IDX(_idx, 2, 0x44, 4, ZL3073X_NUM_REFS, 4)

/**********************
 * 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)

#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)

/***********************
 * Register Page 5, DPLL
 ***********************/
@@ -108,6 +137,18 @@
#define ZL_DPLL_MODE_REFSEL_MODE_NCO		4
#define ZL_DPLL_MODE_REFSEL_REF			GENMASK(7, 4)

#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_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)

#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
 ***********************************/
@@ -146,6 +187,16 @@
#define ZL_REF_CONFIG_ENABLE			BIT(0)
#define ZL_REF_CONFIG_DIFF_EN			BIT(2)

#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
#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 +239,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
@@ -204,5 +258,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)

#endif /* _ZL3073X_REGS_H */