blob: 71af2c8001109e2383201b7122c7d813cbaa1ed5 [file] [log] [blame]
/* SPDX-License-Identifier: GPL-2.0-only */
#ifndef _ZL3073X_CORE_H
#define _ZL3073X_CORE_H
#include <linux/kthread.h>
#include <linux/list.h>
#include <linux/mutex.h>
#include <linux/types.h>
#include "regs.h"
struct device;
struct regmap;
struct zl3073x_dpll;
/*
* Hardware limits for ZL3073x chip family
*/
#define ZL3073X_MAX_CHANNELS 5
#define ZL3073X_NUM_REFS 10
#define ZL3073X_NUM_OUTS 10
#define ZL3073X_NUM_SYNTHS 5
#define ZL3073X_NUM_INPUT_PINS ZL3073X_NUM_REFS
#define ZL3073X_NUM_OUTPUT_PINS (ZL3073X_NUM_OUTS * 2)
#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
* @regmap: regmap to access device registers
* @multiop_lock: to serialize multiple register operations
* @clock_id: clock id of the device
* @ref: array of input references' invariants
* @out: array of outs' invariants
* @synth: array of synths' invariants
* @dplls: list of DPLLs
* @kworker: thread for periodic work
* @work: periodic work
*/
struct zl3073x_dev {
struct device *dev;
struct regmap *regmap;
struct mutex multiop_lock;
u64 clock_id;
/* Invariants */
struct zl3073x_ref ref[ZL3073X_NUM_REFS];
struct zl3073x_out out[ZL3073X_NUM_OUTS];
struct zl3073x_synth synth[ZL3073X_NUM_SYNTHS];
/* DPLL channels */
struct list_head dplls;
/* Monitor */
struct kthread_worker *kworker;
struct kthread_delayed_work work;
};
struct zl3073x_chip_info {
const u16 *ids;
size_t num_ids;
int num_channels;
};
extern const struct zl3073x_chip_info zl30731_chip_info;
extern const struct zl3073x_chip_info zl30732_chip_info;
extern const struct zl3073x_chip_info zl30733_chip_info;
extern const struct zl3073x_chip_info zl30734_chip_info;
extern const struct zl3073x_chip_info zl30735_chip_info;
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,
const struct zl3073x_chip_info *chip_info);
/**********************
* Registers operations
**********************/
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);
/*****************
* 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
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_ref_ffo_get - get current fractional frequency offset
* @zldev: pointer to zl3073x device
* @index: input reference index
*
* Return: the latest measured fractional frequency offset
*/
static inline s64
zl3073x_ref_ffo_get(struct zl3073x_dev *zldev, u8 index)
{
return zldev->ref[index].ffo;
}
/**
* zl3073x_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)
{
return zldev->ref[index].diff;
}
/**
* zl3073x_ref_is_enabled - check if the given input reference is enabled
* @zldev: pointer to zl3073x device
* @index: input reference index
*
* Return: true if input refernce is enabled, false otherwise
*/
static inline bool
zl3073x_ref_is_enabled(struct zl3073x_dev *zldev, u8 index)
{
return zldev->ref[index].enabled;
}
/**
* 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
* @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)
{
return zldev->synth[index].freq;
}
/**
* 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
* @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)
{
return zldev->out[index].synth;
}
/**
* zl3073x_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)
{
u8 synth;
/* 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;
return false;
}
/**
* 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
* @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 synth;
/* Get synthesizer connected to given output */
synth = zl3073x_out_synth_get(zldev, index);
/* Return DPLL that drives the synth */
return zl3073x_synth_dpll_get(zldev, synth);
}
/**
* zl3073x_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)
{
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;
}
return false;
}
/**
* zl3073x_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_output_pin_is_enabled(struct zl3073x_dev *zldev, u8 id)
{
u8 output = zl3073x_output_pin_out_get(id);
/* Check if the whole output is enabled */
if (!zl3073x_out_is_enabled(zldev, output))
return false;
/* Check signal format */
switch (zl3073x_out_signal_format_get(zldev, output)) {
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 */