419 lines
11 KiB
C
419 lines
11 KiB
C
/* SPDX-License-Identifier: GPL-2.0-only */
|
|
|
|
#ifndef _ZL3073X_CORE_H
|
|
#define _ZL3073X_CORE_H
|
|
|
|
#include <linux/bitfield.h>
|
|
#include <linux/kthread.h>
|
|
#include <linux/list.h>
|
|
#include <linux/mutex.h>
|
|
#include <linux/types.h>
|
|
|
|
#include "chan.h"
|
|
#include "out.h"
|
|
#include "ref.h"
|
|
#include "regs.h"
|
|
#include "synth.h"
|
|
|
|
struct device;
|
|
struct regmap;
|
|
struct zl3073x_dpll;
|
|
|
|
|
|
enum zl3073x_flags {
|
|
ZL3073X_FLAG_REF_PHASE_COMP_32_BIT,
|
|
ZL3073X_FLAG_DIE_TEMP_BIT,
|
|
ZL3073X_FLAGS_NBITS /* must be last */
|
|
};
|
|
|
|
#define __ZL3073X_FLAG(name) BIT(ZL3073X_FLAG_ ## name ## _BIT)
|
|
#define ZL3073X_FLAG_REF_PHASE_COMP_32 __ZL3073X_FLAG(REF_PHASE_COMP_32)
|
|
#define ZL3073X_FLAG_DIE_TEMP __ZL3073X_FLAG(DIE_TEMP)
|
|
|
|
/**
|
|
* struct zl3073x_chip_info - chip variant identification
|
|
* @id: chip ID
|
|
* @num_channels: number of DPLL channels supported by this variant
|
|
* @flags: chip variant flags
|
|
*/
|
|
struct zl3073x_chip_info {
|
|
u16 id;
|
|
u8 num_channels;
|
|
unsigned long flags;
|
|
};
|
|
|
|
/**
|
|
* struct zl3073x_dev - zl3073x device
|
|
* @dev: pointer to device
|
|
* @regmap: regmap to access device registers
|
|
* @info: detected chip info
|
|
* @multiop_lock: to serialize multiple register operations
|
|
* @ref: array of input references' invariants
|
|
* @out: array of outs' invariants
|
|
* @synth: array of synths' invariants
|
|
* @chan: array of DPLL channels' state
|
|
* @dplls: list of DPLLs
|
|
* @kworker: thread for periodic work
|
|
* @work: periodic work
|
|
* @clock_id: clock id of the device
|
|
* @phase_avg_factor: phase offset measurement averaging factor
|
|
* @freq_monitor: is frequency monitor enabled
|
|
*/
|
|
struct zl3073x_dev {
|
|
struct device *dev;
|
|
struct regmap *regmap;
|
|
const struct zl3073x_chip_info *info;
|
|
struct mutex multiop_lock;
|
|
|
|
/* Invariants */
|
|
struct zl3073x_ref ref[ZL3073X_NUM_REFS];
|
|
struct zl3073x_out out[ZL3073X_NUM_OUTS];
|
|
struct zl3073x_synth synth[ZL3073X_NUM_SYNTHS];
|
|
struct zl3073x_chan chan[ZL3073X_MAX_CHANNELS];
|
|
|
|
/* DPLL channels */
|
|
struct list_head dplls;
|
|
|
|
/* Monitor */
|
|
struct kthread_worker *kworker;
|
|
struct kthread_delayed_work work;
|
|
|
|
/* Per-chip parameters */
|
|
u64 clock_id;
|
|
u8 phase_avg_factor;
|
|
bool freq_monitor;
|
|
};
|
|
|
|
extern const struct regmap_config zl3073x_regmap_config;
|
|
|
|
struct zl3073x_dev *zl3073x_devm_alloc(struct device *dev);
|
|
int zl3073x_dev_probe(struct zl3073x_dev *zldev);
|
|
|
|
int zl3073x_dev_start(struct zl3073x_dev *zldev, bool full);
|
|
void zl3073x_dev_stop(struct zl3073x_dev *zldev);
|
|
|
|
static inline u8 zl3073x_dev_phase_avg_factor_get(struct zl3073x_dev *zldev)
|
|
{
|
|
return zldev->phase_avg_factor;
|
|
}
|
|
|
|
int zl3073x_dev_phase_avg_factor_set(struct zl3073x_dev *zldev, u8 factor);
|
|
|
|
/**********************
|
|
* Registers operations
|
|
**********************/
|
|
|
|
/**
|
|
* struct zl3073x_hwreg_seq_item - HW register write sequence item
|
|
* @addr: HW register to be written
|
|
* @value: value to be written to HW register
|
|
* @mask: bitmask indicating bits to be updated
|
|
* @wait: number of ms to wait after register write
|
|
*/
|
|
struct zl3073x_hwreg_seq_item {
|
|
u32 addr;
|
|
u32 value;
|
|
u32 mask;
|
|
u32 wait;
|
|
};
|
|
|
|
#define HWREG_SEQ_ITEM(_addr, _value, _mask, _wait) \
|
|
{ \
|
|
.addr = _addr, \
|
|
.value = FIELD_PREP_CONST(_mask, _value), \
|
|
.mask = _mask, \
|
|
.wait = _wait, \
|
|
}
|
|
|
|
int zl3073x_mb_op(struct zl3073x_dev *zldev, unsigned int op_reg, u8 op_val,
|
|
unsigned int mask_reg, u16 mask_val);
|
|
int zl3073x_poll_zero_u8(struct zl3073x_dev *zldev, unsigned int reg, u8 mask);
|
|
int zl3073x_read_u8(struct zl3073x_dev *zldev, unsigned int reg, u8 *val);
|
|
int zl3073x_read_u16(struct zl3073x_dev *zldev, unsigned int reg, u16 *val);
|
|
int zl3073x_read_u32(struct zl3073x_dev *zldev, unsigned int reg, u32 *val);
|
|
int zl3073x_read_u48(struct zl3073x_dev *zldev, unsigned int reg, u64 *val);
|
|
int zl3073x_write_u8(struct zl3073x_dev *zldev, unsigned int reg, u8 val);
|
|
int zl3073x_write_u16(struct zl3073x_dev *zldev, unsigned int reg, u16 val);
|
|
int zl3073x_write_u32(struct zl3073x_dev *zldev, unsigned int reg, u32 val);
|
|
int zl3073x_write_u48(struct zl3073x_dev *zldev, unsigned int reg, u64 val);
|
|
int zl3073x_read_hwreg(struct zl3073x_dev *zldev, u32 addr, u32 *value);
|
|
int zl3073x_write_hwreg(struct zl3073x_dev *zldev, u32 addr, u32 value);
|
|
int zl3073x_update_hwreg(struct zl3073x_dev *zldev, u32 addr, u32 value,
|
|
u32 mask);
|
|
int zl3073x_write_hwreg_seq(struct zl3073x_dev *zldev,
|
|
const struct zl3073x_hwreg_seq_item *seq,
|
|
size_t num_items);
|
|
|
|
/*****************
|
|
* Misc operations
|
|
*****************/
|
|
|
|
int zl3073x_ref_phase_offsets_update(struct zl3073x_dev *zldev, int channel);
|
|
|
|
/**
|
|
* zl3073x_dev_is_ref_phase_comp_32bit - check ref phase comp register size
|
|
* @zldev: pointer to zl3073x device
|
|
*
|
|
* Some chip IDs have a 32-bit wide ref_phase_offset_comp register instead
|
|
* of the default 48-bit.
|
|
*
|
|
* Return: true if the register is 32-bit, false if 48-bit
|
|
*/
|
|
static inline bool
|
|
zl3073x_dev_is_ref_phase_comp_32bit(struct zl3073x_dev *zldev)
|
|
{
|
|
return zldev->info->flags & ZL3073X_FLAG_REF_PHASE_COMP_32;
|
|
}
|
|
|
|
static inline bool
|
|
zl3073x_is_n_pin(u8 id)
|
|
{
|
|
/* P-pins ids are even while N-pins are odd */
|
|
return id & 1;
|
|
}
|
|
|
|
static inline bool
|
|
zl3073x_is_p_pin(u8 id)
|
|
{
|
|
return !zl3073x_is_n_pin(id);
|
|
}
|
|
|
|
/**
|
|
* zl3073x_input_pin_ref_get - get reference for given input pin
|
|
* @id: input pin id
|
|
*
|
|
* Return: reference id for the given input pin
|
|
*/
|
|
static inline u8
|
|
zl3073x_input_pin_ref_get(u8 id)
|
|
{
|
|
return id;
|
|
}
|
|
|
|
/**
|
|
* zl3073x_output_pin_out_get - get output for the given output pin
|
|
* @id: output pin id
|
|
*
|
|
* Return: output id for the given output pin
|
|
*/
|
|
static inline u8
|
|
zl3073x_output_pin_out_get(u8 id)
|
|
{
|
|
/* Output pin pair shares the single output */
|
|
return id / 2;
|
|
}
|
|
|
|
/**
|
|
* zl3073x_dev_ref_freq_get - get input reference frequency
|
|
* @zldev: pointer to zl3073x device
|
|
* @index: input reference index
|
|
*
|
|
* Return: frequency of given input reference
|
|
*/
|
|
static inline u32
|
|
zl3073x_dev_ref_freq_get(struct zl3073x_dev *zldev, u8 index)
|
|
{
|
|
const struct zl3073x_ref *ref = zl3073x_ref_state_get(zldev, index);
|
|
|
|
return zl3073x_ref_freq_get(ref);
|
|
}
|
|
|
|
/**
|
|
* 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_dev_ref_is_diff(struct zl3073x_dev *zldev, u8 index)
|
|
{
|
|
const struct zl3073x_ref *ref = zl3073x_ref_state_get(zldev, index);
|
|
|
|
return zl3073x_ref_is_diff(ref);
|
|
}
|
|
|
|
/*
|
|
* zl3073x_dev_ref_is_status_ok - check the given input reference status
|
|
* @zldev: pointer to zl3073x device
|
|
* @index: input reference index
|
|
*
|
|
* Return: true if the status is ok, false otherwise
|
|
*/
|
|
static inline bool
|
|
zl3073x_dev_ref_is_status_ok(struct zl3073x_dev *zldev, u8 index)
|
|
{
|
|
const struct zl3073x_ref *ref = zl3073x_ref_state_get(zldev, index);
|
|
|
|
return zl3073x_ref_is_status_ok(ref);
|
|
}
|
|
|
|
/**
|
|
* 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_dev_synth_freq_get(struct zl3073x_dev *zldev, u8 index)
|
|
{
|
|
const struct zl3073x_synth *synth;
|
|
|
|
synth = zl3073x_synth_state_get(zldev, index);
|
|
return zl3073x_synth_freq_get(synth);
|
|
}
|
|
|
|
/**
|
|
* 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_dev_out_synth_get(struct zl3073x_dev *zldev, u8 index)
|
|
{
|
|
const struct zl3073x_out *out = zl3073x_out_state_get(zldev, index);
|
|
|
|
return zl3073x_out_synth_get(out);
|
|
}
|
|
|
|
/**
|
|
* 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_dev_out_is_enabled(struct zl3073x_dev *zldev, u8 index)
|
|
{
|
|
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_id = zl3073x_out_synth_get(out);
|
|
synth = zl3073x_synth_state_get(zldev, synth_id);
|
|
|
|
return zl3073x_synth_is_enabled(synth) && zl3073x_out_is_enabled(out);
|
|
}
|
|
|
|
/**
|
|
* 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_dev_out_dpll_get(struct zl3073x_dev *zldev, u8 index)
|
|
{
|
|
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_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(synth);
|
|
}
|
|
|
|
/**
|
|
* zl3073x_dev_output_pin_freq_get - get output pin frequency
|
|
* @zldev: pointer to zl3073x device
|
|
* @id: output pin id
|
|
*
|
|
* Computes the output pin frequency based on the synth frequency, output
|
|
* divisor, and signal format. For N-div formats, N-pin frequency is
|
|
* additionally divided by esync_n_period.
|
|
*
|
|
* Return: frequency of the given output pin in Hz
|
|
*/
|
|
static inline u32
|
|
zl3073x_dev_output_pin_freq_get(struct zl3073x_dev *zldev, u8 id)
|
|
{
|
|
const struct zl3073x_synth *synth;
|
|
const struct zl3073x_out *out;
|
|
u8 out_id;
|
|
u32 freq;
|
|
|
|
out_id = zl3073x_output_pin_out_get(id);
|
|
out = zl3073x_out_state_get(zldev, out_id);
|
|
synth = zl3073x_synth_state_get(zldev, zl3073x_out_synth_get(out));
|
|
freq = zl3073x_synth_freq_get(synth) / out->div;
|
|
|
|
if (zl3073x_out_is_ndiv(out) && zl3073x_is_n_pin(id))
|
|
freq /= out->esync_n_period;
|
|
|
|
return freq;
|
|
}
|
|
|
|
/**
|
|
* 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_dev_out_is_diff(struct zl3073x_dev *zldev, u8 index)
|
|
{
|
|
const struct zl3073x_out *out = zl3073x_out_state_get(zldev, index);
|
|
|
|
return zl3073x_out_is_diff(out);
|
|
}
|
|
|
|
/**
|
|
* zl3073x_dev_output_pin_is_enabled - check if the given output pin is enabled
|
|
* @zldev: pointer to zl3073x device
|
|
* @id: output pin id
|
|
*
|
|
* Checks if the output of the given output pin is enabled and also that
|
|
* its signal format also enables the given pin.
|
|
*
|
|
* Return: true if output pin is enabled, false if output pin is disabled
|
|
*/
|
|
static inline bool
|
|
zl3073x_dev_output_pin_is_enabled(struct zl3073x_dev *zldev, u8 id)
|
|
{
|
|
u8 out_id = zl3073x_output_pin_out_get(id);
|
|
const struct zl3073x_out *out;
|
|
|
|
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(out)) {
|
|
case ZL_OUTPUT_MODE_SIGNAL_FORMAT_DISABLED:
|
|
/* Both output pins are disabled by signal format */
|
|
return false;
|
|
|
|
case ZL_OUTPUT_MODE_SIGNAL_FORMAT_1P:
|
|
/* Output is one single ended P-pin output */
|
|
if (zl3073x_is_n_pin(id))
|
|
return false;
|
|
break;
|
|
case ZL_OUTPUT_MODE_SIGNAL_FORMAT_1N:
|
|
/* Output is one single ended N-pin output */
|
|
if (zl3073x_is_p_pin(id))
|
|
return false;
|
|
break;
|
|
default:
|
|
/* For other format both pins are enabled */
|
|
break;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
#endif /* _ZL3073X_CORE_H */
|