diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/pwm/Kconfig | 6 | ||||
-rw-r--r-- | drivers/pwm/pwm-atmel-tcb.c | 1 | ||||
-rw-r--r-- | drivers/pwm/pwm-imx.c | 3 | ||||
-rw-r--r-- | drivers/pwm/pwm-jz4740.c | 41 | ||||
-rw-r--r-- | drivers/pwm/pwm-mediatek.c | 36 | ||||
-rw-r--r-- | drivers/pwm/pwm-puv3.c | 4 | ||||
-rw-r--r-- | drivers/pwm/pwm-rcar.c | 58 | ||||
-rw-r--r-- | drivers/pwm/pwm-stm32-lp.c | 5 | ||||
-rw-r--r-- | drivers/pwm/pwm-stm32.c | 22 | ||||
-rw-r--r-- | drivers/pwm/pwm-sun4i.c | 38 | ||||
-rw-r--r-- | drivers/pwm/sysfs.c | 3 |
11 files changed, 161 insertions, 56 deletions
diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index 38d49dbbf9b7..4635cb35008c 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig @@ -200,10 +200,10 @@ config PWM_IMX will be called pwm-imx. config PWM_JZ4740 - tristate "Ingenic JZ4740 PWM support" - depends on MACH_JZ4740 + tristate "Ingenic JZ47xx PWM support" + depends on MACH_INGENIC help - Generic PWM framework driver for Ingenic JZ4740 based + Generic PWM framework driver for Ingenic JZ47xx based machines. To compile this driver as a module, choose M here: the module diff --git a/drivers/pwm/pwm-atmel-tcb.c b/drivers/pwm/pwm-atmel-tcb.c index acd3ce8ecf3f..4fb1be246c44 100644 --- a/drivers/pwm/pwm-atmel-tcb.c +++ b/drivers/pwm/pwm-atmel-tcb.c @@ -401,7 +401,6 @@ static int atmel_tcb_pwm_probe(struct platform_device *pdev) tcbpwm = devm_kzalloc(&pdev->dev, sizeof(*tcbpwm), GFP_KERNEL); if (tcbpwm == NULL) { err = -ENOMEM; - dev_err(&pdev->dev, "failed to allocate memory\n"); goto err_free_tc; } diff --git a/drivers/pwm/pwm-imx.c b/drivers/pwm/pwm-imx.c index 2ba5c3a398ff..08cbe8120588 100644 --- a/drivers/pwm/pwm-imx.c +++ b/drivers/pwm/pwm-imx.c @@ -35,6 +35,7 @@ #define MX3_PWMSAR 0x0C /* PWM Sample Register */ #define MX3_PWMPR 0x10 /* PWM Period Register */ #define MX3_PWMCR_PRESCALER(x) ((((x) - 1) & 0xFFF) << 4) +#define MX3_PWMCR_STOPEN (1 << 25) #define MX3_PWMCR_DOZEEN (1 << 24) #define MX3_PWMCR_WAITEN (1 << 23) #define MX3_PWMCR_DBGEN (1 << 22) @@ -210,7 +211,7 @@ static int imx_pwm_apply_v2(struct pwm_chip *chip, struct pwm_device *pwm, writel(period_cycles, imx->mmio_base + MX3_PWMPR); cr = MX3_PWMCR_PRESCALER(prescale) | - MX3_PWMCR_DOZEEN | MX3_PWMCR_WAITEN | + MX3_PWMCR_STOPEN | MX3_PWMCR_DOZEEN | MX3_PWMCR_WAITEN | MX3_PWMCR_DBGEN | MX3_PWMCR_CLKSRC_IPG_HIGH | MX3_PWMCR_EN; diff --git a/drivers/pwm/pwm-jz4740.c b/drivers/pwm/pwm-jz4740.c index a75ff3622450..a7b134af5e04 100644 --- a/drivers/pwm/pwm-jz4740.c +++ b/drivers/pwm/pwm-jz4740.c @@ -18,6 +18,7 @@ #include <linux/gpio.h> #include <linux/kernel.h> #include <linux/module.h> +#include <linux/of_device.h> #include <linux/platform_device.h> #include <linux/pwm.h> @@ -71,9 +72,15 @@ static void jz4740_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) { uint32_t ctrl = jz4740_timer_get_ctrl(pwm->hwpwm); + /* Disable PWM output. + * In TCU2 mode (channel 1/2 on JZ4750+), this must be done before the + * counter is stopped, while in TCU1 mode the order does not matter. + */ ctrl &= ~JZ_TIMER_CTRL_PWM_ENABLE; - jz4740_timer_disable(pwm->hwpwm); jz4740_timer_set_ctrl(pwm->hwpwm, ctrl); + + /* Stop counter */ + jz4740_timer_disable(pwm->hwpwm); } static int jz4740_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, @@ -124,10 +131,29 @@ static int jz4740_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, return 0; } +static int jz4740_pwm_set_polarity(struct pwm_chip *chip, + struct pwm_device *pwm, enum pwm_polarity polarity) +{ + uint32_t ctrl = jz4740_timer_get_ctrl(pwm->pwm); + + switch (polarity) { + case PWM_POLARITY_NORMAL: + ctrl &= ~JZ_TIMER_CTRL_PWM_ACTIVE_LOW; + break; + case PWM_POLARITY_INVERSED: + ctrl |= JZ_TIMER_CTRL_PWM_ACTIVE_LOW; + break; + } + + jz4740_timer_set_ctrl(pwm->hwpwm, ctrl); + return 0; +} + static const struct pwm_ops jz4740_pwm_ops = { .request = jz4740_pwm_request, .free = jz4740_pwm_free, .config = jz4740_pwm_config, + .set_polarity = jz4740_pwm_set_polarity, .enable = jz4740_pwm_enable, .disable = jz4740_pwm_disable, .owner = THIS_MODULE, @@ -149,6 +175,8 @@ static int jz4740_pwm_probe(struct platform_device *pdev) jz4740->chip.ops = &jz4740_pwm_ops; jz4740->chip.npwm = NUM_PWM; jz4740->chip.base = -1; + jz4740->chip.of_xlate = of_pwm_xlate_with_flags; + jz4740->chip.of_pwm_n_cells = 3; platform_set_drvdata(pdev, jz4740); @@ -162,9 +190,20 @@ static int jz4740_pwm_remove(struct platform_device *pdev) return pwmchip_remove(&jz4740->chip); } +#ifdef CONFIG_OF +static const struct of_device_id jz4740_pwm_dt_ids[] = { + { .compatible = "ingenic,jz4740-pwm", }, + { .compatible = "ingenic,jz4770-pwm", }, + { .compatible = "ingenic,jz4780-pwm", }, + {}, +}; +MODULE_DEVICE_TABLE(of, jz4740_pwm_dt_ids); +#endif + static struct platform_driver jz4740_pwm_driver = { .driver = { .name = "jz4740-pwm", + .of_match_table = of_match_ptr(jz4740_pwm_dt_ids), }, .probe = jz4740_pwm_probe, .remove = jz4740_pwm_remove, diff --git a/drivers/pwm/pwm-mediatek.c b/drivers/pwm/pwm-mediatek.c index f5d97e0ad52b..328c124773b2 100644 --- a/drivers/pwm/pwm-mediatek.c +++ b/drivers/pwm/pwm-mediatek.c @@ -29,7 +29,9 @@ #define PWMGDUR 0x0c #define PWMWAVENUM 0x28 #define PWMDWIDTH 0x2c +#define PWM45DWIDTH_FIXUP 0x30 #define PWMTHRES 0x30 +#define PWM45THRES_FIXUP 0x34 #define PWM_CLK_DIV_MAX 7 @@ -54,6 +56,7 @@ static const char * const mtk_pwm_clk_name[MTK_CLK_MAX] = { struct mtk_pwm_platform_data { unsigned int num_pwms; + bool pwm45_fixup; }; /** @@ -66,6 +69,7 @@ struct mtk_pwm_chip { struct pwm_chip chip; void __iomem *regs; struct clk *clks[MTK_CLK_MAX]; + const struct mtk_pwm_platform_data *soc; }; static const unsigned int mtk_pwm_reg_offset[] = { @@ -131,18 +135,25 @@ static int mtk_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, { struct mtk_pwm_chip *pc = to_mtk_pwm_chip(chip); struct clk *clk = pc->clks[MTK_CLK_PWM1 + pwm->hwpwm]; - u32 resolution, clkdiv = 0; + u32 clkdiv = 0, cnt_period, cnt_duty, reg_width = PWMDWIDTH, + reg_thres = PWMTHRES; + u64 resolution; int ret; ret = mtk_pwm_clk_enable(chip, pwm); if (ret < 0) return ret; - resolution = NSEC_PER_SEC / clk_get_rate(clk); + /* Using resolution in picosecond gets accuracy higher */ + resolution = (u64)NSEC_PER_SEC * 1000; + do_div(resolution, clk_get_rate(clk)); - while (period_ns / resolution > 8191) { + cnt_period = DIV_ROUND_CLOSEST_ULL((u64)period_ns * 1000, resolution); + while (cnt_period > 8191) { resolution *= 2; clkdiv++; + cnt_period = DIV_ROUND_CLOSEST_ULL((u64)period_ns * 1000, + resolution); } if (clkdiv > PWM_CLK_DIV_MAX) { @@ -151,9 +162,19 @@ static int mtk_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, return -EINVAL; } + if (pc->soc->pwm45_fixup && pwm->hwpwm > 2) { + /* + * PWM[4,5] has distinct offset for PWMDWIDTH and PWMTHRES + * from the other PWMs on MT7623. + */ + reg_width = PWM45DWIDTH_FIXUP; + reg_thres = PWM45THRES_FIXUP; + } + + cnt_duty = DIV_ROUND_CLOSEST_ULL((u64)duty_ns * 1000, resolution); mtk_pwm_writel(pc, pwm->hwpwm, PWMCON, BIT(15) | clkdiv); - mtk_pwm_writel(pc, pwm->hwpwm, PWMDWIDTH, period_ns / resolution); - mtk_pwm_writel(pc, pwm->hwpwm, PWMTHRES, duty_ns / resolution); + mtk_pwm_writel(pc, pwm->hwpwm, reg_width, cnt_period); + mtk_pwm_writel(pc, pwm->hwpwm, reg_thres, cnt_duty); mtk_pwm_clk_disable(chip, pwm); @@ -211,6 +232,7 @@ static int mtk_pwm_probe(struct platform_device *pdev) data = of_device_get_match_data(&pdev->dev); if (data == NULL) return -EINVAL; + pc->soc = data; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); pc->regs = devm_ioremap_resource(&pdev->dev, res); @@ -251,14 +273,17 @@ static int mtk_pwm_remove(struct platform_device *pdev) static const struct mtk_pwm_platform_data mt2712_pwm_data = { .num_pwms = 8, + .pwm45_fixup = false, }; static const struct mtk_pwm_platform_data mt7622_pwm_data = { .num_pwms = 6, + .pwm45_fixup = false, }; static const struct mtk_pwm_platform_data mt7623_pwm_data = { .num_pwms = 5, + .pwm45_fixup = true, }; static const struct of_device_id mtk_pwm_of_match[] = { @@ -280,5 +305,4 @@ static struct platform_driver mtk_pwm_driver = { module_platform_driver(mtk_pwm_driver); MODULE_AUTHOR("John Crispin <blogic@openwrt.org>"); -MODULE_ALIAS("platform:mtk-pwm"); MODULE_LICENSE("GPL"); diff --git a/drivers/pwm/pwm-puv3.c b/drivers/pwm/pwm-puv3.c index ed6007b27585..754fd9a98f6b 100644 --- a/drivers/pwm/pwm-puv3.c +++ b/drivers/pwm/pwm-puv3.c @@ -107,10 +107,8 @@ static int pwm_probe(struct platform_device *pdev) int ret; puv3 = devm_kzalloc(&pdev->dev, sizeof(*puv3), GFP_KERNEL); - if (puv3 == NULL) { - dev_err(&pdev->dev, "failed to allocate memory\n"); + if (!puv3) return -ENOMEM; - } puv3->clk = devm_clk_get(&pdev->dev, "OST_CLK"); if (IS_ERR(puv3->clk)) diff --git a/drivers/pwm/pwm-rcar.c b/drivers/pwm/pwm-rcar.c index 1c85ecc9e7ac..91d11f2e2fef 100644 --- a/drivers/pwm/pwm-rcar.c +++ b/drivers/pwm/pwm-rcar.c @@ -134,16 +134,12 @@ static int rcar_pwm_set_counter(struct rcar_pwm_chip *rp, int div, int duty_ns, static int rcar_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm) { - struct rcar_pwm_chip *rp = to_rcar_pwm_chip(chip); - - return clk_prepare_enable(rp->clk); + return pm_runtime_get_sync(chip->dev); } static void rcar_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm) { - struct rcar_pwm_chip *rp = to_rcar_pwm_chip(chip); - - clk_disable_unprepare(rp->clk); + pm_runtime_put(chip->dev); } static int rcar_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, @@ -156,8 +152,12 @@ static int rcar_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, if (div < 0) return div; - /* Let the core driver set pwm->period if disabled and duty_ns == 0 */ - if (!pwm_is_enabled(pwm) && !duty_ns) + /* + * Let the core driver set pwm->period if disabled and duty_ns == 0. + * But, this driver should prevent to set the new duty_ns if current + * duty_cycle is not set + */ + if (!pwm_is_enabled(pwm) && !duty_ns && !pwm->state.duty_cycle) return 0; rcar_pwm_update(rp, RCAR_PWMCR_SYNC, RCAR_PWMCR_SYNC, RCAR_PWMCR); @@ -258,11 +258,53 @@ static const struct of_device_id rcar_pwm_of_table[] = { }; MODULE_DEVICE_TABLE(of, rcar_pwm_of_table); +#ifdef CONFIG_PM_SLEEP +static struct pwm_device *rcar_pwm_dev_to_pwm_dev(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct rcar_pwm_chip *rcar_pwm = platform_get_drvdata(pdev); + struct pwm_chip *chip = &rcar_pwm->chip; + + return &chip->pwms[0]; +} + +static int rcar_pwm_suspend(struct device *dev) +{ + struct pwm_device *pwm = rcar_pwm_dev_to_pwm_dev(dev); + + if (!test_bit(PWMF_REQUESTED, &pwm->flags)) + return 0; + + pm_runtime_put(dev); + + return 0; +} + +static int rcar_pwm_resume(struct device *dev) +{ + struct pwm_device *pwm = rcar_pwm_dev_to_pwm_dev(dev); + + if (!test_bit(PWMF_REQUESTED, &pwm->flags)) + return 0; + + pm_runtime_get_sync(dev); + + rcar_pwm_config(pwm->chip, pwm, pwm->state.duty_cycle, + pwm->state.period); + if (pwm_is_enabled(pwm)) + rcar_pwm_enable(pwm->chip, pwm); + + return 0; +} +#endif /* CONFIG_PM_SLEEP */ +static SIMPLE_DEV_PM_OPS(rcar_pwm_pm_ops, rcar_pwm_suspend, rcar_pwm_resume); + static struct platform_driver rcar_pwm_driver = { .probe = rcar_pwm_probe, .remove = rcar_pwm_remove, .driver = { .name = "pwm-rcar", + .pm = &rcar_pwm_pm_ops, .of_match_table = of_match_ptr(rcar_pwm_of_table), } }; diff --git a/drivers/pwm/pwm-stm32-lp.c b/drivers/pwm/pwm-stm32-lp.c index 1ac9e4384142..7c13e2505080 100644 --- a/drivers/pwm/pwm-stm32-lp.c +++ b/drivers/pwm/pwm-stm32-lp.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * STM32 Low-Power Timer PWM driver * @@ -5,8 +6,6 @@ * * Author: Gerald Baeza <gerald.baeza@st.com> * - * License terms: GNU General Public License (GPL), version 2 - * * Inspired by Gerald Baeza's pwm-stm32 driver */ @@ -203,6 +202,8 @@ static int stm32_pwm_lp_probe(struct platform_device *pdev) priv->chip.dev = &pdev->dev; priv->chip.ops = &stm32_pwm_lp_ops; priv->chip.npwm = 1; + priv->chip.of_xlate = of_pwm_xlate_with_flags; + priv->chip.of_pwm_n_cells = 3; ret = pwmchip_add(&priv->chip); if (ret < 0) diff --git a/drivers/pwm/pwm-stm32.c b/drivers/pwm/pwm-stm32.c index 6139512aab7b..2708212933f7 100644 --- a/drivers/pwm/pwm-stm32.c +++ b/drivers/pwm/pwm-stm32.c @@ -1,10 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) STMicroelectronics 2016 * * Author: Gerald Baeza <gerald.baeza@st.com> * - * License terms: GNU General Public License (GPL), version 2 - * * Inspired by timer-stm32.c from Maxime Coquelin * pwm-atmel.c from Bo Shen */ @@ -21,7 +20,7 @@ struct stm32_pwm { struct pwm_chip chip; - struct device *dev; + struct mutex lock; /* protect pwm config/enable */ struct clk *clk; struct regmap *regmap; u32 max_arr; @@ -214,9 +213,23 @@ static int stm32_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, return ret; } +static int stm32_pwm_apply_locked(struct pwm_chip *chip, struct pwm_device *pwm, + struct pwm_state *state) +{ + struct stm32_pwm *priv = to_stm32_pwm_dev(chip); + int ret; + + /* protect common prescaler for all active channels */ + mutex_lock(&priv->lock); + ret = stm32_pwm_apply(chip, pwm, state); + mutex_unlock(&priv->lock); + + return ret; +} + static const struct pwm_ops stm32pwm_ops = { .owner = THIS_MODULE, - .apply = stm32_pwm_apply, + .apply = stm32_pwm_apply_locked, }; static int stm32_pwm_set_breakinput(struct stm32_pwm *priv, @@ -336,6 +349,7 @@ static int stm32_pwm_probe(struct platform_device *pdev) if (!priv) return -ENOMEM; + mutex_init(&priv->lock); priv->regmap = ddata->regmap; priv->clk = ddata->clk; priv->max_arr = ddata->max_arr; diff --git a/drivers/pwm/pwm-sun4i.c b/drivers/pwm/pwm-sun4i.c index 334199c58f1d..470d4f71e7eb 100644 --- a/drivers/pwm/pwm-sun4i.c +++ b/drivers/pwm/pwm-sun4i.c @@ -73,7 +73,6 @@ static const u32 prescaler_table[] = { struct sun4i_pwm_data { bool has_prescaler_bypass; - bool has_rdy; unsigned int npwm; }; @@ -117,7 +116,8 @@ static void sun4i_pwm_get_state(struct pwm_chip *chip, val = sun4i_pwm_readl(sun4i_pwm, PWM_CTRL_REG); - if ((val == PWM_PRESCAL_MASK) && sun4i_pwm->data->has_prescaler_bypass) + if ((PWM_REG_PRESCAL(val, pwm->hwpwm) == PWM_PRESCAL_MASK) && + sun4i_pwm->data->has_prescaler_bypass) prescaler = 1; else prescaler = prescaler_table[PWM_REG_PRESCAL(val, pwm->hwpwm)]; @@ -130,7 +130,8 @@ static void sun4i_pwm_get_state(struct pwm_chip *chip, else state->polarity = PWM_POLARITY_INVERSED; - if (val & BIT_CH(PWM_CLK_GATING | PWM_EN, pwm->hwpwm)) + if ((val & BIT_CH(PWM_CLK_GATING | PWM_EN, pwm->hwpwm)) == + BIT_CH(PWM_CLK_GATING | PWM_EN, pwm->hwpwm)) state->enabled = true; else state->enabled = false; @@ -311,52 +312,37 @@ static const struct pwm_ops sun4i_pwm_ops = { .owner = THIS_MODULE, }; -static const struct sun4i_pwm_data sun4i_pwm_data_a10 = { +static const struct sun4i_pwm_data sun4i_pwm_dual_nobypass = { .has_prescaler_bypass = false, - .has_rdy = false, .npwm = 2, }; -static const struct sun4i_pwm_data sun4i_pwm_data_a10s = { +static const struct sun4i_pwm_data sun4i_pwm_dual_bypass = { .has_prescaler_bypass = true, - .has_rdy = true, .npwm = 2, }; -static const struct sun4i_pwm_data sun4i_pwm_data_a13 = { +static const struct sun4i_pwm_data sun4i_pwm_single_bypass = { .has_prescaler_bypass = true, - .has_rdy = true, - .npwm = 1, -}; - -static const struct sun4i_pwm_data sun4i_pwm_data_a20 = { - .has_prescaler_bypass = true, - .has_rdy = true, - .npwm = 2, -}; - -static const struct sun4i_pwm_data sun4i_pwm_data_h3 = { - .has_prescaler_bypass = true, - .has_rdy = true, .npwm = 1, }; static const struct of_device_id sun4i_pwm_dt_ids[] = { { .compatible = "allwinner,sun4i-a10-pwm", - .data = &sun4i_pwm_data_a10, + .data = &sun4i_pwm_dual_nobypass, }, { .compatible = "allwinner,sun5i-a10s-pwm", - .data = &sun4i_pwm_data_a10s, + .data = &sun4i_pwm_dual_bypass, }, { .compatible = "allwinner,sun5i-a13-pwm", - .data = &sun4i_pwm_data_a13, + .data = &sun4i_pwm_single_bypass, }, { .compatible = "allwinner,sun7i-a20-pwm", - .data = &sun4i_pwm_data_a20, + .data = &sun4i_pwm_dual_bypass, }, { .compatible = "allwinner,sun8i-h3-pwm", - .data = &sun4i_pwm_data_h3, + .data = &sun4i_pwm_single_bypass, }, { /* sentinel */ }, diff --git a/drivers/pwm/sysfs.c b/drivers/pwm/sysfs.c index 83f2b0b15712..7c71cdb8a9d8 100644 --- a/drivers/pwm/sysfs.c +++ b/drivers/pwm/sysfs.c @@ -273,7 +273,8 @@ static int pwm_export_child(struct device *parent, struct pwm_device *pwm) ret = device_register(&export->child); if (ret) { clear_bit(PWMF_EXPORTED, &pwm->flags); - kfree(export); + put_device(&export->child); + export = NULL; return ret; } |