Skip to content

Commit da1c7ca

Browse files
committed
dpll: zl3073x: Add support to get/set frequency on pins
jira KERNEL-318 Rebuild_History Non-Buildable kernel-6.12.0-124.20.1.el10_1 commit-author Ivan Vecera <ivecera@redhat.com> commit ce26d7c Add support to get/set frequency on pins. The frequency for input pins (references) is computed in the device according this formula: freq = base_freq * multiplier * (nominator / denominator) where the base_freq comes from the list of supported base frequencies and other parameters are arbitrary numbers. All these parameters are 16-bit unsigned integers. The frequency for output pin is determined by the frequency of synthesizer the output pin is connected to and divisor of the output to which is the given pin belongs. The resulting frequency of the P-pin and the N-pin from this output pair depends on the signal format of this output pair. The device supports so-called N-divided signal formats where for the N-pin there is an additional divisor. The frequencies for both pins from such output pair are computed: P-pin-freq = synth_freq / output_div N-pin-freq = synth_freq / output_div / n_div For other signal-format types both P and N pin have the same frequency based only synth frequency and output divisor. Implement output pin callbacks to get and set frequency. The frequency setting for the output non-N-divided signal format is simple as we have to compute just new output divisor. For N-divided formats it is more complex because by changing of output divisor we change frequency for both P and N pins. In this case if we are changing frequency for P-pin we have to compute also new N-divisor for N-pin to keep its current frequency. From this and the above it follows that the frequency of the N-pin cannot be higher than the frequency of the P-pin and the callback must take this limitation into account. Co-developed-by: Prathosh Satish <Prathosh.Satish@microchip.com> Signed-off-by: Prathosh Satish <Prathosh.Satish@microchip.com> Signed-off-by: Ivan Vecera <ivecera@redhat.com> Reviewed-by: Jiri Pirko <jiri@nvidia.com> Link: https://patch.msgid.link/20250704182202.1641943-13-ivecera@redhat.com Signed-off-by: Jakub Kicinski <kuba@kernel.org> (cherry picked from commit ce26d7c) Signed-off-by: Jonathan Maple <jmaple@ciq.com>
1 parent 9496cba commit da1c7ca

File tree

2 files changed

+359
-0
lines changed

2 files changed

+359
-0
lines changed

drivers/dpll/zl3073x/dpll.c

Lines changed: 349 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include <linux/dpll.h>
99
#include <linux/err.h>
1010
#include <linux/kthread.h>
11+
#include <linux/math64.h>
1112
#include <linux/mod_devicetable.h>
1213
#include <linux/module.h>
1314
#include <linux/netlink.h>
@@ -84,6 +85,127 @@ zl3073x_dpll_pin_direction_get(const struct dpll_pin *dpll_pin, void *pin_priv,
8485
return 0;
8586
}
8687

88+
/**
89+
* zl3073x_dpll_input_ref_frequency_get - get input reference frequency
90+
* @zldpll: pointer to zl3073x_dpll
91+
* @ref_id: reference id
92+
* @frequency: pointer to variable to store frequency
93+
*
94+
* Reads frequency of given input reference.
95+
*
96+
* Return: 0 on success, <0 on error
97+
*/
98+
static int
99+
zl3073x_dpll_input_ref_frequency_get(struct zl3073x_dpll *zldpll, u8 ref_id,
100+
u32 *frequency)
101+
{
102+
struct zl3073x_dev *zldev = zldpll->dev;
103+
u16 base, mult, num, denom;
104+
int rc;
105+
106+
guard(mutex)(&zldev->multiop_lock);
107+
108+
/* Read reference configuration */
109+
rc = zl3073x_mb_op(zldev, ZL_REG_REF_MB_SEM, ZL_REF_MB_SEM_RD,
110+
ZL_REG_REF_MB_MASK, BIT(ref_id));
111+
if (rc)
112+
return rc;
113+
114+
/* Read registers to compute resulting frequency */
115+
rc = zl3073x_read_u16(zldev, ZL_REG_REF_FREQ_BASE, &base);
116+
if (rc)
117+
return rc;
118+
rc = zl3073x_read_u16(zldev, ZL_REG_REF_FREQ_MULT, &mult);
119+
if (rc)
120+
return rc;
121+
rc = zl3073x_read_u16(zldev, ZL_REG_REF_RATIO_M, &num);
122+
if (rc)
123+
return rc;
124+
rc = zl3073x_read_u16(zldev, ZL_REG_REF_RATIO_N, &denom);
125+
if (rc)
126+
return rc;
127+
128+
/* Sanity check that HW has not returned zero denominator */
129+
if (!denom) {
130+
dev_err(zldev->dev,
131+
"Zero divisor for ref %u frequency got from device\n",
132+
ref_id);
133+
return -EINVAL;
134+
}
135+
136+
/* Compute the frequency */
137+
*frequency = mul_u64_u32_div(base * mult, num, denom);
138+
139+
return rc;
140+
}
141+
142+
static int
143+
zl3073x_dpll_input_pin_frequency_get(const struct dpll_pin *dpll_pin,
144+
void *pin_priv,
145+
const struct dpll_device *dpll,
146+
void *dpll_priv, u64 *frequency,
147+
struct netlink_ext_ack *extack)
148+
{
149+
struct zl3073x_dpll *zldpll = dpll_priv;
150+
struct zl3073x_dpll_pin *pin = pin_priv;
151+
u32 ref_freq;
152+
u8 ref;
153+
int rc;
154+
155+
/* Read and return ref frequency */
156+
ref = zl3073x_input_pin_ref_get(pin->id);
157+
rc = zl3073x_dpll_input_ref_frequency_get(zldpll, ref, &ref_freq);
158+
if (!rc)
159+
*frequency = ref_freq;
160+
161+
return rc;
162+
}
163+
164+
static int
165+
zl3073x_dpll_input_pin_frequency_set(const struct dpll_pin *dpll_pin,
166+
void *pin_priv,
167+
const struct dpll_device *dpll,
168+
void *dpll_priv, u64 frequency,
169+
struct netlink_ext_ack *extack)
170+
{
171+
struct zl3073x_dpll *zldpll = dpll_priv;
172+
struct zl3073x_dev *zldev = zldpll->dev;
173+
struct zl3073x_dpll_pin *pin = pin_priv;
174+
u16 base, mult;
175+
u8 ref;
176+
int rc;
177+
178+
/* Get base frequency and multiplier for the requested frequency */
179+
rc = zl3073x_ref_freq_factorize(frequency, &base, &mult);
180+
if (rc)
181+
return rc;
182+
183+
guard(mutex)(&zldev->multiop_lock);
184+
185+
/* Load reference configuration */
186+
ref = zl3073x_input_pin_ref_get(pin->id);
187+
rc = zl3073x_mb_op(zldev, ZL_REG_REF_MB_SEM, ZL_REF_MB_SEM_RD,
188+
ZL_REG_REF_MB_MASK, BIT(ref));
189+
190+
/* Update base frequency, multiplier, numerator & denominator */
191+
rc = zl3073x_write_u16(zldev, ZL_REG_REF_FREQ_BASE, base);
192+
if (rc)
193+
return rc;
194+
rc = zl3073x_write_u16(zldev, ZL_REG_REF_FREQ_MULT, mult);
195+
if (rc)
196+
return rc;
197+
rc = zl3073x_write_u16(zldev, ZL_REG_REF_RATIO_M, 1);
198+
if (rc)
199+
return rc;
200+
rc = zl3073x_write_u16(zldev, ZL_REG_REF_RATIO_N, 1);
201+
if (rc)
202+
return rc;
203+
204+
/* Commit reference configuration */
205+
return zl3073x_mb_op(zldev, ZL_REG_REF_MB_SEM, ZL_REF_MB_SEM_WR,
206+
ZL_REG_REF_MB_MASK, BIT(ref));
207+
}
208+
87209
/**
88210
* zl3073x_dpll_selected_ref_get - get currently selected reference
89211
* @zldpll: pointer to zl3073x_dpll
@@ -518,6 +640,229 @@ zl3073x_dpll_input_pin_prio_set(const struct dpll_pin *dpll_pin, void *pin_priv,
518640
return 0;
519641
}
520642

643+
static int
644+
zl3073x_dpll_output_pin_frequency_get(const struct dpll_pin *dpll_pin,
645+
void *pin_priv,
646+
const struct dpll_device *dpll,
647+
void *dpll_priv, u64 *frequency,
648+
struct netlink_ext_ack *extack)
649+
{
650+
struct zl3073x_dpll *zldpll = dpll_priv;
651+
struct zl3073x_dev *zldev = zldpll->dev;
652+
struct zl3073x_dpll_pin *pin = pin_priv;
653+
struct device *dev = zldev->dev;
654+
u8 out, signal_format, synth;
655+
u32 output_div, synth_freq;
656+
int rc;
657+
658+
out = zl3073x_output_pin_out_get(pin->id);
659+
synth = zl3073x_out_synth_get(zldev, out);
660+
synth_freq = zl3073x_synth_freq_get(zldev, synth);
661+
662+
guard(mutex)(&zldev->multiop_lock);
663+
664+
/* Read output configuration into mailbox */
665+
rc = zl3073x_mb_op(zldev, ZL_REG_OUTPUT_MB_SEM, ZL_OUTPUT_MB_SEM_RD,
666+
ZL_REG_OUTPUT_MB_MASK, BIT(out));
667+
if (rc)
668+
return rc;
669+
670+
rc = zl3073x_read_u32(zldev, ZL_REG_OUTPUT_DIV, &output_div);
671+
if (rc)
672+
return rc;
673+
674+
/* Check output divisor for zero */
675+
if (!output_div) {
676+
dev_err(dev, "Zero divisor for output %u got from device\n",
677+
out);
678+
return -EINVAL;
679+
}
680+
681+
/* Read used signal format for the given output */
682+
signal_format = zl3073x_out_signal_format_get(zldev, out);
683+
684+
switch (signal_format) {
685+
case ZL_OUTPUT_MODE_SIGNAL_FORMAT_2_NDIV:
686+
case ZL_OUTPUT_MODE_SIGNAL_FORMAT_2_NDIV_INV:
687+
/* In case of divided format we have to distiguish between
688+
* given output pin type.
689+
*/
690+
if (zl3073x_dpll_is_p_pin(pin)) {
691+
/* For P-pin the resulting frequency is computed as
692+
* simple division of synth frequency and output
693+
* divisor.
694+
*/
695+
*frequency = synth_freq / output_div;
696+
} else {
697+
/* For N-pin we have to divide additionally by
698+
* divisor stored in esync_period output mailbox
699+
* register that is used as N-pin divisor for these
700+
* modes.
701+
*/
702+
u32 ndiv;
703+
704+
rc = zl3073x_read_u32(zldev, ZL_REG_OUTPUT_ESYNC_PERIOD,
705+
&ndiv);
706+
if (rc)
707+
return rc;
708+
709+
/* Check N-pin divisor for zero */
710+
if (!ndiv) {
711+
dev_err(dev,
712+
"Zero N-pin divisor for output %u got from device\n",
713+
out);
714+
return -EINVAL;
715+
}
716+
717+
/* Compute final divisor for N-pin */
718+
*frequency = synth_freq / output_div / ndiv;
719+
}
720+
break;
721+
default:
722+
/* In other modes the resulting frequency is computed as
723+
* division of synth frequency and output divisor.
724+
*/
725+
*frequency = synth_freq / output_div;
726+
break;
727+
}
728+
729+
return rc;
730+
}
731+
732+
static int
733+
zl3073x_dpll_output_pin_frequency_set(const struct dpll_pin *dpll_pin,
734+
void *pin_priv,
735+
const struct dpll_device *dpll,
736+
void *dpll_priv, u64 frequency,
737+
struct netlink_ext_ack *extack)
738+
{
739+
struct zl3073x_dpll *zldpll = dpll_priv;
740+
struct zl3073x_dev *zldev = zldpll->dev;
741+
struct zl3073x_dpll_pin *pin = pin_priv;
742+
struct device *dev = zldev->dev;
743+
u32 output_n_freq, output_p_freq;
744+
u8 out, signal_format, synth;
745+
u32 cur_div, new_div, ndiv;
746+
u32 synth_freq;
747+
int rc;
748+
749+
out = zl3073x_output_pin_out_get(pin->id);
750+
synth = zl3073x_out_synth_get(zldev, out);
751+
synth_freq = zl3073x_synth_freq_get(zldev, synth);
752+
new_div = synth_freq / (u32)frequency;
753+
754+
/* Get used signal format for the given output */
755+
signal_format = zl3073x_out_signal_format_get(zldev, out);
756+
757+
guard(mutex)(&zldev->multiop_lock);
758+
759+
/* Load output configuration */
760+
rc = zl3073x_mb_op(zldev, ZL_REG_OUTPUT_MB_SEM, ZL_OUTPUT_MB_SEM_RD,
761+
ZL_REG_OUTPUT_MB_MASK, BIT(out));
762+
if (rc)
763+
return rc;
764+
765+
/* Check signal format */
766+
if (signal_format != ZL_OUTPUT_MODE_SIGNAL_FORMAT_2_NDIV &&
767+
signal_format != ZL_OUTPUT_MODE_SIGNAL_FORMAT_2_NDIV_INV) {
768+
/* For non N-divided signal formats the frequency is computed
769+
* as division of synth frequency and output divisor.
770+
*/
771+
rc = zl3073x_write_u32(zldev, ZL_REG_OUTPUT_DIV, new_div);
772+
if (rc)
773+
return rc;
774+
775+
/* For 50/50 duty cycle the divisor is equal to width */
776+
rc = zl3073x_write_u32(zldev, ZL_REG_OUTPUT_WIDTH, new_div);
777+
if (rc)
778+
return rc;
779+
780+
/* Commit output configuration */
781+
return zl3073x_mb_op(zldev,
782+
ZL_REG_OUTPUT_MB_SEM, ZL_OUTPUT_MB_SEM_WR,
783+
ZL_REG_OUTPUT_MB_MASK, BIT(out));
784+
}
785+
786+
/* For N-divided signal format get current divisor */
787+
rc = zl3073x_read_u32(zldev, ZL_REG_OUTPUT_DIV, &cur_div);
788+
if (rc)
789+
return rc;
790+
791+
/* Check output divisor for zero */
792+
if (!cur_div) {
793+
dev_err(dev, "Zero divisor for output %u got from device\n",
794+
out);
795+
return -EINVAL;
796+
}
797+
798+
/* Get N-pin divisor (shares the same register with esync */
799+
rc = zl3073x_read_u32(zldev, ZL_REG_OUTPUT_ESYNC_PERIOD, &ndiv);
800+
if (rc)
801+
return rc;
802+
803+
/* Check N-pin divisor for zero */
804+
if (!ndiv) {
805+
dev_err(dev,
806+
"Zero N-pin divisor for output %u got from device\n",
807+
out);
808+
return -EINVAL;
809+
}
810+
811+
/* Compute current output frequency for P-pin */
812+
output_p_freq = synth_freq / cur_div;
813+
814+
/* Compute current N-pin frequency */
815+
output_n_freq = output_p_freq / ndiv;
816+
817+
if (zl3073x_dpll_is_p_pin(pin)) {
818+
/* We are going to change output frequency for P-pin but
819+
* if the requested frequency is less than current N-pin
820+
* frequency then indicate a failure as we are not able
821+
* to compute N-pin divisor to keep its frequency unchanged.
822+
*/
823+
if (frequency <= output_n_freq)
824+
return -EINVAL;
825+
826+
/* Update the output divisor */
827+
rc = zl3073x_write_u32(zldev, ZL_REG_OUTPUT_DIV, new_div);
828+
if (rc)
829+
return rc;
830+
831+
/* For 50/50 duty cycle the divisor is equal to width */
832+
rc = zl3073x_write_u32(zldev, ZL_REG_OUTPUT_WIDTH, new_div);
833+
if (rc)
834+
return rc;
835+
836+
/* Compute new divisor for N-pin */
837+
ndiv = (u32)frequency / output_n_freq;
838+
} else {
839+
/* We are going to change frequency of N-pin but if
840+
* the requested freq is greater or equal than freq of P-pin
841+
* in the output pair we cannot compute divisor for the N-pin.
842+
* In this case indicate a failure.
843+
*/
844+
if (output_p_freq <= frequency)
845+
return -EINVAL;
846+
847+
/* Compute new divisor for N-pin */
848+
ndiv = output_p_freq / (u32)frequency;
849+
}
850+
851+
/* Update divisor for the N-pin */
852+
rc = zl3073x_write_u32(zldev, ZL_REG_OUTPUT_ESYNC_PERIOD, ndiv);
853+
if (rc)
854+
return rc;
855+
856+
/* For 50/50 duty cycle the divisor is equal to width */
857+
rc = zl3073x_write_u32(zldev, ZL_REG_OUTPUT_ESYNC_WIDTH, ndiv);
858+
if (rc)
859+
return rc;
860+
861+
/* Commit output configuration */
862+
return zl3073x_mb_op(zldev, ZL_REG_OUTPUT_MB_SEM, ZL_OUTPUT_MB_SEM_WR,
863+
ZL_REG_OUTPUT_MB_MASK, BIT(out));
864+
}
865+
521866
static int
522867
zl3073x_dpll_output_pin_state_on_dpll_get(const struct dpll_pin *dpll_pin,
523868
void *pin_priv,
@@ -611,6 +956,8 @@ zl3073x_dpll_mode_get(const struct dpll_device *dpll, void *dpll_priv,
611956

612957
static const struct dpll_pin_ops zl3073x_dpll_input_pin_ops = {
613958
.direction_get = zl3073x_dpll_pin_direction_get,
959+
.frequency_get = zl3073x_dpll_input_pin_frequency_get,
960+
.frequency_set = zl3073x_dpll_input_pin_frequency_set,
614961
.prio_get = zl3073x_dpll_input_pin_prio_get,
615962
.prio_set = zl3073x_dpll_input_pin_prio_set,
616963
.state_on_dpll_get = zl3073x_dpll_input_pin_state_on_dpll_get,
@@ -619,6 +966,8 @@ static const struct dpll_pin_ops zl3073x_dpll_input_pin_ops = {
619966

620967
static const struct dpll_pin_ops zl3073x_dpll_output_pin_ops = {
621968
.direction_get = zl3073x_dpll_pin_direction_get,
969+
.frequency_get = zl3073x_dpll_output_pin_frequency_get,
970+
.frequency_set = zl3073x_dpll_output_pin_frequency_set,
622971
.state_on_dpll_get = zl3073x_dpll_output_pin_state_on_dpll_get,
623972
};
624973

0 commit comments

Comments
 (0)