diff options
Diffstat (limited to 'drivers/pwm')
44 files changed, 569 insertions, 365 deletions
diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index 54f8238aac0d..dff5a93f7daa 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only menuconfig PWM bool "Pulse-Width Modulation (PWM) Support" help @@ -210,6 +211,17 @@ config PWM_IMX27 To compile this driver as a module, choose M here: the module will be called pwm-imx27. +config PWM_IMX_TPM + tristate "i.MX TPM PWM support" + depends on ARCH_MXC || COMPILE_TEST + depends on HAVE_CLK && HAS_IOMEM + help + Generic PWM framework driver for i.MX7ULP TPM module, TPM's full + name is Low Power Timer/Pulse Width Modulation Module. + + To compile this driver as a module, choose M here: the module + will be called pwm-imx-tpm. + config PWM_JZ4740 tristate "Ingenic JZ47xx PWM support" depends on MACH_INGENIC @@ -467,10 +479,9 @@ config PWM_TIECAP config PWM_TIEHRPWM tristate "EHRPWM PWM support" - depends on ARCH_OMAP2PLUS || ARCH_DAVINCI_DA8XX + depends on ARCH_OMAP2PLUS || ARCH_DAVINCI_DA8XX || ARCH_K3 help - PWM driver support for the EHRPWM controller found on AM33XX - TI SOC + PWM driver support for the EHRPWM controller found on TI SOCs To compile this driver as a module, choose M here: the module will be called pwm-tiehrpwm. diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile index 448825e892bc..c368599d36c0 100644 --- a/drivers/pwm/Makefile +++ b/drivers/pwm/Makefile @@ -19,6 +19,7 @@ obj-$(CONFIG_PWM_HIBVT) += pwm-hibvt.o obj-$(CONFIG_PWM_IMG) += pwm-img.o obj-$(CONFIG_PWM_IMX1) += pwm-imx1.o obj-$(CONFIG_PWM_IMX27) += pwm-imx27.o +obj-$(CONFIG_PWM_IMX_TPM) += pwm-imx-tpm.o obj-$(CONFIG_PWM_JZ4740) += pwm-jz4740.o obj-$(CONFIG_PWM_LP3943) += pwm-lp3943.o obj-$(CONFIG_PWM_LPC18XX_SCT) += pwm-lpc18xx-sct.o diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c index 3149204567f3..275b5f399a1a 100644 --- a/drivers/pwm/core.c +++ b/drivers/pwm/core.c @@ -1,22 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * Generic pwmlib implementation * * Copyright (C) 2011 Sascha Hauer <s.hauer@pengutronix.de> * Copyright (C) 2011-2012 Avionic Design GmbH - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; see the file COPYING. If not, write to - * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ #include <linux/module.h> @@ -311,10 +298,12 @@ int pwmchip_add_with_polarity(struct pwm_chip *chip, if (IS_ENABLED(CONFIG_OF)) of_pwmchip_add(chip); - pwmchip_sysfs_export(chip); - out: mutex_unlock(&pwm_lock); + + if (!ret) + pwmchip_sysfs_export(chip); + return ret; } EXPORT_SYMBOL_GPL(pwmchip_add_with_polarity); @@ -348,7 +337,7 @@ int pwmchip_remove(struct pwm_chip *chip) unsigned int i; int ret = 0; - pwmchip_sysfs_unexport_children(chip); + pwmchip_sysfs_unexport(chip); mutex_lock(&pwm_lock); @@ -368,8 +357,6 @@ int pwmchip_remove(struct pwm_chip *chip) free_pwms(chip); - pwmchip_sysfs_unexport(chip); - out: mutex_unlock(&pwm_lock); return ret; @@ -877,6 +864,7 @@ void pwm_put(struct pwm_device *pwm) if (pwm->chip->ops->free) pwm->chip->ops->free(pwm->chip, pwm); + pwm_set_chip_data(pwm, NULL); pwm->label = NULL; module_put(pwm->chip->ops->owner); diff --git a/drivers/pwm/pwm-ab8500.c b/drivers/pwm/pwm-ab8500.c index f39399273426..fdf3964db4a6 100644 --- a/drivers/pwm/pwm-ab8500.c +++ b/drivers/pwm/pwm-ab8500.c @@ -1,8 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) ST-Ericsson SA 2010 * * Author: Arun R Murthy <arun.murthy@stericsson.com> - * License terms: GNU General Public License (GPL) version 2 */ #include <linux/err.h> #include <linux/platform_device.h> diff --git a/drivers/pwm/pwm-atmel-hlcdc.c b/drivers/pwm/pwm-atmel-hlcdc.c index 54c6633d9b5d..7186db85b15f 100644 --- a/drivers/pwm/pwm-atmel-hlcdc.c +++ b/drivers/pwm/pwm-atmel-hlcdc.c @@ -1,20 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) 2014 Free Electrons * Copyright (C) 2014 Atmel * * Author: Boris BREZILLON <boris.brezillon@free-electrons.com> - * - * 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. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see <http://www.gnu.org/licenses/>. */ #include <linux/clk.h> diff --git a/drivers/pwm/pwm-atmel-tcb.c b/drivers/pwm/pwm-atmel-tcb.c index 0d0f8376bc35..85c53701958c 100644 --- a/drivers/pwm/pwm-atmel-tcb.c +++ b/drivers/pwm/pwm-atmel-tcb.c @@ -1,8 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) Overkiz SAS 2012 * * Author: Boris BREZILLON <b.brezillon@overkiz.com> - * License terms: GNU General Public License (GPL) version 2 */ #include <linux/module.h> @@ -17,10 +17,10 @@ #include <linux/ioport.h> #include <linux/io.h> #include <linux/platform_device.h> -#include <linux/atmel_tc.h> #include <linux/pwm.h> #include <linux/of_device.h> #include <linux/slab.h> +#include <soc/at91/atmel_tcb.h> #define NPWM 6 diff --git a/drivers/pwm/pwm-atmel.c b/drivers/pwm/pwm-atmel.c index a9fd6f0d408c..e5e1eaf372fa 100644 --- a/drivers/pwm/pwm-atmel.c +++ b/drivers/pwm/pwm-atmel.c @@ -1,10 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Driver for Atmel Pulse Width Modulation Controller * * Copyright (C) 2013 Atmel Corporation * Bo Shen <voice.shen@atmel.com> - * - * Licensed under GPLv2. */ #include <linux/clk.h> diff --git a/drivers/pwm/pwm-berlin.c b/drivers/pwm/pwm-berlin.c index 7c8d6a168ceb..b91c477cc84b 100644 --- a/drivers/pwm/pwm-berlin.c +++ b/drivers/pwm/pwm-berlin.c @@ -84,7 +84,6 @@ static void berlin_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm) { struct berlin_pwm_channel *channel = pwm_get_chip_data(pwm); - pwm_set_chip_data(pwm, NULL); kfree(channel); } diff --git a/drivers/pwm/pwm-brcmstb.c b/drivers/pwm/pwm-brcmstb.c index 8063cffa1c96..fea612c45f20 100644 --- a/drivers/pwm/pwm-brcmstb.c +++ b/drivers/pwm/pwm-brcmstb.c @@ -1,18 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * Broadcom BCM7038 PWM driver * Author: Florian Fainelli * * Copyright (C) 2015 Broadcom Corporation - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt diff --git a/drivers/pwm/pwm-crc.c b/drivers/pwm/pwm-crc.c index bd0ebd04856a..272eeb071147 100644 --- a/drivers/pwm/pwm-crc.c +++ b/drivers/pwm/pwm-crc.c @@ -1,15 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) 2015 Intel Corporation. All rights reserved. * - * 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. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * * Author: Shobhit Kumar <shobhit.kumar@intel.com> */ diff --git a/drivers/pwm/pwm-ep93xx.c b/drivers/pwm/pwm-ep93xx.c index bbf10ae02f0e..4bab73073ad7 100644 --- a/drivers/pwm/pwm-ep93xx.c +++ b/drivers/pwm/pwm-ep93xx.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * PWM framework driver for Cirrus Logic EP93xx * @@ -13,16 +14,6 @@ * EP9312/15 have two channels: * platform device ep93xx-pwm.0 - PWMOUT * platform device ep93xx-pwm.1 - PWMOUT1 (EGPIO14) - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include <linux/module.h> @@ -35,7 +26,7 @@ #include <asm/div64.h> -#include <mach/platform.h> /* for ep93xx_pwm_{acquire,release}_gpio() */ +#include <linux/soc/cirrus/ep93xx.h> /* for ep93xx_pwm_{acquire,release}_gpio() */ #define EP93XX_PWMx_TERM_COUNT 0x00 #define EP93XX_PWMx_DUTY_CYCLE 0x04 diff --git a/drivers/pwm/pwm-fsl-ftm.c b/drivers/pwm/pwm-fsl-ftm.c index 883378d055c6..a39b48839df7 100644 --- a/drivers/pwm/pwm-fsl-ftm.c +++ b/drivers/pwm/pwm-fsl-ftm.c @@ -1,12 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * Freescale FlexTimer Module (FTM) PWM Driver * * Copyright 2012-2013 Freescale Semiconductor, Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. */ #include <linux/clk.h> @@ -22,51 +18,9 @@ #include <linux/pwm.h> #include <linux/regmap.h> #include <linux/slab.h> +#include <linux/fsl/ftm.h> -#define FTM_SC 0x00 -#define FTM_SC_CLK_MASK_SHIFT 3 -#define FTM_SC_CLK_MASK (3 << FTM_SC_CLK_MASK_SHIFT) #define FTM_SC_CLK(c) (((c) + 1) << FTM_SC_CLK_MASK_SHIFT) -#define FTM_SC_PS_MASK 0x7 - -#define FTM_CNT 0x04 -#define FTM_MOD 0x08 - -#define FTM_CSC_BASE 0x0C -#define FTM_CSC_MSB BIT(5) -#define FTM_CSC_MSA BIT(4) -#define FTM_CSC_ELSB BIT(3) -#define FTM_CSC_ELSA BIT(2) -#define FTM_CSC(_channel) (FTM_CSC_BASE + ((_channel) * 8)) - -#define FTM_CV_BASE 0x10 -#define FTM_CV(_channel) (FTM_CV_BASE + ((_channel) * 8)) - -#define FTM_CNTIN 0x4C -#define FTM_STATUS 0x50 - -#define FTM_MODE 0x54 -#define FTM_MODE_FTMEN BIT(0) -#define FTM_MODE_INIT BIT(2) -#define FTM_MODE_PWMSYNC BIT(3) - -#define FTM_SYNC 0x58 -#define FTM_OUTINIT 0x5C -#define FTM_OUTMASK 0x60 -#define FTM_COMBINE 0x64 -#define FTM_DEADTIME 0x68 -#define FTM_EXTTRIG 0x6C -#define FTM_POL 0x70 -#define FTM_FMS 0x74 -#define FTM_FILTER 0x78 -#define FTM_FLTCTRL 0x7C -#define FTM_QDCTRL 0x80 -#define FTM_CONF 0x84 -#define FTM_FLTPOL 0x88 -#define FTM_SYNCONF 0x8C -#define FTM_INVCTRL 0x90 -#define FTM_SWOCTRL 0x94 -#define FTM_PWMLOAD 0x98 enum fsl_pwm_clk { FSL_PWM_CLK_SYS, diff --git a/drivers/pwm/pwm-hibvt.c b/drivers/pwm/pwm-hibvt.c index a0b09603d13d..753bd58111e4 100644 --- a/drivers/pwm/pwm-hibvt.c +++ b/drivers/pwm/pwm-hibvt.c @@ -1,20 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * PWM Controller Driver for HiSilicon BVT SoCs * * Copyright (c) 2016 HiSilicon Technologies Co., Ltd. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. */ #include <linux/bitops.h> diff --git a/drivers/pwm/pwm-img.c b/drivers/pwm/pwm-img.c index 815f5333bb8f..c9e57bd109fb 100644 --- a/drivers/pwm/pwm-img.c +++ b/drivers/pwm/pwm-img.c @@ -1,13 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Imagination Technologies Pulse Width Modulator driver * * Copyright (c) 2014-2015, Imagination Technologies * * Based on drivers/pwm/pwm-tegra.c, Copyright (c) 2010, NVIDIA Corporation - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License. */ #include <linux/clk.h> @@ -123,7 +120,7 @@ static int img_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, } else if (mul <= max_timebase * 512) { div = PWM_CTRL_CFG_SUB_DIV0_DIV1; timebase = DIV_ROUND_UP(mul, 512); - } else if (mul > max_timebase * 512) { + } else { dev_err(chip->dev, "failed to configure timebase steps/divider value\n"); return -EINVAL; diff --git a/drivers/pwm/pwm-imx-tpm.c b/drivers/pwm/pwm-imx-tpm.c new file mode 100644 index 000000000000..e8385c1cf342 --- /dev/null +++ b/drivers/pwm/pwm-imx-tpm.c @@ -0,0 +1,449 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2018-2019 NXP. + * + * Limitations: + * - The TPM counter and period counter are shared between + * multiple channels, so all channels should use same period + * settings. + * - Changes to polarity cannot be latched at the time of the + * next period start. + * - Changing period and duty cycle together isn't atomic, + * with the wrong timing it might happen that a period is + * produced with old duty cycle but new period settings. + */ + +#include <linux/bitfield.h> +#include <linux/bitops.h> +#include <linux/clk.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/log2.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/platform_device.h> +#include <linux/pwm.h> +#include <linux/slab.h> + +#define PWM_IMX_TPM_PARAM 0x4 +#define PWM_IMX_TPM_GLOBAL 0x8 +#define PWM_IMX_TPM_SC 0x10 +#define PWM_IMX_TPM_CNT 0x14 +#define PWM_IMX_TPM_MOD 0x18 +#define PWM_IMX_TPM_CnSC(n) (0x20 + (n) * 0x8) +#define PWM_IMX_TPM_CnV(n) (0x24 + (n) * 0x8) + +#define PWM_IMX_TPM_PARAM_CHAN GENMASK(7, 0) + +#define PWM_IMX_TPM_SC_PS GENMASK(2, 0) +#define PWM_IMX_TPM_SC_CMOD GENMASK(4, 3) +#define PWM_IMX_TPM_SC_CMOD_INC_EVERY_CLK FIELD_PREP(PWM_IMX_TPM_SC_CMOD, 1) +#define PWM_IMX_TPM_SC_CPWMS BIT(5) + +#define PWM_IMX_TPM_CnSC_CHF BIT(7) +#define PWM_IMX_TPM_CnSC_MSB BIT(5) +#define PWM_IMX_TPM_CnSC_MSA BIT(4) + +/* + * The reference manual describes this field as two separate bits. The + * semantic of the two bits isn't orthogonal though, so they are treated + * together as a 2-bit field here. + */ +#define PWM_IMX_TPM_CnSC_ELS GENMASK(3, 2) +#define PWM_IMX_TPM_CnSC_ELS_INVERSED FIELD_PREP(PWM_IMX_TPM_CnSC_ELS, 1) +#define PWM_IMX_TPM_CnSC_ELS_NORMAL FIELD_PREP(PWM_IMX_TPM_CnSC_ELS, 2) + + +#define PWM_IMX_TPM_MOD_WIDTH 16 +#define PWM_IMX_TPM_MOD_MOD GENMASK(PWM_IMX_TPM_MOD_WIDTH - 1, 0) + +struct imx_tpm_pwm_chip { + struct pwm_chip chip; + struct clk *clk; + void __iomem *base; + struct mutex lock; + u32 user_count; + u32 enable_count; + u32 real_period; +}; + +struct imx_tpm_pwm_param { + u8 prescale; + u32 mod; + u32 val; +}; + +static inline struct imx_tpm_pwm_chip * +to_imx_tpm_pwm_chip(struct pwm_chip *chip) +{ + return container_of(chip, struct imx_tpm_pwm_chip, chip); +} + +/* + * This function determines for a given pwm_state *state that a consumer + * might request the pwm_state *real_state that eventually is implemented + * by the hardware and the necessary register values (in *p) to achieve + * this. + */ +static int pwm_imx_tpm_round_state(struct pwm_chip *chip, + struct imx_tpm_pwm_param *p, + struct pwm_state *real_state, + struct pwm_state *state) +{ + struct imx_tpm_pwm_chip *tpm = to_imx_tpm_pwm_chip(chip); + u32 rate, prescale, period_count, clock_unit; + u64 tmp; + + rate = clk_get_rate(tpm->clk); + tmp = (u64)state->period * rate; + clock_unit = DIV_ROUND_CLOSEST_ULL(tmp, NSEC_PER_SEC); + if (clock_unit <= PWM_IMX_TPM_MOD_MOD) + prescale = 0; + else + prescale = ilog2(clock_unit) + 1 - PWM_IMX_TPM_MOD_WIDTH; + + if ((!FIELD_FIT(PWM_IMX_TPM_SC_PS, prescale))) + return -ERANGE; + p->prescale = prescale; + + period_count = (clock_unit + ((1 << prescale) >> 1)) >> prescale; + p->mod = period_count; + + /* calculate real period HW can support */ + tmp = (u64)period_count << prescale; + tmp *= NSEC_PER_SEC; + real_state->period = DIV_ROUND_CLOSEST_ULL(tmp, rate); + + /* + * if eventually the PWM output is inactive, either + * duty cycle is 0 or status is disabled, need to + * make sure the output pin is inactive. + */ + if (!state->enabled) + real_state->duty_cycle = 0; + else + real_state->duty_cycle = state->duty_cycle; + + tmp = (u64)p->mod * real_state->duty_cycle; + p->val = DIV_ROUND_CLOSEST_ULL(tmp, real_state->period); + + real_state->polarity = state->polarity; + real_state->enabled = state->enabled; + + return 0; +} + +static void pwm_imx_tpm_get_state(struct pwm_chip *chip, + struct pwm_device *pwm, + struct pwm_state *state) +{ + struct imx_tpm_pwm_chip *tpm = to_imx_tpm_pwm_chip(chip); + u32 rate, val, prescale; + u64 tmp; + + /* get period */ + state->period = tpm->real_period; + + /* get duty cycle */ + rate = clk_get_rate(tpm->clk); + val = readl(tpm->base + PWM_IMX_TPM_SC); + prescale = FIELD_GET(PWM_IMX_TPM_SC_PS, val); + tmp = readl(tpm->base + PWM_IMX_TPM_CnV(pwm->hwpwm)); + tmp = (tmp << prescale) * NSEC_PER_SEC; + state->duty_cycle = DIV_ROUND_CLOSEST_ULL(tmp, rate); + + /* get polarity */ + val = readl(tpm->base + PWM_IMX_TPM_CnSC(pwm->hwpwm)); + if ((val & PWM_IMX_TPM_CnSC_ELS) == PWM_IMX_TPM_CnSC_ELS_INVERSED) + state->polarity = PWM_POLARITY_INVERSED; + else + /* + * Assume reserved values (2b00 and 2b11) to yield + * normal polarity. + */ + state->polarity = PWM_POLARITY_NORMAL; + + /* get channel status */ + state->enabled = FIELD_GET(PWM_IMX_TPM_CnSC_ELS, val) ? true : false; +} + +/* this function is supposed to be called with mutex hold */ +static int pwm_imx_tpm_apply_hw(struct pwm_chip *chip, + struct imx_tpm_pwm_param *p, + struct pwm_state *state, + struct pwm_device *pwm) +{ + struct imx_tpm_pwm_chip *tpm = to_imx_tpm_pwm_chip(chip); + bool period_update = false; + bool duty_update = false; + u32 val, cmod, cur_prescale; + unsigned long timeout; + struct pwm_state c; + + if (state->period != tpm->real_period) { + /* + * TPM counter is shared by multiple channels, so + * prescale and period can NOT be modified when + * there are multiple channels in use with different + * period settings. + */ + if (tpm->user_count > 1) + return -EBUSY; + + val = readl(tpm->base + PWM_IMX_TPM_SC); + cmod = FIELD_GET(PWM_IMX_TPM_SC_CMOD, val); + cur_prescale = FIELD_GET(PWM_IMX_TPM_SC_PS, val); + if (cmod && cur_prescale != p->prescale) + return -EBUSY; + + /* set TPM counter prescale */ + val &= ~PWM_IMX_TPM_SC_PS; + val |= FIELD_PREP(PWM_IMX_TPM_SC_PS, p->prescale); + writel(val, tpm->base + PWM_IMX_TPM_SC); + + /* + * set period count: + * if the PWM is disabled (CMOD[1:0] = 2b00), then MOD register + * is updated when MOD register is written. + * + * if the PWM is enabled (CMOD[1:0] ≠ 2b00), the period length + * is latched into hardware when the next period starts. + */ + writel(p->mod, tpm->base + PWM_IMX_TPM_MOD); + tpm->real_period = state->period; + period_update = true; + } + + pwm_imx_tpm_get_state(chip, pwm, &c); + + /* polarity is NOT allowed to be changed if PWM is active */ + if (c.enabled && c.polarity != state->polarity) + return -EBUSY; + + if (state->duty_cycle != c.duty_cycle) { + /* + * set channel value: + * if the PWM is disabled (CMOD[1:0] = 2b00), then CnV register + * is updated when CnV register is written. + * + * if the PWM is enabled (CMOD[1:0] ≠ 2b00), the duty length + * is latched into hardware when the next period starts. + */ + writel(p->val, tpm->base + PWM_IMX_TPM_CnV(pwm->hwpwm)); + duty_update = true; + } + + /* make sure MOD & CnV registers are updated */ + if (period_update || duty_update) { + timeout = jiffies + msecs_to_jiffies(tpm->real_period / + NSEC_PER_MSEC + 1); + while (readl(tpm->base + PWM_IMX_TPM_MOD) != p->mod + || readl(tpm->base + PWM_IMX_TPM_CnV(pwm->hwpwm)) + != p->val) { + if (time_after(jiffies, timeout)) + return -ETIME; + cpu_relax(); + } + } + + /* + * polarity settings will enabled/disable output status + * immediately, so if the channel is disabled, need to + * make sure MSA/MSB/ELS are set to 0 which means channel + * disabled. + */ + val = readl(tpm->base + PWM_IMX_TPM_CnSC(pwm->hwpwm)); + val &= ~(PWM_IMX_TPM_CnSC_ELS | PWM_IMX_TPM_CnSC_MSA | + PWM_IMX_TPM_CnSC_MSB); + if (state->enabled) { + /* + * set polarity (for edge-aligned PWM modes) + * + * ELS[1:0] = 2b10 yields normal polarity behaviour, + * ELS[1:0] = 2b01 yields inversed polarity. + * The other values are reserved. + */ + val |= PWM_IMX_TPM_CnSC_MSB; + val |= (state->polarity == PWM_POLARITY_NORMAL) ? + PWM_IMX_TPM_CnSC_ELS_NORMAL : + PWM_IMX_TPM_CnSC_ELS_INVERSED; + } + writel(val, tpm->base + PWM_IMX_TPM_CnSC(pwm->hwpwm)); + + /* control the counter status */ + if (state->enabled != c.enabled) { + val = readl(tpm->base + PWM_IMX_TPM_SC); + if (state->enabled) { + if (++tpm->enable_count == 1) + val |= PWM_IMX_TPM_SC_CMOD_INC_EVERY_CLK; + } else { + if (--tpm->enable_count == 0) + val &= ~PWM_IMX_TPM_SC_CMOD; + } + writel(val, tpm->base + PWM_IMX_TPM_SC); + } + + return 0; +} + +static int pwm_imx_tpm_apply(struct pwm_chip *chip, + struct pwm_device *pwm, + struct pwm_state *state) +{ + struct imx_tpm_pwm_chip *tpm = to_imx_tpm_pwm_chip(chip); + struct imx_tpm_pwm_param param; + struct pwm_state real_state; + int ret; + + ret = pwm_imx_tpm_round_state(chip, ¶m, &real_state, state); + if (ret) + return ret; + + mutex_lock(&tpm->lock); + ret = pwm_imx_tpm_apply_hw(chip, ¶m, &real_state, pwm); + mutex_unlock(&tpm->lock); + + return ret; +} + +static int pwm_imx_tpm_request(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct imx_tpm_pwm_chip *tpm = to_imx_tpm_pwm_chip(chip); + + mutex_lock(&tpm->lock); + tpm->user_count++; + mutex_unlock(&tpm->lock); + + return 0; +} + +static void pwm_imx_tpm_free(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct imx_tpm_pwm_chip *tpm = to_imx_tpm_pwm_chip(chip); + + mutex_lock(&tpm->lock); + tpm->user_count--; + mutex_unlock(&tpm->lock); +} + +static const struct pwm_ops imx_tpm_pwm_ops = { + .request = pwm_imx_tpm_request, + .free = pwm_imx_tpm_free, + .get_state = pwm_imx_tpm_get_state, + .apply = pwm_imx_tpm_apply, + .owner = THIS_MODULE, +}; + +static int pwm_imx_tpm_probe(struct platform_device *pdev) +{ + struct imx_tpm_pwm_chip *tpm; + int ret; + u32 val; + + tpm = devm_kzalloc(&pdev->dev, sizeof(*tpm), GFP_KERNEL); + if (!tpm) + return -ENOMEM; + + platform_set_drvdata(pdev, tpm); + + tpm->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(tpm->base)) + return PTR_ERR(tpm->base); + + tpm->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(tpm->clk)) { + ret = PTR_ERR(tpm->clk); + if (ret != -EPROBE_DEFER) + dev_err(&pdev->dev, + "failed to get PWM clock: %d\n", ret); + return ret; + } + + ret = clk_prepare_enable(tpm->clk); + if (ret) { + dev_err(&pdev->dev, + "failed to prepare or enable clock: %d\n", ret); + return ret; + } + + tpm->chip.dev = &pdev->dev; + tpm->chip.ops = &imx_tpm_pwm_ops; + tpm->chip.base = -1; + tpm->chip.of_xlate = of_pwm_xlate_with_flags; + tpm->chip.of_pwm_n_cells = 3; + + /* get number of channels */ + val = readl(tpm->base + PWM_IMX_TPM_PARAM); + tpm->chip.npwm = FIELD_GET(PWM_IMX_TPM_PARAM_CHAN, val); + + mutex_init(&tpm->lock); + + ret = pwmchip_add(&tpm->chip); + if (ret) { + dev_err(&pdev->dev, "failed to add PWM chip: %d\n", ret); + clk_disable_unprepare(tpm->clk); + } + + return ret; +} + +static int pwm_imx_tpm_remove(struct platform_device *pdev) +{ + struct imx_tpm_pwm_chip *tpm = platform_get_drvdata(pdev); + int ret = pwmchip_remove(&tpm->chip); + + clk_disable_unprepare(tpm->clk); + + return ret; +} + +static int __maybe_unused pwm_imx_tpm_suspend(struct device *dev) +{ + struct imx_tpm_pwm_chip *tpm = dev_get_drvdata(dev); + + if (tpm->enable_count > 0) + return -EBUSY; + + clk_disable_unprepare(tpm->clk); + + return 0; +} + +static int __maybe_unused pwm_imx_tpm_resume(struct device *dev) +{ + struct imx_tpm_pwm_chip *tpm = dev_get_drvdata(dev); + int ret = 0; + + ret = clk_prepare_enable(tpm->clk); + if (ret) + dev_err(dev, + "failed to prepare or enable clock: %d\n", + ret); + + return ret; +} + +static SIMPLE_DEV_PM_OPS(imx_tpm_pwm_pm, + pwm_imx_tpm_suspend, pwm_imx_tpm_resume); + +static const struct of_device_id imx_tpm_pwm_dt_ids[] = { + { .compatible = "fsl,imx7ulp-pwm", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, imx_tpm_pwm_dt_ids); + +static struct platform_driver imx_tpm_pwm_driver = { + .driver = { + .name = "imx7ulp-tpm-pwm", + .of_match_table = imx_tpm_pwm_dt_ids, + .pm = &imx_tpm_pwm_pm, + }, + .probe = pwm_imx_tpm_probe, + .remove = pwm_imx_tpm_remove, +}; +module_platform_driver(imx_tpm_pwm_driver); + +MODULE_AUTHOR("Anson Huang <Anson.Huang@nxp.com>"); +MODULE_DESCRIPTION("i.MX TPM PWM Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/pwm/pwm-imx27.c b/drivers/pwm/pwm-imx27.c index 806130654211..434a351fb626 100644 --- a/drivers/pwm/pwm-imx27.c +++ b/drivers/pwm/pwm-imx27.c @@ -291,7 +291,6 @@ MODULE_DEVICE_TABLE(of, pwm_imx27_dt_ids); static int pwm_imx27_probe(struct platform_device *pdev) { struct pwm_imx27_chip *imx; - struct resource *r; imx = devm_kzalloc(&pdev->dev, sizeof(*imx), GFP_KERNEL); if (imx == NULL) @@ -326,8 +325,7 @@ static int pwm_imx27_probe(struct platform_device *pdev) imx->chip.of_xlate = of_pwm_xlate_with_flags; imx->chip.of_pwm_n_cells = 3; - r = platform_get_resource(pdev, IORESOURCE_MEM, 0); - imx->mmio_base = devm_ioremap_resource(&pdev->dev, r); + imx->mmio_base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(imx->mmio_base)) return PTR_ERR(imx->mmio_base); diff --git a/drivers/pwm/pwm-jz4740.c b/drivers/pwm/pwm-jz4740.c index a7b134af5e04..88a51a40e695 100644 --- a/drivers/pwm/pwm-jz4740.c +++ b/drivers/pwm/pwm-jz4740.c @@ -1,16 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (C) 2010, Lars-Peter Clausen <lars@metafoo.de> * JZ4740 platform PWM support - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 675 Mass Ave, Cambridge, MA 02139, USA. - * */ #include <linux/clk.h> diff --git a/drivers/pwm/pwm-lp3943.c b/drivers/pwm/pwm-lp3943.c index 15b40a8bc4fb..7551253ada32 100644 --- a/drivers/pwm/pwm-lp3943.c +++ b/drivers/pwm/pwm-lp3943.c @@ -1,13 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * TI/National Semiconductor LP3943 PWM driver * * Copyright 2013 Texas Instruments * * Author: Milo Kim <milo.kim@ti.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2. */ #include <linux/err.h> diff --git a/drivers/pwm/pwm-lpc18xx-sct.c b/drivers/pwm/pwm-lpc18xx-sct.c index 475918d9f543..5ff11145c1a3 100644 --- a/drivers/pwm/pwm-lpc18xx-sct.c +++ b/drivers/pwm/pwm-lpc18xx-sct.c @@ -1,12 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * NXP LPC18xx State Configurable Timer - Pulse Width Modulator driver * * Copyright (c) 2015 Ariel D'Alessandro <ariel@vanguardiasur.com> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License. - * * Notes * ===== * NXP LPC18xx provides a State Configurable Timer (SCT) which can be configured diff --git a/drivers/pwm/pwm-lpc32xx.c b/drivers/pwm/pwm-lpc32xx.c index a9b3cff96aac..710d9a207d2b 100644 --- a/drivers/pwm/pwm-lpc32xx.c +++ b/drivers/pwm/pwm-lpc32xx.c @@ -1,10 +1,6 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright 2012 Alexandre Pereira da Silva <aletes.xgr@gmail.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2. - * */ #include <linux/clk.h> diff --git a/drivers/pwm/pwm-lpss-pci.c b/drivers/pwm/pwm-lpss-pci.c index c1527cb645be..cf749ea0de9f 100644 --- a/drivers/pwm/pwm-lpss-pci.c +++ b/drivers/pwm/pwm-lpss-pci.c @@ -1,13 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Intel Low Power Subsystem PWM controller PCI driver * * Copyright (C) 2014, Intel Corporation * * Derived from the original pwm-lpss.c - * - * 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. */ #include <linux/kernel.h> diff --git a/drivers/pwm/pwm-lpss-platform.c b/drivers/pwm/pwm-lpss-platform.c index 757230e1f575..48f34d20aecd 100644 --- a/drivers/pwm/pwm-lpss-platform.c +++ b/drivers/pwm/pwm-lpss-platform.c @@ -1,13 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Intel Low Power Subsystem PWM controller driver * * Copyright (C) 2014, Intel Corporation * * Derived from the original pwm-lpss.c - * - * 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. */ #include <linux/acpi.h> diff --git a/drivers/pwm/pwm-lpss.c b/drivers/pwm/pwm-lpss.c index 2ac3a2aa9e53..4098a4601691 100644 --- a/drivers/pwm/pwm-lpss.c +++ b/drivers/pwm/pwm-lpss.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Intel Low Power Subsystem PWM controller driver * @@ -7,10 +8,6 @@ * Author: Chang Rebecca Swee Fun <rebecca.swee.fun.chang@intel.com> * Author: Chew Chiau Ee <chiau.ee.chew@intel.com> * Author: Alan Cox <alan@linux.intel.com> - * - * 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. */ #include <linux/delay.h> diff --git a/drivers/pwm/pwm-lpss.h b/drivers/pwm/pwm-lpss.h index 3236be835bd9..7909fa12fca2 100644 --- a/drivers/pwm/pwm-lpss.h +++ b/drivers/pwm/pwm-lpss.h @@ -1,13 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * Intel Low Power Subsystem PWM controller driver * * Copyright (C) 2014, Intel Corporation * * Derived from the original pwm-lpss.c - * - * 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. */ #ifndef __PWM_LPSS_H diff --git a/drivers/pwm/pwm-meson.c b/drivers/pwm/pwm-meson.c index c1ed641b3e26..fb5a369b1a8d 100644 --- a/drivers/pwm/pwm-meson.c +++ b/drivers/pwm/pwm-meson.c @@ -111,6 +111,10 @@ struct meson_pwm { const struct meson_pwm_data *data; void __iomem *base; u8 inverter_mask; + /* + * Protects register (write) access to the REG_MISC_AB register + * that is shared between the two PWMs. + */ spinlock_t lock; }; @@ -184,7 +188,7 @@ static int meson_pwm_calc(struct meson_pwm *meson, do_div(fin_ps, fin_freq); /* Calc pre_div with the period */ - for (pre_div = 0; pre_div < MISC_CLK_DIV_MASK; pre_div++) { + for (pre_div = 0; pre_div <= MISC_CLK_DIV_MASK; pre_div++) { cnt = DIV_ROUND_CLOSEST_ULL((u64)period * 1000, fin_ps * (pre_div + 1)); dev_dbg(meson->chip.dev, "fin_ps=%llu pre_div=%u cnt=%u\n", @@ -193,7 +197,7 @@ static int meson_pwm_calc(struct meson_pwm *meson, break; } - if (pre_div == MISC_CLK_DIV_MASK) { + if (pre_div > MISC_CLK_DIV_MASK) { dev_err(meson->chip.dev, "unable to get period pre_div\n"); return -EINVAL; } @@ -235,6 +239,7 @@ static void meson_pwm_enable(struct meson_pwm *meson, { u32 value, clk_shift, clk_enable, enable; unsigned int offset; + unsigned long flags; switch (id) { case 0: @@ -255,6 +260,8 @@ static void meson_pwm_enable(struct meson_pwm *meson, return; } + spin_lock_irqsave(&meson->lock, flags); + value = readl(meson->base + REG_MISC_AB); value &= ~(MISC_CLK_DIV_MASK << clk_shift); value |= channel->pre_div << clk_shift; @@ -267,11 +274,14 @@ static void meson_pwm_enable(struct meson_pwm *meson, value = readl(meson->base + REG_MISC_AB); value |= enable; writel(value, meson->base + REG_MISC_AB); + + spin_unlock_irqrestore(&meson->lock, flags); } static void meson_pwm_disable(struct meson_pwm *meson, unsigned int id) { u32 value, enable; + unsigned long flags; switch (id) { case 0: @@ -286,9 +296,13 @@ static void meson_pwm_disable(struct meson_pwm *meson, unsigned int id) return; } + spin_lock_irqsave(&meson->lock, flags); + value = readl(meson->base + REG_MISC_AB); value &= ~enable; writel(value, meson->base + REG_MISC_AB); + + spin_unlock_irqrestore(&meson->lock, flags); } static int meson_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, @@ -296,29 +310,21 @@ static int meson_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, { struct meson_pwm_channel *channel = pwm_get_chip_data(pwm); struct meson_pwm *meson = to_meson_pwm(chip); - unsigned long flags; int err = 0; if (!state) return -EINVAL; - spin_lock_irqsave(&meson->lock, flags); - if (!state->enabled) { meson_pwm_disable(meson, pwm->hwpwm); channel->state.enabled = false; - goto unlock; + return 0; } if (state->period != channel->state.period || state->duty_cycle != channel->state.duty_cycle || state->polarity != channel->state.polarity) { - if (channel->state.enabled) { - meson_pwm_disable(meson, pwm->hwpwm); - channel->state.enabled = false; - } - if (state->polarity != channel->state.polarity) { if (state->polarity == PWM_POLARITY_NORMAL) meson->inverter_mask |= BIT(pwm->hwpwm); @@ -329,7 +335,7 @@ static int meson_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, err = meson_pwm_calc(meson, channel, pwm->hwpwm, state->duty_cycle, state->period); if (err < 0) - goto unlock; + return err; channel->state.polarity = state->polarity; channel->state.period = state->period; @@ -341,9 +347,7 @@ static int meson_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, channel->state.enabled = true; } -unlock: - spin_unlock_irqrestore(&meson->lock, flags); - return err; + return 0; } static void meson_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm, @@ -429,6 +433,24 @@ static const struct meson_pwm_data pwm_axg_ao_data = { .num_parents = ARRAY_SIZE(pwm_axg_ao_parent_names), }; +static const char * const pwm_g12a_ao_cd_parent_names[] = { + "aoclk81", "xtal", +}; + +static const struct meson_pwm_data pwm_g12a_ao_cd_data = { + .parent_names = pwm_g12a_ao_cd_parent_names, + .num_parents = ARRAY_SIZE(pwm_g12a_ao_cd_parent_names), +}; + +static const char * const pwm_g12a_ee_parent_names[] = { + "xtal", "hdmi_pll", "fclk_div4", "fclk_div3" +}; + +static const struct meson_pwm_data pwm_g12a_ee_data = { + .parent_names = pwm_g12a_ee_parent_names, + .num_parents = ARRAY_SIZE(pwm_g12a_ee_parent_names), +}; + static const struct of_device_id meson_pwm_matches[] = { { .compatible = "amlogic,meson8b-pwm", @@ -450,6 +472,18 @@ static const struct of_device_id meson_pwm_matches[] = { .compatible = "amlogic,meson-axg-ao-pwm", .data = &pwm_axg_ao_data }, + { + .compatible = "amlogic,meson-g12a-ee-pwm", + .data = &pwm_g12a_ee_data + }, + { + .compatible = "amlogic,meson-g12a-ao-pwm-ab", + .data = &pwm_axg_ao_data + }, + { + .compatible = "amlogic,meson-g12a-ao-pwm-cd", + .data = &pwm_g12a_ao_cd_data + }, {}, }; MODULE_DEVICE_TABLE(of, meson_pwm_matches); @@ -470,7 +504,7 @@ static int meson_pwm_init_channels(struct meson_pwm *meson, init.name = name; init.ops = &clk_mux_ops; - init.flags = CLK_IS_BASIC; + init.flags = 0; init.parent_names = meson->data->parent_names; init.num_parents = meson->data->num_parents; diff --git a/drivers/pwm/pwm-mtk-disp.c b/drivers/pwm/pwm-mtk-disp.c index 15803c71fe80..83b8be0209b7 100644 --- a/drivers/pwm/pwm-mtk-disp.c +++ b/drivers/pwm/pwm-mtk-disp.c @@ -1,16 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * MediaTek display pulse-width-modulation controller driver. * Copyright (c) 2015 MediaTek Inc. * Author: YH Huang <yh.huang@mediatek.com> - * - * 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. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include <linux/clk.h> diff --git a/drivers/pwm/pwm-omap-dmtimer.c b/drivers/pwm/pwm-omap-dmtimer.c index f45798679e3c..00772fc53490 100644 --- a/drivers/pwm/pwm-omap-dmtimer.c +++ b/drivers/pwm/pwm-omap-dmtimer.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2015 Neil Armstrong <narmstrong@baylibre.com> * Copyright (c) 2014 Joachim Eastwood <manabian@gmail.com> @@ -7,10 +8,6 @@ * * Also based on pwm-samsung.c * - * 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. - * * Description: * This file is the core OMAP support for the generic, Linux * PWM driver / controller, using the OMAP's dual-mode timers. diff --git a/drivers/pwm/pwm-pca9685.c b/drivers/pwm/pwm-pca9685.c index a7eaf962a95b..168684b02ebc 100644 --- a/drivers/pwm/pwm-pca9685.c +++ b/drivers/pwm/pwm-pca9685.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Driver for PCA9685 16-channel 12-bit PWM LED controller * @@ -5,18 +6,6 @@ * Copyright (C) 2015 Clemens Gruber <clemens.gruber@pqgruber.com> * * based on the pwm-twl-led.c driver - * - * 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. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see <http://www.gnu.org/licenses/>. */ #include <linux/acpi.h> @@ -176,7 +165,6 @@ static void pca9685_pwm_gpio_free(struct gpio_chip *gpio, unsigned int offset) pm_runtime_put(pca->chip.dev); mutex_lock(&pca->lock); pwm = &pca->chip.pwms[offset]; - pwm_set_chip_data(pwm, NULL); mutex_unlock(&pca->lock); } diff --git a/drivers/pwm/pwm-puv3.c b/drivers/pwm/pwm-puv3.c index 754fd9a98f6b..9d0bd87a425e 100644 --- a/drivers/pwm/pwm-puv3.c +++ b/drivers/pwm/pwm-puv3.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * linux/arch/unicore32/kernel/pwm.c * @@ -5,10 +6,6 @@ * * Maintained by GUAN Xue-tao <gxt@mprc.pku.edu.cn> * Copyright (C) 2001-2010 Guan Xuetao - * - * 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. */ #include <linux/module.h> diff --git a/drivers/pwm/pwm-pxa.c b/drivers/pwm/pwm-pxa.c index 4143a46684d2..a2a0912c2dcd 100644 --- a/drivers/pwm/pwm-pxa.c +++ b/drivers/pwm/pwm-pxa.c @@ -1,12 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * drivers/pwm/pwm-pxa.c * * 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. - * * 2008-02-13 initial version * eric miao <eric.miao@marvell.com> */ diff --git a/drivers/pwm/pwm-rockchip.c b/drivers/pwm/pwm-rockchip.c index 4d99d468df09..51b96cb7dd25 100644 --- a/drivers/pwm/pwm-rockchip.c +++ b/drivers/pwm/pwm-rockchip.c @@ -1,12 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * PWM driver for Rockchip SoCs * * Copyright (C) 2014 Beniamino Galvani <b.galvani@gmail.com> * Copyright (C) 2014 ROCKCHIP, Inc. - * - * 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. */ #include <linux/clk.h> diff --git a/drivers/pwm/pwm-samsung.c b/drivers/pwm/pwm-samsung.c index 062f2cfc45ec..87a886f7dc2f 100644 --- a/drivers/pwm/pwm-samsung.c +++ b/drivers/pwm/pwm-samsung.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2007 Ben Dooks * Copyright (c) 2008 Simtec Electronics @@ -6,10 +7,6 @@ * Copyright (c) 2017 Samsung Electronics Co., Ltd. * * PWM driver for Samsung SoCs - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License. */ #include <linux/bitops.h> @@ -226,7 +223,7 @@ static int pwm_samsung_request(struct pwm_chip *chip, struct pwm_device *pwm) return -EINVAL; } - our_chan = devm_kzalloc(chip->dev, sizeof(*our_chan), GFP_KERNEL); + our_chan = kzalloc(sizeof(*our_chan), GFP_KERNEL); if (!our_chan) return -ENOMEM; @@ -237,8 +234,7 @@ static int pwm_samsung_request(struct pwm_chip *chip, struct pwm_device *pwm) static void pwm_samsung_free(struct pwm_chip *chip, struct pwm_device *pwm) { - devm_kfree(chip->dev, pwm_get_chip_data(pwm)); - pwm_set_chip_data(pwm, NULL); + kfree(pwm_get_chip_data(pwm)); } static int pwm_samsung_enable(struct pwm_chip *chip, struct pwm_device *pwm) diff --git a/drivers/pwm/pwm-sti.c b/drivers/pwm/pwm-sti.c index 2b7c31c9d1ab..20450e34ad57 100644 --- a/drivers/pwm/pwm-sti.c +++ b/drivers/pwm/pwm-sti.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * PWM device driver for ST SoCs * @@ -5,11 +6,6 @@ * * Author: Ajit Pal Singh <ajitpal.singh@st.com> * Lee Jones <lee.jones@linaro.org> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. */ #include <linux/clk.h> diff --git a/drivers/pwm/pwm-stmpe.c b/drivers/pwm/pwm-stmpe.c index 3439f1e902cb..be5f6d7359d4 100644 --- a/drivers/pwm/pwm-stmpe.c +++ b/drivers/pwm/pwm-stmpe.c @@ -1,12 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) 2016 Linaro Ltd. * * Author: Linus Walleij <linus.walleij@linaro.org> - * - * 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. - * */ #include <linux/bitops.h> diff --git a/drivers/pwm/pwm-sun4i.c b/drivers/pwm/pwm-sun4i.c index 470d4f71e7eb..de78c824bbfd 100644 --- a/drivers/pwm/pwm-sun4i.c +++ b/drivers/pwm/pwm-sun4i.c @@ -1,9 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Driver for Allwinner sun4i Pulse Width Modulation Controller * * Copyright (C) 2014 Alexandre Belloni <alexandre.belloni@free-electrons.com> - * - * Licensed under GPLv2. */ #include <linux/bitops.h> diff --git a/drivers/pwm/pwm-tegra.c b/drivers/pwm/pwm-tegra.c index 48c4595a0ffc..aa12fb3ed92e 100644 --- a/drivers/pwm/pwm-tegra.c +++ b/drivers/pwm/pwm-tegra.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * drivers/pwm/pwm-tegra.c * @@ -5,20 +6,6 @@ * * Copyright (c) 2010, NVIDIA Corporation. * Based on arch/arm/plat-mxc/pwm.c by Sascha Hauer <s.hauer@pengutronix.de> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include <linux/clk.h> diff --git a/drivers/pwm/pwm-tiecap.c b/drivers/pwm/pwm-tiecap.c index 34b228626bd5..ab38c8203b79 100644 --- a/drivers/pwm/pwm-tiecap.c +++ b/drivers/pwm/pwm-tiecap.c @@ -1,21 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * ECAP PWM driver * * Copyright (C) 2012 Texas Instruments, Inc. - http://www.ti.com/ - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include <linux/module.h> diff --git a/drivers/pwm/pwm-tiehrpwm.c b/drivers/pwm/pwm-tiehrpwm.c index f7b8a86fa5c5..7b4c770ce9d6 100644 --- a/drivers/pwm/pwm-tiehrpwm.c +++ b/drivers/pwm/pwm-tiehrpwm.c @@ -1,21 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * EHRPWM PWM driver * * Copyright (C) 2012 Texas Instruments, Inc. - http://www.ti.com/ - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include <linux/module.h> @@ -382,6 +369,8 @@ static void ehrpwm_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) } /* Update shadow register first before modifying active register */ + ehrpwm_modify(pc->mmio_base, AQSFRC, AQSFRC_RLDCSF_MASK, + AQSFRC_RLDCSF_ZRO); ehrpwm_modify(pc->mmio_base, AQCSFRC, aqcsfrc_mask, aqcsfrc_val); /* * Changes to immediate action on Action Qualifier. This puts diff --git a/drivers/pwm/pwm-tipwmss.c b/drivers/pwm/pwm-tipwmss.c index 7fa85a1604da..e9c26c94251b 100644 --- a/drivers/pwm/pwm-tipwmss.c +++ b/drivers/pwm/pwm-tipwmss.c @@ -1,18 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * TI PWM Subsystem driver * * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * */ #include <linux/module.h> diff --git a/drivers/pwm/pwm-twl-led.c b/drivers/pwm/pwm-twl-led.c index 01153622778b..630b9a578820 100644 --- a/drivers/pwm/pwm-twl-led.c +++ b/drivers/pwm/pwm-twl-led.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Driver for TWL4030/6030 Pulse Width Modulator used as LED driver * @@ -6,18 +7,6 @@ * * This driver is a complete rewrite of the former pwm-twl6030.c authorded by: * Hemanth V <hemanthv@ti.com> - * - * 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. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see <http://www.gnu.org/licenses/>. */ #include <linux/module.h> diff --git a/drivers/pwm/pwm-twl.c b/drivers/pwm/pwm-twl.c index b7a45be99815..aee67974f353 100644 --- a/drivers/pwm/pwm-twl.c +++ b/drivers/pwm/pwm-twl.c @@ -1,20 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Driver for TWL4030/6030 Generic Pulse Width Modulator * * Copyright (C) 2012 Texas Instruments * Author: Peter Ujfalusi <peter.ujfalusi@ti.com> - * - * 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. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see <http://www.gnu.org/licenses/>. */ #include <linux/module.h> diff --git a/drivers/pwm/pwm-vt8500.c b/drivers/pwm/pwm-vt8500.c index 3a78dd09ac81..11d45e56a923 100644 --- a/drivers/pwm/pwm-vt8500.c +++ b/drivers/pwm/pwm-vt8500.c @@ -1,17 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * drivers/pwm/pwm-vt8500.c * * Copyright (C) 2012 Tony Prisk <linux@prisktech.co.nz> * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com> - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include <linux/module.h> diff --git a/drivers/pwm/pwm-zx.c b/drivers/pwm/pwm-zx.c index 5d27c16edfb1..e24f4be35316 100644 --- a/drivers/pwm/pwm-zx.c +++ b/drivers/pwm/pwm-zx.c @@ -1,10 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) 2017 Sanechips Technology Co., Ltd. * Copyright 2017 Linaro Ltd. - * - * 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. */ #include <linux/clk.h> diff --git a/drivers/pwm/sysfs.c b/drivers/pwm/sysfs.c index ceb233dd6048..bf6823fe0812 100644 --- a/drivers/pwm/sysfs.c +++ b/drivers/pwm/sysfs.c @@ -1,19 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * A simple sysfs interface for the generic PWM framework * * Copyright (C) 2013 H Hartley Sweeten <hsweeten@visionengravers.com> * * Based on previous work by Lars Poeschel <poeschel@lemonage.de> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include <linux/device.h> @@ -398,7 +389,7 @@ void pwmchip_sysfs_export(struct pwm_chip *chip) /* * If device_create() fails the pwm_chip is still usable by - * the kernel its just not exported. + * the kernel it's just not exported. */ parent = device_create(&pwm_class, chip->dev, MKDEV(0, 0), chip, "pwmchip%d", chip->base); @@ -411,19 +402,6 @@ void pwmchip_sysfs_export(struct pwm_chip *chip) void pwmchip_sysfs_unexport(struct pwm_chip *chip) { struct device *parent; - - parent = class_find_device(&pwm_class, NULL, chip, - pwmchip_sysfs_match); - if (parent) { - /* for class_find_device() */ - put_device(parent); - device_unregister(parent); - } -} - -void pwmchip_sysfs_unexport_children(struct pwm_chip *chip) -{ - struct device *parent; unsigned int i; parent = class_find_device(&pwm_class, NULL, chip, @@ -439,6 +417,7 @@ void pwmchip_sysfs_unexport_children(struct pwm_chip *chip) } put_device(parent); + device_unregister(parent); } static int __init pwm_sysfs_init(void) |