diff options
Diffstat (limited to 'drivers/pwm')
85 files changed, 15395 insertions, 6768 deletions
diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index cb8d739067d2..bf2d101f67a1 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig @@ -29,10 +29,6 @@ menuconfig PWM if PWM -config PWM_SYSFS - bool - default y if SYSFS - config PWM_DEBUG bool "PWM lowlevel drivers additional checks and debug messages" depends on DEBUG_KERNEL @@ -42,6 +38,15 @@ config PWM_DEBUG 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 @@ -51,10 +56,48 @@ config PWM_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 OF depends on ARCH_AT91 || COMPILE_TEST + depends on HAS_IOMEM && OF help Generic PWM framework driver for Atmel SoC. @@ -75,7 +118,8 @@ config PWM_ATMEL_HLCDC_PWM config PWM_ATMEL_TCB tristate "Atmel TC Block PWM support" - depends on ATMEL_TCLIB && OF + depends on OF + select REGMAP_MMIO help Generic PWM framework driver for Atmel Timer Counter Block. @@ -85,10 +129,33 @@ config PWM_ATMEL_TCB To compile this driver as a module, choose M here: the module will be called pwm-atmel-tcb. +config PWM_AXI_PWMGEN + tristate "Analog Devices AXI PWM generator" + depends on MICROBLAZE || NIOS2 || ARCH_ZYNQ || ARCH_ZYNQMP || ARCH_INTEL_SOCFPGA || COMPILE_TEST + select REGMAP_MMIO + help + This enables support for the Analog Devices AXI PWM generator. + + This is a configurable PWM generator with variable pulse width and + period. + + To compile this driver as a module, choose M here: the module will be + called pwm-axi-pwmgen. + +config PWM_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 + depends on COMMON_CLK && HAS_IOMEM default ARCH_BCM_IPROC help Generic PWM framework driver for Broadcom iProc PWM block. This @@ -108,18 +175,10 @@ config PWM_BCM_KONA To compile this driver as a module, choose M here: the module will be called pwm-bcm-kona. -config PWM_BCM2835 - tristate "BCM2835 PWM support" - depends on ARCH_BCM2835 || ARCH_BRCMSTB || COMPILE_TEST - help - PWM framework driver for BCM2835 controller (Raspberry Pi) - - To compile this driver as a module, choose M here: the module - will be called pwm-bcm2835. - config PWM_BERLIN tristate "Marvell Berlin PWM support" depends on ARCH_BERLIN || COMPILE_TEST + depends on HAS_IOMEM help PWM framework driver for Marvell Berlin SoCs. @@ -129,6 +188,7 @@ config 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). @@ -136,6 +196,16 @@ config PWM_BRCMSTB 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 @@ -147,8 +217,8 @@ config PWM_CLPS711X will be called pwm-clps711x. config PWM_CRC - bool "Intel Crystalcove (CRC) PWM support" - depends on X86 && INTEL_SOC_PMIC + tristate "Intel Crystalcove (CRC) PWM support" + depends on INTEL_SOC_PMIC help Generic PWM framework driver for Crystalcove (CRC) PMIC based PWM control. @@ -160,9 +230,29 @@ config PWM_CROS_EC 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. @@ -181,9 +271,21 @@ config PWM_FSL_FTM To compile this driver as a module, choose M here: the module will be called pwm-fsl-ftm. +config PWM_GPIO + tristate "GPIO PWM support" + depends on GPIOLIB + depends on HIGH_RES_TIMERS + help + Generic PWM framework driver for software PWM toggling a GPIO pin + from kernel high-resolution timers. + + To compile this driver as a module, choose M here: the module + will be called pwm-gpio. + config PWM_HIBVT tristate "HiSilicon BVT PWM support" depends on ARCH_HISI || COMPILE_TEST + depends on HAS_IOMEM help Generic PWM framework driver for HiSilicon BVT SoCs. @@ -206,6 +308,7 @@ config 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 @@ -215,6 +318,7 @@ config 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. @@ -232,6 +336,17 @@ config PWM_IMX_TPM 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_IQS620A tristate "Azoteq IQS620A PWM support" depends on MFD_IQS62X || COMPILE_TEST @@ -244,8 +359,8 @@ config PWM_IQS620A config PWM_JZ4740 tristate "Ingenic JZ47xx PWM support" - depends on MIPS - depends on COMMON_CLK + depends on MACH_INGENIC || COMPILE_TEST + depends on COMMON_CLK && OF select MFD_SYSCON help Generic PWM framework driver for Ingenic JZ47xx based @@ -254,6 +369,28 @@ config PWM_JZ4740 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 @@ -267,6 +404,7 @@ config 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. @@ -279,6 +417,7 @@ config 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. @@ -287,11 +426,13 @@ config PWM_LPC32XX 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 && PCI + depends on X86 || COMPILE_TEST + depends on HAS_IOMEM && PCI select PWM_LPSS help The PCI driver for Intel Low Power Subsystem PWM controller. @@ -301,7 +442,8 @@ config PWM_LPSS_PCI config PWM_LPSS_PLATFORM tristate "Intel LPSS PWM platform driver" - depends on X86 && ACPI + depends on (X86 && ACPI) || COMPILE_TEST + depends on HAS_IOMEM select PWM_LPSS help The platform driver for Intel Low Power Subsystem PWM controller. @@ -309,16 +451,59 @@ config PWM_LPSS_PLATFORM 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 + 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 @@ -330,19 +515,10 @@ config PWM_MTK_DISP To compile this driver as a module, choose M here: the module will be called pwm-mtk-disp. -config PWM_MEDIATEK - tristate "MediaTek PWM support" - depends on ARCH_MEDIATEK || RALINK || COMPILE_TEST - 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_MXS tristate "Freescale MXS PWM support" - depends on OF depends on ARCH_MXS || COMPILE_TEST + depends on HAS_IOMEM && OF select STMP_DEVICE help Generic PWM framework driver for Freescale MXS. @@ -350,6 +526,14 @@ config PWM_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 @@ -370,25 +554,26 @@ config PWM_PCA9685 To compile this driver as a module, choose M here: the module will be called pwm-pca9685. -config PWM_PUV3 - tristate "PKUnity NetBook-0916 PWM support" - depends on ARCH_PUV3 - help - Generic PWM framework driver for PKUnity NetBook-0916. - - To compile this driver as a module, choose M here: the module - will be called pwm-puv3. - config PWM_PXA tristate "PXA PWM support" - depends on ARCH_PXA || COMPILE_TEST + depends on ARCH_PXA || ARCH_MMP || ARCH_SPACEMIT || COMPILE_TEST + depends on HAS_IOMEM 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_RCAR +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 @@ -399,6 +584,28 @@ config PWM_RCAR To compile this driver as a module, choose M here: the module will be called pwm-rcar. +config PWM_RENESAS_RZG2L_GPT + tristate "Renesas RZ/G2L General PWM Timer support" + depends on ARCH_RZG2L || COMPILE_TEST + depends on HAS_IOMEM + help + This driver exposes the General PWM Timer controller found in Renesas + RZ/G2L like chips through the PWM API. + + To compile this driver as a module, choose M here: the module + will be called pwm-rzg2l-gpt. + +config PWM_RENESAS_RZ_MTU3 + tristate "Renesas RZ/G2L MTU3a PWM Timer support" + depends on RZ_MTU3 + depends on HAS_IOMEM + help + This driver exposes the MTU3a PWM Timer controller found in Renesas + RZ/G2L like chips through the PWM API. + + To compile this driver as a module, choose M here: the module + will be called pwm-rz-mtu3. + config PWM_RENESAS_TPU tristate "Renesas TPU PWM support" depends on ARCH_RENESAS || COMPILE_TEST @@ -413,15 +620,19 @@ config 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_EXYNOS || COMPILE_TEST + depends on PLAT_SAMSUNG || ARCH_S5PV210 || ARCH_EXYNOS || COMPILE_TEST + depends on HAS_IOMEM help - Generic PWM framework driver for Samsung. + 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. @@ -429,18 +640,38 @@ config PWM_SAMSUNG config PWM_SIFIVE tristate "SiFive PWM support" depends on OF - depends on COMMON_CLK - depends on RISCV || COMPILE_TEST + 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 OF + depends on HAS_IOMEM && OF help Generic PWM framework driver for the PWM controller on ST SPEAr SoCs. @@ -462,7 +693,7 @@ config PWM_SPRD config PWM_STI tristate "STiH4xx PWM support" depends on ARCH_STI || COMPILE_TEST - depends on OF + depends on HAS_IOMEM && OF help Generic PWM framework driver for STiH4xx SoCs. @@ -489,7 +720,7 @@ config PWM_STM32_LP will be called pwm-stm32-lp. config PWM_STMPE - bool "STMPE expander PWM export" + tristate "STMPE expander PWM export" depends on MFD_STMPE help This enables support for the PWMs found in the STMPE I/O @@ -505,9 +736,21 @@ config PWM_SUN4I 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. @@ -515,9 +758,21 @@ config PWM_TEGRA 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 RUST + 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 @@ -527,6 +782,7 @@ config 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 @@ -551,22 +807,49 @@ config PWM_TWL_LED 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_ZX - tristate "ZTE ZX PWM support" - depends on ARCH_ZX || COMPILE_TEST +config PWM_XILINX + tristate "Xilinx AXI Timer PWM support" + depends on OF_ADDRESS + depends on COMMON_CLK + select REGMAP_MMIO help - Generic PWM framework driver for ZTE ZX family SoCs. + 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-zx. + 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 index a59c710e98c7..0dc0d2b69025 100644 --- a/drivers/pwm/Makefile +++ b/drivers/pwm/Makefile @@ -1,46 +1,65 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_PWM) += core.o -obj-$(CONFIG_PWM_SYSFS) += sysfs.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_BCM2835) += pwm-bcm2835.o obj-$(CONFIG_PWM_BERLIN) += pwm-berlin.o obj-$(CONFIG_PWM_BRCMSTB) += pwm-brcmstb.o +obj-$(CONFIG_PWM_CLK) += pwm-clk.o 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_IQS620A) += pwm-iqs620a.o obj-$(CONFIG_PWM_JZ4740) += pwm-jz4740.o +obj-$(CONFIG_PWM_KEEMBAY) += pwm-keembay.o +obj-$(CONFIG_PWM_LOONGSON) += pwm-loongson.o obj-$(CONFIG_PWM_LP3943) += pwm-lp3943.o obj-$(CONFIG_PWM_LPC18XX_SCT) += pwm-lpc18xx-sct.o obj-$(CONFIG_PWM_LPC32XX) += pwm-lpc32xx.o obj-$(CONFIG_PWM_LPSS) += pwm-lpss.o obj-$(CONFIG_PWM_LPSS_PCI) += pwm-lpss-pci.o obj-$(CONFIG_PWM_LPSS_PLATFORM) += pwm-lpss-platform.o -obj-$(CONFIG_PWM_MESON) += pwm-meson.o +obj-$(CONFIG_PWM_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_PUV3) += pwm-puv3.o obj-$(CONFIG_PWM_PXA) += pwm-pxa.o -obj-$(CONFIG_PWM_RCAR) += pwm-rcar.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 @@ -48,10 +67,13 @@ 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_ZX) += pwm-zx.o +obj-$(CONFIG_PWM_XILINX) += pwm-xilinx.o diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c index 004b2ea9b5fd..cd06229db394 100644 --- a/drivers/pwm/core.c +++ b/drivers/pwm/core.c @@ -6,10 +6,13 @@ * 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/radix-tree.h> #include <linux/list.h> #include <linux/mutex.h> #include <linux/err.h> @@ -20,456 +23,470 @@ #include <dt-bindings/pwm/pwm.h> +#include <uapi/linux/pwm.h> + #define CREATE_TRACE_POINTS #include <trace/events/pwm.h> -#define MAX_PWMS 1024 +#define PWM_MINOR_COUNT 256 -static DEFINE_MUTEX(pwm_lookup_lock); -static LIST_HEAD(pwm_lookup_list); +/* protects access to pwm_chips */ static DEFINE_MUTEX(pwm_lock); -static LIST_HEAD(pwm_chips); -static DECLARE_BITMAP(allocated_pwms, MAX_PWMS); -static RADIX_TREE(pwm_tree, GFP_KERNEL); -static struct pwm_device *pwm_to_device(unsigned int pwm) +static DEFINE_IDR(pwm_chips); + +static void pwmchip_lock(struct pwm_chip *chip) { - return radix_tree_lookup(&pwm_tree, pwm); + if (chip->atomic) + spin_lock(&chip->atomic_lock); + else + mutex_lock(&chip->nonatomic_lock); } -static int alloc_pwms(int pwm, unsigned int count) +static void pwmchip_unlock(struct pwm_chip *chip) { - unsigned int from = 0; - unsigned int start; - - if (pwm >= MAX_PWMS) - return -EINVAL; + if (chip->atomic) + spin_unlock(&chip->atomic_lock); + else + mutex_unlock(&chip->nonatomic_lock); +} - if (pwm >= 0) - from = pwm; +DEFINE_GUARD(pwmchip, struct pwm_chip *, pwmchip_lock(_T), pwmchip_unlock(_T)) - start = bitmap_find_next_zero_area(allocated_pwms, MAX_PWMS, from, - count, 0); +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 (pwm >= 0 && start != pwm) - return -EEXIST; + if (wf->duty_length_ns > wf->period_length_ns) + return false; - if (start + count > MAX_PWMS) - return -ENOSPC; + /* + * .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 start; + return true; } -static void free_pwms(struct pwm_chip *chip) +static void pwm_wf2state(const struct pwm_waveform *wf, struct pwm_state *state) { - unsigned int i; - - for (i = 0; i < chip->npwm; i++) { - struct pwm_device *pwm = &chip->pwms[i]; - - radix_tree_delete(&pwm_tree, pwm->pwm); + 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, + }; } - - bitmap_clear(allocated_pwms, chip->base, chip->npwm); - - kfree(chip->pwms); - chip->pwms = NULL; } -static struct pwm_chip *pwmchip_find_by_name(const char *name) +static void pwm_state2wf(const struct pwm_state *state, struct pwm_waveform *wf) { - struct pwm_chip *chip; - - if (!name) - return NULL; - - mutex_lock(&pwm_lock); - - list_for_each_entry(chip, &pwm_chips, list) { - const char *chip_name = dev_name(chip->dev); - - if (chip_name && strcmp(chip_name, name) == 0) { - mutex_unlock(&pwm_lock); - return chip; - } + 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, + }; } - - mutex_unlock(&pwm_lock); - - return NULL; } -static int pwm_device_request(struct pwm_device *pwm, const char *label) +static int pwmwfcmp(const struct pwm_waveform *a, const struct pwm_waveform *b) { - int err; - - if (test_bit(PWMF_REQUESTED, &pwm->flags)) - return -EBUSY; + if (a->period_length_ns > b->period_length_ns) + return 1; - if (!try_module_get(pwm->chip->ops->owner)) - return -ENODEV; + if (a->period_length_ns < b->period_length_ns) + return -1; - if (pwm->chip->ops->request) { - err = pwm->chip->ops->request(pwm->chip, pwm); - if (err) { - module_put(pwm->chip->ops->owner); - return err; - } - } + if (a->duty_length_ns > b->duty_length_ns) + return 1; - if (pwm->chip->ops->get_state) { - pwm->chip->ops->get_state(pwm->chip, pwm, &pwm->state); - trace_pwm_get(pwm, &pwm->state); + if (a->duty_length_ns < b->duty_length_ns) + return -1; - if (IS_ENABLED(CONFIG_PWM_DEBUG)) - pwm->last = pwm->state; - } + if (a->duty_offset_ns > b->duty_offset_ns) + return 1; - set_bit(PWMF_REQUESTED, &pwm->flags); - pwm->label = label; + if (a->duty_offset_ns < b->duty_offset_ns) + return -1; return 0; } -struct pwm_device * -of_pwm_xlate_with_flags(struct pwm_chip *pc, const struct of_phandle_args *args) +static bool pwm_check_rounding(const struct pwm_waveform *wf, + const struct pwm_waveform *wf_rounded) { - struct pwm_device *pwm; - - /* check, whether the driver supports a third cell for flags */ - if (pc->of_pwm_n_cells < 3) - return ERR_PTR(-EINVAL); - - /* flags in the third cell are optional */ - if (args->args_count < 2) - return ERR_PTR(-EINVAL); - - if (args->args[0] >= pc->npwm) - return ERR_PTR(-EINVAL); + if (!wf->period_length_ns) + return true; - pwm = pwm_request_from_chip(pc, args->args[0], NULL); - if (IS_ERR(pwm)) - return pwm; + if (wf->period_length_ns < wf_rounded->period_length_ns) + return false; - pwm->args.period = args->args[1]; - pwm->args.polarity = PWM_POLARITY_NORMAL; + if (wf->duty_length_ns < wf_rounded->duty_length_ns) + return false; - if (args->args_count > 2 && args->args[2] & PWM_POLARITY_INVERTED) - pwm->args.polarity = PWM_POLARITY_INVERSED; + if (wf->duty_offset_ns < wf_rounded->duty_offset_ns) + return false; - return pwm; + return true; } -EXPORT_SYMBOL_GPL(of_pwm_xlate_with_flags); -static struct pwm_device * -of_pwm_simple_xlate(struct pwm_chip *pc, const struct of_phandle_args *args) +static int __pwm_round_waveform_tohw(struct pwm_chip *chip, struct pwm_device *pwm, + const struct pwm_waveform *wf, void *wfhw) { - struct pwm_device *pwm; - - /* sanity check driver support */ - if (pc->of_pwm_n_cells < 2) - return ERR_PTR(-EINVAL); + const struct pwm_ops *ops = chip->ops; + int ret; - /* all cells are required */ - if (args->args_count != pc->of_pwm_n_cells) - return ERR_PTR(-EINVAL); + ret = ops->round_waveform_tohw(chip, pwm, wf, wfhw); + trace_pwm_round_waveform_tohw(pwm, wf, wfhw, ret); - if (args->args[0] >= pc->npwm) - return ERR_PTR(-EINVAL); + return ret; +} - pwm = pwm_request_from_chip(pc, args->args[0], NULL); - if (IS_ERR(pwm)) - return pwm; +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; - pwm->args.period = args->args[1]; + ret = ops->round_waveform_fromhw(chip, pwm, wfhw, wf); + trace_pwm_round_waveform_fromhw(pwm, wfhw, wf, ret); - return pwm; + return ret; } -static void of_pwmchip_add(struct pwm_chip *chip) +static int __pwm_read_waveform(struct pwm_chip *chip, struct pwm_device *pwm, void *wfhw) { - if (!chip->dev || !chip->dev->of_node) - return; + const struct pwm_ops *ops = chip->ops; + int ret; - if (!chip->of_xlate) { - chip->of_xlate = of_pwm_simple_xlate; - chip->of_pwm_n_cells = 2; - } + ret = ops->read_waveform(chip, pwm, wfhw); + trace_pwm_read_waveform(pwm, wfhw, ret); - of_node_get(chip->dev->of_node); + return ret; } -static void of_pwmchip_remove(struct pwm_chip *chip) +static int __pwm_write_waveform(struct pwm_chip *chip, struct pwm_device *pwm, const void *wfhw) { - if (chip->dev) - of_node_put(chip->dev->of_node); + 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_set_chip_data() - set private chip data for a PWM + * pwm_round_waveform_might_sleep - Query hardware capabilities + * Cannot be used in atomic context. * @pwm: PWM device - * @data: pointer to chip-specific data + * @wf: waveform to round and output parameter * - * Returns: 0 on success or a negative error code on failure. + * 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_set_chip_data(struct pwm_device *pwm, void *data) +int pwm_round_waveform_might_sleep(struct pwm_device *pwm, struct pwm_waveform *wf) { - if (!pwm) - return -EINVAL; + 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; - pwm->chip_data = data; + BUG_ON(PWM_WFHWSIZE < ops->sizeof_wfhw); - return 0; -} -EXPORT_SYMBOL_GPL(pwm_set_chip_data); + if (!pwmchip_supports_waveform(chip)) + return -EOPNOTSUPP; -/** - * pwm_get_chip_data() - get private chip data for a PWM - * @pwm: PWM device - * - * Returns: A pointer to the chip-private data for the PWM device. - */ -void *pwm_get_chip_data(struct pwm_device *pwm) -{ - return pwm ? pwm->chip_data : NULL; -} -EXPORT_SYMBOL_GPL(pwm_get_chip_data); + if (!pwm_wf_valid(wf)) + return -EINVAL; -static bool pwm_ops_check(const struct pwm_chip *chip) -{ + guard(pwmchip)(chip); - const struct pwm_ops *ops = chip->ops; + if (!chip->operational) + return -ENODEV; - /* driver supports legacy, non-atomic operation */ - if (ops->config && ops->enable && ops->disable) { - if (IS_ENABLED(CONFIG_PWM_DEBUG)) - dev_warn(chip->dev, - "Driver needs updating to atomic API\n"); + ret_tohw = __pwm_round_waveform_tohw(chip, pwm, wf, wfhw); + if (ret_tohw < 0) + return ret_tohw; - return true; - } + 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); - if (!ops->apply) - return false; + ret_fromhw = __pwm_round_waveform_fromhw(chip, pwm, wfhw, wf); + if (ret_fromhw < 0) + return ret_fromhw; - if (IS_ENABLED(CONFIG_PWM_DEBUG) && !ops->get_state) - dev_warn(chip->dev, - "Please implement the .get_state() callback\n"); + 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); - return true; + 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); /** - * pwmchip_add_with_polarity() - register a new PWM chip - * @chip: the PWM chip to add - * @polarity: initial polarity of PWM channels + * pwm_get_waveform_might_sleep - Query hardware about current configuration + * Cannot be used in atomic context. + * @pwm: PWM device + * @wf: output parameter * - * Register a new PWM chip. If chip->base < 0 then a dynamically assigned base - * will be used. The initial polarity for all channels is specified by the - * @polarity 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 error code on failure. + * Returns: 0 on success or a negative errno + * Context: May sleep. */ -int pwmchip_add_with_polarity(struct pwm_chip *chip, - enum pwm_polarity polarity) +int pwm_get_waveform_might_sleep(struct pwm_device *pwm, struct pwm_waveform *wf) { - struct pwm_device *pwm; - unsigned int i; - int ret; - - if (!chip || !chip->dev || !chip->ops || !chip->npwm) - return -EINVAL; - - if (!pwm_ops_check(chip)) - return -EINVAL; - - mutex_lock(&pwm_lock); - - ret = alloc_pwms(chip->base, chip->npwm); - if (ret < 0) - goto out; - - chip->pwms = kcalloc(chip->npwm, sizeof(*pwm), GFP_KERNEL); - if (!chip->pwms) { - ret = -ENOMEM; - goto out; - } + struct pwm_chip *chip = pwm->chip; + const struct pwm_ops *ops = chip->ops; + char wfhw[PWM_WFHWSIZE]; + int err; - chip->base = ret; + BUG_ON(PWM_WFHWSIZE < ops->sizeof_wfhw); - for (i = 0; i < chip->npwm; i++) { - pwm = &chip->pwms[i]; + if (!pwmchip_supports_waveform(chip) || !ops->read_waveform) + return -EOPNOTSUPP; - pwm->chip = chip; - pwm->pwm = chip->base + i; - pwm->hwpwm = i; - pwm->state.polarity = polarity; + guard(pwmchip)(chip); - radix_tree_insert(&pwm_tree, pwm->pwm, pwm); - } + if (!chip->operational) + return -ENODEV; - bitmap_set(allocated_pwms, chip->base, chip->npwm); + err = __pwm_read_waveform(chip, pwm, &wfhw); + if (err) + return err; - INIT_LIST_HEAD(&chip->list); - list_add(&chip->list, &pwm_chips); + return __pwm_round_waveform_fromhw(chip, pwm, &wfhw, wf); +} +EXPORT_SYMBOL_GPL(pwm_get_waveform_might_sleep); - ret = 0; +/* 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; - if (IS_ENABLED(CONFIG_OF)) - of_pwmchip_add(chip); + BUG_ON(PWM_WFHWSIZE < ops->sizeof_wfhw); -out: - mutex_unlock(&pwm_lock); + if (!pwmchip_supports_waveform(chip)) + return -EOPNOTSUPP; - if (!ret) - pwmchip_sysfs_export(chip); - - return ret; -} -EXPORT_SYMBOL_GPL(pwmchip_add_with_polarity); + if (!pwm_wf_valid(wf)) + return -EINVAL; -/** - * pwmchip_add() - register a new PWM chip - * @chip: the PWM chip to add - * - * Register a new PWM chip. If chip->base < 0 then a dynamically assigned base - * will be used. The initial polarity for all channels is normal. - * - * Returns: 0 on success or a negative error code on failure. - */ -int pwmchip_add(struct pwm_chip *chip) -{ - return pwmchip_add_with_polarity(chip, PWM_POLARITY_NORMAL); -} -EXPORT_SYMBOL_GPL(pwmchip_add); + ret_tohw = __pwm_round_waveform_tohw(chip, pwm, wf, &wfhw); + if (ret_tohw < 0) + return ret_tohw; -/** - * pwmchip_remove() - remove a PWM chip - * @chip: the PWM chip to remove - * - * Removes a PWM chip. This function may return busy if the PWM chip provides - * a PWM device that is still requested. - * - * Returns: 0 on success or a negative error code on failure. - */ -int pwmchip_remove(struct pwm_chip *chip) -{ - unsigned int i; - int ret = 0; - - pwmchip_sysfs_unexport(chip); + 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; - mutex_lock(&pwm_lock); + 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); - for (i = 0; i < chip->npwm; i++) { - struct pwm_device *pwm = &chip->pwms[i]; + 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); - if (test_bit(PWMF_REQUESTED, &pwm->flags)) { - ret = -EBUSY; - goto out; + return 1; } } - list_del_init(&chip->list); - - if (IS_ENABLED(CONFIG_OF)) - of_pwmchip_remove(chip); + err = __pwm_write_waveform(chip, pwm, &wfhw); + if (err) + return err; - free_pwms(chip); - -out: - mutex_unlock(&pwm_lock); - return ret; -} -EXPORT_SYMBOL_GPL(pwmchip_remove); + /* update .state */ + pwm_wf2state(wf, &pwm->state); -/** - * pwm_request() - request a PWM device - * @pwm: global PWM device index - * @label: PWM device label - * - * This function is deprecated, use pwm_get() instead. - * - * Returns: A pointer to a PWM device or an ERR_PTR()-encoded error code on - * failure. - */ -struct pwm_device *pwm_request(int pwm, const char *label) -{ - struct pwm_device *dev; - int err; + if (IS_ENABLED(CONFIG_PWM_DEBUG) && ops->read_waveform && wf->period_length_ns) { + struct pwm_waveform wf_set; - if (pwm < 0 || pwm >= MAX_PWMS) - return ERR_PTR(-EINVAL); + err = __pwm_read_waveform(chip, pwm, &wfhw); + if (err) + /* maybe ignore? */ + return err; - mutex_lock(&pwm_lock); + err = __pwm_round_waveform_fromhw(chip, pwm, &wfhw, &wf_set); + if (err) + /* maybe ignore? */ + return err; - dev = pwm_to_device(pwm); - if (!dev) { - dev = ERR_PTR(-EPROBE_DEFER); - goto out; + 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); } - err = pwm_device_request(dev, label); - if (err < 0) - dev = ERR_PTR(err); - -out: - mutex_unlock(&pwm_lock); - - return dev; + return ret_tohw; } -EXPORT_SYMBOL_GPL(pwm_request); /** - * 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 + * 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 * - * 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. + * 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. */ -struct pwm_device *pwm_request_from_chip(struct pwm_chip *chip, - unsigned int index, - const char *label) +int pwm_set_waveform_might_sleep(struct pwm_device *pwm, + const struct pwm_waveform *wf, bool exact) { - struct pwm_device *pwm; + struct pwm_chip *chip = pwm->chip; int err; - if (!chip || index >= chip->npwm) - return ERR_PTR(-EINVAL); + might_sleep(); - mutex_lock(&pwm_lock); - pwm = &chip->pwms[index]; + guard(pwmchip)(chip); - err = pwm_device_request(pwm, label); - if (err < 0) - pwm = ERR_PTR(err); + if (!chip->operational) + return -ENODEV; - mutex_unlock(&pwm_lock); - return pwm; -} -EXPORT_SYMBOL_GPL(pwm_request_from_chip); + 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); + } -/** - * pwm_free() - free a PWM device - * @pwm: PWM device - * - * This function is deprecated, use pwm_put() instead. - */ -void pwm_free(struct pwm_device *pwm) -{ - pwm_put(pwm); + /* + * 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_free); +EXPORT_SYMBOL_GPL(pwm_set_waveform_might_sleep); -static void pwm_apply_state_debug(struct pwm_device *pwm, - const struct pwm_state *state) +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, s2; + struct pwm_state s1 = { 0 }, s2 = { 0 }; int err; if (!IS_ENABLED(CONFIG_PWM_DEBUG)) @@ -480,208 +497,347 @@ static void pwm_apply_state_debug(struct pwm_device *pwm, 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. */ - chip->ops->get_state(chip, pwm, &s1); - trace_pwm_get(pwm, &s1); + 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.enabled && s1.polarity != state->polarity) { + if (s1.polarity != state->polarity) { s2.polarity = state->polarity; s2.duty_cycle = s1.period - s1.duty_cycle; s2.period = s1.period; - s2.enabled = s1.enabled; + s2.enabled = true; } else { s2 = s1; } if (s2.polarity != state->polarity && - state->duty_cycle < state->period) - dev_warn(chip->dev, ".apply ignored .polarity\n"); + s2.duty_cycle < s2.period) + dev_warn(pwmchip_parent(chip), ".apply ignored .polarity\n"); - if (state->enabled && - last->polarity == state->polarity && + if (last->polarity == state->polarity && last->period > s2.period && last->period <= state->period) - dev_warn(chip->dev, - ".apply didn't pick the best available period (requested: %u, applied: %u, possible: %u)\n", + 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); - if (state->enabled && state->period < s2.period) - dev_warn(chip->dev, - ".apply is supposed to round down period (requested: %u, applied: %u)\n", + /* + * 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 (state->enabled && - last->polarity == state->polarity && + if (last->polarity == state->polarity && last->period == s2.period && last->duty_cycle > s2.duty_cycle && last->duty_cycle <= state->duty_cycle) - dev_warn(chip->dev, - ".apply didn't pick the best available duty cycle (requested: %u/%u, applied: %u/%u, possible: %u/%u)\n", + 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->enabled && state->duty_cycle < s2.duty_cycle) - dev_warn(chip->dev, - ".apply is supposed to round down duty_cycle (requested: %u/%u, applied: %u/%u)\n", + 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); - if (!state->enabled && s2.enabled && s2.duty_cycle > 0) - dev_warn(chip->dev, - "requested disabled, but yielded enabled with duty > 0\n"); - /* 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(chip->dev, "failed to reapply current setting\n"); + dev_err(pwmchip_parent(chip), "failed to reapply current setting\n"); return; } - trace_pwm_apply(pwm, &s1); - - chip->ops->get_state(chip, pwm, last); - trace_pwm_get(pwm, last); + *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(chip->dev, - ".apply is not idempotent (ena=%d pol=%d %u/%u) -> (ena=%d pol=%d %u/%u)\n", + 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); } } -/** - * pwm_apply_state() - atomically apply a new state to a PWM device - * @pwm: PWM device - * @state: new state to apply - */ -int pwm_apply_state(struct pwm_device *pwm, const struct pwm_state *state) +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 || !state->period || - state->duty_cycle > state->period) + if (!pwm || !state) + return -EINVAL; + + if (!pwm_state_valid(state)) { + /* + * Allow to transition from one invalid state to another. + * This ensures that you can e.g. change the polarity while + * the period is zero. (This happens on stm32 when the hardware + * is in its poweron default state.) This greatly simplifies + * working with the sysfs API where you can only change one + * parameter at a time. + */ + if (!pwm_state_valid(&pwm->state)) { + pwm->state = *state; + return 0; + } + return -EINVAL; + } chip = pwm->chip; + 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->enabled == pwm->state.enabled && + state->usage_power == pwm->state.usage_power) return 0; - if (chip->ops->apply) { - err = chip->ops->apply(chip, pwm, state); - if (err) - return err; + if (pwmchip_supports_waveform(chip)) { + struct pwm_waveform wf; + char wfhw[PWM_WFHWSIZE]; - trace_pwm_apply(pwm, state); + BUG_ON(PWM_WFHWSIZE < ops->sizeof_wfhw); - pwm->state = *state; + pwm_state2wf(state, &wf); /* - * only do this after pwm->state was applied as some - * implementations of .get_state depend on this - */ - pwm_apply_state_debug(pwm, state); - } else { - /* - * FIXME: restore the initial state in case of error. + * 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. */ - if (state->polarity != pwm->state.polarity) { - if (!chip->ops->set_polarity) - return -ENOTSUPP; - /* - * Changing the polarity of a running PWM is - * only allowed when the PWM driver implements - * ->apply(). - */ - if (pwm->state.enabled) { - chip->ops->disable(chip, pwm); - pwm->state.enabled = false; - } - - err = chip->ops->set_polarity(chip, pwm, - state->polarity); - if (err) - return err; + 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; - pwm->state.polarity = state->polarity; + return err; } - if (state->period != pwm->state.period || - state->duty_cycle != pwm->state.duty_cycle) { - err = chip->ops->config(pwm->chip, pwm, - state->duty_cycle, - state->period); + 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; - pwm->state.duty_cycle = state->duty_cycle; - pwm->state.period = state->period; + 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); } - if (state->enabled != pwm->state.enabled) { - if (state->enabled) { - err = chip->ops->enable(chip, pwm); - if (err) - return err; - } else { - chip->ops->disable(chip, pwm); - } + err = __pwm_write_waveform(chip, pwm, &wfhw); + if (err) + return err; - pwm->state.enabled = state->enabled; - } + 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; } -EXPORT_SYMBOL_GPL(pwm_apply_state); /** - * pwm_capture() - capture and report a PWM signal + * pwm_apply_might_sleep() - atomically apply a new state to a PWM device + * Cannot be used in atomic context. * @pwm: PWM device - * @result: structure to fill with capture result - * @timeout: time to wait, in milliseconds, before giving up on capture + * @state: new state to apply * - * Returns: 0 on success or a negative error code on failure. + * Returns: 0 on success, or a negative errno + * Context: May sleep. */ -int pwm_capture(struct pwm_device *pwm, struct pwm_capture *result, - unsigned long timeout) +int pwm_apply_might_sleep(struct pwm_device *pwm, const struct pwm_state *state) { int err; + struct pwm_chip *chip = pwm->chip; - if (!pwm || !pwm->chip->ops) - return -EINVAL; + /* + * 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(); - if (!pwm->chip->ops->capture) - return -ENOSYS; + guard(pwmchip)(chip); - mutex_lock(&pwm_lock); - err = pwm->chip->ops->capture(pwm->chip, pwm, result, timeout); - mutex_unlock(&pwm_lock); + 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_capture); +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 @@ -690,6 +846,9 @@ EXPORT_SYMBOL_GPL(pwm_capture); * 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) { @@ -712,7 +871,7 @@ int pwm_adjust_config(struct pwm_device *pwm) state.period = pargs.period; state.polarity = pargs.polarity; - return pwm_apply_state(pwm, &state); + return pwm_apply_might_sleep(pwm, &state); } /* @@ -735,25 +894,829 @@ int pwm_adjust_config(struct pwm_device *pwm) state.duty_cycle = state.period - state.duty_cycle; } - return pwm_apply_state(pwm, &state); + return pwm_apply_might_sleep(pwm, &state); } EXPORT_SYMBOL_GPL(pwm_adjust_config); -static struct pwm_chip *of_node_to_pwmchip(struct device_node *np) +/** + * 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; - mutex_lock(&pwm_lock); + if (!name) + return NULL; + + guard(mutex)(&pwm_lock); - list_for_each_entry(chip, &pwm_chips, list) - if (chip->dev && chip->dev->of_node == np) { - mutex_unlock(&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; } + } - mutex_unlock(&pwm_lock); + 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, }; - return ERR_PTR(-EPROBE_DEFER); + 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(sizeof(*export), GFP_KERNEL); + 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 || + !ops->write_waveform) + 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, @@ -767,21 +1730,35 @@ static struct device_link *pwm_device_link_add(struct device *dev, * impact the PM sequence ordering: the PWM supplier may get * suspended before the consumer. */ - dev_warn(pwm->chip->dev, + dev_warn(pwmchip_parent(pwm->chip), "No consumer device specified to create a link to\n"); return NULL; } - dl = device_link_add(dev, pwm->chip->dev, DL_FLAG_AUTOREMOVE_CONSUMER); + 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(pwm->chip->dev)); + 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 @@ -802,13 +1779,13 @@ static struct device_link *pwm_device_link_add(struct device *dev, * Returns: A pointer to the requested PWM device or an ERR_PTR()-encoded * error code on failure. */ -struct pwm_device *of_pwm_get(struct device *dev, struct device_node *np, - const char *con_id) +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 *pc; + struct pwm_chip *chip; int index = 0; int err; @@ -818,30 +1795,29 @@ struct pwm_device *of_pwm_get(struct device *dev, struct device_node *np, return ERR_PTR(index); } - err = of_parse_phandle_with_args(np, "pwms", "#pwm-cells", index, - &args); + 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); } - pc = of_node_to_pwmchip(args.np); - if (IS_ERR(pc)) { - if (PTR_ERR(pc) != -EPROBE_DEFER) + 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(pc); + pwm = ERR_CAST(chip); goto put; } - pwm = pc->of_xlate(pc, &args); + 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_free(pwm); + pwm_put(pwm); pwm = ERR_CAST(dl); goto put; } @@ -865,33 +1841,10 @@ put: return pwm; } -EXPORT_SYMBOL_GPL(of_pwm_get); - -#if IS_ENABLED(CONFIG_ACPI) -static struct pwm_chip *device_to_pwmchip(struct device *dev) -{ - struct pwm_chip *chip; - - mutex_lock(&pwm_lock); - - list_for_each_entry(chip, &pwm_chips, list) { - struct acpi_device *adev = ACPI_COMPANION(chip->dev); - - if ((chip->dev == dev) || (adev && &adev->dev == dev)) { - mutex_unlock(&pwm_lock); - return chip; - } - } - - mutex_unlock(&pwm_lock); - - return ERR_PTR(-EPROBE_DEFER); -} -#endif /** * acpi_pwm_get() - request a PWM via parsing "pwms" property in ACPI - * @fwnode: firmware node to get the "pwm" property from + * @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. @@ -906,12 +1859,10 @@ static struct pwm_chip *device_to_pwmchip(struct device *dev) * Returns: A pointer to the requested PWM device or an ERR_PTR()-encoded * error code on failure. */ -static struct pwm_device *acpi_pwm_get(struct fwnode_handle *fwnode) +static struct pwm_device *acpi_pwm_get(const struct fwnode_handle *fwnode) { - struct pwm_device *pwm = ERR_PTR(-ENODEV); -#if IS_ENABLED(CONFIG_ACPI) + struct pwm_device *pwm; struct fwnode_reference_args args; - struct acpi_device *acpi; struct pwm_chip *chip; int ret; @@ -921,14 +1872,10 @@ static struct pwm_device *acpi_pwm_get(struct fwnode_handle *fwnode) if (ret < 0) return ERR_PTR(ret); - acpi = to_acpi_device_node(args.fwnode); - if (!acpi) - return ERR_PTR(-EINVAL); - if (args.nargs < 2) return ERR_PTR(-EPROTO); - chip = device_to_pwmchip(&acpi->dev); + chip = fwnode_to_pwmchip(args.fwnode); if (IS_ERR(chip)) return ERR_CAST(chip); @@ -941,44 +1888,12 @@ static struct pwm_device *acpi_pwm_get(struct fwnode_handle *fwnode) if (args.nargs > 2 && args.args[2] & PWM_POLARITY_INVERTED) pwm->args.polarity = PWM_POLARITY_INVERSED; -#endif return pwm; } -/** - * 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) -{ - mutex_lock(&pwm_lookup_lock); - - while (num--) { - list_add_tail(&table->list, &pwm_lookup_list); - table++; - } - - mutex_unlock(&pwm_lookup_lock); -} - -/** - * 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) -{ - mutex_lock(&pwm_lookup_lock); - - while (num--) { - list_del(&table->list); - table++; - } - - mutex_unlock(&pwm_lookup_lock); -} +static DEFINE_MUTEX(pwm_lookup_lock); +static LIST_HEAD(pwm_lookup_list); /** * pwm_get() - look up and request a PWM device @@ -997,6 +1912,7 @@ void pwm_remove_table(struct pwm_lookup *table, size_t num) */ 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; @@ -1007,12 +1923,12 @@ struct pwm_device *pwm_get(struct device *dev, const char *con_id) int err; /* look up via DT first */ - if (IS_ENABLED(CONFIG_OF) && dev && dev->of_node) - return of_pwm_get(dev, dev->of_node, con_id); + if (is_of_node(fwnode)) + return of_pwm_get(dev, to_of_node(fwnode), con_id); /* then lookup via ACPI */ - if (dev && is_acpi_node(dev->fwnode)) { - pwm = acpi_pwm_get(dev->fwnode); + if (is_acpi_node(fwnode)) { + pwm = acpi_pwm_get(fwnode); if (!IS_ERR(pwm) || PTR_ERR(pwm) != -ENOENT) return pwm; } @@ -1037,36 +1953,33 @@ struct pwm_device *pwm_get(struct device *dev, const char *con_id) * Then we take the most specific entry - with the following order * of precedence: dev+con > dev only > con only. */ - mutex_lock(&pwm_lookup_lock); - - list_for_each_entry(p, &pwm_lookup_list, list) { - match = 0; + scoped_guard(mutex, &pwm_lookup_lock) + list_for_each_entry(p, &pwm_lookup_list, list) { + match = 0; - if (p->dev_id) { - if (!dev_id || strcmp(p->dev_id, dev_id)) - continue; + if (p->dev_id) { + if (!dev_id || strcmp(p->dev_id, dev_id)) + continue; - match += 2; - } + match += 2; + } - if (p->con_id) { - if (!con_id || strcmp(p->con_id, con_id)) - continue; + if (p->con_id) { + if (!con_id || strcmp(p->con_id, con_id)) + continue; - match += 1; - } + match += 1; + } - if (match > best) { - chosen = p; + if (match > best) { + chosen = p; - if (match != 3) - best = match; - else - break; + if (match != 3) + best = match; + else + break; + } } - } - - mutex_unlock(&pwm_lookup_lock); if (!chosen) return ERR_PTR(-ENODEV); @@ -1094,7 +2007,7 @@ struct pwm_device *pwm_get(struct device *dev, const char *con_id) dl = pwm_device_link_add(dev, pwm); if (IS_ERR(dl)) { - pwm_free(pwm); + pwm_put(pwm); return ERR_CAST(dl); } @@ -1105,102 +2018,77 @@ struct pwm_device *pwm_get(struct device *dev, const char *con_id) } EXPORT_SYMBOL_GPL(pwm_get); -/** - * pwm_put() - release a PWM device - * @pwm: PWM device - */ -void pwm_put(struct pwm_device *pwm) +static void __pwm_put(struct pwm_device *pwm) { - if (!pwm) - return; - - mutex_lock(&pwm_lock); + struct pwm_chip *chip = pwm->chip; - if (!test_and_clear_bit(PWMF_REQUESTED, &pwm->flags)) { + /* + * 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"); - goto out; + return; } - if (pwm->chip->ops->free) + if (chip->operational && chip->ops->free) pwm->chip->ops->free(pwm->chip, pwm); - pwm_set_chip_data(pwm, NULL); pwm->label = NULL; - module_put(pwm->chip->ops->owner); -out: - mutex_unlock(&pwm_lock); -} -EXPORT_SYMBOL_GPL(pwm_put); + put_device(&chip->dev); -static void devm_pwm_release(struct device *dev, void *res) -{ - pwm_put(*(struct pwm_device **)res); + module_put(chip->owner); } /** - * 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. + * pwm_put() - release a PWM device + * @pwm: PWM device */ -struct pwm_device *devm_pwm_get(struct device *dev, const char *con_id) +void pwm_put(struct pwm_device *pwm) { - struct pwm_device **ptr, *pwm; + if (!pwm) + return; - ptr = devres_alloc(devm_pwm_release, sizeof(*ptr), GFP_KERNEL); - if (!ptr) - return ERR_PTR(-ENOMEM); + guard(mutex)(&pwm_lock); - pwm = pwm_get(dev, con_id); - if (!IS_ERR(pwm)) { - *ptr = pwm; - devres_add(dev, ptr); - } else { - devres_free(ptr); - } + __pwm_put(pwm); +} +EXPORT_SYMBOL_GPL(pwm_put); - return pwm; +static void devm_pwm_release(void *pwm) +{ + pwm_put(pwm); } -EXPORT_SYMBOL_GPL(devm_pwm_get); /** - * devm_of_pwm_get() - resource managed of_pwm_get() + * devm_pwm_get() - resource managed pwm_get() * @dev: device for PWM consumer - * @np: device node to get the PWM from * @con_id: consumer name * - * This function performs like of_pwm_get() but the acquired PWM device will + * 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_of_pwm_get(struct device *dev, struct device_node *np, - const char *con_id) +struct pwm_device *devm_pwm_get(struct device *dev, const char *con_id) { - struct pwm_device **ptr, *pwm; + struct pwm_device *pwm; + int ret; - ptr = devres_alloc(devm_pwm_release, sizeof(*ptr), GFP_KERNEL); - if (!ptr) - return ERR_PTR(-ENOMEM); + pwm = pwm_get(dev, con_id); + if (IS_ERR(pwm)) + return pwm; - pwm = of_pwm_get(dev, np, con_id); - if (!IS_ERR(pwm)) { - *ptr = pwm; - devres_add(dev, ptr); - } else { - devres_free(ptr); - } + ret = devm_add_action_or_reset(dev, devm_pwm_release, pwm); + if (ret) + return ERR_PTR(ret); return pwm; } -EXPORT_SYMBOL_GPL(devm_of_pwm_get); +EXPORT_SYMBOL_GPL(devm_pwm_get); /** * devm_fwnode_pwm_get() - request a resource managed PWM from firmware node @@ -1218,76 +2106,558 @@ struct pwm_device *devm_fwnode_pwm_get(struct device *dev, struct fwnode_handle *fwnode, const char *con_id) { - struct pwm_device **ptr, *pwm = ERR_PTR(-ENODEV); - - ptr = devres_alloc(devm_pwm_release, sizeof(*ptr), GFP_KERNEL); - if (!ptr) - return ERR_PTR(-ENOMEM); + 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; - if (!IS_ERR(pwm)) { - *ptr = pwm; - devres_add(dev, ptr); - } else { - devres_free(ptr); - } + 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); -static int devm_pwm_match(struct device *dev, void *res, void *data) +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_device **p = res; + struct pwm_chip *chip = container_of(inode->i_cdev, struct pwm_chip, cdev); + struct pwm_cdev_data *cdata; - if (WARN_ON(!p || !*p)) - return 0; + guard(mutex)(&pwm_lock); + + if (!chip->operational) + return -ENXIO; + + cdata = kzalloc(struct_size(cdata, pwm, chip->npwm), GFP_KERNEL); + if (!cdata) + return -ENOMEM; + + cdata->chip = chip; + + file->private_data = cdata; - return *p == data; + 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, + }; + + return copy_to_user((struct pwmchip_waveform __user *)arg, + &cwf, sizeof(cwf)); + } + + 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, + }; + + return copy_to_user((struct pwmchip_waveform __user *)arg, + &cwf, sizeof(cwf)); + } + + 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); } /** - * devm_pwm_put() - resource managed pwm_put() - * @dev: device for PWM consumer - * @pwm: PWM device + * __pwmchip_add() - register a new PWM chip + * @chip: the PWM chip to add + * @owner: reference to the module providing the chip. * - * Release a PWM previously allocated using devm_pwm_get(). Calling this - * function is usually not needed because devm-allocated resources are - * automatically released on driver detach. + * 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. */ -void devm_pwm_put(struct device *dev, struct pwm_device *pwm) +int __pwmchip_add(struct pwm_chip *chip, struct module *owner) { - WARN_ON(devres_release(dev, devm_pwm_release, devm_pwm_match, pwm)); + 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++; + } } -EXPORT_SYMBOL_GPL(devm_pwm_put); -#ifdef CONFIG_DEBUG_FS 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; + struct pwm_state state, hwstate; pwm_get_state(pwm, &state); + pwm_get_state_hw(pwm, &hwstate); seq_printf(s, " pwm-%-3d (%-20.20s):", i, pwm->label); if (test_bit(PWMF_REQUESTED, &pwm->flags)) seq_puts(s, " requested"); - if (state.enabled) - seq_puts(s, " enabled"); + seq_puts(s, "\n"); - seq_printf(s, " period: %u ns", state.period); - seq_printf(s, " duty: %u ns", state.duty_cycle); - seq_printf(s, " polarity: %s", + seq_printf(s, " requested configuration: %3sabled, %llu/%llu ns, %s polarity", + state.enabled ? "en" : "dis", state.duty_cycle, state.period, state.polarity ? "inverse" : "normal"); + if (state.usage_power) + seq_puts(s, ", usage_power"); + seq_puts(s, "\n"); + + seq_printf(s, " actual configuration: %3sabled, %llu/%llu ns, %s polarity", + hwstate.enabled ? "en" : "dis", hwstate.duty_cycle, hwstate.period, + hwstate.polarity ? "inverse" : "normal"); seq_puts(s, "\n"); } @@ -1295,17 +2665,27 @@ static void pwm_dbg_show(struct pwm_chip *chip, struct seq_file *s) static void *pwm_seq_start(struct seq_file *s, loff_t *pos) { + unsigned long id = *pos; + void *ret; + mutex_lock(&pwm_lock); s->private = ""; - return seq_list_start(&pwm_chips, *pos); + 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"; - return seq_list_next(v, &pwm_chips, pos); + ret = idr_get_next_ul(&pwm_chips, &id); + *pos = id; + return ret; } static void pwm_seq_stop(struct seq_file *s, void *v) @@ -1315,44 +2695,47 @@ static void pwm_seq_stop(struct seq_file *s, void *v) static int pwm_seq_show(struct seq_file *s, void *v) { - struct pwm_chip *chip = list_entry(v, struct pwm_chip, list); + struct pwm_chip *chip = v; - seq_printf(s, "%s%s/%s, %d PWM device%s\n", (char *)s->private, - chip->dev->bus ? chip->dev->bus->name : "no-bus", - dev_name(chip->dev), chip->npwm, - (chip->npwm != 1) ? "s" : ""); + 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_seq_ops = { +static const struct seq_operations pwm_debugfs_sops = { .start = pwm_seq_start, .next = pwm_seq_next, .stop = pwm_seq_stop, .show = pwm_seq_show, }; -static int pwm_seq_open(struct inode *inode, struct file *file) +DEFINE_SEQ_ATTRIBUTE(pwm_debugfs); + +static int __init pwm_init(void) { - return seq_open(file, &pwm_seq_ops); -} + int ret; -static const struct file_operations pwm_debugfs_ops = { - .owner = THIS_MODULE, - .open = pwm_seq_open, - .read = seq_read, - .llseek = seq_lseek, - .release = seq_release, -}; + 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; + } -static int __init pwm_debugfs_init(void) -{ - debugfs_create_file("pwm", S_IFREG | S_IRUGO, NULL, NULL, - &pwm_debugfs_ops); + 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_debugfs_init); -#endif /* CONFIG_DEBUG_FS */ +subsys_initcall(pwm_init); diff --git a/drivers/pwm/pwm-ab8500.c b/drivers/pwm/pwm-ab8500.c index fdf3964db4a6..f000adab85b0 100644 --- a/drivers/pwm/pwm-ab8500.c +++ b/drivers/pwm/pwm-ab8500.c @@ -3,6 +3,7 @@ * 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> @@ -20,110 +21,194 @@ #define AB8500_PWM_OUT_CTRL2_REG 0x61 #define AB8500_PWM_OUT_CTRL7_REG 0x66 +#define AB8500_PWM_CLKRATE 9600000 + struct ab8500_pwm_chip { - struct pwm_chip chip; + unsigned int hwid; }; -static int ab8500_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, - int duty_ns, int period_ns) +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 = 0; - unsigned int higher_val, lower_val; + 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); + } /* - * get the first 8 bits that are be written to + * 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_ns & 0x00FF; + lower_val = (duty_steps - 1) & 0x00ff; /* - * get bits [9:10] that are to be written to - * AB8500_PWM_OUT_CTRL2_REG[0:1] + * The two remaining high bits to + * AB8500_PWM_OUT_CTRL2_REG[0:1]; together with FreqPWMOutx. */ - higher_val = ((duty_ns & 0x0300) >> 8); + higher_val = ((duty_steps - 1) & 0x0300) >> 8 | (32 - div) << 4; - reg = AB8500_PWM_OUT_CTRL1_REG + ((chip->base - 1) * 2); + reg = AB8500_PWM_OUT_CTRL1_REG + (ab8500->hwid * 2); - ret = abx500_set_register_interruptible(chip->dev, AB8500_MISC, - reg, (u8)lower_val); + ret = abx500_set_register_interruptible(pwmchip_parent(chip), AB8500_MISC, + reg, lower_val); if (ret < 0) return ret; - ret = abx500_set_register_interruptible(chip->dev, AB8500_MISC, - (reg + 1), (u8)higher_val); - return ret; -} - -static int ab8500_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) -{ - int ret; + ret = abx500_set_register_interruptible(pwmchip_parent(chip), AB8500_MISC, + (reg + 1), higher_val); + if (ret < 0) + return ret; - ret = abx500_mask_and_set_register_interruptible(chip->dev, + /* enable */ + ret = abx500_mask_and_set_register_interruptible(pwmchip_parent(chip), AB8500_MISC, AB8500_PWM_OUT_CTRL7_REG, - 1 << (chip->base - 1), 1 << (chip->base - 1)); + 1 << ab8500->hwid, 1 << ab8500->hwid); if (ret < 0) - dev_err(chip->dev, "%s: Failed to enable PWM, Error %d\n", + dev_err(pwmchip_parent(chip), "%s: Failed to enable PWM, Error %d\n", pwm->label, ret); + return ret; } -static void ab8500_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) +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_mask_and_set_register_interruptible(chip->dev, - AB8500_MISC, AB8500_PWM_OUT_CTRL7_REG, - 1 << (chip->base - 1), 0); - if (ret < 0) - dev_err(chip->dev, "%s: Failed to disable PWM, Error %d\n", - pwm->label, ret); + 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 = { - .config = ab8500_pwm_config, - .enable = ab8500_pwm_enable, - .disable = ab8500_pwm_disable, - .owner = THIS_MODULE, + .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 */ - ab8500 = devm_kzalloc(&pdev->dev, sizeof(*ab8500), GFP_KERNEL); - if (ab8500 == NULL) - return -ENOMEM; + chip = devm_pwmchip_alloc(&pdev->dev, 1, sizeof(*ab8500)); + if (IS_ERR(chip)) + return PTR_ERR(chip); - ab8500->chip.dev = &pdev->dev; - ab8500->chip.ops = &ab8500_pwm_ops; - ab8500->chip.base = pdev->id; - ab8500->chip.npwm = 1; + ab8500 = ab8500_pwm_from_chip(chip); - err = pwmchip_add(&ab8500->chip); - if (err < 0) - return err; - - dev_dbg(&pdev->dev, "pwm probe successful\n"); - platform_set_drvdata(pdev, ab8500); - - return 0; -} + chip->ops = &ab8500_pwm_ops; + ab8500->hwid = pdev->id - 1; -static int ab8500_pwm_remove(struct platform_device *pdev) -{ - struct ab8500_pwm_chip *ab8500 = platform_get_drvdata(pdev); - int err; - - err = pwmchip_remove(&ab8500->chip); + err = devm_pwmchip_add(&pdev->dev, chip); if (err < 0) - return err; + return dev_err_probe(&pdev->dev, err, "Failed to add pwm chip\n"); - dev_dbg(&pdev->dev, "pwm driver removed\n"); + dev_dbg(&pdev->dev, "pwm probe successful\n"); return 0; } @@ -133,7 +218,6 @@ static struct platform_driver ab8500_pwm_driver = { .name = "ab8500-pwm", }, .probe = ab8500_pwm_probe, - .remove = ab8500_pwm_remove, }; module_platform_driver(ab8500_pwm_driver); diff --git a/drivers/pwm/pwm-adp5585.c b/drivers/pwm/pwm-adp5585.c new file mode 100644 index 000000000000..806f8d79b0d7 --- /dev/null +++ b/drivers/pwm/pwm-adp5585.c @@ -0,0 +1,223 @@ +// 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/mod_devicetable.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[] = { + { "adp5585-pwm", (kernel_ulong_t)&adp5585_pwm_chip_info }, + { "adp5589-pwm", (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 000000000000..7236e31d2f17 --- /dev/null +++ b/drivers/pwm/pwm-airoha.c @@ -0,0 +1,622 @@ +// 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/mod_devicetable.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 000000000000..6e58aca2f13c --- /dev/null +++ b/drivers/pwm/pwm-apple.c @@ -0,0 +1,159 @@ +// 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/mod_devicetable.h> +#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 000000000000..2c59bd142d40 --- /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 index dcbc0489dfd4..387a0d1fa4f2 100644 --- a/drivers/pwm/pwm-atmel-hlcdc.c +++ b/drivers/pwm/pwm-atmel-hlcdc.c @@ -10,6 +10,7 @@ #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> @@ -27,7 +28,6 @@ struct atmel_hlcdc_pwm_errata { }; struct atmel_hlcdc_pwm { - struct pwm_chip chip; struct atmel_hlcdc *hlcdc; struct clk *cur_clk; const struct atmel_hlcdc_pwm_errata *errata; @@ -35,14 +35,14 @@ struct atmel_hlcdc_pwm { static inline struct atmel_hlcdc_pwm *to_atmel_hlcdc_pwm(struct pwm_chip *chip) { - return container_of(chip, struct atmel_hlcdc_pwm, chip); + return pwmchip_get_drvdata(chip); } -static int atmel_hlcdc_pwm_apply(struct pwm_chip *c, struct pwm_device *pwm, +static int atmel_hlcdc_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, const struct pwm_state *state) { - struct atmel_hlcdc_pwm *chip = to_atmel_hlcdc_pwm(c); - struct atmel_hlcdc *hlcdc = chip->hlcdc; + struct atmel_hlcdc_pwm *atmel = to_atmel_hlcdc_pwm(chip); + struct atmel_hlcdc *hlcdc = atmel->hlcdc; unsigned int status; int ret; @@ -54,7 +54,7 @@ static int atmel_hlcdc_pwm_apply(struct pwm_chip *c, struct pwm_device *pwm, u32 pwmcfg; int pres; - if (!chip->errata || !chip->errata->slow_clk_erratum) { + if (!atmel->errata || !atmel->errata->slow_clk_erratum) { clk_freq = clk_get_rate(new_clk); if (!clk_freq) return -EINVAL; @@ -64,7 +64,7 @@ static int atmel_hlcdc_pwm_apply(struct pwm_chip *c, struct pwm_device *pwm, } /* Errata: cannot use slow clk on some IP revisions */ - if ((chip->errata && chip->errata->slow_clk_erratum) || + 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); @@ -77,8 +77,8 @@ static int atmel_hlcdc_pwm_apply(struct pwm_chip *c, struct pwm_device *pwm, for (pres = 0; pres <= ATMEL_HLCDC_PWMPS_MAX; pres++) { /* Errata: cannot divide by 1 on some IP revisions */ - if (!pres && chip->errata && - chip->errata->div1_clk_erratum) + if (!pres && atmel->errata && + atmel->errata->div1_clk_erratum) continue; if ((clk_period_ns << pres) >= state->period) @@ -90,7 +90,7 @@ static int atmel_hlcdc_pwm_apply(struct pwm_chip *c, struct pwm_device *pwm, pwmcfg = ATMEL_HLCDC_PWMPS(pres); - if (new_clk != chip->cur_clk) { + if (new_clk != atmel->cur_clk) { u32 gencfg = 0; int ret; @@ -98,8 +98,8 @@ static int atmel_hlcdc_pwm_apply(struct pwm_chip *c, struct pwm_device *pwm, if (ret) return ret; - clk_disable_unprepare(chip->cur_clk); - chip->cur_clk = new_clk; + clk_disable_unprepare(atmel->cur_clk); + atmel->cur_clk = new_clk; if (new_clk == hlcdc->sys_clk) gencfg = ATMEL_HLCDC_CLKPWMSEL; @@ -160,8 +160,8 @@ static int atmel_hlcdc_pwm_apply(struct pwm_chip *c, struct pwm_device *pwm, if (ret) return ret; - clk_disable_unprepare(chip->cur_clk); - chip->cur_clk = NULL; + clk_disable_unprepare(atmel->cur_clk); + atmel->cur_clk = NULL; } return 0; @@ -169,7 +169,6 @@ static int atmel_hlcdc_pwm_apply(struct pwm_chip *c, struct pwm_device *pwm, static const struct pwm_ops atmel_hlcdc_pwm_ops = { .apply = atmel_hlcdc_pwm_apply, - .owner = THIS_MODULE, }; static const struct atmel_hlcdc_pwm_errata atmel_hlcdc_pwm_at91sam9x5_errata = { @@ -180,39 +179,38 @@ static const struct atmel_hlcdc_pwm_errata atmel_hlcdc_pwm_sama5d3_errata = { .div1_clk_erratum = true, }; -#ifdef CONFIG_PM_SLEEP static int atmel_hlcdc_pwm_suspend(struct device *dev) { - struct atmel_hlcdc_pwm *chip = dev_get_drvdata(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_is_enabled(&chip->chip.pwms[0])) - clk_disable_unprepare(chip->hlcdc->periph_clk); + if (!pwm->state.enabled) + clk_disable_unprepare(atmel->hlcdc->periph_clk); return 0; } static int atmel_hlcdc_pwm_resume(struct device *dev) { - struct atmel_hlcdc_pwm *chip = dev_get_drvdata(dev); - struct pwm_state state; + 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; - pwm_get_state(&chip->chip.pwms[0], &state); - /* Re-enable the periph clock it was stopped during suspend. */ - if (!state.enabled) { - ret = clk_prepare_enable(chip->hlcdc->periph_clk); + if (!pwm->state.enabled) { + ret = clk_prepare_enable(atmel->hlcdc->periph_clk); if (ret) return ret; } - return atmel_hlcdc_pwm_apply(&chip->chip, &chip->chip.pwms[0], &state); + return atmel_hlcdc_pwm_apply(chip, pwm, &pwm->state); } -#endif -static SIMPLE_DEV_PM_OPS(atmel_hlcdc_pwm_pm_ops, - atmel_hlcdc_pwm_suspend, atmel_hlcdc_pwm_resume); +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[] = { { @@ -236,7 +234,7 @@ static const struct of_device_id atmel_hlcdc_dt_ids[] = { .data = &atmel_hlcdc_pwm_sama5d3_errata, }, { .compatible = "microchip,sam9x60-hlcdc", }, - { /* sentinel */ }, + { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, atmel_hlcdc_dt_ids); @@ -244,15 +242,17 @@ static int atmel_hlcdc_pwm_probe(struct platform_device *pdev) { const struct of_device_id *match; struct device *dev = &pdev->dev; - struct atmel_hlcdc_pwm *chip; + struct pwm_chip *chip; + struct atmel_hlcdc_pwm *atmel; struct atmel_hlcdc *hlcdc; int ret; hlcdc = dev_get_drvdata(dev->parent); - chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL); - if (!chip) - return -ENOMEM; + 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) @@ -260,17 +260,12 @@ static int atmel_hlcdc_pwm_probe(struct platform_device *pdev) match = of_match_node(atmel_hlcdc_dt_ids, dev->parent->of_node); if (match) - chip->errata = match->data; + atmel->errata = match->data; - chip->hlcdc = hlcdc; - chip->chip.ops = &atmel_hlcdc_pwm_ops; - chip->chip.dev = dev; - chip->chip.base = -1; - chip->chip.npwm = 1; - chip->chip.of_xlate = of_pwm_xlate_with_flags; - chip->chip.of_pwm_n_cells = 3; + atmel->hlcdc = hlcdc; + chip->ops = &atmel_hlcdc_pwm_ops; - ret = pwmchip_add_with_polarity(&chip->chip, PWM_POLARITY_INVERSED); + ret = pwmchip_add(chip); if (ret) { clk_disable_unprepare(hlcdc->periph_clk); return ret; @@ -281,30 +276,27 @@ static int atmel_hlcdc_pwm_probe(struct platform_device *pdev) return 0; } -static int atmel_hlcdc_pwm_remove(struct platform_device *pdev) +static void atmel_hlcdc_pwm_remove(struct platform_device *pdev) { - struct atmel_hlcdc_pwm *chip = platform_get_drvdata(pdev); - int ret; - - ret = pwmchip_remove(&chip->chip); - if (ret) - return ret; + struct pwm_chip *chip = platform_get_drvdata(pdev); + struct atmel_hlcdc_pwm *atmel = to_atmel_hlcdc_pwm(chip); - clk_disable_unprepare(chip->hlcdc->periph_clk); + pwmchip_remove(chip); - return 0; + clk_disable_unprepare(atmel->hlcdc->periph_clk); } static const struct of_device_id atmel_hlcdc_pwm_dt_ids[] = { { .compatible = "atmel,hlcdc-pwm" }, - { /* sentinel */ }, + { /* 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 = &atmel_hlcdc_pwm_pm_ops, + .pm = pm_ptr(&atmel_hlcdc_pwm_pm_ops), }, .probe = atmel_hlcdc_pwm_probe, .remove = atmel_hlcdc_pwm_remove, diff --git a/drivers/pwm/pwm-atmel-tcb.c b/drivers/pwm/pwm-atmel-tcb.c index 85c53701958c..f9ff78ba122d 100644 --- a/drivers/pwm/pwm-atmel-tcb.c +++ b/drivers/pwm/pwm-atmel-tcb.c @@ -16,13 +16,15 @@ #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_device.h> +#include <linux/of.h> +#include <linux/regmap.h> #include <linux/slab.h> #include <soc/at91/atmel_tcb.h> -#define NPWM 6 +#define NPWM 2 #define ATMEL_TC_ACMR_MASK (ATMEL_TC_ACPA | ATMEL_TC_ACPC | \ ATMEL_TC_AEEVT | ATMEL_TC_ASWTRG) @@ -31,7 +33,6 @@ ATMEL_TC_BEEVT | ATMEL_TC_BSWTRG) struct atmel_tcb_pwm_device { - enum pwm_polarity polarity; /* PWM polarity */ unsigned div; /* PWM clock divider */ unsigned duty; /* PWM duty expressed in clk cycles */ unsigned period; /* PWM period expressed in clk cycles */ @@ -46,83 +47,67 @@ struct atmel_tcb_channel { }; struct atmel_tcb_pwm_chip { - struct pwm_chip chip; spinlock_t lock; - struct atmel_tc *tc; - struct atmel_tcb_pwm_device *pwms[NPWM]; - struct atmel_tcb_channel bkup[NPWM / 2]; + u8 channel; + u8 width; + 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 inline struct atmel_tcb_pwm_chip *to_tcb_chip(struct pwm_chip *chip) -{ - return container_of(chip, struct atmel_tcb_pwm_chip, chip); -} +static const u8 atmel_tcb_divisors[] = { 2, 8, 32, 128, 0, }; -static int atmel_tcb_pwm_set_polarity(struct pwm_chip *chip, - struct pwm_device *pwm, - enum pwm_polarity polarity) +static inline struct atmel_tcb_pwm_chip *to_tcb_chip(struct pwm_chip *chip) { - struct atmel_tcb_pwm_device *tcbpwm = pwm_get_chip_data(pwm); - - tcbpwm->polarity = polarity; - - return 0; + 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; - struct atmel_tc *tc = tcbpwmc->tc; - void __iomem *regs = tc->regs; - unsigned group = pwm->hwpwm / 2; - unsigned index = pwm->hwpwm % 2; + struct atmel_tcb_pwm_device *tcbpwm = &tcbpwmc->pwms[pwm->hwpwm]; unsigned cmr; int ret; - tcbpwm = devm_kzalloc(chip->dev, sizeof(*tcbpwm), GFP_KERNEL); - if (!tcbpwm) - return -ENOMEM; - - ret = clk_prepare_enable(tc->clk[group]); - if (ret) { - devm_kfree(chip->dev, tcbpwm); + ret = clk_prepare_enable(tcbpwmc->clk); + if (ret) return ret; - } - pwm_set_chip_data(pwm, tcbpwm); - tcbpwm->polarity = PWM_POLARITY_NORMAL; tcbpwm->duty = 0; tcbpwm->period = 0; tcbpwm->div = 0; - spin_lock(&tcbpwmc->lock); - cmr = __raw_readl(regs + ATMEL_TC_REG(group, CMR)); + 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 (index == 0) - tcbpwm->duty = - __raw_readl(regs + ATMEL_TC_REG(group, RA)); + if (pwm->hwpwm == 0) + regmap_read(tcbpwmc->regmap, + ATMEL_TC_REG(tcbpwmc->channel, RA), + &tcbpwm->duty); else - tcbpwm->duty = - __raw_readl(regs + ATMEL_TC_REG(group, RB)); + regmap_read(tcbpwmc->regmap, + ATMEL_TC_REG(tcbpwmc->channel, RB), + &tcbpwm->duty); tcbpwm->div = cmr & ATMEL_TC_TCCLKS; - tcbpwm->period = __raw_readl(regs + ATMEL_TC_REG(group, RC)); + 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; - __raw_writel(cmr, regs + ATMEL_TC_REG(group, CMR)); - spin_unlock(&tcbpwmc->lock); - - tcbpwmc->pwms[pwm->hwpwm] = tcbpwm; + regmap_write(tcbpwmc->regmap, ATMEL_TC_REG(tcbpwmc->channel, CMR), cmr); return 0; } @@ -130,24 +115,16 @@ static int atmel_tcb_pwm_request(struct pwm_chip *chip, static void atmel_tcb_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm) { struct atmel_tcb_pwm_chip *tcbpwmc = to_tcb_chip(chip); - struct atmel_tcb_pwm_device *tcbpwm = pwm_get_chip_data(pwm); - struct atmel_tc *tc = tcbpwmc->tc; - clk_disable_unprepare(tc->clk[pwm->hwpwm / 2]); - tcbpwmc->pwms[pwm->hwpwm] = NULL; - devm_kfree(chip->dev, tcbpwm); + clk_disable_unprepare(tcbpwmc->clk); } -static void atmel_tcb_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) +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 = pwm_get_chip_data(pwm); - struct atmel_tc *tc = tcbpwmc->tc; - void __iomem *regs = tc->regs; - unsigned group = pwm->hwpwm / 2; - unsigned index = pwm->hwpwm % 2; + struct atmel_tcb_pwm_device *tcbpwm = &tcbpwmc->pwms[pwm->hwpwm]; unsigned cmr; - enum pwm_polarity polarity = tcbpwm->polarity; /* * If duty is 0 the timer will be stopped and we have to @@ -160,11 +137,10 @@ static void atmel_tcb_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) if (tcbpwm->duty == 0) polarity = !polarity; - spin_lock(&tcbpwmc->lock); - cmr = __raw_readl(regs + ATMEL_TC_REG(group, CMR)); + regmap_read(tcbpwmc->regmap, ATMEL_TC_REG(tcbpwmc->channel, CMR), &cmr); /* flush old setting and set the new one */ - if (index == 0) { + if (pwm->hwpwm == 0) { cmr &= ~ATMEL_TC_ACMR_MASK; if (polarity == PWM_POLARITY_INVERSED) cmr |= ATMEL_TC_ASWTRG_CLEAR; @@ -178,35 +154,31 @@ static void atmel_tcb_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) cmr |= ATMEL_TC_BSWTRG_SET; } - __raw_writel(cmr, regs + ATMEL_TC_REG(group, CMR)); + 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))) { - __raw_writel(ATMEL_TC_SWTRG | ATMEL_TC_CLKDIS, - regs + ATMEL_TC_REG(group, CCR)); - tcbpwmc->bkup[group].enabled = 1; + regmap_write(tcbpwmc->regmap, + ATMEL_TC_REG(tcbpwmc->channel, CCR), + ATMEL_TC_SWTRG | ATMEL_TC_CLKDIS); + tcbpwmc->bkup.enabled = 1; } else { - __raw_writel(ATMEL_TC_SWTRG, regs + - ATMEL_TC_REG(group, CCR)); - tcbpwmc->bkup[group].enabled = 0; + regmap_write(tcbpwmc->regmap, + ATMEL_TC_REG(tcbpwmc->channel, CCR), + ATMEL_TC_SWTRG); + tcbpwmc->bkup.enabled = 0; } - - spin_unlock(&tcbpwmc->lock); } -static int atmel_tcb_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) +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 = pwm_get_chip_data(pwm); - struct atmel_tc *tc = tcbpwmc->tc; - void __iomem *regs = tc->regs; - unsigned group = pwm->hwpwm / 2; - unsigned index = pwm->hwpwm % 2; + struct atmel_tcb_pwm_device *tcbpwm = &tcbpwmc->pwms[pwm->hwpwm]; u32 cmr; - enum pwm_polarity polarity = tcbpwm->polarity; /* * If duty is 0 the timer will be stopped and we have to @@ -219,13 +191,12 @@ static int atmel_tcb_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) if (tcbpwm->duty == 0) polarity = !polarity; - spin_lock(&tcbpwmc->lock); - cmr = __raw_readl(regs + ATMEL_TC_REG(group, CMR)); + regmap_read(tcbpwmc->regmap, ATMEL_TC_REG(tcbpwmc->channel, CMR), &cmr); /* flush old setting and set the new one */ cmr &= ~ATMEL_TC_TCCLKS; - if (index == 0) { + if (pwm->hwpwm == 0) { cmr &= ~ATMEL_TC_ACMR_MASK; /* Set CMR flags according to given polarity */ @@ -248,7 +219,7 @@ static int atmel_tcb_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) * this config till next config call. */ if (tcbpwm->duty != tcbpwm->period && tcbpwm->duty > 0) { - if (index == 0) { + if (pwm->hwpwm == 0) { if (polarity == PWM_POLARITY_INVERSED) cmr |= ATMEL_TC_ACPA_SET | ATMEL_TC_ACPC_CLEAR; else @@ -263,20 +234,24 @@ static int atmel_tcb_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) cmr |= (tcbpwm->div & ATMEL_TC_TCCLKS); - __raw_writel(cmr, regs + ATMEL_TC_REG(group, CMR)); + regmap_write(tcbpwmc->regmap, ATMEL_TC_REG(tcbpwmc->channel, CMR), cmr); - if (index == 0) - __raw_writel(tcbpwm->duty, regs + ATMEL_TC_REG(group, RA)); + if (pwm->hwpwm == 0) + regmap_write(tcbpwmc->regmap, + ATMEL_TC_REG(tcbpwmc->channel, RA), + tcbpwm->duty); else - __raw_writel(tcbpwm->duty, regs + ATMEL_TC_REG(group, RB)); + regmap_write(tcbpwmc->regmap, + ATMEL_TC_REG(tcbpwmc->channel, RB), + tcbpwm->duty); - __raw_writel(tcbpwm->period, regs + ATMEL_TC_REG(group, RC)); + regmap_write(tcbpwmc->regmap, ATMEL_TC_REG(tcbpwmc->channel, RC), + tcbpwm->period); /* Use software trigger to apply the new setting */ - __raw_writel(ATMEL_TC_CLKEN | ATMEL_TC_SWTRG, - regs + ATMEL_TC_REG(group, CCR)); - tcbpwmc->bkup[group].enabled = 1; - spin_unlock(&tcbpwmc->lock); + regmap_write(tcbpwmc->regmap, ATMEL_TC_REG(tcbpwmc->channel, CCR), + ATMEL_TC_SWTRG | ATMEL_TC_CLKEN); + tcbpwmc->bkup.enabled = 1; return 0; } @@ -284,30 +259,31 @@ 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 = pwm_get_chip_data(pwm); - unsigned group = pwm->hwpwm / 2; - unsigned index = pwm->hwpwm % 2; - struct atmel_tcb_pwm_device *atcbpwm = NULL; - struct atmel_tc *tc = tcbpwmc->tc; - int i; + 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 rate = clk_get_rate(tc->clk[group]); + unsigned rate = clk_get_rate(tcbpwmc->clk); 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 */ - for (i = 0; i < 5; ++i) { - if (atmel_tc_divisors[i] == 0) { + 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_tc_divisors[i], rate); - max = min << tc->tcb_config->counter_width; + min = div_u64((u64)NSEC_PER_SEC * atmel_tcb_divisors[i], rate); + max = min << tcbpwmc->width; if (max >= period_ns) break; } @@ -316,11 +292,11 @@ static int atmel_tcb_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, * If none of the divisor are small enough to represent period_ns * take slow clock (32KHz). */ - if (i == 5) { + if (i == ARRAY_SIZE(atmel_tcb_divisors)) { i = slowclk; - rate = clk_get_rate(tc->slow_clk); + rate = clk_get_rate(tcbpwmc->slow_clk); min = div_u64(NSEC_PER_SEC, rate); - max = min << tc->tcb_config->counter_width; + max = min << tcbpwmc->width; /* If period is too big return ERANGE error */ if (max < period_ns) @@ -330,27 +306,17 @@ static int atmel_tcb_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, duty = div_u64(duty_ns, min); period = div_u64(period_ns, min); - if (index == 0) - atcbpwm = tcbpwmc->pwms[pwm->hwpwm + 1]; - else - atcbpwm = tcbpwmc->pwms[pwm->hwpwm - 1]; - /* - * PWM devices provided by TCB driver are grouped by 2: - * - group 0: PWM 0 & 1 - * - group 1: PWM 2 & 3 - * - group 2: PWM 4 & 5 - * + * 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 && atcbpwm->duty > 0 && - atcbpwm->duty != atcbpwm->period) && + if ((atcbpwm->duty > 0 && atcbpwm->duty != atcbpwm->period) && (atcbpwm->div != i || atcbpwm->period != period)) { - dev_err(chip->dev, + dev_err(pwmchip_parent(chip), "failed to configure period_ns: PWM group already configured with a different value\n"); return -EINVAL; } @@ -359,96 +325,155 @@ static int atmel_tcb_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, tcbpwm->div = i; tcbpwm->duty = duty; - /* If the PWM is enabled, call enable to apply the new conf */ - if (pwm_is_enabled(pwm)) - atmel_tcb_pwm_enable(chip, pwm); - 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, - .config = atmel_tcb_pwm_config, - .set_polarity = atmel_tcb_pwm_set_polarity, - .enable = atmel_tcb_pwm_enable, - .disable = atmel_tcb_pwm_disable, - .owner = THIS_MODULE, + .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 atmel_tcb_pwm_chip *tcbpwm; + 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; - struct atmel_tc *tc; + char clk_name[] = "t0_clk"; int err; - int tcblock; + 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, "tc-block", &tcblock); + err = of_property_read_u32(np, "reg", &channel); if (err < 0) { dev_err(&pdev->dev, - "failed to get Timer Counter Block number from device tree (error: %d)\n", + "failed to get Timer Counter Block channel from device tree (error: %d)\n", err); return err; } - tc = atmel_tc_alloc(tcblock); - if (tc == NULL) { - dev_err(&pdev->dev, "failed to allocate Timer Counter Block\n"); - return -ENOMEM; + 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; } - tcbpwm = devm_kzalloc(&pdev->dev, sizeof(*tcbpwm), GFP_KERNEL); - if (tcbpwm == NULL) { - err = -ENOMEM; - goto err_free_tc; + 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; + } } - tcbpwm->chip.dev = &pdev->dev; - tcbpwm->chip.ops = &atmel_tcb_pwm_ops; - tcbpwm->chip.of_xlate = of_pwm_xlate_with_flags; - tcbpwm->chip.of_pwm_n_cells = 3; - tcbpwm->chip.base = -1; - tcbpwm->chip.npwm = NPWM; - tcbpwm->tc = tc; + chip->ops = &atmel_tcb_pwm_ops; + tcbpwmc->channel = channel; + tcbpwmc->width = config->counter_width; - err = clk_prepare_enable(tc->slow_clk); + err = clk_prepare_enable(tcbpwmc->slow_clk); if (err) - goto err_free_tc; + goto err_gclk; - spin_lock_init(&tcbpwm->lock); + spin_lock_init(&tcbpwmc->lock); - err = pwmchip_add(&tcbpwm->chip); + err = pwmchip_add(chip); if (err < 0) goto err_disable_clk; - platform_set_drvdata(pdev, tcbpwm); + platform_set_drvdata(pdev, chip); return 0; err_disable_clk: - clk_disable_unprepare(tcbpwm->tc->slow_clk); + clk_disable_unprepare(tcbpwmc->slow_clk); -err_free_tc: - atmel_tc_free(tc); +err_gclk: + clk_put(tcbpwmc->gclk); + +err_clk: + clk_put(tcbpwmc->clk); + +err_slow_clk: + clk_put(tcbpwmc->slow_clk); return err; } -static int atmel_tcb_pwm_remove(struct platform_device *pdev) +static void atmel_tcb_pwm_remove(struct platform_device *pdev) { - struct atmel_tcb_pwm_chip *tcbpwm = platform_get_drvdata(pdev); - int err; - - clk_disable_unprepare(tcbpwm->tc->slow_clk); - - err = pwmchip_remove(&tcbpwm->chip); - if (err < 0) - return err; + struct pwm_chip *chip = platform_get_drvdata(pdev); + struct atmel_tcb_pwm_chip *tcbpwmc = to_tcb_chip(chip); - atmel_tc_free(tcbpwm->tc); + pwmchip_remove(chip); - return 0; + 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[] = { @@ -457,54 +482,49 @@ static const struct of_device_id atmel_tcb_pwm_dt_ids[] = { }; MODULE_DEVICE_TABLE(of, atmel_tcb_pwm_dt_ids); -#ifdef CONFIG_PM_SLEEP static int atmel_tcb_pwm_suspend(struct device *dev) { - struct atmel_tcb_pwm_chip *tcbpwm = dev_get_drvdata(dev); - void __iomem *base = tcbpwm->tc->regs; - int i; + 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; - for (i = 0; i < (NPWM / 2); i++) { - struct atmel_tcb_channel *chan = &tcbpwm->bkup[i]; + 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); - chan->cmr = readl(base + ATMEL_TC_REG(i, CMR)); - chan->ra = readl(base + ATMEL_TC_REG(i, RA)); - chan->rb = readl(base + ATMEL_TC_REG(i, RB)); - chan->rc = readl(base + ATMEL_TC_REG(i, RC)); - } return 0; } static int atmel_tcb_pwm_resume(struct device *dev) { - struct atmel_tcb_pwm_chip *tcbpwm = dev_get_drvdata(dev); - void __iomem *base = tcbpwm->tc->regs; - int i; - - for (i = 0; i < (NPWM / 2); i++) { - struct atmel_tcb_channel *chan = &tcbpwm->bkup[i]; - - writel(chan->cmr, base + ATMEL_TC_REG(i, CMR)); - writel(chan->ra, base + ATMEL_TC_REG(i, RA)); - writel(chan->rb, base + ATMEL_TC_REG(i, RB)); - writel(chan->rc, base + ATMEL_TC_REG(i, RC)); - if (chan->enabled) { - writel(ATMEL_TC_CLKEN | ATMEL_TC_SWTRG, - base + ATMEL_TC_REG(i, CCR)); - } - } + 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; } -#endif -static SIMPLE_DEV_PM_OPS(atmel_tcb_pwm_pm_ops, atmel_tcb_pwm_suspend, - atmel_tcb_pwm_resume); +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 = &atmel_tcb_pwm_pm_ops, + .pm = pm_ptr(&atmel_tcb_pwm_pm_ops), }, .probe = atmel_tcb_pwm_probe, .remove = atmel_tcb_pwm_remove, diff --git a/drivers/pwm/pwm-atmel.c b/drivers/pwm/pwm-atmel.c index 6161e7e3e9ac..06d22d0f7b26 100644 --- a/drivers/pwm/pwm-atmel.c +++ b/drivers/pwm/pwm-atmel.c @@ -6,7 +6,7 @@ * Bo Shen <voice.shen@atmel.com> * * Links to reference manuals for the supported PWM chips can be found in - * Documentation/arm/microchip.rst. + * Documentation/arch/arm/microchip.rst. * * Limitations: * - Periods start with the inactive level. @@ -24,9 +24,7 @@ #include <linux/err.h> #include <linux/io.h> #include <linux/module.h> -#include <linux/mutex.h> #include <linux/of.h> -#include <linux/of_device.h> #include <linux/platform_device.h> #include <linux/pwm.h> #include <linux/slab.h> @@ -37,7 +35,7 @@ #define PWM_SR 0x0C #define PWM_ISR 0x1C /* Bit field in SR */ -#define PWM_SR_ALL_CH_ON 0x0F +#define PWM_SR_ALL_CH_MASK 0x0F /* The following register is PWM channel related registers */ #define PWM_CH_REG_OFFSET 0x200 @@ -79,19 +77,25 @@ struct atmel_pwm_data { }; struct atmel_pwm_chip { - struct pwm_chip chip; struct clk *clk; void __iomem *base; const struct atmel_pwm_data *data; - unsigned int updated_pwms; - /* ISR is cleared when read, ensure only one thread does that */ - struct mutex isr_lock; + /* + * 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 container_of(chip, struct atmel_pwm_chip, chip); + return pwmchip_get_drvdata(chip); } static inline u32 atmel_pwm_readl(struct atmel_pwm_chip *chip, @@ -123,7 +127,58 @@ static inline void atmel_pwm_ch_writel(struct atmel_pwm_chip *chip, 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) { @@ -132,7 +187,7 @@ static int atmel_pwm_calculate_cprd_and_pres(struct pwm_chip *chip, int shift; /* Calculate the period cycles and prescale value */ - cycles *= clk_get_rate(atmel_pwm->clk); + cycles *= clkrate; do_div(cycles, NSEC_PER_SEC); /* @@ -143,7 +198,7 @@ static int atmel_pwm_calculate_cprd_and_pres(struct pwm_chip *chip, shift = fls(cycles) - atmel_pwm->data->cfg.period_bits; if (shift > PWM_MAX_PRES) { - dev_err(chip->dev, "pres exceeds the maximum value\n"); + dev_err(pwmchip_parent(chip), "pres exceeds the maximum value\n"); return -EINVAL; } else if (shift > 0) { *pres = shift; @@ -158,12 +213,14 @@ static int atmel_pwm_calculate_cprd_and_pres(struct pwm_chip *chip, } static void atmel_pwm_calculate_cdty(const struct pwm_state *state, - unsigned long cprd, unsigned long *cdty) + unsigned long clkrate, unsigned long cprd, + u32 pres, unsigned long *cdty) { unsigned long long cycles = state->duty_cycle; - cycles *= cprd; - do_div(cycles, state->period); + cycles *= clkrate; + do_div(cycles, NSEC_PER_SEC); + cycles >>= pres; *cdty = cprd - cycles; } @@ -182,6 +239,7 @@ static void atmel_pwm_update_cdty(struct pwm_chip *chip, struct pwm_device *pwm, 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, @@ -200,22 +258,10 @@ 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 = jiffies + 2 * HZ; + unsigned long timeout; - /* - * Wait for at least a complete period to have passed before disabling a - * channel to be sure that CDTY has been updated - */ - mutex_lock(&atmel_pwm->isr_lock); - atmel_pwm->updated_pwms |= atmel_pwm_readl(atmel_pwm, PWM_ISR); - - while (!(atmel_pwm->updated_pwms & (1 << pwm->hwpwm)) && - time_before(jiffies, timeout)) { - usleep_range(10, 100); - atmel_pwm->updated_pwms |= atmel_pwm_readl(atmel_pwm, PWM_ISR); - } + atmel_pwm_wait_nonpending(atmel_pwm, pwm->hwpwm); - mutex_unlock(&atmel_pwm->isr_lock); atmel_pwm_writel(atmel_pwm, PWM_DIS, 1 << pwm->hwpwm); /* @@ -236,40 +282,43 @@ 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); - struct pwm_state cstate; unsigned long cprd, cdty; u32 pres, val; int ret; - pwm_get_state(pwm, &cstate); - if (state->enabled) { - if (cstate.enabled && - cstate.polarity == state->polarity && - cstate.period == state->period) { + 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); - atmel_pwm_calculate_cdty(state, cprd, &cdty); + 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, state, &cprd, + ret = atmel_pwm_calculate_cprd_and_pres(chip, clkrate, state, &cprd, &pres); if (ret) { - dev_err(chip->dev, + dev_err(pwmchip_parent(chip), "failed to calculate cprd and prescaler\n"); return ret; } - atmel_pwm_calculate_cdty(state, cprd, &cdty); + atmel_pwm_calculate_cdty(state, clkrate, cprd, pres, &cdty); - if (cstate.enabled) { + if (pwm->state.enabled) { atmel_pwm_disable(chip, pwm, false); } else { ret = clk_enable(atmel_pwm->clk); if (ret) { - dev_err(chip->dev, "failed to enable clock\n"); + dev_err(pwmchip_parent(chip), "failed to enable clock\n"); return ret; } } @@ -283,20 +332,16 @@ static int atmel_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, val |= PWM_CMR_CPOL; atmel_pwm_ch_writel(atmel_pwm, pwm->hwpwm, PWM_CMR, val); atmel_pwm_set_cprd_cdty(chip, pwm, cprd, cdty); - mutex_lock(&atmel_pwm->isr_lock); - atmel_pwm->updated_pwms |= atmel_pwm_readl(atmel_pwm, PWM_ISR); - atmel_pwm->updated_pwms &= ~(1 << pwm->hwpwm); - mutex_unlock(&atmel_pwm->isr_lock); atmel_pwm_writel(atmel_pwm, PWM_ENA, 1 << pwm->hwpwm); - } else if (cstate.enabled) { + } else if (pwm->state.enabled) { atmel_pwm_disable(chip, pwm, true); } return 0; } -static void atmel_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm, - struct pwm_state *state) +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; @@ -317,9 +362,12 @@ static void atmel_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm, 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)cdty * NSEC_PER_SEC; + tmp = (u64)(cprd - cdty) * NSEC_PER_SEC; tmp <<= pres; state->duty_cycle = DIV64_U64_ROUND_UP(tmp, rate); @@ -332,12 +380,13 @@ static void atmel_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm, 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, - .owner = THIS_MODULE, }; static const struct atmel_pwm_data atmel_sam9rl_pwm_data = { @@ -398,74 +447,93 @@ static const struct of_device_id atmel_pwm_dt_ids[] = { }; 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 resource *res; + struct pwm_chip *chip; int ret; - atmel_pwm = devm_kzalloc(&pdev->dev, sizeof(*atmel_pwm), GFP_KERNEL); - if (!atmel_pwm) - return -ENOMEM; + chip = devm_pwmchip_alloc(&pdev->dev, 4, sizeof(*atmel_pwm)); + if (IS_ERR(chip)) + return PTR_ERR(chip); - mutex_init(&atmel_pwm->isr_lock); + atmel_pwm = to_atmel_pwm_chip(chip); atmel_pwm->data = of_device_get_match_data(&pdev->dev); - atmel_pwm->updated_pwms = 0; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - atmel_pwm->base = devm_ioremap_resource(&pdev->dev, res); + 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(&pdev->dev, NULL); + atmel_pwm->clk = devm_clk_get_prepared(&pdev->dev, NULL); if (IS_ERR(atmel_pwm->clk)) - return PTR_ERR(atmel_pwm->clk); + return dev_err_probe(&pdev->dev, PTR_ERR(atmel_pwm->clk), + "failed to get prepared PWM clock\n"); - ret = clk_prepare(atmel_pwm->clk); - if (ret) { - dev_err(&pdev->dev, "failed to prepare PWM clock\n"); - return ret; - } + chip->ops = &atmel_pwm_ops; - atmel_pwm->chip.dev = &pdev->dev; - atmel_pwm->chip.ops = &atmel_pwm_ops; - atmel_pwm->chip.of_xlate = of_pwm_xlate_with_flags; - atmel_pwm->chip.of_pwm_n_cells = 3; - atmel_pwm->chip.base = -1; - atmel_pwm->chip.npwm = 4; + ret = atmel_pwm_enable_clk_if_on(chip, true); + if (ret < 0) + return ret; - ret = pwmchip_add(&atmel_pwm->chip); + ret = devm_pwmchip_add(&pdev->dev, chip); if (ret < 0) { - dev_err(&pdev->dev, "failed to add PWM chip %d\n", ret); - goto unprepare_clk; + dev_err_probe(&pdev->dev, ret, "failed to add PWM chip\n"); + goto disable_clk; } - platform_set_drvdata(pdev, atmel_pwm); + return 0; - return ret; +disable_clk: + atmel_pwm_enable_clk_if_on(chip, false); -unprepare_clk: - clk_unprepare(atmel_pwm->clk); return ret; } -static int atmel_pwm_remove(struct platform_device *pdev) -{ - struct atmel_pwm_chip *atmel_pwm = platform_get_drvdata(pdev); - - clk_unprepare(atmel_pwm->clk); - mutex_destroy(&atmel_pwm->isr_lock); - - return pwmchip_remove(&atmel_pwm->chip); -} - static struct platform_driver atmel_pwm_driver = { .driver = { .name = "atmel-pwm", - .of_match_table = of_match_ptr(atmel_pwm_dt_ids), + .of_match_table = atmel_pwm_dt_ids, }, .probe = atmel_pwm_probe, - .remove = atmel_pwm_remove, }; module_platform_driver(atmel_pwm_driver); diff --git a/drivers/pwm/pwm-axi-pwmgen.c b/drivers/pwm/pwm-axi-pwmgen.c new file mode 100644 index 000000000000..b40522f01002 --- /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 index 1f829edd8ee7..f4c9f10e490e 100644 --- a/drivers/pwm/pwm-bcm-iproc.c +++ b/drivers/pwm/pwm-bcm-iproc.c @@ -1,15 +1,5 @@ -/* - * Copyright (C) 2016 Broadcom - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (C) 2016 Broadcom #include <linux/clk.h> #include <linux/delay.h> @@ -44,14 +34,13 @@ #define IPROC_PWM_PRESCALE_MAX 0x3f struct iproc_pwmc { - struct pwm_chip chip; void __iomem *base; struct clk *clk; }; static inline struct iproc_pwmc *to_iproc_pwmc(struct pwm_chip *chip) { - return container_of(chip, struct iproc_pwmc, chip); + return pwmchip_get_drvdata(chip); } static void iproc_pwmc_enable(struct iproc_pwmc *ip, unsigned int channel) @@ -78,15 +67,13 @@ static void iproc_pwmc_disable(struct iproc_pwmc *ip, unsigned int channel) ndelay(400); } -static void iproc_pwmc_get_state(struct pwm_chip *chip, struct pwm_device *pwm, - struct pwm_state *state) +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; - rate = clk_get_rate(ip->clk); - value = readl(ip->base + IPROC_PWM_CTRL_OFFSET); if (value & BIT(IPROC_PWM_CTRL_EN_SHIFT(pwm->hwpwm))) @@ -99,6 +86,13 @@ static void iproc_pwmc_get_state(struct pwm_chip *chip, struct pwm_device *pwm, 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; @@ -112,6 +106,8 @@ static void iproc_pwmc_get_state(struct pwm_chip *chip, struct pwm_device *pwm, 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, @@ -143,8 +139,7 @@ static int iproc_pwmc_apply(struct pwm_chip *chip, struct pwm_device *pwm, value = rate * state->duty_cycle; duty = div64_u64(value, div); - if (period < IPROC_PWM_PERIOD_MIN || - duty < IPROC_PWM_DUTY_CYCLE_MIN) + if (period < IPROC_PWM_PERIOD_MIN) return -EINVAL; if (period <= IPROC_PWM_PERIOD_MAX && @@ -187,74 +182,50 @@ static int iproc_pwmc_apply(struct pwm_chip *chip, struct pwm_device *pwm, static const struct pwm_ops iproc_pwm_ops = { .apply = iproc_pwmc_apply, .get_state = iproc_pwmc_get_state, - .owner = THIS_MODULE, }; static int iproc_pwmc_probe(struct platform_device *pdev) { + struct pwm_chip *chip; struct iproc_pwmc *ip; - struct resource *res; unsigned int i; u32 value; int ret; - ip = devm_kzalloc(&pdev->dev, sizeof(*ip), GFP_KERNEL); - if (!ip) - return -ENOMEM; + 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); - ip->chip.dev = &pdev->dev; - ip->chip.ops = &iproc_pwm_ops; - ip->chip.base = -1; - ip->chip.npwm = 4; - ip->chip.of_xlate = of_pwm_xlate_with_flags; - ip->chip.of_pwm_n_cells = 3; + chip->ops = &iproc_pwm_ops; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - ip->base = devm_ioremap_resource(&pdev->dev, res); + ip->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(ip->base)) return PTR_ERR(ip->base); - ip->clk = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(ip->clk)) { - dev_err(&pdev->dev, "failed to get clock: %ld\n", - PTR_ERR(ip->clk)); - return PTR_ERR(ip->clk); - } - - ret = clk_prepare_enable(ip->clk); - if (ret < 0) { - dev_err(&pdev->dev, "failed to enable clock: %d\n", ret); - return ret; - } + 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 < ip->chip.npwm; i++) { + 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 = pwmchip_add(&ip->chip); - if (ret < 0) { - dev_err(&pdev->dev, "failed to add PWM chip: %d\n", ret); - clk_disable_unprepare(ip->clk); - } - - return ret; -} - -static int iproc_pwmc_remove(struct platform_device *pdev) -{ - struct iproc_pwmc *ip = platform_get_drvdata(pdev); - - clk_disable_unprepare(ip->clk); + ret = devm_pwmchip_add(&pdev->dev, chip); + if (ret < 0) + return dev_err_probe(&pdev->dev, ret, + "failed to add PWM chip\n"); - return pwmchip_remove(&ip->chip); + return 0; } static const struct of_device_id bcm_iproc_pwmc_dt[] = { @@ -269,7 +240,6 @@ static struct platform_driver iproc_pwmc_driver = { .of_match_table = bcm_iproc_pwmc_dt, }, .probe = iproc_pwmc_probe, - .remove = iproc_pwmc_remove, }; module_platform_driver(iproc_pwmc_driver); diff --git a/drivers/pwm/pwm-bcm-kona.c b/drivers/pwm/pwm-bcm-kona.c index 81da91df2529..022c078aae84 100644 --- a/drivers/pwm/pwm-bcm-kona.c +++ b/drivers/pwm/pwm-bcm-kona.c @@ -1,15 +1,5 @@ -/* - * Copyright (C) 2014 Broadcom Corporation - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (C) 2014 Broadcom Corporation #include <linux/clk.h> #include <linux/delay.h> @@ -66,14 +56,13 @@ #define DUTY_CYCLE_HIGH_MAX 0x00ffffff struct kona_pwmc { - struct pwm_chip chip; void __iomem *base; struct clk *clk; }; -static inline struct kona_pwmc *to_kona_pwmc(struct pwm_chip *_chip) +static inline struct kona_pwmc *to_kona_pwmc(struct pwm_chip *chip) { - return container_of(_chip, struct kona_pwmc, chip); + return pwmchip_get_drvdata(chip); } /* @@ -109,10 +98,10 @@ static void kona_pwmc_apply_settings(struct kona_pwmc *kp, unsigned int chan) } static int kona_pwmc_config(struct pwm_chip *chip, struct pwm_device *pwm, - int duty_ns, int period_ns) + u64 duty_ns, u64 period_ns) { struct kona_pwmc *kp = to_kona_pwmc(chip); - u64 val, div, rate; + u64 div, rate; unsigned long prescale = PRESCALE_MIN, pc, dc; unsigned int value, chan = pwm->hwpwm; @@ -132,13 +121,11 @@ static int kona_pwmc_config(struct pwm_chip *chip, struct pwm_device *pwm, while (1) { div = 1000000000; div *= 1 + prescale; - val = rate * period_ns; - pc = div64_u64(val, div); - val = rate * duty_ns; - dc = div64_u64(val, div); + 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 || dc < DUTY_CYCLE_HIGH_MIN) + if (pc < PERIOD_COUNT_MIN) return -EINVAL; /* If pc and dc are in bounds, the calculation is done */ @@ -150,25 +137,18 @@ static int kona_pwmc_config(struct pwm_chip *chip, struct pwm_device *pwm, return -EINVAL; } - /* - * Don't apply settings if disabled. The period and duty cycle are - * always calculated above to ensure the new values are - * validated immediately instead of on enable. - */ - if (pwm_is_enabled(pwm)) { - kona_pwmc_prepare_for_settings(kp, chan); + 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); + 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(pc, kp->base + PERIOD_COUNT_OFFSET(chan)); - writel(dc, kp->base + DUTY_CYCLE_HIGH_OFFSET(chan)); + writel(dc, kp->base + DUTY_CYCLE_HIGH_OFFSET(chan)); - kona_pwmc_apply_settings(kp, chan); - } + kona_pwmc_apply_settings(kp, chan); return 0; } @@ -183,7 +163,7 @@ static int kona_pwmc_set_polarity(struct pwm_chip *chip, struct pwm_device *pwm, ret = clk_prepare_enable(kp->clk); if (ret < 0) { - dev_err(chip->dev, "failed to enable clock: %d\n", ret); + dev_err(pwmchip_parent(chip), "failed to enable clock: %d\n", ret); return ret; } @@ -212,14 +192,7 @@ static int kona_pwmc_enable(struct pwm_chip *chip, struct pwm_device *pwm) ret = clk_prepare_enable(kp->clk); if (ret < 0) { - dev_err(chip->dev, "failed to enable clock: %d\n", ret); - return ret; - } - - ret = kona_pwmc_config(chip, pwm, pwm_get_duty_cycle(pwm), - pwm_get_period(pwm)); - if (ret < 0) { - clk_disable_unprepare(kp->clk); + dev_err(pwmchip_parent(chip), "failed to enable clock: %d\n", ret); return ret; } @@ -248,37 +221,71 @@ static void kona_pwmc_disable(struct pwm_chip *chip, struct pwm_device *pwm) 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 = { - .config = kona_pwmc_config, - .set_polarity = kona_pwmc_set_polarity, - .enable = kona_pwmc_enable, - .disable = kona_pwmc_disable, - .owner = THIS_MODULE, + .apply = kona_pwmc_apply, }; static int kona_pwmc_probe(struct platform_device *pdev) { + struct pwm_chip *chip; struct kona_pwmc *kp; - struct resource *res; unsigned int chan; unsigned int value = 0; int ret = 0; - kp = devm_kzalloc(&pdev->dev, sizeof(*kp), GFP_KERNEL); - if (kp == NULL) - return -ENOMEM; - - platform_set_drvdata(pdev, kp); + chip = devm_pwmchip_alloc(&pdev->dev, 6, sizeof(*kp)); + if (IS_ERR(chip)) + return PTR_ERR(chip); + kp = to_kona_pwmc(chip); - kp->chip.dev = &pdev->dev; - kp->chip.ops = &kona_pwm_ops; - kp->chip.base = -1; - kp->chip.npwm = 6; - kp->chip.of_xlate = of_pwm_xlate_with_flags; - kp->chip.of_pwm_n_cells = 3; + chip->ops = &kona_pwm_ops; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - kp->base = devm_ioremap_resource(&pdev->dev, res); + kp->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(kp->base)) return PTR_ERR(kp->base); @@ -296,32 +303,20 @@ static int kona_pwmc_probe(struct platform_device *pdev) } /* Set push/pull for all channels */ - for (chan = 0; chan < kp->chip.npwm; chan++) + 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 = pwmchip_add_with_polarity(&kp->chip, PWM_POLARITY_INVERSED); + 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 int kona_pwmc_remove(struct platform_device *pdev) -{ - struct kona_pwmc *kp = platform_get_drvdata(pdev); - unsigned int chan; - - for (chan = 0; chan < kp->chip.npwm; chan++) - if (pwm_is_enabled(&kp->chip.pwms[chan])) - clk_disable_unprepare(kp->clk); - - return pwmchip_remove(&kp->chip); -} - static const struct of_device_id bcm_kona_pwmc_dt[] = { { .compatible = "brcm,kona-pwm" }, { }, @@ -334,7 +329,6 @@ static struct platform_driver kona_pwmc_driver = { .of_match_table = bcm_kona_pwmc_dt, }, .probe = kona_pwmc_probe, - .remove = kona_pwmc_remove, }; module_platform_driver(kona_pwmc_driver); diff --git a/drivers/pwm/pwm-bcm2835.c b/drivers/pwm/pwm-bcm2835.c index d78f86f8e462..532903da521f 100644 --- a/drivers/pwm/pwm-bcm2835.c +++ b/drivers/pwm/pwm-bcm2835.c @@ -24,175 +24,146 @@ #define PERIOD_MIN 0x2 struct bcm2835_pwm { - struct pwm_chip chip; - struct device *dev; void __iomem *base; struct clk *clk; + unsigned long rate; }; static inline struct bcm2835_pwm *to_bcm2835_pwm(struct pwm_chip *chip) { - return container_of(chip, struct bcm2835_pwm, chip); + return pwmchip_get_drvdata(chip); } -static int bcm2835_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm) +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); - u32 value; - - value = readl(pc->base + PWM_CONTROL); - value &= ~(PWM_CONTROL_MASK << PWM_CONTROL_SHIFT(pwm->hwpwm)); - value |= (PWM_MODE << PWM_CONTROL_SHIFT(pwm->hwpwm)); - writel(value, pc->base + PWM_CONTROL); - - return 0; -} - -static void bcm2835_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm) -{ - struct bcm2835_pwm *pc = to_bcm2835_pwm(chip); - u32 value; - - value = readl(pc->base + PWM_CONTROL); - value &= ~(PWM_CONTROL_MASK << PWM_CONTROL_SHIFT(pwm->hwpwm)); - writel(value, pc->base + PWM_CONTROL); -} -static int bcm2835_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, - int duty_ns, int period_ns) -{ struct bcm2835_pwm *pc = to_bcm2835_pwm(chip); - unsigned long rate = clk_get_rate(pc->clk); - unsigned long scaler; - u32 period; - - if (!rate) { - dev_err(pc->dev, "failed to get clock rate\n"); + 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; - } - scaler = DIV_ROUND_CLOSEST(NSEC_PER_SEC, rate); - period = DIV_ROUND_CLOSEST(period_ns, scaler); + /* set period */ + period_cycles = DIV_ROUND_CLOSEST_ULL(state->period * pc->rate, NSEC_PER_SEC); - if (period < PERIOD_MIN) + /* don't accept a period that is too small */ + if (period_cycles < PERIOD_MIN) return -EINVAL; - writel(DIV_ROUND_CLOSEST(duty_ns, scaler), - pc->base + DUTY(pwm->hwpwm)); - writel(period, pc->base + PERIOD(pwm->hwpwm)); - - return 0; -} + writel(period_cycles, pc->base + PERIOD(pwm->hwpwm)); -static int bcm2835_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) -{ - struct bcm2835_pwm *pc = to_bcm2835_pwm(chip); - u32 value; + /* set duty cycle */ + val = DIV_ROUND_CLOSEST_ULL(state->duty_cycle * pc->rate, NSEC_PER_SEC); + writel(val, pc->base + DUTY(pwm->hwpwm)); - value = readl(pc->base + PWM_CONTROL); - value |= PWM_ENABLE << PWM_CONTROL_SHIFT(pwm->hwpwm); - writel(value, pc->base + PWM_CONTROL); + /* set polarity */ + val = readl(pc->base + PWM_CONTROL); - return 0; -} + val &= ~(PWM_CONTROL_MASK << PWM_CONTROL_SHIFT(pwm->hwpwm)); + val |= PWM_MODE << PWM_CONTROL_SHIFT(pwm->hwpwm); -static void bcm2835_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) -{ - struct bcm2835_pwm *pc = to_bcm2835_pwm(chip); - u32 value; - - value = readl(pc->base + PWM_CONTROL); - value &= ~(PWM_ENABLE << PWM_CONTROL_SHIFT(pwm->hwpwm)); - writel(value, pc->base + PWM_CONTROL); -} - -static int bcm2835_set_polarity(struct pwm_chip *chip, struct pwm_device *pwm, - enum pwm_polarity polarity) -{ - struct bcm2835_pwm *pc = to_bcm2835_pwm(chip); - u32 value; - - value = readl(pc->base + PWM_CONTROL); + if (state->polarity == PWM_POLARITY_NORMAL) + val &= ~(PWM_POLARITY << PWM_CONTROL_SHIFT(pwm->hwpwm)); + else + val |= PWM_POLARITY << PWM_CONTROL_SHIFT(pwm->hwpwm); - if (polarity == PWM_POLARITY_NORMAL) - value &= ~(PWM_POLARITY << PWM_CONTROL_SHIFT(pwm->hwpwm)); + /* enable/disable */ + if (state->enabled) + val |= PWM_ENABLE << PWM_CONTROL_SHIFT(pwm->hwpwm); else - value |= PWM_POLARITY << PWM_CONTROL_SHIFT(pwm->hwpwm); + val &= ~(PWM_ENABLE << PWM_CONTROL_SHIFT(pwm->hwpwm)); - writel(value, pc->base + PWM_CONTROL); + writel(val, pc->base + PWM_CONTROL); return 0; } static const struct pwm_ops bcm2835_pwm_ops = { - .request = bcm2835_pwm_request, - .free = bcm2835_pwm_free, - .config = bcm2835_pwm_config, - .enable = bcm2835_pwm_enable, - .disable = bcm2835_pwm_disable, - .set_polarity = bcm2835_set_polarity, - .owner = THIS_MODULE, + .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; - struct resource *res; int ret; - pc = devm_kzalloc(&pdev->dev, sizeof(*pc), GFP_KERNEL); - if (!pc) - return -ENOMEM; + chip = devm_pwmchip_alloc(dev, 2, sizeof(*pc)); + if (IS_ERR(chip)) + return PTR_ERR(chip); + pc = to_bcm2835_pwm(chip); - pc->dev = &pdev->dev; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - pc->base = devm_ioremap_resource(&pdev->dev, res); + 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, NULL); - if (IS_ERR(pc->clk)) { - ret = PTR_ERR(pc->clk); - if (ret != -EPROBE_DEFER) - dev_err(&pdev->dev, "clock not found: %d\n", ret); - - return ret; - } + 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 = clk_prepare_enable(pc->clk); + ret = devm_clk_rate_exclusive_get(dev, pc->clk); if (ret) - return ret; + return dev_err_probe(dev, ret, + "fail to get exclusive rate\n"); - pc->chip.dev = &pdev->dev; - pc->chip.ops = &bcm2835_pwm_ops; - pc->chip.base = -1; - pc->chip.npwm = 2; - pc->chip.of_xlate = of_pwm_xlate_with_flags; - pc->chip.of_pwm_n_cells = 3; + 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 = pwmchip_add(&pc->chip); + ret = devm_pwmchip_add(dev, chip); if (ret < 0) - goto add_fail; + return dev_err_probe(dev, ret, "failed to add pwmchip\n"); return 0; - -add_fail: - clk_disable_unprepare(pc->clk); - return ret; } -static int bcm2835_pwm_remove(struct platform_device *pdev) +static int bcm2835_pwm_suspend(struct device *dev) { - struct bcm2835_pwm *pc = platform_get_drvdata(pdev); + struct bcm2835_pwm *pc = dev_get_drvdata(dev); clk_disable_unprepare(pc->clk); - return pwmchip_remove(&pc->chip); + 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 */ } @@ -203,9 +174,9 @@ 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, - .remove = bcm2835_pwm_remove, }; module_platform_driver(bcm2835_pwm_driver); diff --git a/drivers/pwm/pwm-berlin.c b/drivers/pwm/pwm-berlin.c index b91c477cc84b..858d36991374 100644 --- a/drivers/pwm/pwm-berlin.c +++ b/drivers/pwm/pwm-berlin.c @@ -13,6 +13,7 @@ #include <linux/clk.h> #include <linux/io.h> #include <linux/kernel.h> +#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/platform_device.h> #include <linux/pwm.h> @@ -38,6 +39,8 @@ #define BERLIN_PWM_TCNT 0xc #define BERLIN_PWM_MAX_TCNT 65535 +#define BERLIN_PWM_NUMPWMS 4 + struct berlin_pwm_channel { u32 enable; u32 ctrl; @@ -46,56 +49,38 @@ struct berlin_pwm_channel { }; struct berlin_pwm_chip { - struct pwm_chip 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 container_of(chip, struct berlin_pwm_chip, chip); + return pwmchip_get_drvdata(chip); } -static inline u32 berlin_pwm_readl(struct berlin_pwm_chip *chip, +static inline u32 berlin_pwm_readl(struct berlin_pwm_chip *bpc, unsigned int channel, unsigned long offset) { - return readl_relaxed(chip->base + channel * 0x10 + offset); + return readl_relaxed(bpc->base + channel * 0x10 + offset); } -static inline void berlin_pwm_writel(struct berlin_pwm_chip *chip, +static inline void berlin_pwm_writel(struct berlin_pwm_chip *bpc, unsigned int channel, u32 value, unsigned long offset) { - writel_relaxed(value, chip->base + channel * 0x10 + offset); -} - -static int berlin_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm) -{ - struct berlin_pwm_channel *channel; - - channel = kzalloc(sizeof(*channel), GFP_KERNEL); - if (!channel) - return -ENOMEM; - - return pwm_set_chip_data(pwm, channel); -} - -static void berlin_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm) -{ - struct berlin_pwm_channel *channel = pwm_get_chip_data(pwm); - - kfree(channel); + writel_relaxed(value, bpc->base + channel * 0x10 + offset); } -static int berlin_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm_dev, - int duty_ns, int period_ns) +static int berlin_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, + u64 duty_ns, u64 period_ns) { - struct berlin_pwm_chip *pwm = to_berlin_pwm_chip(chip); + struct berlin_pwm_chip *bpc = to_berlin_pwm_chip(chip); bool prescale_4096 = false; u32 value, duty, period; u64 cycles; - cycles = clk_get_rate(pwm->clk); + cycles = clk_get_rate(bpc->clk); cycles *= period_ns; do_div(cycles, NSEC_PER_SEC); @@ -112,69 +97,96 @@ static int berlin_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm_dev, do_div(cycles, period_ns); duty = cycles; - value = berlin_pwm_readl(pwm, pwm_dev->hwpwm, BERLIN_PWM_CONTROL); + 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(pwm, pwm_dev->hwpwm, value, BERLIN_PWM_CONTROL); + berlin_pwm_writel(bpc, pwm->hwpwm, value, BERLIN_PWM_CONTROL); - berlin_pwm_writel(pwm, pwm_dev->hwpwm, duty, BERLIN_PWM_DUTY); - berlin_pwm_writel(pwm, pwm_dev->hwpwm, period, BERLIN_PWM_TCNT); + 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_dev, + struct pwm_device *pwm, enum pwm_polarity polarity) { - struct berlin_pwm_chip *pwm = to_berlin_pwm_chip(chip); + struct berlin_pwm_chip *bpc = to_berlin_pwm_chip(chip); u32 value; - value = berlin_pwm_readl(pwm, pwm_dev->hwpwm, BERLIN_PWM_CONTROL); + 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(pwm, pwm_dev->hwpwm, value, BERLIN_PWM_CONTROL); + 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_dev) +static int berlin_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) { - struct berlin_pwm_chip *pwm = to_berlin_pwm_chip(chip); + struct berlin_pwm_chip *bpc = to_berlin_pwm_chip(chip); u32 value; - value = berlin_pwm_readl(pwm, pwm_dev->hwpwm, BERLIN_PWM_EN); + value = berlin_pwm_readl(bpc, pwm->hwpwm, BERLIN_PWM_EN); value |= BERLIN_PWM_ENABLE; - berlin_pwm_writel(pwm, pwm_dev->hwpwm, value, BERLIN_PWM_EN); + 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_dev) + struct pwm_device *pwm) { - struct berlin_pwm_chip *pwm = to_berlin_pwm_chip(chip); + struct berlin_pwm_chip *bpc = to_berlin_pwm_chip(chip); u32 value; - value = berlin_pwm_readl(pwm, pwm_dev->hwpwm, BERLIN_PWM_EN); + value = berlin_pwm_readl(bpc, pwm->hwpwm, BERLIN_PWM_EN); value &= ~BERLIN_PWM_ENABLE; - berlin_pwm_writel(pwm, pwm_dev->hwpwm, value, BERLIN_PWM_EN); + 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 = { - .request = berlin_pwm_request, - .free = berlin_pwm_free, - .config = berlin_pwm_config, - .set_polarity = berlin_pwm_set_polarity, - .enable = berlin_pwm_enable, - .disable = berlin_pwm_disable, - .owner = THIS_MODULE, + .apply = berlin_pwm_apply, }; static const struct of_device_id berlin_pwm_match[] = { @@ -185,118 +197,86 @@ MODULE_DEVICE_TABLE(of, berlin_pwm_match); static int berlin_pwm_probe(struct platform_device *pdev) { - struct berlin_pwm_chip *pwm; - struct resource *res; + struct pwm_chip *chip; + struct berlin_pwm_chip *bpc; int ret; - pwm = devm_kzalloc(&pdev->dev, sizeof(*pwm), GFP_KERNEL); - if (!pwm) - return -ENOMEM; + 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); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - pwm->base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(pwm->base)) - return PTR_ERR(pwm->base); + bpc->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(bpc->base)) + return PTR_ERR(bpc->base); - pwm->clk = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(pwm->clk)) - return PTR_ERR(pwm->clk); + bpc->clk = devm_clk_get_enabled(&pdev->dev, NULL); + if (IS_ERR(bpc->clk)) + return PTR_ERR(bpc->clk); - ret = clk_prepare_enable(pwm->clk); - if (ret) - return ret; + chip->ops = &berlin_pwm_ops; - pwm->chip.dev = &pdev->dev; - pwm->chip.ops = &berlin_pwm_ops; - pwm->chip.base = -1; - pwm->chip.npwm = 4; - pwm->chip.of_xlate = of_pwm_xlate_with_flags; - pwm->chip.of_pwm_n_cells = 3; - - ret = pwmchip_add(&pwm->chip); - if (ret < 0) { - dev_err(&pdev->dev, "failed to add PWM chip: %d\n", ret); - clk_disable_unprepare(pwm->clk); - return ret; - } + 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, pwm); + platform_set_drvdata(pdev, chip); return 0; } -static int berlin_pwm_remove(struct platform_device *pdev) -{ - struct berlin_pwm_chip *pwm = platform_get_drvdata(pdev); - int ret; - - ret = pwmchip_remove(&pwm->chip); - clk_disable_unprepare(pwm->clk); - - return ret; -} - -#ifdef CONFIG_PM_SLEEP static int berlin_pwm_suspend(struct device *dev) { - struct berlin_pwm_chip *pwm = dev_get_drvdata(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 < pwm->chip.npwm; i++) { - struct berlin_pwm_channel *channel; + for (i = 0; i < chip->npwm; i++) { + struct berlin_pwm_channel *channel = &bpc->channel[i]; - channel = pwm_get_chip_data(&pwm->chip.pwms[i]); - if (!channel) - continue; - - channel->enable = berlin_pwm_readl(pwm, i, BERLIN_PWM_ENABLE); - channel->ctrl = berlin_pwm_readl(pwm, i, BERLIN_PWM_CONTROL); - channel->duty = berlin_pwm_readl(pwm, i, BERLIN_PWM_DUTY); - channel->tcnt = berlin_pwm_readl(pwm, i, BERLIN_PWM_TCNT); + 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(pwm->clk); + clk_disable_unprepare(bpc->clk); return 0; } static int berlin_pwm_resume(struct device *dev) { - struct berlin_pwm_chip *pwm = dev_get_drvdata(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(pwm->clk); + ret = clk_prepare_enable(bpc->clk); if (ret) return ret; - for (i = 0; i < pwm->chip.npwm; i++) { - struct berlin_pwm_channel *channel; - - channel = pwm_get_chip_data(&pwm->chip.pwms[i]); - if (!channel) - continue; + for (i = 0; i < chip->npwm; i++) { + struct berlin_pwm_channel *channel = &bpc->channel[i]; - berlin_pwm_writel(pwm, i, channel->ctrl, BERLIN_PWM_CONTROL); - berlin_pwm_writel(pwm, i, channel->duty, BERLIN_PWM_DUTY); - berlin_pwm_writel(pwm, i, channel->tcnt, BERLIN_PWM_TCNT); - berlin_pwm_writel(pwm, i, channel->enable, BERLIN_PWM_ENABLE); + 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; } -#endif -static SIMPLE_DEV_PM_OPS(berlin_pwm_pm_ops, berlin_pwm_suspend, - berlin_pwm_resume); +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, - .remove = berlin_pwm_remove, .driver = { .name = "berlin-pwm", .of_match_table = berlin_pwm_match, - .pm = &berlin_pwm_pm_ops, + .pm = pm_ptr(&berlin_pwm_pm_ops), }, }; module_platform_driver(berlin_pwm_driver); diff --git a/drivers/pwm/pwm-brcmstb.c b/drivers/pwm/pwm-brcmstb.c index fea612c45f20..82d27d07ba91 100644 --- a/drivers/pwm/pwm-brcmstb.c +++ b/drivers/pwm/pwm-brcmstb.c @@ -53,9 +53,7 @@ struct brcmstb_pwm { void __iomem *base; - spinlock_t lock; struct clk *clk; - struct pwm_chip chip; }; static inline u32 brcmstb_pwm_readl(struct brcmstb_pwm *p, @@ -78,7 +76,7 @@ static inline void brcmstb_pwm_writel(struct brcmstb_pwm *p, u32 value, static inline struct brcmstb_pwm *to_brcmstb_pwm(struct pwm_chip *chip) { - return container_of(chip, struct brcmstb_pwm, chip); + return pwmchip_get_drvdata(chip); } /* @@ -95,7 +93,7 @@ static inline struct brcmstb_pwm *to_brcmstb_pwm(struct pwm_chip *chip) * "on" time, so this translates directly into our HW programming here. */ static int brcmstb_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, - int duty_ns, int period_ns) + u64 duty_ns, u64 period_ns) { struct brcmstb_pwm *p = to_brcmstb_pwm(chip); unsigned long pc, dc, cword = CONST_VAR_F_MAX; @@ -114,22 +112,17 @@ static int brcmstb_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, } while (1) { - u64 rate, tmp; + u64 rate; /* * Calculate the base rate from base frequency and current * cword */ rate = (u64)clk_get_rate(p->clk) * (u64)cword; - do_div(rate, 1 << CWORD_BIT_SIZE); + rate >>= CWORD_BIT_SIZE; - tmp = period_ns * rate; - do_div(tmp, NSEC_PER_SEC); - pc = tmp; - - tmp = (duty_ns + 1) * rate; - do_div(tmp, NSEC_PER_SEC); - dc = tmp; + 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, @@ -164,7 +157,6 @@ done: * generator output a base frequency for the constant frequency * generator to derive from. */ - spin_lock(&p->lock); brcmstb_pwm_writel(p, cword >> 8, PWM_CWORD_MSB(channel)); brcmstb_pwm_writel(p, cword & 0xff, PWM_CWORD_LSB(channel)); @@ -176,7 +168,6 @@ done: /* Configure on and period value */ brcmstb_pwm_writel(p, pc, PWM_PERIOD(channel)); brcmstb_pwm_writel(p, dc, PWM_ON(channel)); - spin_unlock(&p->lock); return 0; } @@ -187,7 +178,6 @@ static inline void brcmstb_pwm_enable_set(struct brcmstb_pwm *p, unsigned int shift = channel * CTRL_CHAN_OFFS; u32 value; - spin_lock(&p->lock); value = brcmstb_pwm_readl(p, PWM_CTRL); if (enable) { @@ -199,30 +189,36 @@ static inline void brcmstb_pwm_enable_set(struct brcmstb_pwm *p, } brcmstb_pwm_writel(p, value, PWM_CTRL); - spin_unlock(&p->lock); } -static int brcmstb_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) +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; - brcmstb_pwm_enable_set(p, pwm->hwpwm, true); + if (state->polarity != PWM_POLARITY_NORMAL) + return -EINVAL; - return 0; -} + if (!state->enabled) { + if (pwm->state.enabled) + brcmstb_pwm_enable_set(p, pwm->hwpwm, false); -static void brcmstb_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) -{ - struct brcmstb_pwm *p = to_brcmstb_pwm(chip); + return 0; + } + + err = brcmstb_pwm_config(chip, pwm, state->duty_cycle, state->period); + if (err) + return err; - brcmstb_pwm_enable_set(p, pwm->hwpwm, false); + if (!pwm->state.enabled) + brcmstb_pwm_enable_set(p, pwm->hwpwm, true); + + return 0; } static const struct pwm_ops brcmstb_pwm_ops = { - .config = brcmstb_pwm_config, - .enable = brcmstb_pwm_enable, - .disable = brcmstb_pwm_disable, - .owner = THIS_MODULE, + .apply = brcmstb_pwm_apply, }; static const struct of_device_id brcmstb_pwm_of_match[] = { @@ -233,72 +229,40 @@ 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; - struct resource *res; int ret; - p = devm_kzalloc(&pdev->dev, sizeof(*p), GFP_KERNEL); - if (!p) - return -ENOMEM; + chip = devm_pwmchip_alloc(&pdev->dev, 2, sizeof(*p)); + if (IS_ERR(chip)) + return PTR_ERR(chip); + p = to_brcmstb_pwm(chip); - spin_lock_init(&p->lock); - - p->clk = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(p->clk)) { - dev_err(&pdev->dev, "failed to obtain clock\n"); - return PTR_ERR(p->clk); - } - - ret = clk_prepare_enable(p->clk); - if (ret < 0) { - dev_err(&pdev->dev, "failed to enable clock: %d\n", ret); - return ret; - } + 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); - p->chip.dev = &pdev->dev; - p->chip.ops = &brcmstb_pwm_ops; - p->chip.base = -1; - p->chip.npwm = 2; + chip->ops = &brcmstb_pwm_ops; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - p->base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(p->base)) { - ret = PTR_ERR(p->base); - goto out_clk; - } + p->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(p->base)) + return PTR_ERR(p->base); - ret = pwmchip_add(&p->chip); - if (ret) { - dev_err(&pdev->dev, "failed to add PWM chip: %d\n", ret); - goto out_clk; - } + ret = devm_pwmchip_add(&pdev->dev, chip); + if (ret) + return dev_err_probe(&pdev->dev, ret, "failed to add PWM chip\n"); return 0; - -out_clk: - clk_disable_unprepare(p->clk); - return ret; } -static int brcmstb_pwm_remove(struct platform_device *pdev) -{ - struct brcmstb_pwm *p = platform_get_drvdata(pdev); - int ret; - - ret = pwmchip_remove(&p->chip); - clk_disable_unprepare(p->clk); - - return ret; -} - -#ifdef CONFIG_PM_SLEEP static int brcmstb_pwm_suspend(struct device *dev) { struct brcmstb_pwm *p = dev_get_drvdata(dev); - clk_disable(p->clk); + clk_disable_unprepare(p->clk); return 0; } @@ -307,22 +271,18 @@ static int brcmstb_pwm_resume(struct device *dev) { struct brcmstb_pwm *p = dev_get_drvdata(dev); - clk_enable(p->clk); - - return 0; + return clk_prepare_enable(p->clk); } -#endif -static SIMPLE_DEV_PM_OPS(brcmstb_pwm_pm_ops, brcmstb_pwm_suspend, - brcmstb_pwm_resume); +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, - .remove = brcmstb_pwm_remove, .driver = { .name = "pwm-brcmstb", .of_match_table = brcmstb_pwm_of_match, - .pm = &brcmstb_pwm_pm_ops, + .pm = pm_ptr(&brcmstb_pwm_pm_ops), }, }; module_platform_driver(brcmstb_pwm_driver); diff --git a/drivers/pwm/pwm-clk.c b/drivers/pwm/pwm-clk.c new file mode 100644 index 000000000000..f8f5af57acba --- /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 index 924d39a797cf..2c92ce754872 100644 --- a/drivers/pwm/pwm-clps711x.c +++ b/drivers/pwm/pwm-clps711x.c @@ -12,38 +12,13 @@ #include <linux/pwm.h> struct clps711x_chip { - struct pwm_chip chip; void __iomem *pmpcon; struct clk *clk; - spinlock_t lock; }; static inline struct clps711x_chip *to_clps711x_chip(struct pwm_chip *chip) { - return container_of(chip, struct clps711x_chip, chip); -} - -static void clps711x_pwm_update_val(struct clps711x_chip *priv, u32 n, u32 v) -{ - /* PWM0 - bits 4..7, PWM1 - bits 8..11 */ - u32 shift = (n + 1) * 4; - unsigned long flags; - u32 tmp; - - spin_lock_irqsave(&priv->lock, flags); - - tmp = readl(priv->pmpcon); - tmp &= ~(0xf << shift); - tmp |= v << shift; - writel(tmp, priv->pmpcon); - - spin_unlock_irqrestore(&priv->lock, flags); -} - -static unsigned int clps711x_get_duty(struct pwm_device *pwm, unsigned int v) -{ - /* Duty cycle 0..15 max */ - return DIV_ROUND_CLOSEST(v * 0xf, pwm->args.period); + return pwmchip_get_drvdata(chip); } static int clps711x_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm) @@ -60,67 +35,49 @@ static int clps711x_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm) return 0; } -static int clps711x_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, - int duty_ns, int period_ns) +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); - unsigned int duty; + /* PWM0 - bits 4..7, PWM1 - bits 8..11 */ + u32 shift = (pwm->hwpwm + 1) * 4; + u32 pmpcon, val; - if (period_ns != pwm->args.period) + if (state->polarity != PWM_POLARITY_NORMAL) return -EINVAL; - duty = clps711x_get_duty(pwm, duty_ns); - clps711x_pwm_update_val(priv, pwm->hwpwm, duty); - - return 0; -} + if (state->period != pwm->args.period) + return -EINVAL; -static int clps711x_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) -{ - struct clps711x_chip *priv = to_clps711x_chip(chip); - unsigned int duty; + if (state->enabled) + val = mul_u64_u64_div_u64(state->duty_cycle, 0xf, state->period); + else + val = 0; - duty = clps711x_get_duty(pwm, pwm_get_duty_cycle(pwm)); - clps711x_pwm_update_val(priv, pwm->hwpwm, duty); + pmpcon = readl(priv->pmpcon); + pmpcon &= ~(0xf << shift); + pmpcon |= val << shift; + writel(pmpcon, priv->pmpcon); return 0; } -static void clps711x_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) -{ - struct clps711x_chip *priv = to_clps711x_chip(chip); - - clps711x_pwm_update_val(priv, pwm->hwpwm, 0); -} - static const struct pwm_ops clps711x_pwm_ops = { .request = clps711x_pwm_request, - .config = clps711x_pwm_config, - .enable = clps711x_pwm_enable, - .disable = clps711x_pwm_disable, - .owner = THIS_MODULE, + .apply = clps711x_pwm_apply, }; -static struct pwm_device *clps711x_pwm_xlate(struct pwm_chip *chip, - const struct of_phandle_args *args) -{ - if (args->args[0] >= chip->npwm) - return ERR_PTR(-EINVAL); - - return pwm_request_from_chip(chip, args->args[0], NULL); -} - static int clps711x_pwm_probe(struct platform_device *pdev) { + struct pwm_chip *chip; struct clps711x_chip *priv; - struct resource *res; - priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; + chip = devm_pwmchip_alloc(&pdev->dev, 2, sizeof(*priv)); + if (IS_ERR(chip)) + return PTR_ERR(chip); + priv = to_clps711x_chip(chip); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - priv->pmpcon = devm_ioremap_resource(&pdev->dev, res); + priv->pmpcon = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(priv->pmpcon)) return PTR_ERR(priv->pmpcon); @@ -128,28 +85,12 @@ static int clps711x_pwm_probe(struct platform_device *pdev) if (IS_ERR(priv->clk)) return PTR_ERR(priv->clk); - priv->chip.ops = &clps711x_pwm_ops; - priv->chip.dev = &pdev->dev; - priv->chip.base = -1; - priv->chip.npwm = 2; - priv->chip.of_xlate = clps711x_pwm_xlate; - priv->chip.of_pwm_n_cells = 1; - - spin_lock_init(&priv->lock); - - platform_set_drvdata(pdev, priv); - - return pwmchip_add(&priv->chip); -} - -static int clps711x_pwm_remove(struct platform_device *pdev) -{ - struct clps711x_chip *priv = platform_get_drvdata(pdev); + chip->ops = &clps711x_pwm_ops; - return pwmchip_remove(&priv->chip); + return devm_pwmchip_add(&pdev->dev, chip); } -static const struct of_device_id __maybe_unused clps711x_pwm_dt_ids[] = { +static const struct of_device_id clps711x_pwm_dt_ids[] = { { .compatible = "cirrus,ep7209-pwm", }, { } }; @@ -158,10 +99,9 @@ MODULE_DEVICE_TABLE(of, clps711x_pwm_dt_ids); static struct platform_driver clps711x_pwm_driver = { .driver = { .name = "clps711x-pwm", - .of_match_table = of_match_ptr(clps711x_pwm_dt_ids), + .of_match_table = clps711x_pwm_dt_ids, }, .probe = clps711x_pwm_probe, - .remove = clps711x_pwm_remove, }; module_platform_driver(clps711x_pwm_driver); diff --git a/drivers/pwm/pwm-crc.c b/drivers/pwm/pwm-crc.c index 272eeb071147..98ee5cdbd0ba 100644 --- a/drivers/pwm/pwm-crc.c +++ b/drivers/pwm/pwm-crc.c @@ -21,115 +21,169 @@ #define PWM_MAX_LEVEL 0xFF -#define PWM_BASE_CLK 6000000 /* 6 MHz */ -#define PWM_MAX_PERIOD_NS 21333 /* 46.875KHz */ +#define PWM_BASE_CLK_MHZ 6 /* 6 MHz */ +#define PWM_MAX_PERIOD_NS 5461334 /* 183 Hz */ /** * struct crystalcove_pwm - Crystal Cove PWM controller - * @chip: the abstract pwm_chip structure. * @regmap: the regmap from the parent device. */ struct crystalcove_pwm { - struct pwm_chip chip; struct regmap *regmap; }; -static inline struct crystalcove_pwm *to_crc_pwm(struct pwm_chip *pc) +static inline struct crystalcove_pwm *to_crc_pwm(struct pwm_chip *chip) { - return container_of(pc, struct crystalcove_pwm, chip); + return pwmchip_get_drvdata(chip); } -static int crc_pwm_enable(struct pwm_chip *c, struct pwm_device *pwm) +static int crc_pwm_calc_clk_div(int period_ns) { - struct crystalcove_pwm *crc_pwm = to_crc_pwm(c); + int clk_div; - regmap_write(crc_pwm->regmap, BACKLIGHT_EN, 1); + 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 0; + return clk_div; } -static void crc_pwm_disable(struct pwm_chip *c, struct pwm_device *pwm) +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(c); - - regmap_write(crc_pwm->regmap, BACKLIGHT_EN, 0); -} + struct crystalcove_pwm *crc_pwm = to_crc_pwm(chip); + struct device *dev = pwmchip_parent(chip); + int err; -static int crc_pwm_config(struct pwm_chip *c, struct pwm_device *pwm, - int duty_ns, int period_ns) -{ - struct crystalcove_pwm *crc_pwm = to_crc_pwm(c); - struct device *dev = crc_pwm->chip.dev; - int level; - - if (period_ns > PWM_MAX_PERIOD_NS) { + if (state->period > PWM_MAX_PERIOD_NS) { dev_err(dev, "un-supported period_ns\n"); return -EINVAL; } - if (pwm_get_period(pwm) != period_ns) { - int clk_div; + 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; + } + } - /* changing the clk divisor, need to disable fisrt */ - crc_pwm_disable(c, pwm); - clk_div = PWM_BASE_CLK * period_ns / NSEC_PER_SEC; + 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; + } + } - regmap_write(crc_pwm->regmap, PWM0_CLK_DIV, - clk_div | PWM_OUTPUT_ENABLE); + 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; + } - /* enable back */ - crc_pwm_enable(c, pwm); + 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; } - /* change the pwm duty cycle */ - level = duty_ns * PWM_MAX_LEVEL / period_ns; - regmap_write(crc_pwm->regmap, PWM0_DUTY_CYCLE, level); + 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 = { - .config = crc_pwm_config, - .enable = crc_pwm_enable, - .disable = crc_pwm_disable, + .apply = crc_pwm_apply, + .get_state = crc_pwm_get_state, }; static int crystalcove_pwm_probe(struct platform_device *pdev) { - struct crystalcove_pwm *pwm; + struct pwm_chip *chip; + struct crystalcove_pwm *crc_pwm; struct device *dev = pdev->dev.parent; struct intel_soc_pmic *pmic = dev_get_drvdata(dev); - pwm = devm_kzalloc(&pdev->dev, sizeof(*pwm), GFP_KERNEL); - if (!pwm) - return -ENOMEM; + chip = devm_pwmchip_alloc(&pdev->dev, 1, sizeof(*crc_pwm)); + if (IS_ERR(chip)) + return PTR_ERR(chip); + crc_pwm = to_crc_pwm(chip); - pwm->chip.dev = &pdev->dev; - pwm->chip.ops = &crc_pwm_ops; - pwm->chip.base = -1; - pwm->chip.npwm = 1; + chip->ops = &crc_pwm_ops; /* get the PMIC regmap */ - pwm->regmap = pmic->regmap; - - platform_set_drvdata(pdev, pwm); - - return pwmchip_add(&pwm->chip); -} - -static int crystalcove_pwm_remove(struct platform_device *pdev) -{ - struct crystalcove_pwm *pwm = platform_get_drvdata(pdev); + crc_pwm->regmap = pmic->regmap; - return pwmchip_remove(&pwm->chip); + return devm_pwmchip_add(&pdev->dev, chip); } static struct platform_driver crystalcove_pwm_driver = { .probe = crystalcove_pwm_probe, - .remove = crystalcove_pwm_remove, .driver = { .name = "crystal_cove_pwm", }, }; +module_platform_driver(crystalcove_pwm_driver); -builtin_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 index 09c08dee099e..67cfa17f58e0 100644 --- a/drivers/pwm/pwm-cros-ec.c +++ b/drivers/pwm/pwm-cros-ec.c @@ -6,66 +6,55 @@ */ #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 * - * @dev: Device node * @ec: Pointer to EC device - * @chip: PWM controller chip + * @use_pwm_type: Use PWM types instead of generic channels */ struct cros_ec_pwm_device { - struct device *dev; struct cros_ec_device *ec; - struct pwm_chip chip; -}; - -/** - * struct cros_ec_pwm - per-PWM driver data - * @duty_cycle: cached duty cycle - */ -struct cros_ec_pwm { - u16 duty_cycle; + bool use_pwm_type; }; -static inline struct cros_ec_pwm_device *pwm_to_cros_ec_pwm(struct pwm_chip *c) -{ - return container_of(c, struct cros_ec_pwm_device, chip); -} - -static int cros_ec_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm) +static inline struct cros_ec_pwm_device *pwm_to_cros_ec_pwm(struct pwm_chip *chip) { - struct cros_ec_pwm *channel; - - channel = kzalloc(sizeof(*channel), GFP_KERNEL); - if (!channel) - return -ENOMEM; - - pwm_set_chip_data(pwm, channel); - - return 0; + return pwmchip_get_drvdata(chip); } -static void cros_ec_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm) +static int cros_ec_dt_type_to_pwm_type(u8 dt_index, u8 *pwm_type) { - struct cros_ec_pwm *channel = pwm_get_chip_data(pwm); - - kfree(channel); + 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_device *ec, u8 index, u16 duty) +static int cros_ec_pwm_set_duty(struct cros_ec_pwm_device *ec_pwm, u8 index, + u16 duty) { - struct { - struct cros_ec_command msg; + 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; + ) __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)); @@ -75,22 +64,30 @@ static int cros_ec_pwm_set_duty(struct cros_ec_device *ec, u8 index, u16 duty) msg->outsize = sizeof(*params); params->duty = duty; - params->pwm_type = EC_PWM_TYPE_GENERIC; - params->index = index; + + if (ec_pwm->use_pwm_type) { + ret = cros_ec_dt_type_to_pwm_type(index, ¶ms->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, u8 index, - u32 *result) +static int cros_ec_pwm_get_duty(struct cros_ec_device *ec, bool use_pwm_type, u8 index) { - struct { - struct cros_ec_command msg; + 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; + ) __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; @@ -103,28 +100,29 @@ static int __cros_ec_pwm_get_duty(struct cros_ec_device *ec, u8 index, msg->insize = sizeof(*resp); msg->outsize = sizeof(*params); - params->pwm_type = EC_PWM_TYPE_GENERIC; - params->index = index; + if (use_pwm_type) { + ret = cros_ec_dt_type_to_pwm_type(index, ¶ms->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 (result) - *result = msg->result; if (ret < 0) return ret; return resp->duty; } -static int cros_ec_pwm_get_duty(struct cros_ec_device *ec, u8 index) -{ - return __cros_ec_pwm_get_duty(ec, index, NULL); -} - static int cros_ec_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, const struct pwm_state *state) { struct cros_ec_pwm_device *ec_pwm = pwm_to_cros_ec_pwm(chip); - struct cros_ec_pwm *channel = pwm_get_chip_data(pwm); u16 duty_cycle; int ret; @@ -132,101 +130,79 @@ static int cros_ec_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, 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->ec, pwm->hwpwm, duty_cycle); + ret = cros_ec_pwm_set_duty(ec_pwm, pwm->hwpwm, duty_cycle); if (ret < 0) return ret; - channel->duty_cycle = state->duty_cycle; - return 0; } -static void cros_ec_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm, - struct pwm_state *state) +static int cros_ec_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm, + struct pwm_state *state) { struct cros_ec_pwm_device *ec_pwm = pwm_to_cros_ec_pwm(chip); - struct cros_ec_pwm *channel = pwm_get_chip_data(pwm); int ret; - ret = cros_ec_pwm_get_duty(ec_pwm->ec, pwm->hwpwm); + ret = cros_ec_pwm_get_duty(ec_pwm->ec, ec_pwm->use_pwm_type, pwm->hwpwm); if (ret < 0) { - dev_err(chip->dev, "error getting initial duty: %d\n", ret); - return; + 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; - /* - * Note that "disabled" and "duty cycle == 0" are treated the same. If - * the cached duty cycle is not zero, used the cached duty cycle. This - * ensures that the configured duty cycle is kept across a disable and - * enable operation and avoids potentially confusing consumers. - * - * For the case of the initial hardware readout, channel->duty_cycle - * will be 0 and the actual duty cycle read from the EC is used. - */ - if (ret == 0 && channel->duty_cycle > 0) - state->duty_cycle = channel->duty_cycle; - else - state->duty_cycle = ret; -} - -static struct pwm_device * -cros_ec_pwm_xlate(struct pwm_chip *pc, const struct of_phandle_args *args) -{ - struct pwm_device *pwm; - - if (args->args[0] >= pc->npwm) - return ERR_PTR(-EINVAL); - - pwm = pwm_request_from_chip(pc, args->args[0], NULL); - if (IS_ERR(pwm)) - return pwm; - - /* The EC won't let us change the period */ - pwm->args.period = EC_PWM_MAX_DUTY; - - return pwm; + return 0; } static const struct pwm_ops cros_ec_pwm_ops = { - .request = cros_ec_pwm_request, - .free = cros_ec_pwm_free, .get_state = cros_ec_pwm_get_state, .apply = cros_ec_pwm_apply, - .owner = THIS_MODULE, }; +/* + * 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++) { - u32 result = 0; - - ret = __cros_ec_pwm_get_duty(ec, i, &result); - /* We want to parse EC protocol errors */ - if (ret < 0 && !(ret == -EPROTO && result)) - return ret; - + /* + * 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. */ - if (result == EC_RES_INVALID_COMMAND) + switch (ret) { + case -EOPNOTSUPP: /* invalid command */ return -ENODEV; - else if (result == EC_RES_INVALID_PARAM) + case -EINVAL: /* invalid parameter */ return i; - else if (result) - return -EPROTO; + default: + if (ret < 0) + return ret; + break; + } } return U8_MAX; @@ -236,57 +212,61 @@ 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) { - dev_err(dev, "no parent EC device\n"); - return -EINVAL; + 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; } - ec_pwm = devm_kzalloc(dev, sizeof(*ec_pwm), GFP_KERNEL); - if (!ec_pwm) - return -ENOMEM; - chip = &ec_pwm->chip; + 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->dev = dev; chip->ops = &cros_ec_pwm_ops; - chip->of_xlate = cros_ec_pwm_xlate; - chip->of_pwm_n_cells = 1; - chip->base = -1; - ret = cros_ec_num_pwms(ec); - if (ret < 0) { - dev_err(dev, "Couldn't find PWMs: %d\n", ret); - return ret; - } - chip->npwm = ret; - dev_dbg(dev, "Probed %u PWMs\n", chip->npwm); - - ret = pwmchip_add(chip); - if (ret < 0) { - dev_err(dev, "cannot register PWM: %d\n", ret); - return ret; - } - platform_set_drvdata(pdev, ec_pwm); + /* + * 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; - return ret; -} + dev_dbg(dev, "Probed %u PWMs\n", chip->npwm); -static int cros_ec_pwm_remove(struct platform_device *dev) -{ - struct cros_ec_pwm_device *ec_pwm = platform_get_drvdata(dev); - struct pwm_chip *chip = &ec_pwm->chip; + ret = devm_pwmchip_add(dev, chip); + if (ret < 0) + return dev_err_probe(dev, ret, "cannot register PWM\n"); - return pwmchip_remove(chip); + 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); @@ -294,7 +274,6 @@ MODULE_DEVICE_TABLE(of, cros_ec_pwm_of_match); static struct platform_driver cros_ec_pwm_driver = { .probe = cros_ec_pwm_probe, - .remove = cros_ec_pwm_remove, .driver = { .name = "cros-ec-pwm", .of_match_table = of_match_ptr(cros_ec_pwm_of_match), diff --git a/drivers/pwm/pwm-dwc-core.c b/drivers/pwm/pwm-dwc-core.c new file mode 100644 index 000000000000..6dabec93a3c6 --- /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 000000000000..b6c16139ce4a --- /dev/null +++ b/drivers/pwm/pwm-dwc.c @@ -0,0 +1,170 @@ +// 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 "pwm-dwc.h" + +/* Elkhart Lake */ +static const struct dwc_pwm_info ehl_pwm_info = { + .nr = 2, + .size = 0x1000, +}; + +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), (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 000000000000..1562594e7f85 --- /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 index 4bab73073ad7..994f89ac43b4 100644 --- a/drivers/pwm/pwm-ep93xx.c +++ b/drivers/pwm/pwm-ep93xx.c @@ -17,6 +17,7 @@ */ #include <linux/module.h> +#include <linux/mod_devicetable.h> #include <linux/platform_device.h> #include <linux/slab.h> #include <linux/clk.h> @@ -26,8 +27,6 @@ #include <asm/div64.h> -#include <linux/soc/cirrus/ep93xx.h> /* for ep93xx_pwm_{acquire,release}_gpio() */ - #define EP93XX_PWMx_TERM_COUNT 0x00 #define EP93XX_PWMx_DUTY_CYCLE 0x04 #define EP93XX_PWMx_ENABLE 0x08 @@ -36,57 +35,75 @@ struct ep93xx_pwm { void __iomem *base; struct clk *clk; - struct pwm_chip chip; }; static inline struct ep93xx_pwm *to_ep93xx_pwm(struct pwm_chip *chip) { - return container_of(chip, struct ep93xx_pwm, chip); -} - -static int ep93xx_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm) -{ - struct platform_device *pdev = to_platform_device(chip->dev); - - return ep93xx_pwm_acquire_gpio(pdev); -} - -static void ep93xx_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm) -{ - struct platform_device *pdev = to_platform_device(chip->dev); - - ep93xx_pwm_release_gpio(pdev); + return pwmchip_get_drvdata(chip); } -static int ep93xx_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, - int duty_ns, int period_ns) +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; - int ret = 0; + + 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_enable(ep93xx_pwm->clk); + ret = clk_prepare_enable(ep93xx_pwm->clk); if (ret) return ret; } c = clk_get_rate(ep93xx_pwm->clk); - c *= period_ns; + c *= state->period; do_div(c, 1000000000); period_cycles = c; c = period_cycles; - c *= duty_ns; - do_div(c, period_ns); + c *= state->duty_cycle; + do_div(c, state->period); duty_cycles = c; if (period_cycles < 0x10000 && duty_cycles < 0x10000) { @@ -100,84 +117,44 @@ static int ep93xx_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, 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(ep93xx_pwm->clk); - - return ret; -} - -static int ep93xx_pwm_polarity(struct pwm_chip *chip, struct pwm_device *pwm, - enum pwm_polarity polarity) -{ - struct ep93xx_pwm *ep93xx_pwm = to_ep93xx_pwm(chip); - int ret; + clk_disable_unprepare(ep93xx_pwm->clk); - /* - * The clock needs to be enabled to access the PWM registers. - * Polarity can only be changed when the PWM is disabled. - */ - ret = clk_enable(ep93xx_pwm->clk); if (ret) return ret; - if (polarity == PWM_POLARITY_INVERSED) - writew(0x1, ep93xx_pwm->base + EP93XX_PWMx_INVERT); - else - writew(0x0, ep93xx_pwm->base + EP93XX_PWMx_INVERT); - - clk_disable(ep93xx_pwm->clk); - - return 0; -} - -static int ep93xx_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) -{ - struct ep93xx_pwm *ep93xx_pwm = to_ep93xx_pwm(chip); - int ret; - - ret = clk_enable(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); + writew(0x1, ep93xx_pwm->base + EP93XX_PWMx_ENABLE); + } return 0; } -static void ep93xx_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) -{ - struct ep93xx_pwm *ep93xx_pwm = to_ep93xx_pwm(chip); - - writew(0x0, ep93xx_pwm->base + EP93XX_PWMx_ENABLE); - clk_disable(ep93xx_pwm->clk); -} - static const struct pwm_ops ep93xx_pwm_ops = { - .request = ep93xx_pwm_request, - .free = ep93xx_pwm_free, - .config = ep93xx_pwm_config, - .set_polarity = ep93xx_pwm_polarity, - .enable = ep93xx_pwm_enable, - .disable = ep93xx_pwm_disable, - .owner = THIS_MODULE, + .apply = ep93xx_pwm_apply, }; static int ep93xx_pwm_probe(struct platform_device *pdev) { + struct pwm_chip *chip; struct ep93xx_pwm *ep93xx_pwm; - struct resource *res; int ret; - ep93xx_pwm = devm_kzalloc(&pdev->dev, sizeof(*ep93xx_pwm), GFP_KERNEL); - if (!ep93xx_pwm) - return -ENOMEM; + chip = devm_pwmchip_alloc(&pdev->dev, 1, sizeof(*ep93xx_pwm)); + if (IS_ERR(chip)) + return PTR_ERR(chip); + ep93xx_pwm = to_ep93xx_pwm(chip); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - ep93xx_pwm->base = devm_ioremap_resource(&pdev->dev, res); + ep93xx_pwm->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(ep93xx_pwm->base)) return PTR_ERR(ep93xx_pwm->base); @@ -185,32 +162,27 @@ static int ep93xx_pwm_probe(struct platform_device *pdev) if (IS_ERR(ep93xx_pwm->clk)) return PTR_ERR(ep93xx_pwm->clk); - ep93xx_pwm->chip.dev = &pdev->dev; - ep93xx_pwm->chip.ops = &ep93xx_pwm_ops; - ep93xx_pwm->chip.base = -1; - ep93xx_pwm->chip.npwm = 1; + chip->ops = &ep93xx_pwm_ops; - ret = pwmchip_add(&ep93xx_pwm->chip); + ret = devm_pwmchip_add(&pdev->dev, chip); if (ret < 0) return ret; - platform_set_drvdata(pdev, ep93xx_pwm); return 0; } -static int ep93xx_pwm_remove(struct platform_device *pdev) -{ - struct ep93xx_pwm *ep93xx_pwm = platform_get_drvdata(pdev); - - return pwmchip_remove(&ep93xx_pwm->chip); -} +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, - .remove = ep93xx_pwm_remove, }; module_platform_driver(ep93xx_pwm_driver); diff --git a/drivers/pwm/pwm-fsl-ftm.c b/drivers/pwm/pwm-fsl-ftm.c index 59272a920479..35406b2e1925 100644 --- a/drivers/pwm/pwm-fsl-ftm.c +++ b/drivers/pwm/pwm-fsl-ftm.c @@ -3,6 +3,7 @@ * Freescale FlexTimer Module (FTM) PWM Driver * * Copyright 2012-2013 Freescale Semiconductor, Inc. + * Copyright 2020-2025 NXP */ #include <linux/clk.h> @@ -10,9 +11,7 @@ #include <linux/io.h> #include <linux/kernel.h> #include <linux/module.h> -#include <linux/mutex.h> -#include <linux/of_address.h> -#include <linux/of_device.h> +#include <linux/of.h> #include <linux/platform_device.h> #include <linux/pm.h> #include <linux/pwm.h> @@ -32,6 +31,8 @@ enum fsl_pwm_clk { struct fsl_ftm_soc { bool has_enable_bits; + bool has_flt_reg; + unsigned int npwm; }; struct fsl_pwm_periodcfg { @@ -41,8 +42,6 @@ struct fsl_pwm_periodcfg { }; struct fsl_pwm_chip { - struct pwm_chip chip; - struct mutex lock; struct regmap *regmap; /* This value is valid iff a pwm is running */ @@ -56,7 +55,7 @@ struct fsl_pwm_chip { static inline struct fsl_pwm_chip *to_fsl_chip(struct pwm_chip *chip) { - return container_of(chip, struct fsl_pwm_chip, chip); + return pwmchip_get_drvdata(chip); } static void ftm_clear_write_protection(struct fsl_pwm_chip *fpc) @@ -65,13 +64,12 @@ static void ftm_clear_write_protection(struct fsl_pwm_chip *fpc) regmap_read(fpc->regmap, FTM_FMS, &val); if (val & FTM_FMS_WPEN) - regmap_update_bits(fpc->regmap, FTM_MODE, FTM_MODE_WPDIS, - FTM_MODE_WPDIS); + regmap_set_bits(fpc->regmap, FTM_MODE, FTM_MODE_WPDIS); } static void ftm_set_write_protection(struct fsl_pwm_chip *fpc) { - regmap_update_bits(fpc->regmap, FTM_FMS, FTM_FMS_WPEN, FTM_FMS_WPEN); + regmap_set_bits(fpc->regmap, FTM_FMS, FTM_FMS_WPEN); } static bool fsl_pwm_periodcfg_are_equal(const struct fsl_pwm_periodcfg *a, @@ -92,12 +90,8 @@ static int fsl_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm) struct fsl_pwm_chip *fpc = to_fsl_chip(chip); ret = clk_prepare_enable(fpc->ipg_clk); - if (!ret && fpc->soc->has_enable_bits) { - mutex_lock(&fpc->lock); - regmap_update_bits(fpc->regmap, FTM_SC, BIT(pwm->hwpwm + 16), - BIT(pwm->hwpwm + 16)); - mutex_unlock(&fpc->lock); - } + if (!ret && fpc->soc->has_enable_bits) + regmap_set_bits(fpc->regmap, FTM_SC, BIT(pwm->hwpwm + 16)); return ret; } @@ -106,12 +100,8 @@ 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) { - mutex_lock(&fpc->lock); - regmap_update_bits(fpc->regmap, FTM_SC, BIT(pwm->hwpwm + 16), - 0); - mutex_unlock(&fpc->lock); - } + if (fpc->soc->has_enable_bits) + regmap_clear_bits(fpc->regmap, FTM_SC, BIT(pwm->hwpwm + 16)); clk_disable_unprepare(fpc->ipg_clk); } @@ -123,6 +113,9 @@ static unsigned int fsl_pwm_ticks_to_ns(struct fsl_pwm_chip *fpc, 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); @@ -195,6 +188,9 @@ static unsigned int fsl_pwm_calculate_duty(struct fsl_pwm_chip *fpc, 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); @@ -225,10 +221,11 @@ static bool fsl_pwm_is_other_pwm_enabled(struct fsl_pwm_chip *fpc, return false; } -static int fsl_pwm_apply_config(struct fsl_pwm_chip *fpc, +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; @@ -236,7 +233,7 @@ static int fsl_pwm_apply_config(struct fsl_pwm_chip *fpc, bool do_write_period = false; if (!fsl_pwm_calculate_period(fpc, newstate->period, &periodcfg)) { - dev_err(fpc->chip.dev, "failed to calculate new period\n"); + dev_err(pwmchip_parent(chip), "failed to calculate new period\n"); return -EINVAL; } @@ -250,7 +247,7 @@ static int fsl_pwm_apply_config(struct fsl_pwm_chip *fpc, */ else if (!fsl_pwm_periodcfg_are_equal(&fpc->period, &periodcfg)) { if (fsl_pwm_is_other_pwm_enabled(fpc, pwm)) { - dev_err(fpc->chip.dev, + dev_err(pwmchip_parent(chip), "Cannot change period for PWM %u, disable other PWMs first\n", pwm->hwpwm); return -EBUSY; @@ -302,7 +299,7 @@ static int fsl_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, { struct fsl_pwm_chip *fpc = to_fsl_chip(chip); struct pwm_state *oldstate = &pwm->state; - int ret = 0; + int ret; /* * oldstate to newstate : action @@ -313,41 +310,36 @@ static int fsl_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, * disabled to enabled : update settings + enable */ - mutex_lock(&fpc->lock); - if (!newstate->enabled) { if (oldstate->enabled) { - regmap_update_bits(fpc->regmap, FTM_OUTMASK, - BIT(pwm->hwpwm), BIT(pwm->hwpwm)); + 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]); } - goto end_mutex; + return 0; } - ret = fsl_pwm_apply_config(fpc, pwm, newstate); + ret = fsl_pwm_apply_config(chip, pwm, newstate); if (ret) - goto end_mutex; + return ret; /* check if need to enable */ if (!oldstate->enabled) { ret = clk_prepare_enable(fpc->clk[fpc->period.clk_select]); if (ret) - goto end_mutex; + return ret; ret = clk_prepare_enable(fpc->clk[FSL_PWM_CLK_CNTEN]); if (ret) { clk_disable_unprepare(fpc->clk[fpc->period.clk_select]); - goto end_mutex; + return ret; } - regmap_update_bits(fpc->regmap, FTM_OUTMASK, BIT(pwm->hwpwm), - 0); + regmap_clear_bits(fpc->regmap, FTM_OUTMASK, BIT(pwm->hwpwm)); } -end_mutex: - mutex_unlock(&fpc->lock); return ret; } @@ -355,7 +347,6 @@ static const struct pwm_ops fsl_pwm_ops = { .request = fsl_pwm_request, .free = fsl_pwm_free, .apply = fsl_pwm_apply, - .owner = THIS_MODULE, }; static int fsl_pwm_init(struct fsl_pwm_chip *fpc) @@ -386,6 +377,20 @@ static bool fsl_pwm_volatile_reg(struct device *dev, unsigned int reg) 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, @@ -394,26 +399,26 @@ static const struct regmap_config fsl_pwm_regmap_config = { .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; - struct resource *res; void __iomem *base; int ret; - fpc = devm_kzalloc(&pdev->dev, sizeof(*fpc), GFP_KERNEL); - if (!fpc) - return -ENOMEM; - - mutex_init(&fpc->lock); + 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 = of_device_get_match_data(&pdev->dev); - fpc->chip.dev = &pdev->dev; + fpc->soc = soc; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - base = devm_ioremap_resource(&pdev->dev, res); + base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(base)) return PTR_ERR(base); @@ -430,16 +435,16 @@ static int fsl_pwm_probe(struct platform_device *pdev) return PTR_ERR(fpc->clk[FSL_PWM_CLK_SYS]); } - fpc->clk[FSL_PWM_CLK_FIX] = devm_clk_get(fpc->chip.dev, "ftm_fix"); + 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(fpc->chip.dev, "ftm_ext"); + 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(fpc->chip.dev, "ftm_cnt_clk_en"); + 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]); @@ -451,42 +456,31 @@ static int fsl_pwm_probe(struct platform_device *pdev) if (IS_ERR(fpc->ipg_clk)) fpc->ipg_clk = fpc->clk[FSL_PWM_CLK_SYS]; + chip->ops = &fsl_pwm_ops; - fpc->chip.ops = &fsl_pwm_ops; - fpc->chip.of_xlate = of_pwm_xlate_with_flags; - fpc->chip.of_pwm_n_cells = 3; - fpc->chip.base = -1; - fpc->chip.npwm = 8; - - ret = pwmchip_add(&fpc->chip); + 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, fpc); + platform_set_drvdata(pdev, chip); return fsl_pwm_init(fpc); } -static int fsl_pwm_remove(struct platform_device *pdev) -{ - struct fsl_pwm_chip *fpc = platform_get_drvdata(pdev); - - return pwmchip_remove(&fpc->chip); -} - #ifdef CONFIG_PM_SLEEP static int fsl_pwm_suspend(struct device *dev) { - struct fsl_pwm_chip *fpc = dev_get_drvdata(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 < fpc->chip.npwm; i++) { - struct pwm_device *pwm = &fpc->chip.pwms[i]; + for (i = 0; i < chip->npwm; i++) { + struct pwm_device *pwm = &chip->pwms[i]; if (!test_bit(PWMF_REQUESTED, &pwm->flags)) continue; @@ -505,11 +499,12 @@ static int fsl_pwm_suspend(struct device *dev) static int fsl_pwm_resume(struct device *dev) { - struct fsl_pwm_chip *fpc = dev_get_drvdata(dev); + struct pwm_chip *chip = dev_get_drvdata(dev); + struct fsl_pwm_chip *fpc = to_fsl_chip(chip); int i; - for (i = 0; i < fpc->chip.npwm; i++) { - struct pwm_device *pwm = &fpc->chip.pwms[i]; + for (i = 0; i < chip->npwm; i++) { + struct pwm_device *pwm = &chip->pwms[i]; if (!test_bit(PWMF_REQUESTED, &pwm->flags)) continue; @@ -537,15 +532,26 @@ static const struct dev_pm_ops fsl_pwm_pm_ops = { 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); @@ -557,7 +563,6 @@ static struct platform_driver fsl_pwm_driver = { .pm = &fsl_pwm_pm_ops, }, .probe = fsl_pwm_probe, - .remove = fsl_pwm_remove, }; module_platform_driver(fsl_pwm_driver); diff --git a/drivers/pwm/pwm-gpio.c b/drivers/pwm/pwm-gpio.c new file mode 100644 index 000000000000..5f4edeb394a9 --- /dev/null +++ b/drivers/pwm/pwm-gpio.c @@ -0,0 +1,240 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Generic software PWM for modulating GPIOs + * + * Copyright (C) 2020 Axis Communications AB + * Copyright (C) 2020 Nicola Di Lieto + * Copyright (C) 2024 Stefan Wahren + * Copyright (C) 2024 Linus Walleij + */ + +#include <linux/cleanup.h> +#include <linux/container_of.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/gpio/consumer.h> +#include <linux/hrtimer.h> +#include <linux/math.h> +#include <linux/module.h> +#include <linux/mod_devicetable.h> +#include <linux/platform_device.h> +#include <linux/property.h> +#include <linux/pwm.h> +#include <linux/spinlock.h> +#include <linux/time.h> +#include <linux/types.h> + +struct pwm_gpio { + struct hrtimer gpio_timer; + struct gpio_desc *gpio; + struct pwm_state state; + struct pwm_state next_state; + + /* Protect internal state between pwm_ops and hrtimer */ + spinlock_t lock; + + bool changing; + bool running; + bool level; +}; + +static void pwm_gpio_round(struct pwm_state *dest, const struct pwm_state *src) +{ + u64 dividend; + u32 remainder; + + *dest = *src; + + /* Round down to hrtimer resolution */ + dividend = dest->period; + remainder = do_div(dividend, hrtimer_resolution); + dest->period -= remainder; + + dividend = dest->duty_cycle; + remainder = do_div(dividend, hrtimer_resolution); + dest->duty_cycle -= remainder; +} + +static u64 pwm_gpio_toggle(struct pwm_gpio *gpwm, bool level) +{ + const struct pwm_state *state = &gpwm->state; + bool invert = state->polarity == PWM_POLARITY_INVERSED; + + gpwm->level = level; + gpiod_set_value(gpwm->gpio, gpwm->level ^ invert); + + if (!state->duty_cycle || state->duty_cycle == state->period) { + gpwm->running = false; + return 0; + } + + gpwm->running = true; + return level ? state->duty_cycle : state->period - state->duty_cycle; +} + +static enum hrtimer_restart pwm_gpio_timer(struct hrtimer *gpio_timer) +{ + struct pwm_gpio *gpwm = container_of(gpio_timer, struct pwm_gpio, + gpio_timer); + u64 next_toggle; + bool new_level; + + guard(spinlock_irqsave)(&gpwm->lock); + + /* Apply new state at end of current period */ + if (!gpwm->level && gpwm->changing) { + gpwm->changing = false; + gpwm->state = gpwm->next_state; + new_level = !!gpwm->state.duty_cycle; + } else { + new_level = !gpwm->level; + } + + next_toggle = pwm_gpio_toggle(gpwm, new_level); + if (next_toggle) + hrtimer_forward(gpio_timer, hrtimer_get_expires(gpio_timer), + ns_to_ktime(next_toggle)); + + return next_toggle ? HRTIMER_RESTART : HRTIMER_NORESTART; +} + +static int pwm_gpio_apply(struct pwm_chip *chip, struct pwm_device *pwm, + const struct pwm_state *state) +{ + struct pwm_gpio *gpwm = pwmchip_get_drvdata(chip); + bool invert = state->polarity == PWM_POLARITY_INVERSED; + + if (state->duty_cycle && state->duty_cycle < hrtimer_resolution) + return -EINVAL; + + if (state->duty_cycle != state->period && + (state->period - state->duty_cycle < hrtimer_resolution)) + return -EINVAL; + + if (!state->enabled) { + hrtimer_cancel(&gpwm->gpio_timer); + } else if (!gpwm->running) { + int ret; + + /* + * This just enables the output, but pwm_gpio_toggle() + * really starts the duty cycle. + */ + ret = gpiod_direction_output(gpwm->gpio, invert); + if (ret) + return ret; + } + + guard(spinlock_irqsave)(&gpwm->lock); + + if (!state->enabled) { + pwm_gpio_round(&gpwm->state, state); + gpwm->running = false; + gpwm->changing = false; + + gpiod_set_value(gpwm->gpio, invert); + } else if (gpwm->running) { + pwm_gpio_round(&gpwm->next_state, state); + gpwm->changing = true; + } else { + unsigned long next_toggle; + + pwm_gpio_round(&gpwm->state, state); + gpwm->changing = false; + + next_toggle = pwm_gpio_toggle(gpwm, !!state->duty_cycle); + if (next_toggle) + hrtimer_start(&gpwm->gpio_timer, next_toggle, + HRTIMER_MODE_REL); + } + + return 0; +} + +static int pwm_gpio_get_state(struct pwm_chip *chip, struct pwm_device *pwm, + struct pwm_state *state) +{ + struct pwm_gpio *gpwm = pwmchip_get_drvdata(chip); + + guard(spinlock_irqsave)(&gpwm->lock); + + if (gpwm->changing) + *state = gpwm->next_state; + else + *state = gpwm->state; + + return 0; +} + +static const struct pwm_ops pwm_gpio_ops = { + .apply = pwm_gpio_apply, + .get_state = pwm_gpio_get_state, +}; + +static void pwm_gpio_disable_hrtimer(void *data) +{ + struct pwm_gpio *gpwm = data; + + hrtimer_cancel(&gpwm->gpio_timer); +} + +static int pwm_gpio_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct pwm_chip *chip; + struct pwm_gpio *gpwm; + int ret; + + chip = devm_pwmchip_alloc(dev, 1, sizeof(*gpwm)); + if (IS_ERR(chip)) + return PTR_ERR(chip); + + gpwm = pwmchip_get_drvdata(chip); + + spin_lock_init(&gpwm->lock); + + gpwm->gpio = devm_gpiod_get(dev, NULL, GPIOD_ASIS); + if (IS_ERR(gpwm->gpio)) + return dev_err_probe(dev, PTR_ERR(gpwm->gpio), + "%pfw: could not get gpio\n", + dev_fwnode(dev)); + + if (gpiod_cansleep(gpwm->gpio)) + return dev_err_probe(dev, -EINVAL, + "%pfw: sleeping GPIO not supported\n", + dev_fwnode(dev)); + + chip->ops = &pwm_gpio_ops; + chip->atomic = true; + + hrtimer_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 index ad205fdad372..e02ee6383dbc 100644 --- a/drivers/pwm/pwm-hibvt.c +++ b/drivers/pwm/pwm-hibvt.c @@ -10,7 +10,7 @@ #include <linux/delay.h> #include <linux/io.h> #include <linux/module.h> -#include <linux/of_device.h> +#include <linux/of.h> #include <linux/platform_device.h> #include <linux/pwm.h> #include <linux/reset.h> @@ -33,7 +33,6 @@ #define PWM_DUTY_MASK GENMASK(31, 0) struct hibvt_pwm_chip { - struct pwm_chip chip; struct clk *clk; void __iomem *base; struct reset_control *rstc; @@ -65,7 +64,7 @@ static const struct hibvt_pwm_soc hi3559v100_soc_info = { static inline struct hibvt_pwm_chip *to_hibvt_pwm_chip(struct pwm_chip *chip) { - return container_of(chip, struct hibvt_pwm_chip, chip); + return pwmchip_get_drvdata(chip); } static void hibvt_pwm_set_bits(void __iomem *base, u32 offset, @@ -128,8 +127,8 @@ static void hibvt_pwm_set_polarity(struct pwm_chip *chip, PWM_POLARITY_MASK, (0x0 << PWM_POLARITY_SHIFT)); } -static void hibvt_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm, - struct pwm_state *state) +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; @@ -146,6 +145,9 @@ static void hibvt_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm, 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, @@ -182,85 +184,77 @@ static const struct pwm_ops hibvt_pwm_ops = { .get_state = hibvt_pwm_get_state, .apply = hibvt_pwm_apply, - .owner = THIS_MODULE, }; static int hibvt_pwm_probe(struct platform_device *pdev) { const struct hibvt_pwm_soc *soc = of_device_get_match_data(&pdev->dev); - struct hibvt_pwm_chip *pwm_chip; - struct resource *res; - int ret; - int i; + struct pwm_chip *chip; + struct hibvt_pwm_chip *hi_pwm_chip; + int ret, i; - pwm_chip = devm_kzalloc(&pdev->dev, sizeof(*pwm_chip), GFP_KERNEL); - if (pwm_chip == NULL) - return -ENOMEM; + 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); - pwm_chip->clk = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(pwm_chip->clk)) { + 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(pwm_chip->clk)); - return PTR_ERR(pwm_chip->clk); + PTR_ERR(hi_pwm_chip->clk)); + return PTR_ERR(hi_pwm_chip->clk); } - pwm_chip->chip.ops = &hibvt_pwm_ops; - pwm_chip->chip.dev = &pdev->dev; - pwm_chip->chip.base = -1; - pwm_chip->chip.npwm = soc->num_pwms; - pwm_chip->chip.of_xlate = of_pwm_xlate_with_flags; - pwm_chip->chip.of_pwm_n_cells = 3; - pwm_chip->soc = soc; + chip->ops = &hibvt_pwm_ops; + hi_pwm_chip->soc = soc; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - pwm_chip->base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(pwm_chip->base)) - return PTR_ERR(pwm_chip->base); + 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(pwm_chip->clk); + ret = clk_prepare_enable(hi_pwm_chip->clk); if (ret < 0) return ret; - pwm_chip->rstc = devm_reset_control_get_exclusive(&pdev->dev, NULL); - if (IS_ERR(pwm_chip->rstc)) { - clk_disable_unprepare(pwm_chip->clk); - return PTR_ERR(pwm_chip->rstc); + 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(pwm_chip->rstc); + reset_control_assert(hi_pwm_chip->rstc); msleep(30); - reset_control_deassert(pwm_chip->rstc); + reset_control_deassert(hi_pwm_chip->rstc); - ret = pwmchip_add(&pwm_chip->chip); + ret = pwmchip_add(chip); if (ret < 0) { - clk_disable_unprepare(pwm_chip->clk); + clk_disable_unprepare(hi_pwm_chip->clk); return ret; } - for (i = 0; i < pwm_chip->chip.npwm; i++) { - hibvt_pwm_set_bits(pwm_chip->base, PWM_CTRL_ADDR(i), + 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, pwm_chip); + platform_set_drvdata(pdev, chip); return 0; } -static int hibvt_pwm_remove(struct platform_device *pdev) +static void hibvt_pwm_remove(struct platform_device *pdev) { - struct hibvt_pwm_chip *pwm_chip; + struct pwm_chip *chip = platform_get_drvdata(pdev); + struct hibvt_pwm_chip *hi_pwm_chip = to_hibvt_pwm_chip(chip); - pwm_chip = platform_get_drvdata(pdev); + pwmchip_remove(chip); - reset_control_assert(pwm_chip->rstc); + reset_control_assert(hi_pwm_chip->rstc); msleep(30); - reset_control_deassert(pwm_chip->rstc); - - clk_disable_unprepare(pwm_chip->clk); + reset_control_deassert(hi_pwm_chip->rstc); - return pwmchip_remove(&pwm_chip->chip); + clk_disable_unprepare(hi_pwm_chip->clk); } static const struct of_device_id hibvt_pwm_of_match[] = { @@ -282,7 +276,7 @@ static struct platform_driver hibvt_pwm_driver = { .of_match_table = hibvt_pwm_of_match, }, .probe = hibvt_pwm_probe, - .remove = hibvt_pwm_remove, + .remove = hibvt_pwm_remove, }; module_platform_driver(hibvt_pwm_driver); diff --git a/drivers/pwm/pwm-img.c b/drivers/pwm/pwm-img.c index 599a0f66a384..91e0b19f0c08 100644 --- a/drivers/pwm/pwm-img.c +++ b/drivers/pwm/pwm-img.c @@ -13,9 +13,9 @@ #include <linux/mfd/syscon.h> #include <linux/module.h> #include <linux/of.h> -#include <linux/of_device.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> @@ -59,8 +59,6 @@ struct img_pwm_soc_data { }; struct img_pwm_chip { - struct device *dev; - struct pwm_chip chip; struct clk *pwm_clk; struct clk *sys_clk; void __iomem *base; @@ -74,19 +72,18 @@ struct img_pwm_chip { static inline struct img_pwm_chip *to_img_pwm_chip(struct pwm_chip *chip) { - return container_of(chip, struct img_pwm_chip, chip); + return pwmchip_get_drvdata(chip); } -static inline void img_pwm_writel(struct img_pwm_chip *chip, +static inline void img_pwm_writel(struct img_pwm_chip *imgchip, u32 reg, u32 val) { - writel(val, chip->base + reg); + writel(val, imgchip->base + reg); } -static inline u32 img_pwm_readl(struct img_pwm_chip *chip, - u32 reg) +static inline u32 img_pwm_readl(struct img_pwm_chip *imgchip, u32 reg) { - return readl(chip->base + reg); + return readl(imgchip->base + reg); } static int img_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, @@ -94,17 +91,17 @@ static int img_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, { u32 val, div, duty, timebase; unsigned long mul, output_clk_hz, input_clk_hz; - struct img_pwm_chip *pwm_chip = to_img_pwm_chip(chip); - unsigned int max_timebase = pwm_chip->data->max_timebase; + struct img_pwm_chip *imgchip = to_img_pwm_chip(chip); + unsigned int max_timebase = imgchip->data->max_timebase; int ret; - if (period_ns < pwm_chip->min_period_ns || - period_ns > pwm_chip->max_period_ns) { - dev_err(chip->dev, "configured period not in range\n"); + 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(pwm_chip->pwm_clk); + 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); @@ -121,31 +118,28 @@ static int img_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, div = PWM_CTRL_CFG_SUB_DIV0_DIV1; timebase = DIV_ROUND_UP(mul, 512); } else { - dev_err(chip->dev, + 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_get_sync(chip->dev); - if (ret < 0) { - pm_runtime_put_autosuspend(chip->dev); + ret = pm_runtime_resume_and_get(pwmchip_parent(chip)); + if (ret < 0) return ret; - } - val = img_pwm_readl(pwm_chip, PWM_CTRL_CFG); + 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(pwm_chip, PWM_CTRL_CFG, val); + img_pwm_writel(imgchip, PWM_CTRL_CFG, val); val = (duty << PWM_CH_CFG_DUTY_SHIFT) | (timebase << PWM_CH_CFG_TMBASE_SHIFT); - img_pwm_writel(pwm_chip, PWM_CH_CFG(pwm->hwpwm), val); + img_pwm_writel(imgchip, PWM_CH_CFG(pwm->hwpwm), val); - pm_runtime_mark_last_busy(chip->dev); - pm_runtime_put_autosuspend(chip->dev); + pm_runtime_put_autosuspend(pwmchip_parent(chip)); return 0; } @@ -153,20 +147,20 @@ static int img_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, static int img_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) { u32 val; - struct img_pwm_chip *pwm_chip = to_img_pwm_chip(chip); + struct img_pwm_chip *imgchip = to_img_pwm_chip(chip); int ret; - ret = pm_runtime_get_sync(chip->dev); + ret = pm_runtime_resume_and_get(pwmchip_parent(chip)); if (ret < 0) return ret; - val = img_pwm_readl(pwm_chip, PWM_CTRL_CFG); + val = img_pwm_readl(imgchip, PWM_CTRL_CFG); val |= BIT(pwm->hwpwm); - img_pwm_writel(pwm_chip, PWM_CTRL_CFG, val); + img_pwm_writel(imgchip, PWM_CTRL_CFG, val); - regmap_update_bits(pwm_chip->periph_regs, PERIP_PWM_PDM_CONTROL, - PERIP_PWM_PDM_CONTROL_CH_MASK << - PERIP_PWM_PDM_CONTROL_CH_SHIFT(pwm->hwpwm), 0); + 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; } @@ -174,21 +168,42 @@ static int img_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) static void img_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) { u32 val; - struct img_pwm_chip *pwm_chip = to_img_pwm_chip(chip); + struct img_pwm_chip *imgchip = to_img_pwm_chip(chip); - val = img_pwm_readl(pwm_chip, PWM_CTRL_CFG); + val = img_pwm_readl(imgchip, PWM_CTRL_CFG); val &= ~BIT(pwm->hwpwm); - img_pwm_writel(pwm_chip, PWM_CTRL_CFG, val); + 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; + } - pm_runtime_mark_last_busy(chip->dev); - pm_runtime_put_autosuspend(chip->dev); + 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 = { - .config = img_pwm_config, - .enable = img_pwm_enable, - .disable = img_pwm_disable, - .owner = THIS_MODULE, + .apply = img_pwm_apply, }; static const struct img_pwm_soc_data pistachio_pwm = { @@ -206,29 +221,31 @@ MODULE_DEVICE_TABLE(of, img_pwm_of_match); static int img_pwm_runtime_suspend(struct device *dev) { - struct img_pwm_chip *pwm_chip = dev_get_drvdata(dev); + struct pwm_chip *chip = dev_get_drvdata(dev); + struct img_pwm_chip *imgchip = to_img_pwm_chip(chip); - clk_disable_unprepare(pwm_chip->pwm_clk); - clk_disable_unprepare(pwm_chip->sys_clk); + clk_disable_unprepare(imgchip->pwm_clk); + clk_disable_unprepare(imgchip->sys_clk); return 0; } static int img_pwm_runtime_resume(struct device *dev) { - struct img_pwm_chip *pwm_chip = dev_get_drvdata(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(pwm_chip->sys_clk); + 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(pwm_chip->pwm_clk); + ret = clk_prepare_enable(imgchip->pwm_clk); if (ret < 0) { dev_err(dev, "could not prepare or enable pwm clock\n"); - clk_disable_unprepare(pwm_chip->sys_clk); + clk_disable_unprepare(imgchip->sys_clk); return ret; } @@ -240,43 +257,39 @@ static int img_pwm_probe(struct platform_device *pdev) int ret; u64 val; unsigned long clk_rate; - struct resource *res; - struct img_pwm_chip *pwm; - const struct of_device_id *of_dev_id; - - pwm = devm_kzalloc(&pdev->dev, sizeof(*pwm), GFP_KERNEL); - if (!pwm) - return -ENOMEM; + struct pwm_chip *chip; + struct img_pwm_chip *imgchip; - pwm->dev = &pdev->dev; + 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); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - pwm->base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(pwm->base)) - return PTR_ERR(pwm->base); + imgchip->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(imgchip->base)) + return PTR_ERR(imgchip->base); - of_dev_id = of_match_device(img_pwm_of_match, &pdev->dev); - if (!of_dev_id) - return -ENODEV; - pwm->data = of_dev_id->data; + imgchip->data = device_get_match_data(&pdev->dev); - pwm->periph_regs = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, - "img,cr-periph"); - if (IS_ERR(pwm->periph_regs)) - return PTR_ERR(pwm->periph_regs); + 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); - pwm->sys_clk = devm_clk_get(&pdev->dev, "sys"); - if (IS_ERR(pwm->sys_clk)) { + 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(pwm->sys_clk); + return PTR_ERR(imgchip->sys_clk); } - pwm->pwm_clk = devm_clk_get(&pdev->dev, "pwm"); - if (IS_ERR(pwm->pwm_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(pwm->pwm_clk); + 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); @@ -286,34 +299,30 @@ static int img_pwm_probe(struct platform_device *pdev) goto err_pm_disable; } - clk_rate = clk_get_rate(pwm->pwm_clk); + clk_rate = clk_get_rate(imgchip->pwm_clk); if (!clk_rate) { - dev_err(&pdev->dev, "pwm clock has no frequency\n"); + 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 * pwm->data->max_timebase; + val = (u64)NSEC_PER_SEC * 512 * imgchip->data->max_timebase; do_div(val, clk_rate); - pwm->max_period_ns = val; + imgchip->max_period_ns = val; val = (u64)NSEC_PER_SEC * MIN_TMBASE_STEPS; do_div(val, clk_rate); - pwm->min_period_ns = val; + imgchip->min_period_ns = val; - pwm->chip.dev = &pdev->dev; - pwm->chip.ops = &img_pwm_ops; - pwm->chip.base = -1; - pwm->chip.npwm = IMG_PWM_NPWM; + chip->ops = &img_pwm_ops; - ret = pwmchip_add(&pwm->chip); + ret = pwmchip_add(chip); if (ret < 0) { dev_err(&pdev->dev, "pwmchip_add failed: %d\n", ret); goto err_suspend; } - platform_set_drvdata(pdev, pwm); return 0; err_suspend: @@ -325,37 +334,22 @@ err_pm_disable: return ret; } -static int img_pwm_remove(struct platform_device *pdev) +static void img_pwm_remove(struct platform_device *pdev) { - struct img_pwm_chip *pwm_chip = platform_get_drvdata(pdev); - u32 val; - unsigned int i; - int ret; - - ret = pm_runtime_get_sync(&pdev->dev); - if (ret < 0) { - pm_runtime_put(&pdev->dev); - return ret; - } - - for (i = 0; i < pwm_chip->chip.npwm; i++) { - val = img_pwm_readl(pwm_chip, PWM_CTRL_CFG); - val &= ~BIT(i); - img_pwm_writel(pwm_chip, PWM_CTRL_CFG, val); - } + struct pwm_chip *chip = platform_get_drvdata(pdev); - pm_runtime_put(&pdev->dev); pm_runtime_disable(&pdev->dev); if (!pm_runtime_status_suspended(&pdev->dev)) img_pwm_runtime_suspend(&pdev->dev); - return pwmchip_remove(&pwm_chip->chip); + pwmchip_remove(chip); } #ifdef CONFIG_PM_SLEEP static int img_pwm_suspend(struct device *dev) { - struct img_pwm_chip *pwm_chip = dev_get_drvdata(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)) { @@ -364,11 +358,11 @@ static int img_pwm_suspend(struct device *dev) return ret; } - for (i = 0; i < pwm_chip->chip.npwm; i++) - pwm_chip->suspend_ch_cfg[i] = img_pwm_readl(pwm_chip, - PWM_CH_CFG(i)); + for (i = 0; i < chip->npwm; i++) + imgchip->suspend_ch_cfg[i] = img_pwm_readl(imgchip, + PWM_CH_CFG(i)); - pwm_chip->suspend_ctrl_cfg = img_pwm_readl(pwm_chip, PWM_CTRL_CFG); + imgchip->suspend_ctrl_cfg = img_pwm_readl(imgchip, PWM_CTRL_CFG); img_pwm_runtime_suspend(dev); @@ -377,7 +371,8 @@ static int img_pwm_suspend(struct device *dev) static int img_pwm_resume(struct device *dev) { - struct img_pwm_chip *pwm_chip = dev_get_drvdata(dev); + struct pwm_chip *chip = dev_get_drvdata(dev); + struct img_pwm_chip *imgchip = to_img_pwm_chip(chip); int ret; int i; @@ -385,19 +380,18 @@ static int img_pwm_resume(struct device *dev) if (ret) return ret; - for (i = 0; i < pwm_chip->chip.npwm; i++) - img_pwm_writel(pwm_chip, PWM_CH_CFG(i), - pwm_chip->suspend_ch_cfg[i]); + for (i = 0; i < chip->npwm; i++) + img_pwm_writel(imgchip, PWM_CH_CFG(i), + imgchip->suspend_ch_cfg[i]); - img_pwm_writel(pwm_chip, PWM_CTRL_CFG, pwm_chip->suspend_ctrl_cfg); + img_pwm_writel(imgchip, PWM_CTRL_CFG, imgchip->suspend_ctrl_cfg); - for (i = 0; i < pwm_chip->chip.npwm; i++) - if (pwm_chip->suspend_ctrl_cfg & BIT(i)) - regmap_update_bits(pwm_chip->periph_regs, - PERIP_PWM_PDM_CONTROL, - PERIP_PWM_PDM_CONTROL_CH_MASK << - PERIP_PWM_PDM_CONTROL_CH_SHIFT(i), - 0); + 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); diff --git a/drivers/pwm/pwm-imx-tpm.c b/drivers/pwm/pwm-imx-tpm.c index 5f3d7f7e6aef..5b399de16d60 100644 --- a/drivers/pwm/pwm-imx-tpm.c +++ b/drivers/pwm/pwm-imx-tpm.c @@ -20,6 +20,7 @@ #include <linux/io.h> #include <linux/module.h> #include <linux/of.h> +#include <linux/pinctrl/consumer.h> #include <linux/platform_device.h> #include <linux/pwm.h> #include <linux/slab.h> @@ -57,7 +58,6 @@ #define PWM_IMX_TPM_MOD_MOD GENMASK(PWM_IMX_TPM_MOD_WIDTH - 1, 0) struct imx_tpm_pwm_chip { - struct pwm_chip chip; struct clk *clk; void __iomem *base; struct mutex lock; @@ -75,7 +75,7 @@ struct imx_tpm_pwm_param { static inline struct imx_tpm_pwm_chip * to_imx_tpm_pwm_chip(struct pwm_chip *chip) { - return container_of(chip, struct imx_tpm_pwm_chip, chip); + return pwmchip_get_drvdata(chip); } /* @@ -106,7 +106,9 @@ static int pwm_imx_tpm_round_state(struct pwm_chip *chip, p->prescale = prescale; period_count = (clock_unit + ((1 << prescale) >> 1)) >> prescale; - p->mod = period_count; + if (period_count == 0) + return -EINVAL; + p->mod = period_count - 1; /* calculate real period HW can support */ tmp = (u64)period_count << prescale; @@ -124,7 +126,7 @@ static int pwm_imx_tpm_round_state(struct pwm_chip *chip, real_state->duty_cycle = state->duty_cycle; tmp = (u64)p->mod * real_state->duty_cycle; - p->val = DIV_ROUND_CLOSEST_ULL(tmp, real_state->period); + p->val = DIV64_U64_ROUND_CLOSEST(tmp, real_state->period); real_state->polarity = state->polarity; real_state->enabled = state->enabled; @@ -132,9 +134,9 @@ static int pwm_imx_tpm_round_state(struct pwm_chip *chip, return 0; } -static void pwm_imx_tpm_get_state(struct pwm_chip *chip, - struct pwm_device *pwm, - struct pwm_state *state) +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; @@ -164,6 +166,8 @@ static void pwm_imx_tpm_get_state(struct pwm_chip *chip, /* 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 */ @@ -201,6 +205,15 @@ static int pwm_imx_tpm_apply_hw(struct pwm_chip *chip, 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. @@ -330,100 +343,96 @@ static const struct pwm_ops imx_tpm_pwm_ops = { .free = pwm_imx_tpm_free, .get_state = pwm_imx_tpm_get_state, .apply = pwm_imx_tpm_apply, - .owner = THIS_MODULE, }; static int pwm_imx_tpm_probe(struct platform_device *pdev) { + struct pwm_chip *chip; struct imx_tpm_pwm_chip *tpm; + struct clk *clk; + void __iomem *base; int ret; + unsigned int npwm; u32 val; - tpm = devm_kzalloc(&pdev->dev, sizeof(*tpm), GFP_KERNEL); - if (!tpm) - return -ENOMEM; + base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(base)) + return PTR_ERR(base); - platform_set_drvdata(pdev, tpm); + 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"); - tpm->base = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(tpm->base)) - return PTR_ERR(tpm->base); + /* get number of channels */ + val = readl(base + PWM_IMX_TPM_PARAM); + npwm = FIELD_GET(PWM_IMX_TPM_PARAM_CHAN, val); - tpm->clk = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(tpm->clk)) { - ret = PTR_ERR(tpm->clk); - if (ret != -EPROBE_DEFER) - dev_err(&pdev->dev, - "failed to get PWM clock: %d\n", ret); - return ret; - } + chip = devm_pwmchip_alloc(&pdev->dev, npwm, sizeof(*tpm)); + if (IS_ERR(chip)) + return PTR_ERR(chip); + tpm = to_imx_tpm_pwm_chip(chip); - ret = clk_prepare_enable(tpm->clk); - if (ret) { - dev_err(&pdev->dev, - "failed to prepare or enable clock: %d\n", ret); - return ret; - } + platform_set_drvdata(pdev, tpm); - tpm->chip.dev = &pdev->dev; - tpm->chip.ops = &imx_tpm_pwm_ops; - tpm->chip.base = -1; - tpm->chip.of_xlate = of_pwm_xlate_with_flags; - tpm->chip.of_pwm_n_cells = 3; + tpm->base = base; + tpm->clk = clk; - /* get number of channels */ - val = readl(tpm->base + PWM_IMX_TPM_PARAM); - tpm->chip.npwm = FIELD_GET(PWM_IMX_TPM_PARAM_CHAN, val); + chip->ops = &imx_tpm_pwm_ops; mutex_init(&tpm->lock); - ret = pwmchip_add(&tpm->chip); - if (ret) { - dev_err(&pdev->dev, "failed to add PWM chip: %d\n", ret); - clk_disable_unprepare(tpm->clk); - } - - return ret; -} - -static int pwm_imx_tpm_remove(struct platform_device *pdev) -{ - struct imx_tpm_pwm_chip *tpm = platform_get_drvdata(pdev); - int ret = pwmchip_remove(&tpm->chip); - - clk_disable_unprepare(tpm->clk); + ret = devm_pwmchip_add(&pdev->dev, chip); + if (ret) + return dev_err_probe(&pdev->dev, ret, "failed to add PWM chip\n"); - return ret; + return 0; } -static int __maybe_unused pwm_imx_tpm_suspend(struct device *dev) +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); - return 0; + ret = pinctrl_pm_select_sleep_state(dev); + if (ret) + clk_prepare_enable(tpm->clk); + + return ret; } -static int __maybe_unused pwm_imx_tpm_resume(struct device *dev) +static int pwm_imx_tpm_resume(struct device *dev) { struct imx_tpm_pwm_chip *tpm = dev_get_drvdata(dev); int ret = 0; - ret = clk_prepare_enable(tpm->clk); + ret = pinctrl_pm_select_default_state(dev); if (ret) - dev_err(dev, - "failed to prepare or enable clock: %d\n", - 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 SIMPLE_DEV_PM_OPS(imx_tpm_pwm_pm, - pwm_imx_tpm_suspend, pwm_imx_tpm_resume); +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", }, @@ -435,10 +444,9 @@ static struct platform_driver imx_tpm_pwm_driver = { .driver = { .name = "imx7ulp-tpm-pwm", .of_match_table = imx_tpm_pwm_dt_ids, - .pm = &imx_tpm_pwm_pm, + .pm = pm_ptr(&imx_tpm_pwm_pm), }, .probe = pwm_imx_tpm_probe, - .remove = pwm_imx_tpm_remove, }; module_platform_driver(imx_tpm_pwm_driver); diff --git a/drivers/pwm/pwm-imx1.c b/drivers/pwm/pwm-imx1.c index f8b2c2e001a7..d5535d208005 100644 --- a/drivers/pwm/pwm-imx1.c +++ b/drivers/pwm/pwm-imx1.c @@ -14,7 +14,6 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/of.h> -#include <linux/of_device.h> #include <linux/platform_device.h> #include <linux/pwm.h> #include <linux/slab.h> @@ -29,10 +28,12 @@ struct pwm_imx1_chip { struct clk *clk_ipg; struct clk *clk_per; void __iomem *mmio_base; - struct pwm_chip chip; }; -#define to_pwm_imx1_chip(chip) container_of(chip, struct pwm_imx1_chip, chip) +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) { @@ -61,7 +62,7 @@ static void pwm_imx1_clk_disable_unprepare(struct pwm_chip *chip) } static int pwm_imx1_config(struct pwm_chip *chip, - struct pwm_device *pwm, int duty_ns, int period_ns) + struct pwm_device *pwm, u64 duty_ns, u64 period_ns) { struct pwm_imx1_chip *imx = to_pwm_imx1_chip(chip); u32 max, p; @@ -84,7 +85,7 @@ static int pwm_imx1_config(struct pwm_chip *chip, * (/2 .. /16). */ max = readl(imx->mmio_base + MX1_PWMP); - p = max * duty_ns / period_ns; + p = mul_u64_u64_div_u64(max, duty_ns, period_ns); writel(max - p, imx->mmio_base + MX1_PWMS); @@ -120,11 +121,33 @@ static void pwm_imx1_disable(struct pwm_chip *chip, struct pwm_device *pwm) 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 = { - .enable = pwm_imx1_enable, - .disable = pwm_imx1_disable, - .config = pwm_imx1_config, - .owner = THIS_MODULE, + .apply = pwm_imx1_apply, }; static const struct of_device_id pwm_imx1_dt_ids[] = { @@ -135,54 +158,31 @@ 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; - struct resource *r; - - imx = devm_kzalloc(&pdev->dev, sizeof(*imx), GFP_KERNEL); - if (!imx) - return -ENOMEM; - platform_set_drvdata(pdev, 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)) { - dev_err(&pdev->dev, "getting ipg clock failed with %ld\n", - PTR_ERR(imx->clk_ipg)); - return PTR_ERR(imx->clk_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)) { - int ret = PTR_ERR(imx->clk_per); - - if (ret != -EPROBE_DEFER) - dev_err(&pdev->dev, - "failed to get peripheral clock: %d\n", - ret); - - return ret; - } + if (IS_ERR(imx->clk_per)) + return dev_err_probe(&pdev->dev, PTR_ERR(imx->clk_per), + "failed to get peripheral clock\n"); - imx->chip.ops = &pwm_imx1_ops; - imx->chip.dev = &pdev->dev; - imx->chip.base = -1; - imx->chip.npwm = 1; + chip->ops = &pwm_imx1_ops; - r = platform_get_resource(pdev, IORESOURCE_MEM, 0); - imx->mmio_base = devm_ioremap_resource(&pdev->dev, r); + imx->mmio_base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(imx->mmio_base)) return PTR_ERR(imx->mmio_base); - return pwmchip_add(&imx->chip); -} - -static int pwm_imx1_remove(struct platform_device *pdev) -{ - struct pwm_imx1_chip *imx = platform_get_drvdata(pdev); - - pwm_imx1_clk_disable_unprepare(&imx->chip); - - return pwmchip_remove(&imx->chip); + return devm_pwmchip_add(&pdev->dev, chip); } static struct platform_driver pwm_imx1_driver = { @@ -191,9 +191,9 @@ static struct platform_driver pwm_imx1_driver = { .of_match_table = pwm_imx1_dt_ids, }, .probe = pwm_imx1_probe, - .remove = pwm_imx1_remove, }; 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 index 732a6f3701e8..3d34cdc4a3a5 100644 --- a/drivers/pwm/pwm-imx27.c +++ b/drivers/pwm/pwm-imx27.c @@ -26,6 +26,7 @@ #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) @@ -79,11 +80,13 @@ /* 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 *clk_ipg; - struct clk *clk_per; + struct clk_bulk_data clks[ARRAY_SIZE(pwm_imx27_clks)]; + int clks_cnt; void __iomem *mmio_base; - struct pwm_chip chip; /* * The driver cannot read the current duty cycle from the hardware if @@ -93,42 +96,22 @@ struct pwm_imx27_chip { unsigned int duty_cycle; }; -#define to_pwm_imx27_chip(chip) container_of(chip, struct pwm_imx27_chip, chip) - -static int pwm_imx27_clk_prepare_enable(struct pwm_imx27_chip *imx) +static inline struct pwm_imx27_chip *to_pwm_imx27_chip(struct pwm_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; + return pwmchip_get_drvdata(chip); } -static void pwm_imx27_clk_disable_unprepare(struct pwm_imx27_chip *imx) -{ - clk_disable_unprepare(imx->clk_per); - clk_disable_unprepare(imx->clk_ipg); -} - -static void pwm_imx27_get_state(struct pwm_chip *chip, - struct pwm_device *pwm, struct pwm_state *state) +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 = pwm_imx27_clk_prepare_enable(imx); + ret = clk_bulk_prepare_enable(imx->clks_cnt, imx->clks); if (ret < 0) - return; + return ret; val = readl(imx->mmio_base + MX3_PWMCR); @@ -145,11 +128,11 @@ static void pwm_imx27_get_state(struct pwm_chip *chip, state->polarity = PWM_POLARITY_INVERSED; break; default: - dev_warn(chip->dev, "can't set polarity, output disconnected"); + dev_warn(pwmchip_parent(chip), "can't set polarity, output disconnected"); } prescaler = MX3_PWMCR_PRESCALER_GET(val); - pwm_clk = clk_get_rate(imx->clk_per); + 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; @@ -169,13 +152,15 @@ static void pwm_imx27_get_state(struct pwm_chip *chip, tmp = NSEC_PER_SEC * (u64)(val) * prescaler; state->duty_cycle = DIV_ROUND_UP_ULL(tmp, pwm_clk); - pwm_imx27_clk_disable_unprepare(imx); + 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 = chip->dev; + struct device *dev = pwmchip_parent(chip); int wait_count = 0; u32 cr; @@ -194,7 +179,7 @@ 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 = chip->dev; + struct device *dev = pwmchip_parent(chip); unsigned int period_ms; int fifoav; u32 sr; @@ -202,8 +187,8 @@ static void pwm_imx27_wait_fifo_slot(struct pwm_chip *chip, 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(pwm_get_period(pwm), - NSEC_PER_MSEC); + period_ms = DIV_ROUND_UP_ULL(pwm->state.period, + NSEC_PER_MSEC); msleep(period_ms); sr = readl(imx->mmio_base + MX3_PWMSR); @@ -215,17 +200,16 @@ static void pwm_imx27_wait_fifo_slot(struct pwm_chip *chip, 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; + unsigned long period_cycles, duty_cycles, prescale, period_us, tmp; struct pwm_imx27_chip *imx = to_pwm_imx27_chip(chip); - struct pwm_state cstate; unsigned long long c; unsigned long long clkrate; + unsigned long flags; + int val; int ret; u32 cr; - pwm_get_state(pwm, &cstate); - - clkrate = clk_get_rate(imx->clk_per); + clkrate = clk_get_rate(imx->clks[PWM_IMX27_PER].clk); c = clkrate * state->period; do_div(c, NSEC_PER_SEC); @@ -235,8 +219,9 @@ static int pwm_imx27_apply(struct pwm_chip *chip, struct pwm_device *pwm, period_cycles /= prescale; c = clkrate * state->duty_cycle; - do_div(c, NSEC_PER_SEC * prescale); + do_div(c, NSEC_PER_SEC); duty_cycles = c; + duty_cycles /= prescale; /* * according to imx pwm RM, the real period value should be PERIOD @@ -251,17 +236,108 @@ static int pwm_imx27_apply(struct pwm_chip *chip, struct pwm_device *pwm, * 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 (cstate.enabled) { + if (pwm->state.enabled) { pwm_imx27_wait_fifo_slot(chip, pwm); } else { - ret = pwm_imx27_clk_prepare_enable(imx); + ret = clk_bulk_prepare_enable(imx->clks_cnt, imx->clks); if (ret) return ret; pwm_imx27_sw_reset(chip); } - writel(duty_cycles, imx->mmio_base + MX3_PWMSAR); + val = readl(imx->mmio_base + MX3_PWMPR); + val = val >= MX3_PWMPR_MAX ? MX3_PWMPR_MAX : val; + cr = readl(imx->mmio_base + MX3_PWMCR); + 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); /* @@ -285,7 +361,7 @@ static int pwm_imx27_apply(struct pwm_chip *chip, struct pwm_device *pwm, writel(cr, imx->mmio_base + MX3_PWMCR); if (!state->enabled) - pwm_imx27_clk_disable_unprepare(imx); + clk_bulk_disable_unprepare(imx->clks_cnt, imx->clks); return 0; } @@ -293,7 +369,6 @@ static int pwm_imx27_apply(struct pwm_chip *chip, struct pwm_device *pwm, static const struct pwm_ops pwm_imx27_ops = { .apply = pwm_imx27_apply, .get_state = pwm_imx27_get_state, - .owner = THIS_MODULE, }; static const struct of_device_id pwm_imx27_dt_ids[] = { @@ -304,70 +379,43 @@ 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; - imx = devm_kzalloc(&pdev->dev, sizeof(*imx), GFP_KERNEL); - if (imx == NULL) - return -ENOMEM; - - platform_set_drvdata(pdev, imx); - - imx->clk_ipg = devm_clk_get(&pdev->dev, "ipg"); - if (IS_ERR(imx->clk_ipg)) { - int ret = PTR_ERR(imx->clk_ipg); - - if (ret != -EPROBE_DEFER) - dev_err(&pdev->dev, - "getting ipg clock failed with %d\n", - ret); - return ret; - } + chip = devm_pwmchip_alloc(&pdev->dev, 1, sizeof(*imx)); + if (IS_ERR(chip)) + return PTR_ERR(chip); + imx = to_pwm_imx27_chip(chip); - imx->clk_per = devm_clk_get(&pdev->dev, "per"); - if (IS_ERR(imx->clk_per)) { - int ret = PTR_ERR(imx->clk_per); + imx->clks_cnt = ARRAY_SIZE(pwm_imx27_clks); + for (i = 0; i < imx->clks_cnt; ++i) + imx->clks[i].id = pwm_imx27_clks[i]; - if (ret != -EPROBE_DEFER) - dev_err(&pdev->dev, - "failed to get peripheral clock: %d\n", - ret); + ret = devm_clk_bulk_get(&pdev->dev, imx->clks_cnt, imx->clks); - return ret; - } - - imx->chip.ops = &pwm_imx27_ops; - imx->chip.dev = &pdev->dev; - imx->chip.base = -1; - imx->chip.npwm = 1; + if (ret) + return dev_err_probe(&pdev->dev, ret, + "getting clocks failed\n"); - imx->chip.of_xlate = of_pwm_xlate_with_flags; - imx->chip.of_pwm_n_cells = 3; + 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 = pwm_imx27_clk_prepare_enable(imx); + 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)) - pwm_imx27_clk_disable_unprepare(imx); - - return pwmchip_add(&imx->chip); -} - -static int pwm_imx27_remove(struct platform_device *pdev) -{ - struct pwm_imx27_chip *imx; - - imx = platform_get_drvdata(pdev); + clk_bulk_disable_unprepare(imx->clks_cnt, imx->clks); - return pwmchip_remove(&imx->chip); + return devm_pwmchip_add(&pdev->dev, chip); } static struct platform_driver imx_pwm_driver = { @@ -376,9 +424,9 @@ static struct platform_driver imx_pwm_driver = { .of_match_table = pwm_imx27_dt_ids, }, .probe = pwm_imx27_probe, - .remove = pwm_imx27_remove, }; 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 000000000000..084c71a0a11b --- /dev/null +++ b/drivers/pwm/pwm-intel-lgm.c @@ -0,0 +1,234 @@ +// 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/mod_devicetable.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-iqs620a.c b/drivers/pwm/pwm-iqs620a.c index 674f0e238ba0..13e5e138c8e9 100644 --- a/drivers/pwm/pwm-iqs620a.c +++ b/drivers/pwm/pwm-iqs620a.c @@ -25,37 +25,60 @@ #include <linux/regmap.h> #include <linux/slab.h> -#define IQS620_PWR_SETTINGS 0xD2 +#define IQS620_PWR_SETTINGS 0xd2 #define IQS620_PWR_SETTINGS_PWM_OUT BIT(7) -#define IQS620_PWM_DUTY_CYCLE 0xD8 +#define IQS620_PWM_DUTY_CYCLE 0xd8 #define IQS620_PWM_PERIOD_NS 1000000 struct iqs620_pwm_private { struct iqs62x_core *iqs62x; - struct pwm_chip chip; + struct device *dev; struct notifier_block notifier; struct mutex lock; - bool out_en; - u8 duty_val; + 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; - struct iqs62x_core *iqs62x; - int duty_scale, ret; + unsigned int duty_cycle; + unsigned int duty_scale; + int ret; if (state->polarity != PWM_POLARITY_NORMAL) - return -ENOTSUPP; + return -EINVAL; if (state->period < IQS620_PWM_PERIOD_NS) return -EINVAL; - iqs620_pwm = container_of(chip, struct iqs620_pwm_private, chip); - iqs62x = iqs620_pwm->iqs62x; + iqs620_pwm = iqs620_pwm_from_chip(chip); /* * The duty cycle generated by the device is calculated as follows: @@ -69,73 +92,54 @@ static int iqs620_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, * 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_scale = state->duty_cycle * 256 / IQS620_PWM_PERIOD_NS; - - mutex_lock(&iqs620_pwm->lock); - - if (!state->enabled || !duty_scale) { - ret = regmap_update_bits(iqs62x->regmap, IQS620_PWR_SETTINGS, - IQS620_PWR_SETTINGS_PWM_OUT, 0); - if (ret) - goto err_mutex; - } - - if (duty_scale) { - u8 duty_val = min(duty_scale - 1, 0xFF); - - ret = regmap_write(iqs62x->regmap, IQS620_PWM_DUTY_CYCLE, - duty_val); - if (ret) - goto err_mutex; + duty_cycle = min_t(u64, state->duty_cycle, IQS620_PWM_PERIOD_NS); + duty_scale = duty_cycle * 256 / IQS620_PWM_PERIOD_NS; - iqs620_pwm->duty_val = duty_val; - } + if (!state->enabled) + duty_scale = 0; - if (state->enabled && duty_scale) { - ret = regmap_update_bits(iqs62x->regmap, IQS620_PWR_SETTINGS, - IQS620_PWR_SETTINGS_PWM_OUT, 0xFF); - if (ret) - goto err_mutex; - } + mutex_lock(&iqs620_pwm->lock); - iqs620_pwm->out_en = state->enabled; + ret = iqs620_pwm_init(iqs620_pwm, duty_scale); + if (!ret) + iqs620_pwm->duty_scale = duty_scale; -err_mutex: mutex_unlock(&iqs620_pwm->lock); return ret; } -static void iqs620_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm, - struct pwm_state *state) +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 = container_of(chip, struct iqs620_pwm_private, chip); + 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 with duty cycle equal to that which was in use prior to - * the request. This is not ideal, but is the best compromise based on + * as disabled. This is not ideal, but is the best compromise based on * the capabilities of the device. */ - state->enabled = iqs620_pwm->out_en; - state->duty_cycle = DIV_ROUND_UP((iqs620_pwm->duty_val + 1) * + 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; - struct iqs62x_core *iqs62x; int ret; if (!(event_flags & BIT(IQS62X_EVENT_SYS_RESET))) @@ -143,7 +147,6 @@ static int iqs620_pwm_notifier(struct notifier_block *notifier, iqs620_pwm = container_of(notifier, struct iqs620_pwm_private, notifier); - iqs62x = iqs620_pwm->iqs62x; mutex_lock(&iqs620_pwm->lock); @@ -152,20 +155,12 @@ static int iqs620_pwm_notifier(struct notifier_block *notifier, * of a device reset, so nothing else is printed here unless there is * an additional failure. */ - ret = regmap_write(iqs62x->regmap, IQS620_PWM_DUTY_CYCLE, - iqs620_pwm->duty_val); - if (ret) - goto err_mutex; + ret = iqs620_pwm_init(iqs620_pwm, iqs620_pwm->duty_scale); - ret = regmap_update_bits(iqs62x->regmap, IQS620_PWR_SETTINGS, - IQS620_PWR_SETTINGS_PWM_OUT, - iqs620_pwm->out_en ? 0xFF : 0); - -err_mutex: mutex_unlock(&iqs620_pwm->lock); if (ret) { - dev_err(iqs620_pwm->chip.dev, + dev_err(iqs620_pwm->dev, "Failed to re-initialize device: %d\n", ret); return NOTIFY_BAD; } @@ -176,7 +171,6 @@ err_mutex: static const struct pwm_ops iqs620_pwm_ops = { .apply = iqs620_pwm_apply, .get_state = iqs620_pwm_get_state, - .owner = THIS_MODULE, }; static void iqs620_pwm_notifier_unregister(void *context) @@ -187,38 +181,39 @@ static void iqs620_pwm_notifier_unregister(void *context) ret = blocking_notifier_chain_unregister(&iqs620_pwm->iqs62x->nh, &iqs620_pwm->notifier); if (ret) - dev_err(iqs620_pwm->chip.dev, + 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; - iqs620_pwm = devm_kzalloc(&pdev->dev, sizeof(*iqs620_pwm), GFP_KERNEL); - if (!iqs620_pwm) - return -ENOMEM; + chip = devm_pwmchip_alloc(&pdev->dev, 1, sizeof(*iqs620_pwm)); + if (IS_ERR(chip)) + return PTR_ERR(chip); - platform_set_drvdata(pdev, iqs620_pwm); + 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; - iqs620_pwm->out_en = val & IQS620_PWR_SETTINGS_PWM_OUT; - ret = regmap_read(iqs62x->regmap, IQS620_PWM_DUTY_CYCLE, &val); - if (ret) - return ret; - iqs620_pwm->duty_val = val; + 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; + } - iqs620_pwm->chip.dev = &pdev->dev; - iqs620_pwm->chip.ops = &iqs620_pwm_ops; - iqs620_pwm->chip.base = -1; - iqs620_pwm->chip.npwm = 1; + chip->ops = &iqs620_pwm_ops; mutex_init(&iqs620_pwm->lock); @@ -236,31 +231,18 @@ static int iqs620_pwm_probe(struct platform_device *pdev) if (ret) return ret; - ret = pwmchip_add(&iqs620_pwm->chip); + ret = devm_pwmchip_add(&pdev->dev, chip); if (ret) dev_err(&pdev->dev, "Failed to add device: %d\n", ret); return ret; } -static int iqs620_pwm_remove(struct platform_device *pdev) -{ - struct iqs620_pwm_private *iqs620_pwm = platform_get_drvdata(pdev); - int ret; - - ret = pwmchip_remove(&iqs620_pwm->chip); - if (ret) - dev_err(&pdev->dev, "Failed to remove device: %d\n", ret); - - return ret; -} - static struct platform_driver iqs620_pwm_platform_driver = { .driver = { .name = "iqs620a-pwm", }, .probe = iqs620_pwm_probe, - .remove = iqs620_pwm_remove, }; module_platform_driver(iqs620_pwm_platform_driver); diff --git a/drivers/pwm/pwm-jz4740.c b/drivers/pwm/pwm-jz4740.c index 5830ac2bdf6a..6bdb01619380 100644 --- a/drivers/pwm/pwm-jz4740.c +++ b/drivers/pwm/pwm-jz4740.c @@ -15,7 +15,7 @@ #include <linux/mfd/ingenic-tcu.h> #include <linux/mfd/syscon.h> #include <linux/module.h> -#include <linux/of_device.h> +#include <linux/of.h> #include <linux/platform_device.h> #include <linux/pwm.h> #include <linux/regmap.h> @@ -25,22 +25,21 @@ struct soc_info { }; struct jz4740_pwm_chip { - struct pwm_chip chip; struct regmap *map; + struct clk *clk[]; }; static inline struct jz4740_pwm_chip *to_jz4740(struct pwm_chip *chip) { - return container_of(chip, struct jz4740_pwm_chip, chip); + return pwmchip_get_drvdata(chip); } -static bool jz4740_pwm_can_use_chn(struct jz4740_pwm_chip *jz, - unsigned int channel) +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(jz->chip.npwm - 1, 2); + u32 pwm_channels_mask = GENMASK(chip->npwm - 1, 2); - device_property_read_u32(jz->chip.dev->parent, + device_property_read_u32(pwmchip_parent(chip)->parent, "ingenic,pwm-channels-mask", &pwm_channels_mask); @@ -54,16 +53,15 @@ static int jz4740_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm) char name[16]; int err; - if (!jz4740_pwm_can_use_chn(jz, pwm->hwpwm)) + if (!jz4740_pwm_can_use_chn(chip, pwm->hwpwm)) return -EBUSY; snprintf(name, sizeof(name), "timer%u", pwm->hwpwm); - clk = clk_get(chip->dev, name); + clk = clk_get(pwmchip_parent(chip), name); if (IS_ERR(clk)) { - if (PTR_ERR(clk) != -EPROBE_DEFER) - dev_err(chip->dev, "Failed to get clock: %pe", clk); - + dev_err(pwmchip_parent(chip), + "error %pe: Failed to get clock\n", clk); return PTR_ERR(clk); } @@ -73,14 +71,15 @@ static int jz4740_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm) return err; } - pwm_set_chip_data(pwm, clk); + jz->clk[pwm->hwpwm] = clk; return 0; } static void jz4740_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm) { - struct clk *clk = pwm_get_chip_data(pwm); + struct jz4740_pwm_chip *jz = to_jz4740(chip); + struct clk *clk = jz->clk[pwm->hwpwm]; clk_disable_unprepare(clk); clk_put(clk); @@ -91,8 +90,7 @@ 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_update_bits(jz->map, TCU_REG_TCSRc(pwm->hwpwm), - TCU_TCSR_PWM_EN, TCU_TCSR_PWM_EN); + 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)); @@ -116,8 +114,7 @@ static void jz4740_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) * 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_update_bits(jz->map, TCU_REG_TCSRc(pwm->hwpwm), - TCU_TCSR_PWM_EN, 0); + 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)); @@ -126,9 +123,9 @@ static void jz4740_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) static int jz4740_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, const struct pwm_state *state) { - struct jz4740_pwm_chip *jz4740 = to_jz4740(pwm->chip); + struct jz4740_pwm_chip *jz = to_jz4740(chip); unsigned long long tmp = 0xffffull * NSEC_PER_SEC; - struct clk *clk = pwm_get_chip_data(pwm); + struct clk *clk = jz->clk[pwm->hwpwm]; unsigned long period, duty; long rate; int err; @@ -152,7 +149,7 @@ static int jz4740_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, */ rate = clk_round_rate(clk, tmp); if (rate < 0) { - dev_err(chip->dev, "Unable to round rate: %ld", rate); + dev_err(pwmchip_parent(chip), "Unable to round rate: %ld\n", rate); return rate; } @@ -173,22 +170,22 @@ static int jz4740_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, err = clk_set_rate(clk, rate); if (err) { - dev_err(chip->dev, "Unable to set rate: %d", err); + dev_err(pwmchip_parent(chip), "Unable to set rate: %d\n", err); return err; } /* Reset counter to 0 */ - regmap_write(jz4740->map, TCU_REG_TCNTc(pwm->hwpwm), 0); + regmap_write(jz->map, TCU_REG_TCNTc(pwm->hwpwm), 0); /* Set duty */ - regmap_write(jz4740->map, TCU_REG_TDHRc(pwm->hwpwm), duty); + regmap_write(jz->map, TCU_REG_TDHRc(pwm->hwpwm), duty); /* Set period */ - regmap_write(jz4740->map, TCU_REG_TDFRc(pwm->hwpwm), period); + regmap_write(jz->map, TCU_REG_TDFRc(pwm->hwpwm), period); /* Set abrupt shutdown */ - regmap_update_bits(jz4740->map, TCU_REG_TCSRc(pwm->hwpwm), - TCU_TCSR_PWM_SD, TCU_TCSR_PWM_SD); + regmap_set_bits(jz->map, TCU_REG_TCSRc(pwm->hwpwm), + TCU_TCSR_PWM_SD); /* * Set polarity. @@ -204,12 +201,11 @@ static int jz4740_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, * state instead of its inactive state. */ if ((state->polarity == PWM_POLARITY_NORMAL) ^ state->enabled) - regmap_update_bits(jz4740->map, TCU_REG_TCSRc(pwm->hwpwm), - TCU_TCSR_PWM_INITL_HIGH, 0); + regmap_clear_bits(jz->map, TCU_REG_TCSRc(pwm->hwpwm), + TCU_TCSR_PWM_INITL_HIGH); else - regmap_update_bits(jz4740->map, TCU_REG_TCSRc(pwm->hwpwm), - TCU_TCSR_PWM_INITL_HIGH, - TCU_TCSR_PWM_INITL_HIGH); + regmap_set_bits(jz->map, TCU_REG_TCSRc(pwm->hwpwm), + TCU_TCSR_PWM_INITL_HIGH); if (state->enabled) jz4740_pwm_enable(chip, pwm); @@ -221,72 +217,61 @@ static const struct pwm_ops jz4740_pwm_ops = { .request = jz4740_pwm_request, .free = jz4740_pwm_free, .apply = jz4740_pwm_apply, - .owner = THIS_MODULE, }; static int jz4740_pwm_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - struct jz4740_pwm_chip *jz4740; + struct pwm_chip *chip; + struct jz4740_pwm_chip *jz; const struct soc_info *info; info = device_get_match_data(dev); if (!info) return -EINVAL; - jz4740 = devm_kzalloc(dev, sizeof(*jz4740), GFP_KERNEL); - if (!jz4740) - return -ENOMEM; + 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); - jz4740->map = device_node_to_regmap(dev->parent->of_node); - if (IS_ERR(jz4740->map)) { - dev_err(dev, "regmap not found: %ld\n", PTR_ERR(jz4740->map)); - return PTR_ERR(jz4740->map); + 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); } - jz4740->chip.dev = dev; - jz4740->chip.ops = &jz4740_pwm_ops; - jz4740->chip.npwm = info->num_pwms; - jz4740->chip.base = -1; - jz4740->chip.of_xlate = of_pwm_xlate_with_flags; - jz4740->chip.of_pwm_n_cells = 3; - - platform_set_drvdata(pdev, jz4740); - - return pwmchip_add(&jz4740->chip); -} - -static int jz4740_pwm_remove(struct platform_device *pdev) -{ - struct jz4740_pwm_chip *jz4740 = platform_get_drvdata(pdev); + chip->ops = &jz4740_pwm_ops; - return pwmchip_remove(&jz4740->chip); + return devm_pwmchip_add(dev, chip); } -static const struct soc_info __maybe_unused jz4740_soc_info = { +static const struct soc_info jz4740_soc_info = { .num_pwms = 8, }; -static const struct soc_info __maybe_unused jz4725b_soc_info = { +static const struct soc_info jz4725b_soc_info = { .num_pwms = 6, }; -#ifdef CONFIG_OF +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); -#endif static struct platform_driver jz4740_pwm_driver = { .driver = { .name = "jz4740-pwm", - .of_match_table = of_match_ptr(jz4740_pwm_dt_ids), + .of_match_table = jz4740_pwm_dt_ids, }, .probe = jz4740_pwm_probe, - .remove = jz4740_pwm_remove, }; module_platform_driver(jz4740_pwm_driver); diff --git a/drivers/pwm/pwm-keembay.c b/drivers/pwm/pwm-keembay.c new file mode 100644 index 000000000000..35b641f3f6ed --- /dev/null +++ b/drivers/pwm/pwm-keembay.c @@ -0,0 +1,234 @@ +// 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/mod_devicetable.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, ¤t_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 000000000000..31a57edecfd0 --- /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[] = { + { "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 index 7551253ada32..90b0733c00c1 100644 --- a/drivers/pwm/pwm-lp3943.c +++ b/drivers/pwm/pwm-lp3943.c @@ -8,9 +8,9 @@ */ #include <linux/err.h> -#include <linux/i2c.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> @@ -20,14 +20,14 @@ #define LP3943_MAX_PERIOD 1600000 struct lp3943_pwm { - struct pwm_chip chip; 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) +static inline struct lp3943_pwm *to_lp3943_pwm(struct pwm_chip *chip) { - return container_of(_chip, struct lp3943_pwm, chip); + return pwmchip_get_drvdata(chip); } static struct lp3943_pwm_map * @@ -35,13 +35,9 @@ 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; + struct lp3943_pwm_map *pwm_map = &lp3943_pwm->pwm_map[hwpwm]; int i, offset; - pwm_map = kzalloc(sizeof(*pwm_map), GFP_KERNEL); - if (!pwm_map) - return ERR_PTR(-ENOMEM); - pwm_map->output = pdata->pwms[hwpwm]->output; pwm_map->num_outputs = pdata->pwms[hwpwm]->num_outputs; @@ -49,10 +45,8 @@ lp3943_pwm_request_map(struct lp3943_pwm *lp3943_pwm, int hwpwm) offset = pwm_map->output[i]; /* Return an error if the pin is already assigned */ - if (test_and_set_bit(offset, &lp3943->pin_used)) { - kfree(pwm_map); + if (test_and_set_bit(offset, &lp3943->pin_used)) return ERR_PTR(-EBUSY); - } } return pwm_map; @@ -67,7 +61,7 @@ static int lp3943_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm) if (IS_ERR(pwm_map)) return PTR_ERR(pwm_map); - return pwm_set_chip_data(pwm, pwm_map); + return 0; } static void lp3943_pwm_free_map(struct lp3943_pwm *lp3943_pwm, @@ -80,20 +74,18 @@ static void lp3943_pwm_free_map(struct lp3943_pwm *lp3943_pwm, offset = pwm_map->output[i]; clear_bit(offset, &lp3943->pin_used); } - - kfree(pwm_map); } 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 = pwm_get_chip_data(pwm); + 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, - int duty_ns, int period_ns) + u64 duty_ns, u64 period_ns) { struct lp3943_pwm *lp3943_pwm = to_lp3943_pwm(chip); struct lp3943 *lp3943 = lp3943_pwm->lp3943; @@ -118,14 +110,20 @@ static int lp3943_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, reg_duty = LP3943_REG_PWM1; } - period_ns = clamp(period_ns, LP3943_MIN_PERIOD, LP3943_MAX_PERIOD); - val = (u8)(period_ns / LP3943_MIN_PERIOD - 1); + /* + * 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; - val = (u8)(duty_ns * LP3943_MAX_DUTY / period_ns); + 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); } @@ -153,7 +151,7 @@ static int lp3943_pwm_set_mode(struct lp3943_pwm *lp3943_pwm, 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 = pwm_get_chip_data(pwm); + struct lp3943_pwm_map *pwm_map = &lp3943_pwm->pwm_map[pwm->hwpwm]; u8 val; if (pwm->hwpwm == 0) @@ -172,7 +170,7 @@ static int lp3943_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) 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 = pwm_get_chip_data(pwm); + struct lp3943_pwm_map *pwm_map = &lp3943_pwm->pwm_map[pwm->hwpwm]; /* * LP3943 outputs are open-drain, so the pin should be configured @@ -182,13 +180,34 @@ static void lp3943_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) 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, - .config = lp3943_pwm_config, - .enable = lp3943_pwm_enable, - .disable = lp3943_pwm_disable, - .owner = THIS_MODULE, + .apply = lp3943_pwm_apply, }; static int lp3943_pwm_parse_dt(struct device *dev, @@ -199,8 +218,7 @@ static int lp3943_pwm_parse_dt(struct device *dev, struct lp3943_platform_data *pdata; struct lp3943_pwm_map *pwm_map; enum lp3943_pwm_output *output; - int i, err, proplen, count = 0; - u32 num_outputs; + int i, err, num_outputs, count = 0; if (!node) return -EINVAL; @@ -215,11 +233,8 @@ static int lp3943_pwm_parse_dt(struct device *dev, */ for (i = 0; i < LP3943_NUM_PWMS; i++) { - if (!of_get_property(node, name[i], &proplen)) - continue; - - num_outputs = proplen / sizeof(u32); - if (num_outputs == 0) + num_outputs = of_property_count_u32_elems(node, name[i]); + if (num_outputs <= 0) continue; output = devm_kcalloc(dev, num_outputs, sizeof(*output), @@ -253,12 +268,14 @@ static int lp3943_pwm_parse_dt(struct device *dev, 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; - lp3943_pwm = devm_kzalloc(&pdev->dev, sizeof(*lp3943_pwm), GFP_KERNEL); - if (!lp3943_pwm) - return -ENOMEM; + 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) { @@ -272,20 +289,9 @@ static int lp3943_pwm_probe(struct platform_device *pdev) } lp3943_pwm->lp3943 = lp3943; - lp3943_pwm->chip.dev = &pdev->dev; - lp3943_pwm->chip.ops = &lp3943_pwm_ops; - lp3943_pwm->chip.npwm = LP3943_NUM_PWMS; - - platform_set_drvdata(pdev, lp3943_pwm); - - return pwmchip_add(&lp3943_pwm->chip); -} - -static int lp3943_pwm_remove(struct platform_device *pdev) -{ - struct lp3943_pwm *lp3943_pwm = platform_get_drvdata(pdev); + chip->ops = &lp3943_pwm_ops; - return pwmchip_remove(&lp3943_pwm->chip); + return devm_pwmchip_add(&pdev->dev, chip); } #ifdef CONFIG_OF @@ -298,7 +304,6 @@ MODULE_DEVICE_TABLE(of, lp3943_pwm_of_match); static struct platform_driver lp3943_pwm_driver = { .probe = lp3943_pwm_probe, - .remove = lp3943_pwm_remove, .driver = { .name = "lp3943-pwm", .of_match_table = of_match_ptr(lp3943_pwm_of_match), diff --git a/drivers/pwm/pwm-lpc18xx-sct.c b/drivers/pwm/pwm-lpc18xx-sct.c index 5ff11145c1a3..1e614b2a0227 100644 --- a/drivers/pwm/pwm-lpc18xx-sct.c +++ b/drivers/pwm/pwm-lpc18xx-sct.c @@ -22,6 +22,7 @@ #include <linux/clk.h> #include <linux/err.h> #include <linux/io.h> +#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/platform_device.h> #include <linux/pwm.h> @@ -76,6 +77,8 @@ #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, @@ -89,24 +92,21 @@ struct lpc18xx_pwm_data { }; struct lpc18xx_pwm_chip { - struct device *dev; - struct pwm_chip chip; void __iomem *base; struct clk *pwm_clk; unsigned long clk_rate; unsigned int period_ns; unsigned int min_period_ns; - unsigned int max_period_ns; + u64 max_period_ns; unsigned int period_event; unsigned long event_map; - struct mutex res_lock; - struct mutex period_lock; + struct lpc18xx_pwm_data channeldata[LPC18XX_NUM_PWMS]; }; static inline struct lpc18xx_pwm_chip * to_lpc18xx_pwm_chip(struct pwm_chip *chip) { - return container_of(chip, struct lpc18xx_pwm_chip, chip); + return pwmchip_get_drvdata(chip); } static inline void lpc18xx_pwm_writel(struct lpc18xx_pwm_chip *lpc18xx_pwm, @@ -127,8 +127,6 @@ static void lpc18xx_pwm_set_conflict_res(struct lpc18xx_pwm_chip *lpc18xx_pwm, { u32 val; - mutex_lock(&lpc18xx_pwm->res_lock); - /* * 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 @@ -138,60 +136,64 @@ static void lpc18xx_pwm_set_conflict_res(struct lpc18xx_pwm_chip *lpc18xx_pwm, val &= ~LPC18XX_PWM_RES_MASK(pwm->hwpwm); val |= LPC18XX_PWM_RES(pwm->hwpwm, action); lpc18xx_pwm_writel(lpc18xx_pwm, LPC18XX_PWM_RES_BASE, val); - - mutex_unlock(&lpc18xx_pwm->res_lock); } -static void lpc18xx_pwm_config_period(struct pwm_chip *chip, int period_ns) +static void lpc18xx_pwm_config_period(struct pwm_chip *chip, u64 period_ns) { struct lpc18xx_pwm_chip *lpc18xx_pwm = to_lpc18xx_pwm_chip(chip); - u64 val; + u32 val; - val = (u64)period_ns * lpc18xx_pwm->clk_rate; - do_div(val, NSEC_PER_SEC); + /* + * 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), - (u32)val - 1); + val - 1); lpc18xx_pwm_writel(lpc18xx_pwm, LPC18XX_PWM_MATCHREL(lpc18xx_pwm->period_event), - (u32)val - 1); + val - 1); } static void lpc18xx_pwm_config_duty(struct pwm_chip *chip, - struct pwm_device *pwm, int duty_ns) + struct pwm_device *pwm, u64 duty_ns) { struct lpc18xx_pwm_chip *lpc18xx_pwm = to_lpc18xx_pwm_chip(chip); - struct lpc18xx_pwm_data *lpc18xx_data = pwm_get_chip_data(pwm); - u64 val; + struct lpc18xx_pwm_data *lpc18xx_data = &lpc18xx_pwm->channeldata[pwm->hwpwm]; + u32 val; - val = (u64)duty_ns * lpc18xx_pwm->clk_rate; - do_div(val, NSEC_PER_SEC); + /* + * 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), - (u32)val); + val); lpc18xx_pwm_writel(lpc18xx_pwm, LPC18XX_PWM_MATCHREL(lpc18xx_data->duty_event), - (u32)val); + 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, i; + int requested_events; if (period_ns < lpc18xx_pwm->min_period_ns || period_ns > lpc18xx_pwm->max_period_ns) { - dev_err(chip->dev, "period %d not in range\n", period_ns); + dev_err(pwmchip_parent(chip), "period %d not in range\n", period_ns); return -ERANGE; } - mutex_lock(&lpc18xx_pwm->period_lock); - requested_events = bitmap_weight(&lpc18xx_pwm->event_map, LPC18XX_PWM_EVENT_MAX); @@ -202,38 +204,26 @@ static int lpc18xx_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, */ if (requested_events > 2 && lpc18xx_pwm->period_ns != period_ns && lpc18xx_pwm->period_ns) { - dev_err(chip->dev, "conflicting period requested for PWM %u\n", + dev_err(pwmchip_parent(chip), "conflicting period requested for PWM %u\n", pwm->hwpwm); - mutex_unlock(&lpc18xx_pwm->period_lock); return -EBUSY; } if ((requested_events <= 2 && lpc18xx_pwm->period_ns != period_ns) || !lpc18xx_pwm->period_ns) { lpc18xx_pwm->period_ns = period_ns; - for (i = 0; i < chip->npwm; i++) - pwm_set_period(&chip->pwms[i], period_ns); lpc18xx_pwm_config_period(chip, period_ns); } - mutex_unlock(&lpc18xx_pwm->period_lock); - lpc18xx_pwm_config_duty(chip, pwm, duty_ns); return 0; } -static int lpc18xx_pwm_set_polarity(struct pwm_chip *chip, - struct pwm_device *pwm, - enum pwm_polarity polarity) -{ - return 0; -} - -static int lpc18xx_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) +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 = pwm_get_chip_data(pwm); + struct lpc18xx_pwm_data *lpc18xx_data = &lpc18xx_pwm->channeldata[pwm->hwpwm]; enum lpc18xx_pwm_res_action res_action; unsigned int set_event, clear_event; @@ -246,7 +236,7 @@ static int lpc18xx_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) LPC18XX_PWM_EVSTATEMSK(lpc18xx_data->duty_event), LPC18XX_PWM_EVSTATEMSK_ALL); - if (pwm_get_polarity(pwm) == PWM_POLARITY_NORMAL) { + if (polarity == PWM_POLARITY_NORMAL) { set_event = lpc18xx_pwm->period_event; clear_event = lpc18xx_data->duty_event; res_action = LPC18XX_PWM_RES_SET; @@ -268,7 +258,7 @@ static int lpc18xx_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) 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 = pwm_get_chip_data(pwm); + struct lpc18xx_pwm_data *lpc18xx_data = &lpc18xx_pwm->channeldata[pwm->hwpwm]; lpc18xx_pwm_writel(lpc18xx_pwm, LPC18XX_PWM_EVCTRL(lpc18xx_data->duty_event), 0); @@ -279,17 +269,17 @@ static void lpc18xx_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) 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 = pwm_get_chip_data(pwm); + 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(lpc18xx_pwm->dev, + 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; @@ -300,19 +290,42 @@ static int lpc18xx_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm) 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 = pwm_get_chip_data(pwm); + 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 = { - .config = lpc18xx_pwm_config, - .set_polarity = lpc18xx_pwm_set_polarity, - .enable = lpc18xx_pwm_enable, - .disable = lpc18xx_pwm_disable, + .apply = lpc18xx_pwm_apply, .request = lpc18xx_pwm_request, .free = lpc18xx_pwm_free, - .owner = THIS_MODULE, }; static const struct of_device_id lpc18xx_pwm_of_match[] = { @@ -323,59 +336,43 @@ 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; - struct pwm_device *pwm; - struct resource *res; - int ret, i; + int ret; u64 val; - lpc18xx_pwm = devm_kzalloc(&pdev->dev, sizeof(*lpc18xx_pwm), - GFP_KERNEL); - if (!lpc18xx_pwm) - return -ENOMEM; - - lpc18xx_pwm->dev = &pdev->dev; + 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); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - lpc18xx_pwm->base = devm_ioremap_resource(&pdev->dev, res); + 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(&pdev->dev, "pwm"); - if (IS_ERR(lpc18xx_pwm->pwm_clk)) { - dev_err(&pdev->dev, "failed to get pwm clock\n"); - return PTR_ERR(lpc18xx_pwm->pwm_clk); - } - - ret = clk_prepare_enable(lpc18xx_pwm->pwm_clk); - if (ret < 0) { - dev_err(&pdev->dev, "could not prepare or enable pwm clock\n"); - return ret; - } + 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) { - dev_err(&pdev->dev, "pwm clock has no frequency\n"); - ret = -EINVAL; - goto disable_pwmclk; - } + if (!lpc18xx_pwm->clk_rate) + return dev_err_probe(&pdev->dev, + -EINVAL, "pwm clock has no frequency\n"); - mutex_init(&lpc18xx_pwm->res_lock); - mutex_init(&lpc18xx_pwm->period_lock); + /* + * 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"); - val = (u64)NSEC_PER_SEC * LPC18XX_PWM_TIMER_MAX; - do_div(val, lpc18xx_pwm->clk_rate); - lpc18xx_pwm->max_period_ns = val; + 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); - lpc18xx_pwm->chip.dev = &pdev->dev; - lpc18xx_pwm->chip.ops = &lpc18xx_pwm_ops; - lpc18xx_pwm->chip.base = -1; - lpc18xx_pwm->chip.npwm = 16; - lpc18xx_pwm->chip.of_xlate = of_pwm_xlate_with_flags; - lpc18xx_pwm->chip.of_pwm_n_cells = 3; + chip->ops = &lpc18xx_pwm_ops; /* SCT counter must be in unify (32 bit) mode */ lpc18xx_pwm_writel(lpc18xx_pwm, LPC18XX_PWM_CONFIG, @@ -400,29 +397,6 @@ static int lpc18xx_pwm_probe(struct platform_device *pdev) lpc18xx_pwm_writel(lpc18xx_pwm, LPC18XX_PWM_LIMIT, BIT(lpc18xx_pwm->period_event)); - ret = pwmchip_add(&lpc18xx_pwm->chip); - if (ret < 0) { - dev_err(&pdev->dev, "pwmchip_add failed: %d\n", ret); - goto disable_pwmclk; - } - - for (i = 0; i < lpc18xx_pwm->chip.npwm; i++) { - struct lpc18xx_pwm_data *data; - - pwm = &lpc18xx_pwm->chip.pwms[i]; - - data = devm_kzalloc(lpc18xx_pwm->dev, sizeof(*data), - GFP_KERNEL); - if (!data) { - ret = -ENOMEM; - goto remove_pwmchip; - } - - pwm_set_chip_data(pwm, data); - } - - platform_set_drvdata(pdev, lpc18xx_pwm); - val = lpc18xx_pwm_readl(lpc18xx_pwm, LPC18XX_PWM_CTRL); val &= ~LPC18XX_PWM_BIDIR; val &= ~LPC18XX_PWM_CTRL_HALT; @@ -430,27 +404,26 @@ static int lpc18xx_pwm_probe(struct platform_device *pdev) val |= LPC18XX_PWM_PRE(0); lpc18xx_pwm_writel(lpc18xx_pwm, LPC18XX_PWM_CTRL, val); - return 0; + ret = pwmchip_add(chip); + if (ret < 0) + return dev_err_probe(&pdev->dev, ret, "pwmchip_add failed\n"); -remove_pwmchip: - pwmchip_remove(&lpc18xx_pwm->chip); -disable_pwmclk: - clk_disable_unprepare(lpc18xx_pwm->pwm_clk); - return ret; + platform_set_drvdata(pdev, chip); + + return 0; } -static int lpc18xx_pwm_remove(struct platform_device *pdev) +static void lpc18xx_pwm_remove(struct platform_device *pdev) { - struct lpc18xx_pwm_chip *lpc18xx_pwm = platform_get_drvdata(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); - - clk_disable_unprepare(lpc18xx_pwm->pwm_clk); - - return pwmchip_remove(&lpc18xx_pwm->chip); } static struct platform_driver lpc18xx_pwm_driver = { diff --git a/drivers/pwm/pwm-lpc32xx.c b/drivers/pwm/pwm-lpc32xx.c index 710d9a207d2b..c748537e57d1 100644 --- a/drivers/pwm/pwm-lpc32xx.c +++ b/drivers/pwm/pwm-lpc32xx.c @@ -15,7 +15,6 @@ #include <linux/slab.h> struct lpc32xx_pwm_chip { - struct pwm_chip chip; struct clk *clk; void __iomem *base; }; @@ -23,8 +22,10 @@ struct lpc32xx_pwm_chip { #define PWM_ENABLE BIT(31) #define PWM_PIN_LEVEL BIT(30) -#define to_lpc32xx_pwm_chip(_chip) \ - container_of(_chip, struct lpc32xx_pwm_chip, chip) +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) @@ -51,10 +52,10 @@ static int lpc32xx_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, if (duty_cycles > 255) duty_cycles = 255; - val = readl(lpc32xx->base + (pwm->hwpwm << 2)); + val = readl(lpc32xx->base); val &= ~0xFFFF; val |= (period_cycles << 8) | duty_cycles; - writel(val, lpc32xx->base + (pwm->hwpwm << 2)); + writel(val, lpc32xx->base); return 0; } @@ -69,9 +70,9 @@ static int lpc32xx_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) if (ret) return ret; - val = readl(lpc32xx->base + (pwm->hwpwm << 2)); + val = readl(lpc32xx->base); val |= PWM_ENABLE; - writel(val, lpc32xx->base + (pwm->hwpwm << 2)); + writel(val, lpc32xx->base); return 0; } @@ -81,33 +82,55 @@ 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 + (pwm->hwpwm << 2)); + val = readl(lpc32xx->base); val &= ~PWM_ENABLE; - writel(val, lpc32xx->base + (pwm->hwpwm << 2)); + 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 = { - .config = lpc32xx_pwm_config, - .enable = lpc32xx_pwm_enable, - .disable = lpc32xx_pwm_disable, - .owner = THIS_MODULE, + .apply = lpc32xx_pwm_apply, }; static int lpc32xx_pwm_probe(struct platform_device *pdev) { + struct pwm_chip *chip; struct lpc32xx_pwm_chip *lpc32xx; - struct resource *res; int ret; u32 val; - lpc32xx = devm_kzalloc(&pdev->dev, sizeof(*lpc32xx), GFP_KERNEL); - if (!lpc32xx) - return -ENOMEM; + chip = devm_pwmchip_alloc(&pdev->dev, 1, sizeof(*lpc32xx)); + if (IS_ERR(chip)) + return PTR_ERR(chip); + lpc32xx = to_lpc32xx_pwm_chip(chip); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - lpc32xx->base = devm_ioremap_resource(&pdev->dev, res); + lpc32xx->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(lpc32xx->base)) return PTR_ERR(lpc32xx->base); @@ -115,38 +138,22 @@ static int lpc32xx_pwm_probe(struct platform_device *pdev) if (IS_ERR(lpc32xx->clk)) return PTR_ERR(lpc32xx->clk); - lpc32xx->chip.dev = &pdev->dev; - lpc32xx->chip.ops = &lpc32xx_pwm_ops; - lpc32xx->chip.npwm = 1; - lpc32xx->chip.base = -1; + 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 = pwmchip_add(&lpc32xx->chip); + 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; } - /* When PWM is disable, configure the output to the default value */ - val = readl(lpc32xx->base + (lpc32xx->chip.pwms[0].hwpwm << 2)); - val &= ~PWM_PIN_LEVEL; - writel(val, lpc32xx->base + (lpc32xx->chip.pwms[0].hwpwm << 2)); - - platform_set_drvdata(pdev, lpc32xx); - return 0; } -static int lpc32xx_pwm_remove(struct platform_device *pdev) -{ - struct lpc32xx_pwm_chip *lpc32xx = platform_get_drvdata(pdev); - unsigned int i; - - for (i = 0; i < lpc32xx->chip.npwm; i++) - pwm_disable(&lpc32xx->chip.pwms[i]); - - return pwmchip_remove(&lpc32xx->chip); -} - static const struct of_device_id lpc32xx_pwm_dt_ids[] = { { .compatible = "nxp,lpc3220-pwm", }, { /* sentinel */ } @@ -159,7 +166,6 @@ static struct platform_driver lpc32xx_pwm_driver = { .of_match_table = lpc32xx_pwm_dt_ids, }, .probe = lpc32xx_pwm_probe, - .remove = lpc32xx_pwm_remove, }; module_platform_driver(lpc32xx_pwm_driver); diff --git a/drivers/pwm/pwm-lpss-pci.c b/drivers/pwm/pwm-lpss-pci.c index cf749ea0de9f..ae25d9321d75 100644 --- a/drivers/pwm/pwm-lpss-pci.c +++ b/drivers/pwm/pwm-lpss-pci.c @@ -14,52 +14,26 @@ #include "pwm-lpss.h" -/* BayTrail */ -static const struct pwm_lpss_boardinfo pwm_lpss_byt_info = { - .clk_rate = 25000000, - .npwm = 1, - .base_unit_bits = 16, -}; - -/* Braswell */ -static const struct pwm_lpss_boardinfo pwm_lpss_bsw_info = { - .clk_rate = 19200000, - .npwm = 1, - .base_unit_bits = 16, -}; - -/* Broxton */ -static const struct pwm_lpss_boardinfo pwm_lpss_bxt_info = { - .clk_rate = 19200000, - .npwm = 4, - .base_unit_bits = 22, - .bypass = true, -}; - -/* Tangier */ -static const struct pwm_lpss_boardinfo pwm_lpss_tng_info = { - .clk_rate = 19200000, - .npwm = 4, - .base_unit_bits = 22, -}; - static int pwm_lpss_probe_pci(struct pci_dev *pdev, const struct pci_device_id *id) { const struct pwm_lpss_boardinfo *info; - struct pwm_lpss_chip *lpwm; + void __iomem *io_base; + struct pwm_chip *chip; int err; err = pcim_enable_device(pdev); if (err < 0) return err; - info = (struct pwm_lpss_boardinfo *)id->driver_data; - lpwm = pwm_lpss_probe(&pdev->dev, &pdev->resource[0], info); - if (IS_ERR(lpwm)) - return PTR_ERR(lpwm); + io_base = pcim_iomap_region(pdev, 0, "pwm-lpss"); + if (IS_ERR(io_base)) + return PTR_ERR(io_base); - pci_set_drvdata(pdev, lpwm); + 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); @@ -69,35 +43,10 @@ static int pwm_lpss_probe_pci(struct pci_dev *pdev, static void pwm_lpss_remove_pci(struct pci_dev *pdev) { - struct pwm_lpss_chip *lpwm = pci_get_drvdata(pdev); - pm_runtime_forbid(&pdev->dev); pm_runtime_get_sync(&pdev->dev); - - pwm_lpss_remove(lpwm); -} - -#ifdef CONFIG_PM -static int pwm_lpss_runtime_suspend_pci(struct device *dev) -{ - /* - * The PCI core will handle transition to D3 automatically. We only - * need to provide runtime PM hooks for that to happen. - */ - return 0; } -static int pwm_lpss_runtime_resume_pci(struct device *dev) -{ - return 0; -} -#endif - -static const struct dev_pm_ops pwm_lpss_pci_pm = { - SET_RUNTIME_PM_OPS(pwm_lpss_runtime_suspend_pci, - pwm_lpss_runtime_resume_pci, NULL) -}; - static const struct pci_device_id pwm_lpss_pci_ids[] = { { PCI_VDEVICE(INTEL, 0x0ac8), (unsigned long)&pwm_lpss_bxt_info}, { PCI_VDEVICE(INTEL, 0x0f08), (unsigned long)&pwm_lpss_byt_info}, @@ -117,11 +66,9 @@ static struct pci_driver pwm_lpss_driver_pci = { .id_table = pwm_lpss_pci_ids, .probe = pwm_lpss_probe_pci, .remove = pwm_lpss_remove_pci, - .driver = { - .pm = &pwm_lpss_pci_pm, - }, }; 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 index 48f34d20aecd..653ec9d0c8bf 100644 --- a/drivers/pwm/pwm-lpss-platform.c +++ b/drivers/pwm/pwm-lpss-platform.c @@ -7,91 +7,57 @@ * Derived from the original pwm-lpss.c */ -#include <linux/acpi.h> #include <linux/kernel.h> +#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/platform_device.h> #include <linux/pm_runtime.h> +#include <linux/property.h> #include "pwm-lpss.h" -/* BayTrail */ -static const struct pwm_lpss_boardinfo pwm_lpss_byt_info = { - .clk_rate = 25000000, - .npwm = 1, - .base_unit_bits = 16, -}; - -/* Braswell */ -static 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, -}; - -/* Broxton */ -static const struct pwm_lpss_boardinfo pwm_lpss_bxt_info = { - .clk_rate = 19200000, - .npwm = 4, - .base_unit_bits = 22, - .bypass = true, -}; static int pwm_lpss_probe_platform(struct platform_device *pdev) { const struct pwm_lpss_boardinfo *info; - const struct acpi_device_id *id; - struct pwm_lpss_chip *lpwm; - struct resource *r; + struct pwm_chip *chip; + void __iomem *base; - id = acpi_match_device(pdev->dev.driver->acpi_match_table, &pdev->dev); - if (!id) + info = device_get_match_data(&pdev->dev); + if (!info) return -ENODEV; - info = (const struct pwm_lpss_boardinfo *)id->driver_data; - r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(base)) + return PTR_ERR(base); - lpwm = pwm_lpss_probe(&pdev->dev, r, info); - if (IS_ERR(lpwm)) - return PTR_ERR(lpwm); - - platform_set_drvdata(pdev, lpwm); - - dev_pm_set_driver_flags(&pdev->dev, DPM_FLAG_SMART_PREPARE); - pm_runtime_set_active(&pdev->dev); - pm_runtime_enable(&pdev->dev); - - return 0; -} - -static int pwm_lpss_remove_platform(struct platform_device *pdev) -{ - struct pwm_lpss_chip *lpwm = platform_get_drvdata(pdev); - - pm_runtime_disable(&pdev->dev); - return pwm_lpss_remove(lpwm); -} - -static int pwm_lpss_prepare(struct device *dev) -{ - struct pwm_lpss_chip *lpwm = dev_get_drvdata(dev); + chip = devm_pwm_lpss_probe(&pdev->dev, base, info); + if (IS_ERR(chip)) + return PTR_ERR(chip); /* - * If other device's AML code touches the PWM regs on suspend/resume - * force runtime-resume the PWM controller to allow this. + * 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 (lpwm->info->other_devices_aml_touches_pwm_regs) - return 0; /* Force runtime-resume */ + if (info->other_devices_aml_touches_pwm_regs) + dev_pm_set_driver_flags(&pdev->dev, DPM_FLAG_NO_DIRECT_COMPLETE| + DPM_FLAG_SMART_SUSPEND); - return 1; /* If runtime-suspended leave as is */ + pm_runtime_set_active(&pdev->dev); + return devm_pm_runtime_enable(&pdev->dev); } -static const struct dev_pm_ops pwm_lpss_platform_pm_ops = { - .prepare = pwm_lpss_prepare, - SET_SYSTEM_SLEEP_PM_OPS(pwm_lpss_suspend, pwm_lpss_resume) -}; - static const struct acpi_device_id pwm_lpss_acpi_match[] = { { "80860F09", (unsigned long)&pwm_lpss_byt_info }, { "80862288", (unsigned long)&pwm_lpss_bsw_info }, @@ -105,13 +71,12 @@ static struct platform_driver pwm_lpss_driver_platform = { .driver = { .name = "pwm-lpss", .acpi_match_table = pwm_lpss_acpi_match, - .pm = &pwm_lpss_platform_pm_ops, }, .probe = pwm_lpss_probe_platform, - .remove = pwm_lpss_remove_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 index 9d965ffe66d1..c976ff1c8ed9 100644 --- a/drivers/pwm/pwm-lpss.c +++ b/drivers/pwm/pwm-lpss.c @@ -10,12 +10,16 @@ * 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" @@ -24,14 +28,48 @@ #define PWM_ENABLE BIT(31) #define PWM_SW_UPDATE BIT(30) #define PWM_BASE_UNIT_SHIFT 8 -#define PWM_ON_TIME_DIV_MASK 0x000000ff +#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 container_of(chip, struct pwm_lpss_chip, chip); + return pwmchip_get_drvdata(chip); } static inline u32 pwm_lpss_read(const struct pwm_device *pwm) @@ -69,14 +107,19 @@ static int pwm_lpss_wait_for_update(struct pwm_device *pwm) */ err = readl_poll_timeout(addr, val, !(val & PWM_SW_UPDATE), 40, ms); if (err) - dev_err(pwm->chip->dev, "PWM_SW_UPDATE was not cleared\n"); + 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) { - return (pwm_lpss_read(pwm) & PWM_SW_UPDATE) ? -EBUSY : 0; + 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, @@ -85,7 +128,7 @@ static void pwm_lpss_prepare(struct pwm_lpss_chip *lpwm, struct pwm_device *pwm, 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 orig_ctrl, ctrl; + u32 ctrl; do_div(freq, period_ns); @@ -93,26 +136,25 @@ static void pwm_lpss_prepare(struct pwm_lpss_chip *lpwm, struct pwm_device *pwm, * The equation is: * base_unit = round(base_unit_range * freq / c) */ - base_unit_range = BIT(lpwm->info->base_unit_bits) - 1; + 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; - orig_ctrl = ctrl = pwm_lpss_read(pwm); + ctrl = pwm_lpss_read(pwm); ctrl &= ~PWM_ON_TIME_DIV_MASK; - ctrl &= ~(base_unit_range << PWM_BASE_UNIT_SHIFT); - base_unit &= base_unit_range; + ctrl &= ~((base_unit_range - 1) << PWM_BASE_UNIT_SHIFT); ctrl |= (u32) base_unit << PWM_BASE_UNIT_SHIFT; ctrl |= on_time_div; - if (orig_ctrl != ctrl) { - pwm_lpss_write(pwm, ctrl); - pwm_lpss_write(pwm, ctrl | PWM_SW_UPDATE); - } + 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) @@ -121,52 +163,58 @@ static inline void pwm_lpss_cond_enable(struct pwm_device *pwm, bool 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; + int ret = 0; if (state->enabled) { if (!pwm_is_enabled(pwm)) { - pm_runtime_get_sync(chip->dev); - ret = pwm_lpss_is_updating(pwm); - if (ret) { - pm_runtime_put(chip->dev); - 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) { - pm_runtime_put(chip->dev); - return ret; - } - pwm_lpss_cond_enable(pwm, lpwm->info->bypass == true); - } else { - ret = pwm_lpss_is_updating(pwm); + pm_runtime_get_sync(pwmchip_parent(chip)); + ret = pwm_lpss_prepare_enable(lpwm, pwm, state); if (ret) - return ret; - pwm_lpss_prepare(lpwm, pwm, state->duty_cycle, state->period); - return pwm_lpss_wait_for_update(pwm); + 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(chip->dev); + pm_runtime_put(pwmchip_parent(chip)); } - return 0; + return ret; } -static void pwm_lpss_get_state(struct pwm_chip *chip, struct pwm_device *pwm, - struct pwm_state *state) +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(chip->dev); + pm_runtime_get_sync(pwmchip_parent(chip)); base_unit_range = BIT(lpwm->info->base_unit_bits); @@ -188,96 +236,57 @@ static void pwm_lpss_get_state(struct pwm_chip *chip, struct pwm_device *pwm, state->polarity = PWM_POLARITY_NORMAL; state->enabled = !!(ctrl & PWM_ENABLE); - pm_runtime_put(chip->dev); + 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, - .owner = THIS_MODULE, }; -struct pwm_lpss_chip *pwm_lpss_probe(struct device *dev, struct resource *r, +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 > MAX_PWMS)) + if (WARN_ON(info->npwm > LPSS_MAX_PWMS)) return ERR_PTR(-ENODEV); - lpwm = devm_kzalloc(dev, sizeof(*lpwm), GFP_KERNEL); - if (!lpwm) - return ERR_PTR(-ENOMEM); - - lpwm->regs = devm_ioremap_resource(dev, r); - if (IS_ERR(lpwm->regs)) - return ERR_CAST(lpwm->regs); + 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); - lpwm->chip.dev = dev; - lpwm->chip.ops = &pwm_lpss_ops; - lpwm->chip.base = -1; - lpwm->chip.npwm = info->npwm; + chip->ops = &pwm_lpss_ops; - ret = pwmchip_add(&lpwm->chip); + 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(&lpwm->chip.pwms[i]); + ctrl = pwm_lpss_read(&chip->pwms[i]); if (ctrl & PWM_ENABLE) pm_runtime_get(dev); } - return lpwm; -} -EXPORT_SYMBOL_GPL(pwm_lpss_probe); - -int pwm_lpss_remove(struct pwm_lpss_chip *lpwm) -{ - int i; - - for (i = 0; i < lpwm->info->npwm; i++) { - if (pwm_is_enabled(&lpwm->chip.pwms[i])) - pm_runtime_put(lpwm->chip.dev); - } - return pwmchip_remove(&lpwm->chip); -} -EXPORT_SYMBOL_GPL(pwm_lpss_remove); - -int pwm_lpss_suspend(struct device *dev) -{ - struct pwm_lpss_chip *lpwm = dev_get_drvdata(dev); - int i; - - for (i = 0; i < lpwm->info->npwm; i++) - lpwm->saved_ctrl[i] = readl(lpwm->regs + i * PWM_SIZE + PWM); - - return 0; -} -EXPORT_SYMBOL_GPL(pwm_lpss_suspend); - -int pwm_lpss_resume(struct device *dev) -{ - struct pwm_lpss_chip *lpwm = dev_get_drvdata(dev); - int i; - - for (i = 0; i < lpwm->info->npwm; i++) - writel(lpwm->saved_ctrl[i], lpwm->regs + i * PWM_SIZE + PWM); - - return 0; + return chip; } -EXPORT_SYMBOL_GPL(pwm_lpss_resume); +EXPORT_SYMBOL_GPL(devm_pwm_lpss_probe); MODULE_DESCRIPTION("PWM driver for Intel LPSS"); MODULE_AUTHOR("Mika Westerberg <mika.westerberg@linux.intel.com>"); diff --git a/drivers/pwm/pwm-lpss.h b/drivers/pwm/pwm-lpss.h index 7909fa12fca2..60792181401e 100644 --- a/drivers/pwm/pwm-lpss.h +++ b/drivers/pwm/pwm-lpss.h @@ -10,34 +10,20 @@ #ifndef __PWM_LPSS_H #define __PWM_LPSS_H -#include <linux/device.h> -#include <linux/pwm.h> +#include <linux/types.h> -#define MAX_PWMS 4 +#include <linux/platform_data/x86/pwm-lpss.h> + +#define LPSS_MAX_PWMS 4 struct pwm_lpss_chip { - struct pwm_chip chip; void __iomem *regs; const struct pwm_lpss_boardinfo *info; - u32 saved_ctrl[MAX_PWMS]; -}; - -struct pwm_lpss_boardinfo { - unsigned long clk_rate; - unsigned int npwm; - unsigned long base_unit_bits; - bool bypass; - /* - * On some devices the _PS0/_PS3 AML code of the GPU (GFX0) device - * messes with the PWM0 controllers state, - */ - bool other_devices_aml_touches_pwm_regs; }; -struct pwm_lpss_chip *pwm_lpss_probe(struct device *dev, struct resource *r, - const struct pwm_lpss_boardinfo *info); -int pwm_lpss_remove(struct pwm_lpss_chip *lpwm); -int pwm_lpss_suspend(struct device *dev); -int pwm_lpss_resume(struct device *dev); +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 000000000000..16261958ce7f --- /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/mod_devicetable.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 = { + .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 000000000000..6d99e3ff7239 --- /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, ®, &val, 1); +} + +static int mc33xs2410_read_reg(struct spi_device *spi, u8 reg, u16 *val, u8 flag) +{ + return mc33xs2410_read_regs(spi, ®, flag, val, 1); +} + +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, ®[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[] = { + { "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 index b94e0d09c300..9d206303404a 100644 --- a/drivers/pwm/pwm-mediatek.c +++ b/drivers/pwm/pwm-mediatek.c @@ -7,6 +7,7 @@ * */ +#include <linux/bitfield.h> #include <linux/err.h> #include <linux/io.h> #include <linux/ioport.h> @@ -14,7 +15,6 @@ #include <linux/module.h> #include <linux/clk.h> #include <linux/of.h> -#include <linux/of_device.h> #include <linux/platform_device.h> #include <linux/pwm.h> #include <linux/slab.h> @@ -22,54 +22,56 @@ /* PWM registers and bits definitions */ #define PWMCON 0x00 +#define PWMCON_CLKDIV GENMASK(2, 0) #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_CLK_DIV_MAX 7 +#define PWM_CK_26M_SEL_V3 0x74 +#define PWM_CK_26M_SEL 0x210 struct pwm_mediatek_of_data { unsigned int num_pwms; 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 - * @chip: linux PWM chip representation * @regs: base address of PWM chip * @clk_top: the top clock generator * @clk_main: the clock used by PWM core - * @clk_pwms: the clock used by each PWM channel - * @clk_freq: the fix clock frequency of legacy MIPS SoC + * @soc: pointer to chip's platform data + * @clk_pwms: the clock and clkrate used by each PWM channel */ struct pwm_mediatek_chip { - struct pwm_chip chip; void __iomem *regs; struct clk *clk_top; struct clk *clk_main; - struct clk **clk_pwms; const struct pwm_mediatek_of_data *soc; -}; - -static const unsigned int pwm_mediatek_reg_offset[] = { - 0x0010, 0x0050, 0x0090, 0x00d0, 0x0110, 0x0150, 0x0190, 0x0220 + struct { + struct clk *clk; + unsigned long rate; + } clk_pwms[]; }; static inline struct pwm_mediatek_chip * to_pwm_mediatek_chip(struct pwm_chip *chip) { - return container_of(chip, struct pwm_mediatek_chip, chip); + return pwmchip_get_drvdata(chip); } -static int pwm_mediatek_clk_enable(struct pwm_chip *chip, - struct pwm_device *pwm) +static int pwm_mediatek_clk_enable(struct pwm_mediatek_chip *pc, + unsigned int hwpwm) { - struct pwm_mediatek_chip *pc = to_pwm_mediatek_chip(chip); int ret; ret = clk_prepare_enable(pc->clk_top); @@ -80,12 +82,28 @@ static int pwm_mediatek_clk_enable(struct pwm_chip *chip, if (ret < 0) goto disable_clk_top; - ret = clk_prepare_enable(pc->clk_pwms[pwm->hwpwm]); + 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: @@ -94,225 +112,499 @@ disable_clk_top: return ret; } -static void pwm_mediatek_clk_disable(struct pwm_chip *chip, - struct pwm_device *pwm) +static void pwm_mediatek_clk_disable(struct pwm_mediatek_chip *pc, + unsigned int hwpwm) { - struct pwm_mediatek_chip *pc = to_pwm_mediatek_chip(chip); - - clk_disable_unprepare(pc->clk_pwms[pwm->hwpwm]); + clk_disable_unprepare(pc->clk_pwms[hwpwm].clk); clk_disable_unprepare(pc->clk_main); clk_disable_unprepare(pc->clk_top); } -static inline u32 pwm_mediatek_readl(struct pwm_mediatek_chip *chip, - unsigned int num, unsigned int offset) -{ - return readl(chip->regs + pwm_mediatek_reg_offset[num] + offset); -} - static inline void pwm_mediatek_writel(struct pwm_mediatek_chip *chip, unsigned int num, unsigned int offset, u32 value) { - writel(value, chip->regs + pwm_mediatek_reg_offset[num] + offset); + writel(value, chip->regs + chip->soc->chanreg_base + + num * chip->soc->chanreg_width + offset); } -static int pwm_mediatek_config(struct pwm_chip *chip, struct pwm_device *pwm, - int duty_ns, int period_ns) +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 = 0, cnt_period, cnt_duty, reg_width = PWMDWIDTH, - reg_thres = PWMTHRES; - u64 resolution; - int ret; + u32 clkdiv, enable; + u64 cnt_period, cnt_duty; + unsigned long clk_rate; + int ret = 0; - ret = pwm_mediatek_clk_enable(chip, pwm); + if (wf->period_length_ns == 0) { + *wfhw = (typeof(*wfhw)){ + .enable = 0, + }; - if (ret < 0) - return ret; + 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; - /* Using resolution in picosecond gets accuracy higher */ - resolution = (u64)NSEC_PER_SEC * 1000; - do_div(resolution, clk_get_rate(pc->clk_pwms[pwm->hwpwm])); + pc->clk_pwms[pwm->hwpwm].rate = clk_get_rate(clk); - cnt_period = DIV_ROUND_CLOSEST_ULL((u64)period_ns * 1000, resolution); - while (cnt_period > 8191) { - resolution *= 2; - clkdiv++; - cnt_period = DIV_ROUND_CLOSEST_ULL((u64)period_ns * 1000, - resolution); + clk_disable_unprepare(clk); } - if (clkdiv > PWM_CLK_DIV_MAX) { - pwm_mediatek_clk_disable(chip, pwm); - dev_err(chip->dev, "period %d not supported\n", period_ns); + 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; } - if (pc->soc->pwm45_fixup && pwm->hwpwm > 2) { + 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); + /* - * PWM[4,5] has distinct offset for PWMDWIDTH and PWMTHRES - * from the other PWMs on MT7623. + * 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. */ - reg_width = PWM45DWIDTH_FIXUP; - reg_thres = PWM45THRES_FIXUP; - } + *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; - cnt_duty = DIV_ROUND_CLOSEST_ULL((u64)duty_ns * 1000, resolution); - pwm_mediatek_writel(pc, pwm->hwpwm, PWMCON, BIT(15) | clkdiv); - pwm_mediatek_writel(pc, pwm->hwpwm, reg_width, cnt_period); - pwm_mediatek_writel(pc, pwm->hwpwm, reg_thres, cnt_duty); + /* + * .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, + }; + } - pwm_mediatek_clk_disable(chip, pwm); + 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_enable(struct pwm_chip *chip, struct pwm_device *pwm) +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 value; + u32 enable, clkdiv, cnt_period, cnt_duty; + u32 reg_width = PWMDWIDTH, reg_thres = PWMTHRES; int ret; - ret = pwm_mediatek_clk_enable(chip, pwm); + ret = pwm_mediatek_clk_enable(pc, pwm->hwpwm); if (ret < 0) return ret; - value = readl(pc->regs); - value |= BIT(pwm->hwpwm); - writel(value, pc->regs); + enable = readl(pc->regs) & BIT(pwm->hwpwm); - return 0; + 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 void pwm_mediatek_disable(struct pwm_chip *chip, struct pwm_device *pwm) +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 value; + 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; + + 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); + + pwm_mediatek_writel(pc, pwm->hwpwm, PWMCON, BIT(15) | wfhw->con); + 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); - value = readl(pc->regs); - value &= ~BIT(pwm->hwpwm); - writel(value, pc->regs); + pwm_mediatek_clk_disable(pc, pwm->hwpwm); + } + } - pwm_mediatek_clk_disable(chip, pwm); +out: + pwm_mediatek_clk_disable(pc, pwm->hwpwm); + + return ret; } static const struct pwm_ops pwm_mediatek_ops = { - .config = pwm_mediatek_config, - .enable = pwm_mediatek_enable, - .disable = pwm_mediatek_disable, - .owner = THIS_MODULE, + .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; - struct resource *res; + const struct pwm_mediatek_of_data *soc; unsigned int i; int ret; - pc = devm_kzalloc(&pdev->dev, sizeof(*pc), GFP_KERNEL); - if (!pc) - return -ENOMEM; + 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 = of_device_get_match_data(&pdev->dev); + pc->soc = soc; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - pc->regs = devm_ioremap_resource(&pdev->dev, res); + pc->regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(pc->regs)) return PTR_ERR(pc->regs); - pc->clk_pwms = devm_kcalloc(&pdev->dev, pc->soc->num_pwms, - sizeof(*pc->clk_pwms), GFP_KERNEL); - if (!pc->clk_pwms) - return -ENOMEM; - pc->clk_top = devm_clk_get(&pdev->dev, "top"); - if (IS_ERR(pc->clk_top)) { - dev_err(&pdev->dev, "clock: top fail: %ld\n", - PTR_ERR(pc->clk_top)); - return PTR_ERR(pc->clk_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)) { - dev_err(&pdev->dev, "clock: main fail: %ld\n", - PTR_ERR(pc->clk_main)); - return PTR_ERR(pc->clk_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 < pc->soc->num_pwms; i++) { + for (i = 0; i < soc->num_pwms; i++) { char name[8]; snprintf(name, sizeof(name), "pwm%d", i + 1); - pc->clk_pwms[i] = devm_clk_get(&pdev->dev, name); - if (IS_ERR(pc->clk_pwms[i])) { - dev_err(&pdev->dev, "clock: %s fail: %ld\n", - name, PTR_ERR(pc->clk_pwms[i])); - return PTR_ERR(pc->clk_pwms[i]); - } + 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); } - platform_set_drvdata(pdev, pc); + ret = pwm_mediatek_init_used_clks(pc); + if (ret) + return dev_err_probe(&pdev->dev, ret, "Failed to initialize used clocks\n"); - pc->chip.dev = &pdev->dev; - pc->chip.ops = &pwm_mediatek_ops; - pc->chip.base = -1; - pc->chip.npwm = pc->soc->num_pwms; + chip->ops = &pwm_mediatek_ops; - ret = pwmchip_add(&pc->chip); - if (ret < 0) { - dev_err(&pdev->dev, "pwmchip_add() failed: %d\n", ret); - return ret; - } + ret = devm_pwmchip_add(&pdev->dev, chip); + if (ret < 0) + return dev_err_probe(&pdev->dev, ret, "pwmchip_add() failed\n"); return 0; } -static int pwm_mediatek_remove(struct platform_device *pdev) -{ - struct pwm_mediatek_chip *pc = platform_get_drvdata(pdev); - - return pwmchip_remove(&pc->chip); -} - static const struct pwm_mediatek_of_data mt2712_pwm_data = { .num_pwms = 8, .pwm45_fixup = false, + .chanreg_base = 0x10, + .chanreg_width = 0x40, +}; + +static const struct pwm_mediatek_of_data mt6795_pwm_data = { + .num_pwms = 7, + .pwm45_fixup = false, + .chanreg_base = 0x10, + .chanreg_width = 0x40, }; static const struct pwm_mediatek_of_data mt7622_pwm_data = { .num_pwms = 6, .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, .pwm45_fixup = true, + .chanreg_base = 0x10, + .chanreg_width = 0x40, }; static const struct pwm_mediatek_of_data mt7628_pwm_data = { .num_pwms = 4, .pwm45_fixup = true, + .chanreg_base = 0x10, + .chanreg_width = 0x40, }; static const struct pwm_mediatek_of_data mt7629_pwm_data = { .num_pwms = 1, .pwm45_fixup = false, + .chanreg_base = 0x10, + .chanreg_width = 0x40, +}; + +static const struct pwm_mediatek_of_data mt7981_pwm_data = { + .num_pwms = 3, + .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, + .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, + .pwm45_fixup = false, + .chanreg_base = 0x80, + .chanreg_width = 0x40, +}; + +static const struct pwm_mediatek_of_data mt8183_pwm_data = { + .num_pwms = 4, + .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, + .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, .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, + .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 }, { }, }; @@ -324,9 +616,9 @@ static struct platform_driver pwm_mediatek_driver = { .of_match_table = pwm_mediatek_of_match, }, .probe = pwm_mediatek_probe, - .remove = pwm_mediatek_remove, }; 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 index bd0d7336b898..8c6bf3d49753 100644 --- a/drivers/pwm/pwm-meson.c +++ b/drivers/pwm/pwm-meson.c @@ -6,7 +6,7 @@ * PWM output is achieved by calculating a clock that permits calculating * two periods (low and high). The counter then has to be set to switch after * N cycles for the first half period. - * The hardware has no "polarity" setting. This driver reverses the period + * Partly the hardware has no "polarity" setting. This driver reverses the period * cycles (the low length is inverted with the high length) for * PWM_POLARITY_INVERSED. This means that .get_state cannot read the polarity * from the hardware. @@ -37,7 +37,6 @@ #include <linux/math64.h> #include <linux/module.h> #include <linux/of.h> -#include <linux/of_device.h> #include <linux/platform_device.h> #include <linux/pwm.h> #include <linux/slab.h> @@ -49,59 +48,74 @@ #define PWM_HIGH_MASK GENMASK(31, 16) #define REG_MISC_AB 0x8 -#define MISC_B_CLK_EN BIT(23) -#define MISC_A_CLK_EN BIT(15) -#define MISC_CLK_DIV_MASK 0x7f +#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; - u32 clk_en_mask; + 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_mask = MISC_A_CLK_EN, + .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_mask = MISC_B_CLK_EN, + .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; - u8 pre_div; + bool constant; + bool inverted; - struct clk *clk_parent; struct clk_mux mux; + struct clk_divider div; + struct clk_gate gate; struct clk *clk; }; struct meson_pwm_data { - const char * const *parent_names; - unsigned int num_parents; + 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 { - struct pwm_chip chip; const struct meson_pwm_data *data; struct meson_pwm_channel channels[MESON_NUM_PWMS]; void __iomem *base; @@ -114,32 +128,16 @@ struct meson_pwm { static inline struct meson_pwm *to_meson_pwm(struct pwm_chip *chip) { - return container_of(chip, struct meson_pwm, 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; - struct device *dev = chip->dev; + struct meson_pwm_channel *channel = &meson->channels[pwm->hwpwm]; + struct device *dev = pwmchip_parent(chip); int err; - channel = pwm_get_chip_data(pwm); - if (channel) - return 0; - - channel = &meson->channels[pwm->hwpwm]; - - if (channel->clk_parent) { - err = clk_set_parent(channel->clk, channel->clk_parent); - if (err < 0) { - dev_err(dev, "failed to set parent %s for %s: %d\n", - __clk_get_name(channel->clk_parent), - __clk_get_name(channel->clk), err); - return err; - } - } - err = clk_prepare_enable(channel->clk); if (err < 0) { dev_err(dev, "failed to enable clock %s: %d\n", @@ -147,97 +145,98 @@ static int meson_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm) return err; } - return pwm_set_chip_data(pwm, channel); + return 0; } static void meson_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm) { - struct meson_pwm_channel *channel = pwm_get_chip_data(pwm); + struct meson_pwm *meson = to_meson_pwm(chip); + struct meson_pwm_channel *channel = &meson->channels[pwm->hwpwm]; - if (channel) - clk_disable_unprepare(channel->clk); + clk_disable_unprepare(channel->clk); } -static int meson_pwm_calc(struct meson_pwm *meson, struct pwm_device *pwm, +static int meson_pwm_calc(struct pwm_chip *chip, struct pwm_device *pwm, const struct pwm_state *state) { - struct meson_pwm_channel *channel = pwm_get_chip_data(pwm); - unsigned int duty, period, pre_div, cnt, duty_cnt; - unsigned long fin_freq; + 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; - if (state->polarity == PWM_POLARITY_INVERSED) + /* + * 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; - fin_freq = clk_get_rate(channel->clk); - if (fin_freq == 0) { - dev_err(meson->chip.dev, "invalid source clock frequency\n"); - return -EINVAL; - } - - dev_dbg(meson->chip.dev, "fin_freq: %lu Hz\n", fin_freq); + freq = div64_u64(NSEC_PER_SEC * 0xffffULL, period); + if (freq > ULONG_MAX) + freq = ULONG_MAX; - pre_div = div64_u64(fin_freq * (u64)period, NSEC_PER_SEC * 0xffffLL); - if (pre_div > MISC_CLK_DIV_MASK) { - dev_err(meson->chip.dev, "unable to get period pre_div\n"); - return -EINVAL; + 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; } - cnt = div64_u64(fin_freq * (u64)period, NSEC_PER_SEC * (pre_div + 1)); + 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(meson->chip.dev, "unable to get period cnt\n"); + dev_err(pwmchip_parent(chip), "unable to get period cnt\n"); return -EINVAL; } - dev_dbg(meson->chip.dev, "period=%u pre_div=%u cnt=%u\n", period, - pre_div, cnt); + dev_dbg(pwmchip_parent(chip), "period=%llu cnt=%u\n", period, cnt); if (duty == period) { - channel->pre_div = pre_div; channel->hi = cnt; channel->lo = 0; + channel->constant = true; } else if (duty == 0) { - channel->pre_div = pre_div; channel->hi = 0; channel->lo = cnt; + channel->constant = true; } else { - /* Then check is we can have the duty with the same pre_div */ - duty_cnt = div64_u64(fin_freq * (u64)duty, - NSEC_PER_SEC * (pre_div + 1)); - if (duty_cnt > 0xffff) { - dev_err(meson->chip.dev, "unable to get duty cycle\n"); - return -EINVAL; - } + duty_cnt = mul_u64_u64_div_u64(fin_freq, duty, NSEC_PER_SEC); - dev_dbg(meson->chip.dev, "duty=%u pre_div=%u duty_cnt=%u\n", - duty, pre_div, duty_cnt); + dev_dbg(pwmchip_parent(chip), "duty=%llu duty_cnt=%u\n", duty, duty_cnt); - channel->pre_div = pre_div; channel->hi = duty_cnt; channel->lo = cnt - duty_cnt; + channel->constant = false; } + channel->rate = fin_freq; + return 0; } -static void meson_pwm_enable(struct meson_pwm *meson, struct pwm_device *pwm) +static void meson_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) { - struct meson_pwm_channel *channel = pwm_get_chip_data(pwm); + struct meson_pwm *meson = to_meson_pwm(chip); + 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]; - spin_lock_irqsave(&meson->lock, flags); + err = clk_set_rate(channel->clk, channel->rate); + if (err) + dev_err(pwmchip_parent(chip), "setting clock rate failed\n"); - value = readl(meson->base + REG_MISC_AB); - value &= ~(MISC_CLK_DIV_MASK << channel_data->clk_div_shift); - value |= channel->pre_div << channel_data->clk_div_shift; - value |= channel_data->clk_en_mask; - writel(value, meson->base + REG_MISC_AB); + spin_lock_irqsave(&meson->lock, flags); value = FIELD_PREP(PWM_HIGH_MASK, channel->hi) | FIELD_PREP(PWM_LOW_MASK, channel->lo); @@ -245,20 +244,45 @@ static void meson_pwm_enable(struct meson_pwm *meson, struct pwm_device *pwm) value = readl(meson->base + REG_MISC_AB); value |= channel_data->pwm_en_mask; + + if (meson->data->has_constant) { + value &= ~channel_data->const_en_mask; + if (channel->constant) + value |= channel_data->const_en_mask; + } + + if (meson->data->has_polarity) { + value &= ~channel_data->inv_en_mask; + if (channel->inverted) + value |= channel_data->inv_en_mask; + } + writel(value, meson->base + REG_MISC_AB); spin_unlock_irqrestore(&meson->lock, flags); } -static void meson_pwm_disable(struct meson_pwm *meson, struct pwm_device *pwm) +static void meson_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) { + struct meson_pwm *meson = to_meson_pwm(chip); + struct meson_pwm_channel *channel = &meson->channels[pwm->hwpwm]; + struct meson_pwm_channel_data *channel_data; unsigned long flags; u32 value; + channel_data = &meson_pwm_per_channel_data[pwm->hwpwm]; + spin_lock_irqsave(&meson->lock, flags); value = readl(meson->base + REG_MISC_AB); - value &= ~meson_pwm_per_channel_data[pwm->hwpwm].pwm_en_mask; + value &= ~channel_data->pwm_en_mask; + + if (meson->data->has_polarity) { + value &= ~channel_data->inv_en_mask; + if (channel->inverted) + value |= channel_data->inv_en_mask; + } + writel(value, meson->base + REG_MISC_AB); spin_unlock_irqrestore(&meson->lock, flags); @@ -267,105 +291,79 @@ static void meson_pwm_disable(struct meson_pwm *meson, struct pwm_device *pwm) static int meson_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, const struct pwm_state *state) { - struct meson_pwm_channel *channel = pwm_get_chip_data(pwm); struct meson_pwm *meson = to_meson_pwm(chip); + struct meson_pwm_channel *channel = &meson->channels[pwm->hwpwm]; int err = 0; - if (!state) - return -EINVAL; + channel->inverted = (state->polarity == PWM_POLARITY_INVERSED); if (!state->enabled) { - if (state->polarity == PWM_POLARITY_INVERSED) { + if (channel->inverted && !meson->data->has_polarity) { /* - * This IP block revision doesn't have an "always high" + * Some of IP block revisions don't have an "always high" * setting which we can use for "inverted disabled". - * Instead we achieve this using the same settings - * that we use a pre_div of 0 (to get the shortest - * possible duration for one "count") and - * "period == duty_cycle". This results in a signal + * 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->pre_div = 0; + channel->rate = ULONG_MAX; channel->hi = ~0; channel->lo = 0; + channel->constant = true; - meson_pwm_enable(meson, pwm); + meson_pwm_enable(chip, pwm); } else { - meson_pwm_disable(meson, pwm); + meson_pwm_disable(chip, pwm); } } else { - err = meson_pwm_calc(meson, pwm, state); + err = meson_pwm_calc(chip, pwm, state); if (err < 0) return err; - meson_pwm_enable(meson, pwm); + meson_pwm_enable(chip, pwm); } return 0; } -static unsigned int meson_pwm_cnt_to_ns(struct pwm_chip *chip, - struct pwm_device *pwm, u32 cnt) +static u64 meson_pwm_cnt_to_ns(unsigned long fin_freq, u32 cnt) { - struct meson_pwm *meson = to_meson_pwm(chip); - struct meson_pwm_channel *channel; - unsigned long fin_freq; - u32 fin_ns; - - /* to_meson_pwm() can only be used after .get_state() is called */ - channel = &meson->channels[pwm->hwpwm]; - - fin_freq = clk_get_rate(channel->clk); - if (fin_freq == 0) - return 0; - - fin_ns = div_u64(NSEC_PER_SEC, fin_freq); - - return cnt * fin_ns * (channel->pre_div + 1); + return fin_freq ? div64_ul(NSEC_PER_SEC * (u64)cnt, fin_freq) : 0; } -static void meson_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm, - struct pwm_state *state) +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; - struct meson_pwm_channel *channel; - u32 value, tmp; - - if (!state) - return; + unsigned long fin_freq; + unsigned int hi, lo; + u32 value; - channel = &meson->channels[pwm->hwpwm]; channel_data = &meson_pwm_per_channel_data[pwm->hwpwm]; + fin_freq = clk_get_rate(meson->channels[pwm->hwpwm].clk); value = readl(meson->base + REG_MISC_AB); + state->enabled = value & channel_data->pwm_en_mask; - tmp = channel_data->pwm_en_mask | channel_data->clk_en_mask; - state->enabled = (value & tmp) == tmp; - - tmp = value >> channel_data->clk_div_shift; - channel->pre_div = FIELD_GET(MISC_CLK_DIV_MASK, tmp); + 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); - channel->lo = FIELD_GET(PWM_LOW_MASK, value); - channel->hi = FIELD_GET(PWM_HIGH_MASK, value); - - if (channel->lo == 0) { - state->period = meson_pwm_cnt_to_ns(chip, pwm, channel->hi); - state->duty_cycle = state->period; - } else if (channel->lo >= channel->hi) { - state->period = meson_pwm_cnt_to_ns(chip, pwm, - channel->lo + channel->hi); - state->duty_cycle = meson_pwm_cnt_to_ns(chip, pwm, - channel->hi); - } else { - state->period = 0; - state->duty_cycle = 0; - } + 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 = { @@ -373,93 +371,248 @@ static const struct pwm_ops meson_pwm_ops = { .free = meson_pwm_free, .apply = meson_pwm_apply, .get_state = meson_pwm_get_state, - .owner = THIS_MODULE, }; -static const char * const pwm_meson8b_parent_names[] = { - "xtal", "vid_pll", "fclk_div4", "fclk_div3" -}; +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; -static const struct meson_pwm_data pwm_meson8b_data = { - .parent_names = pwm_meson8b_parent_names, - .num_parents = ARRAY_SIZE(pwm_meson8b_parent_names), -}; + 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 = {}; -static const char * const pwm_gxbb_parent_names[] = { - "xtal", "hdmi_pll", "fclk_div4", "fclk_div3" -}; + 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 const struct meson_pwm_data pwm_gxbb_data = { - .parent_names = pwm_gxbb_parent_names, - .num_parents = ARRAY_SIZE(pwm_gxbb_parent_names), +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 char * const pwm_gxbb_ao_parent_names[] = { - "xtal", "clk81" -}; - static const struct meson_pwm_data pwm_gxbb_ao_data = { - .parent_names = pwm_gxbb_ao_parent_names, - .num_parents = ARRAY_SIZE(pwm_gxbb_ao_parent_names), -}; - -static const char * const pwm_axg_ee_parent_names[] = { - "xtal", "fclk_div5", "fclk_div4", "fclk_div3" + .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 = pwm_axg_ee_parent_names, - .num_parents = ARRAY_SIZE(pwm_axg_ee_parent_names), -}; - -static const char * const pwm_axg_ao_parent_names[] = { - "aoclk81", "xtal", "fclk_div4", "fclk_div5" + .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 = pwm_axg_ao_parent_names, - .num_parents = ARRAY_SIZE(pwm_axg_ao_parent_names), + .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 char * const pwm_g12a_ao_ab_parent_names[] = { - "xtal", "aoclk81", "fclk_div4", "fclk_div5" +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 = pwm_g12a_ao_ab_parent_names, - .num_parents = ARRAY_SIZE(pwm_g12a_ao_ab_parent_names), + .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 char * const pwm_g12a_ao_cd_parent_names[] = { - "xtal", "aoclk81", +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_g12a_ao_cd_data = { - .parent_names = pwm_g12a_ao_cd_parent_names, - .num_parents = ARRAY_SIZE(pwm_g12a_ao_cd_parent_names), +static const struct meson_pwm_data pwm_meson8_v2_data = { + .channels_init = meson_pwm_init_channels_meson8b_v2, }; -static const char * const pwm_g12a_ee_parent_names[] = { - "xtal", "hdmi_pll", "fclk_div4", "fclk_div3" +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_g12a_ee_data = { - .parent_names = pwm_g12a_ee_parent_names, - .num_parents = ARRAY_SIZE(pwm_g12a_ee_parent_names), +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_gxbb_data + .data = &pwm_meson8b_data }, { .compatible = "amlogic,meson-gxbb-ao-pwm", @@ -485,109 +638,52 @@ static const struct of_device_id meson_pwm_matches[] = { .compatible = "amlogic,meson-g12a-ao-pwm-cd", .data = &pwm_g12a_ao_cd_data }, + { + .compatible = "amlogic,meson-s4-pwm", + .data = &pwm_s4_data + }, {}, }; MODULE_DEVICE_TABLE(of, meson_pwm_matches); -static int meson_pwm_init_channels(struct meson_pwm *meson) -{ - struct device *dev = meson->chip.dev; - struct clk_init_data init; - unsigned int i; - char name[255]; - int err; - - for (i = 0; i < meson->chip.npwm; i++) { - struct meson_pwm_channel *channel = &meson->channels[i]; - - snprintf(name, sizeof(name), "%s#mux%u", dev_name(dev), i); - - init.name = name; - init.ops = &clk_mux_ops; - init.flags = 0; - init.parent_names = meson->data->parent_names; - init.num_parents = meson->data->num_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; - - channel->clk = devm_clk_register(dev, &channel->mux.hw); - if (IS_ERR(channel->clk)) { - err = PTR_ERR(channel->clk); - dev_err(dev, "failed to register %s: %d\n", name, err); - return err; - } - - snprintf(name, sizeof(name), "clkin%u", i); - - channel->clk_parent = devm_clk_get_optional(dev, name); - if (IS_ERR(channel->clk_parent)) - return PTR_ERR(channel->clk_parent); - } - - return 0; -} - static int meson_pwm_probe(struct platform_device *pdev) { + struct pwm_chip *chip; struct meson_pwm *meson; - struct resource *regs; int err; - meson = devm_kzalloc(&pdev->dev, sizeof(*meson), GFP_KERNEL); - if (!meson) - return -ENOMEM; + chip = devm_pwmchip_alloc(&pdev->dev, MESON_NUM_PWMS, sizeof(*meson)); + if (IS_ERR(chip)) + return PTR_ERR(chip); + meson = to_meson_pwm(chip); - regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); - meson->base = devm_ioremap_resource(&pdev->dev, regs); + meson->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(meson->base)) return PTR_ERR(meson->base); spin_lock_init(&meson->lock); - meson->chip.dev = &pdev->dev; - meson->chip.ops = &meson_pwm_ops; - meson->chip.base = -1; - meson->chip.npwm = MESON_NUM_PWMS; - meson->chip.of_xlate = of_pwm_xlate_with_flags; - meson->chip.of_pwm_n_cells = 3; + chip->ops = &meson_pwm_ops; meson->data = of_device_get_match_data(&pdev->dev); - err = meson_pwm_init_channels(meson); + err = meson->data->channels_init(chip); if (err < 0) return err; - err = pwmchip_add(&meson->chip); - if (err < 0) { - dev_err(&pdev->dev, "failed to register PWM chip: %d\n", err); - return err; - } - - platform_set_drvdata(pdev, meson); + 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 int meson_pwm_remove(struct platform_device *pdev) -{ - struct meson_pwm *meson = platform_get_drvdata(pdev); - - return pwmchip_remove(&meson->chip); -} - static struct platform_driver meson_pwm_driver = { .driver = { .name = "meson-pwm", .of_match_table = meson_pwm_matches, }, .probe = meson_pwm_probe, - .remove = meson_pwm_remove, }; module_platform_driver(meson_pwm_driver); diff --git a/drivers/pwm/pwm-microchip-core.c b/drivers/pwm/pwm-microchip-core.c new file mode 100644 index 000000000000..4ff32bb4c205 --- /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, ®s); + 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 index 83b8be0209b7..bafd6b6195f6 100644 --- a/drivers/pwm/pwm-mtk-disp.c +++ b/drivers/pwm/pwm-mtk-disp.c @@ -5,12 +5,12 @@ * 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/of_device.h> #include <linux/platform_device.h> #include <linux/pwm.h> #include <linux/slab.h> @@ -42,16 +42,16 @@ struct mtk_pwm_data { }; struct mtk_disp_pwm { - struct pwm_chip chip; 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 container_of(chip, struct mtk_disp_pwm, chip); + return pwmchip_get_drvdata(chip); } static void mtk_disp_pwm_update_bits(struct mtk_disp_pwm *mdp, u32 offset, @@ -66,14 +66,44 @@ static void mtk_disp_pwm_update_bits(struct mtk_disp_pwm *mdp, u32 offset, writel(value, address); } -static int mtk_disp_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, - int duty_ns, int period_ns) +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. @@ -85,27 +115,35 @@ static int mtk_disp_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, * high_width = (PWM_CLK_RATE * duty_ns) / (10^9 * (clk_div + 1)) */ rate = clk_get_rate(mdp->clk_main); - clk_div = div_u64(rate * period_ns, NSEC_PER_SEC) >> + clk_div = mul_u64_u64_div_u64(state->period, rate, NSEC_PER_SEC) >> PWM_PERIOD_BIT_WIDTH; - if (clk_div > PWM_CLKDIV_MAX) + 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 = div64_u64(rate * period_ns, div); + period = mul_u64_u64_div_u64(state->period, rate, div); if (period > 0) period--; - high_width = div64_u64(rate * duty_ns, div); + high_width = mul_u64_u64_div_u64(state->duty_cycle, rate, div); value = period | (high_width << PWM_HIGH_WIDTH_SHIFT); - err = clk_enable(mdp->clk_main); - if (err < 0) - return err; - - err = clk_enable(mdp->clk_mm); - if (err < 0) { - clk_disable(mdp->clk_main); - return err; + 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, @@ -124,129 +162,106 @@ static int mtk_disp_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, 0x0); } - clk_disable(mdp->clk_mm); - clk_disable(mdp->clk_main); + 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_enable(struct pwm_chip *chip, struct pwm_device *pwm) +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_enable(mdp->clk_main); - if (err < 0) + 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_enable(mdp->clk_mm); + err = clk_prepare_enable(mdp->clk_mm); if (err < 0) { - clk_disable(mdp->clk_main); + dev_err(pwmchip_parent(chip), "Can't enable mdp->clk_mm: %pe\n", ERR_PTR(err)); + clk_disable_unprepare(mdp->clk_main); return err; } - mtk_disp_pwm_update_bits(mdp, DISP_PWM_EN, mdp->data->enable_mask, - mdp->data->enable_mask); - - return 0; -} - -static void mtk_disp_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) -{ - struct mtk_disp_pwm *mdp = to_mtk_disp_pwm(chip); + /* + * 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); - mtk_disp_pwm_update_bits(mdp, DISP_PWM_EN, mdp->data->enable_mask, - 0x0); + 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); - clk_disable(mdp->clk_mm); - clk_disable(mdp->clk_main); + return 0; } static const struct pwm_ops mtk_disp_pwm_ops = { - .config = mtk_disp_pwm_config, - .enable = mtk_disp_pwm_enable, - .disable = mtk_disp_pwm_disable, - .owner = THIS_MODULE, + .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; - struct resource *r; int ret; - mdp = devm_kzalloc(&pdev->dev, sizeof(*mdp), GFP_KERNEL); - if (!mdp) - return -ENOMEM; + 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); - r = platform_get_resource(pdev, IORESOURCE_MEM, 0); - mdp->base = devm_ioremap_resource(&pdev->dev, r); + 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 PTR_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 PTR_ERR(mdp->clk_mm); + return dev_err_probe(&pdev->dev, PTR_ERR(mdp->clk_mm), + "Failed to get mm clock\n"); - ret = clk_prepare(mdp->clk_main); - if (ret < 0) - return ret; + chip->ops = &mtk_disp_pwm_ops; - ret = clk_prepare(mdp->clk_mm); + ret = devm_pwmchip_add(&pdev->dev, chip); if (ret < 0) - goto disable_clk_main; - - mdp->chip.dev = &pdev->dev; - mdp->chip.ops = &mtk_disp_pwm_ops; - mdp->chip.base = -1; - mdp->chip.npwm = 1; - - ret = pwmchip_add(&mdp->chip); - if (ret < 0) { - dev_err(&pdev->dev, "pwmchip_add() failed: %d\n", ret); - goto disable_clk_mm; - } - - platform_set_drvdata(pdev, mdp); - - /* - * For MT2701, disable double buffer before writing register - * and select manual mode and use PWM_PERIOD/PWM_HIGH_WIDTH. - */ - if (!mdp->data->has_commit) { - 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); - } + return dev_err_probe(&pdev->dev, ret, "pwmchip_add() failed\n"); return 0; - -disable_clk_mm: - clk_unprepare(mdp->clk_mm); -disable_clk_main: - clk_unprepare(mdp->clk_main); - return ret; -} - -static int mtk_disp_pwm_remove(struct platform_device *pdev) -{ - struct mtk_disp_pwm *mdp = platform_get_drvdata(pdev); - int ret; - - ret = pwmchip_remove(&mdp->chip); - clk_unprepare(mdp->clk_mm); - clk_unprepare(mdp->clk_main); - - return ret; } static const struct mtk_pwm_data mt2701_pwm_data = { @@ -294,7 +309,6 @@ static struct platform_driver mtk_disp_pwm_driver = { .of_match_table = mtk_disp_pwm_of_match, }, .probe = mtk_disp_pwm_probe, - .remove = mtk_disp_pwm_remove, }; module_platform_driver(mtk_disp_pwm_driver); diff --git a/drivers/pwm/pwm-mxs.c b/drivers/pwm/pwm-mxs.c index 7ce616923c52..8cad214b1c29 100644 --- a/drivers/pwm/pwm-mxs.c +++ b/drivers/pwm/pwm-mxs.c @@ -37,12 +37,14 @@ static const u8 cdiv_shift[PERIOD_CDIV_MAX] = { }; struct mxs_pwm_chip { - struct pwm_chip chip; struct clk *clk; void __iomem *base; }; -#define to_mxs_pwm_chip(_chip) container_of(_chip, struct mxs_pwm_chip, chip) +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) @@ -115,18 +117,26 @@ static int mxs_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, static const struct pwm_ops mxs_pwm_ops = { .apply = mxs_pwm_apply, - .owner = THIS_MODULE, }; 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; - mxs = devm_kzalloc(&pdev->dev, sizeof(*mxs), GFP_KERNEL); - if (!mxs) - return -ENOMEM; + 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)) @@ -136,42 +146,20 @@ static int mxs_pwm_probe(struct platform_device *pdev) if (IS_ERR(mxs->clk)) return PTR_ERR(mxs->clk); - mxs->chip.dev = &pdev->dev; - mxs->chip.ops = &mxs_pwm_ops; - mxs->chip.of_xlate = of_pwm_xlate_with_flags; - mxs->chip.of_pwm_n_cells = 3; - mxs->chip.base = -1; + chip->ops = &mxs_pwm_ops; - ret = of_property_read_u32(np, "fsl,pwm-number", &mxs->chip.npwm); - if (ret < 0) { - dev_err(&pdev->dev, "failed to get pwm number: %d\n", ret); - return ret; - } + /* 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 = pwmchip_add(&mxs->chip); + 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, mxs); - - ret = stmp_reset_block(mxs->base); - if (ret) - goto pwm_remove; - return 0; - -pwm_remove: - pwmchip_remove(&mxs->chip); - return ret; -} - -static int mxs_pwm_remove(struct platform_device *pdev) -{ - struct mxs_pwm_chip *mxs = platform_get_drvdata(pdev); - - return pwmchip_remove(&mxs->chip); } static const struct of_device_id mxs_pwm_dt_ids[] = { @@ -186,7 +174,6 @@ static struct platform_driver mxs_pwm_driver = { .of_match_table = mxs_pwm_dt_ids, }, .probe = mxs_pwm_probe, - .remove = mxs_pwm_remove, }; module_platform_driver(mxs_pwm_driver); diff --git a/drivers/pwm/pwm-ntxec.c b/drivers/pwm/pwm-ntxec.c new file mode 100644 index 000000000000..28d1c2e5a98f --- /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 index 0d31833db2e2..1858a77401f8 100644 --- a/drivers/pwm/pwm-omap-dmtimer.c +++ b/drivers/pwm/pwm-omap-dmtimer.c @@ -14,7 +14,7 @@ * 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: http://www.ti.com/lit/ug/spruh73q/spruh73q.pdf + * Reference Manual: https://www.ti.com/lit/ug/spruh73q/spruh73q.pdf * * Limitations: * - When PWM is stopped, timer counter gets stopped immediately. This @@ -37,7 +37,6 @@ #include <linux/err.h> #include <linux/kernel.h> #include <linux/module.h> -#include <linux/mutex.h> #include <linux/of.h> #include <linux/of_platform.h> #include <clocksource/timer-ti-dm.h> @@ -54,16 +53,12 @@ /** * struct pwm_omap_dmtimer_chip - Structure representing a pwm chip * corresponding to omap dmtimer. - * @chip: PWM chip structure representing PWM controller - * @mutex: Mutex to protect pwm apply state * @dm_timer: Pointer to omap dm timer. * @pdata: Pointer to omap dm timer ops. - * dm_timer_pdev: Pointer to omap dm timer platform device + * @dm_timer_pdev: Pointer to omap dm timer platform device */ struct pwm_omap_dmtimer_chip { - struct pwm_chip chip; /* Mutex to protect pwm apply state */ - struct mutex mutex; struct omap_dm_timer *dm_timer; const struct omap_dm_timer_ops *pdata; struct platform_device *dm_timer_pdev; @@ -72,7 +67,7 @@ struct pwm_omap_dmtimer_chip { static inline struct pwm_omap_dmtimer_chip * to_pwm_omap_dmtimer_chip(struct pwm_chip *chip) { - return container_of(chip, struct pwm_omap_dmtimer_chip, chip); + return pwmchip_get_drvdata(chip); } /** @@ -158,7 +153,7 @@ static int pwm_omap_dmtimer_config(struct pwm_chip *chip, unsigned long clk_rate; struct clk *fclk; - dev_dbg(chip->dev, "requested duty cycle: %d ns, period: %d ns\n", + 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) && @@ -167,17 +162,17 @@ static int pwm_omap_dmtimer_config(struct pwm_chip *chip, fclk = omap->pdata->get_fclk(omap->dm_timer); if (!fclk) { - dev_err(chip->dev, "invalid pmtimer fclk\n"); + dev_err(pwmchip_parent(chip), "invalid pmtimer fclk\n"); return -EINVAL; } clk_rate = clk_get_rate(fclk); if (!clk_rate) { - dev_err(chip->dev, "invalid pmtimer fclk rate\n"); + dev_err(pwmchip_parent(chip), "invalid pmtimer fclk rate\n"); return -EINVAL; } - dev_dbg(chip->dev, "clk rate: %luHz\n", clk_rate); + dev_dbg(pwmchip_parent(chip), "clk rate: %luHz\n", clk_rate); /* * Calculate the appropriate load and match values based on the @@ -199,27 +194,27 @@ static int pwm_omap_dmtimer_config(struct pwm_chip *chip, duty_cycles = pwm_omap_dmtimer_get_clock_cycles(clk_rate, duty_ns); if (period_cycles < 2) { - dev_info(chip->dev, + 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(chip->dev, + dev_dbg(pwmchip_parent(chip), "duty cycle %d ns is too short for clock rate %lu Hz\n", duty_ns, clk_rate); - dev_dbg(chip->dev, "using minimum of 1 clock cycle\n"); + dev_dbg(pwmchip_parent(chip), "using minimum of 1 clock cycle\n"); duty_cycles = 1; } else if (duty_cycles >= period_cycles) { - dev_dbg(chip->dev, + 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(chip->dev, "using maximum of 1 clock cycle less than period\n"); + dev_dbg(pwmchip_parent(chip), "using maximum of 1 clock cycle less than period\n"); duty_cycles = period_cycles - 1; } - dev_dbg(chip->dev, "effective duty cycle: %lld ns, period: %lld ns\n", + 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, @@ -231,7 +226,7 @@ static int pwm_omap_dmtimer_config(struct pwm_chip *chip, omap->pdata->set_load(omap->dm_timer, load_value); omap->pdata->set_match(omap->dm_timer, true, match_value); - dev_dbg(chip->dev, "load value: %#08x (%d), match value: %#08x (%d)\n", + dev_dbg(pwmchip_parent(chip), "load value: %#08x (%d), match value: %#08x (%d)\n", load_value, load_value, match_value, match_value); return 0; @@ -277,13 +272,11 @@ static int pwm_omap_dmtimer_apply(struct pwm_chip *chip, const struct pwm_state *state) { struct pwm_omap_dmtimer_chip *omap = to_pwm_omap_dmtimer_chip(chip); - int ret = 0; - - mutex_lock(&omap->mutex); + int ret; if (pwm_omap_dmtimer_is_enabled(omap) && !state->enabled) { omap->pdata->stop(omap->dm_timer); - goto unlock_mutex; + return 0; } if (pwm_omap_dmtimer_polarity(omap) != state->polarity) @@ -292,7 +285,7 @@ static int pwm_omap_dmtimer_apply(struct pwm_chip *chip, ret = pwm_omap_dmtimer_config(chip, pwm, state->duty_cycle, state->period); if (ret) - goto unlock_mutex; + return ret; if (!pwm_omap_dmtimer_is_enabled(omap) && state->enabled) { omap->pdata->set_pwm(omap->dm_timer, @@ -303,15 +296,11 @@ static int pwm_omap_dmtimer_apply(struct pwm_chip *chip, pwm_omap_dmtimer_start(omap); } -unlock_mutex: - mutex_unlock(&omap->mutex); - - return ret; + return 0; } static const struct pwm_ops pwm_omap_dmtimer_ops = { .apply = pwm_omap_dmtimer_apply, - .owner = THIS_MODULE, }; static int pwm_omap_dmtimer_probe(struct platform_device *pdev) @@ -320,6 +309,7 @@ static int pwm_omap_dmtimer_probe(struct platform_device *pdev) 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; @@ -365,7 +355,7 @@ static int pwm_omap_dmtimer_probe(struct platform_device *pdev) goto err_platdata; } - if (!of_get_property(timer, "ti,timer-pwm", NULL)) { + 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; @@ -377,11 +367,12 @@ static int pwm_omap_dmtimer_probe(struct platform_device *pdev) goto err_request_timer; } - omap = devm_kzalloc(&pdev->dev, sizeof(*omap), GFP_KERNEL); - if (!omap) { - ret = -ENOMEM; + 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; @@ -401,16 +392,9 @@ static int pwm_omap_dmtimer_probe(struct platform_device *pdev) if (!of_property_read_u32(pdev->dev.of_node, "ti,clock-source", &v)) omap->pdata->set_source(omap->dm_timer, v); - omap->chip.dev = &pdev->dev; - omap->chip.ops = &pwm_omap_dmtimer_ops; - omap->chip.base = -1; - omap->chip.npwm = 1; - omap->chip.of_xlate = of_pwm_xlate_with_flags; - omap->chip.of_pwm_n_cells = 3; + chip->ops = &pwm_omap_dmtimer_ops; - mutex_init(&omap->mutex); - - ret = pwmchip_add(&omap->chip); + ret = pwmchip_add(chip); if (ret < 0) { dev_err(&pdev->dev, "failed to register PWM\n"); goto err_pwmchip_add; @@ -418,7 +402,7 @@ static int pwm_omap_dmtimer_probe(struct platform_device *pdev) of_node_put(timer); - platform_set_drvdata(pdev, omap); + platform_set_drvdata(pdev, chip); return 0; @@ -444,14 +428,12 @@ err_find_timer_pdev: return ret; } -static int pwm_omap_dmtimer_remove(struct platform_device *pdev) +static void pwm_omap_dmtimer_remove(struct platform_device *pdev) { - struct pwm_omap_dmtimer_chip *omap = platform_get_drvdata(pdev); - int ret; + struct pwm_chip *chip = platform_get_drvdata(pdev); + struct pwm_omap_dmtimer_chip *omap = to_pwm_omap_dmtimer_chip(chip); - ret = pwmchip_remove(&omap->chip); - if (ret) - return ret; + pwmchip_remove(chip); if (pm_runtime_active(&omap->dm_timer_pdev->dev)) omap->pdata->stop(omap->dm_timer); @@ -459,10 +441,6 @@ static int pwm_omap_dmtimer_remove(struct platform_device *pdev) omap->pdata->free(omap->dm_timer); put_device(&omap->dm_timer_pdev->dev); - - mutex_destroy(&omap->mutex); - - return 0; } static const struct of_device_id pwm_omap_dmtimer_of_match[] = { @@ -474,10 +452,10 @@ 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 = of_match_ptr(pwm_omap_dmtimer_of_match), + .of_match_table = pwm_omap_dmtimer_of_match, }, .probe = pwm_omap_dmtimer_probe, - .remove = pwm_omap_dmtimer_remove, + .remove = pwm_omap_dmtimer_remove, }; module_platform_driver(pwm_omap_dmtimer_driver); diff --git a/drivers/pwm/pwm-pca9685.c b/drivers/pwm/pwm-pca9685.c index 76cd22bd6614..107bebec3546 100644 --- a/drivers/pwm/pwm-pca9685.c +++ b/drivers/pwm/pwm-pca9685.c @@ -8,7 +8,6 @@ * based on the pwm-twl-led.c driver */ -#include <linux/acpi.h> #include <linux/gpio/driver.h> #include <linux/i2c.h> #include <linux/module.h> @@ -23,11 +22,10 @@ #include <linux/bitmap.h> /* - * Because the PCA9685 has only one prescaler per chip, changing the period of - * one channel affects the period of all 16 PWM outputs! - * However, the ratio between each configured duty cycle and the chip-wide - * period remains constant, because the OFF time is set in proportion to the - * counter range. + * 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 @@ -51,365 +49,326 @@ #define PCA9685_PRESCALE_MAX 0xFF /* => min. frequency of 24 Hz */ #define PCA9685_COUNTER_RANGE 4096 -#define PCA9685_DEFAULT_PERIOD 5000000 /* Default period_ns = 1/200 Hz */ -#define PCA9685_OSC_CLOCK_MHZ 25 /* Internal oscillator with 25 MHz */ +#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 (1 << 4) -#define MODE1_SLEEP (1 << 4) -#define MODE2_INVRT (1 << 4) -#define MODE2_OUTDRV (1 << 2) +#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 pwm_chip chip; struct regmap *regmap; - int period_ns; -#if IS_ENABLED(CONFIG_GPIOLIB) struct mutex lock; - struct gpio_chip gpio; - DECLARE_BITMAP(pwms_inuse, PCA9685_MAXCHAN + 1); -#endif + DECLARE_BITMAP(pwms_enabled, PCA9685_MAXCHAN + 1); }; static inline struct pca9685 *to_pca(struct pwm_chip *chip) { - return container_of(chip, struct pca9685, chip); + return pwmchip_get_drvdata(chip); } -#if IS_ENABLED(CONFIG_GPIOLIB) -static bool pca9685_pwm_test_and_set_inuse(struct pca9685 *pca, int pwm_idx) +/* This function is supposed to be called with the lock mutex held */ +static bool pca9685_prescaler_can_change(struct pca9685 *pca, int channel) { - bool is_inuse; - - mutex_lock(&pca->lock); - if (pwm_idx >= PCA9685_MAXCHAN) { - /* - * "all LEDs" channel: - * pretend already in use if any of the PWMs are requested - */ - if (!bitmap_empty(pca->pwms_inuse, PCA9685_MAXCHAN)) { - is_inuse = true; - goto out; - } - } else { - /* - * regular channel: - * pretend already in use if the "all LEDs" channel is requested - */ - if (test_bit(PCA9685_MAXCHAN, pca->pwms_inuse)) { - is_inuse = true; - goto out; - } - } - is_inuse = test_and_set_bit(pwm_idx, pca->pwms_inuse); -out: - mutex_unlock(&pca->lock); - return is_inuse; + /* 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 void pca9685_pwm_clear_inuse(struct pca9685 *pca, int pwm_idx) +static int pca9685_read_reg(struct pwm_chip *chip, unsigned int reg, unsigned int *val) { - mutex_lock(&pca->lock); - clear_bit(pwm_idx, pca->pwms_inuse); - mutex_unlock(&pca->lock); -} + struct pca9685 *pca = to_pca(chip); + struct device *dev = pwmchip_parent(chip); + int err; -static int pca9685_pwm_gpio_request(struct gpio_chip *gpio, unsigned int offset) -{ - struct pca9685 *pca = gpiochip_get_data(gpio); + 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)); - if (pca9685_pwm_test_and_set_inuse(pca, offset)) - return -EBUSY; - pm_runtime_get_sync(pca->chip.dev); - return 0; + return err; } -static int pca9685_pwm_gpio_get(struct gpio_chip *gpio, unsigned int offset) +static int pca9685_write_reg(struct pwm_chip *chip, unsigned int reg, unsigned int val) { - struct pca9685 *pca = gpiochip_get_data(gpio); - struct pwm_device *pwm = &pca->chip.pwms[offset]; - unsigned int value; + struct pca9685 *pca = to_pca(chip); + struct device *dev = pwmchip_parent(chip); + int err; - regmap_read(pca->regmap, LED_N_ON_H(pwm->hwpwm), &value); + 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 value & LED_FULL; + return err; } -static void pca9685_pwm_gpio_set(struct gpio_chip *gpio, unsigned int offset, - int value) +static int pca9685_write_4reg(struct pwm_chip *chip, unsigned int reg, u8 val[4]) { - struct pca9685 *pca = gpiochip_get_data(gpio); - struct pwm_device *pwm = &pca->chip.pwms[offset]; - unsigned int on = value ? LED_FULL : 0; - - /* Clear both OFF registers */ - regmap_write(pca->regmap, LED_N_OFF_L(pwm->hwpwm), 0); - regmap_write(pca->regmap, LED_N_OFF_H(pwm->hwpwm), 0); - - /* Set the full ON bit */ - regmap_write(pca->regmap, LED_N_ON_H(pwm->hwpwm), on); -} + struct pca9685 *pca = to_pca(chip); + struct device *dev = pwmchip_parent(chip); + int err; -static void pca9685_pwm_gpio_free(struct gpio_chip *gpio, unsigned int offset) -{ - struct pca9685 *pca = gpiochip_get_data(gpio); + 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)); - pca9685_pwm_gpio_set(gpio, offset, 0); - pm_runtime_put(pca->chip.dev); - pca9685_pwm_clear_inuse(pca, offset); + return err; } -static int pca9685_pwm_gpio_get_direction(struct gpio_chip *chip, - unsigned int offset) +static int pca9685_set_sleep_mode(struct pwm_chip *chip, bool enable) { - /* Always out */ - return GPIO_LINE_DIRECTION_OUT; -} + struct pca9685 *pca = to_pca(chip); + int err; -static int pca9685_pwm_gpio_direction_input(struct gpio_chip *gpio, - unsigned int offset) -{ - return -EINVAL; -} + err = regmap_update_bits(pca->regmap, PCA9685_MODE1, + MODE1_SLEEP, enable ? MODE1_SLEEP : 0); + if (err) + return err; -static int pca9685_pwm_gpio_direction_output(struct gpio_chip *gpio, - unsigned int offset, int value) -{ - pca9685_pwm_gpio_set(gpio, offset, value); + if (!enable) { + /* Wait 500us for the oscillator to be back up */ + udelay(500); + } return 0; } -/* - * The PCA9685 has a bit for turning the PWM output full off or on. Some - * boards like Intel Galileo actually uses these as normal GPIOs so we - * expose a GPIO chip here which can exclusively take over the underlying - * PWM channel. - */ -static int pca9685_pwm_gpio_probe(struct pca9685 *pca) -{ - struct device *dev = pca->chip.dev; - - mutex_init(&pca->lock); +struct pca9685_waveform { + u8 onoff[4]; + u8 prescale; +}; - pca->gpio.label = dev_name(dev); - pca->gpio.parent = dev; - pca->gpio.request = pca9685_pwm_gpio_request; - pca->gpio.free = pca9685_pwm_gpio_free; - pca->gpio.get_direction = pca9685_pwm_gpio_get_direction; - pca->gpio.direction_input = pca9685_pwm_gpio_direction_input; - pca->gpio.direction_output = pca9685_pwm_gpio_direction_output; - pca->gpio.get = pca9685_pwm_gpio_get; - pca->gpio.set = pca9685_pwm_gpio_set; - pca->gpio.base = -1; - pca->gpio.ngpio = PCA9685_MAXCHAN; - pca->gpio.can_sleep = true; - - return devm_gpiochip_add_data(dev, &pca->gpio, pca); -} -#else -static inline bool pca9685_pwm_test_and_set_inuse(struct pca9685 *pca, - int pwm_idx) +static int pca9685_round_waveform_tohw(struct pwm_chip *chip, struct pwm_device *pwm, const struct pwm_waveform *wf, void *_wfhw) { - return false; -} + 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; -static inline void -pca9685_pwm_clear_inuse(struct pca9685 *pca, int pwm_idx) -{ -} + if (!wf->period_length_ns) { + *wfhw = (typeof(*wfhw)){ + .onoff = { 0, 0, 0, LED_FULL, }, + .prescale = 0, + }; -static inline int pca9685_pwm_gpio_probe(struct pca9685 *pca) -{ - return 0; -} -#endif + 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); -static void pca9685_set_sleep_mode(struct pca9685 *pca, bool enable) -{ - regmap_update_bits(pca->regmap, PCA9685_MODE1, - MODE1_SLEEP, enable ? MODE1_SLEEP : 0); - if (!enable) { - /* Wait 500us for the oscillator to be back up */ - udelay(500); + return 0; } -} - -static int pca9685_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, - int duty_ns, int period_ns) -{ - struct pca9685 *pca = to_pca(chip); - unsigned long long duty; - unsigned int reg; - int prescale; - - if (period_ns != pca->period_ns) { - prescale = DIV_ROUND_CLOSEST(PCA9685_OSC_CLOCK_MHZ * period_ns, - PCA9685_COUNTER_RANGE * 1000) - 1; - - if (prescale >= PCA9685_PRESCALE_MIN && - prescale <= PCA9685_PRESCALE_MAX) { - /* - * putting the chip briefly into SLEEP mode - * at this point won't interfere with the - * pm_runtime framework, because the pm_runtime - * state is guaranteed active here. - */ - /* Put chip into sleep mode */ - pca9685_set_sleep_mode(pca, true); - /* Change the chip-wide output frequency */ - regmap_write(pca->regmap, PCA9685_PRESCALE, prescale); + 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; + } - /* Wake the chip up */ - pca9685_set_sleep_mode(pca, false); + guard(mutex)(&pca->lock); - pca->period_ns = period_ns; - } else { - dev_err(chip->dev, - "prescaler not set: period out of bounds!\n"); - return -EINVAL; - } - } + if (!pca9685_prescaler_can_change(pca, pwm->hwpwm)) { + unsigned int current_prescale; + int ret; - if (duty_ns < 1) { - if (pwm->hwpwm >= PCA9685_MAXCHAN) - reg = PCA9685_ALL_LED_OFF_H; - else - reg = LED_N_OFF_H(pwm->hwpwm); + ret = regmap_read(pca->regmap, PCA9685_PRESCALE, ¤t_prescale); + if (ret) + return ret; - regmap_write(pca->regmap, reg, LED_FULL); + if (current_prescale > best_prescale) + ret_tohw = 1; - return 0; + prescale = current_prescale; + } else { + prescale = best_prescale; } - if (duty_ns == period_ns) { - /* Clear both OFF registers */ - if (pwm->hwpwm >= PCA9685_MAXCHAN) - reg = PCA9685_ALL_LED_OFF_L; - else - reg = LED_N_OFF_L(pwm->hwpwm); - - regmap_write(pca->regmap, reg, 0x0); + period_ns = PCA9685_COUNTER_RANGE * PCA9685_QUANTUM_NS(prescale); - if (pwm->hwpwm >= PCA9685_MAXCHAN) - reg = PCA9685_ALL_LED_OFF_H; - else - reg = LED_N_OFF_H(pwm->hwpwm); + duty = (unsigned)min_t(u64, wf->duty_length_ns, period_ns) / PCA9685_QUANTUM_NS(prescale); - regmap_write(pca->regmap, reg, 0x0); + if (duty < PCA9685_COUNTER_RANGE) { + unsigned int on, off; - /* Set the full ON bit */ - if (pwm->hwpwm >= PCA9685_MAXCHAN) - reg = PCA9685_ALL_LED_ON_H; - else - reg = LED_N_ON_H(pwm->hwpwm); + on = (unsigned)min_t(u64, wf->duty_offset_ns, period_ns) / PCA9685_QUANTUM_NS(prescale); + off = (on + duty) % PCA9685_COUNTER_RANGE; - regmap_write(pca->regmap, reg, LED_FULL); + /* + * With a zero duty cycle, it doesn't matter if period was + * rounded up + */ + if (!duty) + ret_tohw = 0; - return 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, + }; } - duty = PCA9685_COUNTER_RANGE * (unsigned long long)duty_ns; - duty = DIV_ROUND_UP_ULL(duty, period_ns); + 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); - if (pwm->hwpwm >= PCA9685_MAXCHAN) - reg = PCA9685_ALL_LED_OFF_L; - else - reg = LED_N_OFF_L(pwm->hwpwm); + return ret_tohw; +} - regmap_write(pca->regmap, reg, (int)duty & 0xff); +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 (pwm->hwpwm >= PCA9685_MAXCHAN) - reg = PCA9685_ALL_LED_OFF_H; + if (wfhw->prescale) + prescale = wfhw->prescale; else - reg = LED_N_OFF_H(pwm->hwpwm); + scoped_guard(mutex, &pca->lock) { + int ret; - regmap_write(pca->regmap, reg, ((int)duty >> 8) & 0xf); + ret = regmap_read(pca->regmap, PCA9685_PRESCALE, &prescale); + if (ret) + return ret; + } - /* Clear the full ON bit, otherwise the set OFF time has no effect */ - if (pwm->hwpwm >= PCA9685_MAXCHAN) - reg = PCA9685_ALL_LED_ON_H; - else - reg = LED_N_ON_H(pwm->hwpwm); + 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; - regmap_write(pca->regmap, reg, 0); + 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_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) +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 reg; - - /* - * The PWM subsystem does not support a pre-delay. - * So, set the ON-timeout to 0 - */ - if (pwm->hwpwm >= PCA9685_MAXCHAN) - reg = PCA9685_ALL_LED_ON_L; - else - reg = LED_N_ON_L(pwm->hwpwm); - - regmap_write(pca->regmap, reg, 0); + unsigned int prescale; + int ret; - if (pwm->hwpwm >= PCA9685_MAXCHAN) - reg = PCA9685_ALL_LED_ON_H; - else - reg = LED_N_ON_H(pwm->hwpwm); + guard(mutex)(&pca->lock); - regmap_write(pca->regmap, reg, 0); + ret = regmap_bulk_read(pca->regmap, REG_ON_L(pwm->hwpwm), &wfhw->onoff, 4); + if (ret) + return ret; - /* - * Clear the full-off bit. - * It has precedence over the others and must be off. - */ - if (pwm->hwpwm >= PCA9685_MAXCHAN) - reg = PCA9685_ALL_LED_OFF_H; - else - reg = LED_N_OFF_H(pwm->hwpwm); + ret = regmap_read(pca->regmap, PCA9685_PRESCALE, &prescale); + if (ret) + return ret; - regmap_update_bits(pca->regmap, reg, LED_FULL, 0x0); + wfhw->prescale = prescale; return 0; } -static void pca9685_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) +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 reg; + unsigned int current_prescale; + int ret; - if (pwm->hwpwm >= PCA9685_MAXCHAN) - reg = PCA9685_ALL_LED_OFF_H; - else - reg = LED_N_OFF_H(pwm->hwpwm); + guard(mutex)(&pca->lock); - regmap_write(pca->regmap, reg, LED_FULL); + if (wfhw->prescale) { + ret = regmap_read(pca->regmap, PCA9685_PRESCALE, ¤t_prescale); + if (ret) + return ret; - /* Clear the LED_OFF counter. */ - if (pwm->hwpwm >= PCA9685_MAXCHAN) - reg = PCA9685_ALL_LED_OFF_L; - else - reg = LED_N_OFF_L(pwm->hwpwm); + 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; - regmap_write(pca->regmap, reg, 0x0); + /* 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 (pca9685_pwm_test_and_set_inuse(pca, pwm->hwpwm)) - return -EBUSY; - pm_runtime_get_sync(chip->dev); + 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; } @@ -418,37 +377,67 @@ static void pca9685_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm) { struct pca9685 *pca = to_pca(chip); - pca9685_pwm_disable(chip, pwm); - pm_runtime_put(chip->dev); - pca9685_pwm_clear_inuse(pca, pwm->hwpwm); + 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 = { - .enable = pca9685_pwm_enable, - .disable = pca9685_pwm_disable, - .config = pca9685_pwm_config, + .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, - .owner = THIS_MODULE, }; +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_NONE, + .cache_type = REGCACHE_MAPLE, }; -static int pca9685_pwm_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int pca9685_pwm_probe(struct i2c_client *client) { + struct pwm_chip *chip; struct pca9685 *pca; + unsigned int reg; int ret; - int mode2; - pca = devm_kzalloc(&client->dev, sizeof(*pca), GFP_KERNEL); - if (!pca) - return -ENOMEM; + /* 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)) { @@ -457,107 +446,114 @@ static int pca9685_pwm_probe(struct i2c_client *client, ret); return ret; } - pca->period_ns = PCA9685_DEFAULT_PERIOD; - i2c_set_clientdata(client, pca); + i2c_set_clientdata(client, chip); + + mutex_init(&pca->lock); - regmap_read(pca->regmap, PCA9685_MODE2, &mode2); + /* clear MODE2_OCH */ + reg = 0; if (device_property_read_bool(&client->dev, "invert")) - mode2 |= MODE2_INVRT; + reg |= MODE2_INVRT; else - mode2 &= ~MODE2_INVRT; + reg &= ~MODE2_INVRT; if (device_property_read_bool(&client->dev, "open-drain")) - mode2 &= ~MODE2_OUTDRV; + reg &= ~MODE2_OUTDRV; else - mode2 |= MODE2_OUTDRV; + reg |= MODE2_OUTDRV; - regmap_write(pca->regmap, PCA9685_MODE2, mode2); + ret = pca9685_write_reg(chip, PCA9685_MODE2, reg); + if (ret) + return ret; - /* clear all "full off" bits */ - regmap_write(pca->regmap, PCA9685_ALL_LED_OFF_L, 0); - regmap_write(pca->regmap, PCA9685_ALL_LED_OFF_H, 0); + /* + * Disable all LED ALLCALL and SUBx addresses to avoid bus collisions, + * enable Auto-Increment. + */ + pca9685_read_reg(chip, PCA9685_MODE1, ®); + reg &= ~(MODE1_ALLCALL | MODE1_SUB1 | MODE1_SUB2 | MODE1_SUB3); + reg |= MODE1_AI; + pca9685_write_reg(chip, PCA9685_MODE1, reg); - pca->chip.ops = &pca9685_pwm_ops; - /* add an extra channel for ALL_LED */ - pca->chip.npwm = PCA9685_MAXCHAN + 1; + /* 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"); - pca->chip.dev = &client->dev; - pca->chip.base = -1; + chip->ops = &pca9685_pwm_ops; - ret = pwmchip_add(&pca->chip); + ret = pwmchip_add(chip); if (ret < 0) return ret; - ret = pca9685_pwm_gpio_probe(pca); - if (ret < 0) { - pwmchip_remove(&pca->chip); - return ret; - } - - /* the chip comes out of power-up in the active state */ - pm_runtime_set_active(&client->dev); - /* - * enable will put the chip into suspend, which is what we - * want as all outputs are disabled at this point - */ 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 int pca9685_pwm_remove(struct i2c_client *client) +static void pca9685_pwm_remove(struct i2c_client *client) { - struct pca9685 *pca = i2c_get_clientdata(client); - int ret; + 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); + } - ret = pwmchip_remove(&pca->chip); - if (ret) - return ret; pm_runtime_disable(&client->dev); - return 0; } static int __maybe_unused pca9685_pwm_runtime_suspend(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); - struct pca9685 *pca = i2c_get_clientdata(client); + struct pwm_chip *chip = i2c_get_clientdata(client); - pca9685_set_sleep_mode(pca, true); + 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 pca9685 *pca = i2c_get_clientdata(client); + struct pwm_chip *chip = i2c_get_clientdata(client); - pca9685_set_sleep_mode(pca, false); + pca9685_set_sleep_mode(chip, false); return 0; } static const struct i2c_device_id pca9685_id[] = { - { "pca9685", 0 }, - { /* sentinel */ }, + { "pca9685" }, + { /* sentinel */ } }; MODULE_DEVICE_TABLE(i2c, pca9685_id); -#ifdef CONFIG_ACPI static const struct acpi_device_id pca9685_acpi_ids[] = { { "INT3492", 0 }, { /* sentinel */ }, }; MODULE_DEVICE_TABLE(acpi, pca9685_acpi_ids); -#endif -#ifdef CONFIG_OF static const struct of_device_id pca9685_dt_ids[] = { { .compatible = "nxp,pca9685-pwm", }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, pca9685_dt_ids); -#endif static const struct dev_pm_ops pca9685_pwm_pm = { SET_RUNTIME_PM_OPS(pca9685_pwm_runtime_suspend, @@ -567,8 +563,8 @@ static const struct dev_pm_ops pca9685_pwm_pm = { static struct i2c_driver pca9685_i2c_driver = { .driver = { .name = "pca9685-pwm", - .acpi_match_table = ACPI_PTR(pca9685_acpi_ids), - .of_match_table = of_match_ptr(pca9685_dt_ids), + .acpi_match_table = pca9685_acpi_ids, + .of_match_table = pca9685_dt_ids, .pm = &pca9685_pwm_pm, }, .probe = pca9685_pwm_probe, diff --git a/drivers/pwm/pwm-puv3.c b/drivers/pwm/pwm-puv3.c deleted file mode 100644 index 9d0bd87a425e..000000000000 --- a/drivers/pwm/pwm-puv3.c +++ /dev/null @@ -1,150 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * linux/arch/unicore32/kernel/pwm.c - * - * Code specific to PKUnity SoC and UniCore ISA - * - * Maintained by GUAN Xue-tao <gxt@mprc.pku.edu.cn> - * Copyright (C) 2001-2010 Guan Xuetao - */ - -#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 <asm/div64.h> -#include <mach/hardware.h> - -struct puv3_pwm_chip { - struct pwm_chip chip; - void __iomem *base; - struct clk *clk; -}; - -static inline struct puv3_pwm_chip *to_puv3(struct pwm_chip *chip) -{ - return container_of(chip, struct puv3_pwm_chip, chip); -} - -/* - * period_ns = 10^9 * (PRESCALE + 1) * (PV + 1) / PWM_CLK_RATE - * duty_ns = 10^9 * (PRESCALE + 1) * DC / PWM_CLK_RATE - */ -static int puv3_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, - int duty_ns, int period_ns) -{ - unsigned long period_cycles, prescale, pv, dc; - struct puv3_pwm_chip *puv3 = to_puv3(chip); - unsigned long long c; - - c = clk_get_rate(puv3->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 = OST_PWMDCCR_FDCYCLE; - else - dc = (pv + 1) * duty_ns / period_ns; - - /* - * NOTE: the clock to PWM has to be enabled first - * before writing to the registers - */ - clk_prepare_enable(puv3->clk); - - writel(prescale, puv3->base + OST_PWM_PWCR); - writel(pv - dc, puv3->base + OST_PWM_DCCR); - writel(pv, puv3->base + OST_PWM_PCR); - - clk_disable_unprepare(puv3->clk); - - return 0; -} - -static int puv3_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) -{ - struct puv3_pwm_chip *puv3 = to_puv3(chip); - - return clk_prepare_enable(puv3->clk); -} - -static void puv3_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) -{ - struct puv3_pwm_chip *puv3 = to_puv3(chip); - - clk_disable_unprepare(puv3->clk); -} - -static const struct pwm_ops puv3_pwm_ops = { - .config = puv3_pwm_config, - .enable = puv3_pwm_enable, - .disable = puv3_pwm_disable, - .owner = THIS_MODULE, -}; - -static int pwm_probe(struct platform_device *pdev) -{ - struct puv3_pwm_chip *puv3; - struct resource *r; - int ret; - - puv3 = devm_kzalloc(&pdev->dev, sizeof(*puv3), GFP_KERNEL); - if (!puv3) - return -ENOMEM; - - puv3->clk = devm_clk_get(&pdev->dev, "OST_CLK"); - if (IS_ERR(puv3->clk)) - return PTR_ERR(puv3->clk); - - r = platform_get_resource(pdev, IORESOURCE_MEM, 0); - puv3->base = devm_ioremap_resource(&pdev->dev, r); - if (IS_ERR(puv3->base)) - return PTR_ERR(puv3->base); - - puv3->chip.dev = &pdev->dev; - puv3->chip.ops = &puv3_pwm_ops; - puv3->chip.base = -1; - puv3->chip.npwm = 1; - - ret = pwmchip_add(&puv3->chip); - if (ret < 0) { - dev_err(&pdev->dev, "pwmchip_add() failed: %d\n", ret); - return ret; - } - - platform_set_drvdata(pdev, puv3); - return 0; -} - -static int pwm_remove(struct platform_device *pdev) -{ - struct puv3_pwm_chip *puv3 = platform_get_drvdata(pdev); - - return pwmchip_remove(&puv3->chip); -} - -static struct platform_driver puv3_pwm_driver = { - .driver = { - .name = "PKUnity-v3-PWM", - }, - .probe = pwm_probe, - .remove = pwm_remove, -}; -module_platform_driver(puv3_pwm_driver); - -MODULE_LICENSE("GPL v2"); diff --git a/drivers/pwm/pwm-pxa.c b/drivers/pwm/pwm-pxa.c index a2a0912c2dcd..0f5bdb0e395e 100644 --- a/drivers/pwm/pwm-pxa.c +++ b/drivers/pwm/pwm-pxa.c @@ -6,8 +6,16 @@ * * 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/mod_devicetable.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/platform_device.h> @@ -16,7 +24,8 @@ #include <linux/clk.h> #include <linux/io.h> #include <linux/pwm.h> -#include <linux/of_device.h> +#include <linux/of.h> +#include <linux/reset.h> #include <asm/div64.h> @@ -41,7 +50,6 @@ MODULE_DEVICE_TABLE(platform, pwm_id_table); #define PWMDCR_FD (1 << 10) struct pxa_pwm_chip { - struct pwm_chip chip; struct device *dev; struct clk *clk; @@ -50,7 +58,7 @@ struct pxa_pwm_chip { static inline struct pxa_pwm_chip *to_pxa_pwm_chip(struct pwm_chip *chip) { - return container_of(chip, struct pxa_pwm_chip, chip); + return pwmchip_get_drvdata(chip); } /* @@ -58,13 +66,12 @@ static inline struct pxa_pwm_chip *to_pxa_pwm_chip(struct pwm_chip *chip) * duty_ns = 10^9 * (PRESCALE + 1) * DC / PWM_CLK_RATE */ static int pxa_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, - int duty_ns, int period_ns) + 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; - int rc; offset = pwm->hwpwm ? 0x10 : 0; @@ -84,42 +91,50 @@ static int pxa_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, if (duty_ns == period_ns) dc = PWMDCR_FD; else - dc = (pv + 1) * duty_ns / period_ns; - - /* NOTE: the clock to PWM has to be enabled first - * before writing to the registers - */ - rc = clk_prepare_enable(pc->clk); - if (rc < 0) - return rc; + dc = mul_u64_u64_div_u64(pv + 1, duty_ns, period_ns); - writel(prescale, pc->mmio_base + offset + PWMCR); + writel(prescale | PWMCR_SD, pc->mmio_base + offset + PWMCR); writel(dc, pc->mmio_base + offset + PWMDCR); writel(pv, pc->mmio_base + offset + PWMPCR); - clk_disable_unprepare(pc->clk); return 0; } -static int pxa_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) +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; - return clk_prepare_enable(pc->clk); -} + if (state->polarity != PWM_POLARITY_NORMAL) + return -EINVAL; -static void pxa_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) -{ - struct pxa_pwm_chip *pc = to_pxa_pwm_chip(chip); + 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 = { - .config = pxa_pwm_config, - .enable = pxa_pwm_enable, - .disable = pxa_pwm_disable, - .owner = THIS_MODULE, + .apply = pxa_pwm_apply, }; #ifdef CONFIG_OF @@ -141,82 +156,49 @@ MODULE_DEVICE_TABLE(of, pwm_of_match); #define pwm_of_match NULL #endif -static const struct platform_device_id *pxa_pwm_get_id_dt(struct device *dev) -{ - const struct of_device_id *id = of_match_device(pwm_of_match, dev); - - return id ? id->data : NULL; -} - -static struct pwm_device * -pxa_pwm_of_xlate(struct pwm_chip *pc, const struct of_phandle_args *args) -{ - struct pwm_device *pwm; - - pwm = pwm_request_from_chip(pc, 0, NULL); - if (IS_ERR(pwm)) - return pwm; - - pwm->args.period = args->args[0]; - - return pwm; -} - static int pwm_probe(struct platform_device *pdev) { const struct platform_device_id *id = platform_get_device_id(pdev); - struct pxa_pwm_chip *pwm; - struct resource *r; + struct pwm_chip *chip; + struct pxa_pwm_chip *pc; + struct device *dev = &pdev->dev; + struct reset_control *rst; int ret = 0; if (IS_ENABLED(CONFIG_OF) && id == NULL) - id = pxa_pwm_get_id_dt(&pdev->dev); + id = of_device_get_match_data(dev); if (id == NULL) return -EINVAL; - pwm = devm_kzalloc(&pdev->dev, sizeof(*pwm), GFP_KERNEL); - if (pwm == NULL) - return -ENOMEM; + 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); - pwm->clk = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(pwm->clk)) - return PTR_ERR(pwm->clk); + pc->clk = devm_clk_get(dev, NULL); + if (IS_ERR(pc->clk)) + return dev_err_probe(dev, PTR_ERR(pc->clk), "Failed to get clock\n"); - pwm->chip.dev = &pdev->dev; - pwm->chip.ops = &pxa_pwm_ops; - pwm->chip.base = -1; - pwm->chip.npwm = (id->driver_data & HAS_SECONDARY_PWM) ? 2 : 1; + rst = devm_reset_control_get_optional_exclusive_deasserted(dev, NULL); + if (IS_ERR(rst)) + return PTR_ERR(rst); - if (IS_ENABLED(CONFIG_OF)) { - pwm->chip.of_xlate = pxa_pwm_of_xlate; - pwm->chip.of_pwm_n_cells = 1; - } - - r = platform_get_resource(pdev, IORESOURCE_MEM, 0); - pwm->mmio_base = devm_ioremap_resource(&pdev->dev, r); - if (IS_ERR(pwm->mmio_base)) - return PTR_ERR(pwm->mmio_base); - - ret = pwmchip_add(&pwm->chip); - if (ret < 0) { - dev_err(&pdev->dev, "pwmchip_add() failed: %d\n", ret); - return ret; - } + chip->ops = &pxa_pwm_ops; - platform_set_drvdata(pdev, pwm); - return 0; -} + if (IS_ENABLED(CONFIG_OF)) + chip->of_xlate = of_pwm_single_xlate; -static int pwm_remove(struct platform_device *pdev) -{ - struct pxa_pwm_chip *chip; + pc->mmio_base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(pc->mmio_base)) + return PTR_ERR(pc->mmio_base); - chip = platform_get_drvdata(pdev); - if (chip == NULL) - return -ENODEV; + ret = devm_pwmchip_add(dev, chip); + if (ret < 0) + return dev_err_probe(dev, ret, "pwmchip_add() failed\n"); - return pwmchip_remove(&chip->chip); + return 0; } static struct platform_driver pwm_driver = { @@ -225,10 +207,10 @@ static struct platform_driver pwm_driver = { .of_match_table = pwm_of_match, }, .probe = pwm_probe, - .remove = pwm_remove, .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 000000000000..8921e7ea2cea --- /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 index 7ab9eb6616d9..578dbdd2d5a7 100644 --- a/drivers/pwm/pwm-rcar.c +++ b/drivers/pwm/pwm-rcar.c @@ -8,6 +8,7 @@ * - The hardware cannot generate a 0% duty cycle. */ +#include <linux/bitfield.h> #include <linux/clk.h> #include <linux/err.h> #include <linux/io.h> @@ -38,14 +39,13 @@ #define RCAR_PWMCNT_PH0_SHIFT 0 struct rcar_pwm_chip { - struct pwm_chip chip; void __iomem *base; struct clk *clk; }; static inline struct rcar_pwm_chip *to_rcar_pwm_chip(struct pwm_chip *chip) { - return container_of(chip, struct rcar_pwm_chip, chip); + return pwmchip_get_drvdata(chip); } static void rcar_pwm_write(struct rcar_pwm_chip *rp, u32 data, @@ -103,23 +103,24 @@ static void rcar_pwm_set_clock_control(struct rcar_pwm_chip *rp, rcar_pwm_write(rp, value, RCAR_PWMCR); } -static int rcar_pwm_set_counter(struct rcar_pwm_chip *rp, int div, int duty_ns, - int period_ns) +static int rcar_pwm_set_counter(struct rcar_pwm_chip *rp, int div, u64 duty_ns, + u64 period_ns) { - unsigned long long one_cycle, tmp; /* 0.01 nanoseconds */ + unsigned long long tmp; unsigned long clk_rate = clk_get_rate(rp->clk); u32 cyc, ph; - one_cycle = (unsigned long long)NSEC_PER_SEC * 100ULL * (1 << div); - do_div(one_cycle, clk_rate); + /* 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); - tmp = period_ns * 100ULL; - do_div(tmp, one_cycle); - cyc = (tmp << RCAR_PWMCNT_CYC0_SHIFT) & RCAR_PWMCNT_CYC0_MASK; + cyc = FIELD_PREP(RCAR_PWMCNT_CYC0_MASK, tmp); - tmp = duty_ns * 100ULL; - do_div(tmp, one_cycle); - ph = tmp & RCAR_PWMCNT_PH0_MASK; + 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) @@ -132,12 +133,12 @@ static int rcar_pwm_set_counter(struct rcar_pwm_chip *rp, int div, int duty_ns, static int rcar_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm) { - return pm_runtime_get_sync(chip->dev); + return pm_runtime_get_sync(pwmchip_parent(chip)); } static void rcar_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm) { - pm_runtime_put(chip->dev); + pm_runtime_put(pwmchip_parent(chip)); } static int rcar_pwm_enable(struct rcar_pwm_chip *rp) @@ -168,7 +169,7 @@ static int rcar_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, /* This HW/driver only supports normal polarity */ if (state->polarity != PWM_POLARITY_NORMAL) - return -ENOTSUPP; + return -EINVAL; if (!state->enabled) { rcar_pwm_disable(rp); @@ -198,21 +199,20 @@ static const struct pwm_ops rcar_pwm_ops = { .request = rcar_pwm_request, .free = rcar_pwm_free, .apply = rcar_pwm_apply, - .owner = THIS_MODULE, }; static int rcar_pwm_probe(struct platform_device *pdev) { + struct pwm_chip *chip; struct rcar_pwm_chip *rcar_pwm; - struct resource *res; int ret; - rcar_pwm = devm_kzalloc(&pdev->dev, sizeof(*rcar_pwm), GFP_KERNEL); - if (rcar_pwm == NULL) - return -ENOMEM; + 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); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - rcar_pwm->base = devm_ioremap_resource(&pdev->dev, res); + rcar_pwm->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(rcar_pwm->base)) return PTR_ERR(rcar_pwm->base); @@ -222,16 +222,13 @@ static int rcar_pwm_probe(struct platform_device *pdev) return PTR_ERR(rcar_pwm->clk); } - platform_set_drvdata(pdev, rcar_pwm); + chip->ops = &rcar_pwm_ops; - rcar_pwm->chip.dev = &pdev->dev; - rcar_pwm->chip.ops = &rcar_pwm_ops; - rcar_pwm->chip.base = -1; - rcar_pwm->chip.npwm = 1; + platform_set_drvdata(pdev, chip); pm_runtime_enable(&pdev->dev); - ret = pwmchip_add(&rcar_pwm->chip); + ret = pwmchip_add(chip); if (ret < 0) { dev_err(&pdev->dev, "failed to register PWM chip: %d\n", ret); pm_runtime_disable(&pdev->dev); @@ -241,16 +238,13 @@ static int rcar_pwm_probe(struct platform_device *pdev) return 0; } -static int rcar_pwm_remove(struct platform_device *pdev) +static void rcar_pwm_remove(struct platform_device *pdev) { - struct rcar_pwm_chip *rcar_pwm = platform_get_drvdata(pdev); - int ret; + struct pwm_chip *chip = platform_get_drvdata(pdev); - ret = pwmchip_remove(&rcar_pwm->chip); + pwmchip_remove(chip); pm_runtime_disable(&pdev->dev); - - return ret; } static const struct of_device_id rcar_pwm_of_table[] = { @@ -264,7 +258,7 @@ static struct platform_driver rcar_pwm_driver = { .remove = rcar_pwm_remove, .driver = { .name = "pwm-rcar", - .of_match_table = of_match_ptr(rcar_pwm_of_table), + .of_match_table = rcar_pwm_of_table, } }; module_platform_driver(rcar_pwm_driver); diff --git a/drivers/pwm/pwm-renesas-tpu.c b/drivers/pwm/pwm-renesas-tpu.c index 81ad5a551455..2196080b4177 100644 --- a/drivers/pwm/pwm-renesas-tpu.c +++ b/drivers/pwm/pwm-renesas-tpu.c @@ -11,7 +11,6 @@ #include <linux/init.h> #include <linux/ioport.h> #include <linux/module.h> -#include <linux/mutex.h> #include <linux/of.h> #include <linux/platform_device.h> #include <linux/pm_runtime.h> @@ -80,80 +79,83 @@ struct tpu_pwm_device { struct tpu_device { struct platform_device *pdev; - struct pwm_chip chip; spinlock_t lock; void __iomem *base; struct clk *clk; + struct tpu_pwm_device tpd[TPU_CHANNEL_MAX]; }; -#define to_tpu_device(c) container_of(c, struct tpu_device, chip) +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 *pwm, int reg_nr, u16 value) +static void tpu_pwm_write(struct tpu_pwm_device *tpd, int reg_nr, u16 value) { - void __iomem *base = pwm->tpu->base + TPU_CHANNEL_OFFSET - + pwm->channel * TPU_CHANNEL_SIZE; + 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 *pwm, +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(&pwm->tpu->pdev->dev, "%u: configuring pin as %s\n", - pwm->channel, states[state]); + 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(pwm, TPU_TIORn, - pwm->polarity == PWM_POLARITY_INVERSED ? + 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(pwm, TPU_TIORn, - pwm->polarity == PWM_POLARITY_INVERSED ? + 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(pwm, TPU_TIORn, - pwm->polarity == PWM_POLARITY_INVERSED ? + 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 *pwm, int start) +static void tpu_pwm_start_stop(struct tpu_pwm_device *tpd, int start) { unsigned long flags; u16 value; - spin_lock_irqsave(&pwm->tpu->lock, flags); - value = ioread16(pwm->tpu->base + TPU_TSTR); + spin_lock_irqsave(&tpd->tpu->lock, flags); + value = ioread16(tpd->tpu->base + TPU_TSTR); if (start) - value |= 1 << pwm->channel; + value |= 1 << tpd->channel; else - value &= ~(1 << pwm->channel); + value &= ~(1 << tpd->channel); - iowrite16(value, pwm->tpu->base + TPU_TSTR); - spin_unlock_irqrestore(&pwm->tpu->lock, flags); + iowrite16(value, tpd->tpu->base + TPU_TSTR); + spin_unlock_irqrestore(&tpd->tpu->lock, flags); } -static int tpu_pwm_timer_start(struct tpu_pwm_device *pwm) +static int tpu_pwm_timer_start(struct tpu_pwm_device *tpd) { int ret; - if (!pwm->timer_on) { + if (!tpd->timer_on) { /* Wake up device and enable clock. */ - pm_runtime_get_sync(&pwm->tpu->pdev->dev); - ret = clk_prepare_enable(pwm->tpu->clk); + pm_runtime_get_sync(&tpd->tpu->pdev->dev); + ret = clk_prepare_enable(tpd->tpu->clk); if (ret) { - dev_err(&pwm->tpu->pdev->dev, "cannot enable clock\n"); + dev_err(&tpd->tpu->pdev->dev, "cannot enable clock\n"); return ret; } - pwm->timer_on = true; + tpd->timer_on = true; } /* @@ -161,8 +163,8 @@ static int tpu_pwm_timer_start(struct tpu_pwm_device *pwm) * completely. First drive the pin to the inactive state to avoid * glitches. */ - tpu_pwm_set_pin(pwm, TPU_PIN_INACTIVE); - tpu_pwm_start_stop(pwm, false); + tpu_pwm_set_pin(tpd, TPU_PIN_INACTIVE); + tpu_pwm_start_stop(tpd, false); /* * - Clear TCNT on TGRB match @@ -172,142 +174,164 @@ static int tpu_pwm_timer_start(struct tpu_pwm_device *pwm) * - Output 1 until TGRA, output 0 until TGRB (active high polarity * - PWM mode */ - tpu_pwm_write(pwm, TPU_TCRn, TPU_TCR_CCLR_TGRB | TPU_TCR_CKEG_RISING | - pwm->prescaler); - tpu_pwm_write(pwm, TPU_TMDRn, TPU_TMDR_MD_PWM); - tpu_pwm_set_pin(pwm, TPU_PIN_PWM); - tpu_pwm_write(pwm, TPU_TGRAn, pwm->duty); - tpu_pwm_write(pwm, TPU_TGRBn, pwm->period); + 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(&pwm->tpu->pdev->dev, "%u: TGRA 0x%04x TGRB 0x%04x\n", - pwm->channel, pwm->duty, pwm->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(pwm, true); + tpu_pwm_start_stop(tpd, true); return 0; } -static void tpu_pwm_timer_stop(struct tpu_pwm_device *pwm) +static void tpu_pwm_timer_stop(struct tpu_pwm_device *tpd) { - if (!pwm->timer_on) + if (!tpd->timer_on) return; /* Disable channel. */ - tpu_pwm_start_stop(pwm, false); + tpu_pwm_start_stop(tpd, false); /* Stop clock and mark device as idle. */ - clk_disable_unprepare(pwm->tpu->clk); - pm_runtime_put(&pwm->tpu->pdev->dev); + clk_disable_unprepare(tpd->tpu->clk); + pm_runtime_put(&tpd->tpu->pdev->dev); - pwm->timer_on = false; + tpd->timer_on = false; } /* ----------------------------------------------------------------------------- * PWM API */ -static int tpu_pwm_request(struct pwm_chip *chip, struct pwm_device *_pwm) +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 *pwm; + struct tpu_pwm_device *tpd; - if (_pwm->hwpwm >= TPU_CHANNEL_MAX) + if (pwm->hwpwm >= TPU_CHANNEL_MAX) return -EINVAL; - pwm = kzalloc(sizeof(*pwm), GFP_KERNEL); - if (pwm == NULL) - return -ENOMEM; + tpd = &tpu->tpd[pwm->hwpwm]; - pwm->tpu = tpu; - pwm->channel = _pwm->hwpwm; - pwm->polarity = PWM_POLARITY_NORMAL; - pwm->prescaler = 0; - pwm->period = 0; - pwm->duty = 0; + tpd->tpu = tpu; + tpd->channel = pwm->hwpwm; + tpd->polarity = PWM_POLARITY_NORMAL; + tpd->prescaler = 0; + tpd->period = 0; + tpd->duty = 0; - pwm->timer_on = false; - - pwm_set_chip_data(_pwm, pwm); + tpd->timer_on = false; return 0; } -static void tpu_pwm_free(struct pwm_chip *chip, struct pwm_device *_pwm) +static void tpu_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm) { - struct tpu_pwm_device *pwm = pwm_get_chip_data(_pwm); + struct tpu_device *tpu = to_tpu_device(chip); + struct tpu_pwm_device *tpd = &tpu->tpd[pwm->hwpwm]; - tpu_pwm_timer_stop(pwm); - kfree(pwm); + tpu_pwm_timer_stop(tpd); } -static int tpu_pwm_config(struct pwm_chip *chip, struct pwm_device *_pwm, - int duty_ns, int period_ns) +static int tpu_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, + u64 duty_ns, u64 period_ns, bool enabled) { - static const unsigned int prescalers[] = { 1, 4, 16, 64 }; - struct tpu_pwm_device *pwm = pwm_get_chip_data(_pwm); 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; - u32 period; + 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); + /* - * Pick a prescaler to avoid overflowing the counter. - * TODO: Pick the highest acceptable prescaler. + * 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. */ - clk_rate = clk_get_rate(tpu->clk); + switch (period) { + case 1 ... 0xffff: + prescaler = 0; + break; - for (prescaler = 0; prescaler < ARRAY_SIZE(prescalers); ++prescaler) { - period = clk_rate / prescalers[prescaler] - / (NSEC_PER_SEC / period_ns); - if (period <= 0xffff) - break; - } + case 0x10000 ... 0x3ffff: + prescaler = 1; + break; + + case 0x40000 ... 0xfffff: + prescaler = 2; + break; - if (prescaler == ARRAY_SIZE(prescalers) || period == 0) { - dev_err(&tpu->pdev->dev, "clock rate mismatch\n"); - return -ENOTSUPP; + case 0x100000 ... 0x3fffff: + prescaler = 3; + break; + + default: + return -EINVAL; } - if (duty_ns) { - duty = clk_rate / prescalers[prescaler] - / (NSEC_PER_SEC / duty_ns); - if (duty > period) - return -EINVAL; - } else { + 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, prescalers[prescaler], period, duty); + clk_rate, 1 << (2 * prescaler), (u32)period, duty); - if (pwm->prescaler == prescaler && pwm->period == period) + if (tpd->prescaler == prescaler && tpd->period == period) duty_only = true; - pwm->prescaler = prescaler; - pwm->period = period; - pwm->duty = duty; + tpd->prescaler = prescaler; + tpd->period = period; + tpd->duty = duty; /* If the channel is disabled we're done. */ - if (!pwm_is_enabled(_pwm)) + if (!enabled) return 0; - if (duty_only && pwm->timer_on) { + 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(pwm, TPU_TGRAn, pwm->duty); - dev_dbg(&tpu->pdev->dev, "%u: TGRA 0x%04x\n", pwm->channel, - pwm->duty); + 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(pwm); + ret = tpu_pwm_timer_start(tpd); if (ret < 0) return ret; } @@ -317,29 +341,31 @@ static int tpu_pwm_config(struct pwm_chip *chip, struct pwm_device *_pwm, * 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(pwm, duty ? TPU_PIN_ACTIVE : TPU_PIN_INACTIVE); - tpu_pwm_timer_stop(pwm); + 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, +static int tpu_pwm_set_polarity(struct pwm_chip *chip, struct pwm_device *pwm, enum pwm_polarity polarity) { - struct tpu_pwm_device *pwm = pwm_get_chip_data(_pwm); + struct tpu_device *tpu = to_tpu_device(chip); + struct tpu_pwm_device *tpd = &tpu->tpd[pwm->hwpwm]; - pwm->polarity = polarity; + tpd->polarity = polarity; return 0; } -static int tpu_pwm_enable(struct pwm_chip *chip, struct pwm_device *_pwm) +static int tpu_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) { - struct tpu_pwm_device *pwm = pwm_get_chip_data(_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(pwm); + ret = tpu_pwm_timer_start(tpd); if (ret < 0) return ret; @@ -347,33 +373,65 @@ static int tpu_pwm_enable(struct pwm_chip *chip, struct pwm_device *_pwm) * To avoid running the timer when not strictly required, handle 0% and * 100% duty cycles as fixed levels and stop the timer. */ - if (pwm->duty == 0 || pwm->duty == pwm->period) { - tpu_pwm_set_pin(pwm, pwm->duty ? + 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(pwm); + tpu_pwm_timer_stop(tpd); } return 0; } -static void tpu_pwm_disable(struct pwm_chip *chip, struct pwm_device *_pwm) +static void tpu_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) { - struct tpu_pwm_device *pwm = pwm_get_chip_data(_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(pwm); - tpu_pwm_set_pin(pwm, TPU_PIN_INACTIVE); - tpu_pwm_timer_stop(pwm); + 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, - .config = tpu_pwm_config, - .set_polarity = tpu_pwm_set_polarity, - .enable = tpu_pwm_enable, - .disable = tpu_pwm_disable, - .owner = THIS_MODULE, + .apply = tpu_pwm_apply, }; /* ----------------------------------------------------------------------------- @@ -382,63 +440,43 @@ static const struct pwm_ops tpu_pwm_ops = { static int tpu_probe(struct platform_device *pdev) { + struct pwm_chip *chip; struct tpu_device *tpu; - struct resource *res; int ret; - tpu = devm_kzalloc(&pdev->dev, sizeof(*tpu), GFP_KERNEL); - if (tpu == NULL) - return -ENOMEM; + 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. */ - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - tpu->base = devm_ioremap_resource(&pdev->dev, res); + 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)) { - dev_err(&pdev->dev, "cannot get clock\n"); - return PTR_ERR(tpu->clk); - } + 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); - tpu->chip.dev = &pdev->dev; - tpu->chip.ops = &tpu_pwm_ops; - tpu->chip.of_xlate = of_pwm_xlate_with_flags; - tpu->chip.of_pwm_n_cells = 3; - tpu->chip.base = -1; - tpu->chip.npwm = TPU_CHANNEL_MAX; + chip->ops = &tpu_pwm_ops; - pm_runtime_enable(&pdev->dev); + ret = devm_pm_runtime_enable(&pdev->dev); + if (ret < 0) + return dev_err_probe(&pdev->dev, ret, "Failed to enable runtime PM\n"); - ret = pwmchip_add(&tpu->chip); - if (ret < 0) { - dev_err(&pdev->dev, "failed to register PWM chip\n"); - pm_runtime_disable(&pdev->dev); - return ret; - } + 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; } -static int tpu_remove(struct platform_device *pdev) -{ - struct tpu_device *tpu = platform_get_drvdata(pdev); - int ret; - - ret = pwmchip_remove(&tpu->chip); - - pm_runtime_disable(&pdev->dev); - - return ret; -} - #ifdef CONFIG_OF static const struct of_device_id tpu_of_table[] = { { .compatible = "renesas,tpu-r8a73a4", }, @@ -453,7 +491,6 @@ MODULE_DEVICE_TABLE(of, tpu_of_table); static struct platform_driver tpu_driver = { .probe = tpu_probe, - .remove = tpu_remove, .driver = { .name = "renesas-tpu-pwm", .of_match_table = of_match_ptr(tpu_of_table), diff --git a/drivers/pwm/pwm-rockchip.c b/drivers/pwm/pwm-rockchip.c index eb8c9cb645a6..67b85bdb491b 100644 --- a/drivers/pwm/pwm-rockchip.c +++ b/drivers/pwm/pwm-rockchip.c @@ -8,10 +8,12 @@ #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/of_device.h> #include <linux/platform_device.h> +#include <linux/property.h> #include <linux/pwm.h> #include <linux/time.h> @@ -30,7 +32,6 @@ #define PWM_LP_DISABLE (0 << 8) struct rockchip_pwm_chip { - struct pwm_chip chip; struct clk *clk; struct clk *pclk; const struct rockchip_pwm_data *data; @@ -52,16 +53,17 @@ struct rockchip_pwm_data { u32 enable_conf; }; -static inline struct rockchip_pwm_chip *to_rockchip_pwm_chip(struct pwm_chip *c) +static inline struct rockchip_pwm_chip *to_rockchip_pwm_chip(struct pwm_chip *chip) { - return container_of(c, struct rockchip_pwm_chip, chip); + return pwmchip_get_drvdata(chip); } -static void rockchip_pwm_get_state(struct pwm_chip *chip, - struct pwm_device *pwm, - struct pwm_state *state) +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; @@ -70,17 +72,21 @@ static void rockchip_pwm_get_state(struct pwm_chip *chip, ret = clk_enable(pc->pclk); if (ret) - return; + 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 *= pc->data->prescaler * NSEC_PER_SEC; - state->period = DIV_ROUND_CLOSEST_ULL(tmp, clk_rate); + tmp *= prescaled_ns; + state->period = DIV_U64_ROUND_UP(tmp, clk_rate); tmp = readl_relaxed(pc->base + pc->data->regs.duty); - tmp *= pc->data->prescaler * NSEC_PER_SEC; - state->duty_cycle = DIV_ROUND_CLOSEST_ULL(tmp, clk_rate); + 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; @@ -90,15 +96,19 @@ static void rockchip_pwm_get_state(struct pwm_chip *chip, 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); - unsigned long period, duty; - u64 clk_rate, div; + 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); @@ -108,12 +118,15 @@ static void rockchip_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, * bits, every possible input period can be obtained using the * default prescaler value for all practical clock rate values. */ - div = clk_rate * state->period; - period = DIV_ROUND_CLOSEST_ULL(div, - pc->data->prescaler * NSEC_PER_SEC); + tmp = mul_u64_u64_div_u64(clk_rate, state->period, prescaled_ns); + if (tmp > U32_MAX) + tmp = U32_MAX; + period_ticks = tmp; - div = clk_rate * state->duty_cycle; - duty = DIV_ROUND_CLOSEST_ULL(div, pc->data->prescaler * NSEC_PER_SEC); + 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 @@ -125,8 +138,8 @@ static void rockchip_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, writel_relaxed(ctrl, pc->base + pc->data->regs.ctrl); } - writel(period, pc->base + pc->data->regs.period); - writel(duty, pc->base + pc->data->regs.duty); + 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; @@ -189,6 +202,10 @@ static int rockchip_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, if (ret) return ret; + ret = clk_enable(pc->clk); + if (ret) + return ret; + pwm_get_state(pwm, &curstate); enabled = curstate.enabled; @@ -208,6 +225,7 @@ static int rockchip_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, } out: + clk_disable(pc->clk); clk_disable(pc->pclk); return ret; @@ -216,7 +234,6 @@ out: static const struct pwm_ops rockchip_pwm_ops = { .get_state = rockchip_pwm_get_state, .apply = rockchip_pwm_apply, - .owner = THIS_MODULE, }; static const struct rockchip_pwm_data pwm_data_v1 = { @@ -285,34 +302,27 @@ MODULE_DEVICE_TABLE(of, rockchip_pwm_dt_ids); static int rockchip_pwm_probe(struct platform_device *pdev) { - const struct of_device_id *id; + struct pwm_chip *chip; struct rockchip_pwm_chip *pc; - struct resource *r; + u32 enable_conf, ctrl; + bool enabled; int ret, count; - id = of_match_device(rockchip_pwm_dt_ids, &pdev->dev); - if (!id) - return -EINVAL; + chip = devm_pwmchip_alloc(&pdev->dev, 1, sizeof(*pc)); + if (IS_ERR(chip)) + return PTR_ERR(chip); + pc = to_rockchip_pwm_chip(chip); - pc = devm_kzalloc(&pdev->dev, sizeof(*pc), GFP_KERNEL); - if (!pc) - return -ENOMEM; - - r = platform_get_resource(pdev, IORESOURCE_MEM, 0); - pc->base = devm_ioremap_resource(&pdev->dev, r); + 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)) { - ret = PTR_ERR(pc->clk); - if (ret != -EPROBE_DEFER) - dev_err(&pdev->dev, "Can't get bus clk: %d\n", - ret); - return ret; - } + 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, @@ -322,81 +332,59 @@ static int rockchip_pwm_probe(struct platform_device *pdev) else pc->pclk = pc->clk; - if (IS_ERR(pc->pclk)) { - ret = PTR_ERR(pc->pclk); - if (ret != -EPROBE_DEFER) - dev_err(&pdev->dev, "Can't get APB clk: %d\n", ret); - return ret; - } + 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) { - dev_err(&pdev->dev, "Can't prepare enable bus clk: %d\n", ret); - return ret; - } + if (ret) + return dev_err_probe(&pdev->dev, ret, "Can't prepare enable PWM clk\n"); - ret = clk_prepare(pc->pclk); + ret = clk_prepare_enable(pc->pclk); if (ret) { - dev_err(&pdev->dev, "Can't prepare APB clk: %d\n", ret); + dev_err_probe(&pdev->dev, ret, "Can't prepare enable APB clk\n"); goto err_clk; } - platform_set_drvdata(pdev, pc); + platform_set_drvdata(pdev, chip); - pc->data = id->data; - pc->chip.dev = &pdev->dev; - pc->chip.ops = &rockchip_pwm_ops; - pc->chip.base = -1; - pc->chip.npwm = 1; + pc->data = device_get_match_data(&pdev->dev); + chip->ops = &rockchip_pwm_ops; - if (pc->data->supports_polarity) { - pc->chip.of_xlate = of_pwm_xlate_with_flags; - pc->chip.of_pwm_n_cells = 3; - } + enable_conf = pc->data->enable_conf; + ctrl = readl_relaxed(pc->base + pc->data->regs.ctrl); + enabled = (ctrl & enable_conf) == enable_conf; - ret = pwmchip_add(&pc->chip); + ret = pwmchip_add(chip); if (ret < 0) { - clk_unprepare(pc->clk); - dev_err(&pdev->dev, "pwmchip_add() failed: %d\n", ret); + 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 (!pwm_is_enabled(pc->chip.pwms)) + if (!enabled) clk_disable(pc->clk); + clk_disable(pc->pclk); + return 0; err_pclk: - clk_unprepare(pc->pclk); + clk_disable_unprepare(pc->pclk); err_clk: clk_disable_unprepare(pc->clk); return ret; } -static int rockchip_pwm_remove(struct platform_device *pdev) +static void rockchip_pwm_remove(struct platform_device *pdev) { - struct rockchip_pwm_chip *pc = platform_get_drvdata(pdev); + struct pwm_chip *chip = platform_get_drvdata(pdev); + struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip); - /* - * Disable the PWM clk before unpreparing it if the PWM device is still - * running. This should only happen when the last PWM user left it - * enabled, or when nobody requested a PWM that was previously enabled - * by the bootloader. - * - * FIXME: Maybe the core should disable all PWM devices in - * pwmchip_remove(). In this case we'd only have to call - * clk_unprepare() after pwmchip_remove(). - * - */ - if (pwm_is_enabled(pc->chip.pwms)) - clk_disable(pc->clk); + pwmchip_remove(chip); clk_unprepare(pc->pclk); clk_unprepare(pc->clk); - - return pwmchip_remove(&pc->chip); } static struct platform_driver rockchip_pwm_driver = { diff --git a/drivers/pwm/pwm-rz-mtu3.c b/drivers/pwm/pwm-rz-mtu3.c new file mode 100644 index 000000000000..ab39bd37edaf --- /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 000000000000..4856af080e8e --- /dev/null +++ b/drivers/pwm/pwm-rzg2l-gpt.c @@ -0,0 +1,456 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Renesas RZ/G2L General PWM Timer (GPT) driver + * + * Copyright (C) 2025 Renesas Electronics Corporation + * + * Hardware manual for this IP can be found here + * https://www.renesas.com/eu/en/document/mah/rzg2l-group-rzg2lc-group-users-manual-hardware-0?language=en + * + * Limitations: + * - Counter must be stopped before modifying Mode and Prescaler. + * - When PWM is disabled, the output is driven to inactive. + * - While the hardware supports both polarities, the driver (for now) + * only handles normal polarity. + * - General PWM Timer (GPT) has 8 HW channels for PWM operations and + * each HW channel have 2 IOs. + * - Each IO is modelled as an independent PWM channel. + * - When both channels are used, disabling the channel on one stops the + * other. + * - When both channels are used, the period of both IOs in the HW channel + * must be same (for now). + */ + +#include <linux/bitfield.h> +#include <linux/clk.h> +#include <linux/io.h> +#include <linux/limits.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/pwm.h> +#include <linux/reset.h> +#include <linux/time.h> +#include <linux/units.h> + +#define RZG2L_GET_CH(hwpwm) ((hwpwm) / 2) +#define RZG2L_GET_CH_OFFS(ch) (0x100 * (ch)) + +#define RZG2L_GTCR(ch) (0x2c + RZG2L_GET_CH_OFFS(ch)) +#define RZG2L_GTUDDTYC(ch) (0x30 + RZG2L_GET_CH_OFFS(ch)) +#define RZG2L_GTIOR(ch) (0x34 + RZG2L_GET_CH_OFFS(ch)) +#define RZG2L_GTBER(ch) (0x40 + RZG2L_GET_CH_OFFS(ch)) +#define RZG2L_GTCNT(ch) (0x48 + RZG2L_GET_CH_OFFS(ch)) +#define RZG2L_GTCCR(ch, sub_ch) (0x4c + RZG2L_GET_CH_OFFS(ch) + 4 * (sub_ch)) +#define RZG2L_GTPR(ch) (0x64 + RZG2L_GET_CH_OFFS(ch)) + +#define RZG2L_GTCR_CST BIT(0) +#define RZG2L_GTCR_MD GENMASK(18, 16) +#define RZG2L_GTCR_TPCS GENMASK(26, 24) + +#define RZG2L_GTCR_MD_SAW_WAVE_PWM_MODE FIELD_PREP(RZG2L_GTCR_MD, 0) + +#define RZG2L_GTUDDTYC_UP BIT(0) +#define RZG2L_GTUDDTYC_UDF BIT(1) +#define RZG2L_GTUDDTYC_UP_COUNTING (RZG2L_GTUDDTYC_UP | RZG2L_GTUDDTYC_UDF) + +#define RZG2L_GTIOR_GTIOA GENMASK(4, 0) +#define RZG2L_GTIOR_GTIOB GENMASK(20, 16) +#define RZG2L_GTIOR_GTIOx(sub_ch) ((sub_ch) ? RZG2L_GTIOR_GTIOB : RZG2L_GTIOR_GTIOA) +#define RZG2L_GTIOR_OAE BIT(8) +#define RZG2L_GTIOR_OBE BIT(24) +#define RZG2L_GTIOR_OxE(sub_ch) ((sub_ch) ? RZG2L_GTIOR_OBE : RZG2L_GTIOR_OAE) + +#define RZG2L_INIT_OUT_HI_OUT_HI_END_TOGGLE 0x1b +#define RZG2L_GTIOR_GTIOA_OUT_HI_END_TOGGLE_CMP_MATCH \ + (RZG2L_INIT_OUT_HI_OUT_HI_END_TOGGLE | RZG2L_GTIOR_OAE) +#define RZG2L_GTIOR_GTIOB_OUT_HI_END_TOGGLE_CMP_MATCH \ + (FIELD_PREP(RZG2L_GTIOR_GTIOB, RZG2L_INIT_OUT_HI_OUT_HI_END_TOGGLE) | RZG2L_GTIOR_OBE) + +#define RZG2L_GTIOR_GTIOx_OUT_HI_END_TOGGLE_CMP_MATCH(sub_ch) \ + ((sub_ch) ? RZG2L_GTIOR_GTIOB_OUT_HI_END_TOGGLE_CMP_MATCH : \ + RZG2L_GTIOR_GTIOA_OUT_HI_END_TOGGLE_CMP_MATCH) + +#define RZG2L_MAX_HW_CHANNELS 8 +#define RZG2L_CHANNELS_PER_IO 2 +#define RZG2L_MAX_PWM_CHANNELS (RZG2L_MAX_HW_CHANNELS * RZG2L_CHANNELS_PER_IO) +#define RZG2L_MAX_SCALE_FACTOR 1024 +#define RZG2L_MAX_TICKS ((u64)U32_MAX * RZG2L_MAX_SCALE_FACTOR) + +struct rzg2l_gpt_chip { + void __iomem *mmio; + struct mutex lock; /* lock to protect shared channel resources */ + unsigned long rate_khz; + u32 period_ticks[RZG2L_MAX_HW_CHANNELS]; + u32 channel_request_count[RZG2L_MAX_HW_CHANNELS]; + u32 channel_enable_count[RZG2L_MAX_HW_CHANNELS]; +}; + +static inline struct rzg2l_gpt_chip *to_rzg2l_gpt_chip(struct pwm_chip *chip) +{ + return pwmchip_get_drvdata(chip); +} + +static inline unsigned int rzg2l_gpt_subchannel(unsigned int hwpwm) +{ + return hwpwm & 0x1; +} + +static 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(struct rzg2l_gpt_chip *rzg2l_gpt, + u64 period_ticks) +{ + u32 prescaled_period_ticks; + u8 prescale; + + prescaled_period_ticks = period_ticks >> 32; + if (prescaled_period_ticks >= 256) + prescale = 5; + else + prescale = (fls(prescaled_period_ticks) + 1) / 2; + + return prescale; +} + +static int rzg2l_gpt_request(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct rzg2l_gpt_chip *rzg2l_gpt = to_rzg2l_gpt_chip(chip); + u32 ch = RZG2L_GET_CH(pwm->hwpwm); + + guard(mutex)(&rzg2l_gpt->lock); + rzg2l_gpt->channel_request_count[ch]++; + + return 0; +} + +static void rzg2l_gpt_free(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct rzg2l_gpt_chip *rzg2l_gpt = to_rzg2l_gpt_chip(chip); + u32 ch = RZG2L_GET_CH(pwm->hwpwm); + + guard(mutex)(&rzg2l_gpt->lock); + rzg2l_gpt->channel_request_count[ch]--; +} + +static bool rzg2l_gpt_is_ch_enabled(struct rzg2l_gpt_chip *rzg2l_gpt, u8 hwpwm) +{ + u8 ch = RZG2L_GET_CH(hwpwm); + u32 val; + + val = rzg2l_gpt_read(rzg2l_gpt, RZG2L_GTCR(ch)); + if (!(val & RZG2L_GTCR_CST)) + return false; + + val = rzg2l_gpt_read(rzg2l_gpt, RZG2L_GTIOR(ch)); + + return val & RZG2L_GTIOR_OxE(rzg2l_gpt_subchannel(hwpwm)); +} + +/* Caller holds the lock while calling rzg2l_gpt_enable() */ +static void rzg2l_gpt_enable(struct rzg2l_gpt_chip *rzg2l_gpt, + struct pwm_device *pwm) +{ + u8 sub_ch = rzg2l_gpt_subchannel(pwm->hwpwm); + u32 val = RZG2L_GTIOR_GTIOx(sub_ch) | RZG2L_GTIOR_OxE(sub_ch); + u8 ch = RZG2L_GET_CH(pwm->hwpwm); + + /* Enable pin output */ + rzg2l_gpt_modify(rzg2l_gpt, RZG2L_GTIOR(ch), val, + RZG2L_GTIOR_GTIOx_OUT_HI_END_TOGGLE_CMP_MATCH(sub_ch)); + + if (!rzg2l_gpt->channel_enable_count[ch]) + rzg2l_gpt_modify(rzg2l_gpt, RZG2L_GTCR(ch), 0, RZG2L_GTCR_CST); + + rzg2l_gpt->channel_enable_count[ch]++; +} + +/* Caller holds the lock while calling rzg2l_gpt_disable() */ +static void rzg2l_gpt_disable(struct rzg2l_gpt_chip *rzg2l_gpt, + struct pwm_device *pwm) +{ + u8 sub_ch = rzg2l_gpt_subchannel(pwm->hwpwm); + u8 ch = RZG2L_GET_CH(pwm->hwpwm); + + /* Stop count, Output low on GTIOCx pin when counting stops */ + rzg2l_gpt->channel_enable_count[ch]--; + + if (!rzg2l_gpt->channel_enable_count[ch]) + rzg2l_gpt_modify(rzg2l_gpt, RZG2L_GTCR(ch), RZG2L_GTCR_CST, 0); + + /* Disable pin output */ + rzg2l_gpt_modify(rzg2l_gpt, RZG2L_GTIOR(ch), RZG2L_GTIOR_OxE(sub_ch), 0); +} + +static u64 rzg2l_gpt_calculate_period_or_duty(struct rzg2l_gpt_chip *rzg2l_gpt, + u32 val, u8 prescale) +{ + u64 tmp; + + /* + * The calculation doesn't overflow an u64 because prescale ≤ 5 and so + * tmp = val << (2 * prescale) * USEC_PER_SEC + * < 2^32 * 2^10 * 10^6 + * < 2^32 * 2^10 * 2^20 + * = 2^62 + */ + tmp = (u64)val << (2 * prescale); + tmp *= USEC_PER_SEC; + + return DIV64_U64_ROUND_UP(tmp, rzg2l_gpt->rate_khz); +} + +static int rzg2l_gpt_get_state(struct pwm_chip *chip, struct pwm_device *pwm, + struct pwm_state *state) +{ + struct rzg2l_gpt_chip *rzg2l_gpt = to_rzg2l_gpt_chip(chip); + + state->enabled = rzg2l_gpt_is_ch_enabled(rzg2l_gpt, pwm->hwpwm); + if (state->enabled) { + u32 sub_ch = rzg2l_gpt_subchannel(pwm->hwpwm); + u32 ch = RZG2L_GET_CH(pwm->hwpwm); + u8 prescale; + u32 val; + + val = rzg2l_gpt_read(rzg2l_gpt, RZG2L_GTCR(ch)); + prescale = FIELD_GET(RZG2L_GTCR_TPCS, val); + + val = rzg2l_gpt_read(rzg2l_gpt, RZG2L_GTPR(ch)); + state->period = rzg2l_gpt_calculate_period_or_duty(rzg2l_gpt, val, prescale); + + val = rzg2l_gpt_read(rzg2l_gpt, RZG2L_GTCCR(ch, sub_ch)); + state->duty_cycle = rzg2l_gpt_calculate_period_or_duty(rzg2l_gpt, val, prescale); + if (state->duty_cycle > state->period) + state->duty_cycle = state->period; + } + + state->polarity = PWM_POLARITY_NORMAL; + + return 0; +} + +static u32 rzg2l_gpt_calculate_pv_or_dc(u64 period_or_duty_cycle, u8 prescale) +{ + return min_t(u64, DIV_ROUND_DOWN_ULL(period_or_duty_cycle, 1 << (2 * prescale)), + U32_MAX); +} + +/* Caller holds the lock while calling rzg2l_gpt_config() */ +static int rzg2l_gpt_config(struct pwm_chip *chip, struct pwm_device *pwm, + const struct pwm_state *state) +{ + struct rzg2l_gpt_chip *rzg2l_gpt = to_rzg2l_gpt_chip(chip); + u8 sub_ch = rzg2l_gpt_subchannel(pwm->hwpwm); + u8 ch = RZG2L_GET_CH(pwm->hwpwm); + u64 period_ticks, duty_ticks; + unsigned long pv, dc; + u8 prescale; + + /* Limit period/duty cycle to max value supported by the HW */ + period_ticks = mul_u64_u64_div_u64(state->period, rzg2l_gpt->rate_khz, USEC_PER_SEC); + if (period_ticks > RZG2L_MAX_TICKS) + period_ticks = RZG2L_MAX_TICKS; + /* + * GPT counter is shared by the two IOs of a single channel, so + * prescale and period can NOT be modified when there are multiple IOs + * in use with different settings. + */ + if (rzg2l_gpt->channel_request_count[ch] > 1) { + 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(rzg2l_gpt, period_ticks); + pv = rzg2l_gpt_calculate_pv_or_dc(period_ticks, prescale); + + duty_ticks = mul_u64_u64_div_u64(state->duty_cycle, rzg2l_gpt->rate_khz, USEC_PER_SEC); + if (duty_ticks > period_ticks) + duty_ticks = period_ticks; + dc = rzg2l_gpt_calculate_pv_or_dc(duty_ticks, prescale); + + /* + * GPT counter is shared by multiple channels, we cache the period ticks + * from the first enabled channel and use the same value for both + * channels. + */ + rzg2l_gpt->period_ticks[ch] = period_ticks; + + /* + * Counter must be stopped before modifying mode, prescaler, timer + * counter and buffer enable registers. These registers are shared + * between both channels. So allow updating these registers only for the + * first enabled channel. + */ + if (rzg2l_gpt->channel_enable_count[ch] <= 1) { + rzg2l_gpt_modify(rzg2l_gpt, RZG2L_GTCR(ch), RZG2L_GTCR_CST, 0); + + /* GPT set operating mode (saw-wave up-counting) */ + rzg2l_gpt_modify(rzg2l_gpt, RZG2L_GTCR(ch), RZG2L_GTCR_MD, + RZG2L_GTCR_MD_SAW_WAVE_PWM_MODE); + + /* Set count direction */ + rzg2l_gpt_write(rzg2l_gpt, RZG2L_GTUDDTYC(ch), RZG2L_GTUDDTYC_UP_COUNTING); + + /* Select count clock */ + rzg2l_gpt_modify(rzg2l_gpt, RZG2L_GTCR(ch), RZG2L_GTCR_TPCS, + FIELD_PREP(RZG2L_GTCR_TPCS, prescale)); + + /* Set period */ + rzg2l_gpt_write(rzg2l_gpt, RZG2L_GTPR(ch), pv); + } + + /* Set duty cycle */ + rzg2l_gpt_write(rzg2l_gpt, RZG2L_GTCCR(ch, sub_ch), dc); + + if (rzg2l_gpt->channel_enable_count[ch] <= 1) { + /* Set initial value for counter */ + rzg2l_gpt_write(rzg2l_gpt, RZG2L_GTCNT(ch), 0); + + /* Set no buffer operation */ + rzg2l_gpt_write(rzg2l_gpt, RZG2L_GTBER(ch), 0); + + /* Restart the counter after updating the registers */ + rzg2l_gpt_modify(rzg2l_gpt, RZG2L_GTCR(ch), + RZG2L_GTCR_CST, RZG2L_GTCR_CST); + } + + return 0; +} + +static int rzg2l_gpt_apply(struct pwm_chip *chip, struct pwm_device *pwm, + const struct pwm_state *state) +{ + struct rzg2l_gpt_chip *rzg2l_gpt = to_rzg2l_gpt_chip(chip); + bool enabled = pwm->state.enabled; + int ret; + + if (state->polarity != PWM_POLARITY_NORMAL) + return -EINVAL; + + guard(mutex)(&rzg2l_gpt->lock); + if (!state->enabled) { + if (enabled) + rzg2l_gpt_disable(rzg2l_gpt, pwm); + + return 0; + } + + ret = rzg2l_gpt_config(chip, pwm, state); + if (!ret && !enabled) + rzg2l_gpt_enable(rzg2l_gpt, pwm); + + return ret; +} + +static const struct pwm_ops rzg2l_gpt_ops = { + .request = rzg2l_gpt_request, + .free = rzg2l_gpt_free, + .get_state = rzg2l_gpt_get_state, + .apply = rzg2l_gpt_apply, +}; + +static int rzg2l_gpt_probe(struct platform_device *pdev) +{ + struct rzg2l_gpt_chip *rzg2l_gpt; + struct device *dev = &pdev->dev; + struct reset_control *rstc; + struct pwm_chip *chip; + unsigned long rate; + struct clk *clk; + int ret; + + chip = devm_pwmchip_alloc(dev, RZG2L_MAX_PWM_CHANNELS, sizeof(*rzg2l_gpt)); + if (IS_ERR(chip)) + return PTR_ERR(chip); + rzg2l_gpt = to_rzg2l_gpt_chip(chip); + + rzg2l_gpt->mmio = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(rzg2l_gpt->mmio)) + return PTR_ERR(rzg2l_gpt->mmio); + + rstc = devm_reset_control_get_exclusive_deasserted(dev, NULL); + if (IS_ERR(rstc)) + return dev_err_probe(dev, PTR_ERR(rstc), "Cannot deassert reset control\n"); + + clk = devm_clk_get_enabled(dev, NULL); + if (IS_ERR(clk)) + return dev_err_probe(dev, PTR_ERR(clk), "Cannot get clock\n"); + + ret = devm_clk_rate_exclusive_get(dev, clk); + if (ret) + return ret; + + rate = clk_get_rate(clk); + if (!rate) + return dev_err_probe(dev, -EINVAL, "The gpt clk rate is 0"); + + /* + * Refuse clk rates > 1 GHz to prevent overflow later for computing + * period and duty cycle. + */ + if (rate > NSEC_PER_SEC) + return dev_err_probe(dev, -EINVAL, "The gpt clk rate is > 1GHz"); + + /* + * Rate is in MHz and is always integer for peripheral clk + * 2^32 * 2^10 (prescalar) * 10^6 (rate_khz) < 2^64 + * So make sure rate is multiple of 1000. + */ + rzg2l_gpt->rate_khz = rate / KILO; + if (rzg2l_gpt->rate_khz * KILO != rate) + return dev_err_probe(dev, -EINVAL, "Rate is not multiple of 1000"); + + mutex_init(&rzg2l_gpt->lock); + + chip->ops = &rzg2l_gpt_ops; + ret = devm_pwmchip_add(dev, chip); + if (ret) + return dev_err_probe(dev, ret, "Failed to add PWM chip\n"); + + return 0; +} + +static const struct of_device_id rzg2l_gpt_of_table[] = { + { .compatible = "renesas,rzg2l-gpt", }, + { /* Sentinel */ } +}; +MODULE_DEVICE_TABLE(of, rzg2l_gpt_of_table); + +static struct platform_driver rzg2l_gpt_driver = { + .driver = { + .name = "pwm-rzg2l-gpt", + .of_match_table = rzg2l_gpt_of_table, + }, + .probe = rzg2l_gpt_probe, +}; +module_platform_driver(rzg2l_gpt_driver); + +MODULE_AUTHOR("Biju Das <biju.das.jz@bp.renesas.com>"); +MODULE_DESCRIPTION("Renesas RZ/G2L General PWM Timer (GPT) Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/pwm/pwm-samsung.c b/drivers/pwm/pwm-samsung.c index 87a886f7dc2f..951b38ff5f8e 100644 --- a/drivers/pwm/pwm-samsung.c +++ b/drivers/pwm/pwm-samsung.c @@ -69,7 +69,6 @@ struct samsung_pwm_channel { /** * struct samsung_pwm_chip - private data of PWM chip - * @chip: generic 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 @@ -77,9 +76,9 @@ struct samsung_pwm_channel { * @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 pwm_chip chip; struct samsung_pwm_variant variant; u8 inverter_mask; u8 disabled_mask; @@ -88,6 +87,7 @@ struct samsung_pwm_chip { struct clk *base_clk; struct clk *tclk0; struct clk *tclk1; + struct samsung_pwm_channel channel[SAMSUNG_PWM_NUM]; }; #ifndef CONFIG_CLKSRC_SAMSUNG_PWM @@ -108,7 +108,7 @@ static DEFINE_SPINLOCK(samsung_pwm_lock); static inline struct samsung_pwm_chip *to_samsung_pwm_chip(struct pwm_chip *chip) { - return container_of(chip, struct samsung_pwm_chip, chip); + return pwmchip_get_drvdata(chip); } static inline unsigned int to_tcon_channel(unsigned int channel) @@ -117,7 +117,21 @@ static inline unsigned int to_tcon_channel(unsigned int channel) return (channel == 0) ? 0 : (channel + 1); } -static void pwm_samsung_set_divisor(struct samsung_pwm_chip *pwm, +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); @@ -125,39 +139,39 @@ static void pwm_samsung_set_divisor(struct samsung_pwm_chip *pwm, u32 reg; u8 bits; - bits = (fls(divisor) - 1) - pwm->variant.div_base; + bits = (fls(divisor) - 1) - our_chip->variant.div_base; spin_lock_irqsave(&samsung_pwm_lock, flags); - reg = readl(pwm->base + REG_TCFG1); + reg = readl(our_chip->base + REG_TCFG1); reg &= ~(TCFG1_MUX_MASK << shift); reg |= bits << shift; - writel(reg, pwm->base + REG_TCFG1); + writel(reg, our_chip->base + REG_TCFG1); spin_unlock_irqrestore(&samsung_pwm_lock, flags); } -static int pwm_samsung_is_tdiv(struct samsung_pwm_chip *chip, unsigned int chan) +static int pwm_samsung_is_tdiv(struct samsung_pwm_chip *our_chip, unsigned int chan) { - struct samsung_pwm_variant *variant = &chip->variant; + struct samsung_pwm_variant *variant = &our_chip->variant; u32 reg; - reg = readl(chip->base + REG_TCFG1); + 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 *chip, +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(chip->base_clk); + rate = clk_get_rate(our_chip->base_clk); - reg = readl(chip->base + REG_TCFG0); + reg = readl(our_chip->base + REG_TCFG0); if (chan >= 2) reg >>= TCFG0_PRESCALER1_SHIFT; reg &= TCFG0_PRESCALER_MASK; @@ -165,28 +179,29 @@ static unsigned long pwm_samsung_get_tin_rate(struct samsung_pwm_chip *chip, return rate / (reg + 1); } -static unsigned long pwm_samsung_calc_tin(struct samsung_pwm_chip *chip, +static unsigned long pwm_samsung_calc_tin(struct pwm_chip *chip, unsigned int chan, unsigned long freq) { - struct samsung_pwm_variant *variant = &chip->variant; + 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(chip, chan)) { - clk = (chan < 2) ? chip->tclk0 : chip->tclk1; + 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(chip->chip.dev, + dev_warn(pwmchip_parent(chip), "tclk of PWM %d is inoperational, using tdiv\n", chan); } - rate = pwm_samsung_get_tin_rate(chip, chan); - dev_dbg(chip->chip.dev, "tin parent at %lu\n", rate); + 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 @@ -206,7 +221,7 @@ static unsigned long pwm_samsung_calc_tin(struct samsung_pwm_chip *chip, div = variant->div_base; } - pwm_samsung_set_divisor(chip, chan, BIT(div)); + pwm_samsung_set_divisor(our_chip, chan, BIT(div)); return rate >> div; } @@ -214,29 +229,19 @@ static unsigned long pwm_samsung_calc_tin(struct samsung_pwm_chip *chip, static int pwm_samsung_request(struct pwm_chip *chip, struct pwm_device *pwm) { struct samsung_pwm_chip *our_chip = to_samsung_pwm_chip(chip); - struct samsung_pwm_channel *our_chan; if (!(our_chip->variant.output_mask & BIT(pwm->hwpwm))) { - dev_warn(chip->dev, + dev_warn(pwmchip_parent(chip), "tried to request PWM channel %d without output\n", pwm->hwpwm); return -EINVAL; } - our_chan = kzalloc(sizeof(*our_chan), GFP_KERNEL); - if (!our_chan) - return -ENOMEM; - - pwm_set_chip_data(pwm, our_chan); + memset(&our_chip->channel[pwm->hwpwm], 0, sizeof(our_chip->channel[pwm->hwpwm])); return 0; } -static void pwm_samsung_free(struct pwm_chip *chip, struct pwm_device *pwm) -{ - kfree(pwm_get_chip_data(pwm)); -} - static int pwm_samsung_enable(struct pwm_chip *chip, struct pwm_device *pwm) { struct samsung_pwm_chip *our_chip = to_samsung_pwm_chip(chip); @@ -276,26 +281,26 @@ static void pwm_samsung_disable(struct pwm_chip *chip, struct pwm_device *pwm) 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); spin_unlock_irqrestore(&samsung_pwm_lock, flags); } -static void pwm_samsung_manual_update(struct samsung_pwm_chip *chip, +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; unsigned long flags; spin_lock_irqsave(&samsung_pwm_lock, flags); - tcon = readl(chip->base + REG_TCON); - tcon |= TCON_MANUALUPDATE(tcon_chan); - writel(tcon, chip->base + REG_TCON); - - tcon &= ~TCON_MANUALUPDATE(tcon_chan); - writel(tcon, chip->base + REG_TCON); + __pwm_samsung_manual_update(our_chip, pwm); spin_unlock_irqrestore(&samsung_pwm_lock, flags); } @@ -304,17 +309,9 @@ 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 = pwm_get_chip_data(pwm); + struct samsung_pwm_channel *chan = &our_chip->channel[pwm->hwpwm]; u32 tin_ns = chan->tin_ns, tcnt, tcmp, oldtcmp; - /* - * We currently avoid using 64bit arithmetic by using the - * fact that anything faster than 1Hz is easily representable - * by 32bits. - */ - if (period_ns > NSEC_PER_SEC) - return -ERANGE; - tcnt = readl(our_chip->base + REG_TCNTB(pwm->hwpwm)); oldtcmp = readl(our_chip->base + REG_TCMPB(pwm->hwpwm)); @@ -328,12 +325,12 @@ static int __pwm_samsung_config(struct pwm_chip *chip, struct pwm_device *pwm, period = NSEC_PER_SEC / period_ns; - dev_dbg(our_chip->chip.dev, "duty_ns=%d, period_ns=%d (%u)\n", + dev_dbg(pwmchip_parent(chip), "duty_ns=%d, period_ns=%d (%u)\n", duty_ns, period_ns, period); - tin_rate = pwm_samsung_calc_tin(our_chip, pwm->hwpwm, period); + tin_rate = pwm_samsung_calc_tin(chip, pwm->hwpwm, period); - dev_dbg(our_chip->chip.dev, "tin_rate=%lu\n", tin_rate); + dev_dbg(pwmchip_parent(chip), "tin_rate=%lu\n", tin_rate); tin_ns = NSEC_PER_SEC / tin_rate; tcnt = period_ns / tin_ns; @@ -357,8 +354,7 @@ static int __pwm_samsung_config(struct pwm_chip *chip, struct pwm_device *pwm, /* -1UL will give 100% duty. */ --tcmp; - dev_dbg(our_chip->chip.dev, - "tin_ns=%u, tcmp=%u/%u\n", tin_ns, tcmp, tcnt); + 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)); @@ -370,7 +366,7 @@ static int __pwm_samsung_config(struct pwm_chip *chip, struct pwm_device *pwm, * shortly afer this update (before it autoreloaded the new values). */ if (oldtcmp == (u32) -1) { - dev_dbg(our_chip->chip.dev, "Forcing manual update"); + dev_dbg(pwmchip_parent(chip), "Forcing manual update"); pwm_samsung_manual_update(our_chip, pwm); } @@ -387,7 +383,7 @@ static int pwm_samsung_config(struct pwm_chip *chip, struct pwm_device *pwm, return __pwm_samsung_config(chip, pwm, duty_ns, period_ns, false); } -static void pwm_samsung_set_invert(struct samsung_pwm_chip *chip, +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); @@ -396,17 +392,17 @@ static void pwm_samsung_set_invert(struct samsung_pwm_chip *chip, spin_lock_irqsave(&samsung_pwm_lock, flags); - tcon = readl(chip->base + REG_TCON); + tcon = readl(our_chip->base + REG_TCON); if (invert) { - chip->inverter_mask |= BIT(channel); + our_chip->inverter_mask |= BIT(channel); tcon |= TCON_INVERT(tcon_chan); } else { - chip->inverter_mask &= ~BIT(channel); + our_chip->inverter_mask &= ~BIT(channel); tcon &= ~TCON_INVERT(tcon_chan); } - writel(tcon, chip->base + REG_TCON); + writel(tcon, our_chip->base + REG_TCON); spin_unlock_irqrestore(&samsung_pwm_lock, flags); } @@ -424,14 +420,50 @@ static int pwm_samsung_set_polarity(struct pwm_chip *chip, 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, - .free = pwm_samsung_free, - .enable = pwm_samsung_enable, - .disable = pwm_samsung_disable, - .config = pwm_samsung_config, - .set_polarity = pwm_samsung_set_polarity, - .owner = THIS_MODULE, + .apply = pwm_samsung_apply, }; #ifdef CONFIG_OF @@ -473,34 +505,33 @@ static const struct of_device_id samsung_pwm_matches[] = { }; MODULE_DEVICE_TABLE(of, samsung_pwm_matches); -static int pwm_samsung_parse_dt(struct samsung_pwm_chip *chip) +static int pwm_samsung_parse_dt(struct pwm_chip *chip) { - struct device_node *np = chip->chip.dev->of_node; + 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; - struct property *prop; - const __be32 *cur; u32 val; match = of_match_node(samsung_pwm_matches, np); if (!match) return -ENODEV; - memcpy(&chip->variant, match->data, sizeof(chip->variant)); + memcpy(&our_chip->variant, match->data, sizeof(our_chip->variant)); - of_property_for_each_u32(np, "samsung,pwm-outputs", prop, cur, val) { + of_property_for_each_u32(np, "samsung,pwm-outputs", val) { if (val >= SAMSUNG_PWM_NUM) { - dev_err(chip->chip.dev, + dev_err(pwmchip_parent(chip), "%s: invalid channel index in samsung,pwm-outputs property\n", __func__); continue; } - chip->variant.output_mask |= BIT(val); + our_chip->variant.output_mask |= BIT(val); } return 0; } #else -static int pwm_samsung_parse_dt(struct samsung_pwm_chip *chip) +static int pwm_samsung_parse_dt(struct pwm_chip *chip) { return -ENODEV; } @@ -509,106 +540,74 @@ static int pwm_samsung_parse_dt(struct samsung_pwm_chip *chip) static int pwm_samsung_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - struct samsung_pwm_chip *chip; - struct resource *res; + struct samsung_pwm_chip *our_chip; + struct pwm_chip *chip; unsigned int chan; int ret; - chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL); - if (chip == NULL) - return -ENOMEM; + 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->chip.dev = &pdev->dev; - chip->chip.ops = &pwm_samsung_ops; - chip->chip.base = -1; - chip->chip.npwm = SAMSUNG_PWM_NUM; - chip->inverter_mask = BIT(SAMSUNG_PWM_NUM) - 1; + 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; - - chip->chip.of_xlate = of_pwm_xlate_with_flags; - chip->chip.of_pwm_n_cells = 3; } else { - if (!pdev->dev.platform_data) { - dev_err(&pdev->dev, "no platform data specified\n"); - return -EINVAL; - } + if (!pdev->dev.platform_data) + return dev_err_probe(&pdev->dev, -EINVAL, + "no platform data specified\n"); - memcpy(&chip->variant, pdev->dev.platform_data, - sizeof(chip->variant)); + memcpy(&our_chip->variant, pdev->dev.platform_data, + sizeof(our_chip->variant)); } - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - chip->base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(chip->base)) - return PTR_ERR(chip->base); - - chip->base_clk = devm_clk_get(&pdev->dev, "timers"); - if (IS_ERR(chip->base_clk)) { - dev_err(dev, "failed to get timer base clk\n"); - return PTR_ERR(chip->base_clk); - } + our_chip->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(our_chip->base)) + return PTR_ERR(our_chip->base); - ret = clk_prepare_enable(chip->base_clk); - if (ret < 0) { - dev_err(dev, "failed to enable base clock\n"); - return ret; - } + 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 (chip->variant.output_mask & BIT(chan)) - pwm_samsung_set_invert(chip, chan, true); + if (our_chip->variant.output_mask & BIT(chan)) + pwm_samsung_set_invert(our_chip, chan, true); /* Following clocks are optional. */ - chip->tclk0 = devm_clk_get(&pdev->dev, "pwm-tclk0"); - chip->tclk1 = devm_clk_get(&pdev->dev, "pwm-tclk1"); + 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 = pwmchip_add(&chip->chip); - if (ret < 0) { - dev_err(dev, "failed to register PWM chip\n"); - clk_disable_unprepare(chip->base_clk); - return ret; - } - - dev_dbg(dev, "base_clk at %lu, tclk0 at %lu, tclk1 at %lu\n", - clk_get_rate(chip->base_clk), - !IS_ERR(chip->tclk0) ? clk_get_rate(chip->tclk0) : 0, - !IS_ERR(chip->tclk1) ? clk_get_rate(chip->tclk1) : 0); - - return 0; -} - -static int pwm_samsung_remove(struct platform_device *pdev) -{ - struct samsung_pwm_chip *chip = platform_get_drvdata(pdev); - int ret; - - ret = pwmchip_remove(&chip->chip); + ret = devm_pwmchip_add(&pdev->dev, chip); if (ret < 0) - return ret; + return dev_err_probe(dev, ret, "failed to register PWM chip\n"); - clk_disable_unprepare(chip->base_clk); + 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; } -#ifdef CONFIG_PM_SLEEP static int pwm_samsung_resume(struct device *dev) { - struct samsung_pwm_chip *our_chip = dev_get_drvdata(dev); - struct pwm_chip *chip = &our_chip->chip; + 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 = pwm_get_chip_data(pwm); + struct samsung_pwm_channel *chan = &our_chip->channel[i]; - if (!chan) + if (!test_bit(PWMF_REQUESTED, &pwm->flags)) continue; if (our_chip->variant.output_mask & BIT(i)) @@ -630,21 +629,20 @@ static int pwm_samsung_resume(struct device *dev) return 0; } -#endif -static SIMPLE_DEV_PM_OPS(pwm_samsung_pm_ops, NULL, pwm_samsung_resume); +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 = &pwm_samsung_pm_ops, + .pm = pm_ptr(&pwm_samsung_pm_ops), .of_match_table = of_match_ptr(samsung_pwm_matches), }, .probe = pwm_samsung_probe, - .remove = pwm_samsung_remove, }; 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 index cc63f9baa481..4a07315b0744 100644 --- a/drivers/pwm/pwm-sifive.c +++ b/drivers/pwm/pwm-sifive.c @@ -4,15 +4,33 @@ * 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 100% 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/mod_devicetable.h> #include <linux/module.h> #include <linux/platform_device.h> #include <linux/pwm.h> @@ -23,7 +41,7 @@ #define PWM_SIFIVE_PWMCFG 0x0 #define PWM_SIFIVE_PWMCOUNT 0x8 #define PWM_SIFIVE_PWMS 0x10 -#define PWM_SIFIVE_PWMCMP0 0x20 +#define PWM_SIFIVE_PWMCMP(i) (0x20 + 4 * (i)) /* PWMCFG fields */ #define PWM_SIFIVE_PWMCFG_SCALE GENMASK(3, 0) @@ -36,14 +54,12 @@ #define PWM_SIFIVE_PWMCFG_GANG BIT(24) #define PWM_SIFIVE_PWMCFG_IP BIT(28) -/* PWM_SIFIVE_SIZE_PWMCMP is used to calculate offset for pwmcmpX registers */ -#define PWM_SIFIVE_SIZE_PWMCMP 4 #define PWM_SIFIVE_CMPWIDTH 16 #define PWM_SIFIVE_DEFAULT_PERIOD 10000000 struct pwm_sifive_ddata { - struct pwm_chip chip; - struct mutex lock; /* lock to protect user_count */ + struct device *parent; + struct mutex lock; /* lock to protect user_count and approx_period */ struct notifier_block notifier; struct clk *clk; void __iomem *regs; @@ -53,9 +69,9 @@ struct pwm_sifive_ddata { }; static inline -struct pwm_sifive_ddata *pwm_sifive_chip_to_ddata(struct pwm_chip *c) +struct pwm_sifive_ddata *pwm_sifive_chip_to_ddata(struct pwm_chip *chip) { - return container_of(c, struct pwm_sifive_ddata, chip); + return pwmchip_get_drvdata(chip); } static int pwm_sifive_request(struct pwm_chip *chip, struct pwm_device *pwm) @@ -78,6 +94,7 @@ static void pwm_sifive_free(struct pwm_chip *chip, struct pwm_device *pwm) mutex_unlock(&ddata->lock); } +/* Called holding ddata->lock */ static void pwm_sifive_update_clock(struct pwm_sifive_ddata *ddata, unsigned long rate) { @@ -101,19 +118,23 @@ static void pwm_sifive_update_clock(struct pwm_sifive_ddata *ddata, /* As scale <= 15 the shift operation cannot overflow. */ num = (unsigned long long)NSEC_PER_SEC << (PWM_SIFIVE_CMPWIDTH + scale); - ddata->real_period = div64_ul(num, rate); - dev_dbg(ddata->chip.dev, + ddata->real_period = DIV_ROUND_UP_ULL(num, rate); + dev_dbg(ddata->parent, "New real_period = %u ns\n", ddata->real_period); } -static void pwm_sifive_get_state(struct pwm_chip *chip, struct pwm_device *pwm, - struct pwm_state *state) +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; + u32 duty, val, inactive; - duty = readl(ddata->regs + PWM_SIFIVE_PWMCMP0 + - pwm->hwpwm * PWM_SIFIVE_SIZE_PWMCMP); + 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; @@ -122,26 +143,9 @@ static void pwm_sifive_get_state(struct pwm_chip *chip, struct pwm_device *pwm, state->enabled = false; state->period = ddata->real_period; - state->duty_cycle = - (u64)duty * ddata->real_period >> PWM_SIFIVE_CMPWIDTH; - state->polarity = PWM_POLARITY_INVERSED; -} - -static int pwm_sifive_enable(struct pwm_chip *chip, bool enable) -{ - struct pwm_sifive_ddata *ddata = pwm_sifive_chip_to_ddata(chip); - int ret; - - if (enable) { - ret = clk_enable(ddata->clk); - if (ret) { - dev_err(ddata->chip.dev, "Enable clk failed\n"); - return ret; - } - } - - if (!enable) - clk_disable(ddata->clk); + state->duty_cycle = DIV_ROUND_UP_ULL((u64)duty * ddata->real_period, + (1U << PWM_SIFIVE_CMPWIDTH)); + state->polarity = PWM_POLARITY_NORMAL; return 0; } @@ -155,18 +159,12 @@ static int pwm_sifive_apply(struct pwm_chip *chip, struct pwm_device *pwm, unsigned long long num; bool enabled; int ret = 0; - u32 frac; + u64 frac; + u32 inactive; - if (state->polarity != PWM_POLARITY_INVERSED) + if (state->polarity != PWM_POLARITY_NORMAL) return -EINVAL; - ret = clk_enable(ddata->clk); - if (ret) { - dev_err(ddata->chip.dev, "Enable clk failed\n"); - return ret; - } - - mutex_lock(&ddata->lock); cur_state = pwm->state; enabled = cur_state.enabled; @@ -181,29 +179,49 @@ static int pwm_sifive_apply(struct pwm_chip *chip, struct pwm_device *pwm, * consecutively */ num = (u64)duty_cycle * (1U << PWM_SIFIVE_CMPWIDTH); - frac = DIV_ROUND_CLOSEST_ULL(num, state->period); - /* The hardware cannot generate a 100% duty cycle */ - frac = min(frac, (1U << PWM_SIFIVE_CMPWIDTH) - 1); + 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) { - if (ddata->user_count != 1) { - ret = -EBUSY; - goto exit; + /* + * 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); - writel(frac, ddata->regs + PWM_SIFIVE_PWMCMP0 + - pwm->hwpwm * PWM_SIFIVE_SIZE_PWMCMP); + /* + * 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; + } + } - if (state->enabled != enabled) - pwm_sifive_enable(chip, state->enabled); + writel(inactive, ddata->regs + PWM_SIFIVE_PWMCMP(pwm->hwpwm)); -exit: - clk_disable(ddata->clk); - mutex_unlock(&ddata->lock); - return ret; + if (!state->enabled) + clk_disable(ddata->clk); + + return 0; } static const struct pwm_ops pwm_sifive_ops = { @@ -211,7 +229,6 @@ static const struct pwm_ops pwm_sifive_ops = { .free = pwm_sifive_free, .get_state = pwm_sifive_get_state, .apply = pwm_sifive_apply, - .owner = THIS_MODULE, }; static int pwm_sifive_clock_notifier(struct notifier_block *nb, @@ -221,8 +238,11 @@ static int pwm_sifive_clock_notifier(struct notifier_block *nb, struct pwm_sifive_ddata *ddata = container_of(nb, struct pwm_sifive_ddata, notifier); - if (event == POST_RATE_CHANGE) + if (event == POST_RATE_CHANGE) { + mutex_lock(&ddata->lock); pwm_sifive_update_clock(ddata, ndata->new_rate); + mutex_unlock(&ddata->lock); + } return NOTIFY_OK; } @@ -232,40 +252,61 @@ static int pwm_sifive_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct pwm_sifive_ddata *ddata; struct pwm_chip *chip; - struct resource *res; int ret; + u32 val; + unsigned int enabled_pwms = 0, enabled_clks = 1; - ddata = devm_kzalloc(dev, sizeof(*ddata), GFP_KERNEL); - if (!ddata) - return -ENOMEM; + 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 = &ddata->chip; - chip->dev = dev; chip->ops = &pwm_sifive_ops; - chip->of_xlate = of_pwm_xlate_with_flags; - chip->of_pwm_n_cells = 3; - chip->base = -1; - chip->npwm = 4; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - ddata->regs = devm_ioremap_resource(dev, res); + ddata->regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(ddata->regs)) return PTR_ERR(ddata->regs); - ddata->clk = devm_clk_get(dev, NULL); - if (IS_ERR(ddata->clk)) { - if (PTR_ERR(ddata->clk) != -EPROBE_DEFER) - dev_err(dev, "Unable to find controller clock\n"); - return PTR_ERR(ddata->clk); - } + 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_prepare_enable(ddata->clk); + 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); @@ -280,7 +321,7 @@ static int pwm_sifive_probe(struct platform_device *pdev) goto unregister_clk; } - platform_set_drvdata(pdev, ddata); + platform_set_drvdata(pdev, chip); dev_dbg(dev, "SiFive PWM chip registered %d PWMs\n", chip->npwm); return 0; @@ -288,33 +329,29 @@ static int pwm_sifive_probe(struct platform_device *pdev) unregister_clk: clk_notifier_unregister(ddata->clk, &ddata->notifier); disable_clk: - clk_disable_unprepare(ddata->clk); + while (enabled_clks) { + clk_disable(ddata->clk); + --enabled_clks; + } return ret; } -static int pwm_sifive_remove(struct platform_device *dev) +static void pwm_sifive_remove(struct platform_device *dev) { - struct pwm_sifive_ddata *ddata = platform_get_drvdata(dev); - bool is_enabled = false; + struct pwm_chip *chip = platform_get_drvdata(dev); + struct pwm_sifive_ddata *ddata = pwm_sifive_chip_to_ddata(chip); struct pwm_device *pwm; - int ret, ch; + int ch; - for (ch = 0; ch < ddata->chip.npwm; ch++) { - pwm = &ddata->chip.pwms[ch]; - if (pwm->state.enabled) { - is_enabled = true; - break; - } - } - if (is_enabled) - clk_disable(ddata->clk); - - clk_disable_unprepare(ddata->clk); - ret = pwmchip_remove(&ddata->chip); + pwmchip_remove(chip); clk_notifier_unregister(ddata->clk, &ddata->notifier); - return ret; + 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[] = { diff --git a/drivers/pwm/pwm-sl28cpld.c b/drivers/pwm/pwm-sl28cpld.c new file mode 100644 index 000000000000..934378d6a002 --- /dev/null +++ b/drivers/pwm/pwm-sl28cpld.c @@ -0,0 +1,263 @@ +// 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/mod_devicetable.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, ®); + + 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, ®); + 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 000000000000..7d07b0ca7d29 --- /dev/null +++ b/drivers/pwm/pwm-sophgo-sg2042.c @@ -0,0 +1,301 @@ +// 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 index 6c6b44fd3f43..4f372279f313 100644 --- a/drivers/pwm/pwm-spear.c +++ b/drivers/pwm/pwm-spear.c @@ -48,17 +48,15 @@ * * @mmio_base: base address of pwm chip * @clk: pointer to clk structure of pwm chip - * @chip: linux pwm chip representation */ struct spear_pwm_chip { void __iomem *mmio_base; struct clk *clk; - struct pwm_chip chip; }; static inline struct spear_pwm_chip *to_spear_pwm_chip(struct pwm_chip *chip) { - return container_of(chip, struct spear_pwm_chip, chip); + return pwmchip_get_drvdata(chip); } static inline u32 spear_pwm_readl(struct spear_pwm_chip *chip, unsigned int num, @@ -75,7 +73,7 @@ static inline void spear_pwm_writel(struct spear_pwm_chip *chip, } static int spear_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, - int duty_ns, int period_ns) + u64 duty_ns, u64 period_ns) { struct spear_pwm_chip *pc = to_spear_pwm_chip(chip); u64 val, div, clk_rate; @@ -163,51 +161,64 @@ static void spear_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) 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 = { - .config = spear_pwm_config, - .enable = spear_pwm_enable, - .disable = spear_pwm_disable, - .owner = THIS_MODULE, + .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; - struct resource *r; int ret; u32 val; - pc = devm_kzalloc(&pdev->dev, sizeof(*pc), GFP_KERNEL); - if (!pc) - return -ENOMEM; + chip = devm_pwmchip_alloc(&pdev->dev, NUM_PWM, sizeof(*pc)); + if (IS_ERR(chip)) + return PTR_ERR(chip); + pc = to_spear_pwm_chip(chip); - r = platform_get_resource(pdev, IORESOURCE_MEM, 0); - pc->mmio_base = devm_ioremap_resource(&pdev->dev, r); + 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(&pdev->dev, NULL); + pc->clk = devm_clk_get_prepared(&pdev->dev, NULL); if (IS_ERR(pc->clk)) - return PTR_ERR(pc->clk); - - platform_set_drvdata(pdev, pc); + return dev_err_probe(&pdev->dev, PTR_ERR(pc->clk), + "Failed to get clock\n"); - pc->chip.dev = &pdev->dev; - pc->chip.ops = &spear_pwm_ops; - pc->chip.base = -1; - pc->chip.npwm = NUM_PWM; - - ret = clk_prepare(pc->clk); - if (ret) - return ret; + chip->ops = &spear_pwm_ops; if (of_device_is_compatible(np, "st,spear1340-pwm")) { ret = clk_enable(pc->clk); - if (ret) { - clk_unprepare(pc->clk); - return ret; - } + 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 @@ -219,26 +230,11 @@ static int spear_pwm_probe(struct platform_device *pdev) clk_disable(pc->clk); } - ret = pwmchip_add(&pc->chip); - if (ret < 0) { - clk_unprepare(pc->clk); - dev_err(&pdev->dev, "pwmchip_add() failed: %d\n", ret); - } + ret = devm_pwmchip_add(&pdev->dev, chip); + if (ret < 0) + return dev_err_probe(&pdev->dev, ret, "pwmchip_add() failed\n"); - return ret; -} - -static int spear_pwm_remove(struct platform_device *pdev) -{ - struct spear_pwm_chip *pc = platform_get_drvdata(pdev); - int i; - - for (i = 0; i < NUM_PWM; i++) - pwm_disable(&pc->chip.pwms[i]); - - /* clk was prepared in probe, hence unprepare it here */ - clk_unprepare(pc->clk); - return pwmchip_remove(&pc->chip); + return 0; } static const struct of_device_id spear_pwm_of_match[] = { @@ -255,11 +251,11 @@ static struct platform_driver spear_pwm_driver = { .of_match_table = spear_pwm_of_match, }, .probe = spear_pwm_probe, - .remove = spear_pwm_remove, }; 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>"); diff --git a/drivers/pwm/pwm-sprd.c b/drivers/pwm/pwm-sprd.c index be2394227423..4c76ca5e4cdd 100644 --- a/drivers/pwm/pwm-sprd.c +++ b/drivers/pwm/pwm-sprd.c @@ -7,6 +7,7 @@ #include <linux/err.h> #include <linux/io.h> #include <linux/math64.h> +#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/platform_device.h> #include <linux/pwm.h> @@ -33,12 +34,14 @@ struct sprd_pwm_chn { struct sprd_pwm_chip { void __iomem *base; - struct device *dev; - struct pwm_chip chip; - int num_pwms; 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. @@ -65,11 +68,10 @@ static void sprd_pwm_write(struct sprd_pwm_chip *spc, u32 hwid, writel_relaxed(val, spc->base + offset); } -static void sprd_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm, - struct pwm_state *state) +static int sprd_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm, + struct pwm_state *state) { - struct sprd_pwm_chip *spc = - container_of(chip, struct sprd_pwm_chip, chip); + 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; @@ -81,9 +83,9 @@ static void sprd_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm, */ ret = clk_bulk_prepare_enable(SPRD_PWM_CHN_CLKS_NUM, chn->clks); if (ret) { - dev_err(spc->dev, "failed to enable pwm%u clocks\n", + dev_err(pwmchip_parent(chip), "failed to enable pwm%u clocks\n", pwm->hwpwm); - return; + return ret; } val = sprd_pwm_read(spc, pwm->hwpwm, SPRD_PWM_ENABLE); @@ -109,10 +111,13 @@ static void sprd_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm, 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, @@ -158,12 +163,14 @@ static int sprd_pwm_config(struct sprd_pwm_chip *spc, struct pwm_device *pwm, static int sprd_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, const struct pwm_state *state) { - struct sprd_pwm_chip *spc = - container_of(chip, struct sprd_pwm_chip, chip); + 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) { /* @@ -173,20 +180,17 @@ static int sprd_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, ret = clk_bulk_prepare_enable(SPRD_PWM_CHN_CLKS_NUM, chn->clks); if (ret) { - dev_err(spc->dev, + dev_err(pwmchip_parent(chip), "failed to enable pwm%u clocks\n", pwm->hwpwm); return ret; } } - if (state->period != cstate->period || - state->duty_cycle != cstate->duty_cycle) { - ret = sprd_pwm_config(spc, pwm, state->duty_cycle, - state->period); - if (ret) - 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) { @@ -206,88 +210,72 @@ static int sprd_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, static const struct pwm_ops sprd_pwm_ops = { .apply = sprd_pwm_apply, .get_state = sprd_pwm_get_state, - .owner = THIS_MODULE, }; -static int sprd_pwm_clk_init(struct sprd_pwm_chip *spc) +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++) { - struct sprd_pwm_chn *chn = &spc->chn[i]; int j; for (j = 0; j < SPRD_PWM_CHN_CLKS_NUM; ++j) - chn->clks[j].id = + chn[i].clks[j].id = sprd_pwm_clks[i * SPRD_PWM_CHN_CLKS_NUM + j]; - ret = devm_clk_bulk_get(spc->dev, SPRD_PWM_CHN_CLKS_NUM, - chn->clks); + ret = devm_clk_bulk_get(dev, SPRD_PWM_CHN_CLKS_NUM, + chn[i].clks); if (ret) { if (ret == -ENOENT) break; - if (ret != -EPROBE_DEFER) - dev_err(spc->dev, - "failed to get channel clocks\n"); - - return ret; + return dev_err_probe(dev, ret, + "failed to get channel clocks\n"); } - clk_pwm = chn->clks[SPRD_PWM_CHN_OUTPUT_CLK].clk; - chn->clk_rate = clk_get_rate(clk_pwm); + clk_pwm = chn[i].clks[SPRD_PWM_CHN_OUTPUT_CLK].clk; + chn[i].clk_rate = clk_get_rate(clk_pwm); } - if (!i) { - dev_err(spc->dev, "no available PWM channels\n"); - return -ENODEV; - } + if (!i) + return dev_err_probe(dev, -ENODEV, "no available PWM channels\n"); - spc->num_pwms = i; - - return 0; + return i; } static int sprd_pwm_probe(struct platform_device *pdev) { + struct pwm_chip *chip; struct sprd_pwm_chip *spc; - int ret; + 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; - spc = devm_kzalloc(&pdev->dev, sizeof(*spc), GFP_KERNEL); - if (!spc) - return -ENOMEM; + 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); - spc->dev = &pdev->dev; - platform_set_drvdata(pdev, spc); - - ret = sprd_pwm_clk_init(spc); - if (ret) - return ret; + memcpy(spc->chn, chn, sizeof(chn)); - spc->chip.dev = &pdev->dev; - spc->chip.ops = &sprd_pwm_ops; - spc->chip.base = -1; - spc->chip.npwm = spc->num_pwms; + chip->ops = &sprd_pwm_ops; - ret = pwmchip_add(&spc->chip); + ret = devm_pwmchip_add(&pdev->dev, chip); if (ret) dev_err(&pdev->dev, "failed to add PWM chip\n"); return ret; } -static int sprd_pwm_remove(struct platform_device *pdev) -{ - struct sprd_pwm_chip *spc = platform_get_drvdata(pdev); - - return pwmchip_remove(&spc->chip); -} - static const struct of_device_id sprd_pwm_of_match[] = { { .compatible = "sprd,ums512-pwm", }, { }, @@ -300,7 +288,6 @@ static struct platform_driver sprd_pwm_driver = { .of_match_table = sprd_pwm_of_match, }, .probe = sprd_pwm_probe, - .remove = sprd_pwm_remove, }; module_platform_driver(sprd_pwm_driver); diff --git a/drivers/pwm/pwm-sti.c b/drivers/pwm/pwm-sti.c index 1508616d794c..3b702b8f0c7f 100644 --- a/drivers/pwm/pwm-sti.c +++ b/drivers/pwm/pwm-sti.c @@ -73,31 +73,25 @@ struct sti_cpt_ddata { wait_queue_head_t wait; }; -struct sti_pwm_compat_data { - const struct reg_field *reg_fields; - unsigned int pwm_num_devs; - unsigned int cpt_num_devs; - unsigned int max_pwm_cnt; - unsigned int max_prescale; -}; - struct sti_pwm_chip { struct device *dev; struct clk *pwm_clk; struct clk *cpt_clk; struct regmap *regmap; - struct sti_pwm_compat_data *cdata; + 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_chip chip; struct pwm_device *cur; unsigned long configured; unsigned int en_count; - struct mutex sti_pwm_lock; /* To sync between enable/disable calls */ void __iomem *mmio; }; @@ -113,7 +107,7 @@ static const struct reg_field sti_pwm_regfields[MAX_REGFIELDS] = { static inline struct sti_pwm_chip *to_sti_pwmchip(struct pwm_chip *chip) { - return container_of(chip, struct sti_pwm_chip, chip); + return pwmchip_get_drvdata(chip); } /* @@ -122,7 +116,6 @@ static inline struct sti_pwm_chip *to_sti_pwmchip(struct pwm_chip *chip) static int sti_pwm_get_prescale(struct sti_pwm_chip *pc, unsigned long period, unsigned int *prescale) { - struct sti_pwm_compat_data *cdata = pc->cdata; unsigned long clk_rate; unsigned long value; unsigned int ps; @@ -137,13 +130,13 @@ static int sti_pwm_get_prescale(struct sti_pwm_chip *pc, unsigned long period, * prescale = ((period_ns * clk_rate) / (10^9 * (max_pwm_cnt + 1)) - 1 */ value = NSEC_PER_SEC / clk_rate; - value *= cdata->max_pwm_cnt + 1; + value *= pc->max_pwm_cnt + 1; if (period % value) return -EINVAL; ps = period / value - 1; - if (ps > cdata->max_prescale) + if (ps > pc->max_prescale) return -EINVAL; *prescale = ps; @@ -164,7 +157,6 @@ 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); - struct sti_pwm_compat_data *cdata = pc->cdata; unsigned int ncfg, value, prescale = 0; struct pwm_device *cur = pc->cur; struct device *dev = pc->dev; @@ -224,7 +216,7 @@ static int sti_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, * PWM pulse = (max_pwm_count + 1) local cycles, * that is continuous pulse: signal never goes low. */ - value = cdata->max_pwm_cnt * duty_ns / period_ns; + value = pc->max_pwm_cnt * duty_ns / period_ns; ret = regmap_write(pc->regmap, PWM_OUT_VAL(pwm->hwpwm), value); if (ret) @@ -251,55 +243,46 @@ 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 = 0; + int ret; /* * Since we have a common enable for all PWM devices, do not enable if * already enabled. */ - mutex_lock(&pc->sti_pwm_lock); if (!pc->en_count) { ret = clk_enable(pc->pwm_clk); if (ret) - goto out; + return ret; ret = clk_enable(pc->cpt_clk); if (ret) - goto out; + 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); - goto out; + return ret; } } pc->en_count++; -out: - mutex_unlock(&pc->sti_pwm_lock); - return ret; + return 0; } static void sti_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) { struct sti_pwm_chip *pc = to_sti_pwmchip(chip); - mutex_lock(&pc->sti_pwm_lock); - - if (--pc->en_count) { - mutex_unlock(&pc->sti_pwm_lock); + if (--pc->en_count) return; - } regmap_field_write(pc->pwm_out_en, 0); clk_disable(pc->pwm_clk); clk_disable(pc->cpt_clk); - - mutex_unlock(&pc->sti_pwm_lock); } static void sti_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm) @@ -313,14 +296,13 @@ 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_pwm_compat_data *cdata = pc->cdata; - struct sti_cpt_ddata *ddata = pwm_get_chip_data(pwm); + 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 >= cdata->cpt_num_devs) { + if (pwm->hwpwm >= pc->cpt_num_devs) { dev_err(dev, "device %u is not valid\n", pwm->hwpwm); return -EINVAL; } @@ -391,13 +373,43 @@ out: 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, - .config = sti_pwm_config, - .enable = sti_pwm_enable, - .disable = sti_pwm_disable, + .apply = sti_pwm_apply, .free = sti_pwm_free, - .owner = THIS_MODULE, }; static irqreturn_t sti_pwm_interrupt(int irq, void *data) @@ -417,7 +429,7 @@ static irqreturn_t sti_pwm_interrupt(int irq, void *data) while (cpt_int_stat) { devicenum = ffs(cpt_int_stat) - 1; - ddata = pwm_get_chip_data(&pc->chip.pwms[devicenum]); + ddata = &pc->ddata[devicenum]; /* * Capture input: @@ -471,58 +483,37 @@ static irqreturn_t sti_pwm_interrupt(int irq, void *data) return ret; } -static int sti_pwm_probe_dt(struct sti_pwm_chip *pc) +static int sti_pwm_probe_regmap(struct sti_pwm_chip *pc) { struct device *dev = pc->dev; - const struct reg_field *reg_fields; - struct device_node *np = dev->of_node; - struct sti_pwm_compat_data *cdata = pc->cdata; - u32 num_devs; - int ret; - - ret = of_property_read_u32(np, "st,pwm-num-chan", &num_devs); - if (!ret) - cdata->pwm_num_devs = num_devs; - - ret = of_property_read_u32(np, "st,capture-num-chan", &num_devs); - if (!ret) - cdata->cpt_num_devs = num_devs; - - if (!cdata->pwm_num_devs && !cdata->cpt_num_devs) { - dev_err(dev, "No channels configured\n"); - return -EINVAL; - } - - reg_fields = cdata->reg_fields; pc->prescale_low = devm_regmap_field_alloc(dev, pc->regmap, - reg_fields[PWMCLK_PRESCALE_LOW]); + 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, - reg_fields[PWMCLK_PRESCALE_HIGH]); + 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, - reg_fields[PWM_OUT_EN]); + 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, - reg_fields[PWM_CPT_EN]); + 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, - reg_fields[PWM_CPT_INT_EN]); + 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, - reg_fields[PWM_CPT_INT_STAT]); + 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); @@ -538,30 +529,40 @@ static const struct regmap_config sti_pwm_regmap_config = { static int sti_pwm_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - struct sti_pwm_compat_data *cdata; + 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; - struct resource *res; unsigned int i; int irq, ret; - pc = devm_kzalloc(dev, sizeof(*pc), GFP_KERNEL); - if (!pc) - return -ENOMEM; + 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; - cdata = devm_kzalloc(dev, sizeof(*cdata), GFP_KERNEL); - if (!cdata) - return -ENOMEM; + if (!pwm_num_devs && !cpt_num_devs) + return dev_err_probe(dev, -EINVAL, "No channels configured\n"); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + 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_ioremap_resource(dev, res); + 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 PTR_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) @@ -569,104 +570,58 @@ static int sti_pwm_probe(struct platform_device *pdev) ret = devm_request_irq(&pdev->dev, irq, sti_pwm_interrupt, 0, pdev->name, pc); - if (ret < 0) { - dev_err(&pdev->dev, "Failed to request IRQ\n"); - return ret; - } + 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. */ - cdata->reg_fields = sti_pwm_regfields; - cdata->max_prescale = 0xff; - cdata->max_pwm_cnt = 255; - cdata->pwm_num_devs = 0; - cdata->cpt_num_devs = 0; + pc->max_prescale = 0xff; + pc->max_pwm_cnt = 255; + pc->pwm_num_devs = pwm_num_devs; + pc->cpt_num_devs = cpt_num_devs; - pc->cdata = cdata; pc->dev = dev; pc->en_count = 0; - mutex_init(&pc->sti_pwm_lock); - ret = sti_pwm_probe_dt(pc); + ret = sti_pwm_probe_regmap(pc); if (ret) - return ret; + return dev_err_probe(dev, ret, "Failed to initialize regmap fields\n"); - if (!cdata->pwm_num_devs) - goto skip_pwm; - - pc->pwm_clk = of_clk_get_by_name(dev->of_node, "pwm"); - if (IS_ERR(pc->pwm_clk)) { - dev_err(dev, "failed to get PWM clock\n"); - return PTR_ERR(pc->pwm_clk); - } - - ret = clk_prepare(pc->pwm_clk); - if (ret) { - dev_err(dev, "failed to prepare clock\n"); - return ret; + 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"); } -skip_pwm: - if (!cdata->cpt_num_devs) - goto skip_cpt; + 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->cpt_clk = of_clk_get_by_name(dev->of_node, "capture"); - if (IS_ERR(pc->cpt_clk)) { - dev_err(dev, "failed to get PWM capture clock\n"); - return PTR_ERR(pc->cpt_clk); - } - - ret = clk_prepare(pc->cpt_clk); - if (ret) { - dev_err(dev, "failed to prepare clock\n"); - return ret; - } - -skip_cpt: - pc->chip.dev = dev; - pc->chip.ops = &sti_pwm_ops; - pc->chip.base = -1; - pc->chip.npwm = pc->cdata->pwm_num_devs; - - ret = pwmchip_add(&pc->chip); - if (ret < 0) { - clk_unprepare(pc->pwm_clk); - clk_unprepare(pc->cpt_clk); - return ret; - } - - for (i = 0; i < cdata->cpt_num_devs; i++) { - struct sti_cpt_ddata *ddata; - - ddata = devm_kzalloc(dev, sizeof(*ddata), GFP_KERNEL); - if (!ddata) + pc->ddata = devm_kcalloc(dev, cpt_num_devs, + sizeof(*pc->ddata), GFP_KERNEL); + if (!pc->ddata) return -ENOMEM; - init_waitqueue_head(&ddata->wait); - mutex_init(&ddata->lock); + for (i = 0; i < cpt_num_devs; i++) { + struct sti_cpt_ddata *ddata = &pc->ddata[i]; - pwm_set_chip_data(&pc->chip.pwms[i], ddata); + init_waitqueue_head(&ddata->wait); + mutex_init(&ddata->lock); + } } - platform_set_drvdata(pdev, pc); - - return 0; -} - -static int sti_pwm_remove(struct platform_device *pdev) -{ - struct sti_pwm_chip *pc = platform_get_drvdata(pdev); - unsigned int i; - - for (i = 0; i < pc->cdata->pwm_num_devs; i++) - pwm_disable(&pc->chip.pwms[i]); + chip->ops = &sti_pwm_ops; - clk_unprepare(pc->pwm_clk); - clk_unprepare(pc->cpt_clk); + ret = devm_pwmchip_add(dev, chip); + if (ret) + return dev_err_probe(dev, ret, "Failed to register pwm chip\n"); - return pwmchip_remove(&pc->chip); + return 0; } static const struct of_device_id sti_pwm_of_match[] = { @@ -681,7 +636,6 @@ static struct platform_driver sti_pwm_driver = { .of_match_table = sti_pwm_of_match, }, .probe = sti_pwm_probe, - .remove = sti_pwm_remove, }; module_platform_driver(sti_pwm_driver); diff --git a/drivers/pwm/pwm-stm32-lp.c b/drivers/pwm/pwm-stm32-lp.c index 67fca62524dc..4789eafb8bac 100644 --- a/drivers/pwm/pwm-stm32-lp.c +++ b/drivers/pwm/pwm-stm32-lp.c @@ -18,26 +18,114 @@ #include <linux/pwm.h> struct stm32_pwm_lp { - struct pwm_chip chip; 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 container_of(chip, struct stm32_pwm_lp, 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 val, mask, cfgr, presc = 0; + u32 arr, val, mask, cfgr, presc = 0; bool reenable; int ret; @@ -46,10 +134,28 @@ static int stm32_pwm_lp_apply(struct pwm_chip *chip, struct pwm_device *pwm, if (!state->enabled) { if (cstate.enabled) { - /* Disable LP timer */ - ret = regmap_write(priv->regmap, STM32_LPTIM_CR, 0); + /* 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); } @@ -61,7 +167,7 @@ static int stm32_pwm_lp_apply(struct pwm_chip *chip, struct pwm_device *pwm, do_div(div, NSEC_PER_SEC); if (!div) { /* Clock is too slow to achieve requested period. */ - dev_dbg(priv->chip.dev, "Can't reach %u ns\n", state->period); + dev_dbg(pwmchip_parent(chip), "Can't reach %llu ns\n", state->period); return -EINVAL; } @@ -69,7 +175,7 @@ static int stm32_pwm_lp_apply(struct pwm_chip *chip, struct pwm_device *pwm, while (div > STM32_LPTIM_MAX_ARR) { presc++; if ((1 << presc) > STM32_LPTIM_MAX_PRESCALER) { - dev_err(priv->chip.dev, "max prescaler exceeded\n"); + dev_err(pwmchip_parent(chip), "max prescaler exceeded\n"); return -EINVAL; } div = prd >> presc; @@ -80,6 +186,23 @@ static int stm32_pwm_lp_apply(struct pwm_chip *chip, struct pwm_device *pwm, 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); @@ -87,15 +210,20 @@ static int stm32_pwm_lp_apply(struct pwm_chip *chip, struct pwm_device *pwm, return ret; } - ret = regmap_read(priv->regmap, STM32_LPTIM_CFGR, &cfgr); - if (ret) - goto err; - if ((FIELD_GET(STM32_LPTIM_PRESC, cfgr) != presc) || - (FIELD_GET(STM32_LPTIM_WAVPOL, cfgr) != state->polarity)) { + ((FIELD_GET(STM32_LPTIM_WAVPOL, cfgr) != state->polarity) && !priv->num_cc_chans)) { val = FIELD_PREP(STM32_LPTIM_PRESC, presc); - val |= FIELD_PREP(STM32_LPTIM_WAVPOL, state->polarity); - mask = STM32_LPTIM_PRESC | STM32_LPTIM_WAVPOL; + 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; @@ -121,28 +249,34 @@ static int stm32_pwm_lp_apply(struct pwm_chip *chip, struct pwm_device *pwm, if (ret) goto err; - ret = regmap_write(priv->regmap, STM32_LPTIM_CMP, prd - (1 + dty)); + /* 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 CMP & ARR registers are properly written */ - ret = regmap_read_poll_timeout(priv->regmap, STM32_LPTIM_ISR, val, - (val & STM32_LPTIM_CMPOK_ARROK), + /* 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(priv->chip.dev, "ARR/CMP registers write issue\n"); + dev_err(pwmchip_parent(chip), "ARR/CMP registers write issue\n"); goto err; } - ret = regmap_write(priv->regmap, STM32_LPTIM_ICR, - STM32_LPTIM_CMPOKCF_ARROKCF); + 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_update_bits(priv->regmap, STM32_LPTIM_CR, - STM32_LPTIM_CNTSTRT, - STM32_LPTIM_CNTSTRT); + ret = regmap_set_bits(priv->regmap, STM32_LPTIM_CR, + STM32_LPTIM_CNTSTRT); if (ret) { regmap_write(priv->regmap, STM32_LPTIM_CR, 0); goto err; @@ -157,38 +291,61 @@ err: return ret; } -static void stm32_pwm_lp_get_state(struct pwm_chip *chip, - struct pwm_device *pwm, - struct pwm_state *state) +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; + u32 val, presc, prd, ccmr1; + bool enabled; u64 tmp; regmap_read(priv->regmap, STM32_LPTIM_CR, &val); - state->enabled = !!FIELD_GET(STM32_LPTIM_ENABLE, 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) - clk_enable(priv->clk); + 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); - state->polarity = FIELD_GET(STM32_LPTIM_WAVPOL, 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, STM32_LPTIM_CMP, &val); + 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 = { - .owner = THIS_MODULE, .apply = stm32_pwm_lp_apply, .get_state = stm32_pwm_lp_get_state, }; @@ -197,61 +354,62 @@ 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; - priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; + 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->chip.base = -1; - priv->chip.dev = &pdev->dev; - priv->chip.ops = &stm32_pwm_lp_ops; - priv->chip.npwm = 1; - priv->chip.of_xlate = of_pwm_xlate_with_flags; - priv->chip.of_pwm_n_cells = 3; - - ret = pwmchip_add(&priv->chip); + 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, priv); + platform_set_drvdata(pdev, chip); return 0; } -static int stm32_pwm_lp_remove(struct platform_device *pdev) +static int stm32_pwm_lp_suspend(struct device *dev) { - struct stm32_pwm_lp *priv = platform_get_drvdata(pdev); - - pwm_disable(&priv->chip.pwms[0]); - - return pwmchip_remove(&priv->chip); -} - -static int __maybe_unused stm32_pwm_lp_suspend(struct device *dev) -{ - struct stm32_pwm_lp *priv = dev_get_drvdata(dev); + struct pwm_chip *chip = dev_get_drvdata(dev); struct pwm_state state; - - pwm_get_state(&priv->chip.pwms[0], &state); - if (state.enabled) { - dev_err(dev, "The consumer didn't stop us (%s)\n", - priv->chip.pwms[0].label); - return -EBUSY; + 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 __maybe_unused stm32_pwm_lp_resume(struct device *dev) +static int stm32_pwm_lp_resume(struct device *dev) { return pinctrl_pm_select_default_state(dev); } -static SIMPLE_DEV_PM_OPS(stm32_pwm_lp_pm_ops, stm32_pwm_lp_suspend, - stm32_pwm_lp_resume); +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", }, @@ -261,11 +419,10 @@ MODULE_DEVICE_TABLE(of, stm32_pwm_lp_of_match); static struct platform_driver stm32_pwm_lp_driver = { .probe = stm32_pwm_lp_probe, - .remove = stm32_pwm_lp_remove, .driver = { .name = "stm32-pwm-lp", - .of_match_table = of_match_ptr(stm32_pwm_lp_of_match), - .pm = &stm32_pwm_lp_pm_ops, + .of_match_table = stm32_pwm_lp_of_match, + .pm = pm_ptr(&stm32_pwm_lp_pm_ops), }, }; module_platform_driver(stm32_pwm_lp_driver); diff --git a/drivers/pwm/pwm-stm32.c b/drivers/pwm/pwm-stm32.c index d3be944f2ae9..2594fb771b04 100644 --- a/drivers/pwm/pwm-stm32.c +++ b/drivers/pwm/pwm-stm32.c @@ -19,6 +19,7 @@ #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; @@ -27,7 +28,6 @@ struct stm32_breakinput { }; struct stm32_pwm { - struct pwm_chip chip; struct mutex lock; /* protect pwm config/enable */ struct clk *clk; struct regmap *regmap; @@ -40,7 +40,7 @@ struct stm32_pwm { static inline struct stm32_pwm *to_stm32_pwm_dev(struct pwm_chip *chip) { - return container_of(chip, struct stm32_pwm, chip); + return pwmchip_get_drvdata(chip); } static u32 active_channels(struct stm32_pwm *dev) @@ -52,19 +52,382 @@ static u32 active_channels(struct stm32_pwm *dev) return ccer & TIM_CCER_CCXE; } -static int write_ccrx(struct stm32_pwm *dev, int ch, u32 value) +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 ccr, duty; + 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 = mul_u64_u64_div_u64(wf->duty_length_ns, rate, + (u64)NSEC_PER_SEC * (wfhw->psc + 1)); + duty = min_t(u64, duty, wfhw->arr + 1); + + if (wf->duty_length_ns && wf->duty_offset_ns && + wf->duty_length_ns + wf->duty_offset_ns >= wf->period_length_ns) { + wfhw->ccer |= TIM_CCER_CCxP(ch + 1); + if (priv->have_complementary_output) + wfhw->ccer |= TIM_CCER_CCxNP(ch + 1); + + ccr = wfhw->arr + 1 - duty; + } else { + ccr = duty; + } + + wfhw->ccr = min_t(u64, ccr, wfhw->arr + 1); + +out: + dev_dbg(&chip->dev, "pwm#%u: %lld/%lld [+%lld] @%lu -> CCER: %08x, PSC: %08x, ARR: %08x, CCR: %08x\n", + pwm->hwpwm, wf->duty_length_ns, wf->period_length_ns, wf->duty_offset_ns, + rate, wfhw->ccer, wfhw->psc, wfhw->arr, wfhw->ccr); + + clk_disable(priv->clk); + + return ret; +} + +/* + * This should be moved to lib/math/div64.c. Currently there are some changes + * pending to mul_u64_u64_div_u64. Uwe will care for that when the dust settles. + */ +static u64 stm32_pwm_mul_u64_u64_div_u64_roundup(u64 a, u64 b, u64 c) +{ + u64 res = mul_u64_u64_div_u64(a, b, c); + /* Those multiplications might overflow but it doesn't matter */ + u64 rem = a * b - c * res; + + if (rem) + res += 1; + + return res; +} + +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 = stm32_pwm_mul_u64_u64_div_u64_roundup(((u64)wfhw->psc + 1) * (wfhw->arr + 1), + NSEC_PER_SEC, rate); + + ccr_ns = stm32_pwm_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 = + stm32_pwm_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) { - switch (ch) { - case 0: - return regmap_write(dev->regmap, TIM_CCR1, value); - case 1: - return regmap_write(dev->regmap, TIM_CCR2, value); - case 2: - return regmap_write(dev->regmap, TIM_CCR3, value); - case 3: - return regmap_write(dev->regmap, TIM_CCR4, value); + 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; } - return -EINVAL; + +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) @@ -105,24 +468,25 @@ static int write_ccrx(struct stm32_pwm *dev, int ch, u32 value) * - Period = t2 - t0 * - Duty cycle = t1 - t0 */ -static int stm32_pwm_raw_capture(struct stm32_pwm *priv, struct pwm_device *pwm, +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 device *parent = priv->chip.dev->parent; + 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_update_bits(priv->regmap, TIM_EGR, TIM_EGR_UG, TIM_EGR_UG); - regmap_update_bits(priv->regmap, TIM_CR1, TIM_CR1_CEN, TIM_CR1_CEN); + 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_update_bits(priv->regmap, TIM_CCER, ccen, ccen); + regmap_set_bits(priv->regmap, TIM_CCER, ccen); /* * Timer DMA burst mode. Request 2 registers, 2 bursts, to get both @@ -160,8 +524,8 @@ static int stm32_pwm_raw_capture(struct stm32_pwm *priv, struct pwm_device *pwm, } stop: - regmap_update_bits(priv->regmap, TIM_CCER, ccen, 0); - regmap_update_bits(priv->regmap, TIM_CR1, TIM_CR1_CEN, 0); + regmap_clear_bits(priv->regmap, TIM_CCER, ccen); + regmap_clear_bits(priv->regmap, TIM_CR1, TIM_CR1_CEN); return ret; } @@ -185,7 +549,7 @@ static int stm32_pwm_capture(struct pwm_chip *chip, struct pwm_device *pwm, ret = clk_enable(priv->clk); if (ret) { - dev_err(priv->chip.dev, "failed to enable counter clock\n"); + dev_err(pwmchip_parent(chip), "failed to enable counter clock\n"); goto unlock; } @@ -207,6 +571,10 @@ static int stm32_pwm_capture(struct pwm_chip *chip, struct pwm_device *pwm, 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, @@ -219,7 +587,7 @@ static int stm32_pwm_capture(struct pwm_chip *chip, struct pwm_device *pwm, TIM_CCER_CC12P : TIM_CCER_CC34P, pwm->hwpwm < 2 ? TIM_CCER_CC2P : TIM_CCER_CC4P); - ret = stm32_pwm_raw_capture(priv, pwm, tmo_ms, &raw_prd, &raw_dty); + ret = stm32_pwm_raw_capture(chip, pwm, tmo_ms, &raw_prd, &raw_dty); if (ret) goto stop; @@ -233,14 +601,14 @@ static int stm32_pwm_capture(struct pwm_chip *chip, struct pwm_device *pwm, scale = max_arr / min(max_arr, raw_prd); } else { - scale = priv->max_arr; /* bellow resolution, use max scale */ + 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(priv, pwm, tmo_ms, &raw_prd, + ret = stm32_pwm_raw_capture(chip, pwm, tmo_ms, &raw_prd, &raw_dty); if (ret) goto stop; @@ -268,7 +636,7 @@ static int stm32_pwm_capture(struct pwm_chip *chip, struct pwm_device *pwm, FIELD_PREP(TIM_CCMR_IC1PSC, icpsc) | FIELD_PREP(TIM_CCMR_IC2PSC, icpsc)); - ret = stm32_pwm_raw_capture(priv, pwm, tmo_ms, &raw_prd, &raw_dty); + ret = stm32_pwm_raw_capture(chip, pwm, tmo_ms, &raw_prd, &raw_dty); if (ret) goto stop; @@ -319,172 +687,13 @@ unlock: return ret; } -static int stm32_pwm_config(struct stm32_pwm *priv, int ch, - int duty_ns, int period_ns) -{ - unsigned long long prd, div, dty; - unsigned int prescaler = 0; - u32 ccmr, mask, shift; - - /* Period and prescaler values depends on clock rate */ - div = (unsigned long long)clk_get_rate(priv->clk) * period_ns; - - do_div(div, NSEC_PER_SEC); - prd = div; - - while (div > priv->max_arr) { - prescaler++; - div = prd; - do_div(div, prescaler + 1); - } - - prd = div; - - if (prescaler > MAX_TIM_PSC) - return -EINVAL; - - /* - * All channels share the same prescaler and counter so when two - * channels are active at the same time we can't change them - */ - if (active_channels(priv) & ~(1 << ch * 4)) { - u32 psc, arr; - - regmap_read(priv->regmap, TIM_PSC, &psc); - regmap_read(priv->regmap, TIM_ARR, &arr); - - if ((psc != prescaler) || (arr != prd - 1)) - return -EBUSY; - } - - regmap_write(priv->regmap, TIM_PSC, prescaler); - regmap_write(priv->regmap, TIM_ARR, prd - 1); - regmap_update_bits(priv->regmap, TIM_CR1, TIM_CR1_ARPE, TIM_CR1_ARPE); - - /* Calculate the duty cycles */ - dty = prd * duty_ns; - do_div(dty, period_ns); - - write_ccrx(priv, ch, dty); - - /* 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) - regmap_update_bits(priv->regmap, TIM_CCMR1, mask, ccmr); - else - regmap_update_bits(priv->regmap, TIM_CCMR2, mask, ccmr); - - regmap_update_bits(priv->regmap, TIM_BDTR, TIM_BDTR_MOE, TIM_BDTR_MOE); - - return 0; -} - -static int stm32_pwm_set_polarity(struct stm32_pwm *priv, int ch, - enum pwm_polarity polarity) -{ - u32 mask; - - mask = TIM_CCER_CC1P << (ch * 4); - if (priv->have_complementary_output) - mask |= TIM_CCER_CC1NP << (ch * 4); - - regmap_update_bits(priv->regmap, TIM_CCER, mask, - polarity == PWM_POLARITY_NORMAL ? 0 : mask); - - return 0; -} - -static int stm32_pwm_enable(struct stm32_pwm *priv, int ch) -{ - u32 mask; - int ret; - - ret = clk_enable(priv->clk); - if (ret) - return ret; - - /* Enable channel */ - mask = TIM_CCER_CC1E << (ch * 4); - if (priv->have_complementary_output) - mask |= TIM_CCER_CC1NE << (ch * 4); - - regmap_update_bits(priv->regmap, TIM_CCER, mask, mask); - - /* Make sure that registers are updated */ - regmap_update_bits(priv->regmap, TIM_EGR, TIM_EGR_UG, TIM_EGR_UG); - - /* Enable controller */ - regmap_update_bits(priv->regmap, TIM_CR1, TIM_CR1_CEN, TIM_CR1_CEN); - - return 0; -} - -static void stm32_pwm_disable(struct stm32_pwm *priv, int ch) -{ - u32 mask; - - /* Disable channel */ - mask = TIM_CCER_CC1E << (ch * 4); - if (priv->have_complementary_output) - mask |= TIM_CCER_CC1NE << (ch * 4); - - regmap_update_bits(priv->regmap, TIM_CCER, mask, 0); - - /* When all channels are disabled, we can disable the controller */ - if (!active_channels(priv)) - regmap_update_bits(priv->regmap, TIM_CR1, TIM_CR1_CEN, 0); - - clk_disable(priv->clk); -} - -static int stm32_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, - const struct pwm_state *state) -{ - bool enabled; - struct stm32_pwm *priv = to_stm32_pwm_dev(chip); - int ret; - - enabled = pwm->state.enabled; - - if (enabled && !state->enabled) { - stm32_pwm_disable(priv, pwm->hwpwm); - return 0; - } - - if (state->polarity != pwm->state.polarity) - stm32_pwm_set_polarity(priv, pwm->hwpwm, state->polarity); - - ret = stm32_pwm_config(priv, pwm->hwpwm, - state->duty_cycle, state->period); - if (ret) - return ret; - - if (!enabled && state->enabled) - ret = stm32_pwm_enable(priv, pwm->hwpwm); - - return ret; -} - -static int stm32_pwm_apply_locked(struct pwm_chip *chip, struct pwm_device *pwm, - const struct pwm_state *state) -{ - struct stm32_pwm *priv = to_stm32_pwm_dev(chip); - int ret; - - /* protect common prescaler for all active channels */ - mutex_lock(&priv->lock); - ret = stm32_pwm_apply(chip, pwm, state); - mutex_unlock(&priv->lock); - - return ret; -} - static const struct pwm_ops stm32pwm_ops = { - .owner = THIS_MODULE, - .apply = stm32_pwm_apply_locked, + .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, }; @@ -560,49 +769,64 @@ static int stm32_pwm_probe_breakinputs(struct stm32_pwm *priv, return stm32_pwm_apply_breakinputs(priv); } -static void stm32_pwm_detect_complementary(struct stm32_pwm *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_update_bits(priv->regmap, - TIM_CCER, TIM_CCER_CC1NE, TIM_CCER_CC1NE); + regmap_set_bits(priv->regmap, TIM_CCER, TIM_CCER_CC1NE); regmap_read(priv->regmap, TIM_CCER, &ccer); - regmap_update_bits(priv->regmap, TIM_CCER, TIM_CCER_CC1NE, 0); + regmap_clear_bits(priv->regmap, TIM_CCER, TIM_CCER_CC1NE); priv->have_complementary_output = (ccer != 0); } -static int stm32_pwm_detect_channels(struct stm32_pwm *priv) +static unsigned int stm32_pwm_detect_channels(struct stm32_timers *ddata, + unsigned int *num_enabled) { - u32 ccer; - int npwm = 0; + struct regmap *regmap = ddata->regmap; + u32 ccer, ccer_backup; - /* - * If channels enable bits don't exist writing 1 will have no - * effect so we can detect and count them. - */ - regmap_update_bits(priv->regmap, - TIM_CCER, TIM_CCER_CCXE, TIM_CCER_CCXE); - regmap_read(priv->regmap, TIM_CCER, &ccer); - regmap_update_bits(priv->regmap, TIM_CCER, TIM_CCER_CCXE, 0); + regmap_read(regmap, TIM_CCER, &ccer_backup); + *num_enabled = hweight32(ccer_backup & TIM_CCER_CCXE); - if (ccer & TIM_CCER_CC1E) - npwm++; + if (ddata->ipidr) { + u32 hwcfgr; + unsigned int npwm; - if (ccer & TIM_CCER_CC2E) - npwm++; + /* Deduce from HWCFGR the number of outputs (MP25). */ + regmap_read(regmap, TIM_HWCFGR1, &hwcfgr); - if (ccer & TIM_CCER_CC3E) - npwm++; + /* + * 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); - if (ccer & TIM_CCER_CC4E) - npwm++; + return npwm < STM32_MAX_PWM_OUTPUT ? npwm : STM32_MAX_PWM_OUTPUT; + } - return npwm; + /* + * 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) @@ -610,70 +834,81 @@ 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; - priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; + 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; - priv->chip.of_xlate = of_pwm_xlate_with_flags; - priv->chip.of_pwm_n_cells = 3; if (!priv->regmap || !priv->clk) - return -EINVAL; + 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 ret; + return dev_err_probe(dev, ret, + "Failed to configure breakinputs\n"); - stm32_pwm_detect_complementary(priv); + stm32_pwm_detect_complementary(priv, ddata); - priv->chip.base = -1; - priv->chip.dev = dev; - priv->chip.ops = &stm32pwm_ops; - priv->chip.npwm = stm32_pwm_detect_channels(priv); - - ret = pwmchip_add(&priv->chip); - if (ret < 0) - return ret; + ret = devm_clk_rate_exclusive_get(dev, priv->clk); + if (ret) + return dev_err_probe(dev, ret, "Failed to lock clock\n"); - platform_set_drvdata(pdev, priv); + /* + * 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)); - return 0; -} + chip->ops = &stm32pwm_ops; -static int stm32_pwm_remove(struct platform_device *pdev) -{ - struct stm32_pwm *priv = platform_get_drvdata(pdev); - unsigned int i; + /* 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; + } - for (i = 0; i < priv->chip.npwm; i++) - pwm_disable(&priv->chip.pwms[i]); + ret = devm_pwmchip_add(dev, chip); + if (ret < 0) + return dev_err_probe(dev, ret, + "Failed to register pwmchip\n"); - pwmchip_remove(&priv->chip); + platform_set_drvdata(pdev, chip); return 0; } -static int __maybe_unused stm32_pwm_suspend(struct device *dev) +static int stm32_pwm_suspend(struct device *dev) { - struct stm32_pwm *priv = dev_get_drvdata(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 < priv->chip.npwm; i++) { - mask = TIM_CCER_CC1E << (i * 4); + 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, priv->chip.pwms[i].label); + i, chip->pwms[i].label); return -EBUSY; } } @@ -681,9 +916,10 @@ static int __maybe_unused stm32_pwm_suspend(struct device *dev) return pinctrl_pm_select_sleep_state(dev); } -static int __maybe_unused stm32_pwm_resume(struct device *dev) +static int stm32_pwm_resume(struct device *dev) { - struct stm32_pwm *priv = dev_get_drvdata(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); @@ -694,21 +930,21 @@ static int __maybe_unused stm32_pwm_resume(struct device *dev) return stm32_pwm_apply_breakinputs(priv); } -static SIMPLE_DEV_PM_OPS(stm32_pwm_pm_ops, stm32_pwm_suspend, stm32_pwm_resume); +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, - .remove = stm32_pwm_remove, .driver = { .name = "stm32-pwm", .of_match_table = stm32_pwm_of_match, - .pm = &stm32_pwm_pm_ops, + .pm = pm_ptr(&stm32_pwm_pm_ops), }, }; module_platform_driver(stm32_pwm_driver); diff --git a/drivers/pwm/pwm-stmpe.c b/drivers/pwm/pwm-stmpe.c index be5f6d7359d4..73f12843999a 100644 --- a/drivers/pwm/pwm-stmpe.c +++ b/drivers/pwm/pwm-stmpe.c @@ -27,13 +27,12 @@ struct stmpe_pwm { struct stmpe *stmpe; - struct pwm_chip chip; u8 last_duty; }; static inline struct stmpe_pwm *to_stmpe_pwm(struct pwm_chip *chip) { - return container_of(chip, struct stmpe_pwm, chip); + return pwmchip_get_drvdata(chip); } static int stmpe_24xx_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) @@ -44,7 +43,7 @@ static int stmpe_24xx_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) ret = stmpe_reg_read(stmpe_pwm->stmpe, STMPE24XX_PWMCS); if (ret < 0) { - dev_err(chip->dev, "error reading PWM#%u control\n", + dev_dbg(pwmchip_parent(chip), "error reading PWM#%u control\n", pwm->hwpwm); return ret; } @@ -53,7 +52,7 @@ static int stmpe_24xx_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) ret = stmpe_reg_write(stmpe_pwm->stmpe, STMPE24XX_PWMCS, value); if (ret) { - dev_err(chip->dev, "error writing PWM#%u control\n", + dev_dbg(pwmchip_parent(chip), "error writing PWM#%u control\n", pwm->hwpwm); return ret; } @@ -61,8 +60,8 @@ static int stmpe_24xx_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) return 0; } -static void stmpe_24xx_pwm_disable(struct pwm_chip *chip, - struct pwm_device *pwm) +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; @@ -70,19 +69,18 @@ static void stmpe_24xx_pwm_disable(struct pwm_chip *chip, ret = stmpe_reg_read(stmpe_pwm->stmpe, STMPE24XX_PWMCS); if (ret < 0) { - dev_err(chip->dev, "error reading PWM#%u control\n", + dev_dbg(pwmchip_parent(chip), "error reading PWM#%u control\n", pwm->hwpwm); - return; + return ret; } value = ret & ~BIT(pwm->hwpwm); ret = stmpe_reg_write(stmpe_pwm->stmpe, STMPE24XX_PWMCS, value); - if (ret) { - dev_err(chip->dev, "error writing PWM#%u control\n", + if (ret) + dev_dbg(pwmchip_parent(chip), "error writing PWM#%u control\n", pwm->hwpwm); - return; - } + return ret; } /* STMPE 24xx PWM instructions */ @@ -111,7 +109,9 @@ static int stmpe_24xx_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, /* Make sure we are disabled */ if (pwm_is_enabled(pwm)) { - stmpe_24xx_pwm_disable(chip, pwm); + ret = stmpe_24xx_pwm_disable(chip, pwm); + if (ret) + return ret; } else { /* Connect the PWM to the pin */ pin = pwm->hwpwm; @@ -124,7 +124,7 @@ static int stmpe_24xx_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, ret = stmpe_set_altfunc(stmpe_pwm->stmpe, BIT(pin), STMPE_BLOCK_PWM); if (ret) { - dev_err(chip->dev, "unable to connect PWM#%u to pin\n", + dev_err(pwmchip_parent(chip), "unable to connect PWM#%u to pin\n", pwm->hwpwm); return ret; } @@ -149,7 +149,7 @@ static int stmpe_24xx_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, return -ENODEV; } - dev_dbg(chip->dev, "PWM#%u: config duty %d ns, period %d ns\n", + 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) { @@ -215,7 +215,7 @@ static int stmpe_24xx_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, program[1] = BRANCH; } - dev_dbg(chip->dev, + 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]); @@ -232,7 +232,7 @@ static int stmpe_24xx_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, ret = stmpe_reg_write(stmpe_pwm->stmpe, offset, value); if (ret) { - dev_err(chip->dev, "error writing register %02x: %d\n", + dev_dbg(pwmchip_parent(chip), "error writing register %02x: %d\n", offset, ret); return ret; } @@ -241,7 +241,7 @@ static int stmpe_24xx_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, ret = stmpe_reg_write(stmpe_pwm->stmpe, offset, value); if (ret) { - dev_err(chip->dev, "error writing register %02x: %d\n", + dev_dbg(pwmchip_parent(chip), "error writing register %02x: %d\n", offset, ret); return ret; } @@ -254,62 +254,105 @@ static int stmpe_24xx_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, /* Sleep for 200ms so we're sure it will take effect */ msleep(200); - dev_dbg(chip->dev, "programmed PWM#%u, %u bytes\n", pwm->hwpwm, i); + 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 = { - .config = stmpe_24xx_pwm_config, - .enable = stmpe_24xx_pwm_enable, - .disable = stmpe_24xx_pwm_disable, - .owner = THIS_MODULE, + .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 stmpe_pwm *pwm; + struct pwm_chip *chip; + struct stmpe_pwm *stmpe_pwm; int ret; - pwm = devm_kzalloc(&pdev->dev, sizeof(*pwm), GFP_KERNEL); - if (!pwm) - return -ENOMEM; + 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"); + } - pwm->stmpe = stmpe; - pwm->chip.dev = &pdev->dev; - pwm->chip.base = -1; + chip = devm_pwmchip_alloc(&pdev->dev, 3, sizeof(*stmpe_pwm)); + if (IS_ERR(chip)) + return PTR_ERR(chip); + stmpe_pwm = to_stmpe_pwm(chip); - if (stmpe->partnum == STMPE2401 || stmpe->partnum == STMPE2403) { - pwm->chip.ops = &stmpe_24xx_pwm_ops; - pwm->chip.npwm = 3; - } else { - if (stmpe->partnum == STMPE1601) - dev_err(&pdev->dev, "STMPE1601 not yet supported\n"); - else - dev_err(&pdev->dev, "Unknown STMPE PWM\n"); + stmpe_pwm->stmpe = stmpe; - return -ENODEV; - } + chip->ops = &stmpe_24xx_pwm_ops; ret = stmpe_enable(stmpe, STMPE_BLOCK_PWM); if (ret) return ret; - ret = pwmchip_add(&pwm->chip); + ret = pwmchip_add(chip); if (ret) { stmpe_disable(stmpe, STMPE_BLOCK_PWM); return ret; } - platform_set_drvdata(pdev, pwm); + platform_set_drvdata(pdev, chip); return 0; } -static struct platform_driver stmpe_pwm_driver = { +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), }; -builtin_platform_driver_probe(stmpe_pwm_driver, stmpe_pwm_probe); +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 index 18fbbe3277d0..6c5591ca868b 100644 --- a/drivers/pwm/pwm-sun4i.c +++ b/drivers/pwm/pwm-sun4i.c @@ -17,12 +17,10 @@ #include <linux/jiffies.h> #include <linux/module.h> #include <linux/of.h> -#include <linux/of_device.h> #include <linux/platform_device.h> #include <linux/pwm.h> #include <linux/reset.h> #include <linux/slab.h> -#include <linux/spinlock.h> #include <linux/time.h> #define PWM_CTRL_REG 0x0 @@ -82,45 +80,44 @@ struct sun4i_pwm_data { }; struct sun4i_pwm_chip { - struct pwm_chip chip; struct clk *bus_clk; struct clk *clk; struct reset_control *rst; void __iomem *base; - spinlock_t ctrl_lock; const struct sun4i_pwm_data *data; - unsigned long next_period[2]; }; static inline struct sun4i_pwm_chip *to_sun4i_pwm_chip(struct pwm_chip *chip) { - return container_of(chip, struct sun4i_pwm_chip, chip); + return pwmchip_get_drvdata(chip); } -static inline u32 sun4i_pwm_readl(struct sun4i_pwm_chip *chip, +static inline u32 sun4i_pwm_readl(struct sun4i_pwm_chip *sun4ichip, unsigned long offset) { - return readl(chip->base + offset); + return readl(sun4ichip->base + offset); } -static inline void sun4i_pwm_writel(struct sun4i_pwm_chip *chip, +static inline void sun4i_pwm_writel(struct sun4i_pwm_chip *sun4ichip, u32 val, unsigned long offset) { - writel(val, chip->base + offset); + writel(val, sun4ichip->base + offset); } -static void sun4i_pwm_get_state(struct pwm_chip *chip, - struct pwm_device *pwm, - struct pwm_state *state) +static int sun4i_pwm_get_state(struct pwm_chip *chip, + struct pwm_device *pwm, + struct pwm_state *state) { - struct sun4i_pwm_chip *sun4i_pwm = to_sun4i_pwm_chip(chip); + struct sun4i_pwm_chip *sun4ichip = to_sun4i_pwm_chip(chip); u64 clk_rate, tmp; u32 val; unsigned int prescaler; - clk_rate = clk_get_rate(sun4i_pwm->clk); + clk_rate = clk_get_rate(sun4ichip->clk); + if (!clk_rate) + return -EINVAL; - val = sun4i_pwm_readl(sun4i_pwm, PWM_CTRL_REG); + val = sun4i_pwm_readl(sun4ichip, PWM_CTRL_REG); /* * PWM chapter in H6 manual has a diagram which explains that if bypass @@ -128,22 +125,22 @@ static void sun4i_pwm_get_state(struct pwm_chip *chip, * proved that also enable bit is ignored in this case. */ if ((val & BIT_CH(PWM_BYPASS, pwm->hwpwm)) && - sun4i_pwm->data->has_direct_mod_clk_output) { + 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; + return 0; } if ((PWM_REG_PRESCAL(val, pwm->hwpwm) == PWM_PRESCAL_MASK) && - sun4i_pwm->data->has_prescaler_bypass) + sun4ichip->data->has_prescaler_bypass) prescaler = 1; else prescaler = prescaler_table[PWM_REG_PRESCAL(val, pwm->hwpwm)]; if (prescaler == 0) - return; + return -EINVAL; if (val & BIT_CH(PWM_ACT_STATE, pwm->hwpwm)) state->polarity = PWM_POLARITY_NORMAL; @@ -156,16 +153,18 @@ static void sun4i_pwm_get_state(struct pwm_chip *chip, else state->enabled = false; - val = sun4i_pwm_readl(sun4i_pwm, PWM_CH_PRD(pwm->hwpwm)); + 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 *sun4i_pwm, +static int sun4i_pwm_calculate(struct sun4i_pwm_chip *sun4ichip, const struct pwm_state *state, u32 *dty, u32 *prd, unsigned int *prsclr, bool *bypass) @@ -173,9 +172,9 @@ static int sun4i_pwm_calculate(struct sun4i_pwm_chip *sun4i_pwm, u64 clk_rate, div = 0; unsigned int prescaler = 0; - clk_rate = clk_get_rate(sun4i_pwm->clk); + clk_rate = clk_get_rate(sun4ichip->clk); - *bypass = sun4i_pwm->data->has_direct_mod_clk_output && + *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) && @@ -185,7 +184,7 @@ static int sun4i_pwm_calculate(struct sun4i_pwm_chip *sun4i_pwm, if (*bypass) return 0; - if (sun4i_pwm->data->has_prescaler_bypass) { + if (sun4ichip->data->has_prescaler_bypass) { /* First, test without any prescaler when available */ prescaler = PWM_PRESCAL_MASK; /* @@ -231,42 +230,39 @@ static int sun4i_pwm_calculate(struct sun4i_pwm_chip *sun4i_pwm, static int sun4i_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, const struct pwm_state *state) { - struct sun4i_pwm_chip *sun4i_pwm = to_sun4i_pwm_chip(chip); + 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; - unsigned long now; bool bypass; pwm_get_state(pwm, &cstate); if (!cstate.enabled) { - ret = clk_prepare_enable(sun4i_pwm->clk); + ret = clk_prepare_enable(sun4ichip->clk); if (ret) { - dev_err(chip->dev, "failed to enable PWM clock\n"); + dev_err(pwmchip_parent(chip), "failed to enable PWM clock\n"); return ret; } } - ret = sun4i_pwm_calculate(sun4i_pwm, state, &duty, &period, &prescaler, + ret = sun4i_pwm_calculate(sun4ichip, state, &duty, &period, &prescaler, &bypass); if (ret) { - dev_err(chip->dev, "period exceeds the maximum value\n"); + dev_err(pwmchip_parent(chip), "period exceeds the maximum value\n"); if (!cstate.enabled) - clk_disable_unprepare(sun4i_pwm->clk); + clk_disable_unprepare(sun4ichip->clk); return ret; } - spin_lock(&sun4i_pwm->ctrl_lock); - ctrl = sun4i_pwm_readl(sun4i_pwm, PWM_CTRL_REG); + ctrl = sun4i_pwm_readl(sun4ichip, PWM_CTRL_REG); - if (sun4i_pwm->data->has_direct_mod_clk_output) { + 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(sun4i_pwm, ctrl, PWM_CTRL_REG); - spin_unlock(&sun4i_pwm->ctrl_lock); + sun4i_pwm_writel(sun4ichip, ctrl, PWM_CTRL_REG); return 0; } @@ -276,16 +272,14 @@ static int sun4i_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, 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(sun4i_pwm, ctrl, PWM_CTRL_REG); + 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(sun4i_pwm, val, PWM_CH_PRD(pwm->hwpwm)); - sun4i_pwm->next_period[pwm->hwpwm] = jiffies + - usecs_to_jiffies(cstate.period / 1000 + 1); + sun4i_pwm_writel(sun4ichip, val, PWM_CH_PRD(pwm->hwpwm)); if (state->polarity != PWM_POLARITY_NORMAL) ctrl &= ~BIT_CH(PWM_ACT_STATE, pwm->hwpwm); @@ -294,39 +288,27 @@ static int sun4i_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, ctrl |= BIT_CH(PWM_CLK_GATING, pwm->hwpwm); - if (state->enabled) { + if (state->enabled) ctrl |= BIT_CH(PWM_EN, pwm->hwpwm); - } else { - ctrl &= ~BIT_CH(PWM_EN, pwm->hwpwm); - ctrl &= ~BIT_CH(PWM_CLK_GATING, pwm->hwpwm); - } - sun4i_pwm_writel(sun4i_pwm, ctrl, PWM_CTRL_REG); - - spin_unlock(&sun4i_pwm->ctrl_lock); + sun4i_pwm_writel(sun4ichip, ctrl, PWM_CTRL_REG); if (state->enabled) return 0; /* We need a full period to elapse before disabling the channel. */ - now = jiffies; - if (time_before(now, sun4i_pwm->next_period[pwm->hwpwm])) { - delay_us = jiffies_to_usecs(sun4i_pwm->next_period[pwm->hwpwm] - - now); - if ((delay_us / 500) > MAX_UDELAY_MS) - msleep(delay_us / 1000 + 1); - else - usleep_range(delay_us, delay_us * 2); - } + 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); - spin_lock(&sun4i_pwm->ctrl_lock); - ctrl = sun4i_pwm_readl(sun4i_pwm, PWM_CTRL_REG); + 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(sun4i_pwm, ctrl, PWM_CTRL_REG); - spin_unlock(&sun4i_pwm->ctrl_lock); + sun4i_pwm_writel(sun4ichip, ctrl, PWM_CTRL_REG); - clk_disable_unprepare(sun4i_pwm->clk); + clk_disable_unprepare(sun4ichip->clk); return 0; } @@ -334,7 +316,6 @@ static int sun4i_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, static const struct pwm_ops sun4i_pwm_ops = { .apply = sun4i_pwm_apply, .get_state = sun4i_pwm_get_state, - .owner = THIS_MODULE, }; static const struct sun4i_pwm_data sun4i_pwm_dual_nobypass = { @@ -394,22 +375,24 @@ MODULE_DEVICE_TABLE(of, sun4i_pwm_dt_ids); static int sun4i_pwm_probe(struct platform_device *pdev) { - struct sun4i_pwm_chip *pwm; - struct resource *res; + struct pwm_chip *chip; + const struct sun4i_pwm_data *data; + struct sun4i_pwm_chip *sun4ichip; int ret; - pwm = devm_kzalloc(&pdev->dev, sizeof(*pwm), GFP_KERNEL); - if (!pwm) - return -ENOMEM; - - pwm->data = of_device_get_match_data(&pdev->dev); - if (!pwm->data) + data = of_device_get_match_data(&pdev->dev); + if (!data) return -ENODEV; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - pwm->base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(pwm->base)) - return PTR_ERR(pwm->base); + 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 @@ -422,42 +405,30 @@ static int sun4i_pwm_probe(struct platform_device *pdev) * unnamed one of the PWM device) and if this is not found we fall * back to the first clock of the PWM. */ - pwm->clk = devm_clk_get_optional(&pdev->dev, "mod"); - if (IS_ERR(pwm->clk)) { - if (PTR_ERR(pwm->clk) != -EPROBE_DEFER) - dev_err(&pdev->dev, "get mod clock failed %pe\n", - pwm->clk); - return PTR_ERR(pwm->clk); - } - - if (!pwm->clk) { - pwm->clk = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(pwm->clk)) { - if (PTR_ERR(pwm->clk) != -EPROBE_DEFER) - dev_err(&pdev->dev, "get unnamed clock failed %pe\n", - pwm->clk); - return PTR_ERR(pwm->clk); - } + 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"); } - pwm->bus_clk = devm_clk_get_optional(&pdev->dev, "bus"); - if (IS_ERR(pwm->bus_clk)) { - if (PTR_ERR(pwm->bus_clk) != -EPROBE_DEFER) - dev_err(&pdev->dev, "get bus clock failed %pe\n", - pwm->bus_clk); - return PTR_ERR(pwm->bus_clk); - } + 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"); - pwm->rst = devm_reset_control_get_optional_shared(&pdev->dev, NULL); - if (IS_ERR(pwm->rst)) { - if (PTR_ERR(pwm->rst) != -EPROBE_DEFER) - dev_err(&pdev->dev, "get reset failed %pe\n", - pwm->rst); - return PTR_ERR(pwm->rst); - } + 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(pwm->rst); + ret = reset_control_deassert(sun4ichip->rst); if (ret) { dev_err(&pdev->dev, "cannot deassert reset control: %pe\n", ERR_PTR(ret)); @@ -468,53 +439,42 @@ static int sun4i_pwm_probe(struct platform_device *pdev) * 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(pwm->bus_clk); + 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; } - pwm->chip.dev = &pdev->dev; - pwm->chip.ops = &sun4i_pwm_ops; - pwm->chip.base = -1; - pwm->chip.npwm = pwm->data->npwm; - pwm->chip.of_xlate = of_pwm_xlate_with_flags; - pwm->chip.of_pwm_n_cells = 3; + chip->ops = &sun4i_pwm_ops; - spin_lock_init(&pwm->ctrl_lock); - - ret = pwmchip_add(&pwm->chip); + 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, pwm); + platform_set_drvdata(pdev, chip); return 0; err_pwm_add: - clk_disable_unprepare(pwm->bus_clk); + clk_disable_unprepare(sun4ichip->bus_clk); err_bus: - reset_control_assert(pwm->rst); + reset_control_assert(sun4ichip->rst); return ret; } -static int sun4i_pwm_remove(struct platform_device *pdev) +static void sun4i_pwm_remove(struct platform_device *pdev) { - struct sun4i_pwm_chip *pwm = platform_get_drvdata(pdev); - int ret; + struct pwm_chip *chip = platform_get_drvdata(pdev); + struct sun4i_pwm_chip *sun4ichip = to_sun4i_pwm_chip(chip); - ret = pwmchip_remove(&pwm->chip); - if (ret) - return ret; + pwmchip_remove(chip); - clk_disable_unprepare(pwm->bus_clk); - reset_control_assert(pwm->rst); - - return 0; + clk_disable_unprepare(sun4ichip->bus_clk); + reset_control_assert(sun4ichip->rst); } static struct platform_driver sun4i_pwm_driver = { diff --git a/drivers/pwm/pwm-sunplus.c b/drivers/pwm/pwm-sunplus.c new file mode 100644 index 000000000000..b342b843247b --- /dev/null +++ b/drivers/pwm/pwm-sunplus.c @@ -0,0 +1,233 @@ +// 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/mod_devicetable.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 index 1daf591025c0..172063b51d44 100644 --- a/drivers/pwm/pwm-tegra.c +++ b/drivers/pwm/pwm-tegra.c @@ -41,13 +41,16 @@ #include <linux/io.h> #include <linux/module.h> #include <linux/of.h> -#include <linux/of_device.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 PWM_ENABLE (1 << 31) #define PWM_DUTY_WIDTH 8 #define PWM_DUTY_SHIFT 16 @@ -62,9 +65,6 @@ struct tegra_pwm_soc { }; struct tegra_pwm_chip { - struct pwm_chip chip; - struct device *dev; - struct clk *clk; struct reset_control*rst; @@ -78,25 +78,24 @@ struct tegra_pwm_chip { static inline struct tegra_pwm_chip *to_tegra_pwm_chip(struct pwm_chip *chip) { - return container_of(chip, struct tegra_pwm_chip, chip); + return pwmchip_get_drvdata(chip); } -static inline u32 pwm_readl(struct tegra_pwm_chip *chip, unsigned int num) +static inline u32 pwm_readl(struct tegra_pwm_chip *pc, unsigned int offset) { - return readl(chip->regs + (num << 4)); + return readl(pc->regs + (offset << 4)); } -static inline void pwm_writel(struct tegra_pwm_chip *chip, unsigned int num, - unsigned long val) +static inline void pwm_writel(struct tegra_pwm_chip *pc, unsigned int offset, u32 value) { - writel(val, chip->regs + (num << 4)); + writel(value, pc->regs + (offset << 4)); } 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, hz; + unsigned long long c = duty_ns; unsigned long rate, required_clk_rate; u32 val = 0; int err; @@ -142,10 +141,21 @@ static int tegra_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, * source clock rate as required_clk_rate, PWM controller will * be able to configure the requested period. */ - required_clk_rate = - (NSEC_PER_SEC / period_ns) << PWM_DUTY_WIDTH; - - err = clk_set_rate(pc->clk, required_clk_rate); + required_clk_rate = DIV_ROUND_UP_ULL((u64)NSEC_PER_SEC << PWM_DUTY_WIDTH, + 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; @@ -153,11 +163,9 @@ static int tegra_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, pc->clk_rate = clk_get_rate(pc->clk); } - rate = pc->clk_rate >> PWM_DUTY_WIDTH; - /* Consider precision in PWM_SCALE_WIDTH rate calculation */ - hz = DIV_ROUND_CLOSEST_ULL(100ULL * NSEC_PER_SEC, period_ns); - rate = DIV_ROUND_CLOSEST_ULL(100ULL * rate, hz); + rate = mul_u64_u64_div_u64(pc->clk_rate, period_ns, + (u64)NSEC_PER_SEC << PWM_DUTY_WIDTH); /* * Since the actual PWM divider is the register's frequency divider @@ -166,6 +174,8 @@ static int tegra_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, */ if (rate > 0) rate--; + else + return -EINVAL; /* * Make sure that the rate will fit in the register's frequency @@ -181,8 +191,8 @@ static int tegra_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, * before writing the register. Otherwise, keep it enabled. */ if (!pwm_is_enabled(pwm)) { - err = clk_prepare_enable(pc->clk); - if (err < 0) + err = pm_runtime_resume_and_get(pwmchip_parent(chip)); + if (err) return err; } else val |= PWM_ENABLE; @@ -193,7 +203,7 @@ static int tegra_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, * If the PWM is not enabled, turn the clock off again to save power. */ if (!pwm_is_enabled(pwm)) - clk_disable_unprepare(pc->clk); + pm_runtime_put(pwmchip_parent(chip)); return 0; } @@ -204,8 +214,8 @@ static int tegra_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) int rc = 0; u32 val; - rc = clk_prepare_enable(pc->clk); - if (rc < 0) + rc = pm_runtime_resume_and_get(pwmchip_parent(chip)); + if (rc) return rc; val = pwm_readl(pc, pwm->hwpwm); @@ -224,45 +234,79 @@ static void tegra_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) val &= ~PWM_ENABLE; pwm_writel(pc, pwm->hwpwm, val); - clk_disable_unprepare(pc->clk); + 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 = { - .config = tegra_pwm_config, - .enable = tegra_pwm_enable, - .disable = tegra_pwm_disable, - .owner = THIS_MODULE, + .apply = tegra_pwm_apply, }; static int tegra_pwm_probe(struct platform_device *pdev) { - struct tegra_pwm_chip *pwm; - struct resource *r; + struct pwm_chip *chip; + struct tegra_pwm_chip *pc; + const struct tegra_pwm_soc *soc; int ret; - pwm = devm_kzalloc(&pdev->dev, sizeof(*pwm), GFP_KERNEL); - if (!pwm) - return -ENOMEM; + soc = of_device_get_match_data(&pdev->dev); - pwm->soc = of_device_get_match_data(&pdev->dev); - pwm->dev = &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); - r = platform_get_resource(pdev, IORESOURCE_MEM, 0); - pwm->regs = devm_ioremap_resource(&pdev->dev, r); - if (IS_ERR(pwm->regs)) - return PTR_ERR(pwm->regs); + pc->soc = soc; - platform_set_drvdata(pdev, pwm); + pc->regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(pc->regs)) + return PTR_ERR(pc->regs); - pwm->clk = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(pwm->clk)) - return PTR_ERR(pwm->clk); + 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 = clk_set_rate(pwm->clk, pwm->soc->max_frequency); + ret = dev_pm_opp_set_rate(&pdev->dev, pc->soc->max_frequency); if (ret < 0) { dev_err(&pdev->dev, "Failed to set max frequency: %d\n", ret); - return ret; + goto put_pm; } /* @@ -270,78 +314,86 @@ static int tegra_pwm_probe(struct platform_device *pdev) * clock register resolutions. Get the configured frequency * so that PWM period can be calculated more accurately. */ - pwm->clk_rate = clk_get_rate(pwm->clk); + pc->clk_rate = clk_get_rate(pc->clk); /* Set minimum limit of PWM period for the IP */ - pwm->min_period_ns = - (NSEC_PER_SEC / (pwm->soc->max_frequency >> PWM_DUTY_WIDTH)) + 1; + pc->min_period_ns = + (NSEC_PER_SEC / (pc->soc->max_frequency >> PWM_DUTY_WIDTH)) + 1; - pwm->rst = devm_reset_control_get_exclusive(&pdev->dev, "pwm"); - if (IS_ERR(pwm->rst)) { - ret = PTR_ERR(pwm->rst); + 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); - return ret; + goto put_pm; } - reset_control_deassert(pwm->rst); + reset_control_deassert(pc->rst); - pwm->chip.dev = &pdev->dev; - pwm->chip.ops = &tegra_pwm_ops; - pwm->chip.base = -1; - pwm->chip.npwm = pwm->soc->num_channels; + chip->ops = &tegra_pwm_ops; - ret = pwmchip_add(&pwm->chip); + ret = pwmchip_add(chip); if (ret < 0) { dev_err(&pdev->dev, "pwmchip_add() failed: %d\n", ret); - reset_control_assert(pwm->rst); - return 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 int tegra_pwm_remove(struct platform_device *pdev) +static void tegra_pwm_remove(struct platform_device *pdev) { - struct tegra_pwm_chip *pc = platform_get_drvdata(pdev); - unsigned int i; - int err; + struct pwm_chip *chip = platform_get_drvdata(pdev); + struct tegra_pwm_chip *pc = to_tegra_pwm_chip(chip); - if (WARN_ON(!pc)) - return -ENODEV; + pwmchip_remove(chip); - err = clk_prepare_enable(pc->clk); - if (err < 0) - return err; + reset_control_assert(pc->rst); - for (i = 0; i < pc->chip.npwm; i++) { - struct pwm_device *pwm = &pc->chip.pwms[i]; + pm_runtime_force_suspend(&pdev->dev); +} - if (!pwm_is_enabled(pwm)) - if (clk_prepare_enable(pc->clk) < 0) - continue; +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; - pwm_writel(pc, i, 0); + clk_disable_unprepare(pc->clk); - clk_disable_unprepare(pc->clk); + err = pinctrl_pm_select_sleep_state(dev); + if (err) { + clk_prepare_enable(pc->clk); + return err; } - reset_control_assert(pc->rst); - clk_disable_unprepare(pc->clk); - - return pwmchip_remove(&pc->chip); + return 0; } -#ifdef CONFIG_PM_SLEEP -static int tegra_pwm_suspend(struct device *dev) +static int __maybe_unused tegra_pwm_runtime_resume(struct device *dev) { - return pinctrl_pm_select_sleep_state(dev); -} + struct pwm_chip *chip = dev_get_drvdata(dev); + struct tegra_pwm_chip *pc = to_tegra_pwm_chip(chip); + int err; -static int tegra_pwm_resume(struct device *dev) -{ - return pinctrl_pm_select_default_state(dev); + 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; } -#endif static const struct tegra_pwm_soc tegra20_pwm_soc = { .num_channels = 4, @@ -367,7 +419,10 @@ static const struct of_device_id tegra_pwm_of_match[] = { MODULE_DEVICE_TABLE(of, tegra_pwm_of_match); static const struct dev_pm_ops tegra_pwm_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(tegra_pwm_suspend, tegra_pwm_resume) + 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 = { diff --git a/drivers/pwm/pwm-tiecap.c b/drivers/pwm/pwm-tiecap.c index ab38c8203b79..67cc5e8bdb0e 100644 --- a/drivers/pwm/pwm-tiecap.c +++ b/drivers/pwm/pwm-tiecap.c @@ -2,7 +2,11 @@ /* * ECAP PWM driver * - * Copyright (C) 2012 Texas Instruments, Inc. - http://www.ti.com/ + * 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> @@ -12,7 +16,7 @@ #include <linux/clk.h> #include <linux/pm_runtime.h> #include <linux/pwm.h> -#include <linux/of_device.h> +#include <linux/of.h> /* ECAP registers and bits definitions */ #define CAP1 0x08 @@ -32,7 +36,6 @@ struct ecap_context { }; struct ecap_pwm_chip { - struct pwm_chip chip; unsigned int clk_rate; void __iomem *mmio_base; struct ecap_context ctx; @@ -40,7 +43,7 @@ struct ecap_pwm_chip { static inline struct ecap_pwm_chip *to_ecap_pwm_chip(struct pwm_chip *chip) { - return container_of(chip, struct ecap_pwm_chip, chip); + return pwmchip_get_drvdata(chip); } /* @@ -48,16 +51,13 @@ static inline struct ecap_pwm_chip *to_ecap_pwm_chip(struct pwm_chip *chip) * 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 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; - if (period_ns > NSEC_PER_SEC) - return -ERANGE; - c = pc->clk_rate; c = c * period_ns; do_div(c, NSEC_PER_SEC); @@ -73,7 +73,7 @@ static int ecap_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, duty_cycles = (u32)c; } - pm_runtime_get_sync(pc->chip.dev); + pm_runtime_get_sync(pwmchip_parent(chip)); value = readw(pc->mmio_base + ECCTL2); @@ -82,7 +82,7 @@ static int ecap_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, writew(value, pc->mmio_base + ECCTL2); - if (!pwm_is_enabled(pwm)) { + if (!enabled) { /* Update active registers if not running */ writel(duty_cycles, pc->mmio_base + CAP2); writel(period_cycles, pc->mmio_base + CAP1); @@ -96,14 +96,14 @@ static int ecap_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, writel(period_cycles, pc->mmio_base + CAP3); } - if (!pwm_is_enabled(pwm)) { + 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(pc->chip.dev); + pm_runtime_put_sync(pwmchip_parent(chip)); return 0; } @@ -114,7 +114,7 @@ static int ecap_pwm_set_polarity(struct pwm_chip *chip, struct pwm_device *pwm, struct ecap_pwm_chip *pc = to_ecap_pwm_chip(chip); u16 value; - pm_runtime_get_sync(pc->chip.dev); + pm_runtime_get_sync(pwmchip_parent(chip)); value = readw(pc->mmio_base + ECCTL2); @@ -127,7 +127,7 @@ static int ecap_pwm_set_polarity(struct pwm_chip *chip, struct pwm_device *pwm, writew(value, pc->mmio_base + ECCTL2); - pm_runtime_put_sync(pc->chip.dev); + pm_runtime_put_sync(pwmchip_parent(chip)); return 0; } @@ -138,7 +138,7 @@ static int ecap_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) u16 value; /* Leave clock enabled on enabling PWM */ - pm_runtime_get_sync(pc->chip.dev); + pm_runtime_get_sync(pwmchip_parent(chip)); /* * Enable 'Free run Time stamp counter mode' to start counter @@ -165,24 +165,49 @@ static void ecap_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) writew(value, pc->mmio_base + ECCTL2); /* Disable clock on PWM disable */ - pm_runtime_put_sync(pc->chip.dev); + pm_runtime_put_sync(pwmchip_parent(chip)); } -static void ecap_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm) +static int ecap_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, + const struct pwm_state *state) { - if (pwm_is_enabled(pwm)) { - dev_warn(chip->dev, "Removing PWM device without disabling\n"); - pm_runtime_put_sync(chip->dev); + 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 = { - .free = ecap_pwm_free, - .config = ecap_pwm_config, - .set_polarity = ecap_pwm_set_polarity, - .enable = ecap_pwm_enable, - .disable = ecap_pwm_disable, - .owner = THIS_MODULE, + .apply = ecap_pwm_apply, }; static const struct of_device_id ecap_of_match[] = { @@ -196,13 +221,14 @@ static int ecap_pwm_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; struct ecap_pwm_chip *pc; - struct resource *r; + struct pwm_chip *chip; struct clk *clk; int ret; - pc = devm_kzalloc(&pdev->dev, sizeof(*pc), GFP_KERNEL); - if (!pc) - return -ENOMEM; + 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)) { @@ -223,51 +249,44 @@ static int ecap_pwm_probe(struct platform_device *pdev) return -EINVAL; } - pc->chip.dev = &pdev->dev; - pc->chip.ops = &ecap_pwm_ops; - pc->chip.of_xlate = of_pwm_xlate_with_flags; - pc->chip.of_pwm_n_cells = 3; - pc->chip.base = -1; - pc->chip.npwm = 1; + chip->ops = &ecap_pwm_ops; - r = platform_get_resource(pdev, IORESOURCE_MEM, 0); - pc->mmio_base = devm_ioremap_resource(&pdev->dev, r); + pc->mmio_base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(pc->mmio_base)) return PTR_ERR(pc->mmio_base); - ret = pwmchip_add(&pc->chip); + 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, pc); + platform_set_drvdata(pdev, chip); pm_runtime_enable(&pdev->dev); return 0; } -static int ecap_pwm_remove(struct platform_device *pdev) +static void ecap_pwm_remove(struct platform_device *pdev) { - struct ecap_pwm_chip *pc = platform_get_drvdata(pdev); - pm_runtime_disable(&pdev->dev); - - return pwmchip_remove(&pc->chip); } -#ifdef CONFIG_PM_SLEEP -static void ecap_pwm_save_context(struct ecap_pwm_chip *pc) +static void ecap_pwm_save_context(struct pwm_chip *chip) { - pm_runtime_get_sync(pc->chip.dev); + 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(pc->chip.dev); + pm_runtime_put_sync(pwmchip_parent(chip)); } -static void ecap_pwm_restore_context(struct ecap_pwm_chip *pc) +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); @@ -275,10 +294,10 @@ static void ecap_pwm_restore_context(struct ecap_pwm_chip *pc) static int ecap_pwm_suspend(struct device *dev) { - struct ecap_pwm_chip *pc = dev_get_drvdata(dev); - struct pwm_device *pwm = pc->chip.pwms; + struct pwm_chip *chip = dev_get_drvdata(dev); + struct pwm_device *pwm = chip->pwms; - ecap_pwm_save_context(pc); + ecap_pwm_save_context(chip); /* Disable explicitly if PWM is running */ if (pwm_is_enabled(pwm)) @@ -289,25 +308,24 @@ static int ecap_pwm_suspend(struct device *dev) static int ecap_pwm_resume(struct device *dev) { - struct ecap_pwm_chip *pc = dev_get_drvdata(dev); - struct pwm_device *pwm = pc->chip.pwms; + 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(pc); + ecap_pwm_restore_context(chip); return 0; } -#endif -static SIMPLE_DEV_PM_OPS(ecap_pwm_pm_ops, ecap_pwm_suspend, ecap_pwm_resume); +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 = &ecap_pwm_pm_ops, + .pm = pm_ptr(&ecap_pwm_pm_ops), }, .probe = ecap_pwm_probe, .remove = ecap_pwm_remove, diff --git a/drivers/pwm/pwm-tiehrpwm.c b/drivers/pwm/pwm-tiehrpwm.c index 7b4c770ce9d6..7a86cb090f76 100644 --- a/drivers/pwm/pwm-tiehrpwm.c +++ b/drivers/pwm/pwm-tiehrpwm.c @@ -2,7 +2,7 @@ /* * EHRPWM PWM driver * - * Copyright (C) 2012 Texas Instruments, Inc. - http://www.ti.com/ + * Copyright (C) 2012 Texas Instruments, Inc. - https://www.ti.com/ */ #include <linux/module.h> @@ -12,7 +12,7 @@ #include <linux/err.h> #include <linux/clk.h> #include <linux/pm_runtime.h> -#include <linux/of_device.h> +#include <linux/of.h> /* EHRPWM registers and bits definitions */ @@ -36,7 +36,7 @@ #define CLKDIV_MAX 7 #define HSPCLKDIV_MAX 7 -#define PERIOD_MAX 0xFFFF +#define PERIOD_MAX 0x10000 /* compare module registers */ #define CMPA 0x12 @@ -65,14 +65,10 @@ #define AQCTL_ZRO_FRCHIGH BIT(1) #define AQCTL_ZRO_FRCTOGGLE (BIT(1) | BIT(0)) -#define AQCTL_CHANA_POLNORMAL (AQCTL_CAU_FRCLOW | AQCTL_PRD_FRCHIGH | \ - AQCTL_ZRO_FRCHIGH) -#define AQCTL_CHANA_POLINVERSED (AQCTL_CAU_FRCHIGH | AQCTL_PRD_FRCLOW | \ - AQCTL_ZRO_FRCLOW) -#define AQCTL_CHANB_POLNORMAL (AQCTL_CBU_FRCLOW | AQCTL_PRD_FRCHIGH | \ - AQCTL_ZRO_FRCHIGH) -#define AQCTL_CHANB_POLINVERSED (AQCTL_CBU_FRCHIGH | AQCTL_PRD_FRCLOW | \ - AQCTL_ZRO_FRCLOW) +#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 @@ -105,18 +101,16 @@ struct ehrpwm_context { }; struct ehrpwm_pwm_chip { - struct pwm_chip chip; unsigned long clk_rate; void __iomem *mmio_base; unsigned long period_cycles[NUM_PWM_CHANNEL]; - enum pwm_polarity polarity[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 container_of(chip, struct ehrpwm_pwm_chip, chip); + return pwmchip_get_drvdata(chip); } static inline u16 ehrpwm_read(void __iomem *base, unsigned int offset) @@ -167,7 +161,7 @@ static int set_prescale_div(unsigned long rqst_prescaler, u16 *prescale_div, *prescale_div = (1 << clkdiv) * (hspclkdiv ? (hspclkdiv * 2) : 1); - if (*prescale_div > rqst_prescaler) { + if (*prescale_div >= rqst_prescaler) { *tb_clk_div = (clkdiv << TBCTL_CLKDIV_SHIFT) | (hspclkdiv << TBCTL_HSPCLKDIV_SHIFT); return 0; @@ -178,51 +172,20 @@ static int set_prescale_div(unsigned long rqst_prescaler, u16 *prescale_div, return 1; } -static void configure_polarity(struct ehrpwm_pwm_chip *pc, int chan) -{ - u16 aqctl_val, aqctl_mask; - unsigned int aqctl_reg; - - /* - * Configure PWM output to HIGH/LOW level on counter - * reaches compare register value and LOW/HIGH level - * on counter value reaches period register value and - * zero value on counter - */ - if (chan == 1) { - aqctl_reg = AQCTLB; - aqctl_mask = AQCTL_CBU_MASK; - - if (pc->polarity[chan] == PWM_POLARITY_INVERSED) - aqctl_val = AQCTL_CHANB_POLINVERSED; - else - aqctl_val = AQCTL_CHANB_POLNORMAL; - } else { - aqctl_reg = AQCTLA; - aqctl_mask = AQCTL_CAU_MASK; - - if (pc->polarity[chan] == PWM_POLARITY_INVERSED) - aqctl_val = AQCTL_CHANA_POLINVERSED; - else - aqctl_val = AQCTL_CHANA_POLNORMAL; - } - - aqctl_mask |= AQCTL_PRD_MASK | AQCTL_ZRO_MASK; - ehrpwm_modify(pc->mmio_base, aqctl_reg, aqctl_mask, aqctl_val); -} - /* * 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, - int duty_ns, int period_ns) + 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; @@ -232,15 +195,10 @@ static int ehrpwm_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, do_div(c, NSEC_PER_SEC); period_cycles = (unsigned long)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 = (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 @@ -256,7 +214,7 @@ static int ehrpwm_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, if (i == pwm->hwpwm) continue; - dev_err(chip->dev, + dev_err(pwmchip_parent(chip), "period value conflicts with channel %u\n", i); return -EINVAL; @@ -266,52 +224,73 @@ static int ehrpwm_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, pc->period_cycles[pwm->hwpwm] = period_cycles; /* Configure clock prescaler to support Low frequency PWM wave */ - if (set_prescale_div(period_cycles/PERIOD_MAX, &ps_divval, + if (set_prescale_div(DIV_ROUND_UP(period_cycles, PERIOD_MAX), &ps_divval, &tb_divval)) { - dev_err(chip->dev, "Unsupported values\n"); + dev_err(pwmchip_parent(chip), "Unsupported values\n"); return -EINVAL; } - pm_runtime_get_sync(chip->dev); - - /* Update clock prescaler values */ - ehrpwm_modify(pc->mmio_base, TBCTL, TBCTL_CLKDIV_MASK, tb_divval); - /* Update period & duty cycle with presacler division */ period_cycles = period_cycles / ps_divval; duty_cycles = duty_cycles / ps_divval; - /* Configure shadow loading on Period register */ - ehrpwm_modify(pc->mmio_base, TBCTL, TBCTL_PRDLD_MASK, TBCTL_PRDLD_SHDW); + if (period_cycles < 1) + period_cycles = 1; - ehrpwm_write(pc->mmio_base, TBPRD, period_cycles); + pm_runtime_get_sync(pwmchip_parent(chip)); - /* Configure ehrpwm counter for up-count mode */ - ehrpwm_modify(pc->mmio_base, TBCTL, TBCTL_CTRMODE_MASK, - TBCTL_CTRMODE_UP); + /* Update clock prescaler values */ + ehrpwm_modify(pc->mmio_base, TBCTL, TBCTL_CLKDIV_MASK, tb_divval); - if (pwm->hwpwm == 1) + if (pwm->hwpwm == 1) { /* Channel 1 configured with compare B register */ cmp_reg = CMPB; - else + + 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; - ehrpwm_write(pc->mmio_base, cmp_reg, duty_cycles); + aqctl_reg = AQCTLA; + aqctl_mask = AQCTL_CAU_MASK; - pm_runtime_put_sync(chip->dev); + if (polarity == PWM_POLARITY_INVERSED) + aqctl_val = AQCTL_CHANA_POLINVERSED; + else + aqctl_val = AQCTL_CHANA_POLNORMAL; - return 0; -} + /* if duty_cycle is big, don't toggle on CAU */ + if (duty_cycles > period_cycles) + aqctl_val &= ~AQCTL_CAU_MASK; + } -static int ehrpwm_pwm_set_polarity(struct pwm_chip *chip, - struct pwm_device *pwm, - enum pwm_polarity polarity) -{ - struct ehrpwm_pwm_chip *pc = to_ehrpwm_pwm_chip(chip); + 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); - /* Configuration of polarity in hardware delayed, do at enable */ - pc->polarity[pwm->hwpwm] = polarity; + pm_runtime_put_sync(pwmchip_parent(chip)); return 0; } @@ -323,7 +302,7 @@ static int ehrpwm_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) int ret; /* Leave clock enabled on enabling PWM */ - pm_runtime_get_sync(chip->dev); + pm_runtime_get_sync(pwmchip_parent(chip)); /* Disabling Action Qualifier on PWM output */ if (pwm->hwpwm) { @@ -340,14 +319,11 @@ static int ehrpwm_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) ehrpwm_modify(pc->mmio_base, AQCSFRC, aqcsfrc_mask, aqcsfrc_val); - /* Channels polarity can be configured from action qualifier module */ - configure_polarity(pc, pwm->hwpwm); - /* Enable TBCLK */ ret = clk_enable(pc->tbclk); if (ret) { - dev_err(chip->dev, "Failed to enable TBCLK for %s: %d\n", - dev_name(pc->chip.dev), ret); + dev_err(pwmchip_parent(chip), "Failed to enable TBCLK for %s: %d\n", + dev_name(pwmchip_parent(chip)), ret); return ret; } @@ -385,29 +361,49 @@ static void ehrpwm_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) clk_disable(pc->tbclk); /* Disable clock on PWM disable */ - pm_runtime_put_sync(chip->dev); + 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); - if (pwm_is_enabled(pwm)) { - dev_warn(chip->dev, "Removing PWM device without disabling\n"); - pm_runtime_put_sync(chip->dev); + /* 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; + + if (state->polarity != pwm->state.polarity) { + if (enabled) { + ehrpwm_pwm_disable(chip, pwm); + enabled = false; + } } - /* set period value to zero on free */ - pc->period_cycles[pwm->hwpwm] = 0; + 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, - .config = ehrpwm_pwm_config, - .set_polarity = ehrpwm_pwm_set_polarity, - .enable = ehrpwm_pwm_enable, - .disable = ehrpwm_pwm_disable, - .owner = THIS_MODULE, + .apply = ehrpwm_pwm_apply, }; static const struct of_device_id ehrpwm_of_match[] = { @@ -421,13 +417,14 @@ static int ehrpwm_pwm_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; struct ehrpwm_pwm_chip *pc; - struct resource *r; + struct pwm_chip *chip; struct clk *clk; int ret; - pc = devm_kzalloc(&pdev->dev, sizeof(*pc), GFP_KERNEL); - if (!pc) - return -ENOMEM; + 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)) { @@ -437,10 +434,8 @@ static int ehrpwm_pwm_probe(struct platform_device *pdev) } } - if (IS_ERR(clk)) { - dev_err(&pdev->dev, "failed to get clock\n"); - return PTR_ERR(clk); - } + 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) { @@ -448,24 +443,16 @@ static int ehrpwm_pwm_probe(struct platform_device *pdev) return -EINVAL; } - pc->chip.dev = &pdev->dev; - pc->chip.ops = &ehrpwm_pwm_ops; - pc->chip.of_xlate = of_pwm_xlate_with_flags; - pc->chip.of_pwm_n_cells = 3; - pc->chip.base = -1; - pc->chip.npwm = NUM_PWM_CHANNEL; + chip->ops = &ehrpwm_pwm_ops; - r = platform_get_resource(pdev, IORESOURCE_MEM, 0); - pc->mmio_base = devm_ioremap_resource(&pdev->dev, r); + 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)) { - dev_err(&pdev->dev, "Failed to get tbclk\n"); - return PTR_ERR(pc->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) { @@ -473,13 +460,13 @@ static int ehrpwm_pwm_probe(struct platform_device *pdev) return ret; } - ret = pwmchip_add(&pc->chip); + 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, pc); + platform_set_drvdata(pdev, chip); pm_runtime_enable(&pdev->dev); return 0; @@ -490,21 +477,23 @@ err_clk_unprepare: return ret; } -static int ehrpwm_pwm_remove(struct platform_device *pdev) +static void ehrpwm_pwm_remove(struct platform_device *pdev) { - struct ehrpwm_pwm_chip *pc = platform_get_drvdata(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); - - return pwmchip_remove(&pc->chip); } -#ifdef CONFIG_PM_SLEEP -static void ehrpwm_pwm_save_context(struct ehrpwm_pwm_chip *pc) +static void ehrpwm_pwm_save_context(struct pwm_chip *chip) { - pm_runtime_get_sync(pc->chip.dev); + 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); @@ -515,11 +504,13 @@ static void ehrpwm_pwm_save_context(struct ehrpwm_pwm_chip *pc) pc->ctx.aqsfrc = ehrpwm_read(pc->mmio_base, AQSFRC); pc->ctx.aqcsfrc = ehrpwm_read(pc->mmio_base, AQCSFRC); - pm_runtime_put_sync(pc->chip.dev); + pm_runtime_put_sync(pwmchip_parent(chip)); } -static void ehrpwm_pwm_restore_context(struct ehrpwm_pwm_chip *pc) +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); @@ -532,13 +523,13 @@ static void ehrpwm_pwm_restore_context(struct ehrpwm_pwm_chip *pc) static int ehrpwm_pwm_suspend(struct device *dev) { - struct ehrpwm_pwm_chip *pc = dev_get_drvdata(dev); + struct pwm_chip *chip = dev_get_drvdata(dev); unsigned int i; - ehrpwm_pwm_save_context(pc); + ehrpwm_pwm_save_context(chip); - for (i = 0; i < pc->chip.npwm; i++) { - struct pwm_device *pwm = &pc->chip.pwms[i]; + for (i = 0; i < chip->npwm; i++) { + struct pwm_device *pwm = &chip->pwms[i]; if (!pwm_is_enabled(pwm)) continue; @@ -552,11 +543,11 @@ static int ehrpwm_pwm_suspend(struct device *dev) static int ehrpwm_pwm_resume(struct device *dev) { - struct ehrpwm_pwm_chip *pc = dev_get_drvdata(dev); + struct pwm_chip *chip = dev_get_drvdata(dev); unsigned int i; - for (i = 0; i < pc->chip.npwm; i++) { - struct pwm_device *pwm = &pc->chip.pwms[i]; + for (i = 0; i < chip->npwm; i++) { + struct pwm_device *pwm = &chip->pwms[i]; if (!pwm_is_enabled(pwm)) continue; @@ -565,20 +556,19 @@ static int ehrpwm_pwm_resume(struct device *dev) pm_runtime_get_sync(dev); } - ehrpwm_pwm_restore_context(pc); + ehrpwm_pwm_restore_context(chip); return 0; } -#endif -static SIMPLE_DEV_PM_OPS(ehrpwm_pwm_pm_ops, ehrpwm_pwm_suspend, - ehrpwm_pwm_resume); +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 = &ehrpwm_pwm_pm_ops, + .pm = pm_ptr(&ehrpwm_pwm_pm_ops), }, .probe = ehrpwm_pwm_probe, .remove = ehrpwm_pwm_remove, diff --git a/drivers/pwm/pwm-twl-led.c b/drivers/pwm/pwm-twl-led.c index 630b9a578820..a555cc3be4b3 100644 --- a/drivers/pwm/pwm-twl-led.c +++ b/drivers/pwm/pwm-twl-led.c @@ -7,6 +7,22 @@ * * 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> @@ -45,14 +61,9 @@ #define TWL6040_LED_MODE_OFF 0x02 #define TWL6040_LED_MODE_MASK 0x03 -struct twl_pwmled_chip { - struct pwm_chip chip; - struct mutex mutex; -}; - static inline struct twl_pwmled_chip *to_twl(struct pwm_chip *chip) { - return container_of(chip, struct twl_pwmled_chip, chip); + return pwmchip_get_drvdata(chip); } static int twl4030_pwmled_config(struct pwm_chip *chip, struct pwm_device *pwm, @@ -84,59 +95,88 @@ static int twl4030_pwmled_config(struct pwm_chip *chip, struct pwm_device *pwm, ret = twl_i2c_write(TWL4030_MODULE_LED, pwm_config, base, 2); if (ret < 0) - dev_err(chip->dev, "%s: Failed to configure PWM\n", pwm->label); + 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) { - struct twl_pwmled_chip *twl = to_twl(chip); int ret; u8 val; - mutex_lock(&twl->mutex); ret = twl_i2c_read_u8(TWL4030_MODULE_LED, &val, TWL4030_LEDEN_REG); if (ret < 0) { - dev_err(chip->dev, "%s: Failed to read LEDEN\n", pwm->label); - goto out; + 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(chip->dev, "%s: Failed to enable PWM\n", pwm->label); + dev_err(pwmchip_parent(chip), "%s: Failed to enable PWM\n", pwm->label); -out: - mutex_unlock(&twl->mutex); return ret; } static void twl4030_pwmled_disable(struct pwm_chip *chip, struct pwm_device *pwm) { - struct twl_pwmled_chip *twl = to_twl(chip); int ret; u8 val; - mutex_lock(&twl->mutex); ret = twl_i2c_read_u8(TWL4030_MODULE_LED, &val, TWL4030_LEDEN_REG); if (ret < 0) { - dev_err(chip->dev, "%s: Failed to read LEDEN\n", pwm->label); - goto out; + 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(chip->dev, "%s: Failed to disable PWM\n", pwm->label); + 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); -out: - mutex_unlock(&twl->mutex); + 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) { @@ -149,23 +189,21 @@ static int twl6030_pwmled_config(struct pwm_chip *chip, struct pwm_device *pwm, ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, on_time, TWL6030_LED_PWM_CTRL1); if (ret < 0) - dev_err(chip->dev, "%s: Failed to configure PWM\n", pwm->label); + 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) { - struct twl_pwmled_chip *twl = to_twl(chip); int ret; u8 val; - mutex_lock(&twl->mutex); ret = twl_i2c_read_u8(TWL6030_MODULE_ID1, &val, TWL6030_LED_PWM_CTRL2); if (ret < 0) { - dev_err(chip->dev, "%s: Failed to read PWM_CTRL2\n", + dev_err(pwmchip_parent(chip), "%s: Failed to read PWM_CTRL2\n", pwm->label); - goto out; + return ret; } val &= ~TWL6040_LED_MODE_MASK; @@ -173,26 +211,22 @@ static int twl6030_pwmled_enable(struct pwm_chip *chip, struct pwm_device *pwm) ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, val, TWL6030_LED_PWM_CTRL2); if (ret < 0) - dev_err(chip->dev, "%s: Failed to enable PWM\n", pwm->label); + dev_err(pwmchip_parent(chip), "%s: Failed to enable PWM\n", pwm->label); -out: - mutex_unlock(&twl->mutex); return ret; } static void twl6030_pwmled_disable(struct pwm_chip *chip, struct pwm_device *pwm) { - struct twl_pwmled_chip *twl = to_twl(chip); int ret; u8 val; - mutex_lock(&twl->mutex); ret = twl_i2c_read_u8(TWL6030_MODULE_ID1, &val, TWL6030_LED_PWM_CTRL2); if (ret < 0) { - dev_err(chip->dev, "%s: Failed to read PWM_CTRL2\n", + dev_err(pwmchip_parent(chip), "%s: Failed to read PWM_CTRL2\n", pwm->label); - goto out; + return; } val &= ~TWL6040_LED_MODE_MASK; @@ -200,24 +234,45 @@ static void twl6030_pwmled_disable(struct pwm_chip *chip, ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, val, TWL6030_LED_PWM_CTRL2); if (ret < 0) - dev_err(chip->dev, "%s: Failed to disable PWM\n", pwm->label); + 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); -out: - mutex_unlock(&twl->mutex); + return err; } static int twl6030_pwmled_request(struct pwm_chip *chip, struct pwm_device *pwm) { - struct twl_pwmled_chip *twl = to_twl(chip); int ret; u8 val; - mutex_lock(&twl->mutex); ret = twl_i2c_read_u8(TWL6030_MODULE_ID1, &val, TWL6030_LED_PWM_CTRL2); if (ret < 0) { - dev_err(chip->dev, "%s: Failed to read PWM_CTRL2\n", + dev_err(pwmchip_parent(chip), "%s: Failed to read PWM_CTRL2\n", pwm->label); - goto out; + return ret; } val &= ~TWL6040_LED_MODE_MASK; @@ -225,25 +280,21 @@ static int twl6030_pwmled_request(struct pwm_chip *chip, struct pwm_device *pwm) ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, val, TWL6030_LED_PWM_CTRL2); if (ret < 0) - dev_err(chip->dev, "%s: Failed to request PWM\n", pwm->label); + dev_err(pwmchip_parent(chip), "%s: Failed to request PWM\n", pwm->label); -out: - mutex_unlock(&twl->mutex); return ret; } static void twl6030_pwmled_free(struct pwm_chip *chip, struct pwm_device *pwm) { - struct twl_pwmled_chip *twl = to_twl(chip); int ret; u8 val; - mutex_lock(&twl->mutex); ret = twl_i2c_read_u8(TWL6030_MODULE_ID1, &val, TWL6030_LED_PWM_CTRL2); if (ret < 0) { - dev_err(chip->dev, "%s: Failed to read PWM_CTRL2\n", + dev_err(pwmchip_parent(chip), "%s: Failed to read PWM_CTRL2\n", pwm->label); - goto out; + return; } val &= ~TWL6040_LED_MODE_MASK; @@ -251,64 +302,36 @@ static void twl6030_pwmled_free(struct pwm_chip *chip, struct pwm_device *pwm) ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, val, TWL6030_LED_PWM_CTRL2); if (ret < 0) - dev_err(chip->dev, "%s: Failed to free PWM\n", pwm->label); - -out: - mutex_unlock(&twl->mutex); + dev_err(pwmchip_parent(chip), "%s: Failed to free PWM\n", pwm->label); } -static const struct pwm_ops twl4030_pwmled_ops = { - .enable = twl4030_pwmled_enable, - .disable = twl4030_pwmled_disable, - .config = twl4030_pwmled_config, - .owner = THIS_MODULE, -}; - static const struct pwm_ops twl6030_pwmled_ops = { - .enable = twl6030_pwmled_enable, - .disable = twl6030_pwmled_disable, - .config = twl6030_pwmled_config, + .apply = twl6030_pwmled_apply, .request = twl6030_pwmled_request, .free = twl6030_pwmled_free, - .owner = THIS_MODULE, }; static int twl_pwmled_probe(struct platform_device *pdev) { - struct twl_pwmled_chip *twl; - int ret; - - twl = devm_kzalloc(&pdev->dev, sizeof(*twl), GFP_KERNEL); - if (!twl) - return -ENOMEM; + struct pwm_chip *chip; + unsigned int npwm; + const struct pwm_ops *ops; if (twl_class_is_4030()) { - twl->chip.ops = &twl4030_pwmled_ops; - twl->chip.npwm = 2; + ops = &twl4030_pwmled_ops; + npwm = 2; } else { - twl->chip.ops = &twl6030_pwmled_ops; - twl->chip.npwm = 1; + ops = &twl6030_pwmled_ops; + npwm = 1; } - twl->chip.dev = &pdev->dev; - twl->chip.base = -1; - - mutex_init(&twl->mutex); - - ret = pwmchip_add(&twl->chip); - if (ret < 0) - return ret; - - platform_set_drvdata(pdev, twl); + chip = devm_pwmchip_alloc(&pdev->dev, npwm, 0); + if (IS_ERR(chip)) + return PTR_ERR(chip); - return 0; -} - -static int twl_pwmled_remove(struct platform_device *pdev) -{ - struct twl_pwmled_chip *twl = platform_get_drvdata(pdev); + chip->ops = ops; - return pwmchip_remove(&twl->chip); + return devm_pwmchip_add(&pdev->dev, chip); } #ifdef CONFIG_OF @@ -326,7 +349,6 @@ static struct platform_driver twl_pwmled_driver = { .of_match_table = of_match_ptr(twl_pwmled_of_match), }, .probe = twl_pwmled_probe, - .remove = twl_pwmled_remove, }; module_platform_driver(twl_pwmled_driver); diff --git a/drivers/pwm/pwm-twl.c b/drivers/pwm/pwm-twl.c index aee67974f353..8f981ffff4b4 100644 --- a/drivers/pwm/pwm-twl.c +++ b/drivers/pwm/pwm-twl.c @@ -46,7 +46,6 @@ #define TWL6030_PWM_TOGGLE(pwm, x) ((x) << (pwm * 3)) struct twl_pwm_chip { - struct pwm_chip chip; struct mutex mutex; u8 twl6030_toggle3; u8 twl4030_pwm_mux; @@ -54,13 +53,13 @@ struct twl_pwm_chip { static inline struct twl_pwm_chip *to_twl(struct pwm_chip *chip) { - return container_of(chip, struct twl_pwm_chip, chip); + return pwmchip_get_drvdata(chip); } static int twl_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, - int duty_ns, int period_ns) + u64 duty_ns, u64 period_ns) { - int duty_cycle = DIV_ROUND_UP(duty_ns * TWL_PWM_MAX, period_ns) + 1; + int duty_cycle = DIV64_U64_ROUND_UP(duty_ns * TWL_PWM_MAX, period_ns) + 1; u8 pwm_config[2] = { 1, 0 }; int base, ret; @@ -86,7 +85,7 @@ static int twl_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, ret = twl_i2c_write(TWL_MODULE_PWM, pwm_config, base, 2); if (ret < 0) - dev_err(chip->dev, "%s: Failed to configure PWM\n", pwm->label); + dev_err(pwmchip_parent(chip), "%s: Failed to configure PWM\n", pwm->label); return ret; } @@ -100,7 +99,7 @@ static int twl4030_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) mutex_lock(&twl->mutex); ret = twl_i2c_read_u8(TWL4030_MODULE_INTBR, &val, TWL4030_GPBR1_REG); if (ret < 0) { - dev_err(chip->dev, "%s: Failed to read GPBR1\n", pwm->label); + dev_err(pwmchip_parent(chip), "%s: Failed to read GPBR1\n", pwm->label); goto out; } @@ -108,13 +107,13 @@ static int twl4030_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) ret = twl_i2c_write_u8(TWL4030_MODULE_INTBR, val, TWL4030_GPBR1_REG); if (ret < 0) - dev_err(chip->dev, "%s: Failed to enable PWM\n", pwm->label); + 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(chip->dev, "%s: Failed to enable PWM\n", pwm->label); + dev_err(pwmchip_parent(chip), "%s: Failed to enable PWM\n", pwm->label); out: mutex_unlock(&twl->mutex); @@ -130,7 +129,7 @@ static void twl4030_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) mutex_lock(&twl->mutex); ret = twl_i2c_read_u8(TWL4030_MODULE_INTBR, &val, TWL4030_GPBR1_REG); if (ret < 0) { - dev_err(chip->dev, "%s: Failed to read GPBR1\n", pwm->label); + dev_err(pwmchip_parent(chip), "%s: Failed to read GPBR1\n", pwm->label); goto out; } @@ -138,13 +137,13 @@ static void twl4030_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) ret = twl_i2c_write_u8(TWL4030_MODULE_INTBR, val, TWL4030_GPBR1_REG); if (ret < 0) - dev_err(chip->dev, "%s: Failed to disable PWM\n", pwm->label); + 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(chip->dev, "%s: Failed to disable PWM\n", pwm->label); + dev_err(pwmchip_parent(chip), "%s: Failed to disable PWM\n", pwm->label); out: mutex_unlock(&twl->mutex); @@ -167,7 +166,7 @@ static int twl4030_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm) mutex_lock(&twl->mutex); ret = twl_i2c_read_u8(TWL4030_MODULE_INTBR, &val, TWL4030_PMBR1_REG); if (ret < 0) { - dev_err(chip->dev, "%s: Failed to read PMBR1\n", pwm->label); + dev_err(pwmchip_parent(chip), "%s: Failed to read PMBR1\n", pwm->label); goto out; } @@ -181,7 +180,7 @@ static int twl4030_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm) ret = twl_i2c_write_u8(TWL4030_MODULE_INTBR, val, TWL4030_PMBR1_REG); if (ret < 0) - dev_err(chip->dev, "%s: Failed to request PWM\n", pwm->label); + dev_err(pwmchip_parent(chip), "%s: Failed to request PWM\n", pwm->label); out: mutex_unlock(&twl->mutex); @@ -202,7 +201,7 @@ static void twl4030_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm) mutex_lock(&twl->mutex); ret = twl_i2c_read_u8(TWL4030_MODULE_INTBR, &val, TWL4030_PMBR1_REG); if (ret < 0) { - dev_err(chip->dev, "%s: Failed to read PMBR1\n", pwm->label); + dev_err(pwmchip_parent(chip), "%s: Failed to read PMBR1\n", pwm->label); goto out; } @@ -212,7 +211,7 @@ static void twl4030_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm) ret = twl_i2c_write_u8(TWL4030_MODULE_INTBR, val, TWL4030_PMBR1_REG); if (ret < 0) - dev_err(chip->dev, "%s: Failed to free PWM\n", pwm->label); + dev_err(pwmchip_parent(chip), "%s: Failed to free PWM\n", pwm->label); out: mutex_unlock(&twl->mutex); @@ -231,7 +230,7 @@ static int twl6030_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, val, TWL6030_TOGGLE3_REG); if (ret < 0) { - dev_err(chip->dev, "%s: Failed to enable PWM\n", pwm->label); + dev_err(pwmchip_parent(chip), "%s: Failed to enable PWM\n", pwm->label); goto out; } @@ -254,7 +253,7 @@ static void twl6030_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, val, TWL6030_TOGGLE3_REG); if (ret < 0) { - dev_err(chip->dev, "%s: Failed to disable PWM\n", pwm->label); + dev_err(pwmchip_parent(chip), "%s: Failed to disable PWM\n", pwm->label); goto out; } @@ -262,7 +261,7 @@ static void twl6030_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, val, TWL6030_TOGGLE3_REG); if (ret < 0) { - dev_err(chip->dev, "%s: Failed to disable PWM\n", pwm->label); + dev_err(pwmchip_parent(chip), "%s: Failed to disable PWM\n", pwm->label); goto out; } @@ -270,7 +269,7 @@ static void twl6030_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, val, TWL6030_TOGGLE3_REG); if (ret < 0) { - dev_err(chip->dev, "%s: Failed to disable PWM\n", pwm->label); + dev_err(pwmchip_parent(chip), "%s: Failed to disable PWM\n", pwm->label); goto out; } @@ -279,56 +278,84 @@ 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 = { - .config = twl_pwm_config, - .enable = twl4030_pwm_enable, - .disable = twl4030_pwm_disable, + .apply = twl4030_pwm_apply, .request = twl4030_pwm_request, .free = twl4030_pwm_free, - .owner = THIS_MODULE, }; static const struct pwm_ops twl6030_pwm_ops = { - .config = twl_pwm_config, - .enable = twl6030_pwm_enable, - .disable = twl6030_pwm_disable, - .owner = THIS_MODULE, + .apply = twl6030_pwm_apply, }; static int twl_pwm_probe(struct platform_device *pdev) { + struct pwm_chip *chip; struct twl_pwm_chip *twl; - int ret; - twl = devm_kzalloc(&pdev->dev, sizeof(*twl), GFP_KERNEL); - if (!twl) - return -ENOMEM; + 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()) - twl->chip.ops = &twl4030_pwm_ops; + chip->ops = &twl4030_pwm_ops; else - twl->chip.ops = &twl6030_pwm_ops; - - twl->chip.dev = &pdev->dev; - twl->chip.base = -1; - twl->chip.npwm = 2; + chip->ops = &twl6030_pwm_ops; mutex_init(&twl->mutex); - ret = pwmchip_add(&twl->chip); - if (ret < 0) - return ret; - - platform_set_drvdata(pdev, twl); - - return 0; -} - -static int twl_pwm_remove(struct platform_device *pdev) -{ - struct twl_pwm_chip *twl = platform_get_drvdata(pdev); - - return pwmchip_remove(&twl->chip); + return devm_pwmchip_add(&pdev->dev, chip); } #ifdef CONFIG_OF @@ -346,7 +373,6 @@ static struct platform_driver twl_pwm_driver = { .of_match_table = of_match_ptr(twl_pwm_of_match), }, .probe = twl_pwm_probe, - .remove = twl_pwm_remove, }; module_platform_driver(twl_pwm_driver); diff --git a/drivers/pwm/pwm-visconti.c b/drivers/pwm/pwm-visconti.c new file mode 100644 index 000000000000..28fae4979e3f --- /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 index 11d45e56a923..016c82d65527 100644 --- a/drivers/pwm/pwm-vt8500.c +++ b/drivers/pwm/pwm-vt8500.c @@ -6,6 +6,7 @@ * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com> */ +#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/platform_device.h> @@ -18,10 +19,6 @@ #include <asm/div64.h> -#include <linux/of.h> -#include <linux/of_device.h> -#include <linux/of_address.h> - /* * SoC architecture allocates register space for 4 PWMs but only * 2 are currently implemented. @@ -48,16 +45,19 @@ #define STATUS_ALL_UPDATE 0x0F struct vt8500_chip { - struct pwm_chip chip; void __iomem *base; struct clk *clk; }; -#define to_vt8500_chip(chip) container_of(chip, struct vt8500_chip, chip) +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 pwm_busy_wait(struct vt8500_chip *vt8500, int nr, u8 bitmask) +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); @@ -65,12 +65,12 @@ static inline void pwm_busy_wait(struct vt8500_chip *vt8500, int nr, u8 bitmask) cpu_relax(); if (unlikely(!loops)) - dev_warn(vt8500->chip.dev, "Waiting for status bits 0x%x to clear timed out\n", + 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, - int duty_ns, int period_ns) + u64 duty_ns, u64 period_ns) { struct vt8500_chip *vt8500 = to_vt8500_chip(chip); unsigned long long c; @@ -80,7 +80,7 @@ static int vt8500_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, err = clk_enable(vt8500->clk); if (err < 0) { - dev_err(chip->dev, "failed to enable clock\n"); + dev_err(pwmchip_parent(chip), "failed to enable clock\n"); return err; } @@ -102,22 +102,22 @@ static int vt8500_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, } c = (unsigned long long)pv * duty_ns; - do_div(c, period_ns); - dc = c; + + dc = div64_u64(c, period_ns); writel(prescale, vt8500->base + REG_SCALAR(pwm->hwpwm)); - pwm_busy_wait(vt8500, pwm->hwpwm, STATUS_SCALAR_UPDATE); + vt8500_pwm_busy_wait(chip, pwm->hwpwm, STATUS_SCALAR_UPDATE); writel(pv, vt8500->base + REG_PERIOD(pwm->hwpwm)); - pwm_busy_wait(vt8500, pwm->hwpwm, STATUS_PERIOD_UPDATE); + vt8500_pwm_busy_wait(chip, pwm->hwpwm, STATUS_PERIOD_UPDATE); writel(dc, vt8500->base + REG_DUTY(pwm->hwpwm)); - pwm_busy_wait(vt8500, pwm->hwpwm, STATUS_DUTY_UPDATE); + 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)); - pwm_busy_wait(vt8500, pwm->hwpwm, STATUS_CTRL_UPDATE); + vt8500_pwm_busy_wait(chip, pwm->hwpwm, STATUS_CTRL_UPDATE); clk_disable(vt8500->clk); return 0; @@ -131,14 +131,14 @@ static int vt8500_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) err = clk_enable(vt8500->clk); if (err < 0) { - dev_err(chip->dev, "failed to enable clock\n"); + 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)); - pwm_busy_wait(vt8500, pwm->hwpwm, STATUS_CTRL_UPDATE); + vt8500_pwm_busy_wait(chip, pwm->hwpwm, STATUS_CTRL_UPDATE); return 0; } @@ -151,7 +151,7 @@ static void vt8500_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) val = readl(vt8500->base + REG_CTRL(pwm->hwpwm)); val &= ~CTRL_ENABLE; writel(val, vt8500->base + REG_CTRL(pwm->hwpwm)); - pwm_busy_wait(vt8500, pwm->hwpwm, STATUS_CTRL_UPDATE); + vt8500_pwm_busy_wait(chip, pwm->hwpwm, STATUS_CTRL_UPDATE); clk_disable(vt8500->clk); } @@ -171,17 +171,59 @@ static int vt8500_pwm_set_polarity(struct pwm_chip *chip, val &= ~CTRL_INVERT; writel(val, vt8500->base + REG_CTRL(pwm->hwpwm)); - pwm_busy_wait(vt8500, pwm->hwpwm, STATUS_CTRL_UPDATE); + 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 = { - .enable = vt8500_pwm_enable, - .disable = vt8500_pwm_disable, - .config = vt8500_pwm_config, - .set_polarity = vt8500_pwm_set_polarity, - .owner = THIS_MODULE, + .apply = vt8500_pwm_apply, }; static const struct of_device_id vt8500_pwm_dt_ids[] = { @@ -192,71 +234,38 @@ MODULE_DEVICE_TABLE(of, vt8500_pwm_dt_ids); static int vt8500_pwm_probe(struct platform_device *pdev) { - struct vt8500_chip *chip; - struct resource *r; + struct pwm_chip *chip; + struct vt8500_chip *vt8500; struct device_node *np = pdev->dev.of_node; int ret; - if (!np) { - dev_err(&pdev->dev, "invalid devicetree node\n"); - return -EINVAL; - } - - chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL); - if (chip == NULL) - return -ENOMEM; - - chip->chip.dev = &pdev->dev; - chip->chip.ops = &vt8500_pwm_ops; - chip->chip.of_xlate = of_pwm_xlate_with_flags; - chip->chip.of_pwm_n_cells = 3; - chip->chip.base = -1; - chip->chip.npwm = VT8500_NR_PWMS; - - chip->clk = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(chip->clk)) { - dev_err(&pdev->dev, "clock source not specified\n"); - return PTR_ERR(chip->clk); - } - - r = platform_get_resource(pdev, IORESOURCE_MEM, 0); - chip->base = devm_ioremap_resource(&pdev->dev, r); - if (IS_ERR(chip->base)) - return PTR_ERR(chip->base); + if (!np) + return dev_err_probe(&pdev->dev, -EINVAL, "invalid devicetree node\n"); - ret = clk_prepare(chip->clk); - if (ret < 0) { - dev_err(&pdev->dev, "failed to prepare clock\n"); - return ret; - } + chip = devm_pwmchip_alloc(&pdev->dev, VT8500_NR_PWMS, sizeof(*vt8500)); + if (IS_ERR(chip)) + return PTR_ERR(chip); + vt8500 = to_vt8500_chip(chip); - ret = pwmchip_add(&chip->chip); - if (ret < 0) { - dev_err(&pdev->dev, "failed to add PWM chip\n"); - clk_unprepare(chip->clk); - return ret; - } + chip->ops = &vt8500_pwm_ops; - platform_set_drvdata(pdev, chip); - return ret; -} + 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"); -static int vt8500_pwm_remove(struct platform_device *pdev) -{ - struct vt8500_chip *chip; - - chip = platform_get_drvdata(pdev); - if (chip == NULL) - return -ENODEV; + vt8500->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(vt8500->base)) + return PTR_ERR(vt8500->base); - clk_unprepare(chip->clk); + ret = devm_pwmchip_add(&pdev->dev, chip); + if (ret < 0) + return dev_err_probe(&pdev->dev, ret, "failed to add PWM chip\n"); - return pwmchip_remove(&chip->chip); + return 0; } static struct platform_driver vt8500_pwm_driver = { .probe = vt8500_pwm_probe, - .remove = vt8500_pwm_remove, .driver = { .name = "vt8500-pwm", .of_match_table = vt8500_pwm_dt_ids, diff --git a/drivers/pwm/pwm-xilinx.c b/drivers/pwm/pwm-xilinx.c new file mode 100644 index 000000000000..52c241982807 --- /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-zx.c b/drivers/pwm/pwm-zx.c deleted file mode 100644 index e2c21cc34a96..000000000000 --- a/drivers/pwm/pwm-zx.c +++ /dev/null @@ -1,279 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (C) 2017 Sanechips Technology Co., Ltd. - * Copyright 2017 Linaro Ltd. - */ - -#include <linux/clk.h> -#include <linux/err.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 ZX_PWM_MODE 0x0 -#define ZX_PWM_CLKDIV_SHIFT 2 -#define ZX_PWM_CLKDIV_MASK GENMASK(11, 2) -#define ZX_PWM_CLKDIV(x) (((x) << ZX_PWM_CLKDIV_SHIFT) & \ - ZX_PWM_CLKDIV_MASK) -#define ZX_PWM_POLAR BIT(1) -#define ZX_PWM_EN BIT(0) -#define ZX_PWM_PERIOD 0x4 -#define ZX_PWM_DUTY 0x8 - -#define ZX_PWM_CLKDIV_MAX 1023 -#define ZX_PWM_PERIOD_MAX 65535 - -struct zx_pwm_chip { - struct pwm_chip chip; - struct clk *pclk; - struct clk *wclk; - void __iomem *base; -}; - -static inline struct zx_pwm_chip *to_zx_pwm_chip(struct pwm_chip *chip) -{ - return container_of(chip, struct zx_pwm_chip, chip); -} - -static inline u32 zx_pwm_readl(struct zx_pwm_chip *zpc, unsigned int hwpwm, - unsigned int offset) -{ - return readl(zpc->base + (hwpwm + 1) * 0x10 + offset); -} - -static inline void zx_pwm_writel(struct zx_pwm_chip *zpc, unsigned int hwpwm, - unsigned int offset, u32 value) -{ - writel(value, zpc->base + (hwpwm + 1) * 0x10 + offset); -} - -static void zx_pwm_set_mask(struct zx_pwm_chip *zpc, unsigned int hwpwm, - unsigned int offset, u32 mask, u32 value) -{ - u32 data; - - data = zx_pwm_readl(zpc, hwpwm, offset); - data &= ~mask; - data |= value & mask; - zx_pwm_writel(zpc, hwpwm, offset, data); -} - -static void zx_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm, - struct pwm_state *state) -{ - struct zx_pwm_chip *zpc = to_zx_pwm_chip(chip); - unsigned long rate; - unsigned int div; - u32 value; - u64 tmp; - - value = zx_pwm_readl(zpc, pwm->hwpwm, ZX_PWM_MODE); - - if (value & ZX_PWM_POLAR) - state->polarity = PWM_POLARITY_NORMAL; - else - state->polarity = PWM_POLARITY_INVERSED; - - if (value & ZX_PWM_EN) - state->enabled = true; - else - state->enabled = false; - - div = (value & ZX_PWM_CLKDIV_MASK) >> ZX_PWM_CLKDIV_SHIFT; - rate = clk_get_rate(zpc->wclk); - - tmp = zx_pwm_readl(zpc, pwm->hwpwm, ZX_PWM_PERIOD); - tmp *= div * NSEC_PER_SEC; - state->period = DIV_ROUND_CLOSEST_ULL(tmp, rate); - - tmp = zx_pwm_readl(zpc, pwm->hwpwm, ZX_PWM_DUTY); - tmp *= div * NSEC_PER_SEC; - state->duty_cycle = DIV_ROUND_CLOSEST_ULL(tmp, rate); -} - -static int zx_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, - unsigned int duty_ns, unsigned int period_ns) -{ - struct zx_pwm_chip *zpc = to_zx_pwm_chip(chip); - unsigned int period_cycles, duty_cycles; - unsigned long long c; - unsigned int div = 1; - unsigned long rate; - - /* Find out the best divider */ - rate = clk_get_rate(zpc->wclk); - - while (1) { - c = rate / div; - c = c * period_ns; - do_div(c, NSEC_PER_SEC); - - if (c < ZX_PWM_PERIOD_MAX) - break; - - div++; - - if (div > ZX_PWM_CLKDIV_MAX) - return -ERANGE; - } - - /* Calculate duty cycles */ - period_cycles = c; - c *= duty_ns; - do_div(c, period_ns); - duty_cycles = c; - - /* - * If the PWM is being enabled, we have to temporarily disable it - * before configuring the registers. - */ - if (pwm_is_enabled(pwm)) - zx_pwm_set_mask(zpc, pwm->hwpwm, ZX_PWM_MODE, ZX_PWM_EN, 0); - - /* Set up registers */ - zx_pwm_set_mask(zpc, pwm->hwpwm, ZX_PWM_MODE, ZX_PWM_CLKDIV_MASK, - ZX_PWM_CLKDIV(div)); - zx_pwm_writel(zpc, pwm->hwpwm, ZX_PWM_PERIOD, period_cycles); - zx_pwm_writel(zpc, pwm->hwpwm, ZX_PWM_DUTY, duty_cycles); - - /* Re-enable the PWM if needed */ - if (pwm_is_enabled(pwm)) - zx_pwm_set_mask(zpc, pwm->hwpwm, ZX_PWM_MODE, - ZX_PWM_EN, ZX_PWM_EN); - - return 0; -} - -static int zx_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, - const struct pwm_state *state) -{ - struct zx_pwm_chip *zpc = to_zx_pwm_chip(chip); - struct pwm_state cstate; - int ret; - - pwm_get_state(pwm, &cstate); - - if (state->polarity != cstate.polarity) - zx_pwm_set_mask(zpc, pwm->hwpwm, ZX_PWM_MODE, ZX_PWM_POLAR, - (state->polarity == PWM_POLARITY_INVERSED) ? - 0 : ZX_PWM_POLAR); - - if (state->period != cstate.period || - state->duty_cycle != cstate.duty_cycle) { - ret = zx_pwm_config(chip, pwm, state->duty_cycle, - state->period); - if (ret) - return ret; - } - - if (state->enabled != cstate.enabled) { - if (state->enabled) { - ret = clk_prepare_enable(zpc->wclk); - if (ret) - return ret; - - zx_pwm_set_mask(zpc, pwm->hwpwm, ZX_PWM_MODE, - ZX_PWM_EN, ZX_PWM_EN); - } else { - zx_pwm_set_mask(zpc, pwm->hwpwm, ZX_PWM_MODE, - ZX_PWM_EN, 0); - clk_disable_unprepare(zpc->wclk); - } - } - - return 0; -} - -static const struct pwm_ops zx_pwm_ops = { - .apply = zx_pwm_apply, - .get_state = zx_pwm_get_state, - .owner = THIS_MODULE, -}; - -static int zx_pwm_probe(struct platform_device *pdev) -{ - struct zx_pwm_chip *zpc; - struct resource *res; - unsigned int i; - int ret; - - zpc = devm_kzalloc(&pdev->dev, sizeof(*zpc), GFP_KERNEL); - if (!zpc) - return -ENOMEM; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - zpc->base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(zpc->base)) - return PTR_ERR(zpc->base); - - zpc->pclk = devm_clk_get(&pdev->dev, "pclk"); - if (IS_ERR(zpc->pclk)) - return PTR_ERR(zpc->pclk); - - zpc->wclk = devm_clk_get(&pdev->dev, "wclk"); - if (IS_ERR(zpc->wclk)) - return PTR_ERR(zpc->wclk); - - ret = clk_prepare_enable(zpc->pclk); - if (ret) - return ret; - - zpc->chip.dev = &pdev->dev; - zpc->chip.ops = &zx_pwm_ops; - zpc->chip.base = -1; - zpc->chip.npwm = 4; - zpc->chip.of_xlate = of_pwm_xlate_with_flags; - zpc->chip.of_pwm_n_cells = 3; - - /* - * PWM devices may be enabled by firmware, and let's disable all of - * them initially to save power. - */ - for (i = 0; i < zpc->chip.npwm; i++) - zx_pwm_set_mask(zpc, i, ZX_PWM_MODE, ZX_PWM_EN, 0); - - ret = pwmchip_add(&zpc->chip); - if (ret < 0) { - dev_err(&pdev->dev, "failed to add PWM chip: %d\n", ret); - return ret; - } - - platform_set_drvdata(pdev, zpc); - - return 0; -} - -static int zx_pwm_remove(struct platform_device *pdev) -{ - struct zx_pwm_chip *zpc = platform_get_drvdata(pdev); - int ret; - - ret = pwmchip_remove(&zpc->chip); - clk_disable_unprepare(zpc->pclk); - - return ret; -} - -static const struct of_device_id zx_pwm_dt_ids[] = { - { .compatible = "zte,zx296718-pwm", }, - { /* sentinel */ } -}; -MODULE_DEVICE_TABLE(of, zx_pwm_dt_ids); - -static struct platform_driver zx_pwm_driver = { - .driver = { - .name = "zx-pwm", - .of_match_table = zx_pwm_dt_ids, - }, - .probe = zx_pwm_probe, - .remove = zx_pwm_remove, -}; -module_platform_driver(zx_pwm_driver); - -MODULE_ALIAS("platform:zx-pwm"); -MODULE_AUTHOR("Shawn Guo <shawn.guo@linaro.org>"); -MODULE_DESCRIPTION("ZTE ZX PWM Driver"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/pwm/pwm_th1520.rs b/drivers/pwm/pwm_th1520.rs new file mode 100644 index 000000000000..e3b7e77356fc --- /dev/null +++ b/drivers/pwm/pwm_th1520.rs @@ -0,0 +1,387 @@ +// 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 core::ops::Deref; +use kernel::{ + c_str, + clk::Clk, + device::{Bound, Core, Device}, + devres, + io::mem::IoMem, + 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; + + (match ns.checked_mul(rate_hz) { + Some(product) => product, + None => u64::MAX, + }) / NSEC_PER_SEC_U64 +} + +fn cycles_to_ns(cycles: u64, rate_hz: u64) -> u64 { + const NSEC_PER_SEC_U64: u64 = time::NSEC_PER_SEC as u64; + + // TODO: Replace with a kernel helper like `mul_u64_u64_div_u64_roundup` + // once available in Rust. + let numerator = cycles + .saturating_mul(NSEC_PER_SEC_U64) + .saturating_add(rate_hz - 1); + + numerator / 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: devres::Devres<IoMem<TH1520_PWM_REG_SIZE>>, + clk: Clk, +} + +// This `unsafe` implementation is a temporary necessity because the underlying `kernel::clk::Clk` +// type does not yet expose `Send` and `Sync` implementations. This block should be removed +// as soon as the clock abstraction provides these guarantees directly. +// TODO: Remove those unsafe impl's when Clk will support them itself. + +// SAFETY: The `devres` framework requires the driver's private data to be `Send` and `Sync`. +// We can guarantee this because the PWM core synchronizes all callbacks, preventing concurrent +// access to the contained `iomem` and `clk` resources. +unsafe impl Send for Th1520PwmDriverData {} + +// SAFETY: The same reasoning applies as for `Send`. The PWM core's synchronization +// guarantees that it is safe for multiple threads to have shared access (`&self`) +// to the driver data during callbacks. +unsafe impl Sync for Th1520PwmDriverData {} + +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(u64::from(wfhw.period_cycles), rate_hz); + + let duty_cycles = u64::from(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 = u64::from(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 iomem_accessor = data.iomem.access(parent_dev)?; + let iomap = iomem_accessor.deref(); + + 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 iomem_accessor = data.iomem.access(parent_dev)?; + let iomap = iomem_accessor.deref(); + 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, + MODULE_OF_TABLE, + <Th1520PwmPlatformDriver as platform::Driver>::IdInfo, + [(of::DeviceId::new(c_str!("thead,th1520-pwm")), ())] +); + +impl platform::Driver for Th1520PwmPlatformDriver { + type IdInfo = (); + const OF_ID_TABLE: Option<of::IdTable<Self::IdInfo>> = Some(&OF_TABLE); + + fn probe( + pdev: &platform::Device<Core>, + _id_info: Option<&Self::IdInfo>, + ) -> impl PinInit<Self, Error> { + 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>(), + clk <- clk, + }), + )?; + + pwm::Registration::register(dev, chip)?; + + 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", +} diff --git a/drivers/pwm/sysfs.c b/drivers/pwm/sysfs.c deleted file mode 100644 index 2389b8669846..000000000000 --- a/drivers/pwm/sysfs.c +++ /dev/null @@ -1,529 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * A simple sysfs interface for the generic PWM framework - * - * Copyright (C) 2013 H Hartley Sweeten <hsweeten@visionengravers.com> - * - * Based on previous work by Lars Poeschel <poeschel@lemonage.de> - */ - -#include <linux/device.h> -#include <linux/mutex.h> -#include <linux/err.h> -#include <linux/slab.h> -#include <linux/kdev_t.h> -#include <linux/pwm.h> - -struct pwm_export { - struct device child; - struct pwm_device *pwm; - struct mutex lock; - struct pwm_state suspend; -}; - -static struct pwm_export *child_to_pwm_export(struct device *child) -{ - return container_of(child, struct pwm_export, child); -} - -static struct pwm_device *child_to_pwm_device(struct device *child) -{ - struct pwm_export *export = child_to_pwm_export(child); - - return export->pwm; -} - -static ssize_t period_show(struct device *child, - struct device_attribute *attr, - char *buf) -{ - const struct pwm_device *pwm = child_to_pwm_device(child); - struct pwm_state state; - - pwm_get_state(pwm, &state); - - return sprintf(buf, "%u\n", state.period); -} - -static ssize_t period_store(struct device *child, - struct device_attribute *attr, - const char *buf, size_t size) -{ - struct pwm_export *export = child_to_pwm_export(child); - struct pwm_device *pwm = export->pwm; - struct pwm_state state; - unsigned int val; - int ret; - - ret = kstrtouint(buf, 0, &val); - if (ret) - return ret; - - mutex_lock(&export->lock); - pwm_get_state(pwm, &state); - state.period = val; - ret = pwm_apply_state(pwm, &state); - mutex_unlock(&export->lock); - - return ret ? : size; -} - -static ssize_t duty_cycle_show(struct device *child, - struct device_attribute *attr, - char *buf) -{ - const struct pwm_device *pwm = child_to_pwm_device(child); - struct pwm_state state; - - pwm_get_state(pwm, &state); - - return sprintf(buf, "%u\n", state.duty_cycle); -} - -static ssize_t duty_cycle_store(struct device *child, - struct device_attribute *attr, - const char *buf, size_t size) -{ - struct pwm_export *export = child_to_pwm_export(child); - struct pwm_device *pwm = export->pwm; - struct pwm_state state; - unsigned int val; - int ret; - - ret = kstrtouint(buf, 0, &val); - if (ret) - return ret; - - mutex_lock(&export->lock); - pwm_get_state(pwm, &state); - state.duty_cycle = val; - ret = pwm_apply_state(pwm, &state); - mutex_unlock(&export->lock); - - return ret ? : size; -} - -static ssize_t enable_show(struct device *child, - struct device_attribute *attr, - char *buf) -{ - const struct pwm_device *pwm = child_to_pwm_device(child); - struct pwm_state state; - - pwm_get_state(pwm, &state); - - return sprintf(buf, "%d\n", state.enabled); -} - -static ssize_t enable_store(struct device *child, - struct device_attribute *attr, - const char *buf, size_t size) -{ - struct pwm_export *export = child_to_pwm_export(child); - struct pwm_device *pwm = export->pwm; - struct pwm_state state; - int val, ret; - - ret = kstrtoint(buf, 0, &val); - if (ret) - return ret; - - mutex_lock(&export->lock); - - pwm_get_state(pwm, &state); - - switch (val) { - case 0: - state.enabled = false; - break; - case 1: - state.enabled = true; - break; - default: - ret = -EINVAL; - goto unlock; - } - - ret = pwm_apply_state(pwm, &state); - -unlock: - mutex_unlock(&export->lock); - return ret ? : size; -} - -static ssize_t polarity_show(struct device *child, - struct device_attribute *attr, - char *buf) -{ - const struct pwm_device *pwm = child_to_pwm_device(child); - 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 sprintf(buf, "%s\n", polarity); -} - -static ssize_t polarity_store(struct device *child, - struct device_attribute *attr, - const char *buf, size_t size) -{ - struct pwm_export *export = child_to_pwm_export(child); - 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; - - mutex_lock(&export->lock); - pwm_get_state(pwm, &state); - state.polarity = polarity; - ret = pwm_apply_state(pwm, &state); - mutex_unlock(&export->lock); - - return ret ? : size; -} - -static ssize_t capture_show(struct device *child, - struct device_attribute *attr, - char *buf) -{ - struct pwm_device *pwm = child_to_pwm_device(child); - struct pwm_capture result; - int ret; - - ret = pwm_capture(pwm, &result, jiffies_to_msecs(HZ)); - if (ret) - return ret; - - return sprintf(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 *child) -{ - struct pwm_export *export = child_to_pwm_export(child); - - kfree(export); -} - -static int pwm_export_child(struct device *parent, 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(sizeof(*export), GFP_KERNEL); - if (!export) { - clear_bit(PWMF_EXPORTED, &pwm->flags); - return -ENOMEM; - } - - export->pwm = pwm; - mutex_init(&export->lock); - - export->child.release = pwm_export_release; - export->child.parent = parent; - export->child.devt = MKDEV(0, 0); - export->child.groups = pwm_groups; - dev_set_name(&export->child, "pwm%u", pwm->hwpwm); - - ret = device_register(&export->child); - if (ret) { - clear_bit(PWMF_EXPORTED, &pwm->flags); - put_device(&export->child); - export = NULL; - return ret; - } - pwm_prop[0] = kasprintf(GFP_KERNEL, "EXPORT=pwm%u", pwm->hwpwm); - pwm_prop[1] = NULL; - kobject_uevent_env(&parent->kobj, KOBJ_CHANGE, pwm_prop); - kfree(pwm_prop[0]); - - return 0; -} - -static int pwm_unexport_match(struct device *child, void *data) -{ - return child_to_pwm_device(child) == data; -} - -static int pwm_unexport_child(struct device *parent, struct pwm_device *pwm) -{ - struct device *child; - char *pwm_prop[2]; - - if (!test_and_clear_bit(PWMF_EXPORTED, &pwm->flags)) - return -ENODEV; - - child = device_find_child(parent, pwm, pwm_unexport_match); - if (!child) - return -ENODEV; - - pwm_prop[0] = kasprintf(GFP_KERNEL, "UNEXPORT=pwm%u", pwm->hwpwm); - pwm_prop[1] = NULL; - kobject_uevent_env(&parent->kobj, KOBJ_CHANGE, pwm_prop); - kfree(pwm_prop[0]); - - /* for device_find_child() */ - put_device(child); - device_unregister(child); - pwm_put(pwm); - - return 0; -} - -static ssize_t export_store(struct device *parent, - struct device_attribute *attr, - const char *buf, size_t len) -{ - struct pwm_chip *chip = dev_get_drvdata(parent); - 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(parent, pwm); - if (ret < 0) - pwm_put(pwm); - - return ret ? : len; -} -static DEVICE_ATTR_WO(export); - -static ssize_t unexport_store(struct device *parent, - struct device_attribute *attr, - const char *buf, size_t len) -{ - struct pwm_chip *chip = dev_get_drvdata(parent); - 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(parent, &chip->pwms[hwpwm]); - - return ret ? : len; -} -static DEVICE_ATTR_WO(unexport); - -static ssize_t npwm_show(struct device *parent, struct device_attribute *attr, - char *buf) -{ - const struct pwm_chip *chip = dev_get_drvdata(parent); - - return sprintf(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 *parent, - struct pwm_device *pwm, - struct pwm_state *state) -{ - struct device *child; - struct pwm_export *export; - - if (!test_bit(PWMF_EXPORTED, &pwm->flags)) - return NULL; - - child = device_find_child(parent, pwm, pwm_unexport_match); - if (!child) - return NULL; - - export = child_to_pwm_export(child); - put_device(child); /* 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_state(pwm, state); - - /* release lock taken in pwm_class_get_state */ - mutex_unlock(&export->lock); - - return ret; -} - -static int pwm_class_resume_npwm(struct device *parent, unsigned int npwm) -{ - struct pwm_chip *chip = dev_get_drvdata(parent); - 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(parent, pwm, &state); - if (!export) - continue; - - state.enabled = export->suspend.enabled; - ret = pwm_class_apply_state(export, pwm, &state); - if (ret < 0) - break; - } - - return ret; -} - -static int __maybe_unused pwm_class_suspend(struct device *parent) -{ - struct pwm_chip *chip = dev_get_drvdata(parent); - 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(parent, pwm, &state); - if (!export) - continue; - - export->suspend = state; - 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(parent, i); - break; - } - } - - return ret; -} - -static int __maybe_unused pwm_class_resume(struct device *parent) -{ - struct pwm_chip *chip = dev_get_drvdata(parent); - - return pwm_class_resume_npwm(parent, chip->npwm); -} - -static SIMPLE_DEV_PM_OPS(pwm_class_pm_ops, pwm_class_suspend, pwm_class_resume); - -static struct class pwm_class = { - .name = "pwm", - .owner = THIS_MODULE, - .dev_groups = pwm_chip_groups, - .pm = &pwm_class_pm_ops, -}; - -static int pwmchip_sysfs_match(struct device *parent, const void *data) -{ - return dev_get_drvdata(parent) == data; -} - -void pwmchip_sysfs_export(struct pwm_chip *chip) -{ - struct device *parent; - - /* - * If device_create() fails the pwm_chip is still usable by - * the kernel it's just not exported. - */ - parent = device_create(&pwm_class, chip->dev, MKDEV(0, 0), chip, - "pwmchip%d", chip->base); - if (IS_ERR(parent)) { - dev_warn(chip->dev, - "device_create failed for pwm_chip sysfs export\n"); - } -} - -void pwmchip_sysfs_unexport(struct pwm_chip *chip) -{ - struct device *parent; - unsigned int i; - - parent = class_find_device(&pwm_class, NULL, chip, - pwmchip_sysfs_match); - if (!parent) - return; - - for (i = 0; i < chip->npwm; i++) { - struct pwm_device *pwm = &chip->pwms[i]; - - if (test_bit(PWMF_EXPORTED, &pwm->flags)) - pwm_unexport_child(parent, pwm); - } - - put_device(parent); - device_unregister(parent); -} - -static int __init pwm_sysfs_init(void) -{ - return class_register(&pwm_class); -} -subsys_initcall(pwm_sysfs_init); |
