Skip to content

Commit b7dbde2

Browse files
iveceraPaolo Abeni
authored andcommitted
dpll: zl3073x: Implement phase offset monitor feature
Implement phase offset monitor feature to allow a user to monitor phase offsets across all available inputs. The device firmware periodically performs phase offsets measurements for all available DPLL channels and input references. The driver can ask the firmware to fill appropriate latch registers with measured values. There are 2 sets of latch registers for phase offsets reporting: 1) DPLL-to-connected-ref: up to 5 registers that contain values for phase offset between particular DPLL channel and its connected input reference. 2) selected-DPLL-to-ref: 10 registers that contain values for phase offsets between selected DPLL channel and all available input references. Both are filled with single read request so the driver can read DPLL-to-connected-ref phase offset for all DPLL channels at once. This was implemented in the previous patch. To read selected-DPLL-to-ref registers for all DPLLs a separate read request has to be sent to device firmware for each DPLL channel. To implement phase offset monitor feature: * Extend zl3073x_ref_phase_offsets_update() to select given DPLL channel in phase offset read request. The caller can set channel==-1 if it will not read Type2 registers. * Use this extended function to update phase offset latch registers during zl3073x_dpll_changes_check() call if phase monitor is enabled * Extend zl3073x_dpll_pin_phase_offset_check() to check phase offset changes for all available input references * Extend zl3073x_dpll_input_pin_phase_offset_get() to report phase offset values for all available input references * Implement phase offset monitor callbacks to enable/disable this feature Reviewed-by: Jiri Pirko <[email protected]> Tested-by: Prathosh Satish <[email protected]> Co-developed-by: Prathosh Satish <[email protected]> Signed-off-by: Prathosh Satish <[email protected]> Signed-off-by: Ivan Vecera <[email protected]> Link: https://patch.msgid.link/[email protected] Signed-off-by: Paolo Abeni <[email protected]>
1 parent 86ed4cd commit b7dbde2

File tree

5 files changed

+148
-14
lines changed

5 files changed

+148
-14
lines changed

drivers/dpll/zl3073x/core.c

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -672,14 +672,25 @@ zl3073x_dev_state_fetch(struct zl3073x_dev *zldev)
672672
/**
673673
* zl3073x_ref_phase_offsets_update - update reference phase offsets
674674
* @zldev: pointer to zl3073x_dev structure
675+
* @channel: DPLL channel number or -1
675676
*
676-
* Ask device to update phase offsets latch registers with the latest
677-
* measured values.
677+
* The function asks device to update phase offsets latch registers with
678+
* the latest measured values. There are 2 sets of latch registers:
679+
*
680+
* 1) Up to 5 DPLL-to-connected-ref registers that contain phase offset
681+
* values between particular DPLL channel and its *connected* input
682+
* reference.
683+
*
684+
* 2) 10 selected-DPLL-to-all-ref registers that contain phase offset values
685+
* between selected DPLL channel and all input references.
686+
*
687+
* If the caller is interested in 2) then it has to pass DPLL channel number
688+
* in @channel parameter. If it is interested only in 1) then it should pass
689+
* @channel parameter with value of -1.
678690
*
679691
* Return: 0 on success, <0 on error
680692
*/
681-
static int
682-
zl3073x_ref_phase_offsets_update(struct zl3073x_dev *zldev)
693+
int zl3073x_ref_phase_offsets_update(struct zl3073x_dev *zldev, int channel)
683694
{
684695
int rc;
685696

@@ -691,6 +702,13 @@ zl3073x_ref_phase_offsets_update(struct zl3073x_dev *zldev)
691702
if (rc)
692703
return rc;
693704

705+
/* Select DPLL channel if it is specified */
706+
if (channel != -1) {
707+
rc = zl3073x_write_u8(zldev, ZL_REG_DPLL_MEAS_IDX, channel);
708+
if (rc)
709+
return rc;
710+
}
711+
694712
/* Request to update phase offsets measurement values */
695713
rc = zl3073x_write_u8(zldev, ZL_REG_REF_PHASE_ERR_READ_RQST,
696714
ZL_REF_PHASE_ERR_READ_RQST_RD);
@@ -711,7 +729,7 @@ zl3073x_dev_periodic_work(struct kthread_work *work)
711729
int rc;
712730

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

drivers/dpll/zl3073x/core.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ int zl3073x_write_u48(struct zl3073x_dev *zldev, unsigned int reg, u64 val);
130130
*****************/
131131

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

134135
static inline bool
135136
zl3073x_is_n_pin(u8 id)

drivers/dpll/zl3073x/dpll.c

Lines changed: 116 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -509,16 +509,19 @@ zl3073x_dpll_input_pin_phase_offset_get(const struct dpll_pin *dpll_pin,
509509
struct zl3073x_dev *zldev = zldpll->dev;
510510
struct zl3073x_dpll_pin *pin = pin_priv;
511511
u8 conn_ref, ref, ref_status;
512+
s64 ref_phase;
512513
int rc;
513514

514515
/* Get currently connected reference */
515516
rc = zl3073x_dpll_connected_ref_get(zldpll, &conn_ref);
516517
if (rc)
517518
return rc;
518519

519-
/* Report phase offset only for currently connected pin */
520+
/* Report phase offset only for currently connected pin if the phase
521+
* monitor feature is disabled.
522+
*/
520523
ref = zl3073x_input_pin_ref_get(pin->id);
521-
if (ref != conn_ref) {
524+
if (!zldpll->phase_monitor && ref != conn_ref) {
522525
*phase_offset = 0;
523526

524527
return 0;
@@ -536,8 +539,37 @@ zl3073x_dpll_input_pin_phase_offset_get(const struct dpll_pin *dpll_pin,
536539
return 0;
537540
}
538541

539-
/* Report the latest measured phase offset for the connected ref */
540-
*phase_offset = pin->phase_offset * DPLL_PHASE_OFFSET_DIVIDER;
542+
ref_phase = pin->phase_offset;
543+
544+
/* The DPLL being locked to a higher freq than the current ref
545+
* the phase offset is modded to the period of the signal
546+
* the dpll is locked to.
547+
*/
548+
if (ZL3073X_DPLL_REF_IS_VALID(conn_ref) && conn_ref != ref) {
549+
u32 conn_freq, ref_freq;
550+
551+
/* Get frequency of connected ref */
552+
rc = zl3073x_dpll_input_ref_frequency_get(zldpll, conn_ref,
553+
&conn_freq);
554+
if (rc)
555+
return rc;
556+
557+
/* Get frequency of given ref */
558+
rc = zl3073x_dpll_input_ref_frequency_get(zldpll, ref,
559+
&ref_freq);
560+
if (rc)
561+
return rc;
562+
563+
if (conn_freq > ref_freq) {
564+
s64 conn_period, div_factor;
565+
566+
conn_period = div_s64(PSEC_PER_SEC, conn_freq);
567+
div_factor = div64_s64(ref_phase, conn_period);
568+
ref_phase -= conn_period * div_factor;
569+
}
570+
}
571+
572+
*phase_offset = ref_phase * DPLL_PHASE_OFFSET_DIVIDER;
541573

542574
return rc;
543575
}
@@ -1343,6 +1375,35 @@ zl3073x_dpll_mode_get(const struct dpll_device *dpll, void *dpll_priv,
13431375
return 0;
13441376
}
13451377

1378+
static int
1379+
zl3073x_dpll_phase_offset_monitor_get(const struct dpll_device *dpll,
1380+
void *dpll_priv,
1381+
enum dpll_feature_state *state,
1382+
struct netlink_ext_ack *extack)
1383+
{
1384+
struct zl3073x_dpll *zldpll = dpll_priv;
1385+
1386+
if (zldpll->phase_monitor)
1387+
*state = DPLL_FEATURE_STATE_ENABLE;
1388+
else
1389+
*state = DPLL_FEATURE_STATE_DISABLE;
1390+
1391+
return 0;
1392+
}
1393+
1394+
static int
1395+
zl3073x_dpll_phase_offset_monitor_set(const struct dpll_device *dpll,
1396+
void *dpll_priv,
1397+
enum dpll_feature_state state,
1398+
struct netlink_ext_ack *extack)
1399+
{
1400+
struct zl3073x_dpll *zldpll = dpll_priv;
1401+
1402+
zldpll->phase_monitor = (state == DPLL_FEATURE_STATE_ENABLE);
1403+
1404+
return 0;
1405+
}
1406+
13461407
static const struct dpll_pin_ops zl3073x_dpll_input_pin_ops = {
13471408
.direction_get = zl3073x_dpll_pin_direction_get,
13481409
.esync_get = zl3073x_dpll_input_pin_esync_get,
@@ -1368,6 +1429,8 @@ static const struct dpll_pin_ops zl3073x_dpll_output_pin_ops = {
13681429
static const struct dpll_device_ops zl3073x_dpll_device_ops = {
13691430
.lock_status_get = zl3073x_dpll_lock_status_get,
13701431
.mode_get = zl3073x_dpll_mode_get,
1432+
.phase_offset_monitor_get = zl3073x_dpll_phase_offset_monitor_get,
1433+
.phase_offset_monitor_set = zl3073x_dpll_phase_offset_monitor_set,
13711434
};
13721435

13731436
/**
@@ -1733,16 +1796,47 @@ zl3073x_dpll_pin_phase_offset_check(struct zl3073x_dpll_pin *pin)
17331796
{
17341797
struct zl3073x_dpll *zldpll = pin->dpll;
17351798
struct zl3073x_dev *zldev = zldpll->dev;
1799+
unsigned int reg;
17361800
s64 phase_offset;
1801+
u8 ref;
17371802
int rc;
17381803

1739-
/* Do not check phase offset if the pin is not connected one */
1740-
if (pin->pin_state != DPLL_PIN_STATE_CONNECTED)
1804+
ref = zl3073x_input_pin_ref_get(pin->id);
1805+
1806+
/* Select register to read phase offset value depending on pin and
1807+
* phase monitor state:
1808+
* 1) For connected pin use dpll_phase_err_data register
1809+
* 2) For other pins use appropriate ref_phase register if the phase
1810+
* monitor feature is enabled and reference monitor does not
1811+
* report signal errors for given input pin
1812+
*/
1813+
if (pin->pin_state == DPLL_PIN_STATE_CONNECTED) {
1814+
reg = ZL_REG_DPLL_PHASE_ERR_DATA(zldpll->id);
1815+
} else if (zldpll->phase_monitor) {
1816+
u8 status;
1817+
1818+
/* Get reference monitor status */
1819+
rc = zl3073x_read_u8(zldev, ZL_REG_REF_MON_STATUS(ref),
1820+
&status);
1821+
if (rc) {
1822+
dev_err(zldev->dev,
1823+
"Failed to read %s refmon status: %pe\n",
1824+
pin->label, ERR_PTR(rc));
1825+
1826+
return false;
1827+
}
1828+
1829+
if (status != ZL_REF_MON_STATUS_OK)
1830+
return false;
1831+
1832+
reg = ZL_REG_REF_PHASE(ref);
1833+
} else {
1834+
/* The pin is not connected or phase monitor disabled */
17411835
return false;
1836+
}
17421837

1743-
/* Read DPLL-to-connected-ref phase offset measurement value */
1744-
rc = zl3073x_read_u48(zldev, ZL_REG_DPLL_PHASE_ERR_DATA(zldpll->id),
1745-
&phase_offset);
1838+
/* Read measured phase offset value */
1839+
rc = zl3073x_read_u48(zldev, reg, &phase_offset);
17461840
if (rc) {
17471841
dev_err(zldev->dev, "Failed to read ref phase offset: %pe\n",
17481842
ERR_PTR(rc));
@@ -1807,6 +1901,19 @@ zl3073x_dpll_changes_check(struct zl3073x_dpll *zldpll)
18071901
zldpll->refsel_mode != ZL_DPLL_MODE_REFSEL_MODE_REFLOCK)
18081902
return;
18091903

1904+
/* Update phase offset latch registers for this DPLL if the phase
1905+
* offset monitor feature is enabled.
1906+
*/
1907+
if (zldpll->phase_monitor) {
1908+
rc = zl3073x_ref_phase_offsets_update(zldev, zldpll->id);
1909+
if (rc) {
1910+
dev_err(zldev->dev,
1911+
"Failed to update phase offsets: %pe\n",
1912+
ERR_PTR(rc));
1913+
return;
1914+
}
1915+
}
1916+
18101917
list_for_each_entry(pin, &zldpll->pins, list) {
18111918
enum dpll_pin_state state;
18121919
bool pin_changed = false;

drivers/dpll/zl3073x/dpll.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
* @refsel_mode: reference selection mode
1717
* @forced_ref: selected reference in forced reference lock mode
1818
* @check_count: periodic check counter
19+
* @phase_monitor: is phase offset monitor enabled
1920
* @dpll_dev: pointer to registered DPLL device
2021
* @lock_status: last saved DPLL lock status
2122
* @pins: list of pins
@@ -27,6 +28,7 @@ struct zl3073x_dpll {
2728
u8 refsel_mode;
2829
u8 forced_ref;
2930
u8 check_count;
31+
bool phase_monitor;
3032
struct dpll_device *dpll_dev;
3133
enum dpll_lock_status lock_status;
3234
struct list_head pins;

drivers/dpll/zl3073x/regs.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,9 @@
101101
#define ZL_REG_REF_PHASE_ERR_READ_RQST ZL_REG(4, 0x0f, 1)
102102
#define ZL_REF_PHASE_ERR_READ_RQST_RD BIT(0)
103103

104+
#define ZL_REG_REF_PHASE(_idx) \
105+
ZL_REG_IDX(_idx, 4, 0x20, 6, ZL3073X_NUM_REFS, 6)
106+
104107
/***********************
105108
* Register Page 5, DPLL
106109
***********************/
@@ -119,6 +122,9 @@
119122
#define ZL_DPLL_MEAS_CTRL_EN BIT(0)
120123
#define ZL_DPLL_MEAS_CTRL_AVG_FACTOR GENMASK(7, 4)
121124

125+
#define ZL_REG_DPLL_MEAS_IDX ZL_REG(5, 0x51, 1)
126+
#define ZL_DPLL_MEAS_IDX GENMASK(2, 0)
127+
122128
#define ZL_REG_DPLL_PHASE_ERR_READ_MASK ZL_REG(5, 0x54, 1)
123129

124130
#define ZL_REG_DPLL_PHASE_ERR_DATA(_idx) \

0 commit comments

Comments
 (0)