summaryrefslogtreecommitdiffstats
path: root/drivers/pwm
diff options
context:
space:
mode:
authorJinke Han <jinkehan@didiglobal.com>2026-09-08 15:37:42 +0800
committerIngo Molnar <mingo@kernel.org>2026-09-17 09:48:42 +0200
commita5f7a5bb3b7f28ba7e4fa246775b29a0e5537255 (patch)
tree73fef824f9a846cf935869d0dbc5070ef6e2f741 /drivers/pwm
downloadlinux-stable-a5f7a5bb3b7f28ba7e4fa246775b29a0e5537255.tar.gz
linux-stable-a5f7a5bb3b7f28ba7e4fa246775b29a0e5537255.zip
x86/kprobes: Fix crash when probing CS CALL instructionsgrafted
When using eBPF to probe CS CALL instructions within a function, a crash can be triggered. The eBPF tool probes offset 257 of the __hrtimer_run_queues() function: <__hrtimer_run_queues+249>: nopl 0x0(%rax,%rax,1) <__hrtimer_run_queues+254>: mov %r14,%rdi <__hrtimer_run_queues+257>: cs call <__x86_indirect_thunk_r12> <__hrtimer_run_queues+263>: mov %eax,%r12d <__hrtimer_run_queues+266>: xchg %ax,%ax <__hrtimer_run_queues+268>: mov %r13,%rdi Which triggers this crash: BUG: unable to handle page fault for address: 00000000000f41c9 #PF: supervisor write access in kernel mode #PF: error_code(0x0002) - not-present page PGD 0 P4D 0 Oops: 0002 [#1] SMP NOPTI CPU: 1 PID: 0 Comm: swapper/1 Kdump: loaded Tainted: P RIP: 0010:__hrtimer_run_queues+0x106/0x230 Note that __hrtimer_run_queues+0x106 is __hrtimer_run_queues+262, which is at the 6th byte of the above CS CALL instruction. Since the CS CALL instruction occupies 6 bytes, the exception occurred in the middle of that call instruction. The root cause is that when using eBPF tools to probe in the middle of a function, a kprobe with INT3 is used as the underlying implementation. During single-step emulation of the original CALL instruction, int3_emulate_call() assumes that the probed CALL instruction is 5 bytes long. However, the actual CS-prefixed CALL instruction occupies 6 bytes, so it constructs an incorrect exception return address. When the CPU returns from the kprobe handler, the next instruction to be executed is at the address of the last byte of that CS CALL instruction. Coincidentally, starting from that address, the CPU fetches and decodes a completely different instruction, which ultimately triggers a kernel crash. Fix the issue by using the actual instruction length obtained from the instruction decoder when constructing the exception return address, rather than relying on the hardcoded CALL_INSN_SIZE macro. [ mingo: Refined the changelog ] Fixes: 6256e668b7af ("x86/kprobes: Use int3 instead of debug trap for single-step") Suggested-by: Masami Hiramatsu (Google) <mhiramat@kernel.org> Signed-off-by: Jinke Han <jinkehan@didiglobal.com> Signed-off-by: Ingo Molnar <mingo@kernel.org> Reviewed-by: Masami Hiramatsu (Google) <mhiramat@kernel.org> Acked-by: Yafang Shao <laoar.shao@gmail.com> Acked-by: Borislav Petkov <bp@alien8.de> Cc: Peter Zijlstra <peterz@infradead.org> Link: https://patch.msgid.link/20260908073742.GA10517@didi-ThinkCentre-M920t-N000
Diffstat (limited to 'drivers/pwm')
-rw-r--r--drivers/pwm/Kconfig869
-rw-r--r--drivers/pwm/Makefile80
-rw-r--r--drivers/pwm/core.c2759
-rw-r--r--drivers/pwm/pwm-ab8500.c227
-rw-r--r--drivers/pwm/pwm-adp5585.c222
-rw-r--r--drivers/pwm/pwm-airoha.c621
-rw-r--r--drivers/pwm/pwm-apple.c158
-rw-r--r--drivers/pwm/pwm-argon-fan-hat.c109
-rw-r--r--drivers/pwm/pwm-atmel-hlcdc.c306
-rw-r--r--drivers/pwm/pwm-atmel-tcb.c566
-rw-r--r--drivers/pwm/pwm-atmel.c542
-rw-r--r--drivers/pwm/pwm-axi-pwmgen.c341
-rw-r--r--drivers/pwm/pwm-bcm-iproc.c248
-rw-r--r--drivers/pwm/pwm-bcm-kona.c338
-rw-r--r--drivers/pwm/pwm-bcm2835.c185
-rw-r--r--drivers/pwm/pwm-berlin.c285
-rw-r--r--drivers/pwm/pwm-brcmstb.c293
-rw-r--r--drivers/pwm/pwm-clk.c140
-rw-r--r--drivers/pwm/pwm-clps711x.c110
-rw-r--r--drivers/pwm/pwm-crc.c189
-rw-r--r--drivers/pwm/pwm-cros-ec.c286
-rw-r--r--drivers/pwm/pwm-dwc-core.c183
-rw-r--r--drivers/pwm/pwm-dwc.c171
-rw-r--r--drivers/pwm/pwm-dwc.h74
-rw-r--r--drivers/pwm/pwm-ep93xx.c192
-rw-r--r--drivers/pwm/pwm-fsl-ftm.c572
-rw-r--r--drivers/pwm/pwm-gpio.c239
-rw-r--r--drivers/pwm/pwm-hibvt.c285
-rw-r--r--drivers/pwm/pwm-img.c423
-rw-r--r--drivers/pwm/pwm-imx-tpm.c462
-rw-r--r--drivers/pwm/pwm-imx1.c199
-rw-r--r--drivers/pwm/pwm-imx27.c438
-rw-r--r--drivers/pwm/pwm-intel-lgm.c233
-rw-r--r--drivers/pwm/pwm-ipq.c264
-rw-r--r--drivers/pwm/pwm-iqs620a.c252
-rw-r--r--drivers/pwm/pwm-jz4740.c280
-rw-r--r--drivers/pwm/pwm-keembay.c233
-rw-r--r--drivers/pwm/pwm-loongson.c290
-rw-r--r--drivers/pwm/pwm-lp3943.c317
-rw-r--r--drivers/pwm/pwm-lpc18xx-sct.c440
-rw-r--r--drivers/pwm/pwm-lpc32xx.c175
-rw-r--r--drivers/pwm/pwm-lpss-pci.c74
-rw-r--r--drivers/pwm/pwm-lpss-platform.c81
-rw-r--r--drivers/pwm/pwm-lpss.c293
-rw-r--r--drivers/pwm/pwm-lpss.h29
-rw-r--r--drivers/pwm/pwm-max7360.c209
-rw-r--r--drivers/pwm/pwm-mc33xs2410.c407
-rw-r--r--drivers/pwm/pwm-mediatek.c645
-rw-r--r--drivers/pwm/pwm-meson.c691
-rw-r--r--drivers/pwm/pwm-microchip-core.c490
-rw-r--r--drivers/pwm/pwm-mtk-disp.c317
-rw-r--r--drivers/pwm/pwm-mxs.c183
-rw-r--r--drivers/pwm/pwm-ntxec.c165
-rw-r--r--drivers/pwm/pwm-omap-dmtimer.c466
-rw-r--r--drivers/pwm/pwm-pca9685.c579
-rw-r--r--drivers/pwm/pwm-pxa.c215
-rw-r--r--drivers/pwm/pwm-raspberrypi-poe.c196
-rw-r--r--drivers/pwm/pwm-rcar.c269
-rw-r--r--drivers/pwm/pwm-renesas-tpu.c505
-rw-r--r--drivers/pwm/pwm-rockchip.c402
-rw-r--r--drivers/pwm/pwm-rz-mtu3.c552
-rw-r--r--drivers/pwm/pwm-rzg2l-gpt.c455
-rw-r--r--drivers/pwm/pwm-samsung.c648
-rw-r--r--drivers/pwm/pwm-sifive.c373
-rw-r--r--drivers/pwm/pwm-sl28cpld.c262
-rw-r--r--drivers/pwm/pwm-sophgo-sg2042.c300
-rw-r--r--drivers/pwm/pwm-spear.c262
-rw-r--r--drivers/pwm/pwm-sprd.c295
-rw-r--r--drivers/pwm/pwm-sti.c644
-rw-r--r--drivers/pwm/pwm-stm32-lp.c432
-rw-r--r--drivers/pwm/pwm-stm32.c939
-rw-r--r--drivers/pwm/pwm-stmpe.c358
-rw-r--r--drivers/pwm/pwm-sun4i.c493
-rw-r--r--drivers/pwm/pwm-sunplus.c232
-rw-r--r--drivers/pwm/pwm-tegra.c499
-rw-r--r--drivers/pwm/pwm-tiecap.c337
-rw-r--r--drivers/pwm/pwm-tiehrpwm.c578
-rw-r--r--drivers/pwm/pwm-twl-led.c358
-rw-r--r--drivers/pwm/pwm-twl.c382
-rw-r--r--drivers/pwm/pwm-visconti.c176
-rw-r--r--drivers/pwm/pwm-vt8500.c277
-rw-r--r--drivers/pwm/pwm-xilinx.c300
-rw-r--r--drivers/pwm/pwm_th1520.rs367
83 files changed, 30331 insertions, 0 deletions
diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
new file mode 100644
index 000000000..729776086
--- /dev/null
+++ b/drivers/pwm/Kconfig
@@ -0,0 +1,869 @@
+# SPDX-License-Identifier: GPL-2.0-only
+menuconfig PWM
+ bool "Pulse-Width Modulation (PWM) Support"
+ help
+ Generic Pulse-Width Modulation (PWM) support.
+
+ In Pulse-Width Modulation, a variation of the width of pulses
+ in a rectangular pulse signal is used as a means to alter the
+ average power of the signal. Applications include efficient
+ power delivery and voltage regulation. In computer systems,
+ PWMs are commonly used to control fans or the brightness of
+ display backlights.
+
+ This framework provides a generic interface to PWM devices
+ within the Linux kernel. On the driver side it provides an API
+ to register and unregister a PWM chip, an abstraction of a PWM
+ controller, that supports one or more PWM devices. Client
+ drivers can request PWM devices and use the generic framework
+ to configure as well as enable and disable them.
+
+ This generic framework replaces the legacy PWM framework which
+ allows only a single driver implementing the required API. Not
+ all legacy implementations have been ported to the framework
+ yet. The framework provides an API that is backward compatible
+ with the legacy framework so that existing client drivers
+ continue to work as expected.
+
+ If unsure, say no.
+
+if PWM
+
+config PWM_DEBUG
+ bool "PWM lowlevel drivers additional checks and debug messages"
+ depends on DEBUG_KERNEL
+ help
+ This option enables some additional checks to help lowlevel driver
+ authors to get their callbacks implemented correctly.
+ It is expected to introduce some runtime overhead and diagnostic
+ output to the kernel log, so only enable while working on a driver.
+
+config PWM_PROVIDE_GPIO
+ bool "Provide a GPIO chip for each PWM chip"
+ depends on GPIOLIB
+ help
+ Most PWMs can emit both a constant active high and a constant active
+ low signal and so they can be used as GPIO. Say Y here to let each
+ PWM chip provide a GPIO chip and so be easily plugged into consumers
+ that know how to handle GPIOs but not PWMs.
+
+config PWM_AB8500
+ tristate "AB8500 PWM support"
+ depends on AB8500_CORE && ARCH_U8500
+ help
+ Generic PWM framework driver for Analog Baseband AB8500.
+
+ To compile this driver as a module, choose M here: the module
+ will be called pwm-ab8500.
+
+config PWM_ADP5585
+ tristate "ADP5585 PWM support"
+ depends on MFD_ADP5585
+ help
+ This option enables support for the PWM function found in the Analog
+ Devices ADP5585.
+
+config PWM_AIROHA
+ tristate "Airoha PWM support"
+ depends on ARCH_AIROHA || COMPILE_TEST
+ select REGMAP_MMIO
+ help
+ Generic PWM framework driver for Airoha SoC.
+
+ To compile this driver as a module, choose M here: the module
+ will be called pwm-airoha.
+
+config PWM_APPLE
+ tristate "Apple SoC PWM support"
+ depends on ARCH_APPLE || COMPILE_TEST
+ help
+ Generic PWM framework driver for PWM controller present on
+ Apple SoCs
+
+ Say Y here if you have an ARM Apple laptop, otherwise say N
+
+ To compile this driver as a module, choose M here: the module
+ will be called pwm-apple.
+
+config PWM_ARGON_FAN_HAT
+ tristate "Argon40 Fan HAT support"
+ depends on I2C && OF
+ help
+ Generic PWM framework driver for Argon40 Fan HAT.
+
+ To compile this driver as a module, choose M here: the module
+ will be called pwm-argon-fan-hat.
+
+config PWM_ATMEL
+ tristate "Atmel PWM support"
+ depends on ARCH_AT91 || COMPILE_TEST
+ depends on HAS_IOMEM && OF
+ help
+ Generic PWM framework driver for Atmel SoC.
+
+ To compile this driver as a module, choose M here: the module
+ will be called pwm-atmel.
+
+config PWM_ATMEL_HLCDC_PWM
+ tristate "Atmel HLCDC PWM support"
+ depends on MFD_ATMEL_HLCDC
+ depends on HAVE_CLK
+ help
+ Generic PWM framework driver for the PWM output of the HLCDC
+ (Atmel High-end LCD Controller). This PWM output is mainly used
+ to control the LCD backlight.
+
+ To compile this driver as a module, choose M here: the module
+ will be called pwm-atmel-hlcdc.
+
+config PWM_ATMEL_TCB
+ tristate "Atmel TC Block PWM support"
+ depends on OF
+ select REGMAP_MMIO
+ help
+ Generic PWM framework driver for Atmel Timer Counter Block.
+
+ A Timer Counter Block provides 6 PWM devices grouped by 2.
+ Devices in a given group must have the same period.
+
+ To compile this driver as a module, choose M here: the module
+ will be called pwm-atmel-tcb.
+
+config PWM_AXI_PWMGEN
+ tristate "Analog Devices AXI PWM generator"
+ depends on MICROBLAZE || NIOS2 || ARCH_ZYNQ || ARCH_ZYNQMP || ARCH_INTEL_SOCFPGA || COMPILE_TEST
+ select REGMAP_MMIO
+ help
+ This enables support for the Analog Devices AXI PWM generator.
+
+ This is a configurable PWM generator with variable pulse width and
+ period.
+
+ To compile this driver as a module, choose M here: the module will be
+ called pwm-axi-pwmgen.
+
+config PWM_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
+ depends on COMMON_CLK && HAS_IOMEM
+ default ARCH_BCM_IPROC
+ help
+ Generic PWM framework driver for Broadcom iProc PWM block. This
+ block is used in Broadcom iProc SoC's.
+
+ To compile this driver as a module, choose M here: the module
+ will be called pwm-bcm-iproc.
+
+config PWM_BCM_KONA
+ tristate "Kona PWM support"
+ depends on ARCH_BCM_MOBILE || ARCH_BCM_CYGNUS || COMPILE_TEST
+ depends on HAVE_CLK && HAS_IOMEM
+ default ARCH_BCM_MOBILE || ARCH_BCM_CYGNUS
+ help
+ Generic PWM framework driver for Broadcom Kona PWM block.
+
+ To compile this driver as a module, choose M here: the module
+ will be called pwm-bcm-kona.
+
+config PWM_BERLIN
+ tristate "Marvell Berlin PWM support"
+ depends on ARCH_BERLIN || COMPILE_TEST
+ depends on HAS_IOMEM
+ help
+ PWM framework driver for Marvell Berlin SoCs.
+
+ To compile this driver as a module, choose M here: the module
+ will be called pwm-berlin.
+
+config PWM_BRCMSTB
+ tristate "Broadcom STB PWM support"
+ depends on ARCH_BRCMSTB || BMIPS_GENERIC || COMPILE_TEST
+ depends on HAS_IOMEM
+ help
+ Generic PWM framework driver for the Broadcom Set-top-Box
+ SoCs (BCM7xxx).
+
+ To compile this driver as a module, choose M Here: the module
+ will be called pwm-brcmstb.c.
+
+config PWM_CLK
+ tristate "Clock based PWM support"
+ depends on HAVE_CLK || COMPILE_TEST
+ help
+ Generic PWM framework driver for outputs that can be
+ muxed to clocks.
+
+ To compile this driver as a module, choose M here: the module
+ will be called pwm-clk.
+
+config PWM_CLPS711X
+ tristate "CLPS711X PWM support"
+ depends on ARCH_CLPS711X || COMPILE_TEST
+ depends on HAS_IOMEM
+ help
+ Generic PWM framework driver for Cirrus Logic CLPS711X.
+
+ To compile this driver as a module, choose M here: the module
+ will be called pwm-clps711x.
+
+config PWM_CRC
+ tristate "Intel Crystalcove (CRC) PWM support"
+ depends on INTEL_SOC_PMIC
+ help
+ Generic PWM framework driver for Crystalcove (CRC) PMIC based PWM
+ control.
+
+config PWM_CROS_EC
+ tristate "ChromeOS EC PWM driver"
+ depends on CROS_EC
+ help
+ PWM driver for exposing a PWM attached to the ChromeOS Embedded
+ Controller.
+
+config PWM_DWC_CORE
+ tristate
+ depends on HAS_IOMEM
+ help
+ PWM driver for Synopsys DWC PWM Controller.
+
+ To compile this driver as a module, build the dependecies as
+ modules, this will be called pwm-dwc-core.
+
+config PWM_DWC
+ tristate "DesignWare PWM Controller (PCI bus)"
+ depends on HAS_IOMEM && PCI
+ select PWM_DWC_CORE
+ help
+ PWM driver for Synopsys DWC PWM Controller attached to a PCI bus.
+
+ To compile this driver as a module, choose M here: the module
+ will be called pwm-dwc.
+
+config PWM_EP93XX
+ tristate "Cirrus Logic EP93xx PWM support"
+ depends on ARCH_EP93XX || COMPILE_TEST
+ depends on HAS_IOMEM
+ help
+ Generic PWM framework driver for Cirrus Logic EP93xx.
+
+ To compile this driver as a module, choose M here: the module
+ will be called pwm-ep93xx.
+
+config PWM_FSL_FTM
+ tristate "Freescale FlexTimer Module (FTM) PWM support"
+ depends on HAS_IOMEM
+ depends on OF
+ select REGMAP_MMIO
+ help
+ Generic FTM PWM framework driver for Freescale VF610 and
+ Layerscape LS-1 SoCs.
+
+ To compile this driver as a module, choose M here: the module
+ will be called pwm-fsl-ftm.
+
+config PWM_GPIO
+ tristate "GPIO PWM support"
+ depends on GPIOLIB
+ depends on HIGH_RES_TIMERS
+ help
+ Generic PWM framework driver for software PWM toggling a GPIO pin
+ from kernel high-resolution timers.
+
+ To compile this driver as a module, choose M here: the module
+ will be called pwm-gpio.
+
+config PWM_HIBVT
+ tristate "HiSilicon BVT PWM support"
+ depends on ARCH_HISI || COMPILE_TEST
+ depends on HAS_IOMEM
+ help
+ Generic PWM framework driver for HiSilicon BVT SoCs.
+
+ To compile this driver as a module, choose M here: the module
+ will be called pwm-hibvt.
+
+config PWM_IMG
+ tristate "Imagination Technologies PWM driver"
+ depends on HAS_IOMEM
+ depends on MFD_SYSCON
+ depends on COMMON_CLK
+ depends on MIPS || COMPILE_TEST
+ help
+ Generic PWM framework driver for Imagination Technologies
+ PWM block which supports 4 channels.
+
+ To compile this driver as a module, choose M here: the module
+ will be called pwm-img
+
+config PWM_IMX1
+ tristate "i.MX1 PWM support"
+ depends on ARCH_MXC || COMPILE_TEST
+ depends on HAS_IOMEM
+ help
+ Generic PWM framework driver for i.MX1 and i.MX21
+
+ To compile this driver as a module, choose M here: the module
+ will be called pwm-imx1.
+
+config PWM_IMX27
+ tristate "i.MX27 PWM support"
+ depends on ARCH_MXC || COMPILE_TEST
+ depends on HAS_IOMEM
+ help
+ Generic PWM framework driver for i.MX27 and later i.MX SoCs.
+
+ To compile this driver as a module, choose M here: the module
+ will be called pwm-imx27.
+
+config PWM_IMX_TPM
+ tristate "i.MX TPM PWM support"
+ depends on ARCH_MXC || COMPILE_TEST
+ depends on HAVE_CLK && HAS_IOMEM
+ help
+ Generic PWM framework driver for i.MX7ULP TPM module, TPM's full
+ name is Low Power Timer/Pulse Width Modulation Module.
+
+ To compile this driver as a module, choose M here: the module
+ will be called pwm-imx-tpm.
+
+config PWM_INTEL_LGM
+ tristate "Intel LGM PWM support"
+ depends on HAS_IOMEM
+ depends on (OF && X86) || COMPILE_TEST
+ select REGMAP_MMIO
+ help
+ Generic PWM fan controller driver for LGM SoC.
+
+ To compile this driver as a module, choose M here: the module
+ will be called pwm-intel-lgm.
+
+config PWM_IPQ
+ tristate "IPQ PWM support"
+ depends on ARCH_QCOM || COMPILE_TEST
+ depends on HAVE_CLK && HAS_IOMEM
+ help
+ Generic PWM framework driver for IPQ PWM block which supports
+ 4 pwm channels. Each of the these channels can be configured
+ independent of each other.
+
+ To compile this driver as a module, choose M here: the module
+ will be called pwm-ipq.
+
+config PWM_IQS620A
+ tristate "Azoteq IQS620A PWM support"
+ depends on MFD_IQS62X || COMPILE_TEST
+ help
+ Generic PWM framework driver for the Azoteq IQS620A multi-function
+ sensor.
+
+ To compile this driver as a module, choose M here: the module will
+ be called pwm-iqs620a.
+
+config PWM_JZ4740
+ tristate "Ingenic JZ47xx PWM support"
+ depends on MACH_INGENIC || COMPILE_TEST
+ depends on COMMON_CLK && OF
+ select MFD_SYSCON
+ help
+ Generic PWM framework driver for Ingenic JZ47xx based
+ machines.
+
+ To compile this driver as a module, choose M here: the module
+ will be called pwm-jz4740.
+
+config PWM_KEEMBAY
+ tristate "Intel Keem Bay PWM driver"
+ depends on ARCH_KEEMBAY || COMPILE_TEST
+ depends on COMMON_CLK && HAS_IOMEM
+ help
+ The platform driver for Intel Keem Bay PWM controller.
+
+ 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
+ help
+ Generic PWM framework driver for LP3943 which supports two PWM
+ channels.
+
+ To compile this driver as a module, choose M here: the module
+ will be called pwm-lp3943.
+
+config PWM_LPC18XX_SCT
+ tristate "LPC18xx/43xx PWM/SCT support"
+ depends on ARCH_LPC18XX || COMPILE_TEST
+ depends on HAS_IOMEM
+ help
+ Generic PWM framework driver for NXP LPC18xx PWM/SCT which
+ supports 16 channels.
+ A maximum of 15 channels can be requested simultaneously and
+ must have the same period.
+
+ To compile this driver as a module, choose M here: the module
+ will be called pwm-lpc18xx-sct.
+
+config PWM_LPC32XX
+ tristate "LPC32XX PWM support"
+ depends on ARCH_LPC32XX || COMPILE_TEST
+ depends on HAS_IOMEM
+ help
+ Generic PWM framework driver for LPC32XX. The LPC32XX SOC has two
+ PWM controllers.
+
+ To compile this driver as a module, choose M here: the module
+ will be called pwm-lpc32xx.
+
+config PWM_LPSS
+ depends on HAS_IOMEM
+ tristate
+
+config PWM_LPSS_PCI
+ tristate "Intel LPSS PWM PCI driver"
+ depends on X86 || COMPILE_TEST
+ depends on HAS_IOMEM && PCI
+ select PWM_LPSS
+ help
+ The PCI driver for Intel Low Power Subsystem PWM controller.
+
+ To compile this driver as a module, choose M here: the module
+ will be called pwm-lpss-pci.
+
+config PWM_LPSS_PLATFORM
+ tristate "Intel LPSS PWM platform driver"
+ depends on (X86 && ACPI) || COMPILE_TEST
+ depends on HAS_IOMEM
+ select PWM_LPSS
+ help
+ The platform driver for Intel Low Power Subsystem PWM controller.
+
+ To compile this driver as a module, choose M here: the module
+ will be called pwm-lpss-platform.
+
+config PWM_MAX7360
+ tristate "MAX7360 PWMs"
+ depends on MFD_MAX7360
+ help
+ PWM driver for Maxim Integrated MAX7360 multifunction device, with
+ support for up to 8 PWM outputs.
+
+ To compile this driver as a module, choose M here: the module
+ will be called pwm-max7360.
+
+config PWM_MC33XS2410
+ tristate "MC33XS2410 PWM support"
+ depends on OF
+ depends on SPI
+ select AUXILIARY_BUS
+ help
+ 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-mc33xs2410.
+
+config PWM_MEDIATEK
+ tristate "MediaTek PWM support"
+ depends on ARCH_MEDIATEK || RALINK || COMPILE_TEST
+ depends on HAS_IOMEM
+ help
+ Generic PWM framework driver for Mediatek ARM SoC.
+
+ 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
+ depends on HAS_IOMEM && OF
+ help
+ PWM driver for Microchip FPGA soft IP 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
+ depends on HAS_IOMEM && OF
+ select STMP_DEVICE
+ help
+ Generic PWM framework driver for Freescale MXS.
+
+ To compile this driver as a module, choose M here: the module
+ will be called pwm-mxs.
+
+config PWM_NTXEC
+ tristate "Netronix embedded controller PWM support"
+ depends on MFD_NTXEC
+ help
+ Say yes here if you want to support the PWM output of the embedded
+ controller found in certain e-book readers designed by the original
+ design manufacturer Netronix.
+
+config PWM_OMAP_DMTIMER
+ tristate "OMAP Dual-Mode Timer PWM support"
+ depends on OF
+ depends on OMAP_DM_TIMER || COMPILE_TEST
+ help
+ Generic PWM framework driver for OMAP Dual-Mode Timer PWM output
+
+ To compile this driver as a module, choose M here: the module
+ will be called pwm-omap-dmtimer
+
+config PWM_PCA9685
+ tristate "NXP PCA9685 PWM driver"
+ depends on I2C
+ select REGMAP_I2C
+ help
+ Generic PWM framework driver for NXP PCA9685 LED controller.
+
+ To compile this driver as a module, choose M here: the module
+ will be called pwm-pca9685.
+
+config PWM_PXA
+ tristate "PXA PWM support"
+ depends on ARCH_PXA || ARCH_MMP || ARCH_SPACEMIT || COMPILE_TEST
+ depends on HAS_IOMEM && OF
+ help
+ Generic PWM framework driver for PXA.
+
+ To compile this driver as a module, choose M here: the module
+ will be called pwm-pxa.
+
+config PWM_RASPBERRYPI_POE
+ tristate "Raspberry Pi Firmware PoE Hat PWM support"
+ # Make sure not 'y' when RASPBERRYPI_FIRMWARE is 'm'. This can only
+ # happen when COMPILE_TEST=y, hence the added !RASPBERRYPI_FIRMWARE.
+ depends on RASPBERRYPI_FIRMWARE || (COMPILE_TEST && !RASPBERRYPI_FIRMWARE)
+ help
+ Enable Raspberry Pi firmware controller PWM bus used to control the
+ official RPI PoE hat
+
+config PWM_RENESAS_RCAR
+ tristate "Renesas R-Car PWM support"
+ depends on ARCH_RENESAS || COMPILE_TEST
+ depends on HAS_IOMEM
+ help
+ This driver exposes the PWM Timer controller found in Renesas
+ R-Car chips through the PWM API.
+
+ 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
+ depends on HAS_IOMEM
+ help
+ This driver exposes the Timer Pulse Unit (TPU) PWM controller found
+ in Renesas chips through the PWM API.
+
+ To compile this driver as a module, choose M here: the module
+ will be called pwm-renesas-tpu.
+
+config PWM_ROCKCHIP
+ tristate "Rockchip PWM support"
+ depends on ARCH_ROCKCHIP || COMPILE_TEST
+ depends on HAS_IOMEM
+ help
+ Generic PWM framework driver for the PWM controller found on
+ Rockchip SoCs.
+
+config PWM_SAMSUNG
+ tristate "Samsung PWM support"
+ depends on PLAT_SAMSUNG || ARCH_S5PV210 || ARCH_EXYNOS || COMPILE_TEST
+ depends on HAS_IOMEM
+ help
+ Generic PWM framework driver for Samsung S3C24xx, S3C64xx, S5Pv210
+ and Exynos SoCs.
+ Choose Y here only if you build for such Samsung SoC.
+
+ To compile this driver as a module, choose M here: the module
+ will be called pwm-samsung.
+
+config PWM_SIFIVE
+ tristate "SiFive PWM support"
+ depends on OF
+ depends on COMMON_CLK && HAS_IOMEM
+ depends on ARCH_SIFIVE || COMPILE_TEST
+ help
+ Generic PWM framework driver for SiFive SoCs.
+
+ To compile this driver as a module, choose M here: the module
+ will be called pwm-sifive.
+
+config PWM_SL28CPLD
+ tristate "Kontron sl28cpld PWM support"
+ depends on MFD_SL28CPLD || COMPILE_TEST
+ help
+ Generic PWM framework driver for board management controller
+ found on the Kontron sl28 CPLD.
+
+ To compile this driver as a module, choose M here: the module
+ will be called pwm-sl28cpld.
+
+config PWM_SOPHGO_SG2042
+ tristate "Sophgo SG2042 PWM support"
+ depends on ARCH_SOPHGO || COMPILE_TEST
+ help
+ PWM driver for the PWM controller on Sophgo SG2042 SoC. The PWM
+ controller supports outputing 4 channels of PWM waveforms.
+
+ To compile this driver as a module, choose M here: the module
+ will be called pwm_sophgo_sg2042.
+
+config PWM_SPEAR
+ tristate "STMicroelectronics SPEAr PWM support"
+ depends on PLAT_SPEAR || COMPILE_TEST
+ depends on HAS_IOMEM && OF
+ help
+ Generic PWM framework driver for the PWM controller on ST
+ SPEAr SoCs.
+
+ To compile this driver as a module, choose M here: the module
+ will be called pwm-spear.
+
+config PWM_SPRD
+ tristate "Spreadtrum PWM support"
+ depends on ARCH_SPRD || COMPILE_TEST
+ depends on HAS_IOMEM
+ help
+ Generic PWM framework driver for the PWM controller on
+ Spreadtrum SoCs.
+
+ To compile this driver as a module, choose M here: the module
+ will be called pwm-sprd.
+
+config PWM_STI
+ tristate "STiH4xx PWM support"
+ depends on ARCH_STI || COMPILE_TEST
+ depends on HAS_IOMEM && OF
+ help
+ Generic PWM framework driver for STiH4xx SoCs.
+
+ To compile this driver as a module, choose M here: the module
+ will be called pwm-sti.
+
+config PWM_STM32
+ tristate "STMicroelectronics STM32 PWM"
+ depends on MFD_STM32_TIMERS || COMPILE_TEST
+ help
+ Generic PWM framework driver for STM32 SoCs.
+
+ To compile this driver as a module, choose M here: the module
+ will be called pwm-stm32.
+
+config PWM_STM32_LP
+ tristate "STMicroelectronics STM32 PWM LP"
+ depends on MFD_STM32_LPTIMER || COMPILE_TEST
+ help
+ Generic PWM framework driver for STMicroelectronics STM32 SoCs
+ with Low-Power Timer (LPTIM).
+
+ To compile this driver as a module, choose M here: the module
+ will be called pwm-stm32-lp.
+
+config PWM_STMPE
+ tristate "STMPE expander PWM export"
+ depends on MFD_STMPE
+ help
+ This enables support for the PWMs found in the STMPE I/O
+ expanders.
+
+config PWM_SUN4I
+ tristate "Allwinner PWM support"
+ depends on ARCH_SUNXI || COMPILE_TEST
+ depends on HAS_IOMEM && COMMON_CLK
+ help
+ Generic PWM framework driver for Allwinner SoCs.
+
+ To compile this driver as a module, choose M here: the module
+ will be called pwm-sun4i.
+
+config PWM_SUNPLUS
+ tristate "Sunplus PWM support"
+ depends on ARCH_SUNPLUS || COMPILE_TEST
+ depends on HAS_IOMEM && OF
+ help
+ Generic PWM framework driver for the PWM controller on
+ Sunplus SoCs.
+
+ To compile this driver as a module, choose M here: the module
+ will be called pwm-sunplus.
+
+config PWM_TEGRA
+ tristate "NVIDIA Tegra PWM support"
+ depends on ARCH_TEGRA || COMPILE_TEST
+ depends on HAS_IOMEM
+ help
+ Generic PWM framework driver for the PWFM controller found on NVIDIA
+ Tegra SoCs.
+
+ To compile this driver as a module, choose M here: the module
+ will be called pwm-tegra.
+
+config PWM_TH1520
+ tristate "TH1520 PWM support"
+ depends on ARCH_THEAD || COMPILE_TEST
+ depends on RUST
+ depends on HAS_IOMEM && COMMON_CLK
+ select RUST_PWM_ABSTRACTIONS
+ help
+ This option enables the driver for the PWM controller found on the
+ T-HEAD TH1520 SoC.
+
+ To compile this driver as a module, choose M here; the module
+ will be called pwm-th1520. If you are unsure, say N.
+
+config PWM_TIECAP
+ tristate "ECAP PWM support"
+ depends on ARCH_OMAP2PLUS || ARCH_DAVINCI_DA8XX || ARCH_KEYSTONE || ARCH_K3 || COMPILE_TEST
+ depends on HAS_IOMEM
+ help
+ PWM driver support for the ECAP APWM controller found on TI SOCs
+
+ To compile this driver as a module, choose M here: the module
+ will be called pwm-tiecap.
+
+config PWM_TIEHRPWM
+ tristate "EHRPWM PWM support"
+ depends on ARCH_OMAP2PLUS || ARCH_DAVINCI_DA8XX || ARCH_K3 || COMPILE_TEST
+ depends on HAS_IOMEM
+ help
+ PWM driver support for the EHRPWM controller found on TI SOCs
+
+ To compile this driver as a module, choose M here: the module
+ will be called pwm-tiehrpwm.
+
+config PWM_TWL
+ tristate "TWL4030/6030 PWM support"
+ depends on TWL4030_CORE
+ help
+ Generic PWM framework driver for TWL4030/6030.
+
+ To compile this driver as a module, choose M here: the module
+ will be called pwm-twl.
+
+config PWM_TWL_LED
+ tristate "TWL4030/6030 PWM support for LED drivers"
+ depends on TWL4030_CORE
+ help
+ Generic PWM framework driver for TWL4030/6030 LED terminals.
+
+ To compile this driver as a module, choose M here: the module
+ will be called pwm-twl-led.
+
+config PWM_VISCONTI
+ tristate "Toshiba Visconti PWM support"
+ depends on ARCH_VISCONTI || COMPILE_TEST
+ help
+ PWM Subsystem driver support for Toshiba Visconti SoCs.
+
+ To compile this driver as a module, choose M here: the module
+ will be called pwm-visconti.
+
+config PWM_VT8500
+ tristate "vt8500 PWM support"
+ depends on ARCH_VT8500 || COMPILE_TEST
+ depends on HAS_IOMEM
+ help
+ Generic PWM framework driver for vt8500.
+
+ To compile this driver as a module, choose M here: the module
+ will be called pwm-vt8500.
+
+config PWM_XILINX
+ tristate "Xilinx AXI Timer PWM support"
+ depends on OF_ADDRESS
+ depends on COMMON_CLK
+ select REGMAP_MMIO
+ help
+ PWM driver for Xilinx LogiCORE IP AXI timers. This timer is
+ typically a soft core which may be present in Xilinx FPGAs.
+ This device may also be present in Microblaze soft processors.
+ If you don't have this IP in your design, choose N.
+
+ To compile this driver as a module, choose M here: the module
+ will be called pwm-xilinx.
+
+ config RUST_PWM_ABSTRACTIONS
+ bool
+ depends on RUST
+ help
+ This option enables the safe Rust abstraction layer for the PWM
+ subsystem. It provides idiomatic wrappers and traits necessary for
+ writing PWM controller drivers in Rust.
+
+ The abstractions handle resource management (like memory and reference
+ counting) and provide safe interfaces to the underlying C core,
+ allowing driver logic to be written in safe Rust.
+
+endif
diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile
new file mode 100644
index 000000000..5630a521a
--- /dev/null
+++ b/drivers/pwm/Makefile
@@ -0,0 +1,80 @@
+# SPDX-License-Identifier: GPL-2.0
+obj-$(CONFIG_PWM) += core.o
+obj-$(CONFIG_PWM_AB8500) += pwm-ab8500.o
+obj-$(CONFIG_PWM_ADP5585) += pwm-adp5585.o
+obj-$(CONFIG_PWM_AIROHA) += pwm-airoha.o
+obj-$(CONFIG_PWM_APPLE) += pwm-apple.o
+obj-$(CONFIG_PWM_ARGON_FAN_HAT) += pwm-argon-fan-hat.o
+obj-$(CONFIG_PWM_ATMEL) += pwm-atmel.o
+obj-$(CONFIG_PWM_ATMEL_HLCDC_PWM) += pwm-atmel-hlcdc.o
+obj-$(CONFIG_PWM_ATMEL_TCB) += pwm-atmel-tcb.o
+obj-$(CONFIG_PWM_AXI_PWMGEN) += pwm-axi-pwmgen.o
+obj-$(CONFIG_PWM_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_BERLIN) += pwm-berlin.o
+obj-$(CONFIG_PWM_BRCMSTB) += pwm-brcmstb.o
+obj-$(CONFIG_PWM_CLK) += pwm-clk.o
+obj-$(CONFIG_PWM_CLPS711X) += pwm-clps711x.o
+obj-$(CONFIG_PWM_CRC) += pwm-crc.o
+obj-$(CONFIG_PWM_CROS_EC) += pwm-cros-ec.o
+obj-$(CONFIG_PWM_DWC_CORE) += pwm-dwc-core.o
+obj-$(CONFIG_PWM_DWC) += pwm-dwc.o
+obj-$(CONFIG_PWM_EP93XX) += pwm-ep93xx.o
+obj-$(CONFIG_PWM_FSL_FTM) += pwm-fsl-ftm.o
+obj-$(CONFIG_PWM_GPIO) += pwm-gpio.o
+obj-$(CONFIG_PWM_HIBVT) += pwm-hibvt.o
+obj-$(CONFIG_PWM_IMG) += pwm-img.o
+obj-$(CONFIG_PWM_IMX1) += pwm-imx1.o
+obj-$(CONFIG_PWM_IMX27) += pwm-imx27.o
+obj-$(CONFIG_PWM_IMX_TPM) += pwm-imx-tpm.o
+obj-$(CONFIG_PWM_INTEL_LGM) += pwm-intel-lgm.o
+obj-$(CONFIG_PWM_IPQ) += pwm-ipq.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_MAX7360) += pwm-max7360.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
+obj-$(CONFIG_PWM_NTXEC) += pwm-ntxec.o
+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_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_SAMSUNG) += pwm-samsung.o
+obj-$(CONFIG_PWM_SIFIVE) += pwm-sifive.o
+obj-$(CONFIG_PWM_SL28CPLD) += pwm-sl28cpld.o
+obj-$(CONFIG_PWM_SOPHGO_SG2042) += pwm-sophgo-sg2042.o
+obj-$(CONFIG_PWM_SPEAR) += pwm-spear.o
+obj-$(CONFIG_PWM_SPRD) += pwm-sprd.o
+obj-$(CONFIG_PWM_STI) += pwm-sti.o
+obj-$(CONFIG_PWM_STM32) += pwm-stm32.o
+obj-$(CONFIG_PWM_STM32_LP) += pwm-stm32-lp.o
+obj-$(CONFIG_PWM_STMPE) += pwm-stmpe.o
+obj-$(CONFIG_PWM_SUN4I) += pwm-sun4i.o
+obj-$(CONFIG_PWM_SUNPLUS) += pwm-sunplus.o
+obj-$(CONFIG_PWM_TEGRA) += pwm-tegra.o
+obj-$(CONFIG_PWM_TH1520) += pwm_th1520.o
+obj-$(CONFIG_PWM_TIECAP) += pwm-tiecap.o
+obj-$(CONFIG_PWM_TIEHRPWM) += pwm-tiehrpwm.o
+obj-$(CONFIG_PWM_TWL) += pwm-twl.o
+obj-$(CONFIG_PWM_TWL_LED) += pwm-twl-led.o
+obj-$(CONFIG_PWM_VISCONTI) += pwm-visconti.o
+obj-$(CONFIG_PWM_VT8500) += pwm-vt8500.o
+obj-$(CONFIG_PWM_XILINX) += pwm-xilinx.o
diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c
new file mode 100644
index 000000000..2a050ff46
--- /dev/null
+++ b/drivers/pwm/core.c
@@ -0,0 +1,2759 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Generic pwmlib implementation
+ *
+ * Copyright (C) 2011 Sascha Hauer <s.hauer@pengutronix.de>
+ * Copyright (C) 2011-2012 Avionic Design GmbH
+ */
+
+#define DEFAULT_SYMBOL_NAMESPACE "PWM"
+
+#include <linux/acpi.h>
+#include <linux/module.h>
+#include <linux/idr.h>
+#include <linux/of.h>
+#include <linux/pwm.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+
+#include <dt-bindings/pwm/pwm.h>
+
+#include <uapi/linux/pwm.h>
+
+#define CREATE_TRACE_POINTS
+#include <trace/events/pwm.h>
+
+#define PWM_MINOR_COUNT 256
+
+/* protects access to pwm_chips */
+static DEFINE_MUTEX(pwm_lock);
+
+static DEFINE_IDR(pwm_chips);
+
+static void pwmchip_lock(struct pwm_chip *chip)
+{
+ if (chip->atomic)
+ spin_lock(&chip->atomic_lock);
+ else
+ mutex_lock(&chip->nonatomic_lock);
+}
+
+static void pwmchip_unlock(struct pwm_chip *chip)
+{
+ if (chip->atomic)
+ spin_unlock(&chip->atomic_lock);
+ else
+ mutex_unlock(&chip->nonatomic_lock);
+}
+
+DEFINE_GUARD(pwmchip, struct pwm_chip *, pwmchip_lock(_T), pwmchip_unlock(_T))
+
+static bool pwm_wf_valid(const struct pwm_waveform *wf)
+{
+ /*
+ * For now restrict waveforms to period_length_ns <= S64_MAX to provide
+ * some space for future extensions. One possibility is to simplify
+ * representing waveforms with inverted polarity using negative values
+ * somehow.
+ */
+ if (wf->period_length_ns > S64_MAX)
+ return false;
+
+ if (wf->duty_length_ns > wf->period_length_ns)
+ return false;
+
+ /*
+ * .duty_offset_ns is supposed to be smaller than .period_length_ns, apart
+ * from the corner case .duty_offset_ns == 0 && .period_length_ns == 0.
+ */
+ if (wf->duty_offset_ns && wf->duty_offset_ns >= wf->period_length_ns)
+ return false;
+
+ return true;
+}
+
+static void pwm_wf2state(const struct pwm_waveform *wf, struct pwm_state *state)
+{
+ if (wf->period_length_ns) {
+ if (wf->duty_length_ns + wf->duty_offset_ns < wf->period_length_ns)
+ *state = (struct pwm_state){
+ .enabled = true,
+ .polarity = PWM_POLARITY_NORMAL,
+ .period = wf->period_length_ns,
+ .duty_cycle = wf->duty_length_ns,
+ };
+ else
+ *state = (struct pwm_state){
+ .enabled = true,
+ .polarity = PWM_POLARITY_INVERSED,
+ .period = wf->period_length_ns,
+ .duty_cycle = wf->period_length_ns - wf->duty_length_ns,
+ };
+ } else {
+ *state = (struct pwm_state){
+ .enabled = false,
+ };
+ }
+}
+
+static void pwm_state2wf(const struct pwm_state *state, struct pwm_waveform *wf)
+{
+ if (state->enabled) {
+ if (state->polarity == PWM_POLARITY_NORMAL)
+ *wf = (struct pwm_waveform){
+ .period_length_ns = state->period,
+ .duty_length_ns = state->duty_cycle,
+ .duty_offset_ns = 0,
+ };
+ else
+ *wf = (struct pwm_waveform){
+ .period_length_ns = state->period,
+ .duty_length_ns = state->period - state->duty_cycle,
+ .duty_offset_ns = state->duty_cycle,
+ };
+ } else {
+ *wf = (struct pwm_waveform){
+ .period_length_ns = 0,
+ };
+ }
+}
+
+static int pwmwfcmp(const struct pwm_waveform *a, const struct pwm_waveform *b)
+{
+ if (a->period_length_ns > b->period_length_ns)
+ return 1;
+
+ if (a->period_length_ns < b->period_length_ns)
+ return -1;
+
+ if (a->duty_length_ns > b->duty_length_ns)
+ return 1;
+
+ if (a->duty_length_ns < b->duty_length_ns)
+ return -1;
+
+ if (a->duty_offset_ns > b->duty_offset_ns)
+ return 1;
+
+ if (a->duty_offset_ns < b->duty_offset_ns)
+ return -1;
+
+ return 0;
+}
+
+static bool pwm_check_rounding(const struct pwm_waveform *wf,
+ const struct pwm_waveform *wf_rounded)
+{
+ if (!wf->period_length_ns)
+ return true;
+
+ if (wf->period_length_ns < wf_rounded->period_length_ns)
+ return false;
+
+ if (wf->duty_length_ns < wf_rounded->duty_length_ns)
+ return false;
+
+ if (wf->duty_offset_ns < wf_rounded->duty_offset_ns)
+ return false;
+
+ return true;
+}
+
+static int __pwm_round_waveform_tohw(struct pwm_chip *chip, struct pwm_device *pwm,
+ const struct pwm_waveform *wf, void *wfhw)
+{
+ const struct pwm_ops *ops = chip->ops;
+ int ret;
+
+ ret = ops->round_waveform_tohw(chip, pwm, wf, wfhw);
+ trace_pwm_round_waveform_tohw(pwm, wf, wfhw, ret);
+
+ return ret;
+}
+
+static int __pwm_round_waveform_fromhw(struct pwm_chip *chip, struct pwm_device *pwm,
+ const void *wfhw, struct pwm_waveform *wf)
+{
+ const struct pwm_ops *ops = chip->ops;
+ int ret;
+
+ ret = ops->round_waveform_fromhw(chip, pwm, wfhw, wf);
+ trace_pwm_round_waveform_fromhw(pwm, wfhw, wf, ret);
+
+ return ret;
+}
+
+static int __pwm_read_waveform(struct pwm_chip *chip, struct pwm_device *pwm, void *wfhw)
+{
+ const struct pwm_ops *ops = chip->ops;
+ int ret;
+
+ ret = ops->read_waveform(chip, pwm, wfhw);
+ trace_pwm_read_waveform(pwm, wfhw, ret);
+
+ return ret;
+}
+
+static int __pwm_write_waveform(struct pwm_chip *chip, struct pwm_device *pwm, const void *wfhw)
+{
+ const struct pwm_ops *ops = chip->ops;
+ int ret;
+
+ ret = ops->write_waveform(chip, pwm, wfhw);
+ trace_pwm_write_waveform(pwm, wfhw, ret);
+
+ return ret;
+}
+
+/**
+ * pwm_round_waveform_might_sleep - Query hardware capabilities
+ * Cannot be used in atomic context.
+ * @pwm: PWM device
+ * @wf: waveform to round and output parameter
+ *
+ * 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.
+ *
+ * Note however that the world doesn't stop turning when you call it, so when
+ * doing::
+ *
+ * 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.
+ *
+ * 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)
+{
+ struct pwm_chip *chip = pwm->chip;
+ const struct pwm_ops *ops = chip->ops;
+ struct pwm_waveform wf_req = *wf;
+ char wfhw[PWM_WFHWSIZE];
+ int ret_tohw, ret_fromhw;
+
+ BUG_ON(PWM_WFHWSIZE < ops->sizeof_wfhw);
+
+ if (!pwmchip_supports_waveform(chip))
+ return -EOPNOTSUPP;
+
+ if (!pwm_wf_valid(wf))
+ return -EINVAL;
+
+ guard(pwmchip)(chip);
+
+ if (!chip->operational)
+ return -ENODEV;
+
+ ret_tohw = __pwm_round_waveform_tohw(chip, pwm, wf, wfhw);
+ if (ret_tohw < 0)
+ return ret_tohw;
+
+ if (IS_ENABLED(CONFIG_PWM_DEBUG) && ret_tohw > 1)
+ dev_err(&chip->dev, "Unexpected return value from __pwm_round_waveform_tohw: requested %llu/%llu [+%llu], return value %d\n",
+ wf_req.duty_length_ns, wf_req.period_length_ns, wf_req.duty_offset_ns, ret_tohw);
+
+ ret_fromhw = __pwm_round_waveform_fromhw(chip, pwm, wfhw, wf);
+ if (ret_fromhw < 0)
+ return ret_fromhw;
+
+ if (IS_ENABLED(CONFIG_PWM_DEBUG) && ret_fromhw > 0)
+ dev_err(&chip->dev, "Unexpected return value from __pwm_round_waveform_fromhw: requested %llu/%llu [+%llu], return value %d\n",
+ wf_req.duty_length_ns, wf_req.period_length_ns, wf_req.duty_offset_ns, ret_fromhw);
+
+ 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], 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, ret_tohw);
+
+ return ret_tohw;
+}
+EXPORT_SYMBOL_GPL(pwm_round_waveform_might_sleep);
+
+/**
+ * pwm_get_waveform_might_sleep - Query hardware about current configuration
+ * Cannot be used in atomic context.
+ * @pwm: PWM device
+ * @wf: output parameter
+ *
+ * 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)
+{
+ struct pwm_chip *chip = pwm->chip;
+ const struct pwm_ops *ops = chip->ops;
+ char wfhw[PWM_WFHWSIZE];
+ int err;
+
+ BUG_ON(PWM_WFHWSIZE < ops->sizeof_wfhw);
+
+ if (!pwmchip_supports_waveform(chip) || !ops->read_waveform)
+ return -EOPNOTSUPP;
+
+ guard(pwmchip)(chip);
+
+ if (!chip->operational)
+ return -ENODEV;
+
+ err = __pwm_read_waveform(chip, pwm, &wfhw);
+ if (err)
+ return err;
+
+ return __pwm_round_waveform_fromhw(chip, pwm, &wfhw, wf);
+}
+EXPORT_SYMBOL_GPL(pwm_get_waveform_might_sleep);
+
+/* Called with the pwmchip lock held */
+static int __pwm_set_waveform(struct pwm_device *pwm,
+ const struct pwm_waveform *wf,
+ bool exact)
+{
+ struct pwm_chip *chip = pwm->chip;
+ const struct pwm_ops *ops = chip->ops;
+ char wfhw[PWM_WFHWSIZE];
+ struct pwm_waveform wf_rounded;
+ int err, ret_tohw;
+
+ BUG_ON(PWM_WFHWSIZE < ops->sizeof_wfhw);
+
+ if (!pwmchip_supports_waveform(chip))
+ return -EOPNOTSUPP;
+
+ if (!pwm_wf_valid(wf))
+ return -EINVAL;
+
+ ret_tohw = __pwm_round_waveform_tohw(chip, pwm, wf, &wfhw);
+ if (ret_tohw < 0)
+ return ret_tohw;
+
+ if ((IS_ENABLED(CONFIG_PWM_DEBUG) || exact) && wf->period_length_ns) {
+ err = __pwm_round_waveform_fromhw(chip, pwm, &wfhw, &wf_rounded);
+ 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], 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, ret_tohw);
+
+ if (exact && pwmwfcmp(wf, &wf_rounded)) {
+ dev_dbg(&chip->dev, "Requested no rounding, but %llu/%llu [+%llu] -> %llu/%llu [+%llu]\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);
+
+ return 1;
+ }
+ }
+
+ err = __pwm_write_waveform(chip, pwm, &wfhw);
+ if (err)
+ return err;
+
+ /* update .state */
+ pwm_wf2state(wf, &pwm->state);
+
+ if (IS_ENABLED(CONFIG_PWM_DEBUG) && ops->read_waveform && wf->period_length_ns) {
+ struct pwm_waveform wf_set;
+
+ err = __pwm_read_waveform(chip, pwm, &wfhw);
+ if (err)
+ /* maybe ignore? */
+ return err;
+
+ err = __pwm_round_waveform_fromhw(chip, pwm, &wfhw, &wf_set);
+ if (err)
+ /* maybe ignore? */
+ return err;
+
+ if (pwmwfcmp(&wf_set, &wf_rounded) != 0)
+ dev_err(&chip->dev,
+ "Unexpected setting: requested %llu/%llu [+%llu], expected %llu/%llu [+%llu], set %llu/%llu [+%llu]\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_set.duty_length_ns, wf_set.period_length_ns, wf_set.duty_offset_ns);
+ }
+
+ return ret_tohw;
+}
+
+/**
+ * pwm_set_waveform_might_sleep - Apply a new waveform
+ * Cannot be used in atomic context.
+ * @pwm: PWM device
+ * @wf: The waveform to apply
+ * @exact: If true no rounding is allowed
+ *
+ * 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 -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.
+ *
+ * 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)
+{
+ struct pwm_chip *chip = pwm->chip;
+ int err;
+
+ might_sleep();
+
+ guard(pwmchip)(chip);
+
+ if (!chip->operational)
+ return -ENODEV;
+
+ if (IS_ENABLED(CONFIG_PWM_DEBUG) && chip->atomic) {
+ /*
+ * Catch any drivers that have been marked as atomic but
+ * that will sleep anyway.
+ */
+ non_block_start();
+ err = __pwm_set_waveform(pwm, wf, exact);
+ non_block_end();
+ } else {
+ 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);
+
+static void pwm_apply_debug(struct pwm_device *pwm,
+ const struct pwm_state *state)
+{
+ struct pwm_state *last = &pwm->last;
+ struct pwm_chip *chip = pwm->chip;
+ struct pwm_state s1 = { 0 }, s2 = { 0 };
+ int err;
+
+ if (!IS_ENABLED(CONFIG_PWM_DEBUG))
+ return;
+
+ /* No reasonable diagnosis possible without .get_state() */
+ if (!chip->ops->get_state)
+ return;
+
+ /*
+ * If a disabled PWM was requested the result is unspecified, so nothing
+ * to check.
+ */
+ if (!state->enabled)
+ return;
+
+ /*
+ * *state was just applied. Read out the hardware state and do some
+ * checks.
+ */
+
+ err = chip->ops->get_state(chip, pwm, &s1);
+ trace_pwm_get(pwm, &s1, err);
+ if (err)
+ /* If that failed there isn't much to debug */
+ return;
+
+ /*
+ * If the PWM was disabled that's maybe strange but there is nothing
+ * that can be sensibly checked then. So return early.
+ */
+ if (!s1.enabled)
+ return;
+
+ /*
+ * The lowlevel driver either ignored .polarity (which is a bug) or as
+ * best effort inverted .polarity and fixed .duty_cycle respectively.
+ * Undo this inversion and fixup for further tests.
+ */
+ if (s1.polarity != state->polarity) {
+ s2.polarity = state->polarity;
+ s2.duty_cycle = s1.period - s1.duty_cycle;
+ s2.period = s1.period;
+ s2.enabled = true;
+ } else {
+ s2 = s1;
+ }
+
+ if (s2.polarity != state->polarity &&
+ s2.duty_cycle < s2.period)
+ dev_warn(pwmchip_parent(chip), ".apply ignored .polarity\n");
+
+ if (last->polarity == state->polarity &&
+ last->period > s2.period &&
+ last->period <= state->period)
+ dev_warn(pwmchip_parent(chip),
+ ".apply didn't pick the best available period (requested: %llu, applied: %llu, possible: %llu)\n",
+ state->period, s2.period, last->period);
+
+ /*
+ * Rounding period up is fine only if duty_cycle is 0 then, because a
+ * flat line doesn't have a characteristic period.
+ */
+ if (state->period < s2.period && s2.duty_cycle)
+ dev_warn(pwmchip_parent(chip),
+ ".apply is supposed to round down period (requested: %llu, applied: %llu)\n",
+ state->period, s2.period);
+
+ if (last->polarity == state->polarity &&
+ last->period == s2.period &&
+ last->duty_cycle > s2.duty_cycle &&
+ last->duty_cycle <= state->duty_cycle)
+ dev_warn(pwmchip_parent(chip),
+ ".apply didn't pick the best available duty cycle (requested: %llu/%llu, applied: %llu/%llu, possible: %llu/%llu)\n",
+ state->duty_cycle, state->period,
+ s2.duty_cycle, s2.period,
+ last->duty_cycle, last->period);
+
+ if (state->duty_cycle < s2.duty_cycle)
+ dev_warn(pwmchip_parent(chip),
+ ".apply is supposed to round down duty_cycle (requested: %llu/%llu, applied: %llu/%llu)\n",
+ state->duty_cycle, state->period,
+ s2.duty_cycle, s2.period);
+
+ /* reapply the state that the driver reported being configured. */
+ err = chip->ops->apply(chip, pwm, &s1);
+ trace_pwm_apply(pwm, &s1, err);
+ if (err) {
+ *last = s1;
+ dev_err(pwmchip_parent(chip), "failed to reapply current setting\n");
+ return;
+ }
+
+ *last = (struct pwm_state){ 0 };
+ err = chip->ops->get_state(chip, pwm, last);
+ trace_pwm_get(pwm, last, err);
+ if (err)
+ return;
+
+ /* reapplication of the current state should give an exact match */
+ if (s1.enabled != last->enabled ||
+ s1.polarity != last->polarity ||
+ (s1.enabled && s1.period != last->period) ||
+ (s1.enabled && s1.duty_cycle != last->duty_cycle)) {
+ dev_err(pwmchip_parent(chip),
+ ".apply is not idempotent (ena=%d pol=%d %llu/%llu) -> (ena=%d pol=%d %llu/%llu)\n",
+ s1.enabled, s1.polarity, s1.duty_cycle, s1.period,
+ last->enabled, last->polarity, last->duty_cycle,
+ last->period);
+ }
+}
+
+static bool pwm_state_valid(const struct pwm_state *state)
+{
+ /*
+ * For a disabled state all other state description is irrelevant and
+ * and supposed to be ignored. So also ignore any strange values and
+ * consider the state ok.
+ */
+ if (!state->enabled)
+ return true;
+
+ if (!state->period)
+ return false;
+
+ if (state->duty_cycle > state->period)
+ return false;
+
+ return true;
+}
+
+static int __pwm_apply(struct pwm_device *pwm, const struct pwm_state *state)
+{
+ struct pwm_chip *chip;
+ const struct pwm_ops *ops;
+ int err;
+
+ if (!pwm || !state)
+ return -EINVAL;
+
+ if (!pwm_state_valid(state)) {
+ /*
+ * Allow to transition from one invalid state to another.
+ * This ensures that you can e.g. change the polarity while
+ * the period is zero. (This happens on stm32 when the hardware
+ * is in its poweron default state.) This greatly simplifies
+ * working with the sysfs API where you can only change one
+ * parameter at a time.
+ */
+ if (!pwm_state_valid(&pwm->state)) {
+ pwm->state = *state;
+ return 0;
+ }
+
+ return -EINVAL;
+ }
+
+ chip = pwm->chip;
+ ops = chip->ops;
+
+ if (state->period == pwm->state.period &&
+ state->duty_cycle == pwm->state.duty_cycle &&
+ state->polarity == pwm->state.polarity &&
+ state->enabled == pwm->state.enabled &&
+ state->usage_power == pwm->state.usage_power)
+ return 0;
+
+ if (pwmchip_supports_waveform(chip)) {
+ struct pwm_waveform wf;
+ char wfhw[PWM_WFHWSIZE];
+
+ BUG_ON(PWM_WFHWSIZE < ops->sizeof_wfhw);
+
+ pwm_state2wf(state, &wf);
+
+ /*
+ * The rounding is wrong here for states with inverted polarity.
+ * While .apply() rounds down duty_cycle (which represents the
+ * time from the start of the period to the inner edge),
+ * .round_waveform_tohw() rounds down the time the PWM is high.
+ * Can be fixed if the need arises, until reported otherwise
+ * let's assume that consumers don't care.
+ */
+
+ err = __pwm_round_waveform_tohw(chip, pwm, &wf, &wfhw);
+ if (err) {
+ if (err > 0)
+ /*
+ * This signals an invalid request, typically
+ * the requested period (or duty_offset) is
+ * smaller than possible with the hardware.
+ */
+ return -EINVAL;
+
+ return err;
+ }
+
+ if (IS_ENABLED(CONFIG_PWM_DEBUG)) {
+ struct pwm_waveform wf_rounded;
+
+ err = __pwm_round_waveform_fromhw(chip, pwm, &wfhw, &wf_rounded);
+ if (err)
+ return err;
+
+ if (!pwm_check_rounding(&wf, &wf_rounded))
+ dev_err(&chip->dev, "Wrong rounding: requested %llu/%llu [+%llu], result %llu/%llu [+%llu]\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);
+ }
+
+ err = __pwm_write_waveform(chip, pwm, &wfhw);
+ if (err)
+ return err;
+
+ pwm->state = *state;
+
+ } else {
+ err = ops->apply(chip, pwm, state);
+ trace_pwm_apply(pwm, state, err);
+ if (err)
+ return err;
+
+ pwm->state = *state;
+
+ /*
+ * only do this after pwm->state was applied as some
+ * implementations of .get_state() depend on this
+ */
+ pwm_apply_debug(pwm, state);
+ }
+
+ return 0;
+}
+
+/**
+ * pwm_apply_might_sleep() - atomically apply a new state to a PWM device
+ * 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)
+{
+ int err;
+ struct pwm_chip *chip = pwm->chip;
+
+ /*
+ * Some lowlevel driver's implementations of .apply() make use of
+ * mutexes, also with some drivers only returning when the new
+ * configuration is active calling pwm_apply_might_sleep() from atomic context
+ * is a bad idea. So make it explicit that calling this function might
+ * sleep.
+ */
+ might_sleep();
+
+ guard(pwmchip)(chip);
+
+ if (!chip->operational)
+ return -ENODEV;
+
+ if (IS_ENABLED(CONFIG_PWM_DEBUG) && chip->atomic) {
+ /*
+ * Catch any drivers that have been marked as atomic but
+ * that will sleep anyway.
+ */
+ non_block_start();
+ err = __pwm_apply(pwm, state);
+ non_block_end();
+ } else {
+ err = __pwm_apply(pwm, state);
+ }
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(pwm_apply_might_sleep);
+
+/**
+ * pwm_apply_atomic() - apply a new state to a PWM device from atomic context
+ * 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)
+{
+ struct pwm_chip *chip = pwm->chip;
+
+ WARN_ONCE(!chip->atomic,
+ "sleeping PWM driver used in atomic context\n");
+
+ guard(pwmchip)(chip);
+
+ if (!chip->operational)
+ return -ENODEV;
+
+ return __pwm_apply(pwm, state);
+}
+EXPORT_SYMBOL_GPL(pwm_apply_atomic);
+
+/**
+ * pwm_get_state_hw() - get the current PWM state from hardware
+ * @pwm: PWM device
+ * @state: state to fill with the current PWM state
+ *
+ * Similar to pwm_get_state() but reads the current PWM state from hardware
+ * instead of the requested state.
+ *
+ * Returns: 0 on success or a negative error code on failure.
+ * Context: May sleep.
+ */
+int pwm_get_state_hw(struct pwm_device *pwm, struct pwm_state *state)
+{
+ struct pwm_chip *chip = pwm->chip;
+ const struct pwm_ops *ops = chip->ops;
+ int ret = -EOPNOTSUPP;
+
+ might_sleep();
+
+ guard(pwmchip)(chip);
+
+ if (!chip->operational)
+ return -ENODEV;
+
+ if (pwmchip_supports_waveform(chip) && ops->read_waveform) {
+ char wfhw[PWM_WFHWSIZE];
+ struct pwm_waveform wf;
+
+ BUG_ON(PWM_WFHWSIZE < ops->sizeof_wfhw);
+
+ ret = __pwm_read_waveform(chip, pwm, &wfhw);
+ if (ret)
+ return ret;
+
+ ret = __pwm_round_waveform_fromhw(chip, pwm, &wfhw, &wf);
+ if (ret)
+ return ret;
+
+ pwm_wf2state(&wf, state);
+
+ } else if (ops->get_state) {
+ ret = ops->get_state(chip, pwm, state);
+ trace_pwm_get(pwm, state, ret);
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(pwm_get_state_hw);
+
+/**
+ * pwm_adjust_config() - adjust the current PWM config to the PWM arguments
+ * @pwm: PWM device
+ *
+ * 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)
+{
+ struct pwm_state state;
+ struct pwm_args pargs;
+
+ pwm_get_args(pwm, &pargs);
+ pwm_get_state(pwm, &state);
+
+ /*
+ * If the current period is zero it means that either the PWM driver
+ * does not support initial state retrieval or the PWM has not yet
+ * been configured.
+ *
+ * In either case, we setup the new period and polarity, and assign a
+ * duty cycle of 0.
+ */
+ if (!state.period) {
+ state.duty_cycle = 0;
+ state.period = pargs.period;
+ state.polarity = pargs.polarity;
+
+ return pwm_apply_might_sleep(pwm, &state);
+ }
+
+ /*
+ * Adjust the PWM duty cycle/period based on the period value provided
+ * in PWM args.
+ */
+ if (pargs.period != state.period) {
+ u64 dutycycle = (u64)state.duty_cycle * pargs.period;
+
+ do_div(dutycycle, state.period);
+ state.duty_cycle = dutycycle;
+ state.period = pargs.period;
+ }
+
+ /*
+ * If the polarity changed, we should also change the duty cycle.
+ */
+ if (pargs.polarity != state.polarity) {
+ state.polarity = pargs.polarity;
+ state.duty_cycle = state.period - state.duty_cycle;
+ }
+
+ return pwm_apply_might_sleep(pwm, &state);
+}
+EXPORT_SYMBOL_GPL(pwm_adjust_config);
+
+/**
+ * pwm_capture() - capture and report a PWM signal
+ * @pwm: PWM device
+ * @result: structure to fill with capture result
+ * @timeout: time to wait, in milliseconds, before giving up on capture
+ *
+ * Returns: 0 on success or a negative error code on failure.
+ */
+static int pwm_capture(struct pwm_device *pwm, struct pwm_capture *result,
+ unsigned long timeout)
+{
+ struct pwm_chip *chip = pwm->chip;
+ const struct pwm_ops *ops = chip->ops;
+
+ if (!ops->capture)
+ return -ENOSYS;
+
+ /*
+ * Holding the pwm_lock is probably not needed. If you use pwm_capture()
+ * and you're interested to speed it up, please convince yourself it's
+ * really not needed, test and then suggest a patch on the mailing list.
+ */
+ guard(mutex)(&pwm_lock);
+
+ guard(pwmchip)(chip);
+
+ if (!chip->operational)
+ return -ENODEV;
+
+ return ops->capture(chip, pwm, result, timeout);
+}
+
+static struct pwm_chip *pwmchip_find_by_name(const char *name)
+{
+ struct pwm_chip *chip;
+ unsigned long id, tmp;
+
+ if (!name)
+ return NULL;
+
+ guard(mutex)(&pwm_lock);
+
+ idr_for_each_entry_ul(&pwm_chips, chip, tmp, id) {
+ if (device_match_name(pwmchip_parent(chip), name))
+ return chip;
+ }
+
+ return NULL;
+}
+
+static int pwm_device_request(struct pwm_device *pwm, const char *label)
+{
+ int err;
+ struct pwm_chip *chip = pwm->chip;
+ const struct pwm_ops *ops = chip->ops;
+
+ if (test_bit(PWMF_REQUESTED, &pwm->flags))
+ return -EBUSY;
+
+ /*
+ * This function is called while holding pwm_lock. As .operational only
+ * changes while holding this lock, checking it here without holding the
+ * chip lock is fine.
+ */
+ if (!chip->operational)
+ return -ENODEV;
+
+ if (!try_module_get(chip->owner))
+ return -ENODEV;
+
+ if (!get_device(&chip->dev)) {
+ err = -ENODEV;
+ goto err_get_device;
+ }
+
+ if (ops->request) {
+ err = ops->request(chip, pwm);
+ if (err) {
+ put_device(&chip->dev);
+err_get_device:
+ module_put(chip->owner);
+ return err;
+ }
+ }
+
+ if (ops->read_waveform || ops->get_state) {
+ /*
+ * Zero-initialize state because most drivers are unaware of
+ * .usage_power. The other members of state are supposed to be
+ * set by lowlevel drivers. We still initialize the whole
+ * structure for simplicity even though this might paper over
+ * faulty implementations of .get_state().
+ */
+ struct pwm_state state = { 0, };
+
+ err = pwm_get_state_hw(pwm, &state);
+ if (!err)
+ pwm->state = state;
+
+ if (IS_ENABLED(CONFIG_PWM_DEBUG))
+ pwm->last = pwm->state;
+ }
+
+ set_bit(PWMF_REQUESTED, &pwm->flags);
+ pwm->label = label;
+
+ return 0;
+}
+
+/**
+ * pwm_request_from_chip() - request a PWM device relative to a PWM chip
+ * @chip: PWM chip
+ * @index: per-chip index of the PWM to request
+ * @label: a literal description string of this PWM
+ *
+ * Returns: A pointer to the PWM device at the given index of the given PWM
+ * chip. A negative error code is returned if the index is not valid for the
+ * specified PWM chip or if the PWM device cannot be requested.
+ */
+static struct pwm_device *pwm_request_from_chip(struct pwm_chip *chip,
+ unsigned int index,
+ const char *label)
+{
+ struct pwm_device *pwm;
+ int err;
+
+ if (!chip || index >= chip->npwm)
+ return ERR_PTR(-EINVAL);
+
+ guard(mutex)(&pwm_lock);
+
+ pwm = &chip->pwms[index];
+
+ err = pwm_device_request(pwm, label);
+ if (err < 0)
+ return ERR_PTR(err);
+
+ return pwm;
+}
+
+struct pwm_device *
+of_pwm_xlate_with_flags(struct pwm_chip *chip, const struct of_phandle_args *args)
+{
+ struct pwm_device *pwm;
+
+ /* period in the second cell and flags in the third cell are optional */
+ if (args->args_count < 1)
+ return ERR_PTR(-EINVAL);
+
+ pwm = pwm_request_from_chip(chip, args->args[0], NULL);
+ if (IS_ERR(pwm))
+ return pwm;
+
+ if (args->args_count > 1)
+ pwm->args.period = args->args[1];
+
+ pwm->args.polarity = PWM_POLARITY_NORMAL;
+ if (args->args_count > 2 && args->args[2] & PWM_POLARITY_INVERTED)
+ pwm->args.polarity = PWM_POLARITY_INVERSED;
+
+ return pwm;
+}
+EXPORT_SYMBOL_GPL(of_pwm_xlate_with_flags);
+
+/*
+ * This callback is used for PXA PWM chips that only have a single PWM line.
+ * For such chips you could argue that passing the line number (i.e. the first
+ * parameter in the common case) is useless as it's always zero. So compared to
+ * the default xlate function of_pwm_xlate_with_flags() the first parameter is
+ * the default period and the second are flags.
+ *
+ * Note that if #pwm-cells = <3>, the semantic is the same as for
+ * of_pwm_xlate_with_flags() to allow converting the affected driver to
+ * #pwm-cells = <3> without breaking the legacy binding.
+ *
+ * Don't use for new drivers.
+ */
+struct pwm_device *
+of_pwm_single_xlate(struct pwm_chip *chip, const struct of_phandle_args *args)
+{
+ struct pwm_device *pwm;
+
+ if (args->args_count >= 3)
+ return of_pwm_xlate_with_flags(chip, args);
+
+ pwm = pwm_request_from_chip(chip, 0, NULL);
+ if (IS_ERR(pwm))
+ return pwm;
+
+ if (args->args_count > 0)
+ pwm->args.period = args->args[0];
+
+ pwm->args.polarity = PWM_POLARITY_NORMAL;
+ if (args->args_count > 1 && args->args[1] & PWM_POLARITY_INVERTED)
+ pwm->args.polarity = PWM_POLARITY_INVERSED;
+
+ return pwm;
+}
+EXPORT_SYMBOL_GPL(of_pwm_single_xlate);
+
+struct pwm_export {
+ struct device pwm_dev;
+ struct pwm_device *pwm;
+ struct mutex lock;
+ struct pwm_state suspend;
+};
+
+static inline struct pwm_chip *pwmchip_from_dev(struct device *pwmchip_dev)
+{
+ return container_of(pwmchip_dev, struct pwm_chip, dev);
+}
+
+static inline struct pwm_export *pwmexport_from_dev(struct device *pwm_dev)
+{
+ return container_of(pwm_dev, struct pwm_export, pwm_dev);
+}
+
+static inline struct pwm_device *pwm_from_dev(struct device *pwm_dev)
+{
+ struct pwm_export *export = pwmexport_from_dev(pwm_dev);
+
+ return export->pwm;
+}
+
+static ssize_t period_show(struct device *pwm_dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ const struct pwm_device *pwm = pwm_from_dev(pwm_dev);
+ struct pwm_state state;
+
+ pwm_get_state(pwm, &state);
+
+ return sysfs_emit(buf, "%llu\n", state.period);
+}
+
+static ssize_t period_store(struct device *pwm_dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct pwm_export *export = pwmexport_from_dev(pwm_dev);
+ struct pwm_device *pwm = export->pwm;
+ struct pwm_state state;
+ u64 val;
+ int ret;
+
+ ret = kstrtou64(buf, 0, &val);
+ if (ret)
+ return ret;
+
+ guard(mutex)(&export->lock);
+
+ pwm_get_state(pwm, &state);
+ state.period = val;
+ ret = pwm_apply_might_sleep(pwm, &state);
+
+ return ret ? : size;
+}
+
+static ssize_t duty_cycle_show(struct device *pwm_dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ const struct pwm_device *pwm = pwm_from_dev(pwm_dev);
+ struct pwm_state state;
+
+ pwm_get_state(pwm, &state);
+
+ return sysfs_emit(buf, "%llu\n", state.duty_cycle);
+}
+
+static ssize_t duty_cycle_store(struct device *pwm_dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct pwm_export *export = pwmexport_from_dev(pwm_dev);
+ struct pwm_device *pwm = export->pwm;
+ struct pwm_state state;
+ u64 val;
+ int ret;
+
+ ret = kstrtou64(buf, 0, &val);
+ if (ret)
+ return ret;
+
+ guard(mutex)(&export->lock);
+
+ pwm_get_state(pwm, &state);
+ state.duty_cycle = val;
+ ret = pwm_apply_might_sleep(pwm, &state);
+
+ return ret ? : size;
+}
+
+static ssize_t enable_show(struct device *pwm_dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ const struct pwm_device *pwm = pwm_from_dev(pwm_dev);
+ struct pwm_state state;
+
+ pwm_get_state(pwm, &state);
+
+ return sysfs_emit(buf, "%d\n", state.enabled);
+}
+
+static ssize_t enable_store(struct device *pwm_dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct pwm_export *export = pwmexport_from_dev(pwm_dev);
+ struct pwm_device *pwm = export->pwm;
+ struct pwm_state state;
+ int val, ret;
+
+ ret = kstrtoint(buf, 0, &val);
+ if (ret)
+ return ret;
+
+ guard(mutex)(&export->lock);
+
+ pwm_get_state(pwm, &state);
+
+ switch (val) {
+ case 0:
+ state.enabled = false;
+ break;
+ case 1:
+ state.enabled = true;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = pwm_apply_might_sleep(pwm, &state);
+
+ return ret ? : size;
+}
+
+static ssize_t polarity_show(struct device *pwm_dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ const struct pwm_device *pwm = pwm_from_dev(pwm_dev);
+ const char *polarity = "unknown";
+ struct pwm_state state;
+
+ pwm_get_state(pwm, &state);
+
+ switch (state.polarity) {
+ case PWM_POLARITY_NORMAL:
+ polarity = "normal";
+ break;
+
+ case PWM_POLARITY_INVERSED:
+ polarity = "inversed";
+ break;
+ }
+
+ return sysfs_emit(buf, "%s\n", polarity);
+}
+
+static ssize_t polarity_store(struct device *pwm_dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct pwm_export *export = pwmexport_from_dev(pwm_dev);
+ struct pwm_device *pwm = export->pwm;
+ enum pwm_polarity polarity;
+ struct pwm_state state;
+ int ret;
+
+ if (sysfs_streq(buf, "normal"))
+ polarity = PWM_POLARITY_NORMAL;
+ else if (sysfs_streq(buf, "inversed"))
+ polarity = PWM_POLARITY_INVERSED;
+ else
+ return -EINVAL;
+
+ guard(mutex)(&export->lock);
+
+ pwm_get_state(pwm, &state);
+ state.polarity = polarity;
+ ret = pwm_apply_might_sleep(pwm, &state);
+
+ return ret ? : size;
+}
+
+static ssize_t capture_show(struct device *pwm_dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct pwm_device *pwm = pwm_from_dev(pwm_dev);
+ struct pwm_capture result;
+ int ret;
+
+ ret = pwm_capture(pwm, &result, jiffies_to_msecs(HZ));
+ if (ret)
+ return ret;
+
+ return sysfs_emit(buf, "%u %u\n", result.period, result.duty_cycle);
+}
+
+static DEVICE_ATTR_RW(period);
+static DEVICE_ATTR_RW(duty_cycle);
+static DEVICE_ATTR_RW(enable);
+static DEVICE_ATTR_RW(polarity);
+static DEVICE_ATTR_RO(capture);
+
+static struct attribute *pwm_attrs[] = {
+ &dev_attr_period.attr,
+ &dev_attr_duty_cycle.attr,
+ &dev_attr_enable.attr,
+ &dev_attr_polarity.attr,
+ &dev_attr_capture.attr,
+ NULL
+};
+ATTRIBUTE_GROUPS(pwm);
+
+static void pwm_export_release(struct device *pwm_dev)
+{
+ struct pwm_export *export = pwmexport_from_dev(pwm_dev);
+
+ kfree(export);
+}
+
+static int pwm_export_child(struct device *pwmchip_dev, struct pwm_device *pwm)
+{
+ struct pwm_export *export;
+ char *pwm_prop[2];
+ int ret;
+
+ if (test_and_set_bit(PWMF_EXPORTED, &pwm->flags))
+ return -EBUSY;
+
+ export = kzalloc_obj(*export);
+ if (!export) {
+ clear_bit(PWMF_EXPORTED, &pwm->flags);
+ return -ENOMEM;
+ }
+
+ export->pwm = pwm;
+ mutex_init(&export->lock);
+
+ export->pwm_dev.release = pwm_export_release;
+ export->pwm_dev.parent = pwmchip_dev;
+ export->pwm_dev.devt = MKDEV(0, 0);
+ export->pwm_dev.groups = pwm_groups;
+ dev_set_name(&export->pwm_dev, "pwm%u", pwm->hwpwm);
+
+ ret = device_register(&export->pwm_dev);
+ if (ret) {
+ clear_bit(PWMF_EXPORTED, &pwm->flags);
+ put_device(&export->pwm_dev);
+ export = NULL;
+ return ret;
+ }
+ pwm_prop[0] = kasprintf(GFP_KERNEL, "EXPORT=pwm%u", pwm->hwpwm);
+ pwm_prop[1] = NULL;
+ kobject_uevent_env(&pwmchip_dev->kobj, KOBJ_CHANGE, pwm_prop);
+ kfree(pwm_prop[0]);
+
+ return 0;
+}
+
+static int pwm_unexport_match(struct device *pwm_dev, const void *data)
+{
+ return pwm_from_dev(pwm_dev) == data;
+}
+
+static int pwm_unexport_child(struct device *pwmchip_dev, struct pwm_device *pwm)
+{
+ struct device *pwm_dev;
+ char *pwm_prop[2];
+
+ if (!test_and_clear_bit(PWMF_EXPORTED, &pwm->flags))
+ return -ENODEV;
+
+ pwm_dev = device_find_child(pwmchip_dev, pwm, pwm_unexport_match);
+ if (!pwm_dev)
+ return -ENODEV;
+
+ pwm_prop[0] = kasprintf(GFP_KERNEL, "UNEXPORT=pwm%u", pwm->hwpwm);
+ pwm_prop[1] = NULL;
+ kobject_uevent_env(&pwmchip_dev->kobj, KOBJ_CHANGE, pwm_prop);
+ kfree(pwm_prop[0]);
+
+ /* for device_find_child() */
+ put_device(pwm_dev);
+ device_unregister(pwm_dev);
+ pwm_put(pwm);
+
+ return 0;
+}
+
+static ssize_t export_store(struct device *pwmchip_dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ struct pwm_chip *chip = pwmchip_from_dev(pwmchip_dev);
+ struct pwm_device *pwm;
+ unsigned int hwpwm;
+ int ret;
+
+ ret = kstrtouint(buf, 0, &hwpwm);
+ if (ret < 0)
+ return ret;
+
+ if (hwpwm >= chip->npwm)
+ return -ENODEV;
+
+ pwm = pwm_request_from_chip(chip, hwpwm, "sysfs");
+ if (IS_ERR(pwm))
+ return PTR_ERR(pwm);
+
+ ret = pwm_export_child(pwmchip_dev, pwm);
+ if (ret < 0)
+ pwm_put(pwm);
+
+ return ret ? : len;
+}
+static DEVICE_ATTR_WO(export);
+
+static ssize_t unexport_store(struct device *pwmchip_dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ struct pwm_chip *chip = pwmchip_from_dev(pwmchip_dev);
+ unsigned int hwpwm;
+ int ret;
+
+ ret = kstrtouint(buf, 0, &hwpwm);
+ if (ret < 0)
+ return ret;
+
+ if (hwpwm >= chip->npwm)
+ return -ENODEV;
+
+ ret = pwm_unexport_child(pwmchip_dev, &chip->pwms[hwpwm]);
+
+ return ret ? : len;
+}
+static DEVICE_ATTR_WO(unexport);
+
+static ssize_t npwm_show(struct device *pwmchip_dev, struct device_attribute *attr,
+ char *buf)
+{
+ const struct pwm_chip *chip = pwmchip_from_dev(pwmchip_dev);
+
+ return sysfs_emit(buf, "%u\n", chip->npwm);
+}
+static DEVICE_ATTR_RO(npwm);
+
+static struct attribute *pwm_chip_attrs[] = {
+ &dev_attr_export.attr,
+ &dev_attr_unexport.attr,
+ &dev_attr_npwm.attr,
+ NULL,
+};
+ATTRIBUTE_GROUPS(pwm_chip);
+
+/* takes export->lock on success */
+static struct pwm_export *pwm_class_get_state(struct device *pwmchip_dev,
+ struct pwm_device *pwm,
+ struct pwm_state *state)
+{
+ struct device *pwm_dev;
+ struct pwm_export *export;
+
+ if (!test_bit(PWMF_EXPORTED, &pwm->flags))
+ return NULL;
+
+ pwm_dev = device_find_child(pwmchip_dev, pwm, pwm_unexport_match);
+ if (!pwm_dev)
+ return NULL;
+
+ export = pwmexport_from_dev(pwm_dev);
+ put_device(pwm_dev); /* for device_find_child() */
+
+ mutex_lock(&export->lock);
+ pwm_get_state(pwm, state);
+
+ return export;
+}
+
+static int pwm_class_apply_state(struct pwm_export *export,
+ struct pwm_device *pwm,
+ struct pwm_state *state)
+{
+ int ret = pwm_apply_might_sleep(pwm, state);
+
+ /* release lock taken in pwm_class_get_state */
+ mutex_unlock(&export->lock);
+
+ return ret;
+}
+
+static int pwm_class_resume_npwm(struct device *pwmchip_dev, unsigned int npwm)
+{
+ struct pwm_chip *chip = pwmchip_from_dev(pwmchip_dev);
+ unsigned int i;
+ int ret = 0;
+
+ for (i = 0; i < npwm; i++) {
+ struct pwm_device *pwm = &chip->pwms[i];
+ struct pwm_state state;
+ struct pwm_export *export;
+
+ export = pwm_class_get_state(pwmchip_dev, pwm, &state);
+ if (!export)
+ continue;
+
+ /* If pwmchip was not enabled before suspend, do nothing. */
+ if (!export->suspend.enabled) {
+ /* release lock taken in pwm_class_get_state */
+ mutex_unlock(&export->lock);
+ continue;
+ }
+
+ state.enabled = export->suspend.enabled;
+ ret = pwm_class_apply_state(export, pwm, &state);
+ if (ret < 0)
+ break;
+ }
+
+ return ret;
+}
+
+static int pwm_class_suspend(struct device *pwmchip_dev)
+{
+ struct pwm_chip *chip = pwmchip_from_dev(pwmchip_dev);
+ unsigned int i;
+ int ret = 0;
+
+ for (i = 0; i < chip->npwm; i++) {
+ struct pwm_device *pwm = &chip->pwms[i];
+ struct pwm_state state;
+ struct pwm_export *export;
+
+ export = pwm_class_get_state(pwmchip_dev, pwm, &state);
+ if (!export)
+ continue;
+
+ /*
+ * If pwmchip was not enabled before suspend, save
+ * state for resume time and do nothing else.
+ */
+ export->suspend = state;
+ if (!state.enabled) {
+ /* release lock taken in pwm_class_get_state */
+ mutex_unlock(&export->lock);
+ continue;
+ }
+
+ state.enabled = false;
+ ret = pwm_class_apply_state(export, pwm, &state);
+ if (ret < 0) {
+ /*
+ * roll back the PWM devices that were disabled by
+ * this suspend function.
+ */
+ pwm_class_resume_npwm(pwmchip_dev, i);
+ break;
+ }
+ }
+
+ return ret;
+}
+
+static int pwm_class_resume(struct device *pwmchip_dev)
+{
+ struct pwm_chip *chip = pwmchip_from_dev(pwmchip_dev);
+
+ return pwm_class_resume_npwm(pwmchip_dev, chip->npwm);
+}
+
+static DEFINE_SIMPLE_DEV_PM_OPS(pwm_class_pm_ops, pwm_class_suspend, pwm_class_resume);
+
+static struct class pwm_class = {
+ .name = "pwm",
+ .dev_groups = pwm_chip_groups,
+ .pm = pm_sleep_ptr(&pwm_class_pm_ops),
+};
+
+static void pwmchip_sysfs_unexport(struct pwm_chip *chip)
+{
+ unsigned int i;
+
+ for (i = 0; i < chip->npwm; i++) {
+ struct pwm_device *pwm = &chip->pwms[i];
+
+ if (test_bit(PWMF_EXPORTED, &pwm->flags))
+ pwm_unexport_child(&chip->dev, pwm);
+ }
+}
+
+#define PWMCHIP_ALIGN ARCH_DMA_MINALIGN
+
+static void *pwmchip_priv(struct pwm_chip *chip)
+{
+ return (void *)chip + ALIGN(struct_size(chip, pwms, chip->npwm), PWMCHIP_ALIGN);
+}
+
+/* This is the counterpart to pwmchip_alloc() */
+void pwmchip_put(struct pwm_chip *chip)
+{
+ put_device(&chip->dev);
+}
+EXPORT_SYMBOL_GPL(pwmchip_put);
+
+void pwmchip_release(struct device *pwmchip_dev)
+{
+ struct pwm_chip *chip = pwmchip_from_dev(pwmchip_dev);
+
+ kfree(chip);
+}
+EXPORT_SYMBOL_GPL(pwmchip_release);
+
+struct pwm_chip *pwmchip_alloc(struct device *parent, unsigned int npwm, size_t sizeof_priv)
+{
+ struct pwm_chip *chip;
+ struct device *pwmchip_dev;
+ size_t alloc_size;
+ unsigned int i;
+
+ alloc_size = size_add(ALIGN(struct_size(chip, pwms, npwm), PWMCHIP_ALIGN),
+ sizeof_priv);
+
+ chip = kzalloc(alloc_size, GFP_KERNEL);
+ if (!chip)
+ return ERR_PTR(-ENOMEM);
+
+ chip->npwm = npwm;
+ chip->uses_pwmchip_alloc = true;
+ chip->operational = false;
+
+ pwmchip_dev = &chip->dev;
+ device_initialize(pwmchip_dev);
+ pwmchip_dev->class = &pwm_class;
+ pwmchip_dev->parent = parent;
+ pwmchip_dev->release = pwmchip_release;
+
+ pwmchip_set_drvdata(chip, pwmchip_priv(chip));
+
+ for (i = 0; i < chip->npwm; i++) {
+ struct pwm_device *pwm = &chip->pwms[i];
+ pwm->chip = chip;
+ pwm->hwpwm = i;
+ }
+
+ return chip;
+}
+EXPORT_SYMBOL_GPL(pwmchip_alloc);
+
+static void devm_pwmchip_put(void *data)
+{
+ struct pwm_chip *chip = data;
+
+ pwmchip_put(chip);
+}
+
+struct pwm_chip *devm_pwmchip_alloc(struct device *parent, unsigned int npwm, size_t sizeof_priv)
+{
+ struct pwm_chip *chip;
+ int ret;
+
+ chip = pwmchip_alloc(parent, npwm, sizeof_priv);
+ if (IS_ERR(chip))
+ return chip;
+
+ ret = devm_add_action_or_reset(parent, devm_pwmchip_put, chip);
+ if (ret)
+ return ERR_PTR(ret);
+
+ return chip;
+}
+EXPORT_SYMBOL_GPL(devm_pwmchip_alloc);
+
+static void of_pwmchip_add(struct pwm_chip *chip)
+{
+ if (!pwmchip_parent(chip) || !pwmchip_parent(chip)->of_node)
+ return;
+
+ if (!chip->of_xlate)
+ chip->of_xlate = of_pwm_xlate_with_flags;
+
+ of_node_get(pwmchip_parent(chip)->of_node);
+}
+
+static void of_pwmchip_remove(struct pwm_chip *chip)
+{
+ if (pwmchip_parent(chip))
+ of_node_put(pwmchip_parent(chip)->of_node);
+}
+
+static bool pwm_ops_check(const struct pwm_chip *chip)
+{
+ const struct pwm_ops *ops = chip->ops;
+
+ if (ops->write_waveform) {
+ if (!ops->round_waveform_tohw ||
+ !ops->round_waveform_fromhw)
+ return false;
+
+ if (PWM_WFHWSIZE < ops->sizeof_wfhw) {
+ dev_warn(pwmchip_parent(chip), "PWM_WFHWSIZE < %zu\n", ops->sizeof_wfhw);
+ return false;
+ }
+ } else {
+ if (!ops->apply)
+ return false;
+
+ if (IS_ENABLED(CONFIG_PWM_DEBUG) && !ops->get_state)
+ dev_warn(pwmchip_parent(chip),
+ "Please implement the .get_state() callback\n");
+ }
+
+ return true;
+}
+
+static struct device_link *pwm_device_link_add(struct device *dev,
+ struct pwm_device *pwm)
+{
+ struct device_link *dl;
+
+ if (!dev) {
+ /*
+ * No device for the PWM consumer has been provided. It may
+ * impact the PM sequence ordering: the PWM supplier may get
+ * suspended before the consumer.
+ */
+ dev_warn(pwmchip_parent(pwm->chip),
+ "No consumer device specified to create a link to\n");
+ return NULL;
+ }
+
+ dl = device_link_add(dev, pwmchip_parent(pwm->chip), DL_FLAG_AUTOREMOVE_CONSUMER);
+ if (!dl) {
+ dev_err(dev, "failed to create device link to %s\n",
+ dev_name(pwmchip_parent(pwm->chip)));
+ return ERR_PTR(-EINVAL);
+ }
+
+ return dl;
+}
+
+static struct pwm_chip *fwnode_to_pwmchip(struct fwnode_handle *fwnode)
+{
+ struct pwm_chip *chip;
+ unsigned long id, tmp;
+
+ guard(mutex)(&pwm_lock);
+
+ idr_for_each_entry_ul(&pwm_chips, chip, tmp, id)
+ if (pwmchip_parent(chip) && device_match_fwnode(pwmchip_parent(chip), fwnode))
+ return chip;
+
+ return ERR_PTR(-EPROBE_DEFER);
+}
+
+/**
+ * of_pwm_get() - request a PWM via the PWM framework
+ * @dev: device for PWM consumer
+ * @np: device node to get the PWM from
+ * @con_id: consumer name
+ *
+ * Returns the PWM device parsed from the phandle and index specified in the
+ * "pwms" property of a device tree node or a negative error-code on failure.
+ * Values parsed from the device tree are stored in the returned PWM device
+ * object.
+ *
+ * If con_id is NULL, the first PWM device listed in the "pwms" property will
+ * be requested. Otherwise the "pwm-names" property is used to do a reverse
+ * lookup of the PWM index. This also means that the "pwm-names" property
+ * becomes mandatory for devices that look up the PWM device via the con_id
+ * parameter.
+ *
+ * Returns: A pointer to the requested PWM device or an ERR_PTR()-encoded
+ * error code on failure.
+ */
+static struct pwm_device *of_pwm_get(struct device *dev, struct device_node *np,
+ const char *con_id)
+{
+ struct pwm_device *pwm = NULL;
+ struct of_phandle_args args;
+ struct device_link *dl;
+ struct pwm_chip *chip;
+ int index = 0;
+ int err;
+
+ if (con_id) {
+ index = of_property_match_string(np, "pwm-names", con_id);
+ if (index < 0)
+ return ERR_PTR(index);
+ }
+
+ err = of_parse_phandle_with_args_map(np, "pwms", "pwm", index, &args);
+ if (err) {
+ pr_err("%s(): can't parse \"pwms\" property\n", __func__);
+ return ERR_PTR(err);
+ }
+
+ chip = fwnode_to_pwmchip(of_fwnode_handle(args.np));
+ if (IS_ERR(chip)) {
+ if (PTR_ERR(chip) != -EPROBE_DEFER)
+ pr_err("%s(): PWM chip not found\n", __func__);
+
+ pwm = ERR_CAST(chip);
+ goto put;
+ }
+
+ pwm = chip->of_xlate(chip, &args);
+ if (IS_ERR(pwm))
+ goto put;
+
+ dl = pwm_device_link_add(dev, pwm);
+ if (IS_ERR(dl)) {
+ /* of_xlate ended up calling pwm_request_from_chip() */
+ pwm_put(pwm);
+ pwm = ERR_CAST(dl);
+ goto put;
+ }
+
+ /*
+ * If a consumer name was not given, try to look it up from the
+ * "pwm-names" property if it exists. Otherwise use the name of
+ * the user device node.
+ */
+ if (!con_id) {
+ err = of_property_read_string_index(np, "pwm-names", index,
+ &con_id);
+ if (err < 0)
+ con_id = np->name;
+ }
+
+ pwm->label = con_id;
+
+put:
+ of_node_put(args.np);
+
+ return pwm;
+}
+
+/**
+ * acpi_pwm_get() - request a PWM via parsing "pwms" property in ACPI
+ * @fwnode: firmware node to get the "pwms" property from
+ *
+ * Returns the PWM device parsed from the fwnode and index specified in the
+ * "pwms" property or a negative error-code on failure.
+ * Values parsed from the device tree are stored in the returned PWM device
+ * object.
+ *
+ * This is analogous to of_pwm_get() except con_id is not yet supported.
+ * ACPI entries must look like
+ * Package () {"pwms", Package ()
+ * { <PWM device reference>, <PWM index>, <PWM period> [, <PWM flags>]}}
+ *
+ * Returns: A pointer to the requested PWM device or an ERR_PTR()-encoded
+ * error code on failure.
+ */
+static struct pwm_device *acpi_pwm_get(const struct fwnode_handle *fwnode)
+{
+ struct pwm_device *pwm;
+ struct fwnode_reference_args args;
+ struct pwm_chip *chip;
+ int ret;
+
+ memset(&args, 0, sizeof(args));
+
+ ret = __acpi_node_get_property_reference(fwnode, "pwms", 0, 3, &args);
+ if (ret < 0)
+ return ERR_PTR(ret);
+
+ if (args.nargs < 2)
+ return ERR_PTR(-EPROTO);
+
+ chip = fwnode_to_pwmchip(args.fwnode);
+ if (IS_ERR(chip))
+ return ERR_CAST(chip);
+
+ pwm = pwm_request_from_chip(chip, args.args[0], NULL);
+ if (IS_ERR(pwm))
+ return pwm;
+
+ pwm->args.period = args.args[1];
+ pwm->args.polarity = PWM_POLARITY_NORMAL;
+
+ if (args.nargs > 2 && args.args[2] & PWM_POLARITY_INVERTED)
+ pwm->args.polarity = PWM_POLARITY_INVERSED;
+
+ return pwm;
+}
+
+static DEFINE_MUTEX(pwm_lookup_lock);
+static LIST_HEAD(pwm_lookup_list);
+
+/**
+ * pwm_get() - look up and request a PWM device
+ * @dev: device for PWM consumer
+ * @con_id: consumer name
+ *
+ * Lookup is first attempted using DT. If the device was not instantiated from
+ * a device tree, a PWM chip and a relative index is looked up via a table
+ * supplied by board setup code (see pwm_add_table()).
+ *
+ * Once a PWM chip has been found the specified PWM device will be requested
+ * and is ready to be used.
+ *
+ * Returns: A pointer to the requested PWM device or an ERR_PTR()-encoded
+ * error code on failure.
+ */
+struct pwm_device *pwm_get(struct device *dev, const char *con_id)
+{
+ const struct fwnode_handle *fwnode = dev ? dev_fwnode(dev) : NULL;
+ const char *dev_id = dev ? dev_name(dev) : NULL;
+ struct pwm_device *pwm;
+ struct pwm_chip *chip;
+ struct device_link *dl;
+ unsigned int best = 0;
+ struct pwm_lookup *p, *chosen = NULL;
+ unsigned int match;
+ int err;
+
+ /* look up via DT first */
+ if (is_of_node(fwnode))
+ return of_pwm_get(dev, to_of_node(fwnode), con_id);
+
+ /* then lookup via ACPI */
+ if (is_acpi_node(fwnode)) {
+ pwm = acpi_pwm_get(fwnode);
+ if (!IS_ERR(pwm) || PTR_ERR(pwm) != -ENOENT)
+ return pwm;
+ }
+
+ /*
+ * We look up the provider in the static table typically provided by
+ * board setup code. We first try to lookup the consumer device by
+ * name. If the consumer device was passed in as NULL or if no match
+ * was found, we try to find the consumer by directly looking it up
+ * by name.
+ *
+ * If a match is found, the provider PWM chip is looked up by name
+ * and a PWM device is requested using the PWM device per-chip index.
+ *
+ * The lookup algorithm was shamelessly taken from the clock
+ * framework:
+ *
+ * We do slightly fuzzy matching here:
+ * An entry with a NULL ID is assumed to be a wildcard.
+ * If an entry has a device ID, it must match
+ * If an entry has a connection ID, it must match
+ * Then we take the most specific entry - with the following order
+ * of precedence: dev+con > dev only > con only.
+ */
+ scoped_guard(mutex, &pwm_lookup_lock)
+ list_for_each_entry(p, &pwm_lookup_list, list) {
+ match = 0;
+
+ if (p->dev_id) {
+ if (!dev_id || strcmp(p->dev_id, dev_id))
+ continue;
+
+ match += 2;
+ }
+
+ if (p->con_id) {
+ if (!con_id || strcmp(p->con_id, con_id))
+ continue;
+
+ match += 1;
+ }
+
+ if (match > best) {
+ chosen = p;
+
+ if (match != 3)
+ best = match;
+ else
+ break;
+ }
+ }
+
+ if (!chosen)
+ return ERR_PTR(-ENODEV);
+
+ chip = pwmchip_find_by_name(chosen->provider);
+
+ /*
+ * If the lookup entry specifies a module, load the module and retry
+ * the PWM chip lookup. This can be used to work around driver load
+ * ordering issues if driver's can't be made to properly support the
+ * deferred probe mechanism.
+ */
+ if (!chip && chosen->module) {
+ err = request_module(chosen->module);
+ if (err == 0)
+ chip = pwmchip_find_by_name(chosen->provider);
+ }
+
+ if (!chip)
+ return ERR_PTR(-EPROBE_DEFER);
+
+ pwm = pwm_request_from_chip(chip, chosen->index, con_id ?: dev_id);
+ if (IS_ERR(pwm))
+ return pwm;
+
+ dl = pwm_device_link_add(dev, pwm);
+ if (IS_ERR(dl)) {
+ pwm_put(pwm);
+ return ERR_CAST(dl);
+ }
+
+ pwm->args.period = chosen->period;
+ pwm->args.polarity = chosen->polarity;
+
+ return pwm;
+}
+EXPORT_SYMBOL_GPL(pwm_get);
+
+static void __pwm_put(struct pwm_device *pwm)
+{
+ struct pwm_chip *chip = pwm->chip;
+
+ /*
+ * Trigger a warning if a consumer called pwm_put() twice.
+ * If the chip isn't operational, PWMF_REQUESTED was already cleared in
+ * pwmchip_remove(). So don't warn in this case.
+ */
+ if (chip->operational && !test_and_clear_bit(PWMF_REQUESTED, &pwm->flags)) {
+ pr_warn("PWM device already freed\n");
+ return;
+ }
+
+ if (chip->operational && chip->ops->free)
+ pwm->chip->ops->free(pwm->chip, pwm);
+
+ pwm->label = NULL;
+
+ put_device(&chip->dev);
+
+ module_put(chip->owner);
+}
+
+/**
+ * pwm_put() - release a PWM device
+ * @pwm: PWM device
+ */
+void pwm_put(struct pwm_device *pwm)
+{
+ if (!pwm)
+ return;
+
+ guard(mutex)(&pwm_lock);
+
+ __pwm_put(pwm);
+}
+EXPORT_SYMBOL_GPL(pwm_put);
+
+static void devm_pwm_release(void *pwm)
+{
+ pwm_put(pwm);
+}
+
+/**
+ * devm_pwm_get() - resource managed pwm_get()
+ * @dev: device for PWM consumer
+ * @con_id: consumer name
+ *
+ * This function performs like pwm_get() but the acquired PWM device will
+ * automatically be released on driver detach.
+ *
+ * Returns: A pointer to the requested PWM device or an ERR_PTR()-encoded
+ * error code on failure.
+ */
+struct pwm_device *devm_pwm_get(struct device *dev, const char *con_id)
+{
+ struct pwm_device *pwm;
+ int ret;
+
+ pwm = pwm_get(dev, con_id);
+ if (IS_ERR(pwm))
+ return pwm;
+
+ ret = devm_add_action_or_reset(dev, devm_pwm_release, pwm);
+ if (ret)
+ return ERR_PTR(ret);
+
+ return pwm;
+}
+EXPORT_SYMBOL_GPL(devm_pwm_get);
+
+/**
+ * devm_fwnode_pwm_get() - request a resource managed PWM from firmware node
+ * @dev: device for PWM consumer
+ * @fwnode: firmware node to get the PWM from
+ * @con_id: consumer name
+ *
+ * Returns the PWM device parsed from the firmware node. See of_pwm_get() and
+ * acpi_pwm_get() for a detailed description.
+ *
+ * Returns: A pointer to the requested PWM device or an ERR_PTR()-encoded
+ * error code on failure.
+ */
+struct pwm_device *devm_fwnode_pwm_get(struct device *dev,
+ struct fwnode_handle *fwnode,
+ const char *con_id)
+{
+ struct pwm_device *pwm = ERR_PTR(-ENODEV);
+ int ret;
+
+ if (is_of_node(fwnode))
+ pwm = of_pwm_get(dev, to_of_node(fwnode), con_id);
+ else if (is_acpi_node(fwnode))
+ pwm = acpi_pwm_get(fwnode);
+ if (IS_ERR(pwm))
+ return pwm;
+
+ ret = devm_add_action_or_reset(dev, devm_pwm_release, pwm);
+ if (ret)
+ return ERR_PTR(ret);
+
+ return pwm;
+}
+EXPORT_SYMBOL_GPL(devm_fwnode_pwm_get);
+
+struct pwm_cdev_data {
+ struct pwm_chip *chip;
+ struct pwm_device *pwm[];
+};
+
+static int pwm_cdev_open(struct inode *inode, struct file *file)
+{
+ struct pwm_chip *chip = container_of(inode->i_cdev, struct pwm_chip, cdev);
+ struct pwm_cdev_data *cdata;
+
+ guard(mutex)(&pwm_lock);
+
+ if (!chip->operational)
+ return -ENXIO;
+
+ cdata = kzalloc_flex(*cdata, pwm, chip->npwm);
+ if (!cdata)
+ return -ENOMEM;
+
+ cdata->chip = chip;
+
+ file->private_data = cdata;
+
+ return nonseekable_open(inode, file);
+}
+
+static int pwm_cdev_release(struct inode *inode, struct file *file)
+{
+ struct pwm_cdev_data *cdata = file->private_data;
+ unsigned int i;
+
+ for (i = 0; i < cdata->chip->npwm; ++i) {
+ struct pwm_device *pwm = cdata->pwm[i];
+
+ if (pwm) {
+ const char *label = pwm->label;
+
+ pwm_put(cdata->pwm[i]);
+ kfree(label);
+ }
+ }
+ kfree(cdata);
+
+ return 0;
+}
+
+static int pwm_cdev_request(struct pwm_cdev_data *cdata, unsigned int hwpwm)
+{
+ struct pwm_chip *chip = cdata->chip;
+
+ if (hwpwm >= chip->npwm)
+ return -EINVAL;
+
+ if (!cdata->pwm[hwpwm]) {
+ struct pwm_device *pwm = &chip->pwms[hwpwm];
+ const char *label;
+ int ret;
+
+ label = kasprintf(GFP_KERNEL, "pwm-cdev (pid=%d)", current->pid);
+ if (!label)
+ return -ENOMEM;
+
+ ret = pwm_device_request(pwm, label);
+ if (ret < 0) {
+ kfree(label);
+ return ret;
+ }
+
+ cdata->pwm[hwpwm] = pwm;
+ }
+
+ return 0;
+}
+
+static int pwm_cdev_free(struct pwm_cdev_data *cdata, unsigned int hwpwm)
+{
+ struct pwm_chip *chip = cdata->chip;
+
+ if (hwpwm >= chip->npwm)
+ return -EINVAL;
+
+ if (cdata->pwm[hwpwm]) {
+ struct pwm_device *pwm = cdata->pwm[hwpwm];
+ const char *label = pwm->label;
+
+ __pwm_put(pwm);
+
+ kfree(label);
+
+ cdata->pwm[hwpwm] = NULL;
+ }
+
+ return 0;
+}
+
+static struct pwm_device *pwm_cdev_get_requested_pwm(struct pwm_cdev_data *cdata,
+ u32 hwpwm)
+{
+ struct pwm_chip *chip = cdata->chip;
+
+ if (hwpwm >= chip->npwm)
+ return ERR_PTR(-EINVAL);
+
+ if (cdata->pwm[hwpwm])
+ return cdata->pwm[hwpwm];
+
+ return ERR_PTR(-EINVAL);
+}
+
+static long pwm_cdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ int ret = 0;
+ struct pwm_cdev_data *cdata = file->private_data;
+ struct pwm_chip *chip = cdata->chip;
+
+ guard(mutex)(&pwm_lock);
+
+ if (!chip->operational)
+ return -ENODEV;
+
+ switch (cmd) {
+ case PWM_IOCTL_REQUEST:
+ {
+ unsigned int hwpwm = arg;
+
+ return pwm_cdev_request(cdata, hwpwm);
+ }
+
+ case PWM_IOCTL_FREE:
+ {
+ unsigned int hwpwm = arg;
+
+ return pwm_cdev_free(cdata, hwpwm);
+ }
+
+ case PWM_IOCTL_ROUNDWF:
+ {
+ struct pwmchip_waveform cwf;
+ struct pwm_waveform wf;
+ struct pwm_device *pwm;
+
+ ret = copy_from_user(&cwf,
+ (struct pwmchip_waveform __user *)arg,
+ sizeof(cwf));
+ if (ret)
+ return -EFAULT;
+
+ if (cwf.__pad != 0)
+ return -EINVAL;
+
+ pwm = pwm_cdev_get_requested_pwm(cdata, cwf.hwpwm);
+ if (IS_ERR(pwm))
+ return PTR_ERR(pwm);
+
+ wf = (struct pwm_waveform) {
+ .period_length_ns = cwf.period_length_ns,
+ .duty_length_ns = cwf.duty_length_ns,
+ .duty_offset_ns = cwf.duty_offset_ns,
+ };
+
+ ret = pwm_round_waveform_might_sleep(pwm, &wf);
+ if (ret < 0)
+ return ret;
+
+ cwf = (struct pwmchip_waveform) {
+ .hwpwm = cwf.hwpwm,
+ .period_length_ns = wf.period_length_ns,
+ .duty_length_ns = wf.duty_length_ns,
+ .duty_offset_ns = wf.duty_offset_ns,
+ };
+
+ ret = copy_to_user((struct pwmchip_waveform __user *)arg,
+ &cwf, sizeof(cwf));
+ return ret ? -EFAULT : 0;
+ }
+
+ case PWM_IOCTL_GETWF:
+ {
+ struct pwmchip_waveform cwf;
+ struct pwm_waveform wf;
+ struct pwm_device *pwm;
+
+ ret = copy_from_user(&cwf,
+ (struct pwmchip_waveform __user *)arg,
+ sizeof(cwf));
+ if (ret)
+ return -EFAULT;
+
+ if (cwf.__pad != 0)
+ return -EINVAL;
+
+ pwm = pwm_cdev_get_requested_pwm(cdata, cwf.hwpwm);
+ if (IS_ERR(pwm))
+ return PTR_ERR(pwm);
+
+ ret = pwm_get_waveform_might_sleep(pwm, &wf);
+ if (ret)
+ return ret;
+
+ cwf = (struct pwmchip_waveform) {
+ .hwpwm = cwf.hwpwm,
+ .period_length_ns = wf.period_length_ns,
+ .duty_length_ns = wf.duty_length_ns,
+ .duty_offset_ns = wf.duty_offset_ns,
+ };
+
+ ret = copy_to_user((struct pwmchip_waveform __user *)arg,
+ &cwf, sizeof(cwf));
+ return ret ? -EFAULT : 0;
+ }
+
+ case PWM_IOCTL_SETROUNDEDWF:
+ case PWM_IOCTL_SETEXACTWF:
+ {
+ struct pwmchip_waveform cwf;
+ struct pwm_waveform wf;
+ struct pwm_device *pwm;
+
+ ret = copy_from_user(&cwf,
+ (struct pwmchip_waveform __user *)arg,
+ sizeof(cwf));
+ if (ret)
+ return -EFAULT;
+
+ if (cwf.__pad != 0)
+ return -EINVAL;
+
+ wf = (struct pwm_waveform){
+ .period_length_ns = cwf.period_length_ns,
+ .duty_length_ns = cwf.duty_length_ns,
+ .duty_offset_ns = cwf.duty_offset_ns,
+ };
+
+ if (!pwm_wf_valid(&wf))
+ return -EINVAL;
+
+ pwm = pwm_cdev_get_requested_pwm(cdata, cwf.hwpwm);
+ if (IS_ERR(pwm))
+ return PTR_ERR(pwm);
+
+ ret = pwm_set_waveform_might_sleep(pwm, &wf,
+ cmd == PWM_IOCTL_SETEXACTWF);
+
+ /*
+ * If userspace cares about rounding deviations it has
+ * to check the values anyhow, so simplify handling for
+ * them and don't signal uprounding. This matches the
+ * behaviour of PWM_IOCTL_ROUNDWF which also returns 0
+ * in that case.
+ */
+ if (ret == 1)
+ ret = 0;
+
+ return ret;
+ }
+
+ default:
+ return -ENOTTY;
+ }
+}
+
+static const struct file_operations pwm_cdev_fileops = {
+ .open = pwm_cdev_open,
+ .release = pwm_cdev_release,
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = pwm_cdev_ioctl,
+};
+
+static dev_t pwm_devt;
+
+static int pwm_gpio_request(struct gpio_chip *gc, unsigned int offset)
+{
+ struct pwm_chip *chip = gpiochip_get_data(gc);
+ struct pwm_device *pwm;
+
+ pwm = pwm_request_from_chip(chip, offset, "pwm-gpio");
+ if (IS_ERR(pwm))
+ return PTR_ERR(pwm);
+
+ return 0;
+}
+
+static void pwm_gpio_free(struct gpio_chip *gc, unsigned int offset)
+{
+ struct pwm_chip *chip = gpiochip_get_data(gc);
+
+ pwm_put(&chip->pwms[offset]);
+}
+
+static int pwm_gpio_get_direction(struct gpio_chip *gc, unsigned int offset)
+{
+ return GPIO_LINE_DIRECTION_OUT;
+}
+
+static int pwm_gpio_set(struct gpio_chip *gc, unsigned int offset, int value)
+{
+ struct pwm_chip *chip = gpiochip_get_data(gc);
+ struct pwm_device *pwm = &chip->pwms[offset];
+ int ret;
+ struct pwm_waveform wf = {
+ .period_length_ns = 1,
+ };
+
+ ret = pwm_round_waveform_might_sleep(pwm, &wf);
+ if (ret < 0)
+ return ret;
+
+ if (value)
+ wf.duty_length_ns = wf.period_length_ns;
+ else
+ wf.duty_length_ns = 0;
+
+ return pwm_set_waveform_might_sleep(pwm, &wf, true);
+}
+
+/**
+ * __pwmchip_add() - register a new PWM chip
+ * @chip: the PWM chip to add
+ * @owner: reference to the module providing the chip.
+ *
+ * Register a new PWM chip. @owner is supposed to be THIS_MODULE, use the
+ * pwmchip_add wrapper to do this right.
+ *
+ * Returns: 0 on success or a negative error code on failure.
+ */
+int __pwmchip_add(struct pwm_chip *chip, struct module *owner)
+{
+ int ret;
+
+ if (!chip || !pwmchip_parent(chip) || !chip->ops || !chip->npwm)
+ return -EINVAL;
+
+ /*
+ * a struct pwm_chip must be allocated using (devm_)pwmchip_alloc,
+ * otherwise the embedded struct device might disappear too early
+ * resulting in memory corruption.
+ * Catch drivers that were not converted appropriately.
+ */
+ if (!chip->uses_pwmchip_alloc)
+ return -EINVAL;
+
+ if (!pwm_ops_check(chip))
+ return -EINVAL;
+
+ chip->owner = owner;
+
+ if (chip->atomic)
+ spin_lock_init(&chip->atomic_lock);
+ else
+ mutex_init(&chip->nonatomic_lock);
+
+ guard(mutex)(&pwm_lock);
+
+ ret = idr_alloc(&pwm_chips, chip, 0, 0, GFP_KERNEL);
+ if (ret < 0)
+ return ret;
+
+ chip->id = ret;
+
+ dev_set_name(&chip->dev, "pwmchip%u", chip->id);
+
+ if (IS_ENABLED(CONFIG_OF))
+ of_pwmchip_add(chip);
+
+ scoped_guard(pwmchip, chip)
+ chip->operational = true;
+
+ if (chip->ops->write_waveform) {
+ if (chip->id < PWM_MINOR_COUNT)
+ chip->dev.devt = MKDEV(MAJOR(pwm_devt), chip->id);
+ else
+ dev_warn(&chip->dev, "chip id too high to create a chardev\n");
+ }
+
+ cdev_init(&chip->cdev, &pwm_cdev_fileops);
+ chip->cdev.owner = owner;
+
+ ret = cdev_device_add(&chip->cdev, &chip->dev);
+ if (ret)
+ goto err_device_add;
+
+ if (IS_ENABLED(CONFIG_PWM_PROVIDE_GPIO) && chip->ops->write_waveform) {
+ struct device *parent = pwmchip_parent(chip);
+
+ chip->gpio = (typeof(chip->gpio)){
+ .label = dev_name(parent),
+ .parent = parent,
+ .request = pwm_gpio_request,
+ .free = pwm_gpio_free,
+ .get_direction = pwm_gpio_get_direction,
+ .set = pwm_gpio_set,
+ .base = -1,
+ .ngpio = chip->npwm,
+ .can_sleep = true,
+ };
+
+ ret = gpiochip_add_data(&chip->gpio, chip);
+ if (ret)
+ goto err_gpiochip_add;
+ }
+
+ return 0;
+
+err_gpiochip_add:
+
+ cdev_device_del(&chip->cdev, &chip->dev);
+err_device_add:
+
+ scoped_guard(pwmchip, chip)
+ chip->operational = false;
+
+ if (IS_ENABLED(CONFIG_OF))
+ of_pwmchip_remove(chip);
+
+ idr_remove(&pwm_chips, chip->id);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(__pwmchip_add);
+
+/**
+ * pwmchip_remove() - remove a PWM chip
+ * @chip: the PWM chip to remove
+ *
+ * Removes a PWM chip.
+ */
+void pwmchip_remove(struct pwm_chip *chip)
+{
+ if (IS_ENABLED(CONFIG_PWM_PROVIDE_GPIO) && chip->ops->write_waveform)
+ gpiochip_remove(&chip->gpio);
+
+ pwmchip_sysfs_unexport(chip);
+
+ scoped_guard(mutex, &pwm_lock) {
+ unsigned int i;
+
+ scoped_guard(pwmchip, chip)
+ chip->operational = false;
+
+ for (i = 0; i < chip->npwm; ++i) {
+ struct pwm_device *pwm = &chip->pwms[i];
+
+ if (test_and_clear_bit(PWMF_REQUESTED, &pwm->flags)) {
+ dev_warn(&chip->dev, "Freeing requested PWM #%u\n", i);
+ if (pwm->chip->ops->free)
+ pwm->chip->ops->free(pwm->chip, pwm);
+ }
+ }
+
+ if (IS_ENABLED(CONFIG_OF))
+ of_pwmchip_remove(chip);
+
+ idr_remove(&pwm_chips, chip->id);
+ }
+
+ cdev_device_del(&chip->cdev, &chip->dev);
+}
+EXPORT_SYMBOL_GPL(pwmchip_remove);
+
+static void devm_pwmchip_remove(void *data)
+{
+ struct pwm_chip *chip = data;
+
+ pwmchip_remove(chip);
+}
+
+int __devm_pwmchip_add(struct device *dev, struct pwm_chip *chip, struct module *owner)
+{
+ int ret;
+
+ ret = __pwmchip_add(chip, owner);
+ if (ret)
+ return ret;
+
+ return devm_add_action_or_reset(dev, devm_pwmchip_remove, chip);
+}
+EXPORT_SYMBOL_GPL(__devm_pwmchip_add);
+
+/**
+ * pwm_add_table() - register PWM device consumers
+ * @table: array of consumers to register
+ * @num: number of consumers in table
+ */
+void pwm_add_table(struct pwm_lookup *table, size_t num)
+{
+ guard(mutex)(&pwm_lookup_lock);
+
+ while (num--) {
+ list_add_tail(&table->list, &pwm_lookup_list);
+ table++;
+ }
+}
+
+/**
+ * pwm_remove_table() - unregister PWM device consumers
+ * @table: array of consumers to unregister
+ * @num: number of consumers in table
+ */
+void pwm_remove_table(struct pwm_lookup *table, size_t num)
+{
+ guard(mutex)(&pwm_lookup_lock);
+
+ while (num--) {
+ list_del(&table->list);
+ table++;
+ }
+}
+
+static void pwm_dbg_show(struct pwm_chip *chip, struct seq_file *s)
+{
+ unsigned int i;
+
+ for (i = 0; i < chip->npwm; i++) {
+ struct pwm_device *pwm = &chip->pwms[i];
+ struct pwm_state state;
+ int err;
+
+ pwm_get_state(pwm, &state);
+
+ seq_printf(s, " pwm-%-3d (%-20.20s):", i, pwm->label);
+
+ if (test_bit(PWMF_REQUESTED, &pwm->flags))
+ seq_puts(s, " requested");
+
+ seq_putc(s, '\n');
+
+ 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_putc(s, '\n');
+
+ if (pwmchip_supports_waveform(chip)) {
+ struct pwm_waveform wf;
+
+ err = pwm_get_waveform_might_sleep(pwm, &wf);
+ if (!err)
+ seq_printf(s, " actual configuration: %lld/%lld [+%lld]",
+ wf.duty_length_ns, wf.period_length_ns, wf.duty_offset_ns);
+ else
+ seq_printf(s, " actual configuration: read out error: %pe\n", ERR_PTR(err));
+ } else {
+ struct pwm_state hwstate;
+
+ err = pwm_get_state_hw(pwm, &hwstate);
+ if (!err)
+ seq_printf(s, " actual configuration: %3sabled, %llu/%llu ns, %s polarity",
+ hwstate.enabled ? "en" : "dis", hwstate.duty_cycle, hwstate.period,
+ hwstate.polarity ? "inverse" : "normal");
+ else
+ seq_printf(s, " actual configuration: read out error: %pe", ERR_PTR(err));
+ }
+
+ seq_putc(s, '\n');
+ }
+}
+
+static void *pwm_seq_start(struct seq_file *s, loff_t *pos)
+{
+ unsigned long id = *pos;
+ void *ret;
+
+ mutex_lock(&pwm_lock);
+ s->private = "";
+
+ ret = idr_get_next_ul(&pwm_chips, &id);
+ *pos = id;
+ return ret;
+}
+
+static void *pwm_seq_next(struct seq_file *s, void *v, loff_t *pos)
+{
+ unsigned long id = *pos + 1;
+ void *ret;
+
+ s->private = "\n";
+
+ ret = idr_get_next_ul(&pwm_chips, &id);
+ *pos = id;
+ return ret;
+}
+
+static void pwm_seq_stop(struct seq_file *s, void *v)
+{
+ mutex_unlock(&pwm_lock);
+}
+
+static int pwm_seq_show(struct seq_file *s, void *v)
+{
+ struct pwm_chip *chip = v;
+
+ seq_printf(s, "%s%u: %s/%s, npwm: %u\n",
+ (char *)s->private, chip->id,
+ pwmchip_parent(chip)->bus ? pwmchip_parent(chip)->bus->name : "no-bus",
+ dev_name(pwmchip_parent(chip)), chip->npwm);
+
+ pwm_dbg_show(chip, s);
+
+ return 0;
+}
+
+static const struct seq_operations pwm_debugfs_sops = {
+ .start = pwm_seq_start,
+ .next = pwm_seq_next,
+ .stop = pwm_seq_stop,
+ .show = pwm_seq_show,
+};
+
+DEFINE_SEQ_ATTRIBUTE(pwm_debugfs);
+
+static int __init pwm_init(void)
+{
+ int ret;
+
+ ret = alloc_chrdev_region(&pwm_devt, 0, PWM_MINOR_COUNT, "pwm");
+ if (ret) {
+ pr_err("Failed to initialize chrdev region for PWM usage\n");
+ return ret;
+ }
+
+ ret = class_register(&pwm_class);
+ if (ret) {
+ pr_err("Failed to initialize PWM class (%pe)\n", ERR_PTR(ret));
+ unregister_chrdev_region(pwm_devt, 256);
+ return ret;
+ }
+
+ if (IS_ENABLED(CONFIG_DEBUG_FS))
+ debugfs_create_file("pwm", 0444, NULL, NULL, &pwm_debugfs_fops);
+
+ return 0;
+}
+subsys_initcall(pwm_init);
diff --git a/drivers/pwm/pwm-ab8500.c b/drivers/pwm/pwm-ab8500.c
new file mode 100644
index 000000000..f000adab8
--- /dev/null
+++ b/drivers/pwm/pwm-ab8500.c
@@ -0,0 +1,227 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * Author: Arun R Murthy <arun.murthy@stericsson.com>
+ * Datasheet: https://web.archive.org/web/20130614115108/http://www.stericsson.com/developers/CD00291561_UM1031_AB8500_user_manual-rev5_CTDS_public.pdf
+ */
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/pwm.h>
+#include <linux/mfd/abx500.h>
+#include <linux/mfd/abx500/ab8500.h>
+#include <linux/module.h>
+
+/*
+ * PWM Out generators
+ * Bank: 0x10
+ */
+#define AB8500_PWM_OUT_CTRL1_REG 0x60
+#define AB8500_PWM_OUT_CTRL2_REG 0x61
+#define AB8500_PWM_OUT_CTRL7_REG 0x66
+
+#define AB8500_PWM_CLKRATE 9600000
+
+struct ab8500_pwm_chip {
+ unsigned int hwid;
+};
+
+static struct ab8500_pwm_chip *ab8500_pwm_from_chip(struct pwm_chip *chip)
+{
+ return pwmchip_get_drvdata(chip);
+}
+
+static int ab8500_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
+ const struct pwm_state *state)
+{
+ int ret;
+ u8 reg;
+ u8 higher_val, lower_val;
+ unsigned int duty_steps, div;
+ struct ab8500_pwm_chip *ab8500 = ab8500_pwm_from_chip(chip);
+
+ if (state->polarity != PWM_POLARITY_NORMAL)
+ return -EINVAL;
+
+ if (state->enabled) {
+ /*
+ * A time quantum is
+ * q = (32 - FreqPWMOutx[3:0]) / AB8500_PWM_CLKRATE
+ * The period is always 1024 q, duty_cycle is between 1q and 1024q.
+ *
+ * FreqPWMOutx[3:0] | output frequency | output frequency | 1024q = period
+ * | (from manual) | (1 / 1024q) | = 1 / freq
+ * -----------------+------------------+------------------+--------------
+ * b0000 | 293 Hz | 292.968750 Hz | 3413333.33 ns
+ * b0001 | 302 Hz | 302.419355 Hz | 3306666.66 ns
+ * b0010 | 312 Hz | 312.500000 Hz | 3200000 ns
+ * b0011 | 323 Hz | 323.275862 Hz | 3093333.33 ns
+ * b0100 | 334 Hz | 334.821429 Hz | 2986666.66 ns
+ * b0101 | 347 Hz | 347.222222 Hz | 2880000 ns
+ * b0110 | 360 Hz | 360.576923 Hz | 2773333.33 ns
+ * b0111 | 375 Hz | 375.000000 Hz | 2666666.66 ns
+ * b1000 | 390 Hz | 390.625000 Hz | 2560000 ns
+ * b1001 | 407 Hz | 407.608696 Hz | 2453333.33 ns
+ * b1010 | 426 Hz | 426.136364 Hz | 2346666.66 ns
+ * b1011 | 446 Hz | 446.428571 Hz | 2240000 ns
+ * b1100 | 468 Hz | 468.750000 Hz | 2133333.33 ns
+ * b1101 | 493 Hz | 493.421053 Hz | 2026666.66 ns
+ * b1110 | 520 Hz | 520.833333 Hz | 1920000 ns
+ * b1111 | 551 Hz | 551.470588 Hz | 1813333.33 ns
+ *
+ *
+ * AB8500_PWM_CLKRATE is a multiple of 1024, so the division by
+ * 1024 can be done in this factor without loss of precision.
+ */
+ div = min_t(u64, mul_u64_u64_div_u64(state->period,
+ AB8500_PWM_CLKRATE >> 10,
+ NSEC_PER_SEC), 32); /* 32 - FreqPWMOutx[3:0] */
+ if (div <= 16)
+ /* requested period < 3413333.33 */
+ return -EINVAL;
+
+ duty_steps = max_t(u64, mul_u64_u64_div_u64(state->duty_cycle,
+ AB8500_PWM_CLKRATE,
+ (u64)NSEC_PER_SEC * div), 1024);
+ }
+
+ /*
+ * The hardware doesn't support duty_steps = 0 explicitly, but emits low
+ * when disabled.
+ */
+ if (!state->enabled || duty_steps == 0) {
+ ret = abx500_mask_and_set_register_interruptible(pwmchip_parent(chip),
+ AB8500_MISC, AB8500_PWM_OUT_CTRL7_REG,
+ 1 << ab8500->hwid, 0);
+
+ if (ret < 0)
+ dev_err(pwmchip_parent(chip), "%s: Failed to disable PWM, Error %d\n",
+ pwm->label, ret);
+ return ret;
+ }
+
+ /*
+ * The lower 8 bits of duty_steps is written to ...
+ * AB8500_PWM_OUT_CTRL1_REG[0:7]
+ */
+ lower_val = (duty_steps - 1) & 0x00ff;
+ /*
+ * The two remaining high bits to
+ * AB8500_PWM_OUT_CTRL2_REG[0:1]; together with FreqPWMOutx.
+ */
+ higher_val = ((duty_steps - 1) & 0x0300) >> 8 | (32 - div) << 4;
+
+ reg = AB8500_PWM_OUT_CTRL1_REG + (ab8500->hwid * 2);
+
+ ret = abx500_set_register_interruptible(pwmchip_parent(chip), AB8500_MISC,
+ reg, lower_val);
+ if (ret < 0)
+ return ret;
+
+ ret = abx500_set_register_interruptible(pwmchip_parent(chip), AB8500_MISC,
+ (reg + 1), higher_val);
+ if (ret < 0)
+ return ret;
+
+ /* enable */
+ ret = abx500_mask_and_set_register_interruptible(pwmchip_parent(chip),
+ AB8500_MISC, AB8500_PWM_OUT_CTRL7_REG,
+ 1 << ab8500->hwid, 1 << ab8500->hwid);
+ if (ret < 0)
+ dev_err(pwmchip_parent(chip), "%s: Failed to enable PWM, Error %d\n",
+ pwm->label, ret);
+
+ return ret;
+}
+
+static int ab8500_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm,
+ struct pwm_state *state)
+{
+ u8 ctrl7, lower_val, higher_val;
+ int ret;
+ struct ab8500_pwm_chip *ab8500 = ab8500_pwm_from_chip(chip);
+ unsigned int div, duty_steps;
+
+ ret = abx500_get_register_interruptible(pwmchip_parent(chip), AB8500_MISC,
+ AB8500_PWM_OUT_CTRL7_REG,
+ &ctrl7);
+ if (ret)
+ return ret;
+
+ state->polarity = PWM_POLARITY_NORMAL;
+
+ if (!(ctrl7 & 1 << ab8500->hwid)) {
+ state->enabled = false;
+ return 0;
+ }
+
+ ret = abx500_get_register_interruptible(pwmchip_parent(chip), AB8500_MISC,
+ AB8500_PWM_OUT_CTRL1_REG + (ab8500->hwid * 2),
+ &lower_val);
+ if (ret)
+ return ret;
+
+ ret = abx500_get_register_interruptible(pwmchip_parent(chip), AB8500_MISC,
+ AB8500_PWM_OUT_CTRL2_REG + (ab8500->hwid * 2),
+ &higher_val);
+ if (ret)
+ return ret;
+
+ div = 32 - ((higher_val & 0xf0) >> 4);
+ duty_steps = ((higher_val & 3) << 8 | lower_val) + 1;
+
+ state->period = DIV64_U64_ROUND_UP((u64)div << 10, AB8500_PWM_CLKRATE);
+ state->duty_cycle = DIV64_U64_ROUND_UP((u64)div * duty_steps, AB8500_PWM_CLKRATE);
+
+ return 0;
+}
+
+static const struct pwm_ops ab8500_pwm_ops = {
+ .apply = ab8500_pwm_apply,
+ .get_state = ab8500_pwm_get_state,
+};
+
+static int ab8500_pwm_probe(struct platform_device *pdev)
+{
+ struct pwm_chip *chip;
+ struct ab8500_pwm_chip *ab8500;
+ int err;
+
+ if (pdev->id < 1 || pdev->id > 31)
+ return dev_err_probe(&pdev->dev, -EINVAL, "Invalid device id %d\n", pdev->id);
+
+ /*
+ * Nothing to be done in probe, this is required to get the
+ * device which is required for ab8500 read and write
+ */
+ chip = devm_pwmchip_alloc(&pdev->dev, 1, sizeof(*ab8500));
+ if (IS_ERR(chip))
+ return PTR_ERR(chip);
+
+ ab8500 = ab8500_pwm_from_chip(chip);
+
+ chip->ops = &ab8500_pwm_ops;
+ ab8500->hwid = pdev->id - 1;
+
+ err = devm_pwmchip_add(&pdev->dev, chip);
+ if (err < 0)
+ return dev_err_probe(&pdev->dev, err, "Failed to add pwm chip\n");
+
+ dev_dbg(&pdev->dev, "pwm probe successful\n");
+
+ return 0;
+}
+
+static struct platform_driver ab8500_pwm_driver = {
+ .driver = {
+ .name = "ab8500-pwm",
+ },
+ .probe = ab8500_pwm_probe,
+};
+module_platform_driver(ab8500_pwm_driver);
+
+MODULE_AUTHOR("Arun MURTHY <arun.murthy@stericsson.com>");
+MODULE_DESCRIPTION("AB8500 Pulse Width Modulation Driver");
+MODULE_ALIAS("platform:ab8500-pwm");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/pwm/pwm-adp5585.c b/drivers/pwm/pwm-adp5585.c
new file mode 100644
index 000000000..9a1dbba75
--- /dev/null
+++ b/drivers/pwm/pwm-adp5585.c
@@ -0,0 +1,222 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Analog Devices ADP5585 PWM driver
+ *
+ * Copyright 2022 NXP
+ * Copyright 2024 Ideas on Board Oy
+ *
+ * Limitations:
+ * - The .apply() operation executes atomically, but may not wait for the
+ * period to complete (this is not documented and would need to be tested).
+ * - Disabling the PWM drives the output pin to a low level immediately.
+ * - The hardware can only generate normal polarity output.
+ */
+
+#include <asm/byteorder.h>
+
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/math64.h>
+#include <linux/mfd/adp5585.h>
+#include <linux/minmax.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+#include <linux/regmap.h>
+#include <linux/time.h>
+#include <linux/types.h>
+
+#define ADP5585_PWM_CHAN_NUM 1
+
+#define ADP5585_PWM_OSC_FREQ_HZ 1000000U
+#define ADP5585_PWM_MIN_PERIOD_NS (2ULL * NSEC_PER_SEC / ADP5585_PWM_OSC_FREQ_HZ)
+#define ADP5585_PWM_MAX_PERIOD_NS (2ULL * 0xffff * NSEC_PER_SEC / ADP5585_PWM_OSC_FREQ_HZ)
+
+struct adp5585_pwm_chip {
+ unsigned int pwm_cfg;
+ unsigned int pwm_offt_low;
+ unsigned int pwm_ont_low;
+};
+
+struct adp5585_pwm {
+ const struct adp5585_pwm_chip *info;
+ struct regmap *regmap;
+ unsigned int ext_cfg;
+};
+
+static int pwm_adp5585_request(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct adp5585_pwm *adp5585_pwm = pwmchip_get_drvdata(chip);
+
+ /* Configure the R3 pin as PWM output. */
+ return regmap_update_bits(adp5585_pwm->regmap, adp5585_pwm->ext_cfg,
+ ADP5585_R3_EXTEND_CFG_MASK,
+ ADP5585_R3_EXTEND_CFG_PWM_OUT);
+}
+
+static void pwm_adp5585_free(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct adp5585_pwm *adp5585_pwm = pwmchip_get_drvdata(chip);
+
+ regmap_update_bits(adp5585_pwm->regmap, adp5585_pwm->ext_cfg,
+ ADP5585_R3_EXTEND_CFG_MASK,
+ ADP5585_R3_EXTEND_CFG_GPIO4);
+}
+
+static int pwm_adp5585_apply(struct pwm_chip *chip,
+ struct pwm_device *pwm,
+ const struct pwm_state *state)
+{
+ struct adp5585_pwm *adp5585_pwm = pwmchip_get_drvdata(chip);
+ const struct adp5585_pwm_chip *info = adp5585_pwm->info;
+ struct regmap *regmap = adp5585_pwm->regmap;
+ u64 period, duty_cycle;
+ u32 on, off;
+ __le16 val;
+ int ret;
+
+ if (!state->enabled) {
+ regmap_clear_bits(regmap, info->pwm_cfg, ADP5585_PWM_EN);
+ return 0;
+ }
+
+ if (state->polarity != PWM_POLARITY_NORMAL)
+ return -EINVAL;
+
+ if (state->period < ADP5585_PWM_MIN_PERIOD_NS)
+ return -EINVAL;
+
+ period = min(state->period, ADP5585_PWM_MAX_PERIOD_NS);
+ duty_cycle = min(state->duty_cycle, period);
+
+ /*
+ * Compute the on and off time. As the internal oscillator frequency is
+ * 1MHz, the calculation can be simplified without loss of precision.
+ */
+ on = div_u64(duty_cycle, NSEC_PER_SEC / ADP5585_PWM_OSC_FREQ_HZ);
+ off = div_u64(period, NSEC_PER_SEC / ADP5585_PWM_OSC_FREQ_HZ) - on;
+
+ val = cpu_to_le16(off);
+ ret = regmap_bulk_write(regmap, info->pwm_offt_low, &val, 2);
+ if (ret)
+ return ret;
+
+ val = cpu_to_le16(on);
+ ret = regmap_bulk_write(regmap, info->pwm_ont_low, &val, 2);
+ if (ret)
+ return ret;
+
+ /* Enable PWM in continuous mode and no external AND'ing. */
+ ret = regmap_update_bits(regmap, info->pwm_cfg,
+ ADP5585_PWM_IN_AND | ADP5585_PWM_MODE |
+ ADP5585_PWM_EN, ADP5585_PWM_EN);
+ if (ret)
+ return ret;
+
+ return regmap_set_bits(regmap, info->pwm_cfg, ADP5585_PWM_EN);
+}
+
+static int pwm_adp5585_get_state(struct pwm_chip *chip,
+ struct pwm_device *pwm,
+ struct pwm_state *state)
+{
+ struct adp5585_pwm *adp5585_pwm = pwmchip_get_drvdata(chip);
+ const struct adp5585_pwm_chip *info = adp5585_pwm->info;
+ struct regmap *regmap = adp5585_pwm->regmap;
+ unsigned int on, off;
+ unsigned int val;
+ __le16 on_off;
+ int ret;
+
+ ret = regmap_bulk_read(regmap, info->pwm_offt_low, &on_off, 2);
+ if (ret)
+ return ret;
+ off = le16_to_cpu(on_off);
+
+ ret = regmap_bulk_read(regmap, info->pwm_ont_low, &on_off, 2);
+ if (ret)
+ return ret;
+ on = le16_to_cpu(on_off);
+
+ state->duty_cycle = on * (NSEC_PER_SEC / ADP5585_PWM_OSC_FREQ_HZ);
+ state->period = (on + off) * (NSEC_PER_SEC / ADP5585_PWM_OSC_FREQ_HZ);
+
+ state->polarity = PWM_POLARITY_NORMAL;
+
+ regmap_read(regmap, info->pwm_cfg, &val);
+ state->enabled = !!(val & ADP5585_PWM_EN);
+
+ return 0;
+}
+
+static const struct pwm_ops adp5585_pwm_ops = {
+ .request = pwm_adp5585_request,
+ .free = pwm_adp5585_free,
+ .apply = pwm_adp5585_apply,
+ .get_state = pwm_adp5585_get_state,
+};
+
+static int adp5585_pwm_probe(struct platform_device *pdev)
+{
+ const struct platform_device_id *id = platform_get_device_id(pdev);
+ struct device *dev = &pdev->dev;
+ struct adp5585_dev *adp5585 = dev_get_drvdata(dev->parent);
+ struct adp5585_pwm *adp5585_pwm;
+ struct pwm_chip *chip;
+ int ret;
+
+ chip = devm_pwmchip_alloc(dev, ADP5585_PWM_CHAN_NUM,
+ sizeof(*adp5585_pwm));
+ if (IS_ERR(chip))
+ return PTR_ERR(chip);
+
+ adp5585_pwm = pwmchip_get_drvdata(chip);
+ adp5585_pwm->regmap = adp5585->regmap;
+ adp5585_pwm->ext_cfg = adp5585->regs->ext_cfg;
+
+ adp5585_pwm->info = (const struct adp5585_pwm_chip *)id->driver_data;
+ if (!adp5585_pwm->info)
+ return -ENODEV;
+
+ device_set_of_node_from_dev(dev, dev->parent);
+
+ chip->ops = &adp5585_pwm_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 adp5585_pwm_chip adp5585_pwm_chip_info = {
+ .pwm_cfg = ADP5585_PWM_CFG,
+ .pwm_offt_low = ADP5585_PWM_OFFT_LOW,
+ .pwm_ont_low = ADP5585_PWM_ONT_LOW,
+};
+
+static const struct adp5585_pwm_chip adp5589_pwm_chip_info = {
+ .pwm_cfg = ADP5589_PWM_CFG,
+ .pwm_offt_low = ADP5589_PWM_OFFT_LOW,
+ .pwm_ont_low = ADP5589_PWM_ONT_LOW,
+};
+
+static const struct platform_device_id adp5585_pwm_id_table[] = {
+ { .name = "adp5585-pwm", .driver_data = (kernel_ulong_t)&adp5585_pwm_chip_info },
+ { .name = "adp5589-pwm", .driver_data = (kernel_ulong_t)&adp5589_pwm_chip_info },
+ { /* Sentinel */ }
+};
+MODULE_DEVICE_TABLE(platform, adp5585_pwm_id_table);
+
+static struct platform_driver adp5585_pwm_driver = {
+ .driver = {
+ .name = "adp5585-pwm",
+ },
+ .probe = adp5585_pwm_probe,
+ .id_table = adp5585_pwm_id_table,
+};
+module_platform_driver(adp5585_pwm_driver);
+
+MODULE_AUTHOR("Xiaoning Wang <xiaoning.wang@nxp.com>");
+MODULE_DESCRIPTION("ADP5585 PWM Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/pwm/pwm-airoha.c b/drivers/pwm/pwm-airoha.c
new file mode 100644
index 000000000..0cf6b0115
--- /dev/null
+++ b/drivers/pwm/pwm-airoha.c
@@ -0,0 +1,621 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2022 Markus Gothe <markus.gothe@genexis.eu>
+ * Copyright 2025 Christian Marangi <ansuelsmth@gmail.com>
+ *
+ * Limitations:
+ * - Only 8 concurrent waveform generators are available for 8 combinations of
+ * duty_cycle and period. Waveform generators are shared between 16 GPIO
+ * pins and 17 SIPO GPIO pins.
+ * - Supports only normal polarity.
+ * - On configuration the currently running period is completed.
+ * - Minimum supported period is 4 ms
+ * - Maximum supported period is 1s
+ */
+
+#include <linux/array_size.h>
+#include <linux/bitfield.h>
+#include <linux/bitmap.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/math64.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+#include <linux/regmap.h>
+#include <linux/types.h>
+
+#define AIROHA_PWM_REG_SGPIO_LED_DATA 0x0024
+#define AIROHA_PWM_SGPIO_LED_DATA_SHIFT_FLAG BIT(31)
+#define AIROHA_PWM_SGPIO_LED_DATA_DATA GENMASK(16, 0)
+
+#define AIROHA_PWM_REG_SGPIO_CLK_DIVR 0x0028
+#define AIROHA_PWM_SGPIO_CLK_DIVR GENMASK(1, 0)
+#define AIROHA_PWM_SGPIO_CLK_DIVR_32 FIELD_PREP_CONST(AIROHA_PWM_SGPIO_CLK_DIVR, 3)
+#define AIROHA_PWM_SGPIO_CLK_DIVR_16 FIELD_PREP_CONST(AIROHA_PWM_SGPIO_CLK_DIVR, 2)
+#define AIROHA_PWM_SGPIO_CLK_DIVR_8 FIELD_PREP_CONST(AIROHA_PWM_SGPIO_CLK_DIVR, 1)
+#define AIROHA_PWM_SGPIO_CLK_DIVR_4 FIELD_PREP_CONST(AIROHA_PWM_SGPIO_CLK_DIVR, 0)
+
+#define AIROHA_PWM_REG_SGPIO_CLK_DLY 0x002c
+
+#define AIROHA_PWM_REG_SIPO_FLASH_MODE_CFG 0x0030
+#define AIROHA_PWM_SERIAL_GPIO_FLASH_MODE BIT(1)
+#define AIROHA_PWM_SERIAL_GPIO_MODE_74HC164 BIT(0)
+
+#define AIROHA_PWM_REG_GPIO_FLASH_PRD_SET(_n) (0x003c + (4 * (_n)))
+#define AIROHA_PWM_REG_GPIO_FLASH_PRD_SHIFT(_n) (16 * (_n))
+#define AIROHA_PWM_GPIO_FLASH_PRD_LOW GENMASK(15, 8)
+#define AIROHA_PWM_GPIO_FLASH_PRD_HIGH GENMASK(7, 0)
+
+#define AIROHA_PWM_REG_GPIO_FLASH_MAP(_n) (0x004c + (4 * (_n)))
+#define AIROHA_PWM_REG_GPIO_FLASH_MAP_SHIFT(_n) (4 * (_n))
+#define AIROHA_PWM_GPIO_FLASH_EN BIT(3)
+#define AIROHA_PWM_GPIO_FLASH_SET_ID GENMASK(2, 0)
+
+/* Register map is equal to GPIO flash map */
+#define AIROHA_PWM_REG_SIPO_FLASH_MAP(_n) (0x0054 + (4 * (_n)))
+
+#define AIROHA_PWM_REG_CYCLE_CFG_VALUE(_n) (0x0098 + (4 * (_n)))
+#define AIROHA_PWM_REG_CYCLE_CFG_SHIFT(_n) (8 * (_n))
+#define AIROHA_PWM_WAVE_GEN_CYCLE GENMASK(7, 0)
+
+/* GPIO/SIPO flash map handles 8 pins in one register */
+#define AIROHA_PWM_PINS_PER_FLASH_MAP 8
+/* Cycle(Period) registers handles 4 generators in one 32-bit register */
+#define AIROHA_PWM_BUCKET_PER_CYCLE_CFG 4
+/* Flash(Duty) producer handles 2 generators in one 32-bit register */
+#define AIROHA_PWM_BUCKET_PER_FLASH_PROD 2
+
+#define AIROHA_PWM_NUM_BUCKETS 8
+/*
+ * The first 16 GPIO pins, GPIO0-GPIO15, are mapped into 16 PWM channels, 0-15.
+ * The SIPO GPIO pins are 17 pins which are mapped into 17 PWM channels, 16-32.
+ * However, we've only got 8 concurrent waveform generators and can therefore
+ * only use up to 8 different combinations of duty cycle and period at a time.
+ */
+#define AIROHA_PWM_NUM_GPIO 16
+#define AIROHA_PWM_NUM_SIPO 17
+#define AIROHA_PWM_MAX_CHANNELS (AIROHA_PWM_NUM_GPIO + AIROHA_PWM_NUM_SIPO)
+
+struct airoha_pwm_bucket {
+ /* Concurrent access protected by PWM core */
+ int used;
+ u32 period_ticks;
+ u32 duty_ticks;
+};
+
+struct airoha_pwm {
+ struct regmap *regmap;
+
+ DECLARE_BITMAP(initialized, AIROHA_PWM_MAX_CHANNELS);
+
+ struct airoha_pwm_bucket buckets[AIROHA_PWM_NUM_BUCKETS];
+
+ /* Cache bucket used by each pwm channel */
+ u8 channel_bucket[AIROHA_PWM_MAX_CHANNELS];
+};
+
+/* The PWM hardware supports periods between 4 ms and 1 s */
+#define AIROHA_PWM_PERIOD_TICK_NS (4 * NSEC_PER_MSEC)
+#define AIROHA_PWM_PERIOD_MAX_NS (1 * NSEC_PER_SEC)
+/* It is represented internally as 1/250 s between 1 and 250. Unit is ticks. */
+#define AIROHA_PWM_PERIOD_MIN 1
+#define AIROHA_PWM_PERIOD_MAX 250
+/* Duty cycle is relative with 255 corresponding to 100% */
+#define AIROHA_PWM_DUTY_FULL 255
+
+static void airoha_pwm_get_flash_map_addr_and_shift(unsigned int hwpwm,
+ u32 *addr, u32 *shift)
+{
+ unsigned int offset, hwpwm_bit;
+
+ if (hwpwm >= AIROHA_PWM_NUM_GPIO) {
+ unsigned int sipohwpwm = hwpwm - AIROHA_PWM_NUM_GPIO;
+
+ offset = sipohwpwm / AIROHA_PWM_PINS_PER_FLASH_MAP;
+ hwpwm_bit = sipohwpwm % AIROHA_PWM_PINS_PER_FLASH_MAP;
+
+ /* One FLASH_MAP register handles 8 pins */
+ *shift = AIROHA_PWM_REG_GPIO_FLASH_MAP_SHIFT(hwpwm_bit);
+ *addr = AIROHA_PWM_REG_SIPO_FLASH_MAP(offset);
+ } else {
+ offset = hwpwm / AIROHA_PWM_PINS_PER_FLASH_MAP;
+ hwpwm_bit = hwpwm % AIROHA_PWM_PINS_PER_FLASH_MAP;
+
+ /* One FLASH_MAP register handles 8 pins */
+ *shift = AIROHA_PWM_REG_GPIO_FLASH_MAP_SHIFT(hwpwm_bit);
+ *addr = AIROHA_PWM_REG_GPIO_FLASH_MAP(offset);
+ }
+}
+
+static u32 airoha_pwm_get_period_ticks_from_ns(u32 period_ns)
+{
+ return period_ns / AIROHA_PWM_PERIOD_TICK_NS;
+}
+
+static u32 airoha_pwm_get_duty_ticks_from_ns(u32 period_ns, u32 duty_ns)
+{
+ return mul_u64_u32_div(duty_ns, AIROHA_PWM_DUTY_FULL, period_ns);
+}
+
+static u32 airoha_pwm_get_period_ns_from_ticks(u32 period_tick)
+{
+ return period_tick * AIROHA_PWM_PERIOD_TICK_NS;
+}
+
+static u32 airoha_pwm_get_duty_ns_from_ticks(u32 period_tick, u32 duty_tick)
+{
+ u32 period_ns = period_tick * AIROHA_PWM_PERIOD_TICK_NS;
+
+ /*
+ * Overflow can't occur in multiplication as duty_tick is just 8 bit
+ * and period_ns is clamped to AIROHA_PWM_PERIOD_MAX_NS and fit in a
+ * u64.
+ */
+ return DIV_U64_ROUND_UP(duty_tick * period_ns, AIROHA_PWM_DUTY_FULL);
+}
+
+static int airoha_pwm_get_bucket(struct airoha_pwm *pc, int bucket,
+ u64 *period_ns, u64 *duty_ns)
+{
+ struct regmap *map = pc->regmap;
+ u32 period_tick, duty_tick;
+ unsigned int offset;
+ u32 shift, val;
+ int ret;
+
+ offset = bucket / AIROHA_PWM_BUCKET_PER_CYCLE_CFG;
+ shift = bucket % AIROHA_PWM_BUCKET_PER_CYCLE_CFG;
+ shift = AIROHA_PWM_REG_CYCLE_CFG_SHIFT(shift);
+
+ ret = regmap_read(map, AIROHA_PWM_REG_CYCLE_CFG_VALUE(offset), &val);
+ if (ret)
+ return ret;
+
+ period_tick = FIELD_GET(AIROHA_PWM_WAVE_GEN_CYCLE, val >> shift);
+ *period_ns = airoha_pwm_get_period_ns_from_ticks(period_tick);
+
+ offset = bucket / AIROHA_PWM_BUCKET_PER_FLASH_PROD;
+ shift = bucket % AIROHA_PWM_BUCKET_PER_FLASH_PROD;
+ shift = AIROHA_PWM_REG_GPIO_FLASH_PRD_SHIFT(shift);
+
+ ret = regmap_read(map, AIROHA_PWM_REG_GPIO_FLASH_PRD_SET(offset),
+ &val);
+ if (ret)
+ return ret;
+
+ duty_tick = FIELD_GET(AIROHA_PWM_GPIO_FLASH_PRD_HIGH, val >> shift);
+ *duty_ns = airoha_pwm_get_duty_ns_from_ticks(period_tick, duty_tick);
+
+ return 0;
+}
+
+static int airoha_pwm_get_generator(struct airoha_pwm *pc, u32 duty_ticks,
+ u32 period_ticks)
+{
+ int best = -ENOENT, unused = -ENOENT;
+ u32 duty_ns, best_duty_ns = 0;
+ u32 best_period_ticks = 0;
+ unsigned int i;
+
+ duty_ns = airoha_pwm_get_duty_ns_from_ticks(period_ticks, duty_ticks);
+
+ for (i = 0; i < ARRAY_SIZE(pc->buckets); i++) {
+ struct airoha_pwm_bucket *bucket = &pc->buckets[i];
+ u32 bucket_period_ticks = bucket->period_ticks;
+ u32 bucket_duty_ticks = bucket->duty_ticks;
+
+ /* If found, save an unused bucket to return it later */
+ if (!bucket->used) {
+ unused = i;
+ continue;
+ }
+
+ /* We found a matching bucket, exit early */
+ if (duty_ticks == bucket_duty_ticks &&
+ period_ticks == bucket_period_ticks)
+ return i;
+
+ /*
+ * Unlike duty cycle zero, which can be handled by
+ * disabling PWM, a generator is needed for full duty
+ * cycle but it can be reused regardless of period
+ */
+ if (duty_ticks == AIROHA_PWM_DUTY_FULL &&
+ bucket_duty_ticks == AIROHA_PWM_DUTY_FULL)
+ return i;
+
+ /*
+ * With an unused bucket available, skip searching for
+ * a bucket to recycle (closer to the requested period/duty)
+ */
+ if (unused >= 0)
+ continue;
+
+ /* Ignore bucket with invalid period */
+ if (bucket_period_ticks > period_ticks)
+ continue;
+
+ /*
+ * Search for a bucket closer to the requested period
+ * that has the maximal possible period that isn't bigger
+ * than the requested period. For that period pick the maximal
+ * duty cycle that isn't bigger than the requested duty_cycle.
+ */
+ if (bucket_period_ticks >= best_period_ticks) {
+ u32 bucket_duty_ns = airoha_pwm_get_duty_ns_from_ticks(bucket_period_ticks,
+ bucket_duty_ticks);
+
+ /* Skip bucket that goes over the requested duty */
+ if (bucket_duty_ns > duty_ns)
+ continue;
+
+ if (bucket_duty_ns > best_duty_ns) {
+ best_period_ticks = bucket_period_ticks;
+ best_duty_ns = bucket_duty_ns;
+ best = i;
+ }
+ }
+ }
+
+ /* Return an unused bucket or the best one found (if ever) */
+ return unused >= 0 ? unused : best;
+}
+
+static void airoha_pwm_release_bucket_config(struct airoha_pwm *pc,
+ unsigned int hwpwm)
+{
+ int bucket;
+
+ /* Nothing to clear, PWM channel never used */
+ if (!test_bit(hwpwm, pc->initialized))
+ return;
+
+ bucket = pc->channel_bucket[hwpwm];
+ pc->buckets[bucket].used--;
+}
+
+static int airoha_pwm_apply_bucket_config(struct airoha_pwm *pc, unsigned int bucket,
+ u32 duty_ticks, u32 period_ticks)
+{
+ u32 mask, shift, val;
+ u32 offset;
+ int ret;
+
+ offset = bucket / AIROHA_PWM_BUCKET_PER_CYCLE_CFG;
+ shift = bucket % AIROHA_PWM_BUCKET_PER_CYCLE_CFG;
+ shift = AIROHA_PWM_REG_CYCLE_CFG_SHIFT(shift);
+
+ /* Configure frequency divisor */
+ mask = AIROHA_PWM_WAVE_GEN_CYCLE << shift;
+ val = FIELD_PREP(AIROHA_PWM_WAVE_GEN_CYCLE, period_ticks) << shift;
+ ret = regmap_update_bits(pc->regmap, AIROHA_PWM_REG_CYCLE_CFG_VALUE(offset),
+ mask, val);
+ if (ret)
+ return ret;
+
+ offset = bucket / AIROHA_PWM_BUCKET_PER_FLASH_PROD;
+ shift = bucket % AIROHA_PWM_BUCKET_PER_FLASH_PROD;
+ shift = AIROHA_PWM_REG_GPIO_FLASH_PRD_SHIFT(shift);
+
+ /* Configure duty cycle */
+ mask = AIROHA_PWM_GPIO_FLASH_PRD_HIGH << shift;
+ val = FIELD_PREP(AIROHA_PWM_GPIO_FLASH_PRD_HIGH, duty_ticks) << shift;
+ ret = regmap_update_bits(pc->regmap, AIROHA_PWM_REG_GPIO_FLASH_PRD_SET(offset),
+ mask, val);
+ if (ret)
+ return ret;
+
+ mask = AIROHA_PWM_GPIO_FLASH_PRD_LOW << shift;
+ val = FIELD_PREP(AIROHA_PWM_GPIO_FLASH_PRD_LOW,
+ AIROHA_PWM_DUTY_FULL - duty_ticks) << shift;
+ return regmap_update_bits(pc->regmap, AIROHA_PWM_REG_GPIO_FLASH_PRD_SET(offset),
+ mask, val);
+}
+
+static int airoha_pwm_consume_generator(struct airoha_pwm *pc,
+ u32 duty_ticks, u32 period_ticks,
+ unsigned int hwpwm)
+{
+ bool config_bucket = false;
+ int bucket, ret;
+
+ /*
+ * Search for a bucket that already satisfies duty and period
+ * or an unused one.
+ * If not found, -ENOENT is returned.
+ */
+ bucket = airoha_pwm_get_generator(pc, duty_ticks, period_ticks);
+ if (bucket < 0)
+ return bucket;
+
+ /* Release previous used bucket (if any) */
+ airoha_pwm_release_bucket_config(pc, hwpwm);
+
+ if (!pc->buckets[bucket].used)
+ config_bucket = true;
+ pc->buckets[bucket].used++;
+
+ if (config_bucket) {
+ pc->buckets[bucket].period_ticks = period_ticks;
+ pc->buckets[bucket].duty_ticks = duty_ticks;
+ ret = airoha_pwm_apply_bucket_config(pc, bucket,
+ duty_ticks,
+ period_ticks);
+ if (ret) {
+ pc->buckets[bucket].used--;
+ return ret;
+ }
+ }
+
+ return bucket;
+}
+
+static int airoha_pwm_sipo_init(struct airoha_pwm *pc)
+{
+ u32 val;
+ int ret;
+
+ ret = regmap_clear_bits(pc->regmap, AIROHA_PWM_REG_SIPO_FLASH_MODE_CFG,
+ AIROHA_PWM_SERIAL_GPIO_MODE_74HC164);
+ if (ret)
+ return ret;
+
+ /* Configure shift register chip clock timings, use 32x divisor */
+ ret = regmap_write(pc->regmap, AIROHA_PWM_REG_SGPIO_CLK_DIVR,
+ AIROHA_PWM_SGPIO_CLK_DIVR_32);
+ if (ret)
+ return ret;
+
+ /*
+ * Configure the shift register chip clock delay. This needs
+ * to be configured based on the chip characteristics when the SoC
+ * apply the shift register configuration.
+ * This doesn't affect actual PWM operation and is only specific to
+ * the shift register chip.
+ *
+ * For 74HC164 we set it to 0.
+ *
+ * For reference, the actual delay applied is the internal clock
+ * feed to the SGPIO chip + 1.
+ *
+ * From documentation is specified that clock delay should not be
+ * greater than (AIROHA_PWM_REG_SGPIO_CLK_DIVR / 2) - 1.
+ */
+ ret = regmap_write(pc->regmap, AIROHA_PWM_REG_SGPIO_CLK_DLY, 0);
+ if (ret)
+ return ret;
+
+ /*
+ * It is necessary to explicitly shift out all zeros after muxing
+ * to initialize the shift register before enabling PWM
+ * mode because in PWM mode SIPO will not start shifting until
+ * it needs to output a non-zero value (bit 31 of led_data
+ * indicates shifting in progress and it must return to zero
+ * before led_data can be written or PWM mode can be set).
+ */
+ ret = regmap_read_poll_timeout(pc->regmap, AIROHA_PWM_REG_SGPIO_LED_DATA, val,
+ !(val & AIROHA_PWM_SGPIO_LED_DATA_SHIFT_FLAG),
+ 10, 200 * USEC_PER_MSEC);
+ if (ret)
+ return ret;
+
+ ret = regmap_clear_bits(pc->regmap, AIROHA_PWM_REG_SGPIO_LED_DATA,
+ AIROHA_PWM_SGPIO_LED_DATA_DATA);
+ if (ret)
+ return ret;
+ ret = regmap_read_poll_timeout(pc->regmap, AIROHA_PWM_REG_SGPIO_LED_DATA, val,
+ !(val & AIROHA_PWM_SGPIO_LED_DATA_SHIFT_FLAG),
+ 10, 200 * USEC_PER_MSEC);
+ if (ret)
+ return ret;
+
+ /* Set SIPO in PWM mode */
+ return regmap_set_bits(pc->regmap, AIROHA_PWM_REG_SIPO_FLASH_MODE_CFG,
+ AIROHA_PWM_SERIAL_GPIO_FLASH_MODE);
+}
+
+static int airoha_pwm_config_flash_map(struct airoha_pwm *pc,
+ unsigned int hwpwm, int index)
+{
+ unsigned int addr;
+ u32 shift;
+ int ret;
+
+ airoha_pwm_get_flash_map_addr_and_shift(hwpwm, &addr, &shift);
+
+ /* negative index means disable PWM channel */
+ if (index < 0) {
+ /*
+ * If we need to disable the PWM, we just put low the
+ * GPIO. No need to setup buckets.
+ */
+ return regmap_clear_bits(pc->regmap, addr,
+ AIROHA_PWM_GPIO_FLASH_EN << shift);
+ }
+
+ ret = regmap_update_bits(pc->regmap, addr,
+ AIROHA_PWM_GPIO_FLASH_SET_ID << shift,
+ FIELD_PREP(AIROHA_PWM_GPIO_FLASH_SET_ID, index) << shift);
+ if (ret)
+ return ret;
+
+ return regmap_set_bits(pc->regmap, addr, AIROHA_PWM_GPIO_FLASH_EN << shift);
+}
+
+static int airoha_pwm_config(struct airoha_pwm *pc, struct pwm_device *pwm,
+ u32 period_ticks, u32 duty_ticks)
+{
+ unsigned int hwpwm = pwm->hwpwm;
+ int bucket, ret;
+
+ bucket = airoha_pwm_consume_generator(pc, duty_ticks, period_ticks,
+ hwpwm);
+ if (bucket < 0)
+ return bucket;
+
+ ret = airoha_pwm_config_flash_map(pc, hwpwm, bucket);
+ if (ret) {
+ pc->buckets[bucket].used--;
+ return ret;
+ }
+
+ __set_bit(hwpwm, pc->initialized);
+ pc->channel_bucket[hwpwm] = bucket;
+
+ /*
+ * SIPO are special GPIO attached to a shift register chip. The handling
+ * of this chip is internal to the SoC that takes care of applying the
+ * values based on the flash map. To apply a new flash map, it's needed
+ * to trigger a refresh on the shift register chip.
+ * If a SIPO is getting configuring , always reinit the shift register
+ * chip to make sure the correct flash map is applied.
+ * Skip reconfiguring the shift register if the related hwpwm
+ * is disabled (as it doesn't need to be mapped).
+ */
+ if (hwpwm >= AIROHA_PWM_NUM_GPIO) {
+ ret = airoha_pwm_sipo_init(pc);
+ if (ret) {
+ airoha_pwm_release_bucket_config(pc, hwpwm);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static void airoha_pwm_disable(struct airoha_pwm *pc, struct pwm_device *pwm)
+{
+ /* Disable PWM and release the bucket */
+ airoha_pwm_config_flash_map(pc, pwm->hwpwm, -1);
+ airoha_pwm_release_bucket_config(pc, pwm->hwpwm);
+
+ __clear_bit(pwm->hwpwm, pc->initialized);
+
+ /* If no SIPO is used, disable the shift register chip */
+ if (!bitmap_read(pc->initialized,
+ AIROHA_PWM_NUM_GPIO, AIROHA_PWM_NUM_SIPO))
+ regmap_clear_bits(pc->regmap, AIROHA_PWM_REG_SIPO_FLASH_MODE_CFG,
+ AIROHA_PWM_SERIAL_GPIO_FLASH_MODE);
+}
+
+static int airoha_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
+ const struct pwm_state *state)
+{
+ struct airoha_pwm *pc = pwmchip_get_drvdata(chip);
+ u32 period_ticks, duty_ticks;
+ u32 period_ns, duty_ns;
+
+ if (!state->enabled) {
+ airoha_pwm_disable(pc, pwm);
+ return 0;
+ }
+
+ /* Only normal polarity is supported */
+ if (state->polarity == PWM_POLARITY_INVERSED)
+ return -EINVAL;
+
+ /* Exit early if period is less than minimum supported */
+ if (state->period < AIROHA_PWM_PERIOD_TICK_NS)
+ return -EINVAL;
+
+ /* Clamp period to MAX supported value */
+ if (state->period > AIROHA_PWM_PERIOD_MAX_NS)
+ period_ns = AIROHA_PWM_PERIOD_MAX_NS;
+ else
+ period_ns = state->period;
+
+ /* Validate duty to configured period */
+ if (state->duty_cycle > period_ns)
+ duty_ns = period_ns;
+ else
+ duty_ns = state->duty_cycle;
+
+ /* Convert period ns to ticks */
+ period_ticks = airoha_pwm_get_period_ticks_from_ns(period_ns);
+ /* Convert period ticks to ns again for cosistent duty tick calculation */
+ period_ns = airoha_pwm_get_period_ns_from_ticks(period_ticks);
+ duty_ticks = airoha_pwm_get_duty_ticks_from_ns(period_ns, duty_ns);
+
+ return airoha_pwm_config(pc, pwm, period_ticks, duty_ticks);
+}
+
+static int airoha_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm,
+ struct pwm_state *state)
+{
+ struct airoha_pwm *pc = pwmchip_get_drvdata(chip);
+ int ret, hwpwm = pwm->hwpwm;
+ u32 addr, shift, val;
+ u8 bucket;
+
+ airoha_pwm_get_flash_map_addr_and_shift(hwpwm, &addr, &shift);
+
+ ret = regmap_read(pc->regmap, addr, &val);
+ if (ret)
+ return ret;
+
+ state->enabled = FIELD_GET(AIROHA_PWM_GPIO_FLASH_EN, val >> shift);
+ if (!state->enabled)
+ return 0;
+
+ state->polarity = PWM_POLARITY_NORMAL;
+
+ bucket = FIELD_GET(AIROHA_PWM_GPIO_FLASH_SET_ID, val >> shift);
+ return airoha_pwm_get_bucket(pc, bucket, &state->period,
+ &state->duty_cycle);
+}
+
+static const struct pwm_ops airoha_pwm_ops = {
+ .apply = airoha_pwm_apply,
+ .get_state = airoha_pwm_get_state,
+};
+
+static int airoha_pwm_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct airoha_pwm *pc;
+ struct pwm_chip *chip;
+ int ret;
+
+ chip = devm_pwmchip_alloc(dev, AIROHA_PWM_MAX_CHANNELS, sizeof(*pc));
+ if (IS_ERR(chip))
+ return PTR_ERR(chip);
+
+ chip->ops = &airoha_pwm_ops;
+ pc = pwmchip_get_drvdata(chip);
+
+ pc->regmap = device_node_to_regmap(dev_of_node(dev->parent));
+ if (IS_ERR(pc->regmap))
+ return dev_err_probe(dev, PTR_ERR(pc->regmap), "Failed to get PWM regmap\n");
+
+ 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 airoha_pwm_of_match[] = {
+ { .compatible = "airoha,en7581-pwm" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, airoha_pwm_of_match);
+
+static struct platform_driver airoha_pwm_driver = {
+ .driver = {
+ .name = "pwm-airoha",
+ .probe_type = PROBE_PREFER_ASYNCHRONOUS,
+ .of_match_table = airoha_pwm_of_match,
+ },
+ .probe = airoha_pwm_probe,
+};
+module_platform_driver(airoha_pwm_driver);
+
+MODULE_AUTHOR("Lorenzo Bianconi <lorenzo@kernel.org>");
+MODULE_AUTHOR("Markus Gothe <markus.gothe@genexis.eu>");
+MODULE_AUTHOR("Benjamin Larsson <benjamin.larsson@genexis.eu>");
+MODULE_AUTHOR("Christian Marangi <ansuelsmth@gmail.com>");
+MODULE_DESCRIPTION("Airoha EN7581 PWM driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/pwm/pwm-apple.c b/drivers/pwm/pwm-apple.c
new file mode 100644
index 000000000..dac8c9a76
--- /dev/null
+++ b/drivers/pwm/pwm-apple.c
@@ -0,0 +1,158 @@
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+/*
+ * Driver for the Apple SoC PWM controller
+ *
+ * Copyright The Asahi Linux Contributors
+ *
+ * Limitations:
+ * - The writes to cycle registers are shadowed until a write to
+ * the control register.
+ * - If both OFF_CYCLES and ON_CYCLES are set to 0, the output
+ * is a constant off signal.
+ * - When APPLE_PWM_CTRL is set to 0, the output is constant low
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/math64.h>
+
+#define APPLE_PWM_CTRL 0x00
+#define APPLE_PWM_ON_CYCLES 0x1c
+#define APPLE_PWM_OFF_CYCLES 0x18
+
+#define APPLE_PWM_CTRL_ENABLE BIT(0)
+#define APPLE_PWM_CTRL_MODE BIT(2)
+#define APPLE_PWM_CTRL_UPDATE BIT(5)
+#define APPLE_PWM_CTRL_TRIGGER BIT(9)
+#define APPLE_PWM_CTRL_INVERT BIT(10)
+#define APPLE_PWM_CTRL_OUTPUT_ENABLE BIT(14)
+
+struct apple_pwm {
+ void __iomem *base;
+ u64 clkrate;
+};
+
+static inline struct apple_pwm *to_apple_pwm(struct pwm_chip *chip)
+{
+ return pwmchip_get_drvdata(chip);
+}
+
+static int apple_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
+ const struct pwm_state *state)
+{
+ struct apple_pwm *fpwm;
+
+ if (state->polarity == PWM_POLARITY_INVERSED)
+ return -EINVAL;
+
+ fpwm = to_apple_pwm(chip);
+ if (state->enabled) {
+ u64 on_cycles, off_cycles;
+
+ on_cycles = mul_u64_u64_div_u64(fpwm->clkrate,
+ state->duty_cycle, NSEC_PER_SEC);
+ if (on_cycles > 0xFFFFFFFF)
+ on_cycles = 0xFFFFFFFF;
+
+ off_cycles = mul_u64_u64_div_u64(fpwm->clkrate,
+ state->period, NSEC_PER_SEC) - on_cycles;
+ if (off_cycles > 0xFFFFFFFF)
+ off_cycles = 0xFFFFFFFF;
+
+ writel(on_cycles, fpwm->base + APPLE_PWM_ON_CYCLES);
+ writel(off_cycles, fpwm->base + APPLE_PWM_OFF_CYCLES);
+ writel(APPLE_PWM_CTRL_ENABLE | APPLE_PWM_CTRL_OUTPUT_ENABLE | APPLE_PWM_CTRL_UPDATE,
+ fpwm->base + APPLE_PWM_CTRL);
+ } else {
+ writel(0, fpwm->base + APPLE_PWM_CTRL);
+ }
+ return 0;
+}
+
+static int apple_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm,
+ struct pwm_state *state)
+{
+ struct apple_pwm *fpwm;
+ u32 on_cycles, off_cycles, ctrl;
+
+ fpwm = to_apple_pwm(chip);
+
+ ctrl = readl(fpwm->base + APPLE_PWM_CTRL);
+ on_cycles = readl(fpwm->base + APPLE_PWM_ON_CYCLES);
+ off_cycles = readl(fpwm->base + APPLE_PWM_OFF_CYCLES);
+
+ state->enabled = (ctrl & APPLE_PWM_CTRL_ENABLE) && (ctrl & APPLE_PWM_CTRL_OUTPUT_ENABLE);
+ state->polarity = PWM_POLARITY_NORMAL;
+ // on_cycles + off_cycles is 33 bits, NSEC_PER_SEC is 30, there is no overflow
+ state->duty_cycle = DIV64_U64_ROUND_UP((u64)on_cycles * NSEC_PER_SEC, fpwm->clkrate);
+ state->period = DIV64_U64_ROUND_UP(((u64)off_cycles + (u64)on_cycles) *
+ NSEC_PER_SEC, fpwm->clkrate);
+
+ return 0;
+}
+
+static const struct pwm_ops apple_pwm_ops = {
+ .apply = apple_pwm_apply,
+ .get_state = apple_pwm_get_state,
+};
+
+static int apple_pwm_probe(struct platform_device *pdev)
+{
+ struct pwm_chip *chip;
+ struct apple_pwm *fpwm;
+ struct clk *clk;
+ int ret;
+
+ chip = devm_pwmchip_alloc(&pdev->dev, 1, sizeof(*fpwm));
+ if (IS_ERR(chip))
+ return PTR_ERR(chip);
+
+ fpwm = to_apple_pwm(chip);
+
+ fpwm->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(fpwm->base))
+ return PTR_ERR(fpwm->base);
+
+ clk = devm_clk_get_enabled(&pdev->dev, NULL);
+ if (IS_ERR(clk))
+ return dev_err_probe(&pdev->dev, PTR_ERR(clk), "unable to get the clock");
+
+ /*
+ * Uses the 24MHz system clock on all existing devices, can only
+ * happen if the device tree is broken
+ *
+ * This check is done to prevent an overflow in .apply
+ */
+ fpwm->clkrate = clk_get_rate(clk);
+ if (fpwm->clkrate > NSEC_PER_SEC)
+ return dev_err_probe(&pdev->dev, -EINVAL, "pwm clock out of range");
+
+ chip->ops = &apple_pwm_ops;
+
+ ret = devm_pwmchip_add(&pdev->dev, chip);
+ if (ret < 0)
+ return dev_err_probe(&pdev->dev, ret, "unable to add pwm chip");
+
+ return 0;
+}
+
+static const struct of_device_id apple_pwm_of_match[] = {
+ { .compatible = "apple,s5l-fpwm" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, apple_pwm_of_match);
+
+static struct platform_driver apple_pwm_driver = {
+ .probe = apple_pwm_probe,
+ .driver = {
+ .name = "apple-pwm",
+ .of_match_table = apple_pwm_of_match,
+ },
+};
+module_platform_driver(apple_pwm_driver);
+
+MODULE_DESCRIPTION("Apple SoC PWM driver");
+MODULE_LICENSE("Dual MIT/GPL");
diff --git a/drivers/pwm/pwm-argon-fan-hat.c b/drivers/pwm/pwm-argon-fan-hat.c
new file mode 100644
index 000000000..b480663e8
--- /dev/null
+++ b/drivers/pwm/pwm-argon-fan-hat.c
@@ -0,0 +1,109 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2025 Marek Vasut
+ *
+ * Limitations:
+ * - no support for offset/polarity
+ * - fixed 30 kHz period
+ *
+ * Argon Fan HAT https://argon40.com/products/argon-fan-hat
+ */
+
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/pwm.h>
+
+#define ARGON40_FAN_HAT_PERIOD_NS 33333 /* ~30 kHz */
+
+#define ARGON40_FAN_HAT_REG_DUTY_CYCLE 0x80
+
+static int argon_fan_hat_round_waveform_tohw(struct pwm_chip *chip,
+ struct pwm_device *pwm,
+ const struct pwm_waveform *wf,
+ void *_wfhw)
+{
+ u8 *wfhw = _wfhw;
+
+ if (wf->duty_length_ns > ARGON40_FAN_HAT_PERIOD_NS)
+ *wfhw = 100;
+ else
+ *wfhw = mul_u64_u64_div_u64(wf->duty_length_ns, 100, ARGON40_FAN_HAT_PERIOD_NS);
+
+ return 0;
+}
+
+static int argon_fan_hat_round_waveform_fromhw(struct pwm_chip *chip,
+ struct pwm_device *pwm,
+ const void *_wfhw,
+ struct pwm_waveform *wf)
+{
+ const u8 *wfhw = _wfhw;
+
+ wf->period_length_ns = ARGON40_FAN_HAT_PERIOD_NS;
+ wf->duty_length_ns = DIV64_U64_ROUND_UP(wf->period_length_ns * *wfhw, 100);
+ wf->duty_offset_ns = 0;
+
+ return 0;
+}
+
+static int argon_fan_hat_write_waveform(struct pwm_chip *chip,
+ struct pwm_device *pwm,
+ const void *_wfhw)
+{
+ struct i2c_client *i2c = pwmchip_get_drvdata(chip);
+ const u8 *wfhw = _wfhw;
+
+ return i2c_smbus_write_byte_data(i2c, ARGON40_FAN_HAT_REG_DUTY_CYCLE, *wfhw);
+}
+
+static const struct pwm_ops argon_fan_hat_pwm_ops = {
+ .sizeof_wfhw = sizeof(u8),
+ .round_waveform_fromhw = argon_fan_hat_round_waveform_fromhw,
+ .round_waveform_tohw = argon_fan_hat_round_waveform_tohw,
+ .write_waveform = argon_fan_hat_write_waveform,
+ /*
+ * The controller does not provide any way to read info back,
+ * reading from the controller stops the fan, therefore there
+ * is no .read_waveform here.
+ */
+};
+
+static int argon_fan_hat_i2c_probe(struct i2c_client *i2c)
+{
+ struct pwm_chip *chip = devm_pwmchip_alloc(&i2c->dev, 1, 0);
+ int ret;
+
+ if (IS_ERR(chip))
+ return PTR_ERR(chip);
+
+ chip->ops = &argon_fan_hat_pwm_ops;
+ pwmchip_set_drvdata(chip, i2c);
+
+ ret = devm_pwmchip_add(&i2c->dev, chip);
+ if (ret)
+ return dev_err_probe(&i2c->dev, ret, "Could not add PWM chip\n");
+
+ return 0;
+}
+
+static const struct of_device_id argon_fan_hat_dt_ids[] = {
+ { .compatible = "argon40,fan-hat" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, argon_fan_hat_dt_ids);
+
+static struct i2c_driver argon_fan_hat_driver = {
+ .driver = {
+ .name = "argon-fan-hat",
+ .probe_type = PROBE_PREFER_ASYNCHRONOUS,
+ .of_match_table = argon_fan_hat_dt_ids,
+ },
+ .probe = argon_fan_hat_i2c_probe,
+};
+
+module_i2c_driver(argon_fan_hat_driver);
+
+MODULE_AUTHOR("Marek Vasut <marek.vasut+renesas@mailbox.org>");
+MODULE_DESCRIPTION("Argon40 Fan HAT");
+MODULE_LICENSE("GPL");
diff --git a/drivers/pwm/pwm-atmel-hlcdc.c b/drivers/pwm/pwm-atmel-hlcdc.c
new file mode 100644
index 000000000..1ba2c9a04
--- /dev/null
+++ b/drivers/pwm/pwm-atmel-hlcdc.c
@@ -0,0 +1,306 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2014 Free Electrons
+ * Copyright (C) 2014 Atmel
+ *
+ * Author: Boris BREZILLON <boris.brezillon@free-electrons.com>
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/mfd/atmel-hlcdc.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+#include <linux/regmap.h>
+
+#define ATMEL_HLCDC_PWMCVAL_MASK GENMASK(15, 8)
+#define ATMEL_HLCDC_PWMCVAL(x) (((x) << 8) & ATMEL_HLCDC_PWMCVAL_MASK)
+#define ATMEL_HLCDC_PWMPOL BIT(4)
+#define ATMEL_HLCDC_PWMPS_MASK GENMASK(2, 0)
+#define ATMEL_HLCDC_PWMPS_MAX 0x6
+#define ATMEL_HLCDC_PWMPS(x) ((x) & ATMEL_HLCDC_PWMPS_MASK)
+
+struct atmel_hlcdc_pwm_errata {
+ bool slow_clk_erratum;
+ bool div1_clk_erratum;
+};
+
+struct atmel_hlcdc_pwm {
+ struct atmel_hlcdc *hlcdc;
+ struct clk *cur_clk;
+ const struct atmel_hlcdc_pwm_errata *errata;
+};
+
+static inline struct atmel_hlcdc_pwm *to_atmel_hlcdc_pwm(struct pwm_chip *chip)
+{
+ return pwmchip_get_drvdata(chip);
+}
+
+static int atmel_hlcdc_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
+ const struct pwm_state *state)
+{
+ struct atmel_hlcdc_pwm *atmel = to_atmel_hlcdc_pwm(chip);
+ struct atmel_hlcdc *hlcdc = atmel->hlcdc;
+ unsigned int status;
+ int ret;
+
+ if (state->enabled) {
+ struct clk *new_clk = hlcdc->slow_clk;
+ u64 pwmcval = state->duty_cycle * 256;
+ unsigned long clk_freq;
+ u64 clk_period_ns;
+ u32 pwmcfg;
+ int pres;
+
+ if (!atmel->errata || !atmel->errata->slow_clk_erratum) {
+ clk_freq = clk_get_rate(new_clk);
+ if (!clk_freq)
+ return -EINVAL;
+
+ clk_period_ns = (u64)NSEC_PER_SEC * 256;
+ do_div(clk_period_ns, clk_freq);
+ }
+
+ /* Errata: cannot use slow clk on some IP revisions */
+ if ((atmel->errata && atmel->errata->slow_clk_erratum) ||
+ clk_period_ns > state->period) {
+ new_clk = hlcdc->sys_clk;
+ clk_freq = clk_get_rate(new_clk);
+ if (!clk_freq)
+ return -EINVAL;
+
+ clk_period_ns = (u64)NSEC_PER_SEC * 256;
+ do_div(clk_period_ns, clk_freq);
+ }
+
+ for (pres = 0; pres <= ATMEL_HLCDC_PWMPS_MAX; pres++) {
+ /* Errata: cannot divide by 1 on some IP revisions */
+ if (!pres && atmel->errata &&
+ atmel->errata->div1_clk_erratum)
+ continue;
+
+ if ((clk_period_ns << pres) >= state->period)
+ break;
+ }
+
+ if (pres > ATMEL_HLCDC_PWMPS_MAX)
+ return -EINVAL;
+
+ pwmcfg = ATMEL_HLCDC_PWMPS(pres);
+
+ if (new_clk != atmel->cur_clk) {
+ u32 gencfg = 0;
+ int ret;
+
+ ret = clk_prepare_enable(new_clk);
+ if (ret)
+ return ret;
+
+ clk_disable_unprepare(atmel->cur_clk);
+ atmel->cur_clk = new_clk;
+
+ if (new_clk == hlcdc->sys_clk)
+ gencfg = ATMEL_HLCDC_CLKPWMSEL;
+
+ ret = regmap_update_bits(hlcdc->regmap,
+ ATMEL_HLCDC_CFG(0),
+ ATMEL_HLCDC_CLKPWMSEL,
+ gencfg);
+ if (ret)
+ return ret;
+ }
+
+ do_div(pwmcval, state->period);
+
+ /*
+ * The PWM duty cycle is configurable from 0/256 to 255/256 of
+ * the period cycle. Hence we can't set a duty cycle occupying
+ * the whole period cycle if we're asked to.
+ * Set it to 255 if pwmcval is greater than 256.
+ */
+ if (pwmcval > 255)
+ pwmcval = 255;
+
+ pwmcfg |= ATMEL_HLCDC_PWMCVAL(pwmcval);
+
+ if (state->polarity == PWM_POLARITY_NORMAL)
+ pwmcfg |= ATMEL_HLCDC_PWMPOL;
+
+ ret = regmap_update_bits(hlcdc->regmap, ATMEL_HLCDC_CFG(6),
+ ATMEL_HLCDC_PWMCVAL_MASK |
+ ATMEL_HLCDC_PWMPS_MASK |
+ ATMEL_HLCDC_PWMPOL,
+ pwmcfg);
+ if (ret)
+ return ret;
+
+ ret = regmap_write(hlcdc->regmap, ATMEL_HLCDC_EN,
+ ATMEL_HLCDC_PWM);
+ if (ret)
+ return ret;
+
+ ret = regmap_read_poll_timeout(hlcdc->regmap, ATMEL_HLCDC_SR,
+ status,
+ status & ATMEL_HLCDC_PWM,
+ 10, 0);
+ if (ret)
+ return ret;
+ } else {
+ ret = regmap_write(hlcdc->regmap, ATMEL_HLCDC_DIS,
+ ATMEL_HLCDC_PWM);
+ if (ret)
+ return ret;
+
+ ret = regmap_read_poll_timeout(hlcdc->regmap, ATMEL_HLCDC_SR,
+ status,
+ !(status & ATMEL_HLCDC_PWM),
+ 10, 0);
+ if (ret)
+ return ret;
+
+ clk_disable_unprepare(atmel->cur_clk);
+ atmel->cur_clk = NULL;
+ }
+
+ return 0;
+}
+
+static const struct pwm_ops atmel_hlcdc_pwm_ops = {
+ .apply = atmel_hlcdc_pwm_apply,
+};
+
+static const struct atmel_hlcdc_pwm_errata atmel_hlcdc_pwm_at91sam9x5_errata = {
+ .slow_clk_erratum = true,
+};
+
+static const struct atmel_hlcdc_pwm_errata atmel_hlcdc_pwm_sama5d3_errata = {
+ .div1_clk_erratum = true,
+};
+
+static int atmel_hlcdc_pwm_suspend(struct device *dev)
+{
+ struct pwm_chip *chip = dev_get_drvdata(dev);
+ struct atmel_hlcdc_pwm *atmel = to_atmel_hlcdc_pwm(chip);
+ struct pwm_device *pwm = &chip->pwms[0];
+
+ /* Keep the periph clock enabled if the PWM is still running. */
+ if (!pwm->state.enabled)
+ clk_disable_unprepare(atmel->hlcdc->periph_clk);
+
+ return 0;
+}
+
+static int atmel_hlcdc_pwm_resume(struct device *dev)
+{
+ struct pwm_chip *chip = dev_get_drvdata(dev);
+ struct atmel_hlcdc_pwm *atmel = to_atmel_hlcdc_pwm(chip);
+ struct pwm_device *pwm = &chip->pwms[0];
+ int ret;
+
+ /* Re-enable the periph clock it was stopped during suspend. */
+ if (!pwm->state.enabled) {
+ ret = clk_prepare_enable(atmel->hlcdc->periph_clk);
+ if (ret)
+ return ret;
+ }
+
+ return atmel_hlcdc_pwm_apply(chip, pwm, &pwm->state);
+}
+
+static DEFINE_SIMPLE_DEV_PM_OPS(atmel_hlcdc_pwm_pm_ops,
+ atmel_hlcdc_pwm_suspend, atmel_hlcdc_pwm_resume);
+
+static const struct of_device_id atmel_hlcdc_dt_ids[] = {
+ {
+ .compatible = "atmel,at91sam9n12-hlcdc",
+ /* 9n12 has same errata as 9x5 HLCDC PWM */
+ .data = &atmel_hlcdc_pwm_at91sam9x5_errata,
+ }, {
+ .compatible = "atmel,at91sam9x5-hlcdc",
+ .data = &atmel_hlcdc_pwm_at91sam9x5_errata,
+ }, {
+ .compatible = "atmel,sama5d2-hlcdc",
+ }, {
+ .compatible = "atmel,sama5d3-hlcdc",
+ .data = &atmel_hlcdc_pwm_sama5d3_errata,
+ }, {
+ .compatible = "atmel,sama5d4-hlcdc",
+ .data = &atmel_hlcdc_pwm_sama5d3_errata,
+ }, {
+ .compatible = "microchip,sam9x60-hlcdc",
+ },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, atmel_hlcdc_dt_ids);
+
+static int atmel_hlcdc_pwm_probe(struct platform_device *pdev)
+{
+ const struct of_device_id *match;
+ struct device *dev = &pdev->dev;
+ struct pwm_chip *chip;
+ struct atmel_hlcdc_pwm *atmel;
+ struct atmel_hlcdc *hlcdc;
+ int ret;
+
+ hlcdc = dev_get_drvdata(dev->parent);
+
+ chip = devm_pwmchip_alloc(dev, 1, sizeof(*atmel));
+ if (IS_ERR(chip))
+ return PTR_ERR(chip);
+ atmel = to_atmel_hlcdc_pwm(chip);
+
+ ret = clk_prepare_enable(hlcdc->periph_clk);
+ if (ret)
+ return ret;
+
+ match = of_match_node(atmel_hlcdc_dt_ids, dev->parent->of_node);
+ if (match)
+ atmel->errata = match->data;
+
+ atmel->hlcdc = hlcdc;
+ chip->ops = &atmel_hlcdc_pwm_ops;
+
+ ret = pwmchip_add(chip);
+ if (ret) {
+ clk_disable_unprepare(hlcdc->periph_clk);
+ return ret;
+ }
+
+ platform_set_drvdata(pdev, chip);
+
+ return 0;
+}
+
+static void atmel_hlcdc_pwm_remove(struct platform_device *pdev)
+{
+ struct pwm_chip *chip = platform_get_drvdata(pdev);
+ struct atmel_hlcdc_pwm *atmel = to_atmel_hlcdc_pwm(chip);
+
+ pwmchip_remove(chip);
+
+ clk_disable_unprepare(atmel->hlcdc->periph_clk);
+}
+
+static const struct of_device_id atmel_hlcdc_pwm_dt_ids[] = {
+ { .compatible = "atmel,hlcdc-pwm" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, atmel_hlcdc_pwm_dt_ids);
+
+static struct platform_driver atmel_hlcdc_pwm_driver = {
+ .driver = {
+ .name = "atmel-hlcdc-pwm",
+ .of_match_table = atmel_hlcdc_pwm_dt_ids,
+ .pm = pm_ptr(&atmel_hlcdc_pwm_pm_ops),
+ },
+ .probe = atmel_hlcdc_pwm_probe,
+ .remove = atmel_hlcdc_pwm_remove,
+};
+module_platform_driver(atmel_hlcdc_pwm_driver);
+
+MODULE_ALIAS("platform:atmel-hlcdc-pwm");
+MODULE_AUTHOR("Boris Brezillon <boris.brezillon@free-electrons.com>");
+MODULE_DESCRIPTION("Atmel HLCDC PWM driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/pwm/pwm-atmel-tcb.c b/drivers/pwm/pwm-atmel-tcb.c
new file mode 100644
index 000000000..f74a42d75
--- /dev/null
+++ b/drivers/pwm/pwm-atmel-tcb.c
@@ -0,0 +1,566 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) Overkiz SAS 2012
+ *
+ * Author: Boris BREZILLON <b.brezillon@overkiz.com>
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/clocksource.h>
+#include <linux/clockchips.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/ioport.h>
+#include <linux/io.h>
+#include <linux/mfd/syscon.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <soc/at91/atmel_tcb.h>
+
+#define NPWM 2
+
+#define ATMEL_TC_ACMR_MASK (ATMEL_TC_ACPA | ATMEL_TC_ACPC | \
+ ATMEL_TC_AEEVT | ATMEL_TC_ASWTRG)
+
+#define ATMEL_TC_BCMR_MASK (ATMEL_TC_BCPB | ATMEL_TC_BCPC | \
+ ATMEL_TC_BEEVT | ATMEL_TC_BSWTRG)
+
+struct atmel_tcb_pwm_device {
+ unsigned div; /* PWM clock divider */
+ unsigned duty; /* PWM duty expressed in clk cycles */
+ unsigned period; /* PWM period expressed in clk cycles */
+};
+
+struct atmel_tcb_channel {
+ u32 enabled;
+ u32 cmr;
+ u32 ra;
+ u32 rb;
+ u32 rc;
+};
+
+struct atmel_tcb_pwm_chip {
+ spinlock_t lock;
+ u8 channel;
+ u8 width;
+ unsigned long rate;
+ unsigned long slow_rate;
+ struct regmap *regmap;
+ struct clk *clk;
+ struct clk *gclk;
+ struct clk *slow_clk;
+ struct atmel_tcb_pwm_device pwms[NPWM];
+ struct atmel_tcb_channel bkup;
+};
+
+static const u8 atmel_tcb_divisors[] = { 2, 8, 32, 128, 0, };
+
+static inline struct atmel_tcb_pwm_chip *to_tcb_chip(struct pwm_chip *chip)
+{
+ return pwmchip_get_drvdata(chip);
+}
+
+static int atmel_tcb_pwm_request(struct pwm_chip *chip,
+ struct pwm_device *pwm)
+{
+ struct atmel_tcb_pwm_chip *tcbpwmc = to_tcb_chip(chip);
+ struct atmel_tcb_pwm_device *tcbpwm = &tcbpwmc->pwms[pwm->hwpwm];
+ unsigned cmr;
+ int ret;
+
+ ret = clk_prepare_enable(tcbpwmc->clk);
+ if (ret)
+ return ret;
+
+ tcbpwm->duty = 0;
+ tcbpwm->period = 0;
+ tcbpwm->div = 0;
+
+ guard(spinlock)(&tcbpwmc->lock);
+
+ regmap_read(tcbpwmc->regmap, ATMEL_TC_REG(tcbpwmc->channel, CMR), &cmr);
+ /*
+ * Get init config from Timer Counter registers if
+ * Timer Counter is already configured as a PWM generator.
+ */
+ if (cmr & ATMEL_TC_WAVE) {
+ if (pwm->hwpwm == 0)
+ regmap_read(tcbpwmc->regmap,
+ ATMEL_TC_REG(tcbpwmc->channel, RA),
+ &tcbpwm->duty);
+ else
+ regmap_read(tcbpwmc->regmap,
+ ATMEL_TC_REG(tcbpwmc->channel, RB),
+ &tcbpwm->duty);
+
+ tcbpwm->div = cmr & ATMEL_TC_TCCLKS;
+ regmap_read(tcbpwmc->regmap, ATMEL_TC_REG(tcbpwmc->channel, RC),
+ &tcbpwm->period);
+ cmr &= (ATMEL_TC_TCCLKS | ATMEL_TC_ACMR_MASK |
+ ATMEL_TC_BCMR_MASK);
+ } else
+ cmr = 0;
+
+ cmr |= ATMEL_TC_WAVE | ATMEL_TC_WAVESEL_UP_AUTO | ATMEL_TC_EEVT_XC0;
+ regmap_write(tcbpwmc->regmap, ATMEL_TC_REG(tcbpwmc->channel, CMR), cmr);
+
+ return 0;
+}
+
+static void atmel_tcb_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct atmel_tcb_pwm_chip *tcbpwmc = to_tcb_chip(chip);
+
+ clk_disable_unprepare(tcbpwmc->clk);
+}
+
+static void atmel_tcb_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm,
+ enum pwm_polarity polarity)
+{
+ struct atmel_tcb_pwm_chip *tcbpwmc = to_tcb_chip(chip);
+ struct atmel_tcb_pwm_device *tcbpwm = &tcbpwmc->pwms[pwm->hwpwm];
+ unsigned cmr;
+
+ /*
+ * If duty is 0 the timer will be stopped and we have to
+ * configure the output correctly on software trigger:
+ * - set output to high if PWM_POLARITY_INVERSED
+ * - set output to low if PWM_POLARITY_NORMAL
+ *
+ * This is why we're reverting polarity in this case.
+ */
+ if (tcbpwm->duty == 0)
+ polarity = !polarity;
+
+ regmap_read(tcbpwmc->regmap, ATMEL_TC_REG(tcbpwmc->channel, CMR), &cmr);
+
+ /* flush old setting and set the new one */
+ if (pwm->hwpwm == 0) {
+ cmr &= ~ATMEL_TC_ACMR_MASK;
+ if (polarity == PWM_POLARITY_INVERSED)
+ cmr |= ATMEL_TC_ASWTRG_CLEAR;
+ else
+ cmr |= ATMEL_TC_ASWTRG_SET;
+ } else {
+ cmr &= ~ATMEL_TC_BCMR_MASK;
+ if (polarity == PWM_POLARITY_INVERSED)
+ cmr |= ATMEL_TC_BSWTRG_CLEAR;
+ else
+ cmr |= ATMEL_TC_BSWTRG_SET;
+ }
+
+ regmap_write(tcbpwmc->regmap, ATMEL_TC_REG(tcbpwmc->channel, CMR), cmr);
+
+ /*
+ * Use software trigger to apply the new setting.
+ * If both PWM devices in this group are disabled we stop the clock.
+ */
+ if (!(cmr & (ATMEL_TC_ACPC | ATMEL_TC_BCPC))) {
+ regmap_write(tcbpwmc->regmap,
+ ATMEL_TC_REG(tcbpwmc->channel, CCR),
+ ATMEL_TC_SWTRG | ATMEL_TC_CLKDIS);
+ tcbpwmc->bkup.enabled = 1;
+ } else {
+ regmap_write(tcbpwmc->regmap,
+ ATMEL_TC_REG(tcbpwmc->channel, CCR),
+ ATMEL_TC_SWTRG);
+ tcbpwmc->bkup.enabled = 0;
+ }
+}
+
+static int atmel_tcb_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm,
+ enum pwm_polarity polarity)
+{
+ struct atmel_tcb_pwm_chip *tcbpwmc = to_tcb_chip(chip);
+ struct atmel_tcb_pwm_device *tcbpwm = &tcbpwmc->pwms[pwm->hwpwm];
+ u32 cmr;
+
+ /*
+ * If duty is 0 the timer will be stopped and we have to
+ * configure the output correctly on software trigger:
+ * - set output to high if PWM_POLARITY_INVERSED
+ * - set output to low if PWM_POLARITY_NORMAL
+ *
+ * This is why we're reverting polarity in this case.
+ */
+ if (tcbpwm->duty == 0)
+ polarity = !polarity;
+
+ regmap_read(tcbpwmc->regmap, ATMEL_TC_REG(tcbpwmc->channel, CMR), &cmr);
+
+ /* flush old setting and set the new one */
+ cmr &= ~ATMEL_TC_TCCLKS;
+
+ if (pwm->hwpwm == 0) {
+ cmr &= ~ATMEL_TC_ACMR_MASK;
+
+ /* Set CMR flags according to given polarity */
+ if (polarity == PWM_POLARITY_INVERSED)
+ cmr |= ATMEL_TC_ASWTRG_CLEAR;
+ else
+ cmr |= ATMEL_TC_ASWTRG_SET;
+ } else {
+ cmr &= ~ATMEL_TC_BCMR_MASK;
+ if (polarity == PWM_POLARITY_INVERSED)
+ cmr |= ATMEL_TC_BSWTRG_CLEAR;
+ else
+ cmr |= ATMEL_TC_BSWTRG_SET;
+ }
+
+ /*
+ * If duty is 0 or equal to period there's no need to register
+ * a specific action on RA/RB and RC compare.
+ * The output will be configured on software trigger and keep
+ * this config till next config call.
+ */
+ if (tcbpwm->duty != tcbpwm->period && tcbpwm->duty > 0) {
+ if (pwm->hwpwm == 0) {
+ if (polarity == PWM_POLARITY_INVERSED)
+ cmr |= ATMEL_TC_ACPA_SET | ATMEL_TC_ACPC_CLEAR;
+ else
+ cmr |= ATMEL_TC_ACPA_CLEAR | ATMEL_TC_ACPC_SET;
+ } else {
+ if (polarity == PWM_POLARITY_INVERSED)
+ cmr |= ATMEL_TC_BCPB_SET | ATMEL_TC_BCPC_CLEAR;
+ else
+ cmr |= ATMEL_TC_BCPB_CLEAR | ATMEL_TC_BCPC_SET;
+ }
+ }
+
+ cmr |= (tcbpwm->div & ATMEL_TC_TCCLKS);
+
+ regmap_write(tcbpwmc->regmap, ATMEL_TC_REG(tcbpwmc->channel, CMR), cmr);
+
+ if (pwm->hwpwm == 0)
+ regmap_write(tcbpwmc->regmap,
+ ATMEL_TC_REG(tcbpwmc->channel, RA),
+ tcbpwm->duty);
+ else
+ regmap_write(tcbpwmc->regmap,
+ ATMEL_TC_REG(tcbpwmc->channel, RB),
+ tcbpwm->duty);
+
+ regmap_write(tcbpwmc->regmap, ATMEL_TC_REG(tcbpwmc->channel, RC),
+ tcbpwm->period);
+
+ /* Use software trigger to apply the new setting */
+ regmap_write(tcbpwmc->regmap, ATMEL_TC_REG(tcbpwmc->channel, CCR),
+ ATMEL_TC_SWTRG | ATMEL_TC_CLKEN);
+ tcbpwmc->bkup.enabled = 1;
+ return 0;
+}
+
+static int atmel_tcb_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
+ int duty_ns, int period_ns)
+{
+ struct atmel_tcb_pwm_chip *tcbpwmc = to_tcb_chip(chip);
+ struct atmel_tcb_pwm_device *tcbpwm = &tcbpwmc->pwms[pwm->hwpwm];
+ /* companion PWM sharing register values period and div */
+ struct atmel_tcb_pwm_device *atcbpwm = &tcbpwmc->pwms[pwm->hwpwm ^ 1];
+ int i = 0;
+ int slowclk = 0;
+ unsigned period;
+ unsigned duty;
+ unsigned long rate = tcbpwmc->rate;
+ unsigned long long min;
+ unsigned long long max;
+
+ /*
+ * Find best clk divisor:
+ * the smallest divisor which can fulfill the period_ns requirements.
+ * If there is a gclk, the first divisor is actually the gclk selector
+ */
+ if (tcbpwmc->gclk)
+ i = 1;
+ for (; i < ARRAY_SIZE(atmel_tcb_divisors); ++i) {
+ if (atmel_tcb_divisors[i] == 0) {
+ slowclk = i;
+ continue;
+ }
+ min = div_u64((u64)NSEC_PER_SEC * atmel_tcb_divisors[i], rate);
+ max = min << tcbpwmc->width;
+ if (max >= period_ns)
+ break;
+ }
+
+ /*
+ * If none of the divisor are small enough to represent period_ns
+ * take slow clock (32KHz).
+ */
+ if (i == ARRAY_SIZE(atmel_tcb_divisors)) {
+ i = slowclk;
+ rate = tcbpwmc->slow_rate;
+ min = div_u64(NSEC_PER_SEC, rate);
+ max = min << tcbpwmc->width;
+
+ /* If period is too big return ERANGE error */
+ if (max < period_ns)
+ return -ERANGE;
+ }
+
+ duty = div_u64(duty_ns, min);
+ period = div_u64(period_ns, min);
+
+ /*
+ * PWM devices provided by the TCB driver are grouped by 2.
+ * PWM devices in a given group must be configured with the
+ * same period_ns.
+ *
+ * We're checking the period value of the second PWM device
+ * in this group before applying the new config.
+ */
+ if ((atcbpwm->duty > 0 && atcbpwm->duty != atcbpwm->period) &&
+ (atcbpwm->div != i || atcbpwm->period != period)) {
+ dev_err(pwmchip_parent(chip),
+ "failed to configure period_ns: PWM group already configured with a different value\n");
+ return -EINVAL;
+ }
+
+ tcbpwm->period = period;
+ tcbpwm->div = i;
+ tcbpwm->duty = duty;
+
+ return 0;
+}
+
+static int atmel_tcb_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
+ const struct pwm_state *state)
+{
+ struct atmel_tcb_pwm_chip *tcbpwmc = to_tcb_chip(chip);
+ int duty_cycle, period;
+ int ret;
+
+ guard(spinlock)(&tcbpwmc->lock);
+
+ if (!state->enabled) {
+ atmel_tcb_pwm_disable(chip, pwm, state->polarity);
+ return 0;
+ }
+
+ period = min(state->period, INT_MAX);
+ duty_cycle = min(state->duty_cycle, INT_MAX);
+
+ ret = atmel_tcb_pwm_config(chip, pwm, duty_cycle, period);
+ if (ret)
+ return ret;
+
+ return atmel_tcb_pwm_enable(chip, pwm, state->polarity);
+}
+
+static const struct pwm_ops atmel_tcb_pwm_ops = {
+ .request = atmel_tcb_pwm_request,
+ .free = atmel_tcb_pwm_free,
+ .apply = atmel_tcb_pwm_apply,
+};
+
+static struct atmel_tcb_config tcb_rm9200_config = {
+ .counter_width = 16,
+};
+
+static struct atmel_tcb_config tcb_sam9x5_config = {
+ .counter_width = 32,
+};
+
+static struct atmel_tcb_config tcb_sama5d2_config = {
+ .counter_width = 32,
+ .has_gclk = 1,
+};
+
+static const struct of_device_id atmel_tcb_of_match[] = {
+ { .compatible = "atmel,at91rm9200-tcb", .data = &tcb_rm9200_config },
+ { .compatible = "atmel,at91sam9x5-tcb", .data = &tcb_sam9x5_config },
+ { .compatible = "atmel,sama5d2-tcb", .data = &tcb_sama5d2_config },
+ { /* sentinel */ }
+};
+
+static int atmel_tcb_pwm_probe(struct platform_device *pdev)
+{
+ struct pwm_chip *chip;
+ const struct of_device_id *match;
+ struct atmel_tcb_pwm_chip *tcbpwmc;
+ const struct atmel_tcb_config *config;
+ struct device_node *np = pdev->dev.of_node;
+ char clk_name[] = "t0_clk";
+ int err;
+ int channel;
+
+ chip = devm_pwmchip_alloc(&pdev->dev, NPWM, sizeof(*tcbpwmc));
+ if (IS_ERR(chip))
+ return PTR_ERR(chip);
+ tcbpwmc = to_tcb_chip(chip);
+
+ err = of_property_read_u32(np, "reg", &channel);
+ if (err < 0) {
+ dev_err(&pdev->dev,
+ "failed to get Timer Counter Block channel from device tree (error: %d)\n",
+ err);
+ return err;
+ }
+
+ tcbpwmc->regmap = syscon_node_to_regmap(np->parent);
+ if (IS_ERR(tcbpwmc->regmap))
+ return PTR_ERR(tcbpwmc->regmap);
+
+ tcbpwmc->slow_clk = of_clk_get_by_name(np->parent, "slow_clk");
+ if (IS_ERR(tcbpwmc->slow_clk))
+ return PTR_ERR(tcbpwmc->slow_clk);
+
+ clk_name[1] += channel;
+ tcbpwmc->clk = of_clk_get_by_name(np->parent, clk_name);
+ if (IS_ERR(tcbpwmc->clk))
+ tcbpwmc->clk = of_clk_get_by_name(np->parent, "t0_clk");
+ if (IS_ERR(tcbpwmc->clk)) {
+ err = PTR_ERR(tcbpwmc->clk);
+ goto err_slow_clk;
+ }
+
+ match = of_match_node(atmel_tcb_of_match, np->parent);
+ config = match->data;
+
+ if (config->has_gclk) {
+ tcbpwmc->gclk = of_clk_get_by_name(np->parent, "gclk");
+ if (IS_ERR(tcbpwmc->gclk)) {
+ err = PTR_ERR(tcbpwmc->gclk);
+ goto err_clk;
+ }
+ }
+
+ chip->ops = &atmel_tcb_pwm_ops;
+ chip->atomic = true;
+ tcbpwmc->channel = channel;
+ tcbpwmc->width = config->counter_width;
+
+ err = clk_prepare_enable(tcbpwmc->clk);
+ if (err)
+ goto err_gclk;
+
+ err = clk_prepare_enable(tcbpwmc->slow_clk);
+ if (err)
+ goto err_disable_clk;
+
+ err = clk_rate_exclusive_get(tcbpwmc->clk);
+ if (err)
+ goto err_disable_slow_clk;
+
+ err = clk_rate_exclusive_get(tcbpwmc->slow_clk);
+ if (err)
+ goto err_clk_unlock;
+
+ tcbpwmc->rate = clk_get_rate(tcbpwmc->clk);
+ tcbpwmc->slow_rate = clk_get_rate(tcbpwmc->slow_clk);
+
+ spin_lock_init(&tcbpwmc->lock);
+
+ err = pwmchip_add(chip);
+ if (err < 0)
+ goto err_slow_clk_unlock;
+
+ platform_set_drvdata(pdev, chip);
+
+ return 0;
+
+err_slow_clk_unlock:
+ clk_rate_exclusive_put(tcbpwmc->slow_clk);
+
+err_clk_unlock:
+ clk_rate_exclusive_put(tcbpwmc->clk);
+
+err_disable_clk:
+ clk_disable_unprepare(tcbpwmc->clk);
+
+err_disable_slow_clk:
+ clk_disable_unprepare(tcbpwmc->slow_clk);
+
+err_gclk:
+ clk_put(tcbpwmc->gclk);
+
+err_clk:
+ clk_put(tcbpwmc->clk);
+
+err_slow_clk:
+ clk_put(tcbpwmc->slow_clk);
+
+ return err;
+}
+
+static void atmel_tcb_pwm_remove(struct platform_device *pdev)
+{
+ struct pwm_chip *chip = platform_get_drvdata(pdev);
+ struct atmel_tcb_pwm_chip *tcbpwmc = to_tcb_chip(chip);
+
+ pwmchip_remove(chip);
+
+ clk_rate_exclusive_put(tcbpwmc->slow_clk);
+ clk_rate_exclusive_put(tcbpwmc->clk);
+ clk_disable_unprepare(tcbpwmc->clk);
+ clk_disable_unprepare(tcbpwmc->slow_clk);
+ clk_put(tcbpwmc->gclk);
+ clk_put(tcbpwmc->clk);
+ clk_put(tcbpwmc->slow_clk);
+}
+
+static const struct of_device_id atmel_tcb_pwm_dt_ids[] = {
+ { .compatible = "atmel,tcb-pwm", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, atmel_tcb_pwm_dt_ids);
+
+static int atmel_tcb_pwm_suspend(struct device *dev)
+{
+ struct pwm_chip *chip = dev_get_drvdata(dev);
+ struct atmel_tcb_pwm_chip *tcbpwmc = to_tcb_chip(chip);
+ struct atmel_tcb_channel *chan = &tcbpwmc->bkup;
+ unsigned int channel = tcbpwmc->channel;
+
+ regmap_read(tcbpwmc->regmap, ATMEL_TC_REG(channel, CMR), &chan->cmr);
+ regmap_read(tcbpwmc->regmap, ATMEL_TC_REG(channel, RA), &chan->ra);
+ regmap_read(tcbpwmc->regmap, ATMEL_TC_REG(channel, RB), &chan->rb);
+ regmap_read(tcbpwmc->regmap, ATMEL_TC_REG(channel, RC), &chan->rc);
+
+ return 0;
+}
+
+static int atmel_tcb_pwm_resume(struct device *dev)
+{
+ struct pwm_chip *chip = dev_get_drvdata(dev);
+ struct atmel_tcb_pwm_chip *tcbpwmc = to_tcb_chip(chip);
+ struct atmel_tcb_channel *chan = &tcbpwmc->bkup;
+ unsigned int channel = tcbpwmc->channel;
+
+ regmap_write(tcbpwmc->regmap, ATMEL_TC_REG(channel, CMR), chan->cmr);
+ regmap_write(tcbpwmc->regmap, ATMEL_TC_REG(channel, RA), chan->ra);
+ regmap_write(tcbpwmc->regmap, ATMEL_TC_REG(channel, RB), chan->rb);
+ regmap_write(tcbpwmc->regmap, ATMEL_TC_REG(channel, RC), chan->rc);
+
+ if (chan->enabled)
+ regmap_write(tcbpwmc->regmap,
+ ATMEL_TC_CLKEN | ATMEL_TC_SWTRG,
+ ATMEL_TC_REG(channel, CCR));
+
+ return 0;
+}
+
+static DEFINE_SIMPLE_DEV_PM_OPS(atmel_tcb_pwm_pm_ops, atmel_tcb_pwm_suspend,
+ atmel_tcb_pwm_resume);
+
+static struct platform_driver atmel_tcb_pwm_driver = {
+ .driver = {
+ .name = "atmel-tcb-pwm",
+ .of_match_table = atmel_tcb_pwm_dt_ids,
+ .pm = pm_ptr(&atmel_tcb_pwm_pm_ops),
+ },
+ .probe = atmel_tcb_pwm_probe,
+ .remove = atmel_tcb_pwm_remove,
+};
+module_platform_driver(atmel_tcb_pwm_driver);
+
+MODULE_AUTHOR("Boris BREZILLON <b.brezillon@overkiz.com>");
+MODULE_DESCRIPTION("Atmel Timer Counter Pulse Width Modulation Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/pwm/pwm-atmel.c b/drivers/pwm/pwm-atmel.c
new file mode 100644
index 000000000..86918523d
--- /dev/null
+++ b/drivers/pwm/pwm-atmel.c
@@ -0,0 +1,542 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Driver for Atmel Pulse Width Modulation Controller
+ *
+ * Copyright (C) 2013 Atmel Corporation
+ * Bo Shen <voice.shen@atmel.com>
+ *
+ * Links to reference manuals for the supported PWM chips can be found in
+ * Documentation/arch/arm/microchip.rst.
+ *
+ * Limitations:
+ * - Periods start with the inactive level.
+ * - Hardware has to be stopped in general to update settings.
+ *
+ * Software bugs/possible improvements:
+ * - When atmel_pwm_apply() is called with state->enabled=false a change in
+ * state->polarity isn't honored.
+ * - Instead of sleeping to wait for a completed period, the interrupt
+ * functionality could be used.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+#include <linux/slab.h>
+
+/* The following is global registers for PWM controller */
+#define PWM_ENA 0x04
+#define PWM_DIS 0x08
+#define PWM_SR 0x0C
+#define PWM_ISR 0x1C
+/* Bit field in SR */
+#define PWM_SR_ALL_CH_MASK 0x0F
+
+/* The following register is PWM channel related registers */
+#define PWM_CH_REG_OFFSET 0x200
+#define PWM_CH_REG_SIZE 0x20
+
+#define PWM_CMR 0x0
+/* Bit field in CMR */
+#define PWM_CMR_CPOL (1 << 9)
+#define PWM_CMR_UPD_CDTY (1 << 10)
+#define PWM_CMR_CPRE_MSK 0xF
+
+/* The following registers for PWM v1 */
+#define PWMV1_CDTY 0x04
+#define PWMV1_CPRD 0x08
+#define PWMV1_CUPD 0x10
+
+/* The following registers for PWM v2 */
+#define PWMV2_CDTY 0x04
+#define PWMV2_CDTYUPD 0x08
+#define PWMV2_CPRD 0x0C
+#define PWMV2_CPRDUPD 0x10
+
+#define PWM_MAX_PRES 10
+
+struct atmel_pwm_registers {
+ u8 period;
+ u8 period_upd;
+ u8 duty;
+ u8 duty_upd;
+};
+
+struct atmel_pwm_config {
+ u32 period_bits;
+};
+
+struct atmel_pwm_data {
+ struct atmel_pwm_registers regs;
+ struct atmel_pwm_config cfg;
+};
+
+struct atmel_pwm_chip {
+ struct clk *clk;
+ void __iomem *base;
+ const struct atmel_pwm_data *data;
+
+ /*
+ * The hardware supports a mechanism to update a channel's duty cycle at
+ * the end of the currently running period. When such an update is
+ * pending we delay disabling the PWM until the new configuration is
+ * active because otherwise pmw_config(duty_cycle=0); pwm_disable();
+ * might not result in an inactive output.
+ * This bitmask tracks for which channels an update is pending in
+ * hardware.
+ */
+ u32 update_pending;
+};
+
+static inline struct atmel_pwm_chip *to_atmel_pwm_chip(struct pwm_chip *chip)
+{
+ return pwmchip_get_drvdata(chip);
+}
+
+static inline u32 atmel_pwm_readl(struct atmel_pwm_chip *chip,
+ unsigned long offset)
+{
+ return readl_relaxed(chip->base + offset);
+}
+
+static inline void atmel_pwm_writel(struct atmel_pwm_chip *chip,
+ unsigned long offset, unsigned long val)
+{
+ writel_relaxed(val, chip->base + offset);
+}
+
+static inline u32 atmel_pwm_ch_readl(struct atmel_pwm_chip *chip,
+ unsigned int ch, unsigned long offset)
+{
+ unsigned long base = PWM_CH_REG_OFFSET + ch * PWM_CH_REG_SIZE;
+
+ return atmel_pwm_readl(chip, base + offset);
+}
+
+static inline void atmel_pwm_ch_writel(struct atmel_pwm_chip *chip,
+ unsigned int ch, unsigned long offset,
+ unsigned long val)
+{
+ unsigned long base = PWM_CH_REG_OFFSET + ch * PWM_CH_REG_SIZE;
+
+ atmel_pwm_writel(chip, base + offset, val);
+}
+
+static void atmel_pwm_update_pending(struct atmel_pwm_chip *chip)
+{
+ /*
+ * Each channel that has its bit in ISR set started a new period since
+ * ISR was cleared and so there is no more update pending. Note that
+ * reading ISR clears it, so this needs to handle all channels to not
+ * loose information.
+ */
+ u32 isr = atmel_pwm_readl(chip, PWM_ISR);
+
+ chip->update_pending &= ~isr;
+}
+
+static void atmel_pwm_set_pending(struct atmel_pwm_chip *chip, unsigned int ch)
+{
+ /*
+ * Clear pending flags in hardware because otherwise there might still
+ * be a stale flag in ISR.
+ */
+ atmel_pwm_update_pending(chip);
+
+ chip->update_pending |= (1 << ch);
+}
+
+static int atmel_pwm_test_pending(struct atmel_pwm_chip *chip, unsigned int ch)
+{
+ int ret = 0;
+
+ if (chip->update_pending & (1 << ch)) {
+ atmel_pwm_update_pending(chip);
+
+ if (chip->update_pending & (1 << ch))
+ ret = 1;
+ }
+
+ return ret;
+}
+
+static int atmel_pwm_wait_nonpending(struct atmel_pwm_chip *chip, unsigned int ch)
+{
+ unsigned long timeout = jiffies + 2 * HZ;
+ int ret;
+
+ while ((ret = atmel_pwm_test_pending(chip, ch)) &&
+ time_before(jiffies, timeout))
+ usleep_range(10, 100);
+
+ return ret ? -ETIMEDOUT : 0;
+}
+
+static int atmel_pwm_calculate_cprd_and_pres(struct pwm_chip *chip,
+ unsigned long clkrate,
+ const struct pwm_state *state,
+ unsigned long *cprd, u32 *pres)
+{
+ struct atmel_pwm_chip *atmel_pwm = to_atmel_pwm_chip(chip);
+ unsigned long long cycles = state->period;
+ int shift;
+
+ /* Calculate the period cycles and prescale value */
+ cycles *= clkrate;
+ do_div(cycles, NSEC_PER_SEC);
+
+ /*
+ * The register for the period length is cfg.period_bits bits wide.
+ * So for each bit the number of clock cycles is wider divide the input
+ * clock frequency by two using pres and shift cprd accordingly.
+ */
+ shift = fls(cycles) - atmel_pwm->data->cfg.period_bits;
+
+ if (shift > PWM_MAX_PRES) {
+ dev_err(pwmchip_parent(chip), "pres exceeds the maximum value\n");
+ return -EINVAL;
+ } else if (shift > 0) {
+ *pres = shift;
+ cycles >>= *pres;
+ } else {
+ *pres = 0;
+ }
+
+ *cprd = cycles;
+
+ return 0;
+}
+
+static void atmel_pwm_calculate_cdty(const struct pwm_state *state,
+ unsigned long clkrate, unsigned long cprd,
+ u32 pres, unsigned long *cdty)
+{
+ unsigned long long cycles = state->duty_cycle;
+
+ cycles *= clkrate;
+ do_div(cycles, NSEC_PER_SEC);
+ cycles >>= pres;
+ *cdty = cprd - cycles;
+}
+
+static void atmel_pwm_update_cdty(struct pwm_chip *chip, struct pwm_device *pwm,
+ unsigned long cdty)
+{
+ struct atmel_pwm_chip *atmel_pwm = to_atmel_pwm_chip(chip);
+ u32 val;
+
+ if (atmel_pwm->data->regs.duty_upd ==
+ atmel_pwm->data->regs.period_upd) {
+ val = atmel_pwm_ch_readl(atmel_pwm, pwm->hwpwm, PWM_CMR);
+ val &= ~PWM_CMR_UPD_CDTY;
+ atmel_pwm_ch_writel(atmel_pwm, pwm->hwpwm, PWM_CMR, val);
+ }
+
+ atmel_pwm_ch_writel(atmel_pwm, pwm->hwpwm,
+ atmel_pwm->data->regs.duty_upd, cdty);
+ atmel_pwm_set_pending(atmel_pwm, pwm->hwpwm);
+}
+
+static void atmel_pwm_set_cprd_cdty(struct pwm_chip *chip,
+ struct pwm_device *pwm,
+ unsigned long cprd, unsigned long cdty)
+{
+ struct atmel_pwm_chip *atmel_pwm = to_atmel_pwm_chip(chip);
+
+ atmel_pwm_ch_writel(atmel_pwm, pwm->hwpwm,
+ atmel_pwm->data->regs.duty, cdty);
+ atmel_pwm_ch_writel(atmel_pwm, pwm->hwpwm,
+ atmel_pwm->data->regs.period, cprd);
+}
+
+static void atmel_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm,
+ bool disable_clk)
+{
+ struct atmel_pwm_chip *atmel_pwm = to_atmel_pwm_chip(chip);
+ unsigned long timeout;
+
+ atmel_pwm_wait_nonpending(atmel_pwm, pwm->hwpwm);
+
+ atmel_pwm_writel(atmel_pwm, PWM_DIS, 1 << pwm->hwpwm);
+
+ /*
+ * Wait for the PWM channel disable operation to be effective before
+ * stopping the clock.
+ */
+ timeout = jiffies + 2 * HZ;
+
+ while ((atmel_pwm_readl(atmel_pwm, PWM_SR) & (1 << pwm->hwpwm)) &&
+ time_before(jiffies, timeout))
+ usleep_range(10, 100);
+
+ if (disable_clk)
+ clk_disable(atmel_pwm->clk);
+}
+
+static int atmel_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
+ const struct pwm_state *state)
+{
+ struct atmel_pwm_chip *atmel_pwm = to_atmel_pwm_chip(chip);
+ unsigned long cprd, cdty;
+ u32 pres, val;
+ int ret;
+
+ if (state->enabled) {
+ unsigned long clkrate = clk_get_rate(atmel_pwm->clk);
+
+ if (pwm->state.enabled &&
+ pwm->state.polarity == state->polarity &&
+ pwm->state.period == state->period) {
+ u32 cmr = atmel_pwm_ch_readl(atmel_pwm, pwm->hwpwm, PWM_CMR);
+
+ cprd = atmel_pwm_ch_readl(atmel_pwm, pwm->hwpwm,
+ atmel_pwm->data->regs.period);
+ pres = cmr & PWM_CMR_CPRE_MSK;
+
+ atmel_pwm_calculate_cdty(state, clkrate, cprd, pres, &cdty);
+ atmel_pwm_update_cdty(chip, pwm, cdty);
+ return 0;
+ }
+
+ ret = atmel_pwm_calculate_cprd_and_pres(chip, clkrate, state, &cprd,
+ &pres);
+ if (ret) {
+ dev_err(pwmchip_parent(chip),
+ "failed to calculate cprd and prescaler\n");
+ return ret;
+ }
+
+ atmel_pwm_calculate_cdty(state, clkrate, cprd, pres, &cdty);
+
+ if (pwm->state.enabled) {
+ atmel_pwm_disable(chip, pwm, false);
+ } else {
+ ret = clk_enable(atmel_pwm->clk);
+ if (ret) {
+ dev_err(pwmchip_parent(chip), "failed to enable clock\n");
+ return ret;
+ }
+ }
+
+ /* It is necessary to preserve CPOL, inside CMR */
+ val = atmel_pwm_ch_readl(atmel_pwm, pwm->hwpwm, PWM_CMR);
+ val = (val & ~PWM_CMR_CPRE_MSK) | (pres & PWM_CMR_CPRE_MSK);
+ if (state->polarity == PWM_POLARITY_NORMAL)
+ val &= ~PWM_CMR_CPOL;
+ else
+ val |= PWM_CMR_CPOL;
+ atmel_pwm_ch_writel(atmel_pwm, pwm->hwpwm, PWM_CMR, val);
+ atmel_pwm_set_cprd_cdty(chip, pwm, cprd, cdty);
+ atmel_pwm_writel(atmel_pwm, PWM_ENA, 1 << pwm->hwpwm);
+ } else if (pwm->state.enabled) {
+ atmel_pwm_disable(chip, pwm, true);
+ }
+
+ return 0;
+}
+
+static int atmel_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm,
+ struct pwm_state *state)
+{
+ struct atmel_pwm_chip *atmel_pwm = to_atmel_pwm_chip(chip);
+ u32 sr, cmr;
+
+ sr = atmel_pwm_readl(atmel_pwm, PWM_SR);
+ cmr = atmel_pwm_ch_readl(atmel_pwm, pwm->hwpwm, PWM_CMR);
+
+ if (sr & (1 << pwm->hwpwm)) {
+ unsigned long rate = clk_get_rate(atmel_pwm->clk);
+ u32 cdty, cprd, pres;
+ u64 tmp;
+
+ pres = cmr & PWM_CMR_CPRE_MSK;
+
+ cprd = atmel_pwm_ch_readl(atmel_pwm, pwm->hwpwm,
+ atmel_pwm->data->regs.period);
+ tmp = (u64)cprd * NSEC_PER_SEC;
+ tmp <<= pres;
+ state->period = DIV64_U64_ROUND_UP(tmp, rate);
+
+ /* Wait for an updated duty_cycle queued in hardware */
+ atmel_pwm_wait_nonpending(atmel_pwm, pwm->hwpwm);
+
+ cdty = atmel_pwm_ch_readl(atmel_pwm, pwm->hwpwm,
+ atmel_pwm->data->regs.duty);
+ tmp = (u64)(cprd - cdty) * NSEC_PER_SEC;
+ tmp <<= pres;
+ state->duty_cycle = DIV64_U64_ROUND_UP(tmp, rate);
+
+ state->enabled = true;
+ } else {
+ state->enabled = false;
+ }
+
+ if (cmr & PWM_CMR_CPOL)
+ state->polarity = PWM_POLARITY_INVERSED;
+ else
+ state->polarity = PWM_POLARITY_NORMAL;
+
+ return 0;
+}
+
+static const struct pwm_ops atmel_pwm_ops = {
+ .apply = atmel_pwm_apply,
+ .get_state = atmel_pwm_get_state,
+};
+
+static const struct atmel_pwm_data atmel_sam9rl_pwm_data = {
+ .regs = {
+ .period = PWMV1_CPRD,
+ .period_upd = PWMV1_CUPD,
+ .duty = PWMV1_CDTY,
+ .duty_upd = PWMV1_CUPD,
+ },
+ .cfg = {
+ /* 16 bits to keep period and duty. */
+ .period_bits = 16,
+ },
+};
+
+static const struct atmel_pwm_data atmel_sama5_pwm_data = {
+ .regs = {
+ .period = PWMV2_CPRD,
+ .period_upd = PWMV2_CPRDUPD,
+ .duty = PWMV2_CDTY,
+ .duty_upd = PWMV2_CDTYUPD,
+ },
+ .cfg = {
+ /* 16 bits to keep period and duty. */
+ .period_bits = 16,
+ },
+};
+
+static const struct atmel_pwm_data mchp_sam9x60_pwm_data = {
+ .regs = {
+ .period = PWMV1_CPRD,
+ .period_upd = PWMV1_CUPD,
+ .duty = PWMV1_CDTY,
+ .duty_upd = PWMV1_CUPD,
+ },
+ .cfg = {
+ /* 32 bits to keep period and duty. */
+ .period_bits = 32,
+ },
+};
+
+static const struct of_device_id atmel_pwm_dt_ids[] = {
+ {
+ .compatible = "atmel,at91sam9rl-pwm",
+ .data = &atmel_sam9rl_pwm_data,
+ }, {
+ .compatible = "atmel,sama5d3-pwm",
+ .data = &atmel_sama5_pwm_data,
+ }, {
+ .compatible = "atmel,sama5d2-pwm",
+ .data = &atmel_sama5_pwm_data,
+ }, {
+ .compatible = "microchip,sam9x60-pwm",
+ .data = &mchp_sam9x60_pwm_data,
+ },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, atmel_pwm_dt_ids);
+
+static int atmel_pwm_enable_clk_if_on(struct pwm_chip *chip, bool on)
+{
+ struct atmel_pwm_chip *atmel_pwm = to_atmel_pwm_chip(chip);
+ unsigned int i, cnt = 0;
+ unsigned long sr;
+ int ret = 0;
+
+ sr = atmel_pwm_readl(atmel_pwm, PWM_SR) & PWM_SR_ALL_CH_MASK;
+ if (!sr)
+ return 0;
+
+ cnt = bitmap_weight(&sr, chip->npwm);
+
+ if (!on)
+ goto disable_clk;
+
+ for (i = 0; i < cnt; i++) {
+ ret = clk_enable(atmel_pwm->clk);
+ if (ret) {
+ dev_err(pwmchip_parent(chip),
+ "failed to enable clock for pwm %pe\n",
+ ERR_PTR(ret));
+
+ cnt = i;
+ goto disable_clk;
+ }
+ }
+
+ return 0;
+
+disable_clk:
+ while (cnt--)
+ clk_disable(atmel_pwm->clk);
+
+ return ret;
+}
+
+static int atmel_pwm_probe(struct platform_device *pdev)
+{
+ struct atmel_pwm_chip *atmel_pwm;
+ struct pwm_chip *chip;
+ int ret;
+
+ chip = devm_pwmchip_alloc(&pdev->dev, 4, sizeof(*atmel_pwm));
+ if (IS_ERR(chip))
+ return PTR_ERR(chip);
+
+ atmel_pwm = to_atmel_pwm_chip(chip);
+ atmel_pwm->data = of_device_get_match_data(&pdev->dev);
+
+ atmel_pwm->update_pending = 0;
+
+ atmel_pwm->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(atmel_pwm->base))
+ return PTR_ERR(atmel_pwm->base);
+
+ atmel_pwm->clk = devm_clk_get_prepared(&pdev->dev, NULL);
+ if (IS_ERR(atmel_pwm->clk))
+ return dev_err_probe(&pdev->dev, PTR_ERR(atmel_pwm->clk),
+ "failed to get prepared PWM clock\n");
+
+ chip->ops = &atmel_pwm_ops;
+
+ ret = atmel_pwm_enable_clk_if_on(chip, true);
+ if (ret < 0)
+ return ret;
+
+ ret = devm_pwmchip_add(&pdev->dev, chip);
+ if (ret < 0) {
+ dev_err_probe(&pdev->dev, ret, "failed to add PWM chip\n");
+ goto disable_clk;
+ }
+
+ return 0;
+
+disable_clk:
+ atmel_pwm_enable_clk_if_on(chip, false);
+
+ return ret;
+}
+
+static struct platform_driver atmel_pwm_driver = {
+ .driver = {
+ .name = "atmel-pwm",
+ .of_match_table = atmel_pwm_dt_ids,
+ },
+ .probe = atmel_pwm_probe,
+};
+module_platform_driver(atmel_pwm_driver);
+
+MODULE_ALIAS("platform:atmel-pwm");
+MODULE_AUTHOR("Bo Shen <voice.shen@atmel.com>");
+MODULE_DESCRIPTION("Atmel PWM driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/pwm/pwm-axi-pwmgen.c b/drivers/pwm/pwm-axi-pwmgen.c
new file mode 100644
index 000000000..b40522f01
--- /dev/null
+++ b/drivers/pwm/pwm-axi-pwmgen.c
@@ -0,0 +1,341 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Analog Devices AXI PWM generator
+ *
+ * Copyright 2024 Analog Devices Inc.
+ * Copyright 2024 Baylibre SAS
+ *
+ * Device docs: https://analogdevicesinc.github.io/hdl/library/axi_pwm_gen/index.html
+ *
+ * Limitations:
+ * - The writes to registers for period and duty are shadowed until
+ * LOAD_CONFIG is written to AXI_PWMGEN_REG_RSTN, at which point
+ * they take effect.
+ * - Writing LOAD_CONFIG also has the effect of re-synchronizing all
+ * enabled channels, which could cause glitching on other channels. It
+ * is therefore expected that channels are assigned harmonic periods
+ * and all have a single user coordinating this.
+ * - Supports normal polarity. Does not support changing polarity.
+ * - On disable, the PWM output becomes low (inactive).
+ */
+#include <linux/adi-axi-common.h>
+#include <linux/bits.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/minmax.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+#define AXI_PWMGEN_REG_ID 0x04
+#define AXI_PWMGEN_REG_SCRATCHPAD 0x08
+#define AXI_PWMGEN_REG_CORE_MAGIC 0x0C
+#define AXI_PWMGEN_REG_RSTN 0x10
+#define AXI_PWMGEN_REG_RSTN_LOAD_CONFIG BIT(1)
+#define AXI_PWMGEN_REG_RSTN_RESET BIT(0)
+#define AXI_PWMGEN_REG_NPWM 0x14
+#define AXI_PWMGEN_REG_CONFIG 0x18
+#define AXI_PWMGEN_REG_CONFIG_FORCE_ALIGN BIT(1)
+#define AXI_PWMGEN_CHX_PERIOD(ch) (0x40 + (4 * (ch)))
+#define AXI_PWMGEN_CHX_DUTY(ch) (0x80 + (4 * (ch)))
+#define AXI_PWMGEN_CHX_OFFSET(ch) (0xC0 + (4 * (ch)))
+#define AXI_PWMGEN_REG_CORE_MAGIC_VAL 0x601A3471 /* Identification number to test during setup */
+
+struct axi_pwmgen_ddata {
+ struct regmap *regmap;
+ unsigned long clk_rate_hz;
+};
+
+static const struct regmap_config axi_pwmgen_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = 0xFC,
+};
+
+/* This represents a hardware configuration for one channel */
+struct axi_pwmgen_waveform {
+ u32 period_cnt;
+ u32 duty_cycle_cnt;
+ u32 duty_offset_cnt;
+};
+
+static struct axi_pwmgen_ddata *axi_pwmgen_ddata_from_chip(struct pwm_chip *chip)
+{
+ return pwmchip_get_drvdata(chip);
+}
+
+static int axi_pwmgen_round_waveform_tohw(struct pwm_chip *chip,
+ struct pwm_device *pwm,
+ const struct pwm_waveform *wf,
+ void *_wfhw)
+{
+ struct axi_pwmgen_waveform *wfhw = _wfhw;
+ struct axi_pwmgen_ddata *ddata = axi_pwmgen_ddata_from_chip(chip);
+ int ret = 0;
+
+ if (wf->period_length_ns == 0) {
+ *wfhw = (struct axi_pwmgen_waveform){
+ .period_cnt = 0,
+ .duty_cycle_cnt = 0,
+ .duty_offset_cnt = 0,
+ };
+ } else {
+ /* With ddata->clk_rate_hz < NSEC_PER_SEC this won't overflow. */
+ wfhw->period_cnt = min_t(u64,
+ mul_u64_u32_div(wf->period_length_ns, ddata->clk_rate_hz, NSEC_PER_SEC),
+ U32_MAX);
+
+ if (wfhw->period_cnt == 0) {
+ /*
+ * The specified period is too short for the hardware.
+ * So round up .period_cnt to 1 (i.e. the smallest
+ * possible period). With .duty_cycle and .duty_offset
+ * being less than or equal to .period, their rounded
+ * value must be 0.
+ */
+ wfhw->period_cnt = 1;
+ wfhw->duty_cycle_cnt = 0;
+ wfhw->duty_offset_cnt = 0;
+ ret = 1;
+ } else {
+ wfhw->duty_cycle_cnt = min_t(u64,
+ mul_u64_u32_div(wf->duty_length_ns, ddata->clk_rate_hz, NSEC_PER_SEC),
+ U32_MAX);
+ wfhw->duty_offset_cnt = min_t(u64,
+ mul_u64_u32_div(wf->duty_offset_ns, ddata->clk_rate_hz, NSEC_PER_SEC),
+ U32_MAX);
+ }
+ }
+
+ dev_dbg(&chip->dev, "pwm#%u: %lld/%lld [+%lld] @%lu -> PERIOD: %08x, DUTY: %08x, OFFSET: %08x\n",
+ pwm->hwpwm, wf->duty_length_ns, wf->period_length_ns, wf->duty_offset_ns,
+ ddata->clk_rate_hz, wfhw->period_cnt, wfhw->duty_cycle_cnt, wfhw->duty_offset_cnt);
+
+ return ret;
+}
+
+static int axi_pwmgen_round_waveform_fromhw(struct pwm_chip *chip, struct pwm_device *pwm,
+ const void *_wfhw, struct pwm_waveform *wf)
+{
+ const struct axi_pwmgen_waveform *wfhw = _wfhw;
+ struct axi_pwmgen_ddata *ddata = axi_pwmgen_ddata_from_chip(chip);
+
+ wf->period_length_ns = DIV64_U64_ROUND_UP((u64)wfhw->period_cnt * NSEC_PER_SEC,
+ ddata->clk_rate_hz);
+
+ wf->duty_length_ns = DIV64_U64_ROUND_UP((u64)wfhw->duty_cycle_cnt * NSEC_PER_SEC,
+ ddata->clk_rate_hz);
+
+ wf->duty_offset_ns = DIV64_U64_ROUND_UP((u64)wfhw->duty_offset_cnt * NSEC_PER_SEC,
+ ddata->clk_rate_hz);
+
+ return 0;
+}
+
+static int axi_pwmgen_write_waveform(struct pwm_chip *chip,
+ struct pwm_device *pwm,
+ const void *_wfhw)
+{
+ const struct axi_pwmgen_waveform *wfhw = _wfhw;
+ struct axi_pwmgen_ddata *ddata = axi_pwmgen_ddata_from_chip(chip);
+ struct regmap *regmap = ddata->regmap;
+ unsigned int ch = pwm->hwpwm;
+ int ret;
+
+ ret = regmap_write(regmap, AXI_PWMGEN_CHX_PERIOD(ch), wfhw->period_cnt);
+ if (ret)
+ return ret;
+
+ ret = regmap_write(regmap, AXI_PWMGEN_CHX_DUTY(ch), wfhw->duty_cycle_cnt);
+ if (ret)
+ return ret;
+
+ ret = regmap_write(regmap, AXI_PWMGEN_CHX_OFFSET(ch), wfhw->duty_offset_cnt);
+ if (ret)
+ return ret;
+
+ return regmap_write(regmap, AXI_PWMGEN_REG_RSTN, AXI_PWMGEN_REG_RSTN_LOAD_CONFIG);
+}
+
+static int axi_pwmgen_read_waveform(struct pwm_chip *chip,
+ struct pwm_device *pwm,
+ void *_wfhw)
+{
+ struct axi_pwmgen_waveform *wfhw = _wfhw;
+ struct axi_pwmgen_ddata *ddata = axi_pwmgen_ddata_from_chip(chip);
+ struct regmap *regmap = ddata->regmap;
+ unsigned int ch = pwm->hwpwm;
+ int ret;
+
+ ret = regmap_read(regmap, AXI_PWMGEN_CHX_PERIOD(ch), &wfhw->period_cnt);
+ if (ret)
+ return ret;
+
+ ret = regmap_read(regmap, AXI_PWMGEN_CHX_DUTY(ch), &wfhw->duty_cycle_cnt);
+ if (ret)
+ return ret;
+
+ ret = regmap_read(regmap, AXI_PWMGEN_CHX_OFFSET(ch), &wfhw->duty_offset_cnt);
+ if (ret)
+ return ret;
+
+ if (wfhw->duty_cycle_cnt > wfhw->period_cnt)
+ wfhw->duty_cycle_cnt = wfhw->period_cnt;
+
+ /* XXX: is this the actual behaviour of the hardware? */
+ if (wfhw->duty_offset_cnt >= wfhw->period_cnt) {
+ wfhw->duty_cycle_cnt = 0;
+ wfhw->duty_offset_cnt = 0;
+ }
+
+ return 0;
+}
+
+static const struct pwm_ops axi_pwmgen_pwm_ops = {
+ .sizeof_wfhw = sizeof(struct axi_pwmgen_waveform),
+ .round_waveform_tohw = axi_pwmgen_round_waveform_tohw,
+ .round_waveform_fromhw = axi_pwmgen_round_waveform_fromhw,
+ .read_waveform = axi_pwmgen_read_waveform,
+ .write_waveform = axi_pwmgen_write_waveform,
+};
+
+static int axi_pwmgen_setup(struct regmap *regmap, struct device *dev)
+{
+ int ret;
+ u32 val;
+
+ ret = regmap_read(regmap, AXI_PWMGEN_REG_CORE_MAGIC, &val);
+ if (ret)
+ return ret;
+
+ if (val != AXI_PWMGEN_REG_CORE_MAGIC_VAL)
+ return dev_err_probe(dev, -ENODEV,
+ "failed to read expected value from register: got %08x, expected %08x\n",
+ val, AXI_PWMGEN_REG_CORE_MAGIC_VAL);
+
+ ret = regmap_read(regmap, ADI_AXI_REG_VERSION, &val);
+ if (ret)
+ return ret;
+
+ if (ADI_AXI_PCORE_VER_MAJOR(val) != 2) {
+ return dev_err_probe(dev, -ENODEV, "Unsupported peripheral version %u.%u.%u\n",
+ ADI_AXI_PCORE_VER_MAJOR(val),
+ ADI_AXI_PCORE_VER_MINOR(val),
+ ADI_AXI_PCORE_VER_PATCH(val));
+ }
+
+ /* Enable the core */
+ ret = regmap_clear_bits(regmap, AXI_PWMGEN_REG_RSTN, AXI_PWMGEN_REG_RSTN_RESET);
+ if (ret)
+ return ret;
+
+ /*
+ * Enable force align so that changes to PWM period and duty cycle take
+ * effect immediately. Otherwise, the effect of the change is delayed
+ * until the period of all channels run out, which can be long after the
+ * apply function returns.
+ */
+ ret = regmap_set_bits(regmap, AXI_PWMGEN_REG_CONFIG, AXI_PWMGEN_REG_CONFIG_FORCE_ALIGN);
+ if (ret)
+ return ret;
+
+ ret = regmap_read(regmap, AXI_PWMGEN_REG_NPWM, &val);
+ if (ret)
+ return ret;
+
+ /* Return the number of PWMs */
+ return val;
+}
+
+static int axi_pwmgen_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct regmap *regmap;
+ struct pwm_chip *chip;
+ struct axi_pwmgen_ddata *ddata;
+ struct clk *axi_clk, *clk;
+ void __iomem *io_base;
+ int ret;
+
+ io_base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(io_base))
+ return PTR_ERR(io_base);
+
+ regmap = devm_regmap_init_mmio(dev, io_base, &axi_pwmgen_regmap_config);
+ if (IS_ERR(regmap))
+ return dev_err_probe(dev, PTR_ERR(regmap),
+ "failed to init register map\n");
+
+ ret = axi_pwmgen_setup(regmap, dev);
+ if (ret < 0)
+ return ret;
+
+ chip = devm_pwmchip_alloc(dev, ret, sizeof(*ddata));
+ if (IS_ERR(chip))
+ return PTR_ERR(chip);
+ ddata = pwmchip_get_drvdata(chip);
+ ddata->regmap = regmap;
+
+ /*
+ * Using NULL here instead of "axi" for backwards compatibility. There
+ * are some dtbs that don't give clock-names and have the "ext" clock
+ * as the one and only clock (due to mistake in the original bindings).
+ */
+ axi_clk = devm_clk_get_enabled(dev, NULL);
+ if (IS_ERR(axi_clk))
+ return dev_err_probe(dev, PTR_ERR(axi_clk), "failed to get axi clock\n");
+
+ clk = devm_clk_get_optional_enabled(dev, "ext");
+ if (IS_ERR(clk))
+ return dev_err_probe(dev, PTR_ERR(clk), "failed to get ext clock\n");
+
+ /*
+ * If there is no "ext" clock, it means the HDL was compiled with
+ * ASYNC_CLK_EN=0. In this case, the AXI clock is also used for the
+ * PWM output clock.
+ */
+ if (!clk)
+ clk = axi_clk;
+
+ ret = devm_clk_rate_exclusive_get(dev, clk);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to get exclusive rate\n");
+
+ ddata->clk_rate_hz = clk_get_rate(clk);
+ if (!ddata->clk_rate_hz || ddata->clk_rate_hz > NSEC_PER_SEC)
+ return dev_err_probe(dev, -EINVAL,
+ "Invalid clock rate: %lu\n", ddata->clk_rate_hz);
+
+ chip->ops = &axi_pwmgen_pwm_ops;
+ chip->atomic = true;
+
+ ret = devm_pwmchip_add(dev, chip);
+ if (ret)
+ return dev_err_probe(dev, ret, "could not add PWM chip\n");
+
+ return 0;
+}
+
+static const struct of_device_id axi_pwmgen_ids[] = {
+ { .compatible = "adi,axi-pwmgen-2.00.a" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, axi_pwmgen_ids);
+
+static struct platform_driver axi_pwmgen_driver = {
+ .driver = {
+ .name = "axi-pwmgen",
+ .of_match_table = axi_pwmgen_ids,
+ },
+ .probe = axi_pwmgen_probe,
+};
+module_platform_driver(axi_pwmgen_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Sergiu Cuciurean <sergiu.cuciurean@analog.com>");
+MODULE_AUTHOR("Trevor Gamblin <tgamblin@baylibre.com>");
+MODULE_DESCRIPTION("Driver for the Analog Devices AXI PWM generator");
diff --git a/drivers/pwm/pwm-bcm-iproc.c b/drivers/pwm/pwm-bcm-iproc.c
new file mode 100644
index 000000000..dc1e792b1
--- /dev/null
+++ b/drivers/pwm/pwm-bcm-iproc.c
@@ -0,0 +1,248 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright (C) 2016 Broadcom
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/math64.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+
+#define IPROC_PWM_CTRL_OFFSET 0x00
+#define IPROC_PWM_CTRL_TYPE_SHIFT(ch) (15 + (ch))
+#define IPROC_PWM_CTRL_POLARITY_SHIFT(ch) (8 + (ch))
+#define IPROC_PWM_CTRL_EN_SHIFT(ch) (ch)
+
+#define IPROC_PWM_PERIOD_OFFSET(ch) (0x04 + ((ch) << 3))
+#define IPROC_PWM_PERIOD_MIN 0x02
+#define IPROC_PWM_PERIOD_MAX 0xffff
+
+#define IPROC_PWM_DUTY_CYCLE_OFFSET(ch) (0x08 + ((ch) << 3))
+#define IPROC_PWM_DUTY_CYCLE_MIN 0x00
+#define IPROC_PWM_DUTY_CYCLE_MAX 0xffff
+
+#define IPROC_PWM_PRESCALE_OFFSET 0x24
+#define IPROC_PWM_PRESCALE_BITS 0x06
+#define IPROC_PWM_PRESCALE_SHIFT(ch) ((3 - (ch)) * \
+ IPROC_PWM_PRESCALE_BITS)
+#define IPROC_PWM_PRESCALE_MASK(ch) (IPROC_PWM_PRESCALE_MAX << \
+ IPROC_PWM_PRESCALE_SHIFT(ch))
+#define IPROC_PWM_PRESCALE_MIN 0x00
+#define IPROC_PWM_PRESCALE_MAX 0x3f
+
+struct iproc_pwmc {
+ void __iomem *base;
+ struct clk *clk;
+};
+
+static inline struct iproc_pwmc *to_iproc_pwmc(struct pwm_chip *chip)
+{
+ return pwmchip_get_drvdata(chip);
+}
+
+static void iproc_pwmc_enable(struct iproc_pwmc *ip, unsigned int channel)
+{
+ u32 value;
+
+ value = readl(ip->base + IPROC_PWM_CTRL_OFFSET);
+ value |= 1 << IPROC_PWM_CTRL_EN_SHIFT(channel);
+ writel(value, ip->base + IPROC_PWM_CTRL_OFFSET);
+
+ /* must be a 400 ns delay between clearing and setting enable bit */
+ ndelay(400);
+}
+
+static void iproc_pwmc_disable(struct iproc_pwmc *ip, unsigned int channel)
+{
+ u32 value;
+
+ value = readl(ip->base + IPROC_PWM_CTRL_OFFSET);
+ value &= ~(1 << IPROC_PWM_CTRL_EN_SHIFT(channel));
+ writel(value, ip->base + IPROC_PWM_CTRL_OFFSET);
+
+ /* must be a 400 ns delay between clearing and setting enable bit */
+ ndelay(400);
+}
+
+static int iproc_pwmc_get_state(struct pwm_chip *chip, struct pwm_device *pwm,
+ struct pwm_state *state)
+{
+ struct iproc_pwmc *ip = to_iproc_pwmc(chip);
+ u64 tmp, multi, rate;
+ u32 value, prescale;
+
+ value = readl(ip->base + IPROC_PWM_CTRL_OFFSET);
+
+ if (value & BIT(IPROC_PWM_CTRL_EN_SHIFT(pwm->hwpwm)))
+ state->enabled = true;
+ else
+ state->enabled = false;
+
+ if (value & BIT(IPROC_PWM_CTRL_POLARITY_SHIFT(pwm->hwpwm)))
+ state->polarity = PWM_POLARITY_NORMAL;
+ else
+ state->polarity = PWM_POLARITY_INVERSED;
+
+ rate = clk_get_rate(ip->clk);
+ if (rate == 0) {
+ state->period = 0;
+ state->duty_cycle = 0;
+ return 0;
+ }
+
+ value = readl(ip->base + IPROC_PWM_PRESCALE_OFFSET);
+ prescale = value >> IPROC_PWM_PRESCALE_SHIFT(pwm->hwpwm);
+ prescale &= IPROC_PWM_PRESCALE_MAX;
+
+ multi = NSEC_PER_SEC * (prescale + 1);
+
+ value = readl(ip->base + IPROC_PWM_PERIOD_OFFSET(pwm->hwpwm));
+ tmp = (value & IPROC_PWM_PERIOD_MAX) * multi;
+ state->period = div64_u64(tmp, rate);
+
+ value = readl(ip->base + IPROC_PWM_DUTY_CYCLE_OFFSET(pwm->hwpwm));
+ tmp = (value & IPROC_PWM_PERIOD_MAX) * multi;
+ state->duty_cycle = div64_u64(tmp, rate);
+
+ return 0;
+}
+
+static int iproc_pwmc_apply(struct pwm_chip *chip, struct pwm_device *pwm,
+ const struct pwm_state *state)
+{
+ unsigned long prescale = IPROC_PWM_PRESCALE_MIN;
+ struct iproc_pwmc *ip = to_iproc_pwmc(chip);
+ u32 value, period, duty;
+ u64 rate;
+
+ rate = clk_get_rate(ip->clk);
+
+ /*
+ * Find period count, duty count and prescale to suit duty_cycle and
+ * period. This is done according to formulas described below:
+ *
+ * period_ns = 10^9 * (PRESCALE + 1) * PC / PWM_CLK_RATE
+ * duty_ns = 10^9 * (PRESCALE + 1) * DC / PWM_CLK_RATE
+ *
+ * PC = (PWM_CLK_RATE * period_ns) / (10^9 * (PRESCALE + 1))
+ * DC = (PWM_CLK_RATE * duty_ns) / (10^9 * (PRESCALE + 1))
+ */
+ while (1) {
+ u64 value, div;
+
+ div = NSEC_PER_SEC * (prescale + 1);
+ value = rate * state->period;
+ period = div64_u64(value, div);
+ value = rate * state->duty_cycle;
+ duty = div64_u64(value, div);
+
+ if (period < IPROC_PWM_PERIOD_MIN)
+ return -EINVAL;
+
+ if (period <= IPROC_PWM_PERIOD_MAX &&
+ duty <= IPROC_PWM_DUTY_CYCLE_MAX)
+ break;
+
+ /* Otherwise, increase prescale and recalculate counts */
+ if (++prescale > IPROC_PWM_PRESCALE_MAX)
+ return -EINVAL;
+ }
+
+ iproc_pwmc_disable(ip, pwm->hwpwm);
+
+ /* Set prescale */
+ value = readl(ip->base + IPROC_PWM_PRESCALE_OFFSET);
+ value &= ~IPROC_PWM_PRESCALE_MASK(pwm->hwpwm);
+ value |= prescale << IPROC_PWM_PRESCALE_SHIFT(pwm->hwpwm);
+ writel(value, ip->base + IPROC_PWM_PRESCALE_OFFSET);
+
+ /* set period and duty cycle */
+ writel(period, ip->base + IPROC_PWM_PERIOD_OFFSET(pwm->hwpwm));
+ writel(duty, ip->base + IPROC_PWM_DUTY_CYCLE_OFFSET(pwm->hwpwm));
+
+ /* set polarity */
+ value = readl(ip->base + IPROC_PWM_CTRL_OFFSET);
+
+ if (state->polarity == PWM_POLARITY_NORMAL)
+ value |= 1 << IPROC_PWM_CTRL_POLARITY_SHIFT(pwm->hwpwm);
+ else
+ value &= ~(1 << IPROC_PWM_CTRL_POLARITY_SHIFT(pwm->hwpwm));
+
+ writel(value, ip->base + IPROC_PWM_CTRL_OFFSET);
+
+ if (state->enabled)
+ iproc_pwmc_enable(ip, pwm->hwpwm);
+
+ return 0;
+}
+
+static const struct pwm_ops iproc_pwm_ops = {
+ .apply = iproc_pwmc_apply,
+ .get_state = iproc_pwmc_get_state,
+};
+
+static int iproc_pwmc_probe(struct platform_device *pdev)
+{
+ struct pwm_chip *chip;
+ struct iproc_pwmc *ip;
+ unsigned int i;
+ u32 value;
+ int ret;
+
+ chip = devm_pwmchip_alloc(&pdev->dev, 4, sizeof(*ip));
+ if (IS_ERR(chip))
+ return PTR_ERR(chip);
+ ip = to_iproc_pwmc(chip);
+
+ platform_set_drvdata(pdev, ip);
+
+ chip->ops = &iproc_pwm_ops;
+
+ ip->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(ip->base))
+ return PTR_ERR(ip->base);
+
+ ip->clk = devm_clk_get_enabled(&pdev->dev, NULL);
+ if (IS_ERR(ip->clk))
+ return dev_err_probe(&pdev->dev, PTR_ERR(ip->clk),
+ "failed to get clock\n");
+
+ /* Set full drive and normal polarity for all channels */
+ value = readl(ip->base + IPROC_PWM_CTRL_OFFSET);
+
+ for (i = 0; i < chip->npwm; i++) {
+ value &= ~(1 << IPROC_PWM_CTRL_TYPE_SHIFT(i));
+ value |= 1 << IPROC_PWM_CTRL_POLARITY_SHIFT(i);
+ }
+
+ writel(value, ip->base + IPROC_PWM_CTRL_OFFSET);
+
+ ret = devm_pwmchip_add(&pdev->dev, chip);
+ if (ret < 0)
+ return dev_err_probe(&pdev->dev, ret,
+ "failed to add PWM chip\n");
+
+ return 0;
+}
+
+static const struct of_device_id bcm_iproc_pwmc_dt[] = {
+ { .compatible = "brcm,iproc-pwm" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, bcm_iproc_pwmc_dt);
+
+static struct platform_driver iproc_pwmc_driver = {
+ .driver = {
+ .name = "bcm-iproc-pwm",
+ .of_match_table = bcm_iproc_pwmc_dt,
+ },
+ .probe = iproc_pwmc_probe,
+};
+module_platform_driver(iproc_pwmc_driver);
+
+MODULE_AUTHOR("Yendapally Reddy Dhananjaya Reddy <yendapally.reddy@broadcom.com>");
+MODULE_DESCRIPTION("Broadcom iProc PWM driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/pwm/pwm-bcm-kona.c b/drivers/pwm/pwm-bcm-kona.c
new file mode 100644
index 000000000..ecfa3e370
--- /dev/null
+++ b/drivers/pwm/pwm-bcm-kona.c
@@ -0,0 +1,338 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright (C) 2014 Broadcom Corporation
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/math64.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+
+/*
+ * The Kona PWM has some unusual characteristics. Here are the main points.
+ *
+ * 1) There is no disable bit and the hardware docs advise programming a zero
+ * duty to achieve output equivalent to that of a normal disable operation.
+ *
+ * 2) Changes to prescale, duty, period, and polarity do not take effect until
+ * a subsequent rising edge of the trigger bit.
+ *
+ * 3) If the smooth bit and trigger bit are both low, the output is a constant
+ * high signal. Otherwise, the earlier waveform continues to be output.
+ *
+ * 4) If the smooth bit is set on the rising edge of the trigger bit, output
+ * will transition to the new settings on a period boundary (which could be
+ * seconds away). If the smooth bit is clear, new settings will be applied
+ * as soon as possible (the hardware always has a 400ns delay).
+ *
+ * 5) When the external clock that feeds the PWM is disabled, output is pegged
+ * high or low depending on its state at that exact instant.
+ */
+
+#define PWM_CONTROL_OFFSET 0x00000000
+#define PWM_CONTROL_SMOOTH_SHIFT(chan) (24 + (chan))
+#define PWM_CONTROL_TYPE_SHIFT(chan) (16 + (chan))
+#define PWM_CONTROL_POLARITY_SHIFT(chan) (8 + (chan))
+#define PWM_CONTROL_TRIGGER_SHIFT(chan) (chan)
+
+#define PRESCALE_OFFSET 0x00000004
+#define PRESCALE_SHIFT(chan) ((chan) << 2)
+#define PRESCALE_MASK(chan) (0x7 << PRESCALE_SHIFT(chan))
+#define PRESCALE_MIN 0x00000000
+#define PRESCALE_MAX 0x00000007
+
+#define PERIOD_COUNT_OFFSET(chan) (0x00000008 + ((chan) << 3))
+#define PERIOD_COUNT_MIN 0x00000002
+#define PERIOD_COUNT_MAX 0x00ffffff
+
+#define DUTY_CYCLE_HIGH_OFFSET(chan) (0x0000000c + ((chan) << 3))
+#define DUTY_CYCLE_HIGH_MIN 0x00000000
+#define DUTY_CYCLE_HIGH_MAX 0x00ffffff
+
+struct kona_pwmc {
+ void __iomem *base;
+ struct clk *clk;
+};
+
+static inline struct kona_pwmc *to_kona_pwmc(struct pwm_chip *chip)
+{
+ return pwmchip_get_drvdata(chip);
+}
+
+/*
+ * Clear trigger bit but set smooth bit to maintain old output.
+ */
+static void kona_pwmc_prepare_for_settings(struct kona_pwmc *kp,
+ unsigned int chan)
+{
+ unsigned int value = readl(kp->base + PWM_CONTROL_OFFSET);
+
+ value |= 1 << PWM_CONTROL_SMOOTH_SHIFT(chan);
+ value &= ~(1 << PWM_CONTROL_TRIGGER_SHIFT(chan));
+ writel(value, kp->base + PWM_CONTROL_OFFSET);
+
+ /*
+ * There must be a min 400ns delay between clearing trigger and setting
+ * it. Failing to do this may result in no PWM signal.
+ */
+ ndelay(400);
+}
+
+static void kona_pwmc_apply_settings(struct kona_pwmc *kp, unsigned int chan)
+{
+ unsigned int value = readl(kp->base + PWM_CONTROL_OFFSET);
+
+ /* Set trigger bit and clear smooth bit to apply new settings */
+ value &= ~(1 << PWM_CONTROL_SMOOTH_SHIFT(chan));
+ value |= 1 << PWM_CONTROL_TRIGGER_SHIFT(chan);
+ writel(value, kp->base + PWM_CONTROL_OFFSET);
+
+ /* Trigger bit must be held high for at least 400 ns. */
+ ndelay(400);
+}
+
+static int kona_pwmc_config(struct pwm_chip *chip, struct pwm_device *pwm,
+ u64 duty_ns, u64 period_ns)
+{
+ struct kona_pwmc *kp = to_kona_pwmc(chip);
+ u64 div, rate;
+ unsigned long prescale = PRESCALE_MIN, pc, dc;
+ unsigned int value, chan = pwm->hwpwm;
+
+ /*
+ * Find period count, duty count and prescale to suit duty_ns and
+ * period_ns. This is done according to formulas described below:
+ *
+ * period_ns = 10^9 * (PRESCALE + 1) * PC / PWM_CLK_RATE
+ * duty_ns = 10^9 * (PRESCALE + 1) * DC / PWM_CLK_RATE
+ *
+ * PC = (PWM_CLK_RATE * period_ns) / (10^9 * (PRESCALE + 1))
+ * DC = (PWM_CLK_RATE * duty_ns) / (10^9 * (PRESCALE + 1))
+ */
+
+ rate = clk_get_rate(kp->clk);
+
+ while (1) {
+ div = 1000000000;
+ div *= 1 + prescale;
+ pc = mul_u64_u64_div_u64(rate, period_ns, div);
+ dc = mul_u64_u64_div_u64(rate, duty_ns, div);
+
+ /* If duty_ns or period_ns are not achievable then return */
+ if (pc < PERIOD_COUNT_MIN)
+ return -EINVAL;
+
+ /* If pc and dc are in bounds, the calculation is done */
+ if (pc <= PERIOD_COUNT_MAX && dc <= DUTY_CYCLE_HIGH_MAX)
+ break;
+
+ /* Otherwise, increase prescale and recalculate pc and dc */
+ if (++prescale > PRESCALE_MAX)
+ return -EINVAL;
+ }
+
+ kona_pwmc_prepare_for_settings(kp, chan);
+
+ value = readl(kp->base + PRESCALE_OFFSET);
+ value &= ~PRESCALE_MASK(chan);
+ value |= prescale << PRESCALE_SHIFT(chan);
+ writel(value, kp->base + PRESCALE_OFFSET);
+
+ writel(pc, kp->base + PERIOD_COUNT_OFFSET(chan));
+
+ writel(dc, kp->base + DUTY_CYCLE_HIGH_OFFSET(chan));
+
+ kona_pwmc_apply_settings(kp, chan);
+
+ return 0;
+}
+
+static int kona_pwmc_set_polarity(struct pwm_chip *chip, struct pwm_device *pwm,
+ enum pwm_polarity polarity)
+{
+ struct kona_pwmc *kp = to_kona_pwmc(chip);
+ unsigned int chan = pwm->hwpwm;
+ unsigned int value;
+ int ret;
+
+ ret = clk_prepare_enable(kp->clk);
+ if (ret < 0) {
+ dev_err(pwmchip_parent(chip), "failed to enable clock: %d\n", ret);
+ return ret;
+ }
+
+ kona_pwmc_prepare_for_settings(kp, chan);
+
+ value = readl(kp->base + PWM_CONTROL_OFFSET);
+
+ if (polarity == PWM_POLARITY_NORMAL)
+ value |= 1 << PWM_CONTROL_POLARITY_SHIFT(chan);
+ else
+ value &= ~(1 << PWM_CONTROL_POLARITY_SHIFT(chan));
+
+ writel(value, kp->base + PWM_CONTROL_OFFSET);
+
+ kona_pwmc_apply_settings(kp, chan);
+
+ clk_disable_unprepare(kp->clk);
+
+ return 0;
+}
+
+static int kona_pwmc_enable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct kona_pwmc *kp = to_kona_pwmc(chip);
+ int ret;
+
+ ret = clk_prepare_enable(kp->clk);
+ if (ret < 0) {
+ dev_err(pwmchip_parent(chip), "failed to enable clock: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void kona_pwmc_disable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct kona_pwmc *kp = to_kona_pwmc(chip);
+ unsigned int chan = pwm->hwpwm;
+ unsigned int value;
+
+ kona_pwmc_prepare_for_settings(kp, chan);
+
+ /* Simulate a disable by configuring for zero duty */
+ writel(0, kp->base + DUTY_CYCLE_HIGH_OFFSET(chan));
+ writel(0, kp->base + PERIOD_COUNT_OFFSET(chan));
+
+ /* Set prescale to 0 for this channel */
+ value = readl(kp->base + PRESCALE_OFFSET);
+ value &= ~PRESCALE_MASK(chan);
+ writel(value, kp->base + PRESCALE_OFFSET);
+
+ kona_pwmc_apply_settings(kp, chan);
+
+ clk_disable_unprepare(kp->clk);
+}
+
+static int kona_pwmc_apply(struct pwm_chip *chip, struct pwm_device *pwm,
+ const struct pwm_state *state)
+{
+ int err;
+ struct kona_pwmc *kp = to_kona_pwmc(chip);
+ bool enabled = pwm->state.enabled;
+
+ if (state->polarity != pwm->state.polarity) {
+ if (enabled) {
+ kona_pwmc_disable(chip, pwm);
+ enabled = false;
+ }
+
+ err = kona_pwmc_set_polarity(chip, pwm, state->polarity);
+ if (err)
+ return err;
+
+ pwm->state.polarity = state->polarity;
+ }
+
+ if (!state->enabled) {
+ if (enabled)
+ kona_pwmc_disable(chip, pwm);
+ return 0;
+ } else if (!enabled) {
+ /*
+ * This is a bit special here, usually the PWM should only be
+ * enabled when duty and period are setup. But before this
+ * driver was converted to .apply it was done the other way
+ * around and so this behaviour was kept even though this might
+ * result in a glitch. This might be improvable by someone with
+ * hardware and/or documentation.
+ */
+ err = kona_pwmc_enable(chip, pwm);
+ if (err)
+ return err;
+ }
+
+ err = kona_pwmc_config(chip, pwm, state->duty_cycle, state->period);
+ if (err && !pwm->state.enabled)
+ clk_disable_unprepare(kp->clk);
+
+ return err;
+}
+
+static const struct pwm_ops kona_pwm_ops = {
+ .apply = kona_pwmc_apply,
+};
+
+static int kona_pwmc_probe(struct platform_device *pdev)
+{
+ struct pwm_chip *chip;
+ struct kona_pwmc *kp;
+ unsigned int chan;
+ unsigned int value = 0;
+ int ret = 0;
+
+ chip = devm_pwmchip_alloc(&pdev->dev, 6, sizeof(*kp));
+ if (IS_ERR(chip))
+ return PTR_ERR(chip);
+ kp = to_kona_pwmc(chip);
+
+ chip->ops = &kona_pwm_ops;
+
+ kp->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(kp->base))
+ return PTR_ERR(kp->base);
+
+ kp->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(kp->clk)) {
+ dev_err(&pdev->dev, "failed to get clock: %ld\n",
+ PTR_ERR(kp->clk));
+ return PTR_ERR(kp->clk);
+ }
+
+ ret = clk_prepare_enable(kp->clk);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to enable clock: %d\n", ret);
+ return ret;
+ }
+
+ /* Set push/pull for all channels */
+ for (chan = 0; chan < chip->npwm; chan++)
+ value |= (1 << PWM_CONTROL_TYPE_SHIFT(chan));
+
+ writel(value, kp->base + PWM_CONTROL_OFFSET);
+
+ clk_disable_unprepare(kp->clk);
+
+ ret = devm_pwmchip_add(&pdev->dev, chip);
+ if (ret < 0)
+ dev_err(&pdev->dev, "failed to add PWM chip: %d\n", ret);
+
+ return ret;
+}
+
+static const struct of_device_id bcm_kona_pwmc_dt[] = {
+ { .compatible = "brcm,kona-pwm" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, bcm_kona_pwmc_dt);
+
+static struct platform_driver kona_pwmc_driver = {
+ .driver = {
+ .name = "bcm-kona-pwm",
+ .of_match_table = bcm_kona_pwmc_dt,
+ },
+ .probe = kona_pwmc_probe,
+};
+module_platform_driver(kona_pwmc_driver);
+
+MODULE_AUTHOR("Broadcom Corporation <bcm-kernel-feedback-list@broadcom.com>");
+MODULE_AUTHOR("Tim Kryger <tkryger@broadcom.com>");
+MODULE_DESCRIPTION("Broadcom Kona PWM driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/pwm/pwm-bcm2835.c b/drivers/pwm/pwm-bcm2835.c
new file mode 100644
index 000000000..a4fbf9cfc
--- /dev/null
+++ b/drivers/pwm/pwm-bcm2835.c
@@ -0,0 +1,185 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2014 Bart Tanghe <bart.tanghe@thomasmore.be>
+ */
+
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+
+#define PWM_CONTROL 0x000
+#define PWM_CONTROL_SHIFT(x) ((x) * 8)
+#define PWM_CONTROL_MASK 0xff
+#define PWM_MODE 0x80 /* set timer in PWM mode */
+#define PWM_ENABLE (1 << 0)
+#define PWM_POLARITY (1 << 4)
+
+#define PERIOD(x) (((x) * 0x10) + 0x10)
+#define DUTY(x) (((x) * 0x10) + 0x14)
+
+#define PERIOD_MIN 0x2
+
+struct bcm2835_pwm {
+ void __iomem *base;
+ struct clk *clk;
+ unsigned long rate;
+};
+
+static inline struct bcm2835_pwm *to_bcm2835_pwm(struct pwm_chip *chip)
+{
+ return pwmchip_get_drvdata(chip);
+}
+
+static int bcm2835_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
+ const struct pwm_state *state)
+{
+
+ struct bcm2835_pwm *pc = to_bcm2835_pwm(chip);
+ unsigned long long period_cycles;
+ u64 max_period;
+
+ u32 val;
+
+ /*
+ * period_cycles must be a 32 bit value, so period * rate / NSEC_PER_SEC
+ * must be <= U32_MAX. As U32_MAX * NSEC_PER_SEC < U64_MAX the
+ * multiplication period * rate doesn't overflow.
+ * To calculate the maximal possible period that guarantees the
+ * above inequality:
+ *
+ * round(period * rate / NSEC_PER_SEC) <= U32_MAX
+ * <=> period * rate / NSEC_PER_SEC < U32_MAX + 0.5
+ * <=> period * rate < (U32_MAX + 0.5) * NSEC_PER_SEC
+ * <=> period < ((U32_MAX + 0.5) * NSEC_PER_SEC) / rate
+ * <=> period < ((U32_MAX * NSEC_PER_SEC + NSEC_PER_SEC/2) / rate
+ * <=> period <= ceil((U32_MAX * NSEC_PER_SEC + NSEC_PER_SEC/2) / rate) - 1
+ */
+ max_period = DIV_ROUND_UP_ULL((u64)U32_MAX * NSEC_PER_SEC + NSEC_PER_SEC / 2, pc->rate) - 1;
+
+ if (state->period > max_period)
+ return -EINVAL;
+
+ /* set period */
+ period_cycles = DIV_ROUND_CLOSEST_ULL(state->period * pc->rate, NSEC_PER_SEC);
+
+ /* don't accept a period that is too small */
+ if (period_cycles < PERIOD_MIN)
+ return -EINVAL;
+
+ writel(period_cycles, pc->base + PERIOD(pwm->hwpwm));
+
+ /* set duty cycle */
+ val = DIV_ROUND_CLOSEST_ULL(state->duty_cycle * pc->rate, NSEC_PER_SEC);
+ writel(val, pc->base + DUTY(pwm->hwpwm));
+
+ /* set polarity */
+ val = readl(pc->base + PWM_CONTROL);
+
+ val &= ~(PWM_CONTROL_MASK << PWM_CONTROL_SHIFT(pwm->hwpwm));
+ val |= PWM_MODE << PWM_CONTROL_SHIFT(pwm->hwpwm);
+
+ if (state->polarity == PWM_POLARITY_NORMAL)
+ val &= ~(PWM_POLARITY << PWM_CONTROL_SHIFT(pwm->hwpwm));
+ else
+ val |= PWM_POLARITY << PWM_CONTROL_SHIFT(pwm->hwpwm);
+
+ /* enable/disable */
+ if (state->enabled)
+ val |= PWM_ENABLE << PWM_CONTROL_SHIFT(pwm->hwpwm);
+ else
+ val &= ~(PWM_ENABLE << PWM_CONTROL_SHIFT(pwm->hwpwm));
+
+ writel(val, pc->base + PWM_CONTROL);
+
+ return 0;
+}
+
+static const struct pwm_ops bcm2835_pwm_ops = {
+ .apply = bcm2835_pwm_apply,
+};
+
+static int bcm2835_pwm_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct pwm_chip *chip;
+ struct bcm2835_pwm *pc;
+ int ret;
+
+ chip = devm_pwmchip_alloc(dev, 2, sizeof(*pc));
+ if (IS_ERR(chip))
+ return PTR_ERR(chip);
+ pc = to_bcm2835_pwm(chip);
+
+ pc->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(pc->base))
+ return PTR_ERR(pc->base);
+
+ pc->clk = devm_clk_get_enabled(dev, NULL);
+ if (IS_ERR(pc->clk))
+ return dev_err_probe(dev, PTR_ERR(pc->clk),
+ "clock not found\n");
+
+ ret = devm_clk_rate_exclusive_get(dev, pc->clk);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "fail to get exclusive rate\n");
+
+ pc->rate = clk_get_rate(pc->clk);
+ if (!pc->rate)
+ return dev_err_probe(dev, -EINVAL,
+ "failed to get clock rate\n");
+
+ chip->ops = &bcm2835_pwm_ops;
+ chip->atomic = true;
+
+ platform_set_drvdata(pdev, pc);
+
+ ret = devm_pwmchip_add(dev, chip);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "failed to add pwmchip\n");
+
+ return 0;
+}
+
+static int bcm2835_pwm_suspend(struct device *dev)
+{
+ struct bcm2835_pwm *pc = dev_get_drvdata(dev);
+
+ clk_disable_unprepare(pc->clk);
+
+ return 0;
+}
+
+static int bcm2835_pwm_resume(struct device *dev)
+{
+ struct bcm2835_pwm *pc = dev_get_drvdata(dev);
+
+ return clk_prepare_enable(pc->clk);
+}
+
+static DEFINE_SIMPLE_DEV_PM_OPS(bcm2835_pwm_pm_ops, bcm2835_pwm_suspend,
+ bcm2835_pwm_resume);
+
+static const struct of_device_id bcm2835_pwm_of_match[] = {
+ { .compatible = "brcm,bcm2835-pwm" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, bcm2835_pwm_of_match);
+
+static struct platform_driver bcm2835_pwm_driver = {
+ .driver = {
+ .name = "bcm2835-pwm",
+ .of_match_table = bcm2835_pwm_of_match,
+ .pm = pm_ptr(&bcm2835_pwm_pm_ops),
+ },
+ .probe = bcm2835_pwm_probe,
+};
+module_platform_driver(bcm2835_pwm_driver);
+
+MODULE_AUTHOR("Bart Tanghe <bart.tanghe@thomasmore.be>");
+MODULE_DESCRIPTION("Broadcom BCM2835 PWM driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/pwm/pwm-berlin.c b/drivers/pwm/pwm-berlin.c
new file mode 100644
index 000000000..d36a245b6
--- /dev/null
+++ b/drivers/pwm/pwm-berlin.c
@@ -0,0 +1,285 @@
+/*
+ * Marvell Berlin PWM driver
+ *
+ * Copyright (C) 2015 Marvell Technology Group Ltd.
+ *
+ * Author: Antoine Tenart <antoine.tenart@free-electrons.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+#include <linux/slab.h>
+
+#define BERLIN_PWM_EN 0x0
+#define BERLIN_PWM_ENABLE BIT(0)
+#define BERLIN_PWM_CONTROL 0x4
+/*
+ * The prescaler claims to support 8 different moduli, configured using the
+ * low three bits of PWM_CONTROL. (Sequentially, they are 1, 4, 8, 16, 64,
+ * 256, 1024, and 4096.) However, the moduli from 4 to 1024 appear to be
+ * implemented by internally shifting TCNT left without adding additional
+ * bits. So, the max TCNT that actually works for a modulus of 4 is 0x3fff;
+ * for 8, 0x1fff; and so on. This means that those moduli are entirely
+ * useless, as we could just do the shift ourselves. The 4096 modulus is
+ * implemented with a real prescaler, so we do use that, but we treat it
+ * as a flag instead of pretending the modulus is actually configurable.
+ */
+#define BERLIN_PWM_PRESCALE_4096 0x7
+#define BERLIN_PWM_INVERT_POLARITY BIT(3)
+#define BERLIN_PWM_DUTY 0x8
+#define BERLIN_PWM_TCNT 0xc
+#define BERLIN_PWM_MAX_TCNT 65535
+
+#define BERLIN_PWM_NUMPWMS 4
+
+struct berlin_pwm_channel {
+ u32 enable;
+ u32 ctrl;
+ u32 duty;
+ u32 tcnt;
+};
+
+struct berlin_pwm_chip {
+ struct clk *clk;
+ void __iomem *base;
+ struct berlin_pwm_channel channel[BERLIN_PWM_NUMPWMS];
+};
+
+static inline struct berlin_pwm_chip *to_berlin_pwm_chip(struct pwm_chip *chip)
+{
+ return pwmchip_get_drvdata(chip);
+}
+
+static inline u32 berlin_pwm_readl(struct berlin_pwm_chip *bpc,
+ unsigned int channel, unsigned long offset)
+{
+ return readl_relaxed(bpc->base + channel * 0x10 + offset);
+}
+
+static inline void berlin_pwm_writel(struct berlin_pwm_chip *bpc,
+ unsigned int channel, u32 value,
+ unsigned long offset)
+{
+ writel_relaxed(value, bpc->base + channel * 0x10 + offset);
+}
+
+static int berlin_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
+ u64 duty_ns, u64 period_ns)
+{
+ struct berlin_pwm_chip *bpc = to_berlin_pwm_chip(chip);
+ bool prescale_4096 = false;
+ u32 value, duty, period;
+ u64 cycles;
+
+ cycles = clk_get_rate(bpc->clk);
+ cycles *= period_ns;
+ do_div(cycles, NSEC_PER_SEC);
+
+ if (cycles > BERLIN_PWM_MAX_TCNT) {
+ prescale_4096 = true;
+ cycles >>= 12; // Prescaled by 4096
+
+ if (cycles > BERLIN_PWM_MAX_TCNT)
+ return -ERANGE;
+ }
+
+ period = cycles;
+ cycles *= duty_ns;
+ do_div(cycles, period_ns);
+ duty = cycles;
+
+ value = berlin_pwm_readl(bpc, pwm->hwpwm, BERLIN_PWM_CONTROL);
+ if (prescale_4096)
+ value |= BERLIN_PWM_PRESCALE_4096;
+ else
+ value &= ~BERLIN_PWM_PRESCALE_4096;
+ berlin_pwm_writel(bpc, pwm->hwpwm, value, BERLIN_PWM_CONTROL);
+
+ berlin_pwm_writel(bpc, pwm->hwpwm, duty, BERLIN_PWM_DUTY);
+ berlin_pwm_writel(bpc, pwm->hwpwm, period, BERLIN_PWM_TCNT);
+
+ return 0;
+}
+
+static int berlin_pwm_set_polarity(struct pwm_chip *chip,
+ struct pwm_device *pwm,
+ enum pwm_polarity polarity)
+{
+ struct berlin_pwm_chip *bpc = to_berlin_pwm_chip(chip);
+ u32 value;
+
+ value = berlin_pwm_readl(bpc, pwm->hwpwm, BERLIN_PWM_CONTROL);
+
+ if (polarity == PWM_POLARITY_NORMAL)
+ value &= ~BERLIN_PWM_INVERT_POLARITY;
+ else
+ value |= BERLIN_PWM_INVERT_POLARITY;
+
+ berlin_pwm_writel(bpc, pwm->hwpwm, value, BERLIN_PWM_CONTROL);
+
+ return 0;
+}
+
+static int berlin_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct berlin_pwm_chip *bpc = to_berlin_pwm_chip(chip);
+ u32 value;
+
+ value = berlin_pwm_readl(bpc, pwm->hwpwm, BERLIN_PWM_EN);
+ value |= BERLIN_PWM_ENABLE;
+ berlin_pwm_writel(bpc, pwm->hwpwm, value, BERLIN_PWM_EN);
+
+ return 0;
+}
+
+static void berlin_pwm_disable(struct pwm_chip *chip,
+ struct pwm_device *pwm)
+{
+ struct berlin_pwm_chip *bpc = to_berlin_pwm_chip(chip);
+ u32 value;
+
+ value = berlin_pwm_readl(bpc, pwm->hwpwm, BERLIN_PWM_EN);
+ value &= ~BERLIN_PWM_ENABLE;
+ berlin_pwm_writel(bpc, pwm->hwpwm, value, BERLIN_PWM_EN);
+}
+
+static int berlin_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
+ const struct pwm_state *state)
+{
+ int err;
+ bool enabled = pwm->state.enabled;
+
+ if (state->polarity != pwm->state.polarity) {
+ if (enabled) {
+ berlin_pwm_disable(chip, pwm);
+ enabled = false;
+ }
+
+ err = berlin_pwm_set_polarity(chip, pwm, state->polarity);
+ if (err)
+ return err;
+ }
+
+ if (!state->enabled) {
+ if (enabled)
+ berlin_pwm_disable(chip, pwm);
+ return 0;
+ }
+
+ err = berlin_pwm_config(chip, pwm, state->duty_cycle, state->period);
+ if (err)
+ return err;
+
+ if (!enabled)
+ return berlin_pwm_enable(chip, pwm);
+
+ return 0;
+}
+
+static const struct pwm_ops berlin_pwm_ops = {
+ .apply = berlin_pwm_apply,
+};
+
+static const struct of_device_id berlin_pwm_match[] = {
+ { .compatible = "marvell,berlin-pwm" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, berlin_pwm_match);
+
+static int berlin_pwm_probe(struct platform_device *pdev)
+{
+ struct pwm_chip *chip;
+ struct berlin_pwm_chip *bpc;
+ int ret;
+
+ chip = devm_pwmchip_alloc(&pdev->dev, BERLIN_PWM_NUMPWMS, sizeof(*bpc));
+ if (IS_ERR(chip))
+ return PTR_ERR(chip);
+ bpc = to_berlin_pwm_chip(chip);
+
+ bpc->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(bpc->base))
+ return PTR_ERR(bpc->base);
+
+ bpc->clk = devm_clk_get_enabled(&pdev->dev, NULL);
+ if (IS_ERR(bpc->clk))
+ return PTR_ERR(bpc->clk);
+
+ chip->ops = &berlin_pwm_ops;
+
+ ret = devm_pwmchip_add(&pdev->dev, chip);
+ if (ret < 0)
+ return dev_err_probe(&pdev->dev, ret, "failed to add PWM chip\n");
+
+ platform_set_drvdata(pdev, chip);
+
+ return 0;
+}
+
+static int berlin_pwm_suspend(struct device *dev)
+{
+ struct pwm_chip *chip = dev_get_drvdata(dev);
+ struct berlin_pwm_chip *bpc = to_berlin_pwm_chip(chip);
+ unsigned int i;
+
+ for (i = 0; i < chip->npwm; i++) {
+ struct berlin_pwm_channel *channel = &bpc->channel[i];
+
+ channel->enable = berlin_pwm_readl(bpc, i, BERLIN_PWM_EN);
+ channel->ctrl = berlin_pwm_readl(bpc, i, BERLIN_PWM_CONTROL);
+ channel->duty = berlin_pwm_readl(bpc, i, BERLIN_PWM_DUTY);
+ channel->tcnt = berlin_pwm_readl(bpc, i, BERLIN_PWM_TCNT);
+ }
+
+ clk_disable_unprepare(bpc->clk);
+
+ return 0;
+}
+
+static int berlin_pwm_resume(struct device *dev)
+{
+ struct pwm_chip *chip = dev_get_drvdata(dev);
+ struct berlin_pwm_chip *bpc = to_berlin_pwm_chip(chip);
+ unsigned int i;
+ int ret;
+
+ ret = clk_prepare_enable(bpc->clk);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < chip->npwm; i++) {
+ struct berlin_pwm_channel *channel = &bpc->channel[i];
+
+ berlin_pwm_writel(bpc, i, channel->ctrl, BERLIN_PWM_CONTROL);
+ berlin_pwm_writel(bpc, i, channel->duty, BERLIN_PWM_DUTY);
+ berlin_pwm_writel(bpc, i, channel->tcnt, BERLIN_PWM_TCNT);
+ berlin_pwm_writel(bpc, i, channel->enable, BERLIN_PWM_EN);
+ }
+
+ return 0;
+}
+
+static DEFINE_SIMPLE_DEV_PM_OPS(berlin_pwm_pm_ops, berlin_pwm_suspend,
+ berlin_pwm_resume);
+
+static struct platform_driver berlin_pwm_driver = {
+ .probe = berlin_pwm_probe,
+ .driver = {
+ .name = "berlin-pwm",
+ .of_match_table = berlin_pwm_match,
+ .pm = pm_ptr(&berlin_pwm_pm_ops),
+ },
+};
+module_platform_driver(berlin_pwm_driver);
+
+MODULE_AUTHOR("Antoine Tenart <antoine.tenart@free-electrons.com>");
+MODULE_DESCRIPTION("Marvell Berlin PWM driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/pwm/pwm-brcmstb.c b/drivers/pwm/pwm-brcmstb.c
new file mode 100644
index 000000000..790ef7ffb
--- /dev/null
+++ b/drivers/pwm/pwm-brcmstb.c
@@ -0,0 +1,293 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Broadcom BCM7038 PWM driver
+ * Author: Florian Fainelli
+ *
+ * Copyright (C) 2015 Broadcom Corporation
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/clk.h>
+#include <linux/export.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+#include <linux/spinlock.h>
+
+#define PWM_CTRL 0x00
+#define CTRL_START BIT(0)
+#define CTRL_OEB BIT(1)
+#define CTRL_FORCE_HIGH BIT(2)
+#define CTRL_OPENDRAIN BIT(3)
+#define CTRL_CHAN_OFFS 4
+
+#define PWM_CTRL2 0x04
+#define CTRL2_OUT_SELECT BIT(0)
+
+#define PWM_CH_SIZE 0x8
+
+#define PWM_CWORD_MSB(ch) (0x08 + ((ch) * PWM_CH_SIZE))
+#define PWM_CWORD_LSB(ch) (0x0c + ((ch) * PWM_CH_SIZE))
+
+/* Number of bits for the CWORD value */
+#define CWORD_BIT_SIZE 16
+
+/*
+ * Maximum control word value allowed when variable-frequency PWM is used as a
+ * clock for the constant-frequency PMW.
+ */
+#define CONST_VAR_F_MAX 32768
+#define CONST_VAR_F_MIN 1
+
+#define PWM_ON(ch) (0x18 + ((ch) * PWM_CH_SIZE))
+#define PWM_ON_MIN 1
+#define PWM_PERIOD(ch) (0x1c + ((ch) * PWM_CH_SIZE))
+#define PWM_PERIOD_MIN 0
+
+#define PWM_ON_PERIOD_MAX 0xff
+
+struct brcmstb_pwm {
+ void __iomem *base;
+ struct clk *clk;
+};
+
+static inline u32 brcmstb_pwm_readl(struct brcmstb_pwm *p,
+ unsigned int offset)
+{
+ if (IS_ENABLED(CONFIG_MIPS) && IS_ENABLED(CONFIG_CPU_BIG_ENDIAN))
+ return __raw_readl(p->base + offset);
+ else
+ return readl_relaxed(p->base + offset);
+}
+
+static inline void brcmstb_pwm_writel(struct brcmstb_pwm *p, u32 value,
+ unsigned int offset)
+{
+ if (IS_ENABLED(CONFIG_MIPS) && IS_ENABLED(CONFIG_CPU_BIG_ENDIAN))
+ __raw_writel(value, p->base + offset);
+ else
+ writel_relaxed(value, p->base + offset);
+}
+
+static inline struct brcmstb_pwm *to_brcmstb_pwm(struct pwm_chip *chip)
+{
+ return pwmchip_get_drvdata(chip);
+}
+
+/*
+ * Fv is derived from the variable frequency output. The variable frequency
+ * output is configured using this formula:
+ *
+ * W = cword, if cword < 2 ^ 15 else 16-bit 2's complement of cword
+ *
+ * Fv = W x 2 ^ -16 x 27Mhz (reference clock)
+ *
+ * The period is: (period + 1) / Fv and "on" time is on / (period + 1)
+ *
+ * The PWM core framework specifies that the "duty_ns" parameter is in fact the
+ * "on" time, so this translates directly into our HW programming here.
+ */
+static int brcmstb_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
+ u64 duty_ns, u64 period_ns)
+{
+ struct brcmstb_pwm *p = to_brcmstb_pwm(chip);
+ unsigned long pc, dc, cword = CONST_VAR_F_MAX;
+ unsigned int channel = pwm->hwpwm;
+ u32 value;
+
+ /*
+ * If asking for a duty_ns equal to period_ns, we need to substract
+ * the period value by 1 to make it shorter than the "on" time and
+ * produce a flat 100% duty cycle signal, and max out the "on" time
+ */
+ if (duty_ns == period_ns) {
+ dc = PWM_ON_PERIOD_MAX;
+ pc = PWM_ON_PERIOD_MAX - 1;
+ goto done;
+ }
+
+ while (1) {
+ u64 rate;
+
+ /*
+ * Calculate the base rate from base frequency and current
+ * cword
+ */
+ rate = (u64)clk_get_rate(p->clk) * (u64)cword;
+ rate >>= CWORD_BIT_SIZE;
+
+ pc = mul_u64_u64_div_u64(period_ns, rate, NSEC_PER_SEC);
+ dc = mul_u64_u64_div_u64(duty_ns + 1, rate, NSEC_PER_SEC);
+
+ /*
+ * We can be called with separate duty and period updates,
+ * so do not reject dc == 0 right away
+ */
+ if (pc == PWM_PERIOD_MIN || (dc < PWM_ON_MIN && duty_ns))
+ return -EINVAL;
+
+ /* We converged on a calculation */
+ if (pc <= PWM_ON_PERIOD_MAX && dc <= PWM_ON_PERIOD_MAX)
+ break;
+
+ /*
+ * The cword needs to be a power of 2 for the variable
+ * frequency generator to output a 50% duty cycle variable
+ * frequency which is used as input clock to the fixed
+ * frequency generator.
+ */
+ cword >>= 1;
+
+ /*
+ * Desired periods are too large, we do not have a divider
+ * for them
+ */
+ if (cword < CONST_VAR_F_MIN)
+ return -EINVAL;
+ }
+
+done:
+ /*
+ * Configure the defined "cword" value to have the variable frequency
+ * generator output a base frequency for the constant frequency
+ * generator to derive from.
+ */
+ brcmstb_pwm_writel(p, cword >> 8, PWM_CWORD_MSB(channel));
+ brcmstb_pwm_writel(p, cword & 0xff, PWM_CWORD_LSB(channel));
+
+ /* Select constant frequency signal output */
+ value = brcmstb_pwm_readl(p, PWM_CTRL2);
+ value |= CTRL2_OUT_SELECT << (channel * CTRL_CHAN_OFFS);
+ brcmstb_pwm_writel(p, value, PWM_CTRL2);
+
+ /* Configure on and period value */
+ brcmstb_pwm_writel(p, pc, PWM_PERIOD(channel));
+ brcmstb_pwm_writel(p, dc, PWM_ON(channel));
+
+ return 0;
+}
+
+static inline void brcmstb_pwm_enable_set(struct brcmstb_pwm *p,
+ unsigned int channel, bool enable)
+{
+ unsigned int shift = channel * CTRL_CHAN_OFFS;
+ u32 value;
+
+ value = brcmstb_pwm_readl(p, PWM_CTRL);
+
+ if (enable) {
+ value &= ~(CTRL_OEB << shift);
+ value |= (CTRL_START | CTRL_OPENDRAIN) << shift;
+ } else {
+ value &= ~((CTRL_START | CTRL_OPENDRAIN) << shift);
+ value |= CTRL_OEB << shift;
+ }
+
+ brcmstb_pwm_writel(p, value, PWM_CTRL);
+}
+
+static int brcmstb_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
+ const struct pwm_state *state)
+{
+ struct brcmstb_pwm *p = to_brcmstb_pwm(chip);
+ int err;
+
+ if (state->polarity != PWM_POLARITY_NORMAL)
+ return -EINVAL;
+
+ if (!state->enabled) {
+ if (pwm->state.enabled)
+ brcmstb_pwm_enable_set(p, pwm->hwpwm, false);
+
+ return 0;
+ }
+
+ err = brcmstb_pwm_config(chip, pwm, state->duty_cycle, state->period);
+ if (err)
+ return err;
+
+ if (!pwm->state.enabled)
+ brcmstb_pwm_enable_set(p, pwm->hwpwm, true);
+
+ return 0;
+}
+
+static const struct pwm_ops brcmstb_pwm_ops = {
+ .apply = brcmstb_pwm_apply,
+};
+
+static const struct of_device_id brcmstb_pwm_of_match[] = {
+ { .compatible = "brcm,bcm7038-pwm" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, brcmstb_pwm_of_match);
+
+static int brcmstb_pwm_probe(struct platform_device *pdev)
+{
+ struct pwm_chip *chip;
+ struct brcmstb_pwm *p;
+ int ret;
+
+ chip = devm_pwmchip_alloc(&pdev->dev, 2, sizeof(*p));
+ if (IS_ERR(chip))
+ return PTR_ERR(chip);
+ p = to_brcmstb_pwm(chip);
+
+ p->clk = devm_clk_get_enabled(&pdev->dev, NULL);
+ if (IS_ERR(p->clk))
+ return dev_err_probe(&pdev->dev, PTR_ERR(p->clk),
+ "failed to obtain clock\n");
+
+ platform_set_drvdata(pdev, p);
+
+ chip->ops = &brcmstb_pwm_ops;
+
+ p->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(p->base))
+ return PTR_ERR(p->base);
+
+ ret = devm_pwmchip_add(&pdev->dev, chip);
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret, "failed to add PWM chip\n");
+
+ return 0;
+}
+
+static int brcmstb_pwm_suspend(struct device *dev)
+{
+ struct brcmstb_pwm *p = dev_get_drvdata(dev);
+
+ clk_disable_unprepare(p->clk);
+
+ return 0;
+}
+
+static int brcmstb_pwm_resume(struct device *dev)
+{
+ struct brcmstb_pwm *p = dev_get_drvdata(dev);
+
+ return clk_prepare_enable(p->clk);
+}
+
+static DEFINE_SIMPLE_DEV_PM_OPS(brcmstb_pwm_pm_ops, brcmstb_pwm_suspend,
+ brcmstb_pwm_resume);
+
+static struct platform_driver brcmstb_pwm_driver = {
+ .probe = brcmstb_pwm_probe,
+ .driver = {
+ .name = "pwm-brcmstb",
+ .of_match_table = brcmstb_pwm_of_match,
+ .pm = pm_ptr(&brcmstb_pwm_pm_ops),
+ },
+};
+module_platform_driver(brcmstb_pwm_driver);
+
+MODULE_AUTHOR("Florian Fainelli <f.fainelli@gmail.com>");
+MODULE_DESCRIPTION("Broadcom STB PWM driver");
+MODULE_ALIAS("platform:pwm-brcmstb");
+MODULE_LICENSE("GPL");
diff --git a/drivers/pwm/pwm-clk.c b/drivers/pwm/pwm-clk.c
new file mode 100644
index 000000000..1907f2f10
--- /dev/null
+++ b/drivers/pwm/pwm-clk.c
@@ -0,0 +1,140 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Clock based PWM controller
+ *
+ * Copyright (c) 2021 Nikita Travkin <nikita@trvn.ru>
+ *
+ * This is an "adapter" driver that allows PWM consumers to use
+ * system clocks with duty cycle control as PWM outputs.
+ *
+ * Limitations:
+ * - Due to the fact that exact behavior depends on the underlying
+ * clock driver, various limitations are possible.
+ * - Underlying clock may not be able to give 0% or 100% duty cycle
+ * (constant off or on), exact behavior will depend on the clock.
+ * - When the PWM is disabled, the clock will be disabled as well,
+ * line state will depend on the clock.
+ * - The clk API doesn't expose the necessary calls to implement
+ * .get_state().
+ */
+
+#include <linux/kernel.h>
+#include <linux/math64.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/pwm.h>
+
+struct pwm_clk_chip {
+ struct clk *clk;
+ bool clk_enabled;
+};
+
+static inline struct pwm_clk_chip *to_pwm_clk_chip(struct pwm_chip *chip)
+{
+ return pwmchip_get_drvdata(chip);
+}
+
+static int pwm_clk_apply(struct pwm_chip *chip, struct pwm_device *pwm,
+ const struct pwm_state *state)
+{
+ struct pwm_clk_chip *pcchip = to_pwm_clk_chip(chip);
+ int ret;
+ u32 rate;
+ u64 period = state->period;
+ u64 duty_cycle = state->duty_cycle;
+
+ if (!state->enabled) {
+ if (pwm->state.enabled) {
+ clk_disable(pcchip->clk);
+ pcchip->clk_enabled = false;
+ }
+ return 0;
+ } else if (!pwm->state.enabled) {
+ ret = clk_enable(pcchip->clk);
+ if (ret)
+ return ret;
+ pcchip->clk_enabled = true;
+ }
+
+ /*
+ * We have to enable the clk before setting the rate and duty_cycle,
+ * that however results in a window where the clk is on with a
+ * (potentially) different setting. Also setting period and duty_cycle
+ * are two separate calls, so that probably isn't atomic either.
+ */
+
+ rate = DIV64_U64_ROUND_UP(NSEC_PER_SEC, period);
+ ret = clk_set_rate(pcchip->clk, rate);
+ if (ret)
+ return ret;
+
+ if (state->polarity == PWM_POLARITY_INVERSED)
+ duty_cycle = period - duty_cycle;
+
+ return clk_set_duty_cycle(pcchip->clk, duty_cycle, period);
+}
+
+static const struct pwm_ops pwm_clk_ops = {
+ .apply = pwm_clk_apply,
+};
+
+static int pwm_clk_probe(struct platform_device *pdev)
+{
+ struct pwm_chip *chip;
+ struct pwm_clk_chip *pcchip;
+ int ret;
+
+ chip = devm_pwmchip_alloc(&pdev->dev, 1, sizeof(*pcchip));
+ if (IS_ERR(chip))
+ return PTR_ERR(chip);
+ pcchip = to_pwm_clk_chip(chip);
+
+ pcchip->clk = devm_clk_get_prepared(&pdev->dev, NULL);
+ if (IS_ERR(pcchip->clk))
+ return dev_err_probe(&pdev->dev, PTR_ERR(pcchip->clk),
+ "Failed to get clock\n");
+
+ chip->ops = &pwm_clk_ops;
+
+ ret = pwmchip_add(chip);
+ if (ret < 0)
+ return dev_err_probe(&pdev->dev, ret, "Failed to add pwm chip\n");
+
+ platform_set_drvdata(pdev, chip);
+ return 0;
+}
+
+static void pwm_clk_remove(struct platform_device *pdev)
+{
+ struct pwm_chip *chip = platform_get_drvdata(pdev);
+ struct pwm_clk_chip *pcchip = to_pwm_clk_chip(chip);
+
+ pwmchip_remove(chip);
+
+ if (pcchip->clk_enabled)
+ clk_disable(pcchip->clk);
+}
+
+static const struct of_device_id pwm_clk_dt_ids[] = {
+ { .compatible = "clk-pwm" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, pwm_clk_dt_ids);
+
+static struct platform_driver pwm_clk_driver = {
+ .driver = {
+ .name = "pwm-clk",
+ .of_match_table = pwm_clk_dt_ids,
+ },
+ .probe = pwm_clk_probe,
+ .remove = pwm_clk_remove,
+};
+module_platform_driver(pwm_clk_driver);
+
+MODULE_ALIAS("platform:pwm-clk");
+MODULE_AUTHOR("Nikita Travkin <nikita@trvn.ru>");
+MODULE_DESCRIPTION("Clock based PWM driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/pwm/pwm-clps711x.c b/drivers/pwm/pwm-clps711x.c
new file mode 100644
index 000000000..1ce12ee03
--- /dev/null
+++ b/drivers/pwm/pwm-clps711x.c
@@ -0,0 +1,110 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Cirrus Logic CLPS711X PWM driver
+ * Author: Alexander Shiyan <shc_work@mail.ru>
+ */
+
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+
+struct clps711x_chip {
+ void __iomem *pmpcon;
+ struct clk *clk;
+};
+
+static inline struct clps711x_chip *to_clps711x_chip(struct pwm_chip *chip)
+{
+ return pwmchip_get_drvdata(chip);
+}
+
+static int clps711x_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct clps711x_chip *priv = to_clps711x_chip(chip);
+ unsigned int freq = clk_get_rate(priv->clk);
+
+ if (!freq)
+ return -EINVAL;
+
+ /* Store constant period value */
+ pwm->args.period = DIV_ROUND_CLOSEST(NSEC_PER_SEC, freq);
+
+ return 0;
+}
+
+static int clps711x_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
+ const struct pwm_state *state)
+{
+ struct clps711x_chip *priv = to_clps711x_chip(chip);
+ /* PWM0 - bits 4..7, PWM1 - bits 8..11 */
+ u32 shift = (pwm->hwpwm + 1) * 4;
+ u32 pmpcon, val;
+
+ if (state->polarity != PWM_POLARITY_NORMAL)
+ return -EINVAL;
+
+ if (state->period != pwm->args.period)
+ return -EINVAL;
+
+ if (state->enabled)
+ val = mul_u64_u64_div_u64(state->duty_cycle, 0xf, state->period);
+ else
+ val = 0;
+
+ pmpcon = readl(priv->pmpcon);
+ pmpcon &= ~(0xf << shift);
+ pmpcon |= val << shift;
+ writel(pmpcon, priv->pmpcon);
+
+ return 0;
+}
+
+static const struct pwm_ops clps711x_pwm_ops = {
+ .request = clps711x_pwm_request,
+ .apply = clps711x_pwm_apply,
+};
+
+static int clps711x_pwm_probe(struct platform_device *pdev)
+{
+ struct pwm_chip *chip;
+ struct clps711x_chip *priv;
+
+ chip = devm_pwmchip_alloc(&pdev->dev, 2, sizeof(*priv));
+ if (IS_ERR(chip))
+ return PTR_ERR(chip);
+ priv = to_clps711x_chip(chip);
+
+ priv->pmpcon = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(priv->pmpcon))
+ return PTR_ERR(priv->pmpcon);
+
+ priv->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(priv->clk))
+ return PTR_ERR(priv->clk);
+
+ chip->ops = &clps711x_pwm_ops;
+
+ return devm_pwmchip_add(&pdev->dev, chip);
+}
+
+static const struct of_device_id clps711x_pwm_dt_ids[] = {
+ { .compatible = "cirrus,ep7209-pwm" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, clps711x_pwm_dt_ids);
+
+static struct platform_driver clps711x_pwm_driver = {
+ .driver = {
+ .name = "clps711x-pwm",
+ .of_match_table = clps711x_pwm_dt_ids,
+ },
+ .probe = clps711x_pwm_probe,
+};
+module_platform_driver(clps711x_pwm_driver);
+
+MODULE_AUTHOR("Alexander Shiyan <shc_work@mail.ru>");
+MODULE_DESCRIPTION("Cirrus Logic CLPS711X PWM driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/pwm/pwm-crc.c b/drivers/pwm/pwm-crc.c
new file mode 100644
index 000000000..98ee5cdbd
--- /dev/null
+++ b/drivers/pwm/pwm-crc.c
@@ -0,0 +1,189 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2015 Intel Corporation. All rights reserved.
+ *
+ * Author: Shobhit Kumar <shobhit.kumar@intel.com>
+ */
+
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/mfd/intel_soc_pmic.h>
+#include <linux/pwm.h>
+
+#define PWM0_CLK_DIV 0x4B
+#define PWM_OUTPUT_ENABLE BIT(7)
+#define PWM_DIV_CLK_0 0x00 /* DIVIDECLK = BASECLK */
+#define PWM_DIV_CLK_100 0x63 /* DIVIDECLK = BASECLK/100 */
+#define PWM_DIV_CLK_128 0x7F /* DIVIDECLK = BASECLK/128 */
+
+#define PWM0_DUTY_CYCLE 0x4E
+#define BACKLIGHT_EN 0x51
+
+#define PWM_MAX_LEVEL 0xFF
+
+#define PWM_BASE_CLK_MHZ 6 /* 6 MHz */
+#define PWM_MAX_PERIOD_NS 5461334 /* 183 Hz */
+
+/**
+ * struct crystalcove_pwm - Crystal Cove PWM controller
+ * @regmap: the regmap from the parent device.
+ */
+struct crystalcove_pwm {
+ struct regmap *regmap;
+};
+
+static inline struct crystalcove_pwm *to_crc_pwm(struct pwm_chip *chip)
+{
+ return pwmchip_get_drvdata(chip);
+}
+
+static int crc_pwm_calc_clk_div(int period_ns)
+{
+ int clk_div;
+
+ clk_div = PWM_BASE_CLK_MHZ * period_ns / (256 * NSEC_PER_USEC);
+ /* clk_div 1 - 128, maps to register values 0-127 */
+ if (clk_div > 0)
+ clk_div--;
+
+ return clk_div;
+}
+
+static int crc_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
+ const struct pwm_state *state)
+{
+ struct crystalcove_pwm *crc_pwm = to_crc_pwm(chip);
+ struct device *dev = pwmchip_parent(chip);
+ int err;
+
+ if (state->period > PWM_MAX_PERIOD_NS) {
+ dev_err(dev, "un-supported period_ns\n");
+ return -EINVAL;
+ }
+
+ if (state->polarity != PWM_POLARITY_NORMAL)
+ return -EINVAL;
+
+ if (pwm_is_enabled(pwm) && !state->enabled) {
+ err = regmap_write(crc_pwm->regmap, BACKLIGHT_EN, 0);
+ if (err) {
+ dev_err(dev, "Error writing BACKLIGHT_EN %d\n", err);
+ return err;
+ }
+ }
+
+ if (pwm_get_duty_cycle(pwm) != state->duty_cycle ||
+ pwm_get_period(pwm) != state->period) {
+ u64 level = state->duty_cycle * PWM_MAX_LEVEL;
+
+ do_div(level, state->period);
+
+ err = regmap_write(crc_pwm->regmap, PWM0_DUTY_CYCLE, level);
+ if (err) {
+ dev_err(dev, "Error writing PWM0_DUTY_CYCLE %d\n", err);
+ return err;
+ }
+ }
+
+ if (pwm_is_enabled(pwm) && state->enabled &&
+ pwm_get_period(pwm) != state->period) {
+ /* changing the clk divisor, clear PWM_OUTPUT_ENABLE first */
+ err = regmap_write(crc_pwm->regmap, PWM0_CLK_DIV, 0);
+ if (err) {
+ dev_err(dev, "Error writing PWM0_CLK_DIV %d\n", err);
+ return err;
+ }
+ }
+
+ if (pwm_get_period(pwm) != state->period ||
+ pwm_is_enabled(pwm) != state->enabled) {
+ int clk_div = crc_pwm_calc_clk_div(state->period);
+ int pwm_output_enable = state->enabled ? PWM_OUTPUT_ENABLE : 0;
+
+ err = regmap_write(crc_pwm->regmap, PWM0_CLK_DIV,
+ clk_div | pwm_output_enable);
+ if (err) {
+ dev_err(dev, "Error writing PWM0_CLK_DIV %d\n", err);
+ return err;
+ }
+ }
+
+ if (!pwm_is_enabled(pwm) && state->enabled) {
+ err = regmap_write(crc_pwm->regmap, BACKLIGHT_EN, 1);
+ if (err) {
+ dev_err(dev, "Error writing BACKLIGHT_EN %d\n", err);
+ return err;
+ }
+ }
+
+ return 0;
+}
+
+static int crc_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm,
+ struct pwm_state *state)
+{
+ struct crystalcove_pwm *crc_pwm = to_crc_pwm(chip);
+ struct device *dev = pwmchip_parent(chip);
+ unsigned int clk_div, clk_div_reg, duty_cycle_reg;
+ int error;
+
+ error = regmap_read(crc_pwm->regmap, PWM0_CLK_DIV, &clk_div_reg);
+ if (error) {
+ dev_err(dev, "Error reading PWM0_CLK_DIV %d\n", error);
+ return error;
+ }
+
+ error = regmap_read(crc_pwm->regmap, PWM0_DUTY_CYCLE, &duty_cycle_reg);
+ if (error) {
+ dev_err(dev, "Error reading PWM0_DUTY_CYCLE %d\n", error);
+ return error;
+ }
+
+ clk_div = (clk_div_reg & ~PWM_OUTPUT_ENABLE) + 1;
+
+ state->period =
+ DIV_ROUND_UP(clk_div * NSEC_PER_USEC * 256, PWM_BASE_CLK_MHZ);
+ state->duty_cycle =
+ DIV_ROUND_UP_ULL(duty_cycle_reg * state->period, PWM_MAX_LEVEL);
+ state->polarity = PWM_POLARITY_NORMAL;
+ state->enabled = !!(clk_div_reg & PWM_OUTPUT_ENABLE);
+
+ return 0;
+}
+
+static const struct pwm_ops crc_pwm_ops = {
+ .apply = crc_pwm_apply,
+ .get_state = crc_pwm_get_state,
+};
+
+static int crystalcove_pwm_probe(struct platform_device *pdev)
+{
+ struct pwm_chip *chip;
+ struct crystalcove_pwm *crc_pwm;
+ struct device *dev = pdev->dev.parent;
+ struct intel_soc_pmic *pmic = dev_get_drvdata(dev);
+
+ chip = devm_pwmchip_alloc(&pdev->dev, 1, sizeof(*crc_pwm));
+ if (IS_ERR(chip))
+ return PTR_ERR(chip);
+ crc_pwm = to_crc_pwm(chip);
+
+ chip->ops = &crc_pwm_ops;
+
+ /* get the PMIC regmap */
+ crc_pwm->regmap = pmic->regmap;
+
+ return devm_pwmchip_add(&pdev->dev, chip);
+}
+
+static struct platform_driver crystalcove_pwm_driver = {
+ .probe = crystalcove_pwm_probe,
+ .driver = {
+ .name = "crystal_cove_pwm",
+ },
+};
+module_platform_driver(crystalcove_pwm_driver);
+
+MODULE_ALIAS("platform:crystal_cove_pwm");
+MODULE_DESCRIPTION("Intel Crystalcove (CRC) PWM support");
+MODULE_LICENSE("GPL");
diff --git a/drivers/pwm/pwm-cros-ec.c b/drivers/pwm/pwm-cros-ec.c
new file mode 100644
index 000000000..08a24a45c
--- /dev/null
+++ b/drivers/pwm/pwm-cros-ec.c
@@ -0,0 +1,286 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Expose a PWM controlled by the ChromeOS EC to the host processor.
+ *
+ * Copyright (C) 2016 Google, Inc.
+ */
+
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_data/cros_ec_commands.h>
+#include <linux/platform_data/cros_ec_proto.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+#include <linux/slab.h>
+
+#include <dt-bindings/mfd/cros_ec.h>
+
+/**
+ * struct cros_ec_pwm_device - Driver data for EC PWM
+ *
+ * @ec: Pointer to EC device
+ * @use_pwm_type: Use PWM types instead of generic channels
+ */
+struct cros_ec_pwm_device {
+ struct cros_ec_device *ec;
+ bool use_pwm_type;
+};
+
+static inline struct cros_ec_pwm_device *pwm_to_cros_ec_pwm(struct pwm_chip *chip)
+{
+ return pwmchip_get_drvdata(chip);
+}
+
+static int cros_ec_dt_type_to_pwm_type(u8 dt_index, u8 *pwm_type)
+{
+ switch (dt_index) {
+ case CROS_EC_PWM_DT_KB_LIGHT:
+ *pwm_type = EC_PWM_TYPE_KB_LIGHT;
+ return 0;
+ case CROS_EC_PWM_DT_DISPLAY_LIGHT:
+ *pwm_type = EC_PWM_TYPE_DISPLAY_LIGHT;
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int cros_ec_pwm_set_duty(struct cros_ec_pwm_device *ec_pwm, u8 index,
+ u16 duty)
+{
+ struct cros_ec_device *ec = ec_pwm->ec;
+ TRAILING_OVERLAP(struct cros_ec_command, msg, data,
+ struct ec_params_pwm_set_duty params;
+ ) __packed buf;
+ struct ec_params_pwm_set_duty *params = &buf.params;
+ struct cros_ec_command *msg = &buf.msg;
+ int ret;
+
+ memset(&buf, 0, sizeof(buf));
+
+ msg->version = 0;
+ msg->command = EC_CMD_PWM_SET_DUTY;
+ msg->insize = 0;
+ msg->outsize = sizeof(*params);
+
+ params->duty = duty;
+
+ if (ec_pwm->use_pwm_type) {
+ ret = cros_ec_dt_type_to_pwm_type(index, &params->pwm_type);
+ if (ret) {
+ dev_err(ec->dev, "Invalid PWM type index: %d\n", index);
+ return ret;
+ }
+ params->index = 0;
+ } else {
+ params->pwm_type = EC_PWM_TYPE_GENERIC;
+ params->index = index;
+ }
+
+ return cros_ec_cmd_xfer_status(ec, msg);
+}
+
+static int cros_ec_pwm_get_duty(struct cros_ec_device *ec, bool use_pwm_type, u8 index)
+{
+ TRAILING_OVERLAP(struct cros_ec_command, msg, data,
+ union {
+ struct ec_params_pwm_get_duty params;
+ struct ec_response_pwm_get_duty resp;
+ };
+ ) __packed buf;
+ struct ec_params_pwm_get_duty *params = &buf.params;
+ struct ec_response_pwm_get_duty *resp = &buf.resp;
+ struct cros_ec_command *msg = &buf.msg;
+ int ret;
+
+ memset(&buf, 0, sizeof(buf));
+
+ msg->version = 0;
+ msg->command = EC_CMD_PWM_GET_DUTY;
+ msg->insize = sizeof(*resp);
+ msg->outsize = sizeof(*params);
+
+ if (use_pwm_type) {
+ ret = cros_ec_dt_type_to_pwm_type(index, &params->pwm_type);
+ if (ret) {
+ dev_err(ec->dev, "Invalid PWM type index: %d\n", index);
+ return ret;
+ }
+ params->index = 0;
+ } else {
+ params->pwm_type = EC_PWM_TYPE_GENERIC;
+ params->index = index;
+ }
+
+ ret = cros_ec_cmd_xfer_status(ec, msg);
+ if (ret < 0)
+ return ret;
+
+ return resp->duty;
+}
+
+static int cros_ec_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
+ const struct pwm_state *state)
+{
+ struct cros_ec_pwm_device *ec_pwm = pwm_to_cros_ec_pwm(chip);
+ u16 duty_cycle;
+ int ret;
+
+ /* The EC won't let us change the period */
+ if (state->period != EC_PWM_MAX_DUTY)
+ return -EINVAL;
+
+ if (state->polarity != PWM_POLARITY_NORMAL)
+ return -EINVAL;
+
+ /*
+ * EC doesn't separate the concept of duty cycle and enabled, but
+ * kernel does. Translate.
+ */
+ duty_cycle = state->enabled ? state->duty_cycle : 0;
+
+ ret = cros_ec_pwm_set_duty(ec_pwm, pwm->hwpwm, duty_cycle);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int cros_ec_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm,
+ struct pwm_state *state)
+{
+ struct cros_ec_pwm_device *ec_pwm = pwm_to_cros_ec_pwm(chip);
+ int ret;
+
+ ret = cros_ec_pwm_get_duty(ec_pwm->ec, ec_pwm->use_pwm_type, pwm->hwpwm);
+ if (ret < 0) {
+ dev_err(pwmchip_parent(chip), "error getting initial duty: %d\n", ret);
+ return ret;
+ }
+
+ state->enabled = (ret > 0);
+ state->duty_cycle = ret;
+ state->period = EC_PWM_MAX_DUTY;
+ state->polarity = PWM_POLARITY_NORMAL;
+
+ return 0;
+}
+
+static const struct pwm_ops cros_ec_pwm_ops = {
+ .get_state = cros_ec_pwm_get_state,
+ .apply = cros_ec_pwm_apply,
+};
+
+/*
+ * Determine the number of supported PWMs. The EC does not return the number
+ * of PWMs it supports directly, so we have to read the pwm duty cycle for
+ * subsequent channels until we get an error.
+ */
+static int cros_ec_num_pwms(struct cros_ec_device *ec)
+{
+ int i, ret;
+
+ /* The index field is only 8 bits */
+ for (i = 0; i <= U8_MAX; i++) {
+ /*
+ * Note that this function is only called when use_pwm_type is
+ * false. With use_pwm_type == true the number of PWMs is fixed.
+ */
+ ret = cros_ec_pwm_get_duty(ec, false, i);
+ /*
+ * We look for SUCCESS, INVALID_COMMAND, or INVALID_PARAM
+ * responses; everything else is treated as an error.
+ * The EC error codes map to -EOPNOTSUPP and -EINVAL,
+ * so check for those.
+ */
+ switch (ret) {
+ case -EOPNOTSUPP: /* invalid command */
+ return -ENODEV;
+ case -EINVAL: /* invalid parameter */
+ return i;
+ default:
+ if (ret < 0)
+ return ret;
+ break;
+ }
+ }
+
+ return U8_MAX;
+}
+
+static int cros_ec_pwm_probe(struct platform_device *pdev)
+{
+ struct cros_ec_device *ec = dev_get_drvdata(pdev->dev.parent);
+ struct device *dev = &pdev->dev;
+ struct device_node *np = pdev->dev.of_node;
+ struct cros_ec_pwm_device *ec_pwm;
+ struct pwm_chip *chip;
+ bool use_pwm_type = false;
+ unsigned int i, npwm;
+ int ret;
+
+ if (!ec)
+ return dev_err_probe(dev, -EINVAL, "no parent EC device\n");
+
+ if (of_device_is_compatible(np, "google,cros-ec-pwm-type")) {
+ use_pwm_type = true;
+ npwm = CROS_EC_PWM_DT_COUNT;
+ } else {
+ ret = cros_ec_num_pwms(ec);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "Couldn't find PWMs\n");
+ npwm = ret;
+ }
+
+ chip = devm_pwmchip_alloc(dev, npwm, sizeof(*ec_pwm));
+ if (IS_ERR(chip))
+ return PTR_ERR(chip);
+
+ ec_pwm = pwm_to_cros_ec_pwm(chip);
+ ec_pwm->use_pwm_type = use_pwm_type;
+ ec_pwm->ec = ec;
+
+ /* PWM chip */
+ chip->ops = &cros_ec_pwm_ops;
+
+ /*
+ * The device tree binding for this device is special as it only uses a
+ * single cell (for the hwid) and so doesn't provide a default period.
+ * This isn't a big problem though as the hardware only supports a
+ * single period length, it's just a bit ugly to make this fit into the
+ * pwm core abstractions. So initialize the period here, as
+ * of_pwm_xlate_with_flags() won't do that for us.
+ */
+ for (i = 0; i < npwm; ++i)
+ chip->pwms[i].args.period = EC_PWM_MAX_DUTY;
+
+ dev_dbg(dev, "Probed %u PWMs\n", chip->npwm);
+
+ ret = devm_pwmchip_add(dev, chip);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "cannot register PWM\n");
+
+ return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id cros_ec_pwm_of_match[] = {
+ { .compatible = "google,cros-ec-pwm" },
+ { .compatible = "google,cros-ec-pwm-type" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, cros_ec_pwm_of_match);
+#endif
+
+static struct platform_driver cros_ec_pwm_driver = {
+ .probe = cros_ec_pwm_probe,
+ .driver = {
+ .name = "cros-ec-pwm",
+ .of_match_table = of_match_ptr(cros_ec_pwm_of_match),
+ },
+};
+module_platform_driver(cros_ec_pwm_driver);
+
+MODULE_ALIAS("platform:cros-ec-pwm");
+MODULE_DESCRIPTION("ChromeOS EC PWM driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/pwm/pwm-dwc-core.c b/drivers/pwm/pwm-dwc-core.c
new file mode 100644
index 000000000..6dabec93a
--- /dev/null
+++ b/drivers/pwm/pwm-dwc-core.c
@@ -0,0 +1,183 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * DesignWare PWM Controller driver core
+ *
+ * Copyright (C) 2018-2020 Intel Corporation
+ *
+ * Author: Felipe Balbi (Intel)
+ * Author: Jarkko Nikula <jarkko.nikula@linux.intel.com>
+ * Author: Raymond Tan <raymond.tan@intel.com>
+ */
+
+#define DEFAULT_SYMBOL_NAMESPACE "dwc_pwm"
+
+#include <linux/bitops.h>
+#include <linux/export.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/pm_runtime.h>
+#include <linux/pwm.h>
+
+#include "pwm-dwc.h"
+
+static void __dwc_pwm_set_enable(struct dwc_pwm *dwc, int pwm, int enabled)
+{
+ u32 reg;
+
+ reg = dwc_pwm_readl(dwc, DWC_TIM_CTRL(pwm));
+
+ if (enabled)
+ reg |= DWC_TIM_CTRL_EN;
+ else
+ reg &= ~DWC_TIM_CTRL_EN;
+
+ dwc_pwm_writel(dwc, reg, DWC_TIM_CTRL(pwm));
+}
+
+static int __dwc_pwm_configure_timer(struct dwc_pwm *dwc,
+ struct pwm_device *pwm,
+ const struct pwm_state *state)
+{
+ u64 tmp;
+ u32 ctrl;
+ u32 high;
+ u32 low;
+
+ /*
+ * Calculate width of low and high period in terms of input clock
+ * periods and check are the result within HW limits between 1 and
+ * 2^32 periods.
+ */
+ tmp = DIV_ROUND_CLOSEST_ULL(state->duty_cycle, dwc->clk_ns);
+ if (tmp < 1 || tmp > (1ULL << 32))
+ return -ERANGE;
+ low = tmp - 1;
+
+ tmp = DIV_ROUND_CLOSEST_ULL(state->period - state->duty_cycle,
+ dwc->clk_ns);
+ if (tmp < 1 || tmp > (1ULL << 32))
+ return -ERANGE;
+ high = tmp - 1;
+
+ /*
+ * Specification says timer usage flow is to disable timer, then
+ * program it followed by enable. It also says Load Count is loaded
+ * into timer after it is enabled - either after a disable or
+ * a reset. Based on measurements it happens also without disable
+ * whenever Load Count is updated. But follow the specification.
+ */
+ __dwc_pwm_set_enable(dwc, pwm->hwpwm, false);
+
+ /*
+ * Write Load Count and Load Count 2 registers. Former defines the
+ * width of low period and latter the width of high period in terms
+ * multiple of input clock periods:
+ * Width = ((Count + 1) * input clock period).
+ */
+ dwc_pwm_writel(dwc, low, DWC_TIM_LD_CNT(pwm->hwpwm));
+ dwc_pwm_writel(dwc, high, DWC_TIM_LD_CNT2(pwm->hwpwm));
+
+ /*
+ * Set user-defined mode, timer reloads from Load Count registers
+ * when it counts down to 0.
+ * Set PWM mode, it makes output to toggle and width of low and high
+ * periods are set by Load Count registers.
+ */
+ ctrl = DWC_TIM_CTRL_MODE_USER | DWC_TIM_CTRL_PWM;
+ dwc_pwm_writel(dwc, ctrl, DWC_TIM_CTRL(pwm->hwpwm));
+
+ /*
+ * Enable timer. Output starts from low period.
+ */
+ __dwc_pwm_set_enable(dwc, pwm->hwpwm, state->enabled);
+
+ return 0;
+}
+
+static int dwc_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
+ const struct pwm_state *state)
+{
+ struct dwc_pwm *dwc = to_dwc_pwm(chip);
+
+ if (state->polarity != PWM_POLARITY_INVERSED)
+ return -EINVAL;
+
+ if (state->enabled) {
+ if (!pwm->state.enabled)
+ pm_runtime_get_sync(pwmchip_parent(chip));
+ return __dwc_pwm_configure_timer(dwc, pwm, state);
+ } else {
+ if (pwm->state.enabled) {
+ __dwc_pwm_set_enable(dwc, pwm->hwpwm, false);
+ pm_runtime_put_sync(pwmchip_parent(chip));
+ }
+ }
+
+ return 0;
+}
+
+static int dwc_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm,
+ struct pwm_state *state)
+{
+ struct dwc_pwm *dwc = to_dwc_pwm(chip);
+ u64 duty, period;
+ u32 ctrl, ld, ld2;
+
+ pm_runtime_get_sync(pwmchip_parent(chip));
+
+ ctrl = dwc_pwm_readl(dwc, DWC_TIM_CTRL(pwm->hwpwm));
+ ld = dwc_pwm_readl(dwc, DWC_TIM_LD_CNT(pwm->hwpwm));
+ ld2 = dwc_pwm_readl(dwc, DWC_TIM_LD_CNT2(pwm->hwpwm));
+
+ state->enabled = !!(ctrl & DWC_TIM_CTRL_EN);
+
+ /*
+ * If we're not in PWM, technically the output is a 50-50
+ * based on the timer load-count only.
+ */
+ if (ctrl & DWC_TIM_CTRL_PWM) {
+ duty = (ld + 1) * dwc->clk_ns;
+ period = (ld2 + 1) * dwc->clk_ns;
+ period += duty;
+ } else {
+ duty = (ld + 1) * dwc->clk_ns;
+ period = duty * 2;
+ }
+
+ state->polarity = PWM_POLARITY_INVERSED;
+ state->period = period;
+ state->duty_cycle = duty;
+
+ pm_runtime_put_sync(pwmchip_parent(chip));
+
+ return 0;
+}
+
+static const struct pwm_ops dwc_pwm_ops = {
+ .apply = dwc_pwm_apply,
+ .get_state = dwc_pwm_get_state,
+};
+
+struct pwm_chip *dwc_pwm_alloc(struct device *dev)
+{
+ struct pwm_chip *chip;
+ struct dwc_pwm *dwc;
+
+ chip = devm_pwmchip_alloc(dev, DWC_TIMERS_TOTAL, sizeof(*dwc));
+ if (IS_ERR(chip))
+ return chip;
+ dwc = to_dwc_pwm(chip);
+
+ dwc->clk_ns = 10;
+ chip->ops = &dwc_pwm_ops;
+
+ return chip;
+}
+EXPORT_SYMBOL_GPL(dwc_pwm_alloc);
+
+MODULE_AUTHOR("Felipe Balbi (Intel)");
+MODULE_AUTHOR("Jarkko Nikula <jarkko.nikula@linux.intel.com>");
+MODULE_AUTHOR("Raymond Tan <raymond.tan@intel.com>");
+MODULE_DESCRIPTION("DesignWare PWM Controller");
+MODULE_LICENSE("GPL");
diff --git a/drivers/pwm/pwm-dwc.c b/drivers/pwm/pwm-dwc.c
new file mode 100644
index 000000000..8ba7ef2c2
--- /dev/null
+++ b/drivers/pwm/pwm-dwc.c
@@ -0,0 +1,171 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * DesignWare PWM Controller driver (PCI part)
+ *
+ * Copyright (C) 2018-2020 Intel Corporation
+ *
+ * Author: Felipe Balbi (Intel)
+ * Author: Jarkko Nikula <jarkko.nikula@linux.intel.com>
+ * Author: Raymond Tan <raymond.tan@intel.com>
+ *
+ * Limitations:
+ * - The hardware cannot generate a 0 % or 100 % duty cycle. Both high and low
+ * periods are one or more input clock periods long.
+ */
+
+#define DEFAULT_MOUDLE_NAMESPACE dwc_pwm
+
+#include <linux/bitops.h>
+#include <linux/export.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/pm_runtime.h>
+#include <linux/pwm.h>
+#include <linux/sizes.h>
+
+#include "pwm-dwc.h"
+
+/* Elkhart Lake */
+static const struct dwc_pwm_info ehl_pwm_info = {
+ .nr = 2,
+ .size = SZ_4K,
+};
+
+static int dwc_pwm_init_one(struct device *dev, struct dwc_pwm_drvdata *ddata, unsigned int idx)
+{
+ struct pwm_chip *chip;
+ struct dwc_pwm *dwc;
+ int ret;
+
+ chip = dwc_pwm_alloc(dev);
+ if (IS_ERR(chip))
+ return PTR_ERR(chip);
+
+ dwc = to_dwc_pwm(chip);
+ dwc->base = ddata->io_base + (ddata->info->size * idx);
+
+ ret = devm_pwmchip_add(dev, chip);
+ if (ret)
+ return ret;
+
+ ddata->chips[idx] = chip;
+ return 0;
+}
+
+static int dwc_pwm_probe(struct pci_dev *pci, const struct pci_device_id *id)
+{
+ const struct dwc_pwm_info *info;
+ struct device *dev = &pci->dev;
+ struct dwc_pwm_drvdata *ddata;
+ unsigned int idx;
+ int ret;
+
+ ret = pcim_enable_device(pci);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to enable device\n");
+
+ pci_set_master(pci);
+
+ info = (const struct dwc_pwm_info *)id->driver_data;
+ ddata = devm_kzalloc(dev, struct_size(ddata, chips, info->nr), GFP_KERNEL);
+ if (!ddata)
+ return -ENOMEM;
+
+ ddata->io_base = pcim_iomap_region(pci, 0, "pwm-dwc");
+ if (IS_ERR(ddata->io_base))
+ return dev_err_probe(dev, PTR_ERR(ddata->io_base),
+ "Failed to request / iomap PCI BAR\n");
+
+ ddata->info = info;
+
+ for (idx = 0; idx < ddata->info->nr; idx++) {
+ ret = dwc_pwm_init_one(dev, ddata, idx);
+ if (ret)
+ return ret;
+ }
+
+ dev_set_drvdata(dev, ddata);
+
+ pm_runtime_put(dev);
+ pm_runtime_allow(dev);
+
+ return 0;
+}
+
+static void dwc_pwm_remove(struct pci_dev *pci)
+{
+ pm_runtime_forbid(&pci->dev);
+ pm_runtime_get_noresume(&pci->dev);
+}
+
+static int dwc_pwm_suspend(struct device *dev)
+{
+ struct dwc_pwm_drvdata *ddata = dev_get_drvdata(dev);
+ unsigned int idx;
+
+ for (idx = 0; idx < ddata->info->nr; idx++) {
+ struct pwm_chip *chip = ddata->chips[idx];
+ struct dwc_pwm *dwc = to_dwc_pwm(chip);
+ unsigned int i;
+
+ for (i = 0; i < DWC_TIMERS_TOTAL; i++) {
+ if (chip->pwms[i].state.enabled) {
+ dev_err(dev, "PWM %u in use by consumer (%s)\n",
+ i, chip->pwms[i].label);
+ return -EBUSY;
+ }
+ dwc->ctx[i].cnt = dwc_pwm_readl(dwc, DWC_TIM_LD_CNT(i));
+ dwc->ctx[i].cnt2 = dwc_pwm_readl(dwc, DWC_TIM_LD_CNT2(i));
+ dwc->ctx[i].ctrl = dwc_pwm_readl(dwc, DWC_TIM_CTRL(i));
+ }
+ }
+
+ return 0;
+}
+
+static int dwc_pwm_resume(struct device *dev)
+{
+ struct dwc_pwm_drvdata *ddata = dev_get_drvdata(dev);
+ unsigned int idx;
+
+ for (idx = 0; idx < ddata->info->nr; idx++) {
+ struct pwm_chip *chip = ddata->chips[idx];
+ struct dwc_pwm *dwc = to_dwc_pwm(chip);
+ unsigned int i;
+
+ for (i = 0; i < DWC_TIMERS_TOTAL; i++) {
+ dwc_pwm_writel(dwc, dwc->ctx[i].cnt, DWC_TIM_LD_CNT(i));
+ dwc_pwm_writel(dwc, dwc->ctx[i].cnt2, DWC_TIM_LD_CNT2(i));
+ dwc_pwm_writel(dwc, dwc->ctx[i].ctrl, DWC_TIM_CTRL(i));
+ }
+ }
+
+ return 0;
+}
+
+static DEFINE_SIMPLE_DEV_PM_OPS(dwc_pwm_pm_ops, dwc_pwm_suspend, dwc_pwm_resume);
+
+static const struct pci_device_id dwc_pwm_id_table[] = {
+ { PCI_VDEVICE(INTEL, 0x4bb7), .driver_data = (kernel_ulong_t)&ehl_pwm_info },
+ { } /* Terminating Entry */
+};
+MODULE_DEVICE_TABLE(pci, dwc_pwm_id_table);
+
+static struct pci_driver dwc_pwm_driver = {
+ .name = "pwm-dwc",
+ .probe = dwc_pwm_probe,
+ .remove = dwc_pwm_remove,
+ .id_table = dwc_pwm_id_table,
+ .driver = {
+ .pm = pm_sleep_ptr(&dwc_pwm_pm_ops),
+ },
+};
+
+module_pci_driver(dwc_pwm_driver);
+
+MODULE_AUTHOR("Felipe Balbi (Intel)");
+MODULE_AUTHOR("Jarkko Nikula <jarkko.nikula@linux.intel.com>");
+MODULE_AUTHOR("Raymond Tan <raymond.tan@intel.com>");
+MODULE_DESCRIPTION("DesignWare PWM Controller");
+MODULE_LICENSE("GPL");
diff --git a/drivers/pwm/pwm-dwc.h b/drivers/pwm/pwm-dwc.h
new file mode 100644
index 000000000..1562594e7
--- /dev/null
+++ b/drivers/pwm/pwm-dwc.h
@@ -0,0 +1,74 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * DesignWare PWM Controller driver
+ *
+ * Copyright (C) 2018-2020 Intel Corporation
+ *
+ * Author: Felipe Balbi (Intel)
+ * Author: Jarkko Nikula <jarkko.nikula@linux.intel.com>
+ * Author: Raymond Tan <raymond.tan@intel.com>
+ */
+
+MODULE_IMPORT_NS("dwc_pwm");
+
+#define DWC_TIM_LD_CNT(n) ((n) * 0x14)
+#define DWC_TIM_LD_CNT2(n) (((n) * 4) + 0xb0)
+#define DWC_TIM_CUR_VAL(n) (((n) * 0x14) + 0x04)
+#define DWC_TIM_CTRL(n) (((n) * 0x14) + 0x08)
+#define DWC_TIM_EOI(n) (((n) * 0x14) + 0x0c)
+#define DWC_TIM_INT_STS(n) (((n) * 0x14) + 0x10)
+
+#define DWC_TIMERS_INT_STS 0xa0
+#define DWC_TIMERS_EOI 0xa4
+#define DWC_TIMERS_RAW_INT_STS 0xa8
+#define DWC_TIMERS_COMP_VERSION 0xac
+
+#define DWC_TIMERS_TOTAL 8
+
+/* Timer Control Register */
+#define DWC_TIM_CTRL_EN BIT(0)
+#define DWC_TIM_CTRL_MODE BIT(1)
+#define DWC_TIM_CTRL_MODE_FREE (0 << 1)
+#define DWC_TIM_CTRL_MODE_USER (1 << 1)
+#define DWC_TIM_CTRL_INT_MASK BIT(2)
+#define DWC_TIM_CTRL_PWM BIT(3)
+
+struct dwc_pwm_info {
+ unsigned int nr;
+ unsigned int size;
+};
+
+struct dwc_pwm_drvdata {
+ const struct dwc_pwm_info *info;
+ void __iomem *io_base;
+ struct pwm_chip *chips[];
+};
+
+struct dwc_pwm_ctx {
+ u32 cnt;
+ u32 cnt2;
+ u32 ctrl;
+};
+
+struct dwc_pwm {
+ void __iomem *base;
+ unsigned int clk_ns;
+ struct dwc_pwm_ctx ctx[DWC_TIMERS_TOTAL];
+};
+
+static inline struct dwc_pwm *to_dwc_pwm(struct pwm_chip *chip)
+{
+ return pwmchip_get_drvdata(chip);
+}
+
+static inline u32 dwc_pwm_readl(struct dwc_pwm *dwc, u32 offset)
+{
+ return readl(dwc->base + offset);
+}
+
+static inline void dwc_pwm_writel(struct dwc_pwm *dwc, u32 value, u32 offset)
+{
+ writel(value, dwc->base + offset);
+}
+
+extern struct pwm_chip *dwc_pwm_alloc(struct device *dev);
diff --git a/drivers/pwm/pwm-ep93xx.c b/drivers/pwm/pwm-ep93xx.c
new file mode 100644
index 000000000..3cc1e4008
--- /dev/null
+++ b/drivers/pwm/pwm-ep93xx.c
@@ -0,0 +1,192 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * PWM framework driver for Cirrus Logic EP93xx
+ *
+ * Copyright (c) 2009 Matthieu Crapet <mcrapet@gmail.com>
+ * Copyright (c) 2009, 2013 H Hartley Sweeten <hsweeten@visionengravers.com>
+ *
+ * EP9301/02 have only one channel:
+ * platform device ep93xx-pwm.1 - PWMOUT1 (EGPIO14)
+ *
+ * EP9307 has only one channel:
+ * platform device ep93xx-pwm.0 - PWMOUT
+ *
+ * EP9312/15 have two channels:
+ * platform device ep93xx-pwm.0 - PWMOUT
+ * platform device ep93xx-pwm.1 - PWMOUT1 (EGPIO14)
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/pwm.h>
+
+#include <asm/div64.h>
+
+#define EP93XX_PWMx_TERM_COUNT 0x00
+#define EP93XX_PWMx_DUTY_CYCLE 0x04
+#define EP93XX_PWMx_ENABLE 0x08
+#define EP93XX_PWMx_INVERT 0x0c
+
+struct ep93xx_pwm {
+ void __iomem *base;
+ struct clk *clk;
+};
+
+static inline struct ep93xx_pwm *to_ep93xx_pwm(struct pwm_chip *chip)
+{
+ return pwmchip_get_drvdata(chip);
+}
+
+static int ep93xx_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
+ const struct pwm_state *state)
+{
+ int ret;
+ struct ep93xx_pwm *ep93xx_pwm = to_ep93xx_pwm(chip);
+ bool enabled = state->enabled;
+ void __iomem *base = ep93xx_pwm->base;
+ unsigned long long c;
+ unsigned long period_cycles;
+ unsigned long duty_cycles;
+ unsigned long term;
+
+ if (state->polarity != pwm->state.polarity) {
+ if (enabled) {
+ writew(0x0, ep93xx_pwm->base + EP93XX_PWMx_ENABLE);
+ clk_disable_unprepare(ep93xx_pwm->clk);
+ enabled = false;
+ }
+
+ /*
+ * The clock needs to be enabled to access the PWM registers.
+ * Polarity can only be changed when the PWM is disabled.
+ */
+ ret = clk_prepare_enable(ep93xx_pwm->clk);
+ if (ret)
+ return ret;
+
+ if (state->polarity == PWM_POLARITY_INVERSED)
+ writew(0x1, ep93xx_pwm->base + EP93XX_PWMx_INVERT);
+ else
+ writew(0x0, ep93xx_pwm->base + EP93XX_PWMx_INVERT);
+
+ clk_disable_unprepare(ep93xx_pwm->clk);
+ }
+
+ if (!state->enabled) {
+ if (enabled) {
+ writew(0x0, ep93xx_pwm->base + EP93XX_PWMx_ENABLE);
+ clk_disable_unprepare(ep93xx_pwm->clk);
+ }
+
+ return 0;
+ }
+
+ /*
+ * The clock needs to be enabled to access the PWM registers.
+ * Configuration can be changed at any time.
+ */
+ if (!pwm_is_enabled(pwm)) {
+ ret = clk_prepare_enable(ep93xx_pwm->clk);
+ if (ret)
+ return ret;
+ }
+
+ c = clk_get_rate(ep93xx_pwm->clk);
+ c *= state->period;
+ do_div(c, 1000000000);
+ period_cycles = c;
+
+ c = period_cycles;
+ c *= state->duty_cycle;
+ do_div(c, state->period);
+ duty_cycles = c;
+
+ if (period_cycles < 0x10000 && duty_cycles < 0x10000) {
+ term = readw(base + EP93XX_PWMx_TERM_COUNT);
+
+ /* Order is important if PWM is running */
+ if (period_cycles > term) {
+ writew(period_cycles, base + EP93XX_PWMx_TERM_COUNT);
+ writew(duty_cycles, base + EP93XX_PWMx_DUTY_CYCLE);
+ } else {
+ writew(duty_cycles, base + EP93XX_PWMx_DUTY_CYCLE);
+ writew(period_cycles, base + EP93XX_PWMx_TERM_COUNT);
+ }
+ ret = 0;
+ } else {
+ ret = -EINVAL;
+ }
+
+ if (!pwm_is_enabled(pwm))
+ clk_disable_unprepare(ep93xx_pwm->clk);
+
+ if (ret)
+ return ret;
+
+ if (!enabled) {
+ ret = clk_prepare_enable(ep93xx_pwm->clk);
+ if (ret)
+ return ret;
+
+ writew(0x1, ep93xx_pwm->base + EP93XX_PWMx_ENABLE);
+ }
+
+ return 0;
+}
+
+static const struct pwm_ops ep93xx_pwm_ops = {
+ .apply = ep93xx_pwm_apply,
+};
+
+static int ep93xx_pwm_probe(struct platform_device *pdev)
+{
+ struct pwm_chip *chip;
+ struct ep93xx_pwm *ep93xx_pwm;
+ int ret;
+
+ chip = devm_pwmchip_alloc(&pdev->dev, 1, sizeof(*ep93xx_pwm));
+ if (IS_ERR(chip))
+ return PTR_ERR(chip);
+ ep93xx_pwm = to_ep93xx_pwm(chip);
+
+ ep93xx_pwm->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(ep93xx_pwm->base))
+ return PTR_ERR(ep93xx_pwm->base);
+
+ ep93xx_pwm->clk = devm_clk_get(&pdev->dev, "pwm_clk");
+ if (IS_ERR(ep93xx_pwm->clk))
+ return PTR_ERR(ep93xx_pwm->clk);
+
+ chip->ops = &ep93xx_pwm_ops;
+
+ ret = devm_pwmchip_add(&pdev->dev, chip);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static const struct of_device_id ep93xx_pwm_of_ids[] = {
+ { .compatible = "cirrus,ep9301-pwm" },
+ { /* sentinel */}
+};
+MODULE_DEVICE_TABLE(of, ep93xx_pwm_of_ids);
+
+static struct platform_driver ep93xx_pwm_driver = {
+ .driver = {
+ .name = "ep93xx-pwm",
+ .of_match_table = ep93xx_pwm_of_ids,
+ },
+ .probe = ep93xx_pwm_probe,
+};
+module_platform_driver(ep93xx_pwm_driver);
+
+MODULE_DESCRIPTION("Cirrus Logic EP93xx PWM driver");
+MODULE_AUTHOR("Matthieu Crapet <mcrapet@gmail.com>");
+MODULE_AUTHOR("H Hartley Sweeten <hsweeten@visionengravers.com>");
+MODULE_ALIAS("platform:ep93xx-pwm");
+MODULE_LICENSE("GPL");
diff --git a/drivers/pwm/pwm-fsl-ftm.c b/drivers/pwm/pwm-fsl-ftm.c
new file mode 100644
index 000000000..35406b2e1
--- /dev/null
+++ b/drivers/pwm/pwm-fsl-ftm.c
@@ -0,0 +1,572 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Freescale FlexTimer Module (FTM) PWM Driver
+ *
+ * Copyright 2012-2013 Freescale Semiconductor, Inc.
+ * Copyright 2020-2025 NXP
+ */
+
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pm.h>
+#include <linux/pwm.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/fsl/ftm.h>
+
+#define FTM_SC_CLK(c) (((c) + 1) << FTM_SC_CLK_MASK_SHIFT)
+
+enum fsl_pwm_clk {
+ FSL_PWM_CLK_SYS,
+ FSL_PWM_CLK_FIX,
+ FSL_PWM_CLK_EXT,
+ FSL_PWM_CLK_CNTEN,
+ FSL_PWM_CLK_MAX
+};
+
+struct fsl_ftm_soc {
+ bool has_enable_bits;
+ bool has_flt_reg;
+ unsigned int npwm;
+};
+
+struct fsl_pwm_periodcfg {
+ enum fsl_pwm_clk clk_select;
+ unsigned int clk_ps;
+ unsigned int mod_period;
+};
+
+struct fsl_pwm_chip {
+ struct regmap *regmap;
+
+ /* This value is valid iff a pwm is running */
+ struct fsl_pwm_periodcfg period;
+
+ struct clk *ipg_clk;
+ struct clk *clk[FSL_PWM_CLK_MAX];
+
+ const struct fsl_ftm_soc *soc;
+};
+
+static inline struct fsl_pwm_chip *to_fsl_chip(struct pwm_chip *chip)
+{
+ return pwmchip_get_drvdata(chip);
+}
+
+static void ftm_clear_write_protection(struct fsl_pwm_chip *fpc)
+{
+ u32 val;
+
+ regmap_read(fpc->regmap, FTM_FMS, &val);
+ if (val & FTM_FMS_WPEN)
+ regmap_set_bits(fpc->regmap, FTM_MODE, FTM_MODE_WPDIS);
+}
+
+static void ftm_set_write_protection(struct fsl_pwm_chip *fpc)
+{
+ regmap_set_bits(fpc->regmap, FTM_FMS, FTM_FMS_WPEN);
+}
+
+static bool fsl_pwm_periodcfg_are_equal(const struct fsl_pwm_periodcfg *a,
+ const struct fsl_pwm_periodcfg *b)
+{
+ if (a->clk_select != b->clk_select)
+ return false;
+ if (a->clk_ps != b->clk_ps)
+ return false;
+ if (a->mod_period != b->mod_period)
+ return false;
+ return true;
+}
+
+static int fsl_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ int ret;
+ struct fsl_pwm_chip *fpc = to_fsl_chip(chip);
+
+ ret = clk_prepare_enable(fpc->ipg_clk);
+ if (!ret && fpc->soc->has_enable_bits)
+ regmap_set_bits(fpc->regmap, FTM_SC, BIT(pwm->hwpwm + 16));
+
+ return ret;
+}
+
+static void fsl_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct fsl_pwm_chip *fpc = to_fsl_chip(chip);
+
+ if (fpc->soc->has_enable_bits)
+ regmap_clear_bits(fpc->regmap, FTM_SC, BIT(pwm->hwpwm + 16));
+
+ clk_disable_unprepare(fpc->ipg_clk);
+}
+
+static unsigned int fsl_pwm_ticks_to_ns(struct fsl_pwm_chip *fpc,
+ unsigned int ticks)
+{
+ unsigned long rate;
+ unsigned long long exval;
+
+ rate = clk_get_rate(fpc->clk[fpc->period.clk_select]);
+ if (rate >> fpc->period.clk_ps == 0)
+ return 0;
+
+ exval = ticks;
+ exval *= 1000000000UL;
+ do_div(exval, rate >> fpc->period.clk_ps);
+ return exval;
+}
+
+static bool fsl_pwm_calculate_period_clk(struct fsl_pwm_chip *fpc,
+ unsigned int period_ns,
+ enum fsl_pwm_clk index,
+ struct fsl_pwm_periodcfg *periodcfg
+ )
+{
+ unsigned long long c;
+ unsigned int ps;
+
+ c = clk_get_rate(fpc->clk[index]);
+ c = c * period_ns;
+ do_div(c, 1000000000UL);
+
+ if (c == 0)
+ return false;
+
+ for (ps = 0; ps < 8 ; ++ps, c >>= 1) {
+ if (c <= 0x10000) {
+ periodcfg->clk_select = index;
+ periodcfg->clk_ps = ps;
+ periodcfg->mod_period = c - 1;
+ return true;
+ }
+ }
+ return false;
+}
+
+static bool fsl_pwm_calculate_period(struct fsl_pwm_chip *fpc,
+ unsigned int period_ns,
+ struct fsl_pwm_periodcfg *periodcfg)
+{
+ enum fsl_pwm_clk m0, m1;
+ unsigned long fix_rate, ext_rate;
+ bool ret;
+
+ ret = fsl_pwm_calculate_period_clk(fpc, period_ns, FSL_PWM_CLK_SYS,
+ periodcfg);
+ if (ret)
+ return true;
+
+ fix_rate = clk_get_rate(fpc->clk[FSL_PWM_CLK_FIX]);
+ ext_rate = clk_get_rate(fpc->clk[FSL_PWM_CLK_EXT]);
+
+ if (fix_rate > ext_rate) {
+ m0 = FSL_PWM_CLK_FIX;
+ m1 = FSL_PWM_CLK_EXT;
+ } else {
+ m0 = FSL_PWM_CLK_EXT;
+ m1 = FSL_PWM_CLK_FIX;
+ }
+
+ ret = fsl_pwm_calculate_period_clk(fpc, period_ns, m0, periodcfg);
+ if (ret)
+ return true;
+
+ return fsl_pwm_calculate_period_clk(fpc, period_ns, m1, periodcfg);
+}
+
+static unsigned int fsl_pwm_calculate_duty(struct fsl_pwm_chip *fpc,
+ unsigned int duty_ns)
+{
+ unsigned long long duty;
+
+ unsigned int period = fpc->period.mod_period + 1;
+ unsigned int period_ns = fsl_pwm_ticks_to_ns(fpc, period);
+
+ if (!period_ns)
+ return 0;
+
+ duty = (unsigned long long)duty_ns * period;
+ do_div(duty, period_ns);
+
+ return (unsigned int)duty;
+}
+
+static bool fsl_pwm_is_any_pwm_enabled(struct fsl_pwm_chip *fpc,
+ struct pwm_device *pwm)
+{
+ u32 val;
+
+ regmap_read(fpc->regmap, FTM_OUTMASK, &val);
+ if (~val & 0xFF)
+ return true;
+ else
+ return false;
+}
+
+static bool fsl_pwm_is_other_pwm_enabled(struct fsl_pwm_chip *fpc,
+ struct pwm_device *pwm)
+{
+ u32 val;
+
+ regmap_read(fpc->regmap, FTM_OUTMASK, &val);
+ if (~(val | BIT(pwm->hwpwm)) & 0xFF)
+ return true;
+ else
+ return false;
+}
+
+static int fsl_pwm_apply_config(struct pwm_chip *chip,
+ struct pwm_device *pwm,
+ const struct pwm_state *newstate)
+{
+ struct fsl_pwm_chip *fpc = to_fsl_chip(chip);
+ unsigned int duty;
+ u32 reg_polarity;
+
+ struct fsl_pwm_periodcfg periodcfg;
+ bool do_write_period = false;
+
+ if (!fsl_pwm_calculate_period(fpc, newstate->period, &periodcfg)) {
+ dev_err(pwmchip_parent(chip), "failed to calculate new period\n");
+ return -EINVAL;
+ }
+
+ if (!fsl_pwm_is_any_pwm_enabled(fpc, pwm))
+ do_write_period = true;
+ /*
+ * The Freescale FTM controller supports only a single period for
+ * all PWM channels, therefore verify if the newly computed period
+ * is different than the current period being used. In such case
+ * we allow to change the period only if no other pwm is running.
+ */
+ else if (!fsl_pwm_periodcfg_are_equal(&fpc->period, &periodcfg)) {
+ if (fsl_pwm_is_other_pwm_enabled(fpc, pwm)) {
+ dev_err(pwmchip_parent(chip),
+ "Cannot change period for PWM %u, disable other PWMs first\n",
+ pwm->hwpwm);
+ return -EBUSY;
+ }
+ if (fpc->period.clk_select != periodcfg.clk_select) {
+ int ret;
+ enum fsl_pwm_clk oldclk = fpc->period.clk_select;
+ enum fsl_pwm_clk newclk = periodcfg.clk_select;
+
+ ret = clk_prepare_enable(fpc->clk[newclk]);
+ if (ret)
+ return ret;
+ clk_disable_unprepare(fpc->clk[oldclk]);
+ }
+ do_write_period = true;
+ }
+
+ ftm_clear_write_protection(fpc);
+
+ if (do_write_period) {
+ regmap_update_bits(fpc->regmap, FTM_SC, FTM_SC_CLK_MASK,
+ FTM_SC_CLK(periodcfg.clk_select));
+ regmap_update_bits(fpc->regmap, FTM_SC, FTM_SC_PS_MASK,
+ periodcfg.clk_ps);
+ regmap_write(fpc->regmap, FTM_MOD, periodcfg.mod_period);
+
+ fpc->period = periodcfg;
+ }
+
+ duty = fsl_pwm_calculate_duty(fpc, newstate->duty_cycle);
+
+ regmap_write(fpc->regmap, FTM_CSC(pwm->hwpwm),
+ FTM_CSC_MSB | FTM_CSC_ELSB);
+ regmap_write(fpc->regmap, FTM_CV(pwm->hwpwm), duty);
+
+ reg_polarity = 0;
+ if (newstate->polarity == PWM_POLARITY_INVERSED)
+ reg_polarity = BIT(pwm->hwpwm);
+
+ regmap_update_bits(fpc->regmap, FTM_POL, BIT(pwm->hwpwm), reg_polarity);
+
+ ftm_set_write_protection(fpc);
+
+ return 0;
+}
+
+static int fsl_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
+ const struct pwm_state *newstate)
+{
+ struct fsl_pwm_chip *fpc = to_fsl_chip(chip);
+ struct pwm_state *oldstate = &pwm->state;
+ int ret;
+
+ /*
+ * oldstate to newstate : action
+ *
+ * disabled to disabled : ignore
+ * enabled to disabled : disable
+ * enabled to enabled : update settings
+ * disabled to enabled : update settings + enable
+ */
+
+ if (!newstate->enabled) {
+ if (oldstate->enabled) {
+ regmap_set_bits(fpc->regmap, FTM_OUTMASK,
+ BIT(pwm->hwpwm));
+ clk_disable_unprepare(fpc->clk[FSL_PWM_CLK_CNTEN]);
+ clk_disable_unprepare(fpc->clk[fpc->period.clk_select]);
+ }
+
+ return 0;
+ }
+
+ ret = fsl_pwm_apply_config(chip, pwm, newstate);
+ if (ret)
+ return ret;
+
+ /* check if need to enable */
+ if (!oldstate->enabled) {
+ ret = clk_prepare_enable(fpc->clk[fpc->period.clk_select]);
+ if (ret)
+ return ret;
+
+ ret = clk_prepare_enable(fpc->clk[FSL_PWM_CLK_CNTEN]);
+ if (ret) {
+ clk_disable_unprepare(fpc->clk[fpc->period.clk_select]);
+ return ret;
+ }
+
+ regmap_clear_bits(fpc->regmap, FTM_OUTMASK, BIT(pwm->hwpwm));
+ }
+
+ return ret;
+}
+
+static const struct pwm_ops fsl_pwm_ops = {
+ .request = fsl_pwm_request,
+ .free = fsl_pwm_free,
+ .apply = fsl_pwm_apply,
+};
+
+static int fsl_pwm_init(struct fsl_pwm_chip *fpc)
+{
+ int ret;
+
+ ret = clk_prepare_enable(fpc->ipg_clk);
+ if (ret)
+ return ret;
+
+ regmap_write(fpc->regmap, FTM_CNTIN, 0x00);
+ regmap_write(fpc->regmap, FTM_OUTINIT, 0x00);
+ regmap_write(fpc->regmap, FTM_OUTMASK, 0xFF);
+
+ clk_disable_unprepare(fpc->ipg_clk);
+
+ return 0;
+}
+
+static bool fsl_pwm_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case FTM_FMS:
+ case FTM_MODE:
+ case FTM_CNT:
+ return true;
+ }
+ return false;
+}
+
+static bool fsl_pwm_is_reg(struct device *dev, unsigned int reg)
+{
+ struct pwm_chip *chip = dev_get_drvdata(dev);
+ struct fsl_pwm_chip *fpc = to_fsl_chip(chip);
+
+ if (reg >= FTM_CSC(fpc->soc->npwm) && reg < FTM_CNTIN)
+ return false;
+
+ if ((reg == FTM_FLTCTRL || reg == FTM_FLTPOL) && !fpc->soc->has_flt_reg)
+ return false;
+
+ return true;
+}
+
+static const struct regmap_config fsl_pwm_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+
+ .max_register = FTM_PWMLOAD,
+ .volatile_reg = fsl_pwm_volatile_reg,
+ .cache_type = REGCACHE_FLAT,
+ .writeable_reg = fsl_pwm_is_reg,
+ .readable_reg = fsl_pwm_is_reg,
+};
+
+static int fsl_pwm_probe(struct platform_device *pdev)
+{
+ const struct fsl_ftm_soc *soc = of_device_get_match_data(&pdev->dev);
+ struct pwm_chip *chip;
+ struct fsl_pwm_chip *fpc;
+ void __iomem *base;
+ int ret;
+
+ chip = devm_pwmchip_alloc(&pdev->dev, soc->npwm, sizeof(*fpc));
+ if (IS_ERR(chip))
+ return PTR_ERR(chip);
+ fpc = to_fsl_chip(chip);
+
+ fpc->soc = soc;
+
+ base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ fpc->regmap = devm_regmap_init_mmio_clk(&pdev->dev, "ftm_sys", base,
+ &fsl_pwm_regmap_config);
+ if (IS_ERR(fpc->regmap)) {
+ dev_err(&pdev->dev, "regmap init failed\n");
+ return PTR_ERR(fpc->regmap);
+ }
+
+ fpc->clk[FSL_PWM_CLK_SYS] = devm_clk_get(&pdev->dev, "ftm_sys");
+ if (IS_ERR(fpc->clk[FSL_PWM_CLK_SYS])) {
+ dev_err(&pdev->dev, "failed to get \"ftm_sys\" clock\n");
+ return PTR_ERR(fpc->clk[FSL_PWM_CLK_SYS]);
+ }
+
+ fpc->clk[FSL_PWM_CLK_FIX] = devm_clk_get(&pdev->dev, "ftm_fix");
+ if (IS_ERR(fpc->clk[FSL_PWM_CLK_FIX]))
+ return PTR_ERR(fpc->clk[FSL_PWM_CLK_FIX]);
+
+ fpc->clk[FSL_PWM_CLK_EXT] = devm_clk_get(&pdev->dev, "ftm_ext");
+ if (IS_ERR(fpc->clk[FSL_PWM_CLK_EXT]))
+ return PTR_ERR(fpc->clk[FSL_PWM_CLK_EXT]);
+
+ fpc->clk[FSL_PWM_CLK_CNTEN] =
+ devm_clk_get(&pdev->dev, "ftm_cnt_clk_en");
+ if (IS_ERR(fpc->clk[FSL_PWM_CLK_CNTEN]))
+ return PTR_ERR(fpc->clk[FSL_PWM_CLK_CNTEN]);
+
+ /*
+ * ipg_clk is the interface clock for the IP. If not provided, use the
+ * ftm_sys clock as the default.
+ */
+ fpc->ipg_clk = devm_clk_get(&pdev->dev, "ipg");
+ if (IS_ERR(fpc->ipg_clk))
+ fpc->ipg_clk = fpc->clk[FSL_PWM_CLK_SYS];
+
+ chip->ops = &fsl_pwm_ops;
+
+ ret = devm_pwmchip_add(&pdev->dev, chip);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to add PWM chip: %d\n", ret);
+ return ret;
+ }
+
+ platform_set_drvdata(pdev, chip);
+
+ return fsl_pwm_init(fpc);
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int fsl_pwm_suspend(struct device *dev)
+{
+ struct pwm_chip *chip = dev_get_drvdata(dev);
+ struct fsl_pwm_chip *fpc = to_fsl_chip(chip);
+ int i;
+
+ regcache_cache_only(fpc->regmap, true);
+ regcache_mark_dirty(fpc->regmap);
+
+ for (i = 0; i < chip->npwm; i++) {
+ struct pwm_device *pwm = &chip->pwms[i];
+
+ if (!test_bit(PWMF_REQUESTED, &pwm->flags))
+ continue;
+
+ clk_disable_unprepare(fpc->ipg_clk);
+
+ if (!pwm_is_enabled(pwm))
+ continue;
+
+ clk_disable_unprepare(fpc->clk[FSL_PWM_CLK_CNTEN]);
+ clk_disable_unprepare(fpc->clk[fpc->period.clk_select]);
+ }
+
+ return 0;
+}
+
+static int fsl_pwm_resume(struct device *dev)
+{
+ struct pwm_chip *chip = dev_get_drvdata(dev);
+ struct fsl_pwm_chip *fpc = to_fsl_chip(chip);
+ int i;
+
+ for (i = 0; i < chip->npwm; i++) {
+ struct pwm_device *pwm = &chip->pwms[i];
+
+ if (!test_bit(PWMF_REQUESTED, &pwm->flags))
+ continue;
+
+ clk_prepare_enable(fpc->ipg_clk);
+
+ if (!pwm_is_enabled(pwm))
+ continue;
+
+ clk_prepare_enable(fpc->clk[fpc->period.clk_select]);
+ clk_prepare_enable(fpc->clk[FSL_PWM_CLK_CNTEN]);
+ }
+
+ /* restore all registers from cache */
+ regcache_cache_only(fpc->regmap, false);
+ regcache_sync(fpc->regmap);
+
+ return 0;
+}
+#endif
+
+static const struct dev_pm_ops fsl_pwm_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(fsl_pwm_suspend, fsl_pwm_resume)
+};
+
+static const struct fsl_ftm_soc vf610_ftm_pwm = {
+ .has_enable_bits = false,
+ .has_flt_reg = true,
+ .npwm = 8,
+};
+
+static const struct fsl_ftm_soc imx8qm_ftm_pwm = {
+ .has_enable_bits = true,
+ .has_flt_reg = true,
+ .npwm = 8,
+};
+
+static const struct fsl_ftm_soc s32g2_ftm_pwm = {
+ .has_enable_bits = true,
+ .has_flt_reg = false,
+ .npwm = 6,
+};
+
+static const struct of_device_id fsl_pwm_dt_ids[] = {
+ { .compatible = "fsl,vf610-ftm-pwm", .data = &vf610_ftm_pwm },
+ { .compatible = "fsl,imx8qm-ftm-pwm", .data = &imx8qm_ftm_pwm },
+ { .compatible = "nxp,s32g2-ftm-pwm", .data = &s32g2_ftm_pwm },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, fsl_pwm_dt_ids);
+
+static struct platform_driver fsl_pwm_driver = {
+ .driver = {
+ .name = "fsl-ftm-pwm",
+ .of_match_table = fsl_pwm_dt_ids,
+ .pm = &fsl_pwm_pm_ops,
+ },
+ .probe = fsl_pwm_probe,
+};
+module_platform_driver(fsl_pwm_driver);
+
+MODULE_DESCRIPTION("Freescale FlexTimer Module PWM Driver");
+MODULE_AUTHOR("Xiubo Li <Li.Xiubo@freescale.com>");
+MODULE_ALIAS("platform:fsl-ftm-pwm");
+MODULE_LICENSE("GPL");
diff --git a/drivers/pwm/pwm-gpio.c b/drivers/pwm/pwm-gpio.c
new file mode 100644
index 000000000..5644d78df
--- /dev/null
+++ b/drivers/pwm/pwm-gpio.c
@@ -0,0 +1,239 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Generic software PWM for modulating GPIOs
+ *
+ * Copyright (C) 2020 Axis Communications AB
+ * Copyright (C) 2020 Nicola Di Lieto
+ * Copyright (C) 2024 Stefan Wahren
+ * Copyright (C) 2024 Linus Walleij
+ */
+
+#include <linux/cleanup.h>
+#include <linux/container_of.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/gpio/consumer.h>
+#include <linux/hrtimer.h>
+#include <linux/math.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <linux/pwm.h>
+#include <linux/spinlock.h>
+#include <linux/time.h>
+#include <linux/types.h>
+
+struct pwm_gpio {
+ struct hrtimer gpio_timer;
+ struct gpio_desc *gpio;
+ struct pwm_state state;
+ struct pwm_state next_state;
+
+ /* Protect internal state between pwm_ops and hrtimer */
+ spinlock_t lock;
+
+ bool changing;
+ bool running;
+ bool level;
+};
+
+static void pwm_gpio_round(struct pwm_state *dest, const struct pwm_state *src)
+{
+ u64 dividend;
+ u32 remainder;
+
+ *dest = *src;
+
+ /* Round down to hrtimer resolution */
+ dividend = dest->period;
+ remainder = do_div(dividend, hrtimer_resolution);
+ dest->period -= remainder;
+
+ dividend = dest->duty_cycle;
+ remainder = do_div(dividend, hrtimer_resolution);
+ dest->duty_cycle -= remainder;
+}
+
+static u64 pwm_gpio_toggle(struct pwm_gpio *gpwm, bool level)
+{
+ const struct pwm_state *state = &gpwm->state;
+ bool invert = state->polarity == PWM_POLARITY_INVERSED;
+
+ gpwm->level = level;
+ gpiod_set_value(gpwm->gpio, gpwm->level ^ invert);
+
+ if (!state->duty_cycle || state->duty_cycle == state->period) {
+ gpwm->running = false;
+ return 0;
+ }
+
+ gpwm->running = true;
+ return level ? state->duty_cycle : state->period - state->duty_cycle;
+}
+
+static enum hrtimer_restart pwm_gpio_timer(struct hrtimer *gpio_timer)
+{
+ struct pwm_gpio *gpwm = container_of(gpio_timer, struct pwm_gpio,
+ gpio_timer);
+ u64 next_toggle;
+ bool new_level;
+
+ guard(spinlock_irqsave)(&gpwm->lock);
+
+ /* Apply new state at end of current period */
+ if (!gpwm->level && gpwm->changing) {
+ gpwm->changing = false;
+ gpwm->state = gpwm->next_state;
+ new_level = !!gpwm->state.duty_cycle;
+ } else {
+ new_level = !gpwm->level;
+ }
+
+ next_toggle = pwm_gpio_toggle(gpwm, new_level);
+ if (next_toggle)
+ hrtimer_forward(gpio_timer, hrtimer_get_expires(gpio_timer),
+ ns_to_ktime(next_toggle));
+
+ return next_toggle ? HRTIMER_RESTART : HRTIMER_NORESTART;
+}
+
+static int pwm_gpio_apply(struct pwm_chip *chip, struct pwm_device *pwm,
+ const struct pwm_state *state)
+{
+ struct pwm_gpio *gpwm = pwmchip_get_drvdata(chip);
+ bool invert = state->polarity == PWM_POLARITY_INVERSED;
+
+ if (state->duty_cycle && state->duty_cycle < hrtimer_resolution)
+ return -EINVAL;
+
+ if (state->duty_cycle != state->period &&
+ (state->period - state->duty_cycle < hrtimer_resolution))
+ return -EINVAL;
+
+ if (!state->enabled) {
+ hrtimer_cancel(&gpwm->gpio_timer);
+ } else if (!gpwm->running) {
+ int ret;
+
+ /*
+ * This just enables the output, but pwm_gpio_toggle()
+ * really starts the duty cycle.
+ */
+ ret = gpiod_direction_output(gpwm->gpio, invert);
+ if (ret)
+ return ret;
+ }
+
+ guard(spinlock_irqsave)(&gpwm->lock);
+
+ if (!state->enabled) {
+ pwm_gpio_round(&gpwm->state, state);
+ gpwm->running = false;
+ gpwm->changing = false;
+
+ gpiod_set_value(gpwm->gpio, invert);
+ } else if (gpwm->running) {
+ pwm_gpio_round(&gpwm->next_state, state);
+ gpwm->changing = true;
+ } else {
+ unsigned long next_toggle;
+
+ pwm_gpio_round(&gpwm->state, state);
+ gpwm->changing = false;
+
+ next_toggle = pwm_gpio_toggle(gpwm, !!state->duty_cycle);
+ if (next_toggle)
+ hrtimer_start(&gpwm->gpio_timer, next_toggle,
+ HRTIMER_MODE_REL);
+ }
+
+ return 0;
+}
+
+static int pwm_gpio_get_state(struct pwm_chip *chip, struct pwm_device *pwm,
+ struct pwm_state *state)
+{
+ struct pwm_gpio *gpwm = pwmchip_get_drvdata(chip);
+
+ guard(spinlock_irqsave)(&gpwm->lock);
+
+ if (gpwm->changing)
+ *state = gpwm->next_state;
+ else
+ *state = gpwm->state;
+
+ return 0;
+}
+
+static const struct pwm_ops pwm_gpio_ops = {
+ .apply = pwm_gpio_apply,
+ .get_state = pwm_gpio_get_state,
+};
+
+static void pwm_gpio_disable_hrtimer(void *data)
+{
+ struct pwm_gpio *gpwm = data;
+
+ hrtimer_cancel(&gpwm->gpio_timer);
+}
+
+static int pwm_gpio_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct pwm_chip *chip;
+ struct pwm_gpio *gpwm;
+ int ret;
+
+ chip = devm_pwmchip_alloc(dev, 1, sizeof(*gpwm));
+ if (IS_ERR(chip))
+ return PTR_ERR(chip);
+
+ gpwm = pwmchip_get_drvdata(chip);
+
+ spin_lock_init(&gpwm->lock);
+
+ gpwm->gpio = devm_gpiod_get(dev, NULL, GPIOD_ASIS);
+ if (IS_ERR(gpwm->gpio))
+ return dev_err_probe(dev, PTR_ERR(gpwm->gpio),
+ "%pfw: could not get gpio\n",
+ dev_fwnode(dev));
+
+ if (gpiod_cansleep(gpwm->gpio))
+ return dev_err_probe(dev, -EINVAL,
+ "%pfw: sleeping GPIO not supported\n",
+ dev_fwnode(dev));
+
+ chip->ops = &pwm_gpio_ops;
+ chip->atomic = true;
+
+ hrtimer_setup(&gpwm->gpio_timer, pwm_gpio_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+
+ ret = devm_add_action_or_reset(dev, pwm_gpio_disable_hrtimer, gpwm);
+ if (ret)
+ return ret;
+
+ ret = pwmchip_add(chip);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "could not add pwmchip\n");
+
+ return 0;
+}
+
+static const struct of_device_id pwm_gpio_dt_ids[] = {
+ { .compatible = "pwm-gpio" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, pwm_gpio_dt_ids);
+
+static struct platform_driver pwm_gpio_driver = {
+ .driver = {
+ .name = "pwm-gpio",
+ .of_match_table = pwm_gpio_dt_ids,
+ },
+ .probe = pwm_gpio_probe,
+};
+module_platform_driver(pwm_gpio_driver);
+
+MODULE_DESCRIPTION("PWM GPIO driver");
+MODULE_AUTHOR("Vincent Whitchurch");
+MODULE_LICENSE("GPL");
diff --git a/drivers/pwm/pwm-hibvt.c b/drivers/pwm/pwm-hibvt.c
new file mode 100644
index 000000000..29ad8d0b5
--- /dev/null
+++ b/drivers/pwm/pwm-hibvt.c
@@ -0,0 +1,285 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * PWM Controller Driver for HiSilicon BVT SoCs
+ *
+ * Copyright (c) 2016 HiSilicon Technologies Co., Ltd.
+ */
+
+#include <linux/bitops.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+#include <linux/reset.h>
+
+#define PWM_CFG0_ADDR(x) (((x) * 0x20) + 0x0)
+#define PWM_CFG1_ADDR(x) (((x) * 0x20) + 0x4)
+#define PWM_CFG2_ADDR(x) (((x) * 0x20) + 0x8)
+#define PWM_CTRL_ADDR(x) (((x) * 0x20) + 0xC)
+
+#define PWM_ENABLE_SHIFT 0
+#define PWM_ENABLE_MASK BIT(0)
+
+#define PWM_POLARITY_SHIFT 1
+#define PWM_POLARITY_MASK BIT(1)
+
+#define PWM_KEEP_SHIFT 2
+#define PWM_KEEP_MASK BIT(2)
+
+#define PWM_PERIOD_MASK GENMASK(31, 0)
+#define PWM_DUTY_MASK GENMASK(31, 0)
+
+struct hibvt_pwm_chip {
+ struct clk *clk;
+ void __iomem *base;
+ struct reset_control *rstc;
+ const struct hibvt_pwm_soc *soc;
+};
+
+struct hibvt_pwm_soc {
+ u32 num_pwms;
+ bool quirk_force_enable;
+};
+
+static const struct hibvt_pwm_soc hi3516cv300_soc_info = {
+ .num_pwms = 4,
+};
+
+static const struct hibvt_pwm_soc hi3519v100_soc_info = {
+ .num_pwms = 8,
+};
+
+static const struct hibvt_pwm_soc hi3559v100_shub_soc_info = {
+ .num_pwms = 8,
+ .quirk_force_enable = true,
+};
+
+static const struct hibvt_pwm_soc hi3559v100_soc_info = {
+ .num_pwms = 2,
+ .quirk_force_enable = true,
+};
+
+static inline struct hibvt_pwm_chip *to_hibvt_pwm_chip(struct pwm_chip *chip)
+{
+ return pwmchip_get_drvdata(chip);
+}
+
+static void hibvt_pwm_set_bits(void __iomem *base, u32 offset,
+ u32 mask, u32 data)
+{
+ void __iomem *address = base + offset;
+ u32 value;
+
+ value = readl(address);
+ value &= ~mask;
+ value |= (data & mask);
+ writel(value, address);
+}
+
+static void hibvt_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct hibvt_pwm_chip *hi_pwm_chip = to_hibvt_pwm_chip(chip);
+
+ hibvt_pwm_set_bits(hi_pwm_chip->base, PWM_CTRL_ADDR(pwm->hwpwm),
+ PWM_ENABLE_MASK, 0x1);
+}
+
+static void hibvt_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct hibvt_pwm_chip *hi_pwm_chip = to_hibvt_pwm_chip(chip);
+
+ hibvt_pwm_set_bits(hi_pwm_chip->base, PWM_CTRL_ADDR(pwm->hwpwm),
+ PWM_ENABLE_MASK, 0x0);
+}
+
+static void hibvt_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
+ int duty_cycle_ns, int period_ns)
+{
+ struct hibvt_pwm_chip *hi_pwm_chip = to_hibvt_pwm_chip(chip);
+ u32 freq, period, duty;
+
+ freq = div_u64(clk_get_rate(hi_pwm_chip->clk), 1000000);
+
+ period = div_u64(freq * period_ns, 1000);
+ duty = div_u64(period * duty_cycle_ns, period_ns);
+
+ hibvt_pwm_set_bits(hi_pwm_chip->base, PWM_CFG0_ADDR(pwm->hwpwm),
+ PWM_PERIOD_MASK, period);
+
+ hibvt_pwm_set_bits(hi_pwm_chip->base, PWM_CFG1_ADDR(pwm->hwpwm),
+ PWM_DUTY_MASK, duty);
+}
+
+static void hibvt_pwm_set_polarity(struct pwm_chip *chip,
+ struct pwm_device *pwm,
+ enum pwm_polarity polarity)
+{
+ struct hibvt_pwm_chip *hi_pwm_chip = to_hibvt_pwm_chip(chip);
+
+ if (polarity == PWM_POLARITY_INVERSED)
+ hibvt_pwm_set_bits(hi_pwm_chip->base, PWM_CTRL_ADDR(pwm->hwpwm),
+ PWM_POLARITY_MASK, (0x1 << PWM_POLARITY_SHIFT));
+ else
+ hibvt_pwm_set_bits(hi_pwm_chip->base, PWM_CTRL_ADDR(pwm->hwpwm),
+ PWM_POLARITY_MASK, (0x0 << PWM_POLARITY_SHIFT));
+}
+
+static int hibvt_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm,
+ struct pwm_state *state)
+{
+ struct hibvt_pwm_chip *hi_pwm_chip = to_hibvt_pwm_chip(chip);
+ void __iomem *base;
+ u32 freq, value;
+
+ freq = div_u64(clk_get_rate(hi_pwm_chip->clk), 1000000);
+ base = hi_pwm_chip->base;
+
+ value = readl(base + PWM_CFG0_ADDR(pwm->hwpwm));
+ state->period = div_u64(value * 1000, freq);
+
+ value = readl(base + PWM_CFG1_ADDR(pwm->hwpwm));
+ state->duty_cycle = div_u64(value * 1000, freq);
+
+ value = readl(base + PWM_CTRL_ADDR(pwm->hwpwm));
+ state->enabled = (PWM_ENABLE_MASK & value);
+ state->polarity = (PWM_POLARITY_MASK & value) ? PWM_POLARITY_INVERSED : PWM_POLARITY_NORMAL;
+
+ return 0;
+}
+
+static int hibvt_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
+ const struct pwm_state *state)
+{
+ struct hibvt_pwm_chip *hi_pwm_chip = to_hibvt_pwm_chip(chip);
+
+ if (state->polarity != pwm->state.polarity)
+ hibvt_pwm_set_polarity(chip, pwm, state->polarity);
+
+ if (state->period != pwm->state.period ||
+ state->duty_cycle != pwm->state.duty_cycle) {
+ hibvt_pwm_config(chip, pwm, state->duty_cycle, state->period);
+
+ /*
+ * Some implementations require the PWM to be enabled twice
+ * each time the duty cycle is refreshed.
+ */
+ if (hi_pwm_chip->soc->quirk_force_enable && state->enabled)
+ hibvt_pwm_enable(chip, pwm);
+ }
+
+ if (state->enabled != pwm->state.enabled) {
+ if (state->enabled)
+ hibvt_pwm_enable(chip, pwm);
+ else
+ hibvt_pwm_disable(chip, pwm);
+ }
+
+ return 0;
+}
+
+static const struct pwm_ops hibvt_pwm_ops = {
+ .get_state = hibvt_pwm_get_state,
+ .apply = hibvt_pwm_apply,
+
+};
+
+static int hibvt_pwm_probe(struct platform_device *pdev)
+{
+ const struct hibvt_pwm_soc *soc =
+ of_device_get_match_data(&pdev->dev);
+ struct pwm_chip *chip;
+ struct hibvt_pwm_chip *hi_pwm_chip;
+ int ret, i;
+
+ chip = devm_pwmchip_alloc(&pdev->dev, soc->num_pwms, sizeof(*hi_pwm_chip));
+ if (IS_ERR(chip))
+ return PTR_ERR(chip);
+ hi_pwm_chip = to_hibvt_pwm_chip(chip);
+
+ hi_pwm_chip->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(hi_pwm_chip->clk)) {
+ dev_err(&pdev->dev, "getting clock failed with %ld\n",
+ PTR_ERR(hi_pwm_chip->clk));
+ return PTR_ERR(hi_pwm_chip->clk);
+ }
+
+ chip->ops = &hibvt_pwm_ops;
+ hi_pwm_chip->soc = soc;
+
+ hi_pwm_chip->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(hi_pwm_chip->base))
+ return PTR_ERR(hi_pwm_chip->base);
+
+ ret = clk_prepare_enable(hi_pwm_chip->clk);
+ if (ret < 0)
+ return ret;
+
+ hi_pwm_chip->rstc = devm_reset_control_get_exclusive(&pdev->dev, NULL);
+ if (IS_ERR(hi_pwm_chip->rstc)) {
+ clk_disable_unprepare(hi_pwm_chip->clk);
+ return PTR_ERR(hi_pwm_chip->rstc);
+ }
+
+ reset_control_assert(hi_pwm_chip->rstc);
+ msleep(30);
+ reset_control_deassert(hi_pwm_chip->rstc);
+
+ ret = pwmchip_add(chip);
+ if (ret < 0) {
+ clk_disable_unprepare(hi_pwm_chip->clk);
+ return ret;
+ }
+
+ for (i = 0; i < chip->npwm; i++) {
+ hibvt_pwm_set_bits(hi_pwm_chip->base, PWM_CTRL_ADDR(i),
+ PWM_KEEP_MASK, (0x1 << PWM_KEEP_SHIFT));
+ }
+
+ platform_set_drvdata(pdev, chip);
+
+ return 0;
+}
+
+static void hibvt_pwm_remove(struct platform_device *pdev)
+{
+ struct pwm_chip *chip = platform_get_drvdata(pdev);
+ struct hibvt_pwm_chip *hi_pwm_chip = to_hibvt_pwm_chip(chip);
+
+ pwmchip_remove(chip);
+
+ reset_control_assert(hi_pwm_chip->rstc);
+ msleep(30);
+ reset_control_deassert(hi_pwm_chip->rstc);
+
+ clk_disable_unprepare(hi_pwm_chip->clk);
+}
+
+static const struct of_device_id hibvt_pwm_of_match[] = {
+ { .compatible = "hisilicon,hi3516cv300-pwm",
+ .data = &hi3516cv300_soc_info },
+ { .compatible = "hisilicon,hi3519v100-pwm",
+ .data = &hi3519v100_soc_info },
+ { .compatible = "hisilicon,hi3559v100-shub-pwm",
+ .data = &hi3559v100_shub_soc_info },
+ { .compatible = "hisilicon,hi3559v100-pwm",
+ .data = &hi3559v100_soc_info },
+ { }
+};
+MODULE_DEVICE_TABLE(of, hibvt_pwm_of_match);
+
+static struct platform_driver hibvt_pwm_driver = {
+ .driver = {
+ .name = "hibvt-pwm",
+ .of_match_table = hibvt_pwm_of_match,
+ },
+ .probe = hibvt_pwm_probe,
+ .remove = hibvt_pwm_remove,
+};
+module_platform_driver(hibvt_pwm_driver);
+
+MODULE_AUTHOR("Jian Yuan");
+MODULE_DESCRIPTION("HiSilicon BVT SoCs PWM driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/pwm/pwm-img.c b/drivers/pwm/pwm-img.c
new file mode 100644
index 000000000..91e0b19f0
--- /dev/null
+++ b/drivers/pwm/pwm-img.c
@@ -0,0 +1,423 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Imagination Technologies Pulse Width Modulator driver
+ *
+ * Copyright (c) 2014-2015, Imagination Technologies
+ *
+ * Based on drivers/pwm/pwm-tegra.c, Copyright (c) 2010, NVIDIA Corporation
+ */
+
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/property.h>
+#include <linux/pwm.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+/* PWM registers */
+#define PWM_CTRL_CFG 0x0000
+#define PWM_CTRL_CFG_NO_SUB_DIV 0
+#define PWM_CTRL_CFG_SUB_DIV0 1
+#define PWM_CTRL_CFG_SUB_DIV1 2
+#define PWM_CTRL_CFG_SUB_DIV0_DIV1 3
+#define PWM_CTRL_CFG_DIV_SHIFT(ch) ((ch) * 2 + 4)
+#define PWM_CTRL_CFG_DIV_MASK 0x3
+
+#define PWM_CH_CFG(ch) (0x4 + (ch) * 4)
+#define PWM_CH_CFG_TMBASE_SHIFT 0
+#define PWM_CH_CFG_DUTY_SHIFT 16
+
+#define PERIP_PWM_PDM_CONTROL 0x0140
+#define PERIP_PWM_PDM_CONTROL_CH_MASK 0x1
+#define PERIP_PWM_PDM_CONTROL_CH_SHIFT(ch) ((ch) * 4)
+
+#define IMG_PWM_PM_TIMEOUT 1000 /* ms */
+
+/*
+ * PWM period is specified with a timebase register,
+ * in number of step periods. The PWM duty cycle is also
+ * specified in step periods, in the [0, $timebase] range.
+ * In other words, the timebase imposes the duty cycle
+ * resolution. Therefore, let's constraint the timebase to
+ * a minimum value to allow a sane range of duty cycle values.
+ * Imposing a minimum timebase, will impose a maximum PWM frequency.
+ *
+ * The value chosen is completely arbitrary.
+ */
+#define MIN_TMBASE_STEPS 16
+
+#define IMG_PWM_NPWM 4
+
+struct img_pwm_soc_data {
+ u32 max_timebase;
+};
+
+struct img_pwm_chip {
+ struct clk *pwm_clk;
+ struct clk *sys_clk;
+ void __iomem *base;
+ struct regmap *periph_regs;
+ int max_period_ns;
+ int min_period_ns;
+ const struct img_pwm_soc_data *data;
+ u32 suspend_ctrl_cfg;
+ u32 suspend_ch_cfg[IMG_PWM_NPWM];
+};
+
+static inline struct img_pwm_chip *to_img_pwm_chip(struct pwm_chip *chip)
+{
+ return pwmchip_get_drvdata(chip);
+}
+
+static inline void img_pwm_writel(struct img_pwm_chip *imgchip,
+ u32 reg, u32 val)
+{
+ writel(val, imgchip->base + reg);
+}
+
+static inline u32 img_pwm_readl(struct img_pwm_chip *imgchip, u32 reg)
+{
+ return readl(imgchip->base + reg);
+}
+
+static int img_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
+ int duty_ns, int period_ns)
+{
+ u32 val, div, duty, timebase;
+ unsigned long mul, output_clk_hz, input_clk_hz;
+ struct img_pwm_chip *imgchip = to_img_pwm_chip(chip);
+ unsigned int max_timebase = imgchip->data->max_timebase;
+ int ret;
+
+ if (period_ns < imgchip->min_period_ns ||
+ period_ns > imgchip->max_period_ns) {
+ dev_err(pwmchip_parent(chip), "configured period not in range\n");
+ return -ERANGE;
+ }
+
+ input_clk_hz = clk_get_rate(imgchip->pwm_clk);
+ output_clk_hz = DIV_ROUND_UP(NSEC_PER_SEC, period_ns);
+
+ mul = DIV_ROUND_UP(input_clk_hz, output_clk_hz);
+ if (mul <= max_timebase) {
+ div = PWM_CTRL_CFG_NO_SUB_DIV;
+ timebase = DIV_ROUND_UP(mul, 1);
+ } else if (mul <= max_timebase * 8) {
+ div = PWM_CTRL_CFG_SUB_DIV0;
+ timebase = DIV_ROUND_UP(mul, 8);
+ } else if (mul <= max_timebase * 64) {
+ div = PWM_CTRL_CFG_SUB_DIV1;
+ timebase = DIV_ROUND_UP(mul, 64);
+ } else if (mul <= max_timebase * 512) {
+ div = PWM_CTRL_CFG_SUB_DIV0_DIV1;
+ timebase = DIV_ROUND_UP(mul, 512);
+ } else {
+ dev_err(pwmchip_parent(chip),
+ "failed to configure timebase steps/divider value\n");
+ return -EINVAL;
+ }
+
+ duty = DIV_ROUND_UP(timebase * duty_ns, period_ns);
+
+ ret = pm_runtime_resume_and_get(pwmchip_parent(chip));
+ if (ret < 0)
+ return ret;
+
+ val = img_pwm_readl(imgchip, PWM_CTRL_CFG);
+ val &= ~(PWM_CTRL_CFG_DIV_MASK << PWM_CTRL_CFG_DIV_SHIFT(pwm->hwpwm));
+ val |= (div & PWM_CTRL_CFG_DIV_MASK) <<
+ PWM_CTRL_CFG_DIV_SHIFT(pwm->hwpwm);
+ img_pwm_writel(imgchip, PWM_CTRL_CFG, val);
+
+ val = (duty << PWM_CH_CFG_DUTY_SHIFT) |
+ (timebase << PWM_CH_CFG_TMBASE_SHIFT);
+ img_pwm_writel(imgchip, PWM_CH_CFG(pwm->hwpwm), val);
+
+ pm_runtime_put_autosuspend(pwmchip_parent(chip));
+
+ return 0;
+}
+
+static int img_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ u32 val;
+ struct img_pwm_chip *imgchip = to_img_pwm_chip(chip);
+ int ret;
+
+ ret = pm_runtime_resume_and_get(pwmchip_parent(chip));
+ if (ret < 0)
+ return ret;
+
+ val = img_pwm_readl(imgchip, PWM_CTRL_CFG);
+ val |= BIT(pwm->hwpwm);
+ img_pwm_writel(imgchip, PWM_CTRL_CFG, val);
+
+ regmap_clear_bits(imgchip->periph_regs, PERIP_PWM_PDM_CONTROL,
+ PERIP_PWM_PDM_CONTROL_CH_MASK <<
+ PERIP_PWM_PDM_CONTROL_CH_SHIFT(pwm->hwpwm));
+
+ return 0;
+}
+
+static void img_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ u32 val;
+ struct img_pwm_chip *imgchip = to_img_pwm_chip(chip);
+
+ val = img_pwm_readl(imgchip, PWM_CTRL_CFG);
+ val &= ~BIT(pwm->hwpwm);
+ img_pwm_writel(imgchip, PWM_CTRL_CFG, val);
+
+ pm_runtime_put_autosuspend(pwmchip_parent(chip));
+}
+
+static int img_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
+ const struct pwm_state *state)
+{
+ int err;
+
+ if (state->polarity != PWM_POLARITY_NORMAL)
+ return -EINVAL;
+
+ if (!state->enabled) {
+ if (pwm->state.enabled)
+ img_pwm_disable(chip, pwm);
+
+ return 0;
+ }
+
+ err = img_pwm_config(chip, pwm, state->duty_cycle, state->period);
+ if (err)
+ return err;
+
+ if (!pwm->state.enabled)
+ err = img_pwm_enable(chip, pwm);
+
+ return err;
+}
+
+static const struct pwm_ops img_pwm_ops = {
+ .apply = img_pwm_apply,
+};
+
+static const struct img_pwm_soc_data pistachio_pwm = {
+ .max_timebase = 255,
+};
+
+static const struct of_device_id img_pwm_of_match[] = {
+ {
+ .compatible = "img,pistachio-pwm",
+ .data = &pistachio_pwm,
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(of, img_pwm_of_match);
+
+static int img_pwm_runtime_suspend(struct device *dev)
+{
+ struct pwm_chip *chip = dev_get_drvdata(dev);
+ struct img_pwm_chip *imgchip = to_img_pwm_chip(chip);
+
+ clk_disable_unprepare(imgchip->pwm_clk);
+ clk_disable_unprepare(imgchip->sys_clk);
+
+ return 0;
+}
+
+static int img_pwm_runtime_resume(struct device *dev)
+{
+ struct pwm_chip *chip = dev_get_drvdata(dev);
+ struct img_pwm_chip *imgchip = to_img_pwm_chip(chip);
+ int ret;
+
+ ret = clk_prepare_enable(imgchip->sys_clk);
+ if (ret < 0) {
+ dev_err(dev, "could not prepare or enable sys clock\n");
+ return ret;
+ }
+
+ ret = clk_prepare_enable(imgchip->pwm_clk);
+ if (ret < 0) {
+ dev_err(dev, "could not prepare or enable pwm clock\n");
+ clk_disable_unprepare(imgchip->sys_clk);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int img_pwm_probe(struct platform_device *pdev)
+{
+ int ret;
+ u64 val;
+ unsigned long clk_rate;
+ struct pwm_chip *chip;
+ struct img_pwm_chip *imgchip;
+
+ chip = devm_pwmchip_alloc(&pdev->dev, IMG_PWM_NPWM, sizeof(*imgchip));
+ if (IS_ERR(chip))
+ return PTR_ERR(chip);
+ imgchip = to_img_pwm_chip(chip);
+
+ imgchip->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(imgchip->base))
+ return PTR_ERR(imgchip->base);
+
+ imgchip->data = device_get_match_data(&pdev->dev);
+
+ imgchip->periph_regs = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
+ "img,cr-periph");
+ if (IS_ERR(imgchip->periph_regs))
+ return PTR_ERR(imgchip->periph_regs);
+
+ imgchip->sys_clk = devm_clk_get(&pdev->dev, "sys");
+ if (IS_ERR(imgchip->sys_clk)) {
+ dev_err(&pdev->dev, "failed to get system clock\n");
+ return PTR_ERR(imgchip->sys_clk);
+ }
+
+ imgchip->pwm_clk = devm_clk_get(&pdev->dev, "pwm");
+ if (IS_ERR(imgchip->pwm_clk)) {
+ dev_err(&pdev->dev, "failed to get pwm clock\n");
+ return PTR_ERR(imgchip->pwm_clk);
+ }
+
+ platform_set_drvdata(pdev, chip);
+
+ pm_runtime_set_autosuspend_delay(&pdev->dev, IMG_PWM_PM_TIMEOUT);
+ pm_runtime_use_autosuspend(&pdev->dev);
+ pm_runtime_enable(&pdev->dev);
+ if (!pm_runtime_enabled(&pdev->dev)) {
+ ret = img_pwm_runtime_resume(&pdev->dev);
+ if (ret)
+ goto err_pm_disable;
+ }
+
+ clk_rate = clk_get_rate(imgchip->pwm_clk);
+ if (!clk_rate) {
+ dev_err(&pdev->dev, "imgchip clock has no frequency\n");
+ ret = -EINVAL;
+ goto err_suspend;
+ }
+
+ /* The maximum input clock divider is 512 */
+ val = (u64)NSEC_PER_SEC * 512 * imgchip->data->max_timebase;
+ do_div(val, clk_rate);
+ imgchip->max_period_ns = val;
+
+ val = (u64)NSEC_PER_SEC * MIN_TMBASE_STEPS;
+ do_div(val, clk_rate);
+ imgchip->min_period_ns = val;
+
+ chip->ops = &img_pwm_ops;
+
+ ret = pwmchip_add(chip);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "pwmchip_add failed: %d\n", ret);
+ goto err_suspend;
+ }
+
+ return 0;
+
+err_suspend:
+ if (!pm_runtime_enabled(&pdev->dev))
+ img_pwm_runtime_suspend(&pdev->dev);
+err_pm_disable:
+ pm_runtime_disable(&pdev->dev);
+ pm_runtime_dont_use_autosuspend(&pdev->dev);
+ return ret;
+}
+
+static void img_pwm_remove(struct platform_device *pdev)
+{
+ struct pwm_chip *chip = platform_get_drvdata(pdev);
+
+ pm_runtime_disable(&pdev->dev);
+ if (!pm_runtime_status_suspended(&pdev->dev))
+ img_pwm_runtime_suspend(&pdev->dev);
+
+ pwmchip_remove(chip);
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int img_pwm_suspend(struct device *dev)
+{
+ struct pwm_chip *chip = dev_get_drvdata(dev);
+ struct img_pwm_chip *imgchip = to_img_pwm_chip(chip);
+ int i, ret;
+
+ if (pm_runtime_status_suspended(dev)) {
+ ret = img_pwm_runtime_resume(dev);
+ if (ret)
+ return ret;
+ }
+
+ for (i = 0; i < chip->npwm; i++)
+ imgchip->suspend_ch_cfg[i] = img_pwm_readl(imgchip,
+ PWM_CH_CFG(i));
+
+ imgchip->suspend_ctrl_cfg = img_pwm_readl(imgchip, PWM_CTRL_CFG);
+
+ img_pwm_runtime_suspend(dev);
+
+ return 0;
+}
+
+static int img_pwm_resume(struct device *dev)
+{
+ struct pwm_chip *chip = dev_get_drvdata(dev);
+ struct img_pwm_chip *imgchip = to_img_pwm_chip(chip);
+ int ret;
+ int i;
+
+ ret = img_pwm_runtime_resume(dev);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < chip->npwm; i++)
+ img_pwm_writel(imgchip, PWM_CH_CFG(i),
+ imgchip->suspend_ch_cfg[i]);
+
+ img_pwm_writel(imgchip, PWM_CTRL_CFG, imgchip->suspend_ctrl_cfg);
+
+ for (i = 0; i < chip->npwm; i++)
+ if (imgchip->suspend_ctrl_cfg & BIT(i))
+ regmap_clear_bits(imgchip->periph_regs,
+ PERIP_PWM_PDM_CONTROL,
+ PERIP_PWM_PDM_CONTROL_CH_MASK <<
+ PERIP_PWM_PDM_CONTROL_CH_SHIFT(i));
+
+ if (pm_runtime_status_suspended(dev))
+ img_pwm_runtime_suspend(dev);
+
+ return 0;
+}
+#endif /* CONFIG_PM */
+
+static const struct dev_pm_ops img_pwm_pm_ops = {
+ SET_RUNTIME_PM_OPS(img_pwm_runtime_suspend,
+ img_pwm_runtime_resume,
+ NULL)
+ SET_SYSTEM_SLEEP_PM_OPS(img_pwm_suspend, img_pwm_resume)
+};
+
+static struct platform_driver img_pwm_driver = {
+ .driver = {
+ .name = "img-pwm",
+ .pm = &img_pwm_pm_ops,
+ .of_match_table = img_pwm_of_match,
+ },
+ .probe = img_pwm_probe,
+ .remove = img_pwm_remove,
+};
+module_platform_driver(img_pwm_driver);
+
+MODULE_AUTHOR("Sai Masarapu <Sai.Masarapu@imgtec.com>");
+MODULE_DESCRIPTION("Imagination Technologies PWM DAC driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/pwm/pwm-imx-tpm.c b/drivers/pwm/pwm-imx-tpm.c
new file mode 100644
index 000000000..c20753a36
--- /dev/null
+++ b/drivers/pwm/pwm-imx-tpm.c
@@ -0,0 +1,462 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2018-2019 NXP.
+ *
+ * Limitations:
+ * - The TPM counter and period counter are shared between
+ * multiple channels, so all channels should use same period
+ * settings.
+ * - Changes to polarity cannot be latched at the time of the
+ * next period start.
+ * - Changing period and duty cycle together isn't atomic,
+ * with the wrong timing it might happen that a period is
+ * produced with old duty cycle but new period settings.
+ */
+
+#include <linux/bitfield.h>
+#include <linux/bitops.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+#include <linux/slab.h>
+
+#define PWM_IMX_TPM_PARAM 0x4
+#define PWM_IMX_TPM_GLOBAL 0x8
+#define PWM_IMX_TPM_SC 0x10
+#define PWM_IMX_TPM_CNT 0x14
+#define PWM_IMX_TPM_MOD 0x18
+#define PWM_IMX_TPM_CnSC(n) (0x20 + (n) * 0x8)
+#define PWM_IMX_TPM_CnV(n) (0x24 + (n) * 0x8)
+
+#define PWM_IMX_TPM_PARAM_CHAN GENMASK(7, 0)
+
+#define PWM_IMX_TPM_SC_PS GENMASK(2, 0)
+#define PWM_IMX_TPM_SC_CMOD GENMASK(4, 3)
+#define PWM_IMX_TPM_SC_CMOD_INC_EVERY_CLK FIELD_PREP(PWM_IMX_TPM_SC_CMOD, 1)
+#define PWM_IMX_TPM_SC_CPWMS BIT(5)
+
+#define PWM_IMX_TPM_CnSC_CHF BIT(7)
+#define PWM_IMX_TPM_CnSC_MSB BIT(5)
+#define PWM_IMX_TPM_CnSC_MSA BIT(4)
+
+/*
+ * The reference manual describes this field as two separate bits. The
+ * semantic of the two bits isn't orthogonal though, so they are treated
+ * together as a 2-bit field here.
+ */
+#define PWM_IMX_TPM_CnSC_ELS GENMASK(3, 2)
+#define PWM_IMX_TPM_CnSC_ELS_INVERSED FIELD_PREP(PWM_IMX_TPM_CnSC_ELS, 1)
+#define PWM_IMX_TPM_CnSC_ELS_NORMAL FIELD_PREP(PWM_IMX_TPM_CnSC_ELS, 2)
+
+
+#define PWM_IMX_TPM_MOD_WIDTH 16
+#define PWM_IMX_TPM_MOD_MOD GENMASK(PWM_IMX_TPM_MOD_WIDTH - 1, 0)
+
+struct imx_tpm_pwm_chip {
+ struct clk *clk;
+ void __iomem *base;
+ struct mutex lock;
+ u32 user_count;
+ u32 enable_count;
+ u32 real_period;
+};
+
+struct imx_tpm_pwm_param {
+ u8 prescale;
+ u32 mod;
+ u32 val;
+};
+
+static inline struct imx_tpm_pwm_chip *
+to_imx_tpm_pwm_chip(struct pwm_chip *chip)
+{
+ return pwmchip_get_drvdata(chip);
+}
+
+/*
+ * This function determines for a given pwm_state *state that a consumer
+ * might request the pwm_state *real_state that eventually is implemented
+ * by the hardware and the necessary register values (in *p) to achieve
+ * this.
+ */
+static int pwm_imx_tpm_round_state(struct pwm_chip *chip,
+ struct imx_tpm_pwm_param *p,
+ struct pwm_state *real_state,
+ const struct pwm_state *state)
+{
+ struct imx_tpm_pwm_chip *tpm = to_imx_tpm_pwm_chip(chip);
+ u32 rate, prescale, period_count, clock_unit;
+ u64 tmp;
+
+ rate = clk_get_rate(tpm->clk);
+ tmp = (u64)state->period * rate;
+ clock_unit = DIV_ROUND_CLOSEST_ULL(tmp, NSEC_PER_SEC);
+ if (clock_unit <= PWM_IMX_TPM_MOD_MOD)
+ prescale = 0;
+ else
+ prescale = ilog2(clock_unit) + 1 - PWM_IMX_TPM_MOD_WIDTH;
+
+ if ((!FIELD_FIT(PWM_IMX_TPM_SC_PS, prescale)))
+ return -ERANGE;
+ p->prescale = prescale;
+
+ period_count = (clock_unit + ((1 << prescale) >> 1)) >> prescale;
+ if (period_count == 0)
+ return -EINVAL;
+ p->mod = period_count - 1;
+
+ /* calculate real period HW can support */
+ tmp = (u64)period_count << prescale;
+ tmp *= NSEC_PER_SEC;
+ real_state->period = DIV_ROUND_CLOSEST_ULL(tmp, rate);
+
+ /*
+ * if eventually the PWM output is inactive, either
+ * duty cycle is 0 or status is disabled, need to
+ * make sure the output pin is inactive.
+ */
+ if (!state->enabled)
+ real_state->duty_cycle = 0;
+ else
+ real_state->duty_cycle = state->duty_cycle;
+
+ tmp = (u64)p->mod * real_state->duty_cycle;
+ p->val = DIV64_U64_ROUND_CLOSEST(tmp, real_state->period);
+
+ real_state->polarity = state->polarity;
+ real_state->enabled = state->enabled;
+
+ return 0;
+}
+
+static int pwm_imx_tpm_get_state(struct pwm_chip *chip,
+ struct pwm_device *pwm,
+ struct pwm_state *state)
+{
+ struct imx_tpm_pwm_chip *tpm = to_imx_tpm_pwm_chip(chip);
+ u32 rate, val, prescale;
+ u64 tmp;
+
+ /* get period */
+ state->period = tpm->real_period;
+
+ /* get duty cycle */
+ rate = clk_get_rate(tpm->clk);
+ val = readl(tpm->base + PWM_IMX_TPM_SC);
+ prescale = FIELD_GET(PWM_IMX_TPM_SC_PS, val);
+ tmp = readl(tpm->base + PWM_IMX_TPM_CnV(pwm->hwpwm));
+ tmp = (tmp << prescale) * NSEC_PER_SEC;
+ state->duty_cycle = DIV_ROUND_CLOSEST_ULL(tmp, rate);
+
+ /* get polarity */
+ val = readl(tpm->base + PWM_IMX_TPM_CnSC(pwm->hwpwm));
+ if ((val & PWM_IMX_TPM_CnSC_ELS) == PWM_IMX_TPM_CnSC_ELS_INVERSED)
+ state->polarity = PWM_POLARITY_INVERSED;
+ else
+ /*
+ * Assume reserved values (2b00 and 2b11) to yield
+ * normal polarity.
+ */
+ state->polarity = PWM_POLARITY_NORMAL;
+
+ /* get channel status */
+ state->enabled = FIELD_GET(PWM_IMX_TPM_CnSC_ELS, val) ? true : false;
+
+ return 0;
+}
+
+/* this function is supposed to be called with mutex hold */
+static int pwm_imx_tpm_apply_hw(struct pwm_chip *chip,
+ struct imx_tpm_pwm_param *p,
+ struct pwm_state *state,
+ struct pwm_device *pwm)
+{
+ struct imx_tpm_pwm_chip *tpm = to_imx_tpm_pwm_chip(chip);
+ bool period_update = false;
+ bool duty_update = false;
+ u32 val, cmod, cur_prescale;
+ unsigned long timeout;
+ struct pwm_state c;
+
+ if (state->period != tpm->real_period) {
+ /*
+ * TPM counter is shared by multiple channels, so
+ * prescale and period can NOT be modified when
+ * there are multiple channels in use with different
+ * period settings.
+ */
+ if (tpm->user_count > 1)
+ return -EBUSY;
+
+ val = readl(tpm->base + PWM_IMX_TPM_SC);
+ cmod = FIELD_GET(PWM_IMX_TPM_SC_CMOD, val);
+ cur_prescale = FIELD_GET(PWM_IMX_TPM_SC_PS, val);
+ if (cmod && cur_prescale != p->prescale)
+ return -EBUSY;
+
+ /* set TPM counter prescale */
+ val &= ~PWM_IMX_TPM_SC_PS;
+ val |= FIELD_PREP(PWM_IMX_TPM_SC_PS, p->prescale);
+ writel(val, tpm->base + PWM_IMX_TPM_SC);
+
+ /*
+ * if the counter is disabled (CMOD == 0), programming the new
+ * period length (MOD) will not reset the counter (CNT). If
+ * CNT.COUNT happens to be bigger than the new MOD value then
+ * the counter will end up being reset way too late. Therefore,
+ * manually reset it to 0.
+ */
+ if (!cmod)
+ writel(0x0, tpm->base + PWM_IMX_TPM_CNT);
+ /*
+ * set period count:
+ * if the PWM is disabled (CMOD[1:0] = 2b00), then MOD register
+ * is updated when MOD register is written.
+ *
+ * if the PWM is enabled (CMOD[1:0] ≠ 2b00), the period length
+ * is latched into hardware when the next period starts.
+ */
+ writel(p->mod, tpm->base + PWM_IMX_TPM_MOD);
+ tpm->real_period = state->period;
+ period_update = true;
+ }
+
+ pwm_imx_tpm_get_state(chip, pwm, &c);
+
+ /* polarity is NOT allowed to be changed if PWM is active */
+ if (c.enabled && c.polarity != state->polarity)
+ return -EBUSY;
+
+ if (state->duty_cycle != c.duty_cycle) {
+ /*
+ * set channel value:
+ * if the PWM is disabled (CMOD[1:0] = 2b00), then CnV register
+ * is updated when CnV register is written.
+ *
+ * if the PWM is enabled (CMOD[1:0] ≠ 2b00), the duty length
+ * is latched into hardware when the next period starts.
+ */
+ writel(p->val, tpm->base + PWM_IMX_TPM_CnV(pwm->hwpwm));
+ duty_update = true;
+ }
+
+ /* make sure MOD & CnV registers are updated */
+ if (period_update || duty_update) {
+ timeout = jiffies + msecs_to_jiffies(tpm->real_period /
+ NSEC_PER_MSEC + 1);
+ while (readl(tpm->base + PWM_IMX_TPM_MOD) != p->mod
+ || readl(tpm->base + PWM_IMX_TPM_CnV(pwm->hwpwm))
+ != p->val) {
+ if (time_after(jiffies, timeout))
+ return -ETIME;
+ cpu_relax();
+ }
+ }
+
+ /*
+ * polarity settings will enabled/disable output status
+ * immediately, so if the channel is disabled, need to
+ * make sure MSA/MSB/ELS are set to 0 which means channel
+ * disabled.
+ */
+ val = readl(tpm->base + PWM_IMX_TPM_CnSC(pwm->hwpwm));
+ val &= ~(PWM_IMX_TPM_CnSC_ELS | PWM_IMX_TPM_CnSC_MSA |
+ PWM_IMX_TPM_CnSC_MSB);
+ if (state->enabled) {
+ /*
+ * set polarity (for edge-aligned PWM modes)
+ *
+ * ELS[1:0] = 2b10 yields normal polarity behaviour,
+ * ELS[1:0] = 2b01 yields inversed polarity.
+ * The other values are reserved.
+ */
+ val |= PWM_IMX_TPM_CnSC_MSB;
+ val |= (state->polarity == PWM_POLARITY_NORMAL) ?
+ PWM_IMX_TPM_CnSC_ELS_NORMAL :
+ PWM_IMX_TPM_CnSC_ELS_INVERSED;
+ }
+ writel(val, tpm->base + PWM_IMX_TPM_CnSC(pwm->hwpwm));
+
+ /* control the counter status */
+ if (state->enabled != c.enabled) {
+ val = readl(tpm->base + PWM_IMX_TPM_SC);
+ if (state->enabled) {
+ if (++tpm->enable_count == 1)
+ val |= PWM_IMX_TPM_SC_CMOD_INC_EVERY_CLK;
+ } else {
+ if (--tpm->enable_count == 0)
+ val &= ~PWM_IMX_TPM_SC_CMOD;
+ }
+ writel(val, tpm->base + PWM_IMX_TPM_SC);
+ }
+
+ return 0;
+}
+
+static int pwm_imx_tpm_apply(struct pwm_chip *chip,
+ struct pwm_device *pwm,
+ const struct pwm_state *state)
+{
+ struct imx_tpm_pwm_chip *tpm = to_imx_tpm_pwm_chip(chip);
+ struct imx_tpm_pwm_param param;
+ struct pwm_state real_state;
+ int ret;
+
+ ret = pwm_imx_tpm_round_state(chip, &param, &real_state, state);
+ if (ret)
+ return ret;
+
+ mutex_lock(&tpm->lock);
+ ret = pwm_imx_tpm_apply_hw(chip, &param, &real_state, pwm);
+ mutex_unlock(&tpm->lock);
+
+ return ret;
+}
+
+static int pwm_imx_tpm_request(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct imx_tpm_pwm_chip *tpm = to_imx_tpm_pwm_chip(chip);
+
+ mutex_lock(&tpm->lock);
+ tpm->user_count++;
+ mutex_unlock(&tpm->lock);
+
+ return 0;
+}
+
+static void pwm_imx_tpm_free(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct imx_tpm_pwm_chip *tpm = to_imx_tpm_pwm_chip(chip);
+
+ mutex_lock(&tpm->lock);
+ tpm->user_count--;
+ mutex_unlock(&tpm->lock);
+}
+
+static const struct pwm_ops imx_tpm_pwm_ops = {
+ .request = pwm_imx_tpm_request,
+ .free = pwm_imx_tpm_free,
+ .get_state = pwm_imx_tpm_get_state,
+ .apply = pwm_imx_tpm_apply,
+};
+
+static int pwm_imx_tpm_probe(struct platform_device *pdev)
+{
+ struct pwm_chip *chip;
+ struct imx_tpm_pwm_chip *tpm;
+ struct clk *clk;
+ void __iomem *base;
+ int ret;
+ unsigned int i, npwm;
+ u32 val;
+
+ base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ clk = devm_clk_get_enabled(&pdev->dev, NULL);
+ if (IS_ERR(clk))
+ return dev_err_probe(&pdev->dev, PTR_ERR(clk),
+ "failed to get PWM clock\n");
+
+ /* get number of channels */
+ val = readl(base + PWM_IMX_TPM_PARAM);
+ npwm = FIELD_GET(PWM_IMX_TPM_PARAM_CHAN, val);
+
+ chip = devm_pwmchip_alloc(&pdev->dev, npwm, sizeof(*tpm));
+ if (IS_ERR(chip))
+ return PTR_ERR(chip);
+ tpm = to_imx_tpm_pwm_chip(chip);
+
+ platform_set_drvdata(pdev, tpm);
+
+ tpm->base = base;
+ tpm->clk = clk;
+
+ chip->ops = &imx_tpm_pwm_ops;
+
+ mutex_init(&tpm->lock);
+
+ /* count the enabled channels */
+ for (i = 0; i < npwm; ++i) {
+ val = readl(base + PWM_IMX_TPM_CnSC(i));
+ if (FIELD_GET(PWM_IMX_TPM_CnSC_ELS, val))
+ ++tpm->enable_count;
+ }
+
+ ret = devm_pwmchip_add(&pdev->dev, chip);
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret, "failed to add PWM chip\n");
+
+ return 0;
+}
+
+static int pwm_imx_tpm_suspend(struct device *dev)
+{
+ struct imx_tpm_pwm_chip *tpm = dev_get_drvdata(dev);
+ int ret;
+
+ if (tpm->enable_count > 0)
+ return -EBUSY;
+
+ /*
+ * Force 'real_period' to be zero to force period update code
+ * can be executed after system resume back, since suspend causes
+ * the period related registers to become their reset values.
+ */
+ tpm->real_period = 0;
+
+ clk_disable_unprepare(tpm->clk);
+
+ ret = pinctrl_pm_select_sleep_state(dev);
+ if (ret)
+ clk_prepare_enable(tpm->clk);
+
+ return ret;
+}
+
+static int pwm_imx_tpm_resume(struct device *dev)
+{
+ struct imx_tpm_pwm_chip *tpm = dev_get_drvdata(dev);
+ int ret = 0;
+
+ ret = pinctrl_pm_select_default_state(dev);
+ if (ret)
+ return ret;
+
+ ret = clk_prepare_enable(tpm->clk);
+ if (ret) {
+ dev_err(dev, "failed to prepare or enable clock: %d\n", ret);
+ pinctrl_pm_select_sleep_state(dev);
+ }
+
+ return ret;
+}
+
+static DEFINE_SIMPLE_DEV_PM_OPS(imx_tpm_pwm_pm,
+ pwm_imx_tpm_suspend, pwm_imx_tpm_resume);
+
+static const struct of_device_id imx_tpm_pwm_dt_ids[] = {
+ { .compatible = "fsl,imx7ulp-pwm" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, imx_tpm_pwm_dt_ids);
+
+static struct platform_driver imx_tpm_pwm_driver = {
+ .driver = {
+ .name = "imx7ulp-tpm-pwm",
+ .of_match_table = imx_tpm_pwm_dt_ids,
+ .pm = pm_ptr(&imx_tpm_pwm_pm),
+ },
+ .probe = pwm_imx_tpm_probe,
+};
+module_platform_driver(imx_tpm_pwm_driver);
+
+MODULE_AUTHOR("Anson Huang <Anson.Huang@nxp.com>");
+MODULE_DESCRIPTION("i.MX TPM PWM Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/pwm/pwm-imx1.c b/drivers/pwm/pwm-imx1.c
new file mode 100644
index 000000000..075d46180
--- /dev/null
+++ b/drivers/pwm/pwm-imx1.c
@@ -0,0 +1,199 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * simple driver for PWM (Pulse Width Modulator) controller
+ *
+ * Derived from pxa PWM driver by eric miao <eric.miao@marvell.com>
+ */
+
+#include <linux/bitfield.h>
+#include <linux/bitops.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+#include <linux/slab.h>
+
+#define MX1_PWMC 0x00 /* PWM Control Register */
+#define MX1_PWMS 0x04 /* PWM Sample Register */
+#define MX1_PWMP 0x08 /* PWM Period Register */
+
+#define MX1_PWMC_EN BIT(4)
+
+struct pwm_imx1_chip {
+ struct clk *clk_ipg;
+ struct clk *clk_per;
+ void __iomem *mmio_base;
+};
+
+static inline struct pwm_imx1_chip *to_pwm_imx1_chip(struct pwm_chip *chip)
+{
+ return pwmchip_get_drvdata(chip);
+}
+
+static int pwm_imx1_clk_prepare_enable(struct pwm_chip *chip)
+{
+ struct pwm_imx1_chip *imx = to_pwm_imx1_chip(chip);
+ int ret;
+
+ ret = clk_prepare_enable(imx->clk_ipg);
+ if (ret)
+ return ret;
+
+ ret = clk_prepare_enable(imx->clk_per);
+ if (ret) {
+ clk_disable_unprepare(imx->clk_ipg);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void pwm_imx1_clk_disable_unprepare(struct pwm_chip *chip)
+{
+ struct pwm_imx1_chip *imx = to_pwm_imx1_chip(chip);
+
+ clk_disable_unprepare(imx->clk_per);
+ clk_disable_unprepare(imx->clk_ipg);
+}
+
+static int pwm_imx1_config(struct pwm_chip *chip,
+ struct pwm_device *pwm, u64 duty_ns, u64 period_ns)
+{
+ struct pwm_imx1_chip *imx = to_pwm_imx1_chip(chip);
+ u32 max, p;
+
+ /*
+ * The PWM subsystem allows for exact frequencies. However,
+ * I cannot connect a scope on my device to the PWM line and
+ * thus cannot provide the program the PWM controller
+ * exactly. Instead, I'm relying on the fact that the
+ * Bootloader (u-boot or WinCE+haret) has programmed the PWM
+ * function group already. So I'll just modify the PWM sample
+ * register to follow the ratio of duty_ns vs. period_ns
+ * accordingly.
+ *
+ * This is good enough for programming the brightness of
+ * the LCD backlight.
+ *
+ * The real implementation would divide PERCLK[0] first by
+ * both the prescaler (/1 .. /128) and then by CLKSEL
+ * (/2 .. /16).
+ */
+ max = readl(imx->mmio_base + MX1_PWMP);
+ p = mul_u64_u64_div_u64(max, duty_ns, period_ns);
+
+ writel(max - p, imx->mmio_base + MX1_PWMS);
+
+ return 0;
+}
+
+static int pwm_imx1_enable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct pwm_imx1_chip *imx = to_pwm_imx1_chip(chip);
+ u32 value;
+ int ret;
+
+ ret = pwm_imx1_clk_prepare_enable(chip);
+ if (ret < 0)
+ return ret;
+
+ value = readl(imx->mmio_base + MX1_PWMC);
+ value |= MX1_PWMC_EN;
+ writel(value, imx->mmio_base + MX1_PWMC);
+
+ return 0;
+}
+
+static void pwm_imx1_disable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct pwm_imx1_chip *imx = to_pwm_imx1_chip(chip);
+ u32 value;
+
+ value = readl(imx->mmio_base + MX1_PWMC);
+ value &= ~MX1_PWMC_EN;
+ writel(value, imx->mmio_base + MX1_PWMC);
+
+ pwm_imx1_clk_disable_unprepare(chip);
+}
+
+static int pwm_imx1_apply(struct pwm_chip *chip, struct pwm_device *pwm,
+ const struct pwm_state *state)
+{
+ int err;
+
+ if (state->polarity != PWM_POLARITY_NORMAL)
+ return -EINVAL;
+
+ if (!state->enabled) {
+ if (pwm->state.enabled)
+ pwm_imx1_disable(chip, pwm);
+
+ return 0;
+ }
+
+ err = pwm_imx1_config(chip, pwm, state->duty_cycle, state->period);
+ if (err)
+ return err;
+
+ if (!pwm->state.enabled)
+ return pwm_imx1_enable(chip, pwm);
+
+ return 0;
+}
+
+static const struct pwm_ops pwm_imx1_ops = {
+ .apply = pwm_imx1_apply,
+};
+
+static const struct of_device_id pwm_imx1_dt_ids[] = {
+ { .compatible = "fsl,imx1-pwm" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, pwm_imx1_dt_ids);
+
+static int pwm_imx1_probe(struct platform_device *pdev)
+{
+ struct pwm_chip *chip;
+ struct pwm_imx1_chip *imx;
+
+ chip = devm_pwmchip_alloc(&pdev->dev, 1, sizeof(*imx));
+ if (IS_ERR(chip))
+ return PTR_ERR(chip);
+ imx = to_pwm_imx1_chip(chip);
+
+ imx->clk_ipg = devm_clk_get(&pdev->dev, "ipg");
+ if (IS_ERR(imx->clk_ipg))
+ return dev_err_probe(&pdev->dev, PTR_ERR(imx->clk_ipg),
+ "getting ipg clock failed\n");
+
+ imx->clk_per = devm_clk_get(&pdev->dev, "per");
+ if (IS_ERR(imx->clk_per))
+ return dev_err_probe(&pdev->dev, PTR_ERR(imx->clk_per),
+ "failed to get peripheral clock\n");
+
+ chip->ops = &pwm_imx1_ops;
+
+ imx->mmio_base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(imx->mmio_base))
+ return PTR_ERR(imx->mmio_base);
+
+ return devm_pwmchip_add(&pdev->dev, chip);
+}
+
+static struct platform_driver pwm_imx1_driver = {
+ .driver = {
+ .name = "pwm-imx1",
+ .of_match_table = pwm_imx1_dt_ids,
+ },
+ .probe = pwm_imx1_probe,
+};
+module_platform_driver(pwm_imx1_driver);
+
+MODULE_DESCRIPTION("i.MX1 and i.MX21 Pulse Width Modulator driver");
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");
diff --git a/drivers/pwm/pwm-imx27.c b/drivers/pwm/pwm-imx27.c
new file mode 100644
index 000000000..7c1ad1500
--- /dev/null
+++ b/drivers/pwm/pwm-imx27.c
@@ -0,0 +1,438 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * simple driver for PWM (Pulse Width Modulator) controller
+ *
+ * Derived from pxa PWM driver by eric miao <eric.miao@marvell.com>
+ *
+ * Limitations:
+ * - When disabled the output is driven to 0 independent of the configured
+ * polarity.
+ */
+
+#include <linux/bitfield.h>
+#include <linux/bitops.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+#include <linux/slab.h>
+
+#define MX3_PWMCR 0x00 /* PWM Control Register */
+#define MX3_PWMSR 0x04 /* PWM Status Register */
+#define MX3_PWMSAR 0x0C /* PWM Sample Register */
+#define MX3_PWMPR 0x10 /* PWM Period Register */
+#define MX3_PWMCNR 0x14 /* PWM Counter Register */
+
+#define MX3_PWMCR_FWM GENMASK(27, 26)
+#define MX3_PWMCR_STOPEN BIT(25)
+#define MX3_PWMCR_DOZEN BIT(24)
+#define MX3_PWMCR_WAITEN BIT(23)
+#define MX3_PWMCR_DBGEN BIT(22)
+#define MX3_PWMCR_BCTR BIT(21)
+#define MX3_PWMCR_HCTR BIT(20)
+
+#define MX3_PWMCR_POUTC GENMASK(19, 18)
+#define MX3_PWMCR_POUTC_NORMAL 0
+#define MX3_PWMCR_POUTC_INVERTED 1
+#define MX3_PWMCR_POUTC_OFF 2
+
+#define MX3_PWMCR_CLKSRC GENMASK(17, 16)
+#define MX3_PWMCR_CLKSRC_OFF 0
+#define MX3_PWMCR_CLKSRC_IPG 1
+#define MX3_PWMCR_CLKSRC_IPG_HIGH 2
+#define MX3_PWMCR_CLKSRC_IPG_32K 3
+
+#define MX3_PWMCR_PRESCALER GENMASK(15, 4)
+
+#define MX3_PWMCR_SWR BIT(3)
+
+#define MX3_PWMCR_REPEAT GENMASK(2, 1)
+#define MX3_PWMCR_REPEAT_1X 0
+#define MX3_PWMCR_REPEAT_2X 1
+#define MX3_PWMCR_REPEAT_4X 2
+#define MX3_PWMCR_REPEAT_8X 3
+
+#define MX3_PWMCR_EN BIT(0)
+
+#define MX3_PWMSR_FWE BIT(6)
+#define MX3_PWMSR_CMP BIT(5)
+#define MX3_PWMSR_ROV BIT(4)
+#define MX3_PWMSR_FE BIT(3)
+
+#define MX3_PWMSR_FIFOAV GENMASK(2, 0)
+#define MX3_PWMSR_FIFOAV_EMPTY 0
+#define MX3_PWMSR_FIFOAV_1WORD 1
+#define MX3_PWMSR_FIFOAV_2WORDS 2
+#define MX3_PWMSR_FIFOAV_3WORDS 3
+#define MX3_PWMSR_FIFOAV_4WORDS 4
+
+#define MX3_PWMCR_PRESCALER_SET(x) FIELD_PREP(MX3_PWMCR_PRESCALER, (x) - 1)
+#define MX3_PWMCR_PRESCALER_GET(x) (FIELD_GET(MX3_PWMCR_PRESCALER, \
+ (x)) + 1)
+
+#define MX3_PWM_SWR_LOOP 5
+
+/* PWMPR register value of 0xffff has the same effect as 0xfffe */
+#define MX3_PWMPR_MAX 0xfffe
+
+static const char * const pwm_imx27_clks[] = {"ipg", "per"};
+#define PWM_IMX27_PER 1
+
+struct pwm_imx27_chip {
+ struct clk_bulk_data clks[ARRAY_SIZE(pwm_imx27_clks)];
+ int clks_cnt;
+ void __iomem *mmio_base;
+
+ /*
+ * The driver cannot read the current duty cycle from the hardware if
+ * the hardware is disabled. Cache the last programmed duty cycle
+ * value to return in that case.
+ */
+ unsigned int duty_cycle;
+};
+
+static inline struct pwm_imx27_chip *to_pwm_imx27_chip(struct pwm_chip *chip)
+{
+ return pwmchip_get_drvdata(chip);
+}
+
+static int pwm_imx27_get_state(struct pwm_chip *chip,
+ struct pwm_device *pwm, struct pwm_state *state)
+{
+ struct pwm_imx27_chip *imx = to_pwm_imx27_chip(chip);
+ u32 period, prescaler, pwm_clk, val;
+ u64 tmp;
+ int ret;
+
+ ret = clk_bulk_prepare_enable(imx->clks_cnt, imx->clks);
+ if (ret < 0)
+ return ret;
+
+ val = readl(imx->mmio_base + MX3_PWMCR);
+
+ if (val & MX3_PWMCR_EN)
+ state->enabled = true;
+ else
+ state->enabled = false;
+
+ switch (FIELD_GET(MX3_PWMCR_POUTC, val)) {
+ case MX3_PWMCR_POUTC_NORMAL:
+ state->polarity = PWM_POLARITY_NORMAL;
+ break;
+ case MX3_PWMCR_POUTC_INVERTED:
+ state->polarity = PWM_POLARITY_INVERSED;
+ break;
+ default:
+ dev_warn(pwmchip_parent(chip), "can't set polarity, output disconnected");
+ }
+
+ prescaler = MX3_PWMCR_PRESCALER_GET(val);
+ pwm_clk = clk_get_rate(imx->clks[PWM_IMX27_PER].clk);
+ val = readl(imx->mmio_base + MX3_PWMPR);
+ period = val >= MX3_PWMPR_MAX ? MX3_PWMPR_MAX : val;
+
+ /* PWMOUT (Hz) = PWMCLK / (PWMPR + 2) */
+ tmp = NSEC_PER_SEC * (u64)(period + 2) * prescaler;
+ state->period = DIV_ROUND_UP_ULL(tmp, pwm_clk);
+
+ /*
+ * PWMSAR can be read only if PWM is enabled. If the PWM is disabled,
+ * use the cached value.
+ */
+ if (state->enabled)
+ val = readl(imx->mmio_base + MX3_PWMSAR);
+ else
+ val = imx->duty_cycle;
+
+ tmp = NSEC_PER_SEC * (u64)(val) * prescaler;
+ state->duty_cycle = DIV_ROUND_UP_ULL(tmp, pwm_clk);
+
+ clk_bulk_disable_unprepare(imx->clks_cnt, imx->clks);
+
+ return 0;
+}
+
+static void pwm_imx27_sw_reset(struct pwm_chip *chip)
+{
+ struct pwm_imx27_chip *imx = to_pwm_imx27_chip(chip);
+ struct device *dev = pwmchip_parent(chip);
+ int wait_count = 0;
+ u32 cr;
+
+ writel(MX3_PWMCR_SWR, imx->mmio_base + MX3_PWMCR);
+ do {
+ usleep_range(200, 1000);
+ cr = readl(imx->mmio_base + MX3_PWMCR);
+ } while ((cr & MX3_PWMCR_SWR) &&
+ (wait_count++ < MX3_PWM_SWR_LOOP));
+
+ if (cr & MX3_PWMCR_SWR)
+ dev_warn(dev, "software reset timeout\n");
+}
+
+static void pwm_imx27_wait_fifo_slot(struct pwm_chip *chip,
+ struct pwm_device *pwm)
+{
+ struct pwm_imx27_chip *imx = to_pwm_imx27_chip(chip);
+ struct device *dev = pwmchip_parent(chip);
+ unsigned int period_ms;
+ int fifoav;
+ u32 sr;
+
+ sr = readl(imx->mmio_base + MX3_PWMSR);
+ fifoav = FIELD_GET(MX3_PWMSR_FIFOAV, sr);
+ if (fifoav == MX3_PWMSR_FIFOAV_4WORDS) {
+ period_ms = DIV_ROUND_UP_ULL(pwm->state.period,
+ NSEC_PER_MSEC);
+ msleep(period_ms);
+
+ sr = readl(imx->mmio_base + MX3_PWMSR);
+ if (fifoav == FIELD_GET(MX3_PWMSR_FIFOAV, sr))
+ dev_warn(dev, "there is no free FIFO slot\n");
+ }
+}
+
+static int pwm_imx27_apply(struct pwm_chip *chip, struct pwm_device *pwm,
+ const struct pwm_state *state)
+{
+ unsigned long period_cycles, duty_cycles, prescale, period_us;
+ struct pwm_imx27_chip *imx = to_pwm_imx27_chip(chip);
+ unsigned long long c;
+ unsigned long long clkrate;
+ unsigned long flags;
+ int val;
+ int ret;
+ u32 cr;
+ u64 tmp;
+
+ clkrate = clk_get_rate(imx->clks[PWM_IMX27_PER].clk);
+ c = clkrate * state->period;
+
+ do_div(c, NSEC_PER_SEC);
+ period_cycles = c;
+
+ prescale = period_cycles / 0x10000 + 1;
+
+ period_cycles /= prescale;
+ c = clkrate * state->duty_cycle;
+ do_div(c, NSEC_PER_SEC);
+ duty_cycles = c;
+ duty_cycles /= prescale;
+
+ /*
+ * according to imx pwm RM, the real period value should be PERIOD
+ * value in PWMPR plus 2.
+ */
+ if (period_cycles > 2)
+ period_cycles -= 2;
+ else
+ period_cycles = 0;
+
+ /*
+ * Wait for a free FIFO slot if the PWM is already enabled, and flush
+ * the FIFO if the PWM was disabled and is about to be enabled.
+ */
+ if (pwm->state.enabled) {
+ pwm_imx27_wait_fifo_slot(chip, pwm);
+ } else {
+ ret = clk_bulk_prepare_enable(imx->clks_cnt, imx->clks);
+ if (ret)
+ return ret;
+
+ pwm_imx27_sw_reset(chip);
+ }
+
+ val = readl(imx->mmio_base + MX3_PWMPR);
+ val = val >= MX3_PWMPR_MAX ? MX3_PWMPR_MAX : val;
+ cr = readl(imx->mmio_base + MX3_PWMCR);
+
+ /*
+ * tmp stores period in nanoseconds. Result fits in u64 since
+ * val <= 0xfffe and prescaler in [1, 0x1000].
+ */
+ tmp = NSEC_PER_SEC * (u64)(val + 2) * MX3_PWMCR_PRESCALER_GET(cr);
+ tmp = DIV_ROUND_UP_ULL(tmp, clkrate);
+ period_us = DIV_ROUND_UP_ULL(tmp, 1000);
+
+ /*
+ * ERR051198:
+ * PWM: PWM output may not function correctly if the FIFO is empty when
+ * a new SAR value is programmed
+ *
+ * Description:
+ * When the PWM FIFO is empty, a new value programmed to the PWM Sample
+ * register (PWM_PWMSAR) will be directly applied even if the current
+ * timer period has not expired.
+ *
+ * If the new SAMPLE value programmed in the PWM_PWMSAR register is
+ * less than the previous value, and the PWM counter register
+ * (PWM_PWMCNR) that contains the current COUNT value is greater than
+ * the new programmed SAMPLE value, the current period will not flip
+ * the level. This may result in an output pulse with a duty cycle of
+ * 100%.
+ *
+ * Consider a change from
+ * ________
+ * / \______/
+ * ^ * ^
+ * to
+ * ____
+ * / \__________/
+ * ^ ^
+ * At the time marked by *, the new write value will be directly applied
+ * to SAR even the current period is not over if FIFO is empty.
+ *
+ * ________ ____________________
+ * / \______/ \__________/
+ * ^ ^ * ^ ^
+ * |<-- old SAR -->| |<-- new SAR -->|
+ *
+ * That is the output is active for a whole period.
+ *
+ * Workaround:
+ * Check new SAR less than old SAR and current counter is in errata
+ * windows, write extra old SAR into FIFO and new SAR will effect at
+ * next period.
+ *
+ * Sometime period is quite long, such as over 1 second. If add old SAR
+ * into FIFO unconditional, new SAR have to wait for next period. It
+ * may be too long.
+ *
+ * Turn off the interrupt to ensure that not IRQ and schedule happen
+ * during above operations. If any irq and schedule happen, counter
+ * in PWM will be out of data and take wrong action.
+ *
+ * Add a safety margin 1.5us because it needs some time to complete
+ * IO write.
+ *
+ * Use writel_relaxed() to minimize the interval between two writes to
+ * the SAR register to increase the fastest PWM frequency supported.
+ *
+ * When the PWM period is longer than 2us(or <500kHz), this workaround
+ * can solve this problem. No software workaround is available if PWM
+ * period is shorter than IO write. Just try best to fill old data
+ * into FIFO.
+ */
+ c = clkrate * 1500;
+ do_div(c, NSEC_PER_SEC);
+
+ local_irq_save(flags);
+ val = FIELD_GET(MX3_PWMSR_FIFOAV, readl_relaxed(imx->mmio_base + MX3_PWMSR));
+
+ if (duty_cycles < imx->duty_cycle && (cr & MX3_PWMCR_EN)) {
+ if (period_us < 2) { /* 2us = 500 kHz */
+ /* Best effort attempt to fix up >500 kHz case */
+ udelay(3 * period_us);
+ writel_relaxed(imx->duty_cycle, imx->mmio_base + MX3_PWMSAR);
+ writel_relaxed(imx->duty_cycle, imx->mmio_base + MX3_PWMSAR);
+ } else if (val < MX3_PWMSR_FIFOAV_2WORDS) {
+ val = readl_relaxed(imx->mmio_base + MX3_PWMCNR);
+ /*
+ * If counter is close to period, controller may roll over when
+ * next IO write.
+ */
+ if ((val + c >= duty_cycles && val < imx->duty_cycle) ||
+ val + c >= period_cycles)
+ writel_relaxed(imx->duty_cycle, imx->mmio_base + MX3_PWMSAR);
+ }
+ }
+ writel_relaxed(duty_cycles, imx->mmio_base + MX3_PWMSAR);
+ local_irq_restore(flags);
+
+ writel(period_cycles, imx->mmio_base + MX3_PWMPR);
+
+ /*
+ * Store the duty cycle for future reference in cases where the
+ * MX3_PWMSAR register can't be read (i.e. when the PWM is disabled).
+ */
+ imx->duty_cycle = duty_cycles;
+
+ cr = MX3_PWMCR_PRESCALER_SET(prescale) |
+ MX3_PWMCR_STOPEN | MX3_PWMCR_DOZEN | MX3_PWMCR_WAITEN |
+ FIELD_PREP(MX3_PWMCR_CLKSRC, MX3_PWMCR_CLKSRC_IPG_HIGH) |
+ MX3_PWMCR_DBGEN;
+
+ if (state->polarity == PWM_POLARITY_INVERSED)
+ cr |= FIELD_PREP(MX3_PWMCR_POUTC,
+ MX3_PWMCR_POUTC_INVERTED);
+
+ if (state->enabled)
+ cr |= MX3_PWMCR_EN;
+
+ writel(cr, imx->mmio_base + MX3_PWMCR);
+
+ if (!state->enabled)
+ clk_bulk_disable_unprepare(imx->clks_cnt, imx->clks);
+
+ return 0;
+}
+
+static const struct pwm_ops pwm_imx27_ops = {
+ .apply = pwm_imx27_apply,
+ .get_state = pwm_imx27_get_state,
+};
+
+static const struct of_device_id pwm_imx27_dt_ids[] = {
+ { .compatible = "fsl,imx27-pwm" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, pwm_imx27_dt_ids);
+
+static int pwm_imx27_probe(struct platform_device *pdev)
+{
+ struct pwm_chip *chip;
+ struct pwm_imx27_chip *imx;
+ int ret;
+ u32 pwmcr;
+ int i;
+
+ chip = devm_pwmchip_alloc(&pdev->dev, 1, sizeof(*imx));
+ if (IS_ERR(chip))
+ return PTR_ERR(chip);
+ imx = to_pwm_imx27_chip(chip);
+
+ imx->clks_cnt = ARRAY_SIZE(pwm_imx27_clks);
+ for (i = 0; i < imx->clks_cnt; ++i)
+ imx->clks[i].id = pwm_imx27_clks[i];
+
+ ret = devm_clk_bulk_get(&pdev->dev, imx->clks_cnt, imx->clks);
+
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret,
+ "getting clocks failed\n");
+
+ chip->ops = &pwm_imx27_ops;
+
+ imx->mmio_base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(imx->mmio_base))
+ return PTR_ERR(imx->mmio_base);
+
+ ret = clk_bulk_prepare_enable(imx->clks_cnt, imx->clks);
+ if (ret)
+ return ret;
+
+ /* keep clks on if pwm is running */
+ pwmcr = readl(imx->mmio_base + MX3_PWMCR);
+ if (!(pwmcr & MX3_PWMCR_EN))
+ clk_bulk_disable_unprepare(imx->clks_cnt, imx->clks);
+
+ return devm_pwmchip_add(&pdev->dev, chip);
+}
+
+static struct platform_driver imx_pwm_driver = {
+ .driver = {
+ .name = "pwm-imx27",
+ .of_match_table = pwm_imx27_dt_ids,
+ },
+ .probe = pwm_imx27_probe,
+};
+module_platform_driver(imx_pwm_driver);
+
+MODULE_DESCRIPTION("i.MX27 and later i.MX SoCs Pulse Width Modulator driver");
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");
diff --git a/drivers/pwm/pwm-intel-lgm.c b/drivers/pwm/pwm-intel-lgm.c
new file mode 100644
index 000000000..3ae1324b1
--- /dev/null
+++ b/drivers/pwm/pwm-intel-lgm.c
@@ -0,0 +1,233 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 Intel Corporation.
+ *
+ * Limitations:
+ * - The hardware supports fixed period & configures only 2-wire mode.
+ * - Supports normal polarity. Does not support changing polarity.
+ * - When PWM is disabled, output of PWM will become 0(inactive). It doesn't
+ * keep track of running period.
+ * - When duty cycle is changed, PWM output may be a mix of previous setting
+ * and new setting for the first period. From second period, the output is
+ * based on new setting.
+ * - It is a dedicated PWM fan controller. There are no other consumers for
+ * this PWM controller.
+ */
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+
+#define LGM_PWM_FAN_CON0 0x0
+#define LGM_PWM_FAN_EN_EN BIT(0)
+#define LGM_PWM_FAN_EN_DIS 0x0
+#define LGM_PWM_FAN_EN_MSK BIT(0)
+#define LGM_PWM_FAN_MODE_2WIRE 0x0
+#define LGM_PWM_FAN_MODE_MSK BIT(1)
+#define LGM_PWM_FAN_DC_MSK GENMASK(23, 16)
+
+#define LGM_PWM_FAN_CON1 0x4
+#define LGM_PWM_FAN_MAX_RPM_MSK GENMASK(15, 0)
+
+#define LGM_PWM_MAX_RPM (BIT(16) - 1)
+#define LGM_PWM_DEFAULT_RPM 4000
+#define LGM_PWM_MAX_DUTY_CYCLE (BIT(8) - 1)
+
+#define LGM_PWM_DC_BITS 8
+
+#define LGM_PWM_PERIOD_2WIRE_NS (40 * NSEC_PER_MSEC)
+
+struct lgm_pwm_chip {
+ struct regmap *regmap;
+ u32 period;
+};
+
+static inline struct lgm_pwm_chip *to_lgm_pwm_chip(struct pwm_chip *chip)
+{
+ return pwmchip_get_drvdata(chip);
+}
+
+static int lgm_pwm_enable(struct pwm_chip *chip, bool enable)
+{
+ struct lgm_pwm_chip *pc = to_lgm_pwm_chip(chip);
+ struct regmap *regmap = pc->regmap;
+
+ return regmap_update_bits(regmap, LGM_PWM_FAN_CON0, LGM_PWM_FAN_EN_MSK,
+ enable ? LGM_PWM_FAN_EN_EN : LGM_PWM_FAN_EN_DIS);
+}
+
+static int lgm_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
+ const struct pwm_state *state)
+{
+ struct lgm_pwm_chip *pc = to_lgm_pwm_chip(chip);
+ u32 duty_cycle, val;
+ int ret;
+
+ /* The hardware only supports normal polarity and fixed period. */
+ if (state->polarity != PWM_POLARITY_NORMAL || state->period < pc->period)
+ return -EINVAL;
+
+ if (!state->enabled)
+ return lgm_pwm_enable(chip, 0);
+
+ duty_cycle = min_t(u64, state->duty_cycle, pc->period);
+ val = duty_cycle * LGM_PWM_MAX_DUTY_CYCLE / pc->period;
+
+ ret = regmap_update_bits(pc->regmap, LGM_PWM_FAN_CON0, LGM_PWM_FAN_DC_MSK,
+ FIELD_PREP(LGM_PWM_FAN_DC_MSK, val));
+ if (ret)
+ return ret;
+
+ return lgm_pwm_enable(chip, 1);
+}
+
+static int lgm_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm,
+ struct pwm_state *state)
+{
+ struct lgm_pwm_chip *pc = to_lgm_pwm_chip(chip);
+ u32 duty, val;
+
+ state->enabled = regmap_test_bits(pc->regmap, LGM_PWM_FAN_CON0,
+ LGM_PWM_FAN_EN_EN);
+ state->polarity = PWM_POLARITY_NORMAL;
+ state->period = pc->period; /* fixed period */
+
+ regmap_read(pc->regmap, LGM_PWM_FAN_CON0, &val);
+ duty = FIELD_GET(LGM_PWM_FAN_DC_MSK, val);
+ state->duty_cycle = DIV_ROUND_UP(duty * pc->period, LGM_PWM_MAX_DUTY_CYCLE);
+
+ return 0;
+}
+
+static const struct pwm_ops lgm_pwm_ops = {
+ .get_state = lgm_pwm_get_state,
+ .apply = lgm_pwm_apply,
+};
+
+static void lgm_pwm_init(struct lgm_pwm_chip *pc)
+{
+ struct regmap *regmap = pc->regmap;
+ u32 con0_val;
+
+ con0_val = FIELD_PREP(LGM_PWM_FAN_MODE_MSK, LGM_PWM_FAN_MODE_2WIRE);
+ pc->period = LGM_PWM_PERIOD_2WIRE_NS;
+ regmap_update_bits(regmap, LGM_PWM_FAN_CON1, LGM_PWM_FAN_MAX_RPM_MSK,
+ LGM_PWM_DEFAULT_RPM);
+ regmap_update_bits(regmap, LGM_PWM_FAN_CON0, LGM_PWM_FAN_MODE_MSK,
+ con0_val);
+}
+
+static const struct regmap_config lgm_pwm_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+};
+
+static void lgm_clk_release(void *data)
+{
+ struct clk *clk = data;
+
+ clk_disable_unprepare(clk);
+}
+
+static int lgm_clk_enable(struct device *dev, struct clk *clk)
+{
+ int ret;
+
+ ret = clk_prepare_enable(clk);
+ if (ret)
+ return ret;
+
+ return devm_add_action_or_reset(dev, lgm_clk_release, clk);
+}
+
+static void lgm_reset_control_release(void *data)
+{
+ struct reset_control *rst = data;
+
+ reset_control_assert(rst);
+}
+
+static int lgm_reset_control_deassert(struct device *dev, struct reset_control *rst)
+{
+ int ret;
+
+ ret = reset_control_deassert(rst);
+ if (ret)
+ return ret;
+
+ return devm_add_action_or_reset(dev, lgm_reset_control_release, rst);
+}
+
+static int lgm_pwm_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct reset_control *rst;
+ struct pwm_chip *chip;
+ struct lgm_pwm_chip *pc;
+ void __iomem *io_base;
+ struct clk *clk;
+ int ret;
+
+ chip = devm_pwmchip_alloc(dev, 1, sizeof(*pc));
+ if (IS_ERR(chip))
+ return PTR_ERR(chip);
+ pc = to_lgm_pwm_chip(chip);
+
+ io_base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(io_base))
+ return PTR_ERR(io_base);
+
+ pc->regmap = devm_regmap_init_mmio(dev, io_base, &lgm_pwm_regmap_config);
+ if (IS_ERR(pc->regmap))
+ return dev_err_probe(dev, PTR_ERR(pc->regmap),
+ "failed to init register map\n");
+
+ clk = devm_clk_get(dev, NULL);
+ if (IS_ERR(clk))
+ return dev_err_probe(dev, PTR_ERR(clk), "failed to get clock\n");
+
+ ret = lgm_clk_enable(dev, clk);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to enable clock\n");
+
+ rst = devm_reset_control_get_exclusive(dev, NULL);
+ if (IS_ERR(rst))
+ return dev_err_probe(dev, PTR_ERR(rst),
+ "failed to get reset control\n");
+
+ ret = lgm_reset_control_deassert(dev, rst);
+ if (ret)
+ return dev_err_probe(dev, ret, "cannot deassert reset control\n");
+
+ chip->ops = &lgm_pwm_ops;
+
+ lgm_pwm_init(pc);
+
+ 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 of_device_id lgm_pwm_of_match[] = {
+ { .compatible = "intel,lgm-pwm" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, lgm_pwm_of_match);
+
+static struct platform_driver lgm_pwm_driver = {
+ .driver = {
+ .name = "intel-pwm",
+ .of_match_table = lgm_pwm_of_match,
+ },
+ .probe = lgm_pwm_probe,
+};
+module_platform_driver(lgm_pwm_driver);
+
+MODULE_DESCRIPTION("Intel LGM Pulse Width Modulator driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/pwm/pwm-ipq.c b/drivers/pwm/pwm-ipq.c
new file mode 100644
index 000000000..6b7aec566
--- /dev/null
+++ b/drivers/pwm/pwm-ipq.c
@@ -0,0 +1,264 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/*
+ * Copyright (c) 2016-2017, 2020 The Linux Foundation. All rights reserved.
+ *
+ * Limitations:
+ * - The PWM controller has no publicly available datasheet.
+ * - Each of the four channels is programmed via two 32-bit registers
+ * (REG0 and REG1 at 8-byte stride).
+ * - Period and duty-cycle reconfiguration is fully atomic: new divider,
+ * pre-divider, and high-duration values are latched by setting the
+ * UPDATE bit (bit 30 in REG1). The hardware applies the new settings
+ * at the beginning of the next period without disabling the output,
+ * so the currently running period is always completed.
+ * - On disable (clearing the ENABLE bit 31 in REG1), the hardware
+ * finishes the current period before stopping the output. The pin
+ * is then driven to the inactive (low) level.
+ * - Upon disabling, the hardware resets the pre-divider (PRE_DIV) and divider
+ * fields (PWM_DIV) in REG0 and REG1 to 0x0000 and 0x0001 respectively.
+ * - Only normal polarity is supported.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/math64.h>
+#include <linux/of_device.h>
+#include <linux/bitfield.h>
+#include <linux/units.h>
+
+/* The frequency range supported is 1 Hz to 100 Mhz (clock rate) */
+#define IPQ_PWM_MAX_PERIOD_NS ((u64)NSEC_PER_SEC)
+#define IPQ_PWM_MIN_PERIOD_NS 10
+
+/*
+ * Two 32-bit registers for each PWM: REG0, and REG1.
+ * Base offset for PWM #i is at 8 * #i.
+ */
+#define IPQ_PWM_REG0 0
+#define IPQ_PWM_REG0_PWM_DIV GENMASK(15, 0)
+#define IPQ_PWM_REG0_HI_DURATION GENMASK(31, 16)
+
+#define IPQ_PWM_REG1 4
+#define IPQ_PWM_REG1_PRE_DIV GENMASK(15, 0)
+/*
+ * Enable bit is set to enable output toggling in pwm device.
+ * Update bit is set to trigger the change and is unset automatically
+ * to reflect the changed divider and high duration values in register.
+ */
+#define IPQ_PWM_REG1_UPDATE BIT(30)
+#define IPQ_PWM_REG1_ENABLE BIT(31)
+
+/*
+ * The max value specified for each field is based on the number of bits
+ * in the pwm control register for that field (16-bit)
+ */
+#define IPQ_PWM_MAX_DIV FIELD_MAX(IPQ_PWM_REG0_PWM_DIV)
+
+struct ipq_pwm_chip {
+ void __iomem *mem;
+ unsigned long clk_rate;
+};
+
+static struct ipq_pwm_chip *ipq_pwm_from_chip(struct pwm_chip *chip)
+{
+ return pwmchip_get_drvdata(chip);
+}
+
+static unsigned int ipq_pwm_reg_read(struct pwm_device *pwm, unsigned int reg)
+{
+ struct ipq_pwm_chip *ipq_chip = ipq_pwm_from_chip(pwm->chip);
+ unsigned int off = 8 * pwm->hwpwm + reg;
+
+ return readl(ipq_chip->mem + off);
+}
+
+static void ipq_pwm_reg_write(struct pwm_device *pwm, unsigned int reg,
+ unsigned int val)
+{
+ struct ipq_pwm_chip *ipq_chip = ipq_pwm_from_chip(pwm->chip);
+ unsigned int off = 8 * pwm->hwpwm + reg;
+
+ writel(val, ipq_chip->mem + off);
+}
+
+static int ipq_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
+ const struct pwm_state *state)
+{
+ struct ipq_pwm_chip *ipq_chip = ipq_pwm_from_chip(chip);
+ unsigned int pre_div, pwm_div;
+ u64 period_ns, duty_ns;
+ unsigned long val = 0;
+ unsigned long hi_dur;
+
+ if (!state->enabled) {
+ /* clear IPQ_PWM_REG1_ENABLE */
+ ipq_pwm_reg_write(pwm, IPQ_PWM_REG1, IPQ_PWM_REG1_UPDATE);
+ return 0;
+ }
+
+ if (state->polarity != PWM_POLARITY_NORMAL)
+ return -EINVAL;
+
+ /*
+ * Check the upper and lower bounds for the period as per
+ * hardware limits
+ */
+ if (state->period < IPQ_PWM_MIN_PERIOD_NS)
+ return -ERANGE;
+ period_ns = min(state->period, IPQ_PWM_MAX_PERIOD_NS);
+ duty_ns = min(state->duty_cycle, period_ns);
+
+ /*
+ * Pick the maximal value for PWM_DIV that still allows a
+ * 100% relative duty cycle. This allows a fine grained
+ * selection of duty cycles.
+ */
+ pwm_div = IPQ_PWM_MAX_DIV - 1;
+
+ /*
+ * although mul_u64_u64_div_u64 returns a u64, in practice it
+ * won't overflow due to above constraints. Take the max period
+ * of 10^9 (NSEC_PER_SEC) and the pwm_div + 1 (IPQ_PWM_MAX_DIV)
+ * 10^9 * 10^8
+ * ------------- => which fits well into a 32-bit unsigned int.
+ * 10^9 * 65,535
+ */
+ pre_div = mul_u64_u64_div_u64(period_ns, ipq_chip->clk_rate,
+ (u64)NSEC_PER_SEC * (pwm_div + 1));
+
+ if (!pre_div)
+ return -ERANGE;
+
+ pre_div -= 1;
+
+ if (pre_div > IPQ_PWM_MAX_DIV)
+ pre_div = IPQ_PWM_MAX_DIV;
+
+ /* pwm duty = HI_DUR * (PRE_DIV + 1) / clk_rate */
+ hi_dur = mul_u64_u64_div_u64(duty_ns, ipq_chip->clk_rate,
+ (u64)NSEC_PER_SEC * (pre_div + 1));
+
+ val = FIELD_PREP(IPQ_PWM_REG0_HI_DURATION, hi_dur) |
+ FIELD_PREP(IPQ_PWM_REG0_PWM_DIV, pwm_div);
+ ipq_pwm_reg_write(pwm, IPQ_PWM_REG0, val);
+
+ val = FIELD_PREP(IPQ_PWM_REG1_PRE_DIV, pre_div);
+ ipq_pwm_reg_write(pwm, IPQ_PWM_REG1, val);
+
+ /* PWM enable toggle needs a separate write to REG1 */
+ val |= IPQ_PWM_REG1_UPDATE | IPQ_PWM_REG1_ENABLE;
+ ipq_pwm_reg_write(pwm, IPQ_PWM_REG1, val);
+
+ return 0;
+}
+
+static int ipq_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm,
+ struct pwm_state *state)
+{
+ struct ipq_pwm_chip *ipq_chip = ipq_pwm_from_chip(chip);
+ unsigned int pre_div, pwm_div, hi_dur;
+ u64 effective_div, hi_div;
+ u32 reg0, reg1;
+
+ reg1 = ipq_pwm_reg_read(pwm, IPQ_PWM_REG1);
+ state->enabled = reg1 & IPQ_PWM_REG1_ENABLE;
+
+ if (!state->enabled)
+ return 0;
+
+ reg0 = ipq_pwm_reg_read(pwm, IPQ_PWM_REG0);
+
+ state->polarity = PWM_POLARITY_NORMAL;
+
+ pwm_div = FIELD_GET(IPQ_PWM_REG0_PWM_DIV, reg0);
+ hi_dur = FIELD_GET(IPQ_PWM_REG0_HI_DURATION, reg0);
+ pre_div = FIELD_GET(IPQ_PWM_REG1_PRE_DIV, reg1);
+
+ effective_div = (u64)(pwm_div + 1) * (pre_div + 1);
+
+ /*
+ * effective_div <= 0x100000000, so the multiplication doesn't overflow.
+ */
+ state->period = DIV64_U64_ROUND_UP(effective_div * NSEC_PER_SEC,
+ ipq_chip->clk_rate);
+
+ hi_div = hi_dur * (pre_div + 1);
+ state->duty_cycle = DIV64_U64_ROUND_UP(hi_div * NSEC_PER_SEC,
+ ipq_chip->clk_rate);
+
+ /*
+ * ensure a valid config is passed back to PWM core in case duty_cycle
+ * is > period (>100%)
+ */
+ state->duty_cycle = min(state->duty_cycle, state->period);
+
+ return 0;
+}
+
+static const struct pwm_ops ipq_pwm_ops = {
+ .apply = ipq_pwm_apply,
+ .get_state = ipq_pwm_get_state,
+};
+
+static int ipq_pwm_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct ipq_pwm_chip *pwm;
+ struct pwm_chip *chip;
+ struct clk *clk;
+ int ret;
+
+ chip = devm_pwmchip_alloc(dev, 4, sizeof(*pwm));
+ if (IS_ERR(chip))
+ return PTR_ERR(chip);
+ pwm = ipq_pwm_from_chip(chip);
+
+ pwm->mem = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(pwm->mem))
+ return dev_err_probe(dev, PTR_ERR(pwm->mem),
+ "Failed to acquire resource\n");
+
+ clk = devm_clk_get_enabled(dev, NULL);
+ if (IS_ERR(clk))
+ return dev_err_probe(dev, PTR_ERR(clk),
+ "Failed to get clock\n");
+
+ ret = devm_clk_rate_exclusive_get(dev, clk);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to lock clock rate\n");
+
+ pwm->clk_rate = clk_get_rate(clk);
+ if (!pwm->clk_rate)
+ return dev_err_probe(dev, -EINVAL, "Failed due to clock rate being zero\n");
+
+ chip->ops = &ipq_pwm_ops;
+
+ 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 of_device_id pwm_ipq_dt_match[] = {
+ { .compatible = "qcom,ipq6018-pwm" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, pwm_ipq_dt_match);
+
+static struct platform_driver ipq_pwm_driver = {
+ .driver = {
+ .name = "ipq-pwm",
+ .of_match_table = pwm_ipq_dt_match,
+ },
+ .probe = ipq_pwm_probe,
+};
+
+module_platform_driver(ipq_pwm_driver);
+
+MODULE_DESCRIPTION("Qualcomm IPQ PWM driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/pwm/pwm-iqs620a.c b/drivers/pwm/pwm-iqs620a.c
new file mode 100644
index 000000000..13e5e138c
--- /dev/null
+++ b/drivers/pwm/pwm-iqs620a.c
@@ -0,0 +1,252 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Azoteq IQS620A PWM Generator
+ *
+ * Copyright (C) 2019 Jeff LaBundy <jeff@labundy.com>
+ *
+ * Limitations:
+ * - The period is fixed to 1 ms and is generated continuously despite changes
+ * to the duty cycle or enable/disable state.
+ * - Changes to the duty cycle or enable/disable state take effect immediately
+ * and may result in a glitch during the period in which the change is made.
+ * - The device cannot generate a 0% duty cycle. For duty cycles below 1 / 256
+ * ms, the output is disabled and relies upon an external pull-down resistor
+ * to hold the GPIO3/LTX pin low.
+ */
+
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/mfd/iqs62x.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/notifier.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+#define IQS620_PWR_SETTINGS 0xd2
+#define IQS620_PWR_SETTINGS_PWM_OUT BIT(7)
+
+#define IQS620_PWM_DUTY_CYCLE 0xd8
+
+#define IQS620_PWM_PERIOD_NS 1000000
+
+struct iqs620_pwm_private {
+ struct iqs62x_core *iqs62x;
+ struct device *dev;
+ struct notifier_block notifier;
+ struct mutex lock;
+ unsigned int duty_scale;
+};
+
+static inline struct iqs620_pwm_private *iqs620_pwm_from_chip(struct pwm_chip *chip)
+{
+ return pwmchip_get_drvdata(chip);
+}
+
+static int iqs620_pwm_init(struct iqs620_pwm_private *iqs620_pwm,
+ unsigned int duty_scale)
+{
+ struct iqs62x_core *iqs62x = iqs620_pwm->iqs62x;
+ int ret;
+
+ if (!duty_scale)
+ return regmap_clear_bits(iqs62x->regmap, IQS620_PWR_SETTINGS,
+ IQS620_PWR_SETTINGS_PWM_OUT);
+
+ ret = regmap_write(iqs62x->regmap, IQS620_PWM_DUTY_CYCLE,
+ duty_scale - 1);
+ if (ret)
+ return ret;
+
+ return regmap_set_bits(iqs62x->regmap, IQS620_PWR_SETTINGS,
+ IQS620_PWR_SETTINGS_PWM_OUT);
+}
+
+static int iqs620_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
+ const struct pwm_state *state)
+{
+ struct iqs620_pwm_private *iqs620_pwm;
+ unsigned int duty_cycle;
+ unsigned int duty_scale;
+ int ret;
+
+ if (state->polarity != PWM_POLARITY_NORMAL)
+ return -EINVAL;
+
+ if (state->period < IQS620_PWM_PERIOD_NS)
+ return -EINVAL;
+
+ iqs620_pwm = iqs620_pwm_from_chip(chip);
+
+ /*
+ * The duty cycle generated by the device is calculated as follows:
+ *
+ * duty_cycle = (IQS620_PWM_DUTY_CYCLE + 1) / 256 * 1 ms
+ *
+ * ...where IQS620_PWM_DUTY_CYCLE is a register value between 0 and 255
+ * (inclusive). Therefore the lowest duty cycle the device can generate
+ * while the output is enabled is 1 / 256 ms.
+ *
+ * For lower duty cycles (e.g. 0), the PWM output is simply disabled to
+ * allow an external pull-down resistor to hold the GPIO3/LTX pin low.
+ */
+ duty_cycle = min_t(u64, state->duty_cycle, IQS620_PWM_PERIOD_NS);
+ duty_scale = duty_cycle * 256 / IQS620_PWM_PERIOD_NS;
+
+ if (!state->enabled)
+ duty_scale = 0;
+
+ mutex_lock(&iqs620_pwm->lock);
+
+ ret = iqs620_pwm_init(iqs620_pwm, duty_scale);
+ if (!ret)
+ iqs620_pwm->duty_scale = duty_scale;
+
+ mutex_unlock(&iqs620_pwm->lock);
+
+ return ret;
+}
+
+static int iqs620_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm,
+ struct pwm_state *state)
+{
+ struct iqs620_pwm_private *iqs620_pwm;
+
+ iqs620_pwm = iqs620_pwm_from_chip(chip);
+
+ mutex_lock(&iqs620_pwm->lock);
+
+ /*
+ * Since the device cannot generate a 0% duty cycle, requests to do so
+ * cause subsequent calls to iqs620_pwm_get_state to report the output
+ * as disabled. This is not ideal, but is the best compromise based on
+ * the capabilities of the device.
+ */
+ state->enabled = iqs620_pwm->duty_scale > 0;
+ state->duty_cycle = DIV_ROUND_UP(iqs620_pwm->duty_scale *
+ IQS620_PWM_PERIOD_NS, 256);
+
+ mutex_unlock(&iqs620_pwm->lock);
+
+ state->period = IQS620_PWM_PERIOD_NS;
+ state->polarity = PWM_POLARITY_NORMAL;
+
+ return 0;
+}
+
+static int iqs620_pwm_notifier(struct notifier_block *notifier,
+ unsigned long event_flags, void *context)
+{
+ struct iqs620_pwm_private *iqs620_pwm;
+ int ret;
+
+ if (!(event_flags & BIT(IQS62X_EVENT_SYS_RESET)))
+ return NOTIFY_DONE;
+
+ iqs620_pwm = container_of(notifier, struct iqs620_pwm_private,
+ notifier);
+
+ mutex_lock(&iqs620_pwm->lock);
+
+ /*
+ * The parent MFD driver already prints an error message in the event
+ * of a device reset, so nothing else is printed here unless there is
+ * an additional failure.
+ */
+ ret = iqs620_pwm_init(iqs620_pwm, iqs620_pwm->duty_scale);
+
+ mutex_unlock(&iqs620_pwm->lock);
+
+ if (ret) {
+ dev_err(iqs620_pwm->dev,
+ "Failed to re-initialize device: %d\n", ret);
+ return NOTIFY_BAD;
+ }
+
+ return NOTIFY_OK;
+}
+
+static const struct pwm_ops iqs620_pwm_ops = {
+ .apply = iqs620_pwm_apply,
+ .get_state = iqs620_pwm_get_state,
+};
+
+static void iqs620_pwm_notifier_unregister(void *context)
+{
+ struct iqs620_pwm_private *iqs620_pwm = context;
+ int ret;
+
+ ret = blocking_notifier_chain_unregister(&iqs620_pwm->iqs62x->nh,
+ &iqs620_pwm->notifier);
+ if (ret)
+ dev_err(iqs620_pwm->dev,
+ "Failed to unregister notifier: %d\n", ret);
+}
+
+static int iqs620_pwm_probe(struct platform_device *pdev)
+{
+ struct iqs62x_core *iqs62x = dev_get_drvdata(pdev->dev.parent);
+ struct pwm_chip *chip;
+ struct iqs620_pwm_private *iqs620_pwm;
+ unsigned int val;
+ int ret;
+
+ chip = devm_pwmchip_alloc(&pdev->dev, 1, sizeof(*iqs620_pwm));
+ if (IS_ERR(chip))
+ return PTR_ERR(chip);
+
+ iqs620_pwm = iqs620_pwm_from_chip(chip);
+ iqs620_pwm->dev = &pdev->dev;
+ iqs620_pwm->iqs62x = iqs62x;
+
+ ret = regmap_read(iqs62x->regmap, IQS620_PWR_SETTINGS, &val);
+ if (ret)
+ return ret;
+
+ if (val & IQS620_PWR_SETTINGS_PWM_OUT) {
+ ret = regmap_read(iqs62x->regmap, IQS620_PWM_DUTY_CYCLE, &val);
+ if (ret)
+ return ret;
+
+ iqs620_pwm->duty_scale = val + 1;
+ }
+
+ chip->ops = &iqs620_pwm_ops;
+
+ mutex_init(&iqs620_pwm->lock);
+
+ iqs620_pwm->notifier.notifier_call = iqs620_pwm_notifier;
+ ret = blocking_notifier_chain_register(&iqs620_pwm->iqs62x->nh,
+ &iqs620_pwm->notifier);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to register notifier: %d\n", ret);
+ return ret;
+ }
+
+ ret = devm_add_action_or_reset(&pdev->dev,
+ iqs620_pwm_notifier_unregister,
+ iqs620_pwm);
+ if (ret)
+ return ret;
+
+ ret = devm_pwmchip_add(&pdev->dev, chip);
+ if (ret)
+ dev_err(&pdev->dev, "Failed to add device: %d\n", ret);
+
+ return ret;
+}
+
+static struct platform_driver iqs620_pwm_platform_driver = {
+ .driver = {
+ .name = "iqs620a-pwm",
+ },
+ .probe = iqs620_pwm_probe,
+};
+module_platform_driver(iqs620_pwm_platform_driver);
+
+MODULE_AUTHOR("Jeff LaBundy <jeff@labundy.com>");
+MODULE_DESCRIPTION("Azoteq IQS620A PWM Generator");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:iqs620a-pwm");
diff --git a/drivers/pwm/pwm-jz4740.c b/drivers/pwm/pwm-jz4740.c
new file mode 100644
index 000000000..0db1474c7
--- /dev/null
+++ b/drivers/pwm/pwm-jz4740.c
@@ -0,0 +1,280 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2010, Lars-Peter Clausen <lars@metafoo.de>
+ * JZ4740 platform PWM support
+ *
+ * Limitations:
+ * - The .apply callback doesn't complete the currently running period before
+ * reconfiguring the hardware.
+ */
+
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/mfd/ingenic-tcu.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+#include <linux/regmap.h>
+
+struct soc_info {
+ unsigned int num_pwms;
+};
+
+struct jz4740_pwm_chip {
+ struct regmap *map;
+ struct clk *clk[];
+};
+
+static inline struct jz4740_pwm_chip *to_jz4740(struct pwm_chip *chip)
+{
+ return pwmchip_get_drvdata(chip);
+}
+
+static bool jz4740_pwm_can_use_chn(struct pwm_chip *chip, unsigned int channel)
+{
+ /* Enable all TCU channels for PWM use by default except channels 0/1 */
+ u32 pwm_channels_mask = GENMASK(chip->npwm - 1, 2);
+
+ device_property_read_u32(pwmchip_parent(chip)->parent,
+ "ingenic,pwm-channels-mask",
+ &pwm_channels_mask);
+
+ return !!(pwm_channels_mask & BIT(channel));
+}
+
+static int jz4740_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct jz4740_pwm_chip *jz = to_jz4740(chip);
+ struct clk *clk;
+ char name[16];
+ int err;
+
+ if (!jz4740_pwm_can_use_chn(chip, pwm->hwpwm))
+ return -EBUSY;
+
+ snprintf(name, sizeof(name), "timer%u", pwm->hwpwm);
+
+ clk = clk_get(pwmchip_parent(chip), name);
+ if (IS_ERR(clk)) {
+ dev_err(pwmchip_parent(chip),
+ "error %pe: Failed to get clock\n", clk);
+ return PTR_ERR(clk);
+ }
+
+ err = clk_prepare_enable(clk);
+ if (err < 0) {
+ clk_put(clk);
+ return err;
+ }
+
+ jz->clk[pwm->hwpwm] = clk;
+
+ return 0;
+}
+
+static void jz4740_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct jz4740_pwm_chip *jz = to_jz4740(chip);
+ struct clk *clk = jz->clk[pwm->hwpwm];
+
+ clk_disable_unprepare(clk);
+ clk_put(clk);
+}
+
+static int jz4740_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct jz4740_pwm_chip *jz = to_jz4740(chip);
+
+ /* Enable PWM output */
+ regmap_set_bits(jz->map, TCU_REG_TCSRc(pwm->hwpwm), TCU_TCSR_PWM_EN);
+
+ /* Start counter */
+ regmap_write(jz->map, TCU_REG_TESR, BIT(pwm->hwpwm));
+
+ return 0;
+}
+
+static void jz4740_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct jz4740_pwm_chip *jz = to_jz4740(chip);
+
+ /*
+ * Set duty > period. This trick allows the TCU channels in TCU2 mode to
+ * properly return to their init level.
+ */
+ regmap_write(jz->map, TCU_REG_TDHRc(pwm->hwpwm), 0xffff);
+ regmap_write(jz->map, TCU_REG_TDFRc(pwm->hwpwm), 0x0);
+
+ /*
+ * Disable PWM output.
+ * In TCU2 mode (channel 1/2 on JZ4750+), this must be done before the
+ * counter is stopped, while in TCU1 mode the order does not matter.
+ */
+ regmap_clear_bits(jz->map, TCU_REG_TCSRc(pwm->hwpwm), TCU_TCSR_PWM_EN);
+
+ /* Stop counter */
+ regmap_write(jz->map, TCU_REG_TECR, BIT(pwm->hwpwm));
+}
+
+static int jz4740_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
+ const struct pwm_state *state)
+{
+ struct jz4740_pwm_chip *jz = to_jz4740(chip);
+ unsigned long long tmp = 0xffffull * NSEC_PER_SEC;
+ struct clk *clk = jz->clk[pwm->hwpwm];
+ unsigned long period, duty;
+ long rate;
+ int err;
+
+ /*
+ * Limit the clock to a maximum rate that still gives us a period value
+ * which fits in 16 bits.
+ */
+ do_div(tmp, state->period);
+
+ /*
+ * /!\ IMPORTANT NOTE:
+ * -------------------
+ * This code relies on the fact that clk_round_rate() will always round
+ * down, which is not a valid assumption given by the clk API, but only
+ * happens to be true with the clk drivers used for Ingenic SoCs.
+ *
+ * Right now, there is no alternative as the clk API does not have a
+ * round-down function (and won't have one for a while), but if it ever
+ * comes to light, a round-down function should be used instead.
+ */
+ rate = clk_round_rate(clk, tmp);
+ if (rate < 0) {
+ dev_err(pwmchip_parent(chip), "Unable to round rate: %ld\n", rate);
+ return rate;
+ }
+
+ /* Calculate period value */
+ tmp = (unsigned long long)rate * state->period;
+ do_div(tmp, NSEC_PER_SEC);
+ period = tmp;
+
+ /* Calculate duty value */
+ tmp = (unsigned long long)rate * state->duty_cycle;
+ do_div(tmp, NSEC_PER_SEC);
+ duty = tmp;
+
+ if (duty >= period)
+ duty = period - 1;
+
+ jz4740_pwm_disable(chip, pwm);
+
+ err = clk_set_rate(clk, rate);
+ if (err) {
+ dev_err(pwmchip_parent(chip), "Unable to set rate: %d\n", err);
+ return err;
+ }
+
+ /* Reset counter to 0 */
+ regmap_write(jz->map, TCU_REG_TCNTc(pwm->hwpwm), 0);
+
+ /* Set duty */
+ regmap_write(jz->map, TCU_REG_TDHRc(pwm->hwpwm), duty);
+
+ /* Set period */
+ regmap_write(jz->map, TCU_REG_TDFRc(pwm->hwpwm), period);
+
+ /* Set abrupt shutdown */
+ regmap_set_bits(jz->map, TCU_REG_TCSRc(pwm->hwpwm),
+ TCU_TCSR_PWM_SD);
+
+ /*
+ * Set polarity.
+ *
+ * The PWM starts in inactive state until the internal timer reaches the
+ * duty value, then becomes active until the timer reaches the period
+ * value. In theory, we should then use (period - duty) as the real duty
+ * value, as a high duty value would otherwise result in the PWM pin
+ * being inactive most of the time.
+ *
+ * Here, we don't do that, and instead invert the polarity of the PWM
+ * when it is active. This trick makes the PWM start with its active
+ * state instead of its inactive state.
+ */
+ if ((state->polarity == PWM_POLARITY_NORMAL) ^ state->enabled)
+ regmap_clear_bits(jz->map, TCU_REG_TCSRc(pwm->hwpwm),
+ TCU_TCSR_PWM_INITL_HIGH);
+ else
+ regmap_set_bits(jz->map, TCU_REG_TCSRc(pwm->hwpwm),
+ TCU_TCSR_PWM_INITL_HIGH);
+
+ if (state->enabled)
+ jz4740_pwm_enable(chip, pwm);
+
+ return 0;
+}
+
+static const struct pwm_ops jz4740_pwm_ops = {
+ .request = jz4740_pwm_request,
+ .free = jz4740_pwm_free,
+ .apply = jz4740_pwm_apply,
+};
+
+static int jz4740_pwm_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct pwm_chip *chip;
+ struct jz4740_pwm_chip *jz;
+ const struct soc_info *info;
+
+ info = device_get_match_data(dev);
+ if (!info)
+ return -EINVAL;
+
+ chip = devm_pwmchip_alloc(dev, info->num_pwms, struct_size(jz, clk, info->num_pwms));
+ if (IS_ERR(chip))
+ return PTR_ERR(chip);
+ jz = to_jz4740(chip);
+
+ jz->map = device_node_to_regmap(dev->parent->of_node);
+ if (IS_ERR(jz->map)) {
+ dev_err(dev, "regmap not found: %ld\n", PTR_ERR(jz->map));
+ return PTR_ERR(jz->map);
+ }
+
+ chip->ops = &jz4740_pwm_ops;
+
+ return devm_pwmchip_add(dev, chip);
+}
+
+static const struct soc_info jz4740_soc_info = {
+ .num_pwms = 8,
+};
+
+static const struct soc_info jz4725b_soc_info = {
+ .num_pwms = 6,
+};
+
+static const struct soc_info x1000_soc_info = {
+ .num_pwms = 5,
+};
+
+static const struct of_device_id jz4740_pwm_dt_ids[] = {
+ { .compatible = "ingenic,jz4740-pwm", .data = &jz4740_soc_info },
+ { .compatible = "ingenic,jz4725b-pwm", .data = &jz4725b_soc_info },
+ { .compatible = "ingenic,x1000-pwm", .data = &x1000_soc_info },
+ { }
+};
+MODULE_DEVICE_TABLE(of, jz4740_pwm_dt_ids);
+
+static struct platform_driver jz4740_pwm_driver = {
+ .driver = {
+ .name = "jz4740-pwm",
+ .of_match_table = jz4740_pwm_dt_ids,
+ },
+ .probe = jz4740_pwm_probe,
+};
+module_platform_driver(jz4740_pwm_driver);
+
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_DESCRIPTION("Ingenic JZ4740 PWM driver");
+MODULE_ALIAS("platform:jz4740-pwm");
+MODULE_LICENSE("GPL");
diff --git a/drivers/pwm/pwm-keembay.c b/drivers/pwm/pwm-keembay.c
new file mode 100644
index 000000000..2e282e067
--- /dev/null
+++ b/drivers/pwm/pwm-keembay.c
@@ -0,0 +1,233 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Intel Keem Bay PWM driver
+ *
+ * Copyright (C) 2020 Intel Corporation
+ * Authors: Lai Poey Seng <poey.seng.lai@intel.com>
+ * Vineetha G. Jaya Kumaran <vineetha.g.jaya.kumaran@intel.com>
+ *
+ * Limitations:
+ * - Upon disabling a channel, the currently running
+ * period will not be completed. However, upon
+ * reconfiguration of the duty cycle/period, the
+ * currently running period will be completed first.
+ */
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+#include <linux/regmap.h>
+
+#define KMB_TOTAL_PWM_CHANNELS 6
+#define KMB_PWM_COUNT_MAX U16_MAX
+#define KMB_PWM_EN_BIT BIT(31)
+
+/* Mask */
+#define KMB_PWM_HIGH_MASK GENMASK(31, 16)
+#define KMB_PWM_LOW_MASK GENMASK(15, 0)
+#define KMB_PWM_LEADIN_MASK GENMASK(30, 0)
+
+/* PWM Register offset */
+#define KMB_PWM_LEADIN_OFFSET(ch) (0x00 + 4 * (ch))
+#define KMB_PWM_HIGHLOW_OFFSET(ch) (0x20 + 4 * (ch))
+
+struct keembay_pwm {
+ struct device *dev;
+ struct clk *clk;
+ void __iomem *base;
+};
+
+static inline struct keembay_pwm *to_keembay_pwm_dev(struct pwm_chip *chip)
+{
+ return pwmchip_get_drvdata(chip);
+}
+
+static void keembay_clk_unprepare(void *data)
+{
+ clk_disable_unprepare(data);
+}
+
+static int keembay_clk_enable(struct device *dev, struct clk *clk)
+{
+ int ret;
+
+ ret = clk_prepare_enable(clk);
+ if (ret)
+ return ret;
+
+ return devm_add_action_or_reset(dev, keembay_clk_unprepare, clk);
+}
+
+/*
+ * With gcc 10, CONFIG_CC_OPTIMIZE_FOR_SIZE and only "inline" instead of
+ * "__always_inline" this fails to compile because the compiler doesn't notice
+ * for all valid masks (e.g. KMB_PWM_LEADIN_MASK) that they are ok.
+ */
+static __always_inline void keembay_pwm_update_bits(struct keembay_pwm *priv, u32 mask,
+ u32 val, u32 offset)
+{
+ u32 buff = readl(priv->base + offset);
+
+ buff = u32_replace_bits(buff, val, mask);
+ writel(buff, priv->base + offset);
+}
+
+static void keembay_pwm_enable(struct keembay_pwm *priv, int ch)
+{
+ keembay_pwm_update_bits(priv, KMB_PWM_EN_BIT, 1,
+ KMB_PWM_LEADIN_OFFSET(ch));
+}
+
+static void keembay_pwm_disable(struct keembay_pwm *priv, int ch)
+{
+ keembay_pwm_update_bits(priv, KMB_PWM_EN_BIT, 0,
+ KMB_PWM_LEADIN_OFFSET(ch));
+}
+
+static int keembay_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm,
+ struct pwm_state *state)
+{
+ struct keembay_pwm *priv = to_keembay_pwm_dev(chip);
+ unsigned long long high, low;
+ unsigned long clk_rate;
+ u32 highlow;
+
+ clk_rate = clk_get_rate(priv->clk);
+
+ /* Read channel enabled status */
+ highlow = readl(priv->base + KMB_PWM_LEADIN_OFFSET(pwm->hwpwm));
+ if (highlow & KMB_PWM_EN_BIT)
+ state->enabled = true;
+ else
+ state->enabled = false;
+
+ /* Read period and duty cycle */
+ highlow = readl(priv->base + KMB_PWM_HIGHLOW_OFFSET(pwm->hwpwm));
+ low = FIELD_GET(KMB_PWM_LOW_MASK, highlow) * NSEC_PER_SEC;
+ high = FIELD_GET(KMB_PWM_HIGH_MASK, highlow) * NSEC_PER_SEC;
+ state->duty_cycle = DIV_ROUND_UP_ULL(high, clk_rate);
+ state->period = DIV_ROUND_UP_ULL(high + low, clk_rate);
+ state->polarity = PWM_POLARITY_NORMAL;
+
+ return 0;
+}
+
+static int keembay_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
+ const struct pwm_state *state)
+{
+ struct keembay_pwm *priv = to_keembay_pwm_dev(chip);
+ struct pwm_state current_state;
+ unsigned long long div;
+ unsigned long clk_rate;
+ u32 pwm_count = 0;
+ u16 high, low;
+
+ if (state->polarity != PWM_POLARITY_NORMAL)
+ return -EINVAL;
+
+ /*
+ * Configure the pwm repeat count as infinite at (15:0) and leadin
+ * low time as 0 at (30:16), which is in terms of clock cycles.
+ */
+ keembay_pwm_update_bits(priv, KMB_PWM_LEADIN_MASK, 0,
+ KMB_PWM_LEADIN_OFFSET(pwm->hwpwm));
+
+ keembay_pwm_get_state(chip, pwm, &current_state);
+
+ if (!state->enabled) {
+ if (current_state.enabled)
+ keembay_pwm_disable(priv, pwm->hwpwm);
+ return 0;
+ }
+
+ /*
+ * The upper 16 bits and lower 16 bits of the KMB_PWM_HIGHLOW_OFFSET
+ * register contain the high time and low time of waveform accordingly.
+ * All the values are in terms of clock cycles.
+ */
+
+ clk_rate = clk_get_rate(priv->clk);
+ div = clk_rate * state->duty_cycle;
+ div = DIV_ROUND_DOWN_ULL(div, NSEC_PER_SEC);
+ if (div > KMB_PWM_COUNT_MAX)
+ return -ERANGE;
+
+ high = div;
+ div = clk_rate * state->period;
+ div = DIV_ROUND_DOWN_ULL(div, NSEC_PER_SEC);
+ div = div - high;
+ if (div > KMB_PWM_COUNT_MAX)
+ return -ERANGE;
+
+ low = div;
+
+ pwm_count = FIELD_PREP(KMB_PWM_HIGH_MASK, high) |
+ FIELD_PREP(KMB_PWM_LOW_MASK, low);
+
+ writel(pwm_count, priv->base + KMB_PWM_HIGHLOW_OFFSET(pwm->hwpwm));
+
+ if (state->enabled && !current_state.enabled)
+ keembay_pwm_enable(priv, pwm->hwpwm);
+
+ return 0;
+}
+
+static const struct pwm_ops keembay_pwm_ops = {
+ .apply = keembay_pwm_apply,
+ .get_state = keembay_pwm_get_state,
+};
+
+static int keembay_pwm_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct pwm_chip *chip;
+ struct keembay_pwm *priv;
+ int ret;
+
+ chip = devm_pwmchip_alloc(dev, KMB_TOTAL_PWM_CHANNELS, sizeof(*priv));
+ if (IS_ERR(chip))
+ return PTR_ERR(chip);
+ priv = to_keembay_pwm_dev(chip);
+
+ priv->clk = devm_clk_get(dev, NULL);
+ if (IS_ERR(priv->clk))
+ return dev_err_probe(dev, PTR_ERR(priv->clk), "Failed to get clock\n");
+
+ priv->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(priv->base))
+ return PTR_ERR(priv->base);
+
+ ret = keembay_clk_enable(dev, priv->clk);
+ if (ret)
+ return ret;
+
+ chip->ops = &keembay_pwm_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 keembay_pwm_of_match[] = {
+ { .compatible = "intel,keembay-pwm" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, keembay_pwm_of_match);
+
+static struct platform_driver keembay_pwm_driver = {
+ .probe = keembay_pwm_probe,
+ .driver = {
+ .name = "pwm-keembay",
+ .of_match_table = keembay_pwm_of_match,
+ },
+};
+module_platform_driver(keembay_pwm_driver);
+
+MODULE_ALIAS("platform:pwm-keembay");
+MODULE_DESCRIPTION("Intel Keem Bay PWM driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/pwm/pwm-loongson.c b/drivers/pwm/pwm-loongson.c
new file mode 100644
index 000000000..f2fb35b7a
--- /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 50000000 /* 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[] = {
+ { .id = "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-lp3943.c b/drivers/pwm/pwm-lp3943.c
new file mode 100644
index 000000000..10537e74b
--- /dev/null
+++ b/drivers/pwm/pwm-lp3943.c
@@ -0,0 +1,317 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * TI/National Semiconductor LP3943 PWM driver
+ *
+ * Copyright 2013 Texas Instruments
+ *
+ * Author: Milo Kim <milo.kim@ti.com>
+ */
+
+#include <linux/err.h>
+#include <linux/mfd/lp3943.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+#include <linux/slab.h>
+
+#define LP3943_MAX_DUTY 255
+#define LP3943_MIN_PERIOD 6250
+#define LP3943_MAX_PERIOD 1600000
+
+struct lp3943_pwm {
+ struct lp3943 *lp3943;
+ struct lp3943_platform_data *pdata;
+ struct lp3943_pwm_map pwm_map[LP3943_NUM_PWMS];
+};
+
+static inline struct lp3943_pwm *to_lp3943_pwm(struct pwm_chip *chip)
+{
+ return pwmchip_get_drvdata(chip);
+}
+
+static struct lp3943_pwm_map *
+lp3943_pwm_request_map(struct lp3943_pwm *lp3943_pwm, int hwpwm)
+{
+ struct lp3943_platform_data *pdata = lp3943_pwm->pdata;
+ struct lp3943 *lp3943 = lp3943_pwm->lp3943;
+ struct lp3943_pwm_map *pwm_map = &lp3943_pwm->pwm_map[hwpwm];
+ int i, offset;
+
+ pwm_map->output = pdata->pwms[hwpwm]->output;
+ pwm_map->num_outputs = pdata->pwms[hwpwm]->num_outputs;
+
+ for (i = 0; i < pwm_map->num_outputs; i++) {
+ offset = pwm_map->output[i];
+
+ /* Return an error if the pin is already assigned */
+ if (test_and_set_bit(offset, &lp3943->pin_used))
+ return ERR_PTR(-EBUSY);
+ }
+
+ return pwm_map;
+}
+
+static int lp3943_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct lp3943_pwm *lp3943_pwm = to_lp3943_pwm(chip);
+ struct lp3943_pwm_map *pwm_map;
+
+ pwm_map = lp3943_pwm_request_map(lp3943_pwm, pwm->hwpwm);
+ if (IS_ERR(pwm_map))
+ return PTR_ERR(pwm_map);
+
+ return 0;
+}
+
+static void lp3943_pwm_free_map(struct lp3943_pwm *lp3943_pwm,
+ struct lp3943_pwm_map *pwm_map)
+{
+ struct lp3943 *lp3943 = lp3943_pwm->lp3943;
+ int i, offset;
+
+ for (i = 0; i < pwm_map->num_outputs; i++) {
+ offset = pwm_map->output[i];
+ clear_bit(offset, &lp3943->pin_used);
+ }
+}
+
+static void lp3943_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct lp3943_pwm *lp3943_pwm = to_lp3943_pwm(chip);
+ struct lp3943_pwm_map *pwm_map = &lp3943_pwm->pwm_map[pwm->hwpwm];
+
+ lp3943_pwm_free_map(lp3943_pwm, pwm_map);
+}
+
+static int lp3943_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
+ u64 duty_ns, u64 period_ns)
+{
+ struct lp3943_pwm *lp3943_pwm = to_lp3943_pwm(chip);
+ struct lp3943 *lp3943 = lp3943_pwm->lp3943;
+ u8 val, reg_duty, reg_prescale;
+ int err;
+
+ /*
+ * How to configure the LP3943 PWMs
+ *
+ * 1) Period = 6250 ~ 1600000
+ * 2) Prescale = period / 6250 -1
+ * 3) Duty = input duty
+ *
+ * Prescale and duty are register values
+ */
+
+ if (pwm->hwpwm == 0) {
+ reg_prescale = LP3943_REG_PRESCALE0;
+ reg_duty = LP3943_REG_PWM0;
+ } else {
+ reg_prescale = LP3943_REG_PRESCALE1;
+ reg_duty = LP3943_REG_PWM1;
+ }
+
+ /*
+ * Note that after this clamping, period_ns fits into an int. This is
+ * helpful because we can resort to integer division below instead of
+ * the (more expensive) 64 bit division.
+ */
+ period_ns = clamp(period_ns, (u64)LP3943_MIN_PERIOD, (u64)LP3943_MAX_PERIOD);
+ val = (u8)((int)period_ns / LP3943_MIN_PERIOD - 1);
+
+ err = lp3943_write_byte(lp3943, reg_prescale, val);
+ if (err)
+ return err;
+
+ duty_ns = min(duty_ns, period_ns);
+ val = (u8)((int)duty_ns * LP3943_MAX_DUTY / (int)period_ns);
+
+ return lp3943_write_byte(lp3943, reg_duty, val);
+}
+
+static int lp3943_pwm_set_mode(struct lp3943_pwm *lp3943_pwm,
+ struct lp3943_pwm_map *pwm_map,
+ u8 val)
+{
+ struct lp3943 *lp3943 = lp3943_pwm->lp3943;
+ const struct lp3943_reg_cfg *mux = lp3943->mux_cfg;
+ int i, index, err;
+
+ for (i = 0; i < pwm_map->num_outputs; i++) {
+ index = pwm_map->output[i];
+ err = lp3943_update_bits(lp3943, mux[index].reg,
+ mux[index].mask,
+ val << mux[index].shift);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
+static int lp3943_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct lp3943_pwm *lp3943_pwm = to_lp3943_pwm(chip);
+ struct lp3943_pwm_map *pwm_map = &lp3943_pwm->pwm_map[pwm->hwpwm];
+ u8 val;
+
+ if (pwm->hwpwm == 0)
+ val = LP3943_DIM_PWM0;
+ else
+ val = LP3943_DIM_PWM1;
+
+ /*
+ * Each PWM generator is set to control any of outputs of LP3943.
+ * To enable/disable the PWM, these output pins should be configured.
+ */
+
+ return lp3943_pwm_set_mode(lp3943_pwm, pwm_map, val);
+}
+
+static void lp3943_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct lp3943_pwm *lp3943_pwm = to_lp3943_pwm(chip);
+ struct lp3943_pwm_map *pwm_map = &lp3943_pwm->pwm_map[pwm->hwpwm];
+
+ /*
+ * LP3943 outputs are open-drain, so the pin should be configured
+ * when the PWM is disabled.
+ */
+
+ lp3943_pwm_set_mode(lp3943_pwm, pwm_map, LP3943_GPIO_OUT_HIGH);
+}
+
+static int lp3943_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
+ const struct pwm_state *state)
+{
+ int err;
+
+ if (state->polarity != PWM_POLARITY_NORMAL)
+ return -EINVAL;
+
+ if (!state->enabled) {
+ if (pwm->state.enabled)
+ lp3943_pwm_disable(chip, pwm);
+ return 0;
+ }
+
+ err = lp3943_pwm_config(chip, pwm, state->duty_cycle, state->period);
+ if (err)
+ return err;
+
+ if (!pwm->state.enabled)
+ err = lp3943_pwm_enable(chip, pwm);
+
+ return err;
+}
+
+static const struct pwm_ops lp3943_pwm_ops = {
+ .request = lp3943_pwm_request,
+ .free = lp3943_pwm_free,
+ .apply = lp3943_pwm_apply,
+};
+
+static int lp3943_pwm_parse_dt(struct device *dev,
+ struct lp3943_pwm *lp3943_pwm)
+{
+ static const char * const name[] = { "ti,pwm0", "ti,pwm1", };
+ struct device_node *node = dev->of_node;
+ struct lp3943_platform_data *pdata;
+ struct lp3943_pwm_map *pwm_map;
+ enum lp3943_pwm_output *output;
+ int i, err, num_outputs, count = 0;
+
+ if (!node)
+ return -EINVAL;
+
+ pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return -ENOMEM;
+
+ /*
+ * Read the output map configuration from the device tree.
+ * Each of the two PWM generators can drive zero or more outputs.
+ */
+
+ for (i = 0; i < LP3943_NUM_PWMS; i++) {
+ num_outputs = of_property_count_u32_elems(node, name[i]);
+ if (num_outputs <= 0)
+ continue;
+
+ output = devm_kcalloc(dev, num_outputs, sizeof(*output),
+ GFP_KERNEL);
+ if (!output)
+ return -ENOMEM;
+
+ err = of_property_read_u32_array(node, name[i], output,
+ num_outputs);
+ if (err)
+ return err;
+
+ pwm_map = devm_kzalloc(dev, sizeof(*pwm_map), GFP_KERNEL);
+ if (!pwm_map)
+ return -ENOMEM;
+
+ pwm_map->output = output;
+ pwm_map->num_outputs = num_outputs;
+ pdata->pwms[i] = pwm_map;
+
+ count++;
+ }
+
+ if (count == 0)
+ return -ENODATA;
+
+ lp3943_pwm->pdata = pdata;
+ return 0;
+}
+
+static int lp3943_pwm_probe(struct platform_device *pdev)
+{
+ struct lp3943 *lp3943 = dev_get_drvdata(pdev->dev.parent);
+ struct pwm_chip *chip;
+ struct lp3943_pwm *lp3943_pwm;
+ int ret;
+
+ chip = devm_pwmchip_alloc(&pdev->dev, LP3943_NUM_PWMS, sizeof(*lp3943_pwm));
+ if (IS_ERR(chip))
+ return PTR_ERR(chip);
+ lp3943_pwm = to_lp3943_pwm(chip);
+
+ lp3943_pwm->pdata = lp3943->pdata;
+ if (!lp3943_pwm->pdata) {
+ if (IS_ENABLED(CONFIG_OF))
+ ret = lp3943_pwm_parse_dt(&pdev->dev, lp3943_pwm);
+ else
+ ret = -ENODEV;
+
+ if (ret)
+ return ret;
+ }
+
+ lp3943_pwm->lp3943 = lp3943;
+ chip->ops = &lp3943_pwm_ops;
+
+ return devm_pwmchip_add(&pdev->dev, chip);
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id lp3943_pwm_of_match[] = {
+ { .compatible = "ti,lp3943-pwm" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, lp3943_pwm_of_match);
+#endif
+
+static struct platform_driver lp3943_pwm_driver = {
+ .probe = lp3943_pwm_probe,
+ .driver = {
+ .name = "lp3943-pwm",
+ .of_match_table = of_match_ptr(lp3943_pwm_of_match),
+ },
+};
+module_platform_driver(lp3943_pwm_driver);
+
+MODULE_DESCRIPTION("LP3943 PWM driver");
+MODULE_ALIAS("platform:lp3943-pwm");
+MODULE_AUTHOR("Milo Kim");
+MODULE_LICENSE("GPL");
diff --git a/drivers/pwm/pwm-lpc18xx-sct.c b/drivers/pwm/pwm-lpc18xx-sct.c
new file mode 100644
index 000000000..59e9b58a3
--- /dev/null
+++ b/drivers/pwm/pwm-lpc18xx-sct.c
@@ -0,0 +1,440 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * NXP LPC18xx State Configurable Timer - Pulse Width Modulator driver
+ *
+ * Copyright (c) 2015 Ariel D'Alessandro <ariel@vanguardiasur.com>
+ *
+ * Notes
+ * =====
+ * NXP LPC18xx provides a State Configurable Timer (SCT) which can be configured
+ * as a Pulse Width Modulator.
+ *
+ * SCT supports 16 outputs, 16 events and 16 registers. Each event will be
+ * triggered when its related register matches the SCT counter value, and it
+ * will set or clear a selected output.
+ *
+ * One of the events is preselected to generate the period, thus the maximum
+ * number of simultaneous channels is limited to 15. Notice that period is
+ * global to all the channels, thus PWM driver will refuse setting different
+ * values to it, unless there's only one channel requested.
+ */
+
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+
+/* LPC18xx SCT registers */
+#define LPC18XX_PWM_CONFIG 0x000
+#define LPC18XX_PWM_CONFIG_UNIFY BIT(0)
+#define LPC18XX_PWM_CONFIG_NORELOAD BIT(7)
+
+#define LPC18XX_PWM_CTRL 0x004
+#define LPC18XX_PWM_CTRL_HALT BIT(2)
+#define LPC18XX_PWM_BIDIR BIT(4)
+#define LPC18XX_PWM_PRE_SHIFT 5
+#define LPC18XX_PWM_PRE_MASK (0xff << LPC18XX_PWM_PRE_SHIFT)
+#define LPC18XX_PWM_PRE(x) (x << LPC18XX_PWM_PRE_SHIFT)
+
+#define LPC18XX_PWM_LIMIT 0x008
+
+#define LPC18XX_PWM_RES_BASE 0x058
+#define LPC18XX_PWM_RES_SHIFT(_ch) (_ch * 2)
+#define LPC18XX_PWM_RES(_ch, _action) (_action << LPC18XX_PWM_RES_SHIFT(_ch))
+#define LPC18XX_PWM_RES_MASK(_ch) (0x3 << LPC18XX_PWM_RES_SHIFT(_ch))
+
+#define LPC18XX_PWM_MATCH_BASE 0x100
+#define LPC18XX_PWM_MATCH(_ch) (LPC18XX_PWM_MATCH_BASE + _ch * 4)
+
+#define LPC18XX_PWM_MATCHREL_BASE 0x200
+#define LPC18XX_PWM_MATCHREL(_ch) (LPC18XX_PWM_MATCHREL_BASE + _ch * 4)
+
+#define LPC18XX_PWM_EVSTATEMSK_BASE 0x300
+#define LPC18XX_PWM_EVSTATEMSK(_ch) (LPC18XX_PWM_EVSTATEMSK_BASE + _ch * 8)
+#define LPC18XX_PWM_EVSTATEMSK_ALL 0xffffffff
+
+#define LPC18XX_PWM_EVCTRL_BASE 0x304
+#define LPC18XX_PWM_EVCTRL(_ev) (LPC18XX_PWM_EVCTRL_BASE + _ev * 8)
+
+#define LPC18XX_PWM_EVCTRL_MATCH(_ch) _ch
+
+#define LPC18XX_PWM_EVCTRL_COMB_SHIFT 12
+#define LPC18XX_PWM_EVCTRL_COMB_MATCH (0x1 << LPC18XX_PWM_EVCTRL_COMB_SHIFT)
+
+#define LPC18XX_PWM_OUTPUTSET_BASE 0x500
+#define LPC18XX_PWM_OUTPUTSET(_ch) (LPC18XX_PWM_OUTPUTSET_BASE + _ch * 8)
+
+#define LPC18XX_PWM_OUTPUTCL_BASE 0x504
+#define LPC18XX_PWM_OUTPUTCL(_ch) (LPC18XX_PWM_OUTPUTCL_BASE + _ch * 8)
+
+/* LPC18xx SCT unified counter */
+#define LPC18XX_PWM_TIMER_MAX 0xffffffff
+
+/* LPC18xx SCT events */
+#define LPC18XX_PWM_EVENT_PERIOD 0
+#define LPC18XX_PWM_EVENT_MAX 16
+
+#define LPC18XX_NUM_PWMS 16
+
+/* SCT conflict resolution */
+enum lpc18xx_pwm_res_action {
+ LPC18XX_PWM_RES_NONE,
+ LPC18XX_PWM_RES_SET,
+ LPC18XX_PWM_RES_CLEAR,
+ LPC18XX_PWM_RES_TOGGLE,
+};
+
+struct lpc18xx_pwm_data {
+ unsigned int duty_event;
+};
+
+struct lpc18xx_pwm_chip {
+ void __iomem *base;
+ struct clk *pwm_clk;
+ unsigned long clk_rate;
+ unsigned int period_ns;
+ unsigned int min_period_ns;
+ u64 max_period_ns;
+ unsigned int period_event;
+ unsigned long event_map;
+ struct lpc18xx_pwm_data channeldata[LPC18XX_NUM_PWMS];
+};
+
+static inline struct lpc18xx_pwm_chip *
+to_lpc18xx_pwm_chip(struct pwm_chip *chip)
+{
+ return pwmchip_get_drvdata(chip);
+}
+
+static inline void lpc18xx_pwm_writel(struct lpc18xx_pwm_chip *lpc18xx_pwm,
+ u32 reg, u32 val)
+{
+ writel(val, lpc18xx_pwm->base + reg);
+}
+
+static inline u32 lpc18xx_pwm_readl(struct lpc18xx_pwm_chip *lpc18xx_pwm,
+ u32 reg)
+{
+ return readl(lpc18xx_pwm->base + reg);
+}
+
+static void lpc18xx_pwm_set_conflict_res(struct lpc18xx_pwm_chip *lpc18xx_pwm,
+ struct pwm_device *pwm,
+ enum lpc18xx_pwm_res_action action)
+{
+ u32 val;
+
+ /*
+ * Simultaneous set and clear may happen on an output, that is the case
+ * when duty_ns == period_ns. LPC18xx SCT allows to set a conflict
+ * resolution action to be taken in such a case.
+ */
+ val = lpc18xx_pwm_readl(lpc18xx_pwm, LPC18XX_PWM_RES_BASE);
+ val &= ~LPC18XX_PWM_RES_MASK(pwm->hwpwm);
+ val |= LPC18XX_PWM_RES(pwm->hwpwm, action);
+ lpc18xx_pwm_writel(lpc18xx_pwm, LPC18XX_PWM_RES_BASE, val);
+}
+
+static void lpc18xx_pwm_config_period(struct pwm_chip *chip, u64 period_ns)
+{
+ struct lpc18xx_pwm_chip *lpc18xx_pwm = to_lpc18xx_pwm_chip(chip);
+ u32 val;
+
+ /*
+ * With clk_rate < NSEC_PER_SEC this cannot overflow.
+ * With period_ns < max_period_ns this also fits into an u32.
+ * As period_ns >= min_period_ns = DIV_ROUND_UP(NSEC_PER_SEC, lpc18xx_pwm->clk_rate);
+ * we have val >= 1.
+ */
+ val = mul_u64_u64_div_u64(period_ns, lpc18xx_pwm->clk_rate, NSEC_PER_SEC);
+
+ lpc18xx_pwm_writel(lpc18xx_pwm,
+ LPC18XX_PWM_MATCH(lpc18xx_pwm->period_event),
+ val - 1);
+
+ lpc18xx_pwm_writel(lpc18xx_pwm,
+ LPC18XX_PWM_MATCHREL(lpc18xx_pwm->period_event),
+ val - 1);
+}
+
+static void lpc18xx_pwm_config_duty(struct pwm_chip *chip,
+ struct pwm_device *pwm, u64 duty_ns)
+{
+ struct lpc18xx_pwm_chip *lpc18xx_pwm = to_lpc18xx_pwm_chip(chip);
+ struct lpc18xx_pwm_data *lpc18xx_data = &lpc18xx_pwm->channeldata[pwm->hwpwm];
+ u32 val;
+
+ /*
+ * With clk_rate <= NSEC_PER_SEC this cannot overflow.
+ * With duty_ns <= period_ns < max_period_ns this also fits into an u32.
+ */
+ val = mul_u64_u64_div_u64(duty_ns, lpc18xx_pwm->clk_rate, NSEC_PER_SEC);
+
+ lpc18xx_pwm_writel(lpc18xx_pwm,
+ LPC18XX_PWM_MATCH(lpc18xx_data->duty_event),
+ val);
+
+ lpc18xx_pwm_writel(lpc18xx_pwm,
+ LPC18XX_PWM_MATCHREL(lpc18xx_data->duty_event),
+ val);
+}
+
+static int lpc18xx_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
+ int duty_ns, int period_ns)
+{
+ struct lpc18xx_pwm_chip *lpc18xx_pwm = to_lpc18xx_pwm_chip(chip);
+ int requested_events;
+
+ if (period_ns < lpc18xx_pwm->min_period_ns ||
+ period_ns > lpc18xx_pwm->max_period_ns) {
+ dev_err(pwmchip_parent(chip), "period %d not in range\n", period_ns);
+ return -ERANGE;
+ }
+
+ requested_events = bitmap_weight(&lpc18xx_pwm->event_map,
+ LPC18XX_PWM_EVENT_MAX);
+
+ /*
+ * The PWM supports only a single period for all PWM channels.
+ * Once the period is set, it can only be changed if no more than one
+ * channel is requested at that moment.
+ */
+ if (requested_events > 2 && lpc18xx_pwm->period_ns != period_ns &&
+ lpc18xx_pwm->period_ns) {
+ dev_err(pwmchip_parent(chip), "conflicting period requested for PWM %u\n",
+ pwm->hwpwm);
+ return -EBUSY;
+ }
+
+ if ((requested_events <= 2 && lpc18xx_pwm->period_ns != period_ns) ||
+ !lpc18xx_pwm->period_ns) {
+ lpc18xx_pwm->period_ns = period_ns;
+ lpc18xx_pwm_config_period(chip, period_ns);
+ }
+
+ lpc18xx_pwm_config_duty(chip, pwm, duty_ns);
+
+ return 0;
+}
+
+static int lpc18xx_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm, enum pwm_polarity polarity)
+{
+ struct lpc18xx_pwm_chip *lpc18xx_pwm = to_lpc18xx_pwm_chip(chip);
+ struct lpc18xx_pwm_data *lpc18xx_data = &lpc18xx_pwm->channeldata[pwm->hwpwm];
+ enum lpc18xx_pwm_res_action res_action;
+ unsigned int set_event, clear_event;
+
+ lpc18xx_pwm_writel(lpc18xx_pwm,
+ LPC18XX_PWM_EVCTRL(lpc18xx_data->duty_event),
+ LPC18XX_PWM_EVCTRL_MATCH(lpc18xx_data->duty_event) |
+ LPC18XX_PWM_EVCTRL_COMB_MATCH);
+
+ lpc18xx_pwm_writel(lpc18xx_pwm,
+ LPC18XX_PWM_EVSTATEMSK(lpc18xx_data->duty_event),
+ LPC18XX_PWM_EVSTATEMSK_ALL);
+
+ if (polarity == PWM_POLARITY_NORMAL) {
+ set_event = lpc18xx_pwm->period_event;
+ clear_event = lpc18xx_data->duty_event;
+ res_action = LPC18XX_PWM_RES_SET;
+ } else {
+ set_event = lpc18xx_data->duty_event;
+ clear_event = lpc18xx_pwm->period_event;
+ res_action = LPC18XX_PWM_RES_CLEAR;
+ }
+
+ lpc18xx_pwm_writel(lpc18xx_pwm, LPC18XX_PWM_OUTPUTSET(pwm->hwpwm),
+ BIT(set_event));
+ lpc18xx_pwm_writel(lpc18xx_pwm, LPC18XX_PWM_OUTPUTCL(pwm->hwpwm),
+ BIT(clear_event));
+ lpc18xx_pwm_set_conflict_res(lpc18xx_pwm, pwm, res_action);
+
+ return 0;
+}
+
+static void lpc18xx_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct lpc18xx_pwm_chip *lpc18xx_pwm = to_lpc18xx_pwm_chip(chip);
+ struct lpc18xx_pwm_data *lpc18xx_data = &lpc18xx_pwm->channeldata[pwm->hwpwm];
+
+ lpc18xx_pwm_writel(lpc18xx_pwm,
+ LPC18XX_PWM_EVCTRL(lpc18xx_data->duty_event), 0);
+ lpc18xx_pwm_writel(lpc18xx_pwm, LPC18XX_PWM_OUTPUTSET(pwm->hwpwm), 0);
+ lpc18xx_pwm_writel(lpc18xx_pwm, LPC18XX_PWM_OUTPUTCL(pwm->hwpwm), 0);
+}
+
+static int lpc18xx_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct lpc18xx_pwm_chip *lpc18xx_pwm = to_lpc18xx_pwm_chip(chip);
+ struct lpc18xx_pwm_data *lpc18xx_data = &lpc18xx_pwm->channeldata[pwm->hwpwm];
+ unsigned long event;
+
+ event = find_first_zero_bit(&lpc18xx_pwm->event_map,
+ LPC18XX_PWM_EVENT_MAX);
+
+ if (event >= LPC18XX_PWM_EVENT_MAX) {
+ dev_err(pwmchip_parent(chip),
+ "maximum number of simultaneous channels reached\n");
+ return -EBUSY;
+ }
+
+ set_bit(event, &lpc18xx_pwm->event_map);
+ lpc18xx_data->duty_event = event;
+
+ return 0;
+}
+
+static void lpc18xx_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct lpc18xx_pwm_chip *lpc18xx_pwm = to_lpc18xx_pwm_chip(chip);
+ struct lpc18xx_pwm_data *lpc18xx_data = &lpc18xx_pwm->channeldata[pwm->hwpwm];
+
+ clear_bit(lpc18xx_data->duty_event, &lpc18xx_pwm->event_map);
+}
+
+static int lpc18xx_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
+ const struct pwm_state *state)
+{
+ int err;
+ bool enabled = pwm->state.enabled;
+
+ if (state->polarity != pwm->state.polarity && pwm->state.enabled) {
+ lpc18xx_pwm_disable(chip, pwm);
+ enabled = false;
+ }
+
+ if (!state->enabled) {
+ if (enabled)
+ lpc18xx_pwm_disable(chip, pwm);
+
+ return 0;
+ }
+
+ err = lpc18xx_pwm_config(chip, pwm, state->duty_cycle, state->period);
+ if (err)
+ return err;
+
+ if (!enabled)
+ err = lpc18xx_pwm_enable(chip, pwm, state->polarity);
+
+ return err;
+}
+static const struct pwm_ops lpc18xx_pwm_ops = {
+ .apply = lpc18xx_pwm_apply,
+ .request = lpc18xx_pwm_request,
+ .free = lpc18xx_pwm_free,
+};
+
+static const struct of_device_id lpc18xx_pwm_of_match[] = {
+ { .compatible = "nxp,lpc1850-sct-pwm" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, lpc18xx_pwm_of_match);
+
+static int lpc18xx_pwm_probe(struct platform_device *pdev)
+{
+ struct pwm_chip *chip;
+ struct lpc18xx_pwm_chip *lpc18xx_pwm;
+ int ret;
+ u64 val;
+
+ chip = devm_pwmchip_alloc(&pdev->dev, LPC18XX_NUM_PWMS, sizeof(*lpc18xx_pwm));
+ if (IS_ERR(chip))
+ return PTR_ERR(chip);
+ lpc18xx_pwm = to_lpc18xx_pwm_chip(chip);
+
+ lpc18xx_pwm->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(lpc18xx_pwm->base))
+ return PTR_ERR(lpc18xx_pwm->base);
+
+ lpc18xx_pwm->pwm_clk = devm_clk_get_enabled(&pdev->dev, "pwm");
+ if (IS_ERR(lpc18xx_pwm->pwm_clk))
+ return dev_err_probe(&pdev->dev, PTR_ERR(lpc18xx_pwm->pwm_clk),
+ "failed to get pwm clock\n");
+
+ lpc18xx_pwm->clk_rate = clk_get_rate(lpc18xx_pwm->pwm_clk);
+ if (!lpc18xx_pwm->clk_rate)
+ return dev_err_probe(&pdev->dev,
+ -EINVAL, "pwm clock has no frequency\n");
+
+ /*
+ * If clkrate is too fast, the calculations in .apply() might overflow.
+ */
+ if (lpc18xx_pwm->clk_rate > NSEC_PER_SEC)
+ return dev_err_probe(&pdev->dev, -EINVAL, "pwm clock to fast\n");
+
+ lpc18xx_pwm->max_period_ns =
+ mul_u64_u64_div_u64(NSEC_PER_SEC, LPC18XX_PWM_TIMER_MAX, lpc18xx_pwm->clk_rate);
+
+ lpc18xx_pwm->min_period_ns = DIV_ROUND_UP(NSEC_PER_SEC,
+ lpc18xx_pwm->clk_rate);
+
+ chip->ops = &lpc18xx_pwm_ops;
+
+ /* SCT counter must be in unify (32 bit) mode */
+ lpc18xx_pwm_writel(lpc18xx_pwm, LPC18XX_PWM_CONFIG,
+ LPC18XX_PWM_CONFIG_UNIFY);
+
+ /*
+ * Everytime the timer counter reaches the period value, the related
+ * event will be triggered and the counter reset to 0.
+ */
+ set_bit(LPC18XX_PWM_EVENT_PERIOD, &lpc18xx_pwm->event_map);
+ lpc18xx_pwm->period_event = LPC18XX_PWM_EVENT_PERIOD;
+
+ lpc18xx_pwm_writel(lpc18xx_pwm,
+ LPC18XX_PWM_EVSTATEMSK(lpc18xx_pwm->period_event),
+ LPC18XX_PWM_EVSTATEMSK_ALL);
+
+ val = LPC18XX_PWM_EVCTRL_MATCH(lpc18xx_pwm->period_event) |
+ LPC18XX_PWM_EVCTRL_COMB_MATCH;
+ lpc18xx_pwm_writel(lpc18xx_pwm,
+ LPC18XX_PWM_EVCTRL(lpc18xx_pwm->period_event), val);
+
+ lpc18xx_pwm_writel(lpc18xx_pwm, LPC18XX_PWM_LIMIT,
+ BIT(lpc18xx_pwm->period_event));
+
+ val = lpc18xx_pwm_readl(lpc18xx_pwm, LPC18XX_PWM_CTRL);
+ val &= ~LPC18XX_PWM_BIDIR;
+ val &= ~LPC18XX_PWM_CTRL_HALT;
+ val &= ~LPC18XX_PWM_PRE_MASK;
+ val |= LPC18XX_PWM_PRE(0);
+ lpc18xx_pwm_writel(lpc18xx_pwm, LPC18XX_PWM_CTRL, val);
+
+ ret = pwmchip_add(chip);
+ if (ret < 0)
+ return dev_err_probe(&pdev->dev, ret, "pwmchip_add failed\n");
+
+ platform_set_drvdata(pdev, chip);
+
+ return 0;
+}
+
+static void lpc18xx_pwm_remove(struct platform_device *pdev)
+{
+ struct pwm_chip *chip = platform_get_drvdata(pdev);
+ struct lpc18xx_pwm_chip *lpc18xx_pwm = to_lpc18xx_pwm_chip(chip);
+ u32 val;
+
+ pwmchip_remove(chip);
+
+ val = lpc18xx_pwm_readl(lpc18xx_pwm, LPC18XX_PWM_CTRL);
+ lpc18xx_pwm_writel(lpc18xx_pwm, LPC18XX_PWM_CTRL,
+ val | LPC18XX_PWM_CTRL_HALT);
+}
+
+static struct platform_driver lpc18xx_pwm_driver = {
+ .driver = {
+ .name = "lpc18xx-sct-pwm",
+ .of_match_table = lpc18xx_pwm_of_match,
+ },
+ .probe = lpc18xx_pwm_probe,
+ .remove = lpc18xx_pwm_remove,
+};
+module_platform_driver(lpc18xx_pwm_driver);
+
+MODULE_AUTHOR("Ariel D'Alessandro <ariel@vanguardiasur.com.ar>");
+MODULE_DESCRIPTION("NXP LPC18xx PWM driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/pwm/pwm-lpc32xx.c b/drivers/pwm/pwm-lpc32xx.c
new file mode 100644
index 000000000..19ec53648
--- /dev/null
+++ b/drivers/pwm/pwm-lpc32xx.c
@@ -0,0 +1,175 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright 2012 Alexandre Pereira da Silva <aletes.xgr@gmail.com>
+ */
+
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+#include <linux/slab.h>
+
+struct lpc32xx_pwm_chip {
+ struct clk *clk;
+ void __iomem *base;
+};
+
+#define PWM_ENABLE BIT(31)
+#define PWM_PIN_LEVEL BIT(30)
+
+static inline struct lpc32xx_pwm_chip *to_lpc32xx_pwm_chip(struct pwm_chip *chip)
+{
+ return pwmchip_get_drvdata(chip);
+}
+
+static int lpc32xx_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
+ int duty_ns, int period_ns)
+{
+ struct lpc32xx_pwm_chip *lpc32xx = to_lpc32xx_pwm_chip(chip);
+ unsigned long long c;
+ int period_cycles, duty_cycles;
+ u32 val;
+ c = clk_get_rate(lpc32xx->clk);
+
+ /* The highest acceptable divisor is 256, which is represented by 0 */
+ period_cycles = div64_u64(c * period_ns,
+ (unsigned long long)NSEC_PER_SEC * 256);
+ if (!period_cycles || period_cycles > 256)
+ return -ERANGE;
+ if (period_cycles == 256)
+ period_cycles = 0;
+
+ /* Compute 256 x #duty/period value and care for corner cases */
+ duty_cycles = div64_u64((unsigned long long)(period_ns - duty_ns) * 256,
+ period_ns);
+ if (!duty_cycles)
+ duty_cycles = 1;
+ if (duty_cycles > 255)
+ duty_cycles = 255;
+
+ val = readl(lpc32xx->base);
+ val &= ~0xFFFF;
+ val |= (period_cycles << 8) | duty_cycles;
+ writel(val, lpc32xx->base);
+
+ return 0;
+}
+
+static int lpc32xx_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct lpc32xx_pwm_chip *lpc32xx = to_lpc32xx_pwm_chip(chip);
+ u32 val;
+ int ret;
+
+ ret = clk_prepare_enable(lpc32xx->clk);
+ if (ret)
+ return ret;
+
+ val = readl(lpc32xx->base);
+ val |= PWM_ENABLE;
+ writel(val, lpc32xx->base);
+
+ return 0;
+}
+
+static void lpc32xx_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct lpc32xx_pwm_chip *lpc32xx = to_lpc32xx_pwm_chip(chip);
+ u32 val;
+
+ val = readl(lpc32xx->base);
+ val &= ~PWM_ENABLE;
+ writel(val, lpc32xx->base);
+
+ clk_disable_unprepare(lpc32xx->clk);
+}
+
+static int lpc32xx_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
+ const struct pwm_state *state)
+{
+ int err;
+
+ if (state->polarity != PWM_POLARITY_NORMAL)
+ return -EINVAL;
+
+ if (!state->enabled) {
+ if (pwm->state.enabled)
+ lpc32xx_pwm_disable(chip, pwm);
+
+ return 0;
+ }
+
+ err = lpc32xx_pwm_config(chip, pwm, state->duty_cycle, state->period);
+ if (err)
+ return err;
+
+ if (!pwm->state.enabled)
+ err = lpc32xx_pwm_enable(chip, pwm);
+
+ return err;
+}
+
+static const struct pwm_ops lpc32xx_pwm_ops = {
+ .apply = lpc32xx_pwm_apply,
+};
+
+static int lpc32xx_pwm_probe(struct platform_device *pdev)
+{
+ struct pwm_chip *chip;
+ struct lpc32xx_pwm_chip *lpc32xx;
+ int ret;
+ u32 val;
+
+ chip = devm_pwmchip_alloc(&pdev->dev, 1, sizeof(*lpc32xx));
+ if (IS_ERR(chip))
+ return PTR_ERR(chip);
+ lpc32xx = to_lpc32xx_pwm_chip(chip);
+
+ lpc32xx->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(lpc32xx->base))
+ return PTR_ERR(lpc32xx->base);
+
+ lpc32xx->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(lpc32xx->clk))
+ return PTR_ERR(lpc32xx->clk);
+
+ chip->ops = &lpc32xx_pwm_ops;
+
+ /* If PWM is disabled, configure the output to the default value */
+ val = readl(lpc32xx->base);
+ val &= ~PWM_PIN_LEVEL;
+ writel(val, lpc32xx->base);
+
+ ret = devm_pwmchip_add(&pdev->dev, chip);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to add PWM chip, error %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct of_device_id lpc32xx_pwm_dt_ids[] = {
+ { .compatible = "nxp,lpc3220-pwm" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, lpc32xx_pwm_dt_ids);
+
+static struct platform_driver lpc32xx_pwm_driver = {
+ .driver = {
+ .name = "lpc32xx-pwm",
+ .of_match_table = lpc32xx_pwm_dt_ids,
+ },
+ .probe = lpc32xx_pwm_probe,
+};
+module_platform_driver(lpc32xx_pwm_driver);
+
+MODULE_ALIAS("platform:lpc32xx-pwm");
+MODULE_AUTHOR("Alexandre Pereira da Silva <aletes.xgr@gmail.com>");
+MODULE_DESCRIPTION("LPC32XX PWM Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/pwm/pwm-lpss-pci.c b/drivers/pwm/pwm-lpss-pci.c
new file mode 100644
index 000000000..bcbfda1a6
--- /dev/null
+++ b/drivers/pwm/pwm-lpss-pci.c
@@ -0,0 +1,74 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Intel Low Power Subsystem PWM controller PCI driver
+ *
+ * Copyright (C) 2014, Intel Corporation
+ *
+ * Derived from the original pwm-lpss.c
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/pm_runtime.h>
+
+#include "pwm-lpss.h"
+
+static int pwm_lpss_probe_pci(struct pci_dev *pdev,
+ const struct pci_device_id *id)
+{
+ const struct pwm_lpss_boardinfo *info;
+ void __iomem *io_base;
+ struct pwm_chip *chip;
+ int err;
+
+ err = pcim_enable_device(pdev);
+ if (err < 0)
+ return err;
+
+ io_base = pcim_iomap_region(pdev, 0, "pwm-lpss");
+ if (IS_ERR(io_base))
+ return PTR_ERR(io_base);
+
+ info = (struct pwm_lpss_boardinfo *)id->driver_data;
+ chip = devm_pwm_lpss_probe(&pdev->dev, io_base, info);
+ if (IS_ERR(chip))
+ return PTR_ERR(chip);
+
+ pm_runtime_put(&pdev->dev);
+ pm_runtime_allow(&pdev->dev);
+
+ return 0;
+}
+
+static void pwm_lpss_remove_pci(struct pci_dev *pdev)
+{
+ pm_runtime_forbid(&pdev->dev);
+ pm_runtime_get_sync(&pdev->dev);
+}
+
+static const struct pci_device_id pwm_lpss_pci_ids[] = {
+ { PCI_VDEVICE(INTEL, 0x0ac8), .driver_data = (unsigned long)&pwm_lpss_bxt_info },
+ { PCI_VDEVICE(INTEL, 0x0f08), .driver_data = (unsigned long)&pwm_lpss_byt_info },
+ { PCI_VDEVICE(INTEL, 0x0f09), .driver_data = (unsigned long)&pwm_lpss_byt_info },
+ { PCI_VDEVICE(INTEL, 0x11a5), .driver_data = (unsigned long)&pwm_lpss_tng_info },
+ { PCI_VDEVICE(INTEL, 0x1ac8), .driver_data = (unsigned long)&pwm_lpss_bxt_info },
+ { PCI_VDEVICE(INTEL, 0x2288), .driver_data = (unsigned long)&pwm_lpss_bsw_info },
+ { PCI_VDEVICE(INTEL, 0x2289), .driver_data = (unsigned long)&pwm_lpss_bsw_info },
+ { PCI_VDEVICE(INTEL, 0x31c8), .driver_data = (unsigned long)&pwm_lpss_bxt_info },
+ { PCI_VDEVICE(INTEL, 0x5ac8), .driver_data = (unsigned long)&pwm_lpss_bxt_info },
+ { }
+};
+MODULE_DEVICE_TABLE(pci, pwm_lpss_pci_ids);
+
+static struct pci_driver pwm_lpss_driver_pci = {
+ .name = "pwm-lpss",
+ .id_table = pwm_lpss_pci_ids,
+ .probe = pwm_lpss_probe_pci,
+ .remove = pwm_lpss_remove_pci,
+};
+module_pci_driver(pwm_lpss_driver_pci);
+
+MODULE_DESCRIPTION("PWM PCI driver for Intel LPSS");
+MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS("PWM_LPSS");
diff --git a/drivers/pwm/pwm-lpss-platform.c b/drivers/pwm/pwm-lpss-platform.c
new file mode 100644
index 000000000..489ca0c09
--- /dev/null
+++ b/drivers/pwm/pwm-lpss-platform.c
@@ -0,0 +1,81 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Intel Low Power Subsystem PWM controller driver
+ *
+ * Copyright (C) 2014, Intel Corporation
+ *
+ * Derived from the original pwm-lpss.c
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/property.h>
+
+#include "pwm-lpss.h"
+
+
+static int pwm_lpss_probe_platform(struct platform_device *pdev)
+{
+ const struct pwm_lpss_boardinfo *info;
+ struct pwm_chip *chip;
+ void __iomem *base;
+
+ info = device_get_match_data(&pdev->dev);
+ if (!info)
+ return -ENODEV;
+
+ base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ chip = devm_pwm_lpss_probe(&pdev->dev, base, info);
+ if (IS_ERR(chip))
+ return PTR_ERR(chip);
+
+ /*
+ * On Cherry Trail devices the GFX0._PS0 AML checks if the controller
+ * is on and if it is not on it turns it on and restores what it
+ * believes is the correct state to the PWM controller.
+ * Because of this we must disallow direct-complete, which keeps the
+ * controller (runtime)suspended on resume, to avoid 2 issues:
+ * 1. The controller getting turned on without the linux-pm code
+ * knowing about this. On devices where the controller is unused
+ * this causes it to stay on during the next suspend causing high
+ * battery drain (because S0i3 is not reached)
+ * 2. The state restoring code unexpectedly messing with the controller
+ *
+ * Leaving the controller runtime-suspended (skipping runtime-resume +
+ * normal-suspend) during suspend is fine.
+ */
+ if (info->other_devices_aml_touches_pwm_regs)
+ dev_pm_set_driver_flags(&pdev->dev, DPM_FLAG_NO_DIRECT_COMPLETE|
+ DPM_FLAG_SMART_SUSPEND);
+
+ pm_runtime_set_active(&pdev->dev);
+ return devm_pm_runtime_enable(&pdev->dev);
+}
+
+static const struct acpi_device_id pwm_lpss_acpi_match[] = {
+ { .id = "80860F09", .driver_data = (unsigned long)&pwm_lpss_byt_info },
+ { .id = "80862288", .driver_data = (unsigned long)&pwm_lpss_bsw_info },
+ { .id = "80862289", .driver_data = (unsigned long)&pwm_lpss_bsw_info },
+ { .id = "80865AC8", .driver_data = (unsigned long)&pwm_lpss_bxt_info },
+ { }
+};
+MODULE_DEVICE_TABLE(acpi, pwm_lpss_acpi_match);
+
+static struct platform_driver pwm_lpss_driver_platform = {
+ .driver = {
+ .name = "pwm-lpss",
+ .acpi_match_table = pwm_lpss_acpi_match,
+ },
+ .probe = pwm_lpss_probe_platform,
+};
+module_platform_driver(pwm_lpss_driver_platform);
+
+MODULE_DESCRIPTION("PWM platform driver for Intel LPSS");
+MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS("PWM_LPSS");
+MODULE_ALIAS("platform:pwm-lpss");
diff --git a/drivers/pwm/pwm-lpss.c b/drivers/pwm/pwm-lpss.c
new file mode 100644
index 000000000..c976ff1c8
--- /dev/null
+++ b/drivers/pwm/pwm-lpss.c
@@ -0,0 +1,293 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Intel Low Power Subsystem PWM controller driver
+ *
+ * Copyright (C) 2014, Intel Corporation
+ * Author: Mika Westerberg <mika.westerberg@linux.intel.com>
+ * Author: Chew Kean Ho <kean.ho.chew@intel.com>
+ * Author: Chang Rebecca Swee Fun <rebecca.swee.fun.chang@intel.com>
+ * Author: Chew Chiau Ee <chiau.ee.chew@intel.com>
+ * Author: Alan Cox <alan@linux.intel.com>
+ */
+
+#define DEFAULT_SYMBOL_NAMESPACE "PWM_LPSS"
+
+#include <linux/bits.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/pwm.h>
+#include <linux/time.h>
+
+#include "pwm-lpss.h"
+
+#define PWM 0x00000000
+#define PWM_ENABLE BIT(31)
+#define PWM_SW_UPDATE BIT(30)
+#define PWM_BASE_UNIT_SHIFT 8
+#define PWM_ON_TIME_DIV_MASK GENMASK(7, 0)
+
+/* Size of each PWM register space if multiple */
+#define PWM_SIZE 0x400
+
+/* BayTrail */
+const struct pwm_lpss_boardinfo pwm_lpss_byt_info = {
+ .clk_rate = 25000000,
+ .npwm = 1,
+ .base_unit_bits = 16,
+};
+EXPORT_SYMBOL_GPL(pwm_lpss_byt_info);
+
+/* Braswell */
+const struct pwm_lpss_boardinfo pwm_lpss_bsw_info = {
+ .clk_rate = 19200000,
+ .npwm = 1,
+ .base_unit_bits = 16,
+ .other_devices_aml_touches_pwm_regs = true,
+};
+EXPORT_SYMBOL_GPL(pwm_lpss_bsw_info);
+
+/* Broxton */
+const struct pwm_lpss_boardinfo pwm_lpss_bxt_info = {
+ .clk_rate = 19200000,
+ .npwm = 4,
+ .base_unit_bits = 22,
+ .bypass = true,
+};
+EXPORT_SYMBOL_GPL(pwm_lpss_bxt_info);
+
+/* Tangier */
+const struct pwm_lpss_boardinfo pwm_lpss_tng_info = {
+ .clk_rate = 19200000,
+ .npwm = 4,
+ .base_unit_bits = 22,
+};
+EXPORT_SYMBOL_GPL(pwm_lpss_tng_info);
+
+static inline struct pwm_lpss_chip *to_lpwm(struct pwm_chip *chip)
+{
+ return pwmchip_get_drvdata(chip);
+}
+
+static inline u32 pwm_lpss_read(const struct pwm_device *pwm)
+{
+ struct pwm_lpss_chip *lpwm = to_lpwm(pwm->chip);
+
+ return readl(lpwm->regs + pwm->hwpwm * PWM_SIZE + PWM);
+}
+
+static inline void pwm_lpss_write(const struct pwm_device *pwm, u32 value)
+{
+ struct pwm_lpss_chip *lpwm = to_lpwm(pwm->chip);
+
+ writel(value, lpwm->regs + pwm->hwpwm * PWM_SIZE + PWM);
+}
+
+static int pwm_lpss_wait_for_update(struct pwm_device *pwm)
+{
+ struct pwm_lpss_chip *lpwm = to_lpwm(pwm->chip);
+ const void __iomem *addr = lpwm->regs + pwm->hwpwm * PWM_SIZE + PWM;
+ const unsigned int ms = 500 * USEC_PER_MSEC;
+ u32 val;
+ int err;
+
+ /*
+ * PWM Configuration register has SW_UPDATE bit that is set when a new
+ * configuration is written to the register. The bit is automatically
+ * cleared at the start of the next output cycle by the IP block.
+ *
+ * If one writes a new configuration to the register while it still has
+ * the bit enabled, PWM may freeze. That is, while one can still write
+ * to the register, it won't have an effect. Thus, we try to sleep long
+ * enough that the bit gets cleared and make sure the bit is not
+ * enabled while we update the configuration.
+ */
+ err = readl_poll_timeout(addr, val, !(val & PWM_SW_UPDATE), 40, ms);
+ if (err)
+ dev_err(pwmchip_parent(pwm->chip), "PWM_SW_UPDATE was not cleared\n");
+
+ return err;
+}
+
+static inline int pwm_lpss_is_updating(struct pwm_device *pwm)
+{
+ if (pwm_lpss_read(pwm) & PWM_SW_UPDATE) {
+ dev_err(pwmchip_parent(pwm->chip), "PWM_SW_UPDATE is still set, skipping update\n");
+ return -EBUSY;
+ }
+
+ return 0;
+}
+
+static void pwm_lpss_prepare(struct pwm_lpss_chip *lpwm, struct pwm_device *pwm,
+ int duty_ns, int period_ns)
+{
+ unsigned long long on_time_div;
+ unsigned long c = lpwm->info->clk_rate, base_unit_range;
+ unsigned long long base_unit, freq = NSEC_PER_SEC;
+ u32 ctrl;
+
+ do_div(freq, period_ns);
+
+ /*
+ * The equation is:
+ * base_unit = round(base_unit_range * freq / c)
+ */
+ base_unit_range = BIT(lpwm->info->base_unit_bits);
+ freq *= base_unit_range;
+
+ base_unit = DIV_ROUND_CLOSEST_ULL(freq, c);
+ /* base_unit must not be 0 and we also want to avoid overflowing it */
+ base_unit = clamp_val(base_unit, 1, base_unit_range - 1);
+
+ on_time_div = 255ULL * duty_ns;
+ do_div(on_time_div, period_ns);
+ on_time_div = 255ULL - on_time_div;
+
+ ctrl = pwm_lpss_read(pwm);
+ ctrl &= ~PWM_ON_TIME_DIV_MASK;
+ ctrl &= ~((base_unit_range - 1) << PWM_BASE_UNIT_SHIFT);
+ ctrl |= (u32) base_unit << PWM_BASE_UNIT_SHIFT;
+ ctrl |= on_time_div;
+
+ pwm_lpss_write(pwm, ctrl);
+ pwm_lpss_write(pwm, ctrl | PWM_SW_UPDATE);
+}
+
+static inline void pwm_lpss_cond_enable(struct pwm_device *pwm, bool cond)
+{
+ if (cond)
+ pwm_lpss_write(pwm, pwm_lpss_read(pwm) | PWM_ENABLE);
+}
+
+static int pwm_lpss_prepare_enable(struct pwm_lpss_chip *lpwm,
+ struct pwm_device *pwm,
+ const struct pwm_state *state)
+{
+ int ret;
+
+ ret = pwm_lpss_is_updating(pwm);
+ if (ret)
+ return ret;
+
+ pwm_lpss_prepare(lpwm, pwm, state->duty_cycle, state->period);
+ pwm_lpss_cond_enable(pwm, lpwm->info->bypass == false);
+ ret = pwm_lpss_wait_for_update(pwm);
+ if (ret)
+ return ret;
+
+ pwm_lpss_cond_enable(pwm, lpwm->info->bypass == true);
+ return 0;
+}
+
+static int pwm_lpss_apply(struct pwm_chip *chip, struct pwm_device *pwm,
+ const struct pwm_state *state)
+{
+ struct pwm_lpss_chip *lpwm = to_lpwm(chip);
+ int ret = 0;
+
+ if (state->enabled) {
+ if (!pwm_is_enabled(pwm)) {
+ pm_runtime_get_sync(pwmchip_parent(chip));
+ ret = pwm_lpss_prepare_enable(lpwm, pwm, state);
+ if (ret)
+ pm_runtime_put(pwmchip_parent(chip));
+ } else {
+ ret = pwm_lpss_prepare_enable(lpwm, pwm, state);
+ }
+ } else if (pwm_is_enabled(pwm)) {
+ pwm_lpss_write(pwm, pwm_lpss_read(pwm) & ~PWM_ENABLE);
+ pm_runtime_put(pwmchip_parent(chip));
+ }
+
+ return ret;
+}
+
+static int pwm_lpss_get_state(struct pwm_chip *chip, struct pwm_device *pwm,
+ struct pwm_state *state)
+{
+ struct pwm_lpss_chip *lpwm = to_lpwm(chip);
+ unsigned long base_unit_range;
+ unsigned long long base_unit, freq, on_time_div;
+ u32 ctrl;
+
+ pm_runtime_get_sync(pwmchip_parent(chip));
+
+ base_unit_range = BIT(lpwm->info->base_unit_bits);
+
+ ctrl = pwm_lpss_read(pwm);
+ on_time_div = 255 - (ctrl & PWM_ON_TIME_DIV_MASK);
+ base_unit = (ctrl >> PWM_BASE_UNIT_SHIFT) & (base_unit_range - 1);
+
+ freq = base_unit * lpwm->info->clk_rate;
+ do_div(freq, base_unit_range);
+ if (freq == 0)
+ state->period = NSEC_PER_SEC;
+ else
+ state->period = NSEC_PER_SEC / (unsigned long)freq;
+
+ on_time_div *= state->period;
+ do_div(on_time_div, 255);
+ state->duty_cycle = on_time_div;
+
+ state->polarity = PWM_POLARITY_NORMAL;
+ state->enabled = !!(ctrl & PWM_ENABLE);
+
+ pm_runtime_put(pwmchip_parent(chip));
+
+ return 0;
+}
+
+static const struct pwm_ops pwm_lpss_ops = {
+ .apply = pwm_lpss_apply,
+ .get_state = pwm_lpss_get_state,
+};
+
+struct pwm_chip *devm_pwm_lpss_probe(struct device *dev, void __iomem *base,
+ const struct pwm_lpss_boardinfo *info)
+{
+ struct pwm_lpss_chip *lpwm;
+ struct pwm_chip *chip;
+ unsigned long c;
+ int i, ret;
+ u32 ctrl;
+
+ if (WARN_ON(info->npwm > LPSS_MAX_PWMS))
+ return ERR_PTR(-ENODEV);
+
+ chip = devm_pwmchip_alloc(dev, info->npwm, sizeof(*lpwm));
+ if (IS_ERR(chip))
+ return chip;
+ lpwm = to_lpwm(chip);
+
+ lpwm->regs = base;
+ lpwm->info = info;
+
+ c = lpwm->info->clk_rate;
+ if (!c)
+ return ERR_PTR(-EINVAL);
+
+ chip->ops = &pwm_lpss_ops;
+
+ ret = devm_pwmchip_add(dev, chip);
+ if (ret) {
+ dev_err(dev, "failed to add PWM chip: %d\n", ret);
+ return ERR_PTR(ret);
+ }
+
+ for (i = 0; i < lpwm->info->npwm; i++) {
+ ctrl = pwm_lpss_read(&chip->pwms[i]);
+ if (ctrl & PWM_ENABLE)
+ pm_runtime_get(dev);
+ }
+
+ return chip;
+}
+EXPORT_SYMBOL_GPL(devm_pwm_lpss_probe);
+
+MODULE_DESCRIPTION("PWM driver for Intel LPSS");
+MODULE_AUTHOR("Mika Westerberg <mika.westerberg@linux.intel.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/pwm/pwm-lpss.h b/drivers/pwm/pwm-lpss.h
new file mode 100644
index 000000000..607921814
--- /dev/null
+++ b/drivers/pwm/pwm-lpss.h
@@ -0,0 +1,29 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Intel Low Power Subsystem PWM controller driver
+ *
+ * Copyright (C) 2014, Intel Corporation
+ *
+ * Derived from the original pwm-lpss.c
+ */
+
+#ifndef __PWM_LPSS_H
+#define __PWM_LPSS_H
+
+#include <linux/types.h>
+
+#include <linux/platform_data/x86/pwm-lpss.h>
+
+#define LPSS_MAX_PWMS 4
+
+struct pwm_lpss_chip {
+ void __iomem *regs;
+ const struct pwm_lpss_boardinfo *info;
+};
+
+extern const struct pwm_lpss_boardinfo pwm_lpss_byt_info;
+extern const struct pwm_lpss_boardinfo pwm_lpss_bsw_info;
+extern const struct pwm_lpss_boardinfo pwm_lpss_bxt_info;
+extern const struct pwm_lpss_boardinfo pwm_lpss_tng_info;
+
+#endif /* __PWM_LPSS_H */
diff --git a/drivers/pwm/pwm-max7360.c b/drivers/pwm/pwm-max7360.c
new file mode 100644
index 000000000..f920877d1
--- /dev/null
+++ b/drivers/pwm/pwm-max7360.c
@@ -0,0 +1,209 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright 2025 Bootlin
+ *
+ * Author: Kamel BOUHARA <kamel.bouhara@bootlin.com>
+ * Author: Mathieu Dubois-Briand <mathieu.dubois-briand@bootlin.com>
+ *
+ * PWM functionality of the MAX7360 multi-function device.
+ * https://www.analog.com/media/en/technical-documentation/data-sheets/MAX7360.pdf
+ *
+ * Limitations:
+ * - Only supports normal polarity.
+ * - The period is fixed to 2 ms.
+ * - Only the duty cycle can be changed, new values are applied at the beginning
+ * of the next cycle.
+ * - When disabled, the output is put in Hi-Z immediately.
+ */
+#include <linux/bits.h>
+#include <linux/dev_printk.h>
+#include <linux/err.h>
+#include <linux/math64.h>
+#include <linux/mfd/max7360.h>
+#include <linux/minmax.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+#include <linux/regmap.h>
+#include <linux/time.h>
+#include <linux/types.h>
+
+#define MAX7360_NUM_PWMS 8
+#define MAX7360_PWM_MAX 255
+#define MAX7360_PWM_STEPS 256
+#define MAX7360_PWM_PERIOD_NS (2 * NSEC_PER_MSEC)
+
+struct max7360_pwm_waveform {
+ u8 duty_steps;
+ bool enabled;
+};
+
+static int max7360_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct regmap *regmap = pwmchip_get_drvdata(chip);
+
+ /*
+ * Make sure we use the individual PWM configuration register and not
+ * the global one.
+ * We never need to use the global one, so there is no need to revert
+ * that in the .free() callback.
+ */
+ return regmap_write_bits(regmap, MAX7360_REG_PWMCFG(pwm->hwpwm),
+ MAX7360_PORT_CFG_COMMON_PWM, 0);
+}
+
+static int max7360_pwm_round_waveform_tohw(struct pwm_chip *chip,
+ struct pwm_device *pwm,
+ const struct pwm_waveform *wf,
+ void *_wfhw)
+{
+ struct max7360_pwm_waveform *wfhw = _wfhw;
+ u64 duty_steps;
+
+ /*
+ * Ignore user provided values for period_length_ns and duty_offset_ns:
+ * we only support fixed period of MAX7360_PWM_PERIOD_NS and offset of 0.
+ * Values from 0 to 254 as duty_steps will provide duty cycles of 0/256
+ * to 254/256, while value 255 will provide a duty cycle of 100%.
+ */
+ if (wf->duty_length_ns >= MAX7360_PWM_PERIOD_NS) {
+ duty_steps = MAX7360_PWM_MAX;
+ } else {
+ duty_steps = (u32)wf->duty_length_ns * MAX7360_PWM_STEPS / MAX7360_PWM_PERIOD_NS;
+ if (duty_steps == MAX7360_PWM_MAX)
+ duty_steps = MAX7360_PWM_MAX - 1;
+ }
+
+ wfhw->duty_steps = duty_steps;
+ wfhw->enabled = !!wf->period_length_ns;
+
+ if (wf->period_length_ns && wf->period_length_ns < MAX7360_PWM_PERIOD_NS)
+ return 1;
+ else
+ return 0;
+}
+
+static int max7360_pwm_round_waveform_fromhw(struct pwm_chip *chip, struct pwm_device *pwm,
+ const void *_wfhw, struct pwm_waveform *wf)
+{
+ const struct max7360_pwm_waveform *wfhw = _wfhw;
+
+ wf->period_length_ns = wfhw->enabled ? MAX7360_PWM_PERIOD_NS : 0;
+ wf->duty_offset_ns = 0;
+
+ if (wfhw->enabled) {
+ if (wfhw->duty_steps == MAX7360_PWM_MAX)
+ wf->duty_length_ns = MAX7360_PWM_PERIOD_NS;
+ else
+ wf->duty_length_ns = DIV_ROUND_UP(wfhw->duty_steps * MAX7360_PWM_PERIOD_NS,
+ MAX7360_PWM_STEPS);
+ } else {
+ wf->duty_length_ns = 0;
+ }
+
+ return 0;
+}
+
+static int max7360_pwm_write_waveform(struct pwm_chip *chip,
+ struct pwm_device *pwm,
+ const void *_wfhw)
+{
+ struct regmap *regmap = pwmchip_get_drvdata(chip);
+ const struct max7360_pwm_waveform *wfhw = _wfhw;
+ unsigned int val;
+ int ret;
+
+ if (wfhw->enabled) {
+ ret = regmap_write(regmap, MAX7360_REG_PWM(pwm->hwpwm), wfhw->duty_steps);
+ if (ret)
+ return ret;
+ }
+
+ val = wfhw->enabled ? BIT(pwm->hwpwm) : 0;
+ return regmap_write_bits(regmap, MAX7360_REG_GPIOCTRL, BIT(pwm->hwpwm), val);
+}
+
+static int max7360_pwm_read_waveform(struct pwm_chip *chip,
+ struct pwm_device *pwm,
+ void *_wfhw)
+{
+ struct regmap *regmap = pwmchip_get_drvdata(chip);
+ struct max7360_pwm_waveform *wfhw = _wfhw;
+ unsigned int val;
+ int ret;
+
+ ret = regmap_read(regmap, MAX7360_REG_GPIOCTRL, &val);
+ if (ret)
+ return ret;
+
+ if (val & BIT(pwm->hwpwm)) {
+ wfhw->enabled = true;
+ ret = regmap_read(regmap, MAX7360_REG_PWM(pwm->hwpwm), &val);
+ if (ret)
+ return ret;
+
+ wfhw->duty_steps = val;
+ } else {
+ wfhw->enabled = false;
+ wfhw->duty_steps = 0;
+ }
+
+ return 0;
+}
+
+static const struct pwm_ops max7360_pwm_ops = {
+ .sizeof_wfhw = sizeof(struct max7360_pwm_waveform),
+ .request = max7360_pwm_request,
+ .round_waveform_tohw = max7360_pwm_round_waveform_tohw,
+ .round_waveform_fromhw = max7360_pwm_round_waveform_fromhw,
+ .read_waveform = max7360_pwm_read_waveform,
+ .write_waveform = max7360_pwm_write_waveform,
+};
+
+static int max7360_pwm_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct pwm_chip *chip;
+ struct regmap *regmap;
+ int ret;
+
+ regmap = dev_get_regmap(dev->parent, NULL);
+ if (!regmap)
+ return dev_err_probe(dev, -ENODEV, "Could not get parent regmap\n");
+
+ /*
+ * This MFD sub-device does not have any associated device tree node:
+ * properties are stored in the device node of the parent (MFD) device
+ * and this same node is used in phandles of client devices.
+ * Reuse this device tree node here, as otherwise the PWM subsystem
+ * would be confused by this topology.
+ */
+ device_set_of_node_from_dev(dev, dev->parent);
+
+ chip = devm_pwmchip_alloc(dev, MAX7360_NUM_PWMS, 0);
+ if (IS_ERR(chip))
+ return PTR_ERR(chip);
+ chip->ops = &max7360_pwm_ops;
+
+ pwmchip_set_drvdata(chip, regmap);
+
+ ret = devm_pwmchip_add(dev, chip);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to add PWM chip\n");
+
+ return 0;
+}
+
+static struct platform_driver max7360_pwm_driver = {
+ .driver = {
+ .name = "max7360-pwm",
+ .probe_type = PROBE_PREFER_ASYNCHRONOUS,
+ },
+ .probe = max7360_pwm_probe,
+};
+module_platform_driver(max7360_pwm_driver);
+
+MODULE_DESCRIPTION("MAX7360 PWM driver");
+MODULE_AUTHOR("Kamel BOUHARA <kamel.bouhara@bootlin.com>");
+MODULE_AUTHOR("Mathieu Dubois-Briand <mathieu.dubois-briand@bootlin.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/pwm/pwm-mc33xs2410.c b/drivers/pwm/pwm-mc33xs2410.c
new file mode 100644
index 000000000..9f89b60dd
--- /dev/null
+++ b/drivers/pwm/pwm-mc33xs2410.c
@@ -0,0 +1,407 @@
+// 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.
+ */
+#define DEFAULT_SYMBOL_NAMESPACE "PWM_MC33XS2410"
+
+#include <linux/auxiliary_bus.h>
+#include <linux/bitfield.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/math64.h>
+#include <linux/mc33xs2410.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, &reg, &val, 1);
+}
+
+static int mc33xs2410_read_reg(struct spi_device *spi, u8 reg, u16 *val, u8 flag)
+{
+ return mc33xs2410_read_regs(spi, &reg, flag, val, 1);
+}
+
+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);
+}
+EXPORT_SYMBOL_GPL(mc33xs2410_read_reg_ctrl);
+
+int mc33xs2410_read_reg_diag(struct spi_device *spi, u8 reg, u16 *val)
+{
+ return mc33xs2410_read_reg(spi, reg, val, 0);
+}
+EXPORT_SYMBOL_GPL(mc33xs2410_read_reg_diag);
+
+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);
+}
+EXPORT_SYMBOL_GPL(mc33xs2410_modify_reg);
+
+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, &reg[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 auxiliary_device *adev;
+ 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");
+
+ adev = devm_auxiliary_device_create(dev, "hwmon", NULL);
+ if (!adev)
+ return dev_err_probe(dev, -ENODEV, "Failed to register hwmon device\n");
+
+ return 0;
+}
+
+static const struct spi_device_id mc33xs2410_spi_id[] = {
+ { .name = "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-mediatek.c b/drivers/pwm/pwm-mediatek.c
new file mode 100644
index 000000000..0bb49be65
--- /dev/null
+++ b/drivers/pwm/pwm-mediatek.c
@@ -0,0 +1,645 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * MediaTek Pulse Width Modulator driver
+ *
+ * Copyright (C) 2015 John Crispin <blogic@openwrt.org>
+ * Copyright (C) 2017 Zhi Mao <zhi.mao@mediatek.com>
+ *
+ */
+
+#include <linux/bitfield.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/clk.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+
+/* PWM registers and bits definitions */
+#define PWMCON 0x00
+#define PWMCON_CLKDIV GENMASK(2, 0)
+#define PWMCON_CLKSEL BIT(3)
+#define PWMCON_OLD_PWM_MODE BIT(15)
+#define PWMHDUR 0x04
+#define PWMLDUR 0x08
+#define PWMGDUR 0x0c
+#define PWMWAVENUM 0x28
+#define PWMDWIDTH 0x2c
+#define PWMDWIDTH_PERIOD GENMASK(12, 0)
+#define PWM45DWIDTH_FIXUP 0x30
+#define PWMTHRES 0x30
+#define PWMTHRES_DUTY GENMASK(12, 0)
+#define PWM45THRES_FIXUP 0x34
+#define PWM_CK_26M_SEL_V3 0x74
+#define PWM_CK_26M_SEL 0x210
+
+struct pwm_mediatek_of_data {
+ unsigned int num_pwms;
+ bool clksel_fixup;
+ bool pwm45_fixup;
+ u16 pwm_ck_26m_sel_reg;
+ unsigned int chanreg_base;
+ unsigned int chanreg_width;
+};
+
+/**
+ * struct pwm_mediatek_chip - struct representing PWM chip
+ * @regs: base address of PWM chip
+ * @clk_top: the top clock generator
+ * @clk_main: the clock used by PWM core
+ * @soc: pointer to chip's platform data
+ * @clk_pwms: the clock and clkrate used by each PWM channel
+ */
+struct pwm_mediatek_chip {
+ void __iomem *regs;
+ struct clk *clk_top;
+ struct clk *clk_main;
+ const struct pwm_mediatek_of_data *soc;
+ struct {
+ struct clk *clk;
+ unsigned long rate;
+ } clk_pwms[];
+};
+
+static inline struct pwm_mediatek_chip *
+to_pwm_mediatek_chip(struct pwm_chip *chip)
+{
+ return pwmchip_get_drvdata(chip);
+}
+
+static int pwm_mediatek_clk_enable(struct pwm_mediatek_chip *pc,
+ unsigned int hwpwm)
+{
+ int ret;
+
+ ret = clk_prepare_enable(pc->clk_top);
+ if (ret < 0)
+ return ret;
+
+ ret = clk_prepare_enable(pc->clk_main);
+ if (ret < 0)
+ goto disable_clk_top;
+
+ ret = clk_prepare_enable(pc->clk_pwms[hwpwm].clk);
+ if (ret < 0)
+ goto disable_clk_main;
+
+ if (!pc->clk_pwms[hwpwm].rate) {
+ pc->clk_pwms[hwpwm].rate = clk_get_rate(pc->clk_pwms[hwpwm].clk);
+
+ /*
+ * With the clk running with not more than 1 GHz the
+ * calculations in .apply() won't overflow.
+ */
+ if (!pc->clk_pwms[hwpwm].rate ||
+ pc->clk_pwms[hwpwm].rate > 1000000000) {
+ ret = -EINVAL;
+ goto disable_clk_hwpwm;
+ }
+ }
+
+ return 0;
+
+disable_clk_hwpwm:
+ clk_disable_unprepare(pc->clk_pwms[hwpwm].clk);
+disable_clk_main:
+ clk_disable_unprepare(pc->clk_main);
+disable_clk_top:
+ clk_disable_unprepare(pc->clk_top);
+
+ return ret;
+}
+
+static void pwm_mediatek_clk_disable(struct pwm_mediatek_chip *pc,
+ unsigned int hwpwm)
+{
+ clk_disable_unprepare(pc->clk_pwms[hwpwm].clk);
+ clk_disable_unprepare(pc->clk_main);
+ clk_disable_unprepare(pc->clk_top);
+}
+
+static inline void pwm_mediatek_writel(struct pwm_mediatek_chip *chip,
+ unsigned int num, unsigned int offset,
+ u32 value)
+{
+ writel(value, chip->regs + chip->soc->chanreg_base +
+ num * chip->soc->chanreg_width + offset);
+}
+
+static inline u32 pwm_mediatek_readl(struct pwm_mediatek_chip *chip,
+ unsigned int num, unsigned int offset)
+{
+ return readl(chip->regs + chip->soc->chanreg_base +
+ num * chip->soc->chanreg_width + offset);
+}
+
+struct pwm_mediatek_waveform {
+ u32 enable;
+ u32 con;
+ u32 width;
+ u32 thres;
+};
+
+static int pwm_mediatek_round_waveform_tohw(struct pwm_chip *chip, struct pwm_device *pwm,
+ const struct pwm_waveform *wf, void *_wfhw)
+{
+ struct pwm_mediatek_waveform *wfhw = _wfhw;
+ struct pwm_mediatek_chip *pc = to_pwm_mediatek_chip(chip);
+ u32 clkdiv, enable;
+ u64 cnt_period, cnt_duty;
+ unsigned long clk_rate;
+ int ret = 0;
+
+ if (wf->period_length_ns == 0) {
+ *wfhw = (typeof(*wfhw)){
+ .enable = 0,
+ };
+
+ return 0;
+ }
+
+ if (!pc->clk_pwms[pwm->hwpwm].rate) {
+ struct clk *clk = pc->clk_pwms[pwm->hwpwm].clk;
+
+ ret = clk_prepare_enable(clk);
+ if (ret)
+ return ret;
+
+ pc->clk_pwms[pwm->hwpwm].rate = clk_get_rate(clk);
+
+ clk_disable_unprepare(clk);
+ }
+
+ clk_rate = pc->clk_pwms[pwm->hwpwm].rate;
+ if (clk_rate == 0 || clk_rate > 1000000000)
+ return -EINVAL;
+
+ cnt_period = mul_u64_u64_div_u64(wf->period_length_ns, clk_rate, NSEC_PER_SEC);
+ if (cnt_period == 0) {
+ cnt_period = 1;
+ ret = 1;
+ }
+
+ if (cnt_period > FIELD_MAX(PWMDWIDTH_PERIOD) + 1) {
+ if (cnt_period >= ((FIELD_MAX(PWMDWIDTH_PERIOD) + 1) << FIELD_MAX(PWMCON_CLKDIV))) {
+ clkdiv = FIELD_MAX(PWMCON_CLKDIV);
+ cnt_period = FIELD_MAX(PWMDWIDTH_PERIOD) + 1;
+ } else {
+ clkdiv = ilog2(cnt_period) - ilog2(FIELD_MAX(PWMDWIDTH_PERIOD));
+ cnt_period >>= clkdiv;
+ }
+ } else {
+ clkdiv = 0;
+ }
+
+ cnt_duty = mul_u64_u64_div_u64(wf->duty_length_ns, clk_rate, NSEC_PER_SEC) >> clkdiv;
+ if (cnt_duty > cnt_period)
+ cnt_duty = cnt_period;
+
+ if (cnt_duty) {
+ cnt_duty -= 1;
+ enable = BIT(pwm->hwpwm);
+ } else {
+ enable = 0;
+ }
+
+ cnt_period -= 1;
+
+ dev_dbg(&chip->dev, "pwm#%u: %lld/%lld @%lu -> ENABLE: %x, CON: %x, PERIOD: %llx, DUTY: %llx\n",
+ pwm->hwpwm, wf->duty_length_ns, wf->period_length_ns, clk_rate,
+ enable, clkdiv, cnt_period, cnt_duty);
+
+ *wfhw = (typeof(*wfhw)){
+ .enable = enable,
+ .con = clkdiv,
+ .width = cnt_period,
+ .thres = cnt_duty,
+ };
+
+ return ret;
+}
+
+static int pwm_mediatek_round_waveform_fromhw(struct pwm_chip *chip, struct pwm_device *pwm,
+ const void *_wfhw, struct pwm_waveform *wf)
+{
+ const struct pwm_mediatek_waveform *wfhw = _wfhw;
+ struct pwm_mediatek_chip *pc = to_pwm_mediatek_chip(chip);
+ u32 clkdiv, cnt_period, cnt_duty;
+ unsigned long clk_rate;
+
+ /*
+ * When _wfhw was populated, the clock was on, so .rate is
+ * already set appropriately.
+ */
+ clk_rate = pc->clk_pwms[pwm->hwpwm].rate;
+
+ if (wfhw->enable) {
+ clkdiv = FIELD_GET(PWMCON_CLKDIV, wfhw->con);
+ cnt_period = FIELD_GET(PWMDWIDTH_PERIOD, wfhw->width);
+ cnt_duty = FIELD_GET(PWMTHRES_DUTY, wfhw->thres);
+
+ /*
+ * cnt_period is a 13 bit value, NSEC_PER_SEC is 30 bits wide
+ * and clkdiv is less than 8, so the multiplication doesn't
+ * overflow an u64.
+ */
+ *wf = (typeof(*wf)){
+ .period_length_ns =
+ DIV_ROUND_UP_ULL((u64)(cnt_period + 1) * NSEC_PER_SEC << clkdiv, clk_rate),
+ .duty_length_ns =
+ DIV_ROUND_UP_ULL((u64)(cnt_duty + 1) * NSEC_PER_SEC << clkdiv, clk_rate),
+ };
+ } else {
+ clkdiv = 0;
+ cnt_period = 0;
+ cnt_duty = 0;
+
+ /*
+ * .enable = 0 is also used for too small duty_cycle values, so
+ * report the HW as being enabled to communicate the minimal
+ * period.
+ */
+ *wf = (typeof(*wf)){
+ .period_length_ns =
+ DIV_ROUND_UP_ULL(NSEC_PER_SEC, clk_rate),
+ .duty_length_ns = 0,
+ };
+ }
+
+ dev_dbg(&chip->dev, "pwm#%u: ENABLE: %x, CLKDIV: %x, PERIOD: %x, DUTY: %x @%lu -> %lld/%lld\n",
+ pwm->hwpwm, wfhw->enable, clkdiv, cnt_period, cnt_duty, clk_rate,
+ wf->duty_length_ns, wf->period_length_ns);
+
+ return 0;
+}
+
+static int pwm_mediatek_read_waveform(struct pwm_chip *chip,
+ struct pwm_device *pwm, void *_wfhw)
+{
+ struct pwm_mediatek_waveform *wfhw = _wfhw;
+ struct pwm_mediatek_chip *pc = to_pwm_mediatek_chip(chip);
+ u32 enable, clkdiv, cnt_period, cnt_duty;
+ u32 reg_width = PWMDWIDTH, reg_thres = PWMTHRES;
+ int ret;
+
+ ret = pwm_mediatek_clk_enable(pc, pwm->hwpwm);
+ if (ret < 0)
+ return ret;
+
+ enable = readl(pc->regs) & BIT(pwm->hwpwm);
+
+ if (enable) {
+ if (pc->soc->pwm45_fixup && pwm->hwpwm > 2) {
+ /*
+ * PWM[4,5] has distinct offset for PWMDWIDTH and PWMTHRES
+ * from the other PWMs on MT7623.
+ */
+ reg_width = PWM45DWIDTH_FIXUP;
+ reg_thres = PWM45THRES_FIXUP;
+ }
+
+ clkdiv = FIELD_GET(PWMCON_CLKDIV, pwm_mediatek_readl(pc, pwm->hwpwm, PWMCON));
+ cnt_period = FIELD_GET(PWMDWIDTH_PERIOD, pwm_mediatek_readl(pc, pwm->hwpwm, reg_width));
+ cnt_duty = FIELD_GET(PWMTHRES_DUTY, pwm_mediatek_readl(pc, pwm->hwpwm, reg_thres));
+
+ *wfhw = (typeof(*wfhw)){
+ .enable = enable,
+ .con = BIT(15) | clkdiv,
+ .width = cnt_period,
+ .thres = cnt_duty,
+ };
+ } else {
+ *wfhw = (typeof(*wfhw)){
+ .enable = 0,
+ };
+ }
+
+ pwm_mediatek_clk_disable(pc, pwm->hwpwm);
+
+ return ret;
+}
+
+static int pwm_mediatek_write_waveform(struct pwm_chip *chip,
+ struct pwm_device *pwm, const void *_wfhw)
+{
+ const struct pwm_mediatek_waveform *wfhw = _wfhw;
+ struct pwm_mediatek_chip *pc = to_pwm_mediatek_chip(chip);
+ u32 ctrl;
+ int ret;
+
+ ret = pwm_mediatek_clk_enable(pc, pwm->hwpwm);
+ if (ret < 0)
+ return ret;
+
+ ctrl = readl(pc->regs);
+
+ if (wfhw->enable) {
+ u32 reg_width = PWMDWIDTH, reg_thres = PWMTHRES;
+ u32 con_val = PWMCON_OLD_PWM_MODE | wfhw->con;
+
+ if (pc->soc->pwm45_fixup && pwm->hwpwm > 2) {
+ /*
+ * PWM[4,5] has distinct offset for PWMDWIDTH and PWMTHRES
+ * from the other PWMs on MT7623.
+ */
+ reg_width = PWM45DWIDTH_FIXUP;
+ reg_thres = PWM45THRES_FIXUP;
+ }
+
+ if (!(ctrl & BIT(pwm->hwpwm))) {
+ /*
+ * The clks are already on, just increasing the usage
+ * counter doesn't fail.
+ */
+ ret = pwm_mediatek_clk_enable(pc, pwm->hwpwm);
+ if (unlikely(ret < 0))
+ goto out;
+
+ ctrl |= BIT(pwm->hwpwm);
+ writel(ctrl, pc->regs);
+ }
+
+ /* Make sure we use the bus clock and not the 26MHz clock */
+ if (pc->soc->pwm_ck_26m_sel_reg)
+ writel(0, pc->regs + pc->soc->pwm_ck_26m_sel_reg);
+
+ /* Set BIT(3) to disable clock division */
+ if (pc->soc->clksel_fixup)
+ con_val |= PWMCON_CLKSEL;
+
+ pwm_mediatek_writel(pc, pwm->hwpwm, PWMCON, con_val);
+ pwm_mediatek_writel(pc, pwm->hwpwm, reg_width, wfhw->width);
+ pwm_mediatek_writel(pc, pwm->hwpwm, reg_thres, wfhw->thres);
+ } else {
+ if (ctrl & BIT(pwm->hwpwm)) {
+ ctrl &= ~BIT(pwm->hwpwm);
+ writel(ctrl, pc->regs);
+
+ pwm_mediatek_clk_disable(pc, pwm->hwpwm);
+ }
+ }
+
+out:
+ pwm_mediatek_clk_disable(pc, pwm->hwpwm);
+
+ return ret;
+}
+
+static const struct pwm_ops pwm_mediatek_ops = {
+ .sizeof_wfhw = sizeof(struct pwm_mediatek_waveform),
+ .round_waveform_tohw = pwm_mediatek_round_waveform_tohw,
+ .round_waveform_fromhw = pwm_mediatek_round_waveform_fromhw,
+ .read_waveform = pwm_mediatek_read_waveform,
+ .write_waveform = pwm_mediatek_write_waveform,
+};
+
+static int pwm_mediatek_init_used_clks(struct pwm_mediatek_chip *pc)
+{
+ const struct pwm_mediatek_of_data *soc = pc->soc;
+ unsigned int hwpwm;
+ u32 enabled, handled = 0;
+ int ret;
+
+ ret = clk_prepare_enable(pc->clk_top);
+ if (ret)
+ return ret;
+
+ ret = clk_prepare_enable(pc->clk_main);
+ if (ret)
+ goto err_enable_main;
+
+ enabled = readl(pc->regs) & GENMASK(soc->num_pwms - 1, 0);
+
+ while (enabled & ~handled) {
+ hwpwm = ilog2(enabled & ~handled);
+
+ ret = pwm_mediatek_clk_enable(pc, hwpwm);
+ if (ret) {
+ while (handled) {
+ hwpwm = ilog2(handled);
+
+ pwm_mediatek_clk_disable(pc, hwpwm);
+ handled &= ~BIT(hwpwm);
+ }
+
+ break;
+ }
+
+ handled |= BIT(hwpwm);
+ }
+
+ clk_disable_unprepare(pc->clk_main);
+err_enable_main:
+
+ clk_disable_unprepare(pc->clk_top);
+
+ return ret;
+}
+
+static int pwm_mediatek_probe(struct platform_device *pdev)
+{
+ struct pwm_chip *chip;
+ struct pwm_mediatek_chip *pc;
+ const struct pwm_mediatek_of_data *soc;
+ unsigned int i;
+ int ret;
+
+ soc = of_device_get_match_data(&pdev->dev);
+
+ chip = devm_pwmchip_alloc(&pdev->dev, soc->num_pwms,
+ struct_size(pc, clk_pwms, soc->num_pwms));
+ if (IS_ERR(chip))
+ return PTR_ERR(chip);
+ pc = to_pwm_mediatek_chip(chip);
+
+ pc->soc = soc;
+
+ pc->regs = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(pc->regs))
+ return PTR_ERR(pc->regs);
+
+ pc->clk_top = devm_clk_get(&pdev->dev, "top");
+ if (IS_ERR(pc->clk_top))
+ return dev_err_probe(&pdev->dev, PTR_ERR(pc->clk_top),
+ "Failed to get top clock\n");
+
+ pc->clk_main = devm_clk_get(&pdev->dev, "main");
+ if (IS_ERR(pc->clk_main))
+ return dev_err_probe(&pdev->dev, PTR_ERR(pc->clk_main),
+ "Failed to get main clock\n");
+
+ for (i = 0; i < soc->num_pwms; i++) {
+ char name[8];
+
+ snprintf(name, sizeof(name), "pwm%d", i + 1);
+
+ pc->clk_pwms[i].clk = devm_clk_get(&pdev->dev, name);
+ if (IS_ERR(pc->clk_pwms[i].clk))
+ return dev_err_probe(&pdev->dev, PTR_ERR(pc->clk_pwms[i].clk),
+ "Failed to get %s clock\n", name);
+
+ ret = devm_clk_rate_exclusive_get(&pdev->dev, pc->clk_pwms[i].clk);
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret,
+ "Failed to lock clock rate for %s\n", name);
+ }
+
+ ret = pwm_mediatek_init_used_clks(pc);
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret, "Failed to initialize used clocks\n");
+
+ chip->ops = &pwm_mediatek_ops;
+
+ ret = devm_pwmchip_add(&pdev->dev, chip);
+ if (ret < 0)
+ return dev_err_probe(&pdev->dev, ret, "pwmchip_add() failed\n");
+
+ return 0;
+}
+
+static const struct pwm_mediatek_of_data mt2712_pwm_data = {
+ .num_pwms = 8,
+ .clksel_fixup = false,
+ .pwm45_fixup = false,
+ .chanreg_base = 0x10,
+ .chanreg_width = 0x40,
+};
+
+static const struct pwm_mediatek_of_data mt6795_pwm_data = {
+ .num_pwms = 7,
+ .clksel_fixup = false,
+ .pwm45_fixup = false,
+ .chanreg_base = 0x10,
+ .chanreg_width = 0x40,
+};
+
+static const struct pwm_mediatek_of_data mt7622_pwm_data = {
+ .num_pwms = 6,
+ .clksel_fixup = false,
+ .pwm45_fixup = false,
+ .pwm_ck_26m_sel_reg = PWM_CK_26M_SEL,
+ .chanreg_base = 0x10,
+ .chanreg_width = 0x40,
+};
+
+static const struct pwm_mediatek_of_data mt7623_pwm_data = {
+ .num_pwms = 5,
+ .clksel_fixup = false,
+ .pwm45_fixup = true,
+ .chanreg_base = 0x10,
+ .chanreg_width = 0x40,
+};
+
+static const struct pwm_mediatek_of_data mt7628_pwm_data = {
+ .num_pwms = 4,
+ .clksel_fixup = true,
+ .pwm45_fixup = false,
+ .chanreg_base = 0x10,
+ .chanreg_width = 0x40,
+};
+
+static const struct pwm_mediatek_of_data mt7629_pwm_data = {
+ .num_pwms = 1,
+ .clksel_fixup = false,
+ .pwm45_fixup = false,
+ .chanreg_base = 0x10,
+ .chanreg_width = 0x40,
+};
+
+static const struct pwm_mediatek_of_data mt7981_pwm_data = {
+ .num_pwms = 3,
+ .clksel_fixup = false,
+ .pwm45_fixup = false,
+ .pwm_ck_26m_sel_reg = PWM_CK_26M_SEL,
+ .chanreg_base = 0x80,
+ .chanreg_width = 0x40,
+};
+
+static const struct pwm_mediatek_of_data mt7986_pwm_data = {
+ .num_pwms = 2,
+ .clksel_fixup = false,
+ .pwm45_fixup = false,
+ .pwm_ck_26m_sel_reg = PWM_CK_26M_SEL,
+ .chanreg_base = 0x10,
+ .chanreg_width = 0x40,
+};
+
+static const struct pwm_mediatek_of_data mt7988_pwm_data = {
+ .num_pwms = 8,
+ .clksel_fixup = false,
+ .pwm45_fixup = false,
+ .chanreg_base = 0x80,
+ .chanreg_width = 0x40,
+};
+
+static const struct pwm_mediatek_of_data mt8183_pwm_data = {
+ .num_pwms = 4,
+ .clksel_fixup = false,
+ .pwm45_fixup = false,
+ .pwm_ck_26m_sel_reg = PWM_CK_26M_SEL,
+ .chanreg_base = 0x10,
+ .chanreg_width = 0x40,
+};
+
+static const struct pwm_mediatek_of_data mt8365_pwm_data = {
+ .num_pwms = 3,
+ .clksel_fixup = false,
+ .pwm45_fixup = false,
+ .pwm_ck_26m_sel_reg = PWM_CK_26M_SEL,
+ .chanreg_base = 0x10,
+ .chanreg_width = 0x40,
+};
+
+static const struct pwm_mediatek_of_data mt8516_pwm_data = {
+ .num_pwms = 5,
+ .clksel_fixup = false,
+ .pwm45_fixup = false,
+ .pwm_ck_26m_sel_reg = PWM_CK_26M_SEL,
+ .chanreg_base = 0x10,
+ .chanreg_width = 0x40,
+};
+
+static const struct pwm_mediatek_of_data mt6991_pwm_data = {
+ .num_pwms = 4,
+ .clksel_fixup = false,
+ .pwm45_fixup = false,
+ .pwm_ck_26m_sel_reg = PWM_CK_26M_SEL_V3,
+ .chanreg_base = 0x100,
+ .chanreg_width = 0x100,
+};
+
+static const struct of_device_id pwm_mediatek_of_match[] = {
+ { .compatible = "mediatek,mt2712-pwm", .data = &mt2712_pwm_data },
+ { .compatible = "mediatek,mt6795-pwm", .data = &mt6795_pwm_data },
+ { .compatible = "mediatek,mt6991-pwm", .data = &mt6991_pwm_data },
+ { .compatible = "mediatek,mt7622-pwm", .data = &mt7622_pwm_data },
+ { .compatible = "mediatek,mt7623-pwm", .data = &mt7623_pwm_data },
+ { .compatible = "mediatek,mt7628-pwm", .data = &mt7628_pwm_data },
+ { .compatible = "mediatek,mt7629-pwm", .data = &mt7629_pwm_data },
+ { .compatible = "mediatek,mt7981-pwm", .data = &mt7981_pwm_data },
+ { .compatible = "mediatek,mt7986-pwm", .data = &mt7986_pwm_data },
+ { .compatible = "mediatek,mt7988-pwm", .data = &mt7988_pwm_data },
+ { .compatible = "mediatek,mt8183-pwm", .data = &mt8183_pwm_data },
+ { .compatible = "mediatek,mt8365-pwm", .data = &mt8365_pwm_data },
+ { .compatible = "mediatek,mt8516-pwm", .data = &mt8516_pwm_data },
+ { }
+};
+MODULE_DEVICE_TABLE(of, pwm_mediatek_of_match);
+
+static struct platform_driver pwm_mediatek_driver = {
+ .driver = {
+ .name = "pwm-mediatek",
+ .of_match_table = pwm_mediatek_of_match,
+ },
+ .probe = pwm_mediatek_probe,
+};
+module_platform_driver(pwm_mediatek_driver);
+
+MODULE_AUTHOR("John Crispin <blogic@openwrt.org>");
+MODULE_DESCRIPTION("MediaTek general purpose Pulse Width Modulator driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/pwm/pwm-meson.c b/drivers/pwm/pwm-meson.c
new file mode 100644
index 000000000..20e6cd071
--- /dev/null
+++ b/drivers/pwm/pwm-meson.c
@@ -0,0 +1,691 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+/*
+ * PWM controller driver for Amlogic Meson SoCs.
+ *
+ * This PWM is only a set of Gates, Dividers and Counters:
+ * 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.
+ * 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.
+ * Setting the duty cycle will disable and re-enable the PWM output.
+ * Disabling the PWM stops the output immediately (without waiting for the
+ * current period to complete first).
+ *
+ * The public S912 (GXM) datasheet contains some documentation for this PWM
+ * controller starting on page 543:
+ * https://dl.khadas.com/Hardware/VIM2/Datasheet/S912_Datasheet_V0.220170314publicversion-Wesion.pdf
+ * An updated version of this IP block is found in S922X (G12B) SoCs. The
+ * datasheet contains the description for this IP block revision starting at
+ * page 1084:
+ * https://dn.odroid.com/S922X/ODROID-N2/Datasheet/S922X_Public_Datasheet_V0.2.pdf
+ *
+ * Copyright (c) 2016 BayLibre, SAS.
+ * Author: Neil Armstrong <narmstrong@baylibre.com>
+ * Copyright (C) 2014 Amlogic, Inc.
+ */
+
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/math64.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+
+#define REG_PWM_A 0x0
+#define REG_PWM_B 0x4
+#define PWM_LOW_MASK GENMASK(15, 0)
+#define PWM_HIGH_MASK GENMASK(31, 16)
+
+#define REG_MISC_AB 0x8
+#define MISC_B_CLK_EN_SHIFT 23
+#define MISC_A_CLK_EN_SHIFT 15
+#define MISC_CLK_DIV_WIDTH 7
+#define MISC_B_CLK_DIV_SHIFT 16
+#define MISC_A_CLK_DIV_SHIFT 8
+#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)
+
+#define MESON_NUM_PWMS 2
+#define MESON_NUM_MUX_PARENTS 4
+
+static struct meson_pwm_channel_data {
+ u8 reg_offset;
+ u8 clk_sel_shift;
+ 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,
+ .clk_sel_shift = MISC_A_CLK_SEL_SHIFT,
+ .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,
+ .clk_sel_shift = MISC_B_CLK_SEL_SHIFT,
+ .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,
+ }
+};
+
+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;
+ struct clk_gate gate;
+ struct clk *clk;
+};
+
+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 {
+ const struct meson_pwm_data *data;
+ struct meson_pwm_channel channels[MESON_NUM_PWMS];
+ void __iomem *base;
+ /*
+ * Protects register (write) access to the REG_MISC_AB register
+ * that is shared between the two PWMs.
+ */
+ spinlock_t lock;
+};
+
+static inline struct meson_pwm *to_meson_pwm(struct pwm_chip *chip)
+{
+ return pwmchip_get_drvdata(chip);
+}
+
+static int meson_pwm_request(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 device *dev = pwmchip_parent(chip);
+ int err;
+
+ err = clk_prepare_enable(channel->clk);
+ if (err < 0) {
+ dev_err(dev, "failed to enable clock %s: %d\n",
+ __clk_get_name(channel->clk), err);
+ return err;
+ }
+
+ return 0;
+}
+
+static void meson_pwm_free(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];
+
+ clk_disable_unprepare(channel->clk);
+}
+
+static int meson_pwm_calc(struct pwm_chip *chip, struct pwm_device *pwm,
+ const struct pwm_state *state)
+{
+ struct meson_pwm *meson = to_meson_pwm(chip);
+ struct meson_pwm_channel *channel = &meson->channels[pwm->hwpwm];
+ unsigned int cnt, duty_cnt;
+ long fin_freq;
+ u64 duty, period, freq;
+
+ duty = state->duty_cycle;
+ period = state->period;
+
+ /*
+ * Note this is wrong. The result is an output wave that isn't really
+ * inverted and so is wrongly identified by .get_state as normal.
+ * Fixing this needs some care however as some machines might rely on
+ * this.
+ */
+ if (state->polarity == PWM_POLARITY_INVERSED && !meson->data->has_polarity)
+ duty = period - duty;
+
+ freq = div64_u64(NSEC_PER_SEC * 0xffffULL, period);
+ if (freq > ULONG_MAX)
+ freq = ULONG_MAX;
+
+ fin_freq = clk_round_rate(channel->clk, freq);
+ if (fin_freq <= 0) {
+ dev_err(pwmchip_parent(chip),
+ "invalid source clock frequency %llu\n", freq);
+ return fin_freq ? fin_freq : -EINVAL;
+ }
+
+ dev_dbg(pwmchip_parent(chip), "fin_freq: %ld Hz\n", fin_freq);
+
+ cnt = mul_u64_u64_div_u64(fin_freq, period, NSEC_PER_SEC);
+ if (cnt > 0xffff) {
+ dev_err(pwmchip_parent(chip), "unable to get period cnt\n");
+ return -EINVAL;
+ }
+
+ dev_dbg(pwmchip_parent(chip), "period=%llu cnt=%u\n", period, cnt);
+
+ 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);
+
+ dev_dbg(pwmchip_parent(chip), "duty=%llu duty_cnt=%u\n", duty, duty_cnt);
+
+ channel->hi = duty_cnt;
+ channel->lo = cnt - duty_cnt;
+ channel->constant = false;
+ }
+
+ channel->rate = fin_freq;
+
+ return 0;
+}
+
+static void meson_pwm_enable(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;
+ int err;
+
+ channel_data = &meson_pwm_per_channel_data[pwm->hwpwm];
+
+ err = clk_set_rate(channel->clk, channel->rate);
+ if (err)
+ dev_err(pwmchip_parent(chip), "setting clock rate failed\n");
+
+ spin_lock_irqsave(&meson->lock, flags);
+
+ value = FIELD_PREP(PWM_HIGH_MASK, channel->hi) |
+ FIELD_PREP(PWM_LOW_MASK, channel->lo);
+ writel(value, meson->base + channel_data->reg_offset);
+
+ 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);
+}
+
+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 &= ~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);
+}
+
+static int meson_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
+ const struct pwm_state *state)
+{
+ struct meson_pwm *meson = to_meson_pwm(chip);
+ struct meson_pwm_channel *channel = &meson->channels[pwm->hwpwm];
+ int err = 0;
+
+ channel->inverted = (state->polarity == PWM_POLARITY_INVERSED);
+
+ if (!state->enabled) {
+ if (channel->inverted && !meson->data->has_polarity) {
+ /*
+ * 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
+ * in the shortest possible duration for one "count"
+ * and "period == duty_cycle". This results in a signal
+ * which is LOW for one "count", while being HIGH for
+ * the rest of the (so the signal is HIGH for slightly
+ * less than 100% of the period, but this is the best
+ * we can achieve).
+ */
+ channel->rate = ULONG_MAX;
+ channel->hi = ~0;
+ channel->lo = 0;
+ channel->constant = true;
+
+ meson_pwm_enable(chip, pwm);
+ } else {
+ meson_pwm_disable(chip, pwm);
+ }
+ } else {
+ err = meson_pwm_calc(chip, pwm, state);
+ if (err < 0)
+ return err;
+
+ meson_pwm_enable(chip, pwm);
+ }
+
+ return 0;
+}
+
+static u64 meson_pwm_cnt_to_ns(unsigned long fin_freq, u32 cnt)
+{
+ 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,
+ struct pwm_state *state)
+{
+ struct meson_pwm *meson = to_meson_pwm(chip);
+ struct meson_pwm_channel_data *channel_data;
+ unsigned long fin_freq;
+ unsigned int hi, lo;
+ u32 value;
+
+ 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;
+
+ if (meson->data->has_polarity && (value & channel_data->inv_en_mask))
+ state->polarity = PWM_POLARITY_INVERSED;
+ else
+ state->polarity = PWM_POLARITY_NORMAL;
+
+ value = readl(meson->base + channel_data->reg_offset);
+ lo = FIELD_GET(PWM_LOW_MASK, value);
+ hi = FIELD_GET(PWM_HIGH_MASK, value);
+
+ state->period = meson_pwm_cnt_to_ns(fin_freq, lo + hi);
+ state->duty_cycle = meson_pwm_cnt_to_ns(fin_freq, hi);
+
+ return 0;
+}
+
+static const struct pwm_ops meson_pwm_ops = {
+ .request = meson_pwm_request,
+ .free = meson_pwm_free,
+ .apply = meson_pwm_apply,
+ .get_state = meson_pwm_get_state,
+};
+
+static int meson_pwm_init_clocks_meson8b(struct pwm_chip *chip,
+ struct clk_parent_data *mux_parent_data)
+{
+ struct meson_pwm *meson = to_meson_pwm(chip);
+ struct device *dev = pwmchip_parent(chip);
+ unsigned int i;
+ char name[255];
+ int err;
+
+ for (i = 0; i < MESON_NUM_PWMS; i++) {
+ struct meson_pwm_channel *channel = &meson->channels[i];
+ struct clk_parent_data div_parent = {}, gate_parent = {};
+ struct clk_init_data init = {};
+
+ snprintf(name, sizeof(name), "%s#mux%u", dev_name(dev), i);
+
+ init.name = name;
+ init.ops = &clk_mux_ops;
+ init.flags = 0;
+ init.parent_data = mux_parent_data;
+ init.num_parents = MESON_NUM_MUX_PARENTS;
+
+ channel->mux.reg = meson->base + REG_MISC_AB;
+ channel->mux.shift =
+ meson_pwm_per_channel_data[i].clk_sel_shift;
+ channel->mux.mask = MISC_CLK_SEL_MASK;
+ channel->mux.flags = 0;
+ channel->mux.lock = &meson->lock;
+ channel->mux.table = NULL;
+ channel->mux.hw.init = &init;
+
+ err = devm_clk_hw_register(dev, &channel->mux.hw);
+ if (err)
+ return dev_err_probe(dev, err,
+ "failed to register %s\n", name);
+
+ snprintf(name, sizeof(name), "%s#div%u", dev_name(dev), i);
+
+ init.name = name;
+ init.ops = &clk_divider_ops;
+ init.flags = CLK_SET_RATE_PARENT;
+ div_parent.index = -1;
+ div_parent.hw = &channel->mux.hw;
+ init.parent_data = &div_parent;
+ init.num_parents = 1;
+
+ channel->div.reg = meson->base + REG_MISC_AB;
+ channel->div.shift = meson_pwm_per_channel_data[i].clk_div_shift;
+ channel->div.width = MISC_CLK_DIV_WIDTH;
+ channel->div.hw.init = &init;
+ channel->div.flags = 0;
+ channel->div.lock = &meson->lock;
+
+ err = devm_clk_hw_register(dev, &channel->div.hw);
+ if (err)
+ return dev_err_probe(dev, err,
+ "failed to register %s\n", name);
+
+ snprintf(name, sizeof(name), "%s#gate%u", dev_name(dev), i);
+
+ init.name = name;
+ init.ops = &clk_gate_ops;
+ init.flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED;
+ gate_parent.index = -1;
+ gate_parent.hw = &channel->div.hw;
+ init.parent_data = &gate_parent;
+ init.num_parents = 1;
+
+ channel->gate.reg = meson->base + REG_MISC_AB;
+ channel->gate.bit_idx = meson_pwm_per_channel_data[i].clk_en_shift;
+ channel->gate.hw.init = &init;
+ channel->gate.flags = 0;
+ channel->gate.lock = &meson->lock;
+
+ err = devm_clk_hw_register(dev, &channel->gate.hw);
+ if (err)
+ return dev_err_probe(dev, err, "failed to register %s\n", name);
+
+ channel->clk = devm_clk_hw_get_clk(dev, &channel->gate.hw, NULL);
+ if (IS_ERR(channel->clk))
+ return dev_err_probe(dev, PTR_ERR(channel->clk),
+ "failed to register %s\n", name);
+ }
+
+ return 0;
+}
+
+static int meson_pwm_init_channels_meson8b_legacy(struct pwm_chip *chip)
+{
+ struct clk_parent_data mux_parent_data[MESON_NUM_MUX_PARENTS] = {};
+ struct meson_pwm *meson = to_meson_pwm(chip);
+ int i;
+
+ dev_warn_once(pwmchip_parent(chip),
+ "using obsolete compatible, please consider updating dt\n");
+
+ for (i = 0; i < MESON_NUM_MUX_PARENTS; i++) {
+ mux_parent_data[i].index = -1;
+ mux_parent_data[i].name = meson->data->parent_names[i];
+ }
+
+ return meson_pwm_init_clocks_meson8b(chip, mux_parent_data);
+}
+
+static int meson_pwm_init_channels_meson8b_v2(struct pwm_chip *chip)
+{
+ struct clk_parent_data mux_parent_data[MESON_NUM_MUX_PARENTS] = {};
+ int i;
+
+ /*
+ * NOTE: Instead of relying on the hard coded names in the driver
+ * as the legacy version, this relies on DT to provide the list of
+ * clocks.
+ * For once, using input numbers actually makes more sense than names.
+ * Also DT requires clock-names to be explicitly ordered, so there is
+ * no point bothering with clock names in this case.
+ */
+ for (i = 0; i < MESON_NUM_MUX_PARENTS; i++)
+ mux_parent_data[i].index = i;
+
+ return meson_pwm_init_clocks_meson8b(chip, mux_parent_data);
+}
+
+static void meson_pwm_s4_put_clk(void *data)
+{
+ struct clk *clk = data;
+
+ clk_put(clk);
+}
+
+static int meson_pwm_init_channels_s4(struct pwm_chip *chip)
+{
+ struct device *dev = pwmchip_parent(chip);
+ struct device_node *np = dev->of_node;
+ struct meson_pwm *meson = to_meson_pwm(chip);
+ int i, ret;
+
+ for (i = 0; i < MESON_NUM_PWMS; i++) {
+ meson->channels[i].clk = of_clk_get(np, i);
+ if (IS_ERR(meson->channels[i].clk))
+ return dev_err_probe(dev,
+ PTR_ERR(meson->channels[i].clk),
+ "Failed to get clk\n");
+
+ ret = devm_add_action_or_reset(dev, meson_pwm_s4_put_clk,
+ meson->channels[i].clk);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "Failed to add clk_put action\n");
+ }
+
+ return 0;
+}
+
+static const struct meson_pwm_data pwm_meson8b_data = {
+ .parent_names = { "xtal", NULL, "fclk_div4", "fclk_div3" },
+ .channels_init = meson_pwm_init_channels_meson8b_legacy,
+};
+
+/*
+ * Only the 2 first inputs of the GXBB AO PWMs are valid
+ * The last 2 are grounded
+ */
+static const struct meson_pwm_data pwm_gxbb_ao_data = {
+ .parent_names = { "xtal", "clk81", NULL, NULL },
+ .channels_init = meson_pwm_init_channels_meson8b_legacy,
+};
+
+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[] = {
+ {
+ .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",
+ .data = &pwm_meson8b_data
+ },
+ {
+ .compatible = "amlogic,meson-gxbb-pwm",
+ .data = &pwm_meson8b_data
+ },
+ {
+ .compatible = "amlogic,meson-gxbb-ao-pwm",
+ .data = &pwm_gxbb_ao_data
+ },
+ {
+ .compatible = "amlogic,meson-axg-ee-pwm",
+ .data = &pwm_axg_ee_data
+ },
+ {
+ .compatible = "amlogic,meson-axg-ao-pwm",
+ .data = &pwm_axg_ao_data
+ },
+ {
+ .compatible = "amlogic,meson-g12a-ee-pwm",
+ .data = &pwm_g12a_ee_data
+ },
+ {
+ .compatible = "amlogic,meson-g12a-ao-pwm-ab",
+ .data = &pwm_g12a_ao_ab_data
+ },
+ {
+ .compatible = "amlogic,meson-g12a-ao-pwm-cd",
+ .data = &pwm_g12a_ao_cd_data
+ },
+ {
+ .compatible = "amlogic,meson-s4-pwm",
+ .data = &pwm_s4_data
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(of, meson_pwm_matches);
+
+static int meson_pwm_probe(struct platform_device *pdev)
+{
+ struct pwm_chip *chip;
+ struct meson_pwm *meson;
+ int err;
+
+ chip = devm_pwmchip_alloc(&pdev->dev, MESON_NUM_PWMS, sizeof(*meson));
+ if (IS_ERR(chip))
+ return PTR_ERR(chip);
+ meson = to_meson_pwm(chip);
+
+ meson->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(meson->base))
+ return PTR_ERR(meson->base);
+
+ spin_lock_init(&meson->lock);
+ chip->ops = &meson_pwm_ops;
+
+ meson->data = of_device_get_match_data(&pdev->dev);
+
+ err = meson->data->channels_init(chip);
+ if (err < 0)
+ return err;
+
+ err = devm_pwmchip_add(&pdev->dev, chip);
+ if (err < 0)
+ return dev_err_probe(&pdev->dev, err,
+ "failed to register PWM chip\n");
+
+ return 0;
+}
+
+static struct platform_driver meson_pwm_driver = {
+ .driver = {
+ .name = "meson-pwm",
+ .of_match_table = meson_pwm_matches,
+ },
+ .probe = meson_pwm_probe,
+};
+module_platform_driver(meson_pwm_driver);
+
+MODULE_DESCRIPTION("Amlogic Meson PWM Generator driver");
+MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>");
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/drivers/pwm/pwm-microchip-core.c b/drivers/pwm/pwm-microchip-core.c
new file mode 100644
index 000000000..4ff32bb4c
--- /dev/null
+++ b/drivers/pwm/pwm-microchip-core.c
@@ -0,0 +1,490 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * corePWM driver for Microchip "soft" FPGA IP cores.
+ *
+ * Copyright (c) 2021-2023 Microchip Corporation. All rights reserved.
+ * Author: Conor Dooley <conor.dooley@microchip.com>
+ * Documentation:
+ * https://www.microsemi.com/document-portal/doc_download/1245275-corepwm-hb
+ *
+ * Limitations:
+ * - If the IP block is configured without "shadow registers", all register
+ * writes will take effect immediately, causing glitches on the output.
+ * If shadow registers *are* enabled, setting the "SYNC_UPDATE" register
+ * notifies the core that it needs to update the registers defining the
+ * waveform from the contents of the "shadow registers". Otherwise, changes
+ * will take effective immediately, even for those channels.
+ * As setting the period/duty cycle takes 4 register writes, there is a window
+ * in which this races against the start of a new period.
+ * - The IP block has no concept of a duty cycle, only rising/falling edges of
+ * the waveform. Unfortunately, if the rising & falling edges registers have
+ * the same value written to them the IP block will do whichever of a rising
+ * or a falling edge is possible. I.E. a 50% waveform at twice the requested
+ * period. Therefore to get a 0% waveform, the output is set the max high/low
+ * time depending on polarity.
+ * If the duty cycle is 0%, and the requested period is less than the
+ * available period resolution, this will manifest as a ~100% waveform (with
+ * some output glitches) rather than 50%.
+ * - The PWM period is set for the whole IP block not per channel. The driver
+ * will only change the period if no other PWM output is enabled.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/ktime.h>
+#include <linux/math.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+
+#define MCHPCOREPWM_PRESCALE_MAX 0xff
+#define MCHPCOREPWM_PERIOD_STEPS_MAX 0xfe
+#define MCHPCOREPWM_PERIOD_MAX 0xff00
+
+#define MCHPCOREPWM_PRESCALE 0x00
+#define MCHPCOREPWM_PERIOD 0x04
+#define MCHPCOREPWM_EN(i) (0x08 + 0x04 * (i)) /* 0x08, 0x0c */
+#define MCHPCOREPWM_POSEDGE(i) (0x10 + 0x08 * (i)) /* 0x10, 0x18, ..., 0x88 */
+#define MCHPCOREPWM_NEGEDGE(i) (0x14 + 0x08 * (i)) /* 0x14, 0x1c, ..., 0x8c */
+#define MCHPCOREPWM_SYNC_UPD 0xe4
+#define MCHPCOREPWM_TIMEOUT_MS 100u
+
+struct mchp_core_pwm_chip {
+ struct clk *clk;
+ void __iomem *base;
+ ktime_t update_timestamp;
+ u32 sync_update_mask;
+ u16 channel_enabled;
+};
+
+static inline struct mchp_core_pwm_chip *to_mchp_core_pwm(struct pwm_chip *chip)
+{
+ return pwmchip_get_drvdata(chip);
+}
+
+static void mchp_core_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm,
+ bool enable, u64 period)
+{
+ struct mchp_core_pwm_chip *mchp_core_pwm = to_mchp_core_pwm(chip);
+ u8 channel_enable, reg_offset, shift;
+
+ /*
+ * There are two adjacent 8 bit control regs, the lower reg controls
+ * 0-7 and the upper reg 8-15. Check if the pwm is in the upper reg
+ * and if so, offset by the bus width.
+ */
+ reg_offset = MCHPCOREPWM_EN(pwm->hwpwm >> 3);
+ shift = pwm->hwpwm & 7;
+
+ channel_enable = readb_relaxed(mchp_core_pwm->base + reg_offset);
+ channel_enable &= ~(1 << shift);
+ channel_enable |= (enable << shift);
+
+ writel_relaxed(channel_enable, mchp_core_pwm->base + reg_offset);
+ mchp_core_pwm->channel_enabled &= ~BIT(pwm->hwpwm);
+ mchp_core_pwm->channel_enabled |= enable << pwm->hwpwm;
+
+ /*
+ * The updated values will not appear on the bus until they have been
+ * applied to the waveform at the beginning of the next period.
+ * This is a NO-OP if the channel does not have shadow registers.
+ */
+ if (mchp_core_pwm->sync_update_mask & (1 << pwm->hwpwm))
+ mchp_core_pwm->update_timestamp = ktime_add_ns(ktime_get(), period);
+}
+
+static void mchp_core_pwm_wait_for_sync_update(struct mchp_core_pwm_chip *mchp_core_pwm,
+ unsigned int channel)
+{
+ /*
+ * If a shadow register is used for this PWM channel, and iff there is
+ * a pending update to the waveform, we must wait for it to be applied
+ * before attempting to read its state. Reading the registers yields
+ * the currently implemented settings & the new ones are only readable
+ * once the current period has ended.
+ */
+
+ if (mchp_core_pwm->sync_update_mask & (1 << channel)) {
+ ktime_t current_time = ktime_get();
+ s64 remaining_ns;
+ u32 delay_us;
+
+ remaining_ns = ktime_to_ns(ktime_sub(mchp_core_pwm->update_timestamp,
+ current_time));
+
+ /*
+ * If the update has gone through, don't bother waiting for
+ * obvious reasons. Otherwise wait around for an appropriate
+ * amount of time for the update to go through.
+ */
+ if (remaining_ns <= 0)
+ return;
+
+ delay_us = DIV_ROUND_UP_ULL(remaining_ns, NSEC_PER_USEC);
+ fsleep(delay_us);
+ }
+}
+
+static u64 mchp_core_pwm_calc_duty(const struct pwm_state *state, u64 clk_rate,
+ u8 prescale, u8 period_steps)
+{
+ u64 duty_steps, tmp;
+
+ /*
+ * Calculate the duty cycle in multiples of the prescaled period:
+ * duty_steps = duty_in_ns / step_in_ns
+ * step_in_ns = (prescale * NSEC_PER_SEC) / clk_rate
+ * The code below is rearranged slightly to only divide once.
+ */
+ tmp = (((u64)prescale) + 1) * NSEC_PER_SEC;
+ duty_steps = mul_u64_u64_div_u64(state->duty_cycle, clk_rate, tmp);
+
+ return duty_steps;
+}
+
+static void mchp_core_pwm_apply_duty(struct pwm_chip *chip, struct pwm_device *pwm,
+ const struct pwm_state *state, u64 duty_steps,
+ u16 period_steps)
+{
+ struct mchp_core_pwm_chip *mchp_core_pwm = to_mchp_core_pwm(chip);
+ u8 posedge, negedge;
+ u8 first_edge = 0, second_edge = duty_steps;
+
+ /*
+ * Setting posedge == negedge doesn't yield a constant output,
+ * so that's an unsuitable setting to model duty_steps = 0.
+ * In that case set the unwanted edge to a value that never
+ * triggers.
+ */
+ if (duty_steps == 0)
+ first_edge = period_steps + 1;
+
+ if (state->polarity == PWM_POLARITY_INVERSED) {
+ negedge = first_edge;
+ posedge = second_edge;
+ } else {
+ posedge = first_edge;
+ negedge = second_edge;
+ }
+
+ /*
+ * Set the sync bit which ensures that periods that already started are
+ * completed unaltered. At each counter reset event the values are
+ * updated from the shadow registers.
+ */
+ writel_relaxed(posedge, mchp_core_pwm->base + MCHPCOREPWM_POSEDGE(pwm->hwpwm));
+ writel_relaxed(negedge, mchp_core_pwm->base + MCHPCOREPWM_NEGEDGE(pwm->hwpwm));
+}
+
+static int mchp_core_pwm_calc_period(const struct pwm_state *state, unsigned long clk_rate,
+ u16 *prescale, u16 *period_steps)
+{
+ u64 tmp;
+
+ /*
+ * Calculate the period cycles and prescale values.
+ * The registers are each 8 bits wide & multiplied to compute the period
+ * using the formula:
+ * (prescale + 1) * (period_steps + 1)
+ * period = -------------------------------------
+ * clk_rate
+ * so the maximum period that can be generated is 0x10000 times the
+ * period of the input clock.
+ * However, due to the design of the "hardware", it is not possible to
+ * attain a 100% duty cycle if the full range of period_steps is used.
+ * Therefore period_steps is restricted to 0xfe and the maximum multiple
+ * of the clock period attainable is (0xff + 1) * (0xfe + 1) = 0xff00
+ *
+ * The prescale and period_steps registers operate similarly to
+ * CLK_DIVIDER_ONE_BASED, where the value used by the hardware is that
+ * in the register plus one.
+ * It's therefore not possible to set a period lower than 1/clk_rate, so
+ * if tmp is 0, abort. Without aborting, we will set a period that is
+ * greater than that requested and, more importantly, will trigger the
+ * neg-/pos-edge issue described in the limitations.
+ */
+ tmp = mul_u64_u64_div_u64(state->period, clk_rate, NSEC_PER_SEC);
+ if (tmp >= MCHPCOREPWM_PERIOD_MAX) {
+ *prescale = MCHPCOREPWM_PRESCALE_MAX;
+ *period_steps = MCHPCOREPWM_PERIOD_STEPS_MAX;
+
+ return 0;
+ }
+
+ /*
+ * There are multiple strategies that could be used to choose the
+ * prescale & period_steps values.
+ * Here the idea is to pick values so that the selection of duty cycles
+ * is as finegrain as possible, while also keeping the period less than
+ * that requested.
+ *
+ * A simple way to satisfy the first condition is to always set
+ * period_steps to its maximum value. This neatly also satisfies the
+ * second condition too, since using the maximum value of period_steps
+ * to calculate prescale actually calculates its upper bound.
+ * Integer division will ensure a round down, so the period will thereby
+ * always be less than that requested.
+ *
+ * The downside of this approach is a significant degree of inaccuracy,
+ * especially as tmp approaches integer multiples of
+ * MCHPCOREPWM_PERIOD_STEPS_MAX.
+ *
+ * As we must produce a period less than that requested, and for the
+ * sake of creating a simple algorithm, disallow small values of tmp
+ * that would need special handling.
+ */
+ if (tmp < MCHPCOREPWM_PERIOD_STEPS_MAX + 1)
+ return -EINVAL;
+
+ /*
+ * This "optimal" value for prescale is be calculated using the maximum
+ * permitted value of period_steps, 0xfe.
+ *
+ * period * clk_rate
+ * prescale = ------------------------- - 1
+ * NSEC_PER_SEC * (0xfe + 1)
+ *
+ *
+ * period * clk_rate
+ * ------------------- was precomputed as `tmp`
+ * NSEC_PER_SEC
+ */
+ *prescale = ((u16)tmp) / (MCHPCOREPWM_PERIOD_STEPS_MAX + 1) - 1;
+
+ /*
+ * period_steps can be computed from prescale:
+ * period * clk_rate
+ * period_steps = ----------------------------- - 1
+ * NSEC_PER_SEC * (prescale + 1)
+ *
+ * However, in this approximation, we simply use the maximum value that
+ * was used to compute prescale.
+ */
+ *period_steps = MCHPCOREPWM_PERIOD_STEPS_MAX;
+
+ return 0;
+}
+
+static int mchp_core_pwm_apply_locked(struct pwm_chip *chip, struct pwm_device *pwm,
+ const struct pwm_state *state)
+{
+ struct mchp_core_pwm_chip *mchp_core_pwm = to_mchp_core_pwm(chip);
+ bool period_locked;
+ unsigned long clk_rate;
+ u64 duty_steps;
+ u16 prescale, period_steps;
+ int ret;
+
+ if (!state->enabled) {
+ mchp_core_pwm_enable(chip, pwm, false, pwm->state.period);
+ return 0;
+ }
+
+ /*
+ * If clk_rate is too big, the following multiplication might overflow.
+ * However this is implausible, as the fabric of current FPGAs cannot
+ * provide clocks at a rate high enough.
+ */
+ clk_rate = clk_get_rate(mchp_core_pwm->clk);
+ if (clk_rate >= NSEC_PER_SEC)
+ return -EINVAL;
+
+ ret = mchp_core_pwm_calc_period(state, clk_rate, &prescale, &period_steps);
+ if (ret)
+ return ret;
+
+ /*
+ * If the only thing that has changed is the duty cycle or the polarity,
+ * we can shortcut the calculations and just compute/apply the new duty
+ * cycle pos & neg edges
+ * As all the channels share the same period, do not allow it to be
+ * changed if any other channels are enabled.
+ * If the period is locked, it may not be possible to use a period
+ * less than that requested. In that case, we just abort.
+ */
+ period_locked = mchp_core_pwm->channel_enabled & ~(1 << pwm->hwpwm);
+
+ if (period_locked) {
+ u16 hw_prescale;
+ u16 hw_period_steps;
+
+ hw_prescale = readb_relaxed(mchp_core_pwm->base + MCHPCOREPWM_PRESCALE);
+ hw_period_steps = readb_relaxed(mchp_core_pwm->base + MCHPCOREPWM_PERIOD);
+
+ if ((period_steps + 1) * (prescale + 1) <
+ (hw_period_steps + 1) * (hw_prescale + 1))
+ return -EINVAL;
+
+ /*
+ * It is possible that something could have set the period_steps
+ * register to 0xff, which would prevent us from setting a 100%
+ * or 0% relative duty cycle, as explained above in
+ * mchp_core_pwm_calc_period().
+ * The period is locked and we cannot change this, so we abort.
+ */
+ if (hw_period_steps > MCHPCOREPWM_PERIOD_STEPS_MAX)
+ return -EINVAL;
+
+ prescale = hw_prescale;
+ period_steps = hw_period_steps;
+ }
+
+ duty_steps = mchp_core_pwm_calc_duty(state, clk_rate, prescale, period_steps);
+
+ /*
+ * Because the period is not per channel, it is possible that the
+ * requested duty cycle is longer than the period, in which case cap it
+ * to the period, IOW a 100% duty cycle.
+ */
+ if (duty_steps > period_steps)
+ duty_steps = period_steps + 1;
+
+ if (!period_locked) {
+ writel_relaxed(prescale, mchp_core_pwm->base + MCHPCOREPWM_PRESCALE);
+ writel_relaxed(period_steps, mchp_core_pwm->base + MCHPCOREPWM_PERIOD);
+ }
+
+ mchp_core_pwm_apply_duty(chip, pwm, state, duty_steps, period_steps);
+
+ mchp_core_pwm_enable(chip, pwm, true, pwm->state.period);
+
+ return 0;
+}
+
+static int mchp_core_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
+ const struct pwm_state *state)
+{
+ struct mchp_core_pwm_chip *mchp_core_pwm = to_mchp_core_pwm(chip);
+
+ mchp_core_pwm_wait_for_sync_update(mchp_core_pwm, pwm->hwpwm);
+
+ return mchp_core_pwm_apply_locked(chip, pwm, state);
+}
+
+static int mchp_core_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm,
+ struct pwm_state *state)
+{
+ struct mchp_core_pwm_chip *mchp_core_pwm = to_mchp_core_pwm(chip);
+ u64 rate;
+ u16 prescale, period_steps;
+ u8 duty_steps, posedge, negedge;
+
+ mchp_core_pwm_wait_for_sync_update(mchp_core_pwm, pwm->hwpwm);
+
+ if (mchp_core_pwm->channel_enabled & (1 << pwm->hwpwm))
+ state->enabled = true;
+ else
+ state->enabled = false;
+
+ rate = clk_get_rate(mchp_core_pwm->clk);
+
+ /*
+ * Calculating the period:
+ * The registers are each 8 bits wide & multiplied to compute the period
+ * using the formula:
+ * (prescale + 1) * (period_steps + 1)
+ * period = -------------------------------------
+ * clk_rate
+ *
+ * Note:
+ * The prescale and period_steps registers operate similarly to
+ * CLK_DIVIDER_ONE_BASED, where the value used by the hardware is that
+ * in the register plus one.
+ */
+ prescale = readb_relaxed(mchp_core_pwm->base + MCHPCOREPWM_PRESCALE);
+ period_steps = readb_relaxed(mchp_core_pwm->base + MCHPCOREPWM_PERIOD);
+
+ state->period = (period_steps + 1) * (prescale + 1);
+ state->period *= NSEC_PER_SEC;
+ state->period = DIV64_U64_ROUND_UP(state->period, rate);
+
+ posedge = readb_relaxed(mchp_core_pwm->base + MCHPCOREPWM_POSEDGE(pwm->hwpwm));
+ negedge = readb_relaxed(mchp_core_pwm->base + MCHPCOREPWM_NEGEDGE(pwm->hwpwm));
+
+ if (negedge == posedge) {
+ state->duty_cycle = state->period;
+ state->period *= 2;
+ } else {
+ duty_steps = abs((s16)posedge - (s16)negedge);
+ state->duty_cycle = duty_steps * (prescale + 1) * NSEC_PER_SEC;
+ state->duty_cycle = DIV64_U64_ROUND_UP(state->duty_cycle, rate);
+ }
+
+ state->polarity = negedge < posedge ? PWM_POLARITY_INVERSED : PWM_POLARITY_NORMAL;
+
+ return 0;
+}
+
+static const struct pwm_ops mchp_core_pwm_ops = {
+ .apply = mchp_core_pwm_apply,
+ .get_state = mchp_core_pwm_get_state,
+};
+
+static const struct of_device_id mchp_core_of_match[] = {
+ {
+ .compatible = "microchip,corepwm-rtl-v4",
+ },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, mchp_core_of_match);
+
+static int mchp_core_pwm_probe(struct platform_device *pdev)
+{
+ struct pwm_chip *chip;
+ struct mchp_core_pwm_chip *mchp_core_pwm;
+ struct resource *regs;
+ int ret;
+
+ chip = devm_pwmchip_alloc(&pdev->dev, 16, sizeof(*mchp_core_pwm));
+ if (IS_ERR(chip))
+ return PTR_ERR(chip);
+ mchp_core_pwm = to_mchp_core_pwm(chip);
+
+ mchp_core_pwm->base = devm_platform_get_and_ioremap_resource(pdev, 0, &regs);
+ if (IS_ERR(mchp_core_pwm->base))
+ return PTR_ERR(mchp_core_pwm->base);
+
+ mchp_core_pwm->clk = devm_clk_get_enabled(&pdev->dev, NULL);
+ if (IS_ERR(mchp_core_pwm->clk))
+ return dev_err_probe(&pdev->dev, PTR_ERR(mchp_core_pwm->clk),
+ "failed to get PWM clock\n");
+
+ if (of_property_read_u32(pdev->dev.of_node, "microchip,sync-update-mask",
+ &mchp_core_pwm->sync_update_mask))
+ mchp_core_pwm->sync_update_mask = 0;
+
+ chip->ops = &mchp_core_pwm_ops;
+
+ mchp_core_pwm->channel_enabled = readb_relaxed(mchp_core_pwm->base + MCHPCOREPWM_EN(0));
+ mchp_core_pwm->channel_enabled |=
+ readb_relaxed(mchp_core_pwm->base + MCHPCOREPWM_EN(1)) << 8;
+
+ /*
+ * Enable synchronous update mode for all channels for which shadow
+ * registers have been synthesised.
+ */
+ writel_relaxed(1U, mchp_core_pwm->base + MCHPCOREPWM_SYNC_UPD);
+ mchp_core_pwm->update_timestamp = ktime_get();
+
+ ret = devm_pwmchip_add(&pdev->dev, chip);
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret, "Failed to add pwmchip\n");
+
+ return 0;
+}
+
+static struct platform_driver mchp_core_pwm_driver = {
+ .driver = {
+ .name = "mchp-core-pwm",
+ .of_match_table = mchp_core_of_match,
+ },
+ .probe = mchp_core_pwm_probe,
+};
+module_platform_driver(mchp_core_pwm_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Conor Dooley <conor.dooley@microchip.com>");
+MODULE_DESCRIPTION("corePWM driver for Microchip FPGAs");
diff --git a/drivers/pwm/pwm-mtk-disp.c b/drivers/pwm/pwm-mtk-disp.c
new file mode 100644
index 000000000..b5409e15d
--- /dev/null
+++ b/drivers/pwm/pwm-mtk-disp.c
@@ -0,0 +1,317 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * MediaTek display pulse-width-modulation controller driver.
+ * Copyright (c) 2015 MediaTek Inc.
+ * Author: YH Huang <yh.huang@mediatek.com>
+ */
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+#include <linux/slab.h>
+
+#define DISP_PWM_EN 0x00
+
+#define PWM_CLKDIV_SHIFT 16
+#define PWM_CLKDIV_MAX 0x3ff
+#define PWM_CLKDIV_MASK (PWM_CLKDIV_MAX << PWM_CLKDIV_SHIFT)
+
+#define PWM_PERIOD_BIT_WIDTH 12
+#define PWM_PERIOD_MASK ((1 << PWM_PERIOD_BIT_WIDTH) - 1)
+
+#define PWM_HIGH_WIDTH_SHIFT 16
+#define PWM_HIGH_WIDTH_MASK (0x1fff << PWM_HIGH_WIDTH_SHIFT)
+
+struct mtk_pwm_data {
+ u32 enable_mask;
+ unsigned int con0;
+ u32 con0_sel;
+ unsigned int con1;
+
+ bool has_commit;
+ unsigned int commit;
+ unsigned int commit_mask;
+
+ unsigned int bls_debug;
+ u32 bls_debug_mask;
+};
+
+struct mtk_disp_pwm {
+ const struct mtk_pwm_data *data;
+ struct clk *clk_main;
+ struct clk *clk_mm;
+ void __iomem *base;
+ bool enabled;
+};
+
+static inline struct mtk_disp_pwm *to_mtk_disp_pwm(struct pwm_chip *chip)
+{
+ return pwmchip_get_drvdata(chip);
+}
+
+static void mtk_disp_pwm_update_bits(struct mtk_disp_pwm *mdp, u32 offset,
+ u32 mask, u32 data)
+{
+ void __iomem *address = mdp->base + offset;
+ u32 value;
+
+ value = readl(address);
+ value &= ~mask;
+ value |= data;
+ writel(value, address);
+}
+
+static int mtk_disp_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
+ const struct pwm_state *state)
+{
+ struct mtk_disp_pwm *mdp = to_mtk_disp_pwm(chip);
+ u32 clk_div, period, high_width, value;
+ u64 div, rate;
+ int err;
+
+ if (state->polarity != PWM_POLARITY_NORMAL)
+ return -EINVAL;
+
+ if (!state->enabled && mdp->enabled) {
+ mtk_disp_pwm_update_bits(mdp, DISP_PWM_EN,
+ mdp->data->enable_mask, 0x0);
+ clk_disable_unprepare(mdp->clk_mm);
+ clk_disable_unprepare(mdp->clk_main);
+
+ mdp->enabled = false;
+ return 0;
+ }
+
+ if (!mdp->enabled) {
+ err = clk_prepare_enable(mdp->clk_main);
+ if (err < 0) {
+ dev_err(pwmchip_parent(chip), "Can't enable mdp->clk_main: %pe\n",
+ ERR_PTR(err));
+ return err;
+ }
+
+ err = clk_prepare_enable(mdp->clk_mm);
+ if (err < 0) {
+ dev_err(pwmchip_parent(chip), "Can't enable mdp->clk_mm: %pe\n",
+ ERR_PTR(err));
+ clk_disable_unprepare(mdp->clk_main);
+ return err;
+ }
+ }
+
+ /*
+ * Find period, high_width and clk_div to suit duty_ns and period_ns.
+ * Calculate proper div value to keep period value in the bound.
+ *
+ * period_ns = 10^9 * (clk_div + 1) * (period + 1) / PWM_CLK_RATE
+ * duty_ns = 10^9 * (clk_div + 1) * high_width / PWM_CLK_RATE
+ *
+ * period = (PWM_CLK_RATE * period_ns) / (10^9 * (clk_div + 1)) - 1
+ * high_width = (PWM_CLK_RATE * duty_ns) / (10^9 * (clk_div + 1))
+ */
+ rate = clk_get_rate(mdp->clk_main);
+ clk_div = mul_u64_u64_div_u64(state->period, rate, NSEC_PER_SEC) >>
+ PWM_PERIOD_BIT_WIDTH;
+ if (clk_div > PWM_CLKDIV_MAX) {
+ if (!mdp->enabled) {
+ clk_disable_unprepare(mdp->clk_mm);
+ clk_disable_unprepare(mdp->clk_main);
+ }
+ return -EINVAL;
+ }
+
+ div = NSEC_PER_SEC * (clk_div + 1);
+ period = mul_u64_u64_div_u64(state->period, rate, div);
+ if (period > 0)
+ period--;
+
+ high_width = mul_u64_u64_div_u64(state->duty_cycle, rate, div);
+ value = period | (high_width << PWM_HIGH_WIDTH_SHIFT);
+
+ if (mdp->data->bls_debug && !mdp->data->has_commit) {
+ /*
+ * For MT2701, disable double buffer before writing register
+ * and select manual mode and use PWM_PERIOD/PWM_HIGH_WIDTH.
+ */
+ mtk_disp_pwm_update_bits(mdp, mdp->data->bls_debug,
+ mdp->data->bls_debug_mask,
+ mdp->data->bls_debug_mask);
+ mtk_disp_pwm_update_bits(mdp, mdp->data->con0,
+ mdp->data->con0_sel,
+ mdp->data->con0_sel);
+ }
+
+ mtk_disp_pwm_update_bits(mdp, mdp->data->con0,
+ PWM_CLKDIV_MASK,
+ clk_div << PWM_CLKDIV_SHIFT);
+ mtk_disp_pwm_update_bits(mdp, mdp->data->con1,
+ PWM_PERIOD_MASK | PWM_HIGH_WIDTH_MASK,
+ value);
+
+ if (mdp->data->has_commit) {
+ mtk_disp_pwm_update_bits(mdp, mdp->data->commit,
+ mdp->data->commit_mask,
+ mdp->data->commit_mask);
+ mtk_disp_pwm_update_bits(mdp, mdp->data->commit,
+ mdp->data->commit_mask,
+ 0x0);
+ }
+
+ mtk_disp_pwm_update_bits(mdp, DISP_PWM_EN, mdp->data->enable_mask,
+ mdp->data->enable_mask);
+ mdp->enabled = true;
+
+ return 0;
+}
+
+static int mtk_disp_pwm_get_state(struct pwm_chip *chip,
+ struct pwm_device *pwm,
+ struct pwm_state *state)
+{
+ struct mtk_disp_pwm *mdp = to_mtk_disp_pwm(chip);
+ u64 rate, period, high_width;
+ u32 clk_div, pwm_en, con0, con1;
+ int err;
+
+ err = clk_prepare_enable(mdp->clk_main);
+ if (err < 0) {
+ dev_err(pwmchip_parent(chip), "Can't enable mdp->clk_main: %pe\n", ERR_PTR(err));
+ return err;
+ }
+
+ err = clk_prepare_enable(mdp->clk_mm);
+ if (err < 0) {
+ dev_err(pwmchip_parent(chip), "Can't enable mdp->clk_mm: %pe\n", ERR_PTR(err));
+ clk_disable_unprepare(mdp->clk_main);
+ return err;
+ }
+
+ /*
+ * Apply DISP_PWM_DEBUG settings to choose whether to enable or disable
+ * registers double buffer and manual commit to working register before
+ * performing any read/write operation
+ */
+ if (mdp->data->bls_debug)
+ mtk_disp_pwm_update_bits(mdp, mdp->data->bls_debug,
+ mdp->data->bls_debug_mask,
+ mdp->data->bls_debug_mask);
+
+ rate = clk_get_rate(mdp->clk_main);
+ con0 = readl(mdp->base + mdp->data->con0);
+ con1 = readl(mdp->base + mdp->data->con1);
+ pwm_en = readl(mdp->base + DISP_PWM_EN);
+ state->enabled = !!(pwm_en & mdp->data->enable_mask);
+ clk_div = FIELD_GET(PWM_CLKDIV_MASK, con0);
+ period = FIELD_GET(PWM_PERIOD_MASK, con1);
+ /*
+ * period has 12 bits, clk_div 11 and NSEC_PER_SEC has 30,
+ * so period * (clk_div + 1) * NSEC_PER_SEC doesn't overflow.
+ */
+ state->period = DIV64_U64_ROUND_UP(period * (clk_div + 1) * NSEC_PER_SEC, rate);
+ high_width = FIELD_GET(PWM_HIGH_WIDTH_MASK, con1);
+ state->duty_cycle = DIV64_U64_ROUND_UP(high_width * (clk_div + 1) * NSEC_PER_SEC,
+ rate);
+ state->polarity = PWM_POLARITY_NORMAL;
+ clk_disable_unprepare(mdp->clk_mm);
+ clk_disable_unprepare(mdp->clk_main);
+
+ return 0;
+}
+
+static const struct pwm_ops mtk_disp_pwm_ops = {
+ .apply = mtk_disp_pwm_apply,
+ .get_state = mtk_disp_pwm_get_state,
+};
+
+static int mtk_disp_pwm_probe(struct platform_device *pdev)
+{
+ struct pwm_chip *chip;
+ struct mtk_disp_pwm *mdp;
+ int ret;
+
+ chip = devm_pwmchip_alloc(&pdev->dev, 1, sizeof(*mdp));
+ if (IS_ERR(chip))
+ return PTR_ERR(chip);
+ mdp = to_mtk_disp_pwm(chip);
+
+ mdp->data = of_device_get_match_data(&pdev->dev);
+
+ mdp->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(mdp->base))
+ return PTR_ERR(mdp->base);
+
+ mdp->clk_main = devm_clk_get(&pdev->dev, "main");
+ if (IS_ERR(mdp->clk_main))
+ return dev_err_probe(&pdev->dev, PTR_ERR(mdp->clk_main),
+ "Failed to get main clock\n");
+
+ mdp->clk_mm = devm_clk_get(&pdev->dev, "mm");
+ if (IS_ERR(mdp->clk_mm))
+ return dev_err_probe(&pdev->dev, PTR_ERR(mdp->clk_mm),
+ "Failed to get mm clock\n");
+
+ chip->ops = &mtk_disp_pwm_ops;
+
+ ret = devm_pwmchip_add(&pdev->dev, chip);
+ if (ret < 0)
+ return dev_err_probe(&pdev->dev, ret, "pwmchip_add() failed\n");
+
+ return 0;
+}
+
+static const struct mtk_pwm_data mt2701_pwm_data = {
+ .enable_mask = BIT(16),
+ .con0 = 0xa8,
+ .con0_sel = 0x2,
+ .con1 = 0xac,
+ .has_commit = false,
+ .bls_debug = 0xb0,
+ .bls_debug_mask = 0x3,
+};
+
+static const struct mtk_pwm_data mt8173_pwm_data = {
+ .enable_mask = BIT(0),
+ .con0 = 0x10,
+ .con0_sel = 0x0,
+ .con1 = 0x14,
+ .has_commit = true,
+ .commit = 0x8,
+ .commit_mask = 0x1,
+};
+
+static const struct mtk_pwm_data mt8183_pwm_data = {
+ .enable_mask = BIT(0),
+ .con0 = 0x18,
+ .con0_sel = 0x0,
+ .con1 = 0x1c,
+ .has_commit = false,
+ .bls_debug = 0x80,
+ .bls_debug_mask = 0x3,
+};
+
+static const struct of_device_id mtk_disp_pwm_of_match[] = {
+ { .compatible = "mediatek,mt2701-disp-pwm", .data = &mt2701_pwm_data },
+ { .compatible = "mediatek,mt6595-disp-pwm", .data = &mt8173_pwm_data },
+ { .compatible = "mediatek,mt8173-disp-pwm", .data = &mt8173_pwm_data },
+ { .compatible = "mediatek,mt8183-disp-pwm", .data = &mt8183_pwm_data },
+ { }
+};
+MODULE_DEVICE_TABLE(of, mtk_disp_pwm_of_match);
+
+static struct platform_driver mtk_disp_pwm_driver = {
+ .driver = {
+ .name = "mediatek-disp-pwm",
+ .of_match_table = mtk_disp_pwm_of_match,
+ },
+ .probe = mtk_disp_pwm_probe,
+};
+module_platform_driver(mtk_disp_pwm_driver);
+
+MODULE_AUTHOR("YH Huang <yh.huang@mediatek.com>");
+MODULE_DESCRIPTION("MediaTek SoC display PWM driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/pwm/pwm-mxs.c b/drivers/pwm/pwm-mxs.c
new file mode 100644
index 000000000..e1a3df654
--- /dev/null
+++ b/drivers/pwm/pwm-mxs.c
@@ -0,0 +1,183 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2012 Freescale Semiconductor, Inc.
+ */
+
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+#include <linux/slab.h>
+#include <linux/stmp_device.h>
+
+#define SET 0x4
+#define CLR 0x8
+#define TOG 0xc
+
+#define PWM_CTRL 0x0
+#define PWM_ACTIVE0 0x10
+#define PWM_PERIOD0 0x20
+#define PERIOD_PERIOD(p) ((p) & 0xffff)
+#define PERIOD_PERIOD_MAX 0x10000
+#define PERIOD_ACTIVE_HIGH (3 << 16)
+#define PERIOD_ACTIVE_LOW (2 << 16)
+#define PERIOD_INACTIVE_HIGH (3 << 18)
+#define PERIOD_INACTIVE_LOW (2 << 18)
+#define PERIOD_POLARITY_NORMAL (PERIOD_ACTIVE_HIGH | PERIOD_INACTIVE_LOW)
+#define PERIOD_POLARITY_INVERSE (PERIOD_ACTIVE_LOW | PERIOD_INACTIVE_HIGH)
+#define PERIOD_CDIV(div) (((div) & 0x7) << 20)
+#define PERIOD_CDIV_MAX 8
+
+static const u8 cdiv_shift[PERIOD_CDIV_MAX] = {
+ 0, 1, 2, 3, 4, 6, 8, 10
+};
+
+struct mxs_pwm_chip {
+ struct clk *clk;
+ void __iomem *base;
+};
+
+static inline struct mxs_pwm_chip *to_mxs_pwm_chip(struct pwm_chip *chip)
+{
+ return pwmchip_get_drvdata(chip);
+}
+
+static int mxs_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
+ const struct pwm_state *state)
+{
+ struct mxs_pwm_chip *mxs = to_mxs_pwm_chip(chip);
+ int ret, div = 0;
+ unsigned int period_cycles, duty_cycles;
+ unsigned long rate;
+ unsigned long long c;
+ unsigned int pol_bits;
+
+ /*
+ * If the PWM channel is disabled, make sure to turn on the
+ * clock before calling clk_get_rate() and writing to the
+ * registers. Otherwise, just keep it enabled.
+ */
+ if (!pwm_is_enabled(pwm)) {
+ ret = clk_prepare_enable(mxs->clk);
+ if (ret)
+ return ret;
+ }
+
+ if (!state->enabled && pwm_is_enabled(pwm))
+ writel(1 << pwm->hwpwm, mxs->base + PWM_CTRL + CLR);
+
+ rate = clk_get_rate(mxs->clk);
+ while (1) {
+ c = rate >> cdiv_shift[div];
+ c = c * state->period;
+ do_div(c, 1000000000);
+ if (c < PERIOD_PERIOD_MAX)
+ break;
+ div++;
+ if (div >= PERIOD_CDIV_MAX)
+ return -EINVAL;
+ }
+
+ period_cycles = c;
+ c *= state->duty_cycle;
+ do_div(c, state->period);
+ duty_cycles = c;
+
+ /*
+ * The data sheet the says registers must be written to in
+ * this order (ACTIVEn, then PERIODn). Also, the new settings
+ * only take effect at the beginning of a new period, avoiding
+ * glitches.
+ */
+
+ pol_bits = state->polarity == PWM_POLARITY_NORMAL ?
+ PERIOD_POLARITY_NORMAL : PERIOD_POLARITY_INVERSE;
+ writel(duty_cycles << 16,
+ mxs->base + PWM_ACTIVE0 + pwm->hwpwm * 0x20);
+ writel(PERIOD_PERIOD(period_cycles) | pol_bits | PERIOD_CDIV(div),
+ mxs->base + PWM_PERIOD0 + pwm->hwpwm * 0x20);
+
+ if (state->enabled) {
+ if (!pwm_is_enabled(pwm)) {
+ /*
+ * The clock was enabled above. Just enable
+ * the channel in the control register.
+ */
+ writel(1 << pwm->hwpwm, mxs->base + PWM_CTRL + SET);
+ }
+ } else {
+ clk_disable_unprepare(mxs->clk);
+ }
+ return 0;
+}
+
+static const struct pwm_ops mxs_pwm_ops = {
+ .apply = mxs_pwm_apply,
+};
+
+static int mxs_pwm_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct pwm_chip *chip;
+ struct mxs_pwm_chip *mxs;
+ u32 npwm;
+ int ret;
+
+ ret = of_property_read_u32(np, "fsl,pwm-number", &npwm);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to get pwm number: %d\n", ret);
+ return ret;
+ }
+
+ chip = devm_pwmchip_alloc(&pdev->dev, npwm, sizeof(*mxs));
+ if (IS_ERR(chip))
+ return PTR_ERR(chip);
+ mxs = to_mxs_pwm_chip(chip);
+
+ mxs->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(mxs->base))
+ return PTR_ERR(mxs->base);
+
+ mxs->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(mxs->clk))
+ return PTR_ERR(mxs->clk);
+
+ chip->ops = &mxs_pwm_ops;
+
+ /* FIXME: Only do this if the PWM isn't already running */
+ ret = stmp_reset_block(mxs->base);
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret, "failed to reset PWM\n");
+
+ ret = devm_pwmchip_add(&pdev->dev, chip);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to add pwm chip %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct of_device_id mxs_pwm_dt_ids[] = {
+ { .compatible = "fsl,imx23-pwm" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, mxs_pwm_dt_ids);
+
+static struct platform_driver mxs_pwm_driver = {
+ .driver = {
+ .name = "mxs-pwm",
+ .of_match_table = mxs_pwm_dt_ids,
+ },
+ .probe = mxs_pwm_probe,
+};
+module_platform_driver(mxs_pwm_driver);
+
+MODULE_ALIAS("platform:mxs-pwm");
+MODULE_AUTHOR("Shawn Guo <shawn.guo@linaro.org>");
+MODULE_DESCRIPTION("Freescale MXS PWM Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/pwm/pwm-ntxec.c b/drivers/pwm/pwm-ntxec.c
new file mode 100644
index 000000000..28d1c2e5a
--- /dev/null
+++ b/drivers/pwm/pwm-ntxec.c
@@ -0,0 +1,165 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * The Netronix embedded controller is a microcontroller found in some
+ * e-book readers designed by the original design manufacturer Netronix, Inc.
+ * It contains RTC, battery monitoring, system power management, and PWM
+ * functionality.
+ *
+ * This driver implements PWM output.
+ *
+ * Copyright 2020 Jonathan Neuschäfer <j.neuschaefer@gmx.net>
+ *
+ * Limitations:
+ * - The get_state callback is not implemented, because the current state of
+ * the PWM output can't be read back from the hardware.
+ * - The hardware can only generate normal polarity output.
+ * - The period and duty cycle can't be changed together in one atomic action.
+ */
+
+#include <linux/mfd/ntxec.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+#include <linux/regmap.h>
+#include <linux/types.h>
+
+struct ntxec_pwm {
+ struct ntxec *ec;
+};
+
+static struct ntxec_pwm *ntxec_pwm_from_chip(struct pwm_chip *chip)
+{
+ return pwmchip_get_drvdata(chip);
+}
+
+#define NTXEC_REG_AUTO_OFF_HI 0xa1
+#define NTXEC_REG_AUTO_OFF_LO 0xa2
+#define NTXEC_REG_ENABLE 0xa3
+#define NTXEC_REG_PERIOD_LOW 0xa4
+#define NTXEC_REG_PERIOD_HIGH 0xa5
+#define NTXEC_REG_DUTY_LOW 0xa6
+#define NTXEC_REG_DUTY_HIGH 0xa7
+
+/*
+ * The time base used in the EC is 8MHz, or 125ns. Period and duty cycle are
+ * measured in this unit.
+ */
+#define TIME_BASE_NS 125
+
+/*
+ * The maximum input value (in nanoseconds) is determined by the time base and
+ * the range of the hardware registers that hold the converted value.
+ * It fits into 32 bits, so we can do our calculations in 32 bits as well.
+ */
+#define MAX_PERIOD_NS (TIME_BASE_NS * 0xffff)
+
+static int ntxec_pwm_set_raw_period_and_duty_cycle(struct pwm_chip *chip,
+ int period, int duty)
+{
+ struct ntxec_pwm *priv = ntxec_pwm_from_chip(chip);
+
+ /*
+ * Changes to the period and duty cycle take effect as soon as the
+ * corresponding low byte is written, so the hardware may be configured
+ * to an inconsistent state after the period is written and before the
+ * duty cycle is fully written. If, in such a case, the old duty cycle
+ * is longer than the new period, the EC may output 100% for a moment.
+ *
+ * To minimize the time between the changes to period and duty cycle
+ * taking effect, the writes are interleaved.
+ */
+
+ struct reg_sequence regs[] = {
+ { NTXEC_REG_PERIOD_HIGH, ntxec_reg8(period >> 8) },
+ { NTXEC_REG_DUTY_HIGH, ntxec_reg8(duty >> 8) },
+ { NTXEC_REG_PERIOD_LOW, ntxec_reg8(period) },
+ { NTXEC_REG_DUTY_LOW, ntxec_reg8(duty) },
+ };
+
+ return regmap_multi_reg_write(priv->ec->regmap, regs, ARRAY_SIZE(regs));
+}
+
+static int ntxec_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm_dev,
+ const struct pwm_state *state)
+{
+ struct ntxec_pwm *priv = ntxec_pwm_from_chip(chip);
+ unsigned int period, duty;
+ int res;
+
+ if (state->polarity != PWM_POLARITY_NORMAL)
+ return -EINVAL;
+
+ period = min_t(u64, state->period, MAX_PERIOD_NS);
+ duty = min_t(u64, state->duty_cycle, period);
+
+ period /= TIME_BASE_NS;
+ duty /= TIME_BASE_NS;
+
+ /*
+ * Writing a duty cycle of zero puts the device into a state where
+ * writing a higher duty cycle doesn't result in the brightness that it
+ * usually results in. This can be fixed by cycling the ENABLE register.
+ *
+ * As a workaround, write ENABLE=0 when the duty cycle is zero.
+ * The case that something has previously set the duty cycle to zero
+ * but ENABLE=1, is not handled.
+ */
+ if (state->enabled && duty != 0) {
+ res = ntxec_pwm_set_raw_period_and_duty_cycle(chip, period, duty);
+ if (res)
+ return res;
+
+ res = regmap_write(priv->ec->regmap, NTXEC_REG_ENABLE, ntxec_reg8(1));
+ if (res)
+ return res;
+
+ /* Disable the auto-off timer */
+ res = regmap_write(priv->ec->regmap, NTXEC_REG_AUTO_OFF_HI, ntxec_reg8(0xff));
+ if (res)
+ return res;
+
+ return regmap_write(priv->ec->regmap, NTXEC_REG_AUTO_OFF_LO, ntxec_reg8(0xff));
+ } else {
+ return regmap_write(priv->ec->regmap, NTXEC_REG_ENABLE, ntxec_reg8(0));
+ }
+}
+
+static const struct pwm_ops ntxec_pwm_ops = {
+ .apply = ntxec_pwm_apply,
+ /*
+ * No .get_state callback, because the current state cannot be read
+ * back from the hardware.
+ */
+};
+
+static int ntxec_pwm_probe(struct platform_device *pdev)
+{
+ struct ntxec *ec = dev_get_drvdata(pdev->dev.parent);
+ struct ntxec_pwm *priv;
+ struct pwm_chip *chip;
+
+ device_set_of_node_from_dev(&pdev->dev, pdev->dev.parent);
+
+ chip = devm_pwmchip_alloc(&pdev->dev, 1, sizeof(*priv));
+ if (IS_ERR(chip))
+ return PTR_ERR(chip);
+ priv = ntxec_pwm_from_chip(chip);
+
+ priv->ec = ec;
+ chip->ops = &ntxec_pwm_ops;
+
+ return devm_pwmchip_add(&pdev->dev, chip);
+}
+
+static struct platform_driver ntxec_pwm_driver = {
+ .driver = {
+ .name = "ntxec-pwm",
+ },
+ .probe = ntxec_pwm_probe,
+};
+module_platform_driver(ntxec_pwm_driver);
+
+MODULE_AUTHOR("Jonathan Neuschäfer <j.neuschaefer@gmx.net>");
+MODULE_DESCRIPTION("PWM driver for Netronix EC");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:ntxec-pwm");
diff --git a/drivers/pwm/pwm-omap-dmtimer.c b/drivers/pwm/pwm-omap-dmtimer.c
new file mode 100644
index 000000000..51830a872
--- /dev/null
+++ b/drivers/pwm/pwm-omap-dmtimer.c
@@ -0,0 +1,466 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2015 Neil Armstrong <narmstrong@baylibre.com>
+ * Copyright (c) 2014 Joachim Eastwood <manabian@gmail.com>
+ * Copyright (c) 2012 NeilBrown <neilb@suse.de>
+ * Heavily based on earlier code which is:
+ * Copyright (c) 2010 Grant Erickson <marathon96@gmail.com>
+ *
+ * Also based on pwm-samsung.c
+ *
+ * Description:
+ * This file is the core OMAP support for the generic, Linux
+ * PWM driver / controller, using the OMAP's dual-mode timers
+ * with a timer counter that goes up. When it overflows it gets
+ * reloaded with the load value and the pwm output goes up.
+ * When counter matches with match register, the output goes down.
+ * Reference Manual: https://www.ti.com/lit/ug/spruh73q/spruh73q.pdf
+ *
+ * Limitations:
+ * - When PWM is stopped, timer counter gets stopped immediately. This
+ * doesn't allow the current PWM period to complete and stops abruptly.
+ * - When PWM is running and changing both duty cycle and period,
+ * we cannot prevent in software that the output might produce
+ * a period with mixed settings. Especially when period/duty_cyle
+ * is updated while the pwm pin is high, current pwm period/duty_cycle
+ * can get updated as below based on the current timer counter:
+ * - period for current cycle = current_period + new period
+ * - duty_cycle for current period = current period + new duty_cycle.
+ * - PWM OMAP DM timer cannot change the polarity when pwm is active. When
+ * user requests a change in polarity when in active state:
+ * - PWM is stopped abruptly(without completing the current cycle)
+ * - Polarity is changed
+ * - A fresh cycle is started.
+ */
+
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <clocksource/timer-ti-dm.h>
+#include <linux/platform_data/dmtimer-omap.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/pwm.h>
+#include <linux/slab.h>
+#include <linux/time.h>
+
+#define DM_TIMER_LOAD_MIN 0xfffffffe
+#define DM_TIMER_MAX 0xffffffff
+
+/**
+ * struct pwm_omap_dmtimer_chip - Structure representing a pwm chip
+ * corresponding to omap dmtimer.
+ * @dm_timer: Pointer to omap dm timer.
+ * @pdata: Pointer to omap dm timer ops.
+ * @dm_timer_pdev: Pointer to omap dm timer platform device
+ */
+struct pwm_omap_dmtimer_chip {
+ /* Mutex to protect pwm apply state */
+ struct omap_dm_timer *dm_timer;
+ const struct omap_dm_timer_ops *pdata;
+ struct platform_device *dm_timer_pdev;
+};
+
+static inline struct pwm_omap_dmtimer_chip *
+to_pwm_omap_dmtimer_chip(struct pwm_chip *chip)
+{
+ return pwmchip_get_drvdata(chip);
+}
+
+/**
+ * pwm_omap_dmtimer_get_clock_cycles() - Get clock cycles in a time frame
+ * @clk_rate: pwm timer clock rate
+ * @ns: time frame in nano seconds.
+ *
+ * Return number of clock cycles in a given period(ins ns).
+ */
+static u32 pwm_omap_dmtimer_get_clock_cycles(unsigned long clk_rate, int ns)
+{
+ return DIV_ROUND_CLOSEST_ULL((u64)clk_rate * ns, NSEC_PER_SEC);
+}
+
+/**
+ * pwm_omap_dmtimer_start() - Start the pwm omap dm timer in pwm mode
+ * @omap: Pointer to pwm omap dm timer chip
+ */
+static void pwm_omap_dmtimer_start(struct pwm_omap_dmtimer_chip *omap)
+{
+ /*
+ * According to OMAP 4 TRM section 22.2.4.10 the counter should be
+ * started at 0xFFFFFFFE when overflow and match is used to ensure
+ * that the PWM line is toggled on the first event.
+ *
+ * Note that omap_dm_timer_enable/disable is for register access and
+ * not the timer counter itself.
+ */
+ omap->pdata->enable(omap->dm_timer);
+ omap->pdata->write_counter(omap->dm_timer, DM_TIMER_LOAD_MIN);
+ omap->pdata->disable(omap->dm_timer);
+
+ omap->pdata->start(omap->dm_timer);
+}
+
+/**
+ * pwm_omap_dmtimer_is_enabled() - Detect if the pwm is enabled.
+ * @omap: Pointer to pwm omap dm timer chip
+ *
+ * Return true if pwm is enabled else false.
+ */
+static bool pwm_omap_dmtimer_is_enabled(struct pwm_omap_dmtimer_chip *omap)
+{
+ u32 status;
+
+ status = omap->pdata->get_pwm_status(omap->dm_timer);
+
+ return !!(status & OMAP_TIMER_CTRL_ST);
+}
+
+/**
+ * pwm_omap_dmtimer_polarity() - Detect the polarity of pwm.
+ * @omap: Pointer to pwm omap dm timer chip
+ *
+ * Return the polarity of pwm.
+ */
+static int pwm_omap_dmtimer_polarity(struct pwm_omap_dmtimer_chip *omap)
+{
+ u32 status;
+
+ status = omap->pdata->get_pwm_status(omap->dm_timer);
+
+ return !!(status & OMAP_TIMER_CTRL_SCPWM);
+}
+
+/**
+ * pwm_omap_dmtimer_config() - Update the configuration of pwm omap dm timer
+ * @chip: Pointer to PWM controller
+ * @pwm: Pointer to PWM channel
+ * @duty_ns: New duty cycle in nano seconds
+ * @period_ns: New period in nano seconds
+ *
+ * Return 0 if successfully changed the period/duty_cycle else appropriate
+ * error.
+ */
+static int pwm_omap_dmtimer_config(struct pwm_chip *chip,
+ struct pwm_device *pwm,
+ int duty_ns, int period_ns)
+{
+ struct pwm_omap_dmtimer_chip *omap = to_pwm_omap_dmtimer_chip(chip);
+ u32 period_cycles, duty_cycles;
+ u32 load_value, match_value;
+ unsigned long clk_rate;
+ struct clk *fclk;
+
+ dev_dbg(pwmchip_parent(chip), "requested duty cycle: %d ns, period: %d ns\n",
+ duty_ns, period_ns);
+
+ if (duty_ns == pwm_get_duty_cycle(pwm) &&
+ period_ns == pwm_get_period(pwm))
+ return 0;
+
+ fclk = omap->pdata->get_fclk(omap->dm_timer);
+ if (!fclk) {
+ dev_err(pwmchip_parent(chip), "invalid pmtimer fclk\n");
+ return -EINVAL;
+ }
+
+ clk_rate = clk_get_rate(fclk);
+ if (!clk_rate) {
+ dev_err(pwmchip_parent(chip), "invalid pmtimer fclk rate\n");
+ return -EINVAL;
+ }
+
+ dev_dbg(pwmchip_parent(chip), "clk rate: %luHz\n", clk_rate);
+
+ /*
+ * Calculate the appropriate load and match values based on the
+ * specified period and duty cycle. The load value determines the
+ * period time and the match value determines the duty time.
+ *
+ * The period lasts for (DM_TIMER_MAX-load_value+1) clock cycles.
+ * Similarly, the active time lasts (match_value-load_value+1) cycles.
+ * The non-active time is the remainder: (DM_TIMER_MAX-match_value)
+ * clock cycles.
+ *
+ * NOTE: It is required that: load_value <= match_value < DM_TIMER_MAX
+ *
+ * References:
+ * OMAP4430/60/70 TRM sections 22.2.4.10 and 22.2.4.11
+ * AM335x Sitara TRM sections 20.1.3.5 and 20.1.3.6
+ */
+ period_cycles = pwm_omap_dmtimer_get_clock_cycles(clk_rate, period_ns);
+ duty_cycles = pwm_omap_dmtimer_get_clock_cycles(clk_rate, duty_ns);
+
+ if (period_cycles < 2) {
+ dev_info(pwmchip_parent(chip),
+ "period %d ns too short for clock rate %lu Hz\n",
+ period_ns, clk_rate);
+ return -EINVAL;
+ }
+
+ if (duty_cycles < 1) {
+ dev_dbg(pwmchip_parent(chip),
+ "duty cycle %d ns is too short for clock rate %lu Hz\n",
+ duty_ns, clk_rate);
+ dev_dbg(pwmchip_parent(chip), "using minimum of 1 clock cycle\n");
+ duty_cycles = 1;
+ } else if (duty_cycles >= period_cycles) {
+ dev_dbg(pwmchip_parent(chip),
+ "duty cycle %d ns is too long for period %d ns at clock rate %lu Hz\n",
+ duty_ns, period_ns, clk_rate);
+ dev_dbg(pwmchip_parent(chip), "using maximum of 1 clock cycle less than period\n");
+ duty_cycles = period_cycles - 1;
+ }
+
+ dev_dbg(pwmchip_parent(chip), "effective duty cycle: %lld ns, period: %lld ns\n",
+ DIV_ROUND_CLOSEST_ULL((u64)NSEC_PER_SEC * duty_cycles,
+ clk_rate),
+ DIV_ROUND_CLOSEST_ULL((u64)NSEC_PER_SEC * period_cycles,
+ clk_rate));
+
+ load_value = (DM_TIMER_MAX - period_cycles) + 1;
+ match_value = load_value + duty_cycles - 1;
+
+ omap->pdata->set_load(omap->dm_timer, load_value);
+ omap->pdata->set_match(omap->dm_timer, true, match_value);
+
+ dev_dbg(pwmchip_parent(chip), "load value: %#08x (%d), match value: %#08x (%d)\n",
+ load_value, load_value, match_value, match_value);
+
+ return 0;
+}
+
+/**
+ * pwm_omap_dmtimer_set_polarity() - Changes the polarity of the pwm dm timer.
+ * @chip: Pointer to PWM controller
+ * @pwm: Pointer to PWM channel
+ * @polarity: New pwm polarity to be set
+ */
+static void pwm_omap_dmtimer_set_polarity(struct pwm_chip *chip,
+ struct pwm_device *pwm,
+ enum pwm_polarity polarity)
+{
+ struct pwm_omap_dmtimer_chip *omap = to_pwm_omap_dmtimer_chip(chip);
+ bool enabled;
+
+ /* Disable the PWM before changing the polarity. */
+ enabled = pwm_omap_dmtimer_is_enabled(omap);
+ if (enabled)
+ omap->pdata->stop(omap->dm_timer);
+
+ omap->pdata->set_pwm(omap->dm_timer,
+ polarity == PWM_POLARITY_INVERSED,
+ true, OMAP_TIMER_TRIGGER_OVERFLOW_AND_COMPARE,
+ true);
+
+ if (enabled)
+ pwm_omap_dmtimer_start(omap);
+}
+
+/**
+ * pwm_omap_dmtimer_apply() - Changes the state of the pwm omap dm timer.
+ * @chip: Pointer to PWM controller
+ * @pwm: Pointer to PWM channel
+ * @state: New state to apply
+ *
+ * Return 0 if successfully changed the state else appropriate error.
+ */
+static int pwm_omap_dmtimer_apply(struct pwm_chip *chip,
+ struct pwm_device *pwm,
+ const struct pwm_state *state)
+{
+ struct pwm_omap_dmtimer_chip *omap = to_pwm_omap_dmtimer_chip(chip);
+ int ret;
+
+ if (pwm_omap_dmtimer_is_enabled(omap) && !state->enabled) {
+ omap->pdata->stop(omap->dm_timer);
+ return 0;
+ }
+
+ if (pwm_omap_dmtimer_polarity(omap) != state->polarity)
+ pwm_omap_dmtimer_set_polarity(chip, pwm, state->polarity);
+
+ ret = pwm_omap_dmtimer_config(chip, pwm, state->duty_cycle,
+ state->period);
+ if (ret)
+ return ret;
+
+ if (!pwm_omap_dmtimer_is_enabled(omap) && state->enabled) {
+ omap->pdata->set_pwm(omap->dm_timer,
+ state->polarity == PWM_POLARITY_INVERSED,
+ true,
+ OMAP_TIMER_TRIGGER_OVERFLOW_AND_COMPARE,
+ true);
+ pwm_omap_dmtimer_start(omap);
+ }
+
+ return 0;
+}
+
+static const struct pwm_ops pwm_omap_dmtimer_ops = {
+ .apply = pwm_omap_dmtimer_apply,
+};
+
+static int pwm_omap_dmtimer_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct dmtimer_platform_data *timer_pdata;
+ const struct omap_dm_timer_ops *pdata;
+ struct platform_device *timer_pdev;
+ struct pwm_chip *chip;
+ struct pwm_omap_dmtimer_chip *omap;
+ struct omap_dm_timer *dm_timer;
+ struct device_node *timer;
+ int ret = 0;
+ u32 v;
+
+ timer = of_parse_phandle(np, "ti,timers", 0);
+ if (!timer)
+ return -ENODEV;
+
+ timer_pdev = of_find_device_by_node(timer);
+ if (!timer_pdev) {
+ dev_err(&pdev->dev, "Unable to find Timer pdev\n");
+ ret = -ENODEV;
+ goto err_find_timer_pdev;
+ }
+
+ timer_pdata = dev_get_platdata(&timer_pdev->dev);
+ if (!timer_pdata) {
+ dev_dbg(&pdev->dev,
+ "dmtimer pdata structure NULL, deferring probe\n");
+ ret = -EPROBE_DEFER;
+ goto err_platdata;
+ }
+
+ pdata = timer_pdata->timer_ops;
+
+ if (!pdata || !pdata->request_by_node ||
+ !pdata->free ||
+ !pdata->enable ||
+ !pdata->disable ||
+ !pdata->get_fclk ||
+ !pdata->start ||
+ !pdata->stop ||
+ !pdata->set_load ||
+ !pdata->set_match ||
+ !pdata->set_pwm ||
+ !pdata->get_pwm_status ||
+ !pdata->set_prescaler ||
+ !pdata->write_counter) {
+ dev_err(&pdev->dev, "Incomplete dmtimer pdata structure\n");
+ ret = -EINVAL;
+ goto err_platdata;
+ }
+
+ if (!of_property_read_bool(timer, "ti,timer-pwm")) {
+ dev_err(&pdev->dev, "Missing ti,timer-pwm capability\n");
+ ret = -ENODEV;
+ goto err_timer_property;
+ }
+
+ dm_timer = pdata->request_by_node(timer);
+ if (!dm_timer) {
+ ret = -EPROBE_DEFER;
+ goto err_request_timer;
+ }
+
+ chip = devm_pwmchip_alloc(&pdev->dev, 1, sizeof(*omap));
+ if (IS_ERR(chip)) {
+ ret = PTR_ERR(chip);
+ goto err_alloc_omap;
+ }
+ omap = to_pwm_omap_dmtimer_chip(chip);
+
+ omap->pdata = pdata;
+ omap->dm_timer = dm_timer;
+ omap->dm_timer_pdev = timer_pdev;
+
+ /*
+ * Ensure that the timer is stopped before we allow PWM core to call
+ * pwm_enable.
+ */
+ if (pm_runtime_active(&omap->dm_timer_pdev->dev))
+ omap->pdata->stop(omap->dm_timer);
+
+ if (!of_property_read_u32(pdev->dev.of_node, "ti,prescaler", &v))
+ omap->pdata->set_prescaler(omap->dm_timer, v);
+
+ /* setup dmtimer clock source */
+ if (!of_property_read_u32(pdev->dev.of_node, "ti,clock-source", &v))
+ omap->pdata->set_source(omap->dm_timer, v);
+
+ chip->ops = &pwm_omap_dmtimer_ops;
+
+ ret = pwmchip_add(chip);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to register PWM\n");
+ goto err_pwmchip_add;
+ }
+
+ of_node_put(timer);
+
+ platform_set_drvdata(pdev, chip);
+
+ return 0;
+
+err_pwmchip_add:
+
+ /*
+ * *omap is allocated using devm_kzalloc,
+ * so no free necessary here
+ */
+err_alloc_omap:
+
+ pdata->free(dm_timer);
+err_request_timer:
+
+err_timer_property:
+err_platdata:
+
+ put_device(&timer_pdev->dev);
+err_find_timer_pdev:
+
+ of_node_put(timer);
+
+ return ret;
+}
+
+static void pwm_omap_dmtimer_remove(struct platform_device *pdev)
+{
+ struct pwm_chip *chip = platform_get_drvdata(pdev);
+ struct pwm_omap_dmtimer_chip *omap = to_pwm_omap_dmtimer_chip(chip);
+
+ pwmchip_remove(chip);
+
+ if (pm_runtime_active(&omap->dm_timer_pdev->dev))
+ omap->pdata->stop(omap->dm_timer);
+
+ omap->pdata->free(omap->dm_timer);
+
+ put_device(&omap->dm_timer_pdev->dev);
+}
+
+static const struct of_device_id pwm_omap_dmtimer_of_match[] = {
+ { .compatible = "ti,omap-dmtimer-pwm" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, pwm_omap_dmtimer_of_match);
+
+static struct platform_driver pwm_omap_dmtimer_driver = {
+ .driver = {
+ .name = "omap-dmtimer-pwm",
+ .of_match_table = pwm_omap_dmtimer_of_match,
+ },
+ .probe = pwm_omap_dmtimer_probe,
+ .remove = pwm_omap_dmtimer_remove,
+};
+module_platform_driver(pwm_omap_dmtimer_driver);
+
+MODULE_AUTHOR("Grant Erickson <marathon96@gmail.com>");
+MODULE_AUTHOR("NeilBrown <neilb@suse.de>");
+MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("OMAP PWM Driver using Dual-mode Timers");
diff --git a/drivers/pwm/pwm-pca9685.c b/drivers/pwm/pwm-pca9685.c
new file mode 100644
index 000000000..a2792b803
--- /dev/null
+++ b/drivers/pwm/pwm-pca9685.c
@@ -0,0 +1,579 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Driver for PCA9685 16-channel 12-bit PWM LED controller
+ *
+ * Copyright (C) 2013 Steffen Trumtrar <s.trumtrar@pengutronix.de>
+ * Copyright (C) 2015 Clemens Gruber <clemens.gruber@pqgruber.com>
+ *
+ * based on the pwm-twl-led.c driver
+ */
+
+#include <linux/gpio/driver.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <linux/pwm.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/pm_runtime.h>
+#include <linux/bitmap.h>
+
+/*
+ * Because the PCA9685 has only one prescaler per chip, only the first channel
+ * that is enabled is allowed to change the prescale register.
+ * PWM channels requested afterwards must use a period that results in the same
+ * prescale setting as the one set by the first requested channel.
+ */
+
+#define PCA9685_MODE1 0x00
+#define PCA9685_MODE2 0x01
+#define PCA9685_SUBADDR1 0x02
+#define PCA9685_SUBADDR2 0x03
+#define PCA9685_SUBADDR3 0x04
+#define PCA9685_ALLCALLADDR 0x05
+#define PCA9685_LEDX_ON_L 0x06
+#define PCA9685_LEDX_ON_H 0x07
+#define PCA9685_LEDX_OFF_L 0x08
+#define PCA9685_LEDX_OFF_H 0x09
+
+#define PCA9685_ALL_LED_ON_L 0xFA
+#define PCA9685_ALL_LED_ON_H 0xFB
+#define PCA9685_ALL_LED_OFF_L 0xFC
+#define PCA9685_ALL_LED_OFF_H 0xFD
+#define PCA9685_PRESCALE 0xFE
+
+#define PCA9685_PRESCALE_MIN 0x03 /* => max. frequency of 1526 Hz */
+#define PCA9685_PRESCALE_MAX 0xFF /* => min. frequency of 24 Hz */
+
+#define PCA9685_COUNTER_RANGE 4096
+#define PCA9685_OSC_CLOCK_HZ 25000000 /* Internal oscillator with 25 MHz */
+
+/*
+ * The time value of one counter tick. Note that NSEC_PER_SEC is an integer
+ * multiple of PCA9685_OSC_CLOCK_HZ, so there is no rounding involved and we're
+ * not loosing precision due to the early division.
+ */
+#define PCA9685_QUANTUM_NS(_prescale) ((NSEC_PER_SEC / PCA9685_OSC_CLOCK_HZ) * (_prescale + 1))
+
+#define PCA9685_NUMREGS 0xFF
+#define PCA9685_MAXCHAN 0x10
+
+#define LED_FULL BIT(4)
+#define MODE1_ALLCALL BIT(0)
+#define MODE1_SUB3 BIT(1)
+#define MODE1_SUB2 BIT(2)
+#define MODE1_SUB1 BIT(3)
+#define MODE1_SLEEP BIT(4)
+#define MODE1_AI BIT(5)
+
+#define MODE2_INVRT BIT(4)
+#define MODE2_OUTDRV BIT(2)
+
+#define LED_N_ON_H(N) (PCA9685_LEDX_ON_H + (4 * (N)))
+#define LED_N_ON_L(N) (PCA9685_LEDX_ON_L + (4 * (N)))
+#define LED_N_OFF_H(N) (PCA9685_LEDX_OFF_H + (4 * (N)))
+#define LED_N_OFF_L(N) (PCA9685_LEDX_OFF_L + (4 * (N)))
+
+#define REG_ON_H(C) ((C) >= PCA9685_MAXCHAN ? PCA9685_ALL_LED_ON_H : LED_N_ON_H((C)))
+#define REG_ON_L(C) ((C) >= PCA9685_MAXCHAN ? PCA9685_ALL_LED_ON_L : LED_N_ON_L((C)))
+#define REG_OFF_H(C) ((C) >= PCA9685_MAXCHAN ? PCA9685_ALL_LED_OFF_H : LED_N_OFF_H((C)))
+#define REG_OFF_L(C) ((C) >= PCA9685_MAXCHAN ? PCA9685_ALL_LED_OFF_L : LED_N_OFF_L((C)))
+
+struct pca9685 {
+ struct regmap *regmap;
+ struct mutex lock;
+ DECLARE_BITMAP(pwms_enabled, PCA9685_MAXCHAN + 1);
+};
+
+static inline struct pca9685 *to_pca(struct pwm_chip *chip)
+{
+ return pwmchip_get_drvdata(chip);
+}
+
+/* This function is supposed to be called with the lock mutex held */
+static bool pca9685_prescaler_can_change(struct pca9685 *pca, int channel)
+{
+ /* No PWM enabled: Change allowed */
+ if (bitmap_empty(pca->pwms_enabled, PCA9685_MAXCHAN + 1))
+ return true;
+ /* More than one PWM enabled: Change not allowed */
+ if (bitmap_weight(pca->pwms_enabled, PCA9685_MAXCHAN + 1) > 1)
+ return false;
+ /*
+ * Only one PWM enabled: Change allowed if the PWM about to
+ * be changed is the one that is already enabled
+ */
+ return test_bit(channel, pca->pwms_enabled);
+}
+
+static int pca9685_read_reg(struct pwm_chip *chip, unsigned int reg, unsigned int *val)
+{
+ struct pca9685 *pca = to_pca(chip);
+ struct device *dev = pwmchip_parent(chip);
+ int err;
+
+ err = regmap_read(pca->regmap, reg, val);
+ if (err)
+ dev_err(dev, "regmap_read of register 0x%x failed: %pe\n", reg, ERR_PTR(err));
+
+ return err;
+}
+
+static int pca9685_write_reg(struct pwm_chip *chip, unsigned int reg, unsigned int val)
+{
+ struct pca9685 *pca = to_pca(chip);
+ struct device *dev = pwmchip_parent(chip);
+ int err;
+
+ err = regmap_write(pca->regmap, reg, val);
+ if (err)
+ dev_err(dev, "regmap_write to register 0x%x failed: %pe\n", reg, ERR_PTR(err));
+
+ return err;
+}
+
+static int pca9685_write_4reg(struct pwm_chip *chip, unsigned int reg, u8 val[4])
+{
+ struct pca9685 *pca = to_pca(chip);
+ struct device *dev = pwmchip_parent(chip);
+ int err;
+
+ err = regmap_bulk_write(pca->regmap, reg, val, 4);
+ if (err)
+ dev_err(dev, "regmap_write to register 0x%x failed: %pe\n", reg, ERR_PTR(err));
+
+ return err;
+}
+
+static int pca9685_set_sleep_mode(struct pwm_chip *chip, bool enable)
+{
+ struct pca9685 *pca = to_pca(chip);
+ int err;
+
+ err = regmap_update_bits(pca->regmap, PCA9685_MODE1,
+ MODE1_SLEEP, enable ? MODE1_SLEEP : 0);
+ if (err)
+ return err;
+
+ if (!enable) {
+ /* Wait 500us for the oscillator to be back up */
+ udelay(500);
+ }
+
+ return 0;
+}
+
+struct pca9685_waveform {
+ u8 onoff[4];
+ u8 prescale;
+};
+
+static int pca9685_round_waveform_tohw(struct pwm_chip *chip, struct pwm_device *pwm, const struct pwm_waveform *wf, void *_wfhw)
+{
+ struct pca9685_waveform *wfhw = _wfhw;
+ struct pca9685 *pca = to_pca(chip);
+ unsigned int best_prescale;
+ u8 prescale;
+ unsigned int period_ns, duty;
+ int ret_tohw = 0;
+
+ if (!wf->period_length_ns) {
+ *wfhw = (typeof(*wfhw)){
+ .onoff = { 0, 0, 0, LED_FULL, },
+ .prescale = 0,
+ };
+
+ dev_dbg(&chip->dev, "pwm#%u: %lld/%lld [+%lld] -> [%hhx %hhx %hhx %hhx] PSC:%hhx\n",
+ pwm->hwpwm, wf->duty_length_ns, wf->period_length_ns, wf->duty_offset_ns,
+ wfhw->onoff[0], wfhw->onoff[1], wfhw->onoff[2], wfhw->onoff[3], wfhw->prescale);
+
+ return 0;
+ }
+
+ if (wf->period_length_ns >= PCA9685_COUNTER_RANGE * PCA9685_QUANTUM_NS(255)) {
+ best_prescale = 255;
+ } else if (wf->period_length_ns < PCA9685_COUNTER_RANGE * PCA9685_QUANTUM_NS(3)) {
+ best_prescale = 3;
+ ret_tohw = 1;
+ } else {
+ best_prescale = (unsigned int)wf->period_length_ns / (PCA9685_COUNTER_RANGE * (NSEC_PER_SEC / PCA9685_OSC_CLOCK_HZ)) - 1;
+ }
+
+ guard(mutex)(&pca->lock);
+
+ if (!pca9685_prescaler_can_change(pca, pwm->hwpwm)) {
+ unsigned int current_prescale;
+ int ret;
+
+ ret = regmap_read(pca->regmap, PCA9685_PRESCALE, &current_prescale);
+ if (ret)
+ return ret;
+
+ if (current_prescale > best_prescale)
+ ret_tohw = 1;
+
+ prescale = current_prescale;
+ } else {
+ prescale = best_prescale;
+ }
+
+ period_ns = PCA9685_COUNTER_RANGE * PCA9685_QUANTUM_NS(prescale);
+
+ duty = (unsigned)min_t(u64, wf->duty_length_ns, period_ns) / PCA9685_QUANTUM_NS(prescale);
+
+ if (duty < PCA9685_COUNTER_RANGE) {
+ unsigned int on, off;
+
+ on = (unsigned)min_t(u64, wf->duty_offset_ns, period_ns) / PCA9685_QUANTUM_NS(prescale);
+ off = (on + duty) % PCA9685_COUNTER_RANGE;
+
+ /*
+ * With a zero duty cycle, it doesn't matter if period was
+ * rounded up
+ */
+ if (!duty)
+ ret_tohw = 0;
+
+ *wfhw = (typeof(*wfhw)){
+ .onoff = { on & 0xff, (on >> 8) & 0xf, off & 0xff, (off >> 8) & 0xf },
+ .prescale = prescale,
+ };
+ } else {
+ *wfhw = (typeof(*wfhw)){
+ .onoff = { 0, LED_FULL, 0, 0, },
+ .prescale = prescale,
+ };
+ }
+
+ dev_dbg(&chip->dev, "pwm#%u: %lld/%lld [+%lld] -> %s[%hhx %hhx %hhx %hhx] PSC:%hhx\n",
+ pwm->hwpwm, wf->duty_length_ns, wf->period_length_ns, wf->duty_offset_ns,
+ ret_tohw ? "#" : "", wfhw->onoff[0], wfhw->onoff[1], wfhw->onoff[2], wfhw->onoff[3], wfhw->prescale);
+
+ return ret_tohw;
+}
+
+static int pca9685_round_waveform_fromhw(struct pwm_chip *chip, struct pwm_device *pwm,
+ const void *_wfhw, struct pwm_waveform *wf)
+{
+ const struct pca9685_waveform *wfhw = _wfhw;
+ struct pca9685 *pca = to_pca(chip);
+ unsigned int prescale;
+
+ if (wfhw->prescale)
+ prescale = wfhw->prescale;
+ else
+ scoped_guard(mutex, &pca->lock) {
+ int ret;
+
+ ret = regmap_read(pca->regmap, PCA9685_PRESCALE, &prescale);
+ if (ret)
+ return ret;
+ }
+
+ wf->period_length_ns = PCA9685_COUNTER_RANGE * PCA9685_QUANTUM_NS(prescale);
+
+ if (wfhw->onoff[3] & LED_FULL) {
+ wf->duty_length_ns = 0;
+ wf->duty_offset_ns = 0;
+ } else if (wfhw->onoff[1] & LED_FULL) {
+ wf->duty_length_ns = wf->period_length_ns;
+ wf->duty_offset_ns = 0;
+ } else {
+ unsigned int on = wfhw->onoff[0] | (wfhw->onoff[1] & 0xf) << 8;
+ unsigned int off = wfhw->onoff[2] | (wfhw->onoff[3] & 0xf) << 8;
+
+ wf->duty_length_ns = (off - on) % PCA9685_COUNTER_RANGE * PCA9685_QUANTUM_NS(prescale);
+ wf->duty_offset_ns = on * PCA9685_QUANTUM_NS(prescale);
+ }
+
+ dev_dbg(&chip->dev, "pwm#%u: [%hhx %hhx %hhx %hhx] PSC:%hhx -> %lld/%lld [+%lld]\n",
+ pwm->hwpwm,
+ wfhw->onoff[0], wfhw->onoff[1], wfhw->onoff[2], wfhw->onoff[3], wfhw->prescale,
+ wf->duty_length_ns, wf->period_length_ns, wf->duty_offset_ns);
+
+ return 0;
+}
+
+static int pca9685_read_waveform(struct pwm_chip *chip, struct pwm_device *pwm, void *_wfhw)
+{
+ struct pca9685_waveform *wfhw = _wfhw;
+ struct pca9685 *pca = to_pca(chip);
+ unsigned int prescale;
+ int ret;
+
+ guard(mutex)(&pca->lock);
+
+ ret = regmap_bulk_read(pca->regmap, REG_ON_L(pwm->hwpwm), &wfhw->onoff, 4);
+ if (ret)
+ return ret;
+
+ ret = regmap_read(pca->regmap, PCA9685_PRESCALE, &prescale);
+ if (ret)
+ return ret;
+
+ wfhw->prescale = prescale;
+
+ return 0;
+}
+
+static int pca9685_write_waveform(struct pwm_chip *chip, struct pwm_device *pwm, const void *_wfhw)
+{
+ const struct pca9685_waveform *wfhw = _wfhw;
+ struct pca9685 *pca = to_pca(chip);
+ unsigned int current_prescale;
+ int ret;
+
+ guard(mutex)(&pca->lock);
+
+ if (wfhw->prescale) {
+ ret = regmap_read(pca->regmap, PCA9685_PRESCALE, &current_prescale);
+ if (ret)
+ return ret;
+
+ if (current_prescale != wfhw->prescale) {
+ if (!pca9685_prescaler_can_change(pca, pwm->hwpwm))
+ return -EBUSY;
+
+ /* Put chip into sleep mode */
+ ret = pca9685_set_sleep_mode(chip, true);
+ if (ret)
+ return ret;
+
+ /* Change the chip-wide output frequency */
+ ret = regmap_write(pca->regmap, PCA9685_PRESCALE, wfhw->prescale);
+ if (ret)
+ return ret;
+
+ /* Wake the chip up */
+ ret = pca9685_set_sleep_mode(chip, false);
+ if (ret)
+ return ret;
+ }
+ }
+
+ return regmap_bulk_write(pca->regmap, REG_ON_L(pwm->hwpwm), &wfhw->onoff, 4);
+}
+
+static int pca9685_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct pca9685 *pca = to_pca(chip);
+
+ if (pwm->hwpwm < PCA9685_MAXCHAN) {
+ /* PWMs - except the "all LEDs" channel - default to enabled */
+ mutex_lock(&pca->lock);
+ set_bit(pwm->hwpwm, pca->pwms_enabled);
+ mutex_unlock(&pca->lock);
+ }
+
+ pm_runtime_get_sync(pwmchip_parent(chip));
+
+ return 0;
+}
+
+static void pca9685_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct pca9685 *pca = to_pca(chip);
+
+ mutex_lock(&pca->lock);
+ clear_bit(pwm->hwpwm, pca->pwms_enabled);
+ mutex_unlock(&pca->lock);
+
+ pm_runtime_put(pwmchip_parent(chip));
+}
+
+static const struct pwm_ops pca9685_pwm_ops = {
+ .sizeof_wfhw = sizeof(struct pca9685_waveform),
+ .round_waveform_tohw = pca9685_round_waveform_tohw,
+ .round_waveform_fromhw = pca9685_round_waveform_fromhw,
+ .read_waveform = pca9685_read_waveform,
+ .write_waveform = pca9685_write_waveform,
+ .request = pca9685_pwm_request,
+ .free = pca9685_pwm_free,
+};
+
+static bool pca9685_readable_reg(struct device *dev, unsigned int reg)
+{
+ /* The ALL_LED registers are readable but read as zero */
+ return reg <= REG_OFF_H(15) || reg >= PCA9685_PRESCALE;
+}
+
+static bool pca9685_writeable_reg(struct device *dev, unsigned int reg)
+{
+ return reg <= REG_OFF_H(15) || reg >= PCA9685_ALL_LED_ON_L;
+}
+
+static bool pca9685_volatile_reg(struct device *dev, unsigned int reg)
+{
+ /*
+ * Writing to an ALL_LED register affects all LEDi registers, so they
+ * are not cachable. :-\
+ */
+ return reg < PCA9685_PRESCALE;
+}
+
+static const struct regmap_config pca9685_regmap_i2c_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .readable_reg = pca9685_readable_reg,
+ .writeable_reg = pca9685_writeable_reg,
+ .volatile_reg = pca9685_volatile_reg,
+
+ .max_register = PCA9685_NUMREGS,
+ .cache_type = REGCACHE_MAPLE,
+};
+
+static int pca9685_pwm_probe(struct i2c_client *client)
+{
+ struct pwm_chip *chip;
+ struct pca9685 *pca;
+ unsigned int reg;
+ int ret;
+
+ /* Add an extra channel for ALL_LED */
+ chip = devm_pwmchip_alloc(&client->dev, PCA9685_MAXCHAN + 1, sizeof(*pca));
+ if (IS_ERR(chip))
+ return PTR_ERR(chip);
+ pca = to_pca(chip);
+
+ pca->regmap = devm_regmap_init_i2c(client, &pca9685_regmap_i2c_config);
+ if (IS_ERR(pca->regmap)) {
+ ret = PTR_ERR(pca->regmap);
+ dev_err(&client->dev, "Failed to initialize register map: %d\n",
+ ret);
+ return ret;
+ }
+
+ i2c_set_clientdata(client, chip);
+
+ mutex_init(&pca->lock);
+
+ /* clear MODE2_OCH */
+ reg = 0;
+
+ if (device_property_read_bool(&client->dev, "invert"))
+ reg |= MODE2_INVRT;
+ else
+ reg &= ~MODE2_INVRT;
+
+ if (device_property_read_bool(&client->dev, "open-drain"))
+ reg &= ~MODE2_OUTDRV;
+ else
+ reg |= MODE2_OUTDRV;
+
+ ret = pca9685_write_reg(chip, PCA9685_MODE2, reg);
+ if (ret)
+ return ret;
+
+ /*
+ * Disable all LED ALLCALL and SUBx addresses to avoid bus collisions,
+ * enable Auto-Increment.
+ */
+ pca9685_read_reg(chip, PCA9685_MODE1, &reg);
+ reg &= ~(MODE1_ALLCALL | MODE1_SUB1 | MODE1_SUB2 | MODE1_SUB3);
+ reg |= MODE1_AI;
+ pca9685_write_reg(chip, PCA9685_MODE1, reg);
+
+ /* Reset OFF/ON registers to POR default */
+ ret = pca9685_write_4reg(chip, PCA9685_ALL_LED_ON_L, (u8[]){ 0, LED_FULL, 0, LED_FULL });
+ if (ret < 0)
+ return dev_err_probe(&client->dev, ret, "Failed to reset ON/OFF registers\n");
+
+ chip->ops = &pca9685_pwm_ops;
+
+ ret = pwmchip_add(chip);
+ if (ret < 0)
+ return ret;
+
+ pm_runtime_enable(&client->dev);
+
+ if (pm_runtime_enabled(&client->dev)) {
+ /*
+ * Although the chip comes out of power-up in the sleep state,
+ * we force it to sleep in case it was woken up before
+ */
+ pca9685_set_sleep_mode(chip, true);
+ pm_runtime_set_suspended(&client->dev);
+ } else {
+ /* Wake the chip up if runtime PM is disabled */
+ pca9685_set_sleep_mode(chip, false);
+ }
+
+ return 0;
+}
+
+static void pca9685_pwm_remove(struct i2c_client *client)
+{
+ struct pwm_chip *chip = i2c_get_clientdata(client);
+
+ pwmchip_remove(chip);
+
+ if (!pm_runtime_enabled(&client->dev)) {
+ /* Put chip in sleep state if runtime PM is disabled */
+ pca9685_set_sleep_mode(chip, true);
+ }
+
+ pm_runtime_disable(&client->dev);
+}
+
+static int __maybe_unused pca9685_pwm_runtime_suspend(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct pwm_chip *chip = i2c_get_clientdata(client);
+
+ pca9685_set_sleep_mode(chip, true);
+ return 0;
+}
+
+static int __maybe_unused pca9685_pwm_runtime_resume(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct pwm_chip *chip = i2c_get_clientdata(client);
+
+ pca9685_set_sleep_mode(chip, false);
+ return 0;
+}
+
+static const struct i2c_device_id pca9685_id[] = {
+ { .name = "pca9685" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(i2c, pca9685_id);
+
+static const struct acpi_device_id pca9685_acpi_ids[] = {
+ { .id = "INT3492" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(acpi, pca9685_acpi_ids);
+
+static const struct of_device_id pca9685_dt_ids[] = {
+ { .compatible = "nxp,pca9685-pwm" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, pca9685_dt_ids);
+
+static const struct dev_pm_ops pca9685_pwm_pm = {
+ SET_RUNTIME_PM_OPS(pca9685_pwm_runtime_suspend,
+ pca9685_pwm_runtime_resume, NULL)
+};
+
+static struct i2c_driver pca9685_i2c_driver = {
+ .driver = {
+ .name = "pca9685-pwm",
+ .acpi_match_table = pca9685_acpi_ids,
+ .of_match_table = pca9685_dt_ids,
+ .pm = &pca9685_pwm_pm,
+ },
+ .probe = pca9685_pwm_probe,
+ .remove = pca9685_pwm_remove,
+ .id_table = pca9685_id,
+};
+
+module_i2c_driver(pca9685_i2c_driver);
+
+MODULE_AUTHOR("Steffen Trumtrar <s.trumtrar@pengutronix.de>");
+MODULE_DESCRIPTION("PWM driver for PCA9685");
+MODULE_LICENSE("GPL");
diff --git a/drivers/pwm/pwm-pxa.c b/drivers/pwm/pwm-pxa.c
new file mode 100644
index 000000000..5f06088f8
--- /dev/null
+++ b/drivers/pwm/pwm-pxa.c
@@ -0,0 +1,215 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * drivers/pwm/pwm-pxa.c
+ *
+ * simple driver for PWM (Pulse Width Modulator) controller
+ *
+ * 2008-02-13 initial version
+ * eric miao <eric.miao@marvell.com>
+ *
+ * Links to reference manuals for some of the supported PWM chips can be found
+ * in Documentation/arch/arm/marvell.rst.
+ *
+ * Limitations:
+ * - When PWM is stopped, the current PWM period stops abruptly at the next
+ * input clock (PWMCR_SD is set) and the output is driven to inactive.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/pwm.h>
+#include <linux/of.h>
+#include <linux/reset.h>
+
+#include <asm/div64.h>
+
+#define HAS_SECONDARY_PWM 0x10
+
+static const struct platform_device_id pwm_id_table[] = {
+ /* PWM has_secondary_pwm? */
+ { .name = "pxa25x-pwm", .driver_data = 0 },
+ { .name = "pxa27x-pwm", .driver_data = HAS_SECONDARY_PWM },
+ { .name = "pxa168-pwm", .driver_data = 0 },
+ { .name = "pxa910-pwm", .driver_data = 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(platform, pwm_id_table);
+
+/* PWM registers and bits definitions */
+#define PWMCR (0x00)
+#define PWMDCR (0x04)
+#define PWMPCR (0x08)
+
+#define PWMCR_SD (1 << 6)
+#define PWMDCR_FD (1 << 10)
+
+struct pxa_pwm_chip {
+ struct device *dev;
+
+ struct clk *clk;
+ void __iomem *mmio_base;
+};
+
+static inline struct pxa_pwm_chip *to_pxa_pwm_chip(struct pwm_chip *chip)
+{
+ return pwmchip_get_drvdata(chip);
+}
+
+/*
+ * period_ns = 10^9 * (PRESCALE + 1) * (PV + 1) / PWM_CLK_RATE
+ * duty_ns = 10^9 * (PRESCALE + 1) * DC / PWM_CLK_RATE
+ */
+static int pxa_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
+ u64 duty_ns, u64 period_ns)
+{
+ struct pxa_pwm_chip *pc = to_pxa_pwm_chip(chip);
+ unsigned long long c;
+ unsigned long period_cycles, prescale, pv, dc;
+ unsigned long offset;
+
+ offset = pwm->hwpwm ? 0x10 : 0;
+
+ c = clk_get_rate(pc->clk);
+ c = c * period_ns;
+ do_div(c, 1000000000);
+ period_cycles = c;
+
+ if (period_cycles < 1)
+ period_cycles = 1;
+ prescale = (period_cycles - 1) / 1024;
+ pv = period_cycles / (prescale + 1) - 1;
+
+ if (prescale > 63)
+ return -EINVAL;
+
+ if (duty_ns == period_ns)
+ dc = PWMDCR_FD;
+ else
+ dc = mul_u64_u64_div_u64(pv + 1, duty_ns, period_ns);
+
+ writel(prescale | PWMCR_SD, pc->mmio_base + offset + PWMCR);
+ writel(dc, pc->mmio_base + offset + PWMDCR);
+ writel(pv, pc->mmio_base + offset + PWMPCR);
+
+ return 0;
+}
+
+static int pxa_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
+ const struct pwm_state *state)
+{
+ struct pxa_pwm_chip *pc = to_pxa_pwm_chip(chip);
+ u64 duty_cycle;
+ int err;
+
+ if (state->polarity != PWM_POLARITY_NORMAL)
+ return -EINVAL;
+
+ err = clk_prepare_enable(pc->clk);
+ if (err)
+ return err;
+
+ duty_cycle = state->enabled ? state->duty_cycle : 0;
+
+ err = pxa_pwm_config(chip, pwm, duty_cycle, state->period);
+ if (err) {
+ clk_disable_unprepare(pc->clk);
+ return err;
+ }
+
+ if (state->enabled && !pwm->state.enabled)
+ return 0;
+
+ clk_disable_unprepare(pc->clk);
+
+ if (!state->enabled && pwm->state.enabled)
+ clk_disable_unprepare(pc->clk);
+
+ return 0;
+}
+
+static const struct pwm_ops pxa_pwm_ops = {
+ .apply = pxa_pwm_apply,
+};
+
+/*
+ * Device tree users must create one device instance for each PWM channel.
+ * Hence we dispense with the HAS_SECONDARY_PWM and "tell" the original driver
+ * code that this is a single channel pxa25x-pwm. Currently all devices are
+ * supported identically.
+ */
+static const struct of_device_id pwm_of_match[] = {
+ { .compatible = "marvell,pxa250-pwm", .data = &pwm_id_table[0] },
+ { .compatible = "marvell,pxa270-pwm", .data = &pwm_id_table[0] },
+ { .compatible = "marvell,pxa168-pwm", .data = &pwm_id_table[0] },
+ { .compatible = "marvell,pxa910-pwm", .data = &pwm_id_table[0] },
+ { }
+};
+MODULE_DEVICE_TABLE(of, pwm_of_match);
+
+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 clk *bus_clk;
+ struct device *dev = &pdev->dev;
+ struct reset_control *rst;
+ int ret = 0;
+
+ if (id == NULL)
+ id = of_device_get_match_data(dev);
+
+ if (id == NULL)
+ return -EINVAL;
+
+ 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);
+
+ bus_clk = devm_clk_get_optional_enabled(dev, "bus");
+ if (IS_ERR(bus_clk))
+ return dev_err_probe(dev, PTR_ERR(bus_clk), "Failed to get bus clock\n");
+
+ /* Get named func clk if bus clock is valid */
+ pc->clk = devm_clk_get(dev, bus_clk ? "func" : NULL);
+ if (IS_ERR(pc->clk))
+ return dev_err_probe(dev, PTR_ERR(pc->clk), "Failed to get clock\n");
+
+ rst = devm_reset_control_get_optional_exclusive_deasserted(dev, NULL);
+ if (IS_ERR(rst))
+ return PTR_ERR(rst);
+
+ chip->ops = &pxa_pwm_ops;
+ chip->of_xlate = of_pwm_single_xlate;
+
+ pc->mmio_base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(pc->mmio_base))
+ return PTR_ERR(pc->mmio_base);
+
+ ret = devm_pwmchip_add(dev, chip);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "pwmchip_add() failed\n");
+
+ return 0;
+}
+
+static struct platform_driver pwm_driver = {
+ .driver = {
+ .name = "pxa25x-pwm",
+ .of_match_table = pwm_of_match,
+ },
+ .probe = pwm_probe,
+ .id_table = pwm_id_table,
+};
+
+module_platform_driver(pwm_driver);
+
+MODULE_DESCRIPTION("PXA Pulse Width Modulator driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/pwm/pwm-raspberrypi-poe.c b/drivers/pwm/pwm-raspberrypi-poe.c
new file mode 100644
index 000000000..7cea6561c
--- /dev/null
+++ b/drivers/pwm/pwm-raspberrypi-poe.c
@@ -0,0 +1,196 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2021 Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
+ * For more information on Raspberry Pi's PoE hat see:
+ * https://www.raspberrypi.org/products/poe-hat/
+ *
+ * Limitations:
+ * - No disable bit, so a disabled PWM is simulated by duty_cycle 0
+ * - Only normal polarity
+ * - Fixed 12.5 kHz period
+ *
+ * The current period is completed when HW is reconfigured.
+ */
+
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+
+#include <soc/bcm2835/raspberrypi-firmware.h>
+#include <dt-bindings/pwm/raspberrypi,firmware-poe-pwm.h>
+
+#define RPI_PWM_MAX_DUTY 255
+#define RPI_PWM_PERIOD_NS 80000 /* 12.5 kHz */
+
+#define RPI_PWM_CUR_DUTY_REG 0x0
+
+struct raspberrypi_pwm {
+ struct rpi_firmware *firmware;
+ unsigned int duty_cycle;
+};
+
+struct raspberrypi_pwm_prop {
+ __le32 reg;
+ __le32 val;
+ __le32 ret;
+} __packed;
+
+static inline
+struct raspberrypi_pwm *raspberrypi_pwm_from_chip(struct pwm_chip *chip)
+{
+ return pwmchip_get_drvdata(chip);
+}
+
+static int raspberrypi_pwm_set_property(struct rpi_firmware *firmware,
+ u32 reg, u32 val)
+{
+ struct raspberrypi_pwm_prop msg = {
+ .reg = cpu_to_le32(reg),
+ .val = cpu_to_le32(val),
+ };
+ int ret;
+
+ ret = rpi_firmware_property(firmware, RPI_FIRMWARE_SET_POE_HAT_VAL,
+ &msg, sizeof(msg));
+ if (ret)
+ return ret;
+ if (msg.ret)
+ return -EIO;
+
+ return 0;
+}
+
+static int raspberrypi_pwm_get_property(struct rpi_firmware *firmware,
+ u32 reg, u32 *val)
+{
+ struct raspberrypi_pwm_prop msg = {
+ .reg = cpu_to_le32(reg),
+ };
+ int ret;
+
+ ret = rpi_firmware_property(firmware, RPI_FIRMWARE_GET_POE_HAT_VAL,
+ &msg, sizeof(msg));
+ if (ret)
+ return ret;
+ if (msg.ret)
+ return -EIO;
+
+ *val = le32_to_cpu(msg.val);
+
+ return 0;
+}
+
+static int raspberrypi_pwm_get_state(struct pwm_chip *chip,
+ struct pwm_device *pwm,
+ struct pwm_state *state)
+{
+ struct raspberrypi_pwm *rpipwm = raspberrypi_pwm_from_chip(chip);
+
+ state->period = RPI_PWM_PERIOD_NS;
+ state->duty_cycle = DIV_ROUND_UP(rpipwm->duty_cycle * RPI_PWM_PERIOD_NS,
+ RPI_PWM_MAX_DUTY);
+ state->enabled = !!(rpipwm->duty_cycle);
+ state->polarity = PWM_POLARITY_NORMAL;
+
+ return 0;
+}
+
+static int raspberrypi_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
+ const struct pwm_state *state)
+{
+ struct raspberrypi_pwm *rpipwm = raspberrypi_pwm_from_chip(chip);
+ unsigned int duty_cycle;
+ int ret;
+
+ if (state->period < RPI_PWM_PERIOD_NS ||
+ state->polarity != PWM_POLARITY_NORMAL)
+ return -EINVAL;
+
+ if (!state->enabled)
+ duty_cycle = 0;
+ else if (state->duty_cycle < RPI_PWM_PERIOD_NS)
+ duty_cycle = DIV_ROUND_DOWN_ULL(state->duty_cycle * RPI_PWM_MAX_DUTY,
+ RPI_PWM_PERIOD_NS);
+ else
+ duty_cycle = RPI_PWM_MAX_DUTY;
+
+ if (duty_cycle == rpipwm->duty_cycle)
+ return 0;
+
+ ret = raspberrypi_pwm_set_property(rpipwm->firmware, RPI_PWM_CUR_DUTY_REG,
+ duty_cycle);
+ if (ret) {
+ dev_err(pwmchip_parent(chip), "Failed to set duty cycle: %pe\n",
+ ERR_PTR(ret));
+ return ret;
+ }
+
+ rpipwm->duty_cycle = duty_cycle;
+
+ return 0;
+}
+
+static const struct pwm_ops raspberrypi_pwm_ops = {
+ .get_state = raspberrypi_pwm_get_state,
+ .apply = raspberrypi_pwm_apply,
+};
+
+static int raspberrypi_pwm_probe(struct platform_device *pdev)
+{
+ struct device_node *firmware_node;
+ struct device *dev = &pdev->dev;
+ struct rpi_firmware *firmware;
+ struct pwm_chip *chip;
+ struct raspberrypi_pwm *rpipwm;
+ int ret;
+
+ firmware_node = of_get_parent(dev->of_node);
+ if (!firmware_node) {
+ dev_err(dev, "Missing firmware node\n");
+ return -ENOENT;
+ }
+
+ firmware = devm_rpi_firmware_get(&pdev->dev, firmware_node);
+ of_node_put(firmware_node);
+ if (!firmware)
+ return dev_err_probe(dev, -EPROBE_DEFER,
+ "Failed to get firmware handle\n");
+
+ chip = devm_pwmchip_alloc(&pdev->dev, RASPBERRYPI_FIRMWARE_PWM_NUM,
+ sizeof(*rpipwm));
+ if (IS_ERR(chip))
+ return PTR_ERR(chip);
+ rpipwm = raspberrypi_pwm_from_chip(chip);
+
+ rpipwm->firmware = firmware;
+ chip->ops = &raspberrypi_pwm_ops;
+
+ ret = raspberrypi_pwm_get_property(rpipwm->firmware, RPI_PWM_CUR_DUTY_REG,
+ &rpipwm->duty_cycle);
+ if (ret) {
+ dev_err(dev, "Failed to get duty cycle: %pe\n", ERR_PTR(ret));
+ return ret;
+ }
+
+ return devm_pwmchip_add(dev, chip);
+}
+
+static const struct of_device_id raspberrypi_pwm_of_match[] = {
+ { .compatible = "raspberrypi,firmware-poe-pwm" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, raspberrypi_pwm_of_match);
+
+static struct platform_driver raspberrypi_pwm_driver = {
+ .driver = {
+ .name = "raspberrypi-poe-pwm",
+ .of_match_table = raspberrypi_pwm_of_match,
+ },
+ .probe = raspberrypi_pwm_probe,
+};
+module_platform_driver(raspberrypi_pwm_driver);
+
+MODULE_AUTHOR("Nicolas Saenz Julienne <nsaenzjulienne@suse.de>");
+MODULE_DESCRIPTION("Raspberry Pi Firmware Based PWM Bus Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/pwm/pwm-rcar.c b/drivers/pwm/pwm-rcar.c
new file mode 100644
index 000000000..1733cd700
--- /dev/null
+++ b/drivers/pwm/pwm-rcar.c
@@ -0,0 +1,269 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * R-Car PWM Timer driver
+ *
+ * Copyright (C) 2015 Renesas Electronics Corporation
+ *
+ * Limitations:
+ * - The hardware cannot generate a 0% duty cycle.
+ */
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/log2.h>
+#include <linux/math64.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/pwm.h>
+#include <linux/slab.h>
+
+#define RCAR_PWM_MAX_DIVISION 24
+#define RCAR_PWM_MAX_CYCLE 1023
+
+#define RCAR_PWMCR 0x00
+#define RCAR_PWMCR_CC0_MASK 0x000f0000
+#define RCAR_PWMCR_CC0_SHIFT 16
+#define RCAR_PWMCR_CCMD BIT(15)
+#define RCAR_PWMCR_SYNC BIT(11)
+#define RCAR_PWMCR_SS0 BIT(4)
+#define RCAR_PWMCR_EN0 BIT(0)
+
+#define RCAR_PWMCNT 0x04
+#define RCAR_PWMCNT_CYC0_MASK 0x03ff0000
+#define RCAR_PWMCNT_CYC0_SHIFT 16
+#define RCAR_PWMCNT_PH0_MASK 0x000003ff
+#define RCAR_PWMCNT_PH0_SHIFT 0
+
+struct rcar_pwm_chip {
+ void __iomem *base;
+ struct clk *clk;
+};
+
+static inline struct rcar_pwm_chip *to_rcar_pwm_chip(struct pwm_chip *chip)
+{
+ return pwmchip_get_drvdata(chip);
+}
+
+static void rcar_pwm_write(struct rcar_pwm_chip *rp, u32 data,
+ unsigned int offset)
+{
+ writel(data, rp->base + offset);
+}
+
+static u32 rcar_pwm_read(struct rcar_pwm_chip *rp, unsigned int offset)
+{
+ return readl(rp->base + offset);
+}
+
+static void rcar_pwm_update(struct rcar_pwm_chip *rp, u32 mask, u32 data,
+ unsigned int offset)
+{
+ u32 value;
+
+ value = rcar_pwm_read(rp, offset);
+ value &= ~mask;
+ value |= data & mask;
+ rcar_pwm_write(rp, value, offset);
+}
+
+static int rcar_pwm_get_clock_division(struct rcar_pwm_chip *rp, int period_ns)
+{
+ unsigned long clk_rate = clk_get_rate(rp->clk);
+ u64 div, tmp;
+
+ if (clk_rate == 0)
+ return -EINVAL;
+
+ div = (u64)NSEC_PER_SEC * RCAR_PWM_MAX_CYCLE;
+ tmp = (u64)period_ns * clk_rate + div - 1;
+ tmp = div64_u64(tmp, div);
+ div = ilog2(tmp - 1) + 1;
+
+ return (div <= RCAR_PWM_MAX_DIVISION) ? div : -ERANGE;
+}
+
+static void rcar_pwm_set_clock_control(struct rcar_pwm_chip *rp,
+ unsigned int div)
+{
+ u32 value;
+
+ value = rcar_pwm_read(rp, RCAR_PWMCR);
+ value &= ~(RCAR_PWMCR_CCMD | RCAR_PWMCR_CC0_MASK);
+
+ if (div & 1)
+ value |= RCAR_PWMCR_CCMD;
+
+ div >>= 1;
+
+ value |= div << RCAR_PWMCR_CC0_SHIFT;
+ rcar_pwm_write(rp, value, RCAR_PWMCR);
+}
+
+static int rcar_pwm_set_counter(struct rcar_pwm_chip *rp, int div, u64 duty_ns,
+ u64 period_ns)
+{
+ unsigned long long tmp;
+ unsigned long clk_rate = clk_get_rate(rp->clk);
+ u32 cyc, ph;
+
+ /* div <= 24 == RCAR_PWM_MAX_DIVISION, so the shift doesn't overflow. */
+ tmp = mul_u64_u64_div_u64(period_ns, clk_rate, (u64)NSEC_PER_SEC << div);
+ if (tmp > FIELD_MAX(RCAR_PWMCNT_CYC0_MASK))
+ tmp = FIELD_MAX(RCAR_PWMCNT_CYC0_MASK);
+
+ cyc = FIELD_PREP(RCAR_PWMCNT_CYC0_MASK, tmp);
+
+ tmp = mul_u64_u64_div_u64(duty_ns, clk_rate, (u64)NSEC_PER_SEC << div);
+ if (tmp > FIELD_MAX(RCAR_PWMCNT_PH0_MASK))
+ tmp = FIELD_MAX(RCAR_PWMCNT_PH0_MASK);
+ ph = FIELD_PREP(RCAR_PWMCNT_PH0_MASK, tmp);
+
+ /* Avoid prohibited setting */
+ if (cyc == 0 || ph == 0)
+ return -EINVAL;
+
+ rcar_pwm_write(rp, cyc | ph, RCAR_PWMCNT);
+
+ return 0;
+}
+
+static int rcar_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ return pm_runtime_get_sync(pwmchip_parent(chip));
+}
+
+static void rcar_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ pm_runtime_put(pwmchip_parent(chip));
+}
+
+static int rcar_pwm_enable(struct rcar_pwm_chip *rp)
+{
+ u32 value;
+
+ /* Don't enable the PWM device if CYC0 or PH0 is 0 */
+ value = rcar_pwm_read(rp, RCAR_PWMCNT);
+ if ((value & RCAR_PWMCNT_CYC0_MASK) == 0 ||
+ (value & RCAR_PWMCNT_PH0_MASK) == 0)
+ return -EINVAL;
+
+ rcar_pwm_update(rp, RCAR_PWMCR_EN0, RCAR_PWMCR_EN0, RCAR_PWMCR);
+
+ return 0;
+}
+
+static void rcar_pwm_disable(struct rcar_pwm_chip *rp)
+{
+ rcar_pwm_update(rp, RCAR_PWMCR_EN0, 0, RCAR_PWMCR);
+}
+
+static int rcar_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
+ const struct pwm_state *state)
+{
+ struct rcar_pwm_chip *rp = to_rcar_pwm_chip(chip);
+ int div, ret;
+
+ /* This HW/driver only supports normal polarity */
+ if (state->polarity != PWM_POLARITY_NORMAL)
+ return -EINVAL;
+
+ if (!state->enabled) {
+ rcar_pwm_disable(rp);
+ return 0;
+ }
+
+ div = rcar_pwm_get_clock_division(rp, state->period);
+ if (div < 0)
+ return div;
+
+ rcar_pwm_update(rp, RCAR_PWMCR_SYNC, RCAR_PWMCR_SYNC, RCAR_PWMCR);
+
+ ret = rcar_pwm_set_counter(rp, div, state->duty_cycle, state->period);
+ if (!ret)
+ rcar_pwm_set_clock_control(rp, div);
+
+ /* The SYNC should be set to 0 even if rcar_pwm_set_counter failed */
+ rcar_pwm_update(rp, RCAR_PWMCR_SYNC, 0, RCAR_PWMCR);
+
+ if (!ret)
+ ret = rcar_pwm_enable(rp);
+
+ return ret;
+}
+
+static const struct pwm_ops rcar_pwm_ops = {
+ .request = rcar_pwm_request,
+ .free = rcar_pwm_free,
+ .apply = rcar_pwm_apply,
+};
+
+static int rcar_pwm_probe(struct platform_device *pdev)
+{
+ struct pwm_chip *chip;
+ struct rcar_pwm_chip *rcar_pwm;
+ int ret;
+
+ chip = devm_pwmchip_alloc(&pdev->dev, 1, sizeof(*rcar_pwm));
+ if (IS_ERR(chip))
+ return PTR_ERR(chip);
+ rcar_pwm = to_rcar_pwm_chip(chip);
+
+ rcar_pwm->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(rcar_pwm->base))
+ return PTR_ERR(rcar_pwm->base);
+
+ rcar_pwm->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(rcar_pwm->clk)) {
+ dev_err(&pdev->dev, "cannot get clock\n");
+ return PTR_ERR(rcar_pwm->clk);
+ }
+
+ chip->ops = &rcar_pwm_ops;
+
+ platform_set_drvdata(pdev, chip);
+
+ pm_runtime_enable(&pdev->dev);
+
+ ret = pwmchip_add(chip);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to register PWM chip: %d\n", ret);
+ pm_runtime_disable(&pdev->dev);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void rcar_pwm_remove(struct platform_device *pdev)
+{
+ struct pwm_chip *chip = platform_get_drvdata(pdev);
+
+ pwmchip_remove(chip);
+
+ pm_runtime_disable(&pdev->dev);
+}
+
+static const struct of_device_id rcar_pwm_of_table[] = {
+ { .compatible = "renesas,pwm-rcar" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, rcar_pwm_of_table);
+
+static struct platform_driver rcar_pwm_driver = {
+ .probe = rcar_pwm_probe,
+ .remove = rcar_pwm_remove,
+ .driver = {
+ .name = "pwm-rcar",
+ .of_match_table = rcar_pwm_of_table,
+ }
+};
+module_platform_driver(rcar_pwm_driver);
+
+MODULE_AUTHOR("Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com>");
+MODULE_DESCRIPTION("Renesas PWM Timer Driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:pwm-rcar");
diff --git a/drivers/pwm/pwm-renesas-tpu.c b/drivers/pwm/pwm-renesas-tpu.c
new file mode 100644
index 000000000..140fd0bcb
--- /dev/null
+++ b/drivers/pwm/pwm-renesas-tpu.c
@@ -0,0 +1,505 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * R-Mobile TPU PWM driver
+ *
+ * Copyright (C) 2012 Renesas Solutions Corp.
+ */
+
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/pwm.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+
+#define TPU_CHANNEL_MAX 4
+
+#define TPU_TSTR 0x00 /* Timer start register (shared) */
+
+#define TPU_TCRn 0x00 /* Timer control register */
+#define TPU_TCR_CCLR_NONE (0 << 5)
+#define TPU_TCR_CCLR_TGRA (1 << 5)
+#define TPU_TCR_CCLR_TGRB (2 << 5)
+#define TPU_TCR_CCLR_TGRC (5 << 5)
+#define TPU_TCR_CCLR_TGRD (6 << 5)
+#define TPU_TCR_CKEG_RISING (0 << 3)
+#define TPU_TCR_CKEG_FALLING (1 << 3)
+#define TPU_TCR_CKEG_BOTH (2 << 3)
+#define TPU_TMDRn 0x04 /* Timer mode register */
+#define TPU_TMDR_BFWT (1 << 6)
+#define TPU_TMDR_BFB (1 << 5)
+#define TPU_TMDR_BFA (1 << 4)
+#define TPU_TMDR_MD_NORMAL (0 << 0)
+#define TPU_TMDR_MD_PWM (2 << 0)
+#define TPU_TIORn 0x08 /* Timer I/O control register */
+#define TPU_TIOR_IOA_0 (0 << 0)
+#define TPU_TIOR_IOA_0_CLR (1 << 0)
+#define TPU_TIOR_IOA_0_SET (2 << 0)
+#define TPU_TIOR_IOA_0_TOGGLE (3 << 0)
+#define TPU_TIOR_IOA_1 (4 << 0)
+#define TPU_TIOR_IOA_1_CLR (5 << 0)
+#define TPU_TIOR_IOA_1_SET (6 << 0)
+#define TPU_TIOR_IOA_1_TOGGLE (7 << 0)
+#define TPU_TIERn 0x0c /* Timer interrupt enable register */
+#define TPU_TSRn 0x10 /* Timer status register */
+#define TPU_TCNTn 0x14 /* Timer counter */
+#define TPU_TGRAn 0x18 /* Timer general register A */
+#define TPU_TGRBn 0x1c /* Timer general register B */
+#define TPU_TGRCn 0x20 /* Timer general register C */
+#define TPU_TGRDn 0x24 /* Timer general register D */
+
+#define TPU_CHANNEL_OFFSET 0x10
+#define TPU_CHANNEL_SIZE 0x40
+
+enum tpu_pin_state {
+ TPU_PIN_INACTIVE, /* Pin is driven inactive */
+ TPU_PIN_PWM, /* Pin is driven by PWM */
+ TPU_PIN_ACTIVE, /* Pin is driven active */
+};
+
+struct tpu_device;
+
+struct tpu_pwm_device {
+ bool timer_on; /* Whether the timer is running */
+
+ struct tpu_device *tpu;
+ unsigned int channel; /* Channel number in the TPU */
+
+ enum pwm_polarity polarity;
+ unsigned int prescaler;
+ u16 period;
+ u16 duty;
+};
+
+struct tpu_device {
+ struct platform_device *pdev;
+ spinlock_t lock;
+
+ void __iomem *base;
+ struct clk *clk;
+ struct tpu_pwm_device tpd[TPU_CHANNEL_MAX];
+};
+
+static inline struct tpu_device *to_tpu_device(struct pwm_chip *chip)
+{
+ return pwmchip_get_drvdata(chip);
+}
+
+static void tpu_pwm_write(struct tpu_pwm_device *tpd, int reg_nr, u16 value)
+{
+ void __iomem *base = tpd->tpu->base + TPU_CHANNEL_OFFSET
+ + tpd->channel * TPU_CHANNEL_SIZE;
+
+ iowrite16(value, base + reg_nr);
+}
+
+static void tpu_pwm_set_pin(struct tpu_pwm_device *tpd,
+ enum tpu_pin_state state)
+{
+ static const char * const states[] = { "inactive", "PWM", "active" };
+
+ dev_dbg(&tpd->tpu->pdev->dev, "%u: configuring pin as %s\n",
+ tpd->channel, states[state]);
+
+ switch (state) {
+ case TPU_PIN_INACTIVE:
+ tpu_pwm_write(tpd, TPU_TIORn,
+ tpd->polarity == PWM_POLARITY_INVERSED ?
+ TPU_TIOR_IOA_1 : TPU_TIOR_IOA_0);
+ break;
+ case TPU_PIN_PWM:
+ tpu_pwm_write(tpd, TPU_TIORn,
+ tpd->polarity == PWM_POLARITY_INVERSED ?
+ TPU_TIOR_IOA_0_SET : TPU_TIOR_IOA_1_CLR);
+ break;
+ case TPU_PIN_ACTIVE:
+ tpu_pwm_write(tpd, TPU_TIORn,
+ tpd->polarity == PWM_POLARITY_INVERSED ?
+ TPU_TIOR_IOA_0 : TPU_TIOR_IOA_1);
+ break;
+ }
+}
+
+static void tpu_pwm_start_stop(struct tpu_pwm_device *tpd, int start)
+{
+ unsigned long flags;
+ u16 value;
+
+ spin_lock_irqsave(&tpd->tpu->lock, flags);
+ value = ioread16(tpd->tpu->base + TPU_TSTR);
+
+ if (start)
+ value |= 1 << tpd->channel;
+ else
+ value &= ~(1 << tpd->channel);
+
+ iowrite16(value, tpd->tpu->base + TPU_TSTR);
+ spin_unlock_irqrestore(&tpd->tpu->lock, flags);
+}
+
+static int tpu_pwm_timer_start(struct tpu_pwm_device *tpd)
+{
+ int ret;
+
+ if (!tpd->timer_on) {
+ /* Wake up device and enable clock. */
+ pm_runtime_get_sync(&tpd->tpu->pdev->dev);
+ ret = clk_prepare_enable(tpd->tpu->clk);
+ if (ret) {
+ dev_err(&tpd->tpu->pdev->dev, "cannot enable clock\n");
+ return ret;
+ }
+ tpd->timer_on = true;
+ }
+
+ /*
+ * Make sure the channel is stopped, as we need to reconfigure it
+ * completely. First drive the pin to the inactive state to avoid
+ * glitches.
+ */
+ tpu_pwm_set_pin(tpd, TPU_PIN_INACTIVE);
+ tpu_pwm_start_stop(tpd, false);
+
+ /*
+ * - Clear TCNT on TGRB match
+ * - Count on rising edge
+ * - Set prescaler
+ * - Output 0 until TGRA, output 1 until TGRB (active low polarity)
+ * - Output 1 until TGRA, output 0 until TGRB (active high polarity
+ * - PWM mode
+ */
+ tpu_pwm_write(tpd, TPU_TCRn, TPU_TCR_CCLR_TGRB | TPU_TCR_CKEG_RISING |
+ tpd->prescaler);
+ tpu_pwm_write(tpd, TPU_TMDRn, TPU_TMDR_MD_PWM);
+ tpu_pwm_set_pin(tpd, TPU_PIN_PWM);
+ tpu_pwm_write(tpd, TPU_TGRAn, tpd->duty);
+ tpu_pwm_write(tpd, TPU_TGRBn, tpd->period);
+
+ dev_dbg(&tpd->tpu->pdev->dev, "%u: TGRA 0x%04x TGRB 0x%04x\n",
+ tpd->channel, tpd->duty, tpd->period);
+
+ /* Start the channel. */
+ tpu_pwm_start_stop(tpd, true);
+
+ return 0;
+}
+
+static void tpu_pwm_timer_stop(struct tpu_pwm_device *tpd)
+{
+ if (!tpd->timer_on)
+ return;
+
+ /* Disable channel. */
+ tpu_pwm_start_stop(tpd, false);
+
+ /* Stop clock and mark device as idle. */
+ clk_disable_unprepare(tpd->tpu->clk);
+ pm_runtime_put(&tpd->tpu->pdev->dev);
+
+ tpd->timer_on = false;
+}
+
+/* -----------------------------------------------------------------------------
+ * PWM API
+ */
+
+static int tpu_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct tpu_device *tpu = to_tpu_device(chip);
+ struct tpu_pwm_device *tpd;
+
+ if (pwm->hwpwm >= TPU_CHANNEL_MAX)
+ return -EINVAL;
+
+ tpd = &tpu->tpd[pwm->hwpwm];
+
+ tpd->tpu = tpu;
+ tpd->channel = pwm->hwpwm;
+ tpd->polarity = PWM_POLARITY_NORMAL;
+ tpd->prescaler = 0;
+ tpd->period = 0;
+ tpd->duty = 0;
+
+ tpd->timer_on = false;
+
+ return 0;
+}
+
+static void tpu_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct tpu_device *tpu = to_tpu_device(chip);
+ struct tpu_pwm_device *tpd = &tpu->tpd[pwm->hwpwm];
+
+ tpu_pwm_timer_stop(tpd);
+}
+
+static int tpu_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
+ u64 duty_ns, u64 period_ns, bool enabled)
+{
+ struct tpu_device *tpu = to_tpu_device(chip);
+ struct tpu_pwm_device *tpd = &tpu->tpd[pwm->hwpwm];
+ unsigned int prescaler;
+ bool duty_only = false;
+ u32 clk_rate;
+ u64 period;
+ u32 duty;
+ int ret;
+
+ clk_rate = clk_get_rate(tpu->clk);
+ if (unlikely(clk_rate > NSEC_PER_SEC)) {
+ /*
+ * This won't happen in the nearer future, so this is only a
+ * safeguard to prevent the following calculation from
+ * overflowing. With this clk_rate * period_ns / NSEC_PER_SEC is
+ * not greater than period_ns and so fits into an u64.
+ */
+ return -EINVAL;
+ }
+
+ period = mul_u64_u64_div_u64(clk_rate, period_ns, NSEC_PER_SEC);
+
+ /*
+ * Find the minimal prescaler in [0..3] such that
+ *
+ * period >> (2 * prescaler) < 0x10000
+ *
+ * This could be calculated using something like:
+ *
+ * prescaler = max(ilog2(period) / 2, 7) - 7;
+ *
+ * but given there are only four allowed results and that ilog2 isn't
+ * cheap on all platforms using a switch statement is more effective.
+ */
+ switch (period) {
+ case 1 ... 0xffff:
+ prescaler = 0;
+ break;
+
+ case 0x10000 ... 0x3ffff:
+ prescaler = 1;
+ break;
+
+ case 0x40000 ... 0xfffff:
+ prescaler = 2;
+ break;
+
+ case 0x100000 ... 0x3fffff:
+ prescaler = 3;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ period >>= 2 * prescaler;
+
+ if (duty_ns)
+ duty = mul_u64_u64_div_u64(clk_rate, duty_ns,
+ (u64)NSEC_PER_SEC << (2 * prescaler));
+ else
+ duty = 0;
+
+ dev_dbg(&tpu->pdev->dev,
+ "rate %u, prescaler %u, period %u, duty %u\n",
+ clk_rate, 1 << (2 * prescaler), (u32)period, duty);
+
+ if (tpd->prescaler == prescaler && tpd->period == period)
+ duty_only = true;
+
+ tpd->prescaler = prescaler;
+ tpd->period = period;
+ tpd->duty = duty;
+
+ /* If the channel is disabled we're done. */
+ if (!enabled)
+ return 0;
+
+ if (duty_only && tpd->timer_on) {
+ /*
+ * If only the duty cycle changed and the timer is already
+ * running, there's no need to reconfigure it completely, Just
+ * modify the duty cycle.
+ */
+ tpu_pwm_write(tpd, TPU_TGRAn, tpd->duty);
+ dev_dbg(&tpu->pdev->dev, "%u: TGRA 0x%04x\n", tpd->channel,
+ tpd->duty);
+ } else {
+ /* Otherwise perform a full reconfiguration. */
+ ret = tpu_pwm_timer_start(tpd);
+ if (ret < 0)
+ return ret;
+ }
+
+ if (duty == 0 || duty == period) {
+ /*
+ * To avoid running the timer when not strictly required, handle
+ * 0% and 100% duty cycles as fixed levels and stop the timer.
+ */
+ tpu_pwm_set_pin(tpd, duty ? TPU_PIN_ACTIVE : TPU_PIN_INACTIVE);
+ tpu_pwm_timer_stop(tpd);
+ }
+
+ return 0;
+}
+
+static int tpu_pwm_set_polarity(struct pwm_chip *chip, struct pwm_device *pwm,
+ enum pwm_polarity polarity)
+{
+ struct tpu_device *tpu = to_tpu_device(chip);
+ struct tpu_pwm_device *tpd = &tpu->tpd[pwm->hwpwm];
+
+ tpd->polarity = polarity;
+
+ return 0;
+}
+
+static int tpu_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct tpu_device *tpu = to_tpu_device(chip);
+ struct tpu_pwm_device *tpd = &tpu->tpd[pwm->hwpwm];
+ int ret;
+
+ ret = tpu_pwm_timer_start(tpd);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * To avoid running the timer when not strictly required, handle 0% and
+ * 100% duty cycles as fixed levels and stop the timer.
+ */
+ if (tpd->duty == 0 || tpd->duty == tpd->period) {
+ tpu_pwm_set_pin(tpd, tpd->duty ?
+ TPU_PIN_ACTIVE : TPU_PIN_INACTIVE);
+ tpu_pwm_timer_stop(tpd);
+ }
+
+ return 0;
+}
+
+static void tpu_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct tpu_device *tpu = to_tpu_device(chip);
+ struct tpu_pwm_device *tpd = &tpu->tpd[pwm->hwpwm];
+
+ /* The timer must be running to modify the pin output configuration. */
+ tpu_pwm_timer_start(tpd);
+ tpu_pwm_set_pin(tpd, TPU_PIN_INACTIVE);
+ tpu_pwm_timer_stop(tpd);
+}
+
+static int tpu_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
+ const struct pwm_state *state)
+{
+ int err;
+ bool enabled = pwm->state.enabled;
+
+ if (state->polarity != pwm->state.polarity) {
+ if (enabled) {
+ tpu_pwm_disable(chip, pwm);
+ enabled = false;
+ }
+
+ err = tpu_pwm_set_polarity(chip, pwm, state->polarity);
+ if (err)
+ return err;
+ }
+
+ if (!state->enabled) {
+ if (enabled)
+ tpu_pwm_disable(chip, pwm);
+
+ return 0;
+ }
+
+ err = tpu_pwm_config(chip, pwm,
+ state->duty_cycle, state->period, enabled);
+ if (err)
+ return err;
+
+ if (!enabled)
+ err = tpu_pwm_enable(chip, pwm);
+
+ return err;
+}
+
+static const struct pwm_ops tpu_pwm_ops = {
+ .request = tpu_pwm_request,
+ .free = tpu_pwm_free,
+ .apply = tpu_pwm_apply,
+};
+
+/* -----------------------------------------------------------------------------
+ * Probe and remove
+ */
+
+static int tpu_probe(struct platform_device *pdev)
+{
+ struct pwm_chip *chip;
+ struct tpu_device *tpu;
+ int ret;
+
+ chip = devm_pwmchip_alloc(&pdev->dev, TPU_CHANNEL_MAX, sizeof(*tpu));
+ if (IS_ERR(chip))
+ return PTR_ERR(chip);
+ tpu = to_tpu_device(chip);
+
+ spin_lock_init(&tpu->lock);
+ tpu->pdev = pdev;
+
+ /* Map memory, get clock and pin control. */
+ tpu->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(tpu->base))
+ return PTR_ERR(tpu->base);
+
+ tpu->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(tpu->clk))
+ return dev_err_probe(&pdev->dev, PTR_ERR(tpu->clk), "Failed to get clock\n");
+
+ /* Initialize and register the device. */
+ platform_set_drvdata(pdev, tpu);
+
+ chip->ops = &tpu_pwm_ops;
+
+ ret = devm_pm_runtime_enable(&pdev->dev);
+ if (ret < 0)
+ return dev_err_probe(&pdev->dev, ret, "Failed to enable runtime PM\n");
+
+ ret = devm_pwmchip_add(&pdev->dev, chip);
+ if (ret < 0)
+ return dev_err_probe(&pdev->dev, ret, "Failed to register PWM chip\n");
+
+ return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id tpu_of_table[] = {
+ { .compatible = "renesas,tpu-r8a73a4" },
+ { .compatible = "renesas,tpu-r8a7740" },
+ { .compatible = "renesas,tpu-r8a7790" },
+ { .compatible = "renesas,tpu" },
+ { }
+};
+
+MODULE_DEVICE_TABLE(of, tpu_of_table);
+#endif
+
+static struct platform_driver tpu_driver = {
+ .probe = tpu_probe,
+ .driver = {
+ .name = "renesas-tpu-pwm",
+ .of_match_table = of_match_ptr(tpu_of_table),
+ }
+};
+
+module_platform_driver(tpu_driver);
+
+MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
+MODULE_DESCRIPTION("Renesas TPU PWM Driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:renesas-tpu-pwm");
diff --git a/drivers/pwm/pwm-rockchip.c b/drivers/pwm/pwm-rockchip.c
new file mode 100644
index 000000000..050ca8aad
--- /dev/null
+++ b/drivers/pwm/pwm-rockchip.c
@@ -0,0 +1,402 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * PWM driver for Rockchip SoCs
+ *
+ * Copyright (C) 2014 Beniamino Galvani <b.galvani@gmail.com>
+ * Copyright (C) 2014 ROCKCHIP, Inc.
+ */
+
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/limits.h>
+#include <linux/math64.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <linux/pwm.h>
+#include <linux/time.h>
+
+#define PWM_CTRL_TIMER_EN (1 << 0)
+#define PWM_CTRL_OUTPUT_EN (1 << 3)
+
+#define PWM_ENABLE (1 << 0)
+#define PWM_CONTINUOUS (1 << 1)
+#define PWM_DUTY_POSITIVE (1 << 3)
+#define PWM_DUTY_NEGATIVE (0 << 3)
+#define PWM_INACTIVE_NEGATIVE (0 << 4)
+#define PWM_INACTIVE_POSITIVE (1 << 4)
+#define PWM_POLARITY_MASK (PWM_DUTY_POSITIVE | PWM_INACTIVE_POSITIVE)
+#define PWM_OUTPUT_LEFT (0 << 5)
+#define PWM_LOCK_EN (1 << 6)
+#define PWM_LP_DISABLE (0 << 8)
+
+struct rockchip_pwm_chip {
+ struct clk *clk;
+ struct clk *pclk;
+ const struct rockchip_pwm_data *data;
+ void __iomem *base;
+};
+
+struct rockchip_pwm_regs {
+ unsigned long duty;
+ unsigned long period;
+ unsigned long cntr;
+ unsigned long ctrl;
+};
+
+struct rockchip_pwm_data {
+ struct rockchip_pwm_regs regs;
+ unsigned int prescaler;
+ bool supports_polarity;
+ bool supports_lock;
+ u32 enable_conf;
+};
+
+static inline struct rockchip_pwm_chip *to_rockchip_pwm_chip(struct pwm_chip *chip)
+{
+ return pwmchip_get_drvdata(chip);
+}
+
+static int rockchip_pwm_get_state(struct pwm_chip *chip,
+ struct pwm_device *pwm,
+ struct pwm_state *state)
+{
+ struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip);
+ u64 prescaled_ns = (u64)pc->data->prescaler * NSEC_PER_SEC;
+ u32 enable_conf = pc->data->enable_conf;
+ unsigned long clk_rate;
+ u64 tmp;
+ u32 val;
+ int ret;
+
+ ret = clk_enable(pc->pclk);
+ if (ret)
+ return ret;
+
+ ret = clk_enable(pc->clk);
+ if (ret)
+ return ret;
+
+ clk_rate = clk_get_rate(pc->clk);
+
+ tmp = readl_relaxed(pc->base + pc->data->regs.period);
+ tmp *= prescaled_ns;
+ state->period = DIV_U64_ROUND_UP(tmp, clk_rate);
+
+ tmp = readl_relaxed(pc->base + pc->data->regs.duty);
+ tmp *= prescaled_ns;
+ state->duty_cycle = DIV_U64_ROUND_UP(tmp, clk_rate);
+
+ val = readl_relaxed(pc->base + pc->data->regs.ctrl);
+ state->enabled = (val & enable_conf) == enable_conf;
+
+ if (pc->data->supports_polarity && !(val & PWM_DUTY_POSITIVE))
+ state->polarity = PWM_POLARITY_INVERSED;
+ else
+ state->polarity = PWM_POLARITY_NORMAL;
+
+ clk_disable(pc->clk);
+ clk_disable(pc->pclk);
+
+ return 0;
+}
+
+static void rockchip_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
+ const struct pwm_state *state)
+{
+ struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip);
+ u64 prescaled_ns = (u64)pc->data->prescaler * NSEC_PER_SEC;
+ u64 clk_rate, tmp;
+ u32 period_ticks, duty_ticks;
+ u32 ctrl;
+
+ clk_rate = clk_get_rate(pc->clk);
+
+ /*
+ * Since period and duty cycle registers have a width of 32
+ * bits, every possible input period can be obtained using the
+ * default prescaler value for all practical clock rate values.
+ */
+ tmp = mul_u64_u64_div_u64(clk_rate, state->period, prescaled_ns);
+ if (tmp > U32_MAX)
+ tmp = U32_MAX;
+ period_ticks = tmp;
+
+ tmp = mul_u64_u64_div_u64(clk_rate, state->duty_cycle, prescaled_ns);
+ if (tmp > U32_MAX)
+ tmp = U32_MAX;
+ duty_ticks = tmp;
+
+ /*
+ * Lock the period and duty of previous configuration, then
+ * change the duty and period, that would not be effective.
+ */
+ ctrl = readl_relaxed(pc->base + pc->data->regs.ctrl);
+ if (pc->data->supports_lock) {
+ ctrl |= PWM_LOCK_EN;
+ writel_relaxed(ctrl, pc->base + pc->data->regs.ctrl);
+ }
+
+ writel(period_ticks, pc->base + pc->data->regs.period);
+ writel(duty_ticks, pc->base + pc->data->regs.duty);
+
+ if (pc->data->supports_polarity) {
+ ctrl &= ~PWM_POLARITY_MASK;
+ if (state->polarity == PWM_POLARITY_INVERSED)
+ ctrl |= PWM_DUTY_NEGATIVE | PWM_INACTIVE_POSITIVE;
+ else
+ ctrl |= PWM_DUTY_POSITIVE | PWM_INACTIVE_NEGATIVE;
+ }
+
+ /*
+ * Unlock and set polarity at the same time,
+ * the configuration of duty, period and polarity
+ * would be effective together at next period.
+ */
+ if (pc->data->supports_lock)
+ ctrl &= ~PWM_LOCK_EN;
+
+ writel(ctrl, pc->base + pc->data->regs.ctrl);
+}
+
+static int rockchip_pwm_enable(struct pwm_chip *chip,
+ struct pwm_device *pwm,
+ bool enable)
+{
+ struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip);
+ u32 enable_conf = pc->data->enable_conf;
+ int ret;
+ u32 val;
+
+ if (enable) {
+ ret = clk_enable(pc->clk);
+ if (ret)
+ return ret;
+ }
+
+ val = readl_relaxed(pc->base + pc->data->regs.ctrl);
+
+ if (enable)
+ val |= enable_conf;
+ else
+ val &= ~enable_conf;
+
+ writel_relaxed(val, pc->base + pc->data->regs.ctrl);
+
+ if (!enable)
+ clk_disable(pc->clk);
+
+ return 0;
+}
+
+static int rockchip_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
+ const struct pwm_state *state)
+{
+ struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip);
+ struct pwm_state curstate;
+ bool enabled;
+ int ret = 0;
+
+ ret = clk_enable(pc->pclk);
+ if (ret)
+ return ret;
+
+ ret = clk_enable(pc->clk);
+ if (ret)
+ return ret;
+
+ pwm_get_state(pwm, &curstate);
+ enabled = curstate.enabled;
+
+ if (state->polarity != curstate.polarity && enabled &&
+ !pc->data->supports_lock) {
+ ret = rockchip_pwm_enable(chip, pwm, false);
+ if (ret)
+ goto out;
+ enabled = false;
+ }
+
+ rockchip_pwm_config(chip, pwm, state);
+ if (state->enabled != enabled) {
+ ret = rockchip_pwm_enable(chip, pwm, state->enabled);
+ if (ret)
+ goto out;
+ }
+
+out:
+ clk_disable(pc->clk);
+ clk_disable(pc->pclk);
+
+ return ret;
+}
+
+static const struct pwm_ops rockchip_pwm_ops = {
+ .get_state = rockchip_pwm_get_state,
+ .apply = rockchip_pwm_apply,
+};
+
+static const struct rockchip_pwm_data pwm_data_v1 = {
+ .regs = {
+ .duty = 0x04,
+ .period = 0x08,
+ .cntr = 0x00,
+ .ctrl = 0x0c,
+ },
+ .prescaler = 2,
+ .supports_polarity = false,
+ .supports_lock = false,
+ .enable_conf = PWM_CTRL_OUTPUT_EN | PWM_CTRL_TIMER_EN,
+};
+
+static const struct rockchip_pwm_data pwm_data_v2 = {
+ .regs = {
+ .duty = 0x08,
+ .period = 0x04,
+ .cntr = 0x00,
+ .ctrl = 0x0c,
+ },
+ .prescaler = 1,
+ .supports_polarity = true,
+ .supports_lock = false,
+ .enable_conf = PWM_OUTPUT_LEFT | PWM_LP_DISABLE | PWM_ENABLE |
+ PWM_CONTINUOUS,
+};
+
+static const struct rockchip_pwm_data pwm_data_vop = {
+ .regs = {
+ .duty = 0x08,
+ .period = 0x04,
+ .cntr = 0x0c,
+ .ctrl = 0x00,
+ },
+ .prescaler = 1,
+ .supports_polarity = true,
+ .supports_lock = false,
+ .enable_conf = PWM_OUTPUT_LEFT | PWM_LP_DISABLE | PWM_ENABLE |
+ PWM_CONTINUOUS,
+};
+
+static const struct rockchip_pwm_data pwm_data_v3 = {
+ .regs = {
+ .duty = 0x08,
+ .period = 0x04,
+ .cntr = 0x00,
+ .ctrl = 0x0c,
+ },
+ .prescaler = 1,
+ .supports_polarity = true,
+ .supports_lock = true,
+ .enable_conf = PWM_OUTPUT_LEFT | PWM_LP_DISABLE | PWM_ENABLE |
+ PWM_CONTINUOUS,
+};
+
+static const struct of_device_id rockchip_pwm_dt_ids[] = {
+ { .compatible = "rockchip,rk2928-pwm", .data = &pwm_data_v1 },
+ { .compatible = "rockchip,rk3288-pwm", .data = &pwm_data_v2 },
+ { .compatible = "rockchip,vop-pwm", .data = &pwm_data_vop },
+ { .compatible = "rockchip,rk3328-pwm", .data = &pwm_data_v3 },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, rockchip_pwm_dt_ids);
+
+static int rockchip_pwm_probe(struct platform_device *pdev)
+{
+ struct pwm_chip *chip;
+ struct rockchip_pwm_chip *pc;
+ u32 enable_conf, ctrl;
+ bool enabled;
+ int ret, count;
+
+ chip = devm_pwmchip_alloc(&pdev->dev, 1, sizeof(*pc));
+ if (IS_ERR(chip))
+ return PTR_ERR(chip);
+ pc = to_rockchip_pwm_chip(chip);
+
+ pc->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(pc->base))
+ return PTR_ERR(pc->base);
+
+ pc->clk = devm_clk_get(&pdev->dev, "pwm");
+ if (IS_ERR(pc->clk)) {
+ pc->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(pc->clk))
+ return dev_err_probe(&pdev->dev, PTR_ERR(pc->clk),
+ "Can't get PWM clk\n");
+ }
+
+ count = of_count_phandle_with_args(pdev->dev.of_node,
+ "clocks", "#clock-cells");
+ if (count == 2)
+ pc->pclk = devm_clk_get(&pdev->dev, "pclk");
+ else
+ pc->pclk = pc->clk;
+
+ if (IS_ERR(pc->pclk))
+ return dev_err_probe(&pdev->dev, PTR_ERR(pc->pclk), "Can't get APB clk\n");
+
+ ret = clk_prepare_enable(pc->clk);
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret, "Can't prepare enable PWM clk\n");
+
+ ret = clk_prepare_enable(pc->pclk);
+ if (ret) {
+ dev_err_probe(&pdev->dev, ret, "Can't prepare enable APB clk\n");
+ goto err_clk;
+ }
+
+ platform_set_drvdata(pdev, chip);
+
+ pc->data = device_get_match_data(&pdev->dev);
+ chip->ops = &rockchip_pwm_ops;
+
+ enable_conf = pc->data->enable_conf;
+ ctrl = readl_relaxed(pc->base + pc->data->regs.ctrl);
+ enabled = (ctrl & enable_conf) == enable_conf;
+
+ ret = pwmchip_add(chip);
+ if (ret < 0) {
+ dev_err_probe(&pdev->dev, ret, "pwmchip_add() failed\n");
+ goto err_pclk;
+ }
+
+ /* Keep the PWM clk enabled if the PWM appears to be up and running. */
+ if (!enabled)
+ clk_disable(pc->clk);
+
+ clk_disable(pc->pclk);
+
+ return 0;
+
+err_pclk:
+ clk_disable_unprepare(pc->pclk);
+err_clk:
+ clk_disable_unprepare(pc->clk);
+
+ return ret;
+}
+
+static void rockchip_pwm_remove(struct platform_device *pdev)
+{
+ struct pwm_chip *chip = platform_get_drvdata(pdev);
+ struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip);
+
+ pwmchip_remove(chip);
+
+ clk_unprepare(pc->pclk);
+ clk_unprepare(pc->clk);
+}
+
+static struct platform_driver rockchip_pwm_driver = {
+ .driver = {
+ .name = "rockchip-pwm",
+ .of_match_table = rockchip_pwm_dt_ids,
+ },
+ .probe = rockchip_pwm_probe,
+ .remove = rockchip_pwm_remove,
+};
+module_platform_driver(rockchip_pwm_driver);
+
+MODULE_AUTHOR("Beniamino Galvani <b.galvani@gmail.com>");
+MODULE_DESCRIPTION("Rockchip SoC PWM driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/pwm/pwm-rz-mtu3.c b/drivers/pwm/pwm-rz-mtu3.c
new file mode 100644
index 000000000..ab39bd37e
--- /dev/null
+++ b/drivers/pwm/pwm-rz-mtu3.c
@@ -0,0 +1,552 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Renesas RZ/G2L MTU3a PWM Timer driver
+ *
+ * Copyright (C) 2023 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:
+ * - When PWM is disabled, the output is driven to Hi-Z.
+ * - While the hardware supports both polarities, the driver (for now)
+ * only handles normal polarity.
+ * - HW uses one counter and two match components to configure duty_cycle
+ * and period.
+ * - Multi-Function Timer Pulse Unit (a.k.a MTU) has 7 HW channels for PWM
+ * operations. (The channels are MTU{0..4, 6, 7}.)
+ * - MTU{1, 2} channels have a single IO, whereas all other HW channels have
+ * 2 IOs.
+ * - Each IO is modelled as an independent PWM channel.
+ * - rz_mtu3_channel_io_map table is used to map the PWM channel to the
+ * corresponding HW channel as there are difference in number of IOs
+ * between HW channels.
+ */
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/limits.h>
+#include <linux/mfd/rz-mtu3.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/pwm.h>
+#include <linux/time.h>
+
+#define RZ_MTU3_MAX_PWM_CHANNELS 12
+#define RZ_MTU3_MAX_HW_CHANNELS 7
+
+/**
+ * struct rz_mtu3_channel_io_map - MTU3 pwm channel map
+ *
+ * @base_pwm_number: First PWM of a channel
+ * @num_channel_ios: number of IOs on the HW channel.
+ */
+struct rz_mtu3_channel_io_map {
+ u8 base_pwm_number;
+ u8 num_channel_ios;
+};
+
+/**
+ * struct rz_mtu3_pwm_channel - MTU3 pwm channel data
+ *
+ * @mtu: MTU3 channel data
+ * @map: MTU3 pwm channel map
+ */
+struct rz_mtu3_pwm_channel {
+ struct rz_mtu3_channel *mtu;
+ const struct rz_mtu3_channel_io_map *map;
+};
+
+/**
+ * struct rz_mtu3_pwm_chip - MTU3 pwm private data
+ *
+ * @clk: MTU3 module clock
+ * @lock: Lock to prevent concurrent access for usage count
+ * @rate: MTU3 clock rate
+ * @user_count: MTU3 usage count
+ * @enable_count: MTU3 enable count
+ * @prescale: MTU3 prescale
+ * @channel_data: MTU3 pwm channel data
+ */
+
+struct rz_mtu3_pwm_chip {
+ struct clk *clk;
+ struct mutex lock;
+ unsigned long rate;
+ u32 user_count[RZ_MTU3_MAX_HW_CHANNELS];
+ u32 enable_count[RZ_MTU3_MAX_HW_CHANNELS];
+ u8 prescale[RZ_MTU3_MAX_HW_CHANNELS];
+ struct rz_mtu3_pwm_channel channel_data[RZ_MTU3_MAX_HW_CHANNELS];
+};
+
+/*
+ * The MTU channels are {0..4, 6, 7} and the number of IO on MTU1
+ * and MTU2 channel is 1 compared to 2 on others.
+ */
+static const struct rz_mtu3_channel_io_map channel_map[] = {
+ { 0, 2 }, { 2, 1 }, { 3, 1 }, { 4, 2 }, { 6, 2 }, { 8, 2 }, { 10, 2 }
+};
+
+static inline struct rz_mtu3_pwm_chip *to_rz_mtu3_pwm_chip(struct pwm_chip *chip)
+{
+ return pwmchip_get_drvdata(chip);
+}
+
+static void rz_mtu3_pwm_read_tgr_registers(struct rz_mtu3_pwm_channel *priv,
+ u16 reg_pv_offset, u16 *pv_val,
+ u16 reg_dc_offset, u16 *dc_val)
+{
+ *pv_val = rz_mtu3_16bit_ch_read(priv->mtu, reg_pv_offset);
+ *dc_val = rz_mtu3_16bit_ch_read(priv->mtu, reg_dc_offset);
+}
+
+static void rz_mtu3_pwm_write_tgr_registers(struct rz_mtu3_pwm_channel *priv,
+ u16 reg_pv_offset, u16 pv_val,
+ u16 reg_dc_offset, u16 dc_val)
+{
+ rz_mtu3_16bit_ch_write(priv->mtu, reg_pv_offset, pv_val);
+ rz_mtu3_16bit_ch_write(priv->mtu, reg_dc_offset, dc_val);
+}
+
+static u8 rz_mtu3_pwm_calculate_prescale(struct rz_mtu3_pwm_chip *rz_mtu3,
+ u64 period_cycles)
+{
+ u32 prescaled_period_cycles;
+ u8 prescale;
+
+ /*
+ * Supported prescale values are 1, 4, 16 and 64.
+ * TODO: Support prescale values 2, 8, 32, 256 and 1024.
+ */
+ prescaled_period_cycles = period_cycles >> 16;
+ if (prescaled_period_cycles >= 16)
+ prescale = 3;
+ else
+ prescale = (fls(prescaled_period_cycles) + 1) / 2;
+
+ return prescale;
+}
+
+static struct rz_mtu3_pwm_channel *
+rz_mtu3_get_channel(struct rz_mtu3_pwm_chip *rz_mtu3_pwm, u32 hwpwm)
+{
+ struct rz_mtu3_pwm_channel *priv = rz_mtu3_pwm->channel_data;
+ unsigned int ch;
+
+ for (ch = 0; ch < RZ_MTU3_MAX_HW_CHANNELS; ch++, priv++) {
+ if (priv->map->base_pwm_number + priv->map->num_channel_ios > hwpwm)
+ break;
+ }
+
+ return priv;
+}
+
+static bool rz_mtu3_pwm_is_ch_enabled(struct rz_mtu3_pwm_chip *rz_mtu3_pwm,
+ u32 hwpwm)
+{
+ struct rz_mtu3_pwm_channel *priv;
+ bool is_channel_en;
+ u8 val;
+
+ priv = rz_mtu3_get_channel(rz_mtu3_pwm, hwpwm);
+ is_channel_en = rz_mtu3_is_enabled(priv->mtu);
+ if (!is_channel_en)
+ return false;
+
+ if (priv->map->base_pwm_number == hwpwm)
+ val = rz_mtu3_8bit_ch_read(priv->mtu, RZ_MTU3_TIORH);
+ else
+ val = rz_mtu3_8bit_ch_read(priv->mtu, RZ_MTU3_TIORL);
+
+ return val & RZ_MTU3_TIOR_IOA;
+}
+
+static int rz_mtu3_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct rz_mtu3_pwm_chip *rz_mtu3_pwm = to_rz_mtu3_pwm_chip(chip);
+ struct rz_mtu3_pwm_channel *priv;
+ bool is_mtu3_channel_available;
+ u32 ch;
+
+ priv = rz_mtu3_get_channel(rz_mtu3_pwm, pwm->hwpwm);
+ ch = priv - rz_mtu3_pwm->channel_data;
+
+ mutex_lock(&rz_mtu3_pwm->lock);
+ /*
+ * Each channel must be requested only once, so if the channel
+ * serves two PWMs and the other is already requested, skip over
+ * rz_mtu3_request_channel()
+ */
+ if (!rz_mtu3_pwm->user_count[ch]) {
+ is_mtu3_channel_available = rz_mtu3_request_channel(priv->mtu);
+ if (!is_mtu3_channel_available) {
+ mutex_unlock(&rz_mtu3_pwm->lock);
+ return -EBUSY;
+ }
+ }
+
+ rz_mtu3_pwm->user_count[ch]++;
+ mutex_unlock(&rz_mtu3_pwm->lock);
+
+ return 0;
+}
+
+static void rz_mtu3_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct rz_mtu3_pwm_chip *rz_mtu3_pwm = to_rz_mtu3_pwm_chip(chip);
+ struct rz_mtu3_pwm_channel *priv;
+ u32 ch;
+
+ priv = rz_mtu3_get_channel(rz_mtu3_pwm, pwm->hwpwm);
+ ch = priv - rz_mtu3_pwm->channel_data;
+
+ mutex_lock(&rz_mtu3_pwm->lock);
+ rz_mtu3_pwm->user_count[ch]--;
+ if (!rz_mtu3_pwm->user_count[ch])
+ rz_mtu3_release_channel(priv->mtu);
+
+ mutex_unlock(&rz_mtu3_pwm->lock);
+}
+
+static int rz_mtu3_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct rz_mtu3_pwm_chip *rz_mtu3_pwm = to_rz_mtu3_pwm_chip(chip);
+ struct rz_mtu3_pwm_channel *priv;
+ u32 ch;
+ u8 val;
+ int rc;
+
+ rc = pm_runtime_resume_and_get(pwmchip_parent(chip));
+ if (rc)
+ return rc;
+
+ priv = rz_mtu3_get_channel(rz_mtu3_pwm, pwm->hwpwm);
+ ch = priv - rz_mtu3_pwm->channel_data;
+ val = RZ_MTU3_TIOR_OC_IOB_TOGGLE | RZ_MTU3_TIOR_OC_IOA_H_COMP_MATCH;
+
+ rz_mtu3_8bit_ch_write(priv->mtu, RZ_MTU3_TMDR1, RZ_MTU3_TMDR1_MD_PWMMODE1);
+ if (priv->map->base_pwm_number == pwm->hwpwm)
+ rz_mtu3_8bit_ch_write(priv->mtu, RZ_MTU3_TIORH, val);
+ else
+ rz_mtu3_8bit_ch_write(priv->mtu, RZ_MTU3_TIORL, val);
+
+ mutex_lock(&rz_mtu3_pwm->lock);
+ if (!rz_mtu3_pwm->enable_count[ch])
+ rz_mtu3_enable(priv->mtu);
+
+ rz_mtu3_pwm->enable_count[ch]++;
+ mutex_unlock(&rz_mtu3_pwm->lock);
+
+ return 0;
+}
+
+static void rz_mtu3_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct rz_mtu3_pwm_chip *rz_mtu3_pwm = to_rz_mtu3_pwm_chip(chip);
+ struct rz_mtu3_pwm_channel *priv;
+ u32 ch;
+
+ priv = rz_mtu3_get_channel(rz_mtu3_pwm, pwm->hwpwm);
+ ch = priv - rz_mtu3_pwm->channel_data;
+
+ /* Disable output pins of MTU3 channel */
+ if (priv->map->base_pwm_number == pwm->hwpwm)
+ rz_mtu3_8bit_ch_write(priv->mtu, RZ_MTU3_TIORH, RZ_MTU3_TIOR_OC_RETAIN);
+ else
+ rz_mtu3_8bit_ch_write(priv->mtu, RZ_MTU3_TIORL, RZ_MTU3_TIOR_OC_RETAIN);
+
+ mutex_lock(&rz_mtu3_pwm->lock);
+ rz_mtu3_pwm->enable_count[ch]--;
+ if (!rz_mtu3_pwm->enable_count[ch])
+ rz_mtu3_disable(priv->mtu);
+
+ mutex_unlock(&rz_mtu3_pwm->lock);
+
+ pm_runtime_put_sync(pwmchip_parent(chip));
+}
+
+static int rz_mtu3_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm,
+ struct pwm_state *state)
+{
+ struct rz_mtu3_pwm_chip *rz_mtu3_pwm = to_rz_mtu3_pwm_chip(chip);
+ int rc;
+
+ rc = pm_runtime_resume_and_get(pwmchip_parent(chip));
+ if (rc)
+ return rc;
+
+ state->enabled = rz_mtu3_pwm_is_ch_enabled(rz_mtu3_pwm, pwm->hwpwm);
+ if (state->enabled) {
+ struct rz_mtu3_pwm_channel *priv;
+ u8 prescale, val;
+ u16 dc, pv;
+ u64 tmp;
+
+ priv = rz_mtu3_get_channel(rz_mtu3_pwm, pwm->hwpwm);
+ if (priv->map->base_pwm_number == pwm->hwpwm)
+ rz_mtu3_pwm_read_tgr_registers(priv, RZ_MTU3_TGRA, &pv,
+ RZ_MTU3_TGRB, &dc);
+ else
+ rz_mtu3_pwm_read_tgr_registers(priv, RZ_MTU3_TGRC, &pv,
+ RZ_MTU3_TGRD, &dc);
+
+ val = rz_mtu3_8bit_ch_read(priv->mtu, RZ_MTU3_TCR);
+ prescale = FIELD_GET(RZ_MTU3_TCR_TPCS, val);
+
+ /* With prescale <= 7 and pv <= 0xffff this doesn't overflow. */
+ tmp = NSEC_PER_SEC * (u64)pv << (2 * prescale);
+ state->period = DIV_ROUND_UP_ULL(tmp, rz_mtu3_pwm->rate);
+ tmp = NSEC_PER_SEC * (u64)dc << (2 * prescale);
+ state->duty_cycle = DIV_ROUND_UP_ULL(tmp, rz_mtu3_pwm->rate);
+
+ if (state->duty_cycle > state->period)
+ state->duty_cycle = state->period;
+ }
+
+ state->polarity = PWM_POLARITY_NORMAL;
+ pm_runtime_put(pwmchip_parent(chip));
+
+ return 0;
+}
+
+static u16 rz_mtu3_pwm_calculate_pv_or_dc(u64 period_or_duty_cycle, u8 prescale)
+{
+ return min(period_or_duty_cycle >> (2 * prescale), (u64)U16_MAX);
+}
+
+static int rz_mtu3_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
+ const struct pwm_state *state)
+{
+ struct rz_mtu3_pwm_chip *rz_mtu3_pwm = to_rz_mtu3_pwm_chip(chip);
+ struct rz_mtu3_pwm_channel *priv;
+ u64 period_cycles;
+ u64 duty_cycles;
+ u8 prescale;
+ u16 pv, dc;
+ u8 val;
+ u32 ch;
+
+ priv = rz_mtu3_get_channel(rz_mtu3_pwm, pwm->hwpwm);
+ ch = priv - rz_mtu3_pwm->channel_data;
+
+ period_cycles = mul_u64_u32_div(state->period, rz_mtu3_pwm->rate,
+ NSEC_PER_SEC);
+ prescale = rz_mtu3_pwm_calculate_prescale(rz_mtu3_pwm, period_cycles);
+
+ /*
+ * Prescalar is shared by multiple channels, so prescale can
+ * NOT be modified when there are multiple channels in use with
+ * different settings. Modify prescalar if other PWM is off or handle
+ * it, if current prescale value is less than the one we want to set.
+ */
+ if (rz_mtu3_pwm->enable_count[ch] > 1) {
+ if (rz_mtu3_pwm->prescale[ch] > prescale)
+ return -EBUSY;
+
+ prescale = rz_mtu3_pwm->prescale[ch];
+ }
+
+ pv = rz_mtu3_pwm_calculate_pv_or_dc(period_cycles, prescale);
+
+ duty_cycles = mul_u64_u32_div(state->duty_cycle, rz_mtu3_pwm->rate,
+ NSEC_PER_SEC);
+ dc = rz_mtu3_pwm_calculate_pv_or_dc(duty_cycles, prescale);
+
+ /*
+ * If the PWM channel is disabled, make sure to turn on the clock
+ * before writing the register.
+ */
+ if (!pwm->state.enabled) {
+ int rc;
+
+ rc = pm_runtime_resume_and_get(pwmchip_parent(chip));
+ if (rc)
+ return rc;
+ }
+
+ val = RZ_MTU3_TCR_CKEG_RISING | prescale;
+
+ /* Counter must be stopped while updating TCR register */
+ if (rz_mtu3_pwm->prescale[ch] != prescale && rz_mtu3_pwm->enable_count[ch])
+ rz_mtu3_disable(priv->mtu);
+
+ if (priv->map->base_pwm_number == pwm->hwpwm) {
+ rz_mtu3_8bit_ch_write(priv->mtu, RZ_MTU3_TCR,
+ RZ_MTU3_TCR_CCLR_TGRA | val);
+ rz_mtu3_pwm_write_tgr_registers(priv, RZ_MTU3_TGRA, pv,
+ RZ_MTU3_TGRB, dc);
+ } else {
+ rz_mtu3_8bit_ch_write(priv->mtu, RZ_MTU3_TCR,
+ RZ_MTU3_TCR_CCLR_TGRC | val);
+ rz_mtu3_pwm_write_tgr_registers(priv, RZ_MTU3_TGRC, pv,
+ RZ_MTU3_TGRD, dc);
+ }
+
+ if (rz_mtu3_pwm->prescale[ch] != prescale) {
+ /*
+ * Prescalar is shared by multiple channels, we cache the
+ * prescalar value from first enabled channel and use the same
+ * value for both channels.
+ */
+ rz_mtu3_pwm->prescale[ch] = prescale;
+
+ if (rz_mtu3_pwm->enable_count[ch])
+ rz_mtu3_enable(priv->mtu);
+ }
+
+ /* If the PWM is not enabled, turn the clock off again to save power. */
+ if (!pwm->state.enabled)
+ pm_runtime_put(pwmchip_parent(chip));
+
+ return 0;
+}
+
+static int rz_mtu3_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
+ const struct pwm_state *state)
+{
+ struct rz_mtu3_pwm_chip *rz_mtu3_pwm = to_rz_mtu3_pwm_chip(chip);
+ bool enabled = pwm->state.enabled;
+ int ret;
+
+ if (state->polarity != PWM_POLARITY_NORMAL)
+ return -EINVAL;
+
+ if (!state->enabled) {
+ if (enabled)
+ rz_mtu3_pwm_disable(chip, pwm);
+
+ return 0;
+ }
+
+ mutex_lock(&rz_mtu3_pwm->lock);
+ ret = rz_mtu3_pwm_config(chip, pwm, state);
+ mutex_unlock(&rz_mtu3_pwm->lock);
+ if (ret)
+ return ret;
+
+ if (!enabled)
+ ret = rz_mtu3_pwm_enable(chip, pwm);
+
+ return ret;
+}
+
+static const struct pwm_ops rz_mtu3_pwm_ops = {
+ .request = rz_mtu3_pwm_request,
+ .free = rz_mtu3_pwm_free,
+ .get_state = rz_mtu3_pwm_get_state,
+ .apply = rz_mtu3_pwm_apply,
+};
+
+static int rz_mtu3_pwm_pm_runtime_suspend(struct device *dev)
+{
+ struct pwm_chip *chip = dev_get_drvdata(dev);
+ struct rz_mtu3_pwm_chip *rz_mtu3_pwm = to_rz_mtu3_pwm_chip(chip);
+
+ clk_disable_unprepare(rz_mtu3_pwm->clk);
+
+ return 0;
+}
+
+static int rz_mtu3_pwm_pm_runtime_resume(struct device *dev)
+{
+ struct pwm_chip *chip = dev_get_drvdata(dev);
+ struct rz_mtu3_pwm_chip *rz_mtu3_pwm = to_rz_mtu3_pwm_chip(chip);
+
+ return clk_prepare_enable(rz_mtu3_pwm->clk);
+}
+
+static DEFINE_RUNTIME_DEV_PM_OPS(rz_mtu3_pwm_pm_ops,
+ rz_mtu3_pwm_pm_runtime_suspend,
+ rz_mtu3_pwm_pm_runtime_resume, NULL);
+
+static void rz_mtu3_pwm_pm_disable(void *data)
+{
+ struct pwm_chip *chip = data;
+ struct rz_mtu3_pwm_chip *rz_mtu3_pwm = to_rz_mtu3_pwm_chip(chip);
+
+ clk_rate_exclusive_put(rz_mtu3_pwm->clk);
+ pm_runtime_disable(pwmchip_parent(chip));
+ pm_runtime_set_suspended(pwmchip_parent(chip));
+}
+
+static int rz_mtu3_pwm_probe(struct platform_device *pdev)
+{
+ struct rz_mtu3 *parent_ddata = dev_get_drvdata(pdev->dev.parent);
+ struct rz_mtu3_pwm_chip *rz_mtu3_pwm;
+ struct pwm_chip *chip;
+ struct device *dev = &pdev->dev;
+ unsigned int i, j = 0;
+ int ret;
+
+ chip = devm_pwmchip_alloc(&pdev->dev, RZ_MTU3_MAX_PWM_CHANNELS,
+ sizeof(*rz_mtu3_pwm));
+ if (IS_ERR(chip))
+ return PTR_ERR(chip);
+ rz_mtu3_pwm = to_rz_mtu3_pwm_chip(chip);
+
+ rz_mtu3_pwm->clk = parent_ddata->clk;
+
+ for (i = 0; i < RZ_MTU_NUM_CHANNELS; i++) {
+ if (i == RZ_MTU3_CHAN_5 || i == RZ_MTU3_CHAN_8)
+ continue;
+
+ rz_mtu3_pwm->channel_data[j].mtu = &parent_ddata->channels[i];
+ rz_mtu3_pwm->channel_data[j].mtu->dev = dev;
+ rz_mtu3_pwm->channel_data[j].map = &channel_map[j];
+ j++;
+ }
+
+ mutex_init(&rz_mtu3_pwm->lock);
+ platform_set_drvdata(pdev, chip);
+ ret = clk_prepare_enable(rz_mtu3_pwm->clk);
+ if (ret)
+ return dev_err_probe(dev, ret, "Clock enable failed\n");
+
+ clk_rate_exclusive_get(rz_mtu3_pwm->clk);
+
+ rz_mtu3_pwm->rate = clk_get_rate(rz_mtu3_pwm->clk);
+ /*
+ * Refuse clk rates > 1 GHz to prevent overflow later for computing
+ * period and duty cycle.
+ */
+ if (rz_mtu3_pwm->rate > NSEC_PER_SEC) {
+ ret = -EINVAL;
+ clk_rate_exclusive_put(rz_mtu3_pwm->clk);
+ goto disable_clock;
+ }
+
+ pm_runtime_set_active(&pdev->dev);
+ pm_runtime_enable(&pdev->dev);
+ ret = devm_add_action_or_reset(&pdev->dev, rz_mtu3_pwm_pm_disable,
+ chip);
+ if (ret < 0)
+ return ret;
+
+ chip->ops = &rz_mtu3_pwm_ops;
+ ret = devm_pwmchip_add(&pdev->dev, chip);
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret, "failed to add PWM chip\n");
+
+ pm_runtime_idle(&pdev->dev);
+
+ return 0;
+
+disable_clock:
+ clk_disable_unprepare(rz_mtu3_pwm->clk);
+ return ret;
+}
+
+static struct platform_driver rz_mtu3_pwm_driver = {
+ .driver = {
+ .name = "pwm-rz-mtu3",
+ .pm = pm_ptr(&rz_mtu3_pwm_pm_ops),
+ },
+ .probe = rz_mtu3_pwm_probe,
+};
+module_platform_driver(rz_mtu3_pwm_driver);
+
+MODULE_AUTHOR("Biju Das <biju.das.jz@bp.renesas.com>");
+MODULE_ALIAS("platform:pwm-rz-mtu3");
+MODULE_DESCRIPTION("Renesas RZ/G2L MTU3a PWM Timer Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/pwm/pwm-rzg2l-gpt.c b/drivers/pwm/pwm-rzg2l-gpt.c
new file mode 100644
index 000000000..1569f3ba8
--- /dev/null
+++ b/drivers/pwm/pwm-rzg2l-gpt.c
@@ -0,0 +1,455 @@
+// 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;
+ u64 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 inline unsigned int rzg2l_gpt_sibling(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(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) {
+ u8 sibling_ch = rzg2l_gpt_sibling(pwm->hwpwm);
+
+ if (rzg2l_gpt_is_ch_enabled(rzg2l_gpt, sibling_ch)) {
+ if (period_ticks < rzg2l_gpt->period_ticks[ch])
+ return -EBUSY;
+
+ period_ticks = rzg2l_gpt->period_ticks[ch];
+ }
+ }
+
+ prescale = rzg2l_gpt_calculate_prescale(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\n");
+
+ /*
+ * 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\n");
+
+ /*
+ * 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\n");
+
+ 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-samsung.c b/drivers/pwm/pwm-samsung.c
new file mode 100644
index 000000000..a30bd7c30
--- /dev/null
+++ b/drivers/pwm/pwm-samsung.c
@@ -0,0 +1,648 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2007 Ben Dooks
+ * Copyright (c) 2008 Simtec Electronics
+ * Ben Dooks <ben@simtec.co.uk>, <ben-linux@fluff.org>
+ * Copyright (c) 2013 Tomasz Figa <tomasz.figa@gmail.com>
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd.
+ *
+ * PWM driver for Samsung SoCs
+ */
+
+#include <linux/bitops.h>
+#include <linux/clk.h>
+#include <linux/export.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/time.h>
+
+/* For struct samsung_timer_variant and samsung_pwm_lock. */
+#include <clocksource/samsung_pwm.h>
+
+#define REG_TCFG0 0x00
+#define REG_TCFG1 0x04
+#define REG_TCON 0x08
+
+#define REG_TCNTB(chan) (0x0c + ((chan) * 0xc))
+#define REG_TCMPB(chan) (0x10 + ((chan) * 0xc))
+
+#define TCFG0_PRESCALER_MASK 0xff
+#define TCFG0_PRESCALER1_SHIFT 8
+
+#define TCFG1_MUX_MASK 0xf
+#define TCFG1_SHIFT(chan) (4 * (chan))
+
+/*
+ * Each channel occupies 4 bits in TCON register, but there is a gap of 4
+ * bits (one channel) after channel 0, so channels have different numbering
+ * when accessing TCON register. See to_tcon_channel() function.
+ *
+ * In addition, the location of autoreload bit for channel 4 (TCON channel 5)
+ * in its set of bits is 2 as opposed to 3 for other channels.
+ */
+#define TCON_START(chan) BIT(4 * (chan) + 0)
+#define TCON_MANUALUPDATE(chan) BIT(4 * (chan) + 1)
+#define TCON_INVERT(chan) BIT(4 * (chan) + 2)
+#define _TCON_AUTORELOAD(chan) BIT(4 * (chan) + 3)
+#define _TCON_AUTORELOAD4(chan) BIT(4 * (chan) + 2)
+#define TCON_AUTORELOAD(chan) \
+ ((chan < 5) ? _TCON_AUTORELOAD(chan) : _TCON_AUTORELOAD4(chan))
+
+/**
+ * struct samsung_pwm_channel - private data of PWM channel
+ * @period_ns: current period in nanoseconds programmed to the hardware
+ * @duty_ns: current duty time in nanoseconds programmed to the hardware
+ * @tin_ns: time of one timer tick in nanoseconds with current timer rate
+ */
+struct samsung_pwm_channel {
+ u32 period_ns;
+ u32 duty_ns;
+ u32 tin_ns;
+};
+
+/**
+ * struct samsung_pwm_chip - private data of PWM chip
+ * @variant: local copy of hardware variant data
+ * @inverter_mask: inverter status for all channels - one bit per channel
+ * @disabled_mask: disabled status for all channels - one bit per channel
+ * @base: base address of mapped PWM registers
+ * @base_clk: base clock used to drive the timers
+ * @tclk0: external clock 0 (can be ERR_PTR if not present)
+ * @tclk1: external clock 1 (can be ERR_PTR if not present)
+ * @channel: per channel driver data
+ */
+struct samsung_pwm_chip {
+ struct samsung_pwm_variant variant;
+ u8 inverter_mask;
+ u8 disabled_mask;
+
+ void __iomem *base;
+ struct clk *base_clk;
+ struct clk *tclk0;
+ struct clk *tclk1;
+ struct samsung_pwm_channel channel[SAMSUNG_PWM_NUM];
+};
+
+#ifndef CONFIG_CLKSRC_SAMSUNG_PWM
+/*
+ * PWM block is shared between pwm-samsung and samsung_pwm_timer drivers
+ * and some registers need access synchronization. If both drivers are
+ * compiled in, the spinlock is defined in the clocksource driver,
+ * otherwise following definition is used.
+ *
+ * Currently we do not need any more complex synchronization method
+ * because all the supported SoCs contain only one instance of the PWM
+ * IP. Should this change, both drivers will need to be modified to
+ * properly synchronize accesses to particular instances.
+ */
+static DEFINE_RAW_SPINLOCK(samsung_pwm_lock);
+#endif
+
+static inline
+struct samsung_pwm_chip *to_samsung_pwm_chip(struct pwm_chip *chip)
+{
+ return pwmchip_get_drvdata(chip);
+}
+
+static inline unsigned int to_tcon_channel(unsigned int channel)
+{
+ /* TCON register has a gap of 4 bits (1 channel) after channel 0 */
+ return (channel == 0) ? 0 : (channel + 1);
+}
+
+static void __pwm_samsung_manual_update(struct samsung_pwm_chip *our_chip,
+ struct pwm_device *pwm)
+{
+ unsigned int tcon_chan = to_tcon_channel(pwm->hwpwm);
+ u32 tcon;
+
+ tcon = readl(our_chip->base + REG_TCON);
+ tcon |= TCON_MANUALUPDATE(tcon_chan);
+ writel(tcon, our_chip->base + REG_TCON);
+
+ tcon &= ~TCON_MANUALUPDATE(tcon_chan);
+ writel(tcon, our_chip->base + REG_TCON);
+}
+
+static void pwm_samsung_set_divisor(struct samsung_pwm_chip *our_chip,
+ unsigned int channel, u8 divisor)
+{
+ u8 shift = TCFG1_SHIFT(channel);
+ unsigned long flags;
+ u32 reg;
+ u8 bits;
+
+ bits = (fls(divisor) - 1) - our_chip->variant.div_base;
+
+ raw_spin_lock_irqsave(&samsung_pwm_lock, flags);
+
+ reg = readl(our_chip->base + REG_TCFG1);
+ reg &= ~(TCFG1_MUX_MASK << shift);
+ reg |= bits << shift;
+ writel(reg, our_chip->base + REG_TCFG1);
+
+ raw_spin_unlock_irqrestore(&samsung_pwm_lock, flags);
+}
+
+static int pwm_samsung_is_tdiv(struct samsung_pwm_chip *our_chip, unsigned int chan)
+{
+ struct samsung_pwm_variant *variant = &our_chip->variant;
+ u32 reg;
+
+ reg = readl(our_chip->base + REG_TCFG1);
+ reg >>= TCFG1_SHIFT(chan);
+ reg &= TCFG1_MUX_MASK;
+
+ return (BIT(reg) & variant->tclk_mask) == 0;
+}
+
+static unsigned long pwm_samsung_get_tin_rate(struct samsung_pwm_chip *our_chip,
+ unsigned int chan)
+{
+ unsigned long rate;
+ u32 reg;
+
+ rate = clk_get_rate(our_chip->base_clk);
+
+ reg = readl(our_chip->base + REG_TCFG0);
+ if (chan >= 2)
+ reg >>= TCFG0_PRESCALER1_SHIFT;
+ reg &= TCFG0_PRESCALER_MASK;
+
+ return rate / (reg + 1);
+}
+
+static unsigned long pwm_samsung_calc_tin(struct pwm_chip *chip,
+ unsigned int chan, unsigned long freq)
+{
+ struct samsung_pwm_chip *our_chip = to_samsung_pwm_chip(chip);
+ struct samsung_pwm_variant *variant = &our_chip->variant;
+ unsigned long rate;
+ struct clk *clk;
+ u8 div;
+
+ if (!pwm_samsung_is_tdiv(our_chip, chan)) {
+ clk = (chan < 2) ? our_chip->tclk0 : our_chip->tclk1;
+ if (!IS_ERR(clk)) {
+ rate = clk_get_rate(clk);
+ if (rate)
+ return rate;
+ }
+
+ dev_warn(pwmchip_parent(chip),
+ "tclk of PWM %d is inoperational, using tdiv\n", chan);
+ }
+
+ rate = pwm_samsung_get_tin_rate(our_chip, chan);
+ dev_dbg(pwmchip_parent(chip), "tin parent at %lu\n", rate);
+
+ /*
+ * Compare minimum PWM frequency that can be achieved with possible
+ * divider settings and choose the lowest divisor that can generate
+ * frequencies lower than requested.
+ */
+ if (variant->bits < 32) {
+ /* Only for s3c24xx */
+ for (div = variant->div_base; div < 4; ++div)
+ if ((rate >> (variant->bits + div)) < freq)
+ break;
+ } else {
+ /*
+ * Other variants have enough counter bits to generate any
+ * requested rate, so no need to check higher divisors.
+ */
+ div = variant->div_base;
+ }
+
+ pwm_samsung_set_divisor(our_chip, chan, BIT(div));
+
+ return rate >> div;
+}
+
+static int pwm_samsung_request(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct samsung_pwm_chip *our_chip = to_samsung_pwm_chip(chip);
+
+ if (!(our_chip->variant.output_mask & BIT(pwm->hwpwm))) {
+ dev_warn(pwmchip_parent(chip),
+ "tried to request PWM channel %d without output\n",
+ pwm->hwpwm);
+ return -EINVAL;
+ }
+
+ memset(&our_chip->channel[pwm->hwpwm], 0, sizeof(our_chip->channel[pwm->hwpwm]));
+
+ return 0;
+}
+
+static int pwm_samsung_enable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct samsung_pwm_chip *our_chip = to_samsung_pwm_chip(chip);
+ unsigned int tcon_chan = to_tcon_channel(pwm->hwpwm);
+ unsigned long flags;
+ u32 tcon;
+
+ raw_spin_lock_irqsave(&samsung_pwm_lock, flags);
+
+ tcon = readl(our_chip->base + REG_TCON);
+
+ tcon &= ~TCON_START(tcon_chan);
+ tcon |= TCON_MANUALUPDATE(tcon_chan);
+ writel(tcon, our_chip->base + REG_TCON);
+
+ tcon &= ~TCON_MANUALUPDATE(tcon_chan);
+ tcon |= TCON_START(tcon_chan) | TCON_AUTORELOAD(tcon_chan);
+ writel(tcon, our_chip->base + REG_TCON);
+
+ our_chip->disabled_mask &= ~BIT(pwm->hwpwm);
+
+ raw_spin_unlock_irqrestore(&samsung_pwm_lock, flags);
+
+ return 0;
+}
+
+static void pwm_samsung_disable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct samsung_pwm_chip *our_chip = to_samsung_pwm_chip(chip);
+ unsigned int tcon_chan = to_tcon_channel(pwm->hwpwm);
+ unsigned long flags;
+ u32 tcon;
+
+ raw_spin_lock_irqsave(&samsung_pwm_lock, flags);
+
+ tcon = readl(our_chip->base + REG_TCON);
+ tcon &= ~TCON_AUTORELOAD(tcon_chan);
+ writel(tcon, our_chip->base + REG_TCON);
+
+ /*
+ * In case the PWM is at 100% duty cycle, force a manual
+ * update to prevent the signal from staying high.
+ */
+ if (readl(our_chip->base + REG_TCMPB(pwm->hwpwm)) == (u32)-1U)
+ __pwm_samsung_manual_update(our_chip, pwm);
+
+ our_chip->disabled_mask |= BIT(pwm->hwpwm);
+
+ raw_spin_unlock_irqrestore(&samsung_pwm_lock, flags);
+}
+
+static void pwm_samsung_manual_update(struct samsung_pwm_chip *our_chip,
+ struct pwm_device *pwm)
+{
+ unsigned long flags;
+
+ raw_spin_lock_irqsave(&samsung_pwm_lock, flags);
+
+ __pwm_samsung_manual_update(our_chip, pwm);
+
+ raw_spin_unlock_irqrestore(&samsung_pwm_lock, flags);
+}
+
+static int __pwm_samsung_config(struct pwm_chip *chip, struct pwm_device *pwm,
+ int duty_ns, int period_ns, bool force_period)
+{
+ struct samsung_pwm_chip *our_chip = to_samsung_pwm_chip(chip);
+ struct samsung_pwm_channel *chan = &our_chip->channel[pwm->hwpwm];
+ u32 tin_ns = chan->tin_ns, tcnt, tcmp, oldtcmp;
+
+ tcnt = readl(our_chip->base + REG_TCNTB(pwm->hwpwm));
+ oldtcmp = readl(our_chip->base + REG_TCMPB(pwm->hwpwm));
+
+ /* We need tick count for calculation, not last tick. */
+ ++tcnt;
+
+ /* Check to see if we are changing the clock rate of the PWM. */
+ if (chan->period_ns != period_ns || force_period) {
+ unsigned long tin_rate;
+ u32 period;
+
+ period = NSEC_PER_SEC / period_ns;
+
+ dev_dbg(pwmchip_parent(chip), "duty_ns=%d, period_ns=%d (%u)\n",
+ duty_ns, period_ns, period);
+
+ tin_rate = pwm_samsung_calc_tin(chip, pwm->hwpwm, period);
+
+ dev_dbg(pwmchip_parent(chip), "tin_rate=%lu\n", tin_rate);
+
+ tin_ns = NSEC_PER_SEC / tin_rate;
+ tcnt = period_ns / tin_ns;
+ }
+
+ /* Period is too short. */
+ if (tcnt <= 1)
+ return -ERANGE;
+
+ /* Note that counters count down. */
+ tcmp = duty_ns / tin_ns;
+
+ /* 0% duty is not available */
+ if (!tcmp)
+ ++tcmp;
+
+ tcmp = tcnt - tcmp;
+
+ /* Decrement to get tick numbers, instead of tick counts. */
+ --tcnt;
+ /* -1UL will give 100% duty. */
+ --tcmp;
+
+ dev_dbg(pwmchip_parent(chip), "tin_ns=%u, tcmp=%u/%u\n", tin_ns, tcmp, tcnt);
+
+ /* Update PWM registers. */
+ writel(tcnt, our_chip->base + REG_TCNTB(pwm->hwpwm));
+ writel(tcmp, our_chip->base + REG_TCMPB(pwm->hwpwm));
+
+ /*
+ * In case the PWM is currently at 100% duty cycle, force a manual
+ * update to prevent the signal staying high if the PWM is disabled
+ * shortly afer this update (before it autoreloaded the new values).
+ */
+ if (oldtcmp == (u32) -1) {
+ dev_dbg(pwmchip_parent(chip), "Forcing manual update");
+ pwm_samsung_manual_update(our_chip, pwm);
+ }
+
+ chan->period_ns = period_ns;
+ chan->tin_ns = tin_ns;
+ chan->duty_ns = duty_ns;
+
+ return 0;
+}
+
+static int pwm_samsung_config(struct pwm_chip *chip, struct pwm_device *pwm,
+ int duty_ns, int period_ns)
+{
+ return __pwm_samsung_config(chip, pwm, duty_ns, period_ns, false);
+}
+
+static void pwm_samsung_set_invert(struct samsung_pwm_chip *our_chip,
+ unsigned int channel, bool invert)
+{
+ unsigned int tcon_chan = to_tcon_channel(channel);
+ unsigned long flags;
+ u32 tcon;
+
+ raw_spin_lock_irqsave(&samsung_pwm_lock, flags);
+
+ tcon = readl(our_chip->base + REG_TCON);
+
+ if (invert) {
+ our_chip->inverter_mask |= BIT(channel);
+ tcon |= TCON_INVERT(tcon_chan);
+ } else {
+ our_chip->inverter_mask &= ~BIT(channel);
+ tcon &= ~TCON_INVERT(tcon_chan);
+ }
+
+ writel(tcon, our_chip->base + REG_TCON);
+
+ raw_spin_unlock_irqrestore(&samsung_pwm_lock, flags);
+}
+
+static int pwm_samsung_set_polarity(struct pwm_chip *chip,
+ struct pwm_device *pwm,
+ enum pwm_polarity polarity)
+{
+ struct samsung_pwm_chip *our_chip = to_samsung_pwm_chip(chip);
+ bool invert = (polarity == PWM_POLARITY_NORMAL);
+
+ /* Inverted means normal in the hardware. */
+ pwm_samsung_set_invert(our_chip, pwm->hwpwm, invert);
+
+ return 0;
+}
+
+static int pwm_samsung_apply(struct pwm_chip *chip, struct pwm_device *pwm,
+ const struct pwm_state *state)
+{
+ int err, enabled = pwm->state.enabled;
+
+ if (state->polarity != pwm->state.polarity) {
+ if (enabled) {
+ pwm_samsung_disable(chip, pwm);
+ enabled = false;
+ }
+
+ err = pwm_samsung_set_polarity(chip, pwm, state->polarity);
+ if (err)
+ return err;
+ }
+
+ if (!state->enabled) {
+ if (enabled)
+ pwm_samsung_disable(chip, pwm);
+
+ return 0;
+ }
+
+ /*
+ * We currently avoid using 64bit arithmetic by using the
+ * fact that anything faster than 1Hz is easily representable
+ * by 32bits.
+ */
+ if (state->period > NSEC_PER_SEC)
+ return -ERANGE;
+
+ err = pwm_samsung_config(chip, pwm, state->duty_cycle, state->period);
+ if (err)
+ return err;
+
+ if (!pwm->state.enabled)
+ err = pwm_samsung_enable(chip, pwm);
+
+ return err;
+}
+
+static const struct pwm_ops pwm_samsung_ops = {
+ .request = pwm_samsung_request,
+ .apply = pwm_samsung_apply,
+};
+
+#ifdef CONFIG_OF
+static const struct samsung_pwm_variant s3c24xx_variant = {
+ .bits = 16,
+ .div_base = 1,
+ .has_tint_cstat = false,
+ .tclk_mask = BIT(4),
+};
+
+static const struct samsung_pwm_variant s3c64xx_variant = {
+ .bits = 32,
+ .div_base = 0,
+ .has_tint_cstat = true,
+ .tclk_mask = BIT(7) | BIT(6) | BIT(5),
+};
+
+static const struct samsung_pwm_variant s5p64x0_variant = {
+ .bits = 32,
+ .div_base = 0,
+ .has_tint_cstat = true,
+ .tclk_mask = 0,
+};
+
+static const struct samsung_pwm_variant s5pc100_variant = {
+ .bits = 32,
+ .div_base = 0,
+ .has_tint_cstat = true,
+ .tclk_mask = BIT(5),
+};
+
+static const struct of_device_id samsung_pwm_matches[] = {
+ { .compatible = "samsung,s3c2410-pwm", .data = &s3c24xx_variant },
+ { .compatible = "samsung,s3c6400-pwm", .data = &s3c64xx_variant },
+ { .compatible = "samsung,s5p6440-pwm", .data = &s5p64x0_variant },
+ { .compatible = "samsung,s5pc100-pwm", .data = &s5pc100_variant },
+ { .compatible = "samsung,exynos4210-pwm", .data = &s5p64x0_variant },
+ { }
+};
+MODULE_DEVICE_TABLE(of, samsung_pwm_matches);
+
+static int pwm_samsung_parse_dt(struct pwm_chip *chip)
+{
+ struct samsung_pwm_chip *our_chip = to_samsung_pwm_chip(chip);
+ struct device_node *np = pwmchip_parent(chip)->of_node;
+ const struct of_device_id *match;
+ u32 val;
+
+ match = of_match_node(samsung_pwm_matches, np);
+ if (!match)
+ return -ENODEV;
+
+ memcpy(&our_chip->variant, match->data, sizeof(our_chip->variant));
+
+ of_property_for_each_u32(np, "samsung,pwm-outputs", val) {
+ if (val >= SAMSUNG_PWM_NUM) {
+ dev_err(pwmchip_parent(chip),
+ "%s: invalid channel index in samsung,pwm-outputs property\n",
+ __func__);
+ continue;
+ }
+ our_chip->variant.output_mask |= BIT(val);
+ }
+
+ return 0;
+}
+#else
+static int pwm_samsung_parse_dt(struct pwm_chip *chip)
+{
+ return -ENODEV;
+}
+#endif
+
+static int pwm_samsung_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct samsung_pwm_chip *our_chip;
+ struct pwm_chip *chip;
+ unsigned int chan;
+ int ret;
+
+ chip = devm_pwmchip_alloc(&pdev->dev, SAMSUNG_PWM_NUM, sizeof(*our_chip));
+ if (IS_ERR(chip))
+ return PTR_ERR(chip);
+ our_chip = to_samsung_pwm_chip(chip);
+
+ chip->ops = &pwm_samsung_ops;
+ our_chip->inverter_mask = BIT(SAMSUNG_PWM_NUM) - 1;
+
+ if (IS_ENABLED(CONFIG_OF) && pdev->dev.of_node) {
+ ret = pwm_samsung_parse_dt(chip);
+ if (ret)
+ return ret;
+ } else {
+ if (!pdev->dev.platform_data)
+ return dev_err_probe(&pdev->dev, -EINVAL,
+ "no platform data specified\n");
+
+ memcpy(&our_chip->variant, pdev->dev.platform_data,
+ sizeof(our_chip->variant));
+ }
+
+ our_chip->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(our_chip->base))
+ return PTR_ERR(our_chip->base);
+
+ our_chip->base_clk = devm_clk_get_enabled(&pdev->dev, "timers");
+ if (IS_ERR(our_chip->base_clk))
+ return dev_err_probe(dev, PTR_ERR(our_chip->base_clk),
+ "failed to get timer base clk\n");
+
+ for (chan = 0; chan < SAMSUNG_PWM_NUM; ++chan)
+ if (our_chip->variant.output_mask & BIT(chan))
+ pwm_samsung_set_invert(our_chip, chan, true);
+
+ /* Following clocks are optional. */
+ our_chip->tclk0 = devm_clk_get(&pdev->dev, "pwm-tclk0");
+ our_chip->tclk1 = devm_clk_get(&pdev->dev, "pwm-tclk1");
+
+ platform_set_drvdata(pdev, chip);
+
+ ret = devm_pwmchip_add(&pdev->dev, chip);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "failed to register PWM chip\n");
+
+ dev_dbg(dev, "base_clk at %lu, tclk0 at %lu, tclk1 at %lu\n",
+ clk_get_rate(our_chip->base_clk),
+ !IS_ERR(our_chip->tclk0) ? clk_get_rate(our_chip->tclk0) : 0,
+ !IS_ERR(our_chip->tclk1) ? clk_get_rate(our_chip->tclk1) : 0);
+
+ return 0;
+}
+
+static int pwm_samsung_resume(struct device *dev)
+{
+ struct pwm_chip *chip = dev_get_drvdata(dev);
+ struct samsung_pwm_chip *our_chip = to_samsung_pwm_chip(chip);
+ unsigned int i;
+
+ for (i = 0; i < SAMSUNG_PWM_NUM; i++) {
+ struct pwm_device *pwm = &chip->pwms[i];
+ struct samsung_pwm_channel *chan = &our_chip->channel[i];
+
+ if (!test_bit(PWMF_REQUESTED, &pwm->flags))
+ continue;
+
+ if (our_chip->variant.output_mask & BIT(i))
+ pwm_samsung_set_invert(our_chip, i,
+ our_chip->inverter_mask & BIT(i));
+
+ if (chan->period_ns) {
+ __pwm_samsung_config(chip, pwm, chan->duty_ns,
+ chan->period_ns, true);
+ /* needed to make PWM disable work on Odroid-XU3 */
+ pwm_samsung_manual_update(our_chip, pwm);
+ }
+
+ if (our_chip->disabled_mask & BIT(i))
+ pwm_samsung_disable(chip, pwm);
+ else
+ pwm_samsung_enable(chip, pwm);
+ }
+
+ return 0;
+}
+
+static DEFINE_SIMPLE_DEV_PM_OPS(pwm_samsung_pm_ops, NULL, pwm_samsung_resume);
+
+static struct platform_driver pwm_samsung_driver = {
+ .driver = {
+ .name = "samsung-pwm",
+ .pm = pm_ptr(&pwm_samsung_pm_ops),
+ .of_match_table = of_match_ptr(samsung_pwm_matches),
+ },
+ .probe = pwm_samsung_probe,
+};
+module_platform_driver(pwm_samsung_driver);
+
+MODULE_DESCRIPTION("Samsung Pulse Width Modulator driver");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Tomasz Figa <tomasz.figa@gmail.com>");
+MODULE_ALIAS("platform:samsung-pwm");
diff --git a/drivers/pwm/pwm-sifive.c b/drivers/pwm/pwm-sifive.c
new file mode 100644
index 000000000..4d0e4512d
--- /dev/null
+++ b/drivers/pwm/pwm-sifive.c
@@ -0,0 +1,373 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2017-2018 SiFive
+ * For SiFive's PWM IP block documentation please refer Chapter 14 of
+ * Reference Manual : https://static.dev.sifive.com/FU540-C000-v1.0.pdf
+ *
+ * PWM output inversion: According to the SiFive Reference manual
+ * the output of each comparator is high whenever the value of pwms is
+ * greater than or equal to the corresponding pwmcmpX[Reference Manual].
+ *
+ * Figure 29 in the same manual shows that the pwmcmpXcenter bit is
+ * hard-tied to 0 (XNOR), which effectively inverts the comparison so that
+ * the output goes HIGH when `pwms < pwmcmpX`.
+ *
+ * In other words, each pwmcmp register actually defines the **inactive**
+ * (low) period of the pulse, not the active time exactly opposite to what
+ * the documentation text implies.
+ *
+ * To compensate, this driver always **inverts** the duty value when reading
+ * or writing pwmcmp registers , so that users interact with a conventional
+ * **active-high** PWM interface.
+ *
+ *
+ * Limitations:
+ * - When changing both duty cycle and period, we cannot prevent in
+ * software that the output might produce a period with mixed
+ * settings (new period length and old duty cycle).
+ * - The hardware cannot generate a 0% duty cycle.
+ * - The hardware generates only inverted output.
+ */
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+#include <linux/slab.h>
+#include <linux/bitfield.h>
+
+/* Register offsets */
+#define PWM_SIFIVE_PWMCFG 0x0
+#define PWM_SIFIVE_PWMCOUNT 0x8
+#define PWM_SIFIVE_PWMS 0x10
+#define PWM_SIFIVE_PWMCMP(i) (0x20 + 4 * (i))
+
+/* PWMCFG fields */
+#define PWM_SIFIVE_PWMCFG_SCALE GENMASK(3, 0)
+#define PWM_SIFIVE_PWMCFG_STICKY BIT(8)
+#define PWM_SIFIVE_PWMCFG_ZERO_CMP BIT(9)
+#define PWM_SIFIVE_PWMCFG_DEGLITCH BIT(10)
+#define PWM_SIFIVE_PWMCFG_EN_ALWAYS BIT(12)
+#define PWM_SIFIVE_PWMCFG_EN_ONCE BIT(13)
+#define PWM_SIFIVE_PWMCFG_CENTER BIT(16)
+#define PWM_SIFIVE_PWMCFG_GANG BIT(24)
+#define PWM_SIFIVE_PWMCFG_IP BIT(28)
+
+#define PWM_SIFIVE_CMPWIDTH 16
+#define PWM_SIFIVE_DEFAULT_PERIOD 10000000
+
+struct pwm_sifive_ddata {
+ struct device *parent;
+ struct mutex lock; /* lock to protect user_count and approx_period */
+ struct notifier_block notifier;
+ struct clk *clk;
+ void __iomem *regs;
+ unsigned int real_period;
+ unsigned int approx_period;
+ int user_count;
+};
+
+static inline
+struct pwm_sifive_ddata *pwm_sifive_chip_to_ddata(struct pwm_chip *chip)
+{
+ return pwmchip_get_drvdata(chip);
+}
+
+static int pwm_sifive_request(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct pwm_sifive_ddata *ddata = pwm_sifive_chip_to_ddata(chip);
+
+ mutex_lock(&ddata->lock);
+ ddata->user_count++;
+ mutex_unlock(&ddata->lock);
+
+ return 0;
+}
+
+static void pwm_sifive_free(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct pwm_sifive_ddata *ddata = pwm_sifive_chip_to_ddata(chip);
+
+ mutex_lock(&ddata->lock);
+ ddata->user_count--;
+ mutex_unlock(&ddata->lock);
+}
+
+/* Called holding ddata->lock */
+static void pwm_sifive_update_clock(struct pwm_sifive_ddata *ddata,
+ unsigned long rate)
+{
+ unsigned long long num;
+ unsigned long scale_pow;
+ int scale;
+ u32 val;
+ /*
+ * The PWM unit is used with pwmzerocmp=0, so the only way to modify the
+ * period length is using pwmscale which provides the number of bits the
+ * counter is shifted before being feed to the comparators. A period
+ * lasts (1 << (PWM_SIFIVE_CMPWIDTH + pwmscale)) clock ticks.
+ * (1 << (PWM_SIFIVE_CMPWIDTH + scale)) * 10^9/rate = period
+ */
+ scale_pow = div64_ul(ddata->approx_period * (u64)rate, NSEC_PER_SEC);
+ scale = clamp(ilog2(scale_pow) - PWM_SIFIVE_CMPWIDTH, 0, 0xf);
+
+ val = PWM_SIFIVE_PWMCFG_EN_ALWAYS |
+ FIELD_PREP(PWM_SIFIVE_PWMCFG_SCALE, scale);
+ writel(val, ddata->regs + PWM_SIFIVE_PWMCFG);
+
+ /* As scale <= 15 the shift operation cannot overflow. */
+ num = (unsigned long long)NSEC_PER_SEC << (PWM_SIFIVE_CMPWIDTH + scale);
+ ddata->real_period = DIV_ROUND_UP_ULL(num, rate);
+ dev_dbg(ddata->parent,
+ "New real_period = %u ns\n", ddata->real_period);
+}
+
+static int pwm_sifive_get_state(struct pwm_chip *chip, struct pwm_device *pwm,
+ struct pwm_state *state)
+{
+ struct pwm_sifive_ddata *ddata = pwm_sifive_chip_to_ddata(chip);
+ u32 duty, val, inactive;
+
+ inactive = readl(ddata->regs + PWM_SIFIVE_PWMCMP(pwm->hwpwm));
+ /*
+ * PWM hardware uses 'inactive' counts in pwmcmp, so invert to get actual duty.
+ * Here, 'inactive' is the low time and we compute duty as max_count - inactive.
+ */
+ duty = (1U << PWM_SIFIVE_CMPWIDTH) - 1 - inactive;
+
+ state->enabled = duty > 0;
+
+ val = readl(ddata->regs + PWM_SIFIVE_PWMCFG);
+ if (!(val & PWM_SIFIVE_PWMCFG_EN_ALWAYS))
+ state->enabled = false;
+
+ state->period = ddata->real_period;
+ state->duty_cycle = DIV_ROUND_UP_ULL((u64)duty * ddata->real_period,
+ (1U << PWM_SIFIVE_CMPWIDTH));
+ state->polarity = PWM_POLARITY_NORMAL;
+
+ return 0;
+}
+
+static int pwm_sifive_apply(struct pwm_chip *chip, struct pwm_device *pwm,
+ const struct pwm_state *state)
+{
+ struct pwm_sifive_ddata *ddata = pwm_sifive_chip_to_ddata(chip);
+ struct pwm_state cur_state;
+ unsigned int duty_cycle;
+ unsigned long long num;
+ bool enabled;
+ int ret = 0;
+ u64 frac;
+ u32 inactive;
+
+ if (state->polarity != PWM_POLARITY_NORMAL)
+ return -EINVAL;
+
+ cur_state = pwm->state;
+ enabled = cur_state.enabled;
+
+ duty_cycle = state->duty_cycle;
+ if (!state->enabled)
+ duty_cycle = 0;
+
+ /*
+ * The problem of output producing mixed setting as mentioned at top,
+ * occurs here. To minimize the window for this problem, we are
+ * calculating the register values first and then writing them
+ * consecutively
+ */
+ num = (u64)duty_cycle * (1U << PWM_SIFIVE_CMPWIDTH);
+ frac = num;
+ do_div(frac, state->period);
+ /* The hardware cannot generate a 0% duty cycle */
+ frac = min(frac, (u64)(1U << PWM_SIFIVE_CMPWIDTH) - 1);
+ /* pwmcmp register must be loaded with the inactive(invert the duty) */
+ inactive = (1U << PWM_SIFIVE_CMPWIDTH) - 1 - frac;
+
+ mutex_lock(&ddata->lock);
+ if (state->period != ddata->approx_period) {
+ /*
+ * Don't let a 2nd user change the period underneath the 1st user.
+ * However if ddate->approx_period == 0 this is the first time we set
+ * any period, so let whoever gets here first set the period so other
+ * users who agree on the period won't fail.
+ */
+ if (ddata->user_count != 1 && ddata->approx_period) {
+ mutex_unlock(&ddata->lock);
+ return -EBUSY;
+ }
+ ddata->approx_period = state->period;
+ pwm_sifive_update_clock(ddata, clk_get_rate(ddata->clk));
+ }
+ mutex_unlock(&ddata->lock);
+
+ /*
+ * If the PWM is enabled the clk is already on. So only enable it
+ * conditionally to have it on exactly once afterwards independent of
+ * the PWM state.
+ */
+ if (!enabled) {
+ ret = clk_enable(ddata->clk);
+ if (ret) {
+ dev_err(pwmchip_parent(chip), "Enable clk failed\n");
+ return ret;
+ }
+ }
+
+ writel(inactive, ddata->regs + PWM_SIFIVE_PWMCMP(pwm->hwpwm));
+
+ if (!state->enabled)
+ clk_disable(ddata->clk);
+
+ return 0;
+}
+
+static const struct pwm_ops pwm_sifive_ops = {
+ .request = pwm_sifive_request,
+ .free = pwm_sifive_free,
+ .get_state = pwm_sifive_get_state,
+ .apply = pwm_sifive_apply,
+};
+
+static int pwm_sifive_clock_notifier(struct notifier_block *nb,
+ unsigned long event, void *data)
+{
+ struct clk_notifier_data *ndata = data;
+ struct pwm_sifive_ddata *ddata =
+ container_of(nb, struct pwm_sifive_ddata, notifier);
+
+ if (event == POST_RATE_CHANGE) {
+ mutex_lock(&ddata->lock);
+ pwm_sifive_update_clock(ddata, ndata->new_rate);
+ mutex_unlock(&ddata->lock);
+ }
+
+ return NOTIFY_OK;
+}
+
+static int pwm_sifive_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct pwm_sifive_ddata *ddata;
+ struct pwm_chip *chip;
+ int ret;
+ u32 val;
+ unsigned int enabled_pwms = 0, enabled_clks = 1;
+
+ chip = devm_pwmchip_alloc(dev, 4, sizeof(*ddata));
+ if (IS_ERR(chip))
+ return PTR_ERR(chip);
+
+ ddata = pwm_sifive_chip_to_ddata(chip);
+ ddata->parent = dev;
+ mutex_init(&ddata->lock);
+ chip->ops = &pwm_sifive_ops;
+
+ ddata->regs = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(ddata->regs))
+ return PTR_ERR(ddata->regs);
+
+ ddata->clk = devm_clk_get_prepared(dev, NULL);
+ if (IS_ERR(ddata->clk))
+ return dev_err_probe(dev, PTR_ERR(ddata->clk),
+ "Unable to find controller clock\n");
+
+ ret = clk_enable(ddata->clk);
+ if (ret) {
+ dev_err(dev, "failed to enable clock for pwm: %d\n", ret);
+ return ret;
+ }
+
+ val = readl(ddata->regs + PWM_SIFIVE_PWMCFG);
+ if (val & PWM_SIFIVE_PWMCFG_EN_ALWAYS) {
+ unsigned int i;
+
+ for (i = 0; i < chip->npwm; ++i) {
+ val = readl(ddata->regs + PWM_SIFIVE_PWMCMP(i));
+ if (val > 0)
+ ++enabled_pwms;
+ }
+ }
+
+ /* The clk should be on once for each running PWM. */
+ if (enabled_pwms) {
+ while (enabled_clks < enabled_pwms) {
+ /* This is not expected to fail as the clk is already on */
+ ret = clk_enable(ddata->clk);
+ if (unlikely(ret)) {
+ dev_err_probe(dev, ret, "Failed to enable clk\n");
+ goto disable_clk;
+ }
+ ++enabled_clks;
+ }
+ } else {
+ clk_disable(ddata->clk);
+ enabled_clks = 0;
+ }
+
+ /* Watch for changes to underlying clock frequency */
+ ddata->notifier.notifier_call = pwm_sifive_clock_notifier;
+ ret = clk_notifier_register(ddata->clk, &ddata->notifier);
+ if (ret) {
+ dev_err(dev, "failed to register clock notifier: %d\n", ret);
+ goto disable_clk;
+ }
+
+ ret = pwmchip_add(chip);
+ if (ret < 0) {
+ dev_err(dev, "cannot register PWM: %d\n", ret);
+ goto unregister_clk;
+ }
+
+ platform_set_drvdata(pdev, chip);
+ dev_dbg(dev, "SiFive PWM chip registered %d PWMs\n", chip->npwm);
+
+ return 0;
+
+unregister_clk:
+ clk_notifier_unregister(ddata->clk, &ddata->notifier);
+disable_clk:
+ while (enabled_clks) {
+ clk_disable(ddata->clk);
+ --enabled_clks;
+ }
+
+ return ret;
+}
+
+static void pwm_sifive_remove(struct platform_device *dev)
+{
+ struct pwm_chip *chip = platform_get_drvdata(dev);
+ struct pwm_sifive_ddata *ddata = pwm_sifive_chip_to_ddata(chip);
+ struct pwm_device *pwm;
+ int ch;
+
+ pwmchip_remove(chip);
+ clk_notifier_unregister(ddata->clk, &ddata->notifier);
+
+ for (ch = 0; ch < chip->npwm; ch++) {
+ pwm = &chip->pwms[ch];
+ if (pwm->state.enabled)
+ clk_disable(ddata->clk);
+ }
+}
+
+static const struct of_device_id pwm_sifive_of_match[] = {
+ { .compatible = "sifive,pwm0" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, pwm_sifive_of_match);
+
+static struct platform_driver pwm_sifive_driver = {
+ .probe = pwm_sifive_probe,
+ .remove = pwm_sifive_remove,
+ .driver = {
+ .name = "pwm-sifive",
+ .of_match_table = pwm_sifive_of_match,
+ },
+};
+module_platform_driver(pwm_sifive_driver);
+
+MODULE_DESCRIPTION("SiFive PWM driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/pwm/pwm-sl28cpld.c b/drivers/pwm/pwm-sl28cpld.c
new file mode 100644
index 000000000..0e25a05ea
--- /dev/null
+++ b/drivers/pwm/pwm-sl28cpld.c
@@ -0,0 +1,262 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * sl28cpld PWM driver
+ *
+ * Copyright (c) 2020 Michael Walle <michael@walle.cc>
+ *
+ * There is no public datasheet available for this PWM core. But it is easy
+ * enough to be briefly explained. It consists of one 8-bit counter. The PWM
+ * supports four distinct frequencies by selecting when to reset the counter.
+ * With the prescaler setting you can select which bit of the counter is used
+ * to reset it. This implies that the higher the frequency the less remaining
+ * bits are available for the actual counter.
+ *
+ * Let cnt[7:0] be the counter, clocked at 32kHz:
+ * +-----------+--------+--------------+-----------+---------------+
+ * | prescaler | reset | counter bits | frequency | period length |
+ * +-----------+--------+--------------+-----------+---------------+
+ * | 0 | cnt[7] | cnt[6:0] | 250 Hz | 4000000 ns |
+ * | 1 | cnt[6] | cnt[5:0] | 500 Hz | 2000000 ns |
+ * | 2 | cnt[5] | cnt[4:0] | 1 kHz | 1000000 ns |
+ * | 3 | cnt[4] | cnt[3:0] | 2 kHz | 500000 ns |
+ * +-----------+--------+--------------+-----------+---------------+
+ *
+ * Limitations:
+ * - The hardware cannot generate a 100% duty cycle if the prescaler is 0.
+ * - The hardware cannot atomically set the prescaler and the counter value,
+ * which might lead to glitches and inconsistent states if a write fails.
+ * - The counter is not reset if you switch the prescaler which leads
+ * to glitches, too.
+ * - The duty cycle will switch immediately and not after a complete cycle.
+ * - Depending on the actual implementation, disabling the PWM might have
+ * side effects. For example, if the output pin is shared with a GPIO pin
+ * it will automatically switch back to GPIO mode.
+ */
+
+#include <linux/bitfield.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <linux/pwm.h>
+#include <linux/regmap.h>
+
+/*
+ * PWM timer block registers.
+ */
+#define SL28CPLD_PWM_CTRL 0x00
+#define SL28CPLD_PWM_CTRL_ENABLE BIT(7)
+#define SL28CPLD_PWM_CTRL_PRESCALER_MASK GENMASK(1, 0)
+#define SL28CPLD_PWM_CYCLE 0x01
+#define SL28CPLD_PWM_CYCLE_MAX GENMASK(6, 0)
+
+#define SL28CPLD_PWM_CLK 32000 /* 32 kHz */
+#define SL28CPLD_PWM_MAX_DUTY_CYCLE(prescaler) (1 << (7 - (prescaler)))
+#define SL28CPLD_PWM_PERIOD(prescaler) \
+ (NSEC_PER_SEC / SL28CPLD_PWM_CLK * SL28CPLD_PWM_MAX_DUTY_CYCLE(prescaler))
+
+/*
+ * We calculate the duty cycle like this:
+ * duty_cycle_ns = pwm_cycle_reg * max_period_ns / max_duty_cycle
+ *
+ * With
+ * max_period_ns = 1 << (7 - prescaler) / SL28CPLD_PWM_CLK * NSEC_PER_SEC
+ * max_duty_cycle = 1 << (7 - prescaler)
+ * this then simplifies to:
+ * duty_cycle_ns = pwm_cycle_reg / SL28CPLD_PWM_CLK * NSEC_PER_SEC
+ * = NSEC_PER_SEC / SL28CPLD_PWM_CLK * pwm_cycle_reg
+ *
+ * NSEC_PER_SEC is a multiple of SL28CPLD_PWM_CLK, therefore we're not losing
+ * precision by doing the divison first.
+ */
+#define SL28CPLD_PWM_TO_DUTY_CYCLE(reg) \
+ (NSEC_PER_SEC / SL28CPLD_PWM_CLK * (reg))
+#define SL28CPLD_PWM_FROM_DUTY_CYCLE(duty_cycle) \
+ (DIV_ROUND_DOWN_ULL((duty_cycle), NSEC_PER_SEC / SL28CPLD_PWM_CLK))
+
+#define sl28cpld_pwm_read(priv, reg, val) \
+ regmap_read((priv)->regmap, (priv)->offset + (reg), (val))
+#define sl28cpld_pwm_write(priv, reg, val) \
+ regmap_write((priv)->regmap, (priv)->offset + (reg), (val))
+
+struct sl28cpld_pwm {
+ struct regmap *regmap;
+ u32 offset;
+};
+
+static inline struct sl28cpld_pwm *sl28cpld_pwm_from_chip(struct pwm_chip *chip)
+{
+ return pwmchip_get_drvdata(chip);
+}
+
+static int sl28cpld_pwm_get_state(struct pwm_chip *chip,
+ struct pwm_device *pwm,
+ struct pwm_state *state)
+{
+ struct sl28cpld_pwm *priv = sl28cpld_pwm_from_chip(chip);
+ unsigned int reg;
+ int prescaler;
+
+ sl28cpld_pwm_read(priv, SL28CPLD_PWM_CTRL, &reg);
+
+ state->enabled = reg & SL28CPLD_PWM_CTRL_ENABLE;
+
+ prescaler = FIELD_GET(SL28CPLD_PWM_CTRL_PRESCALER_MASK, reg);
+ state->period = SL28CPLD_PWM_PERIOD(prescaler);
+
+ sl28cpld_pwm_read(priv, SL28CPLD_PWM_CYCLE, &reg);
+ state->duty_cycle = SL28CPLD_PWM_TO_DUTY_CYCLE(reg);
+ state->polarity = PWM_POLARITY_NORMAL;
+
+ /*
+ * Sanitize values for the PWM core. Depending on the prescaler it
+ * might happen that we calculate a duty_cycle greater than the actual
+ * period. This might happen if someone (e.g. the bootloader) sets an
+ * invalid combination of values. The behavior of the hardware is
+ * undefined in this case. But we need to report sane values back to
+ * the PWM core.
+ */
+ state->duty_cycle = min(state->duty_cycle, state->period);
+
+ return 0;
+}
+
+static int sl28cpld_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
+ const struct pwm_state *state)
+{
+ struct sl28cpld_pwm *priv = sl28cpld_pwm_from_chip(chip);
+ unsigned int cycle, prescaler;
+ bool write_duty_cycle_first;
+ int ret;
+ u8 ctrl;
+
+ /* Polarity inversion is not supported */
+ if (state->polarity != PWM_POLARITY_NORMAL)
+ return -EINVAL;
+
+ /*
+ * Calculate the prescaler. Pick the biggest period that isn't
+ * bigger than the requested period.
+ */
+ prescaler = DIV_ROUND_UP_ULL(SL28CPLD_PWM_PERIOD(0), state->period);
+ prescaler = order_base_2(prescaler);
+
+ if (prescaler > field_max(SL28CPLD_PWM_CTRL_PRESCALER_MASK))
+ return -ERANGE;
+
+ ctrl = FIELD_PREP(SL28CPLD_PWM_CTRL_PRESCALER_MASK, prescaler);
+ if (state->enabled)
+ ctrl |= SL28CPLD_PWM_CTRL_ENABLE;
+
+ cycle = SL28CPLD_PWM_FROM_DUTY_CYCLE(state->duty_cycle);
+ cycle = min_t(unsigned int, cycle, SL28CPLD_PWM_MAX_DUTY_CYCLE(prescaler));
+
+ /*
+ * Work around the hardware limitation. See also above. Trap 100% duty
+ * cycle if the prescaler is 0. Set prescaler to 1 instead. We don't
+ * care about the frequency because its "all-one" in either case.
+ *
+ * We don't need to check the actual prescaler setting, because only
+ * if the prescaler is 0 we can have this particular value.
+ */
+ if (cycle == SL28CPLD_PWM_MAX_DUTY_CYCLE(0)) {
+ ctrl &= ~SL28CPLD_PWM_CTRL_PRESCALER_MASK;
+ ctrl |= FIELD_PREP(SL28CPLD_PWM_CTRL_PRESCALER_MASK, 1);
+ cycle = SL28CPLD_PWM_MAX_DUTY_CYCLE(1);
+ }
+
+ /*
+ * To avoid glitches when we switch the prescaler, we have to make sure
+ * we have a valid duty cycle for the new mode.
+ *
+ * Take the current prescaler (or the current period length) into
+ * account to decide whether we have to write the duty cycle or the new
+ * prescaler first. If the period length is decreasing we have to
+ * write the duty cycle first.
+ */
+ write_duty_cycle_first = pwm->state.period > state->period;
+
+ if (write_duty_cycle_first) {
+ ret = sl28cpld_pwm_write(priv, SL28CPLD_PWM_CYCLE, cycle);
+ if (ret)
+ return ret;
+ }
+
+ ret = sl28cpld_pwm_write(priv, SL28CPLD_PWM_CTRL, ctrl);
+ if (ret)
+ return ret;
+
+ if (!write_duty_cycle_first) {
+ ret = sl28cpld_pwm_write(priv, SL28CPLD_PWM_CYCLE, cycle);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct pwm_ops sl28cpld_pwm_ops = {
+ .apply = sl28cpld_pwm_apply,
+ .get_state = sl28cpld_pwm_get_state,
+};
+
+static int sl28cpld_pwm_probe(struct platform_device *pdev)
+{
+ struct sl28cpld_pwm *priv;
+ struct pwm_chip *chip;
+ int ret;
+
+ if (!pdev->dev.parent) {
+ dev_err(&pdev->dev, "no parent device\n");
+ return -ENODEV;
+ }
+
+ chip = devm_pwmchip_alloc(&pdev->dev, 1, sizeof(*priv));
+ if (IS_ERR(chip))
+ return PTR_ERR(chip);
+ priv = sl28cpld_pwm_from_chip(chip);
+
+ priv->regmap = dev_get_regmap(pdev->dev.parent, NULL);
+ if (!priv->regmap) {
+ dev_err(&pdev->dev, "could not get parent regmap\n");
+ return -ENODEV;
+ }
+
+ ret = device_property_read_u32(&pdev->dev, "reg", &priv->offset);
+ if (ret) {
+ dev_err(&pdev->dev, "no 'reg' property found (%pe)\n",
+ ERR_PTR(ret));
+ return -EINVAL;
+ }
+
+ /* Initialize the pwm_chip structure */
+ chip->ops = &sl28cpld_pwm_ops;
+
+ ret = devm_pwmchip_add(&pdev->dev, chip);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to add PWM chip (%pe)",
+ ERR_PTR(ret));
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct of_device_id sl28cpld_pwm_of_match[] = {
+ { .compatible = "kontron,sl28cpld-pwm" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, sl28cpld_pwm_of_match);
+
+static struct platform_driver sl28cpld_pwm_driver = {
+ .probe = sl28cpld_pwm_probe,
+ .driver = {
+ .name = "sl28cpld-pwm",
+ .of_match_table = sl28cpld_pwm_of_match,
+ },
+};
+module_platform_driver(sl28cpld_pwm_driver);
+
+MODULE_DESCRIPTION("sl28cpld PWM Driver");
+MODULE_AUTHOR("Michael Walle <michael@walle.cc>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/pwm/pwm-sophgo-sg2042.c b/drivers/pwm/pwm-sophgo-sg2042.c
new file mode 100644
index 000000000..0b6461172
--- /dev/null
+++ b/drivers/pwm/pwm-sophgo-sg2042.c
@@ -0,0 +1,300 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Sophgo SG2042 PWM Controller Driver
+ *
+ * Copyright (C) 2024 Sophgo Technology Inc.
+ * Copyright (C) 2024 Chen Wang <unicorn_wang@outlook.com>
+ *
+ * Limitations:
+ * - After reset, the output of the PWM channel is always high.
+ * The value of HLPERIOD/PERIOD is 0.
+ * - When HLPERIOD or PERIOD is reconfigured, PWM will start to
+ * output waveforms with the new configuration after completing
+ * the running period.
+ * - When PERIOD and HLPERIOD is set to 0, the PWM wave output will
+ * be stopped and the output is pulled to high.
+ * - SG2044 supports both polarities, SG2042 only normal polarity.
+ * See the datasheet [1] for more details.
+ * [1]:https://github.com/sophgo/sophgo-doc/tree/main/SG2042/TRM
+ */
+
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/math64.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+#include <linux/reset.h>
+
+/*
+ * Offset RegisterName
+ * 0x0000 HLPERIOD0
+ * 0x0004 PERIOD0
+ * 0x0008 HLPERIOD1
+ * 0x000C PERIOD1
+ * 0x0010 HLPERIOD2
+ * 0x0014 PERIOD2
+ * 0x0018 HLPERIOD3
+ * 0x001C PERIOD3
+ * Four groups and every group is composed of HLPERIOD & PERIOD
+ */
+#define SG2042_PWM_HLPERIOD(chan) ((chan) * 8 + 0)
+#define SG2042_PWM_PERIOD(chan) ((chan) * 8 + 4)
+
+#define SG2044_PWM_POLARITY 0x40
+#define SG2044_PWM_PWMSTART 0x44
+#define SG2044_PWM_OE 0xd0
+
+#define SG2042_PWM_CHANNELNUM 4
+
+/**
+ * struct sg2042_pwm_ddata - private driver data
+ * @base: base address of mapped PWM registers
+ * @clk_rate_hz: rate of base clock in HZ
+ */
+struct sg2042_pwm_ddata {
+ void __iomem *base;
+ unsigned long clk_rate_hz;
+};
+
+struct sg2042_chip_data {
+ const struct pwm_ops ops;
+};
+
+/*
+ * period_ticks: PERIOD
+ * hlperiod_ticks: HLPERIOD
+ */
+static void pwm_sg2042_config(struct sg2042_pwm_ddata *ddata, unsigned int chan,
+ u32 period_ticks, u32 hlperiod_ticks)
+{
+ void __iomem *base = ddata->base;
+
+ writel(period_ticks, base + SG2042_PWM_PERIOD(chan));
+ writel(hlperiod_ticks, base + SG2042_PWM_HLPERIOD(chan));
+}
+
+static void pwm_sg2042_set_dutycycle(struct pwm_chip *chip, struct pwm_device *pwm,
+ const struct pwm_state *state)
+{
+ struct sg2042_pwm_ddata *ddata = pwmchip_get_drvdata(chip);
+ u32 hlperiod_ticks;
+ u32 period_ticks;
+
+ /*
+ * Duration of High level (duty_cycle) = HLPERIOD x Period_of_input_clk
+ * Duration of One Cycle (period) = PERIOD x Period_of_input_clk
+ */
+ period_ticks = min(mul_u64_u64_div_u64(ddata->clk_rate_hz, state->period, NSEC_PER_SEC), U32_MAX);
+ hlperiod_ticks = min(mul_u64_u64_div_u64(ddata->clk_rate_hz, state->duty_cycle, NSEC_PER_SEC), U32_MAX);
+
+ dev_dbg(pwmchip_parent(chip), "chan[%u]: ENABLE=%u, PERIOD=%u, HLPERIOD=%u, POLARITY=%u\n",
+ pwm->hwpwm, state->enabled, period_ticks, hlperiod_ticks, state->polarity);
+
+ pwm_sg2042_config(ddata, pwm->hwpwm, period_ticks, hlperiod_ticks);
+}
+
+static int pwm_sg2042_apply(struct pwm_chip *chip, struct pwm_device *pwm,
+ const struct pwm_state *state)
+{
+ struct sg2042_pwm_ddata *ddata = pwmchip_get_drvdata(chip);
+
+ if (state->polarity == PWM_POLARITY_INVERSED)
+ return -EINVAL;
+
+ if (!state->enabled) {
+ pwm_sg2042_config(ddata, pwm->hwpwm, 0, 0);
+ return 0;
+ }
+
+ pwm_sg2042_set_dutycycle(chip, pwm, state);
+
+ return 0;
+}
+
+static int pwm_sg2042_get_state(struct pwm_chip *chip, struct pwm_device *pwm,
+ struct pwm_state *state)
+{
+ struct sg2042_pwm_ddata *ddata = pwmchip_get_drvdata(chip);
+ unsigned int chan = pwm->hwpwm;
+ u32 hlperiod_ticks;
+ u32 period_ticks;
+
+ period_ticks = readl(ddata->base + SG2042_PWM_PERIOD(chan));
+ hlperiod_ticks = readl(ddata->base + SG2042_PWM_HLPERIOD(chan));
+
+ if (!period_ticks) {
+ state->enabled = false;
+ return 0;
+ }
+
+ if (hlperiod_ticks > period_ticks)
+ hlperiod_ticks = period_ticks;
+
+ state->enabled = true;
+ state->period = DIV_ROUND_UP_ULL((u64)period_ticks * NSEC_PER_SEC, ddata->clk_rate_hz);
+ state->duty_cycle = DIV_ROUND_UP_ULL((u64)hlperiod_ticks * NSEC_PER_SEC, ddata->clk_rate_hz);
+ state->polarity = PWM_POLARITY_NORMAL;
+
+ return 0;
+}
+
+static void pwm_sg2044_set_outputen(struct sg2042_pwm_ddata *ddata, struct pwm_device *pwm,
+ bool enabled)
+{
+ u32 pwmstart;
+
+ pwmstart = readl(ddata->base + SG2044_PWM_PWMSTART);
+
+ if (enabled)
+ pwmstart |= BIT(pwm->hwpwm);
+ else
+ pwmstart &= ~BIT(pwm->hwpwm);
+
+ writel(pwmstart, ddata->base + SG2044_PWM_PWMSTART);
+}
+
+static void pwm_sg2044_set_outputdir(struct sg2042_pwm_ddata *ddata, struct pwm_device *pwm,
+ bool enabled)
+{
+ u32 pwm_oe;
+
+ pwm_oe = readl(ddata->base + SG2044_PWM_OE);
+
+ if (enabled)
+ pwm_oe |= BIT(pwm->hwpwm);
+ else
+ pwm_oe &= ~BIT(pwm->hwpwm);
+
+ writel(pwm_oe, ddata->base + SG2044_PWM_OE);
+}
+
+static void pwm_sg2044_set_polarity(struct sg2042_pwm_ddata *ddata, struct pwm_device *pwm,
+ const struct pwm_state *state)
+{
+ u32 pwm_polarity;
+
+ pwm_polarity = readl(ddata->base + SG2044_PWM_POLARITY);
+
+ if (state->polarity == PWM_POLARITY_NORMAL)
+ pwm_polarity &= ~BIT(pwm->hwpwm);
+ else
+ pwm_polarity |= BIT(pwm->hwpwm);
+
+ writel(pwm_polarity, ddata->base + SG2044_PWM_POLARITY);
+}
+
+static int pwm_sg2044_apply(struct pwm_chip *chip, struct pwm_device *pwm,
+ const struct pwm_state *state)
+{
+ struct sg2042_pwm_ddata *ddata = pwmchip_get_drvdata(chip);
+
+ pwm_sg2044_set_polarity(ddata, pwm, state);
+
+ pwm_sg2042_set_dutycycle(chip, pwm, state);
+
+ /*
+ * re-enable PWMSTART to refresh the register period
+ */
+ pwm_sg2044_set_outputen(ddata, pwm, false);
+
+ if (!state->enabled)
+ return 0;
+
+ pwm_sg2044_set_outputdir(ddata, pwm, true);
+ pwm_sg2044_set_outputen(ddata, pwm, true);
+
+ return 0;
+}
+
+static const struct sg2042_chip_data sg2042_chip_data = {
+ .ops = {
+ .apply = pwm_sg2042_apply,
+ .get_state = pwm_sg2042_get_state,
+ },
+};
+
+static const struct sg2042_chip_data sg2044_chip_data = {
+ .ops = {
+ .apply = pwm_sg2044_apply,
+ .get_state = pwm_sg2042_get_state,
+ },
+};
+
+static const struct of_device_id sg2042_pwm_ids[] = {
+ {
+ .compatible = "sophgo,sg2042-pwm",
+ .data = &sg2042_chip_data,
+ }, {
+ .compatible = "sophgo,sg2044-pwm",
+ .data = &sg2044_chip_data,
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(of, sg2042_pwm_ids);
+
+static int pwm_sg2042_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ const struct sg2042_chip_data *chip_data;
+ struct sg2042_pwm_ddata *ddata;
+ struct reset_control *rst;
+ struct pwm_chip *chip;
+ struct clk *clk;
+ int ret;
+
+ chip_data = device_get_match_data(dev);
+ if (!chip_data)
+ return -ENODEV;
+
+ chip = devm_pwmchip_alloc(dev, SG2042_PWM_CHANNELNUM, sizeof(*ddata));
+ if (IS_ERR(chip))
+ return PTR_ERR(chip);
+ ddata = pwmchip_get_drvdata(chip);
+
+ ddata->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(ddata->base))
+ return PTR_ERR(ddata->base);
+
+ clk = devm_clk_get_enabled(dev, "apb");
+ if (IS_ERR(clk))
+ return dev_err_probe(dev, PTR_ERR(clk), "Failed to get base clk\n");
+
+ ret = devm_clk_rate_exclusive_get(dev, clk);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to get exclusive rate\n");
+
+ ddata->clk_rate_hz = clk_get_rate(clk);
+ /* period = PERIOD * NSEC_PER_SEC / clk_rate_hz */
+ if (!ddata->clk_rate_hz || ddata->clk_rate_hz > NSEC_PER_SEC)
+ return dev_err_probe(dev, -EINVAL,
+ "Invalid clock rate: %lu\n", ddata->clk_rate_hz);
+
+ rst = devm_reset_control_get_optional_shared_deasserted(dev, NULL);
+ if (IS_ERR(rst))
+ return dev_err_probe(dev, PTR_ERR(rst), "Failed to get reset\n");
+
+ chip->ops = &chip_data->ops;
+ chip->atomic = true;
+
+ ret = devm_pwmchip_add(dev, chip);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "Failed to register PWM chip\n");
+
+ return 0;
+}
+
+static struct platform_driver pwm_sg2042_driver = {
+ .driver = {
+ .name = "sg2042-pwm",
+ .of_match_table = sg2042_pwm_ids,
+ },
+ .probe = pwm_sg2042_probe,
+};
+module_platform_driver(pwm_sg2042_driver);
+
+MODULE_AUTHOR("Chen Wang");
+MODULE_AUTHOR("Longbin Li <looong.bin@gmail.com>");
+MODULE_DESCRIPTION("Sophgo SG2042 PWM driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/pwm/pwm-spear.c b/drivers/pwm/pwm-spear.c
new file mode 100644
index 000000000..4f372279f
--- /dev/null
+++ b/drivers/pwm/pwm-spear.c
@@ -0,0 +1,262 @@
+/*
+ * ST Microelectronics SPEAr Pulse Width Modulator driver
+ *
+ * Copyright (C) 2012 ST Microelectronics
+ * Shiraz Hashim <shiraz.linux.kernel@gmail.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include <linux/math64.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+
+#define NUM_PWM 4
+
+/* PWM registers and bits definitions */
+#define PWMCR 0x00 /* Control Register */
+#define PWMCR_PWM_ENABLE 0x1
+#define PWMCR_PRESCALE_SHIFT 2
+#define PWMCR_MIN_PRESCALE 0x00
+#define PWMCR_MAX_PRESCALE 0x3FFF
+
+#define PWMDCR 0x04 /* Duty Cycle Register */
+#define PWMDCR_MIN_DUTY 0x0001
+#define PWMDCR_MAX_DUTY 0xFFFF
+
+#define PWMPCR 0x08 /* Period Register */
+#define PWMPCR_MIN_PERIOD 0x0001
+#define PWMPCR_MAX_PERIOD 0xFFFF
+
+/* Following only available on 13xx SoCs */
+#define PWMMCR 0x3C /* Master Control Register */
+#define PWMMCR_PWM_ENABLE 0x1
+
+/**
+ * struct spear_pwm_chip - struct representing pwm chip
+ *
+ * @mmio_base: base address of pwm chip
+ * @clk: pointer to clk structure of pwm chip
+ */
+struct spear_pwm_chip {
+ void __iomem *mmio_base;
+ struct clk *clk;
+};
+
+static inline struct spear_pwm_chip *to_spear_pwm_chip(struct pwm_chip *chip)
+{
+ return pwmchip_get_drvdata(chip);
+}
+
+static inline u32 spear_pwm_readl(struct spear_pwm_chip *chip, unsigned int num,
+ unsigned long offset)
+{
+ return readl_relaxed(chip->mmio_base + (num << 4) + offset);
+}
+
+static inline void spear_pwm_writel(struct spear_pwm_chip *chip,
+ unsigned int num, unsigned long offset,
+ unsigned long val)
+{
+ writel_relaxed(val, chip->mmio_base + (num << 4) + offset);
+}
+
+static int spear_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
+ u64 duty_ns, u64 period_ns)
+{
+ struct spear_pwm_chip *pc = to_spear_pwm_chip(chip);
+ u64 val, div, clk_rate;
+ unsigned long prescale = PWMCR_MIN_PRESCALE, pv, dc;
+ int ret;
+
+ /*
+ * Find pv, dc and prescale to suit duty_ns and period_ns. This is done
+ * according to formulas described below:
+ *
+ * period_ns = 10^9 * (PRESCALE + 1) * PV / PWM_CLK_RATE
+ * duty_ns = 10^9 * (PRESCALE + 1) * DC / PWM_CLK_RATE
+ *
+ * PV = (PWM_CLK_RATE * period_ns) / (10^9 * (PRESCALE + 1))
+ * DC = (PWM_CLK_RATE * duty_ns) / (10^9 * (PRESCALE + 1))
+ */
+ clk_rate = clk_get_rate(pc->clk);
+ while (1) {
+ div = 1000000000;
+ div *= 1 + prescale;
+ val = clk_rate * period_ns;
+ pv = div64_u64(val, div);
+ val = clk_rate * duty_ns;
+ dc = div64_u64(val, div);
+
+ /* if duty_ns and period_ns are not achievable then return */
+ if (pv < PWMPCR_MIN_PERIOD || dc < PWMDCR_MIN_DUTY)
+ return -EINVAL;
+
+ /*
+ * if pv and dc have crossed their upper limit, then increase
+ * prescale and recalculate pv and dc.
+ */
+ if (pv > PWMPCR_MAX_PERIOD || dc > PWMDCR_MAX_DUTY) {
+ if (++prescale > PWMCR_MAX_PRESCALE)
+ return -EINVAL;
+ continue;
+ }
+ break;
+ }
+
+ /*
+ * NOTE: the clock to PWM has to be enabled first before writing to the
+ * registers.
+ */
+ ret = clk_enable(pc->clk);
+ if (ret)
+ return ret;
+
+ spear_pwm_writel(pc, pwm->hwpwm, PWMCR,
+ prescale << PWMCR_PRESCALE_SHIFT);
+ spear_pwm_writel(pc, pwm->hwpwm, PWMDCR, dc);
+ spear_pwm_writel(pc, pwm->hwpwm, PWMPCR, pv);
+ clk_disable(pc->clk);
+
+ return 0;
+}
+
+static int spear_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct spear_pwm_chip *pc = to_spear_pwm_chip(chip);
+ int rc = 0;
+ u32 val;
+
+ rc = clk_enable(pc->clk);
+ if (rc)
+ return rc;
+
+ val = spear_pwm_readl(pc, pwm->hwpwm, PWMCR);
+ val |= PWMCR_PWM_ENABLE;
+ spear_pwm_writel(pc, pwm->hwpwm, PWMCR, val);
+
+ return 0;
+}
+
+static void spear_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct spear_pwm_chip *pc = to_spear_pwm_chip(chip);
+ u32 val;
+
+ val = spear_pwm_readl(pc, pwm->hwpwm, PWMCR);
+ val &= ~PWMCR_PWM_ENABLE;
+ spear_pwm_writel(pc, pwm->hwpwm, PWMCR, val);
+
+ clk_disable(pc->clk);
+}
+
+static int spear_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
+ const struct pwm_state *state)
+{
+ int err;
+
+ if (state->polarity != PWM_POLARITY_NORMAL)
+ return -EINVAL;
+
+ if (!state->enabled) {
+ if (pwm->state.enabled)
+ spear_pwm_disable(chip, pwm);
+ return 0;
+ }
+
+ err = spear_pwm_config(chip, pwm, state->duty_cycle, state->period);
+ if (err)
+ return err;
+
+ if (!pwm->state.enabled)
+ return spear_pwm_enable(chip, pwm);
+
+ return 0;
+}
+
+static const struct pwm_ops spear_pwm_ops = {
+ .apply = spear_pwm_apply,
+};
+
+static int spear_pwm_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct pwm_chip *chip;
+ struct spear_pwm_chip *pc;
+ int ret;
+ u32 val;
+
+ chip = devm_pwmchip_alloc(&pdev->dev, NUM_PWM, sizeof(*pc));
+ if (IS_ERR(chip))
+ return PTR_ERR(chip);
+ pc = to_spear_pwm_chip(chip);
+
+ pc->mmio_base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(pc->mmio_base))
+ return PTR_ERR(pc->mmio_base);
+
+ pc->clk = devm_clk_get_prepared(&pdev->dev, NULL);
+ if (IS_ERR(pc->clk))
+ return dev_err_probe(&pdev->dev, PTR_ERR(pc->clk),
+ "Failed to get clock\n");
+
+ chip->ops = &spear_pwm_ops;
+
+ if (of_device_is_compatible(np, "st,spear1340-pwm")) {
+ ret = clk_enable(pc->clk);
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret,
+ "Failed to enable clk\n");
+
+ /*
+ * Following enables PWM chip, channels would still be
+ * enabled individually through their control register
+ */
+ val = readl_relaxed(pc->mmio_base + PWMMCR);
+ val |= PWMMCR_PWM_ENABLE;
+ writel_relaxed(val, pc->mmio_base + PWMMCR);
+
+ clk_disable(pc->clk);
+ }
+
+ ret = devm_pwmchip_add(&pdev->dev, chip);
+ if (ret < 0)
+ return dev_err_probe(&pdev->dev, ret, "pwmchip_add() failed\n");
+
+ return 0;
+}
+
+static const struct of_device_id spear_pwm_of_match[] = {
+ { .compatible = "st,spear320-pwm" },
+ { .compatible = "st,spear1340-pwm" },
+ { }
+};
+
+MODULE_DEVICE_TABLE(of, spear_pwm_of_match);
+
+static struct platform_driver spear_pwm_driver = {
+ .driver = {
+ .name = "spear-pwm",
+ .of_match_table = spear_pwm_of_match,
+ },
+ .probe = spear_pwm_probe,
+};
+
+module_platform_driver(spear_pwm_driver);
+
+MODULE_DESCRIPTION("ST Microelectronics SPEAr Pulse Width Modulator driver");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Shiraz Hashim <shiraz.linux.kernel@gmail.com>");
+MODULE_AUTHOR("Viresh Kumar <viresh.kumar@linaro.com>");
+MODULE_ALIAS("platform:spear-pwm");
diff --git a/drivers/pwm/pwm-sprd.c b/drivers/pwm/pwm-sprd.c
new file mode 100644
index 000000000..51cab6a54
--- /dev/null
+++ b/drivers/pwm/pwm-sprd.c
@@ -0,0 +1,295 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2019 Spreadtrum Communications Inc.
+ */
+
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/math64.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+
+#define SPRD_PWM_PRESCALE 0x0
+#define SPRD_PWM_MOD 0x4
+#define SPRD_PWM_DUTY 0x8
+#define SPRD_PWM_ENABLE 0x18
+
+#define SPRD_PWM_MOD_MAX GENMASK(7, 0)
+#define SPRD_PWM_DUTY_MSK GENMASK(15, 0)
+#define SPRD_PWM_PRESCALE_MSK GENMASK(7, 0)
+#define SPRD_PWM_ENABLE_BIT BIT(0)
+
+#define SPRD_PWM_CHN_NUM 4
+#define SPRD_PWM_REGS_SHIFT 5
+#define SPRD_PWM_CHN_CLKS_NUM 2
+#define SPRD_PWM_CHN_OUTPUT_CLK 1
+
+struct sprd_pwm_chn {
+ struct clk_bulk_data clks[SPRD_PWM_CHN_CLKS_NUM];
+ u32 clk_rate;
+};
+
+struct sprd_pwm_chip {
+ void __iomem *base;
+ struct sprd_pwm_chn chn[SPRD_PWM_CHN_NUM];
+};
+
+static inline struct sprd_pwm_chip* sprd_pwm_from_chip(struct pwm_chip *chip)
+{
+ return pwmchip_get_drvdata(chip);
+}
+
+/*
+ * The list of clocks required by PWM channels, and each channel has 2 clocks:
+ * enable clock and pwm clock.
+ */
+static const char * const sprd_pwm_clks[] = {
+ "enable0", "pwm0",
+ "enable1", "pwm1",
+ "enable2", "pwm2",
+ "enable3", "pwm3",
+};
+
+static u32 sprd_pwm_read(struct sprd_pwm_chip *spc, u32 hwid, u32 reg)
+{
+ u32 offset = reg + (hwid << SPRD_PWM_REGS_SHIFT);
+
+ return readl_relaxed(spc->base + offset);
+}
+
+static void sprd_pwm_write(struct sprd_pwm_chip *spc, u32 hwid,
+ u32 reg, u32 val)
+{
+ u32 offset = reg + (hwid << SPRD_PWM_REGS_SHIFT);
+
+ writel_relaxed(val, spc->base + offset);
+}
+
+static int sprd_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm,
+ struct pwm_state *state)
+{
+ struct sprd_pwm_chip *spc = sprd_pwm_from_chip(chip);
+ struct sprd_pwm_chn *chn = &spc->chn[pwm->hwpwm];
+ u32 val, duty, prescale;
+ u64 tmp;
+ int ret;
+
+ /*
+ * The clocks to PWM channel has to be enabled first before
+ * reading to the registers.
+ */
+ ret = clk_bulk_prepare_enable(SPRD_PWM_CHN_CLKS_NUM, chn->clks);
+ if (ret) {
+ dev_err(pwmchip_parent(chip), "failed to enable pwm%u clocks\n",
+ pwm->hwpwm);
+ return ret;
+ }
+
+ val = sprd_pwm_read(spc, pwm->hwpwm, SPRD_PWM_ENABLE);
+ if (val & SPRD_PWM_ENABLE_BIT)
+ state->enabled = true;
+ else
+ state->enabled = false;
+
+ /*
+ * The hardware provides a counter that is feed by the source clock.
+ * The period length is (PRESCALE + 1) * MOD counter steps.
+ * The duty cycle length is (PRESCALE + 1) * DUTY counter steps.
+ * Thus the period_ns and duty_ns calculation formula should be:
+ * period_ns = NSEC_PER_SEC * (prescale + 1) * mod / clk_rate
+ * duty_ns = NSEC_PER_SEC * (prescale + 1) * duty / clk_rate
+ */
+ val = sprd_pwm_read(spc, pwm->hwpwm, SPRD_PWM_PRESCALE);
+ prescale = val & SPRD_PWM_PRESCALE_MSK;
+ tmp = (prescale + 1) * NSEC_PER_SEC * SPRD_PWM_MOD_MAX;
+ state->period = DIV_ROUND_CLOSEST_ULL(tmp, chn->clk_rate);
+
+ val = sprd_pwm_read(spc, pwm->hwpwm, SPRD_PWM_DUTY);
+ duty = val & SPRD_PWM_DUTY_MSK;
+ tmp = (prescale + 1) * NSEC_PER_SEC * duty;
+ state->duty_cycle = DIV_ROUND_CLOSEST_ULL(tmp, chn->clk_rate);
+ state->polarity = PWM_POLARITY_NORMAL;
+
+ /* Disable PWM clocks if the PWM channel is not in enable state. */
+ if (!state->enabled)
+ clk_bulk_disable_unprepare(SPRD_PWM_CHN_CLKS_NUM, chn->clks);
+
+ return 0;
+}
+
+static int sprd_pwm_config(struct sprd_pwm_chip *spc, struct pwm_device *pwm,
+ int duty_ns, int period_ns)
+{
+ struct sprd_pwm_chn *chn = &spc->chn[pwm->hwpwm];
+ u32 prescale, duty;
+ u64 tmp;
+
+ /*
+ * The hardware provides a counter that is feed by the source clock.
+ * The period length is (PRESCALE + 1) * MOD counter steps.
+ * The duty cycle length is (PRESCALE + 1) * DUTY counter steps.
+ *
+ * To keep the maths simple we're always using MOD = SPRD_PWM_MOD_MAX.
+ * The value for PRESCALE is selected such that the resulting period
+ * gets the maximal length not bigger than the requested one with the
+ * given settings (MOD = SPRD_PWM_MOD_MAX and input clock).
+ */
+ duty = duty_ns * SPRD_PWM_MOD_MAX / period_ns;
+
+ tmp = (u64)chn->clk_rate * period_ns;
+ do_div(tmp, NSEC_PER_SEC);
+ prescale = DIV_ROUND_CLOSEST_ULL(tmp, SPRD_PWM_MOD_MAX) - 1;
+ if (prescale > SPRD_PWM_PRESCALE_MSK)
+ prescale = SPRD_PWM_PRESCALE_MSK;
+
+ /*
+ * Note: Writing DUTY triggers the hardware to actually apply the
+ * values written to MOD and DUTY to the output, so must keep writing
+ * DUTY last.
+ *
+ * The hardware can ensures that current running period is completed
+ * before changing a new configuration to avoid mixed settings.
+ */
+ sprd_pwm_write(spc, pwm->hwpwm, SPRD_PWM_PRESCALE, prescale);
+ sprd_pwm_write(spc, pwm->hwpwm, SPRD_PWM_MOD, SPRD_PWM_MOD_MAX);
+ sprd_pwm_write(spc, pwm->hwpwm, SPRD_PWM_DUTY, duty);
+
+ return 0;
+}
+
+static int sprd_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
+ const struct pwm_state *state)
+{
+ struct sprd_pwm_chip *spc = sprd_pwm_from_chip(chip);
+ struct sprd_pwm_chn *chn = &spc->chn[pwm->hwpwm];
+ struct pwm_state *cstate = &pwm->state;
+ int ret;
+
+ if (state->polarity != PWM_POLARITY_NORMAL)
+ return -EINVAL;
+
+ if (state->enabled) {
+ if (!cstate->enabled) {
+ /*
+ * The clocks to PWM channel has to be enabled first
+ * before writing to the registers.
+ */
+ ret = clk_bulk_prepare_enable(SPRD_PWM_CHN_CLKS_NUM,
+ chn->clks);
+ if (ret) {
+ dev_err(pwmchip_parent(chip),
+ "failed to enable pwm%u clocks\n",
+ pwm->hwpwm);
+ return ret;
+ }
+ }
+
+ ret = sprd_pwm_config(spc, pwm, state->duty_cycle,
+ state->period);
+ if (ret)
+ return ret;
+
+ sprd_pwm_write(spc, pwm->hwpwm, SPRD_PWM_ENABLE, 1);
+ } else if (cstate->enabled) {
+ /*
+ * Note: After setting SPRD_PWM_ENABLE to zero, the controller
+ * will not wait for current period to be completed, instead it
+ * will stop the PWM channel immediately.
+ */
+ sprd_pwm_write(spc, pwm->hwpwm, SPRD_PWM_ENABLE, 0);
+
+ clk_bulk_disable_unprepare(SPRD_PWM_CHN_CLKS_NUM, chn->clks);
+ }
+
+ return 0;
+}
+
+static const struct pwm_ops sprd_pwm_ops = {
+ .apply = sprd_pwm_apply,
+ .get_state = sprd_pwm_get_state,
+};
+
+static int sprd_pwm_clk_init(struct device *dev,
+ struct sprd_pwm_chn chn[SPRD_PWM_CHN_NUM])
+{
+ struct clk *clk_pwm;
+ int ret, i;
+
+ for (i = 0; i < SPRD_PWM_CHN_NUM; i++) {
+ int j;
+
+ for (j = 0; j < SPRD_PWM_CHN_CLKS_NUM; ++j)
+ chn[i].clks[j].id =
+ sprd_pwm_clks[i * SPRD_PWM_CHN_CLKS_NUM + j];
+
+ ret = devm_clk_bulk_get(dev, SPRD_PWM_CHN_CLKS_NUM,
+ chn[i].clks);
+ if (ret) {
+ if (ret == -ENOENT)
+ break;
+
+ return dev_err_probe(dev, ret,
+ "failed to get channel clocks\n");
+ }
+
+ clk_pwm = chn[i].clks[SPRD_PWM_CHN_OUTPUT_CLK].clk;
+ chn[i].clk_rate = clk_get_rate(clk_pwm);
+ }
+
+ if (!i)
+ return dev_err_probe(dev, -ENODEV, "no available PWM channels\n");
+
+ return i;
+}
+
+static int sprd_pwm_probe(struct platform_device *pdev)
+{
+ struct pwm_chip *chip;
+ struct sprd_pwm_chip *spc;
+ struct sprd_pwm_chn chn[SPRD_PWM_CHN_NUM];
+ int ret, npwm;
+
+ npwm = sprd_pwm_clk_init(&pdev->dev, chn);
+ if (npwm < 0)
+ return npwm;
+
+ chip = devm_pwmchip_alloc(&pdev->dev, npwm, sizeof(*spc));
+ if (IS_ERR(chip))
+ return PTR_ERR(chip);
+ spc = sprd_pwm_from_chip(chip);
+
+ spc->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(spc->base))
+ return PTR_ERR(spc->base);
+
+ memcpy(spc->chn, chn, sizeof(chn));
+
+ chip->ops = &sprd_pwm_ops;
+
+ ret = devm_pwmchip_add(&pdev->dev, chip);
+ if (ret)
+ dev_err(&pdev->dev, "failed to add PWM chip\n");
+
+ return ret;
+}
+
+static const struct of_device_id sprd_pwm_of_match[] = {
+ { .compatible = "sprd,ums512-pwm" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, sprd_pwm_of_match);
+
+static struct platform_driver sprd_pwm_driver = {
+ .driver = {
+ .name = "sprd-pwm",
+ .of_match_table = sprd_pwm_of_match,
+ },
+ .probe = sprd_pwm_probe,
+};
+
+module_platform_driver(sprd_pwm_driver);
+
+MODULE_DESCRIPTION("Spreadtrum PWM Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/pwm/pwm-sti.c b/drivers/pwm/pwm-sti.c
new file mode 100644
index 000000000..e5c3cf594
--- /dev/null
+++ b/drivers/pwm/pwm-sti.c
@@ -0,0 +1,644 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * PWM device driver for ST SoCs
+ *
+ * Copyright (C) 2013-2016 STMicroelectronics (R&D) Limited
+ *
+ * Author: Ajit Pal Singh <ajitpal.singh@st.com>
+ * Lee Jones <lee.jones@linaro.org>
+ */
+
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/math64.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+#include <linux/regmap.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/time.h>
+#include <linux/wait.h>
+
+#define PWM_OUT_VAL(x) (0x00 + (4 * (x))) /* Device's Duty Cycle register */
+#define PWM_CPT_VAL(x) (0x10 + (4 * (x))) /* Capture value */
+#define PWM_CPT_EDGE(x) (0x30 + (4 * (x))) /* Edge to capture on */
+
+#define STI_PWM_CTRL 0x50 /* Control/Config register */
+#define STI_INT_EN 0x54 /* Interrupt Enable/Disable register */
+#define STI_INT_STA 0x58 /* Interrupt Status register */
+#define PWM_INT_ACK 0x5c
+#define PWM_PRESCALE_LOW_MASK 0x0f
+#define PWM_PRESCALE_HIGH_MASK 0xf0
+#define PWM_CPT_EDGE_MASK 0x03
+#define PWM_INT_ACK_MASK 0x1ff
+
+#define STI_MAX_CPT_DEVS 4
+#define CPT_DC_MAX 0xff
+
+/* Regfield IDs */
+enum {
+ /* Bits in PWM_CTRL*/
+ PWMCLK_PRESCALE_LOW,
+ PWMCLK_PRESCALE_HIGH,
+ CPTCLK_PRESCALE,
+
+ PWM_OUT_EN,
+ PWM_CPT_EN,
+
+ PWM_CPT_INT_EN,
+ PWM_CPT_INT_STAT,
+
+ /* Keep last */
+ MAX_REGFIELDS
+};
+
+/*
+ * Each capture input can be programmed to detect rising-edge, falling-edge,
+ * either edge or neither egde.
+ */
+enum sti_cpt_edge {
+ CPT_EDGE_DISABLED,
+ CPT_EDGE_RISING,
+ CPT_EDGE_FALLING,
+ CPT_EDGE_BOTH,
+};
+
+struct sti_cpt_ddata {
+ u32 snapshot[3];
+ unsigned int index;
+ struct mutex lock;
+ wait_queue_head_t wait;
+};
+
+struct sti_pwm_chip {
+ struct device *dev;
+ struct clk *pwm_clk;
+ struct clk *cpt_clk;
+ struct regmap *regmap;
+ unsigned int pwm_num_devs;
+ unsigned int cpt_num_devs;
+ unsigned int max_pwm_cnt;
+ unsigned int max_prescale;
+ struct sti_cpt_ddata *ddata;
+ struct regmap_field *prescale_low;
+ struct regmap_field *prescale_high;
+ struct regmap_field *pwm_out_en;
+ struct regmap_field *pwm_cpt_en;
+ struct regmap_field *pwm_cpt_int_en;
+ struct regmap_field *pwm_cpt_int_stat;
+ struct pwm_device *cur;
+ unsigned long configured;
+ unsigned int en_count;
+ void __iomem *mmio;
+};
+
+static const struct reg_field sti_pwm_regfields[MAX_REGFIELDS] = {
+ [PWMCLK_PRESCALE_LOW] = REG_FIELD(STI_PWM_CTRL, 0, 3),
+ [PWMCLK_PRESCALE_HIGH] = REG_FIELD(STI_PWM_CTRL, 11, 14),
+ [CPTCLK_PRESCALE] = REG_FIELD(STI_PWM_CTRL, 4, 8),
+ [PWM_OUT_EN] = REG_FIELD(STI_PWM_CTRL, 9, 9),
+ [PWM_CPT_EN] = REG_FIELD(STI_PWM_CTRL, 10, 10),
+ [PWM_CPT_INT_EN] = REG_FIELD(STI_INT_EN, 1, 4),
+ [PWM_CPT_INT_STAT] = REG_FIELD(STI_INT_STA, 1, 4),
+};
+
+static inline struct sti_pwm_chip *to_sti_pwmchip(struct pwm_chip *chip)
+{
+ return pwmchip_get_drvdata(chip);
+}
+
+/*
+ * Calculate the prescaler value corresponding to the period.
+ */
+static int sti_pwm_get_prescale(struct sti_pwm_chip *pc, unsigned long period,
+ unsigned int *prescale)
+{
+ unsigned long clk_rate;
+ unsigned long value;
+ unsigned int ps;
+
+ clk_rate = clk_get_rate(pc->pwm_clk);
+ if (!clk_rate) {
+ dev_err(pc->dev, "failed to get clock rate\n");
+ return -EINVAL;
+ }
+
+ /*
+ * prescale = ((period_ns * clk_rate) / (10^9 * (max_pwm_cnt + 1)) - 1
+ */
+ value = NSEC_PER_SEC / clk_rate;
+ value *= pc->max_pwm_cnt + 1;
+
+ if (period % value)
+ return -EINVAL;
+
+ ps = period / value - 1;
+ if (ps > pc->max_prescale)
+ return -EINVAL;
+
+ *prescale = ps;
+
+ return 0;
+}
+
+/*
+ * For STiH4xx PWM IP, the PWM period is fixed to 256 local clock cycles. The
+ * only way to change the period (apart from changing the PWM input clock) is
+ * to change the PWM clock prescaler.
+ *
+ * The prescaler is of 8 bits, so 256 prescaler values and hence 256 possible
+ * period values are supported (for a particular clock rate). The requested
+ * period will be applied only if it matches one of these 256 values.
+ */
+static int sti_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
+ int duty_ns, int period_ns)
+{
+ struct sti_pwm_chip *pc = to_sti_pwmchip(chip);
+ unsigned int ncfg, value, prescale = 0;
+ struct pwm_device *cur = pc->cur;
+ struct device *dev = pc->dev;
+ bool period_same = false;
+ int ret;
+
+ ncfg = hweight_long(pc->configured);
+ if (ncfg)
+ period_same = (period_ns == pwm_get_period(cur));
+
+ /*
+ * Allow configuration changes if one of the following conditions
+ * satisfy.
+ * 1. No devices have been configured.
+ * 2. Only one device has been configured and the new request is for
+ * the same device.
+ * 3. Only one device has been configured and the new request is for
+ * a new device and period of the new device is same as the current
+ * configured period.
+ * 4. More than one devices are configured and period of the new
+ * requestis the same as the current period.
+ */
+ if (!ncfg ||
+ ((ncfg == 1) && (pwm->hwpwm == cur->hwpwm)) ||
+ ((ncfg == 1) && (pwm->hwpwm != cur->hwpwm) && period_same) ||
+ ((ncfg > 1) && period_same)) {
+ /* Enable clock before writing to PWM registers. */
+ ret = clk_enable(pc->pwm_clk);
+ if (ret)
+ return ret;
+
+ ret = clk_enable(pc->cpt_clk);
+ if (ret)
+ return ret;
+
+ if (!period_same) {
+ ret = sti_pwm_get_prescale(pc, period_ns, &prescale);
+ if (ret)
+ goto clk_dis;
+
+ value = prescale & PWM_PRESCALE_LOW_MASK;
+
+ ret = regmap_field_write(pc->prescale_low, value);
+ if (ret)
+ goto clk_dis;
+
+ value = (prescale & PWM_PRESCALE_HIGH_MASK) >> 4;
+
+ ret = regmap_field_write(pc->prescale_high, value);
+ if (ret)
+ goto clk_dis;
+ }
+
+ /*
+ * When PWMVal == 0, PWM pulse = 1 local clock cycle.
+ * When PWMVal == max_pwm_count,
+ * PWM pulse = (max_pwm_count + 1) local cycles,
+ * that is continuous pulse: signal never goes low.
+ */
+ value = pc->max_pwm_cnt * duty_ns / period_ns;
+
+ ret = regmap_write(pc->regmap, PWM_OUT_VAL(pwm->hwpwm), value);
+ if (ret)
+ goto clk_dis;
+
+ ret = regmap_field_write(pc->pwm_cpt_int_en, 0);
+
+ set_bit(pwm->hwpwm, &pc->configured);
+ pc->cur = pwm;
+
+ dev_dbg(dev, "prescale:%u, period:%i, duty:%i, value:%u\n",
+ prescale, period_ns, duty_ns, value);
+ } else {
+ return -EINVAL;
+ }
+
+clk_dis:
+ clk_disable(pc->pwm_clk);
+ clk_disable(pc->cpt_clk);
+ return ret;
+}
+
+static int sti_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct sti_pwm_chip *pc = to_sti_pwmchip(chip);
+ struct device *dev = pc->dev;
+ int ret;
+
+ /*
+ * Since we have a common enable for all PWM devices, do not enable if
+ * already enabled.
+ */
+
+ if (!pc->en_count) {
+ ret = clk_enable(pc->pwm_clk);
+ if (ret)
+ return ret;
+
+ ret = clk_enable(pc->cpt_clk);
+ if (ret)
+ return ret;
+
+ ret = regmap_field_write(pc->pwm_out_en, 1);
+ if (ret) {
+ dev_err(dev, "failed to enable PWM device %u: %d\n",
+ pwm->hwpwm, ret);
+ return ret;
+ }
+ }
+
+ pc->en_count++;
+
+ return 0;
+}
+
+static void sti_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct sti_pwm_chip *pc = to_sti_pwmchip(chip);
+
+ if (--pc->en_count)
+ return;
+
+ regmap_field_write(pc->pwm_out_en, 0);
+
+ clk_disable(pc->pwm_clk);
+ clk_disable(pc->cpt_clk);
+}
+
+static void sti_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct sti_pwm_chip *pc = to_sti_pwmchip(chip);
+
+ clear_bit(pwm->hwpwm, &pc->configured);
+}
+
+static int sti_pwm_capture(struct pwm_chip *chip, struct pwm_device *pwm,
+ struct pwm_capture *result, unsigned long timeout)
+{
+ struct sti_pwm_chip *pc = to_sti_pwmchip(chip);
+ struct sti_cpt_ddata *ddata = &pc->ddata[pwm->hwpwm];
+ struct device *dev = pc->dev;
+ unsigned int effective_ticks;
+ unsigned long long high, low;
+ int ret;
+
+ if (pwm->hwpwm >= pc->cpt_num_devs) {
+ dev_err(dev, "device %u is not valid\n", pwm->hwpwm);
+ return -EINVAL;
+ }
+
+ mutex_lock(&ddata->lock);
+ ddata->index = 0;
+
+ /* Prepare capture measurement */
+ regmap_write(pc->regmap, PWM_CPT_EDGE(pwm->hwpwm), CPT_EDGE_RISING);
+ regmap_field_write(pc->pwm_cpt_int_en, BIT(pwm->hwpwm));
+
+ /* Enable capture */
+ ret = regmap_field_write(pc->pwm_cpt_en, 1);
+ if (ret) {
+ dev_err(dev, "failed to enable PWM capture %u: %d\n",
+ pwm->hwpwm, ret);
+ goto out;
+ }
+
+ ret = wait_event_interruptible_timeout(ddata->wait, ddata->index > 1,
+ msecs_to_jiffies(timeout));
+
+ regmap_write(pc->regmap, PWM_CPT_EDGE(pwm->hwpwm), CPT_EDGE_DISABLED);
+
+ if (ret == -ERESTARTSYS)
+ goto out;
+
+ switch (ddata->index) {
+ case 0:
+ case 1:
+ /*
+ * Getting here could mean:
+ * - input signal is constant of less than 1 Hz
+ * - there is no input signal at all
+ *
+ * In such case the frequency is rounded down to 0
+ */
+ result->period = 0;
+ result->duty_cycle = 0;
+
+ break;
+
+ case 2:
+ /* We have everying we need */
+ high = ddata->snapshot[1] - ddata->snapshot[0];
+ low = ddata->snapshot[2] - ddata->snapshot[1];
+
+ effective_ticks = clk_get_rate(pc->cpt_clk);
+
+ result->period = (high + low) * NSEC_PER_SEC;
+ result->period /= effective_ticks;
+
+ result->duty_cycle = high * NSEC_PER_SEC;
+ result->duty_cycle /= effective_ticks;
+
+ break;
+
+ default:
+ dev_err(dev, "internal error\n");
+ break;
+ }
+
+out:
+ /* Disable capture */
+ regmap_field_write(pc->pwm_cpt_en, 0);
+
+ mutex_unlock(&ddata->lock);
+ return ret;
+}
+
+static int sti_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
+ const struct pwm_state *state)
+{
+ struct sti_pwm_chip *pc = to_sti_pwmchip(chip);
+ struct device *dev = pc->dev;
+ int err;
+
+ if (pwm->hwpwm >= pc->pwm_num_devs) {
+ dev_err(dev, "device %u is not valid for pwm mode\n",
+ pwm->hwpwm);
+ return -EINVAL;
+ }
+
+ if (state->polarity != PWM_POLARITY_NORMAL)
+ return -EINVAL;
+
+ if (!state->enabled) {
+ if (pwm->state.enabled)
+ sti_pwm_disable(chip, pwm);
+
+ return 0;
+ }
+
+ err = sti_pwm_config(chip, pwm, state->duty_cycle, state->period);
+ if (err)
+ return err;
+
+ if (!pwm->state.enabled)
+ err = sti_pwm_enable(chip, pwm);
+
+ return err;
+}
+
+static const struct pwm_ops sti_pwm_ops = {
+ .capture = sti_pwm_capture,
+ .apply = sti_pwm_apply,
+ .free = sti_pwm_free,
+};
+
+static irqreturn_t sti_pwm_interrupt(int irq, void *data)
+{
+ struct sti_pwm_chip *pc = data;
+ struct device *dev = pc->dev;
+ struct sti_cpt_ddata *ddata;
+ int devicenum;
+ unsigned int cpt_int_stat;
+ unsigned int reg;
+ int ret = IRQ_NONE;
+
+ ret = regmap_field_read(pc->pwm_cpt_int_stat, &cpt_int_stat);
+ if (ret)
+ return ret;
+
+ while (cpt_int_stat) {
+ devicenum = ffs(cpt_int_stat) - 1;
+
+ ddata = &pc->ddata[devicenum];
+
+ /*
+ * Capture input:
+ * _______ _______
+ * | | | |
+ * __| |_________________| |________
+ * ^0 ^1 ^2
+ *
+ * Capture start by the first available rising edge. When a
+ * capture event occurs, capture value (CPT_VALx) is stored,
+ * index incremented, capture edge changed.
+ *
+ * After the capture, if the index > 1, we have collected the
+ * necessary data so we signal the thread waiting for it and
+ * disable the capture by setting capture edge to none
+ */
+
+ regmap_read(pc->regmap,
+ PWM_CPT_VAL(devicenum),
+ &ddata->snapshot[ddata->index]);
+
+ switch (ddata->index) {
+ case 0:
+ case 1:
+ regmap_read(pc->regmap, PWM_CPT_EDGE(devicenum), &reg);
+ reg ^= PWM_CPT_EDGE_MASK;
+ regmap_write(pc->regmap, PWM_CPT_EDGE(devicenum), reg);
+
+ ddata->index++;
+ break;
+
+ case 2:
+ regmap_write(pc->regmap,
+ PWM_CPT_EDGE(devicenum),
+ CPT_EDGE_DISABLED);
+ wake_up(&ddata->wait);
+ break;
+
+ default:
+ dev_err(dev, "Internal error\n");
+ }
+
+ cpt_int_stat &= ~BIT_MASK(devicenum);
+
+ ret = IRQ_HANDLED;
+ }
+
+ /* Just ACK everything */
+ regmap_write(pc->regmap, PWM_INT_ACK, PWM_INT_ACK_MASK);
+
+ return ret;
+}
+
+static int sti_pwm_probe_regmap(struct sti_pwm_chip *pc)
+{
+ struct device *dev = pc->dev;
+
+ pc->prescale_low = devm_regmap_field_alloc(dev, pc->regmap,
+ sti_pwm_regfields[PWMCLK_PRESCALE_LOW]);
+ if (IS_ERR(pc->prescale_low))
+ return PTR_ERR(pc->prescale_low);
+
+ pc->prescale_high = devm_regmap_field_alloc(dev, pc->regmap,
+ sti_pwm_regfields[PWMCLK_PRESCALE_HIGH]);
+ if (IS_ERR(pc->prescale_high))
+ return PTR_ERR(pc->prescale_high);
+
+ pc->pwm_out_en = devm_regmap_field_alloc(dev, pc->regmap,
+ sti_pwm_regfields[PWM_OUT_EN]);
+ if (IS_ERR(pc->pwm_out_en))
+ return PTR_ERR(pc->pwm_out_en);
+
+ pc->pwm_cpt_en = devm_regmap_field_alloc(dev, pc->regmap,
+ sti_pwm_regfields[PWM_CPT_EN]);
+ if (IS_ERR(pc->pwm_cpt_en))
+ return PTR_ERR(pc->pwm_cpt_en);
+
+ pc->pwm_cpt_int_en = devm_regmap_field_alloc(dev, pc->regmap,
+ sti_pwm_regfields[PWM_CPT_INT_EN]);
+ if (IS_ERR(pc->pwm_cpt_int_en))
+ return PTR_ERR(pc->pwm_cpt_int_en);
+
+ pc->pwm_cpt_int_stat = devm_regmap_field_alloc(dev, pc->regmap,
+ sti_pwm_regfields[PWM_CPT_INT_STAT]);
+ if (PTR_ERR_OR_ZERO(pc->pwm_cpt_int_stat))
+ return PTR_ERR(pc->pwm_cpt_int_stat);
+
+ return 0;
+}
+
+static const struct regmap_config sti_pwm_regmap_config = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+};
+
+static int sti_pwm_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+ u32 num_devs;
+ unsigned int pwm_num_devs = 0;
+ unsigned int cpt_num_devs = 0;
+ struct pwm_chip *chip;
+ struct sti_pwm_chip *pc;
+ unsigned int i;
+ int irq, ret;
+
+ ret = of_property_read_u32(np, "st,pwm-num-chan", &num_devs);
+ if (!ret)
+ pwm_num_devs = num_devs;
+
+ ret = of_property_read_u32(np, "st,capture-num-chan", &num_devs);
+ if (!ret)
+ cpt_num_devs = num_devs;
+
+ if (!pwm_num_devs && !cpt_num_devs)
+ return dev_err_probe(dev, -EINVAL, "No channels configured\n");
+
+ chip = devm_pwmchip_alloc(dev, max(pwm_num_devs, cpt_num_devs), sizeof(*pc));
+ if (IS_ERR(chip))
+ return PTR_ERR(chip);
+ pc = to_sti_pwmchip(chip);
+
+ pc->mmio = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(pc->mmio))
+ return PTR_ERR(pc->mmio);
+
+ pc->regmap = devm_regmap_init_mmio(dev, pc->mmio,
+ &sti_pwm_regmap_config);
+ if (IS_ERR(pc->regmap))
+ return dev_err_probe(dev, PTR_ERR(pc->regmap),
+ "Failed to initialize regmap\n");
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
+
+ ret = devm_request_irq(&pdev->dev, irq, sti_pwm_interrupt, 0,
+ pdev->name, pc);
+ if (ret < 0)
+ dev_err_probe(&pdev->dev, ret, "Failed to request IRQ\n");
+
+ /*
+ * Setup PWM data with default values: some values could be replaced
+ * with specific ones provided from Device Tree.
+ */
+ pc->max_prescale = 0xff;
+ pc->max_pwm_cnt = 255;
+ pc->pwm_num_devs = pwm_num_devs;
+ pc->cpt_num_devs = cpt_num_devs;
+
+ pc->dev = dev;
+ pc->en_count = 0;
+
+ ret = sti_pwm_probe_regmap(pc);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to initialize regmap fields\n");
+
+ if (pwm_num_devs) {
+ pc->pwm_clk = devm_clk_get_prepared(dev, "pwm");
+ if (IS_ERR(pc->pwm_clk))
+ return dev_err_probe(dev, PTR_ERR(pc->pwm_clk),
+ "failed to get PWM clock\n");
+ }
+
+ if (cpt_num_devs) {
+ pc->cpt_clk = devm_clk_get_prepared(dev, "capture");
+ if (IS_ERR(pc->cpt_clk))
+ return dev_err_probe(dev, PTR_ERR(pc->cpt_clk),
+ "failed to get PWM capture clock\n");
+
+ pc->ddata = devm_kcalloc(dev, cpt_num_devs,
+ sizeof(*pc->ddata), GFP_KERNEL);
+ if (!pc->ddata)
+ return -ENOMEM;
+
+ for (i = 0; i < cpt_num_devs; i++) {
+ struct sti_cpt_ddata *ddata = &pc->ddata[i];
+
+ init_waitqueue_head(&ddata->wait);
+ mutex_init(&ddata->lock);
+ }
+ }
+
+ chip->ops = &sti_pwm_ops;
+
+ ret = devm_pwmchip_add(dev, chip);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to register pwm chip\n");
+
+ return 0;
+}
+
+static const struct of_device_id sti_pwm_of_match[] = {
+ { .compatible = "st,sti-pwm" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, sti_pwm_of_match);
+
+static struct platform_driver sti_pwm_driver = {
+ .driver = {
+ .name = "sti-pwm",
+ .of_match_table = sti_pwm_of_match,
+ },
+ .probe = sti_pwm_probe,
+};
+module_platform_driver(sti_pwm_driver);
+
+MODULE_AUTHOR("Ajit Pal Singh <ajitpal.singh@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics ST PWM driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/pwm/pwm-stm32-lp.c b/drivers/pwm/pwm-stm32-lp.c
new file mode 100644
index 000000000..2aa88f498
--- /dev/null
+++ b/drivers/pwm/pwm-stm32-lp.c
@@ -0,0 +1,432 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * STM32 Low-Power Timer PWM driver
+ *
+ * Copyright (C) STMicroelectronics 2017
+ *
+ * Author: Gerald Baeza <gerald.baeza@st.com>
+ *
+ * Inspired by Gerald Baeza's pwm-stm32 driver
+ */
+
+#include <linux/bitfield.h>
+#include <linux/mfd/stm32-lptimer.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+
+struct stm32_pwm_lp {
+ struct clk *clk;
+ struct regmap *regmap;
+ unsigned int num_cc_chans;
+};
+
+static inline struct stm32_pwm_lp *to_stm32_pwm_lp(struct pwm_chip *chip)
+{
+ return pwmchip_get_drvdata(chip);
+}
+
+/* STM32 Low-Power Timer is preceded by a configurable power-of-2 prescaler */
+#define STM32_LPTIM_MAX_PRESCALER 128
+
+static int stm32_pwm_lp_update_allowed(struct stm32_pwm_lp *priv, int channel)
+{
+ int ret;
+ u32 ccmr1;
+ unsigned long ccmr;
+
+ /* Only one PWM on this LPTIMER: enable, prescaler and reload value can be changed */
+ if (!priv->num_cc_chans)
+ return true;
+
+ ret = regmap_read(priv->regmap, STM32_LPTIM_CCMR1, &ccmr1);
+ if (ret)
+ return ret;
+ ccmr = ccmr1 & (STM32_LPTIM_CC1E | STM32_LPTIM_CC2E);
+
+ /* More than one channel enabled: enable, prescaler or ARR value can't be changed */
+ if (bitmap_weight(&ccmr, sizeof(u32) * BITS_PER_BYTE) > 1)
+ return false;
+
+ /*
+ * Only one channel is enabled (or none): check status on the other channel, to
+ * report if enable, prescaler or ARR value can be changed.
+ */
+ if (channel)
+ return !(ccmr1 & STM32_LPTIM_CC1E);
+ else
+ return !(ccmr1 & STM32_LPTIM_CC2E);
+}
+
+static int stm32_pwm_lp_compare_channel_apply(struct stm32_pwm_lp *priv, int channel,
+ bool enable, enum pwm_polarity polarity)
+{
+ u32 ccmr1, val, mask;
+ bool reenable;
+ int ret;
+
+ /* No dedicated CC channel: nothing to do */
+ if (!priv->num_cc_chans)
+ return 0;
+
+ ret = regmap_read(priv->regmap, STM32_LPTIM_CCMR1, &ccmr1);
+ if (ret)
+ return ret;
+
+ if (channel) {
+ /* Must disable CC channel (CCxE) to modify polarity (CCxP), then re-enable */
+ reenable = (enable && FIELD_GET(STM32_LPTIM_CC2E, ccmr1)) &&
+ (polarity != FIELD_GET(STM32_LPTIM_CC2P, ccmr1));
+
+ mask = STM32_LPTIM_CC2SEL | STM32_LPTIM_CC2E | STM32_LPTIM_CC2P;
+ val = FIELD_PREP(STM32_LPTIM_CC2P, polarity);
+ val |= FIELD_PREP(STM32_LPTIM_CC2E, enable);
+ } else {
+ reenable = (enable && FIELD_GET(STM32_LPTIM_CC1E, ccmr1)) &&
+ (polarity != FIELD_GET(STM32_LPTIM_CC1P, ccmr1));
+
+ mask = STM32_LPTIM_CC1SEL | STM32_LPTIM_CC1E | STM32_LPTIM_CC1P;
+ val = FIELD_PREP(STM32_LPTIM_CC1P, polarity);
+ val |= FIELD_PREP(STM32_LPTIM_CC1E, enable);
+ }
+
+ if (reenable) {
+ u32 cfgr, presc;
+ unsigned long rate;
+ unsigned int delay_us;
+
+ ret = regmap_update_bits(priv->regmap, STM32_LPTIM_CCMR1,
+ channel ? STM32_LPTIM_CC2E : STM32_LPTIM_CC1E, 0);
+ if (ret)
+ return ret;
+ /*
+ * After a write to the LPTIM_CCMRx register, a new write operation can only be
+ * performed after a delay of at least (PRESC × 3) clock cycles
+ */
+ ret = regmap_read(priv->regmap, STM32_LPTIM_CFGR, &cfgr);
+ if (ret)
+ return ret;
+ presc = FIELD_GET(STM32_LPTIM_PRESC, cfgr);
+ rate = clk_get_rate(priv->clk) >> presc;
+ if (!rate)
+ return -EINVAL;
+ delay_us = 3 * DIV_ROUND_UP(USEC_PER_SEC, rate);
+ usleep_range(delay_us, delay_us * 2);
+ }
+
+ return regmap_update_bits(priv->regmap, STM32_LPTIM_CCMR1, mask, val);
+}
+
+static int stm32_pwm_lp_apply(struct pwm_chip *chip, struct pwm_device *pwm,
+ const struct pwm_state *state)
+{
+ struct stm32_pwm_lp *priv = to_stm32_pwm_lp(chip);
+ unsigned long long prd, div, dty;
+ struct pwm_state cstate;
+ u32 arr, val, mask, cfgr, presc = 0;
+ bool reenable;
+ int ret;
+
+ pwm_get_state(pwm, &cstate);
+ reenable = !cstate.enabled;
+
+ if (!state->enabled) {
+ if (cstate.enabled) {
+ /* Disable CC channel if any */
+ ret = stm32_pwm_lp_compare_channel_apply(priv, pwm->hwpwm, false,
+ state->polarity);
+ if (ret)
+ return ret;
+ ret = regmap_write(priv->regmap, pwm->hwpwm ?
+ STM32_LPTIM_CCR2 : STM32_LPTIM_CMP, 0);
+ if (ret)
+ return ret;
+
+ /* Check if the timer can be disabled */
+ ret = stm32_pwm_lp_update_allowed(priv, pwm->hwpwm);
+ if (ret < 0)
+ return ret;
+
+ if (ret) {
+ /* Disable LP timer */
+ ret = regmap_write(priv->regmap, STM32_LPTIM_CR, 0);
+ if (ret)
+ return ret;
+ }
+
+ /* disable clock to PWM counter */
+ clk_disable(priv->clk);
+ }
+ return 0;
+ }
+
+ /* Calculate the period and prescaler value */
+ div = (unsigned long long)clk_get_rate(priv->clk) * state->period;
+ do_div(div, NSEC_PER_SEC);
+ if (!div) {
+ /* Clock is too slow to achieve requested period. */
+ dev_dbg(pwmchip_parent(chip), "Can't reach %llu ns\n", state->period);
+ return -EINVAL;
+ }
+
+ prd = div;
+ while (div > STM32_LPTIM_MAX_ARR) {
+ presc++;
+ if ((1 << presc) > STM32_LPTIM_MAX_PRESCALER) {
+ dev_err(pwmchip_parent(chip), "max prescaler exceeded\n");
+ return -EINVAL;
+ }
+ div = prd >> presc;
+ }
+ prd = div;
+
+ /* Calculate the duty cycle */
+ dty = prd * state->duty_cycle;
+ do_div(dty, state->period);
+
+ ret = regmap_read(priv->regmap, STM32_LPTIM_CFGR, &cfgr);
+ if (ret)
+ return ret;
+
+ /*
+ * When there are several channels, they share the same prescaler and reload value.
+ * Check if this can be changed, or the values are the same for all channels.
+ */
+ if (!stm32_pwm_lp_update_allowed(priv, pwm->hwpwm)) {
+ ret = regmap_read(priv->regmap, STM32_LPTIM_ARR, &arr);
+ if (ret)
+ return ret;
+
+ if ((FIELD_GET(STM32_LPTIM_PRESC, cfgr) != presc) || (arr != prd - 1))
+ return -EBUSY;
+ }
+
+ if (!cstate.enabled) {
+ /* enable clock to drive PWM counter */
+ ret = clk_enable(priv->clk);
+ if (ret)
+ return ret;
+ }
+
+ if ((FIELD_GET(STM32_LPTIM_PRESC, cfgr) != presc) ||
+ ((FIELD_GET(STM32_LPTIM_WAVPOL, cfgr) != state->polarity) && !priv->num_cc_chans)) {
+ val = FIELD_PREP(STM32_LPTIM_PRESC, presc);
+ mask = STM32_LPTIM_PRESC;
+
+ if (!priv->num_cc_chans) {
+ /*
+ * WAVPOL bit is only available when no capature compare channel is used,
+ * e.g. on LPTIMER instances that have only one output channel. CCMR1 is
+ * used otherwise.
+ */
+ val |= FIELD_PREP(STM32_LPTIM_WAVPOL, state->polarity);
+ mask |= STM32_LPTIM_WAVPOL;
+ }
+
+ /* Must disable LP timer to modify CFGR */
+ reenable = true;
+ ret = regmap_write(priv->regmap, STM32_LPTIM_CR, 0);
+ if (ret)
+ goto err;
+
+ ret = regmap_update_bits(priv->regmap, STM32_LPTIM_CFGR, mask,
+ val);
+ if (ret)
+ goto err;
+ }
+
+ if (reenable) {
+ /* Must (re)enable LP timer to modify CMP & ARR */
+ ret = regmap_write(priv->regmap, STM32_LPTIM_CR,
+ STM32_LPTIM_ENABLE);
+ if (ret)
+ goto err;
+ }
+
+ ret = regmap_write(priv->regmap, STM32_LPTIM_ARR, prd - 1);
+ if (ret)
+ goto err;
+
+ /* Write CMP/CCRx register and ensure it's been properly written */
+ ret = regmap_write(priv->regmap, pwm->hwpwm ? STM32_LPTIM_CCR2 : STM32_LPTIM_CMP,
+ prd - (1 + dty));
+ if (ret)
+ goto err;
+
+ /* ensure ARR and CMP/CCRx registers are properly written */
+ ret = regmap_read_poll_timeout(priv->regmap, STM32_LPTIM_ISR, val, pwm->hwpwm ?
+ (val & STM32_LPTIM_CMP2_ARROK) == STM32_LPTIM_CMP2_ARROK :
+ (val & STM32_LPTIM_CMPOK_ARROK) == STM32_LPTIM_CMPOK_ARROK,
+ 100, 1000);
+ if (ret) {
+ dev_err(pwmchip_parent(chip), "ARR/CMP registers write issue\n");
+ goto err;
+ }
+ ret = regmap_write(priv->regmap, STM32_LPTIM_ICR, pwm->hwpwm ?
+ STM32_LPTIM_CMP2OKCF_ARROKCF : STM32_LPTIM_CMPOKCF_ARROKCF);
+ if (ret)
+ goto err;
+
+ ret = stm32_pwm_lp_compare_channel_apply(priv, pwm->hwpwm, true, state->polarity);
+ if (ret)
+ goto err;
+
+ if (reenable) {
+ /* Start LP timer in continuous mode */
+ ret = regmap_set_bits(priv->regmap, STM32_LPTIM_CR,
+ STM32_LPTIM_CNTSTRT);
+ if (ret) {
+ regmap_write(priv->regmap, STM32_LPTIM_CR, 0);
+ goto err;
+ }
+ }
+
+ return 0;
+err:
+ if (!cstate.enabled)
+ clk_disable(priv->clk);
+
+ return ret;
+}
+
+static int stm32_pwm_lp_get_state(struct pwm_chip *chip,
+ struct pwm_device *pwm,
+ struct pwm_state *state)
+{
+ struct stm32_pwm_lp *priv = to_stm32_pwm_lp(chip);
+ unsigned long rate = clk_get_rate(priv->clk);
+ u32 val, presc, prd, ccmr1;
+ bool enabled;
+ u64 tmp;
+
+ regmap_read(priv->regmap, STM32_LPTIM_CR, &val);
+ enabled = !!FIELD_GET(STM32_LPTIM_ENABLE, val);
+ if (priv->num_cc_chans) {
+ /* There's a CC chan, need to also check if it's enabled */
+ regmap_read(priv->regmap, STM32_LPTIM_CCMR1, &ccmr1);
+ if (pwm->hwpwm)
+ enabled &= !!FIELD_GET(STM32_LPTIM_CC2E, ccmr1);
+ else
+ enabled &= !!FIELD_GET(STM32_LPTIM_CC1E, ccmr1);
+ }
+ state->enabled = enabled;
+
+ /* Keep PWM counter clock refcount in sync with PWM initial state */
+ if (state->enabled) {
+ int ret = clk_enable(priv->clk);
+
+ if (ret)
+ return ret;
+ }
+
+ regmap_read(priv->regmap, STM32_LPTIM_CFGR, &val);
+ presc = FIELD_GET(STM32_LPTIM_PRESC, val);
+ if (priv->num_cc_chans) {
+ if (pwm->hwpwm)
+ state->polarity = FIELD_GET(STM32_LPTIM_CC2P, ccmr1);
+ else
+ state->polarity = FIELD_GET(STM32_LPTIM_CC1P, ccmr1);
+ } else {
+ state->polarity = FIELD_GET(STM32_LPTIM_WAVPOL, val);
+ }
+
+ regmap_read(priv->regmap, STM32_LPTIM_ARR, &prd);
+ tmp = prd + 1;
+ tmp = (tmp << presc) * NSEC_PER_SEC;
+ state->period = DIV_ROUND_CLOSEST_ULL(tmp, rate);
+
+ regmap_read(priv->regmap, pwm->hwpwm ? STM32_LPTIM_CCR2 : STM32_LPTIM_CMP, &val);
+ tmp = prd - val;
+ tmp = (tmp << presc) * NSEC_PER_SEC;
+ state->duty_cycle = DIV_ROUND_CLOSEST_ULL(tmp, rate);
+
+ return 0;
+}
+
+static const struct pwm_ops stm32_pwm_lp_ops = {
+ .apply = stm32_pwm_lp_apply,
+ .get_state = stm32_pwm_lp_get_state,
+};
+
+static int stm32_pwm_lp_probe(struct platform_device *pdev)
+{
+ struct stm32_lptimer *ddata = dev_get_drvdata(pdev->dev.parent);
+ struct stm32_pwm_lp *priv;
+ struct pwm_chip *chip;
+ unsigned int npwm;
+ int ret;
+
+ if (!ddata->num_cc_chans) {
+ /* No dedicated CC channel, so there's only one PWM channel */
+ npwm = 1;
+ } else {
+ /* There are dedicated CC channels, each with one PWM output */
+ npwm = ddata->num_cc_chans;
+ }
+
+ chip = devm_pwmchip_alloc(&pdev->dev, npwm, sizeof(*priv));
+ if (IS_ERR(chip))
+ return PTR_ERR(chip);
+ priv = to_stm32_pwm_lp(chip);
+
+ priv->regmap = ddata->regmap;
+ priv->clk = ddata->clk;
+ priv->num_cc_chans = ddata->num_cc_chans;
+ chip->ops = &stm32_pwm_lp_ops;
+
+ ret = devm_pwmchip_add(&pdev->dev, chip);
+ if (ret < 0)
+ return ret;
+
+ platform_set_drvdata(pdev, chip);
+
+ return 0;
+}
+
+static int stm32_pwm_lp_suspend(struct device *dev)
+{
+ struct pwm_chip *chip = dev_get_drvdata(dev);
+ struct pwm_state state;
+ unsigned int i;
+
+ for (i = 0; i < chip->npwm; i++) {
+ pwm_get_state(&chip->pwms[i], &state);
+ if (state.enabled) {
+ dev_err(dev, "The consumer didn't stop us (%s)\n",
+ chip->pwms[i].label);
+ return -EBUSY;
+ }
+ }
+
+ return pinctrl_pm_select_sleep_state(dev);
+}
+
+static int stm32_pwm_lp_resume(struct device *dev)
+{
+ return pinctrl_pm_select_default_state(dev);
+}
+
+static DEFINE_SIMPLE_DEV_PM_OPS(stm32_pwm_lp_pm_ops, stm32_pwm_lp_suspend,
+ stm32_pwm_lp_resume);
+
+static const struct of_device_id stm32_pwm_lp_of_match[] = {
+ { .compatible = "st,stm32-pwm-lp" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, stm32_pwm_lp_of_match);
+
+static struct platform_driver stm32_pwm_lp_driver = {
+ .probe = stm32_pwm_lp_probe,
+ .driver = {
+ .name = "stm32-pwm-lp",
+ .of_match_table = stm32_pwm_lp_of_match,
+ .pm = pm_ptr(&stm32_pwm_lp_pm_ops),
+ },
+};
+module_platform_driver(stm32_pwm_lp_driver);
+
+MODULE_ALIAS("platform:stm32-pwm-lp");
+MODULE_DESCRIPTION("STMicroelectronics STM32 PWM LP driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/pwm/pwm-stm32.c b/drivers/pwm/pwm-stm32.c
new file mode 100644
index 000000000..ea5a51a28
--- /dev/null
+++ b/drivers/pwm/pwm-stm32.c
@@ -0,0 +1,939 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) STMicroelectronics 2016
+ *
+ * Author: Gerald Baeza <gerald.baeza@st.com>
+ *
+ * Inspired by timer-stm32.c from Maxime Coquelin
+ * pwm-atmel.c from Bo Shen
+ */
+
+#include <linux/bitfield.h>
+#include <linux/mfd/stm32-timers.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+
+#define CCMR_CHANNEL_SHIFT 8
+#define CCMR_CHANNEL_MASK 0xFF
+#define MAX_BREAKINPUT 2
+#define STM32_MAX_PWM_OUTPUT 4
+
+struct stm32_breakinput {
+ u32 index;
+ u32 level;
+ u32 filter;
+};
+
+struct stm32_pwm {
+ struct mutex lock; /* protect pwm config/enable */
+ struct clk *clk;
+ struct regmap *regmap;
+ u32 max_arr;
+ bool have_complementary_output;
+ struct stm32_breakinput breakinputs[MAX_BREAKINPUT];
+ unsigned int num_breakinputs;
+ u32 capture[4] ____cacheline_aligned; /* DMA'able buffer */
+};
+
+static inline struct stm32_pwm *to_stm32_pwm_dev(struct pwm_chip *chip)
+{
+ return pwmchip_get_drvdata(chip);
+}
+
+static u32 active_channels(struct stm32_pwm *dev)
+{
+ u32 ccer;
+
+ regmap_read(dev->regmap, TIM_CCER, &ccer);
+
+ return ccer & TIM_CCER_CCXE;
+}
+
+struct stm32_pwm_waveform {
+ u32 ccer;
+ u32 psc;
+ u32 arr;
+ u32 ccr;
+};
+
+static int stm32_pwm_round_waveform_tohw(struct pwm_chip *chip,
+ struct pwm_device *pwm,
+ const struct pwm_waveform *wf,
+ void *_wfhw)
+{
+ struct stm32_pwm_waveform *wfhw = _wfhw;
+ struct stm32_pwm *priv = to_stm32_pwm_dev(chip);
+ unsigned int ch = pwm->hwpwm;
+ unsigned long rate;
+ u64 duty_ticks, offset_ticks;
+ int ret;
+
+ if (wf->period_length_ns == 0) {
+ *wfhw = (struct stm32_pwm_waveform){
+ .ccer = 0,
+ };
+
+ return 0;
+ }
+
+ ret = clk_enable(priv->clk);
+ if (ret)
+ return ret;
+
+ wfhw->ccer = TIM_CCER_CCxE(ch + 1);
+ if (priv->have_complementary_output)
+ wfhw->ccer |= TIM_CCER_CCxNE(ch + 1);
+
+ rate = clk_get_rate(priv->clk);
+
+ if (active_channels(priv) & ~TIM_CCER_CCxE(ch + 1)) {
+ u64 arr;
+
+ /*
+ * Other channels are already enabled, so the configured PSC and
+ * ARR must be used for this channel, too.
+ */
+ ret = regmap_read(priv->regmap, TIM_PSC, &wfhw->psc);
+ if (ret)
+ goto out;
+
+ ret = regmap_read(priv->regmap, TIM_ARR, &wfhw->arr);
+ if (ret)
+ goto out;
+
+ arr = mul_u64_u64_div_u64(wf->period_length_ns, rate,
+ (u64)NSEC_PER_SEC * (wfhw->psc + 1));
+ if (arr <= wfhw->arr) {
+ /*
+ * requested period is smaller than the currently
+ * configured and unchangable period, report back the smallest
+ * possible period, i.e. the current state and return 1
+ * to indicate the wrong rounding direction.
+ */
+ ret = 1;
+ }
+
+ } else {
+ /*
+ * .probe() asserted that clk_get_rate() is not bigger than 1 GHz, so
+ * the calculations here won't overflow.
+ * First we need to find the minimal value for prescaler such that
+ *
+ * period_ns * clkrate
+ * ------------------------------ < max_arr + 1
+ * NSEC_PER_SEC * (prescaler + 1)
+ *
+ * This equation is equivalent to
+ *
+ * period_ns * clkrate
+ * ---------------------------- < prescaler + 1
+ * NSEC_PER_SEC * (max_arr + 1)
+ *
+ * Using integer division and knowing that the right hand side is
+ * integer, this is further equivalent to
+ *
+ * (period_ns * clkrate) // (NSEC_PER_SEC * (max_arr + 1)) ≤ prescaler
+ */
+ u64 psc = mul_u64_u64_div_u64(wf->period_length_ns, rate,
+ (u64)NSEC_PER_SEC * ((u64)priv->max_arr + 1));
+ u64 arr;
+
+ wfhw->psc = min_t(u64, psc, MAX_TIM_PSC);
+
+ arr = mul_u64_u64_div_u64(wf->period_length_ns, rate,
+ (u64)NSEC_PER_SEC * (wfhw->psc + 1));
+ if (!arr) {
+ /*
+ * requested period is too small, report back the smallest
+ * possible period, i.e. ARR = 0. The only valid CCR
+ * value is then zero, too.
+ */
+ wfhw->arr = 0;
+ wfhw->ccr = 0;
+ ret = 1;
+ goto out;
+ }
+
+ /*
+ * ARR is limited intentionally to values less than
+ * priv->max_arr to allow 100% duty cycle.
+ */
+ wfhw->arr = min_t(u64, arr, priv->max_arr) - 1;
+ }
+
+ duty_ticks = mul_u64_u64_div_u64(wf->duty_length_ns, rate,
+ (u64)NSEC_PER_SEC * (wfhw->psc + 1));
+ duty_ticks = min_t(u64, duty_ticks, wfhw->arr + 1);
+
+ offset_ticks = mul_u64_u64_div_u64(wf->duty_offset_ns, rate,
+ (u64)NSEC_PER_SEC * (wfhw->psc + 1));
+ offset_ticks = min_t(u64, offset_ticks, wfhw->arr + 1);
+
+ if (duty_ticks && offset_ticks &&
+ duty_ticks + offset_ticks >= wfhw->arr + 1) {
+ wfhw->ccer |= TIM_CCER_CCxP(ch + 1);
+ if (priv->have_complementary_output)
+ wfhw->ccer |= TIM_CCER_CCxNP(ch + 1);
+
+ wfhw->ccr = wfhw->arr + 1 - duty_ticks;
+ } else {
+ wfhw->ccr = duty_ticks;
+ }
+
+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);
+
+ clk_disable(priv->clk);
+
+ return ret;
+}
+
+static int stm32_pwm_round_waveform_fromhw(struct pwm_chip *chip,
+ struct pwm_device *pwm,
+ const void *_wfhw,
+ struct pwm_waveform *wf)
+{
+ 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)) {
+ u64 ccr_ns;
+
+ /* The result doesn't overflow for rate >= 15259 */
+ wf->period_length_ns = mul_u64_u64_div_u64_roundup(((u64)wfhw->psc + 1) * (wfhw->arr + 1),
+ NSEC_PER_SEC, rate);
+
+ ccr_ns = mul_u64_u64_div_u64_roundup(((u64)wfhw->psc + 1) * wfhw->ccr, NSEC_PER_SEC, rate);
+
+ if (wfhw->ccer & TIM_CCER_CCxP(ch + 1)) {
+ wf->duty_length_ns =
+ mul_u64_u64_div_u64_roundup(((u64)wfhw->psc + 1) * (wfhw->arr + 1 - wfhw->ccr),
+ NSEC_PER_SEC, rate);
+
+ wf->duty_offset_ns = ccr_ns;
+ } else {
+ wf->duty_length_ns = ccr_ns;
+ wf->duty_offset_ns = 0;
+ }
+ } 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;
+}
+
+static int stm32_pwm_read_waveform(struct pwm_chip *chip,
+ struct pwm_device *pwm,
+ void *_wfhw)
+{
+ struct stm32_pwm_waveform *wfhw = _wfhw;
+ struct stm32_pwm *priv = to_stm32_pwm_dev(chip);
+ unsigned int ch = pwm->hwpwm;
+ int ret;
+
+ ret = clk_enable(priv->clk);
+ if (ret)
+ return ret;
+
+ ret = regmap_read(priv->regmap, TIM_CCER, &wfhw->ccer);
+ if (ret)
+ goto out;
+
+ if (wfhw->ccer & TIM_CCER_CCxE(ch + 1)) {
+ ret = regmap_read(priv->regmap, TIM_PSC, &wfhw->psc);
+ if (ret)
+ goto out;
+
+ ret = regmap_read(priv->regmap, TIM_ARR, &wfhw->arr);
+ if (ret)
+ goto out;
+
+ if (wfhw->arr == U32_MAX)
+ wfhw->arr -= 1;
+
+ ret = regmap_read(priv->regmap, TIM_CCRx(ch + 1), &wfhw->ccr);
+ if (ret)
+ goto out;
+
+ if (wfhw->ccr > wfhw->arr + 1)
+ wfhw->ccr = wfhw->arr + 1;
+ }
+
+out:
+ clk_disable(priv->clk);
+
+ return ret;
+}
+
+static int stm32_pwm_write_waveform(struct pwm_chip *chip,
+ struct pwm_device *pwm,
+ const void *_wfhw)
+{
+ const struct stm32_pwm_waveform *wfhw = _wfhw;
+ struct stm32_pwm *priv = to_stm32_pwm_dev(chip);
+ unsigned int ch = pwm->hwpwm;
+ int ret;
+
+ ret = clk_enable(priv->clk);
+ if (ret)
+ return ret;
+
+ if (wfhw->ccer & TIM_CCER_CCxE(ch + 1)) {
+ u32 ccer, mask;
+ unsigned int shift;
+ u32 ccmr;
+
+ ret = regmap_read(priv->regmap, TIM_CCER, &ccer);
+ if (ret)
+ goto out;
+
+ /* If there are other channels enabled, don't update PSC and ARR */
+ if (ccer & ~TIM_CCER_CCxE(ch + 1) & TIM_CCER_CCXE) {
+ u32 psc, arr;
+
+ ret = regmap_read(priv->regmap, TIM_PSC, &psc);
+ if (ret)
+ goto out;
+
+ if (psc != wfhw->psc) {
+ ret = -EBUSY;
+ goto out;
+ }
+
+ ret = regmap_read(priv->regmap, TIM_ARR, &arr);
+ if (ret)
+ goto out;
+
+ if (arr != wfhw->arr) {
+ ret = -EBUSY;
+ goto out;
+ }
+ } else {
+ ret = regmap_write(priv->regmap, TIM_PSC, wfhw->psc);
+ if (ret)
+ goto out;
+
+ ret = regmap_write(priv->regmap, TIM_ARR, wfhw->arr);
+ if (ret)
+ goto out;
+
+ ret = regmap_set_bits(priv->regmap, TIM_CR1, TIM_CR1_ARPE);
+ if (ret)
+ goto out;
+
+ }
+
+ /* set polarity */
+ mask = TIM_CCER_CCxP(ch + 1) | TIM_CCER_CCxNP(ch + 1);
+ ret = regmap_update_bits(priv->regmap, TIM_CCER, mask, wfhw->ccer);
+ if (ret)
+ goto out;
+
+ ret = regmap_write(priv->regmap, TIM_CCRx(ch + 1), wfhw->ccr);
+ if (ret)
+ goto out;
+
+ /* Configure output mode */
+ shift = (ch & 0x1) * CCMR_CHANNEL_SHIFT;
+ ccmr = (TIM_CCMR_PE | TIM_CCMR_M1) << shift;
+ mask = CCMR_CHANNEL_MASK << shift;
+
+ if (ch < 2)
+ ret = regmap_update_bits(priv->regmap, TIM_CCMR1, mask, ccmr);
+ else
+ ret = regmap_update_bits(priv->regmap, TIM_CCMR2, mask, ccmr);
+ if (ret)
+ goto out;
+
+ ret = regmap_set_bits(priv->regmap, TIM_BDTR, TIM_BDTR_MOE);
+ if (ret)
+ goto out;
+
+ if (!(ccer & TIM_CCER_CCxE(ch + 1))) {
+ mask = TIM_CCER_CCxE(ch + 1) | TIM_CCER_CCxNE(ch + 1);
+
+ ret = clk_enable(priv->clk);
+ if (ret)
+ goto out;
+
+ ccer = (ccer & ~mask) | (wfhw->ccer & mask);
+ regmap_write(priv->regmap, TIM_CCER, ccer);
+
+ /* Make sure that registers are updated */
+ regmap_set_bits(priv->regmap, TIM_EGR, TIM_EGR_UG);
+
+ /* Enable controller */
+ regmap_set_bits(priv->regmap, TIM_CR1, TIM_CR1_CEN);
+ }
+
+ } else {
+ /* disable channel */
+ u32 mask, ccer;
+
+ mask = TIM_CCER_CCxE(ch + 1);
+ if (priv->have_complementary_output)
+ mask |= TIM_CCER_CCxNE(ch + 1);
+
+ ret = regmap_read(priv->regmap, TIM_CCER, &ccer);
+ if (ret)
+ goto out;
+
+ if (ccer & mask) {
+ ccer = ccer & ~mask;
+
+ ret = regmap_write(priv->regmap, TIM_CCER, ccer);
+ if (ret)
+ goto out;
+
+ if (!(ccer & TIM_CCER_CCXE)) {
+ /* When all channels are disabled, we can disable the controller */
+ ret = regmap_clear_bits(priv->regmap, TIM_CR1, TIM_CR1_CEN);
+ if (ret)
+ goto out;
+ }
+
+ clk_disable(priv->clk);
+ }
+ }
+
+out:
+ clk_disable(priv->clk);
+
+ return ret;
+}
+
+#define TIM_CCER_CC12P (TIM_CCER_CC1P | TIM_CCER_CC2P)
+#define TIM_CCER_CC12E (TIM_CCER_CC1E | TIM_CCER_CC2E)
+#define TIM_CCER_CC34P (TIM_CCER_CC3P | TIM_CCER_CC4P)
+#define TIM_CCER_CC34E (TIM_CCER_CC3E | TIM_CCER_CC4E)
+
+/*
+ * Capture using PWM input mode:
+ * ___ ___
+ * TI[1, 2, 3 or 4]: ........._| |________|
+ * ^0 ^1 ^2
+ * . . .
+ * . . XXXXX
+ * . . XXXXX |
+ * . XXXXX . |
+ * XXXXX . . |
+ * COUNTER: ______XXXXX . . . |_XXX
+ * start^ . . . ^stop
+ * . . . .
+ * v v . v
+ * v
+ * CCR1/CCR3: tx..........t0...........t2
+ * CCR2/CCR4: tx..............t1.........
+ *
+ * DMA burst transfer: | |
+ * v v
+ * DMA buffer: { t0, tx } { t2, t1 }
+ * DMA done: ^
+ *
+ * 0: IC1/3 snapchot on rising edge: counter value -> CCR1/CCR3
+ * + DMA transfer CCR[1/3] & CCR[2/4] values (t0, tx: doesn't care)
+ * 1: IC2/4 snapchot on falling edge: counter value -> CCR2/CCR4
+ * 2: IC1/3 snapchot on rising edge: counter value -> CCR1/CCR3
+ * + DMA transfer CCR[1/3] & CCR[2/4] values (t2, t1)
+ *
+ * DMA done, compute:
+ * - Period = t2 - t0
+ * - Duty cycle = t1 - t0
+ */
+static int stm32_pwm_raw_capture(struct pwm_chip *chip, struct pwm_device *pwm,
+ unsigned long tmo_ms, u32 *raw_prd,
+ u32 *raw_dty)
+{
+ struct stm32_pwm *priv = to_stm32_pwm_dev(chip);
+ struct device *parent = pwmchip_parent(chip)->parent;
+ enum stm32_timers_dmas dma_id;
+ u32 ccen, ccr;
+ int ret;
+
+ /* Ensure registers have been updated, enable counter and capture */
+ regmap_set_bits(priv->regmap, TIM_EGR, TIM_EGR_UG);
+ regmap_set_bits(priv->regmap, TIM_CR1, TIM_CR1_CEN);
+
+ /* Use cc1 or cc3 DMA resp for PWM input channels 1 & 2 or 3 & 4 */
+ dma_id = pwm->hwpwm < 2 ? STM32_TIMERS_DMA_CH1 : STM32_TIMERS_DMA_CH3;
+ ccen = pwm->hwpwm < 2 ? TIM_CCER_CC12E : TIM_CCER_CC34E;
+ ccr = pwm->hwpwm < 2 ? TIM_CCR1 : TIM_CCR3;
+ regmap_set_bits(priv->regmap, TIM_CCER, ccen);
+
+ /*
+ * Timer DMA burst mode. Request 2 registers, 2 bursts, to get both
+ * CCR1 & CCR2 (or CCR3 & CCR4) on each capture event.
+ * We'll get two capture snapchots: { CCR1, CCR2 }, { CCR1, CCR2 }
+ * or { CCR3, CCR4 }, { CCR3, CCR4 }
+ */
+ ret = stm32_timers_dma_burst_read(parent, priv->capture, dma_id, ccr, 2,
+ 2, tmo_ms);
+ if (ret)
+ goto stop;
+
+ /* Period: t2 - t0 (take care of counter overflow) */
+ if (priv->capture[0] <= priv->capture[2])
+ *raw_prd = priv->capture[2] - priv->capture[0];
+ else
+ *raw_prd = priv->max_arr - priv->capture[0] + priv->capture[2];
+
+ /* Duty cycle capture requires at least two capture units */
+ if (pwm->chip->npwm < 2)
+ *raw_dty = 0;
+ else if (priv->capture[0] <= priv->capture[3])
+ *raw_dty = priv->capture[3] - priv->capture[0];
+ else
+ *raw_dty = priv->max_arr - priv->capture[0] + priv->capture[3];
+
+ if (*raw_dty > *raw_prd) {
+ /*
+ * Race beetween PWM input and DMA: it may happen
+ * falling edge triggers new capture on TI2/4 before DMA
+ * had a chance to read CCR2/4. It means capture[1]
+ * contains period + duty_cycle. So, subtract period.
+ */
+ *raw_dty -= *raw_prd;
+ }
+
+stop:
+ regmap_clear_bits(priv->regmap, TIM_CCER, ccen);
+ regmap_clear_bits(priv->regmap, TIM_CR1, TIM_CR1_CEN);
+
+ return ret;
+}
+
+static int stm32_pwm_capture(struct pwm_chip *chip, struct pwm_device *pwm,
+ struct pwm_capture *result, unsigned long tmo_ms)
+{
+ struct stm32_pwm *priv = to_stm32_pwm_dev(chip);
+ unsigned long long prd, div, dty;
+ unsigned long rate;
+ unsigned int psc = 0, icpsc, scale;
+ u32 raw_prd = 0, raw_dty = 0;
+ int ret = 0;
+
+ mutex_lock(&priv->lock);
+
+ if (active_channels(priv)) {
+ ret = -EBUSY;
+ goto unlock;
+ }
+
+ ret = clk_enable(priv->clk);
+ if (ret) {
+ dev_err(pwmchip_parent(chip), "failed to enable counter clock\n");
+ goto unlock;
+ }
+
+ rate = clk_get_rate(priv->clk);
+ if (!rate) {
+ ret = -EINVAL;
+ goto clk_dis;
+ }
+
+ /* prescaler: fit timeout window provided by upper layer */
+ div = (unsigned long long)rate * (unsigned long long)tmo_ms;
+ do_div(div, MSEC_PER_SEC);
+ prd = div;
+ while ((div > priv->max_arr) && (psc < MAX_TIM_PSC)) {
+ psc++;
+ div = prd;
+ do_div(div, psc + 1);
+ }
+ regmap_write(priv->regmap, TIM_ARR, priv->max_arr);
+ regmap_write(priv->regmap, TIM_PSC, psc);
+
+ /* Reset input selector to its default input and disable slave mode */
+ regmap_write(priv->regmap, TIM_TISEL, 0x0);
+ regmap_write(priv->regmap, TIM_SMCR, 0x0);
+
+ /* Map TI1 or TI2 PWM input to IC1 & IC2 (or TI3/4 to IC3 & IC4) */
+ regmap_update_bits(priv->regmap,
+ pwm->hwpwm < 2 ? TIM_CCMR1 : TIM_CCMR2,
+ TIM_CCMR_CC1S | TIM_CCMR_CC2S, pwm->hwpwm & 0x1 ?
+ TIM_CCMR_CC1S_TI2 | TIM_CCMR_CC2S_TI2 :
+ TIM_CCMR_CC1S_TI1 | TIM_CCMR_CC2S_TI1);
+
+ /* Capture period on IC1/3 rising edge, duty cycle on IC2/4 falling. */
+ regmap_update_bits(priv->regmap, TIM_CCER, pwm->hwpwm < 2 ?
+ TIM_CCER_CC12P : TIM_CCER_CC34P, pwm->hwpwm < 2 ?
+ TIM_CCER_CC2P : TIM_CCER_CC4P);
+
+ ret = stm32_pwm_raw_capture(chip, pwm, tmo_ms, &raw_prd, &raw_dty);
+ if (ret)
+ goto stop;
+
+ /*
+ * Got a capture. Try to improve accuracy at high rates:
+ * - decrease counter clock prescaler, scale up to max rate.
+ * - use input prescaler, capture once every /2 /4 or /8 edges.
+ */
+ if (raw_prd) {
+ u32 max_arr = priv->max_arr - 0x1000; /* arbitrary margin */
+
+ scale = max_arr / min(max_arr, raw_prd);
+ } else {
+ scale = priv->max_arr; /* below resolution, use max scale */
+ }
+
+ if (psc && scale > 1) {
+ /* 2nd measure with new scale */
+ psc /= scale;
+ regmap_write(priv->regmap, TIM_PSC, psc);
+ ret = stm32_pwm_raw_capture(chip, pwm, tmo_ms, &raw_prd,
+ &raw_dty);
+ if (ret)
+ goto stop;
+ }
+
+ /* Compute intermediate period not to exceed timeout at low rates */
+ prd = (unsigned long long)raw_prd * (psc + 1) * NSEC_PER_SEC;
+ do_div(prd, rate);
+
+ for (icpsc = 0; icpsc < MAX_TIM_ICPSC ; icpsc++) {
+ /* input prescaler: also keep arbitrary margin */
+ if (raw_prd >= (priv->max_arr - 0x1000) >> (icpsc + 1))
+ break;
+ if (prd >= (tmo_ms * NSEC_PER_MSEC) >> (icpsc + 2))
+ break;
+ }
+
+ if (!icpsc)
+ goto done;
+
+ /* Last chance to improve period accuracy, using input prescaler */
+ regmap_update_bits(priv->regmap,
+ pwm->hwpwm < 2 ? TIM_CCMR1 : TIM_CCMR2,
+ TIM_CCMR_IC1PSC | TIM_CCMR_IC2PSC,
+ FIELD_PREP(TIM_CCMR_IC1PSC, icpsc) |
+ FIELD_PREP(TIM_CCMR_IC2PSC, icpsc));
+
+ ret = stm32_pwm_raw_capture(chip, pwm, tmo_ms, &raw_prd, &raw_dty);
+ if (ret)
+ goto stop;
+
+ if (raw_dty >= (raw_prd >> icpsc)) {
+ /*
+ * We may fall here using input prescaler, when input
+ * capture starts on high side (before falling edge).
+ * Example with icpsc to capture on each 4 events:
+ *
+ * start 1st capture 2nd capture
+ * v v v
+ * ___ _____ _____ _____ _____ ____
+ * TI1..4 |__| |__| |__| |__| |__|
+ * v v . . . . . v v
+ * icpsc1/3: . 0 . 1 . 2 . 3 . 0
+ * icpsc2/4: 0 1 2 3 0
+ * v v v v
+ * CCR1/3 ......t0..............................t2
+ * CCR2/4 ..t1..............................t1'...
+ * . . .
+ * Capture0: .<----------------------------->.
+ * Capture1: .<-------------------------->. .
+ * . . .
+ * Period: .<------> . .
+ * Low side: .<>.
+ *
+ * Result:
+ * - Period = Capture0 / icpsc
+ * - Duty = Period - Low side = Period - (Capture0 - Capture1)
+ */
+ raw_dty = (raw_prd >> icpsc) - (raw_prd - raw_dty);
+ }
+
+done:
+ prd = (unsigned long long)raw_prd * (psc + 1) * NSEC_PER_SEC;
+ result->period = DIV_ROUND_UP_ULL(prd, rate << icpsc);
+ dty = (unsigned long long)raw_dty * (psc + 1) * NSEC_PER_SEC;
+ result->duty_cycle = DIV_ROUND_UP_ULL(dty, rate);
+stop:
+ regmap_write(priv->regmap, TIM_CCER, 0);
+ regmap_write(priv->regmap, pwm->hwpwm < 2 ? TIM_CCMR1 : TIM_CCMR2, 0);
+ regmap_write(priv->regmap, TIM_PSC, 0);
+clk_dis:
+ clk_disable(priv->clk);
+unlock:
+ mutex_unlock(&priv->lock);
+
+ return ret;
+}
+
+static const struct pwm_ops stm32pwm_ops = {
+ .sizeof_wfhw = sizeof(struct stm32_pwm_waveform),
+ .round_waveform_tohw = stm32_pwm_round_waveform_tohw,
+ .round_waveform_fromhw = stm32_pwm_round_waveform_fromhw,
+ .read_waveform = stm32_pwm_read_waveform,
+ .write_waveform = stm32_pwm_write_waveform,
+
+ .capture = IS_ENABLED(CONFIG_DMA_ENGINE) ? stm32_pwm_capture : NULL,
+};
+
+static int stm32_pwm_set_breakinput(struct stm32_pwm *priv,
+ const struct stm32_breakinput *bi)
+{
+ u32 shift = TIM_BDTR_BKF_SHIFT(bi->index);
+ u32 bke = TIM_BDTR_BKE(bi->index);
+ u32 bkp = TIM_BDTR_BKP(bi->index);
+ u32 bkf = TIM_BDTR_BKF(bi->index);
+ u32 mask = bkf | bkp | bke;
+ u32 bdtr;
+
+ bdtr = (bi->filter & TIM_BDTR_BKF_MASK) << shift | bke;
+
+ if (bi->level)
+ bdtr |= bkp;
+
+ regmap_update_bits(priv->regmap, TIM_BDTR, mask, bdtr);
+
+ regmap_read(priv->regmap, TIM_BDTR, &bdtr);
+
+ return (bdtr & bke) ? 0 : -EINVAL;
+}
+
+static int stm32_pwm_apply_breakinputs(struct stm32_pwm *priv)
+{
+ unsigned int i;
+ int ret;
+
+ for (i = 0; i < priv->num_breakinputs; i++) {
+ ret = stm32_pwm_set_breakinput(priv, &priv->breakinputs[i]);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int stm32_pwm_probe_breakinputs(struct stm32_pwm *priv,
+ struct device_node *np)
+{
+ int nb, ret, array_size;
+ unsigned int i;
+
+ nb = of_property_count_elems_of_size(np, "st,breakinput",
+ sizeof(struct stm32_breakinput));
+
+ /*
+ * Because "st,breakinput" parameter is optional do not make probe
+ * failed if it doesn't exist.
+ */
+ if (nb <= 0)
+ return 0;
+
+ if (nb > MAX_BREAKINPUT)
+ return -EINVAL;
+
+ priv->num_breakinputs = nb;
+ array_size = nb * sizeof(struct stm32_breakinput) / sizeof(u32);
+ ret = of_property_read_u32_array(np, "st,breakinput",
+ (u32 *)priv->breakinputs, array_size);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < priv->num_breakinputs; i++) {
+ if (priv->breakinputs[i].index > 1 ||
+ priv->breakinputs[i].level > 1 ||
+ priv->breakinputs[i].filter > 15)
+ return -EINVAL;
+ }
+
+ return stm32_pwm_apply_breakinputs(priv);
+}
+
+static void stm32_pwm_detect_complementary(struct stm32_pwm *priv, struct stm32_timers *ddata)
+{
+ u32 ccer;
+
+ if (ddata->ipidr) {
+ u32 val;
+
+ /* Simply read from HWCFGR the number of complementary outputs (MP25). */
+ regmap_read(priv->regmap, TIM_HWCFGR1, &val);
+ priv->have_complementary_output = !!FIELD_GET(TIM_HWCFGR1_NB_OF_DT, val);
+ return;
+ }
+
+ /*
+ * If complementary bit doesn't exist writing 1 will have no
+ * effect so we can detect it.
+ */
+ regmap_set_bits(priv->regmap, TIM_CCER, TIM_CCER_CC1NE);
+ regmap_read(priv->regmap, TIM_CCER, &ccer);
+ regmap_clear_bits(priv->regmap, TIM_CCER, TIM_CCER_CC1NE);
+
+ priv->have_complementary_output = (ccer != 0);
+}
+
+static unsigned int stm32_pwm_detect_channels(struct stm32_timers *ddata,
+ unsigned int *num_enabled)
+{
+ struct regmap *regmap = ddata->regmap;
+ u32 ccer, ccer_backup;
+
+ regmap_read(regmap, TIM_CCER, &ccer_backup);
+ *num_enabled = hweight32(ccer_backup & TIM_CCER_CCXE);
+
+ if (ddata->ipidr) {
+ u32 hwcfgr;
+ unsigned int npwm;
+
+ /* Deduce from HWCFGR the number of outputs (MP25). */
+ regmap_read(regmap, TIM_HWCFGR1, &hwcfgr);
+
+ /*
+ * Timers may have more capture/compare channels than the
+ * actual number of PWM channel outputs (e.g. TIM_CH[1..4]).
+ */
+ npwm = FIELD_GET(TIM_HWCFGR1_NB_OF_CC, hwcfgr);
+
+ return npwm < STM32_MAX_PWM_OUTPUT ? npwm : STM32_MAX_PWM_OUTPUT;
+ }
+
+ /*
+ * If channels enable bits don't exist writing 1 will have no
+ * effect so we can detect and count them.
+ */
+ regmap_set_bits(regmap, TIM_CCER, TIM_CCER_CCXE);
+ regmap_read(regmap, TIM_CCER, &ccer);
+ regmap_write(regmap, TIM_CCER, ccer_backup);
+
+ return hweight32(ccer & TIM_CCER_CCXE);
+}
+
+static int stm32_pwm_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+ struct stm32_timers *ddata = dev_get_drvdata(pdev->dev.parent);
+ struct pwm_chip *chip;
+ struct stm32_pwm *priv;
+ unsigned int npwm, num_enabled;
+ unsigned int i;
+ int ret;
+
+ npwm = stm32_pwm_detect_channels(ddata, &num_enabled);
+
+ chip = devm_pwmchip_alloc(dev, npwm, sizeof(*priv));
+ if (IS_ERR(chip))
+ return PTR_ERR(chip);
+ priv = to_stm32_pwm_dev(chip);
+
+ mutex_init(&priv->lock);
+ priv->regmap = ddata->regmap;
+ priv->clk = ddata->clk;
+ priv->max_arr = ddata->max_arr;
+
+ if (!priv->regmap || !priv->clk)
+ return dev_err_probe(dev, -EINVAL, "Failed to get %s\n",
+ priv->regmap ? "clk" : "regmap");
+
+ ret = stm32_pwm_probe_breakinputs(priv, np);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "Failed to configure breakinputs\n");
+
+ stm32_pwm_detect_complementary(priv, ddata);
+
+ ret = devm_clk_rate_exclusive_get(dev, priv->clk);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to lock clock\n");
+
+ /*
+ * With the clk running with not more than 1 GHz the calculations in
+ * .apply() won't overflow.
+ */
+ if (clk_get_rate(priv->clk) > 1000000000)
+ return dev_err_probe(dev, -EINVAL, "Clock freq too high (%lu)\n",
+ clk_get_rate(priv->clk));
+
+ chip->ops = &stm32pwm_ops;
+
+ /* Initialize clock refcount to number of enabled PWM channels. */
+ for (i = 0; i < num_enabled; i++) {
+ ret = clk_enable(priv->clk);
+ if (ret)
+ return ret;
+ }
+
+ ret = devm_pwmchip_add(dev, chip);
+ if (ret < 0)
+ return dev_err_probe(dev, ret,
+ "Failed to register pwmchip\n");
+
+ platform_set_drvdata(pdev, chip);
+
+ return 0;
+}
+
+static int stm32_pwm_suspend(struct device *dev)
+{
+ struct pwm_chip *chip = dev_get_drvdata(dev);
+ struct stm32_pwm *priv = to_stm32_pwm_dev(chip);
+ unsigned int i;
+ u32 ccer, mask;
+
+ /* Look for active channels */
+ ccer = active_channels(priv);
+
+ for (i = 0; i < chip->npwm; i++) {
+ mask = TIM_CCER_CCxE(i + 1);
+ if (ccer & mask) {
+ dev_err(dev, "PWM %u still in use by consumer %s\n",
+ i, chip->pwms[i].label);
+ return -EBUSY;
+ }
+ }
+
+ return pinctrl_pm_select_sleep_state(dev);
+}
+
+static int stm32_pwm_resume(struct device *dev)
+{
+ struct pwm_chip *chip = dev_get_drvdata(dev);
+ struct stm32_pwm *priv = to_stm32_pwm_dev(chip);
+ int ret;
+
+ ret = pinctrl_pm_select_default_state(dev);
+ if (ret)
+ return ret;
+
+ /* restore breakinput registers that may have been lost in low power */
+ return stm32_pwm_apply_breakinputs(priv);
+}
+
+static DEFINE_SIMPLE_DEV_PM_OPS(stm32_pwm_pm_ops, stm32_pwm_suspend, stm32_pwm_resume);
+
+static const struct of_device_id stm32_pwm_of_match[] = {
+ { .compatible = "st,stm32-pwm" },
+ { .compatible = "st,stm32mp25-pwm" },
+ { /* end node */ }
+};
+MODULE_DEVICE_TABLE(of, stm32_pwm_of_match);
+
+static struct platform_driver stm32_pwm_driver = {
+ .probe = stm32_pwm_probe,
+ .driver = {
+ .name = "stm32-pwm",
+ .of_match_table = stm32_pwm_of_match,
+ .pm = pm_ptr(&stm32_pwm_pm_ops),
+ },
+};
+module_platform_driver(stm32_pwm_driver);
+
+MODULE_ALIAS("platform:stm32-pwm");
+MODULE_DESCRIPTION("STMicroelectronics STM32 PWM driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/pwm/pwm-stmpe.c b/drivers/pwm/pwm-stmpe.c
new file mode 100644
index 000000000..73f128439
--- /dev/null
+++ b/drivers/pwm/pwm-stmpe.c
@@ -0,0 +1,358 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2016 Linaro Ltd.
+ *
+ * Author: Linus Walleij <linus.walleij@linaro.org>
+ */
+
+#include <linux/bitops.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/mfd/stmpe.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+#include <linux/slab.h>
+
+#define STMPE24XX_PWMCS 0x30
+#define PWMCS_EN_PWM0 BIT(0)
+#define PWMCS_EN_PWM1 BIT(1)
+#define PWMCS_EN_PWM2 BIT(2)
+#define STMPE24XX_PWMIC0 0x38
+#define STMPE24XX_PWMIC1 0x39
+#define STMPE24XX_PWMIC2 0x3a
+
+#define STMPE_PWM_24XX_PINBASE 21
+
+struct stmpe_pwm {
+ struct stmpe *stmpe;
+ u8 last_duty;
+};
+
+static inline struct stmpe_pwm *to_stmpe_pwm(struct pwm_chip *chip)
+{
+ return pwmchip_get_drvdata(chip);
+}
+
+static int stmpe_24xx_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct stmpe_pwm *stmpe_pwm = to_stmpe_pwm(chip);
+ u8 value;
+ int ret;
+
+ ret = stmpe_reg_read(stmpe_pwm->stmpe, STMPE24XX_PWMCS);
+ if (ret < 0) {
+ dev_dbg(pwmchip_parent(chip), "error reading PWM#%u control\n",
+ pwm->hwpwm);
+ return ret;
+ }
+
+ value = ret | BIT(pwm->hwpwm);
+
+ ret = stmpe_reg_write(stmpe_pwm->stmpe, STMPE24XX_PWMCS, value);
+ if (ret) {
+ dev_dbg(pwmchip_parent(chip), "error writing PWM#%u control\n",
+ pwm->hwpwm);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int stmpe_24xx_pwm_disable(struct pwm_chip *chip,
+ struct pwm_device *pwm)
+{
+ struct stmpe_pwm *stmpe_pwm = to_stmpe_pwm(chip);
+ u8 value;
+ int ret;
+
+ ret = stmpe_reg_read(stmpe_pwm->stmpe, STMPE24XX_PWMCS);
+ if (ret < 0) {
+ dev_dbg(pwmchip_parent(chip), "error reading PWM#%u control\n",
+ pwm->hwpwm);
+ return ret;
+ }
+
+ value = ret & ~BIT(pwm->hwpwm);
+
+ ret = stmpe_reg_write(stmpe_pwm->stmpe, STMPE24XX_PWMCS, value);
+ if (ret)
+ dev_dbg(pwmchip_parent(chip), "error writing PWM#%u control\n",
+ pwm->hwpwm);
+ return ret;
+}
+
+/* STMPE 24xx PWM instructions */
+#define SMAX 0x007f
+#define SMIN 0x00ff
+#define GTS 0x0000
+#define LOAD BIT(14) /* Only available on 2403 */
+#define RAMPUP 0x0000
+#define RAMPDOWN BIT(7)
+#define PRESCALE_512 BIT(14)
+#define STEPTIME_1 BIT(8)
+#define BRANCH (BIT(15) | BIT(13))
+
+static int stmpe_24xx_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
+ int duty_ns, int period_ns)
+{
+ struct stmpe_pwm *stmpe_pwm = to_stmpe_pwm(chip);
+ unsigned int i, pin;
+ u16 program[3] = {
+ SMAX,
+ GTS,
+ GTS,
+ };
+ u8 offset;
+ int ret;
+
+ /* Make sure we are disabled */
+ if (pwm_is_enabled(pwm)) {
+ ret = stmpe_24xx_pwm_disable(chip, pwm);
+ if (ret)
+ return ret;
+ } else {
+ /* Connect the PWM to the pin */
+ pin = pwm->hwpwm;
+
+ /* On STMPE2401 and 2403 pins 21,22,23 are used */
+ if (stmpe_pwm->stmpe->partnum == STMPE2401 ||
+ stmpe_pwm->stmpe->partnum == STMPE2403)
+ pin += STMPE_PWM_24XX_PINBASE;
+
+ ret = stmpe_set_altfunc(stmpe_pwm->stmpe, BIT(pin),
+ STMPE_BLOCK_PWM);
+ if (ret) {
+ dev_err(pwmchip_parent(chip), "unable to connect PWM#%u to pin\n",
+ pwm->hwpwm);
+ return ret;
+ }
+ }
+
+ /* STMPE24XX */
+ switch (pwm->hwpwm) {
+ case 0:
+ offset = STMPE24XX_PWMIC0;
+ break;
+
+ case 1:
+ offset = STMPE24XX_PWMIC1;
+ break;
+
+ case 2:
+ offset = STMPE24XX_PWMIC2;
+ break;
+
+ default:
+ /* Should not happen as npwm is 3 */
+ return -ENODEV;
+ }
+
+ dev_dbg(pwmchip_parent(chip), "PWM#%u: config duty %d ns, period %d ns\n",
+ pwm->hwpwm, duty_ns, period_ns);
+
+ if (duty_ns == 0) {
+ if (stmpe_pwm->stmpe->partnum == STMPE2401)
+ program[0] = SMAX; /* off all the time */
+
+ if (stmpe_pwm->stmpe->partnum == STMPE2403)
+ program[0] = LOAD | 0xff; /* LOAD 0xff */
+
+ stmpe_pwm->last_duty = 0x00;
+ } else if (duty_ns == period_ns) {
+ if (stmpe_pwm->stmpe->partnum == STMPE2401)
+ program[0] = SMIN; /* on all the time */
+
+ if (stmpe_pwm->stmpe->partnum == STMPE2403)
+ program[0] = LOAD | 0x00; /* LOAD 0x00 */
+
+ stmpe_pwm->last_duty = 0xff;
+ } else {
+ u8 value, last = stmpe_pwm->last_duty;
+ unsigned long duty;
+
+ /*
+ * Counter goes from 0x00 to 0xff repeatedly at 32768 Hz,
+ * (means a period of 30517 ns) then this is compared to the
+ * counter from the ramp, if this is >= PWM counter the output
+ * is high. With LOAD we can define how much of the cycle it
+ * is on.
+ *
+ * Prescale = 0 -> 2 kHz -> T = 1/f = 488281.25 ns
+ */
+
+ /* Scale to 0..0xff */
+ duty = duty_ns * 256;
+ duty = DIV_ROUND_CLOSEST(duty, period_ns);
+ value = duty;
+
+ if (value == last) {
+ /* Run the old program */
+ if (pwm_is_enabled(pwm))
+ stmpe_24xx_pwm_enable(chip, pwm);
+
+ return 0;
+ } else if (stmpe_pwm->stmpe->partnum == STMPE2403) {
+ /* STMPE2403 can simply set the right PWM value */
+ program[0] = LOAD | value;
+ program[1] = 0x0000;
+ } else if (stmpe_pwm->stmpe->partnum == STMPE2401) {
+ /* STMPE2401 need a complex program */
+ u16 incdec = 0x0000;
+
+ if (last < value)
+ /* Count up */
+ incdec = RAMPUP | (value - last);
+ else
+ /* Count down */
+ incdec = RAMPDOWN | (last - value);
+
+ /* Step to desired value, smoothly */
+ program[0] = PRESCALE_512 | STEPTIME_1 | incdec;
+
+ /* Loop eternally to 0x00 */
+ program[1] = BRANCH;
+ }
+
+ dev_dbg(pwmchip_parent(chip),
+ "PWM#%u: value = %02x, last_duty = %02x, program=%04x,%04x,%04x\n",
+ pwm->hwpwm, value, last, program[0], program[1],
+ program[2]);
+ stmpe_pwm->last_duty = value;
+ }
+
+ /*
+ * We can write programs of up to 64 16-bit words into this channel.
+ */
+ for (i = 0; i < ARRAY_SIZE(program); i++) {
+ u8 value;
+
+ value = (program[i] >> 8) & 0xff;
+
+ ret = stmpe_reg_write(stmpe_pwm->stmpe, offset, value);
+ if (ret) {
+ dev_dbg(pwmchip_parent(chip), "error writing register %02x: %d\n",
+ offset, ret);
+ return ret;
+ }
+
+ value = program[i] & 0xff;
+
+ ret = stmpe_reg_write(stmpe_pwm->stmpe, offset, value);
+ if (ret) {
+ dev_dbg(pwmchip_parent(chip), "error writing register %02x: %d\n",
+ offset, ret);
+ return ret;
+ }
+ }
+
+ /* If we were enabled, re-enable this PWM */
+ if (pwm_is_enabled(pwm))
+ stmpe_24xx_pwm_enable(chip, pwm);
+
+ /* Sleep for 200ms so we're sure it will take effect */
+ msleep(200);
+
+ dev_dbg(pwmchip_parent(chip), "programmed PWM#%u, %u bytes\n", pwm->hwpwm, i);
+
+ return 0;
+}
+
+static int stmpe_24xx_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
+ const struct pwm_state *state)
+{
+ int err;
+
+ if (state->polarity != PWM_POLARITY_NORMAL)
+ return -EINVAL;
+
+ if (!state->enabled) {
+ if (pwm->state.enabled)
+ return stmpe_24xx_pwm_disable(chip, pwm);
+
+ return 0;
+ }
+
+ err = stmpe_24xx_pwm_config(chip, pwm, state->duty_cycle, state->period);
+ if (err)
+ return err;
+
+ if (!pwm->state.enabled)
+ err = stmpe_24xx_pwm_enable(chip, pwm);
+
+ return err;
+}
+
+static const struct pwm_ops stmpe_24xx_pwm_ops = {
+ .apply = stmpe_24xx_pwm_apply,
+};
+
+static int __init stmpe_pwm_probe(struct platform_device *pdev)
+{
+ struct stmpe *stmpe = dev_get_drvdata(pdev->dev.parent);
+ struct pwm_chip *chip;
+ struct stmpe_pwm *stmpe_pwm;
+ int ret;
+
+ switch (stmpe->partnum) {
+ case STMPE2401:
+ case STMPE2403:
+ break;
+ case STMPE1601:
+ return dev_err_probe(&pdev->dev, -ENODEV,
+ "STMPE1601 not yet supported\n");
+ default:
+ return dev_err_probe(&pdev->dev, -ENODEV,
+ "Unknown STMPE PWM\n");
+ }
+
+ chip = devm_pwmchip_alloc(&pdev->dev, 3, sizeof(*stmpe_pwm));
+ if (IS_ERR(chip))
+ return PTR_ERR(chip);
+ stmpe_pwm = to_stmpe_pwm(chip);
+
+ stmpe_pwm->stmpe = stmpe;
+
+ chip->ops = &stmpe_24xx_pwm_ops;
+
+ ret = stmpe_enable(stmpe, STMPE_BLOCK_PWM);
+ if (ret)
+ return ret;
+
+ ret = pwmchip_add(chip);
+ if (ret) {
+ stmpe_disable(stmpe, STMPE_BLOCK_PWM);
+ return ret;
+ }
+
+ platform_set_drvdata(pdev, chip);
+
+ return 0;
+}
+
+static void __exit stmpe_pwm_remove(struct platform_device *pdev)
+{
+ struct stmpe *stmpe = dev_get_drvdata(pdev->dev.parent);
+ struct pwm_chip *chip = platform_get_drvdata(pdev);
+
+ pwmchip_remove(chip);
+ stmpe_disable(stmpe, STMPE_BLOCK_PWM);
+}
+
+/*
+ * stmpe_pwm_remove() lives in .exit.text. For drivers registered via
+ * module_platform_driver_probe() this is ok because they cannot get unbound at
+ * runtime. So mark the driver struct with __refdata to prevent modpost
+ * triggering a section mismatch warning.
+ */
+static struct platform_driver stmpe_pwm_driver __refdata = {
+ .driver = {
+ .name = "stmpe-pwm",
+ },
+ .remove = __exit_p(stmpe_pwm_remove),
+};
+module_platform_driver_probe(stmpe_pwm_driver, stmpe_pwm_probe);
+
+MODULE_DESCRIPTION("STMPE expander PWM");
+MODULE_LICENSE("GPL");
diff --git a/drivers/pwm/pwm-sun4i.c b/drivers/pwm/pwm-sun4i.c
new file mode 100644
index 000000000..0752152b1
--- /dev/null
+++ b/drivers/pwm/pwm-sun4i.c
@@ -0,0 +1,493 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Driver for Allwinner sun4i Pulse Width Modulation Controller
+ *
+ * Copyright (C) 2014 Alexandre Belloni <alexandre.belloni@free-electrons.com>
+ *
+ * Limitations:
+ * - When outputing the source clock directly, the PWM logic will be bypassed
+ * and the currently running period is not guaranteed to be completed
+ */
+
+#include <linux/bitops.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/jiffies.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+#include <linux/reset.h>
+#include <linux/slab.h>
+#include <linux/time.h>
+
+#define PWM_CTRL_REG 0x0
+
+#define PWM_CH_PRD_BASE 0x4
+#define PWM_CH_PRD_OFFSET 0x4
+#define PWM_CH_PRD(ch) (PWM_CH_PRD_BASE + PWM_CH_PRD_OFFSET * (ch))
+
+#define PWMCH_OFFSET 15
+#define PWM_PRESCAL_MASK GENMASK(3, 0)
+#define PWM_PRESCAL_OFF 0
+#define PWM_EN BIT(4)
+#define PWM_ACT_STATE BIT(5)
+#define PWM_CLK_GATING BIT(6)
+#define PWM_MODE BIT(7)
+#define PWM_PULSE BIT(8)
+#define PWM_BYPASS BIT(9)
+
+#define PWM_RDY_BASE 28
+#define PWM_RDY_OFFSET 1
+#define PWM_RDY(ch) BIT(PWM_RDY_BASE + PWM_RDY_OFFSET * (ch))
+
+#define PWM_PRD(prd) (((prd) - 1) << 16)
+#define PWM_PRD_MASK GENMASK(15, 0)
+
+#define PWM_DTY_MASK GENMASK(15, 0)
+
+#define PWM_REG_PRD(reg) ((((reg) >> 16) & PWM_PRD_MASK) + 1)
+#define PWM_REG_DTY(reg) ((reg) & PWM_DTY_MASK)
+#define PWM_REG_PRESCAL(reg, chan) (((reg) >> ((chan) * PWMCH_OFFSET)) & PWM_PRESCAL_MASK)
+
+#define BIT_CH(bit, chan) ((bit) << ((chan) * PWMCH_OFFSET))
+
+static const u32 prescaler_table[] = {
+ 120,
+ 180,
+ 240,
+ 360,
+ 480,
+ 0,
+ 0,
+ 0,
+ 12000,
+ 24000,
+ 36000,
+ 48000,
+ 72000,
+ 0,
+ 0,
+ 0, /* Actually 1 but tested separately */
+};
+
+struct sun4i_pwm_data {
+ bool has_prescaler_bypass;
+ bool has_direct_mod_clk_output;
+ unsigned int npwm;
+};
+
+struct sun4i_pwm_chip {
+ struct clk *bus_clk;
+ struct clk *clk;
+ struct reset_control *rst;
+ void __iomem *base;
+ const struct sun4i_pwm_data *data;
+};
+
+static inline struct sun4i_pwm_chip *to_sun4i_pwm_chip(struct pwm_chip *chip)
+{
+ return pwmchip_get_drvdata(chip);
+}
+
+static inline u32 sun4i_pwm_readl(struct sun4i_pwm_chip *sun4ichip,
+ unsigned long offset)
+{
+ return readl(sun4ichip->base + offset);
+}
+
+static inline void sun4i_pwm_writel(struct sun4i_pwm_chip *sun4ichip,
+ u32 val, unsigned long offset)
+{
+ writel(val, sun4ichip->base + offset);
+}
+
+static int sun4i_pwm_get_state(struct pwm_chip *chip,
+ struct pwm_device *pwm,
+ struct pwm_state *state)
+{
+ struct sun4i_pwm_chip *sun4ichip = to_sun4i_pwm_chip(chip);
+ u64 clk_rate, tmp;
+ u32 val;
+ unsigned int prescaler;
+
+ clk_rate = clk_get_rate(sun4ichip->clk);
+ if (!clk_rate)
+ return -EINVAL;
+
+ val = sun4i_pwm_readl(sun4ichip, PWM_CTRL_REG);
+
+ /*
+ * PWM chapter in H6 manual has a diagram which explains that if bypass
+ * bit is set, no other setting has any meaning. Even more, experiment
+ * proved that also enable bit is ignored in this case.
+ */
+ if ((val & BIT_CH(PWM_BYPASS, pwm->hwpwm)) &&
+ sun4ichip->data->has_direct_mod_clk_output) {
+ state->period = DIV_ROUND_UP_ULL(NSEC_PER_SEC, clk_rate);
+ state->duty_cycle = DIV_ROUND_UP_ULL(state->period, 2);
+ state->polarity = PWM_POLARITY_NORMAL;
+ state->enabled = true;
+ return 0;
+ }
+
+ if ((PWM_REG_PRESCAL(val, pwm->hwpwm) == PWM_PRESCAL_MASK) &&
+ sun4ichip->data->has_prescaler_bypass)
+ prescaler = 1;
+ else
+ prescaler = prescaler_table[PWM_REG_PRESCAL(val, pwm->hwpwm)];
+
+ if (prescaler == 0)
+ return -EINVAL;
+
+ if (val & BIT_CH(PWM_ACT_STATE, pwm->hwpwm))
+ state->polarity = PWM_POLARITY_NORMAL;
+ else
+ state->polarity = PWM_POLARITY_INVERSED;
+
+ if ((val & BIT_CH(PWM_CLK_GATING | PWM_EN, pwm->hwpwm)) ==
+ BIT_CH(PWM_CLK_GATING | PWM_EN, pwm->hwpwm))
+ state->enabled = true;
+ else
+ state->enabled = false;
+
+ val = sun4i_pwm_readl(sun4ichip, PWM_CH_PRD(pwm->hwpwm));
+
+ tmp = (u64)prescaler * NSEC_PER_SEC * PWM_REG_DTY(val);
+ state->duty_cycle = DIV_ROUND_CLOSEST_ULL(tmp, clk_rate);
+
+ tmp = (u64)prescaler * NSEC_PER_SEC * PWM_REG_PRD(val);
+ state->period = DIV_ROUND_CLOSEST_ULL(tmp, clk_rate);
+
+ return 0;
+}
+
+static int sun4i_pwm_calculate(struct sun4i_pwm_chip *sun4ichip,
+ const struct pwm_state *state,
+ u32 *dty, u32 *prd, unsigned int *prsclr,
+ bool *bypass)
+{
+ u64 clk_rate, div = 0;
+ unsigned int prescaler = 0;
+
+ clk_rate = clk_get_rate(sun4ichip->clk);
+
+ *bypass = sun4ichip->data->has_direct_mod_clk_output &&
+ state->enabled &&
+ (state->period * clk_rate >= NSEC_PER_SEC) &&
+ (state->period * clk_rate < 2 * NSEC_PER_SEC) &&
+ (state->duty_cycle * clk_rate * 2 >= NSEC_PER_SEC);
+
+ /* Skip calculation of other parameters if we bypass them */
+ if (*bypass)
+ return 0;
+
+ if (sun4ichip->data->has_prescaler_bypass) {
+ /* First, test without any prescaler when available */
+ prescaler = PWM_PRESCAL_MASK;
+ /*
+ * When not using any prescaler, the clock period in nanoseconds
+ * is not an integer so round it half up instead of
+ * truncating to get less surprising values.
+ */
+ div = clk_rate * state->period + NSEC_PER_SEC / 2;
+ do_div(div, NSEC_PER_SEC);
+ if (div - 1 > PWM_PRD_MASK)
+ prescaler = 0;
+ }
+
+ if (prescaler == 0) {
+ /* Go up from the first divider */
+ for (prescaler = 0; prescaler < PWM_PRESCAL_MASK; prescaler++) {
+ unsigned int pval = prescaler_table[prescaler];
+
+ if (!pval)
+ continue;
+
+ div = clk_rate;
+ do_div(div, pval);
+ div = div * state->period;
+ do_div(div, NSEC_PER_SEC);
+ if (div - 1 <= PWM_PRD_MASK)
+ break;
+ }
+
+ if (div - 1 > PWM_PRD_MASK)
+ return -EINVAL;
+ }
+
+ *prd = div;
+ div *= state->duty_cycle;
+ do_div(div, state->period);
+ *dty = div;
+ *prsclr = prescaler;
+
+ return 0;
+}
+
+static int sun4i_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
+ const struct pwm_state *state)
+{
+ struct sun4i_pwm_chip *sun4ichip = to_sun4i_pwm_chip(chip);
+ struct pwm_state cstate;
+ u32 ctrl, duty = 0, period = 0, val;
+ int ret;
+ unsigned int delay_us, prescaler = 0;
+ bool bypass;
+
+ pwm_get_state(pwm, &cstate);
+
+ if (!cstate.enabled) {
+ ret = clk_prepare_enable(sun4ichip->clk);
+ if (ret) {
+ dev_err(pwmchip_parent(chip), "failed to enable PWM clock\n");
+ return ret;
+ }
+ }
+
+ ret = sun4i_pwm_calculate(sun4ichip, state, &duty, &period, &prescaler,
+ &bypass);
+ if (ret) {
+ dev_err(pwmchip_parent(chip), "period exceeds the maximum value\n");
+ if (!cstate.enabled)
+ clk_disable_unprepare(sun4ichip->clk);
+ return ret;
+ }
+
+ ctrl = sun4i_pwm_readl(sun4ichip, PWM_CTRL_REG);
+
+ if (sun4ichip->data->has_direct_mod_clk_output) {
+ if (bypass) {
+ ctrl |= BIT_CH(PWM_BYPASS, pwm->hwpwm);
+ /* We can skip other parameter */
+ sun4i_pwm_writel(sun4ichip, ctrl, PWM_CTRL_REG);
+ return 0;
+ }
+
+ ctrl &= ~BIT_CH(PWM_BYPASS, pwm->hwpwm);
+ }
+
+ if (PWM_REG_PRESCAL(ctrl, pwm->hwpwm) != prescaler) {
+ /* Prescaler changed, the clock has to be gated */
+ ctrl &= ~BIT_CH(PWM_CLK_GATING, pwm->hwpwm);
+ sun4i_pwm_writel(sun4ichip, ctrl, PWM_CTRL_REG);
+
+ ctrl &= ~BIT_CH(PWM_PRESCAL_MASK, pwm->hwpwm);
+ ctrl |= BIT_CH(prescaler, pwm->hwpwm);
+ }
+
+ val = (duty & PWM_DTY_MASK) | PWM_PRD(period);
+ sun4i_pwm_writel(sun4ichip, val, PWM_CH_PRD(pwm->hwpwm));
+
+ if (state->polarity != PWM_POLARITY_NORMAL)
+ ctrl &= ~BIT_CH(PWM_ACT_STATE, pwm->hwpwm);
+ else
+ ctrl |= BIT_CH(PWM_ACT_STATE, pwm->hwpwm);
+
+ ctrl |= BIT_CH(PWM_CLK_GATING, pwm->hwpwm);
+
+ if (state->enabled)
+ ctrl |= BIT_CH(PWM_EN, pwm->hwpwm);
+
+ sun4i_pwm_writel(sun4ichip, ctrl, PWM_CTRL_REG);
+
+ if (state->enabled)
+ return 0;
+
+ /* We need a full period to elapse before disabling the channel. */
+ delay_us = DIV_ROUND_UP_ULL(cstate.period, NSEC_PER_USEC);
+ if ((delay_us / 500) > MAX_UDELAY_MS)
+ msleep(delay_us / 1000 + 1);
+ else
+ usleep_range(delay_us, delay_us * 2);
+
+ ctrl = sun4i_pwm_readl(sun4ichip, PWM_CTRL_REG);
+ ctrl &= ~BIT_CH(PWM_CLK_GATING, pwm->hwpwm);
+ ctrl &= ~BIT_CH(PWM_EN, pwm->hwpwm);
+ sun4i_pwm_writel(sun4ichip, ctrl, PWM_CTRL_REG);
+
+ clk_disable_unprepare(sun4ichip->clk);
+
+ return 0;
+}
+
+static const struct pwm_ops sun4i_pwm_ops = {
+ .apply = sun4i_pwm_apply,
+ .get_state = sun4i_pwm_get_state,
+};
+
+static const struct sun4i_pwm_data sun4i_pwm_dual_nobypass = {
+ .has_prescaler_bypass = false,
+ .npwm = 2,
+};
+
+static const struct sun4i_pwm_data sun4i_pwm_dual_bypass = {
+ .has_prescaler_bypass = true,
+ .npwm = 2,
+};
+
+static const struct sun4i_pwm_data sun4i_pwm_single_bypass = {
+ .has_prescaler_bypass = true,
+ .npwm = 1,
+};
+
+static const struct sun4i_pwm_data sun50i_a64_pwm_data = {
+ .has_prescaler_bypass = true,
+ .has_direct_mod_clk_output = true,
+ .npwm = 1,
+};
+
+static const struct sun4i_pwm_data sun50i_h6_pwm_data = {
+ .has_prescaler_bypass = true,
+ .has_direct_mod_clk_output = true,
+ .npwm = 2,
+};
+
+static const struct of_device_id sun4i_pwm_dt_ids[] = {
+ {
+ .compatible = "allwinner,sun4i-a10-pwm",
+ .data = &sun4i_pwm_dual_nobypass,
+ }, {
+ .compatible = "allwinner,sun5i-a10s-pwm",
+ .data = &sun4i_pwm_dual_bypass,
+ }, {
+ .compatible = "allwinner,sun5i-a13-pwm",
+ .data = &sun4i_pwm_single_bypass,
+ }, {
+ .compatible = "allwinner,sun7i-a20-pwm",
+ .data = &sun4i_pwm_dual_bypass,
+ }, {
+ .compatible = "allwinner,sun8i-h3-pwm",
+ .data = &sun4i_pwm_single_bypass,
+ }, {
+ .compatible = "allwinner,sun50i-a64-pwm",
+ .data = &sun50i_a64_pwm_data,
+ }, {
+ .compatible = "allwinner,sun50i-h6-pwm",
+ .data = &sun50i_h6_pwm_data,
+ }, {
+ /* sentinel */
+ }
+};
+MODULE_DEVICE_TABLE(of, sun4i_pwm_dt_ids);
+
+static int sun4i_pwm_probe(struct platform_device *pdev)
+{
+ struct pwm_chip *chip;
+ const struct sun4i_pwm_data *data;
+ struct sun4i_pwm_chip *sun4ichip;
+ int ret;
+
+ data = of_device_get_match_data(&pdev->dev);
+ if (!data)
+ return -ENODEV;
+
+ chip = devm_pwmchip_alloc(&pdev->dev, data->npwm, sizeof(*sun4ichip));
+ if (IS_ERR(chip))
+ return PTR_ERR(chip);
+ sun4ichip = to_sun4i_pwm_chip(chip);
+
+ sun4ichip->data = data;
+ sun4ichip->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(sun4ichip->base))
+ return PTR_ERR(sun4ichip->base);
+
+ /*
+ * All hardware variants need a source clock that is divided and
+ * then feeds the counter that defines the output wave form. In the
+ * device tree this clock is either unnamed or called "mod".
+ * Some variants (e.g. H6) need another clock to access the
+ * hardware registers; this is called "bus".
+ * So we request "mod" first (and ignore the corner case that a
+ * parent provides a "mod" clock while the right one would be the
+ * unnamed one of the PWM device) and if this is not found we fall
+ * back to the first clock of the PWM.
+ */
+ sun4ichip->clk = devm_clk_get_optional(&pdev->dev, "mod");
+ if (IS_ERR(sun4ichip->clk))
+ return dev_err_probe(&pdev->dev, PTR_ERR(sun4ichip->clk),
+ "get mod clock failed\n");
+
+ if (!sun4ichip->clk) {
+ sun4ichip->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(sun4ichip->clk))
+ return dev_err_probe(&pdev->dev, PTR_ERR(sun4ichip->clk),
+ "get unnamed clock failed\n");
+ }
+
+ sun4ichip->bus_clk = devm_clk_get_optional(&pdev->dev, "bus");
+ if (IS_ERR(sun4ichip->bus_clk))
+ return dev_err_probe(&pdev->dev, PTR_ERR(sun4ichip->bus_clk),
+ "get bus clock failed\n");
+
+ sun4ichip->rst = devm_reset_control_get_optional_shared(&pdev->dev, NULL);
+ if (IS_ERR(sun4ichip->rst))
+ return dev_err_probe(&pdev->dev, PTR_ERR(sun4ichip->rst),
+ "get reset failed\n");
+
+ /* Deassert reset */
+ ret = reset_control_deassert(sun4ichip->rst);
+ if (ret) {
+ dev_err(&pdev->dev, "cannot deassert reset control: %pe\n",
+ ERR_PTR(ret));
+ return ret;
+ }
+
+ /*
+ * We're keeping the bus clock on for the sake of simplicity.
+ * Actually it only needs to be on for hardware register accesses.
+ */
+ ret = clk_prepare_enable(sun4ichip->bus_clk);
+ if (ret) {
+ dev_err(&pdev->dev, "cannot prepare and enable bus_clk %pe\n",
+ ERR_PTR(ret));
+ goto err_bus;
+ }
+
+ chip->ops = &sun4i_pwm_ops;
+
+ ret = pwmchip_add(chip);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to add PWM chip: %d\n", ret);
+ goto err_pwm_add;
+ }
+
+ platform_set_drvdata(pdev, chip);
+
+ return 0;
+
+err_pwm_add:
+ clk_disable_unprepare(sun4ichip->bus_clk);
+err_bus:
+ reset_control_assert(sun4ichip->rst);
+
+ return ret;
+}
+
+static void sun4i_pwm_remove(struct platform_device *pdev)
+{
+ struct pwm_chip *chip = platform_get_drvdata(pdev);
+ struct sun4i_pwm_chip *sun4ichip = to_sun4i_pwm_chip(chip);
+
+ pwmchip_remove(chip);
+
+ clk_disable_unprepare(sun4ichip->bus_clk);
+ reset_control_assert(sun4ichip->rst);
+}
+
+static struct platform_driver sun4i_pwm_driver = {
+ .driver = {
+ .name = "sun4i-pwm",
+ .of_match_table = sun4i_pwm_dt_ids,
+ },
+ .probe = sun4i_pwm_probe,
+ .remove = sun4i_pwm_remove,
+};
+module_platform_driver(sun4i_pwm_driver);
+
+MODULE_ALIAS("platform:sun4i-pwm");
+MODULE_AUTHOR("Alexandre Belloni <alexandre.belloni@free-electrons.com>");
+MODULE_DESCRIPTION("Allwinner sun4i PWM driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/pwm/pwm-sunplus.c b/drivers/pwm/pwm-sunplus.c
new file mode 100644
index 000000000..4c4a4b0f5
--- /dev/null
+++ b/drivers/pwm/pwm-sunplus.c
@@ -0,0 +1,232 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * PWM device driver for SUNPLUS SP7021 SoC
+ *
+ * Links:
+ * Reference Manual:
+ * https://sunplus-tibbo.atlassian.net/wiki/spaces/doc/overview
+ *
+ * Reference Manual(PWM module):
+ * https://sunplus.atlassian.net/wiki/spaces/doc/pages/461144198/12.+Pulse+Width+Modulation+PWM
+ *
+ * Limitations:
+ * - Only supports normal polarity.
+ * - It output low when PWM channel disabled.
+ * - When the parameters change, current running period will not be completed
+ * and run new settings immediately.
+ * - In .apply() PWM output need to write register FREQ and DUTY. When first write FREQ
+ * done and not yet write DUTY, it has short timing gap use new FREQ and old DUTY.
+ *
+ * Author: Hammer Hsieh <hammerh0314@gmail.com>
+ */
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+
+#define SP7021_PWM_MODE0 0x000
+#define SP7021_PWM_MODE0_PWMEN(ch) BIT(ch)
+#define SP7021_PWM_MODE0_BYPASS(ch) BIT(8 + (ch))
+#define SP7021_PWM_MODE1 0x004
+#define SP7021_PWM_MODE1_CNT_EN(ch) BIT(ch)
+#define SP7021_PWM_FREQ(ch) (0x008 + 4 * (ch))
+#define SP7021_PWM_FREQ_MAX GENMASK(15, 0)
+#define SP7021_PWM_DUTY(ch) (0x018 + 4 * (ch))
+#define SP7021_PWM_DUTY_DD_SEL(ch) FIELD_PREP(GENMASK(9, 8), ch)
+#define SP7021_PWM_DUTY_MAX GENMASK(7, 0)
+#define SP7021_PWM_DUTY_MASK SP7021_PWM_DUTY_MAX
+#define SP7021_PWM_FREQ_SCALER 256
+#define SP7021_PWM_NUM 4
+
+struct sunplus_pwm {
+ void __iomem *base;
+ struct clk *clk;
+};
+
+static inline struct sunplus_pwm *to_sunplus_pwm(struct pwm_chip *chip)
+{
+ return pwmchip_get_drvdata(chip);
+}
+
+static int sunplus_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
+ const struct pwm_state *state)
+{
+ struct sunplus_pwm *priv = to_sunplus_pwm(chip);
+ u32 dd_freq, duty, mode0, mode1;
+ u64 clk_rate;
+
+ if (state->polarity != pwm->state.polarity)
+ return -EINVAL;
+
+ if (!state->enabled) {
+ /* disable pwm channel output */
+ mode0 = readl(priv->base + SP7021_PWM_MODE0);
+ mode0 &= ~SP7021_PWM_MODE0_PWMEN(pwm->hwpwm);
+ writel(mode0, priv->base + SP7021_PWM_MODE0);
+ /* disable pwm channel clk source */
+ mode1 = readl(priv->base + SP7021_PWM_MODE1);
+ mode1 &= ~SP7021_PWM_MODE1_CNT_EN(pwm->hwpwm);
+ writel(mode1, priv->base + SP7021_PWM_MODE1);
+ return 0;
+ }
+
+ clk_rate = clk_get_rate(priv->clk);
+
+ /*
+ * The following calculations might overflow if clk is bigger
+ * than 256 GHz. In practise it's 202.5MHz, so this limitation
+ * is only theoretic.
+ */
+ if (clk_rate > (u64)SP7021_PWM_FREQ_SCALER * NSEC_PER_SEC)
+ return -EINVAL;
+
+ /*
+ * With clk_rate limited above we have dd_freq <= state->period,
+ * so this cannot overflow.
+ */
+ dd_freq = mul_u64_u64_div_u64(clk_rate, state->period, (u64)SP7021_PWM_FREQ_SCALER
+ * NSEC_PER_SEC);
+
+ if (dd_freq == 0)
+ return -EINVAL;
+
+ if (dd_freq > SP7021_PWM_FREQ_MAX)
+ dd_freq = SP7021_PWM_FREQ_MAX;
+
+ writel(dd_freq, priv->base + SP7021_PWM_FREQ(pwm->hwpwm));
+
+ /* cal and set pwm duty */
+ mode0 = readl(priv->base + SP7021_PWM_MODE0);
+ mode0 |= SP7021_PWM_MODE0_PWMEN(pwm->hwpwm);
+ mode1 = readl(priv->base + SP7021_PWM_MODE1);
+ mode1 |= SP7021_PWM_MODE1_CNT_EN(pwm->hwpwm);
+ if (state->duty_cycle == state->period) {
+ /* PWM channel output = high */
+ mode0 |= SP7021_PWM_MODE0_BYPASS(pwm->hwpwm);
+ duty = SP7021_PWM_DUTY_DD_SEL(pwm->hwpwm) | SP7021_PWM_DUTY_MAX;
+ } else {
+ mode0 &= ~SP7021_PWM_MODE0_BYPASS(pwm->hwpwm);
+ /*
+ * duty_ns <= period_ns 27 bits, clk_rate 28 bits, won't overflow.
+ */
+ duty = mul_u64_u64_div_u64(state->duty_cycle, clk_rate,
+ (u64)dd_freq * NSEC_PER_SEC);
+ duty = SP7021_PWM_DUTY_DD_SEL(pwm->hwpwm) | duty;
+ }
+ writel(duty, priv->base + SP7021_PWM_DUTY(pwm->hwpwm));
+ writel(mode1, priv->base + SP7021_PWM_MODE1);
+ writel(mode0, priv->base + SP7021_PWM_MODE0);
+
+ return 0;
+}
+
+static int sunplus_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm,
+ struct pwm_state *state)
+{
+ struct sunplus_pwm *priv = to_sunplus_pwm(chip);
+ u32 mode0, dd_freq, duty;
+ u64 clk_rate;
+
+ mode0 = readl(priv->base + SP7021_PWM_MODE0);
+
+ if (mode0 & BIT(pwm->hwpwm)) {
+ clk_rate = clk_get_rate(priv->clk);
+ dd_freq = readl(priv->base + SP7021_PWM_FREQ(pwm->hwpwm));
+ duty = readl(priv->base + SP7021_PWM_DUTY(pwm->hwpwm));
+ duty = FIELD_GET(SP7021_PWM_DUTY_MASK, duty);
+ /*
+ * dd_freq 16 bits, SP7021_PWM_FREQ_SCALER 8 bits
+ * NSEC_PER_SEC 30 bits, won't overflow.
+ */
+ state->period = DIV64_U64_ROUND_UP((u64)dd_freq * (u64)SP7021_PWM_FREQ_SCALER
+ * NSEC_PER_SEC, clk_rate);
+ /*
+ * dd_freq 16 bits, duty 8 bits, NSEC_PER_SEC 30 bits, won't overflow.
+ */
+ state->duty_cycle = DIV64_U64_ROUND_UP((u64)dd_freq * (u64)duty * NSEC_PER_SEC,
+ clk_rate);
+ state->enabled = true;
+ } else {
+ state->enabled = false;
+ }
+
+ state->polarity = PWM_POLARITY_NORMAL;
+
+ return 0;
+}
+
+static const struct pwm_ops sunplus_pwm_ops = {
+ .apply = sunplus_pwm_apply,
+ .get_state = sunplus_pwm_get_state,
+};
+
+static void sunplus_pwm_clk_release(void *data)
+{
+ struct clk *clk = data;
+
+ clk_disable_unprepare(clk);
+}
+
+static int sunplus_pwm_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct pwm_chip *chip;
+ struct sunplus_pwm *priv;
+ int ret;
+
+ chip = devm_pwmchip_alloc(dev, SP7021_PWM_NUM, sizeof(*priv));
+ if (IS_ERR(chip))
+ return PTR_ERR(chip);
+ priv = to_sunplus_pwm(chip);
+
+ priv->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(priv->base))
+ return PTR_ERR(priv->base);
+
+ priv->clk = devm_clk_get(dev, NULL);
+ if (IS_ERR(priv->clk))
+ return dev_err_probe(dev, PTR_ERR(priv->clk),
+ "get pwm clock failed\n");
+
+ ret = clk_prepare_enable(priv->clk);
+ if (ret < 0) {
+ dev_err(dev, "failed to enable clock: %d\n", ret);
+ return ret;
+ }
+
+ ret = devm_add_action_or_reset(dev, sunplus_pwm_clk_release, priv->clk);
+ if (ret < 0) {
+ dev_err(dev, "failed to release clock: %d\n", ret);
+ return ret;
+ }
+
+ chip->ops = &sunplus_pwm_ops;
+
+ ret = devm_pwmchip_add(dev, chip);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "Cannot register sunplus PWM\n");
+
+ return 0;
+}
+
+static const struct of_device_id sunplus_pwm_of_match[] = {
+ { .compatible = "sunplus,sp7021-pwm" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, sunplus_pwm_of_match);
+
+static struct platform_driver sunplus_pwm_driver = {
+ .probe = sunplus_pwm_probe,
+ .driver = {
+ .name = "sunplus-pwm",
+ .of_match_table = sunplus_pwm_of_match,
+ },
+};
+module_platform_driver(sunplus_pwm_driver);
+
+MODULE_DESCRIPTION("Sunplus SoC PWM Driver");
+MODULE_AUTHOR("Hammer Hsieh <hammerh0314@gmail.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/pwm/pwm-tegra.c b/drivers/pwm/pwm-tegra.c
new file mode 100644
index 000000000..5cdbe120b
--- /dev/null
+++ b/drivers/pwm/pwm-tegra.c
@@ -0,0 +1,499 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * drivers/pwm/pwm-tegra.c
+ *
+ * Tegra pulse-width-modulation controller driver
+ *
+ * Copyright (c) 2010-2020, NVIDIA Corporation.
+ * Based on arch/arm/plat-mxc/pwm.c by Sascha Hauer <s.hauer@pengutronix.de>
+ *
+ * Overview of Tegra Pulse Width Modulator Register
+ * CSR_0 of Tegra20, Tegra186, and Tegra194:
+ * +-------+-------+-----------------------------------------------------------+
+ * | Bit | Field | Description |
+ * +-------+-------+-----------------------------------------------------------+
+ * | 31 | ENB | Enable Pulse width modulator. |
+ * | | | 0 = DISABLE, 1 = ENABLE. |
+ * +-------+-------+-----------------------------------------------------------+
+ * | 30:16 | PWM_0 | Pulse width that needs to be programmed. |
+ * | | | 0 = Always low. |
+ * | | | 1 = 1 / 256 pulse high. |
+ * | | | 2 = 2 / 256 pulse high. |
+ * | | | N = N / 256 pulse high. |
+ * | | | Only 8 bits are usable [23:16]. |
+ * | | | Bit[24] can be programmed to 1 to achieve 100% duty |
+ * | | | cycle. In this case the other bits [23:16] are set to |
+ * | | | don’t care. |
+ * +-------+-------+-----------------------------------------------------------+
+ * | 12:0 | PFM_0 | Frequency divider that needs to be programmed, also known |
+ * | | | as SCALE. Division by (1 + PFM_0). |
+ * +-------+-------+-----------------------------------------------------------+
+ *
+ * CSR_0 of Tegra264:
+ * +-------+-------+-----------------------------------------------------------+
+ * | Bit | Field | Description |
+ * +-------+-------+-----------------------------------------------------------+
+ * | 31:16 | PWM_0 | Pulse width that needs to be programmed. |
+ * | | | 0 = Always low. |
+ * | | | 1 = 1 / (1 + CSR_1.DEPTH) pulse high. |
+ * | | | 2 = 2 / (1 + CSR_1.DEPTH) pulse high. |
+ * | | | N = N / (1 + CSR_1.DEPTH) pulse high. |
+ * +-------+-------+-----------------------------------------------------------+
+ * | 15:0 | PFM_0 | Frequency divider that needs to be programmed, also known |
+ * | | | as SCALE. Division by (1 + PFM_0). |
+ * +-------+-------+-----------------------------------------------------------+
+ *
+ * CSR_1 of Tegra264:
+ * +-------+-------+-----------------------------------------------------------+
+ * | Bit | Field | Description |
+ * +-------+-------+-----------------------------------------------------------+
+ * | 31 | ENB | Enable Pulse width modulator. |
+ * | | | 0 = DISABLE, 1 = ENABLE. |
+ * +-------+-------+-----------------------------------------------------------+
+ * | 30:15 | DEPTH | Depth for pulse width modulator. This controls the pulse |
+ * | | | time generated. Division by (1 + CSR_1.DEPTH). |
+ * +-------+-------+-----------------------------------------------------------+
+ *
+ * The PWM clock frequency is divided by DEPTH = (1 + CSR_1.DEPTH) before
+ * subdividing it based on the programmable frequency division value to
+ * generate the required frequency for PWM output. DEPTH is fixed to 256
+ * before Tegra264. The maximum output frequency that can be achieved is
+ * (max rate of source clock) / DEPTH.
+ * e.g. if source clock rate is 408 MHz, and DEPTH = 256, maximum output
+ * frequency can be: 408 MHz / 256 ~= 1.6 MHz.
+ * This 1.6 MHz frequency can further be divided using SCALE value in PWM.
+ *
+ * Limitations:
+ * - When PWM is disabled, the output is driven to inactive.
+ * - It does not allow the current PWM period to complete and
+ * stops abruptly.
+ *
+ * - If the register is reconfigured while PWM is running,
+ * it does not complete the currently running period.
+ *
+ * - If the user input duty is beyond acceptible limits,
+ * -EINVAL is returned.
+ */
+
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/pm_opp.h>
+#include <linux/pwm.h>
+#include <linux/platform_device.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
+#include <linux/reset.h>
+
+#include <soc/tegra/common.h>
+
+#define TEGRA_PWM_ENABLE (1 << 31)
+#define TEGRA_PWM_DUTY_SHIFT 16
+#define TEGRA_PWM_SCALE_SHIFT 0
+
+#define TEGRA_PWM_CSR_0 0
+#define TEGRA_PWM_CSR_1 4
+
+#define TEGRA_PWM_DEPTH 256
+
+struct tegra_pwm_soc {
+ unsigned int num_channels;
+ unsigned int enable_reg;
+
+ unsigned int scale_width;
+};
+
+struct tegra_pwm_chip {
+ struct clk *clk;
+ struct reset_control*rst;
+
+ unsigned long clk_rate;
+ unsigned long min_period_ns;
+
+ void __iomem *regs;
+
+ const struct tegra_pwm_soc *soc;
+};
+
+static inline struct tegra_pwm_chip *to_tegra_pwm_chip(struct pwm_chip *chip)
+{
+ return pwmchip_get_drvdata(chip);
+}
+
+static inline u32 tegra_pwm_readl(struct pwm_device *pwm, unsigned int offset)
+{
+ struct tegra_pwm_chip *chip = to_tegra_pwm_chip(pwm->chip);
+
+ return readl(chip->regs + (pwm->hwpwm * 16) + offset);
+}
+
+static inline void tegra_pwm_writel(struct pwm_device *pwm, unsigned int offset, u32 value)
+{
+ struct tegra_pwm_chip *chip = to_tegra_pwm_chip(pwm->chip);
+
+ writel(value, chip->regs + (pwm->hwpwm * 16) + offset);
+}
+
+static int tegra_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
+ int duty_ns, int period_ns)
+{
+ struct tegra_pwm_chip *pc = to_tegra_pwm_chip(chip);
+ unsigned long long c = duty_ns;
+ unsigned long rate, required_clk_rate;
+ u32 val = 0;
+ int err;
+
+ /*
+ * Convert from duty_ns / period_ns to a fixed number of duty ticks
+ * per TEGRA_PWM_DEPTH cycles and make sure to round to the
+ * nearest integer during division.
+ */
+ c *= TEGRA_PWM_DEPTH;
+ c = DIV_ROUND_CLOSEST_ULL(c, period_ns);
+
+ val = (u32)c << TEGRA_PWM_DUTY_SHIFT;
+
+ /*
+ * min period = max clock limit / TEGRA_PWM_DEPTH
+ */
+ if (period_ns < pc->min_period_ns)
+ return -EINVAL;
+
+ /*
+ * Compute the prescaler value for which TEGRA_PWM_DEPTH
+ * cycles at the PWM clock rate will take period_ns nanoseconds.
+ *
+ * num_channels: If single instance of PWM controller has multiple
+ * channels (e.g. Tegra210 or older) then it is not possible to
+ * configure separate clock rates to each of the channels, in such
+ * case the value stored during probe will be referred.
+ *
+ * If every PWM controller instance has one channel respectively, i.e.
+ * nums_channels == 1 then only the clock rate can be modified
+ * dynamically (e.g. Tegra186 or Tegra194).
+ */
+ if (pc->soc->num_channels == 1) {
+ /*
+ * Rate is multiplied with TEGRA_PWM_DEPTH so that it matches
+ * with the maximum possible rate that the controller can
+ * provide. Any further lower value can be derived by setting
+ * PFM bits[0:12].
+ *
+ * required_clk_rate is a reference rate for source clock and
+ * it is derived based on user requested period. By setting the
+ * source clock rate as required_clk_rate, PWM controller will
+ * be able to configure the requested period.
+ */
+ required_clk_rate = DIV_ROUND_UP_ULL((u64)NSEC_PER_SEC * TEGRA_PWM_DEPTH,
+ period_ns);
+
+ if (required_clk_rate > clk_round_rate(pc->clk, required_clk_rate))
+ /*
+ * required_clk_rate is a lower bound for the input
+ * rate; for lower rates there is no value for PWM_SCALE
+ * that yields a period less than or equal to the
+ * requested period. Hence, for lower rates, double the
+ * required_clk_rate to get a clock rate that can meet
+ * the requested period.
+ */
+ required_clk_rate *= 2;
+
+ err = dev_pm_opp_set_rate(pwmchip_parent(chip), required_clk_rate);
+ if (err < 0)
+ return -EINVAL;
+
+ /* Store the new rate for further references */
+ pc->clk_rate = clk_get_rate(pc->clk);
+ }
+
+ /* Consider precision in scale_width rate calculation */
+ rate = mul_u64_u64_div_u64(pc->clk_rate, period_ns,
+ (u64)NSEC_PER_SEC * TEGRA_PWM_DEPTH);
+
+ /*
+ * Since the actual PWM divider is the register's frequency divider
+ * field plus 1, we need to decrement to get the correct value to
+ * write to the register.
+ */
+ if (rate > 0)
+ rate--;
+ else
+ return -EINVAL;
+
+ /*
+ * Make sure that the rate will fit in the register's frequency
+ * divider field.
+ */
+ if (rate >> pc->soc->scale_width)
+ return -EINVAL;
+
+ val |= rate << TEGRA_PWM_SCALE_SHIFT;
+
+ /*
+ * If the PWM channel is disabled, make sure to turn on the clock
+ * before writing the register. Otherwise, keep it enabled.
+ */
+ if (!pwm_is_enabled(pwm)) {
+ err = pm_runtime_resume_and_get(pwmchip_parent(chip));
+ if (err)
+ return err;
+ } else if (pc->soc->enable_reg == TEGRA_PWM_CSR_0) {
+ val |= TEGRA_PWM_ENABLE;
+ }
+
+ tegra_pwm_writel(pwm, TEGRA_PWM_CSR_0, val);
+
+ /*
+ * If the PWM is not enabled, turn the clock off again to save power.
+ */
+ if (!pwm_is_enabled(pwm))
+ pm_runtime_put(pwmchip_parent(chip));
+
+ return 0;
+}
+
+static int tegra_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct tegra_pwm_chip *pc = to_tegra_pwm_chip(chip);
+ int rc = 0;
+ u32 val;
+
+ rc = pm_runtime_resume_and_get(pwmchip_parent(chip));
+ if (rc)
+ return rc;
+
+ val = tegra_pwm_readl(pwm, pc->soc->enable_reg);
+ val |= TEGRA_PWM_ENABLE;
+ tegra_pwm_writel(pwm, pc->soc->enable_reg, val);
+
+ return 0;
+}
+
+static void tegra_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct tegra_pwm_chip *pc = to_tegra_pwm_chip(chip);
+ u32 val;
+
+ val = tegra_pwm_readl(pwm, pc->soc->enable_reg);
+ val &= ~TEGRA_PWM_ENABLE;
+ tegra_pwm_writel(pwm, pc->soc->enable_reg, val);
+
+ pm_runtime_put_sync(pwmchip_parent(chip));
+}
+
+static int tegra_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
+ const struct pwm_state *state)
+{
+ int err;
+ bool enabled = pwm->state.enabled;
+
+ if (state->polarity != PWM_POLARITY_NORMAL)
+ return -EINVAL;
+
+ if (!state->enabled) {
+ if (enabled)
+ tegra_pwm_disable(chip, pwm);
+
+ return 0;
+ }
+
+ err = tegra_pwm_config(chip, pwm, state->duty_cycle, state->period);
+ if (err)
+ return err;
+
+ if (!enabled)
+ err = tegra_pwm_enable(chip, pwm);
+
+ return err;
+}
+
+static const struct pwm_ops tegra_pwm_ops = {
+ .apply = tegra_pwm_apply,
+};
+
+static int tegra_pwm_probe(struct platform_device *pdev)
+{
+ struct pwm_chip *chip;
+ struct tegra_pwm_chip *pc;
+ const struct tegra_pwm_soc *soc;
+ int ret;
+
+ soc = of_device_get_match_data(&pdev->dev);
+
+ chip = devm_pwmchip_alloc(&pdev->dev, soc->num_channels, sizeof(*pc));
+ if (IS_ERR(chip))
+ return PTR_ERR(chip);
+ pc = to_tegra_pwm_chip(chip);
+
+ pc->soc = soc;
+
+ pc->regs = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(pc->regs))
+ return PTR_ERR(pc->regs);
+
+ platform_set_drvdata(pdev, chip);
+
+ pc->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(pc->clk))
+ return PTR_ERR(pc->clk);
+
+ ret = devm_tegra_core_dev_init_opp_table_common(&pdev->dev);
+ if (ret)
+ return ret;
+
+ pm_runtime_enable(&pdev->dev);
+ ret = pm_runtime_resume_and_get(&pdev->dev);
+ if (ret)
+ return ret;
+
+ /* Set maximum frequency of the IP */
+ ret = dev_pm_opp_set_rate(&pdev->dev, ULONG_MAX);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Failed to set max frequency: %d\n", ret);
+ goto put_pm;
+ }
+
+ /*
+ * The requested and configured frequency may differ due to
+ * clock register resolutions. Get the configured frequency
+ * so that PWM period can be calculated more accurately.
+ */
+ pc->clk_rate = clk_get_rate(pc->clk);
+ if (pc->clk_rate < TEGRA_PWM_DEPTH) {
+ dev_err(&pdev->dev, "clock maximum frequency out of range\n");
+ ret = -ERANGE;
+ goto put_pm;
+ }
+
+ /* Set minimum limit of PWM period for the IP */
+ pc->min_period_ns =
+ (NSEC_PER_SEC / (pc->clk_rate / TEGRA_PWM_DEPTH)) + 1;
+
+ pc->rst = devm_reset_control_get_exclusive(&pdev->dev, "pwm");
+ if (IS_ERR(pc->rst)) {
+ ret = PTR_ERR(pc->rst);
+ dev_err(&pdev->dev, "Reset control is not found: %d\n", ret);
+ goto put_pm;
+ }
+
+ reset_control_deassert(pc->rst);
+
+ chip->ops = &tegra_pwm_ops;
+
+ ret = pwmchip_add(chip);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "pwmchip_add() failed: %d\n", ret);
+ reset_control_assert(pc->rst);
+ goto put_pm;
+ }
+
+ pm_runtime_put(&pdev->dev);
+
+ return 0;
+put_pm:
+ pm_runtime_put_sync_suspend(&pdev->dev);
+ pm_runtime_force_suspend(&pdev->dev);
+ return ret;
+}
+
+static void tegra_pwm_remove(struct platform_device *pdev)
+{
+ struct pwm_chip *chip = platform_get_drvdata(pdev);
+ struct tegra_pwm_chip *pc = to_tegra_pwm_chip(chip);
+
+ pwmchip_remove(chip);
+
+ reset_control_assert(pc->rst);
+
+ pm_runtime_force_suspend(&pdev->dev);
+}
+
+static int __maybe_unused tegra_pwm_runtime_suspend(struct device *dev)
+{
+ struct pwm_chip *chip = dev_get_drvdata(dev);
+ struct tegra_pwm_chip *pc = to_tegra_pwm_chip(chip);
+ int err;
+
+ clk_disable_unprepare(pc->clk);
+
+ err = pinctrl_pm_select_sleep_state(dev);
+ if (err) {
+ clk_prepare_enable(pc->clk);
+ return err;
+ }
+
+ return 0;
+}
+
+static int __maybe_unused tegra_pwm_runtime_resume(struct device *dev)
+{
+ struct pwm_chip *chip = dev_get_drvdata(dev);
+ struct tegra_pwm_chip *pc = to_tegra_pwm_chip(chip);
+ int err;
+
+ err = pinctrl_pm_select_default_state(dev);
+ if (err)
+ return err;
+
+ err = clk_prepare_enable(pc->clk);
+ if (err) {
+ pinctrl_pm_select_sleep_state(dev);
+ return err;
+ }
+
+ return 0;
+}
+
+static const struct tegra_pwm_soc tegra20_pwm_soc = {
+ .num_channels = 4,
+ .enable_reg = TEGRA_PWM_CSR_0,
+ .scale_width = 13,
+};
+
+static const struct tegra_pwm_soc tegra186_pwm_soc = {
+ .num_channels = 1,
+ .enable_reg = TEGRA_PWM_CSR_0,
+ .scale_width = 13,
+};
+
+static const struct tegra_pwm_soc tegra264_pwm_soc = {
+ .num_channels = 1,
+ .enable_reg = TEGRA_PWM_CSR_1,
+ .scale_width = 16,
+};
+
+static const struct of_device_id tegra_pwm_of_match[] = {
+ { .compatible = "nvidia,tegra20-pwm", .data = &tegra20_pwm_soc },
+ { .compatible = "nvidia,tegra186-pwm", .data = &tegra186_pwm_soc },
+ { .compatible = "nvidia,tegra194-pwm", .data = &tegra186_pwm_soc },
+ { .compatible = "nvidia,tegra264-pwm", .data = &tegra264_pwm_soc },
+ { }
+};
+MODULE_DEVICE_TABLE(of, tegra_pwm_of_match);
+
+static const struct dev_pm_ops tegra_pwm_pm_ops = {
+ SET_RUNTIME_PM_OPS(tegra_pwm_runtime_suspend, tegra_pwm_runtime_resume,
+ NULL)
+ SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+ pm_runtime_force_resume)
+};
+
+static struct platform_driver tegra_pwm_driver = {
+ .driver = {
+ .name = "tegra-pwm",
+ .of_match_table = tegra_pwm_of_match,
+ .pm = &tegra_pwm_pm_ops,
+ },
+ .probe = tegra_pwm_probe,
+ .remove = tegra_pwm_remove,
+};
+
+module_platform_driver(tegra_pwm_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Sandipan Patra <spatra@nvidia.com>");
+MODULE_DESCRIPTION("Tegra PWM controller driver");
+MODULE_ALIAS("platform:tegra-pwm");
diff --git a/drivers/pwm/pwm-tiecap.c b/drivers/pwm/pwm-tiecap.c
new file mode 100644
index 000000000..3751737ea
--- /dev/null
+++ b/drivers/pwm/pwm-tiecap.c
@@ -0,0 +1,337 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * ECAP PWM driver
+ *
+ * Copyright (C) 2012 Texas Instruments, Inc. - https://www.ti.com/
+ *
+ * Hardware properties:
+ * - On disable the PWM pin becomes an input, so the behaviour depends on
+ * external wiring.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/pm_runtime.h>
+#include <linux/pwm.h>
+#include <linux/of.h>
+
+/* ECAP registers and bits definitions */
+#define CAP1 0x08
+#define CAP2 0x0C
+#define CAP3 0x10
+#define CAP4 0x14
+#define ECCTL2 0x2A
+#define ECCTL2_APWM_POL_LOW BIT(10)
+#define ECCTL2_APWM_MODE BIT(9)
+#define ECCTL2_SYNC_SEL_DISA (BIT(7) | BIT(6))
+#define ECCTL2_TSCTR_FREERUN BIT(4)
+
+struct ecap_context {
+ u32 cap3;
+ u32 cap4;
+ u16 ecctl2;
+};
+
+struct ecap_pwm_chip {
+ unsigned int clk_rate;
+ void __iomem *mmio_base;
+ struct ecap_context ctx;
+};
+
+static inline struct ecap_pwm_chip *to_ecap_pwm_chip(struct pwm_chip *chip)
+{
+ return pwmchip_get_drvdata(chip);
+}
+
+/*
+ * period_ns = 10^9 * period_cycles / PWM_CLK_RATE
+ * duty_ns = 10^9 * duty_cycles / PWM_CLK_RATE
+ */
+static int ecap_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
+ int duty_ns, int period_ns, int enabled)
+{
+ struct ecap_pwm_chip *pc = to_ecap_pwm_chip(chip);
+ u32 period_cycles, duty_cycles;
+ unsigned long long c;
+ u16 value;
+
+ c = pc->clk_rate;
+ c = c * period_ns;
+ do_div(c, NSEC_PER_SEC);
+ period_cycles = (u32)c;
+
+ if (period_cycles < 1) {
+ period_cycles = 1;
+ duty_cycles = 1;
+ } else {
+ c = pc->clk_rate;
+ c = c * duty_ns;
+ do_div(c, NSEC_PER_SEC);
+ duty_cycles = (u32)c;
+ }
+
+ pm_runtime_get_sync(pwmchip_parent(chip));
+
+ value = readw(pc->mmio_base + ECCTL2);
+
+ /* Configure APWM mode & disable sync option */
+ value |= ECCTL2_APWM_MODE | ECCTL2_SYNC_SEL_DISA;
+
+ writew(value, pc->mmio_base + ECCTL2);
+
+ if (!enabled) {
+ /* Update active registers if not running */
+ writel(duty_cycles, pc->mmio_base + CAP2);
+ writel(period_cycles, pc->mmio_base + CAP1);
+ } else {
+ /*
+ * Update shadow registers to configure period and
+ * compare values. This helps current PWM period to
+ * complete on reconfiguring
+ */
+ writel(duty_cycles, pc->mmio_base + CAP4);
+ writel(period_cycles, pc->mmio_base + CAP3);
+ }
+
+ if (!enabled) {
+ value = readw(pc->mmio_base + ECCTL2);
+ /* Disable APWM mode to put APWM output Low */
+ value &= ~ECCTL2_APWM_MODE;
+ writew(value, pc->mmio_base + ECCTL2);
+ }
+
+ pm_runtime_put_sync(pwmchip_parent(chip));
+
+ return 0;
+}
+
+static int ecap_pwm_set_polarity(struct pwm_chip *chip, struct pwm_device *pwm,
+ enum pwm_polarity polarity)
+{
+ struct ecap_pwm_chip *pc = to_ecap_pwm_chip(chip);
+ u16 value;
+
+ pm_runtime_get_sync(pwmchip_parent(chip));
+
+ value = readw(pc->mmio_base + ECCTL2);
+
+ if (polarity == PWM_POLARITY_INVERSED)
+ /* Duty cycle defines LOW period of PWM */
+ value |= ECCTL2_APWM_POL_LOW;
+ else
+ /* Duty cycle defines HIGH period of PWM */
+ value &= ~ECCTL2_APWM_POL_LOW;
+
+ writew(value, pc->mmio_base + ECCTL2);
+
+ pm_runtime_put_sync(pwmchip_parent(chip));
+
+ return 0;
+}
+
+static int ecap_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct ecap_pwm_chip *pc = to_ecap_pwm_chip(chip);
+ u16 value;
+
+ /* Leave clock enabled on enabling PWM */
+ pm_runtime_get_sync(pwmchip_parent(chip));
+
+ /*
+ * Enable 'Free run Time stamp counter mode' to start counter
+ * and 'APWM mode' to enable APWM output
+ */
+ value = readw(pc->mmio_base + ECCTL2);
+ value |= ECCTL2_TSCTR_FREERUN | ECCTL2_APWM_MODE;
+ writew(value, pc->mmio_base + ECCTL2);
+
+ return 0;
+}
+
+static void ecap_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct ecap_pwm_chip *pc = to_ecap_pwm_chip(chip);
+ u16 value;
+
+ /*
+ * Disable 'Free run Time stamp counter mode' to stop counter
+ * and 'APWM mode' to put APWM output to low
+ */
+ value = readw(pc->mmio_base + ECCTL2);
+ value &= ~(ECCTL2_TSCTR_FREERUN | ECCTL2_APWM_MODE);
+ writew(value, pc->mmio_base + ECCTL2);
+
+ /* Disable clock on PWM disable */
+ pm_runtime_put_sync(pwmchip_parent(chip));
+}
+
+static int ecap_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
+ const struct pwm_state *state)
+{
+ int err;
+ int enabled = pwm->state.enabled;
+
+ if (state->polarity != pwm->state.polarity) {
+
+ if (enabled) {
+ ecap_pwm_disable(chip, pwm);
+ enabled = false;
+ }
+
+ err = ecap_pwm_set_polarity(chip, pwm, state->polarity);
+ if (err)
+ return err;
+ }
+
+ if (!state->enabled) {
+ if (enabled)
+ ecap_pwm_disable(chip, pwm);
+ return 0;
+ }
+
+ if (state->period > NSEC_PER_SEC)
+ return -ERANGE;
+
+ err = ecap_pwm_config(chip, pwm, state->duty_cycle,
+ state->period, enabled);
+ if (err)
+ return err;
+
+ if (!enabled)
+ return ecap_pwm_enable(chip, pwm);
+
+ return 0;
+}
+
+static const struct pwm_ops ecap_pwm_ops = {
+ .apply = ecap_pwm_apply,
+};
+
+static const struct of_device_id ecap_of_match[] = {
+ { .compatible = "ti,am3352-ecap" },
+ { .compatible = "ti,am33xx-ecap" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, ecap_of_match);
+
+static int ecap_pwm_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct ecap_pwm_chip *pc;
+ struct pwm_chip *chip;
+ struct clk *clk;
+ int ret;
+
+ chip = devm_pwmchip_alloc(&pdev->dev, 1, sizeof(*pc));
+ if (IS_ERR(chip))
+ return PTR_ERR(chip);
+ pc = to_ecap_pwm_chip(chip);
+
+ clk = devm_clk_get(&pdev->dev, "fck");
+ if (IS_ERR(clk)) {
+ if (of_device_is_compatible(np, "ti,am33xx-ecap")) {
+ dev_warn(&pdev->dev, "Binding is obsolete.\n");
+ clk = devm_clk_get(pdev->dev.parent, "fck");
+ }
+ }
+
+ if (IS_ERR(clk)) {
+ dev_err(&pdev->dev, "failed to get clock\n");
+ return PTR_ERR(clk);
+ }
+
+ pc->clk_rate = clk_get_rate(clk);
+ if (!pc->clk_rate) {
+ dev_err(&pdev->dev, "failed to get clock rate\n");
+ return -EINVAL;
+ }
+
+ chip->ops = &ecap_pwm_ops;
+
+ pc->mmio_base = devm_platform_ioremap_resource(pdev, 0);
+ 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;
+ }
+
+ platform_set_drvdata(pdev, chip);
+ pm_runtime_enable(&pdev->dev);
+
+ return 0;
+}
+
+static void ecap_pwm_remove(struct platform_device *pdev)
+{
+ pm_runtime_disable(&pdev->dev);
+}
+
+static void ecap_pwm_save_context(struct pwm_chip *chip)
+{
+ struct ecap_pwm_chip *pc = to_ecap_pwm_chip(chip);
+
+ pm_runtime_get_sync(pwmchip_parent(chip));
+ pc->ctx.ecctl2 = readw(pc->mmio_base + ECCTL2);
+ pc->ctx.cap4 = readl(pc->mmio_base + CAP4);
+ pc->ctx.cap3 = readl(pc->mmio_base + CAP3);
+ pm_runtime_put_sync(pwmchip_parent(chip));
+}
+
+static void ecap_pwm_restore_context(struct pwm_chip *chip)
+{
+ struct ecap_pwm_chip *pc = to_ecap_pwm_chip(chip);
+
+ writel(pc->ctx.cap3, pc->mmio_base + CAP3);
+ writel(pc->ctx.cap4, pc->mmio_base + CAP4);
+ writew(pc->ctx.ecctl2, pc->mmio_base + ECCTL2);
+}
+
+static int ecap_pwm_suspend(struct device *dev)
+{
+ struct pwm_chip *chip = dev_get_drvdata(dev);
+ struct pwm_device *pwm = chip->pwms;
+
+ ecap_pwm_save_context(chip);
+
+ /* Disable explicitly if PWM is running */
+ if (pwm_is_enabled(pwm))
+ pm_runtime_put_sync(dev);
+
+ return 0;
+}
+
+static int ecap_pwm_resume(struct device *dev)
+{
+ struct pwm_chip *chip = dev_get_drvdata(dev);
+ struct pwm_device *pwm = chip->pwms;
+
+ /* Enable explicitly if PWM was running */
+ if (pwm_is_enabled(pwm))
+ pm_runtime_get_sync(dev);
+
+ ecap_pwm_restore_context(chip);
+ return 0;
+}
+
+static DEFINE_SIMPLE_DEV_PM_OPS(ecap_pwm_pm_ops, ecap_pwm_suspend, ecap_pwm_resume);
+
+static struct platform_driver ecap_pwm_driver = {
+ .driver = {
+ .name = "ecap",
+ .of_match_table = ecap_of_match,
+ .pm = pm_ptr(&ecap_pwm_pm_ops),
+ },
+ .probe = ecap_pwm_probe,
+ .remove = ecap_pwm_remove,
+};
+module_platform_driver(ecap_pwm_driver);
+
+MODULE_DESCRIPTION("ECAP PWM driver");
+MODULE_AUTHOR("Texas Instruments");
+MODULE_LICENSE("GPL");
diff --git a/drivers/pwm/pwm-tiehrpwm.c b/drivers/pwm/pwm-tiehrpwm.c
new file mode 100644
index 000000000..3a53489f0
--- /dev/null
+++ b/drivers/pwm/pwm-tiehrpwm.c
@@ -0,0 +1,578 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * EHRPWM PWM driver
+ *
+ * Copyright (C) 2012 Texas Instruments, Inc. - https://www.ti.com/
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/pm_runtime.h>
+#include <linux/of.h>
+
+/* EHRPWM registers and bits definitions */
+
+/* Time base module registers */
+#define TBCTL 0x00
+#define TBPRD 0x0A
+
+#define TBCTL_PRDLD_MASK BIT(3)
+#define TBCTL_PRDLD_SHDW 0
+#define TBCTL_PRDLD_IMDT BIT(3)
+#define TBCTL_CLKDIV_MASK (BIT(12) | BIT(11) | BIT(10) | BIT(9) | \
+ BIT(8) | BIT(7))
+#define TBCTL_CTRMODE_MASK (BIT(1) | BIT(0))
+#define TBCTL_CTRMODE_UP 0
+#define TBCTL_CTRMODE_DOWN BIT(0)
+#define TBCTL_CTRMODE_UPDOWN BIT(1)
+#define TBCTL_CTRMODE_FREEZE (BIT(1) | BIT(0))
+
+#define TBCTL_HSPCLKDIV_SHIFT 7
+#define TBCTL_CLKDIV_SHIFT 10
+
+#define CLKDIV_MAX 7
+#define HSPCLKDIV_MAX 7
+#define PERIOD_MAX 0x10000
+
+/* compare module registers */
+#define CMPA 0x12
+#define CMPB 0x14
+
+/* Action qualifier module registers */
+#define AQCTLA 0x16
+#define AQCTLB 0x18
+#define AQSFRC 0x1A
+#define AQCSFRC 0x1C
+
+#define AQCTL_CBU_MASK (BIT(9) | BIT(8))
+#define AQCTL_CBU_FRCLOW BIT(8)
+#define AQCTL_CBU_FRCHIGH BIT(9)
+#define AQCTL_CBU_FRCTOGGLE (BIT(9) | BIT(8))
+#define AQCTL_CAU_MASK (BIT(5) | BIT(4))
+#define AQCTL_CAU_FRCLOW BIT(4)
+#define AQCTL_CAU_FRCHIGH BIT(5)
+#define AQCTL_CAU_FRCTOGGLE (BIT(5) | BIT(4))
+#define AQCTL_PRD_MASK (BIT(3) | BIT(2))
+#define AQCTL_PRD_FRCLOW BIT(2)
+#define AQCTL_PRD_FRCHIGH BIT(3)
+#define AQCTL_PRD_FRCTOGGLE (BIT(3) | BIT(2))
+#define AQCTL_ZRO_MASK (BIT(1) | BIT(0))
+#define AQCTL_ZRO_FRCLOW BIT(0)
+#define AQCTL_ZRO_FRCHIGH BIT(1)
+#define AQCTL_ZRO_FRCTOGGLE (BIT(1) | BIT(0))
+
+#define AQCTL_CHANA_POLNORMAL (AQCTL_CAU_FRCLOW | AQCTL_ZRO_FRCHIGH)
+#define AQCTL_CHANA_POLINVERSED (AQCTL_CAU_FRCHIGH | AQCTL_ZRO_FRCLOW)
+#define AQCTL_CHANB_POLNORMAL (AQCTL_CBU_FRCLOW | AQCTL_ZRO_FRCHIGH)
+#define AQCTL_CHANB_POLINVERSED (AQCTL_CBU_FRCHIGH | AQCTL_ZRO_FRCLOW)
+
+#define AQSFRC_RLDCSF_MASK (BIT(7) | BIT(6))
+#define AQSFRC_RLDCSF_ZRO 0
+#define AQSFRC_RLDCSF_PRD BIT(6)
+#define AQSFRC_RLDCSF_ZROPRD BIT(7)
+#define AQSFRC_RLDCSF_IMDT (BIT(7) | BIT(6))
+
+#define AQCSFRC_CSFB_MASK (BIT(3) | BIT(2))
+#define AQCSFRC_CSFB_FRCDIS 0
+#define AQCSFRC_CSFB_FRCLOW BIT(2)
+#define AQCSFRC_CSFB_FRCHIGH BIT(3)
+#define AQCSFRC_CSFB_DISSWFRC (BIT(3) | BIT(2))
+#define AQCSFRC_CSFA_MASK (BIT(1) | BIT(0))
+#define AQCSFRC_CSFA_FRCDIS 0
+#define AQCSFRC_CSFA_FRCLOW BIT(0)
+#define AQCSFRC_CSFA_FRCHIGH BIT(1)
+#define AQCSFRC_CSFA_DISSWFRC (BIT(1) | BIT(0))
+
+#define NUM_PWM_CHANNEL 2 /* EHRPWM channels */
+
+struct ehrpwm_context {
+ u16 tbctl;
+ u16 tbprd;
+ u16 cmpa;
+ u16 cmpb;
+ u16 aqctla;
+ u16 aqctlb;
+ u16 aqsfrc;
+ u16 aqcsfrc;
+};
+
+struct ehrpwm_pwm_chip {
+ unsigned long clk_rate;
+ void __iomem *mmio_base;
+ unsigned long period_cycles[NUM_PWM_CHANNEL];
+ struct clk *tbclk;
+ struct ehrpwm_context ctx;
+};
+
+static inline struct ehrpwm_pwm_chip *to_ehrpwm_pwm_chip(struct pwm_chip *chip)
+{
+ return pwmchip_get_drvdata(chip);
+}
+
+static inline u16 ehrpwm_read(void __iomem *base, unsigned int offset)
+{
+ return readw(base + offset);
+}
+
+static inline void ehrpwm_write(void __iomem *base, unsigned int offset,
+ u16 value)
+{
+ writew(value, base + offset);
+}
+
+static void ehrpwm_modify(void __iomem *base, unsigned int offset, u16 mask,
+ u16 value)
+{
+ unsigned short val;
+
+ val = readw(base + offset);
+ val &= ~mask;
+ val |= value & mask;
+ writew(val, base + offset);
+}
+
+/**
+ * set_prescale_div - Set up the prescaler divider function
+ * @rqst_prescaler: prescaler value min
+ * @prescale_div: prescaler value set
+ * @tb_clk_div: Time Base Control prescaler bits
+ */
+static int set_prescale_div(unsigned long rqst_prescaler, u16 *prescale_div,
+ u16 *tb_clk_div)
+{
+ unsigned int clkdiv, hspclkdiv;
+
+ for (clkdiv = 0; clkdiv <= CLKDIV_MAX; clkdiv++) {
+ for (hspclkdiv = 0; hspclkdiv <= HSPCLKDIV_MAX; hspclkdiv++) {
+ /*
+ * calculations for prescaler value :
+ * prescale_div = HSPCLKDIVIDER * CLKDIVIDER.
+ * HSPCLKDIVIDER = 2 ** hspclkdiv
+ * CLKDIVIDER = (1), if clkdiv == 0 *OR*
+ * (2 * clkdiv), if clkdiv != 0
+ *
+ * Configure prescale_div value such that period
+ * register value is less than 65535.
+ */
+
+ *prescale_div = (1 << clkdiv) *
+ (hspclkdiv ? (hspclkdiv * 2) : 1);
+ if (*prescale_div >= rqst_prescaler) {
+ *tb_clk_div = (clkdiv << TBCTL_CLKDIV_SHIFT) |
+ (hspclkdiv << TBCTL_HSPCLKDIV_SHIFT);
+ return 0;
+ }
+ }
+ }
+
+ return 1;
+}
+
+/*
+ * period_ns = 10^9 * (ps_divval * period_cycles) / PWM_CLK_RATE
+ * duty_ns = 10^9 * (ps_divval * duty_cycles) / PWM_CLK_RATE
+ */
+static int ehrpwm_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
+ u64 duty_ns, u64 period_ns, enum pwm_polarity polarity)
+{
+ struct ehrpwm_pwm_chip *pc = to_ehrpwm_pwm_chip(chip);
+ u32 period_cycles, duty_cycles;
+ u16 ps_divval, tb_divval;
+ unsigned int i, cmp_reg;
+ unsigned long long c;
+ u16 aqctl_val, aqctl_mask;
+ unsigned int aqctl_reg;
+
+ if (period_ns > NSEC_PER_SEC)
+ return -ERANGE;
+
+ c = pc->clk_rate;
+ c = c * period_ns;
+ do_div(c, NSEC_PER_SEC);
+ period_cycles = (unsigned long)c;
+
+ c = pc->clk_rate;
+ c = c * duty_ns;
+ do_div(c, NSEC_PER_SEC);
+ duty_cycles = (unsigned long)c;
+
+ /*
+ * Period values should be same for multiple PWM channels as IP uses
+ * same period register for multiple channels.
+ */
+ for (i = 0; i < NUM_PWM_CHANNEL; i++) {
+ if (pc->period_cycles[i] &&
+ (pc->period_cycles[i] != period_cycles)) {
+ /*
+ * Allow channel to reconfigure period if no other
+ * channels being configured.
+ */
+ if (i == pwm->hwpwm)
+ continue;
+
+ dev_err(pwmchip_parent(chip),
+ "period value conflicts with channel %u\n",
+ i);
+ return -EINVAL;
+ }
+ }
+
+ pc->period_cycles[pwm->hwpwm] = period_cycles;
+
+ /* Configure clock prescaler to support Low frequency PWM wave */
+ if (set_prescale_div(DIV_ROUND_UP(period_cycles, PERIOD_MAX), &ps_divval,
+ &tb_divval)) {
+ dev_err(pwmchip_parent(chip), "Unsupported values\n");
+ return -EINVAL;
+ }
+
+ /* Update period & duty cycle with presacler division */
+ period_cycles = period_cycles / ps_divval;
+ duty_cycles = duty_cycles / ps_divval;
+
+ if (period_cycles < 1)
+ period_cycles = 1;
+
+ /* Update clock prescaler values */
+ ehrpwm_modify(pc->mmio_base, TBCTL, TBCTL_CLKDIV_MASK, tb_divval);
+
+ if (pwm->hwpwm == 1) {
+ /* Channel 1 configured with compare B register */
+ cmp_reg = CMPB;
+
+ aqctl_reg = AQCTLB;
+ aqctl_mask = AQCTL_CBU_MASK;
+
+ if (polarity == PWM_POLARITY_INVERSED)
+ aqctl_val = AQCTL_CHANB_POLINVERSED;
+ else
+ aqctl_val = AQCTL_CHANB_POLNORMAL;
+
+ /* if duty_cycle is big, don't toggle on CBU */
+ if (duty_cycles > period_cycles)
+ aqctl_val &= ~AQCTL_CBU_MASK;
+
+ } else {
+ /* Channel 0 configured with compare A register */
+ cmp_reg = CMPA;
+
+ aqctl_reg = AQCTLA;
+ aqctl_mask = AQCTL_CAU_MASK;
+
+ if (polarity == PWM_POLARITY_INVERSED)
+ aqctl_val = AQCTL_CHANA_POLINVERSED;
+ else
+ aqctl_val = AQCTL_CHANA_POLNORMAL;
+
+ /* if duty_cycle is big, don't toggle on CAU */
+ if (duty_cycles > period_cycles)
+ aqctl_val &= ~AQCTL_CAU_MASK;
+ }
+
+ aqctl_mask |= AQCTL_PRD_MASK | AQCTL_ZRO_MASK;
+ ehrpwm_modify(pc->mmio_base, aqctl_reg, aqctl_mask, aqctl_val);
+
+ /* Configure shadow loading on Period register */
+ ehrpwm_modify(pc->mmio_base, TBCTL, TBCTL_PRDLD_MASK, TBCTL_PRDLD_SHDW);
+
+ ehrpwm_write(pc->mmio_base, TBPRD, period_cycles - 1);
+
+ /* Configure ehrpwm counter for up-count mode */
+ ehrpwm_modify(pc->mmio_base, TBCTL, TBCTL_CTRMODE_MASK,
+ TBCTL_CTRMODE_UP);
+
+ if (!(duty_cycles > period_cycles))
+ ehrpwm_write(pc->mmio_base, cmp_reg, duty_cycles);
+
+ return 0;
+}
+
+static int ehrpwm_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct ehrpwm_pwm_chip *pc = to_ehrpwm_pwm_chip(chip);
+ u16 aqcsfrc_val, aqcsfrc_mask;
+ int ret;
+
+ /* Leave clock enabled on enabling PWM */
+ pm_runtime_get_sync(pwmchip_parent(chip));
+
+ /* Disabling Action Qualifier on PWM output */
+ if (pwm->hwpwm) {
+ aqcsfrc_val = AQCSFRC_CSFB_FRCDIS;
+ aqcsfrc_mask = AQCSFRC_CSFB_MASK;
+ } else {
+ aqcsfrc_val = AQCSFRC_CSFA_FRCDIS;
+ aqcsfrc_mask = AQCSFRC_CSFA_MASK;
+ }
+
+ /* Changes to shadow mode */
+ ehrpwm_modify(pc->mmio_base, AQSFRC, AQSFRC_RLDCSF_MASK,
+ AQSFRC_RLDCSF_ZRO);
+
+ ehrpwm_modify(pc->mmio_base, AQCSFRC, aqcsfrc_mask, aqcsfrc_val);
+
+ /* Enable TBCLK */
+ ret = clk_enable(pc->tbclk);
+ if (ret) {
+ dev_err(pwmchip_parent(chip), "Failed to enable TBCLK for %s: %d\n",
+ dev_name(pwmchip_parent(chip)), ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void ehrpwm_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct ehrpwm_pwm_chip *pc = to_ehrpwm_pwm_chip(chip);
+ u16 aqcsfrc_val, aqcsfrc_mask;
+
+ /* Action Qualifier puts PWM output low forcefully */
+ if (pwm->hwpwm) {
+ aqcsfrc_val = AQCSFRC_CSFB_FRCLOW;
+ aqcsfrc_mask = AQCSFRC_CSFB_MASK;
+ } else {
+ aqcsfrc_val = AQCSFRC_CSFA_FRCLOW;
+ aqcsfrc_mask = AQCSFRC_CSFA_MASK;
+ }
+
+ /* Update shadow register first before modifying active register */
+ ehrpwm_modify(pc->mmio_base, AQSFRC, AQSFRC_RLDCSF_MASK,
+ AQSFRC_RLDCSF_ZRO);
+ ehrpwm_modify(pc->mmio_base, AQCSFRC, aqcsfrc_mask, aqcsfrc_val);
+ /*
+ * Changes to immediate action on Action Qualifier. This puts
+ * Action Qualifier control on PWM output from next TBCLK
+ */
+ ehrpwm_modify(pc->mmio_base, AQSFRC, AQSFRC_RLDCSF_MASK,
+ AQSFRC_RLDCSF_IMDT);
+
+ ehrpwm_modify(pc->mmio_base, AQCSFRC, aqcsfrc_mask, aqcsfrc_val);
+
+ /* Disabling TBCLK on PWM disable */
+ clk_disable(pc->tbclk);
+
+ /* Disable clock on PWM disable */
+ pm_runtime_put_sync(pwmchip_parent(chip));
+}
+
+static void ehrpwm_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct ehrpwm_pwm_chip *pc = to_ehrpwm_pwm_chip(chip);
+
+ /* Don't let a pwm without consumer block requests to the other channel */
+ pc->period_cycles[pwm->hwpwm] = 0;
+}
+
+static int ehrpwm_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
+ const struct pwm_state *state)
+{
+ int err;
+ bool enabled = pwm->state.enabled;
+
+ guard(pm_runtime_active)(pwmchip_parent(chip));
+
+ if (state->polarity != pwm->state.polarity) {
+ if (enabled) {
+ ehrpwm_pwm_disable(chip, pwm);
+ enabled = false;
+ }
+ }
+
+ if (!state->enabled) {
+ if (enabled)
+ ehrpwm_pwm_disable(chip, pwm);
+ return 0;
+ }
+
+ err = ehrpwm_pwm_config(chip, pwm, state->duty_cycle, state->period, state->polarity);
+ if (err)
+ return err;
+
+ if (!enabled)
+ err = ehrpwm_pwm_enable(chip, pwm);
+
+ return err;
+}
+
+static const struct pwm_ops ehrpwm_pwm_ops = {
+ .free = ehrpwm_pwm_free,
+ .apply = ehrpwm_pwm_apply,
+};
+
+static const struct of_device_id ehrpwm_of_match[] = {
+ { .compatible = "ti,am3352-ehrpwm" },
+ { .compatible = "ti,am33xx-ehrpwm" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, ehrpwm_of_match);
+
+static int ehrpwm_pwm_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct ehrpwm_pwm_chip *pc;
+ struct pwm_chip *chip;
+ struct clk *clk;
+ int ret;
+
+ chip = devm_pwmchip_alloc(&pdev->dev, NUM_PWM_CHANNEL, sizeof(*pc));
+ if (IS_ERR(chip))
+ return PTR_ERR(chip);
+ pc = to_ehrpwm_pwm_chip(chip);
+
+ clk = devm_clk_get(&pdev->dev, "fck");
+ if (IS_ERR(clk)) {
+ if (of_device_is_compatible(np, "ti,am33xx-ecap")) {
+ dev_warn(&pdev->dev, "Binding is obsolete.\n");
+ clk = devm_clk_get(pdev->dev.parent, "fck");
+ }
+ }
+
+ if (IS_ERR(clk))
+ return dev_err_probe(&pdev->dev, PTR_ERR(clk), "Failed to get fck\n");
+
+ pc->clk_rate = clk_get_rate(clk);
+ if (!pc->clk_rate) {
+ dev_err(&pdev->dev, "failed to get clock rate\n");
+ return -EINVAL;
+ }
+
+ chip->ops = &ehrpwm_pwm_ops;
+
+ pc->mmio_base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(pc->mmio_base))
+ return PTR_ERR(pc->mmio_base);
+
+ /* Acquire tbclk for Time Base EHRPWM submodule */
+ pc->tbclk = devm_clk_get(&pdev->dev, "tbclk");
+ if (IS_ERR(pc->tbclk))
+ return dev_err_probe(&pdev->dev, PTR_ERR(pc->tbclk), "Failed to get tbclk\n");
+
+ ret = clk_prepare(pc->tbclk);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "clk_prepare() failed: %d\n", ret);
+ return ret;
+ }
+
+ ret = pwmchip_add(chip);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "pwmchip_add() failed: %d\n", ret);
+ goto err_clk_unprepare;
+ }
+
+ platform_set_drvdata(pdev, chip);
+ pm_runtime_enable(&pdev->dev);
+
+ return 0;
+
+err_clk_unprepare:
+ clk_unprepare(pc->tbclk);
+
+ return ret;
+}
+
+static void ehrpwm_pwm_remove(struct platform_device *pdev)
+{
+ struct pwm_chip *chip = platform_get_drvdata(pdev);
+ struct ehrpwm_pwm_chip *pc = to_ehrpwm_pwm_chip(chip);
+
+ pwmchip_remove(chip);
+
+ clk_unprepare(pc->tbclk);
+
+ pm_runtime_disable(&pdev->dev);
+}
+
+static void ehrpwm_pwm_save_context(struct pwm_chip *chip)
+{
+ struct ehrpwm_pwm_chip *pc = to_ehrpwm_pwm_chip(chip);
+
+ pm_runtime_get_sync(pwmchip_parent(chip));
+
+ pc->ctx.tbctl = ehrpwm_read(pc->mmio_base, TBCTL);
+ pc->ctx.tbprd = ehrpwm_read(pc->mmio_base, TBPRD);
+ pc->ctx.cmpa = ehrpwm_read(pc->mmio_base, CMPA);
+ pc->ctx.cmpb = ehrpwm_read(pc->mmio_base, CMPB);
+ pc->ctx.aqctla = ehrpwm_read(pc->mmio_base, AQCTLA);
+ pc->ctx.aqctlb = ehrpwm_read(pc->mmio_base, AQCTLB);
+ pc->ctx.aqsfrc = ehrpwm_read(pc->mmio_base, AQSFRC);
+ pc->ctx.aqcsfrc = ehrpwm_read(pc->mmio_base, AQCSFRC);
+
+ pm_runtime_put_sync(pwmchip_parent(chip));
+}
+
+static void ehrpwm_pwm_restore_context(struct pwm_chip *chip)
+{
+ struct ehrpwm_pwm_chip *pc = to_ehrpwm_pwm_chip(chip);
+
+ ehrpwm_write(pc->mmio_base, TBPRD, pc->ctx.tbprd);
+ ehrpwm_write(pc->mmio_base, CMPA, pc->ctx.cmpa);
+ ehrpwm_write(pc->mmio_base, CMPB, pc->ctx.cmpb);
+ ehrpwm_write(pc->mmio_base, AQCTLA, pc->ctx.aqctla);
+ ehrpwm_write(pc->mmio_base, AQCTLB, pc->ctx.aqctlb);
+ ehrpwm_write(pc->mmio_base, AQSFRC, pc->ctx.aqsfrc);
+ ehrpwm_write(pc->mmio_base, AQCSFRC, pc->ctx.aqcsfrc);
+ ehrpwm_write(pc->mmio_base, TBCTL, pc->ctx.tbctl);
+}
+
+static int ehrpwm_pwm_suspend(struct device *dev)
+{
+ struct pwm_chip *chip = dev_get_drvdata(dev);
+ unsigned int i;
+
+ ehrpwm_pwm_save_context(chip);
+
+ for (i = 0; i < chip->npwm; i++) {
+ struct pwm_device *pwm = &chip->pwms[i];
+
+ if (!pwm_is_enabled(pwm))
+ continue;
+
+ /* Disable explicitly if PWM is running */
+ pm_runtime_put_sync(dev);
+ }
+
+ return 0;
+}
+
+static int ehrpwm_pwm_resume(struct device *dev)
+{
+ struct pwm_chip *chip = dev_get_drvdata(dev);
+ unsigned int i;
+
+ for (i = 0; i < chip->npwm; i++) {
+ struct pwm_device *pwm = &chip->pwms[i];
+
+ if (!pwm_is_enabled(pwm))
+ continue;
+
+ /* Enable explicitly if PWM was running */
+ pm_runtime_get_sync(dev);
+ }
+
+ ehrpwm_pwm_restore_context(chip);
+
+ return 0;
+}
+
+static DEFINE_SIMPLE_DEV_PM_OPS(ehrpwm_pwm_pm_ops, ehrpwm_pwm_suspend,
+ ehrpwm_pwm_resume);
+
+static struct platform_driver ehrpwm_pwm_driver = {
+ .driver = {
+ .name = "ehrpwm",
+ .of_match_table = ehrpwm_of_match,
+ .pm = pm_ptr(&ehrpwm_pwm_pm_ops),
+ },
+ .probe = ehrpwm_pwm_probe,
+ .remove = ehrpwm_pwm_remove,
+};
+module_platform_driver(ehrpwm_pwm_driver);
+
+MODULE_DESCRIPTION("EHRPWM PWM driver");
+MODULE_AUTHOR("Texas Instruments");
+MODULE_LICENSE("GPL");
diff --git a/drivers/pwm/pwm-twl-led.c b/drivers/pwm/pwm-twl-led.c
new file mode 100644
index 000000000..e1b6871af
--- /dev/null
+++ b/drivers/pwm/pwm-twl-led.c
@@ -0,0 +1,358 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Driver for TWL4030/6030 Pulse Width Modulator used as LED driver
+ *
+ * Copyright (C) 2012 Texas Instruments
+ * Author: Peter Ujfalusi <peter.ujfalusi@ti.com>
+ *
+ * This driver is a complete rewrite of the former pwm-twl6030.c authorded by:
+ * Hemanth V <hemanthv@ti.com>
+ *
+ * Reference manual for the twl6030 is available at:
+ * https://www.ti.com/lit/ds/symlink/twl6030.pdf
+ *
+ * Limitations:
+ * - The twl6030 hardware only supports two period lengths (128 clock ticks and
+ * 64 clock ticks), the driver only uses 128 ticks
+ * - The hardware doesn't support ON = 0, so the active part of a period doesn't
+ * start at its beginning.
+ * - The hardware could support inverted polarity (with a similar limitation as
+ * for normal: the last clock tick is always inactive).
+ * - The hardware emits a constant low output when disabled.
+ * - A request for .duty_cycle = 0 results in an output wave with one active
+ * clock tick per period. This should better use the disabled state.
+ * - The driver only implements setting the relative duty cycle.
+ * - The driver doesn't implement .get_state().
+ */
+
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+#include <linux/mfd/twl.h>
+#include <linux/slab.h>
+
+/*
+ * This driver handles the PWM driven LED terminals of TWL4030 and TWL6030.
+ * To generate the signal on TWL4030:
+ * - LEDA uses PWMA
+ * - LEDB uses PWMB
+ * TWL6030 has one LED pin with dedicated LEDPWM
+ */
+
+#define TWL4030_LED_MAX 0x7f
+#define TWL6030_LED_MAX 0xff
+
+/* Registers, bits and macro for TWL4030 */
+#define TWL4030_LEDEN_REG 0x00
+#define TWL4030_PWMA_REG 0x01
+
+#define TWL4030_LEDXON (1 << 0)
+#define TWL4030_LEDXPWM (1 << 4)
+#define TWL4030_LED_PINS (TWL4030_LEDXON | TWL4030_LEDXPWM)
+#define TWL4030_LED_TOGGLE(led, x) ((x) << (led))
+
+/* Register, bits and macro for TWL6030 */
+#define TWL6030_LED_PWM_CTRL1 0xf4
+#define TWL6030_LED_PWM_CTRL2 0xf5
+
+#define TWL6040_LED_MODE_HW 0x00
+#define TWL6040_LED_MODE_ON 0x01
+#define TWL6040_LED_MODE_OFF 0x02
+#define TWL6040_LED_MODE_MASK 0x03
+
+static inline struct twl_pwmled_chip *to_twl(struct pwm_chip *chip)
+{
+ return pwmchip_get_drvdata(chip);
+}
+
+static int twl4030_pwmled_config(struct pwm_chip *chip, struct pwm_device *pwm,
+ int duty_ns, int period_ns)
+{
+ int duty_cycle = DIV_ROUND_UP(duty_ns * TWL4030_LED_MAX, period_ns) + 1;
+ u8 pwm_config[2] = { 1, 0 };
+ int base, ret;
+
+ /*
+ * To configure the duty period:
+ * On-cycle is set to 1 (the minimum allowed value)
+ * The off time of 0 is not configurable, so the mapping is:
+ * 0 -> off cycle = 2,
+ * 1 -> off cycle = 2,
+ * 2 -> off cycle = 3,
+ * 126 - > off cycle 127,
+ * 127 - > off cycle 1
+ * When on cycle == off cycle the PWM will be always on
+ */
+ if (duty_cycle == 1)
+ duty_cycle = 2;
+ else if (duty_cycle > TWL4030_LED_MAX)
+ duty_cycle = 1;
+
+ base = pwm->hwpwm * 2 + TWL4030_PWMA_REG;
+
+ pwm_config[1] = duty_cycle;
+
+ ret = twl_i2c_write(TWL4030_MODULE_LED, pwm_config, base, 2);
+ if (ret < 0)
+ dev_err(pwmchip_parent(chip), "%s: Failed to configure PWM\n", pwm->label);
+
+ return ret;
+}
+
+static int twl4030_pwmled_enable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ int ret;
+ u8 val;
+
+ ret = twl_i2c_read_u8(TWL4030_MODULE_LED, &val, TWL4030_LEDEN_REG);
+ if (ret < 0) {
+ dev_err(pwmchip_parent(chip), "%s: Failed to read LEDEN\n", pwm->label);
+ return ret;
+ }
+
+ val |= TWL4030_LED_TOGGLE(pwm->hwpwm, TWL4030_LED_PINS);
+
+ ret = twl_i2c_write_u8(TWL4030_MODULE_LED, val, TWL4030_LEDEN_REG);
+ if (ret < 0)
+ dev_err(pwmchip_parent(chip), "%s: Failed to enable PWM\n", pwm->label);
+
+ return ret;
+}
+
+static void twl4030_pwmled_disable(struct pwm_chip *chip,
+ struct pwm_device *pwm)
+{
+ int ret;
+ u8 val;
+
+ ret = twl_i2c_read_u8(TWL4030_MODULE_LED, &val, TWL4030_LEDEN_REG);
+ if (ret < 0) {
+ dev_err(pwmchip_parent(chip), "%s: Failed to read LEDEN\n", pwm->label);
+ return;
+ }
+
+ val &= ~TWL4030_LED_TOGGLE(pwm->hwpwm, TWL4030_LED_PINS);
+
+ ret = twl_i2c_write_u8(TWL4030_MODULE_LED, val, TWL4030_LEDEN_REG);
+ if (ret < 0)
+ dev_err(pwmchip_parent(chip), "%s: Failed to disable PWM\n", pwm->label);
+}
+
+static int twl4030_pwmled_apply(struct pwm_chip *chip, struct pwm_device *pwm,
+ const struct pwm_state *state)
+{
+ int ret;
+
+ if (state->polarity != PWM_POLARITY_NORMAL)
+ return -EINVAL;
+
+ if (!state->enabled) {
+ if (pwm->state.enabled)
+ twl4030_pwmled_disable(chip, pwm);
+
+ return 0;
+ }
+
+ /*
+ * We cannot skip calling ->config even if state->period ==
+ * pwm->state.period && state->duty_cycle == pwm->state.duty_cycle
+ * because we might have exited early in the last call to
+ * pwm_apply_might_sleep because of !state->enabled and so the two values in
+ * pwm->state might not be configured in hardware.
+ */
+ ret = twl4030_pwmled_config(chip, pwm,
+ state->duty_cycle, state->period);
+ if (ret)
+ return ret;
+
+ if (!pwm->state.enabled)
+ ret = twl4030_pwmled_enable(chip, pwm);
+
+ return ret;
+}
+
+
+static const struct pwm_ops twl4030_pwmled_ops = {
+ .apply = twl4030_pwmled_apply,
+};
+
+static int twl6030_pwmled_config(struct pwm_chip *chip, struct pwm_device *pwm,
+ int duty_ns, int period_ns)
+{
+ int duty_cycle = (duty_ns * TWL6030_LED_MAX) / period_ns;
+ u8 on_time;
+ int ret;
+
+ on_time = duty_cycle & 0xff;
+
+ ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, on_time,
+ TWL6030_LED_PWM_CTRL1);
+ if (ret < 0)
+ dev_err(pwmchip_parent(chip), "%s: Failed to configure PWM\n", pwm->label);
+
+ return ret;
+}
+
+static int twl6030_pwmled_enable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ int ret;
+ u8 val;
+
+ ret = twl_i2c_read_u8(TWL6030_MODULE_ID1, &val, TWL6030_LED_PWM_CTRL2);
+ if (ret < 0) {
+ dev_err(pwmchip_parent(chip), "%s: Failed to read PWM_CTRL2\n",
+ pwm->label);
+ return ret;
+ }
+
+ val &= ~TWL6040_LED_MODE_MASK;
+ val |= TWL6040_LED_MODE_ON;
+
+ ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, val, TWL6030_LED_PWM_CTRL2);
+ if (ret < 0)
+ dev_err(pwmchip_parent(chip), "%s: Failed to enable PWM\n", pwm->label);
+
+ return ret;
+}
+
+static void twl6030_pwmled_disable(struct pwm_chip *chip,
+ struct pwm_device *pwm)
+{
+ int ret;
+ u8 val;
+
+ ret = twl_i2c_read_u8(TWL6030_MODULE_ID1, &val, TWL6030_LED_PWM_CTRL2);
+ if (ret < 0) {
+ dev_err(pwmchip_parent(chip), "%s: Failed to read PWM_CTRL2\n",
+ pwm->label);
+ return;
+ }
+
+ val &= ~TWL6040_LED_MODE_MASK;
+ val |= TWL6040_LED_MODE_OFF;
+
+ ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, val, TWL6030_LED_PWM_CTRL2);
+ if (ret < 0)
+ dev_err(pwmchip_parent(chip), "%s: Failed to disable PWM\n", pwm->label);
+}
+
+static int twl6030_pwmled_apply(struct pwm_chip *chip, struct pwm_device *pwm,
+ const struct pwm_state *state)
+{
+ int err;
+
+ if (state->polarity != pwm->state.polarity)
+ return -EINVAL;
+
+ if (!state->enabled) {
+ if (pwm->state.enabled)
+ twl6030_pwmled_disable(chip, pwm);
+
+ return 0;
+ }
+
+ err = twl6030_pwmled_config(chip, pwm,
+ state->duty_cycle, state->period);
+ if (err)
+ return err;
+
+ if (!pwm->state.enabled)
+ err = twl6030_pwmled_enable(chip, pwm);
+
+ return err;
+}
+
+static int twl6030_pwmled_request(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ int ret;
+ u8 val;
+
+ ret = twl_i2c_read_u8(TWL6030_MODULE_ID1, &val, TWL6030_LED_PWM_CTRL2);
+ if (ret < 0) {
+ dev_err(pwmchip_parent(chip), "%s: Failed to read PWM_CTRL2\n",
+ pwm->label);
+ return ret;
+ }
+
+ val &= ~TWL6040_LED_MODE_MASK;
+ val |= TWL6040_LED_MODE_OFF;
+
+ ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, val, TWL6030_LED_PWM_CTRL2);
+ if (ret < 0)
+ dev_err(pwmchip_parent(chip), "%s: Failed to request PWM\n", pwm->label);
+
+ return ret;
+}
+
+static void twl6030_pwmled_free(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ int ret;
+ u8 val;
+
+ ret = twl_i2c_read_u8(TWL6030_MODULE_ID1, &val, TWL6030_LED_PWM_CTRL2);
+ if (ret < 0) {
+ dev_err(pwmchip_parent(chip), "%s: Failed to read PWM_CTRL2\n",
+ pwm->label);
+ return;
+ }
+
+ val &= ~TWL6040_LED_MODE_MASK;
+ val |= TWL6040_LED_MODE_HW;
+
+ ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, val, TWL6030_LED_PWM_CTRL2);
+ if (ret < 0)
+ dev_err(pwmchip_parent(chip), "%s: Failed to free PWM\n", pwm->label);
+}
+
+static const struct pwm_ops twl6030_pwmled_ops = {
+ .apply = twl6030_pwmled_apply,
+ .request = twl6030_pwmled_request,
+ .free = twl6030_pwmled_free,
+};
+
+static int twl_pwmled_probe(struct platform_device *pdev)
+{
+ struct pwm_chip *chip;
+ unsigned int npwm;
+ const struct pwm_ops *ops;
+
+ if (twl_class_is_4030()) {
+ ops = &twl4030_pwmled_ops;
+ npwm = 2;
+ } else {
+ ops = &twl6030_pwmled_ops;
+ npwm = 1;
+ }
+
+ chip = devm_pwmchip_alloc(&pdev->dev, npwm, 0);
+ if (IS_ERR(chip))
+ return PTR_ERR(chip);
+
+ chip->ops = ops;
+
+ return devm_pwmchip_add(&pdev->dev, chip);
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id twl_pwmled_of_match[] = {
+ { .compatible = "ti,twl4030-pwmled" },
+ { .compatible = "ti,twl6030-pwmled" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, twl_pwmled_of_match);
+#endif
+
+static struct platform_driver twl_pwmled_driver = {
+ .driver = {
+ .name = "twl-pwmled",
+ .of_match_table = of_match_ptr(twl_pwmled_of_match),
+ },
+ .probe = twl_pwmled_probe,
+};
+module_platform_driver(twl_pwmled_driver);
+
+MODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@ti.com>");
+MODULE_DESCRIPTION("PWM driver for TWL4030 and TWL6030 LED outputs");
+MODULE_ALIAS("platform:twl-pwmled");
+MODULE_LICENSE("GPL");
diff --git a/drivers/pwm/pwm-twl.c b/drivers/pwm/pwm-twl.c
new file mode 100644
index 000000000..99c1caf4f
--- /dev/null
+++ b/drivers/pwm/pwm-twl.c
@@ -0,0 +1,382 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Driver for TWL4030/6030 Generic Pulse Width Modulator
+ *
+ * Copyright (C) 2012 Texas Instruments
+ * Author: Peter Ujfalusi <peter.ujfalusi@ti.com>
+ */
+
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+#include <linux/mfd/twl.h>
+#include <linux/slab.h>
+
+/*
+ * This driver handles the PWMs of TWL4030 and TWL6030.
+ * The TRM names for the PWMs on TWL4030 are: PWM0, PWM1
+ * TWL6030 also have two PWMs named in the TRM as PWM1, PWM2
+ */
+
+#define TWL_PWM_MAX 0x7f
+
+/* Registers, bits and macro for TWL4030 */
+#define TWL4030_GPBR1_REG 0x0c
+#define TWL4030_PMBR1_REG 0x0d
+
+/* GPBR1 register bits */
+#define TWL4030_PWMXCLK_ENABLE (1 << 0)
+#define TWL4030_PWMX_ENABLE (1 << 2)
+#define TWL4030_PWMX_BITS (TWL4030_PWMX_ENABLE | TWL4030_PWMXCLK_ENABLE)
+#define TWL4030_PWM_TOGGLE(pwm, x) ((x) << (pwm))
+
+/* PMBR1 register bits */
+#define TWL4030_GPIO6_PWM0_MUTE_MASK (0x03 << 2)
+#define TWL4030_GPIO6_PWM0_MUTE_PWM0 (0x01 << 2)
+#define TWL4030_GPIO7_VIBRASYNC_PWM1_MASK (0x03 << 4)
+#define TWL4030_GPIO7_VIBRASYNC_PWM1_PWM1 (0x03 << 4)
+
+/* Register, bits and macro for TWL6030 */
+#define TWL6030_TOGGLE3_REG 0x92
+
+#define TWL6030_PWMXR (1 << 0)
+#define TWL6030_PWMXS (1 << 1)
+#define TWL6030_PWMXEN (1 << 2)
+#define TWL6030_PWM_TOGGLE(pwm, x) ((x) << (pwm * 3))
+
+struct twl_pwm_chip {
+ struct mutex mutex;
+ u8 twl6030_toggle3;
+ u8 twl4030_pwm_mux;
+};
+
+static inline struct twl_pwm_chip *to_twl(struct pwm_chip *chip)
+{
+ return pwmchip_get_drvdata(chip);
+}
+
+static int twl_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
+ u64 duty_ns, u64 period_ns)
+{
+ int duty_cycle = DIV64_U64_ROUND_UP(duty_ns * TWL_PWM_MAX, period_ns) + 1;
+ u8 pwm_config[2] = { 1, 0 };
+ int base, ret;
+
+ /*
+ * To configure the duty period:
+ * On-cycle is set to 1 (the minimum allowed value)
+ * The off time of 0 is not configurable, so the mapping is:
+ * 0 -> off cycle = 2,
+ * 1 -> off cycle = 2,
+ * 2 -> off cycle = 3,
+ * 126 - > off cycle 127,
+ * 127 - > off cycle 1
+ * When on cycle == off cycle the PWM will be always on
+ */
+ if (duty_cycle == 1)
+ duty_cycle = 2;
+ else if (duty_cycle > TWL_PWM_MAX)
+ duty_cycle = 1;
+
+ base = pwm->hwpwm * 3;
+
+ pwm_config[1] = duty_cycle;
+
+ ret = twl_i2c_write(TWL_MODULE_PWM, pwm_config, base, 2);
+ if (ret < 0)
+ dev_err(pwmchip_parent(chip), "%s: Failed to configure PWM\n", pwm->label);
+
+ return ret;
+}
+
+static int twl4030_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct twl_pwm_chip *twl = to_twl(chip);
+ int ret;
+ u8 val;
+
+ mutex_lock(&twl->mutex);
+ ret = twl_i2c_read_u8(TWL4030_MODULE_INTBR, &val, TWL4030_GPBR1_REG);
+ if (ret < 0) {
+ dev_err(pwmchip_parent(chip), "%s: Failed to read GPBR1\n", pwm->label);
+ goto out;
+ }
+
+ val |= TWL4030_PWM_TOGGLE(pwm->hwpwm, TWL4030_PWMXCLK_ENABLE);
+
+ ret = twl_i2c_write_u8(TWL4030_MODULE_INTBR, val, TWL4030_GPBR1_REG);
+ if (ret < 0)
+ dev_err(pwmchip_parent(chip), "%s: Failed to enable PWM\n", pwm->label);
+
+ val |= TWL4030_PWM_TOGGLE(pwm->hwpwm, TWL4030_PWMX_ENABLE);
+
+ ret = twl_i2c_write_u8(TWL4030_MODULE_INTBR, val, TWL4030_GPBR1_REG);
+ if (ret < 0)
+ dev_err(pwmchip_parent(chip), "%s: Failed to enable PWM\n", pwm->label);
+
+out:
+ mutex_unlock(&twl->mutex);
+ return ret;
+}
+
+static void twl4030_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct twl_pwm_chip *twl = to_twl(chip);
+ int ret;
+ u8 val;
+
+ mutex_lock(&twl->mutex);
+ ret = twl_i2c_read_u8(TWL4030_MODULE_INTBR, &val, TWL4030_GPBR1_REG);
+ if (ret < 0) {
+ dev_err(pwmchip_parent(chip), "%s: Failed to read GPBR1\n", pwm->label);
+ goto out;
+ }
+
+ val &= ~TWL4030_PWM_TOGGLE(pwm->hwpwm, TWL4030_PWMX_ENABLE);
+
+ ret = twl_i2c_write_u8(TWL4030_MODULE_INTBR, val, TWL4030_GPBR1_REG);
+ if (ret < 0)
+ dev_err(pwmchip_parent(chip), "%s: Failed to disable PWM\n", pwm->label);
+
+ val &= ~TWL4030_PWM_TOGGLE(pwm->hwpwm, TWL4030_PWMXCLK_ENABLE);
+
+ ret = twl_i2c_write_u8(TWL4030_MODULE_INTBR, val, TWL4030_GPBR1_REG);
+ if (ret < 0)
+ dev_err(pwmchip_parent(chip), "%s: Failed to disable PWM\n", pwm->label);
+
+out:
+ mutex_unlock(&twl->mutex);
+}
+
+static int twl4030_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct twl_pwm_chip *twl = to_twl(chip);
+ int ret;
+ u8 val, mask, bits;
+
+ if (pwm->hwpwm == 1) {
+ mask = TWL4030_GPIO7_VIBRASYNC_PWM1_MASK;
+ bits = TWL4030_GPIO7_VIBRASYNC_PWM1_PWM1;
+ } else {
+ mask = TWL4030_GPIO6_PWM0_MUTE_MASK;
+ bits = TWL4030_GPIO6_PWM0_MUTE_PWM0;
+ }
+
+ mutex_lock(&twl->mutex);
+ ret = twl_i2c_read_u8(TWL4030_MODULE_INTBR, &val, TWL4030_PMBR1_REG);
+ if (ret < 0) {
+ dev_err(pwmchip_parent(chip), "%s: Failed to read PMBR1\n", pwm->label);
+ goto out;
+ }
+
+ /* Save the current MUX configuration for the PWM */
+ twl->twl4030_pwm_mux &= ~mask;
+ twl->twl4030_pwm_mux |= (val & mask);
+
+ /* Select PWM functionality */
+ val &= ~mask;
+ val |= bits;
+
+ ret = twl_i2c_write_u8(TWL4030_MODULE_INTBR, val, TWL4030_PMBR1_REG);
+ if (ret < 0)
+ dev_err(pwmchip_parent(chip), "%s: Failed to request PWM\n", pwm->label);
+
+out:
+ mutex_unlock(&twl->mutex);
+ return ret;
+}
+
+static void twl4030_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct twl_pwm_chip *twl = to_twl(chip);
+ int ret;
+ u8 val, mask;
+
+ if (pwm->hwpwm == 1)
+ mask = TWL4030_GPIO7_VIBRASYNC_PWM1_MASK;
+ else
+ mask = TWL4030_GPIO6_PWM0_MUTE_MASK;
+
+ mutex_lock(&twl->mutex);
+ ret = twl_i2c_read_u8(TWL4030_MODULE_INTBR, &val, TWL4030_PMBR1_REG);
+ if (ret < 0) {
+ dev_err(pwmchip_parent(chip), "%s: Failed to read PMBR1\n", pwm->label);
+ goto out;
+ }
+
+ /* Restore the MUX configuration for the PWM */
+ val &= ~mask;
+ val |= (twl->twl4030_pwm_mux & mask);
+
+ ret = twl_i2c_write_u8(TWL4030_MODULE_INTBR, val, TWL4030_PMBR1_REG);
+ if (ret < 0)
+ dev_err(pwmchip_parent(chip), "%s: Failed to free PWM\n", pwm->label);
+
+out:
+ mutex_unlock(&twl->mutex);
+}
+
+static int twl6030_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct twl_pwm_chip *twl = to_twl(chip);
+ int ret;
+ u8 val;
+
+ mutex_lock(&twl->mutex);
+ val = twl->twl6030_toggle3;
+ val |= TWL6030_PWM_TOGGLE(pwm->hwpwm, TWL6030_PWMXS | TWL6030_PWMXEN);
+ val &= ~TWL6030_PWM_TOGGLE(pwm->hwpwm, TWL6030_PWMXR);
+
+ ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, val, TWL6030_TOGGLE3_REG);
+ if (ret < 0) {
+ dev_err(pwmchip_parent(chip), "%s: Failed to enable PWM\n", pwm->label);
+ goto out;
+ }
+
+ twl->twl6030_toggle3 = val;
+out:
+ mutex_unlock(&twl->mutex);
+ return ret;
+}
+
+static void twl6030_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct twl_pwm_chip *twl = to_twl(chip);
+ int ret;
+ u8 val;
+
+ mutex_lock(&twl->mutex);
+ val = twl->twl6030_toggle3;
+ val |= TWL6030_PWM_TOGGLE(pwm->hwpwm, TWL6030_PWMXR);
+ val &= ~TWL6030_PWM_TOGGLE(pwm->hwpwm, TWL6030_PWMXS | TWL6030_PWMXEN);
+
+ ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, val, TWL6030_TOGGLE3_REG);
+ if (ret < 0) {
+ dev_err(pwmchip_parent(chip), "%s: Failed to disable PWM\n", pwm->label);
+ goto out;
+ }
+
+ val |= TWL6030_PWM_TOGGLE(pwm->hwpwm, TWL6030_PWMXEN);
+
+ ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, val, TWL6030_TOGGLE3_REG);
+ if (ret < 0) {
+ dev_err(pwmchip_parent(chip), "%s: Failed to disable PWM\n", pwm->label);
+ goto out;
+ }
+
+ val &= ~TWL6030_PWM_TOGGLE(pwm->hwpwm, TWL6030_PWMXEN);
+
+ ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, val, TWL6030_TOGGLE3_REG);
+ if (ret < 0) {
+ dev_err(pwmchip_parent(chip), "%s: Failed to disable PWM\n", pwm->label);
+ goto out;
+ }
+
+ twl->twl6030_toggle3 = val;
+out:
+ mutex_unlock(&twl->mutex);
+}
+
+static int twl4030_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
+ const struct pwm_state *state)
+{
+ int err;
+
+ if (state->polarity != PWM_POLARITY_NORMAL)
+ return -EINVAL;
+
+ if (!state->enabled) {
+ if (pwm->state.enabled)
+ twl4030_pwm_disable(chip, pwm);
+
+ return 0;
+ }
+
+ err = twl_pwm_config(chip, pwm, state->duty_cycle, state->period);
+ if (err)
+ return err;
+
+ if (!pwm->state.enabled)
+ err = twl4030_pwm_enable(chip, pwm);
+
+ return err;
+}
+
+static int twl6030_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
+ const struct pwm_state *state)
+{
+ int err;
+
+ if (state->polarity != PWM_POLARITY_NORMAL)
+ return -EINVAL;
+
+ if (!state->enabled) {
+ if (pwm->state.enabled)
+ twl6030_pwm_disable(chip, pwm);
+
+ return 0;
+ }
+
+ err = twl_pwm_config(chip, pwm, state->duty_cycle, state->period);
+ if (err)
+ return err;
+
+ if (!pwm->state.enabled)
+ err = twl6030_pwm_enable(chip, pwm);
+
+ return err;
+}
+
+static const struct pwm_ops twl4030_pwm_ops = {
+ .apply = twl4030_pwm_apply,
+ .request = twl4030_pwm_request,
+ .free = twl4030_pwm_free,
+};
+
+static const struct pwm_ops twl6030_pwm_ops = {
+ .apply = twl6030_pwm_apply,
+};
+
+static int twl_pwm_probe(struct platform_device *pdev)
+{
+ struct pwm_chip *chip;
+ struct twl_pwm_chip *twl;
+
+ chip = devm_pwmchip_alloc(&pdev->dev, 2, sizeof(*twl));
+ if (IS_ERR(chip))
+ return PTR_ERR(chip);
+ twl = to_twl(chip);
+
+ if (twl_class_is_4030())
+ chip->ops = &twl4030_pwm_ops;
+ else
+ chip->ops = &twl6030_pwm_ops;
+
+ mutex_init(&twl->mutex);
+
+ return devm_pwmchip_add(&pdev->dev, chip);
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id twl_pwm_of_match[] = {
+ { .compatible = "ti,twl4030-pwm" },
+ { .compatible = "ti,twl6030-pwm" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, twl_pwm_of_match);
+#endif
+
+static struct platform_driver twl_pwm_driver = {
+ .driver = {
+ .name = "twl-pwm",
+ .of_match_table = of_match_ptr(twl_pwm_of_match),
+ },
+ .probe = twl_pwm_probe,
+};
+module_platform_driver(twl_pwm_driver);
+
+MODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@ti.com>");
+MODULE_DESCRIPTION("PWM driver for TWL4030 and TWL6030");
+MODULE_ALIAS("platform:twl-pwm");
+MODULE_LICENSE("GPL");
diff --git a/drivers/pwm/pwm-visconti.c b/drivers/pwm/pwm-visconti.c
new file mode 100644
index 000000000..830aef483
--- /dev/null
+++ b/drivers/pwm/pwm-visconti.c
@@ -0,0 +1,176 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Toshiba Visconti pulse-width-modulation controller driver
+ *
+ * Copyright (c) 2020 - 2021 TOSHIBA CORPORATION
+ * Copyright (c) 2020 - 2021 Toshiba Electronic Devices & Storage Corporation
+ *
+ * Authors: Nobuhiro Iwamatsu <nobuhiro1.iwamatsu@toshiba.co.jp>
+ *
+ * Limitations:
+ * - The fixed input clock is running at 1 MHz and is divided by either 1,
+ * 2, 4 or 8.
+ * - When the settings of the PWM are modified, the new values are shadowed
+ * in hardware until the PIPGM_PCSR register is written and the currently
+ * running period is completed. This way the hardware switches atomically
+ * from the old setting to the new.
+ * - Disabling the hardware completes the currently running period and keeps
+ * the output at low level at all times.
+ */
+
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+
+#define PIPGM_PCSR(ch) (0x400 + 4 * (ch))
+#define PIPGM_PDUT(ch) (0x420 + 4 * (ch))
+#define PIPGM_PWMC(ch) (0x440 + 4 * (ch))
+
+#define PIPGM_PWMC_PWMACT BIT(5)
+#define PIPGM_PWMC_CLK_MASK GENMASK(1, 0)
+#define PIPGM_PWMC_POLARITY_MASK GENMASK(5, 5)
+
+struct visconti_pwm_chip {
+ void __iomem *base;
+};
+
+static inline struct visconti_pwm_chip *visconti_pwm_from_chip(struct pwm_chip *chip)
+{
+ return pwmchip_get_drvdata(chip);
+}
+
+static int visconti_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
+ const struct pwm_state *state)
+{
+ struct visconti_pwm_chip *priv = visconti_pwm_from_chip(chip);
+ u32 period, duty_cycle, pwmc0;
+
+ if (!state->enabled) {
+ writel(0, priv->base + PIPGM_PCSR(pwm->hwpwm));
+ return 0;
+ }
+
+ /*
+ * The biggest period the hardware can provide is
+ * (0xffff << 3) * 1000 ns
+ * This value fits easily in an u32, so simplify the maths by
+ * capping the values to 32 bit integers.
+ */
+ if (state->period > (0xffff << 3) * 1000)
+ period = (0xffff << 3) * 1000;
+ else
+ period = state->period;
+
+ if (state->duty_cycle > period)
+ duty_cycle = period;
+ else
+ duty_cycle = state->duty_cycle;
+
+ /*
+ * The input clock runs fixed at 1 MHz, so we have only
+ * microsecond resolution and so can divide by
+ * NSEC_PER_SEC / CLKFREQ = 1000 without losing precision.
+ */
+ period /= 1000;
+ duty_cycle /= 1000;
+
+ if (!period)
+ return -ERANGE;
+
+ /*
+ * PWMC controls a divider that divides the input clk by a power of two
+ * between 1 and 8. As a smaller divider yields higher precision, pick
+ * the smallest possible one. As period is at most 0xffff << 3, pwmc0 is
+ * in the intended range [0..3].
+ */
+ pwmc0 = fls(period >> 16);
+ if (WARN_ON(pwmc0 > 3))
+ return -EINVAL;
+
+ period >>= pwmc0;
+ duty_cycle >>= pwmc0;
+
+ if (state->polarity == PWM_POLARITY_INVERSED)
+ pwmc0 |= PIPGM_PWMC_PWMACT;
+ writel(pwmc0, priv->base + PIPGM_PWMC(pwm->hwpwm));
+ writel(duty_cycle, priv->base + PIPGM_PDUT(pwm->hwpwm));
+ writel(period, priv->base + PIPGM_PCSR(pwm->hwpwm));
+
+ return 0;
+}
+
+static int visconti_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm,
+ struct pwm_state *state)
+{
+ struct visconti_pwm_chip *priv = visconti_pwm_from_chip(chip);
+ u32 period, duty, pwmc0, pwmc0_clk;
+
+ period = readl(priv->base + PIPGM_PCSR(pwm->hwpwm));
+ duty = readl(priv->base + PIPGM_PDUT(pwm->hwpwm));
+ pwmc0 = readl(priv->base + PIPGM_PWMC(pwm->hwpwm));
+ pwmc0_clk = pwmc0 & PIPGM_PWMC_CLK_MASK;
+
+ state->period = (period << pwmc0_clk) * NSEC_PER_USEC;
+ state->duty_cycle = (duty << pwmc0_clk) * NSEC_PER_USEC;
+ if (pwmc0 & PIPGM_PWMC_POLARITY_MASK)
+ state->polarity = PWM_POLARITY_INVERSED;
+ else
+ state->polarity = PWM_POLARITY_NORMAL;
+
+ state->enabled = true;
+
+ return 0;
+}
+
+static const struct pwm_ops visconti_pwm_ops = {
+ .apply = visconti_pwm_apply,
+ .get_state = visconti_pwm_get_state,
+};
+
+static int visconti_pwm_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct pwm_chip *chip;
+ struct visconti_pwm_chip *priv;
+ int ret;
+
+ chip = devm_pwmchip_alloc(dev, 4, sizeof(*priv));
+ if (IS_ERR(chip))
+ return PTR_ERR(chip);
+ priv = visconti_pwm_from_chip(chip);
+
+ priv->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(priv->base))
+ return PTR_ERR(priv->base);
+
+ chip->ops = &visconti_pwm_ops;
+
+ ret = devm_pwmchip_add(&pdev->dev, chip);
+ if (ret < 0)
+ return dev_err_probe(&pdev->dev, ret, "Cannot register visconti PWM\n");
+
+ return 0;
+}
+
+static const struct of_device_id visconti_pwm_of_match[] = {
+ { .compatible = "toshiba,visconti-pwm" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, visconti_pwm_of_match);
+
+static struct platform_driver visconti_pwm_driver = {
+ .driver = {
+ .name = "pwm-visconti",
+ .of_match_table = visconti_pwm_of_match,
+ },
+ .probe = visconti_pwm_probe,
+};
+module_platform_driver(visconti_pwm_driver);
+
+MODULE_DESCRIPTION("Toshiba Visconti Pulse Width Modulator driver");
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Nobuhiro Iwamatsu <nobuhiro1.iwamatsu@toshiba.co.jp>");
+MODULE_ALIAS("platform:pwm-visconti");
diff --git a/drivers/pwm/pwm-vt8500.c b/drivers/pwm/pwm-vt8500.c
new file mode 100644
index 000000000..8013e31b1
--- /dev/null
+++ b/drivers/pwm/pwm-vt8500.c
@@ -0,0 +1,277 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * drivers/pwm/pwm-vt8500.c
+ *
+ * Copyright (C) 2012 Tony Prisk <linux@prisktech.co.nz>
+ * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/pwm.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+
+#include <asm/div64.h>
+
+/*
+ * SoC architecture allocates register space for 4 PWMs but only
+ * 2 are currently implemented.
+ */
+#define VT8500_NR_PWMS 2
+
+#define REG_CTRL(pwm) (((pwm) << 4) + 0x00)
+#define REG_SCALAR(pwm) (((pwm) << 4) + 0x04)
+#define REG_PERIOD(pwm) (((pwm) << 4) + 0x08)
+#define REG_DUTY(pwm) (((pwm) << 4) + 0x0C)
+#define REG_STATUS 0x40
+
+#define CTRL_ENABLE BIT(0)
+#define CTRL_INVERT BIT(1)
+#define CTRL_AUTOLOAD BIT(2)
+#define CTRL_STOP_IMM BIT(3)
+#define CTRL_LOAD_PRESCALE BIT(4)
+#define CTRL_LOAD_PERIOD BIT(5)
+
+#define STATUS_CTRL_UPDATE BIT(0)
+#define STATUS_SCALAR_UPDATE BIT(1)
+#define STATUS_PERIOD_UPDATE BIT(2)
+#define STATUS_DUTY_UPDATE BIT(3)
+#define STATUS_ALL_UPDATE 0x0F
+
+struct vt8500_chip {
+ void __iomem *base;
+ struct clk *clk;
+};
+
+static inline struct vt8500_chip *to_vt8500_chip(struct pwm_chip *chip)
+{
+ return pwmchip_get_drvdata(chip);
+}
+
+#define msecs_to_loops(t) (loops_per_jiffy / 1000 * HZ * t)
+static inline void vt8500_pwm_busy_wait(struct pwm_chip *chip, int nr, u8 bitmask)
+{
+ struct vt8500_chip *vt8500 = to_vt8500_chip(chip);
+ int loops = msecs_to_loops(10);
+ u32 mask = bitmask << (nr << 8);
+
+ while ((readl(vt8500->base + REG_STATUS) & mask) && --loops)
+ cpu_relax();
+
+ if (unlikely(!loops))
+ dev_warn(pwmchip_parent(chip), "Waiting for status bits 0x%x to clear timed out\n",
+ mask);
+}
+
+static int vt8500_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
+ u64 duty_ns, u64 period_ns)
+{
+ struct vt8500_chip *vt8500 = to_vt8500_chip(chip);
+ unsigned long long c;
+ unsigned long period_cycles, prescale, pv, dc;
+ int err;
+ u32 val;
+
+ err = clk_enable(vt8500->clk);
+ if (err < 0) {
+ dev_err(pwmchip_parent(chip), "failed to enable clock\n");
+ return err;
+ }
+
+ c = clk_get_rate(vt8500->clk);
+ c = c * period_ns;
+ do_div(c, 1000000000);
+ period_cycles = c;
+
+ if (period_cycles < 1)
+ period_cycles = 1;
+ prescale = (period_cycles - 1) / 4096;
+ pv = period_cycles / (prescale + 1) - 1;
+ if (pv > 4095)
+ pv = 4095;
+
+ if (prescale > 1023) {
+ clk_disable(vt8500->clk);
+ return -EINVAL;
+ }
+
+ c = (unsigned long long)pv * duty_ns;
+
+ dc = div64_u64(c, period_ns);
+
+ writel(prescale, vt8500->base + REG_SCALAR(pwm->hwpwm));
+ vt8500_pwm_busy_wait(chip, pwm->hwpwm, STATUS_SCALAR_UPDATE);
+
+ writel(pv, vt8500->base + REG_PERIOD(pwm->hwpwm));
+ vt8500_pwm_busy_wait(chip, pwm->hwpwm, STATUS_PERIOD_UPDATE);
+
+ writel(dc, vt8500->base + REG_DUTY(pwm->hwpwm));
+ vt8500_pwm_busy_wait(chip, pwm->hwpwm, STATUS_DUTY_UPDATE);
+
+ val = readl(vt8500->base + REG_CTRL(pwm->hwpwm));
+ val |= CTRL_AUTOLOAD;
+ writel(val, vt8500->base + REG_CTRL(pwm->hwpwm));
+ vt8500_pwm_busy_wait(chip, pwm->hwpwm, STATUS_CTRL_UPDATE);
+
+ clk_disable(vt8500->clk);
+ return 0;
+}
+
+static int vt8500_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct vt8500_chip *vt8500 = to_vt8500_chip(chip);
+ int err;
+ u32 val;
+
+ err = clk_enable(vt8500->clk);
+ if (err < 0) {
+ dev_err(pwmchip_parent(chip), "failed to enable clock\n");
+ return err;
+ }
+
+ val = readl(vt8500->base + REG_CTRL(pwm->hwpwm));
+ val |= CTRL_ENABLE;
+ writel(val, vt8500->base + REG_CTRL(pwm->hwpwm));
+ vt8500_pwm_busy_wait(chip, pwm->hwpwm, STATUS_CTRL_UPDATE);
+
+ return 0;
+}
+
+static void vt8500_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct vt8500_chip *vt8500 = to_vt8500_chip(chip);
+ u32 val;
+
+ val = readl(vt8500->base + REG_CTRL(pwm->hwpwm));
+ val &= ~CTRL_ENABLE;
+ writel(val, vt8500->base + REG_CTRL(pwm->hwpwm));
+ vt8500_pwm_busy_wait(chip, pwm->hwpwm, STATUS_CTRL_UPDATE);
+
+ clk_disable(vt8500->clk);
+}
+
+static int vt8500_pwm_set_polarity(struct pwm_chip *chip,
+ struct pwm_device *pwm,
+ enum pwm_polarity polarity)
+{
+ struct vt8500_chip *vt8500 = to_vt8500_chip(chip);
+ u32 val;
+
+ val = readl(vt8500->base + REG_CTRL(pwm->hwpwm));
+
+ if (polarity == PWM_POLARITY_INVERSED)
+ val |= CTRL_INVERT;
+ else
+ val &= ~CTRL_INVERT;
+
+ writel(val, vt8500->base + REG_CTRL(pwm->hwpwm));
+ vt8500_pwm_busy_wait(chip, pwm->hwpwm, STATUS_CTRL_UPDATE);
+
+ return 0;
+}
+
+static int vt8500_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
+ const struct pwm_state *state)
+{
+ int err;
+ bool enabled = pwm->state.enabled;
+
+ if (state->polarity != pwm->state.polarity) {
+ /*
+ * Changing the polarity of a running PWM is only allowed when
+ * the PWM driver implements ->apply().
+ */
+ if (enabled) {
+ vt8500_pwm_disable(chip, pwm);
+
+ enabled = false;
+ }
+
+ err = vt8500_pwm_set_polarity(chip, pwm, state->polarity);
+ if (err)
+ return err;
+ }
+
+ if (!state->enabled) {
+ if (enabled)
+ vt8500_pwm_disable(chip, pwm);
+
+ return 0;
+ }
+
+ /*
+ * We cannot skip calling ->config even if state->period ==
+ * pwm->state.period && state->duty_cycle == pwm->state.duty_cycle
+ * because we might have exited early in the last call to
+ * pwm_apply_might_sleep because of !state->enabled and so the two values in
+ * pwm->state might not be configured in hardware.
+ */
+ err = vt8500_pwm_config(chip, pwm, state->duty_cycle, state->period);
+ if (err)
+ return err;
+
+ if (!enabled)
+ err = vt8500_pwm_enable(chip, pwm);
+
+ return err;
+}
+
+static const struct pwm_ops vt8500_pwm_ops = {
+ .apply = vt8500_pwm_apply,
+};
+
+static const struct of_device_id vt8500_pwm_dt_ids[] = {
+ { .compatible = "via,vt8500-pwm" },
+ { /* Sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, vt8500_pwm_dt_ids);
+
+static int vt8500_pwm_probe(struct platform_device *pdev)
+{
+ struct pwm_chip *chip;
+ struct vt8500_chip *vt8500;
+ struct device_node *np = pdev->dev.of_node;
+ int ret;
+
+ if (!np)
+ return dev_err_probe(&pdev->dev, -EINVAL, "invalid devicetree node\n");
+
+ chip = devm_pwmchip_alloc(&pdev->dev, VT8500_NR_PWMS, sizeof(*vt8500));
+ if (IS_ERR(chip))
+ return PTR_ERR(chip);
+ vt8500 = to_vt8500_chip(chip);
+
+ chip->ops = &vt8500_pwm_ops;
+
+ vt8500->clk = devm_clk_get_prepared(&pdev->dev, NULL);
+ if (IS_ERR(vt8500->clk))
+ return dev_err_probe(&pdev->dev, PTR_ERR(vt8500->clk), "clock source not specified\n");
+
+ vt8500->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(vt8500->base))
+ return PTR_ERR(vt8500->base);
+
+ ret = devm_pwmchip_add(&pdev->dev, chip);
+ if (ret < 0)
+ return dev_err_probe(&pdev->dev, ret, "failed to add PWM chip\n");
+
+ return 0;
+}
+
+static struct platform_driver vt8500_pwm_driver = {
+ .probe = vt8500_pwm_probe,
+ .driver = {
+ .name = "vt8500-pwm",
+ .of_match_table = vt8500_pwm_dt_ids,
+ },
+};
+module_platform_driver(vt8500_pwm_driver);
+
+MODULE_DESCRIPTION("VT8500 PWM Driver");
+MODULE_AUTHOR("Tony Prisk <linux@prisktech.co.nz>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/pwm/pwm-xilinx.c b/drivers/pwm/pwm-xilinx.c
new file mode 100644
index 000000000..fa2d10e65
--- /dev/null
+++ b/drivers/pwm/pwm-xilinx.c
@@ -0,0 +1,300 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2021 Sean Anderson <sean.anderson@seco.com>
+ *
+ * Limitations:
+ * - When changing both duty cycle and period, we may end up with one cycle
+ * with the old duty cycle and the new period. This is because the counters
+ * may only be reloaded by first stopping them, or by letting them be
+ * automatically reloaded at the end of a cycle. If this automatic reload
+ * happens after we set TLR0 but before we set TLR1 then we will have a
+ * bad cycle. This could probably be fixed by reading TCR0 just before
+ * reprogramming, but I think it would add complexity for little gain.
+ * - Cannot produce 100% duty cycle by configuring the TLRs. This might be
+ * possible by stopping the counters at an appropriate point in the cycle,
+ * but this is not (yet) implemented.
+ * - Only produces "normal" output.
+ * - Always produces low output if disabled.
+ */
+
+#include <clocksource/timer-xilinx.h>
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+#include <linux/regmap.h>
+
+/*
+ * The following functions are "common" to drivers for this device, and may be
+ * exported at a future date.
+ */
+u32 xilinx_timer_tlr_cycles(struct xilinx_timer_priv *priv, u32 tcsr,
+ u64 cycles)
+{
+ WARN_ON(cycles < 2 || cycles - 2 > priv->max);
+
+ if (tcsr & TCSR_UDT)
+ return cycles - 2;
+ return priv->max - cycles + 2;
+}
+
+unsigned int xilinx_timer_get_period(struct xilinx_timer_priv *priv,
+ u32 tlr, u32 tcsr)
+{
+ u64 cycles;
+
+ if (tcsr & TCSR_UDT)
+ cycles = tlr + 2;
+ else
+ cycles = (u64)priv->max - tlr + 2;
+
+ /* cycles has a max of 2^32 + 2, so we can't overflow */
+ return DIV64_U64_ROUND_UP(cycles * NSEC_PER_SEC,
+ clk_get_rate(priv->clk));
+}
+
+/*
+ * The idea here is to capture whether the PWM is actually running (e.g.
+ * because we or the bootloader set it up) and we need to be careful to ensure
+ * we don't cause a glitch. According to the data sheet, to enable the PWM we
+ * need to
+ *
+ * - Set both timers to generate mode (MDT=1)
+ * - Set both timers to PWM mode (PWMA=1)
+ * - Enable the generate out signals (GENT=1)
+ *
+ * In addition,
+ *
+ * - The timer must be running (ENT=1)
+ * - The timer must auto-reload TLR into TCR (ARHT=1)
+ * - We must not be in the process of loading TLR into TCR (LOAD=0)
+ * - Cascade mode must be disabled (CASC=0)
+ *
+ * If any of these differ from usual, then the PWM is either disabled, or is
+ * running in a mode that this driver does not support.
+ */
+#define TCSR_PWM_SET (TCSR_GENT | TCSR_ARHT | TCSR_ENT | TCSR_PWMA)
+#define TCSR_PWM_CLEAR (TCSR_MDT | TCSR_LOAD)
+#define TCSR_PWM_MASK (TCSR_PWM_SET | TCSR_PWM_CLEAR)
+
+static inline struct xilinx_timer_priv
+*xilinx_pwm_chip_to_priv(struct pwm_chip *chip)
+{
+ return pwmchip_get_drvdata(chip);
+}
+
+static bool xilinx_timer_pwm_enabled(u32 tcsr0, u32 tcsr1)
+{
+ return ((TCSR_PWM_MASK | TCSR_CASC) & tcsr0) == TCSR_PWM_SET &&
+ (TCSR_PWM_MASK & tcsr1) == TCSR_PWM_SET;
+}
+
+static int xilinx_pwm_apply(struct pwm_chip *chip, struct pwm_device *unused,
+ const struct pwm_state *state)
+{
+ struct xilinx_timer_priv *priv = xilinx_pwm_chip_to_priv(chip);
+ u32 tlr0, tlr1, tcsr0, tcsr1;
+ u64 period_cycles, duty_cycles;
+ unsigned long rate;
+
+ if (state->polarity != PWM_POLARITY_NORMAL)
+ return -EINVAL;
+
+ /*
+ * To be representable by TLR, cycles must be between 2 and
+ * priv->max + 2. To enforce this we can reduce the cycles, but we may
+ * not increase them. Caveat emptor: while this does result in more
+ * predictable rounding, it may also result in a completely different
+ * duty cycle (% high time) than what was requested.
+ */
+ rate = clk_get_rate(priv->clk);
+ /* Avoid overflow */
+ period_cycles = min_t(u64, state->period, U32_MAX * NSEC_PER_SEC);
+ period_cycles = mul_u64_u32_div(period_cycles, rate, NSEC_PER_SEC);
+ period_cycles = min_t(u64, period_cycles, priv->max + 2);
+ if (period_cycles < 2)
+ return -ERANGE;
+
+ /* Same thing for duty cycles */
+ duty_cycles = min_t(u64, state->duty_cycle, U32_MAX * NSEC_PER_SEC);
+ duty_cycles = mul_u64_u32_div(duty_cycles, rate, NSEC_PER_SEC);
+ duty_cycles = min_t(u64, duty_cycles, priv->max + 2);
+
+ /*
+ * If we specify 100% duty cycle, we will get 0% instead, so decrease
+ * the duty cycle count by one.
+ */
+ if (duty_cycles >= period_cycles)
+ duty_cycles = period_cycles - 1;
+
+ /* Round down to 0% duty cycle for unrepresentable duty cycles */
+ if (duty_cycles < 2)
+ duty_cycles = period_cycles;
+
+ regmap_read(priv->map, TCSR0, &tcsr0);
+ regmap_read(priv->map, TCSR1, &tcsr1);
+ tlr0 = xilinx_timer_tlr_cycles(priv, tcsr0, period_cycles);
+ tlr1 = xilinx_timer_tlr_cycles(priv, tcsr1, duty_cycles);
+ regmap_write(priv->map, TLR0, tlr0);
+ regmap_write(priv->map, TLR1, tlr1);
+
+ if (state->enabled) {
+ /*
+ * If the PWM is already running, then the counters will be
+ * reloaded at the end of the current cycle.
+ */
+ if (!xilinx_timer_pwm_enabled(tcsr0, tcsr1)) {
+ /* Load TLR into TCR */
+ regmap_write(priv->map, TCSR0, tcsr0 | TCSR_LOAD);
+ regmap_write(priv->map, TCSR1, tcsr1 | TCSR_LOAD);
+ /* Enable timers all at once with ENALL */
+ tcsr0 = (TCSR_PWM_SET & ~TCSR_ENT) | (tcsr0 & TCSR_UDT);
+ tcsr1 = TCSR_PWM_SET | TCSR_ENALL | (tcsr1 & TCSR_UDT);
+ regmap_write(priv->map, TCSR0, tcsr0);
+ regmap_write(priv->map, TCSR1, tcsr1);
+ }
+ } else {
+ regmap_write(priv->map, TCSR0, 0);
+ regmap_write(priv->map, TCSR1, 0);
+ }
+
+ return 0;
+}
+
+static int xilinx_pwm_get_state(struct pwm_chip *chip,
+ struct pwm_device *unused,
+ struct pwm_state *state)
+{
+ struct xilinx_timer_priv *priv = xilinx_pwm_chip_to_priv(chip);
+ u32 tlr0, tlr1, tcsr0, tcsr1;
+
+ regmap_read(priv->map, TLR0, &tlr0);
+ regmap_read(priv->map, TLR1, &tlr1);
+ regmap_read(priv->map, TCSR0, &tcsr0);
+ regmap_read(priv->map, TCSR1, &tcsr1);
+ state->period = xilinx_timer_get_period(priv, tlr0, tcsr0);
+ state->duty_cycle = xilinx_timer_get_period(priv, tlr1, tcsr1);
+ state->enabled = xilinx_timer_pwm_enabled(tcsr0, tcsr1);
+ state->polarity = PWM_POLARITY_NORMAL;
+
+ /*
+ * 100% duty cycle results in constant low output. This may be (very)
+ * wrong if rate > 1 GHz, so fix this if you have such hardware :)
+ */
+ if (state->period == state->duty_cycle)
+ state->duty_cycle = 0;
+
+ return 0;
+}
+
+static const struct pwm_ops xilinx_pwm_ops = {
+ .apply = xilinx_pwm_apply,
+ .get_state = xilinx_pwm_get_state,
+};
+
+static const struct regmap_config xilinx_pwm_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .val_format_endian = REGMAP_ENDIAN_LITTLE,
+ .max_register = TCR1,
+};
+
+static int xilinx_pwm_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+ struct xilinx_timer_priv *priv;
+ struct pwm_chip *chip;
+ u32 pwm_cells, one_timer, width;
+ void __iomem *regs;
+
+ /* If there are no PWM cells, this binding is for a timer */
+ ret = of_property_read_u32(np, "#pwm-cells", &pwm_cells);
+ if (ret == -EINVAL)
+ return -ENODEV;
+ if (ret)
+ return dev_err_probe(dev, ret, "could not read #pwm-cells\n");
+
+ chip = devm_pwmchip_alloc(dev, 1, sizeof(*priv));
+ if (IS_ERR(chip))
+ return PTR_ERR(chip);
+ priv = xilinx_pwm_chip_to_priv(chip);
+
+ regs = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(regs))
+ return PTR_ERR(regs);
+
+ priv->map = devm_regmap_init_mmio(dev, regs,
+ &xilinx_pwm_regmap_config);
+ if (IS_ERR(priv->map))
+ return dev_err_probe(dev, PTR_ERR(priv->map),
+ "Could not create regmap\n");
+
+ ret = of_property_read_u32(np, "xlnx,one-timer-only", &one_timer);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "Could not read xlnx,one-timer-only\n");
+
+ if (one_timer)
+ return dev_err_probe(dev, -EINVAL,
+ "Two timers required for PWM mode\n");
+
+ ret = of_property_read_u32(np, "xlnx,count-width", &width);
+ if (ret == -EINVAL)
+ width = 32;
+ else if (ret)
+ return dev_err_probe(dev, ret,
+ "Could not read xlnx,count-width\n");
+
+ if (width != 8 && width != 16 && width != 32)
+ return dev_err_probe(dev, -EINVAL,
+ "Invalid counter width %d\n", width);
+ priv->max = BIT_ULL(width) - 1;
+
+ /*
+ * The polarity of the Generate Out signals must be active high for PWM
+ * mode to work. We could determine this from the device tree, but
+ * alas, such properties are not allowed to be used.
+ */
+
+ priv->clk = devm_clk_get_enabled(dev, "s_axi_aclk");
+ if (IS_ERR(priv->clk))
+ return dev_err_probe(dev, PTR_ERR(priv->clk),
+ "Could not get clock\n");
+
+ ret = devm_clk_rate_exclusive_get(dev, priv->clk);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "Failed to lock clock rate\n");
+
+ chip->ops = &xilinx_pwm_ops;
+ ret = devm_pwmchip_add(dev, chip);
+ if (ret)
+ return dev_err_probe(dev, ret, "Could not register PWM chip\n");
+
+ return 0;
+}
+
+static const struct of_device_id xilinx_pwm_of_match[] = {
+ { .compatible = "xlnx,xps-timer-1.00.a" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, xilinx_pwm_of_match);
+
+static struct platform_driver xilinx_pwm_driver = {
+ .probe = xilinx_pwm_probe,
+ .driver = {
+ .name = "xilinx-pwm",
+ .of_match_table = of_match_ptr(xilinx_pwm_of_match),
+ },
+};
+module_platform_driver(xilinx_pwm_driver);
+
+MODULE_ALIAS("platform:xilinx-pwm");
+MODULE_DESCRIPTION("PWM driver for Xilinx LogiCORE IP AXI Timer");
+MODULE_LICENSE("GPL");
diff --git a/drivers/pwm/pwm_th1520.rs b/drivers/pwm/pwm_th1520.rs
new file mode 100644
index 000000000..728993998
--- /dev/null
+++ b/drivers/pwm/pwm_th1520.rs
@@ -0,0 +1,367 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2025 Samsung Electronics Co., Ltd.
+// Author: Michal Wilczynski <m.wilczynski@samsung.com>
+
+//! Rust T-HEAD TH1520 PWM driver
+//!
+//! Limitations:
+//! - The period and duty cycle are controlled by 32-bit hardware registers,
+//! limiting the maximum resolution.
+//! - The driver supports continuous output mode only; one-shot mode is not
+//! implemented.
+//! - The controller hardware provides up to 6 PWM channels.
+//! - Reconfiguration is glitch free - new period and duty cycle values are
+//! latched and take effect at the start of the next period.
+//! - Polarity is handled via a simple hardware inversion bit; arbitrary
+//! duty cycle offsets are not supported.
+//! - Disabling a channel is achieved by configuring its duty cycle to zero to
+//! produce a static low output. Clearing the `start` does not reliably
+//! force the static inactive level defined by the `INACTOUT` bit. Hence
+//! this method is not used in this driver.
+//!
+
+use kernel::{
+ clk::Clk,
+ device::{
+ Bound,
+ Core,
+ Device, //
+ },
+ io::{
+ mem::DevresIoMem,
+ Io, //
+ },
+ of,
+ platform,
+ prelude::*,
+ pwm,
+ time, //
+};
+
+const TH1520_MAX_PWM_NUM: u32 = 6;
+
+// Register offsets
+const fn th1520_pwm_chn_base(n: u32) -> usize {
+ (n * 0x20) as usize
+}
+
+const fn th1520_pwm_ctrl(n: u32) -> usize {
+ th1520_pwm_chn_base(n)
+}
+
+const fn th1520_pwm_per(n: u32) -> usize {
+ th1520_pwm_chn_base(n) + 0x08
+}
+
+const fn th1520_pwm_fp(n: u32) -> usize {
+ th1520_pwm_chn_base(n) + 0x0c
+}
+
+// Control register bits
+const TH1520_PWM_START: u32 = 1 << 0;
+const TH1520_PWM_CFG_UPDATE: u32 = 1 << 2;
+const TH1520_PWM_CONTINUOUS_MODE: u32 = 1 << 5;
+const TH1520_PWM_FPOUT: u32 = 1 << 8;
+
+const TH1520_PWM_REG_SIZE: usize = 0xB0;
+
+fn ns_to_cycles(ns: u64, rate_hz: u64) -> u64 {
+ const NSEC_PER_SEC_U64: u64 = time::NSEC_PER_SEC as u64;
+
+ ns.saturating_mul(rate_hz) / NSEC_PER_SEC_U64
+}
+
+fn cycles_to_ns(cycles: u32, rate_hz: u64) -> u64 {
+ const NSEC_PER_SEC_U64: u64 = time::NSEC_PER_SEC as u64;
+
+ (u64::from(cycles) * NSEC_PER_SEC_U64).div_ceil(rate_hz)
+}
+
+/// Hardware-specific waveform representation for TH1520.
+#[derive(Copy, Clone, Debug, Default)]
+struct Th1520WfHw {
+ period_cycles: u32,
+ duty_cycles: u32,
+ ctrl_val: u32,
+ enabled: bool,
+}
+
+/// The driver's private data struct. It holds all necessary devres managed resources.
+#[pin_data(PinnedDrop)]
+struct Th1520PwmDriverData {
+ #[pin]
+ iomem: DevresIoMem<TH1520_PWM_REG_SIZE>,
+ clk: Clk,
+}
+
+impl pwm::PwmOps for Th1520PwmDriverData {
+ type WfHw = Th1520WfHw;
+
+ fn round_waveform_tohw(
+ chip: &pwm::Chip<Self>,
+ _pwm: &pwm::Device,
+ wf: &pwm::Waveform,
+ ) -> Result<pwm::RoundedWaveform<Self::WfHw>> {
+ let data = chip.drvdata();
+ let mut status = 0;
+
+ if wf.period_length_ns == 0 {
+ dev_dbg!(chip.device(), "Requested period is 0, disabling PWM.\n");
+
+ return Ok(pwm::RoundedWaveform {
+ status: 0,
+ hardware_waveform: Th1520WfHw {
+ enabled: false,
+ ..Default::default()
+ },
+ });
+ }
+
+ let rate_hz = data.clk.rate().as_hz() as u64;
+
+ let mut period_cycles = ns_to_cycles(wf.period_length_ns, rate_hz).min(u64::from(u32::MAX));
+
+ if period_cycles == 0 {
+ dev_dbg!(
+ chip.device(),
+ "Requested period {} ns is too small for clock rate {} Hz, rounding up.\n",
+ wf.period_length_ns,
+ rate_hz
+ );
+
+ period_cycles = 1;
+ status = 1;
+ }
+
+ let mut duty_cycles = ns_to_cycles(wf.duty_length_ns, rate_hz).min(u64::from(u32::MAX));
+
+ let mut ctrl_val = TH1520_PWM_CONTINUOUS_MODE;
+
+ let is_inversed = wf.duty_length_ns > 0
+ && wf.duty_offset_ns > 0
+ && wf.duty_offset_ns >= wf.period_length_ns.saturating_sub(wf.duty_length_ns);
+ if is_inversed {
+ duty_cycles = period_cycles - duty_cycles;
+ } else {
+ ctrl_val |= TH1520_PWM_FPOUT;
+ }
+
+ let wfhw = Th1520WfHw {
+ // The cast is safe because the value was clamped with `.min(u64::from(u32::MAX))`.
+ period_cycles: period_cycles as u32,
+ duty_cycles: duty_cycles as u32,
+ ctrl_val,
+ enabled: true,
+ };
+
+ dev_dbg!(
+ chip.device(),
+ "Requested: {}/{} ns [+{} ns] -> HW: {}/{} cycles, ctrl 0x{:x}, rate {} Hz\n",
+ wf.duty_length_ns,
+ wf.period_length_ns,
+ wf.duty_offset_ns,
+ wfhw.duty_cycles,
+ wfhw.period_cycles,
+ wfhw.ctrl_val,
+ rate_hz
+ );
+
+ Ok(pwm::RoundedWaveform {
+ status,
+ hardware_waveform: wfhw,
+ })
+ }
+
+ fn round_waveform_fromhw(
+ chip: &pwm::Chip<Self>,
+ _pwm: &pwm::Device,
+ wfhw: &Self::WfHw,
+ wf: &mut pwm::Waveform,
+ ) -> Result {
+ let data = chip.drvdata();
+ let rate_hz = data.clk.rate().as_hz() as u64;
+
+ if wfhw.period_cycles == 0 {
+ dev_dbg!(
+ chip.device(),
+ "HW state has zero period, reporting as disabled.\n"
+ );
+ *wf = pwm::Waveform::default();
+ return Ok(());
+ }
+
+ wf.period_length_ns = cycles_to_ns(wfhw.period_cycles, rate_hz);
+
+ let duty_cycles = wfhw.duty_cycles;
+
+ if (wfhw.ctrl_val & TH1520_PWM_FPOUT) != 0 {
+ wf.duty_length_ns = cycles_to_ns(duty_cycles, rate_hz);
+ wf.duty_offset_ns = 0;
+ } else {
+ let period_cycles = wfhw.period_cycles;
+ let original_duty_cycles = period_cycles.saturating_sub(duty_cycles);
+
+ // For an inverted signal, `duty_length_ns` is the high time (period - low_time).
+ wf.duty_length_ns = cycles_to_ns(original_duty_cycles, rate_hz);
+ // The offset is the initial low time, which is what the hardware register provides.
+ wf.duty_offset_ns = cycles_to_ns(duty_cycles, rate_hz);
+ }
+
+ Ok(())
+ }
+
+ fn read_waveform(
+ chip: &pwm::Chip<Self>,
+ pwm: &pwm::Device,
+ parent_dev: &Device<Bound>,
+ ) -> Result<Self::WfHw> {
+ let data = chip.drvdata();
+ let hwpwm = pwm.hwpwm();
+ let iomap = data.iomem.access(parent_dev)?;
+
+ let ctrl = iomap.try_read32(th1520_pwm_ctrl(hwpwm))?;
+ let period_cycles = iomap.try_read32(th1520_pwm_per(hwpwm))?;
+ let duty_cycles = iomap.try_read32(th1520_pwm_fp(hwpwm))?;
+
+ let wfhw = Th1520WfHw {
+ period_cycles,
+ duty_cycles,
+ ctrl_val: ctrl,
+ enabled: duty_cycles != 0,
+ };
+
+ dev_dbg!(
+ chip.device(),
+ "PWM-{}: read_waveform: Read hw state - period: {}, duty: {}, ctrl: 0x{:x}, enabled: {}",
+ hwpwm,
+ wfhw.period_cycles,
+ wfhw.duty_cycles,
+ wfhw.ctrl_val,
+ wfhw.enabled
+ );
+
+ Ok(wfhw)
+ }
+
+ fn write_waveform(
+ chip: &pwm::Chip<Self>,
+ pwm: &pwm::Device,
+ wfhw: &Self::WfHw,
+ parent_dev: &Device<Bound>,
+ ) -> Result {
+ let data = chip.drvdata();
+ let hwpwm = pwm.hwpwm();
+ let iomap = data.iomem.access(parent_dev)?;
+ let duty_cycles = iomap.try_read32(th1520_pwm_fp(hwpwm))?;
+ let was_enabled = duty_cycles != 0;
+
+ if !wfhw.enabled {
+ dev_dbg!(chip.device(), "PWM-{}: Disabling channel.\n", hwpwm);
+ if was_enabled {
+ iomap.try_write32(wfhw.ctrl_val, th1520_pwm_ctrl(hwpwm))?;
+ iomap.try_write32(0, th1520_pwm_fp(hwpwm))?;
+ iomap.try_write32(
+ wfhw.ctrl_val | TH1520_PWM_CFG_UPDATE,
+ th1520_pwm_ctrl(hwpwm),
+ )?;
+ }
+ return Ok(());
+ }
+
+ iomap.try_write32(wfhw.ctrl_val, th1520_pwm_ctrl(hwpwm))?;
+ iomap.try_write32(wfhw.period_cycles, th1520_pwm_per(hwpwm))?;
+ iomap.try_write32(wfhw.duty_cycles, th1520_pwm_fp(hwpwm))?;
+ iomap.try_write32(
+ wfhw.ctrl_val | TH1520_PWM_CFG_UPDATE,
+ th1520_pwm_ctrl(hwpwm),
+ )?;
+
+ // The `TH1520_PWM_START` bit must be written in a separate, final transaction, and
+ // only when enabling the channel from a disabled state.
+ if !was_enabled {
+ iomap.try_write32(wfhw.ctrl_val | TH1520_PWM_START, th1520_pwm_ctrl(hwpwm))?;
+ }
+
+ dev_dbg!(
+ chip.device(),
+ "PWM-{}: Wrote {}/{} cycles",
+ hwpwm,
+ wfhw.duty_cycles,
+ wfhw.period_cycles,
+ );
+
+ Ok(())
+ }
+}
+
+#[pinned_drop]
+impl PinnedDrop for Th1520PwmDriverData {
+ fn drop(self: Pin<&mut Self>) {
+ self.clk.disable_unprepare();
+ }
+}
+
+struct Th1520PwmPlatformDriver;
+
+kernel::of_device_table!(
+ OF_TABLE,
+ <Th1520PwmPlatformDriver as platform::Driver>::IdInfo,
+ [(of::DeviceId::new(c"thead,th1520-pwm"), ())]
+);
+
+impl platform::Driver for Th1520PwmPlatformDriver {
+ type IdInfo = ();
+ type Data<'bound> = Self;
+ const OF_ID_TABLE: Option<of::IdTable<Self::IdInfo>> = Some(&OF_TABLE);
+
+ fn probe<'bound>(
+ pdev: &'bound platform::Device<Core<'_>>,
+ _id_info: Option<&'bound Self::IdInfo>,
+ ) -> impl PinInit<Self, Error> + 'bound {
+ let dev = pdev.as_ref();
+ let request = pdev.io_request_by_index(0).ok_or(ENODEV)?;
+
+ let clk = Clk::get(dev, None)?;
+
+ clk.prepare_enable()?;
+
+ // TODO: Get exclusive ownership of the clock to prevent rate changes.
+ // The Rust equivalent of `clk_rate_exclusive_get()` is not yet available.
+ // This should be updated once it is implemented.
+ let rate_hz = clk.rate().as_hz();
+ if rate_hz == 0 {
+ dev_err!(dev, "Clock rate is zero\n");
+ return Err(EINVAL);
+ }
+
+ if rate_hz > time::NSEC_PER_SEC as usize {
+ dev_err!(
+ dev,
+ "Clock rate {} Hz is too high, not supported.\n",
+ rate_hz
+ );
+ return Err(EINVAL);
+ }
+
+ let chip = pwm::Chip::new(
+ dev,
+ TH1520_MAX_PWM_NUM,
+ try_pin_init!(Th1520PwmDriverData {
+ iomem <- request.iomap_sized::<TH1520_PWM_REG_SIZE>()?.into_devres(),
+ clk <- clk,
+ }),
+ )?;
+
+ chip.register()?;
+
+ Ok(Th1520PwmPlatformDriver)
+ }
+}
+
+kernel::module_pwm_platform_driver! {
+ type: Th1520PwmPlatformDriver,
+ name: "pwm-th1520",
+ authors: ["Michal Wilczynski <m.wilczynski@samsung.com>"],
+ description: "T-HEAD TH1520 PWM driver",
+ license: "GPL v2",
+}