Skip to content

Commit 904c99e

Browse files
iveceraPaolo Abeni
authored andcommitted
dpll: zl3073x: Add support to get fractional frequency offset
Adds support to get fractional frequency offset for input pins. Implement the appropriate callback and function that periodicaly performs reference frequency measurement and notifies DPLL core about changes. 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 6287262 commit 904c99e

File tree

4 files changed

+168
-2
lines changed

4 files changed

+168
-2
lines changed

drivers/dpll/zl3073x/core.c

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -720,6 +720,66 @@ int zl3073x_ref_phase_offsets_update(struct zl3073x_dev *zldev, int channel)
720720
ZL_REF_PHASE_ERR_READ_RQST_RD);
721721
}
722722

723+
/**
724+
* zl3073x_ref_ffo_update - update reference fractional frequency offsets
725+
* @zldev: pointer to zl3073x_dev structure
726+
*
727+
* The function asks device to update fractional frequency offsets latch
728+
* registers the latest measured values, reads and stores them into
729+
*
730+
* Return: 0 on success, <0 on error
731+
*/
732+
static int
733+
zl3073x_ref_ffo_update(struct zl3073x_dev *zldev)
734+
{
735+
int i, rc;
736+
737+
/* Per datasheet we have to wait for 'ref_freq_meas_ctrl' to be zero
738+
* to ensure that the measured data are coherent.
739+
*/
740+
rc = zl3073x_poll_zero_u8(zldev, ZL_REG_REF_FREQ_MEAS_CTRL,
741+
ZL_REF_FREQ_MEAS_CTRL);
742+
if (rc)
743+
return rc;
744+
745+
/* Select all references for measurement */
746+
rc = zl3073x_write_u8(zldev, ZL_REG_REF_FREQ_MEAS_MASK_3_0,
747+
GENMASK(7, 0)); /* REF0P..REF3N */
748+
if (rc)
749+
return rc;
750+
rc = zl3073x_write_u8(zldev, ZL_REG_REF_FREQ_MEAS_MASK_4,
751+
GENMASK(1, 0)); /* REF4P..REF4N */
752+
if (rc)
753+
return rc;
754+
755+
/* Request frequency offset measurement */
756+
rc = zl3073x_write_u8(zldev, ZL_REG_REF_FREQ_MEAS_CTRL,
757+
ZL_REF_FREQ_MEAS_CTRL_REF_FREQ_OFF);
758+
if (rc)
759+
return rc;
760+
761+
/* Wait for finish */
762+
rc = zl3073x_poll_zero_u8(zldev, ZL_REG_REF_FREQ_MEAS_CTRL,
763+
ZL_REF_FREQ_MEAS_CTRL);
764+
if (rc)
765+
return rc;
766+
767+
/* Read DPLL-to-REFx frequency offset measurements */
768+
for (i = 0; i < ZL3073X_NUM_REFS; i++) {
769+
s32 value;
770+
771+
/* Read value stored in units of 2^-32 signed */
772+
rc = zl3073x_read_u32(zldev, ZL_REG_REF_FREQ(i), &value);
773+
if (rc)
774+
return rc;
775+
776+
/* Convert to ppm -> ffo = (10^6 * value) / 2^32 */
777+
zldev->ref[i].ffo = mul_s64_u64_shr(value, 1000000, 32);
778+
}
779+
780+
return 0;
781+
}
782+
723783
static void
724784
zl3073x_dev_periodic_work(struct kthread_work *work)
725785
{
@@ -734,6 +794,13 @@ zl3073x_dev_periodic_work(struct kthread_work *work)
734794
dev_warn(zldev->dev, "Failed to update phase offsets: %pe\n",
735795
ERR_PTR(rc));
736796

797+
/* Update references' fractional frequency offsets */
798+
rc = zl3073x_ref_ffo_update(zldev);
799+
if (rc)
800+
dev_warn(zldev->dev,
801+
"Failed to update fractional frequency offsets: %pe\n",
802+
ERR_PTR(rc));
803+
737804
list_for_each_entry(zldpll, &zldev->dplls, list)
738805
zl3073x_dpll_changes_check(zldpll);
739806

drivers/dpll/zl3073x/core.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,12 @@ struct zl3073x_dpll;
3030
* struct zl3073x_ref - input reference invariant info
3131
* @enabled: input reference is enabled or disabled
3232
* @diff: true if input reference is differential
33+
* @ffo: current fractional frequency offset
3334
*/
3435
struct zl3073x_ref {
3536
bool enabled;
3637
bool diff;
38+
s64 ffo;
3739
};
3840

3941
/**
@@ -170,6 +172,19 @@ zl3073x_output_pin_out_get(u8 id)
170172
return id / 2;
171173
}
172174

175+
/**
176+
* zl3073x_ref_ffo_get - get current fractional frequency offset
177+
* @zldev: pointer to zl3073x device
178+
* @index: input reference index
179+
*
180+
* Return: the latest measured fractional frequency offset
181+
*/
182+
static inline s64
183+
zl3073x_ref_ffo_get(struct zl3073x_dev *zldev, u8 index)
184+
{
185+
return zldev->ref[index].ffo;
186+
}
187+
173188
/**
174189
* zl3073x_ref_is_diff - check if the given input reference is differential
175190
* @zldev: pointer to zl3073x device

drivers/dpll/zl3073x/dpll.c

Lines changed: 67 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
* @esync_control: embedded sync is controllable
3838
* @pin_state: last saved pin state
3939
* @phase_offset: last saved pin phase offset
40+
* @freq_offset: last saved fractional frequency offset
4041
*/
4142
struct zl3073x_dpll_pin {
4243
struct list_head list;
@@ -50,6 +51,7 @@ struct zl3073x_dpll_pin {
5051
bool esync_control;
5152
enum dpll_pin_state pin_state;
5253
s64 phase_offset;
54+
s64 freq_offset;
5355
};
5456

5557
/*
@@ -270,6 +272,18 @@ zl3073x_dpll_input_pin_esync_set(const struct dpll_pin *dpll_pin,
270272
ZL_REG_REF_MB_MASK, BIT(ref));
271273
}
272274

275+
static int
276+
zl3073x_dpll_input_pin_ffo_get(const struct dpll_pin *dpll_pin, void *pin_priv,
277+
const struct dpll_device *dpll, void *dpll_priv,
278+
s64 *ffo, struct netlink_ext_ack *extack)
279+
{
280+
struct zl3073x_dpll_pin *pin = pin_priv;
281+
282+
*ffo = pin->freq_offset;
283+
284+
return 0;
285+
}
286+
273287
static int
274288
zl3073x_dpll_input_pin_frequency_get(const struct dpll_pin *dpll_pin,
275289
void *pin_priv,
@@ -1595,6 +1609,7 @@ static const struct dpll_pin_ops zl3073x_dpll_input_pin_ops = {
15951609
.direction_get = zl3073x_dpll_pin_direction_get,
15961610
.esync_get = zl3073x_dpll_input_pin_esync_get,
15971611
.esync_set = zl3073x_dpll_input_pin_esync_set,
1612+
.ffo_get = zl3073x_dpll_input_pin_ffo_get,
15981613
.frequency_get = zl3073x_dpll_input_pin_frequency_get,
15991614
.frequency_set = zl3073x_dpll_input_pin_frequency_set,
16001615
.phase_offset_get = zl3073x_dpll_input_pin_phase_offset_get,
@@ -2050,6 +2065,52 @@ zl3073x_dpll_pin_phase_offset_check(struct zl3073x_dpll_pin *pin)
20502065
return false;
20512066
}
20522067

2068+
/**
2069+
* zl3073x_dpll_pin_ffo_check - check for pin fractional frequency offset change
2070+
* @pin: pin to check
2071+
*
2072+
* Check for the given pin's fractional frequency change.
2073+
*
2074+
* Return: true on fractional frequency offset change, false otherwise
2075+
*/
2076+
static bool
2077+
zl3073x_dpll_pin_ffo_check(struct zl3073x_dpll_pin *pin)
2078+
{
2079+
struct zl3073x_dpll *zldpll = pin->dpll;
2080+
struct zl3073x_dev *zldev = zldpll->dev;
2081+
u8 ref, status;
2082+
s64 ffo;
2083+
int rc;
2084+
2085+
/* Get reference monitor status */
2086+
ref = zl3073x_input_pin_ref_get(pin->id);
2087+
rc = zl3073x_read_u8(zldev, ZL_REG_REF_MON_STATUS(ref), &status);
2088+
if (rc) {
2089+
dev_err(zldev->dev, "Failed to read %s refmon status: %pe\n",
2090+
pin->label, ERR_PTR(rc));
2091+
2092+
return false;
2093+
}
2094+
2095+
/* Do not report ffo changes if the reference monitor report errors */
2096+
if (status != ZL_REF_MON_STATUS_OK)
2097+
return false;
2098+
2099+
/* Get the latest measured ref's ffo */
2100+
ffo = zl3073x_ref_ffo_get(zldev, ref);
2101+
2102+
/* Compare with previous value */
2103+
if (pin->freq_offset != ffo) {
2104+
dev_dbg(zldev->dev, "%s freq offset changed: %lld -> %lld\n",
2105+
pin->label, pin->freq_offset, ffo);
2106+
pin->freq_offset = ffo;
2107+
2108+
return true;
2109+
}
2110+
2111+
return false;
2112+
}
2113+
20532114
/**
20542115
* zl3073x_dpll_changes_check - check for changes and send notifications
20552116
* @zldpll: pointer to zl3073x_dpll structure
@@ -2130,11 +2191,15 @@ zl3073x_dpll_changes_check(struct zl3073x_dpll *zldpll)
21302191
pin_changed = true;
21312192
}
21322193

2133-
/* Check for phase offset change once per second */
2134-
if (zldpll->check_count % 2 == 0)
2194+
/* Check for phase offset and ffo change once per second */
2195+
if (zldpll->check_count % 2 == 0) {
21352196
if (zl3073x_dpll_pin_phase_offset_check(pin))
21362197
pin_changed = true;
21372198

2199+
if (zl3073x_dpll_pin_ffo_check(pin))
2200+
pin_changed = true;
2201+
}
2202+
21382203
if (pin_changed)
21392204
dpll_pin_change_ntf(pin->dpll_pin);
21402205
}

drivers/dpll/zl3073x/regs.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,13 +94,32 @@
9494
#define ZL_DPLL_REFSEL_STATUS_STATE GENMASK(6, 4)
9595
#define ZL_DPLL_REFSEL_STATUS_STATE_LOCK 4
9696

97+
#define ZL_REG_REF_FREQ(_idx) \
98+
ZL_REG_IDX(_idx, 2, 0x44, 4, ZL3073X_NUM_REFS, 4)
99+
97100
/**********************
98101
* Register Page 4, Ref
99102
**********************/
100103

101104
#define ZL_REG_REF_PHASE_ERR_READ_RQST ZL_REG(4, 0x0f, 1)
102105
#define ZL_REF_PHASE_ERR_READ_RQST_RD BIT(0)
103106

107+
#define ZL_REG_REF_FREQ_MEAS_CTRL ZL_REG(4, 0x1c, 1)
108+
#define ZL_REF_FREQ_MEAS_CTRL GENMASK(1, 0)
109+
#define ZL_REF_FREQ_MEAS_CTRL_REF_FREQ 1
110+
#define ZL_REF_FREQ_MEAS_CTRL_REF_FREQ_OFF 2
111+
#define ZL_REF_FREQ_MEAS_CTRL_DPLL_FREQ_OFF 3
112+
113+
#define ZL_REG_REF_FREQ_MEAS_MASK_3_0 ZL_REG(4, 0x1d, 1)
114+
#define ZL_REF_FREQ_MEAS_MASK_3_0(_ref) BIT(_ref)
115+
116+
#define ZL_REG_REF_FREQ_MEAS_MASK_4 ZL_REG(4, 0x1e, 1)
117+
#define ZL_REF_FREQ_MEAS_MASK_4(_ref) BIT((_ref) - 8)
118+
119+
#define ZL_REG_DPLL_MEAS_REF_FREQ_CTRL ZL_REG(4, 0x1f, 1)
120+
#define ZL_DPLL_MEAS_REF_FREQ_CTRL_EN BIT(0)
121+
#define ZL_DPLL_MEAS_REF_FREQ_CTRL_IDX GENMASK(6, 4)
122+
104123
#define ZL_REG_REF_PHASE(_idx) \
105124
ZL_REG_IDX(_idx, 4, 0x20, 6, ZL3073X_NUM_REFS, 6)
106125

0 commit comments

Comments
 (0)