root/drivers/dpll/zl3073x/core.h
/* 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 */