diff options
| author | Boris BREZILLON <boris.brezillon@free-electrons.com> | 2014-11-19 15:33:09 +0100 | 
|---|---|---|
| committer | Thierry Reding <thierry.reding@gmail.com> | 2014-12-04 11:32:36 +0100 | 
| 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 @@  #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;  | 
