diff --git a/Documentation/devicetree/bindings/pwm/marvell,pxa-pwm.yaml b/Documentation/devicetree/bindings/pwm/marvell,pxa-pwm.yaml index 8df327e52810..f1422a401b6b 100644 --- a/Documentation/devicetree/bindings/pwm/marvell,pxa-pwm.yaml +++ b/Documentation/devicetree/bindings/pwm/marvell,pxa-pwm.yaml @@ -15,7 +15,9 @@ allOf: properties: compatible: contains: - const: spacemit,k1-pwm + enum: + - spacemit,k1-pwm + - spacemit,k3-pwm then: properties: "#pwm-cells": @@ -26,6 +28,26 @@ allOf: const: 1 description: | Used for specifying the period length in nanoseconds. + - if: + properties: + compatible: + contains: + enum: + - spacemit,k3-pwm + then: + required: + - clock-names + properties: + clocks: + minItems: 2 + clock-names: + minItems: 2 + else: + properties: + clocks: + maxItems: 1 + clock-names: + maxItems: 1 properties: compatible: @@ -36,7 +58,9 @@ properties: - marvell,pxa168-pwm - marvell,pxa910-pwm - items: - - const: spacemit,k1-pwm + - enum: + - spacemit,k1-pwm + - spacemit,k3-pwm - const: marvell,pxa910-pwm reg: @@ -47,7 +71,18 @@ properties: description: Number of cells in a pwm specifier. clocks: - maxItems: 1 + minItems: 1 + items: + - description: The function clock + - description: An optional bus clock + + clock-names: + minItems: 1 + maxItems: 2 + oneOf: + - items: + - const: func + - const: bus resets: maxItems: 1 diff --git a/Documentation/devicetree/bindings/pwm/st,stmpe-pwm.txt b/Documentation/devicetree/bindings/pwm/st,stmpe-pwm.txt deleted file mode 100644 index f401316e0248..000000000000 --- a/Documentation/devicetree/bindings/pwm/st,stmpe-pwm.txt +++ /dev/null @@ -1,18 +0,0 @@ -== ST STMPE PWM controller == - -This is a PWM block embedded in the ST Microelectronics STMPE -(ST Multi-Purpose Expander) chips. The PWM is registered as a -subdevices of the STMPE MFD device. - -Required properties: -- compatible: should be: - - "st,stmpe-pwm" -- #pwm-cells: should be 2. See pwm.yaml in this directory for a description of - the cells format. - -Example: - -pwm0: pwm { - compatible = "st,stmpe-pwm"; - #pwm-cells = <2>; -}; diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index 6f3147518376..e8886a9b64d9 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig @@ -347,6 +347,18 @@ config PWM_INTEL_LGM To compile this driver as a module, choose M here: the module will be called pwm-intel-lgm. +config PWM_IPQ + tristate "IPQ PWM support" + depends on ARCH_QCOM || COMPILE_TEST + depends on HAVE_CLK && HAS_IOMEM + help + Generic PWM framework driver for IPQ PWM block which supports + 4 pwm channels. Each of the these channels can be configured + independent of each other. + + To compile this driver as a module, choose M here: the module + will be called pwm-ipq. + config PWM_IQS620A tristate "Azoteq IQS620A PWM support" depends on MFD_IQS62X || COMPILE_TEST diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile index 0dc0d2b69025..5630a521a7cf 100644 --- a/drivers/pwm/Makefile +++ b/drivers/pwm/Makefile @@ -29,6 +29,7 @@ obj-$(CONFIG_PWM_IMX1) += pwm-imx1.o obj-$(CONFIG_PWM_IMX27) += pwm-imx27.o obj-$(CONFIG_PWM_IMX_TPM) += pwm-imx-tpm.o obj-$(CONFIG_PWM_INTEL_LGM) += pwm-intel-lgm.o +obj-$(CONFIG_PWM_IPQ) += pwm-ipq.o obj-$(CONFIG_PWM_IQS620A) += pwm-iqs620a.o obj-$(CONFIG_PWM_JZ4740) += pwm-jz4740.o obj-$(CONFIG_PWM_KEEMBAY) += pwm-keembay.o diff --git a/drivers/pwm/pwm-atmel-tcb.c b/drivers/pwm/pwm-atmel-tcb.c index 3d30aeab507e..a765ef279b51 100644 --- a/drivers/pwm/pwm-atmel-tcb.c +++ b/drivers/pwm/pwm-atmel-tcb.c @@ -443,7 +443,7 @@ static int atmel_tcb_pwm_probe(struct platform_device *pdev) err = clk_prepare_enable(tcbpwmc->slow_clk); if (err) - goto err_disable_clk;; + goto err_disable_clk; err = clk_rate_exclusive_get(tcbpwmc->clk); if (err) diff --git a/drivers/pwm/pwm-dwc.c b/drivers/pwm/pwm-dwc.c index 86b72db58741..8ba7ef2c27d0 100644 --- a/drivers/pwm/pwm-dwc.c +++ b/drivers/pwm/pwm-dwc.c @@ -147,8 +147,8 @@ static int dwc_pwm_resume(struct device *dev) 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 */ + { PCI_VDEVICE(INTEL, 0x4bb7), .driver_data = (kernel_ulong_t)&ehl_pwm_info }, + { } /* Terminating Entry */ }; MODULE_DEVICE_TABLE(pci, dwc_pwm_id_table); diff --git a/drivers/pwm/pwm-imx27.c b/drivers/pwm/pwm-imx27.c index 3d34cdc4a3a5..c8b801fcb525 100644 --- a/drivers/pwm/pwm-imx27.c +++ b/drivers/pwm/pwm-imx27.c @@ -200,7 +200,7 @@ 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, period_us, tmp; + unsigned long period_cycles, duty_cycles, prescale, period_us; struct pwm_imx27_chip *imx = to_pwm_imx27_chip(chip); unsigned long long c; unsigned long long clkrate; @@ -208,6 +208,7 @@ static int pwm_imx27_apply(struct pwm_chip *chip, struct pwm_device *pwm, int val; int ret; u32 cr; + u64 tmp; clkrate = clk_get_rate(imx->clks[PWM_IMX27_PER].clk); c = clkrate * state->period; @@ -249,6 +250,11 @@ static int pwm_imx27_apply(struct pwm_chip *chip, struct pwm_device *pwm, val = readl(imx->mmio_base + MX3_PWMPR); val = val >= MX3_PWMPR_MAX ? MX3_PWMPR_MAX : val; cr = readl(imx->mmio_base + MX3_PWMCR); + + /* + * tmp stores period in nanoseconds. Result fits in u64 since + * val <= 0xfffe and prescaler in [1, 0x1000]. + */ tmp = NSEC_PER_SEC * (u64)(val + 2) * MX3_PWMCR_PRESCALER_GET(cr); tmp = DIV_ROUND_UP_ULL(tmp, clkrate); period_us = DIV_ROUND_UP_ULL(tmp, 1000); diff --git a/drivers/pwm/pwm-ipq.c b/drivers/pwm/pwm-ipq.c new file mode 100644 index 000000000000..c53373948136 --- /dev/null +++ b/drivers/pwm/pwm-ipq.c @@ -0,0 +1,264 @@ +// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 +/* + * Copyright (c) 2016-2017, 2020 The Linux Foundation. All rights reserved. + * + * Limitations: + * - The PWM controller has no publicly available datasheet. + * - Each of the four channels is programmed via two 32-bit registers + * (REG0 and REG1 at 8-byte stride). + * - Period and duty-cycle reconfiguration is fully atomic: new divider, + * pre-divider, and high-duration values are latched by setting the + * UPDATE bit (bit 30 in REG1). The hardware applies the new settings + * at the beginning of the next period without disabling the output, + * so the currently running period is always completed. + * - On disable (clearing the ENABLE bit 31 in REG1), the hardware + * finishes the current period before stopping the output. The pin + * is then driven to the inactive (low) level. + * - Upon disabling, the hardware resets the pre-divider (PRE_DIV) and divider + * fields (PWM_DIV) in REG0 and REG1 to 0x0000 and 0x0001 respectively. + * - Only normal polarity is supported. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* The frequency range supported is 1 Hz to 100 Mhz (clock rate) */ +#define IPQ_PWM_MAX_PERIOD_NS ((u64)NSEC_PER_SEC) +#define IPQ_PWM_MIN_PERIOD_NS 10 + +/* + * Two 32-bit registers for each PWM: REG0, and REG1. + * Base offset for PWM #i is at 8 * #i. + */ +#define IPQ_PWM_REG0 0 +#define IPQ_PWM_REG0_PWM_DIV GENMASK(15, 0) +#define IPQ_PWM_REG0_HI_DURATION GENMASK(31, 16) + +#define IPQ_PWM_REG1 4 +#define IPQ_PWM_REG1_PRE_DIV GENMASK(15, 0) +/* + * Enable bit is set to enable output toggling in pwm device. + * Update bit is set to trigger the change and is unset automatically + * to reflect the changed divider and high duration values in register. + */ +#define IPQ_PWM_REG1_UPDATE BIT(30) +#define IPQ_PWM_REG1_ENABLE BIT(31) + +/* + * The max value specified for each field is based on the number of bits + * in the pwm control register for that field (16-bit) + */ +#define IPQ_PWM_MAX_DIV FIELD_MAX(IPQ_PWM_REG0_PWM_DIV) + +struct ipq_pwm_chip { + void __iomem *mem; + unsigned long clk_rate; +}; + +static struct ipq_pwm_chip *ipq_pwm_from_chip(struct pwm_chip *chip) +{ + return pwmchip_get_drvdata(chip); +} + +static unsigned int ipq_pwm_reg_read(struct pwm_device *pwm, unsigned int reg) +{ + struct ipq_pwm_chip *ipq_chip = ipq_pwm_from_chip(pwm->chip); + unsigned int off = 8 * pwm->hwpwm + reg; + + return readl(ipq_chip->mem + off); +} + +static void ipq_pwm_reg_write(struct pwm_device *pwm, unsigned int reg, + unsigned int val) +{ + struct ipq_pwm_chip *ipq_chip = ipq_pwm_from_chip(pwm->chip); + unsigned int off = 8 * pwm->hwpwm + reg; + + writel(val, ipq_chip->mem + off); +} + +static int ipq_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, + const struct pwm_state *state) +{ + struct ipq_pwm_chip *ipq_chip = ipq_pwm_from_chip(chip); + unsigned int pre_div, pwm_div; + u64 period_ns, duty_ns; + unsigned long val = 0; + unsigned long hi_dur; + + if (!state->enabled) { + /* clear IPQ_PWM_REG1_ENABLE */ + ipq_pwm_reg_write(pwm, IPQ_PWM_REG1, IPQ_PWM_REG1_UPDATE); + return 0; + } + + if (state->polarity != PWM_POLARITY_NORMAL) + return -EINVAL; + + /* + * Check the upper and lower bounds for the period as per + * hardware limits + */ + if (state->period < IPQ_PWM_MIN_PERIOD_NS) + return -ERANGE; + period_ns = min(state->period, IPQ_PWM_MAX_PERIOD_NS); + duty_ns = min(state->duty_cycle, period_ns); + + /* + * Pick the maximal value for PWM_DIV that still allows a + * 100% relative duty cycle. This allows a fine grained + * selection of duty cycles. + */ + pwm_div = IPQ_PWM_MAX_DIV - 1; + + /* + * although mul_u64_u64_div_u64 returns a u64, in practice it + * won't overflow due to above constraints. Take the max period + * of 10^9 (NSEC_PER_SEC) and the pwm_div + 1 (IPQ_PWM_MAX_DIV) + * 10^9 * 10^8 + * ------------- => which fits well into a 32-bit unsigned int. + * 10^9 * 65,535 + */ + pre_div = mul_u64_u64_div_u64(period_ns, ipq_chip->clk_rate, + (u64)NSEC_PER_SEC * (pwm_div + 1)); + + if (!pre_div) + return -ERANGE; + + pre_div -= 1; + + if (pre_div > IPQ_PWM_MAX_DIV) + pre_div = IPQ_PWM_MAX_DIV; + + /* pwm duty = HI_DUR * (PRE_DIV + 1) / clk_rate */ + hi_dur = mul_u64_u64_div_u64(duty_ns, ipq_chip->clk_rate, + (u64)NSEC_PER_SEC * (pre_div + 1)); + + val = FIELD_PREP(IPQ_PWM_REG0_HI_DURATION, hi_dur) | + FIELD_PREP(IPQ_PWM_REG0_PWM_DIV, pwm_div); + ipq_pwm_reg_write(pwm, IPQ_PWM_REG0, val); + + val = FIELD_PREP(IPQ_PWM_REG1_PRE_DIV, pre_div); + ipq_pwm_reg_write(pwm, IPQ_PWM_REG1, val); + + /* PWM enable toggle needs a separate write to REG1 */ + val |= IPQ_PWM_REG1_UPDATE | IPQ_PWM_REG1_ENABLE; + ipq_pwm_reg_write(pwm, IPQ_PWM_REG1, val); + + return 0; +} + +static int ipq_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm, + struct pwm_state *state) +{ + struct ipq_pwm_chip *ipq_chip = ipq_pwm_from_chip(chip); + unsigned int pre_div, pwm_div, hi_dur; + u64 effective_div, hi_div; + u32 reg0, reg1; + + reg1 = ipq_pwm_reg_read(pwm, IPQ_PWM_REG1); + state->enabled = reg1 & IPQ_PWM_REG1_ENABLE; + + if (!state->enabled) + return 0; + + reg0 = ipq_pwm_reg_read(pwm, IPQ_PWM_REG0); + + state->polarity = PWM_POLARITY_NORMAL; + + pwm_div = FIELD_GET(IPQ_PWM_REG0_PWM_DIV, reg0); + hi_dur = FIELD_GET(IPQ_PWM_REG0_HI_DURATION, reg0); + pre_div = FIELD_GET(IPQ_PWM_REG1_PRE_DIV, reg1); + + effective_div = (u64)(pwm_div + 1) * (pre_div + 1); + + /* + * effective_div <= 0x100000000, so the multiplication doesn't overflow. + */ + state->period = DIV64_U64_ROUND_UP(effective_div * NSEC_PER_SEC, + ipq_chip->clk_rate); + + hi_div = hi_dur * (pre_div + 1); + state->duty_cycle = DIV64_U64_ROUND_UP(hi_div * NSEC_PER_SEC, + ipq_chip->clk_rate); + + /* + * ensure a valid config is passed back to PWM core in case duty_cycle + * is > period (>100%) + */ + state->duty_cycle = min(state->duty_cycle, state->period); + + return 0; +} + +static const struct pwm_ops ipq_pwm_ops = { + .apply = ipq_pwm_apply, + .get_state = ipq_pwm_get_state, +}; + +static int ipq_pwm_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct ipq_pwm_chip *pwm; + struct pwm_chip *chip; + struct clk *clk; + int ret; + + chip = devm_pwmchip_alloc(dev, 4, sizeof(*pwm)); + if (IS_ERR(chip)) + return PTR_ERR(chip); + pwm = ipq_pwm_from_chip(chip); + + pwm->mem = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(pwm->mem)) + return dev_err_probe(dev, PTR_ERR(pwm->mem), + "Failed to acquire resource\n"); + + clk = devm_clk_get_enabled(dev, NULL); + if (IS_ERR(clk)) + return dev_err_probe(dev, PTR_ERR(clk), + "Failed to get clock\n"); + + ret = devm_clk_rate_exclusive_get(dev, clk); + if (ret) + return dev_err_probe(dev, ret, "Failed to lock clock rate\n"); + + pwm->clk_rate = clk_get_rate(clk); + if (!pwm->clk_rate) + return dev_err_probe(dev, -EINVAL, "Failed due to clock rate being zero\n"); + + chip->ops = &ipq_pwm_ops; + + ret = devm_pwmchip_add(dev, chip); + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to add pwm chip\n"); + + return 0; +} + +static const struct of_device_id pwm_ipq_dt_match[] = { + { .compatible = "qcom,ipq6018-pwm", }, + {} +}; +MODULE_DEVICE_TABLE(of, pwm_ipq_dt_match); + +static struct platform_driver ipq_pwm_driver = { + .driver = { + .name = "ipq-pwm", + .of_match_table = pwm_ipq_dt_match, + }, + .probe = ipq_pwm_probe, +}; + +module_platform_driver(ipq_pwm_driver); + +MODULE_DESCRIPTION("Qualcomm IPQ PWM driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/pwm/pwm-lpss-pci.c b/drivers/pwm/pwm-lpss-pci.c index ae25d9321d75..3a0fd6593520 100644 --- a/drivers/pwm/pwm-lpss-pci.c +++ b/drivers/pwm/pwm-lpss-pci.c @@ -48,15 +48,15 @@ static void pwm_lpss_remove_pci(struct pci_dev *pdev) } 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}, - { PCI_VDEVICE(INTEL, 0x0f09), (unsigned long)&pwm_lpss_byt_info}, - { PCI_VDEVICE(INTEL, 0x11a5), (unsigned long)&pwm_lpss_tng_info}, - { PCI_VDEVICE(INTEL, 0x1ac8), (unsigned long)&pwm_lpss_bxt_info}, - { PCI_VDEVICE(INTEL, 0x2288), (unsigned long)&pwm_lpss_bsw_info}, - { PCI_VDEVICE(INTEL, 0x2289), (unsigned long)&pwm_lpss_bsw_info}, - { PCI_VDEVICE(INTEL, 0x31c8), (unsigned long)&pwm_lpss_bxt_info}, - { PCI_VDEVICE(INTEL, 0x5ac8), (unsigned long)&pwm_lpss_bxt_info}, + { PCI_VDEVICE(INTEL, 0x0ac8), .driver_data = (unsigned long)&pwm_lpss_bxt_info }, + { PCI_VDEVICE(INTEL, 0x0f08), .driver_data = (unsigned long)&pwm_lpss_byt_info }, + { PCI_VDEVICE(INTEL, 0x0f09), .driver_data = (unsigned long)&pwm_lpss_byt_info }, + { PCI_VDEVICE(INTEL, 0x11a5), .driver_data = (unsigned long)&pwm_lpss_tng_info }, + { PCI_VDEVICE(INTEL, 0x1ac8), .driver_data = (unsigned long)&pwm_lpss_bxt_info }, + { PCI_VDEVICE(INTEL, 0x2288), .driver_data = (unsigned long)&pwm_lpss_bsw_info }, + { PCI_VDEVICE(INTEL, 0x2289), .driver_data = (unsigned long)&pwm_lpss_bsw_info }, + { PCI_VDEVICE(INTEL, 0x31c8), .driver_data = (unsigned long)&pwm_lpss_bxt_info }, + { PCI_VDEVICE(INTEL, 0x5ac8), .driver_data = (unsigned long)&pwm_lpss_bxt_info }, { }, }; MODULE_DEVICE_TABLE(pci, pwm_lpss_pci_ids); diff --git a/drivers/pwm/pwm-mediatek.c b/drivers/pwm/pwm-mediatek.c index 9d206303404a..992137a27750 100644 --- a/drivers/pwm/pwm-mediatek.c +++ b/drivers/pwm/pwm-mediatek.c @@ -23,6 +23,8 @@ /* PWM registers and bits definitions */ #define PWMCON 0x00 #define PWMCON_CLKDIV GENMASK(2, 0) +#define PWMCON_CLKSEL BIT(3) +#define PWMCON_OLD_PWM_MODE BIT(15) #define PWMHDUR 0x04 #define PWMLDUR 0x08 #define PWMGDUR 0x0c @@ -38,6 +40,7 @@ struct pwm_mediatek_of_data { unsigned int num_pwms; + bool clksel_fixup; bool pwm45_fixup; u16 pwm_ck_26m_sel_reg; unsigned int chanreg_base; @@ -337,6 +340,7 @@ static int pwm_mediatek_write_waveform(struct pwm_chip *chip, if (wfhw->enable) { u32 reg_width = PWMDWIDTH, reg_thres = PWMTHRES; + u32 con_val = PWMCON_OLD_PWM_MODE | wfhw->con; if (pc->soc->pwm45_fixup && pwm->hwpwm > 2) { /* @@ -364,7 +368,11 @@ static int pwm_mediatek_write_waveform(struct pwm_chip *chip, 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); + /* Set BIT(3) to disable clock division */ + if (pc->soc->clksel_fixup) + con_val |= PWMCON_CLKSEL; + + pwm_mediatek_writel(pc, pwm->hwpwm, PWMCON, con_val); pwm_mediatek_writel(pc, pwm->hwpwm, reg_width, wfhw->width); pwm_mediatek_writel(pc, pwm->hwpwm, reg_thres, wfhw->thres); } else { @@ -496,6 +504,7 @@ static int pwm_mediatek_probe(struct platform_device *pdev) static const struct pwm_mediatek_of_data mt2712_pwm_data = { .num_pwms = 8, + .clksel_fixup = false, .pwm45_fixup = false, .chanreg_base = 0x10, .chanreg_width = 0x40, @@ -503,6 +512,7 @@ static const struct pwm_mediatek_of_data mt2712_pwm_data = { static const struct pwm_mediatek_of_data mt6795_pwm_data = { .num_pwms = 7, + .clksel_fixup = false, .pwm45_fixup = false, .chanreg_base = 0x10, .chanreg_width = 0x40, @@ -510,6 +520,7 @@ static const struct pwm_mediatek_of_data mt6795_pwm_data = { static const struct pwm_mediatek_of_data mt7622_pwm_data = { .num_pwms = 6, + .clksel_fixup = false, .pwm45_fixup = false, .pwm_ck_26m_sel_reg = PWM_CK_26M_SEL, .chanreg_base = 0x10, @@ -518,6 +529,7 @@ static const struct pwm_mediatek_of_data mt7622_pwm_data = { static const struct pwm_mediatek_of_data mt7623_pwm_data = { .num_pwms = 5, + .clksel_fixup = false, .pwm45_fixup = true, .chanreg_base = 0x10, .chanreg_width = 0x40, @@ -525,13 +537,15 @@ static const struct pwm_mediatek_of_data mt7623_pwm_data = { static const struct pwm_mediatek_of_data mt7628_pwm_data = { .num_pwms = 4, - .pwm45_fixup = true, + .clksel_fixup = true, + .pwm45_fixup = false, .chanreg_base = 0x10, .chanreg_width = 0x40, }; static const struct pwm_mediatek_of_data mt7629_pwm_data = { .num_pwms = 1, + .clksel_fixup = false, .pwm45_fixup = false, .chanreg_base = 0x10, .chanreg_width = 0x40, @@ -539,6 +553,7 @@ static const struct pwm_mediatek_of_data mt7629_pwm_data = { static const struct pwm_mediatek_of_data mt7981_pwm_data = { .num_pwms = 3, + .clksel_fixup = false, .pwm45_fixup = false, .pwm_ck_26m_sel_reg = PWM_CK_26M_SEL, .chanreg_base = 0x80, @@ -547,6 +562,7 @@ static const struct pwm_mediatek_of_data mt7981_pwm_data = { static const struct pwm_mediatek_of_data mt7986_pwm_data = { .num_pwms = 2, + .clksel_fixup = false, .pwm45_fixup = false, .pwm_ck_26m_sel_reg = PWM_CK_26M_SEL, .chanreg_base = 0x10, @@ -555,6 +571,7 @@ static const struct pwm_mediatek_of_data mt7986_pwm_data = { static const struct pwm_mediatek_of_data mt7988_pwm_data = { .num_pwms = 8, + .clksel_fixup = false, .pwm45_fixup = false, .chanreg_base = 0x80, .chanreg_width = 0x40, @@ -562,6 +579,7 @@ static const struct pwm_mediatek_of_data mt7988_pwm_data = { static const struct pwm_mediatek_of_data mt8183_pwm_data = { .num_pwms = 4, + .clksel_fixup = false, .pwm45_fixup = false, .pwm_ck_26m_sel_reg = PWM_CK_26M_SEL, .chanreg_base = 0x10, @@ -570,6 +588,7 @@ static const struct pwm_mediatek_of_data mt8183_pwm_data = { static const struct pwm_mediatek_of_data mt8365_pwm_data = { .num_pwms = 3, + .clksel_fixup = false, .pwm45_fixup = false, .pwm_ck_26m_sel_reg = PWM_CK_26M_SEL, .chanreg_base = 0x10, @@ -578,6 +597,7 @@ static const struct pwm_mediatek_of_data mt8365_pwm_data = { static const struct pwm_mediatek_of_data mt8516_pwm_data = { .num_pwms = 5, + .clksel_fixup = false, .pwm45_fixup = false, .pwm_ck_26m_sel_reg = PWM_CK_26M_SEL, .chanreg_base = 0x10, @@ -586,6 +606,7 @@ static const struct pwm_mediatek_of_data mt8516_pwm_data = { static const struct pwm_mediatek_of_data mt6991_pwm_data = { .num_pwms = 4, + .clksel_fixup = false, .pwm45_fixup = false, .pwm_ck_26m_sel_reg = PWM_CK_26M_SEL_V3, .chanreg_base = 0x100, diff --git a/drivers/pwm/pwm-pca9685.c b/drivers/pwm/pwm-pca9685.c index 107bebec3546..a02255a64ea8 100644 --- a/drivers/pwm/pwm-pca9685.c +++ b/drivers/pwm/pwm-pca9685.c @@ -538,7 +538,7 @@ static int __maybe_unused pca9685_pwm_runtime_resume(struct device *dev) } static const struct i2c_device_id pca9685_id[] = { - { "pca9685" }, + { .name = "pca9685" }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(i2c, pca9685_id); diff --git a/drivers/pwm/pwm-pxa.c b/drivers/pwm/pwm-pxa.c index 0f5bdb0e395e..80d2fa10919f 100644 --- a/drivers/pwm/pwm-pxa.c +++ b/drivers/pwm/pwm-pxa.c @@ -161,6 +161,7 @@ static int pwm_probe(struct platform_device *pdev) const struct platform_device_id *id = platform_get_device_id(pdev); struct pwm_chip *chip; struct pxa_pwm_chip *pc; + struct clk *bus_clk; struct device *dev = &pdev->dev; struct reset_control *rst; int ret = 0; @@ -177,7 +178,12 @@ static int pwm_probe(struct platform_device *pdev) return PTR_ERR(chip); pc = to_pxa_pwm_chip(chip); - pc->clk = devm_clk_get(dev, NULL); + bus_clk = devm_clk_get_optional_enabled(dev, "bus"); + if (IS_ERR(bus_clk)) + return dev_err_probe(dev, PTR_ERR(bus_clk), "Failed to get bus clock\n"); + + /* Get named func clk if bus clock is valid */ + pc->clk = devm_clk_get(dev, bus_clk ? "func" : NULL); if (IS_ERR(pc->clk)) return dev_err_probe(dev, PTR_ERR(pc->clk), "Failed to get clock\n"); diff --git a/drivers/pwm/pwm-stm32.c b/drivers/pwm/pwm-stm32.c index 935257a890b0..c708e4a7ad70 100644 --- a/drivers/pwm/pwm-stm32.c +++ b/drivers/pwm/pwm-stm32.c @@ -193,22 +193,6 @@ static int stm32_pwm_round_waveform_tohw(struct pwm_chip *chip, 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, @@ -223,16 +207,15 @@ static int stm32_pwm_round_waveform_fromhw(struct pwm_chip *chip, 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); + wf->period_length_ns = 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); + ccr_ns = mul_u64_u64_div_u64_roundup(((u64)wfhw->psc + 1) * wfhw->ccr, NSEC_PER_SEC, rate); if (wfhw->ccer & TIM_CCER_CCxP(ch + 1)) { wf->duty_length_ns = - stm32_pwm_mul_u64_u64_div_u64_roundup(((u64)wfhw->psc + 1) * (wfhw->arr + 1 - wfhw->ccr), - NSEC_PER_SEC, rate); + 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 { diff --git a/drivers/pwm/pwm_th1520.rs b/drivers/pwm/pwm_th1520.rs index 48808cd80737..3e3fa51ccef9 100644 --- a/drivers/pwm/pwm_th1520.rs +++ b/drivers/pwm/pwm_th1520.rs @@ -67,16 +67,10 @@ fn ns_to_cycles(ns: u64, rate_hz: u64) -> u64 { ns.saturating_mul(rate_hz) / NSEC_PER_SEC_U64 } -fn cycles_to_ns(cycles: u64, rate_hz: u64) -> u64 { +fn cycles_to_ns(cycles: u32, 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 + (u64::from(cycles) * NSEC_PER_SEC_U64).div_ceil(rate_hz) } /// Hardware-specific waveform representation for TH1520. @@ -192,15 +186,15 @@ fn round_waveform_fromhw( return Ok(()); } - wf.period_length_ns = cycles_to_ns(u64::from(wfhw.period_cycles), rate_hz); + wf.period_length_ns = cycles_to_ns(wfhw.period_cycles, rate_hz); - let duty_cycles = u64::from(wfhw.duty_cycles); + let duty_cycles = wfhw.duty_cycles; if (wfhw.ctrl_val & TH1520_PWM_FPOUT) != 0 { wf.duty_length_ns = cycles_to_ns(duty_cycles, rate_hz); wf.duty_offset_ns = 0; } else { - let period_cycles = u64::from(wfhw.period_cycles); + let period_cycles = wfhw.period_cycles; let original_duty_cycles = period_cycles.saturating_sub(duty_cycles); // For an inverted signal, `duty_length_ns` is the high time (period - low_time).