dpll: zl3073x: Cache all output properties in zl3073x_out
Expand the zl3073x_out structure to cache all output-related hardware registers, including divisors, widths, embedded-sync parameters and phase compensation. Modify zl3073x_out_state_fetch() to read and populate all these new fields at once, including zero-divisor checks. Refactor all dpll "getter" functions in dpll.c to read from this new cached state instead of performing direct register access. Introduce a new function, zl3073x_out_state_set(), to handle writing changes back to the hardware. This function compares the provided state with the current cached state and writes *only* the modified register values via a single mailbox sequence before updating the local cache. Refactor all dpll "setter" functions to modify a local copy of the output state and then call zl3073x_out_state_set() to commit the changes. This change centralizes all output-related register I/O into out.c, significantly reduces bus traffic, and simplifies the logic in dpll.c. Reviewed-by: Petr Oros <poros@redhat.com> Tested-by: Prathosh Satish <Prathosh.Satish@microchip.com> Signed-off-by: Ivan Vecera <ivecera@redhat.com> Link: https://patch.msgid.link/20251113074105.141379-6-ivecera@redhat.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
committed by
Jakub Kicinski
parent
5bc02b190a
commit
5fb9b0d411
@@ -770,21 +770,19 @@ zl3073x_dpll_output_pin_esync_get(const struct dpll_pin *dpll_pin,
|
||||
struct zl3073x_dpll *zldpll = dpll_priv;
|
||||
struct zl3073x_dev *zldev = zldpll->dev;
|
||||
struct zl3073x_dpll_pin *pin = pin_priv;
|
||||
struct device *dev = zldev->dev;
|
||||
u32 esync_period, esync_width;
|
||||
u8 clock_type, synth;
|
||||
u8 out, output_mode;
|
||||
u32 output_div;
|
||||
const struct zl3073x_synth *synth;
|
||||
const struct zl3073x_out *out;
|
||||
u8 clock_type, out_id;
|
||||
u32 synth_freq;
|
||||
int rc;
|
||||
|
||||
out = zl3073x_output_pin_out_get(pin->id);
|
||||
out_id = zl3073x_output_pin_out_get(pin->id);
|
||||
out = zl3073x_out_state_get(zldev, out_id);
|
||||
|
||||
/* If N-division is enabled, esync is not supported. The register used
|
||||
* for N-division is also used for the esync divider so both cannot
|
||||
* be used.
|
||||
*/
|
||||
switch (zl3073x_dev_out_signal_format_get(zldev, out)) {
|
||||
switch (zl3073x_out_signal_format_get(out)) {
|
||||
case ZL_OUTPUT_MODE_SIGNAL_FORMAT_2_NDIV:
|
||||
case ZL_OUTPUT_MODE_SIGNAL_FORMAT_2_NDIV_INV:
|
||||
return -EOPNOTSUPP;
|
||||
@@ -792,38 +790,11 @@ zl3073x_dpll_output_pin_esync_get(const struct dpll_pin *dpll_pin,
|
||||
break;
|
||||
}
|
||||
|
||||
guard(mutex)(&zldev->multiop_lock);
|
||||
/* Get attached synth frequency */
|
||||
synth = zl3073x_synth_state_get(zldev, zl3073x_out_synth_get(out));
|
||||
synth_freq = zl3073x_synth_freq_get(synth);
|
||||
|
||||
/* Read output configuration into mailbox */
|
||||
rc = zl3073x_mb_op(zldev, ZL_REG_OUTPUT_MB_SEM, ZL_OUTPUT_MB_SEM_RD,
|
||||
ZL_REG_OUTPUT_MB_MASK, BIT(out));
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/* Read output mode */
|
||||
rc = zl3073x_read_u8(zldev, ZL_REG_OUTPUT_MODE, &output_mode);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/* Read output divisor */
|
||||
rc = zl3073x_read_u32(zldev, ZL_REG_OUTPUT_DIV, &output_div);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/* Check output divisor for zero */
|
||||
if (!output_div) {
|
||||
dev_err(dev, "Zero divisor for OUTPUT%u got from device\n",
|
||||
out);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Get synth attached to output pin */
|
||||
synth = zl3073x_dev_out_synth_get(zldev, out);
|
||||
|
||||
/* Get synth frequency */
|
||||
synth_freq = zl3073x_dev_synth_freq_get(zldev, synth);
|
||||
|
||||
clock_type = FIELD_GET(ZL_OUTPUT_MODE_CLOCK_TYPE, output_mode);
|
||||
clock_type = FIELD_GET(ZL_OUTPUT_MODE_CLOCK_TYPE, out->mode);
|
||||
if (clock_type != ZL_OUTPUT_MODE_CLOCK_TYPE_ESYNC) {
|
||||
/* No need to read esync data if it is not enabled */
|
||||
esync->freq = 0;
|
||||
@@ -832,38 +803,21 @@ zl3073x_dpll_output_pin_esync_get(const struct dpll_pin *dpll_pin,
|
||||
goto finish;
|
||||
}
|
||||
|
||||
/* Read esync period */
|
||||
rc = zl3073x_read_u32(zldev, ZL_REG_OUTPUT_ESYNC_PERIOD, &esync_period);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/* Check esync divisor for zero */
|
||||
if (!esync_period) {
|
||||
dev_err(dev, "Zero esync divisor for OUTPUT%u got from device\n",
|
||||
out);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Get esync pulse width in units of half synth cycles */
|
||||
rc = zl3073x_read_u32(zldev, ZL_REG_OUTPUT_ESYNC_WIDTH, &esync_width);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/* Compute esync frequency */
|
||||
esync->freq = synth_freq / output_div / esync_period;
|
||||
esync->freq = synth_freq / out->div / out->esync_n_period;
|
||||
|
||||
/* By comparing the esync_pulse_width to the half of the pulse width
|
||||
* the esync pulse percentage can be determined.
|
||||
* Note that half pulse width is in units of half synth cycles, which
|
||||
* is why it reduces down to be output_div.
|
||||
*/
|
||||
esync->pulse = (50 * esync_width) / output_div;
|
||||
esync->pulse = (50 * out->esync_n_width) / out->div;
|
||||
|
||||
finish:
|
||||
/* Set supported esync ranges if the pin supports esync control and
|
||||
* if the output frequency is > 1 Hz.
|
||||
*/
|
||||
if (pin->esync_control && (synth_freq / output_div) > 1) {
|
||||
if (pin->esync_control && (synth_freq / out->div) > 1) {
|
||||
esync->range = esync_freq_ranges;
|
||||
esync->range_num = ARRAY_SIZE(esync_freq_ranges);
|
||||
} else {
|
||||
@@ -881,21 +835,22 @@ zl3073x_dpll_output_pin_esync_set(const struct dpll_pin *dpll_pin,
|
||||
void *dpll_priv, u64 freq,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
u32 esync_period, esync_width, output_div;
|
||||
struct zl3073x_dpll *zldpll = dpll_priv;
|
||||
struct zl3073x_dev *zldev = zldpll->dev;
|
||||
struct zl3073x_dpll_pin *pin = pin_priv;
|
||||
u8 clock_type, out, output_mode, synth;
|
||||
const struct zl3073x_synth *synth;
|
||||
struct zl3073x_out out;
|
||||
u8 clock_type, out_id;
|
||||
u32 synth_freq;
|
||||
int rc;
|
||||
|
||||
out = zl3073x_output_pin_out_get(pin->id);
|
||||
out_id = zl3073x_output_pin_out_get(pin->id);
|
||||
out = *zl3073x_out_state_get(zldev, out_id);
|
||||
|
||||
/* If N-division is enabled, esync is not supported. The register used
|
||||
* for N-division is also used for the esync divider so both cannot
|
||||
* be used.
|
||||
*/
|
||||
switch (zl3073x_dev_out_signal_format_get(zldev, out)) {
|
||||
switch (zl3073x_out_signal_format_get(&out)) {
|
||||
case ZL_OUTPUT_MODE_SIGNAL_FORMAT_2_NDIV:
|
||||
case ZL_OUTPUT_MODE_SIGNAL_FORMAT_2_NDIV_INV:
|
||||
return -EOPNOTSUPP;
|
||||
@@ -903,19 +858,6 @@ zl3073x_dpll_output_pin_esync_set(const struct dpll_pin *dpll_pin,
|
||||
break;
|
||||
}
|
||||
|
||||
guard(mutex)(&zldev->multiop_lock);
|
||||
|
||||
/* Read output configuration into mailbox */
|
||||
rc = zl3073x_mb_op(zldev, ZL_REG_OUTPUT_MB_SEM, ZL_OUTPUT_MB_SEM_RD,
|
||||
ZL_REG_OUTPUT_MB_MASK, BIT(out));
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/* Read output mode */
|
||||
rc = zl3073x_read_u8(zldev, ZL_REG_OUTPUT_MODE, &output_mode);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/* Select clock type */
|
||||
if (freq)
|
||||
clock_type = ZL_OUTPUT_MODE_CLOCK_TYPE_ESYNC;
|
||||
@@ -923,38 +865,19 @@ zl3073x_dpll_output_pin_esync_set(const struct dpll_pin *dpll_pin,
|
||||
clock_type = ZL_OUTPUT_MODE_CLOCK_TYPE_NORMAL;
|
||||
|
||||
/* Update clock type in output mode */
|
||||
output_mode &= ~ZL_OUTPUT_MODE_CLOCK_TYPE;
|
||||
output_mode |= FIELD_PREP(ZL_OUTPUT_MODE_CLOCK_TYPE, clock_type);
|
||||
rc = zl3073x_write_u8(zldev, ZL_REG_OUTPUT_MODE, output_mode);
|
||||
if (rc)
|
||||
return rc;
|
||||
out.mode &= ~ZL_OUTPUT_MODE_CLOCK_TYPE;
|
||||
out.mode |= FIELD_PREP(ZL_OUTPUT_MODE_CLOCK_TYPE, clock_type);
|
||||
|
||||
/* If esync is being disabled just write mailbox and finish */
|
||||
if (!freq)
|
||||
goto write_mailbox;
|
||||
|
||||
/* Get synth attached to output pin */
|
||||
synth = zl3073x_dev_out_synth_get(zldev, out);
|
||||
|
||||
/* Get synth frequency */
|
||||
synth_freq = zl3073x_dev_synth_freq_get(zldev, synth);
|
||||
|
||||
rc = zl3073x_read_u32(zldev, ZL_REG_OUTPUT_DIV, &output_div);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/* Check output divisor for zero */
|
||||
if (!output_div) {
|
||||
dev_err(zldev->dev,
|
||||
"Zero divisor for OUTPUT%u got from device\n", out);
|
||||
return -EINVAL;
|
||||
}
|
||||
/* Get attached synth frequency */
|
||||
synth = zl3073x_synth_state_get(zldev, zl3073x_out_synth_get(&out));
|
||||
synth_freq = zl3073x_synth_freq_get(synth);
|
||||
|
||||
/* Compute and update esync period */
|
||||
esync_period = synth_freq / (u32)freq / output_div;
|
||||
rc = zl3073x_write_u32(zldev, ZL_REG_OUTPUT_ESYNC_PERIOD, esync_period);
|
||||
if (rc)
|
||||
return rc;
|
||||
out.esync_n_period = synth_freq / (u32)freq / out.div;
|
||||
|
||||
/* Half of the period in units of 1/2 synth cycle can be represented by
|
||||
* the output_div. To get the supported esync pulse width of 25% of the
|
||||
@@ -962,15 +885,11 @@ zl3073x_dpll_output_pin_esync_set(const struct dpll_pin *dpll_pin,
|
||||
* assumes that output_div is even, otherwise some resolution will be
|
||||
* lost.
|
||||
*/
|
||||
esync_width = output_div / 2;
|
||||
rc = zl3073x_write_u32(zldev, ZL_REG_OUTPUT_ESYNC_WIDTH, esync_width);
|
||||
if (rc)
|
||||
return rc;
|
||||
out.esync_n_width = out.div / 2;
|
||||
|
||||
write_mailbox:
|
||||
/* Commit output configuration */
|
||||
return zl3073x_mb_op(zldev, ZL_REG_OUTPUT_MB_SEM, ZL_OUTPUT_MB_SEM_WR,
|
||||
ZL_REG_OUTPUT_MB_MASK, BIT(out));
|
||||
return zl3073x_out_state_set(zldev, out_id, &out);
|
||||
}
|
||||
|
||||
static int
|
||||
@@ -983,83 +902,46 @@ zl3073x_dpll_output_pin_frequency_get(const struct dpll_pin *dpll_pin,
|
||||
struct zl3073x_dpll *zldpll = dpll_priv;
|
||||
struct zl3073x_dev *zldev = zldpll->dev;
|
||||
struct zl3073x_dpll_pin *pin = pin_priv;
|
||||
struct device *dev = zldev->dev;
|
||||
u8 out, signal_format, synth;
|
||||
u32 output_div, synth_freq;
|
||||
int rc;
|
||||
const struct zl3073x_synth *synth;
|
||||
const struct zl3073x_out *out;
|
||||
u32 synth_freq;
|
||||
u8 out_id;
|
||||
|
||||
out = zl3073x_output_pin_out_get(pin->id);
|
||||
synth = zl3073x_dev_out_synth_get(zldev, out);
|
||||
synth_freq = zl3073x_dev_synth_freq_get(zldev, synth);
|
||||
out_id = zl3073x_output_pin_out_get(pin->id);
|
||||
out = zl3073x_out_state_get(zldev, out_id);
|
||||
|
||||
guard(mutex)(&zldev->multiop_lock);
|
||||
/* Get attached synth frequency */
|
||||
synth = zl3073x_synth_state_get(zldev, zl3073x_out_synth_get(out));
|
||||
synth_freq = zl3073x_synth_freq_get(synth);
|
||||
|
||||
/* Read output configuration into mailbox */
|
||||
rc = zl3073x_mb_op(zldev, ZL_REG_OUTPUT_MB_SEM, ZL_OUTPUT_MB_SEM_RD,
|
||||
ZL_REG_OUTPUT_MB_MASK, BIT(out));
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
rc = zl3073x_read_u32(zldev, ZL_REG_OUTPUT_DIV, &output_div);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/* Check output divisor for zero */
|
||||
if (!output_div) {
|
||||
dev_err(dev, "Zero divisor for output %u got from device\n",
|
||||
out);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Read used signal format for the given output */
|
||||
signal_format = zl3073x_dev_out_signal_format_get(zldev, out);
|
||||
|
||||
switch (signal_format) {
|
||||
switch (zl3073x_out_signal_format_get(out)) {
|
||||
case ZL_OUTPUT_MODE_SIGNAL_FORMAT_2_NDIV:
|
||||
case ZL_OUTPUT_MODE_SIGNAL_FORMAT_2_NDIV_INV:
|
||||
/* In case of divided format we have to distiguish between
|
||||
* given output pin type.
|
||||
*
|
||||
* For P-pin the resulting frequency is computed as simple
|
||||
* division of synth frequency and output divisor.
|
||||
*
|
||||
* For N-pin we have to divide additionally by divisor stored
|
||||
* in esync_n_period output mailbox register that is used as
|
||||
* N-pin divisor for these modes.
|
||||
*/
|
||||
if (zl3073x_dpll_is_p_pin(pin)) {
|
||||
/* For P-pin the resulting frequency is computed as
|
||||
* simple division of synth frequency and output
|
||||
* divisor.
|
||||
*/
|
||||
*frequency = synth_freq / output_div;
|
||||
} else {
|
||||
/* For N-pin we have to divide additionally by
|
||||
* divisor stored in esync_period output mailbox
|
||||
* register that is used as N-pin divisor for these
|
||||
* modes.
|
||||
*/
|
||||
u32 ndiv;
|
||||
*frequency = synth_freq / out->div;
|
||||
|
||||
rc = zl3073x_read_u32(zldev, ZL_REG_OUTPUT_ESYNC_PERIOD,
|
||||
&ndiv);
|
||||
if (rc)
|
||||
return rc;
|
||||
if (!zl3073x_dpll_is_p_pin(pin))
|
||||
*frequency = (u32)*frequency / out->esync_n_period;
|
||||
|
||||
/* Check N-pin divisor for zero */
|
||||
if (!ndiv) {
|
||||
dev_err(dev,
|
||||
"Zero N-pin divisor for output %u got from device\n",
|
||||
out);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Compute final divisor for N-pin */
|
||||
*frequency = synth_freq / output_div / ndiv;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
/* In other modes the resulting frequency is computed as
|
||||
* division of synth frequency and output divisor.
|
||||
*/
|
||||
*frequency = synth_freq / output_div;
|
||||
*frequency = synth_freq / out->div;
|
||||
break;
|
||||
}
|
||||
|
||||
return rc;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
@@ -1072,28 +954,21 @@ zl3073x_dpll_output_pin_frequency_set(const struct dpll_pin *dpll_pin,
|
||||
struct zl3073x_dpll *zldpll = dpll_priv;
|
||||
struct zl3073x_dev *zldev = zldpll->dev;
|
||||
struct zl3073x_dpll_pin *pin = pin_priv;
|
||||
struct device *dev = zldev->dev;
|
||||
u32 output_n_freq, output_p_freq;
|
||||
u8 out, signal_format, synth;
|
||||
u32 cur_div, new_div, ndiv;
|
||||
u32 synth_freq;
|
||||
int rc;
|
||||
const struct zl3073x_synth *synth;
|
||||
u8 out_id, signal_format;
|
||||
u32 new_div, synth_freq;
|
||||
struct zl3073x_out out;
|
||||
|
||||
out = zl3073x_output_pin_out_get(pin->id);
|
||||
synth = zl3073x_dev_out_synth_get(zldev, out);
|
||||
synth_freq = zl3073x_dev_synth_freq_get(zldev, synth);
|
||||
out_id = zl3073x_output_pin_out_get(pin->id);
|
||||
out = *zl3073x_out_state_get(zldev, out_id);
|
||||
|
||||
/* Get attached synth frequency and compute new divisor */
|
||||
synth = zl3073x_synth_state_get(zldev, zl3073x_out_synth_get(&out));
|
||||
synth_freq = zl3073x_synth_freq_get(synth);
|
||||
new_div = synth_freq / (u32)frequency;
|
||||
|
||||
/* Get used signal format for the given output */
|
||||
signal_format = zl3073x_dev_out_signal_format_get(zldev, out);
|
||||
|
||||
guard(mutex)(&zldev->multiop_lock);
|
||||
|
||||
/* Load output configuration */
|
||||
rc = zl3073x_mb_op(zldev, ZL_REG_OUTPUT_MB_SEM, ZL_OUTPUT_MB_SEM_RD,
|
||||
ZL_REG_OUTPUT_MB_MASK, BIT(out));
|
||||
if (rc)
|
||||
return rc;
|
||||
signal_format = zl3073x_out_signal_format_get(&out);
|
||||
|
||||
/* Check signal format */
|
||||
if (signal_format != ZL_OUTPUT_MODE_SIGNAL_FORMAT_2_NDIV &&
|
||||
@@ -1101,99 +976,50 @@ zl3073x_dpll_output_pin_frequency_set(const struct dpll_pin *dpll_pin,
|
||||
/* For non N-divided signal formats the frequency is computed
|
||||
* as division of synth frequency and output divisor.
|
||||
*/
|
||||
rc = zl3073x_write_u32(zldev, ZL_REG_OUTPUT_DIV, new_div);
|
||||
if (rc)
|
||||
return rc;
|
||||
out.div = new_div;
|
||||
|
||||
/* For 50/50 duty cycle the divisor is equal to width */
|
||||
rc = zl3073x_write_u32(zldev, ZL_REG_OUTPUT_WIDTH, new_div);
|
||||
if (rc)
|
||||
return rc;
|
||||
out.width = new_div;
|
||||
|
||||
/* Commit output configuration */
|
||||
return zl3073x_mb_op(zldev,
|
||||
ZL_REG_OUTPUT_MB_SEM, ZL_OUTPUT_MB_SEM_WR,
|
||||
ZL_REG_OUTPUT_MB_MASK, BIT(out));
|
||||
return zl3073x_out_state_set(zldev, out_id, &out);
|
||||
}
|
||||
|
||||
/* For N-divided signal format get current divisor */
|
||||
rc = zl3073x_read_u32(zldev, ZL_REG_OUTPUT_DIV, &cur_div);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/* Check output divisor for zero */
|
||||
if (!cur_div) {
|
||||
dev_err(dev, "Zero divisor for output %u got from device\n",
|
||||
out);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Get N-pin divisor (shares the same register with esync */
|
||||
rc = zl3073x_read_u32(zldev, ZL_REG_OUTPUT_ESYNC_PERIOD, &ndiv);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/* Check N-pin divisor for zero */
|
||||
if (!ndiv) {
|
||||
dev_err(dev,
|
||||
"Zero N-pin divisor for output %u got from device\n",
|
||||
out);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Compute current output frequency for P-pin */
|
||||
output_p_freq = synth_freq / cur_div;
|
||||
|
||||
/* Compute current N-pin frequency */
|
||||
output_n_freq = output_p_freq / ndiv;
|
||||
|
||||
if (zl3073x_dpll_is_p_pin(pin)) {
|
||||
/* We are going to change output frequency for P-pin but
|
||||
* if the requested frequency is less than current N-pin
|
||||
* frequency then indicate a failure as we are not able
|
||||
* to compute N-pin divisor to keep its frequency unchanged.
|
||||
*
|
||||
* Update divisor for N-pin to keep N-pin frequency.
|
||||
*/
|
||||
if (frequency <= output_n_freq)
|
||||
out.esync_n_period = (out.esync_n_period * out.div) / new_div;
|
||||
if (!out.esync_n_period)
|
||||
return -EINVAL;
|
||||
|
||||
/* Update the output divisor */
|
||||
rc = zl3073x_write_u32(zldev, ZL_REG_OUTPUT_DIV, new_div);
|
||||
if (rc)
|
||||
return rc;
|
||||
out.div = new_div;
|
||||
|
||||
/* For 50/50 duty cycle the divisor is equal to width */
|
||||
rc = zl3073x_write_u32(zldev, ZL_REG_OUTPUT_WIDTH, new_div);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/* Compute new divisor for N-pin */
|
||||
ndiv = (u32)frequency / output_n_freq;
|
||||
out.width = out.div;
|
||||
} else {
|
||||
/* We are going to change frequency of N-pin but if
|
||||
* the requested freq is greater or equal than freq of P-pin
|
||||
* in the output pair we cannot compute divisor for the N-pin.
|
||||
* In this case indicate a failure.
|
||||
*
|
||||
* Update divisor for N-pin
|
||||
*/
|
||||
if (output_p_freq <= frequency)
|
||||
out.esync_n_period = div64_u64(synth_freq, frequency * out.div);
|
||||
if (!out.esync_n_period)
|
||||
return -EINVAL;
|
||||
|
||||
/* Compute new divisor for N-pin */
|
||||
ndiv = output_p_freq / (u32)frequency;
|
||||
}
|
||||
|
||||
/* Update divisor for the N-pin */
|
||||
rc = zl3073x_write_u32(zldev, ZL_REG_OUTPUT_ESYNC_PERIOD, ndiv);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/* For 50/50 duty cycle the divisor is equal to width */
|
||||
rc = zl3073x_write_u32(zldev, ZL_REG_OUTPUT_ESYNC_WIDTH, ndiv);
|
||||
if (rc)
|
||||
return rc;
|
||||
out.esync_n_width = out.esync_n_period;
|
||||
|
||||
/* Commit output configuration */
|
||||
return zl3073x_mb_op(zldev, ZL_REG_OUTPUT_MB_SEM, ZL_OUTPUT_MB_SEM_WR,
|
||||
ZL_REG_OUTPUT_MB_MASK, BIT(out));
|
||||
return zl3073x_out_state_set(zldev, out_id, &out);
|
||||
}
|
||||
|
||||
static int
|
||||
@@ -1207,30 +1033,18 @@ zl3073x_dpll_output_pin_phase_adjust_get(const struct dpll_pin *dpll_pin,
|
||||
struct zl3073x_dpll *zldpll = dpll_priv;
|
||||
struct zl3073x_dev *zldev = zldpll->dev;
|
||||
struct zl3073x_dpll_pin *pin = pin_priv;
|
||||
s32 phase_comp;
|
||||
u8 out;
|
||||
int rc;
|
||||
const struct zl3073x_out *out;
|
||||
u8 out_id;
|
||||
|
||||
guard(mutex)(&zldev->multiop_lock);
|
||||
|
||||
/* Read output configuration */
|
||||
out = zl3073x_output_pin_out_get(pin->id);
|
||||
rc = zl3073x_mb_op(zldev, ZL_REG_OUTPUT_MB_SEM, ZL_OUTPUT_MB_SEM_RD,
|
||||
ZL_REG_OUTPUT_MB_MASK, BIT(out));
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/* Read current output phase compensation */
|
||||
rc = zl3073x_read_u32(zldev, ZL_REG_OUTPUT_PHASE_COMP, &phase_comp);
|
||||
if (rc)
|
||||
return rc;
|
||||
out_id = zl3073x_output_pin_out_get(pin->id);
|
||||
out = zl3073x_out_state_get(zldev, out_id);
|
||||
|
||||
/* Convert value to ps and reverse two's complement negation applied
|
||||
* during 'set'
|
||||
*/
|
||||
*phase_adjust = -phase_comp * pin->phase_gran;
|
||||
*phase_adjust = -out->phase_comp * pin->phase_gran;
|
||||
|
||||
return rc;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
@@ -1244,31 +1058,19 @@ zl3073x_dpll_output_pin_phase_adjust_set(const struct dpll_pin *dpll_pin,
|
||||
struct zl3073x_dpll *zldpll = dpll_priv;
|
||||
struct zl3073x_dev *zldev = zldpll->dev;
|
||||
struct zl3073x_dpll_pin *pin = pin_priv;
|
||||
u8 out;
|
||||
int rc;
|
||||
struct zl3073x_out out;
|
||||
u8 out_id;
|
||||
|
||||
out_id = zl3073x_output_pin_out_get(pin->id);
|
||||
out = *zl3073x_out_state_get(zldev, out_id);
|
||||
|
||||
/* The value in the register is stored as two's complement negation
|
||||
* of requested value and expressed in half synth clock cycles.
|
||||
*/
|
||||
phase_adjust = -phase_adjust / pin->phase_gran;
|
||||
|
||||
guard(mutex)(&zldev->multiop_lock);
|
||||
|
||||
/* Read output configuration */
|
||||
out = zl3073x_output_pin_out_get(pin->id);
|
||||
rc = zl3073x_mb_op(zldev, ZL_REG_OUTPUT_MB_SEM, ZL_OUTPUT_MB_SEM_RD,
|
||||
ZL_REG_OUTPUT_MB_MASK, BIT(out));
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/* Write the requested value into the compensation register */
|
||||
rc = zl3073x_write_u32(zldev, ZL_REG_OUTPUT_PHASE_COMP, phase_adjust);
|
||||
if (rc)
|
||||
return rc;
|
||||
out.phase_comp = -phase_adjust / pin->phase_gran;
|
||||
|
||||
/* Update output configuration from mailbox */
|
||||
return zl3073x_mb_op(zldev, ZL_REG_OUTPUT_MB_SEM, ZL_OUTPUT_MB_SEM_WR,
|
||||
ZL_REG_OUTPUT_MB_MASK, BIT(out));
|
||||
return zl3073x_out_state_set(zldev, out_id, &out);
|
||||
}
|
||||
|
||||
static int
|
||||
@@ -1680,17 +1482,15 @@ zl3073x_dpll_pin_is_registrable(struct zl3073x_dpll *zldpll,
|
||||
/* Output P&N pair shares single HW output */
|
||||
u8 out = zl3073x_output_pin_out_get(index);
|
||||
|
||||
name = "OUT";
|
||||
|
||||
/* Skip the pin if it is connected to different DPLL channel */
|
||||
if (zl3073x_dev_out_dpll_get(zldev, out) != zldpll->id) {
|
||||
dev_dbg(zldev->dev,
|
||||
"%s%u is driven by different DPLL\n", name,
|
||||
out);
|
||||
"OUT%u is driven by different DPLL\n", out);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
name = "OUT";
|
||||
is_diff = zl3073x_dev_out_is_diff(zldev, out);
|
||||
is_enabled = zl3073x_dev_output_pin_is_enabled(zldev, index);
|
||||
}
|
||||
|
||||
@@ -50,6 +50,46 @@ int zl3073x_out_state_fetch(struct zl3073x_dev *zldev, u8 index)
|
||||
dev_dbg(zldev->dev, "OUT%u has signal format 0x%02x\n", index,
|
||||
zl3073x_out_signal_format_get(out));
|
||||
|
||||
/* Read output divisor */
|
||||
rc = zl3073x_read_u32(zldev, ZL_REG_OUTPUT_DIV, &out->div);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
if (!out->div) {
|
||||
dev_err(zldev->dev, "Zero divisor for OUT%u got from device\n",
|
||||
index);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
dev_dbg(zldev->dev, "OUT%u divisor: %u\n", index, out->div);
|
||||
|
||||
/* Read output width */
|
||||
rc = zl3073x_read_u32(zldev, ZL_REG_OUTPUT_WIDTH, &out->width);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
rc = zl3073x_read_u32(zldev, ZL_REG_OUTPUT_ESYNC_PERIOD,
|
||||
&out->esync_n_period);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
if (!out->esync_n_period) {
|
||||
dev_err(zldev->dev,
|
||||
"Zero esync divisor for OUT%u got from device\n",
|
||||
index);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
rc = zl3073x_read_u32(zldev, ZL_REG_OUTPUT_ESYNC_WIDTH,
|
||||
&out->esync_n_width);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
rc = zl3073x_read_u32(zldev, ZL_REG_OUTPUT_PHASE_COMP,
|
||||
&out->phase_comp);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -65,3 +105,53 @@ const struct zl3073x_out *zl3073x_out_state_get(struct zl3073x_dev *zldev,
|
||||
{
|
||||
return &zldev->out[index];
|
||||
}
|
||||
|
||||
int zl3073x_out_state_set(struct zl3073x_dev *zldev, u8 index,
|
||||
const struct zl3073x_out *out)
|
||||
{
|
||||
struct zl3073x_out *dout = &zldev->out[index];
|
||||
int rc;
|
||||
|
||||
guard(mutex)(&zldev->multiop_lock);
|
||||
|
||||
/* Read output configuration into mailbox */
|
||||
rc = zl3073x_mb_op(zldev, ZL_REG_OUTPUT_MB_SEM, ZL_OUTPUT_MB_SEM_RD,
|
||||
ZL_REG_OUTPUT_MB_MASK, BIT(index));
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/* Update mailbox with changed values */
|
||||
if (dout->div != out->div)
|
||||
rc = zl3073x_write_u32(zldev, ZL_REG_OUTPUT_DIV, out->div);
|
||||
if (!rc && dout->width != out->width)
|
||||
rc = zl3073x_write_u32(zldev, ZL_REG_OUTPUT_WIDTH, out->width);
|
||||
if (!rc && dout->esync_n_period != out->esync_n_period)
|
||||
rc = zl3073x_write_u32(zldev, ZL_REG_OUTPUT_ESYNC_PERIOD,
|
||||
out->esync_n_period);
|
||||
if (!rc && dout->esync_n_width != out->esync_n_width)
|
||||
rc = zl3073x_write_u32(zldev, ZL_REG_OUTPUT_ESYNC_WIDTH,
|
||||
out->esync_n_width);
|
||||
if (!rc && dout->mode != out->mode)
|
||||
rc = zl3073x_write_u8(zldev, ZL_REG_OUTPUT_MODE, out->mode);
|
||||
if (!rc && dout->phase_comp != out->phase_comp)
|
||||
rc = zl3073x_write_u32(zldev, ZL_REG_OUTPUT_PHASE_COMP,
|
||||
out->phase_comp);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/* Commit output configuration */
|
||||
rc = zl3073x_mb_op(zldev, ZL_REG_OUTPUT_MB_SEM, ZL_OUTPUT_MB_SEM_WR,
|
||||
ZL_REG_OUTPUT_MB_MASK, BIT(index));
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/* After successful commit store new state */
|
||||
dout->div = out->div;
|
||||
dout->width = out->width;
|
||||
dout->esync_n_period = out->esync_n_period;
|
||||
dout->esync_n_width = out->esync_n_width;
|
||||
dout->mode = out->mode;
|
||||
dout->phase_comp = out->phase_comp;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -12,10 +12,20 @@ struct zl3073x_dev;
|
||||
|
||||
/**
|
||||
* struct zl3073x_out - output state
|
||||
* @div: output divisor
|
||||
* @width: output pulse width
|
||||
* @esync_n_period: embedded sync or n-pin period (for n-div formats)
|
||||
* @esync_n_width: embedded sync or n-pin pulse width
|
||||
* @phase_comp: phase compensation
|
||||
* @ctrl: output control
|
||||
* @mode: output mode
|
||||
*/
|
||||
struct zl3073x_out {
|
||||
u32 div;
|
||||
u32 width;
|
||||
u32 esync_n_period;
|
||||
u32 esync_n_width;
|
||||
s32 phase_comp;
|
||||
u8 ctrl;
|
||||
u8 mode;
|
||||
};
|
||||
@@ -24,6 +34,9 @@ int zl3073x_out_state_fetch(struct zl3073x_dev *zldev, u8 index);
|
||||
const struct zl3073x_out *zl3073x_out_state_get(struct zl3073x_dev *zldev,
|
||||
u8 index);
|
||||
|
||||
int zl3073x_out_state_set(struct zl3073x_dev *zldev, u8 index,
|
||||
const struct zl3073x_out *out);
|
||||
|
||||
/**
|
||||
* zl3073x_out_signal_format_get - get output signal format
|
||||
* @out: pointer to out state
|
||||
|
||||
Reference in New Issue
Block a user