@@ -135,50 +135,51 @@ static inline u32 pwm_mediatek_readl(struct pwm_mediatek_chip *chip,
135135 num * chip -> soc -> chanreg_width + offset );
136136}
137137
138- static void pwm_mediatek_enable (struct pwm_chip * chip , struct pwm_device * pwm )
139- {
140- struct pwm_mediatek_chip * pc = to_pwm_mediatek_chip (chip );
141- u32 value ;
142-
143- value = readl (pc -> regs );
144- value |= BIT (pwm -> hwpwm );
145- writel (value , pc -> regs );
146- }
147-
148- static void pwm_mediatek_disable (struct pwm_chip * chip , struct pwm_device * pwm )
149- {
150- struct pwm_mediatek_chip * pc = to_pwm_mediatek_chip (chip );
151- u32 value ;
152-
153- value = readl (pc -> regs );
154- value &= ~BIT (pwm -> hwpwm );
155- writel (value , pc -> regs );
156- }
138+ struct pwm_mediatek_waveform {
139+ u32 enable ;
140+ u32 con ;
141+ u32 width ;
142+ u32 thres ;
143+ };
157144
158- static int pwm_mediatek_config (struct pwm_chip * chip , struct pwm_device * pwm ,
159- u64 duty_ns , u64 period_ns )
145+ static int pwm_mediatek_round_waveform_tohw (struct pwm_chip * chip , struct pwm_device * pwm ,
146+ const struct pwm_waveform * wf , void * _wfhw )
160147{
148+ struct pwm_mediatek_waveform * wfhw = _wfhw ;
161149 struct pwm_mediatek_chip * pc = to_pwm_mediatek_chip (chip );
162150 u32 clkdiv , enable ;
163- u32 reg_width = PWMDWIDTH , reg_thres = PWMTHRES ;
164151 u64 cnt_period , cnt_duty ;
165152 unsigned long clk_rate ;
166- int ret ;
153+ int ret = 0 ;
167154
168- ret = pwm_mediatek_clk_enable (pc , pwm -> hwpwm );
169- if (ret < 0 )
170- return ret ;
155+ if (wf -> period_length_ns == 0 ) {
156+ * wfhw = (typeof (* wfhw )){
157+ .enable = 0 ,
158+ };
171159
172- clk_rate = pc -> clk_pwms [pwm -> hwpwm ].rate ;
160+ return 0 ;
161+ }
162+
163+ if (!pc -> clk_pwms [pwm -> hwpwm ].rate ) {
164+ struct clk * clk = pc -> clk_pwms [pwm -> hwpwm ].clk ;
165+
166+ ret = clk_prepare_enable (clk );
167+ if (ret )
168+ return ret ;
173169
174- /* Make sure we use the bus clock and not the 26MHz clock */
175- if (pc -> soc -> pwm_ck_26m_sel_reg )
176- writel (0 , pc -> regs + pc -> soc -> pwm_ck_26m_sel_reg );
170+ pc -> clk_pwms [pwm -> hwpwm ].rate = clk_get_rate (clk );
177171
178- cnt_period = mul_u64_u64_div_u64 (period_ns , clk_rate , NSEC_PER_SEC );
172+ clk_disable_unprepare (clk );
173+ }
174+
175+ clk_rate = pc -> clk_pwms [pwm -> hwpwm ].rate ;
176+ if (clk_rate == 0 || clk_rate > 1000000000 )
177+ return - EINVAL ;
178+
179+ cnt_period = mul_u64_u64_div_u64 (wf -> period_length_ns , clk_rate , NSEC_PER_SEC );
179180 if (cnt_period == 0 ) {
180- ret = - ERANGE ;
181- goto out ;
181+ cnt_period = 1 ;
182+ ret = 1 ;
182183 }
183184
184185 if (cnt_period > FIELD_MAX (PWMDWIDTH_PERIOD ) + 1 ) {
@@ -193,7 +194,7 @@ static int pwm_mediatek_config(struct pwm_chip *chip, struct pwm_device *pwm,
193194 clkdiv = 0 ;
194195 }
195196
196- cnt_duty = mul_u64_u64_div_u64 (duty_ns , clk_rate , NSEC_PER_SEC ) >> clkdiv ;
197+ cnt_duty = mul_u64_u64_div_u64 (wf -> duty_length_ns , clk_rate , NSEC_PER_SEC ) >> clkdiv ;
197198 if (cnt_duty > cnt_period )
198199 cnt_duty = cnt_period ;
199200
@@ -206,121 +207,187 @@ static int pwm_mediatek_config(struct pwm_chip *chip, struct pwm_device *pwm,
206207
207208 cnt_period -= 1 ;
208209
209- dev_dbg (& chip -> dev , "pwm#%u: %lld/%lld @%lu -> CON: %x, PERIOD: %llx, DUTY: %llx\n" ,
210- pwm -> hwpwm , duty_ns , period_ns , clk_rate , clkdiv , cnt_period , cnt_duty );
210+ dev_dbg (& chip -> dev , "pwm#%u: %lld/%lld @%lu -> ENABLE: %x, CON: %x, PERIOD: %llx, DUTY: %llx\n" ,
211+ pwm -> hwpwm , wf -> duty_length_ns , wf -> period_length_ns , clk_rate ,
212+ enable , clkdiv , cnt_period , cnt_duty );
211213
212- if (pc -> soc -> pwm45_fixup && pwm -> hwpwm > 2 ) {
213- /*
214- * PWM[4,5] has distinct offset for PWMDWIDTH and PWMTHRES
215- * from the other PWMs on MT7623.
216- */
217- reg_width = PWM45DWIDTH_FIXUP ;
218- reg_thres = PWM45THRES_FIXUP ;
219- }
214+ * wfhw = (typeof (* wfhw )){
215+ .enable = enable ,
216+ .con = clkdiv ,
217+ .width = cnt_period ,
218+ .thres = cnt_duty ,
219+ };
220220
221- pwm_mediatek_writel ( pc , pwm -> hwpwm , PWMCON , BIT ( 15 ) | clkdiv ) ;
222- pwm_mediatek_writel ( pc , pwm -> hwpwm , reg_width , cnt_period );
221+ return ret ;
222+ }
223223
224- if (enable ) {
225- pwm_mediatek_writel (pc , pwm -> hwpwm , reg_thres , cnt_duty );
226- pwm_mediatek_enable (chip , pwm );
224+ static int pwm_mediatek_round_waveform_fromhw (struct pwm_chip * chip , struct pwm_device * pwm ,
225+ const void * _wfhw , struct pwm_waveform * wf )
226+ {
227+ const struct pwm_mediatek_waveform * wfhw = _wfhw ;
228+ struct pwm_mediatek_chip * pc = to_pwm_mediatek_chip (chip );
229+ u32 clkdiv , cnt_period , cnt_duty ;
230+ unsigned long clk_rate ;
231+
232+ /*
233+ * When _wfhw was populated, the clock was on, so .rate is
234+ * already set appropriately.
235+ */
236+ clk_rate = pc -> clk_pwms [pwm -> hwpwm ].rate ;
237+
238+ if (wfhw -> enable ) {
239+ clkdiv = FIELD_GET (PWMCON_CLKDIV , wfhw -> con );
240+ cnt_period = FIELD_GET (PWMDWIDTH_PERIOD , wfhw -> width );
241+ cnt_duty = FIELD_GET (PWMTHRES_DUTY , wfhw -> thres );
242+
243+ /*
244+ * cnt_period is a 13 bit value, NSEC_PER_SEC is 30 bits wide
245+ * and clkdiv is less than 8, so the multiplication doesn't
246+ * overflow an u64.
247+ */
248+ * wf = (typeof (* wf )){
249+ .period_length_ns =
250+ DIV_ROUND_UP_ULL ((u64 )(cnt_period + 1 ) * NSEC_PER_SEC << clkdiv , clk_rate ),
251+ .duty_length_ns =
252+ DIV_ROUND_UP_ULL ((u64 )(cnt_duty + 1 ) * NSEC_PER_SEC << clkdiv , clk_rate ),
253+ };
227254 } else {
228- pwm_mediatek_disable (chip , pwm );
229- }
255+ clkdiv = 0 ;
256+ cnt_period = 0 ;
257+ cnt_duty = 0 ;
230258
231- out :
232- pwm_mediatek_clk_disable (pc , pwm -> hwpwm );
259+ /*
260+ * .enable = 0 is also used for too small duty_cycle values, so
261+ * report the HW as being enabled to communicate the minimal
262+ * period.
263+ */
264+ * wf = (typeof (* wf )){
265+ .period_length_ns =
266+ DIV_ROUND_UP_ULL (NSEC_PER_SEC , clk_rate ),
267+ .duty_length_ns = 0 ,
268+ };
269+ };
233270
234- return ret ;
271+ dev_dbg (& chip -> dev , "pwm#%u: ENABLE: %x, CLKDIV: %x, PERIOD: %x, DUTY: %x @%lu -> %lld/%lld\n" ,
272+ pwm -> hwpwm , wfhw -> enable , clkdiv , cnt_period , cnt_duty , clk_rate ,
273+ wf -> duty_length_ns , wf -> period_length_ns );
274+
275+ return 0 ;
235276}
236277
237- static int pwm_mediatek_apply (struct pwm_chip * chip , struct pwm_device * pwm ,
238- const struct pwm_state * state )
278+ static int pwm_mediatek_read_waveform (struct pwm_chip * chip ,
279+ struct pwm_device * pwm , void * _wfhw )
239280{
281+ struct pwm_mediatek_waveform * wfhw = _wfhw ;
240282 struct pwm_mediatek_chip * pc = to_pwm_mediatek_chip (chip );
241- int err ;
283+ u32 enable , clkdiv , cnt_period , cnt_duty ;
284+ u32 reg_width = PWMDWIDTH , reg_thres = PWMTHRES ;
285+ int ret ;
242286
243- if (state -> polarity != PWM_POLARITY_NORMAL )
244- return - EINVAL ;
287+ ret = pwm_mediatek_clk_enable (pc , pwm -> hwpwm );
288+ if (ret < 0 )
289+ return ret ;
245290
246- if (!state -> enabled ) {
247- if (pwm -> state .enabled ) {
248- pwm_mediatek_disable (chip , pwm );
249- pwm_mediatek_clk_disable (pc , pwm -> hwpwm );
291+ enable = readl (pc -> regs ) & BIT (pwm -> hwpwm );
292+
293+ if (enable ) {
294+ if (pc -> soc -> pwm45_fixup && pwm -> hwpwm > 2 ) {
295+ /*
296+ * PWM[4,5] has distinct offset for PWMDWIDTH and PWMTHRES
297+ * from the other PWMs on MT7623.
298+ */
299+ reg_width = PWM45DWIDTH_FIXUP ;
300+ reg_thres = PWM45THRES_FIXUP ;
250301 }
251302
252- return 0 ;
253- }
303+ clkdiv = FIELD_GET (PWMCON_CLKDIV , pwm_mediatek_readl (pc , pwm -> hwpwm , PWMCON ));
304+ cnt_period = FIELD_GET (PWMDWIDTH_PERIOD , pwm_mediatek_readl (pc , pwm -> hwpwm , reg_width ));
305+ cnt_duty = FIELD_GET (PWMTHRES_DUTY , pwm_mediatek_readl (pc , pwm -> hwpwm , reg_thres ));
254306
255- err = pwm_mediatek_config (chip , pwm , state -> duty_cycle , state -> period );
256- if (err )
257- return err ;
307+ * wfhw = (typeof (* wfhw )){
308+ .enable = enable ,
309+ .con = BIT (15 ) | clkdiv ,
310+ .width = cnt_period ,
311+ .thres = cnt_duty ,
312+ };
313+ } else {
314+ * wfhw = (typeof (* wfhw )){
315+ .enable = 0 ,
316+ };
317+ }
258318
259- if (!pwm -> state .enabled )
260- err = pwm_mediatek_clk_enable (pc , pwm -> hwpwm );
319+ pwm_mediatek_clk_disable (pc , pwm -> hwpwm );
261320
262- return err ;
321+ return ret ;
263322}
264323
265- static int pwm_mediatek_get_state (struct pwm_chip * chip , struct pwm_device * pwm ,
266- struct pwm_state * state )
324+ static int pwm_mediatek_write_waveform (struct pwm_chip * chip ,
325+ struct pwm_device * pwm , const void * _wfhw )
267326{
327+ const struct pwm_mediatek_waveform * wfhw = _wfhw ;
268328 struct pwm_mediatek_chip * pc = to_pwm_mediatek_chip (chip );
329+ u32 ctrl ;
269330 int ret ;
270- u32 enable ;
271- u32 reg_width = PWMDWIDTH , reg_thres = PWMTHRES ;
272-
273- if (pc -> soc -> pwm45_fixup && pwm -> hwpwm > 2 ) {
274- /*
275- * PWM[4,5] has distinct offset for PWMDWIDTH and PWMTHRES
276- * from the other PWMs on MT7623.
277- */
278- reg_width = PWM45DWIDTH_FIXUP ;
279- reg_thres = PWM45THRES_FIXUP ;
280- }
281331
282332 ret = pwm_mediatek_clk_enable (pc , pwm -> hwpwm );
283333 if (ret < 0 )
284334 return ret ;
285335
286- enable = readl (pc -> regs );
287- if (enable & BIT (pwm -> hwpwm )) {
288- u32 clkdiv , cnt_period , cnt_duty ;
289- unsigned long clk_rate ;
336+ ctrl = readl (pc -> regs );
290337
291- clk_rate = pc -> clk_pwms [pwm -> hwpwm ].rate ;
338+ if (wfhw -> enable ) {
339+ u32 reg_width = PWMDWIDTH , reg_thres = PWMTHRES ;
292340
293- state -> enabled = true;
294- state -> polarity = PWM_POLARITY_NORMAL ;
341+ if (pc -> soc -> pwm45_fixup && pwm -> hwpwm > 2 ) {
342+ /*
343+ * PWM[4,5] has distinct offset for PWMDWIDTH and PWMTHRES
344+ * from the other PWMs on MT7623.
345+ */
346+ reg_width = PWM45DWIDTH_FIXUP ;
347+ reg_thres = PWM45THRES_FIXUP ;
348+ }
295349
296- clkdiv = FIELD_GET (PWMCON_CLKDIV ,
297- pwm_mediatek_readl (pc , pwm -> hwpwm , PWMCON ));
298- cnt_period = FIELD_GET (PWMDWIDTH_PERIOD ,
299- pwm_mediatek_readl (pc , pwm -> hwpwm , reg_width ));
300- cnt_duty = FIELD_GET (PWMTHRES_DUTY ,
301- pwm_mediatek_readl (pc , pwm -> hwpwm , reg_thres ));
350+ if (!(ctrl & BIT (pwm -> hwpwm ))) {
351+ /*
352+ * The clks are already on, just increasing the usage
353+ * counter doesn't fail.
354+ */
355+ ret = pwm_mediatek_clk_enable (pc , pwm -> hwpwm );
356+ if (unlikely (ret < 0 ))
357+ goto out ;
358+
359+ ctrl |= BIT (pwm -> hwpwm );
360+ writel (ctrl , pc -> regs );
361+ }
302362
303- /*
304- * cnt_period is a 13 bit value, NSEC_PER_SEC is 30 bits wide
305- * and clkdiv is less than 8, so the multiplication doesn't
306- * overflow an u64.
307- */
308- state -> period =
309- DIV_ROUND_UP_ULL ((u64 )cnt_period * NSEC_PER_SEC << clkdiv , clk_rate );
310- state -> duty_cycle =
311- DIV_ROUND_UP_ULL ((u64 )cnt_duty * NSEC_PER_SEC << clkdiv , clk_rate );
363+ /* Make sure we use the bus clock and not the 26MHz clock */
364+ if (pc -> soc -> pwm_ck_26m_sel_reg )
365+ writel (0 , pc -> regs + pc -> soc -> pwm_ck_26m_sel_reg );
366+
367+ pwm_mediatek_writel (pc , pwm -> hwpwm , PWMCON , BIT (15 ) | wfhw -> con );
368+ pwm_mediatek_writel (pc , pwm -> hwpwm , reg_width , wfhw -> width );
369+ pwm_mediatek_writel (pc , pwm -> hwpwm , reg_thres , wfhw -> thres );
312370 } else {
313- state -> enabled = false;
371+ if (ctrl & BIT (pwm -> hwpwm )) {
372+ ctrl &= ~BIT (pwm -> hwpwm );
373+ writel (ctrl , pc -> regs );
374+
375+ pwm_mediatek_clk_disable (pc , pwm -> hwpwm );
376+ }
314377 }
315378
379+ out :
316380 pwm_mediatek_clk_disable (pc , pwm -> hwpwm );
317381
318382 return ret ;
319383}
320384
321385static const struct pwm_ops pwm_mediatek_ops = {
322- .apply = pwm_mediatek_apply ,
323- .get_state = pwm_mediatek_get_state ,
386+ .sizeof_wfhw = sizeof (struct pwm_mediatek_waveform ),
387+ .round_waveform_tohw = pwm_mediatek_round_waveform_tohw ,
388+ .round_waveform_fromhw = pwm_mediatek_round_waveform_fromhw ,
389+ .read_waveform = pwm_mediatek_read_waveform ,
390+ .write_waveform = pwm_mediatek_write_waveform ,
324391};
325392
326393static int pwm_mediatek_init_used_clks (struct pwm_mediatek_chip * pc )
0 commit comments