/* SPDX-License-Identifier: GPL-2.0-only */ #ifndef _ZL3073X_CORE_H #define _ZL3073X_CORE_H #include #include #include #include #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 */