diff options
author | Maarten Lankhorst <maarten.lankhorst@linux.intel.com> | 2021-01-07 16:11:28 +0300 |
---|---|---|
committer | Maarten Lankhorst <maarten.lankhorst@linux.intel.com> | 2021-01-07 16:11:28 +0300 |
commit | 2313f4700327fb7d6aa3989edfa5bffcd7080d36 (patch) | |
tree | 4781ddf9b844024cede6c8e99d3d509192d977c9 /drivers/pwm | |
parent | 70612d0e121e55ea3c057c526bf7374da41aa2f0 (diff) | |
parent | cb3cfbf79aff7decb4e5ee69a7c74864497f61dc (diff) | |
download | linux-2313f4700327fb7d6aa3989edfa5bffcd7080d36.tar.xz |
Merge drm/drm-next into drm-misc-next
Staying in sync to drm-next, and to be able to pull ttm fixes.
Signed-off-by: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>
Diffstat (limited to 'drivers/pwm')
47 files changed, 1181 insertions, 371 deletions
diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index 63be5362fd3a..0937e1c047ac 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig @@ -53,8 +53,8 @@ config PWM_AB8500 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 +75,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. @@ -88,7 +89,7 @@ config PWM_ATMEL_TCB 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 @@ -111,6 +112,7 @@ config PWM_BCM_KONA config PWM_BCM2835 tristate "BCM2835 PWM support" depends on ARCH_BCM2835 || ARCH_BRCMSTB || COMPILE_TEST + depends on HAS_IOMEM help PWM framework driver for BCM2835 controller (Raspberry Pi) @@ -120,6 +122,7 @@ config 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 +132,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). @@ -160,9 +164,19 @@ config PWM_CROS_EC PWM driver for exposing a PWM attached to the ChromeOS Embedded Controller. +config PWM_DWC + tristate "DesignWare PWM Controller" + depends on PCI + 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. @@ -184,6 +198,7 @@ config PWM_FSL_FTM 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 +221,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 +231,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 +249,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 @@ -254,6 +282,15 @@ 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 || (ARM64 && COMPILE_TEST) + 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_LP3943 tristate "TI/National Semiconductor LP3943 PWM support" depends on MFD_LP3943 @@ -267,6 +304,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 +317,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 +326,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 +342,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. @@ -312,7 +354,7 @@ config PWM_LPSS_PLATFORM 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. @@ -333,6 +375,7 @@ config PWM_MTK_DISP 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. @@ -341,8 +384,8 @@ config 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. @@ -373,6 +416,7 @@ config PWM_PCA9685 config PWM_PXA tristate "PXA PWM support" depends on ARCH_PXA || COMPILE_TEST + depends on HAS_IOMEM help Generic PWM framework driver for PXA. @@ -404,6 +448,7 @@ 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. @@ -411,6 +456,7 @@ config PWM_ROCKCHIP config PWM_SAMSUNG tristate "Samsung PWM support" depends on PLAT_SAMSUNG || ARCH_S5PV210 || ARCH_EXYNOS || COMPILE_TEST + depends on HAS_IOMEM help Generic PWM framework driver for Samsung. @@ -420,7 +466,7 @@ config PWM_SAMSUNG config PWM_SIFIVE tristate "SiFive PWM support" depends on OF - depends on COMMON_CLK + depends on COMMON_CLK && HAS_IOMEM depends on RISCV || COMPILE_TEST help Generic PWM framework driver for SiFive SoCs. @@ -441,7 +487,7 @@ config PWM_SL28CPLD 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. @@ -463,7 +509,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. @@ -509,6 +555,7 @@ config PWM_SUN4I 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. @@ -519,6 +566,7 @@ config PWM_TEGRA 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 @@ -528,6 +576,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 @@ -555,6 +604,7 @@ config PWM_TWL_LED config PWM_VT8500 tristate "vt8500 PWM support" depends on ARCH_VT8500 || COMPILE_TEST + depends on HAS_IOMEM help Generic PWM framework driver for vt8500. @@ -564,6 +614,7 @@ config PWM_VT8500 config PWM_ZX tristate "ZTE ZX PWM support" depends on ARCH_ZX || COMPILE_TEST + depends on HAS_IOMEM help Generic PWM framework driver for ZTE ZX family SoCs. diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile index cbdcd55d69ee..18b89d7fd092 100644 --- a/drivers/pwm/Makefile +++ b/drivers/pwm/Makefile @@ -13,6 +13,7 @@ obj-$(CONFIG_PWM_BRCMSTB) += pwm-brcmstb.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) += pwm-dwc.o obj-$(CONFIG_PWM_EP93XX) += pwm-ep93xx.o obj-$(CONFIG_PWM_FSL_FTM) += pwm-fsl-ftm.o obj-$(CONFIG_PWM_HIBVT) += pwm-hibvt.o @@ -20,8 +21,10 @@ 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_LP3943) += pwm-lp3943.o obj-$(CONFIG_PWM_LPC18XX_SCT) += pwm-lpc18xx-sct.o obj-$(CONFIG_PWM_LPC32XX) += pwm-lpc32xx.o diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c index 1f16f5365d3c..a8eff4b3ee36 100644 --- a/drivers/pwm/core.c +++ b/drivers/pwm/core.c @@ -1338,7 +1338,7 @@ DEFINE_SEQ_ATTRIBUTE(pwm_debugfs); static int __init pwm_debugfs_init(void) { - debugfs_create_file("pwm", S_IFREG | S_IRUGO, NULL, NULL, + debugfs_create_file("pwm", S_IFREG | 0444, NULL, NULL, &pwm_debugfs_fops); return 0; diff --git a/drivers/pwm/pwm-ab8500.c b/drivers/pwm/pwm-ab8500.c index fdf3964db4a6..58c6c0f5b0ec 100644 --- a/drivers/pwm/pwm-ab8500.c +++ b/drivers/pwm/pwm-ab8500.c @@ -101,12 +101,12 @@ static int ab8500_pwm_probe(struct platform_device *pdev) ab8500->chip.dev = &pdev->dev; ab8500->chip.ops = &ab8500_pwm_ops; - ab8500->chip.base = pdev->id; + ab8500->chip.base = -1; ab8500->chip.npwm = 1; err = pwmchip_add(&ab8500->chip); if (err < 0) - return err; + return dev_err_probe(&pdev->dev, err, "Failed to add pwm chip\n"); dev_dbg(&pdev->dev, "pwm probe successful\n"); platform_set_drvdata(pdev, ab8500); diff --git a/drivers/pwm/pwm-atmel-tcb.c b/drivers/pwm/pwm-atmel-tcb.c index 85c53701958c..5ccc3e7420e9 100644 --- a/drivers/pwm/pwm-atmel-tcb.c +++ b/drivers/pwm/pwm-atmel-tcb.c @@ -16,13 +16,16 @@ #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_irq.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) @@ -48,11 +51,18 @@ struct atmel_tcb_channel { struct atmel_tcb_pwm_chip { struct pwm_chip chip; spinlock_t lock; - struct atmel_tc *tc; + 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[NPWM / 2]; + struct atmel_tcb_channel bkup; }; +const u8 atmel_tcb_divisors[] = { 2, 8, 32, 128, 0, }; + static inline struct atmel_tcb_pwm_chip *to_tcb_chip(struct pwm_chip *chip) { return container_of(chip, struct atmel_tcb_pwm_chip, chip); @@ -74,10 +84,6 @@ static int atmel_tcb_pwm_request(struct pwm_chip *chip, { 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; unsigned cmr; int ret; @@ -85,7 +91,7 @@ static int atmel_tcb_pwm_request(struct pwm_chip *chip, if (!tcbpwm) return -ENOMEM; - ret = clk_prepare_enable(tc->clk[group]); + ret = clk_prepare_enable(tcbpwmc->clk); if (ret) { devm_kfree(chip->dev, tcbpwm); return ret; @@ -98,28 +104,31 @@ static int atmel_tcb_pwm_request(struct pwm_chip *chip, tcbpwm->div = 0; spin_lock(&tcbpwmc->lock); - cmr = __raw_readl(regs + ATMEL_TC_REG(group, CMR)); + 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)); + regmap_write(tcbpwmc->regmap, ATMEL_TC_REG(tcbpwmc->channel, CMR), cmr); spin_unlock(&tcbpwmc->lock); tcbpwmc->pwms[pwm->hwpwm] = tcbpwm; @@ -131,9 +140,8 @@ 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]); + clk_disable_unprepare(tcbpwmc->clk); tcbpwmc->pwms[pwm->hwpwm] = NULL; devm_kfree(chip->dev, tcbpwm); } @@ -142,10 +150,6 @@ static void atmel_tcb_pwm_disable(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; - void __iomem *regs = tc->regs; - unsigned group = pwm->hwpwm / 2; - unsigned index = pwm->hwpwm % 2; unsigned cmr; enum pwm_polarity polarity = tcbpwm->polarity; @@ -161,10 +165,10 @@ static void atmel_tcb_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) 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,20 +182,22 @@ 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); @@ -201,10 +207,6 @@ static int atmel_tcb_pwm_enable(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; - void __iomem *regs = tc->regs; - unsigned group = pwm->hwpwm / 2; - unsigned index = pwm->hwpwm % 2; u32 cmr; enum pwm_polarity polarity = tcbpwm->polarity; @@ -220,12 +222,12 @@ static int atmel_tcb_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) 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 +250,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,19 +265,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; + regmap_write(tcbpwmc->regmap, ATMEL_TC_REG(tcbpwmc->channel, CCR), + ATMEL_TC_SWTRG | ATMEL_TC_CLKEN); + tcbpwmc->bkup.enabled = 1; spin_unlock(&tcbpwmc->lock); return 0; } @@ -285,29 +292,29 @@ static int atmel_tcb_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, { struct atmel_tcb_pwm_chip *tcbpwmc = to_tcb_chip(chip); struct atmel_tcb_pwm_device *tcbpwm = 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; + 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 actuallly 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 +323,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,17 +337,13 @@ 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]; + if (pwm->hwpwm == 0) + atcbpwm = tcbpwmc->pwms[1]; else - atcbpwm = tcbpwmc->pwms[pwm->hwpwm - 1]; + atcbpwm = tcbpwmc->pwms[0]; /* - * 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. * @@ -376,32 +379,75 @@ static const struct pwm_ops atmel_tcb_pwm_ops = { .owner = THIS_MODULE, }; +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) { + const struct of_device_id *match; struct atmel_tcb_pwm_chip *tcbpwm; + const struct atmel_tcb_config *config; struct device_node *np = pdev->dev.of_node; - struct atmel_tc *tc; + struct regmap *regmap; + struct clk *clk, *gclk = NULL; + struct clk *slow_clk; + char clk_name[] = "t0_clk"; int err; - int tcblock; + int channel; - 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; + regmap = syscon_node_to_regmap(np->parent); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + slow_clk = of_clk_get_by_name(np->parent, "slow_clk"); + if (IS_ERR(slow_clk)) + return PTR_ERR(slow_clk); + + clk_name[1] += channel; + clk = of_clk_get_by_name(np->parent, clk_name); + if (IS_ERR(clk)) + clk = of_clk_get_by_name(np->parent, "t0_clk"); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + match = of_match_node(atmel_tcb_of_match, np->parent); + config = match->data; + + if (config->has_gclk) { + gclk = of_clk_get_by_name(np->parent, "gclk"); + if (IS_ERR(gclk)) + return PTR_ERR(gclk); } tcbpwm = devm_kzalloc(&pdev->dev, sizeof(*tcbpwm), GFP_KERNEL); if (tcbpwm == NULL) { err = -ENOMEM; - goto err_free_tc; + goto err_slow_clk; } tcbpwm->chip.dev = &pdev->dev; @@ -410,11 +456,16 @@ static int atmel_tcb_pwm_probe(struct platform_device *pdev) tcbpwm->chip.of_pwm_n_cells = 3; tcbpwm->chip.base = -1; tcbpwm->chip.npwm = NPWM; - tcbpwm->tc = tc; - - err = clk_prepare_enable(tc->slow_clk); + tcbpwm->channel = channel; + tcbpwm->regmap = regmap; + tcbpwm->clk = clk; + tcbpwm->gclk = gclk; + tcbpwm->slow_clk = slow_clk; + tcbpwm->width = config->counter_width; + + err = clk_prepare_enable(slow_clk); if (err) - goto err_free_tc; + goto err_slow_clk; spin_lock_init(&tcbpwm->lock); @@ -427,10 +478,10 @@ static int atmel_tcb_pwm_probe(struct platform_device *pdev) return 0; err_disable_clk: - clk_disable_unprepare(tcbpwm->tc->slow_clk); + clk_disable_unprepare(tcbpwm->slow_clk); -err_free_tc: - atmel_tc_free(tc); +err_slow_clk: + clk_put(slow_clk); return err; } @@ -440,14 +491,14 @@ static int 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); + clk_disable_unprepare(tcbpwm->slow_clk); + clk_put(tcbpwm->slow_clk); + clk_put(tcbpwm->clk); err = pwmchip_remove(&tcbpwm->chip); if (err < 0) return err; - atmel_tc_free(tcbpwm->tc); - return 0; } @@ -461,38 +512,33 @@ MODULE_DEVICE_TABLE(of, atmel_tcb_pwm_dt_ids); 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 atmel_tcb_channel *chan = &tcbpwm->bkup; + unsigned int channel = tcbpwm->channel; - for (i = 0; i < (NPWM / 2); i++) { - struct atmel_tcb_channel *chan = &tcbpwm->bkup[i]; + regmap_read(tcbpwm->regmap, ATMEL_TC_REG(channel, CMR), &chan->cmr); + regmap_read(tcbpwm->regmap, ATMEL_TC_REG(channel, RA), &chan->ra); + regmap_read(tcbpwm->regmap, ATMEL_TC_REG(channel, RB), &chan->rb); + regmap_read(tcbpwm->regmap, ATMEL_TC_REG(channel, RC), &chan->rc); - 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 atmel_tcb_channel *chan = &tcbpwm->bkup; + unsigned int channel = tcbpwm->channel; + + regmap_write(tcbpwm->regmap, ATMEL_TC_REG(channel, CMR), chan->cmr); + regmap_write(tcbpwm->regmap, ATMEL_TC_REG(channel, RA), chan->ra); + regmap_write(tcbpwm->regmap, ATMEL_TC_REG(channel, RB), chan->rb); + regmap_write(tcbpwm->regmap, ATMEL_TC_REG(channel, RC), chan->rc); + + if (chan->enabled) + regmap_write(tcbpwm->regmap, + ATMEL_TC_CLKEN | ATMEL_TC_SWTRG, + ATMEL_TC_REG(channel, CCR)); + return 0; } #endif diff --git a/drivers/pwm/pwm-atmel.c b/drivers/pwm/pwm-atmel.c index 6161e7e3e9ac..5813339b597b 100644 --- a/drivers/pwm/pwm-atmel.c +++ b/drivers/pwm/pwm-atmel.c @@ -401,7 +401,6 @@ MODULE_DEVICE_TABLE(of, atmel_pwm_dt_ids); static int atmel_pwm_probe(struct platform_device *pdev) { struct atmel_pwm_chip *atmel_pwm; - struct resource *res; int ret; atmel_pwm = devm_kzalloc(&pdev->dev, sizeof(*atmel_pwm), GFP_KERNEL); @@ -412,8 +411,7 @@ static int atmel_pwm_probe(struct platform_device *pdev) 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->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(atmel_pwm->base)) return PTR_ERR(atmel_pwm->base); diff --git a/drivers/pwm/pwm-bcm-iproc.c b/drivers/pwm/pwm-bcm-iproc.c index 79b1e58e946d..f4853c4a2d75 100644 --- a/drivers/pwm/pwm-bcm-iproc.c +++ b/drivers/pwm/pwm-bcm-iproc.c @@ -197,7 +197,6 @@ static const struct pwm_ops iproc_pwm_ops = { static int iproc_pwmc_probe(struct platform_device *pdev) { struct iproc_pwmc *ip; - struct resource *res; unsigned int i; u32 value; int ret; @@ -215,8 +214,7 @@ static int iproc_pwmc_probe(struct platform_device *pdev) ip->chip.of_xlate = of_pwm_xlate_with_flags; ip->chip.of_pwm_n_cells = 3; - 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); diff --git a/drivers/pwm/pwm-bcm-kona.c b/drivers/pwm/pwm-bcm-kona.c index 16c5898b934a..578b3621c97e 100644 --- a/drivers/pwm/pwm-bcm-kona.c +++ b/drivers/pwm/pwm-bcm-kona.c @@ -259,7 +259,6 @@ static const struct pwm_ops kona_pwm_ops = { static int kona_pwmc_probe(struct platform_device *pdev) { struct kona_pwmc *kp; - struct resource *res; unsigned int chan; unsigned int value = 0; int ret = 0; @@ -277,8 +276,7 @@ static int kona_pwmc_probe(struct platform_device *pdev) kp->chip.of_xlate = of_pwm_xlate_with_flags; kp->chip.of_pwm_n_cells = 3; - 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); diff --git a/drivers/pwm/pwm-bcm2835.c b/drivers/pwm/pwm-bcm2835.c index 6841dcfe27fc..6ff5f04b3e07 100644 --- a/drivers/pwm/pwm-bcm2835.c +++ b/drivers/pwm/pwm-bcm2835.c @@ -58,13 +58,15 @@ static void bcm2835_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm) 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) +static int bcm2835_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, + const struct pwm_state *state) { + struct bcm2835_pwm *pc = to_bcm2835_pwm(chip); unsigned long rate = clk_get_rate(pc->clk); + unsigned long long period; unsigned long scaler; - u32 period; + u32 val; if (!rate) { dev_err(pc->dev, "failed to get clock rate\n"); @@ -72,54 +74,34 @@ static int bcm2835_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, } scaler = DIV_ROUND_CLOSEST(NSEC_PER_SEC, rate); - period = DIV_ROUND_CLOSEST(period_ns, scaler); + /* set period */ + period = DIV_ROUND_CLOSEST_ULL(state->period, scaler); - if (period < PERIOD_MIN) + /* dont accept a period that is too small or has been truncated */ + if ((period < PERIOD_MIN) || (period > U32_MAX)) return -EINVAL; - writel(DIV_ROUND_CLOSEST(duty_ns, scaler), - pc->base + DUTY(pwm->hwpwm)); writel(period, pc->base + PERIOD(pwm->hwpwm)); - return 0; -} - -static int bcm2835_pwm_enable(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); + /* set duty cycle */ + val = DIV_ROUND_CLOSEST_ULL(state->duty_cycle, scaler); + writel(val, pc->base + DUTY(pwm->hwpwm)); - return 0; -} + /* set polarity */ + val = readl(pc->base + PWM_CONTROL); -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; } @@ -127,17 +109,13 @@ static int bcm2835_set_polarity(struct pwm_chip *chip, struct pwm_device *pwm, 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, + .apply = bcm2835_pwm_apply, .owner = THIS_MODULE, }; static int bcm2835_pwm_probe(struct platform_device *pdev) { struct bcm2835_pwm *pc; - struct resource *res; int ret; pc = devm_kzalloc(&pdev->dev, sizeof(*pc), GFP_KERNEL); @@ -146,8 +124,7 @@ static int bcm2835_pwm_probe(struct platform_device *pdev) 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); diff --git a/drivers/pwm/pwm-berlin.c b/drivers/pwm/pwm-berlin.c index b91c477cc84b..fe405289e582 100644 --- a/drivers/pwm/pwm-berlin.c +++ b/drivers/pwm/pwm-berlin.c @@ -186,15 +186,13 @@ MODULE_DEVICE_TABLE(of, berlin_pwm_match); static int berlin_pwm_probe(struct platform_device *pdev) { struct berlin_pwm_chip *pwm; - struct resource *res; int ret; pwm = devm_kzalloc(&pdev->dev, sizeof(*pwm), GFP_KERNEL); if (!pwm) return -ENOMEM; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - pwm->base = devm_ioremap_resource(&pdev->dev, res); + pwm->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(pwm->base)) return PTR_ERR(pwm->base); diff --git a/drivers/pwm/pwm-brcmstb.c b/drivers/pwm/pwm-brcmstb.c index fea612c45f20..8b66f9d2f589 100644 --- a/drivers/pwm/pwm-brcmstb.c +++ b/drivers/pwm/pwm-brcmstb.c @@ -234,7 +234,6 @@ MODULE_DEVICE_TABLE(of, brcmstb_pwm_of_match); static int brcmstb_pwm_probe(struct platform_device *pdev) { struct brcmstb_pwm *p; - struct resource *res; int ret; p = devm_kzalloc(&pdev->dev, sizeof(*p), GFP_KERNEL); @@ -262,8 +261,7 @@ static int brcmstb_pwm_probe(struct platform_device *pdev) p->chip.base = -1; p->chip.npwm = 2; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - p->base = devm_ioremap_resource(&pdev->dev, res); + p->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(p->base)) { ret = PTR_ERR(p->base); goto out_clk; diff --git a/drivers/pwm/pwm-clps711x.c b/drivers/pwm/pwm-clps711x.c index ba9500aca078..cb1af86873ee 100644 --- a/drivers/pwm/pwm-clps711x.c +++ b/drivers/pwm/pwm-clps711x.c @@ -113,14 +113,12 @@ static struct pwm_device *clps711x_pwm_xlate(struct pwm_chip *chip, static int clps711x_pwm_probe(struct platform_device *pdev) { struct clps711x_chip *priv; - struct resource *res; priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; - 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); diff --git a/drivers/pwm/pwm-crc.c b/drivers/pwm/pwm-crc.c index ecfdfac0c2d9..1e2276808b7a 100644 --- a/drivers/pwm/pwm-crc.c +++ b/drivers/pwm/pwm-crc.c @@ -64,7 +64,7 @@ static int crc_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, } if (state->polarity != PWM_POLARITY_NORMAL) - return -EOPNOTSUPP; + return -EINVAL; if (pwm_is_enabled(pwm) && !state->enabled) { err = regmap_write(crc_pwm->regmap, BACKLIGHT_EN, 0); diff --git a/drivers/pwm/pwm-dwc.c b/drivers/pwm/pwm-dwc.c new file mode 100644 index 000000000000..f6c98e0d57c2 --- /dev/null +++ b/drivers/pwm/pwm-dwc.c @@ -0,0 +1,319 @@ +// 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> + * + * Limitations: + * - The hardware cannot generate a 0 % or 100 % duty cycle. Both high and low + * periods are one or more input clock periods long. + */ + +#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> + +#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 +#define DWC_CLK_PERIOD_NS 10 + +/* 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_ctx { + u32 cnt; + u32 cnt2; + u32 ctrl; +}; + +struct dwc_pwm { + struct pwm_chip chip; + void __iomem *base; + struct dwc_pwm_ctx ctx[DWC_TIMERS_TOTAL]; +}; +#define to_dwc_pwm(p) (container_of((p), struct dwc_pwm, 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); +} + +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_PERIOD_NS); + if (tmp < 1 || tmp > (1ULL << 32)) + return -ERANGE; + low = tmp - 1; + + tmp = DIV_ROUND_CLOSEST_ULL(state->period - state->duty_cycle, + DWC_CLK_PERIOD_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(chip->dev); + 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(chip->dev); + } + } + + return 0; +} + +static void 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; + + pm_runtime_get_sync(chip->dev); + + state->enabled = !!(dwc_pwm_readl(dwc, + DWC_TIM_CTRL(pwm->hwpwm)) & DWC_TIM_CTRL_EN); + + duty = dwc_pwm_readl(dwc, DWC_TIM_LD_CNT(pwm->hwpwm)); + duty += 1; + duty *= DWC_CLK_PERIOD_NS; + state->duty_cycle = duty; + + period = dwc_pwm_readl(dwc, DWC_TIM_LD_CNT2(pwm->hwpwm)); + period += 1; + period *= DWC_CLK_PERIOD_NS; + period += duty; + state->period = period; + + state->polarity = PWM_POLARITY_INVERSED; + + pm_runtime_put_sync(chip->dev); +} + +static const struct pwm_ops dwc_pwm_ops = { + .apply = dwc_pwm_apply, + .get_state = dwc_pwm_get_state, + .owner = THIS_MODULE, +}; + +static int dwc_pwm_probe(struct pci_dev *pci, const struct pci_device_id *id) +{ + struct device *dev = &pci->dev; + struct dwc_pwm *dwc; + int ret; + + dwc = devm_kzalloc(&pci->dev, sizeof(*dwc), GFP_KERNEL); + if (!dwc) + return -ENOMEM; + + ret = pcim_enable_device(pci); + if (ret) { + dev_err(&pci->dev, + "Failed to enable device (%pe)\n", ERR_PTR(ret)); + return ret; + } + + pci_set_master(pci); + + ret = pcim_iomap_regions(pci, BIT(0), pci_name(pci)); + if (ret) { + dev_err(&pci->dev, + "Failed to iomap PCI BAR (%pe)\n", ERR_PTR(ret)); + return ret; + } + + dwc->base = pcim_iomap_table(pci)[0]; + if (!dwc->base) { + dev_err(&pci->dev, "Base address missing\n"); + return -ENOMEM; + } + + pci_set_drvdata(pci, dwc); + + dwc->chip.dev = dev; + dwc->chip.ops = &dwc_pwm_ops; + dwc->chip.npwm = DWC_TIMERS_TOTAL; + dwc->chip.base = -1; + + ret = pwmchip_add(&dwc->chip); + if (ret) + return ret; + + pm_runtime_put(dev); + pm_runtime_allow(dev); + + return 0; +} + +static void dwc_pwm_remove(struct pci_dev *pci) +{ + struct dwc_pwm *dwc = pci_get_drvdata(pci); + + pm_runtime_forbid(&pci->dev); + pm_runtime_get_noresume(&pci->dev); + + pwmchip_remove(&dwc->chip); +} + +#ifdef CONFIG_PM_SLEEP +static int dwc_pwm_suspend(struct device *dev) +{ + struct pci_dev *pdev = container_of(dev, struct pci_dev, dev); + struct dwc_pwm *dwc = pci_get_drvdata(pdev); + int i; + + for (i = 0; i < DWC_TIMERS_TOTAL; i++) { + if (dwc->chip.pwms[i].state.enabled) { + dev_err(dev, "PWM %u in use by consumer (%s)\n", + i, dwc->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 pci_dev *pdev = container_of(dev, struct pci_dev, dev); + struct dwc_pwm *dwc = pci_get_drvdata(pdev); + 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; +} +#endif + +static 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) }, /* Elkhart Lake */ + { } /* 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 = &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-ep93xx.c b/drivers/pwm/pwm-ep93xx.c index 4bab73073ad7..c9fc6f223640 100644 --- a/drivers/pwm/pwm-ep93xx.c +++ b/drivers/pwm/pwm-ep93xx.c @@ -169,15 +169,13 @@ static const struct pwm_ops ep93xx_pwm_ops = { static int ep93xx_pwm_probe(struct platform_device *pdev) { 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; - 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); diff --git a/drivers/pwm/pwm-fsl-ftm.c b/drivers/pwm/pwm-fsl-ftm.c index 59272a920479..2a6801226aba 100644 --- a/drivers/pwm/pwm-fsl-ftm.c +++ b/drivers/pwm/pwm-fsl-ftm.c @@ -399,7 +399,6 @@ static const struct regmap_config fsl_pwm_regmap_config = { static int fsl_pwm_probe(struct platform_device *pdev) { struct fsl_pwm_chip *fpc; - struct resource *res; void __iomem *base; int ret; @@ -412,8 +411,7 @@ static int fsl_pwm_probe(struct platform_device *pdev) fpc->soc = of_device_get_match_data(&pdev->dev); fpc->chip.dev = &pdev->dev; - 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); diff --git a/drivers/pwm/pwm-hibvt.c b/drivers/pwm/pwm-hibvt.c index ad205fdad372..a1900d0a872e 100644 --- a/drivers/pwm/pwm-hibvt.c +++ b/drivers/pwm/pwm-hibvt.c @@ -190,9 +190,7 @@ 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; + int ret, i; pwm_chip = devm_kzalloc(&pdev->dev, sizeof(*pwm_chip), GFP_KERNEL); if (pwm_chip == NULL) @@ -213,8 +211,7 @@ static int hibvt_pwm_probe(struct platform_device *pdev) pwm_chip->chip.of_pwm_n_cells = 3; pwm_chip->soc = soc; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - pwm_chip->base = devm_ioremap_resource(&pdev->dev, res); + pwm_chip->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(pwm_chip->base)) return PTR_ERR(pwm_chip->base); diff --git a/drivers/pwm/pwm-img.c b/drivers/pwm/pwm-img.c index a34d95ed70b2..6faf5b5a5584 100644 --- a/drivers/pwm/pwm-img.c +++ b/drivers/pwm/pwm-img.c @@ -240,7 +240,6 @@ 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; @@ -250,8 +249,7 @@ static int img_pwm_probe(struct platform_device *pdev) pwm->dev = &pdev->dev; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - pwm->base = devm_ioremap_resource(&pdev->dev, res); + pwm->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(pwm->base)) return PTR_ERR(pwm->base); diff --git a/drivers/pwm/pwm-imx-tpm.c b/drivers/pwm/pwm-imx-tpm.c index fcdf6befb838..aaf629bd8c35 100644 --- a/drivers/pwm/pwm-imx-tpm.c +++ b/drivers/pwm/pwm-imx-tpm.c @@ -350,13 +350,9 @@ static int pwm_imx_tpm_probe(struct platform_device *pdev) return PTR_ERR(tpm->base); 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; - } + if (IS_ERR(tpm->clk)) + return dev_err_probe(&pdev->dev, PTR_ERR(tpm->clk), + "failed to get PWM clock\n"); ret = clk_prepare_enable(tpm->clk); if (ret) { diff --git a/drivers/pwm/pwm-imx1.c b/drivers/pwm/pwm-imx1.c index f8b2c2e001a7..727e0d3e249e 100644 --- a/drivers/pwm/pwm-imx1.c +++ b/drivers/pwm/pwm-imx1.c @@ -136,7 +136,6 @@ MODULE_DEVICE_TABLE(of, pwm_imx1_dt_ids); static int pwm_imx1_probe(struct platform_device *pdev) { struct pwm_imx1_chip *imx; - struct resource *r; imx = devm_kzalloc(&pdev->dev, sizeof(*imx), GFP_KERNEL); if (!imx) @@ -145,31 +144,21 @@ static int pwm_imx1_probe(struct platform_device *pdev) platform_set_drvdata(pdev, imx); 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; - 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); diff --git a/drivers/pwm/pwm-imx27.c b/drivers/pwm/pwm-imx27.c index c50d453552bd..18055326a2f3 100644 --- a/drivers/pwm/pwm-imx27.c +++ b/drivers/pwm/pwm-imx27.c @@ -235,8 +235,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 @@ -315,27 +316,14 @@ static int pwm_imx27_probe(struct platform_device *pdev) 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; - } + 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_imx27_ops; imx->chip.dev = &pdev->dev; diff --git a/drivers/pwm/pwm-intel-lgm.c b/drivers/pwm/pwm-intel-lgm.c new file mode 100644 index 000000000000..e9e54dda07aa --- /dev/null +++ b/drivers/pwm/pwm-intel-lgm.c @@ -0,0 +1,244 @@ +// 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 pwm_chip chip; + struct regmap *regmap; + u32 period; +}; + +static inline struct lgm_pwm_chip *to_lgm_pwm_chip(struct pwm_chip *chip) +{ + return container_of(chip, struct lgm_pwm_chip, 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 void 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); +} + +static const struct pwm_ops lgm_pwm_ops = { + .get_state = lgm_pwm_get_state, + .apply = lgm_pwm_apply, + .owner = THIS_MODULE, +}; + +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 lgm_pwm_chip *pc; + void __iomem *io_base; + struct clk *clk; + int ret; + + pc = devm_kzalloc(dev, sizeof(*pc), GFP_KERNEL); + if (!pc) + return -ENOMEM; + + platform_set_drvdata(pdev, pc); + + 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"); + + pc->chip.dev = dev; + pc->chip.ops = &lgm_pwm_ops; + pc->chip.npwm = 1; + pc->chip.base = -1; + + lgm_pwm_init(pc); + + ret = pwmchip_add(&pc->chip); + if (ret < 0) + return dev_err_probe(dev, ret, "failed to add PWM chip\n"); + + return 0; +} + +static int lgm_pwm_remove(struct platform_device *pdev) +{ + struct lgm_pwm_chip *pc = platform_get_drvdata(pdev); + + return pwmchip_remove(&pc->chip); +} + +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, + .remove = lgm_pwm_remove, +}; +module_platform_driver(lgm_pwm_driver); + +MODULE_LICENSE("GPL v2"); diff --git a/drivers/pwm/pwm-iqs620a.c b/drivers/pwm/pwm-iqs620a.c index 7d33e3646436..5ede8255926e 100644 --- a/drivers/pwm/pwm-iqs620a.c +++ b/drivers/pwm/pwm-iqs620a.c @@ -50,7 +50,7 @@ static int iqs620_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, int ret; if (state->polarity != PWM_POLARITY_NORMAL) - return -ENOTSUPP; + return -EINVAL; if (state->period < IQS620_PWM_PERIOD_NS) return -EINVAL; diff --git a/drivers/pwm/pwm-keembay.c b/drivers/pwm/pwm-keembay.c new file mode 100644 index 000000000000..cdfdef66ff8e --- /dev/null +++ b/drivers/pwm/pwm-keembay.c @@ -0,0 +1,245 @@ +// 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 pwm_chip chip; + struct device *dev; + struct clk *clk; + void __iomem *base; +}; + +static inline struct keembay_pwm *to_keembay_pwm_dev(struct pwm_chip *chip) +{ + return container_of(chip, struct keembay_pwm, 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 void 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; +} + +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 = { + .owner = THIS_MODULE, + .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 keembay_pwm *priv; + int ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + 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; + + priv->chip.base = -1; + priv->chip.dev = dev; + priv->chip.ops = &keembay_pwm_ops; + priv->chip.npwm = KMB_TOTAL_PWM_CHANNELS; + + ret = pwmchip_add(&priv->chip); + if (ret) + return dev_err_probe(dev, ret, "Failed to add PWM chip\n"); + + platform_set_drvdata(pdev, priv); + + return 0; +} + +static int keembay_pwm_remove(struct platform_device *pdev) +{ + struct keembay_pwm *priv = platform_get_drvdata(pdev); + + return pwmchip_remove(&priv->chip); +} + +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, + .remove = keembay_pwm_remove, + .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-lp3943.c b/drivers/pwm/pwm-lp3943.c index 7551253ada32..bf3f14fb5f24 100644 --- a/drivers/pwm/pwm-lp3943.c +++ b/drivers/pwm/pwm-lp3943.c @@ -275,6 +275,7 @@ static int lp3943_pwm_probe(struct platform_device *pdev) lp3943_pwm->chip.dev = &pdev->dev; lp3943_pwm->chip.ops = &lp3943_pwm_ops; lp3943_pwm->chip.npwm = LP3943_NUM_PWMS; + lp3943_pwm->chip.base = -1; platform_set_drvdata(pdev, lp3943_pwm); diff --git a/drivers/pwm/pwm-lpc18xx-sct.c b/drivers/pwm/pwm-lpc18xx-sct.c index 5ff11145c1a3..dc5133bec3e7 100644 --- a/drivers/pwm/pwm-lpc18xx-sct.c +++ b/drivers/pwm/pwm-lpc18xx-sct.c @@ -325,7 +325,6 @@ static int lpc18xx_pwm_probe(struct platform_device *pdev) { struct lpc18xx_pwm_chip *lpc18xx_pwm; struct pwm_device *pwm; - struct resource *res; int ret, i; u64 val; @@ -336,8 +335,7 @@ static int lpc18xx_pwm_probe(struct platform_device *pdev) lpc18xx_pwm->dev = &pdev->dev; - 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); diff --git a/drivers/pwm/pwm-lpc32xx.c b/drivers/pwm/pwm-lpc32xx.c index 710d9a207d2b..6b4090436c06 100644 --- a/drivers/pwm/pwm-lpc32xx.c +++ b/drivers/pwm/pwm-lpc32xx.c @@ -98,7 +98,6 @@ static const struct pwm_ops lpc32xx_pwm_ops = { static int lpc32xx_pwm_probe(struct platform_device *pdev) { struct lpc32xx_pwm_chip *lpc32xx; - struct resource *res; int ret; u32 val; @@ -106,8 +105,7 @@ static int lpc32xx_pwm_probe(struct platform_device *pdev) if (!lpc32xx) return -ENOMEM; - 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); diff --git a/drivers/pwm/pwm-lpss-platform.c b/drivers/pwm/pwm-lpss-platform.c index c6502cf7a7af..986786be1e49 100644 --- a/drivers/pwm/pwm-lpss-platform.c +++ b/drivers/pwm/pwm-lpss-platform.c @@ -58,7 +58,25 @@ static int pwm_lpss_probe_platform(struct platform_device *pdev) platform_set_drvdata(pdev, lpwm); - dev_pm_set_driver_flags(&pdev->dev, DPM_FLAG_SMART_PREPARE); + /* + * On Cherry Trail devices the GFX0._PS0 AML checks if the controller + * is on and if it is not on it turns it on and restores what it + * believes is the correct state to the PWM controller. + * Because of this we must disallow direct-complete, which keeps the + * controller (runtime)suspended on resume, to avoid 2 issues: + * 1. The controller getting turned on without the linux-pm code + * knowing about this. On devices where the controller is unused + * this causes it to stay on during the next suspend causing high + * battery drain (because S0i3 is not reached) + * 2. The state restoring code unexpectedly messing with the controller + * + * Leaving the controller runtime-suspended (skipping runtime-resume + + * normal-suspend) during suspend is fine. + */ + if (info->other_devices_aml_touches_pwm_regs) + dev_pm_set_driver_flags(&pdev->dev, DPM_FLAG_NO_DIRECT_COMPLETE| + DPM_FLAG_SMART_SUSPEND); + pm_runtime_set_active(&pdev->dev); pm_runtime_enable(&pdev->dev); @@ -73,24 +91,6 @@ static int pwm_lpss_remove_platform(struct platform_device *pdev) return pwm_lpss_remove(lpwm); } -static int pwm_lpss_prepare(struct device *dev) -{ - struct pwm_lpss_chip *lpwm = dev_get_drvdata(dev); - - /* - * If other device's AML code touches the PWM regs on suspend/resume - * force runtime-resume the PWM controller to allow this. - */ - if (lpwm->info->other_devices_aml_touches_pwm_regs) - return 0; /* Force runtime-resume */ - - return 1; /* If runtime-suspended leave as is */ -} - -static const struct dev_pm_ops pwm_lpss_platform_pm_ops = { - .prepare = pwm_lpss_prepare, -}; - static const struct acpi_device_id pwm_lpss_acpi_match[] = { { "80860F09", (unsigned long)&pwm_lpss_byt_info }, { "80862288", (unsigned long)&pwm_lpss_bsw_info }, @@ -104,7 +104,6 @@ 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, diff --git a/drivers/pwm/pwm-lpss.c b/drivers/pwm/pwm-lpss.c index 3444c56b4bed..939de93c157b 100644 --- a/drivers/pwm/pwm-lpss.c +++ b/drivers/pwm/pwm-lpss.c @@ -76,7 +76,12 @@ static int pwm_lpss_wait_for_update(struct pwm_device *pwm) 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(pwm->chip->dev, "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, diff --git a/drivers/pwm/pwm-mediatek.c b/drivers/pwm/pwm-mediatek.c index ab001ce55178..fcfc3b147e5f 100644 --- a/drivers/pwm/pwm-mediatek.c +++ b/drivers/pwm/pwm-mediatek.c @@ -30,12 +30,14 @@ #define PWM45DWIDTH_FIXUP 0x30 #define PWMTHRES 0x30 #define PWM45THRES_FIXUP 0x34 +#define PWM_CK_26M_SEL 0x210 #define PWM_CLK_DIV_MAX 7 struct pwm_mediatek_of_data { unsigned int num_pwms; bool pwm45_fixup; + bool has_ck_26m_sel; }; /** @@ -132,6 +134,10 @@ static int pwm_mediatek_config(struct pwm_chip *chip, struct pwm_device *pwm, if (ret < 0) return ret; + /* Make sure we use the bus clock and not the 26MHz clock */ + if (pc->soc->has_ck_26m_sel) + writel(0, pc->regs + PWM_CK_26M_SEL); + /* Using resolution in picosecond gets accuracy higher */ resolution = (u64)NSEC_PER_SEC * 1000; do_div(resolution, clk_get_rate(pc->clk_pwms[pwm->hwpwm])); @@ -208,7 +214,6 @@ static const struct pwm_ops pwm_mediatek_ops = { static int pwm_mediatek_probe(struct platform_device *pdev) { struct pwm_mediatek_chip *pc; - struct resource *res; unsigned int i; int ret; @@ -218,8 +223,7 @@ static int pwm_mediatek_probe(struct platform_device *pdev) pc->soc = of_device_get_match_data(&pdev->dev); - 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); @@ -281,31 +285,43 @@ static int pwm_mediatek_remove(struct platform_device *pdev) static const struct pwm_mediatek_of_data mt2712_pwm_data = { .num_pwms = 8, .pwm45_fixup = false, + .has_ck_26m_sel = false, }; static const struct pwm_mediatek_of_data mt7622_pwm_data = { .num_pwms = 6, .pwm45_fixup = false, + .has_ck_26m_sel = false, }; static const struct pwm_mediatek_of_data mt7623_pwm_data = { .num_pwms = 5, .pwm45_fixup = true, + .has_ck_26m_sel = false, }; static const struct pwm_mediatek_of_data mt7628_pwm_data = { .num_pwms = 4, .pwm45_fixup = true, + .has_ck_26m_sel = false, }; static const struct pwm_mediatek_of_data mt7629_pwm_data = { .num_pwms = 1, .pwm45_fixup = false, + .has_ck_26m_sel = false, +}; + +static const struct pwm_mediatek_of_data mt8183_pwm_data = { + .num_pwms = 4, + .pwm45_fixup = false, + .has_ck_26m_sel = true, }; static const struct pwm_mediatek_of_data mt8516_pwm_data = { .num_pwms = 5, .pwm45_fixup = false, + .has_ck_26m_sel = true, }; static const struct of_device_id pwm_mediatek_of_match[] = { @@ -314,6 +330,7 @@ static const struct of_device_id pwm_mediatek_of_match[] = { { .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,mt8183-pwm", .data = &mt8183_pwm_data }, { .compatible = "mediatek,mt8516-pwm", .data = &mt8516_pwm_data }, { }, }; diff --git a/drivers/pwm/pwm-meson.c b/drivers/pwm/pwm-meson.c index bd0d7336b898..a3ce9789412a 100644 --- a/drivers/pwm/pwm-meson.c +++ b/drivers/pwm/pwm-meson.c @@ -537,15 +537,13 @@ static int meson_pwm_init_channels(struct meson_pwm *meson) static int meson_pwm_probe(struct platform_device *pdev) { struct meson_pwm *meson; - struct resource *regs; int err; meson = devm_kzalloc(&pdev->dev, sizeof(*meson), GFP_KERNEL); if (!meson) return -ENOMEM; - 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); diff --git a/drivers/pwm/pwm-mtk-disp.c b/drivers/pwm/pwm-mtk-disp.c index 83b8be0209b7..87c6b4bc5d43 100644 --- a/drivers/pwm/pwm-mtk-disp.c +++ b/drivers/pwm/pwm-mtk-disp.c @@ -172,7 +172,6 @@ static const struct pwm_ops mtk_disp_pwm_ops = { static int mtk_disp_pwm_probe(struct platform_device *pdev) { struct mtk_disp_pwm *mdp; - struct resource *r; int ret; mdp = devm_kzalloc(&pdev->dev, sizeof(*mdp), GFP_KERNEL); @@ -181,8 +180,7 @@ static int mtk_disp_pwm_probe(struct platform_device *pdev) 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); diff --git a/drivers/pwm/pwm-pxa.c b/drivers/pwm/pwm-pxa.c index a2a0912c2dcd..d06cf60e6575 100644 --- a/drivers/pwm/pwm-pxa.c +++ b/drivers/pwm/pwm-pxa.c @@ -166,7 +166,6 @@ 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; int ret = 0; if (IS_ENABLED(CONFIG_OF) && id == NULL) @@ -193,8 +192,7 @@ static int pwm_probe(struct platform_device *pdev) pwm->chip.of_pwm_n_cells = 1; } - r = platform_get_resource(pdev, IORESOURCE_MEM, 0); - pwm->mmio_base = devm_ioremap_resource(&pdev->dev, r); + pwm->mmio_base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(pwm->mmio_base)) return PTR_ERR(pwm->mmio_base); diff --git a/drivers/pwm/pwm-rcar.c b/drivers/pwm/pwm-rcar.c index 7ab9eb6616d9..002ab79a7ec2 100644 --- a/drivers/pwm/pwm-rcar.c +++ b/drivers/pwm/pwm-rcar.c @@ -168,7 +168,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); @@ -204,15 +204,13 @@ static const struct pwm_ops rcar_pwm_ops = { static int rcar_pwm_probe(struct platform_device *pdev) { 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; - 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); diff --git a/drivers/pwm/pwm-renesas-tpu.c b/drivers/pwm/pwm-renesas-tpu.c index 81ad5a551455..d02b24b77cdf 100644 --- a/drivers/pwm/pwm-renesas-tpu.c +++ b/drivers/pwm/pwm-renesas-tpu.c @@ -383,7 +383,6 @@ static const struct pwm_ops tpu_pwm_ops = { static int tpu_probe(struct platform_device *pdev) { struct tpu_device *tpu; - struct resource *res; int ret; tpu = devm_kzalloc(&pdev->dev, sizeof(*tpu), GFP_KERNEL); @@ -394,8 +393,7 @@ static int tpu_probe(struct platform_device *pdev) 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); diff --git a/drivers/pwm/pwm-rockchip.c b/drivers/pwm/pwm-rockchip.c index 77c23a2c6d71..389a5e140412 100644 --- a/drivers/pwm/pwm-rockchip.c +++ b/drivers/pwm/pwm-rockchip.c @@ -287,7 +287,6 @@ static int rockchip_pwm_probe(struct platform_device *pdev) { const struct of_device_id *id; struct rockchip_pwm_chip *pc; - struct resource *r; u32 enable_conf, ctrl; int ret, count; @@ -299,8 +298,7 @@ static int rockchip_pwm_probe(struct platform_device *pdev) 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); diff --git a/drivers/pwm/pwm-samsung.c b/drivers/pwm/pwm-samsung.c index 87a886f7dc2f..645d0066ff0a 100644 --- a/drivers/pwm/pwm-samsung.c +++ b/drivers/pwm/pwm-samsung.c @@ -510,7 +510,6 @@ static int pwm_samsung_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct samsung_pwm_chip *chip; - struct resource *res; unsigned int chan; int ret; @@ -541,8 +540,7 @@ static int pwm_samsung_probe(struct platform_device *pdev) sizeof(chip->variant)); } - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - chip->base = devm_ioremap_resource(&pdev->dev, res); + chip->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(chip->base)) return PTR_ERR(chip->base); diff --git a/drivers/pwm/pwm-sifive.c b/drivers/pwm/pwm-sifive.c index 2485fbaaead2..2a7cd2deaeea 100644 --- a/drivers/pwm/pwm-sifive.c +++ b/drivers/pwm/pwm-sifive.c @@ -232,7 +232,6 @@ 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; ddata = devm_kzalloc(dev, sizeof(*ddata), GFP_KERNEL); @@ -248,8 +247,7 @@ static int pwm_sifive_probe(struct platform_device *pdev) 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); diff --git a/drivers/pwm/pwm-sl28cpld.c b/drivers/pwm/pwm-sl28cpld.c index b4c651fc749c..0b01ec25e2f0 100644 --- a/drivers/pwm/pwm-sl28cpld.c +++ b/drivers/pwm/pwm-sl28cpld.c @@ -232,6 +232,8 @@ static int sl28cpld_pwm_probe(struct platform_device *pdev) chip->base = -1; chip->npwm = 1; + platform_set_drvdata(pdev, priv); + ret = pwmchip_add(&priv->pwm_chip); if (ret) { dev_err(&pdev->dev, "failed to add PWM chip (%pe)", @@ -239,8 +241,6 @@ static int sl28cpld_pwm_probe(struct platform_device *pdev) return ret; } - platform_set_drvdata(pdev, priv); - return 0; } diff --git a/drivers/pwm/pwm-spear.c b/drivers/pwm/pwm-spear.c index 6c6b44fd3f43..f63b54aae1b4 100644 --- a/drivers/pwm/pwm-spear.c +++ b/drivers/pwm/pwm-spear.c @@ -174,7 +174,6 @@ static int spear_pwm_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; struct spear_pwm_chip *pc; - struct resource *r; int ret; u32 val; @@ -182,8 +181,7 @@ static int spear_pwm_probe(struct platform_device *pdev) if (!pc) return -ENOMEM; - 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); diff --git a/drivers/pwm/pwm-sti.c b/drivers/pwm/pwm-sti.c index 1508616d794c..99c70e07858d 100644 --- a/drivers/pwm/pwm-sti.c +++ b/drivers/pwm/pwm-sti.c @@ -505,7 +505,6 @@ static int sti_pwm_probe_dt(struct sti_pwm_chip *pc) 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]); if (IS_ERR(pc->pwm_out_en)) @@ -540,7 +539,6 @@ static int sti_pwm_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct sti_pwm_compat_data *cdata; struct sti_pwm_chip *pc; - struct resource *res; unsigned int i; int irq, ret; @@ -552,9 +550,7 @@ static int sti_pwm_probe(struct platform_device *pdev) if (!cdata) return -ENOMEM; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - - 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); @@ -593,38 +589,34 @@ static int sti_pwm_probe(struct platform_device *pdev) if (ret) return ret; - 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); - } + if (cdata->pwm_num_devs) { + 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; + ret = clk_prepare(pc->pwm_clk); + if (ret) { + dev_err(dev, "failed to prepare clock\n"); + return ret; + } } -skip_pwm: - if (!cdata->cpt_num_devs) - goto skip_cpt; - - 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); - } + if (cdata->cpt_num_devs) { + 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; + 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; diff --git a/drivers/pwm/pwm-sun4i.c b/drivers/pwm/pwm-sun4i.c index 38a4c5c1317b..ce5c4fc8da6f 100644 --- a/drivers/pwm/pwm-sun4i.c +++ b/drivers/pwm/pwm-sun4i.c @@ -294,12 +294,8 @@ 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); @@ -395,7 +391,6 @@ 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; int ret; pwm = devm_kzalloc(&pdev->dev, sizeof(*pwm), GFP_KERNEL); @@ -406,8 +401,7 @@ static int sun4i_pwm_probe(struct platform_device *pdev) if (!pwm->data) return -ENODEV; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - pwm->base = devm_ioremap_resource(&pdev->dev, res); + pwm->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(pwm->base)) return PTR_ERR(pwm->base); diff --git a/drivers/pwm/pwm-tegra.c b/drivers/pwm/pwm-tegra.c index 1daf591025c0..55bc63d5a0ae 100644 --- a/drivers/pwm/pwm-tegra.c +++ b/drivers/pwm/pwm-tegra.c @@ -237,7 +237,6 @@ static const struct pwm_ops tegra_pwm_ops = { static int tegra_pwm_probe(struct platform_device *pdev) { struct tegra_pwm_chip *pwm; - struct resource *r; int ret; pwm = devm_kzalloc(&pdev->dev, sizeof(*pwm), GFP_KERNEL); @@ -247,8 +246,7 @@ static int tegra_pwm_probe(struct platform_device *pdev) pwm->soc = of_device_get_match_data(&pdev->dev); pwm->dev = &pdev->dev; - r = platform_get_resource(pdev, IORESOURCE_MEM, 0); - pwm->regs = devm_ioremap_resource(&pdev->dev, r); + pwm->regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(pwm->regs)) return PTR_ERR(pwm->regs); diff --git a/drivers/pwm/pwm-tiecap.c b/drivers/pwm/pwm-tiecap.c index 683804c7d26c..2a8949014bb1 100644 --- a/drivers/pwm/pwm-tiecap.c +++ b/drivers/pwm/pwm-tiecap.c @@ -196,7 +196,6 @@ 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 clk *clk; int ret; @@ -230,8 +229,7 @@ static int ecap_pwm_probe(struct platform_device *pdev) pc->chip.base = -1; pc->chip.npwm = 1; - 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); diff --git a/drivers/pwm/pwm-tiehrpwm.c b/drivers/pwm/pwm-tiehrpwm.c index 0846917ff2d2..a7fb224d6535 100644 --- a/drivers/pwm/pwm-tiehrpwm.c +++ b/drivers/pwm/pwm-tiehrpwm.c @@ -421,7 +421,6 @@ 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 clk *clk; int ret; @@ -437,10 +436,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) { @@ -455,17 +452,14 @@ static int ehrpwm_pwm_probe(struct platform_device *pdev) pc->chip.base = -1; pc->chip.npwm = NUM_PWM_CHANNEL; - 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) { diff --git a/drivers/pwm/pwm-vt8500.c b/drivers/pwm/pwm-vt8500.c index 11d45e56a923..6e36851a22bb 100644 --- a/drivers/pwm/pwm-vt8500.c +++ b/drivers/pwm/pwm-vt8500.c @@ -193,7 +193,6 @@ 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 device_node *np = pdev->dev.of_node; int ret; @@ -219,8 +218,7 @@ static int vt8500_pwm_probe(struct platform_device *pdev) return PTR_ERR(chip->clk); } - r = platform_get_resource(pdev, IORESOURCE_MEM, 0); - chip->base = devm_ioremap_resource(&pdev->dev, r); + chip->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(chip->base)) return PTR_ERR(chip->base); diff --git a/drivers/pwm/pwm-zx.c b/drivers/pwm/pwm-zx.c index e2c21cc34a96..34e91195ce98 100644 --- a/drivers/pwm/pwm-zx.c +++ b/drivers/pwm/pwm-zx.c @@ -196,7 +196,6 @@ static const struct pwm_ops zx_pwm_ops = { static int zx_pwm_probe(struct platform_device *pdev) { struct zx_pwm_chip *zpc; - struct resource *res; unsigned int i; int ret; @@ -204,8 +203,7 @@ static int zx_pwm_probe(struct platform_device *pdev) if (!zpc) return -ENOMEM; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - zpc->base = devm_ioremap_resource(&pdev->dev, res); + zpc->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(zpc->base)) return PTR_ERR(zpc->base); @@ -238,6 +236,7 @@ static int zx_pwm_probe(struct platform_device *pdev) ret = pwmchip_add(&zpc->chip); if (ret < 0) { dev_err(&pdev->dev, "failed to add PWM chip: %d\n", ret); + clk_disable_unprepare(zpc->pclk); return ret; } |