rename the mx2 target to imx21
[openwrt/svn-archive/archive.git] / target / linux / imx21 / patches-2.6.34 / 040-pwm.patch
diff --git a/target/linux/imx21/patches-2.6.34/040-pwm.patch b/target/linux/imx21/patches-2.6.34/040-pwm.patch
new file mode 100644 (file)
index 0000000..af47ad1
--- /dev/null
@@ -0,0 +1,137 @@
+--- a/arch/arm/plat-mxc/pwm.c
++++ b/arch/arm/plat-mxc/pwm.c
+@@ -25,6 +25,11 @@
+ #define MX1_PWMS    0x04   /* PWM Sample Register */
+ #define MX1_PWMP    0x08   /* PWM Period Register */
++#define MX1_PWMC_EN (1 << 4)
++#define MX1_PWMC_PRESCALER_MASK (0x7f << 8)
++#define MX1_PWMC_PRESCALER(x) ((x & 0x7f) << 8)
++#define MX1_PWMC_CLKSEL_MASK 0x3
++#define MX1_PWMC_CLKSEL(x) ((x & 0x3))
+ /* i.MX27, i.MX31, i.MX35 share the same PWM function block: */
+@@ -54,26 +59,33 @@ struct pwm_device {
+ int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
+ {
++      unsigned long long c;
++      unsigned long period_cycles, duty_cycles, prescale;
++
+       if (pwm == NULL || period_ns == 0 || duty_ns > period_ns)
+               return -EINVAL;
++      c = clk_get_rate(pwm->clk);
++
++      c = c * period_ns;
++
++      if (cpu_is_mx1() || cpu_is_mx2())
++              c >>= 1;
++
++      do_div(c, 1000000000);
++      period_cycles = c;
++
++      prescale = period_cycles / 0x10000 + 1;
++
++      period_cycles /= prescale;
++      c = (unsigned long long)period_cycles * duty_ns;
++      do_div(c, period_ns);
++      duty_cycles = c;
++
++
+       if (cpu_is_mx27() || cpu_is_mx3() || cpu_is_mx25()) {
+-              unsigned long long c;
+-              unsigned long period_cycles, duty_cycles, prescale;
+               u32 cr;
+-              c = clk_get_rate(pwm->clk);
+-              c = c * period_ns;
+-              do_div(c, 1000000000);
+-              period_cycles = c;
+-
+-              prescale = period_cycles / 0x10000 + 1;
+-
+-              period_cycles /= prescale;
+-              c = (unsigned long long)period_cycles * duty_ns;
+-              do_div(c, period_ns);
+-              duty_cycles = c;
+-
+               writel(duty_cycles, pwm->mmio_base + MX3_PWMSAR);
+               writel(period_cycles, pwm->mmio_base + MX3_PWMPR);
+@@ -86,25 +98,28 @@ int pwm_config(struct pwm_device *pwm, i
+               writel(cr, pwm->mmio_base + MX3_PWMCR);
+       } else if (cpu_is_mx1() || cpu_is_mx21()) {
+-              /* The PWM subsystem allows for exact frequencies. However,
+-               * I cannot connect a scope on my device to the PWM line and
+-               * thus cannot provide the program the PWM controller
+-               * exactly. Instead, I'm relying on the fact that the
+-               * Bootloader (u-boot or WinCE+haret) has programmed the PWM
+-               * function group already. So I'll just modify the PWM sample
+-               * register to follow the ratio of duty_ns vs. period_ns
+-               * accordingly.
+-               *
+-               * This is good enough for programming the brightness of
+-               * the LCD backlight.
+-               *
+-               * The real implementation would divide PERCLK[0] first by
+-               * both the prescaler (/1 .. /128) and then by CLKSEL
+-               * (/2 .. /16).
+-               */
+-              u32 max = readl(pwm->mmio_base + MX1_PWMP);
+-              u32 p = max * duty_ns / period_ns;
+-              writel(max - p, pwm->mmio_base + MX1_PWMS);
++              unsigned long clksel = 0;
++              u32 ctrl;
++
++              while (prescale >= 0x80 && clksel < 4) {
++                      prescale >>= 1;
++                      ++clksel;
++              }
++
++              if (clksel > 3)
++                      return -EINVAL;
++
++              ctrl = readl(pwm->mmio_base + MX1_PWMC);
++              writel(ctrl & ~MX1_PWMC_EN, pwm->mmio_base + MX1_PWMC);
++
++              writel(duty_cycles, pwm->mmio_base + MX1_PWMS);
++              writel(period_cycles, pwm->mmio_base + MX1_PWMP);
++
++              ctrl &= ~(MX1_PWMC_PRESCALER_MASK | MX1_PWMC_CLKSEL_MASK);
++              ctrl |= MX1_PWMC_PRESCALER(prescale);
++              ctrl |= MX1_PWMC_CLKSEL(clksel);
++              writel(ctrl, pwm->mmio_base + MX1_PWMC);
++
+       } else {
+               BUG();
+       }
+@@ -116,6 +131,11 @@ EXPORT_SYMBOL(pwm_config);
+ int pwm_enable(struct pwm_device *pwm)
+ {
+       int rc = 0;
++      if (cpu_is_mx1() || cpu_is_mx2()) {
++              u32 ctrl;
++              ctrl = readl(pwm->mmio_base + MX1_PWMC);
++              writel(ctrl | MX1_PWMC_EN, pwm->mmio_base + MX1_PWMC);
++      }
+       if (!pwm->clk_enabled) {
+               rc = clk_enable(pwm->clk);
+@@ -128,7 +148,13 @@ EXPORT_SYMBOL(pwm_enable);
+ void pwm_disable(struct pwm_device *pwm)
+ {
+-      writel(0, pwm->mmio_base + MX3_PWMCR);
++      if (cpu_is_mx27() || cpu_is_mx3() || cpu_is_mx25()) {
++              writel(0, pwm->mmio_base + MX3_PWMCR);
++      } else if (cpu_is_mx1() || cpu_is_mx2()) {
++              u32 ctrl;
++              ctrl = readl(pwm->mmio_base + MX1_PWMC);
++              writel(ctrl & ~MX1_PWMC_EN, pwm->mmio_base + MX1_PWMC);
++      }
+       if (pwm->clk_enabled) {
+               clk_disable(pwm->clk);