diff options
Diffstat (limited to 'drivers/pwm/pwm-imx.c')
-rw-r--r-- | drivers/pwm/pwm-imx.c | 104 |
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, |