summaryrefslogtreecommitdiff
path: root/drivers/pwm/pwm-atmel-tcb.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/pwm/pwm-atmel-tcb.c')
-rw-r--r--drivers/pwm/pwm-atmel-tcb.c223
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,