diff options
Diffstat (limited to 'drivers/pwm/pwm-atmel-tcb.c')
| -rw-r--r-- | drivers/pwm/pwm-atmel-tcb.c | 223 |
1 files changed, 93 insertions, 130 deletions
diff --git a/drivers/pwm/pwm-atmel-tcb.c b/drivers/pwm/pwm-atmel-tcb.c index 2837b4ce8053..f9ff78ba122d 100644 --- a/drivers/pwm/pwm-atmel-tcb.c +++ b/drivers/pwm/pwm-atmel-tcb.c @@ -19,8 +19,7 @@ #include <linux/mfd/syscon.h> #include <linux/platform_device.h> #include <linux/pwm.h> -#include <linux/of_device.h> -#include <linux/of_irq.h> +#include <linux/of.h> #include <linux/regmap.h> #include <linux/slab.h> #include <soc/at91/atmel_tcb.h> @@ -34,7 +33,6 @@ ATMEL_TC_BEEVT | ATMEL_TC_BSWTRG) struct atmel_tcb_pwm_device { - enum pwm_polarity polarity; /* PWM polarity */ unsigned div; /* PWM clock divider */ unsigned duty; /* PWM duty expressed in clk cycles */ unsigned period; /* PWM period expressed in clk cycles */ @@ -49,7 +47,6 @@ struct atmel_tcb_channel { }; struct atmel_tcb_pwm_chip { - struct pwm_chip chip; spinlock_t lock; u8 channel; u8 width; @@ -57,7 +54,7 @@ struct atmel_tcb_pwm_chip { struct clk *clk; struct clk *gclk; struct clk *slow_clk; - struct atmel_tcb_pwm_device *pwms[NPWM]; + struct atmel_tcb_pwm_device pwms[NPWM]; struct atmel_tcb_channel bkup; }; @@ -65,45 +62,27 @@ static const u8 atmel_tcb_divisors[] = { 2, 8, 32, 128, 0, }; static inline struct atmel_tcb_pwm_chip *to_tcb_chip(struct pwm_chip *chip) { - return container_of(chip, struct atmel_tcb_pwm_chip, chip); -} - -static int atmel_tcb_pwm_set_polarity(struct pwm_chip *chip, - struct pwm_device *pwm, - enum pwm_polarity polarity) -{ - struct atmel_tcb_pwm_chip *tcbpwmc = to_tcb_chip(chip); - struct atmel_tcb_pwm_device *tcbpwm = tcbpwmc->pwms[pwm->hwpwm]; - - tcbpwm->polarity = polarity; - - return 0; + return pwmchip_get_drvdata(chip); } static int atmel_tcb_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm) { struct atmel_tcb_pwm_chip *tcbpwmc = to_tcb_chip(chip); - struct atmel_tcb_pwm_device *tcbpwm; + struct atmel_tcb_pwm_device *tcbpwm = &tcbpwmc->pwms[pwm->hwpwm]; unsigned cmr; int ret; - tcbpwm = devm_kzalloc(chip->dev, sizeof(*tcbpwm), GFP_KERNEL); - if (!tcbpwm) - return -ENOMEM; - ret = clk_prepare_enable(tcbpwmc->clk); - if (ret) { - devm_kfree(chip->dev, tcbpwm); + if (ret) return ret; - } - tcbpwm->polarity = PWM_POLARITY_NORMAL; tcbpwm->duty = 0; tcbpwm->period = 0; tcbpwm->div = 0; - spin_lock(&tcbpwmc->lock); + guard(spinlock)(&tcbpwmc->lock); + regmap_read(tcbpwmc->regmap, ATMEL_TC_REG(tcbpwmc->channel, CMR), &cmr); /* * Get init config from Timer Counter registers if @@ -129,9 +108,6 @@ static int atmel_tcb_pwm_request(struct pwm_chip *chip, cmr |= ATMEL_TC_WAVE | ATMEL_TC_WAVESEL_UP_AUTO | ATMEL_TC_EEVT_XC0; regmap_write(tcbpwmc->regmap, ATMEL_TC_REG(tcbpwmc->channel, CMR), cmr); - spin_unlock(&tcbpwmc->lock); - - tcbpwmc->pwms[pwm->hwpwm] = tcbpwm; return 0; } @@ -139,19 +115,16 @@ static int atmel_tcb_pwm_request(struct pwm_chip *chip, static void atmel_tcb_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm) { struct atmel_tcb_pwm_chip *tcbpwmc = to_tcb_chip(chip); - struct atmel_tcb_pwm_device *tcbpwm = tcbpwmc->pwms[pwm->hwpwm]; clk_disable_unprepare(tcbpwmc->clk); - tcbpwmc->pwms[pwm->hwpwm] = NULL; - devm_kfree(chip->dev, tcbpwm); } -static void atmel_tcb_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) +static void atmel_tcb_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm, + enum pwm_polarity polarity) { struct atmel_tcb_pwm_chip *tcbpwmc = to_tcb_chip(chip); - struct atmel_tcb_pwm_device *tcbpwm = tcbpwmc->pwms[pwm->hwpwm]; + struct atmel_tcb_pwm_device *tcbpwm = &tcbpwmc->pwms[pwm->hwpwm]; unsigned cmr; - enum pwm_polarity polarity = tcbpwm->polarity; /* * If duty is 0 the timer will be stopped and we have to @@ -164,7 +137,6 @@ static void atmel_tcb_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) if (tcbpwm->duty == 0) polarity = !polarity; - spin_lock(&tcbpwmc->lock); regmap_read(tcbpwmc->regmap, ATMEL_TC_REG(tcbpwmc->channel, CMR), &cmr); /* flush old setting and set the new one */ @@ -199,16 +171,14 @@ static void atmel_tcb_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) ATMEL_TC_SWTRG); tcbpwmc->bkup.enabled = 0; } - - spin_unlock(&tcbpwmc->lock); } -static int atmel_tcb_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) +static int atmel_tcb_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm, + enum pwm_polarity polarity) { struct atmel_tcb_pwm_chip *tcbpwmc = to_tcb_chip(chip); - struct atmel_tcb_pwm_device *tcbpwm = tcbpwmc->pwms[pwm->hwpwm]; + struct atmel_tcb_pwm_device *tcbpwm = &tcbpwmc->pwms[pwm->hwpwm]; u32 cmr; - enum pwm_polarity polarity = tcbpwm->polarity; /* * If duty is 0 the timer will be stopped and we have to @@ -221,7 +191,6 @@ static int atmel_tcb_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) if (tcbpwm->duty == 0) polarity = !polarity; - spin_lock(&tcbpwmc->lock); regmap_read(tcbpwmc->regmap, ATMEL_TC_REG(tcbpwmc->channel, CMR), &cmr); /* flush old setting and set the new one */ @@ -283,7 +252,6 @@ static int atmel_tcb_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) regmap_write(tcbpwmc->regmap, ATMEL_TC_REG(tcbpwmc->channel, CCR), ATMEL_TC_SWTRG | ATMEL_TC_CLKEN); tcbpwmc->bkup.enabled = 1; - spin_unlock(&tcbpwmc->lock); return 0; } @@ -291,8 +259,9 @@ static int atmel_tcb_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, int duty_ns, int period_ns) { struct atmel_tcb_pwm_chip *tcbpwmc = to_tcb_chip(chip); - struct atmel_tcb_pwm_device *tcbpwm = tcbpwmc->pwms[pwm->hwpwm]; - struct atmel_tcb_pwm_device *atcbpwm = NULL; + struct atmel_tcb_pwm_device *tcbpwm = &tcbpwmc->pwms[pwm->hwpwm]; + /* companion PWM sharing register values period and div */ + struct atmel_tcb_pwm_device *atcbpwm = &tcbpwmc->pwms[pwm->hwpwm ^ 1]; int i = 0; int slowclk = 0; unsigned period; @@ -337,11 +306,6 @@ static int atmel_tcb_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, duty = div_u64(duty_ns, min); period = div_u64(period_ns, min); - if (pwm->hwpwm == 0) - atcbpwm = tcbpwmc->pwms[1]; - else - atcbpwm = tcbpwmc->pwms[0]; - /* * PWM devices provided by the TCB driver are grouped by 2. * PWM devices in a given group must be configured with the @@ -350,10 +314,9 @@ static int atmel_tcb_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, * We're checking the period value of the second PWM device * in this group before applying the new config. */ - if ((atcbpwm && atcbpwm->duty > 0 && - atcbpwm->duty != atcbpwm->period) && + if ((atcbpwm->duty > 0 && atcbpwm->duty != atcbpwm->period) && (atcbpwm->div != i || atcbpwm->period != period)) { - dev_err(chip->dev, + dev_err(pwmchip_parent(chip), "failed to configure period_ns: PWM group already configured with a different value\n"); return -EINVAL; } @@ -368,32 +331,31 @@ static int atmel_tcb_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, static int atmel_tcb_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, const struct pwm_state *state) { + struct atmel_tcb_pwm_chip *tcbpwmc = to_tcb_chip(chip); int duty_cycle, period; int ret; - /* This function only sets a flag in driver data */ - atmel_tcb_pwm_set_polarity(chip, pwm, state->polarity); + guard(spinlock)(&tcbpwmc->lock); if (!state->enabled) { - atmel_tcb_pwm_disable(chip, pwm); + atmel_tcb_pwm_disable(chip, pwm, state->polarity); return 0; } - period = state->period < INT_MAX ? state->period : INT_MAX; - duty_cycle = state->duty_cycle < INT_MAX ? state->duty_cycle : INT_MAX; + period = min(state->period, INT_MAX); + duty_cycle = min(state->duty_cycle, INT_MAX); ret = atmel_tcb_pwm_config(chip, pwm, duty_cycle, period); if (ret) return ret; - return atmel_tcb_pwm_enable(chip, pwm); + return atmel_tcb_pwm_enable(chip, pwm, state->polarity); } static const struct pwm_ops atmel_tcb_pwm_ops = { .request = atmel_tcb_pwm_request, .free = atmel_tcb_pwm_free, .apply = atmel_tcb_pwm_apply, - .owner = THIS_MODULE, }; static struct atmel_tcb_config tcb_rm9200_config = { @@ -418,17 +380,20 @@ static const struct of_device_id atmel_tcb_of_match[] = { static int atmel_tcb_pwm_probe(struct platform_device *pdev) { + struct pwm_chip *chip; const struct of_device_id *match; - struct atmel_tcb_pwm_chip *tcbpwm; + struct atmel_tcb_pwm_chip *tcbpwmc; const struct atmel_tcb_config *config; struct device_node *np = pdev->dev.of_node; - struct regmap *regmap; - struct clk *clk, *gclk = NULL; - struct clk *slow_clk; char clk_name[] = "t0_clk"; int err; int channel; + chip = devm_pwmchip_alloc(&pdev->dev, NPWM, sizeof(*tcbpwmc)); + if (IS_ERR(chip)) + return PTR_ERR(chip); + tcbpwmc = to_tcb_chip(chip); + err = of_property_read_u32(np, "reg", &channel); if (err < 0) { dev_err(&pdev->dev, @@ -437,80 +402,78 @@ static int atmel_tcb_pwm_probe(struct platform_device *pdev) return err; } - regmap = syscon_node_to_regmap(np->parent); - if (IS_ERR(regmap)) - return PTR_ERR(regmap); + tcbpwmc->regmap = syscon_node_to_regmap(np->parent); + if (IS_ERR(tcbpwmc->regmap)) + return PTR_ERR(tcbpwmc->regmap); - slow_clk = of_clk_get_by_name(np->parent, "slow_clk"); - if (IS_ERR(slow_clk)) - return PTR_ERR(slow_clk); + tcbpwmc->slow_clk = of_clk_get_by_name(np->parent, "slow_clk"); + if (IS_ERR(tcbpwmc->slow_clk)) + return PTR_ERR(tcbpwmc->slow_clk); clk_name[1] += channel; - clk = of_clk_get_by_name(np->parent, clk_name); - if (IS_ERR(clk)) - clk = of_clk_get_by_name(np->parent, "t0_clk"); - if (IS_ERR(clk)) - return PTR_ERR(clk); + tcbpwmc->clk = of_clk_get_by_name(np->parent, clk_name); + if (IS_ERR(tcbpwmc->clk)) + tcbpwmc->clk = of_clk_get_by_name(np->parent, "t0_clk"); + if (IS_ERR(tcbpwmc->clk)) { + err = PTR_ERR(tcbpwmc->clk); + goto err_slow_clk; + } match = of_match_node(atmel_tcb_of_match, np->parent); config = match->data; if (config->has_gclk) { - gclk = of_clk_get_by_name(np->parent, "gclk"); - if (IS_ERR(gclk)) - return PTR_ERR(gclk); + tcbpwmc->gclk = of_clk_get_by_name(np->parent, "gclk"); + if (IS_ERR(tcbpwmc->gclk)) { + err = PTR_ERR(tcbpwmc->gclk); + goto err_clk; + } } - tcbpwm = devm_kzalloc(&pdev->dev, sizeof(*tcbpwm), GFP_KERNEL); - if (tcbpwm == NULL) { - err = -ENOMEM; - goto err_slow_clk; - } + chip->ops = &atmel_tcb_pwm_ops; + tcbpwmc->channel = channel; + tcbpwmc->width = config->counter_width; - tcbpwm->chip.dev = &pdev->dev; - tcbpwm->chip.ops = &atmel_tcb_pwm_ops; - tcbpwm->chip.npwm = NPWM; - tcbpwm->channel = channel; - tcbpwm->regmap = regmap; - tcbpwm->clk = clk; - tcbpwm->gclk = gclk; - tcbpwm->slow_clk = slow_clk; - tcbpwm->width = config->counter_width; - - err = clk_prepare_enable(slow_clk); + err = clk_prepare_enable(tcbpwmc->slow_clk); if (err) - goto err_slow_clk; + goto err_gclk; - spin_lock_init(&tcbpwm->lock); + spin_lock_init(&tcbpwmc->lock); - err = pwmchip_add(&tcbpwm->chip); + err = pwmchip_add(chip); if (err < 0) goto err_disable_clk; - platform_set_drvdata(pdev, tcbpwm); + platform_set_drvdata(pdev, chip); return 0; err_disable_clk: - clk_disable_unprepare(tcbpwm->slow_clk); + clk_disable_unprepare(tcbpwmc->slow_clk); + +err_gclk: + clk_put(tcbpwmc->gclk); + +err_clk: + clk_put(tcbpwmc->clk); err_slow_clk: - clk_put(slow_clk); + clk_put(tcbpwmc->slow_clk); return err; } -static int atmel_tcb_pwm_remove(struct platform_device *pdev) +static void atmel_tcb_pwm_remove(struct platform_device *pdev) { - struct atmel_tcb_pwm_chip *tcbpwm = platform_get_drvdata(pdev); - - pwmchip_remove(&tcbpwm->chip); + struct pwm_chip *chip = platform_get_drvdata(pdev); + struct atmel_tcb_pwm_chip *tcbpwmc = to_tcb_chip(chip); - clk_disable_unprepare(tcbpwm->slow_clk); - clk_put(tcbpwm->slow_clk); - clk_put(tcbpwm->clk); + pwmchip_remove(chip); - return 0; + clk_disable_unprepare(tcbpwmc->slow_clk); + clk_put(tcbpwmc->gclk); + clk_put(tcbpwmc->clk); + clk_put(tcbpwmc->slow_clk); } static const struct of_device_id atmel_tcb_pwm_dt_ids[] = { @@ -519,49 +482,49 @@ static const struct of_device_id atmel_tcb_pwm_dt_ids[] = { }; MODULE_DEVICE_TABLE(of, atmel_tcb_pwm_dt_ids); -#ifdef CONFIG_PM_SLEEP static int atmel_tcb_pwm_suspend(struct device *dev) { - struct atmel_tcb_pwm_chip *tcbpwm = dev_get_drvdata(dev); - struct atmel_tcb_channel *chan = &tcbpwm->bkup; - unsigned int channel = tcbpwm->channel; + struct pwm_chip *chip = dev_get_drvdata(dev); + struct atmel_tcb_pwm_chip *tcbpwmc = to_tcb_chip(chip); + struct atmel_tcb_channel *chan = &tcbpwmc->bkup; + unsigned int channel = tcbpwmc->channel; - regmap_read(tcbpwm->regmap, ATMEL_TC_REG(channel, CMR), &chan->cmr); - regmap_read(tcbpwm->regmap, ATMEL_TC_REG(channel, RA), &chan->ra); - regmap_read(tcbpwm->regmap, ATMEL_TC_REG(channel, RB), &chan->rb); - regmap_read(tcbpwm->regmap, ATMEL_TC_REG(channel, RC), &chan->rc); + regmap_read(tcbpwmc->regmap, ATMEL_TC_REG(channel, CMR), &chan->cmr); + regmap_read(tcbpwmc->regmap, ATMEL_TC_REG(channel, RA), &chan->ra); + regmap_read(tcbpwmc->regmap, ATMEL_TC_REG(channel, RB), &chan->rb); + regmap_read(tcbpwmc->regmap, ATMEL_TC_REG(channel, RC), &chan->rc); return 0; } static int atmel_tcb_pwm_resume(struct device *dev) { - struct atmel_tcb_pwm_chip *tcbpwm = dev_get_drvdata(dev); - struct atmel_tcb_channel *chan = &tcbpwm->bkup; - unsigned int channel = tcbpwm->channel; + struct pwm_chip *chip = dev_get_drvdata(dev); + struct atmel_tcb_pwm_chip *tcbpwmc = to_tcb_chip(chip); + struct atmel_tcb_channel *chan = &tcbpwmc->bkup; + unsigned int channel = tcbpwmc->channel; - regmap_write(tcbpwm->regmap, ATMEL_TC_REG(channel, CMR), chan->cmr); - regmap_write(tcbpwm->regmap, ATMEL_TC_REG(channel, RA), chan->ra); - regmap_write(tcbpwm->regmap, ATMEL_TC_REG(channel, RB), chan->rb); - regmap_write(tcbpwm->regmap, ATMEL_TC_REG(channel, RC), chan->rc); + regmap_write(tcbpwmc->regmap, ATMEL_TC_REG(channel, CMR), chan->cmr); + regmap_write(tcbpwmc->regmap, ATMEL_TC_REG(channel, RA), chan->ra); + regmap_write(tcbpwmc->regmap, ATMEL_TC_REG(channel, RB), chan->rb); + regmap_write(tcbpwmc->regmap, ATMEL_TC_REG(channel, RC), chan->rc); if (chan->enabled) - regmap_write(tcbpwm->regmap, + regmap_write(tcbpwmc->regmap, ATMEL_TC_CLKEN | ATMEL_TC_SWTRG, ATMEL_TC_REG(channel, CCR)); return 0; } -#endif -static SIMPLE_DEV_PM_OPS(atmel_tcb_pwm_pm_ops, atmel_tcb_pwm_suspend, - atmel_tcb_pwm_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(atmel_tcb_pwm_pm_ops, atmel_tcb_pwm_suspend, + atmel_tcb_pwm_resume); static struct platform_driver atmel_tcb_pwm_driver = { .driver = { .name = "atmel-tcb-pwm", .of_match_table = atmel_tcb_pwm_dt_ids, - .pm = &atmel_tcb_pwm_pm_ops, + .pm = pm_ptr(&atmel_tcb_pwm_pm_ops), }, .probe = atmel_tcb_pwm_probe, .remove = atmel_tcb_pwm_remove, |
