Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge tag 'pwm/for-6.17-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/ukleinek/linux

Pull pwm updates from Uwe Kleine-König:
"Apart from the usual mix of new drivers (pwm-argon-fan-hat), adding
support for variants to existing drivers, minor improvements to both
drivers and docs, device tree documenation updates, the noteworthy
changes are:

- A hwmon companion driver to pwm-mc33xs2410 living in drivers/hwmon
and acked by Guenter Roeck

- chardev support for PWM devices. This leverages atomic PWM updates
to userspace and at the same time simplifies and accelerates PWM
configuration changes"

* tag 'pwm/for-6.17-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/ukleinek/linux: (35 commits)
pwm: raspberrypi-poe: Fix spelling mistake "Firwmware" -> "Firmware"
hwmon: add support for MC33XS2410 hardware monitoring
pwm: mc33xs2410: add hwmon support
pwm: img: Remove redundant pm_runtime_mark_last_busy() calls
pwm: Expose PWM_WFHWSIZE in public header
dt-bindings: pwm: Convert lpc32xx-pwm.txt to yaml format
docs: pwm: Adapt Locking paragraph to reality
pwm: twl-led: Drop driver local locking
pwm: sun4i: Drop driver local locking
pwm: sti: Drop driver local locking
pwm: microchip-core: Drop driver local locking
pwm: lpc18xx-sct: Drop driver local locking
pwm: fsl-ftm: Drop driver local locking
pwm: clps711x: Drop driver local locking
pwm: atmel: Drop driver local locking
pwm: argon-fan-hat: Add Argon40 Fan HAT support
dt-bindings: pwm: argon40,fan-hat: Document Argon40 Fan HAT
dt-bindings: vendor-prefixes: Document Argon40
pwm: pwm-mediatek: Add support for PWM IP V3.0.2 in MT6991/MT8196
pwm: pwm-mediatek: Pass PWM_CK_26M_SEL from platform data
...

+1228 -307
+1 -1
Documentation/devicetree/bindings/pwm/adi,axi-pwmgen.yaml
··· 14 14 The Analog Devices AXI PWM generator can generate PWM signals 15 15 with variable pulse width and period. 16 16 17 - https://wiki.analog.com/resources/fpga/docs/axi_pwm_gen 17 + https://analogdevicesinc.github.io/hdl/library/axi_pwm_gen/index.html 18 18 19 19 allOf: 20 20 - $ref: pwm.yaml#
+48
Documentation/devicetree/bindings/pwm/argon40,fan-hat.yaml
··· 1 + # SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) 2 + %YAML 1.2 3 + --- 4 + $id: http://devicetree.org/schemas/pwm/argon40,fan-hat.yaml# 5 + $schema: http://devicetree.org/meta-schemas/core.yaml# 6 + 7 + title: Argon40 Fan HAT PWM controller 8 + 9 + maintainers: 10 + - Marek Vasut <marek.vasut+renesas@mailbox.org> 11 + 12 + description: 13 + The trivial PWM on Argon40 Fan HAT, which is a RaspberryPi blower fan 14 + hat which can be controlled over I2C, generates a fixed 30 kHz period 15 + PWM signal with configurable 0..100% duty cycle to control the fan 16 + speed. 17 + 18 + allOf: 19 + - $ref: pwm.yaml# 20 + 21 + properties: 22 + compatible: 23 + const: argon40,fan-hat 24 + 25 + reg: 26 + maxItems: 1 27 + 28 + "#pwm-cells": 29 + const: 3 30 + 31 + required: 32 + - compatible 33 + - reg 34 + 35 + additionalProperties: false 36 + 37 + examples: 38 + - | 39 + i2c { 40 + #address-cells = <1>; 41 + #size-cells = <0>; 42 + 43 + pwm@1a { 44 + compatible = "argon40,fan-hat"; 45 + reg = <0x1a>; 46 + #pwm-cells = <3>; 47 + }; 48 + };
-20
Documentation/devicetree/bindings/pwm/lpc1850-sct-pwm.txt
··· 1 - * NXP LPC18xx State Configurable Timer - Pulse Width Modulator driver 2 - 3 - Required properties: 4 - - compatible: Should be "nxp,lpc1850-sct-pwm" 5 - - reg: Should contain physical base address and length of pwm registers. 6 - - clocks: Must contain an entry for each entry in clock-names. 7 - See ../clock/clock-bindings.txt for details. 8 - - clock-names: Must include the following entries. 9 - - pwm: PWM operating clock. 10 - - #pwm-cells: Should be 3. See pwm.yaml in this directory for the description 11 - of the cells format. 12 - 13 - Example: 14 - pwm: pwm@40000000 { 15 - compatible = "nxp,lpc1850-sct-pwm"; 16 - reg = <0x40000000 0x1000>; 17 - clocks =<&ccu1 CLK_CPU_SCT>; 18 - clock-names = "pwm"; 19 - #pwm-cells = <3>; 20 - };
-17
Documentation/devicetree/bindings/pwm/lpc32xx-pwm.txt
··· 1 - LPC32XX PWM controller 2 - 3 - Required properties: 4 - - compatible: should be "nxp,lpc3220-pwm" 5 - - reg: physical base address and length of the controller's registers 6 - 7 - Examples: 8 - 9 - pwm@4005c000 { 10 - compatible = "nxp,lpc3220-pwm"; 11 - reg = <0x4005c000 0x4>; 12 - }; 13 - 14 - pwm@4005c004 { 15 - compatible = "nxp,lpc3220-pwm"; 16 - reg = <0x4005c004 0x4>; 17 - };
+28 -7
Documentation/devicetree/bindings/pwm/marvell,pxa-pwm.yaml
··· 11 11 12 12 allOf: 13 13 - $ref: pwm.yaml# 14 + - if: 15 + properties: 16 + compatible: 17 + contains: 18 + const: spacemit,k1-pwm 19 + then: 20 + properties: 21 + "#pwm-cells": 22 + const: 3 23 + else: 24 + properties: 25 + "#pwm-cells": 26 + const: 1 27 + description: | 28 + Used for specifying the period length in nanoseconds. 14 29 15 30 properties: 16 31 compatible: 17 - enum: 18 - - marvell,pxa250-pwm 19 - - marvell,pxa270-pwm 20 - - marvell,pxa168-pwm 21 - - marvell,pxa910-pwm 32 + oneOf: 33 + - enum: 34 + - marvell,pxa250-pwm 35 + - marvell,pxa270-pwm 36 + - marvell,pxa168-pwm 37 + - marvell,pxa910-pwm 38 + - items: 39 + - const: spacemit,k1-pwm 40 + - const: marvell,pxa910-pwm 22 41 23 42 reg: 24 43 # Length should be 0x10 25 44 maxItems: 1 26 45 27 46 "#pwm-cells": 28 - # Used for specifying the period length in nanoseconds 29 - const: 1 47 + description: Number of cells in a pwm specifier. 30 48 31 49 clocks: 50 + maxItems: 1 51 + 52 + resets: 32 53 maxItems: 1 33 54 34 55 required:
+5
Documentation/devicetree/bindings/pwm/mediatek,mt2712-pwm.yaml
··· 18 18 - enum: 19 19 - mediatek,mt2712-pwm 20 20 - mediatek,mt6795-pwm 21 + - mediatek,mt6991-pwm 21 22 - mediatek,mt7622-pwm 22 23 - mediatek,mt7623-pwm 23 24 - mediatek,mt7628-pwm ··· 33 32 - enum: 34 33 - mediatek,mt8195-pwm 35 34 - const: mediatek,mt8183-pwm 35 + - items: 36 + - enum: 37 + - mediatek,mt8196-pwm 38 + - const: mediatek,mt6991-pwm 36 39 37 40 reg: 38 41 maxItems: 1
+54
Documentation/devicetree/bindings/pwm/nxp,lpc1850-sct-pwm.yaml
··· 1 + # SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) 2 + %YAML 1.2 3 + --- 4 + $id: http://devicetree.org/schemas/pwm/nxp,lpc1850-sct-pwm.yaml# 5 + $schema: http://devicetree.org/meta-schemas/core.yaml# 6 + 7 + title: NXP LPC18xx State Configurable Timer 8 + 9 + maintainers: 10 + - Frank Li <Frank.Li@nxp.com> 11 + 12 + properties: 13 + compatible: 14 + const: nxp,lpc1850-sct-pwm 15 + 16 + reg: 17 + maxItems: 1 18 + 19 + clocks: 20 + maxItems: 1 21 + 22 + clock-names: 23 + items: 24 + - const: pwm 25 + 26 + '#pwm-cells': 27 + const: 3 28 + 29 + resets: 30 + maxItems: 1 31 + 32 + required: 33 + - compatible 34 + - reg 35 + - clocks 36 + - clock-names 37 + - '#pwm-cells' 38 + 39 + allOf: 40 + - $ref: pwm.yaml# 41 + 42 + unevaluatedProperties: false 43 + 44 + examples: 45 + - | 46 + #include <dt-bindings/clock/lpc18xx-ccu.h> 47 + 48 + pwm@40000000 { 49 + compatible = "nxp,lpc1850-sct-pwm"; 50 + reg = <0x40000000 0x1000>; 51 + clocks =<&ccu1 CLK_CPU_SCT>; 52 + clock-names = "pwm"; 53 + #pwm-cells = <3>; 54 + };
+44
Documentation/devicetree/bindings/pwm/nxp,lpc3220-pwm.yaml
··· 1 + # SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) 2 + %YAML 1.2 3 + --- 4 + $id: http://devicetree.org/schemas/pwm/nxp,lpc3220-pwm.yaml# 5 + $schema: http://devicetree.org/meta-schemas/core.yaml# 6 + 7 + title: NXP LPC32XX PWM controller 8 + 9 + maintainers: 10 + - Frank Li <Frank.Li@nxp.com> 11 + 12 + properties: 13 + compatible: 14 + enum: 15 + - nxp,lpc3220-pwm 16 + - nxp,lpc3220-motor-pwm 17 + 18 + reg: 19 + maxItems: 1 20 + 21 + clocks: 22 + maxItems: 1 23 + 24 + '#pwm-cells': 25 + const: 3 26 + 27 + required: 28 + - compatible 29 + - reg 30 + - '#pwm-cells' 31 + 32 + allOf: 33 + - $ref: pwm.yaml# 34 + 35 + unevaluatedProperties: false 36 + 37 + examples: 38 + - | 39 + pwm@4005c000 { 40 + compatible = "nxp,lpc3220-pwm"; 41 + reg = <0x4005c000 0x4>; 42 + #pwm-cells = <3>; 43 + }; 44 +
+3 -1
Documentation/devicetree/bindings/pwm/sophgo,sg2042-pwm.yaml
··· 17 17 18 18 properties: 19 19 compatible: 20 - const: sophgo,sg2042-pwm 20 + enum: 21 + - sophgo,sg2042-pwm 22 + - sophgo,sg2044-pwm 21 23 22 24 reg: 23 25 maxItems: 1
+2
Documentation/devicetree/bindings/vendor-prefixes.yaml
··· 149 149 description: Arctic Sand 150 150 "^arcx,.*": 151 151 description: arcx Inc. / Archronix Inc. 152 + "^argon40,.*": 153 + description: Argon 40 Technologies Limited 152 154 "^ariaboard,.*": 153 155 description: Shanghai Novotech Co., Ltd. (Ariaboard) 154 156 "^aries,.*":
+9 -4
Documentation/driver-api/pwm.rst
··· 173 173 ------- 174 174 175 175 The PWM core list manipulations are protected by a mutex, so pwm_get() 176 - and pwm_put() may not be called from an atomic context. Currently the 177 - PWM core does not enforce any locking to pwm_enable(), pwm_disable() and 178 - pwm_config(), so the calling context is currently driver specific. This 179 - is an issue derived from the former barebone API and should be fixed soon. 176 + and pwm_put() may not be called from an atomic context. 177 + Most functions in the PWM consumer API might sleep and so must not be called 178 + from atomic context. The notable exception is pwm_apply_atomic() which has the 179 + same semantics as pwm_apply_might_sleep() but can be called from atomic context. 180 + (The price for that is that it doesn't work for all PWM devices, use 181 + pwm_might_sleep() to check if a given PWM supports atomic operation. 182 + 183 + Locking in the PWM core ensures that callbacks related to a single chip are 184 + serialized. 180 185 181 186 Helpers 182 187 -------
+1
Documentation/hwmon/index.rst
··· 167 167 max77705 168 168 max8688 169 169 mc13783-adc 170 + mc33xs2410_hwmon 170 171 mc34vr500 171 172 mcp3021 172 173 menf21bmc
+34
Documentation/hwmon/mc33xs2410_hwmon.rst
··· 1 + .. SPDX-License-Identifier: GPL-2.0 2 + 3 + Kernel driver mc33xs2410_hwmon 4 + ============================== 5 + 6 + Supported devices: 7 + 8 + * NXPs MC33XS2410 9 + 10 + Datasheet: https://www.nxp.com/docs/en/data-sheet/MC33XS2410.pdf 11 + 12 + Authors: 13 + 14 + Dimitri Fedrau <dimitri.fedrau@liebherr.com> 15 + 16 + Description 17 + ----------- 18 + 19 + The MC33XS2410 is a four channel self-protected high-side switch featuring 20 + hardware monitoring functions such as temperature, current and voltages for each 21 + of the four channels. 22 + 23 + Sysfs entries 24 + ------------- 25 + 26 + ======================= ====================================================== 27 + temp1_label "Central die temperature" 28 + temp1_input Measured temperature of central die 29 + 30 + temp[2-5]_label "Channel [1-4] temperature" 31 + temp[2-5]_input Measured temperature of a single channel 32 + temp[2-5]_alarm Temperature alarm 33 + temp[2-5]_max Maximal temperature 34 + ======================= ======================================================
+4 -8
arch/riscv/boot/dts/sifive/hifive-unleashed-a00.dts
··· 49 49 compatible = "pwm-leds"; 50 50 51 51 led-d1 { 52 - pwms = <&pwm0 0 7812500 PWM_POLARITY_INVERTED>; 53 - active-low; 52 + pwms = <&pwm0 0 7812500 0>; 54 53 color = <LED_COLOR_ID_GREEN>; 55 54 max-brightness = <255>; 56 55 label = "d1"; 57 56 }; 58 57 59 58 led-d2 { 60 - pwms = <&pwm0 1 7812500 PWM_POLARITY_INVERTED>; 61 - active-low; 59 + pwms = <&pwm0 1 7812500 0>; 62 60 color = <LED_COLOR_ID_GREEN>; 63 61 max-brightness = <255>; 64 62 label = "d2"; 65 63 }; 66 64 67 65 led-d3 { 68 - pwms = <&pwm0 2 7812500 PWM_POLARITY_INVERTED>; 69 - active-low; 66 + pwms = <&pwm0 2 7812500 0>; 70 67 color = <LED_COLOR_ID_GREEN>; 71 68 max-brightness = <255>; 72 69 label = "d3"; 73 70 }; 74 71 75 72 led-d4 { 76 - pwms = <&pwm0 3 7812500 PWM_POLARITY_INVERTED>; 77 - active-low; 73 + pwms = <&pwm0 3 7812500 0>; 78 74 color = <LED_COLOR_ID_GREEN>; 79 75 max-brightness = <255>; 80 76 label = "d4";
+4 -8
arch/riscv/boot/dts/sifive/hifive-unmatched-a00.dts
··· 51 51 compatible = "pwm-leds"; 52 52 53 53 led-d12 { 54 - pwms = <&pwm0 0 7812500 PWM_POLARITY_INVERTED>; 55 - active-low; 54 + pwms = <&pwm0 0 7812500 0>; 56 55 color = <LED_COLOR_ID_GREEN>; 57 56 max-brightness = <255>; 58 57 label = "d12"; ··· 67 68 label = "d2"; 68 69 69 70 led-red { 70 - pwms = <&pwm0 2 7812500 PWM_POLARITY_INVERTED>; 71 - active-low; 71 + pwms = <&pwm0 2 7812500 0>; 72 72 color = <LED_COLOR_ID_RED>; 73 73 }; 74 74 75 75 led-green { 76 - pwms = <&pwm0 1 7812500 PWM_POLARITY_INVERTED>; 77 - active-low; 76 + pwms = <&pwm0 1 7812500 0>; 78 77 color = <LED_COLOR_ID_GREEN>; 79 78 }; 80 79 81 80 led-blue { 82 - pwms = <&pwm0 3 7812500 PWM_POLARITY_INVERTED>; 83 - active-low; 81 + pwms = <&pwm0 3 7812500 0>; 84 82 color = <LED_COLOR_ID_BLUE>; 85 83 }; 86 84 };
+10
drivers/hwmon/Kconfig
··· 700 700 help 701 701 Support for the A/D converter on MC13783 and MC13892 PMIC. 702 702 703 + config SENSORS_MC33XS2410 704 + tristate "MC33XS2410 HWMON support" 705 + depends on PWM_MC33XS2410 706 + help 707 + If you say yes here you get hardware monitoring support for 708 + MC33XS2410. 709 + 710 + This driver can also be built as a module. If so, the module 711 + will be called mc33xs2410_hwmon. 712 + 703 713 config SENSORS_FSCHMD 704 714 tristate "Fujitsu Siemens Computers sensor chips" 705 715 depends on (X86 || COMPILE_TEST) && I2C
+1
drivers/hwmon/Makefile
··· 165 165 obj-$(CONFIG_MAX31827) += max31827.o 166 166 obj-$(CONFIG_SENSORS_MAX77705) += max77705-hwmon.o 167 167 obj-$(CONFIG_SENSORS_MC13783_ADC)+= mc13783-adc.o 168 + obj-$(CONFIG_SENSORS_MC33XS2410) += mc33xs2410_hwmon.o 168 169 obj-$(CONFIG_SENSORS_MC34VR500) += mc34vr500.o 169 170 obj-$(CONFIG_SENSORS_MCP3021) += mcp3021.o 170 171 obj-$(CONFIG_SENSORS_TC654) += tc654.o
+178
drivers/hwmon/mc33xs2410_hwmon.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Copyright (C) 2025 Liebherr-Electronics and Drives GmbH 4 + */ 5 + 6 + #include <linux/auxiliary_bus.h> 7 + #include <linux/bitfield.h> 8 + #include <linux/bitops.h> 9 + #include <linux/hwmon.h> 10 + #include <linux/mc33xs2410.h> 11 + #include <linux/module.h> 12 + 13 + /* ctrl registers */ 14 + 15 + #define MC33XS2410_TEMP_WT 0x29 16 + #define MC33XS2410_TEMP_WT_MASK GENMASK(7, 0) 17 + 18 + /* diag registers */ 19 + 20 + /* chan in { 1 ... 4 } */ 21 + #define MC33XS2410_OUT_STA(chan) (0x02 + (chan) - 1) 22 + #define MC33XS2410_OUT_STA_OTW BIT(8) 23 + 24 + #define MC33XS2410_TS_TEMP_DIE 0x26 25 + #define MC33XS2410_TS_TEMP_MASK GENMASK(9, 0) 26 + 27 + /* chan in { 1 ... 4 } */ 28 + #define MC33XS2410_TS_TEMP(chan) (0x2f + (chan) - 1) 29 + 30 + static const struct hwmon_channel_info * const mc33xs2410_hwmon_info[] = { 31 + HWMON_CHANNEL_INFO(temp, 32 + HWMON_T_LABEL | HWMON_T_INPUT, 33 + HWMON_T_LABEL | HWMON_T_INPUT | HWMON_T_MAX | 34 + HWMON_T_ALARM, 35 + HWMON_T_LABEL | HWMON_T_INPUT | HWMON_T_MAX | 36 + HWMON_T_ALARM, 37 + HWMON_T_LABEL | HWMON_T_INPUT | HWMON_T_MAX | 38 + HWMON_T_ALARM, 39 + HWMON_T_LABEL | HWMON_T_INPUT | HWMON_T_MAX | 40 + HWMON_T_ALARM), 41 + NULL, 42 + }; 43 + 44 + static umode_t mc33xs2410_hwmon_is_visible(const void *data, 45 + enum hwmon_sensor_types type, 46 + u32 attr, int channel) 47 + { 48 + switch (attr) { 49 + case hwmon_temp_input: 50 + case hwmon_temp_alarm: 51 + case hwmon_temp_label: 52 + return 0444; 53 + case hwmon_temp_max: 54 + return 0644; 55 + default: 56 + return 0; 57 + } 58 + } 59 + 60 + static int mc33xs2410_hwmon_read(struct device *dev, 61 + enum hwmon_sensor_types type, 62 + u32 attr, int channel, long *val) 63 + { 64 + struct spi_device *spi = dev_get_drvdata(dev); 65 + u16 reg_val; 66 + int ret; 67 + u8 reg; 68 + 69 + switch (attr) { 70 + case hwmon_temp_input: 71 + reg = (channel == 0) ? MC33XS2410_TS_TEMP_DIE : 72 + MC33XS2410_TS_TEMP(channel); 73 + ret = mc33xs2410_read_reg_diag(spi, reg, &reg_val); 74 + if (ret < 0) 75 + return ret; 76 + 77 + /* LSB is 0.25 degree celsius */ 78 + *val = FIELD_GET(MC33XS2410_TS_TEMP_MASK, reg_val) * 250 - 40000; 79 + return 0; 80 + case hwmon_temp_alarm: 81 + ret = mc33xs2410_read_reg_diag(spi, MC33XS2410_OUT_STA(channel), 82 + &reg_val); 83 + if (ret < 0) 84 + return ret; 85 + 86 + *val = FIELD_GET(MC33XS2410_OUT_STA_OTW, reg_val); 87 + return 0; 88 + case hwmon_temp_max: 89 + ret = mc33xs2410_read_reg_ctrl(spi, MC33XS2410_TEMP_WT, &reg_val); 90 + if (ret < 0) 91 + return ret; 92 + 93 + /* LSB is 1 degree celsius */ 94 + *val = FIELD_GET(MC33XS2410_TEMP_WT_MASK, reg_val) * 1000 - 40000; 95 + return 0; 96 + default: 97 + return -EOPNOTSUPP; 98 + } 99 + } 100 + 101 + static int mc33xs2410_hwmon_write(struct device *dev, 102 + enum hwmon_sensor_types type, u32 attr, 103 + int channel, long val) 104 + { 105 + struct spi_device *spi = dev_get_drvdata(dev); 106 + 107 + switch (attr) { 108 + case hwmon_temp_max: 109 + val = clamp_val(val, -40000, 215000); 110 + 111 + /* LSB is 1 degree celsius */ 112 + val = (val / 1000) + 40; 113 + return mc33xs2410_modify_reg(spi, MC33XS2410_TEMP_WT, 114 + MC33XS2410_TEMP_WT_MASK, val); 115 + default: 116 + return -EOPNOTSUPP; 117 + } 118 + } 119 + 120 + static const char *const mc33xs2410_temp_label[] = { 121 + "Central die temperature", 122 + "Channel 1 temperature", 123 + "Channel 2 temperature", 124 + "Channel 3 temperature", 125 + "Channel 4 temperature", 126 + }; 127 + 128 + static int mc33xs2410_read_string(struct device *dev, 129 + enum hwmon_sensor_types type, 130 + u32 attr, int channel, const char **str) 131 + { 132 + *str = mc33xs2410_temp_label[channel]; 133 + 134 + return 0; 135 + } 136 + 137 + static const struct hwmon_ops mc33xs2410_hwmon_hwmon_ops = { 138 + .is_visible = mc33xs2410_hwmon_is_visible, 139 + .read = mc33xs2410_hwmon_read, 140 + .read_string = mc33xs2410_read_string, 141 + .write = mc33xs2410_hwmon_write, 142 + }; 143 + 144 + static const struct hwmon_chip_info mc33xs2410_hwmon_chip_info = { 145 + .ops = &mc33xs2410_hwmon_hwmon_ops, 146 + .info = mc33xs2410_hwmon_info, 147 + }; 148 + 149 + static int mc33xs2410_hwmon_probe(struct auxiliary_device *adev, 150 + const struct auxiliary_device_id *id) 151 + { 152 + struct device *dev = &adev->dev; 153 + struct spi_device *spi = container_of(dev->parent, struct spi_device, dev); 154 + struct device *hwmon; 155 + 156 + hwmon = devm_hwmon_device_register_with_info(dev, NULL, spi, 157 + &mc33xs2410_hwmon_chip_info, 158 + NULL); 159 + return PTR_ERR_OR_ZERO(hwmon); 160 + } 161 + 162 + static const struct auxiliary_device_id mc33xs2410_hwmon_ids[] = { 163 + { 164 + .name = "pwm_mc33xs2410.hwmon", 165 + }, 166 + { } 167 + }; 168 + MODULE_DEVICE_TABLE(auxiliary, mc33xs2410_hwmon_ids); 169 + 170 + static struct auxiliary_driver mc33xs2410_hwmon_driver = { 171 + .probe = mc33xs2410_hwmon_probe, 172 + .id_table = mc33xs2410_hwmon_ids, 173 + }; 174 + module_auxiliary_driver(mc33xs2410_hwmon_driver); 175 + 176 + MODULE_DESCRIPTION("NXP MC33XS2410 hwmon driver"); 177 + MODULE_AUTHOR("Dimitri Fedrau <dimitri.fedrau@liebherr.com>"); 178 + MODULE_LICENSE("GPL");
+12 -2
drivers/pwm/Kconfig
··· 66 66 To compile this driver as a module, choose M here: the module 67 67 will be called pwm-apple. 68 68 69 + config PWM_ARGON_FAN_HAT 70 + tristate "Argon40 Fan HAT support" 71 + depends on I2C && OF 72 + help 73 + Generic PWM framework driver for Argon40 Fan HAT. 74 + 75 + To compile this driver as a module, choose M here: the module 76 + will be called pwm-argon-fan-hat. 77 + 69 78 config PWM_ATMEL 70 79 tristate "Atmel PWM support" 71 80 depends on ARCH_AT91 || COMPILE_TEST ··· 436 427 tristate "MC33XS2410 PWM support" 437 428 depends on OF 438 429 depends on SPI 430 + select AUXILIARY_BUS 439 431 help 440 432 NXP MC33XS2410 high-side switch driver. The MC33XS2410 is a four 441 433 channel high-side switch. The device is operational from 3.0 V ··· 527 517 528 518 config PWM_PXA 529 519 tristate "PXA PWM support" 530 - depends on ARCH_PXA || ARCH_MMP || COMPILE_TEST 520 + depends on ARCH_PXA || ARCH_MMP || ARCH_SPACEMIT || COMPILE_TEST 531 521 depends on HAS_IOMEM 532 522 help 533 523 Generic PWM framework driver for PXA. ··· 536 526 will be called pwm-pxa. 537 527 538 528 config PWM_RASPBERRYPI_POE 539 - tristate "Raspberry Pi Firwmware PoE Hat PWM support" 529 + tristate "Raspberry Pi Firmware PoE Hat PWM support" 540 530 # Make sure not 'y' when RASPBERRYPI_FIRMWARE is 'm'. This can only 541 531 # happen when COMPILE_TEST=y, hence the added !RASPBERRYPI_FIRMWARE. 542 532 depends on RASPBERRYPI_FIRMWARE || (COMPILE_TEST && !RASPBERRYPI_FIRMWARE)
+1
drivers/pwm/Makefile
··· 3 3 obj-$(CONFIG_PWM_AB8500) += pwm-ab8500.o 4 4 obj-$(CONFIG_PWM_ADP5585) += pwm-adp5585.o 5 5 obj-$(CONFIG_PWM_APPLE) += pwm-apple.o 6 + obj-$(CONFIG_PWM_ARGON_FAN_HAT) += pwm-argon-fan-hat.o 6 7 obj-$(CONFIG_PWM_ATMEL) += pwm-atmel.o 7 8 obj-$(CONFIG_PWM_ATMEL_HLCDC_PWM) += pwm-atmel-hlcdc.o 8 9 obj-$(CONFIG_PWM_ATMEL_TCB) += pwm-atmel-tcb.o
+319 -29
drivers/pwm/core.c
··· 23 23 24 24 #include <dt-bindings/pwm/pwm.h> 25 25 26 + #include <uapi/linux/pwm.h> 27 + 26 28 #define CREATE_TRACE_POINTS 27 29 #include <trace/events/pwm.h> 30 + 31 + #define PWM_MINOR_COUNT 256 28 32 29 33 /* protects access to pwm_chips */ 30 34 static DEFINE_MUTEX(pwm_lock); ··· 210 206 return ret; 211 207 } 212 208 213 - #define WFHWSIZE 20 214 - 215 209 /** 216 210 * pwm_round_waveform_might_sleep - Query hardware capabilities 217 211 * Cannot be used in atomic context. ··· 246 244 struct pwm_chip *chip = pwm->chip; 247 245 const struct pwm_ops *ops = chip->ops; 248 246 struct pwm_waveform wf_req = *wf; 249 - char wfhw[WFHWSIZE]; 247 + char wfhw[PWM_WFHWSIZE]; 250 248 int ret_tohw, ret_fromhw; 251 249 252 - BUG_ON(WFHWSIZE < ops->sizeof_wfhw); 250 + BUG_ON(PWM_WFHWSIZE < ops->sizeof_wfhw); 253 251 254 252 if (!pwmchip_supports_waveform(chip)) 255 253 return -EOPNOTSUPP; ··· 304 302 { 305 303 struct pwm_chip *chip = pwm->chip; 306 304 const struct pwm_ops *ops = chip->ops; 307 - char wfhw[WFHWSIZE]; 305 + char wfhw[PWM_WFHWSIZE]; 308 306 int err; 309 307 310 - BUG_ON(WFHWSIZE < ops->sizeof_wfhw); 308 + BUG_ON(PWM_WFHWSIZE < ops->sizeof_wfhw); 311 309 312 310 if (!pwmchip_supports_waveform(chip) || !ops->read_waveform) 313 311 return -EOPNOTSUPP; ··· 332 330 { 333 331 struct pwm_chip *chip = pwm->chip; 334 332 const struct pwm_ops *ops = chip->ops; 335 - char wfhw[WFHWSIZE]; 333 + char wfhw[PWM_WFHWSIZE]; 336 334 struct pwm_waveform wf_rounded; 337 335 int err, ret_tohw; 338 336 339 - BUG_ON(WFHWSIZE < ops->sizeof_wfhw); 337 + BUG_ON(PWM_WFHWSIZE < ops->sizeof_wfhw); 340 338 341 339 if (!pwmchip_supports_waveform(chip)) 342 340 return -EOPNOTSUPP; ··· 648 646 649 647 if (pwmchip_supports_waveform(chip)) { 650 648 struct pwm_waveform wf; 651 - char wfhw[WFHWSIZE]; 649 + char wfhw[PWM_WFHWSIZE]; 652 650 653 - BUG_ON(WFHWSIZE < ops->sizeof_wfhw); 651 + BUG_ON(PWM_WFHWSIZE < ops->sizeof_wfhw); 654 652 655 653 pwm_state2wf(state, &wf); 656 654 ··· 807 805 return -ENODEV; 808 806 809 807 if (pwmchip_supports_waveform(chip) && ops->read_waveform) { 810 - char wfhw[WFHWSIZE]; 808 + char wfhw[PWM_WFHWSIZE]; 811 809 struct pwm_waveform wf; 812 810 813 - BUG_ON(WFHWSIZE < ops->sizeof_wfhw); 811 + BUG_ON(PWM_WFHWSIZE < ops->sizeof_wfhw); 814 812 815 813 ret = __pwm_read_waveform(chip, pwm, &wfhw); 816 814 if (ret) ··· 1694 1692 !ops->write_waveform) 1695 1693 return false; 1696 1694 1697 - if (WFHWSIZE < ops->sizeof_wfhw) { 1698 - dev_warn(pwmchip_parent(chip), "WFHWSIZE < %zu\n", ops->sizeof_wfhw); 1695 + if (PWM_WFHWSIZE < ops->sizeof_wfhw) { 1696 + dev_warn(pwmchip_parent(chip), "PWM_WFHWSIZE < %zu\n", ops->sizeof_wfhw); 1699 1697 return false; 1700 1698 } 1701 1699 } else { ··· 2009 2007 } 2010 2008 EXPORT_SYMBOL_GPL(pwm_get); 2011 2009 2012 - /** 2013 - * pwm_put() - release a PWM device 2014 - * @pwm: PWM device 2015 - */ 2016 - void pwm_put(struct pwm_device *pwm) 2010 + static void __pwm_put(struct pwm_device *pwm) 2017 2011 { 2018 - struct pwm_chip *chip; 2019 - 2020 - if (!pwm) 2021 - return; 2022 - 2023 - chip = pwm->chip; 2024 - 2025 - guard(mutex)(&pwm_lock); 2012 + struct pwm_chip *chip = pwm->chip; 2026 2013 2027 2014 /* 2028 2015 * Trigger a warning if a consumer called pwm_put() twice. ··· 2031 2040 put_device(&chip->dev); 2032 2041 2033 2042 module_put(chip->owner); 2043 + } 2044 + 2045 + /** 2046 + * pwm_put() - release a PWM device 2047 + * @pwm: PWM device 2048 + */ 2049 + void pwm_put(struct pwm_device *pwm) 2050 + { 2051 + if (!pwm) 2052 + return; 2053 + 2054 + guard(mutex)(&pwm_lock); 2055 + 2056 + __pwm_put(pwm); 2034 2057 } 2035 2058 EXPORT_SYMBOL_GPL(pwm_put); 2036 2059 ··· 2115 2110 } 2116 2111 EXPORT_SYMBOL_GPL(devm_fwnode_pwm_get); 2117 2112 2113 + struct pwm_cdev_data { 2114 + struct pwm_chip *chip; 2115 + struct pwm_device *pwm[]; 2116 + }; 2117 + 2118 + static int pwm_cdev_open(struct inode *inode, struct file *file) 2119 + { 2120 + struct pwm_chip *chip = container_of(inode->i_cdev, struct pwm_chip, cdev); 2121 + struct pwm_cdev_data *cdata; 2122 + 2123 + guard(mutex)(&pwm_lock); 2124 + 2125 + if (!chip->operational) 2126 + return -ENXIO; 2127 + 2128 + cdata = kzalloc(struct_size(cdata, pwm, chip->npwm), GFP_KERNEL); 2129 + if (!cdata) 2130 + return -ENOMEM; 2131 + 2132 + cdata->chip = chip; 2133 + 2134 + file->private_data = cdata; 2135 + 2136 + return nonseekable_open(inode, file); 2137 + } 2138 + 2139 + static int pwm_cdev_release(struct inode *inode, struct file *file) 2140 + { 2141 + struct pwm_cdev_data *cdata = file->private_data; 2142 + unsigned int i; 2143 + 2144 + for (i = 0; i < cdata->chip->npwm; ++i) { 2145 + struct pwm_device *pwm = cdata->pwm[i]; 2146 + 2147 + if (pwm) { 2148 + const char *label = pwm->label; 2149 + 2150 + pwm_put(cdata->pwm[i]); 2151 + kfree(label); 2152 + } 2153 + } 2154 + kfree(cdata); 2155 + 2156 + return 0; 2157 + } 2158 + 2159 + static int pwm_cdev_request(struct pwm_cdev_data *cdata, unsigned int hwpwm) 2160 + { 2161 + struct pwm_chip *chip = cdata->chip; 2162 + 2163 + if (hwpwm >= chip->npwm) 2164 + return -EINVAL; 2165 + 2166 + if (!cdata->pwm[hwpwm]) { 2167 + struct pwm_device *pwm = &chip->pwms[hwpwm]; 2168 + const char *label; 2169 + int ret; 2170 + 2171 + label = kasprintf(GFP_KERNEL, "pwm-cdev (pid=%d)", current->pid); 2172 + if (!label) 2173 + return -ENOMEM; 2174 + 2175 + ret = pwm_device_request(pwm, label); 2176 + if (ret < 0) { 2177 + kfree(label); 2178 + return ret; 2179 + } 2180 + 2181 + cdata->pwm[hwpwm] = pwm; 2182 + } 2183 + 2184 + return 0; 2185 + } 2186 + 2187 + static int pwm_cdev_free(struct pwm_cdev_data *cdata, unsigned int hwpwm) 2188 + { 2189 + struct pwm_chip *chip = cdata->chip; 2190 + 2191 + if (hwpwm >= chip->npwm) 2192 + return -EINVAL; 2193 + 2194 + if (cdata->pwm[hwpwm]) { 2195 + struct pwm_device *pwm = cdata->pwm[hwpwm]; 2196 + const char *label = pwm->label; 2197 + 2198 + __pwm_put(pwm); 2199 + 2200 + kfree(label); 2201 + 2202 + cdata->pwm[hwpwm] = NULL; 2203 + } 2204 + 2205 + return 0; 2206 + } 2207 + 2208 + static struct pwm_device *pwm_cdev_get_requested_pwm(struct pwm_cdev_data *cdata, 2209 + u32 hwpwm) 2210 + { 2211 + struct pwm_chip *chip = cdata->chip; 2212 + 2213 + if (hwpwm >= chip->npwm) 2214 + return ERR_PTR(-EINVAL); 2215 + 2216 + if (cdata->pwm[hwpwm]) 2217 + return cdata->pwm[hwpwm]; 2218 + 2219 + return ERR_PTR(-EINVAL); 2220 + } 2221 + 2222 + static long pwm_cdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 2223 + { 2224 + int ret = 0; 2225 + struct pwm_cdev_data *cdata = file->private_data; 2226 + struct pwm_chip *chip = cdata->chip; 2227 + 2228 + guard(mutex)(&pwm_lock); 2229 + 2230 + if (!chip->operational) 2231 + return -ENODEV; 2232 + 2233 + switch (cmd) { 2234 + case PWM_IOCTL_REQUEST: 2235 + { 2236 + unsigned int hwpwm = arg; 2237 + 2238 + return pwm_cdev_request(cdata, hwpwm); 2239 + } 2240 + 2241 + case PWM_IOCTL_FREE: 2242 + { 2243 + unsigned int hwpwm = arg; 2244 + 2245 + return pwm_cdev_free(cdata, hwpwm); 2246 + } 2247 + 2248 + case PWM_IOCTL_ROUNDWF: 2249 + { 2250 + struct pwmchip_waveform cwf; 2251 + struct pwm_waveform wf; 2252 + struct pwm_device *pwm; 2253 + 2254 + ret = copy_from_user(&cwf, 2255 + (struct pwmchip_waveform __user *)arg, 2256 + sizeof(cwf)); 2257 + if (ret) 2258 + return -EFAULT; 2259 + 2260 + if (cwf.__pad != 0) 2261 + return -EINVAL; 2262 + 2263 + pwm = pwm_cdev_get_requested_pwm(cdata, cwf.hwpwm); 2264 + if (IS_ERR(pwm)) 2265 + return PTR_ERR(pwm); 2266 + 2267 + wf = (struct pwm_waveform) { 2268 + .period_length_ns = cwf.period_length_ns, 2269 + .duty_length_ns = cwf.duty_length_ns, 2270 + .duty_offset_ns = cwf.duty_offset_ns, 2271 + }; 2272 + 2273 + ret = pwm_round_waveform_might_sleep(pwm, &wf); 2274 + if (ret < 0) 2275 + return ret; 2276 + 2277 + cwf = (struct pwmchip_waveform) { 2278 + .hwpwm = cwf.hwpwm, 2279 + .period_length_ns = wf.period_length_ns, 2280 + .duty_length_ns = wf.duty_length_ns, 2281 + .duty_offset_ns = wf.duty_offset_ns, 2282 + }; 2283 + 2284 + return copy_to_user((struct pwmchip_waveform __user *)arg, 2285 + &cwf, sizeof(cwf)); 2286 + } 2287 + 2288 + case PWM_IOCTL_GETWF: 2289 + { 2290 + struct pwmchip_waveform cwf; 2291 + struct pwm_waveform wf; 2292 + struct pwm_device *pwm; 2293 + 2294 + ret = copy_from_user(&cwf, 2295 + (struct pwmchip_waveform __user *)arg, 2296 + sizeof(cwf)); 2297 + if (ret) 2298 + return -EFAULT; 2299 + 2300 + if (cwf.__pad != 0) 2301 + return -EINVAL; 2302 + 2303 + pwm = pwm_cdev_get_requested_pwm(cdata, cwf.hwpwm); 2304 + if (IS_ERR(pwm)) 2305 + return PTR_ERR(pwm); 2306 + 2307 + ret = pwm_get_waveform_might_sleep(pwm, &wf); 2308 + if (ret) 2309 + return ret; 2310 + 2311 + cwf = (struct pwmchip_waveform) { 2312 + .hwpwm = cwf.hwpwm, 2313 + .period_length_ns = wf.period_length_ns, 2314 + .duty_length_ns = wf.duty_length_ns, 2315 + .duty_offset_ns = wf.duty_offset_ns, 2316 + }; 2317 + 2318 + return copy_to_user((struct pwmchip_waveform __user *)arg, 2319 + &cwf, sizeof(cwf)); 2320 + } 2321 + 2322 + case PWM_IOCTL_SETROUNDEDWF: 2323 + case PWM_IOCTL_SETEXACTWF: 2324 + { 2325 + struct pwmchip_waveform cwf; 2326 + struct pwm_waveform wf; 2327 + struct pwm_device *pwm; 2328 + 2329 + ret = copy_from_user(&cwf, 2330 + (struct pwmchip_waveform __user *)arg, 2331 + sizeof(cwf)); 2332 + if (ret) 2333 + return -EFAULT; 2334 + 2335 + if (cwf.__pad != 0) 2336 + return -EINVAL; 2337 + 2338 + wf = (struct pwm_waveform){ 2339 + .period_length_ns = cwf.period_length_ns, 2340 + .duty_length_ns = cwf.duty_length_ns, 2341 + .duty_offset_ns = cwf.duty_offset_ns, 2342 + }; 2343 + 2344 + if (!pwm_wf_valid(&wf)) 2345 + return -EINVAL; 2346 + 2347 + pwm = pwm_cdev_get_requested_pwm(cdata, cwf.hwpwm); 2348 + if (IS_ERR(pwm)) 2349 + return PTR_ERR(pwm); 2350 + 2351 + ret = pwm_set_waveform_might_sleep(pwm, &wf, 2352 + cmd == PWM_IOCTL_SETEXACTWF); 2353 + 2354 + /* 2355 + * If userspace cares about rounding deviations it has 2356 + * to check the values anyhow, so simplify handling for 2357 + * them and don't signal uprounding. This matches the 2358 + * behaviour of PWM_IOCTL_ROUNDWF which also returns 0 2359 + * in that case. 2360 + */ 2361 + if (ret == 1) 2362 + ret = 0; 2363 + 2364 + return ret; 2365 + } 2366 + 2367 + default: 2368 + return -ENOTTY; 2369 + } 2370 + } 2371 + 2372 + static const struct file_operations pwm_cdev_fileops = { 2373 + .open = pwm_cdev_open, 2374 + .release = pwm_cdev_release, 2375 + .owner = THIS_MODULE, 2376 + .unlocked_ioctl = pwm_cdev_ioctl, 2377 + }; 2378 + 2379 + static dev_t pwm_devt; 2380 + 2118 2381 /** 2119 2382 * __pwmchip_add() - register a new PWM chip 2120 2383 * @chip: the PWM chip to add ··· 2435 2162 scoped_guard(pwmchip, chip) 2436 2163 chip->operational = true; 2437 2164 2438 - ret = device_add(&chip->dev); 2165 + if (chip->ops->write_waveform) { 2166 + if (chip->id < PWM_MINOR_COUNT) 2167 + chip->dev.devt = MKDEV(MAJOR(pwm_devt), chip->id); 2168 + else 2169 + dev_warn(&chip->dev, "chip id too high to create a chardev\n"); 2170 + } 2171 + 2172 + cdev_init(&chip->cdev, &pwm_cdev_fileops); 2173 + chip->cdev.owner = owner; 2174 + 2175 + ret = cdev_device_add(&chip->cdev, &chip->dev); 2439 2176 if (ret) 2440 2177 goto err_device_add; 2441 2178 ··· 2496 2213 idr_remove(&pwm_chips, chip->id); 2497 2214 } 2498 2215 2499 - device_del(&chip->dev); 2216 + cdev_device_del(&chip->cdev, &chip->dev); 2500 2217 } 2501 2218 EXPORT_SYMBOL_GPL(pwmchip_remove); 2502 2219 ··· 2640 2357 { 2641 2358 int ret; 2642 2359 2360 + ret = alloc_chrdev_region(&pwm_devt, 0, PWM_MINOR_COUNT, "pwm"); 2361 + if (ret) { 2362 + pr_err("Failed to initialize chrdev region for PWM usage\n"); 2363 + return ret; 2364 + } 2365 + 2643 2366 ret = class_register(&pwm_class); 2644 2367 if (ret) { 2645 2368 pr_err("Failed to initialize PWM class (%pe)\n", ERR_PTR(ret)); 2369 + unregister_chrdev_region(pwm_devt, 256); 2646 2370 return ret; 2647 2371 } 2648 2372
+109
drivers/pwm/pwm-argon-fan-hat.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Copyright (C) 2025 Marek Vasut 4 + * 5 + * Limitations: 6 + * - no support for offset/polarity 7 + * - fixed 30 kHz period 8 + * 9 + * Argon Fan HAT https://argon40.com/products/argon-fan-hat 10 + */ 11 + 12 + #include <linux/err.h> 13 + #include <linux/i2c.h> 14 + #include <linux/module.h> 15 + #include <linux/pwm.h> 16 + 17 + #define ARGON40_FAN_HAT_PERIOD_NS 33333 /* ~30 kHz */ 18 + 19 + #define ARGON40_FAN_HAT_REG_DUTY_CYCLE 0x80 20 + 21 + static int argon_fan_hat_round_waveform_tohw(struct pwm_chip *chip, 22 + struct pwm_device *pwm, 23 + const struct pwm_waveform *wf, 24 + void *_wfhw) 25 + { 26 + u8 *wfhw = _wfhw; 27 + 28 + if (wf->duty_length_ns > ARGON40_FAN_HAT_PERIOD_NS) 29 + *wfhw = 100; 30 + else 31 + *wfhw = mul_u64_u64_div_u64(wf->duty_length_ns, 100, ARGON40_FAN_HAT_PERIOD_NS); 32 + 33 + return 0; 34 + } 35 + 36 + static int argon_fan_hat_round_waveform_fromhw(struct pwm_chip *chip, 37 + struct pwm_device *pwm, 38 + const void *_wfhw, 39 + struct pwm_waveform *wf) 40 + { 41 + const u8 *wfhw = _wfhw; 42 + 43 + wf->period_length_ns = ARGON40_FAN_HAT_PERIOD_NS; 44 + wf->duty_length_ns = DIV64_U64_ROUND_UP(wf->period_length_ns * *wfhw, 100); 45 + wf->duty_offset_ns = 0; 46 + 47 + return 0; 48 + } 49 + 50 + static int argon_fan_hat_write_waveform(struct pwm_chip *chip, 51 + struct pwm_device *pwm, 52 + const void *_wfhw) 53 + { 54 + struct i2c_client *i2c = pwmchip_get_drvdata(chip); 55 + const u8 *wfhw = _wfhw; 56 + 57 + return i2c_smbus_write_byte_data(i2c, ARGON40_FAN_HAT_REG_DUTY_CYCLE, *wfhw); 58 + } 59 + 60 + static const struct pwm_ops argon_fan_hat_pwm_ops = { 61 + .sizeof_wfhw = sizeof(u8), 62 + .round_waveform_fromhw = argon_fan_hat_round_waveform_fromhw, 63 + .round_waveform_tohw = argon_fan_hat_round_waveform_tohw, 64 + .write_waveform = argon_fan_hat_write_waveform, 65 + /* 66 + * The controller does not provide any way to read info back, 67 + * reading from the controller stops the fan, therefore there 68 + * is no .read_waveform here. 69 + */ 70 + }; 71 + 72 + static int argon_fan_hat_i2c_probe(struct i2c_client *i2c) 73 + { 74 + struct pwm_chip *chip = devm_pwmchip_alloc(&i2c->dev, 1, 0); 75 + int ret; 76 + 77 + if (IS_ERR(chip)) 78 + return PTR_ERR(chip); 79 + 80 + chip->ops = &argon_fan_hat_pwm_ops; 81 + pwmchip_set_drvdata(chip, i2c); 82 + 83 + ret = devm_pwmchip_add(&i2c->dev, chip); 84 + if (ret) 85 + return dev_err_probe(&i2c->dev, ret, "Could not add PWM chip\n"); 86 + 87 + return 0; 88 + } 89 + 90 + static const struct of_device_id argon_fan_hat_dt_ids[] = { 91 + { .compatible = "argon40,fan-hat" }, 92 + { }, 93 + }; 94 + MODULE_DEVICE_TABLE(of, argon_fan_hat_dt_ids); 95 + 96 + static struct i2c_driver argon_fan_hat_driver = { 97 + .driver = { 98 + .name = "argon-fan-hat", 99 + .probe_type = PROBE_PREFER_ASYNCHRONOUS, 100 + .of_match_table = argon_fan_hat_dt_ids, 101 + }, 102 + .probe = argon_fan_hat_i2c_probe, 103 + }; 104 + 105 + module_i2c_driver(argon_fan_hat_driver); 106 + 107 + MODULE_AUTHOR("Marek Vasut <marek.vasut+renesas@mailbox.org>"); 108 + MODULE_DESCRIPTION("Argon40 Fan HAT"); 109 + MODULE_LICENSE("GPL");
-12
drivers/pwm/pwm-atmel.c
··· 91 91 * hardware. 92 92 */ 93 93 u32 update_pending; 94 - 95 - /* Protects .update_pending */ 96 - spinlock_t lock; 97 94 }; 98 95 99 96 static inline struct atmel_pwm_chip *to_atmel_pwm_chip(struct pwm_chip *chip) ··· 142 145 143 146 static void atmel_pwm_set_pending(struct atmel_pwm_chip *chip, unsigned int ch) 144 147 { 145 - spin_lock(&chip->lock); 146 - 147 148 /* 148 149 * Clear pending flags in hardware because otherwise there might still 149 150 * be a stale flag in ISR. ··· 149 154 atmel_pwm_update_pending(chip); 150 155 151 156 chip->update_pending |= (1 << ch); 152 - 153 - spin_unlock(&chip->lock); 154 157 } 155 158 156 159 static int atmel_pwm_test_pending(struct atmel_pwm_chip *chip, unsigned int ch) 157 160 { 158 161 int ret = 0; 159 - 160 - spin_lock(&chip->lock); 161 162 162 163 if (chip->update_pending & (1 << ch)) { 163 164 atmel_pwm_update_pending(chip); ··· 161 170 if (chip->update_pending & (1 << ch)) 162 171 ret = 1; 163 172 } 164 - 165 - spin_unlock(&chip->lock); 166 173 167 174 return ret; 168 175 } ··· 498 509 atmel_pwm->data = of_device_get_match_data(&pdev->dev); 499 510 500 511 atmel_pwm->update_pending = 0; 501 - spin_lock_init(&atmel_pwm->lock); 502 512 503 513 atmel_pwm->base = devm_platform_ioremap_resource(pdev, 0); 504 514 if (IS_ERR(atmel_pwm->base))
-8
drivers/pwm/pwm-clps711x.c
··· 14 14 struct clps711x_chip { 15 15 void __iomem *pmpcon; 16 16 struct clk *clk; 17 - spinlock_t lock; 18 17 }; 19 18 20 19 static inline struct clps711x_chip *to_clps711x_chip(struct pwm_chip *chip) ··· 41 42 struct clps711x_chip *priv = to_clps711x_chip(chip); 42 43 /* PWM0 - bits 4..7, PWM1 - bits 8..11 */ 43 44 u32 shift = (pwm->hwpwm + 1) * 4; 44 - unsigned long flags; 45 45 u32 pmpcon, val; 46 46 47 47 if (state->polarity != PWM_POLARITY_NORMAL) ··· 54 56 else 55 57 val = 0; 56 58 57 - spin_lock_irqsave(&priv->lock, flags); 58 - 59 59 pmpcon = readl(priv->pmpcon); 60 60 pmpcon &= ~(0xf << shift); 61 61 pmpcon |= val << shift; 62 62 writel(pmpcon, priv->pmpcon); 63 - 64 - spin_unlock_irqrestore(&priv->lock, flags); 65 63 66 64 return 0; 67 65 } ··· 86 92 return PTR_ERR(priv->clk); 87 93 88 94 chip->ops = &clps711x_pwm_ops; 89 - 90 - spin_lock_init(&priv->lock); 91 95 92 96 return devm_pwmchip_add(&pdev->dev, chip); 93 97 }
+7 -21
drivers/pwm/pwm-fsl-ftm.c
··· 10 10 #include <linux/io.h> 11 11 #include <linux/kernel.h> 12 12 #include <linux/module.h> 13 - #include <linux/mutex.h> 14 13 #include <linux/of.h> 15 14 #include <linux/platform_device.h> 16 15 #include <linux/pm.h> ··· 39 40 }; 40 41 41 42 struct fsl_pwm_chip { 42 - struct mutex lock; 43 43 struct regmap *regmap; 44 44 45 45 /* This value is valid iff a pwm is running */ ··· 87 89 struct fsl_pwm_chip *fpc = to_fsl_chip(chip); 88 90 89 91 ret = clk_prepare_enable(fpc->ipg_clk); 90 - if (!ret && fpc->soc->has_enable_bits) { 91 - mutex_lock(&fpc->lock); 92 + if (!ret && fpc->soc->has_enable_bits) 92 93 regmap_set_bits(fpc->regmap, FTM_SC, BIT(pwm->hwpwm + 16)); 93 - mutex_unlock(&fpc->lock); 94 - } 95 94 96 95 return ret; 97 96 } ··· 97 102 { 98 103 struct fsl_pwm_chip *fpc = to_fsl_chip(chip); 99 104 100 - if (fpc->soc->has_enable_bits) { 101 - mutex_lock(&fpc->lock); 105 + if (fpc->soc->has_enable_bits) 102 106 regmap_clear_bits(fpc->regmap, FTM_SC, BIT(pwm->hwpwm + 16)); 103 - mutex_unlock(&fpc->lock); 104 - } 105 107 106 108 clk_disable_unprepare(fpc->ipg_clk); 107 109 } ··· 296 304 { 297 305 struct fsl_pwm_chip *fpc = to_fsl_chip(chip); 298 306 struct pwm_state *oldstate = &pwm->state; 299 - int ret = 0; 307 + int ret; 300 308 301 309 /* 302 310 * oldstate to newstate : action ··· 307 315 * disabled to enabled : update settings + enable 308 316 */ 309 317 310 - mutex_lock(&fpc->lock); 311 - 312 318 if (!newstate->enabled) { 313 319 if (oldstate->enabled) { 314 320 regmap_set_bits(fpc->regmap, FTM_OUTMASK, ··· 315 325 clk_disable_unprepare(fpc->clk[fpc->period.clk_select]); 316 326 } 317 327 318 - goto end_mutex; 328 + return 0; 319 329 } 320 330 321 331 ret = fsl_pwm_apply_config(chip, pwm, newstate); 322 332 if (ret) 323 - goto end_mutex; 333 + return ret; 324 334 325 335 /* check if need to enable */ 326 336 if (!oldstate->enabled) { 327 337 ret = clk_prepare_enable(fpc->clk[fpc->period.clk_select]); 328 338 if (ret) 329 - goto end_mutex; 339 + return ret; 330 340 331 341 ret = clk_prepare_enable(fpc->clk[FSL_PWM_CLK_CNTEN]); 332 342 if (ret) { 333 343 clk_disable_unprepare(fpc->clk[fpc->period.clk_select]); 334 - goto end_mutex; 344 + return ret; 335 345 } 336 346 337 347 regmap_clear_bits(fpc->regmap, FTM_OUTMASK, BIT(pwm->hwpwm)); 338 348 } 339 349 340 - end_mutex: 341 - mutex_unlock(&fpc->lock); 342 350 return ret; 343 351 } 344 352 ··· 395 407 if (IS_ERR(chip)) 396 408 return PTR_ERR(chip); 397 409 fpc = to_fsl_chip(chip); 398 - 399 - mutex_init(&fpc->lock); 400 410 401 411 fpc->soc = of_device_get_match_data(&pdev->dev); 402 412
-2
drivers/pwm/pwm-img.c
··· 139 139 (timebase << PWM_CH_CFG_TMBASE_SHIFT); 140 140 img_pwm_writel(imgchip, PWM_CH_CFG(pwm->hwpwm), val); 141 141 142 - pm_runtime_mark_last_busy(pwmchip_parent(chip)); 143 142 pm_runtime_put_autosuspend(pwmchip_parent(chip)); 144 143 145 144 return 0; ··· 174 175 val &= ~BIT(pwm->hwpwm); 175 176 img_pwm_writel(imgchip, PWM_CTRL_CFG, val); 176 177 177 - pm_runtime_mark_last_busy(pwmchip_parent(chip)); 178 178 pm_runtime_put_autosuspend(pwmchip_parent(chip)); 179 179 } 180 180
-14
drivers/pwm/pwm-lpc18xx-sct.c
··· 100 100 u64 max_period_ns; 101 101 unsigned int period_event; 102 102 unsigned long event_map; 103 - struct mutex res_lock; 104 - struct mutex period_lock; 105 103 struct lpc18xx_pwm_data channeldata[LPC18XX_NUM_PWMS]; 106 104 }; 107 105 ··· 127 129 { 128 130 u32 val; 129 131 130 - mutex_lock(&lpc18xx_pwm->res_lock); 131 - 132 132 /* 133 133 * Simultaneous set and clear may happen on an output, that is the case 134 134 * when duty_ns == period_ns. LPC18xx SCT allows to set a conflict ··· 136 140 val &= ~LPC18XX_PWM_RES_MASK(pwm->hwpwm); 137 141 val |= LPC18XX_PWM_RES(pwm->hwpwm, action); 138 142 lpc18xx_pwm_writel(lpc18xx_pwm, LPC18XX_PWM_RES_BASE, val); 139 - 140 - mutex_unlock(&lpc18xx_pwm->res_lock); 141 143 } 142 144 143 145 static void lpc18xx_pwm_config_period(struct pwm_chip *chip, u64 period_ns) ··· 194 200 return -ERANGE; 195 201 } 196 202 197 - mutex_lock(&lpc18xx_pwm->period_lock); 198 - 199 203 requested_events = bitmap_weight(&lpc18xx_pwm->event_map, 200 204 LPC18XX_PWM_EVENT_MAX); 201 205 ··· 206 214 lpc18xx_pwm->period_ns) { 207 215 dev_err(pwmchip_parent(chip), "conflicting period requested for PWM %u\n", 208 216 pwm->hwpwm); 209 - mutex_unlock(&lpc18xx_pwm->period_lock); 210 217 return -EBUSY; 211 218 } 212 219 ··· 214 223 lpc18xx_pwm->period_ns = period_ns; 215 224 lpc18xx_pwm_config_period(chip, period_ns); 216 225 } 217 - 218 - mutex_unlock(&lpc18xx_pwm->period_lock); 219 226 220 227 lpc18xx_pwm_config_duty(chip, pwm, duty_ns); 221 228 ··· 365 376 */ 366 377 if (lpc18xx_pwm->clk_rate > NSEC_PER_SEC) 367 378 return dev_err_probe(&pdev->dev, -EINVAL, "pwm clock to fast\n"); 368 - 369 - mutex_init(&lpc18xx_pwm->res_lock); 370 - mutex_init(&lpc18xx_pwm->period_lock); 371 379 372 380 lpc18xx_pwm->max_period_ns = 373 381 mul_u64_u64_div_u64(NSEC_PER_SEC, LPC18XX_PWM_TIMER_MAX, lpc18xx_pwm->clk_rate);
+18 -2
drivers/pwm/pwm-mc33xs2410.c
··· 17 17 * behavior of the output pin that is neither the old nor the new state, 18 18 * rather something in between. 19 19 */ 20 + #define DEFAULT_SYMBOL_NAMESPACE "PWM_MC33XS2410" 20 21 22 + #include <linux/auxiliary_bus.h> 21 23 #include <linux/bitfield.h> 22 24 #include <linux/delay.h> 23 25 #include <linux/err.h> 24 26 #include <linux/math64.h> 27 + #include <linux/mc33xs2410.h> 25 28 #include <linux/minmax.h> 26 29 #include <linux/module.h> 27 30 #include <linux/of.h> ··· 123 120 return mc33xs2410_read_regs(spi, &reg, flag, val, 1); 124 121 } 125 122 126 - static int mc33xs2410_read_reg_ctrl(struct spi_device *spi, u8 reg, u16 *val) 123 + int mc33xs2410_read_reg_ctrl(struct spi_device *spi, u8 reg, u16 *val) 127 124 { 128 125 return mc33xs2410_read_reg(spi, reg, val, MC33XS2410_FRAME_IN_DATA_RD); 129 126 } 127 + EXPORT_SYMBOL_GPL(mc33xs2410_read_reg_ctrl); 130 128 131 - static int mc33xs2410_modify_reg(struct spi_device *spi, u8 reg, u8 mask, u8 val) 129 + int mc33xs2410_read_reg_diag(struct spi_device *spi, u8 reg, u16 *val) 130 + { 131 + return mc33xs2410_read_reg(spi, reg, val, 0); 132 + } 133 + EXPORT_SYMBOL_GPL(mc33xs2410_read_reg_diag); 134 + 135 + int mc33xs2410_modify_reg(struct spi_device *spi, u8 reg, u8 mask, u8 val) 132 136 { 133 137 u16 tmp; 134 138 int ret; ··· 149 139 150 140 return mc33xs2410_write_reg(spi, reg, tmp); 151 141 } 142 + EXPORT_SYMBOL_GPL(mc33xs2410_modify_reg); 152 143 153 144 static u8 mc33xs2410_pwm_get_freq(u64 period) 154 145 { ··· 325 314 static int mc33xs2410_probe(struct spi_device *spi) 326 315 { 327 316 struct device *dev = &spi->dev; 317 + struct auxiliary_device *adev; 328 318 struct pwm_chip *chip; 329 319 int ret; 330 320 ··· 372 360 ret = devm_pwmchip_add(dev, chip); 373 361 if (ret < 0) 374 362 return dev_err_probe(dev, ret, "Failed to add pwm chip\n"); 363 + 364 + adev = devm_auxiliary_device_create(dev, "hwmon", NULL); 365 + if (!adev) 366 + return dev_err_probe(dev, -ENODEV, "Failed to register hwmon device\n"); 375 367 376 368 return 0; 377 369 }
+23 -15
drivers/pwm/pwm-mediatek.c
··· 29 29 #define PWM45DWIDTH_FIXUP 0x30 30 30 #define PWMTHRES 0x30 31 31 #define PWM45THRES_FIXUP 0x34 32 + #define PWM_CK_26M_SEL_V3 0x74 32 33 #define PWM_CK_26M_SEL 0x210 33 34 34 35 #define PWM_CLK_DIV_MAX 7 ··· 37 36 struct pwm_mediatek_of_data { 38 37 unsigned int num_pwms; 39 38 bool pwm45_fixup; 40 - bool has_ck_26m_sel; 39 + u16 pwm_ck_26m_sel_reg; 41 40 const unsigned int *reg_offset; 42 41 }; 43 42 ··· 63 62 64 63 static const unsigned int mtk_pwm_reg_offset_v2[] = { 65 64 0x0080, 0x00c0, 0x0100, 0x0140, 0x0180, 0x01c0, 0x0200, 0x0240 65 + }; 66 + 67 + /* PWM IP Version 3.0.2 */ 68 + static const unsigned int mtk_pwm_reg_offset_v3[] = { 69 + 0x0100, 0x0200, 0x0300, 0x0400, 0x0500, 0x0600, 0x0700, 0x0800 66 70 }; 67 71 68 72 static inline struct pwm_mediatek_chip * ··· 142 136 } 143 137 144 138 /* Make sure we use the bus clock and not the 26MHz clock */ 145 - if (pc->soc->has_ck_26m_sel) 146 - writel(0, pc->regs + PWM_CK_26M_SEL); 139 + if (pc->soc->pwm_ck_26m_sel_reg) 140 + writel(0, pc->regs + pc->soc->pwm_ck_26m_sel_reg); 147 141 148 142 /* Using resolution in picosecond gets accuracy higher */ 149 143 resolution = (u64)NSEC_PER_SEC * 1000; ··· 300 294 static const struct pwm_mediatek_of_data mt2712_pwm_data = { 301 295 .num_pwms = 8, 302 296 .pwm45_fixup = false, 303 - .has_ck_26m_sel = false, 304 297 .reg_offset = mtk_pwm_reg_offset_v1, 305 298 }; 306 299 307 300 static const struct pwm_mediatek_of_data mt6795_pwm_data = { 308 301 .num_pwms = 7, 309 302 .pwm45_fixup = false, 310 - .has_ck_26m_sel = false, 311 303 .reg_offset = mtk_pwm_reg_offset_v1, 312 304 }; 313 305 314 306 static const struct pwm_mediatek_of_data mt7622_pwm_data = { 315 307 .num_pwms = 6, 316 308 .pwm45_fixup = false, 317 - .has_ck_26m_sel = true, 309 + .pwm_ck_26m_sel_reg = PWM_CK_26M_SEL, 318 310 .reg_offset = mtk_pwm_reg_offset_v1, 319 311 }; 320 312 321 313 static const struct pwm_mediatek_of_data mt7623_pwm_data = { 322 314 .num_pwms = 5, 323 315 .pwm45_fixup = true, 324 - .has_ck_26m_sel = false, 325 316 .reg_offset = mtk_pwm_reg_offset_v1, 326 317 }; 327 318 328 319 static const struct pwm_mediatek_of_data mt7628_pwm_data = { 329 320 .num_pwms = 4, 330 321 .pwm45_fixup = true, 331 - .has_ck_26m_sel = false, 332 322 .reg_offset = mtk_pwm_reg_offset_v1, 333 323 }; 334 324 335 325 static const struct pwm_mediatek_of_data mt7629_pwm_data = { 336 326 .num_pwms = 1, 337 327 .pwm45_fixup = false, 338 - .has_ck_26m_sel = false, 339 328 .reg_offset = mtk_pwm_reg_offset_v1, 340 329 }; 341 330 342 331 static const struct pwm_mediatek_of_data mt7981_pwm_data = { 343 332 .num_pwms = 3, 344 333 .pwm45_fixup = false, 345 - .has_ck_26m_sel = true, 334 + .pwm_ck_26m_sel_reg = PWM_CK_26M_SEL, 346 335 .reg_offset = mtk_pwm_reg_offset_v2, 347 336 }; 348 337 349 338 static const struct pwm_mediatek_of_data mt7986_pwm_data = { 350 339 .num_pwms = 2, 351 340 .pwm45_fixup = false, 352 - .has_ck_26m_sel = true, 341 + .pwm_ck_26m_sel_reg = PWM_CK_26M_SEL, 353 342 .reg_offset = mtk_pwm_reg_offset_v1, 354 343 }; 355 344 356 345 static const struct pwm_mediatek_of_data mt7988_pwm_data = { 357 346 .num_pwms = 8, 358 347 .pwm45_fixup = false, 359 - .has_ck_26m_sel = false, 360 348 .reg_offset = mtk_pwm_reg_offset_v2, 361 349 }; 362 350 363 351 static const struct pwm_mediatek_of_data mt8183_pwm_data = { 364 352 .num_pwms = 4, 365 353 .pwm45_fixup = false, 366 - .has_ck_26m_sel = true, 354 + .pwm_ck_26m_sel_reg = PWM_CK_26M_SEL, 367 355 .reg_offset = mtk_pwm_reg_offset_v1, 368 356 }; 369 357 370 358 static const struct pwm_mediatek_of_data mt8365_pwm_data = { 371 359 .num_pwms = 3, 372 360 .pwm45_fixup = false, 373 - .has_ck_26m_sel = true, 361 + .pwm_ck_26m_sel_reg = PWM_CK_26M_SEL, 374 362 .reg_offset = mtk_pwm_reg_offset_v1, 375 363 }; 376 364 377 365 static const struct pwm_mediatek_of_data mt8516_pwm_data = { 378 366 .num_pwms = 5, 379 367 .pwm45_fixup = false, 380 - .has_ck_26m_sel = true, 368 + .pwm_ck_26m_sel_reg = PWM_CK_26M_SEL, 381 369 .reg_offset = mtk_pwm_reg_offset_v1, 370 + }; 371 + 372 + static const struct pwm_mediatek_of_data mt6991_pwm_data = { 373 + .num_pwms = 4, 374 + .pwm45_fixup = false, 375 + .pwm_ck_26m_sel_reg = PWM_CK_26M_SEL_V3, 376 + .reg_offset = mtk_pwm_reg_offset_v3, 382 377 }; 383 378 384 379 static const struct of_device_id pwm_mediatek_of_match[] = { 385 380 { .compatible = "mediatek,mt2712-pwm", .data = &mt2712_pwm_data }, 386 381 { .compatible = "mediatek,mt6795-pwm", .data = &mt6795_pwm_data }, 382 + { .compatible = "mediatek,mt6991-pwm", .data = &mt6991_pwm_data }, 387 383 { .compatible = "mediatek,mt7622-pwm", .data = &mt7622_pwm_data }, 388 384 { .compatible = "mediatek,mt7623-pwm", .data = &mt7623_pwm_data }, 389 385 { .compatible = "mediatek,mt7628-pwm", .data = &mt7628_pwm_data },
+1 -16
drivers/pwm/pwm-microchip-core.c
··· 36 36 #include <linux/ktime.h> 37 37 #include <linux/math.h> 38 38 #include <linux/module.h> 39 - #include <linux/mutex.h> 40 39 #include <linux/of.h> 41 40 #include <linux/platform_device.h> 42 41 #include <linux/pwm.h> ··· 55 56 struct mchp_core_pwm_chip { 56 57 struct clk *clk; 57 58 void __iomem *base; 58 - struct mutex lock; /* protects the shared period */ 59 59 ktime_t update_timestamp; 60 60 u32 sync_update_mask; 61 61 u16 channel_enabled; ··· 358 360 const struct pwm_state *state) 359 361 { 360 362 struct mchp_core_pwm_chip *mchp_core_pwm = to_mchp_core_pwm(chip); 361 - int ret; 362 - 363 - mutex_lock(&mchp_core_pwm->lock); 364 363 365 364 mchp_core_pwm_wait_for_sync_update(mchp_core_pwm, pwm->hwpwm); 366 365 367 - ret = mchp_core_pwm_apply_locked(chip, pwm, state); 368 - 369 - mutex_unlock(&mchp_core_pwm->lock); 370 - 371 - return ret; 366 + return mchp_core_pwm_apply_locked(chip, pwm, state); 372 367 } 373 368 374 369 static int mchp_core_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm, ··· 371 380 u64 rate; 372 381 u16 prescale, period_steps; 373 382 u8 duty_steps, posedge, negedge; 374 - 375 - mutex_lock(&mchp_core_pwm->lock); 376 383 377 384 mchp_core_pwm_wait_for_sync_update(mchp_core_pwm, pwm->hwpwm); 378 385 ··· 403 414 404 415 posedge = readb_relaxed(mchp_core_pwm->base + MCHPCOREPWM_POSEDGE(pwm->hwpwm)); 405 416 negedge = readb_relaxed(mchp_core_pwm->base + MCHPCOREPWM_NEGEDGE(pwm->hwpwm)); 406 - 407 - mutex_unlock(&mchp_core_pwm->lock); 408 417 409 418 if (negedge == posedge) { 410 419 state->duty_cycle = state->period; ··· 455 468 if (of_property_read_u32(pdev->dev.of_node, "microchip,sync-update-mask", 456 469 &mchp_core_pwm->sync_update_mask)) 457 470 mchp_core_pwm->sync_update_mask = 0; 458 - 459 - mutex_init(&mchp_core_pwm->lock); 460 471 461 472 chip->ops = &mchp_core_pwm_ops; 462 473
+6
drivers/pwm/pwm-pxa.c
··· 25 25 #include <linux/io.h> 26 26 #include <linux/pwm.h> 27 27 #include <linux/of.h> 28 + #include <linux/reset.h> 28 29 29 30 #include <asm/div64.h> 30 31 ··· 162 161 struct pwm_chip *chip; 163 162 struct pxa_pwm_chip *pc; 164 163 struct device *dev = &pdev->dev; 164 + struct reset_control *rst; 165 165 int ret = 0; 166 166 167 167 if (IS_ENABLED(CONFIG_OF) && id == NULL) ··· 180 178 pc->clk = devm_clk_get(dev, NULL); 181 179 if (IS_ERR(pc->clk)) 182 180 return dev_err_probe(dev, PTR_ERR(pc->clk), "Failed to get clock\n"); 181 + 182 + rst = devm_reset_control_get_optional_exclusive_deasserted(dev, NULL); 183 + if (IS_ERR(rst)) 184 + return PTR_ERR(rst); 183 185 184 186 chip->ops = &pxa_pwm_ops; 185 187
+20 -13
drivers/pwm/pwm-rockchip.c
··· 8 8 9 9 #include <linux/clk.h> 10 10 #include <linux/io.h> 11 + #include <linux/limits.h> 12 + #include <linux/math64.h> 11 13 #include <linux/module.h> 12 14 #include <linux/of.h> 13 15 #include <linux/platform_device.h> ··· 63 61 struct pwm_state *state) 64 62 { 65 63 struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip); 64 + u64 prescaled_ns = (u64)pc->data->prescaler * NSEC_PER_SEC; 66 65 u32 enable_conf = pc->data->enable_conf; 67 66 unsigned long clk_rate; 68 67 u64 tmp; ··· 81 78 clk_rate = clk_get_rate(pc->clk); 82 79 83 80 tmp = readl_relaxed(pc->base + pc->data->regs.period); 84 - tmp *= pc->data->prescaler * NSEC_PER_SEC; 85 - state->period = DIV_ROUND_CLOSEST_ULL(tmp, clk_rate); 81 + tmp *= prescaled_ns; 82 + state->period = DIV_U64_ROUND_UP(tmp, clk_rate); 86 83 87 84 tmp = readl_relaxed(pc->base + pc->data->regs.duty); 88 - tmp *= pc->data->prescaler * NSEC_PER_SEC; 89 - state->duty_cycle = DIV_ROUND_CLOSEST_ULL(tmp, clk_rate); 85 + tmp *= prescaled_ns; 86 + state->duty_cycle = DIV_U64_ROUND_UP(tmp, clk_rate); 90 87 91 88 val = readl_relaxed(pc->base + pc->data->regs.ctrl); 92 89 state->enabled = (val & enable_conf) == enable_conf; ··· 106 103 const struct pwm_state *state) 107 104 { 108 105 struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip); 109 - unsigned long period, duty; 110 - u64 clk_rate, div; 106 + u64 prescaled_ns = (u64)pc->data->prescaler * NSEC_PER_SEC; 107 + u64 clk_rate, tmp; 108 + u32 period_ticks, duty_ticks; 111 109 u32 ctrl; 112 110 113 111 clk_rate = clk_get_rate(pc->clk); ··· 118 114 * bits, every possible input period can be obtained using the 119 115 * default prescaler value for all practical clock rate values. 120 116 */ 121 - div = clk_rate * state->period; 122 - period = DIV_ROUND_CLOSEST_ULL(div, 123 - pc->data->prescaler * NSEC_PER_SEC); 117 + tmp = mul_u64_u64_div_u64(clk_rate, state->period, prescaled_ns); 118 + if (tmp > U32_MAX) 119 + tmp = U32_MAX; 120 + period_ticks = tmp; 124 121 125 - div = clk_rate * state->duty_cycle; 126 - duty = DIV_ROUND_CLOSEST_ULL(div, pc->data->prescaler * NSEC_PER_SEC); 122 + tmp = mul_u64_u64_div_u64(clk_rate, state->duty_cycle, prescaled_ns); 123 + if (tmp > U32_MAX) 124 + tmp = U32_MAX; 125 + duty_ticks = tmp; 127 126 128 127 /* 129 128 * Lock the period and duty of previous configuration, then ··· 138 131 writel_relaxed(ctrl, pc->base + pc->data->regs.ctrl); 139 132 } 140 133 141 - writel(period, pc->base + pc->data->regs.period); 142 - writel(duty, pc->base + pc->data->regs.duty); 134 + writel(period_ticks, pc->base + pc->data->regs.period); 135 + writel(duty_ticks, pc->base + pc->data->regs.duty); 143 136 144 137 if (pc->data->supports_polarity) { 145 138 ctrl &= ~PWM_POLARITY_MASK;
+39 -13
drivers/pwm/pwm-sifive.c
··· 4 4 * For SiFive's PWM IP block documentation please refer Chapter 14 of 5 5 * Reference Manual : https://static.dev.sifive.com/FU540-C000-v1.0.pdf 6 6 * 7 + * PWM output inversion: According to the SiFive Reference manual 8 + * the output of each comparator is high whenever the value of pwms is 9 + * greater than or equal to the corresponding pwmcmpX[Reference Manual]. 10 + * 11 + * Figure 29 in the same manual shows that the pwmcmpXcenter bit is 12 + * hard-tied to 0 (XNOR), which effectively inverts the comparison so that 13 + * the output goes HIGH when `pwms < pwmcmpX`. 14 + * 15 + * In other words, each pwmcmp register actually defines the **inactive** 16 + * (low) period of the pulse, not the active time exactly opposite to what 17 + * the documentation text implies. 18 + * 19 + * To compensate, this driver always **inverts** the duty value when reading 20 + * or writing pwmcmp registers , so that users interact with a conventional 21 + * **active-high** PWM interface. 22 + * 23 + * 7 24 * Limitations: 8 25 * - When changing both duty cycle and period, we cannot prevent in 9 26 * software that the output might produce a period with mixed 10 27 * settings (new period length and old duty cycle). 11 - * - The hardware cannot generate a 100% duty cycle. 28 + * - The hardware cannot generate a 0% duty cycle. 12 29 * - The hardware generates only inverted output. 13 30 */ 14 31 #include <linux/clk.h> ··· 118 101 119 102 /* As scale <= 15 the shift operation cannot overflow. */ 120 103 num = (unsigned long long)NSEC_PER_SEC << (PWM_SIFIVE_CMPWIDTH + scale); 121 - ddata->real_period = div64_ul(num, rate); 104 + ddata->real_period = DIV_ROUND_UP_ULL(num, rate); 122 105 dev_dbg(ddata->parent, 123 106 "New real_period = %u ns\n", ddata->real_period); 124 107 } ··· 127 110 struct pwm_state *state) 128 111 { 129 112 struct pwm_sifive_ddata *ddata = pwm_sifive_chip_to_ddata(chip); 130 - u32 duty, val; 113 + u32 duty, val, inactive; 131 114 132 - duty = readl(ddata->regs + PWM_SIFIVE_PWMCMP(pwm->hwpwm)); 115 + inactive = readl(ddata->regs + PWM_SIFIVE_PWMCMP(pwm->hwpwm)); 116 + /* 117 + * PWM hardware uses 'inactive' counts in pwmcmp, so invert to get actual duty. 118 + * Here, 'inactive' is the low time and we compute duty as max_count - inactive. 119 + */ 120 + duty = (1U << PWM_SIFIVE_CMPWIDTH) - 1 - inactive; 133 121 134 122 state->enabled = duty > 0; 135 123 ··· 143 121 state->enabled = false; 144 122 145 123 state->period = ddata->real_period; 146 - state->duty_cycle = 147 - (u64)duty * ddata->real_period >> PWM_SIFIVE_CMPWIDTH; 148 - state->polarity = PWM_POLARITY_INVERSED; 124 + state->duty_cycle = DIV_ROUND_UP_ULL((u64)duty * ddata->real_period, 125 + (1U << PWM_SIFIVE_CMPWIDTH)); 126 + state->polarity = PWM_POLARITY_NORMAL; 149 127 150 128 return 0; 151 129 } ··· 159 137 unsigned long long num; 160 138 bool enabled; 161 139 int ret = 0; 162 - u32 frac; 140 + u64 frac; 141 + u32 inactive; 163 142 164 - if (state->polarity != PWM_POLARITY_INVERSED) 143 + if (state->polarity != PWM_POLARITY_NORMAL) 165 144 return -EINVAL; 166 145 167 146 cur_state = pwm->state; ··· 179 156 * consecutively 180 157 */ 181 158 num = (u64)duty_cycle * (1U << PWM_SIFIVE_CMPWIDTH); 182 - frac = DIV64_U64_ROUND_CLOSEST(num, state->period); 183 - /* The hardware cannot generate a 100% duty cycle */ 184 - frac = min(frac, (1U << PWM_SIFIVE_CMPWIDTH) - 1); 159 + frac = num; 160 + do_div(frac, state->period); 161 + /* The hardware cannot generate a 0% duty cycle */ 162 + frac = min(frac, (u64)(1U << PWM_SIFIVE_CMPWIDTH) - 1); 163 + /* pwmcmp register must be loaded with the inactive(invert the duty) */ 164 + inactive = (1U << PWM_SIFIVE_CMPWIDTH) - 1 - frac; 185 165 186 166 mutex_lock(&ddata->lock); 187 167 if (state->period != ddata->approx_period) { ··· 216 190 } 217 191 } 218 192 219 - writel(frac, ddata->regs + PWM_SIFIVE_PWMCMP(pwm->hwpwm)); 193 + writel(inactive, ddata->regs + PWM_SIFIVE_PWMCMP(pwm->hwpwm)); 220 194 221 195 if (!state->enabled) 222 196 clk_disable(ddata->clk);
+125 -18
drivers/pwm/pwm-sophgo-sg2042.c
··· 13 13 * the running period. 14 14 * - When PERIOD and HLPERIOD is set to 0, the PWM wave output will 15 15 * be stopped and the output is pulled to high. 16 + * - SG2044 supports both polarities, SG2042 only normal polarity. 16 17 * See the datasheet [1] for more details. 17 18 * [1]:https://github.com/sophgo/sophgo-doc/tree/main/SG2042/TRM 18 19 */ ··· 42 41 #define SG2042_PWM_HLPERIOD(chan) ((chan) * 8 + 0) 43 42 #define SG2042_PWM_PERIOD(chan) ((chan) * 8 + 4) 44 43 44 + #define SG2044_PWM_POLARITY 0x40 45 + #define SG2044_PWM_PWMSTART 0x44 46 + #define SG2044_PWM_OE 0xd0 47 + 45 48 #define SG2042_PWM_CHANNELNUM 4 46 49 47 50 /** ··· 56 51 struct sg2042_pwm_ddata { 57 52 void __iomem *base; 58 53 unsigned long clk_rate_hz; 54 + }; 55 + 56 + struct sg2042_chip_data { 57 + const struct pwm_ops ops; 59 58 }; 60 59 61 60 /* ··· 75 66 writel(hlperiod_ticks, base + SG2042_PWM_HLPERIOD(chan)); 76 67 } 77 68 78 - static int pwm_sg2042_apply(struct pwm_chip *chip, struct pwm_device *pwm, 79 - const struct pwm_state *state) 69 + static void pwm_sg2042_set_dutycycle(struct pwm_chip *chip, struct pwm_device *pwm, 70 + const struct pwm_state *state) 80 71 { 81 72 struct sg2042_pwm_ddata *ddata = pwmchip_get_drvdata(chip); 82 73 u32 hlperiod_ticks; 83 74 u32 period_ticks; 75 + 76 + /* 77 + * Duration of High level (duty_cycle) = HLPERIOD x Period_of_input_clk 78 + * Duration of One Cycle (period) = PERIOD x Period_of_input_clk 79 + */ 80 + period_ticks = min(mul_u64_u64_div_u64(ddata->clk_rate_hz, state->period, NSEC_PER_SEC), U32_MAX); 81 + hlperiod_ticks = min(mul_u64_u64_div_u64(ddata->clk_rate_hz, state->duty_cycle, NSEC_PER_SEC), U32_MAX); 82 + 83 + dev_dbg(pwmchip_parent(chip), "chan[%u]: ENABLE=%u, PERIOD=%u, HLPERIOD=%u, POLARITY=%u\n", 84 + pwm->hwpwm, state->enabled, period_ticks, hlperiod_ticks, state->polarity); 85 + 86 + pwm_sg2042_config(ddata, pwm->hwpwm, period_ticks, hlperiod_ticks); 87 + } 88 + 89 + static int pwm_sg2042_apply(struct pwm_chip *chip, struct pwm_device *pwm, 90 + const struct pwm_state *state) 91 + { 92 + struct sg2042_pwm_ddata *ddata = pwmchip_get_drvdata(chip); 84 93 85 94 if (state->polarity == PWM_POLARITY_INVERSED) 86 95 return -EINVAL; ··· 108 81 return 0; 109 82 } 110 83 111 - /* 112 - * Duration of High level (duty_cycle) = HLPERIOD x Period_of_input_clk 113 - * Duration of One Cycle (period) = PERIOD x Period_of_input_clk 114 - */ 115 - period_ticks = min(mul_u64_u64_div_u64(ddata->clk_rate_hz, state->period, NSEC_PER_SEC), U32_MAX); 116 - hlperiod_ticks = min(mul_u64_u64_div_u64(ddata->clk_rate_hz, state->duty_cycle, NSEC_PER_SEC), U32_MAX); 117 - 118 - dev_dbg(pwmchip_parent(chip), "chan[%u]: PERIOD=%u, HLPERIOD=%u\n", 119 - pwm->hwpwm, period_ticks, hlperiod_ticks); 120 - 121 - pwm_sg2042_config(ddata, pwm->hwpwm, period_ticks, hlperiod_ticks); 84 + pwm_sg2042_set_dutycycle(chip, pwm, state); 122 85 123 86 return 0; 124 87 } ··· 140 123 return 0; 141 124 } 142 125 143 - static const struct pwm_ops pwm_sg2042_ops = { 144 - .apply = pwm_sg2042_apply, 145 - .get_state = pwm_sg2042_get_state, 126 + static void pwm_sg2044_set_outputen(struct sg2042_pwm_ddata *ddata, struct pwm_device *pwm, 127 + bool enabled) 128 + { 129 + u32 pwmstart; 130 + 131 + pwmstart = readl(ddata->base + SG2044_PWM_PWMSTART); 132 + 133 + if (enabled) 134 + pwmstart |= BIT(pwm->hwpwm); 135 + else 136 + pwmstart &= ~BIT(pwm->hwpwm); 137 + 138 + writel(pwmstart, ddata->base + SG2044_PWM_PWMSTART); 139 + } 140 + 141 + static void pwm_sg2044_set_outputdir(struct sg2042_pwm_ddata *ddata, struct pwm_device *pwm, 142 + bool enabled) 143 + { 144 + u32 pwm_oe; 145 + 146 + pwm_oe = readl(ddata->base + SG2044_PWM_OE); 147 + 148 + if (enabled) 149 + pwm_oe |= BIT(pwm->hwpwm); 150 + else 151 + pwm_oe &= ~BIT(pwm->hwpwm); 152 + 153 + writel(pwm_oe, ddata->base + SG2044_PWM_OE); 154 + } 155 + 156 + static void pwm_sg2044_set_polarity(struct sg2042_pwm_ddata *ddata, struct pwm_device *pwm, 157 + const struct pwm_state *state) 158 + { 159 + u32 pwm_polarity; 160 + 161 + pwm_polarity = readl(ddata->base + SG2044_PWM_POLARITY); 162 + 163 + if (state->polarity == PWM_POLARITY_NORMAL) 164 + pwm_polarity &= ~BIT(pwm->hwpwm); 165 + else 166 + pwm_polarity |= BIT(pwm->hwpwm); 167 + 168 + writel(pwm_polarity, ddata->base + SG2044_PWM_POLARITY); 169 + } 170 + 171 + static int pwm_sg2044_apply(struct pwm_chip *chip, struct pwm_device *pwm, 172 + const struct pwm_state *state) 173 + { 174 + struct sg2042_pwm_ddata *ddata = pwmchip_get_drvdata(chip); 175 + 176 + pwm_sg2044_set_polarity(ddata, pwm, state); 177 + 178 + pwm_sg2042_set_dutycycle(chip, pwm, state); 179 + 180 + /* 181 + * re-enable PWMSTART to refresh the register period 182 + */ 183 + pwm_sg2044_set_outputen(ddata, pwm, false); 184 + 185 + if (!state->enabled) 186 + return 0; 187 + 188 + pwm_sg2044_set_outputdir(ddata, pwm, true); 189 + pwm_sg2044_set_outputen(ddata, pwm, true); 190 + 191 + return 0; 192 + } 193 + 194 + static const struct sg2042_chip_data sg2042_chip_data = { 195 + .ops = { 196 + .apply = pwm_sg2042_apply, 197 + .get_state = pwm_sg2042_get_state, 198 + }, 199 + }; 200 + 201 + static const struct sg2042_chip_data sg2044_chip_data = { 202 + .ops = { 203 + .apply = pwm_sg2044_apply, 204 + .get_state = pwm_sg2042_get_state, 205 + }, 146 206 }; 147 207 148 208 static const struct of_device_id sg2042_pwm_ids[] = { 149 - { .compatible = "sophgo,sg2042-pwm" }, 209 + { 210 + .compatible = "sophgo,sg2042-pwm", 211 + .data = &sg2042_chip_data 212 + }, 213 + { 214 + .compatible = "sophgo,sg2044-pwm", 215 + .data = &sg2044_chip_data 216 + }, 150 217 { } 151 218 }; 152 219 MODULE_DEVICE_TABLE(of, sg2042_pwm_ids); ··· 238 137 static int pwm_sg2042_probe(struct platform_device *pdev) 239 138 { 240 139 struct device *dev = &pdev->dev; 140 + const struct sg2042_chip_data *chip_data; 241 141 struct sg2042_pwm_ddata *ddata; 242 142 struct reset_control *rst; 243 143 struct pwm_chip *chip; 244 144 struct clk *clk; 245 145 int ret; 146 + 147 + chip_data = device_get_match_data(dev); 148 + if (!chip_data) 149 + return -ENODEV; 246 150 247 151 chip = devm_pwmchip_alloc(dev, SG2042_PWM_CHANNELNUM, sizeof(*ddata)); 248 152 if (IS_ERR(chip)) ··· 276 170 if (IS_ERR(rst)) 277 171 return dev_err_probe(dev, PTR_ERR(rst), "Failed to get reset\n"); 278 172 279 - chip->ops = &pwm_sg2042_ops; 173 + chip->ops = &chip_data->ops; 280 174 chip->atomic = true; 281 175 282 176 ret = devm_pwmchip_add(dev, chip); ··· 296 190 module_platform_driver(pwm_sg2042_driver); 297 191 298 192 MODULE_AUTHOR("Chen Wang"); 193 + MODULE_AUTHOR("Longbin Li <looong.bin@gmail.com>"); 299 194 MODULE_DESCRIPTION("Sophgo SG2042 PWM driver"); 300 195 MODULE_LICENSE("GPL");
+6 -17
drivers/pwm/pwm-sti.c
··· 92 92 struct pwm_device *cur; 93 93 unsigned long configured; 94 94 unsigned int en_count; 95 - struct mutex sti_pwm_lock; /* To sync between enable/disable calls */ 96 95 void __iomem *mmio; 97 96 }; 98 97 ··· 243 244 { 244 245 struct sti_pwm_chip *pc = to_sti_pwmchip(chip); 245 246 struct device *dev = pc->dev; 246 - int ret = 0; 247 + int ret; 247 248 248 249 /* 249 250 * Since we have a common enable for all PWM devices, do not enable if 250 251 * already enabled. 251 252 */ 252 - mutex_lock(&pc->sti_pwm_lock); 253 253 254 254 if (!pc->en_count) { 255 255 ret = clk_enable(pc->pwm_clk); 256 256 if (ret) 257 - goto out; 257 + return ret; 258 258 259 259 ret = clk_enable(pc->cpt_clk); 260 260 if (ret) 261 - goto out; 261 + return ret; 262 262 263 263 ret = regmap_field_write(pc->pwm_out_en, 1); 264 264 if (ret) { 265 265 dev_err(dev, "failed to enable PWM device %u: %d\n", 266 266 pwm->hwpwm, ret); 267 - goto out; 267 + return ret; 268 268 } 269 269 } 270 270 271 271 pc->en_count++; 272 272 273 - out: 274 - mutex_unlock(&pc->sti_pwm_lock); 275 - return ret; 273 + return 0; 276 274 } 277 275 278 276 static void sti_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) 279 277 { 280 278 struct sti_pwm_chip *pc = to_sti_pwmchip(chip); 281 279 282 - mutex_lock(&pc->sti_pwm_lock); 283 - 284 - if (--pc->en_count) { 285 - mutex_unlock(&pc->sti_pwm_lock); 280 + if (--pc->en_count) 286 281 return; 287 - } 288 282 289 283 regmap_field_write(pc->pwm_out_en, 0); 290 284 291 285 clk_disable(pc->pwm_clk); 292 286 clk_disable(pc->cpt_clk); 293 - 294 - mutex_unlock(&pc->sti_pwm_lock); 295 287 } 296 288 297 289 static void sti_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm) ··· 584 594 585 595 pc->dev = dev; 586 596 pc->en_count = 0; 587 - mutex_init(&pc->sti_pwm_lock); 588 597 589 598 ret = sti_pwm_probe_regmap(pc); 590 599 if (ret)
+35 -7
drivers/pwm/pwm-stm32.c
··· 19 19 #define CCMR_CHANNEL_SHIFT 8 20 20 #define CCMR_CHANNEL_MASK 0xFF 21 21 #define MAX_BREAKINPUT 2 22 + #define STM32_MAX_PWM_OUTPUT 4 22 23 23 24 struct stm32_breakinput { 24 25 u32 index; ··· 769 768 return stm32_pwm_apply_breakinputs(priv); 770 769 } 771 770 772 - static void stm32_pwm_detect_complementary(struct stm32_pwm *priv) 771 + static void stm32_pwm_detect_complementary(struct stm32_pwm *priv, struct stm32_timers *ddata) 773 772 { 774 773 u32 ccer; 774 + 775 + if (ddata->ipidr) { 776 + u32 val; 777 + 778 + /* Simply read from HWCFGR the number of complementary outputs (MP25). */ 779 + regmap_read(priv->regmap, TIM_HWCFGR1, &val); 780 + priv->have_complementary_output = !!FIELD_GET(TIM_HWCFGR1_NB_OF_DT, val); 781 + return; 782 + } 775 783 776 784 /* 777 785 * If complementary bit doesn't exist writing 1 will have no ··· 793 783 priv->have_complementary_output = (ccer != 0); 794 784 } 795 785 796 - static unsigned int stm32_pwm_detect_channels(struct regmap *regmap, 786 + static unsigned int stm32_pwm_detect_channels(struct stm32_timers *ddata, 797 787 unsigned int *num_enabled) 798 788 { 789 + struct regmap *regmap = ddata->regmap; 799 790 u32 ccer, ccer_backup; 791 + 792 + regmap_read(regmap, TIM_CCER, &ccer_backup); 793 + *num_enabled = hweight32(ccer_backup & TIM_CCER_CCXE); 794 + 795 + if (ddata->ipidr) { 796 + u32 hwcfgr; 797 + unsigned int npwm; 798 + 799 + /* Deduce from HWCFGR the number of outputs (MP25). */ 800 + regmap_read(regmap, TIM_HWCFGR1, &hwcfgr); 801 + 802 + /* 803 + * Timers may have more capture/compare channels than the 804 + * actual number of PWM channel outputs (e.g. TIM_CH[1..4]). 805 + */ 806 + npwm = FIELD_GET(TIM_HWCFGR1_NB_OF_CC, hwcfgr); 807 + 808 + return npwm < STM32_MAX_PWM_OUTPUT ? npwm : STM32_MAX_PWM_OUTPUT; 809 + } 800 810 801 811 /* 802 812 * If channels enable bits don't exist writing 1 will have no 803 813 * effect so we can detect and count them. 804 814 */ 805 - regmap_read(regmap, TIM_CCER, &ccer_backup); 806 815 regmap_set_bits(regmap, TIM_CCER, TIM_CCER_CCXE); 807 816 regmap_read(regmap, TIM_CCER, &ccer); 808 817 regmap_write(regmap, TIM_CCER, ccer_backup); 809 - 810 - *num_enabled = hweight32(ccer_backup & TIM_CCER_CCXE); 811 818 812 819 return hweight32(ccer & TIM_CCER_CCXE); 813 820 } ··· 840 813 unsigned int i; 841 814 int ret; 842 815 843 - npwm = stm32_pwm_detect_channels(ddata->regmap, &num_enabled); 816 + npwm = stm32_pwm_detect_channels(ddata, &num_enabled); 844 817 845 818 chip = devm_pwmchip_alloc(dev, npwm, sizeof(*priv)); 846 819 if (IS_ERR(chip)) ··· 861 834 return dev_err_probe(dev, ret, 862 835 "Failed to configure breakinputs\n"); 863 836 864 - stm32_pwm_detect_complementary(priv); 837 + stm32_pwm_detect_complementary(priv, ddata); 865 838 866 839 ret = devm_clk_rate_exclusive_get(dev, priv->clk); 867 840 if (ret) ··· 934 907 935 908 static const struct of_device_id stm32_pwm_of_match[] = { 936 909 { .compatible = "st,stm32-pwm", }, 910 + { .compatible = "st,stm32mp25-pwm", }, 937 911 { /* end node */ }, 938 912 }; 939 913 MODULE_DEVICE_TABLE(of, stm32_pwm_of_match);
-10
drivers/pwm/pwm-sun4i.c
··· 21 21 #include <linux/pwm.h> 22 22 #include <linux/reset.h> 23 23 #include <linux/slab.h> 24 - #include <linux/spinlock.h> 25 24 #include <linux/time.h> 26 25 27 26 #define PWM_CTRL_REG 0x0 ··· 84 85 struct clk *clk; 85 86 struct reset_control *rst; 86 87 void __iomem *base; 87 - spinlock_t ctrl_lock; 88 88 const struct sun4i_pwm_data *data; 89 89 }; 90 90 ··· 256 258 return ret; 257 259 } 258 260 259 - spin_lock(&sun4ichip->ctrl_lock); 260 261 ctrl = sun4i_pwm_readl(sun4ichip, PWM_CTRL_REG); 261 262 262 263 if (sun4ichip->data->has_direct_mod_clk_output) { ··· 263 266 ctrl |= BIT_CH(PWM_BYPASS, pwm->hwpwm); 264 267 /* We can skip other parameter */ 265 268 sun4i_pwm_writel(sun4ichip, ctrl, PWM_CTRL_REG); 266 - spin_unlock(&sun4ichip->ctrl_lock); 267 269 return 0; 268 270 } 269 271 ··· 293 297 294 298 sun4i_pwm_writel(sun4ichip, ctrl, PWM_CTRL_REG); 295 299 296 - spin_unlock(&sun4ichip->ctrl_lock); 297 - 298 300 if (state->enabled) 299 301 return 0; 300 302 ··· 303 309 else 304 310 usleep_range(delay_us, delay_us * 2); 305 311 306 - spin_lock(&sun4ichip->ctrl_lock); 307 312 ctrl = sun4i_pwm_readl(sun4ichip, PWM_CTRL_REG); 308 313 ctrl &= ~BIT_CH(PWM_CLK_GATING, pwm->hwpwm); 309 314 ctrl &= ~BIT_CH(PWM_EN, pwm->hwpwm); 310 315 sun4i_pwm_writel(sun4ichip, ctrl, PWM_CTRL_REG); 311 - spin_unlock(&sun4ichip->ctrl_lock); 312 316 313 317 clk_disable_unprepare(sun4ichip->clk); 314 318 ··· 447 455 } 448 456 449 457 chip->ops = &sun4i_pwm_ops; 450 - 451 - spin_lock_init(&sun4ichip->ctrl_lock); 452 458 453 459 ret = pwmchip_add(chip); 454 460 if (ret < 0) {
+7 -42
drivers/pwm/pwm-twl-led.c
··· 61 61 #define TWL6040_LED_MODE_OFF 0x02 62 62 #define TWL6040_LED_MODE_MASK 0x03 63 63 64 - struct twl_pwmled_chip { 65 - struct mutex mutex; 66 - }; 67 - 68 64 static inline struct twl_pwmled_chip *to_twl(struct pwm_chip *chip) 69 65 { 70 66 return pwmchip_get_drvdata(chip); ··· 102 106 103 107 static int twl4030_pwmled_enable(struct pwm_chip *chip, struct pwm_device *pwm) 104 108 { 105 - struct twl_pwmled_chip *twl = to_twl(chip); 106 109 int ret; 107 110 u8 val; 108 111 109 - mutex_lock(&twl->mutex); 110 112 ret = twl_i2c_read_u8(TWL4030_MODULE_LED, &val, TWL4030_LEDEN_REG); 111 113 if (ret < 0) { 112 114 dev_err(pwmchip_parent(chip), "%s: Failed to read LEDEN\n", pwm->label); 113 - goto out; 115 + return ret; 114 116 } 115 117 116 118 val |= TWL4030_LED_TOGGLE(pwm->hwpwm, TWL4030_LED_PINS); ··· 117 123 if (ret < 0) 118 124 dev_err(pwmchip_parent(chip), "%s: Failed to enable PWM\n", pwm->label); 119 125 120 - out: 121 - mutex_unlock(&twl->mutex); 122 126 return ret; 123 127 } 124 128 125 129 static void twl4030_pwmled_disable(struct pwm_chip *chip, 126 130 struct pwm_device *pwm) 127 131 { 128 - struct twl_pwmled_chip *twl = to_twl(chip); 129 132 int ret; 130 133 u8 val; 131 134 132 - mutex_lock(&twl->mutex); 133 135 ret = twl_i2c_read_u8(TWL4030_MODULE_LED, &val, TWL4030_LEDEN_REG); 134 136 if (ret < 0) { 135 137 dev_err(pwmchip_parent(chip), "%s: Failed to read LEDEN\n", pwm->label); 136 - goto out; 138 + return; 137 139 } 138 140 139 141 val &= ~TWL4030_LED_TOGGLE(pwm->hwpwm, TWL4030_LED_PINS); ··· 137 147 ret = twl_i2c_write_u8(TWL4030_MODULE_LED, val, TWL4030_LEDEN_REG); 138 148 if (ret < 0) 139 149 dev_err(pwmchip_parent(chip), "%s: Failed to disable PWM\n", pwm->label); 140 - 141 - out: 142 - mutex_unlock(&twl->mutex); 143 150 } 144 151 145 152 static int twl4030_pwmled_apply(struct pwm_chip *chip, struct pwm_device *pwm, ··· 196 209 197 210 static int twl6030_pwmled_enable(struct pwm_chip *chip, struct pwm_device *pwm) 198 211 { 199 - struct twl_pwmled_chip *twl = to_twl(chip); 200 212 int ret; 201 213 u8 val; 202 214 203 - mutex_lock(&twl->mutex); 204 215 ret = twl_i2c_read_u8(TWL6030_MODULE_ID1, &val, TWL6030_LED_PWM_CTRL2); 205 216 if (ret < 0) { 206 217 dev_err(pwmchip_parent(chip), "%s: Failed to read PWM_CTRL2\n", 207 218 pwm->label); 208 - goto out; 219 + return ret; 209 220 } 210 221 211 222 val &= ~TWL6040_LED_MODE_MASK; ··· 213 228 if (ret < 0) 214 229 dev_err(pwmchip_parent(chip), "%s: Failed to enable PWM\n", pwm->label); 215 230 216 - out: 217 - mutex_unlock(&twl->mutex); 218 231 return ret; 219 232 } 220 233 221 234 static void twl6030_pwmled_disable(struct pwm_chip *chip, 222 235 struct pwm_device *pwm) 223 236 { 224 - struct twl_pwmled_chip *twl = to_twl(chip); 225 237 int ret; 226 238 u8 val; 227 239 228 - mutex_lock(&twl->mutex); 229 240 ret = twl_i2c_read_u8(TWL6030_MODULE_ID1, &val, TWL6030_LED_PWM_CTRL2); 230 241 if (ret < 0) { 231 242 dev_err(pwmchip_parent(chip), "%s: Failed to read PWM_CTRL2\n", 232 243 pwm->label); 233 - goto out; 244 + return; 234 245 } 235 246 236 247 val &= ~TWL6040_LED_MODE_MASK; ··· 235 254 ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, val, TWL6030_LED_PWM_CTRL2); 236 255 if (ret < 0) 237 256 dev_err(pwmchip_parent(chip), "%s: Failed to disable PWM\n", pwm->label); 238 - 239 - out: 240 - mutex_unlock(&twl->mutex); 241 257 } 242 258 243 259 static int twl6030_pwmled_apply(struct pwm_chip *chip, struct pwm_device *pwm, ··· 265 287 266 288 static int twl6030_pwmled_request(struct pwm_chip *chip, struct pwm_device *pwm) 267 289 { 268 - struct twl_pwmled_chip *twl = to_twl(chip); 269 290 int ret; 270 291 u8 val; 271 292 272 - mutex_lock(&twl->mutex); 273 293 ret = twl_i2c_read_u8(TWL6030_MODULE_ID1, &val, TWL6030_LED_PWM_CTRL2); 274 294 if (ret < 0) { 275 295 dev_err(pwmchip_parent(chip), "%s: Failed to read PWM_CTRL2\n", 276 296 pwm->label); 277 - goto out; 297 + return ret; 278 298 } 279 299 280 300 val &= ~TWL6040_LED_MODE_MASK; ··· 282 306 if (ret < 0) 283 307 dev_err(pwmchip_parent(chip), "%s: Failed to request PWM\n", pwm->label); 284 308 285 - out: 286 - mutex_unlock(&twl->mutex); 287 309 return ret; 288 310 } 289 311 290 312 static void twl6030_pwmled_free(struct pwm_chip *chip, struct pwm_device *pwm) 291 313 { 292 - struct twl_pwmled_chip *twl = to_twl(chip); 293 314 int ret; 294 315 u8 val; 295 316 296 - mutex_lock(&twl->mutex); 297 317 ret = twl_i2c_read_u8(TWL6030_MODULE_ID1, &val, TWL6030_LED_PWM_CTRL2); 298 318 if (ret < 0) { 299 319 dev_err(pwmchip_parent(chip), "%s: Failed to read PWM_CTRL2\n", 300 320 pwm->label); 301 - goto out; 321 + return; 302 322 } 303 323 304 324 val &= ~TWL6040_LED_MODE_MASK; ··· 303 331 ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, val, TWL6030_LED_PWM_CTRL2); 304 332 if (ret < 0) 305 333 dev_err(pwmchip_parent(chip), "%s: Failed to free PWM\n", pwm->label); 306 - 307 - out: 308 - mutex_unlock(&twl->mutex); 309 334 } 310 335 311 336 static const struct pwm_ops twl6030_pwmled_ops = { ··· 314 345 static int twl_pwmled_probe(struct platform_device *pdev) 315 346 { 316 347 struct pwm_chip *chip; 317 - struct twl_pwmled_chip *twl; 318 348 unsigned int npwm; 319 349 const struct pwm_ops *ops; 320 350 ··· 325 357 npwm = 1; 326 358 } 327 359 328 - chip = devm_pwmchip_alloc(&pdev->dev, npwm, sizeof(*twl)); 360 + chip = devm_pwmchip_alloc(&pdev->dev, npwm, 0); 329 361 if (IS_ERR(chip)) 330 362 return PTR_ERR(chip); 331 - twl = to_twl(chip); 332 363 333 364 chip->ops = ops; 334 - 335 - mutex_init(&twl->mutex); 336 365 337 366 return devm_pwmchip_add(&pdev->dev, chip); 338 367 }
+16
include/linux/mc33xs2410.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 2 + /* 3 + * Copyright (C) 2024 Liebherr-Electronics and Drives GmbH 4 + */ 5 + #ifndef _MC33XS2410_H 6 + #define _MC33XS2410_H 7 + 8 + #include <linux/spi/spi.h> 9 + 10 + MODULE_IMPORT_NS("PWM_MC33XS2410"); 11 + 12 + int mc33xs2410_read_reg_ctrl(struct spi_device *spi, u8 reg, u16 *val); 13 + int mc33xs2410_read_reg_diag(struct spi_device *spi, u8 reg, u16 *val); 14 + int mc33xs2410_modify_reg(struct spi_device *spi, u8 reg, u8 mask, u8 val); 15 + 16 + #endif /* _MC33XS2410_H */
+5
include/linux/pwm.h
··· 2 2 #ifndef __LINUX_PWM_H 3 3 #define __LINUX_PWM_H 4 4 5 + #include <linux/cdev.h> 5 6 #include <linux/device.h> 6 7 #include <linux/err.h> 7 8 #include <linux/module.h> ··· 274 273 unsigned int duty_cycle; 275 274 }; 276 275 276 + #define PWM_WFHWSIZE 20 277 + 277 278 /** 278 279 * struct pwm_ops - PWM controller operations 279 280 * @request: optional hook for requesting a PWM ··· 314 311 /** 315 312 * struct pwm_chip - abstract a PWM controller 316 313 * @dev: device providing the PWMs 314 + * @cdev: &struct cdev for this device 317 315 * @ops: callbacks for this PWM controller 318 316 * @owner: module providing this chip 319 317 * @id: unique number of this PWM chip ··· 329 325 */ 330 326 struct pwm_chip { 331 327 struct device dev; 328 + struct cdev cdev; 332 329 const struct pwm_ops *ops; 333 330 struct module *owner; 334 331 unsigned int id;
+53
include/uapi/linux/pwm.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note */ 2 + 3 + #ifndef _UAPI_PWM_H_ 4 + #define _UAPI_PWM_H_ 5 + 6 + #include <linux/ioctl.h> 7 + #include <linux/types.h> 8 + 9 + /** 10 + * struct pwmchip_waveform - Describe a PWM waveform for a pwm_chip's PWM channel 11 + * @hwpwm: per-chip relative index of the PWM device 12 + * @__pad: padding, must be zero 13 + * @period_length_ns: duration of the repeating period. 14 + * A value of 0 represents a disabled PWM. 15 + * @duty_length_ns: duration of the active part in each period 16 + * @duty_offset_ns: offset of the rising edge from a period's start 17 + */ 18 + struct pwmchip_waveform { 19 + __u32 hwpwm; 20 + __u32 __pad; 21 + __u64 period_length_ns; 22 + __u64 duty_length_ns; 23 + __u64 duty_offset_ns; 24 + }; 25 + 26 + /* Reserves the passed hwpwm for exclusive control. */ 27 + #define PWM_IOCTL_REQUEST _IO(0x75, 1) 28 + 29 + /* counter part to PWM_IOCTL_REQUEST */ 30 + #define PWM_IOCTL_FREE _IO(0x75, 2) 31 + 32 + /* 33 + * Modifies the passed wf according to hardware constraints. All parameters are 34 + * rounded down to the next possible value, unless there is no such value, then 35 + * values are rounded up. Note that zero isn't considered for rounding down 36 + * period_length_ns. 37 + */ 38 + #define PWM_IOCTL_ROUNDWF _IOWR(0x75, 3, struct pwmchip_waveform) 39 + 40 + /* Get the currently implemented waveform */ 41 + #define PWM_IOCTL_GETWF _IOWR(0x75, 4, struct pwmchip_waveform) 42 + 43 + /* Like PWM_IOCTL_ROUNDWF + PWM_IOCTL_SETEXACTWF in one go. */ 44 + #define PWM_IOCTL_SETROUNDEDWF _IOW(0x75, 5, struct pwmchip_waveform) 45 + 46 + /* 47 + * Program the PWM to emit exactly the passed waveform, subject only to rounding 48 + * down each value less than 1 ns. Returns 0 on success, -EDOM if the waveform 49 + * cannot be implemented exactly, or other negative error codes. 50 + */ 51 + #define PWM_IOCTL_SETEXACTWF _IOW(0x75, 6, struct pwmchip_waveform) 52 + 53 + #endif /* _UAPI_PWM_H_ */