Merge branch 'dpll-zl3073x-refactor-state-management'
Ivan Vecera says:
====================
dpll: zl3073x: Refactor state management
This patch set is a refactoring of the zl3073x driver to clean up
state management, improve modularity, and significantly reduce
on-demand I/O.
The driver's dpll.c implementation previously performed on-demand
register reads and writes (wrapped in mailbox operations) to get
or set properties like frequency, phase, and embedded-sync settings.
This cluttered the DPLL logic with low-level I/O, duplicated locking,
and led to inefficient bus traffic.
This series addresses this by:
1. Splitting the monolithic 'core.c' into logical units ('ref.c',
'out.c', 'synth.c').
2. Implementing a full read/write-back cache for 'zl3073x_ref' and
'zl3073x_out' structures.
All state is now read once during '_state_fetch()' (and status updated
periodically). DPLL get callbacks read from this cache. Set callbacks
modify a copy of the state, which is then committed via a new
'..._state_set()' function. These '_state_set' functions compare
the new state to the cached state and write *only* the modified
register values back to the hardware, all within a single mailbox
sequence.
The result is a much cleaner 'dpll.c' that is almost entirely
free of direct register I/O, and all state logic is properly
encapsulated in its respective file.
The series is broken down as follows:
* Patch 1: Changes the state structs to store raw register values
(e.g., 'config', 'ctrl') instead of parsed booleans, centralizing
parsing logic into the helpers.
* Patch 2: Splits the logic from 'core.c' into new 'ref.c', 'out.c'
and 'synth.c' files, creating a 'zl3073x_dev_...' abstraction layer.
* Patch 3: Introduces the caching concept by reading and caching
the reference monitor status periodically, removing scattered
reads from 'dpll.c'.
* Patch 4: Expands the 'zl3073x_ref' struct to cache *all* reference
properties and adds 'zl3073x_ref_state_set()' to write back changes.
* Patch 5: Does the same for the 'zl3073x_out' struct, caching all
output properties and adding 'zl3073x_out_state_set()'.
* Patch 6: A final cleanup that removes the 'zl3073x_dev_...' wrapper
functions that became redundant after the refactoring.
====================
Link: https://patch.msgid.link/20251113074105.141379-1-ivecera@redhat.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
@@ -1,7 +1,8 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
obj-$(CONFIG_ZL3073X) += zl3073x.o
|
||||
zl3073x-objs := core.o devlink.o dpll.o flash.o fw.o prop.o
|
||||
zl3073x-objs := core.o devlink.o dpll.o flash.o fw.o \
|
||||
out.o prop.o ref.o synth.o
|
||||
|
||||
obj-$(CONFIG_ZL3073X_I2C) += zl3073x_i2c.o
|
||||
zl3073x_i2c-objs := i2c.o
|
||||
|
||||
+18
-225
@@ -129,47 +129,6 @@ const struct regmap_config zl3073x_regmap_config = {
|
||||
};
|
||||
EXPORT_SYMBOL_NS_GPL(zl3073x_regmap_config, "ZL3073X");
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
static bool
|
||||
zl3073x_check_reg(struct zl3073x_dev *zldev, unsigned int reg, size_t size)
|
||||
{
|
||||
@@ -593,190 +552,6 @@ int zl3073x_write_hwreg_seq(struct zl3073x_dev *zldev,
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* zl3073x_ref_state_fetch - get input reference state
|
||||
* @zldev: pointer to zl3073x_dev structure
|
||||
* @index: input reference index to fetch state for
|
||||
*
|
||||
* Function fetches information for the given input reference that are
|
||||
* invariant and stores them for later use.
|
||||
*
|
||||
* Return: 0 on success, <0 on error
|
||||
*/
|
||||
static int
|
||||
zl3073x_ref_state_fetch(struct zl3073x_dev *zldev, u8 index)
|
||||
{
|
||||
struct zl3073x_ref *input = &zldev->ref[index];
|
||||
u8 ref_config;
|
||||
int rc;
|
||||
|
||||
/* If the input is differential then the configuration for N-pin
|
||||
* reference is ignored and P-pin config is used for both.
|
||||
*/
|
||||
if (zl3073x_is_n_pin(index) &&
|
||||
zl3073x_ref_is_diff(zldev, index - 1)) {
|
||||
input->enabled = zl3073x_ref_is_enabled(zldev, index - 1);
|
||||
input->diff = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
input->enabled = FIELD_GET(ZL_REF_CONFIG_ENABLE, ref_config);
|
||||
input->diff = FIELD_GET(ZL_REF_CONFIG_DIFF_EN, ref_config);
|
||||
|
||||
dev_dbg(zldev->dev, "REF%u is %s and configured as %s\n", index,
|
||||
str_enabled_disabled(input->enabled),
|
||||
input->diff ? "differential" : "single-ended");
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* zl3073x_out_state_fetch - get output state
|
||||
* @zldev: pointer to zl3073x_dev structure
|
||||
* @index: output index to fetch state for
|
||||
*
|
||||
* Function fetches information for the given output (not output pin)
|
||||
* that are invariant and stores them for later use.
|
||||
*
|
||||
* Return: 0 on success, <0 on error
|
||||
*/
|
||||
static int
|
||||
zl3073x_out_state_fetch(struct zl3073x_dev *zldev, u8 index)
|
||||
{
|
||||
struct zl3073x_out *out = &zldev->out[index];
|
||||
u8 output_ctrl, output_mode;
|
||||
int rc;
|
||||
|
||||
/* Read output configuration */
|
||||
rc = zl3073x_read_u8(zldev, ZL_REG_OUTPUT_CTRL(index), &output_ctrl);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/* Store info about output enablement and synthesizer the output
|
||||
* is connected to.
|
||||
*/
|
||||
out->enabled = FIELD_GET(ZL_OUTPUT_CTRL_EN, output_ctrl);
|
||||
out->synth = FIELD_GET(ZL_OUTPUT_CTRL_SYNTH_SEL, output_ctrl);
|
||||
|
||||
dev_dbg(zldev->dev, "OUT%u is %s and connected to SYNTH%u\n", index,
|
||||
str_enabled_disabled(out->enabled), out->synth);
|
||||
|
||||
guard(mutex)(&zldev->multiop_lock);
|
||||
|
||||
/* Read output configuration */
|
||||
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;
|
||||
|
||||
/* Read output_mode */
|
||||
rc = zl3073x_read_u8(zldev, ZL_REG_OUTPUT_MODE, &output_mode);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/* Extract and store output signal format */
|
||||
out->signal_format = FIELD_GET(ZL_OUTPUT_MODE_SIGNAL_FORMAT,
|
||||
output_mode);
|
||||
|
||||
dev_dbg(zldev->dev, "OUT%u has signal format 0x%02x\n", index,
|
||||
out->signal_format);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* zl3073x_synth_state_fetch - get synth state
|
||||
* @zldev: pointer to zl3073x_dev structure
|
||||
* @index: synth index to fetch state for
|
||||
*
|
||||
* Function fetches information for the given synthesizer that are
|
||||
* invariant and stores them for later use.
|
||||
*
|
||||
* Return: 0 on success, <0 on error
|
||||
*/
|
||||
static int
|
||||
zl3073x_synth_state_fetch(struct zl3073x_dev *zldev, u8 index)
|
||||
{
|
||||
struct zl3073x_synth *synth = &zldev->synth[index];
|
||||
u16 base, m, n;
|
||||
u8 synth_ctrl;
|
||||
u32 mult;
|
||||
int rc;
|
||||
|
||||
/* Read synth control register */
|
||||
rc = zl3073x_read_u8(zldev, ZL_REG_SYNTH_CTRL(index), &synth_ctrl);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/* Store info about synth enablement and DPLL channel the synth is
|
||||
* driven by.
|
||||
*/
|
||||
synth->enabled = FIELD_GET(ZL_SYNTH_CTRL_EN, synth_ctrl);
|
||||
synth->dpll = FIELD_GET(ZL_SYNTH_CTRL_DPLL_SEL, synth_ctrl);
|
||||
|
||||
dev_dbg(zldev->dev, "SYNTH%u is %s and driven by DPLL%u\n", index,
|
||||
str_enabled_disabled(synth->enabled), synth->dpll);
|
||||
|
||||
guard(mutex)(&zldev->multiop_lock);
|
||||
|
||||
/* Read synth configuration */
|
||||
rc = zl3073x_mb_op(zldev, ZL_REG_SYNTH_MB_SEM, ZL_SYNTH_MB_SEM_RD,
|
||||
ZL_REG_SYNTH_MB_MASK, BIT(index));
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/* The output frequency is determined by the following formula:
|
||||
* base * multiplier * numerator / denominator
|
||||
*
|
||||
* Read registers with these values
|
||||
*/
|
||||
rc = zl3073x_read_u16(zldev, ZL_REG_SYNTH_FREQ_BASE, &base);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
rc = zl3073x_read_u32(zldev, ZL_REG_SYNTH_FREQ_MULT, &mult);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
rc = zl3073x_read_u16(zldev, ZL_REG_SYNTH_FREQ_M, &m);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
rc = zl3073x_read_u16(zldev, ZL_REG_SYNTH_FREQ_N, &n);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/* Check denominator for zero to avoid div by 0 */
|
||||
if (!n) {
|
||||
dev_err(zldev->dev,
|
||||
"Zero divisor for SYNTH%u retrieved from device\n",
|
||||
index);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Compute and store synth frequency */
|
||||
zldev->synth[index].freq = div_u64(mul_u32_u32(base * m, mult), n);
|
||||
|
||||
dev_dbg(zldev->dev, "SYNTH%u frequency: %u Hz\n", index,
|
||||
zldev->synth[index].freq);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int
|
||||
zl3073x_dev_state_fetch(struct zl3073x_dev *zldev)
|
||||
{
|
||||
@@ -816,6 +591,21 @@ zl3073x_dev_state_fetch(struct zl3073x_dev *zldev)
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void
|
||||
zl3073x_dev_ref_status_update(struct zl3073x_dev *zldev)
|
||||
{
|
||||
int i, rc;
|
||||
|
||||
for (i = 0; i < ZL3073X_NUM_REFS; i++) {
|
||||
rc = zl3073x_read_u8(zldev, ZL_REG_REF_MON_STATUS(i),
|
||||
&zldev->ref[i].mon_status);
|
||||
if (rc)
|
||||
dev_warn(zldev->dev,
|
||||
"Failed to get REF%u status: %pe\n", i,
|
||||
ERR_PTR(rc));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* zl3073x_ref_phase_offsets_update - update reference phase offsets
|
||||
* @zldev: pointer to zl3073x_dev structure
|
||||
@@ -935,6 +725,9 @@ zl3073x_dev_periodic_work(struct kthread_work *work)
|
||||
struct zl3073x_dpll *zldpll;
|
||||
int rc;
|
||||
|
||||
/* Update input references status */
|
||||
zl3073x_dev_ref_status_update(zldev);
|
||||
|
||||
/* Update DPLL-to-connected-ref phase offsets registers */
|
||||
rc = zl3073x_ref_phase_offsets_update(zldev, -1);
|
||||
if (rc)
|
||||
|
||||
+64
-124
@@ -9,7 +9,10 @@
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#include "out.h"
|
||||
#include "ref.h"
|
||||
#include "regs.h"
|
||||
#include "synth.h"
|
||||
|
||||
struct device;
|
||||
struct regmap;
|
||||
@@ -27,42 +30,6 @@ struct zl3073x_dpll;
|
||||
#define ZL3073X_NUM_PINS (ZL3073X_NUM_INPUT_PINS + \
|
||||
ZL3073X_NUM_OUTPUT_PINS)
|
||||
|
||||
/**
|
||||
* 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;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct zl3073x_out - output invariant info
|
||||
* @enabled: out is enabled or disabled
|
||||
* @synth: synthesizer the out is connected to
|
||||
* @signal_format: out signal format
|
||||
*/
|
||||
struct zl3073x_out {
|
||||
bool enabled;
|
||||
u8 synth;
|
||||
u8 signal_format;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct zl3073x_synth - synthesizer invariant info
|
||||
* @freq: synthesizer frequency
|
||||
* @dpll: ID of DPLL the synthesizer is driven by
|
||||
* @enabled: synth is enabled or disabled
|
||||
*/
|
||||
struct zl3073x_synth {
|
||||
u32 freq;
|
||||
u8 dpll;
|
||||
bool enabled;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct zl3073x_dev - zl3073x device
|
||||
* @dev: pointer to device
|
||||
@@ -175,7 +142,6 @@ int zl3073x_write_hwreg_seq(struct zl3073x_dev *zldev,
|
||||
* Misc operations
|
||||
*****************/
|
||||
|
||||
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
|
||||
@@ -217,172 +183,141 @@ zl3073x_output_pin_out_get(u8 id)
|
||||
}
|
||||
|
||||
/**
|
||||
* zl3073x_ref_ffo_get - get current fractional frequency offset
|
||||
* zl3073x_dev_ref_freq_get - get input reference frequency
|
||||
* @zldev: pointer to zl3073x device
|
||||
* @index: input reference index
|
||||
*
|
||||
* Return: the latest measured fractional frequency offset
|
||||
* Return: frequency of given input reference
|
||||
*/
|
||||
static inline s64
|
||||
zl3073x_ref_ffo_get(struct zl3073x_dev *zldev, u8 index)
|
||||
static inline u32
|
||||
zl3073x_dev_ref_freq_get(struct zl3073x_dev *zldev, u8 index)
|
||||
{
|
||||
return zldev->ref[index].ffo;
|
||||
const struct zl3073x_ref *ref = zl3073x_ref_state_get(zldev, index);
|
||||
|
||||
return zl3073x_ref_freq_get(ref);
|
||||
}
|
||||
|
||||
/**
|
||||
* zl3073x_ref_is_diff - check if the given input reference is differential
|
||||
* zl3073x_dev_ref_is_diff - check if the given input reference is differential
|
||||
* @zldev: pointer to zl3073x device
|
||||
* @index: input reference index
|
||||
*
|
||||
* Return: true if reference is differential, false if reference is single-ended
|
||||
*/
|
||||
static inline bool
|
||||
zl3073x_ref_is_diff(struct zl3073x_dev *zldev, u8 index)
|
||||
zl3073x_dev_ref_is_diff(struct zl3073x_dev *zldev, u8 index)
|
||||
{
|
||||
return zldev->ref[index].diff;
|
||||
const struct zl3073x_ref *ref = zl3073x_ref_state_get(zldev, index);
|
||||
|
||||
return zl3073x_ref_is_diff(ref);
|
||||
}
|
||||
|
||||
/**
|
||||
* zl3073x_ref_is_enabled - check if the given input reference is enabled
|
||||
/*
|
||||
* zl3073x_dev_ref_is_status_ok - check the given input reference status
|
||||
* @zldev: pointer to zl3073x device
|
||||
* @index: input reference index
|
||||
*
|
||||
* Return: true if input refernce is enabled, false otherwise
|
||||
* Return: true if the status is ok, false otherwise
|
||||
*/
|
||||
static inline bool
|
||||
zl3073x_ref_is_enabled(struct zl3073x_dev *zldev, u8 index)
|
||||
zl3073x_dev_ref_is_status_ok(struct zl3073x_dev *zldev, u8 index)
|
||||
{
|
||||
return zldev->ref[index].enabled;
|
||||
const struct zl3073x_ref *ref = zl3073x_ref_state_get(zldev, index);
|
||||
|
||||
return zl3073x_ref_is_status_ok(ref);
|
||||
}
|
||||
|
||||
/**
|
||||
* zl3073x_synth_dpll_get - get DPLL ID the synth is driven by
|
||||
* @zldev: pointer to zl3073x device
|
||||
* @index: synth index
|
||||
*
|
||||
* Return: ID of DPLL the given synthetizer is driven by
|
||||
*/
|
||||
static inline u8
|
||||
zl3073x_synth_dpll_get(struct zl3073x_dev *zldev, u8 index)
|
||||
{
|
||||
return zldev->synth[index].dpll;
|
||||
}
|
||||
|
||||
/**
|
||||
* zl3073x_synth_freq_get - get synth current freq
|
||||
* zl3073x_dev_synth_freq_get - get synth current freq
|
||||
* @zldev: pointer to zl3073x device
|
||||
* @index: synth index
|
||||
*
|
||||
* Return: frequency of given synthetizer
|
||||
*/
|
||||
static inline u32
|
||||
zl3073x_synth_freq_get(struct zl3073x_dev *zldev, u8 index)
|
||||
zl3073x_dev_synth_freq_get(struct zl3073x_dev *zldev, u8 index)
|
||||
{
|
||||
return zldev->synth[index].freq;
|
||||
const struct zl3073x_synth *synth;
|
||||
|
||||
synth = zl3073x_synth_state_get(zldev, index);
|
||||
return zl3073x_synth_freq_get(synth);
|
||||
}
|
||||
|
||||
/**
|
||||
* zl3073x_synth_is_enabled - check if the given synth is enabled
|
||||
* @zldev: pointer to zl3073x device
|
||||
* @index: synth index
|
||||
*
|
||||
* Return: true if synth is enabled, false otherwise
|
||||
*/
|
||||
static inline bool
|
||||
zl3073x_synth_is_enabled(struct zl3073x_dev *zldev, u8 index)
|
||||
{
|
||||
return zldev->synth[index].enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* zl3073x_out_synth_get - get synth connected to given output
|
||||
* zl3073x_dev_out_synth_get - get synth connected to given output
|
||||
* @zldev: pointer to zl3073x device
|
||||
* @index: output index
|
||||
*
|
||||
* Return: index of synth connected to given output.
|
||||
*/
|
||||
static inline u8
|
||||
zl3073x_out_synth_get(struct zl3073x_dev *zldev, u8 index)
|
||||
zl3073x_dev_out_synth_get(struct zl3073x_dev *zldev, u8 index)
|
||||
{
|
||||
return zldev->out[index].synth;
|
||||
const struct zl3073x_out *out = zl3073x_out_state_get(zldev, index);
|
||||
|
||||
return zl3073x_out_synth_get(out);
|
||||
}
|
||||
|
||||
/**
|
||||
* zl3073x_out_is_enabled - check if the given output is enabled
|
||||
* zl3073x_dev_out_is_enabled - check if the given output is enabled
|
||||
* @zldev: pointer to zl3073x device
|
||||
* @index: output index
|
||||
*
|
||||
* Return: true if the output is enabled, false otherwise
|
||||
*/
|
||||
static inline bool
|
||||
zl3073x_out_is_enabled(struct zl3073x_dev *zldev, u8 index)
|
||||
zl3073x_dev_out_is_enabled(struct zl3073x_dev *zldev, u8 index)
|
||||
{
|
||||
u8 synth;
|
||||
const struct zl3073x_out *out = zl3073x_out_state_get(zldev, index);
|
||||
const struct zl3073x_synth *synth;
|
||||
u8 synth_id;
|
||||
|
||||
/* Output is enabled only if associated synth is enabled */
|
||||
synth = zl3073x_out_synth_get(zldev, index);
|
||||
if (zl3073x_synth_is_enabled(zldev, synth))
|
||||
return zldev->out[index].enabled;
|
||||
synth_id = zl3073x_out_synth_get(out);
|
||||
synth = zl3073x_synth_state_get(zldev, synth_id);
|
||||
|
||||
return false;
|
||||
return zl3073x_synth_is_enabled(synth) && zl3073x_out_is_enabled(out);
|
||||
}
|
||||
|
||||
/**
|
||||
* zl3073x_out_signal_format_get - get output signal format
|
||||
* @zldev: pointer to zl3073x device
|
||||
* @index: output index
|
||||
*
|
||||
* Return: signal format of given output
|
||||
*/
|
||||
static inline u8
|
||||
zl3073x_out_signal_format_get(struct zl3073x_dev *zldev, u8 index)
|
||||
{
|
||||
return zldev->out[index].signal_format;
|
||||
}
|
||||
|
||||
/**
|
||||
* zl3073x_out_dpll_get - get DPLL ID the output is driven by
|
||||
* zl3073x_dev_out_dpll_get - get DPLL ID the output is driven by
|
||||
* @zldev: pointer to zl3073x device
|
||||
* @index: output index
|
||||
*
|
||||
* Return: ID of DPLL the given output is driven by
|
||||
*/
|
||||
static inline
|
||||
u8 zl3073x_out_dpll_get(struct zl3073x_dev *zldev, u8 index)
|
||||
u8 zl3073x_dev_out_dpll_get(struct zl3073x_dev *zldev, u8 index)
|
||||
{
|
||||
u8 synth;
|
||||
const struct zl3073x_out *out = zl3073x_out_state_get(zldev, index);
|
||||
const struct zl3073x_synth *synth;
|
||||
u8 synth_id;
|
||||
|
||||
/* Get synthesizer connected to given output */
|
||||
synth = zl3073x_out_synth_get(zldev, index);
|
||||
synth_id = zl3073x_out_synth_get(out);
|
||||
synth = zl3073x_synth_state_get(zldev, synth_id);
|
||||
|
||||
/* Return DPLL that drives the synth */
|
||||
return zl3073x_synth_dpll_get(zldev, synth);
|
||||
return zl3073x_synth_dpll_get(synth);
|
||||
}
|
||||
|
||||
/**
|
||||
* zl3073x_out_is_diff - check if the given output is differential
|
||||
* zl3073x_dev_out_is_diff - check if the given output is differential
|
||||
* @zldev: pointer to zl3073x device
|
||||
* @index: output index
|
||||
*
|
||||
* Return: true if output is differential, false if output is single-ended
|
||||
*/
|
||||
static inline bool
|
||||
zl3073x_out_is_diff(struct zl3073x_dev *zldev, u8 index)
|
||||
zl3073x_dev_out_is_diff(struct zl3073x_dev *zldev, u8 index)
|
||||
{
|
||||
switch (zl3073x_out_signal_format_get(zldev, index)) {
|
||||
case ZL_OUTPUT_MODE_SIGNAL_FORMAT_LVDS:
|
||||
case ZL_OUTPUT_MODE_SIGNAL_FORMAT_DIFF:
|
||||
case ZL_OUTPUT_MODE_SIGNAL_FORMAT_LOWVCM:
|
||||
return true;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
const struct zl3073x_out *out = zl3073x_out_state_get(zldev, index);
|
||||
|
||||
return false;
|
||||
return zl3073x_out_is_diff(out);
|
||||
}
|
||||
|
||||
/**
|
||||
* zl3073x_output_pin_is_enabled - check if the given output pin is enabled
|
||||
* zl3073x_dev_output_pin_is_enabled - check if the given output pin is enabled
|
||||
* @zldev: pointer to zl3073x device
|
||||
* @id: output pin id
|
||||
*
|
||||
@@ -392,16 +327,21 @@ zl3073x_out_is_diff(struct zl3073x_dev *zldev, u8 index)
|
||||
* Return: true if output pin is enabled, false if output pin is disabled
|
||||
*/
|
||||
static inline bool
|
||||
zl3073x_output_pin_is_enabled(struct zl3073x_dev *zldev, u8 id)
|
||||
zl3073x_dev_output_pin_is_enabled(struct zl3073x_dev *zldev, u8 id)
|
||||
{
|
||||
u8 output = zl3073x_output_pin_out_get(id);
|
||||
u8 out_id = zl3073x_output_pin_out_get(id);
|
||||
const struct zl3073x_out *out;
|
||||
|
||||
/* Check if the whole output is enabled */
|
||||
if (!zl3073x_out_is_enabled(zldev, output))
|
||||
out = zl3073x_out_state_get(zldev, out_id);
|
||||
|
||||
/* Check if the output is enabled - call _dev_ helper that
|
||||
* additionally checks for attached synth enablement.
|
||||
*/
|
||||
if (!zl3073x_dev_out_is_enabled(zldev, out_id))
|
||||
return false;
|
||||
|
||||
/* Check signal format */
|
||||
switch (zl3073x_out_signal_format_get(zldev, output)) {
|
||||
switch (zl3073x_out_signal_format_get(out)) {
|
||||
case ZL_OUTPUT_MODE_SIGNAL_FORMAT_DISABLED:
|
||||
/* Both output pins are disabled by signal format */
|
||||
return false;
|
||||
|
||||
+184
-592
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,157 @@
|
||||
// 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 "out.h"
|
||||
|
||||
/**
|
||||
* zl3073x_out_state_fetch - fetch output state from hardware
|
||||
* @zldev: pointer to zl3073x_dev structure
|
||||
* @index: output index to fetch state for
|
||||
*
|
||||
* Function fetches state of the given output from hardware and stores it
|
||||
* for later use.
|
||||
*
|
||||
* Return: 0 on success, <0 on error
|
||||
*/
|
||||
int zl3073x_out_state_fetch(struct zl3073x_dev *zldev, u8 index)
|
||||
{
|
||||
struct zl3073x_out *out = &zldev->out[index];
|
||||
int rc;
|
||||
|
||||
/* Read output configuration */
|
||||
rc = zl3073x_read_u8(zldev, ZL_REG_OUTPUT_CTRL(index), &out->ctrl);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
dev_dbg(zldev->dev, "OUT%u is %s and connected to SYNTH%u\n", index,
|
||||
str_enabled_disabled(zl3073x_out_is_enabled(out)),
|
||||
zl3073x_out_synth_get(out));
|
||||
|
||||
guard(mutex)(&zldev->multiop_lock);
|
||||
|
||||
/* Read output configuration */
|
||||
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;
|
||||
|
||||
/* Read output mode */
|
||||
rc = zl3073x_read_u8(zldev, ZL_REG_OUTPUT_MODE, &out->mode);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* zl3073x_out_state_get - get current output state
|
||||
* @zldev: pointer to zl3073x_dev structure
|
||||
* @index: output index to get state for
|
||||
*
|
||||
* Return: pointer to given output state
|
||||
*/
|
||||
const struct zl3073x_out *zl3073x_out_state_get(struct zl3073x_dev *zldev,
|
||||
u8 index)
|
||||
{
|
||||
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;
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
|
||||
#ifndef _ZL3073X_OUT_H
|
||||
#define _ZL3073X_OUT_H
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#include "regs.h"
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
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
|
||||
*
|
||||
* Return: signal format of given output
|
||||
*/
|
||||
static inline u8 zl3073x_out_signal_format_get(const struct zl3073x_out *out)
|
||||
{
|
||||
return FIELD_GET(ZL_OUTPUT_MODE_SIGNAL_FORMAT, out->mode);
|
||||
}
|
||||
|
||||
/**
|
||||
* zl3073x_out_is_diff - check if the given output is differential
|
||||
* @out: pointer to out state
|
||||
*
|
||||
* Return: true if output is differential, false if output is single-ended
|
||||
*/
|
||||
static inline bool zl3073x_out_is_diff(const struct zl3073x_out *out)
|
||||
{
|
||||
switch (zl3073x_out_signal_format_get(out)) {
|
||||
case ZL_OUTPUT_MODE_SIGNAL_FORMAT_LVDS:
|
||||
case ZL_OUTPUT_MODE_SIGNAL_FORMAT_DIFF:
|
||||
case ZL_OUTPUT_MODE_SIGNAL_FORMAT_LOWVCM:
|
||||
return true;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* zl3073x_out_is_enabled - check if the given output is enabled
|
||||
* @out: pointer to out state
|
||||
*
|
||||
* Return: true if output is enabled, false if output is disabled
|
||||
*/
|
||||
static inline bool zl3073x_out_is_enabled(const struct zl3073x_out *out)
|
||||
{
|
||||
return !!FIELD_GET(ZL_OUTPUT_CTRL_EN, out->ctrl);
|
||||
}
|
||||
|
||||
/**
|
||||
* zl3073x_out_synth_get - get synth connected to given output
|
||||
* @out: pointer to out state
|
||||
*
|
||||
* Return: index of synth connected to given output.
|
||||
*/
|
||||
static inline u8 zl3073x_out_synth_get(const struct zl3073x_out *out)
|
||||
{
|
||||
return FIELD_GET(ZL_OUTPUT_CTRL_SYNTH_SEL, out->ctrl);
|
||||
}
|
||||
|
||||
#endif /* _ZL3073X_OUT_H */
|
||||
@@ -46,10 +46,10 @@ zl3073x_pin_check_freq(struct zl3073x_dev *zldev, enum dpll_pin_direction dir,
|
||||
|
||||
/* Get output pin synthesizer */
|
||||
out = zl3073x_output_pin_out_get(id);
|
||||
synth = zl3073x_out_synth_get(zldev, out);
|
||||
synth = zl3073x_dev_out_synth_get(zldev, out);
|
||||
|
||||
/* Get synth frequency */
|
||||
synth_freq = zl3073x_synth_freq_get(zldev, synth);
|
||||
synth_freq = zl3073x_dev_synth_freq_get(zldev, synth);
|
||||
|
||||
/* Check the frequency divides synth frequency */
|
||||
if (synth_freq % (u32)freq)
|
||||
@@ -93,13 +93,13 @@ zl3073x_prop_pin_package_label_set(struct zl3073x_dev *zldev,
|
||||
|
||||
prefix = "REF";
|
||||
ref = zl3073x_input_pin_ref_get(id);
|
||||
is_diff = zl3073x_ref_is_diff(zldev, ref);
|
||||
is_diff = zl3073x_dev_ref_is_diff(zldev, ref);
|
||||
} else {
|
||||
u8 out;
|
||||
|
||||
prefix = "OUT";
|
||||
out = zl3073x_output_pin_out_get(id);
|
||||
is_diff = zl3073x_out_is_diff(zldev, out);
|
||||
is_diff = zl3073x_dev_out_is_diff(zldev, out);
|
||||
}
|
||||
|
||||
if (!is_diff)
|
||||
@@ -217,8 +217,8 @@ struct zl3073x_pin_props *zl3073x_pin_props_get(struct zl3073x_dev *zldev,
|
||||
* the synth frequency count.
|
||||
*/
|
||||
out = zl3073x_output_pin_out_get(index);
|
||||
synth = zl3073x_out_synth_get(zldev, out);
|
||||
f = 2 * zl3073x_synth_freq_get(zldev, synth);
|
||||
synth = zl3073x_dev_out_synth_get(zldev, out);
|
||||
f = 2 * zl3073x_dev_synth_freq_get(zldev, synth);
|
||||
props->dpll_props.phase_gran = f ? div_u64(PSEC_PER_SEC, f) : 1;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,204 @@
|
||||
// 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;
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
|
||||
#ifndef _ZL3073X_REF_H
|
||||
#define _ZL3073X_REF_H
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/math64.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#include "regs.h"
|
||||
|
||||
struct zl3073x_dev;
|
||||
|
||||
/**
|
||||
* struct zl3073x_ref - input reference state
|
||||
* @ffo: current fractional frequency offset
|
||||
* @phase_comp: phase compensation
|
||||
* @esync_n_div: divisor for embedded sync or n-divided signal formats
|
||||
* @freq_base: frequency base
|
||||
* @freq_mult: frequnecy multiplier
|
||||
* @freq_ratio_m: FEC mode multiplier
|
||||
* @freq_ratio_n: FEC mode divisor
|
||||
* @config: reference config
|
||||
* @sync_ctrl: reference sync control
|
||||
* @mon_status: reference monitor status
|
||||
*/
|
||||
struct zl3073x_ref {
|
||||
s64 ffo;
|
||||
u64 phase_comp;
|
||||
u32 esync_n_div;
|
||||
u16 freq_base;
|
||||
u16 freq_mult;
|
||||
u16 freq_ratio_m;
|
||||
u16 freq_ratio_n;
|
||||
u8 config;
|
||||
u8 sync_ctrl;
|
||||
u8 mon_status;
|
||||
};
|
||||
|
||||
int zl3073x_ref_state_fetch(struct zl3073x_dev *zldev, u8 index);
|
||||
|
||||
const struct zl3073x_ref *zl3073x_ref_state_get(struct zl3073x_dev *zldev,
|
||||
u8 index);
|
||||
|
||||
int zl3073x_ref_state_set(struct zl3073x_dev *zldev, u8 index,
|
||||
const struct zl3073x_ref *ref);
|
||||
|
||||
int zl3073x_ref_freq_factorize(u32 freq, u16 *base, u16 *mult);
|
||||
|
||||
/**
|
||||
* zl3073x_ref_ffo_get - get current fractional frequency offset
|
||||
* @ref: pointer to ref state
|
||||
*
|
||||
* Return: the latest measured fractional frequency offset
|
||||
*/
|
||||
static inline s64
|
||||
zl3073x_ref_ffo_get(const struct zl3073x_ref *ref)
|
||||
{
|
||||
return ref->ffo;
|
||||
}
|
||||
|
||||
/**
|
||||
* zl3073x_ref_freq_get - get given input reference frequency
|
||||
* @ref: pointer to ref state
|
||||
*
|
||||
* Return: frequency of the given input reference
|
||||
*/
|
||||
static inline u32
|
||||
zl3073x_ref_freq_get(const struct zl3073x_ref *ref)
|
||||
{
|
||||
return mul_u64_u32_div(ref->freq_base * ref->freq_mult,
|
||||
ref->freq_ratio_m, ref->freq_ratio_n);
|
||||
}
|
||||
|
||||
/**
|
||||
* zl3073x_ref_freq_set - set given input reference frequency
|
||||
* @ref: pointer to ref state
|
||||
* @freq: frequency to be set
|
||||
*
|
||||
* Return: 0 on success, <0 when frequency cannot be factorized
|
||||
*/
|
||||
static inline int
|
||||
zl3073x_ref_freq_set(struct zl3073x_ref *ref, u32 freq)
|
||||
{
|
||||
u16 base, mult;
|
||||
int rc;
|
||||
|
||||
rc = zl3073x_ref_freq_factorize(freq, &base, &mult);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
ref->freq_base = base;
|
||||
ref->freq_mult = mult;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* zl3073x_ref_is_diff - check if the given input reference is differential
|
||||
* @ref: pointer to ref state
|
||||
*
|
||||
* Return: true if reference is differential, false if reference is single-ended
|
||||
*/
|
||||
static inline bool
|
||||
zl3073x_ref_is_diff(const struct zl3073x_ref *ref)
|
||||
{
|
||||
return !!FIELD_GET(ZL_REF_CONFIG_DIFF_EN, ref->config);
|
||||
}
|
||||
|
||||
/**
|
||||
* zl3073x_ref_is_enabled - check if the given input reference is enabled
|
||||
* @ref: pointer to ref state
|
||||
*
|
||||
* Return: true if input refernce is enabled, false otherwise
|
||||
*/
|
||||
static inline bool
|
||||
zl3073x_ref_is_enabled(const struct zl3073x_ref *ref)
|
||||
{
|
||||
return !!FIELD_GET(ZL_REF_CONFIG_ENABLE, ref->config);
|
||||
}
|
||||
|
||||
/**
|
||||
* zl3073x_ref_is_status_ok - check the given input reference status
|
||||
* @ref: pointer to ref state
|
||||
*
|
||||
* Return: true if the status is ok, false otherwise
|
||||
*/
|
||||
static inline bool
|
||||
zl3073x_ref_is_status_ok(const struct zl3073x_ref *ref)
|
||||
{
|
||||
return ref->mon_status == ZL_REF_MON_STATUS_OK;
|
||||
}
|
||||
|
||||
#endif /* _ZL3073X_REF_H */
|
||||
@@ -0,0 +1,87 @@
|
||||
// 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 "synth.h"
|
||||
|
||||
/**
|
||||
* zl3073x_synth_state_fetch - fetch synth state from hardware
|
||||
* @zldev: pointer to zl3073x_dev structure
|
||||
* @index: synth index to fetch state for
|
||||
*
|
||||
* Function fetches state of the given synthesizer from the hardware and
|
||||
* stores it for later use.
|
||||
*
|
||||
* Return: 0 on success, <0 on error
|
||||
*/
|
||||
int zl3073x_synth_state_fetch(struct zl3073x_dev *zldev, u8 index)
|
||||
{
|
||||
struct zl3073x_synth *synth = &zldev->synth[index];
|
||||
int rc;
|
||||
|
||||
/* Read synth control register */
|
||||
rc = zl3073x_read_u8(zldev, ZL_REG_SYNTH_CTRL(index), &synth->ctrl);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
guard(mutex)(&zldev->multiop_lock);
|
||||
|
||||
/* Read synth configuration */
|
||||
rc = zl3073x_mb_op(zldev, ZL_REG_SYNTH_MB_SEM, ZL_SYNTH_MB_SEM_RD,
|
||||
ZL_REG_SYNTH_MB_MASK, BIT(index));
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/* The output frequency is determined by the following formula:
|
||||
* base * multiplier * numerator / denominator
|
||||
*
|
||||
* Read registers with these values
|
||||
*/
|
||||
rc = zl3073x_read_u16(zldev, ZL_REG_SYNTH_FREQ_BASE, &synth->freq_base);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
rc = zl3073x_read_u32(zldev, ZL_REG_SYNTH_FREQ_MULT, &synth->freq_mult);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
rc = zl3073x_read_u16(zldev, ZL_REG_SYNTH_FREQ_M, &synth->freq_m);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
rc = zl3073x_read_u16(zldev, ZL_REG_SYNTH_FREQ_N, &synth->freq_n);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/* Check denominator for zero to avoid div by 0 */
|
||||
if (!synth->freq_n) {
|
||||
dev_err(zldev->dev,
|
||||
"Zero divisor for SYNTH%u retrieved from device\n",
|
||||
index);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
dev_dbg(zldev->dev, "SYNTH%u frequency: %u Hz\n", index,
|
||||
zl3073x_synth_freq_get(synth));
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* zl3073x_synth_state_get - get current synth state
|
||||
* @zldev: pointer to zl3073x_dev structure
|
||||
* @index: synth index to get state for
|
||||
*
|
||||
* Return: pointer to given synth state
|
||||
*/
|
||||
const struct zl3073x_synth *zl3073x_synth_state_get(struct zl3073x_dev *zldev,
|
||||
u8 index)
|
||||
{
|
||||
return &zldev->synth[index];
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
|
||||
#ifndef _ZL3073X_SYNTH_H
|
||||
#define _ZL3073X_SYNTH_H
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/math64.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#include "regs.h"
|
||||
|
||||
struct zl3073x_dev;
|
||||
|
||||
/**
|
||||
* struct zl3073x_synth - synthesizer state
|
||||
* @freq_mult: frequency multiplier
|
||||
* @freq_base: frequency base
|
||||
* @freq_m: frequency numerator
|
||||
* @freq_n: frequency denominator
|
||||
* @ctrl: synth control
|
||||
*/
|
||||
struct zl3073x_synth {
|
||||
u32 freq_mult;
|
||||
u16 freq_base;
|
||||
u16 freq_m;
|
||||
u16 freq_n;
|
||||
u8 ctrl;
|
||||
};
|
||||
|
||||
int zl3073x_synth_state_fetch(struct zl3073x_dev *zldev, u8 synth_id);
|
||||
|
||||
const struct zl3073x_synth *zl3073x_synth_state_get(struct zl3073x_dev *zldev,
|
||||
u8 synth_id);
|
||||
|
||||
int zl3073x_synth_state_set(struct zl3073x_dev *zldev, u8 synth_id,
|
||||
const struct zl3073x_synth *synth);
|
||||
|
||||
/**
|
||||
* zl3073x_synth_dpll_get - get DPLL ID the synth is driven by
|
||||
* @synth: pointer to synth state
|
||||
*
|
||||
* Return: ID of DPLL the given synthetizer is driven by
|
||||
*/
|
||||
static inline u8 zl3073x_synth_dpll_get(const struct zl3073x_synth *synth)
|
||||
{
|
||||
return FIELD_GET(ZL_SYNTH_CTRL_DPLL_SEL, synth->ctrl);
|
||||
}
|
||||
|
||||
/**
|
||||
* zl3073x_synth_freq_get - get synth current freq
|
||||
* @synth: pointer to synth state
|
||||
*
|
||||
* Return: frequency of given synthetizer
|
||||
*/
|
||||
static inline u32 zl3073x_synth_freq_get(const struct zl3073x_synth *synth)
|
||||
{
|
||||
return mul_u64_u32_div(synth->freq_base * synth->freq_m,
|
||||
synth->freq_mult, synth->freq_n);
|
||||
}
|
||||
|
||||
/**
|
||||
* zl3073x_synth_is_enabled - check if the given synth is enabled
|
||||
* @synth: pointer to synth state
|
||||
*
|
||||
* Return: true if synth is enabled, false otherwise
|
||||
*/
|
||||
static inline bool zl3073x_synth_is_enabled(const struct zl3073x_synth *synth)
|
||||
{
|
||||
return FIELD_GET(ZL_SYNTH_CTRL_EN, synth->ctrl);
|
||||
}
|
||||
|
||||
#endif /* _ZL3073X_SYNTH_H */
|
||||
Reference in New Issue
Block a user