diff options
23 files changed, 2086 insertions, 212 deletions
diff --git a/Documentation/devicetree/bindings/pwm/loongson,ls7a-pwm.yaml b/Documentation/devicetree/bindings/pwm/loongson,ls7a-pwm.yaml new file mode 100644 index 000000000000..5d64fb40a0d6 --- /dev/null +++ b/Documentation/devicetree/bindings/pwm/loongson,ls7a-pwm.yaml @@ -0,0 +1,67 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pwm/loongson,ls7a-pwm.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Loongson PWM Controller + +maintainers: + - Binbin Zhou <zhoubinbin@loongson.cn> + +description: + The Loongson PWM has one pulse width output signal and one pulse input + signal to be measured. + It can be found on Loongson-2K series cpus and Loongson LS7A bridge chips. + +allOf: + - $ref: pwm.yaml# + +properties: + compatible: + oneOf: + - const: loongson,ls7a-pwm + - items: + - enum: + - loongson,ls2k0500-pwm + - loongson,ls2k1000-pwm + - loongson,ls2k2000-pwm + - const: loongson,ls7a-pwm + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + clocks: + maxItems: 1 + + '#pwm-cells': + description: + The first cell must have a value of 0, which specifies the PWM output signal; + The second cell is the period in nanoseconds; + The third cell flag supported by this binding is PWM_POLARITY_INVERTED. + const: 3 + +required: + - compatible + - reg + - interrupts + - clocks + +additionalProperties: false + +examples: + - | + #include <dt-bindings/interrupt-controller/irq.h> + #include <dt-bindings/clock/loongson,ls2k-clk.h> + + pwm@1fe22000 { + compatible = "loongson,ls2k1000-pwm", "loongson,ls7a-pwm"; + reg = <0x1fe22000 0x10>; + interrupt-parent = <&liointc0>; + interrupts = <24 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clk LOONGSON2_APB_CLK>; + #pwm-cells = <3>; + }; diff --git a/Documentation/devicetree/bindings/pwm/mediatek,pwm-disp.yaml b/Documentation/devicetree/bindings/pwm/mediatek,pwm-disp.yaml index 195e4371196b..68ef30414325 100644 --- a/Documentation/devicetree/bindings/pwm/mediatek,pwm-disp.yaml +++ b/Documentation/devicetree/bindings/pwm/mediatek,pwm-disp.yaml @@ -27,6 +27,7 @@ properties: - const: mediatek,mt8173-disp-pwm - items: - enum: + - mediatek,mt6893-disp-pwm - mediatek,mt8186-disp-pwm - mediatek,mt8188-disp-pwm - mediatek,mt8192-disp-pwm diff --git a/Documentation/devicetree/bindings/pwm/nxp,mc33xs2410.yaml b/Documentation/devicetree/bindings/pwm/nxp,mc33xs2410.yaml new file mode 100644 index 000000000000..1729fe5c3dfb --- /dev/null +++ b/Documentation/devicetree/bindings/pwm/nxp,mc33xs2410.yaml @@ -0,0 +1,118 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pwm/nxp,mc33xs2410.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: High-side switch MC33XS2410 + +maintainers: + - Dimitri Fedrau <dima.fedrau@gmail.com> + +allOf: + - $ref: pwm.yaml# + - $ref: /schemas/spi/spi-peripheral-props.yaml# + +properties: + compatible: + const: nxp,mc33xs2410 + + reg: + maxItems: 1 + + spi-max-frequency: + maximum: 10000000 + + spi-cpha: true + + spi-cs-setup-delay-ns: + minimum: 100 + default: 100 + + spi-cs-hold-delay-ns: + minimum: 10 + default: 10 + + spi-cs-inactive-delay-ns: + minimum: 300 + default: 300 + + reset-gpios: + description: + GPIO connected to the active low reset pin. + maxItems: 1 + + "#pwm-cells": + const: 3 + + pwm-names: + items: + - const: di0 + - const: di1 + - const: di2 + - const: di3 + + pwms: + description: + Direct inputs(di0-3) are used to directly turn-on or turn-off the + outputs. + maxItems: 4 + + interrupts: + maxItems: 1 + + clocks: + description: + The external clock can be used if the internal clock doesn't meet + timing requirements over temperature and voltage operating range. + maxItems: 1 + + vdd-supply: + description: + Logic supply voltage + + vspi-supply: + description: + Supply voltage for SPI + + vpwr-supply: + description: + Power switch supply + +required: + - compatible + - reg + +unevaluatedProperties: false + +examples: + - | + #include <dt-bindings/gpio/gpio.h> + #include <dt-bindings/interrupt-controller/irq.h> + spi { + #address-cells = <1>; + #size-cells = <0>; + + pwm@0 { + compatible = "nxp,mc33xs2410"; + reg = <0x0>; + spi-max-frequency = <4000000>; + spi-cpha; + spi-cs-setup-delay-ns = <100>; + spi-cs-hold-delay-ns = <10>; + spi-cs-inactive-delay-ns = <300>; + reset-gpios = <&gpio3 22 GPIO_ACTIVE_LOW>; + #pwm-cells = <3>; + pwm-names = "di0", "di1", "di2", "di3"; + pwms = <&pwm0 0 1000000>, + <&pwm1 0 1000000>, + <&pwm2 0 1000000>, + <&pwm3 0 1000000>; + interrupt-parent = <&gpio0>; + interrupts = <31 IRQ_TYPE_LEVEL_LOW>; + clocks = <&clk_ext_fixed>; + vdd-supply = <®_3v3>; + vspi-supply = <®_3v3>; + vpwr-supply = <®_24v0>; + }; + }; diff --git a/Documentation/devicetree/bindings/pwm/renesas,rzg2l-gpt.yaml b/Documentation/devicetree/bindings/pwm/renesas,rzg2l-gpt.yaml new file mode 100644 index 000000000000..13b807765a30 --- /dev/null +++ b/Documentation/devicetree/bindings/pwm/renesas,rzg2l-gpt.yaml @@ -0,0 +1,378 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pwm/renesas,rzg2l-gpt.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Renesas RZ/G2L General PWM Timer (GPT) + +maintainers: + - Biju Das <biju.das.jz@bp.renesas.com> + +description: | + RZ/G2L General PWM Timer (GPT) composed of 8 channels with 32-bit timer + (GPT32E). It supports the following functions + * 32 bits x 8 channels. + * Up-counting or down-counting (saw waves) or up/down-counting + (triangle waves) for each counter. + * Clock sources independently selectable for each channel. + * Two I/O pins per channel. + * Two output compare/input capture registers per channel. + * For the two output compare/input capture registers of each channel, + four registers are provided as buffer registers and are capable of + operating as comparison registers when buffering is not in use. + * In output compare operation, buffer switching can be at crests or + troughs, enabling the generation of laterally asymmetric PWM waveforms. + * Registers for setting up frame cycles in each channel (with capability + for generating interrupts at overflow or underflow) + * Generation of dead times in PWM operation. + * Synchronous starting, stopping and clearing counters for arbitrary + channels. + * Starting, stopping, clearing and up/down counters in response to input + level comparison. + * Starting, clearing, stopping and up/down counters in response to a + maximum of four external triggers. + * Output pin disable function by dead time error and detected + short-circuits between output pins. + * A/D converter start triggers can be generated (GPT32E0 to GPT32E3) + * Enables the noise filter for input capture and external trigger + operation. + + The below pwm channels are supported. + pwm0 - GPT32E0.GTIOC0A channel + pwm1 - GPT32E0.GTIOC0B channel + pwm2 - GPT32E1.GTIOC1A channel + pwm3 - GPT32E1.GTIOC1B channel + pwm4 - GPT32E2.GTIOC2A channel + pwm5 - GPT32E2.GTIOC2B channel + pwm6 - GPT32E3.GTIOC3A channel + pwm7 - GPT32E3.GTIOC3B channel + pwm8 - GPT32E4.GTIOC4A channel + pwm9 - GPT32E4.GTIOC4B channel + pwm10 - GPT32E5.GTIOC5A channel + pwm11 - GPT32E5.GTIOC5B channel + pwm12 - GPT32E6.GTIOC6A channel + pwm13 - GPT32E6.GTIOC6B channel + pwm14 - GPT32E7.GTIOC7A channel + pwm15 - GPT32E7.GTIOC7B channel + +properties: + compatible: + items: + - enum: + - renesas,r9a07g044-gpt # RZ/G2{L,LC} + - renesas,r9a07g054-gpt # RZ/V2L + - const: renesas,rzg2l-gpt + + reg: + maxItems: 1 + + '#pwm-cells': + const: 3 + + interrupts: + items: + - description: GPT32E0.GTCCRA input capture/compare match + - description: GPT32E0.GTCCRB input capture/compare + - description: GPT32E0.GTCCRC compare match + - description: GPT32E0.GTCCRD compare match + - description: GPT32E0.GTCCRE compare match + - description: GPT32E0.GTCCRF compare match + - description: GPT32E0.GTADTRA compare match + - description: GPT32E0.GTADTRB compare match + - description: GPT32E0.GTCNT overflow/GTPR compare match + - description: GPT32E0.GTCNT underflow + - description: GPT32E1.GTCCRA input capture/compare match + - description: GPT32E1.GTCCRB input capture/compare + - description: GPT32E1.GTCCRC compare match + - description: GPT32E1.GTCCRD compare match + - description: GPT32E1.GTCCRE compare match + - description: GPT32E1.GTCCRF compare match + - description: GPT32E1.GTADTRA compare match + - description: GPT32E1.GTADTRB compare match + - description: GPT32E1.GTCNT overflow/GTPR compare match + - description: GPT32E1.GTCNT underflow + - description: GPT32E2.GTCCRA input capture/compare match + - description: GPT32E2.GTCCRB input capture/compare + - description: GPT32E2.GTCCRC compare match + - description: GPT32E2.GTCCRD compare match + - description: GPT32E2.GTCCRE compare match + - description: GPT32E2.GTCCRF compare match + - description: GPT32E2.GTADTRA compare match + - description: GPT32E2.GTADTRB compare match + - description: GPT32E2.GTCNT overflow/GTPR compare match + - description: GPT32E2.GTCNT underflow + - description: GPT32E3.GTCCRA input capture/compare match + - description: GPT32E3.GTCCRB input capture/compare + - description: GPT32E3.GTCCRC compare match + - description: GPT32E3.GTCCRD compare match + - description: GPT32E3.GTCCRE compare match + - description: GPT32E3.GTCCRF compare match + - description: GPT32E3.GTADTRA compare match + - description: GPT32E3.GTADTRB compare match + - description: GPT32E3.GTCNT overflow/GTPR compare match + - description: GPT32E3.GTCNT underflow + - description: GPT32E4.GTCCRA input capture/compare match + - description: GPT32E4.GTCCRB input capture/compare + - description: GPT32E4.GTCCRC compare match + - description: GPT32E4.GTCCRD compare match + - description: GPT32E4.GTCCRE compare match + - description: GPT32E4.GTCCRF compare match + - description: GPT32E4.GTADTRA compare match + - description: GPT32E4.GTADTRB compare match + - description: GPT32E4.GTCNT overflow/GTPR compare match + - description: GPT32E4.GTCNT underflow + - description: GPT32E5.GTCCRA input capture/compare match + - description: GPT32E5.GTCCRB input capture/compare + - description: GPT32E5.GTCCRC compare match + - description: GPT32E5.GTCCRD compare match + - description: GPT32E5.GTCCRE compare match + - description: GPT32E5.GTCCRF compare match + - description: GPT32E5.GTADTRA compare match + - description: GPT32E5.GTADTRB compare match + - description: GPT32E5.GTCNT overflow/GTPR compare match + - description: GPT32E5.GTCNT underflow + - description: GPT32E6.GTCCRA input capture/compare match + - description: GPT32E6.GTCCRB input capture/compare + - description: GPT32E6.GTCCRC compare match + - description: GPT32E6.GTCCRD compare match + - description: GPT32E6.GTCCRE compare match + - description: GPT32E6.GTCCRF compare match + - description: GPT32E6.GTADTRA compare match + - description: GPT32E6.GTADTRB compare match + - description: GPT32E6.GTCNT overflow/GTPR compare match + - description: GPT32E6.GTCNT underflow + - description: GPT32E7.GTCCRA input capture/compare match + - description: GPT32E7.GTCCRB input capture/compare + - description: GPT32E7.GTCCRC compare match + - description: GPT32E7.GTCCRD compare match + - description: GPT32E7.GTCCRE compare match + - description: GPT32E7.GTCCRF compare match + - description: GPT32E7.GTADTRA compare match + - description: GPT32E7.GTADTRB compare match + - description: GPT32E7.GTCNT overflow/GTPR compare match + - description: GPT32E7.GTCNT underflow + + interrupt-names: + items: + - const: ccmpa0 + - const: ccmpb0 + - const: cmpc0 + - const: cmpd0 + - const: cmpe0 + - const: cmpf0 + - const: adtrga0 + - const: adtrgb0 + - const: ovf0 + - const: unf0 + - const: ccmpa1 + - const: ccmpb1 + - const: cmpc1 + - const: cmpd1 + - const: cmpe1 + - const: cmpf1 + - const: adtrga1 + - const: adtrgb1 + - const: ovf1 + - const: unf1 + - const: ccmpa2 + - const: ccmpb2 + - const: cmpc2 + - const: cmpd2 + - const: cmpe2 + - const: cmpf2 + - const: adtrga2 + - const: adtrgb2 + - const: ovf2 + - const: unf2 + - const: ccmpa3 + - const: ccmpb3 + - const: cmpc3 + - const: cmpd3 + - const: cmpe3 + - const: cmpf3 + - const: adtrga3 + - const: adtrgb3 + - const: ovf3 + - const: unf3 + - const: ccmpa4 + - const: ccmpb4 + - const: cmpc4 + - const: cmpd4 + - const: cmpe4 + - const: cmpf4 + - const: adtrga4 + - const: adtrgb4 + - const: ovf4 + - const: unf4 + - const: ccmpa5 + - const: ccmpb5 + - const: cmpc5 + - const: cmpd5 + - const: cmpe5 + - const: cmpf5 + - const: adtrga5 + - const: adtrgb5 + - const: ovf5 + - const: unf5 + - const: ccmpa6 + - const: ccmpb6 + - const: cmpc6 + - const: cmpd6 + - const: cmpe6 + - const: cmpf6 + - const: adtrga6 + - const: adtrgb6 + - const: ovf6 + - const: unf6 + - const: ccmpa7 + - const: ccmpb7 + - const: cmpc7 + - const: cmpd7 + - const: cmpe7 + - const: cmpf7 + - const: adtrga7 + - const: adtrgb7 + - const: ovf7 + - const: unf7 + + clocks: + maxItems: 1 + + power-domains: + maxItems: 1 + + resets: + maxItems: 1 + +required: + - compatible + - reg + - interrupts + - interrupt-names + - clocks + - power-domains + - resets + +allOf: + - $ref: pwm.yaml# + +additionalProperties: false + +examples: + - | + #include <dt-bindings/clock/r9a07g044-cpg.h> + #include <dt-bindings/interrupt-controller/arm-gic.h> + + gpt: pwm@10048000 { + compatible = "renesas,r9a07g044-gpt", "renesas,rzg2l-gpt"; + reg = <0x10048000 0x800>; + interrupts = <GIC_SPI 218 IRQ_TYPE_EDGE_RISING>, + <GIC_SPI 219 IRQ_TYPE_EDGE_RISING>, + <GIC_SPI 220 IRQ_TYPE_EDGE_RISING>, + <GIC_SPI 221 IRQ_TYPE_EDGE_RISING>, + <GIC_SPI 222 IRQ_TYPE_EDGE_RISING>, + <GIC_SPI 223 IRQ_TYPE_EDGE_RISING>, + <GIC_SPI 224 IRQ_TYPE_EDGE_RISING>, + <GIC_SPI 225 IRQ_TYPE_EDGE_RISING>, + <GIC_SPI 226 IRQ_TYPE_EDGE_RISING>, + <GIC_SPI 227 IRQ_TYPE_EDGE_RISING>, + <GIC_SPI 231 IRQ_TYPE_EDGE_RISING>, + <GIC_SPI 232 IRQ_TYPE_EDGE_RISING>, + <GIC_SPI 233 IRQ_TYPE_EDGE_RISING>, + <GIC_SPI 234 IRQ_TYPE_EDGE_RISING>, + <GIC_SPI 235 IRQ_TYPE_EDGE_RISING>, + <GIC_SPI 236 IRQ_TYPE_EDGE_RISING>, + <GIC_SPI 237 IRQ_TYPE_EDGE_RISING>, + <GIC_SPI 238 IRQ_TYPE_EDGE_RISING>, + <GIC_SPI 239 IRQ_TYPE_EDGE_RISING>, + <GIC_SPI 240 IRQ_TYPE_EDGE_RISING>, + <GIC_SPI 244 IRQ_TYPE_EDGE_RISING>, + <GIC_SPI 245 IRQ_TYPE_EDGE_RISING>, + <GIC_SPI 246 IRQ_TYPE_EDGE_RISING>, + <GIC_SPI 247 IRQ_TYPE_EDGE_RISING>, + <GIC_SPI 248 IRQ_TYPE_EDGE_RISING>, + <GIC_SPI 249 IRQ_TYPE_EDGE_RISING>, + <GIC_SPI 250 IRQ_TYPE_EDGE_RISING>, + <GIC_SPI 251 IRQ_TYPE_EDGE_RISING>, + <GIC_SPI 252 IRQ_TYPE_EDGE_RISING>, + <GIC_SPI 253 IRQ_TYPE_EDGE_RISING>, + <GIC_SPI 257 IRQ_TYPE_EDGE_RISING>, + <GIC_SPI 258 IRQ_TYPE_EDGE_RISING>, + <GIC_SPI 259 IRQ_TYPE_EDGE_RISING>, + <GIC_SPI 260 IRQ_TYPE_EDGE_RISING>, + <GIC_SPI 261 IRQ_TYPE_EDGE_RISING>, + <GIC_SPI 262 IRQ_TYPE_EDGE_RISING>, + <GIC_SPI 263 IRQ_TYPE_EDGE_RISING>, + <GIC_SPI 264 IRQ_TYPE_EDGE_RISING>, + <GIC_SPI 265 IRQ_TYPE_EDGE_RISING>, + <GIC_SPI 266 IRQ_TYPE_EDGE_RISING>, + <GIC_SPI 270 IRQ_TYPE_EDGE_RISING>, + <GIC_SPI 271 IRQ_TYPE_EDGE_RISING>, + <GIC_SPI 272 IRQ_TYPE_EDGE_RISING>, + <GIC_SPI 273 IRQ_TYPE_EDGE_RISING>, + <GIC_SPI 274 IRQ_TYPE_EDGE_RISING>, + <GIC_SPI 275 IRQ_TYPE_EDGE_RISING>, + <GIC_SPI 276 IRQ_TYPE_EDGE_RISING>, + <GIC_SPI 277 IRQ_TYPE_EDGE_RISING>, + <GIC_SPI 278 IRQ_TYPE_EDGE_RISING>, + <GIC_SPI 279 IRQ_TYPE_EDGE_RISING>, + <GIC_SPI 283 IRQ_TYPE_EDGE_RISING>, + <GIC_SPI 284 IRQ_TYPE_EDGE_RISING>, + <GIC_SPI 285 IRQ_TYPE_EDGE_RISING>, + <GIC_SPI 286 IRQ_TYPE_EDGE_RISING>, + <GIC_SPI 287 IRQ_TYPE_EDGE_RISING>, + <GIC_SPI 288 IRQ_TYPE_EDGE_RISING>, + <GIC_SPI 289 IRQ_TYPE_EDGE_RISING>, + <GIC_SPI 290 IRQ_TYPE_EDGE_RISING>, + <GIC_SPI 291 IRQ_TYPE_EDGE_RISING>, + <GIC_SPI 292 IRQ_TYPE_EDGE_RISING>, + <GIC_SPI 296 IRQ_TYPE_EDGE_RISING>, + <GIC_SPI 297 IRQ_TYPE_EDGE_RISING>, + <GIC_SPI 298 IRQ_TYPE_EDGE_RISING>, + <GIC_SPI 299 IRQ_TYPE_EDGE_RISING>, + <GIC_SPI 300 IRQ_TYPE_EDGE_RISING>, + <GIC_SPI 301 IRQ_TYPE_EDGE_RISING>, + <GIC_SPI 302 IRQ_TYPE_EDGE_RISING>, + <GIC_SPI 303 IRQ_TYPE_EDGE_RISING>, + <GIC_SPI 304 IRQ_TYPE_EDGE_RISING>, + <GIC_SPI 305 IRQ_TYPE_EDGE_RISING>, + <GIC_SPI 309 IRQ_TYPE_EDGE_RISING>, + <GIC_SPI 310 IRQ_TYPE_EDGE_RISING>, + <GIC_SPI 311 IRQ_TYPE_EDGE_RISING>, + <GIC_SPI 312 IRQ_TYPE_EDGE_RISING>, + <GIC_SPI 313 IRQ_TYPE_EDGE_RISING>, + <GIC_SPI 314 IRQ_TYPE_EDGE_RISING>, + <GIC_SPI 315 IRQ_TYPE_EDGE_RISING>, + <GIC_SPI 316 IRQ_TYPE_EDGE_RISING>, + <GIC_SPI 317 IRQ_TYPE_EDGE_RISING>, + <GIC_SPI 318 IRQ_TYPE_EDGE_RISING>; + interrupt-names = "ccmpa0", "ccmpb0", "cmpc0", "cmpd0", + "cmpe0", "cmpf0", "adtrga0", "adtrgb0", + "ovf0", "unf0", + "ccmpa1", "ccmpb1", "cmpc1", "cmpd1", + "cmpe1", "cmpf1", "adtrga1", "adtrgb1", + "ovf1", "unf1", + "ccmpa2", "ccmpb2", "cmpc2", "cmpd2", + "cmpe2", "cmpf2", "adtrga2", "adtrgb2", + "ovf2", "unf2", + "ccmpa3", "ccmpb3", "cmpc3", "cmpd3", + "cmpe3", "cmpf3", "adtrga3", "adtrgb3", + "ovf3", "unf3", + "ccmpa4", "ccmpb4", "cmpc4", "cmpd4", + "cmpe4", "cmpf4", "adtrga4", "adtrgb4", + "ovf4", "unf4", + "ccmpa5", "ccmpb5", "cmpc5", "cmpd5", + "cmpe5", "cmpf5", "adtrga5", "adtrgb5", + "ovf5", "unf5", + "ccmpa6", "ccmpb6", "cmpc6", "cmpd6", + "cmpe6", "cmpf6", "adtrga6", "adtrgb6", + "ovf6", "unf6", + "ccmpa7", "ccmpb7", "cmpc7", "cmpd7", + "cmpe7", "cmpf7", "adtrga7", "adtrgb7", + "ovf7", "unf7"; + clocks = <&cpg CPG_MOD R9A07G044_GPT_PCLK>; + power-domains = <&cpg>; + resets = <&cpg R9A07G044_GPT_RST_C>; + #pwm-cells = <3>; + }; diff --git a/Documentation/devicetree/bindings/pwm/via,vt8500-pwm.yaml b/Documentation/devicetree/bindings/pwm/via,vt8500-pwm.yaml new file mode 100644 index 000000000000..d9146ad715ba --- /dev/null +++ b/Documentation/devicetree/bindings/pwm/via,vt8500-pwm.yaml @@ -0,0 +1,43 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pwm/via,vt8500-pwm.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: VIA/Wondermedia VT8500/WM8xxx series SoC PWM controller + +maintainers: + - Alexey Charkov <alchark@gmail.com> + +allOf: + - $ref: pwm.yaml# + +properties: + compatible: + items: + - const: via,vt8500-pwm + + reg: + maxItems: 1 + + '#pwm-cells': + const: 3 + + clocks: + maxItems: 1 + +required: + - compatible + - reg + - clocks + +additionalProperties: false + +examples: + - | + pwm1: pwm@d8220000 { + compatible = "via,vt8500-pwm"; + reg = <0xd8220000 0x1000>; + #pwm-cells = <3>; + clocks = <&clkpwm>; + }; diff --git a/Documentation/devicetree/bindings/pwm/vt8500-pwm.txt b/Documentation/devicetree/bindings/pwm/vt8500-pwm.txt deleted file mode 100644 index 4fba93ce1985..000000000000 --- a/Documentation/devicetree/bindings/pwm/vt8500-pwm.txt +++ /dev/null @@ -1,18 +0,0 @@ -VIA/Wondermedia VT8500/WM8xxx series SoC PWM controller - -Required properties: -- compatible: should be "via,vt8500-pwm" -- 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. The only third cell flag supported by this binding is - PWM_POLARITY_INVERTED. -- clocks: phandle to the PWM source clock - -Example: - -pwm1: pwm@d8220000 { - #pwm-cells = <3>; - compatible = "via,vt8500-pwm"; - reg = <0xd8220000 0x1000>; - clocks = <&clkpwm>; -}; diff --git a/MAINTAINERS b/MAINTAINERS index 830ef5f9d864..c836bb4b223d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3435,6 +3435,7 @@ M: Krzysztof Kozlowski <krzk@kernel.org> L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Odd Fixes F: Documentation/devicetree/bindings/i2c/i2c-wmt.txt +F: Documentation/devicetree/bindings/pwm/via,vt8500-pwm.yaml F: arch/arm/boot/dts/vt8500/ F: arch/arm/mach-vt8500/ F: drivers/clocksource/timer-vt8500.c @@ -13953,6 +13954,13 @@ S: Maintained F: Documentation/devicetree/bindings/i2c/loongson,ls2x-i2c.yaml F: drivers/i2c/busses/i2c-ls2x.c +LOONGSON PWM DRIVER +M: Binbin Zhou <zhoubinbin@loongson.cn> +L: linux-pwm@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/pwm/loongson,ls7a-pwm.yaml +F: drivers/pwm/pwm-loongson.c + LOONGSON-2 SOC SERIES CLOCK DRIVER M: Yinbo Zhu <zhuyinbo@loongson.cn> L: linux-clk@vger.kernel.org diff --git a/arch/arm/configs/multi_v7_defconfig b/arch/arm/configs/multi_v7_defconfig index 96178acedad0..aca01ad6aafc 100644 --- a/arch/arm/configs/multi_v7_defconfig +++ b/arch/arm/configs/multi_v7_defconfig @@ -1121,25 +1121,6 @@ CONFIG_QCOM_SMSM=y CONFIG_QCOM_SOCINFO=m CONFIG_QCOM_STATS=m CONFIG_QCOM_WCNSS_CTRL=m -CONFIG_ARCH_EMEV2=y -CONFIG_ARCH_R8A7794=y -CONFIG_ARCH_R8A7779=y -CONFIG_ARCH_R8A7790=y -CONFIG_ARCH_R8A7778=y -CONFIG_ARCH_R8A7793=y -CONFIG_ARCH_R8A7791=y -CONFIG_ARCH_R8A7792=y -CONFIG_ARCH_R8A7740=y -CONFIG_ARCH_R8A73A4=y -CONFIG_ARCH_R7S72100=y -CONFIG_ARCH_R7S9210=y -CONFIG_ARCH_R8A77470=y -CONFIG_ARCH_R8A7745=y -CONFIG_ARCH_R8A7742=y -CONFIG_ARCH_R8A7743=y -CONFIG_ARCH_R8A7744=y -CONFIG_ARCH_R9A06G032=y -CONFIG_ARCH_SH73A0=y CONFIG_ROCKCHIP_IODOMAIN=y CONFIG_ARCH_TEGRA_2x_SOC=y CONFIG_ARCH_TEGRA_3x_SOC=y @@ -1203,7 +1184,7 @@ CONFIG_PWM_BCM2835=y CONFIG_PWM_BRCMSTB=m CONFIG_PWM_FSL_FTM=m CONFIG_PWM_MESON=m -CONFIG_PWM_RCAR=m +CONFIG_PWM_RENESAS_RCAR=m CONFIG_PWM_RENESAS_TPU=y CONFIG_PWM_ROCKCHIP=m CONFIG_PWM_SAMSUNG=m diff --git a/arch/arm/configs/shmobile_defconfig b/arch/arm/configs/shmobile_defconfig index 8c30ed14e52c..7c3d6a8f0038 100644 --- a/arch/arm/configs/shmobile_defconfig +++ b/arch/arm/configs/shmobile_defconfig @@ -63,6 +63,7 @@ CONFIG_SMSC_PHY=y CONFIG_CAN_RCAR=y CONFIG_INPUT_EVDEV=y CONFIG_KEYBOARD_GPIO=y +CONFIG_KEYBOARD_GPIO_POLLED=y # CONFIG_INPUT_MOUSE is not set CONFIG_INPUT_TOUCHSCREEN=y CONFIG_TOUCHSCREEN_EDT_FT5X06=y @@ -84,6 +85,7 @@ CONFIG_SERIAL_8250_EM=y CONFIG_SERIAL_SH_SCI=y CONFIG_I2C_CHARDEV=y CONFIG_I2C_DEMUX_PINCTRL=y +CONFIG_I2C_DESIGNWARE_CORE=y CONFIG_I2C_EMEV2=y CONFIG_I2C_GPIO=y CONFIG_I2C_RIIC=y @@ -104,7 +106,7 @@ CONFIG_GPIO_PCF857X=y CONFIG_POWER_RESET=y CONFIG_POWER_RESET_RMOBILE=y CONFIG_POWER_SUPPLY=y -# CONFIG_HWMON is not set +CONFIG_SENSORS_LM75=y CONFIG_THERMAL=y CONFIG_CPU_THERMAL=y CONFIG_RCAR_THERMAL=y @@ -174,6 +176,9 @@ CONFIG_USB_RENESAS_USBHS_UDC=y CONFIG_USB_RENESAS_USBF=y CONFIG_USB_ETH=y CONFIG_MMC=y +CONFIG_MMC_SDHCI=y +CONFIG_MMC_SDHCI_PLTFM=y +CONFIG_MMC_SDHCI_OF_ARASAN=y CONFIG_MMC_SDHI=y CONFIG_MMC_SH_MMCIF=y CONFIG_NEW_LEDS=y @@ -195,29 +200,10 @@ CONFIG_RCAR_DMAC=y CONFIG_RENESAS_USB_DMAC=y CONFIG_RZ_DMAC=y # CONFIG_IOMMU_SUPPORT is not set -CONFIG_ARCH_EMEV2=y -CONFIG_ARCH_R8A7794=y -CONFIG_ARCH_R8A7779=y -CONFIG_ARCH_R8A7790=y -CONFIG_ARCH_R8A7778=y -CONFIG_ARCH_R8A7793=y -CONFIG_ARCH_R8A7791=y -CONFIG_ARCH_R8A7792=y -CONFIG_ARCH_R8A7740=y -CONFIG_ARCH_R8A73A4=y -CONFIG_ARCH_R7S72100=y -CONFIG_ARCH_R7S9210=y -CONFIG_ARCH_R8A77470=y -CONFIG_ARCH_R8A7745=y -CONFIG_ARCH_R8A7742=y -CONFIG_ARCH_R8A7743=y -CONFIG_ARCH_R8A7744=y -CONFIG_ARCH_R9A06G032=y -CONFIG_ARCH_SH73A0=y CONFIG_IIO=y CONFIG_AK8975=y CONFIG_PWM=y -CONFIG_PWM_RCAR=y +CONFIG_PWM_RENESAS_RCAR=y CONFIG_PWM_RENESAS_TPU=y CONFIG_PHY_RCAR_GEN2=y CONFIG_PHY_RCAR_GEN3_USB2=y diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig index 370ad70b4be8..a61154545c89 100644 --- a/arch/arm64/configs/defconfig +++ b/arch/arm64/configs/defconfig @@ -1010,6 +1010,7 @@ CONFIG_SND_SOC_ROCKCHIP_RT5645=m CONFIG_SND_SOC_RK3399_GRU_SOUND=m CONFIG_SND_SOC_SAMSUNG=y CONFIG_SND_SOC_RCAR=m +CONFIG_SND_SOC_MSIOF=m CONFIG_SND_SOC_RZ=m CONFIG_SND_SOC_SOF_TOPLEVEL=y CONFIG_SND_SOC_SOF_OF=y @@ -1474,29 +1475,6 @@ CONFIG_QCOM_WCNSS_CTRL=m CONFIG_QCOM_APR=m CONFIG_QCOM_ICC_BWMON=m CONFIG_QCOM_PBS=m -CONFIG_ARCH_R8A77995=y -CONFIG_ARCH_R8A77990=y -CONFIG_ARCH_R8A77951=y -CONFIG_ARCH_R8A77965=y -CONFIG_ARCH_R8A77960=y -CONFIG_ARCH_R8A77961=y -CONFIG_ARCH_R8A779F0=y -CONFIG_ARCH_R8A77980=y -CONFIG_ARCH_R8A77970=y -CONFIG_ARCH_R8A779A0=y -CONFIG_ARCH_R8A779G0=y -CONFIG_ARCH_R8A779H0=y -CONFIG_ARCH_R8A774C0=y -CONFIG_ARCH_R8A774E1=y -CONFIG_ARCH_R8A774A1=y -CONFIG_ARCH_R8A774B1=y -CONFIG_ARCH_R9A07G043=y -CONFIG_ARCH_R9A07G044=y -CONFIG_ARCH_R9A07G054=y -CONFIG_ARCH_R9A08G045=y -CONFIG_ARCH_R9A09G011=y -CONFIG_ARCH_R9A09G047=y -CONFIG_ARCH_R9A09G057=y CONFIG_ROCKCHIP_IODOMAIN=y CONFIG_ARCH_TEGRA_132_SOC=y CONFIG_ARCH_TEGRA_210_SOC=y @@ -1550,10 +1528,11 @@ CONFIG_PWM_IMX27=m CONFIG_PWM_MESON=m CONFIG_PWM_MTK_DISP=m CONFIG_PWM_MEDIATEK=m -CONFIG_PWM_RCAR=m +CONFIG_PWM_RENESAS_RCAR=m +CONFIG_PWM_RENESAS_RZG2L_GPT=m +CONFIG_PWM_RENESAS_RZ_MTU3=m CONFIG_PWM_RENESAS_TPU=m CONFIG_PWM_ROCKCHIP=y -CONFIG_PWM_RZ_MTU3=m CONFIG_PWM_SAMSUNG=y CONFIG_PWM_SL28CPLD=m CONFIG_PWM_SUN4I=m diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index 4731d5b90d7e..d9bcd1e8413e 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig @@ -114,6 +114,16 @@ config PWM_AXI_PWMGEN To compile this driver as a module, choose M here: the module will be called pwm-axi-pwmgen. +config PWM_BCM2835 + tristate "BCM2835 PWM support" + depends on ARCH_BCM2835 || ARCH_BRCMSTB || COMPILE_TEST + depends on HAS_IOMEM + help + PWM framework driver for BCM2835 controller (Raspberry Pi) + + To compile this driver as a module, choose M here: the module + will be called pwm-bcm2835. + config PWM_BCM_IPROC tristate "iProc PWM support" depends on ARCH_BCM_IPROC || COMPILE_TEST @@ -137,16 +147,6 @@ config PWM_BCM_KONA To compile this driver as a module, choose M here: the module will be called pwm-bcm-kona. -config PWM_BCM2835 - tristate "BCM2835 PWM support" - depends on ARCH_BCM2835 || ARCH_BRCMSTB || COMPILE_TEST - depends on HAS_IOMEM - help - PWM framework driver for BCM2835 controller (Raspberry Pi) - - To compile this driver as a module, choose M here: the module - will be called pwm-bcm2835. - config PWM_BERLIN tristate "Marvell Berlin PWM support" depends on ARCH_BERLIN || COMPILE_TEST @@ -351,6 +351,18 @@ config PWM_KEEMBAY To compile this driver as a module, choose M here: the module will be called pwm-keembay. +config PWM_LOONGSON + tristate "Loongson PWM support" + depends on MACH_LOONGSON64 || COMPILE_TEST + depends on COMMON_CLK + help + Generic PWM framework driver for Loongson family. + It can be found on Loongson-2K series cpus and Loongson LS7A + bridge chips. + + To compile this driver as a module, choose M here: the module + will be called pwm-loongson. + config PWM_LP3943 tristate "TI/National Semiconductor LP3943 PWM support" depends on MFD_LP3943 @@ -411,26 +423,17 @@ config PWM_LPSS_PLATFORM To compile this driver as a module, choose M here: the module will be called pwm-lpss-platform. -config PWM_MESON - tristate "Amlogic Meson PWM driver" - depends on ARCH_MESON || COMPILE_TEST - depends on COMMON_CLK && HAS_IOMEM - help - The platform driver for Amlogic Meson PWM controller. - - To compile this driver as a module, choose M here: the module - will be called pwm-meson. - -config PWM_MTK_DISP - tristate "MediaTek display PWM driver" - depends on ARCH_MEDIATEK || COMPILE_TEST - depends on HAS_IOMEM +config PWM_MC33XS2410 + tristate "MC33XS2410 PWM support" + depends on OF + depends on SPI help - Generic PWM framework driver for MediaTek disp-pwm device. - The PWM is used to control the backlight brightness for display. + NXP MC33XS2410 high-side switch driver. The MC33XS2410 is a four + channel high-side switch. The device is operational from 3.0 V + to 60 V. The device is controlled by SPI port for configuration. To compile this driver as a module, choose M here: the module - will be called pwm-mtk-disp. + will be called pwm-mc33xs2410. config PWM_MEDIATEK tristate "MediaTek PWM support" @@ -442,6 +445,16 @@ config PWM_MEDIATEK To compile this driver as a module, choose M here: the module will be called pwm-mediatek. +config PWM_MESON + tristate "Amlogic Meson PWM driver" + depends on ARCH_MESON || COMPILE_TEST + depends on COMMON_CLK && HAS_IOMEM + help + The platform driver for Amlogic Meson PWM controller. + + To compile this driver as a module, choose M here: the module + will be called pwm-meson. + config PWM_MICROCHIP_CORE tristate "Microchip corePWM PWM support" depends on ARCH_MICROCHIP_POLARFIRE || COMPILE_TEST @@ -452,6 +465,17 @@ config PWM_MICROCHIP_CORE To compile this driver as a module, choose M here: the module will be called pwm-microchip-core. +config PWM_MTK_DISP + tristate "MediaTek display PWM driver" + depends on ARCH_MEDIATEK || COMPILE_TEST + depends on HAS_IOMEM + help + Generic PWM framework driver for MediaTek disp-pwm device. + The PWM is used to control the backlight brightness for display. + + To compile this driver as a module, choose M here: the module + will be called pwm-mtk-disp. + config PWM_MXS tristate "Freescale MXS PWM support" depends on ARCH_MXS || COMPILE_TEST @@ -510,7 +534,7 @@ config PWM_RASPBERRYPI_POE Enable Raspberry Pi firmware controller PWM bus used to control the official RPI PoE hat -config PWM_RCAR +config PWM_RENESAS_RCAR tristate "Renesas R-Car PWM support" depends on ARCH_RENESAS || COMPILE_TEST depends on HAS_IOMEM @@ -521,6 +545,28 @@ config PWM_RCAR To compile this driver as a module, choose M here: the module will be called pwm-rcar. +config PWM_RENESAS_RZG2L_GPT + tristate "Renesas RZ/G2L General PWM Timer support" + depends on ARCH_RZG2L || COMPILE_TEST + depends on HAS_IOMEM + help + This driver exposes the General PWM Timer controller found in Renesas + RZ/G2L like chips through the PWM API. + + To compile this driver as a module, choose M here: the module + will be called pwm-rzg2l-gpt. + +config PWM_RENESAS_RZ_MTU3 + tristate "Renesas RZ/G2L MTU3a PWM Timer support" + depends on RZ_MTU3 + depends on HAS_IOMEM + help + This driver exposes the MTU3a PWM Timer controller found in Renesas + RZ/G2L like chips through the PWM API. + + To compile this driver as a module, choose M here: the module + will be called pwm-rz-mtu3. + config PWM_RENESAS_TPU tristate "Renesas TPU PWM support" depends on ARCH_RENESAS || COMPILE_TEST @@ -540,17 +586,6 @@ config PWM_ROCKCHIP Generic PWM framework driver for the PWM controller found on Rockchip SoCs. -config PWM_RZ_MTU3 - tristate "Renesas RZ/G2L MTU3a PWM Timer support" - depends on RZ_MTU3 - depends on HAS_IOMEM - help - This driver exposes the MTU3a PWM Timer controller found in Renesas - RZ/G2L like chips through the PWM API. - - To compile this driver as a module, choose M here: the module - will be called pwm-rz-mtu3. - config PWM_SAMSUNG tristate "Samsung PWM support" depends on PLAT_SAMSUNG || ARCH_S5PV210 || ARCH_EXYNOS || COMPILE_TEST diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile index 539e0def3f82..96160f4257fc 100644 --- a/drivers/pwm/Makefile +++ b/drivers/pwm/Makefile @@ -7,9 +7,9 @@ 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_BCM2835) += pwm-bcm2835.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 obj-$(CONFIG_PWM_BERLIN) += pwm-berlin.o obj-$(CONFIG_PWM_BRCMSTB) += pwm-brcmstb.o obj-$(CONFIG_PWM_CLK) += pwm-clk.o @@ -30,14 +30,16 @@ obj-$(CONFIG_PWM_INTEL_LGM) += pwm-intel-lgm.o obj-$(CONFIG_PWM_IQS620A) += pwm-iqs620a.o obj-$(CONFIG_PWM_JZ4740) += pwm-jz4740.o obj-$(CONFIG_PWM_KEEMBAY) += pwm-keembay.o +obj-$(CONFIG_PWM_LOONGSON) += pwm-loongson.o obj-$(CONFIG_PWM_LP3943) += pwm-lp3943.o obj-$(CONFIG_PWM_LPC18XX_SCT) += pwm-lpc18xx-sct.o obj-$(CONFIG_PWM_LPC32XX) += pwm-lpc32xx.o obj-$(CONFIG_PWM_LPSS) += pwm-lpss.o obj-$(CONFIG_PWM_LPSS_PCI) += pwm-lpss-pci.o obj-$(CONFIG_PWM_LPSS_PLATFORM) += pwm-lpss-platform.o -obj-$(CONFIG_PWM_MESON) += pwm-meson.o +obj-$(CONFIG_PWM_MC33XS2410) += pwm-mc33xs2410.o obj-$(CONFIG_PWM_MEDIATEK) += pwm-mediatek.o +obj-$(CONFIG_PWM_MESON) += pwm-meson.o obj-$(CONFIG_PWM_MICROCHIP_CORE) += pwm-microchip-core.o obj-$(CONFIG_PWM_MTK_DISP) += pwm-mtk-disp.o obj-$(CONFIG_PWM_MXS) += pwm-mxs.o @@ -46,10 +48,11 @@ obj-$(CONFIG_PWM_OMAP_DMTIMER) += pwm-omap-dmtimer.o obj-$(CONFIG_PWM_PCA9685) += pwm-pca9685.o obj-$(CONFIG_PWM_PXA) += pwm-pxa.o obj-$(CONFIG_PWM_RASPBERRYPI_POE) += pwm-raspberrypi-poe.o -obj-$(CONFIG_PWM_RCAR) += pwm-rcar.o +obj-$(CONFIG_PWM_RENESAS_RCAR) += pwm-rcar.o +obj-$(CONFIG_PWM_RENESAS_RZG2L_GPT) += pwm-rzg2l-gpt.o +obj-$(CONFIG_PWM_RENESAS_RZ_MTU3) += pwm-rz-mtu3.o obj-$(CONFIG_PWM_RENESAS_TPU) += pwm-renesas-tpu.o obj-$(CONFIG_PWM_ROCKCHIP) += pwm-rockchip.o -obj-$(CONFIG_PWM_RZ_MTU3) += pwm-rz-mtu3.o obj-$(CONFIG_PWM_SAMSUNG) += pwm-samsung.o obj-$(CONFIG_PWM_SIFIVE) += pwm-sifive.o obj-$(CONFIG_PWM_SL28CPLD) += pwm-sl28cpld.o diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c index 0387bd838487..4d842c692194 100644 --- a/drivers/pwm/core.c +++ b/drivers/pwm/core.c @@ -216,21 +216,28 @@ static int __pwm_write_waveform(struct pwm_chip *chip, struct pwm_device *pwm, c * * Typically a given waveform cannot be implemented exactly by hardware, e.g. * because hardware only supports coarse period resolution or no duty_offset. - * This function returns the actually implemented waveform if you pass wf to - * pwm_set_waveform_might_sleep now. + * This function returns the actually implemented waveform if you pass @wf to + * pwm_set_waveform_might_sleep() now. * * Note however that the world doesn't stop turning when you call it, so when - * doing + * doing:: * - * pwm_round_waveform_might_sleep(mypwm, &wf); - * pwm_set_waveform_might_sleep(mypwm, &wf, true); + * pwm_round_waveform_might_sleep(mypwm, &wf); + * pwm_set_waveform_might_sleep(mypwm, &wf, true); * * the latter might fail, e.g. because an input clock changed its rate between * these two calls and the waveform determined by * pwm_round_waveform_might_sleep() cannot be implemented any more. * - * Returns 0 on success, 1 if there is no valid hardware configuration matching - * the input waveform under the PWM rounding rules or a negative errno. + * Usually all values passed in @wf are rounded down to the nearest possible + * value (in the order period_length_ns, duty_length_ns and then + * duty_offset_ns). Only if this isn't possible, a value might grow. See the + * documentation for pwm_set_waveform_might_sleep() for a more formal + * description. + * + * Returns: 0 on success, 1 if at least one value had to be rounded up or a + * negative errno. + * Context: May sleep. */ int pwm_round_waveform_might_sleep(struct pwm_device *pwm, struct pwm_waveform *wf) { @@ -270,10 +277,10 @@ int pwm_round_waveform_might_sleep(struct pwm_device *pwm, struct pwm_waveform * wf_req.duty_length_ns, wf_req.period_length_ns, wf_req.duty_offset_ns, ret_tohw); if (IS_ENABLED(CONFIG_PWM_DEBUG) && - ret_tohw == 0 && !pwm_check_rounding(&wf_req, wf)) - dev_err(&chip->dev, "Wrong rounding: requested %llu/%llu [+%llu], result %llu/%llu [+%llu]\n", + (ret_tohw == 0) != pwm_check_rounding(&wf_req, wf)) + dev_err(&chip->dev, "Wrong rounding: requested %llu/%llu [+%llu], result %llu/%llu [+%llu], ret: %d\n", wf_req.duty_length_ns, wf_req.period_length_ns, wf_req.duty_offset_ns, - wf->duty_length_ns, wf->period_length_ns, wf->duty_offset_ns); + wf->duty_length_ns, wf->period_length_ns, wf->duty_offset_ns, ret_tohw); return ret_tohw; } @@ -287,6 +294,9 @@ EXPORT_SYMBOL_GPL(pwm_round_waveform_might_sleep); * * Stores the current configuration of the PWM in @wf. Note this is the * equivalent of pwm_get_state_hw() (and not pwm_get_state()) for pwm_waveform. + * + * Returns: 0 on success or a negative errno + * Context: May sleep. */ int pwm_get_waveform_might_sleep(struct pwm_device *pwm, struct pwm_waveform *wf) { @@ -341,10 +351,10 @@ static int __pwm_set_waveform(struct pwm_device *pwm, if (err) return err; - if (IS_ENABLED(CONFIG_PWM_DEBUG) && ret_tohw == 0 && !pwm_check_rounding(wf, &wf_rounded)) - dev_err(&chip->dev, "Wrong rounding: requested %llu/%llu [+%llu], result %llu/%llu [+%llu]\n", + if (IS_ENABLED(CONFIG_PWM_DEBUG) && (ret_tohw == 0) != pwm_check_rounding(wf, &wf_rounded)) + dev_err(&chip->dev, "Wrong rounding: requested %llu/%llu [+%llu], result %llu/%llu [+%llu], ret: %d\n", wf->duty_length_ns, wf->period_length_ns, wf->duty_offset_ns, - wf_rounded.duty_length_ns, wf_rounded.period_length_ns, wf_rounded.duty_offset_ns); + wf_rounded.duty_length_ns, wf_rounded.period_length_ns, wf_rounded.duty_offset_ns, ret_tohw); if (exact && pwmwfcmp(wf, &wf_rounded)) { dev_dbg(&chip->dev, "Requested no rounding, but %llu/%llu [+%llu] -> %llu/%llu [+%llu]\n", @@ -395,13 +405,37 @@ static int __pwm_set_waveform(struct pwm_device *pwm, * * Typically a requested waveform cannot be implemented exactly, e.g. because * you requested .period_length_ns = 100 ns, but the hardware can only set - * periods that are a multiple of 8.5 ns. With that hardware passing exact = - * true results in pwm_set_waveform_might_sleep() failing and returning 1. If - * exact = false you get a period of 93.5 ns (i.e. the biggest period not bigger - * than the requested value). - * Note that even with exact = true, some rounding by less than 1 is + * periods that are a multiple of 8.5 ns. With that hardware passing @exact = + * true results in pwm_set_waveform_might_sleep() failing and returning -EDOM. + * If @exact = false you get a period of 93.5 ns (i.e. the biggest period not + * bigger than the requested value). + * Note that even with @exact = true, some rounding by less than 1 ns is * possible/needed. In the above example requesting .period_length_ns = 94 and - * exact = true, you get the hardware configured with period = 93.5 ns. + * @exact = true, you get the hardware configured with period = 93.5 ns. + * + * Let C be the set of possible hardware configurations for a given PWM device, + * consisting of tuples (p, d, o) where p is the period length, d is the duty + * length and o the duty offset. + * + * The following algorithm is implemented to pick the hardware setting + * (p, d, o) ∈ C for a given request (p', d', o') with @exact = false:: + * + * p = max( { ṗ | ∃ ḋ, ȯ : (ṗ, ḋ, ȯ) ∈ C ∧ ṗ ≤ p' } ∪ { min({ ṗ | ∃ ḋ, ȯ : (ṗ, ḋ, ȯ) ∈ C }) }) + * d = max( { ḋ | ∃ ȯ : (p, ḋ, ȯ) ∈ C ∧ ḋ ≤ d' } ∪ { min({ ḋ | ∃ ȯ : (p, ḋ, ȯ) ∈ C }) }) + * o = max( { ȯ | (p, d, ȯ) ∈ C ∧ ȯ ≤ o' } ∪ { min({ ȯ | (p, d, ȯ) ∈ C }) }) + * + * In words: The chosen period length is the maximal possible period length not + * bigger than the requested period length and if that doesn't exist, the + * minimal period length. The chosen duty length is the maximal possible duty + * length that is compatible with the chosen period length and isn't bigger than + * the requested duty length. Again if such a value doesn't exist, the minimal + * duty length compatible with the chosen period is picked. After that the duty + * offset compatible with the chosen period and duty length is chosen in the + * same way. + * + * Returns: 0 on success, -EDOM if setting failed due to the exact waveform not + * being possible (if @exact), or a different negative errno on failure. + * Context: May sleep. */ int pwm_set_waveform_might_sleep(struct pwm_device *pwm, const struct pwm_waveform *wf, bool exact) @@ -428,6 +462,19 @@ int pwm_set_waveform_might_sleep(struct pwm_device *pwm, err = __pwm_set_waveform(pwm, wf, exact); } + /* + * map err == 1 to -EDOM for exact requests and 0 for !exact ones. Also + * make sure that -EDOM is only returned in exactly that case. Note that + * __pwm_set_waveform() should never return -EDOM which justifies the + * unlikely(). + */ + if (unlikely(err == -EDOM)) + err = -EINVAL; + else if (exact && err == 1) + err = -EDOM; + else if (err == 1) + err = 0; + return err; } EXPORT_SYMBOL_GPL(pwm_set_waveform_might_sleep); @@ -561,11 +608,6 @@ static bool pwm_state_valid(const struct pwm_state *state) return true; } -/** - * __pwm_apply() - atomically apply a new state to a PWM device - * @pwm: PWM device - * @state: new state to apply - */ static int __pwm_apply(struct pwm_device *pwm, const struct pwm_state *state) { struct pwm_chip *chip; @@ -674,6 +716,9 @@ static int __pwm_apply(struct pwm_device *pwm, const struct pwm_state *state) * Cannot be used in atomic context. * @pwm: PWM device * @state: new state to apply + * + * Returns: 0 on success, or a negative errno + * Context: May sleep. */ int pwm_apply_might_sleep(struct pwm_device *pwm, const struct pwm_state *state) { @@ -715,6 +760,9 @@ EXPORT_SYMBOL_GPL(pwm_apply_might_sleep); * Not all PWM devices support this function, check with pwm_might_sleep(). * @pwm: PWM device * @state: new state to apply + * + * Returns: 0 on success, or a negative errno + * Context: Any */ int pwm_apply_atomic(struct pwm_device *pwm, const struct pwm_state *state) { @@ -788,6 +836,9 @@ EXPORT_SYMBOL_GPL(pwm_get_state_hw); * This function will adjust the PWM config to the PWM arguments provided * by the DT or PWM lookup table. This is particularly useful to adapt * the bootloader config to the Linux one. + * + * Returns: 0 on success or a negative error code on failure. + * Context: May sleep. */ int pwm_adjust_config(struct pwm_device *pwm) { @@ -2221,25 +2272,28 @@ static void pwm_dbg_show(struct pwm_chip *chip, struct seq_file *s) for (i = 0; i < chip->npwm; i++) { struct pwm_device *pwm = &chip->pwms[i]; - struct pwm_state state; + struct pwm_state state, hwstate; pwm_get_state(pwm, &state); + pwm_get_state_hw(pwm, &hwstate); seq_printf(s, " pwm-%-3d (%-20.20s):", i, pwm->label); if (test_bit(PWMF_REQUESTED, &pwm->flags)) seq_puts(s, " requested"); - if (state.enabled) - seq_puts(s, " enabled"); + seq_puts(s, "\n"); - seq_printf(s, " period: %llu ns", state.period); - seq_printf(s, " duty: %llu ns", state.duty_cycle); - seq_printf(s, " polarity: %s", + seq_printf(s, " requested configuration: %3sabled, %llu/%llu ns, %s polarity", + state.enabled ? "en" : "dis", state.duty_cycle, state.period, state.polarity ? "inverse" : "normal"); - if (state.usage_power) - seq_puts(s, " usage_power"); + seq_puts(s, ", usage_power"); + seq_puts(s, "\n"); + + seq_printf(s, " actual configuration: %3sabled, %llu/%llu ns, %s polarity", + hwstate.enabled ? "en" : "dis", hwstate.duty_cycle, hwstate.period, + hwstate.polarity ? "inverse" : "normal"); seq_puts(s, "\n"); } diff --git a/drivers/pwm/pwm-adp5585.c b/drivers/pwm/pwm-adp5585.c index 40472ac5db64..d79106d12181 100644 --- a/drivers/pwm/pwm-adp5585.c +++ b/drivers/pwm/pwm-adp5585.c @@ -20,6 +20,7 @@ #include <linux/mfd/adp5585.h> #include <linux/minmax.h> #include <linux/module.h> +#include <linux/mod_devicetable.h> #include <linux/platform_device.h> #include <linux/pwm.h> #include <linux/regmap.h> diff --git a/drivers/pwm/pwm-loongson.c b/drivers/pwm/pwm-loongson.c new file mode 100644 index 000000000000..1ba16168cbb4 --- /dev/null +++ b/drivers/pwm/pwm-loongson.c @@ -0,0 +1,290 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2017-2025 Loongson Technology Corporation Limited. + * + * Loongson PWM driver + * + * For Loongson's PWM IP block documentation please refer Chapter 11 of + * Reference Manual: https://loongson.github.io/LoongArch-Documentation/Loongson-7A1000-usermanual-EN.pdf + * + * Author: Juxin Gao <gaojuxin@loongson.cn> + * Further cleanup and restructuring by: + * Binbin Zhou <zhoubinbin@loongson.cn> + * + * Limitations: + * - If both DUTY and PERIOD are set to 0, the output is a constant low signal. + * - When disabled the output is driven to 0 independent of the configured + * polarity. + * - If the register is reconfigured while PWM is running, it does not complete + * the currently running period. + * - Disabling the PWM stops the output immediately (without waiting for current + * period to complete first). + */ + +#include <linux/acpi.h> +#include <linux/clk.h> +#include <linux/device.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/pwm.h> +#include <linux/units.h> + +/* Loongson PWM registers */ +#define LOONGSON_PWM_REG_DUTY 0x4 /* Low Pulse Buffer Register */ +#define LOONGSON_PWM_REG_PERIOD 0x8 /* Pulse Period Buffer Register */ +#define LOONGSON_PWM_REG_CTRL 0xc /* Control Register */ + +/* Control register bits */ +#define LOONGSON_PWM_CTRL_REG_EN BIT(0) /* Counter Enable Bit */ +#define LOONGSON_PWM_CTRL_REG_OE BIT(3) /* Pulse Output Enable Control Bit, Valid Low */ +#define LOONGSON_PWM_CTRL_REG_SINGLE BIT(4) /* Single Pulse Control Bit */ +#define LOONGSON_PWM_CTRL_REG_INTE BIT(5) /* Interrupt Enable Bit */ +#define LOONGSON_PWM_CTRL_REG_INT BIT(6) /* Interrupt Bit */ +#define LOONGSON_PWM_CTRL_REG_RST BIT(7) /* Counter Reset Bit */ +#define LOONGSON_PWM_CTRL_REG_CAPTE BIT(8) /* Measurement Pulse Enable Bit */ +#define LOONGSON_PWM_CTRL_REG_INVERT BIT(9) /* Output flip-flop Enable Bit */ +#define LOONGSON_PWM_CTRL_REG_DZONE BIT(10) /* Anti-dead Zone Enable Bit */ + +/* default input clk frequency for the ACPI case */ +#define LOONGSON_PWM_FREQ_DEFAULT 50000 /* Hz */ + +struct pwm_loongson_ddata { + struct clk *clk; + void __iomem *base; + u64 clk_rate; +}; + +static inline __pure struct pwm_loongson_ddata *to_pwm_loongson_ddata(struct pwm_chip *chip) +{ + return pwmchip_get_drvdata(chip); +} + +static inline u32 pwm_loongson_readl(struct pwm_loongson_ddata *ddata, u32 offset) +{ + return readl(ddata->base + offset); +} + +static inline void pwm_loongson_writel(struct pwm_loongson_ddata *ddata, + u32 val, u32 offset) +{ + writel(val, ddata->base + offset); +} + +static int pwm_loongson_set_polarity(struct pwm_chip *chip, struct pwm_device *pwm, + enum pwm_polarity polarity) +{ + u16 val; + struct pwm_loongson_ddata *ddata = to_pwm_loongson_ddata(chip); + + val = pwm_loongson_readl(ddata, LOONGSON_PWM_REG_CTRL); + + if (polarity == PWM_POLARITY_INVERSED) + /* Duty cycle defines LOW period of PWM */ + val |= LOONGSON_PWM_CTRL_REG_INVERT; + else + /* Duty cycle defines HIGH period of PWM */ + val &= ~LOONGSON_PWM_CTRL_REG_INVERT; + + pwm_loongson_writel(ddata, val, LOONGSON_PWM_REG_CTRL); + + return 0; +} + +static void pwm_loongson_disable(struct pwm_chip *chip, struct pwm_device *pwm) +{ + u32 val; + struct pwm_loongson_ddata *ddata = to_pwm_loongson_ddata(chip); + + val = pwm_loongson_readl(ddata, LOONGSON_PWM_REG_CTRL); + val &= ~LOONGSON_PWM_CTRL_REG_EN; + pwm_loongson_writel(ddata, val, LOONGSON_PWM_REG_CTRL); +} + +static int pwm_loongson_enable(struct pwm_chip *chip, struct pwm_device *pwm) +{ + u32 val; + struct pwm_loongson_ddata *ddata = to_pwm_loongson_ddata(chip); + + val = pwm_loongson_readl(ddata, LOONGSON_PWM_REG_CTRL); + val |= LOONGSON_PWM_CTRL_REG_EN; + pwm_loongson_writel(ddata, val, LOONGSON_PWM_REG_CTRL); + + return 0; +} + +static int pwm_loongson_config(struct pwm_chip *chip, struct pwm_device *pwm, + u64 duty_ns, u64 period_ns) +{ + u64 duty, period; + struct pwm_loongson_ddata *ddata = to_pwm_loongson_ddata(chip); + + /* duty = duty_ns * ddata->clk_rate / NSEC_PER_SEC */ + duty = mul_u64_u64_div_u64(duty_ns, ddata->clk_rate, NSEC_PER_SEC); + if (duty > U32_MAX) + duty = U32_MAX; + + /* period = period_ns * ddata->clk_rate / NSEC_PER_SEC */ + period = mul_u64_u64_div_u64(period_ns, ddata->clk_rate, NSEC_PER_SEC); + if (period > U32_MAX) + period = U32_MAX; + + pwm_loongson_writel(ddata, duty, LOONGSON_PWM_REG_DUTY); + pwm_loongson_writel(ddata, period, LOONGSON_PWM_REG_PERIOD); + + return 0; +} + +static int pwm_loongson_apply(struct pwm_chip *chip, struct pwm_device *pwm, + const struct pwm_state *state) +{ + int ret; + bool enabled = pwm->state.enabled; + + if (!state->enabled) { + if (enabled) + pwm_loongson_disable(chip, pwm); + return 0; + } + + ret = pwm_loongson_set_polarity(chip, pwm, state->polarity); + if (ret) + return ret; + + ret = pwm_loongson_config(chip, pwm, state->duty_cycle, state->period); + if (ret) + return ret; + + if (!enabled && state->enabled) + ret = pwm_loongson_enable(chip, pwm); + + return ret; +} + +static int pwm_loongson_get_state(struct pwm_chip *chip, struct pwm_device *pwm, + struct pwm_state *state) +{ + u32 duty, period, ctrl; + struct pwm_loongson_ddata *ddata = to_pwm_loongson_ddata(chip); + + duty = pwm_loongson_readl(ddata, LOONGSON_PWM_REG_DUTY); + period = pwm_loongson_readl(ddata, LOONGSON_PWM_REG_PERIOD); + ctrl = pwm_loongson_readl(ddata, LOONGSON_PWM_REG_CTRL); + + /* duty & period have a max of 2^32, so we can't overflow */ + state->duty_cycle = DIV64_U64_ROUND_UP((u64)duty * NSEC_PER_SEC, ddata->clk_rate); + state->period = DIV64_U64_ROUND_UP((u64)period * NSEC_PER_SEC, ddata->clk_rate); + state->polarity = (ctrl & LOONGSON_PWM_CTRL_REG_INVERT) ? PWM_POLARITY_INVERSED : + PWM_POLARITY_NORMAL; + state->enabled = (ctrl & LOONGSON_PWM_CTRL_REG_EN) ? true : false; + + return 0; +} + +static const struct pwm_ops pwm_loongson_ops = { + .apply = pwm_loongson_apply, + .get_state = pwm_loongson_get_state, +}; + +static int pwm_loongson_probe(struct platform_device *pdev) +{ + int ret; + struct pwm_chip *chip; + struct pwm_loongson_ddata *ddata; + struct device *dev = &pdev->dev; + + chip = devm_pwmchip_alloc(dev, 1, sizeof(*ddata)); + if (IS_ERR(chip)) + return PTR_ERR(chip); + ddata = to_pwm_loongson_ddata(chip); + + ddata->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(ddata->base)) + return PTR_ERR(ddata->base); + + ddata->clk = devm_clk_get_optional_enabled(dev, NULL); + if (IS_ERR(ddata->clk)) + return dev_err_probe(dev, PTR_ERR(ddata->clk), + "Failed to get pwm clock\n"); + if (ddata->clk) { + ret = devm_clk_rate_exclusive_get(dev, ddata->clk); + if (ret) + return dev_err_probe(dev, ret, + "Failed to get exclusive rate\n"); + + ddata->clk_rate = clk_get_rate(ddata->clk); + if (!ddata->clk_rate) + return dev_err_probe(dev, -EINVAL, + "Failed to get frequency\n"); + } else { + ddata->clk_rate = LOONGSON_PWM_FREQ_DEFAULT; + } + + /* This check is done to prevent an overflow in .apply */ + if (ddata->clk_rate > NSEC_PER_SEC) + return dev_err_probe(dev, -EINVAL, "PWM clock out of range\n"); + + chip->ops = &pwm_loongson_ops; + chip->atomic = true; + dev_set_drvdata(dev, chip); + + ret = devm_pwmchip_add(dev, chip); + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to add PWM chip\n"); + + return 0; +} + +static int pwm_loongson_suspend(struct device *dev) +{ + struct pwm_chip *chip = dev_get_drvdata(dev); + struct pwm_loongson_ddata *ddata = to_pwm_loongson_ddata(chip); + struct pwm_device *pwm = &chip->pwms[0]; + + if (pwm->state.enabled) + return -EBUSY; + + clk_disable_unprepare(ddata->clk); + + return 0; +} + +static int pwm_loongson_resume(struct device *dev) +{ + struct pwm_chip *chip = dev_get_drvdata(dev); + struct pwm_loongson_ddata *ddata = to_pwm_loongson_ddata(chip); + + return clk_prepare_enable(ddata->clk); +} + +static DEFINE_SIMPLE_DEV_PM_OPS(pwm_loongson_pm_ops, pwm_loongson_suspend, + pwm_loongson_resume); + +static const struct of_device_id pwm_loongson_of_ids[] = { + { .compatible = "loongson,ls7a-pwm" }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, pwm_loongson_of_ids); + +static const struct acpi_device_id pwm_loongson_acpi_ids[] = { + { "LOON0006" }, + { } +}; +MODULE_DEVICE_TABLE(acpi, pwm_loongson_acpi_ids); + +static struct platform_driver pwm_loongson_driver = { + .probe = pwm_loongson_probe, + .driver = { + .name = "loongson-pwm", + .pm = pm_ptr(&pwm_loongson_pm_ops), + .of_match_table = pwm_loongson_of_ids, + .acpi_match_table = pwm_loongson_acpi_ids, + }, +}; +module_platform_driver(pwm_loongson_driver); + +MODULE_DESCRIPTION("Loongson PWM driver"); +MODULE_AUTHOR("Loongson Technology Corporation Limited."); +MODULE_LICENSE("GPL"); diff --git a/drivers/pwm/pwm-mc33xs2410.c b/drivers/pwm/pwm-mc33xs2410.c new file mode 100644 index 000000000000..a1ac3445ccdb --- /dev/null +++ b/drivers/pwm/pwm-mc33xs2410.c @@ -0,0 +1,391 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2024 Liebherr-Electronics and Drives GmbH + * + * Reference Manual : https://www.nxp.com/docs/en/data-sheet/MC33XS2410.pdf + * + * Limitations: + * - Supports frequencies between 0.5Hz and 2048Hz with following steps: + * - 0.5 Hz steps from 0.5 Hz to 32 Hz + * - 2 Hz steps from 2 Hz to 128 Hz + * - 8 Hz steps from 8 Hz to 512 Hz + * - 32 Hz steps from 32 Hz to 2048 Hz + * - Cannot generate a 0 % duty cycle. + * - Always produces low output if disabled. + * - Configuration isn't atomic. When changing polarity, duty cycle or period + * the data is taken immediately, counters not being affected, resulting in a + * behavior of the output pin that is neither the old nor the new state, + * rather something in between. + */ + +#include <linux/bitfield.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/math64.h> +#include <linux/minmax.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/pwm.h> + +#include <linux/spi/spi.h> + +#define MC33XS2410_GLB_CTRL 0x00 +#define MC33XS2410_GLB_CTRL_MODE GENMASK(7, 6) +#define MC33XS2410_GLB_CTRL_MODE_NORMAL FIELD_PREP(MC33XS2410_GLB_CTRL_MODE, 1) + +#define MC33XS2410_PWM_CTRL1 0x05 +/* chan in { 1 ... 4 } */ +#define MC33XS2410_PWM_CTRL1_POL_INV(chan) BIT((chan) + 1) + +#define MC33XS2410_PWM_CTRL3 0x07 +/* chan in { 1 ... 4 } */ +#define MC33XS2410_PWM_CTRL3_EN(chan) BIT(4 + (chan) - 1) + +/* chan in { 1 ... 4 } */ +#define MC33XS2410_PWM_FREQ(chan) (0x08 + (chan) - 1) +#define MC33XS2410_PWM_FREQ_STEP GENMASK(7, 6) +#define MC33XS2410_PWM_FREQ_COUNT GENMASK(5, 0) + +/* chan in { 1 ... 4 } */ +#define MC33XS2410_PWM_DC(chan) (0x0c + (chan) - 1) + +#define MC33XS2410_WDT 0x14 + +#define MC33XS2410_PWM_MIN_PERIOD 488282 +/* step in { 0 ... 3 } */ +#define MC33XS2410_PWM_MAX_PERIOD(step) (2000000000 >> (2 * (step))) + +#define MC33XS2410_FRAME_IN_ADDR GENMASK(15, 8) +#define MC33XS2410_FRAME_IN_DATA GENMASK(7, 0) +#define MC33XS2410_FRAME_IN_ADDR_WR BIT(7) +#define MC33XS2410_FRAME_IN_DATA_RD BIT(7) +#define MC33XS2410_FRAME_OUT_DATA GENMASK(13, 0) + +#define MC33XS2410_MAX_TRANSFERS 5 + +static int mc33xs2410_write_regs(struct spi_device *spi, u8 *reg, u8 *val, + unsigned int len) +{ + u16 tx[MC33XS2410_MAX_TRANSFERS]; + int i; + + if (len > MC33XS2410_MAX_TRANSFERS) + return -EINVAL; + + for (i = 0; i < len; i++) + tx[i] = FIELD_PREP(MC33XS2410_FRAME_IN_DATA, val[i]) | + FIELD_PREP(MC33XS2410_FRAME_IN_ADDR, + MC33XS2410_FRAME_IN_ADDR_WR | reg[i]); + + return spi_write(spi, tx, len * 2); +} + +static int mc33xs2410_read_regs(struct spi_device *spi, u8 *reg, u8 flag, + u16 *val, unsigned int len) +{ + u16 tx[MC33XS2410_MAX_TRANSFERS]; + u16 rx[MC33XS2410_MAX_TRANSFERS]; + struct spi_transfer t = { + .tx_buf = tx, + .rx_buf = rx, + }; + int i, ret; + + len++; + if (len > MC33XS2410_MAX_TRANSFERS) + return -EINVAL; + + t.len = len * 2; + for (i = 0; i < len - 1; i++) + tx[i] = FIELD_PREP(MC33XS2410_FRAME_IN_DATA, flag) | + FIELD_PREP(MC33XS2410_FRAME_IN_ADDR, reg[i]); + + ret = spi_sync_transfer(spi, &t, 1); + if (ret < 0) + return ret; + + for (i = 1; i < len; i++) + val[i - 1] = FIELD_GET(MC33XS2410_FRAME_OUT_DATA, rx[i]); + + return 0; +} + +static int mc33xs2410_write_reg(struct spi_device *spi, u8 reg, u8 val) +{ + return mc33xs2410_write_regs(spi, ®, &val, 1); +} + +static int mc33xs2410_read_reg(struct spi_device *spi, u8 reg, u16 *val, u8 flag) +{ + return mc33xs2410_read_regs(spi, ®, flag, val, 1); +} + +static int mc33xs2410_read_reg_ctrl(struct spi_device *spi, u8 reg, u16 *val) +{ + return mc33xs2410_read_reg(spi, reg, val, MC33XS2410_FRAME_IN_DATA_RD); +} + +static int mc33xs2410_modify_reg(struct spi_device *spi, u8 reg, u8 mask, u8 val) +{ + u16 tmp; + int ret; + + ret = mc33xs2410_read_reg_ctrl(spi, reg, &tmp); + if (ret < 0) + return ret; + + tmp &= ~mask; + tmp |= val & mask; + + return mc33xs2410_write_reg(spi, reg, tmp); +} + +static u8 mc33xs2410_pwm_get_freq(u64 period) +{ + u8 step, count; + + /* + * Check which step [0 .. 3] is appropriate for the given period. The + * period ranges for the different step values overlap. Prefer big step + * values as these allow more finegrained period and duty cycle + * selection. + */ + + switch (period) { + case MC33XS2410_PWM_MIN_PERIOD ... MC33XS2410_PWM_MAX_PERIOD(3): + step = 3; + break; + case MC33XS2410_PWM_MAX_PERIOD(3) + 1 ... MC33XS2410_PWM_MAX_PERIOD(2): + step = 2; + break; + case MC33XS2410_PWM_MAX_PERIOD(2) + 1 ... MC33XS2410_PWM_MAX_PERIOD(1): + step = 1; + break; + case MC33XS2410_PWM_MAX_PERIOD(1) + 1 ... MC33XS2410_PWM_MAX_PERIOD(0): + step = 0; + break; + } + + /* + * Round up here because a higher count results in a higher frequency + * and so a smaller period. + */ + count = DIV_ROUND_UP((u32)MC33XS2410_PWM_MAX_PERIOD(step), (u32)period); + return FIELD_PREP(MC33XS2410_PWM_FREQ_STEP, step) | + FIELD_PREP(MC33XS2410_PWM_FREQ_COUNT, count - 1); +} + +static u64 mc33xs2410_pwm_get_period(u8 reg) +{ + u32 doubled_freq, code, doubled_steps; + + /* + * steps: + * - 0 = 0.5Hz + * - 1 = 2Hz + * - 2 = 8Hz + * - 3 = 32Hz + * frequency = (code + 1) x steps. + * + * To avoid losing precision in case steps value is zero, scale the + * steps value for now by two and keep it in mind when calculating the + * period that the frequency had been doubled. + */ + doubled_steps = 1 << (FIELD_GET(MC33XS2410_PWM_FREQ_STEP, reg) * 2); + code = FIELD_GET(MC33XS2410_PWM_FREQ_COUNT, reg); + doubled_freq = (code + 1) * doubled_steps; + + /* Convert frequency to period, considering the doubled frequency. */ + return DIV_ROUND_UP(2 * NSEC_PER_SEC, doubled_freq); +} + +/* + * The hardware cannot generate a 0% relative duty cycle for normal and inversed + * polarity. For normal polarity, the channel must be disabled, the device then + * emits a constant low signal. + * For inverted polarity, the channel must be enabled, the polarity must be set + * to normal and the relative duty cylce must be set to 100%. The device then + * emits a constant high signal. + */ +static int mc33xs2410_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, + const struct pwm_state *state) +{ + struct spi_device *spi = pwmchip_get_drvdata(chip); + u8 reg[4] = { + MC33XS2410_PWM_FREQ(pwm->hwpwm + 1), + MC33XS2410_PWM_DC(pwm->hwpwm + 1), + MC33XS2410_PWM_CTRL1, + MC33XS2410_PWM_CTRL3 + }; + u64 period, duty_cycle; + int ret, rel_dc; + u16 rd_val[2]; + u8 wr_val[4]; + u8 mask; + + period = min(state->period, MC33XS2410_PWM_MAX_PERIOD(0)); + if (period < MC33XS2410_PWM_MIN_PERIOD) + return -EINVAL; + + ret = mc33xs2410_read_regs(spi, ®[2], MC33XS2410_FRAME_IN_DATA_RD, rd_val, 2); + if (ret < 0) + return ret; + + /* Frequency */ + wr_val[0] = mc33xs2410_pwm_get_freq(period); + /* Continue calculations with the possibly truncated period */ + period = mc33xs2410_pwm_get_period(wr_val[0]); + + /* Duty cycle */ + duty_cycle = min(period, state->duty_cycle); + rel_dc = div64_u64(duty_cycle * 256, period) - 1; + if (rel_dc >= 0) + wr_val[1] = rel_dc; + else if (state->polarity == PWM_POLARITY_NORMAL) + wr_val[1] = 0; + else + wr_val[1] = 255; + + /* Polarity */ + mask = MC33XS2410_PWM_CTRL1_POL_INV(pwm->hwpwm + 1); + if (state->polarity == PWM_POLARITY_INVERSED && rel_dc >= 0) + wr_val[2] = rd_val[0] | mask; + else + wr_val[2] = rd_val[0] & ~mask; + + /* Enable */ + mask = MC33XS2410_PWM_CTRL3_EN(pwm->hwpwm + 1); + if (state->enabled && + !(state->polarity == PWM_POLARITY_NORMAL && rel_dc < 0)) + wr_val[3] = rd_val[1] | mask; + else + wr_val[3] = rd_val[1] & ~mask; + + return mc33xs2410_write_regs(spi, reg, wr_val, 4); +} + +static int mc33xs2410_pwm_get_state(struct pwm_chip *chip, + struct pwm_device *pwm, + struct pwm_state *state) +{ + struct spi_device *spi = pwmchip_get_drvdata(chip); + u8 reg[4] = { + MC33XS2410_PWM_FREQ(pwm->hwpwm + 1), + MC33XS2410_PWM_DC(pwm->hwpwm + 1), + MC33XS2410_PWM_CTRL1, + MC33XS2410_PWM_CTRL3, + }; + u16 val[4]; + int ret; + + ret = mc33xs2410_read_regs(spi, reg, MC33XS2410_FRAME_IN_DATA_RD, val, + ARRAY_SIZE(reg)); + if (ret < 0) + return ret; + + state->period = mc33xs2410_pwm_get_period(val[0]); + state->polarity = (val[2] & MC33XS2410_PWM_CTRL1_POL_INV(pwm->hwpwm + 1)) ? + PWM_POLARITY_INVERSED : PWM_POLARITY_NORMAL; + state->enabled = !!(val[3] & MC33XS2410_PWM_CTRL3_EN(pwm->hwpwm + 1)); + state->duty_cycle = DIV_ROUND_UP_ULL((val[1] + 1) * state->period, 256); + + return 0; +} + +static const struct pwm_ops mc33xs2410_pwm_ops = { + .apply = mc33xs2410_pwm_apply, + .get_state = mc33xs2410_pwm_get_state, +}; + +static int mc33xs2410_reset(struct device *dev) +{ + struct gpio_desc *reset_gpio; + + reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR_OR_NULL(reset_gpio)) + return PTR_ERR_OR_ZERO(reset_gpio); + + /* Wake-up time */ + fsleep(10000); + + return 0; +} + +static int mc33xs2410_probe(struct spi_device *spi) +{ + struct device *dev = &spi->dev; + struct pwm_chip *chip; + int ret; + + chip = devm_pwmchip_alloc(dev, 4, 0); + if (IS_ERR(chip)) + return PTR_ERR(chip); + + spi->bits_per_word = 16; + spi->mode |= SPI_CS_WORD; + ret = spi_setup(spi); + if (ret < 0) + return ret; + + pwmchip_set_drvdata(chip, spi); + chip->ops = &mc33xs2410_pwm_ops; + + /* + * Deasserts the reset of the device. Shouldn't change the output signal + * if the device was setup prior to probing. + */ + ret = mc33xs2410_reset(dev); + if (ret) + return ret; + + /* + * Disable watchdog and keep in mind that the watchdog won't trigger a + * reset of the machine when running into an timeout, instead the + * control over the outputs is handed over to the INx input logic + * signals of the device. Disabling it here just deactivates this + * feature until a proper solution is found. + */ + ret = mc33xs2410_write_reg(spi, MC33XS2410_WDT, 0x0); + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to disable watchdog\n"); + + /* Transition to normal mode */ + ret = mc33xs2410_modify_reg(spi, MC33XS2410_GLB_CTRL, + MC33XS2410_GLB_CTRL_MODE, + MC33XS2410_GLB_CTRL_MODE_NORMAL); + if (ret < 0) + return dev_err_probe(dev, ret, + "Failed to transition to normal mode\n"); + + ret = devm_pwmchip_add(dev, chip); + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to add pwm chip\n"); + + return 0; +} + +static const struct spi_device_id mc33xs2410_spi_id[] = { + { "mc33xs2410" }, + { } +}; +MODULE_DEVICE_TABLE(spi, mc33xs2410_spi_id); + +static const struct of_device_id mc33xs2410_of_match[] = { + { .compatible = "nxp,mc33xs2410" }, + { } +}; +MODULE_DEVICE_TABLE(of, mc33xs2410_of_match); + +static struct spi_driver mc33xs2410_driver = { + .driver = { + .name = "mc33xs2410-pwm", + .of_match_table = mc33xs2410_of_match, + }, + .probe = mc33xs2410_probe, + .id_table = mc33xs2410_spi_id, +}; +module_spi_driver(mc33xs2410_driver); + +MODULE_DESCRIPTION("NXP MC33XS2410 high-side switch driver"); +MODULE_AUTHOR("Dimitri Fedrau <dimitri.fedrau@liebherr.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/pwm/pwm-meson.c b/drivers/pwm/pwm-meson.c index 98e6c1533312..8c6bf3d49753 100644 --- a/drivers/pwm/pwm-meson.c +++ b/drivers/pwm/pwm-meson.c @@ -6,7 +6,7 @@ * PWM output is achieved by calculating a clock that permits calculating * two periods (low and high). The counter then has to be set to switch after * N cycles for the first half period. - * The hardware has no "polarity" setting. This driver reverses the period + * Partly the hardware has no "polarity" setting. This driver reverses the period * cycles (the low length is inverted with the high length) for * PWM_POLARITY_INVERSED. This means that .get_state cannot read the polarity * from the hardware. @@ -56,6 +56,10 @@ #define MISC_B_CLK_SEL_SHIFT 6 #define MISC_A_CLK_SEL_SHIFT 4 #define MISC_CLK_SEL_MASK 0x3 +#define MISC_B_CONSTANT_EN BIT(29) +#define MISC_A_CONSTANT_EN BIT(28) +#define MISC_B_INVERT_EN BIT(27) +#define MISC_A_INVERT_EN BIT(26) #define MISC_B_EN BIT(1) #define MISC_A_EN BIT(0) @@ -68,6 +72,8 @@ static struct meson_pwm_channel_data { u8 clk_div_shift; u8 clk_en_shift; u32 pwm_en_mask; + u32 const_en_mask; + u32 inv_en_mask; } meson_pwm_per_channel_data[MESON_NUM_PWMS] = { { .reg_offset = REG_PWM_A, @@ -75,6 +81,8 @@ static struct meson_pwm_channel_data { .clk_div_shift = MISC_A_CLK_DIV_SHIFT, .clk_en_shift = MISC_A_CLK_EN_SHIFT, .pwm_en_mask = MISC_A_EN, + .const_en_mask = MISC_A_CONSTANT_EN, + .inv_en_mask = MISC_A_INVERT_EN, }, { .reg_offset = REG_PWM_B, @@ -82,6 +90,8 @@ static struct meson_pwm_channel_data { .clk_div_shift = MISC_B_CLK_DIV_SHIFT, .clk_en_shift = MISC_B_CLK_EN_SHIFT, .pwm_en_mask = MISC_B_EN, + .const_en_mask = MISC_B_CONSTANT_EN, + .inv_en_mask = MISC_B_INVERT_EN, } }; @@ -89,6 +99,8 @@ struct meson_pwm_channel { unsigned long rate; unsigned int hi; unsigned int lo; + bool constant; + bool inverted; struct clk_mux mux; struct clk_divider div; @@ -99,6 +111,8 @@ struct meson_pwm_channel { struct meson_pwm_data { const char *const parent_names[MESON_NUM_MUX_PARENTS]; int (*channels_init)(struct pwm_chip *chip); + bool has_constant; + bool has_polarity; }; struct meson_pwm { @@ -160,7 +174,7 @@ static int meson_pwm_calc(struct pwm_chip *chip, struct pwm_device *pwm, * Fixing this needs some care however as some machines might rely on * this. */ - if (state->polarity == PWM_POLARITY_INVERSED) + if (state->polarity == PWM_POLARITY_INVERSED && !meson->data->has_polarity) duty = period - duty; freq = div64_u64(NSEC_PER_SEC * 0xffffULL, period); @@ -187,9 +201,11 @@ static int meson_pwm_calc(struct pwm_chip *chip, struct pwm_device *pwm, if (duty == period) { channel->hi = cnt; channel->lo = 0; + channel->constant = true; } else if (duty == 0) { channel->hi = 0; channel->lo = cnt; + channel->constant = true; } else { duty_cnt = mul_u64_u64_div_u64(fin_freq, duty, NSEC_PER_SEC); @@ -197,6 +213,7 @@ static int meson_pwm_calc(struct pwm_chip *chip, struct pwm_device *pwm, channel->hi = duty_cnt; channel->lo = cnt - duty_cnt; + channel->constant = false; } channel->rate = fin_freq; @@ -227,6 +244,19 @@ static void meson_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) value = readl(meson->base + REG_MISC_AB); value |= channel_data->pwm_en_mask; + + if (meson->data->has_constant) { + value &= ~channel_data->const_en_mask; + if (channel->constant) + value |= channel_data->const_en_mask; + } + + if (meson->data->has_polarity) { + value &= ~channel_data->inv_en_mask; + if (channel->inverted) + value |= channel_data->inv_en_mask; + } + writel(value, meson->base + REG_MISC_AB); spin_unlock_irqrestore(&meson->lock, flags); @@ -235,13 +265,24 @@ static void meson_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) static void meson_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) { struct meson_pwm *meson = to_meson_pwm(chip); + struct meson_pwm_channel *channel = &meson->channels[pwm->hwpwm]; + struct meson_pwm_channel_data *channel_data; unsigned long flags; u32 value; + channel_data = &meson_pwm_per_channel_data[pwm->hwpwm]; + spin_lock_irqsave(&meson->lock, flags); value = readl(meson->base + REG_MISC_AB); - value &= ~meson_pwm_per_channel_data[pwm->hwpwm].pwm_en_mask; + value &= ~channel_data->pwm_en_mask; + + if (meson->data->has_polarity) { + value &= ~channel_data->inv_en_mask; + if (channel->inverted) + value |= channel_data->inv_en_mask; + } + writel(value, meson->base + REG_MISC_AB); spin_unlock_irqrestore(&meson->lock, flags); @@ -254,10 +295,12 @@ static int meson_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, struct meson_pwm_channel *channel = &meson->channels[pwm->hwpwm]; int err = 0; + channel->inverted = (state->polarity == PWM_POLARITY_INVERSED); + if (!state->enabled) { - if (state->polarity == PWM_POLARITY_INVERSED) { + if (channel->inverted && !meson->data->has_polarity) { /* - * This IP block revision doesn't have an "always high" + * Some of IP block revisions don't have an "always high" * setting which we can use for "inverted disabled". * Instead we achieve this by setting mux parent with * highest rate and minimum divider value, resulting @@ -271,6 +314,7 @@ static int meson_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, channel->rate = ULONG_MAX; channel->hi = ~0; channel->lo = 0; + channel->constant = true; meson_pwm_enable(chip, pwm); } else { @@ -287,21 +331,9 @@ static int meson_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, return 0; } -static u64 meson_pwm_cnt_to_ns(struct pwm_chip *chip, struct pwm_device *pwm, - u32 cnt) +static u64 meson_pwm_cnt_to_ns(unsigned long fin_freq, u32 cnt) { - struct meson_pwm *meson = to_meson_pwm(chip); - struct meson_pwm_channel *channel; - unsigned long fin_freq; - - /* to_meson_pwm() can only be used after .get_state() is called */ - channel = &meson->channels[pwm->hwpwm]; - - fin_freq = clk_get_rate(channel->clk); - if (fin_freq == 0) - return 0; - - return div64_ul(NSEC_PER_SEC * (u64)cnt, fin_freq); + return fin_freq ? div64_ul(NSEC_PER_SEC * (u64)cnt, fin_freq) : 0; } static int meson_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm, @@ -309,23 +341,27 @@ static int meson_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm, { struct meson_pwm *meson = to_meson_pwm(chip); struct meson_pwm_channel_data *channel_data; - struct meson_pwm_channel *channel; + unsigned long fin_freq; + unsigned int hi, lo; u32 value; - channel = &meson->channels[pwm->hwpwm]; channel_data = &meson_pwm_per_channel_data[pwm->hwpwm]; + fin_freq = clk_get_rate(meson->channels[pwm->hwpwm].clk); value = readl(meson->base + REG_MISC_AB); state->enabled = value & channel_data->pwm_en_mask; - value = readl(meson->base + channel_data->reg_offset); - channel->lo = FIELD_GET(PWM_LOW_MASK, value); - channel->hi = FIELD_GET(PWM_HIGH_MASK, value); + if (meson->data->has_polarity && (value & channel_data->inv_en_mask)) + state->polarity = PWM_POLARITY_INVERSED; + else + state->polarity = PWM_POLARITY_NORMAL; - state->period = meson_pwm_cnt_to_ns(chip, pwm, channel->lo + channel->hi); - state->duty_cycle = meson_pwm_cnt_to_ns(chip, pwm, channel->hi); + value = readl(meson->base + channel_data->reg_offset); + lo = FIELD_GET(PWM_LOW_MASK, value); + hi = FIELD_GET(PWM_HIGH_MASK, value); - state->polarity = PWM_POLARITY_NORMAL; + state->period = meson_pwm_cnt_to_ns(fin_freq, lo + hi); + state->duty_cycle = meson_pwm_cnt_to_ns(fin_freq, hi); return 0; } @@ -508,29 +544,52 @@ static const struct meson_pwm_data pwm_gxbb_ao_data = { static const struct meson_pwm_data pwm_axg_ee_data = { .parent_names = { "xtal", "fclk_div5", "fclk_div4", "fclk_div3" }, .channels_init = meson_pwm_init_channels_meson8b_legacy, + .has_constant = true, + .has_polarity = true, }; static const struct meson_pwm_data pwm_axg_ao_data = { .parent_names = { "xtal", "axg_ao_clk81", "fclk_div4", "fclk_div5" }, .channels_init = meson_pwm_init_channels_meson8b_legacy, + .has_constant = true, + .has_polarity = true, +}; + +static const struct meson_pwm_data pwm_g12a_ee_data = { + .parent_names = { "xtal", NULL, "fclk_div4", "fclk_div3" }, + .channels_init = meson_pwm_init_channels_meson8b_legacy, + .has_constant = true, + .has_polarity = true, }; static const struct meson_pwm_data pwm_g12a_ao_ab_data = { .parent_names = { "xtal", "g12a_ao_clk81", "fclk_div4", "fclk_div5" }, .channels_init = meson_pwm_init_channels_meson8b_legacy, + .has_constant = true, + .has_polarity = true, }; static const struct meson_pwm_data pwm_g12a_ao_cd_data = { .parent_names = { "xtal", "g12a_ao_clk81", NULL, NULL }, .channels_init = meson_pwm_init_channels_meson8b_legacy, + .has_constant = true, + .has_polarity = true, }; 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_meson_axg_v2_data = { + .channels_init = meson_pwm_init_channels_meson8b_v2, + .has_constant = true, + .has_polarity = true, +}; + static const struct meson_pwm_data pwm_s4_data = { .channels_init = meson_pwm_init_channels_s4, + .has_constant = true, + .has_polarity = true, }; static const struct of_device_id meson_pwm_matches[] = { @@ -538,6 +597,14 @@ static const struct of_device_id meson_pwm_matches[] = { .compatible = "amlogic,meson8-pwm-v2", .data = &pwm_meson8_v2_data }, + { + .compatible = "amlogic,meson-axg-pwm-v2", + .data = &pwm_meson_axg_v2_data + }, + { + .compatible = "amlogic,meson-g12-pwm-v2", + .data = &pwm_meson_axg_v2_data + }, /* The following compatibles are obsolete */ { .compatible = "amlogic,meson8b-pwm", @@ -561,7 +628,7 @@ static const struct of_device_id meson_pwm_matches[] = { }, { .compatible = "amlogic,meson-g12a-ee-pwm", - .data = &pwm_meson8b_data + .data = &pwm_g12a_ee_data }, { .compatible = "amlogic,meson-g12a-ao-pwm-ab", diff --git a/drivers/pwm/pwm-pca9685.c b/drivers/pwm/pwm-pca9685.c index 5162f3991644..eb03ccd5b688 100644 --- a/drivers/pwm/pwm-pca9685.c +++ b/drivers/pwm/pwm-pca9685.c @@ -263,12 +263,14 @@ static int pca9685_pwm_gpio_get(struct gpio_chip *gpio, unsigned int offset) return pca9685_pwm_get_duty(chip, offset) != 0; } -static void pca9685_pwm_gpio_set(struct gpio_chip *gpio, unsigned int offset, - int value) +static int pca9685_pwm_gpio_set(struct gpio_chip *gpio, unsigned int offset, + int value) { struct pwm_chip *chip = gpiochip_get_data(gpio); pca9685_pwm_set_duty(chip, offset, value ? PCA9685_COUNTER_RANGE : 0); + + return 0; } static void pca9685_pwm_gpio_free(struct gpio_chip *gpio, unsigned int offset) @@ -321,7 +323,7 @@ static int pca9685_pwm_gpio_probe(struct pwm_chip *chip) pca->gpio.direction_input = pca9685_pwm_gpio_direction_input; pca->gpio.direction_output = pca9685_pwm_gpio_direction_output; pca->gpio.get = pca9685_pwm_gpio_get; - pca->gpio.set = pca9685_pwm_gpio_set; + pca->gpio.set_rv = pca9685_pwm_gpio_set; pca->gpio.base = -1; pca->gpio.ngpio = PCA9685_MAXCHAN; pca->gpio.can_sleep = true; diff --git a/drivers/pwm/pwm-pxa.c b/drivers/pwm/pwm-pxa.c index 430bd6a709e9..8a4a3d2df30d 100644 --- a/drivers/pwm/pwm-pxa.c +++ b/drivers/pwm/pwm-pxa.c @@ -160,24 +160,24 @@ static int pwm_probe(struct platform_device *pdev) const struct platform_device_id *id = platform_get_device_id(pdev); struct pwm_chip *chip; struct pxa_pwm_chip *pc; + struct device *dev = &pdev->dev; int ret = 0; if (IS_ENABLED(CONFIG_OF) && id == NULL) - id = of_device_get_match_data(&pdev->dev); + id = of_device_get_match_data(dev); if (id == NULL) return -EINVAL; - chip = devm_pwmchip_alloc(&pdev->dev, - (id->driver_data & HAS_SECONDARY_PWM) ? 2 : 1, + chip = devm_pwmchip_alloc(dev, (id->driver_data & HAS_SECONDARY_PWM) ? 2 : 1, sizeof(*pc)); if (IS_ERR(chip)) return PTR_ERR(chip); pc = to_pxa_pwm_chip(chip); - pc->clk = devm_clk_get(&pdev->dev, NULL); + pc->clk = devm_clk_get(dev, NULL); if (IS_ERR(pc->clk)) - return PTR_ERR(pc->clk); + return dev_err_probe(dev, PTR_ERR(pc->clk), "Failed to get clock\n"); chip->ops = &pxa_pwm_ops; @@ -188,11 +188,9 @@ static int pwm_probe(struct platform_device *pdev) if (IS_ERR(pc->mmio_base)) return PTR_ERR(pc->mmio_base); - ret = devm_pwmchip_add(&pdev->dev, chip); - if (ret < 0) { - dev_err(&pdev->dev, "pwmchip_add() failed: %d\n", ret); - return ret; - } + ret = devm_pwmchip_add(dev, chip); + if (ret < 0) + return dev_err_probe(dev, ret, "pwmchip_add() failed\n"); return 0; } diff --git a/drivers/pwm/pwm-rzg2l-gpt.c b/drivers/pwm/pwm-rzg2l-gpt.c new file mode 100644 index 000000000000..360c8bf3b190 --- /dev/null +++ b/drivers/pwm/pwm-rzg2l-gpt.c @@ -0,0 +1,447 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Renesas RZ/G2L General PWM Timer (GPT) driver + * + * Copyright (C) 2025 Renesas Electronics Corporation + * + * Hardware manual for this IP can be found here + * https://www.renesas.com/eu/en/document/mah/rzg2l-group-rzg2lc-group-users-manual-hardware-0?language=en + * + * Limitations: + * - Counter must be stopped before modifying Mode and Prescaler. + * - When PWM is disabled, the output is driven to inactive. + * - While the hardware supports both polarities, the driver (for now) + * only handles normal polarity. + * - General PWM Timer (GPT) has 8 HW channels for PWM operations and + * each HW channel have 2 IOs. + * - Each IO is modelled as an independent PWM channel. + * - When both channels are used, disabling the channel on one stops the + * other. + * - When both channels are used, the period of both IOs in the HW channel + * must be same (for now). + */ + +#include <linux/bitfield.h> +#include <linux/clk.h> +#include <linux/io.h> +#include <linux/limits.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/pwm.h> +#include <linux/reset.h> +#include <linux/time.h> +#include <linux/units.h> + +#define RZG2L_GET_CH(hwpwm) ((hwpwm) / 2) +#define RZG2L_GET_CH_OFFS(ch) (0x100 * (ch)) + +#define RZG2L_GTCR(ch) (0x2c + RZG2L_GET_CH_OFFS(ch)) +#define RZG2L_GTUDDTYC(ch) (0x30 + RZG2L_GET_CH_OFFS(ch)) +#define RZG2L_GTIOR(ch) (0x34 + RZG2L_GET_CH_OFFS(ch)) +#define RZG2L_GTBER(ch) (0x40 + RZG2L_GET_CH_OFFS(ch)) +#define RZG2L_GTCNT(ch) (0x48 + RZG2L_GET_CH_OFFS(ch)) +#define RZG2L_GTCCR(ch, sub_ch) (0x4c + RZG2L_GET_CH_OFFS(ch) + 4 * (sub_ch)) +#define RZG2L_GTPR(ch) (0x64 + RZG2L_GET_CH_OFFS(ch)) + +#define RZG2L_GTCR_CST BIT(0) +#define RZG2L_GTCR_MD GENMASK(18, 16) +#define RZG2L_GTCR_TPCS GENMASK(26, 24) + +#define RZG2L_GTCR_MD_SAW_WAVE_PWM_MODE FIELD_PREP(RZG2L_GTCR_MD, 0) + +#define RZG2L_GTUDDTYC_UP BIT(0) +#define RZG2L_GTUDDTYC_UDF BIT(1) +#define RZG2L_GTUDDTYC_UP_COUNTING (RZG2L_GTUDDTYC_UP | RZG2L_GTUDDTYC_UDF) + +#define RZG2L_GTIOR_GTIOA GENMASK(4, 0) +#define RZG2L_GTIOR_GTIOB GENMASK(20, 16) +#define RZG2L_GTIOR_GTIOx(sub_ch) ((sub_ch) ? RZG2L_GTIOR_GTIOB : RZG2L_GTIOR_GTIOA) +#define RZG2L_GTIOR_OAE BIT(8) +#define RZG2L_GTIOR_OBE BIT(24) +#define RZG2L_GTIOR_OxE(sub_ch) ((sub_ch) ? RZG2L_GTIOR_OBE : RZG2L_GTIOR_OAE) + +#define RZG2L_INIT_OUT_HI_OUT_HI_END_TOGGLE 0x1b +#define RZG2L_GTIOR_GTIOA_OUT_HI_END_TOGGLE_CMP_MATCH \ + (RZG2L_INIT_OUT_HI_OUT_HI_END_TOGGLE | RZG2L_GTIOR_OAE) +#define RZG2L_GTIOR_GTIOB_OUT_HI_END_TOGGLE_CMP_MATCH \ + (FIELD_PREP(RZG2L_GTIOR_GTIOB, RZG2L_INIT_OUT_HI_OUT_HI_END_TOGGLE) | RZG2L_GTIOR_OBE) + +#define RZG2L_GTIOR_GTIOx_OUT_HI_END_TOGGLE_CMP_MATCH(sub_ch) \ + ((sub_ch) ? RZG2L_GTIOR_GTIOB_OUT_HI_END_TOGGLE_CMP_MATCH : \ + RZG2L_GTIOR_GTIOA_OUT_HI_END_TOGGLE_CMP_MATCH) + +#define RZG2L_MAX_HW_CHANNELS 8 +#define RZG2L_CHANNELS_PER_IO 2 +#define RZG2L_MAX_PWM_CHANNELS (RZG2L_MAX_HW_CHANNELS * RZG2L_CHANNELS_PER_IO) +#define RZG2L_MAX_SCALE_FACTOR 1024 +#define RZG2L_MAX_TICKS ((u64)U32_MAX * RZG2L_MAX_SCALE_FACTOR) + +struct rzg2l_gpt_chip { + void __iomem *mmio; + struct mutex lock; /* lock to protect shared channel resources */ + unsigned long rate_khz; + u32 period_ticks[RZG2L_MAX_HW_CHANNELS]; + u32 channel_request_count[RZG2L_MAX_HW_CHANNELS]; + u32 channel_enable_count[RZG2L_MAX_HW_CHANNELS]; +}; + +static inline struct rzg2l_gpt_chip *to_rzg2l_gpt_chip(struct pwm_chip *chip) +{ + return pwmchip_get_drvdata(chip); +} + +static inline unsigned int rzg2l_gpt_subchannel(unsigned int hwpwm) +{ + return hwpwm & 0x1; +} + +static void rzg2l_gpt_write(struct rzg2l_gpt_chip *rzg2l_gpt, u32 reg, u32 data) +{ + writel(data, rzg2l_gpt->mmio + reg); +} + +static u32 rzg2l_gpt_read(struct rzg2l_gpt_chip *rzg2l_gpt, u32 reg) +{ + return readl(rzg2l_gpt->mmio + reg); +} + +static void rzg2l_gpt_modify(struct rzg2l_gpt_chip *rzg2l_gpt, u32 reg, u32 clr, + u32 set) +{ + rzg2l_gpt_write(rzg2l_gpt, reg, + (rzg2l_gpt_read(rzg2l_gpt, reg) & ~clr) | set); +} + +static u8 rzg2l_gpt_calculate_prescale(struct rzg2l_gpt_chip *rzg2l_gpt, + u64 period_ticks) +{ + u32 prescaled_period_ticks; + u8 prescale; + + prescaled_period_ticks = period_ticks >> 32; + if (prescaled_period_ticks >= 256) + prescale = 5; + else + prescale = (fls(prescaled_period_ticks) + 1) / 2; + + return prescale; +} + +static int rzg2l_gpt_request(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct rzg2l_gpt_chip *rzg2l_gpt = to_rzg2l_gpt_chip(chip); + u32 ch = RZG2L_GET_CH(pwm->hwpwm); + + guard(mutex)(&rzg2l_gpt->lock); + rzg2l_gpt->channel_request_count[ch]++; + + return 0; +} + +static void rzg2l_gpt_free(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct rzg2l_gpt_chip *rzg2l_gpt = to_rzg2l_gpt_chip(chip); + u32 ch = RZG2L_GET_CH(pwm->hwpwm); + + guard(mutex)(&rzg2l_gpt->lock); + rzg2l_gpt->channel_request_count[ch]--; +} + +static bool rzg2l_gpt_is_ch_enabled(struct rzg2l_gpt_chip *rzg2l_gpt, u8 hwpwm) +{ + u8 ch = RZG2L_GET_CH(hwpwm); + u32 val; + + val = rzg2l_gpt_read(rzg2l_gpt, RZG2L_GTCR(ch)); + if (!(val & RZG2L_GTCR_CST)) + return false; + + val = rzg2l_gpt_read(rzg2l_gpt, RZG2L_GTIOR(ch)); + + return val & RZG2L_GTIOR_OxE(rzg2l_gpt_subchannel(hwpwm)); +} + +/* Caller holds the lock while calling rzg2l_gpt_enable() */ +static void rzg2l_gpt_enable(struct rzg2l_gpt_chip *rzg2l_gpt, + struct pwm_device *pwm) +{ + u8 sub_ch = rzg2l_gpt_subchannel(pwm->hwpwm); + u32 val = RZG2L_GTIOR_GTIOx(sub_ch) | RZG2L_GTIOR_OxE(sub_ch); + u8 ch = RZG2L_GET_CH(pwm->hwpwm); + + /* Enable pin output */ + rzg2l_gpt_modify(rzg2l_gpt, RZG2L_GTIOR(ch), val, + RZG2L_GTIOR_GTIOx_OUT_HI_END_TOGGLE_CMP_MATCH(sub_ch)); + + if (!rzg2l_gpt->channel_enable_count[ch]) + rzg2l_gpt_modify(rzg2l_gpt, RZG2L_GTCR(ch), 0, RZG2L_GTCR_CST); + + rzg2l_gpt->channel_enable_count[ch]++; +} + +/* Caller holds the lock while calling rzg2l_gpt_disable() */ +static void rzg2l_gpt_disable(struct rzg2l_gpt_chip *rzg2l_gpt, + struct pwm_device *pwm) +{ + u8 sub_ch = rzg2l_gpt_subchannel(pwm->hwpwm); + u8 ch = RZG2L_GET_CH(pwm->hwpwm); + + /* Stop count, Output low on GTIOCx pin when counting stops */ + rzg2l_gpt->channel_enable_count[ch]--; + + if (!rzg2l_gpt->channel_enable_count[ch]) + rzg2l_gpt_modify(rzg2l_gpt, RZG2L_GTCR(ch), RZG2L_GTCR_CST, 0); + + /* Disable pin output */ + rzg2l_gpt_modify(rzg2l_gpt, RZG2L_GTIOR(ch), RZG2L_GTIOR_OxE(sub_ch), 0); +} + +static u64 rzg2l_gpt_calculate_period_or_duty(struct rzg2l_gpt_chip *rzg2l_gpt, + u32 val, u8 prescale) +{ + u64 tmp; + + /* + * The calculation doesn't overflow an u64 because prescale ≤ 5 and so + * tmp = val << (2 * prescale) * USEC_PER_SEC + * < 2^32 * 2^10 * 10^6 + * < 2^32 * 2^10 * 2^20 + * = 2^62 + */ + tmp = (u64)val << (2 * prescale); + tmp *= USEC_PER_SEC; + + return DIV64_U64_ROUND_UP(tmp, rzg2l_gpt->rate_khz); +} + +static int rzg2l_gpt_get_state(struct pwm_chip *chip, struct pwm_device *pwm, + struct pwm_state *state) +{ + struct rzg2l_gpt_chip *rzg2l_gpt = to_rzg2l_gpt_chip(chip); + + state->enabled = rzg2l_gpt_is_ch_enabled(rzg2l_gpt, pwm->hwpwm); + if (state->enabled) { + u32 sub_ch = rzg2l_gpt_subchannel(pwm->hwpwm); + u32 ch = RZG2L_GET_CH(pwm->hwpwm); + u8 prescale; + u32 val; + + val = rzg2l_gpt_read(rzg2l_gpt, RZG2L_GTCR(ch)); + prescale = FIELD_GET(RZG2L_GTCR_TPCS, val); + + val = rzg2l_gpt_read(rzg2l_gpt, RZG2L_GTPR(ch)); + state->period = rzg2l_gpt_calculate_period_or_duty(rzg2l_gpt, val, prescale); + + val = rzg2l_gpt_read(rzg2l_gpt, RZG2L_GTCCR(ch, sub_ch)); + state->duty_cycle = rzg2l_gpt_calculate_period_or_duty(rzg2l_gpt, val, prescale); + if (state->duty_cycle > state->period) + state->duty_cycle = state->period; + } + + state->polarity = PWM_POLARITY_NORMAL; + + return 0; +} + +static u32 rzg2l_gpt_calculate_pv_or_dc(u64 period_or_duty_cycle, u8 prescale) +{ + return min_t(u64, DIV_ROUND_DOWN_ULL(period_or_duty_cycle, 1 << (2 * prescale)), + U32_MAX); +} + +/* Caller holds the lock while calling rzg2l_gpt_config() */ +static int rzg2l_gpt_config(struct pwm_chip *chip, struct pwm_device *pwm, + const struct pwm_state *state) +{ + struct rzg2l_gpt_chip *rzg2l_gpt = to_rzg2l_gpt_chip(chip); + u8 sub_ch = rzg2l_gpt_subchannel(pwm->hwpwm); + u8 ch = RZG2L_GET_CH(pwm->hwpwm); + u64 period_ticks, duty_ticks; + unsigned long pv, dc; + u8 prescale; + + /* Limit period/duty cycle to max value supported by the HW */ + period_ticks = mul_u64_u64_div_u64(state->period, rzg2l_gpt->rate_khz, USEC_PER_SEC); + if (period_ticks > RZG2L_MAX_TICKS) + period_ticks = RZG2L_MAX_TICKS; + /* + * GPT counter is shared by the two IOs of a single channel, so + * prescale and period can NOT be modified when there are multiple IOs + * in use with different settings. + */ + if (rzg2l_gpt->channel_request_count[ch] > 1) { + if (period_ticks < rzg2l_gpt->period_ticks[ch]) + return -EBUSY; + else + period_ticks = rzg2l_gpt->period_ticks[ch]; + } + + prescale = rzg2l_gpt_calculate_prescale(rzg2l_gpt, period_ticks); + pv = rzg2l_gpt_calculate_pv_or_dc(period_ticks, prescale); + + duty_ticks = mul_u64_u64_div_u64(state->duty_cycle, rzg2l_gpt->rate_khz, USEC_PER_SEC); + if (duty_ticks > period_ticks) + duty_ticks = period_ticks; + dc = rzg2l_gpt_calculate_pv_or_dc(duty_ticks, prescale); + + /* + * GPT counter is shared by multiple channels, we cache the period ticks + * from the first enabled channel and use the same value for both + * channels. + */ + rzg2l_gpt->period_ticks[ch] = period_ticks; + + /* + * Counter must be stopped before modifying mode, prescaler, timer + * counter and buffer enable registers. These registers are shared + * between both channels. So allow updating these registers only for the + * first enabled channel. + */ + if (rzg2l_gpt->channel_enable_count[ch] <= 1) { + rzg2l_gpt_modify(rzg2l_gpt, RZG2L_GTCR(ch), RZG2L_GTCR_CST, 0); + + /* GPT set operating mode (saw-wave up-counting) */ + rzg2l_gpt_modify(rzg2l_gpt, RZG2L_GTCR(ch), RZG2L_GTCR_MD, + RZG2L_GTCR_MD_SAW_WAVE_PWM_MODE); + + /* Set count direction */ + rzg2l_gpt_write(rzg2l_gpt, RZG2L_GTUDDTYC(ch), RZG2L_GTUDDTYC_UP_COUNTING); + + /* Select count clock */ + rzg2l_gpt_modify(rzg2l_gpt, RZG2L_GTCR(ch), RZG2L_GTCR_TPCS, + FIELD_PREP(RZG2L_GTCR_TPCS, prescale)); + + /* Set period */ + rzg2l_gpt_write(rzg2l_gpt, RZG2L_GTPR(ch), pv); + } + + /* Set duty cycle */ + rzg2l_gpt_write(rzg2l_gpt, RZG2L_GTCCR(ch, sub_ch), dc); + + if (rzg2l_gpt->channel_enable_count[ch] <= 1) { + /* Set initial value for counter */ + rzg2l_gpt_write(rzg2l_gpt, RZG2L_GTCNT(ch), 0); + + /* Set no buffer operation */ + rzg2l_gpt_write(rzg2l_gpt, RZG2L_GTBER(ch), 0); + + /* Restart the counter after updating the registers */ + rzg2l_gpt_modify(rzg2l_gpt, RZG2L_GTCR(ch), + RZG2L_GTCR_CST, RZG2L_GTCR_CST); + } + + return 0; +} + +static int rzg2l_gpt_apply(struct pwm_chip *chip, struct pwm_device *pwm, + const struct pwm_state *state) +{ + struct rzg2l_gpt_chip *rzg2l_gpt = to_rzg2l_gpt_chip(chip); + bool enabled = pwm->state.enabled; + int ret; + + if (state->polarity != PWM_POLARITY_NORMAL) + return -EINVAL; + + guard(mutex)(&rzg2l_gpt->lock); + if (!state->enabled) { + if (enabled) + rzg2l_gpt_disable(rzg2l_gpt, pwm); + + return 0; + } + + ret = rzg2l_gpt_config(chip, pwm, state); + if (!ret && !enabled) + rzg2l_gpt_enable(rzg2l_gpt, pwm); + + return ret; +} + +static const struct pwm_ops rzg2l_gpt_ops = { + .request = rzg2l_gpt_request, + .free = rzg2l_gpt_free, + .get_state = rzg2l_gpt_get_state, + .apply = rzg2l_gpt_apply, +}; + +static int rzg2l_gpt_probe(struct platform_device *pdev) +{ + struct rzg2l_gpt_chip *rzg2l_gpt; + struct device *dev = &pdev->dev; + struct reset_control *rstc; + struct pwm_chip *chip; + unsigned long rate; + struct clk *clk; + int ret; + + chip = devm_pwmchip_alloc(dev, RZG2L_MAX_PWM_CHANNELS, sizeof(*rzg2l_gpt)); + if (IS_ERR(chip)) + return PTR_ERR(chip); + rzg2l_gpt = to_rzg2l_gpt_chip(chip); + + rzg2l_gpt->mmio = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(rzg2l_gpt->mmio)) + return PTR_ERR(rzg2l_gpt->mmio); + + rstc = devm_reset_control_get_exclusive_deasserted(dev, NULL); + if (IS_ERR(rstc)) + return dev_err_probe(dev, PTR_ERR(rstc), "Cannot deassert reset control\n"); + + clk = devm_clk_get_enabled(dev, NULL); + if (IS_ERR(clk)) + return dev_err_probe(dev, PTR_ERR(clk), "Cannot get clock\n"); + + ret = devm_clk_rate_exclusive_get(dev, clk); + if (ret) + return ret; + + rate = clk_get_rate(clk); + if (!rate) + return dev_err_probe(dev, -EINVAL, "The gpt clk rate is 0"); + + /* + * Refuse clk rates > 1 GHz to prevent overflow later for computing + * period and duty cycle. + */ + if (rate > NSEC_PER_SEC) + return dev_err_probe(dev, -EINVAL, "The gpt clk rate is > 1GHz"); + + /* + * Rate is in MHz and is always integer for peripheral clk + * 2^32 * 2^10 (prescalar) * 10^6 (rate_khz) < 2^64 + * So make sure rate is multiple of 1000. + */ + rzg2l_gpt->rate_khz = rate / KILO; + if (rzg2l_gpt->rate_khz * KILO != rate) + return dev_err_probe(dev, -EINVAL, "Rate is not multiple of 1000"); + + mutex_init(&rzg2l_gpt->lock); + + chip->ops = &rzg2l_gpt_ops; + ret = devm_pwmchip_add(dev, chip); + if (ret) + return dev_err_probe(dev, ret, "Failed to add PWM chip\n"); + + return 0; +} + +static const struct of_device_id rzg2l_gpt_of_table[] = { + { .compatible = "renesas,rzg2l-gpt", }, + { /* Sentinel */ } +}; +MODULE_DEVICE_TABLE(of, rzg2l_gpt_of_table); + +static struct platform_driver rzg2l_gpt_driver = { + .driver = { + .name = "pwm-rzg2l-gpt", + .of_match_table = rzg2l_gpt_of_table, + }, + .probe = rzg2l_gpt_probe, +}; +module_platform_driver(rzg2l_gpt_driver); + +MODULE_AUTHOR("Biju Das <biju.das.jz@bp.renesas.com>"); +MODULE_DESCRIPTION("Renesas RZ/G2L General PWM Timer (GPT) Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/pwm/pwm-stm32.c b/drivers/pwm/pwm-stm32.c index ec2c05c9ee7a..4b148f0afeb9 100644 --- a/drivers/pwm/pwm-stm32.c +++ b/drivers/pwm/pwm-stm32.c @@ -88,7 +88,7 @@ static int stm32_pwm_round_waveform_tohw(struct pwm_chip *chip, rate = clk_get_rate(priv->clk); - if (active_channels(priv) & ~(1 << ch * 4)) { + if (active_channels(priv) & ~TIM_CCER_CCxE(ch + 1)) { u64 arr; /* @@ -180,11 +180,11 @@ static int stm32_pwm_round_waveform_tohw(struct pwm_chip *chip, wfhw->ccr = min_t(u64, ccr, wfhw->arr + 1); +out: dev_dbg(&chip->dev, "pwm#%u: %lld/%lld [+%lld] @%lu -> CCER: %08x, PSC: %08x, ARR: %08x, CCR: %08x\n", pwm->hwpwm, wf->duty_length_ns, wf->period_length_ns, wf->duty_offset_ns, rate, wfhw->ccer, wfhw->psc, wfhw->arr, wfhw->ccr); -out: clk_disable(priv->clk); return ret; @@ -213,10 +213,10 @@ static int stm32_pwm_round_waveform_fromhw(struct pwm_chip *chip, { const struct stm32_pwm_waveform *wfhw = _wfhw; struct stm32_pwm *priv = to_stm32_pwm_dev(chip); + unsigned long rate = clk_get_rate(priv->clk); unsigned int ch = pwm->hwpwm; if (wfhw->ccer & TIM_CCER_CCxE(ch + 1)) { - unsigned long rate = clk_get_rate(priv->clk); u64 ccr_ns; /* The result doesn't overflow for rate >= 15259 */ @@ -236,17 +236,16 @@ static int stm32_pwm_round_waveform_fromhw(struct pwm_chip *chip, wf->duty_length_ns = ccr_ns; wf->duty_offset_ns = 0; } - - dev_dbg(&chip->dev, "pwm#%u: CCER: %08x, PSC: %08x, ARR: %08x, CCR: %08x @%lu -> %lld/%lld [+%lld]\n", - pwm->hwpwm, wfhw->ccer, wfhw->psc, wfhw->arr, wfhw->ccr, rate, - wf->duty_length_ns, wf->period_length_ns, wf->duty_offset_ns); - } else { *wf = (struct pwm_waveform){ .period_length_ns = 0, }; } + dev_dbg(&chip->dev, "pwm#%u: CCER: %08x, PSC: %08x, ARR: %08x, CCR: %08x @%lu -> %lld/%lld [+%lld]\n", + pwm->hwpwm, wfhw->ccer, wfhw->psc, wfhw->arr, wfhw->ccr, rate, + wf->duty_length_ns, wf->period_length_ns, wf->duty_offset_ns); + return 0; } diff --git a/drivers/soc/renesas/Kconfig b/drivers/soc/renesas/Kconfig index 49648cf28bd2..4990b85d7df7 100644 --- a/drivers/soc/renesas/Kconfig +++ b/drivers/soc/renesas/Kconfig @@ -65,17 +65,20 @@ if ARM && ARCH_RENESAS config ARCH_EMEV2 bool "ARM32 Platform support for Emma Mobile EV2" + default ARCH_RENESAS select HAVE_ARM_SCU if SMP select SYS_SUPPORTS_EM_STI config ARCH_R8A7794 bool "ARM32 Platform support for R-Car E2" + default ARCH_RENESAS select ARCH_RCAR_GEN2 select ARM_ERRATA_814220 select SYSC_R8A7794 config ARCH_R8A7779 bool "ARM32 Platform support for R-Car H1" + default ARCH_RENESAS select ARCH_RCAR_GEN1 select ARM_ERRATA_754322 select ARM_GLOBAL_TIMER @@ -85,6 +88,7 @@ config ARCH_R8A7779 config ARCH_R8A7790 bool "ARM32 Platform support for R-Car H2" + default ARCH_RENESAS select ARCH_RCAR_GEN2 select ARM_ERRATA_798181 if SMP select ARM_ERRATA_814220 @@ -93,11 +97,13 @@ config ARCH_R8A7790 config ARCH_R8A7778 bool "ARM32 Platform support for R-Car M1A" + default ARCH_RENESAS select ARCH_RCAR_GEN1 select ARM_ERRATA_754322 config ARCH_R8A7793 bool "ARM32 Platform support for R-Car M2-N" + default ARCH_RENESAS select ARCH_RCAR_GEN2 select ARM_ERRATA_798181 if SMP select I2C @@ -105,6 +111,7 @@ config ARCH_R8A7793 config ARCH_R8A7791 bool "ARM32 Platform support for R-Car M2-W" + default ARCH_RENESAS select ARCH_RCAR_GEN2 select ARM_ERRATA_798181 if SMP select I2C @@ -112,18 +119,21 @@ config ARCH_R8A7791 config ARCH_R8A7792 bool "ARM32 Platform support for R-Car V2H" + default ARCH_RENESAS select ARCH_RCAR_GEN2 select ARM_ERRATA_798181 if SMP select SYSC_R8A7792 config ARCH_R8A7740 bool "ARM32 Platform support for R-Mobile A1" + default ARCH_RENESAS select ARCH_RMOBILE select ARM_ERRATA_754322 select RENESAS_INTC_IRQPIN config ARCH_R8A73A4 bool "ARM32 Platform support for R-Mobile APE6" + default ARCH_RENESAS select ARCH_RMOBILE select ARM_ERRATA_798181 if SMP select ARM_ERRATA_814220 @@ -132,6 +142,7 @@ config ARCH_R8A73A4 config ARCH_R7S72100 bool "ARM32 Platform support for RZ/A1H" + default ARCH_RENESAS select ARM_ERRATA_754322 select PM select PM_GENERIC_DOMAINS @@ -141,6 +152,7 @@ config ARCH_R7S72100 config ARCH_R7S9210 bool "ARM32 Platform support for RZ/A2" + default ARCH_RENESAS select PM select PM_GENERIC_DOMAINS select RENESAS_OSTM @@ -148,18 +160,21 @@ config ARCH_R7S9210 config ARCH_R8A77470 bool "ARM32 Platform support for RZ/G1C" + default ARCH_RENESAS select ARCH_RCAR_GEN2 select ARM_ERRATA_814220 select SYSC_R8A77470 config ARCH_R8A7745 bool "ARM32 Platform support for RZ/G1E" + default ARCH_RENESAS select ARCH_RCAR_GEN2 select ARM_ERRATA_814220 select SYSC_R8A7745 config ARCH_R8A7742 bool "ARM32 Platform support for RZ/G1H" + default ARCH_RENESAS select ARCH_RCAR_GEN2 select ARM_ERRATA_798181 if SMP select ARM_ERRATA_814220 @@ -167,23 +182,27 @@ config ARCH_R8A7742 config ARCH_R8A7743 bool "ARM32 Platform support for RZ/G1M" + default ARCH_RENESAS select ARCH_RCAR_GEN2 select ARM_ERRATA_798181 if SMP select SYSC_R8A7743 config ARCH_R8A7744 bool "ARM32 Platform support for RZ/G1N" + default ARCH_RENESAS select ARCH_RCAR_GEN2 select ARM_ERRATA_798181 if SMP select SYSC_R8A7743 config ARCH_R9A06G032 bool "ARM32 Platform support for RZ/N1D" + default ARCH_RENESAS select ARCH_RZN1 select ARM_ERRATA_814220 config ARCH_SH73A0 bool "ARM32 Platform support for SH-Mobile AG5" + default ARCH_RENESAS select ARCH_RMOBILE select ARM_ERRATA_754322 select ARM_GLOBAL_TIMER @@ -197,6 +216,7 @@ if ARM64 config ARCH_R8A77995 bool "ARM64 Platform support for R-Car D3" + default y if ARCH_RENESAS select ARCH_RCAR_GEN3 select SYSC_R8A77995 help @@ -205,6 +225,7 @@ config ARCH_R8A77995 config ARCH_R8A77990 bool "ARM64 Platform support for R-Car E3" + default y if ARCH_RENESAS select ARCH_RCAR_GEN3 select SYSC_R8A77990 help @@ -213,6 +234,7 @@ config ARCH_R8A77990 config ARCH_R8A77951 bool "ARM64 Platform support for R-Car H3 ES2.0+" + default y if ARCH_RENESAS select ARCH_RCAR_GEN3 select SYSC_R8A7795 help @@ -222,6 +244,7 @@ config ARCH_R8A77951 config ARCH_R8A77965 bool "ARM64 Platform support for R-Car M3-N" + default y if ARCH_RENESAS select ARCH_RCAR_GEN3 select SYSC_R8A77965 help @@ -230,6 +253,7 @@ config ARCH_R8A77965 config ARCH_R8A77960 bool "ARM64 Platform support for R-Car M3-W" + default y if ARCH_RENESAS select ARCH_RCAR_GEN3 select SYSC_R8A77960 help @@ -237,6 +261,7 @@ config ARCH_R8A77960 config ARCH_R8A77961 bool "ARM64 Platform support for R-Car M3-W+" + default y if ARCH_RENESAS select ARCH_RCAR_GEN3 select SYSC_R8A77961 help @@ -245,6 +270,7 @@ config ARCH_R8A77961 config ARCH_R8A779F0 bool "ARM64 Platform support for R-Car S4-8" + default y if ARCH_RENESAS select ARCH_RCAR_GEN4 select SYSC_R8A779F0 help @@ -252,6 +278,7 @@ config ARCH_R8A779F0 config ARCH_R8A77980 bool "ARM64 Platform support for R-Car V3H" + default y if ARCH_RENESAS select ARCH_RCAR_GEN3 select SYSC_R8A77980 help @@ -259,6 +286,7 @@ config ARCH_R8A77980 config ARCH_R8A77970 bool "ARM64 Platform support for R-Car V3M" + default y if ARCH_RENESAS select ARCH_RCAR_GEN3 select SYSC_R8A77970 help @@ -266,6 +294,7 @@ config ARCH_R8A77970 config ARCH_R8A779A0 bool "ARM64 Platform support for R-Car V3U" + default y if ARCH_RENESAS select ARCH_RCAR_GEN4 select SYSC_R8A779A0 help @@ -273,6 +302,7 @@ config ARCH_R8A779A0 config ARCH_R8A779G0 bool "ARM64 Platform support for R-Car V4H" + default y if ARCH_RENESAS select ARCH_RCAR_GEN4 select SYSC_R8A779G0 help @@ -280,6 +310,7 @@ config ARCH_R8A779G0 config ARCH_R8A779H0 bool "ARM64 Platform support for R-Car V4M" + default y if ARCH_RENESAS select ARCH_RCAR_GEN4 select SYSC_R8A779H0 help @@ -287,6 +318,7 @@ config ARCH_R8A779H0 config ARCH_R8A774C0 bool "ARM64 Platform support for RZ/G2E" + default y if ARCH_RENESAS select ARCH_RCAR_GEN3 select SYSC_R8A774C0 help @@ -294,6 +326,7 @@ config ARCH_R8A774C0 config ARCH_R8A774E1 bool "ARM64 Platform support for RZ/G2H" + default y if ARCH_RENESAS select ARCH_RCAR_GEN3 select SYSC_R8A774E1 help @@ -301,6 +334,7 @@ config ARCH_R8A774E1 config ARCH_R8A774A1 bool "ARM64 Platform support for RZ/G2M" + default y if ARCH_RENESAS select ARCH_RCAR_GEN3 select SYSC_R8A774A1 help @@ -308,6 +342,7 @@ config ARCH_R8A774A1 config ARCH_R8A774B1 bool "ARM64 Platform support for RZ/G2N" + default y if ARCH_RENESAS select ARCH_RCAR_GEN3 select SYSC_R8A774B1 help @@ -315,24 +350,28 @@ config ARCH_R8A774B1 config ARCH_R9A07G043 bool "ARM64 Platform support for RZ/G2UL" + default y if ARCH_RENESAS select ARCH_RZG2L help This enables support for the Renesas RZ/G2UL SoC variants. config ARCH_R9A07G044 bool "ARM64 Platform support for RZ/G2L" + default y if ARCH_RENESAS select ARCH_RZG2L help This enables support for the Renesas RZ/G2L SoC variants. config ARCH_R9A07G054 bool "ARM64 Platform support for RZ/V2L" + default y if ARCH_RENESAS select ARCH_RZG2L help This enables support for the Renesas RZ/V2L SoC variants. config ARCH_R9A08G045 bool "ARM64 Platform support for RZ/G3S" + default y if ARCH_RENESAS select ARCH_RZG2L select SYSC_R9A08G045 help @@ -340,6 +379,7 @@ config ARCH_R9A08G045 config ARCH_R9A09G011 bool "ARM64 Platform support for RZ/V2M" + default y if ARCH_RENESAS select PM select PM_GENERIC_DOMAINS select PWC_RZV2M @@ -348,12 +388,14 @@ config ARCH_R9A09G011 config ARCH_R9A09G047 bool "ARM64 Platform support for RZ/G3E" + default y if ARCH_RENESAS select SYS_R9A09G047 help This enables support for the Renesas RZ/G3E SoC variants. config ARCH_R9A09G057 bool "ARM64 Platform support for RZ/V2H(P)" + default y if ARCH_RENESAS select RENESAS_RZV2H_ICU select SYS_R9A09G057 help diff --git a/include/linux/pwm.h b/include/linux/pwm.h index 9ece4e5d3815..63a17d2b4ec8 100644 --- a/include/linux/pwm.h +++ b/include/linux/pwm.h @@ -218,6 +218,8 @@ static inline void pwm_init_state(const struct pwm_device *pwm, * * pwm_get_state(pwm, &state); * duty = pwm_get_relative_duty_cycle(&state, 100); + * + * Returns: rounded relative duty cycle multiplied by @scale */ static inline unsigned int pwm_get_relative_duty_cycle(const struct pwm_state *state, unsigned int scale) @@ -244,8 +246,8 @@ pwm_get_relative_duty_cycle(const struct pwm_state *state, unsigned int scale) * pwm_set_relative_duty_cycle(&state, 50, 100); * pwm_apply_might_sleep(pwm, &state); * - * This functions returns -EINVAL if @duty_cycle and/or @scale are - * inconsistent (@scale == 0 or @duty_cycle > @scale). + * Returns: 0 on success or ``-EINVAL`` if @duty_cycle and/or @scale are + * inconsistent (@scale == 0 or @duty_cycle > @scale) */ static inline int pwm_set_relative_duty_cycle(struct pwm_state *state, unsigned int duty_cycle, @@ -351,7 +353,7 @@ struct pwm_chip { * pwmchip_supports_waveform() - checks if the given chip supports waveform callbacks * @chip: The pwm_chip to test * - * Returns true iff the pwm chip support the waveform functions like + * Returns: true iff the pwm chip support the waveform functions like * pwm_set_waveform_might_sleep() and pwm_round_waveform_might_sleep() */ static inline bool pwmchip_supports_waveform(struct pwm_chip *chip) @@ -369,7 +371,7 @@ static inline struct device *pwmchip_parent(const struct pwm_chip *chip) return chip->dev.parent; } -static inline void *pwmchip_get_drvdata(struct pwm_chip *chip) +static inline void *pwmchip_get_drvdata(const struct pwm_chip *chip) { return dev_get_drvdata(&chip->dev); } |