aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/pwm/pwm-imx.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/pwm/pwm-imx.c')
-rw-r--r--drivers/pwm/pwm-imx.c146
1 files changed, 83 insertions, 63 deletions
diff --git a/drivers/pwm/pwm-imx.c b/drivers/pwm/pwm-imx.c
index 2a0b35333972..8b7f01e0a100 100644
--- a/drivers/pwm/pwm-imx.c
+++ b/drivers/pwm/pwm-imx.c
@@ -46,81 +46,96 @@ struct imx_chip {
46 void __iomem *mmio_base; 46 void __iomem *mmio_base;
47 47
48 struct pwm_chip chip; 48 struct pwm_chip chip;
49
50 int (*config)(struct pwm_chip *chip,
51 struct pwm_device *pwm, int duty_ns, int period_ns);
49}; 52};
50 53
51#define to_imx_chip(chip) container_of(chip, struct imx_chip, chip) 54#define to_imx_chip(chip) container_of(chip, struct imx_chip, chip)
52 55
53static int imx_pwm_config(struct pwm_chip *chip, 56static int imx_pwm_config_v1(struct pwm_chip *chip,
54 struct pwm_device *pwm, int duty_ns, int period_ns) 57 struct pwm_device *pwm, int duty_ns, int period_ns)
55{ 58{
56 struct imx_chip *imx = to_imx_chip(chip); 59 struct imx_chip *imx = to_imx_chip(chip);
57 60
58 if (!(cpu_is_mx1() || cpu_is_mx21())) { 61 /*
59 unsigned long long c; 62 * The PWM subsystem allows for exact frequencies. However,
60 unsigned long period_cycles, duty_cycles, prescale; 63 * I cannot connect a scope on my device to the PWM line and
61 u32 cr; 64 * thus cannot provide the program the PWM controller
62 65 * exactly. Instead, I'm relying on the fact that the
63 c = clk_get_rate(imx->clk); 66 * Bootloader (u-boot or WinCE+haret) has programmed the PWM
64 c = c * period_ns; 67 * function group already. So I'll just modify the PWM sample
65 do_div(c, 1000000000); 68 * register to follow the ratio of duty_ns vs. period_ns
66 period_cycles = c; 69 * accordingly.
67 70 *
68 prescale = period_cycles / 0x10000 + 1; 71 * This is good enough for programming the brightness of
69 72 * the LCD backlight.
70 period_cycles /= prescale; 73 *
71 c = (unsigned long long)period_cycles * duty_ns; 74 * The real implementation would divide PERCLK[0] first by
72 do_div(c, period_ns); 75 * both the prescaler (/1 .. /128) and then by CLKSEL
73 duty_cycles = c; 76 * (/2 .. /16).
74 77 */
75 /* 78 u32 max = readl(imx->mmio_base + MX1_PWMP);
76 * according to imx pwm RM, the real period value should be 79 u32 p = max * duty_ns / period_ns;
77 * PERIOD value in PWMPR plus 2. 80 writel(max - p, imx->mmio_base + MX1_PWMS);
78 */ 81
79 if (period_cycles > 2) 82 return 0;
80 period_cycles -= 2; 83}
81 else 84
82 period_cycles = 0; 85static int imx_pwm_config_v2(struct pwm_chip *chip,
83 86 struct pwm_device *pwm, int duty_ns, int period_ns)
84 writel(duty_cycles, imx->mmio_base + MX3_PWMSAR); 87{
85 writel(period_cycles, imx->mmio_base + MX3_PWMPR); 88 struct imx_chip *imx = to_imx_chip(chip);
86 89 unsigned long long c;
87 cr = MX3_PWMCR_PRESCALER(prescale) | 90 unsigned long period_cycles, duty_cycles, prescale;
88 MX3_PWMCR_DOZEEN | MX3_PWMCR_WAITEN | 91 u32 cr;
89 MX3_PWMCR_DBGEN | MX3_PWMCR_EN; 92
90 93 c = clk_get_rate(imx->clk);
91 if (cpu_is_mx25()) 94 c = c * period_ns;
92 cr |= MX3_PWMCR_CLKSRC_IPG; 95 do_div(c, 1000000000);
93 else 96 period_cycles = c;
94 cr |= MX3_PWMCR_CLKSRC_IPG_HIGH; 97
95 98 prescale = period_cycles / 0x10000 + 1;
96 writel(cr, imx->mmio_base + MX3_PWMCR); 99
97 } else if (cpu_is_mx1() || cpu_is_mx21()) { 100 period_cycles /= prescale;
98 /* The PWM subsystem allows for exact frequencies. However, 101 c = (unsigned long long)period_cycles * duty_ns;
99 * I cannot connect a scope on my device to the PWM line and 102 do_div(c, period_ns);
100 * thus cannot provide the program the PWM controller 103 duty_cycles = c;
101 * exactly. Instead, I'm relying on the fact that the 104
102 * Bootloader (u-boot or WinCE+haret) has programmed the PWM 105 /*
103 * function group already. So I'll just modify the PWM sample 106 * according to imx pwm RM, the real period value should be
104 * register to follow the ratio of duty_ns vs. period_ns 107 * PERIOD value in PWMPR plus 2.
105 * accordingly. 108 */
106 * 109 if (period_cycles > 2)
107 * This is good enough for programming the brightness of 110 period_cycles -= 2;
108 * the LCD backlight. 111 else
109 * 112 period_cycles = 0;
110 * The real implementation would divide PERCLK[0] first by 113
111 * both the prescaler (/1 .. /128) and then by CLKSEL 114 writel(duty_cycles, imx->mmio_base + MX3_PWMSAR);
112 * (/2 .. /16). 115 writel(period_cycles, imx->mmio_base + MX3_PWMPR);
113 */ 116
114 u32 max = readl(imx->mmio_base + MX1_PWMP); 117 cr = MX3_PWMCR_PRESCALER(prescale) |
115 u32 p = max * duty_ns / period_ns; 118 MX3_PWMCR_DOZEEN | MX3_PWMCR_WAITEN |
116 writel(max - p, imx->mmio_base + MX1_PWMS); 119 MX3_PWMCR_DBGEN | MX3_PWMCR_EN;
117 } else { 120
118 BUG(); 121 if (cpu_is_mx25())
119 } 122 cr |= MX3_PWMCR_CLKSRC_IPG;
123 else
124 cr |= MX3_PWMCR_CLKSRC_IPG_HIGH;
125
126 writel(cr, imx->mmio_base + MX3_PWMCR);
120 127
121 return 0; 128 return 0;
122} 129}
123 130
131static int imx_pwm_config(struct pwm_chip *chip,
132 struct pwm_device *pwm, int duty_ns, int period_ns)
133{
134 struct imx_chip *imx = to_imx_chip(chip);
135
136 return imx->config(chip, pwm, duty_ns, period_ns);
137}
138
124static int imx_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) 139static int imx_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
125{ 140{
126 struct imx_chip *imx = to_imx_chip(chip); 141 struct imx_chip *imx = to_imx_chip(chip);
@@ -187,6 +202,11 @@ static int __devinit imx_pwm_probe(struct platform_device *pdev)
187 if (imx->mmio_base == NULL) 202 if (imx->mmio_base == NULL)
188 return -EADDRNOTAVAIL; 203 return -EADDRNOTAVAIL;
189 204
205 if (cpu_is_mx1() || cpu_is_mx21())
206 imx->config = imx_pwm_config_v1;
207 else
208 imx->config = imx_pwm_config_v2;
209
190 ret = pwmchip_add(&imx->chip); 210 ret = pwmchip_add(&imx->chip);
191 if (ret < 0) 211 if (ret < 0)
192 return ret; 212 return ret;