|
@@ -32,10 +32,16 @@
|
|
#define ATMEL_HLCDC_PWMPS_MAX 0x6
|
|
#define ATMEL_HLCDC_PWMPS_MAX 0x6
|
|
#define ATMEL_HLCDC_PWMPS(x) ((x) & ATMEL_HLCDC_PWMPS_MASK)
|
|
#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 atmel_hlcdc_pwm {
|
|
struct pwm_chip chip;
|
|
struct pwm_chip chip;
|
|
struct atmel_hlcdc *hlcdc;
|
|
struct atmel_hlcdc *hlcdc;
|
|
struct clk *cur_clk;
|
|
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)
|
|
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;
|
|
u32 pwmcfg;
|
|
int pres;
|
|
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;
|
|
new_clk = hlcdc->sys_clk;
|
|
clk_freq = clk_get_rate(new_clk);
|
|
clk_freq = clk_get_rate(new_clk);
|
|
clk_period_ns = (u64)NSEC_PER_SEC * 256;
|
|
clk_period_ns = (u64)NSEC_PER_SEC * 256;
|
|
do_div(clk_period_ns, clk_freq);
|
|
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)
|
|
if ((clk_period_ns << pres) >= period_ns)
|
|
break;
|
|
break;
|
|
|
|
+ }
|
|
|
|
|
|
if (pres > ATMEL_HLCDC_PWMPS_MAX)
|
|
if (pres > ATMEL_HLCDC_PWMPS_MAX)
|
|
return -EINVAL;
|
|
return -EINVAL;
|
|
@@ -187,8 +202,29 @@ static const struct pwm_ops atmel_hlcdc_pwm_ops = {
|
|
.owner = THIS_MODULE,
|
|
.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)
|
|
static int atmel_hlcdc_pwm_probe(struct platform_device *pdev)
|
|
{
|
|
{
|
|
|
|
+ const struct of_device_id *match;
|
|
struct device *dev = &pdev->dev;
|
|
struct device *dev = &pdev->dev;
|
|
struct atmel_hlcdc_pwm *chip;
|
|
struct atmel_hlcdc_pwm *chip;
|
|
struct atmel_hlcdc *hlcdc;
|
|
struct atmel_hlcdc *hlcdc;
|
|
@@ -204,6 +240,10 @@ static int atmel_hlcdc_pwm_probe(struct platform_device *pdev)
|
|
if (ret)
|
|
if (ret)
|
|
return 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->hlcdc = hlcdc;
|
|
chip->chip.ops = &atmel_hlcdc_pwm_ops;
|
|
chip->chip.ops = &atmel_hlcdc_pwm_ops;
|
|
chip->chip.dev = dev;
|
|
chip->chip.dev = dev;
|