diff options
35 files changed, 1065 insertions, 439 deletions
| diff --git a/Documentation/devicetree/bindings/pwm/adi,axi-pwmgen.yaml b/Documentation/devicetree/bindings/pwm/adi,axi-pwmgen.yaml new file mode 100644 index 000000000000..ec6115d3796b --- /dev/null +++ b/Documentation/devicetree/bindings/pwm/adi,axi-pwmgen.yaml @@ -0,0 +1,48 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pwm/adi,axi-pwmgen.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Analog Devices AXI PWM generator + +maintainers: +  - Michael Hennerich <[email protected]> +  - Nuno Sá <[email protected]> + +description: +  The Analog Devices AXI PWM generator can generate PWM signals +  with variable pulse width and period. + +  https://wiki.analog.com/resources/fpga/docs/axi_pwm_gen + +allOf: +  - $ref: pwm.yaml# + +properties: +  compatible: +    const: adi,axi-pwmgen-2.00.a + +  reg: +    maxItems: 1 + +  "#pwm-cells": +    const: 2 + +  clocks: +    maxItems: 1 + +required: +  - reg +  - clocks + +unevaluatedProperties: false + +examples: +  - | +    pwm@44b00000 { +       compatible = "adi,axi-pwmgen-2.00.a"; +       reg = <0x44b00000 0x1000>; +       clocks = <&spi_clk>; +       #pwm-cells = <2>; +    }; diff --git a/Documentation/devicetree/bindings/pwm/atmel,at91sam-pwm.yaml b/Documentation/devicetree/bindings/pwm/atmel,at91sam-pwm.yaml index 96cd6f3c3546..d20ad27657aa 100644 --- a/Documentation/devicetree/bindings/pwm/atmel,at91sam-pwm.yaml +++ b/Documentation/devicetree/bindings/pwm/atmel,at91sam-pwm.yaml @@ -23,7 +23,9 @@ properties:                - atmel,sama5d2-pwm                - microchip,sam9x60-pwm        - items: -          - const: microchip,sama7g5-pwm +          - enum: +              - microchip,sama7d65-pwm +              - microchip,sama7g5-pwm            - const: atmel,sama5d2-pwm        - items:            - const: microchip,sam9x7-pwm diff --git a/Documentation/devicetree/bindings/pwm/fsl,vf610-ftm-pwm.yaml b/Documentation/devicetree/bindings/pwm/fsl,vf610-ftm-pwm.yaml new file mode 100644 index 000000000000..7f9f72d95e7a --- /dev/null +++ b/Documentation/devicetree/bindings/pwm/fsl,vf610-ftm-pwm.yaml @@ -0,0 +1,92 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pwm/fsl,vf610-ftm-pwm.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Freescale FlexTimer Module (FTM) PWM controller + +description: | +  The same FTM PWM device can have a different endianness on different SoCs. The +  device tree provides a property to describing this so that an operating system +  device driver can handle all variants of the device. Refer to the table below +  for the endianness of the FTM PWM block as integrated into the existing SoCs: + +  SoC     | FTM-PWM endianness +  --------+------------------- +  Vybrid  | LE +  LS1     | BE +  LS2     | LE + +  Please see ../regmap/regmap.txt for more detail about how to specify endian +  modes in device tree. + +maintainers: +  - Frank Li <[email protected]> + +properties: +  compatible: +    enum: +      - fsl,vf610-ftm-pwm +      - fsl,imx8qm-ftm-pwm + +  reg: +    maxItems: 1 + +  "#pwm-cells": +    const: 3 + +  clocks: +    minItems: 4 +    maxItems: 4 + +  clock-names: +    items: +      - const: ftm_sys +      - const: ftm_ext +      - const: ftm_fix +      - const: ftm_cnt_clk_en + +  pinctrl-0: true +  pinctrl-1: true + +  pinctrl-names: +    minItems: 1 +    items: +      - const: default +      - const: sleep + +  big-endian: +    $ref: /schemas/types.yaml#/definitions/flag +    description: +      Boolean property, required if the FTM PWM registers use a big- +      endian rather than little-endian layout. + +required: +  - compatible +  - reg +  - clocks +  - clock-names + +allOf: +  - $ref: pwm.yaml# + +unevaluatedProperties: false + +examples: +  - | +    #include <dt-bindings/clock/vf610-clock.h> + +    pwm@40038000 { +        compatible = "fsl,vf610-ftm-pwm"; +        reg = <0x40038000 0x1000>; +        #pwm-cells = <3>; +        clocks = <&clks VF610_CLK_FTM0>, +                 <&clks VF610_CLK_FTM0_EXT_SEL>, +                 <&clks VF610_CLK_FTM0_FIX_SEL>, +                 <&clks VF610_CLK_FTM0_EXT_FIX_EN>; +        clock-names = "ftm_sys", "ftm_ext", "ftm_fix", "ftm_cnt_clk_en"; +        pinctrl-names = "default"; +        pinctrl-0 = <&pinctrl_pwm0_1>; +        big-endian; +    }; diff --git a/Documentation/devicetree/bindings/pwm/imx-pwm.yaml b/Documentation/devicetree/bindings/pwm/imx-pwm.yaml index a84a240a61dc..04148198e34d 100644 --- a/Documentation/devicetree/bindings/pwm/imx-pwm.yaml +++ b/Documentation/devicetree/bindings/pwm/imx-pwm.yaml @@ -68,7 +68,6 @@ required:    - reg    - clocks    - clock-names -  - interrupts  additionalProperties: false diff --git a/Documentation/devicetree/bindings/pwm/pwm-fsl-ftm.txt b/Documentation/devicetree/bindings/pwm/pwm-fsl-ftm.txt deleted file mode 100644 index 36532cd5ab25..000000000000 --- a/Documentation/devicetree/bindings/pwm/pwm-fsl-ftm.txt +++ /dev/null @@ -1,55 +0,0 @@ -Freescale FlexTimer Module (FTM) PWM controller - -The same FTM PWM device can have a different endianness on different SoCs. The -device tree provides a property to describing this so that an operating system -device driver can handle all variants of the device. Refer to the table below -for the endianness of the FTM PWM block as integrated into the existing SoCs: - -	SoC     | FTM-PWM endianness -	--------+------------------- -	Vybrid  | LE -	LS1     | BE -	LS2     | LE - -Please see ../regmap/regmap.txt for more detail about how to specify endian -modes in device tree. - - -Required properties: -- compatible : should be "fsl,<soc>-ftm-pwm" and one of the following -   compatible strings: -  - "fsl,vf610-ftm-pwm" for PWM compatible with the one integrated on VF610 -  - "fsl,imx8qm-ftm-pwm" for PWM compatible with the one integrated on i.MX8QM -- reg: Physical base address and length of the controller's registers -- #pwm-cells: Should be 3. See pwm.yaml in this directory for a description of -  the cells format. -- clock-names: Should include the following module clock source entries: -    "ftm_sys" (module clock, also can be used as counter clock), -    "ftm_ext" (external counter clock), -    "ftm_fix" (fixed counter clock), -    "ftm_cnt_clk_en" (external and fixed counter clock enable/disable). -- clocks: Must contain a phandle and clock specifier for each entry in -  clock-names, please see clock/clock-bindings.txt for details of the property -  values. -- pinctrl-names: Must contain a "default" entry. -- pinctrl-NNN: One property must exist for each entry in pinctrl-names. -  See pinctrl/pinctrl-bindings.txt for details of the property values. -- big-endian: Boolean property, required if the FTM PWM registers use a big- -  endian rather than little-endian layout. - -Example: - -pwm0: pwm@40038000 { -		compatible = "fsl,vf610-ftm-pwm"; -		reg = <0x40038000 0x1000>; -		#pwm-cells = <3>; -		clock-names = "ftm_sys", "ftm_ext", -				"ftm_fix", "ftm_cnt_clk_en"; -		clocks = <&clks VF610_CLK_FTM0>, -			<&clks VF610_CLK_FTM0_EXT_SEL>, -			<&clks VF610_CLK_FTM0_FIX_SEL>, -			<&clks VF610_CLK_FTM0_EXT_FIX_EN>; -		pinctrl-names = "default"; -		pinctrl-0 = <&pinctrl_pwm0_1>; -		big-endian; -}; diff --git a/Documentation/devicetree/bindings/pwm/pwm-gpio.yaml b/Documentation/devicetree/bindings/pwm/pwm-gpio.yaml new file mode 100644 index 000000000000..1576c193f2ab --- /dev/null +++ b/Documentation/devicetree/bindings/pwm/pwm-gpio.yaml @@ -0,0 +1,46 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pwm/pwm-gpio.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Generic software PWM for modulating GPIOs + +maintainers: +  - Stefan Wahren <[email protected]> + +allOf: +  - $ref: pwm.yaml# + +properties: +  compatible: +    const: pwm-gpio + +  "#pwm-cells": +    const: 3 +    description: +      See pwm.yaml in this directory for a description of the cells format. +      The first cell which represents the PWM instance number must always +      be zero. + +  gpios: +    description: +      GPIO to be modulated +    maxItems: 1 + +required: +  - compatible +  - "#pwm-cells" +  - gpios + +additionalProperties: false + +examples: +  - | +    #include <dt-bindings/gpio/gpio.h> + +    pwm { +        #pwm-cells = <3>; +        compatible = "pwm-gpio"; +        gpios = <&gpio 1 GPIO_ACTIVE_HIGH>; +    }; diff --git a/Documentation/devicetree/bindings/pwm/pwm.yaml b/Documentation/devicetree/bindings/pwm/pwm.yaml index abd9fa873354..f2206ec3c7c4 100644 --- a/Documentation/devicetree/bindings/pwm/pwm.yaml +++ b/Documentation/devicetree/bindings/pwm/pwm.yaml @@ -16,8 +16,10 @@ properties:      pattern: "^pwm(@.*|-([0-9]|[1-9][0-9]+))?$"    "#pwm-cells": -    description: -      Number of cells in a PWM specifier. +    description: | +      Number of cells in a PWM specifier. Typically the cells represent, in +      order: the chip-relative PWM number, the PWM period in nanoseconds and +      optionally a number of flags (defined in <dt-bindings/pwm/pwm.h>).  required:    - "#pwm-cells" diff --git a/Documentation/driver-api/gpio/drivers-on-gpio.rst b/Documentation/driver-api/gpio/drivers-on-gpio.rst index af632d764ac6..95572d2a94ce 100644 --- a/Documentation/driver-api/gpio/drivers-on-gpio.rst +++ b/Documentation/driver-api/gpio/drivers-on-gpio.rst @@ -27,7 +27,12 @@ hardware descriptions such as device tree or ACPI:    to the lines for a more permanent solution of this type.  - gpio-beeper: drivers/input/misc/gpio-beeper.c is used to provide a beep from -  an external speaker connected to a GPIO line. +  an external speaker connected to a GPIO line. (If the beep is controlled by +  off/on, for an actual PWM waveform, see pwm-gpio below.) + +- pwm-gpio: drivers/pwm/pwm-gpio.c is used to toggle a GPIO with a high +  resolution timer producing a PWM waveform on the GPIO line, as well as +  Linux high resolution timers can do.  - extcon-gpio: drivers/extcon/extcon-gpio.c is used when you need to read an    external connector status, such as a headset line for an audio driver or an diff --git a/MAINTAINERS b/MAINTAINERS index 8553b51efb0d..65bdae824587 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3549,6 +3549,15 @@ W:	https://ez.analog.com/linux-software-drivers  F:	Documentation/devicetree/bindings/spi/adi,axi-spi-engine.yaml  F:	drivers/spi/spi-axi-spi-engine.c +AXI PWM GENERATOR +M:	Michael Hennerich <[email protected]> +M:	Nuno Sá <[email protected]> +S:	Supported +W:	https://ez.analog.com/linux-software-drivers +F:	Documentation/devicetree/bindings/pwm/adi,axi-pwmgen.yaml +F:	drivers/pwm/pwm-axi-pwmgen.c +  AXXIA I2C CONTROLLER  M:	Krzysztof Adamski <[email protected]> diff --git a/drivers/bus/ts-nbus.c b/drivers/bus/ts-nbus.c index baf22a82c47a..b8af44c5cdbd 100644 --- a/drivers/bus/ts-nbus.c +++ b/drivers/bus/ts-nbus.c @@ -294,7 +294,7 @@ static int ts_nbus_probe(struct platform_device *pdev)  	state.duty_cycle = state.period;  	state.enabled = true; -	ret = pwm_apply_state(pwm, &state); +	ret = pwm_apply_might_sleep(pwm, &state);  	if (ret < 0)  		return dev_err_probe(dev, ret, "failed to configure PWM\n"); diff --git a/drivers/counter/stm32-timer-cnt.c b/drivers/counter/stm32-timer-cnt.c index 0664ef969f79..186e73d6ccb4 100644 --- a/drivers/counter/stm32-timer-cnt.c +++ b/drivers/counter/stm32-timer-cnt.c @@ -465,7 +465,7 @@ static int stm32_count_events_configure(struct counter_device *counter)  			ret = stm32_count_capture_configure(counter, event_node->channel, true);  			if (ret)  				return ret; -			dier |= TIM_DIER_CC_IE(event_node->channel); +			dier |= TIM_DIER_CCxIE(event_node->channel + 1);  			break;  		default:  			/* should never reach this path */ @@ -478,7 +478,7 @@ static int stm32_count_events_configure(struct counter_device *counter)  	/* check for disabled capture events */  	for (i = 0 ; i < priv->nchannels; i++) { -		if (!(dier & TIM_DIER_CC_IE(i))) { +		if (!(dier & TIM_DIER_CCxIE(i + 1))) {  			ret = stm32_count_capture_configure(counter, i, false);  			if (ret)  				return ret; diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index 1dd7921194f5..3e53838990f5 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig @@ -94,6 +94,19 @@ config PWM_ATMEL_TCB  	  To compile this driver as a module, choose M here: the module  	  will be called pwm-atmel-tcb. +config PWM_AXI_PWMGEN +	tristate "Analog Devices AXI PWM generator" +	depends on MICROBLAZE || NIOS2 || ARCH_ZYNQ || ARCH_ZYNQMP || ARCH_INTEL_SOCFPGA || COMPILE_TEST +	select REGMAP_MMIO +	help +	  This enables support for the Analog Devices AXI PWM generator. + +	  This is a configurable PWM generator with variable pulse width and +	  period. + +	  To compile this driver as a module, choose M here: the module will be +	  called pwm-axi-pwmgen. +  config PWM_BCM_IPROC  	tristate "iProc PWM support"  	depends on ARCH_BCM_IPROC || COMPILE_TEST @@ -223,6 +236,17 @@ config PWM_FSL_FTM  	  To compile this driver as a module, choose M here: the module  	  will be called pwm-fsl-ftm. +config PWM_GPIO +	tristate "GPIO PWM support" +	depends on GPIOLIB +	depends on HIGH_RES_TIMERS +	help +	  Generic PWM framework driver for software PWM toggling a GPIO pin +	  from kernel high-resolution timers. + +	  To compile this driver as a module, choose M here: the module +	  will be called pwm-gpio. +  config PWM_HIBVT  	tristate "HiSilicon BVT PWM support"  	depends on ARCH_HISI || COMPILE_TEST diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile index 90913519f11a..0be4f3e6dd43 100644 --- a/drivers/pwm/Makefile +++ b/drivers/pwm/Makefile @@ -5,6 +5,7 @@ obj-$(CONFIG_PWM_APPLE)		+= pwm-apple.o  obj-$(CONFIG_PWM_ATMEL)		+= pwm-atmel.o  obj-$(CONFIG_PWM_ATMEL_HLCDC_PWM)	+= pwm-atmel-hlcdc.o  obj-$(CONFIG_PWM_ATMEL_TCB)	+= pwm-atmel-tcb.o +obj-$(CONFIG_PWM_AXI_PWMGEN)	+= pwm-axi-pwmgen.o  obj-$(CONFIG_PWM_BCM_IPROC)	+= pwm-bcm-iproc.o  obj-$(CONFIG_PWM_BCM_KONA)	+= pwm-bcm-kona.o  obj-$(CONFIG_PWM_BCM2835)	+= pwm-bcm2835.o @@ -18,6 +19,7 @@ obj-$(CONFIG_PWM_DWC_CORE)	+= pwm-dwc-core.o  obj-$(CONFIG_PWM_DWC)		+= pwm-dwc.o  obj-$(CONFIG_PWM_EP93XX)	+= pwm-ep93xx.o  obj-$(CONFIG_PWM_FSL_FTM)	+= pwm-fsl-ftm.o +obj-$(CONFIG_PWM_GPIO)		+= pwm-gpio.o  obj-$(CONFIG_PWM_HIBVT)		+= pwm-hibvt.o  obj-$(CONFIG_PWM_IMG)		+= pwm-img.o  obj-$(CONFIG_PWM_IMX1)		+= pwm-imx1.o diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c index 18574857641e..8acbcf5b6673 100644 --- a/drivers/pwm/core.c +++ b/drivers/pwm/core.c @@ -6,6 +6,8 @@   * Copyright (C) 2011-2012 Avionic Design GmbH   */ +#define DEFAULT_SYMBOL_NAMESPACE PWM +  #include <linux/acpi.h>  #include <linux/module.h>  #include <linux/idr.h> @@ -135,6 +137,25 @@ static void pwm_apply_debug(struct pwm_device *pwm,  	}  } +static bool pwm_state_valid(const struct pwm_state *state) +{ +	/* +	 * For a disabled state all other state description is irrelevant and +	 * and supposed to be ignored. So also ignore any strange values and +	 * consider the state ok. +	 */ +	if (state->enabled) +		return true; + +	if (!state->period) +		return false; + +	if (state->duty_cycle > state->period) +		return false; + +	return true; +} +  /**   * __pwm_apply() - atomically apply a new state to a PWM device   * @pwm: PWM device @@ -145,9 +166,25 @@ static int __pwm_apply(struct pwm_device *pwm, const struct pwm_state *state)  	struct pwm_chip *chip;  	int err; -	if (!pwm || !state || !state->period || -	    state->duty_cycle > state->period) +	if (!pwm || !state) +		return -EINVAL; + +	if (!pwm_state_valid(state)) { +		/* +		 * Allow to transition from one invalid state to another. +		 * This ensures that you can e.g. change the polarity while +		 * the period is zero. (This happens on stm32 when the hardware +		 * is in its poweron default state.) This greatly simplifies +		 * working with the sysfs API where you can only change one +		 * parameter at a time. +		 */ +		if (!pwm_state_valid(&pwm->state)) { +			pwm->state = *state; +			return 0; +		} +  		return -EINVAL; +	}  	chip = pwm->chip; @@ -291,19 +328,15 @@ EXPORT_SYMBOL_GPL(pwm_adjust_config);  int pwm_capture(struct pwm_device *pwm, struct pwm_capture *result,  		unsigned long timeout)  { -	int err; -  	if (!pwm || !pwm->chip->ops)  		return -EINVAL;  	if (!pwm->chip->ops->capture)  		return -ENOSYS; -	mutex_lock(&pwm_lock); -	err = pwm->chip->ops->capture(pwm->chip, pwm, result, timeout); -	mutex_unlock(&pwm_lock); +	guard(mutex)(&pwm_lock); -	return err; +	return pwm->chip->ops->capture(pwm->chip, pwm, result, timeout);  }  EXPORT_SYMBOL_GPL(pwm_capture); @@ -315,19 +348,15 @@ static struct pwm_chip *pwmchip_find_by_name(const char *name)  	if (!name)  		return NULL; -	mutex_lock(&pwm_lock); +	guard(mutex)(&pwm_lock);  	idr_for_each_entry_ul(&pwm_chips, chip, tmp, id) {  		const char *chip_name = dev_name(pwmchip_parent(chip)); -		if (chip_name && strcmp(chip_name, name) == 0) { -			mutex_unlock(&pwm_lock); +		if (chip_name && strcmp(chip_name, name) == 0)  			return chip; -		}  	} -	mutex_unlock(&pwm_lock); -  	return NULL;  } @@ -394,9 +423,9 @@ err_get_device:   * chip. A negative error code is returned if the index is not valid for the   * specified PWM chip or if the PWM device cannot be requested.   */ -struct pwm_device *pwm_request_from_chip(struct pwm_chip *chip, -					 unsigned int index, -					 const char *label) +static struct pwm_device *pwm_request_from_chip(struct pwm_chip *chip, +						unsigned int index, +						const char *label)  {  	struct pwm_device *pwm;  	int err; @@ -404,18 +433,16 @@ struct pwm_device *pwm_request_from_chip(struct pwm_chip *chip,  	if (!chip || index >= chip->npwm)  		return ERR_PTR(-EINVAL); -	mutex_lock(&pwm_lock); +	guard(mutex)(&pwm_lock); +  	pwm = &chip->pwms[index];  	err = pwm_device_request(pwm, label);  	if (err < 0) -		pwm = ERR_PTR(err); +		return ERR_PTR(err); -	mutex_unlock(&pwm_lock);  	return pwm;  } -EXPORT_SYMBOL_GPL(pwm_request_from_chip); -  struct pwm_device *  of_pwm_xlate_with_flags(struct pwm_chip *chip, const struct of_phandle_args *args) @@ -511,11 +538,11 @@ static ssize_t period_store(struct device *pwm_dev,  	if (ret)  		return ret; -	mutex_lock(&export->lock); +	guard(mutex)(&export->lock); +  	pwm_get_state(pwm, &state);  	state.period = val;  	ret = pwm_apply_might_sleep(pwm, &state); -	mutex_unlock(&export->lock);  	return ret ? : size;  } @@ -546,11 +573,11 @@ static ssize_t duty_cycle_store(struct device *pwm_dev,  	if (ret)  		return ret; -	mutex_lock(&export->lock); +	guard(mutex)(&export->lock); +  	pwm_get_state(pwm, &state);  	state.duty_cycle = val;  	ret = pwm_apply_might_sleep(pwm, &state); -	mutex_unlock(&export->lock);  	return ret ? : size;  } @@ -580,7 +607,7 @@ static ssize_t enable_store(struct device *pwm_dev,  	if (ret)  		return ret; -	mutex_lock(&export->lock); +	guard(mutex)(&export->lock);  	pwm_get_state(pwm, &state); @@ -592,14 +619,11 @@ static ssize_t enable_store(struct device *pwm_dev,  		state.enabled = true;  		break;  	default: -		ret = -EINVAL; -		goto unlock; +		return -EINVAL;  	}  	ret = pwm_apply_might_sleep(pwm, &state); -unlock: -	mutex_unlock(&export->lock);  	return ret ? : size;  } @@ -643,11 +667,11 @@ static ssize_t polarity_store(struct device *pwm_dev,  	else  		return -EINVAL; -	mutex_lock(&export->lock); +	guard(mutex)(&export->lock); +  	pwm_get_state(pwm, &state);  	state.polarity = polarity;  	ret = pwm_apply_might_sleep(pwm, &state); -	mutex_unlock(&export->lock);  	return ret ? : size;  } @@ -1102,11 +1126,11 @@ int __pwmchip_add(struct pwm_chip *chip, struct module *owner)  	chip->owner = owner; -	mutex_lock(&pwm_lock); +	guard(mutex)(&pwm_lock);  	ret = idr_alloc(&pwm_chips, chip, 0, 0, GFP_KERNEL);  	if (ret < 0) -		goto err_idr_alloc; +		return ret;  	chip->id = ret; @@ -1119,8 +1143,6 @@ int __pwmchip_add(struct pwm_chip *chip, struct module *owner)  	if (ret)  		goto err_device_add; -	mutex_unlock(&pwm_lock); -  	return 0;  err_device_add: @@ -1128,9 +1150,6 @@ err_device_add:  		of_pwmchip_remove(chip);  	idr_remove(&pwm_chips, chip->id); -err_idr_alloc: - -	mutex_unlock(&pwm_lock);  	return ret;  } @@ -1149,11 +1168,8 @@ void pwmchip_remove(struct pwm_chip *chip)  	if (IS_ENABLED(CONFIG_OF))  		of_pwmchip_remove(chip); -	mutex_lock(&pwm_lock); - -	idr_remove(&pwm_chips, chip->id); - -	mutex_unlock(&pwm_lock); +	scoped_guard(mutex, &pwm_lock) +		idr_remove(&pwm_chips, chip->id);  	device_del(&chip->dev);  } @@ -1209,15 +1225,11 @@ static struct pwm_chip *fwnode_to_pwmchip(struct fwnode_handle *fwnode)  	struct pwm_chip *chip;  	unsigned long id, tmp; -	mutex_lock(&pwm_lock); +	guard(mutex)(&pwm_lock);  	idr_for_each_entry_ul(&pwm_chips, chip, tmp, id) -		if (pwmchip_parent(chip) && device_match_fwnode(pwmchip_parent(chip), fwnode)) { -			mutex_unlock(&pwm_lock); +		if (pwmchip_parent(chip) && device_match_fwnode(pwmchip_parent(chip), fwnode))  			return chip; -		} - -	mutex_unlock(&pwm_lock);  	return ERR_PTR(-EPROBE_DEFER);  } @@ -1366,14 +1378,12 @@ static LIST_HEAD(pwm_lookup_list);   */  void pwm_add_table(struct pwm_lookup *table, size_t num)  { -	mutex_lock(&pwm_lookup_lock); +	guard(mutex)(&pwm_lookup_lock);  	while (num--) {  		list_add_tail(&table->list, &pwm_lookup_list);  		table++;  	} - -	mutex_unlock(&pwm_lookup_lock);  }  /** @@ -1383,14 +1393,12 @@ void pwm_add_table(struct pwm_lookup *table, size_t num)   */  void pwm_remove_table(struct pwm_lookup *table, size_t num)  { -	mutex_lock(&pwm_lookup_lock); +	guard(mutex)(&pwm_lookup_lock);  	while (num--) {  		list_del(&table->list);  		table++;  	} - -	mutex_unlock(&pwm_lookup_lock);  }  /** @@ -1451,36 +1459,33 @@ struct pwm_device *pwm_get(struct device *dev, const char *con_id)  	 * Then we take the most specific entry - with the following order  	 * of precedence: dev+con > dev only > con only.  	 */ -	mutex_lock(&pwm_lookup_lock); - -	list_for_each_entry(p, &pwm_lookup_list, list) { -		match = 0; +	scoped_guard(mutex, &pwm_lookup_lock) +		list_for_each_entry(p, &pwm_lookup_list, list) { +			match = 0; -		if (p->dev_id) { -			if (!dev_id || strcmp(p->dev_id, dev_id)) -				continue; +			if (p->dev_id) { +				if (!dev_id || strcmp(p->dev_id, dev_id)) +					continue; -			match += 2; -		} +				match += 2; +			} -		if (p->con_id) { -			if (!con_id || strcmp(p->con_id, con_id)) -				continue; +			if (p->con_id) { +				if (!con_id || strcmp(p->con_id, con_id)) +					continue; -			match += 1; -		} +				match += 1; +			} -		if (match > best) { -			chosen = p; +			if (match > best) { +				chosen = p; -			if (match != 3) -				best = match; -			else -				break; +				if (match != 3) +					best = match; +				else +					break; +			}  		} -	} - -	mutex_unlock(&pwm_lookup_lock);  	if (!chosen)  		return ERR_PTR(-ENODEV); @@ -1532,11 +1537,11 @@ void pwm_put(struct pwm_device *pwm)  	chip = pwm->chip; -	mutex_lock(&pwm_lock); +	guard(mutex)(&pwm_lock);  	if (!test_and_clear_bit(PWMF_REQUESTED, &pwm->flags)) {  		pr_warn("PWM device already freed\n"); -		goto out; +		return;  	}  	if (chip->ops->free) @@ -1547,8 +1552,6 @@ void pwm_put(struct pwm_device *pwm)  	put_device(&chip->dev);  	module_put(chip->owner); -out: -	mutex_unlock(&pwm_lock);  }  EXPORT_SYMBOL_GPL(pwm_put); @@ -1705,9 +1708,17 @@ DEFINE_SEQ_ATTRIBUTE(pwm_debugfs);  static int __init pwm_init(void)  { +	int ret; + +	ret = class_register(&pwm_class); +	if (ret) { +		pr_err("Failed to initialize PWM class (%pe)\n", ERR_PTR(ret)); +		return ret; +	} +  	if (IS_ENABLED(CONFIG_DEBUG_FS))  		debugfs_create_file("pwm", 0444, NULL, NULL, &pwm_debugfs_fops); -	return class_register(&pwm_class); +	return 0;  }  subsys_initcall(pwm_init); diff --git a/drivers/pwm/pwm-atmel-tcb.c b/drivers/pwm/pwm-atmel-tcb.c index 528e54c5999d..f9a9c12cbcdd 100644 --- a/drivers/pwm/pwm-atmel-tcb.c +++ b/drivers/pwm/pwm-atmel-tcb.c @@ -81,7 +81,8 @@ static int atmel_tcb_pwm_request(struct pwm_chip *chip,  	tcbpwm->period = 0;  	tcbpwm->div = 0; -	spin_lock(&tcbpwmc->lock); +	guard(spinlock)(&tcbpwmc->lock); +  	regmap_read(tcbpwmc->regmap, ATMEL_TC_REG(tcbpwmc->channel, CMR), &cmr);  	/*  	 * Get init config from Timer Counter registers if @@ -107,7 +108,6 @@ static int atmel_tcb_pwm_request(struct pwm_chip *chip,  	cmr |= ATMEL_TC_WAVE | ATMEL_TC_WAVESEL_UP_AUTO | ATMEL_TC_EEVT_XC0;  	regmap_write(tcbpwmc->regmap, ATMEL_TC_REG(tcbpwmc->channel, CMR), cmr); -	spin_unlock(&tcbpwmc->lock);  	return 0;  } @@ -137,7 +137,6 @@ static void atmel_tcb_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm,  	if (tcbpwm->duty == 0)  		polarity = !polarity; -	spin_lock(&tcbpwmc->lock);  	regmap_read(tcbpwmc->regmap, ATMEL_TC_REG(tcbpwmc->channel, CMR), &cmr);  	/* flush old setting and set the new one */ @@ -172,8 +171,6 @@ static void atmel_tcb_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm,  			     ATMEL_TC_SWTRG);  		tcbpwmc->bkup.enabled = 0;  	} - -	spin_unlock(&tcbpwmc->lock);  }  static int atmel_tcb_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm, @@ -194,7 +191,6 @@ static int atmel_tcb_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm,  	if (tcbpwm->duty == 0)  		polarity = !polarity; -	spin_lock(&tcbpwmc->lock);  	regmap_read(tcbpwmc->regmap, ATMEL_TC_REG(tcbpwmc->channel, CMR), &cmr);  	/* flush old setting and set the new one */ @@ -256,7 +252,6 @@ static int atmel_tcb_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm,  	regmap_write(tcbpwmc->regmap, ATMEL_TC_REG(tcbpwmc->channel, CCR),  		     ATMEL_TC_SWTRG | ATMEL_TC_CLKEN);  	tcbpwmc->bkup.enabled = 1; -	spin_unlock(&tcbpwmc->lock);  	return 0;  } @@ -265,7 +260,8 @@ static int atmel_tcb_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,  {  	struct atmel_tcb_pwm_chip *tcbpwmc = to_tcb_chip(chip);  	struct atmel_tcb_pwm_device *tcbpwm = &tcbpwmc->pwms[pwm->hwpwm]; -	struct atmel_tcb_pwm_device *atcbpwm = NULL; +	/* companion PWM sharing register values period and div */ +	struct atmel_tcb_pwm_device *atcbpwm = &tcbpwmc->pwms[pwm->hwpwm ^ 1];  	int i = 0;  	int slowclk = 0;  	unsigned period; @@ -310,11 +306,6 @@ static int atmel_tcb_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,  	duty = div_u64(duty_ns, min);  	period = div_u64(period_ns, min); -	if (pwm->hwpwm == 0) -		atcbpwm = &tcbpwmc->pwms[1]; -	else -		atcbpwm = &tcbpwmc->pwms[0]; -  	/*  	 * PWM devices provided by the TCB driver are grouped by 2.  	 * PWM devices in a given group must be configured with the @@ -323,8 +314,7 @@ static int atmel_tcb_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,  	 * We're checking the period value of the second PWM device  	 * in this group before applying the new config.  	 */ -	if ((atcbpwm && atcbpwm->duty > 0 && -			atcbpwm->duty != atcbpwm->period) && +	if ((atcbpwm->duty > 0 && atcbpwm->duty != atcbpwm->period) &&  		(atcbpwm->div != i || atcbpwm->period != period)) {  		dev_err(pwmchip_parent(chip),  			"failed to configure period_ns: PWM group already configured with a different value\n"); @@ -341,9 +331,12 @@ static int atmel_tcb_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,  static int atmel_tcb_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,  			       const struct pwm_state *state)  { +	struct atmel_tcb_pwm_chip *tcbpwmc = to_tcb_chip(chip);  	int duty_cycle, period;  	int ret; +	guard(spinlock)(&tcbpwmc->lock); +  	if (!state->enabled) {  		atmel_tcb_pwm_disable(chip, pwm, state->polarity);  		return 0; @@ -389,17 +382,17 @@ static int atmel_tcb_pwm_probe(struct platform_device *pdev)  {  	struct pwm_chip *chip;  	const struct of_device_id *match; -	struct atmel_tcb_pwm_chip *tcbpwm; +	struct atmel_tcb_pwm_chip *tcbpwmc;  	const struct atmel_tcb_config *config;  	struct device_node *np = pdev->dev.of_node;  	char clk_name[] = "t0_clk";  	int err;  	int channel; -	chip = devm_pwmchip_alloc(&pdev->dev, NPWM, sizeof(*tcbpwm)); +	chip = devm_pwmchip_alloc(&pdev->dev, NPWM, sizeof(*tcbpwmc));  	if (IS_ERR(chip))  		return PTR_ERR(chip); -	tcbpwm = to_tcb_chip(chip); +	tcbpwmc = to_tcb_chip(chip);  	err = of_property_read_u32(np, "reg", &channel);  	if (err < 0) { @@ -409,20 +402,20 @@ static int atmel_tcb_pwm_probe(struct platform_device *pdev)  		return err;  	} -	tcbpwm->regmap = syscon_node_to_regmap(np->parent); -	if (IS_ERR(tcbpwm->regmap)) -		return PTR_ERR(tcbpwm->regmap); +	tcbpwmc->regmap = syscon_node_to_regmap(np->parent); +	if (IS_ERR(tcbpwmc->regmap)) +		return PTR_ERR(tcbpwmc->regmap); -	tcbpwm->slow_clk = of_clk_get_by_name(np->parent, "slow_clk"); -	if (IS_ERR(tcbpwm->slow_clk)) -		return PTR_ERR(tcbpwm->slow_clk); +	tcbpwmc->slow_clk = of_clk_get_by_name(np->parent, "slow_clk"); +	if (IS_ERR(tcbpwmc->slow_clk)) +		return PTR_ERR(tcbpwmc->slow_clk);  	clk_name[1] += channel; -	tcbpwm->clk = of_clk_get_by_name(np->parent, clk_name); -	if (IS_ERR(tcbpwm->clk)) -		tcbpwm->clk = of_clk_get_by_name(np->parent, "t0_clk"); -	if (IS_ERR(tcbpwm->clk)) { -		err = PTR_ERR(tcbpwm->clk); +	tcbpwmc->clk = of_clk_get_by_name(np->parent, clk_name); +	if (IS_ERR(tcbpwmc->clk)) +		tcbpwmc->clk = of_clk_get_by_name(np->parent, "t0_clk"); +	if (IS_ERR(tcbpwmc->clk)) { +		err = PTR_ERR(tcbpwmc->clk);  		goto err_slow_clk;  	} @@ -430,22 +423,22 @@ static int atmel_tcb_pwm_probe(struct platform_device *pdev)  	config = match->data;  	if (config->has_gclk) { -		tcbpwm->gclk = of_clk_get_by_name(np->parent, "gclk"); -		if (IS_ERR(tcbpwm->gclk)) { -			err = PTR_ERR(tcbpwm->gclk); +		tcbpwmc->gclk = of_clk_get_by_name(np->parent, "gclk"); +		if (IS_ERR(tcbpwmc->gclk)) { +			err = PTR_ERR(tcbpwmc->gclk);  			goto err_clk;  		}  	}  	chip->ops = &atmel_tcb_pwm_ops; -	tcbpwm->channel = channel; -	tcbpwm->width = config->counter_width; +	tcbpwmc->channel = channel; +	tcbpwmc->width = config->counter_width; -	err = clk_prepare_enable(tcbpwm->slow_clk); +	err = clk_prepare_enable(tcbpwmc->slow_clk);  	if (err)  		goto err_gclk; -	spin_lock_init(&tcbpwm->lock); +	spin_lock_init(&tcbpwmc->lock);  	err = pwmchip_add(chip);  	if (err < 0) @@ -456,16 +449,16 @@ static int atmel_tcb_pwm_probe(struct platform_device *pdev)  	return 0;  err_disable_clk: -	clk_disable_unprepare(tcbpwm->slow_clk); +	clk_disable_unprepare(tcbpwmc->slow_clk);  err_gclk: -	clk_put(tcbpwm->gclk); +	clk_put(tcbpwmc->gclk);  err_clk: -	clk_put(tcbpwm->clk); +	clk_put(tcbpwmc->clk);  err_slow_clk: -	clk_put(tcbpwm->slow_clk); +	clk_put(tcbpwmc->slow_clk);  	return err;  } @@ -473,14 +466,14 @@ err_slow_clk:  static void atmel_tcb_pwm_remove(struct platform_device *pdev)  {  	struct pwm_chip *chip = platform_get_drvdata(pdev); -	struct atmel_tcb_pwm_chip *tcbpwm = to_tcb_chip(chip); +	struct atmel_tcb_pwm_chip *tcbpwmc = to_tcb_chip(chip);  	pwmchip_remove(chip); -	clk_disable_unprepare(tcbpwm->slow_clk); -	clk_put(tcbpwm->gclk); -	clk_put(tcbpwm->clk); -	clk_put(tcbpwm->slow_clk); +	clk_disable_unprepare(tcbpwmc->slow_clk); +	clk_put(tcbpwmc->gclk); +	clk_put(tcbpwmc->clk); +	clk_put(tcbpwmc->slow_clk);  }  static const struct of_device_id atmel_tcb_pwm_dt_ids[] = { @@ -492,14 +485,14 @@ MODULE_DEVICE_TABLE(of, atmel_tcb_pwm_dt_ids);  static int atmel_tcb_pwm_suspend(struct device *dev)  {  	struct pwm_chip *chip = dev_get_drvdata(dev); -	struct atmel_tcb_pwm_chip *tcbpwm = to_tcb_chip(chip); -	struct atmel_tcb_channel *chan = &tcbpwm->bkup; -	unsigned int channel = tcbpwm->channel; +	struct atmel_tcb_pwm_chip *tcbpwmc = to_tcb_chip(chip); +	struct atmel_tcb_channel *chan = &tcbpwmc->bkup; +	unsigned int channel = tcbpwmc->channel; -	regmap_read(tcbpwm->regmap, ATMEL_TC_REG(channel, CMR), &chan->cmr); -	regmap_read(tcbpwm->regmap, ATMEL_TC_REG(channel, RA), &chan->ra); -	regmap_read(tcbpwm->regmap, ATMEL_TC_REG(channel, RB), &chan->rb); -	regmap_read(tcbpwm->regmap, ATMEL_TC_REG(channel, RC), &chan->rc); +	regmap_read(tcbpwmc->regmap, ATMEL_TC_REG(channel, CMR), &chan->cmr); +	regmap_read(tcbpwmc->regmap, ATMEL_TC_REG(channel, RA), &chan->ra); +	regmap_read(tcbpwmc->regmap, ATMEL_TC_REG(channel, RB), &chan->rb); +	regmap_read(tcbpwmc->regmap, ATMEL_TC_REG(channel, RC), &chan->rc);  	return 0;  } @@ -507,17 +500,17 @@ static int atmel_tcb_pwm_suspend(struct device *dev)  static int atmel_tcb_pwm_resume(struct device *dev)  {  	struct pwm_chip *chip = dev_get_drvdata(dev); -	struct atmel_tcb_pwm_chip *tcbpwm = to_tcb_chip(chip); -	struct atmel_tcb_channel *chan = &tcbpwm->bkup; -	unsigned int channel = tcbpwm->channel; +	struct atmel_tcb_pwm_chip *tcbpwmc = to_tcb_chip(chip); +	struct atmel_tcb_channel *chan = &tcbpwmc->bkup; +	unsigned int channel = tcbpwmc->channel; -	regmap_write(tcbpwm->regmap, ATMEL_TC_REG(channel, CMR), chan->cmr); -	regmap_write(tcbpwm->regmap, ATMEL_TC_REG(channel, RA), chan->ra); -	regmap_write(tcbpwm->regmap, ATMEL_TC_REG(channel, RB), chan->rb); -	regmap_write(tcbpwm->regmap, ATMEL_TC_REG(channel, RC), chan->rc); +	regmap_write(tcbpwmc->regmap, ATMEL_TC_REG(channel, CMR), chan->cmr); +	regmap_write(tcbpwmc->regmap, ATMEL_TC_REG(channel, RA), chan->ra); +	regmap_write(tcbpwmc->regmap, ATMEL_TC_REG(channel, RB), chan->rb); +	regmap_write(tcbpwmc->regmap, ATMEL_TC_REG(channel, RC), chan->rc);  	if (chan->enabled) -		regmap_write(tcbpwm->regmap, +		regmap_write(tcbpwmc->regmap,  			     ATMEL_TC_CLKEN | ATMEL_TC_SWTRG,  			     ATMEL_TC_REG(channel, CCR)); diff --git a/drivers/pwm/pwm-axi-pwmgen.c b/drivers/pwm/pwm-axi-pwmgen.c new file mode 100644 index 000000000000..3ad60edf20a5 --- /dev/null +++ b/drivers/pwm/pwm-axi-pwmgen.c @@ -0,0 +1,242 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Analog Devices AXI PWM generator + * + * Copyright 2024 Analog Devices Inc. + * Copyright 2024 Baylibre SAS + * + * Device docs: https://analogdevicesinc.github.io/hdl/library/axi_pwm_gen/index.html + * + * Limitations: + * - The writes to registers for period and duty are shadowed until + *   LOAD_CONFIG is written to AXI_PWMGEN_REG_CONFIG, at which point + *   they take effect. + * - Writing LOAD_CONFIG also has the effect of re-synchronizing all + *   enabled channels, which could cause glitching on other channels. It + *   is therefore expected that channels are assigned harmonic periods + *   and all have a single user coordinating this. + * - Supports normal polarity. Does not support changing polarity. + * - On disable, the PWM output becomes low (inactive). + */ +#include <linux/bits.h> +#include <linux/clk.h> +#include <linux/err.h> +#include <linux/fpga/adi-axi-common.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/pwm.h> +#include <linux/regmap.h> +#include <linux/slab.h> + +#define AXI_PWMGEN_REG_CORE_VERSION	0x00 +#define AXI_PWMGEN_REG_ID		0x04 +#define AXI_PWMGEN_REG_SCRATCHPAD	0x08 +#define AXI_PWMGEN_REG_CORE_MAGIC	0x0C +#define AXI_PWMGEN_REG_CONFIG		0x10 +#define AXI_PWMGEN_REG_NPWM		0x14 +#define AXI_PWMGEN_CHX_PERIOD(ch)	(0x40 + (4 * (ch))) +#define AXI_PWMGEN_CHX_DUTY(ch)		(0x80 + (4 * (ch))) +#define AXI_PWMGEN_CHX_OFFSET(ch)	(0xC0 + (4 * (ch))) +#define AXI_PWMGEN_REG_CORE_MAGIC_VAL	0x601A3471 /* Identification number to test during setup */ +#define AXI_PWMGEN_LOAD_CONFIG		BIT(1) +#define AXI_PWMGEN_REG_CONFIG_RESET	BIT(0) + +struct axi_pwmgen_ddata { +	struct regmap *regmap; +	unsigned long clk_rate_hz; +}; + +static const struct regmap_config axi_pwmgen_regmap_config = { +	.reg_bits = 32, +	.reg_stride = 4, +	.val_bits = 32, +	.max_register = 0xFC, +}; + +static int axi_pwmgen_apply(struct pwm_chip *chip, struct pwm_device *pwm, +			    const struct pwm_state *state) +{ +	struct axi_pwmgen_ddata *ddata = pwmchip_get_drvdata(chip); +	unsigned int ch = pwm->hwpwm; +	struct regmap *regmap = ddata->regmap; +	u64 period_cnt, duty_cnt; +	int ret; + +	if (state->polarity != PWM_POLARITY_NORMAL) +		return -EINVAL; + +	if (state->enabled) { +		period_cnt = mul_u64_u64_div_u64(state->period, ddata->clk_rate_hz, NSEC_PER_SEC); +		if (period_cnt > UINT_MAX) +			period_cnt = UINT_MAX; + +		if (period_cnt == 0) +			return -EINVAL; + +		ret = regmap_write(regmap, AXI_PWMGEN_CHX_PERIOD(ch), period_cnt); +		if (ret) +			return ret; + +		duty_cnt = mul_u64_u64_div_u64(state->duty_cycle, ddata->clk_rate_hz, NSEC_PER_SEC); +		if (duty_cnt > UINT_MAX) +			duty_cnt = UINT_MAX; + +		ret = regmap_write(regmap, AXI_PWMGEN_CHX_DUTY(ch), duty_cnt); +		if (ret) +			return ret; +	} else { +		ret = regmap_write(regmap, AXI_PWMGEN_CHX_PERIOD(ch), 0); +		if (ret) +			return ret; + +		ret = regmap_write(regmap, AXI_PWMGEN_CHX_DUTY(ch), 0); +		if (ret) +			return ret; +	} + +	return regmap_write(regmap, AXI_PWMGEN_REG_CONFIG, AXI_PWMGEN_LOAD_CONFIG); +} + +static int axi_pwmgen_get_state(struct pwm_chip *chip, struct pwm_device *pwm, +				struct pwm_state *state) +{ +	struct axi_pwmgen_ddata *ddata = pwmchip_get_drvdata(chip); +	struct regmap *regmap = ddata->regmap; +	unsigned int ch = pwm->hwpwm; +	u32 cnt; +	int ret; + +	ret = regmap_read(regmap, AXI_PWMGEN_CHX_PERIOD(ch), &cnt); +	if (ret) +		return ret; + +	state->enabled = cnt != 0; + +	state->period = DIV_ROUND_UP_ULL((u64)cnt * NSEC_PER_SEC, ddata->clk_rate_hz); + +	ret = regmap_read(regmap, AXI_PWMGEN_CHX_DUTY(ch), &cnt); +	if (ret) +		return ret; + +	state->duty_cycle = DIV_ROUND_UP_ULL((u64)cnt * NSEC_PER_SEC, ddata->clk_rate_hz); + +	state->polarity = PWM_POLARITY_NORMAL; + +	return 0; +} + +static const struct pwm_ops axi_pwmgen_pwm_ops = { +	.apply = axi_pwmgen_apply, +	.get_state = axi_pwmgen_get_state, +}; + +static int axi_pwmgen_setup(struct regmap *regmap, struct device *dev) +{ +	int ret; +	u32 val; + +	ret = regmap_read(regmap, AXI_PWMGEN_REG_CORE_MAGIC, &val); +	if (ret) +		return ret; + +	if (val != AXI_PWMGEN_REG_CORE_MAGIC_VAL) +		return dev_err_probe(dev, -ENODEV, +			"failed to read expected value from register: got %08x, expected %08x\n", +			val, AXI_PWMGEN_REG_CORE_MAGIC_VAL); + +	ret = regmap_read(regmap, AXI_PWMGEN_REG_CORE_VERSION, &val); +	if (ret) +		return ret; + +	if (ADI_AXI_PCORE_VER_MAJOR(val) != 2) { +		return dev_err_probe(dev, -ENODEV, "Unsupported peripheral version %u.%u.%u\n", +			ADI_AXI_PCORE_VER_MAJOR(val), +			ADI_AXI_PCORE_VER_MINOR(val), +			ADI_AXI_PCORE_VER_PATCH(val)); +	} + +	/* Enable the core */ +	ret = regmap_clear_bits(regmap, AXI_PWMGEN_REG_CONFIG, AXI_PWMGEN_REG_CONFIG_RESET); +	if (ret) +		return ret; + +	ret = regmap_read(regmap, AXI_PWMGEN_REG_NPWM, &val); +	if (ret) +		return ret; + +	/* Return the number of PWMs */ +	return val; +} + +static int axi_pwmgen_probe(struct platform_device *pdev) +{ +	struct device *dev = &pdev->dev; +	struct regmap *regmap; +	struct pwm_chip *chip; +	struct axi_pwmgen_ddata *ddata; +	struct clk *clk; +	void __iomem *io_base; +	int ret; + +	io_base = devm_platform_ioremap_resource(pdev, 0); +	if (IS_ERR(io_base)) +		return PTR_ERR(io_base); + +	regmap = devm_regmap_init_mmio(dev, io_base, &axi_pwmgen_regmap_config); +	if (IS_ERR(regmap)) +		return dev_err_probe(dev, PTR_ERR(regmap), +				     "failed to init register map\n"); + +	ret = axi_pwmgen_setup(regmap, dev); +	if (ret < 0) +		return ret; + +	chip = devm_pwmchip_alloc(dev, ret, sizeof(*ddata)); +	if (IS_ERR(chip)) +		return PTR_ERR(chip); +	ddata = pwmchip_get_drvdata(chip); +	ddata->regmap = regmap; + +	clk = devm_clk_get_enabled(dev, NULL); +	if (IS_ERR(clk)) +		return dev_err_probe(dev, PTR_ERR(clk), "failed to get clock\n"); + +	ret = devm_clk_rate_exclusive_get(dev, clk); +	if (ret) +		return dev_err_probe(dev, ret, "failed to get exclusive rate\n"); + +	ddata->clk_rate_hz = clk_get_rate(clk); +	if (!ddata->clk_rate_hz || ddata->clk_rate_hz > NSEC_PER_SEC) +		return dev_err_probe(dev, -EINVAL, +				     "Invalid clock rate: %lu\n", ddata->clk_rate_hz); + +	chip->ops = &axi_pwmgen_pwm_ops; +	chip->atomic = true; + +	ret = devm_pwmchip_add(dev, chip); +	if (ret) +		return dev_err_probe(dev, ret, "could not add PWM chip\n"); + +	return 0; +} + +static const struct of_device_id axi_pwmgen_ids[] = { +	{ .compatible = "adi,axi-pwmgen-2.00.a" }, +	{ } +}; +MODULE_DEVICE_TABLE(of, axi_pwmgen_ids); + +static struct platform_driver axi_pwmgen_driver = { +	.driver = { +		.name = "axi-pwmgen", +		.of_match_table = axi_pwmgen_ids, +	}, +	.probe = axi_pwmgen_probe, +}; +module_platform_driver(axi_pwmgen_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Sergiu Cuciurean <[email protected]>"); +MODULE_AUTHOR("Trevor Gamblin <[email protected]>"); +MODULE_DESCRIPTION("Driver for the Analog Devices AXI PWM generator"); diff --git a/drivers/pwm/pwm-cros-ec.c b/drivers/pwm/pwm-cros-ec.c index 606ccfdaf4cc..189301dc395e 100644 --- a/drivers/pwm/pwm-cros-ec.c +++ b/drivers/pwm/pwm-cros-ec.c @@ -20,20 +20,10 @@   *   * @ec: Pointer to EC device   * @use_pwm_type: Use PWM types instead of generic channels - * @channel: array with per-channel data   */  struct cros_ec_pwm_device {  	struct cros_ec_device *ec;  	bool use_pwm_type; -	struct cros_ec_pwm *channel; -}; - -/** - * struct cros_ec_pwm - per-PWM driver data - * @duty_cycle: cached duty cycle - */ -struct cros_ec_pwm { -	u16 duty_cycle;  };  static inline struct cros_ec_pwm_device *pwm_to_cros_ec_pwm(struct pwm_chip *chip) @@ -135,7 +125,6 @@ static int cros_ec_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,  			     const struct pwm_state *state)  {  	struct cros_ec_pwm_device *ec_pwm = pwm_to_cros_ec_pwm(chip); -	struct cros_ec_pwm *channel = &ec_pwm->channel[pwm->hwpwm];  	u16 duty_cycle;  	int ret; @@ -156,8 +145,6 @@ static int cros_ec_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,  	if (ret < 0)  		return ret; -	channel->duty_cycle = state->duty_cycle; -  	return 0;  } @@ -165,7 +152,6 @@ static int cros_ec_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm,  				 struct pwm_state *state)  {  	struct cros_ec_pwm_device *ec_pwm = pwm_to_cros_ec_pwm(chip); -	struct cros_ec_pwm *channel = &ec_pwm->channel[pwm->hwpwm];  	int ret;  	ret = cros_ec_pwm_get_duty(ec_pwm->ec, ec_pwm->use_pwm_type, pwm->hwpwm); @@ -175,44 +161,13 @@ static int cros_ec_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm,  	}  	state->enabled = (ret > 0); +	state->duty_cycle = ret;  	state->period = EC_PWM_MAX_DUTY;  	state->polarity = PWM_POLARITY_NORMAL; -	/* -	 * Note that "disabled" and "duty cycle == 0" are treated the same. If -	 * the cached duty cycle is not zero, used the cached duty cycle. This -	 * ensures that the configured duty cycle is kept across a disable and -	 * enable operation and avoids potentially confusing consumers. -	 * -	 * For the case of the initial hardware readout, channel->duty_cycle -	 * will be 0 and the actual duty cycle read from the EC is used. -	 */ -	if (ret == 0 && channel->duty_cycle > 0) -		state->duty_cycle = channel->duty_cycle; -	else -		state->duty_cycle = ret; -  	return 0;  } -static struct pwm_device * -cros_ec_pwm_xlate(struct pwm_chip *chip, const struct of_phandle_args *args) -{ -	struct pwm_device *pwm; - -	if (args->args[0] >= chip->npwm) -		return ERR_PTR(-EINVAL); - -	pwm = pwm_request_from_chip(chip, args->args[0], NULL); -	if (IS_ERR(pwm)) -		return pwm; - -	/* The EC won't let us change the period */ -	pwm->args.period = EC_PWM_MAX_DUTY; - -	return pwm; -} -  static const struct pwm_ops cros_ec_pwm_ops = {  	.get_state	= cros_ec_pwm_get_state,  	.apply		= cros_ec_pwm_apply, @@ -263,7 +218,7 @@ static int cros_ec_pwm_probe(struct platform_device *pdev)  	struct cros_ec_pwm_device *ec_pwm;  	struct pwm_chip *chip;  	bool use_pwm_type = false; -	unsigned int npwm; +	unsigned int i, npwm;  	int ret;  	if (!ec) @@ -289,12 +244,17 @@ static int cros_ec_pwm_probe(struct platform_device *pdev)  	/* PWM chip */  	chip->ops = &cros_ec_pwm_ops; -	chip->of_xlate = cros_ec_pwm_xlate; -	ec_pwm->channel = devm_kcalloc(dev, chip->npwm, sizeof(*ec_pwm->channel), -					GFP_KERNEL); -	if (!ec_pwm->channel) -		return -ENOMEM; +	/* +	 * The device tree binding for this device is special as it only uses a +	 * single cell (for the hwid) and so doesn't provide a default period. +	 * This isn't a big problem though as the hardware only supports a +	 * single period length, it's just a bit ugly to make this fit into the +	 * pwm core abstractions. So initialize the period here, as +	 * of_pwm_xlate_with_flags() won't do that for us. +	 */ +	for (i = 0; i < npwm; ++i) +		chip->pwms[i].args.period = EC_PWM_MAX_DUTY;  	dev_dbg(dev, "Probed %u PWMs\n", chip->npwm); diff --git a/drivers/pwm/pwm-gpio.c b/drivers/pwm/pwm-gpio.c new file mode 100644 index 000000000000..9f8884ac7504 --- /dev/null +++ b/drivers/pwm/pwm-gpio.c @@ -0,0 +1,241 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Generic software PWM for modulating GPIOs + * + * Copyright (C) 2020 Axis Communications AB + * Copyright (C) 2020 Nicola Di Lieto + * Copyright (C) 2024 Stefan Wahren + * Copyright (C) 2024 Linus Walleij + */ + +#include <linux/cleanup.h> +#include <linux/container_of.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/gpio/consumer.h> +#include <linux/hrtimer.h> +#include <linux/math.h> +#include <linux/module.h> +#include <linux/mod_devicetable.h> +#include <linux/platform_device.h> +#include <linux/property.h> +#include <linux/pwm.h> +#include <linux/spinlock.h> +#include <linux/time.h> +#include <linux/types.h> + +struct pwm_gpio { +	struct hrtimer gpio_timer; +	struct gpio_desc *gpio; +	struct pwm_state state; +	struct pwm_state next_state; + +	/* Protect internal state between pwm_ops and hrtimer */ +	spinlock_t lock; + +	bool changing; +	bool running; +	bool level; +}; + +static void pwm_gpio_round(struct pwm_state *dest, const struct pwm_state *src) +{ +	u64 dividend; +	u32 remainder; + +	*dest = *src; + +	/* Round down to hrtimer resolution */ +	dividend = dest->period; +	remainder = do_div(dividend, hrtimer_resolution); +	dest->period -= remainder; + +	dividend = dest->duty_cycle; +	remainder = do_div(dividend, hrtimer_resolution); +	dest->duty_cycle -= remainder; +} + +static u64 pwm_gpio_toggle(struct pwm_gpio *gpwm, bool level) +{ +	const struct pwm_state *state = &gpwm->state; +	bool invert = state->polarity == PWM_POLARITY_INVERSED; + +	gpwm->level = level; +	gpiod_set_value(gpwm->gpio, gpwm->level ^ invert); + +	if (!state->duty_cycle || state->duty_cycle == state->period) { +		gpwm->running = false; +		return 0; +	} + +	gpwm->running = true; +	return level ? state->duty_cycle : state->period - state->duty_cycle; +} + +static enum hrtimer_restart pwm_gpio_timer(struct hrtimer *gpio_timer) +{ +	struct pwm_gpio *gpwm = container_of(gpio_timer, struct pwm_gpio, +					     gpio_timer); +	u64 next_toggle; +	bool new_level; + +	guard(spinlock_irqsave)(&gpwm->lock); + +	/* Apply new state at end of current period */ +	if (!gpwm->level && gpwm->changing) { +		gpwm->changing = false; +		gpwm->state = gpwm->next_state; +		new_level = !!gpwm->state.duty_cycle; +	} else { +		new_level = !gpwm->level; +	} + +	next_toggle = pwm_gpio_toggle(gpwm, new_level); +	if (next_toggle) +		hrtimer_forward(gpio_timer, hrtimer_get_expires(gpio_timer), +				ns_to_ktime(next_toggle)); + +	return next_toggle ? HRTIMER_RESTART : HRTIMER_NORESTART; +} + +static int pwm_gpio_apply(struct pwm_chip *chip, struct pwm_device *pwm, +			  const struct pwm_state *state) +{ +	struct pwm_gpio *gpwm = pwmchip_get_drvdata(chip); +	bool invert = state->polarity == PWM_POLARITY_INVERSED; + +	if (state->duty_cycle && state->duty_cycle < hrtimer_resolution) +		return -EINVAL; + +	if (state->duty_cycle != state->period && +	    (state->period - state->duty_cycle < hrtimer_resolution)) +		return -EINVAL; + +	if (!state->enabled) { +		hrtimer_cancel(&gpwm->gpio_timer); +	} else if (!gpwm->running) { +		int ret; + +		/* +		 * This just enables the output, but pwm_gpio_toggle() +		 * really starts the duty cycle. +		 */ +		ret = gpiod_direction_output(gpwm->gpio, invert); +		if (ret) +			return ret; +	} + +	guard(spinlock_irqsave)(&gpwm->lock); + +	if (!state->enabled) { +		pwm_gpio_round(&gpwm->state, state); +		gpwm->running = false; +		gpwm->changing = false; + +		gpiod_set_value(gpwm->gpio, invert); +	} else if (gpwm->running) { +		pwm_gpio_round(&gpwm->next_state, state); +		gpwm->changing = true; +	} else { +		unsigned long next_toggle; + +		pwm_gpio_round(&gpwm->state, state); +		gpwm->changing = false; + +		next_toggle = pwm_gpio_toggle(gpwm, !!state->duty_cycle); +		if (next_toggle) +			hrtimer_start(&gpwm->gpio_timer, next_toggle, +				      HRTIMER_MODE_REL); +	} + +	return 0; +} + +static int pwm_gpio_get_state(struct pwm_chip *chip, struct pwm_device *pwm, +			       struct pwm_state *state) +{ +	struct pwm_gpio *gpwm = pwmchip_get_drvdata(chip); + +	guard(spinlock_irqsave)(&gpwm->lock); + +	if (gpwm->changing) +		*state = gpwm->next_state; +	else +		*state = gpwm->state; + +	return 0; +} + +static const struct pwm_ops pwm_gpio_ops = { +	.apply = pwm_gpio_apply, +	.get_state = pwm_gpio_get_state, +}; + +static void pwm_gpio_disable_hrtimer(void *data) +{ +	struct pwm_gpio *gpwm = data; + +	hrtimer_cancel(&gpwm->gpio_timer); +} + +static int pwm_gpio_probe(struct platform_device *pdev) +{ +	struct device *dev = &pdev->dev; +	struct pwm_chip *chip; +	struct pwm_gpio *gpwm; +	int ret; + +	chip = devm_pwmchip_alloc(dev, 1, sizeof(*gpwm)); +	if (IS_ERR(chip)) +		return PTR_ERR(chip); + +	gpwm = pwmchip_get_drvdata(chip); + +	spin_lock_init(&gpwm->lock); + +	gpwm->gpio = devm_gpiod_get(dev, NULL, GPIOD_ASIS); +	if (IS_ERR(gpwm->gpio)) +		return dev_err_probe(dev, PTR_ERR(gpwm->gpio), +				     "%pfw: could not get gpio\n", +				     dev_fwnode(dev)); + +	if (gpiod_cansleep(gpwm->gpio)) +		return dev_err_probe(dev, -EINVAL, +				     "%pfw: sleeping GPIO not supported\n", +				     dev_fwnode(dev)); + +	chip->ops = &pwm_gpio_ops; +	chip->atomic = true; + +	hrtimer_init(&gpwm->gpio_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); +	ret = devm_add_action_or_reset(dev, pwm_gpio_disable_hrtimer, gpwm); +	if (ret) +		return ret; + +	gpwm->gpio_timer.function = pwm_gpio_timer; + +	ret = pwmchip_add(chip); +	if (ret < 0) +		return dev_err_probe(dev, ret, "could not add pwmchip\n"); + +	return 0; +} + +static const struct of_device_id pwm_gpio_dt_ids[] = { +	{ .compatible = "pwm-gpio" }, +	{ /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, pwm_gpio_dt_ids); + +static struct platform_driver pwm_gpio_driver = { +	.driver = { +		.name = "pwm-gpio", +		.of_match_table = pwm_gpio_dt_ids, +	}, +	.probe = pwm_gpio_probe, +}; +module_platform_driver(pwm_gpio_driver); + +MODULE_DESCRIPTION("PWM GPIO driver"); +MODULE_AUTHOR("Vincent Whitchurch"); +MODULE_LICENSE("GPL"); diff --git a/drivers/pwm/pwm-imx-tpm.c b/drivers/pwm/pwm-imx-tpm.c index c50ddbac43c8..96ea343856f0 100644 --- a/drivers/pwm/pwm-imx-tpm.c +++ b/drivers/pwm/pwm-imx-tpm.c @@ -20,6 +20,7 @@  #include <linux/io.h>  #include <linux/module.h>  #include <linux/of.h> +#include <linux/pinctrl/consumer.h>  #include <linux/platform_device.h>  #include <linux/pwm.h>  #include <linux/slab.h> @@ -380,6 +381,7 @@ static int pwm_imx_tpm_probe(struct platform_device *pdev)  static int pwm_imx_tpm_suspend(struct device *dev)  {  	struct imx_tpm_pwm_chip *tpm = dev_get_drvdata(dev); +	int ret;  	if (tpm->enable_count > 0)  		return -EBUSY; @@ -393,7 +395,11 @@ static int pwm_imx_tpm_suspend(struct device *dev)  	clk_disable_unprepare(tpm->clk); -	return 0; +	ret = pinctrl_pm_select_sleep_state(dev); +	if (ret) +		clk_prepare_enable(tpm->clk); + +	return ret;  }  static int pwm_imx_tpm_resume(struct device *dev) @@ -401,9 +407,15 @@ static int 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); +	ret = pinctrl_pm_select_default_state(dev);  	if (ret) +		return ret; + +	ret = clk_prepare_enable(tpm->clk); +	if (ret) {  		dev_err(dev, "failed to prepare or enable clock: %d\n", ret); +		pinctrl_pm_select_sleep_state(dev); +	}  	return ret;  } diff --git a/drivers/pwm/pwm-imx1.c b/drivers/pwm/pwm-imx1.c index 1d2aae2d278f..d5535d208005 100644 --- a/drivers/pwm/pwm-imx1.c +++ b/drivers/pwm/pwm-imx1.c @@ -194,5 +194,6 @@ static struct platform_driver pwm_imx1_driver = {  };  module_platform_driver(pwm_imx1_driver); +MODULE_DESCRIPTION("i.MX1 and i.MX21 Pulse Width Modulator driver");  MODULE_LICENSE("GPL v2");  MODULE_AUTHOR("Sascha Hauer <[email protected]>"); diff --git a/drivers/pwm/pwm-imx27.c b/drivers/pwm/pwm-imx27.c index e1412116ef65..9e2bbf5b4a8c 100644 --- a/drivers/pwm/pwm-imx27.c +++ b/drivers/pwm/pwm-imx27.c @@ -352,5 +352,6 @@ static struct platform_driver imx_pwm_driver = {  };  module_platform_driver(imx_pwm_driver); +MODULE_DESCRIPTION("i.MX27 and later i.MX SoCs Pulse Width Modulator driver");  MODULE_LICENSE("GPL v2");  MODULE_AUTHOR("Sascha Hauer <[email protected]>"); diff --git a/drivers/pwm/pwm-intel-lgm.c b/drivers/pwm/pwm-intel-lgm.c index f9cc7c17c8f0..084c71a0a11b 100644 --- a/drivers/pwm/pwm-intel-lgm.c +++ b/drivers/pwm/pwm-intel-lgm.c @@ -230,4 +230,5 @@ static struct platform_driver lgm_pwm_driver = {  };  module_platform_driver(lgm_pwm_driver); +MODULE_DESCRIPTION("Intel LGM Pulse Width Modulator driver");  MODULE_LICENSE("GPL v2"); diff --git a/drivers/pwm/pwm-jz4740.c b/drivers/pwm/pwm-jz4740.c index da4bf543d357..6bdb01619380 100644 --- a/drivers/pwm/pwm-jz4740.c +++ b/drivers/pwm/pwm-jz4740.c @@ -201,12 +201,11 @@ static int jz4740_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,  	 * state instead of its inactive state.  	 */  	if ((state->polarity == PWM_POLARITY_NORMAL) ^ state->enabled) -		regmap_update_bits(jz->map, TCU_REG_TCSRc(pwm->hwpwm), -				   TCU_TCSR_PWM_INITL_HIGH, 0); +		regmap_clear_bits(jz->map, TCU_REG_TCSRc(pwm->hwpwm), +				  TCU_TCSR_PWM_INITL_HIGH);  	else -		regmap_update_bits(jz->map, TCU_REG_TCSRc(pwm->hwpwm), -				   TCU_TCSR_PWM_INITL_HIGH, -				   TCU_TCSR_PWM_INITL_HIGH); +		regmap_set_bits(jz->map, TCU_REG_TCSRc(pwm->hwpwm), +				TCU_TCSR_PWM_INITL_HIGH);  	if (state->enabled)  		jz4740_pwm_enable(chip, pwm); diff --git a/drivers/pwm/pwm-lpss-pci.c b/drivers/pwm/pwm-lpss-pci.c index 25045c229520..f7ece2809e6b 100644 --- a/drivers/pwm/pwm-lpss-pci.c +++ b/drivers/pwm/pwm-lpss-pci.c @@ -46,25 +46,6 @@ static void pwm_lpss_remove_pci(struct pci_dev *pdev)  	pm_runtime_get_sync(&pdev->dev);  } -static int pwm_lpss_runtime_suspend_pci(struct device *dev) -{ -	/* -	 * The PCI core will handle transition to D3 automatically. We only -	 * need to provide runtime PM hooks for that to happen. -	 */ -	return 0; -} - -static int pwm_lpss_runtime_resume_pci(struct device *dev) -{ -	return 0; -} - -static DEFINE_RUNTIME_DEV_PM_OPS(pwm_lpss_pci_pm, -				 pwm_lpss_runtime_suspend_pci, -				 pwm_lpss_runtime_resume_pci, -				 NULL); -  static const struct pci_device_id pwm_lpss_pci_ids[] = {  	{ PCI_VDEVICE(INTEL, 0x0ac8), (unsigned long)&pwm_lpss_bxt_info},  	{ PCI_VDEVICE(INTEL, 0x0f08), (unsigned long)&pwm_lpss_byt_info}, @@ -84,9 +65,6 @@ static struct pci_driver pwm_lpss_driver_pci = {  	.id_table = pwm_lpss_pci_ids,  	.probe = pwm_lpss_probe_pci,  	.remove = pwm_lpss_remove_pci, -	.driver = { -		.pm = pm_ptr(&pwm_lpss_pci_pm), -	},  };  module_pci_driver(pwm_lpss_driver_pci); diff --git a/drivers/pwm/pwm-lpss-platform.c b/drivers/pwm/pwm-lpss-platform.c index dbc9f5b17bdc..5130238a4567 100644 --- a/drivers/pwm/pwm-lpss-platform.c +++ b/drivers/pwm/pwm-lpss-platform.c @@ -55,14 +55,7 @@ static int pwm_lpss_probe_platform(struct platform_device *pdev)  						    DPM_FLAG_SMART_SUSPEND);  	pm_runtime_set_active(&pdev->dev); -	pm_runtime_enable(&pdev->dev); - -	return 0; -} - -static void pwm_lpss_remove_platform(struct platform_device *pdev) -{ -	pm_runtime_disable(&pdev->dev); +	return devm_pm_runtime_enable(&pdev->dev);  }  static const struct acpi_device_id pwm_lpss_acpi_match[] = { @@ -80,7 +73,6 @@ static struct platform_driver pwm_lpss_driver_platform = {  		.acpi_match_table = pwm_lpss_acpi_match,  	},  	.probe = pwm_lpss_probe_platform, -	.remove_new = pwm_lpss_remove_platform,  };  module_platform_driver(pwm_lpss_driver_platform); diff --git a/drivers/pwm/pwm-mediatek.c b/drivers/pwm/pwm-mediatek.c index 19a87873ad60..01dfa0fab80a 100644 --- a/drivers/pwm/pwm-mediatek.c +++ b/drivers/pwm/pwm-mediatek.c @@ -395,4 +395,5 @@ static struct platform_driver pwm_mediatek_driver = {  module_platform_driver(pwm_mediatek_driver);  MODULE_AUTHOR("John Crispin <[email protected]>"); +MODULE_DESCRIPTION("MediaTek general purpose Pulse Width Modulator driver");  MODULE_LICENSE("GPL v2"); diff --git a/drivers/pwm/pwm-meson.c b/drivers/pwm/pwm-meson.c index b2f97dfb01bb..98e6c1533312 100644 --- a/drivers/pwm/pwm-meson.c +++ b/drivers/pwm/pwm-meson.c @@ -460,6 +460,37 @@ static int meson_pwm_init_channels_meson8b_v2(struct pwm_chip *chip)  	return meson_pwm_init_clocks_meson8b(chip, mux_parent_data);  } +static void meson_pwm_s4_put_clk(void *data) +{ +	struct clk *clk = data; + +	clk_put(clk); +} + +static int meson_pwm_init_channels_s4(struct pwm_chip *chip) +{ +	struct device *dev = pwmchip_parent(chip); +	struct device_node *np = dev->of_node; +	struct meson_pwm *meson = to_meson_pwm(chip); +	int i, ret; + +	for (i = 0; i < MESON_NUM_PWMS; i++) { +		meson->channels[i].clk = of_clk_get(np, i); +		if (IS_ERR(meson->channels[i].clk)) +			return dev_err_probe(dev, +					     PTR_ERR(meson->channels[i].clk), +					     "Failed to get clk\n"); + +		ret = devm_add_action_or_reset(dev, meson_pwm_s4_put_clk, +					       meson->channels[i].clk); +		if (ret) +			return dev_err_probe(dev, ret, +					     "Failed to add clk_put action\n"); +	} + +	return 0; +} +  static const struct meson_pwm_data pwm_meson8b_data = {  	.parent_names = { "xtal", NULL, "fclk_div4", "fclk_div3" },  	.channels_init = meson_pwm_init_channels_meson8b_legacy, @@ -498,6 +529,10 @@ static const struct meson_pwm_data pwm_meson8_v2_data = {  	.channels_init = meson_pwm_init_channels_meson8b_v2,  }; +static const struct meson_pwm_data pwm_s4_data = { +	.channels_init = meson_pwm_init_channels_s4, +}; +  static const struct of_device_id meson_pwm_matches[] = {  	{  		.compatible = "amlogic,meson8-pwm-v2", @@ -536,6 +571,10 @@ static const struct of_device_id meson_pwm_matches[] = {  		.compatible = "amlogic,meson-g12a-ao-pwm-cd",  		.data = &pwm_g12a_ao_cd_data  	}, +	{ +		.compatible = "amlogic,meson-s4-pwm", +		.data = &pwm_s4_data +	},  	{},  };  MODULE_DEVICE_TABLE(of, meson_pwm_matches); diff --git a/drivers/pwm/pwm-pxa.c b/drivers/pwm/pwm-pxa.c index bb7bb48b2e6d..430bd6a709e9 100644 --- a/drivers/pwm/pwm-pxa.c +++ b/drivers/pwm/pwm-pxa.c @@ -208,4 +208,5 @@ static struct platform_driver pwm_driver = {  module_platform_driver(pwm_driver); +MODULE_DESCRIPTION("PXA Pulse Width Modulator driver");  MODULE_LICENSE("GPL v2"); diff --git a/drivers/pwm/pwm-samsung.c b/drivers/pwm/pwm-samsung.c index efb60c9f0cb3..7adf4f2b1049 100644 --- a/drivers/pwm/pwm-samsung.c +++ b/drivers/pwm/pwm-samsung.c @@ -644,6 +644,7 @@ static struct platform_driver pwm_samsung_driver = {  };  module_platform_driver(pwm_samsung_driver); +MODULE_DESCRIPTION("Samsung Pulse Width Modulator driver");  MODULE_LICENSE("GPL");  MODULE_AUTHOR("Tomasz Figa <[email protected]>");  MODULE_ALIAS("platform:samsung-pwm"); diff --git a/drivers/pwm/pwm-spear.c b/drivers/pwm/pwm-spear.c index 6c6f3b38c835..4f372279f313 100644 --- a/drivers/pwm/pwm-spear.c +++ b/drivers/pwm/pwm-spear.c @@ -255,6 +255,7 @@ static struct platform_driver spear_pwm_driver = {  module_platform_driver(spear_pwm_driver); +MODULE_DESCRIPTION("ST Microelectronics SPEAr Pulse Width Modulator driver");  MODULE_LICENSE("GPL");  MODULE_AUTHOR("Shiraz Hashim <[email protected]>");  MODULE_AUTHOR("Viresh Kumar <[email protected]>"); diff --git a/drivers/pwm/pwm-stm32.c b/drivers/pwm/pwm-stm32.c index 8bae3fd2b330..fd754a99cf2e 100644 --- a/drivers/pwm/pwm-stm32.c +++ b/drivers/pwm/pwm-stm32.c @@ -368,7 +368,7 @@ static int stm32_pwm_config(struct stm32_pwm *priv, unsigned int ch,  	dty = mul_u64_u64_div_u64(duty_ns, clk_get_rate(priv->clk),  				  (u64)NSEC_PER_SEC * (prescaler + 1)); -	regmap_write(priv->regmap, TIM_CCR1 + 4 * ch, dty); +	regmap_write(priv->regmap, TIM_CCRx(ch + 1), dty);  	/* Configure output mode */  	shift = (ch & 0x1) * CCMR_CHANNEL_SHIFT; @@ -390,9 +390,9 @@ static int stm32_pwm_set_polarity(struct stm32_pwm *priv, unsigned int ch,  {  	u32 mask; -	mask = TIM_CCER_CC1P << (ch * 4); +	mask = TIM_CCER_CCxP(ch + 1);  	if (priv->have_complementary_output) -		mask |= TIM_CCER_CC1NP << (ch * 4); +		mask |= TIM_CCER_CCxNP(ch + 1);  	regmap_update_bits(priv->regmap, TIM_CCER, mask,  			   polarity == PWM_POLARITY_NORMAL ? 0 : mask); @@ -410,9 +410,9 @@ static int stm32_pwm_enable(struct stm32_pwm *priv, unsigned int ch)  		return ret;  	/* Enable channel */ -	mask = TIM_CCER_CC1E << (ch * 4); +	mask = TIM_CCER_CCxE(ch + 1);  	if (priv->have_complementary_output) -		mask |= TIM_CCER_CC1NE << (ch * 4); +		mask |= TIM_CCER_CCxNE(ch);  	regmap_set_bits(priv->regmap, TIM_CCER, mask); @@ -430,9 +430,9 @@ static void stm32_pwm_disable(struct stm32_pwm *priv, unsigned int ch)  	u32 mask;  	/* Disable channel */ -	mask = TIM_CCER_CC1E << (ch * 4); +	mask = TIM_CCER_CCxE(ch + 1);  	if (priv->have_complementary_output) -		mask |= TIM_CCER_CC1NE << (ch * 4); +		mask |= TIM_CCER_CCxNE(ch + 1);  	regmap_clear_bits(priv->regmap, TIM_CCER, mask); @@ -452,8 +452,9 @@ static int stm32_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,  	enabled = pwm->state.enabled; -	if (enabled && !state->enabled) { -		stm32_pwm_disable(priv, pwm->hwpwm); +	if (!state->enabled) { +		if (enabled) +			stm32_pwm_disable(priv, pwm->hwpwm);  		return 0;  	} @@ -501,8 +502,8 @@ static int stm32_pwm_get_state(struct pwm_chip *chip,  	if (ret)  		goto out; -	state->enabled = ccer & (TIM_CCER_CC1E << (ch * 4)); -	state->polarity = (ccer & (TIM_CCER_CC1P << (ch * 4))) ? +	state->enabled = ccer & TIM_CCER_CCxE(ch + 1); +	state->polarity = (ccer & TIM_CCER_CCxP(ch + 1)) ?  			  PWM_POLARITY_INVERSED : PWM_POLARITY_NORMAL;  	ret = regmap_read(priv->regmap, TIM_PSC, &psc);  	if (ret) @@ -510,7 +511,7 @@ static int stm32_pwm_get_state(struct pwm_chip *chip,  	ret = regmap_read(priv->regmap, TIM_ARR, &arr);  	if (ret)  		goto out; -	ret = regmap_read(priv->regmap, TIM_CCR1 + 4 * ch, &ccr); +	ret = regmap_read(priv->regmap, TIM_CCRx(ch + 1), &ccr);  	if (ret)  		goto out; @@ -711,7 +712,7 @@ static int stm32_pwm_suspend(struct device *dev)  	ccer = active_channels(priv);  	for (i = 0; i < chip->npwm; i++) { -		mask = TIM_CCER_CC1E << (i * 4); +		mask = TIM_CCER_CCxE(i + 1);  		if (ccer & mask) {  			dev_err(dev, "PWM %u still in use by consumer %s\n",  				i, chip->pwms[i].label); diff --git a/drivers/pwm/pwm-visconti.c b/drivers/pwm/pwm-visconti.c index 9e55380957be..28fae4979e3f 100644 --- a/drivers/pwm/pwm-visconti.c +++ b/drivers/pwm/pwm-visconti.c @@ -170,6 +170,7 @@ static struct platform_driver visconti_pwm_driver = {  };  module_platform_driver(visconti_pwm_driver); +MODULE_DESCRIPTION("Toshiba Visconti Pulse Width Modulator driver");  MODULE_LICENSE("GPL v2");  MODULE_AUTHOR("Nobuhiro Iwamatsu <[email protected]>");  MODULE_ALIAS("platform:pwm-visconti"); diff --git a/drivers/pwm/pwm-xilinx.c b/drivers/pwm/pwm-xilinx.c index 3a7deebb0d0c..52c241982807 100644 --- a/drivers/pwm/pwm-xilinx.c +++ b/drivers/pwm/pwm-xilinx.c @@ -224,7 +224,6 @@ static int xilinx_pwm_probe(struct platform_device *pdev)  	if (IS_ERR(chip))  		return PTR_ERR(chip);  	priv = xilinx_pwm_chip_to_priv(chip); -	platform_set_drvdata(pdev, chip);  	regs = devm_platform_ioremap_resource(pdev, 0);  	if (IS_ERR(regs)) @@ -263,37 +262,24 @@ static int xilinx_pwm_probe(struct platform_device *pdev)  	 * alas, such properties are not allowed to be used.  	 */ -	priv->clk = devm_clk_get(dev, "s_axi_aclk"); +	priv->clk = devm_clk_get_enabled(dev, "s_axi_aclk");  	if (IS_ERR(priv->clk))  		return dev_err_probe(dev, PTR_ERR(priv->clk),  				     "Could not get clock\n"); -	ret = clk_prepare_enable(priv->clk); +	ret = devm_clk_rate_exclusive_get(dev, priv->clk);  	if (ret) -		return dev_err_probe(dev, ret, "Clock enable failed\n"); -	clk_rate_exclusive_get(priv->clk); +		return dev_err_probe(dev, ret, +				     "Failed to lock clock rate\n");  	chip->ops = &xilinx_pwm_ops; -	ret = pwmchip_add(chip); -	if (ret) { -		clk_rate_exclusive_put(priv->clk); -		clk_disable_unprepare(priv->clk); +	ret = devm_pwmchip_add(dev, chip); +	if (ret)  		return dev_err_probe(dev, ret, "Could not register PWM chip\n"); -	}  	return 0;  } -static void xilinx_pwm_remove(struct platform_device *pdev) -{ -	struct pwm_chip *chip = platform_get_drvdata(pdev); -	struct xilinx_timer_priv *priv = xilinx_pwm_chip_to_priv(chip); - -	pwmchip_remove(chip); -	clk_rate_exclusive_put(priv->clk); -	clk_disable_unprepare(priv->clk); -} -  static const struct of_device_id xilinx_pwm_of_match[] = {  	{ .compatible = "xlnx,xps-timer-1.00.a", },  	{}, @@ -302,7 +288,6 @@ MODULE_DEVICE_TABLE(of, xilinx_pwm_of_match);  static struct platform_driver xilinx_pwm_driver = {  	.probe = xilinx_pwm_probe, -	.remove_new = xilinx_pwm_remove,  	.driver = {  		.name = "xilinx-pwm",  		.of_match_table = of_match_ptr(xilinx_pwm_of_match), diff --git a/include/linux/mfd/stm32-timers.h b/include/linux/mfd/stm32-timers.h index 9eb17481b07f..f09ba598c97a 100644 --- a/include/linux/mfd/stm32-timers.h +++ b/include/linux/mfd/stm32-timers.h @@ -12,97 +12,106 @@  #include <linux/dma-mapping.h>  #include <linux/regmap.h> -#define TIM_CR1		0x00	/* Control Register 1      */ -#define TIM_CR2		0x04	/* Control Register 2      */ -#define TIM_SMCR	0x08	/* Slave mode control reg  */ -#define TIM_DIER	0x0C	/* DMA/interrupt register  */ -#define TIM_SR		0x10	/* Status register	   */ -#define TIM_EGR		0x14	/* Event Generation Reg    */ -#define TIM_CCMR1	0x18	/* Capt/Comp 1 Mode Reg    */ -#define TIM_CCMR2	0x1C	/* Capt/Comp 2 Mode Reg    */ -#define TIM_CCER	0x20	/* Capt/Comp Enable Reg    */ -#define TIM_CNT		0x24	/* Counter		   */ -#define TIM_PSC		0x28	/* Prescaler               */ -#define TIM_ARR		0x2c	/* Auto-Reload Register    */ -#define TIM_CCR1	0x34	/* Capt/Comp Register 1    */ -#define TIM_CCR2	0x38	/* Capt/Comp Register 2    */ -#define TIM_CCR3	0x3C	/* Capt/Comp Register 3    */ -#define TIM_CCR4	0x40	/* Capt/Comp Register 4    */ -#define TIM_BDTR	0x44	/* Break and Dead-Time Reg */ -#define TIM_DCR		0x48	/* DMA control register    */ -#define TIM_DMAR	0x4C	/* DMA register for transfer */ -#define TIM_TISEL	0x68	/* Input Selection         */ +#define TIM_CR1		0x00			/* Control Register 1			*/ +#define TIM_CR2		0x04			/* Control Register 2			*/ +#define TIM_SMCR	0x08			/* Slave mode control reg		*/ +#define TIM_DIER	0x0C			/* DMA/interrupt register		*/ +#define TIM_SR		0x10			/* Status register			*/ +#define TIM_EGR		0x14			/* Event Generation Reg			*/ +#define TIM_CCMR1	0x18			/* Capt/Comp 1 Mode Reg			*/ +#define TIM_CCMR2	0x1C			/* Capt/Comp 2 Mode Reg			*/ +#define TIM_CCER	0x20			/* Capt/Comp Enable Reg			*/ +#define TIM_CNT		0x24			/* Counter				*/ +#define TIM_PSC		0x28			/* Prescaler				*/ +#define TIM_ARR		0x2c			/* Auto-Reload Register			*/ +#define TIM_CCRx(x)	(0x34 + 4 * ((x) - 1))	/* Capt/Comp Register x (x ∈ {1, .. 4})	*/ +#define TIM_CCR1	TIM_CCRx(1)		/* Capt/Comp Register 1			*/ +#define TIM_CCR2	TIM_CCRx(2)		/* Capt/Comp Register 2			*/ +#define TIM_CCR3	TIM_CCRx(3)		/* Capt/Comp Register 3			*/ +#define TIM_CCR4	TIM_CCRx(4)		/* Capt/Comp Register 4			*/ +#define TIM_BDTR	0x44			/* Break and Dead-Time Reg		*/ +#define TIM_DCR		0x48			/* DMA control register			*/ +#define TIM_DMAR	0x4C			/* DMA register for transfer		*/ +#define TIM_TISEL	0x68			/* Input Selection			*/ -#define TIM_CR1_CEN	BIT(0)	/* Counter Enable	   */ -#define TIM_CR1_DIR	BIT(4)  /* Counter Direction	   */ -#define TIM_CR1_ARPE	BIT(7)	/* Auto-reload Preload Ena */ -#define TIM_CR2_MMS	(BIT(4) | BIT(5) | BIT(6)) /* Master mode selection */ -#define TIM_CR2_MMS2	GENMASK(23, 20) /* Master mode selection 2 */ -#define TIM_SMCR_SMS	(BIT(0) | BIT(1) | BIT(2)) /* Slave mode selection */ -#define TIM_SMCR_TS	(BIT(4) | BIT(5) | BIT(6)) /* Trigger selection */ -#define TIM_DIER_UIE	BIT(0)	/* Update interrupt	   */ -#define TIM_DIER_CC1IE	BIT(1)  /* CC1 Interrupt Enable    */ -#define TIM_DIER_CC2IE	BIT(2)  /* CC2 Interrupt Enable    */ -#define TIM_DIER_CC3IE	BIT(3)  /* CC3 Interrupt Enable    */ -#define TIM_DIER_CC4IE	BIT(4)  /* CC4 Interrupt Enable    */ -#define TIM_DIER_CC_IE(x)	BIT((x) + 1) /* CC1, CC2, CC3, CC4 interrupt enable */ -#define TIM_DIER_UDE	BIT(8)  /* Update DMA request Enable */ -#define TIM_DIER_CC1DE	BIT(9)  /* CC1 DMA request Enable  */ -#define TIM_DIER_CC2DE	BIT(10) /* CC2 DMA request Enable  */ -#define TIM_DIER_CC3DE	BIT(11) /* CC3 DMA request Enable  */ -#define TIM_DIER_CC4DE	BIT(12) /* CC4 DMA request Enable  */ -#define TIM_DIER_COMDE	BIT(13) /* COM DMA request Enable  */ -#define TIM_DIER_TDE	BIT(14) /* Trigger DMA request Enable */ -#define TIM_SR_UIF	BIT(0)	/* Update interrupt flag   */ -#define TIM_SR_CC_IF(x)	BIT((x) + 1) /* CC1, CC2, CC3, CC4 interrupt flag */ -#define TIM_EGR_UG	BIT(0)	/* Update Generation       */ -#define TIM_CCMR_PE	BIT(3)	/* Channel Preload Enable  */ -#define TIM_CCMR_M1	(BIT(6) | BIT(5))  /* Channel PWM Mode 1 */ -#define TIM_CCMR_CC1S		(BIT(0) | BIT(1)) /* Capture/compare 1 sel */ -#define TIM_CCMR_IC1PSC		GENMASK(3, 2)	/* Input capture 1 prescaler */ -#define TIM_CCMR_CC2S		(BIT(8) | BIT(9)) /* Capture/compare 2 sel */ -#define TIM_CCMR_IC2PSC		GENMASK(11, 10)	/* Input capture 2 prescaler */ -#define TIM_CCMR_CC1S_TI1	BIT(0)	/* IC1/IC3 selects TI1/TI3 */ -#define TIM_CCMR_CC1S_TI2	BIT(1)	/* IC1/IC3 selects TI2/TI4 */ -#define TIM_CCMR_CC2S_TI2	BIT(8)	/* IC2/IC4 selects TI2/TI4 */ -#define TIM_CCMR_CC2S_TI1	BIT(9)	/* IC2/IC4 selects TI1/TI3 */ -#define TIM_CCMR_CC3S		(BIT(0) | BIT(1)) /* Capture/compare 3 sel */ -#define TIM_CCMR_CC4S		(BIT(8) | BIT(9)) /* Capture/compare 4 sel */ -#define TIM_CCMR_CC3S_TI3	BIT(0)	/* IC3 selects TI3 */ -#define TIM_CCMR_CC4S_TI4	BIT(8)	/* IC4 selects TI4 */ -#define TIM_CCER_CC1E	BIT(0)	/* Capt/Comp 1  out Ena    */ -#define TIM_CCER_CC1P	BIT(1)	/* Capt/Comp 1  Polarity   */ -#define TIM_CCER_CC1NE	BIT(2)	/* Capt/Comp 1N out Ena    */ -#define TIM_CCER_CC1NP	BIT(3)	/* Capt/Comp 1N Polarity   */ -#define TIM_CCER_CC2E	BIT(4)	/* Capt/Comp 2  out Ena    */ -#define TIM_CCER_CC2P	BIT(5)	/* Capt/Comp 2  Polarity   */ -#define TIM_CCER_CC2NP	BIT(7)	/* Capt/Comp 2N Polarity   */ -#define TIM_CCER_CC3E	BIT(8)	/* Capt/Comp 3  out Ena    */ -#define TIM_CCER_CC3P	BIT(9)	/* Capt/Comp 3  Polarity   */ -#define TIM_CCER_CC3NP	BIT(11)	/* Capt/Comp 3N Polarity   */ -#define TIM_CCER_CC4E	BIT(12)	/* Capt/Comp 4  out Ena    */ -#define TIM_CCER_CC4P	BIT(13)	/* Capt/Comp 4  Polarity   */ -#define TIM_CCER_CC4NP	BIT(15)	/* Capt/Comp 4N Polarity   */ -#define TIM_CCER_CCXE	(BIT(0) | BIT(4) | BIT(8) | BIT(12)) -#define TIM_BDTR_BKE(x)	BIT(12 + (x) * 12) /* Break input enable */ -#define TIM_BDTR_BKP(x)	BIT(13 + (x) * 12) /* Break input polarity */ -#define TIM_BDTR_AOE	BIT(14)	/* Automatic Output Enable */ -#define TIM_BDTR_MOE	BIT(15)	/* Main Output Enable      */ -#define TIM_BDTR_BKF(x)	(0xf << (16 + (x) * 4)) -#define TIM_DCR_DBA	GENMASK(4, 0)	/* DMA base addr */ -#define TIM_DCR_DBL	GENMASK(12, 8)	/* DMA burst len */ +#define TIM_CR1_CEN		BIT(0)					/* Counter Enable				*/ +#define TIM_CR1_DIR		BIT(4)					/* Counter Direction				*/ +#define TIM_CR1_ARPE		BIT(7)					/* Auto-reload Preload Ena			*/ +#define TIM_CR2_MMS		(BIT(4) | BIT(5) | BIT(6))		/* Master mode selection			*/ +#define TIM_CR2_MMS2		GENMASK(23, 20)				/* Master mode selection 2			*/ +#define TIM_SMCR_SMS		(BIT(0) | BIT(1) | BIT(2))		/* Slave mode selection				*/ +#define TIM_SMCR_TS		(BIT(4) | BIT(5) | BIT(6))		/* Trigger selection				*/ +#define TIM_DIER_UIE		BIT(0)					/* Update interrupt				*/ +#define TIM_DIER_CCxIE(x)	BIT(1 + ((x) - 1))			/* CCx Interrupt Enable	(x ∈ {1, .. 4})		*/ +#define TIM_DIER_CC1IE		TIM_DIER_CCxIE(1)			/* CC1 Interrupt Enable				*/ +#define TIM_DIER_CC2IE		TIM_DIER_CCxIE(2)			/* CC2 Interrupt Enable				*/ +#define TIM_DIER_CC3IE		TIM_DIER_CCxIE(3)			/* CC3 Interrupt Enable				*/ +#define TIM_DIER_CC4IE		TIM_DIER_CCxIE(4)			/* CC4 Interrupt Enable				*/ +#define TIM_DIER_UDE		BIT(8)					/* Update DMA request Enable			*/ +#define TIM_DIER_CCxDE(x)	BIT(9 + ((x) - 1))			/* CCx DMA request Enable (x ∈ {1, .. 4})	*/ +#define TIM_DIER_CC1DE		TIM_DIER_CCxDE(1)			/* CC1 DMA request Enable			*/ +#define TIM_DIER_CC2DE		TIM_DIER_CCxDE(2)			/* CC2 DMA request Enable			*/ +#define TIM_DIER_CC3DE		TIM_DIER_CCxDE(3)			/* CC3 DMA request Enable			*/ +#define TIM_DIER_CC4DE		TIM_DIER_CCxDE(4)			/* CC4 DMA request Enable			*/ +#define TIM_DIER_COMDE		BIT(13)					/* COM DMA request Enable			*/ +#define TIM_DIER_TDE		BIT(14)					/* Trigger DMA request Enable			*/ +#define TIM_SR_UIF		BIT(0)					/* Update interrupt flag			*/ +#define TIM_SR_CC_IF(x)		BIT((x) + 1)				/* CC1, CC2, CC3, CC4 interrupt flag		*/ +#define TIM_EGR_UG		BIT(0)					/* Update Generation				*/ +#define TIM_CCMR_PE		BIT(3)					/* Channel Preload Enable			*/ +#define TIM_CCMR_M1		(BIT(6) | BIT(5))			/* Channel PWM Mode 1				*/ +#define TIM_CCMR_CC1S		(BIT(0) | BIT(1))			/* Capture/compare 1 sel			*/ +#define TIM_CCMR_IC1PSC		GENMASK(3, 2)				/* Input capture 1 prescaler			*/ +#define TIM_CCMR_CC2S		(BIT(8) | BIT(9))			/* Capture/compare 2 sel			*/ +#define TIM_CCMR_IC2PSC		GENMASK(11, 10)				/* Input capture 2 prescaler			*/ +#define TIM_CCMR_CC1S_TI1	BIT(0)					/* IC1/IC3 selects TI1/TI3			*/ +#define TIM_CCMR_CC1S_TI2	BIT(1)					/* IC1/IC3 selects TI2/TI4			*/ +#define TIM_CCMR_CC2S_TI2	BIT(8)					/* IC2/IC4 selects TI2/TI4			*/ +#define TIM_CCMR_CC2S_TI1	BIT(9)					/* IC2/IC4 selects TI1/TI3			*/ +#define TIM_CCMR_CC3S		(BIT(0) | BIT(1))			/* Capture/compare 3 sel			*/ +#define TIM_CCMR_CC4S		(BIT(8) | BIT(9))			/* Capture/compare 4 sel			*/ +#define TIM_CCMR_CC3S_TI3	BIT(0)					/* IC3 selects TI3				*/ +#define TIM_CCMR_CC4S_TI4	BIT(8)					/* IC4 selects TI4				*/ +#define TIM_CCER_CCxE(x)	BIT(0 + 4 * ((x) - 1))			/* Capt/Comp x  out Ena (x ∈ {1, .. 4})		*/ +#define TIM_CCER_CCxP(x)	BIT(1 + 4 * ((x) - 1))			/* Capt/Comp x  Polarity (x ∈ {1, .. 4})	*/ +#define TIM_CCER_CCxNE(x)	BIT(2 + 4 * ((x) - 1))			/* Capt/Comp xN out Ena (x ∈ {1, .. 4})		*/ +#define TIM_CCER_CCxNP(x)	BIT(3 + 4 * ((x) - 1))			/* Capt/Comp xN Polarity (x ∈ {1, .. 4})	*/ +#define TIM_CCER_CC1E		TIM_CCER_CCxE(1)			/* Capt/Comp 1  out Ena				*/ +#define TIM_CCER_CC1P		TIM_CCER_CCxP(1)			/* Capt/Comp 1  Polarity			*/ +#define TIM_CCER_CC1NE		TIM_CCER_CCxNE(1)			/* Capt/Comp 1N out Ena				*/ +#define TIM_CCER_CC1NP		TIM_CCER_CCxNP(1)			/* Capt/Comp 1N Polarity			*/ +#define TIM_CCER_CC2E		TIM_CCER_CCxE(2)			/* Capt/Comp 2  out Ena				*/ +#define TIM_CCER_CC2P		TIM_CCER_CCxP(2)			/* Capt/Comp 2  Polarity			*/ +#define TIM_CCER_CC2NE		TIM_CCER_CCxNE(2)			/* Capt/Comp 2N out Ena				*/ +#define TIM_CCER_CC2NP		TIM_CCER_CCxNP(2)			/* Capt/Comp 2N Polarity			*/ +#define TIM_CCER_CC3E		TIM_CCER_CCxE(3)			/* Capt/Comp 3  out Ena				*/ +#define TIM_CCER_CC3P		TIM_CCER_CCxP(3)			/* Capt/Comp 3  Polarity			*/ +#define TIM_CCER_CC3NE		TIM_CCER_CCxNE(3)			/* Capt/Comp 3N out Ena				*/ +#define TIM_CCER_CC3NP		TIM_CCER_CCxNP(3)			/* Capt/Comp 3N Polarity			*/ +#define TIM_CCER_CC4E		TIM_CCER_CCxE(4)			/* Capt/Comp 4  out Ena				*/ +#define TIM_CCER_CC4P		TIM_CCER_CCxP(4)			/* Capt/Comp 4  Polarity			*/ +#define TIM_CCER_CC4NE		TIM_CCER_CCxNE(4)			/* Capt/Comp 4N out Ena				*/ +#define TIM_CCER_CC4NP		TIM_CCER_CCxNP(4)			/* Capt/Comp 4N Polarity			*/ +#define TIM_CCER_CCXE		(BIT(0) | BIT(4) | BIT(8) | BIT(12)) +#define TIM_BDTR_BKE(x)		BIT(12 + (x) * 12)			/* Break input enable				*/ +#define TIM_BDTR_BKP(x)		BIT(13 + (x) * 12)			/* Break input polarity				*/ +#define TIM_BDTR_AOE		BIT(14)					/* Automatic Output Enable			*/ +#define TIM_BDTR_MOE		BIT(15)					/* Main Output Enable				*/ +#define TIM_BDTR_BKF(x)		(0xf << (16 + (x) * 4)) +#define TIM_DCR_DBA		GENMASK(4, 0)				/* DMA base addr				*/ +#define TIM_DCR_DBL		GENMASK(12, 8)				/* DMA burst len				*/ -#define MAX_TIM_PSC		0xFFFF -#define MAX_TIM_ICPSC		0x3 -#define TIM_CR2_MMS_SHIFT	4 -#define TIM_CR2_MMS2_SHIFT	20 +#define MAX_TIM_PSC				0xFFFF +#define MAX_TIM_ICPSC				0x3 +#define TIM_CR2_MMS_SHIFT			4 +#define TIM_CR2_MMS2_SHIFT			20  #define TIM_SMCR_SMS_SLAVE_MODE_DISABLED	0 /* counts on internal clock when CEN=1 */  #define TIM_SMCR_SMS_ENCODER_MODE_1		1 /* counts TI1FP1 edges, depending on TI2FP2 level */  #define TIM_SMCR_SMS_ENCODER_MODE_2		2 /* counts TI2FP2 edges, depending on TI1FP1 level */  #define TIM_SMCR_SMS_ENCODER_MODE_3		3 /* counts on both TI1FP1 and TI2FP2 edges */ -#define TIM_SMCR_TS_SHIFT	4 -#define TIM_BDTR_BKF_MASK	0xF -#define TIM_BDTR_BKF_SHIFT(x)	(16 + (x) * 4) +#define TIM_SMCR_TS_SHIFT			4 +#define TIM_BDTR_BKF_MASK			0xF +#define TIM_BDTR_BKF_SHIFT(x)			(16 + (x) * 4)  enum stm32_timers_dmas {  	STM32_TIMERS_DMA_CH1, diff --git a/include/linux/pwm.h b/include/linux/pwm.h index 60b92c2c75ef..f8c2dc12dbd3 100644 --- a/include/linux/pwm.h +++ b/include/linux/pwm.h @@ -4,9 +4,12 @@  #include <linux/device.h>  #include <linux/err.h> +#include <linux/module.h>  #include <linux/mutex.h>  #include <linux/of.h> +MODULE_IMPORT_NS(PWM); +  struct pwm_chip;  /** @@ -249,9 +252,7 @@ struct pwm_capture {   * @free: optional hook for freeing a PWM   * @capture: capture and report PWM signal   * @apply: atomically apply a new PWM config - * @get_state: get the current PWM state. This function is only - *	       called once per PWM device when the PWM chip is - *	       registered. + * @get_state: get the current PWM state.   */  struct pwm_ops {  	int (*request)(struct pwm_chip *chip, struct pwm_device *pwm); @@ -407,10 +408,6 @@ void pwmchip_remove(struct pwm_chip *chip);  int __devm_pwmchip_add(struct device *dev, struct pwm_chip *chip, struct module *owner);  #define devm_pwmchip_add(dev, chip) __devm_pwmchip_add(dev, chip, THIS_MODULE) -struct pwm_device *pwm_request_from_chip(struct pwm_chip *chip, -					 unsigned int index, -					 const char *label); -  struct pwm_device *of_pwm_xlate_with_flags(struct pwm_chip *chip,  		const struct of_phandle_args *args);  struct pwm_device *of_pwm_single_xlate(struct pwm_chip *chip, @@ -505,14 +502,6 @@ static inline int devm_pwmchip_add(struct device *dev, struct pwm_chip *chip)  	return -EINVAL;  } -static inline struct pwm_device *pwm_request_from_chip(struct pwm_chip *chip, -						       unsigned int index, -						       const char *label) -{ -	might_sleep(); -	return ERR_PTR(-ENODEV); -} -  static inline struct pwm_device *pwm_get(struct device *dev,  					 const char *consumer)  { @@ -574,13 +563,6 @@ static inline void pwm_apply_args(struct pwm_device *pwm)  	pwm_apply_might_sleep(pwm, &state);  } -/* only for backwards-compatibility, new code should not use this */ -static inline int pwm_apply_state(struct pwm_device *pwm, -				  const struct pwm_state *state) -{ -	return pwm_apply_might_sleep(pwm, state); -} -  struct pwm_lookup {  	struct list_head list;  	const char *provider; |