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+
521866static int
522867zl3073x_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
612957static 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
620967static 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