[Armadillo:08329] Re: Armadillo-440のバックライト制御PWMについて

柳原 考作 email@hidden
2012年 9月 20日 (木) 23:00:15 JST


いつもお世話になっております。
柳原です。

なんとか自己解決できました。
参考に原因をまとめておきます。

■不具合症状
pwm_config()の第2引数(duty_ns)と第3引数(period_ns)が等しい場合に、
Armadillo-440のLCDバックライト制御(CON11のPWM1)にて10ミリ秒周期で300マイ
クロ秒程度のHIGH期間が見られる。

■期待動作
duty_nsとperiod_nsが等しい場合はPWM1はLOWホールド。

■不具合箇所
/linux-2.6.26-at16/drivers/mxc/pwm/mxc_pwm.c
pwm_config()

■原因と対策
i.MX25データシートのPWM機能説明を調べたところ、
 ・PWMカウンタは0~PWMPR+1の値域で累積される
 ・PWMカウンタとPWMSARが一致するとPWMOが反転する
とされている。

PWMPR(period_cycles)とPWMSAR(duty_cycles)が等しい場合は、PWMカウンタが
PWMPRまで累積した段階でPWMSARと一致するためPWMOが反転するが、PWMカウンタ
はPWMPR+1になってから0に戻るため、一瞬、PWMカウンタがPWMPR~PWMPR+1の期
間だけ、PWMOに反転出力が出ていた。

対策として、duty_nsとperiod_nsが等しい場合は、
 ・PWMSARを0で上書き
 ・PWMCR_POUTCを排他的論理和で上書き
のようにして瞬間的なPWMO反転出力が無くなることを確認した。

i.MX25のデータシートではPWM関連レジスタの変更はPWMを止めて行なうように推
奨しているが、pwm_config()はPWMを止めずにレジスタ書き換えている。
特に不具合は無さそうだけど、気持ち悪いので、ついでに修正した。


--- ./linux-2.6.26-at16.org/drivers/mxc/pwm/mxc_pwm.c
+++ ./linux-2.6.26-at16/drivers/mxc/pwm/mxc_pwm.c
@@ -69,7 +69,7 @@
 	if (cpu_is_mx27() || cpu_is_mx25() || cpu_is_mx31() || cpu_is_mx51()) {
 		unsigned long long c, clock;
 		unsigned long period_cycles, duty_cycles, prescale;
-		unsigned long cr;
+		unsigned long cr, cr_en_cur, cr_en_off;
 		clock = c = clk_get_rate(pwm->clk);
 		c = c * period_ns;
 		do_div(c, 1000000000);
@@ -82,17 +82,26 @@
 		do_div(c, period_ns);
 		duty_cycles = c;
 
+		cr = readl(pwm->mmio_base + MXC_PWMCR);
+		cr_en_cur = cr & MXC_PWMCR_EN;
+		cr_en_off = cr & ~MXC_PWMCR_EN;
+		cr = cr_en_cur | MXC_PWMCR_PRESCALER(prescale) |
+			MXC_PWMCR_CLKSRC_IPG_HIGH |
+			MXC_PWMCR_STOPEN | MXC_PWMCR_DOZEEN |
+			MXC_PWMCR_WAITEN | MXC_PWMCR_DBGEN;
+		if (pwm->invert) cr |= MXC_PWMCR_POUTC_INVERT;
+
+		if (duty_ns == period_ns) {
+			duty_cycles = 0;
+			cr ^= MXC_PWMCR_POUTC_INVERT;
+		}
+
 		pr_debug("%s: clock=%lld, period cycles=%ld, duty cycle=%ld, prescale=%ld\n",
 			 __func__, clock, period_cycles, duty_cycles, prescale);
-
+		
+		if (cr_en_cur) writel(cr_en_off, pwm->mmio_base + MXC_PWMCR);
 		writel(duty_cycles, pwm->mmio_base + MXC_PWMSAR);
 		writel(period_cycles, pwm->mmio_base + MXC_PWMPR);
-		cr = readl(pwm->mmio_base + MXC_PWMCR) & MXC_PWMCR_EN;
-		cr |= MXC_PWMCR_PRESCALER(prescale) |
-			MXC_PWMCR_CLKSRC_IPG_HIGH |
-			MXC_PWMCR_STOPEN | MXC_PWMCR_DOZEEN |
-			MXC_PWMCR_WAITEN | MXC_PWMCR_DBGEN;
-		if (pwm->invert) cr |= MXC_PWMCR_POUTC_INVERT;
 		writel(cr, pwm->mmio_base + MXC_PWMCR);
 	} else if (cpu_is_mx21()) {
 		/* The PWM subsystem allows for exact frequencies. However,



armadillo メーリングリストの案内