diff options
author | Boris BREZILLON <boris.brezillon@free-electrons.com> | 2014-11-19 09:33:09 -0500 |
---|---|---|
committer | Thierry Reding <thierry.reding@gmail.com> | 2014-12-04 05:32:36 -0500 |
commit | 39e046f2c1dd0742976d7ee6e464744cf2122f41 (patch) | |
tree | cd2e044d7b76473eace7179f4b30d00890339776 /drivers/pwm/pwm-atmel-hlcdc.c | |
parent | 97d0b42e39a73c7e0683927ee4b3775c7411b7bf (diff) |
pwm: atmel-hlcdc: add at91sam9x5 and sama5d3 errata handling
at91sam9x5 has an errata forbidding the use of slow clk as a clk source and
sama5d3 SoCs has another errata forbidding the use of div1 prescaler.
Take both of these erratas into account.
Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com>
Acked-by: Nicolas Ferre <nicolas.ferre@atmel.com>
Signed-off-by: Thierry Reding <thierry.reding@gmail.com>
Diffstat (limited to 'drivers/pwm/pwm-atmel-hlcdc.c')
-rw-r--r-- | drivers/pwm/pwm-atmel-hlcdc.c | 50 |
1 files changed, 45 insertions, 5 deletions
diff --git a/drivers/pwm/pwm-atmel-hlcdc.c b/drivers/pwm/pwm-atmel-hlcdc.c index eaf8b12ce1e5..e7a785fadcdf 100644 --- a/drivers/pwm/pwm-atmel-hlcdc.c +++ b/drivers/pwm/pwm-atmel-hlcdc.c | |||
@@ -32,10 +32,16 @@ | |||
32 | #define ATMEL_HLCDC_PWMPS_MAX 0x6 | 32 | #define ATMEL_HLCDC_PWMPS_MAX 0x6 |
33 | #define ATMEL_HLCDC_PWMPS(x) ((x) & ATMEL_HLCDC_PWMPS_MASK) | 33 | #define ATMEL_HLCDC_PWMPS(x) ((x) & ATMEL_HLCDC_PWMPS_MASK) |
34 | 34 | ||
35 | struct atmel_hlcdc_pwm_errata { | ||
36 | bool slow_clk_erratum; | ||
37 | bool div1_clk_erratum; | ||
38 | }; | ||
39 | |||
35 | struct atmel_hlcdc_pwm { | 40 | struct atmel_hlcdc_pwm { |
36 | struct pwm_chip chip; | 41 | struct pwm_chip chip; |
37 | struct atmel_hlcdc *hlcdc; | 42 | struct atmel_hlcdc *hlcdc; |
38 | struct clk *cur_clk; | 43 | struct clk *cur_clk; |
44 | const struct atmel_hlcdc_pwm_errata *errata; | ||
39 | }; | 45 | }; |
40 | 46 | ||
41 | static inline struct atmel_hlcdc_pwm *to_atmel_hlcdc_pwm(struct pwm_chip *chip) | 47 | static inline struct atmel_hlcdc_pwm *to_atmel_hlcdc_pwm(struct pwm_chip *chip) |
@@ -56,20 +62,29 @@ static int atmel_hlcdc_pwm_config(struct pwm_chip *c, | |||
56 | u32 pwmcfg; | 62 | u32 pwmcfg; |
57 | int pres; | 63 | int pres; |
58 | 64 | ||
59 | clk_freq = clk_get_rate(new_clk); | 65 | if (!chip->errata || !chip->errata->slow_clk_erratum) { |
60 | clk_period_ns = (u64)NSEC_PER_SEC * 256; | 66 | clk_freq = clk_get_rate(new_clk); |
61 | do_div(clk_period_ns, clk_freq); | 67 | clk_period_ns = (u64)NSEC_PER_SEC * 256; |
68 | do_div(clk_period_ns, clk_freq); | ||
69 | } | ||
62 | 70 | ||
63 | if (clk_period_ns > period_ns) { | 71 | /* Errata: cannot use slow clk on some IP revisions */ |
72 | if ((chip->errata && chip->errata->slow_clk_erratum) || | ||
73 | clk_period_ns > period_ns) { | ||
64 | new_clk = hlcdc->sys_clk; | 74 | new_clk = hlcdc->sys_clk; |
65 | clk_freq = clk_get_rate(new_clk); | 75 | clk_freq = clk_get_rate(new_clk); |
66 | clk_period_ns = (u64)NSEC_PER_SEC * 256; | 76 | clk_period_ns = (u64)NSEC_PER_SEC * 256; |
67 | do_div(clk_period_ns, clk_freq); | 77 | do_div(clk_period_ns, clk_freq); |
68 | } | 78 | } |
69 | 79 | ||
70 | for (pres = 0; pres <= ATMEL_HLCDC_PWMPS_MAX; pres++) | 80 | for (pres = 0; pres <= ATMEL_HLCDC_PWMPS_MAX; pres++) { |
81 | /* Errata: cannot divide by 1 on some IP revisions */ | ||
82 | if (!pres && chip->errata && chip->errata->div1_clk_erratum) | ||
83 | continue; | ||
84 | |||
71 | if ((clk_period_ns << pres) >= period_ns) | 85 | if ((clk_period_ns << pres) >= period_ns) |
72 | break; | 86 | break; |
87 | } | ||
73 | 88 | ||
74 | if (pres > ATMEL_HLCDC_PWMPS_MAX) | 89 | if (pres > ATMEL_HLCDC_PWMPS_MAX) |
75 | return -EINVAL; | 90 | return -EINVAL; |
@@ -187,8 +202,29 @@ static const struct pwm_ops atmel_hlcdc_pwm_ops = { | |||
187 | .owner = THIS_MODULE, | 202 | .owner = THIS_MODULE, |
188 | }; | 203 | }; |
189 | 204 | ||
205 | static const struct atmel_hlcdc_pwm_errata atmel_hlcdc_pwm_at91sam9x5_errata = { | ||
206 | .slow_clk_erratum = true, | ||
207 | }; | ||
208 | |||
209 | static const struct atmel_hlcdc_pwm_errata atmel_hlcdc_pwm_sama5d3_errata = { | ||
210 | .div1_clk_erratum = true, | ||
211 | }; | ||
212 | |||
213 | static const struct of_device_id atmel_hlcdc_dt_ids[] = { | ||
214 | { | ||
215 | .compatible = "atmel,at91sam9x5-hlcdc", | ||
216 | .data = &atmel_hlcdc_pwm_at91sam9x5_errata, | ||
217 | }, | ||
218 | { | ||
219 | .compatible = "atmel,sama5d3-hlcdc", | ||
220 | .data = &atmel_hlcdc_pwm_sama5d3_errata, | ||
221 | }, | ||
222 | { /* sentinel */ }, | ||
223 | }; | ||
224 | |||
190 | static int atmel_hlcdc_pwm_probe(struct platform_device *pdev) | 225 | static int atmel_hlcdc_pwm_probe(struct platform_device *pdev) |
191 | { | 226 | { |
227 | const struct of_device_id *match; | ||
192 | struct device *dev = &pdev->dev; | 228 | struct device *dev = &pdev->dev; |
193 | struct atmel_hlcdc_pwm *chip; | 229 | struct atmel_hlcdc_pwm *chip; |
194 | struct atmel_hlcdc *hlcdc; | 230 | struct atmel_hlcdc *hlcdc; |
@@ -204,6 +240,10 @@ static int atmel_hlcdc_pwm_probe(struct platform_device *pdev) | |||
204 | if (ret) | 240 | if (ret) |
205 | return ret; | 241 | return ret; |
206 | 242 | ||
243 | match = of_match_node(atmel_hlcdc_dt_ids, dev->parent->of_node); | ||
244 | if (match) | ||
245 | chip->errata = match->data; | ||
246 | |||
207 | chip->hlcdc = hlcdc; | 247 | chip->hlcdc = hlcdc; |
208 | chip->chip.ops = &atmel_hlcdc_pwm_ops; | 248 | chip->chip.ops = &atmel_hlcdc_pwm_ops; |
209 | chip->chip.dev = dev; | 249 | chip->chip.dev = dev; |