summaryrefslogtreecommitdiff
path: root/drivers/pwm/pwm-fsl-ftm.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/pwm/pwm-fsl-ftm.c')
-rw-r--r--drivers/pwm/pwm-fsl-ftm.c120
1 files changed, 71 insertions, 49 deletions
diff --git a/drivers/pwm/pwm-fsl-ftm.c b/drivers/pwm/pwm-fsl-ftm.c
index 5caadbd6194e..35406b2e1925 100644
--- a/drivers/pwm/pwm-fsl-ftm.c
+++ b/drivers/pwm/pwm-fsl-ftm.c
@@ -3,6 +3,7 @@
* Freescale FlexTimer Module (FTM) PWM Driver
*
* Copyright 2012-2013 Freescale Semiconductor, Inc.
+ * Copyright 2020-2025 NXP
*/
#include <linux/clk.h>
@@ -10,9 +11,7 @@
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
-#include <linux/mutex.h>
-#include <linux/of_address.h>
-#include <linux/of_device.h>
+#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/pm.h>
#include <linux/pwm.h>
@@ -32,6 +31,8 @@ enum fsl_pwm_clk {
struct fsl_ftm_soc {
bool has_enable_bits;
+ bool has_flt_reg;
+ unsigned int npwm;
};
struct fsl_pwm_periodcfg {
@@ -41,8 +42,6 @@ struct fsl_pwm_periodcfg {
};
struct fsl_pwm_chip {
- struct pwm_chip chip;
- struct mutex lock;
struct regmap *regmap;
/* This value is valid iff a pwm is running */
@@ -56,7 +55,7 @@ struct fsl_pwm_chip {
static inline struct fsl_pwm_chip *to_fsl_chip(struct pwm_chip *chip)
{
- return container_of(chip, struct fsl_pwm_chip, chip);
+ return pwmchip_get_drvdata(chip);
}
static void ftm_clear_write_protection(struct fsl_pwm_chip *fpc)
@@ -91,11 +90,8 @@ static int fsl_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm)
struct fsl_pwm_chip *fpc = to_fsl_chip(chip);
ret = clk_prepare_enable(fpc->ipg_clk);
- if (!ret && fpc->soc->has_enable_bits) {
- mutex_lock(&fpc->lock);
+ if (!ret && fpc->soc->has_enable_bits)
regmap_set_bits(fpc->regmap, FTM_SC, BIT(pwm->hwpwm + 16));
- mutex_unlock(&fpc->lock);
- }
return ret;
}
@@ -104,11 +100,8 @@ static void fsl_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm)
{
struct fsl_pwm_chip *fpc = to_fsl_chip(chip);
- if (fpc->soc->has_enable_bits) {
- mutex_lock(&fpc->lock);
+ if (fpc->soc->has_enable_bits)
regmap_clear_bits(fpc->regmap, FTM_SC, BIT(pwm->hwpwm + 16));
- mutex_unlock(&fpc->lock);
- }
clk_disable_unprepare(fpc->ipg_clk);
}
@@ -120,6 +113,9 @@ static unsigned int fsl_pwm_ticks_to_ns(struct fsl_pwm_chip *fpc,
unsigned long long exval;
rate = clk_get_rate(fpc->clk[fpc->period.clk_select]);
+ if (rate >> fpc->period.clk_ps == 0)
+ return 0;
+
exval = ticks;
exval *= 1000000000UL;
do_div(exval, rate >> fpc->period.clk_ps);
@@ -192,6 +188,9 @@ static unsigned int fsl_pwm_calculate_duty(struct fsl_pwm_chip *fpc,
unsigned int period = fpc->period.mod_period + 1;
unsigned int period_ns = fsl_pwm_ticks_to_ns(fpc, period);
+ if (!period_ns)
+ return 0;
+
duty = (unsigned long long)duty_ns * period;
do_div(duty, period_ns);
@@ -222,10 +221,11 @@ static bool fsl_pwm_is_other_pwm_enabled(struct fsl_pwm_chip *fpc,
return false;
}
-static int fsl_pwm_apply_config(struct fsl_pwm_chip *fpc,
+static int fsl_pwm_apply_config(struct pwm_chip *chip,
struct pwm_device *pwm,
const struct pwm_state *newstate)
{
+ struct fsl_pwm_chip *fpc = to_fsl_chip(chip);
unsigned int duty;
u32 reg_polarity;
@@ -233,7 +233,7 @@ static int fsl_pwm_apply_config(struct fsl_pwm_chip *fpc,
bool do_write_period = false;
if (!fsl_pwm_calculate_period(fpc, newstate->period, &periodcfg)) {
- dev_err(fpc->chip.dev, "failed to calculate new period\n");
+ dev_err(pwmchip_parent(chip), "failed to calculate new period\n");
return -EINVAL;
}
@@ -247,7 +247,7 @@ static int fsl_pwm_apply_config(struct fsl_pwm_chip *fpc,
*/
else if (!fsl_pwm_periodcfg_are_equal(&fpc->period, &periodcfg)) {
if (fsl_pwm_is_other_pwm_enabled(fpc, pwm)) {
- dev_err(fpc->chip.dev,
+ dev_err(pwmchip_parent(chip),
"Cannot change period for PWM %u, disable other PWMs first\n",
pwm->hwpwm);
return -EBUSY;
@@ -299,7 +299,7 @@ static int fsl_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
{
struct fsl_pwm_chip *fpc = to_fsl_chip(chip);
struct pwm_state *oldstate = &pwm->state;
- int ret = 0;
+ int ret;
/*
* oldstate to newstate : action
@@ -310,8 +310,6 @@ static int fsl_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
* disabled to enabled : update settings + enable
*/
- mutex_lock(&fpc->lock);
-
if (!newstate->enabled) {
if (oldstate->enabled) {
regmap_set_bits(fpc->regmap, FTM_OUTMASK,
@@ -320,30 +318,28 @@ static int fsl_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
clk_disable_unprepare(fpc->clk[fpc->period.clk_select]);
}
- goto end_mutex;
+ return 0;
}
- ret = fsl_pwm_apply_config(fpc, pwm, newstate);
+ ret = fsl_pwm_apply_config(chip, pwm, newstate);
if (ret)
- goto end_mutex;
+ return ret;
/* check if need to enable */
if (!oldstate->enabled) {
ret = clk_prepare_enable(fpc->clk[fpc->period.clk_select]);
if (ret)
- goto end_mutex;
+ return ret;
ret = clk_prepare_enable(fpc->clk[FSL_PWM_CLK_CNTEN]);
if (ret) {
clk_disable_unprepare(fpc->clk[fpc->period.clk_select]);
- goto end_mutex;
+ return ret;
}
regmap_clear_bits(fpc->regmap, FTM_OUTMASK, BIT(pwm->hwpwm));
}
-end_mutex:
- mutex_unlock(&fpc->lock);
return ret;
}
@@ -351,7 +347,6 @@ static const struct pwm_ops fsl_pwm_ops = {
.request = fsl_pwm_request,
.free = fsl_pwm_free,
.apply = fsl_pwm_apply,
- .owner = THIS_MODULE,
};
static int fsl_pwm_init(struct fsl_pwm_chip *fpc)
@@ -382,6 +377,20 @@ static bool fsl_pwm_volatile_reg(struct device *dev, unsigned int reg)
return false;
}
+static bool fsl_pwm_is_reg(struct device *dev, unsigned int reg)
+{
+ struct pwm_chip *chip = dev_get_drvdata(dev);
+ struct fsl_pwm_chip *fpc = to_fsl_chip(chip);
+
+ if (reg >= FTM_CSC(fpc->soc->npwm) && reg < FTM_CNTIN)
+ return false;
+
+ if ((reg == FTM_FLTCTRL || reg == FTM_FLTPOL) && !fpc->soc->has_flt_reg)
+ return false;
+
+ return true;
+}
+
static const struct regmap_config fsl_pwm_regmap_config = {
.reg_bits = 32,
.reg_stride = 4,
@@ -390,22 +399,24 @@ static const struct regmap_config fsl_pwm_regmap_config = {
.max_register = FTM_PWMLOAD,
.volatile_reg = fsl_pwm_volatile_reg,
.cache_type = REGCACHE_FLAT,
+ .writeable_reg = fsl_pwm_is_reg,
+ .readable_reg = fsl_pwm_is_reg,
};
static int fsl_pwm_probe(struct platform_device *pdev)
{
+ const struct fsl_ftm_soc *soc = of_device_get_match_data(&pdev->dev);
+ struct pwm_chip *chip;
struct fsl_pwm_chip *fpc;
void __iomem *base;
int ret;
- fpc = devm_kzalloc(&pdev->dev, sizeof(*fpc), GFP_KERNEL);
- if (!fpc)
- return -ENOMEM;
-
- mutex_init(&fpc->lock);
+ chip = devm_pwmchip_alloc(&pdev->dev, soc->npwm, sizeof(*fpc));
+ if (IS_ERR(chip))
+ return PTR_ERR(chip);
+ fpc = to_fsl_chip(chip);
- fpc->soc = of_device_get_match_data(&pdev->dev);
- fpc->chip.dev = &pdev->dev;
+ fpc->soc = soc;
base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(base))
@@ -424,16 +435,16 @@ static int fsl_pwm_probe(struct platform_device *pdev)
return PTR_ERR(fpc->clk[FSL_PWM_CLK_SYS]);
}
- fpc->clk[FSL_PWM_CLK_FIX] = devm_clk_get(fpc->chip.dev, "ftm_fix");
+ fpc->clk[FSL_PWM_CLK_FIX] = devm_clk_get(&pdev->dev, "ftm_fix");
if (IS_ERR(fpc->clk[FSL_PWM_CLK_FIX]))
return PTR_ERR(fpc->clk[FSL_PWM_CLK_FIX]);
- fpc->clk[FSL_PWM_CLK_EXT] = devm_clk_get(fpc->chip.dev, "ftm_ext");
+ fpc->clk[FSL_PWM_CLK_EXT] = devm_clk_get(&pdev->dev, "ftm_ext");
if (IS_ERR(fpc->clk[FSL_PWM_CLK_EXT]))
return PTR_ERR(fpc->clk[FSL_PWM_CLK_EXT]);
fpc->clk[FSL_PWM_CLK_CNTEN] =
- devm_clk_get(fpc->chip.dev, "ftm_cnt_clk_en");
+ devm_clk_get(&pdev->dev, "ftm_cnt_clk_en");
if (IS_ERR(fpc->clk[FSL_PWM_CLK_CNTEN]))
return PTR_ERR(fpc->clk[FSL_PWM_CLK_CNTEN]);
@@ -445,17 +456,15 @@ static int fsl_pwm_probe(struct platform_device *pdev)
if (IS_ERR(fpc->ipg_clk))
fpc->ipg_clk = fpc->clk[FSL_PWM_CLK_SYS];
+ chip->ops = &fsl_pwm_ops;
- fpc->chip.ops = &fsl_pwm_ops;
- fpc->chip.npwm = 8;
-
- ret = devm_pwmchip_add(&pdev->dev, &fpc->chip);
+ ret = devm_pwmchip_add(&pdev->dev, chip);
if (ret < 0) {
dev_err(&pdev->dev, "failed to add PWM chip: %d\n", ret);
return ret;
}
- platform_set_drvdata(pdev, fpc);
+ platform_set_drvdata(pdev, chip);
return fsl_pwm_init(fpc);
}
@@ -463,14 +472,15 @@ static int fsl_pwm_probe(struct platform_device *pdev)
#ifdef CONFIG_PM_SLEEP
static int fsl_pwm_suspend(struct device *dev)
{
- struct fsl_pwm_chip *fpc = dev_get_drvdata(dev);
+ struct pwm_chip *chip = dev_get_drvdata(dev);
+ struct fsl_pwm_chip *fpc = to_fsl_chip(chip);
int i;
regcache_cache_only(fpc->regmap, true);
regcache_mark_dirty(fpc->regmap);
- for (i = 0; i < fpc->chip.npwm; i++) {
- struct pwm_device *pwm = &fpc->chip.pwms[i];
+ for (i = 0; i < chip->npwm; i++) {
+ struct pwm_device *pwm = &chip->pwms[i];
if (!test_bit(PWMF_REQUESTED, &pwm->flags))
continue;
@@ -489,11 +499,12 @@ static int fsl_pwm_suspend(struct device *dev)
static int fsl_pwm_resume(struct device *dev)
{
- struct fsl_pwm_chip *fpc = dev_get_drvdata(dev);
+ struct pwm_chip *chip = dev_get_drvdata(dev);
+ struct fsl_pwm_chip *fpc = to_fsl_chip(chip);
int i;
- for (i = 0; i < fpc->chip.npwm; i++) {
- struct pwm_device *pwm = &fpc->chip.pwms[i];
+ for (i = 0; i < chip->npwm; i++) {
+ struct pwm_device *pwm = &chip->pwms[i];
if (!test_bit(PWMF_REQUESTED, &pwm->flags))
continue;
@@ -521,15 +532,26 @@ static const struct dev_pm_ops fsl_pwm_pm_ops = {
static const struct fsl_ftm_soc vf610_ftm_pwm = {
.has_enable_bits = false,
+ .has_flt_reg = true,
+ .npwm = 8,
};
static const struct fsl_ftm_soc imx8qm_ftm_pwm = {
.has_enable_bits = true,
+ .has_flt_reg = true,
+ .npwm = 8,
+};
+
+static const struct fsl_ftm_soc s32g2_ftm_pwm = {
+ .has_enable_bits = true,
+ .has_flt_reg = false,
+ .npwm = 6,
};
static const struct of_device_id fsl_pwm_dt_ids[] = {
{ .compatible = "fsl,vf610-ftm-pwm", .data = &vf610_ftm_pwm },
{ .compatible = "fsl,imx8qm-ftm-pwm", .data = &imx8qm_ftm_pwm },
+ { .compatible = "nxp,s32g2-ftm-pwm", .data = &s32g2_ftm_pwm },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, fsl_pwm_dt_ids);