Files
linux/drivers/dpll/zl3073x/ref.c
Ivan Vecera 5bc02b190a dpll: zl3073x: Cache all reference properties in zl3073x_ref
Expand the zl3073x_ref structure to cache all reference-related
hardware registers, including frequency components, embedded-sync
settings  and phase compensation. Previously, these registers were
read on-demand from various functions in dpll.c leading to frequent
mailbox operations.

Modify zl3073x_ref_state_fetch() to read and populate all these new
fields at once. Refactor all "getter" functions in dpll.c to read
from this new cached state instead of performing direct register
access.

Remove the standalone zl3073x_dpll_input_ref_frequency_get() helper,
as its functionality is now replaced by zl3073x_ref_freq_get() which
operates on the cached state and add a corresponding zl3073x_dev_...
wrapper.

Introduce a new function, zl3073x_ref_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 to the device via a single mailbox sequence
before updating the local cache.

Refactor all dpll "setter" functions to modify a local copy of the
ref state and then call zl3073x_ref_state_set() to commit the changes.

As a cleanup, update callers in dpll.c that already have
a struct zl3073x_ref * to use the direct helpers instead of the
zl3073x_dev_... wrappers.

This change centralizes all reference-related register I/O into ref.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-5-ivecera@redhat.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2025-11-17 20:23:37 -08:00

205 lines
5.8 KiB
C

// SPDX-License-Identifier: GPL-2.0-only
#include <linux/bitfield.h>
#include <linux/cleanup.h>
#include <linux/dev_printk.h>
#include <linux/string.h>
#include <linux/string_choices.h>
#include <linux/types.h>
#include "core.h"
#include "ref.h"
/**
* zl3073x_ref_freq_factorize - factorize given frequency
* @freq: input frequency
* @base: base frequency
* @mult: multiplier
*
* Checks if the given frequency can be factorized using one of the
* supported base frequencies. If so the base frequency and multiplier
* are stored into appropriate parameters if they are not NULL.
*
* Return: 0 on success, -EINVAL if the frequency cannot be factorized
*/
int
zl3073x_ref_freq_factorize(u32 freq, u16 *base, u16 *mult)
{
static const u16 base_freqs[] = {
1, 2, 4, 5, 8, 10, 16, 20, 25, 32, 40, 50, 64, 80, 100, 125,
128, 160, 200, 250, 256, 320, 400, 500, 625, 640, 800, 1000,
1250, 1280, 1600, 2000, 2500, 3125, 3200, 4000, 5000, 6250,
6400, 8000, 10000, 12500, 15625, 16000, 20000, 25000, 31250,
32000, 40000, 50000, 62500,
};
u32 div;
int i;
for (i = 0; i < ARRAY_SIZE(base_freqs); i++) {
div = freq / base_freqs[i];
if (div <= U16_MAX && (freq % base_freqs[i]) == 0) {
if (base)
*base = base_freqs[i];
if (mult)
*mult = div;
return 0;
}
}
return -EINVAL;
}
/**
* zl3073x_ref_state_fetch - fetch input reference state from hardware
* @zldev: pointer to zl3073x_dev structure
* @index: input reference index to fetch state for
*
* Function fetches state for the given input reference from hardware and
* stores it for later use.
*
* Return: 0 on success, <0 on error
*/
int zl3073x_ref_state_fetch(struct zl3073x_dev *zldev, u8 index)
{
struct zl3073x_ref *ref = &zldev->ref[index];
int rc;
/* For differential type inputs the N-pin reference shares
* part of the configuration with the P-pin counterpart.
*/
if (zl3073x_is_n_pin(index) && zl3073x_ref_is_diff(ref - 1)) {
struct zl3073x_ref *p_ref = ref - 1; /* P-pin counterpart*/
/* Copy the shared items from the P-pin */
ref->config = p_ref->config;
ref->esync_n_div = p_ref->esync_n_div;
ref->freq_base = p_ref->freq_base;
ref->freq_mult = p_ref->freq_mult;
ref->freq_ratio_m = p_ref->freq_ratio_m;
ref->freq_ratio_n = p_ref->freq_ratio_n;
ref->phase_comp = p_ref->phase_comp;
ref->sync_ctrl = p_ref->sync_ctrl;
return 0; /* Finish - no non-shared items for now */
}
guard(mutex)(&zldev->multiop_lock);
/* Read reference configuration */
rc = zl3073x_mb_op(zldev, ZL_REG_REF_MB_SEM, ZL_REF_MB_SEM_RD,
ZL_REG_REF_MB_MASK, BIT(index));
if (rc)
return rc;
/* Read ref_config register */
rc = zl3073x_read_u8(zldev, ZL_REG_REF_CONFIG, &ref->config);
if (rc)
return rc;
/* Read frequency related registers */
rc = zl3073x_read_u16(zldev, ZL_REG_REF_FREQ_BASE, &ref->freq_base);
if (rc)
return rc;
rc = zl3073x_read_u16(zldev, ZL_REG_REF_FREQ_MULT, &ref->freq_mult);
if (rc)
return rc;
rc = zl3073x_read_u16(zldev, ZL_REG_REF_RATIO_M, &ref->freq_ratio_m);
if (rc)
return rc;
rc = zl3073x_read_u16(zldev, ZL_REG_REF_RATIO_N, &ref->freq_ratio_n);
if (rc)
return rc;
/* Read eSync and N-div rated registers */
rc = zl3073x_read_u32(zldev, ZL_REG_REF_ESYNC_DIV, &ref->esync_n_div);
if (rc)
return rc;
rc = zl3073x_read_u8(zldev, ZL_REG_REF_SYNC_CTRL, &ref->sync_ctrl);
if (rc)
return rc;
/* Read phase compensation register */
rc = zl3073x_read_u48(zldev, ZL_REG_REF_PHASE_OFFSET_COMP,
&ref->phase_comp);
if (rc)
return rc;
dev_dbg(zldev->dev, "REF%u is %s and configured as %s\n", index,
str_enabled_disabled(zl3073x_ref_is_enabled(ref)),
zl3073x_ref_is_diff(ref) ? "differential" : "single-ended");
return rc;
}
/**
* zl3073x_ref_state_get - get current input reference state
* @zldev: pointer to zl3073x_dev structure
* @index: input reference index to get state for
*
* Return: pointer to given input reference state
*/
const struct zl3073x_ref *
zl3073x_ref_state_get(struct zl3073x_dev *zldev, u8 index)
{
return &zldev->ref[index];
}
int zl3073x_ref_state_set(struct zl3073x_dev *zldev, u8 index,
const struct zl3073x_ref *ref)
{
struct zl3073x_ref *dref = &zldev->ref[index];
int rc;
guard(mutex)(&zldev->multiop_lock);
/* Read reference configuration into mailbox */
rc = zl3073x_mb_op(zldev, ZL_REG_REF_MB_SEM, ZL_REF_MB_SEM_RD,
ZL_REG_REF_MB_MASK, BIT(index));
if (rc)
return rc;
/* Update mailbox with changed values */
if (dref->freq_base != ref->freq_base)
rc = zl3073x_write_u16(zldev, ZL_REG_REF_FREQ_BASE,
ref->freq_base);
if (!rc && dref->freq_mult != ref->freq_mult)
rc = zl3073x_write_u16(zldev, ZL_REG_REF_FREQ_MULT,
ref->freq_mult);
if (!rc && dref->freq_ratio_m != ref->freq_ratio_m)
rc = zl3073x_write_u16(zldev, ZL_REG_REF_RATIO_M,
ref->freq_ratio_m);
if (!rc && dref->freq_ratio_n != ref->freq_ratio_n)
rc = zl3073x_write_u16(zldev, ZL_REG_REF_RATIO_N,
ref->freq_ratio_n);
if (!rc && dref->esync_n_div != ref->esync_n_div)
rc = zl3073x_write_u32(zldev, ZL_REG_REF_ESYNC_DIV,
ref->esync_n_div);
if (!rc && dref->sync_ctrl != ref->sync_ctrl)
rc = zl3073x_write_u8(zldev, ZL_REG_REF_SYNC_CTRL,
ref->sync_ctrl);
if (!rc && dref->phase_comp != ref->phase_comp)
rc = zl3073x_write_u48(zldev, ZL_REG_REF_PHASE_OFFSET_COMP,
ref->phase_comp);
if (rc)
return rc;
/* Commit reference configuration */
rc = zl3073x_mb_op(zldev, ZL_REG_REF_MB_SEM, ZL_REF_MB_SEM_WR,
ZL_REG_REF_MB_MASK, BIT(index));
if (rc)
return rc;
/* After successful commit store new state */
dref->freq_base = ref->freq_base;
dref->freq_mult = ref->freq_mult;
dref->freq_ratio_m = ref->freq_ratio_m;
dref->freq_ratio_n = ref->freq_ratio_n;
dref->esync_n_div = ref->esync_n_div;
dref->sync_ctrl = ref->sync_ctrl;
dref->phase_comp = ref->phase_comp;
return 0;
}