[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 メーリングリストの案内