diff options
Diffstat (limited to 'drivers/pwm')
-rw-r--r-- | drivers/pwm/Kconfig | 9 | ||||
-rw-r--r-- | drivers/pwm/Makefile | 1 | ||||
-rw-r--r-- | drivers/pwm/pwm-stm32.c | 112 | ||||
-rw-r--r-- | drivers/pwm/pwm-sun4i.c | 5 | ||||
-rw-r--r-- | drivers/pwm/pwm-tipwmss.c | 55 |
5 files changed, 84 insertions, 98 deletions
diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index e3a2518503ed..bd21655c37a6 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig @@ -508,15 +508,6 @@ config PWM_TIEHRPWM To compile this driver as a module, choose M here: the module will be called pwm-tiehrpwm. -config PWM_TIPWMSS - bool - default y if (ARCH_OMAP2PLUS) && (PWM_TIECAP || PWM_TIEHRPWM) - help - PWM Subsystem driver support for AM33xx SOC. - - PWM submodules require PWM config space access from submodule - drivers and require common parent driver support. - config PWM_TWL tristate "TWL4030/6030 PWM support" depends on TWL4030_CORE diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile index 26326adf71d7..9a475073dafc 100644 --- a/drivers/pwm/Makefile +++ b/drivers/pwm/Makefile @@ -50,7 +50,6 @@ obj-$(CONFIG_PWM_SUN4I) += pwm-sun4i.o obj-$(CONFIG_PWM_TEGRA) += pwm-tegra.o obj-$(CONFIG_PWM_TIECAP) += pwm-tiecap.o obj-$(CONFIG_PWM_TIEHRPWM) += pwm-tiehrpwm.o -obj-$(CONFIG_PWM_TIPWMSS) += pwm-tipwmss.o obj-$(CONFIG_PWM_TWL) += pwm-twl.o obj-$(CONFIG_PWM_TWL_LED) += pwm-twl-led.o obj-$(CONFIG_PWM_VT8500) += pwm-vt8500.o diff --git a/drivers/pwm/pwm-stm32.c b/drivers/pwm/pwm-stm32.c index 359b08596d9e..7ff48c14fae8 100644 --- a/drivers/pwm/pwm-stm32.c +++ b/drivers/pwm/pwm-stm32.c @@ -12,6 +12,7 @@ #include <linux/mfd/stm32-timers.h> #include <linux/module.h> #include <linux/of.h> +#include <linux/pinctrl/consumer.h> #include <linux/platform_device.h> #include <linux/pwm.h> @@ -19,6 +20,12 @@ #define CCMR_CHANNEL_MASK 0xFF #define MAX_BREAKINPUT 2 +struct stm32_breakinput { + u32 index; + u32 level; + u32 filter; +}; + struct stm32_pwm { struct pwm_chip chip; struct mutex lock; /* protect pwm config/enable */ @@ -26,15 +33,11 @@ struct stm32_pwm { struct regmap *regmap; u32 max_arr; bool have_complementary_output; + struct stm32_breakinput breakinputs[MAX_BREAKINPUT]; + unsigned int num_breakinputs; u32 capture[4] ____cacheline_aligned; /* DMA'able buffer */ }; -struct stm32_breakinput { - u32 index; - u32 level; - u32 filter; -}; - static inline struct stm32_pwm *to_stm32_pwm_dev(struct pwm_chip *chip) { return container_of(chip, struct stm32_pwm, chip); @@ -488,22 +491,19 @@ static const struct pwm_ops stm32pwm_ops = { }; static int stm32_pwm_set_breakinput(struct stm32_pwm *priv, - int index, int level, int filter) + const struct stm32_breakinput *bi) { - u32 bke = (index == 0) ? TIM_BDTR_BKE : TIM_BDTR_BK2E; - int shift = (index == 0) ? TIM_BDTR_BKF_SHIFT : TIM_BDTR_BK2F_SHIFT; - u32 mask = (index == 0) ? TIM_BDTR_BKE | TIM_BDTR_BKP | TIM_BDTR_BKF - : TIM_BDTR_BK2E | TIM_BDTR_BK2P | TIM_BDTR_BK2F; - u32 bdtr = bke; + u32 shift = TIM_BDTR_BKF_SHIFT(bi->index); + u32 bke = TIM_BDTR_BKE(bi->index); + u32 bkp = TIM_BDTR_BKP(bi->index); + u32 bkf = TIM_BDTR_BKF(bi->index); + u32 mask = bkf | bkp | bke; + u32 bdtr; - /* - * The both bits could be set since only one will be wrote - * due to mask value. - */ - if (level) - bdtr |= TIM_BDTR_BKP | TIM_BDTR_BK2P; + bdtr = (bi->filter & TIM_BDTR_BKF_MASK) << shift | bke; - bdtr |= (filter & TIM_BDTR_BKF_MASK) << shift; + if (bi->level) + bdtr |= bkp; regmap_update_bits(priv->regmap, TIM_BDTR, mask, bdtr); @@ -512,11 +512,25 @@ static int stm32_pwm_set_breakinput(struct stm32_pwm *priv, return (bdtr & bke) ? 0 : -EINVAL; } -static int stm32_pwm_apply_breakinputs(struct stm32_pwm *priv, +static int stm32_pwm_apply_breakinputs(struct stm32_pwm *priv) +{ + unsigned int i; + int ret; + + for (i = 0; i < priv->num_breakinputs; i++) { + ret = stm32_pwm_set_breakinput(priv, &priv->breakinputs[i]); + if (ret < 0) + return ret; + } + + return 0; +} + +static int stm32_pwm_probe_breakinputs(struct stm32_pwm *priv, struct device_node *np) { - struct stm32_breakinput breakinput[MAX_BREAKINPUT]; - int nb, ret, i, array_size; + int nb, ret, array_size; + unsigned int i; nb = of_property_count_elems_of_size(np, "st,breakinput", sizeof(struct stm32_breakinput)); @@ -531,20 +545,21 @@ static int stm32_pwm_apply_breakinputs(struct stm32_pwm *priv, if (nb > MAX_BREAKINPUT) return -EINVAL; + priv->num_breakinputs = nb; array_size = nb * sizeof(struct stm32_breakinput) / sizeof(u32); ret = of_property_read_u32_array(np, "st,breakinput", - (u32 *)breakinput, array_size); + (u32 *)priv->breakinputs, array_size); if (ret) return ret; - for (i = 0; i < nb && !ret; i++) { - ret = stm32_pwm_set_breakinput(priv, - breakinput[i].index, - breakinput[i].level, - breakinput[i].filter); + for (i = 0; i < priv->num_breakinputs; i++) { + if (priv->breakinputs[i].index > 1 || + priv->breakinputs[i].level > 1 || + priv->breakinputs[i].filter > 15) + return -EINVAL; } - return ret; + return stm32_pwm_apply_breakinputs(priv); } static void stm32_pwm_detect_complementary(struct stm32_pwm *priv) @@ -614,7 +629,7 @@ static int stm32_pwm_probe(struct platform_device *pdev) if (!priv->regmap || !priv->clk) return -EINVAL; - ret = stm32_pwm_apply_breakinputs(priv, np); + ret = stm32_pwm_probe_breakinputs(priv, np); if (ret) return ret; @@ -647,6 +662,42 @@ static int stm32_pwm_remove(struct platform_device *pdev) return 0; } +static int __maybe_unused stm32_pwm_suspend(struct device *dev) +{ + struct stm32_pwm *priv = dev_get_drvdata(dev); + unsigned int i; + u32 ccer, mask; + + /* Look for active channels */ + ccer = active_channels(priv); + + for (i = 0; i < priv->chip.npwm; i++) { + mask = TIM_CCER_CC1E << (i * 4); + if (ccer & mask) { + dev_err(dev, "PWM %u still in use by consumer %s\n", + i, priv->chip.pwms[i].label); + return -EBUSY; + } + } + + return pinctrl_pm_select_sleep_state(dev); +} + +static int __maybe_unused stm32_pwm_resume(struct device *dev) +{ + struct stm32_pwm *priv = dev_get_drvdata(dev); + int ret; + + ret = pinctrl_pm_select_default_state(dev); + if (ret) + return ret; + + /* restore breakinput registers that may have been lost in low power */ + return stm32_pwm_apply_breakinputs(priv); +} + +static SIMPLE_DEV_PM_OPS(stm32_pwm_pm_ops, stm32_pwm_suspend, stm32_pwm_resume); + static const struct of_device_id stm32_pwm_of_match[] = { { .compatible = "st,stm32-pwm", }, { /* end node */ }, @@ -659,6 +710,7 @@ static struct platform_driver stm32_pwm_driver = { .driver = { .name = "stm32-pwm", .of_match_table = stm32_pwm_of_match, + .pm = &stm32_pwm_pm_ops, }, }; module_platform_driver(stm32_pwm_driver); diff --git a/drivers/pwm/pwm-sun4i.c b/drivers/pwm/pwm-sun4i.c index 6f5840a1a82d..581d23287333 100644 --- a/drivers/pwm/pwm-sun4i.c +++ b/drivers/pwm/pwm-sun4i.c @@ -137,10 +137,10 @@ static void sun4i_pwm_get_state(struct pwm_chip *chip, val = sun4i_pwm_readl(sun4i_pwm, PWM_CH_PRD(pwm->hwpwm)); - tmp = prescaler * NSEC_PER_SEC * PWM_REG_DTY(val); + tmp = (u64)prescaler * NSEC_PER_SEC * PWM_REG_DTY(val); state->duty_cycle = DIV_ROUND_CLOSEST_ULL(tmp, clk_rate); - tmp = prescaler * NSEC_PER_SEC * PWM_REG_PRD(val); + tmp = (u64)prescaler * NSEC_PER_SEC * PWM_REG_PRD(val); state->period = DIV_ROUND_CLOSEST_ULL(tmp, clk_rate); } @@ -156,7 +156,6 @@ static int sun4i_pwm_calculate(struct sun4i_pwm_chip *sun4i_pwm, if (sun4i_pwm->data->has_prescaler_bypass) { /* First, test without any prescaler when available */ prescaler = PWM_PRESCAL_MASK; - pval = 1; /* * When not using any prescaler, the clock period in nanoseconds * is not an integer so round it half up instead of diff --git a/drivers/pwm/pwm-tipwmss.c b/drivers/pwm/pwm-tipwmss.c deleted file mode 100644 index e9c26c94251b..000000000000 --- a/drivers/pwm/pwm-tipwmss.c +++ /dev/null @@ -1,55 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * TI PWM Subsystem driver - * - * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ - */ - -#include <linux/module.h> -#include <linux/platform_device.h> -#include <linux/io.h> -#include <linux/err.h> -#include <linux/pm_runtime.h> -#include <linux/of_device.h> - -static const struct of_device_id pwmss_of_match[] = { - { .compatible = "ti,am33xx-pwmss" }, - {}, -}; -MODULE_DEVICE_TABLE(of, pwmss_of_match); - -static int pwmss_probe(struct platform_device *pdev) -{ - int ret; - struct device_node *node = pdev->dev.of_node; - - pm_runtime_enable(&pdev->dev); - - /* Populate all the child nodes here... */ - ret = of_platform_populate(node, NULL, NULL, &pdev->dev); - if (ret) - dev_err(&pdev->dev, "no child node found\n"); - - return ret; -} - -static int pwmss_remove(struct platform_device *pdev) -{ - pm_runtime_disable(&pdev->dev); - return 0; -} - -static struct platform_driver pwmss_driver = { - .driver = { - .name = "pwmss", - .of_match_table = pwmss_of_match, - }, - .probe = pwmss_probe, - .remove = pwmss_remove, -}; - -module_platform_driver(pwmss_driver); - -MODULE_DESCRIPTION("PWM Subsystem driver"); -MODULE_AUTHOR("Texas Instruments"); -MODULE_LICENSE("GPL"); |