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>
This commit is contained in:
Boris BREZILLON 2014-11-19 15:33:09 +01:00 committed by Thierry Reding
parent 97d0b42e39
commit 39e046f2c1
1 changed files with 45 additions and 5 deletions

View File

@ -32,10 +32,16 @@
#define ATMEL_HLCDC_PWMPS_MAX 0x6
#define ATMEL_HLCDC_PWMPS(x) ((x) & ATMEL_HLCDC_PWMPS_MASK)
struct atmel_hlcdc_pwm_errata {
bool slow_clk_erratum;
bool div1_clk_erratum;
};
struct atmel_hlcdc_pwm {
struct pwm_chip chip;
struct atmel_hlcdc *hlcdc;
struct clk *cur_clk;
const struct atmel_hlcdc_pwm_errata *errata;
};
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,
u32 pwmcfg;
int pres;
clk_freq = clk_get_rate(new_clk);
clk_period_ns = (u64)NSEC_PER_SEC * 256;
do_div(clk_period_ns, clk_freq);
if (!chip->errata || !chip->errata->slow_clk_erratum) {
clk_freq = clk_get_rate(new_clk);
clk_period_ns = (u64)NSEC_PER_SEC * 256;
do_div(clk_period_ns, clk_freq);
}
if (clk_period_ns > period_ns) {
/* Errata: cannot use slow clk on some IP revisions */
if ((chip->errata && chip->errata->slow_clk_erratum) ||
clk_period_ns > period_ns) {
new_clk = hlcdc->sys_clk;
clk_freq = clk_get_rate(new_clk);
clk_period_ns = (u64)NSEC_PER_SEC * 256;
do_div(clk_period_ns, clk_freq);
}
for (pres = 0; pres <= ATMEL_HLCDC_PWMPS_MAX; pres++)
for (pres = 0; pres <= ATMEL_HLCDC_PWMPS_MAX; pres++) {
/* Errata: cannot divide by 1 on some IP revisions */
if (!pres && chip->errata && chip->errata->div1_clk_erratum)
continue;
if ((clk_period_ns << pres) >= period_ns)
break;
}
if (pres > ATMEL_HLCDC_PWMPS_MAX)
return -EINVAL;
@ -187,8 +202,29 @@ static const struct pwm_ops atmel_hlcdc_pwm_ops = {
.owner = THIS_MODULE,
};
static const struct atmel_hlcdc_pwm_errata atmel_hlcdc_pwm_at91sam9x5_errata = {
.slow_clk_erratum = true,
};
static const struct atmel_hlcdc_pwm_errata atmel_hlcdc_pwm_sama5d3_errata = {
.div1_clk_erratum = true,
};
static const struct of_device_id atmel_hlcdc_dt_ids[] = {
{
.compatible = "atmel,at91sam9x5-hlcdc",
.data = &atmel_hlcdc_pwm_at91sam9x5_errata,
},
{
.compatible = "atmel,sama5d3-hlcdc",
.data = &atmel_hlcdc_pwm_sama5d3_errata,
},
{ /* sentinel */ },
};
static int atmel_hlcdc_pwm_probe(struct platform_device *pdev)
{
const struct of_device_id *match;
struct device *dev = &pdev->dev;
struct atmel_hlcdc_pwm *chip;
struct atmel_hlcdc *hlcdc;
@ -204,6 +240,10 @@ static int atmel_hlcdc_pwm_probe(struct platform_device *pdev)
if (ret)
return ret;
match = of_match_node(atmel_hlcdc_dt_ids, dev->parent->of_node);
if (match)
chip->errata = match->data;
chip->hlcdc = hlcdc;
chip->chip.ops = &atmel_hlcdc_pwm_ops;
chip->chip.dev = dev;