[mx2] vp6500: Add backlight device
[openwrt.git] / target / linux / mx2 / patches-2.6.34 / 040-pwm.patch
1 diff --git a/arch/arm/plat-mxc/pwm.c b/arch/arm/plat-mxc/pwm.c
2 index c36f263..acc5dc1 100644
3 --- a/arch/arm/plat-mxc/pwm.c
4 +++ b/arch/arm/plat-mxc/pwm.c
5 @@ -25,6 +25,11 @@
6  #define MX1_PWMS    0x04   /* PWM Sample Register */
7  #define MX1_PWMP    0x08   /* PWM Period Register */
8  
9 +#define MX1_PWMC_EN (1 << 4)
10 +#define MX1_PWMC_PRESCALER_MASK (0x7f << 8)
11 +#define MX1_PWMC_PRESCALER(x) ((x & 0x7f) << 8)
12 +#define MX1_PWMC_CLKSEL_MASK 0x3
13 +#define MX1_PWMC_CLKSEL(x) ((x & 0x3))
14  
15  /* i.MX27, i.MX31, i.MX35 share the same PWM function block: */
16  
17 @@ -54,25 +59,32 @@ struct pwm_device {
18  
19  int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
20  {
21 +       unsigned long long c;
22 +       unsigned long period_cycles, duty_cycles, prescale;
23 +
24         if (pwm == NULL || period_ns == 0 || duty_ns > period_ns)
25                 return -EINVAL;
26  
27 -       if (cpu_is_mx27() || cpu_is_mx3() || cpu_is_mx25()) {
28 -               unsigned long long c;
29 -               unsigned long period_cycles, duty_cycles, prescale;
30 -               u32 cr;
31 +       c = clk_get_rate(pwm->clk);
32 +
33 +       c = c * period_ns;
34 +
35 +       if (cpu_is_mx1() || cpu_is_mx2())
36 +               c >>= 1;
37  
38 -               c = clk_get_rate(pwm->clk);
39 -               c = c * period_ns;
40 -               do_div(c, 1000000000);
41 -               period_cycles = c;
42 +       do_div(c, 1000000000);
43 +       period_cycles = c;
44  
45 -               prescale = period_cycles / 0x10000 + 1;
46 +       prescale = period_cycles / 0x10000 + 1;
47  
48 -               period_cycles /= prescale;
49 -               c = (unsigned long long)period_cycles * duty_ns;
50 -               do_div(c, period_ns);
51 -               duty_cycles = c;
52 +       period_cycles /= prescale;
53 +       c = (unsigned long long)period_cycles * duty_ns;
54 +       do_div(c, period_ns);
55 +       duty_cycles = c;
56 +
57 +
58 +       if (cpu_is_mx27() || cpu_is_mx3() || cpu_is_mx25()) {
59 +               u32 cr;
60  
61                 writel(duty_cycles, pwm->mmio_base + MX3_PWMSAR);
62                 writel(period_cycles, pwm->mmio_base + MX3_PWMPR);
63 @@ -86,25 +98,28 @@ int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
64  
65                 writel(cr, pwm->mmio_base + MX3_PWMCR);
66         } else if (cpu_is_mx1() || cpu_is_mx21()) {
67 -               /* The PWM subsystem allows for exact frequencies. However,
68 -                * I cannot connect a scope on my device to the PWM line and
69 -                * thus cannot provide the program the PWM controller
70 -                * exactly. Instead, I'm relying on the fact that the
71 -                * Bootloader (u-boot or WinCE+haret) has programmed the PWM
72 -                * function group already. So I'll just modify the PWM sample
73 -                * register to follow the ratio of duty_ns vs. period_ns
74 -                * accordingly.
75 -                *
76 -                * This is good enough for programming the brightness of
77 -                * the LCD backlight.
78 -                *
79 -                * The real implementation would divide PERCLK[0] first by
80 -                * both the prescaler (/1 .. /128) and then by CLKSEL
81 -                * (/2 .. /16).
82 -                */
83 -               u32 max = readl(pwm->mmio_base + MX1_PWMP);
84 -               u32 p = max * duty_ns / period_ns;
85 -               writel(max - p, pwm->mmio_base + MX1_PWMS);
86 +               unsigned long clksel = 0;
87 +               u32 ctrl;
88 +
89 +               while (prescale >= 0x80 && clksel < 4) {
90 +                       prescale >>= 1;
91 +                       ++clksel;
92 +               }
93 +
94 +               if (clksel > 3)
95 +                       return -EINVAL;
96 +
97 +               ctrl = readl(pwm->mmio_base + MX1_PWMC);
98 +               writel(ctrl & ~MX1_PWMC_EN, pwm->mmio_base + MX1_PWMC);
99 +
100 +               writel(duty_cycles, pwm->mmio_base + MX1_PWMS);
101 +               writel(period_cycles, pwm->mmio_base + MX1_PWMP);
102 +
103 +               ctrl &= ~(MX1_PWMC_PRESCALER_MASK | MX1_PWMC_CLKSEL_MASK);
104 +               ctrl |= MX1_PWMC_PRESCALER(prescale);
105 +               ctrl |= MX1_PWMC_CLKSEL(clksel);
106 +               writel(ctrl, pwm->mmio_base + MX1_PWMC);
107 +
108         } else {
109                 BUG();
110         }
111 @@ -116,6 +130,11 @@ EXPORT_SYMBOL(pwm_config);
112  int pwm_enable(struct pwm_device *pwm)
113  {
114         int rc = 0;
115 +       if (cpu_is_mx1() || cpu_is_mx2()) {
116 +               u32 ctrl;
117 +               ctrl = readl(pwm->mmio_base + MX1_PWMC);
118 +               writel(ctrl | MX1_PWMC_EN, pwm->mmio_base + MX1_PWMC);
119 +       }
120  
121         if (!pwm->clk_enabled) {
122                 rc = clk_enable(pwm->clk);
123 @@ -128,7 +147,13 @@ EXPORT_SYMBOL(pwm_enable);
124  
125  void pwm_disable(struct pwm_device *pwm)
126  {
127 -       writel(0, pwm->mmio_base + MX3_PWMCR);
128 +       if (cpu_is_mx27() || cpu_is_mx3() || cpu_is_mx25()) {
129 +               writel(0, pwm->mmio_base + MX3_PWMCR);
130 +       } else if (cpu_is_mx1() || cpu_is_mx2()) {
131 +               u32 ctrl;
132 +               ctrl = readl(pwm->mmio_base + MX1_PWMC);
133 +               writel(ctrl & ~MX1_PWMC_EN, pwm->mmio_base + MX1_PWMC);
134 +       }
135  
136         if (pwm->clk_enabled) {
137                 clk_disable(pwm->clk);