diff options
author | Sascha Hauer <s.hauer@pengutronix.de> | 2012-07-03 11:28:14 -0400 |
---|---|---|
committer | Thierry Reding <thierry.reding@avionic-design.de> | 2012-09-12 08:25:04 -0400 |
commit | 19e73333236a6115617f8ffb4cc290bdb6f2865a (patch) | |
tree | 25f90c4be1b2e9eff54982ce77ce2f98e481c1b5 /drivers | |
parent | daa5629b21a1e59ed0ef9515a9e791d2f75cc5ca (diff) |
pwm: i.MX: factor out SoC specific functions
To cleanup the code and to make it easier to support different
SoCs.
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Reviewed-by: Shawn Guo <shawn.guo@linaro.org>
Reviewed-by: Benoît Thébaudeau <benoit.thebaudeau@advansee.com>
Signed-off-by: Thierry Reding <thierry.reding@avionic-design.de>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/pwm/pwm-imx.c | 146 |
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 | ||
53 | static int imx_pwm_config(struct pwm_chip *chip, | 56 | static 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; | 85 | static 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 | ||
131 | static 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 | |||
124 | static int imx_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) | 139 | static 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; |