summaryrefslogtreecommitdiffstats
path: root/drivers/pwm/pwm-imx.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/pwm/pwm-imx.c')
-rw-r--r--drivers/pwm/pwm-imx.c104
1 files changed, 69 insertions, 35 deletions
diff --git a/drivers/pwm/pwm-imx.c b/drivers/pwm/pwm-imx.c
index b620e502f2..2a75400593 100644
--- a/drivers/pwm/pwm-imx.c
+++ b/drivers/pwm/pwm-imx.c
@@ -1,10 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* simple driver for PWM (Pulse Width Modulator) controller
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
* Derived from pxa PWM driver by eric miao <eric.miao@marvell.com>
*/
@@ -15,7 +12,7 @@
#include <pwm.h>
#include <linux/clk.h>
#include <linux/err.h>
-#include <asm-generic/div64.h>
+#include <linux/math64.h>
/* i.MX1 and i.MX21 share the same PWM function block: */
@@ -39,7 +36,7 @@
#define MX3_PWMCR_EN (1 << 0)
struct imx_chip {
- struct clk *clk_per;
+ struct clk *clk_per, *clk_ipg;
void __iomem *mmio_base;
@@ -96,14 +93,42 @@ static void imx_pwm_set_enable_v1(struct pwm_chip *chip, bool enable)
writel(val, imx->mmio_base + MX1_PWMC);
}
+static int imx_pwm_clk_enable_v2(struct imx_chip *imx)
+{
+ int ret;
+
+ ret = clk_enable(imx->clk_ipg);
+ if (ret)
+ return ret;
+
+ ret = clk_prepare_enable(imx->clk_per);
+ if (ret) {
+ clk_disable_unprepare(imx->clk_ipg);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void imx_pwm_clk_disable_v2(struct imx_chip *imx)
+{
+ clk_disable_unprepare(imx->clk_per);
+ clk_disable_unprepare(imx->clk_ipg);
+}
+
static int imx_pwm_config_v2(struct pwm_chip *chip,
int duty_ns, int period_ns)
{
struct imx_chip *imx = to_imx_chip(chip);
unsigned long long c;
unsigned long period_cycles, duty_cycles, prescale;
+ int ret;
u32 cr;
+ ret = imx_pwm_clk_enable_v2(imx);
+ if (ret)
+ return ret;
+
c = clk_get_rate(imx->clk_per);
c = c * period_ns;
do_div(c, 1000000000);
@@ -137,6 +162,9 @@ static int imx_pwm_config_v2(struct pwm_chip *chip,
writel(cr, imx->mmio_base + MX3_PWMCR);
+ if (!chip->state.enabled)
+ imx_pwm_clk_disable_v2(imx);
+
return 0;
}
@@ -144,6 +172,11 @@ static void imx_pwm_set_enable_v2(struct pwm_chip *chip, bool enable)
{
struct imx_chip *imx = to_imx_chip(chip);
u32 val;
+ int ret;
+
+ ret = imx_pwm_clk_enable_v2(imx);
+ if (WARN_ON(ret))
+ return;
val = readl(imx->mmio_base + MX3_PWMCR);
@@ -153,39 +186,38 @@ static void imx_pwm_set_enable_v2(struct pwm_chip *chip, bool enable)
val &= ~MX3_PWMCR_EN;
writel(val, imx->mmio_base + MX3_PWMCR);
+
+ if (!enable)
+ imx_pwm_clk_disable_v2(imx);
}
-static int imx_pwm_config(struct pwm_chip *chip,
- int duty_ns, int period_ns)
+static int imx_pwm_apply(struct pwm_chip *chip,
+ struct pwm_device *pwm,
+ const struct pwm_state *state)
{
struct imx_chip *imx = to_imx_chip(chip);
+ bool enabled;
int ret;
- ret = imx->config(chip, duty_ns, period_ns);
+ enabled = chip->state.enabled;
- return ret;
-}
+ if (enabled && !state->enabled) {
+ imx->set_enable(chip, false);
+ return 0;
+ }
-static int imx_pwm_enable(struct pwm_chip *chip)
-{
- struct imx_chip *imx = to_imx_chip(chip);
+ ret = imx->config(chip, state->duty_cycle, state->period);
+ if (ret)
+ return ret;
- imx->set_enable(chip, true);
+ if (!enabled && state->enabled)
+ imx->set_enable(chip, true);
return 0;
}
-static void imx_pwm_disable(struct pwm_chip *chip)
-{
- struct imx_chip *imx = to_imx_chip(chip);
-
- imx->set_enable(chip, false);
-}
-
static struct pwm_ops imx_pwm_ops = {
- .enable = imx_pwm_enable,
- .disable = imx_pwm_disable,
- .config = imx_pwm_config,
+ .apply = imx_pwm_apply,
};
struct imx_pwm_data {
@@ -209,13 +241,14 @@ static struct of_device_id imx_pwm_dt_ids[] = {
{ .compatible = "fsl,imx27-pwm", .data = &imx_pwm_data_v2, },
{ /* sentinel */ }
};
+MODULE_DEVICE_TABLE(of, imx_pwm_dt_ids);
-static int imx_pwm_probe(struct device_d *dev)
+static int imx_pwm_probe(struct device *dev)
{
struct resource *iores;
const struct imx_pwm_data *data;
struct imx_chip *imx;
- int ret = 0;
+ int ret;
ret = dev_get_drvdata(dev, (const void **)&data);
if (ret)
@@ -223,6 +256,10 @@ static int imx_pwm_probe(struct device_d *dev)
imx = xzalloc(sizeof(*imx));
+ imx->clk_ipg = clk_get_optional(dev, "ipg");
+ if (IS_ERR(imx->clk_ipg))
+ return PTR_ERR(imx->clk_ipg);
+
imx->clk_per = clk_get(dev, "per");
if (IS_ERR(imx->clk_per))
return PTR_ERR(imx->clk_per);
@@ -232,9 +269,10 @@ static int imx_pwm_probe(struct device_d *dev)
return PTR_ERR(iores);
imx->mmio_base = IOMEM(iores->start);
+ imx->chip.dev = dev;
imx->chip.ops = &imx_pwm_ops;
- if (dev->device_node) {
- imx->chip.devname = of_alias_get(dev->device_node);
+ if (dev->of_node) {
+ imx->chip.devname = of_alias_get(dev->of_node);
if (!imx->chip.devname)
imx->chip.devname = basprintf("pwm_%p",
imx->mmio_base);
@@ -245,14 +283,10 @@ static int imx_pwm_probe(struct device_d *dev)
imx->config = data->config;
imx->set_enable = data->set_enable;
- ret = pwmchip_add(&imx->chip, dev);
- if (ret < 0)
- return ret;
-
- return 0;
+ return pwmchip_add(&imx->chip);
}
-static struct driver_d imx_pwm_driver = {
+static struct driver imx_pwm_driver = {
.name = "imx-pwm",
.of_compatible = imx_pwm_dt_ids,
.probe = imx_pwm_probe,